@phnx-labs/agents-cli 1.22.40 → 1.22.42
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 +36 -0
- package/README.md +14 -3
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +0 -1
- package/dist/cli/command-registry.d.ts +2 -0
- package/dist/cli/command-registry.js +5 -2
- package/dist/commands/accounts.d.ts +29 -0
- package/dist/commands/accounts.js +220 -29
- package/dist/commands/alias.js +1 -1
- package/dist/commands/auth.d.ts +9 -0
- package/dist/commands/auth.js +104 -0
- package/dist/commands/browser-sessions-picker.js +42 -62
- package/dist/commands/browser.d.ts +15 -0
- package/dist/commands/browser.js +145 -78
- package/dist/commands/computer-actions.d.ts +1 -1
- package/dist/commands/computer-actions.js +1 -1
- package/dist/commands/computer-sessions-picker.js +32 -55
- package/dist/commands/computer.d.ts +1 -1
- package/dist/commands/computer.js +2 -2
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/feed.js +4 -12
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +1 -1
- package/dist/commands/insights.js +163 -85
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/mine.js +1 -1
- package/dist/commands/org.d.ts +6 -0
- package/dist/commands/org.js +174 -0
- package/dist/commands/packages.js +1 -1
- package/dist/commands/prune.js +1 -1
- package/dist/commands/routines.js +1 -1
- package/dist/commands/run-account-picker.d.ts +19 -0
- package/dist/commands/run-account-picker.js +81 -0
- package/dist/commands/secrets.js +87 -97
- package/dist/commands/sessions-picker-factory.d.ts +40 -0
- package/dist/commands/sessions-picker-factory.js +67 -0
- package/dist/commands/sessions-picker.d.ts +1 -0
- package/dist/commands/sessions-picker.js +27 -5
- package/dist/commands/sessions-share.d.ts +25 -0
- package/dist/commands/sessions-share.js +166 -0
- package/dist/commands/sessions.js +2 -0
- package/dist/commands/setup-browser.js +5 -5
- package/dist/commands/setup-mine.js +1 -1
- package/dist/commands/setup-preferences.js +4 -4
- package/dist/commands/setup.js +5 -3
- package/dist/commands/sync.js +1 -1
- package/dist/commands/versions.js +1 -1
- package/dist/commands/view.js +1 -1
- package/dist/commands/webhook.js +14 -9
- package/dist/lib/account-capabilities.d.ts +10 -0
- package/dist/lib/account-capabilities.js +34 -0
- package/dist/lib/agent-spec/agents.d.ts +584 -0
- package/dist/lib/agent-spec/agents.js +3127 -0
- package/dist/lib/agent-spec/provider.js +1 -1
- package/dist/lib/agents.d.ts +2 -584
- package/dist/lib/agents.js +2 -3127
- package/dist/lib/browser/cdp.js +4 -0
- package/dist/lib/browser/chrome.js +12 -1
- package/dist/lib/browser/drivers/local.d.ts +8 -2
- package/dist/lib/browser/drivers/local.js +8 -2
- package/dist/lib/browser/drivers/ssh.d.ts +8 -2
- package/dist/lib/browser/drivers/ssh.js +11 -4
- package/dist/lib/browser/hygiene.d.ts +1 -1
- package/dist/lib/browser/hygiene.js +5 -1
- package/dist/lib/browser/profiles.d.ts +77 -4
- package/dist/lib/browser/profiles.js +121 -12
- package/dist/lib/browser/runtime-state.d.ts +11 -5
- package/dist/lib/browser/runtime-state.js +13 -9
- package/dist/lib/browser/service.d.ts +91 -30
- package/dist/lib/browser/service.js +342 -207
- package/dist/lib/browser/types.d.ts +96 -4
- package/dist/lib/browser/types.js +51 -0
- package/dist/lib/catchup.js +1 -1
- package/dist/lib/computer/actions.d.ts +1 -1
- package/dist/lib/computer/actions.js +1 -1
- package/dist/lib/{computer-rpc.js → computer/computer-rpc.js} +5 -5
- package/dist/lib/computer/dispatch.d.ts +1 -1
- package/dist/lib/computer/download.d.ts +1 -1
- package/dist/lib/computer/download.js +3 -3
- package/dist/lib/{ssh-tunnel.d.ts → computer/ssh-tunnel.d.ts} +1 -1
- package/dist/lib/{ssh-tunnel.js → computer/ssh-tunnel.js} +12 -12
- package/dist/lib/daemon/daemon.js +1 -1
- package/dist/lib/{runner.d.ts → daemon/runner.d.ts} +10 -10
- package/dist/lib/{runner.js → daemon/runner.js} +59 -118
- package/dist/lib/daemon-webhooks.d.ts +3 -1
- package/dist/lib/daemon-webhooks.js +12 -7
- package/dist/lib/device-config.js +1 -1
- package/dist/lib/devices/doctor-findings.d.ts +1 -1
- package/dist/lib/devices/doctor-findings.js +3 -3
- package/dist/lib/devices/fleet-inventory.js +1 -1
- package/dist/lib/devices/harness-inventory.js +1 -1
- package/dist/lib/devices/registry.js +4 -22
- package/dist/lib/doctor-diff.d.ts +1 -1
- package/dist/lib/doctor-diff.js +1 -1
- package/dist/lib/drift.js +1 -1
- package/dist/lib/entitlement.d.ts +31 -0
- package/dist/lib/entitlement.js +137 -0
- package/dist/lib/exec.js +39 -202
- package/dist/lib/feed/feed.js +2 -8
- package/dist/lib/fs-atomic.d.ts +7 -0
- package/dist/lib/fs-atomic.js +9 -0
- package/dist/lib/harness/adapter.d.ts +160 -0
- package/dist/lib/harness/adapter.js +47 -0
- package/dist/lib/harness/adapters/claude.d.ts +2 -0
- package/dist/lib/harness/adapters/claude.js +110 -0
- package/dist/lib/harness/adapters/codex.d.ts +2 -0
- package/dist/lib/harness/adapters/codex.js +83 -0
- package/dist/lib/harness/adapters/copilot.d.ts +2 -0
- package/dist/lib/harness/adapters/copilot.js +23 -0
- package/dist/lib/harness/adapters/cursor.d.ts +2 -0
- package/dist/lib/harness/adapters/cursor.js +38 -0
- package/dist/lib/harness/adapters/droid.d.ts +2 -0
- package/dist/lib/harness/adapters/droid.js +17 -0
- package/dist/lib/harness/adapters/grok.d.ts +2 -0
- package/dist/lib/harness/adapters/grok.js +14 -0
- package/dist/lib/harness/adapters/kimi.d.ts +2 -0
- package/dist/lib/harness/adapters/kimi.js +49 -0
- package/dist/lib/harness/adapters/muse.d.ts +2 -0
- package/dist/lib/harness/adapters/muse.js +44 -0
- package/dist/lib/harness/adapters/opencode.d.ts +2 -0
- package/dist/lib/harness/adapters/opencode.js +14 -0
- package/dist/lib/harness/exec-config-version.d.ts +8 -0
- package/dist/lib/harness/exec-config-version.js +22 -0
- package/dist/lib/harness/index.d.ts +1 -0
- package/dist/lib/harness/index.js +34 -0
- package/dist/lib/{hooks.d.ts → hooks/install.d.ts} +1 -1
- package/dist/lib/{hooks.js → hooks/install.js} +11 -11
- package/dist/lib/import.js +1 -1
- package/dist/lib/installations/index.d.ts +4 -4
- package/dist/lib/installations/index.js +3 -4
- package/dist/lib/installations/migrate.js +6 -6
- package/dist/lib/{shims.d.ts → installations/shims.d.ts} +2 -2
- package/dist/lib/{shims.js → installations/shims.js} +12 -114
- package/dist/lib/installations/store.d.ts +141 -3
- package/dist/lib/installations/store.js +475 -5
- package/dist/lib/installations/versions.d.ts +1 -126
- package/dist/lib/installations/versions.js +5 -465
- package/dist/lib/isolation-boundary-report.d.ts +1 -1
- package/dist/lib/isolation-boundary-report.js +1 -1
- package/dist/lib/mailbox-gc.js +2 -3
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/monitors/dispatch.d.ts +1 -1
- package/dist/lib/monitors/dispatch.js +2 -2
- package/dist/lib/monitors/state.d.ts +1 -1
- package/dist/lib/observe-aliases.d.ts +2 -2
- package/dist/lib/observe-aliases.js +2 -11
- package/dist/lib/plugins/plugins.js +1 -1
- package/dist/lib/plugins/skills.js +1 -1
- package/dist/lib/prix-account.d.ts +158 -0
- package/dist/lib/prix-account.js +214 -0
- package/dist/lib/project-key.js +7 -0
- package/dist/lib/project-launch.js +1 -1
- package/dist/lib/refresh.js +2 -2
- package/dist/lib/resource-inventory.d.ts +1 -1
- package/dist/lib/resource-inventory.js +1 -1
- package/dist/lib/rules/rules.js +1 -1
- package/dist/lib/scheduling/routines.js +1 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/bundles.d.ts +1 -1
- package/dist/lib/secrets/bundles.js +1 -1
- package/dist/lib/secrets/headless.d.ts +16 -0
- package/dist/lib/secrets/headless.js +21 -0
- package/dist/lib/secrets/remote.d.ts +9 -7
- package/dist/lib/secrets/remote.js +18 -9
- package/dist/lib/self-heal/checks/hook-runtime.js +1 -1
- package/dist/lib/self-heal/checks/path.js +1 -1
- package/dist/lib/self-heal/checks/shadowing.js +1 -1
- package/dist/lib/self-heal/checks/shims.js +1 -1
- package/dist/lib/session/discover.d.ts +39 -0
- package/dist/lib/session/discover.js +3 -3
- package/dist/lib/session/parse.js +24 -42
- package/dist/lib/session/recovery.js +1 -1
- package/dist/lib/session/share-html.d.ts +55 -0
- package/dist/lib/session/share-html.js +319 -0
- package/dist/lib/settings-manifest.d.ts +2 -0
- package/dist/lib/settings-manifest.js +81 -3
- package/dist/lib/share/publish.d.ts +38 -0
- package/dist/lib/share/publish.js +81 -8
- package/dist/lib/staleness/prune.d.ts +1 -1
- package/dist/lib/staleness/prune.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +2 -1
- package/dist/lib/startup/command-registry.js +5 -3
- package/dist/lib/teams/agents.js +2 -19
- package/dist/lib/teams/parsers.js +20 -26
- package/dist/lib/teams/registry.js +2 -21
- package/dist/lib/triggers/handlers.d.ts +37 -2
- package/dist/lib/triggers/handlers.js +57 -6
- package/dist/lib/triggers/webhook.d.ts +80 -6
- package/dist/lib/triggers/webhook.js +128 -4
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/uninstall.js +1 -1
- package/dist/lib/version.d.ts +9 -2
- package/dist/lib/version.js +11 -9
- package/dist/lib/wrap.d.ts +33 -0
- package/dist/lib/wrap.js +70 -0
- package/package.json +1 -1
- /package/dist/lib/{computer-rpc.d.ts → computer/computer-rpc.d.ts} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Flag subset both twins share for the interactive-routing gate. */
|
|
2
|
+
export interface SessionsPickerGateOpts {
|
|
3
|
+
interactive?: boolean;
|
|
4
|
+
json?: boolean;
|
|
5
|
+
/** Present only on the browser twin; computer never sets it. */
|
|
6
|
+
open?: string | boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface SessionsPickerCommandSpec<TRow, TOpts extends SessionsPickerGateOpts> {
|
|
9
|
+
/**
|
|
10
|
+
* Browser requires `opts.open === undefined` so `--open` (bare or with a
|
|
11
|
+
* selector) falls through to the flat printer. Computer has no `--open`.
|
|
12
|
+
*/
|
|
13
|
+
requireOpenUndefined?: boolean;
|
|
14
|
+
runFlat: (opts: TOpts) => void;
|
|
15
|
+
buildRows: (opts: TOpts) => TRow[];
|
|
16
|
+
emptyMessage: (opts: TOpts) => string;
|
|
17
|
+
message: string;
|
|
18
|
+
matches: (row: TRow, query: string) => boolean;
|
|
19
|
+
labelFor: (row: TRow) => string;
|
|
20
|
+
buildPreview: (row: TRow) => string;
|
|
21
|
+
emptyFilterMessage: string;
|
|
22
|
+
enterHint: string;
|
|
23
|
+
onOpen: (row: TRow) => void | Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export interface SessionsPickerCommand<TOpts extends SessionsPickerGateOpts> {
|
|
26
|
+
shouldOpen: (opts: TOpts, isTTY: boolean) => boolean;
|
|
27
|
+
run: (opts: TOpts) => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export declare function shouldOpenInteractiveSessions(opts: SessionsPickerGateOpts, isTTY: boolean, requireOpenUndefined?: boolean): boolean;
|
|
30
|
+
export declare function browseSessionsUntilQuit<TRow>(spec: {
|
|
31
|
+
message: string;
|
|
32
|
+
rows: TRow[];
|
|
33
|
+
matches: (row: TRow, query: string) => boolean;
|
|
34
|
+
labelFor: (row: TRow) => string;
|
|
35
|
+
buildPreview: (row: TRow) => string;
|
|
36
|
+
emptyFilterMessage: string;
|
|
37
|
+
enterHint: string;
|
|
38
|
+
onOpen: (row: TRow) => void | Promise<void>;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
export declare function createSessionsPickerCommand<TRow, TOpts extends SessionsPickerGateOpts>(spec: SessionsPickerCommandSpec<TRow, TOpts>): SessionsPickerCommand<TOpts>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared interactive-routing + browse loop for the task-first session
|
|
3
|
+
* pickers (`browser-sessions-picker.ts`, `computer-sessions-picker.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Each twin keeps its own row formatter, matcher, and enter-handler; this
|
|
6
|
+
* factory owns the TTY/`--json`/`--no-interactive` (and optional `--open`)
|
|
7
|
+
* gate, the empty-list message, and the cancel-aware `itemPicker` loop.
|
|
8
|
+
*/
|
|
9
|
+
import { itemPicker } from '../lib/picker.js';
|
|
10
|
+
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
11
|
+
export function shouldOpenInteractiveSessions(opts, isTTY, requireOpenUndefined = false) {
|
|
12
|
+
return (opts.interactive !== false &&
|
|
13
|
+
!opts.json &&
|
|
14
|
+
(!requireOpenUndefined || opts.open === undefined) &&
|
|
15
|
+
isTTY);
|
|
16
|
+
}
|
|
17
|
+
export async function browseSessionsUntilQuit(spec) {
|
|
18
|
+
for (;;) {
|
|
19
|
+
let picked;
|
|
20
|
+
try {
|
|
21
|
+
picked = await itemPicker({
|
|
22
|
+
message: spec.message,
|
|
23
|
+
items: spec.rows,
|
|
24
|
+
filter: (query) => (query.trim() ? spec.rows.filter((r) => spec.matches(r, query)) : spec.rows),
|
|
25
|
+
labelFor: spec.labelFor,
|
|
26
|
+
buildPreview: spec.buildPreview,
|
|
27
|
+
emptyMessage: spec.emptyFilterMessage,
|
|
28
|
+
enterHint: spec.enterHint,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (isPromptCancelled(err))
|
|
33
|
+
return;
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
if (!picked)
|
|
37
|
+
return;
|
|
38
|
+
await spec.onOpen(picked.item);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function createSessionsPickerCommand(spec) {
|
|
42
|
+
const shouldOpen = (opts, isTTY) => shouldOpenInteractiveSessions(opts, isTTY, spec.requireOpenUndefined === true);
|
|
43
|
+
return {
|
|
44
|
+
shouldOpen,
|
|
45
|
+
run: async (opts) => {
|
|
46
|
+
if (!shouldOpen(opts, isInteractiveTerminal())) {
|
|
47
|
+
spec.runFlat(opts);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const rows = spec.buildRows(opts);
|
|
51
|
+
if (rows.length === 0) {
|
|
52
|
+
console.log(spec.emptyMessage(opts));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await browseSessionsUntilQuit({
|
|
56
|
+
message: spec.message,
|
|
57
|
+
rows,
|
|
58
|
+
matches: spec.matches,
|
|
59
|
+
labelFor: spec.labelFor,
|
|
60
|
+
buildPreview: spec.buildPreview,
|
|
61
|
+
emptyFilterMessage: spec.emptyFilterMessage,
|
|
62
|
+
enterHint: spec.enterHint,
|
|
63
|
+
onOpen: spec.onOpen,
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -119,5 +119,6 @@ export declare function buildSessionPreviewDigest(events: SessionEvent[], sessio
|
|
|
119
119
|
export declare function directoriesTouched(session: SessionMeta, events: SessionEvent[], changes: ReturnType<typeof classifyFileChanges>): string[];
|
|
120
120
|
/** Relativize a file path to its parent dir, short enough for one preview line. */
|
|
121
121
|
export declare function relativizeDir(filePath: string, cwd?: string): string | undefined;
|
|
122
|
+
export declare function renderLastResponse(content: string, maxLines?: number, width?: number): string[];
|
|
122
123
|
/** Show an interactive session picker and return the selected session with its action (resume or view). */
|
|
123
124
|
export declare function sessionPicker(config: SessionPickerConfig): Promise<PickedSession | null>;
|
|
@@ -17,6 +17,8 @@ import { linkPath, linkUrl, relativeToCwd, shortenModel } from '../lib/session/r
|
|
|
17
17
|
import { linearIssueUrl } from '../lib/session/linear.js';
|
|
18
18
|
import { extractTodoProgress, WORKTREE_RE } from '../lib/session/state.js';
|
|
19
19
|
import { renderMarkdown } from '../lib/markdown.js';
|
|
20
|
+
import { wrapToWidth } from '../lib/wrap.js';
|
|
21
|
+
import { terminalWidth, stringWidth } from '../lib/session/width.js';
|
|
20
22
|
import { itemPicker } from '../lib/picker.js';
|
|
21
23
|
import { createMemoryCache } from '../lib/memory-cache.js';
|
|
22
24
|
import { classifyFileChanges, changeCounts, toolHistogram, detectTestResult } from '../lib/session/digest.js';
|
|
@@ -435,8 +437,6 @@ function formatHeader(session, events) {
|
|
|
435
437
|
if (totalTokens !== undefined) {
|
|
436
438
|
line3.push(chalk.bold.white(formatTokens(totalTokens)) + chalk.gray(' tokens'));
|
|
437
439
|
}
|
|
438
|
-
if (session.label)
|
|
439
|
-
line3.push(chalk.white(session.label));
|
|
440
440
|
if (session.filePath)
|
|
441
441
|
line3.push(chalk.gray(linkPath(session.filePath, session.id)));
|
|
442
442
|
else
|
|
@@ -452,7 +452,19 @@ function formatHeader(session, events) {
|
|
|
452
452
|
const label = session.prNumber ? `PR#${session.prNumber}` : 'PR';
|
|
453
453
|
line4.push(chalk.blue(linkUrl(session.prUrl, label)));
|
|
454
454
|
}
|
|
455
|
+
// Lead with the session's human title: `session.label` — an agent-generated
|
|
456
|
+
// name / `/rename`, else the `--name` launch handle. NOT `session.topic`: the
|
|
457
|
+
// topic is the derived first-prompt, already shown on the `Prompt:` line, so
|
|
458
|
+
// using it here too would print the same text twice. Unlabelled sessions keep
|
|
459
|
+
// that `Prompt:` line as their topic indicator and simply lead with the agent
|
|
460
|
+
// line. Wrapped to the pane (the header sits at column 0, full terminal width);
|
|
461
|
+
// nothing renders when there is no label.
|
|
462
|
+
const title = (session.label || '').trim();
|
|
463
|
+
const titleLines = title
|
|
464
|
+
? wrapToWidth(title, terminalWidth()).map(l => chalk.bold.white(l))
|
|
465
|
+
: [];
|
|
455
466
|
return [
|
|
467
|
+
...titleLines,
|
|
456
468
|
line1.join(DOT),
|
|
457
469
|
line2.join(DOT),
|
|
458
470
|
line3.join(DOT),
|
|
@@ -821,7 +833,9 @@ function formatCompactPreview(digest, session) {
|
|
|
821
833
|
}
|
|
822
834
|
if (lastAssistant) {
|
|
823
835
|
const maxLines = todosRendered.length > 0 || compact ? LAST_RESPONSE_MAX_LINES_WITH_TODOS : LAST_RESPONSE_MAX_LINES;
|
|
824
|
-
|
|
836
|
+
// Content sits at column 4 (outer body indent + the ' ' prefix below), so
|
|
837
|
+
// wrap it to the pane minus that indent.
|
|
838
|
+
const rendered = renderLastResponse(lastAssistant, maxLines, terminalWidth() - 4);
|
|
825
839
|
if (rendered.length > 0) {
|
|
826
840
|
lines.push('');
|
|
827
841
|
lines.push(chalk.cyan('Last response:'));
|
|
@@ -999,7 +1013,7 @@ export function relativizeDir(filePath, cwd) {
|
|
|
999
1013
|
dir = parts.slice(-3).join('/');
|
|
1000
1014
|
return dir || undefined;
|
|
1001
1015
|
}
|
|
1002
|
-
function renderLastResponse(content, maxLines = LAST_RESPONSE_MAX_LINES) {
|
|
1016
|
+
export function renderLastResponse(content, maxLines = LAST_RESPONSE_MAX_LINES, width = terminalWidth()) {
|
|
1003
1017
|
const cleaned = stripTags(content).trim();
|
|
1004
1018
|
if (!cleaned)
|
|
1005
1019
|
return [];
|
|
@@ -1010,7 +1024,15 @@ function renderLastResponse(content, maxLines = LAST_RESPONSE_MAX_LINES) {
|
|
|
1010
1024
|
catch {
|
|
1011
1025
|
rendered = cleaned;
|
|
1012
1026
|
}
|
|
1013
|
-
|
|
1027
|
+
// `marked-terminal` runs with `reflowText` off, so a paragraph with no hard
|
|
1028
|
+
// breaks renders as one long line that overflows the pane. Wrap any line whose
|
|
1029
|
+
// VISIBLE width exceeds the budget; leave lines that already fit untouched so
|
|
1030
|
+
// rendered-markdown indentation (lists, code blocks) is preserved. The text is
|
|
1031
|
+
// ANSI-coloured, so width is measured with `stringWidth`, never `String.length`.
|
|
1032
|
+
const all = rendered
|
|
1033
|
+
.replace(/\s+$/, '')
|
|
1034
|
+
.split('\n')
|
|
1035
|
+
.flatMap(line => (stringWidth(line) <= width ? [line] : wrapToWidth(line, width)));
|
|
1014
1036
|
// Drop leading/trailing empty lines
|
|
1015
1037
|
while (all.length && !all[0].trim())
|
|
1016
1038
|
all.shift();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { SessionMeta } from '../lib/session/types.js';
|
|
3
|
+
import { type PublishOptions } from '../lib/share/publish.js';
|
|
4
|
+
interface ShareOptions {
|
|
5
|
+
public?: boolean;
|
|
6
|
+
slug?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
expire?: string;
|
|
9
|
+
reasoning: string;
|
|
10
|
+
force?: boolean;
|
|
11
|
+
cover?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** `session-<shortId>` — stable per session, so re-sharing updates the same URL. */
|
|
14
|
+
export declare function defaultSessionSlug(session: SessionMeta): string;
|
|
15
|
+
/**
|
|
16
|
+
* Map the command's flags onto {@link PublishOptions}.
|
|
17
|
+
*
|
|
18
|
+
* Extracted so the defaults are testable directly. `unlisted` in particular is the
|
|
19
|
+
* security-relevant one — it inverts the parent command's public-by-default
|
|
20
|
+
* behavior, and a test that re-implements this mapping would still pass with it
|
|
21
|
+
* flipped.
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildSharePublishOptions(session: SessionMeta, options: Pick<ShareOptions, 'public' | 'slug' | 'label' | 'expire' | 'force' | 'cover'>): PublishOptions;
|
|
24
|
+
export declare function registerSessionsShareCommand(sessionsCmd: Command): void;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents sessions share <id>` — publish one session transcript as a link.
|
|
3
|
+
*
|
|
4
|
+
* Composes three pieces that already existed but were never wired together:
|
|
5
|
+
* `renderSessionMarkdownDocument()` (redacted transcript),
|
|
6
|
+
* `renderSessionHtmlDocument()` (self-contained branded page), and
|
|
7
|
+
* `publishFile()` (the R2-backed share Worker). Before this, sharing a session
|
|
8
|
+
* meant three commands and a detour through the external artifacts-cli.
|
|
9
|
+
*
|
|
10
|
+
* Unlisted by default, unlike `agents artifacts share`. A transcript carries file
|
|
11
|
+
* paths, command output, error text, and whatever a tool printed — strictly more
|
|
12
|
+
* than a plan does — so it does not belong in the public `/<user>` gallery unless
|
|
13
|
+
* the operator asks for it with `--public`. The URL itself stays world-readable:
|
|
14
|
+
* unlisted is a capability URL, not a secret.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as os from 'os';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import chalk from 'chalk';
|
|
20
|
+
import { setHelpSections } from '../lib/help.js';
|
|
21
|
+
import { knownSecretValuesFromEnv } from '../lib/redact.js';
|
|
22
|
+
import { discoverSessions } from '../lib/session/discover.js';
|
|
23
|
+
import { renderSessionHtmlDocument } from '../lib/session/share-html.js';
|
|
24
|
+
import { publishFile, redactEmails } from '../lib/share/publish.js';
|
|
25
|
+
import { renderSessionMarkdownDocument } from './sessions-render.js';
|
|
26
|
+
import { selectSessions } from './sessions-export.js';
|
|
27
|
+
import { parseAgentFilter } from './sessions.js';
|
|
28
|
+
function parseReasoning(value) {
|
|
29
|
+
if (value === 'omit' || value === 'fold' || value === 'include')
|
|
30
|
+
return value;
|
|
31
|
+
throw new Error(`Unknown reasoning mode "${value}". Expected omit, fold, or include.`);
|
|
32
|
+
}
|
|
33
|
+
/** `session-<shortId>` — stable per session, so re-sharing updates the same URL. */
|
|
34
|
+
export function defaultSessionSlug(session) {
|
|
35
|
+
return `session-${session.shortId || session.id}`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Map the command's flags onto {@link PublishOptions}.
|
|
39
|
+
*
|
|
40
|
+
* Extracted so the defaults are testable directly. `unlisted` in particular is the
|
|
41
|
+
* security-relevant one — it inverts the parent command's public-by-default
|
|
42
|
+
* behavior, and a test that re-implements this mapping would still pass with it
|
|
43
|
+
* flipped.
|
|
44
|
+
*/
|
|
45
|
+
export function buildSharePublishOptions(session, options) {
|
|
46
|
+
return {
|
|
47
|
+
slug: options.slug ?? defaultSessionSlug(session),
|
|
48
|
+
unlisted: options.public !== true,
|
|
49
|
+
expire: options.expire,
|
|
50
|
+
force: options.force === true,
|
|
51
|
+
cover: options.cover !== false,
|
|
52
|
+
label: options.label,
|
|
53
|
+
meta: { kind: 'session' },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function registerSessionsShareCommand(sessionsCmd) {
|
|
57
|
+
const cmd = sessionsCmd
|
|
58
|
+
.command('share <session>')
|
|
59
|
+
.description('Publish one session as a redacted, self-contained web page and print the link.')
|
|
60
|
+
.option('--public', 'List the page in your public share gallery (default: unlisted capability URL)')
|
|
61
|
+
.option('--slug <slug>', 'URL slug under your namespace (default: session-<shortId>)')
|
|
62
|
+
.option('--label <text>', 'Display title in the gallery and `agents artifacts share list`')
|
|
63
|
+
.option('--expire <spec>', 'Auto-expire window: 30d, 12h, a date, or never (default: 30d)')
|
|
64
|
+
.option('--reasoning <mode>', 'Reasoning visibility: omit, fold, or include', 'omit')
|
|
65
|
+
.option('--force', 'Publish despite the sensitive-content scan flagging the transcript')
|
|
66
|
+
.option('--no-cover', 'Skip generating the Open Graph preview image');
|
|
67
|
+
setHelpSections(cmd, {
|
|
68
|
+
examples: `# Share a session — prints an unlisted, redacted link
|
|
69
|
+
agents sessions share a1b2c3d4
|
|
70
|
+
|
|
71
|
+
# Put it in your public gallery at share.agents-cli.sh/<you>
|
|
72
|
+
agents sessions share a1b2c3d4 --public --label "How the retry bug got fixed"
|
|
73
|
+
|
|
74
|
+
# Keep the model's reasoning in collapsible sections
|
|
75
|
+
agents sessions share a1b2c3d4 --reasoning fold
|
|
76
|
+
|
|
77
|
+
# A link that does not decay
|
|
78
|
+
agents sessions share a1b2c3d4 --expire never`,
|
|
79
|
+
notes: `Requires a share endpoint: 'agents artifacts share status' shows it, 'agents artifacts setup'
|
|
80
|
+
provisions one, 'agents artifacts share join <baseUrl>' uses an existing one.
|
|
81
|
+
|
|
82
|
+
Unlisted by default — the URL is world-readable but the page stays out of your public
|
|
83
|
+
gallery and out of 'agents artifacts share list'. Pass --public to list it.
|
|
84
|
+
|
|
85
|
+
Secrets are redacted and the same pre-publish scan as 'agents artifacts share' runs on the
|
|
86
|
+
page, so a transcript carrying emails or credential-shaped strings is refused
|
|
87
|
+
unless you pass --force. --no-redact (the sessions-level flag) disables redaction
|
|
88
|
+
and is a bad idea for anything you publish.
|
|
89
|
+
|
|
90
|
+
Re-running with the same session updates the same URL and keeps the prior version
|
|
91
|
+
as a revision ('agents artifacts share revisions <slug>').
|
|
92
|
+
|
|
93
|
+
Manage published sessions with 'agents artifacts share list' and
|
|
94
|
+
'agents artifacts share delete <slug>'.`,
|
|
95
|
+
});
|
|
96
|
+
cmd.action(async (selector, options, command) => {
|
|
97
|
+
const globals = command.optsWithGlobals();
|
|
98
|
+
const reasoning = parseReasoning(options.reasoning);
|
|
99
|
+
const limit = Math.max(1, Number.parseInt(globals.limit || '100', 10) || 100);
|
|
100
|
+
const agent = parseAgentFilter(globals.agent).agent;
|
|
101
|
+
const sessions = selectSessions(await discoverSessions({
|
|
102
|
+
all: globals.all !== false,
|
|
103
|
+
since: globals.since,
|
|
104
|
+
limit,
|
|
105
|
+
agent: agent ?? undefined,
|
|
106
|
+
}), [selector]);
|
|
107
|
+
if (sessions.length === 0) {
|
|
108
|
+
process.stderr.write(chalk.yellow(`No session matched "${selector}".\n`));
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// One link per share: several sessions in one page would give the reader no
|
|
113
|
+
// way to reference just the one that matters, and the slug could only name one.
|
|
114
|
+
if (sessions.length > 1) {
|
|
115
|
+
process.stderr.write(chalk.yellow(`"${selector}" matched ${sessions.length} sessions. Share one at a time — pass a full or unique session id.\n`));
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const session = sessions[0];
|
|
120
|
+
const redact = globals.redact !== false;
|
|
121
|
+
const markdown = renderSessionMarkdownDocument(session, {
|
|
122
|
+
redact,
|
|
123
|
+
reasoning,
|
|
124
|
+
knownSecrets: redact ? knownSecretValuesFromEnv() : undefined,
|
|
125
|
+
});
|
|
126
|
+
// Emails on top of what the renderer masks. Almost every real transcript
|
|
127
|
+
// carries a few — git author addresses, `gh api user`, a pasted log — and
|
|
128
|
+
// publishToEndpoint refuses a body containing any (RUSH-2428). Masking them
|
|
129
|
+
// means the published page genuinely does not carry them; the alternative,
|
|
130
|
+
// telling people to pass --force, would train everyone to bypass the gate
|
|
131
|
+
// that also catches real credentials.
|
|
132
|
+
//
|
|
133
|
+
// Applied to the RENDERED PAGE, not the Markdown, so the text that is masked
|
|
134
|
+
// is exactly the text `scanShareContent` will scan. Markdown escaping stands
|
|
135
|
+
// between the two: `foo\@example.com` hides from the pattern in the Markdown
|
|
136
|
+
// and reappears as a live address once marked drops the backslash.
|
|
137
|
+
const page = renderSessionHtmlDocument(session, markdown, { redacted: redact });
|
|
138
|
+
const html = redact ? redactEmails(page) : page;
|
|
139
|
+
// A real file on disk is what publishFile() takes, and the OG capturer opens
|
|
140
|
+
// it in a browser. 0600 + a per-run directory keeps the intermediate off a
|
|
141
|
+
// world-readable /tmp path while it exists.
|
|
142
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'agents-session-share-'));
|
|
143
|
+
const file = path.join(dir, `${defaultSessionSlug(session)}.html`);
|
|
144
|
+
try {
|
|
145
|
+
fs.writeFileSync(file, html, { mode: 0o600 });
|
|
146
|
+
const result = await publishFile(file, buildSharePublishOptions(session, options));
|
|
147
|
+
if (globals.json) {
|
|
148
|
+
process.stdout.write(JSON.stringify({
|
|
149
|
+
session: session.id,
|
|
150
|
+
agent: session.agent,
|
|
151
|
+
redacted: redact,
|
|
152
|
+
...result,
|
|
153
|
+
}, null, 2) + '\n');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
process.stdout.write(`${result.url}\n`);
|
|
157
|
+
const bits = [result.unlisted ? 'unlisted' : 'public', redact ? 'redacted' : chalk.red('NOT redacted')];
|
|
158
|
+
if (result.expiresAt)
|
|
159
|
+
bits.push(`expires ${result.expiresAt.slice(0, 10)}`);
|
|
160
|
+
process.stderr.write(chalk.dim(`${bits.join(' · ')}\n`));
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
@@ -70,6 +70,7 @@ import { registerAttachCommand } from './attach.js';
|
|
|
70
70
|
import { registerSessionsInjectCommand } from './sessions-inject.js';
|
|
71
71
|
import { registerSessionsExportCommand } from './sessions-export.js';
|
|
72
72
|
import { registerSessionsRenderCommand } from './sessions-render.js';
|
|
73
|
+
import { registerSessionsShareCommand } from './sessions-share.js';
|
|
73
74
|
import { registerSessionsImportCommand } from './sessions-import.js';
|
|
74
75
|
import { registerSessionsMigrateCommand, registerSessionsMigrationsCommand } from './sessions-migrate.js';
|
|
75
76
|
import { registerSessionsBackfillCommand } from './sessions-backfill.js';
|
|
@@ -5173,6 +5174,7 @@ export function registerSessionsCommands(program) {
|
|
|
5173
5174
|
registerSessionsInjectCommand(sessionsCmd);
|
|
5174
5175
|
registerSessionsExportCommand(sessionsCmd);
|
|
5175
5176
|
registerSessionsRenderCommand(sessionsCmd);
|
|
5177
|
+
registerSessionsShareCommand(sessionsCmd);
|
|
5176
5178
|
registerSessionsImportCommand(sessionsCmd);
|
|
5177
5179
|
registerSessionsMigrateCommand(sessionsCmd);
|
|
5178
5180
|
registerSessionsMigrationsCommand(sessionsCmd);
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
import chalk from 'chalk';
|
|
11
11
|
import { setConfigValue } from '../lib/device-config.js';
|
|
12
12
|
import { findFirstInstalledBrowser, listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
13
|
-
import { DEFAULT_BROWSER_PROFILE_NAME, createProfile, findFreeProfilePort, getConfiguredDefaultProfileName,
|
|
13
|
+
import { DEFAULT_BROWSER_PROFILE_NAME, getAutoDetectedProfile, createProfile, findFreeProfilePort, getConfiguredDefaultProfileName, } from '../lib/browser/profiles.js';
|
|
14
14
|
import { DEFAULT_VIEWPORT } from '../lib/browser/devices.js';
|
|
15
15
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
16
16
|
import { defaultBrowserChoice } from './setup-preferences.js';
|
|
17
|
-
const INSTALL_HINT = 'Install one of: Google Chrome, Brave, Microsoft Edge, Chromium, or
|
|
17
|
+
const INSTALL_HINT = 'Install one of: Google Chrome, Brave, Microsoft Edge, Chromium, Comet, or Arc, then re-run `agents setup browser`.\n' +
|
|
18
18
|
'(Safari and Firefox are not supported — agents browser drives over the Chrome DevTools Protocol.)';
|
|
19
19
|
/**
|
|
20
20
|
* Interactive browser setup. Returns true if a usable default profile exists
|
|
@@ -29,9 +29,9 @@ export async function runBrowserWizard() {
|
|
|
29
29
|
console.error(chalk.red('No supported browser found.\n') + chalk.dim(INSTALL_HINT));
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
32
|
-
const existing = await
|
|
32
|
+
const existing = await getAutoDetectedProfile();
|
|
33
33
|
if (existing) {
|
|
34
|
-
console.log(chalk.dim(`Browser profile "${
|
|
34
|
+
console.log(chalk.dim(`Browser profile "${existing.name}" already exists.`));
|
|
35
35
|
return true;
|
|
36
36
|
}
|
|
37
37
|
const created = await createAutoDefault();
|
|
@@ -46,7 +46,7 @@ export async function runBrowserWizard() {
|
|
|
46
46
|
}
|
|
47
47
|
const { confirm, select } = await import('@inquirer/prompts');
|
|
48
48
|
// If a default profile already exists, this is a reconfigure.
|
|
49
|
-
const existing = await
|
|
49
|
+
const existing = await getAutoDetectedProfile();
|
|
50
50
|
if (existing) {
|
|
51
51
|
console.log(chalk.dim(`Browser profile "${existing.name}" already exists (${existing.browser}${existing.binary ? ` · ${existing.binary}` : ''}).`));
|
|
52
52
|
const change = await confirm({ message: 'Re-create it (e.g. to pin a different browser)?', default: false });
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import chalk from 'chalk';
|
|
12
12
|
import { initBrand, registerMineManageCommands } from './mine.js';
|
|
13
13
|
import { validateBrandName, getBrandConfig } from '../lib/brand.js';
|
|
14
|
-
import { isShimsInPath } from '../lib/shims.js';
|
|
14
|
+
import { isShimsInPath } from '../lib/installations/shims.js';
|
|
15
15
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
16
16
|
/**
|
|
17
17
|
* Optional/heavier top-level commands a brand commonly turns off. Kept short so
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import chalk from 'chalk';
|
|
16
16
|
import { listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
17
|
-
import { DEFAULT_BROWSER_PROFILE_NAME, createProfile, findFreeProfilePort, getConfiguredDefaultProfileName,
|
|
17
|
+
import { DEFAULT_BROWSER_PROFILE_NAME, getAutoDetectedProfile, createProfile, findFreeProfilePort, getConfiguredDefaultProfileName, } from '../lib/browser/profiles.js';
|
|
18
18
|
import { DEFAULT_VIEWPORT } from '../lib/browser/devices.js';
|
|
19
19
|
import { getConfigValue, setConfigValue } from '../lib/device-config.js';
|
|
20
20
|
import { loadDevices } from '../lib/devices/registry.js';
|
|
@@ -88,7 +88,7 @@ export async function maybePickBrowserProfile() {
|
|
|
88
88
|
return false;
|
|
89
89
|
if (getConfiguredDefaultProfileName())
|
|
90
90
|
return false;
|
|
91
|
-
if (await
|
|
91
|
+
if (await getAutoDetectedProfile())
|
|
92
92
|
return false;
|
|
93
93
|
const installed = listInstalledBrowsers();
|
|
94
94
|
if (installed.length === 0)
|
|
@@ -115,10 +115,10 @@ export async function maybePickBrowserProfile() {
|
|
|
115
115
|
viewport: { width: DEFAULT_VIEWPORT.width, height: DEFAULT_VIEWPORT.height },
|
|
116
116
|
};
|
|
117
117
|
await createProfile(profile);
|
|
118
|
-
// The same key `agents browser
|
|
118
|
+
// The same key `agents browser use` writes (device-local).
|
|
119
119
|
setConfigValue('browser.profile', profile.name);
|
|
120
120
|
console.log(chalk.green(`Browser: '${chosen.browserType}'`) +
|
|
121
|
-
chalk.dim(` — profile "${profile.name}" is this machine's default (agents browser
|
|
121
|
+
chalk.dim(` — profile "${profile.name}" is this machine's default (agents browser use to change).`));
|
|
122
122
|
return true;
|
|
123
123
|
}
|
|
124
124
|
/**
|
package/dist/commands/setup.js
CHANGED
|
@@ -16,7 +16,7 @@ import { isGitRepo, cloneIntoExisting, pullRepo } from '../lib/git.js';
|
|
|
16
16
|
import { isPromptCancelled, isInteractiveTerminal } from './utils.js';
|
|
17
17
|
import { AGENTS, agentConfigDirName, getUnmanagedAgentInstalls, countSessionFiles, agentLabel } from '../lib/agents.js';
|
|
18
18
|
import { setGlobalDefault } from '../lib/installations/versions.js';
|
|
19
|
-
import { ensureShimCurrent, switchHomeFileSymlinks, isShimsInPath, addShimsToPath, getPathSetupInstructions, assertIsolationBoundary } from '../lib/shims.js';
|
|
19
|
+
import { ensureShimCurrent, switchHomeFileSymlinks, isShimsInPath, addShimsToPath, getPathSetupInstructions, assertIsolationBoundary } from '../lib/installations/shims.js';
|
|
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';
|
|
@@ -26,7 +26,7 @@ import { registerSetupSecretsCommand } from './setup-secrets.js';
|
|
|
26
26
|
import { registerSetupFleetCommand } from './setup-fleet.js';
|
|
27
27
|
import { registerSetupWatchdogCommand, runWatchdogSetupWizard } from './setup-watchdog.js';
|
|
28
28
|
import { runPreferencesStep } from './setup-preferences.js';
|
|
29
|
-
import { getConfiguredDefaultProfileName, getProfile,
|
|
29
|
+
import { getConfiguredDefaultProfileName, getProfile, getAutoDetectedProfile, isProfileLaunchableHere } from '../lib/browser/profiles.js';
|
|
30
30
|
import { listInstalledBrowsers } from '../lib/browser/chrome.js';
|
|
31
31
|
import { probeComputerTrust } from './computer.js';
|
|
32
32
|
import { readShareConfig } from '../lib/share/config.js';
|
|
@@ -265,7 +265,9 @@ export async function ensureInitialized(program) {
|
|
|
265
265
|
}
|
|
266
266
|
export async function getSetupStatus() {
|
|
267
267
|
const configuredBrowserProfile = getConfiguredDefaultProfileName();
|
|
268
|
-
const browserProfile =
|
|
268
|
+
const browserProfile = configuredBrowserProfile
|
|
269
|
+
? await getProfile(configuredBrowserProfile)
|
|
270
|
+
: await getAutoDetectedProfile();
|
|
269
271
|
const browserReady = browserProfile !== null && isProfileLaunchableHere(browserProfile);
|
|
270
272
|
const installedBrowsers = listInstalledBrowsers();
|
|
271
273
|
const computerState = process.platform === 'darwin' ? (await probeComputerTrust() ? 'ready' : 'missing') : 'n/a';
|
package/dist/commands/sync.js
CHANGED
|
@@ -36,7 +36,7 @@ import { resolveSyncPassphraseFromEnv } from '../lib/secrets/sync-passphrase.js'
|
|
|
36
36
|
import { agentLabel, resolveAgentName, MANAGED_AGENT_IDS, isAgentHardDeprecated, hardDeprecationError } from '../lib/agents.js';
|
|
37
37
|
import { isVersionInstalled, syncResourcesToVersion, parseAgentSpec, resolveVersion, resolveVersionAlias, listInstalledVersions, getAvailableResources, getActuallySyncedResources, getProjectOnlyResources, getNewResources, hasNewResources, promptResourceSelection, promptNewResourceSelection, buildSelection, mergeRepoScopedSelections, listRepoNames, getVersionHomePath, } from '../lib/installations/versions.js';
|
|
38
38
|
import { capableAgents } from '../lib/capabilities.js';
|
|
39
|
-
import { parseHookManifest, registerHooksToSettings } from '../lib/hooks.js';
|
|
39
|
+
import { parseHookManifest, registerHooksToSettings } from '../lib/hooks/install.js';
|
|
40
40
|
import { compileRulesForProject } from '../lib/rules/compile.js';
|
|
41
41
|
import { runLaunchSync } from '../lib/project-launch.js';
|
|
42
42
|
import { formatKeptProjectResources } from '../lib/project-resources.js';
|
|
@@ -11,7 +11,7 @@ import { resolveConfiguredModel, formatAgentIdentity } from '../lib/models.js';
|
|
|
11
11
|
import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
|
|
12
12
|
import { installVersion, removeVersion, listInstalledVersions, isVersionInstalled, isLatestInstalled, isOldestInstalled, getGlobalDefault, setGlobalDefault, markVersionIsolated, setIsolatedDefault, isVersionIsolated, getVersionHomePath, getVersionDir, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, printTrashFooter, } from '../lib/installations/versions.js';
|
|
13
13
|
import { carryForwardSettings } from '../lib/settings-manifest.js';
|
|
14
|
-
import { createShim, createVersionedAlias, supportsIsolatedInstall, isIsolationProtected, CONFIG_ENV_ISOLATED_AGENTS, removeShim, shimExists, getShimsDir, getShimPath, getPathShadowingExecutable, isShimsInPath, getPathSetupInstructions, addShimsToPath, switchConfigSymlink, switchHomeFileSymlinks, } from '../lib/shims.js';
|
|
14
|
+
import { createShim, createVersionedAlias, supportsIsolatedInstall, isIsolationProtected, CONFIG_ENV_ISOLATED_AGENTS, removeShim, shimExists, getShimsDir, getShimPath, getPathShadowingExecutable, isShimsInPath, getPathSetupInstructions, addShimsToPath, switchConfigSymlink, switchHomeFileSymlinks, } from '../lib/installations/shims.js';
|
|
15
15
|
import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
|
|
16
16
|
import { tryAutoPull } from '../lib/git.js';
|
|
17
17
|
import { getAgentsDir, getTrashVersionsDir } from '../lib/state.js';
|
package/dist/commands/view.js
CHANGED
|
@@ -11,7 +11,7 @@ import { authCacheKey, formatCheckedAge, readAuthHealthCache } from '../lib/auth
|
|
|
11
11
|
import { agentReportsUsage, deriveUsageStatusFromSnapshot, formatUsageSection, formatUsageSummary, formatUsageStatusBadge, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, isUsageHeadlessScopeError, } from '../lib/accounting/usage.js';
|
|
12
12
|
import { readManifest } from '../lib/manifest.js';
|
|
13
13
|
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, removeVersion, printTrashFooter, reconcileStaleLatestForAgent, isGlobalBinaryAgent, getLiveVersion, isVersionIsolated, getIsolatedDefault, } from '../lib/installations/versions.js';
|
|
14
|
-
import { ensureVersionedAliasCurrent, removeShim, } from '../lib/shims.js';
|
|
14
|
+
import { ensureVersionedAliasCurrent, removeShim, } from '../lib/installations/shims.js';
|
|
15
15
|
import { getAgentResources } from '../lib/resources.js';
|
|
16
16
|
import { renderMergedResources } from '../lib/merged-resources.js';
|
|
17
17
|
import { resolveVersionFilter, AgentSpecError } from '../lib/agent-spec/index.js';
|
package/dist/commands/webhook.js
CHANGED
|
@@ -25,8 +25,10 @@ function readWebhookSecrets(bundleName) {
|
|
|
25
25
|
secrets.github = env.GITHUB_WEBHOOK_SECRET;
|
|
26
26
|
if (env.LINEAR_WEBHOOK_SECRET)
|
|
27
27
|
secrets.linear = env.LINEAR_WEBHOOK_SECRET;
|
|
28
|
-
if (
|
|
29
|
-
|
|
28
|
+
if (env.SLACK_SIGNING_SECRET)
|
|
29
|
+
secrets.slack = env.SLACK_SIGNING_SECRET;
|
|
30
|
+
if (!secrets.github && !secrets.linear && !secrets.slack) {
|
|
31
|
+
throw new Error(`Bundle '${bundleName}' must contain GITHUB_WEBHOOK_SECRET, LINEAR_WEBHOOK_SECRET, or SLACK_SIGNING_SECRET.`);
|
|
30
32
|
}
|
|
31
33
|
return secrets;
|
|
32
34
|
}
|
|
@@ -36,8 +38,8 @@ export function registerWebhooksCommand(program) {
|
|
|
36
38
|
.description('Run a localhost signed webhook receiver for routine triggers.');
|
|
37
39
|
webhooks
|
|
38
40
|
.command('serve')
|
|
39
|
-
.description('Receive signed GitHub/Linear webhooks on /hooks/<source> and fire matching routines.')
|
|
40
|
-
.requiredOption('--secrets-bundle <name>', 'agents secrets bundle containing GITHUB_WEBHOOK_SECRET and/or
|
|
41
|
+
.description('Receive signed GitHub/Linear/Slack webhooks on /hooks/<source> and fire matching routines and handlers.')
|
|
42
|
+
.requiredOption('--secrets-bundle <name>', 'agents secrets bundle containing GITHUB_WEBHOOK_SECRET, LINEAR_WEBHOOK_SECRET, and/or SLACK_SIGNING_SECRET')
|
|
41
43
|
.option('--bind <addr>', `Bind address (default ${DEFAULT_HOST})`, DEFAULT_HOST)
|
|
42
44
|
.option('-p, --port <n>', `Local port (default ${DEFAULT_PORT})`, String(DEFAULT_PORT))
|
|
43
45
|
.option('--rate-limit <n>', 'Accepted deliveries per source per minute', '60')
|
|
@@ -61,12 +63,15 @@ export function registerWebhooksCommand(program) {
|
|
|
61
63
|
// Durable delivery dedup: replays survive a receiver restart (an
|
|
62
64
|
// in-memory store would forget every seen delivery on restart).
|
|
63
65
|
deliveryStore: createFileDeliveryStore(path.join(getRuntimeStateDir(), 'webhook', 'deliveries.json')),
|
|
64
|
-
|
|
66
|
+
// Logged at MATCH time, before dispatch — a `run.command` handler can
|
|
67
|
+
// block on a shelled-out agent run for minutes, and that must never
|
|
68
|
+
// delay the log that says a delivery fired (RUSH-2722).
|
|
69
|
+
onMatch: (webhook, matchedJobNames, matchedHandlerNames) => {
|
|
65
70
|
const parts = [];
|
|
66
|
-
if (
|
|
67
|
-
parts.push(`routines ${
|
|
68
|
-
if (
|
|
69
|
-
parts.push(`handlers ${
|
|
71
|
+
if (matchedJobNames.length)
|
|
72
|
+
parts.push(`routines ${matchedJobNames.join(', ')}`);
|
|
73
|
+
if (matchedHandlerNames.length)
|
|
74
|
+
parts.push(`handlers ${matchedHandlerNames.join(', ')}`);
|
|
70
75
|
console.log(`${new Date().toISOString()} ${webhook.source}:${webhook.event} ` +
|
|
71
76
|
(parts.length ? `fired ${parts.join('; ')}` : 'no match'));
|
|
72
77
|
},
|
|
@@ -24,6 +24,16 @@ export declare const NATIVE_ACCOUNT_CAPABILITIES: Record<AgentId, NativeAccountC
|
|
|
24
24
|
export declare function nativeAccountCapability(agent: AgentId): NativeAccountCapability;
|
|
25
25
|
/** Whether a harness's native login can be named/attached (supported or conditional). */
|
|
26
26
|
export declare function nativeAccountNameable(agent: AgentId): boolean;
|
|
27
|
+
/** Harnesses whose native login is fully nameable/attachable today. */
|
|
28
|
+
export declare function supportedNativeHarnesses(): AgentId[];
|
|
29
|
+
/**
|
|
30
|
+
* Named reason to refuse native name/attach, or null when the harness is
|
|
31
|
+
* nameable. Provider-credential `accounts add` is a different path and MUST
|
|
32
|
+
* not consult this — that surface is supported for every registered provider.
|
|
33
|
+
*/
|
|
34
|
+
export declare function nativeAccountNamingRefusal(agent: AgentId): string | null;
|
|
35
|
+
/** Fail loud on the native name/attach path for an unsupported harness. */
|
|
36
|
+
export declare function assertNativeAccountNameable(agent: AgentId): void;
|
|
27
37
|
/**
|
|
28
38
|
* The single identity key used at `accounts name`, attach, exec validation,
|
|
29
39
|
* view, and inventory. Prefer the harness's stable `accountKey`. For an
|