glimmervoid 0.29.1 → 0.31.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 +8 -10
- package/dist/bin/glimmervoid.js +152 -53
- package/dist/chunks/{adapters-DIavI9-i.js → adapters-ClA_GbtA.js} +1 -1
- package/dist/chunks/{adapters-Dlvu1FVB.js → adapters-gn7ulNO4.js} +49 -20
- package/dist/chunks/agent-api-cli-DAA7Jx8N.js +135 -0
- package/dist/chunks/{agent-api-core-D6LHrpSL.js → agent-api-core-hVcT26JW.js} +10 -1
- package/dist/chunks/{agent-setup-cli-C1CQ8Nkk.js → agent-setup-cli-CiJSDWSo.js} +15 -30
- package/dist/chunks/backoff-DjVpfHk7.js +14 -0
- package/dist/chunks/child-process-safe-BKPZ-5qQ.js +2 -0
- package/dist/chunks/{child-process-safe-Cz22RwlG.js → child-process-safe-Ch-Zgxqs.js} +2 -2
- package/dist/chunks/coerce-NSst8oRP.js +32 -0
- package/dist/chunks/{contracts-JKChPhZ0.js → config-W1Sv-iPK.js} +380 -1682
- package/dist/chunks/{config-store-DtPUFVaO.js → config-store-BCy4FHNG.js} +595 -327
- package/dist/chunks/config-store-DbBp7LJZ.js +2 -0
- package/dist/chunks/contracts-3LkrilQX.js +2078 -0
- package/dist/chunks/{grok-COekzawt.js → grok-CXKeSt2z.js} +69 -36
- package/dist/chunks/hook-command-core-DclEoE9e.js +14 -0
- package/dist/chunks/{hook-relay-core-BHfYFnYV.js → hook-relay-core-DA_bz2KA.js} +10 -5
- package/dist/chunks/hook-tools-UX2fmt81.js +72 -0
- package/dist/chunks/{ingest-editor-core-BuI1gMF_.js → ingest-editor-core-vR6Wv6bP.js} +24 -13
- package/dist/chunks/json-core-dfa_7Fak.js +23 -0
- package/dist/chunks/json-file-oEH9vt-0.js +352 -0
- package/dist/chunks/knowledge-graph-cli-D2tt5hPn.js +1354 -0
- package/dist/chunks/{loopback-post-C7C22eYi.js → loopback-post-DepQfdcM.js} +40 -26
- package/dist/chunks/{main-BVDjy7vH.js → main-DyTCWIu2.js} +28881 -20693
- package/dist/chunks/{node-pty-preflight-CK1knGIR.js → node-pty-preflight-C8QIlrtU.js} +4 -6
- package/dist/chunks/{pair-cli-BQQnhkkc.js → pair-cli-BbCBfd6l.js} +4 -3
- package/dist/chunks/paths-CeJl2Vcg.js +55 -0
- package/dist/chunks/{remote-config-BHoDFCpo.js → remote-config-DKIH_VxM.js} +43 -43
- package/dist/chunks/rtk-hook-core-BmEz2aq9.js +131 -0
- package/dist/chunks/rtk-resolver-BGDzw5XK.js +21 -0
- package/dist/chunks/rtk-resolver-D4Nke1Fr.js +2 -0
- package/dist/chunks/{runtime-paths-CVvq20A4.js → runtime-paths-BPoxENmK.js} +28 -8
- package/dist/chunks/runtime-paths-DU5jBEEO.js +2 -0
- package/dist/chunks/{sandbox-deps-BwvbDBDl.js → sandbox-deps-BPKE-ylr.js} +16 -13
- package/dist/chunks/sandbox-proxy-core-hnQgKR0T.js +27 -0
- package/dist/chunks/{session-CJtOveHl.js → session-fc4pFbon.js} +10 -2
- package/dist/chunks/text-DoirglWV.js +21 -0
- package/dist/chunks/timer-deps-u2DAQLok.js +15 -0
- package/dist/chunks/{visions-cli-DUZnopGY.js → visions-cli-CT5rjiwf.js} +1 -1
- package/dist/chunks/{visions-setup-C_kJobsy.js → visions-setup-BmOkI-QE.js} +20 -357
- package/dist/client/assets/index-C6KD8lgZ.css +1 -0
- package/dist/client/assets/index-CThQEAEy.js +249 -0
- package/dist/client/fonts/CommitMono-OFL.txt +92 -0
- package/dist/client/fonts/commit-mono-400-italic.woff2 +0 -0
- package/dist/client/fonts/commit-mono-400-regular.woff2 +0 -0
- package/dist/client/fonts/commit-mono-700-italic.woff2 +0 -0
- package/dist/client/fonts/commit-mono-700-regular.woff2 +0 -0
- package/dist/client/index.html +7 -24
- package/dist/client/octicons-LICENSE.txt +21 -0
- package/dist/scripts/postinstall-path-check.js +1 -1
- package/dist/server/index.js +3 -3
- package/dist/session/command-hook-relay.js +1 -1
- package/dist/session/hook-relay.js +3 -3
- package/dist/session/{rtk-relay.js → hook-tool-relay.js} +32 -15
- package/dist/session/sandbox-open-launcher.js +79 -0
- package/dist/session/statusline-relay.js +8 -18
- package/dist/session/visions-relay.js +20 -11
- package/package.json +5 -1
- package/dist/chunks/agent-api-cli-ClOzm28U.js +0 -71
- package/dist/chunks/config-store-DtUGwi4-.js +0 -2
- package/dist/chunks/ingest-core-DWI1Mbu4.js +0 -342
- package/dist/chunks/rtk-hook-core-DRYrKwYP.js +0 -26
- package/dist/chunks/rtk-resolver-DDVVNRBT.js +0 -2
- package/dist/chunks/rtk-resolver-oGmO-ew7.js +0 -58
- package/dist/client/assets/index-BTwYgSAH.js +0 -248
- package/dist/client/assets/index-DgA708PE.css +0 -1
package/README.md
CHANGED
|
@@ -17,28 +17,26 @@ Glimmervoid is developed inside Glimmervoid.
|
|
|
17
17
|
|
|
18
18
|
## Quickstart
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Windows and macOS:
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npx
|
|
23
|
+
npx glimmervoid@latest
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Linux (node-pty ships no Linux prebuilds, so it compiles at install time):
|
|
26
|
+
Linux (node-pty ships no Linux prebuilds, so it compiles at install time, and npm runs that build only when allowed):
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
sudo apt install build-essential python3 git
|
|
30
|
-
npx --allow-
|
|
30
|
+
npx --allow-scripts=node-pty glimmervoid@latest
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
To
|
|
34
|
-
|
|
35
|
-
Once the package is published, `npm install -g glimmervoid` (plus `--allow-scripts=node-pty` on Linux) installs it globally; if that fails with `EACCES`, see [docs/troubleshooting.md](docs/troubleshooting.md#eacces-on-a-global-install) and never use `sudo npm`.
|
|
33
|
+
To install it globally instead, run `npm install -g glimmervoid` (add `--allow-scripts=node-pty` on Linux); if that fails with `EACCES`, see [docs/troubleshooting.md](docs/troubleshooting.md#eacces-on-a-global-install) and never use `sudo npm`.
|
|
36
34
|
|
|
37
35
|
Open http://localhost:3000. A fresh install knows no projects, so **+ Session** shows "No projects found" until you say where your repositories live: open **Settings**, then **Repositories**, and add the folder that holds your git checkouts under **Repository roots** (each subfolder becomes a project). For a one-off, **+ Session** then **Advanced options** takes a name and a path directly. That is the whole setup; everything else is optional.
|
|
38
36
|
|
|
39
37
|
To check notifications, allow them when the dashboard asks (or turn on **Desktop notifications** under **Settings**, **Appearance and alerts**) and keep a dashboard tab open. A notification only appears while that tab is not focused, since a focused tab already shows the change. With no dashboard tab open anywhere, only Telegram reaches you (the `telegram` keys in [docs/configuration.md](docs/configuration.md)).
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
The Linux flag matters on npm 12, which blocks dependency install scripts by default; npm 10 and 11 run them (npm 11 prints a notice), so there it is harmless. Keep `@latest` so npx checks for a new release instead of rerunning the version it cached first. If the native module still fails to load, the server refuses to start and prints the repair command; `glimmervoid doctor` runs the same check. See [docs/troubleshooting.md](docs/troubleshooting.md).
|
|
42
40
|
|
|
43
41
|
### From source
|
|
44
42
|
|
|
@@ -59,7 +57,7 @@ Update a clone with `git pull --ff-only && npm ci && npm run build`, then restar
|
|
|
59
57
|
- **Claude Code CLI** on PATH, or another supported agent below.
|
|
60
58
|
- **git** on PATH, for per-session worktrees.
|
|
61
59
|
- **Linux only:** `build-essential` and `python3` for node-pty.
|
|
62
|
-
- **Linux only:** `bubblewrap` (`bwrap`) and `
|
|
60
|
+
- **Linux only:** `bubblewrap` (`bwrap`), `socat` and `ripgrep` (`rg`) for the sandbox that sandboxed sessions and factory checks run in (`sudo apt install bubblewrap socat ripgrep`). `glimmervoid doctor` reports all three.
|
|
63
61
|
|
|
64
62
|
## Supported agents
|
|
65
63
|
|
|
@@ -144,7 +142,7 @@ Remote access is off by default. It is a second loopback listener meant to sit b
|
|
|
144
142
|
```
|
|
145
143
|
|
|
146
144
|
2. Restart Glimmervoid (stop and start it, or `systemctl --user restart glimmervoid` when it runs as a service). The log line `Glimmervoid remote listener on http://127.0.0.1:3456 (paired devices only)` confirms the listener is up.
|
|
147
|
-
3. Point an HTTPS reverse proxy at that port. With Tailscale, `tailscale serve --bg 3456` serves `https://<machine>.<tailnet>.ts.net`, which is the name `publicHost` should hold.
|
|
145
|
+
3. Point an HTTPS reverse proxy at that port. With Tailscale, `tailscale serve --bg 3456` serves `https://<machine>.<tailnet>.ts.net`, which is the name `publicHost` should hold. When the port is reached solely through HTTP-mode `tailscale serve`, also set `"ownerLogin": "you@example.com"` in the `remote` block: the listener then refuses every request and WebSocket whose `Tailscale-User-Login` is not that login, including Funnel and tagged-device traffic, which carry none. Only HTTP-mode `tailscale serve` strips a client-sent `Tailscale-User-Login`; behind any other proxy, or `tailscale serve --tcp` or `--tls-terminated-tcp`, a client can forge it, so `ownerLogin` proves nothing there. It works on top of pairing, never instead of it.
|
|
148
146
|
4. Verify from the device: `https://<publicHost>/` should answer with Glimmervoid's "Pairing required" page.
|
|
149
147
|
5. On the host, run `glimmervoid pair --name phone`. It prints a single-use URL valid for 10 minutes that sets an auth cookie when opened on the device.
|
|
150
148
|
|
package/dist/bin/glimmervoid.js
CHANGED
|
@@ -1,15 +1,93 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { o as nodePtyRebuildHint, t as probeNodePty } from "../chunks/node-pty-preflight-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { o as nodePtyRebuildHint, r as firstLine, t as probeNodePty } from "../chunks/node-pty-preflight-C8QIlrtU.js";
|
|
3
|
+
import { i as errorText } from "../chunks/text-DoirglWV.js";
|
|
4
|
+
import { a as packageRoot } from "../chunks/runtime-paths-BPoxENmK.js";
|
|
5
|
+
import { t as AGENT_API_VERBS } from "../chunks/session-fc4pFbon.js";
|
|
6
|
+
import { i as execSync } from "../chunks/child-process-safe-Ch-Zgxqs.js";
|
|
6
7
|
import { t as renderTable } from "../chunks/ascii-figure-core-pDAwtlKO.js";
|
|
7
8
|
import { n as glimmervoidHomeDir, t as decideConfigPath } from "../chunks/config-path-core-DFt-TemS.js";
|
|
8
|
-
import { i as sandboxDoctorRows, r as probeSandboxDependencies } from "../chunks/sandbox-deps-
|
|
9
|
+
import { i as sandboxDoctorRows, r as probeSandboxDependencies } from "../chunks/sandbox-deps-BPKE-ylr.js";
|
|
9
10
|
import { i as pnpmGlobalBinDir, n as npmGlobalBinDir, r as onPath, t as formatPathNotice } from "../chunks/path-doctor-C23VAqeu.js";
|
|
10
11
|
import fs from "node:fs";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import os from "node:os";
|
|
14
|
+
//#region server/core/command-line-core.ts
|
|
15
|
+
var SUBCOMMANDS_HONORING_TRAILING_GLOBAL_OPTIONS = /* @__PURE__ */ new Set([
|
|
16
|
+
"doctor",
|
|
17
|
+
"pair",
|
|
18
|
+
"visions"
|
|
19
|
+
]);
|
|
20
|
+
var SUBCOMMANDS_HONORING_A_LEADING_HELP_OR_VERSION = new Set(AGENT_API_VERBS);
|
|
21
|
+
var OPTIONS_TAKING_A_VALUE = {
|
|
22
|
+
"--config": "configPath",
|
|
23
|
+
"--port": "port"
|
|
24
|
+
};
|
|
25
|
+
function scanGlobalOption(argv, index, state) {
|
|
26
|
+
const token = argv[index];
|
|
27
|
+
const valueKey = OPTIONS_TAKING_A_VALUE[token];
|
|
28
|
+
if (valueKey) {
|
|
29
|
+
const value = argv[index + 1];
|
|
30
|
+
if (value !== void 0) state[valueKey] = value;
|
|
31
|
+
return index + 2;
|
|
32
|
+
}
|
|
33
|
+
if (token === "--help" || token === "-h") state.wantsHelp = true;
|
|
34
|
+
if (token === "--version") state.wantsVersion = true;
|
|
35
|
+
if (token === "--doctor") state.wantsDoctor = true;
|
|
36
|
+
return index + 1;
|
|
37
|
+
}
|
|
38
|
+
function scanTrailingGlobalOptions(subcommandArgs, state) {
|
|
39
|
+
let index = 0;
|
|
40
|
+
while (index < subcommandArgs.length) {
|
|
41
|
+
if (!subcommandArgs[index].startsWith("-")) {
|
|
42
|
+
index += 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
index = scanGlobalOption(subcommandArgs, index, state);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function scanLeadingHelpOrVersion(subcommandArgs, state) {
|
|
49
|
+
const firstArgument = subcommandArgs[0];
|
|
50
|
+
if (firstArgument === "--help" || firstArgument === "-h") state.wantsHelp = true;
|
|
51
|
+
if (firstArgument === "--version") state.wantsVersion = true;
|
|
52
|
+
}
|
|
53
|
+
function parseCommandLine(argv) {
|
|
54
|
+
const state = {
|
|
55
|
+
configPath: null,
|
|
56
|
+
port: null,
|
|
57
|
+
wantsHelp: false,
|
|
58
|
+
wantsVersion: false,
|
|
59
|
+
wantsDoctor: false
|
|
60
|
+
};
|
|
61
|
+
let index = 0;
|
|
62
|
+
while (index < argv.length && argv[index].startsWith("-")) index = scanGlobalOption(argv, index, state);
|
|
63
|
+
const name = argv[index];
|
|
64
|
+
const subcommandArgs = argv.slice(index + 1);
|
|
65
|
+
if (name !== void 0 && SUBCOMMANDS_HONORING_TRAILING_GLOBAL_OPTIONS.has(name)) scanTrailingGlobalOptions(subcommandArgs, state);
|
|
66
|
+
if (name !== void 0 && SUBCOMMANDS_HONORING_A_LEADING_HELP_OR_VERSION.has(name)) scanLeadingHelpOrVersion(subcommandArgs, state);
|
|
67
|
+
if (state.wantsHelp) return { kind: "help" };
|
|
68
|
+
if (state.wantsVersion) return { kind: "version" };
|
|
69
|
+
const globalOptions = {
|
|
70
|
+
configPath: state.configPath,
|
|
71
|
+
port: state.port
|
|
72
|
+
};
|
|
73
|
+
if (state.wantsDoctor) return {
|
|
74
|
+
kind: "subcommand",
|
|
75
|
+
name: "doctor",
|
|
76
|
+
subcommandArgs: [],
|
|
77
|
+
globalOptions
|
|
78
|
+
};
|
|
79
|
+
if (name === void 0) return {
|
|
80
|
+
kind: "server",
|
|
81
|
+
globalOptions
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
kind: "subcommand",
|
|
85
|
+
name,
|
|
86
|
+
subcommandArgs,
|
|
87
|
+
globalOptions
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
13
91
|
//#region bin/glimmervoid.ts
|
|
14
92
|
var args = process.argv.slice(2);
|
|
15
93
|
var pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
@@ -28,6 +106,7 @@ Commands:
|
|
|
28
106
|
spawn <prompt> From inside a Glimmervoid session, start a sibling session on that prompt
|
|
29
107
|
attention <note> From inside a Glimmervoid session, flag it as needing the operator
|
|
30
108
|
board From inside a Glimmervoid session, list the live sessions
|
|
109
|
+
kg [command] Experimental typed knowledge and task graph (Settings > Lanes > Knowledge graph)
|
|
31
110
|
|
|
32
111
|
Options:
|
|
33
112
|
--name <label> Label for the device being paired (with: pair)
|
|
@@ -35,71 +114,75 @@ Options:
|
|
|
35
114
|
--config <path> Path to config file (default: ~/.glimmervoid/config.json)
|
|
36
115
|
--version Show version number
|
|
37
116
|
--help, -h Show this help message`;
|
|
38
|
-
|
|
117
|
+
var commandLine = parseCommandLine(args);
|
|
118
|
+
if (commandLine.kind === "help") {
|
|
39
119
|
console.log(USAGE);
|
|
40
120
|
process.exit(0);
|
|
41
121
|
}
|
|
42
|
-
if (
|
|
122
|
+
if (commandLine.kind === "version") {
|
|
43
123
|
console.log(pkg.version);
|
|
44
124
|
process.exit(0);
|
|
45
125
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
var configArg = getArgValue("--config");
|
|
52
|
-
if (configArg) process.env.GLIMMERVOID_CONFIG = configArg;
|
|
53
|
-
var portArg = getArgValue("--port");
|
|
54
|
-
if (portArg) process.env.GLIMMERVOID_PORT = portArg;
|
|
126
|
+
if (commandLine.globalOptions.configPath) process.env.GLIMMERVOID_CONFIG = commandLine.globalOptions.configPath;
|
|
127
|
+
if (commandLine.globalOptions.port) process.env.GLIMMERVOID_PORT = commandLine.globalOptions.port;
|
|
55
128
|
var SUBCOMMAND_RUNNERS = new Map([
|
|
56
129
|
["doctor", async () => {
|
|
57
130
|
await runDoctor();
|
|
58
131
|
return 0;
|
|
59
132
|
}],
|
|
60
|
-
["pair", async (
|
|
61
|
-
const { runPairCli } = await import("../chunks/pair-cli-
|
|
62
|
-
return runPairCli(
|
|
133
|
+
["pair", async (subcommandArgs) => {
|
|
134
|
+
const { runPairCli } = await import("../chunks/pair-cli-BbCBfd6l.js");
|
|
135
|
+
return runPairCli(subcommandArgs);
|
|
136
|
+
}],
|
|
137
|
+
["agent", async (subcommandArgs) => {
|
|
138
|
+
const { runAgentSetupCli } = await import("../chunks/agent-setup-cli-CiJSDWSo.js");
|
|
139
|
+
return runAgentSetupCli(subcommandArgs);
|
|
63
140
|
}],
|
|
64
|
-
["
|
|
65
|
-
const {
|
|
66
|
-
return
|
|
141
|
+
["kg", async (subcommandArgs) => {
|
|
142
|
+
const { runKnowledgeGraphCommand } = await import("../chunks/knowledge-graph-cli-D2tt5hPn.js");
|
|
143
|
+
return runKnowledgeGraphCommand(subcommandArgs);
|
|
67
144
|
}],
|
|
68
|
-
["visions", async (
|
|
69
|
-
const { runVisionsCli } = await import("../chunks/visions-cli-
|
|
70
|
-
return runVisionsCli(
|
|
145
|
+
["visions", async (subcommandArgs) => {
|
|
146
|
+
const { runVisionsCli } = await import("../chunks/visions-cli-CT5rjiwf.js");
|
|
147
|
+
return runVisionsCli(subcommandArgs);
|
|
71
148
|
}],
|
|
72
|
-
...AGENT_API_VERBS.map((verb) => [verb, async (
|
|
73
|
-
const { runAgentApiCli } = await import("../chunks/agent-api-cli-
|
|
74
|
-
return runAgentApiCli(
|
|
149
|
+
...AGENT_API_VERBS.map((verb) => [verb, async (subcommandArgs, subcommandName) => {
|
|
150
|
+
const { runAgentApiCli } = await import("../chunks/agent-api-cli-DAA7Jx8N.js");
|
|
151
|
+
return runAgentApiCli([subcommandName, ...subcommandArgs]);
|
|
75
152
|
}])
|
|
76
153
|
]);
|
|
154
|
+
function flushStream(stream) {
|
|
155
|
+
return new Promise((resolve) => {
|
|
156
|
+
stream.once("error", () => resolve());
|
|
157
|
+
stream.write("", () => resolve());
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async function exitAfterOutputDrains(exitCode) {
|
|
161
|
+
await Promise.all([flushStream(process.stdout), flushStream(process.stderr)]);
|
|
162
|
+
process.exit(exitCode);
|
|
163
|
+
}
|
|
164
|
+
async function runSubcommandToExitCode(runSubcommand, subcommandArgs, subcommandName) {
|
|
165
|
+
try {
|
|
166
|
+
return await runSubcommand(subcommandArgs, subcommandName);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.error(errorText(err));
|
|
169
|
+
return 1;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
77
172
|
async function dispatchCommandLine() {
|
|
78
|
-
|
|
79
|
-
if (!requestedSubcommand || requestedSubcommand.startsWith("-")) {
|
|
173
|
+
if (commandLine.kind !== "subcommand") {
|
|
80
174
|
await import("../server/index.js");
|
|
81
175
|
return;
|
|
82
176
|
}
|
|
83
|
-
const runSubcommand = SUBCOMMAND_RUNNERS.get(
|
|
177
|
+
const runSubcommand = SUBCOMMAND_RUNNERS.get(commandLine.name);
|
|
84
178
|
if (!runSubcommand) {
|
|
85
|
-
console.error(`Unknown command: ${
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
process.exit(await runSubcommand(args));
|
|
90
|
-
} catch (err) {
|
|
91
|
-
console.error(messageOf(err));
|
|
92
|
-
process.exit(1);
|
|
179
|
+
console.error(`Unknown command: ${commandLine.name}\n${USAGE}`);
|
|
180
|
+
await exitAfterOutputDrains(1);
|
|
181
|
+
return;
|
|
93
182
|
}
|
|
183
|
+
await exitAfterOutputDrains(await runSubcommandToExitCode(runSubcommand, commandLine.subcommandArgs, commandLine.name));
|
|
94
184
|
}
|
|
95
185
|
await dispatchCommandLine();
|
|
96
|
-
function messageOf(error) {
|
|
97
|
-
if (error instanceof Error && error.message) return error.message;
|
|
98
|
-
return String(error);
|
|
99
|
-
}
|
|
100
|
-
function firstLineOf(error) {
|
|
101
|
-
return messageOf(error).split("\n")[0];
|
|
102
|
-
}
|
|
103
186
|
function decideReadOnlyConfigPath() {
|
|
104
187
|
return decideConfigPath({
|
|
105
188
|
env: process.env,
|
|
@@ -118,7 +201,7 @@ async function readDeclaredCustomAgents() {
|
|
|
118
201
|
declared: [],
|
|
119
202
|
error: null
|
|
120
203
|
};
|
|
121
|
-
const { loadConfigFile } = await import("../chunks/config-store-
|
|
204
|
+
const { loadConfigFile } = await import("../chunks/config-store-DbBp7LJZ.js");
|
|
122
205
|
try {
|
|
123
206
|
const loaded = loadConfigFile(decided.path, { exitOnError: false });
|
|
124
207
|
if (!loaded.config) return {
|
|
@@ -132,7 +215,7 @@ async function readDeclaredCustomAgents() {
|
|
|
132
215
|
} catch (err) {
|
|
133
216
|
return {
|
|
134
217
|
declared: [],
|
|
135
|
-
error: `could not read ${decided.path}: ${
|
|
218
|
+
error: `could not read ${decided.path}: ${firstLine(errorText(err))}`
|
|
136
219
|
};
|
|
137
220
|
}
|
|
138
221
|
}
|
|
@@ -194,7 +277,7 @@ async function runDoctor() {
|
|
|
194
277
|
}
|
|
195
278
|
switchSection("Agents");
|
|
196
279
|
try {
|
|
197
|
-
const { listAgentIds, describeAgentResolvability, setCustomAgents } = await import("../chunks/adapters-
|
|
280
|
+
const { listAgentIds, describeAgentResolvability, setCustomAgents } = await import("../chunks/adapters-ClA_GbtA.js");
|
|
198
281
|
const declaredCustomAgents = await readDeclaredCustomAgents();
|
|
199
282
|
if (declaredCustomAgents.error) line("config", declaredCustomAgents.error);
|
|
200
283
|
setCustomAgents(declaredCustomAgents.declared);
|
|
@@ -202,18 +285,34 @@ async function runDoctor() {
|
|
|
202
285
|
const { label, path: resolvedPath } = describeAgentResolvability(id);
|
|
203
286
|
line(`${id} (${label})`, resolvedPath ?? "not found on PATH");
|
|
204
287
|
}
|
|
205
|
-
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-
|
|
288
|
+
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-CiJSDWSo.js");
|
|
206
289
|
const grokSetup = inspectGrokAgentSetup();
|
|
207
290
|
line("grok hook setup", `${grokSetup.classification}: ${grokSetup.filePath}`);
|
|
208
291
|
} catch (err) {
|
|
209
|
-
line("agents", `probe failed: ${
|
|
292
|
+
line("agents", `probe failed: ${firstLine(errorText(err))}`);
|
|
210
293
|
}
|
|
211
294
|
switchSection("rtk");
|
|
212
295
|
try {
|
|
213
|
-
const { getRtkPath } = await import("../chunks/rtk-resolver-
|
|
296
|
+
const { getRtkPath } = await import("../chunks/rtk-resolver-D4Nke1Fr.js");
|
|
214
297
|
line("rtk", getRtkPath() || "not installed (Glimmervoid installs it when the rtk setting is on)");
|
|
215
298
|
} catch (err) {
|
|
216
|
-
line("rtk", `probe failed: ${
|
|
299
|
+
line("rtk", `probe failed: ${firstLine(errorText(err))}`);
|
|
300
|
+
}
|
|
301
|
+
switchSection("Sane YOLO");
|
|
302
|
+
try {
|
|
303
|
+
const { resolvePackageBin } = await import("../chunks/runtime-paths-DU5jBEEO.js");
|
|
304
|
+
const { execFileSync } = await import("../chunks/child-process-safe-BKPZ-5qQ.js");
|
|
305
|
+
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-CiJSDWSo.js");
|
|
306
|
+
const binPath = resolvePackageBin("cc-safety-net", "cc-safety-net");
|
|
307
|
+
line("Sane YOLO", binPath || "not installed");
|
|
308
|
+
if (binPath) line("cc-safety-net version", execFileSync(process.execPath, [binPath, "--version"], {
|
|
309
|
+
encoding: "utf8",
|
|
310
|
+
timeout: 3e3
|
|
311
|
+
}).trim());
|
|
312
|
+
line("Codex hook trust", "Sane YOLO is inactive unless Codex trusts the injected hooks.");
|
|
313
|
+
line("Grok Sane YOLO", inspectGrokAgentSetup().saneYoloReady ? "installed" : "inactive; run glimmervoid agent setup grok");
|
|
314
|
+
} catch (err) {
|
|
315
|
+
line("Sane YOLO", `probe failed: ${firstLine(errorText(err))}`);
|
|
217
316
|
}
|
|
218
317
|
switchSection("Native module");
|
|
219
318
|
const nodePty = await probeNodePty();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as describeAgentResolvability, d as setCustomAgents, l as listAgentIds } from "./adapters-
|
|
1
|
+
import { a as describeAgentResolvability, d as setCustomAgents, l as listAgentIds } from "./adapters-gn7ulNO4.js";
|
|
2
2
|
export { describeAgentResolvability, listAgentIds, setCustomAgents };
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { o as relayPath } from "./runtime-paths-
|
|
2
|
-
import {
|
|
3
|
-
import { r as execFileSync } from "./child-process-safe-
|
|
1
|
+
import { o as relayPath } from "./runtime-paths-BPoxENmK.js";
|
|
2
|
+
import { _ as firstDetailLine, l as PendingPromptQuestion, s as PROMPT_DETAIL_HIDDEN_CHARACTERS, v as toolDetailFieldValue } from "./session-fc4pFbon.js";
|
|
3
|
+
import { r as execFileSync } from "./child-process-safe-Ch-Zgxqs.js";
|
|
4
4
|
import { n as resolveAgentCommand, t as buildAgentSpawnCommand } from "./spawn-command-Cc40FiZI.js";
|
|
5
|
-
import {
|
|
6
|
-
import "./
|
|
5
|
+
import { n as isRecord } from "./coerce-NSst8oRP.js";
|
|
6
|
+
import { c as buildAgentEnv, i as isSpinnerChar, n as classifyAgentTitle, r as isBrailleChar, t as grok } from "./grok-CXKeSt2z.js";
|
|
7
|
+
import { t as buildHookCommand } from "./hook-command-core-DclEoE9e.js";
|
|
8
|
+
import "./contracts-3LkrilQX.js";
|
|
9
|
+
import { n as mergeCodexPreToolUse, t as HOOK_TOOLS } from "./hook-tools-UX2fmt81.js";
|
|
7
10
|
import fs from "node:fs";
|
|
8
11
|
//#region session/core/anti-slop-prompt.ts
|
|
9
12
|
var ANTI_SLOP_NOTE = [
|
|
@@ -85,9 +88,6 @@ function mapHookPromptDetail(event, payload) {
|
|
|
85
88
|
question: answerableQuestionOf(toolName, payload?.tool_input)
|
|
86
89
|
};
|
|
87
90
|
}
|
|
88
|
-
function isRecord(value) {
|
|
89
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
90
|
-
}
|
|
91
91
|
function answerableQuestionOf(toolName, toolInput) {
|
|
92
92
|
if (toolName !== "AskUserQuestion" || !isRecord(toolInput)) return null;
|
|
93
93
|
const questions = toolInput.questions;
|
|
@@ -121,6 +121,8 @@ function isWholeBashCommandShown(toolName, toolInput, fieldValue, summary) {
|
|
|
121
121
|
function mapHookToSignal$1(event, payload) {
|
|
122
122
|
switch (String(event || "").toLowerCase()) {
|
|
123
123
|
case "sessionstart": return "session-start";
|
|
124
|
+
case "precompact": return "compaction-start";
|
|
125
|
+
case "postcompact": return "compaction-end";
|
|
124
126
|
case "sessionend": return "session-end";
|
|
125
127
|
case "userpromptsubmit": return "resume";
|
|
126
128
|
case "stop": return "ready";
|
|
@@ -176,7 +178,15 @@ var settingsArgs = (settingsPath) => ["--settings", settingsPath];
|
|
|
176
178
|
function buildArgs$1({ dangerouslySkipPermissions = false, resumeSessionId = null, extraArgs = [], antiSlopPrompt = false, initialPrompt = null } = {}) {
|
|
177
179
|
const args = dangerouslySkipPermissions ? ["--dangerously-skip-permissions"] : [];
|
|
178
180
|
if (resumeSessionId) args.push("--resume", resumeSessionId);
|
|
179
|
-
|
|
181
|
+
for (let index = 0; index < extraArgs.length; index += 1) {
|
|
182
|
+
const argument = extraArgs[index];
|
|
183
|
+
if (resumeSessionId && argument === "--session-id") {
|
|
184
|
+
index += 1;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (resumeSessionId && argument.startsWith("--session-id=")) continue;
|
|
188
|
+
args.push(argument);
|
|
189
|
+
}
|
|
180
190
|
args.push(...buildAntiSlopArgs(antiSlopPrompt));
|
|
181
191
|
if (initialPrompt != null) args.push(initialPrompt);
|
|
182
192
|
return args;
|
|
@@ -201,6 +211,7 @@ var claudeCode = {
|
|
|
201
211
|
resume: true,
|
|
202
212
|
statusLine: true,
|
|
203
213
|
rtk: true,
|
|
214
|
+
saneYolo: true,
|
|
204
215
|
antiSlop: true,
|
|
205
216
|
compactQuiet: true,
|
|
206
217
|
skipPermissionsFlag: true,
|
|
@@ -219,9 +230,7 @@ var claudeCode = {
|
|
|
219
230
|
var ID = "codex";
|
|
220
231
|
var COMMAND_NAME = "codex";
|
|
221
232
|
var RELAY_PATH = relayPath("hook-relay");
|
|
222
|
-
var
|
|
223
|
-
var RTK_HOOK_EVENT = "PreToolUse";
|
|
224
|
-
var RTK_TOOL_MATCHER = "Bash";
|
|
233
|
+
var HOOK_TOOL_RELAY_PATH = relayPath("hook-tool-relay");
|
|
225
234
|
var HOOK_EVENTS = [
|
|
226
235
|
"SessionStart",
|
|
227
236
|
"SessionEnd",
|
|
@@ -285,17 +294,23 @@ function mayContributeHooks(configText) {
|
|
|
285
294
|
if (typeof configText !== "string") return false;
|
|
286
295
|
return HOOKS_DECLARATION_RE.test(configText);
|
|
287
296
|
}
|
|
288
|
-
function buildHookArgs({ relayPath = RELAY_PATH, events = HOOK_EVENTS, bypassHookTrust = false,
|
|
297
|
+
function buildHookArgs({ relayPath = RELAY_PATH, events = HOOK_EVENTS, bypassHookTrust = false, hookTools = [], hookToolRelayPath = HOOK_TOOL_RELAY_PATH } = {}) {
|
|
289
298
|
const args = bypassHookTrust ? [TRUST_BYPASS_FLAG] : [];
|
|
299
|
+
const preToolUseGroups = [];
|
|
290
300
|
for (const event of events) {
|
|
291
301
|
const command = buildHookCommand(relayPath, event);
|
|
292
302
|
if (!command) return null;
|
|
303
|
+
if (event === "PreToolUse") {
|
|
304
|
+
preToolUseGroups.push(`{hooks=[{type='command',command='${command}'}]}`);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
293
307
|
args.push("-c", `hooks.${event}=[{hooks=[{type='command',command='${command}'}]}]`);
|
|
294
308
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
309
|
+
const groups = hookTools.flatMap((tool) => {
|
|
310
|
+
const group = HOOK_TOOLS[tool.id].codexGroup(tool, hookToolRelayPath);
|
|
311
|
+
return group ? [group] : [];
|
|
312
|
+
});
|
|
313
|
+
args.push(...mergeCodexPreToolUse([...preToolUseGroups, ...groups]));
|
|
299
314
|
return args;
|
|
300
315
|
}
|
|
301
316
|
var hooks = {
|
|
@@ -337,10 +352,22 @@ var SKIP_PERMISSIONS_ARGS = [
|
|
|
337
352
|
"-s",
|
|
338
353
|
"workspace-write"
|
|
339
354
|
];
|
|
340
|
-
|
|
355
|
+
var OUTER_SANDBOX_ARGS = ["-s", "danger-full-access"];
|
|
356
|
+
var OUTER_SANDBOX_SKIP_PERMISSIONS_ARGS = [
|
|
357
|
+
"-a",
|
|
358
|
+
"never",
|
|
359
|
+
...OUTER_SANDBOX_ARGS
|
|
360
|
+
];
|
|
361
|
+
function permissionArgs(dangerouslySkipPermissions, hasOuterSandbox) {
|
|
362
|
+
if (hasOuterSandbox && dangerouslySkipPermissions) return OUTER_SANDBOX_SKIP_PERMISSIONS_ARGS;
|
|
363
|
+
if (hasOuterSandbox) return OUTER_SANDBOX_ARGS;
|
|
364
|
+
if (dangerouslySkipPermissions) return SKIP_PERMISSIONS_ARGS;
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
function buildArgs({ dangerouslySkipPermissions = false, resumeSessionId = null, extraArgs = [], initialPrompt = null, hasOuterSandbox = false } = {}) {
|
|
341
368
|
const args = [...UPDATE_CHECK_ARGS];
|
|
342
369
|
if (resumeSessionId) args.push("resume", resumeSessionId);
|
|
343
|
-
|
|
370
|
+
args.push(...permissionArgs(dangerouslySkipPermissions, hasOuterSandbox));
|
|
344
371
|
if (extraArgs.length > 0) args.push(...extraArgs);
|
|
345
372
|
if (initialPrompt != null) args.push(initialPrompt);
|
|
346
373
|
return args;
|
|
@@ -368,7 +395,7 @@ var codex = {
|
|
|
368
395
|
sessionIdOf,
|
|
369
396
|
HOOK_EVENTS,
|
|
370
397
|
RELAY_PATH,
|
|
371
|
-
|
|
398
|
+
HOOK_TOOL_RELAY_PATH,
|
|
372
399
|
TRUST_BYPASS_FLAG,
|
|
373
400
|
SKIP_PERMISSIONS_ARGS,
|
|
374
401
|
UPDATE_CHECK_ARGS,
|
|
@@ -379,6 +406,7 @@ var codex = {
|
|
|
379
406
|
resume: true,
|
|
380
407
|
statusLine: false,
|
|
381
408
|
rtk: true,
|
|
409
|
+
saneYolo: true,
|
|
382
410
|
antiSlop: false,
|
|
383
411
|
compactQuiet: false,
|
|
384
412
|
skipPermissionsFlag: true,
|
|
@@ -398,6 +426,7 @@ var TITLE_ONLY_CAPABILITIES = Object.freeze({
|
|
|
398
426
|
resume: false,
|
|
399
427
|
statusLine: false,
|
|
400
428
|
rtk: false,
|
|
429
|
+
saneYolo: false,
|
|
401
430
|
antiSlop: false,
|
|
402
431
|
compactQuiet: false,
|
|
403
432
|
skipPermissionsFlag: false,
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { t as errorCode } from "./text-DoirglWV.js";
|
|
2
|
+
import { a as AgentDispatchRequest, n as AGENT_URL_ENV } from "./session-fc4pFbon.js";
|
|
3
|
+
import { i as resolveHookTarget } from "./hook-relay-core-DA_bz2KA.js";
|
|
4
|
+
import { n as decideSandboxProxy } from "./sandbox-proxy-core-hnQgKR0T.js";
|
|
5
|
+
import { a as isAgentApiVerb } from "./agent-api-core-hVcT26JW.js";
|
|
6
|
+
import http from "node:http";
|
|
7
|
+
//#region server/agent-api-cli.ts
|
|
8
|
+
var AGENT_PATH_PREFIX = "/agent/";
|
|
9
|
+
var BODY_BUILDERS = {
|
|
10
|
+
spawn: (rest) => {
|
|
11
|
+
const prompt = rest.join(" ").trim();
|
|
12
|
+
return prompt ? { prompt } : null;
|
|
13
|
+
},
|
|
14
|
+
attention: (rest) => {
|
|
15
|
+
const note = rest.join(" ").trim();
|
|
16
|
+
return note ? { note } : null;
|
|
17
|
+
},
|
|
18
|
+
board: () => ({}),
|
|
19
|
+
dispatch: (rest) => {
|
|
20
|
+
const parsed = AgentDispatchRequest.safeParse(rest[0] === "--ready" ? { readyIntent: rest[1] } : { workId: rest[0] });
|
|
21
|
+
return parsed.success ? parsed.data : null;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
function bodySaysOk(text) {
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(text);
|
|
27
|
+
return typeof parsed === "object" && parsed !== null && parsed.ok === true;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function verbEndpoint(rawUrl, verb) {
|
|
33
|
+
const target = resolveHookTarget(rawUrl, verb, AGENT_PATH_PREFIX);
|
|
34
|
+
if (!target.url) return { ok: false };
|
|
35
|
+
const endpoint = new URL(target.url);
|
|
36
|
+
const token = endpoint.searchParams.get("t") || "";
|
|
37
|
+
endpoint.search = "";
|
|
38
|
+
return {
|
|
39
|
+
ok: true,
|
|
40
|
+
endpoint,
|
|
41
|
+
token
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function postDirectly(endpoint, headers, bodyText) {
|
|
45
|
+
const response = await fetch(endpoint, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers,
|
|
48
|
+
body: bodyText
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
hasSuccessStatus: response.ok,
|
|
52
|
+
text: await response.text()
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function postThroughSandboxProxy(sandboxProxy, endpoint, headers, bodyText) {
|
|
56
|
+
const proxyHeaders = sandboxProxy.authorization ? { "proxy-authorization": sandboxProxy.authorization } : {};
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const request = http.request({
|
|
59
|
+
host: sandboxProxy.hostname,
|
|
60
|
+
port: sandboxProxy.port,
|
|
61
|
+
method: "POST",
|
|
62
|
+
path: endpoint.href,
|
|
63
|
+
headers: {
|
|
64
|
+
...headers,
|
|
65
|
+
...proxyHeaders,
|
|
66
|
+
host: endpoint.host,
|
|
67
|
+
"content-length": String(Buffer.byteLength(bodyText))
|
|
68
|
+
}
|
|
69
|
+
}, (response) => {
|
|
70
|
+
const chunks = [];
|
|
71
|
+
response.on("data", (chunk) => {
|
|
72
|
+
chunks.push(chunk);
|
|
73
|
+
});
|
|
74
|
+
response.on("error", reject);
|
|
75
|
+
response.on("end", () => {
|
|
76
|
+
const statusCode = response.statusCode ?? 0;
|
|
77
|
+
resolve({
|
|
78
|
+
hasSuccessStatus: statusCode >= 200 && statusCode < 300,
|
|
79
|
+
text: Buffer.concat(chunks).toString("utf8")
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
request.on("error", reject);
|
|
84
|
+
request.end(bodyText);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function wasConnectionRefused(error) {
|
|
88
|
+
return errorCode(error?.cause) === "ECONNREFUSED";
|
|
89
|
+
}
|
|
90
|
+
async function postToAgentIngress(endpoint, token, body) {
|
|
91
|
+
const headers = {
|
|
92
|
+
"content-type": "application/json",
|
|
93
|
+
authorization: `Bearer ${token}`
|
|
94
|
+
};
|
|
95
|
+
const bodyText = JSON.stringify(body);
|
|
96
|
+
try {
|
|
97
|
+
return await postDirectly(endpoint, headers, bodyText);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (!wasConnectionRefused(error)) throw error;
|
|
100
|
+
const sandboxProxy = decideSandboxProxy(process.env);
|
|
101
|
+
if (!sandboxProxy) throw error;
|
|
102
|
+
return postThroughSandboxProxy(sandboxProxy, endpoint, headers, bodyText);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function runAgentApiCli(args) {
|
|
106
|
+
const verb = args[0];
|
|
107
|
+
if (!verb || !isAgentApiVerb(verb)) {
|
|
108
|
+
console.error(`glimmervoid: ${verb || "that"} is not an agent command`);
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
const body = BODY_BUILDERS[verb](args.slice(1));
|
|
112
|
+
if (!body) {
|
|
113
|
+
if (verb === "dispatch") {
|
|
114
|
+
console.error("glimmervoid dispatch: expected a work id matching wrk-[0-9a-f]{16}");
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
console.error(`glimmervoid ${verb}: this command needs text, for example "glimmervoid ${verb} check the failing test"`);
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
const rawUrl = process.env[AGENT_URL_ENV];
|
|
121
|
+
if (!rawUrl) {
|
|
122
|
+
console.error(`glimmervoid: ${AGENT_URL_ENV} is not set, so this is not a Glimmervoid session with the agent API turned on`);
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
const target = verbEndpoint(rawUrl, verb);
|
|
126
|
+
if (!target.ok) {
|
|
127
|
+
console.error(`glimmervoid: ${AGENT_URL_ENV} must be a loopback http url under ${AGENT_PATH_PREFIX}, so nothing was sent`);
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
const reply = await postToAgentIngress(target.endpoint, target.token, body);
|
|
131
|
+
console.log(reply.text);
|
|
132
|
+
return reply.hasSuccessStatus && bodySaysOk(reply.text) ? 0 : 1;
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { runAgentApiCli };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as AgentDispatchRequest, i as AgentAttentionRequest, o as AgentSpawnRequest, t as AGENT_API_VERBS } from "./session-fc4pFbon.js";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
3
|
var MAX_SPAWNS_IN_FLIGHT = 1;
|
|
4
4
|
var SPAWNABLE_DEPTH = 0;
|
|
@@ -67,6 +67,15 @@ function parseAgentVerb(verb, payload) {
|
|
|
67
67
|
status: 404,
|
|
68
68
|
error: REFUSAL_REASON
|
|
69
69
|
};
|
|
70
|
+
if (verb === "dispatch") {
|
|
71
|
+
const parsed = AgentDispatchRequest.safeParse(payload);
|
|
72
|
+
if (!parsed.success) return refuseInvalidBody(parsed.error.issues);
|
|
73
|
+
return {
|
|
74
|
+
ok: true,
|
|
75
|
+
verb,
|
|
76
|
+
request: parsed.data
|
|
77
|
+
};
|
|
78
|
+
}
|
|
70
79
|
if (verb === "board") return {
|
|
71
80
|
ok: true,
|
|
72
81
|
verb
|