@phnx-labs/agents-cli 1.20.92 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +385 -0
- package/README.md +3 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/beta.js +3 -5
- package/dist/commands/doctor.js +23 -0
- package/dist/commands/events.js +91 -1
- package/dist/commands/exec.js +13 -0
- package/dist/commands/mailboxes.js +39 -1
- package/dist/commands/message.js +12 -1
- package/dist/commands/packages.js +14 -5
- package/dist/commands/projects.d.ts +38 -0
- package/dist/commands/projects.js +355 -48
- package/dist/commands/routines.js +27 -1
- package/dist/commands/rules.js +6 -0
- package/dist/commands/secrets.d.ts +17 -0
- package/dist/commands/secrets.js +198 -7
- package/dist/commands/send.d.ts +14 -12
- package/dist/commands/send.js +105 -35
- package/dist/commands/sessions.d.ts +17 -0
- package/dist/commands/sessions.js +16 -8
- package/dist/commands/setup-browser.js +5 -1
- package/dist/commands/setup-fleet.js +5 -0
- package/dist/commands/setup-preferences.d.ts +53 -0
- package/dist/commands/setup-preferences.js +142 -0
- package/dist/commands/setup.js +8 -9
- package/dist/commands/ssh.js +220 -10
- package/dist/commands/sync.d.ts +2 -2
- package/dist/commands/sync.js +13 -12
- package/dist/commands/view.js +4 -0
- package/dist/commands/watchdog.js +26 -3
- package/dist/index.js +38 -6
- package/dist/lib/activity.d.ts +15 -0
- package/dist/lib/activity.js +26 -2
- package/dist/lib/auto-dispatch.d.ts +6 -1
- package/dist/lib/auto-dispatch.js +7 -2
- package/dist/lib/beta.d.ts +1 -1
- package/dist/lib/beta.js +1 -1
- package/dist/lib/channels/send.d.ts +83 -0
- package/dist/lib/channels/send.js +112 -0
- package/dist/lib/daemon.d.ts +13 -0
- package/dist/lib/daemon.js +80 -60
- package/dist/lib/device-config.d.ts +82 -0
- package/dist/lib/device-config.js +296 -0
- package/dist/lib/event-provenance.d.ts +19 -0
- package/dist/lib/event-provenance.js +48 -0
- package/dist/lib/events-ingest.d.ts +46 -0
- package/dist/lib/events-ingest.js +182 -0
- package/dist/lib/events.d.ts +17 -5
- package/dist/lib/events.js +59 -56
- package/dist/lib/feed.d.ts +1 -1
- package/dist/lib/feed.js +2 -0
- package/dist/lib/git.d.ts +13 -2
- package/dist/lib/git.js +38 -6
- package/dist/lib/linear-project-counts.d.ts +129 -0
- package/dist/lib/linear-project-counts.js +206 -0
- package/dist/lib/linear-projects.d.ts +62 -0
- package/dist/lib/linear-projects.js +144 -0
- package/dist/lib/mailbox-gc.js +30 -7
- package/dist/lib/mailbox.d.ts +14 -1
- package/dist/lib/mailbox.js +35 -3
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +5 -21
- package/dist/lib/menubar/install-menubar.js +35 -44
- package/dist/lib/menubar/notify-desktop.d.ts +17 -2
- package/dist/lib/menubar/notify-desktop.js +8 -2
- package/dist/lib/migrate.js +1 -1
- package/dist/lib/models.js +21 -11
- package/dist/lib/overdue.d.ts +1 -1
- package/dist/lib/overdue.js +54 -19
- package/dist/lib/project-import.d.ts +96 -0
- package/dist/lib/project-import.js +171 -0
- package/dist/lib/project-probe.d.ts +75 -0
- package/dist/lib/project-probe.js +160 -0
- package/dist/lib/project-resources.d.ts +8 -0
- package/dist/lib/project-resources.js +31 -3
- package/dist/lib/project-status.d.ts +32 -1
- package/dist/lib/project-status.js +82 -1
- package/dist/lib/projects.d.ts +6 -0
- package/dist/lib/projects.js +12 -0
- package/dist/lib/remote-agents-json.d.ts +14 -1
- package/dist/lib/remote-agents-json.js +21 -3
- package/dist/lib/routine-notify.d.ts +11 -0
- package/dist/lib/routine-notify.js +22 -0
- package/dist/lib/routines.d.ts +53 -0
- package/dist/lib/routines.js +105 -4
- package/dist/lib/rules/run-sync.d.ts +18 -0
- package/dist/lib/rules/run-sync.js +92 -0
- package/dist/lib/run-notify.js +3 -0
- package/dist/lib/scheduler.js +12 -2
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/audit.d.ts +1 -1
- package/dist/lib/secrets/audit.js +53 -10
- package/dist/lib/secrets/index.d.ts +4 -5
- package/dist/lib/secrets/index.js +4 -5
- package/dist/lib/secrets/list-filter.d.ts +20 -5
- package/dist/lib/secrets/list-filter.js +22 -6
- package/dist/lib/secrets/usage-db.d.ts +106 -0
- package/dist/lib/secrets/usage-db.js +236 -0
- package/dist/lib/session/active.d.ts +16 -2
- package/dist/lib/session/active.js +8 -4
- package/dist/lib/session/bundle.js +5 -1
- package/dist/lib/session/remote-active.d.ts +5 -1
- package/dist/lib/session/remote-active.js +4 -1
- package/dist/lib/session/remote-list.d.ts +1 -15
- package/dist/lib/session/remote-list.js +22 -111
- package/dist/lib/session/sync/config.d.ts +13 -5
- package/dist/lib/session/sync/config.js +21 -13
- package/dist/lib/sqlite.js +28 -1
- package/dist/lib/startup/command-registry.d.ts +0 -2
- package/dist/lib/startup/command-registry.js +1 -5
- package/dist/lib/state.d.ts +19 -2
- package/dist/lib/state.js +99 -10
- package/dist/lib/sync-umbrella.d.ts +11 -23
- package/dist/lib/sync-umbrella.js +14 -45
- package/dist/lib/teams/agents.d.ts +35 -3
- package/dist/lib/teams/agents.js +52 -8
- package/dist/lib/teams/scheduler.d.ts +29 -2
- package/dist/lib/teams/scheduler.js +60 -16
- package/dist/lib/types.d.ts +22 -5
- package/dist/lib/versions.d.ts +6 -0
- package/dist/lib/versions.js +6 -4
- package/package.json +1 -2
- package/dist/commands/drive.d.ts +0 -10
- package/dist/commands/drive.js +0 -183
- package/dist/commands/hq.d.ts +0 -2
- package/dist/commands/hq.js +0 -58
- package/dist/commands/sessions-sync.d.ts +0 -17
- package/dist/commands/sessions-sync.js +0 -135
- package/dist/commands/sync-provision.d.ts +0 -23
- package/dist/commands/sync-provision.js +0 -107
- package/dist/lib/drive-sync.d.ts +0 -45
- package/dist/lib/drive-sync.js +0 -238
- package/dist/lib/hq/floor.d.ts +0 -87
- package/dist/lib/hq/floor.js +0 -243
- package/dist/lib/session/sync/crdt.d.ts +0 -44
- package/dist/lib/session/sync/crdt.js +0 -119
- package/dist/lib/session/sync/manifest.d.ts +0 -62
- package/dist/lib/session/sync/manifest.js +0 -100
- package/dist/lib/session/sync/provision.d.ts +0 -49
- package/dist/lib/session/sync/provision.js +0 -91
- package/dist/lib/session/sync/r2.d.ts +0 -32
- package/dist/lib/session/sync/r2.js +0 -121
- package/dist/lib/session/sync/sync.d.ts +0 -106
- package/dist/lib/session/sync/sync.js +0 -374
package/dist/lib/sqlite.js
CHANGED
|
@@ -14,11 +14,38 @@
|
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
15
|
const isBun = typeof globalThis.Bun !== 'undefined';
|
|
16
16
|
const require = createRequire(import.meta.url);
|
|
17
|
+
// node:sqlite emits a process-level ExperimentalWarning the first time it loads.
|
|
18
|
+
// The packaged CLI launches Node with --no-warnings=ExperimentalWarning, but a
|
|
19
|
+
// direct `node dist/...` run (and vitest's subprocesses) does not, so the warning
|
|
20
|
+
// would leak onto stderr and break any command whose --json output is asserted to
|
|
21
|
+
// be clean. Suppress only that single warning for the duration of the load; every
|
|
22
|
+
// other warning passes through untouched.
|
|
23
|
+
function loadNodeSqlite() {
|
|
24
|
+
const original = process.emitWarning;
|
|
25
|
+
const filtered = ((warning, ...rest) => {
|
|
26
|
+
const name = warning instanceof Error
|
|
27
|
+
? warning.name
|
|
28
|
+
: typeof rest[0] === 'string'
|
|
29
|
+
? rest[0]
|
|
30
|
+
: rest[0]?.type;
|
|
31
|
+
const message = warning instanceof Error ? warning.message : warning;
|
|
32
|
+
if (name === 'ExperimentalWarning' && /SQLite/i.test(String(message ?? '')))
|
|
33
|
+
return;
|
|
34
|
+
original.call(process, warning, ...rest);
|
|
35
|
+
});
|
|
36
|
+
process.emitWarning = filtered;
|
|
37
|
+
try {
|
|
38
|
+
return require('node:sqlite');
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
process.emitWarning = original;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
17
44
|
// Keep Node on createRequire() so Vitest doesn't try to prebundle the built-in
|
|
18
45
|
// sqlite module as a userland package during test collection.
|
|
19
46
|
const sqliteMod = isBun
|
|
20
47
|
? await import('bun:sqlite')
|
|
21
|
-
:
|
|
48
|
+
: loadNodeSqlite();
|
|
22
49
|
// bun:sqlite exports `Database`; node:sqlite exports `DatabaseSync`.
|
|
23
50
|
const NativeDatabase = sqliteMod.Database
|
|
24
51
|
?? sqliteMod.DatabaseSync;
|
|
@@ -67,7 +67,6 @@ export declare const loadBeta: ModuleLoader;
|
|
|
67
67
|
export declare const loadSync: ModuleLoader;
|
|
68
68
|
export declare const loadLock: ModuleLoader;
|
|
69
69
|
export declare const loadRefreshRules: ModuleLoader;
|
|
70
|
-
export declare const loadDrive: ModuleLoader;
|
|
71
70
|
export declare const loadFactory: ModuleLoader;
|
|
72
71
|
export declare const loadUsage: ModuleLoader;
|
|
73
72
|
export declare const loadCost: ModuleLoader;
|
|
@@ -96,7 +95,6 @@ export declare const loadTeams: ModuleLoader;
|
|
|
96
95
|
export declare const loadCloud: ModuleLoader;
|
|
97
96
|
export declare const loadMessage: ModuleLoader;
|
|
98
97
|
export declare const loadSend: ModuleLoader;
|
|
99
|
-
export declare const loadHq: ModuleLoader;
|
|
100
98
|
export declare const loadFeed: ModuleLoader;
|
|
101
99
|
export declare const loadActivity: ModuleLoader;
|
|
102
100
|
export declare const loadMailboxes: ModuleLoader;
|
|
@@ -45,7 +45,6 @@ export const loadBeta = async () => (await import('../../commands/beta.js')).reg
|
|
|
45
45
|
export const loadSync = async () => (await import('../../commands/sync.js')).registerSyncCommand;
|
|
46
46
|
export const loadLock = async () => (await import('../../commands/lock.js')).registerLockCommand;
|
|
47
47
|
export const loadRefreshRules = async () => (await import('../../commands/refresh-rules.js')).registerRefreshRulesCommand;
|
|
48
|
-
export const loadDrive = async () => (await import('../../commands/drive.js')).registerDriveCommands;
|
|
49
48
|
export const loadFactory = async () => (await import('../../commands/factory.js')).registerFactoryCommands;
|
|
50
49
|
export const loadUsage = async () => (await import('../../commands/usage.js')).registerUsageCommand;
|
|
51
50
|
export const loadCost = async () => (await import('../../commands/cost.js')).registerCostCommand;
|
|
@@ -74,7 +73,6 @@ export const loadTeams = async () => (await import('../../commands/teams.js')).r
|
|
|
74
73
|
export const loadCloud = async () => (await import('../../commands/cloud.js')).registerCloudCommands;
|
|
75
74
|
export const loadMessage = async () => (await import('../../commands/message.js')).registerMessageCommand;
|
|
76
75
|
export const loadSend = async () => (await import('../../commands/send.js')).registerSendCommand;
|
|
77
|
-
export const loadHq = async () => (await import('../../commands/hq.js')).registerHqCommand;
|
|
78
76
|
export const loadFeed = async () => (await import('../../commands/feed.js')).registerFeedCommand;
|
|
79
77
|
export const loadActivity = async () => (await import('../../commands/activity.js')).registerActivityCommand;
|
|
80
78
|
export const loadMailboxes = async () => (await import('../../commands/mailboxes.js')).registerMailboxesCommand;
|
|
@@ -90,7 +88,7 @@ export const loadFunnel = async () => (await import('../../commands/funnel.js'))
|
|
|
90
88
|
* inherit the root's custom help formatter rather than getting the per-command
|
|
91
89
|
* recursive pass. Keeping that ordering preserves their `--help` output exactly.
|
|
92
90
|
*/
|
|
93
|
-
export const LAZY_COMMAND_NAMES = new Set(['sessions', 'teams', 'cloud', 'message', '
|
|
91
|
+
export const LAZY_COMMAND_NAMES = new Set(['sessions', 'teams', 'cloud', 'message', 'serve']);
|
|
94
92
|
/**
|
|
95
93
|
* User-typed top-level command name -> ordered list of module loaders to run.
|
|
96
94
|
*
|
|
@@ -161,7 +159,6 @@ export const COMMAND_LOADERS = {
|
|
|
161
159
|
sync: [loadSync],
|
|
162
160
|
lock: [loadLock],
|
|
163
161
|
'refresh-rules': [loadRefreshRules],
|
|
164
|
-
drive: [loadDrive],
|
|
165
162
|
factory: [loadFactory],
|
|
166
163
|
usage: [loadUsage],
|
|
167
164
|
cost: [loadCost],
|
|
@@ -199,7 +196,6 @@ export const COMMAND_LOADERS = {
|
|
|
199
196
|
message: [loadMessage],
|
|
200
197
|
send: [loadSend],
|
|
201
198
|
notify: [loadSend],
|
|
202
|
-
hq: [loadHq],
|
|
203
199
|
feed: [loadFeed],
|
|
204
200
|
activity: [loadActivity],
|
|
205
201
|
mailboxes: [loadMailboxes],
|
package/dist/lib/state.d.ts
CHANGED
|
@@ -23,6 +23,13 @@
|
|
|
23
23
|
* and leaves a back-compat symlink at the old path.
|
|
24
24
|
*/
|
|
25
25
|
import type { Meta } from './types.js';
|
|
26
|
+
/**
|
|
27
|
+
* Header prepended to every agents.yaml the CLI writes (central and per-device
|
|
28
|
+
* docs). Carries the yaml-language-server schema hint so editors validate the
|
|
29
|
+
* file against `schema/agents-yaml.schema.json`. Exported so
|
|
30
|
+
* `lib/device-config.ts` writes a sibling device's doc with the same header.
|
|
31
|
+
*/
|
|
32
|
+
export declare const META_HEADER = "# agents-cli metadata\n# Auto-generated - do not edit manually\n# https://github.com/phnx-labs/agents-cli\n# yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/apps/cli/schema/agents-yaml.schema.json\n\n";
|
|
26
33
|
/** Root of the system data directory (~/.agents/.system/). */
|
|
27
34
|
export declare function getAgentsDir(): string;
|
|
28
35
|
/** Root of the system data directory (~/.agents/.system/). */
|
|
@@ -98,6 +105,18 @@ export declare function getUserSubagentsDir(): string;
|
|
|
98
105
|
export declare function getSystemWorkflowsDir(): string;
|
|
99
106
|
export declare function getUserWorkflowsDir(): string;
|
|
100
107
|
export declare function getUserSecretsDir(): string;
|
|
108
|
+
/**
|
|
109
|
+
* Path to the secrets usage read-model database (~/.agents/secrets/secrets.db).
|
|
110
|
+
* Read at CALL time so a test can redirect it to a temp file via
|
|
111
|
+
* AGENTS_SECRETS_DB without racing the module-load capture of USER_SECRETS_DIR —
|
|
112
|
+
* mirrors the AGENTS_EVENTS_PATH / AGENTS_DEVICES_DIR escape hatches. Holds only
|
|
113
|
+
* value-free usage telemetry (which bundle was created/imported/exported/viewed/
|
|
114
|
+
* accessed/unlocked, when, by whom), never a secret value. It is a derived index
|
|
115
|
+
* fed FROM the emitSecretAudit chokepoint alongside the append-only
|
|
116
|
+
* ~/.agents/events.jsonl audit log — the same way sessions.db indexes session
|
|
117
|
+
* metadata off the real session flow — not a second write path.
|
|
118
|
+
*/
|
|
119
|
+
export declare function getSecretsDbPath(): string;
|
|
101
120
|
export declare function getUserPromptcutsPath(): string;
|
|
102
121
|
/** Canonical home anchor (HOME env override or os.homedir()). */
|
|
103
122
|
export declare function getHomeDir(): string;
|
|
@@ -182,8 +201,6 @@ export declare function getSystemPluginsDir(): string;
|
|
|
182
201
|
export declare function getExtraPluginsDir(alias: string): string;
|
|
183
202
|
/** Path to a project-scoped plugins directory (<project>/.agents/plugins/), or null when none. */
|
|
184
203
|
export declare function getProjectPluginsDir(cwd?: string): string | null;
|
|
185
|
-
/** Path to synced remote session data (~/.agents/.cache/drive/). */
|
|
186
|
-
export declare function getDriveDir(): string;
|
|
187
204
|
/** Path to soft-deleted resources (~/.agents/.history/trash/). */
|
|
188
205
|
export declare function getTrashDir(): string;
|
|
189
206
|
/** Path to local session indexer storage (~/.agents/.history/sessions/). */
|
package/dist/lib/state.js
CHANGED
|
@@ -26,7 +26,9 @@ import * as fs from 'fs';
|
|
|
26
26
|
import * as path from 'path';
|
|
27
27
|
import * as os from 'os';
|
|
28
28
|
import * as yaml from 'yaml';
|
|
29
|
+
import { execFileSync } from 'child_process';
|
|
29
30
|
import { ensureLockTarget, atomicWriteFileSync, withFileLock } from './fs-atomic.js';
|
|
31
|
+
import { DEFAULT_SYSTEM_REPO, systemRepoSlug } from './types.js';
|
|
30
32
|
import { machineId } from './machine-id.js';
|
|
31
33
|
const HOME = process.env.HOME ?? os.homedir();
|
|
32
34
|
/**
|
|
@@ -116,7 +118,6 @@ const PACKAGES_DIR = path.join(CACHE_DIR, 'packages');
|
|
|
116
118
|
// They live at the user-root so they're git-tracked as source of truth.
|
|
117
119
|
const PLUGINS_DIR = path.join(USER_AGENTS_DIR, 'plugins');
|
|
118
120
|
const CLOUD_DIR = path.join(CACHE_DIR, 'cloud');
|
|
119
|
-
const DRIVE_DIR = path.join(CACHE_DIR, 'drive');
|
|
120
121
|
const TERMINALS_DIR = path.join(CACHE_DIR, 'terminals');
|
|
121
122
|
const LOGS_DIR = path.join(CACHE_DIR, 'logs');
|
|
122
123
|
/** Disposable performance samples (~/.agents/.cache/perf/) — safe to wipe. */
|
|
@@ -144,9 +145,16 @@ const USER_SUBAGENTS_DIR = path.join(USER_AGENTS_DIR, 'subagents');
|
|
|
144
145
|
const USER_WORKFLOWS_DIR = path.join(USER_AGENTS_DIR, 'workflows');
|
|
145
146
|
const USER_SECRETS_DIR = path.join(USER_AGENTS_DIR, 'secrets');
|
|
146
147
|
const USER_PROMPTCUTS_FILE = path.join(USER_AGENTS_DIR, 'hooks', 'promptcuts.yaml');
|
|
147
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Header prepended to every agents.yaml the CLI writes (central and per-device
|
|
150
|
+
* docs). Carries the yaml-language-server schema hint so editors validate the
|
|
151
|
+
* file against `schema/agents-yaml.schema.json`. Exported so
|
|
152
|
+
* `lib/device-config.ts` writes a sibling device's doc with the same header.
|
|
153
|
+
*/
|
|
154
|
+
export const META_HEADER = `# agents-cli metadata
|
|
148
155
|
# Auto-generated - do not edit manually
|
|
149
156
|
# https://github.com/phnx-labs/agents-cli
|
|
157
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/apps/cli/schema/agents-yaml.schema.json
|
|
150
158
|
|
|
151
159
|
`;
|
|
152
160
|
// ─── Root getters ─────────────────────────────────────────────────────────────
|
|
@@ -190,13 +198,69 @@ export function getOptionalUserAgentsDir() {
|
|
|
190
198
|
}
|
|
191
199
|
return USER_AGENTS_DIR;
|
|
192
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Origin `owner/repo` slug (lowercased, `.git` stripped) of a git checkout, or
|
|
203
|
+
* null when `dir` isn't a git repo / has no origin. Extracts the slug from any
|
|
204
|
+
* remote URL form: `git@host:owner/repo.git`, `https://host/owner/repo.git`,
|
|
205
|
+
* `ssh://git@host/owner/repo`. Sync (mirrors readGitConfigUser in git.ts) so it
|
|
206
|
+
* can be used from the synchronous getProjectAgentsDir walk.
|
|
207
|
+
*/
|
|
208
|
+
function gitOriginSlug(dir) {
|
|
209
|
+
let url;
|
|
210
|
+
try {
|
|
211
|
+
url = execFileSync('git', ['-C', dir, 'config', '--get', 'remote.origin.url'], {
|
|
212
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
213
|
+
}).toString().trim();
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
if (!url)
|
|
219
|
+
return null;
|
|
220
|
+
const m = url.replace(/\.git$/i, '').match(/[:/]([^/:]+\/[^/]+)$/);
|
|
221
|
+
return m ? m[1].toLowerCase() : null;
|
|
222
|
+
}
|
|
223
|
+
/** Slugs of the user + system DotAgents repos — a checkout of any of these is not a project layer. */
|
|
224
|
+
function canonicalDotAgentsRepoSlugs() {
|
|
225
|
+
const slugs = new Set([systemRepoSlug(DEFAULT_SYSTEM_REPO).toLowerCase()]);
|
|
226
|
+
for (const dir of [USER_AGENTS_DIR, SYSTEM_AGENTS_DIR]) {
|
|
227
|
+
const slug = gitOriginSlug(dir);
|
|
228
|
+
if (slug)
|
|
229
|
+
slugs.add(slug);
|
|
230
|
+
}
|
|
231
|
+
return slugs;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* True when `agentsPath` is itself a git checkout of the user's or system's
|
|
235
|
+
* DotAgents repo — i.e. a *clone* of the very repo whose rules already load as
|
|
236
|
+
* the user/system layer (e.g. `git clone …/.agents.git ~/src/github.com/<you>/.agents`).
|
|
237
|
+
* Such a clone must NOT also be treated as a *project* layer: because project
|
|
238
|
+
* outranks user, a stale clone would silently shadow the live user rules by
|
|
239
|
+
* filename and plant a compiled AGENTS.md in an ancestor dir (RUSH-2037).
|
|
240
|
+
*
|
|
241
|
+
* A legitimate project layer is a plain subdirectory of a project and is never
|
|
242
|
+
* itself a git-repo root, so the cheap `.git` gate skips the (git-spawning)
|
|
243
|
+
* origin comparison for the common case — only a `.agents/` that is its own
|
|
244
|
+
* checkout pays it, and even then it's kept unless its origin matches the
|
|
245
|
+
* user/system DotAgents repo (an unrelated repo checked out at `.agents/`,
|
|
246
|
+
* or a project's own versioned `.agents/`, stays a valid project layer).
|
|
247
|
+
*/
|
|
248
|
+
function isUserOrSystemRepoCheckout(agentsPath) {
|
|
249
|
+
if (!fs.existsSync(path.join(agentsPath, '.git')))
|
|
250
|
+
return false;
|
|
251
|
+
const origin = gitOriginSlug(agentsPath);
|
|
252
|
+
if (!origin)
|
|
253
|
+
return false;
|
|
254
|
+
return canonicalDotAgentsRepoSlugs().has(origin);
|
|
255
|
+
}
|
|
193
256
|
/** Walk up from startPath to find a project-scoped .agents/ directory (skipping both roots). */
|
|
194
257
|
export function getProjectAgentsDir(startPath = process.cwd()) {
|
|
195
258
|
let dir = path.resolve(startPath);
|
|
196
259
|
while (true) {
|
|
197
260
|
const agentsPath = path.join(dir, '.agents');
|
|
198
261
|
if (fs.existsSync(agentsPath) && fs.statSync(agentsPath).isDirectory()) {
|
|
199
|
-
if (!isSamePath(agentsPath, SYSTEM_AGENTS_DIR) && !isSamePath(agentsPath, USER_AGENTS_DIR)
|
|
262
|
+
if (!isSamePath(agentsPath, SYSTEM_AGENTS_DIR) && !isSamePath(agentsPath, USER_AGENTS_DIR)
|
|
263
|
+
&& !isUserOrSystemRepoCheckout(agentsPath)) {
|
|
200
264
|
return agentsPath;
|
|
201
265
|
}
|
|
202
266
|
}
|
|
@@ -299,6 +363,20 @@ export function getUserSubagentsDir() { return USER_SUBAGENTS_DIR; }
|
|
|
299
363
|
export function getSystemWorkflowsDir() { return SYSTEM_WORKFLOWS_DIR; }
|
|
300
364
|
export function getUserWorkflowsDir() { return USER_WORKFLOWS_DIR; }
|
|
301
365
|
export function getUserSecretsDir() { return USER_SECRETS_DIR; }
|
|
366
|
+
/**
|
|
367
|
+
* Path to the secrets usage read-model database (~/.agents/secrets/secrets.db).
|
|
368
|
+
* Read at CALL time so a test can redirect it to a temp file via
|
|
369
|
+
* AGENTS_SECRETS_DB without racing the module-load capture of USER_SECRETS_DIR —
|
|
370
|
+
* mirrors the AGENTS_EVENTS_PATH / AGENTS_DEVICES_DIR escape hatches. Holds only
|
|
371
|
+
* value-free usage telemetry (which bundle was created/imported/exported/viewed/
|
|
372
|
+
* accessed/unlocked, when, by whom), never a secret value. It is a derived index
|
|
373
|
+
* fed FROM the emitSecretAudit chokepoint alongside the append-only
|
|
374
|
+
* ~/.agents/events.jsonl audit log — the same way sessions.db indexes session
|
|
375
|
+
* metadata off the real session flow — not a second write path.
|
|
376
|
+
*/
|
|
377
|
+
export function getSecretsDbPath() {
|
|
378
|
+
return process.env.AGENTS_SECRETS_DB ?? path.join(USER_SECRETS_DIR, 'secrets.db');
|
|
379
|
+
}
|
|
302
380
|
export function getUserPromptcutsPath() { return USER_PROMPTCUTS_FILE; }
|
|
303
381
|
// ─── User operational path getters ────────────────────────────────────────────
|
|
304
382
|
//
|
|
@@ -404,8 +482,6 @@ export function getProjectPluginsDir(cwd = process.cwd()) {
|
|
|
404
482
|
return null;
|
|
405
483
|
return path.join(projectAgentsDir, 'plugins');
|
|
406
484
|
}
|
|
407
|
-
/** Path to synced remote session data (~/.agents/.cache/drive/). */
|
|
408
|
-
export function getDriveDir() { return DRIVE_DIR; }
|
|
409
485
|
/** Path to soft-deleted resources (~/.agents/.history/trash/). */
|
|
410
486
|
export function getTrashDir() { return TRASH_DIR; }
|
|
411
487
|
/** Path to local session indexer storage (~/.agents/.history/sessions/). */
|
|
@@ -659,8 +735,9 @@ function writeIfChanged(filePath, content) {
|
|
|
659
735
|
/**
|
|
660
736
|
* Partition the in-memory Meta across three files by sync-domain:
|
|
661
737
|
* - central `~/.agents/agents.yaml` — portable, everything else
|
|
738
|
+
* (including the user-scope `config:` block)
|
|
662
739
|
* - device `~/.agents/devices/<machine>/agents.yaml` — `agents:` pins +
|
|
663
|
-
* `defaultBrowserProfile:` (
|
|
740
|
+
* `defaultBrowserProfile:` + device-scope `config:` (all per-device)
|
|
664
741
|
* - history `~/.agents/.history/version-resources.json` — `versions:` (machine-local)
|
|
665
742
|
* All callers funnel through writeMeta → here, so nothing else changes. Empty
|
|
666
743
|
* `agents:` / `versions:` are not written (no empty committed files).
|
|
@@ -722,7 +799,7 @@ function serializeCentral(central) {
|
|
|
722
799
|
return isEmpty ? META_HEADER : doc.toString({ collectionStyle: 'block' });
|
|
723
800
|
}
|
|
724
801
|
function writeMetaUnlocked(meta) {
|
|
725
|
-
const { agents, isolatedAgents, versions, defaultBrowserProfile, ...central } = meta;
|
|
802
|
+
const { agents, isolatedAgents, versions, defaultBrowserProfile, deviceConfig, ...central } = meta;
|
|
726
803
|
// Write the machine-local files FIRST, then strip central — so a crash mid-write
|
|
727
804
|
// never removes pins/versions from central before they're persisted elsewhere.
|
|
728
805
|
const devicePath = getDeviceMetaPath();
|
|
@@ -733,9 +810,13 @@ function writeMetaUnlocked(meta) {
|
|
|
733
810
|
// it does not have.
|
|
734
811
|
const hasIsolatedAgents = !!isolatedAgents && Object.keys(isolatedAgents).length > 0;
|
|
735
812
|
const hasDefaultBrowser = !!defaultBrowserProfile;
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
813
|
+
// Device-scope config (`maxAgents`, `schedulerEnabled`, …) is per-machine, so it
|
|
814
|
+
// rides the device doc under `config:` — never the central doc that syncs.
|
|
815
|
+
const hasDeviceConfig = !!deviceConfig && Object.keys(deviceConfig).length > 0;
|
|
816
|
+
if (hasAgents || hasIsolatedAgents || hasDefaultBrowser || hasDeviceConfig) {
|
|
817
|
+
// Device-local doc carries `agents:` pins, `defaultBrowserProfile:`, and the
|
|
818
|
+
// device-scope `config:` — all per-machine and must never land in central
|
|
819
|
+
// agents.yaml (which syncs).
|
|
739
820
|
const deviceDoc = {};
|
|
740
821
|
if (hasAgents)
|
|
741
822
|
deviceDoc.agents = agents;
|
|
@@ -743,6 +824,8 @@ function writeMetaUnlocked(meta) {
|
|
|
743
824
|
deviceDoc.isolatedAgents = isolatedAgents;
|
|
744
825
|
if (hasDefaultBrowser)
|
|
745
826
|
deviceDoc.defaultBrowserProfile = defaultBrowserProfile;
|
|
827
|
+
if (hasDeviceConfig)
|
|
828
|
+
deviceDoc.config = deviceConfig;
|
|
746
829
|
fs.mkdirSync(path.dirname(devicePath), { recursive: true });
|
|
747
830
|
writeIfChanged(devicePath, META_HEADER + yaml.stringify(deviceDoc));
|
|
748
831
|
}
|
|
@@ -768,6 +851,10 @@ function writeMetaUnlocked(meta) {
|
|
|
768
851
|
* still has pins)
|
|
769
852
|
* - `defaultBrowserProfile:` from the device file (device is the sole source;
|
|
770
853
|
* the field is stripped from central on write, so nothing to merge against)
|
|
854
|
+
* - `config:` from the device file into `deviceConfig` (device is the sole
|
|
855
|
+
* source for device-scope config, same routing as `defaultBrowserProfile`;
|
|
856
|
+
* the in-memory field is named `deviceConfig` so it can never collide with
|
|
857
|
+
* the user-scope `config:` central carries)
|
|
771
858
|
* - `versions:` from the history JSON (wholesale replace; falls back to
|
|
772
859
|
* whatever central carried when the history file doesn't exist yet)
|
|
773
860
|
*/
|
|
@@ -782,6 +869,8 @@ function overlayMachineLocal(meta) {
|
|
|
782
869
|
meta.isolatedAgents = { ...meta.isolatedAgents, ...dm.isolatedAgents };
|
|
783
870
|
if (dm?.defaultBrowserProfile)
|
|
784
871
|
meta.defaultBrowserProfile = dm.defaultBrowserProfile;
|
|
872
|
+
if (dm?.config)
|
|
873
|
+
meta.deviceConfig = dm.config;
|
|
785
874
|
}
|
|
786
875
|
catch { /* ignore malformed device file */ }
|
|
787
876
|
}
|
|
@@ -2,25 +2,23 @@
|
|
|
2
2
|
* Umbrella `agents sync` orchestration — "make this machine current".
|
|
3
3
|
*
|
|
4
4
|
* Bare `agents sync` fetches the config repos then reconciles them into every
|
|
5
|
-
* installed agent's version home. Secrets
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* installed agent's version home. Secrets are an opt-in stage (`--secrets`) —
|
|
6
|
+
* see `planUmbrellaStages` for why it's off by default. Each stage is an
|
|
7
|
+
* existing exported library function; this module only sequences them and
|
|
8
|
+
* decides — from the flags — which stages run. The planner is pure so the
|
|
9
|
+
* flag matrix is unit-tested without any I/O.
|
|
10
10
|
*
|
|
11
11
|
* Stage backends:
|
|
12
12
|
* repos -> git pull of ~/.agents + enabled ~/.agents-* extras (pullRepo)
|
|
13
13
|
* secrets -> listRemoteBundles + pullBundle (needs a passphrase; skipped
|
|
14
14
|
* cleanly when none is available — tokenized non-interactive auth
|
|
15
15
|
* arrives with `agents login`, #366/#367)
|
|
16
|
-
* sessions -> syncSessions(), gated by the session-sync beta opt-in + isSyncConfigured(), like the daemon
|
|
17
16
|
* reconcile-> refresh({ skipPrompts }) — re-materialize resources into homes
|
|
18
17
|
*/
|
|
19
|
-
/** The
|
|
18
|
+
/** The umbrella flags off `agents sync`. */
|
|
20
19
|
export interface UmbrellaFlags {
|
|
21
20
|
repos?: boolean;
|
|
22
21
|
secrets?: boolean;
|
|
23
|
-
sessions?: boolean;
|
|
24
22
|
cloud?: boolean;
|
|
25
23
|
local?: boolean;
|
|
26
24
|
}
|
|
@@ -28,7 +26,6 @@ export interface UmbrellaFlags {
|
|
|
28
26
|
export interface UmbrellaPlan {
|
|
29
27
|
fetchRepos: boolean;
|
|
30
28
|
fetchSecrets: boolean;
|
|
31
|
-
fetchSessions: boolean;
|
|
32
29
|
reconcile: boolean;
|
|
33
30
|
}
|
|
34
31
|
/**
|
|
@@ -36,14 +33,12 @@ export interface UmbrellaPlan {
|
|
|
36
33
|
* bare (no flags) fetch repos, then reconcile
|
|
37
34
|
* --local reconcile only, no fetch
|
|
38
35
|
* --cloud fetch repos (or the selected subset), skip reconcile
|
|
39
|
-
* --repos/--secrets
|
|
36
|
+
* --repos/--secrets fetch only the selected types, then reconcile
|
|
40
37
|
* `--local` wins over everything; `--cloud` suppresses reconcile.
|
|
41
38
|
*
|
|
42
|
-
* Secrets
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* default, and session transcripts are queryable on demand (`agents sessions
|
|
46
|
-
* --host <machine>`) so they don't need eager mirroring.
|
|
39
|
+
* Secrets are NOT part of the bare default — they are opt-in via `--secrets`.
|
|
40
|
+
* Pulling every secret bundle onto the machine on a bare `agents sync` is more
|
|
41
|
+
* blast radius than the verb should carry by default.
|
|
47
42
|
*/
|
|
48
43
|
export declare function planUmbrellaStages(f: UmbrellaFlags): UmbrellaPlan;
|
|
49
44
|
export interface UmbrellaResult {
|
|
@@ -58,13 +53,6 @@ export interface UmbrellaResult {
|
|
|
58
53
|
reason?: string;
|
|
59
54
|
errors: string[];
|
|
60
55
|
};
|
|
61
|
-
sessions?: {
|
|
62
|
-
ran: boolean;
|
|
63
|
-
pushed: number;
|
|
64
|
-
pulled: number;
|
|
65
|
-
merged: number;
|
|
66
|
-
error?: string;
|
|
67
|
-
};
|
|
68
56
|
devices?: {
|
|
69
57
|
synced: number;
|
|
70
58
|
pending: number;
|
|
@@ -82,7 +70,7 @@ export interface RunUmbrellaArgs {
|
|
|
82
70
|
passphrase?: string;
|
|
83
71
|
}
|
|
84
72
|
/**
|
|
85
|
-
* Execute the planned stages in order: repos -> secrets ->
|
|
73
|
+
* Execute the planned stages in order: repos -> secrets -> reconcile.
|
|
86
74
|
* A failure in one fetch stage is recorded and does not abort the others or the
|
|
87
75
|
* reconcile — `agents sync` should make as much current as it can in one pass.
|
|
88
76
|
*/
|
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
* Umbrella `agents sync` orchestration — "make this machine current".
|
|
3
3
|
*
|
|
4
4
|
* Bare `agents sync` fetches the config repos then reconciles them into every
|
|
5
|
-
* installed agent's version home. Secrets
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* installed agent's version home. Secrets are an opt-in stage (`--secrets`) —
|
|
6
|
+
* see `planUmbrellaStages` for why it's off by default. Each stage is an
|
|
7
|
+
* existing exported library function; this module only sequences them and
|
|
8
|
+
* decides — from the flags — which stages run. The planner is pure so the
|
|
9
|
+
* flag matrix is unit-tested without any I/O.
|
|
10
10
|
*
|
|
11
11
|
* Stage backends:
|
|
12
12
|
* repos -> git pull of ~/.agents + enabled ~/.agents-* extras (pullRepo)
|
|
13
13
|
* secrets -> listRemoteBundles + pullBundle (needs a passphrase; skipped
|
|
14
14
|
* cleanly when none is available — tokenized non-interactive auth
|
|
15
15
|
* arrives with `agents login`, #366/#367)
|
|
16
|
-
* sessions -> syncSessions(), gated by the session-sync beta opt-in + isSyncConfigured(), like the daemon
|
|
17
16
|
* reconcile-> refresh({ skipPrompts }) — re-materialize resources into homes
|
|
18
17
|
*/
|
|
19
18
|
import { pullRepo } from './git.js';
|
|
@@ -24,34 +23,31 @@ import { listRemoteBundles, pullBundle } from './secrets/sync.js';
|
|
|
24
23
|
* bare (no flags) fetch repos, then reconcile
|
|
25
24
|
* --local reconcile only, no fetch
|
|
26
25
|
* --cloud fetch repos (or the selected subset), skip reconcile
|
|
27
|
-
* --repos/--secrets
|
|
26
|
+
* --repos/--secrets fetch only the selected types, then reconcile
|
|
28
27
|
* `--local` wins over everything; `--cloud` suppresses reconcile.
|
|
29
28
|
*
|
|
30
|
-
* Secrets
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* default, and session transcripts are queryable on demand (`agents sessions
|
|
34
|
-
* --host <machine>`) so they don't need eager mirroring.
|
|
29
|
+
* Secrets are NOT part of the bare default — they are opt-in via `--secrets`.
|
|
30
|
+
* Pulling every secret bundle onto the machine on a bare `agents sync` is more
|
|
31
|
+
* blast radius than the verb should carry by default.
|
|
35
32
|
*/
|
|
36
33
|
export function planUmbrellaStages(f) {
|
|
37
34
|
if (f.local) {
|
|
38
|
-
return { fetchRepos: false, fetchSecrets: false,
|
|
35
|
+
return { fetchRepos: false, fetchSecrets: false, reconcile: true };
|
|
39
36
|
}
|
|
40
|
-
const anySelector = !!(f.repos || f.secrets
|
|
37
|
+
const anySelector = !!(f.repos || f.secrets);
|
|
41
38
|
if (anySelector) {
|
|
42
39
|
return {
|
|
43
40
|
fetchRepos: !!f.repos,
|
|
44
41
|
fetchSecrets: !!f.secrets,
|
|
45
|
-
fetchSessions: !!f.sessions,
|
|
46
42
|
reconcile: !f.cloud,
|
|
47
43
|
};
|
|
48
44
|
}
|
|
49
45
|
// No per-type selector: bare = repos + reconcile; --cloud = repos, no
|
|
50
|
-
// reconcile. Secrets
|
|
51
|
-
return { fetchRepos: true, fetchSecrets: false,
|
|
46
|
+
// reconcile. Secrets stay off unless explicitly selected above.
|
|
47
|
+
return { fetchRepos: true, fetchSecrets: false, reconcile: !f.cloud };
|
|
52
48
|
}
|
|
53
49
|
/**
|
|
54
|
-
* Execute the planned stages in order: repos -> secrets ->
|
|
50
|
+
* Execute the planned stages in order: repos -> secrets -> reconcile.
|
|
55
51
|
* A failure in one fetch stage is recorded and does not abort the others or the
|
|
56
52
|
* reconcile — `agents sync` should make as much current as it can in one pass.
|
|
57
53
|
*/
|
|
@@ -110,33 +106,6 @@ export async function runUmbrellaSync(args) {
|
|
|
110
106
|
result.secrets = { pulled, skipped: false, errors };
|
|
111
107
|
}
|
|
112
108
|
}
|
|
113
|
-
if (plan.fetchSessions) {
|
|
114
|
-
// Gate exactly like the daemon: without the `session-sync` beta opt-in (or
|
|
115
|
-
// with a missing r2.backups bundle) this is a clean no-op, not an error that
|
|
116
|
-
// fails the whole sync.
|
|
117
|
-
const { isBetaEnabled } = await import('./beta.js');
|
|
118
|
-
const { isSyncConfigured } = await import('./session/sync/config.js');
|
|
119
|
-
if (isBetaEnabled('session-sync') && isSyncConfigured()) {
|
|
120
|
-
const { syncSessions } = await import('./session/sync/sync.js');
|
|
121
|
-
// Record a failure here instead of letting it throw: like the fetchSecrets
|
|
122
|
-
// block above, a failure in one fetch stage must not abort the others or
|
|
123
|
-
// the reconcile below (see this function's doc comment). Without this,
|
|
124
|
-
// syncSessions() throwing — e.g. via the C1 containment reject on a
|
|
125
|
-
// malicious peer manifest — would skip `plan.reconcile` entirely.
|
|
126
|
-
try {
|
|
127
|
-
const r = await syncSessions();
|
|
128
|
-
result.sessions = { ran: true, pushed: r.pushed, pulled: r.pulled, merged: r.merged };
|
|
129
|
-
log(`sessions: pushed ${r.pushed}, pulled ${r.pulled}, merged ${r.merged}`);
|
|
130
|
-
}
|
|
131
|
-
catch (err) {
|
|
132
|
-
result.sessions = { ran: false, pushed: 0, pulled: 0, merged: 0, error: err.message };
|
|
133
|
-
log(`sessions: failed — ${err.message}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
result.sessions = { ran: false, pushed: 0, pulled: 0, merged: 0 };
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
109
|
if (plan.reconcile) {
|
|
141
110
|
const { refresh } = await import('./refresh.js');
|
|
142
111
|
await refresh({ skipPrompts: yes });
|
|
@@ -218,13 +218,30 @@ export declare class AgentProcess {
|
|
|
218
218
|
* Uses a per-wave batched snapshot (remotePollSnapshot) when the supervisor's
|
|
219
219
|
* one-ssh-per-host pre-pass populated it; otherwise falls back to its own
|
|
220
220
|
* round-trips so a bare `teams status`/`teams logs` is still correct.
|
|
221
|
+
*
|
|
222
|
+
* Only polls a teammate that is plausibly still RUNNING (RUSH-2118). Once a
|
|
223
|
+
* remote teammate reaches a terminal status, the poll that resolved it already
|
|
224
|
+
* mirrored the final log bytes and read the `.exit` sentinel in this SAME
|
|
225
|
+
* function (delta pulled before the exit check below) — the underlying process
|
|
226
|
+
* is gone and can never write more, so there is nothing left to fetch. Without
|
|
227
|
+
* this guard every finished remote teammate still cost one ssh round-trip on
|
|
228
|
+
* EVERY `--active`/`listAll` poll forever, which is what made `agents sessions
|
|
229
|
+
* --active --local` take ~4.3s on a box with 30 completed teammates.
|
|
221
230
|
*/
|
|
222
231
|
private syncRemoteMirror;
|
|
223
232
|
/** Reset the local stdout cursor for a newly truncated resume log. */
|
|
224
233
|
resetLogReadPosition(): number;
|
|
225
234
|
/** Restore the cursor when a resume transaction puts the prior log back. */
|
|
226
235
|
restoreLogReadPosition(position: number): void;
|
|
227
|
-
|
|
236
|
+
/**
|
|
237
|
+
* @param opts.skipRemote A `--local` caller (RUSH-2118): never dial a
|
|
238
|
+
* remote-host teammate, not even a still-RUNNING one — report its
|
|
239
|
+
* last-persisted meta.json state as-is. A local-only query is by definition
|
|
240
|
+
* this-machine-only, so it must not issue an ssh round-trip at all.
|
|
241
|
+
*/
|
|
242
|
+
readNewEvents(opts?: {
|
|
243
|
+
skipRemote?: boolean;
|
|
244
|
+
}): Promise<void>;
|
|
228
245
|
/**
|
|
229
246
|
* Truncate the local mirror to its trailing REMOTE_MIRROR_MAX_BYTES and reset
|
|
230
247
|
* lastReadPos to the new (smaller) size so the parser doesn't re-read the kept
|
|
@@ -244,7 +261,14 @@ export declare class AgentProcess {
|
|
|
244
261
|
saveMeta(): Promise<void>;
|
|
245
262
|
static loadFromDisk(agentId: string, baseDir?: string | null): Promise<AgentProcess | null>;
|
|
246
263
|
isProcessAlive(): boolean;
|
|
247
|
-
|
|
264
|
+
/**
|
|
265
|
+
* @param opts.skipRemote A `--local` caller (RUSH-2118): a distributed
|
|
266
|
+
* teammate is never dialed — its in-memory state (already loaded from
|
|
267
|
+
* meta.json) stands as-is, no ssh, no re-save.
|
|
268
|
+
*/
|
|
269
|
+
updateStatusFromProcess(opts?: {
|
|
270
|
+
skipRemote?: boolean;
|
|
271
|
+
}): Promise<void>;
|
|
248
272
|
/**
|
|
249
273
|
* Recover the teammate's exit status after its process is gone.
|
|
250
274
|
*
|
|
@@ -298,8 +322,16 @@ export declare class AgentManager {
|
|
|
298
322
|
private defaultMode;
|
|
299
323
|
private initPromise;
|
|
300
324
|
private cloudDispatcher;
|
|
325
|
+
/**
|
|
326
|
+
* A `--local` caller (RUSH-2118): every poll this manager issues skips the
|
|
327
|
+
* ssh round-trip for a distributed (remote-host) teammate, reporting its
|
|
328
|
+
* last-persisted meta.json state instead. Set once at construction so the
|
|
329
|
+
* INITIAL load in doInitialize()/loadExistingAgents() — which polls every
|
|
330
|
+
* teammate before listRunning()/listAll() ever run — honors it too.
|
|
331
|
+
*/
|
|
332
|
+
private localOnly;
|
|
301
333
|
private constructorAgentsDir;
|
|
302
|
-
constructor(maxAgents?: number, agentsDir?: string | null, defaultMode?: Mode | null, filterByCwd?: string | null, cleanupAgeDays?: number);
|
|
334
|
+
constructor(maxAgents?: number, agentsDir?: string | null, defaultMode?: Mode | null, filterByCwd?: string | null, cleanupAgeDays?: number, localOnly?: boolean);
|
|
303
335
|
private initialize;
|
|
304
336
|
private doInitialize;
|
|
305
337
|
getDefaultMode(): Mode;
|