@phnx-labs/agents-cli 1.22.41 → 1.22.43
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 +86 -0
- package/README.md +10 -4
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +25 -7
- package/dist/cli/command-registry.d.ts +2 -3
- package/dist/cli/command-registry.js +4 -8
- package/dist/commands/accounts.d.ts +29 -0
- package/dist/commands/accounts.js +220 -29
- package/dist/commands/alias.d.ts +7 -4
- package/dist/commands/alias.js +34 -9
- package/dist/commands/auth.d.ts +9 -0
- package/dist/commands/auth.js +108 -0
- package/dist/commands/browser-sessions-picker.js +42 -62
- package/dist/commands/browser.js +30 -38
- package/dist/commands/computer-actions.d.ts +1 -1
- package/dist/commands/computer-actions.js +1 -1
- package/dist/commands/computer-sessions-picker.js +32 -55
- package/dist/commands/computer.d.ts +1 -1
- package/dist/commands/computer.js +22 -8
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/doctor.d.ts +5 -3
- package/dist/commands/doctor.js +23 -8
- package/dist/commands/exec.js +3 -2
- package/dist/commands/feed.js +2 -31
- package/dist/commands/harness.js +3 -3
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +1 -1
- package/dist/commands/insights.js +163 -85
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/mine.js +1 -1
- package/dist/commands/monitors.js +1 -1
- package/dist/commands/org.d.ts +6 -0
- package/dist/commands/org.js +175 -0
- package/dist/commands/packages.js +1 -1
- package/dist/commands/profiles.js +2 -2
- package/dist/commands/prune.js +1 -1
- package/dist/commands/routines.js +44 -26
- package/dist/commands/run-account-picker.d.ts +19 -0
- package/dist/commands/run-account-picker.js +81 -0
- package/dist/commands/sessions-picker-factory.d.ts +40 -0
- package/dist/commands/sessions-picker-factory.js +67 -0
- package/dist/commands/sessions-picker.js +124 -122
- package/dist/commands/setup-browser.js +4 -4
- package/dist/commands/setup-mine.js +1 -1
- package/dist/commands/setup-preferences.js +2 -2
- package/dist/commands/setup.js +10 -4
- package/dist/commands/snapshot.d.ts +1 -1
- package/dist/commands/snapshot.js +4 -4
- package/dist/commands/status.d.ts +4 -2
- package/dist/commands/status.js +10 -7
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +30 -3
- package/dist/commands/versions.js +1 -1
- package/dist/commands/view.js +7 -1
- package/dist/commands/webhook.js +1 -1
- package/dist/lib/account-capabilities.d.ts +10 -0
- package/dist/lib/account-capabilities.js +34 -0
- package/dist/lib/accounting/rotate.d.ts +5 -3
- package/dist/lib/accounting/rotate.js +36 -7
- package/dist/lib/accounting/usage.d.ts +18 -4
- package/dist/lib/accounting/usage.js +138 -27
- package/dist/lib/agent-spec/agents.d.ts +584 -0
- package/dist/lib/agent-spec/agents.js +3127 -0
- package/dist/lib/agent-spec/provider.js +1 -1
- package/dist/lib/agents.d.ts +2 -584
- package/dist/lib/agents.js +2 -3127
- package/dist/lib/browser/drivers/local.d.ts +8 -2
- package/dist/lib/browser/drivers/local.js +8 -2
- package/dist/lib/browser/drivers/ssh.d.ts +8 -2
- package/dist/lib/browser/drivers/ssh.js +10 -4
- package/dist/lib/browser/hygiene.d.ts +1 -1
- package/dist/lib/browser/hygiene.js +5 -1
- package/dist/lib/browser/profiles.d.ts +74 -1
- package/dist/lib/browser/profiles.js +114 -5
- package/dist/lib/browser/runtime-state.d.ts +11 -5
- package/dist/lib/browser/runtime-state.js +13 -9
- package/dist/lib/browser/service.d.ts +68 -30
- package/dist/lib/browser/service.js +305 -194
- package/dist/lib/browser/types.d.ts +95 -3
- package/dist/lib/browser/types.js +51 -0
- package/dist/lib/catchup.js +1 -1
- package/dist/lib/cloud/rush.d.ts +0 -15
- package/dist/lib/cloud/rush.js +0 -35
- package/dist/lib/computer/actions.d.ts +1 -1
- package/dist/lib/computer/actions.js +1 -1
- package/dist/lib/{computer-rpc.js → computer/computer-rpc.js} +5 -5
- package/dist/lib/computer/dispatch.d.ts +1 -1
- package/dist/lib/computer/download.d.ts +1 -1
- package/dist/lib/computer/download.js +3 -3
- package/dist/lib/{ssh-tunnel.d.ts → computer/ssh-tunnel.d.ts} +1 -1
- package/dist/lib/{ssh-tunnel.js → computer/ssh-tunnel.js} +12 -12
- package/dist/lib/crabbox/runtimes.d.ts +6 -2
- package/dist/lib/crabbox/runtimes.js +21 -5
- package/dist/lib/daemon/daemon.js +87 -59
- package/dist/lib/{runner.d.ts → daemon/runner.d.ts} +28 -11
- package/dist/lib/{runner.js → daemon/runner.js} +143 -123
- package/dist/lib/devices/doctor-findings.d.ts +1 -1
- package/dist/lib/devices/doctor-findings.js +4 -4
- package/dist/lib/devices/fleet-inventory.js +1 -1
- package/dist/lib/devices/harness-inventory.js +1 -1
- package/dist/lib/devices/health-report.js +1 -1
- package/dist/lib/devices/registry.js +4 -22
- package/dist/lib/doctor-diff.d.ts +1 -1
- package/dist/lib/doctor-diff.js +1 -1
- package/dist/lib/drift-sync.d.ts +1 -1
- package/dist/lib/drift-sync.js +2 -2
- package/dist/lib/drift.d.ts +1 -1
- package/dist/lib/drift.js +1 -1
- package/dist/lib/entitlement.d.ts +31 -0
- package/dist/lib/entitlement.js +137 -0
- package/dist/lib/exec.js +52 -205
- package/dist/lib/feed/feed.js +2 -8
- package/dist/lib/format.d.ts +9 -0
- package/dist/lib/format.js +16 -0
- package/dist/lib/fs-atomic.d.ts +7 -0
- package/dist/lib/fs-atomic.js +9 -0
- package/dist/lib/github/pr-mergeable.d.ts +49 -0
- package/dist/lib/github/pr-mergeable.js +157 -0
- package/dist/lib/github/pr-verdict.d.ts +67 -0
- package/dist/lib/github/pr-verdict.js +72 -0
- package/dist/lib/harness/adapter.d.ts +161 -0
- package/dist/lib/harness/adapter.js +47 -0
- package/dist/lib/harness/adapters/claude.d.ts +2 -0
- package/dist/lib/harness/adapters/claude.js +110 -0
- package/dist/lib/harness/adapters/codex.d.ts +2 -0
- package/dist/lib/harness/adapters/codex.js +83 -0
- package/dist/lib/harness/adapters/copilot.d.ts +2 -0
- package/dist/lib/harness/adapters/copilot.js +23 -0
- package/dist/lib/harness/adapters/cursor.d.ts +2 -0
- package/dist/lib/harness/adapters/cursor.js +38 -0
- package/dist/lib/harness/adapters/droid.d.ts +2 -0
- package/dist/lib/harness/adapters/droid.js +17 -0
- package/dist/lib/harness/adapters/grok.d.ts +2 -0
- package/dist/lib/harness/adapters/grok.js +14 -0
- package/dist/lib/harness/adapters/kimi.d.ts +2 -0
- package/dist/lib/harness/adapters/kimi.js +49 -0
- package/dist/lib/harness/adapters/muse.d.ts +2 -0
- package/dist/lib/harness/adapters/muse.js +44 -0
- package/dist/lib/harness/adapters/opencode.d.ts +2 -0
- package/dist/lib/harness/adapters/opencode.js +14 -0
- package/dist/lib/harness/exec-config-version.d.ts +8 -0
- package/dist/lib/harness/exec-config-version.js +22 -0
- package/dist/lib/harness/index.d.ts +1 -0
- package/dist/lib/harness/index.js +34 -0
- package/dist/lib/{hooks.d.ts → hooks/install.d.ts} +1 -1
- package/dist/lib/{hooks.js → hooks/install.js} +11 -11
- package/dist/lib/hosts/passthrough.d.ts +10 -0
- package/dist/lib/hosts/passthrough.js +28 -6
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/import.js +1 -1
- package/dist/lib/installations/index.d.ts +4 -4
- package/dist/lib/installations/index.js +3 -4
- package/dist/lib/installations/migrate.js +6 -6
- package/dist/lib/{shims.d.ts → installations/shims.d.ts} +2 -2
- package/dist/lib/{shims.js → installations/shims.js} +13 -115
- package/dist/lib/installations/store.d.ts +141 -3
- package/dist/lib/installations/store.js +475 -5
- package/dist/lib/installations/versions.d.ts +40 -125
- package/dist/lib/installations/versions.js +57 -462
- package/dist/lib/isolation-boundary-report.d.ts +1 -1
- package/dist/lib/isolation-boundary-report.js +1 -1
- package/dist/lib/mailbox-gc.js +2 -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.js +19 -8
- package/dist/lib/monitors/config.d.ts +2 -2
- package/dist/lib/monitors/config.js +3 -2
- package/dist/lib/monitors/dispatch.d.ts +1 -1
- package/dist/lib/monitors/dispatch.js +2 -2
- package/dist/lib/monitors/state.d.ts +1 -1
- package/dist/lib/origin-machine.d.ts +18 -0
- package/dist/lib/origin-machine.js +34 -0
- package/dist/lib/perf/db.js +1 -1
- package/dist/lib/perf/spool.js +1 -1
- package/dist/lib/plugins/plugins.js +1 -1
- package/dist/lib/plugins/skills.js +1 -1
- package/dist/lib/prix-account.d.ts +158 -0
- package/dist/lib/prix-account.js +214 -0
- package/dist/lib/profiles.d.ts +7 -0
- package/dist/lib/profiles.js +26 -1
- package/dist/lib/project-key.js +2 -2
- package/dist/lib/project-launch.js +1 -1
- package/dist/lib/refresh.js +2 -2
- package/dist/lib/resource-inventory.d.ts +1 -1
- package/dist/lib/resource-inventory.js +1 -1
- package/dist/lib/rules/rules.js +1 -1
- package/dist/lib/sandbox.d.ts +24 -0
- package/dist/lib/sandbox.js +101 -0
- package/dist/lib/scheduling/routines.d.ts +18 -9
- package/dist/lib/scheduling/routines.js +17 -9
- 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/agent.js +9 -1
- package/dist/lib/secrets/index.js +1 -1
- package/dist/lib/secrets/list-filter.js +1 -1
- package/dist/lib/self-heal/checks/hook-runtime.js +1 -1
- package/dist/lib/self-heal/checks/path.js +1 -1
- package/dist/lib/self-heal/checks/shadowing.js +1 -1
- package/dist/lib/self-heal/checks/shims.js +1 -1
- package/dist/lib/service-manifest.d.ts +20 -0
- package/dist/lib/service-manifest.js +34 -0
- package/dist/lib/session/cloud.js +1 -1
- package/dist/lib/session/db.js +1 -1
- package/dist/lib/session/discover.d.ts +39 -0
- package/dist/lib/session/discover.js +3 -3
- package/dist/lib/session/fork.js +1 -1
- package/dist/lib/session/live-metadata.js +1 -1
- package/dist/lib/session/origin-machine.d.ts +1 -18
- package/dist/lib/session/origin-machine.js +4 -34
- package/dist/lib/session/parse.js +24 -42
- package/dist/lib/session/recovery.js +1 -1
- package/dist/lib/session/relative-time.d.ts +1 -44
- package/dist/lib/session/relative-time.js +4 -120
- package/dist/lib/session/remote-list.js +1 -1
- package/dist/lib/session/remote.js +2 -2
- package/dist/lib/session/short-id.d.ts +1 -17
- package/dist/lib/session/short-id.js +4 -20
- package/dist/lib/session/width.d.ts +1 -29
- package/dist/lib/session/width.js +4 -101
- package/dist/lib/snapshot.d.ts +2 -2
- package/dist/lib/snapshot.js +1 -1
- package/dist/lib/staleness/prune.d.ts +1 -1
- package/dist/lib/staleness/prune.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +4 -1
- package/dist/lib/startup/command-registry.js +12 -5
- package/dist/lib/teams/agents.js +2 -19
- package/dist/lib/teams/parsers.js +20 -26
- package/dist/lib/teams/placement-probe.js +1 -1
- package/dist/lib/teams/registry.js +2 -21
- package/dist/lib/teams/scheduler.js +1 -1
- package/dist/lib/text/relative-time.d.ts +44 -0
- package/dist/lib/text/relative-time.js +120 -0
- package/dist/lib/text/short-id.d.ts +17 -0
- package/dist/lib/text/short-id.js +20 -0
- package/dist/lib/text/width.d.ts +29 -0
- package/dist/lib/text/width.js +101 -0
- package/dist/lib/triggers/handlers.d.ts +7 -0
- package/dist/lib/triggers/handlers.js +9 -1
- package/dist/lib/triggers/webhook.d.ts +1 -0
- package/dist/lib/triggers/webhook.js +3 -2
- package/dist/lib/uninstall.js +1 -1
- package/dist/lib/version.d.ts +9 -2
- package/dist/lib/version.js +11 -9
- package/dist/lib/view-types.d.ts +4 -0
- package/package.json +1 -1
- package/dist/commands/tickets.d.ts +0 -2
- package/dist/commands/tickets.js +0 -43
- package/dist/lib/observe-aliases.d.ts +0 -30
- package/dist/lib/observe-aliases.js +0 -38
- package/dist/lib/tickets/list.d.ts +0 -53
- package/dist/lib/tickets/list.js +0 -153
- /package/dist/lib/{computer-rpc.d.ts → computer/computer-rpc.d.ts} +0 -0
|
@@ -17,29 +17,30 @@ import { spawn, execFileSync } from 'child_process';
|
|
|
17
17
|
import * as fs from 'fs';
|
|
18
18
|
import * as path from 'path';
|
|
19
19
|
import * as os from 'os';
|
|
20
|
-
import { getCliLaunch, getAgentsBinDir } from '
|
|
21
|
-
import { resolveJobPrompt, parseTimeout, writeRunMeta, listRuns, getJobRunsDir, getRunDir, checkJobDeviceEligibility, finalizeRunMeta, resolveJobExecutionContext, slotRunId, claimRunSlot, readRunMeta, resolveHostStrategy, } from '
|
|
22
|
-
import { getRunsDir, getUserAgentsDir, readMeta, getDaemonDir } from '
|
|
23
|
-
import { shortCodexHome } from '
|
|
24
|
-
import { prepareJobHome, buildSpawnEnv, getJobHomePath } from '
|
|
25
|
-
import { resolveModel, buildReasoningFlags } from '
|
|
26
|
-
import { createTimer, redactPrompt, emitRoutineEnd } from '
|
|
27
|
-
import {
|
|
28
|
-
import { applyAddDirs } from '
|
|
29
|
-
import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, detectAuthFailure, isAuthFailureFromLog, authFailureReason, AGENT_COMMANDS, } from '
|
|
30
|
-
import { resolveActor } from '
|
|
31
|
-
import { loadTask as loadHostTask } from '
|
|
32
|
-
import { reconcileTask as reconcileHostTask } from '
|
|
33
|
-
import { backgroundSpawnOptions, killTree } from '
|
|
20
|
+
import { getCliLaunch, getAgentsBinDir } from '../cli-entry.js';
|
|
21
|
+
import { resolveJobPrompt, parseTimeout, writeRunMeta, listRuns, getJobRunsDir, getRunDir, checkJobDeviceEligibility, finalizeRunMeta, resolveJobExecutionContext, slotRunId, claimRunSlot, readRunMeta, resolveHostStrategy, } from '../scheduling/routines.js';
|
|
22
|
+
import { getRunsDir, getUserAgentsDir, readMeta, getDaemonDir } from '../state.js';
|
|
23
|
+
import { shortCodexHome } from '../codex-home.js';
|
|
24
|
+
import { prepareJobHome, buildSpawnEnv, getJobHomePath, assertSandboxForwardsHostGhAuth } from '../sandbox.js';
|
|
25
|
+
import { resolveModel, buildReasoningFlags } from '../models.js';
|
|
26
|
+
import { createTimer, redactPrompt, emitRoutineEnd } from '../feed/events.js';
|
|
27
|
+
import { resolveHarnessAdapter } from '../harness/index.js';
|
|
28
|
+
import { applyAddDirs } from '../add-dir.js';
|
|
29
|
+
import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, detectAuthFailure, isAuthFailureFromLog, authFailureReason, AGENT_COMMANDS, } from '../exec.js';
|
|
30
|
+
import { resolveActor } from '../actor.js';
|
|
31
|
+
import { loadTask as loadHostTask } from '../hosts/tasks.js';
|
|
32
|
+
import { reconcileTask as reconcileHostTask } from '../hosts/reconcile.js';
|
|
33
|
+
import { backgroundSpawnOptions, killTree } from '../platform/process.js';
|
|
34
34
|
import lockfile from 'proper-lockfile';
|
|
35
|
-
import { ensureLockTarget } from '
|
|
36
|
-
import { walkForFiles } from '
|
|
37
|
-
import { getBinaryPath, isVersionInstalled, resolveVersion, getVersionHomePath } from '
|
|
38
|
-
import { resolveClaudeSetupToken } from '
|
|
39
|
-
import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from '
|
|
40
|
-
import { readAuthHealth, isDeadVerdict } from '
|
|
41
|
-
import { machineId } from '
|
|
42
|
-
import { isSelfUpdatingAgent,
|
|
35
|
+
import { ensureLockTarget } from '../fs-atomic.js';
|
|
36
|
+
import { walkForFiles } from '../fs-walk.js';
|
|
37
|
+
import { getBinaryPath, isVersionInstalled, resolveVersion, getVersionHomePath } from '../installations/versions.js';
|
|
38
|
+
import { resolveClaudeSetupToken } from '../claude-account-token.js';
|
|
39
|
+
import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from '../accounting/rotate.js';
|
|
40
|
+
import { readAuthHealth, isDeadVerdict } from '../auth-health.js';
|
|
41
|
+
import { machineId } from '../machine-id.js';
|
|
42
|
+
import { isSelfUpdatingAgent, ROUTINE_AGENT_IDS, isAgentHardDeprecated, hardDeprecationError } from '../agents.js';
|
|
43
|
+
import { isCustomHarnessName, readProfile } from '../profiles.js';
|
|
43
44
|
export class RoutineAlreadyRunningError extends Error {
|
|
44
45
|
constructor(jobName, runId) {
|
|
45
46
|
super(`Routine '${jobName}' already has a running execution (${runId})`);
|
|
@@ -308,7 +309,7 @@ function allocateRoutineAttempt(config, trigger) {
|
|
|
308
309
|
// harness leaves the command table (gemini did), and the legacy routine must
|
|
309
310
|
// still land a visible 'blocked' record, never a generic buildJobCommand
|
|
310
311
|
// failure (RUSH-2202).
|
|
311
|
-
if (!config.workflow && config.agent && isAgentHardDeprecated(config.agent)) {
|
|
312
|
+
if (!config.workflow && config.agent && !isCustomHarnessName(config.agent) && isAgentHardDeprecated(config.agent)) {
|
|
312
313
|
const reason = hardDeprecationError(config.agent);
|
|
313
314
|
return {
|
|
314
315
|
proceed: false,
|
|
@@ -488,6 +489,48 @@ export function routineSpawnCwd(config) {
|
|
|
488
489
|
const ctx = resolveJobExecutionContext(config, { mode: 'local' });
|
|
489
490
|
return ctx.absoluteCwd ?? os.homedir();
|
|
490
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Bake the daemon-job argv skeleton from AGENT_COMMANDS.
|
|
494
|
+
*
|
|
495
|
+
* The two tables used to be independent copies of the same launch decision.
|
|
496
|
+
* Argv now comes from AGENT_COMMANDS.base / promptFlag / jsonFlags / modeFlags.
|
|
497
|
+
* Two documented exceptions, not a second table:
|
|
498
|
+
* - kimi uses `--prompt` (long form). `-p` is the exec alias; combining
|
|
499
|
+
* either with --plan/--auto/--yolo aborts, so routineModeArgs emits no
|
|
500
|
+
* mode flag. The daemon has always spawned the long form.
|
|
501
|
+
* - claude places `--verbose` (from jsonFlags) before the prompt and bakes
|
|
502
|
+
* modeFlags.plan so claudeAdapter.routineModeArgs can splice
|
|
503
|
+
* plan → acceptEdits / auto / skip.
|
|
504
|
+
*
|
|
505
|
+
* Returns undefined when `agent` is outside ROUTINE_AGENT_IDS (gemini, grok,
|
|
506
|
+
* and every other harness the daemon does not fire locally).
|
|
507
|
+
*/
|
|
508
|
+
export function bakeRoutineArgv(agent) {
|
|
509
|
+
if (!ROUTINE_AGENT_IDS.includes(agent))
|
|
510
|
+
return undefined;
|
|
511
|
+
const template = AGENT_COMMANDS[agent];
|
|
512
|
+
if (!template)
|
|
513
|
+
return undefined;
|
|
514
|
+
const json = template.jsonFlags ?? [];
|
|
515
|
+
const cmd = [...template.base];
|
|
516
|
+
if (agent === 'kimi') {
|
|
517
|
+
cmd.push('--prompt', '{prompt}', ...json);
|
|
518
|
+
return cmd;
|
|
519
|
+
}
|
|
520
|
+
if (agent === 'claude') {
|
|
521
|
+
const verbose = json.includes('--verbose') ? ['--verbose'] : [];
|
|
522
|
+
const jsonRest = json.filter((flag) => flag !== '--verbose');
|
|
523
|
+
cmd.push(template.promptFlag, ...verbose, '{prompt}', ...jsonRest, ...(template.modeFlags.plan ?? []));
|
|
524
|
+
return cmd;
|
|
525
|
+
}
|
|
526
|
+
if (template.promptFlag === 'positional') {
|
|
527
|
+
cmd.push('{prompt}', ...json);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
cmd.push(template.promptFlag, '{prompt}', ...json);
|
|
531
|
+
}
|
|
532
|
+
return cmd;
|
|
533
|
+
}
|
|
491
534
|
/** Build the full CLI argv for executing a job, applying mode, model, and permission flags. */
|
|
492
535
|
export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
|
|
493
536
|
// Workflow branch: delegate to `agents run <workflow>` which handles subagent
|
|
@@ -514,94 +557,36 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
|
|
|
514
557
|
cmd.push('--account', config.account);
|
|
515
558
|
return cmd;
|
|
516
559
|
}
|
|
517
|
-
|
|
560
|
+
// Custom-harness branch: delegate to `agents run <name>`, which owns profile
|
|
561
|
+
// resolution (host binary, model env, provider auth) — the same delegation
|
|
562
|
+
// the workflow and resume branches use. The profile pins its own version and
|
|
563
|
+
// auth, so no command template, binary pinning, or account-env injection
|
|
564
|
+
// applies here.
|
|
565
|
+
if (isCustomHarnessName(agent)) {
|
|
566
|
+
const cmd = ['agents', 'run', agent, resolvedPrompt, '--mode', config.mode];
|
|
567
|
+
if (config.account && forwardAccount)
|
|
568
|
+
cmd.push('--account', config.account);
|
|
569
|
+
return cmd;
|
|
570
|
+
}
|
|
571
|
+
const template = bakeRoutineArgv(agent);
|
|
518
572
|
if (!template) {
|
|
519
|
-
|
|
573
|
+
// A name outside both tables was either never valid or was a custom
|
|
574
|
+
// harness whose profile has since been deleted — validateJob accepted it
|
|
575
|
+
// against a profile that existed then. Name the repair, not just the miss.
|
|
576
|
+
throw new Error(`Unsupported agent for daemon jobs: ${agent}. ` +
|
|
577
|
+
`If '${agent}' was a custom harness, its profile no longer exists on this device — ` +
|
|
578
|
+
`recreate it (agents harness add ${agent} ...) or point the routine at another agent.`);
|
|
520
579
|
}
|
|
521
580
|
let cmd = template.map((part) => part.replace('{prompt}', resolvedPrompt));
|
|
522
581
|
// Canonicalize mode (accepts legacy `full` as alias for `skip`).
|
|
523
582
|
const mode = normalizeMode(config.mode);
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const planIndex = cmd.indexOf('plan');
|
|
532
|
-
if (planIndex !== -1)
|
|
533
|
-
cmd[planIndex] = 'auto';
|
|
534
|
-
}
|
|
535
|
-
else if (mode === 'skip') {
|
|
536
|
-
// Replace --permission-mode plan with --dangerously-skip-permissions
|
|
537
|
-
const pmIndex = cmd.indexOf('--permission-mode');
|
|
538
|
-
if (pmIndex !== -1)
|
|
539
|
-
cmd.splice(pmIndex, 2, '--dangerously-skip-permissions');
|
|
540
|
-
}
|
|
541
|
-
appendModelAndReasoning(cmd, config);
|
|
542
|
-
}
|
|
543
|
-
if (config.agent === 'codex') {
|
|
544
|
-
const policyMode = mode === 'plan' || mode === 'skip' ? mode : 'edit';
|
|
545
|
-
const routineRoots = (config.allow?.dirs ?? []).map((dir) => {
|
|
546
|
-
if (dir.startsWith('-')) {
|
|
547
|
-
throw new Error(`allow.dirs entries must not start with '-': ${JSON.stringify(dir)}`);
|
|
548
|
-
}
|
|
549
|
-
return dir.replace(/^~/, os.homedir());
|
|
550
|
-
});
|
|
551
|
-
cmd.push(...codexPolicyArgs(policyMode, [...codexEditWritableRoots(), ...routineRoots]));
|
|
552
|
-
appendModelAndReasoning(cmd, config);
|
|
553
|
-
}
|
|
554
|
-
if (config.agent === 'cursor') {
|
|
555
|
-
if (mode === 'plan') {
|
|
556
|
-
cmd.push('--plan');
|
|
557
|
-
}
|
|
558
|
-
else if (mode === 'skip') {
|
|
559
|
-
cmd.push('-f');
|
|
560
|
-
}
|
|
561
|
-
else {
|
|
562
|
-
// The configured cwd is the user's workspace trust decision. --trust is
|
|
563
|
-
// narrower than --yolo/-f because it does not bypass tool permissions.
|
|
564
|
-
cmd.push('--trust');
|
|
565
|
-
}
|
|
566
|
-
appendModelAndReasoning(cmd, config);
|
|
567
|
-
}
|
|
568
|
-
if (config.agent === 'kimi') {
|
|
569
|
-
// kimi daemon jobs always run headless via `--prompt`, which cannot be
|
|
570
|
-
// combined with any startup-mode flag (--plan/--auto/--yolo all abort with
|
|
571
|
-
// "Cannot combine --prompt with --X"). edit/auto/skip reduce to kimi's default
|
|
572
|
-
// headless auto-run, so emit no flag. plan has no headless read-only
|
|
573
|
-
// equivalent, so resolveHeadlessMode downgrades a plan request to auto with a
|
|
574
|
-
// stderr warning (kimi's headlessPlan is false) — routines run headless, so
|
|
575
|
-
// interactive is always false here. The returned mode carries no flag either.
|
|
576
|
-
resolveHeadlessMode('kimi', mode, false);
|
|
577
|
-
appendModelAndReasoning(cmd, config);
|
|
578
|
-
}
|
|
579
|
-
if (config.agent === 'droid') {
|
|
580
|
-
// droid exec defaults to read-only (plan). Escalate autonomy per mode.
|
|
581
|
-
if (mode === 'edit') {
|
|
582
|
-
cmd.push('--auto', 'low');
|
|
583
|
-
}
|
|
584
|
-
else if (mode === 'auto') {
|
|
585
|
-
cmd.push('--auto', 'high');
|
|
586
|
-
}
|
|
587
|
-
else if (mode === 'skip') {
|
|
588
|
-
cmd.push('--skip-permissions-unsafe');
|
|
589
|
-
}
|
|
590
|
-
appendModelAndReasoning(cmd, config);
|
|
591
|
-
}
|
|
592
|
-
if (config.agent === 'muse') {
|
|
593
|
-
// muse exec: plan ≈ no non-shell writes; auto skips approval prompts but
|
|
594
|
-
// keeps the OS sandbox; skip is --yolo (no approval, no sandbox, trust).
|
|
595
|
-
if (mode === 'plan') {
|
|
596
|
-
cmd.push('--disable-write');
|
|
597
|
-
}
|
|
598
|
-
else if (mode === 'auto') {
|
|
599
|
-
cmd.push('--disable-approval');
|
|
600
|
-
}
|
|
601
|
-
else if (mode === 'skip') {
|
|
602
|
-
cmd.push('--yolo');
|
|
603
|
-
}
|
|
604
|
-
// edit: default on-request approval + sandbox
|
|
583
|
+
// Routine launch-arg quirks (the harness axis of Move 3): each per-agent arm
|
|
584
|
+
// moves to its harness adapter; runner appends model/reasoning flags after,
|
|
585
|
+
// exactly as every arm did. Agents with no routine quirk have no adapter
|
|
586
|
+
// override, so they skip both — the old behavior for an entry with no arm.
|
|
587
|
+
const routineAdapter = resolveHarnessAdapter(agent);
|
|
588
|
+
if (routineAdapter.routineModeArgs) {
|
|
589
|
+
routineAdapter.routineModeArgs(cmd, { mode, config, resolveHeadlessMode });
|
|
605
590
|
appendModelAndReasoning(cmd, config);
|
|
606
591
|
}
|
|
607
592
|
// allow.dirs → harness-specific grants. Codex is handled in its branch above
|
|
@@ -628,7 +613,8 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
|
|
|
628
613
|
* Reasoning level (config.config.reasoning) maps to per-agent flags via models.ts.
|
|
629
614
|
*/
|
|
630
615
|
function appendModelAndReasoning(cmd, config) {
|
|
631
|
-
// Only called from buildJobCommand's agent path
|
|
616
|
+
// Only called from buildJobCommand's agent path AFTER the custom-harness
|
|
617
|
+
// branch returned — config.agent is a native id here.
|
|
632
618
|
const agent = config.agent;
|
|
633
619
|
const model = config.config?.model;
|
|
634
620
|
if (model) {
|
|
@@ -710,6 +696,8 @@ function transcriptBasePath(runDir) {
|
|
|
710
696
|
export function snapshotRoutineTranscriptBase(meta, runDir, overlayHome) {
|
|
711
697
|
if (!meta.agent)
|
|
712
698
|
return;
|
|
699
|
+
// A host/cloud custom-harness run records the harness name, which has no
|
|
700
|
+
// transcript spec — the null-specs guard below skips it, as before.
|
|
713
701
|
const specs = ROUTINE_TRANSCRIPT_SPECS[meta.agent];
|
|
714
702
|
if (!specs)
|
|
715
703
|
return;
|
|
@@ -748,6 +736,8 @@ function readTranscriptBase(runDir) {
|
|
|
748
736
|
export function archiveRoutineTranscripts(meta, runDir, overlayHome) {
|
|
749
737
|
if (!meta.agent)
|
|
750
738
|
return;
|
|
739
|
+
// A host/cloud custom-harness run records the harness name, which has no
|
|
740
|
+
// transcript spec — the null-specs guard below skips it, as before.
|
|
751
741
|
const specs = ROUTINE_TRANSCRIPT_SPECS[meta.agent];
|
|
752
742
|
if (!specs)
|
|
753
743
|
return;
|
|
@@ -882,10 +872,17 @@ export async function resolveRoutineLaunch(config, cwd = process.cwd(), deps = {
|
|
|
882
872
|
if (config.workflow) {
|
|
883
873
|
return { chain: [], rotation: null, pinned: false };
|
|
884
874
|
}
|
|
875
|
+
if (config.agent && isCustomHarnessName(config.agent)) {
|
|
876
|
+
// A custom harness pins its own version/auth in the profile, so there is
|
|
877
|
+
// no version/account chain to resolve here — `agents run <name>` owns it
|
|
878
|
+
// (matching exec's "strategy ignored: custom harness pins its own
|
|
879
|
+
// version/auth").
|
|
880
|
+
return { chain: [], rotation: null, pinned: false };
|
|
881
|
+
}
|
|
885
882
|
// resolveRoutineLaunch is only called for agent jobs (workflow returns above;
|
|
886
883
|
// command jobs branch out of execute*Job before reaching this).
|
|
887
884
|
const agent = config.agent;
|
|
888
|
-
const { findAccount, findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('
|
|
885
|
+
const { findAccount, findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('../account-registry.js');
|
|
889
886
|
const meta = (deps.readMeta ?? readMeta)();
|
|
890
887
|
const explicitCredential = config.account
|
|
891
888
|
? (deps.findCredentialAccount?.(config.account) ?? (deps.resolveCredentialAccount !== undefined || findAccount(config.account) !== null))
|
|
@@ -1041,7 +1038,7 @@ export async function assertRoutineAccountLocalForPlacement(config, mode, deps =
|
|
|
1041
1038
|
return;
|
|
1042
1039
|
let account = deps.account;
|
|
1043
1040
|
if (account === undefined) {
|
|
1044
|
-
const { findUnifiedAccount } = await import('
|
|
1041
|
+
const { findUnifiedAccount } = await import('../account-registry.js');
|
|
1045
1042
|
account = findUnifiedAccount(config.account, (deps.readMeta ?? readMeta)());
|
|
1046
1043
|
}
|
|
1047
1044
|
if (!account) {
|
|
@@ -1086,7 +1083,7 @@ export function buildHostDispatchOptions(config, ctx) {
|
|
|
1086
1083
|
};
|
|
1087
1084
|
}
|
|
1088
1085
|
export function dispatchesViaAgentsRun(config) {
|
|
1089
|
-
return Boolean(config.workflow || config.resume);
|
|
1086
|
+
return Boolean(config.workflow || config.resume || (config.agent && isCustomHarnessName(config.agent)));
|
|
1090
1087
|
}
|
|
1091
1088
|
/**
|
|
1092
1089
|
* Merge sandbox/base env with the canonical per-version exec env
|
|
@@ -1261,7 +1258,7 @@ async function executeJobPlaced(config, deps, attempt) {
|
|
|
1261
1258
|
// do not apply. Sync callers (manual `routines run`, catchup) follow the
|
|
1262
1259
|
// remote run to completion when possible.
|
|
1263
1260
|
{
|
|
1264
|
-
const { resolvePlacementTarget } = await import('
|
|
1261
|
+
const { resolvePlacementTarget } = await import('../routines-placement.js');
|
|
1265
1262
|
const target = await resolvePlacementTarget(config);
|
|
1266
1263
|
const placed = await dispatchPlacedJob(config, target, attempt);
|
|
1267
1264
|
if (placed)
|
|
@@ -1288,7 +1285,11 @@ async function executeJobPlaced(config, deps, attempt) {
|
|
|
1288
1285
|
// the agent's config dir, and the sandbox overlay home has only a freshly-generated
|
|
1289
1286
|
// config with no session store. So a resume job is never sandboxed, regardless of
|
|
1290
1287
|
// `config.sandbox` (see the resume branch in buildJobCommand).
|
|
1291
|
-
|
|
1288
|
+
// Resume needs the REAL home (session store); a custom harness needs it too —
|
|
1289
|
+
// the delegated `agents run <name>` resolves ~/.agents (profiles, setup
|
|
1290
|
+
// sentinel, version homes) from HOME, which the overlay would hide. Exec
|
|
1291
|
+
// still isolates the run in the host version home it swaps HOME into.
|
|
1292
|
+
const useSandbox = config.sandbox !== false && !config.resume && !(config.agent && isCustomHarnessName(config.agent));
|
|
1292
1293
|
const overlayHome = useSandbox ? prepareJobHome(config) : undefined;
|
|
1293
1294
|
const runId = attempt.runId;
|
|
1294
1295
|
const runDir = getRunDir(config.name, runId);
|
|
@@ -1299,9 +1300,13 @@ async function executeJobPlaced(config, deps, attempt) {
|
|
|
1299
1300
|
// Workflows run via `agents run <workflow>` which delegates to claude under the hood.
|
|
1300
1301
|
// Use 'claude' as the effective agent for report extraction and metadata when workflow is set.
|
|
1301
1302
|
// (command jobs branched out earlier, so config.agent is set on the non-workflow path.)
|
|
1302
|
-
const effectiveAgent = config.workflow
|
|
1303
|
+
const effectiveAgent = config.workflow
|
|
1304
|
+
? 'claude'
|
|
1305
|
+
: isCustomHarnessName(config.agent)
|
|
1306
|
+
? readProfile(config.agent).host.agent
|
|
1307
|
+
: config.agent;
|
|
1303
1308
|
if (!dispatchesViaAgentsRun(config)) {
|
|
1304
|
-
const { findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('
|
|
1309
|
+
const { findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('../account-registry.js');
|
|
1305
1310
|
const meta = readMeta();
|
|
1306
1311
|
const selectedAccount = resolveAccountSelection(config.account, effectiveAgent, meta);
|
|
1307
1312
|
if (selectedAccount) {
|
|
@@ -1312,6 +1317,9 @@ async function executeJobPlaced(config, deps, attempt) {
|
|
|
1312
1317
|
Object.assign(baseEnv, resolveCredentialAccount(selectedAccount, effectiveAgent).env);
|
|
1313
1318
|
}
|
|
1314
1319
|
}
|
|
1320
|
+
// RUSH-2860: if this host holds gh auth, the sandbox child MUST see it.
|
|
1321
|
+
if (useSandbox)
|
|
1322
|
+
assertSandboxForwardsHostGhAuth(baseEnv);
|
|
1315
1323
|
const meta = {
|
|
1316
1324
|
jobName: config.name,
|
|
1317
1325
|
runId,
|
|
@@ -1374,7 +1382,7 @@ async function executeJobPlaced(config, deps, attempt) {
|
|
|
1374
1382
|
.map((d) => d.replace(/^~/, os.homedir())),
|
|
1375
1383
|
} : {}),
|
|
1376
1384
|
};
|
|
1377
|
-
const { runLoop } = await import('
|
|
1385
|
+
const { runLoop } = await import('../loop.js');
|
|
1378
1386
|
const loopResult = await runLoop(execOptions, config.loop, {
|
|
1379
1387
|
runId,
|
|
1380
1388
|
runDir,
|
|
@@ -1530,8 +1538,8 @@ async function executeJobOnCloud(config, opts, attempt) {
|
|
|
1530
1538
|
if (!config.agent) {
|
|
1531
1539
|
throw new Error(`Routine '${config.name}' hostStrategy: cloud requires an agent`);
|
|
1532
1540
|
}
|
|
1533
|
-
const { resolveProvider } = await import('
|
|
1534
|
-
const { insertTask } = await import('
|
|
1541
|
+
const { resolveProvider } = await import('../cloud/registry.js');
|
|
1542
|
+
const { insertTask } = await import('../cloud/store.js');
|
|
1535
1543
|
const provider = resolveProvider(undefined, config.agent);
|
|
1536
1544
|
const timer = createTimer('agent.run', {
|
|
1537
1545
|
agent: config.agent,
|
|
@@ -1604,9 +1612,9 @@ async function executeJobOnHost(config, opts, attempt) {
|
|
|
1604
1612
|
if (config.command) {
|
|
1605
1613
|
throw new Error(`Routine '${config.name}' uses 'command:', which can't execute on a host yet — remove 'host:' or 'command:'.`);
|
|
1606
1614
|
}
|
|
1607
|
-
const { resolveHostRunTarget, dispatchPromptToHost } = await import('
|
|
1615
|
+
const { resolveHostRunTarget, dispatchPromptToHost } = await import('../hosts/run-target.js');
|
|
1608
1616
|
const host = await resolveHostRunTarget(config.host);
|
|
1609
|
-
const { evaluateHostActivationReadiness } = await import('
|
|
1617
|
+
const { evaluateHostActivationReadiness } = await import('../routine-readiness.js');
|
|
1610
1618
|
const readiness = await evaluateHostActivationReadiness(config);
|
|
1611
1619
|
if (!readiness.ready)
|
|
1612
1620
|
throw new Error(`${readiness.readiness?.code ?? 'not_ready'}: ${readiness.readiness?.message ?? 'target is not ready'}`);
|
|
@@ -1754,7 +1762,7 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
|
|
|
1754
1762
|
// (the monitor tick observes an already-finalized record), so notify-desktop
|
|
1755
1763
|
// sends the finish notification only for local detached runs below (RUSH-2030).
|
|
1756
1764
|
{
|
|
1757
|
-
const { resolvePlacementTarget } = await import('
|
|
1765
|
+
const { resolvePlacementTarget } = await import('../routines-placement.js');
|
|
1758
1766
|
const target = await resolvePlacementTarget(config);
|
|
1759
1767
|
if (target.mode === 'host' || target.mode === 'cloud') {
|
|
1760
1768
|
await assertRoutineAccountLocalForPlacement(config, target.mode);
|
|
@@ -1802,7 +1810,11 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
|
|
|
1802
1810
|
// the agent's config dir, and the sandbox overlay home has only a freshly-generated
|
|
1803
1811
|
// config with no session store. So a resume job is never sandboxed, regardless of
|
|
1804
1812
|
// `config.sandbox` (see the resume branch in buildJobCommand).
|
|
1805
|
-
|
|
1813
|
+
// Resume needs the REAL home (session store); a custom harness needs it too —
|
|
1814
|
+
// the delegated `agents run <name>` resolves ~/.agents (profiles, setup
|
|
1815
|
+
// sentinel, version homes) from HOME, which the overlay would hide. Exec
|
|
1816
|
+
// still isolates the run in the host version home it swaps HOME into.
|
|
1817
|
+
const useSandbox = config.sandbox !== false && !config.resume && !(config.agent && isCustomHarnessName(config.agent));
|
|
1806
1818
|
const overlayHome = useSandbox ? prepareJobHome(config) : undefined;
|
|
1807
1819
|
const runId = attempt.runId;
|
|
1808
1820
|
const runDir = getRunDir(config.name, runId);
|
|
@@ -1813,7 +1825,7 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
|
|
|
1813
1825
|
? buildSpawnEnv(overlayHome, config.env)
|
|
1814
1826
|
: { ...process.env }, config);
|
|
1815
1827
|
if (!dispatchesViaAgentsRun(config)) {
|
|
1816
|
-
const { findAccount, resolveAccountSelection, resolveCredentialAccount } = await import('
|
|
1828
|
+
const { findAccount, resolveAccountSelection, resolveCredentialAccount } = await import('../account-registry.js');
|
|
1817
1829
|
const selectedAccount = config.account && !findAccount(config.account)
|
|
1818
1830
|
? undefined
|
|
1819
1831
|
: resolveAccountSelection(config.account, config.agent, readMeta());
|
|
@@ -1829,7 +1841,15 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
|
|
|
1829
1841
|
})()
|
|
1830
1842
|
// Non-command path only: config.agent is always set here (command/workflow branch earlier).
|
|
1831
1843
|
: buildRoutineSpawnEnv(baseEnv, config.agent, version, config.timezone, overlayHome);
|
|
1832
|
-
|
|
1844
|
+
// RUSH-2860: if this host holds gh auth, the sandbox child MUST see it —
|
|
1845
|
+
// otherwise monitors runs records ok while every gh call inside fails.
|
|
1846
|
+
if (useSandbox)
|
|
1847
|
+
assertSandboxForwardsHostGhAuth(spawnEnv);
|
|
1848
|
+
const effectiveAgent = config.workflow
|
|
1849
|
+
? 'claude'
|
|
1850
|
+
: isCustomHarnessName(config.agent)
|
|
1851
|
+
? readProfile(config.agent).host.agent
|
|
1852
|
+
: config.agent;
|
|
1833
1853
|
const meta = {
|
|
1834
1854
|
jobName: config.name,
|
|
1835
1855
|
runId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentId } from '../types.js';
|
|
2
|
-
import type { DuplicateVersionHook } from '../hooks.js';
|
|
2
|
+
import type { DuplicateVersionHook } from '../hooks/install.js';
|
|
3
3
|
import type { AgentsBinaryShadow } from '../binary-shadow.js';
|
|
4
4
|
import type { RcSecretFinding } from '../secrets/rc-hygiene.js';
|
|
5
5
|
import type { OwnerSinkStatus } from '../channels/owner-sink.js';
|
|
@@ -41,13 +41,13 @@ import chalk from 'chalk';
|
|
|
41
41
|
import { AGENTS, ALL_AGENT_IDS, supportsAccountInspection } from '../agents.js';
|
|
42
42
|
import { blocksLocalScripts } from '../platform/winpath.js';
|
|
43
43
|
import { loginHint } from '../signin-badge.js';
|
|
44
|
-
import { CONFIG_ENV_ISOLATED_AGENTS } from '../shims.js';
|
|
45
|
-
import { padToWidth, stringWidth } from '../
|
|
44
|
+
import { CONFIG_ENV_ISOLATED_AGENTS } from '../installations/shims.js';
|
|
45
|
+
import { padToWidth, stringWidth } from '../text/width.js';
|
|
46
46
|
import { windowsSshEnrollmentProblem } from './windows-ssh-enrollment.js';
|
|
47
47
|
const AGENT_NAMES = Object.fromEntries(ALL_AGENT_IDS.map((id) => [id, AGENTS[id].name]));
|
|
48
48
|
/** Agents with NO per-version credential isolation: their login is shared across
|
|
49
49
|
* every installed version, so a "log into THIS version" remediation would be a
|
|
50
|
-
* lie. Derived from `CONFIG_ENV_ISOLATED_AGENTS` in `lib/shims.ts` — the shim
|
|
50
|
+
* lie. Derived from `CONFIG_ENV_ISOLATED_AGENTS` in `lib/installations/shims.ts` — the shim
|
|
51
51
|
* generator is what actually exports the per-version isolation env var, so it is
|
|
52
52
|
* the source of truth. Do not hand-maintain a second copy: an agent gaining
|
|
53
53
|
* isolation there must not leave a stale "login is shared" hint here. */
|
|
@@ -193,7 +193,7 @@ export function remediationFor(finding) {
|
|
|
193
193
|
}
|
|
194
194
|
// Isolated per-version home: the login must RUN INSIDE that home. A bare
|
|
195
195
|
// `<cli> login` afterwards would NOT — the native shim resolves to the
|
|
196
|
-
// project/default version (`lib/shims.ts:471-474`), so it would log into
|
|
196
|
+
// project/default version (`lib/installations/shims.ts:471-474`), so it would log into
|
|
197
197
|
// whichever version is default, not the one that is logged out.
|
|
198
198
|
switch (loginShape(agent)) {
|
|
199
199
|
case 'subcommand':
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { getAvailableResources, getVersionHomePath, isVersionIsolated, listInstalledVersions } from '../installations/versions.js';
|
|
12
12
|
import { supports } from '../capabilities.js';
|
|
13
|
-
import { checkVersionHookWiring } from '../hooks.js';
|
|
13
|
+
import { checkVersionHookWiring } from '../hooks/install.js';
|
|
14
14
|
import { getUserAgentsDir, getSystemAgentsDir, readMeta } from '../state.js';
|
|
15
15
|
import { listNativeAccounts } from '../account-registry.js';
|
|
16
16
|
import { readRepoState } from '../git.js';
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import chalk from 'chalk';
|
|
17
17
|
import { ALL_AGENT_IDS, accountDisplayLabel, getAccountInfo, } from '../agents.js';
|
|
18
18
|
import { deriveUsageStatusFromSnapshot, getUsageInfoByIdentity, getUsageLookupKey, } from '../accounting/usage.js';
|
|
19
|
-
import { getVersionHomePath, listInstalledVersions } from '../installations/
|
|
19
|
+
import { getVersionHomePath, listInstalledVersions } from '../installations/store.js';
|
|
20
20
|
import { readMeta } from '../state.js';
|
|
21
21
|
import { listNativeAccounts } from '../account-registry.js';
|
|
22
22
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { padToWidth, stringWidth, terminalWidth, truncateToWidth } from '../
|
|
2
|
+
import { padToWidth, stringWidth, terminalWidth, truncateToWidth } from '../text/width.js';
|
|
3
3
|
import { fmtBytes, headroom } from './health.js';
|
|
4
4
|
import { formatCheckedAge } from '../auth-health.js';
|
|
5
5
|
import { compareFleetInventories } from './fleet-divergence.js';
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
import * as fs from 'fs/promises';
|
|
17
17
|
import * as fsSync from 'fs';
|
|
18
18
|
import * as path from 'path';
|
|
19
|
-
import { randomBytes } from 'crypto';
|
|
20
19
|
import lockfile from 'proper-lockfile';
|
|
21
20
|
import { getDevicesRegistryPath, getDevicesIgnoredPath } from '../state.js';
|
|
21
|
+
import { atomicWriteJsonSync } from '../fs-atomic.js';
|
|
22
22
|
/** A device's effective role, defaulting to `worker` when unset. */
|
|
23
23
|
export function deviceRole(d) {
|
|
24
24
|
return d.role ?? 'worker';
|
|
@@ -94,24 +94,6 @@ export function platformFromOs(os) {
|
|
|
94
94
|
export function shellForPlatform(platform) {
|
|
95
95
|
return platform === 'windows' ? 'powershell' : 'posix';
|
|
96
96
|
}
|
|
97
|
-
/**
|
|
98
|
-
* Atomic JSON write: write to a unique sibling tmp file then rename over the
|
|
99
|
-
* target. rename(2) is atomic on POSIX, so a crashed write leaves the old file
|
|
100
|
-
* untouched instead of producing a half-written registry that loadDevices()
|
|
101
|
-
* would reject.
|
|
102
|
-
*/
|
|
103
|
-
async function atomicWriteJson(p, data) {
|
|
104
|
-
await fs.mkdir(path.dirname(p), { recursive: true });
|
|
105
|
-
const tmp = `${p}.tmp.${process.pid}.${randomBytes(4).toString('hex')}`;
|
|
106
|
-
await fs.writeFile(tmp, JSON.stringify(data, null, 2));
|
|
107
|
-
try {
|
|
108
|
-
await fs.rename(tmp, p);
|
|
109
|
-
}
|
|
110
|
-
catch (err) {
|
|
111
|
-
await fs.unlink(tmp).catch(() => { });
|
|
112
|
-
throw err;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
97
|
/**
|
|
116
98
|
* Run `fn` while holding an exclusive cross-process lock on the registry file.
|
|
117
99
|
* proper-lockfile requires the target to exist, so we touch it first. Stale
|
|
@@ -187,7 +169,7 @@ export function loadDevicesSync() {
|
|
|
187
169
|
}
|
|
188
170
|
}
|
|
189
171
|
async function saveDevices(reg) {
|
|
190
|
-
|
|
172
|
+
atomicWriteJsonSync(registryPath(), reg);
|
|
191
173
|
}
|
|
192
174
|
/** Get a single device profile, or null if it is not registered. */
|
|
193
175
|
export async function getDevice(name) {
|
|
@@ -309,7 +291,7 @@ export async function addIgnored(name) {
|
|
|
309
291
|
return withRegistryLock(p, async () => {
|
|
310
292
|
const set = await loadIgnored();
|
|
311
293
|
set.add(name);
|
|
312
|
-
|
|
294
|
+
atomicWriteJsonSync(p, { ignored: [...set].sort(), updatedAt: new Date().toISOString() });
|
|
313
295
|
return set;
|
|
314
296
|
});
|
|
315
297
|
}
|
|
@@ -321,7 +303,7 @@ export async function removeIgnored(name) {
|
|
|
321
303
|
const set = await loadIgnored();
|
|
322
304
|
if (!set.delete(name))
|
|
323
305
|
return false;
|
|
324
|
-
|
|
306
|
+
atomicWriteJsonSync(p, { ignored: [...set].sort(), updatedAt: new Date().toISOString() });
|
|
325
307
|
return true;
|
|
326
308
|
});
|
|
327
309
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import type { AgentId } from './types.js';
|
|
23
23
|
import type { DiscoveredPlugin } from './types.js';
|
|
24
|
-
import { type HookWiringReport } from './hooks.js';
|
|
24
|
+
import { type HookWiringReport } from './hooks/install.js';
|
|
25
25
|
import { type ResourceInventory } from './resource-inventory.js';
|
|
26
26
|
export type DoctorKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'permissions' | 'subagents' | 'plugins' | 'promptcuts';
|
|
27
27
|
export type DiffStatus = 'ok' | 'diff' | 'missing' | 'extra';
|
package/dist/lib/doctor-diff.js
CHANGED
|
@@ -33,7 +33,7 @@ import { shouldInstallCommandAsSkill, commandSkillMatches, commandSkillName } fr
|
|
|
33
33
|
import { gooseCommandMatches, gooseCommandsDir } from './goose-commands.js';
|
|
34
34
|
import { supports } from './capabilities.js';
|
|
35
35
|
import { listSkillsInVersionHome, getVersionSkillsDir } from './plugins/skills.js';
|
|
36
|
-
import { listHookEntriesFromDir } from './hooks.js';
|
|
36
|
+
import { listHookEntriesFromDir } from './hooks/install.js';
|
|
37
37
|
import { getResourceInventory } from './resource-inventory.js';
|
|
38
38
|
const RULES_DOC_FILENAME = 'README.md';
|
|
39
39
|
const ALL_KINDS = [
|
package/dist/lib/drift-sync.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Interactive drift-sync flow — the single "we detected drift, want to fix it?"
|
|
3
|
-
* action, shared by `agents status`, `agents doctor`, and the menu-bar "NEEDS
|
|
3
|
+
* action, shared by `agents sync status`, `agents doctor`, and the menu-bar "NEEDS
|
|
4
4
|
* SYNC" row.
|
|
5
5
|
*
|
|
6
6
|
* It composes existing pieces, re-implementing nothing:
|
package/dist/lib/drift-sync.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Interactive drift-sync flow — the single "we detected drift, want to fix it?"
|
|
3
|
-
* action, shared by `agents status`, `agents doctor`, and the menu-bar "NEEDS
|
|
3
|
+
* action, shared by `agents sync status`, `agents doctor`, and the menu-bar "NEEDS
|
|
4
4
|
* SYNC" row.
|
|
5
5
|
*
|
|
6
6
|
* It composes existing pieces, re-implementing nothing:
|
|
@@ -115,7 +115,7 @@ export async function promptDriftSync(opts = {}) {
|
|
|
115
115
|
if (opts.yes || !isInteractiveTerminal()) {
|
|
116
116
|
if (!opts.yes) {
|
|
117
117
|
// Non-TTY without --yes: report, don't act, don't throw.
|
|
118
|
-
console.log(chalk.gray('\nRun `agents status --yes` to sync, or `agents status` in a terminal to choose.'));
|
|
118
|
+
console.log(chalk.gray('\nRun `agents sync status --yes` to sync, or `agents sync status` in a terminal to choose.'));
|
|
119
119
|
return base;
|
|
120
120
|
}
|
|
121
121
|
const systemPulled = await pullSystem(status);
|
package/dist/lib/drift.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface DriftSummary {
|
|
|
62
62
|
* True when the install is out of sync: any installed version is stale,
|
|
63
63
|
* never-synced, carries unwired hooks, or has a broken generated hook runtime,
|
|
64
64
|
* OR a source layer is behind origin.
|
|
65
|
-
* `agents doctor` surfaces it as "run `agents status`"; `agents doctor --check`
|
|
65
|
+
* `agents doctor` surfaces it as "run `agents sync status`"; `agents doctor --check`
|
|
66
66
|
* maps it to a non-zero exit. Orphans are a `prune` concern, not sync drift, so they do
|
|
67
67
|
* NOT set this flag (mirrors the sync-status engine: an orphan alone never
|
|
68
68
|
* flags needsSync).
|
package/dist/lib/drift.js
CHANGED
|
@@ -4,7 +4,7 @@ import { loadManifest, isStale } from './staleness/index.js';
|
|
|
4
4
|
import { diffVersionResources } from './doctor-diff.js';
|
|
5
5
|
import { diffVersionCommands, iterCommandsCapableVersions } from './commands.js';
|
|
6
6
|
import { diffVersionSkills, iterSkillsCapableVersions } from './plugins/skills.js';
|
|
7
|
-
import { iterHooksCapableVersions, listUnmanagedHooksInVersionHome, checkVersionHookWiring } from './hooks.js';
|
|
7
|
+
import { iterHooksCapableVersions, listUnmanagedHooksInVersionHome, checkVersionHookWiring } from './hooks/install.js';
|
|
8
8
|
import { commitsBehindUpstream } from './git.js';
|
|
9
9
|
import { getUserAgentsDir, getSystemAgentsDir, getEnabledExtraRepos } from './state.js';
|
|
10
10
|
// Lines naming exactly what's out of sync for a version, plugins prioritized:
|