agent-dag 3.22.0 → 3.22.3
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 +6 -477
- package/package.json +14 -48
- package/shim.js +107 -0
- package/LICENSE +0 -661
- package/LICENSING.md +0 -82
- package/THIRD_PARTY_NOTICES.md +0 -395
- package/bin/agent-dag.js +0 -626
- package/bin/deck.js +0 -1805
- package/dist/web/assets/index-3FWd7g_W.css +0 -1
- package/dist/web/assets/index-BOwtoP02.js +0 -266
- package/dist/web/index.html +0 -49
- package/hook/hook.js +0 -542
- package/release-notes.json +0 -392
- package/src/server/activity.mjs +0 -52
- package/src/server/agent-activity.mjs +0 -522
- package/src/server/args.mjs +0 -183
- package/src/server/auto-update.mjs +0 -79
- package/src/server/block-notify.mjs +0 -173
- package/src/server/boot-deadline.mjs +0 -127
- package/src/server/brand.mjs +0 -16
- package/src/server/browser-history.mjs +0 -497
- package/src/server/browser-presence.mjs +0 -211
- package/src/server/browser-profiles.mjs +0 -279
- package/src/server/browser-react.mjs +0 -284
- package/src/server/browser-watch-store.mjs +0 -350
- package/src/server/browser-watch.mjs +0 -905
- package/src/server/ccusage.mjs +0 -1168
- package/src/server/claude-accounts.mjs +0 -951
- package/src/server/claude-dir.mjs +0 -213
- package/src/server/codex-auth.mjs +0 -388
- package/src/server/codex-dir.mjs +0 -171
- package/src/server/codex-quota.mjs +0 -449
- package/src/server/codex-usage.mjs +0 -512
- package/src/server/cswap-admin.mjs +0 -1562
- package/src/server/cswap-auto.mjs +0 -658
- package/src/server/cswap-install.mjs +0 -641
- package/src/server/deck-home.mjs +0 -243
- package/src/server/deck-prefs.mjs +0 -301
- package/src/server/deck-probe.mjs +0 -111
- package/src/server/detach.mjs +0 -244
- package/src/server/exec.mjs +0 -996
- package/src/server/global-install.mjs +0 -67
- package/src/server/hwmonitor.mjs +0 -56
- package/src/server/index.mjs +0 -6043
- package/src/server/installer.mjs +0 -912
- package/src/server/invoked-as.mjs +0 -144
- package/src/server/lan-about.mjs +0 -119
- package/src/server/lan-engine.mjs +0 -952
- package/src/server/lan-reach.mjs +0 -256
- package/src/server/lan-socket.mjs +0 -682
- package/src/server/lan-sync.mjs +0 -941
- package/src/server/lhm-parse.mjs +0 -91
- package/src/server/log-tail.mjs +0 -139
- package/src/server/log-writer.mjs +0 -322
- package/src/server/login-service.mjs +0 -473
- package/src/server/macmon.mjs +0 -310
- package/src/server/npx.mjs +0 -264
- package/src/server/open-url.mjs +0 -242
- package/src/server/presence.mjs +0 -40
- package/src/server/quota.mjs +0 -792
- package/src/server/relay-guard.mjs +0 -507
- package/src/server/reset-label.mjs +0 -78
- package/src/server/retire-sound-hook.mjs +0 -349
- package/src/server/running-deck.mjs +0 -234
- package/src/server/self-update.mjs +0 -1380
- package/src/server/stop-deck.mjs +0 -171
- package/src/server/supervisor.mjs +0 -392
- package/src/server/system-metrics.mjs +0 -1825
- package/src/server/term.mjs +0 -686
- package/src/server/uv-bootstrap.mjs +0 -337
package/src/server/open-url.mjs
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
// Opening the browser, without the ten packages it used to cost.
|
|
2
|
-
//
|
|
3
|
-
// `open@10` was this package's ONLY runtime dependency, and it brought nine
|
|
4
|
-
// more with it — bundle-name, default-browser, default-browser-id,
|
|
5
|
-
// define-lazy-prop, is-docker, is-inside-container, is-wsl, run-applescript,
|
|
6
|
-
// wsl-utils. Ten tarballs to fetch, extract and link on a machine whose npm
|
|
7
|
-
// cache is empty, for one call, made once, whose whole job is to hand a
|
|
8
|
-
// localhost URL to whatever the desktop already uses. On a fast link that is a
|
|
9
|
-
// second; on the links the people who reported a hung `npx ccdeck` are on, it
|
|
10
|
-
// is ten more round trips before the deck's own tarball is even unpacked.
|
|
11
|
-
//
|
|
12
|
-
// What it did that is worth keeping is the platform knowledge, and that is
|
|
13
|
-
// small enough to hold here: three commands, plus the WSL case where
|
|
14
|
-
// `process.platform` says linux and the browser is on the Windows side.
|
|
15
|
-
//
|
|
16
|
-
// DELIBERATELY FIRE AND FORGET. `open` returned a promise the boot awaited;
|
|
17
|
-
// nothing downstream of that await needed the child, and a launcher that takes
|
|
18
|
-
// its time — xdg-open on a machine with no desktop session hunting through
|
|
19
|
-
// every handler it knows — held the boot behind it. Here the candidates are
|
|
20
|
-
// tried in order, the failures move to the next one on their own, and the boot
|
|
21
|
-
// never waits for any of it.
|
|
22
|
-
import { spawn } from "node:child_process";
|
|
23
|
-
import { readFileSync } from "node:fs";
|
|
24
|
-
|
|
25
|
-
/** How long a launcher gets to prove it worked before the next one is tried.
|
|
26
|
-
* A launcher that is still running is a launcher that found something: macOS
|
|
27
|
-
* `open` and cmd's `start` both exit at once, and xdg-open stays up as the
|
|
28
|
-
* browser's parent. So only an EARLY non-zero exit moves on. */
|
|
29
|
-
export const LAUNCH_GRACE_MS = 1_500;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Is this a Linux that is really Windows?
|
|
33
|
-
*
|
|
34
|
-
* WSL reports `process.platform === "linux"`, has no desktop session of its
|
|
35
|
-
* own on a default install, and its browser lives on the Windows side — so
|
|
36
|
-
* xdg-open there either is not installed or opens nothing anybody can see.
|
|
37
|
-
* Both signals are read because either can be absent: the env var is set by
|
|
38
|
-
* the WSL launcher and lost by anything that scrubs the environment, and
|
|
39
|
-
* /proc/version is the kernel's own answer and cannot be.
|
|
40
|
-
*/
|
|
41
|
-
export function isWsl(platform = process.platform, env = process.env, readProcVersion = defaultProcVersion) {
|
|
42
|
-
if (platform !== "linux") return false;
|
|
43
|
-
if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) return true;
|
|
44
|
-
return /microsoft/i.test(readProcVersion());
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function defaultProcVersion() {
|
|
48
|
-
try {
|
|
49
|
-
return readFileSync("/proc/version", "utf8");
|
|
50
|
-
} catch {
|
|
51
|
-
return "";
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* The launchers to try for `url`, best first.
|
|
57
|
-
*
|
|
58
|
-
* A list rather than one answer, because every platform below has a case where
|
|
59
|
-
* the first choice is missing: a Linux without xdg-utils, a WSL without
|
|
60
|
-
* wslview, a Windows whose comspec has been moved. Each entry is exactly what
|
|
61
|
-
* `spawn` is given, so the test can read the command line rather than infer it.
|
|
62
|
-
*
|
|
63
|
-
* Exported with platform and env as parameters for the reason exec.mjs exports
|
|
64
|
-
* its own that way: the Windows and Linux answers have to be checkable from the
|
|
65
|
-
* Mac this is written on.
|
|
66
|
-
*/
|
|
67
|
-
export function launchers(url, { platform = process.platform, env = process.env, wsl } = {}) {
|
|
68
|
-
const inWsl = wsl === undefined ? isWsl(platform, env) : wsl;
|
|
69
|
-
|
|
70
|
-
if (platform === "darwin") {
|
|
71
|
-
// The absolute path first: `open` is also a shell builtin in a few setups
|
|
72
|
-
// and a user's own script called `open` on the PATH is not unheard of, and
|
|
73
|
-
// /usr/bin/open has been on every macOS this package supports.
|
|
74
|
-
return [
|
|
75
|
-
{ file: "/usr/bin/open", args: [url] },
|
|
76
|
-
{ file: "open", args: [url] },
|
|
77
|
-
];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (platform === "win32") {
|
|
81
|
-
// PowerShell FIRST, and this order is the one thing here that was decided by
|
|
82
|
-
// evidence rather than by taste.
|
|
83
|
-
//
|
|
84
|
-
// `open@10` — the dependency this file replaces — launches a Windows URL
|
|
85
|
-
// through `powershell -EncodedCommand` running `Start "<url>"`. That is what
|
|
86
|
-
// every ccdeck install on Windows has been using, so it is the path with a
|
|
87
|
-
// year of field evidence behind it and `cmd /c start` has none.
|
|
88
|
-
//
|
|
89
|
-
// And the fallback below cannot rescue a wrong first choice. `start` exits 0
|
|
90
|
-
// whether or not anything opened — measured on a real Windows 10 box, exit
|
|
91
|
-
// code 0 with no browser process created — so a candidate list that led with
|
|
92
|
-
// it would never reach a second candidate, however badly the first had done.
|
|
93
|
-
// Leading with the one whose failure is visible is the only ordering where
|
|
94
|
-
// having a fallback means anything.
|
|
95
|
-
//
|
|
96
|
-
// The cost is PowerShell's startup, a few hundred milliseconds, and it is
|
|
97
|
-
// paid by nobody: openUrl does not wait for the launcher and the boot does
|
|
98
|
-
// not wait for openUrl.
|
|
99
|
-
const tries = [{
|
|
100
|
-
file: "powershell.exe",
|
|
101
|
-
// The URL as its own argv entry rather than inside a script string, where
|
|
102
|
-
// `;` and `&` are PowerShell's own operators.
|
|
103
|
-
args: ["-NoProfile", "-NonInteractive", "-Command", "Start-Process", url],
|
|
104
|
-
}];
|
|
105
|
-
const viaCmd = startCommand(url, env);
|
|
106
|
-
tries.push(viaCmd);
|
|
107
|
-
// comspec is read by startCommand, and a comspec pointing somewhere that is
|
|
108
|
-
// no longer there is the one way that line fails on a machine which is
|
|
109
|
-
// otherwise fine. The bare name is what the PATH would have answered.
|
|
110
|
-
if (viaCmd.file.toLowerCase() !== "cmd.exe") tries.push(startCommand(url, env, "cmd.exe"));
|
|
111
|
-
return tries;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (inWsl) {
|
|
115
|
-
return [
|
|
116
|
-
// Ships with wslu and is what a WSL user's own `xdg-open` is usually
|
|
117
|
-
// symlinked to anyway. It knows how to hand a URL across the boundary.
|
|
118
|
-
{ file: "wslview", args: [url] },
|
|
119
|
-
// No wslu: go to Windows directly. cmd.exe is reachable from WSL through
|
|
120
|
-
// the interop path and needs no PATH entry of its own.
|
|
121
|
-
startCommand(url, env, "/mnt/c/Windows/System32/cmd.exe"),
|
|
122
|
-
startCommand(url, env, "cmd.exe"),
|
|
123
|
-
// Last, and only useful on a WSL that really does run a desktop.
|
|
124
|
-
{ file: "xdg-open", args: [url] },
|
|
125
|
-
];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Plain Linux, and every other Unix. xdg-open is the standard answer; the
|
|
129
|
-
// rest are the ones that exist on machines that never installed xdg-utils,
|
|
130
|
-
// in the order of how likely they are to be configured rather than merely
|
|
131
|
-
// present.
|
|
132
|
-
return [
|
|
133
|
-
{ file: "xdg-open", args: [url] },
|
|
134
|
-
{ file: "gio", args: ["open", url] },
|
|
135
|
-
{ file: "x-www-browser", args: [url] },
|
|
136
|
-
{ file: "sensible-browser", args: [url] },
|
|
137
|
-
{ file: "wslview", args: [url] },
|
|
138
|
-
];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* `start` the way cmd.exe needs to be given it.
|
|
143
|
-
*
|
|
144
|
-
* Three details, each of which is a bug if it is missing. The empty `""` is the
|
|
145
|
-
* window TITLE — `start "http://…"` opens a console window titled with the URL
|
|
146
|
-
* and nothing else happens. The URL is quoted so an `&` in a query string ends
|
|
147
|
-
* the argument instead of the command. And `windowsVerbatimArguments` stops
|
|
148
|
-
* Node quoting the line a second time, which is exactly what exec.mjs's
|
|
149
|
-
* `viaCmd` does for the same reason.
|
|
150
|
-
*/
|
|
151
|
-
export function startCommand(url, env = process.env, comspec) {
|
|
152
|
-
const shell = comspec || env.comspec || env.ComSpec || "cmd.exe";
|
|
153
|
-
return {
|
|
154
|
-
file: shell,
|
|
155
|
-
args: ["/d", "/s", "/c", `start "" "${url}"`],
|
|
156
|
-
opts: { windowsVerbatimArguments: true },
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* The address to actually hand the desktop, or null when it is not one — and
|
|
162
|
-
* the ONLY guard, since #798 removed the `isOpenable` boolean that sat in front
|
|
163
|
-
* of it with no caller but the suite.
|
|
164
|
-
*
|
|
165
|
-
* Refuse anything that is not an http(s) URL: today's one caller passes a
|
|
166
|
-
* loopback address this deck built itself, so this is not guarding against a
|
|
167
|
-
* hostile input — it is guarding against the day a second caller passes a path,
|
|
168
|
-
* because every launcher above would happily open it and `start` would run it.
|
|
169
|
-
*
|
|
170
|
-
* The guard used to parse into a local `u`, check its protocol and throw the
|
|
171
|
-
* parse away — so what reached `launchers` was the RAW string. `new URL()`
|
|
172
|
-
* accepts characters it normalizes only in `href`, a `"` among them, and on
|
|
173
|
-
* Windows `startCommand` builds `start "" "<url>"` with
|
|
174
|
-
* `windowsVerbatimArguments: true`, where a quote is syntax. Today the only
|
|
175
|
-
* caller passes a loopback URL this deck built itself, so nothing is reachable;
|
|
176
|
-
* the function's own doc says it exists "for the day a second caller passes a
|
|
177
|
-
* path", and as written it would not have stopped that caller.
|
|
178
|
-
*
|
|
179
|
-
* Returning the normalized `href` means the string that was checked is the
|
|
180
|
-
* string that is launched.
|
|
181
|
-
*/
|
|
182
|
-
export function normalizeOpenable(url) {
|
|
183
|
-
try {
|
|
184
|
-
const u = new URL(String(url));
|
|
185
|
-
if (u.protocol !== "http:" && u.protocol !== "https:") return null;
|
|
186
|
-
return u.href;
|
|
187
|
-
} catch {
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Hand `url` to the desktop. Returns nothing, waits for nothing, throws never.
|
|
194
|
-
*
|
|
195
|
-
* The boot calls this between binding the port and starting the pulse line, and
|
|
196
|
-
* neither of those has anything to learn from how it went — a browser that does
|
|
197
|
-
* not open leaves a URL on screen that the user can click or paste, which is
|
|
198
|
-
* the same recovery an error row would have offered.
|
|
199
|
-
*/
|
|
200
|
-
export function openUrl(url, { platform = process.platform, env = process.env, spawnFn = spawn } = {}) {
|
|
201
|
-
const href = normalizeOpenable(url);
|
|
202
|
-
if (href === null) return;
|
|
203
|
-
const tries = launchers(href, { platform, env });
|
|
204
|
-
|
|
205
|
-
const attempt = (i) => {
|
|
206
|
-
if (i >= tries.length) return;
|
|
207
|
-
const { file, args, opts } = tries[i];
|
|
208
|
-
let child;
|
|
209
|
-
try {
|
|
210
|
-
child = spawnFn(file, args, {
|
|
211
|
-
stdio: "ignore",
|
|
212
|
-
// The launcher outlives us on purpose: xdg-open is the browser's parent
|
|
213
|
-
// process on a cold start, and a deck stopped with Ctrl+C two seconds
|
|
214
|
-
// later should not take the window with it. Not on Windows, where a
|
|
215
|
-
// detached child is a child with its own console — the thing
|
|
216
|
-
// windowsHide exists to prevent.
|
|
217
|
-
detached: platform !== "win32",
|
|
218
|
-
windowsHide: true,
|
|
219
|
-
...opts,
|
|
220
|
-
});
|
|
221
|
-
} catch {
|
|
222
|
-
attempt(i + 1);
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
// ENOENT and EACCES both land here rather than throwing, because the failure
|
|
226
|
-
// happens after spawn returns.
|
|
227
|
-
child.on("error", () => attempt(i + 1));
|
|
228
|
-
// An early non-zero exit is xdg-open saying it found no handler (exit 3) or
|
|
229
|
-
// cmd saying it could not find `start`. A launcher still alive after the
|
|
230
|
-
// grace period is one that worked, so the timer is what closes the question.
|
|
231
|
-
let open = true;
|
|
232
|
-
const settled = setTimeout(() => { open = false; }, LAUNCH_GRACE_MS);
|
|
233
|
-
settled.unref?.();
|
|
234
|
-
child.on("exit", (code) => {
|
|
235
|
-
clearTimeout(settled);
|
|
236
|
-
if (open && code !== 0) attempt(i + 1);
|
|
237
|
-
});
|
|
238
|
-
child.unref?.();
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
attempt(0);
|
|
242
|
-
}
|
package/src/server/presence.mjs
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// Whether somebody is looking at the deck right now, as its tabs say.
|
|
2
|
-
//
|
|
3
|
-
// The server can tell whether a page is CONNECTED — `sseClients.size` — but not
|
|
4
|
-
// whether anybody is looking at one: a tab behind other windows keeps its
|
|
5
|
-
// stream open all day. The away-update (auto-update.mjs) needs the second fact,
|
|
6
|
-
// so each tab reports it (src/web/presence.ts), and a claim expires here unless
|
|
7
|
-
// it is renewed, because a tab that crashed or lost its network never gets to
|
|
8
|
-
// take it back.
|
|
9
|
-
export const PRESENCE_TTL_MS = 45_000;
|
|
10
|
-
/** More tabs than anybody opens on one deck. A bound because the ids arrive
|
|
11
|
-
* from pages; past it the oldest claim goes. */
|
|
12
|
-
const MAX_TABS = 32;
|
|
13
|
-
const TAB_ID = /^[\w-]{1,64}$/;
|
|
14
|
-
|
|
15
|
-
export function createPresence({ ttlMs = PRESENCE_TTL_MS } = {}) {
|
|
16
|
-
/** tab id → when it last said it was looking */
|
|
17
|
-
const seen = new Map();
|
|
18
|
-
return {
|
|
19
|
-
/** A tab saying whether it is being looked at. False for an id no page
|
|
20
|
-
* could have made, which the route answers with a 400. */
|
|
21
|
-
report(tab, looking, now) {
|
|
22
|
-
if (typeof tab !== "string" || !TAB_ID.test(tab)) return false;
|
|
23
|
-
seen.delete(tab);
|
|
24
|
-
if (looking) {
|
|
25
|
-
seen.set(tab, now);
|
|
26
|
-
while (seen.size > MAX_TABS) seen.delete(seen.keys().next().value);
|
|
27
|
-
}
|
|
28
|
-
return true;
|
|
29
|
-
},
|
|
30
|
-
/** Whether any tab's claim is still live. A claim stamped by a clock that
|
|
31
|
-
* has since moved either way is as expired as an old one. */
|
|
32
|
-
looking(now) {
|
|
33
|
-
for (const [tab, at] of seen) {
|
|
34
|
-
if (Math.abs(now - at) > ttlMs) seen.delete(tab);
|
|
35
|
-
else return true;
|
|
36
|
-
}
|
|
37
|
-
return false;
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|