@tiny-fish/cli 0.35.1-next.291 → 0.36.0
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/dist/commands/connect.d.ts +1 -5
- package/dist/commands/connect.js +31 -41
- package/dist/commands/doctor.js +2 -11
- package/dist/commands/onboard.js +15 -16
- package/dist/lib/connect-all-auth.js +3 -2
- package/dist/lib/connect-all-summary.js +2 -2
- package/dist/lib/connect-all.js +9 -27
- package/dist/lib/connect-clients.d.ts +0 -9
- package/dist/lib/connect-clients.js +1 -25
- package/dist/lib/harness-spec.d.ts +4 -0
- package/package.json +1 -1
|
@@ -23,11 +23,6 @@ export interface NativeConnectOptions {
|
|
|
23
23
|
/** Targeted connects only: --all keeps its per-harness failed row. */
|
|
24
24
|
fallbackWhenMissing?: boolean;
|
|
25
25
|
}
|
|
26
|
-
export declare function connectClaudeCode(options: NativeConnectOptions): Promise<ConnectAuthMode | undefined>;
|
|
27
|
-
export declare function connectCodex(options: NativeConnectOptions): Promise<ConnectAuthMode | undefined>;
|
|
28
|
-
export declare function connectGrok(options: NativeConnectOptions): Promise<ConnectAuthMode | undefined>;
|
|
29
|
-
export declare function connectHermes(options: NativeConnectOptions): Promise<ConnectAuthMode | undefined>;
|
|
30
|
-
export declare function connectOpencode(options: NativeConnectOptions): Promise<ConnectAuthMode | undefined>;
|
|
31
26
|
export declare function connectOpenClaw(options: {
|
|
32
27
|
apiKey?: string;
|
|
33
28
|
mcpUrl: string;
|
|
@@ -50,4 +45,5 @@ export declare function connectCursor(options: {
|
|
|
50
45
|
verbose?: boolean;
|
|
51
46
|
}): Promise<void>;
|
|
52
47
|
export declare function launchAgent(client: AgentClient): Promise<void>;
|
|
48
|
+
export declare function connectHarness(harness: AgentClient, options: NativeConnectOptions): Promise<ConnectAuthMode | undefined>;
|
|
53
49
|
export declare function registerConnect(program: Command): void;
|
package/dist/commands/connect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import spawn from 'cross-spawn';
|
|
2
2
|
import { CONNECT_SOURCE, persistApiKeyToEnvironment, saveConnectContext, validateKeyFormat, validatedApiKey, } from '../lib/auth.js';
|
|
3
|
-
import {
|
|
3
|
+
import { CURSOR_SKILL_TARGET, OPENCLAW, OPENCLAW_SKILL_INSTALL_ARGS, NATIVE_BY_HARNESS, launchNativeMcpClient, launchOpenClawWalkthrough, openExternalUrl, } from '../lib/connect-clients.js';
|
|
4
4
|
import { captureStdio, capturedOutput, HoistedFailureReportedError, installTinyFishCli, replay, SKILL_INSTALL_TIMEOUT_MS, STEP_MAX_BUFFER, } from '../lib/cli-install.js';
|
|
5
5
|
import { ensureCliAuthenticated } from '../lib/connect-auth.js';
|
|
6
6
|
import { createStdinPrompt, runCliFallback } from '../lib/connect-fallback.js';
|
|
@@ -8,7 +8,7 @@ import { installWebSkill } from '../lib/skill-install.js';
|
|
|
8
8
|
import { ConnectInterruptedError, ConnectStepError, createConnectTelemetry, finishSetupCommand, requireCommandSupport, runGuarded, settle, SignInTimeoutError, spawnStepError, stageDurationOf, withStageDuration, spawnRemoval, } from '../lib/connect-runtime.js';
|
|
9
9
|
import { cursorInstallDeeplink, cursorMcpPath, readCursorTinyfishEntry, writeCursorMcpConfig, } from '../lib/cursor-config.js';
|
|
10
10
|
import { runConnectAll } from '../lib/connect-all.js';
|
|
11
|
-
import { AuthMode, RELOAD_ACTION } from '../lib/harness-detect.js';
|
|
11
|
+
import { ALL_HARNESSES, AuthMode, RELOAD_ACTION } from '../lib/harness-detect.js';
|
|
12
12
|
import { detectHumanInitiated } from '../lib/harness.js';
|
|
13
13
|
import { TINYFISH_API_KEY_VAR } from '../lib/constants.js';
|
|
14
14
|
import { emitNotice } from '../lib/notice.js';
|
|
@@ -442,21 +442,6 @@ function trackPostInstallFailure(displayName, state, telemetry, error) {
|
|
|
442
442
|
errLine(`The ${displayName} MCP connection succeeded, but a finishing step ` +
|
|
443
443
|
`(${state.stage}) failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
444
444
|
}
|
|
445
|
-
export async function connectClaudeCode(options) {
|
|
446
|
-
return connectNativeMcpClient(CLAUDE_CODE, options);
|
|
447
|
-
}
|
|
448
|
-
export async function connectCodex(options) {
|
|
449
|
-
return connectNativeMcpClient(CODEX, options);
|
|
450
|
-
}
|
|
451
|
-
export async function connectGrok(options) {
|
|
452
|
-
return connectNativeMcpClient(GROK, options);
|
|
453
|
-
}
|
|
454
|
-
export async function connectHermes(options) {
|
|
455
|
-
return connectNativeMcpClient(HERMES, options);
|
|
456
|
-
}
|
|
457
|
-
export async function connectOpencode(options) {
|
|
458
|
-
return connectNativeMcpClient(OPENCODE, options);
|
|
459
|
-
}
|
|
460
445
|
export async function connectOpenClaw(options) {
|
|
461
446
|
const telemetry = createConnectTelemetry(options.mcpUrl, 'openclaw', options);
|
|
462
447
|
// Skill install, no MCP server — never degraded, and false keeps it inside a `= false` filter.
|
|
@@ -620,38 +605,46 @@ function launchCursorDeeplink(mcpUrl) {
|
|
|
620
605
|
const result = openExternalUrl(cursorInstallDeeplink(mcpUrl));
|
|
621
606
|
return !result.error && result.status === 0;
|
|
622
607
|
}
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
'claude-code': () => launchNativeMcpClient(CLAUDE_CODE),
|
|
626
|
-
codex: () => launchNativeMcpClient(CODEX),
|
|
627
|
-
grok: () => launchNativeMcpClient(GROK),
|
|
628
|
-
hermes: () => launchNativeMcpClient(HERMES),
|
|
629
|
-
opencode: () => launchNativeMcpClient(OPENCODE),
|
|
608
|
+
/** Total over the non-native harnesses: a new one fails to compile until wired here. */
|
|
609
|
+
const LAUNCH_OVERRIDES = {
|
|
630
610
|
openclaw: launchOpenClawWalkthrough,
|
|
631
611
|
// Cursor has no chat CLI to launch.
|
|
632
612
|
cursor: () => {
|
|
633
613
|
throw new Error('Cursor has no launchable walkthrough. Open Cursor and start chatting.');
|
|
634
614
|
},
|
|
635
615
|
};
|
|
616
|
+
const isNonNative = (harness) => harness in LAUNCH_OVERRIDES;
|
|
636
617
|
export async function launchAgent(client) {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
// `connect cursor --launch` still connects; it just cannot launch after.
|
|
618
|
+
if (isNonNative(client))
|
|
619
|
+
return void (await LAUNCH_OVERRIDES[client]());
|
|
620
|
+
await launchNativeMcpClient(requireNative(client));
|
|
621
|
+
}
|
|
622
|
+
/** Total over the non-native harnesses; native harnesses use the descriptor engine. */
|
|
623
|
+
const CONNECTOR_OVERRIDES = {
|
|
624
|
+
openclaw: async (options) => {
|
|
625
|
+
await connectOpenClaw(options);
|
|
626
|
+
return undefined;
|
|
627
|
+
},
|
|
648
628
|
cursor: async (options) => {
|
|
649
629
|
await connectCursor(options);
|
|
630
|
+
// `connect cursor --launch` still connects; it just cannot launch after.
|
|
650
631
|
if (options.launch) {
|
|
651
632
|
errLine('Cursor has no launchable walkthrough. Open Cursor and start chatting.');
|
|
652
633
|
}
|
|
634
|
+
return undefined;
|
|
653
635
|
},
|
|
654
636
|
};
|
|
637
|
+
function requireNative(harness) {
|
|
638
|
+
const client = NATIVE_BY_HARNESS.get(harness);
|
|
639
|
+
if (!client)
|
|
640
|
+
throw new Error(`No native MCP descriptor for ${harness}`);
|
|
641
|
+
return client;
|
|
642
|
+
}
|
|
643
|
+
export async function connectHarness(harness, options) {
|
|
644
|
+
if (isNonNative(harness))
|
|
645
|
+
return CONNECTOR_OVERRIDES[harness](options);
|
|
646
|
+
return connectNativeMcpClient(requireNative(harness), options);
|
|
647
|
+
}
|
|
655
648
|
function parseMcpUrlOption(options) {
|
|
656
649
|
let mcpUrl = options.url ?? DEFAULT_MCP_URL;
|
|
657
650
|
if (mcpUrl.startsWith('-')) {
|
|
@@ -729,13 +722,10 @@ async function connectSingleClient(client, options, mcpUrl, attemptId) {
|
|
|
729
722
|
fallbackWhenMissing: true,
|
|
730
723
|
verbose: options.verbose ?? false,
|
|
731
724
|
};
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
throw new Error('Unsupported client: ' +
|
|
735
|
-
client +
|
|
736
|
-
'. Supported clients: claude-code, codex, cursor, grok, hermes, openclaw, opencode');
|
|
725
|
+
if (!ALL_HARNESSES.includes(client)) {
|
|
726
|
+
throw new Error('Unsupported client: ' + client + '. Supported clients: ' + ALL_HARNESSES.join(', '));
|
|
737
727
|
}
|
|
738
|
-
await
|
|
728
|
+
await connectHarness(client, connectOptions);
|
|
739
729
|
}
|
|
740
730
|
export function registerConnect(program) {
|
|
741
731
|
program
|
package/dist/commands/doctor.js
CHANGED
|
@@ -11,7 +11,7 @@ import { verifyMcpAuth, verifyMcpHealth } from '../lib/verify.js';
|
|
|
11
11
|
import { resolveHermesHome } from '../lib/hermes-env.js';
|
|
12
12
|
import { hermesWebBackendsOurs, readHermesPluginStatus, } from '../lib/hermes-plugin.js';
|
|
13
13
|
import { DEFAULT_MCP_URL, endpointOf, isDefaultEndpoint } from '../lib/mcp-endpoint.js';
|
|
14
|
-
import {
|
|
14
|
+
import { connectHarness } from './connect.js';
|
|
15
15
|
// A green auth mode with a red auth call proves nothing, so the call's verdict gates the claim.
|
|
16
16
|
function provesHarnessReach(status, keyAuth, healthOk, mcpUrl) {
|
|
17
17
|
// An endpoint doctor could not reach proves nothing about a harness pointed at it.
|
|
@@ -375,21 +375,12 @@ function runAuthLogin() {
|
|
|
375
375
|
throw new Error(`\`tinyfish auth login\` exited ${result.status ?? 'unknown'}`);
|
|
376
376
|
}
|
|
377
377
|
}
|
|
378
|
-
const CONNECTORS = {
|
|
379
|
-
'claude-code': (opts) => connectClaudeCode({ ...opts, launch: false }),
|
|
380
|
-
codex: (opts) => connectCodex({ ...opts, launch: false }),
|
|
381
|
-
cursor: (opts) => connectCursor(opts),
|
|
382
|
-
grok: (opts) => connectGrok({ ...opts, launch: false }),
|
|
383
|
-
hermes: (opts) => connectHermes({ ...opts, launch: false }),
|
|
384
|
-
openclaw: (opts) => connectOpenClaw({ ...opts, launch: false }),
|
|
385
|
-
opencode: (opts) => connectOpencode({ ...opts, launch: false }),
|
|
386
|
-
};
|
|
387
378
|
/** Dispatches on `action`, never on a null harness: that conflation made the credential fix a no-op. */
|
|
388
379
|
async function runRepair(repair, mcpUrl) {
|
|
389
380
|
if (repair.action === 'auth-login')
|
|
390
381
|
return runAuthLogin();
|
|
391
382
|
if (repair.harness)
|
|
392
|
-
await
|
|
383
|
+
await connectHarness(repair.harness, { mcpUrl, launch: false });
|
|
393
384
|
}
|
|
394
385
|
export async function applyRepairs(repairs, options) {
|
|
395
386
|
const interactive = detectHumanInitiated();
|
package/dist/commands/onboard.js
CHANGED
|
@@ -3,19 +3,22 @@ import { getApiKey, saveConfig } from '../lib/auth.js';
|
|
|
3
3
|
import { fetchContentGet, searchQuery } from '../lib/client.js';
|
|
4
4
|
import { BASE_URL, TINYFISH_API_KEY_VAR } from '../lib/constants.js';
|
|
5
5
|
import { errLine, handleApiError, outLine } from '../lib/output.js';
|
|
6
|
-
import {
|
|
6
|
+
import { connectHarness, launchAgent } from './connect.js';
|
|
7
|
+
import { HARNESS_DISPLAY_NAMES } from '../lib/harness-detect.js';
|
|
7
8
|
const EXAMPLE_QUERIES = [
|
|
8
9
|
'What are recent changes in the AI dev stack? Give me the top 5 changes that matter, why they matter, and any action needed.',
|
|
9
10
|
'Find a new-grad software engineering role posted in the last 24 hours and fetch the job description.',
|
|
10
11
|
"Analyse today's market and recommend the best investments for $5,000 with expected returns and risks.",
|
|
11
12
|
];
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
/** Onboarding's own shortlist, not every harness; order is the menu order. */
|
|
14
|
+
const AGENT_IDS = [
|
|
15
|
+
'claude-code',
|
|
16
|
+
'codex',
|
|
17
|
+
'hermes',
|
|
18
|
+
'openclaw',
|
|
19
|
+
'opencode',
|
|
18
20
|
];
|
|
21
|
+
const AGENTS = AGENT_IDS.map((id) => ({ id, label: HARNESS_DISPLAY_NAMES[id] }));
|
|
19
22
|
function cleanFetchedContent(text) {
|
|
20
23
|
const cleaned = text
|
|
21
24
|
.replace(/!\[[^\]]*\]\([^)]+\)/g, '')
|
|
@@ -82,16 +85,12 @@ const page = await client.fetch.getContents({ urls: [topUrl], format: "markdown"
|
|
|
82
85
|
console.log(page.results[0]?.text);`;
|
|
83
86
|
}
|
|
84
87
|
async function connectAgent(id) {
|
|
85
|
-
const agent = AGENTS.find((candidate) => candidate.id === id);
|
|
86
|
-
if (!agent)
|
|
87
|
-
return;
|
|
88
88
|
const mcpUrl = new URL('/mcp', BASE_URL).toString();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
89
|
+
await connectHarness(id, {
|
|
90
|
+
mcpUrl,
|
|
91
|
+
launch: false,
|
|
92
|
+
...(id === 'openclaw' ? { cliInstall: false } : {}),
|
|
93
|
+
});
|
|
95
94
|
}
|
|
96
95
|
function parseSelectedAgent(value, selected) {
|
|
97
96
|
if (!value.trim())
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NATIVE_BY_HARNESS } from './connect-clients.js';
|
|
2
|
+
import { harnessSpec } from './harness-spec.js';
|
|
2
3
|
import { ConnectInterruptedError, ConnectStepError, SignInTimeoutError, } from './connect-runtime.js';
|
|
3
4
|
import { ALL_HARNESSES, HARNESS_DISPLAY_NAMES as DISPLAY_NAMES, } from './harness-detect.js';
|
|
4
5
|
import { errLine } from './output.js';
|
|
@@ -25,7 +26,7 @@ const KEY_REQUIRED = new Set(ALL_HARNESSES.filter((harness) => !!NATIVE_BY_HARNE
|
|
|
25
26
|
function headlessGate(base, oauthBudget, fixCommand) {
|
|
26
27
|
const { harness } = base;
|
|
27
28
|
// A CLI-login sign-in is interactive-only — unboundable headless.
|
|
28
|
-
if (
|
|
29
|
+
if (harnessSpec(harness).signInViaCliLogin) {
|
|
29
30
|
return {
|
|
30
31
|
result: { ...base, outcome: 'auth_pending', fixCommand },
|
|
31
32
|
headlessAuth: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { HARNESS_CAPABILITIES } from './connect-clients.js';
|
|
2
1
|
import { finishSetupCommand } from './connect-runtime.js';
|
|
2
|
+
import { harnessSpec } from './harness-spec.js';
|
|
3
3
|
import { HARNESS_COMMANDS, HARNESS_DISPLAY_NAMES as DISPLAY_NAMES, RELOAD_ACTION, } from './harness-detect.js';
|
|
4
4
|
// Naming a harness the user does not have is the noise PF-3708 cut.
|
|
5
5
|
const HIDDEN_ONCE_SOMETHING_WORKED = new Set([
|
|
@@ -83,7 +83,7 @@ function installedSummaryLine(result) {
|
|
|
83
83
|
`Fix: ${finishSetupCommand(result.harness)}`);
|
|
84
84
|
}
|
|
85
85
|
// A keyed install has no sign-in left, so the deferred-auth warning would contradict it.
|
|
86
|
-
const authDeferred =
|
|
86
|
+
const authDeferred = harnessSpec(result.harness).authDeferredAtInstall && !result.keyAuthed;
|
|
87
87
|
const verifyLabel = result.verifyDepth ? `verified: ${result.verifyDepth}` : 'not verified';
|
|
88
88
|
if (authDeferred || result.verifyOk === false) {
|
|
89
89
|
const reason = authDeferred
|
package/dist/lib/connect-all.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as readline from 'node:readline/promises';
|
|
2
|
+
import { harnessSpec } from './harness-spec.js';
|
|
2
3
|
import spawn from 'cross-spawn';
|
|
3
|
-
import {
|
|
4
|
+
import { connectHarness } from '../commands/connect.js';
|
|
4
5
|
import { loadConfig, validatedApiKey } from './auth.js';
|
|
5
6
|
import { installTinyFishCli, } from './cli-install.js';
|
|
6
7
|
import { authGate, mapConnectError, OAUTH_SIGN_IN_TIMEOUT_MS } from './connect-all-auth.js';
|
|
7
8
|
import { actionable, computeExitCode, reloadHint, renderSummary } from './connect-all-summary.js';
|
|
8
9
|
import { planText, uninstallHarness, uninstallPlanText } from './connect-all-uninstall.js';
|
|
9
|
-
import {
|
|
10
|
+
import { NATIVE_BY_HARNESS } from './connect-clients.js';
|
|
10
11
|
import { createStdinPrompt, runCliFallback } from './connect-fallback.js';
|
|
11
12
|
import { ConnectInterruptedError } from './connect-runtime.js';
|
|
12
13
|
import { pickHarnesses } from './connect-picker.js';
|
|
@@ -18,26 +19,9 @@ import { sendSetupCompleted, sendSetupStarted, } from './setup-telemetry.js';
|
|
|
18
19
|
import { verifyMcpAuth, verifyMcpHealth } from './verify.js';
|
|
19
20
|
const STEP_INDENT = ' ';
|
|
20
21
|
// Derived: a native add carries the key, or the CLI holds it itself.
|
|
21
|
-
const KEY_AUTH_CAPABLE = new Set(ALL_HARNESSES.filter((harness) => !!NATIVE_BY_HARNESS.get(harness)?.keyAuth ||
|
|
22
|
-
// A new harness fails to compile until wired here.
|
|
23
|
-
const HARNESS_CONNECTORS = {
|
|
24
|
-
'claude-code': connectClaudeCode,
|
|
25
|
-
codex: connectCodex,
|
|
26
|
-
grok: connectGrok,
|
|
27
|
-
hermes: connectHermes,
|
|
28
|
-
opencode: connectOpencode,
|
|
29
|
-
// Without the key the child shells an interactive `tinyfish auth login`.
|
|
30
|
-
openclaw: async (base) => {
|
|
31
|
-
await connectOpenClaw(base);
|
|
32
|
-
return undefined;
|
|
33
|
-
},
|
|
34
|
-
cursor: async (base) => {
|
|
35
|
-
await connectCursor({ apiKey: base.apiKey, mcpUrl: base.mcpUrl, verbose: base.verbose });
|
|
36
|
-
return undefined;
|
|
37
|
-
},
|
|
38
|
-
};
|
|
22
|
+
const KEY_AUTH_CAPABLE = new Set(ALL_HARNESSES.filter((harness) => !!NATIVE_BY_HARNESS.get(harness)?.keyAuth || harnessSpec(harness).keyHeldByCli));
|
|
39
23
|
async function runHarnessConnect(harness, opts, extras) {
|
|
40
|
-
return
|
|
24
|
+
return connectHarness(harness, {
|
|
41
25
|
apiKey: opts.apiKey,
|
|
42
26
|
mcpUrl: opts.mcpUrl,
|
|
43
27
|
launch: false,
|
|
@@ -70,7 +54,7 @@ function runHoistedCliInstall(verbose) {
|
|
|
70
54
|
async function verifyHarness(harness, mcpUrl, keyed, apiKey) {
|
|
71
55
|
const health = await verifyMcpHealth(mcpUrl);
|
|
72
56
|
// The own-OAuth harnesses are auth-checkable only in key mode (key-authed config).
|
|
73
|
-
const capable =
|
|
57
|
+
const capable = harnessSpec(harness).canVerifyAuth || keyed;
|
|
74
58
|
if (!capable || !health.ok)
|
|
75
59
|
return health;
|
|
76
60
|
if (!apiKey)
|
|
@@ -158,9 +142,7 @@ function noWorkOutcome(d, uninstall) {
|
|
|
158
142
|
}
|
|
159
143
|
/** A config-writing connect needs no binary; every other harness spawns one. */
|
|
160
144
|
function isStaleConfig(detection) {
|
|
161
|
-
return (detection.detected &&
|
|
162
|
-
!detection.binaryOnPath &&
|
|
163
|
-
!HARNESS_CAPABILITIES[detection.harness].cliWritesConfig);
|
|
145
|
+
return (detection.detected && !detection.binaryOnPath && !harnessSpec(detection.harness).cliWritesConfig);
|
|
164
146
|
}
|
|
165
147
|
async function resolvePick(detections, opts, prompt) {
|
|
166
148
|
let deselected = new Set();
|
|
@@ -187,7 +169,7 @@ function hoistCliIfNeeded(detections, opts, willAttempt) {
|
|
|
187
169
|
!opts.uninstall &&
|
|
188
170
|
detections.some((d) => d.detected &&
|
|
189
171
|
d.binaryOnPath &&
|
|
190
|
-
!
|
|
172
|
+
!harnessSpec(d.harness).cliWritesConfig &&
|
|
191
173
|
willAttempt(d.harness));
|
|
192
174
|
if (!cliNeeded)
|
|
193
175
|
return undefined;
|
|
@@ -339,7 +321,7 @@ export async function runConnectAll(opts) {
|
|
|
339
321
|
// Mirrors processHarness's gate: headless, only keyless CLI-login harnesses skip.
|
|
340
322
|
const willAttempt = (harness) => !deselected.has(harness) &&
|
|
341
323
|
(isTTY ||
|
|
342
|
-
!
|
|
324
|
+
!harnessSpec(harness).signInViaCliLogin ||
|
|
343
325
|
(!!gateKey && KEY_AUTH_CAPABLE.has(harness)));
|
|
344
326
|
const hoisted = hoistCliIfNeeded(detections, opts, willAttempt);
|
|
345
327
|
if (hoisted === 'interrupted') {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import spawn from 'cross-spawn';
|
|
2
|
-
import { type Harness } from './harness-detect.js';
|
|
3
2
|
import { type AgentClient, type SupportedCommand } from './connect-runtime.js';
|
|
4
3
|
import { type HarnessSpec, type SkillAgent } from './harness-spec.js';
|
|
5
4
|
export declare const OPENCLAW_SKILL_INSTALL_ARGS: readonly string[];
|
|
@@ -97,15 +96,7 @@ export declare function hermesRegistrationEnabled(home: string): {
|
|
|
97
96
|
export declare const NATIVE_MCP_CLIENTS: readonly NativeMcpClient[];
|
|
98
97
|
/** Native descriptor by harness id; Cursor and OpenClaw have none. */
|
|
99
98
|
export declare const NATIVE_BY_HARNESS: Map<"openclaw" | "grok" | "cursor" | "codex" | "hermes" | "opencode" | "claude-code", NativeMcpClient>;
|
|
100
|
-
export declare const CLAUDE_CODE: NativeMcpClient;
|
|
101
|
-
export declare const CODEX: NativeMcpClient;
|
|
102
|
-
export declare const GROK: NativeMcpClient;
|
|
103
|
-
export declare const HERMES: NativeMcpClient;
|
|
104
|
-
export declare const OPENCODE: NativeMcpClient;
|
|
105
99
|
export declare const OPENCLAW: SupportedCommand;
|
|
106
|
-
/** Traits with no native descriptor to live on (Cursor and OpenClaw included). */
|
|
107
|
-
export type HarnessCapabilities = Pick<HarnessSpec, 'signInViaCliLogin' | 'canVerifyAuth' | 'keyHeldByCli' | 'cliWritesConfig' | 'authDeferredAtInstall'>;
|
|
108
|
-
export declare const HARNESS_CAPABILITIES: Record<Harness, HarnessCapabilities>;
|
|
109
100
|
/** "printed" = handed to the user to paste; the walkthrough was never started for them. */
|
|
110
101
|
export type WalkthroughOutcome = 'launched' | 'printed';
|
|
111
102
|
/** Awaited before the handover; track+flush must land pre-block. */
|
|
@@ -5,7 +5,7 @@ import { harnessConfigPath } from './harness-detect.js';
|
|
|
5
5
|
import { errLine, sanitizeLine, warnLine } from './output.js';
|
|
6
6
|
import { ConnectStepError, spawnStepError, } from './connect-runtime.js';
|
|
7
7
|
import { TINYFISH_API_KEY_VAR } from './constants.js';
|
|
8
|
-
import {
|
|
8
|
+
import { HARNESS_SPECS, NATIVE_HARNESSES, harnessSpec, } from './harness-spec.js';
|
|
9
9
|
import { HERMES_KEY_VAR, captureHermesKeyRestore, hermesEnvPath, resolveHermesHome, writeHermesKey, } from './hermes-env.js';
|
|
10
10
|
import { hermesConfigPath, readHermesEntry } from './hermes-config.js';
|
|
11
11
|
import { installHermesPlugin, setHermesWebBackends } from './hermes-plugin.js';
|
|
@@ -254,35 +254,11 @@ function toNativeClient(harness) {
|
|
|
254
254
|
export const NATIVE_MCP_CLIENTS = NATIVE_HARNESSES.map(toNativeClient);
|
|
255
255
|
/** Native descriptor by harness id; Cursor and OpenClaw have none. */
|
|
256
256
|
export const NATIVE_BY_HARNESS = new Map(NATIVE_MCP_CLIENTS.map((client) => [client.connectClient, client]));
|
|
257
|
-
function nativeClient(harness) {
|
|
258
|
-
const client = NATIVE_BY_HARNESS.get(harness);
|
|
259
|
-
if (!client)
|
|
260
|
-
throw new Error(`${harness} is not a native MCP client`);
|
|
261
|
-
return client;
|
|
262
|
-
}
|
|
263
|
-
export const CLAUDE_CODE = nativeClient('claude-code');
|
|
264
|
-
export const CODEX = nativeClient('codex');
|
|
265
|
-
export const GROK = nativeClient('grok');
|
|
266
|
-
export const HERMES = nativeClient('hermes');
|
|
267
|
-
export const OPENCODE = nativeClient('opencode');
|
|
268
257
|
export const OPENCLAW = {
|
|
269
258
|
command: HARNESS_SPECS.openclaw.command,
|
|
270
259
|
displayName: HARNESS_SPECS.openclaw.displayName,
|
|
271
260
|
supportCheck: HARNESS_SPECS.openclaw.supportCheck,
|
|
272
261
|
};
|
|
273
|
-
export const HARNESS_CAPABILITIES = Object.fromEntries(ALL_HARNESSES.map((harness) => {
|
|
274
|
-
const spec = harnessSpec(harness);
|
|
275
|
-
return [
|
|
276
|
-
harness,
|
|
277
|
-
{
|
|
278
|
-
signInViaCliLogin: spec.signInViaCliLogin,
|
|
279
|
-
canVerifyAuth: spec.canVerifyAuth,
|
|
280
|
-
keyHeldByCli: spec.keyHeldByCli,
|
|
281
|
-
cliWritesConfig: spec.cliWritesConfig,
|
|
282
|
-
authDeferredAtInstall: spec.authDeferredAtInstall,
|
|
283
|
-
},
|
|
284
|
-
];
|
|
285
|
-
}));
|
|
286
262
|
function printPromptForHandoff(displayName, command, prompt, reason) {
|
|
287
263
|
// "set up", not "connected": sign-in can still be deferred here.
|
|
288
264
|
errLine(`TinyFish is set up in ${displayName}.`);
|
|
@@ -247,5 +247,9 @@ export declare const HARNESS_SPECS: {
|
|
|
247
247
|
export type Harness = keyof typeof HARNESS_SPECS;
|
|
248
248
|
export declare const ALL_HARNESSES: readonly Harness[];
|
|
249
249
|
export declare function harnessSpec(harness: Harness): HarnessSpec;
|
|
250
|
+
/** The rest of the union: every one of these must supply a connect and launch override. */
|
|
251
|
+
export type NonNativeHarness = {
|
|
252
|
+
[K in Harness]: 'urlStyle' extends keyof (typeof HARNESS_SPECS)[K] ? never : K;
|
|
253
|
+
}[Harness];
|
|
250
254
|
/** Native MCP harnesses carry add-generation fields; the rest override connect. */
|
|
251
255
|
export declare const NATIVE_HARNESSES: ("openclaw" | "grok" | "cursor" | "codex" | "hermes" | "opencode" | "claude-code")[];
|