@phnx-labs/agents-cli 1.20.50 → 1.20.51
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 +24 -0
- package/dist/commands/browser-picker.js +1 -18
- package/dist/commands/cloud.js +1 -25
- package/dist/commands/computer.d.ts +1 -0
- package/dist/commands/computer.js +129 -8
- package/dist/commands/exec.js +49 -6
- package/dist/commands/factory.js +1 -4
- package/dist/commands/inspect.js +1 -11
- package/dist/commands/mcp.js +2 -6
- package/dist/commands/message.js +1 -4
- package/dist/commands/profiles.js +1 -18
- package/dist/commands/repo.js +33 -14
- package/dist/commands/resource-view.d.ts +1 -0
- package/dist/commands/resource-view.js +5 -17
- package/dist/commands/secrets.d.ts +1 -0
- package/dist/commands/secrets.js +1 -28
- package/dist/commands/sessions-picker.js +1 -18
- package/dist/commands/sessions.js +6 -8
- package/dist/commands/teams-picker.js +1 -32
- package/dist/commands/teams.js +1 -27
- package/dist/commands/tmux.js +1 -3
- package/dist/commands/view.js +1 -9
- package/dist/commands/worktree.js +1 -4
- package/dist/lib/agents.d.ts +0 -4
- package/dist/lib/agents.js +20 -33
- package/dist/lib/auto-dispatch-linear.d.ts +18 -0
- package/dist/lib/auto-dispatch-linear.js +107 -0
- package/dist/lib/auto-dispatch-provider.d.ts +10 -0
- package/dist/lib/auto-dispatch-provider.js +25 -0
- package/dist/lib/auto-dispatch.d.ts +87 -0
- package/dist/lib/auto-dispatch.js +142 -0
- package/dist/lib/browser/cdp.js +11 -2
- package/dist/lib/browser/drivers/ssh.d.ts +28 -10
- package/dist/lib/browser/drivers/ssh.js +57 -18
- package/dist/lib/browser/refs.js +1 -5
- package/dist/lib/cli-resources.d.ts +0 -2
- package/dist/lib/cli-resources.js +30 -13
- package/dist/lib/cloud/rush.d.ts +0 -24
- package/dist/lib/cloud/rush.js +0 -31
- package/dist/lib/crabbox/cli.js +4 -1
- package/dist/lib/crabbox/lease.js +29 -1
- package/dist/lib/daemon.js +41 -0
- package/dist/lib/exec.js +31 -14
- package/dist/lib/format.d.ts +38 -0
- package/dist/lib/format.js +108 -0
- package/dist/lib/git.d.ts +21 -0
- package/dist/lib/git.js +92 -0
- package/dist/lib/hooks/cache.d.ts +9 -2
- package/dist/lib/hooks/cache.js +220 -8
- package/dist/lib/hooks.js +17 -8
- package/dist/lib/platform/exec.d.ts +4 -1
- package/dist/lib/platform/exec.js +8 -2
- package/dist/lib/resources.d.ts +0 -8
- package/dist/lib/resources.js +0 -10
- package/dist/lib/runner.js +10 -2
- 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/session/active.d.ts +11 -1
- package/dist/lib/session/active.js +3 -0
- package/dist/lib/session/db.d.ts +1 -4
- package/dist/lib/session/db.js +20 -25
- package/dist/lib/session/discover.d.ts +2 -2
- package/dist/lib/session/discover.js +61 -48
- package/dist/lib/session/parse.js +35 -34
- package/dist/lib/session/render.d.ts +7 -3
- package/dist/lib/session/render.js +15 -9
- package/dist/lib/session/state.d.ts +55 -0
- package/dist/lib/session/state.js +87 -10
- package/dist/lib/session/types.d.ts +9 -0
- package/dist/lib/shims.d.ts +9 -3
- package/dist/lib/shims.js +44 -8
- package/dist/lib/ssh-tunnel.d.ts +33 -2
- package/dist/lib/ssh-tunnel.js +94 -7
- package/dist/lib/staleness/types.d.ts +0 -1
- package/dist/lib/types.d.ts +14 -1
- package/dist/lib/versions.d.ts +0 -26
- package/dist/lib/versions.js +2 -145
- package/dist/lib/warn-unpushed.d.ts +40 -0
- package/dist/lib/warn-unpushed.js +128 -0
- package/package.json +3 -1
- package/dist/lib/resources/index.d.ts +0 -53
- package/dist/lib/resources/index.js +0 -76
package/dist/commands/repo.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { visibleWidth, padVisible } from '../lib/format.js';
|
|
2
3
|
import ora from 'ora';
|
|
3
4
|
import * as fs from 'fs';
|
|
4
5
|
import * as path from 'path';
|
|
@@ -26,7 +27,7 @@ function resolveRepoPath(target) {
|
|
|
26
27
|
return path.join(HOME, `.agents-${trimmed}`);
|
|
27
28
|
}
|
|
28
29
|
import { applyExtraAliasToVersions, ensureAgentsDir, getExtraRepoDir, getSystemAgentsDir, getUserAgentsDir, readMeta, resolveExtraRepoDir, updateMeta, } from '../lib/state.js';
|
|
29
|
-
import { parseSource, pullRepo, commitAndPush, isGitRepo, isSystemRepoOrigin } from '../lib/git.js';
|
|
30
|
+
import { parseSource, pullRepo, commitAndPush, isGitRepo, isSystemRepoOrigin, adoptRepo } from '../lib/git.js';
|
|
30
31
|
import { DEFAULT_SYSTEM_REPO } from '../lib/types.js';
|
|
31
32
|
import { ALL_AGENT_IDS, isAgentName, resolveAgentName } from '../lib/agents.js';
|
|
32
33
|
import { refresh } from '../lib/refresh.js';
|
|
@@ -218,14 +219,6 @@ async function diffResourceEntries(git, range) {
|
|
|
218
219
|
}
|
|
219
220
|
return out;
|
|
220
221
|
}
|
|
221
|
-
/** Visible character width of a string with embedded ANSI color codes. */
|
|
222
|
-
function visibleWidth(s) {
|
|
223
|
-
return s.replace(/\[[0-9;]*m/g, '').length;
|
|
224
|
-
}
|
|
225
|
-
/** Pad string with trailing spaces to a target visible column width. */
|
|
226
|
-
function padVisible(s, width) {
|
|
227
|
-
return s + ' '.repeat(Math.max(0, width - visibleWidth(s)));
|
|
228
|
-
}
|
|
229
222
|
/**
|
|
230
223
|
* Read one repo's status into structured data: branch, resource-level sync (what
|
|
231
224
|
* a pull/push would move), resource-level local edits, and remote URL + commit.
|
|
@@ -752,9 +745,9 @@ export function registerRepoCommands(program) {
|
|
|
752
745
|
await toggle(alias, false);
|
|
753
746
|
});
|
|
754
747
|
repoCmd
|
|
755
|
-
.command('pull [alias]')
|
|
756
|
-
.description('Pull updates. Aliases: "system" (~/.agents/.system/), "user" (~/.agents/), or any registered extra. No arg pulls all.')
|
|
757
|
-
.action(async (alias) => {
|
|
748
|
+
.command('pull [alias] [url]')
|
|
749
|
+
.description('Pull updates. Aliases: "system" (~/.agents/.system/), "user" (~/.agents/), or any registered extra. No arg pulls all. Pass a git URL to git-back a not-yet-cloned user repo: "agents repo pull user <url>".')
|
|
750
|
+
.action(async (alias, url) => {
|
|
758
751
|
const targets = collectRepoTargets(alias);
|
|
759
752
|
if (!targets) {
|
|
760
753
|
process.exitCode = 1;
|
|
@@ -766,8 +759,34 @@ export function registerRepoCommands(program) {
|
|
|
766
759
|
}
|
|
767
760
|
for (const t of targets) {
|
|
768
761
|
if (!fs.existsSync(t.dir) || !isGitRepo(t.dir)) {
|
|
769
|
-
|
|
770
|
-
|
|
762
|
+
// A plain (never-cloned) user repo — setup makes ~/.agents a bare dir and
|
|
763
|
+
// never git-clones it (state.ts ensureAgentsDir), so a fresh/Windows box
|
|
764
|
+
// silently falls out of config sync. If a URL was passed, git-back it in
|
|
765
|
+
// place (keeping local files, backing up any that differ); else guide the
|
|
766
|
+
// user. Only the USER repo gets this — system is cloned by setup, extras
|
|
767
|
+
// by `repo add`.
|
|
768
|
+
if (t.alias === 'user' && url) {
|
|
769
|
+
const spinner = ora(`Git-backing ${t.dir} from ${url}...`).start();
|
|
770
|
+
const res = await adoptRepo(url, t.dir);
|
|
771
|
+
if (!res.success) {
|
|
772
|
+
spinner.fail(`user: could not git-back — ${res.error}`);
|
|
773
|
+
process.exitCode = 1;
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
spinner.succeed(`user: git-backed -> ${res.commit}`);
|
|
777
|
+
if (res.backedUp.length > 0) {
|
|
778
|
+
console.log(chalk.yellow(` backed up ${res.backedUp.length} locally-modified file(s) to ${res.backupDir}`));
|
|
779
|
+
console.log(chalk.gray(` (${res.backedUp.slice(0, 5).join(', ')}${res.backedUp.length > 5 ? ', …' : ''})`));
|
|
780
|
+
}
|
|
781
|
+
// Now a git repo — fall through to the normal pull below.
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
console.log(chalk.yellow(` ${t.alias}: not a git repo, skipping`));
|
|
785
|
+
if (t.alias === 'user') {
|
|
786
|
+
console.log(chalk.gray(' git-back it from your config remote: agents repo pull user <git-url>'));
|
|
787
|
+
}
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
771
790
|
}
|
|
772
791
|
if (t.alias === 'system') {
|
|
773
792
|
// Skip system repo unless explicitly requested
|
|
@@ -58,3 +58,4 @@ export declare function resourceLayout(cols: number, o: {
|
|
|
58
58
|
}): ResourceLayout;
|
|
59
59
|
/** Build the sync targets section showing version pills grouped by agent. */
|
|
60
60
|
export declare function buildTargetsSection(targets: SyncTarget[]): string;
|
|
61
|
+
/** Pad a string to a fixed width, accounting for ANSI escape codes in length calculation. */
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* filtering, and paging.
|
|
8
8
|
*/
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
+
import { truncate, padVisible } from '../lib/format.js';
|
|
10
11
|
import { agentLabel } from '../lib/agents.js';
|
|
11
12
|
import { itemPicker } from '../lib/picker.js';
|
|
12
13
|
import { isInteractiveTerminal, isPromptCancelled, printWithPager } from './utils.js';
|
|
@@ -65,15 +66,15 @@ function filterRows(rows, query) {
|
|
|
65
66
|
}
|
|
66
67
|
/** Row label rendered inside the picker list. */
|
|
67
68
|
function formatPickerRow(row, opts) {
|
|
68
|
-
const name = chalk.cyan(
|
|
69
|
+
const name = chalk.cyan(padVisible(row.name, 22));
|
|
69
70
|
const extra = opts.extraLabel
|
|
70
|
-
? chalk.gray(
|
|
71
|
+
? chalk.gray(padVisible(row.extra ?? '-', 10))
|
|
71
72
|
: '';
|
|
72
73
|
const extra2 = opts.extra2Label
|
|
73
|
-
? chalk.gray(
|
|
74
|
+
? chalk.gray(padVisible(row.extra2 ?? '-', 16))
|
|
74
75
|
: '';
|
|
75
76
|
const descRaw = row.description ? truncate(row.description, 40) : '';
|
|
76
|
-
const desc =
|
|
77
|
+
const desc = padVisible(chalk.gray(descRaw), 42);
|
|
77
78
|
const sync = formatSyncSummary(row.targets, opts);
|
|
78
79
|
return `${name} ${extra}${extra2}${desc} ${sync}`;
|
|
79
80
|
}
|
|
@@ -252,16 +253,3 @@ function formatVersionPill(t) {
|
|
|
252
253
|
}
|
|
253
254
|
}
|
|
254
255
|
/** Pad a string to a fixed width, accounting for ANSI escape codes in length calculation. */
|
|
255
|
-
function padRight(s, width) {
|
|
256
|
-
// Strip ANSI for length calc
|
|
257
|
-
const raw = s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
258
|
-
if (raw.length >= width)
|
|
259
|
-
return s;
|
|
260
|
-
return s + ' '.repeat(width - raw.length);
|
|
261
|
-
}
|
|
262
|
-
/** Truncate a string to a maximum length, appending an ellipsis if needed. */
|
|
263
|
-
function truncate(s, max) {
|
|
264
|
-
if (s.length <= max)
|
|
265
|
-
return s;
|
|
266
|
-
return s.slice(0, max - 1) + '…';
|
|
267
|
-
}
|
|
@@ -9,6 +9,7 @@ import { type Command } from 'commander';
|
|
|
9
9
|
import { SSH_TARGET_RE, assertValidSshTarget } from '../lib/ssh-exec.js';
|
|
10
10
|
import { quoteWin32ExecArg } from '../lib/platform/index.js';
|
|
11
11
|
import { type SecretsBundle, type SecretsPolicy } from '../lib/secrets/bundles.js';
|
|
12
|
+
/** Read all available data from stdin synchronously, trimmed. */
|
|
12
13
|
/**
|
|
13
14
|
* Read the raw `.env` text for `import --from <path|->`. A `-` reads the .env
|
|
14
15
|
* from stdin (the SSH push path: `export --host` pipes the resolved dotenv over
|
package/dist/commands/secrets.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Option } from 'commander';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
+
import { padVisible, readStdinSync } from '../lib/format.js';
|
|
10
11
|
import { terminalWidth, truncateToWidth, stringWidth } from '../lib/session/width.js';
|
|
11
12
|
import * as fs from 'fs';
|
|
12
13
|
import { SSH_TARGET_RE, assertValidSshTarget, sshExec } from '../lib/ssh-exec.js';
|
|
@@ -125,23 +126,6 @@ async function resolveVault(vaultOpt) {
|
|
|
125
126
|
});
|
|
126
127
|
}
|
|
127
128
|
/** Read all available data from stdin synchronously, trimmed. */
|
|
128
|
-
function readStdinSync() {
|
|
129
|
-
const chunks = [];
|
|
130
|
-
const buf = Buffer.alloc(65536);
|
|
131
|
-
while (true) {
|
|
132
|
-
let bytesRead;
|
|
133
|
-
try {
|
|
134
|
-
bytesRead = fs.readSync(0, buf, 0, buf.length, null);
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
if (bytesRead === 0)
|
|
140
|
-
break;
|
|
141
|
-
chunks.push(Buffer.from(buf.subarray(0, bytesRead)));
|
|
142
|
-
}
|
|
143
|
-
return Buffer.concat(chunks).toString('utf-8').trim();
|
|
144
|
-
}
|
|
145
129
|
/**
|
|
146
130
|
* Read the raw `.env` text for `import --from <path|->`. A `-` reads the .env
|
|
147
131
|
* from stdin (the SSH push path: `export --host` pipes the resolved dotenv over
|
|
@@ -257,17 +241,6 @@ async function browseRemote(targets, args, tty) {
|
|
|
257
241
|
process.exit(1);
|
|
258
242
|
}
|
|
259
243
|
/** Strip ANSI escape sequences so padding can be computed on visible width. */
|
|
260
|
-
function visibleWidth(s) {
|
|
261
|
-
// eslint-disable-next-line no-control-regex
|
|
262
|
-
return s.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
263
|
-
}
|
|
264
|
-
/** padEnd that respects ANSI color codes (chalk-wrapped strings have invisible bytes). */
|
|
265
|
-
function padVisible(s, n) {
|
|
266
|
-
const w = visibleWidth(s);
|
|
267
|
-
if (w >= n)
|
|
268
|
-
return s;
|
|
269
|
-
return s + ' '.repeat(n - w);
|
|
270
|
-
}
|
|
271
244
|
/** Render an ISO-8601 timestamp as a compact relative age: "now", "5m", "1h", "3d", "2w", "4mo", "1y". */
|
|
272
245
|
function relativeAge(iso) {
|
|
273
246
|
const t = Date.parse(iso);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
+
import { truncate, humanDuration } from '../lib/format.js';
|
|
10
11
|
import { parseSession, sanitizeForTerminal } from '../lib/session/parse.js';
|
|
11
12
|
import { cleanSessionPrompt, extractSessionTopic } from '../lib/session/prompt.js';
|
|
12
13
|
import { linkPath, relativeToCwd } from '../lib/session/render.js';
|
|
@@ -154,21 +155,6 @@ function extractTiming(events) {
|
|
|
154
155
|
const dur = Number.isNaN(lastMs) ? undefined : humanDuration(Math.max(0, lastMs - firstMs));
|
|
155
156
|
return { startedAgo: ago, duration: dur };
|
|
156
157
|
}
|
|
157
|
-
function humanDuration(ms) {
|
|
158
|
-
const s = Math.floor(ms / 1000);
|
|
159
|
-
if (s < 60)
|
|
160
|
-
return `${s}s`;
|
|
161
|
-
const m = Math.floor(s / 60);
|
|
162
|
-
if (m < 60)
|
|
163
|
-
return `${m}m`;
|
|
164
|
-
const h = Math.floor(m / 60);
|
|
165
|
-
const mm = m % 60;
|
|
166
|
-
if (h < 24)
|
|
167
|
-
return mm ? `${h}h ${mm}m` : `${h}h`;
|
|
168
|
-
const d = Math.floor(h / 24);
|
|
169
|
-
const hh = h % 24;
|
|
170
|
-
return hh ? `${d}d ${hh}h` : `${d}d`;
|
|
171
|
-
}
|
|
172
158
|
function countMessages(events) {
|
|
173
159
|
return events.filter(e => e.type === 'message').length;
|
|
174
160
|
}
|
|
@@ -364,9 +350,6 @@ function renderTodos(todos, termWidth) {
|
|
|
364
350
|
}
|
|
365
351
|
return out;
|
|
366
352
|
}
|
|
367
|
-
function truncate(s, max) {
|
|
368
|
-
return s.length > max ? s.slice(0, max - 1) + '…' : s;
|
|
369
|
-
}
|
|
370
353
|
/** Show an interactive session picker and return the selected session with its action (resume or view). */
|
|
371
354
|
export async function sessionPicker(config) {
|
|
372
355
|
const picked = await itemPicker({
|
|
@@ -12,6 +12,7 @@ import * as os from 'os';
|
|
|
12
12
|
import * as path from 'path';
|
|
13
13
|
import { spawn } from 'child_process';
|
|
14
14
|
import chalk from 'chalk';
|
|
15
|
+
import { truncate, padRight } from '../lib/format.js';
|
|
15
16
|
import ora from 'ora';
|
|
16
17
|
import { SESSION_AGENTS } from '../lib/session/types.js';
|
|
17
18
|
import { discoverArtifacts, readArtifact, resolveArtifact } from '../lib/session/artifacts.js';
|
|
@@ -1349,8 +1350,11 @@ async function renderSession(session, mode, filters, options = {}) {
|
|
|
1349
1350
|
process.stdout.write(renderMarkdown(renderConversationMarkdown(events, { redact: options.noRedact !== true })));
|
|
1350
1351
|
return;
|
|
1351
1352
|
}
|
|
1352
|
-
// json —
|
|
1353
|
-
|
|
1353
|
+
// json — normalized events plus the durable session signals from the state
|
|
1354
|
+
// engine (plan text, PR, worktree, ticket). Pre-1.20.51 emitted a bare event
|
|
1355
|
+
// array; consumers that JSON.parse this now read `output.events` for the
|
|
1356
|
+
// array. See issue #743 (plan surfaced) and CHANGELOG for the shape change.
|
|
1357
|
+
process.stdout.write(renderJson(events, session));
|
|
1354
1358
|
}
|
|
1355
1359
|
function renderTopicCell(label, topic, query, visibleWidth, paddedWidth) {
|
|
1356
1360
|
const lbl = (label ?? '').trim();
|
|
@@ -2289,9 +2293,3 @@ function formatAbsoluteTime(isoTimestamp) {
|
|
|
2289
2293
|
const mm = String(d.getMinutes()).padStart(2, '0');
|
|
2290
2294
|
return `${months[d.getMonth()]} ${d.getDate()} ${hh}:${mm}`;
|
|
2291
2295
|
}
|
|
2292
|
-
function padRight(s, width) {
|
|
2293
|
-
return s.length >= width ? s : s + ' '.repeat(width - s.length);
|
|
2294
|
-
}
|
|
2295
|
-
function truncate(s, max) {
|
|
2296
|
-
return s.length > max ? s.slice(0, max - 1) + '.' : s;
|
|
2297
|
-
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* activity for each team.
|
|
8
8
|
*/
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
+
import { relTime, truncate, humanDuration } from '../lib/format.js';
|
|
10
11
|
import { itemPicker } from '../lib/picker.js';
|
|
11
12
|
const AGENT_LABEL = {
|
|
12
13
|
claude: 'Claude',
|
|
@@ -26,23 +27,6 @@ function statusColor(status) {
|
|
|
26
27
|
default: return chalk.white;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
function relTime(iso) {
|
|
30
|
-
const secs = Math.floor((Date.now() - new Date(iso).getTime()) / 1000);
|
|
31
|
-
if (secs < 10)
|
|
32
|
-
return 'just now';
|
|
33
|
-
if (secs < 60)
|
|
34
|
-
return `${secs}s ago`;
|
|
35
|
-
if (secs < 3600)
|
|
36
|
-
return `${Math.floor(secs / 60)}m ago`;
|
|
37
|
-
if (secs < 86400)
|
|
38
|
-
return `${Math.floor(secs / 3600)}h ago`;
|
|
39
|
-
return `${Math.floor(secs / 86400)}d ago`;
|
|
40
|
-
}
|
|
41
|
-
function truncate(s, n) {
|
|
42
|
-
if (s.length <= n)
|
|
43
|
-
return s;
|
|
44
|
-
return s.slice(0, n - 1) + '…';
|
|
45
|
-
}
|
|
46
30
|
function firstLine(s) {
|
|
47
31
|
// Collapse to the first non-empty line so multi-line messages (markdown,
|
|
48
32
|
// code blocks) render cleanly inside a one-line preview slot.
|
|
@@ -120,21 +104,6 @@ function formatComposition(agents) {
|
|
|
120
104
|
.map(([type, n]) => (n === 1 ? type : `${type}×${n}`));
|
|
121
105
|
return parts.join(' ');
|
|
122
106
|
}
|
|
123
|
-
function humanDuration(ms) {
|
|
124
|
-
const s = Math.floor(ms / 1000);
|
|
125
|
-
if (s < 60)
|
|
126
|
-
return `${s}s`;
|
|
127
|
-
const m = Math.floor(s / 60);
|
|
128
|
-
if (m < 60)
|
|
129
|
-
return `${m}m`;
|
|
130
|
-
const h = Math.floor(m / 60);
|
|
131
|
-
const mm = m % 60;
|
|
132
|
-
if (h < 24)
|
|
133
|
-
return mm ? `${h}h${mm}m` : `${h}h`;
|
|
134
|
-
const d = Math.floor(h / 24);
|
|
135
|
-
const hh = h % 24;
|
|
136
|
-
return hh ? `${d}d${hh}h` : `${d}d`;
|
|
137
|
-
}
|
|
138
107
|
function runtimeSpan(t) {
|
|
139
108
|
const startMs = new Date(t.created_at).getTime();
|
|
140
109
|
const endMs = new Date(t.modified_at).getTime();
|
package/dist/commands/teams.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { die, relTime, truncate, isJsonMode, padRight } from '../lib/format.js';
|
|
2
3
|
import * as fs from 'fs/promises';
|
|
3
4
|
import { addHostOption } from '../lib/hosts/option.js';
|
|
4
5
|
import * as path from 'path';
|
|
@@ -48,13 +49,6 @@ const VALID_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max', 'auto'];
|
|
|
48
49
|
const VALID_CLOUD_PROVIDERS = ['rush', 'codex', 'factory'];
|
|
49
50
|
// Auto-enable JSON mode when piped / not a TTY so AI agent consumers get
|
|
50
51
|
// parseable output by default.
|
|
51
|
-
function isJsonMode(opts) {
|
|
52
|
-
return Boolean(opts.json) || !process.stdout.isTTY;
|
|
53
|
-
}
|
|
54
|
-
function die(msg, code = 1) {
|
|
55
|
-
console.error(chalk.red(msg));
|
|
56
|
-
process.exit(code);
|
|
57
|
-
}
|
|
58
52
|
function statusColor(status) {
|
|
59
53
|
switch (status) {
|
|
60
54
|
case 'pending': return chalk.blue;
|
|
@@ -65,23 +59,6 @@ function statusColor(status) {
|
|
|
65
59
|
default: return chalk.white;
|
|
66
60
|
}
|
|
67
61
|
}
|
|
68
|
-
function relTime(iso) {
|
|
69
|
-
const secs = Math.floor((Date.now() - new Date(iso).getTime()) / 1000);
|
|
70
|
-
if (secs < 10)
|
|
71
|
-
return 'just now';
|
|
72
|
-
if (secs < 60)
|
|
73
|
-
return `${secs}s ago`;
|
|
74
|
-
if (secs < 3600)
|
|
75
|
-
return `${Math.floor(secs / 60)}m ago`;
|
|
76
|
-
if (secs < 86400)
|
|
77
|
-
return `${Math.floor(secs / 3600)}h ago`;
|
|
78
|
-
return `${Math.floor(secs / 86400)}d ago`;
|
|
79
|
-
}
|
|
80
|
-
function truncate(s, n) {
|
|
81
|
-
if (s.length <= n)
|
|
82
|
-
return s;
|
|
83
|
-
return s.slice(0, n - 1) + '…';
|
|
84
|
-
}
|
|
85
62
|
function compactPrompt(s, n = 160) {
|
|
86
63
|
return truncate(s.replace(/\s+/g, ' ').trim(), n);
|
|
87
64
|
}
|
|
@@ -98,9 +75,6 @@ function formatTimestamp(iso) {
|
|
|
98
75
|
minute: '2-digit',
|
|
99
76
|
});
|
|
100
77
|
}
|
|
101
|
-
function padRight(s, width) {
|
|
102
|
-
return s.length >= width ? s : s + ' '.repeat(width - s.length);
|
|
103
|
-
}
|
|
104
78
|
function fullName(type, version) {
|
|
105
79
|
const name = AGENT_NAMES[type];
|
|
106
80
|
return version ? `${name} ${version}` : name;
|
package/dist/commands/tmux.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* agents tmux kill-all [--yes]
|
|
22
22
|
*/
|
|
23
23
|
import chalk from 'chalk';
|
|
24
|
+
import { truncate } from '../lib/format.js';
|
|
24
25
|
import * as path from 'path';
|
|
25
26
|
import { setHelpSections } from '../lib/help.js';
|
|
26
27
|
import { assertTmuxAvailable, attachTmux, capturePane, createSession, getDefaultSocketPath, getTmuxVersion, hasSession, isTmuxInstalled, killAll, killSession, listSessions, readSessionMeta, sendKeys, splitPane, TmuxCommandError, TmuxSessionError, TmuxUnavailableError, } from '../lib/tmux/index.js';
|
|
@@ -395,9 +396,6 @@ function formatAge(secs) {
|
|
|
395
396
|
const h = Math.floor(m / 60);
|
|
396
397
|
return `${h}h ${m % 60}m`;
|
|
397
398
|
}
|
|
398
|
-
function truncate(s, n) {
|
|
399
|
-
return s.length > n ? `${s.slice(0, n - 1)}…` : s;
|
|
400
|
-
}
|
|
401
399
|
function printMeta(m) {
|
|
402
400
|
console.log(` ${chalk.bold('name')} ${m.name}`);
|
|
403
401
|
console.log(` ${chalk.bold('socket')} ${m.socket}`);
|
package/dist/commands/view.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { addHostOption } from '../lib/hosts/option.js';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
+
import { visibleWidth, termLink } from '../lib/format.js';
|
|
3
4
|
import ora from 'ora';
|
|
4
5
|
import * as fs from 'fs';
|
|
5
6
|
import * as path from 'path';
|
|
@@ -67,12 +68,6 @@ function profileKindAndModel(model, planWidth) {
|
|
|
67
68
|
const kind = 'profile'.padEnd(Math.max(planWidth, 'profile'.length));
|
|
68
69
|
return `${kind} ${model}`;
|
|
69
70
|
}
|
|
70
|
-
function termLink(text, filePath) {
|
|
71
|
-
if (!process.stdout.isTTY)
|
|
72
|
-
return text;
|
|
73
|
-
const url = `file://${filePath}`;
|
|
74
|
-
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
75
|
-
}
|
|
76
71
|
/**
|
|
77
72
|
* Resolve a resource path to something the IDE can open inline. When `p` is a
|
|
78
73
|
* directory, OSC 8 file:// links cause IDEs (Cursor/VS Code) to open it as a
|
|
@@ -112,9 +107,6 @@ function formatLastActive(date) {
|
|
|
112
107
|
return chalk.gray(`${days}d ago`);
|
|
113
108
|
return chalk.gray(`${days}d ago`);
|
|
114
109
|
}
|
|
115
|
-
function visibleWidth(s) {
|
|
116
|
-
return s.replace(/\u001b\[[0-9;]*m/g, '').length;
|
|
117
|
-
}
|
|
118
110
|
function compareVersions(a, b) {
|
|
119
111
|
const partsA = a.split('.').map(Number);
|
|
120
112
|
const partsB = b.split('.').map(Number);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { die } from '../lib/format.js';
|
|
2
3
|
import { execFile } from 'child_process';
|
|
3
4
|
import { promisify } from 'util';
|
|
4
5
|
import * as fs from 'fs/promises';
|
|
@@ -8,10 +9,6 @@ import { setHelpSections } from '../lib/help.js';
|
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
9
10
|
const WORKTREE_SUBDIR = path.join('.agents', 'worktrees');
|
|
10
11
|
const BRANCH_PREFIX = 'agents/';
|
|
11
|
-
function die(msg, code = 1) {
|
|
12
|
-
console.error(chalk.red(msg));
|
|
13
|
-
process.exit(code);
|
|
14
|
-
}
|
|
15
12
|
function isValidTerminalId(id) {
|
|
16
13
|
// Allow letters, digits, dot, dash, underscore. Reject anything else so a
|
|
17
14
|
// hostile or buggy caller can't inject path traversal or shell metachars.
|
package/dist/lib/agents.d.ts
CHANGED
|
@@ -52,8 +52,6 @@ export declare function getCliPath(agentId: AgentId): Promise<string | null>;
|
|
|
52
52
|
export declare function getCliState(agentId: AgentId): Promise<CliState>;
|
|
53
53
|
/** Resolve CLI state for all registered agents in parallel. */
|
|
54
54
|
export declare function getAllCliStates(): Promise<Partial<Record<AgentId, CliState>>>;
|
|
55
|
-
/** Check whether the agent's config directory exists on disk. */
|
|
56
|
-
export declare function isConfigured(agentId: AgentId): boolean;
|
|
57
55
|
/** Info about an existing unmanaged agent installation. */
|
|
58
56
|
export interface UnmanagedInstall {
|
|
59
57
|
agentId: AgentId;
|
|
@@ -128,8 +126,6 @@ export declare function resolveLastActive(agentId: AgentId, base: string, config
|
|
|
128
126
|
* Used during init to show approximate session count to user.
|
|
129
127
|
*/
|
|
130
128
|
export declare function countSessionFiles(agentId: AgentId): number;
|
|
131
|
-
/** Check whether a named MCP server is registered with the agent's CLI. */
|
|
132
|
-
export declare function isMcpRegistered(agentId: AgentId, mcpName: string): Promise<boolean>;
|
|
133
129
|
/** Register an MCP server with an agent's CLI via `mcp add`. */
|
|
134
130
|
export declare function registerMcp(agentId: AgentId, name: string, command: string, scope?: 'user' | 'project', transport?: string, options?: {
|
|
135
131
|
home?: string;
|
package/dist/lib/agents.js
CHANGED
|
@@ -21,6 +21,7 @@ import { latestFileMtimeMs } from './fs-walk.js';
|
|
|
21
21
|
import { damerauLevenshtein } from './fuzzy.js';
|
|
22
22
|
import { getCacheDir, getVersionsDir, getShimsDir, getCliVersionCachePath } from './state.js';
|
|
23
23
|
import { resolveVersion, getVersionHomePath, getBinaryPath } from './versions.js';
|
|
24
|
+
import { supports } from './capabilities.js';
|
|
24
25
|
const execFileAsync = promisify(execFile);
|
|
25
26
|
const HOME = os.homedir();
|
|
26
27
|
/**
|
|
@@ -212,7 +213,7 @@ export const AGENTS = {
|
|
|
212
213
|
// Claude Code has no headless Anthropic-hosted dispatch CLI (only
|
|
213
214
|
// --remote-control, which bridges a *local* session). Its cloud is Rush.
|
|
214
215
|
cloudProvider: 'rush',
|
|
215
|
-
capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
|
|
216
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: true, mcpHeaders: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
|
|
216
217
|
},
|
|
217
218
|
// codex hooks: gated to >= 0.116.0 (introduced [features] codex_hooks flag).
|
|
218
219
|
codex: {
|
|
@@ -232,7 +233,7 @@ export const AGENTS = {
|
|
|
232
233
|
variableSyntax: '$ARGUMENTS',
|
|
233
234
|
supportsHooks: true,
|
|
234
235
|
cloudProvider: 'codex',
|
|
235
|
-
capabilities: { hooks: { since: '0.116.0' }, mcp: true, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
|
|
236
|
+
capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
|
|
236
237
|
},
|
|
237
238
|
gemini: {
|
|
238
239
|
id: 'gemini',
|
|
@@ -261,7 +262,7 @@ export const AGENTS = {
|
|
|
261
262
|
url: 'https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/',
|
|
262
263
|
},
|
|
263
264
|
// gemini hooks: shipped in v0.26.0 (Jan 2026); older binaries silently ignore the `hooks` key.
|
|
264
|
-
capabilities: { hooks: { since: '0.26.0' }, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'GEMINI.md' }, workflows: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
|
|
265
|
+
capabilities: { hooks: { since: '0.26.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'GEMINI.md' }, workflows: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
|
|
265
266
|
},
|
|
266
267
|
cursor: {
|
|
267
268
|
id: 'cursor',
|
|
@@ -279,7 +280,7 @@ export const AGENTS = {
|
|
|
279
280
|
format: 'markdown',
|
|
280
281
|
variableSyntax: '$ARGUMENTS',
|
|
281
282
|
supportsHooks: false,
|
|
282
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: '.cursorrules' }, workflows: false, modes: ['edit', 'skip'] },
|
|
283
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: '.cursorrules' }, workflows: false, modes: ['edit', 'skip'] },
|
|
283
284
|
},
|
|
284
285
|
opencode: {
|
|
285
286
|
id: 'opencode',
|
|
@@ -296,7 +297,7 @@ export const AGENTS = {
|
|
|
296
297
|
format: 'markdown',
|
|
297
298
|
variableSyntax: '$ARGUMENTS',
|
|
298
299
|
supportsHooks: false,
|
|
299
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
|
|
300
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
|
|
300
301
|
},
|
|
301
302
|
openclaw: {
|
|
302
303
|
id: 'openclaw',
|
|
@@ -314,7 +315,7 @@ export const AGENTS = {
|
|
|
314
315
|
format: 'markdown',
|
|
315
316
|
variableSyntax: '{{ARGUMENTS}}',
|
|
316
317
|
supportsHooks: true,
|
|
317
|
-
capabilities: { hooks: true, mcp: true, allowlist: false, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
|
|
318
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
|
|
318
319
|
},
|
|
319
320
|
copilot: {
|
|
320
321
|
id: 'copilot',
|
|
@@ -336,7 +337,7 @@ export const AGENTS = {
|
|
|
336
337
|
format: 'markdown',
|
|
337
338
|
variableSyntax: '$ARGUMENTS',
|
|
338
339
|
supportsHooks: false,
|
|
339
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'auto', 'skip'] },
|
|
340
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'auto', 'skip'] },
|
|
340
341
|
},
|
|
341
342
|
amp: {
|
|
342
343
|
id: 'amp',
|
|
@@ -353,7 +354,7 @@ export const AGENTS = {
|
|
|
353
354
|
format: 'markdown',
|
|
354
355
|
variableSyntax: '$ARGUMENTS',
|
|
355
356
|
supportsHooks: false,
|
|
356
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
|
|
357
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
|
|
357
358
|
},
|
|
358
359
|
kiro: {
|
|
359
360
|
id: 'kiro',
|
|
@@ -371,7 +372,7 @@ export const AGENTS = {
|
|
|
371
372
|
format: 'markdown',
|
|
372
373
|
variableSyntax: '$ARGUMENTS',
|
|
373
374
|
supportsHooks: false,
|
|
374
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit'] },
|
|
375
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit'] },
|
|
375
376
|
},
|
|
376
377
|
goose: {
|
|
377
378
|
id: 'goose',
|
|
@@ -389,7 +390,7 @@ export const AGENTS = {
|
|
|
389
390
|
format: 'markdown',
|
|
390
391
|
variableSyntax: '$ARGUMENTS',
|
|
391
392
|
supportsHooks: false,
|
|
392
|
-
capabilities: { hooks: false, mcp: true, allowlist: false, skills: false, commands: false, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit'] },
|
|
393
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: false, commands: false, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit'] },
|
|
393
394
|
},
|
|
394
395
|
// Google Antigravity CLI (`agy`) — official replacement for Gemini CLI as of IO 2026.
|
|
395
396
|
// configDir nests inside `~/.gemini/` since agy shares the parent dir with the Gemini
|
|
@@ -418,7 +419,7 @@ export const AGENTS = {
|
|
|
418
419
|
variableSyntax: '{{args}}',
|
|
419
420
|
supportsHooks: true,
|
|
420
421
|
cloudProvider: 'antigravity',
|
|
421
|
-
capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit', 'skip'], rulesImports: false },
|
|
422
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit', 'skip'], rulesImports: false },
|
|
422
423
|
},
|
|
423
424
|
// xAI Grok Build CLI (`grok`) — early beta, SuperGrok Heavy. Auth via OAuth on
|
|
424
425
|
// first launch, or XAI_API_KEY env var for headless. MCP servers configured inline
|
|
@@ -445,6 +446,8 @@ export const AGENTS = {
|
|
|
445
446
|
capabilities: {
|
|
446
447
|
hooks: true,
|
|
447
448
|
mcp: true,
|
|
449
|
+
mcpHttp: false,
|
|
450
|
+
mcpHeaders: false,
|
|
448
451
|
allowlist: true, // maps to Grok's granular Bash/Edit/Write/Read/Grep/WebFetch/MCPTool rules
|
|
449
452
|
skills: true,
|
|
450
453
|
commands: false, // covered by skills
|
|
@@ -481,6 +484,8 @@ export const AGENTS = {
|
|
|
481
484
|
capabilities: {
|
|
482
485
|
hooks: true,
|
|
483
486
|
mcp: true,
|
|
487
|
+
mcpHttp: false,
|
|
488
|
+
mcpHeaders: false,
|
|
484
489
|
allowlist: true,
|
|
485
490
|
skills: true,
|
|
486
491
|
commands: false,
|
|
@@ -540,6 +545,8 @@ export const AGENTS = {
|
|
|
540
545
|
capabilities: {
|
|
541
546
|
hooks: true,
|
|
542
547
|
mcp: true,
|
|
548
|
+
mcpHttp: false,
|
|
549
|
+
mcpHeaders: false,
|
|
543
550
|
allowlist: false,
|
|
544
551
|
skills: false,
|
|
545
552
|
commands: true,
|
|
@@ -708,11 +715,6 @@ export async function getAllCliStates() {
|
|
|
708
715
|
}
|
|
709
716
|
return states;
|
|
710
717
|
}
|
|
711
|
-
/** Check whether the agent's config directory exists on disk. */
|
|
712
|
-
export function isConfigured(agentId) {
|
|
713
|
-
const agent = AGENTS[agentId];
|
|
714
|
-
return fs.existsSync(agent.configDir);
|
|
715
|
-
}
|
|
716
718
|
/**
|
|
717
719
|
* Agents that `agents setup` probes for pre-existing native installations
|
|
718
720
|
* (i.e., a config dir present before agents-cli took over). Add an agent here
|
|
@@ -1310,31 +1312,16 @@ function buildIdentityKey(agentId, parts) {
|
|
|
1310
1312
|
return null;
|
|
1311
1313
|
return `${agentId}:${encoded.join(':')}`;
|
|
1312
1314
|
}
|
|
1313
|
-
/** Check whether a named MCP server is registered with the agent's CLI. */
|
|
1314
|
-
export async function isMcpRegistered(agentId, mcpName) {
|
|
1315
|
-
const agent = AGENTS[agentId];
|
|
1316
|
-
if (!agent.capabilities.mcp || !(await isCliInstalled(agentId))) {
|
|
1317
|
-
return false;
|
|
1318
|
-
}
|
|
1319
|
-
try {
|
|
1320
|
-
const { stdout } = await execFileAsync(agent.cliCommand, ['mcp', 'list']);
|
|
1321
|
-
return stdout.toLowerCase().includes(mcpName.toLowerCase());
|
|
1322
|
-
}
|
|
1323
|
-
catch {
|
|
1324
|
-
/* mcp list command failed */
|
|
1325
|
-
return false;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
1315
|
/** Register an MCP server with an agent's CLI via `mcp add`. */
|
|
1329
1316
|
export async function registerMcp(agentId, name, command, scope = 'user', transport = 'stdio', options) {
|
|
1330
1317
|
const agent = AGENTS[agentId];
|
|
1331
1318
|
if (!agent.capabilities.mcp) {
|
|
1332
1319
|
return { success: false, error: 'Agent does not support MCP' };
|
|
1333
1320
|
}
|
|
1334
|
-
if (transport === 'http' && agentId
|
|
1321
|
+
if (transport === 'http' && !supports(agentId, 'mcpHttp').ok) {
|
|
1335
1322
|
return { success: false, error: 'skipped: agent does not support HTTP MCP registration' };
|
|
1336
1323
|
}
|
|
1337
|
-
if (transport === 'http' && options?.headers && Object.keys(options.headers).length > 0 && agentId
|
|
1324
|
+
if (transport === 'http' && options?.headers && Object.keys(options.headers).length > 0 && !supports(agentId, 'mcpHeaders').ok) {
|
|
1338
1325
|
return { success: false, error: 'skipped: HTTP MCP headers are only supported for Claude registration' };
|
|
1339
1326
|
}
|
|
1340
1327
|
if (!options?.binary && !(await isCliInstalled(agentId))) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concrete Linear gateway for auto-dispatch.
|
|
3
|
+
*
|
|
4
|
+
* Talks to the Linear GraphQL API. The API key is read from the LINEAR_API_KEY
|
|
5
|
+
* env var, falling back to the macOS keychain generic-password `linear-api-key`
|
|
6
|
+
* (how the rest of the stack stores it). If no key is resolvable, the gateway is
|
|
7
|
+
* absent and the daemon skips auto-dispatch entirely.
|
|
8
|
+
*
|
|
9
|
+
* "Started" (Doing) and "unstarted" (Todo) are Linear state *types*, so we never
|
|
10
|
+
* hardcode a team's custom state names. `startIssue` resolves the team's first
|
|
11
|
+
* started-type state (cached per team) and moves the issue there — the existing
|
|
12
|
+
* factory webhook turns that transition into a real run.
|
|
13
|
+
*/
|
|
14
|
+
import type { LinearGateway } from './auto-dispatch.js';
|
|
15
|
+
/** Resolve the Linear API key from env or (on macOS) the keychain. Null if absent. */
|
|
16
|
+
export declare function resolveLinearApiKey(): string | null;
|
|
17
|
+
/** Build a real Linear gateway, or null when no API key is configured. */
|
|
18
|
+
export declare function createLinearGateway(): LinearGateway | null;
|