@synaplink/orqlaude 0.5.2 → 0.5.6

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.
Files changed (45) hide show
  1. package/README.md +25 -6
  2. package/dist/__tests__/desktop_config.test.d.ts +1 -0
  3. package/dist/__tests__/desktop_config.test.js +110 -0
  4. package/dist/__tests__/desktop_config.test.js.map +1 -0
  5. package/dist/__tests__/paths.test.d.ts +1 -0
  6. package/dist/__tests__/paths.test.js +55 -0
  7. package/dist/__tests__/paths.test.js.map +1 -0
  8. package/dist/__tests__/v05.test.js +6 -2
  9. package/dist/__tests__/v05.test.js.map +1 -1
  10. package/dist/__tests__/v053.test.d.ts +1 -0
  11. package/dist/__tests__/v053.test.js +89 -0
  12. package/dist/__tests__/v053.test.js.map +1 -0
  13. package/dist/cli.js +118 -0
  14. package/dist/cli.js.map +1 -1
  15. package/dist/lib/desktop_config.d.ts +66 -0
  16. package/dist/lib/desktop_config.js +126 -0
  17. package/dist/lib/desktop_config.js.map +1 -0
  18. package/dist/lib/spawn_cli.d.ts +79 -0
  19. package/dist/lib/spawn_cli.js +211 -0
  20. package/dist/lib/spawn_cli.js.map +1 -0
  21. package/dist/lib/state.d.ts +10 -6
  22. package/dist/lib/state.js.map +1 -1
  23. package/dist/lib/state_dir.d.ts +15 -0
  24. package/dist/lib/state_dir.js +56 -1
  25. package/dist/lib/state_dir.js.map +1 -1
  26. package/dist/lib/telegram_status.d.ts +24 -0
  27. package/dist/lib/telegram_status.js +87 -0
  28. package/dist/lib/telegram_status.js.map +1 -0
  29. package/dist/server.js +1 -1
  30. package/dist/telegram/api.d.ts +0 -20
  31. package/dist/telegram/api.js +0 -32
  32. package/dist/telegram/api.js.map +1 -1
  33. package/dist/telegram/bot.js +56 -6
  34. package/dist/telegram/bot.js.map +1 -1
  35. package/dist/telegram/notifier.js +10 -98
  36. package/dist/telegram/notifier.js.map +1 -1
  37. package/dist/tools/broker.js +47 -1
  38. package/dist/tools/broker.js.map +1 -1
  39. package/dist/tools/dispatch.js +74 -20
  40. package/dist/tools/dispatch.js.map +1 -1
  41. package/dist/tools/ping.js +16 -2
  42. package/dist/tools/ping.js.map +1 -1
  43. package/dist/tools/userio.js +40 -9
  44. package/dist/tools/userio.js.map +1 -1
  45. package/package.json +1 -1
package/README.md CHANGED
@@ -44,20 +44,39 @@ orqlaude is the thin layer that adds those things. It never spawns processes its
44
44
  npm install -g @synaplink/orqlaude # CLI + MCP server
45
45
  ```
46
46
 
47
- In your project, add to `.mcp.json` (or copy `.mcp.json.template`):
47
+ Then wire it into Claude Desktop's MCP config in one command:
48
+
49
+ ```sh
50
+ cd /path/to/your/project
51
+ orql setup
52
+ ```
53
+
54
+ `orql setup` **patches** Claude Desktop's `claude_desktop_config.json` in place — adds an `orqlaude` MCP server entry pointed at this project's state dir, and **preserves every other server you have configured** (lm-studio, etc.) plus the entire `preferences` block. Writes a timestamped `.bak` before changing anything. Re-runs are idempotent — if the entry is already correct, it reports `already correct; nothing to do` and exits without touching the file.
55
+
56
+ Flags:
57
+ - `--state-dir <path>` override the default (which walks up from cwd for a `.git`)
58
+ - `--config-path <path>` override Claude Desktop's config path
59
+ - `--yes` skip prompts
60
+
61
+ Fully quit and relaunch Claude Desktop after running. The `mcp__orqlaude__*` tools will then appear in your sessions.
62
+
63
+ If you'd rather edit the config yourself, the entry should look like:
48
64
 
49
65
  ```json
