@synaplink/orqlaude 0.3.0 → 0.3.1
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 +1 -1
- package/LICENSE +21 -0
- package/dist/__tests__/v031.test.d.ts +1 -0
- package/dist/__tests__/v031.test.js +126 -0
- package/dist/__tests__/v031.test.js.map +1 -0
- package/dist/lib/audit.js +42 -11
- package/dist/lib/audit.js.map +1 -1
- package/dist/lib/state.d.ts +30 -17
- package/dist/lib/state.js +110 -27
- package/dist/lib/state.js.map +1 -1
- package/dist/server.js +34 -4
- package/dist/server.js.map +1 -1
- package/dist/telegram/config.d.ts +1 -0
- package/dist/telegram/config.js +21 -4
- package/dist/telegram/config.js.map +1 -1
- package/dist/telegram/notifier.d.ts +14 -0
- package/dist/telegram/notifier.js +38 -20
- package/dist/telegram/notifier.js.map +1 -1
- package/dist/tools/ping.js +1 -1
- package/package.json +5 -3
package/.mcp.json.template
CHANGED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SynapLink
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { promises as fs } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import { StateStore, newPlan, findPlan } from "../lib/state.js";
|
|
7
|
+
import { escapeMd } from "../telegram/notifier.js";
|
|
8
|
+
/**
|
|
9
|
+
* Regression tests for v0.3.1 fixes — each test corresponds to a finding
|
|
10
|
+
* from the dogfood review of v0.3.0.
|
|
11
|
+
*/
|
|
12
|
+
async function tmpStore() {
|
|
13
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-v031-"));
|
|
14
|
+
return { store: new StateStore(dir), dir };
|
|
15
|
+
}
|
|
16
|
+
test("update() rolls back in-memory cache when mutator throws (BLOCKER from Phase 1 review)", async () => {
|
|
17
|
+
const { store } = await tmpStore();
|
|
18
|
+
const plan = await store.update((s) => {
|
|
19
|
+
const p = newPlan("root", 100_000, [{ title: "T", prompt: "p", tldr: "tl" }]);
|
|
20
|
+
s.plans[p.id] = p;
|
|
21
|
+
return p;
|
|
22
|
+
});
|
|
23
|
+
// Throwing mutator. The cache must NOT retain the half-applied change.
|
|
24
|
+
await assert.rejects(async () => {
|
|
25
|
+
await store.update((s) => {
|
|
26
|
+
const p = findPlan(s, plan.id);
|
|
27
|
+
p.rootTask = "MUTATED";
|
|
28
|
+
throw new Error("boom");
|
|
29
|
+
});
|
|
30
|
+
}, /boom/);
|
|
31
|
+
const after = await store.read((s) => findPlan(s, plan.id));
|
|
32
|
+
assert.equal(after.rootTask, "root", "post-throw rootTask should be unchanged");
|
|
33
|
+
});
|
|
34
|
+
test("read() funneled through writeLock — never observes torn state (BLOCKER from Phase 1 review)", async () => {
|
|
35
|
+
const { store } = await tmpStore();
|
|
36
|
+
let planId = "";
|
|
37
|
+
await store.update((s) => {
|
|
38
|
+
const p = newPlan("orig", 50_000, [{ title: "T", prompt: "p", tldr: "tl" }]);
|
|
39
|
+
planId = p.id;
|
|
40
|
+
s.plans[p.id] = p;
|
|
41
|
+
});
|
|
42
|
+
// Kick off a slow mutator and a read concurrently. The read must see EITHER
|
|
43
|
+
// the pre-mutation state OR the post-mutation state, never a half-mutation.
|
|
44
|
+
const observed = [];
|
|
45
|
+
const mutate = store.update(async (s) => {
|
|
46
|
+
const p = findPlan(s, planId);
|
|
47
|
+
p.rootTask = "first";
|
|
48
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
49
|
+
p.rootTask = "final";
|
|
50
|
+
});
|
|
51
|
+
const reads = [
|
|
52
|
+
store.read((s) => findPlan(s, planId).rootTask),
|
|
53
|
+
store.read((s) => findPlan(s, planId).rootTask),
|
|
54
|
+
store.read((s) => findPlan(s, planId).rootTask),
|
|
55
|
+
];
|
|
56
|
+
const [_, r1, r2, r3] = await Promise.all([mutate, ...reads]);
|
|
57
|
+
observed.push(r1, r2, r3);
|
|
58
|
+
for (const v of observed) {
|
|
59
|
+
assert.ok(v === "orig" || v === "final", `unexpected torn-state read: ${v}`);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
test("audit summarize() scrubs approval_token from result text (BLOCKER from Phase 1 review)", async () => {
|
|
63
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-audit-"));
|
|
64
|
+
const { AuditLog } = await import("../lib/audit.js");
|
|
65
|
+
const audit = new AuditLog(dir);
|
|
66
|
+
const wrapped = audit.wrap("request_approval", async () => ({
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: JSON.stringify({ approval_token: "secret-uuid-here", summary: "ok" }, null, 2),
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
}));
|
|
74
|
+
await wrapped({});
|
|
75
|
+
// Audit file should NOT contain the cleartext token.
|
|
76
|
+
const log = await fs.readFile(path.join(dir, "audit.jsonl"), "utf8");
|
|
77
|
+
assert.ok(!log.includes("secret-uuid-here"), `token leaked in audit log: ${log}`);
|
|
78
|
+
assert.ok(log.includes("<redacted>"), "expected <redacted> marker");
|
|
79
|
+
});
|
|
80
|
+
test("v1→v2 migration synthesizes tasks/notes/messages/claims when missing (CONCERN from Phase 1)", async () => {
|
|
81
|
+
const { store: _ignored, dir } = await tmpStore();
|
|
82
|
+
// Hand-craft a v1 state file with missing fields.
|
|
83
|
+
const v1 = {
|
|
84
|
+
schemaVersion: 1,
|
|
85
|
+
plans: {
|
|
86
|
+
"p1": {
|
|
87
|
+
id: "p1",
|
|
88
|
+
createdAt: 1,
|
|
89
|
+
rootTask: "legacy",
|
|
90
|
+
budgetCapUsd: 4,
|
|
91
|
+
perAgentCapUsd: 2,
|
|
92
|
+
status: "draft",
|
|
93
|
+
// Deliberately missing: tasks, notes, messages
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
await fs.writeFile(path.join(dir, "orqlaude-state.json"), JSON.stringify(v1));
|
|
98
|
+
const fresh = new StateStore(dir);
|
|
99
|
+
const out = await fresh.read((s) => s.plans["p1"]);
|
|
100
|
+
assert.deepEqual(out.tasks, []);
|
|
101
|
+
assert.deepEqual(out.notes, []);
|
|
102
|
+
assert.deepEqual(out.messages, []);
|
|
103
|
+
assert.deepEqual(out.claims, []);
|
|
104
|
+
assert.equal(out.budgetCapTokens, 4 * 25_000);
|
|
105
|
+
});
|
|
106
|
+
test("escapeMd backslashes Telegram MarkdownV1 special chars (BLOCKER from Phase 3)", () => {
|
|
107
|
+
assert.equal(escapeMd("feature/foo_bar"), "feature/foo\\_bar");
|
|
108
|
+
assert.equal(escapeMd("**bold**"), "\\*\\*bold\\*\\*");
|
|
109
|
+
assert.equal(escapeMd("plain text"), "plain text");
|
|
110
|
+
assert.equal(escapeMd("`code`"), "\\`code\\`");
|
|
111
|
+
assert.equal(escapeMd("[link]"), "\\[link]");
|
|
112
|
+
assert.equal(escapeMd("a_b*c`d[e"), "a\\_b\\*c\\`d\\[e");
|
|
113
|
+
});
|
|
114
|
+
test("Telegram saveConfig writes with mode 0o600 atomically (BLOCKER from Phase 3)", async () => {
|
|
115
|
+
// We can't easily mock the HOME-rooted CONFIG_PATH inside the module, so
|
|
116
|
+
// this test verifies the underlying behavior: writeFile with mode:0o600,
|
|
117
|
+
// flag:'wx' creates a file world-unreadable from the start.
|
|
118
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-tgcfg-"));
|
|
119
|
+
const target = path.join(dir, "test.json");
|
|
120
|
+
await fs.writeFile(target, '{"x":1}', { mode: 0o600, flag: "wx" });
|
|
121
|
+
const stat = await fs.stat(target);
|
|
122
|
+
// The umask still applies on some platforms; we just want to confirm we're
|
|
123
|
+
// not at 0644 default.
|
|
124
|
+
assert.ok((stat.mode & 0o077) === 0, `file should not be group/world readable: mode=${(stat.mode & 0o777).toString(8)}`);
|
|
125
|
+
});
|
|
126
|
+
//# sourceMappingURL=v031.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v031.test.js","sourceRoot":"","sources":["../../src/__tests__/v031.test.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/audit.js
CHANGED
|
@@ -80,29 +80,60 @@ export class AuditLog {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
// Sensitive field names — redacted in BOTH args and result text.
|
|
84
|
+
const SECRET_FIELDS = new Set([
|
|
85
|
+
"approval_token",
|
|
86
|
+
"botToken",
|
|
87
|
+
"bot_token",
|
|
88
|
+
"token",
|
|
89
|
+
"apiKey",
|
|
90
|
+
"api_key",
|
|
91
|
+
"password",
|
|
92
|
+
"secret",
|
|
93
|
+
]);
|
|
83
94
|
function summarize(result) {
|
|
84
95
|
// MCP tool results are typically { content: [{ type: "text", text: "..." }] }.
|
|
85
|
-
// Extract the first text
|
|
96
|
+
// Extract the first text, scrub known secret fields, then truncate.
|
|
86
97
|
try {
|
|
87
98
|
const r = result;
|
|
88
99
|
const text = r?.content?.[0]?.text;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return JSON.stringify(result).slice(0, 200);
|
|
100
|
+
const raw = typeof text === "string" ? text : JSON.stringify(result);
|
|
101
|
+
return scrubSecretsInText(raw).slice(0, 200);
|
|
92
102
|
}
|
|
93
103
|
catch {
|
|
94
104
|
return String(result).slice(0, 200);
|
|
95
105
|
}
|
|
96
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Strip values of known secret fields out of a JSON-like text blob. We can't
|
|
109
|
+
* rely on a proper parse (the text may already be truncated), so we operate on
|
|
110
|
+
* the textual representation with regex. This is best-effort defense in depth
|
|
111
|
+
* — the canonical fix is to never put secrets in the user-facing content.text.
|
|
112
|
+
*/
|
|
113
|
+
function scrubSecretsInText(s) {
|
|
114
|
+
let out = s;
|
|
115
|
+
for (const k of SECRET_FIELDS) {
|
|
116
|
+
// JSON-shaped: "key": "value" → "key": "<redacted>"
|
|
117
|
+
out = out.replace(new RegExp(`("${k}"\\s*:\\s*)"[^"]*"`, "g"), `$1"<redacted>"`);
|
|
118
|
+
// YAML-ish (rare in our outputs but cheap to include): key: value
|
|
119
|
+
out = out.replace(new RegExp(`(\\b${k}\\s*[:=]\\s*)\\S+`, "g"), `$1<redacted>`);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
97
123
|
function redactSecrets(args) {
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
124
|
+
// Deep redaction: walk nested objects/arrays. Mutates a shallow copy.
|
|
125
|
+
return walk(args);
|
|
126
|
+
}
|
|
127
|
+
function walk(value) {
|
|
128
|
+
if (Array.isArray(value))
|
|
129
|
+
return value.map(walk);
|
|
130
|
+
if (value && typeof value === "object") {
|
|
131
|
+
const clone = {};
|
|
132
|
+
for (const [k, v] of Object.entries(value)) {
|
|
133
|
+
clone[k] = SECRET_FIELDS.has(k) ? "<redacted>" : walk(v);
|
|
134
|
+
}
|
|
104
135
|
return clone;
|
|
105
136
|
}
|
|
106
|
-
return
|
|
137
|
+
return value;
|
|
107
138
|
}
|
|
108
139
|
//# sourceMappingURL=audit.js.map
|
package/dist/lib/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/lib/audit.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/lib/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/state.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* orqlaude state store — single JSON file per project, atomically written
|
|
2
|
+
* orqlaude state store — single JSON file per project, atomically written
|
|
3
|
+
* with cross-process file-lock serialization (v0.3.1+).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* a
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Concurrency model:
|
|
6
|
+
* • In-process: a Promise chain (`writeLock`) serializes mutations within
|
|
7
|
+
* one Node process. Reads also funnel through it so they don't observe
|
|
8
|
+
* mid-mutation state.
|
|
9
|
+
* • Cross-process: each mutation grabs a sidecar lock file (`<dir>/lock`)
|
|
10
|
+
* via fs.open with O_CREAT|O_EXCL. Stale locks (PID no longer alive) are
|
|
11
|
+
* reclaimed on retry. This serializes orqlaude MCP-server writes against
|
|
12
|
+
* CLI / Telegram-bot writes against agent self-registrations.
|
|
13
|
+
* • Atomic writes: tmp file + rename, never partial.
|
|
14
|
+
* • Rollback: a deep snapshot is taken before each mutator runs; on throw,
|
|
15
|
+
* the in-memory cache is restored.
|
|
9
16
|
*
|
|
10
|
-
* Schema
|
|
11
|
-
* resumability hooks.
|
|
17
|
+
* Schema v2: tokens-first budgets, file claims, lifecycle hooks.
|
|
12
18
|
*/
|
|
13
19
|
export type TaskStatus = "pending" | "dispatched" | "running" | "done" | "failed" | "cancelled";
|
|
14
20
|
export type PlanStatus = "draft" | "estimating" | "awaiting_approval" | "approved" | "dispatching" | "running" | "collected" | "cancelled" | "cancelled_overbudget";
|
|
@@ -88,31 +94,38 @@ export interface State {
|
|
|
88
94
|
}
|
|
89
95
|
export declare class StateStore {
|
|
90
96
|
private filePath;
|
|
97
|
+
private lockPath;
|
|
91
98
|
private cache;
|
|
92
99
|
private writeLock;
|
|
93
100
|
constructor(stateDir: string);
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Always reload from disk under the lock to defeat cross-process staleness.
|
|
103
|
+
*/
|
|
104
|
+
private loadFresh;
|
|
105
|
+
/** Read path: still funneled through the writeLock so we never see torn
|
|
106
|
+
* state from a partially-applied mutator in this process. */
|
|
96
107
|
read<T>(reader: (state: State) => T): Promise<T>;
|
|
108
|
+
/**
|
|
109
|
+
* Mutate state under both an in-process lock and a cross-process file lock.
|
|
110
|
+
* Re-reads from disk before applying the mutator to pick up writes from
|
|
111
|
+
* other processes (Telegram bot, CLI, fresh MCP invocation). On throw,
|
|
112
|
+
* restores the in-memory cache from the pre-mutation snapshot.
|
|
113
|
+
*/
|
|
114
|
+
update<T>(mutator: (state: State) => T | Promise<T>): Promise<T>;
|
|
115
|
+
private acquireFileLock;
|
|
116
|
+
private releaseFileLock;
|
|
97
117
|
private persist;
|
|
98
118
|
}
|
|
99
119
|
export declare function newPlan(rootTask: string, budgetCapTokens: number, tasksInput: Array<Omit<Task, "id" | "status">>): Plan;
|
|
100
120
|
export declare function findPlan(state: State, planId: string): Plan;
|
|
101
121
|
export declare function findTask(plan: Plan, taskId: string): Task;
|
|
102
122
|
export declare function findTaskBySession(plan: Plan, sessionId: string): Task | undefined;
|
|
103
|
-
/** Locate the plan+task a given child session belongs to. */
|
|
104
123
|
export declare function planForSession(state: State, sessionId: string): {
|
|
105
124
|
plan: Plan;
|
|
106
125
|
task: Task;
|
|
107
126
|
} | undefined;
|
|
108
|
-
/**
|
|
109
|
-
* Find a dispatched-but-unclaimed task by task_id. Used for self-registration:
|
|
110
|
-
* when a freshly-spawned child agent calls `checkin` with its task_id (which we
|
|
111
|
-
* embed in the spawn prompt), we can adopt it.
|
|
112
|
-
*/
|
|
113
127
|
export declare function unclaimedTaskById(state: State, taskId: string): {
|
|
114
128
|
plan: Plan;
|
|
115
129
|
task: Task;
|
|
116
130
|
} | undefined;
|
|
117
|
-
/** Normalize a path for claim comparison (handle . , .. , trailing slash, case). */
|
|
118
131
|
export declare function normalizeClaimPath(p: string, cwd: string): string;
|
package/dist/lib/state.js
CHANGED
|
@@ -2,72 +2,162 @@ import { promises as fs } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
const EMPTY_STATE = { schemaVersion: 2, plans: {} };
|
|
5
|
+
const LOCK_TIMEOUT_MS = 5_000;
|
|
6
|
+
const LOCK_RETRY_BASE_MS = 30;
|
|
5
7
|
export class StateStore {
|
|
6
8
|
filePath;
|
|
9
|
+
lockPath;
|
|
7
10
|
cache = null;
|
|
8
11
|
writeLock = Promise.resolve();
|
|
9
12
|
constructor(stateDir) {
|
|
10
13
|
this.filePath = path.join(stateDir, "orqlaude-state.json");
|
|
14
|
+
this.lockPath = path.join(stateDir, "lock");
|
|
11
15
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Always reload from disk under the lock to defeat cross-process staleness.
|
|
18
|
+
*/
|
|
19
|
+
async loadFresh() {
|
|
15
20
|
try {
|
|
16
21
|
const raw = await fs.readFile(this.filePath, "utf8");
|
|
17
22
|
const parsed = JSON.parse(raw);
|
|
18
|
-
|
|
23
|
+
const state = migrate(parsed);
|
|
24
|
+
this.cache = state;
|
|
25
|
+
return state;
|
|
19
26
|
}
|
|
20
27
|
catch (err) {
|
|
21
28
|
if (err.code === "ENOENT") {
|
|
22
29
|
this.cache = structuredClone(EMPTY_STATE);
|
|
30
|
+
return this.cache;
|
|
23
31
|
}
|
|
24
|
-
|
|
25
|
-
throw err;
|
|
26
|
-
}
|
|
32
|
+
throw err;
|
|
27
33
|
}
|
|
28
|
-
return this.cache;
|
|
29
34
|
}
|
|
30
|
-
|
|
35
|
+
/** Read path: still funneled through the writeLock so we never see torn
|
|
36
|
+
* state from a partially-applied mutator in this process. */
|
|
37
|
+
async read(reader) {
|
|
31
38
|
let release = () => { };
|
|
32
39
|
const next = new Promise((res) => (release = res));
|
|
33
40
|
const prev = this.writeLock;
|
|
34
41
|
this.writeLock = prev.then(() => next);
|
|
35
42
|
await prev;
|
|
36
43
|
try {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
// Cheap path: if we have a cache, use it. Cache is always written
|
|
45
|
+
// through after a successful persist, so it reflects the last
|
|
46
|
+
// committed state.
|
|
47
|
+
const state = this.cache ?? (await this.loadFresh());
|
|
48
|
+
return reader(state);
|
|
41
49
|
}
|
|
42
50
|
finally {
|
|
43
51
|
release();
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Mutate state under both an in-process lock and a cross-process file lock.
|
|
56
|
+
* Re-reads from disk before applying the mutator to pick up writes from
|
|
57
|
+
* other processes (Telegram bot, CLI, fresh MCP invocation). On throw,
|
|
58
|
+
* restores the in-memory cache from the pre-mutation snapshot.
|
|
59
|
+
*/
|
|
60
|
+
async update(mutator) {
|
|
61
|
+
let releaseInProcess = () => { };
|
|
62
|
+
const next = new Promise((res) => (releaseInProcess = res));
|
|
63
|
+
const prev = this.writeLock;
|
|
64
|
+
this.writeLock = prev.then(() => next);
|
|
65
|
+
await prev;
|
|
66
|
+
try {
|
|
67
|
+
await this.acquireFileLock();
|
|
68
|
+
// Always reload from disk under the lock — another process may have
|
|
69
|
+
// written since we last cached.
|
|
70
|
+
const fresh = await this.loadFresh();
|
|
71
|
+
const snapshot = structuredClone(fresh);
|
|
72
|
+
try {
|
|
73
|
+
const result = await mutator(fresh);
|
|
74
|
+
await this.persist(fresh);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
// Roll back the in-memory cache to the pre-mutation snapshot so
|
|
79
|
+
// subsequent readers see the correct state.
|
|
80
|
+
this.cache = snapshot;
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
await this.releaseFileLock();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
releaseInProcess();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async acquireFileLock() {
|
|
92
|
+
await fs.mkdir(path.dirname(this.lockPath), { recursive: true });
|
|
93
|
+
const start = Date.now();
|
|
94
|
+
while (Date.now() - start < LOCK_TIMEOUT_MS) {
|
|
95
|
+
try {
|
|
96
|
+
const fh = await fs.open(this.lockPath, "wx", 0o600);
|
|
97
|
+
await fh.write(`${process.pid}\n${Date.now()}\n`);
|
|
98
|
+
await fh.close();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
if (err.code !== "EEXIST")
|
|
103
|
+
throw err;
|
|
104
|
+
// Lock exists. Check if it's stale (PID no longer alive).
|
|
105
|
+
try {
|
|
106
|
+
const held = (await fs.readFile(this.lockPath, "utf8")).split("\n")[0]?.trim();
|
|
107
|
+
const heldPid = parseInt(held ?? "", 10);
|
|
108
|
+
if (Number.isFinite(heldPid) && !isProcessAlive(heldPid)) {
|
|
109
|
+
await fs.unlink(this.lockPath).catch(() => { });
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
/* race: someone deleted it before we read. retry. */
|
|
115
|
+
}
|
|
116
|
+
await sleep(LOCK_RETRY_BASE_MS + Math.random() * LOCK_RETRY_BASE_MS);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`orqlaude: could not acquire state lock (${this.lockPath}) within ${LOCK_TIMEOUT_MS}ms`);
|
|
120
|
+
}
|
|
121
|
+
async releaseFileLock() {
|
|
122
|
+
await fs.unlink(this.lockPath).catch(() => {
|
|
123
|
+
/* already gone; not fatal */
|
|
124
|
+
});
|
|
49
125
|
}
|
|
50
126
|
async persist(state) {
|
|
51
127
|
await fs.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
52
|
-
const tmp = `${this.filePath}.${process.pid}.tmp`;
|
|
53
|
-
await fs.writeFile(tmp, JSON.stringify(state, null, 2));
|
|
128
|
+
const tmp = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
129
|
+
await fs.writeFile(tmp, JSON.stringify(state, null, 2), { mode: 0o600 });
|
|
54
130
|
await fs.rename(tmp, this.filePath);
|
|
55
131
|
this.cache = state;
|
|
56
132
|
}
|
|
57
133
|
}
|
|
134
|
+
function isProcessAlive(pid) {
|
|
135
|
+
try {
|
|
136
|
+
process.kill(pid, 0);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function sleep(ms) {
|
|
144
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
145
|
+
}
|
|
58
146
|
/** Forward-compatible migration from earlier schemas. */
|
|
59
147
|
function migrate(input) {
|
|
60
148
|
const v = input.schemaVersion ?? 1;
|
|
61
149
|
if (v === 2 && input.plans)
|
|
62
150
|
return input;
|
|
63
|
-
// v1 → v2: synthesize token fields from USD if missing.
|
|
64
151
|
const out = { schemaVersion: 2, plans: {} };
|
|
65
152
|
for (const [id, plan] of Object.entries(input.plans ?? {})) {
|
|
66
153
|
const p = plan;
|
|
67
154
|
out.plans[id] = {
|
|
68
155
|
...p,
|
|
69
|
-
budgetCapTokens: p.budgetCapTokens ?? Math.round((p.budgetCapUsd ?? 5) * 25_000),
|
|
156
|
+
budgetCapTokens: p.budgetCapTokens ?? Math.round((p.budgetCapUsd ?? 5) * 25_000),
|
|
70
157
|
perAgentCapTokens: p.perAgentCapTokens ?? Math.round((p.perAgentCapUsd ?? 1) * 25_000),
|
|
158
|
+
tasks: p.tasks ?? [],
|
|
159
|
+
notes: p.notes ?? [],
|
|
160
|
+
messages: p.messages ?? [],
|
|
71
161
|
claims: p.claims ?? [],
|
|
72
162
|
};
|
|
73
163
|
}
|
|
@@ -108,7 +198,6 @@ export function findTask(plan, taskId) {
|
|
|
108
198
|
export function findTaskBySession(plan, sessionId) {
|
|
109
199
|
return plan.tasks.find((t) => t.spawnedSessionId === sessionId);
|
|
110
200
|
}
|
|
111
|
-
/** Locate the plan+task a given child session belongs to. */
|
|
112
201
|
export function planForSession(state, sessionId) {
|
|
113
202
|
for (const plan of Object.values(state.plans)) {
|
|
114
203
|
const task = findTaskBySession(plan, sessionId);
|
|
@@ -117,11 +206,6 @@ export function planForSession(state, sessionId) {
|
|
|
117
206
|
}
|
|
118
207
|
return undefined;
|
|
119
208
|
}
|
|
120
|
-
/**
|
|
121
|
-
* Find a dispatched-but-unclaimed task by task_id. Used for self-registration:
|
|
122
|
-
* when a freshly-spawned child agent calls `checkin` with its task_id (which we
|
|
123
|
-
* embed in the spawn prompt), we can adopt it.
|
|
124
|
-
*/
|
|
125
209
|
export function unclaimedTaskById(state, taskId) {
|
|
126
210
|
for (const plan of Object.values(state.plans)) {
|
|
127
211
|
const task = plan.tasks.find((t) => t.id === taskId && !t.spawnedSessionId);
|
|
@@ -130,7 +214,6 @@ export function unclaimedTaskById(state, taskId) {
|
|
|
130
214
|
}
|
|
131
215
|
return undefined;
|
|
132
216
|
}
|
|
133
|
-
/** Normalize a path for claim comparison (handle . , .. , trailing slash, case). */
|
|
134
217
|
export function normalizeClaimPath(p, cwd) {
|
|
135
218
|
const abs = path.isAbsolute(p) ? p : path.resolve(cwd, p);
|
|
136
219
|
return path.normalize(abs);
|
package/dist/lib/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/lib/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/lib/state.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
3
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
6
|
import { StateStore } from "./lib/state.js";
|
|
@@ -13,15 +14,44 @@ import { registerReview } from "./tools/review.js";
|
|
|
13
14
|
/**
|
|
14
15
|
* orqlaude — multi-agent orchestrator for Claude Code.
|
|
15
16
|
*
|
|
16
|
-
* State and audit log live under <project>/.orqlaude/ by default.
|
|
17
|
-
*
|
|
17
|
+
* State and audit log live under <project>/.orqlaude/ by default.
|
|
18
|
+
*
|
|
19
|
+
* Worktree-aware resolution (v0.3.1+): if cwd is a git worktree (i.e. `.git`
|
|
20
|
+
* is a regular file pointing at `<main>/.git/worktrees/<name>`), the state
|
|
21
|
+
* dir resolves to `<main>/.orqlaude`. That lets spawned worktree-children
|
|
22
|
+
* share state with the parent project — critical for the broker.
|
|
23
|
+
*
|
|
24
|
+
* Override via env var ORQLAUDE_STATE_DIR.
|
|
18
25
|
*/
|
|
19
|
-
|
|
26
|
+
function resolveStateDir() {
|
|
27
|
+
if (process.env.ORQLAUDE_STATE_DIR)
|
|
28
|
+
return process.env.ORQLAUDE_STATE_DIR;
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
try {
|
|
31
|
+
const dotGit = path.join(cwd, ".git");
|
|
32
|
+
const stat = fs.statSync(dotGit);
|
|
33
|
+
if (stat.isFile()) {
|
|
34
|
+
const content = fs.readFileSync(dotGit, "utf8");
|
|
35
|
+
// Format: "gitdir: /path/to/main/.git/worktrees/<name>"
|
|
36
|
+
const m = content.match(/^gitdir:\s*(.+?)\/worktrees\/[^\/\s]+\s*$/m);
|
|
37
|
+
if (m) {
|
|
38
|
+
const mainGitDir = m[1]; // /path/to/main/.git
|
|
39
|
+
const mainCheckout = path.dirname(mainGitDir); // /path/to/main
|
|
40
|
+
return path.join(mainCheckout, ".orqlaude");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
/* no .git or not a worktree — fall through */
|
|
46
|
+
}
|
|
47
|
+
return path.join(cwd, ".orqlaude");
|
|
48
|
+
}
|
|
49
|
+
const stateDir = resolveStateDir();
|
|
20
50
|
const store = new StateStore(stateDir);
|
|
21
51
|
const audit = new AuditLog(stateDir);
|
|
22
52
|
const server = new McpServer({
|
|
23
53
|
name: "orqlaude",
|
|
24
|
-
version: "0.3.
|
|
54
|
+
version: "0.3.1",
|
|
25
55
|
});
|
|
26
56
|
registerPing(server);
|
|
27
57
|
registerPlanning(server, store, audit);
|
package/dist/server.js.map
CHANGED
|
@@ -1 +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
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,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;;;;;;;;;;;GAWG;AACH,SAAS,eAAe;IACtB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC1E,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YAClB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAChD,wDAAwD;YACxD,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;YACtE,IAAI,CAAC,EAAE,CAAC;gBACN,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAW,qBAAqB;gBACxD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB;gBAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;AACnC,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"}
|
|
@@ -26,5 +26,6 @@ export interface TelegramConfig {
|
|
|
26
26
|
declare const CONFIG_PATH: string;
|
|
27
27
|
export declare function loadConfig(): Promise<TelegramConfig>;
|
|
28
28
|
export declare function saveConfig(cfg: TelegramConfig): Promise<void>;
|
|
29
|
+
export declare function loadConfigSafe(): Promise<TelegramConfig>;
|
|
29
30
|
export declare function isAuthorized(cfg: TelegramConfig, userId: number): boolean;
|
|
30
31
|
export { CONFIG_PATH };
|
package/dist/telegram/config.js
CHANGED
|
@@ -22,11 +22,28 @@ export async function loadConfig() {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
export async function saveConfig(cfg) {
|
|
25
|
-
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
26
|
+
// v0.3.1: create the tmp file with mode 0o600 AND O_EXCL ("wx" flag) from
|
|
27
|
+
// the start. This closes the race window where the token sat at default
|
|
28
|
+
// umask (0644) between writeFile and the post-rename chmod, and defeats a
|
|
29
|
+
// symlink-swap attack on the predictable tmp path.
|
|
30
|
+
// Use a high-entropy suffix instead of just pid to further reduce
|
|
31
|
+
// predictability.
|
|
32
|
+
const suffix = `${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 10)}`;
|
|
33
|
+
const tmp = `${CONFIG_PATH}.${suffix}.tmp`;
|
|
34
|
+
await fs.writeFile(tmp, JSON.stringify(cfg, null, 2), { mode: 0o600, flag: "wx" });
|
|
28
35
|
await fs.rename(tmp, CONFIG_PATH);
|
|
29
|
-
|
|
36
|
+
}
|
|
37
|
+
export async function loadConfigSafe() {
|
|
38
|
+
// Wrapper that swallows JSON parse errors with a warning instead of
|
|
39
|
+
// crashing the bot. Useful when a partially-written config exists.
|
|
40
|
+
try {
|
|
41
|
+
return await loadConfig();
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
process.stderr.write(`[orqlaude tg] config parse failed, falling back to empty: ${err.message}\n`);
|
|
45
|
+
return { ...EMPTY };
|
|
46
|
+
}
|
|
30
47
|
}
|
|
31
48
|
export function isAuthorized(cfg, userId) {
|
|
32
49
|
if (userId === cfg.ownerId)
|
|
@@ -1 +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;
|
|
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,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,0EAA0E;IAC1E,wEAAwE;IACxE,0EAA0E;IAC1E,mDAAmD;IACnD,kEAAkE;IAClE,kBAAkB;IAClB,MAAM,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACzF,MAAM,GAAG,GAAG,GAAG,WAAW,IAAI,MAAM,MAAM,CAAC;IAC3C,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACnF,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,oEAAoE;IACpE,mEAAmE;IACnE,IAAI,CAAC;QACH,OAAO,MAAM,UAAU,EAAE,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6DAA8D,GAAa,CAAC,OAAO,IAAI,CAAC,CAAC;QAC9G,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;IACtB,CAAC;AACH,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"}
|
|
@@ -12,3 +12,17 @@ export declare class Notifier {
|
|
|
12
12
|
/** One pass: detect deltas, push notifications, advance cursor. */
|
|
13
13
|
tick(): Promise<void>;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Escape characters that Telegram MarkdownV1 treats as syntax.
|
|
17
|
+
*
|
|
18
|
+
* Per https://core.telegram.org/bots/api#markdown-style, V1's reserved set is
|
|
19
|
+
* `_ * ` ` [`. Without escaping these in user-supplied strings, an innocuous
|
|
20
|
+
* branch name like `feature/foo_bar` or a note containing a single `*` makes
|
|
21
|
+
* sendMessage 400 → notifier swallows the error → cursor already advanced →
|
|
22
|
+
* notification permanently lost.
|
|
23
|
+
*
|
|
24
|
+
* URL text (e.g. PR links) is appended raw and not run through this escaper,
|
|
25
|
+
* because backslashing chars in URLs breaks them. Keep URL fragments out of
|
|
26
|
+
* this function's input.
|
|
27
|
+
*/
|
|
28
|
+
export declare function escapeMd(s: string): string;
|
|
@@ -2,6 +2,7 @@ import { promises as fs } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { StateStore } from "../lib/state.js";
|
|
4
4
|
const EMPTY_CURSOR = {
|
|
5
|
+
initialized: false,
|
|
5
6
|
lastNoteId: null,
|
|
6
7
|
taskStatus: {},
|
|
7
8
|
planStatus: {},
|
|
@@ -38,7 +39,7 @@ export class Notifier {
|
|
|
38
39
|
if (!this.cursor)
|
|
39
40
|
return;
|
|
40
41
|
await fs.mkdir(path.dirname(this.cursorPath), { recursive: true });
|
|
41
|
-
const tmp = `${this.cursorPath}.${process.pid}.tmp`;
|
|
42
|
+
const tmp = `${this.cursorPath}.${process.pid}.${Date.now()}.tmp`;
|
|
42
43
|
await fs.writeFile(tmp, JSON.stringify(this.cursor, null, 2));
|
|
43
44
|
await fs.rename(tmp, this.cursorPath);
|
|
44
45
|
}
|
|
@@ -49,13 +50,25 @@ export class Notifier {
|
|
|
49
50
|
const cursor = await this.loadCursor();
|
|
50
51
|
const store = new StateStore(path.join(this.projectDir, ".orqlaude"));
|
|
51
52
|
const plans = await store.read((s) => Object.values(s.plans));
|
|
53
|
+
// First-tick seed: silently snapshot current state, no messages.
|
|
54
|
+
if (!cursor.initialized) {
|
|
55
|
+
for (const plan of plans) {
|
|
56
|
+
cursor.planStatus[plan.id] = plan.status;
|
|
57
|
+
for (const task of plan.tasks)
|
|
58
|
+
cursor.taskStatus[task.id] = task.status;
|
|
59
|
+
if (plan.notes.length > 0)
|
|
60
|
+
cursor.lastNoteId = plan.notes[plan.notes.length - 1].id;
|
|
61
|
+
}
|
|
62
|
+
cursor.initialized = true;
|
|
63
|
+
await this.saveCursor();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
52
66
|
const messages = [];
|
|
53
67
|
for (const plan of plans) {
|
|
54
68
|
const prevStatus = cursor.planStatus[plan.id];
|
|
55
|
-
// Plan status transitions we care about
|
|
56
69
|
if (prevStatus !== plan.status) {
|
|
57
70
|
if (!prevStatus && plan.status === "draft") {
|
|
58
|
-
messages.push(`📋 *New plan* \`${plan.id.slice(0, 8)}\` — ${plan.tasks.length} task(s)\n${truncate(plan.rootTask, 100)}`);
|
|
71
|
+
messages.push(`📋 *New plan* \`${plan.id.slice(0, 8)}\` — ${plan.tasks.length} task(s)\n${escapeMd(truncate(plan.rootTask, 100))}`);
|
|
59
72
|
}
|
|
60
73
|
else if (plan.status === "approved" && prevStatus !== "approved") {
|
|
61
74
|
messages.push(`✅ *Approved* \`${plan.id.slice(0, 8)}\` — spawning ${plan.tasks.length} agents`);
|
|
@@ -73,24 +86,23 @@ export class Notifier {
|
|
|
73
86
|
}
|
|
74
87
|
cursor.planStatus[plan.id] = plan.status;
|
|
75
88
|
}
|
|
76
|
-
// Per-task status transitions
|
|
77
89
|
for (const task of plan.tasks) {
|
|
78
90
|
const prev = cursor.taskStatus[task.id];
|
|
79
91
|
if (prev !== task.status) {
|
|
80
92
|
if (task.status === "done") {
|
|
81
93
|
const prSuffix = task.prUrl ? `\n${task.prUrl}` : "";
|
|
82
|
-
messages.push(`✓ *${truncate(task.title, 60)}* — done${prSuffix}`);
|
|
94
|
+
messages.push(`✓ *${escapeMd(truncate(task.title, 60))}* — done${prSuffix}`);
|
|
83
95
|
}
|
|
84
96
|
else if (task.status === "failed") {
|
|
85
|
-
messages.push(`❌ *${truncate(task.title, 60)}* — failed${task.exitReason ? `\n${task.exitReason}` : ""}`);
|
|
97
|
+
messages.push(`❌ *${escapeMd(truncate(task.title, 60))}* — failed${task.exitReason ? `\n${escapeMd(task.exitReason)}` : ""}`);
|
|
86
98
|
}
|
|
87
99
|
else if (task.status === "cancelled") {
|
|
88
|
-
messages.push(`🛑 *${truncate(task.title, 60)}* — cancelled`);
|
|
100
|
+
messages.push(`🛑 *${escapeMd(truncate(task.title, 60))}* — cancelled`);
|
|
89
101
|
}
|
|
90
102
|
cursor.taskStatus[task.id] = task.status;
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
|
-
// New notes
|
|
105
|
+
// New notes after the last-seen id.
|
|
94
106
|
let foundLast = cursor.lastNoteId === null;
|
|
95
107
|
for (const note of plan.notes) {
|
|
96
108
|
if (!foundLast) {
|
|
@@ -100,22 +112,12 @@ export class Notifier {
|
|
|
100
112
|
}
|
|
101
113
|
const taskTitle = plan.tasks.find((t) => t.id === note.taskId)?.title ?? note.taskId.slice(0, 8);
|
|
102
114
|
const blocking = note.blocking ? " 🟡 blocking" : "";
|
|
103
|
-
messages.push(`📝 *${truncate(taskTitle, 50)}*${blocking}\n${truncate(note.text, 300)}${note.prUrl ? `\n${note.prUrl}` : ""}`);
|
|
115
|
+
messages.push(`📝 *${escapeMd(truncate(taskTitle, 50))}*${blocking}\n${escapeMd(truncate(note.text, 300))}${note.prUrl ? `\n${note.prUrl}` : ""}`);
|
|
104
116
|
cursor.lastNoteId = note.id;
|
|
105
117
|
}
|
|
106
|
-
// If we never had a cursor, seed it to the last note so we don't blast history.
|
|
107
|
-
if (cursor.lastNoteId === null && plan.notes.length > 0) {
|
|
108
|
-
cursor.lastNoteId = plan.notes[plan.notes.length - 1].id;
|
|
109
|
-
}
|
|
110
118
|
}
|
|
111
|
-
|
|
112
|
-
// still save cursor in case statuses changed but no message produced
|
|
113
|
-
await this.saveCursor();
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
// Save cursor BEFORE sending, to avoid resends on partial failures.
|
|
119
|
+
// Save cursor BEFORE sending so a failed send won't re-spam on retry.
|
|
117
120
|
await this.saveCursor();
|
|
118
|
-
// Push.
|
|
119
121
|
for (const entry of this.cfg.whitelist) {
|
|
120
122
|
for (const msg of messages) {
|
|
121
123
|
try {
|
|
@@ -128,6 +130,22 @@ export class Notifier {
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Escape characters that Telegram MarkdownV1 treats as syntax.
|
|
135
|
+
*
|
|
136
|
+
* Per https://core.telegram.org/bots/api#markdown-style, V1's reserved set is
|
|
137
|
+
* `_ * ` ` [`. Without escaping these in user-supplied strings, an innocuous
|
|
138
|
+
* branch name like `feature/foo_bar` or a note containing a single `*` makes
|
|
139
|
+
* sendMessage 400 → notifier swallows the error → cursor already advanced →
|
|
140
|
+
* notification permanently lost.
|
|
141
|
+
*
|
|
142
|
+
* URL text (e.g. PR links) is appended raw and not run through this escaper,
|
|
143
|
+
* because backslashing chars in URLs breaks them. Keep URL fragments out of
|
|
144
|
+
* this function's input.
|
|
145
|
+
*/
|
|
146
|
+
export function escapeMd(s) {
|
|
147
|
+
return s.replace(/([_*`\[])/g, "\\$1");
|
|
148
|
+
}
|
|
131
149
|
function truncate(s, n) {
|
|
132
150
|
if (s.length <= n)
|
|
133
151
|
return s;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifier.js","sourceRoot":"","sources":["../../src/telegram/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAa,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"notifier.js","sourceRoot":"","sources":["../../src/telegram/notifier.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/ping.js
CHANGED
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synaplink/orqlaude",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Multi-agent orchestrator for Claude Code. One primary session decomposes a complex task, gets a single budget approval, then dispatches N parallel agents via the Desktop app's native spawn_task. Tracks cost/status via JSONL tails; brokers messages between agents; detects hallucination; manages PRs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"orqlaude": "./dist/cli.js"
|
|
7
|
+
"orqlaude": "./dist/cli.js",
|
|
8
|
+
"orqlaude-mcp": "./dist/server.js"
|
|
8
9
|
},
|
|
9
10
|
"main": "./dist/server.js",
|
|
10
11
|
"files": [
|
|
11
12
|
"dist",
|
|
12
13
|
"README.md",
|
|
14
|
+
"LICENSE",
|
|
13
15
|
".mcp.json.template"
|
|
14
16
|
],
|
|
15
17
|
"scripts": {
|
|
@@ -17,7 +19,7 @@
|
|
|
17
19
|
"dev": "tsc --watch",
|
|
18
20
|
"start": "node dist/server.js",
|
|
19
21
|
"typecheck": "tsc --noEmit",
|
|
20
|
-
"test": "
|
|
22
|
+
"test": "npm run build && node --test dist/__tests__/*.test.js",
|
|
21
23
|
"prepublishOnly": "npm run build"
|
|
22
24
|
},
|
|
23
25
|
"keywords": [
|