@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,27 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `agents sessions share <id>` — publish one session transcript as a link.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Renders the session locally — `renderSessionMarkdownDocument()` (redacted
|
|
5
|
+
* transcript) and `renderSessionHtmlDocument()` (self-contained branded page) —
|
|
6
|
+
* then publishes it through the standalone `artifacts` CLI (`artifacts share`),
|
|
7
|
+
* the single home for artifact sharing since PHNX-3992. agents-cli holds no
|
|
8
|
+
* share engine of its own; this command contributes the session-specific
|
|
9
|
+
* rendering and redaction, and forwards the finished page to `artifacts share`.
|
|
9
10
|
*
|
|
10
|
-
* Unlisted by default, unlike `
|
|
11
|
-
* paths, command output, error text, and whatever a tool printed —
|
|
12
|
-
* than a plan does — so it does not belong in the public `/<user>`
|
|
13
|
-
* the operator asks for it with `--public`. The URL itself stays
|
|
14
|
-
* unlisted is a capability URL, not a secret.
|
|
11
|
+
* Unlisted by default, unlike `artifacts share` (public by default). A transcript
|
|
12
|
+
* carries file paths, command output, error text, and whatever a tool printed —
|
|
13
|
+
* strictly more than a plan does — so it does not belong in the public `/<user>`
|
|
14
|
+
* gallery unless the operator asks for it with `--public`. The URL itself stays
|
|
15
|
+
* world-readable: unlisted is a capability URL, not a secret.
|
|
15
16
|
*/
|
|
16
17
|
import * as fs from 'fs';
|
|
17
18
|
import * as os from 'os';
|
|
18
19
|
import * as path from 'path';
|
|
20
|
+
import { spawnSync } from 'node:child_process';
|
|
19
21
|
import chalk from 'chalk';
|
|
20
22
|
import { setHelpSections } from '../lib/help.js';
|
|
21
|
-
import { knownSecretValuesFromEnv } from '../lib/redact.js';
|
|
23
|
+
import { knownSecretValuesFromEnv, redactEmails } from '../lib/redact.js';
|
|
22
24
|
import { discoverSessions } from '../lib/session/discover.js';
|
|
23
25
|
import { renderSessionHtmlDocument } from '../lib/session/share-html.js';
|
|
24
|
-
import {
|
|
26
|
+
import { resolveArtifactsBin, invocation, ArtifactsClientError } from '../lib/artifacts-client.js';
|
|
25
27
|
import { renderSessionMarkdownDocument } from './sessions-render.js';
|
|
26
28
|
import { selectSessions } from './sessions-export.js';
|
|
27
29
|
import { parseAgentFilter } from './sessions.js';
|
|
@@ -35,23 +37,33 @@ export function defaultSessionSlug(session) {
|
|
|
35
37
|
return `session-${session.shortId || session.id}`;
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
|
-
*
|
|
40
|
+
* Build the `artifacts share` argv for a session publish.
|
|
39
41
|
*
|
|
40
|
-
* Extracted so the
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* flipped.
|
|
42
|
+
* Extracted so the mapping is testable directly. The security-relevant default
|
|
43
|
+
* is `--visibility unlisted` — it inverts `artifacts share`'s public default, and
|
|
44
|
+
* a test that re-implements this mapping would still pass with it flipped.
|
|
44
45
|
*/
|
|
45
|
-
export function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
meta
|
|
54
|
-
|
|
46
|
+
export function buildArtifactsShareArgs(session, options, file) {
|
|
47
|
+
const args = [
|
|
48
|
+
'share',
|
|
49
|
+
file,
|
|
50
|
+
'--slug',
|
|
51
|
+
options.slug ?? defaultSessionSlug(session),
|
|
52
|
+
'--visibility',
|
|
53
|
+
options.public === true ? 'public' : 'unlisted',
|
|
54
|
+
'--meta',
|
|
55
|
+
'kind=session',
|
|
56
|
+
'--json',
|
|
57
|
+
];
|
|
58
|
+
if (options.expire)
|
|
59
|
+
args.push('--expire', options.expire);
|
|
60
|
+
if (options.force === true)
|
|
61
|
+
args.push('--force');
|
|
62
|
+
if (options.cover === false)
|
|
63
|
+
args.push('--no-cover');
|
|
64
|
+
if (options.label)
|
|
65
|
+
args.push('--label', options.label);
|
|
66
|
+
return args;
|
|
55
67
|
}
|
|
56
68
|
export function registerSessionsShareCommand(sessionsCmd) {
|
|
57
69
|
const cmd = sessionsCmd
|
|
@@ -59,7 +71,7 @@ export function registerSessionsShareCommand(sessionsCmd) {
|
|
|
59
71
|
.description('Publish one session as a redacted, self-contained web page and print the link.')
|
|
60
72
|
.option('--public', 'List the page in your public share gallery (default: unlisted capability URL)')
|
|
61
73
|
.option('--slug <slug>', 'URL slug under your namespace (default: session-<shortId>)')
|
|
62
|
-
.option('--label <text>', 'Display title in the gallery and `
|
|
74
|
+
.option('--label <text>', 'Display title in the gallery and `artifacts share list`')
|
|
63
75
|
.option('--expire <spec>', 'Auto-expire window: 30d, 12h, a date, or never (default: 30d)')
|
|
64
76
|
.option('--reasoning <mode>', 'Reasoning visibility: omit, fold, or include', 'omit')
|
|
65
77
|
.option('--force', 'Publish despite the sensitive-content scan flagging the transcript')
|
|
@@ -76,22 +88,23 @@ agents sessions share a1b2c3d4 --reasoning fold
|
|
|
76
88
|
|
|
77
89
|
# A link that does not decay
|
|
78
90
|
agents sessions share a1b2c3d4 --expire never`,
|
|
79
|
-
notes: `
|
|
80
|
-
|
|
91
|
+
notes: `Publishes through the standalone artifacts CLI — install it with
|
|
92
|
+
'npm i -g @phnx-labs/artifacts-cli'. Sign in with
|
|
93
|
+
'artifacts auth login' for the managed endpoint (zero Cloudflare setup), or configure
|
|
94
|
+
your own bucket with 'artifacts share setup' / 'artifacts share join <baseUrl>'.
|
|
81
95
|
|
|
82
96
|
Unlisted by default — the URL is world-readable but the page stays out of your public
|
|
83
|
-
gallery and out of '
|
|
97
|
+
gallery and out of 'artifacts share list'. Pass --public to list it.
|
|
84
98
|
|
|
85
|
-
Secrets are redacted and
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
Secrets are redacted and 'artifacts share' runs its own pre-publish scan on the page,
|
|
100
|
+
so a transcript carrying emails or credential-shaped strings is refused unless you pass
|
|
101
|
+
--force. --no-redact (the sessions-level flag) disables redaction and is a bad idea for
|
|
102
|
+
anything you publish.
|
|
89
103
|
|
|
90
104
|
Re-running with the same session updates the same URL and keeps the prior version
|
|
91
|
-
as a revision ('
|
|
105
|
+
as a revision ('artifacts share revisions <slug>').
|
|
92
106
|
|
|
93
|
-
Manage published sessions with '
|
|
94
|
-
'agents artifacts share delete <slug>'.`,
|
|
107
|
+
Manage published sessions with 'artifacts share list' and 'artifacts share delete <slug>'.`,
|
|
95
108
|
});
|
|
96
109
|
cmd.action(async (selector, options, command) => {
|
|
97
110
|
const globals = command.optsWithGlobals();
|
|
@@ -125,25 +138,60 @@ Manage published sessions with 'agents artifacts share list' and
|
|
|
125
138
|
});
|
|
126
139
|
// Emails on top of what the renderer masks. Almost every real transcript
|
|
127
140
|
// carries a few — git author addresses, `gh api user`, a pasted log — and
|
|
128
|
-
//
|
|
129
|
-
// means the published page genuinely does not carry them; the
|
|
130
|
-
// telling people to pass --force,
|
|
131
|
-
// that also catches real credentials.
|
|
141
|
+
// `artifacts share` refuses a body containing any (its own scan). Masking
|
|
142
|
+
// them means the published page genuinely does not carry them; the
|
|
143
|
+
// alternative, telling people to pass --force, trains everyone to bypass the
|
|
144
|
+
// gate that also catches real credentials.
|
|
132
145
|
//
|
|
133
146
|
// Applied to the RENDERED PAGE, not the Markdown, so the text that is masked
|
|
134
|
-
// is exactly the text
|
|
147
|
+
// is exactly the text the publish scan will see. Markdown escaping stands
|
|
135
148
|
// between the two: `foo\@example.com` hides from the pattern in the Markdown
|
|
136
149
|
// and reappears as a live address once marked drops the backslash.
|
|
137
150
|
const page = renderSessionHtmlDocument(session, markdown, { redacted: redact });
|
|
138
151
|
const html = redact ? redactEmails(page) : page;
|
|
139
|
-
// A real file on disk is what
|
|
140
|
-
// it in a browser. 0600 + a per-run directory keeps the intermediate
|
|
141
|
-
// world-readable /tmp path while it exists.
|
|
152
|
+
// A real file on disk is what `artifacts share` takes, and its OG capturer
|
|
153
|
+
// opens it in a browser. 0600 + a per-run directory keeps the intermediate
|
|
154
|
+
// off a world-readable /tmp path while it exists.
|
|
142
155
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'agents-session-share-'));
|
|
143
156
|
const file = path.join(dir, `${defaultSessionSlug(session)}.html`);
|
|
144
157
|
try {
|
|
145
158
|
fs.writeFileSync(file, html, { mode: 0o600 });
|
|
146
|
-
|
|
159
|
+
let bin;
|
|
160
|
+
try {
|
|
161
|
+
bin = resolveArtifactsBin();
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
if (err instanceof ArtifactsClientError) {
|
|
165
|
+
process.stderr.write(chalk.red(err.message + '\n'));
|
|
166
|
+
process.exitCode = 1;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
const { command: execCommand, prefix } = invocation(bin);
|
|
172
|
+
const args = [...prefix, ...buildArtifactsShareArgs(session, options, file)];
|
|
173
|
+
const proc = spawnSync(execCommand, args, { encoding: 'utf-8' });
|
|
174
|
+
if (proc.error)
|
|
175
|
+
throw proc.error;
|
|
176
|
+
if (proc.status !== 0) {
|
|
177
|
+
process.stderr.write(proc.stderr || chalk.red(`artifacts share exited ${proc.status ?? 'with no status'}\n`));
|
|
178
|
+
process.exitCode = proc.status ?? 1;
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
let result;
|
|
182
|
+
try {
|
|
183
|
+
result = JSON.parse(proc.stdout);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// A 0 exit with non-JSON stdout (a version-skewed `artifacts` that
|
|
187
|
+
// printed a banner before the JSON, or one predating `--json` here) must
|
|
188
|
+
// fail loud with the bytes it actually returned, not an uncaught parse
|
|
189
|
+
// stack trace — mirroring `secrets-client.ts`'s `parseResponse`.
|
|
190
|
+
const preview = proc.stdout.trim().slice(0, 200);
|
|
191
|
+
process.stderr.write(chalk.red(`artifacts share returned a non-JSON response${preview ? `: ${preview}` : ' (empty output)'}\n`));
|
|
192
|
+
process.exitCode = 1;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
147
195
|
if (globals.json) {
|
|
148
196
|
process.stdout.write(JSON.stringify({
|
|
149
197
|
session: session.id,
|
|
@@ -154,9 +202,10 @@ Manage published sessions with 'agents artifacts share list' and
|
|
|
154
202
|
return;
|
|
155
203
|
}
|
|
156
204
|
process.stdout.write(`${result.url}\n`);
|
|
157
|
-
const
|
|
205
|
+
const visibility = result.visibility ?? (result.unlisted ? 'unlisted' : options.public ? 'public' : 'unlisted');
|
|
206
|
+
const bits = [visibility, redact ? 'redacted' : chalk.red('NOT redacted')];
|
|
158
207
|
if (result.expiresAt)
|
|
159
|
-
bits.push(`expires ${result.expiresAt.slice(0, 10)}`);
|
|
208
|
+
bits.push(`expires ${String(result.expiresAt).slice(0, 10)}`);
|
|
160
209
|
process.stderr.write(chalk.dim(`${bits.join(' · ')}\n`));
|
|
161
210
|
}
|
|
162
211
|
finally {
|
|
@@ -64,12 +64,9 @@ import { registerSessionsResumeCommand } from './sessions-resume.js';
|
|
|
64
64
|
import { registerSessionsForkCommand } from './fork.js';
|
|
65
65
|
import { registerSessionsBookmarkCommand } from './sessions-bookmark.js';
|
|
66
66
|
import { isBookmarked, listBookmarks } from '../lib/session/bookmarks.js';
|
|
67
|
-
import { registerGoCommand } from './go.js';
|
|
68
67
|
import { registerFocusCommand } from './focus.js';
|
|
69
|
-
import { registerReconnectCommand } from './reconnect.js';
|
|
70
68
|
import { registerDetachCommand } from './detach.js';
|
|
71
69
|
import { registerSessionsStopCommand } from './sessions-stop.js';
|
|
72
|
-
import { registerAttachCommand } from './attach.js';
|
|
73
70
|
import { registerSessionsInjectCommand } from './sessions-inject.js';
|
|
74
71
|
import { registerSessionsExportCommand } from './sessions-export.js';
|
|
75
72
|
import { registerSessionsRenderCommand } from './sessions-render.js';
|
|
@@ -3963,7 +3960,7 @@ async function resumeOnOwnerIfRemote(session) {
|
|
|
3963
3960
|
export async function resumeSessionInPlace(session) {
|
|
3964
3961
|
// This function is the LOCAL takeover, and every caller is responsible for
|
|
3965
3962
|
// routing a peer-owned session before it gets here (the picker above,
|
|
3966
|
-
// `agents sessions resume
|
|
3963
|
+
// `agents sessions resume`). Reaching it with one anyway means a
|
|
3967
3964
|
// caller skipped that step, so refuse rather than start the harness against
|
|
3968
3965
|
// state this box does not have — the `fs.existsSync(session.cwd)` fallback
|
|
3969
3966
|
// just below is exactly how that used to pass unnoticed, quietly swapping in
|
|
@@ -5317,7 +5314,6 @@ export function registerSessionsCommands(program) {
|
|
|
5317
5314
|
resume <id> --attach-only attach only; never fork a copy
|
|
5318
5315
|
resume multi-select history -> open tabs
|
|
5319
5316
|
detach <id> the other direction: interactive -> headless
|
|
5320
|
-
(retired, still working for one release: sessions attach, sessions go, reconnect)
|
|
5321
5317
|
- The interactive listing and every live-status flag fold in your other online machines automatically (live over SSH, no sync) — each row is labelled by host, this machine first. Use --local to skip the fan-out; single-id lookups stay local.
|
|
5322
5318
|
- --all is not a device flag: it widens historical directory and time filters. Fleet collection is already the default. A status flag (--working/--idle/--waiting/--orphan/--crashed/--closed/--abandoned/--queued/--unknown) implies --active; combine status flags for a union.
|
|
5323
5319
|
- --version <version> requires --agent and is equivalent to --agent <agent@version>.
|
|
@@ -5408,12 +5404,9 @@ export function registerSessionsCommands(program) {
|
|
|
5408
5404
|
registerSessionsResumeCommand(sessionsCmd);
|
|
5409
5405
|
registerSessionsForkCommand(sessionsCmd);
|
|
5410
5406
|
registerSessionsBookmarkCommand(sessionsCmd);
|
|
5411
|
-
registerGoCommand(sessionsCmd);
|
|
5412
5407
|
registerFocusCommand(sessionsCmd);
|
|
5413
|
-
registerReconnectCommand(sessionsCmd);
|
|
5414
5408
|
registerDetachCommand(sessionsCmd);
|
|
5415
5409
|
registerSessionsStopCommand(sessionsCmd);
|
|
5416
|
-
registerAttachCommand(sessionsCmd);
|
|
5417
5410
|
registerSessionsInjectCommand(sessionsCmd);
|
|
5418
5411
|
registerSessionsExportCommand(sessionsCmd);
|
|
5419
5412
|
registerSessionsRenderCommand(sessionsCmd);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Idempotent: re-running shows the current default profile and offers to change
|
|
8
8
|
* the pinned browser or re-point the device default.
|
|
9
9
|
*/
|
|
10
|
+
import { openSetupTerminal } from './setup-terminal.js';
|
|
10
11
|
import chalk from 'chalk';
|
|
11
12
|
import { setConfigValue } from '../lib/device-config.js';
|
|
12
13
|
import { listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
@@ -14,6 +15,8 @@ import { DEFAULT_BROWSER_PROFILE_NAME, getAutoDetectedProfile, createProfile, fi
|
|
|
14
15
|
import { DEFAULT_VIEWPORT } from '../lib/browser/devices.js';
|
|
15
16
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
16
17
|
import { defaultBrowserChoice } from './setup-preferences.js';
|
|
18
|
+
import { installSetupTool } from '../lib/setup-tool-install.js';
|
|
19
|
+
import { refreshToolSetup } from '../lib/setup-tool-status.js';
|
|
17
20
|
const INSTALL_HINT = 'Install one of: Google Chrome, Brave, Microsoft Edge, Chromium, Comet, or Arc, then re-run `agents setup browser`.\n' +
|
|
18
21
|
'(This wizard picks a Chromium-family browser. An installed Firefox is not auto-picked here; its profiles are ' +
|
|
19
22
|
'discovered from profiles.ini and selected with `agents browser use firefox-<name>`. Safari is not supported.)';
|
|
@@ -116,9 +119,22 @@ export function registerSetupBrowserCommand(setupCmd) {
|
|
|
116
119
|
setupCmd
|
|
117
120
|
.command('browser')
|
|
118
121
|
.description('Set up `agents browser` — detect an installed browser and create the default profile.')
|
|
119
|
-
.
|
|
122
|
+
.option('--install-only', 'Install the standalone Browser CLI without changing profiles or starting a browser')
|
|
123
|
+
.option('--terminal [backend]', 'Open interactive setup in a detected or selected terminal')
|
|
124
|
+
.action(async (options) => {
|
|
120
125
|
try {
|
|
121
|
-
|
|
126
|
+
if (options.terminal !== undefined) {
|
|
127
|
+
await openSetupTerminal('browser', options.terminal, options.installOnly);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (options.installOnly) {
|
|
131
|
+
if (!(await installSetupTool('browser')))
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (!(await runBrowserWizard()))
|
|
136
|
+
process.exitCode = 1;
|
|
137
|
+
await refreshToolSetup('browser');
|
|
122
138
|
}
|
|
123
139
|
catch (err) {
|
|
124
140
|
if (isPromptCancelled(err)) {
|
|
@@ -8,12 +8,15 @@
|
|
|
8
8
|
* this wizard chains them with the permission hand-holding a fresh machine needs.
|
|
9
9
|
* Idempotent: re-running re-installs the current helper and re-checks trust.
|
|
10
10
|
*/
|
|
11
|
+
import { openSetupTerminal } from './setup-terminal.js';
|
|
11
12
|
import os from 'node:os';
|
|
12
|
-
import { execFileSync
|
|
13
|
+
import { execFileSync } from 'node:child_process';
|
|
13
14
|
import chalk from 'chalk';
|
|
14
15
|
import { installComputerHelperMacLocal, activateComputerHelperMacLocal, probeComputerTrust, } from './computer.js';
|
|
15
16
|
import { resolveComputerBin, ComputerClientError } from '../lib/computer-client.js';
|
|
16
17
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
18
|
+
import { installSetupTool } from '../lib/setup-tool-install.js';
|
|
19
|
+
import { refreshToolSetup } from '../lib/setup-tool-status.js';
|
|
17
20
|
const ACCESSIBILITY_PANE = 'x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility';
|
|
18
21
|
const SCREEN_PANE = 'x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture';
|
|
19
22
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -43,9 +46,7 @@ export async function runComputerWizard() {
|
|
|
43
46
|
catch (error) {
|
|
44
47
|
if (!(error instanceof ComputerClientError) || error.code !== 'COMPUTER_BIN_MISSING')
|
|
45
48
|
throw error;
|
|
46
|
-
|
|
47
|
-
const installed = spawnSync('npm', ['install', '-g', '@phnx-labs/computer-cli@0.1.2'], { stdio: 'inherit' });
|
|
48
|
-
if (installed.status !== 0)
|
|
49
|
+
if (!(await installSetupTool('computer')))
|
|
49
50
|
return false;
|
|
50
51
|
try {
|
|
51
52
|
resolveComputerBin();
|
|
@@ -136,9 +137,22 @@ export function registerSetupComputerCommand(setupCmd) {
|
|
|
136
137
|
setupCmd
|
|
137
138
|
.command('computer')
|
|
138
139
|
.description('Set up `agents computer` (macOS) — install the signed helper and grant control permissions.')
|
|
139
|
-
.
|
|
140
|
+
.option('--install-only', 'Install the standalone Computer CLI without starting the helper or changing permissions')
|
|
141
|
+
.option('--terminal [backend]', 'Open interactive setup in a detected or selected terminal')
|
|
142
|
+
.action(async (options) => {
|
|
140
143
|
try {
|
|
141
|
-
|
|
144
|
+
if (options.terminal !== undefined) {
|
|
145
|
+
await openSetupTerminal('computer', options.terminal, options.installOnly);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (options.installOnly) {
|
|
149
|
+
if (!(await installSetupTool('computer')))
|
|
150
|
+
process.exitCode = 1;
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (!(await runComputerWizard()))
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
await refreshToolSetup('computer');
|
|
142
156
|
}
|
|
143
157
|
catch (err) {
|
|
144
158
|
if (isPromptCancelled(err)) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* `agents clis install secrets` (system `clis/secrets.yaml`) then a pinned
|
|
8
8
|
* `npm i -g`. Users do not set extra env vars.
|
|
9
9
|
*/
|
|
10
|
+
import { openSetupTerminal } from './setup-terminal.js';
|
|
10
11
|
import chalk from 'chalk';
|
|
11
12
|
import * as fs from 'fs';
|
|
12
13
|
import * as path from 'path';
|
|
@@ -14,7 +15,12 @@ import { spawnSync } from 'node:child_process';
|
|
|
14
15
|
import { getHistoryDir } from '../lib/state.js';
|
|
15
16
|
import { resolveSecretsBin, invocation, SecretsClientError, _resetSecretsClientForTest } from '../lib/secrets-client.js';
|
|
16
17
|
import { installCli, resolveCliManifest } from '../lib/cli-resources.js';
|
|
17
|
-
|
|
18
|
+
import { SECRETS_CLI_SPEC } from '../lib/secrets-cli.js';
|
|
19
|
+
import { refreshToolSetup } from '../lib/setup-tool-status.js';
|
|
20
|
+
import { execFileShellSpec } from '../lib/platform/exec.js';
|
|
21
|
+
// Re-exported for back-compat; the canonical pin lives in `secrets-cli.ts` so a
|
|
22
|
+
// version bump touches ONE place (PHNX-3989 consolidation).
|
|
23
|
+
export const SECRETS_CLI_PACKAGE = SECRETS_CLI_SPEC;
|
|
18
24
|
export const INSTALL_HINT = `agents clis install secrets # or: npm i -g ${SECRETS_CLI_PACKAGE}`;
|
|
19
25
|
export function setupSecretsPrefsPath() {
|
|
20
26
|
return path.join(getHistoryDir(), 'setup', 'secrets.json');
|
|
@@ -56,7 +62,8 @@ export function installSecretsCli() {
|
|
|
56
62
|
console.error(chalk.gray(result.error));
|
|
57
63
|
}
|
|
58
64
|
console.log(chalk.gray(`Installing ${SECRETS_CLI_PACKAGE}…`));
|
|
59
|
-
const
|
|
65
|
+
const npm = execFileShellSpec('npm', ['install', '-g', SECRETS_CLI_PACKAGE]);
|
|
66
|
+
const r = spawnSync(npm.command, npm.args, { stdio: 'inherit', shell: npm.shell });
|
|
60
67
|
_resetSecretsClientForTest();
|
|
61
68
|
if (r.error) {
|
|
62
69
|
console.error(chalk.red(`npm install failed: ${r.error.message}`));
|
|
@@ -93,8 +100,21 @@ export function registerSetupSecretsCommand(setupCmd) {
|
|
|
93
100
|
setupCmd
|
|
94
101
|
.command('secrets')
|
|
95
102
|
.description('Install the standalone `secrets` CLI if missing, then run its `secrets migrate` onboarding.')
|
|
96
|
-
.
|
|
97
|
-
|
|
103
|
+
.option('--install-only', 'Install the standalone Secrets CLI without migrating or unlocking secrets')
|
|
104
|
+
.option('--terminal [backend]', 'Open interactive setup in a detected or selected terminal')
|
|
105
|
+
.action(async (options) => {
|
|
106
|
+
if (options.terminal !== undefined) {
|
|
107
|
+
try {
|
|
108
|
+
await openSetupTerminal('secrets', options.terminal, options.installOnly);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!(options.installOnly ? installSecretsCli() : await runSecretsSetupWizard()))
|
|
98
117
|
process.exitCode = 1;
|
|
118
|
+
await refreshToolSetup('secrets');
|
|
99
119
|
});
|
|
100
120
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { currentContext, openRunInTerminal, parseTerminalFlag } from '../lib/terminal/index.js';
|
|
3
|
+
/** The GUI starts a terminal; the terminal child owns wizard completion. */
|
|
4
|
+
export async function openSetupTerminal(tool, terminal, installOnly = false) {
|
|
5
|
+
if (installOnly)
|
|
6
|
+
throw new Error('--terminal and --install-only are separate setup modes.');
|
|
7
|
+
const parsed = parseTerminalFlag(terminal);
|
|
8
|
+
if (parsed.error)
|
|
9
|
+
throw new Error(parsed.error);
|
|
10
|
+
const { readActiveSessionsCache } = await import('../lib/session/session-cache.js');
|
|
11
|
+
const sessions = parsed.backend ? [] : (readActiveSessionsCache('local')?.sessions ?? []).map(({ host, lastActivityMs, startedAtMs }) => ({ host, lastActivityMs, startedAtMs }));
|
|
12
|
+
const result = await openRunInTerminal({
|
|
13
|
+
argv: ['setup', tool],
|
|
14
|
+
forced: parsed.backend,
|
|
15
|
+
cwd: os.homedir(),
|
|
16
|
+
sessions,
|
|
17
|
+
ctx: currentContext(),
|
|
18
|
+
});
|
|
19
|
+
if (!result.ok)
|
|
20
|
+
throw new Error(result.error ?? 'Could not open a terminal for setup.');
|
|
21
|
+
console.log(result.description ?? 'Setup opened in a terminal.');
|
|
22
|
+
}
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export declare function ensureInitialized(program: Command): Promise<void>;
|
|
|
31
31
|
* wizard. Never throws — a cancel or an optional wizard's error just skips the
|
|
32
32
|
* rest and lets core setup complete.
|
|
33
33
|
*/
|
|
34
|
-
type SetupPhase = 'browser' | 'computer' | '
|
|
34
|
+
type SetupPhase = 'browser' | 'computer' | 'secrets' | 'accounts' | 'fleet' | 'watchdog' | 'preferences';
|
|
35
35
|
type SetupStatusState = 'ready' | 'missing' | 'n/a';
|
|
36
36
|
interface SetupStatusRow {
|
|
37
37
|
phase: 'core' | SetupPhase;
|
package/dist/commands/setup.js
CHANGED
|
@@ -20,7 +20,6 @@ import { ensureShimCurrent, switchHomeFileSymlinks, isShimsInPath, addShimsToPat
|
|
|
20
20
|
import { setHelpSections } from '../lib/help.js';
|
|
21
21
|
import { registerSetupBrowserCommand, runBrowserWizard } from './setup-browser.js';
|
|
22
22
|
import { registerSetupComputerCommand, runComputerWizard } from './setup-computer.js';
|
|
23
|
-
import { runShareWizard } from './artifacts-setup.js';
|
|
24
23
|
import { registerSetupMineCommand } from './setup-mine.js';
|
|
25
24
|
import { registerSetupSecretsCommand } from './setup-secrets.js';
|
|
26
25
|
import { registerSetupFleetCommand } from './setup-fleet.js';
|
|
@@ -34,10 +33,10 @@ import { runPreferencesStep } from './setup-preferences.js';
|
|
|
34
33
|
import { getConfiguredDefaultProfileName, getProfile, getAutoDetectedProfile, isProfileLaunchableHere } from '../lib/browser/profiles.js';
|
|
35
34
|
import { listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
36
35
|
import { probeComputerTrust } from './computer.js';
|
|
37
|
-
import { readShareConfig } from '../lib/share/config.js';
|
|
38
36
|
import { loadDevices } from '../lib/devices/registry.js';
|
|
39
37
|
import { getConfigValue } from '../lib/device-config.js';
|
|
40
38
|
import { setupSecretsPrefsPath } from './setup-secrets.js';
|
|
39
|
+
import { SETUP_TOOLS, getCachedToolSetup, refreshToolSetup } from '../lib/setup-tool-status.js';
|
|
41
40
|
const HOME = os.homedir();
|
|
42
41
|
/**
|
|
43
42
|
* Import an existing unmanaged agent installation into agents-cli.
|
|
@@ -280,7 +279,6 @@ export async function getSetupStatus() {
|
|
|
280
279
|
const coreReady = isGitRepo(getAgentsDir());
|
|
281
280
|
const secretsReady = fs.existsSync(setupSecretsPrefsPath());
|
|
282
281
|
const minted = hasMintedSetupToken();
|
|
283
|
-
const shareConfig = readShareConfig();
|
|
284
282
|
const watchdogEnabled = getConfigValue('watchdog.enabled').value === true;
|
|
285
283
|
const interactiveHost = getConfigValue('interactive.host').value;
|
|
286
284
|
const defaultBrowser = getConfigValue('browser.profile').value;
|
|
@@ -291,7 +289,6 @@ export async function getSetupStatus() {
|
|
|
291
289
|
{ phase: 'secrets', state: secretsReady ? 'ready' : 'missing', detail: secretsReady ? 'defaults chosen' : 'defaults not chosen' },
|
|
292
290
|
{ phase: 'accounts', state: minted.ready ? 'ready' : 'missing', detail: minted.detail },
|
|
293
291
|
{ phase: 'fleet', state: Object.keys(devices).length ? 'ready' : 'missing', detail: Object.keys(devices).length ? `${Object.keys(devices).length} device${Object.keys(devices).length === 1 ? '' : 's'} registered` : 'no devices registered' },
|
|
294
|
-
{ phase: 'share', state: shareConfig ? 'ready' : 'missing', detail: shareConfig?.baseUrl ?? 'endpoint not configured' },
|
|
295
292
|
{ phase: 'watchdog', state: watchdogEnabled ? 'ready' : 'missing', detail: watchdogEnabled ? 'enabled on this device' : 'disabled on this device' },
|
|
296
293
|
{ phase: 'preferences', state: interactiveHost || defaultBrowser ? 'ready' : 'missing', detail: [interactiveHost && `host ${interactiveHost}`, defaultBrowser && `browser ${defaultBrowser}`].filter(Boolean).join(' · ') || 'interactive host and browser unset' },
|
|
297
294
|
];
|
|
@@ -309,8 +306,6 @@ async function runSetupPhase(phase) {
|
|
|
309
306
|
await runBrowserWizard();
|
|
310
307
|
else if (phase === 'computer')
|
|
311
308
|
await runComputerWizard();
|
|
312
|
-
else if (phase === 'share')
|
|
313
|
-
await runShareWizard();
|
|
314
309
|
else if (phase === 'secrets')
|
|
315
310
|
await import('./setup-secrets.js').then((m) => m.runSecretsSetupWizard());
|
|
316
311
|
else if (phase === 'accounts')
|
|
@@ -372,8 +367,8 @@ export function registerSetupCommand(program) {
|
|
|
372
367
|
.option('-f, --force', 'Re-run setup even if ~/.agents/.system/ already exists (use with caution)')
|
|
373
368
|
.option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents/.system/ yourself)');
|
|
374
369
|
// Capability subcommands: `agents setup browser|computer|mine|secrets|accounts|fleet|alias|beta`.
|
|
375
|
-
//
|
|
376
|
-
//
|
|
370
|
+
// Artifact publishing is no longer set up here — it lives in the standalone
|
|
371
|
+
// `artifacts` CLI (`artifacts share setup`/`join`, PHNX-3992).
|
|
377
372
|
registerSetupBrowserCommand(setupCmd);
|
|
378
373
|
registerSetupComputerCommand(setupCmd);
|
|
379
374
|
registerSetupMineCommand(setupCmd);
|
|
@@ -404,9 +399,25 @@ export function registerSetupCommand(program) {
|
|
|
404
399
|
`,
|
|
405
400
|
});
|
|
406
401
|
setupCmd.command('status')
|
|
407
|
-
.description('Show setup readiness for core, browser, computer, secrets, accounts, fleet,
|
|
402
|
+
.description('Show setup readiness for core, browser, computer, secrets, accounts, fleet, watchdog, and preferences.')
|
|
408
403
|
.option('--json', 'print machine-readable JSON')
|
|
404
|
+
.option('--tool <name>', 'Read cached standalone tool status: browser, computer, secrets, or all')
|
|
405
|
+
.option('--refresh', 'Explicitly refresh only the selected standalone tool health checks')
|
|
409
406
|
.action(async (options) => {
|
|
407
|
+
if (options.tool || options.refresh) {
|
|
408
|
+
const selected = options.tool ?? 'all';
|
|
409
|
+
if (selected !== 'all' && !SETUP_TOOLS.includes(selected))
|
|
410
|
+
throw new Error('Unknown tool. Choose browser, computer, secrets, or all.');
|
|
411
|
+
const rows = options.refresh
|
|
412
|
+
? await refreshToolSetup(selected)
|
|
413
|
+
: getCachedToolSetup().filter((row) => selected === 'all' || row.tool === selected);
|
|
414
|
+
if (options.json)
|
|
415
|
+
console.log(JSON.stringify(rows));
|
|
416
|
+
else
|
|
417
|
+
for (const row of rows)
|
|
418
|
+
console.log(`${row.tool}: ${row.installed === false ? 'not installed' : row.readiness} · ${row.detail}`);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
410
421
|
const rows = await getSetupStatus();
|
|
411
422
|
if (options.json)
|
|
412
423
|
console.log(JSON.stringify(rows, null, 2));
|
|
@@ -443,7 +454,6 @@ export function registerSetupCommand(program) {
|
|
|
443
454
|
Capability setup can also be run any time on its own:
|
|
444
455
|
agents setup browser # detect a browser + create the default profile
|
|
445
456
|
agents setup computer # install the signed macOS helper + grant permissions
|
|
446
|
-
agents artifacts setup # provision or join a Cloudflare share endpoint
|
|
447
457
|
agents setup secrets # choose secrets backend/policy defaults + import
|
|
448
458
|
agents setup accounts # mint a Claude setup-token for unattended usage/probe
|
|
449
459
|
agents setup fleet # discover Tailscale devices + configure SSH access
|
package/dist/commands/skills.js
CHANGED
|
@@ -511,14 +511,6 @@ Examples:
|
|
|
511
511
|
const output = lines.join('\n');
|
|
512
512
|
printWithPager(output, lines.length);
|
|
513
513
|
});
|
|
514
|
-
// Deprecated alias for 'view'
|
|
515
|
-
skillsCmd
|
|
516
|
-
.command('info [name]', { hidden: true })
|
|
517
|
-
.action(async (name) => {
|
|
518
|
-
console.log(chalk.yellow('Deprecated: Use "agents skills view" instead of "agents skills info"\n'));
|
|
519
|
-
// Re-execute view command logic
|
|
520
|
-
await skillsCmd.commands.find((c) => c.name() === 'view')?.parseAsync(['view', ...(name ? [name] : [])], { from: 'user' });
|
|
521
|
-
});
|
|
522
514
|
}
|
|
523
515
|
/**
|
|
524
516
|
* Build the row data for `agents skills list`. Each row = one central skill
|
package/dist/commands/ssh.d.ts
CHANGED
|
@@ -83,5 +83,11 @@ export interface HarnessInventoryOpts {
|
|
|
83
83
|
*/
|
|
84
84
|
export declare function collectFleetHarnesses(opts: HarnessInventoryOpts): Promise<HostHarnessResult[]>;
|
|
85
85
|
export declare function runDevicesAccounts(opts: HarnessInventoryOpts): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Parse `--argv`'s JSON array, or report exactly what was wrong and return
|
|
88
|
+
* undefined. Fails loud rather than coercing: a caller that meant to pass argv
|
|
89
|
+
* and typo'd the JSON must not silently get shell-string semantics instead.
|
|
90
|
+
*/
|
|
91
|
+
export declare function parseArgvJson(raw: string, log?: (line: string) => void): string[] | undefined;
|
|
86
92
|
/** Register both `agents ssh` and `agents devices`. */
|
|
87
93
|
export declare function registerSshCommands(program: Command): void;
|