50
66
  {
51
67
  "mcpServers": {
52
68
  "orqlaude": {
53
69
  "command": "npx",
54
- "args": ["-y", "@synaplink/orqlaude"]
70
+ "args": ["-y", "-p", "@synaplink/orqlaude", "orqlaude-mcp"],
71
+ "env": {
72
+ "ORQLAUDE_STATE_DIR": "/absolute/path/to/your/project/.orqlaude"
73
+ }
55
74
  }
56
75
  }
57
76
  }
58
77
  ```
59
78
 
60
- Restart your Claude Code session. The `mcp__orqlaude__*` tools will appear.
79
+ The `ORQLAUDE_STATE_DIR` env var is important — it pins state to a specific path so the MCP server (running with `cwd=/` on some hosts) and the Telegram bot (running in your project) share the same state file.
61
80
 
62
81
  ## Spawning Agnets: which tool to use
63
82
 
@@ -132,13 +151,13 @@ Without a running `orqlaude tg start`, `notify_user` queues silently, `request_u
132
151
 
133
152
  #### Streaming transport
134
153
 
135
- orqlaude streams via Telegram's `sendMessageDraft` endpoint (the native streaming preview API, intended for agent output). Drafts are ephemeral (~30s) and updates that share the same `draft_id` are animated client-side. When a stream ends, orqlaude follows up with a `sendMessage` to persist the final content as a normal chat message with a `✓` marker.
154
+ orqlaude streams by opening a single Telegram message with `sendMessage`, then calling `editMessageText` on each append. The final `stream_to_user_end` does one more edit appending a `✓` marker.
136
155
 
137
- If the bot's Telegram server doesn't yet expose `sendMessageDraft` (older deployments), orqlaude falls back to a single `sendMessage` + repeated `editMessageText` per stream. The fallback is transparent you don't need to do anything; the notifier flips a `transport: "edit"` flag on the stream and continues.
156
+ (v0.5.1 briefly used `sendMessageDraft` for animated, ephemeral previews reverted in v0.5.4 after that endpoint proved unreliable in the standard Bot API.)
138
157
 
139
158
  Limits to know:
140
159
  - A Telegram message tops out at 4096 chars. orqlaude caps stream content at 3800 to leave room for the title + completion marker; further appends are silently truncated.
141
- - The draft path throttles at 400 ms between updates per stream; the edit fallback throttles at 1.5 s (matching the Bot API rate limit for `editMessageText`).
160
+ - Edits are rate-limited (~1/sec per message); orqlaude throttles to 1.5 s between edits per stream.
142
161
  - If you need to stream more than 4 kb of output, start a new stream when you're approaching the cap.
143
162
 
