@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
|
@@ -35,7 +35,9 @@ import { gatherRemoteAgentsJson } from '../lib/remote-agents-json.js';
|
|
|
35
35
|
import { stringWidth, truncateToWidth, padToWidth, terminalWidth } from '../lib/session/width.js';
|
|
36
36
|
import { inferSessionState } from '../lib/session/state.js';
|
|
37
37
|
import { discoverSessions, queryIndexedSessions, countSessionsInScope, resolveSessionById, isCompleteSessionId, looksLikeSessionId, searchContentIndex, getSessionRoots, scopeToManaged } from '../lib/session/discover.js';
|
|
38
|
-
import { findSessionsById, querySessions, readSessionContent, readArchivedSessionPreview } from '../lib/session/db.js';
|
|
38
|
+
import { findSessionsById, querySessions, readSessionContent, readArchivedSessionPreview, readSessionTimelineAny } from '../lib/session/db.js';
|
|
39
|
+
import { foldTimeline, emptyTimelineState, projectTimeline, projectSessionFiles } from '../lib/session/timeline.js';
|
|
40
|
+
import { readSessionTail } from '../lib/session/tail.js';
|
|
39
41
|
import { liveSessionMetas, fleetExecutionMachineById, reconcileLiveMetaMachine } from '../lib/session/live-metadata.js';
|
|
40
42
|
import { sessionHeadline } from '../lib/session/title.js';
|
|
41
43
|
import { filterTeamSessions, shouldShowTeamSessions, safeTeamText, groupSessionsByTeam, NO_TEAM_GROUP_KEY, } from '../lib/session/team-filter.js';
|
|
@@ -64,12 +66,9 @@ import { registerSessionsResumeCommand } from './sessions-resume.js';
|
|
|
64
66
|
import { registerSessionsForkCommand } from './fork.js';
|
|
65
67
|
import { registerSessionsBookmarkCommand } from './sessions-bookmark.js';
|
|
66
68
|
import { isBookmarked, listBookmarks } from '../lib/session/bookmarks.js';
|
|
67
|
-
import { registerGoCommand } from './go.js';
|
|
68
69
|
import { registerFocusCommand } from './focus.js';
|
|
69
|
-
import { registerReconnectCommand } from './reconnect.js';
|
|
70
70
|
import { registerDetachCommand } from './detach.js';
|
|
71
71
|
import { registerSessionsStopCommand } from './sessions-stop.js';
|
|
72
|
-
import { registerAttachCommand } from './attach.js';
|
|
73
72
|
import { registerSessionsInjectCommand } from './sessions-inject.js';
|
|
74
73
|
import { registerSessionsExportCommand } from './sessions-export.js';
|
|
75
74
|
import { registerSessionsRenderCommand } from './sessions-render.js';
|
|
@@ -1985,9 +1984,151 @@ function canonicalSessionsCommand(query, options) {
|
|
|
1985
1984
|
a.push(JSON.stringify(q));
|
|
1986
1985
|
return 'ag ' + a.join(' ');
|
|
1987
1986
|
}
|
|
1987
|
+
/** Bound on the `details.messages` array and on each message's `text` — this
|
|
1988
|
+
* rides a cached JSON payload (and, for the remote fast path, an SSH capture
|
|
1989
|
+
* with its own byte cap), so it is capped independently of either. */
|
|
1990
|
+
const SESSION_DETAIL_MAX_MESSAGES = 8;
|
|
1991
|
+
const SESSION_DETAIL_MESSAGE_MAX_CHARS = 4_000;
|
|
1992
|
+
/**
|
|
1993
|
+
* The additive `details` block on `sessions preview --json` (PHNX-3999):
|
|
1994
|
+
* `request`/`timeline`/`files` are read FIRST from the existing
|
|
1995
|
+
* `session_timelines` projection (`readSessionTimelineAny` — the daemon's own
|
|
1996
|
+
* bounded, incrementally-folded cache, `SessionTimelineProjection` in
|
|
1997
|
+
* `lib/session/db.ts`), so a session the daemon has already reached costs
|
|
1998
|
+
* nothing extra here. When that projection is missing (daemon hasn't reached
|
|
1999
|
+
* this session yet), this does an ON-DEMAND fold using the EXACT same pure,
|
|
2000
|
+
* already-redacting pipeline the daemon uses (`foldTimeline` /
|
|
2001
|
+
* `projectTimeline` / `projectSessionFiles`, `timeline.ts`) over whatever
|
|
2002
|
+
* events are cheaply available: the fresh-parse `events` from
|
|
2003
|
+
* `loadSessionPreviewDigest` when there is one, else a bounded 128 KiB tail
|
|
2004
|
+
* read (`readSessionTail`, Claude/Codex only — the same bounded reader the
|
|
2005
|
+
* local preview's over-cap fallback uses) so a WARM cache hit (no fresh parse)
|
|
2006
|
+
* still gets real, on-demand content instead of falling back to a 2-message
|
|
2007
|
+
* digest summary. This is genuinely bounded either way: it folds only events
|
|
2008
|
+
* already in hand or a fixed 128 KiB window, never a fresh whole-file parse.
|
|
2009
|
+
* `partial`/`reason` say when this happened (`daemon-pending: on-demand
|
|
2010
|
+
* bounded fold`) versus a session with no transcript at all.
|
|
2011
|
+
*/
|
|
2012
|
+
function sessionTranscriptStamp(session) {
|
|
2013
|
+
try {
|
|
2014
|
+
if (!session.filePath)
|
|
2015
|
+
return undefined;
|
|
2016
|
+
const stat = fs.statSync(session.filePath);
|
|
2017
|
+
return { fileMtimeMs: stat.mtimeMs, fileSize: stat.size };
|
|
2018
|
+
}
|
|
2019
|
+
catch {
|
|
2020
|
+
return undefined;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
export function buildSessionDetailBlock(session, digest, events, sourceStamp) {
|
|
2024
|
+
const bound = (text) => redactSecrets(sanitizeForTerminal(text)).slice(0, SESSION_DETAIL_MESSAGE_MAX_CHARS);
|
|
2025
|
+
const stamp = sourceStamp ?? sessionTranscriptStamp(session);
|
|
2026
|
+
const canDateEvents = sourceStamp !== undefined || events.length === 0;
|
|
2027
|
+
const daemonProjection = stamp ? readSessionTimelineAny(session.id, stamp) : undefined;
|
|
2028
|
+
// A bounded tail read backs `messages` on EVERY warm cache hit (no fresh
|
|
2029
|
+
// parse this call), REGARDLESS of whether the daemon projection is already
|
|
2030
|
+
// present — this is what fixes a real bug review caught: a warm digest hit
|
|
2031
|
+
// whose daemon timeline HAD landed still fell through to the 2-message
|
|
2032
|
+
// digest summary below, because the tail-read used to be gated on the
|
|
2033
|
+
// on-demand-fold branch only. `messages` and the on-demand fold are two
|
|
2034
|
+
// independent uses of the same cheap, bounded events window.
|
|
2035
|
+
let foldEvents = events;
|
|
2036
|
+
if (foldEvents.length === 0 && session.filePath) {
|
|
2037
|
+
foldEvents = readSessionTail(session.filePath, session.agent);
|
|
2038
|
+
}
|
|
2039
|
+
let projection = daemonProjection;
|
|
2040
|
+
let onDemand = false;
|
|
2041
|
+
if (!projection && foldEvents.length > 0) {
|
|
2042
|
+
const state = foldTimeline(foldEvents, emptyTimelineState());
|
|
2043
|
+
projection = {
|
|
2044
|
+
request: state.request,
|
|
2045
|
+
timeline: projectTimeline(state, undefined),
|
|
2046
|
+
files: projectSessionFiles(state),
|
|
2047
|
+
};
|
|
2048
|
+
onDemand = true;
|
|
2049
|
+
}
|
|
2050
|
+
let messages;
|
|
2051
|
+
if (foldEvents.length > 0) {
|
|
2052
|
+
messages = foldEvents
|
|
2053
|
+
.filter((e) => e.type === 'message' && !e._synthetic && Boolean(e.content) && (e.role === 'user' || e.role === 'assistant'))
|
|
2054
|
+
.slice(-SESSION_DETAIL_MAX_MESSAGES)
|
|
2055
|
+
.map(e => ({ role: e.role, text: bound(e.content), at: e.timestamp ?? null }));
|
|
2056
|
+
}
|
|
2057
|
+
else {
|
|
2058
|
+
messages = [];
|
|
2059
|
+
if (digest?.firstUser)
|
|
2060
|
+
messages.push({ role: 'user', text: bound(digest.firstUser), at: session.timestamp ?? null });
|
|
2061
|
+
if (digest?.lastAssistant)
|
|
2062
|
+
messages.push({ role: 'assistant', text: bound(digest.lastAssistant), at: session.lastActivity ?? null });
|
|
2063
|
+
}
|
|
2064
|
+
const endStamp = sessionTranscriptStamp(session);
|
|
2065
|
+
const unchanged = canDateEvents && stamp !== undefined && endStamp !== undefined
|
|
2066
|
+
&& stamp.fileMtimeMs === endStamp.fileMtimeMs && stamp.fileSize === endStamp.fileSize;
|
|
2067
|
+
const partial = Boolean(digest?.partial) || !daemonProjection || !unchanged;
|
|
2068
|
+
const reason = digest?.partialReason
|
|
2069
|
+
?? (onDemand
|
|
2070
|
+
? 'background timeline pass has not reached this session yet; request/timeline/files below are an on-demand bounded fold, not the full-history daemon projection'
|
|
2071
|
+
: (!projection ? 'no transcript available to fold request/timeline/files for this session' : null));
|
|
2072
|
+
return {
|
|
2073
|
+
request: projection?.request ?? null,
|
|
2074
|
+
timeline: projection?.timeline ?? null,
|
|
2075
|
+
files: projection?.files ?? null,
|
|
2076
|
+
messages,
|
|
2077
|
+
sourceRevision: unchanged ? new Date(stamp.fileMtimeMs).toISOString() : null,
|
|
2078
|
+
partial,
|
|
2079
|
+
reason,
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
1988
2082
|
/** Resolve a session by id/query globally and print its compact preview (no pager).
|
|
1989
2083
|
* Backs `--preview` — the fast path for the "peek before resume" hot loop. */
|
|
1990
2084
|
export async function renderSessionPreview(query, scope) {
|
|
2085
|
+
// Exact ID + exactly one named, non-local device: the canonical bounded
|
|
2086
|
+
// preview loader (PHNX-3999) answers in ONE ssh hop plus a durable local
|
|
2087
|
+
// cache, instead of the general resolver's metadata fan-out followed by a
|
|
2088
|
+
// second render hop. Scoped tightly on purpose — a short id/label is not a
|
|
2089
|
+
// stable cache key across devices, and this never touches local discovery.
|
|
2090
|
+
//
|
|
2091
|
+
// Gated on `isCompleteSessionId`, NOT a bare-UUID regex: measured over this
|
|
2092
|
+
// fleet's own index, a "full session id" is a bare UUID for most harnesses
|
|
2093
|
+
// but `session_<uuid>` for kimi/rush and `ses_<ulid>` for opencode
|
|
2094
|
+
// (discover.ts). A bare-UUID-only check would silently skip this fast path
|
|
2095
|
+
// (falling through to the slower general resolver, never wrong, but wrong
|
|
2096
|
+
// to claim as "the fast path" for those harnesses) for a real, exact,
|
|
2097
|
+
// caller-supplied full id on those three.
|
|
2098
|
+
if (scope.json && !scope.local && scope.hosts?.length === 1
|
|
2099
|
+
&& scope.hosts[0] !== machineId() && isCompleteSessionId(query.trim())) {
|
|
2100
|
+
const { getRemoteSessionPreview } = await import('../lib/session/remote-preview-cache.js');
|
|
2101
|
+
const result = await getRemoteSessionPreview(query.trim(), scope.hosts[0], {
|
|
2102
|
+
refresh: scope.refresh,
|
|
2103
|
+
revision: scope.revision,
|
|
2104
|
+
});
|
|
2105
|
+
const envelope = result.envelope;
|
|
2106
|
+
console.log(JSON.stringify({
|
|
2107
|
+
schemaVersion: 1,
|
|
2108
|
+
session: envelope?.session ?? null,
|
|
2109
|
+
// `active` is a live snapshot the PEER computed at the moment it ran
|
|
2110
|
+
// `sessions preview --local --json` — meaningful only for that instant.
|
|
2111
|
+
// When this response is served from the durable cache (`cache.source ===
|
|
2112
|
+
// 'cache'`, which can be up to 45s old, or indefinitely old under a
|
|
2113
|
+
// matched --revision), presenting that stale snapshot under the same
|
|
2114
|
+
// `active` key would read as the session's CURRENT working/idle status
|
|
2115
|
+
// when it may no longer be. Null it out rather than relabel it with an
|
|
2116
|
+
// age stamp: the caller already has `cache.fetchedAt`/`cache.stale` to
|
|
2117
|
+
// reason about staleness, and getting a fresh `active` read costs
|
|
2118
|
+
// another SSH round trip this fast path exists specifically to avoid.
|
|
2119
|
+
active: result.cache.source === 'live' ? (envelope?.active ?? null) : null,
|
|
2120
|
+
preview: envelope?.preview ?? null,
|
|
2121
|
+
error: envelope?.error ?? (envelope ? null : result.cache.reason),
|
|
2122
|
+
// The peer's own `sessions preview --local --json` already computed
|
|
2123
|
+
// `details` (request/timeline/files/messages) with its own bounded reader
|
|
2124
|
+
// (see buildSessionDetailBlock) — this hop just forwards it verbatim,
|
|
2125
|
+
// never re-derives it, so a metadata-only/no-transcript peer row's
|
|
2126
|
+
// explicit unavailable reason survives the hop unchanged.
|
|
2127
|
+
details: envelope?.details ?? null,
|
|
2128
|
+
cache: result.cache,
|
|
2129
|
+
}));
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
1991
2132
|
let outcome = await resolveSessionMetadataValue(query, scope);
|
|
1992
2133
|
// resolveSessionMetadataValue already consults the live registry for a running
|
|
1993
2134
|
// session with no transcript row (RUSH-2682), so a live session resolves above.
|
|
@@ -2062,7 +2203,8 @@ export async function renderSessionPreview(query, scope) {
|
|
|
2062
2203
|
}
|
|
2063
2204
|
catch { /* plain preview on any probe failure */ }
|
|
2064
2205
|
if (scope.json) {
|
|
2065
|
-
const
|
|
2206
|
+
const sourceStamp = sessionTranscriptStamp(session);
|
|
2207
|
+
const { digest, error, events } = loadSessionPreviewDigest(session);
|
|
2066
2208
|
console.log(JSON.stringify({
|
|
2067
2209
|
schemaVersion: 1,
|
|
2068
2210
|
session: {
|
|
@@ -2098,6 +2240,7 @@ export async function renderSessionPreview(query, scope) {
|
|
|
2098
2240
|
} : null,
|
|
2099
2241
|
preview: digest ?? null,
|
|
2100
2242
|
error: error ?? null,
|
|
2243
|
+
details: buildSessionDetailBlock(session, digest, events, sourceStamp),
|
|
2101
2244
|
}));
|
|
2102
2245
|
return;
|
|
2103
2246
|
}
|
|
@@ -3963,7 +4106,7 @@ async function resumeOnOwnerIfRemote(session) {
|
|
|
3963
4106
|
export async function resumeSessionInPlace(session) {
|
|
3964
4107
|
// This function is the LOCAL takeover, and every caller is responsible for
|
|
3965
4108
|
// routing a peer-owned session before it gets here (the picker above,
|
|
3966
|
-
// `agents sessions resume
|
|
4109
|
+
// `agents sessions resume`). Reaching it with one anyway means a
|
|
3967
4110
|
// caller skipped that step, so refuse rather than start the harness against
|
|
3968
4111
|
// state this box does not have — the `fs.existsSync(session.cwd)` fallback
|
|
3969
4112
|
// just below is exactly how that used to pass unnoticed, quietly swapping in
|
|
@@ -5317,7 +5460,6 @@ export function registerSessionsCommands(program) {
|
|
|
5317
5460
|
resume <id> --attach-only attach only; never fork a copy
|
|
5318
5461
|
resume multi-select history -> open tabs
|
|
5319
5462
|
detach <id> the other direction: interactive -> headless
|
|
5320
|
-
(retired, still working for one release: sessions attach, sessions go, reconnect)
|
|
5321
5463
|
- 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
5464
|
- --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
5465
|
- --version <version> requires --agent and is equivalent to --agent <agent@version>.
|
|
@@ -5374,7 +5516,9 @@ export function registerSessionsCommands(program) {
|
|
|
5374
5516
|
.option('-p, --project <name>', 'Narrow the ID to one project')
|
|
5375
5517
|
.option('--local', 'Only this machine; do not resolve the ID across the fleet')
|
|
5376
5518
|
.option('-D, --device <target...>', 'Resolve only on the named device(s)')
|
|
5377
|
-
.option('--json', 'Output the session preview as JSON')
|
|
5519
|
+
.option('--json', 'Output the session preview as JSON')
|
|
5520
|
+
.option('--refresh', 'Bypass the durable remote-preview cache and negative backoff for one bounded fetch (full ID + single --device only)')
|
|
5521
|
+
.option('--revision <cursor>', 'Opaque caller-owned activity cursor (any stable value YOU track, e.g. your own feed\'s lastActivityMs) -- passing the SAME value as your last call confirms nothing changed and serves the cache with zero SSH indefinitely; a different value fetches once, still subject to backoff unless --refresh is also set (full ID + single --device only)');
|
|
5378
5522
|
setHelpSections(previewCmd, {
|
|
5379
5523
|
examples: `
|
|
5380
5524
|
# Preview by the 8-character ID shown in agents sessions
|
|
@@ -5386,11 +5530,15 @@ export function registerSessionsCommands(program) {
|
|
|
5386
5530
|
# Stay on this machine or restrict the authoritative lookup to one peer
|
|
5387
5531
|
agents sessions preview 407b8dd5 --local
|
|
5388
5532
|
agents sessions preview 407b8dd5 --device zion
|
|
5533
|
+
|
|
5534
|
+
# Full ID + one --device: durable-cached fast path (PHNX-3999); force a fresh fetch
|
|
5535
|
+
agents sessions preview c70ecdea-6210-4039-9845-246a3a7a9942 --device zion --json --refresh
|
|
5389
5536
|
`,
|
|
5390
5537
|
notes: `
|
|
5391
5538
|
- Full UUIDs are globally unique and may stop the fleet lookup at the first exact hit.
|
|
5392
5539
|
- Short IDs wait for every selected device so ambiguity is never hidden.
|
|
5393
5540
|
- Active status is refreshed through the bounded live-state TTL; transcript-derived details use the durable session index.
|
|
5541
|
+
- A full UUID with exactly one --device and --json is served from a local durable cache (~45s fresh window); the JSON envelope's "cache" field reports fresh/stale/offline state. --refresh forces one bounded re-fetch.
|
|
5394
5542
|
`,
|
|
5395
5543
|
});
|
|
5396
5544
|
previewCmd.action(async (id) => {
|
|
@@ -5402,18 +5550,17 @@ export function registerSessionsCommands(program) {
|
|
|
5402
5550
|
local: options.local,
|
|
5403
5551
|
hosts: hosts.length > 0 ? hosts : undefined,
|
|
5404
5552
|
json: options.json,
|
|
5553
|
+
refresh: options.refresh,
|
|
5554
|
+
revision: options.revision,
|
|
5405
5555
|
});
|
|
5406
5556
|
});
|
|
5407
5557
|
registerSessionsTailCommand(sessionsCmd);
|
|
5408
5558
|
registerSessionsResumeCommand(sessionsCmd);
|
|
5409
5559
|
registerSessionsForkCommand(sessionsCmd);
|
|
5410
5560
|
registerSessionsBookmarkCommand(sessionsCmd);
|
|
5411
|
-
registerGoCommand(sessionsCmd);
|
|
5412
5561
|
registerFocusCommand(sessionsCmd);
|
|
5413
|
-
registerReconnectCommand(sessionsCmd);
|
|
5414
5562
|
registerDetachCommand(sessionsCmd);
|
|
5415
5563
|
registerSessionsStopCommand(sessionsCmd);
|
|
5416
|
-
registerAttachCommand(sessionsCmd);
|
|
5417
5564
|
registerSessionsInjectCommand(sessionsCmd);
|
|
5418
5565
|
registerSessionsExportCommand(sessionsCmd);
|
|
5419
5566
|
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)) {
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* `npm i -g`. Users do not set extra env vars.
|
|
9
9
|
*/
|
|
10
10
|
import type { Command } from 'commander';
|
|
11
|
-
export declare const SECRETS_CLI_PACKAGE = "@phnx-labs/secrets-cli@0.1.
|
|
12
|
-
export declare const INSTALL_HINT = "agents clis install secrets # or: npm i -g @phnx-labs/secrets-cli@0.1.
|
|
11
|
+
export declare const SECRETS_CLI_PACKAGE = "@phnx-labs/secrets-cli@0.1.5";
|
|
12
|
+
export declare const INSTALL_HINT = "agents clis install secrets # or: npm i -g @phnx-labs/secrets-cli@0.1.5";
|
|
13
13
|
export declare function setupSecretsPrefsPath(): string;
|
|
14
14
|
/** True when the standalone `secrets` executable resolves ($SECRETS_BIN or PATH). */
|
|
15
15
|
export declare function isSecretsCliInstalled(): boolean;
|
|
@@ -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,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents setup term` — install the standalone `term` CLI if missing (PHNX-4092).
|
|
3
|
+
*
|
|
4
|
+
* The PTY engine lives in `@phnx-labs/term-cli` (extracted PHNX-4091);
|
|
5
|
+
* agents-cli never rebundles it. The OAuth device-code driver (`agents fleet
|
|
6
|
+
* login`, `agents auth mint`) spawns `term` on demand and fails loud when it is
|
|
7
|
+
* absent, so onboarding installs it here like every other standalone tool.
|
|
8
|
+
*
|
|
9
|
+
* Unlike browser/computer/secrets there is nothing to configure — no profile,
|
|
10
|
+
* no OS permission, no migrate step. A missing binary is a routine install
|
|
11
|
+
* (`agents clis install term` via the system `clis/term.yaml`, then a pinned
|
|
12
|
+
* `npm i -g`, both handled by installSetupTool), and once it is on PATH the
|
|
13
|
+
* tool is ready.
|
|
14
|
+
*/
|
|
15
|
+
import type { Command } from 'commander';
|
|
16
|
+
/** True when the standalone `term` executable resolves ($TERM_BIN or PATH). */
|
|
17
|
+
export declare function isTermCliInstalled(): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Install the standalone `term` CLI if missing. Returns whether `term` is on
|
|
20
|
+
* PATH afterwards. There is no further onboarding — presence is readiness.
|
|
21
|
+
*/
|
|
22
|
+
export declare function runTermWizard(): Promise<boolean>;
|
|
23
|
+
/** Register `agents setup term` under the parent `setup` command. */
|
|
24
|
+
export declare function registerSetupTermCommand(setupCmd: Command): void;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents setup term` — install the standalone `term` CLI if missing (PHNX-4092).
|
|
3
|
+
*
|
|
4
|
+
* The PTY engine lives in `@phnx-labs/term-cli` (extracted PHNX-4091);
|
|
5
|
+
* agents-cli never rebundles it. The OAuth device-code driver (`agents fleet
|
|
6
|
+
* login`, `agents auth mint`) spawns `term` on demand and fails loud when it is
|
|
7
|
+
* absent, so onboarding installs it here like every other standalone tool.
|
|
8
|
+
*
|
|
9
|
+
* Unlike browser/computer/secrets there is nothing to configure — no profile,
|
|
10
|
+
* no OS permission, no migrate step. A missing binary is a routine install
|
|
11
|
+
* (`agents clis install term` via the system `clis/term.yaml`, then a pinned
|
|
12
|
+
* `npm i -g`, both handled by installSetupTool), and once it is on PATH the
|
|
13
|
+
* tool is ready.
|
|
14
|
+
*/
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { isPromptCancelled } from './utils.js';
|
|
17
|
+
import { openSetupTerminal } from './setup-terminal.js';
|
|
18
|
+
import { resolveTermBin } from '../lib/term-client.js';
|
|
19
|
+
import { installSetupTool } from '../lib/setup-tool-install.js';
|
|
20
|
+
import { refreshToolSetup } from '../lib/setup-tool-status.js';
|
|
21
|
+
const INSTALL_HINT = 'agents clis install term # or: npm i -g @phnx-labs/term-cli';
|
|
22
|
+
/** True when the standalone `term` executable resolves ($TERM_BIN or PATH). */
|
|
23
|
+
export function isTermCliInstalled() {
|
|
24
|
+
return resolveTermBin() !== null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Install the standalone `term` CLI if missing. Returns whether `term` is on
|
|
28
|
+
* PATH afterwards. There is no further onboarding — presence is readiness.
|
|
29
|
+
*/
|
|
30
|
+
export async function runTermWizard() {
|
|
31
|
+
if (isTermCliInstalled()) {
|
|
32
|
+
console.log(chalk.green('The standalone `term` CLI is installed.'));
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (await installSetupTool('term')) {
|
|
36
|
+
console.log(chalk.green('Installed the standalone `term` CLI.'));
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
console.log(chalk.yellow('The standalone `term` CLI is not installed.'));
|
|
40
|
+
console.log(chalk.gray('Install it, then re-run `agents setup term`:'));
|
|
41
|
+
console.log(chalk.cyan(` ${INSTALL_HINT}`));
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
/** Register `agents setup term` under the parent `setup` command. */
|
|
45
|
+
export function registerSetupTermCommand(setupCmd) {
|
|
46
|
+
setupCmd
|
|
47
|
+
.command('term')
|
|
48
|
+
.description('Install the standalone `term` CLI (the PTY engine fleet login and auth mint spawn) if missing.')
|
|
49
|
+
.option('--install-only', 'Install the standalone term CLI (identical to the wizard; term needs no further setup)')
|
|
50
|
+
.option('--terminal [backend]', 'Open interactive setup in a detected or selected terminal')
|
|
51
|
+
.action(async (options) => {
|
|
52
|
+
try {
|
|
53
|
+
if (options.terminal !== undefined) {
|
|
54
|
+
await openSetupTerminal('term', options.terminal, options.installOnly);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!(await runTermWizard()))
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
await refreshToolSetup('term');
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (isPromptCancelled(err)) {
|
|
63
|
+
console.log(chalk.yellow('\nCancelled'));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.error(chalk.red(err.message));
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -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' | 'term' | 'accounts' | 'fleet' | 'watchdog' | 'preferences';
|
|
35
35
|
type SetupStatusState = 'ready' | 'missing' | 'n/a';
|
|
36
36
|
interface SetupStatusRow {
|
|
37
37
|
phase: 'core' | SetupPhase;
|