@phnx-labs/agents-cli 1.22.114 → 1.22.116
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 +245 -0
- package/README.md +22 -102
- package/dist/browser.js +0 -0
- package/dist/cli/command-registry.js +0 -7
- package/dist/commands/accounts.d.ts +3 -77
- package/dist/commands/accounts.js +13 -366
- package/dist/commands/audit.js +1 -1
- package/dist/commands/auth.js +0 -2
- package/dist/commands/browser.d.ts +53 -0
- package/dist/commands/browser.js +210 -9
- package/dist/commands/doctor.d.ts +2 -3
- package/dist/commands/doctor.js +0 -11
- package/dist/commands/events.d.ts +1 -3
- package/dist/commands/events.js +4 -7
- package/dist/commands/exec.d.ts +0 -1
- package/dist/commands/exec.js +214 -148
- package/dist/commands/feed-watch.js +52 -8
- package/dist/commands/feed.js +65 -14
- package/dist/commands/focus.js +1 -1
- package/dist/commands/go.d.ts +0 -17
- package/dist/commands/go.js +2 -19
- package/dist/commands/logs.js +1 -1
- package/dist/commands/mcp.js +8 -83
- package/dist/commands/memory.js +4 -47
- package/dist/commands/message.js +4 -4
- package/dist/commands/plugins.js +1 -93
- package/dist/commands/repo.js +0 -44
- package/dist/commands/resume.js +4 -1
- package/dist/commands/secrets-passthrough.js +2 -2
- package/dist/commands/send.d.ts +4 -4
- package/dist/commands/send.js +6 -51
- package/dist/commands/sessions-backup-setup.js +1 -1
- package/dist/commands/sessions-picker.d.ts +11 -0
- package/dist/commands/sessions-picker.js +88 -7
- package/dist/commands/sessions-resume.js +0 -1
- package/dist/commands/sessions-share.d.ts +5 -7
- package/dist/commands/sessions-share.js +98 -49
- package/dist/commands/sessions.d.ts +21 -2
- package/dist/commands/sessions.js +158 -11
- package/dist/commands/setup-browser.js +18 -2
- package/dist/commands/setup-computer.js +20 -6
- package/dist/commands/setup-secrets.d.ts +2 -2
- package/dist/commands/setup-secrets.js +24 -4
- package/dist/commands/setup-term.d.ts +24 -0
- package/dist/commands/setup-term.js +70 -0
- package/dist/commands/setup-terminal.d.ts +3 -0
- package/dist/commands/setup-terminal.js +22 -0
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +29 -11
- package/dist/commands/skills.js +0 -8
- package/dist/commands/ssh.d.ts +6 -0
- package/dist/commands/ssh.js +93 -302
- package/dist/commands/sync.js +14 -5
- package/dist/commands/teams.js +1 -1
- package/dist/commands/traces.js +1 -1
- package/dist/lib/accounting/rotate.d.ts +63 -1
- package/dist/lib/accounting/rotate.js +56 -0
- package/dist/lib/accounts/add.d.ts +0 -5
- package/dist/lib/accounts/add.js +3 -9
- package/dist/lib/accounts/slots.js +32 -2
- package/dist/lib/answer-router.d.ts +11 -2
- package/dist/lib/answer-router.js +26 -2
- package/dist/lib/artifacts-client.d.ts +20 -0
- package/dist/lib/artifacts-client.js +46 -0
- package/dist/lib/auth-mint.d.ts +5 -4
- package/dist/lib/auth-mint.js +6 -5
- package/dist/lib/browser/drivers/arc.d.ts +1 -1
- package/dist/lib/browser/runtime-state.d.ts +55 -0
- package/dist/lib/browser/runtime-state.js +99 -18
- package/dist/lib/browser/service.d.ts +10 -0
- package/dist/lib/browser/service.js +229 -37
- package/dist/lib/browser/types.d.ts +18 -0
- package/dist/lib/cli-resources.js +3 -1
- package/dist/lib/cloud/dispatch.js +1 -1
- package/dist/lib/cloudflare/creds.d.ts +10 -0
- package/dist/lib/cloudflare/creds.js +46 -0
- package/dist/lib/cloudflare/provision.d.ts +35 -0
- package/dist/lib/cloudflare/provision.js +144 -0
- package/dist/lib/computer/sessions-list.d.ts +55 -0
- package/dist/lib/computer/sessions-list.js +168 -1
- package/dist/lib/config-keys.d.ts +1 -1
- package/dist/lib/config-keys.js +5 -0
- package/dist/lib/daemon/daemon.js +8 -1
- package/dist/lib/daemon/feed-stream-service.d.ts +23 -0
- package/dist/lib/daemon/feed-stream-service.js +40 -0
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/device-config.js +61 -0
- package/dist/lib/devices/connect.d.ts +49 -5
- package/dist/lib/devices/connect.js +169 -21
- package/dist/lib/devices/doctor-findings.js +2 -6
- package/dist/lib/feed/answer.d.ts +153 -4
- package/dist/lib/feed/answer.js +716 -105
- package/dist/lib/feed/envelope.d.ts +79 -0
- package/dist/lib/feed/envelope.js +23 -0
- package/dist/lib/feed/events.d.ts +6 -0
- package/dist/lib/feed/events.js +8 -0
- package/dist/lib/feed/feed.d.ts +61 -1
- package/dist/lib/feed/feed.js +226 -14
- package/dist/lib/feed/hub-server.d.ts +141 -0
- package/dist/lib/feed/hub-server.js +586 -0
- package/dist/lib/feed/hub.d.ts +95 -0
- package/dist/lib/feed/hub.js +255 -0
- package/dist/lib/feed/pr-status.d.ts +8 -0
- package/dist/lib/feed/pr-status.js +9 -1
- package/dist/lib/feed/tool-activity.d.ts +108 -0
- package/dist/lib/feed/tool-activity.js +313 -0
- package/dist/lib/feed/tools.d.ts +198 -0
- package/dist/lib/feed/tools.js +265 -0
- package/dist/lib/feed/watch.d.ts +50 -50
- package/dist/lib/feed/watch.js +147 -16
- package/dist/lib/feed-outcome.d.ts +1 -1
- package/dist/lib/feed-outcome.js +9 -2
- package/dist/lib/feed-policy.js +9 -3
- package/dist/lib/fleet/auth-sync.d.ts +2 -55
- package/dist/lib/fleet/auth-sync.js +2 -89
- package/dist/lib/format.d.ts +1 -1
- package/dist/lib/format.js +1 -1
- package/dist/lib/git.d.ts +0 -16
- package/dist/lib/git.js +0 -58
- package/dist/lib/harness-auth-capabilities.js +7 -2
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +20 -1
- package/dist/lib/hosts/dispatch.js +52 -30
- package/dist/lib/hosts/remote-cmd.d.ts +72 -1
- package/dist/lib/hosts/remote-cmd.js +151 -10
- package/dist/lib/hosts/remote-cmd.test-fixture.d.ts +2 -0
- package/dist/lib/hosts/remote-cmd.test-fixture.js +22 -0
- package/dist/lib/mailbox.d.ts +12 -0
- package/dist/lib/mailbox.js +16 -2
- package/dist/lib/mcp.js +17 -11
- package/dist/lib/menubar/snapshot.d.ts +51 -0
- package/dist/lib/menubar/snapshot.js +42 -3
- package/dist/lib/open-url.js +2 -2
- package/dist/lib/probe.d.ts +4 -1
- package/dist/lib/probe.js +5 -2
- package/dist/lib/projects.d.ts +23 -0
- package/dist/lib/projects.js +78 -0
- package/dist/lib/pwsh.d.ts +33 -0
- package/dist/lib/pwsh.js +56 -0
- package/dist/lib/redact.d.ts +8 -0
- package/dist/lib/redact.js +11 -0
- package/dist/lib/refresh.d.ts +6 -2
- package/dist/lib/refresh.js +92 -72
- package/dist/lib/secrets-cli.d.ts +11 -0
- package/dist/lib/secrets-cli.js +30 -0
- package/dist/lib/secrets-client.js +3 -2
- package/dist/lib/session/active.d.ts +1 -0
- package/dist/lib/session/active.js +8 -0
- package/dist/lib/session/db.d.ts +67 -3
- package/dist/lib/session/db.js +381 -126
- package/dist/lib/session/detached.d.ts +7 -0
- package/dist/lib/session/detached.js +29 -0
- package/dist/lib/session/prompt.d.ts +23 -7
- package/dist/lib/session/prompt.js +46 -8
- package/dist/lib/session/remote/peer-stream.d.ts +24 -2
- package/dist/lib/session/remote/peer-stream.js +33 -6
- package/dist/lib/session/remote/remote-list.d.ts +20 -0
- package/dist/lib/session/remote/remote-list.js +22 -6
- package/dist/lib/session/remote/watch.d.ts +12 -0
- package/dist/lib/session/remote/watch.js +9 -0
- package/dist/lib/session/remote-preview-cache.d.ts +29 -0
- package/dist/lib/session/remote-preview-cache.js +373 -0
- package/dist/lib/session/sync/backend.d.ts +3 -3
- package/dist/lib/session/sync/backend.js +3 -3
- package/dist/lib/session/sync/provision.d.ts +1 -1
- package/dist/lib/session/sync/provision.js +2 -2
- package/dist/lib/session/tail.d.ts +50 -0
- package/dist/lib/session/tail.js +219 -0
- package/dist/lib/sessions-client.js +0 -3
- package/dist/lib/setup-tool-install.d.ts +3 -0
- package/dist/lib/setup-tool-install.js +27 -0
- package/dist/lib/setup-tool-status.d.ts +22 -0
- package/dist/lib/setup-tool-status.js +220 -0
- package/dist/lib/share-runtime.d.ts +11 -0
- package/dist/lib/share-runtime.js +63 -0
- package/dist/lib/signin-badge.d.ts +19 -4
- package/dist/lib/signin-badge.js +29 -11
- package/dist/lib/smart-launch.d.ts +1 -5
- package/dist/lib/smart-launch.js +3 -11
- package/dist/lib/ssh-exec.d.ts +44 -0
- package/dist/lib/ssh-exec.js +119 -0
- package/dist/lib/startup/command-registry.d.ts +6 -4
- package/dist/lib/startup/command-registry.js +10 -7
- package/dist/lib/state.js +2 -2
- package/dist/lib/storage/selection.d.ts +2 -2
- package/dist/lib/storage/selection.js +2 -2
- package/dist/lib/sync-umbrella.d.ts +5 -0
- package/dist/lib/sync-umbrella.js +18 -10
- package/dist/lib/term-driver.d.ts +24 -0
- package/dist/lib/term-driver.js +36 -0
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/index.js +1 -1
- package/dist/lib/terminal/inject.d.ts +38 -0
- package/dist/lib/terminal/inject.js +55 -9
- package/dist/lib/terminal/transport.d.ts +15 -5
- package/dist/lib/terminal/transport.js +61 -11
- package/dist/lib/traces/backend.d.ts +1 -2
- package/dist/lib/traces/backend.js +1 -2
- package/dist/lib/traces/provision.d.ts +1 -1
- package/dist/lib/traces/provision.js +2 -2
- package/dist/lib/types.d.ts +8 -6
- package/package.json +2 -3
- package/dist/commands/artifacts-setup.d.ts +0 -53
- package/dist/commands/artifacts-setup.js +0 -161
- package/dist/commands/artifacts.d.ts +0 -18
- package/dist/commands/artifacts.js +0 -58
- package/dist/commands/attach.d.ts +0 -12
- package/dist/commands/attach.js +0 -86
- package/dist/commands/auth-mint.d.ts +0 -12
- package/dist/commands/auth-mint.js +0 -108
- package/dist/commands/reconnect.d.ts +0 -46
- package/dist/commands/reconnect.js +0 -115
- package/dist/commands/share.d.ts +0 -293
- package/dist/commands/share.js +0 -1424
- package/dist/lib/fleet/remote-login.d.ts +0 -170
- package/dist/lib/fleet/remote-login.js +0 -568
- package/dist/lib/share/analytics.d.ts +0 -13
- package/dist/lib/share/analytics.js +0 -45
- package/dist/lib/share/backend.d.ts +0 -120
- package/dist/lib/share/backend.js +0 -176
- package/dist/lib/share/capture.d.ts +0 -31
- package/dist/lib/share/capture.js +0 -174
- package/dist/lib/share/config.d.ts +0 -72
- package/dist/lib/share/config.js +0 -211
- package/dist/lib/share/delete.d.ts +0 -123
- package/dist/lib/share/delete.js +0 -173
- package/dist/lib/share/html.d.ts +0 -20
- package/dist/lib/share/html.js +0 -88
- package/dist/lib/share/http-error.d.ts +0 -53
- package/dist/lib/share/http-error.js +0 -65
- package/dist/lib/share/og.d.ts +0 -26
- package/dist/lib/share/og.js +0 -84
- package/dist/lib/share/provision.d.ts +0 -127
- package/dist/lib/share/provision.js +0 -285
- package/dist/lib/share/publish.d.ts +0 -379
- package/dist/lib/share/publish.js +0 -818
- package/dist/lib/share/worker-template.d.ts +0 -27
- package/dist/lib/share/worker-template.js +0 -2424
- package/dist/lib/storage/index.d.ts +0 -14
- package/dist/lib/storage/index.js +0 -14
- package/dist/lib/storage/visibility.d.ts +0 -82
- package/dist/lib/storage/visibility.js +0 -99
package/dist/commands/exec.js
CHANGED
|
@@ -660,8 +660,6 @@ export function registerRunCommand(program) {
|
|
|
660
660
|
// `--on` and `--computer` are hidden aliases of `--device` — same behavior.
|
|
661
661
|
runCmd.addOption(new Option('--on <name>', 'Alias of --device.').hideHelp());
|
|
662
662
|
runCmd.addOption(new Option('--computer <name>', 'Alias of --device.').hideHelp());
|
|
663
|
-
// Deprecated one-release alias: `agents run … --smart` → treat as `--device auto`.
|
|
664
|
-
runCmd.addOption(new Option('--smart', 'Deprecated: use --device auto (affinity host pick).').hideHelp());
|
|
665
663
|
// Internal: the `--device` dispatch forwards this so the REMOTE run prints its
|
|
666
664
|
// resolved session id as a one-line stdout sentinel (hosts/session-marker.ts),
|
|
667
665
|
// letting the launcher relate the remote-created session back to itself for
|
|
@@ -1226,7 +1224,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1226
1224
|
const defaultPlacement = bareInteractiveRunDefaultsToDeviceAuto(options, { prompt, devicePickerRequested }, { tty: isInteractiveTerminal(), json: options.json });
|
|
1227
1225
|
if (defaultPlacement)
|
|
1228
1226
|
options.device = 'auto';
|
|
1229
|
-
// --device auto
|
|
1227
|
+
// --device auto: live fleet pick.
|
|
1230
1228
|
// Harness is always the agent the user typed — never auto-picked.
|
|
1231
1229
|
// Placement failure propagates; an automatic request never becomes local.
|
|
1232
1230
|
{
|
|
@@ -1252,9 +1250,6 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1252
1250
|
console.error(chalk.gray(`Run here instead: agents run ${runBaseAgentName} --device ${machineId()}`));
|
|
1253
1251
|
process.exit(1);
|
|
1254
1252
|
}
|
|
1255
|
-
if (!options.quiet && result.deprecationSmart) {
|
|
1256
|
-
process.stderr.write(chalk.yellow('[agents] --smart is deprecated; use --device auto\n'));
|
|
1257
|
-
}
|
|
1258
1253
|
if (!options.quiet && result.banner) {
|
|
1259
1254
|
const { hostLabel, deviceHint, acctNote } = result.banner;
|
|
1260
1255
|
process.stderr.write(chalk.gray(`[agents] device=auto → ${hostLabel}` +
|
|
@@ -2050,7 +2045,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2050
2045
|
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, implicitModeFor, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, readProfile, resolveProfileForRun },
|
|
2051
2046
|
// Engine DATA ops resolve through the standalone `secrets` process client
|
|
2052
2047
|
// (PHNX-3989); the agents-owned `bundle@host` policy helpers stay in agents-cli.
|
|
2053
|
-
{ readAndResolveBundleEnv, describeBundle, remoteResolveEnv }, { assertRemoteBundleFlagsUnsupported, splitBundleRef, resolveSecretsContextForRun }, { resolveHostSshTarget }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, rotationFailoverChain, shouldArmRotationFailover, RUN_STRATEGIES, collectHarnessCandidates, pickHarnessWeighted, classifyHarnessCandidates, formatHarnessPickBanner, formatNoHealthyHarnessError, formatNoHealthyAccountError, formatNoVerifiedUsageError, signInRecoverableCandidates }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias, ensureAgentRunnable }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef, resolveAllowedSubagents, pruneStaleWorkflowSubagents, ensureSubagentDispatchTool }, { resolveRunDefaults }, { getMcpServersByName, buildWorkflowMcpConfig }, { supports, capableAgents }, { shareRuntimeEnv },] = await Promise.all([
|
|
2048
|
+
{ readAndResolveBundleEnv, describeBundle, remoteResolveEnv }, { assertRemoteBundleFlagsUnsupported, splitBundleRef, resolveSecretsContextForRun }, { resolveHostSshTarget }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, rotationFailoverChain, DEFAULT_ROTATION_FAILOVER_LIMIT, shouldArmRotationFailover, preflightFallbackHandoff, preflightHandoffEligible, RUN_STRATEGIES, collectHarnessCandidates, pickHarnessWeighted, classifyHarnessCandidates, formatHarnessPickBanner, formatNoHealthyHarnessError, formatNoHealthyAccountError, formatNoVerifiedUsageError, signInRecoverableCandidates }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias, ensureAgentRunnable }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef, resolveAllowedSubagents, pruneStaleWorkflowSubagents, ensureSubagentDispatchTool }, { resolveRunDefaults }, { getMcpServersByName, buildWorkflowMcpConfig }, { supports, capableAgents }, { shareRuntimeEnv },] = await Promise.all([
|
|
2054
2049
|
import('../lib/exec.js'),
|
|
2055
2050
|
import('../lib/agents.js'),
|
|
2056
2051
|
import('../lib/profiles.js'),
|
|
@@ -2064,7 +2059,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2064
2059
|
import('../lib/run-defaults.js'),
|
|
2065
2060
|
import('../lib/mcp.js'),
|
|
2066
2061
|
import('../lib/capabilities.js'),
|
|
2067
|
-
import('../lib/share
|
|
2062
|
+
import('../lib/share-runtime.js'),
|
|
2068
2063
|
]);
|
|
2069
2064
|
bootMark('run-deps:imported');
|
|
2070
2065
|
const isValidAgent = (agent) => ALL_AGENT_IDS.includes(agent);
|
|
@@ -2595,6 +2590,21 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2595
2590
|
// synthesize a same-agent fallback chain from the other healthy accounts
|
|
2596
2591
|
// (issue #348). Stays null unless a non-pinned strategy actually rotated.
|
|
2597
2592
|
let rotationResult = null;
|
|
2593
|
+
/**
|
|
2594
|
+
* The PRIMARY harness's configured run-default mode/effort, carried across a
|
|
2595
|
+
* preflight alternate-harness handoff (PHNX-3999 F19).
|
|
2596
|
+
*
|
|
2597
|
+
* Run defaults are resolved after the harness is known, so without this a
|
|
2598
|
+
* handoff would silently adopt the ALTERNATE's configured defaults — a
|
|
2599
|
+
* primary configured `mode: plan` becoming the alternate's `mode: skip`
|
|
2600
|
+
* escalates a read-only intent into an unattended writable run. The
|
|
2601
|
+
* canonical mid-run cascade keeps the primary's resolved mode for the same
|
|
2602
|
+
* reason (`runWithFallback` forwards `options.mode` and only re-derives the
|
|
2603
|
+
* mode that was never configured at all). An IMPLICIT mode is deliberately
|
|
2604
|
+
* not carried: it is the harness's own safe default, so the alternate's
|
|
2605
|
+
* applies (identical to `modeWasImplicit ? implicitModeFor(agent)`).
|
|
2606
|
+
*/
|
|
2607
|
+
let handoffRunDefaults;
|
|
2598
2608
|
// Precomputed launchable-signed-in verdict for the ACTUAL launched
|
|
2599
2609
|
// candidate, fed to the pre-launch `run.launch` event so it need not
|
|
2600
2610
|
// re-probe. Sourced per resolution branch from the candidate that WON, not
|
|
@@ -2627,155 +2637,202 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2627
2637
|
// account and account rotation silently stops (the gh-monitor heal bug).
|
|
2628
2638
|
// `pinned` still resolves so a logged-out default can yield to a signed-in
|
|
2629
2639
|
// sibling on this device (PHNX-2685); an explicit @version pin is unchanged.
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
if (
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2640
|
+
// Bounded handoff loop: each iteration resolves an account for `agent`, and
|
|
2641
|
+
// the only `continue` is the preflight exhaustion handoff below, which
|
|
2642
|
+
// consumes one entry of the `--fallback` spec before retrying as that
|
|
2643
|
+
// alternate. The spec is finite, so this terminates (PHNX-3999 F19).
|
|
2644
|
+
preflight: for (;;) {
|
|
2645
|
+
if (!accountPickerRequested && !configuredAccount && (!version || strategy !== 'pinned' || options.balanced || explicitStrategy)) {
|
|
2646
|
+
if (version) {
|
|
2647
|
+
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} ignored: version ${version} is pinned\n`));
|
|
2648
|
+
}
|
|
2649
|
+
else if (fromProfile) {
|
|
2650
|
+
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} ignored: custom harness pins its own version/auth\n`));
|
|
2651
|
+
}
|
|
2652
|
+
else {
|
|
2653
|
+
try {
|
|
2654
|
+
// Account-centric candidate list: native version-home logins PLUS
|
|
2655
|
+
// provider accounts (setup-token / API-key) that can auth this agent,
|
|
2656
|
+
// so `--strategy balanced` spreads across ALL accounts, not just the
|
|
2657
|
+
// ones sitting in a version home (RUSH-3182). Run-path only — the
|
|
2658
|
+
// picker's other callers keep the native-only collector.
|
|
2659
|
+
const { collectRunCandidatesForRun } = await import('../lib/accounting/account-pool-collect.js');
|
|
2660
|
+
bootMark('resolve-version:start');
|
|
2661
|
+
const routingModel = options.model ?? resolvedResumeSource?.model ?? workflowModel
|
|
2662
|
+
?? (options.fallback ? undefined : resolveRunDefaults(agent, resolveVersion(agent, cwd), cwd).model);
|
|
2663
|
+
const resolved = await resolveRunVersion(agent, strategy, cwd, collectRunCandidatesForRun, routingModel);
|
|
2664
|
+
bootMark('resolve-version:done');
|
|
2665
|
+
if (resolved.exhausted) {
|
|
2666
|
+
// Zero healthy accounts splits two ways, and conflating them is what
|
|
2667
|
+
// stranded a logged-out harness with no way in at all (RUSH-2334):
|
|
2668
|
+
//
|
|
2669
|
+
// - THROTTLED (rate_limited / out_of_credits) -> fail loud (RUSH-2132).
|
|
2670
|
+
// The old behavior warned "found no usable version; falling back to
|
|
2671
|
+
// defaults" and launched the pinned default anyway — the exact move
|
|
2672
|
+
// that loops a rotate into an exhausted account. Only a window reset
|
|
2673
|
+
// clears it. The message text is a contract the Factory watchdog
|
|
2674
|
+
// tail-detects; do not reword it.
|
|
2675
|
+
// - NEEDS A SIGN-IN (signed_out / revoked) -> launching IS the fix,
|
|
2676
|
+
// because the harness's own TUI is the login surface. So on a TTY we
|
|
2677
|
+
// carry the user into that login instead of erroring. Exiting here
|
|
2678
|
+
// made `agents run <agent>`, `agents run <agent>#`, and `agents use`
|
|
2679
|
+
// all dead-end with no reachable way to authenticate.
|
|
2680
|
+
const recoverable = signInRecoverableCandidates(resolved.exhausted);
|
|
2681
|
+
const { signInLaunchDecision } = await import('./run-account-picker.js');
|
|
2682
|
+
const decision = signInLaunchDecision({
|
|
2683
|
+
recoverable: recoverable.length,
|
|
2684
|
+
tty: isInteractiveTerminal(),
|
|
2685
|
+
json: options.json === true,
|
|
2686
|
+
});
|
|
2687
|
+
if (decision === 'launch') {
|
|
2688
|
+
const { pickSignInLaunchVersion } = await import('./run-account-picker.js');
|
|
2689
|
+
const signInVersion = await pickSignInLaunchVersion(agent, recoverable, !!options.quiet);
|
|
2690
|
+
// A cancelled prompt launches nothing — same contract as the
|
|
2691
|
+
// trailing-# account picker above.
|
|
2692
|
+
if (!signInVersion)
|
|
2693
|
+
return;
|
|
2694
|
+
version = signInVersion;
|
|
2695
|
+
// We just told the user this account is logged out and why we're
|
|
2696
|
+
// launching it, so suppress the downstream login preflight — it
|
|
2697
|
+
// would print a second, near-identical "looks logged out" warning.
|
|
2698
|
+
signInLaunch = true;
|
|
2699
|
+
}
|
|
2700
|
+
else {
|
|
2701
|
+
// Every account of this harness is throttled. A configured
|
|
2702
|
+
// alternate exists for exactly this case, so hand off to it here
|
|
2703
|
+
// rather than exiting — `runWithFallback` would only reach it
|
|
2704
|
+
// after a run that can no longer start (PHNX-3999 F19). The
|
|
2705
|
+
// alternate becomes the primary and is dropped from the spec, so
|
|
2706
|
+
// the chain that remains never lists the agent now running.
|
|
2707
|
+
//
|
|
2708
|
+
// ONLY for a run shape `--fallback` is valid on. The handoff
|
|
2709
|
+
// consumes the entry it uses, and the canonical --fallback
|
|
2710
|
+
// validation runs later (it rejects interactive/--acp/--loop/
|
|
2711
|
+
// --resume-checkpoint chains and requires a prompt) — so
|
|
2712
|
+
// switching first on an ineligible shape would empty the spec,
|
|
2713
|
+
// leave that guard nothing to reject, and start the alternate
|
|
2714
|
+
// harness on a run the CLI refuses today. An ineligible shape
|
|
2715
|
+
// never switches, so the spec survives and still fails there
|
|
2716
|
+
// with its own message. See preflightHandoffEligible.
|
|
2717
|
+
const handoff = preflightHandoffEligible({
|
|
2718
|
+
hasPrompt: prompt !== undefined,
|
|
2719
|
+
interactive: options.interactive === true,
|
|
2720
|
+
acp: options.acp === true,
|
|
2721
|
+
loop: options.loop === true,
|
|
2722
|
+
resumeCheckpoint: !!options.resumeCheckpoint,
|
|
2723
|
+
resume: !!options.resume,
|
|
2724
|
+
workflowScoped: !!workflowToolsRestrict || !!workflowMcpConfigPath,
|
|
2725
|
+
})
|
|
2726
|
+
? preflightFallbackHandoff(options.fallback, agent, resolved.exhausted)
|
|
2727
|
+
: null;
|
|
2728
|
+
if (handoff) {
|
|
2729
|
+
process.stderr.write(chalk.yellow(`[agents] every ${agent} account is exhausted — handing off to ${handoff.agent}${handoff.version ? `@${handoff.version}` : ''}\n`));
|
|
2730
|
+
// Snapshot the PRIMARY's configured defaults BEFORE the switch —
|
|
2731
|
+
// this is the operator's intent for this run, not the alternate's.
|
|
2732
|
+
handoffRunDefaults ??= fromProfile ? undefined : resolveRunDefaults(agent, version ?? resolveVersion(agent, cwd), cwd);
|
|
2733
|
+
agent = handoff.agent;
|
|
2734
|
+
// Alias-resolve against the NEW harness's installs, exactly as
|
|
2735
|
+
// the canonical --fallback parse does for its own entries.
|
|
2736
|
+
version = resolveVersionAlias(agent, handoff.version);
|
|
2737
|
+
options.fallback = handoff.remainingSpec;
|
|
2738
|
+
rotationResult = null;
|
|
2739
|
+
continue preflight;
|
|
2740
|
+
}
|
|
2741
|
+
console.error(chalk.red(formatNoHealthyAccountError(agent, strategy, resolved.exhausted)));
|
|
2742
|
+
if (recoverable.length > 0) {
|
|
2743
|
+
// Off a TTY nobody can complete a login, so we still exit — but
|
|
2744
|
+
// name the actual fix rather than only offering --strategy pinned,
|
|
2745
|
+
// which would just pin the same unauthenticated account.
|
|
2746
|
+
const { loginHint } = await import('../lib/signin-badge.js');
|
|
2747
|
+
console.error(chalk.gray(`To sign in: ${loginHint(agent)} — or run \`agents run ${agent}\` from a terminal.`));
|
|
2748
|
+
}
|
|
2749
|
+
process.exit(1);
|
|
2750
|
+
}
|
|
2684
2751
|
}
|
|
2685
|
-
else {
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2752
|
+
else if (resolved.noVerifiedUsage) {
|
|
2753
|
+
// Entirely stale usage (PHNX-2526): every eligible account carries
|
|
2754
|
+
// a stale-but-present usage number and none is verified, so the
|
|
2755
|
+
// route would be a guess. NEVER auto-pick it. Interactive: show the
|
|
2756
|
+
// account picker so a human chooses with the (stale) numbers in
|
|
2757
|
+
// view. Unattended: fail loud with NO_VERIFIED_USAGE. The stale
|
|
2758
|
+
// pool survives ONLY as `resolved.rotation.healthy` for bounded
|
|
2759
|
+
// post-rejection failover, never as the initial pick.
|
|
2760
|
+
const { noVerifiedUsageDecision, pickRunAccountCandidate } = await import('./run-account-picker.js');
|
|
2761
|
+
const decision = noVerifiedUsageDecision({
|
|
2762
|
+
tty: isInteractiveTerminal(),
|
|
2763
|
+
json: options.json === true,
|
|
2764
|
+
headless: options.headless === true,
|
|
2765
|
+
});
|
|
2766
|
+
if (decision === 'picker') {
|
|
2767
|
+
const selected = await pickRunAccountCandidate(agent);
|
|
2768
|
+
// A cancelled picker launches nothing — same contract as the
|
|
2769
|
+
// trailing-# account picker and the sign-in launch above.
|
|
2770
|
+
if (!selected)
|
|
2771
|
+
return;
|
|
2772
|
+
version = selected.version;
|
|
2773
|
+
// Source the run.launch verdict from the account the user ACTUALLY
|
|
2774
|
+
// picked — the picker may deliberately return a logged-out one
|
|
2775
|
+
// (RUSH-2334), so it can differ from rotationResult.picked.
|
|
2776
|
+
launchSignedIn = selected.signedIn;
|
|
2777
|
+
launchEmail = selected.email;
|
|
2778
|
+
// Keep the rotation so mid-run failover can still cascade across
|
|
2779
|
+
// the other (stale) healthy accounts after a real rejection.
|
|
2780
|
+
rotationResult = resolved.rotation;
|
|
2781
|
+
if (!options.quiet) {
|
|
2782
|
+
const identity = selected.accountLabel || 'signed-in account';
|
|
2783
|
+
process.stderr.write(chalk.gray(`[agents] no fresh usage for any ${agent} account — you picked ${identity} · ${agent}@${selected.version}\n`));
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
else {
|
|
2787
|
+
console.error(chalk.red(formatNoVerifiedUsageError(agent, strategy, resolved.rotation?.healthy ?? [])));
|
|
2788
|
+
process.exit(1);
|
|
2693
2789
|
}
|
|
2694
|
-
process.exit(1);
|
|
2695
2790
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
// Entirely stale usage (PHNX-2526): every eligible account carries
|
|
2699
|
-
// a stale-but-present usage number and none is verified, so the
|
|
2700
|
-
// route would be a guess. NEVER auto-pick it. Interactive: show the
|
|
2701
|
-
// account picker so a human chooses with the (stale) numbers in
|
|
2702
|
-
// view. Unattended: fail loud with NO_VERIFIED_USAGE. The stale
|
|
2703
|
-
// pool survives ONLY as `resolved.rotation.healthy` for bounded
|
|
2704
|
-
// post-rejection failover, never as the initial pick.
|
|
2705
|
-
const { noVerifiedUsageDecision, pickRunAccountCandidate } = await import('./run-account-picker.js');
|
|
2706
|
-
const decision = noVerifiedUsageDecision({
|
|
2707
|
-
tty: isInteractiveTerminal(),
|
|
2708
|
-
json: options.json === true,
|
|
2709
|
-
headless: options.headless === true,
|
|
2710
|
-
});
|
|
2711
|
-
if (decision === 'picker') {
|
|
2712
|
-
const selected = await pickRunAccountCandidate(agent);
|
|
2713
|
-
// A cancelled picker launches nothing — same contract as the
|
|
2714
|
-
// trailing-# account picker and the sign-in launch above.
|
|
2715
|
-
if (!selected)
|
|
2716
|
-
return;
|
|
2717
|
-
version = selected.version;
|
|
2718
|
-
// Source the run.launch verdict from the account the user ACTUALLY
|
|
2719
|
-
// picked — the picker may deliberately return a logged-out one
|
|
2720
|
-
// (RUSH-2334), so it can differ from rotationResult.picked.
|
|
2721
|
-
launchSignedIn = selected.signedIn;
|
|
2722
|
-
launchEmail = selected.email;
|
|
2723
|
-
// Keep the rotation so mid-run failover can still cascade across
|
|
2724
|
-
// the other (stale) healthy accounts after a real rejection.
|
|
2791
|
+
else if (resolved.version) {
|
|
2792
|
+
version = resolved.version;
|
|
2725
2793
|
rotationResult = resolved.rotation;
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2794
|
+
// The auto-pick already computed the launchable-signed-in verdict for
|
|
2795
|
+
// this exact version via the same gate — reuse it for run.launch.
|
|
2796
|
+
if (resolved.rotation) {
|
|
2797
|
+
launchSignedIn = resolved.rotation.picked.signedIn;
|
|
2798
|
+
launchEmail = resolved.rotation.picked.email;
|
|
2729
2799
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
// API-key) carries `providerAccount`. Resolve its env through the
|
|
2747
|
-
// same `resolveSpawnAccount` path an explicit `--account` uses, so
|
|
2748
|
-
// exec injects the credential; a native pick has no providerAccount
|
|
2749
|
-
// and runs from its own version home unchanged (RUSH-3182).
|
|
2750
|
-
const pickedProviderAccount = resolved.rotation?.picked.providerAccount;
|
|
2751
|
-
if (pickedProviderAccount) {
|
|
2752
|
-
try {
|
|
2753
|
-
const picked = resolveSpawnAccount(pickedProviderAccount, agent, resolved.version, readMeta(), { useDefault: false });
|
|
2754
|
-
if (picked?.kind === 'provider')
|
|
2755
|
-
accountEnv = picked.env;
|
|
2800
|
+
// A balanced/available pick of a PROVIDER account (setup-token /
|
|
2801
|
+
// API-key) carries `providerAccount`. Resolve its env through the
|
|
2802
|
+
// same `resolveSpawnAccount` path an explicit `--account` uses, so
|
|
2803
|
+
// exec injects the credential; a native pick has no providerAccount
|
|
2804
|
+
// and runs from its own version home unchanged (RUSH-3182).
|
|
2805
|
+
const pickedProviderAccount = resolved.rotation?.picked.providerAccount;
|
|
2806
|
+
if (pickedProviderAccount) {
|
|
2807
|
+
try {
|
|
2808
|
+
const picked = resolveSpawnAccount(pickedProviderAccount, agent, resolved.version, readMeta(), { useDefault: false });
|
|
2809
|
+
if (picked?.kind === 'provider')
|
|
2810
|
+
accountEnv = picked.env;
|
|
2811
|
+
}
|
|
2812
|
+
catch (err) {
|
|
2813
|
+
console.error(chalk.red(err.message));
|
|
2814
|
+
process.exit(1);
|
|
2815
|
+
}
|
|
2756
2816
|
}
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
process.
|
|
2817
|
+
if (resolved.rotation && !options.quiet) {
|
|
2818
|
+
const banner = formatRotationBanner(resolved.rotation, strategy);
|
|
2819
|
+
process.stderr.write(chalk.gray(banner + '\n'));
|
|
2760
2820
|
}
|
|
2761
2821
|
}
|
|
2762
|
-
if (
|
|
2763
|
-
|
|
2764
|
-
|
|
2822
|
+
else if (!options.quiet) {
|
|
2823
|
+
// No installed version at all (not "accounts exhausted" — that
|
|
2824
|
+
// fails loud above): keep the pre-existing default resolution.
|
|
2825
|
+
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} found no usable ${agent} version; falling back to defaults\n`));
|
|
2765
2826
|
}
|
|
2766
2827
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
}
|
|
2772
|
-
}
|
|
2773
|
-
catch (err) {
|
|
2774
|
-
if (!options.quiet) {
|
|
2775
|
-
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} skipped: ${err.message}\n`));
|
|
2828
|
+
catch (err) {
|
|
2829
|
+
if (!options.quiet) {
|
|
2830
|
+
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} skipped: ${err.message}\n`));
|
|
2831
|
+
}
|
|
2776
2832
|
}
|
|
2777
2833
|
}
|
|
2778
2834
|
}
|
|
2835
|
+
break;
|
|
2779
2836
|
}
|
|
2780
2837
|
// Self-heal the launch target. A gutted install (JS wrapper present,
|
|
2781
2838
|
// native binary missing — a partial/raced npm extraction of the optional
|
|
@@ -2904,9 +2961,12 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2904
2961
|
// alias for 'skip' (rewritten downstream by normalizeMode in exec.ts).
|
|
2905
2962
|
let mode = options.mode;
|
|
2906
2963
|
const modeSource = runCmd.getOptionValueSource('mode');
|
|
2907
|
-
|
|
2964
|
+
// A configured mode carried across a preflight handoff wins over the
|
|
2965
|
+
// alternate harness's own configured default (see handoffRunDefaults).
|
|
2966
|
+
const configuredMode = handoffRunDefaults?.mode ?? runDefaults.mode;
|
|
2967
|
+
const modeFromRunDefault = modeSource === 'default' && !!configuredMode;
|
|
2908
2968
|
if (modeFromRunDefault) {
|
|
2909
|
-
mode =
|
|
2969
|
+
mode = configuredMode;
|
|
2910
2970
|
}
|
|
2911
2971
|
if (!['plan', 'edit', 'auto', 'skip', 'full'].includes(mode)) {
|
|
2912
2972
|
console.error(chalk.red(`Invalid mode: ${mode}. Use plan, edit, auto, or skip ('full' accepted as alias for skip).`));
|
|
@@ -2957,7 +3017,8 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2957
3017
|
process.exit(1);
|
|
2958
3018
|
}
|
|
2959
3019
|
const effortSource = runCmd.getOptionValueSource('effort');
|
|
2960
|
-
const
|
|
3020
|
+
const configuredEffort = handoffRunDefaults?.effort ?? runDefaults.effort;
|
|
3021
|
+
const effort = (effortSource === 'default' && configuredEffort ? configuredEffort : options.effort);
|
|
2961
3022
|
if (!['low', 'medium', 'high', 'xhigh', 'max', 'auto'].includes(effort)) {
|
|
2962
3023
|
console.error(chalk.red(`Invalid effort: ${effort}. Use 'low', 'medium', 'high', 'xhigh', 'max', or 'auto'`));
|
|
2963
3024
|
process.exit(1);
|
|
@@ -3195,7 +3256,12 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
3195
3256
|
loop: !!options.loop,
|
|
3196
3257
|
resumeCheckpoint: !!options.resumeCheckpoint,
|
|
3197
3258
|
})) {
|
|
3198
|
-
|
|
3259
|
+
// With an explicit alternate harness configured, "use the alternate once
|
|
3260
|
+
// the primary's accounts are exhausted" is the policy — so EVERY healthy
|
|
3261
|
+
// same-agent account has to be tried first, not the default cap of three
|
|
3262
|
+
// (PHNX-3999 F19). Without one, the cap stands: it bounds how long a
|
|
3263
|
+
// rate-limited run keeps retrying itself.
|
|
3264
|
+
const failover = rotationFailoverChain(rotationResult, version, fallback.length > 0 ? (rotationResult.healthy.length || DEFAULT_ROTATION_FAILOVER_LIMIT) : undefined);
|
|
3199
3265
|
if (failover.length > 0) {
|
|
3200
3266
|
fallback.unshift(...failover);
|
|
3201
3267
|
if (!options.quiet) {
|
|
@@ -1,10 +1,51 @@
|
|
|
1
|
-
import { machineId } from '../lib/machine-id.js';
|
|
2
1
|
import { setHelpSections } from '../lib/help.js';
|
|
3
|
-
import {
|
|
2
|
+
import { streamFeedFromHub, waitForHub } from '../lib/feed/hub-server.js';
|
|
3
|
+
import { ensureDaemonStarted } from '../lib/daemon/daemon.js';
|
|
4
|
+
/**
|
|
5
|
+
* Attach to the daemon's shared collector, starting the daemon if it is not up.
|
|
6
|
+
*
|
|
7
|
+
* There is deliberately no in-process fallback: running `watchFleetFeed` here
|
|
8
|
+
* would give this reader its own ssh child per peer, which is exactly the
|
|
9
|
+
* per-caller fan-out the hub exists to collapse (see `lib/feed/hub.ts`). So a
|
|
10
|
+
* missing hub is answered by starting its owner and WAITING for it.
|
|
11
|
+
*
|
|
12
|
+
* The wait is what makes that honest. `ensureDaemonStarted()` returns once the
|
|
13
|
+
* process is spawned, long before it has loaded its services and bound the
|
|
14
|
+
* socket, so retrying immediately raced the bind and reported a daemon that was
|
|
15
|
+
* about to be perfectly healthy as unavailable. The final error is the one from
|
|
16
|
+
* the last real attempt, not the first.
|
|
17
|
+
*/
|
|
18
|
+
async function attachToHub(signal, emit, scope) {
|
|
19
|
+
try {
|
|
20
|
+
await streamFeedFromHub({ signal, emit, scope });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
catch (first) {
|
|
24
|
+
if (signal.aborted)
|
|
25
|
+
return;
|
|
26
|
+
ensureDaemonStarted();
|
|
27
|
+
if (!await waitForHub()) {
|
|
28
|
+
throw new Error(`the shared feed stream did not come up: ${first.message}\n`
|
|
29
|
+
+ 'Next: agents daemon status, then agents daemon services enable feed-stream');
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
await streamFeedFromHub({ signal, emit, scope });
|
|
33
|
+
}
|
|
34
|
+
catch (second) {
|
|
35
|
+
throw new Error(`the shared feed stream is unavailable: ${second.message}\n`
|
|
36
|
+
+ 'Next: agents daemon status, then agents daemon services enable feed-stream');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
4
40
|
export function registerFeedWatchCommand(parent) {
|
|
5
|
-
const command = parent.command('watch').description('Stream the canonical agent, attention, and activity projection as NDJSON')
|
|
41
|
+
const command = parent.command('watch').description('Stream the canonical agent, attention, tool, and activity projection as NDJSON')
|
|
6
42
|
.option('--json', 'Emit versioned NDJSON envelopes').option('--local', 'Watch only this machine');
|
|
7
|
-
setHelpSections(command, {
|
|
43
|
+
setHelpSections(command, {
|
|
44
|
+
examples: `agents feed watch --json\nagents feed watch --json --local`,
|
|
45
|
+
notes: 'Order by streamId + sequence. Unavailable scopes retain their last rows until reset on reconnect. '
|
|
46
|
+
+ 'The fleet stream is served by the daemon\'s shared collector, so several readers cost one connection per peer; '
|
|
47
|
+
+ '--local is the per-machine stream the collector itself subscribes to over ssh.',
|
|
48
|
+
});
|
|
8
49
|
command.action(async (_opts, invoked) => {
|
|
9
50
|
const opts = invoked.optsWithGlobals();
|
|
10
51
|
if (!opts.json)
|
|
@@ -15,10 +56,13 @@ export function registerFeedWatchCommand(parent) {
|
|
|
15
56
|
process.once('SIGINT', stop);
|
|
16
57
|
process.once('SIGTERM', stop);
|
|
17
58
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
59
|
+
// BOTH scopes go through the daemon's shared collectors. `--local` used to
|
|
60
|
+
// run its own `watchLocalFeed`, so every observing box's ssh subscription
|
|
61
|
+
// built a separate activity cursor set, tool watcher and setup subscription
|
|
62
|
+
// on the SAME peer. The local collector is a distinct hub from the fleet one
|
|
63
|
+
// (`feed-stream-service.ts`) and its watcher dials no peer at all, so a
|
|
64
|
+
// local reader can never trigger a fan-out, recursively or otherwise.
|
|
65
|
+
await attachToHub(controller.signal, emit, opts.local ? 'local' : 'fleet');
|
|
22
66
|
}
|
|
23
67
|
finally {
|
|
24
68
|
process.off('SIGINT', stop);
|