@phnx-labs/agents-cli 1.22.114 → 1.22.116
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 +245 -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 +210 -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 +214 -148
- package/dist/commands/feed-watch.js +52 -8
- package/dist/commands/feed.js +65 -14
- 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-picker.d.ts +11 -0
- package/dist/commands/sessions-picker.js +88 -7
- 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.d.ts +21 -2
- package/dist/commands/sessions.js +158 -11
- package/dist/commands/setup-browser.js +18 -2
- package/dist/commands/setup-computer.js +20 -6
- package/dist/commands/setup-secrets.d.ts +2 -2
- package/dist/commands/setup-secrets.js +24 -4
- package/dist/commands/setup-term.d.ts +24 -0
- package/dist/commands/setup-term.js +70 -0
- 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 +29 -11
- package/dist/commands/skills.js +0 -8
- package/dist/commands/ssh.d.ts +6 -0
- package/dist/commands/ssh.js +93 -302
- package/dist/commands/sync.js +14 -5
- package/dist/commands/teams.js +1 -1
- package/dist/commands/traces.js +1 -1
- package/dist/lib/accounting/rotate.d.ts +63 -1
- package/dist/lib/accounting/rotate.js +56 -0
- package/dist/lib/accounts/add.d.ts +0 -5
- package/dist/lib/accounts/add.js +3 -9
- package/dist/lib/accounts/slots.js +32 -2
- package/dist/lib/answer-router.d.ts +11 -2
- package/dist/lib/answer-router.js +26 -2
- package/dist/lib/artifacts-client.d.ts +20 -0
- package/dist/lib/artifacts-client.js +46 -0
- package/dist/lib/auth-mint.d.ts +5 -4
- package/dist/lib/auth-mint.js +6 -5
- package/dist/lib/browser/drivers/arc.d.ts +1 -1
- package/dist/lib/browser/runtime-state.d.ts +55 -0
- package/dist/lib/browser/runtime-state.js +99 -18
- package/dist/lib/browser/service.d.ts +10 -0
- package/dist/lib/browser/service.js +229 -37
- package/dist/lib/browser/types.d.ts +18 -0
- 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/config-keys.d.ts +1 -1
- package/dist/lib/config-keys.js +5 -0
- 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/device-config.js +61 -0
- package/dist/lib/devices/connect.d.ts +49 -5
- package/dist/lib/devices/connect.js +169 -21
- package/dist/lib/devices/doctor-findings.js +2 -6
- package/dist/lib/feed/answer.d.ts +153 -4
- package/dist/lib/feed/answer.js +716 -105
- 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/feed.d.ts +61 -1
- package/dist/lib/feed/feed.js +226 -14
- package/dist/lib/feed/hub-server.d.ts +141 -0
- package/dist/lib/feed/hub-server.js +586 -0
- package/dist/lib/feed/hub.d.ts +95 -0
- package/dist/lib/feed/hub.js +255 -0
- package/dist/lib/feed/pr-status.d.ts +8 -0
- package/dist/lib/feed/pr-status.js +9 -1
- 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/feed-outcome.d.ts +1 -1
- package/dist/lib/feed-outcome.js +9 -2
- package/dist/lib/feed-policy.js +9 -3
- package/dist/lib/fleet/auth-sync.d.ts +2 -55
- package/dist/lib/fleet/auth-sync.js +2 -89
- 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/harness-auth-capabilities.js +7 -2
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +20 -1
- package/dist/lib/hosts/dispatch.js +52 -30
- package/dist/lib/hosts/remote-cmd.d.ts +72 -1
- package/dist/lib/hosts/remote-cmd.js +151 -10
- 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/mailbox.d.ts +12 -0
- package/dist/lib/mailbox.js +16 -2
- package/dist/lib/mcp.js +17 -11
- package/dist/lib/menubar/snapshot.d.ts +51 -0
- package/dist/lib/menubar/snapshot.js +42 -3
- package/dist/lib/open-url.js +2 -2
- package/dist/lib/probe.d.ts +4 -1
- package/dist/lib/probe.js +5 -2
- package/dist/lib/projects.d.ts +23 -0
- package/dist/lib/projects.js +78 -0
- 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/active.d.ts +1 -0
- package/dist/lib/session/active.js +8 -0
- package/dist/lib/session/db.d.ts +67 -3
- package/dist/lib/session/db.js +381 -126
- package/dist/lib/session/detached.d.ts +7 -0
- package/dist/lib/session/detached.js +29 -0
- package/dist/lib/session/prompt.d.ts +23 -7
- package/dist/lib/session/prompt.js +46 -8
- 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/remote/remote-list.d.ts +20 -0
- package/dist/lib/session/remote/remote-list.js +22 -6
- package/dist/lib/session/remote/watch.d.ts +12 -0
- package/dist/lib/session/remote/watch.js +9 -0
- package/dist/lib/session/remote-preview-cache.d.ts +29 -0
- package/dist/lib/session/remote-preview-cache.js +373 -0
- 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/session/tail.d.ts +50 -0
- package/dist/lib/session/tail.js +219 -0
- 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 +27 -0
- package/dist/lib/setup-tool-status.d.ts +22 -0
- package/dist/lib/setup-tool-status.js +220 -0
- package/dist/lib/share-runtime.d.ts +11 -0
- package/dist/lib/share-runtime.js +63 -0
- package/dist/lib/signin-badge.d.ts +19 -4
- package/dist/lib/signin-badge.js +29 -11
- 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/term-driver.d.ts +24 -0
- package/dist/lib/term-driver.js +36 -0
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/index.js +1 -1
- package/dist/lib/terminal/inject.d.ts +38 -0
- package/dist/lib/terminal/inject.js +55 -9
- package/dist/lib/terminal/transport.d.ts +15 -5
- package/dist/lib/terminal/transport.js +61 -11
- 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/fleet/remote-login.d.ts +0 -170
- package/dist/lib/fleet/remote-login.js +0 -568
- 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
|
@@ -4,7 +4,7 @@ import { buildServeEnv, invocation, resolveSecretsBin, SecretsClientError } from
|
|
|
4
4
|
import { flagValue } from '../lib/hosts/routing-flag.js';
|
|
5
5
|
import { stripRoutingFlags } from '../lib/hosts/remote-cmd.js';
|
|
6
6
|
import { resolveRemoteDevice } from '../lib/ssh-tunnel.js';
|
|
7
|
-
|
|
7
|
+
import { SECRETS_CLI_INSTALL_HINT } from '../lib/secrets-cli.js';
|
|
8
8
|
/**
|
|
9
9
|
* Rewrite a `--device <name>` (or `-D`) on the forwarded argv to
|
|
10
10
|
* `--host ssh://user@host`, the address grammar `secrets` speaks (PHNX-4090).
|
|
@@ -39,7 +39,7 @@ export function registerSecretsCommands(program) {
|
|
|
39
39
|
if (err instanceof SecretsClientError && err.code === 'SECRETS_BIN_MISSING') {
|
|
40
40
|
console.error(chalk.red('The standalone `secrets` CLI is not installed.'));
|
|
41
41
|
console.error(chalk.gray(`Install it, then re-run this command:`));
|
|
42
|
-
console.error(chalk.cyan(` ${
|
|
42
|
+
console.error(chalk.cyan(` ${SECRETS_CLI_INSTALL_HINT}`));
|
|
43
43
|
process.exit(1);
|
|
44
44
|
}
|
|
45
45
|
throw err;
|
package/dist/commands/send.d.ts
CHANGED
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Compat: positional text still works (`agents send "hi" --channel … --to …`).
|
|
12
12
|
*
|
|
13
|
-
* `
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* `agents
|
|
13
|
+
* `send --to owner` is the owner-delivery path; `agents feed post --level
|
|
14
|
+
* important` records a milestone and broadcasts it through the same sink.
|
|
15
|
+
* Not a second stack. Not agent control — use `agents message` /
|
|
16
|
+
* `agents sessions inject` for running agents.
|
|
17
17
|
*
|
|
18
18
|
* Feed / activity are a different plane (record + read); feed.broadcast may
|
|
19
19
|
* call this command as a forward sink.
|
package/dist/commands/send.js
CHANGED
|
@@ -9,7 +9,7 @@ function mergeAttachments(opts) {
|
|
|
9
9
|
const list = [...(opts.attach ?? []), ...(opts.attachment ?? [])];
|
|
10
10
|
return list.length ? list : undefined;
|
|
11
11
|
}
|
|
12
|
-
function toInput(positionalText, opts
|
|
12
|
+
function toInput(positionalText, opts) {
|
|
13
13
|
return {
|
|
14
14
|
text: opts.text,
|
|
15
15
|
positionalText,
|
|
@@ -20,18 +20,17 @@ function toInput(positionalText, opts, ownerMode) {
|
|
|
20
20
|
urls: opts.url,
|
|
21
21
|
from: opts.from,
|
|
22
22
|
dryRun: opts.dryRun,
|
|
23
|
-
ownerMode,
|
|
24
23
|
};
|
|
25
24
|
}
|
|
26
|
-
async function runSend(positionalText, opts
|
|
25
|
+
async function runSend(positionalText, opts) {
|
|
27
26
|
const meta = readMeta();
|
|
28
|
-
let input = toInput(positionalText, opts
|
|
29
|
-
// An owner-bound ping (`agents
|
|
27
|
+
let input = toInput(positionalText, opts);
|
|
28
|
+
// An owner-bound ping (`agents send --to owner`) goes through
|
|
30
29
|
// the SAME composer as an important `feed post` (PHNX-3698): short-shaped body,
|
|
31
30
|
// TEAM-N keys linkified, session crumb as a tappable console URL — instead of a
|
|
32
31
|
// raw dump. A non-owner send (explicit --channel/--to) is delivered verbatim.
|
|
33
32
|
let ownerCompose;
|
|
34
|
-
if (
|
|
33
|
+
if (isOwnerAlias(opts.to)) {
|
|
35
34
|
const flagged = opts.text?.trim() ?? '';
|
|
36
35
|
const positional = (positionalText ?? '').trim();
|
|
37
36
|
const raw = flagged || positional;
|
|
@@ -112,9 +111,6 @@ export function registerSendCommand(program) {
|
|
|
112
111
|
# Attach a local file
|
|
113
112
|
agents send --to owner --text "screenshot" --attach ./out/cover.png
|
|
114
113
|
|
|
115
|
-
# Owner alias without the notify verb
|
|
116
|
-
agents send --to owner --text "wiring test"
|
|
117
|
-
|
|
118
114
|
# Legacy positional text still works
|
|
119
115
|
agents send "hi" --channel desktop --to local
|
|
120
116
|
|
|
@@ -124,47 +120,6 @@ export function registerSendCommand(program) {
|
|
|
124
120
|
notes: SHARED_NOTES,
|
|
125
121
|
});
|
|
126
122
|
sendCmd.action(async (text, opts) => {
|
|
127
|
-
await runSend(text, opts
|
|
128
|
-
});
|
|
129
|
-
const notifyCmd = program
|
|
130
|
-
.command('notify [text]')
|
|
131
|
-
.description('[DEPRECATED] Deliver to the owner (alias of send --to owner). Use "agents feed post" for new code.')
|
|
132
|
-
.option('--text <text>', 'message body (preferred over positional text)')
|
|
133
|
-
.option('--channel <name>', 'override owner channel')
|
|
134
|
-
.option('--to <target>', 'override owner target (or pass a non-owner dest with --channel)')
|
|
135
|
-
.option('--thread <id>', 'channel thread id / timestamp')
|
|
136
|
-
.option('--attach <path...>', 'local file attachment path (repeatable)')
|
|
137
|
-
.option('--attachment <path...>', 'alias of --attach')
|
|
138
|
-
.option('--url <url...>', 'link or remote media URL to include in the body (repeatable)')
|
|
139
|
-
.option('--from <who>', 'sender label (mailbox)')
|
|
140
|
-
.option('--json', 'output JSON')
|
|
141
|
-
.option('--dry-run', 'resolve + build but do not send');
|
|
142
|
-
setHelpSections(notifyCmd, {
|
|
143
|
-
examples: `
|
|
144
|
-
# Deprecated: prefer "agents feed post --title \"…\" \"…\" --level important"
|
|
145
|
-
agents notify --text "Build finished — PR #1346 is green"
|
|
146
|
-
agents notify "legacy positional still works"
|
|
147
|
-
|
|
148
|
-
# Override channel for this one ping
|
|
149
|
-
agents notify --text "fallback" --channel desktop --to local
|
|
150
|
-
|
|
151
|
-
agents notify --text "probe" --dry-run --json
|
|
152
|
-
`,
|
|
153
|
-
notes: `
|
|
154
|
-
DEPRECATED. notify ≡ send --to owner and still works, but new callers
|
|
155
|
-
should use "agents feed post" (record + optional broadcast) instead.
|
|
156
|
-
Set owner.channels + owner.policy.normal in humans.yaml once per fleet.
|
|
157
|
-
Every channel listed in the normal policy receives an owner-addressed send.
|
|
158
|
-
|
|
159
|
-
Owner sends go through the same composer as "feed post": the body is
|
|
160
|
-
short-shaped, any TEAM-N key becomes a Linear URL, and the session crumb
|
|
161
|
-
becomes a tappable https://prix.dev/console/sessions/<id> link.
|
|
162
|
-
|
|
163
|
-
${SHARED_NOTES}
|
|
164
|
-
`,
|
|
165
|
-
});
|
|
166
|
-
notifyCmd.action(async (text, opts) => {
|
|
167
|
-
console.error(chalk.yellow('Warning: "agents notify" is deprecated. Use "agents feed post" for progress posts that can also reach the owner.'));
|
|
168
|
-
await runSend(text, opts, true);
|
|
123
|
+
await runSend(text, opts);
|
|
169
124
|
});
|
|
170
125
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// the same idempotent `deployWorker`/`createBucket` primitives. Provisioning is
|
|
11
11
|
// idempotent — re-running redeploys the current Worker template in place.
|
|
12
12
|
import chalk from 'chalk';
|
|
13
|
-
import { DEFAULT_CF_BUNDLE, readCloudflareCreds } from '../lib/
|
|
13
|
+
import { DEFAULT_CF_BUNDLE, readCloudflareCreds } from '../lib/cloudflare/creds.js';
|
|
14
14
|
import { PHOENIX_ID_BASE } from '../lib/identity/client.js';
|
|
15
15
|
import { provisionSessions } from '../lib/session/sync/provision.js';
|
|
16
16
|
import { DEFAULT_SESSIONS_BUCKET_NAME, DEFAULT_SESSIONS_DOMAIN, DEFAULT_SESSIONS_WORKER_NAME, } from '../lib/session/sync/managed-config.js';
|
|
@@ -153,6 +153,17 @@ export interface SessionPreviewDigest {
|
|
|
153
153
|
firstError?: string;
|
|
154
154
|
toolHistogram: ReturnType<typeof toolHistogram>;
|
|
155
155
|
test: ReturnType<typeof detectTestResult>;
|
|
156
|
+
/**
|
|
157
|
+
* True when this digest was built WITHOUT parsing the transcript (PHNX-3999):
|
|
158
|
+
* the file exceeded {@link PREVIEW_DIGEST_MAX_PARSE_BYTES} and no cached
|
|
159
|
+
* digest existed yet. `firstUser`/`lastAssistant` fall back to the already-
|
|
160
|
+
* indexed `SessionMeta.firstUserMessage`/`lastUserMessage` (cheap, no parse)
|
|
161
|
+
* rather than being silently empty; every event-derived field (toolCalls,
|
|
162
|
+
* changedFiles, artifacts, etc.) stays at its zero-value default because it
|
|
163
|
+
* genuinely was not computed, not because nothing happened.
|
|
164
|
+
*/
|
|
165
|
+
partial?: boolean;
|
|
166
|
+
partialReason?: string;
|
|
156
167
|
}
|
|
157
168
|
/** Fold a harness-normalized event stream into the stable preview data model. */
|
|
158
169
|
export declare function buildSessionPreviewDigest(events: SessionEvent[], session: SessionMeta): SessionPreviewDigest;
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import chalk from 'chalk';
|
|
11
|
-
import { truncate, humanDuration } from '../lib/format.js';
|
|
11
|
+
import { truncate, humanDuration, formatBytes } from '../lib/format.js';
|
|
12
12
|
import { sessionDisplayAgent } from '../lib/session/types.js';
|
|
13
13
|
import { fetchPeerPreviewDigest } from '../lib/session/remote-list.js';
|
|
14
14
|
import { parseSession, sanitizeForTerminal, SNAPSHOT_TODO_TOOLS } from '../lib/session/parse.js';
|
|
15
|
+
import { readSessionTail, readSessionHead } from '../lib/session/tail.js';
|
|
15
16
|
import { safeTeamText } from '../lib/session/team-filter.js';
|
|
16
|
-
import { cleanSessionPrompt, extractSessionTopic, isSyntheticUserMessage } from '../lib/session/prompt.js';
|
|
17
|
+
import { cleanSessionPrompt, extractSessionTopic, isSyntheticUserMessage, firstUserMessageFromEvents } from '../lib/session/prompt.js';
|
|
17
18
|
import { linkPath, linkUrl, relativeToCwd, shortenModel } from '../lib/session/render.js';
|
|
18
19
|
import { linearIssueUrl } from '../lib/session/linear.js';
|
|
19
20
|
import { extractTodoProgress, WORKTREE_RE } from '../lib/session/state.js';
|
|
@@ -324,7 +325,14 @@ export function loadSessionPreviewDigest(session) {
|
|
|
324
325
|
archived.plugins = getSessionPlugins(session.id);
|
|
325
326
|
return { digest: archived, events: [] };
|
|
326
327
|
}
|
|
327
|
-
|
|
328
|
+
// No file on disk AND no archived digest: a metadata-only row (a Rush
|
|
329
|
+
// dispatch/audit row, a synthesized attach-only entry, a live session not
|
|
330
|
+
// yet indexed) with nothing to read (PHNX-3999). Callers that only
|
|
331
|
+
// destructure `digest` (the picker's "not indexed here" note) are
|
|
332
|
+
// unaffected; a JSON caller reading `error` gets a truthful reason instead
|
|
333
|
+
// of a `preview: null` that looks identical to "this session genuinely has
|
|
334
|
+
// no content yet".
|
|
335
|
+
return { events: [], error: 'no local transcript for this session (metadata-only entry, no archived digest)' };
|
|
328
336
|
}
|
|
329
337
|
const safe = sanitizeMeta(session);
|
|
330
338
|
let events = [];
|
|
@@ -340,9 +348,53 @@ export function loadSessionPreviewDigest(session) {
|
|
|
340
348
|
fileSize: sourceStamp.size,
|
|
341
349
|
});
|
|
342
350
|
if (!digest) {
|
|
343
|
-
|
|
344
|
-
|
|
351
|
+
if (sourceStamp.size > PREVIEW_DIGEST_MAX_PARSE_BYTES) {
|
|
352
|
+
// A full `parseSession` on a cache miss is a synchronous, unbounded
|
|
353
|
+
// whole-file parse with no time/byte cap of its own (PHNX-3999) — real
|
|
354
|
+
// 6.3 MiB and 35.7 MiB screenshot-heavy transcripts on this fleet both
|
|
355
|
+
// lacked a computed digest/timeline, consistent with this path not
|
|
356
|
+
// finishing in a reasonable request budget for either. `PREVIEW_DIGEST_MAX_PARSE_BYTES`
|
|
357
|
+
// is deliberately smaller than the daemon's own background-work ceiling
|
|
358
|
+
// (see that constant's own doc) so this catches both real cases.
|
|
359
|
+
//
|
|
360
|
+
// This IS genuinely partial, not empty: rather than parsing nothing,
|
|
361
|
+
// `readSessionTail` (`tail.ts`) reads only the LAST 128 KiB of the file
|
|
362
|
+
// (already the live-view's own bounded reader, reused verbatim — no new
|
|
363
|
+
// parse logic) for a real recent-events window on the two harnesses it
|
|
364
|
+
// supports (Claude/Codex); event-derived fields below (toolCalls,
|
|
365
|
+
// toolTags, etc.) reflect that tail window, not the whole session, which
|
|
366
|
+
// `partialReason` states explicitly.
|
|
367
|
+
//
|
|
368
|
+
// `firstUser` is NEVER set from the tail fold: a tail window's "first
|
|
369
|
+
// user message IN THAT WINDOW" is a mid-session follow-up on any
|
|
370
|
+
// multi-turn session, not the session's actual original request, and
|
|
371
|
+
// there is no honest way to tell the two apart from the tail alone.
|
|
372
|
+
// Falling back to it would silently mislabel a follow-up as the
|
|
373
|
+
// original ask. Preference order for the CANONICAL original request:
|
|
374
|
+
// (1) the already-indexed `SessionMeta.firstUserMessage` (zero
|
|
375
|
+
// extra I/O); (2) failing that, a bounded HEAD read (`readSessionHead`,
|
|
376
|
+
// `tail.ts` — the mirror of the tail reader, first ~32 KiB from byte 0,
|
|
377
|
+
// where a session's opening turn always lives) so a row with no indexed
|
|
378
|
+
// value yet still gets the REAL original request rather than nothing.
|
|
379
|
+
// Only when neither is available does `firstUser` stay empty —
|
|
380
|
+
// `partial`/`partialReason` already say why detail is missing, which is
|
|
381
|
+
// the truthful signal, never a guessed value. The digest is cached
|
|
382
|
+
// against this stamp so the bound is paid once per transcript version,
|
|
383
|
+
// not once per call.
|
|
384
|
+
events = readSessionTail(session.filePath, session.agent);
|
|
345
385
|
digest = buildSessionPreviewDigest(events, safe);
|
|
386
|
+
if (session.firstUserMessage) {
|
|
387
|
+
digest.firstUser = session.firstUserMessage;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// Reuse the canonical extractor (rejects synthetic/system-injected
|
|
391
|
+
// turns, unwraps a Grok/Cursor <user_query> wrapper) rather than a
|
|
392
|
+
// bespoke inline find — the same rules SessionMeta.firstUserMessage
|
|
393
|
+
// itself was built with.
|
|
394
|
+
digest.firstUser = firstUserMessageFromEvents(readSessionHead(session.filePath, session.agent)) ?? '';
|
|
395
|
+
}
|
|
396
|
+
digest.partial = true;
|
|
397
|
+
digest.partialReason = `transcript is ${formatBytes(sourceStamp.size)}, over the ${formatBytes(PREVIEW_DIGEST_MAX_PARSE_BYTES)} bounded-parse limit for an uncached preview; digest reflects only the last ~128 KiB (tail) of the transcript, not the whole session`;
|
|
346
398
|
writeSessionPreviewCache({
|
|
347
399
|
id: session.id,
|
|
348
400
|
fileMtimeMs: sourceStamp.mtimeMs,
|
|
@@ -350,8 +402,20 @@ export function loadSessionPreviewDigest(session) {
|
|
|
350
402
|
preview: digest,
|
|
351
403
|
});
|
|
352
404
|
}
|
|
353
|
-
|
|
354
|
-
|
|
405
|
+
else {
|
|
406
|
+
try {
|
|
407
|
+
events = parseSession(session.filePath, session.agent);
|
|
408
|
+
digest = buildSessionPreviewDigest(events, safe);
|
|
409
|
+
writeSessionPreviewCache({
|
|
410
|
+
id: session.id,
|
|
411
|
+
fileMtimeMs: sourceStamp.mtimeMs,
|
|
412
|
+
fileSize: sourceStamp.size,
|
|
413
|
+
preview: digest,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
catch (err) {
|
|
417
|
+
return { events, error: sanitizeForTerminal(err?.message ?? String(err)) };
|
|
418
|
+
}
|
|
355
419
|
}
|
|
356
420
|
}
|
|
357
421
|
digest.plugins = getSessionPlugins(session.id);
|
|
@@ -711,6 +775,23 @@ const DIRS_TOUCHED_MAX = 5;
|
|
|
711
775
|
// enough to cover any real session's edits, low enough that a runaway rewrite
|
|
712
776
|
// can't bloat the cached JSON. The `changes` counts stay the true totals.
|
|
713
777
|
const CHANGED_FILES_MAX = 200;
|
|
778
|
+
/**
|
|
779
|
+
* Bound for an uncached `loadSessionPreviewDigest` parse (PHNX-3999).
|
|
780
|
+
*
|
|
781
|
+
* Deliberately SMALLER than the daemon's own
|
|
782
|
+
* `TIMELINE_PASS_MAX_WHOLE_FILE_BYTES` (16 MiB, `timeline-pass.ts`) — that
|
|
783
|
+
* number bounds BACKGROUND work the daemon tick can afford to spend; this one
|
|
784
|
+
* bounds a SYNCHRONOUS request an interactive caller (the Menu, with its own
|
|
785
|
+
* end-to-end latency budget) is actively waiting on. Two real screenshot-heavy
|
|
786
|
+
* transcripts on this fleet (6.3 MiB and 35.7 MiB) both lacked any computed
|
|
787
|
+
* digest/timeline — a 16 MiB threshold here would still miss the smaller one.
|
|
788
|
+
* The exact per-byte cost of `parseSession` is not measured in this change;
|
|
789
|
+
* 4 MiB is a conservative invariant (well under the 6.3 MiB failure case,
|
|
790
|
+
* comfortably above ordinary non-screenshot transcript sizes), not a timing
|
|
791
|
+
* guarantee — verify against real transcripts before relying on a specific
|
|
792
|
+
* elapsed-time bound.
|
|
793
|
+
*/
|
|
794
|
+
const PREVIEW_DIGEST_MAX_PARSE_BYTES = 4 * 1024 * 1024;
|
|
714
795
|
/** Fold a harness-normalized event stream into the stable preview data model. */
|
|
715
796
|
export function buildSessionPreviewDigest(events, session) {
|
|
716
797
|
let firstUser = '';
|
|
@@ -95,7 +95,6 @@ export function registerSessionsResumeCommand(sessionsCmd) {
|
|
|
95
95
|
- Attach a live pane without forking: agents sessions focus <id>.
|
|
96
96
|
- This is the ONE verb for getting back in. It detects the state: a live tmux pane is attached, a headless session comes to the foreground, an ended one recovers on its owning device.
|
|
97
97
|
- A UUID/prefix or ag-<agent>-<suffix> alias bypasses the picker. A live alias attaches by name even when the session index cannot attribute it.
|
|
98
|
-
- Retired spellings still work for one release and print the replacement: sessions attach, sessions go, reconnect.
|
|
99
98
|
- Going the other way (foreground -> background) is 'agents sessions detach <id>'.
|
|
100
99
|
- With no identity selector, space toggles a session, enter confirms, and tab toggles the preview pane.
|
|
101
100
|
- Layout: one tab per session by default. --splits packs session pairs side by side in each tab.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import type { SessionMeta } from '../lib/session/types.js';
|
|
3
|
-
import { type PublishOptions } from '../lib/share/publish.js';
|
|
4
3
|
interface ShareOptions {
|
|
5
4
|
public?: boolean;
|
|
6
5
|
slug?: string;
|
|
@@ -13,13 +12,12 @@ interface ShareOptions {
|
|
|
13
12
|
/** `session-<shortId>` — stable per session, so re-sharing updates the same URL. */
|
|
14
13
|
export declare function defaultSessionSlug(session: SessionMeta): string;
|
|
15
14
|
/**
|
|
16
|
-
*
|
|
15
|
+
* Build the `artifacts share` argv for a session publish.
|
|
17
16
|
*
|
|
18
|
-
* Extracted so the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* flipped.
|
|
17
|
+
* Extracted so the mapping is testable directly. The security-relevant default
|
|
18
|
+
* is `--visibility unlisted` — it inverts `artifacts share`'s public default, and
|
|
19
|
+
* a test that re-implements this mapping would still pass with it flipped.
|
|
22
20
|
*/
|
|
23
|
-
export declare function
|
|
21
|
+
export declare function buildArtifactsShareArgs(session: SessionMeta, options: Pick<ShareOptions, 'public' | 'slug' | 'label' | 'expire' | 'force' | 'cover'>, file: string): string[];
|
|
24
22
|
export declare function registerSessionsShareCommand(sessionsCmd: Command): void;
|
|
25
23
|
export {};
|
|
@@ -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 {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { type Command } from 'commander';
|
|
2
2
|
import { type ProjectDef } from '../lib/projects.js';
|
|
3
|
-
import type { SessionAgentId, SessionMeta, ViewMode } from '../lib/session/types.js';
|
|
3
|
+
import type { SessionAgentId, SessionEvent, SessionMeta, ViewMode } from '../lib/session/types.js';
|
|
4
4
|
import { type ActiveSession } from '../lib/session/active.js';
|
|
5
5
|
export { activeSessionProjectKey, backfillActiveRowsFromIndex, backfillActiveRowsFromMeta, isRunningLiveSession, serializeActiveSessionsForJson, serializeSessionsJson, type BackfillMeta } from '../lib/session/active.js';
|
|
6
6
|
import { loadLocalActiveSessions } from '../lib/session/session-cache.js';
|
|
7
7
|
import { gatherRemoteList, runOnPeer } from '../lib/session/remote-list.js';
|
|
8
8
|
import { type RemoteAgentsJsonParseResult } from '../lib/remote-agents-json.js';
|
|
9
9
|
import { type RunMeta } from '../lib/scheduling/routines.js';
|
|
10
|
-
import { type PickedSession } from './sessions-picker.js';
|
|
10
|
+
import { type PickedSession, type SessionPreviewDigest } from './sessions-picker.js';
|
|
11
11
|
import { type ComputerRunRow } from '../lib/computer/sessions-list.js';
|
|
12
12
|
import { type ToolSearchEnvelope, type ToolProgramCountEnvelope } from '../lib/session/tool-index.js';
|
|
13
13
|
interface SessionFilterOptions {
|
|
@@ -466,6 +466,23 @@ export declare function printRoutineDrilldown(drill: RoutineDrilldown, liveIndex
|
|
|
466
466
|
hiddenCount?: number;
|
|
467
467
|
hiddenUnmanaged?: number;
|
|
468
468
|
}): void;
|
|
469
|
+
export interface SessionDetailMessage {
|
|
470
|
+
role: 'user' | 'assistant';
|
|
471
|
+
text: string;
|
|
472
|
+
at: string | null;
|
|
473
|
+
}
|
|
474
|
+
export declare function buildSessionDetailBlock(session: SessionMeta, digest: SessionPreviewDigest | undefined, events: SessionEvent[], sourceStamp?: {
|
|
475
|
+
fileMtimeMs: number;
|
|
476
|
+
fileSize: number;
|
|
477
|
+
}): {
|
|
478
|
+
request: unknown;
|
|
479
|
+
timeline: unknown;
|
|
480
|
+
files: unknown;
|
|
481
|
+
messages: SessionDetailMessage[];
|
|
482
|
+
sourceRevision: string | null;
|
|
483
|
+
partial: boolean;
|
|
484
|
+
reason: string | null;
|
|
485
|
+
};
|
|
469
486
|
/** Resolve a session by id/query globally and print its compact preview (no pager).
|
|
470
487
|
* Backs `--preview` — the fast path for the "peek before resume" hot loop. */
|
|
471
488
|
export declare function renderSessionPreview(query: string, scope: {
|
|
@@ -474,6 +491,8 @@ export declare function renderSessionPreview(query: string, scope: {
|
|
|
474
491
|
local?: boolean;
|
|
475
492
|
hosts?: string[];
|
|
476
493
|
json?: boolean;
|
|
494
|
+
refresh?: boolean;
|
|
495
|
+
revision?: string;
|
|
477
496
|
}): Promise<void>;
|
|
478
497
|
/**
|
|
479
498
|
* The one-line live status banner shown above a session preview: the glyph, the
|