agentglow 0.2.11 → 0.3.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 +3 -16
- package/cli/agentglow.mjs +22 -3
- package/cli/lib/server.mjs +44 -13
- package/cli/lib/settings.mjs +3 -3
- package/dist/chunks/KitScene-C_6JIRsN.js +4264 -0
- package/dist/chunks/index-Bp54n3io.js +48 -0
- package/dist/chunks/{index-CTZbUTDY.js → index-C8H7LPMJ.js} +136 -136
- package/dist/chunks/{index-URn4zDHp.js → index-CDuUwfpS.js} +297 -297
- package/dist/chunks/{index-B4Cqo621.js → index-CXqvoWvI.js} +155 -155
- package/dist/chunks/{index-u4rYZ1UO.js → index-CqpUvfPA.js} +135 -135
- package/dist/chunks/index-TlqkewVz.js +948 -0
- package/dist/index.js +1 -1
- package/dist/style.css +1 -1
- package/dist/types/AgentScene.d.ts +1 -1
- package/dist/types/themes.d.ts +1 -1
- package/dist/types/world.d.ts +270 -2
- package/package.json +1 -1
- package/dist/chunks/KitScene-DG_pcnWW.js +0 -3119
- package/dist/chunks/index-Agh7jdYu.js +0 -1086
- package/dist/chunks/index-B2EHxcBQ.js +0 -1010
- package/dist/chunks/index-BfQsJ0TH.js +0 -751
- package/dist/chunks/index-BmBGYhJS.js +0 -891
- package/dist/chunks/index-Bnn-x-aE.js +0 -767
- package/dist/chunks/index-CTQTtm9q.js +0 -784
- package/dist/chunks/index-CdKvybsL.js +0 -1143
- package/dist/chunks/index-CeiIEWek.js +0 -818
- package/dist/chunks/index-Ci8tSMhc.js +0 -68
- package/dist/chunks/index-Cls_VZdb.js +0 -980
- package/dist/chunks/index-DFFRhihO.js +0 -892
- package/dist/chunks/index-DMCETM4H.js +0 -605
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ import dynamic from "next/dynamic";
|
|
|
86
86
|
const AgentScene = dynamic(() => import("agentglow").then((m) => m.AgentScene), { ssr: false });
|
|
87
87
|
|
|
88
88
|
export default function Live() {
|
|
89
|
-
return <AgentScene theme="
|
|
89
|
+
return <AgentScene theme="orbit" source="http://localhost:8100" style={{ height: "80vh" }} />;
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
@@ -141,27 +141,14 @@ backoff on its own. Changing `scope`, `run` or `token` reconnects and clears the
|
|
|
141
141
|
|-----------|---------|
|
|
142
142
|
| `orbit` | Agents orbit a graph galaxy. Runs are rings and MCP servers are satellites. |
|
|
143
143
|
| `neural` | A living brain. Agents fire as neurons and messages pulse along synapses. |
|
|
144
|
-
| `subway` | A neon transit map. Each run is a line and each agent is a train. |
|
|
145
|
-
| `city` | A night city. Agents rise as skyscrapers in run districts. |
|
|
146
|
-
| `ocean` | Bioluminescent jellyfish drift on run currents over a coral graph. |
|
|
147
|
-
| `circuit` | Agent chips sit on run buses, wired to a memory bank and MCP I/O ports. |
|
|
148
|
-
| `tunnel` | A time warp. Runs are lanes and gates, and agents are ships. |
|
|
149
144
|
| `flow` | A murmuration. Agents condense as eddies out of the current. |
|
|
150
|
-
| `hive` | A glowing honeycomb. Agents are bees; subagents fly out as workers. |
|
|
151
|
-
| `forest` | A moonlit forest. Agents grow as trees, subagents as saplings, LLM calls as fireflies. |
|
|
152
145
|
| `constellation` | A night sky. Delegation draws constellation lines between agent stars. |
|
|
153
|
-
| `factory` | A neon factory floor. Agents are machines; work rides conveyor belts. |
|
|
154
|
-
| `airport` | A radar scope. Agents are flights; handoffs are flight paths. |
|
|
155
|
-
| `mycelium`| A glowing fungal network. Agents bloom as mushrooms on spreading threads. |
|
|
156
146
|
| `atom` | An atom. Agents are electrons; subagents orbit their parent. |
|
|
157
147
|
|
|
158
148
|
| | | |
|
|
159
149
|
|:-:|:-:|:-:|
|
|
160
|
-
|  **neural** |  **atom** |  **airport** |  **factory** |
|
|
163
|
-
|  **city** |  **ocean** |  **subway** |
|
|
164
|
-
|  **circuit** |  **tunnel** |  **flow** |
|
|
150
|
+
|  **neural** |  **constellation** |  **orbit** |
|
|
151
|
+
|  **atom** |  **flow** | |
|
|
165
152
|
|
|
166
153
|
## Layout
|
|
167
154
|
|
package/cli/agentglow.mjs
CHANGED
|
@@ -31,6 +31,9 @@ const HELP = `agentglow ${VERSION}: watch Claude Code agents in 3D
|
|
|
31
31
|
npx agentglow stop stop the background server
|
|
32
32
|
npx agentglow remove undo setup: remove the hooks and stop the server
|
|
33
33
|
npx agentglow claude [-- <args>] try it without installing: one claude session with AgentGlow
|
|
34
|
+
npx agentglow setup --capture-prompts
|
|
35
|
+
also show your prompts next to Claude's replies in the agent panel
|
|
36
|
+
(off by default; local server only, secrets redacted)
|
|
34
37
|
|
|
35
38
|
|
|
36
39
|
More: npx agentglow start [--background] [--port N] (serve = start in the foreground)
|
|
@@ -61,6 +64,7 @@ function parseArgs(argv) {
|
|
|
61
64
|
else if (a === "--uninstall") o.uninstall = true;
|
|
62
65
|
else if (a === "--remove") o.remove = true;
|
|
63
66
|
else if (a === "--no-autostart") o.autostart = false;
|
|
67
|
+
else if (a === "--capture-prompts") o.capturePrompts = true;
|
|
64
68
|
else if (a === "--background" || a === "-b") o.background = true;
|
|
65
69
|
else if (a === "--quiet" || a === "-q") o.quiet = true;
|
|
66
70
|
else if (a === "-h" || a === "--help") o.help = true;
|
|
@@ -141,6 +145,9 @@ async function cmdSetup(o) {
|
|
|
141
145
|
const remote = remoteUrl();
|
|
142
146
|
const port = o.port;
|
|
143
147
|
const base = remote || baseUrl({ port });
|
|
148
|
+
// Opt-in: the local server keeps the user's prompt text (login item env + SessionStart command + started server)
|
|
149
|
+
if (o.capturePrompts && !remote) process.env.AGENTGLOW_CAPTURE_PROMPTS = "1";
|
|
150
|
+
const capturePrompts = process.env.AGENTGLOW_CAPTURE_PROMPTS === "1";
|
|
144
151
|
// The AgentGlow Claude Code plugin brings the same hooks + server start; adding ours too would post every event twice.
|
|
145
152
|
const plugin = pluginIdsIn(file);
|
|
146
153
|
if (plugin.length) {
|
|
@@ -151,7 +158,7 @@ async function cmdSetup(o) {
|
|
|
151
158
|
if (!remote && !plugin.length) {
|
|
152
159
|
const script = installCliCopy({ srcCliDir: here, version: VERSION });
|
|
153
160
|
startCommand = startHookCommand({
|
|
154
|
-
port, version: VERSION, node: nodeForHook(), script, cacheDir: process.env.AGENTGLOW_CACHE_DIR,
|
|
161
|
+
port, version: VERSION, node: nodeForHook(), script, cacheDir: process.env.AGENTGLOW_CACHE_DIR, capturePrompts,
|
|
155
162
|
});
|
|
156
163
|
}
|
|
157
164
|
const r = installSettingsFile(file, base, { startCommand, hooks: !plugin.length, extra: remote ? {} : { port } });
|
|
@@ -184,6 +191,14 @@ async function cmdSetup(o) {
|
|
|
184
191
|
return 1;
|
|
185
192
|
}
|
|
186
193
|
}
|
|
194
|
+
if (o.capturePrompts && remote) console.log("--capture-prompts is for a local server only; ignored with AGENTGLOW_URL.");
|
|
195
|
+
else if (capturePrompts) {
|
|
196
|
+
const h = await fetchText(localBase(port) + "/live/health", 2000);
|
|
197
|
+
let on = false;
|
|
198
|
+
try { on = JSON.parse(h?.body || "{}").prompts === true; } catch { /* not ours */ }
|
|
199
|
+
console.log(on ? "Prompts: captured (local only)."
|
|
200
|
+
: `Prompts: the running server was started without capture; restart it: npx agentglow stop${portFlag(port)} && npx agentglow start --background${portFlag(port)}`);
|
|
201
|
+
}
|
|
187
202
|
if (o.open) openBrowser(view);
|
|
188
203
|
console.log(`\nDone. Just run \`claude\` as usual. View: ${view} Undo: npx agentglow remove`);
|
|
189
204
|
return 0;
|
|
@@ -194,11 +209,14 @@ async function cmdSetup(o) {
|
|
|
194
209
|
function installLoginItem(port) {
|
|
195
210
|
const script = installCliCopy({ srcCliDir: here, version: VERSION });
|
|
196
211
|
const node = nodeForHook() || process.execPath;
|
|
197
|
-
const command = startHookCommand({
|
|
212
|
+
const command = startHookCommand({
|
|
213
|
+
port, version: VERSION, node, script, cacheDir: process.env.AGENTGLOW_CACHE_DIR,
|
|
214
|
+
capturePrompts: process.env.AGENTGLOW_CAPTURE_PROMPTS === "1",
|
|
215
|
+
});
|
|
198
216
|
// macOS/Linux: the supervisor runs the server in the foreground (argv, no shell); Windows keeps the shell command
|
|
199
217
|
const argv = [node, script, "start", "--port", String(port)];
|
|
200
218
|
const env = { PATH: loginPath({ node }), HOME: os.homedir() };
|
|
201
|
-
for (const k of ["AGENTGLOW_CACHE_DIR", "AGENTGLOW_API_KEY", "AGENTGLOW_INGEST_KEY", "AGENTGLOW_SECRET", "AGENTGLOW_PY_SPEC"]) {
|
|
219
|
+
for (const k of ["AGENTGLOW_CACHE_DIR", "AGENTGLOW_API_KEY", "AGENTGLOW_INGEST_KEY", "AGENTGLOW_SECRET", "AGENTGLOW_PY_SPEC", "AGENTGLOW_CAPTURE_PROMPTS"]) {
|
|
202
220
|
if (process.env[k]) env[k] = process.env[k];
|
|
203
221
|
}
|
|
204
222
|
try {
|
|
@@ -331,6 +349,7 @@ async function cmdStatus(o) {
|
|
|
331
349
|
console.log(`hooks: ${hooksLine}`);
|
|
332
350
|
if (installed && plugin.length) console.log(`warning: hooks are in ${file} AND the plugin is enabled, so events post twice. Run \`npx agentglow remove\` or disable the plugin.`);
|
|
333
351
|
if (hasAutostart()) console.log(`login: server starts at login and restarts on crash`);
|
|
352
|
+
if (health?.prompts) console.log(`prompts: captured (local only)`);
|
|
334
353
|
if (health?.ok) console.log(`view: ${base}/neural`);
|
|
335
354
|
return 0;
|
|
336
355
|
}
|
package/cli/lib/server.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { spawn, spawnSync } from "node:child_process";
|
|
5
|
-
import { probe } from "./net.mjs";
|
|
5
|
+
import { fetchText, probe } from "./net.mjs";
|
|
6
6
|
import { cacheDir, findRunner } from "./uv.mjs";
|
|
7
7
|
|
|
8
8
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -19,10 +19,21 @@ export function pySpecs(version, env = process.env) {
|
|
|
19
19
|
return /^\d+\.\d+\.\d+$/.test(version || "") ? [`agentglow==${version}`, "agentglow"] : ["agentglow"];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Launch attempts in order: the pinned spec, the same pin again with uv's cached PyPI index refreshed (a version
|
|
24
|
+
* published after uv last fetched the index is otherwise "not found"), then the latest agentglow.
|
|
25
|
+
*/
|
|
26
|
+
export function pyAttempts(version, env = process.env) {
|
|
27
|
+
const [first, ...rest] = pySpecs(version, env);
|
|
28
|
+
const pinned = /^agentglow==/.test(first);
|
|
29
|
+
return [{ spec: first }, ...(pinned ? [{ spec: first, refresh: true }] : []), ...rest.map((spec) => ({ spec }))];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function serveArgv(runner, spec, port, { refresh = false } = {}) {
|
|
23
33
|
// a local checkout (AGENTGLOW_PY_SPEC=/path/to/backend): run it editable, uvx otherwise serves a stale cached build
|
|
24
34
|
const local = /^(\/|\.|~|[A-Za-z]:[\\/])/.test(spec);
|
|
25
|
-
return [...runner.args, ...(
|
|
35
|
+
return [...runner.args, ...(refresh ? ["--refresh-package", "agentglow"] : []),
|
|
36
|
+
...(local ? ["--with-editable", spec] : []), "--from", spec,
|
|
26
37
|
"agentglow", "serve", "--host", "127.0.0.1", "--port", String(port)];
|
|
27
38
|
}
|
|
28
39
|
|
|
@@ -37,6 +48,22 @@ export function readPid(port) {
|
|
|
37
48
|
} catch { return null; }
|
|
38
49
|
}
|
|
39
50
|
|
|
51
|
+
/** What to log when attempt i failed and attempt i + 1 (if any) comes next. */
|
|
52
|
+
export function retryNote(attempts, i, version) {
|
|
53
|
+
const { spec } = attempts[i], next = attempts[i + 1];
|
|
54
|
+
if (!next) return `${spec} failed to start, giving up`;
|
|
55
|
+
if (next.refresh) return `${spec} failed to start, refreshing uv's cached PyPI index and retrying`;
|
|
56
|
+
return `${spec} failed to start, falling back to the latest agentglow on PyPI` +
|
|
57
|
+
(version ? ` (this CLI expects ${version}, the server may be older)` : "");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** After a fallback start: say which server version is running when it is not the one this CLI expects. */
|
|
61
|
+
async function noteVersion(port, version, log) {
|
|
62
|
+
let v = "";
|
|
63
|
+
try { v = JSON.parse((await fetchText(localBase(port) + "/live/health", 2000))?.body || "{}").version || ""; } catch { /* unknown */ }
|
|
64
|
+
if (v !== version) log(` WARNING: running agentglow server ${v || "?"}, expected ${version}. Restart it once ${version} is on PyPI.`);
|
|
65
|
+
}
|
|
66
|
+
|
|
40
67
|
function tail(file, n = 15) {
|
|
41
68
|
try { return fs.readFileSync(file, "utf8").trimEnd().split("\n").slice(-n).join("\n"); } catch { return ""; }
|
|
42
69
|
}
|
|
@@ -50,9 +77,10 @@ export async function startBackground({ port, version, log = console.error, time
|
|
|
50
77
|
"First run downloads Python + agentglow, about 30-60s ...");
|
|
51
78
|
const logPath = logFile(port);
|
|
52
79
|
let lastErr = "";
|
|
53
|
-
|
|
80
|
+
const attempts = pyAttempts(version);
|
|
81
|
+
for (const [i, { spec, refresh }] of attempts.entries()) {
|
|
54
82
|
const fd = fs.openSync(logPath, "w");
|
|
55
|
-
const child = spawn(runner.cmd, serveArgv(runner, spec, port), {
|
|
83
|
+
const child = spawn(runner.cmd, serveArgv(runner, spec, port, { refresh }), {
|
|
56
84
|
detached: true, stdio: ["ignore", fd, fd], windowsHide: true,
|
|
57
85
|
env: { ...process.env, PYTHONUNBUFFERED: "1" },
|
|
58
86
|
});
|
|
@@ -65,7 +93,10 @@ export async function startBackground({ port, version, log = console.error, time
|
|
|
65
93
|
const t0 = Date.now();
|
|
66
94
|
let noted = false;
|
|
67
95
|
while (Date.now() - t0 < timeoutMs) {
|
|
68
|
-
if ((await probe(localBase(port), 1500)) === "agentglow")
|
|
96
|
+
if ((await probe(localBase(port), 1500)) === "agentglow") {
|
|
97
|
+
if (spec !== attempts[0].spec) await noteVersion(port, version, log);
|
|
98
|
+
return { pid: child.pid, started: true };
|
|
99
|
+
}
|
|
69
100
|
if (exited !== null) break;
|
|
70
101
|
if (!noted && Date.now() - t0 > 15000) { log(" still preparing the Python environment ..."); noted = true; }
|
|
71
102
|
await sleep(500);
|
|
@@ -77,7 +108,7 @@ export async function startBackground({ port, version, log = console.error, time
|
|
|
77
108
|
}
|
|
78
109
|
fs.rmSync(pidFile(port), { force: true });
|
|
79
110
|
lastErr = tail(logPath) || lastErr;
|
|
80
|
-
log(` ${
|
|
111
|
+
log(` ${retryNote(attempts, i, version)} ...`);
|
|
81
112
|
}
|
|
82
113
|
throw new Error(`AgentGlow server failed to start. Log: ${logPath}\n${lastErr}`);
|
|
83
114
|
}
|
|
@@ -166,12 +197,12 @@ export function removeCliCopies() {
|
|
|
166
197
|
/** Run the server in the foreground (stdio inherited). Resolves with its exit code. */
|
|
167
198
|
export async function serveForeground({ port, version, log = console.error }) {
|
|
168
199
|
const runner = await findRunner({ log });
|
|
169
|
-
const
|
|
200
|
+
const attempts = pyAttempts(version);
|
|
170
201
|
let stopping = false;
|
|
171
|
-
for (const [i, spec] of
|
|
202
|
+
for (const [i, { spec, refresh }] of attempts.entries()) {
|
|
172
203
|
const t0 = Date.now();
|
|
173
204
|
const code = await new Promise((resolve) => {
|
|
174
|
-
const child = spawn(runner.cmd, serveArgv(runner, spec, port), { stdio: "inherit" });
|
|
205
|
+
const child = spawn(runner.cmd, serveArgv(runner, spec, port, { refresh }), { stdio: "inherit" });
|
|
175
206
|
// A requested stop (Ctrl-C, `launchctl stop`, `systemctl stop`) must exit 0, or a supervisor with
|
|
176
207
|
// "restart unless it exited successfully" (launchd KeepAlive/SuccessfulExit) would bring it right back.
|
|
177
208
|
const fwd = (sig) => () => { stopping = true; child.kill(sig); };
|
|
@@ -184,10 +215,10 @@ export async function serveForeground({ port, version, log = console.error }) {
|
|
|
184
215
|
resolve(stopping ? 0 : (c ?? (sig ? 130 : 1)));
|
|
185
216
|
});
|
|
186
217
|
});
|
|
187
|
-
// a pinned version missing
|
|
218
|
+
// a pinned version missing from uv's index fails fast: refresh the index once, then fall back to latest
|
|
188
219
|
if (stopping) return 0;
|
|
189
|
-
if (code !== 0 && i <
|
|
190
|
-
log(`${
|
|
220
|
+
if (code !== 0 && i < attempts.length - 1 && Date.now() - t0 < 30000) {
|
|
221
|
+
log(`${retryNote(attempts, i, version)} ...`);
|
|
191
222
|
continue;
|
|
192
223
|
}
|
|
193
224
|
return code;
|
package/cli/lib/settings.mjs
CHANGED
|
@@ -74,13 +74,13 @@ const shq = (s) => `"${String(s).replace(/(["\\$`])/g, "\\$1")}"`;
|
|
|
74
74
|
* Shell command for the SessionStart hook that makes sure the local server runs. Prints nothing (SessionStart stdout
|
|
75
75
|
* would land in Claude's context) and always exits 0. Calls the CLI copy in the cache directly (~30 ms) and falls
|
|
76
76
|
* back to the pinned npm version through npx (~0.5 s when cached). Windows without Git Bash runs it in PowerShell,
|
|
77
|
-
* so there it is npx only.
|
|
77
|
+
* so there it is npx only. `capturePrompts` (setup --capture-prompts) starts the server with AGENTGLOW_CAPTURE_PROMPTS=1.
|
|
78
78
|
*/
|
|
79
|
-
export function startHookCommand({ port, version, node, script, cacheDir: dir, platform = process.platform }) {
|
|
79
|
+
export function startHookCommand({ port, version, node, script, cacheDir: dir, capturePrompts = false, platform = process.platform }) {
|
|
80
80
|
const args = `${START_MARK} --port ${port}`;
|
|
81
81
|
const npx = `npx -y agentglow${version ? `@${version}` : ""} ${args}`;
|
|
82
82
|
if (platform === "win32" || !node || !script) return npx;
|
|
83
|
-
const env = dir ? `AGENTGLOW_CACHE_DIR=${shq(dir)} ` : "";
|
|
83
|
+
const env = (capturePrompts ? "AGENTGLOW_CAPTURE_PROMPTS=1 " : "") + (dir ? `AGENTGLOW_CACHE_DIR=${shq(dir)} ` : "");
|
|
84
84
|
return `${env}${shq(node)} ${shq(script)} ${args} || ${env}${npx}`;
|
|
85
85
|
}
|
|
86
86
|
|