@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.
- package/README.md +25 -6
- package/dist/__tests__/desktop_config.test.d.ts +1 -0
- package/dist/__tests__/desktop_config.test.js +110 -0
- package/dist/__tests__/desktop_config.test.js.map +1 -0
- package/dist/__tests__/paths.test.d.ts +1 -0
- package/dist/__tests__/paths.test.js +55 -0
- package/dist/__tests__/paths.test.js.map +1 -0
- package/dist/__tests__/v05.test.js +6 -2
- package/dist/__tests__/v05.test.js.map +1 -1
- package/dist/__tests__/v053.test.d.ts +1 -0
- package/dist/__tests__/v053.test.js +89 -0
- package/dist/__tests__/v053.test.js.map +1 -0
- package/dist/cli.js +118 -0
- package/dist/cli.js.map +1 -1
- package/dist/lib/desktop_config.d.ts +66 -0
- package/dist/lib/desktop_config.js +126 -0
- package/dist/lib/desktop_config.js.map +1 -0
- package/dist/lib/spawn_cli.d.ts +79 -0
- package/dist/lib/spawn_cli.js +211 -0
- package/dist/lib/spawn_cli.js.map +1 -0
- package/dist/lib/state.d.ts +10 -6
- package/dist/lib/state.js.map +1 -1
- package/dist/lib/state_dir.d.ts +15 -0
- package/dist/lib/state_dir.js +56 -1
- package/dist/lib/state_dir.js.map +1 -1
- package/dist/lib/telegram_status.d.ts +24 -0
- package/dist/lib/telegram_status.js +87 -0
- package/dist/lib/telegram_status.js.map +1 -0
- package/dist/server.js +1 -1
- package/dist/telegram/api.d.ts +0 -20
- package/dist/telegram/api.js +0 -32
- package/dist/telegram/api.js.map +1 -1
- package/dist/telegram/bot.js +56 -6
- package/dist/telegram/bot.js.map +1 -1
- package/dist/telegram/notifier.js +10 -98
- package/dist/telegram/notifier.js.map +1 -1
- package/dist/tools/broker.js +47 -1
- package/dist/tools/broker.js.map +1 -1
- package/dist/tools/dispatch.js +74 -20
- package/dist/tools/dispatch.js.map +1 -1
- package/dist/tools/ping.js +16 -2
- package/dist/tools/ping.js.map +1 -1
- package/dist/tools/userio.js +40 -9
- package/dist/tools/userio.js.map +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
|
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
|
-
|
|
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":"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"}
|
|
@@ -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("");
|