@synaplink/orqlaude 0.5.6 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -3
- package/dist/__tests__/v06.test.d.ts +1 -0
- package/dist/__tests__/v06.test.js +74 -0
- package/dist/__tests__/v06.test.js.map +1 -0
- package/dist/__tests__/v07.test.d.ts +1 -0
- package/dist/__tests__/v07.test.js +124 -0
- package/dist/__tests__/v07.test.js.map +1 -0
- package/dist/cli/about.d.ts +5 -0
- package/dist/cli/about.js +27 -0
- package/dist/cli/about.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +180 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/easter_egg.d.ts +1 -0
- package/dist/cli/easter_egg.js +92 -0
- package/dist/cli/easter_egg.js.map +1 -0
- package/dist/cli/open.d.ts +6 -0
- package/dist/cli/open.js +57 -0
- package/dist/cli/open.js.map +1 -0
- package/dist/cli/taglines.d.ts +12 -0
- package/dist/cli/taglines.js +163 -0
- package/dist/cli/taglines.js.map +1 -0
- package/dist/cli/tail.d.ts +7 -0
- package/dist/cli/tail.js +87 -0
- package/dist/cli/tail.js.map +1 -0
- package/dist/cli/watch.d.ts +1 -0
- package/dist/cli/watch.js +169 -0
- package/dist/cli/watch.js.map +1 -0
- package/dist/cli.js +234 -43
- package/dist/cli.js.map +1 -1
- package/dist/lib/error_ui.d.ts +21 -0
- package/dist/lib/error_ui.js +59 -0
- package/dist/lib/error_ui.js.map +1 -0
- package/dist/lib/json_out.d.ts +9 -0
- package/dist/lib/json_out.js +15 -0
- package/dist/lib/json_out.js.map +1 -0
- package/dist/lib/notifications.d.ts +14 -0
- package/dist/lib/notifications.js +37 -0
- package/dist/lib/notifications.js.map +1 -0
- package/dist/lib/picker.d.ts +6 -0
- package/dist/lib/picker.js +50 -0
- package/dist/lib/picker.js.map +1 -0
- package/dist/lib/preferences.d.ts +25 -0
- package/dist/lib/preferences.js +32 -0
- package/dist/lib/preferences.js.map +1 -0
- package/dist/lib/process_lib.d.ts +8 -0
- package/dist/lib/process_lib.js +26 -0
- package/dist/lib/process_lib.js.map +1 -0
- package/dist/lib/spawn_cli.d.ts +31 -40
- package/dist/lib/spawn_cli.js +101 -55
- package/dist/lib/spawn_cli.js.map +1 -1
- package/dist/lib/state.d.ts +14 -1
- package/dist/lib/state.js.map +1 -1
- package/dist/lib/update_check.d.ts +1 -0
- package/dist/lib/update_check.js +78 -0
- package/dist/lib/update_check.js.map +1 -0
- package/dist/server.js +1 -1
- package/dist/telegram/notifier.js +30 -0
- package/dist/telegram/notifier.js.map +1 -1
- package/dist/tools/dispatch.js +66 -10
- package/dist/tools/dispatch.js.map +1 -1
- package/dist/tools/ping.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -226,17 +226,43 @@ False positives are acceptable here — we surface concerns, we don't auto-kill.
|
|
|
226
226
|
|
|
227
227
|
## CLI
|
|
228
228
|
|
|
229
|
-
Two binaries are installed: `orqlaude` and the short alias `orql`. Use whichever feels right.
|
|
229
|
+
Two binaries are installed: `orqlaude` and the short alias `orql`. Use whichever feels right. All commands work the same.
|
|
230
|
+
|
|
231
|
+
### Live (v0.6+)
|
|
230
232
|
|
|
231
233
|
```sh
|
|
234
|
+
orql watch <plan_id> # live-updating dashboard (1Hz, Ctrl-C to exit)
|
|
235
|
+
orql tail [plan_id] # tail -f the audit log; filter by plan if given
|
|
236
|
+
orql open <plan_id> # open every PR from a plan in your browser
|
|
237
|
+
orql doctor # end-to-end health check
|
|
238
|
+
orql about # the easter egg
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Local desktop notifications (macOS)
|
|
242
|
+
|
|
243
|
+
```sh
|
|
244
|
+
orql notify on # enable; the Telegram bot will also fire osascript banners
|
|
245
|
+
orql notify off # disable
|
|
246
|
+
orql notify test # send a test notification
|
|
247
|
+
orql notify status # is it on?
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Read-only inspection (`--json` on every one)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
orql # banner + active-plan summary
|
|
232
256
|
orql list # every plan in this project
|
|
233
|
-
orql status
|
|
234
|
-
orql show
|
|
257
|
+
orql status [plan_id] # if omitted, picker prompts
|
|
258
|
+
orql show [plan_id] # raw plan JSON
|
|
235
259
|
orql history --limit 50 # tail audit log
|
|
236
260
|
orql where # show resolved state dir
|
|
237
261
|
orql help
|
|
238
262
|
```
|
|
239
263
|
|
|
264
|
+
Every read command supports `--json` to emit machine-readable output for scripting.
|
|
265
|
+
|
|
240
266
|
Read-only. For active orchestration, use the MCP from inside Claude Code.
|
|
241
267
|
|
|
242
268
|
### Branding & colors (v0.5+)
|
|
@@ -337,6 +363,13 @@ The bot uses raw `fetch` against Telegram's Bot API — zero extra deps. State i
|
|
|
337
363
|
**Symptom: `ENOENT: no such file or directory, mkdir '/.orqlaude'` on `create_plan`.**
|
|
338
364
|
Your MCP host launched orqlaude with `cwd=/`. v0.3.2+ auto-falls back to `~/.orqlaude/projects/...` but the explicit fix is to set `ORQLAUDE_STATE_DIR` in your `.mcp.json` env block (see `.mcp.json.template`). Verify with `mcp__orqlaude__ping` — it now returns `warnings` and `state_dir_source`.
|
|
339
365
|
|
|
366
|
+
**Symptom: `spawn_via_cli` returned a PID but `status()` shows `died_at_launch` shortly after, with a stderr_excerpt + command_line.**
|
|
367
|
+
This is the v0.7.0 hardening doing its job. The child `claude -p` process exited within the 1.5s healthcheck window. Read `stderr_excerpt` (or open `stderr_path` directly) for the cause. Common ones:
|
|
368
|
+
1. `claude` isn't authenticated on this user account (`claude auth status` to check; `claude auth login --claudeai` to fix).
|
|
369
|
+
2. The `--mcp-config` JSON references a server entry that doesn't exist (rare; orqlaude validates this pre-spawn).
|
|
370
|
+
3. The user's environment lacks something `claude` needs (HOME, locale).
|
|
371
|
+
Copy the `command_line` field and paste it into a shell to reproduce by hand.
|
|
372
|
+
|
|
340
373
|
**Symptom: spawn_task chip appeared, agent ran, but `status()` shows the task as `dispatched` forever.**
|
|
341
374
|
The child agent isn't calling `checkin` on its first turn — its prompt didn't get the orqlaude protocol block, or `mcp__orqlaude__checkin` isn't available in the spawned session. Manual unblock: `register_spawn(plan_id, task_id, session_id)` where session_id is the child's session UUID (find via `mcp__ccd_session_mgmt__list_sessions`). For the proper fix, make sure orqlaude is in the spawned worktree's `.mcp.json` (commit `.mcp.json` to the repo so worktrees inherit it).
|
|
342
375
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
/** v0.6.0 regression tests covering the polish-pack modules. */
|
|
7
|
+
test("preferences: read/write/update round-trip", async () => {
|
|
8
|
+
const fakeHome = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-prefs-"));
|
|
9
|
+
const realHome = process.env.HOME;
|
|
10
|
+
process.env.HOME = fakeHome;
|
|
11
|
+
try {
|
|
12
|
+
// Re-import so PREFS_PATH picks up the new HOME. The module caches the
|
|
13
|
+
// path at import time, so this technique only works in a fresh worker —
|
|
14
|
+
// we accept that and validate the shape rather than the path.
|
|
15
|
+
const { readPreferences, updatePreferences } = await import(`../lib/preferences.js?cache=${Date.now()}`);
|
|
16
|
+
let prefs = await readPreferences();
|
|
17
|
+
assert.deepEqual(prefs, {});
|
|
18
|
+
await updatePreferences((p) => {
|
|
19
|
+
p.welcomedAt = 1234;
|
|
20
|
+
p.localNotifications = true;
|
|
21
|
+
});
|
|
22
|
+
prefs = await readPreferences();
|
|
23
|
+
assert.equal(prefs.welcomedAt, 1234);
|
|
24
|
+
assert.equal(prefs.localNotifications, true);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
if (realHome)
|
|
28
|
+
process.env.HOME = realHome;
|
|
29
|
+
else
|
|
30
|
+
delete process.env.HOME;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
test("error_ui: formatError attaches suggestions for known patterns", async () => {
|
|
34
|
+
const { formatError } = await import("../lib/error_ui.js");
|
|
35
|
+
assert.match(formatError(new Error("Plan not found: abc")).suggestion, /orql list/);
|
|
36
|
+
assert.match(formatError(new Error("EACCES: write failed")).suggestion, /ORQLAUDE_STATE_DIR/);
|
|
37
|
+
assert.match(formatError(new Error("ENOENT: nope")).suggestion, /missing path|orql setup/);
|
|
38
|
+
// Unknown messages get no suggestion (returns undefined).
|
|
39
|
+
assert.equal(formatError(new Error("something obscure")).suggestion, undefined);
|
|
40
|
+
});
|
|
41
|
+
test("json_out: hasJsonFlag detects --json anywhere in args", async () => {
|
|
42
|
+
const { hasJsonFlag } = await import("../lib/json_out.js");
|
|
43
|
+
assert.equal(hasJsonFlag([]), false);
|
|
44
|
+
assert.equal(hasJsonFlag(["foo"]), false);
|
|
45
|
+
assert.equal(hasJsonFlag(["--json"]), true);
|
|
46
|
+
assert.equal(hasJsonFlag(["foo", "--json", "bar"]), true);
|
|
47
|
+
});
|
|
48
|
+
test("notifications: isNotificationsAvailable matches platform", async () => {
|
|
49
|
+
const { isNotificationsAvailable } = await import("../lib/notifications.js");
|
|
50
|
+
assert.equal(isNotificationsAvailable(), process.platform === "darwin");
|
|
51
|
+
});
|
|
52
|
+
test("error_ui: errorLine includes ✗ glyph and optional suggestion", async () => {
|
|
53
|
+
const { errorLine } = await import("../lib/error_ui.js");
|
|
54
|
+
const noSuggestion = errorLine("bad");
|
|
55
|
+
// String includes "bad" and the glyph "✗"
|
|
56
|
+
assert.ok(noSuggestion.includes("✗"));
|
|
57
|
+
assert.ok(noSuggestion.includes("bad"));
|
|
58
|
+
const withSuggestion = errorLine("bad", "try foo");
|
|
59
|
+
assert.ok(withSuggestion.includes("try foo"));
|
|
60
|
+
assert.ok(withSuggestion.includes("→"));
|
|
61
|
+
});
|
|
62
|
+
test("v0.6.1 taglines: exactly 149, all non-empty, no consecutive duplicates", async () => {
|
|
63
|
+
const { TAGLINES } = await import("../cli/taglines.js");
|
|
64
|
+
assert.equal(TAGLINES.length, 149);
|
|
65
|
+
for (let i = 0; i < TAGLINES.length; i++) {
|
|
66
|
+
assert.ok(typeof TAGLINES[i] === "string" && TAGLINES[i].length > 0, `tagline ${i} is empty`);
|
|
67
|
+
}
|
|
68
|
+
// The picker uses a never-repeat-last constraint, so duplicates between
|
|
69
|
+
// non-adjacent indices are fine — but adjacent duplicates would defeat it.
|
|
70
|
+
for (let i = 1; i < TAGLINES.length; i++) {
|
|
71
|
+
assert.notEqual(TAGLINES[i], TAGLINES[i - 1], `adjacent duplicate at ${i - 1}/${i}`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=v06.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v06.test.js","sourceRoot":"","sources":["../../src/__tests__/v06.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { promises as fs, existsSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
import { isProcessAlive } from "../lib/process_lib.js";
|
|
8
|
+
import { spawnAgnetViaCli } from "../lib/spawn_cli.js";
|
|
9
|
+
/**
|
|
10
|
+
* v0.7.0: process-liveness + spawn robustness regression tests.
|
|
11
|
+
*/
|
|
12
|
+
test("isProcessAlive: own PID is alive", () => {
|
|
13
|
+
assert.equal(isProcessAlive(process.pid), true);
|
|
14
|
+
});
|
|
15
|
+
test("isProcessAlive: PID 1 reports alive (init exists on every OS)", () => {
|
|
16
|
+
// We don't OWN pid 1, but isProcessAlive should report true via EPERM
|
|
17
|
+
// detection rather than returning false.
|
|
18
|
+
assert.equal(isProcessAlive(1), true);
|
|
19
|
+
});
|
|
20
|
+
test("isProcessAlive: impossibly large PID is dead", () => {
|
|
21
|
+
// 2^31 - 1 is past any realistic process table.
|
|
22
|
+
assert.equal(isProcessAlive(2147483647), false);
|
|
23
|
+
});
|
|
24
|
+
test("isProcessAlive: 0 / negative / undefined are dead", () => {
|
|
25
|
+
assert.equal(isProcessAlive(0), false);
|
|
26
|
+
assert.equal(isProcessAlive(-1), false);
|
|
27
|
+
assert.equal(isProcessAlive(undefined), false);
|
|
28
|
+
assert.equal(isProcessAlive(null), false);
|
|
29
|
+
});
|
|
30
|
+
test("spawnAgnetViaCli: rejects when claude binary doesn't exist", async () => {
|
|
31
|
+
// Build a minimal fake git repo so createWorktreeForTask doesn't fail
|
|
32
|
+
// earlier on its own.
|
|
33
|
+
const dir = await mkTempGitRepo();
|
|
34
|
+
await assert.rejects(() => spawnAgnetViaCli({
|
|
35
|
+
projectRoot: dir,
|
|
36
|
+
stateDir: path.join(dir, ".orqlaude"),
|
|
37
|
+
planId: "deadbeef-1111-2222-3333-444444444444",
|
|
38
|
+
taskId: "11111111-2222-3333-4444-555555555555",
|
|
39
|
+
agnetName: "Test",
|
|
40
|
+
prompt: "noop",
|
|
41
|
+
claudeBin: "/nonexistent/path/to/claude",
|
|
42
|
+
healthCheckDelayMs: 0,
|
|
43
|
+
}), /not executable|ENOENT|EACCES/i);
|
|
44
|
+
});
|
|
45
|
+
test("spawnAgnetViaCli: when child dies at launch, throws with stderr + commandLine", async () => {
|
|
46
|
+
// Use /bin/false (or `false` on PATH) — it exits immediately with code 1.
|
|
47
|
+
// The healthcheck should detect the dead PID and throw with autopsy info.
|
|
48
|
+
const falseBin = existsSync("/bin/false") ? "/bin/false" : "/usr/bin/false";
|
|
49
|
+
if (!existsSync(falseBin)) {
|
|
50
|
+
// Skip on platforms without /bin/false; the contract is otherwise
|
|
51
|
+
// verified by the other tests.
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const dir = await mkTempGitRepo();
|
|
55
|
+
await assert.rejects(() => spawnAgnetViaCli({
|
|
56
|
+
projectRoot: dir,
|
|
57
|
+
stateDir: path.join(dir, ".orqlaude"),
|
|
58
|
+
planId: "deadbeef-1111-2222-3333-444444444444",
|
|
59
|
+
taskId: "22222222-3333-4444-5555-666666666666",
|
|
60
|
+
agnetName: "Dies",
|
|
61
|
+
prompt: "this prompt should appear in the command line echo",
|
|
62
|
+
claudeBin: falseBin,
|
|
63
|
+
healthCheckDelayMs: 500, // short
|
|
64
|
+
}), (err) => {
|
|
65
|
+
// The error message must include:
|
|
66
|
+
// • "died at launch"
|
|
67
|
+
// • the command line (so we can copy-paste to reproduce)
|
|
68
|
+
// • mention of the prompt as the last arg
|
|
69
|
+
assert.match(err.message, /died at launch/i);
|
|
70
|
+
assert.match(err.message, /Command line:/i);
|
|
71
|
+
assert.match(err.message, /this prompt should appear in the command line echo/);
|
|
72
|
+
return true;
|
|
73
|
+
});
|
|
74
|
+
// After failure, the stderr log file should exist in the worktree.
|
|
75
|
+
const worktreeBase = path.join(dir, ".orqlaude-worktrees");
|
|
76
|
+
const found = await fs.readdir(worktreeBase);
|
|
77
|
+
assert.ok(found.length > 0, "expected a worktree to have been created before the spawn died");
|
|
78
|
+
});
|
|
79
|
+
test("spawnAgnetViaCli: prompt is the LAST positional argv element", async () => {
|
|
80
|
+
// Same /bin/false trick but assert on the exact shape of commandLine
|
|
81
|
+
// captured in the thrown error.
|
|
82
|
+
const falseBin = existsSync("/bin/false") ? "/bin/false" : "/usr/bin/false";
|
|
83
|
+
if (!existsSync(falseBin))
|
|
84
|
+
return;
|
|
85
|
+
const dir = await mkTempGitRepo();
|
|
86
|
+
let captured = null;
|
|
87
|
+
try {
|
|
88
|
+
await spawnAgnetViaCli({
|
|
89
|
+
projectRoot: dir,
|
|
90
|
+
stateDir: path.join(dir, ".orqlaude"),
|
|
91
|
+
planId: "deadbeef-aaaa-bbbb-cccc-dddddddddddd",
|
|
92
|
+
taskId: "33333333-4444-5555-6666-777777777777",
|
|
93
|
+
agnetName: "ArgOrder",
|
|
94
|
+
prompt: "ZZZ_PROMPT_SENTINEL_ZZZ",
|
|
95
|
+
claudeBin: falseBin,
|
|
96
|
+
healthCheckDelayMs: 500,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
captured = err.message;
|
|
101
|
+
}
|
|
102
|
+
assert.ok(captured, "expected the spawn to fail and surface the command line");
|
|
103
|
+
// Pull the command-line line from the error message.
|
|
104
|
+
const m = captured.match(/Command line:\n\s*(.+)/);
|
|
105
|
+
assert.ok(m, "error message should contain 'Command line:'");
|
|
106
|
+
const cmd = m[1];
|
|
107
|
+
// The last shell token should be the quoted prompt — i.e. the prompt
|
|
108
|
+
// sentinel should appear AFTER every --flag.
|
|
109
|
+
const promptIdx = cmd.lastIndexOf("ZZZ_PROMPT_SENTINEL_ZZZ");
|
|
110
|
+
const lastFlagIdx = Math.max(cmd.lastIndexOf("--session-id"), cmd.lastIndexOf("--output-format"), cmd.lastIndexOf("--permission-mode"), cmd.lastIndexOf("--mcp-config"));
|
|
111
|
+
assert.ok(promptIdx > lastFlagIdx, `prompt must appear after the last flag — got cmd=\n${cmd}`);
|
|
112
|
+
});
|
|
113
|
+
async function mkTempGitRepo() {
|
|
114
|
+
const raw = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-v07-"));
|
|
115
|
+
const real = await fs.realpath(raw);
|
|
116
|
+
execSync("git init -q", { cwd: real });
|
|
117
|
+
execSync("git config user.email t@e", { cwd: real });
|
|
118
|
+
execSync("git config user.name t", { cwd: real });
|
|
119
|
+
// Need at least one commit so `git worktree add -b ...` succeeds.
|
|
120
|
+
await fs.writeFile(path.join(real, "README.md"), "test\n");
|
|
121
|
+
execSync("git add . && git commit -q -m init", { cwd: real });
|
|
122
|
+
return real;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=v07.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v07.test.js","sourceRoot":"","sources":["../../src/__tests__/v07.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,UAAU,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;GAEG;AAEH,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAC5C,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+DAA+D,EAAE,GAAG,EAAE;IACzE,sEAAsE;IACtE,yCAAyC;IACzC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,gDAAgD;IAChD,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mDAAmD,EAAE,GAAG,EAAE;IAC7D,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;IAC5E,sEAAsE;IACtE,sBAAsB;IACtB,MAAM,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;IAClC,MAAM,MAAM,CAAC,OAAO,CAClB,GAAG,EAAE,CACH,gBAAgB,CAAC;QACf,WAAW,EAAE,GAAG;QAChB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC;QACrC,MAAM,EAAE,sCAAsC;QAC9C,MAAM,EAAE,sCAAsC;QAC9C,SAAS,EAAE,MAAM;QACjB,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,6BAA6B;QACxC,kBAAkB,EAAE,CAAC;KACtB,CAAC,EACJ,+BAA+B,CAChC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+EAA+E,EAAE,KAAK,IAAI,EAAE;IAC/F,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAC5E,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,kEAAkE;QAClE,+BAA+B;QAC/B,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;IAClC,MAAM,MAAM,CAAC,OAAO,CAClB,GAAG,EAAE,CACH,gBAAgB,CAAC;QACf,WAAW,EAAE,GAAG;QAChB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC;QACrC,MAAM,EAAE,sCAAsC;QAC9C,MAAM,EAAE,sCAAsC;QAC9C,SAAS,EAAE,MAAM;QACjB,MAAM,EAAE,oDAAoD;QAC5D,SAAS,EAAE,QAAQ;QACnB,kBAAkB,EAAE,GAAG,EAAE,QAAQ;KAClC,CAAC,EACJ,CAAC,GAAU,EAAE,EAAE;QACb,kCAAkC;QAClC,uBAAuB;QACvB,2DAA2D;QAC3D,4CAA4C;QAC5C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,oDAAoD,CAAC,CAAC;QAChF,OAAO,IAAI,CAAC;IACd,CAAC,CACF,CAAC;IACF,mEAAmE;IACnE,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,gEAAgE,CAAC,CAAC;AAChG,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;IAC9E,qEAAqE;IACrE,gCAAgC;IAChC,MAAM,QAAQ,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAC5E,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO;IAClC,MAAM,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;IAClC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,gBAAgB,CAAC;YACrB,WAAW,EAAE,GAAG;YAChB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC;YACrC,MAAM,EAAE,sCAAsC;YAC9C,MAAM,EAAE,sCAAsC;YAC9C,SAAS,EAAE,UAAU;YACrB,MAAM,EAAE,yBAAyB;YACjC,SAAS,EAAE,QAAQ;YACnB,kBAAkB,EAAE,GAAG;SACxB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,QAAQ,GAAI,GAAa,CAAC,OAAO,CAAC;IACpC,CAAC;IACD,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,yDAAyD,CAAC,CAAC;IAC/E,qDAAqD;IACrD,MAAM,CAAC,GAAG,QAAS,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACpD,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,8CAA8C,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,CAAE,CAAC,CAAC,CAAC,CAAC;IAClB,qEAAqE;IACrE,6CAA6C;IAC7C,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC1B,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,EAC/B,GAAG,CAAC,WAAW,CAAC,iBAAiB,CAAC,EAClC,GAAG,CAAC,WAAW,CAAC,mBAAmB,CAAC,EACpC,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,CAChC,CAAC;IACF,MAAM,CAAC,EAAE,CAAC,SAAS,GAAG,WAAW,EAAE,sDAAsD,GAAG,EAAE,CAAC,CAAC;AAClG,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,aAAa;IAC1B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,QAAQ,CAAC,2BAA2B,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,QAAQ,CAAC,wBAAwB,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,kEAAkE;IAClE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC3D,QAAQ,CAAC,oCAAoC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { style } from "../lib/style.js";
|
|
2
|
+
/**
|
|
3
|
+
* `orql about` — the easter egg. Print a small stylized "what is this"
|
|
4
|
+
* blurb in the brand palette. Just because.
|
|
5
|
+
*/
|
|
6
|
+
export function showAbout(version) {
|
|
7
|
+
const lines = [
|
|
8
|
+
"",
|
|
9
|
+
` ${style.coral("◆")} ${style.bold(style.coral("orqlaude"))}`,
|
|
10
|
+
"",
|
|
11
|
+
` ${style.sand("one Claude session decomposes a task,")}`,
|
|
12
|
+
` ${style.sand("approves a budget,")}`,
|
|
13
|
+
` ${style.sand("dispatches a fleet of")} ${style.coral("Agnets")} ${style.sand("—")}`,
|
|
14
|
+
` ${style.sand("each in its own worktree, named, tracked,")}`,
|
|
15
|
+
` ${style.sand("brokered, budgeted, watchable.")}`,
|
|
16
|
+
"",
|
|
17
|
+
` ${style.dim(`v${version} · MIT · @synaplink/orqlaude`)}`,
|
|
18
|
+
"",
|
|
19
|
+
` ${style.cream("\"More agents working on a task in parallel is just")}`,
|
|
20
|
+
` ${style.cream("a faster way of reaching")} ${style.coral("the same insight")}.${style.cream("\"")}`,
|
|
21
|
+
` ${style.dim("— Claude (probably)")}`,
|
|
22
|
+
"",
|
|
23
|
+
];
|
|
24
|
+
process.stdout.write(lines.join("\n"));
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=about.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"about.js","sourceRoot":"","sources":["../../src/cli/about.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAExC;;;GAGG;AAEH,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,MAAM,KAAK,GAAG;QACZ,EAAE;QACF,KAAK,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE;QAC9D,EAAE;QACF,KAAK,KAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC,EAAE;QAC1D,KAAK,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE;QACvC,KAAK,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QACtF,KAAK,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,EAAE;QAC9D,KAAK,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,EAAE;QACnD,EAAE;QACF,KAAK,KAAK,CAAC,GAAG,CAAC,IAAI,OAAO,8BAA8B,CAAC,EAAE;QAC3D,EAAE;QACF,KAAK,KAAK,CAAC,KAAK,CAAC,qDAAqD,CAAC,EAAE;QACzE,KAAK,KAAK,CAAC,KAAK,CAAC,0BAA0B,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;QACtG,KAAK,KAAK,CAAC,GAAG,CAAC,qBAAqB,CAAC,EAAE;QACvC,EAAE;KACH,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runDoctor(currentVersion: string): Promise<number>;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { style, banner } from "../lib/style.js";
|
|
5
|
+
import { discoverClaudeBinary } from "../lib/spawn_cli.js";
|
|
6
|
+
import { resolveStateDir } from "../lib/state_dir.js";
|
|
7
|
+
import { probeTelegramStatus } from "../lib/telegram_status.js";
|
|
8
|
+
import { findDesktopConfigPath, readDesktopConfig } from "../lib/desktop_config.js";
|
|
9
|
+
export async function runDoctor(currentVersion) {
|
|
10
|
+
console.log(banner());
|
|
11
|
+
console.log("");
|
|
12
|
+
console.log(style.bold(style.cream("doctor")));
|
|
13
|
+
console.log("");
|
|
14
|
+
const checks = [];
|
|
15
|
+
// 1. Node version
|
|
16
|
+
const nodeVer = process.version.replace(/^v/, "");
|
|
17
|
+
const major = parseInt(nodeVer.split(".")[0], 10);
|
|
18
|
+
checks.push({
|
|
19
|
+
name: "Node.js >= 22",
|
|
20
|
+
status: major >= 22 ? "ok" : "fail",
|
|
21
|
+
detail: `Node ${nodeVer}`,
|
|
22
|
+
fix: major < 22 ? "Install Node 22+ (brew install node@22)" : undefined,
|
|
23
|
+
});
|
|
24
|
+
// 2. claude binary
|
|
25
|
+
try {
|
|
26
|
+
const claudeBin = discoverClaudeBinary();
|
|
27
|
+
let claudeVer = "(version probe failed)";
|
|
28
|
+
try {
|
|
29
|
+
claudeVer = execSync(`"${claudeBin}" --version`, { encoding: "utf8", timeout: 3000 }).trim();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
/* ignore */
|
|
33
|
+
}
|
|
34
|
+
checks.push({ name: "claude binary", status: "ok", detail: `${claudeBin} (${claudeVer})` });
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
checks.push({
|
|
38
|
+
name: "claude binary",
|
|
39
|
+
status: "fail",
|
|
40
|
+
detail: err.message,
|
|
41
|
+
fix: "Install Claude Code, or set CLAUDE_BIN env var to its path. spawn_via_cli won't work without this.",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// 3. gh CLI
|
|
45
|
+
try {
|
|
46
|
+
const ghVer = execSync("gh --version", { encoding: "utf8", timeout: 3000 }).split("\n")[0];
|
|
47
|
+
let authed = false;
|
|
48
|
+
try {
|
|
49
|
+
execSync("gh auth status", { encoding: "utf8", timeout: 3000, stdio: "pipe" });
|
|
50
|
+
authed = true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
authed = false;
|
|
54
|
+
}
|
|
55
|
+
checks.push({
|
|
56
|
+
name: "gh CLI + auth",
|
|
57
|
+
status: authed ? "ok" : "warn",
|
|
58
|
+
detail: authed ? ghVer : `${ghVer} (not authenticated)`,
|
|
59
|
+
fix: authed ? undefined : "Run `gh auth login` so Agnets can open PRs.",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
checks.push({
|
|
64
|
+
name: "gh CLI + auth",
|
|
65
|
+
status: "warn",
|
|
66
|
+
detail: "gh not installed",
|
|
67
|
+
fix: "brew install gh; gh auth login. Agnets can still run without it, but PRs won't auto-open.",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// 4. Resolved state dir
|
|
71
|
+
const stateRes = resolveStateDir();
|
|
72
|
+
const stateOk = stateRes.source === "env" || stateRes.source === "project-root" || stateRes.source === "worktree";
|
|
73
|
+
checks.push({
|
|
74
|
+
name: "state dir",
|
|
75
|
+
status: stateOk ? "ok" : "warn",
|
|
76
|
+
detail: `${stateRes.path} (source: ${stateRes.source})`,
|
|
77
|
+
fix: stateOk
|
|
78
|
+
? undefined
|
|
79
|
+
: `Run \`orql setup\` from your project to pin state via ORQLAUDE_STATE_DIR in the Desktop MCP config.`,
|
|
80
|
+
});
|
|
81
|
+
// 5. Claude Desktop MCP config
|
|
82
|
+
const cfgPath = findDesktopConfigPath();
|
|
83
|
+
if (existsSync(cfgPath)) {
|
|
84
|
+
try {
|
|
85
|
+
const cfg = await readDesktopConfig(cfgPath);
|
|
86
|
+
const entry = cfg?.mcpServers?.orqlaude;
|
|
87
|
+
if (!entry) {
|
|
88
|
+
checks.push({
|
|
89
|
+
name: "Desktop MCP config",
|
|
90
|
+
status: "fail",
|
|
91
|
+
detail: `${cfgPath} exists, but no orqlaude entry`,
|
|
92
|
+
fix: "Run `orql setup` in your project to add the entry.",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
else if (!entry.env?.ORQLAUDE_STATE_DIR) {
|
|
96
|
+
checks.push({
|
|
97
|
+
name: "Desktop MCP config",
|
|
98
|
+
status: "warn",
|
|
99
|
+
detail: "orqlaude entry present but has no ORQLAUDE_STATE_DIR env",
|
|
100
|
+
fix: "Run `orql setup` to pin a state dir; without it, MCP cwd=/ can split state.",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
checks.push({
|
|
105
|
+
name: "Desktop MCP config",
|
|
106
|
+
status: "ok",
|
|
107
|
+
detail: `ORQLAUDE_STATE_DIR → ${entry.env.ORQLAUDE_STATE_DIR}`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
checks.push({
|
|
113
|
+
name: "Desktop MCP config",
|
|
114
|
+
status: "fail",
|
|
115
|
+
detail: err.message,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
checks.push({
|
|
121
|
+
name: "Desktop MCP config",
|
|
122
|
+
status: "warn",
|
|
123
|
+
detail: `${cfgPath} does not exist`,
|
|
124
|
+
fix: "Run `orql setup` to create it.",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// 6. Telegram
|
|
128
|
+
const tg = await probeTelegramStatus(stateRes.path);
|
|
129
|
+
let tgStatus = "ok";
|
|
130
|
+
if (tg.status === "unconfigured")
|
|
131
|
+
tgStatus = "warn";
|
|
132
|
+
else if (tg.status === "stale" || tg.status === "configured" || tg.status === "started")
|
|
133
|
+
tgStatus = "warn";
|
|
134
|
+
checks.push({
|
|
135
|
+
name: "Telegram bot",
|
|
136
|
+
status: tgStatus,
|
|
137
|
+
detail: `${tg.status} (token: ${tg.hasToken ? "set" : "missing"}, whitelist: ${tg.whitelistSize})`,
|
|
138
|
+
fix: tg.status === "unconfigured"
|
|
139
|
+
? "`orql tg setup` to wire a bot — optional but recommended."
|
|
140
|
+
: tg.status === "stale" || tg.status === "configured" || tg.status === "started"
|
|
141
|
+
? "Run `orql tg start` from your project to bring the bot online."
|
|
142
|
+
: undefined,
|
|
143
|
+
});
|
|
144
|
+
// 7. orqlaude self-test (MCP server startup)
|
|
145
|
+
try {
|
|
146
|
+
const serverPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..", "server.js");
|
|
147
|
+
if (existsSync(serverPath)) {
|
|
148
|
+
checks.push({ name: "orqlaude MCP server", status: "ok", detail: `${serverPath}` });
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
checks.push({
|
|
152
|
+
name: "orqlaude MCP server",
|
|
153
|
+
status: "fail",
|
|
154
|
+
detail: `Missing ${serverPath}`,
|
|
155
|
+
fix: "Reinstall: npm i -g @synaplink/orqlaude@latest",
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
checks.push({ name: "orqlaude MCP server", status: "warn", detail: err.message });
|
|
161
|
+
}
|
|
162
|
+
// Render
|
|
163
|
+
const counts = { ok: 0, warn: 0, fail: 0 };
|
|
164
|
+
for (const c of checks) {
|
|
165
|
+
counts[c.status]++;
|
|
166
|
+
const glyph = c.status === "ok"
|
|
167
|
+
? style.coral("✓")
|
|
168
|
+
: c.status === "warn"
|
|
169
|
+
? style.crimson("⚠")
|
|
170
|
+
: style.crimson("✗");
|
|
171
|
+
console.log(` ${glyph} ${style.bold(c.name.padEnd(26))} ${c.detail}`);
|
|
172
|
+
if (c.fix)
|
|
173
|
+
console.log(` ${style.coral("→")} ${style.sand(c.fix)}`);
|
|
174
|
+
}
|
|
175
|
+
console.log("");
|
|
176
|
+
const summary = `${style.coral(`${counts.ok} ok`)} · ${style.crimson(`${counts.warn} warn`)} · ${style.crimson(`${counts.fail} fail`)}`;
|
|
177
|
+
console.log(` ${summary} ${style.dim(`(orqlaude ${currentVersion})`)}`);
|
|
178
|
+
return counts.fail > 0 ? 1 : 0;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runEasterEgg(): Promise<number>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { TAGLINES } from "./taglines.js";
|
|
2
|
+
import { style } from "../lib/style.js";
|
|
3
|
+
/**
|
|
4
|
+
* Bare `orql` easter egg — a never-repeating typewriter cycling through
|
|
5
|
+
* 149 tagline variants under the orqlaude diamond logo. Runs until Ctrl-C.
|
|
6
|
+
*
|
|
7
|
+
* The natural terminal cursor sits at the end of each tagline while it's
|
|
8
|
+
* displayed and blinks on its own — no explicit blink animation needed.
|
|
9
|
+
*/
|
|
10
|
+
const ESC = "\x1b[";
|
|
11
|
+
const SAVE = `${ESC}s`;
|
|
12
|
+
const RESTORE = `${ESC}u`;
|
|
13
|
+
const CLEAR_TO_EOL = `${ESC}K`;
|
|
14
|
+
const SAND_FG = `${ESC}38;2;185;182;171m`;
|
|
15
|
+
const RESET = `${ESC}0m`;
|
|
16
|
+
const TYPE_MIN_MS = 28;
|
|
17
|
+
const TYPE_MAX_MS = 90;
|
|
18
|
+
const HOLD_MIN_MS = 1100;
|
|
19
|
+
const HOLD_MAX_MS = 2200;
|
|
20
|
+
const ERASE_MIN_MS = 12;
|
|
21
|
+
const ERASE_MAX_MS = 30;
|
|
22
|
+
const BETWEEN_MIN_MS = 280;
|
|
23
|
+
const BETWEEN_MAX_MS = 600;
|
|
24
|
+
export async function runEasterEgg() {
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
let stopped = false;
|
|
27
|
+
const onSig = () => {
|
|
28
|
+
stopped = true;
|
|
29
|
+
// Move cursor below the tagline line and reset, then exit.
|
|
30
|
+
process.stdout.write(RESET + "\n\n");
|
|
31
|
+
resolve(0);
|
|
32
|
+
};
|
|
33
|
+
process.on("SIGINT", onSig);
|
|
34
|
+
// Static logo + wordmark. Last line ends without a newline so the
|
|
35
|
+
// cursor sits where taglines should animate.
|
|
36
|
+
const logo = [
|
|
37
|
+
` ${style.coral("◆◆◆")}`,
|
|
38
|
+
` ${style.coral("◆ ◆")} ${style.bold(style.coral("orqlaude"))}`,
|
|
39
|
+
` ${style.coral("◆◆◆")} `, // trailing spaces are the "padding" before the tagline
|
|
40
|
+
];
|
|
41
|
+
process.stdout.write("\n" + logo.join("\n"));
|
|
42
|
+
process.stdout.write(SAVE);
|
|
43
|
+
// Animate in a loop. We do this without `await` in the main scope so
|
|
44
|
+
// SIGINT can interrupt cleanly via the `stopped` flag check between
|
|
45
|
+
// sleeps.
|
|
46
|
+
(async () => {
|
|
47
|
+
let lastIdx = -1;
|
|
48
|
+
while (!stopped) {
|
|
49
|
+
let idx = Math.floor(Math.random() * TAGLINES.length);
|
|
50
|
+
// Never repeat the previous tagline.
|
|
51
|
+
while (idx === lastIdx) {
|
|
52
|
+
idx = Math.floor(Math.random() * TAGLINES.length);
|
|
53
|
+
}
|
|
54
|
+
lastIdx = idx;
|
|
55
|
+
const tagline = TAGLINES[idx];
|
|
56
|
+
// Reset cursor + clear any leftover from the previous tagline.
|
|
57
|
+
process.stdout.write(RESTORE + CLEAR_TO_EOL + SAND_FG);
|
|
58
|
+
// Type out character by character. Each char is plain text — the
|
|
59
|
+
// SAND_FG above persists until we reset, so a single \b backspace
|
|
60
|
+
// erases one visible character.
|
|
61
|
+
for (let i = 0; i < tagline.length; i++) {
|
|
62
|
+
if (stopped)
|
|
63
|
+
return;
|
|
64
|
+
process.stdout.write(tagline[i]);
|
|
65
|
+
await sleep(rand(TYPE_MIN_MS, TYPE_MAX_MS));
|
|
66
|
+
}
|
|
67
|
+
// Hold while the natural terminal cursor blinks at the end.
|
|
68
|
+
await sleep(rand(HOLD_MIN_MS, HOLD_MAX_MS));
|
|
69
|
+
if (stopped)
|
|
70
|
+
return;
|
|
71
|
+
// Erase backwards for a satisfying "delete" feel.
|
|
72
|
+
for (let i = tagline.length; i > 0; i--) {
|
|
73
|
+
if (stopped)
|
|
74
|
+
return;
|
|
75
|
+
process.stdout.write("\b \b");
|
|
76
|
+
await sleep(rand(ERASE_MIN_MS, ERASE_MAX_MS));
|
|
77
|
+
}
|
|
78
|
+
process.stdout.write(RESET);
|
|
79
|
+
await sleep(rand(BETWEEN_MIN_MS, BETWEEN_MAX_MS));
|
|
80
|
+
}
|
|
81
|
+
})().catch(() => {
|
|
82
|
+
/* swallow — never crash on the easter egg */
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function sleep(ms) {
|
|
87
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
88
|
+
}
|
|
89
|
+
function rand(min, max) {
|
|
90
|
+
return min + Math.random() * (max - min);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=easter_egg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"easter_egg.js","sourceRoot":"","sources":["../../src/cli/easter_egg.ts"],"names":[],"mappings":"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"}
|