@phnx-labs/agents-cli 1.22.114 → 1.22.115
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 +110 -0
- package/README.md +22 -102
- package/dist/browser.js +0 -0
- package/dist/cli/command-registry.js +0 -7
- package/dist/commands/accounts.d.ts +3 -77
- package/dist/commands/accounts.js +13 -366
- package/dist/commands/audit.js +1 -1
- package/dist/commands/auth.js +0 -2
- package/dist/commands/browser.d.ts +53 -0
- package/dist/commands/browser.js +199 -9
- package/dist/commands/doctor.d.ts +2 -3
- package/dist/commands/doctor.js +0 -11
- package/dist/commands/events.d.ts +1 -3
- package/dist/commands/events.js +4 -7
- package/dist/commands/exec.d.ts +0 -1
- package/dist/commands/exec.js +2 -7
- package/dist/commands/feed-watch.js +52 -8
- package/dist/commands/focus.js +1 -1
- package/dist/commands/go.d.ts +0 -17
- package/dist/commands/go.js +2 -19
- package/dist/commands/logs.js +1 -1
- package/dist/commands/mcp.js +8 -83
- package/dist/commands/memory.js +4 -47
- package/dist/commands/message.js +4 -4
- package/dist/commands/plugins.js +1 -93
- package/dist/commands/repo.js +0 -44
- package/dist/commands/resume.js +4 -1
- package/dist/commands/secrets-passthrough.js +2 -2
- package/dist/commands/send.d.ts +4 -4
- package/dist/commands/send.js +6 -51
- package/dist/commands/sessions-backup-setup.js +1 -1
- package/dist/commands/sessions-resume.js +0 -1
- package/dist/commands/sessions-share.d.ts +5 -7
- package/dist/commands/sessions-share.js +98 -49
- package/dist/commands/sessions.js +1 -8
- package/dist/commands/setup-browser.js +18 -2
- package/dist/commands/setup-computer.js +20 -6
- package/dist/commands/setup-secrets.js +24 -4
- package/dist/commands/setup-terminal.d.ts +3 -0
- package/dist/commands/setup-terminal.js +22 -0
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +20 -10
- package/dist/commands/skills.js +0 -8
- package/dist/commands/ssh.d.ts +6 -0
- package/dist/commands/ssh.js +92 -233
- package/dist/commands/sync.js +14 -5
- package/dist/commands/teams.js +1 -1
- package/dist/commands/traces.js +1 -1
- package/dist/lib/accounts/add.d.ts +0 -5
- package/dist/lib/accounts/add.js +1 -7
- package/dist/lib/artifacts-client.d.ts +20 -0
- package/dist/lib/artifacts-client.js +46 -0
- package/dist/lib/auth-mint.js +2 -2
- package/dist/lib/browser/runtime-state.d.ts +55 -0
- package/dist/lib/browser/runtime-state.js +99 -18
- package/dist/lib/browser/service.js +21 -1
- package/dist/lib/cli-resources.js +3 -1
- package/dist/lib/cloud/dispatch.js +1 -1
- package/dist/lib/cloudflare/creds.d.ts +10 -0
- package/dist/lib/cloudflare/creds.js +46 -0
- package/dist/lib/cloudflare/provision.d.ts +35 -0
- package/dist/lib/cloudflare/provision.js +144 -0
- package/dist/lib/computer/sessions-list.d.ts +55 -0
- package/dist/lib/computer/sessions-list.js +168 -1
- package/dist/lib/daemon/daemon.js +8 -1
- package/dist/lib/daemon/feed-stream-service.d.ts +23 -0
- package/dist/lib/daemon/feed-stream-service.js +40 -0
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/devices/connect.d.ts +49 -5
- package/dist/lib/devices/connect.js +169 -21
- package/dist/lib/feed/envelope.d.ts +79 -0
- package/dist/lib/feed/envelope.js +23 -0
- package/dist/lib/feed/events.d.ts +6 -0
- package/dist/lib/feed/events.js +8 -0
- package/dist/lib/feed/hub-server.d.ts +86 -0
- package/dist/lib/feed/hub-server.js +334 -0
- package/dist/lib/feed/hub.d.ts +95 -0
- package/dist/lib/feed/hub.js +255 -0
- package/dist/lib/feed/tool-activity.d.ts +108 -0
- package/dist/lib/feed/tool-activity.js +313 -0
- package/dist/lib/feed/tools.d.ts +198 -0
- package/dist/lib/feed/tools.js +265 -0
- package/dist/lib/feed/watch.d.ts +50 -50
- package/dist/lib/feed/watch.js +147 -16
- package/dist/lib/format.d.ts +1 -1
- package/dist/lib/format.js +1 -1
- package/dist/lib/git.d.ts +0 -16
- package/dist/lib/git.js +0 -58
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hosts/remote-cmd.d.ts +51 -1
- package/dist/lib/hosts/remote-cmd.js +125 -8
- package/dist/lib/hosts/remote-cmd.test-fixture.d.ts +2 -0
- package/dist/lib/hosts/remote-cmd.test-fixture.js +22 -0
- package/dist/lib/mcp.js +17 -11
- package/dist/lib/probe.d.ts +4 -1
- package/dist/lib/probe.js +5 -2
- package/dist/lib/pwsh.d.ts +33 -0
- package/dist/lib/pwsh.js +56 -0
- package/dist/lib/redact.d.ts +8 -0
- package/dist/lib/redact.js +11 -0
- package/dist/lib/refresh.d.ts +6 -2
- package/dist/lib/refresh.js +92 -72
- package/dist/lib/secrets-cli.d.ts +11 -0
- package/dist/lib/secrets-cli.js +30 -0
- package/dist/lib/secrets-client.js +3 -2
- package/dist/lib/session/detached.d.ts +7 -0
- package/dist/lib/session/detached.js +29 -0
- package/dist/lib/session/remote/peer-stream.d.ts +24 -2
- package/dist/lib/session/remote/peer-stream.js +33 -6
- package/dist/lib/session/sync/backend.d.ts +3 -3
- package/dist/lib/session/sync/backend.js +3 -3
- package/dist/lib/session/sync/provision.d.ts +1 -1
- package/dist/lib/session/sync/provision.js +2 -2
- package/dist/lib/sessions-client.js +0 -3
- package/dist/lib/setup-tool-install.d.ts +3 -0
- package/dist/lib/setup-tool-install.js +26 -0
- package/dist/lib/setup-tool-status.d.ts +22 -0
- package/dist/lib/setup-tool-status.js +215 -0
- package/dist/lib/share-runtime.d.ts +11 -0
- package/dist/lib/share-runtime.js +63 -0
- package/dist/lib/smart-launch.d.ts +1 -5
- package/dist/lib/smart-launch.js +3 -11
- package/dist/lib/ssh-exec.d.ts +44 -0
- package/dist/lib/ssh-exec.js +119 -0
- package/dist/lib/startup/command-registry.d.ts +6 -4
- package/dist/lib/startup/command-registry.js +10 -7
- package/dist/lib/state.js +2 -2
- package/dist/lib/storage/selection.d.ts +2 -2
- package/dist/lib/storage/selection.js +2 -2
- package/dist/lib/sync-umbrella.d.ts +5 -0
- package/dist/lib/sync-umbrella.js +18 -10
- package/dist/lib/traces/backend.d.ts +1 -2
- package/dist/lib/traces/backend.js +1 -2
- package/dist/lib/traces/provision.d.ts +1 -1
- package/dist/lib/traces/provision.js +2 -2
- package/dist/lib/types.d.ts +8 -6
- package/package.json +2 -3
- package/dist/commands/artifacts-setup.d.ts +0 -53
- package/dist/commands/artifacts-setup.js +0 -161
- package/dist/commands/artifacts.d.ts +0 -18
- package/dist/commands/artifacts.js +0 -58
- package/dist/commands/attach.d.ts +0 -12
- package/dist/commands/attach.js +0 -86
- package/dist/commands/auth-mint.d.ts +0 -12
- package/dist/commands/auth-mint.js +0 -108
- package/dist/commands/reconnect.d.ts +0 -46
- package/dist/commands/reconnect.js +0 -115
- package/dist/commands/share.d.ts +0 -293
- package/dist/commands/share.js +0 -1424
- package/dist/lib/share/analytics.d.ts +0 -13
- package/dist/lib/share/analytics.js +0 -45
- package/dist/lib/share/backend.d.ts +0 -120
- package/dist/lib/share/backend.js +0 -176
- package/dist/lib/share/capture.d.ts +0 -31
- package/dist/lib/share/capture.js +0 -174
- package/dist/lib/share/config.d.ts +0 -72
- package/dist/lib/share/config.js +0 -211
- package/dist/lib/share/delete.d.ts +0 -123
- package/dist/lib/share/delete.js +0 -173
- package/dist/lib/share/html.d.ts +0 -20
- package/dist/lib/share/html.js +0 -88
- package/dist/lib/share/http-error.d.ts +0 -53
- package/dist/lib/share/http-error.js +0 -65
- package/dist/lib/share/og.d.ts +0 -26
- package/dist/lib/share/og.js +0 -84
- package/dist/lib/share/provision.d.ts +0 -127
- package/dist/lib/share/provision.js +0 -285
- package/dist/lib/share/publish.d.ts +0 -379
- package/dist/lib/share/publish.js +0 -818
- package/dist/lib/share/worker-template.d.ts +0 -27
- package/dist/lib/share/worker-template.js +0 -2424
- package/dist/lib/storage/index.d.ts +0 -14
- package/dist/lib/storage/index.js +0 -14
- package/dist/lib/storage/visibility.d.ts +0 -82
- package/dist/lib/storage/visibility.js +0 -99
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
1
|
import chalk from 'chalk';
|
|
4
|
-
import { password
|
|
5
|
-
import { readClaudeAccountEmail, resolveClaudeSetupToken, resolveClaudeSetupTokenForEmail, seedClaudeWorkerHomeIdentity } from '../lib/claude-account-token.js';
|
|
2
|
+
import { password } from '@inquirer/prompts';
|
|
6
3
|
import { setHelpSections } from '../lib/help.js';
|
|
7
4
|
import { readMeta, updateMeta } from '../lib/state.js';
|
|
8
5
|
import { machineId } from '../lib/machine-id.js';
|
|
9
|
-
import { ALL_AGENT_IDS,
|
|
10
|
-
import { getGlobalDefault,
|
|
11
|
-
import { assertNativeAccountNameable
|
|
6
|
+
import { ALL_AGENT_IDS, resolveAgentName } from '../lib/agents.js';
|
|
7
|
+
import { getGlobalDefault, listInstalledVersions } from '../lib/installations/versions.js';
|
|
8
|
+
import { assertNativeAccountNameable } from '../lib/account-capabilities.js';
|
|
12
9
|
import { collectRunCandidates } from '../lib/accounting/rotate.js';
|
|
13
|
-
import { isInteractiveTerminal, isPromptCancelled
|
|
10
|
+
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
14
11
|
import { buildSwitchAccountChoices, formatAccountLimits, pickSwitchAccount } from './run-account-picker.js';
|
|
15
|
-
import { profileExists, readProfile } from '../lib/profiles.js';
|
|
16
12
|
import { assertCredentialTransportHostPinned, resolveHostSshTarget } from '../lib/hosts/credential-transport.js';
|
|
17
13
|
import { resolveRemoteOsSync } from '../lib/hosts/remote-os.js';
|
|
18
14
|
import { renderAccountFleetMatrix } from '../lib/devices/harness-inventory.js';
|
|
@@ -24,23 +20,12 @@ import { ensureAdoptedDefaultRepoint } from '../lib/exec-account-home.js';
|
|
|
24
20
|
import { acquireAuthOperationLock } from '../lib/accounts/auth-operation-lock.js';
|
|
25
21
|
import { isSecretsClientError, pushBundleToHost, readAndResolveBundleEnv, readBundle } from '../lib/secrets-client.js';
|
|
26
22
|
import { getAccountProvider, listAccountProviders, providerAuthenticatesHarness } from '../lib/account-provider-registry.js';
|
|
27
|
-
import { accountBindings, addAccount,
|
|
28
|
-
import { registerMintCommand } from './auth-mint.js';
|
|
23
|
+
import { accountBindings, addAccount, assertUnambiguousNativeAccount, findAccount, findUnifiedAccount, inspectAccount, listNativeAccounts, nativeAccountHome, parseAccountSelector, readAccountRegistry, removeAccount, renameAccount, setAccountSecret } from '../lib/account-registry.js';
|
|
29
24
|
/** Comma-joined list of harnesses `accounts add` can drive today. */
|
|
30
25
|
function addSupportedList() {
|
|
31
26
|
return supportedAddHarnesses().join(', ');
|
|
32
27
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Legacy-verb retirement: the command is created with `{ hidden: true }` (help
|
|
35
|
-
* no longer lists it) and this hook prints the pointer to its replacement when
|
|
36
|
-
* it runs. Verbs keep executing for one release.
|
|
37
|
-
*/
|
|
38
|
-
function retireVerb(cmd, pointer) {
|
|
39
|
-
cmd.hook('preAction', () => {
|
|
40
|
-
console.error(chalk.gray(`'agents accounts ${cmd.name()}' is replaced by '${pointer}' — still works this release.`));
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
/** Shared result printer for `accounts add` / `accounts login` / hidden `connect`. */
|
|
28
|
+
/** Shared result printer for `accounts add` / `accounts login`. */
|
|
44
29
|
function printAddResult(result, json) {
|
|
45
30
|
if (json) {
|
|
46
31
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -78,85 +63,6 @@ async function runAccountsAction(command, fn) {
|
|
|
78
63
|
cleanCommandError(command, err);
|
|
79
64
|
}
|
|
80
65
|
}
|
|
81
|
-
function parseInstallation(raw) {
|
|
82
|
-
const at = raw.lastIndexOf('@');
|
|
83
|
-
if (at < 1 || at === raw.length - 1)
|
|
84
|
-
throw new Error(`Expected <agent>@<version>, got '${raw}'.`);
|
|
85
|
-
const agent = resolveAgentName(raw.slice(0, at));
|
|
86
|
-
if (!agent)
|
|
87
|
-
throw new Error(`Unknown agent '${raw.slice(0, at)}'.`);
|
|
88
|
-
return { agent, version: raw.slice(at + 1) };
|
|
89
|
-
}
|
|
90
|
-
export async function nativeIdentityFromSource(raw) {
|
|
91
|
-
const parsed = parseInstallation(raw);
|
|
92
|
-
const capability = nativeAccountCapability(parsed.agent);
|
|
93
|
-
assertNativeAccountNameable(parsed.agent);
|
|
94
|
-
if (!listInstalledVersions(parsed.agent).includes(parsed.version))
|
|
95
|
-
throw new Error(`${raw} is not installed.`);
|
|
96
|
-
const info = await getAccountInfo(parsed.agent, getVersionHomePath(parsed.agent, parsed.version));
|
|
97
|
-
const identityKey = nativeIdentityKey(info, capability);
|
|
98
|
-
if (!identityKey)
|
|
99
|
-
throw new Error(`${raw} has no stable signed-in identity. Run it and complete its normal login first.`);
|
|
100
|
-
return { agent: parsed.agent, version: parsed.version, identityKey, identityLabel: info.email ?? undefined, scope: capability.scope };
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Classify + validate an attach target, rejecting a typo BEFORE any binding is
|
|
104
|
-
* written: it must be an existing custom-harness profile, an installed
|
|
105
|
-
* `agent@version`, or a known harness id.
|
|
106
|
-
*/
|
|
107
|
-
export function classifyAttachTarget(target) {
|
|
108
|
-
if (profileExists(target))
|
|
109
|
-
return { kind: 'profile', profile: readProfile(target) };
|
|
110
|
-
if (target.includes('@')) {
|
|
111
|
-
const at = target.lastIndexOf('@');
|
|
112
|
-
const agent = resolveAgentName(target.slice(0, at));
|
|
113
|
-
const version = target.slice(at + 1);
|
|
114
|
-
if (!agent)
|
|
115
|
-
throw new Error(`Unknown harness '${target.slice(0, at)}' in target '${target}'.`);
|
|
116
|
-
if (!version)
|
|
117
|
-
throw new Error(`Target '${target}' is missing a version.`);
|
|
118
|
-
if (!listInstalledVersions(agent).includes(version))
|
|
119
|
-
throw new Error(`${agent}@${version} is not installed.`);
|
|
120
|
-
return { kind: 'installation', agent, version };
|
|
121
|
-
}
|
|
122
|
-
const agent = resolveAgentName(target);
|
|
123
|
-
if (agent)
|
|
124
|
-
return { kind: 'device-agent', agent };
|
|
125
|
-
throw new Error(`Unknown attach target '${target}'. Expected an installed <agent>@<version>, a harness id, or an existing custom harness profile.`);
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Persist the attached setup-token to a per-version `.oauth_token` file so an
|
|
129
|
-
* INTERACTIVE Claude launch on a keychain-less Linux worker can authenticate from it.
|
|
130
|
-
* Headless runs inject the token via `buildExecEnv`. Since PHNX-3502 an interactive
|
|
131
|
-
* launch on a worker-role device ALSO injects it through `claudeAdapter.applyExecConfigEnv`
|
|
132
|
-
* (only a headed `personal`/`desktop` device defers to its per-version native login), and
|
|
133
|
-
* the shim's Linux fallback (`claudeAdapter.shimConfigEnvBash`) reads exactly this file —
|
|
134
|
-
* so writing it is what makes a freshly-attached setup-token visible to that shim fallback
|
|
135
|
-
* on a worker. macOS keeps the credential in the keychain, so `resolveClaudeSetupToken`
|
|
136
|
-
* returns null there and this is a no-op off Linux.
|
|
137
|
-
*/
|
|
138
|
-
export function writeClaudeInteractiveOauthToken(target, targetAgent, email) {
|
|
139
|
-
if (process.platform !== 'linux' || targetAgent !== 'claude' || target.kind !== 'installation')
|
|
140
|
-
return;
|
|
141
|
-
const versionHome = getVersionHomePath('claude', target.version);
|
|
142
|
-
const tokenPath = path.join(versionHome, '.claude', '.oauth_token');
|
|
143
|
-
// Resolve by the attached account's email when known (a freshly-seeded worker
|
|
144
|
-
// home the `.claude.json` read below could not key on yet), else by the home's
|
|
145
|
-
// own recorded identity for a re-point/detach.
|
|
146
|
-
const token = email
|
|
147
|
-
? resolveClaudeSetupTokenForEmail(email, versionHome)
|
|
148
|
-
: resolveClaudeSetupToken(versionHome);
|
|
149
|
-
// A re-point (attach B over A, or a detach) can leave no setup-token resolving for
|
|
150
|
-
// this version — B's may not be minted yet. A leftover file from the previous binding
|
|
151
|
-
// would silently authenticate interactive runs as the OLD account (the shim's Linux
|
|
152
|
-
// fallback reads it), so clear it rather than leave it stale.
|
|
153
|
-
if (!token) {
|
|
154
|
-
fs.rmSync(tokenPath, { force: true });
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
|
|
158
|
-
fs.writeFileSync(tokenPath, token, { mode: 0o600 });
|
|
159
|
-
}
|
|
160
66
|
export function parseBundleKey(raw) {
|
|
161
67
|
const colon = raw.indexOf(':');
|
|
162
68
|
if (colon < 1 || colon === raw.length - 1)
|
|
@@ -252,107 +158,6 @@ function parseHarness(agentRaw) {
|
|
|
252
158
|
throw new Error(`Unknown agent '${agentRaw}'.`);
|
|
253
159
|
return agentRaw;
|
|
254
160
|
}
|
|
255
|
-
/**
|
|
256
|
-
* Fold signed-in run candidates into distinct identities — the unit a label
|
|
257
|
-
* binds to. The same account signed into several versions is ONE row, so a
|
|
258
|
-
* multi-version single-account harness never demands a selector. Candidates
|
|
259
|
-
* with no stable identity (no accountKey, no email) are dropped: there is
|
|
260
|
-
* nothing durable for a label to bind to. Default-version identity first.
|
|
261
|
-
*/
|
|
262
|
-
export function groupLabelIdentities(candidates, defaultVersion) {
|
|
263
|
-
const byKey = new Map();
|
|
264
|
-
for (const candidate of candidates) {
|
|
265
|
-
const key = candidate.accountKey ?? candidate.email?.toLowerCase();
|
|
266
|
-
if (!key)
|
|
267
|
-
continue;
|
|
268
|
-
const row = byKey.get(key) ?? { identityKey: key, email: null, versions: [], isDefault: false };
|
|
269
|
-
row.email ??= candidate.email;
|
|
270
|
-
row.versions.push(candidate.version);
|
|
271
|
-
if (candidate.version === defaultVersion)
|
|
272
|
-
row.isDefault = true;
|
|
273
|
-
byKey.set(key, row);
|
|
274
|
-
}
|
|
275
|
-
return [...byKey.values()].sort((a, b) => a.isDefault !== b.isDefault ? (a.isDefault ? -1 : 1) : (a.email ?? a.identityKey).localeCompare(b.email ?? b.identityKey));
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Resolve which signed-in login a bare-harness `accounts label` call means.
|
|
279
|
-
* `collect` is injectable (the `resolveRunVersion` pattern) so tests drive the
|
|
280
|
-
* real selection path against fixture candidates.
|
|
281
|
-
*/
|
|
282
|
-
export async function resolveLabelIdentity(agent, accountSelector, collect = collectRunCandidates) {
|
|
283
|
-
const candidates = (await collect(agent)).filter(candidate => candidate.signedIn);
|
|
284
|
-
const identities = groupLabelIdentities(candidates, getGlobalDefault(agent));
|
|
285
|
-
if (identities.length === 0)
|
|
286
|
-
throw new Error(`No signed-in ${agent} account with a stable identity. Run the harness and complete its login first.`);
|
|
287
|
-
const needle = accountSelector?.toLowerCase();
|
|
288
|
-
if (needle) {
|
|
289
|
-
const match = identities.find(identity => identity.email?.toLowerCase() === needle || identity.identityKey.toLowerCase() === needle);
|
|
290
|
-
if (!match)
|
|
291
|
-
throw new Error(`Unknown ${agent} account '${accountSelector}'.`);
|
|
292
|
-
return { kind: 'selected', identity: match };
|
|
293
|
-
}
|
|
294
|
-
if (identities.length === 1)
|
|
295
|
-
return { kind: 'selected', identity: identities[0] };
|
|
296
|
-
return { kind: 'ambiguous', identities };
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Body of `accounts label`, exported with the injectable candidate collector
|
|
300
|
-
* so tests exercise everything but the TTY prompt itself.
|
|
301
|
-
*/
|
|
302
|
-
export async function runAccountsLabel(source, label, opts, collect = collectRunCandidates) {
|
|
303
|
-
if (source.includes('@')) {
|
|
304
|
-
// <harness>@<version> pins the login by where it is signed in, so a
|
|
305
|
-
// second selector can only contradict it.
|
|
306
|
-
if (opts.account)
|
|
307
|
-
throw new Error(`'${source}' already selects one login; drop --account.`);
|
|
308
|
-
const identity = await nativeIdentityFromSource(source);
|
|
309
|
-
const account = labelNativeAccount(identity.agent, identity.identityKey, identity.identityLabel, label, identity.scope);
|
|
310
|
-
console.log(chalk.green(`Labeled ${identity.agent} account ${identity.identityLabel ?? identity.identityKey} as '${account.name}'.`));
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
const agent = parseHarness(source);
|
|
314
|
-
assertNativeAccountNameable(agent);
|
|
315
|
-
const selection = await resolveLabelIdentity(agent, opts.account, collect);
|
|
316
|
-
let selected;
|
|
317
|
-
if (selection.kind === 'selected') {
|
|
318
|
-
selected = selection.identity;
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
if (!isInteractiveTerminal()) {
|
|
322
|
-
requireInteractiveSelection(`Selecting the ${agent} login to label`, [
|
|
323
|
-
`agents accounts label ${agent} ${label ?? '<label>'} --account <email|id>`,
|
|
324
|
-
`agents accounts label ${agent}@<version> ${label ?? '<label>'}`,
|
|
325
|
-
]);
|
|
326
|
-
}
|
|
327
|
-
const picked = await pickLabelIdentity(agent, selection.identities);
|
|
328
|
-
if (!picked)
|
|
329
|
-
return;
|
|
330
|
-
selected = picked;
|
|
331
|
-
}
|
|
332
|
-
const account = labelNativeAccount(agent, selected.identityKey, selected.email ?? undefined, label, nativeAccountCapability(agent).scope);
|
|
333
|
-
console.log(chalk.green(`Labeled ${agent} account ${selected.email ?? selected.identityKey} as '${account.name}'.`));
|
|
334
|
-
}
|
|
335
|
-
/** Prompt for the login a label should bind to. A cancelled picker writes nothing. */
|
|
336
|
-
async function pickLabelIdentity(agent, identities) {
|
|
337
|
-
const idW = Math.max(0, ...identities.map(identity => (identity.email ?? identity.identityKey).length));
|
|
338
|
-
const choices = identities.map(identity => ({
|
|
339
|
-
name: [
|
|
340
|
-
(identity.email ?? identity.identityKey).padEnd(idW),
|
|
341
|
-
identity.isDefault ? chalk.green('default') : ' ',
|
|
342
|
-
chalk.gray(identity.versions.join(', ')),
|
|
343
|
-
].join(' '),
|
|
344
|
-
value: identity.identityKey,
|
|
345
|
-
}));
|
|
346
|
-
try {
|
|
347
|
-
const key = await select({ message: `Select the ${agent} login to label:`, choices, loop: false });
|
|
348
|
-
return identities.find(identity => identity.identityKey === key) ?? null;
|
|
349
|
-
}
|
|
350
|
-
catch (err) {
|
|
351
|
-
if (isPromptCancelled(err))
|
|
352
|
-
return null;
|
|
353
|
-
throw err;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
161
|
/** Every account (native + provider) registered/usable for this harness, oldest-first by name. */
|
|
357
162
|
function accountsForHarness(agent) {
|
|
358
163
|
const meta = readMeta();
|
|
@@ -362,12 +167,12 @@ function accountsForHarness(agent) {
|
|
|
362
167
|
.map(account => ({ ...account, kind: 'provider' }));
|
|
363
168
|
return [...native, ...providers].sort((a, b) => a.name.localeCompare(b.name));
|
|
364
169
|
}
|
|
365
|
-
/** Named accounts that `
|
|
170
|
+
/** Named accounts that `accounts default` can pin for this harness. */
|
|
366
171
|
export async function listSwitchableAccounts(agent) {
|
|
367
172
|
return accountsForHarness(agent);
|
|
368
173
|
}
|
|
369
174
|
/**
|
|
370
|
-
* Pin the per-harness default
|
|
175
|
+
* Pin the per-harness default — the `accounts default` write path.
|
|
371
176
|
* Provider accounts must authenticate the harness; native accounts must belong to it.
|
|
372
177
|
*/
|
|
373
178
|
export function setDefaultAccount(agentRaw, name) {
|
|
@@ -418,7 +223,7 @@ async function switchAccountRows(agent) {
|
|
|
418
223
|
};
|
|
419
224
|
});
|
|
420
225
|
}
|
|
421
|
-
export async function
|
|
226
|
+
export async function runAccountsDefault(harness, accountName, opts) {
|
|
422
227
|
const agent = parseHarness(harness);
|
|
423
228
|
const rows = await switchAccountRows(agent);
|
|
424
229
|
if (accountName) {
|
|
@@ -556,7 +361,6 @@ agents accounts list --json
|
|
|
556
361
|
agents accounts list --fleet`,
|
|
557
362
|
notes: 'One row per account per harness: the account name, its usage bars (`*` stale), then nothing at all when the account is healthy. A state worth acting on trails the row — `rate-limited` (the usage snapshot via `deriveUsageStatusFromSnapshot`/`applyUsageHonesty`, never a probe 429), `expired`/`revoked`/`missing`, partial fleet coverage (`usable on 3 of 5 boxes`), then the exact repair command. `live`, `unverified`, `ready` and `per-device` are the ordinary cases and print nothing. For the identity behind an account use `agents accounts view <name>`; for per-device state use `--fleet`; for everything use `--json`. Reserved credential stores are not listed here; `agents secrets` is the place those show.',
|
|
558
363
|
});
|
|
559
|
-
registerMintCommand(accounts, undefined, { hidden: true });
|
|
560
364
|
const addCmd = accounts.command('add <target> [name]')
|
|
561
365
|
.description('Add an account. Harness form: add <harness> [name] runs the native login in a fresh credential slot and provisions workers. Provider form: add <name> --provider <p> --auth <t> stores a durable credential.')
|
|
562
366
|
.option('--api-key <key>', 'Worker credential for api-key harnesses (codex/grok/cursor/opencode); prompted when omitted')
|
|
@@ -658,13 +462,13 @@ agents accounts login kimi#main # per-device: logs THIS bo
|
|
|
658
462
|
.description('Set the fleet-wide default account for a harness (picker when no name)')
|
|
659
463
|
.option('--json', 'Machine-readable account list or the resulting default')
|
|
660
464
|
.action(async (harness, name, o, command) => {
|
|
661
|
-
await runAccountsAction(command, () =>
|
|
465
|
+
await runAccountsAction(command, () => runAccountsDefault(harness, name, { json: !!(o.json || command.optsWithGlobals().json) }));
|
|
662
466
|
});
|
|
663
467
|
setHelpSections(defaultCmd, {
|
|
664
468
|
examples: `agents accounts default claude
|
|
665
469
|
agents accounts default claude work
|
|
666
470
|
agents accounts default claude --json`,
|
|
667
|
-
notes: 'The one write path for the per-harness default
|
|
471
|
+
notes: 'The one write path for the per-harness default. Pass a name to skip the picker. Rotation already honors the default.',
|
|
668
472
|
});
|
|
669
473
|
const migrateCmd = accounts.command('migrate')
|
|
670
474
|
.description('Fold leftover per-account installations into 1 harness install + N credential slots')
|
|
@@ -732,24 +536,6 @@ agents accounts migrate --apply
|
|
|
732
536
|
agents accounts migrate --dry-run --device worker-1`,
|
|
733
537
|
notes: 'Folds leftover per-account installations (the old connect homes) into one managed install plus a HOME-shaped slot per identity. Empty logged-out homes and duplicate identities go to trash (agents trash restore reverses). A busy home is deferred, never moved. --apply is required to write; this upgrade only prints the dry-run report. Native OAuth files stay on this device inside the moved home.',
|
|
734
538
|
});
|
|
735
|
-
// Hidden legacy verb: connect is the old spelling of add.
|
|
736
|
-
const connectCmd = accounts.command('connect <harness> [name]', { hidden: true })
|
|
737
|
-
.description('Legacy alias of accounts add')
|
|
738
|
-
.option('--json', 'Machine-readable result')
|
|
739
|
-
.action(async (harness, name, o, command) => {
|
|
740
|
-
await runAccountsAction(command, async () => {
|
|
741
|
-
const agent = parseHarness(harness);
|
|
742
|
-
const reason = addRefusal(agent);
|
|
743
|
-
if (reason)
|
|
744
|
-
throw new Error(reason);
|
|
745
|
-
const result = await runAdd(agent, name, {
|
|
746
|
-
meta: readMeta(),
|
|
747
|
-
onProgress: (m) => console.log(chalk.gray(` ${m}`)),
|
|
748
|
-
});
|
|
749
|
-
printAddResult(result, !!(o.json || command.optsWithGlobals().json));
|
|
750
|
-
});
|
|
751
|
-
});
|
|
752
|
-
retireVerb(connectCmd, 'agents accounts add <harness> [name]');
|
|
753
539
|
accounts.command('set-key <name>')
|
|
754
540
|
.description('Rotate an account credential without changing its identity')
|
|
755
541
|
.option('--from-secrets <bundle:key>', 'Import from an existing agents secrets entry')
|
|
@@ -810,123 +596,6 @@ agents accounts migrate --dry-run --device worker-1`,
|
|
|
810
596
|
agents accounts view codex#icloud`,
|
|
811
597
|
notes: 'Native names are unique per harness. A bare name that exists for several harnesses is refused — pick one with <harness>#<name>.',
|
|
812
598
|
});
|
|
813
|
-
retireVerb(accounts.command('name <source> <name>', { hidden: true })
|
|
814
|
-
.description('Name a signed-in native installation without copying its OAuth credentials')
|
|
815
|
-
.action(async (source, name, _o, command) => {
|
|
816
|
-
await runAccountsAction(command, async () => {
|
|
817
|
-
const identity = await nativeIdentityFromSource(source);
|
|
818
|
-
const account = addNativeAccount(name, identity.agent, identity.identityKey, identity.identityLabel, identity.scope);
|
|
819
|
-
console.log(chalk.green(`Named ${source} as ${account.name}.`));
|
|
820
|
-
if (account.scope === 'device')
|
|
821
|
-
console.log(chalk.gray(`${identity.agent} authentication is device-scoped; attach '${account.name}' to '${identity.agent}', not an individual version.`));
|
|
822
|
-
});
|
|
823
|
-
}), 'agents accounts add <harness> <name> (names are chosen at add time)');
|
|
824
|
-
const labelCmd = accounts.command('label <source> [label]', { hidden: true })
|
|
825
|
-
.description('Label a native login by harness or <harness>@<version>; the label binds to the account identity, not the version')
|
|
826
|
-
.option('--account <email-or-id>', 'Native identity to label when the harness has multiple logins')
|
|
827
|
-
.action(async (source, label, o, command) => {
|
|
828
|
-
await runAccountsAction(command, () => runAccountsLabel(source, label, o));
|
|
829
|
-
});
|
|
830
|
-
retireVerb(labelCmd, 'agents accounts add <harness> <name> (rename: agents accounts rename <harness>#<old> <new>)');
|
|
831
|
-
setHelpSections(labelCmd, {
|
|
832
|
-
examples: `agents accounts label codex work
|
|
833
|
-
agents accounts label codex@0.146.0 personal
|
|
834
|
-
agents accounts label codex work --account you@example.com
|
|
835
|
-
agents run codex#work`,
|
|
836
|
-
notes: 'The label binds to the signed-in account identity, not the version — codex#work keeps selecting that account after it moves to a newer install. Labels live on the central account rows in agents.yaml, which `agents repo push/pull` already syncs fleet-wide, keyed by (agent, identityKey). One signed-in login needs no selector; with several, an interactive terminal opens a picker, while scripts pass --account or <harness>@<version>.',
|
|
837
|
-
});
|
|
838
|
-
retireVerb(accounts.command('attach <account> <target>', { hidden: true })
|
|
839
|
-
.description('Attach a named account to a native installation or custom harness')
|
|
840
|
-
.action(async (name, target, _o, command) => {
|
|
841
|
-
await runAccountsAction(command, async () => {
|
|
842
|
-
const meta = readMeta();
|
|
843
|
-
// Validate the target exists before mutating any binding — and resolve it
|
|
844
|
-
// FIRST so the account lookup can be scoped to the harness being attached
|
|
845
|
-
// to. `identityLabel` defaults to the login's email, so a bare identity
|
|
846
|
-
// (`muqsitnawaz@gmail.com`) matches every harness that identity is signed
|
|
847
|
-
// into; un-scoped this resolved whichever row the store ordered first and
|
|
848
|
-
// then rejected it below as "is a <other> login".
|
|
849
|
-
const t = classifyAttachTarget(target);
|
|
850
|
-
const targetAgent = t.kind === 'profile' ? t.profile.host.agent : t.agent;
|
|
851
|
-
const account = findUnifiedAccount(name, meta, undefined, targetAgent);
|
|
852
|
-
if (!account)
|
|
853
|
-
throw new Error(`Unknown account '${name}'.`);
|
|
854
|
-
if (account.kind === 'native')
|
|
855
|
-
assertNativeAccountNameable(account.agent);
|
|
856
|
-
if (account.kind === 'native') {
|
|
857
|
-
// A provider-backed profile injects provider env at spawn, which would run
|
|
858
|
-
// under a different credential than the native identity claims — refuse it.
|
|
859
|
-
if (t.kind === 'profile' && t.profile.provider) {
|
|
860
|
-
throw new Error(`Custom harness '${target}' is provider-backed (${t.profile.provider}); it cannot host the native login account '${account.name}'. Attach a matching provider account instead.`);
|
|
861
|
-
}
|
|
862
|
-
if (targetAgent !== account.agent)
|
|
863
|
-
throw new Error(`Account '${account.name}' is a ${account.agent} login; '${target}' runs ${targetAgent}.`);
|
|
864
|
-
if (account.scope === 'device') {
|
|
865
|
-
if (t.kind !== 'device-agent')
|
|
866
|
-
throw new Error(`${account.agent} authentication is device-scoped. Attach it with 'agents accounts attach ${account.name} ${account.agent}'.`);
|
|
867
|
-
}
|
|
868
|
-
else {
|
|
869
|
-
if (t.kind !== 'installation')
|
|
870
|
-
throw new Error(`${account.agent} authentication is per-version. Attach '${account.name}' to a specific ${account.agent}@<version>.`);
|
|
871
|
-
const versionHome = getVersionHomePath(t.agent, t.version);
|
|
872
|
-
// The literal email keys the account's `auth`-bundle setup-token. It lives
|
|
873
|
-
// in `identityLabel` — `identityKey` is a synthetic composite
|
|
874
|
-
// (`claude:account=<uuid>:org=<uuid>`, agents.ts nativeIdentityKey), never
|
|
875
|
-
// the address, so it must NOT be used to derive the token key.
|
|
876
|
-
const accountEmail = account.identityLabel;
|
|
877
|
-
// Headless-worker bootstrap: a keychain-less Linux worker home never had
|
|
878
|
-
// an interactive login, so its `.claude.json` carries no identity and
|
|
879
|
-
// `nativeIdentityFromSource` would reject the attach — yet the account's
|
|
880
|
-
// non-rotating setup-token is already fleet-synced in the `auth` bundle.
|
|
881
|
-
// Seed the identity (email only, no rotating credential) so the token
|
|
882
|
-
// resolves; `writeClaudeInteractiveOauthToken` then writes `.oauth_token`.
|
|
883
|
-
if (process.platform === 'linux' &&
|
|
884
|
-
account.agent === 'claude' &&
|
|
885
|
-
accountEmail &&
|
|
886
|
-
!readClaudeAccountEmail(versionHome) &&
|
|
887
|
-
resolveClaudeSetupTokenForEmail(accountEmail)) {
|
|
888
|
-
seedClaudeWorkerHomeIdentity(versionHome, accountEmail);
|
|
889
|
-
}
|
|
890
|
-
else {
|
|
891
|
-
const identity = await nativeIdentityFromSource(target);
|
|
892
|
-
if (identity.identityKey !== account.identityKey)
|
|
893
|
-
throw new Error(`'${target}' is signed in to a different identity than account '${account.name}'.`);
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
else {
|
|
898
|
-
// Provider account: it must be able to authenticate the target's harness.
|
|
899
|
-
getAccountProvider(account.provider).envFor(targetAgent, account.auth);
|
|
900
|
-
}
|
|
901
|
-
bindAccount(name, target, targetAgent);
|
|
902
|
-
writeClaudeInteractiveOauthToken(t, targetAgent, account.kind === 'native' && account.agent === 'claude' ? account.identityLabel : undefined);
|
|
903
|
-
console.log(chalk.green(`Attached ${account.name} to ${target}.`));
|
|
904
|
-
});
|
|
905
|
-
}), 'agents run <harness>#<name> (accounts select by name; installation bindings are legacy)');
|
|
906
|
-
retireVerb(accounts.command('detach <account> <target>', { hidden: true })
|
|
907
|
-
.description('Remove one account attachment')
|
|
908
|
-
.action(async (name, target, _o, command) => {
|
|
909
|
-
await runAccountsAction(command, () => {
|
|
910
|
-
// Classify the target first so the unbind is scoped to the right harness
|
|
911
|
-
// for a colliding identity selector (same as the attach path).
|
|
912
|
-
let targetAgent;
|
|
913
|
-
try {
|
|
914
|
-
const t = classifyAttachTarget(target);
|
|
915
|
-
targetAgent = t.kind === 'profile' ? t.profile.host.agent : t.agent;
|
|
916
|
-
}
|
|
917
|
-
catch { /* an unresolvable target still unbinds by whatever row matches */ }
|
|
918
|
-
unbindAccount(name, target, targetAgent);
|
|
919
|
-
// With the binding gone, no setup-token resolves for this version home, so this
|
|
920
|
-
// clears any .oauth_token the attach left behind (else interactive runs would keep
|
|
921
|
-
// authenticating as the just-detached account).
|
|
922
|
-
try {
|
|
923
|
-
const t = classifyAttachTarget(target);
|
|
924
|
-
writeClaudeInteractiveOauthToken(t, t.kind === 'profile' ? t.profile.host.agent : t.agent);
|
|
925
|
-
}
|
|
926
|
-
catch { /* an unresolvable target has no version home to clean */ }
|
|
927
|
-
console.log(chalk.green(`Detached ${name} from ${target}.`));
|
|
928
|
-
});
|
|
929
|
-
}), 'agents run <harness>#<name> (accounts select by name; installation bindings are legacy)');
|
|
930
599
|
const renameCmd = accounts.command('rename <old> <new>')
|
|
931
600
|
.description('Rename an account without changing its stable id. Target may be <harness>#<name> when the name exists for several harnesses')
|
|
932
601
|
.action(async (oldName, newName, _o, command) => {
|
|
@@ -946,21 +615,6 @@ agents accounts rename codex#icloud cloud`,
|
|
|
946
615
|
examples: `agents accounts remove claude#icloud`,
|
|
947
616
|
notes: 'Native names are unique per harness. A bare name that exists for several harnesses is refused — pick one with <harness>#<name>.',
|
|
948
617
|
});
|
|
949
|
-
retireVerb(accounts.command('set-default <agent> <name>', { hidden: true })
|
|
950
|
-
.description('Use this account for a harness when --account is omitted')
|
|
951
|
-
.action(async (agentRaw, name, _o, command) => {
|
|
952
|
-
await runAccountsAction(command, () => {
|
|
953
|
-
const { agent, account } = setDefaultAccount(agentRaw, name);
|
|
954
|
-
console.log(chalk.green(`${agent} now uses account '${account.name}' unless --account overrides it.`));
|
|
955
|
-
});
|
|
956
|
-
}), 'agents accounts default <harness> <name>');
|
|
957
|
-
const switchCmd = accounts.command('switch <harness> [account]', { hidden: true })
|
|
958
|
-
.description('Pick the default account for a harness')
|
|
959
|
-
.option('--json', 'Machine-readable account list or the resulting default')
|
|
960
|
-
.action(async (harness, account, o, command) => {
|
|
961
|
-
await runAccountsAction(command, () => runAccountsSwitch(harness, account, { json: !!(o.json || command.optsWithGlobals().json) }));
|
|
962
|
-
});
|
|
963
|
-
retireVerb(switchCmd, 'agents accounts default <harness> [name]');
|
|
964
618
|
accounts.command('clear-default <agent>')
|
|
965
619
|
.description('Return a harness to native login or balanced account selection')
|
|
966
620
|
.action(async (agentRaw, _o, command) => {
|
|
@@ -1048,13 +702,6 @@ agents accounts rename codex#icloud cloud`,
|
|
|
1048
702
|
console.log(chalk.green(`${account.name} synced to ${device} (${result.keyCount} keys, ${remoteBackend} backend, policy never).`));
|
|
1049
703
|
});
|
|
1050
704
|
});
|
|
1051
|
-
setHelpSections(switchCmd, {
|
|
1052
|
-
examples: `agents accounts switch claude
|
|
1053
|
-
agents accounts switch claude work
|
|
1054
|
-
agents accounts switch claude --json
|
|
1055
|
-
agents run claude`,
|
|
1056
|
-
notes: 'Switch writes the existing per-harness default (same binding as set-default). Rotation already honors it. Pass an account name to skip the picker. Native name/attach is only for harnesses agents-cli can isolate (claude, codex, grok); provider add is unrestricted.',
|
|
1057
|
-
});
|
|
1058
705
|
setHelpSections(accounts, {
|
|
1059
706
|
examples: `agents accounts add claude work
|
|
1060
707
|
agents accounts add codex personal --api-key sk-…
|
|
@@ -1070,6 +717,6 @@ agents run claude#work
|
|
|
1070
717
|
agents accounts logout claude
|
|
1071
718
|
agents accounts migrate --dry-run
|
|
1072
719
|
agents accounts migrate --apply`,
|
|
1073
|
-
notes: 'An account is a credential SLOT, not an installation: `accounts add <harness> [name]` runs the native login in a fresh HOME-shaped slot of the one managed install, registers the fleet-wide row, and mints the durable worker credential (claude: setup-token; codex/grok/cursor/opencode: --api-key or a prompt) in one step. Headed devices only — workers are provisioned automatically. `accounts login <harness>#<name>` re-auths into the same slot and re-mints; `accounts default <harness> [name]` is the one default write path. Native account records contain metadata only; harness-owned OAuth credentials are never copied. Provider accounts are explicit portable bundles with policy never. Harness-native OAuth sign-out is `agents accounts logout <harness>` (API-key accounts use `accounts remove`). Leftover per-account installations fold with `agents accounts migrate --dry-run|--apply`. Synced vault unlock is `agents secrets vault unlock`.
|
|
720
|
+
notes: 'An account is a credential SLOT, not an installation: `accounts add <harness> [name]` runs the native login in a fresh HOME-shaped slot of the one managed install, registers the fleet-wide row, and mints the durable worker credential (claude: setup-token; codex/grok/cursor/opencode: --api-key or a prompt) in one step. Headed devices only — workers are provisioned automatically. `accounts login <harness>#<name>` re-auths into the same slot and re-mints; `accounts default <harness> [name]` is the one default write path. Native account records contain metadata only; harness-owned OAuth credentials are never copied. Provider accounts are explicit portable bundles with policy never. Harness-native OAuth sign-out is `agents accounts logout <harness>` (API-key accounts use `accounts remove`). Leftover per-account installations fold with `agents accounts migrate --dry-run|--apply`. Synced vault unlock is `agents secrets vault unlock`.',
|
|
1074
721
|
});
|
|
1075
722
|
}
|
package/dist/commands/audit.js
CHANGED
|
@@ -16,7 +16,7 @@ import { addEventsReadOptions, runEventsCommand } from './events.js';
|
|
|
16
16
|
export function registerAuditCommands(events) {
|
|
17
17
|
const audit = addEventsReadOptions(events
|
|
18
18
|
.command('audit')
|
|
19
|
-
.description('Alias of `agents events --include runs` — dispatched-run outcomes')
|
|
19
|
+
.description('Alias of `agents events --include runs` — dispatched-run outcomes'))
|
|
20
20
|
.addHelpText('after', `
|
|
21
21
|
Examples:
|
|
22
22
|
agents events audit Same as: agents events --include runs
|
package/dist/commands/auth.js
CHANGED
|
@@ -2,7 +2,6 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { PhoenixApiError, clearSession, createSpace, createSpaceInvite, fetchWhoAmI, listSpaceMembers, listSpaces, pollDeviceToken, readSession, refreshSessionAvatar, removeSpaceMember, resolveMemberFromList, resolveSpaceFromList, slugify, startDeviceAuthorization, updateSpaceMemberRole, writeSession, } from '../lib/identity/index.js';
|
|
3
3
|
import { setHelpSections } from '../lib/help.js';
|
|
4
4
|
import { runOrDie } from '../lib/format.js';
|
|
5
|
-
import { registerMintCommand } from './auth-mint.js';
|
|
6
5
|
/**
|
|
7
6
|
* `agents auth` — sign in to Phoenix ID, the account layer behind team spaces.
|
|
8
7
|
* Signing in is optional: every local feature works with no account. Everything
|
|
@@ -126,7 +125,6 @@ Harness worker credentials are minted by \`agents accounts add <harness> [name]\
|
|
|
126
125
|
const json = !!o.json || !!command.optsWithGlobals().json;
|
|
127
126
|
return runOrDie(() => whoami(json), { json });
|
|
128
127
|
});
|
|
129
|
-
registerMintCommand(auth, undefined, { hidden: true });
|
|
130
128
|
auth
|
|
131
129
|
.command('logout')
|
|
132
130
|
.description("Clear this machine's session (no other device is affected)")
|
|
@@ -1,7 +1,60 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { type BrowserProfile } from '../lib/browser/profiles.js';
|
|
3
3
|
import { listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
4
|
+
import type { DeviceProfile } from '../lib/devices/registry.js';
|
|
4
5
|
export declare function remoteStartTaskName(stdout: string, explicit?: string): string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Fetch an absolute remote file into a private local path, bounded.
|
|
8
|
+
*
|
|
9
|
+
* Bounded for real, not by a pre-flight `stat`: the byte budget is enforced on the
|
|
10
|
+
* stream as it arrives (`sshExecRawStream` + `cat`), so a file that grows between
|
|
11
|
+
* a size check and the copy — or a peer that lies about the size — still cannot
|
|
12
|
+
* spend more than the budget. `scp` could not do this; it would transfer
|
|
13
|
+
* everything before this process got a chance to refuse.
|
|
14
|
+
*
|
|
15
|
+
* The local file is written 0600 inside a 0700 directory, because a capture can
|
|
16
|
+
* show anything that was on the peer's screen and a world-readable temp path
|
|
17
|
+
* would publish it to every local user.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The command that streams one file's RAW BYTES to stdout on the device's shell.
|
|
21
|
+
*
|
|
22
|
+
* Platform-correct, not one command with a portable-looking name. On Windows
|
|
23
|
+
* `cat` is an alias for `Get-Content`, which is a TEXT reader: it decodes the
|
|
24
|
+
* file, splits it into lines, and re-encodes on output — so a screenshot arrives
|
|
25
|
+
* corrupted rather than merely reordered. The .NET path opens a `FileStream` and
|
|
26
|
+
* copies it into the raw stdout handle, bypassing PowerShell's object/text
|
|
27
|
+
* pipeline entirely, which is the only way binary survives.
|
|
28
|
+
*/
|
|
29
|
+
export declare function remoteReadFileArgv(remotePath: string, shell: DeviceProfile['shell']): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Largest capture `browser show --device` will pull. A capture is a screenshot,
|
|
32
|
+
* a PDF or a short recording; anything past this is not something a viewer should
|
|
33
|
+
* stream over ssh, and the cap is what makes the transfer BOUNDED rather than
|
|
34
|
+
* "however big the peer's file happens to be".
|
|
35
|
+
*/
|
|
36
|
+
export declare const REMOTE_VIEW_MAX_BYTES: number;
|
|
37
|
+
/**
|
|
38
|
+
* Fetch an absolute remote file into a private local path, bounded.
|
|
39
|
+
*
|
|
40
|
+
* Auth is the CANONICAL device path, not a hand-rolled ssh: `buildSshInvocation`
|
|
41
|
+
* supplies the askpass shim for a password-auth box, `-i`/`IdentitiesOnly` for an
|
|
42
|
+
* explicit identity file, and the managed known-hosts pinning. Building the ssh
|
|
43
|
+
* args here instead meant a password-auth device could not be reached at all and
|
|
44
|
+
* an explicit `identityFile` was silently ignored.
|
|
45
|
+
*
|
|
46
|
+
* Bounded for real, not by a pre-flight `stat`: the byte budget is enforced on the
|
|
47
|
+
* stream as it arrives, so a file that grows between a size check and the copy — or
|
|
48
|
+
* a peer that misreports its size — still cannot spend more than the budget.
|
|
49
|
+
*
|
|
50
|
+
* The local file is written 0600 inside a 0700 directory, because a capture can
|
|
51
|
+
* show anything that was on the peer's screen and a world-readable temp path
|
|
52
|
+
* would publish it to every local user.
|
|
53
|
+
*/
|
|
54
|
+
export declare function fetchRemoteFileForViewing(device: string, remotePath: string, localPath: string, opts?: {
|
|
55
|
+
maxBytes?: number;
|
|
56
|
+
timeoutMs?: number;
|
|
57
|
+
}): Promise<void>;
|
|
5
58
|
/**
|
|
6
59
|
* The fleet browser hub this box drives by default (`browser.device`), or
|
|
7
60
|
* undefined to drive locally. It is one fleet-synced value in the central
|