Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d918d56dc3 | ||
|
|
a56408711e |
No files matched your search
@@ -2,6 +2,14 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- **OMP status segment** — publishes `Telegram: conectado` or `Telegram: desconectado` immediately after OMP's `cost` segment through the public `setStatusSegment()` API.
|
||||
|
||||
### Changed
|
||||
|
||||
- **OMP compatibility** — require `@oh-my-pi/pi-coding-agent >=17.0.0`; README installation commands now use `omp plugin install` and `omp plugin link`.
|
||||
|
||||
### 0.22.0-evandro.1 — Evandro Fonseca Junior fork (oh-my-pi-telegram)
|
||||
|
||||
- **Repackaged as `@evandrodevbr/oh-my-pi-telegram`** — version suffix `-evandro.1` keeps the 0.22.0 source line (SHA `afe09c5`) traceable to upstream `llblab/pi-telegram` while making the fork unambiguous in the `omp` extension registry.
|
||||
|
||||
@@ -14,7 +14,7 @@ This fork is built and tested against:
|
||||
|
||||
| Component | Version | Status |
|
||||
| --- | --- | --- |
|
||||
| `can1357/oh-my-pi` (CLI: `omp`) | `>=16.5.2` (`@oh-my-pi/pi-coding-agent`) | ✅ verified |
|
||||
| `can1357/oh-my-pi` (CLI: `omp`) | `>=17.0.0` (`@oh-my-pi/pi-coding-agent`) | ✅ verified |
|
||||
| `@earendil-works/pi-{ai,agent-core,coding-agent}` | `>=0.80.6` | ✅ verified (peerDeps preserved from upstream) |
|
||||
| `node` | `>=22.19.0` | ✅ verified on Node 24.15.0 |
|
||||
| Telegram Bot API | current | ✅ unchanged from upstream |
|
||||
@@ -23,13 +23,13 @@ This fork is built and tested against:
|
||||
|
||||
```bash
|
||||
# From npm (after this fork is published):
|
||||
omp install npm:@evandrodevbr/oh-my-pi-telegram
|
||||
omp plugin install npm:@evandrodevbr/oh-my-pi-telegram
|
||||
|
||||
# From git (always works):
|
||||
omp install git:github.com/evandrodevbr/oh-my-pi-telegram
|
||||
omp plugin install git:github.com/evandrodevbr/oh-my-pi-telegram
|
||||
|
||||
# From a local checkout (development):
|
||||
omp install --link /path/to/oh-my-pi-telegram
|
||||
omp plugin link /path/to/oh-my-pi-telegram
|
||||
```
|
||||
|
||||
Then inside `omp`:
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# Telegram OMP 17 status indicator
|
||||
|
||||
## Goal
|
||||
|
||||
Make the Telegram bridge visible in OMP 17's upper status bar, immediately after the cost segment, and correct the OMP installation guidance. Configure a replacement bot token only through the local interactive OMP setup flow.
|
||||
|
||||
## Scope
|
||||
|
||||
- Add a public OMP extension API for a status-bar segment placed after `cost`.
|
||||
- Have `@evandrodevbr/oh-my-pi-telegram` register one segment through that API.
|
||||
- Update the segment when bridge polling starts, stops, fails, or a session is replaced.
|
||||
- Update the plugin README with OMP 17 plugin commands and secure setup instructions.
|
||||
|
||||
## OMP core API
|
||||
|
||||
OMP owns status-bar ordering and rendering. The API accepts a keyed text segment and exposes only the supported placement:
|
||||
|
||||
- `after: "cost"`
|
||||
- a stable extension key
|
||||
- plain text supplied by the extension
|
||||
|
||||
OMP sanitizes extension text using the existing status-text sanitizer and renders registered segments after its own `cost` segment, before subsequent built-in segments. An extension clears its segment by passing `undefined`. Extension segments cannot choose arbitrary insertion positions or access the status-line component directly.
|
||||
|
||||
The implementation belongs in a versioned OMP source checkout or fork, not the global Bun installation, because `omp update` replaces global package files.
|
||||
|
||||
## Telegram bridge behavior
|
||||
|
||||
The plugin registers the `telegram` segment once the extension UI is available. It derives the value from the existing bridge status runtime:
|
||||
|
||||
| Runtime condition | Status-bar text |
|
||||
| --- | --- |
|
||||
| Telegram polling active | `Telegram: conectado` |
|
||||
| Telegram polling inactive | `Telegram: desconectado` |
|
||||
|
||||
The plugin updates the registered segment through its existing status update flow. This covers `/telegram-connect`, `/telegram-disconnect`, polling errors and recovery, lock ownership changes, and session lifecycle updates. It does not make a network request merely to render a status value.
|
||||
|
||||
## Configuration
|
||||
|
||||
The replacement token is configured only in an interactive local OMP session:
|
||||
|
||||
```text
|
||||
/telegram-setup
|
||||
/telegram-connect
|
||||
```
|
||||
|
||||
The token must not be put in chat, source code, README examples, committed configuration, test fixtures, logs, or terminal output. The plugin continues using its existing local configuration file and profile behavior.
|
||||
|
||||
## README
|
||||
|
||||
The primary OMP installation commands become:
|
||||
|
||||
```bash
|
||||
omp plugin install npm:@evandrodevbr/oh-my-pi-telegram
|
||||
omp plugin install git:github.com/evandrodevbr/oh-my-pi-telegram
|
||||
omp plugin link /path/to/oh-my-pi-telegram
|
||||
```
|
||||
|
||||
The README states that an OMP session must be restarted after installation or linking, then documents `/telegram-setup`, `/telegram-connect`, `/telegram-disconnect`, and `/telegram-status`.
|
||||
|
||||
## Verification
|
||||
|
||||
- OMP unit test: a registered extension segment is rendered immediately after `cost`; clearing it removes it; text is sanitized.
|
||||
- Telegram plugin test: active polling renders `Telegram: conectado`; inactive polling renders `Telegram: desconectado`; the current UI registration API is exercised.
|
||||
- Plugin type-check and public-package tests pass.
|
||||
- OMP 17 smoke test: the linked plugin loads and `/telegram-status` returns.
|
||||
- Manual local configuration: `/telegram-setup` validates the new bot token, `/telegram-connect` activates polling, and the status bar changes state.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No direct modification of global Bun-installed OMP files.
|
||||
- No status segment placement other than after `cost`.
|
||||
- No retry policy, network health probe, or separate Telegram connection abstraction.
|
||||
- No token migration or token disclosure.
|
||||
@@ -278,6 +278,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
||||
Queue.TelegramQueueItem<Pi.ExtensionContext>
|
||||
>({
|
||||
getConfig: configStore.get,
|
||||
setStatusSegment: Pi.setExtensionContextTopStatusSegment,
|
||||
getActiveProfileName: configStore.getActiveProfileName,
|
||||
getDiagnosticPaths: Paths.getTelegramDiagnosticsDisplayPaths,
|
||||
isPollingActive: Polling.createTelegramPollingActivityReader(
|
||||
|
||||
@@ -95,6 +95,23 @@ export function canStartPollingInExtensionContext(ctx: unknown): boolean {
|
||||
return !isExtensionContextPassiveRunMode(ctx);
|
||||
}
|
||||
|
||||
export interface PiTopStatusSegmentPorts {
|
||||
setStatusSegment?: (
|
||||
key: string,
|
||||
text: string | undefined,
|
||||
options: { placement: "after-cost" },
|
||||
) => void;
|
||||
}
|
||||
|
||||
export function setExtensionContextTopStatusSegment(
|
||||
ctx: ExtensionContext,
|
||||
key: string,
|
||||
text: string,
|
||||
): void {
|
||||
const ui = ctx.ui as ExtensionContext["ui"] & PiTopStatusSegmentPorts;
|
||||
ui.setStatusSegment?.(key, text, { placement: "after-cost" });
|
||||
}
|
||||
|
||||
export function formatPollingStartBlockedByRunMode(ctx: unknown): string {
|
||||
const mode = getExtensionContextMode(ctx);
|
||||
return mode
|
||||
|
||||
+24
-5
@@ -232,6 +232,14 @@ export interface TelegramStatusBarState {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export function getTelegramTopStatusSegment(
|
||||
state: Pick<TelegramStatusBarState, "pollingActive">,
|
||||
): string {
|
||||
return state.pollingActive
|
||||
? "Telegram: conectado"
|
||||
: "Telegram: desconectado";
|
||||
}
|
||||
|
||||
export interface TelegramStatusRuntimeContext {
|
||||
ui: {
|
||||
theme: TelegramStatusBarTheme;
|
||||
@@ -245,6 +253,7 @@ export interface TelegramStatusRuntimeDeps<
|
||||
statusKey?: string;
|
||||
getStatusBarState: (ctx: TContext, error?: string) => TelegramStatusBarState;
|
||||
getBridgeStatusLineState: () => TelegramBridgeStatusLineState;
|
||||
setStatusSegment?: (ctx: TContext, key: string, text: string) => void;
|
||||
}
|
||||
|
||||
export interface TelegramBridgeStatusConfig {
|
||||
@@ -255,6 +264,7 @@ export interface TelegramBridgeStatusConfig {
|
||||
}
|
||||
|
||||
export interface TelegramBridgeStatusRuntimeDeps<
|
||||
TContext extends TelegramStatusRuntimeContext,
|
||||
TQueueItem extends { queueLane: TelegramStatusQueueLane },
|
||||
> {
|
||||
statusKey?: string;
|
||||
@@ -298,6 +308,11 @@ export interface TelegramBridgeStatusRuntimeDeps<
|
||||
getInstanceSlot?: () => string | undefined;
|
||||
getInstanceThreadName?: () => string | undefined;
|
||||
getNowMs?: () => number;
|
||||
setStatusSegment?: (
|
||||
ctx: TContext,
|
||||
key: string,
|
||||
text: string,
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface TelegramBridgeStatusLineOptions {
|
||||
@@ -565,12 +580,15 @@ export function createTelegramStatusRuntime<
|
||||
const statusKey = deps.statusKey ?? "telegram";
|
||||
return {
|
||||
updateStatus: (ctx, error) => {
|
||||
const state = deps.getStatusBarState(ctx, error);
|
||||
ctx.ui.setStatus(
|
||||
statusKey,
|
||||
buildTelegramStatusBarText(
|
||||
ctx.ui.theme,
|
||||
deps.getStatusBarState(ctx, error),
|
||||
),
|
||||
buildTelegramStatusBarText(ctx.ui.theme, state),
|
||||
);
|
||||
deps.setStatusSegment?.(
|
||||
ctx,
|
||||
statusKey,
|
||||
getTelegramTopStatusSegment(state),
|
||||
);
|
||||
},
|
||||
getStatusLines: (options) =>
|
||||
@@ -583,10 +601,11 @@ export function createTelegramBridgeStatusRuntime<
|
||||
TContext extends TelegramStatusRuntimeContext,
|
||||
TQueueItem extends { queueLane: TelegramStatusQueueLane },
|
||||
>(
|
||||
deps: TelegramBridgeStatusRuntimeDeps<TQueueItem>,
|
||||
deps: TelegramBridgeStatusRuntimeDeps<TContext, TQueueItem>,
|
||||
): TelegramStatusRuntime<TContext> {
|
||||
return createTelegramStatusRuntime({
|
||||
statusKey: deps.statusKey,
|
||||
setStatusSegment: deps.setStatusSegment,
|
||||
getStatusBarState: (_ctx, error) => {
|
||||
const config = deps.getConfig();
|
||||
const queuedItems = deps.getQueuedItems();
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@
|
||||
"./index.ts"
|
||||
],
|
||||
"compatibleWith": {
|
||||
"@oh-my-pi/pi-coding-agent": ">=16.5.2"
|
||||
"@oh-my-pi/pi-coding-agent": ">=17.0.0"
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -100,7 +100,7 @@ test("Package declares the OMP extension entry point and Pi lifecycle compatibil
|
||||
assert.deepEqual(packageJson.omp?.extensions, ["./index.ts"]);
|
||||
assert.equal(
|
||||
packageJson.omp?.compatibleWith?.["@oh-my-pi/pi-coding-agent"],
|
||||
">=16.5.2",
|
||||
">=17.0.0",
|
||||
);
|
||||
assert.equal(
|
||||
packageJson.peerDependencies?.["@earendil-works/pi-coding-agent"],
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
createTelegramStatusHtmlBuilder,
|
||||
createTelegramStatusSnapshot,
|
||||
createTelegramStatusRuntime,
|
||||
getTelegramTopStatusSegment,
|
||||
getTelegramStatusBarProcessingStatus,
|
||||
recordStructuredTelegramRuntimeEvent,
|
||||
recordTelegramRuntimeEvent,
|
||||
@@ -401,6 +402,56 @@ test("Status runtime updates the status bar and exposes bridge lines", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("Status runtime publishes the polling state after cost", () => {
|
||||
const published: Array<[string, string, { placement: "after-cost" }]> = [];
|
||||
let pollingActive = true;
|
||||
const runtime = createTelegramBridgeStatusRuntime({
|
||||
getConfig: () => ({ botToken: "token", allowedUserId: 7 }),
|
||||
isPollingActive: () => pollingActive,
|
||||
getActiveSourceMessageIds: () => undefined,
|
||||
hasActiveTurn: () => false,
|
||||
hasDispatchPending: () => false,
|
||||
isCompactionInProgress: () => false,
|
||||
getActiveToolExecutions: () => 0,
|
||||
hasPendingModelSwitch: () => false,
|
||||
getQueuedItems: () => [],
|
||||
formatQueuedStatus: () => "",
|
||||
getRecentRuntimeEvents: () => [],
|
||||
setStatusSegment: (_ctx, key, text) => {
|
||||
published.push([key, text, { placement: "after-cost" }]);
|
||||
},
|
||||
});
|
||||
const ctx = {
|
||||
ui: {
|
||||
theme: { fg: (_token: string, text: string) => text },
|
||||
setStatus: () => {},
|
||||
},
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
getTelegramTopStatusSegment({ pollingActive: true }),
|
||||
"Telegram: conectado",
|
||||
);
|
||||
runtime.updateStatus(ctx);
|
||||
assert.deepEqual(published.pop(), [
|
||||
"telegram",
|
||||
"Telegram: conectado",
|
||||
{ placement: "after-cost" },
|
||||
]);
|
||||
|
||||
pollingActive = false;
|
||||
assert.equal(
|
||||
getTelegramTopStatusSegment({ pollingActive: false }),
|
||||
"Telegram: desconectado",
|
||||
);
|
||||
runtime.updateStatus(ctx);
|
||||
assert.deepEqual(published.pop(), [
|
||||
"telegram",
|
||||
"Telegram: desconectado",
|
||||
{ placement: "after-cost" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("Status lines expose thread reconciliation state", () => {
|
||||
const lines = buildTelegramBridgeStatusLines({
|
||||
botUsername: "demo_bot",
|
||||
|
||||
Reference in new issue
Block a user