@phnx-labs/agents-cli 1.20.34 → 1.20.36
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/CHANGELOG.md +113 -0
- package/LICENSE +185 -21
- package/README.md +13 -5
- package/dist/commands/audit.d.ts +14 -0
- package/dist/commands/audit.js +68 -0
- package/dist/commands/browser.js +82 -8
- package/dist/commands/check.d.ts +15 -0
- package/dist/commands/check.js +84 -0
- package/dist/commands/cloud.js +143 -3
- package/dist/commands/computer-actions.d.ts +15 -0
- package/dist/commands/computer-actions.js +120 -22
- package/dist/commands/computer.js +81 -0
- package/dist/commands/cost.js +17 -12
- package/dist/commands/daemon.js +4 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +54 -97
- package/dist/commands/events.d.ts +16 -0
- package/dist/commands/events.js +173 -0
- package/dist/commands/exec.d.ts +19 -0
- package/dist/commands/exec.js +283 -12
- package/dist/commands/hosts.js +37 -29
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +62 -16
- package/dist/commands/lock.d.ts +12 -0
- package/dist/commands/lock.js +70 -0
- package/dist/commands/logs.d.ts +17 -0
- package/dist/commands/logs.js +139 -0
- package/dist/commands/message.d.ts +15 -0
- package/dist/commands/message.js +56 -0
- package/dist/commands/models.d.ts +3 -0
- package/dist/commands/models.js +27 -8
- package/dist/commands/permissions.js +9 -2
- package/dist/commands/repo.d.ts +34 -0
- package/dist/commands/repo.js +243 -65
- package/dist/commands/resource-view.d.ts +20 -0
- package/dist/commands/resource-view.js +90 -28
- package/dist/commands/routines.js +101 -5
- package/dist/commands/secrets-migrate.js +106 -57
- package/dist/commands/secrets.d.ts +31 -18
- package/dist/commands/secrets.js +196 -91
- package/dist/commands/serve.d.ts +10 -0
- package/dist/commands/serve.js +37 -0
- package/dist/commands/sessions-inject.d.ts +14 -0
- package/dist/commands/sessions-inject.js +111 -0
- package/dist/commands/sessions-picker.d.ts +2 -0
- package/dist/commands/sessions-picker.js +24 -3
- package/dist/commands/sessions-resume.d.ts +2 -0
- package/dist/commands/sessions-resume.js +221 -0
- package/dist/commands/sessions-tail.d.ts +10 -0
- package/dist/commands/sessions-tail.js +11 -0
- package/dist/commands/sessions.d.ts +143 -1
- package/dist/commands/sessions.js +628 -80
- package/dist/commands/setup.js +4 -2
- package/dist/commands/sync.d.ts +3 -1
- package/dist/commands/sync.js +156 -4
- package/dist/commands/teams.js +217 -0
- package/dist/commands/versions.js +2 -4
- package/dist/commands/view.d.ts +3 -0
- package/dist/commands/view.js +19 -8
- package/dist/commands/wallet.d.ts +6 -0
- package/dist/commands/wallet.js +22 -5
- package/dist/commands/watchdog.d.ts +18 -0
- package/dist/commands/watchdog.js +238 -0
- package/dist/index.js +82 -32
- package/dist/lib/agents.js +30 -9
- package/dist/lib/audit/log.d.ts +92 -0
- package/dist/lib/audit/log.js +177 -0
- package/dist/lib/auto-pull.js +2 -1
- package/dist/lib/browser/cdp.js +5 -1
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +18 -7
- package/dist/lib/browser/drivers/ssh.d.ts +8 -0
- package/dist/lib/browser/drivers/ssh.js +77 -16
- package/dist/lib/browser/har.d.ts +84 -0
- package/dist/lib/browser/har.js +77 -0
- package/dist/lib/browser/ipc.d.ts +3 -0
- package/dist/lib/browser/ipc.js +37 -12
- package/dist/lib/browser/profiles.d.ts +1 -1
- package/dist/lib/browser/profiles.js +8 -10
- package/dist/lib/browser/refs.d.ts +65 -0
- package/dist/lib/browser/refs.js +73 -1
- package/dist/lib/browser/runtime-state.js +1 -0
- package/dist/lib/browser/service.d.ts +38 -2
- package/dist/lib/browser/service.js +183 -23
- package/dist/lib/browser/types.d.ts +14 -1
- package/dist/lib/budget/live-cloud.d.ts +42 -0
- package/dist/lib/budget/live-cloud.js +79 -0
- package/dist/lib/budget/live-team.d.ts +31 -0
- package/dist/lib/budget/live-team.js +115 -0
- package/dist/lib/cloud/codex.js +4 -0
- package/dist/lib/cloud/rush.d.ts +12 -1
- package/dist/lib/cloud/rush.js +13 -3
- package/dist/lib/cloud/types.d.ts +9 -0
- package/dist/lib/computer/dispatch.d.ts +8 -0
- package/dist/lib/computer/dispatch.js +125 -0
- package/dist/lib/computer/loop.d.ts +62 -0
- package/dist/lib/computer/loop.js +98 -0
- package/dist/lib/computer/model.d.ts +44 -0
- package/dist/lib/computer/model.js +157 -0
- package/dist/lib/concurrency.d.ts +19 -0
- package/dist/lib/concurrency.js +33 -0
- package/dist/lib/daemon.d.ts +92 -0
- package/dist/lib/daemon.js +222 -18
- package/dist/lib/devices/connect.d.ts +3 -2
- package/dist/lib/devices/connect.js +5 -3
- package/dist/lib/devices/registry.d.ts +7 -0
- package/dist/lib/devices/registry.js +24 -0
- package/dist/lib/devices/tailscale.js +1 -1
- package/dist/lib/drift.d.ts +52 -0
- package/dist/lib/drift.js +112 -0
- package/dist/lib/events.d.ts +10 -3
- package/dist/lib/events.js +101 -24
- package/dist/lib/exec.d.ts +17 -0
- package/dist/lib/exec.js +100 -12
- package/dist/lib/git.d.ts +27 -0
- package/dist/lib/git.js +56 -1
- package/dist/lib/hooks/cache.d.ts +6 -0
- package/dist/lib/hooks/cache.js +54 -12
- package/dist/lib/hooks.d.ts +27 -0
- package/dist/lib/hooks.js +135 -10
- package/dist/lib/hosts/dispatch.d.ts +15 -0
- package/dist/lib/hosts/dispatch.js +45 -13
- package/dist/lib/hosts/logs.d.ts +16 -0
- package/dist/lib/hosts/logs.js +74 -0
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/passthrough.js +3 -1
- package/dist/lib/hosts/progress.d.ts +66 -0
- package/dist/lib/hosts/progress.js +125 -17
- package/dist/lib/hosts/ready.d.ts +51 -7
- package/dist/lib/hosts/ready.js +98 -25
- package/dist/lib/hosts/reconcile.d.ts +53 -0
- package/dist/lib/hosts/reconcile.js +81 -0
- package/dist/lib/hosts/registry.d.ts +19 -2
- package/dist/lib/hosts/registry.js +58 -2
- package/dist/lib/hosts/remote-cmd.d.ts +62 -1
- package/dist/lib/hosts/remote-cmd.js +70 -1
- package/dist/lib/hosts/remote-os.d.ts +17 -0
- package/dist/lib/hosts/remote-os.js +30 -0
- package/dist/lib/hosts/session-index.d.ts +34 -0
- package/dist/lib/hosts/session-index.js +56 -0
- package/dist/lib/hosts/tasks.d.ts +22 -0
- package/dist/lib/hosts/tasks.js +29 -0
- package/dist/lib/lock.d.ts +93 -0
- package/dist/lib/lock.js +207 -0
- package/dist/lib/loop.js +16 -1
- package/dist/lib/machine-id.d.ts +21 -0
- package/dist/lib/machine-id.js +26 -0
- package/dist/lib/mailbox-target.d.ts +36 -0
- package/dist/lib/mailbox-target.js +45 -0
- package/dist/lib/mailbox.d.ts +47 -0
- package/dist/lib/mailbox.js +194 -0
- package/dist/lib/mcp.d.ts +5 -0
- package/dist/lib/mcp.js +24 -8
- package/dist/lib/migrate.d.ts +19 -0
- package/dist/lib/migrate.js +134 -26
- package/dist/lib/overdue.js +3 -0
- package/dist/lib/permissions.d.ts +4 -0
- package/dist/lib/permissions.js +35 -0
- package/dist/lib/picker.d.ts +26 -0
- package/dist/lib/picker.js +127 -0
- package/dist/lib/platform/exec.d.ts +46 -0
- package/dist/lib/platform/exec.js +74 -0
- package/dist/lib/platform/process.d.ts +31 -0
- package/dist/lib/platform/process.js +34 -1
- package/dist/lib/platform/winpath.js +2 -0
- package/dist/lib/plugin-marketplace.d.ts +30 -0
- package/dist/lib/plugin-marketplace.js +215 -2
- package/dist/lib/plugins.d.ts +5 -0
- package/dist/lib/plugins.js +61 -10
- package/dist/lib/profiles.d.ts +25 -0
- package/dist/lib/profiles.js +22 -6
- package/dist/lib/pty-client.js +2 -1
- package/dist/lib/rotate.d.ts +61 -0
- package/dist/lib/rotate.js +52 -0
- package/dist/lib/routines.d.ts +40 -2
- package/dist/lib/routines.js +66 -8
- package/dist/lib/runner.d.ts +11 -2
- package/dist/lib/runner.js +49 -7
- package/dist/lib/scheduler.js +6 -1
- package/dist/lib/secrets/bundles.d.ts +60 -4
- package/dist/lib/secrets/bundles.js +131 -12
- package/dist/lib/secrets/filestore.d.ts +3 -0
- package/dist/lib/secrets/filestore.js +42 -16
- package/dist/lib/secrets/index.d.ts +43 -2
- package/dist/lib/secrets/index.js +102 -3
- package/dist/lib/secrets/mcp.d.ts +93 -0
- package/dist/lib/secrets/mcp.js +205 -0
- package/dist/lib/secrets/remote.js +12 -5
- package/dist/lib/secrets/sync.js +83 -4
- package/dist/lib/secrets/windows.js +14 -3
- package/dist/lib/serve/data.d.ts +81 -0
- package/dist/lib/serve/data.js +91 -0
- package/dist/lib/serve/page.d.ts +7 -0
- package/dist/lib/serve/page.js +140 -0
- package/dist/lib/serve/server.d.ts +46 -0
- package/dist/lib/serve/server.js +115 -0
- package/dist/lib/session/active.d.ts +61 -0
- package/dist/lib/session/active.js +196 -17
- package/dist/lib/session/discover.d.ts +37 -0
- package/dist/lib/session/discover.js +204 -27
- package/dist/lib/session/inject.d.ts +18 -0
- package/dist/lib/session/inject.js +21 -0
- package/dist/lib/session/parse.d.ts +6 -0
- package/dist/lib/session/parse.js +286 -2
- package/dist/lib/session/pid-registry.d.ts +22 -0
- package/dist/lib/session/pid-registry.js +104 -0
- package/dist/lib/session/provenance.d.ts +14 -2
- package/dist/lib/session/provenance.js +39 -8
- package/dist/lib/session/remote-active.d.ts +26 -0
- package/dist/lib/session/remote-active.js +153 -0
- package/dist/lib/session/remote-list.d.ts +51 -0
- package/dist/lib/session/remote-list.js +213 -0
- package/dist/lib/session/remote.d.ts +7 -1
- package/dist/lib/session/remote.js +18 -8
- package/dist/lib/session/render.js +1 -1
- package/dist/lib/session/sync/config.d.ts +1 -7
- package/dist/lib/session/sync/config.js +4 -11
- package/dist/lib/session/types.d.ts +18 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +44 -6
- package/dist/lib/shims.js +143 -57
- package/dist/lib/ssh-exec.d.ts +26 -3
- package/dist/lib/ssh-exec.js +47 -3
- package/dist/lib/ssh-tunnel.d.ts +24 -5
- package/dist/lib/ssh-tunnel.js +62 -63
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +8 -0
- package/dist/lib/startup/command-registry.js +17 -1
- package/dist/lib/state.d.ts +13 -0
- package/dist/lib/state.js +103 -9
- package/dist/lib/sync-umbrella.d.ts +14 -7
- package/dist/lib/sync-umbrella.js +17 -9
- package/dist/lib/teams/forEach.d.ts +110 -0
- package/dist/lib/teams/forEach.js +186 -0
- package/dist/lib/teams/index.d.ts +1 -0
- package/dist/lib/teams/index.js +1 -0
- package/dist/lib/teams/pr-watch.d.ts +226 -0
- package/dist/lib/teams/pr-watch.js +371 -0
- package/dist/lib/teams/registry.js +25 -9
- package/dist/lib/teams/supervisor.d.ts +14 -1
- package/dist/lib/teams/supervisor.js +19 -0
- package/dist/lib/teams/worktree.d.ts +9 -0
- package/dist/lib/teams/worktree.js +32 -0
- package/dist/lib/terminal/backends/ghostty.d.ts +6 -0
- package/dist/lib/terminal/backends/ghostty.js +69 -0
- package/dist/lib/terminal/backends/index.d.ts +19 -0
- package/dist/lib/terminal/backends/index.js +31 -0
- package/dist/lib/terminal/backends/iterm.d.ts +6 -0
- package/dist/lib/terminal/backends/iterm.js +62 -0
- package/dist/lib/terminal/backends/tmux.d.ts +14 -0
- package/dist/lib/terminal/backends/tmux.js +23 -0
- package/dist/lib/terminal/backends/vscodium-agent.d.ts +31 -0
- package/dist/lib/terminal/backends/vscodium-agent.js +72 -0
- package/dist/lib/terminal/engine.d.ts +39 -0
- package/dist/lib/terminal/engine.js +54 -0
- package/dist/lib/terminal/index.d.ts +17 -0
- package/dist/lib/terminal/index.js +8 -0
- package/dist/lib/terminal/inject.d.ts +204 -0
- package/dist/lib/terminal/inject.js +247 -0
- package/dist/lib/terminal/policy.d.ts +11 -0
- package/dist/lib/terminal/policy.js +11 -0
- package/dist/lib/terminal/quote.d.ts +11 -0
- package/dist/lib/terminal/quote.js +13 -0
- package/dist/lib/terminal/resolve.d.ts +64 -0
- package/dist/lib/terminal/resolve.js +90 -0
- package/dist/lib/terminal/shell.d.ts +6 -0
- package/dist/lib/terminal/shell.js +23 -0
- package/dist/lib/terminal/transport.d.ts +15 -0
- package/dist/lib/terminal/transport.js +40 -0
- package/dist/lib/terminal/types.d.ts +59 -0
- package/dist/lib/terminal/types.js +13 -0
- package/dist/lib/triggers/webhook.d.ts +85 -0
- package/dist/lib/triggers/webhook.js +141 -0
- package/dist/lib/types.d.ts +7 -0
- package/dist/lib/versions.d.ts +30 -3
- package/dist/lib/versions.js +129 -18
- package/dist/lib/watchdog/index.d.ts +3 -0
- package/dist/lib/watchdog/index.js +5 -0
- package/dist/lib/watchdog/read.d.ts +35 -0
- package/dist/lib/watchdog/read.js +149 -0
- package/dist/lib/watchdog/runner.d.ts +127 -0
- package/dist/lib/watchdog/runner.js +322 -0
- package/dist/lib/watchdog/watchdog.d.ts +40 -0
- package/dist/lib/watchdog/watchdog.js +166 -0
- package/dist/lib/watchdog/watchdogTail.d.ts +5 -0
- package/dist/lib/watchdog/watchdogTail.js +154 -0
- package/dist/lib/whats-new.d.ts +9 -0
- package/dist/lib/whats-new.js +35 -0
- package/dist/lib/workflows.d.ts +166 -0
- package/dist/lib/workflows.js +193 -0
- package/package.json +5 -4
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents.lock` — a deterministic SHA-256 manifest of the resolved resource set
|
|
3
|
+
* at a project root, plus the `--frozen` verification that fails closed on any
|
|
4
|
+
* drift. This is the reproducible-CI slice of the governance work (#337); org
|
|
5
|
+
* version pins and bundle signing are deferred follow-ups.
|
|
6
|
+
*
|
|
7
|
+
* Design:
|
|
8
|
+
* - Content hashing REUSES `fingerprintDir` / `fingerprintFile`
|
|
9
|
+
* (src/lib/staleness/fingerprint.ts) — the same two-tier fingerprinter every
|
|
10
|
+
* staleness checker uses. No new recursive file walker is introduced here.
|
|
11
|
+
* - The resource SET is the layered, resolved source union (project > user >
|
|
12
|
+
* system > extras), the same precedence `resolveResource` / `syncResourcesToVersion`
|
|
13
|
+
* read from. It is enumerated agent-independently on purpose: the lock must be
|
|
14
|
+
* reproducible on a fresh CI checkout that has no installed agent VERSIONS, so
|
|
15
|
+
* it snapshots the SOURCES sync copies FROM, not any per-version home.
|
|
16
|
+
*
|
|
17
|
+
* Lock shape (JSON): `{ "version": 1, "resources": { "<relpath>": "<sha256>" } }`,
|
|
18
|
+
* keys sorted so the file is byte-stable across machines and runs.
|
|
19
|
+
*/
|
|
20
|
+
export declare const LOCK_FILENAME = "agents.lock";
|
|
21
|
+
export declare const LOCK_VERSION: 1;
|
|
22
|
+
/**
|
|
23
|
+
* Resource kinds captured by the lock — the file/dir-backed layered resources
|
|
24
|
+
* shared across every agent. Presence-only kinds handled specially by the sync
|
|
25
|
+
* differ (plugins/promptcuts) are intentionally out of this first slice.
|
|
26
|
+
*/
|
|
27
|
+
export declare const LOCK_KINDS: readonly ["commands", "skills", "hooks", "rules", "mcp", "permissions", "subagents"];
|
|
28
|
+
/** The on-disk lockfile: relpath -> sha256 hex digest, keys sorted. */
|
|
29
|
+
export interface AgentsLock {
|
|
30
|
+
version: typeof LOCK_VERSION;
|
|
31
|
+
resources: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* One resolved resource to fingerprint. `path` is an absolute file or directory;
|
|
35
|
+
* `key` is the stable relpath prefix under which its hashes are recorded
|
|
36
|
+
* (e.g. `skills/debug` for a directory, `commands/plan.md` for a single file).
|
|
37
|
+
*/
|
|
38
|
+
export interface LockSource {
|
|
39
|
+
path: string;
|
|
40
|
+
key: string;
|
|
41
|
+
}
|
|
42
|
+
/** Added/removed/changed keys between an expected lock and the live resources. */
|
|
43
|
+
export interface LockDiff {
|
|
44
|
+
/** Present in the live resources, absent from the lock. */
|
|
45
|
+
added: string[];
|
|
46
|
+
/** Present in the lock, absent from the live resources. */
|
|
47
|
+
removed: string[];
|
|
48
|
+
/** Present in both but the sha256 differs. */
|
|
49
|
+
changed: string[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Hash every resolved source into a flat `relpath -> sha256` map. Directories are
|
|
53
|
+
* expanded with `fingerprintDir` (every contained file, sorted); single files use
|
|
54
|
+
* `fingerprintFile`. Unreadable / missing sources are skipped — enumeration only
|
|
55
|
+
* ever hands us paths that existed at scan time.
|
|
56
|
+
*/
|
|
57
|
+
export declare function hashLockSources(sources: LockSource[]): Record<string, string>;
|
|
58
|
+
/** Build a fully-sorted lock object from a resolved source set. */
|
|
59
|
+
export declare function buildLock(sources: LockSource[]): AgentsLock;
|
|
60
|
+
/** Serialise a lock to the canonical, sorted, newline-terminated JSON form. */
|
|
61
|
+
export declare function serializeLock(lock: AgentsLock): string;
|
|
62
|
+
/** Absolute path to the lockfile for a project root. */
|
|
63
|
+
export declare function lockPath(projectRoot: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Read + validate an existing lock. Returns null ONLY when the file is absent —
|
|
66
|
+
* a present-but-malformed lock throws, so `--frozen` fails closed instead of
|
|
67
|
+
* silently treating corruption as "no lock".
|
|
68
|
+
*/
|
|
69
|
+
export declare function readLock(projectRoot: string): AgentsLock | null;
|
|
70
|
+
/** Write the lock to `<projectRoot>/agents.lock`, returning the path written. */
|
|
71
|
+
export declare function writeLock(projectRoot: string, lock: AgentsLock): string;
|
|
72
|
+
/** Diff a recorded lock against a freshly-computed one. */
|
|
73
|
+
export declare function diffLock(expected: AgentsLock, actual: AgentsLock): LockDiff;
|
|
74
|
+
/** True when a diff has no added/removed/changed entries. */
|
|
75
|
+
export declare function lockDiffIsClean(diff: LockDiff): boolean;
|
|
76
|
+
/** Verify a recorded lock against the live resolved sources. */
|
|
77
|
+
export declare function verifyLock(expected: AgentsLock, sources: LockSource[]): LockDiff;
|
|
78
|
+
/**
|
|
79
|
+
* The project root the lockfile lives at: the parent of the discovered project
|
|
80
|
+
* `.agents/` dir, or the cwd itself when none is found.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveProjectRoot(cwd?: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Enumerate the resolved resource sources for a cwd: for each lockable kind, the
|
|
85
|
+
* winning entry per name across layers (project > user > system > extras), keyed
|
|
86
|
+
* `<kind>/<entryName>`. This is a single-level `readdir` per kind dir — the
|
|
87
|
+
* recursive fingerprinting is delegated to `fingerprintDir`.
|
|
88
|
+
*
|
|
89
|
+
* Entry names keep their extension in the key, so a hook script `foo.sh` and its
|
|
90
|
+
* sidecar `foo.yaml` stay distinct (a name-stripped enumerator would collide
|
|
91
|
+
* them). Dotfiles at the kind-dir top level are skipped as noise.
|
|
92
|
+
*/
|
|
93
|
+
export declare function enumerateLockSources(cwd?: string): LockSource[];
|
package/dist/lib/lock.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents.lock` — a deterministic SHA-256 manifest of the resolved resource set
|
|
3
|
+
* at a project root, plus the `--frozen` verification that fails closed on any
|
|
4
|
+
* drift. This is the reproducible-CI slice of the governance work (#337); org
|
|
5
|
+
* version pins and bundle signing are deferred follow-ups.
|
|
6
|
+
*
|
|
7
|
+
* Design:
|
|
8
|
+
* - Content hashing REUSES `fingerprintDir` / `fingerprintFile`
|
|
9
|
+
* (src/lib/staleness/fingerprint.ts) — the same two-tier fingerprinter every
|
|
10
|
+
* staleness checker uses. No new recursive file walker is introduced here.
|
|
11
|
+
* - The resource SET is the layered, resolved source union (project > user >
|
|
12
|
+
* system > extras), the same precedence `resolveResource` / `syncResourcesToVersion`
|
|
13
|
+
* read from. It is enumerated agent-independently on purpose: the lock must be
|
|
14
|
+
* reproducible on a fresh CI checkout that has no installed agent VERSIONS, so
|
|
15
|
+
* it snapshots the SOURCES sync copies FROM, not any per-version home.
|
|
16
|
+
*
|
|
17
|
+
* Lock shape (JSON): `{ "version": 1, "resources": { "<relpath>": "<sha256>" } }`,
|
|
18
|
+
* keys sorted so the file is byte-stable across machines and runs.
|
|
19
|
+
*/
|
|
20
|
+
import * as fs from 'fs';
|
|
21
|
+
import * as path from 'path';
|
|
22
|
+
import { fingerprintDir, fingerprintFile } from './staleness/fingerprint.js';
|
|
23
|
+
import { getProjectAgentsDir, getUserAgentsDir, getSystemAgentsDir, getEnabledExtraRepos, } from './state.js';
|
|
24
|
+
export const LOCK_FILENAME = 'agents.lock';
|
|
25
|
+
export const LOCK_VERSION = 1;
|
|
26
|
+
/**
|
|
27
|
+
* Resource kinds captured by the lock — the file/dir-backed layered resources
|
|
28
|
+
* shared across every agent. Presence-only kinds handled specially by the sync
|
|
29
|
+
* differ (plugins/promptcuts) are intentionally out of this first slice.
|
|
30
|
+
*/
|
|
31
|
+
export const LOCK_KINDS = [
|
|
32
|
+
'commands',
|
|
33
|
+
'skills',
|
|
34
|
+
'hooks',
|
|
35
|
+
'rules',
|
|
36
|
+
'mcp',
|
|
37
|
+
'permissions',
|
|
38
|
+
'subagents',
|
|
39
|
+
];
|
|
40
|
+
/** Normalise a filesystem-relative path to POSIX separators for stable keys. */
|
|
41
|
+
function toPosix(rel) {
|
|
42
|
+
return rel.split(path.sep).join('/');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Hash every resolved source into a flat `relpath -> sha256` map. Directories are
|
|
46
|
+
* expanded with `fingerprintDir` (every contained file, sorted); single files use
|
|
47
|
+
* `fingerprintFile`. Unreadable / missing sources are skipped — enumeration only
|
|
48
|
+
* ever hands us paths that existed at scan time.
|
|
49
|
+
*/
|
|
50
|
+
export function hashLockSources(sources) {
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const src of sources) {
|
|
53
|
+
let stat;
|
|
54
|
+
try {
|
|
55
|
+
stat = fs.statSync(src.path);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (stat.isDirectory()) {
|
|
61
|
+
for (const fp of fingerprintDir(src.path)) {
|
|
62
|
+
const rel = toPosix(path.relative(src.path, fp.path));
|
|
63
|
+
out[`${src.key}/${rel}`] = fp.sha256;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (stat.isFile()) {
|
|
67
|
+
const fp = fingerprintFile(src.path);
|
|
68
|
+
if (fp)
|
|
69
|
+
out[src.key] = fp.sha256;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
/** Build a fully-sorted lock object from a resolved source set. */
|
|
75
|
+
export function buildLock(sources) {
|
|
76
|
+
return { version: LOCK_VERSION, resources: sortRecord(hashLockSources(sources)) };
|
|
77
|
+
}
|
|
78
|
+
/** Return a new record with keys inserted in sorted order (deterministic JSON). */
|
|
79
|
+
function sortRecord(record) {
|
|
80
|
+
const out = {};
|
|
81
|
+
for (const key of Object.keys(record).sort())
|
|
82
|
+
out[key] = record[key];
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/** Serialise a lock to the canonical, sorted, newline-terminated JSON form. */
|
|
86
|
+
export function serializeLock(lock) {
|
|
87
|
+
return JSON.stringify({ version: lock.version, resources: sortRecord(lock.resources) }, null, 2) + '\n';
|
|
88
|
+
}
|
|
89
|
+
/** Absolute path to the lockfile for a project root. */
|
|
90
|
+
export function lockPath(projectRoot) {
|
|
91
|
+
return path.join(projectRoot, LOCK_FILENAME);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Read + validate an existing lock. Returns null ONLY when the file is absent —
|
|
95
|
+
* a present-but-malformed lock throws, so `--frozen` fails closed instead of
|
|
96
|
+
* silently treating corruption as "no lock".
|
|
97
|
+
*/
|
|
98
|
+
export function readLock(projectRoot) {
|
|
99
|
+
const p = lockPath(projectRoot);
|
|
100
|
+
if (!fs.existsSync(p))
|
|
101
|
+
return null;
|
|
102
|
+
let parsed;
|
|
103
|
+
try {
|
|
104
|
+
parsed = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
throw new Error(`${LOCK_FILENAME} is not valid JSON: ${e.message}`);
|
|
108
|
+
}
|
|
109
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
110
|
+
throw new Error(`${LOCK_FILENAME} is malformed (expected an object).`);
|
|
111
|
+
}
|
|
112
|
+
const obj = parsed;
|
|
113
|
+
if (obj.version !== LOCK_VERSION) {
|
|
114
|
+
throw new Error(`${LOCK_FILENAME} version ${String(obj.version)} is unsupported (expected ${LOCK_VERSION}).`);
|
|
115
|
+
}
|
|
116
|
+
if (!obj.resources || typeof obj.resources !== 'object') {
|
|
117
|
+
throw new Error(`${LOCK_FILENAME} is missing a resources map.`);
|
|
118
|
+
}
|
|
119
|
+
return { version: LOCK_VERSION, resources: obj.resources };
|
|
120
|
+
}
|
|
121
|
+
/** Write the lock to `<projectRoot>/agents.lock`, returning the path written. */
|
|
122
|
+
export function writeLock(projectRoot, lock) {
|
|
123
|
+
const p = lockPath(projectRoot);
|
|
124
|
+
fs.writeFileSync(p, serializeLock(lock));
|
|
125
|
+
return p;
|
|
126
|
+
}
|
|
127
|
+
/** Diff a recorded lock against a freshly-computed one. */
|
|
128
|
+
export function diffLock(expected, actual) {
|
|
129
|
+
const e = expected.resources;
|
|
130
|
+
const a = actual.resources;
|
|
131
|
+
const added = [];
|
|
132
|
+
const removed = [];
|
|
133
|
+
const changed = [];
|
|
134
|
+
for (const k of Object.keys(a))
|
|
135
|
+
if (!(k in e))
|
|
136
|
+
added.push(k);
|
|
137
|
+
for (const k of Object.keys(e)) {
|
|
138
|
+
if (!(k in a))
|
|
139
|
+
removed.push(k);
|
|
140
|
+
else if (e[k] !== a[k])
|
|
141
|
+
changed.push(k);
|
|
142
|
+
}
|
|
143
|
+
added.sort();
|
|
144
|
+
removed.sort();
|
|
145
|
+
changed.sort();
|
|
146
|
+
return { added, removed, changed };
|
|
147
|
+
}
|
|
148
|
+
/** True when a diff has no added/removed/changed entries. */
|
|
149
|
+
export function lockDiffIsClean(diff) {
|
|
150
|
+
return diff.added.length === 0 && diff.removed.length === 0 && diff.changed.length === 0;
|
|
151
|
+
}
|
|
152
|
+
/** Verify a recorded lock against the live resolved sources. */
|
|
153
|
+
export function verifyLock(expected, sources) {
|
|
154
|
+
return diffLock(expected, buildLock(sources));
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The project root the lockfile lives at: the parent of the discovered project
|
|
158
|
+
* `.agents/` dir, or the cwd itself when none is found.
|
|
159
|
+
*/
|
|
160
|
+
export function resolveProjectRoot(cwd = process.cwd()) {
|
|
161
|
+
const projectDir = getProjectAgentsDir(cwd);
|
|
162
|
+
return projectDir ? path.dirname(projectDir) : path.resolve(cwd);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Enumerate the resolved resource sources for a cwd: for each lockable kind, the
|
|
166
|
+
* winning entry per name across layers (project > user > system > extras), keyed
|
|
167
|
+
* `<kind>/<entryName>`. This is a single-level `readdir` per kind dir — the
|
|
168
|
+
* recursive fingerprinting is delegated to `fingerprintDir`.
|
|
169
|
+
*
|
|
170
|
+
* Entry names keep their extension in the key, so a hook script `foo.sh` and its
|
|
171
|
+
* sidecar `foo.yaml` stay distinct (a name-stripped enumerator would collide
|
|
172
|
+
* them). Dotfiles at the kind-dir top level are skipped as noise.
|
|
173
|
+
*/
|
|
174
|
+
export function enumerateLockSources(cwd = process.cwd()) {
|
|
175
|
+
const roots = [];
|
|
176
|
+
const projectDir = getProjectAgentsDir(cwd);
|
|
177
|
+
if (projectDir)
|
|
178
|
+
roots.push(projectDir);
|
|
179
|
+
roots.push(getUserAgentsDir());
|
|
180
|
+
roots.push(getSystemAgentsDir());
|
|
181
|
+
for (const extra of getEnabledExtraRepos())
|
|
182
|
+
roots.push(extra.dir);
|
|
183
|
+
const sources = [];
|
|
184
|
+
const seen = new Set();
|
|
185
|
+
for (const kind of LOCK_KINDS) {
|
|
186
|
+
for (const root of roots) {
|
|
187
|
+
const kindDir = path.join(root, kind);
|
|
188
|
+
let entries;
|
|
189
|
+
try {
|
|
190
|
+
entries = fs.readdirSync(kindDir, { withFileTypes: true });
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
for (const entry of entries) {
|
|
196
|
+
if (entry.name.startsWith('.'))
|
|
197
|
+
continue;
|
|
198
|
+
const key = `${kind}/${entry.name}`;
|
|
199
|
+
if (seen.has(key))
|
|
200
|
+
continue; // project layer wins on same name
|
|
201
|
+
seen.add(key);
|
|
202
|
+
sources.push({ path: path.join(kindDir, entry.name), key });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return sources.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
207
|
+
}
|
package/dist/lib/loop.js
CHANGED
|
@@ -26,6 +26,8 @@ import { buildExecCommand, buildExecEnv } from './exec.js';
|
|
|
26
26
|
import { extractUsageEvents } from './budget/enforce.js';
|
|
27
27
|
import { parseTimeout } from './routines.js';
|
|
28
28
|
import { writeCheckpoint } from './checkpoint.js';
|
|
29
|
+
import { mailboxDir } from './mailbox.js';
|
|
30
|
+
import { composeWin32CommandLine } from './platform/index.js';
|
|
29
31
|
const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
30
32
|
/** Path to a run's loop-signal.json. */
|
|
31
33
|
export function loopSignalPath(runDir) {
|
|
@@ -129,8 +131,14 @@ export function defaultRunIteration(options) {
|
|
|
129
131
|
const cwd = execOptions.cwd || process.cwd();
|
|
130
132
|
const model = execOptions.model ?? `${execOptions.agent}-default`;
|
|
131
133
|
return new Promise((resolve, reject) => {
|
|
134
|
+
// DEP0190-safe shell spawn: on the win32 shell path compose ONE fully-quoted
|
|
135
|
+
// command line and pass an EMPTY args array (see composeWin32CommandLine) so
|
|
136
|
+
// Node never concatenates the args array — which carries the re-injected
|
|
137
|
+
// prompt — into the cmd.exe line unescaped.
|
|
132
138
|
const useShell = process.platform === 'win32' && (!path.isAbsolute(executable) || executable.endsWith('.cmd'));
|
|
133
|
-
const
|
|
139
|
+
const spawnCommand = useShell ? composeWin32CommandLine(executable, args) : executable;
|
|
140
|
+
const spawnArgs = useShell ? [] : args;
|
|
141
|
+
const child = spawn(spawnCommand, spawnArgs, {
|
|
134
142
|
cwd,
|
|
135
143
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
136
144
|
env,
|
|
@@ -207,6 +215,9 @@ export async function runLoop(execOptions, loop, ctx, deps) {
|
|
|
207
215
|
process.stderr.write(`[loop] WARNING: cross-iteration conversation continuity applies to claude only. ` +
|
|
208
216
|
`Each ${ctx.agent} iteration runs as an independent fresh conversation (no /continue handoff).\n`);
|
|
209
217
|
}
|
|
218
|
+
// Surface the run-level mailbox id (otherwise undiscoverable — runId is not in
|
|
219
|
+
// the session registry) so an operator can message the loop mid-flight.
|
|
220
|
+
process.stderr.write(`[loop] mailbox: agents message ${ctx.runId} "<text>"\n`);
|
|
210
221
|
let tokens = ctx.startTokens ?? 0;
|
|
211
222
|
let lastSignal;
|
|
212
223
|
let stopSignal = false;
|
|
@@ -271,6 +282,10 @@ export async function runLoop(execOptions, loop, ctx, deps) {
|
|
|
271
282
|
AGENTS_RUN_DIR: ctx.runDir,
|
|
272
283
|
AGENTS_LOOP_SIGNAL: loopSignalPath(ctx.runDir),
|
|
273
284
|
AGENTS_LOOP_ITERATION: String(iteration),
|
|
285
|
+
// Every iteration is a fresh session, so key the mailbox by the stable
|
|
286
|
+
// run id — one box for the whole loop. Overrides the per-iteration
|
|
287
|
+
// AGENTS_MAILBOX_DIR that buildExecEnv would derive from sessionId.
|
|
288
|
+
AGENTS_MAILBOX_DIR: mailboxDir(ctx.runId),
|
|
274
289
|
},
|
|
275
290
|
};
|
|
276
291
|
let result;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This machine's stable, human-readable id. Lives in a dependency-free leaf so
|
|
3
|
+
* low-level modules (state.ts) can key per-device paths off it without importing
|
|
4
|
+
* the secrets/session-sync layer (which would create a cycle).
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a raw hostname into a stable device id: first label only,
|
|
8
|
+
* lowercased, non-alphanumerics collapsed to hyphens. `zion.tail…ts.net` and
|
|
9
|
+
* `ZION` both become `zion`. The single source for this transform — machineId()
|
|
10
|
+
* and cross-machine session grouping must agree or the local machine won't
|
|
11
|
+
* match its own registry key.
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeHost(raw: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* This machine's stable, human-readable id, used as its R2 sync prefix, session
|
|
16
|
+
* mirror directory, `agents devices` self-key, and per-device config folder.
|
|
17
|
+
* Tailnet hostnames (zion, yosemite-s0, mac-mini) are already unique and
|
|
18
|
+
* readable; we lowercase and strip any domain suffix. Overridable via
|
|
19
|
+
* AGENTS_SYNC_MACHINE_ID for tests and unusual setups.
|
|
20
|
+
*/
|
|
21
|
+
export declare function machineId(): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This machine's stable, human-readable id. Lives in a dependency-free leaf so
|
|
3
|
+
* low-level modules (state.ts) can key per-device paths off it without importing
|
|
4
|
+
* the secrets/session-sync layer (which would create a cycle).
|
|
5
|
+
*/
|
|
6
|
+
import * as os from 'os';
|
|
7
|
+
/**
|
|
8
|
+
* Normalize a raw hostname into a stable device id: first label only,
|
|
9
|
+
* lowercased, non-alphanumerics collapsed to hyphens. `zion.tail…ts.net` and
|
|
10
|
+
* `ZION` both become `zion`. The single source for this transform — machineId()
|
|
11
|
+
* and cross-machine session grouping must agree or the local machine won't
|
|
12
|
+
* match its own registry key.
|
|
13
|
+
*/
|
|
14
|
+
export function normalizeHost(raw) {
|
|
15
|
+
return raw.split('.')[0].trim().toLowerCase().replace(/[^a-z0-9_-]/g, '-') || 'unknown';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* This machine's stable, human-readable id, used as its R2 sync prefix, session
|
|
19
|
+
* mirror directory, `agents devices` self-key, and per-device config folder.
|
|
20
|
+
* Tailnet hostnames (zion, yosemite-s0, mac-mini) are already unique and
|
|
21
|
+
* readable; we lowercase and strip any domain suffix. Overridable via
|
|
22
|
+
* AGENTS_SYNC_MACHINE_ID for tests and unusual setups.
|
|
23
|
+
*/
|
|
24
|
+
export function machineId() {
|
|
25
|
+
return normalizeHost(process.env.AGENTS_SYNC_MACHINE_ID || os.hostname());
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve an `agents message <target>` argument to exactly one destination —
|
|
3
|
+
* a cloud task, one live local/teams/loop agent, or an error. The anti-misroute
|
|
4
|
+
* rule lives here: a target that matches zero or more-than-one live agent is
|
|
5
|
+
* NEVER guessed; the caller reports it. Pure (no I/O) so it is unit-testable.
|
|
6
|
+
*/
|
|
7
|
+
import type { ActiveSession } from './session/active.js';
|
|
8
|
+
export type MessageResolution = {
|
|
9
|
+
kind: 'cloud';
|
|
10
|
+
id: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'local';
|
|
13
|
+
id: string;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'none';
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'ambiguous';
|
|
18
|
+
candidates: Array<{
|
|
19
|
+
id: string;
|
|
20
|
+
label: string;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The mailbox id a live session's box is keyed by. Teams stamp a durable
|
|
25
|
+
* `agentId` (== the Claude session id for Claude teammates); a bare run has
|
|
26
|
+
* only its `sessionId`. The spawn-time `AGENTS_MAILBOX_DIR` wiring must key the
|
|
27
|
+
* box by this same id — this is the single source of truth for both sides.
|
|
28
|
+
*/
|
|
29
|
+
export declare function mailboxIdForActiveSession(s: ActiveSession): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve `target` against the live sessions. Exact id matches win over prefix
|
|
32
|
+
* matches; results are de-duped by canonical mailbox id (collapsed subagents
|
|
33
|
+
* share one). `isCloudTask` is consulted first so a cloud task id routes to the
|
|
34
|
+
* cloud provider.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveMessageTarget(target: string, sessions: ActiveSession[], isCloudTask: (id: string) => boolean): MessageResolution;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mailbox id a live session's box is keyed by. Teams stamp a durable
|
|
3
|
+
* `agentId` (== the Claude session id for Claude teammates); a bare run has
|
|
4
|
+
* only its `sessionId`. The spawn-time `AGENTS_MAILBOX_DIR` wiring must key the
|
|
5
|
+
* box by this same id — this is the single source of truth for both sides.
|
|
6
|
+
*/
|
|
7
|
+
export function mailboxIdForActiveSession(s) {
|
|
8
|
+
return s.agentId ?? s.sessionId;
|
|
9
|
+
}
|
|
10
|
+
function labelFor(s) {
|
|
11
|
+
return s.label ?? s.topic ?? s.teamName ?? s.host ?? s.context;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve `target` against the live sessions. Exact id matches win over prefix
|
|
15
|
+
* matches; results are de-duped by canonical mailbox id (collapsed subagents
|
|
16
|
+
* share one). `isCloudTask` is consulted first so a cloud task id routes to the
|
|
17
|
+
* cloud provider.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveMessageTarget(target, sessions, isCloudTask) {
|
|
20
|
+
if (isCloudTask(target))
|
|
21
|
+
return { kind: 'cloud', id: target };
|
|
22
|
+
// An empty target would make every `startsWith` prefix match — never guess.
|
|
23
|
+
if (target.length === 0)
|
|
24
|
+
return { kind: 'none' };
|
|
25
|
+
const exact = sessions.filter((s) => s.sessionId === target || s.agentId === target);
|
|
26
|
+
const chosen = exact.length > 0
|
|
27
|
+
? exact
|
|
28
|
+
: sessions.filter((s) => Boolean(s.sessionId?.startsWith(target)) || Boolean(s.agentId?.startsWith(target)));
|
|
29
|
+
// De-dupe by canonical mailbox id (one box per logical agent).
|
|
30
|
+
const byId = new Map();
|
|
31
|
+
for (const s of chosen) {
|
|
32
|
+
const id = mailboxIdForActiveSession(s);
|
|
33
|
+
if (id && !byId.has(id))
|
|
34
|
+
byId.set(id, s);
|
|
35
|
+
}
|
|
36
|
+
const ids = [...byId.keys()];
|
|
37
|
+
if (ids.length === 0)
|
|
38
|
+
return { kind: 'none' };
|
|
39
|
+
if (ids.length === 1)
|
|
40
|
+
return { kind: 'local', id: ids[0] };
|
|
41
|
+
return {
|
|
42
|
+
kind: 'ambiguous',
|
|
43
|
+
candidates: [...byId.entries()].map(([id, s]) => ({ id, label: labelFor(s) })),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** A single mailbox message. `text` may embed `host:/path` clip tokens. */
|
|
2
|
+
export interface MailboxMessage {
|
|
3
|
+
/** Unique, time-sortable id. Also the on-disk filename stem. */
|
|
4
|
+
msgId: string;
|
|
5
|
+
/** The mailboxId this message is addressed to (anti-misroute stamp). */
|
|
6
|
+
to: string;
|
|
7
|
+
/** Who sent it (operator label / agent id / host). Optional. */
|
|
8
|
+
from?: string;
|
|
9
|
+
/** ISO-8601 creation time. */
|
|
10
|
+
ts: string;
|
|
11
|
+
/** The message body. */
|
|
12
|
+
text: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A mailboxId must be a single, separator-free path segment. Real ids (session
|
|
16
|
+
* UUID / teams agentId / loop runId) already satisfy this; rejecting anything
|
|
17
|
+
* else fails loud instead of silently misrouting — a message whose id contained
|
|
18
|
+
* a `/` would nest under a different dir than the `to` stamp it is matched
|
|
19
|
+
* against, and would be dropped with no error. Also blocks `.`/`..` traversal
|
|
20
|
+
* once `agents message` starts accepting external target ids.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isValidMailboxId(mailboxId: string): boolean;
|
|
23
|
+
export declare function assertValidMailboxId(mailboxId: string): void;
|
|
24
|
+
/** Absolute path to a box. `root` override is for tests. */
|
|
25
|
+
export declare function mailboxDir(mailboxId: string, root?: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Enqueue a message into `boxDir` atomically. Returns the msgId. The `to` field
|
|
28
|
+
* is stamped so a drain can refuse a message that lands in the wrong box.
|
|
29
|
+
*/
|
|
30
|
+
export declare function enqueue(boxDir: string, msg: {
|
|
31
|
+
to: string;
|
|
32
|
+
text: string;
|
|
33
|
+
from?: string;
|
|
34
|
+
}): string;
|
|
35
|
+
/**
|
|
36
|
+
* Drain the box: return every pending message addressed to it, in FIFO order,
|
|
37
|
+
* removing them from the queue. Claim-first (inbox → processing → consumed) so
|
|
38
|
+
* an interrupted drain is recovered on the next call (at-least-once). Corrupt
|
|
39
|
+
* files and messages addressed to a different box are dropped, not returned.
|
|
40
|
+
*
|
|
41
|
+
* `boxId` defaults to the box's directory name — the id it was created under.
|
|
42
|
+
*/
|
|
43
|
+
export declare function drain(boxDir: string, boxId?: string): MailboxMessage[];
|
|
44
|
+
/** Read pending messages (inbox + in-flight) without consuming them. FIFO. */
|
|
45
|
+
export declare function peek(boxDir: string, boxId?: string): MailboxMessage[];
|
|
46
|
+
/** Delete pending (not-yet-claimed) inbox messages. Returns the count removed. */
|
|
47
|
+
export declare function clear(boxDir: string): number;
|