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
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// Which of the three published commands the user typed — and nothing else.
|
|
2
|
-
//
|
|
3
|
-
// The deck is on npm three times over — `ccdeck`, `agents-deck` and `agent-dag`
|
|
4
|
-
// — and since #340 all three are literally the same tarball, published with
|
|
5
|
-
// `name` set to each in turn. Every surface a human reads says ccdeck now, and
|
|
6
|
-
// most of the downloads are still on the other two, so nothing closes that split
|
|
7
|
-
// on its own. This file is the evidence behind saying so — once in the terminal,
|
|
8
|
-
// once in the browser.
|
|
9
|
-
//
|
|
10
|
-
// Two rules make that safe, and they are the whole of the file.
|
|
11
|
-
//
|
|
12
|
-
// It is never a refusal. A build that declined to boot under an old name would
|
|
13
|
-
// take the self-update path down with it: a user on `agents-deck` clicks
|
|
14
|
-
// "Update & restart", the copy that arrives refuses to run, and the deck is
|
|
15
|
-
// dead on the one machine where the deck was the thing that would have
|
|
16
|
-
// explained why.
|
|
17
|
-
//
|
|
18
|
-
// And it is never asked of the PACKAGE. That was unarguable while ccdeck was a
|
|
19
|
-
// stub — it depended on `agents-deck` and spawned its bin, so "is this build
|
|
20
|
-
// agents-deck?" was true inside every ccdeck run, and a notice keyed on it would
|
|
21
|
-
// have scolded exactly the people who did what we asked. #340 removed the stub
|
|
22
|
-
// and the rule outlived it, because the three names are now ONE tarball: asking
|
|
23
|
-
// the package what it is gets you whichever name the publish step happened to
|
|
24
|
-
// set last, which is not evidence about the user at all. The question is which
|
|
25
|
-
// name the USER TYPED. That is a different question, with a different answer,
|
|
26
|
-
// and on one platform with no answer at all:
|
|
27
|
-
//
|
|
28
|
-
// npx, everywhere — npm writes the literal spec into
|
|
29
|
-
// `_npx/<hash>/package.json` as `_npx.packages`, which npxRestartSpec
|
|
30
|
-
// already reads to decide what a restart re-runs. Reused rather than
|
|
31
|
-
// re-derived; the only difference here is that this caller has no fallback
|
|
32
|
-
// to offer, since a guess is the one answer it must not give.
|
|
33
|
-
//
|
|
34
|
-
// a global install on macOS or Linux — npm links <prefix>/bin/<name> at the
|
|
35
|
-
// package's bin/agent-dag.js, and node does not resolve that symlink for
|
|
36
|
-
// argv[1]. All three names reach one file and only argv[1] tells them
|
|
37
|
-
// apart, so its last segment is the command that was typed. Verified with
|
|
38
|
-
// npm's own layout: a symlink named `ccdeck` gives argv[1] `…/bin/ccdeck`.
|
|
39
|
-
//
|
|
40
|
-
// a global install on Windows — nowhere. npm writes <name>.cmd, <name>.ps1
|
|
41
|
-
// and an extensionless sh shim, each of which runs
|
|
42
|
-
// `node "…\node_modules\<pkg>\bin\agent-dag.js" %*` (npm's cmd-shim).
|
|
43
|
-
// The typed name is the shim's FILENAME and never becomes an argument, and
|
|
44
|
-
// there is no other carrier — npm_config_user_agent and friends are not set
|
|
45
|
-
// for a direct bin invocation. So Windows answers null.
|
|
46
|
-
//
|
|
47
|
-
// a git checkout — nothing was typed. `node bin/agent-dag.js` is not any of
|
|
48
|
-
// the three names.
|
|
49
|
-
//
|
|
50
|
-
// Which leaves the callers one rule: positive identification, or silence. A
|
|
51
|
-
// notice shown to somebody who already types `ccdeck` is worse than a notice
|
|
52
|
-
// missed, because it is the failure that teaches everyone to ignore the next
|
|
53
|
-
// one.
|
|
54
|
-
import { bareSpecName, isNpxInstall, npxRestartSpec } from "./self-update.mjs";
|
|
55
|
-
|
|
56
|
-
/** The command every surface promotes. Deliberately not brand.mjs's PRODUCT:
|
|
57
|
-
* that is the name a human reads, this is an npm bin name the user types, and
|
|
58
|
-
* the two are the same string only because the rename made them so —
|
|
59
|
-
* invoked-as.test.ts pins them against each other rather than assuming it. */
|
|
60
|
-
export const PREFERRED = "ccdeck";
|
|
61
|
-
|
|
62
|
-
/** Every command an install of this package provides, which is every name that
|
|
63
|
-
* can be typed. package.json's `bin` block is the other half of this list. */
|
|
64
|
-
export const COMMANDS = [PREFERRED, "agents-deck", "agent-dag"];
|
|
65
|
-
|
|
66
|
-
/** `value` when it is one of those three, and null for everything else — a shim
|
|
67
|
-
* filename with an extension on it, a path, a spec that resolved to some other
|
|
68
|
-
* package, an environment variable somebody set by hand. None of those are
|
|
69
|
-
* evidence of anything, and this function is the only place that decides so. */
|
|
70
|
-
export function knownCommand(value) {
|
|
71
|
-
return typeof value === "string" && COMMANDS.includes(value) ? value : null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** The last segment of a path, split on both separators rather than `path.sep`
|
|
75
|
-
* — the same reason isNpxInstall gives: a Windows path can reach a POSIX
|
|
76
|
-
* process through a test or a config file, and a separator-agnostic split is
|
|
77
|
-
* what keeps this one function on all three platforms. */
|
|
78
|
-
function lastSegment(path) {
|
|
79
|
-
if (typeof path !== "string") return null;
|
|
80
|
-
const parts = path.split(/[\\/]/);
|
|
81
|
-
return parts[parts.length - 1];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* The name the user typed, when we can prove it. Null means unknown, and
|
|
86
|
-
* unknown must never produce a notice.
|
|
87
|
-
*/
|
|
88
|
-
export function invokedAs({ pkgRoot, argv1, platform = process.platform } = {}) {
|
|
89
|
-
// npx first, and alone: under npx argv[1] is a path inside the cache
|
|
90
|
-
// directory, so the metadata is not the better answer, it is the only one.
|
|
91
|
-
// The explicit null fallback is the point of the call — npxRestartSpec's
|
|
92
|
-
// default is the package name, which is right for "what do I re-run" and
|
|
93
|
-
// would be a guess wearing an answer's clothes here.
|
|
94
|
-
if (isNpxInstall(pkgRoot)) return knownCommand(bareSpecName(npxRestartSpec(pkgRoot, null)));
|
|
95
|
-
// See the header: on Windows the shim eats the name. Decided on the platform
|
|
96
|
-
// rather than left to the extension check below, so that a Windows path that
|
|
97
|
-
// somehow did end in a bare name still cannot become a guess.
|
|
98
|
-
if (platform === "win32") return null;
|
|
99
|
-
return knownCommand(lastSegment(argv1));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* The same question, answered for the process that is asking it.
|
|
104
|
-
*
|
|
105
|
-
* bin/deck.js — and the server running inside it — is spawned BY the
|
|
106
|
-
* supervisor, so its own argv[1] is `…/bin/deck.js` every single time: the
|
|
107
|
-
* typed name reached bin/agent-dag.js and stopped there. The supervisor answers
|
|
108
|
-
* once and passes the answer down in AGENTS_DECK_INVOKED_AS, which is read back
|
|
109
|
-
* through knownCommand rather than trusted, since an environment variable is
|
|
110
|
-
* user input like any other. The fall-through still answers the npx half for a
|
|
111
|
-
* worker started with no supervisor above it.
|
|
112
|
-
*/
|
|
113
|
-
export function invokedName({
|
|
114
|
-
env = process.env, pkgRoot, argv1 = process.argv[1], platform = process.platform,
|
|
115
|
-
} = {}) {
|
|
116
|
-
return knownCommand(env?.AGENTS_DECK_INVOKED_AS) ?? invokedAs({ pkgRoot, argv1, platform });
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* What the terminal says about it, or null when there is nothing to say.
|
|
121
|
-
*
|
|
122
|
-
* Two lines, because the second one is where the value is. Someone who ran
|
|
123
|
-
* `npm i -g agents-deck` has nothing to install and nothing to download — the
|
|
124
|
-
* same install already put a `ccdeck` on their PATH, and all they have to do is
|
|
125
|
-
* type it. Handing them an install command instead would be work we invented.
|
|
126
|
-
* Under npx there is no such install, so the fix there is the command itself.
|
|
127
|
-
*
|
|
128
|
-
* `dash` comes from the caller's glyph tier: an em dash is as absent from a
|
|
129
|
-
* legacy Windows console as a check mark is, and this file must not be the one
|
|
130
|
-
* place that forgets it.
|
|
131
|
-
*/
|
|
132
|
-
export function renameNotice({ invoked, pkgRoot, dash = "—", preferred = PREFERRED } = {}) {
|
|
133
|
-
const name = knownCommand(invoked);
|
|
134
|
-
if (!name || name === preferred) return null;
|
|
135
|
-
return {
|
|
136
|
-
// Leads with the reassurance on purpose. Nothing is breaking here, no
|
|
137
|
-
// command is being taken away, and a line that opened with the rename would
|
|
138
|
-
// read as one that was.
|
|
139
|
-
said: `${name} still works ${dash} the deck is called ${preferred} now`,
|
|
140
|
-
fix: isNpxInstall(pkgRoot)
|
|
141
|
-
? `next time: npx ${preferred}`
|
|
142
|
-
: `${preferred} already works here ${dash} same install, no download`,
|
|
143
|
-
};
|
|
144
|
-
}
|
package/src/server/lan-about.mjs
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// What a paired deck says about itself: the version it runs and the machine it
|
|
2
|
-
// runs on.
|
|
3
|
-
//
|
|
4
|
-
// ONLY TO A DECK THAT IS PAIRED, AND SEALED. The beacon is shouted at the whole
|
|
5
|
-
// network and the hello is sent before anybody has proved anything, so neither
|
|
6
|
-
// carries this: which build of which operating system a machine runs is the
|
|
7
|
-
// first thing somebody hunting for a known-broken version would want, and
|
|
8
|
-
// nobody on the network is owed it. It rides the manifest exchange, which only
|
|
9
|
-
// happens between two decks that have each accepted the other — and it is
|
|
10
|
-
// sealed with that connection's key, because the frames around it are JSON in
|
|
11
|
-
// the clear on the wire.
|
|
12
|
-
//
|
|
13
|
-
// BOTH WAYS. The deck that dials sends its own card with the question, and the
|
|
14
|
-
// deck that answers sends its own with the answer. A deck that only calls in —
|
|
15
|
-
// paired, with no address this one can reach — is never asked anything, so the
|
|
16
|
-
// card it sends when it calls is the only way this deck ever learns what it is.
|
|
17
|
-
//
|
|
18
|
-
// OLDER DECKS SAY NOTHING, AND NOTHING BREAKS. The field is extra on a frame
|
|
19
|
-
// both versions already send: a deck from before this reads the accounts and
|
|
20
|
-
// never looks at the rest, and a card that is absent is drawn as absent.
|
|
21
|
-
import { readFileSync } from "node:fs";
|
|
22
|
-
import { release as osRelease } from "node:os";
|
|
23
|
-
import { open, seal } from "./lan-sync.mjs";
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The operating system, in the words its owner would use.
|
|
27
|
-
*
|
|
28
|
-
* Node reports the KERNEL — `darwin 25.5.0`, `win32 10.0.26100` — and nobody
|
|
29
|
-
* calls their laptop Darwin 25. So the numbers are translated on the machine
|
|
30
|
-
* they describe, where the translation is certain, rather than on the one
|
|
31
|
-
* reading them. Every input is a parameter, so all three platforms are tested
|
|
32
|
-
* on any one of them.
|
|
33
|
-
*/
|
|
34
|
-
export function osLabel({ platform, release = "", prettyName = "" } = {}) {
|
|
35
|
-
const [major, minor, build] = String(release).split(".").map(n => Number.parseInt(n, 10));
|
|
36
|
-
const dot = Number.isInteger(minor) ? `.${minor}` : "";
|
|
37
|
-
if (platform === "darwin") {
|
|
38
|
-
if (!Number.isInteger(major)) return "macOS";
|
|
39
|
-
// Darwin 20 was macOS 11 and every major after it was one more — until 25,
|
|
40
|
-
// which Apple shipped as macOS 26 to match the year.
|
|
41
|
-
if (major >= 25) return `macOS ${major + 1}${dot}`;
|
|
42
|
-
if (major >= 20) return `macOS ${major - 9}${dot}`;
|
|
43
|
-
// The 10.x years, where the kernel's major minus four is the second number.
|
|
44
|
-
return major >= 5 ? `macOS 10.${major - 4}` : "macOS";
|
|
45
|
-
}
|
|
46
|
-
if (platform === "win32") {
|
|
47
|
-
// Windows 11 kept the version 10.0 and moved only the build, and 22000 is
|
|
48
|
-
// the first one. From here it is the only way to tell the two apart.
|
|
49
|
-
if (major === 10 && Number.isInteger(build)) return build >= 22_000 ? "Windows 11" : "Windows 10";
|
|
50
|
-
return "Windows";
|
|
51
|
-
}
|
|
52
|
-
if (platform === "linux") return prettyName || "Linux";
|
|
53
|
-
return typeof platform === "string" && platform ? platform : "unknown";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** The distribution's own name for itself, from the file every systemd-era
|
|
57
|
-
* distribution ships, with the fallback path the same spec names. Only ever
|
|
58
|
-
* read on Linux: the paths are Linux's and mean nothing anywhere else. */
|
|
59
|
-
export function linuxPrettyName(read = readFileSync) {
|
|
60
|
-
for (const file of ["/etc/os-release", "/usr/lib/os-release"]) {
|
|
61
|
-
try {
|
|
62
|
-
const line = /^PRETTY_NAME=(.*)$/m.exec(String(read(file, "utf8")))?.[1];
|
|
63
|
-
const name = line?.trim().replace(/^(["'])(.*)\1$/, "$2").trim();
|
|
64
|
-
if (name) return name;
|
|
65
|
-
} catch { /* not this one */ }
|
|
66
|
-
}
|
|
67
|
-
return "";
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** This deck's own card. Built once, at start: nothing in it changes while the
|
|
71
|
-
* process lives, and an upgrade is a restart. */
|
|
72
|
-
export function aboutThisDeck({
|
|
73
|
-
version = null, platform = process.platform, release = osRelease(), arch = process.arch, read = readFileSync,
|
|
74
|
-
} = {}) {
|
|
75
|
-
return readAbout({
|
|
76
|
-
version,
|
|
77
|
-
os: osLabel({ platform, release, prettyName: platform === "linux" ? linuxPrettyName(read) : "" }),
|
|
78
|
-
arch,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** One field of a card, or null. It came off the network, so it is a string of
|
|
83
|
-
* bounded length with no control characters — the rule cleanName applies to a
|
|
84
|
-
* deck's name, for the same reason: it reaches a terminal as well as a page.
|
|
85
|
-
* `\p{Cc}` is the Unicode class for exactly those, C0 and C1 both. */
|
|
86
|
-
function field(v, max, shape = null) {
|
|
87
|
-
if (typeof v !== "string") return null;
|
|
88
|
-
const flat = v.replace(/\p{Cc}/gu, " ").replace(/\s+/g, " ").trim();
|
|
89
|
-
const s = [...flat].slice(0, max).join("").trim();
|
|
90
|
-
return s && (!shape || shape.test(s)) ? s : null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** A card, or null when there is nothing in it worth drawing. */
|
|
94
|
-
export function readAbout(raw) {
|
|
95
|
-
if (!raw || typeof raw !== "object") return null;
|
|
96
|
-
const version = field(raw.version, 32, /^[0-9A-Za-z][0-9A-Za-z.+-]*$/);
|
|
97
|
-
const os = field(raw.os, 48);
|
|
98
|
-
const arch = field(raw.arch, 16, /^[0-9A-Za-z_]+$/);
|
|
99
|
-
return version || os || arch ? { version, os, arch } : null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Bound to one connection AND one direction, so a card cannot be replayed
|
|
103
|
-
* back at the deck that sent it as if it were the other deck's. */
|
|
104
|
-
const aboutAad = (fromFp, toFp) => `${fromFp}->${toFp}|about`;
|
|
105
|
-
|
|
106
|
-
/** This deck's card, sealed for the deck on the other end of `key`. */
|
|
107
|
-
export function sealAbout(key, about, fromFp, toFp) {
|
|
108
|
-
if (!key || !about) return null;
|
|
109
|
-
return seal(key, JSON.stringify(about), aboutAad(fromFp, toFp));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** The other deck's card, or null — for a deck that sent none, and for one
|
|
113
|
-
* whose seal does not open, which mean the same thing to the panel. */
|
|
114
|
-
export function openAbout(key, sealed, fromFp, toFp) {
|
|
115
|
-
if (!key || !sealed || typeof sealed !== "object") return null;
|
|
116
|
-
const text = open(key, sealed, aboutAad(fromFp, toFp));
|
|
117
|
-
if (text == null) return null;
|
|
118
|
-
try { return readAbout(JSON.parse(text)); } catch { return null; }
|
|
119
|
-
}
|