@synaplink/orqlaude 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json.template +8 -0
- package/README.md +239 -0
- package/dist/__tests__/hallucination.test.d.ts +1 -0
- package/dist/__tests__/hallucination.test.js +65 -0
- package/dist/__tests__/hallucination.test.js.map +1 -0
- package/dist/__tests__/state.test.d.ts +1 -0
- package/dist/__tests__/state.test.js +124 -0
- package/dist/__tests__/state.test.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +322 -0
- package/dist/cli.js.map +1 -0
- package/dist/lib/audit.d.ts +38 -0
- package/dist/lib/audit.js +108 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/budgeting.d.ts +37 -0
- package/dist/lib/budgeting.js +67 -0
- package/dist/lib/budgeting.js.map +1 -0
- package/dist/lib/hallucination.d.ts +46 -0
- package/dist/lib/hallucination.js +154 -0
- package/dist/lib/hallucination.js.map +1 -0
- package/dist/lib/jsonl_tail.d.ts +40 -0
- package/dist/lib/jsonl_tail.js +126 -0
- package/dist/lib/jsonl_tail.js.map +1 -0
- package/dist/lib/pricing.d.ts +24 -0
- package/dist/lib/pricing.js +43 -0
- package/dist/lib/pricing.js.map +1 -0
- package/dist/lib/state.d.ts +118 -0
- package/dist/lib/state.js +138 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +34 -0
- package/dist/server.js.map +1 -0
- package/dist/telegram/api.d.ts +45 -0
- package/dist/telegram/api.js +59 -0
- package/dist/telegram/api.js.map +1 -0
- package/dist/telegram/bot.d.ts +11 -0
- package/dist/telegram/bot.js +73 -0
- package/dist/telegram/bot.js.map +1 -0
- package/dist/telegram/commands.d.ts +22 -0
- package/dist/telegram/commands.js +217 -0
- package/dist/telegram/commands.js.map +1 -0
- package/dist/telegram/config.d.ts +30 -0
- package/dist/telegram/config.js +37 -0
- package/dist/telegram/config.js.map +1 -0
- package/dist/telegram/notifier.d.ts +14 -0
- package/dist/telegram/notifier.js +136 -0
- package/dist/telegram/notifier.js.map +1 -0
- package/dist/tools/broker.d.ts +15 -0
- package/dist/tools/broker.js +245 -0
- package/dist/tools/broker.js.map +1 -0
- package/dist/tools/dispatch.d.ts +14 -0
- package/dist/tools/dispatch.js +231 -0
- package/dist/tools/dispatch.js.map +1 -0
- package/dist/tools/lifecycle.d.ts +18 -0
- package/dist/tools/lifecycle.js +124 -0
- package/dist/tools/lifecycle.js.map +1 -0
- package/dist/tools/ping.d.ts +2 -0
- package/dist/tools/ping.js +26 -0
- package/dist/tools/ping.js.map +1 -0
- package/dist/tools/planning.d.ts +4 -0
- package/dist/tools/planning.js +160 -0
- package/dist/tools/planning.js.map +1 -0
- package/dist/tools/review.d.ts +18 -0
- package/dist/tools/review.js +93 -0
- package/dist/tools/review.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/lib/state.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { StateStore } from "./lib/state.js";
|
|
6
|
+
import { AuditLog } from "./lib/audit.js";
|
|
7
|
+
import { registerPing } from "./tools/ping.js";
|
|
8
|
+
import { registerPlanning } from "./tools/planning.js";
|
|
9
|
+
import { registerDispatch } from "./tools/dispatch.js";
|
|
10
|
+
import { registerBroker } from "./tools/broker.js";
|
|
11
|
+
import { registerLifecycle } from "./tools/lifecycle.js";
|
|
12
|
+
import { registerReview } from "./tools/review.js";
|
|
13
|
+
/**
|
|
14
|
+
* orqlaude — multi-agent orchestrator for Claude Code.
|
|
15
|
+
*
|
|
16
|
+
* State and audit log live under <project>/.orqlaude/ by default. Override
|
|
17
|
+
* with ORQLAUDE_STATE_DIR=/some/path.
|
|
18
|
+
*/
|
|
19
|
+
const stateDir = process.env.ORQLAUDE_STATE_DIR ?? path.join(process.cwd(), ".orqlaude");
|
|
20
|
+
const store = new StateStore(stateDir);
|
|
21
|
+
const audit = new AuditLog(stateDir);
|
|
22
|
+
const server = new McpServer({
|
|
23
|
+
name: "orqlaude",
|
|
24
|
+
version: "0.3.0",
|
|
25
|
+
});
|
|
26
|
+
registerPing(server);
|
|
27
|
+
registerPlanning(server, store, audit);
|
|
28
|
+
registerDispatch(server, store, audit);
|
|
29
|
+
registerBroker(server, store, audit);
|
|
30
|
+
registerLifecycle(server, store, audit);
|
|
31
|
+
registerReview(server, store, audit);
|
|
32
|
+
const transport = new StdioServerTransport();
|
|
33
|
+
await server.connect(transport);
|
|
34
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;AACzF,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;AACvC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAErC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,YAAY,CAAC,MAAM,CAAC,CAAC;AACrB,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACvC,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACvC,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACrC,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACxC,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAErC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Telegram Bot API client. Uses fetch (Node 22+ built-in).
|
|
3
|
+
*
|
|
4
|
+
* We use long-polling via getUpdates. Webhooks would require the user to
|
|
5
|
+
* expose a public HTTPS endpoint, which is friction we don't need for a
|
|
6
|
+
* local-developer tool.
|
|
7
|
+
*
|
|
8
|
+
* Docs: https://core.telegram.org/bots/api
|
|
9
|
+
*/
|
|
10
|
+
export interface TelegramUser {
|
|
11
|
+
id: number;
|
|
12
|
+
is_bot: boolean;
|
|
13
|
+
first_name: string;
|
|
14
|
+
username?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TelegramChat {
|
|
17
|
+
id: number;
|
|
18
|
+
type: "private" | "group" | "supergroup" | "channel";
|
|
19
|
+
}
|
|
20
|
+
export interface TelegramMessage {
|
|
21
|
+
message_id: number;
|
|
22
|
+
from?: TelegramUser;
|
|
23
|
+
chat: TelegramChat;
|
|
24
|
+
date: number;
|
|
25
|
+
text?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface TelegramUpdate {
|
|
28
|
+
update_id: number;
|
|
29
|
+
message?: TelegramMessage;
|
|
30
|
+
}
|
|
31
|
+
export declare class TelegramApi {
|
|
32
|
+
private readonly token;
|
|
33
|
+
constructor(token: string);
|
|
34
|
+
private endpoint;
|
|
35
|
+
/** Long-poll for updates. Resolves when at least one is available or `timeoutSec` elapses. */
|
|
36
|
+
getUpdates(offset: number, timeoutSec?: number): Promise<TelegramUpdate[]>;
|
|
37
|
+
sendMessage(chatId: number, text: string, opts?: {
|
|
38
|
+
parseMode?: "Markdown" | "HTML";
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
/** Verify the token works by hitting getMe. Returns the bot's username. */
|
|
41
|
+
getMe(): Promise<{
|
|
42
|
+
username: string;
|
|
43
|
+
id: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Telegram Bot API client. Uses fetch (Node 22+ built-in).
|
|
3
|
+
*
|
|
4
|
+
* We use long-polling via getUpdates. Webhooks would require the user to
|
|
5
|
+
* expose a public HTTPS endpoint, which is friction we don't need for a
|
|
6
|
+
* local-developer tool.
|
|
7
|
+
*
|
|
8
|
+
* Docs: https://core.telegram.org/bots/api
|
|
9
|
+
*/
|
|
10
|
+
const BASE = "https://api.telegram.org";
|
|
11
|
+
export class TelegramApi {
|
|
12
|
+
token;
|
|
13
|
+
constructor(token) {
|
|
14
|
+
this.token = token;
|
|
15
|
+
if (!token)
|
|
16
|
+
throw new Error("TelegramApi: bot token is required");
|
|
17
|
+
}
|
|
18
|
+
endpoint(method) {
|
|
19
|
+
return `${BASE}/bot${this.token}/${method}`;
|
|
20
|
+
}
|
|
21
|
+
/** Long-poll for updates. Resolves when at least one is available or `timeoutSec` elapses. */
|
|
22
|
+
async getUpdates(offset, timeoutSec = 30) {
|
|
23
|
+
const url = `${this.endpoint("getUpdates")}?offset=${offset}&timeout=${timeoutSec}`;
|
|
24
|
+
const res = await fetch(url);
|
|
25
|
+
if (!res.ok)
|
|
26
|
+
throw new Error(`getUpdates ${res.status}: ${await res.text()}`);
|
|
27
|
+
const body = (await res.json());
|
|
28
|
+
if (!body.ok)
|
|
29
|
+
throw new Error(`Telegram API error: ${body.description}`);
|
|
30
|
+
return body.result;
|
|
31
|
+
}
|
|
32
|
+
async sendMessage(chatId, text, opts = {}) {
|
|
33
|
+
const res = await fetch(this.endpoint("sendMessage"), {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: { "content-type": "application/json" },
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
chat_id: chatId,
|
|
38
|
+
text,
|
|
39
|
+
parse_mode: opts.parseMode,
|
|
40
|
+
disable_web_page_preview: true,
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
if (!res.ok) {
|
|
44
|
+
const body = await res.text();
|
|
45
|
+
throw new Error(`sendMessage ${res.status}: ${body}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Verify the token works by hitting getMe. Returns the bot's username. */
|
|
49
|
+
async getMe() {
|
|
50
|
+
const res = await fetch(this.endpoint("getMe"));
|
|
51
|
+
if (!res.ok)
|
|
52
|
+
throw new Error(`getMe ${res.status}: ${await res.text()}`);
|
|
53
|
+
const body = (await res.json());
|
|
54
|
+
if (!body.ok)
|
|
55
|
+
throw new Error("getMe failed");
|
|
56
|
+
return { username: body.result.username ?? "<unknown>", id: body.result.id };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/telegram/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,IAAI,GAAG,0BAA0B,CAAC;AA2BxC,MAAM,OAAO,WAAW;IACO;IAA7B,YAA6B,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;QACxC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACpE,CAAC;IAEO,QAAQ,CAAC,MAAc;QAC7B,OAAO,GAAG,IAAI,OAAO,IAAI,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC;IAC9C,CAAC;IAED,8FAA8F;IAC9F,KAAK,CAAC,UAAU,CAAC,MAAc,EAAE,UAAU,GAAG,EAAE;QAC9C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,WAAW,MAAM,YAAY,UAAU,EAAE,CAAC;QACpF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,cAAc,GAAG,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9E,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoE,CAAC;QACnG,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAc,EAAE,IAAY,EAAE,OAA4C,EAAE;QAC5F,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE;YACpD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,OAAO,EAAE,MAAM;gBACf,IAAI;gBACJ,UAAU,EAAE,IAAI,CAAC,SAAS;gBAC1B,wBAAwB,EAAE,IAAI;aAC/B,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,eAAe,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,KAAK,CAAC,KAAK;QACT,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA0C,CAAC;QACzE,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;QAC9C,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,WAAW,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;IAC/E,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bot main loop.
|
|
3
|
+
*
|
|
4
|
+
* getUpdates loop: long-poll Telegram (timeout 30s), dispatch incoming
|
|
5
|
+
* messages to handleCommand
|
|
6
|
+
* notifier loop: every 5s, run Notifier.tick() against the project
|
|
7
|
+
* state to detect deltas and push notifications
|
|
8
|
+
*
|
|
9
|
+
* Run as a long-lived process: `orqlaude tg start`. Stop with Ctrl-C.
|
|
10
|
+
*/
|
|
11
|
+
export declare function runBot(projectDir: string): Promise<void>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TelegramApi } from "./api.js";
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
|
+
import { handleCommand } from "./commands.js";
|
|
4
|
+
import { Notifier } from "./notifier.js";
|
|
5
|
+
/**
|
|
6
|
+
* Bot main loop.
|
|
7
|
+
*
|
|
8
|
+
* getUpdates loop: long-poll Telegram (timeout 30s), dispatch incoming
|
|
9
|
+
* messages to handleCommand
|
|
10
|
+
* notifier loop: every 5s, run Notifier.tick() against the project
|
|
11
|
+
* state to detect deltas and push notifications
|
|
12
|
+
*
|
|
13
|
+
* Run as a long-lived process: `orqlaude tg start`. Stop with Ctrl-C.
|
|
14
|
+
*/
|
|
15
|
+
export async function runBot(projectDir) {
|
|
16
|
+
const cfg = await loadConfig();
|
|
17
|
+
if (!cfg.botToken) {
|
|
18
|
+
throw new Error("No bot token configured. Run `orqlaude tg setup` first.");
|
|
19
|
+
}
|
|
20
|
+
const api = new TelegramApi(cfg.botToken);
|
|
21
|
+
const me = await api.getMe();
|
|
22
|
+
process.stdout.write(`orqlaude tg: connected as @${me.username} (bot id ${me.id})\n`);
|
|
23
|
+
process.stdout.write(`orqlaude tg: watching ${projectDir}\n`);
|
|
24
|
+
if (cfg.whitelist.length === 0 && !cfg.ownerId) {
|
|
25
|
+
process.stdout.write(`orqlaude tg: NO whitelist yet. Have your user message /start to learn your id, then run \`orqlaude tg whitelist <id> --owner\`.\n`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
process.stdout.write(`orqlaude tg: whitelist has ${cfg.whitelist.length} user(s), owner=${cfg.ownerId ?? "none"}.\n`);
|
|
29
|
+
}
|
|
30
|
+
const notifier = new Notifier(projectDir, cfg, api);
|
|
31
|
+
// Notifier loop runs concurrently.
|
|
32
|
+
const stopFlag = { stopped: false };
|
|
33
|
+
const notifierTask = (async () => {
|
|
34
|
+
while (!stopFlag.stopped) {
|
|
35
|
+
try {
|
|
36
|
+
await notifier.tick();
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
process.stderr.write(`[notifier] ${err.message}\n`);
|
|
40
|
+
}
|
|
41
|
+
await sleep(5000);
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
44
|
+
// Updates loop.
|
|
45
|
+
let offset = 0;
|
|
46
|
+
process.on("SIGINT", () => {
|
|
47
|
+
stopFlag.stopped = true;
|
|
48
|
+
process.stdout.write("\norqlaude tg: shutting down.\n");
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|
|
51
|
+
while (!stopFlag.stopped) {
|
|
52
|
+
try {
|
|
53
|
+
const updates = await api.getUpdates(offset, 30);
|
|
54
|
+
for (const u of updates) {
|
|
55
|
+
offset = u.update_id + 1;
|
|
56
|
+
if (u.message) {
|
|
57
|
+
await handleCommand(api, u.message, projectDir).catch((err) => {
|
|
58
|
+
process.stderr.write(`[command] ${err.message}\n`);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
process.stderr.write(`[getUpdates] ${err.message}\n`);
|
|
65
|
+
await sleep(5000);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
await notifierTask;
|
|
69
|
+
}
|
|
70
|
+
function sleep(ms) {
|
|
71
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=bot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.js","sourceRoot":"","sources":["../../src/telegram/bot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TelegramApi } from "./api.js";
|
|
2
|
+
import type { TelegramMessage } from "./api.js";
|
|
3
|
+
/**
|
|
4
|
+
* Telegram command handlers.
|
|
5
|
+
*
|
|
6
|
+
* Supported commands (all require whitelisted sender):
|
|
7
|
+
* /start — register the sender (one-shot welcome)
|
|
8
|
+
* /help — list commands
|
|
9
|
+
* /plans — list active plans in the watched project(s)
|
|
10
|
+
* /status <id> — refreshed status of one plan
|
|
11
|
+
* /show <id> — full plan JSON (truncated)
|
|
12
|
+
* /notes <id> — recent notes for a plan
|
|
13
|
+
* /kill <plan> <task> <reason> — STOP a task
|
|
14
|
+
* /whitelist <user_id> [label] — owner-only: add a user
|
|
15
|
+
* /approve <plan_id> <token> — confirm a plan from Telegram (future)
|
|
16
|
+
*
|
|
17
|
+
* Each project's state is read via StateStore against that project's path.
|
|
18
|
+
* The single configured set of watchedProjects determines which projects are
|
|
19
|
+
* accessible. For v0.3 we support a single watched project at a time; the
|
|
20
|
+
* config schema is forward-compatible for multi-project later.
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleCommand(api: TelegramApi, msg: TelegramMessage, projectDir: string): Promise<void>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { StateStore } from "../lib/state.js";
|
|
2
|
+
import { snapshotSession } from "../lib/jsonl_tail.js";
|
|
3
|
+
import { loadConfig, saveConfig, isAuthorized } from "./config.js";
|
|
4
|
+
/**
|
|
5
|
+
* Telegram command handlers.
|
|
6
|
+
*
|
|
7
|
+
* Supported commands (all require whitelisted sender):
|
|
8
|
+
* /start — register the sender (one-shot welcome)
|
|
9
|
+
* /help — list commands
|
|
10
|
+
* /plans — list active plans in the watched project(s)
|
|
11
|
+
* /status <id> — refreshed status of one plan
|
|
12
|
+
* /show <id> — full plan JSON (truncated)
|
|
13
|
+
* /notes <id> — recent notes for a plan
|
|
14
|
+
* /kill <plan> <task> <reason> — STOP a task
|
|
15
|
+
* /whitelist <user_id> [label] — owner-only: add a user
|
|
16
|
+
* /approve <plan_id> <token> — confirm a plan from Telegram (future)
|
|
17
|
+
*
|
|
18
|
+
* Each project's state is read via StateStore against that project's path.
|
|
19
|
+
* The single configured set of watchedProjects determines which projects are
|
|
20
|
+
* accessible. For v0.3 we support a single watched project at a time; the
|
|
21
|
+
* config schema is forward-compatible for multi-project later.
|
|
22
|
+
*/
|
|
23
|
+
export async function handleCommand(api, msg, projectDir) {
|
|
24
|
+
const text = (msg.text ?? "").trim();
|
|
25
|
+
const userId = msg.from?.id;
|
|
26
|
+
const chatId = msg.chat.id;
|
|
27
|
+
if (!userId)
|
|
28
|
+
return;
|
|
29
|
+
const cfg = await loadConfig();
|
|
30
|
+
// /start is open: it just prints the sender's ID so they can be whitelisted later.
|
|
31
|
+
if (text === "/start") {
|
|
32
|
+
await api.sendMessage(chatId, `👋 orqlaude bot.\n\nYour Telegram user id: \`${userId}\`\n\nAsk the bot owner to whitelist you with:\n\`orqlaude tg whitelist ${userId}\`\n\nOr if you ARE the owner and haven't set yourself up yet:\n\`orqlaude tg whitelist ${userId} --owner\``, { parseMode: "Markdown" });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!isAuthorized(cfg, userId)) {
|
|
36
|
+
await api.sendMessage(chatId, `⛔ Unauthorized. Your user id is ${userId}. Owner must whitelist you.`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const stateStore = new StateStore(`${projectDir}/.orqlaude`);
|
|
40
|
+
if (text === "/help") {
|
|
41
|
+
await api.sendMessage(chatId, [
|
|
42
|
+
"*orqlaude commands*",
|
|
43
|
+
"`/plans` — list active plans",
|
|
44
|
+
"`/status <plan_id>` — refresh one plan",
|
|
45
|
+
"`/show <plan_id>` — raw plan JSON",
|
|
46
|
+
"`/notes <plan_id>` — recent agent notes",
|
|
47
|
+
"`/kill <plan> <task> <reason>` — STOP a task",
|
|
48
|
+
"`/whoami` — your user id",
|
|
49
|
+
"`/help` — this",
|
|
50
|
+
].join("\n"), { parseMode: "Markdown" });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (text === "/whoami") {
|
|
54
|
+
await api.sendMessage(chatId, `user_id: ${userId}\nproject: ${projectDir}`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (text === "/plans") {
|
|
58
|
+
const plans = await stateStore.read((s) => Object.values(s.plans)
|
|
59
|
+
.filter((p) => p.status !== "collected")
|
|
60
|
+
.sort((a, b) => b.createdAt - a.createdAt));
|
|
61
|
+
if (plans.length === 0) {
|
|
62
|
+
await api.sendMessage(chatId, "No active plans.");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const lines = plans.map((p) => {
|
|
66
|
+
const done = p.tasks.filter((t) => t.status === "done").length;
|
|
67
|
+
return `• \`${p.id.slice(0, 8)}\` ${p.status} ${done}/${p.tasks.length} — ${truncate(p.rootTask, 50)}`;
|
|
68
|
+
});
|
|
69
|
+
await api.sendMessage(chatId, `*Active plans:*\n${lines.join("\n")}`, { parseMode: "Markdown" });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// /status <plan_id> ---------------------------------------------------------
|
|
73
|
+
if (text.startsWith("/status")) {
|
|
74
|
+
const planId = text.split(/\s+/)[1];
|
|
75
|
+
if (!planId)
|
|
76
|
+
return api.sendMessage(chatId, "usage: /status <plan_id>");
|
|
77
|
+
try {
|
|
78
|
+
const plan = await stateStore.read((s) => requirePlan(s.plans, planId));
|
|
79
|
+
await api.sendMessage(chatId, await renderStatus(plan, projectDir), { parseMode: "Markdown" });
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
await api.sendMessage(chatId, `error: ${err.message}`);
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (text.startsWith("/show")) {
|
|
87
|
+
const planId = text.split(/\s+/)[1];
|
|
88
|
+
if (!planId)
|
|
89
|
+
return api.sendMessage(chatId, "usage: /show <plan_id>");
|
|
90
|
+
try {
|
|
91
|
+
const plan = await stateStore.read((s) => requirePlan(s.plans, planId));
|
|
92
|
+
const json = JSON.stringify(plan, null, 2);
|
|
93
|
+
await api.sendMessage(chatId, "```json\n" + truncate(json, 3500) + "\n```", { parseMode: "Markdown" });
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
await api.sendMessage(chatId, `error: ${err.message}`);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (text.startsWith("/notes")) {
|
|
101
|
+
const planId = text.split(/\s+/)[1];
|
|
102
|
+
if (!planId)
|
|
103
|
+
return api.sendMessage(chatId, "usage: /notes <plan_id>");
|
|
104
|
+
try {
|
|
105
|
+
const plan = await stateStore.read((s) => requirePlan(s.plans, planId));
|
|
106
|
+
const recent = plan.notes.slice(-10);
|
|
107
|
+
if (recent.length === 0) {
|
|
108
|
+
await api.sendMessage(chatId, "(no notes yet)");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const lines = recent.map((n) => {
|
|
112
|
+
const taskTitle = plan.tasks.find((t) => t.id === n.taskId)?.title ?? n.taskId.slice(0, 8);
|
|
113
|
+
const blocking = n.blocking ? (n.acked ? "✓" : "🟡") : "";
|
|
114
|
+
return `• [${truncate(taskTitle, 30)}]${blocking} ${truncate(n.text, 200)}${n.prUrl ? `\n ${n.prUrl}` : ""}`;
|
|
115
|
+
});
|
|
116
|
+
await api.sendMessage(chatId, `*Recent notes for ${planId.slice(0, 8)}:*\n${lines.join("\n\n")}`, {
|
|
117
|
+
parseMode: "Markdown",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
await api.sendMessage(chatId, `error: ${err.message}`);
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// /kill <plan> <task> <reason...>
|
|
126
|
+
if (text.startsWith("/kill")) {
|
|
127
|
+
const parts = text.split(/\s+/);
|
|
128
|
+
const [, planId, taskId, ...reasonParts] = parts;
|
|
129
|
+
const reason = reasonParts.join(" ") || "killed from Telegram";
|
|
130
|
+
if (!planId || !taskId)
|
|
131
|
+
return api.sendMessage(chatId, "usage: /kill <plan_id> <task_id> <reason>");
|
|
132
|
+
try {
|
|
133
|
+
await stateStore.update((s) => {
|
|
134
|
+
const plan = requirePlan(s.plans, planId);
|
|
135
|
+
const task = plan.tasks.find((t) => t.id === taskId);
|
|
136
|
+
if (!task)
|
|
137
|
+
throw new Error(`task ${taskId} not in plan ${planId}`);
|
|
138
|
+
if (!task.spawnedSessionId) {
|
|
139
|
+
task.status = "cancelled";
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
task.stopRequested = { reason, requestedAt: Date.now() };
|
|
143
|
+
plan.messages.push({
|
|
144
|
+
id: crypto.randomUUID(),
|
|
145
|
+
toSessionId: task.spawnedSessionId,
|
|
146
|
+
text: `STOP: ${reason}. Commit what you have and exit.`,
|
|
147
|
+
queuedAt: Date.now(),
|
|
148
|
+
delivered: false,
|
|
149
|
+
kind: "stop",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
await api.sendMessage(chatId, `🛑 STOP queued for task ${taskId.slice(0, 8)}: ${reason}`);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
await api.sendMessage(chatId, `error: ${err.message}`);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (text.startsWith("/whitelist")) {
|
|
161
|
+
if (cfg.ownerId !== userId) {
|
|
162
|
+
await api.sendMessage(chatId, "Only the owner can manage the whitelist.");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const [, target, ...rest] = text.split(/\s+/);
|
|
166
|
+
const targetId = Number(target);
|
|
167
|
+
if (!Number.isFinite(targetId))
|
|
168
|
+
return api.sendMessage(chatId, "usage: /whitelist <user_id> [label]");
|
|
169
|
+
const label = rest.join(" ") || undefined;
|
|
170
|
+
if (!cfg.whitelist.some((w) => w.userId === targetId)) {
|
|
171
|
+
cfg.whitelist.push({ userId: targetId, chatId: targetId, label });
|
|
172
|
+
await saveConfig(cfg);
|
|
173
|
+
}
|
|
174
|
+
await api.sendMessage(chatId, `✓ Whitelisted ${targetId}${label ? ` (${label})` : ""}.`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// Default: unknown command
|
|
178
|
+
if (text.startsWith("/")) {
|
|
179
|
+
await api.sendMessage(chatId, `unknown command. /help for the list.`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function requirePlan(plans, planId) {
|
|
183
|
+
const full = plans[planId] ?? Object.values(plans).find((p) => p.id.startsWith(planId));
|
|
184
|
+
if (!full)
|
|
185
|
+
throw new Error(`plan not found: ${planId}`);
|
|
186
|
+
return full;
|
|
187
|
+
}
|
|
188
|
+
async function renderStatus(plan, projectDir) {
|
|
189
|
+
let totalTokens = 0;
|
|
190
|
+
const taskLines = [];
|
|
191
|
+
for (const t of plan.tasks) {
|
|
192
|
+
let extra = "";
|
|
193
|
+
if (t.spawnedSessionId) {
|
|
194
|
+
const snap = await snapshotSession(projectDir, t.spawnedSessionId);
|
|
195
|
+
totalTokens += snap.totalEffectiveTokens;
|
|
196
|
+
const tokK = Math.round(snap.totalEffectiveTokens / 1000);
|
|
197
|
+
const flag = snap.terminated ? "✓" : "▶";
|
|
198
|
+
extra = ` ${flag} ${tokK}k`;
|
|
199
|
+
}
|
|
200
|
+
const prSuffix = t.prUrl ? ` 📎` : "";
|
|
201
|
+
taskLines.push(` • ${truncate(t.title, 35)}${extra}${prSuffix}`);
|
|
202
|
+
}
|
|
203
|
+
const tokK = Math.round(totalTokens / 1000);
|
|
204
|
+
const capK = Math.round(plan.budgetCapTokens / 1000);
|
|
205
|
+
return [
|
|
206
|
+
`*${plan.id.slice(0, 8)}* — ${plan.status}`,
|
|
207
|
+
`${truncate(plan.rootTask, 80)}`,
|
|
208
|
+
`tokens: ${tokK}k / ${capK}k`,
|
|
209
|
+
...taskLines,
|
|
210
|
+
].join("\n");
|
|
211
|
+
}
|
|
212
|
+
function truncate(s, n) {
|
|
213
|
+
if (s.length <= n)
|
|
214
|
+
return s;
|
|
215
|
+
return s.slice(0, n - 1) + "…";
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/telegram/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-user Telegram config: bot token + whitelist of Telegram user ids.
|
|
3
|
+
*
|
|
4
|
+
* Stored at ~/.orqlaude/telegram.json with mode 600 (owner-only). The bot
|
|
5
|
+
* token is a secret — never log it, never include it in audit events.
|
|
6
|
+
*
|
|
7
|
+
* Schema (intentionally simple — the bot is a small feature):
|
|
8
|
+
* {
|
|
9
|
+
* "botToken": "1234:ABC..." // from @BotFather
|
|
10
|
+
* "whitelist": [ { "userId": 12345, "chatId": 12345, "label": "Matthew" } ],
|
|
11
|
+
* "ownerId": 12345 // who can run /whitelist commands
|
|
12
|
+
* "watchedProjects": [ "/Users/.../project1" ] // future: monitor multiple
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
export interface WhitelistEntry {
|
|
16
|
+
userId: number;
|
|
17
|
+
chatId: number;
|
|
18
|
+
label?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface TelegramConfig {
|
|
21
|
+
botToken: string;
|
|
22
|
+
ownerId: number | null;
|
|
23
|
+
whitelist: WhitelistEntry[];
|
|
24
|
+
watchedProjects: string[];
|
|
25
|
+
}
|
|
26
|
+
declare const CONFIG_PATH: string;
|
|
27
|
+
export declare function loadConfig(): Promise<TelegramConfig>;
|
|
28
|
+
export declare function saveConfig(cfg: TelegramConfig): Promise<void>;
|
|
29
|
+
export declare function isAuthorized(cfg: TelegramConfig, userId: number): boolean;
|
|
30
|
+
export { CONFIG_PATH };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), ".orqlaude");
|
|
5
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, "telegram.json");
|
|
6
|
+
const EMPTY = {
|
|
7
|
+
botToken: "",
|
|
8
|
+
ownerId: null,
|
|
9
|
+
whitelist: [],
|
|
10
|
+
watchedProjects: [],
|
|
11
|
+
};
|
|
12
|
+
export async function loadConfig() {
|
|
13
|
+
try {
|
|
14
|
+
const raw = await fs.readFile(CONFIG_PATH, "utf8");
|
|
15
|
+
const parsed = JSON.parse(raw);
|
|
16
|
+
return { ...EMPTY, ...parsed };
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (err.code === "ENOENT")
|
|
20
|
+
return { ...EMPTY };
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function saveConfig(cfg) {
|
|
25
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
26
|
+
const tmp = `${CONFIG_PATH}.${process.pid}.tmp`;
|
|
27
|
+
await fs.writeFile(tmp, JSON.stringify(cfg, null, 2));
|
|
28
|
+
await fs.rename(tmp, CONFIG_PATH);
|
|
29
|
+
await fs.chmod(CONFIG_PATH, 0o600);
|
|
30
|
+
}
|
|
31
|
+
export function isAuthorized(cfg, userId) {
|
|
32
|
+
if (userId === cfg.ownerId)
|
|
33
|
+
return true;
|
|
34
|
+
return cfg.whitelist.some((w) => w.userId === userId);
|
|
35
|
+
}
|
|
36
|
+
export { CONFIG_PATH };
|
|
37
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/telegram/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AA8BzB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACxD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAE3D,MAAM,KAAK,GAAmB;IAC5B,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,EAAE;IACb,eAAe,EAAE,EAAE;CACpB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;QAC/C,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAmB;IAClD,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,GAAG,WAAW,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IAChD,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAClC,MAAM,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAmB,EAAE,MAAc;IAC9D,IAAI,MAAM,KAAK,GAAG,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AACxD,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TelegramApi } from "./api.js";
|
|
2
|
+
import type { TelegramConfig } from "./config.js";
|
|
3
|
+
export declare class Notifier {
|
|
4
|
+
private projectDir;
|
|
5
|
+
private cfg;
|
|
6
|
+
private api;
|
|
7
|
+
private cursorPath;
|
|
8
|
+
private cursor;
|
|
9
|
+
constructor(projectDir: string, cfg: TelegramConfig, api: TelegramApi);
|
|
10
|
+
private loadCursor;
|
|
11
|
+
private saveCursor;
|
|
12
|
+
/** One pass: detect deltas, push notifications, advance cursor. */
|
|
13
|
+
tick(): Promise<void>;
|
|
14
|
+
}
|