Author SHA1 Message Date
evandrodevbr d918d56dc3 feat: show Telegram connection in OMP status bar
Validate / validate (push) Failing after 5m14s
2026-07-15 18:45:03 -03:00
8 changed files with 107 additions and 11 deletions

No files matched your search

+8
View File
@@ -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.
+4 -4
View File
@@ -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`:
+1
View File
@@ -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(
+17
View File
@@ -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
View File
@@ -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
View File
@@ -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": {
+1 -1
View File
@@ -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"],
+51
View File
@@ -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",