@phnx-labs/agents-cli 1.20.74 → 1.20.77
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 +1055 -0
- package/README.md +36 -10
- package/dist/bin/agents +0 -0
- package/dist/commands/apply.js +21 -3
- package/dist/commands/attach.d.ts +10 -0
- package/dist/commands/attach.js +41 -0
- package/dist/commands/computer.js +3 -3
- package/dist/commands/detach-core.d.ts +46 -0
- package/dist/commands/detach-core.js +61 -0
- package/dist/commands/detach.d.ts +5 -0
- package/dist/commands/detach.js +171 -0
- package/dist/commands/doctor.js +18 -0
- package/dist/commands/exec.js +83 -13
- package/dist/commands/feed.d.ts +7 -1
- package/dist/commands/feed.js +47 -4
- package/dist/commands/go.d.ts +9 -2
- package/dist/commands/go.js +16 -6
- package/dist/commands/harness.d.ts +14 -0
- package/dist/commands/harness.js +145 -0
- package/dist/commands/import.js +38 -8
- package/dist/commands/inspect.js +5 -2
- package/dist/commands/profiles.d.ts +20 -0
- package/dist/commands/profiles.js +68 -21
- package/dist/commands/repo.js +14 -0
- package/dist/commands/routines.js +286 -15
- package/dist/commands/secrets.js +102 -37
- package/dist/commands/sessions-browser.js +12 -2
- package/dist/commands/sessions-export.js +19 -5
- package/dist/commands/sessions-migrate.d.ts +29 -0
- package/dist/commands/sessions-migrate.js +596 -0
- package/dist/commands/sessions-picker.d.ts +14 -1
- package/dist/commands/sessions-picker.js +184 -16
- package/dist/commands/sessions.d.ts +56 -14
- package/dist/commands/sessions.js +322 -62
- package/dist/commands/setup-computer.js +2 -2
- package/dist/commands/ssh.d.ts +13 -0
- package/dist/commands/ssh.js +110 -37
- package/dist/commands/status.js +10 -2
- package/dist/commands/versions.js +15 -6
- package/dist/commands/view.d.ts +5 -0
- package/dist/commands/view.js +24 -4
- package/dist/commands/watchdog.d.ts +7 -2
- package/dist/commands/watchdog.js +73 -57
- package/dist/index.js +59 -7
- package/dist/lib/activity.d.ts +17 -6
- package/dist/lib/activity.js +466 -40
- package/dist/lib/actor.d.ts +50 -0
- package/dist/lib/actor.js +166 -0
- package/dist/lib/agent-spec/provider.js +2 -1
- package/dist/lib/agent-spec/resolve.js +19 -5
- package/dist/lib/agent-spec/types.d.ts +9 -1
- package/dist/lib/agents.d.ts +21 -0
- package/dist/lib/agents.js +70 -6
- package/dist/lib/cloud/codex.d.ts +2 -0
- package/dist/lib/cloud/codex.js +14 -3
- package/dist/lib/cloud/session-index.d.ts +32 -0
- package/dist/lib/cloud/session-index.js +58 -0
- package/dist/lib/cloud/store.d.ts +7 -0
- package/dist/lib/cloud/store.js +25 -0
- package/dist/lib/config-transfer.js +4 -0
- package/dist/lib/daemon.d.ts +10 -1
- package/dist/lib/daemon.js +114 -11
- package/dist/lib/devices/connect.d.ts +15 -1
- package/dist/lib/devices/connect.js +15 -1
- package/dist/lib/devices/fleet.d.ts +37 -1
- package/dist/lib/devices/fleet.js +36 -2
- package/dist/lib/devices/health-report.d.ts +38 -0
- package/dist/lib/devices/health-report.js +214 -0
- package/dist/lib/devices/health.js +4 -1
- package/dist/lib/devices/reachability.d.ts +31 -0
- package/dist/lib/devices/reachability.js +40 -0
- package/dist/lib/devices/registry.d.ts +33 -0
- package/dist/lib/devices/registry.js +37 -0
- package/dist/lib/devices/resolve-target.d.ts +15 -27
- package/dist/lib/devices/resolve-target.js +63 -102
- package/dist/lib/devices/sync.d.ts +18 -0
- package/dist/lib/devices/sync.js +23 -1
- package/dist/lib/devices/tailscale.d.ts +3 -0
- package/dist/lib/devices/tailscale.js +1 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/exec.d.ts +82 -0
- package/dist/lib/exec.js +218 -6
- package/dist/lib/feed-post.d.ts +63 -0
- package/dist/lib/feed-post.js +204 -0
- package/dist/lib/feed.d.ts +7 -1
- package/dist/lib/feed.js +96 -6
- package/dist/lib/fleet/apply.d.ts +32 -2
- package/dist/lib/fleet/apply.js +97 -10
- package/dist/lib/fleet/types.d.ts +11 -0
- package/dist/lib/fs-walk.d.ts +13 -0
- package/dist/lib/fs-walk.js +16 -7
- package/dist/lib/heal.d.ts +7 -4
- package/dist/lib/heal.js +10 -22
- package/dist/lib/hooks.d.ts +1 -0
- package/dist/lib/hooks.js +156 -0
- package/dist/lib/hosts/dispatch.d.ts +16 -0
- package/dist/lib/hosts/dispatch.js +26 -4
- package/dist/lib/hosts/passthrough.js +9 -2
- package/dist/lib/hosts/reconnect.d.ts +78 -0
- package/dist/lib/hosts/reconnect.js +127 -0
- package/dist/lib/hosts/registry.d.ts +75 -14
- package/dist/lib/hosts/registry.js +205 -30
- package/dist/lib/hosts/remote-cmd.d.ts +11 -0
- package/dist/lib/hosts/remote-cmd.js +32 -8
- package/dist/lib/hosts/remote-session-id.d.ts +45 -0
- package/dist/lib/hosts/remote-session-id.js +84 -0
- package/dist/lib/hosts/run-target.d.ts +17 -5
- package/dist/lib/hosts/run-target.js +30 -10
- package/dist/lib/hosts/session-index.d.ts +18 -1
- package/dist/lib/hosts/session-index.js +40 -5
- package/dist/lib/hosts/session-marker.d.ts +33 -0
- package/dist/lib/hosts/session-marker.js +51 -0
- package/dist/lib/hosts/tasks.d.ts +8 -4
- package/dist/lib/import.d.ts +2 -0
- package/dist/lib/import.js +35 -2
- package/dist/lib/menubar/install-menubar.d.ts +29 -0
- package/dist/lib/menubar/install-menubar.js +59 -1
- package/dist/lib/menubar/notify-desktop.d.ts +44 -0
- package/dist/lib/menubar/notify-desktop.js +78 -0
- package/dist/lib/migrate.d.ts +16 -0
- package/dist/lib/migrate.js +36 -0
- package/dist/lib/models.d.ts +27 -0
- package/dist/lib/models.js +54 -1
- package/dist/lib/overdue.d.ts +6 -2
- package/dist/lib/overdue.js +10 -36
- package/dist/lib/picker.js +4 -1
- package/dist/lib/platform/process.d.ts +17 -0
- package/dist/lib/platform/process.js +70 -0
- package/dist/lib/profiles-presets.js +9 -7
- package/dist/lib/profiles.d.ts +31 -0
- package/dist/lib/profiles.js +70 -0
- package/dist/lib/pty-server.d.ts +2 -10
- package/dist/lib/pty-server.js +4 -38
- package/dist/lib/registry.d.ts +1 -1
- package/dist/lib/registry.js +48 -8
- package/dist/lib/rotate.d.ts +18 -0
- package/dist/lib/rotate.js +28 -0
- package/dist/lib/routine-notify.d.ts +76 -0
- package/dist/lib/routine-notify.js +190 -0
- package/dist/lib/routines-placement.d.ts +38 -0
- package/dist/lib/routines-placement.js +79 -0
- package/dist/lib/routines-project.d.ts +97 -0
- package/dist/lib/routines-project.js +349 -0
- package/dist/lib/routines.d.ts +94 -0
- package/dist/lib/routines.js +93 -9
- package/dist/lib/runner.d.ts +12 -2
- package/dist/lib/runner.js +242 -20
- package/dist/lib/sandbox.js +10 -0
- package/dist/lib/secrets/account-token.d.ts +20 -0
- package/dist/lib/secrets/account-token.js +64 -0
- package/dist/lib/secrets/agent.d.ts +74 -4
- package/dist/lib/secrets/agent.js +181 -107
- package/dist/lib/secrets/bundles.d.ts +54 -6
- package/dist/lib/secrets/bundles.js +230 -34
- package/dist/lib/secrets/index.d.ts +26 -3
- package/dist/lib/secrets/index.js +42 -12
- package/dist/lib/secrets/rc-hygiene.d.ts +55 -0
- package/dist/lib/secrets/rc-hygiene.js +154 -0
- package/dist/lib/secrets/remote.d.ts +7 -4
- package/dist/lib/secrets/remote.js +7 -4
- package/dist/lib/secrets/session-store.d.ts +6 -2
- package/dist/lib/secrets/session-store.js +65 -15
- package/dist/lib/secrets/sync-commands.d.ts +21 -0
- package/dist/lib/secrets/sync-commands.js +21 -0
- package/dist/lib/secrets/unlock-hints.d.ts +27 -0
- package/dist/lib/secrets/unlock-hints.js +36 -0
- package/dist/lib/self-update.d.ts +23 -2
- package/dist/lib/self-update.js +86 -5
- package/dist/lib/session/active.d.ts +124 -7
- package/dist/lib/session/active.js +390 -56
- package/dist/lib/session/cloud.js +2 -1
- package/dist/lib/session/db.d.ts +54 -0
- package/dist/lib/session/db.js +320 -19
- package/dist/lib/session/detached.d.ts +30 -0
- package/dist/lib/session/detached.js +92 -0
- package/dist/lib/session/discover.d.ts +478 -3
- package/dist/lib/session/discover.js +1740 -472
- package/dist/lib/session/fork.js +2 -1
- package/dist/lib/session/hook-sessions.d.ts +43 -0
- package/dist/lib/session/hook-sessions.js +135 -0
- package/dist/lib/session/linear.d.ts +6 -0
- package/dist/lib/session/linear.js +55 -0
- package/dist/lib/session/migrate-targets.d.ts +65 -0
- package/dist/lib/session/migrate-targets.js +94 -0
- package/dist/lib/session/migrations.d.ts +37 -0
- package/dist/lib/session/migrations.js +60 -0
- package/dist/lib/session/parse.d.ts +18 -0
- package/dist/lib/session/parse.js +141 -32
- package/dist/lib/session/pid-registry.d.ts +34 -2
- package/dist/lib/session/pid-registry.js +49 -2
- package/dist/lib/session/prompt.d.ts +7 -0
- package/dist/lib/session/prompt.js +37 -0
- package/dist/lib/session/provenance.d.ts +7 -0
- package/dist/lib/session/render.d.ts +7 -2
- package/dist/lib/session/render.js +40 -26
- package/dist/lib/session/short-id.d.ts +17 -0
- package/dist/lib/session/short-id.js +20 -0
- package/dist/lib/session/state.d.ts +4 -0
- package/dist/lib/session/state.js +99 -13
- package/dist/lib/session/types.d.ts +25 -1
- package/dist/lib/session/types.js +8 -0
- package/dist/lib/shims.d.ts +1 -1
- package/dist/lib/shims.js +20 -6
- package/dist/lib/sqlite.d.ts +3 -2
- package/dist/lib/sqlite.js +27 -4
- package/dist/lib/staleness/detectors/commands.js +1 -1
- package/dist/lib/staleness/detectors/workflows.js +13 -2
- package/dist/lib/staleness/writers/commands.js +7 -7
- package/dist/lib/staleness/writers/workflows.d.ts +4 -2
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +3 -0
- package/dist/lib/state.d.ts +8 -4
- package/dist/lib/state.js +21 -37
- package/dist/lib/teams/agents.d.ts +19 -10
- package/dist/lib/teams/agents.js +40 -39
- package/dist/lib/types.d.ts +47 -2
- package/dist/lib/usage.d.ts +33 -1
- package/dist/lib/usage.js +172 -12
- package/dist/lib/versions.d.ts +9 -2
- package/dist/lib/versions.js +37 -8
- package/dist/lib/watchdog/log.d.ts +43 -0
- package/dist/lib/watchdog/log.js +69 -0
- package/dist/lib/watchdog/routine.d.ts +44 -0
- package/dist/lib/watchdog/routine.js +69 -0
- package/dist/lib/watchdog/runner.d.ts +51 -7
- package/dist/lib/watchdog/runner.js +239 -64
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdog.js +31 -16
- package/dist/lib/workflows.d.ts +16 -0
- package/dist/lib/workflows.js +110 -1
- package/package.json +1 -1
package/dist/commands/exec.js
CHANGED
|
@@ -20,8 +20,9 @@ import { sshResolve } from '../lib/hosts/ssh-config.js';
|
|
|
20
20
|
import * as fs from 'fs';
|
|
21
21
|
import * as path from 'path';
|
|
22
22
|
import * as os from 'os';
|
|
23
|
-
import { randomUUID } from 'crypto';
|
|
24
23
|
import { spawnSync } from 'child_process';
|
|
24
|
+
import { randomUUID } from 'crypto';
|
|
25
|
+
import { isSessionTrackedAgent } from '../lib/session/types.js';
|
|
25
26
|
/** Distinguish a terminal account-picker marker from an explicit @version pin. */
|
|
26
27
|
export function parseRunAccountPickerRequest(agentSpec) {
|
|
27
28
|
const requested = agentSpec.endsWith('@');
|
|
@@ -398,6 +399,11 @@ export function registerRunCommand(program) {
|
|
|
398
399
|
// `--on` and `--computer` are hidden aliases of `--host` — same behavior.
|
|
399
400
|
runCmd.addOption(new Option('--on <name>', 'Alias of --host.').hideHelp());
|
|
400
401
|
runCmd.addOption(new Option('--computer <name>', 'Alias of --host.').hideHelp());
|
|
402
|
+
// Internal: the `--host` dispatch forwards this so the REMOTE run prints its
|
|
403
|
+
// resolved session id as a one-line stdout sentinel (hosts/session-marker.ts),
|
|
404
|
+
// letting the launcher relate the remote-created session back to itself for
|
|
405
|
+
// every agent — not just Claude, whose id it forces up front.
|
|
406
|
+
runCmd.addOption(new Option('--emit-session-id', 'internal: print the resolved session id for a --host launcher to capture').hideHelp());
|
|
401
407
|
// Required for the documented `agents run <agent> [prompt] -- <native flags>`
|
|
402
408
|
// passthrough: commander >=13 rejects excess operands by default, so any
|
|
403
409
|
// post-`--` token died with "too many arguments" before the action ran. The
|
|
@@ -872,7 +878,7 @@ export function registerRunCommand(program) {
|
|
|
872
878
|
process.exit(1);
|
|
873
879
|
}
|
|
874
880
|
const hostName = hostGiven[0];
|
|
875
|
-
const { resolveHostRunTarget, dispatchPromptToHost, HostResolutionError } = await import('../lib/hosts/run-target.js');
|
|
881
|
+
const { resolveHostRunTarget, resolveHostSessionId, dispatchPromptToHost, HostResolutionError } = await import('../lib/hosts/run-target.js');
|
|
876
882
|
const { runInteractiveOnHost } = await import('../lib/hosts/dispatch.js');
|
|
877
883
|
const { registerInteractiveHostSession } = await import('../lib/hosts/session-index.js');
|
|
878
884
|
const { RUN_OPTION_REJECT_MESSAGES } = await import('../lib/hosts/remote-cmd.js');
|
|
@@ -1030,10 +1036,21 @@ export function registerRunCommand(program) {
|
|
|
1030
1036
|
process.exit(1);
|
|
1031
1037
|
}
|
|
1032
1038
|
// Mirror the local path (lib/exec.ts): only Claude accepts a forced
|
|
1033
|
-
// `--session-id`.
|
|
1034
|
-
//
|
|
1035
|
-
//
|
|
1036
|
-
const hostSessionId = runAgent
|
|
1039
|
+
// `--session-id`. Adopt the caller's id when present; otherwise mint
|
|
1040
|
+
// one here. Registering that same id keeps the local index aligned
|
|
1041
|
+
// with the remote agent. On resume, don't mint a new one.
|
|
1042
|
+
const hostSessionId = resolveHostSessionId(runAgent, resumeId, options.sessionId);
|
|
1043
|
+
// For every OTHER agent the remote coins its own id, which we can't
|
|
1044
|
+
// know up front. Forward a launch id we control as AGENT_LAUNCH_ID:
|
|
1045
|
+
// the remote `agents run` adopts it (exec.ts resolveLaunchId) and its
|
|
1046
|
+
// SessionStart hook records the real id under that exact key, so after
|
|
1047
|
+
// the stream we resolve the id by one ssh read of the remote hook
|
|
1048
|
+
// record — the same launch-id join used locally (RUSH-2034). Not
|
|
1049
|
+
// needed for Claude (id forced) or resume (id already known).
|
|
1050
|
+
const correlationLaunchId = !hostSessionId && !resumeId && isSessionTrackedAgent(runAgent) ? randomUUID() : undefined;
|
|
1051
|
+
const hostEnv = correlationLaunchId
|
|
1052
|
+
? [...options.env, `AGENT_LAUNCH_ID=${correlationLaunchId}`]
|
|
1053
|
+
: options.env;
|
|
1037
1054
|
if (hostSessionId) {
|
|
1038
1055
|
registerInteractiveHostSession({
|
|
1039
1056
|
cwd: process.cwd(),
|
|
@@ -1043,6 +1060,7 @@ export function registerRunCommand(program) {
|
|
|
1043
1060
|
name: options.name,
|
|
1044
1061
|
});
|
|
1045
1062
|
}
|
|
1063
|
+
const isRaw = options.raw || options.tmux === false || options.disableTmux === true;
|
|
1046
1064
|
const exitCode = await runInteractiveOnHost(host, {
|
|
1047
1065
|
agent: runAgent,
|
|
1048
1066
|
version: resumeId ? undefined : runVersion,
|
|
@@ -1052,7 +1070,7 @@ export function registerRunCommand(program) {
|
|
|
1052
1070
|
mode: options.mode,
|
|
1053
1071
|
model: options.model,
|
|
1054
1072
|
effort: options.effort,
|
|
1055
|
-
env:
|
|
1073
|
+
env: hostEnv,
|
|
1056
1074
|
addDir: hostAddDirs,
|
|
1057
1075
|
json: options.json,
|
|
1058
1076
|
verbose: options.verbose,
|
|
@@ -1064,10 +1082,55 @@ export function registerRunCommand(program) {
|
|
|
1064
1082
|
name: options.name,
|
|
1065
1083
|
resume: resumeId,
|
|
1066
1084
|
passthroughArgs,
|
|
1067
|
-
raw:
|
|
1085
|
+
raw: isRaw,
|
|
1068
1086
|
forceInteractive: options.interactive,
|
|
1069
1087
|
copyCreds: hostCopyCreds,
|
|
1070
1088
|
});
|
|
1089
|
+
// Resolve a non-Claude agent's REAL remote session id now the run has
|
|
1090
|
+
// booted (its hook has fired on the peer): one ssh read of the remote
|
|
1091
|
+
// hook record, keyed by the launch id we forwarded. Register it so the
|
|
1092
|
+
// run shows in `agents sessions` and can be reconnected/focused —
|
|
1093
|
+
// closing the non-Claude gap RUSH-2033 left. Best-effort: an
|
|
1094
|
+
// unreachable host or a not-yet-landed record leaves the run un-mapped
|
|
1095
|
+
// rather than mis-mapped.
|
|
1096
|
+
let resolvedRemoteId;
|
|
1097
|
+
if (correlationLaunchId) {
|
|
1098
|
+
const { resolveRemoteSessionId } = await import('../lib/hosts/remote-session-id.js');
|
|
1099
|
+
const { sshTargetFor } = await import('../lib/hosts/types.js');
|
|
1100
|
+
try {
|
|
1101
|
+
resolvedRemoteId = resolveRemoteSessionId(sshTargetFor(host), correlationLaunchId);
|
|
1102
|
+
}
|
|
1103
|
+
catch {
|
|
1104
|
+
/* ssh read is best-effort — keep the run un-mapped, never guess */
|
|
1105
|
+
}
|
|
1106
|
+
if (resolvedRemoteId) {
|
|
1107
|
+
registerInteractiveHostSession({
|
|
1108
|
+
cwd: process.cwd(),
|
|
1109
|
+
host: host.name,
|
|
1110
|
+
agent: runAgent,
|
|
1111
|
+
sessionId: resolvedRemoteId,
|
|
1112
|
+
name: options.name,
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
// A network drop kills the local ssh client (exit 255) but the remote
|
|
1117
|
+
// agent survives in its detached tmux session. With a known session id
|
|
1118
|
+
// (Claude's forced id, a resumed run, or a non-Claude id we just
|
|
1119
|
+
// resolved from the remote hook record) and a tmux-hosted run,
|
|
1120
|
+
// re-attach the live pane automatically instead of exiting — the user
|
|
1121
|
+
// never has to notice the drop and `agents sessions focus` by hand.
|
|
1122
|
+
// `raw` runs aren't tmux wrapped, so there is nothing to reconnect to.
|
|
1123
|
+
const reconnectId = hostSessionId ?? resolvedRemoteId ?? resumeId;
|
|
1124
|
+
if (reconnectId && !isRaw) {
|
|
1125
|
+
const { reconnectInteractiveSession, SSH_CONN_FAILURE } = await import('../lib/hosts/reconnect.js');
|
|
1126
|
+
if (exitCode === SSH_CONN_FAILURE) {
|
|
1127
|
+
process.exit(await reconnectInteractiveSession({
|
|
1128
|
+
host,
|
|
1129
|
+
sessionId: reconnectId,
|
|
1130
|
+
initialExit: exitCode,
|
|
1131
|
+
}));
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1071
1134
|
process.exit(exitCode);
|
|
1072
1135
|
}
|
|
1073
1136
|
// Headless host run: launch detached, tail the remote log, and follow
|
|
@@ -1103,6 +1166,7 @@ export function registerRunCommand(program) {
|
|
|
1103
1166
|
remoteCwd: hostCwd,
|
|
1104
1167
|
name: options.name,
|
|
1105
1168
|
resume: resumeId,
|
|
1169
|
+
sessionId: options.sessionId,
|
|
1106
1170
|
follow: options.follow !== false,
|
|
1107
1171
|
passthroughArgs,
|
|
1108
1172
|
copyCreds: hostCopyCreds,
|
|
@@ -1221,7 +1285,7 @@ export function registerRunCommand(program) {
|
|
|
1221
1285
|
await warnUnpushedWork(resumeExec.cwd ?? process.cwd());
|
|
1222
1286
|
process.exit(resumeExit);
|
|
1223
1287
|
}
|
|
1224
|
-
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle, assertRemoteBundleFlagsUnsupported, isHeadlessSecretsContext }, { splitBundleRef,
|
|
1288
|
+
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, headlessPlanStallCommand, nativeResume, resolveInteractive, inferredInteractiveWithoutTty }, { ALL_AGENT_IDS, ACCOUNT_INSPECTION_AGENT_IDS, agentLabel, supportsAccountInspection }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle, assertRemoteBundleFlagsUnsupported, isHeadlessSecretsContext }, { splitBundleRef, resolveHostSshTarget, remoteResolveEnv }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, rotationFailoverChain, shouldArmRotationFailover, RUN_STRATEGIES }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias, ensureAgentRunnable }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef, resolveAllowedSubagents, pruneStaleWorkflowSubagents, ensureSubagentDispatchTool }, { resolveRunDefaults }, { getMcpServersByName, buildWorkflowMcpConfig }, { supports }, { shareRuntimeEnv },] = await Promise.all([
|
|
1225
1289
|
import('../lib/exec.js'),
|
|
1226
1290
|
import('../lib/agents.js'),
|
|
1227
1291
|
import('../lib/profiles.js'),
|
|
@@ -1851,7 +1915,7 @@ export function registerRunCommand(program) {
|
|
|
1851
1915
|
assertRemoteBundleFlagsUnsupported(bundleName, host, { keys: secretsKeysSubset, allowExpired: options.allowExpired }, { keysFlag: '--secrets-keys', allowExpiredFlag: '--allow-expired' });
|
|
1852
1916
|
// Remote bundle (`bundle@host`): resolve over SSH and inject
|
|
1853
1917
|
// ephemerally — values never touch this machine's keychain or disk.
|
|
1854
|
-
const target = await
|
|
1918
|
+
const target = await resolveHostSshTarget(host);
|
|
1855
1919
|
const bundleEnv = await remoteResolveEnv(target, bundleName, { osLookupName: host });
|
|
1856
1920
|
console.log(chalk.gray(`[secrets] Resolved ${bundleName}@${host}: ${Object.keys(bundleEnv).length} keys (remote, ephemeral)`));
|
|
1857
1921
|
secretsEnv = { ...secretsEnv, ...bundleEnv };
|
|
@@ -1859,11 +1923,13 @@ export function registerRunCommand(program) {
|
|
|
1859
1923
|
else {
|
|
1860
1924
|
const { bundle, env: bundleEnv } = readAndResolveBundleEnv(bundleName, {
|
|
1861
1925
|
caller: `agent ${agent}`,
|
|
1926
|
+
agent,
|
|
1862
1927
|
keys: secretsKeysSubset,
|
|
1863
1928
|
allowExpired: options.allowExpired,
|
|
1864
|
-
//
|
|
1865
|
-
//
|
|
1866
|
-
//
|
|
1929
|
+
// The harness identity scopes any cached grant. It no longer lets the
|
|
1930
|
+
// requesting agent wait for interactive approval — an agent launch
|
|
1931
|
+
// resolves broker-only and fails fast naming
|
|
1932
|
+
// `agents secrets unlock <bundle>` (bundles.ts:interactiveUnlock).
|
|
1867
1933
|
agentOnly: isHeadlessSecretsContext(),
|
|
1868
1934
|
});
|
|
1869
1935
|
const entries = describeBundle(bundle);
|
|
@@ -1923,6 +1989,10 @@ export function registerRunCommand(program) {
|
|
|
1923
1989
|
toolsRestrict: workflowToolsRestrict,
|
|
1924
1990
|
mcpConfigPath: workflowMcpConfigPath,
|
|
1925
1991
|
passthroughArgs,
|
|
1992
|
+
// Set only on the REMOTE side of a `--host` dispatch (the launcher
|
|
1993
|
+
// forwards `--emit-session-id`): print the resolved session id as a
|
|
1994
|
+
// stdout sentinel so the launcher captures the id this run coined.
|
|
1995
|
+
emitSessionId: options.emitSessionId === true,
|
|
1926
1996
|
};
|
|
1927
1997
|
if (options.interactive && options.headless) {
|
|
1928
1998
|
console.error(chalk.red('--interactive and --headless are mutually exclusive. Pass one, or neither (mode is inferred from prompt presence).'));
|
package/dist/commands/feed.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agents feed` --
|
|
2
|
+
* `agents feed` -- operator inbox + agent status posts.
|
|
3
3
|
*
|
|
4
|
+
* Default (`agents feed`): list open blocks (decisions agents are waiting on).
|
|
4
5
|
* Aggregates block records from the local feed store and, with --host, from
|
|
5
6
|
* reachable remote hosts via SSH passthrough. Each block carries enough
|
|
6
7
|
* identity for `agents message` to route a reply back to the right agent.
|
|
@@ -8,6 +9,11 @@
|
|
|
8
9
|
* Default view groups by **outcome** (ticket / PR / worktree / Unassigned) so
|
|
9
10
|
* an operator sees dozens of deliverables, not ~1,100 agents. Pass `--flat`
|
|
10
11
|
* for the legacy per-agent list.
|
|
12
|
+
*
|
|
13
|
+
* `agents feed post`: agent-callable free-text progress into the activity
|
|
14
|
+
* stream (milestone `status.posted`). Session/agent/host/runtime/pid identity
|
|
15
|
+
* is auto-stamped from env + the pid registry — no domain-specific flags.
|
|
16
|
+
* Humans watch via the feed activity lane / `agents activity`.
|
|
11
17
|
*/
|
|
12
18
|
import type { Command } from 'commander';
|
|
13
19
|
import { type OpenBlock } from '../lib/feed.js';
|
package/dist/commands/feed.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { ensureFeedPublishHook, listAskStats, listBlocks, recordNotified } from '../lib/feed.js';
|
|
3
3
|
import { ensureActivityLogHook, readRecentActivity, formatActivityLine } from '../lib/activity.js';
|
|
4
|
+
import { postFeedStatus } from '../lib/feed-post.js';
|
|
4
5
|
import { enrichBlocksFromSessions, groupBlocksByOutcome, isUnambiguousOutcomeAnswer, openBlocksForOutcome, stampBlockOutcomes, } from '../lib/feed-outcome.js';
|
|
5
6
|
import { classifyBlock, filterBlocksForFeed, suppressionDigest, } from '../lib/ask-classifier.js';
|
|
6
7
|
import { machineId, normalizeHost } from '../lib/machine-id.js';
|
|
@@ -214,9 +215,9 @@ export function sessionHintsFromActive(sessions) {
|
|
|
214
215
|
}));
|
|
215
216
|
}
|
|
216
217
|
export function registerFeedCommand(program) {
|
|
217
|
-
program
|
|
218
|
+
const feed = program
|
|
218
219
|
.command('feed')
|
|
219
|
-
.description('
|
|
220
|
+
.description('Open blocks (needs you) + agent status posts (feed post)')
|
|
220
221
|
.option('--json', 'Output as JSON (each block stamped with its outcome + ask class)')
|
|
221
222
|
.option('--flat', 'List one block per agent instead of grouping by outcome')
|
|
222
223
|
.option('--all', 'Include stalls/FYIs that policy would suppress (default: hide them)')
|
|
@@ -225,8 +226,50 @@ export function registerFeedCommand(program) {
|
|
|
225
226
|
.option('--device <target...>', 'Alias for --host; repeatable')
|
|
226
227
|
.option('--dispatch', 'Run stall suppression + default-on-no-answer policy and urgent notifications')
|
|
227
228
|
.option('--pause <id>', 'Pause a runaway/needy local process (SIGSTOP) or cancel a cloud task')
|
|
228
|
-
.option('--kill <id>', 'Kill a runaway/needy local process (SIGTERM) or cancel a cloud task')
|
|
229
|
-
|
|
229
|
+
.option('--kill <id>', 'Kill a runaway/needy local process (SIGTERM) or cancel a cloud task');
|
|
230
|
+
feed
|
|
231
|
+
.command('post')
|
|
232
|
+
.description('Post a status update to the fleet activity stream (for agents)')
|
|
233
|
+
.argument('<text...>', 'What just happened — one short human line')
|
|
234
|
+
.option('--session <id>', 'Session id escape hatch (default: auto from env / pid registry)')
|
|
235
|
+
.option('--json', 'Emit the written event as JSON')
|
|
236
|
+
.addHelpText('after', `
|
|
237
|
+
Examples:
|
|
238
|
+
# Inside an agents-cli run (session identity is already in the env):
|
|
239
|
+
agents feed post "CHANGELOG pushed; watching CI and mac-mini E2E"
|
|
240
|
+
agents feed post "ready for review" --json
|
|
241
|
+
|
|
242
|
+
# Outside a run, pass the session explicitly:
|
|
243
|
+
agents feed post "manual note" --session 00998b0e-2d15-4d2f-a58b-974a886c9b47
|
|
244
|
+
|
|
245
|
+
Identity (session, agent, host, runtime, pid, launchId) is stamped automatically.
|
|
246
|
+
Domain facts (tickets, PRs) are not CLI flags — join them on the session at read time.
|
|
247
|
+
`)
|
|
248
|
+
.action((textParts, opts, cmd) => {
|
|
249
|
+
// Parent `feed` also declares `--json` (for the list view). Commander
|
|
250
|
+
// binds the flag on the parent, so a `feed post … --json` lands on
|
|
251
|
+
// parent.opts().json — not the child. Read both.
|
|
252
|
+
const flags = {
|
|
253
|
+
session: opts?.session ?? cmd?.opts?.()?.session,
|
|
254
|
+
json: Boolean(opts?.json ?? cmd?.opts?.()?.json ?? cmd?.parent?.opts?.()?.json),
|
|
255
|
+
};
|
|
256
|
+
try {
|
|
257
|
+
const { event } = postFeedStatus({
|
|
258
|
+
text: Array.isArray(textParts) ? textParts.join(' ') : String(textParts ?? ''),
|
|
259
|
+
sessionId: flags.session,
|
|
260
|
+
});
|
|
261
|
+
if (flags.json) {
|
|
262
|
+
console.log(JSON.stringify(event, null, 2));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
console.log(formatActivityLine(event, { showHost: true }).trimStart());
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
console.error(chalk.red(err.message));
|
|
269
|
+
process.exitCode = 1;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
feed.action(async (opts) => {
|
|
230
273
|
if (opts.device?.length)
|
|
231
274
|
opts.host = [...(opts.host ?? []), ...opts.device];
|
|
232
275
|
const self = machineId();
|
package/dist/commands/go.d.ts
CHANGED
|
@@ -17,8 +17,15 @@ import type { Command } from 'commander';
|
|
|
17
17
|
import { type ActiveSession } from '../lib/session/active.js';
|
|
18
18
|
import type { SessionMeta } from '../lib/session/types.js';
|
|
19
19
|
export declare function registerGoCommand(program: Command): void;
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Live jump targets (local + remote), keyed by session id. Cloud is excluded by
|
|
22
|
+
* default (it has no local pid to attach), but `detach` opts in with
|
|
23
|
+
* `includeCloud` so it can resolve a cloud id and refuse it with a clear message
|
|
24
|
+
* instead of a bare "no live session".
|
|
25
|
+
*/
|
|
26
|
+
export declare function gatherLiveTargets(local: boolean, opts?: {
|
|
27
|
+
includeCloud?: boolean;
|
|
28
|
+
}): Promise<{
|
|
22
29
|
self: string;
|
|
23
30
|
activeById: Map<string, ActiveSession>;
|
|
24
31
|
}>;
|
package/dist/commands/go.js
CHANGED
|
@@ -20,6 +20,7 @@ import { promisify } from 'util';
|
|
|
20
20
|
import { getActiveSessions, findSessionFileForKind } from '../lib/session/active.js';
|
|
21
21
|
import { gatherRemoteActive } from '../lib/session/remote-active.js';
|
|
22
22
|
import { discoverSessions } from '../lib/session/discover.js';
|
|
23
|
+
import { deriveShortId } from '../lib/session/short-id.js';
|
|
23
24
|
import { dedupeByMachineSession, mergeLocalFirst, pickSessionInteractive } from './sessions.js';
|
|
24
25
|
import { focusAction } from './focus.js';
|
|
25
26
|
import { machineId } from '../lib/session/sync/config.js';
|
|
@@ -39,8 +40,13 @@ export function registerGoCommand(program) {
|
|
|
39
40
|
await focusAction(id, { local: opts.local, attachOnly: true });
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
|
-
/**
|
|
43
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Live jump targets (local + remote), keyed by session id. Cloud is excluded by
|
|
45
|
+
* default (it has no local pid to attach), but `detach` opts in with
|
|
46
|
+
* `includeCloud` so it can resolve a cloud id and refuse it with a clear message
|
|
47
|
+
* instead of a bare "no live session".
|
|
48
|
+
*/
|
|
49
|
+
export async function gatherLiveTargets(local, opts = {}) {
|
|
44
50
|
const self = machineId();
|
|
45
51
|
const localActive = await getActiveSessions();
|
|
46
52
|
for (const s of localActive)
|
|
@@ -55,9 +61,13 @@ export async function gatherLiveTargets(local) {
|
|
|
55
61
|
catch { /* remote sweep is best-effort */ }
|
|
56
62
|
}
|
|
57
63
|
const activeById = new Map();
|
|
58
|
-
for (const s of active)
|
|
59
|
-
if (s.
|
|
60
|
-
|
|
64
|
+
for (const s of active) {
|
|
65
|
+
if (!s.sessionId)
|
|
66
|
+
continue;
|
|
67
|
+
if (s.context === 'cloud' && !opts.includeCloud)
|
|
68
|
+
continue;
|
|
69
|
+
activeById.set(s.sessionId, s);
|
|
70
|
+
}
|
|
61
71
|
return { self, activeById };
|
|
62
72
|
}
|
|
63
73
|
/** Interactive pick over the live sessions' rich SessionMeta; returns the chosen live session. */
|
|
@@ -99,7 +109,7 @@ function synthMeta(s, self) {
|
|
|
99
109
|
const filePath = remote ? '' : (findSessionFileForKind(s.kind, s.cwd, s.sessionId) ?? '');
|
|
100
110
|
return {
|
|
101
111
|
id: s.sessionId,
|
|
102
|
-
shortId: s.sessionId
|
|
112
|
+
shortId: deriveShortId(s.sessionId),
|
|
103
113
|
agent: s.kind,
|
|
104
114
|
timestamp: new Date(s.startedAtMs ?? Date.now()).toISOString(),
|
|
105
115
|
filePath,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom harness commands (`agents harness`).
|
|
3
|
+
*
|
|
4
|
+
* A "custom harness" is a named (host CLI + model) combo — e.g. OpenCode pinned
|
|
5
|
+
* to meta/muse-spark-1.1, called `spark`. It runs like a native agent type
|
|
6
|
+
* (`agents run spark`) and syncs across devices via `agents repo push user`.
|
|
7
|
+
*
|
|
8
|
+
* Mechanism: harnesses ARE profiles (same ~/.agents/profiles/*.yml, same run
|
|
9
|
+
* resolution). This command group is the harness-flavored surface over that
|
|
10
|
+
* layer + a discovery `list` spanning custom harnesses, addable presets, and the
|
|
11
|
+
* native harness registry. The `agents profiles` tree stays unchanged.
|
|
12
|
+
*/
|
|
13
|
+
import type { Command } from 'commander';
|
|
14
|
+
export declare function registerHarnessCommands(program: Command): void;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom harness commands (`agents harness`).
|
|
3
|
+
*
|
|
4
|
+
* A "custom harness" is a named (host CLI + model) combo — e.g. OpenCode pinned
|
|
5
|
+
* to meta/muse-spark-1.1, called `spark`. It runs like a native agent type
|
|
6
|
+
* (`agents run spark`) and syncs across devices via `agents repo push user`.
|
|
7
|
+
*
|
|
8
|
+
* Mechanism: harnesses ARE profiles (same ~/.agents/profiles/*.yml, same run
|
|
9
|
+
* resolution). This command group is the harness-flavored surface over that
|
|
10
|
+
* layer + a discovery `list` spanning custom harnesses, addable presets, and the
|
|
11
|
+
* native harness registry. The `agents profiles` tree stays unchanged.
|
|
12
|
+
*/
|
|
13
|
+
import chalk from 'chalk';
|
|
14
|
+
import { addProfile } from './profiles.js';
|
|
15
|
+
import { listProfiles, readProfile, deleteProfile, profileHostLabel, profileProviderLabel, profileModelLabel, profileAuthLabel, getProfilePath, } from '../lib/profiles.js';
|
|
16
|
+
import { listPresets } from '../lib/profiles-presets.js';
|
|
17
|
+
import { AGENTS, ALL_AGENT_IDS } from '../lib/agents.js';
|
|
18
|
+
/** Short capability summary for a native harness — its supported run modes. */
|
|
19
|
+
function nativeModes(id) {
|
|
20
|
+
const modes = AGENTS[id]?.capabilities?.modes ?? [];
|
|
21
|
+
return modes.length ? modes.join('/') : '-';
|
|
22
|
+
}
|
|
23
|
+
export function registerHarnessCommands(program) {
|
|
24
|
+
const cmd = program
|
|
25
|
+
.command('harness')
|
|
26
|
+
.alias('harnesses')
|
|
27
|
+
.description('Custom harnesses — name a (host CLI + model) combo and run it like a native agent type.')
|
|
28
|
+
.addHelpText('after', `
|
|
29
|
+
A custom harness pins a host CLI (opencode, claude, codex, grok, gemini, ...) to a
|
|
30
|
+
model and gives it a name. 'agents run <name>' then behaves like a native agent
|
|
31
|
+
type, and 'agents repo push user' syncs it to every device.
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
# Meta Muse Spark 1.1 through OpenCode, called 'spark'
|
|
35
|
+
agents harness add spark --host opencode --model meta/muse-spark-1.1
|
|
36
|
+
agents run spark "refactor api/handlers/checkout.py"
|
|
37
|
+
|
|
38
|
+
# Per-run model override still wins
|
|
39
|
+
agents run spark --model opencode/big-pickle "quick pass"
|
|
40
|
+
|
|
41
|
+
# See custom harnesses, addable presets, and native harnesses
|
|
42
|
+
agents harness list
|
|
43
|
+
|
|
44
|
+
# Private OpenAI/Anthropic-compatible endpoint (stores a keychain key)
|
|
45
|
+
agents harness add corp --host claude --model gpt-x --base-url https://gw.corp/v1 --auth-provider corp
|
|
46
|
+
|
|
47
|
+
# Custom harnesses are just profiles — this also works via 'agents profiles'
|
|
48
|
+
`);
|
|
49
|
+
cmd
|
|
50
|
+
.command('add <name>')
|
|
51
|
+
.description('Create a custom harness from a host + model (or apply a built-in preset).')
|
|
52
|
+
.option('--host <agent>', 'Host CLI to run under (opencode, claude, codex, grok, gemini, ...) — pair with --model')
|
|
53
|
+
.option('--model <id>', 'Model id to pin on the host (e.g., meta/muse-spark-1.1) — pair with --host')
|
|
54
|
+
.option('--base-url <url>', 'Custom endpoint base URL (claude/codex hosts)')
|
|
55
|
+
.option('--auth-provider <provider>', 'Attach a keychain-backed API key under this provider (for private endpoints)')
|
|
56
|
+
.option('--preset <preset>', 'Apply a built-in preset instead of --host/--model')
|
|
57
|
+
.option('--version <version>', 'Pin the host CLI version (e.g., 1.16.0)')
|
|
58
|
+
.option('--key-stdin', 'Read API key from stdin instead of prompting (for scripts/CI)')
|
|
59
|
+
.option('--force', 'Overwrite an existing harness with the same name')
|
|
60
|
+
.action(async (name, opts) => {
|
|
61
|
+
try {
|
|
62
|
+
await addProfile(name, opts, 'Harness');
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
console.error(chalk.red(err.message));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
cmd
|
|
70
|
+
.command('list')
|
|
71
|
+
.alias('ls')
|
|
72
|
+
.description('List custom harnesses, addable presets, and native harnesses.')
|
|
73
|
+
.option('--json', 'Emit JSON instead of a table')
|
|
74
|
+
.action((opts) => {
|
|
75
|
+
const custom = listProfiles();
|
|
76
|
+
const presets = listPresets();
|
|
77
|
+
const native = ALL_AGENT_IDS.map((id) => ({ id, name: AGENTS[id].name, modes: nativeModes(id) }));
|
|
78
|
+
if (opts.json) {
|
|
79
|
+
console.log(JSON.stringify({
|
|
80
|
+
custom: custom.map((p) => ({
|
|
81
|
+
name: p.name,
|
|
82
|
+
host: p.host.agent,
|
|
83
|
+
model: profileModelLabel(p),
|
|
84
|
+
provider: profileProviderLabel(p),
|
|
85
|
+
})),
|
|
86
|
+
presets: presets.map((p) => ({ name: p.name, provider: p.provider, description: p.description })),
|
|
87
|
+
native,
|
|
88
|
+
}, null, 2));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
console.log(chalk.bold('Custom harnesses') + chalk.gray(' (yours — agents run <name>)'));
|
|
92
|
+
if (custom.length === 0) {
|
|
93
|
+
console.log(chalk.gray(' none yet — try: agents harness add spark --host opencode --model meta/muse-spark-1.1'));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
for (const p of custom) {
|
|
97
|
+
console.log(` ${chalk.cyan(p.name.padEnd(16))} ${profileHostLabel(p).padEnd(14)} ${chalk.gray(profileModelLabel(p))}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(chalk.bold('Presets') + chalk.gray(' (addable — agents harness add <name>)'));
|
|
102
|
+
for (const p of presets) {
|
|
103
|
+
console.log(` ${chalk.cyan(p.name.padEnd(16))} ${p.provider.padEnd(12)} ${chalk.gray(p.description.slice(0, 70))}`);
|
|
104
|
+
}
|
|
105
|
+
console.log('');
|
|
106
|
+
console.log(chalk.bold('Native harnesses') + chalk.gray(' (built-in — agents run <id>)'));
|
|
107
|
+
for (const n of native) {
|
|
108
|
+
console.log(` ${chalk.cyan(n.id.padEnd(16))} ${n.name.padEnd(14)} ${chalk.gray('modes: ' + n.modes)}`);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
cmd
|
|
112
|
+
.command('view <name>')
|
|
113
|
+
.alias('show')
|
|
114
|
+
.description('Show one custom harness (host, model, provider, auth, path).')
|
|
115
|
+
.action((name) => {
|
|
116
|
+
try {
|
|
117
|
+
const p = readProfile(name);
|
|
118
|
+
console.log(chalk.bold(p.name));
|
|
119
|
+
if (p.description)
|
|
120
|
+
console.log(chalk.gray(p.description));
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(`Host: ${profileHostLabel(p)}`);
|
|
123
|
+
console.log(`Model: ${profileModelLabel(p)}`);
|
|
124
|
+
console.log(`Provider: ${profileProviderLabel(p)}`);
|
|
125
|
+
console.log(`Auth: ${profileAuthLabel(p)}`);
|
|
126
|
+
console.log(chalk.gray(getProfilePath(p.name)));
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
console.error(chalk.red(err.message));
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
cmd
|
|
134
|
+
.command('remove <name>')
|
|
135
|
+
.alias('rm')
|
|
136
|
+
.description('Delete a custom harness (keychain token is kept).')
|
|
137
|
+
.action((name) => {
|
|
138
|
+
const existed = deleteProfile(name);
|
|
139
|
+
if (!existed) {
|
|
140
|
+
console.error(chalk.red(`Harness '${name}' not found.`));
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
console.log(chalk.green(`Harness '${name}' removed.`));
|
|
144
|
+
});
|
|
145
|
+
}
|
package/dist/commands/import.js
CHANGED
|
@@ -134,7 +134,7 @@ async function runImport(agentArg, opts) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
-
let version = opts.
|
|
137
|
+
let version = opts.as;
|
|
138
138
|
if (!version) {
|
|
139
139
|
if (!useDirectBinaryImport && globalPath) {
|
|
140
140
|
try {
|
|
@@ -157,7 +157,7 @@ async function runImport(agentArg, opts) {
|
|
|
157
157
|
}
|
|
158
158
|
if (!version) {
|
|
159
159
|
console.error(chalk.red(`Could not determine version for ${agentLabel(agentId)}.`));
|
|
160
|
-
console.error(chalk.gray('Pass --
|
|
160
|
+
console.error(chalk.gray('Pass --as <version> explicitly.'));
|
|
161
161
|
process.exit(1);
|
|
162
162
|
}
|
|
163
163
|
if (!isValidImportVersion(version)) {
|
|
@@ -167,7 +167,7 @@ async function runImport(agentArg, opts) {
|
|
|
167
167
|
}
|
|
168
168
|
const versionDir = getVersionDir(agentId, version);
|
|
169
169
|
const fromLabel = useDirectBinaryImport ? installScriptBinary : globalPath;
|
|
170
|
-
console.log(chalk.bold(`\nImport ${agentLabel(agentId)} v${version}`));
|
|
170
|
+
console.log(chalk.bold(`\nImport ${agentLabel(agentId)} v${version}${opts.isolated ? ' (isolated copy)' : ''}`));
|
|
171
171
|
console.log(` from: ${chalk.gray(fromLabel)}`);
|
|
172
172
|
console.log(` into: ${chalk.gray(versionDir)}`);
|
|
173
173
|
const configDirExists = fs.existsSync(agent.configDir);
|
|
@@ -178,6 +178,16 @@ async function runImport(agentArg, opts) {
|
|
|
178
178
|
configAlreadyManaged = true;
|
|
179
179
|
console.log(` config: ${chalk.gray(`${agent.configDir} (already managed — will skip)`)}`);
|
|
180
180
|
}
|
|
181
|
+
else if (opts.isolated) {
|
|
182
|
+
// This summary is printed BEFORE the branch that does the work, so it has to
|
|
183
|
+
// describe the mode it is actually about to run. Saying "will be moved" under
|
|
184
|
+
// --isolated announced the exact adoption the flag exists to prevent — a
|
|
185
|
+
// confirmation prompt that misdescribes the operation is worse than none.
|
|
186
|
+
console.log(` config: ${chalk.gray(`${agent.configDir} (will be COPIED — your original stays put)`)}`);
|
|
187
|
+
if (!opts.withAuth) {
|
|
188
|
+
console.log(` ${chalk.gray('credentials are skipped; pass --with-auth to include them')}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
181
191
|
else {
|
|
182
192
|
console.log(` config: ${chalk.gray(`${agent.configDir} (will be moved into version home)`)}`);
|
|
183
193
|
}
|
|
@@ -188,7 +198,9 @@ async function runImport(agentArg, opts) {
|
|
|
188
198
|
if (!opts.yes && isInteractiveTerminal()) {
|
|
189
199
|
console.log();
|
|
190
200
|
const proceed = await confirm({
|
|
191
|
-
message:
|
|
201
|
+
message: opts.isolated
|
|
202
|
+
? `Import ${agentLabel(agentId)} v${version} as an isolated copy?`
|
|
203
|
+
: `Import ${agentLabel(agentId)} v${version} into agents-cli?`,
|
|
192
204
|
default: true,
|
|
193
205
|
}).catch((err) => {
|
|
194
206
|
if (isPromptCancelled(err))
|
|
@@ -205,17 +217,23 @@ async function runImport(agentArg, opts) {
|
|
|
205
217
|
// want a stranded symlink farm. Binary registration is cheap and reversible
|
|
206
218
|
// — if it fails after config, the next `agents import` call retries cleanly.
|
|
207
219
|
const willImportConfig = configDirExists && !configAlreadyManaged && !opts.isolated;
|
|
208
|
-
if (opts.isolated && configDirExists) {
|
|
220
|
+
if (opts.isolated && configDirExists && configAlreadyManaged) {
|
|
221
|
+
console.log(chalk.gray(` Skipping config copy: ${agent.configDir} is a managed symlink, not your real settings.`));
|
|
222
|
+
}
|
|
223
|
+
else if (opts.isolated && configDirExists) {
|
|
209
224
|
// COPY the user's settings in; never move, never symlink. The original stays
|
|
210
225
|
// exactly where it is — that is what separates this from adoption.
|
|
211
226
|
const seedSpinner = ora(`Copying ${agent.configDir} into the isolated copy...`).start();
|
|
212
|
-
const seed = seedIsolatedConfigFromLocal(agentId, version, { withAuth: opts.withAuth });
|
|
227
|
+
const seed = seedIsolatedConfigFromLocal(agentId, version, { withAuth: opts.withAuth, all: opts.all });
|
|
213
228
|
if (seed.error) {
|
|
214
229
|
seedSpinner.fail(`Config: ${seed.error}`);
|
|
215
230
|
process.exit(1);
|
|
216
231
|
}
|
|
217
232
|
else if (seed.seeded) {
|
|
218
233
|
seedSpinner.succeed(`Settings copied (${seed.from} -> ${seed.to}); your original is untouched`);
|
|
234
|
+
if (seed.skippedRuntime.length > 0) {
|
|
235
|
+
console.log(chalk.gray(` Runtime state NOT copied: ${seed.skippedRuntime.join(', ')} (regenerated as you use it; --all to include)`));
|
|
236
|
+
}
|
|
219
237
|
if (seed.skippedAuth.length > 0) {
|
|
220
238
|
console.log(chalk.gray(` Credentials NOT copied: ${seed.skippedAuth.join(', ')}`));
|
|
221
239
|
console.log(chalk.gray(' The copy signs in separately. Use --with-auth to copy them too.'));
|
|
@@ -288,15 +306,22 @@ export function registerImportCommand(program) {
|
|
|
288
306
|
.command('import')
|
|
289
307
|
.argument('<agent>', 'Agent id (e.g. openclaw, claude, codex)')
|
|
290
308
|
.description('Import an existing unmanaged agent install into agents-cli')
|
|
291
|
-
|
|
309
|
+
// NOT `--version`. The program declares `.version(VERSION)` (src/index.ts), which
|
|
310
|
+
// claims `-V, --version` globally and wins over a subcommand option of the same
|
|
311
|
+
// name — so `agents import codex --version 1.2.3` printed the CLI's own version
|
|
312
|
+
// and exited without importing anything. The flag had been unreachable since it
|
|
313
|
+
// was introduced, and the "could not determine version" error even advised using
|
|
314
|
+
// it. Renamed so it actually reaches the command.
|
|
315
|
+
.option('--as <version>', 'Version label to import as (otherwise read from package.json)')
|
|
292
316
|
.option('--from-path <path>', 'Path to the npm package dir (otherwise auto-detected from PATH)')
|
|
293
317
|
.option('--isolated', 'Copy the install into a self-contained isolated version instead of adopting it')
|
|
294
318
|
.option('--with-auth', 'With --isolated, also copy credentials into the sandbox (skipped by default)')
|
|
319
|
+
.option('--all', 'With --isolated, also copy session history, logs and caches (skipped by default)')
|
|
295
320
|
.option('-y, --yes', 'Skip the confirmation prompt')
|
|
296
321
|
.addHelpText('after', `
|
|
297
322
|
Examples:
|
|
298
323
|
$ agents import openclaw Auto-detect via PATH
|
|
299
|
-
$ agents import openclaw --
|
|
324
|
+
$ agents import openclaw --as 2026.3.8 Pin a version label
|
|
300
325
|
$ agents import openclaw --from-path /opt/homebrew/lib/node_modules/openclaw
|
|
301
326
|
|
|
302
327
|
# installScript-based agents (curl/brew installers, no npm package):
|
|
@@ -305,6 +330,11 @@ Examples:
|
|
|
305
330
|
$ agents import cursor Adopt ~/.local/bin/cursor-agent
|
|
306
331
|
$ agents import antigravity --from-path ~/.local/bin/agy
|
|
307
332
|
|
|
333
|
+
# Copy your setup into a sandbox instead of adopting it:
|
|
334
|
+
$ agents import codex --isolated New isolated copy at the local version
|
|
335
|
+
$ agents import codex --isolated --as 0.146.0 Re-seed an EXISTING isolated copy
|
|
336
|
+
$ agents import codex --isolated --with-auth ...and share credentials with it
|
|
337
|
+
|
|
308
338
|
When to use:
|
|
309
339
|
When an agent CLI is already installed globally and you want to bring it
|
|
310
340
|
under agents-cli management without reinstalling. Creates a symlink farm
|