@phnx-labs/agents-cli 1.22.45 → 1.22.47
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 +303 -0
- package/LICENSE +102 -182
- package/README.md +54 -17
- package/dist/cli/command-registry.d.ts +2 -1
- package/dist/cli/command-registry.js +6 -2
- package/dist/commands/accounts.js +56 -12
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +229 -0
- package/dist/commands/browser-sessions-picker.js +9 -7
- package/dist/commands/browser.js +266 -15
- package/dist/commands/config.js +26 -4
- package/dist/commands/cost.d.ts +1 -1
- package/dist/commands/detach.d.ts +13 -0
- package/dist/commands/detach.js +11 -8
- package/dist/commands/exec.js +55 -38
- package/dist/commands/feed-watch.d.ts +2 -0
- package/dist/commands/feed-watch.js +28 -0
- package/dist/commands/feed.js +27 -0
- package/dist/commands/feedback.js +3 -19
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +2 -2
- package/dist/commands/lease.js +3 -3
- package/dist/commands/routines.js +1 -1
- package/dist/commands/sessions-picker-factory.d.ts +2 -1
- package/dist/commands/sessions-picker-factory.js +1 -1
- package/dist/commands/sessions-stop.d.ts +24 -0
- package/dist/commands/sessions-stop.js +66 -0
- package/dist/commands/sessions-trace.d.ts +82 -0
- package/dist/commands/sessions-trace.js +387 -0
- package/dist/commands/sessions.js +4 -0
- package/dist/commands/ssh.d.ts +15 -0
- package/dist/commands/ssh.js +194 -16
- package/dist/commands/teams.js +34 -2
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +19 -32
- package/dist/commands/watchdog.js +9 -14
- package/dist/lib/account-capabilities.d.ts +4 -0
- package/dist/lib/account-capabilities.js +13 -9
- package/dist/lib/account-registry.d.ts +2 -0
- package/dist/lib/account-registry.js +30 -2
- package/dist/lib/accounting/rotate.d.ts +9 -0
- package/dist/lib/accounting/rotate.js +12 -2
- package/dist/lib/accounting/usage.d.ts +92 -6
- package/dist/lib/accounting/usage.js +329 -75
- package/dist/lib/agent-modes.js +29 -1
- package/dist/lib/agent-spec/agents.d.ts +4 -3
- package/dist/lib/agent-spec/agents.js +20 -7
- package/dist/lib/analytics/mix-commands.d.ts +1 -1
- package/dist/lib/analytics/mix-commands.js +2 -2
- package/dist/lib/auth-health.d.ts +13 -3
- package/dist/lib/auth-health.js +50 -8
- package/dist/lib/browser/devices.d.ts +19 -0
- package/dist/lib/browser/devices.js +23 -0
- package/dist/lib/browser/ipc.d.ts +1 -1
- package/dist/lib/browser/ipc.js +33 -7
- package/dist/lib/browser/profiles.d.ts +106 -1
- package/dist/lib/browser/profiles.js +300 -17
- package/dist/lib/browser/remote-control.d.ts +26 -2
- package/dist/lib/browser/remote-control.js +33 -3
- package/dist/lib/browser/runtime-state.d.ts +9 -0
- package/dist/lib/browser/runtime-state.js +24 -8
- package/dist/lib/browser/service.d.ts +37 -0
- package/dist/lib/browser/service.js +78 -20
- package/dist/lib/browser/sessions-list.d.ts +1 -3
- package/dist/lib/browser/sessions-list.js +3 -16
- package/dist/lib/browser/types.d.ts +12 -1
- package/dist/lib/cli-resources.js +17 -15
- package/dist/lib/codex-policy.d.ts +11 -1
- package/dist/lib/codex-policy.js +20 -5
- package/dist/lib/config-keys.d.ts +4 -3
- package/dist/lib/config-keys.js +17 -7
- package/dist/lib/daemon/runner.js +15 -2
- package/dist/lib/daemon-ticks.js +2 -2
- package/dist/lib/device-config.d.ts +25 -8
- package/dist/lib/device-config.js +90 -17
- package/dist/lib/devices/config-migration.d.ts +7 -0
- package/dist/lib/devices/config-migration.js +67 -3
- package/dist/lib/devices/harness-inventory.js +20 -3
- package/dist/lib/devices/health.d.ts +27 -2
- package/dist/lib/devices/health.js +39 -9
- package/dist/lib/devices/interactive-host.d.ts +15 -0
- package/dist/lib/devices/interactive-host.js +53 -0
- package/dist/lib/devices/registry.d.ts +61 -4
- package/dist/lib/devices/registry.js +94 -38
- package/dist/lib/devices/stats-cache.d.ts +6 -0
- package/dist/lib/devices/stats-cache.js +10 -2
- package/dist/lib/exec.d.ts +25 -3
- package/dist/lib/exec.js +75 -22
- package/dist/lib/feed/answer.d.ts +29 -0
- package/dist/lib/feed/answer.js +145 -0
- package/dist/lib/feed/attention.d.ts +83 -0
- package/dist/lib/feed/attention.js +0 -0
- package/dist/lib/feed/feed.d.ts +109 -0
- package/dist/lib/feed/feed.js +154 -3
- package/dist/lib/feed/pr-status.d.ts +14 -0
- package/dist/lib/feed/pr-status.js +47 -0
- package/dist/lib/feed/watch.d.ts +59 -0
- package/dist/lib/feed/watch.js +180 -0
- package/dist/lib/fleet/capture.js +6 -0
- package/dist/lib/fleet/remote-login.js +8 -3
- package/dist/lib/fleet/types.d.ts +17 -0
- package/dist/lib/harness/adapter.d.ts +11 -0
- package/dist/lib/harness/adapters/claude.js +38 -24
- package/dist/lib/harness/adapters/codex.js +2 -4
- package/dist/lib/hooks/cache.js +8 -0
- package/dist/lib/hooks/match.d.ts +3 -0
- package/dist/lib/hooks/match.js +25 -0
- package/dist/lib/hosts/passthrough.js +16 -0
- package/dist/lib/hosts/registry.js +7 -0
- package/dist/lib/identity/client.d.ts +53 -0
- package/dist/lib/identity/client.js +106 -0
- package/dist/lib/identity/index.d.ts +115 -0
- package/dist/lib/identity/index.js +82 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +13 -0
- package/dist/lib/menubar/install-menubar.js +26 -2
- package/dist/lib/open-url.d.ts +60 -2
- package/dist/lib/open-url.js +167 -12
- package/dist/lib/probe.d.ts +8 -0
- package/dist/lib/probe.js +105 -0
- package/dist/lib/refresh-coordinator.d.ts +1 -1
- package/dist/lib/refresh-coordinator.js +1 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +2 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +13 -1
- package/dist/lib/session/active.d.ts +67 -0
- package/dist/lib/session/active.js +69 -1
- package/dist/lib/session/db.js +4 -2
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +12 -6
- package/dist/lib/session/prompt.d.ts +35 -0
- package/dist/lib/session/prompt.js +56 -0
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +6 -1
- package/dist/lib/session/render.js +7 -3
- package/dist/lib/session/session-cache.js +5 -1
- package/dist/lib/session/trajectory-compare.d.ts +47 -0
- package/dist/lib/session/trajectory-compare.js +116 -0
- package/dist/lib/session/trajectory-html.d.ts +15 -0
- package/dist/lib/session/trajectory-html.js +750 -0
- package/dist/lib/session/trajectory-lineage.d.ts +92 -0
- package/dist/lib/session/trajectory-lineage.js +195 -0
- package/dist/lib/session/trajectory-text.d.ts +37 -0
- package/dist/lib/session/trajectory-text.js +277 -0
- package/dist/lib/session/trajectory.d.ts +102 -0
- package/dist/lib/session/trajectory.js +335 -0
- package/dist/lib/startup/command-registry.d.ts +5 -2
- package/dist/lib/startup/command-registry.js +8 -5
- package/dist/lib/state.d.ts +1 -1
- package/dist/lib/state.js +4 -2
- package/dist/lib/teams/agents.js +9 -0
- package/dist/lib/terminal/inject.d.ts +19 -0
- package/dist/lib/terminal/inject.js +19 -9
- package/dist/lib/tmux/session.d.ts +46 -13
- package/dist/lib/tmux/session.js +173 -27
- package/dist/lib/types.d.ts +16 -0
- package/dist/lib/usage-backoff.d.ts +2 -1
- package/dist/lib/usage-backoff.js +60 -19
- package/dist/lib/usage-refresh.d.ts +17 -3
- package/dist/lib/usage-refresh.js +57 -11
- package/dist/lib/view-types.d.ts +2 -2
- package/dist/lib/watchdog/log.d.ts +1 -1
- package/dist/lib/watchdog/log.js +1 -1
- package/dist/lib/watchdog/runner.d.ts +36 -45
- package/dist/lib/watchdog/runner.js +150 -119
- package/dist/lib/watchdog/service.d.ts +0 -1
- package/dist/lib/watchdog/service.js +0 -1
- package/dist/lib/watchdog/watchdog-agent.d.ts +38 -0
- package/dist/lib/watchdog/watchdog-agent.js +59 -0
- package/dist/lib/watchdog/watchdog.d.ts +11 -2
- package/dist/lib/watchdog/watchdog.js +37 -104
- package/package.json +2 -2
- package/dist/commands/usage.d.ts +0 -27
- package/dist/commands/usage.js +0 -96
package/dist/lib/agent-modes.js
CHANGED
|
@@ -14,9 +14,28 @@ import { resolveRunDefaults } from './run-defaults.js';
|
|
|
14
14
|
export const MODE_DESCRIPTIONS = {
|
|
15
15
|
plan: 'read-only investigation; no writes, no shell side-effects',
|
|
16
16
|
edit: 'may edit files; prompts for shell / risky operations',
|
|
17
|
-
auto: '
|
|
17
|
+
auto: 'more autonomy than edit; the exact policy is per-harness (see notes)',
|
|
18
18
|
skip: 'bypass every permission prompt (dangerously-skip-permissions)',
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* What `auto` actually does, per harness. Two genuinely different mechanisms
|
|
22
|
+
* wear the same mode name, and conflating them is a safety error in both
|
|
23
|
+
* directions: telling a claude operator that auto never prompts invites an
|
|
24
|
+
* unattended run that stalls on a risky-operation gate, and telling a codex
|
|
25
|
+
* operator that auto prompts for risky work asserts a gate codex does not have.
|
|
26
|
+
* Absent entry = no extra note; the row's own text suffices.
|
|
27
|
+
*/
|
|
28
|
+
const AUTO_SEMANTICS = {
|
|
29
|
+
// Classifier-style: still escalates. --permission-mode auto / --autopilot.
|
|
30
|
+
claude: 'a smart classifier auto-approves safe operations and still prompts for risky ones.',
|
|
31
|
+
copilot: 'a smart classifier auto-approves safe operations and still prompts for risky ones.',
|
|
32
|
+
// Approvals off, sandbox kept: never escalates. A denied command just fails.
|
|
33
|
+
codex: 'approval_policy=never over the same sandbox as edit — it never prompts, and a sandbox-denied command fails instead of raising an approval request.',
|
|
34
|
+
muse: 'runs --disable-approval, which turns approvals off while keeping the sandbox — like codex, it never prompts and a denied command fails.',
|
|
35
|
+
// Native autonomy dial at its top setting.
|
|
36
|
+
droid: 'runs --auto high, droid\'s full-autonomy setting.',
|
|
37
|
+
kimi: "runs kimi's native --auto.",
|
|
38
|
+
};
|
|
20
39
|
/**
|
|
21
40
|
* Build the permission-mode catalog for one harness.
|
|
22
41
|
* `version` only affects the configured run.defaults lookup (modes themselves
|
|
@@ -42,6 +61,15 @@ export function getAgentModesCatalog(agent, version, cwd = process.cwd()) {
|
|
|
42
61
|
if (unsupported.includes('auto')) {
|
|
43
62
|
notes.push(`--mode auto degrades to edit on ${agent} (no native auto classifier).`);
|
|
44
63
|
}
|
|
64
|
+
// MODE_DESCRIPTIONS is one flat Record<Mode, string> rendered for every agent,
|
|
65
|
+
// so it cannot name any single harness's mechanism without lying about the
|
|
66
|
+
// others: claude/copilot auto STILL PROMPTS for risky operations, while codex
|
|
67
|
+
// auto never prompts at all. The row states only what is true everywhere; the
|
|
68
|
+
// mechanism rides here, per harness.
|
|
69
|
+
const autoSemantics = AUTO_SEMANTICS[agent];
|
|
70
|
+
if (autoSemantics && supported.includes('auto')) {
|
|
71
|
+
notes.push(`${agent} --mode auto: ${autoSemantics}`);
|
|
72
|
+
}
|
|
45
73
|
if (unsupported.includes('plan')) {
|
|
46
74
|
notes.push(`--mode plan degrades to ${defaultMode} on ${agent} (no native read-only mode).`);
|
|
47
75
|
}
|
|
@@ -552,9 +552,9 @@ export declare function resolveAgentName(input: string): AgentId | null;
|
|
|
552
552
|
/** Check whether the input string matches any known agent name or alias. */
|
|
553
553
|
export declare function isAgentName(input: string): boolean;
|
|
554
554
|
/**
|
|
555
|
-
* Split a CLI-provided `<agent>[@<version>]` spec into its bare agent
|
|
556
|
-
* optional exact version token, the same way
|
|
557
|
-
*
|
|
555
|
+
* Split a CLI-provided `<agent>[@<version>][#<label>]` spec into its bare agent
|
|
556
|
+
* id, optional exact version token, and optional account label, the same way
|
|
557
|
+
* `agents run` does — NOT the
|
|
558
558
|
* agent-spec qualifier engine (`@latest`/`@all`), which enumerates installed
|
|
559
559
|
* versions for read/diagnostic commands, not a single launch target. Returns an
|
|
560
560
|
* error message rather than throwing so callers decide exit-vs-continue.
|
|
@@ -562,6 +562,7 @@ export declare function isAgentName(input: string): boolean;
|
|
|
562
562
|
export declare function parseAgentVersionSpec(raw: string): {
|
|
563
563
|
agent: AgentId;
|
|
564
564
|
version?: string;
|
|
565
|
+
label?: string;
|
|
565
566
|
} | {
|
|
566
567
|
error: string;
|
|
567
568
|
};
|
|
@@ -21,6 +21,7 @@ import chalk from 'chalk';
|
|
|
21
21
|
import { execFileShellSpec } from '../platform/index.js';
|
|
22
22
|
import { latestFileMtimeMs } from '../fs-walk.js';
|
|
23
23
|
import { damerauLevenshtein } from '../fuzzy.js';
|
|
24
|
+
import { probeCapture } from '../probe.js';
|
|
24
25
|
import { getCacheDir, getVersionsDir, getShimsDir, getHistoryDir, getCliVersionCachePath } from '../state.js';
|
|
25
26
|
import { resolveVersion, getVersionHomePath, getBinaryPath } from '../installations/versions.js';
|
|
26
27
|
import { supports } from '../capabilities.js';
|
|
@@ -297,7 +298,7 @@ export const AGENTS = {
|
|
|
297
298
|
cloudProvider: 'codex',
|
|
298
299
|
// Subagents: multi-agent plumbing since 0.117.0; custom agents as
|
|
299
300
|
// ~/.codex/agents/*.toml (name, description, developer_instructions).
|
|
300
|
-
capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: { since: '0.138.0' }, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: { since: '0.117.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: true, modes: ['plan', 'edit', 'skip'], interactiveRepl: true },
|
|
301
|
+
capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: { since: '0.138.0' }, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: { since: '0.117.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: true, modes: ['plan', 'edit', 'auto', 'skip'], interactiveRepl: true },
|
|
301
302
|
},
|
|
302
303
|
gemini: {
|
|
303
304
|
id: 'gemini',
|
|
@@ -1194,7 +1195,11 @@ async function getCachedVersionForBinary(agentId, binaryPath) {
|
|
|
1194
1195
|
const agent = AGENTS[agentId];
|
|
1195
1196
|
let version = null;
|
|
1196
1197
|
try {
|
|
1197
|
-
|
|
1198
|
+
// probeCapture, not bare execFileAsync: a probed harness can fork its own
|
|
1199
|
+
// children (copilot's platform-binary downloader), and a timeout kill of
|
|
1200
|
+
// the direct child would orphan them mid-write (RUSH-3028). The probe runs
|
|
1201
|
+
// in its own process group and the whole group is reaped on settle.
|
|
1202
|
+
const { stdout } = await probeCapture(agent.cliCommand, ['--version'], 3000);
|
|
1198
1203
|
const versionRe = agent.versionStdoutMatch === 'openclaw'
|
|
1199
1204
|
? /openclaw\/(\d+\.\d+\.\d+)/
|
|
1200
1205
|
: /(\d+\.\d+\.\d+)/;
|
|
@@ -3045,15 +3050,23 @@ export function isAgentName(input) {
|
|
|
3045
3050
|
return resolveAgentName(input) !== null;
|
|
3046
3051
|
}
|
|
3047
3052
|
/**
|
|
3048
|
-
* Split a CLI-provided `<agent>[@<version>]` spec into its bare agent
|
|
3049
|
-
* optional exact version token, the same way
|
|
3050
|
-
*
|
|
3053
|
+
* Split a CLI-provided `<agent>[@<version>][#<label>]` spec into its bare agent
|
|
3054
|
+
* id, optional exact version token, and optional account label, the same way
|
|
3055
|
+
* `agents run` does — NOT the
|
|
3051
3056
|
* agent-spec qualifier engine (`@latest`/`@all`), which enumerates installed
|
|
3052
3057
|
* versions for read/diagnostic commands, not a single launch target. Returns an
|
|
3053
3058
|
* error message rather than throwing so callers decide exit-vs-continue.
|
|
3054
3059
|
*/
|
|
3055
3060
|
export function parseAgentVersionSpec(raw) {
|
|
3056
|
-
const
|
|
3061
|
+
const labelParts = raw.split('#');
|
|
3062
|
+
if (labelParts.length > 2) {
|
|
3063
|
+
return { error: `Invalid agent spec '${raw}': at most one '#label' is allowed` };
|
|
3064
|
+
}
|
|
3065
|
+
const [versionSpec, rawLabel] = labelParts;
|
|
3066
|
+
if (rawLabel !== undefined && (rawLabel === '' || !/^[a-zA-Z0-9][a-zA-Z0-9@._+-]*$/.test(rawLabel))) {
|
|
3067
|
+
return { error: `Invalid account label '${rawLabel}' in '${raw}'` };
|
|
3068
|
+
}
|
|
3069
|
+
const parts = versionSpec.split('@');
|
|
3057
3070
|
if (parts.length > 2) {
|
|
3058
3071
|
return { error: `Invalid agent spec '${raw}': at most one '@version' is allowed` };
|
|
3059
3072
|
}
|
|
@@ -3065,7 +3078,7 @@ export function parseAgentVersionSpec(raw) {
|
|
|
3065
3078
|
if (rawVersion !== undefined && (rawVersion === '' || !VERSION_RE.test(rawVersion))) {
|
|
3066
3079
|
return { error: `Invalid version '${rawVersion}' in '${raw}'` };
|
|
3067
3080
|
}
|
|
3068
|
-
return { agent, ...(rawVersion ? { version: rawVersion } : {}) };
|
|
3081
|
+
return { agent, ...(rawVersion ? { version: rawVersion } : {}), ...(rawLabel ? { label: rawLabel } : {}) };
|
|
3069
3082
|
}
|
|
3070
3083
|
/**
|
|
3071
3084
|
* Build the deprecation notice lines for an agent, or null if it isn't
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* peer of `agents insights` with overlapping "analytics" meaning, so agents and
|
|
6
6
|
* humans kept picking the wrong verb. The cheap counter path (sessions index +
|
|
7
7
|
* usage.db) still exists — it is now `agents insights mix` and the recipe
|
|
8
|
-
* subcommands below. Latency stays on `agents perf`; quota on `agents
|
|
8
|
+
* subcommands below. Latency stays on `agents perf`; quota on `agents view`.
|
|
9
9
|
*
|
|
10
10
|
* Former top-level `agents trends` is gone. The nested spelling
|
|
11
11
|
* `agents insights trends` is an alias of `agents insights mix`.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* peer of `agents insights` with overlapping "analytics" meaning, so agents and
|
|
6
6
|
* humans kept picking the wrong verb. The cheap counter path (sessions index +
|
|
7
7
|
* usage.db) still exists — it is now `agents insights mix` and the recipe
|
|
8
|
-
* subcommands below. Latency stays on `agents perf`; quota on `agents
|
|
8
|
+
* subcommands below. Latency stays on `agents perf`; quota on `agents view`.
|
|
9
9
|
*
|
|
10
10
|
* Former top-level `agents trends` is gone. The nested spelling
|
|
11
11
|
* `agents insights trends` is an alias of `agents insights mix`.
|
|
@@ -97,7 +97,7 @@ export function registerMixCommands(parent) {
|
|
|
97
97
|
Session recipes read sessions.db; resource recipes read ~/.agents/.history/analytics/usage.db.
|
|
98
98
|
Empty recipes are skipped on the default mix board.
|
|
99
99
|
This is the cheap counter path. Behavioural report (transcript content, account split)
|
|
100
|
-
is bare \`agents insights\`. Latency is \`agents perf\`; quota is \`agents
|
|
100
|
+
is bare \`agents insights\`. Latency is \`agents perf\`; quota is \`agents view\`.
|
|
101
101
|
Skill/slash-command popularity is \`agents sessions stats\`.
|
|
102
102
|
`,
|
|
103
103
|
});
|
|
@@ -148,13 +148,21 @@ export declare function mergeAuthHealthEntries(current: Record<string, AuthHealt
|
|
|
148
148
|
export declare function writeAuthHealthEntries(entries: Record<string, AuthHealth>): void;
|
|
149
149
|
/**
|
|
150
150
|
* Complete a live auth probe for one (agent, home). For claude/kimi/droid this
|
|
151
|
-
* hits the provider
|
|
152
|
-
*
|
|
153
|
-
*
|
|
151
|
+
* hits the provider — unless the account's usage cache already holds a fresh
|
|
152
|
+
* successful fetch, which is the same authenticated request and proves the
|
|
153
|
+
* token live without spending a second one (RUSH-3036). For everyone else it
|
|
154
|
+
* reports a best-effort local verdict (`unverified` when a credential is
|
|
155
|
+
* present, `unconfigured` otherwise) — never masquerading as `live`.
|
|
154
156
|
*/
|
|
155
157
|
export declare function probeAuthHealth(agent: AgentId, home: string | undefined, opts?: {
|
|
156
158
|
cliVersion?: string | null;
|
|
157
159
|
info?: AccountInfo | null;
|
|
160
|
+
/**
|
|
161
|
+
* Skip the derived-from-usage shortcut and fire a real network probe
|
|
162
|
+
* (RUSH-3036). Set by `agents devices ping [--strict]`, whose contract is
|
|
163
|
+
* a genuinely live request that surfaces `revoked` immediately.
|
|
164
|
+
*/
|
|
165
|
+
forceLive?: boolean;
|
|
158
166
|
}): Promise<AuthHealth>;
|
|
159
167
|
/** One probed install on a host. */
|
|
160
168
|
export interface AuthProbeRow {
|
|
@@ -215,6 +223,8 @@ export declare function groupFleetAuthInstalls<T extends FleetAuthInstall>(insta
|
|
|
215
223
|
export declare function probeLocalFleetAuth(opts?: {
|
|
216
224
|
cliVersion?: string | null;
|
|
217
225
|
agents?: readonly AgentId[];
|
|
226
|
+
/** Fire real network probes even when fresh usage evidence exists (RUSH-3036) — the `devices ping [--strict]` contract. */
|
|
227
|
+
forceLive?: boolean;
|
|
218
228
|
}): Promise<AuthProbeRow[]>;
|
|
219
229
|
/** Persist a host's probed rows into the cache (keyed by host+agent+version). */
|
|
220
230
|
export declare function writeFleetAuthRows(host: string, rows: AuthProbeRow[]): void;
|
package/dist/lib/auth-health.js
CHANGED
|
@@ -20,7 +20,7 @@ import * as fs from 'fs';
|
|
|
20
20
|
import * as path from 'path';
|
|
21
21
|
import { ALL_AGENT_IDS, getAccountInfo } from './agents.js';
|
|
22
22
|
import { getCacheDir } from './state.js';
|
|
23
|
-
import { probeClaudeStatus, probeDroidStatus, probeKimiStatus, USAGE_HEADLESS_SCOPE_MARKER, } from './accounting/usage.js';
|
|
23
|
+
import { probeClaudeStatus, probeDroidStatus, probeKimiStatus, USAGE_HEADLESS_SCOPE_MARKER, readClaudeUsageCache, } from './accounting/usage.js';
|
|
24
24
|
import { getVersionHomePath, listInstalledVersions } from './installations/versions.js';
|
|
25
25
|
import { atomicWriteFileSync, ensureLockTarget, withFileLock } from './fs-atomic.js';
|
|
26
26
|
/** Agents with a live network probe wired up today. The rest are best-effort. */
|
|
@@ -294,22 +294,64 @@ export function writeAuthHealthEntries(entries) {
|
|
|
294
294
|
// ---------------------------------------------------------------------------
|
|
295
295
|
// The probe (writer side)
|
|
296
296
|
// ---------------------------------------------------------------------------
|
|
297
|
+
/**
|
|
298
|
+
* A usage snapshot this recent is live proof the account's shared setup-token
|
|
299
|
+
* works: the snapshot only exists because an authenticated `/oauth/usage`
|
|
300
|
+
* request succeeded. Matches the periodic tick's own probe window
|
|
301
|
+
* (AUTH_PROBE_MAX_AGE_MS in daemon-ticks.ts — not imported to avoid a cycle;
|
|
302
|
+
* a drift here only widens/narrows evidence freshness, never correctness).
|
|
303
|
+
*/
|
|
304
|
+
const FRESH_USAGE_VERDICT_MAX_AGE_MS = 20 * 60_000;
|
|
305
|
+
/**
|
|
306
|
+
* A `live` verdict derived from the account's usage cache instead of a second
|
|
307
|
+
* network request (RUSH-3036). The auth probe and the usage fetch hit the SAME
|
|
308
|
+
* rate-limited endpoint with the SAME fleet-shared setup-token, so a fresh
|
|
309
|
+
* successful usage snapshot already proves everything the probe would: paying a
|
|
310
|
+
* second request per account per box was half the fleet's endpoint load.
|
|
311
|
+
*
|
|
312
|
+
* Two guards keep the evidence honest (both review findings on the first cut):
|
|
313
|
+
* the caller must assert this box holds a LOCAL credential for the account
|
|
314
|
+
* (`signedIn`) — a fleet-imported snapshot proves the shared token works, not
|
|
315
|
+
* that THIS box can authenticate, so an unsigned home never derives `live`;
|
|
316
|
+
* and a `forceLive` caller (`agents devices ping --strict`) skips derivation
|
|
317
|
+
* entirely, because its contract is a real request that can surface `revoked`
|
|
318
|
+
* within seconds, not minutes. Returns null when there is no admissible fresh
|
|
319
|
+
* evidence — the caller then live-probes as before.
|
|
320
|
+
*/
|
|
321
|
+
function verdictFromFreshUsage(usageKey, signedIn, now) {
|
|
322
|
+
if (!usageKey || !signedIn)
|
|
323
|
+
return null;
|
|
324
|
+
const snapshot = readClaudeUsageCache(usageKey);
|
|
325
|
+
const capturedAt = snapshot?.capturedAt?.getTime();
|
|
326
|
+
if (!capturedAt || now - capturedAt >= FRESH_USAGE_VERDICT_MAX_AGE_MS)
|
|
327
|
+
return null;
|
|
328
|
+
const ageMin = Math.max(1, Math.round((now - capturedAt) / 60_000));
|
|
329
|
+
return { verdict: 'live', checkedAt: now, detail: `token proven live by a usage fetch ${ageMin}m ago` };
|
|
330
|
+
}
|
|
297
331
|
/**
|
|
298
332
|
* Complete a live auth probe for one (agent, home). For claude/kimi/droid this
|
|
299
|
-
* hits the provider
|
|
300
|
-
*
|
|
301
|
-
*
|
|
333
|
+
* hits the provider — unless the account's usage cache already holds a fresh
|
|
334
|
+
* successful fetch, which is the same authenticated request and proves the
|
|
335
|
+
* token live without spending a second one (RUSH-3036). For everyone else it
|
|
336
|
+
* reports a best-effort local verdict (`unverified` when a credential is
|
|
337
|
+
* present, `unconfigured` otherwise) — never masquerading as `live`.
|
|
302
338
|
*/
|
|
303
339
|
export async function probeAuthHealth(agent, home, opts) {
|
|
304
340
|
const checkedAt = Date.now();
|
|
305
341
|
if (LIVE_PROBE_AGENTS.has(agent)) {
|
|
342
|
+
const usageScope = opts?.info?.usageKey ?? null;
|
|
343
|
+
if (opts?.forceLive !== true) {
|
|
344
|
+
const derived = verdictFromFreshUsage(usageScope, opts?.info?.signedIn === true, checkedAt);
|
|
345
|
+
if (derived)
|
|
346
|
+
return derived;
|
|
347
|
+
}
|
|
306
348
|
let probe;
|
|
307
349
|
if (agent === 'claude')
|
|
308
|
-
probe = await probeClaudeStatus(home, opts?.cliVersion);
|
|
350
|
+
probe = await probeClaudeStatus(home, opts?.cliVersion, usageScope);
|
|
309
351
|
else if (agent === 'kimi')
|
|
310
|
-
probe = await probeKimiStatus(home);
|
|
352
|
+
probe = await probeKimiStatus(home, usageScope);
|
|
311
353
|
else
|
|
312
|
-
probe = await probeDroidStatus(home);
|
|
354
|
+
probe = await probeDroidStatus(home, usageScope);
|
|
313
355
|
return { verdict: verdictFromProbe(probe), checkedAt, detail: probeDetail(probe) };
|
|
314
356
|
}
|
|
315
357
|
const info = opts?.info !== undefined ? opts.info : await getAccountInfo(agent, home).catch(() => null);
|
|
@@ -384,7 +426,7 @@ export async function probeLocalFleetAuth(opts) {
|
|
|
384
426
|
// accounts, so no same-account concurrency is left to trip the throttle.
|
|
385
427
|
const perGroup = await Promise.all(groupFleetAuthInstalls(installs, (inst) => LIVE_PROBE_AGENTS.has(inst.agent)).map(async (group) => {
|
|
386
428
|
const rep = group.probe;
|
|
387
|
-
const health = await probeAuthHealth(rep.agent, rep.home, { cliVersion: opts?.cliVersion, info: rep.info });
|
|
429
|
+
const health = await probeAuthHealth(rep.agent, rep.home, { cliVersion: opts?.cliVersion, info: rep.info, forceLive: opts?.forceLive });
|
|
388
430
|
health.account = authAccountLabel(rep.info);
|
|
389
431
|
if (health.verdict === 'unconfigured')
|
|
390
432
|
return [];
|
|
@@ -13,3 +13,22 @@ export declare const DEFAULT_VIEWPORT: {
|
|
|
13
13
|
export declare const DEVICES: Record<string, DeviceDescriptor>;
|
|
14
14
|
export declare function getDevice(name: string): DeviceDescriptor | undefined;
|
|
15
15
|
export declare function listDevices(): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Parse a `--window WxH` value. Returns null when the value is malformed, so
|
|
18
|
+
* callers emit their own actionable error.
|
|
19
|
+
*
|
|
20
|
+
* Shared by `profiles create` and `profiles edit` — one regex, one test, no
|
|
21
|
+
* drift between the two surfaces.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseWindowSize(raw: string): {
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
} | null;
|
|
27
|
+
/**
|
|
28
|
+
* Parse a `--position X,Y` value. Negative coordinates are valid — a window may
|
|
29
|
+
* sit on a display left of or above the primary one.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseWindowPosition(raw: string): {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
} | null;
|
|
@@ -36,3 +36,26 @@ export function getDevice(name) {
|
|
|
36
36
|
export function listDevices() {
|
|
37
37
|
return Object.keys(DEVICES);
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse a `--window WxH` value. Returns null when the value is malformed, so
|
|
41
|
+
* callers emit their own actionable error.
|
|
42
|
+
*
|
|
43
|
+
* Shared by `profiles create` and `profiles edit` — one regex, one test, no
|
|
44
|
+
* drift between the two surfaces.
|
|
45
|
+
*/
|
|
46
|
+
export function parseWindowSize(raw) {
|
|
47
|
+
const m = String(raw).match(/^(\d+)x(\d+)$/);
|
|
48
|
+
if (!m)
|
|
49
|
+
return null;
|
|
50
|
+
return { width: parseInt(m[1], 10), height: parseInt(m[2], 10) };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse a `--position X,Y` value. Negative coordinates are valid — a window may
|
|
54
|
+
* sit on a display left of or above the primary one.
|
|
55
|
+
*/
|
|
56
|
+
export function parseWindowPosition(raw) {
|
|
57
|
+
const m = String(raw).match(/^(-?\d+),(-?\d+)$/);
|
|
58
|
+
if (!m)
|
|
59
|
+
return null;
|
|
60
|
+
return { x: parseInt(m[1], 10), y: parseInt(m[2], 10) };
|
|
61
|
+
}
|
|
@@ -96,7 +96,7 @@ export declare function sendIPCRequest(request: IPCRequest, opts?: IPCRequestOpt
|
|
|
96
96
|
* Fill actor / launchId / sessionId from the calling process when the request
|
|
97
97
|
* left them blank. Explicit values on the request always win.
|
|
98
98
|
*/
|
|
99
|
-
export declare function stampCallerIdentity(request: IPCRequest): IPCRequest;
|
|
99
|
+
export declare function stampCallerIdentity(request: IPCRequest, env?: NodeJS.ProcessEnv): IPCRequest;
|
|
100
100
|
/**
|
|
101
101
|
* Open one connection that can serve many browser operations. This uses the
|
|
102
102
|
* same daemon readiness and version-reconciliation path as sendIPCRequest;
|
package/dist/lib/browser/ipc.js
CHANGED
|
@@ -8,6 +8,7 @@ import { startDaemon, stopDaemon } from '../daemon/daemon.js';
|
|
|
8
8
|
import { getCliVersion } from '../version.js';
|
|
9
9
|
import { compareVersions } from '../agent-spec/primitives.js';
|
|
10
10
|
import { getDaemonLogPath } from '../daemon/daemon.js';
|
|
11
|
+
import { isFleetRemoteInvocation } from './remote-control.js';
|
|
11
12
|
import { resolveCallerIdentity } from './caller-identity.js';
|
|
12
13
|
import { actionable } from './service.js';
|
|
13
14
|
/**
|
|
@@ -382,6 +383,7 @@ export class BrowserIPCServer {
|
|
|
382
383
|
actor: request.actor,
|
|
383
384
|
launchId: request.launchId,
|
|
384
385
|
sessionId: request.sessionId,
|
|
386
|
+
fleetRemote: request.fleetRemote,
|
|
385
387
|
createIfMissing,
|
|
386
388
|
title: request.title,
|
|
387
389
|
url: request.url,
|
|
@@ -421,6 +423,22 @@ export class BrowserIPCServer {
|
|
|
421
423
|
case 'version': {
|
|
422
424
|
return { ok: true, version: getCliVersion() };
|
|
423
425
|
}
|
|
426
|
+
case 'show': {
|
|
427
|
+
// Task-less by design — see BrowserService.showUrl. Absent from both
|
|
428
|
+
// PAGE_CREATE_VERBS and PAGE_RESOLVE_VERBS, so bindTask never ran above
|
|
429
|
+
// and this request carries no task.
|
|
430
|
+
if (!request.url) {
|
|
431
|
+
return { ok: false, error: actionable('URL required.', 'Next: agents browser navigate <url>') };
|
|
432
|
+
}
|
|
433
|
+
if (!request.profile) {
|
|
434
|
+
return { ok: false, error: actionable('Profile required.', 'Next: agents browser profiles list') };
|
|
435
|
+
}
|
|
436
|
+
const shown = await this.service.showUrl(request.profile, request.url, {
|
|
437
|
+
fleetRemote: request.fleetRemote,
|
|
438
|
+
actor: request.actor,
|
|
439
|
+
});
|
|
440
|
+
return { ok: true, tabId: shown.tabId };
|
|
441
|
+
}
|
|
424
442
|
case 'start': {
|
|
425
443
|
if (!request.profile) {
|
|
426
444
|
return { ok: false, error: actionable('Profile required.', 'Next: agents browser profiles list') };
|
|
@@ -434,6 +452,7 @@ export class BrowserIPCServer {
|
|
|
434
452
|
actor: request.actor,
|
|
435
453
|
launchId: request.launchId,
|
|
436
454
|
sessionId: request.sessionId,
|
|
455
|
+
fleetRemote: request.fleetRemote,
|
|
437
456
|
title: request.title,
|
|
438
457
|
});
|
|
439
458
|
return {
|
|
@@ -855,15 +874,22 @@ export async function sendIPCRequest(request, opts = {}) {
|
|
|
855
874
|
* Fill actor / launchId / sessionId from the calling process when the request
|
|
856
875
|
* left them blank. Explicit values on the request always win.
|
|
857
876
|
*/
|
|
858
|
-
export function stampCallerIdentity(request) {
|
|
859
|
-
|
|
860
|
-
|
|
877
|
+
export function stampCallerIdentity(request, env = process.env) {
|
|
878
|
+
// The consent marker is stamped FIRST, above the identity early-return: a
|
|
879
|
+
// request that already carries a full identity (every streamed request does)
|
|
880
|
+
// would otherwise skip stamping entirely and reach the daemon unmarked.
|
|
881
|
+
// OR-ed, never overwritten — a client may assert the marker but must not be
|
|
882
|
+
// able to clear one this environment sets.
|
|
883
|
+
const fleetRemote = isFleetRemoteInvocation(env) || request.fleetRemote === true;
|
|
884
|
+
const marked = request.fleetRemote === fleetRemote ? request : { ...request, fleetRemote };
|
|
885
|
+
if (marked.actor && marked.launchId && marked.sessionId)
|
|
886
|
+
return marked;
|
|
861
887
|
const id = resolveCallerIdentity();
|
|
862
888
|
return {
|
|
863
|
-
...
|
|
864
|
-
actor:
|
|
865
|
-
launchId:
|
|
866
|
-
sessionId:
|
|
889
|
+
...marked,
|
|
890
|
+
actor: marked.actor ?? id.actor,
|
|
891
|
+
launchId: marked.launchId ?? id.launchId,
|
|
892
|
+
sessionId: marked.sessionId ?? id.sessionId,
|
|
867
893
|
};
|
|
868
894
|
}
|
|
869
895
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BrowserProfile } from './types.js';
|
|
1
|
+
import type { BrowserProfile, ProfileName } from './types.js';
|
|
2
2
|
export type { BrowserProfile } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Name of the profile `ensureDefaultBrowserProfile` auto-detects and pins.
|
|
@@ -167,6 +167,20 @@ export declare function effectiveLocalPort(profile: BrowserProfile): number | un
|
|
|
167
167
|
* skip remote profiles in this scan.
|
|
168
168
|
*/
|
|
169
169
|
export declare function findFreeProfilePort(): Promise<number>;
|
|
170
|
+
/**
|
|
171
|
+
* Refuse a profile whose LOCAL port another profile already owns.
|
|
172
|
+
*
|
|
173
|
+
* Every CDP/SSH profile ends up listening on (or tunneling to) the same LOCAL
|
|
174
|
+
* port number as the one configured in the endpoint URL — SSH profiles reuse
|
|
175
|
+
* `?port=N` locally, so we no longer scope by host. Two profiles that would
|
|
176
|
+
* need the same local port can't both run at the same time.
|
|
177
|
+
*
|
|
178
|
+
* `opts.ignore` is the profile's own name on an edit. Without it every edit
|
|
179
|
+
* collides with itself, since the stored copy still owns the port being kept.
|
|
180
|
+
*/
|
|
181
|
+
export declare function assertLocalPortFree(profile: BrowserProfile, opts?: {
|
|
182
|
+
ignore?: string;
|
|
183
|
+
}): void;
|
|
170
184
|
/**
|
|
171
185
|
* Create a profile. **It lands in this machine's own store by default** — pass
|
|
172
186
|
* `{ fleet: true }` (the CLI's `--fleet`) to put it in the synced `agents.yaml`
|
|
@@ -188,6 +202,97 @@ export declare function createProfile(profile: BrowserProfile, opts?: {
|
|
|
188
202
|
fleet?: boolean;
|
|
189
203
|
}): Promise<void>;
|
|
190
204
|
export declare function updateProfile(profile: BrowserProfile): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Flag a fleet-synced profile whose contents only make sense on ONE machine.
|
|
207
|
+
*
|
|
208
|
+
* A `cdp://` endpoint on loopback always connects on the machine EVALUATING the
|
|
209
|
+
* profile, so a fleet-scope copy does not mean "that browser" — it means "port N
|
|
210
|
+
* on whichever box ran the command". The name then silently resolves to a
|
|
211
|
+
* different, usually logged-out browser on every other machine, which is the
|
|
212
|
+
* worst possible failure for a profile that carries live logins.
|
|
213
|
+
*
|
|
214
|
+
* Reported by `profiles doctor` (a failing `scope` check) and noted in the
|
|
215
|
+
* `kept` reason `profiles prune` prints for a fleet profile. The repair is
|
|
216
|
+
* {@link moveProfileScope}, never an automatic rewrite of the synced doc.
|
|
217
|
+
*/
|
|
218
|
+
export declare function misfiledFleetProfile(profile: BrowserProfile, scope: ProfileScope): {
|
|
219
|
+
misfiled: false;
|
|
220
|
+
} | {
|
|
221
|
+
misfiled: true;
|
|
222
|
+
why: string;
|
|
223
|
+
};
|
|
224
|
+
/** Fields an existing profile may be edited in place.
|
|
225
|
+
*
|
|
226
|
+
* Deliberately a SUBSET of {@link BrowserProfile}. `name` and `browser` are
|
|
227
|
+
* identity, not settings: both key the on-disk runtime dir
|
|
228
|
+
* ({@link getProfileRuntimeDir}) and any live `<name>@<endpoint>` connection, so
|
|
229
|
+
* changing either orphans the cached browser data. Delete and recreate instead.
|
|
230
|
+
*/
|
|
231
|
+
export type EditableProfileFields = Partial<Pick<BrowserProfile, 'description' | 'binary' | 'electron' | 'targetFilter' | 'endpoints' | 'chrome' | 'secrets' | 'viewport'>>;
|
|
232
|
+
export interface EditProfileResult {
|
|
233
|
+
profile: BrowserProfile;
|
|
234
|
+
scope: ProfileScope;
|
|
235
|
+
/** Field names that actually changed. Empty means the edit was a no-op. */
|
|
236
|
+
changed: string[];
|
|
237
|
+
/**
|
|
238
|
+
* Set when the write landed machine-locally while a stale fleet copy remains.
|
|
239
|
+
* Only reachable for {@link isMachineLocalProfile} names that an older version
|
|
240
|
+
* wrote into the fleet store: {@link updateProfile} pins those local, so this
|
|
241
|
+
* box sees the edit and every other box keeps the old value. Callers must say
|
|
242
|
+
* so rather than reporting a clean success.
|
|
243
|
+
*/
|
|
244
|
+
fleetCopyLeftStale?: boolean;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Merge `patch` onto the stored profile and persist it in the store it already
|
|
248
|
+
* lives in (see {@link updateProfile} for the scope rules).
|
|
249
|
+
*
|
|
250
|
+
* Runs {@link createProfile}'s validations against the MERGED record, not just
|
|
251
|
+
* the patched fields — a binary edit re-resolves the browser path, a
|
|
252
|
+
* targetFilter edit re-checks the electron gate, and the local-port scan runs
|
|
253
|
+
* with this profile excluded so an unchanged port is not a self-collision.
|
|
254
|
+
*/
|
|
255
|
+
export declare function editProfile(name: string, patch: EditableProfileFields): Promise<EditProfileResult>;
|
|
256
|
+
/**
|
|
257
|
+
* Throw if `name` cannot be used for a NEW profile.
|
|
258
|
+
*
|
|
259
|
+
* Shared by `profiles create` and {@link renameProfile} so the two cannot drift
|
|
260
|
+
* — the shape rule used to live inline in the command, which is how `rename`
|
|
261
|
+
* would have accepted a name `create` rejects.
|
|
262
|
+
*
|
|
263
|
+
* `default` is refused because it is the reserved ALIAS meaning "whatever
|
|
264
|
+
* profile this machine is configured to use" (RUSH-2709). A literal profile by
|
|
265
|
+
* that name makes `--profile default` mean two different things.
|
|
266
|
+
*/
|
|
267
|
+
export declare function assertRegistrableProfileName(name: string): void;
|
|
268
|
+
/**
|
|
269
|
+
* Rename a profile, taking its on-disk state with it.
|
|
270
|
+
*
|
|
271
|
+
* `editProfile` deliberately refuses a name change, and this is why: the name
|
|
272
|
+
* keys the runtime dir ({@link getProfileRuntimeDir}), every fork/endpoint dir
|
|
273
|
+
* derived from it, and the `browser.profile` pointer. Delete-and-recreate — the
|
|
274
|
+
* only route before this — silently abandons the browser's `--user-data-dir`,
|
|
275
|
+
* which is where a profile's logins live. On a real agent browser that is
|
|
276
|
+
* gigabytes of session state and every account it has ever signed into.
|
|
277
|
+
*
|
|
278
|
+
* Refuses when the profile is in use: moving a `--user-data-dir` out from under
|
|
279
|
+
* a running browser corrupts it.
|
|
280
|
+
*/
|
|
281
|
+
export declare function renameProfile(from: ProfileName, to: ProfileName): Promise<{
|
|
282
|
+
scope: ProfileScope;
|
|
283
|
+
movedDirs: string[];
|
|
284
|
+
repointedDefault: boolean;
|
|
285
|
+
repointedViewer: boolean;
|
|
286
|
+
/** Peers still pinning the OLD name, and which key each used. */
|
|
287
|
+
stalePins: Array<{
|
|
288
|
+
device: string;
|
|
289
|
+
key: 'browser.profile' | 'browser.viewer';
|
|
290
|
+
}>;
|
|
291
|
+
}>;
|
|
292
|
+
export declare function moveProfileScope(name: string, to: ProfileScope): Promise<{
|
|
293
|
+
from: ProfileScope;
|
|
294
|
+
to: ProfileScope;
|
|
295
|
+
}>;
|
|
191
296
|
export declare function deleteProfile(name: string): Promise<void>;
|
|
192
297
|
/**
|
|
193
298
|
* Pad `text` to exactly `width` visible characters, truncating with an ellipsis
|