@phnx-labs/agents-cli 1.20.71 → 1.20.73
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 +325 -1
- package/README.md +40 -4
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +12 -0
- package/dist/commands/activity.js +68 -0
- package/dist/commands/apply.d.ts +13 -0
- package/dist/commands/apply.js +28 -3
- package/dist/commands/browser.js +1 -1
- package/dist/commands/cloud.js +21 -11
- package/dist/commands/commands.js +2 -0
- package/dist/commands/doctor.js +8 -4
- package/dist/commands/events.d.ts +11 -10
- package/dist/commands/events.js +30 -21
- package/dist/commands/exec.d.ts +34 -0
- package/dist/commands/exec.js +337 -39
- package/dist/commands/feed.d.ts +13 -0
- package/dist/commands/feed.js +130 -29
- package/dist/commands/hq.d.ts +2 -0
- package/dist/commands/hq.js +58 -0
- package/dist/commands/lease.d.ts +19 -0
- package/dist/commands/lease.js +138 -11
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +123 -0
- package/dist/commands/logs.js +39 -10
- package/dist/commands/mcp.js +2 -0
- package/dist/commands/mine.d.ts +27 -0
- package/dist/commands/mine.js +207 -0
- package/dist/commands/monitors.js +79 -49
- package/dist/commands/plugins.js +1 -0
- package/dist/commands/profiles.js +158 -0
- package/dist/commands/repo.js +2 -60
- package/dist/commands/resource-view.d.ts +2 -0
- package/dist/commands/resource-view.js +8 -0
- package/dist/commands/resources.d.ts +5 -0
- package/dist/commands/resources.js +95 -0
- package/dist/commands/routines.d.ts +2 -0
- package/dist/commands/routines.js +139 -37
- package/dist/commands/secrets.js +234 -47
- package/dist/commands/send.d.ts +15 -0
- package/dist/commands/send.js +62 -0
- package/dist/commands/sessions-tail.d.ts +1 -0
- package/dist/commands/sessions-tail.js +20 -9
- package/dist/commands/sessions.d.ts +6 -0
- package/dist/commands/sessions.js +26 -3
- package/dist/commands/setup-mine.d.ts +18 -0
- package/dist/commands/setup-mine.js +106 -0
- package/dist/commands/setup-share.js +2 -2
- package/dist/commands/setup.js +3 -1
- package/dist/commands/share.d.ts +8 -1
- package/dist/commands/share.js +122 -37
- package/dist/commands/skills.js +2 -0
- package/dist/commands/ssh.d.ts +9 -0
- package/dist/commands/ssh.js +196 -26
- package/dist/commands/status.js +8 -2
- package/dist/commands/subagents.js +3 -1
- package/dist/commands/sync.js +1 -1
- package/dist/commands/teams.js +11 -2
- package/dist/commands/uninstall.d.ts +11 -0
- package/dist/commands/uninstall.js +158 -0
- package/dist/commands/utils.d.ts +29 -0
- package/dist/commands/utils.js +24 -0
- package/dist/commands/versions.js +120 -11
- package/dist/index.js +83 -12
- package/dist/lib/activity.d.ts +124 -0
- package/dist/lib/activity.js +542 -0
- package/dist/lib/agents.js +16 -5
- package/dist/lib/artifact-actions.d.ts +1 -1
- package/dist/lib/artifact-actions.js +1 -1
- package/dist/lib/ask-classifier.d.ts +2 -1
- package/dist/lib/ask-classifier.js +3 -3
- package/dist/lib/auth-health.d.ts +32 -4
- package/dist/lib/auth-health.js +40 -1
- package/dist/lib/auto-pull.js +8 -1
- package/dist/lib/brand.d.ts +40 -0
- package/dist/lib/brand.js +122 -0
- package/dist/lib/browser/drivers/ssh.js +4 -7
- package/dist/lib/browser/service.js +6 -8
- package/dist/lib/channels/providers/index.d.ts +2 -0
- package/dist/lib/channels/providers/index.js +20 -0
- package/dist/lib/channels/providers/mailbox.d.ts +2 -0
- package/dist/lib/channels/providers/mailbox.js +26 -0
- package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
- package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
- package/dist/lib/channels/providers/rush.d.ts +6 -0
- package/dist/lib/channels/providers/rush.js +55 -0
- package/dist/lib/channels/registry.d.ts +42 -0
- package/dist/lib/channels/registry.js +20 -0
- package/dist/lib/channels/resolve.d.ts +11 -0
- package/dist/lib/channels/resolve.js +12 -0
- package/dist/lib/cli-resources.d.ts +18 -0
- package/dist/lib/cli-resources.js +53 -4
- package/dist/lib/cloud/codex.js +6 -3
- package/dist/lib/cloud/factory.d.ts +1 -0
- package/dist/lib/cloud/factory.js +10 -4
- package/dist/lib/cloud/rush.d.ts +3 -1
- package/dist/lib/cloud/rush.js +6 -1
- package/dist/lib/cloud/types.d.ts +2 -0
- package/dist/lib/codex-home.d.ts +35 -0
- package/dist/lib/codex-home.js +136 -0
- package/dist/lib/crabbox/cli.d.ts +44 -0
- package/dist/lib/crabbox/cli.js +120 -20
- package/dist/lib/crabbox/lease.d.ts +52 -1
- package/dist/lib/crabbox/lease.js +117 -16
- package/dist/lib/crabbox/progress.d.ts +31 -0
- package/dist/lib/crabbox/progress.js +76 -0
- package/dist/lib/crabbox/runtimes.d.ts +4 -0
- package/dist/lib/crabbox/runtimes.js +33 -8
- package/dist/lib/crabbox/setup-copy.d.ts +87 -0
- package/dist/lib/crabbox/setup-copy.js +127 -0
- package/dist/lib/daemon.d.ts +14 -5
- package/dist/lib/daemon.js +52 -9
- package/dist/lib/drive-sync.js +6 -3
- package/dist/lib/event-stream.d.ts +36 -0
- package/dist/lib/event-stream.js +68 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/exec.d.ts +11 -0
- package/dist/lib/exec.js +23 -2
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +14 -9
- package/dist/lib/feed-ranking.d.ts +32 -0
- package/dist/lib/feed-ranking.js +224 -0
- package/dist/lib/feed.d.ts +35 -2
- package/dist/lib/feed.js +66 -3
- package/dist/lib/fleet/auth-sync.d.ts +53 -0
- package/dist/lib/fleet/auth-sync.js +92 -0
- package/dist/lib/fleet/remote-login.d.ts +187 -0
- package/dist/lib/fleet/remote-login.js +556 -0
- package/dist/lib/format.d.ts +30 -3
- package/dist/lib/format.js +34 -5
- package/dist/lib/fs-atomic.d.ts +7 -2
- package/dist/lib/fs-atomic.js +8 -12
- package/dist/lib/git.d.ts +16 -0
- package/dist/lib/git.js +79 -2
- package/dist/lib/heal.js +11 -3
- package/dist/lib/hosts/logs.d.ts +12 -0
- package/dist/lib/hosts/logs.js +18 -0
- package/dist/lib/hosts/progress.d.ts +28 -10
- package/dist/lib/hosts/progress.js +79 -22
- package/dist/lib/hosts/remote-cmd.js +4 -0
- package/dist/lib/hq/floor.d.ts +87 -0
- package/dist/lib/hq/floor.js +226 -0
- package/dist/lib/menubar/install-menubar.js +14 -20
- package/dist/lib/notify.d.ts +1 -0
- package/dist/lib/notify.js +3 -3
- package/dist/lib/open-url.d.ts +2 -0
- package/dist/lib/open-url.js +19 -0
- package/dist/lib/openclaw-keychain.d.ts +56 -0
- package/dist/lib/openclaw-keychain.js +236 -0
- package/dist/lib/overdue.js +10 -0
- package/dist/lib/permissions.d.ts +44 -1
- package/dist/lib/permissions.js +284 -7
- package/dist/lib/project-launch.d.ts +6 -12
- package/dist/lib/project-launch.js +13 -228
- package/dist/lib/project-resources.d.ts +7 -0
- package/dist/lib/project-resources.js +291 -0
- package/dist/lib/pty-client.d.ts +27 -0
- package/dist/lib/pty-client.js +136 -10
- package/dist/lib/refresh.js +14 -10
- package/dist/lib/resource-profiles.d.ts +26 -0
- package/dist/lib/resource-profiles.js +157 -0
- package/dist/lib/resources/permissions.js +7 -1
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/resources.js +32 -3
- package/dist/lib/routines.d.ts +11 -0
- package/dist/lib/routines.js +56 -15
- package/dist/lib/runner.d.ts +1 -0
- package/dist/lib/runner.js +66 -5
- package/dist/lib/secrets/bundles.d.ts +16 -3
- package/dist/lib/secrets/bundles.js +206 -37
- package/dist/lib/secrets/icloud-import.d.ts +2 -2
- package/dist/lib/secrets/icloud-import.js +9 -6
- package/dist/lib/secrets/mcp.js +1 -1
- package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
- package/dist/lib/secrets/vault-age-helper.js +34 -0
- package/dist/lib/secrets/vault.d.ts +49 -0
- package/dist/lib/secrets/vault.js +397 -0
- package/dist/lib/self-heal/checks/path.js +3 -1
- package/dist/lib/self-heal/checks/shadowing.js +10 -2
- package/dist/lib/self-heal/checks/shims.js +19 -11
- package/dist/lib/session/cloud.d.ts +2 -2
- package/dist/lib/session/cloud.js +2 -2
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +44 -7
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +114 -3
- package/dist/lib/session/stream-render.d.ts +3 -0
- package/dist/lib/session/stream-render.js +130 -0
- package/dist/lib/session/types.d.ts +6 -0
- package/dist/lib/share/analytics.d.ts +13 -0
- package/dist/lib/share/analytics.js +45 -0
- package/dist/lib/share/config.d.ts +19 -5
- package/dist/lib/share/config.js +44 -8
- package/dist/lib/share/provision.d.ts +58 -6
- package/dist/lib/share/provision.js +97 -22
- package/dist/lib/share/publish.d.ts +36 -13
- package/dist/lib/share/publish.js +55 -8
- package/dist/lib/share/worker-template.js +83 -17
- package/dist/lib/shims.d.ts +36 -0
- package/dist/lib/shims.js +120 -16
- package/dist/lib/ssh-exec.d.ts +14 -0
- package/dist/lib/ssh-exec.js +57 -0
- package/dist/lib/staleness/detectors/hooks.js +25 -1
- package/dist/lib/staleness/detectors/permissions.js +66 -0
- package/dist/lib/staleness/detectors/workflows.js +20 -0
- package/dist/lib/staleness/writers/hooks.js +58 -4
- package/dist/lib/staleness/writers/permissions.js +2 -2
- package/dist/lib/staleness/writers/skills.js +1 -1
- package/dist/lib/staleness/writers/sources.d.ts +10 -1
- package/dist/lib/staleness/writers/sources.js +68 -1
- package/dist/lib/star-nudge.d.ts +45 -0
- package/dist/lib/star-nudge.js +91 -0
- package/dist/lib/startup/command-registry.d.ts +7 -1
- package/dist/lib/startup/command-registry.js +19 -3
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +3 -0
- package/dist/lib/subagents-registry.d.ts +2 -0
- package/dist/lib/subagents-registry.js +3 -0
- package/dist/lib/teams/parsers.js +214 -6
- package/dist/lib/teams/supervisor.d.ts +7 -0
- package/dist/lib/teams/supervisor.js +2 -1
- package/dist/lib/template.d.ts +1 -1
- package/dist/lib/template.js +1 -1
- package/dist/lib/triggers/webhook.js +36 -3
- package/dist/lib/types.d.ts +73 -0
- package/dist/lib/uninstall.d.ts +84 -0
- package/dist/lib/uninstall.js +347 -0
- package/dist/lib/usage.d.ts +13 -1
- package/dist/lib/usage.js +32 -14
- package/dist/lib/version.d.ts +40 -0
- package/dist/lib/version.js +94 -16
- package/dist/lib/versions.d.ts +27 -0
- package/dist/lib/versions.js +248 -79
- package/dist/lib/workflows.d.ts +2 -0
- package/dist/lib/workflows.js +88 -0
- package/package.json +2 -1
- package/dist/commands/daemon.d.ts +0 -10
- package/dist/commands/daemon.js +0 -121
package/dist/commands/utils.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Small helpers used across multiple commands: prompt cancellation detection,
|
|
5
5
|
* table formatting, spinner management, and platform-specific workarounds.
|
|
6
6
|
*/
|
|
7
|
+
import type { Command } from 'commander';
|
|
7
8
|
import type { AgentId } from '../lib/types.js';
|
|
8
9
|
import { type InstalledAgentTargetResult, type VersionSelectionResult } from '../lib/versions.js';
|
|
9
10
|
/**
|
|
@@ -22,6 +23,34 @@ export declare function isPromptCancelled(err: unknown): boolean;
|
|
|
22
23
|
* True when stdin/stdout are attached to a real terminal.
|
|
23
24
|
*/
|
|
24
25
|
export declare function isInteractiveTerminal(): boolean;
|
|
26
|
+
/** The resolved I/O surface for one command invocation — the human/agent split. */
|
|
27
|
+
export interface Surface {
|
|
28
|
+
/** Machine-readable output was requested (`--json`). */
|
|
29
|
+
json: boolean;
|
|
30
|
+
/** Skip confirmation prompts — explicit `--yes`/`-y`, or a non-interactive shell. */
|
|
31
|
+
assumeYes: boolean;
|
|
32
|
+
/** Suppress non-essential human chrome (`--quiet`). */
|
|
33
|
+
quiet: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Safe to show an interactive picker/prompt: a real terminal AND not asking for
|
|
36
|
+
* `--json` (a JSON consumer is a machine and never wants a picker).
|
|
37
|
+
*/
|
|
38
|
+
interactive: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Compute a command's I/O surface once, in one place, instead of each command
|
|
42
|
+
* re-deriving the human-vs-agent split (and raw-sniffing `process.std*.isTTY`
|
|
43
|
+
* with inconsistent stream choices — the audit's R4). Reads the merged option
|
|
44
|
+
* set via `optsWithGlobals()`, so it sees the command's own flags and any
|
|
45
|
+
* inherited global ones, and folds in the terminal state:
|
|
46
|
+
*
|
|
47
|
+
* - `assumeYes` = explicit `--yes` OR a non-interactive shell (no one to prompt).
|
|
48
|
+
* - `interactive` = a real TTY AND not `--json`.
|
|
49
|
+
*
|
|
50
|
+
* Adopt incrementally: a command switches its ad-hoc `isTTY`/`options.yes` checks
|
|
51
|
+
* to a single `const s = resolveSurface(cmd)` without changing its flag surface.
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveSurface(cmd: Command): Surface;
|
|
25
54
|
/**
|
|
26
55
|
* Exit with a clean message when a picker would be required in a non-interactive shell.
|
|
27
56
|
*/
|
package/dist/commands/utils.js
CHANGED
|
@@ -45,6 +45,30 @@ export function isPromptCancelled(err) {
|
|
|
45
45
|
export function isInteractiveTerminal() {
|
|
46
46
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Compute a command's I/O surface once, in one place, instead of each command
|
|
50
|
+
* re-deriving the human-vs-agent split (and raw-sniffing `process.std*.isTTY`
|
|
51
|
+
* with inconsistent stream choices — the audit's R4). Reads the merged option
|
|
52
|
+
* set via `optsWithGlobals()`, so it sees the command's own flags and any
|
|
53
|
+
* inherited global ones, and folds in the terminal state:
|
|
54
|
+
*
|
|
55
|
+
* - `assumeYes` = explicit `--yes` OR a non-interactive shell (no one to prompt).
|
|
56
|
+
* - `interactive` = a real TTY AND not `--json`.
|
|
57
|
+
*
|
|
58
|
+
* Adopt incrementally: a command switches its ad-hoc `isTTY`/`options.yes` checks
|
|
59
|
+
* to a single `const s = resolveSurface(cmd)` without changing its flag surface.
|
|
60
|
+
*/
|
|
61
|
+
export function resolveSurface(cmd) {
|
|
62
|
+
const opts = cmd.optsWithGlobals();
|
|
63
|
+
const tty = isInteractiveTerminal();
|
|
64
|
+
const json = opts.json === true;
|
|
65
|
+
return {
|
|
66
|
+
json,
|
|
67
|
+
assumeYes: opts.yes === true || !tty,
|
|
68
|
+
quiet: opts.quiet === true,
|
|
69
|
+
interactive: tty && !json,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
48
72
|
/**
|
|
49
73
|
* Exit with a clean message when a picker would be required in a non-interactive shell.
|
|
50
74
|
*/
|
|
@@ -8,9 +8,9 @@ import { AGENTS, ALL_AGENT_IDS, accountOrgBadge, getAccountEmail, getAccountInfo
|
|
|
8
8
|
import { formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
9
9
|
import { viewAction } from './view.js';
|
|
10
10
|
import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
|
|
11
|
-
import { installVersion, removeVersion, listInstalledVersions, isVersionInstalled, isLatestInstalled, isOldestInstalled, getGlobalDefault, setGlobalDefault, getVersionHomePath, getVersionDir, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, printTrashFooter, } from '../lib/versions.js';
|
|
11
|
+
import { installVersion, removeVersion, listInstalledVersions, isVersionInstalled, isLatestInstalled, isOldestInstalled, getGlobalDefault, setGlobalDefault, markVersionIsolated, isVersionIsolated, getVersionHomePath, getVersionDir, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, printTrashFooter, } from '../lib/versions.js';
|
|
12
12
|
import { carryForwardSettings } from '../lib/settings-manifest.js';
|
|
13
|
-
import { createShim, createVersionedAlias, removeShim, shimExists, getShimsDir, getShimPath, getPathShadowingExecutable, isShimsInPath, getPathSetupInstructions, addShimsToPath, switchConfigSymlink, switchHomeFileSymlinks, } from '../lib/shims.js';
|
|
13
|
+
import { createShim, createVersionedAlias, supportsIsolatedInstall, CONFIG_ENV_ISOLATED_AGENTS, removeShim, shimExists, getShimsDir, getShimPath, getPathShadowingExecutable, isShimsInPath, getPathSetupInstructions, addShimsToPath, switchConfigSymlink, switchHomeFileSymlinks, } from '../lib/shims.js';
|
|
14
14
|
import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
|
|
15
15
|
import { tryAutoPull } from '../lib/git.js';
|
|
16
16
|
import { getAgentsDir, getTrashVersionsDir } from '../lib/state.js';
|
|
@@ -98,8 +98,30 @@ function warnIfShimShadowed(agent) {
|
|
|
98
98
|
console.log(chalk.green(` Added shim directory to ${result.location}.`));
|
|
99
99
|
console.log(chalk.gray(` ${result.reloadHint}`));
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Install an isolated copy of an agent version: a fully self-contained install
|
|
103
|
+
* that never touches the user's existing setup.
|
|
104
|
+
*
|
|
105
|
+
* Unlike a normal install it does NOT set (or offer to set) the global default,
|
|
106
|
+
* does NOT create/replace the bare `<agent>` shim, does NOT back up or symlink
|
|
107
|
+
* the user's real `~/.<agent>`, and does NOT carry over settings or resources.
|
|
108
|
+
* It only creates the versioned alias (so the copy is launchable) and records
|
|
109
|
+
* the isolated marker. Invoke the copy explicitly with `agents run <agent>@<v>`.
|
|
110
|
+
*/
|
|
111
|
+
function finalizeIsolatedInstall(agent, version) {
|
|
112
|
+
const agentConfig = AGENTS[agent];
|
|
113
|
+
const label = agentLabel(agentConfig.id);
|
|
114
|
+
createVersionedAlias(agent, version);
|
|
115
|
+
markVersionIsolated(agent, version);
|
|
116
|
+
console.log(chalk.green(` Installed ${label}@${version} as an isolated copy.`));
|
|
117
|
+
console.log(chalk.gray(` Your existing ${agentConfig.configDir} and default ${label} are untouched.`));
|
|
118
|
+
console.log(chalk.gray(` Run it: agents run ${agent}@${version} "your prompt"`));
|
|
119
|
+
console.log(chalk.gray(` It has its own config and login — sign in the first time you run it.`));
|
|
120
|
+
console.log(chalk.gray(` Remove it: agents remove ${agent}@${version} --isolated`));
|
|
121
|
+
}
|
|
101
122
|
async function versionPruneAction(specs, options, commandName) {
|
|
102
123
|
const isProject = options.project;
|
|
124
|
+
const isIsolated = options.isolated;
|
|
103
125
|
const moved = [];
|
|
104
126
|
for (const spec of specs) {
|
|
105
127
|
const parsed = parseAgentSpec(spec);
|
|
@@ -110,6 +132,12 @@ async function versionPruneAction(specs, options, commandName) {
|
|
|
110
132
|
}
|
|
111
133
|
const { agent, version } = parsed;
|
|
112
134
|
const agentConfig = AGENTS[agent];
|
|
135
|
+
// --isolated only ever applies to agents that can BE installed isolated;
|
|
136
|
+
// for the rest no isolated version can exist, so say so plainly.
|
|
137
|
+
if (isIsolated && !supportsIsolatedInstall(agent)) {
|
|
138
|
+
console.log(chalk.gray(`${agentLabel(agentConfig.id)} has no isolated installs (--isolated is not supported for it).`));
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
113
141
|
// Script-installed agents (droid, grok) can have a *literal* `latest`
|
|
114
142
|
// version dir on disk when the post-install version probe failed. An
|
|
115
143
|
// explicit `<agent>@latest` should remove that dir directly rather than
|
|
@@ -117,9 +145,14 @@ async function versionPruneAction(specs, options, commandName) {
|
|
|
117
145
|
// so treat an installed literal `latest` as a concrete pinned version.
|
|
118
146
|
const isLiteralLatestInstalled = version === 'latest' && spec.includes('@') && isVersionInstalled(agent, 'latest');
|
|
119
147
|
if (!isLiteralLatestInstalled && (version === 'latest' || version === 'oldest' || !spec.includes('@'))) {
|
|
120
|
-
|
|
148
|
+
// With --isolated, only isolated installs are eligible for the picker, so
|
|
149
|
+
// a normal/default install can never be selected here by accident.
|
|
150
|
+
const versions = listInstalledVersions(agent)
|
|
151
|
+
.filter((v) => !isIsolated || isVersionIsolated(agent, v));
|
|
121
152
|
if (versions.length === 0) {
|
|
122
|
-
console.log(chalk.gray(
|
|
153
|
+
console.log(chalk.gray(isIsolated
|
|
154
|
+
? `No isolated ${agentLabel(agentConfig.id)} installs`
|
|
155
|
+
: `No versions of ${agentLabel(agentConfig.id)} installed`));
|
|
123
156
|
continue;
|
|
124
157
|
}
|
|
125
158
|
if (!isInteractiveTerminal()) {
|
|
@@ -157,6 +190,9 @@ async function versionPruneAction(specs, options, commandName) {
|
|
|
157
190
|
}
|
|
158
191
|
fixSessionFilePaths(agent, v, versionDir);
|
|
159
192
|
console.log(chalk.green(`Moved ${agentLabel(agentConfig.id)}@${v} to trash`));
|
|
193
|
+
if (isIsolated) {
|
|
194
|
+
console.log(chalk.gray(` Your real ${agentConfig.configDir} and default ${agentLabel(agentConfig.id)} were untouched.`));
|
|
195
|
+
}
|
|
160
196
|
moved.push({ agent, version: v });
|
|
161
197
|
}
|
|
162
198
|
// Default reassignment/clearing is handled at the source in
|
|
@@ -177,6 +213,13 @@ async function versionPruneAction(specs, options, commandName) {
|
|
|
177
213
|
else if (!isVersionInstalled(agent, version)) {
|
|
178
214
|
console.log(chalk.gray(`${agentLabel(agentConfig.id)}@${version} not installed`));
|
|
179
215
|
}
|
|
216
|
+
else if (isIsolated && !isVersionIsolated(agent, version)) {
|
|
217
|
+
// Safety guard: `--isolated` refuses to remove a normal/default install,
|
|
218
|
+
// so an accidental `remove <agent>@<default> --isolated` can never delete
|
|
219
|
+
// the user's primary version or disturb their real ~/.<agent>.
|
|
220
|
+
console.log(chalk.yellow(`${agentLabel(agentConfig.id)}@${version} is not an isolated install; refusing to remove it under --isolated.`));
|
|
221
|
+
console.log(chalk.gray(` Drop --isolated to remove a normal version: agents ${commandName} ${agent}@${version}`));
|
|
222
|
+
}
|
|
180
223
|
else {
|
|
181
224
|
const versionDir = getVersionDir(agent, version);
|
|
182
225
|
const removed = removeVersion(agent, version);
|
|
@@ -186,6 +229,9 @@ async function versionPruneAction(specs, options, commandName) {
|
|
|
186
229
|
}
|
|
187
230
|
fixSessionFilePaths(agent, version, versionDir);
|
|
188
231
|
console.log(chalk.green(`Moved ${agentLabel(agentConfig.id)}@${version} to trash`));
|
|
232
|
+
if (isIsolated) {
|
|
233
|
+
console.log(chalk.gray(` Your real ${agentConfig.configDir} and default ${agentLabel(agentConfig.id)} were untouched.`));
|
|
234
|
+
}
|
|
189
235
|
moved.push({ agent, version });
|
|
190
236
|
const remaining = listInstalledVersions(agent);
|
|
191
237
|
if (remaining.length === 0) {
|
|
@@ -212,7 +258,8 @@ function configureVersionPruneCommand(cmd, commandName) {
|
|
|
212
258
|
.description(isAlias
|
|
213
259
|
? 'Alias for agents prune. Uninstalls agent CLI versions.'
|
|
214
260
|
: 'Uninstall agent CLI versions. Moves version data to trash for recovery.')
|
|
215
|
-
.option('-p, --project', 'Also clear the pinned version from .agents/agents.yaml in the current project')
|
|
261
|
+
.option('-p, --project', 'Also clear the pinned version from .agents/agents.yaml in the current project')
|
|
262
|
+
.option('--isolated', 'Only act on isolated installs (created with `agents add --isolated`). Refuses to remove a normal/default install and never touches your real ~/.<agent>.');
|
|
216
263
|
setHelpSections(cmd, {
|
|
217
264
|
examples: `
|
|
218
265
|
# Prune a specific version
|
|
@@ -223,11 +270,15 @@ function configureVersionPruneCommand(cmd, commandName) {
|
|
|
223
270
|
|
|
224
271
|
# Prune and also clear the project pin
|
|
225
272
|
agents ${commandName} claude@2.0.50 --project
|
|
273
|
+
|
|
274
|
+
# Cleanly remove an isolated copy, leaving your normal install alone
|
|
275
|
+
agents ${commandName} claude@2.1.112 --isolated
|
|
226
276
|
`,
|
|
227
277
|
notes: `
|
|
228
278
|
- Pruned version directories move to trash with their home/ data intact.
|
|
229
279
|
- Session file paths are rewritten so session history remains readable.
|
|
230
280
|
- Removing the default version unsets the default; run 'agents use' to pick a new one.
|
|
281
|
+
- --isolated restricts the operation to isolated installs and refuses to remove a normal/default version, so your existing setup is never disturbed.
|
|
231
282
|
- Reinstall any time with 'agents add'.
|
|
232
283
|
`,
|
|
233
284
|
});
|
|
@@ -239,6 +290,7 @@ export function registerVersionsCommands(program) {
|
|
|
239
290
|
.command('add <specs...>')
|
|
240
291
|
.description('Download and install agent CLI versions. Enables subsidized API usage through managed binaries.')
|
|
241
292
|
.option('-p, --project', 'Lock this version to the current project directory only, stored in project-root agents.yaml')
|
|
293
|
+
.option('--isolated', 'Install a fully self-contained copy that never touches your existing ~/.<agent> or default. Launch it explicitly with `agents run <agent>@<version>`. Cannot be combined with --project.')
|
|
242
294
|
.option('-y, --yes', 'Auto-accept defaults without prompting (useful for scripts and CI)');
|
|
243
295
|
setHelpSections(addCmd, {
|
|
244
296
|
examples: `
|
|
@@ -256,16 +308,26 @@ export function registerVersionsCommands(program) {
|
|
|
256
308
|
|
|
257
309
|
# Lock a version to this project only (won't affect global default)
|
|
258
310
|
agents add claude@2.1.100 --project
|
|
311
|
+
|
|
312
|
+
# Install a clean, separate copy that leaves your existing setup alone
|
|
313
|
+
agents add claude@2.1.112 --isolated
|
|
259
314
|
`,
|
|
260
315
|
notes: `
|
|
261
316
|
- The first version you install becomes the default automatically.
|
|
262
317
|
- 'add' does NOT change the default if a default already exists. Use 'agents use' to switch.
|
|
263
318
|
- Multi-account: each installed version has separate auth, so you can install the same agent twice for two accounts.
|
|
319
|
+
- --isolated installs a self-contained copy: it never sets the default, never creates the bare '<agent>' shim, and never backs up or symlinks your real ~/.<agent>. Run it with 'agents run <agent>@<version>' and remove it with 'agents remove <agent>@<version> --isolated'. Mutually exclusive with --project.
|
|
264
320
|
`,
|
|
265
321
|
});
|
|
266
322
|
addCmd.action(async (specs, options) => {
|
|
267
323
|
const isProject = options.project;
|
|
324
|
+
const isIsolated = options.isolated;
|
|
268
325
|
const skipPrompts = options.yes || !isInteractiveTerminal();
|
|
326
|
+
if (isIsolated && isProject) {
|
|
327
|
+
console.log(chalk.red('--isolated and --project cannot be combined.'));
|
|
328
|
+
console.log(chalk.gray('An isolated copy is global-but-separate; a project pin selects a shared install for one directory.'));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
269
331
|
for (const spec of specs) {
|
|
270
332
|
const parsed = parseAgentSpec(spec);
|
|
271
333
|
if (!parsed) {
|
|
@@ -276,6 +338,16 @@ export function registerVersionsCommands(program) {
|
|
|
276
338
|
const { agent, version } = parsed;
|
|
277
339
|
const agentConfig = AGENTS[agent];
|
|
278
340
|
warnAgentDeprecated(agent);
|
|
341
|
+
// Isolation relies on a config-dir env var to redirect the copy away
|
|
342
|
+
// from the user's real ~/.<agent>. Agents without one isolate only by
|
|
343
|
+
// adopting ~/.<agent> (which --isolated skips), so an isolated copy
|
|
344
|
+
// would silently read/write the real config. Refuse rather than lie.
|
|
345
|
+
if (isIsolated && !supportsIsolatedInstall(agent)) {
|
|
346
|
+
console.log(chalk.red(`${agentLabel(agentConfig.id)} does not support --isolated installs.`));
|
|
347
|
+
console.log(chalk.gray(` It has no config-directory env var, so it can only isolate by adopting ${agentConfig.configDir} — which --isolated deliberately avoids.`));
|
|
348
|
+
console.log(chalk.gray(` Supported with --isolated: ${CONFIG_ENV_ISOLATED_AGENTS.join(', ')}.`));
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
279
351
|
if (!agentConfig.npmPackage && !agentConfig.installScript) {
|
|
280
352
|
console.log(chalk.yellow(`${agentLabel(agentConfig.id)} has no npm package. Install manually.`));
|
|
281
353
|
continue;
|
|
@@ -301,6 +373,19 @@ export function registerVersionsCommands(program) {
|
|
|
301
373
|
alreadyInstalled = isVersionInstalled(agent, version);
|
|
302
374
|
}
|
|
303
375
|
if (alreadyInstalled) {
|
|
376
|
+
if (isIsolated) {
|
|
377
|
+
if (!isVersionIsolated(agent, installedAsVersion)) {
|
|
378
|
+
// A normal and an isolated install of the SAME version share one
|
|
379
|
+
// on-disk dir, so they can't coexist. Refuse rather than silently
|
|
380
|
+
// convert the user's existing (possibly default) install.
|
|
381
|
+
console.log(chalk.yellow(`${agentLabel(agentConfig.id)}@${installedAsVersion} is already installed as a normal (default-eligible) version.`));
|
|
382
|
+
console.log(chalk.gray(` Remove it first (agents remove ${agent}@${installedAsVersion}) then re-add with --isolated, or pick a different version.`));
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
// Already isolated: re-affirm the alias + marker idempotently.
|
|
386
|
+
finalizeIsolatedInstall(agent, installedAsVersion);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
304
389
|
console.log(chalk.gray(`${agentLabel(agentConfig.id)}@${installedAsVersion} already installed`));
|
|
305
390
|
// Ensure shim exists (in case it was deleted or needs updating)
|
|
306
391
|
createShim(agent);
|
|
@@ -312,15 +397,23 @@ export function registerVersionsCommands(program) {
|
|
|
312
397
|
});
|
|
313
398
|
if (result.success) {
|
|
314
399
|
spinner.succeed(`Installed ${agentLabel(agentConfig.id)}@${result.installedVersion}`);
|
|
400
|
+
const installedVersion = result.installedVersion || version;
|
|
401
|
+
// Track the concrete version so a `--project` pin records it instead
|
|
402
|
+
// of the `latest`/`oldest` alias.
|
|
403
|
+
installedAsVersion = installedVersion;
|
|
404
|
+
// Isolated installs stop here: no bare shim, no settings carry-over,
|
|
405
|
+
// no resource sync, no default switch, no PATH edits. Just a
|
|
406
|
+
// launchable versioned alias + the isolated marker, leaving the
|
|
407
|
+
// user's real ~/.<agent> and default untouched.
|
|
408
|
+
if (isIsolated) {
|
|
409
|
+
finalizeIsolatedInstall(agent, installedVersion);
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
315
412
|
// Create shim if first install
|
|
316
413
|
if (!shimExists(agent)) {
|
|
317
414
|
createShim(agent);
|
|
318
415
|
console.log(chalk.gray(` Created shim: ${getShimsDir()}/${agentConfig.cliCommand}`));
|
|
319
416
|
}
|
|
320
|
-
const installedVersion = result.installedVersion || version;
|
|
321
|
-
// Track the concrete version so a `--project` pin records it instead
|
|
322
|
-
// of the `latest`/`oldest` alias.
|
|
323
|
-
installedAsVersion = installedVersion;
|
|
324
417
|
// Seed the fresh version home with user settings from the current
|
|
325
418
|
// default version (settings.json, keybindings, codex config/auth).
|
|
326
419
|
// Gap-filling only — never overwrites what the new home has.
|
|
@@ -540,8 +633,11 @@ export function registerVersionsCommands(program) {
|
|
|
540
633
|
const agentConfig = AGENTS[agentId];
|
|
541
634
|
let selectedVersion = version;
|
|
542
635
|
if (!version) {
|
|
543
|
-
// Interactive version picker
|
|
544
|
-
|
|
636
|
+
// Interactive version picker. Isolated installs are walled off from the
|
|
637
|
+
// real ~/.<agent> on purpose, so they must never be selectable here —
|
|
638
|
+
// setting one as the default would repoint the real config at it. Filter
|
|
639
|
+
// them out (same as the `remove` picker), leaving only usable versions.
|
|
640
|
+
const versions = listInstalledVersions(agentId).filter((v) => !isVersionIsolated(agentId, v));
|
|
545
641
|
if (versions.length === 0) {
|
|
546
642
|
console.log(chalk.red(`No versions of ${agentLabel(agentConfig.id)} installed`));
|
|
547
643
|
console.log(chalk.gray(`Run: agents add ${agentId}@latest`));
|
|
@@ -602,6 +698,19 @@ export function registerVersionsCommands(program) {
|
|
|
602
698
|
}
|
|
603
699
|
// selectedVersion is guaranteed to be defined after the check above
|
|
604
700
|
const finalVersion = selectedVersion;
|
|
701
|
+
// An isolated install is deliberately walled off from the real
|
|
702
|
+
// ~/.<agent>. `agents use` would repoint the real config symlink at it
|
|
703
|
+
// (switchConfigSymlink) and carry settings forward INTO it
|
|
704
|
+
// (carryForwardSettings) — a direct breach of the isolation guarantee.
|
|
705
|
+
// Refuse for both the explicit `use <agent>@<isolated>` path (the picker
|
|
706
|
+
// above already filters isolated versions out of the interactive path).
|
|
707
|
+
// Isolated copies are launched explicitly via `agents run <agent>@<v>`.
|
|
708
|
+
if (isVersionIsolated(agentId, finalVersion)) {
|
|
709
|
+
console.log(chalk.red(`${agentLabel(agentConfig.id)}@${finalVersion} is an isolated install and can't be set as your active version.`));
|
|
710
|
+
console.log(chalk.gray(`Isolated copies stay walled off from your real ${agentConfig.configDir}. Run it directly: agents run ${agentId}@${finalVersion}`));
|
|
711
|
+
console.log(chalk.gray(`To install this version normally: agents add ${agentId}@${finalVersion}`));
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
605
714
|
if (options.project) {
|
|
606
715
|
// Set in project manifest
|
|
607
716
|
const projectManifestDir = path.join(process.cwd(), '.agents');
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,11 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
26
26
|
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
27
27
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
28
28
|
const VERSION = packageJson.version;
|
|
29
|
+
if (process.argv[2] === '__vault-age-helper') {
|
|
30
|
+
const { runVaultAgeHelperCli } = await import('./lib/secrets/vault-age-helper.js');
|
|
31
|
+
await runVaultAgeHelperCli();
|
|
32
|
+
process.exit(process.exitCode ?? 0);
|
|
33
|
+
}
|
|
29
34
|
import { NPM_PACKAGE_NAME, deriveGlobalPrefix, detectPackageManager, installPackageIntoPrefix, installPackageWithBun, verifyInstalledVersion, refreshAliasShims, downloadVerifiedTarball, } from './lib/self-update.js';
|
|
30
35
|
// Detect dev/working-tree builds and default the noisy startup steps off.
|
|
31
36
|
// Three cases trip this:
|
|
@@ -50,7 +55,7 @@ if (IS_DEV_BUILD) {
|
|
|
50
55
|
// module on each invocation (which loaded the whole ~50-module tree before the
|
|
51
56
|
// first byte of output), the registry maps a command name to a thunk that
|
|
52
57
|
// imports only what that command needs. See src/lib/startup/command-registry.ts.
|
|
53
|
-
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadPackages,
|
|
58
|
+
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadResources, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadPackages, loadRoutines, loadMonitors, loadRun, loadFork, loadDefaults, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadCheck, loadStatus, loadProfiles, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadDrive, loadFactory, loadUsage, loadCost, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadHq, loadFeed, loadActivity, loadMailboxes, } from './lib/startup/command-registry.js';
|
|
54
59
|
import { applyGlobalHelpConventions } from './lib/help.js';
|
|
55
60
|
import { renderWhatsNew } from './lib/whats-new.js';
|
|
56
61
|
import { emit, redactArgs } from './lib/events.js';
|
|
@@ -68,9 +73,18 @@ if (process.argv[2] === '__shim') {
|
|
|
68
73
|
const code = await execShimPassthrough(agent, rawArgs, process.cwd(), pinned || undefined);
|
|
69
74
|
process.exit(code);
|
|
70
75
|
}
|
|
76
|
+
if (process.argv[2] === '__daemon-run') {
|
|
77
|
+
const { runDaemon } = await import('./lib/daemon.js');
|
|
78
|
+
await runDaemon();
|
|
79
|
+
process.exit(process.exitCode ?? 0);
|
|
80
|
+
}
|
|
81
|
+
// White-label: the shim for a brand (e.g. `jack`) exports AGENTS_BRAND, so the
|
|
82
|
+
// CLI presents its own name/help/errors as the brand. Unbranded (AGENTS_BRAND
|
|
83
|
+
// unset) resolves to 'agents' and everything below is byte-identical to before.
|
|
84
|
+
const BRAND = resolveBrandName();
|
|
71
85
|
const program = new Command();
|
|
72
86
|
program
|
|
73
|
-
.name(
|
|
87
|
+
.name(BRAND)
|
|
74
88
|
.description('Environment manager for AI agents')
|
|
75
89
|
.version(VERSION)
|
|
76
90
|
.option('--verbose', 'Show startup self-heal details on stderr')
|
|
@@ -86,7 +100,7 @@ program
|
|
|
86
100
|
function auditCommandPath(cmd) {
|
|
87
101
|
const parts = [];
|
|
88
102
|
let c = cmd;
|
|
89
|
-
while (c && c.name() && c.name() !==
|
|
103
|
+
while (c && c.name() && c.name() !== BRAND) {
|
|
90
104
|
parts.unshift(c.name());
|
|
91
105
|
c = c.parent;
|
|
92
106
|
}
|
|
@@ -129,11 +143,36 @@ program.hook('postAction', (_thisCommand, actionCommand) => {
|
|
|
129
143
|
// Best-effort completion record; the start line is the durable audit fact.
|
|
130
144
|
}
|
|
131
145
|
});
|
|
146
|
+
/**
|
|
147
|
+
* Skin the static root help for a brand: rewrite the visible `agents` command
|
|
148
|
+
* examples to the brand name and drop lines for commands this brand disabled.
|
|
149
|
+
* A no-op for the unbranded `agents` CLI with nothing disabled.
|
|
150
|
+
*/
|
|
151
|
+
function brandRootHelp(raw) {
|
|
152
|
+
let text = raw;
|
|
153
|
+
if (BRAND !== 'agents') {
|
|
154
|
+
text = text
|
|
155
|
+
.replace(/Usage: agents /g, `Usage: ${BRAND} `)
|
|
156
|
+
.replace(/^ {2}agents /gm, ` ${BRAND} `)
|
|
157
|
+
.replace(/Run 'agents /g, `Run '${BRAND} `);
|
|
158
|
+
}
|
|
159
|
+
const disabled = disabledCommandsForActiveBrand();
|
|
160
|
+
if (disabled.size > 0) {
|
|
161
|
+
text = text
|
|
162
|
+
.split('\n')
|
|
163
|
+
.filter((line) => {
|
|
164
|
+
const m = line.match(/^ {2}([a-z][\w-]*)/);
|
|
165
|
+
return !(m && disabled.has(m[1]));
|
|
166
|
+
})
|
|
167
|
+
.join('\n');
|
|
168
|
+
}
|
|
169
|
+
return text;
|
|
170
|
+
}
|
|
132
171
|
// Custom help for the main program only
|
|
133
172
|
const originalHelpInformation = program.helpInformation.bind(program);
|
|
134
173
|
program.helpInformation = function () {
|
|
135
|
-
if (this.name() ===
|
|
136
|
-
return `Usage: agents [command] [options]
|
|
174
|
+
if (this.name() === BRAND && !this.parent) {
|
|
175
|
+
return brandRootHelp(`Usage: agents [command] [options]
|
|
137
176
|
|
|
138
177
|
Install, configure, run, and dispatch AI coding agents from one place.
|
|
139
178
|
Works with Claude, Codex, Gemini, Cursor, OpenCode, OpenClaw, and Droid.
|
|
@@ -154,6 +193,7 @@ Agent versions:
|
|
|
154
193
|
trash Inspect and restore soft-deleted version directories
|
|
155
194
|
view [agent[@version]] List versions, or inspect one in detail
|
|
156
195
|
inspect <target> Deep details for one agent+version, or a DotAgents repo (user|system|project|alias|path)
|
|
196
|
+
resources Show merged DotAgents resources with their winning layer
|
|
157
197
|
|
|
158
198
|
Agent configuration (synced across versions):
|
|
159
199
|
rules Instructions given to agents (CLAUDE.md, etc.)
|
|
@@ -173,6 +213,7 @@ Run and dispatch:
|
|
|
173
213
|
run <agent|profile> [prompt] Run an agent. Omit prompt for interactive mode.
|
|
174
214
|
defaults Configure run defaults by agent/version selector
|
|
175
215
|
teams Coordinate multiple agents on shared work
|
|
216
|
+
hq JSON bridge for the interactive Agents HQ floor
|
|
176
217
|
routines Run agents on a cron schedule (scheduler auto-starts)
|
|
177
218
|
webhook Receive signed GitHub/Linear webhooks for trigger routines
|
|
178
219
|
funnel Expose a webhook receiver through Tailscale Funnel
|
|
@@ -182,6 +223,7 @@ Run and dispatch:
|
|
|
182
223
|
pty Drive interactive terminal programs (REPLs, TUIs) via a persistent PTY session
|
|
183
224
|
|
|
184
225
|
Credentials and profiles:
|
|
226
|
+
profile Activate resource profiles across skills, MCP, permissions, and secrets
|
|
185
227
|
profiles Bundles of (host CLI, endpoint, model, auth)
|
|
186
228
|
secrets Keychain-backed env bundles; use 'secrets exec <bundle> -- <cmd>' to inject into a subprocess
|
|
187
229
|
|
|
@@ -213,7 +255,7 @@ Options:
|
|
|
213
255
|
--verbose Show startup self-heal details on stderr
|
|
214
256
|
|
|
215
257
|
System config lives in ~/.agents/.system/. Run 'agents <command> --help' for details.
|
|
216
|
-
|
|
258
|
+
`);
|
|
217
259
|
}
|
|
218
260
|
return originalHelpInformation();
|
|
219
261
|
};
|
|
@@ -251,6 +293,7 @@ async function showWhatsNew(fromVersion, toVersion) {
|
|
|
251
293
|
}
|
|
252
294
|
const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
253
295
|
import { getUpdateCheckPath, getMigratedSentinelPath, getUserAgentsDir, getRuntimeStateDir } from './lib/state.js';
|
|
296
|
+
import { resolveBrandName, disabledCommandsForActiveBrand } from './lib/brand.js';
|
|
254
297
|
import { readUpdateCache, saveUpdateCheck, dismissUpdateVersion, shouldPromptUpgrade, findAgentsCliInstalls, } from './lib/self-update.js';
|
|
255
298
|
const UPDATE_CHECK_FILE = getUpdateCheckPath();
|
|
256
299
|
/**
|
|
@@ -668,7 +711,9 @@ async function registerEagerForRequest(name) {
|
|
|
668
711
|
async function registerAllEagerCommands() {
|
|
669
712
|
await reg(loadView);
|
|
670
713
|
await reg(loadShare);
|
|
714
|
+
await reg(loadSend);
|
|
671
715
|
await reg(loadInspect);
|
|
716
|
+
await reg(loadResources);
|
|
672
717
|
await reg(loadFeedback);
|
|
673
718
|
await reg(loadCommands);
|
|
674
719
|
await reg(loadHooks);
|
|
@@ -686,7 +731,6 @@ async function registerAllEagerCommands() {
|
|
|
686
731
|
await reg(loadVersions);
|
|
687
732
|
await reg(loadImport);
|
|
688
733
|
await reg(loadPackages);
|
|
689
|
-
await reg(loadDaemon);
|
|
690
734
|
await reg(loadRoutines);
|
|
691
735
|
await reg(loadMonitors);
|
|
692
736
|
await reg(loadRun);
|
|
@@ -703,6 +747,7 @@ async function registerAllEagerCommands() {
|
|
|
703
747
|
registerExecAliasCommand(program);
|
|
704
748
|
await reg(loadProfiles);
|
|
705
749
|
await reg(loadSecrets);
|
|
750
|
+
await reg(loadLogin);
|
|
706
751
|
await reg(loadWallet);
|
|
707
752
|
await reg(loadHelper);
|
|
708
753
|
await reg(loadMenubar);
|
|
@@ -717,6 +762,7 @@ async function registerAllEagerCommands() {
|
|
|
717
762
|
await reg(loadOutput);
|
|
718
763
|
await reg(loadBudget);
|
|
719
764
|
await reg(loadAlias);
|
|
765
|
+
await reg(loadMine);
|
|
720
766
|
await reg(loadPty);
|
|
721
767
|
await reg(loadTmux);
|
|
722
768
|
await reg(loadWatchdog);
|
|
@@ -728,7 +774,9 @@ async function registerAllEagerCommands() {
|
|
|
728
774
|
await reg(loadAudit);
|
|
729
775
|
await reg(loadWebhook);
|
|
730
776
|
await reg(loadFunnel);
|
|
777
|
+
await reg(loadHq);
|
|
731
778
|
await reg(loadFeed);
|
|
779
|
+
await reg(loadActivity);
|
|
732
780
|
await reg(loadMailboxes);
|
|
733
781
|
await reg(loadSsh);
|
|
734
782
|
registerJobsCronAliasCommand(program, 'jobs');
|
|
@@ -738,6 +786,7 @@ async function registerAllEagerCommands() {
|
|
|
738
786
|
await reg(loadPush);
|
|
739
787
|
await reg(loadRepo);
|
|
740
788
|
await reg(loadSetup);
|
|
789
|
+
await reg(loadUninstall);
|
|
741
790
|
}
|
|
742
791
|
/** Calculate the Levenshtein edit distance between two strings. */
|
|
743
792
|
function levenshtein(a, b) {
|
|
@@ -794,13 +843,19 @@ const verboseStartup = passedArgs.includes('--verbose');
|
|
|
794
843
|
// Help and version output are pure documentation — they must never gate on
|
|
795
844
|
// setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
|
|
796
845
|
const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
|
|
846
|
+
// White-label: a brand can hide built-in top-level commands. A hidden command
|
|
847
|
+
// must behave as if it doesn't exist under this brand (unknown-command +
|
|
848
|
+
// spellcheck), while `agents` itself is unaffected. `brandDisabled` is empty for
|
|
849
|
+
// the unbranded CLI, so all of this is a no-op there.
|
|
850
|
+
const brandDisabled = disabledCommandsForActiveBrand();
|
|
851
|
+
const requestedIsDisabled = requestedCommand !== undefined && brandDisabled.has(requestedCommand);
|
|
797
852
|
// `--host` passthrough: run this invocation on a remote machine over SSH instead
|
|
798
853
|
// of locally. Handled before any local command registration / update check /
|
|
799
854
|
// background sync — a remote run needs none of that. Only the allowlisted
|
|
800
855
|
// read-only + config + teams commands route here; `run`/`sessions` are absent
|
|
801
856
|
// from the table and fall through to their own richer `--host` handling below.
|
|
802
857
|
// `--help`/`--version` stay local (docs must work without a reachable host).
|
|
803
|
-
if (requestedCommand !== undefined && !helpOrVersionRequested) {
|
|
858
|
+
if (requestedCommand !== undefined && !helpOrVersionRequested && !requestedIsDisabled) {
|
|
804
859
|
const { maybeRunOnHost } = await import('./lib/hosts/passthrough.js');
|
|
805
860
|
if (await maybeRunOnHost(requestedCommand, passedArgs)) {
|
|
806
861
|
process.exit(process.exitCode ?? 0);
|
|
@@ -809,7 +864,13 @@ if (requestedCommand !== undefined && !helpOrVersionRequested) {
|
|
|
809
864
|
// Register only the command(s) this invocation actually uses. Lazy commands
|
|
810
865
|
// (sessions/teams/cloud) are handled after applyGlobalHelpConventions below.
|
|
811
866
|
const isLazyRequest = requestedCommand !== undefined && LAZY_COMMAND_NAMES.has(requestedCommand);
|
|
812
|
-
if (
|
|
867
|
+
if (requestedIsDisabled) {
|
|
868
|
+
// The brand turned this command off: register the full tree so the "did you
|
|
869
|
+
// mean" picker still works, then strip the disabled commands below so the
|
|
870
|
+
// request resolves as unknown.
|
|
871
|
+
await registerAllEagerCommands();
|
|
872
|
+
}
|
|
873
|
+
else if (requestedCommand !== undefined && !isLazyRequest) {
|
|
813
874
|
const known = await registerEagerForRequest(requestedCommand);
|
|
814
875
|
if (!known) {
|
|
815
876
|
// Unknown top-level command: register the full tree so the "did you mean"
|
|
@@ -827,10 +888,19 @@ if (requestedCommand !== undefined && !isLazyRequest) {
|
|
|
827
888
|
applyGlobalHelpConventions(program);
|
|
828
889
|
// Lazy commands pull in the SQLite-backed session/cloud stack; register them
|
|
829
890
|
// only when explicitly requested, keeping lightweight commands off that path.
|
|
830
|
-
if (isLazyRequest) {
|
|
891
|
+
if (isLazyRequest && !requestedIsDisabled) {
|
|
831
892
|
for (const loader of COMMAND_LOADERS[requestedCommand])
|
|
832
893
|
await reg(loader);
|
|
833
894
|
}
|
|
895
|
+
// White-label: remove any commands this brand disabled so they resolve as
|
|
896
|
+
// unknown (the command:* handler then reports "unknown command"). Unbranded or
|
|
897
|
+
// nothing-disabled → no-op. Done after all registration paths above.
|
|
898
|
+
if (brandDisabled.size > 0) {
|
|
899
|
+
const kept = program.commands.filter((c) => !brandDisabled.has(c.name()));
|
|
900
|
+
if (kept.length !== program.commands.length) {
|
|
901
|
+
program.commands = kept;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
834
904
|
// Pure documentation paths (--version / --help / -h) return immediately: skip
|
|
835
905
|
// the update check (PATH scan + cache read) and the detached background sync
|
|
836
906
|
// (spawns a child process) that every other invocation runs.
|
|
@@ -865,8 +935,9 @@ if (firstRun) {
|
|
|
865
935
|
process.exit(0);
|
|
866
936
|
}
|
|
867
937
|
// Every command requires the system repo to be cloned first. `setup` is the
|
|
868
|
-
//
|
|
869
|
-
|
|
938
|
+
// command that does the cloning; `uninstall` is its reverse and must run even
|
|
939
|
+
// from a broken/half-setup state (that is exactly when you want to tear down).
|
|
940
|
+
const SETUP_EXEMPT_COMMANDS = new Set(['setup', 'help', 'uninstall']);
|
|
870
941
|
// Fold legacy ~/.agents-system/ into ~/.agents/.system/ BEFORE ensureInitialized
|
|
871
942
|
// runs. ensureInitialized checks for .git inside the new path; if the user is
|
|
872
943
|
// upgrading from a layout where .git lives under the legacy path, the check
|