@phnx-labs/agents-cli 1.20.35 → 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 +91 -0
- package/LICENSE +185 -21
- package/README.md +8 -4
- 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.js +81 -0
- package/dist/commands/daemon.js +4 -1
- package/dist/commands/doctor.js +1 -89
- package/dist/commands/events.js +3 -3
- package/dist/commands/exec.d.ts +19 -0
- package/dist/commands/exec.js +277 -11
- package/dist/commands/hosts.js +10 -6
- package/dist/commands/inspect.js +8 -9
- package/dist/commands/lock.d.ts +12 -0
- package/dist/commands/lock.js +70 -0
- package/dist/commands/message.d.ts +15 -0
- package/dist/commands/message.js +56 -0
- 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 +156 -75
- 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.js +20 -8
- package/dist/commands/sessions.d.ts +71 -1
- package/dist/commands/sessions.js +365 -37
- 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/watchdog.d.ts +18 -0
- package/dist/commands/watchdog.js +238 -0
- package/dist/index.js +25 -2
- 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/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +18 -7
- package/dist/lib/browser/drivers/ssh.js +2 -1
- package/dist/lib/browser/har.d.ts +84 -0
- package/dist/lib/browser/har.js +77 -0
- package/dist/lib/browser/ipc.js +24 -3
- 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 +112 -8
- 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 +57 -0
- package/dist/lib/daemon.js +192 -16
- 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 +1 -1
- package/dist/lib/events.js +31 -13
- package/dist/lib/exec.d.ts +17 -0
- package/dist/lib/exec.js +79 -13
- 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 +127 -8
- package/dist/lib/hosts/dispatch.d.ts +15 -0
- package/dist/lib/hosts/dispatch.js +39 -6
- package/dist/lib/hosts/logs.js +30 -1
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/passthrough.js +3 -1
- package/dist/lib/hosts/ready.d.ts +29 -6
- package/dist/lib/hosts/ready.js +66 -15
- 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 +14 -0
- package/dist/lib/hosts/tasks.js +15 -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/picker.d.ts +2 -0
- package/dist/lib/picker.js +4 -1
- 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/plugins.js +16 -6
- 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 +54 -0
- package/dist/lib/session/active.js +190 -19
- package/dist/lib/session/discover.d.ts +37 -0
- package/dist/lib/session/discover.js +111 -28
- package/dist/lib/session/inject.d.ts +18 -0
- package/dist/lib/session/inject.js +21 -0
- package/dist/lib/session/parse.js +23 -20
- package/dist/lib/session/pid-registry.d.ts +1 -0
- package/dist/lib/session/pid-registry.js +24 -0
- package/dist/lib/session/provenance.d.ts +14 -2
- package/dist/lib/session/provenance.js +39 -8
- package/dist/lib/session/remote-active.js +19 -7
- 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 +16 -2
- package/dist/lib/session/sync/config.d.ts +1 -15
- package/dist/lib/session/sync/config.js +4 -20
- package/dist/lib/session/types.d.ts +17 -0
- package/dist/lib/shims.d.ts +36 -6
- package/dist/lib/shims.js +91 -29
- package/dist/lib/ssh-exec.js +2 -0
- package/dist/lib/ssh-tunnel.js +2 -1
- package/dist/lib/startup/command-registry.d.ts +6 -0
- package/dist/lib/startup/command-registry.js +13 -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/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/index.d.ts +2 -1
- package/dist/lib/terminal/backends/index.js +3 -1
- 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/index.d.ts +4 -1
- package/dist/lib/terminal/index.js +4 -1
- package/dist/lib/terminal/inject.d.ts +204 -0
- package/dist/lib/terminal/inject.js +247 -0
- package/dist/lib/terminal/resolve.d.ts +64 -0
- package/dist/lib/terminal/resolve.js +90 -0
- package/dist/lib/terminal/types.d.ts +1 -1
- package/dist/lib/triggers/webhook.d.ts +85 -0
- package/dist/lib/triggers/webhook.js +141 -0
- package/dist/lib/versions.d.ts +23 -0
- package/dist/lib/versions.js +119 -13
- 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/workflows.d.ts +166 -0
- package/dist/lib/workflows.js +193 -0
- package/package.json +5 -4
package/dist/lib/ssh-tunnel.js
CHANGED
|
@@ -23,6 +23,7 @@ import * as path from 'path';
|
|
|
23
23
|
import { fileURLToPath } from 'url';
|
|
24
24
|
import { randomBytes } from 'crypto';
|
|
25
25
|
import { sshExec, SSH_OPTS } from './ssh-exec.js';
|
|
26
|
+
import { backgroundSpawnOptions } from './platform/process.js';
|
|
26
27
|
import { encodePowerShell } from './browser/drivers/ssh.js';
|
|
27
28
|
import { getDevice } from './devices/registry.js';
|
|
28
29
|
import { sshTargetFor } from './devices/connect.js';
|
|
@@ -56,7 +57,7 @@ export function startSSHTunnel(user, host, localPort, remotePort, opts = {}) {
|
|
|
56
57
|
const args = buildTunnelArgs(user, host, localPort, remotePort);
|
|
57
58
|
const tunnel = spawn('ssh', args, {
|
|
58
59
|
stdio: opts.detached ? 'ignore' : ['ignore', 'ignore', 'pipe'],
|
|
59
|
-
|
|
60
|
+
...(opts.detached ? backgroundSpawnOptions() : { detached: false, windowsHide: true }),
|
|
60
61
|
});
|
|
61
62
|
let stderr = '';
|
|
62
63
|
tunnel.stderr?.on('data', (data) => {
|
|
@@ -49,6 +49,7 @@ export declare const loadPrune: ModuleLoader;
|
|
|
49
49
|
export declare const loadTrash: ModuleLoader;
|
|
50
50
|
export declare const loadRestore: ModuleLoader;
|
|
51
51
|
export declare const loadDoctor: ModuleLoader;
|
|
52
|
+
export declare const loadCheck: ModuleLoader;
|
|
52
53
|
export declare const loadStatus: ModuleLoader;
|
|
53
54
|
export declare const loadProfiles: ModuleLoader;
|
|
54
55
|
export declare const loadSecrets: ModuleLoader;
|
|
@@ -57,6 +58,7 @@ export declare const loadHelper: ModuleLoader;
|
|
|
57
58
|
export declare const loadMenubar: ModuleLoader;
|
|
58
59
|
export declare const loadBeta: ModuleLoader;
|
|
59
60
|
export declare const loadSync: ModuleLoader;
|
|
61
|
+
export declare const loadLock: ModuleLoader;
|
|
60
62
|
export declare const loadRefreshRules: ModuleLoader;
|
|
61
63
|
export declare const loadDrive: ModuleLoader;
|
|
62
64
|
export declare const loadFactory: ModuleLoader;
|
|
@@ -66,6 +68,7 @@ export declare const loadBudget: ModuleLoader;
|
|
|
66
68
|
export declare const loadAlias: ModuleLoader;
|
|
67
69
|
export declare const loadPty: ModuleLoader;
|
|
68
70
|
export declare const loadTmux: ModuleLoader;
|
|
71
|
+
export declare const loadWatchdog: ModuleLoader;
|
|
69
72
|
export declare const loadBrowser: ModuleLoader;
|
|
70
73
|
export declare const loadComputer: ModuleLoader;
|
|
71
74
|
export declare const loadHosts: ModuleLoader;
|
|
@@ -79,6 +82,9 @@ export declare const loadSetup: ModuleLoader;
|
|
|
79
82
|
export declare const loadSessions: ModuleLoader;
|
|
80
83
|
export declare const loadTeams: ModuleLoader;
|
|
81
84
|
export declare const loadCloud: ModuleLoader;
|
|
85
|
+
export declare const loadMessage: ModuleLoader;
|
|
86
|
+
export declare const loadServe: ModuleLoader;
|
|
87
|
+
export declare const loadAudit: ModuleLoader;
|
|
82
88
|
/**
|
|
83
89
|
* Commands whose modules pull in the SQLite-backed session/cloud stack. They are
|
|
84
90
|
* registered AFTER `applyGlobalHelpConventions` (mirroring main's order: help
|
|
@@ -27,6 +27,7 @@ export const loadPrune = async () => (await import('../../commands/prune.js')).r
|
|
|
27
27
|
export const loadTrash = async () => (await import('../../commands/trash.js')).registerTrashCommands;
|
|
28
28
|
export const loadRestore = async () => (await import('../../commands/trash.js')).registerRestoreCommand;
|
|
29
29
|
export const loadDoctor = async () => (await import('../../commands/doctor.js')).registerDoctorCommand;
|
|
30
|
+
export const loadCheck = async () => (await import('../../commands/check.js')).registerCheckCommand;
|
|
30
31
|
export const loadStatus = async () => (await import('../../commands/status.js')).registerStatusCommand;
|
|
31
32
|
export const loadProfiles = async () => (await import('../../commands/profiles.js')).registerProfilesCommands;
|
|
32
33
|
export const loadSecrets = async () => (await import('../../commands/secrets.js')).registerSecretsCommands;
|
|
@@ -35,6 +36,7 @@ export const loadHelper = async () => (await import('../../commands/helper.js'))
|
|
|
35
36
|
export const loadMenubar = async () => (await import('../../commands/menubar.js')).registerMenubarCommands;
|
|
36
37
|
export const loadBeta = async () => (await import('../../commands/beta.js')).registerBetaCommands;
|
|
37
38
|
export const loadSync = async () => (await import('../../commands/sync.js')).registerSyncCommand;
|
|
39
|
+
export const loadLock = async () => (await import('../../commands/lock.js')).registerLockCommand;
|
|
38
40
|
export const loadRefreshRules = async () => (await import('../../commands/refresh-rules.js')).registerRefreshRulesCommand;
|
|
39
41
|
export const loadDrive = async () => (await import('../../commands/drive.js')).registerDriveCommands;
|
|
40
42
|
export const loadFactory = async () => (await import('../../commands/factory.js')).registerFactoryCommands;
|
|
@@ -44,6 +46,7 @@ export const loadBudget = async () => (await import('../../commands/budget.js'))
|
|
|
44
46
|
export const loadAlias = async () => (await import('../../commands/alias.js')).registerAliasCommand;
|
|
45
47
|
export const loadPty = async () => (await import('../../commands/pty.js')).registerPtyCommands;
|
|
46
48
|
export const loadTmux = async () => (await import('../../commands/tmux.js')).registerTmuxCommands;
|
|
49
|
+
export const loadWatchdog = async () => (await import('../../commands/watchdog.js')).registerWatchdogCommand;
|
|
47
50
|
export const loadBrowser = async () => (await import('../../commands/browser.js')).registerBrowserCommand;
|
|
48
51
|
export const loadComputer = async () => (await import('../../commands/computer.js')).registerComputerCommand;
|
|
49
52
|
export const loadHosts = async () => (await import('../../commands/hosts.js')).registerHostsCommand;
|
|
@@ -57,6 +60,9 @@ export const loadSetup = async () => (await import('../../commands/setup.js')).r
|
|
|
57
60
|
export const loadSessions = async () => (await import('../../commands/sessions.js')).registerSessionsCommands;
|
|
58
61
|
export const loadTeams = async () => (await import('../../commands/teams.js')).registerTeamsCommands;
|
|
59
62
|
export const loadCloud = async () => (await import('../../commands/cloud.js')).registerCloudCommands;
|
|
63
|
+
export const loadMessage = async () => (await import('../../commands/message.js')).registerMessageCommand;
|
|
64
|
+
export const loadServe = async () => (await import('../../commands/serve.js')).registerServeCommand;
|
|
65
|
+
export const loadAudit = async () => (await import('../../commands/audit.js')).registerAuditCommands;
|
|
60
66
|
/**
|
|
61
67
|
* Commands whose modules pull in the SQLite-backed session/cloud stack. They are
|
|
62
68
|
* registered AFTER `applyGlobalHelpConventions` (mirroring main's order: help
|
|
@@ -64,7 +70,7 @@ export const loadCloud = async () => (await import('../../commands/cloud.js')).r
|
|
|
64
70
|
* inherit the root's custom help formatter rather than getting the per-command
|
|
65
71
|
* recursive pass. Keeping that ordering preserves their `--help` output exactly.
|
|
66
72
|
*/
|
|
67
|
-
export const LAZY_COMMAND_NAMES = new Set(['sessions', 'teams', 'cloud']);
|
|
73
|
+
export const LAZY_COMMAND_NAMES = new Set(['sessions', 'teams', 'cloud', 'message', 'serve']);
|
|
68
74
|
/**
|
|
69
75
|
* User-typed top-level command name -> ordered list of module loaders to run.
|
|
70
76
|
*
|
|
@@ -113,6 +119,7 @@ export const COMMAND_LOADERS = {
|
|
|
113
119
|
trash: [loadTrash],
|
|
114
120
|
restore: [loadRestore],
|
|
115
121
|
doctor: [loadDoctor],
|
|
122
|
+
check: [loadCheck],
|
|
116
123
|
status: [loadStatus],
|
|
117
124
|
profiles: [loadProfiles],
|
|
118
125
|
secrets: [loadSecrets],
|
|
@@ -121,6 +128,7 @@ export const COMMAND_LOADERS = {
|
|
|
121
128
|
menubar: [loadMenubar],
|
|
122
129
|
beta: [loadBeta],
|
|
123
130
|
sync: [loadSync],
|
|
131
|
+
lock: [loadLock],
|
|
124
132
|
'refresh-rules': [loadRefreshRules],
|
|
125
133
|
drive: [loadDrive],
|
|
126
134
|
factory: [loadFactory],
|
|
@@ -130,6 +138,7 @@ export const COMMAND_LOADERS = {
|
|
|
130
138
|
alias: [loadAlias],
|
|
131
139
|
pty: [loadPty],
|
|
132
140
|
tmux: [loadTmux],
|
|
141
|
+
watchdog: [loadWatchdog],
|
|
133
142
|
browser: [loadBrowser],
|
|
134
143
|
computer: [loadComputer],
|
|
135
144
|
hosts: [loadHosts],
|
|
@@ -144,4 +153,7 @@ export const COMMAND_LOADERS = {
|
|
|
144
153
|
sessions: [loadSessions],
|
|
145
154
|
teams: [loadTeams],
|
|
146
155
|
cloud: [loadCloud],
|
|
156
|
+
message: [loadMessage],
|
|
157
|
+
serve: [loadServe],
|
|
158
|
+
audit: [loadAudit],
|
|
147
159
|
};
|
package/dist/lib/state.d.ts
CHANGED
|
@@ -120,6 +120,8 @@ export declare function getRoutinesDir(): string;
|
|
|
120
120
|
export declare function getProjectRoutinesDir(cwd?: string): string | null;
|
|
121
121
|
/** Path to routine execution logs (~/.agents/.history/runs/). */
|
|
122
122
|
export declare function getRunsDir(): string;
|
|
123
|
+
/** Root for per-agent mailboxes (~/.agents/.history/mailbox/). */
|
|
124
|
+
export declare function getMailboxRootDir(): string;
|
|
123
125
|
/** Path to installed agent CLI binaries (~/.agents/.history/versions/). */
|
|
124
126
|
export declare function getVersionsDir(): string;
|
|
125
127
|
/** Path to version-switching shim scripts (~/.agents/.cache/shims/). */
|
|
@@ -226,6 +228,17 @@ export declare function getEnabledExtraRepos(): Array<{
|
|
|
226
228
|
export declare function ensureAgentsDir(): void;
|
|
227
229
|
/** Return an empty Meta object used when no agents.yaml exists yet. */
|
|
228
230
|
export declare function createDefaultMeta(): Meta;
|
|
231
|
+
/**
|
|
232
|
+
* Per-device machine-local version pins — `~/.agents/devices/<machine>/agents.yaml`.
|
|
233
|
+
* Committed and synced, but each machine only ever writes its OWN folder, so
|
|
234
|
+
* pulls never conflict. `<machine>` = machineId() (Tailscale-aligned short name).
|
|
235
|
+
*/
|
|
236
|
+
export declare function getDeviceMetaPath(): string;
|
|
237
|
+
/**
|
|
238
|
+
* Machine-local per-version resource tracking — `~/.agents/.history/version-resources.json`.
|
|
239
|
+
* Gitignored (under .history/) and regenerable; never synced.
|
|
240
|
+
*/
|
|
241
|
+
export declare function getVersionResourcesPath(): string;
|
|
229
242
|
/**
|
|
230
243
|
* Read and cache ~/.agents/agents.yaml, migrating from legacy locations if needed.
|
|
231
244
|
*
|
package/dist/lib/state.js
CHANGED
|
@@ -28,6 +28,7 @@ import * as os from 'os';
|
|
|
28
28
|
import * as yaml from 'yaml';
|
|
29
29
|
import { ensureLockTarget, atomicWriteFileSync, withFileLock } from './fs-atomic.js';
|
|
30
30
|
import { SEEDED_REGISTRIES } from './types.js';
|
|
31
|
+
import { machineId } from './machine-id.js';
|
|
31
32
|
const HOME = process.env.HOME ?? os.homedir();
|
|
32
33
|
/**
|
|
33
34
|
* Compare two filesystem paths for identity, resolving symlinks and (on
|
|
@@ -87,6 +88,7 @@ const RUNS_DIR = path.join(HISTORY_DIR, 'runs');
|
|
|
87
88
|
const TEAMS_AGENTS_DIR = path.join(HISTORY_DIR, 'teams', 'agents');
|
|
88
89
|
const BACKUPS_DIR = path.join(HISTORY_DIR, 'backups');
|
|
89
90
|
const TRASH_DIR = path.join(HISTORY_DIR, 'trash');
|
|
91
|
+
const MAILBOX_DIR = path.join(HISTORY_DIR, 'mailbox');
|
|
90
92
|
// Cache bucket (regenerable).
|
|
91
93
|
const SHIMS_DIR = path.join(CACHE_DIR, 'shims');
|
|
92
94
|
const HOOK_SHIMS_DIR = path.join(SHIMS_DIR, 'hooks');
|
|
@@ -309,6 +311,8 @@ export function getProjectRoutinesDir(cwd = process.cwd()) {
|
|
|
309
311
|
}
|
|
310
312
|
/** Path to routine execution logs (~/.agents/.history/runs/). */
|
|
311
313
|
export function getRunsDir() { return RUNS_DIR; }
|
|
314
|
+
/** Root for per-agent mailboxes (~/.agents/.history/mailbox/). */
|
|
315
|
+
export function getMailboxRootDir() { return MAILBOX_DIR; }
|
|
312
316
|
/** Path to installed agent CLI binaries (~/.agents/.history/versions/). */
|
|
313
317
|
export function getVersionsDir() { return VERSIONS_DIR; }
|
|
314
318
|
/** Path to version-switching shim scripts (~/.agents/.cache/shims/). */
|
|
@@ -495,13 +499,37 @@ function safeMtimeMs(filePath) {
|
|
|
495
499
|
return 0;
|
|
496
500
|
}
|
|
497
501
|
}
|
|
498
|
-
/**
|
|
502
|
+
/**
|
|
503
|
+
* Per-device machine-local version pins — `~/.agents/devices/<machine>/agents.yaml`.
|
|
504
|
+
* Committed and synced, but each machine only ever writes its OWN folder, so
|
|
505
|
+
* pulls never conflict. `<machine>` = machineId() (Tailscale-aligned short name).
|
|
506
|
+
*/
|
|
507
|
+
export function getDeviceMetaPath() {
|
|
508
|
+
return path.join(USER_AGENTS_DIR, 'devices', machineId(), 'agents.yaml');
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Machine-local per-version resource tracking — `~/.agents/.history/version-resources.json`.
|
|
512
|
+
* Gitignored (under .history/) and regenerable; never synced.
|
|
513
|
+
*/
|
|
514
|
+
export function getVersionResourcesPath() {
|
|
515
|
+
return path.join(HISTORY_DIR, 'version-resources.json');
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Combined cache stamp across all four Meta sources: central + system
|
|
519
|
+
* agents.yaml, this machine's device pins, and the version-resources tracking.
|
|
520
|
+
* A delimited string, NOT a numeric sum — summing down-scaled epoch-ms values
|
|
521
|
+
* loses precision (float64 rounds sub-unit terms away at ~1.75e12), so a change
|
|
522
|
+
* in any one file must contribute at full resolution.
|
|
523
|
+
*/
|
|
499
524
|
function currentMetaStamp() {
|
|
500
|
-
return safeMtimeMs(META_FILE)
|
|
525
|
+
return safeMtimeMs(META_FILE)
|
|
526
|
+
+ '|' + safeMtimeMs(SYSTEM_META_FILE)
|
|
527
|
+
+ '|' + safeMtimeMs(getDeviceMetaPath())
|
|
528
|
+
+ '|' + safeMtimeMs(getVersionResourcesPath());
|
|
501
529
|
}
|
|
502
530
|
/** Memoize a parsed Meta against the current file mtimes. */
|
|
503
531
|
function rememberMeta(meta) {
|
|
504
|
-
metaCache = {
|
|
532
|
+
metaCache = { stamp: currentMetaStamp(), meta };
|
|
505
533
|
return meta;
|
|
506
534
|
}
|
|
507
535
|
function withMetaLock(fn) {
|
|
@@ -526,11 +554,78 @@ function withMetaLock(fn) {
|
|
|
526
554
|
}
|
|
527
555
|
});
|
|
528
556
|
}
|
|
557
|
+
/** Atomic write only when the on-disk content differs — avoids needless mtime
|
|
558
|
+
* bumps (which would thrash the meta cache) on no-op field routing. */
|
|
559
|
+
function writeIfChanged(filePath, content) {
|
|
560
|
+
let current = null;
|
|
561
|
+
try {
|
|
562
|
+
current = fs.readFileSync(filePath, 'utf-8');
|
|
563
|
+
}
|
|
564
|
+
catch { /* absent */ }
|
|
565
|
+
if (current === content)
|
|
566
|
+
return;
|
|
567
|
+
atomicWriteFileSync(filePath, content);
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Partition the in-memory Meta across three files by sync-domain:
|
|
571
|
+
* - central `~/.agents/agents.yaml` — portable, everything else
|
|
572
|
+
* - device `~/.agents/devices/<machine>/agents.yaml` — `agents:` pins (per-device)
|
|
573
|
+
* - history `~/.agents/.history/version-resources.json` — `versions:` (machine-local)
|
|
574
|
+
* All callers funnel through writeMeta → here, so nothing else changes. Empty
|
|
575
|
+
* `agents:` / `versions:` are not written (no empty committed files).
|
|
576
|
+
*/
|
|
529
577
|
function writeMetaUnlocked(meta) {
|
|
530
|
-
const
|
|
531
|
-
|
|
578
|
+
const { agents, versions, ...central } = meta;
|
|
579
|
+
// Write the machine-local files FIRST, then strip central — so a crash mid-write
|
|
580
|
+
// never removes pins/versions from central before they're persisted elsewhere.
|
|
581
|
+
const devicePath = getDeviceMetaPath();
|
|
582
|
+
if (agents && Object.keys(agents).length > 0) {
|
|
583
|
+
fs.mkdirSync(path.dirname(devicePath), { recursive: true });
|
|
584
|
+
writeIfChanged(devicePath, META_HEADER + yaml.stringify({ agents }));
|
|
585
|
+
}
|
|
586
|
+
else if (fs.existsSync(devicePath)) {
|
|
587
|
+
// Every pin was cleared. Persist the emptied map instead of skipping the
|
|
588
|
+
// write — otherwise the stale device file survives and overlayMachineLocal
|
|
589
|
+
// re-applies the removed pin on the next read, leaving a dangling default
|
|
590
|
+
// (e.g. a launcher pointing at a version that was just uninstalled).
|
|
591
|
+
writeIfChanged(devicePath, META_HEADER + yaml.stringify({ agents: {} }));
|
|
592
|
+
}
|
|
593
|
+
if (versions && Object.keys(versions).length > 0) {
|
|
594
|
+
const vrPath = getVersionResourcesPath();
|
|
595
|
+
fs.mkdirSync(path.dirname(vrPath), { recursive: true });
|
|
596
|
+
writeIfChanged(vrPath, JSON.stringify(versions, null, 2) + '\n');
|
|
597
|
+
}
|
|
598
|
+
writeIfChanged(META_FILE, META_HEADER + yaml.stringify(central));
|
|
532
599
|
metaCache = null;
|
|
533
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* Overlay this machine's local state onto a central-portable Meta:
|
|
603
|
+
* - `agents:` from the device file (device wins; the union both preserves the
|
|
604
|
+
* one-level merge and self-heals a pre-migration central that still has pins)
|
|
605
|
+
* - `versions:` from the history JSON (wholesale replace; falls back to
|
|
606
|
+
* whatever central carried when the history file doesn't exist yet)
|
|
607
|
+
*/
|
|
608
|
+
function overlayMachineLocal(meta) {
|
|
609
|
+
const devicePath = getDeviceMetaPath();
|
|
610
|
+
if (fs.existsSync(devicePath)) {
|
|
611
|
+
try {
|
|
612
|
+
const dm = yaml.parse(fs.readFileSync(devicePath, 'utf-8'));
|
|
613
|
+
if (dm?.agents)
|
|
614
|
+
meta.agents = { ...meta.agents, ...dm.agents };
|
|
615
|
+
}
|
|
616
|
+
catch { /* ignore malformed device file */ }
|
|
617
|
+
}
|
|
618
|
+
const vrPath = getVersionResourcesPath();
|
|
619
|
+
if (fs.existsSync(vrPath)) {
|
|
620
|
+
try {
|
|
621
|
+
const vr = JSON.parse(fs.readFileSync(vrPath, 'utf-8'));
|
|
622
|
+
if (vr)
|
|
623
|
+
meta.versions = vr;
|
|
624
|
+
}
|
|
625
|
+
catch { /* ignore malformed history file */ }
|
|
626
|
+
}
|
|
627
|
+
return meta;
|
|
628
|
+
}
|
|
534
629
|
function applyRegistrySeeds(meta) {
|
|
535
630
|
const seeded = new Set(meta.seededPresets || []);
|
|
536
631
|
let changed = false;
|
|
@@ -590,10 +685,7 @@ export function readMeta() {
|
|
|
590
685
|
// what we last parsed. Reduces N readMeta calls per CLI invocation to ~2 stat
|
|
591
686
|
// syscalls plus an in-memory object spread.
|
|
592
687
|
if (metaCache) {
|
|
593
|
-
|
|
594
|
-
const systemMtime = safeMtimeMs(SYSTEM_META_FILE);
|
|
595
|
-
const stamp = userMtime + systemMtime * 1e-3;
|
|
596
|
-
if (stamp === metaCache.mtime) {
|
|
688
|
+
if (currentMetaStamp() === metaCache.stamp) {
|
|
597
689
|
return metaCache.meta;
|
|
598
690
|
}
|
|
599
691
|
}
|
|
@@ -666,6 +758,7 @@ export function readMeta() {
|
|
|
666
758
|
...userMeta?.registries,
|
|
667
759
|
};
|
|
668
760
|
}
|
|
761
|
+
overlayMachineLocal(meta);
|
|
669
762
|
if (applyRegistrySeeds(meta)) {
|
|
670
763
|
writeMeta(meta);
|
|
671
764
|
return rememberMeta(meta);
|
|
@@ -673,6 +766,7 @@ export function readMeta() {
|
|
|
673
766
|
return rememberMeta(meta);
|
|
674
767
|
}
|
|
675
768
|
const meta = createDefaultMeta();
|
|
769
|
+
overlayMachineLocal(meta);
|
|
676
770
|
if (applyRegistrySeeds(meta)) {
|
|
677
771
|
writeMeta(meta);
|
|
678
772
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Umbrella `agents sync` orchestration — "make this machine current".
|
|
3
3
|
*
|
|
4
|
-
* Bare `agents sync` fetches
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Bare `agents sync` fetches the config repos then reconciles them into every
|
|
5
|
+
* installed agent's version home. Secrets and sessions are opt-in stages
|
|
6
|
+
* (`--secrets` / `--sessions`) — see `planUmbrellaStages` for why they're off by
|
|
7
|
+
* default. Each stage is an existing exported library function; this module only
|
|
8
|
+
* sequences them and decides — from the flags — which stages run. The planner is
|
|
9
|
+
* pure so the flag matrix is unit-tested without any I/O.
|
|
9
10
|
*
|
|
10
11
|
* Stage backends:
|
|
11
12
|
* repos -> git pull of ~/.agents + enabled ~/.agents-* extras (pullRepo)
|
|
@@ -32,11 +33,17 @@ export interface UmbrellaPlan {
|
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* Decide which stages run. Pure — no I/O. Semantics:
|
|
35
|
-
* bare (no flags) fetch
|
|
36
|
+
* bare (no flags) fetch repos, then reconcile
|
|
36
37
|
* --local reconcile only, no fetch
|
|
37
|
-
* --cloud fetch (
|
|
38
|
+
* --cloud fetch repos (or the selected subset), skip reconcile
|
|
38
39
|
* --repos/--secrets/... fetch only the selected types, then reconcile
|
|
39
40
|
* `--local` wins over everything; `--cloud` suppresses reconcile.
|
|
41
|
+
*
|
|
42
|
+
* Secrets and sessions are NOT part of the bare default — they are opt-in via
|
|
43
|
+
* `--secrets` / `--sessions`. Pulling every secret bundle onto the machine on a
|
|
44
|
+
* bare `agents sync` is more blast radius than the verb should carry by
|
|
45
|
+
* default, and session transcripts are queryable on demand (`agents sessions
|
|
46
|
+
* --host <machine>`) so they don't need eager mirroring.
|
|
40
47
|
*/
|
|
41
48
|
export declare function planUmbrellaStages(f: UmbrellaFlags): UmbrellaPlan;
|
|
42
49
|
export interface UmbrellaResult {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Umbrella `agents sync` orchestration — "make this machine current".
|
|
3
3
|
*
|
|
4
|
-
* Bare `agents sync` fetches
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Bare `agents sync` fetches the config repos then reconciles them into every
|
|
5
|
+
* installed agent's version home. Secrets and sessions are opt-in stages
|
|
6
|
+
* (`--secrets` / `--sessions`) — see `planUmbrellaStages` for why they're off by
|
|
7
|
+
* default. Each stage is an existing exported library function; this module only
|
|
8
|
+
* sequences them and decides — from the flags — which stages run. The planner is
|
|
9
|
+
* pure so the flag matrix is unit-tested without any I/O.
|
|
9
10
|
*
|
|
10
11
|
* Stage backends:
|
|
11
12
|
* repos -> git pull of ~/.agents + enabled ~/.agents-* extras (pullRepo)
|
|
@@ -20,11 +21,17 @@ import { getUserAgentsDir, getEnabledExtraRepos } from './state.js';
|
|
|
20
21
|
import { listRemoteBundles, pullBundle } from './secrets/sync.js';
|
|
21
22
|
/**
|
|
22
23
|
* Decide which stages run. Pure — no I/O. Semantics:
|
|
23
|
-
* bare (no flags) fetch
|
|
24
|
+
* bare (no flags) fetch repos, then reconcile
|
|
24
25
|
* --local reconcile only, no fetch
|
|
25
|
-
* --cloud fetch (
|
|
26
|
+
* --cloud fetch repos (or the selected subset), skip reconcile
|
|
26
27
|
* --repos/--secrets/... fetch only the selected types, then reconcile
|
|
27
28
|
* `--local` wins over everything; `--cloud` suppresses reconcile.
|
|
29
|
+
*
|
|
30
|
+
* Secrets and sessions are NOT part of the bare default — they are opt-in via
|
|
31
|
+
* `--secrets` / `--sessions`. Pulling every secret bundle onto the machine on a
|
|
32
|
+
* bare `agents sync` is more blast radius than the verb should carry by
|
|
33
|
+
* default, and session transcripts are queryable on demand (`agents sessions
|
|
34
|
+
* --host <machine>`) so they don't need eager mirroring.
|
|
28
35
|
*/
|
|
29
36
|
export function planUmbrellaStages(f) {
|
|
30
37
|
if (f.local) {
|
|
@@ -39,8 +46,9 @@ export function planUmbrellaStages(f) {
|
|
|
39
46
|
reconcile: !f.cloud,
|
|
40
47
|
};
|
|
41
48
|
}
|
|
42
|
-
// No per-type selector: bare =
|
|
43
|
-
|
|
49
|
+
// No per-type selector: bare = repos + reconcile; --cloud = repos, no
|
|
50
|
+
// reconcile. Secrets/sessions stay off unless explicitly selected above.
|
|
51
|
+
return { fetchRepos: true, fetchSecrets: false, fetchSessions: false, reconcile: !f.cloud };
|
|
44
52
|
}
|
|
45
53
|
/**
|
|
46
54
|
* Execute the planned stages in order: repos -> secrets -> sessions -> reconcile.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { AgentManager, AgentProcess, EffortLevel } from './agents.js';
|
|
2
|
+
import { type ForEachSpec, type ForEachTeammate } from '../workflows.js';
|
|
3
|
+
/**
|
|
4
|
+
* Parse a producer's stdout into the item list `expandForEach` fans out over.
|
|
5
|
+
*
|
|
6
|
+
* Two shapes are accepted (issue #343), tried in order:
|
|
7
|
+
* 1. A JSON array — `["a","b","c"]` (each element coerced to a trimmed string).
|
|
8
|
+
* 2. Newline-delimited — one item per line.
|
|
9
|
+
*
|
|
10
|
+
* Empty lines / entries are dropped so a trailing newline or a `[]` never
|
|
11
|
+
* fabricates a phantom teammate. Pure and deterministic — no I/O.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseProducedItems(stdout: string): string[];
|
|
14
|
+
export interface ProduceItemsOptions {
|
|
15
|
+
/** Working directory for the producer command. */
|
|
16
|
+
cwd?: string | null;
|
|
17
|
+
/** Environment for the producer command (defaults to the current process env). */
|
|
18
|
+
env?: NodeJS.ProcessEnv;
|
|
19
|
+
/** Kill the producer after this many ms (default 120_000). */
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve an `itemsRef` (`${step}`-style reference) to a prior step's produced
|
|
23
|
+
* list. When a spec carries `itemsRef` and this resolver returns a list, the
|
|
24
|
+
* producer command is skipped entirely.
|
|
25
|
+
*/
|
|
26
|
+
resolveItemsRef?: (ref: string) => string[] | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve a `for_each` spec's item list at runtime (issue #343): either by
|
|
30
|
+
* running its `produce:` shell command and parsing stdout, or by resolving an
|
|
31
|
+
* `itemsRef` to a prior step's list. The resulting items feed `expandForEach` /
|
|
32
|
+
* `runForEach`.
|
|
33
|
+
*
|
|
34
|
+
* `itemsRef` wins when a resolver is supplied and returns a list; otherwise the
|
|
35
|
+
* `produce` command runs. A spec with neither a resolvable ref nor a produce
|
|
36
|
+
* command is a hard error — there is nothing to fan out over.
|
|
37
|
+
*/
|
|
38
|
+
export declare function produceItems(spec: ForEachSpec, opts?: ProduceItemsOptions): Promise<string[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Evaluate a verify panel's `keep_if` gate against its boolean votes (issue
|
|
41
|
+
* #343). A `true` vote is a skeptic confirming the finding should be kept.
|
|
42
|
+
*
|
|
43
|
+
* - `all` — every skeptic must vote keep.
|
|
44
|
+
* - `any` — at least one skeptic votes keep.
|
|
45
|
+
* - `majority` — strictly more than half vote keep (a tie does NOT pass).
|
|
46
|
+
*
|
|
47
|
+
* An empty panel returns false: with no votes there is nothing affirming the
|
|
48
|
+
* item, so the conservative gate drops it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function evaluateKeepIf(votes: boolean[], keepIf: 'majority' | 'all' | 'any'): boolean;
|
|
51
|
+
/** Per-item verdict after tallying its verify panel. */
|
|
52
|
+
export interface ForEachItemVerdict {
|
|
53
|
+
/** The produced item this verdict is for. */
|
|
54
|
+
item: string;
|
|
55
|
+
/** Zero-based index in the (capped) produced list. */
|
|
56
|
+
itemIndex: number;
|
|
57
|
+
/** The stage teammate that handled this item. */
|
|
58
|
+
stageName: string;
|
|
59
|
+
/** Whether the item survives its `keep_if` gate (true when it has no panel). */
|
|
60
|
+
kept: boolean;
|
|
61
|
+
/** The votes that were tallied (empty when the item has no verify panel). */
|
|
62
|
+
votes: boolean[];
|
|
63
|
+
/** The gate applied (undefined when the item has no verify panel). */
|
|
64
|
+
keepIf?: 'majority' | 'all' | 'any';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Tally the verify panels of an expanded `for_each` and gate each item (issue
|
|
68
|
+
* #343). Groups verify teammates by the stage teammate they depend on, reads a
|
|
69
|
+
* boolean vote per verify teammate via `readVote`, and applies `keep_if`.
|
|
70
|
+
*
|
|
71
|
+
* An item with no verify panel is kept unconditionally (there is no gate).
|
|
72
|
+
* Pure and deterministic: `readVote` is the only place runtime state enters, so
|
|
73
|
+
* this is unit-testable with a synthetic vote reader.
|
|
74
|
+
*/
|
|
75
|
+
export declare function tallyForEach(teammates: ForEachTeammate[], readVote: (verify: ForEachTeammate) => boolean): ForEachItemVerdict[];
|
|
76
|
+
export interface RunForEachOptions {
|
|
77
|
+
/** Working directory for the spawned teammates. */
|
|
78
|
+
cwd?: string | null;
|
|
79
|
+
/**
|
|
80
|
+
* Name of the producer teammate the stage teammates should depend on. When
|
|
81
|
+
* set, each stage runs `--after` the producer so it can't start before the
|
|
82
|
+
* list is available.
|
|
83
|
+
*/
|
|
84
|
+
producerName?: string;
|
|
85
|
+
/** Default effort for spawned teammates (per-item overrides live in the spec). */
|
|
86
|
+
effort?: EffortLevel;
|
|
87
|
+
/** Concurrency cap for the wave; falls back to the spec's `concurrency`. */
|
|
88
|
+
concurrency?: number;
|
|
89
|
+
}
|
|
90
|
+
export interface RunForEachResult {
|
|
91
|
+
/** The expanded descriptors (stage + verify), in spawn order. */
|
|
92
|
+
teammates: ForEachTeammate[];
|
|
93
|
+
/** The AgentProcess handles returned by `spawn`, aligned to `teammates`. */
|
|
94
|
+
spawned: AgentProcess[];
|
|
95
|
+
/** Items the producer emitted (pre-cap). */
|
|
96
|
+
producedCount: number;
|
|
97
|
+
/** Items actually fanned out (post-cap). */
|
|
98
|
+
usedCount: number;
|
|
99
|
+
/** How many items the runaway guard dropped. */
|
|
100
|
+
truncated: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Expand a `for_each` spec against a producer's output and stage every
|
|
104
|
+
* resulting teammate into the given team via the dynamic-add path.
|
|
105
|
+
*
|
|
106
|
+
* Returns the descriptors and the spawned handles so a caller can drive the
|
|
107
|
+
* supervisor and later gather results (e.g. to evaluate a `verify` panel's
|
|
108
|
+
* `keep_if` gate — that vote-counting lives downstream, not here).
|
|
109
|
+
*/
|
|
110
|
+
export declare function runForEach(mgr: AgentManager, teamName: string, spec: ForEachSpec, items: string[], opts?: RunForEachOptions): Promise<RunForEachResult>;
|