144
163
  ### Health
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,110 @@
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 { planPatch, buildOrqlaudeEntry, readDesktopConfig, writeDesktopConfigAtomic, findDesktopConfigPath, } from "../lib/desktop_config.js";
7
+ /** v0.5.5: regression tests for the desktop_config patcher. */
8
+ const STATE_DIR = "/Users/matthew/Documents/orqlaude/.orqlaude";
9
+ test("planPatch with no existing config creates one + adds orqlaude entry", () => {
10
+ const result = planPatch(null, STATE_DIR);
11
+ assert.equal(result.action, "create-config");
12
+ assert.equal(result.before, null);
13
+ assert.equal(result.after.command, "npx");
14
+ assert.deepEqual(result.after.args, ["-y", "-p", "@synaplink/orqlaude", "orqlaude-mcp"]);
15
+ assert.equal(result.after.env?.ORQLAUDE_STATE_DIR, STATE_DIR);
16
+ });
17
+ test("planPatch with existing config but no orqlaude key adds it without touching others", () => {
18
+ const existing = {
19
+ mcpServers: {
20
+ "lm-studio": {
21
+ command: "npx",
22
+ args: ["-y", "@mzxrai/mcp-openai"],
23
+ env: { OPENAI_API_KEY: "lm-studio" },
24
+ },
25
+ },
26
+ preferences: { someFlag: true },
27
+ };
28
+ const result = planPatch(existing, STATE_DIR);
29
+ assert.equal(result.action, "create-server");
30
+ // Original is preserved
31
+ assert.deepEqual(result.config.mcpServers["lm-studio"], existing.mcpServers["lm-studio"]);
32
+ assert.deepEqual(result.config.preferences, { someFlag: true });
33
+ // New entry present
34
+ assert.equal(result.config.mcpServers["orqlaude"].env?.ORQLAUDE_STATE_DIR, STATE_DIR);
35
+ });
36
+ test("planPatch with correct orqlaude entry already present → noop", () => {
37
+ const existing = {
38
+ mcpServers: { orqlaude: buildOrqlaudeEntry(STATE_DIR) },
39
+ };
40
+ const result = planPatch(existing, STATE_DIR);
41
+ assert.equal(result.action, "noop");
42
+ });
43
+ test("planPatch with orqlaude entry at wrong state dir → update; preserve other env keys", () => {
44
+ const existing = {
45
+ mcpServers: {
46
+ orqlaude: {
47
+ command: "npx",
48
+ args: ["-y", "-p", "@synaplink/orqlaude", "orqlaude-mcp"],
49
+ env: { ORQLAUDE_STATE_DIR: "/old/path", CUSTOM_DEBUG_FLAG: "1" },
50
+ },
51
+ },
52
+ };
53
+ const result = planPatch(existing, STATE_DIR);
54
+ assert.equal(result.action, "update-server");
55
+ // New state dir applied
56
+ assert.equal(result.after.env?.ORQLAUDE_STATE_DIR, STATE_DIR);
57
+ // Custom env key preserved
58
+ assert.equal(result.after.env?.CUSTOM_DEBUG_FLAG, "1");
59
+ });
60
+ test("planPatch preserves top-level keys we don't recognize", () => {
61
+ const existing = {
62
+ mcpServers: {},
63
+ preferences: { x: 1 },
64
+ futureField: { whatever: true },
65
+ };
66
+ const result = planPatch(existing, STATE_DIR);
67
+ assert.equal(result.action, "create-server");
68
+ assert.deepEqual(result.config.preferences, { x: 1 });
69
+ assert.deepEqual(result.config.futureField, { whatever: true });
70
+ });
71
+ test("writeDesktopConfigAtomic creates a timestamped backup when the file exists", async () => {
72
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-desktop-"));
73
+ const filePath = path.join(dir, "config.json");
74
+ await fs.writeFile(filePath, JSON.stringify({ original: true }, null, 2));
75
+ const { backupPath } = await writeDesktopConfigAtomic(filePath, { updated: true });
76
+ assert.ok(backupPath, "expected a backup path");
77
+ const backed = JSON.parse(await fs.readFile(backupPath, "utf8"));
78
+ assert.deepEqual(backed, { original: true });
79
+ const updated = JSON.parse(await fs.readFile(filePath, "utf8"));
80
+ assert.deepEqual(updated, { updated: true });
81
+ });
82
+ test("writeDesktopConfigAtomic creates parent dirs + no backup on fresh file", async () => {
83
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-desktop-"));
84
+ const filePath = path.join(dir, "nested", "deep", "config.json");
85
+ const { backupPath } = await writeDesktopConfigAtomic(filePath, { fresh: true });
86
+ assert.equal(backupPath, null);
87
+ const got = JSON.parse(await fs.readFile(filePath, "utf8"));
88
+ assert.deepEqual(got, { fresh: true });
89
+ });
90
+ test("readDesktopConfig returns null for missing files; throws on malformed JSON", async () => {
91
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-desktop-"));
92
+ const filePath = path.join(dir, "config.json");
93
+ assert.equal(await readDesktopConfig(filePath), null);
94
+ await fs.writeFile(filePath, "{ not valid json,,,");
95
+ await assert.rejects(() => readDesktopConfig(filePath), /Failed to parse/);
96
+ });
97
+ test("findDesktopConfigPath returns a platform-appropriate path", () => {
98
+ const p = findDesktopConfigPath();
99
+ assert.ok(p.endsWith("claude_desktop_config.json"), `got: ${p}`);
100
+ if (process.platform === "darwin") {
101
+ assert.ok(p.includes("Library/Application Support/Claude"), `got: ${p}`);
102
+ }
103
+ else if (process.platform === "win32") {
104
+ assert.ok(p.includes("Claude"), `got: ${p}`);
105
+ }
106
+ else {
107
+ assert.ok(p.includes(".config/Claude"), `got: ${p}`);
108
+ }
109
+ });
110
+ //# sourceMappingURL=desktop_config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop_config.test.js","sourceRoot":"","sources":["../../src/__tests__/desktop_config.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
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 { pathsEquivalent } from "../lib/state_dir.js";
7
+ /** v0.5.6: pathsEquivalent should not be fooled by case or symlinks. */
8
+ test("pathsEquivalent: identical strings", () => {
9
+ assert.equal(pathsEquivalent("/a/b/c", "/a/b/c"), true);
10
+ });
11
+ test("pathsEquivalent: normalized but identical", () => {
12
+ assert.equal(pathsEquivalent("/a/b/c", "/a/b/./c"), true);
13
+ assert.equal(pathsEquivalent("/a/b/c", "/a/b/d/../c"), true);
14
+ });
15
+ test("pathsEquivalent: same dir via symlink (when target exists)", async () => {
16
+ if (process.platform === "win32")
17
+ return; // skip on win
18
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-paths-"));
19
+ const real = path.join(dir, "real");
20
+ await fs.mkdir(real);
21
+ const link = path.join(dir, "link");
22
+ await fs.symlink(real, link);
23
+ assert.equal(pathsEquivalent(real, link), true);
24
+ });
25
+ test("pathsEquivalent: case-insensitive on macOS/Windows for paths that exist", async () => {
26
+ if (process.platform !== "darwin" && process.platform !== "win32")
27
+ return;
28
+ // Real-world case: user's PWD has lowercase 'documents' but the dir on
29
+ // disk is 'Documents'. Both refer to the same inode on case-insensitive
30
+ // filesystems.
31
+ const home = os.homedir();
32
+ if (path.basename(home) === "")
33
+ return;
34
+ // Simple check: if a known path exists with one case, the other case
35
+ // should resolve equivalent.
36
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-case-"));
37
+ const real = path.join(dir, "Mixed");
38
+ await fs.mkdir(real);
39
+ const swapped = path.join(dir, "mixed");
40
+ // The case-insensitive fast path: even if one variant doesn't exist
41
+ // as a distinct dir, normalizing + lowercasing should match.
42
+ assert.equal(pathsEquivalent(real, swapped), true);
43
+ });
44
+ test("pathsEquivalent: not fooled into matching genuinely different paths", () => {
45
+ assert.equal(pathsEquivalent("/a/b/c", "/a/b/d"), false);
46
+ assert.equal(pathsEquivalent("/foo", "/bar"), false);
47
+ });
48
+ test("pathsEquivalent: paths whose leaves don't yet exist still compare", () => {
49
+ const home = os.homedir();
50
+ // Same parent (exists), different leaf — should differ.
51
+ assert.equal(pathsEquivalent(path.join(home, "nope-a-xyz"), path.join(home, "nope-b-xyz")), false);
52
+ // Same leaf, same parent — should match even though the leaf doesn't exist.
53
+ assert.equal(pathsEquivalent(path.join(home, "nope-xyz", ".orqlaude"), path.join(home, "nope-xyz", ".orqlaude")), true);
54
+ });
55
+ //# sourceMappingURL=paths.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.test.js","sourceRoot":"","sources":["../../src/__tests__/paths.test.ts"],"names":[],"mappings":"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"}
@@ -72,9 +72,13 @@ test("v0.5 schema: newPlan initializes userStreams empty", async () => {
72
72
  });
73
73
  assert.deepEqual(plan.userStreams, []);
74
74
  });
75
- test("TelegramApi.sendMessageDraft rejects draft_id=0 (v0.5.1)", async () => {
75
+ test("TelegramApi has the expected post-v0.5.4 surface", async () => {
76
+ // v0.5.4 removed sendMessageDraft after sendMessage proved unreliable in
77
+ // the standard Bot API. Confirm it's no longer exported.
76
78
  const { TelegramApi } = await import("../telegram/api.js");
77
79
  const api = new TelegramApi("test:token");
78
- await assert.rejects(() => api.sendMessageDraft(123, 0, "hi"), /must be a non-zero integer/);
80
+ assert.equal(typeof api.sendMessage, "function");
81
+ assert.equal(typeof api.editMessageText, "function");
82
+ assert.equal(api.sendMessageDraft, undefined);
79
83
  });
80
84
  //# sourceMappingURL=v05.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"v05.test.js","sourceRoot":"","sources":["../../src/__tests__/v05.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"v05.test.js","sourceRoot":"","sources":["../../src/__tests__/v05.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtE;;GAEG;AAEH,KAAK,UAAU,QAAQ;IACrB,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IACtE,OAAO,EAAE,KAAK,EAAE,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;AAC7C,CAAC;AAED,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,EAAE,GAAG,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAC9D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,6CAA6C,IAAI,EAAE,CAAC,CAAC;QACjF,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8DAA8D,EAAE,GAAG,EAAE;IACxE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;IACpE,yEAAyE;IACzE,0EAA0E;IAC1E,gEAAgE;IAChE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;IAC9D,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC;IACnC,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACvB,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC9E,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE,sCAAsC;YAC1C,OAAO,EAAE,UAAU;YACnB,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,QAAQ;YAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC,CAAC;QACH,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;IACH,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,EAAE,sCAAsC,CAAC,CAAC,CAAC;IAC9F,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACd,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IAClE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACd,MAAM,CAAC,KAAK,CAAC,EAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;IACpE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACpC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC9E,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;IAClE,yEAAyE;IACzE,yDAAyD;IACzD,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,YAAY,CAAC,CAAC;IAC1C,MAAM,CAAC,KAAK,CAAC,OAAQ,GAA2C,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAC1F,MAAM,CAAC,KAAK,CAAC,OAAQ,GAA+C,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAClG,MAAM,CAAC,KAAK,CAAE,GAAiD,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;AAC/F,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,89 @@
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, planForSession, unclaimedTaskById } from "../lib/state.js";
7
+ /**
8
+ * v0.5.3: regression tests for checkin conflict detection. We test the
9
+ * underlying state lookup primitives rather than the MCP tool itself —
10
+ * the conflict-detection branches in broker.ts use exactly these helpers.
11
+ */
12
+ async function tmpStore() {
13
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-v053-"));
14
+ return new StateStore(dir);
15
+ }
16
+ test("v0.5.3: session bound to task A cannot be retargeted by passing task B's id", async () => {
17
+ const store = await tmpStore();
18
+ let planId = "";
19
+ let taskAId = "";
20
+ let taskBId = "";
21
+ await store.update((s) => {
22
+ const p = newPlan("root", 100_000, [
23
+ { title: "A", prompt: "a", tldr: "a" },
24
+ { title: "B", prompt: "b", tldr: "b" },
25
+ ]);
26
+ p.tasks[0].status = "dispatched";
27
+ p.tasks[0].spawnedSessionId = "session-X";
28
+ p.tasks[1].status = "dispatched";
29
+ planId = p.id;
30
+ taskAId = p.tasks[0].id;
31
+ taskBId = p.tasks[1].id;
32
+ s.plans[p.id] = p;
33
+ });
34
+ // Caller session X is already bound to task A. If they pass task_id=B,
35
+ // planForSession should return A — not silently switch them to B.
36
+ const result = await store.read((state) => {
37
+ const found = planForSession(state, "session-X");
38
+ return found ? { taskId: found.task.id, title: found.task.title } : null;
39
+ });
40
+ assert.ok(result, "session should be found");
41
+ assert.equal(result.taskId, taskAId);
42
+ assert.notEqual(result.taskId, taskBId);
43
+ });
44
+ test("v0.5.3: task already claimed by another session cannot be re-claimed via task_id", async () => {
45
+ const store = await tmpStore();
46
+ let taskId = "";
47
+ await store.update((s) => {
48
+ const p = newPlan("root", 100_000, [{ title: "T", prompt: "p", tldr: "tl" }]);
49
+ p.tasks[0].status = "running";
50
+ p.tasks[0].spawnedSessionId = "first-session";
51
+ taskId = p.tasks[0].id;
52
+ s.plans[p.id] = p;
53
+ });
54
+ // A different session calls checkin with that task_id.
55
+ // unclaimedTaskById should return undefined because the task IS claimed.
56
+ const target = await store.read((state) => unclaimedTaskById(state, taskId));
57
+ assert.equal(target, undefined);
58
+ });
59
+ test("v0.5.3: spawn_cli util discovers git root via .git directory", async () => {
60
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-git-root-"));
61
+ await fs.mkdir(path.join(dir, ".git"));
62
+ await fs.mkdir(path.join(dir, "nested", "deeper"), { recursive: true });
63
+ const { findGitRoot } = await import("../lib/spawn_cli.js");
64
+ const real = await fs.realpath(dir);
65
+ assert.equal(findGitRoot(path.join(real, "nested", "deeper")), real);
66
+ });
67
+ test("v0.5.3: telegram_status returns 'unconfigured' when no config file exists", async () => {
68
+ const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-tg-status-"));
69
+ const { probeTelegramStatus } = await import("../lib/telegram_status.js");
70
+ // Override HOME so it can't find a real config.
71
+ const realHome = process.env.HOME;
72
+ const fakeHome = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-fakehome-"));
73
+ process.env.HOME = fakeHome;
74
+ try {
75
+ const status = await probeTelegramStatus(stateDir);
76
+ // We can't be certain the user has no real config — but the path under
77
+ // a freshly-minted fake HOME doesn't exist, so status should be
78
+ // unconfigured.
79
+ assert.equal(status.status, "unconfigured");
80
+ assert.equal(status.hasToken, false);
81
+ }
82
+ finally {
83
+ if (realHome)
84
+ process.env.HOME = realHome;
85
+ else
86
+ delete process.env.HOME;
87
+ }
88
+ });
89
+ //# sourceMappingURL=v053.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v053.test.js","sourceRoot":"","sources":["../../src/__tests__/v053.test.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import path from "node:path";
2
3
  import readline from "node:readline/promises";
3
4
  import { stdin, stdout } from "node:process";
4
5
  import { StateStore } from "./lib/state.js";
@@ -10,6 +11,8 @@ import { runBot } from "./telegram/bot.js";
10
11
  import { resolveStateDir } from "./lib/state_dir.js";
11
12
  import { style, styleStatus, banner } from "./lib/style.js";
12
13
  import { agnetLabel } from "./lib/agnet.js";
14
+ import { findDesktopConfigPath, readDesktopConfig, planPatch, writeDesktopConfigAtomic, } from "./lib/desktop_config.js";
15
+ import { existsSync } from "node:fs";
13
16
  /**
14
17
  * orqlaude CLI — read-only inspection of state + audit log + Telegram setup
15
18
  * and run.
@@ -40,6 +43,8 @@ async function main() {
40
43
  return await cmdHistory(parseLimit(rest));
41
44
  case "where":
42
45
  return cmdWhere();
46
+ case "setup":
47
+ return await cmdSetup(rest);
43
48
  case "tg":
44
49
  return await cmdTg(rest);
45
50
  default:
@@ -51,6 +56,10 @@ async function main() {
51
56
  function printHelp() {
52
57
  console.log(banner());
53
58
  console.log("");
59
+ console.log(style.bold(style.cream("Setup")));
60
+ console.log(` ${style.coral("orql setup")} ${style.sand("[--state-dir PATH] [--yes]")}`);
61
+ console.log(` Wire orqlaude into Claude Desktop's MCP config (idempotent, preserves other servers)`);
62
+ console.log("");
54
63
  console.log(style.bold(style.cream("Inspection")));
55
64
  console.log(` ${style.coral("orqlaude list")} List every plan in this project`);
56
65
  console.log(` ${style.coral("orqlaude status")} ${style.sand("<plan_id>")} Refreshed status of one plan`);
@@ -154,6 +163,115 @@ async function cmdShow(planId) {
154
163
  return 1;
155
164
  }
156
165
  }
166
+ // ---- setup -----------------------------------------------------------------
167
+ /**
168
+ * Detect a sensible default state dir for the current shell. Walks up from
169
+ * cwd for a `.git`; falls back to cwd.
170
+ */
171
+ function defaultStateDir() {
172
+ let dir = process.cwd();
173
+ for (let i = 0; i < 10; i++) {
174
+ if (existsSync(path.join(dir, ".git"))) {
175
+ return path.join(dir, ".orqlaude");
176
+ }
177
+ const parent = path.dirname(dir);
178
+ if (parent === dir)
179
+ break;
180
+ dir = parent;
181
+ }
182
+ return path.join(process.cwd(), ".orqlaude");
183
+ }
184
+ async function cmdSetup(args) {
185
+ console.log(banner());
186
+ console.log("");
187
+ console.log(style.bold(style.cream("Setup")));
188
+ console.log("Wires orqlaude into Claude Desktop's MCP config.");
189
+ console.log("");
190
+ // Parse flags
191
+ const stateDirIdx = args.indexOf("--state-dir");
192
+ const overrideStateDir = stateDirIdx !== -1 ? args[stateDirIdx + 1] : null;
193
+ const yes = args.includes("--yes") || args.includes("-y");
194
+ const configPathIdx = args.indexOf("--config-path");
195
+ const configPath = configPathIdx !== -1 ? args[configPathIdx + 1] : findDesktopConfigPath();
196
+ console.log(` ${style.sand("config:")} ${configPath}`);
197
+ // 1. Read existing config (preserve everything we don't own).
198
+ let existing;
199
+ try {
200
+ existing = await readDesktopConfig(configPath);
201
+ }
202
+ catch (err) {
203
+ console.error("");
204
+ console.error(style.crimson(`✗ ${err.message}`));
205
+ return 1;
206
+ }
207
+ if (!existing) {
208
+ console.log(` ${style.sand("status:")} ${style.crimson("config file does not exist — will create")}`);
209
+ }
210
+ else {
211
+ const otherServerCount = Object.keys(existing.mcpServers ?? {}).filter((k) => k !== "orqlaude").length;
212
+ const orqEntry = existing.mcpServers?.orqlaude;
213
+ const has = orqEntry ? style.coral("orqlaude entry present") : style.sand("no orqlaude entry yet");
214
+ console.log(` ${style.sand("status:")} ${has}, ${otherServerCount} other server(s) preserved`);
215
+ }
216
+ // 2. Pick state dir.
217
+ let stateDir = overrideStateDir;
218
+ if (!stateDir) {
219
+ const suggested = defaultStateDir();
220
+ if (yes) {
221
+ stateDir = suggested;
222
+ }
223
+ else {
224
+ const rl = readline.createInterface({ input: stdin, output: stdout });
225
+ const answer = (await rl.question(`\n ${style.coral("?")} state dir (where plans/audit live) [${suggested}]: `)).trim();
226
+ rl.close();
227
+ stateDir = answer || suggested;
228
+ }
229
+ }
230
+ stateDir = path.resolve(stateDir);
231
+ // 3. Compute patch.
232
+ const plan = planPatch(existing, stateDir);
233
+ console.log("");
234
+ console.log(style.bold(style.cream("Plan")));
235
+ switch (plan.action) {
236
+ case "noop":
237
+ console.log(` ${style.coral("✓")} already correct; nothing to do`);
238
+ return 0;
239
+ case "create-config":
240
+ console.log(` ${style.coral("→")} create ${configPath} with one server (orqlaude → ${stateDir})`);
241
+ break;
242
+ case "create-server":
243
+ console.log(` ${style.coral("→")} add orqlaude entry (→ ${stateDir}); preserve existing servers + preferences`);
244
+ break;
245
+ case "update-server":
246
+ console.log(` ${style.coral("→")} update orqlaude entry`);
247
+ console.log(` ${style.sand("before:")} ${JSON.stringify(plan.before)}`);
248
+ console.log(` ${style.sand("after:")} ${JSON.stringify(plan.after)}`);
249
+ break;
250
+ }
251
+ // 4. Confirm + write.
252
+ if (!yes) {
253
+ const rl2 = readline.createInterface({ input: stdin, output: stdout });
254
+ const ans = (await rl2.question(`\n ${style.coral("?")} apply? [Y/n] `)).trim().toLowerCase();
255
+ rl2.close();
256
+ if (ans && ans !== "y" && ans !== "yes") {
257
+ console.log(style.crimson(" ✗ cancelled"));
258
+ return 1;
259
+ }
260
+ }
261
+ const { backupPath } = await writeDesktopConfigAtomic(configPath, plan.config);
262
+ console.log("");
263
+ console.log(style.bold(style.coral("Done.")));
264
+ console.log(` ${style.sand("config:")} ${configPath}`);
265
+ if (backupPath)
266
+ console.log(` ${style.sand("backup:")} ${backupPath}`);
267
+ console.log(` ${style.sand("state dir:")} ${stateDir}`);
268
+ console.log("");
269
+ console.log(style.dim("Next steps:"));
270
+ console.log(style.dim(" 1. Fully quit Claude Desktop (Cmd-Q on macOS) and relaunch."));
271
+ console.log(style.dim(" 2. Open a session in this project. Call `mcp__orqlaude__ping` — it should report state_dir_source:'env'."));
272
+ console.log(style.dim(" 3. (Optional) Set up the Telegram bot: `orql tg setup`."));
273
+ return 0;
274
+ }
157
275
  function cmdWhere() {
158
276
  console.log(banner());
159
277
  console.log("");