@tiny-fish/cli 0.39.1-next.311 → 0.40.1-next.317
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/README.md +7 -0
- package/dist/commands/connect.js +86 -19
- package/dist/commands/doctor.js +3 -1
- package/dist/commands/fetch.js +79 -24
- package/dist/lib/client.d.ts +113 -3
- package/dist/lib/client.js +14 -3
- package/dist/lib/connect-all-summary.js +4 -1
- package/dist/lib/connect-all-uninstall.js +12 -1
- package/dist/lib/connect-all.d.ts +4 -2
- package/dist/lib/connect-all.js +13 -5
- package/dist/lib/connect-clients.d.ts +8 -2
- package/dist/lib/connect-clients.js +53 -13
- package/dist/lib/doctor-checks.d.ts +1 -0
- package/dist/lib/doctor-checks.js +38 -3
- package/dist/lib/doctor-report.d.ts +6 -0
- package/dist/lib/harness-detect.d.ts +2 -0
- package/dist/lib/harness-detect.js +11 -3
- package/dist/lib/harness-spec.d.ts +15 -5
- package/dist/lib/harness-spec.js +11 -1
- package/dist/lib/harness.js +2 -0
- package/dist/lib/hermes-config.d.ts +3 -0
- package/dist/lib/hermes-config.js +8 -4
- package/dist/lib/hermes-env.d.ts +7 -1
- package/dist/lib/hermes-env.js +6 -3
- package/dist/lib/hermes-plugin.d.ts +4 -0
- package/dist/lib/hermes-plugin.js +16 -0
- package/dist/lib/pi-config.d.ts +26 -0
- package/dist/lib/pi-config.js +111 -0
- package/dist/lib/registration-detect.js +29 -2
- package/dist/lib/setup-telemetry.d.ts +10 -0
- package/dist/lib/skill-install.d.ts +1 -0
- package/dist/lib/skill-install.js +44 -37
- package/dist/lib/types.d.ts +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -278,6 +278,13 @@ tinyfish fetch content get https://agentql.com --links --image-links
|
|
|
278
278
|
# Bound each URL independently
|
|
279
279
|
tinyfish fetch content get https://agentql.com --per-url-timeout-ms 45000
|
|
280
280
|
|
|
281
|
+
# Ranked verbatim snippets answering a query (beta; markdown only, max 10 URLs)
|
|
282
|
+
tinyfish fetch content get https://agentql.com --highlights "pricing tiers"
|
|
283
|
+
|
|
284
|
+
# Tune snippets: count (1-20, default 5), total characters, keep full-page text
|
|
285
|
+
tinyfish fetch content get https://agentql.com --highlights "pricing tiers" \
|
|
286
|
+
--max-snippets 3 --max-characters 1200 --include-full-page-text
|
|
287
|
+
|
|
281
288
|
# Human-readable output
|
|
282
289
|
tinyfish fetch content get https://agentql.com --pretty
|
|
283
290
|
```
|
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 { CURSOR_SKILL_TARGET, OPENCLAW, openclawSkillInstallArgs, NATIVE_BY_HARNESS, launchNativeMcpClient, launchOmpWalkthrough, launchOpenClawWalkthrough, openExternalUrl, } from '../lib/connect-clients.js';
|
|
3
|
+
import { CURSOR_SKILL_TARGET, OPENCLAW, openclawSkillInstallArgs, NATIVE_BY_HARNESS, launchNativeMcpClient, launchOmpWalkthrough, launchPiWalkthrough, launchOpenClawWalkthrough, openExternalUrl, } from '../lib/connect-clients.js';
|
|
4
4
|
import { captureStdio, capturedOutput, 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';
|
|
@@ -9,6 +9,7 @@ import { ConnectInterruptedError, ConnectStepError, HoistedFailureReportedError,
|
|
|
9
9
|
import { cursorInstallDeeplink, cursorMcpPath, readCursorTinyfishEntry, writeCursorMcpConfig, } from '../lib/cursor-config.js';
|
|
10
10
|
import { runConnectAll } from '../lib/connect-all.js';
|
|
11
11
|
import { ompMcpPath, readOmpTinyfishEntry, writeOmpMcpConfig } from '../lib/omp-config.js';
|
|
12
|
+
import { PI_ADAPTER_INSTALL_COMMAND, piMcpAdapterState, piSkillDirMismatch, piMcpPath, readPiTinyfishEntry, writePiMcpConfig, } from '../lib/pi-config.js';
|
|
12
13
|
import { ALL_HARNESSES, AuthMode, HARNESS_DISPLAY_NAMES, RELOAD_ACTION, } from '../lib/harness-detect.js';
|
|
13
14
|
import { detectHumanInitiated } from '../lib/harness.js';
|
|
14
15
|
import { TINYFISH_API_KEY_VAR } from '../lib/constants.js';
|
|
@@ -169,6 +170,22 @@ async function prepareInstallAuth(client, options, keyAuthSupported) {
|
|
|
169
170
|
}
|
|
170
171
|
return { keyedAdd, buildAddArgs };
|
|
171
172
|
}
|
|
173
|
+
/** Inherit for an inline-OAuth add; capture otherwise, piping prompts when seeded. */
|
|
174
|
+
function addSpawnOptions(options, interactiveAdd, seeded) {
|
|
175
|
+
if (interactiveAdd)
|
|
176
|
+
return { stdio: 'inherit', timeout: options.authTimeoutMs };
|
|
177
|
+
if (!seeded)
|
|
178
|
+
return captureStdio(options.verbose ?? false);
|
|
179
|
+
return {
|
|
180
|
+
encoding: 'utf8',
|
|
181
|
+
// Without it, the 1 MiB default overflows into a misread Ctrl+C.
|
|
182
|
+
maxBuffer: STEP_MAX_BUFFER,
|
|
183
|
+
// The unattended path had no bound at all before; captureStdio's is the floor.
|
|
184
|
+
timeout: options.authTimeoutMs ?? SKILL_INSTALL_TIMEOUT_MS,
|
|
185
|
+
input: seeded.stdinInput,
|
|
186
|
+
env: seeded.env,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
172
189
|
/** Returns the seeded-credential note when a key was written into a user-owned file. */
|
|
173
190
|
function performMcpAdd(client, options, plan, attemptId) {
|
|
174
191
|
const { keyedAdd, buildAddArgs } = plan;
|
|
@@ -183,20 +200,11 @@ function performMcpAdd(client, options, plan, attemptId) {
|
|
|
183
200
|
const interactiveAdd = !(client.nonInteractiveAdd || keyedAdd);
|
|
184
201
|
// After the removals: an interrupt there leaves no key behind.
|
|
185
202
|
const seeded = keyedAdd?.spec.seedKey?.(keyedAdd.key);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
: seeded
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// Without it, the 1 MiB default overflows into a misread Ctrl+C.
|
|
192
|
-
maxBuffer: STEP_MAX_BUFFER,
|
|
193
|
-
// The unattended path had no bound at all before; captureStdio's is the floor.
|
|
194
|
-
timeout: options.authTimeoutMs ?? SKILL_INSTALL_TIMEOUT_MS,
|
|
195
|
-
input: seeded.stdinInput,
|
|
196
|
-
env: seeded.env,
|
|
197
|
-
}
|
|
198
|
-
: captureStdio(options.verbose ?? false);
|
|
199
|
-
const addResult = spawn.sync(client.command, addArgs, addOptions);
|
|
203
|
+
// Writing the entry ourselves skips the harness probe that saves it disabled.
|
|
204
|
+
if (seeded?.register(mcpUrl.toString())) {
|
|
205
|
+
return { note: seeded.note, seededHome: seeded.home };
|
|
206
|
+
}
|
|
207
|
+
const addResult = spawn.sync(client.command, addArgs, addSpawnOptions(options, interactiveAdd, seeded));
|
|
200
208
|
// Rolls back first: signInStepError throws on a Ctrl+C.
|
|
201
209
|
const failAdd = (build) => {
|
|
202
210
|
seeded?.rollback();
|
|
@@ -214,7 +222,7 @@ function performMcpAdd(client, options, plan, attemptId) {
|
|
|
214
222
|
// The tag keeps the path-bearing prose out of telemetry.
|
|
215
223
|
{ failureDetail: registration.tag }));
|
|
216
224
|
}
|
|
217
|
-
return seeded?.note;
|
|
225
|
+
return { note: seeded?.note, seededHome: seeded?.home };
|
|
218
226
|
}
|
|
219
227
|
function performLoginStep(client, pendingLogin, options) {
|
|
220
228
|
errLine('Signing in to TinyFish...');
|
|
@@ -289,7 +297,7 @@ async function connectNativeMcpClient(client, options) {
|
|
|
289
297
|
// A probed client authenticates inside `mcp add` too, so a failure there is either.
|
|
290
298
|
state.stage =
|
|
291
299
|
loginArgs && !probeAuthenticated ? 'registration' : 'registration_or_authentication';
|
|
292
|
-
const seededNote = performMcpAdd(client, options, plan, telemetry.attemptId);
|
|
300
|
+
const { note: seededNote, seededHome } = performMcpAdd(client, options, plan, telemetry.attemptId);
|
|
293
301
|
telemetry.track('checkpoint', { phase: 'registered' });
|
|
294
302
|
const { authMode, signInDeferred } = completeAuthFlow(client, options, state, telemetry, {
|
|
295
303
|
useKeyAuth,
|
|
@@ -301,7 +309,7 @@ async function connectNativeMcpClient(client, options) {
|
|
|
301
309
|
state.authMode = authMode;
|
|
302
310
|
// Registration/OAuth make MCP work; later steps are cosmetic and must not fail the attempt.
|
|
303
311
|
settle(state, telemetry, 'completed', { authMode });
|
|
304
|
-
await runPostInstallSteps(client, options, state, telemetry, signInDeferred);
|
|
312
|
+
await runPostInstallSteps(client, options, state, telemetry, signInDeferred, seededHome);
|
|
305
313
|
});
|
|
306
314
|
return state.authMode;
|
|
307
315
|
}
|
|
@@ -317,7 +325,7 @@ function connectedLine(client, signInDeferred) {
|
|
|
317
325
|
`${RELOAD_ACTION[client.connectClient]} to pick up the tools.`);
|
|
318
326
|
}
|
|
319
327
|
// Runs after `completed` settled: steps fail alone, the rest still run.
|
|
320
|
-
async function runPostInstallSteps(client, options, state, telemetry, signInDeferred) {
|
|
328
|
+
async function runPostInstallSteps(client, options, state, telemetry, signInDeferred, seededHome) {
|
|
321
329
|
let failed = false;
|
|
322
330
|
// Modelled on upgrade's attempt(); interrupts rethrow and abandon remaining steps.
|
|
323
331
|
const attempt = async (stage, run) => {
|
|
@@ -348,6 +356,7 @@ async function runPostInstallSteps(client, options, state, telemetry, signInDefe
|
|
|
348
356
|
extraPostInstall({
|
|
349
357
|
apiKey: validatedApiKey(options.apiKey),
|
|
350
358
|
verbose: options.verbose ?? false,
|
|
359
|
+
seededHome,
|
|
351
360
|
});
|
|
352
361
|
telemetry.track('checkpoint', {
|
|
353
362
|
phase: 'plugin_installed',
|
|
@@ -501,6 +510,31 @@ export async function connectOpenClaw(options) {
|
|
|
501
510
|
}
|
|
502
511
|
});
|
|
503
512
|
}
|
|
513
|
+
const PI_RELOAD_COPY = 'TinyFish is configured in pi. Restart pi, then run `/mcp-auth tinyfish` there if it asks you ' +
|
|
514
|
+
'to sign in.';
|
|
515
|
+
// The skill is the working path either way, so the note must not read as a failure.
|
|
516
|
+
const PI_ADAPTER_DORMANT_NOTE = 'Note: pi has no built-in MCP support, so the entry in its mcp.json stays dormant until you ' +
|
|
517
|
+
`run \`${PI_ADAPTER_INSTALL_COMMAND}\`. The TinyFish skill works without it.`;
|
|
518
|
+
// Only the dormant case was spoken for, leaving a working MCP install silent.
|
|
519
|
+
const PI_ADAPTER_LIVE_NOTE = 'pi-mcp-adapter is installed, so pi loads the TinyFish MCP tools as well as the skill once ' +
|
|
520
|
+
'you restart it.';
|
|
521
|
+
function piAdapterNote() {
|
|
522
|
+
const state = piMcpAdapterState();
|
|
523
|
+
if (state === 'installed')
|
|
524
|
+
return PI_ADAPTER_LIVE_NOTE;
|
|
525
|
+
return state === 'absent' ? PI_ADAPTER_DORMANT_NOTE : undefined;
|
|
526
|
+
}
|
|
527
|
+
// The `skills` CLI writes to the default dir even when pi reads another one.
|
|
528
|
+
function piPostConnectNotes() {
|
|
529
|
+
const notes = [piAdapterNote()];
|
|
530
|
+
const skills = piSkillDirMismatch();
|
|
531
|
+
if (skills) {
|
|
532
|
+
notes.push(`Note: PI_CODING_AGENT_DIR points pi at ${skills.readFrom}, but the TinyFish skill ` +
|
|
533
|
+
`installed to ${skills.installedTo}. Copy or symlink it across for pi to load it.`);
|
|
534
|
+
}
|
|
535
|
+
const printable = notes.filter(Boolean);
|
|
536
|
+
return printable.length > 0 ? printable.join('\n') : undefined;
|
|
537
|
+
}
|
|
504
538
|
// Only interactive omp wires the OAuth handler; headless cannot sign in.
|
|
505
539
|
const OMP_KEYLESS_COPY = 'TinyFish is configured in omp. Open omp and sign in to TinyFish when prompted, or run ' +
|
|
506
540
|
'`/mcp reauth tinyfish` there.';
|
|
@@ -535,6 +569,22 @@ const CONFIG_FILE_HARNESSES = {
|
|
|
535
569
|
reload: OMP_KEYLESS_COPY,
|
|
536
570
|
},
|
|
537
571
|
},
|
|
572
|
+
pi: {
|
|
573
|
+
skillTarget: { skillAgent: 'pi', displayName: HARNESS_DISPLAY_NAMES.pi },
|
|
574
|
+
registeredUrl: () => readPiTinyfishEntry().url,
|
|
575
|
+
write: writePiMcpConfig,
|
|
576
|
+
configPath: piMcpPath,
|
|
577
|
+
// No install deeplink exists; the copy carries the restart instead.
|
|
578
|
+
tryLaunch: () => false,
|
|
579
|
+
postConnectNote: piPostConnectNotes,
|
|
580
|
+
copy: {
|
|
581
|
+
verified: "TinyFish is connected and verified (health+auth). Restart pi and it's live.",
|
|
582
|
+
authFailed: (reason) => `TinyFish is configured, but the authenticated check failed (${reason}). ` +
|
|
583
|
+
'Fix: rotate/re-enter your key with `tinyfish auth login`, then re-run: tinyfish connect pi',
|
|
584
|
+
launched: PI_RELOAD_COPY,
|
|
585
|
+
reload: PI_RELOAD_COPY,
|
|
586
|
+
},
|
|
587
|
+
},
|
|
538
588
|
};
|
|
539
589
|
/** Writes the harness's MCP config file itself; no `mcp add` exists. */
|
|
540
590
|
async function connectConfigFileHarness(harness, options) {
|
|
@@ -628,6 +678,9 @@ async function connectConfigFileHarness(harness, options) {
|
|
|
628
678
|
else {
|
|
629
679
|
errLine(spec.copy.reload);
|
|
630
680
|
}
|
|
681
|
+
const note = spec.postConnectNote?.();
|
|
682
|
+
if (note)
|
|
683
|
+
errLine(note);
|
|
631
684
|
settle(state, telemetry, 'completed', { authMode: resolvedKey ? AuthMode.ApiKey : 'deferred' });
|
|
632
685
|
});
|
|
633
686
|
}
|
|
@@ -643,6 +696,7 @@ function launchCursorDeeplink(mcpUrl) {
|
|
|
643
696
|
/** Total over the non-native harnesses: a new one fails to compile until wired here. */
|
|
644
697
|
const LAUNCH_OVERRIDES = {
|
|
645
698
|
omp: launchOmpWalkthrough,
|
|
699
|
+
pi: launchPiWalkthrough,
|
|
646
700
|
openclaw: launchOpenClawWalkthrough,
|
|
647
701
|
// Cursor has no chat CLI to launch.
|
|
648
702
|
cursor: () => {
|
|
@@ -683,6 +737,19 @@ const CONNECTOR_OVERRIDES = {
|
|
|
683
737
|
}
|
|
684
738
|
return undefined;
|
|
685
739
|
},
|
|
740
|
+
pi: async (options) => {
|
|
741
|
+
await connectConfigFileHarness('pi', options);
|
|
742
|
+
if (options.launch) {
|
|
743
|
+
try {
|
|
744
|
+
await launchPiWalkthrough();
|
|
745
|
+
}
|
|
746
|
+
catch (error) {
|
|
747
|
+
errLine(error instanceof Error ? error.message : String(error));
|
|
748
|
+
errLine(finishSetupHint('pi'));
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return undefined;
|
|
752
|
+
},
|
|
686
753
|
};
|
|
687
754
|
function requireNative(harness) {
|
|
688
755
|
const client = NATIVE_BY_HARNESS.get(harness);
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CLI_VERSION } from '../lib/constants.js';
|
|
2
|
-
import { checkAuthCall, checkCliVersion, checkConnectivity, checkCredential, checkHermesPlugin, checkRegistration, provesHarnessReach, verifyHarnessKey, } from '../lib/doctor-checks.js';
|
|
2
|
+
import { checkAuthCall, checkCliVersion, checkConnectivity, checkCredential, checkHermesPlugin, checkPiAdapter, checkRegistration, provesHarnessReach, verifyHarnessKey, } from '../lib/doctor-checks.js';
|
|
3
3
|
import { DOCTOR_COULD_NOT_RUN, DOCTOR_INVALID_INPUT, DOCTOR_SCHEMA_VERSION, doctorReportSchema, exitCodeFor, renderPretty, verdictFor, } from '../lib/doctor-report.js';
|
|
4
4
|
import { applyRepairs, repairsFor } from '../lib/doctor-repairs.js';
|
|
5
5
|
import { couldNotRunPayload, telemetryKey, telemetryPayload } from '../lib/doctor-telemetry.js';
|
|
@@ -30,11 +30,13 @@ export async function runDoctor(options) {
|
|
|
30
30
|
]);
|
|
31
31
|
const hermesStatus = statuses.find((status) => status.harness === 'hermes');
|
|
32
32
|
const hermesPlugin = hermesStatus ? checkHermesPlugin(hermesStatus) : undefined;
|
|
33
|
+
const piStatus = statuses.find((status) => status.harness === 'pi');
|
|
33
34
|
const checks = [
|
|
34
35
|
checkCliVersion(),
|
|
35
36
|
connectivity,
|
|
36
37
|
...statuses.map((status, i) => checkRegistration(status, options.mcpUrl, keyAuths[i], !credential.key)),
|
|
37
38
|
...(hermesPlugin ? [hermesPlugin.check] : []),
|
|
39
|
+
...(piStatus ? [checkPiAdapter(piStatus)] : []),
|
|
38
40
|
credential.check,
|
|
39
41
|
checkAuthCall(cliAuthResult),
|
|
40
42
|
];
|
package/dist/commands/fetch.js
CHANGED
|
@@ -4,14 +4,64 @@ import { fetchContentGet } from '../lib/client.js';
|
|
|
4
4
|
import { err, handleApiError, out, outLine } from '../lib/output.js';
|
|
5
5
|
const MIN_PER_URL_TIMEOUT_MS = 1;
|
|
6
6
|
const MAX_PER_URL_TIMEOUT_MS = 110000;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
const MIN_HIGHLIGHT_SNIPPETS = 1;
|
|
8
|
+
const MAX_HIGHLIGHT_SNIPPETS = 20;
|
|
9
|
+
const MAX_URLS = 10;
|
|
10
|
+
function integerInRange(min, max) {
|
|
11
|
+
return (value) => {
|
|
12
|
+
const parsed = Number(value);
|
|
13
|
+
if (!Number.isSafeInteger(parsed) || parsed < min || (max !== undefined && parsed > max)) {
|
|
14
|
+
throw new InvalidArgumentError(max === undefined
|
|
15
|
+
? 'must be a positive integer'
|
|
16
|
+
: `must be an integer between ${min} and ${max}`);
|
|
17
|
+
}
|
|
18
|
+
return parsed;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function validateHighlightsOptions(opts) {
|
|
22
|
+
const hasSubFlag = opts.maxSnippets !== undefined ||
|
|
23
|
+
opts.maxCharacters !== undefined ||
|
|
24
|
+
opts.includeFullPageText !== undefined;
|
|
25
|
+
if (opts.highlights === undefined) {
|
|
26
|
+
return hasSubFlag
|
|
27
|
+
? '--max-snippets, --max-characters and --include-full-page-text require --highlights'
|
|
28
|
+
: null;
|
|
29
|
+
}
|
|
30
|
+
if (opts.highlights.trim() === '') {
|
|
31
|
+
return '--highlights requires a non-empty query';
|
|
13
32
|
}
|
|
14
|
-
|
|
33
|
+
if (opts.format !== undefined && opts.format !== 'markdown') {
|
|
34
|
+
return '--highlights requires --format markdown';
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function buildHighlightsParams(opts) {
|
|
39
|
+
if (opts.highlights === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
return {
|
|
42
|
+
query: opts.highlights,
|
|
43
|
+
...(opts.maxSnippets !== undefined ? { max_snippets: opts.maxSnippets } : {}),
|
|
44
|
+
...(opts.maxCharacters !== undefined ? { max_characters: opts.maxCharacters } : {}),
|
|
45
|
+
...(opts.includeFullPageText !== undefined
|
|
46
|
+
? { include_full_page_text: opts.includeFullPageText }
|
|
47
|
+
: {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function buildFetchParams(urls, opts) {
|
|
51
|
+
const highlights = buildHighlightsParams(opts);
|
|
52
|
+
return {
|
|
53
|
+
urls,
|
|
54
|
+
...(opts.format !== undefined ? { format: opts.format } : {}),
|
|
55
|
+
...(opts.links !== undefined ? { links: opts.links } : {}),
|
|
56
|
+
...(opts.imageLinks !== undefined ? { image_links: opts.imageLinks } : {}),
|
|
57
|
+
...(opts.perUrlTimeoutMs !== undefined ? { per_url_timeout_ms: opts.perUrlTimeoutMs } : {}),
|
|
58
|
+
...(opts.ifNoneMatch !== undefined ? { if_none_match: opts.ifNoneMatch } : {}),
|
|
59
|
+
...(opts.ifModifiedSince !== undefined ? { if_modified_since: opts.ifModifiedSince } : {}),
|
|
60
|
+
...(opts.includeEtagAndLastModified !== undefined
|
|
61
|
+
? { include_etag_and_last_modified: opts.includeEtagAndLastModified }
|
|
62
|
+
: {}),
|
|
63
|
+
...(highlights !== undefined ? { highlights } : {}),
|
|
64
|
+
};
|
|
15
65
|
}
|
|
16
66
|
function printPrettyFetch(response) {
|
|
17
67
|
outLine(`Results: ${response.results.length}`);
|
|
@@ -32,6 +82,12 @@ function printPrettyFetch(response) {
|
|
|
32
82
|
outLine(` ETag: ${result.etag}`);
|
|
33
83
|
if (result.last_modified)
|
|
34
84
|
outLine(` Last-Modified: ${result.last_modified}`);
|
|
85
|
+
if (result.highlights && result.highlights.length > 0) {
|
|
86
|
+
outLine(' Highlights:');
|
|
87
|
+
result.highlights.forEach((snippet, index) => {
|
|
88
|
+
outLine(` ${index + 1}. ${snippet.text}`);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
35
91
|
outLine('');
|
|
36
92
|
}
|
|
37
93
|
}
|
|
@@ -57,12 +113,26 @@ export function registerFetch(program) {
|
|
|
57
113
|
.option('--format <format>', 'Output format: markdown, html, or json')
|
|
58
114
|
.option('--links', 'Include extracted links')
|
|
59
115
|
.option('--image-links', 'Include extracted image links')
|
|
60
|
-
.option('--per-url-timeout-ms <milliseconds>', 'Per-URL timeout budget in milliseconds',
|
|
116
|
+
.option('--per-url-timeout-ms <milliseconds>', 'Per-URL timeout budget in milliseconds', integerInRange(MIN_PER_URL_TIMEOUT_MS, MAX_PER_URL_TIMEOUT_MS))
|
|
61
117
|
.option('--if-none-match <etag>', 'ETag validator for a conditional GET (single URL only)')
|
|
62
118
|
.option('--if-modified-since <http-date>', 'Last-Modified validator for a conditional GET (single URL only)')
|
|
63
119
|
.option('--include-etag-and-last-modified', 'Include etag / last_modified validators on each result')
|
|
120
|
+
.option('--highlights <query>', '(beta) Return ranked verbatim snippets answering the query instead of full-page text (markdown only, max 10 URLs)')
|
|
121
|
+
.option('--max-snippets <n>', 'Maximum snippets per URL, 1-20 (default 5; requires --highlights)', integerInRange(MIN_HIGHLIGHT_SNIPPETS, MAX_HIGHLIGHT_SNIPPETS))
|
|
122
|
+
.option('--max-characters <n>', 'Maximum total characters across snippets (requires --highlights)', integerInRange(1))
|
|
123
|
+
.option('--include-full-page-text', 'Also return full-page text alongside snippets (requires --highlights)')
|
|
64
124
|
.option('--pretty', 'Human-readable output')
|
|
65
125
|
.action(async (urls, opts) => {
|
|
126
|
+
// Mirrors the SDK cap the raw post path no longer enforces.
|
|
127
|
+
if (urls.length > MAX_URLS) {
|
|
128
|
+
err({ error: `urls must contain at most ${MAX_URLS} items` });
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const highlightsError = validateHighlightsOptions(opts);
|
|
132
|
+
if (highlightsError !== null) {
|
|
133
|
+
err({ error: highlightsError });
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
66
136
|
if ((opts.ifNoneMatch !== undefined || opts.ifModifiedSince !== undefined) &&
|
|
67
137
|
urls.length > 1) {
|
|
68
138
|
err({
|
|
@@ -72,22 +142,7 @@ export function registerFetch(program) {
|
|
|
72
142
|
}
|
|
73
143
|
const apiKey = getApiKey();
|
|
74
144
|
try {
|
|
75
|
-
const response = await fetchContentGet(
|
|
76
|
-
urls,
|
|
77
|
-
...(opts.format !== undefined ? { format: opts.format } : {}),
|
|
78
|
-
...(opts.links !== undefined ? { links: opts.links } : {}),
|
|
79
|
-
...(opts.imageLinks !== undefined ? { image_links: opts.imageLinks } : {}),
|
|
80
|
-
...(opts.perUrlTimeoutMs !== undefined
|
|
81
|
-
? { per_url_timeout_ms: opts.perUrlTimeoutMs }
|
|
82
|
-
: {}),
|
|
83
|
-
...(opts.ifNoneMatch !== undefined ? { if_none_match: opts.ifNoneMatch } : {}),
|
|
84
|
-
...(opts.ifModifiedSince !== undefined
|
|
85
|
-
? { if_modified_since: opts.ifModifiedSince }
|
|
86
|
-
: {}),
|
|
87
|
-
...(opts.includeEtagAndLastModified !== undefined
|
|
88
|
-
? { include_etag_and_last_modified: opts.includeEtagAndLastModified }
|
|
89
|
-
: {}),
|
|
90
|
-
}, apiKey);
|
|
145
|
+
const response = await fetchContentGet(buildFetchParams(urls, opts), apiKey);
|
|
91
146
|
if (opts.pretty) {
|
|
92
147
|
printPrettyFetch(response);
|
|
93
148
|
}
|
package/dist/lib/client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type AgentRunAsyncResponse, type AgentRunResponse, type AgentRunWithStreamingResponse, type
|
|
2
|
-
import type { BatchCancelResponse, BatchGetResponse, BatchRunRequest, BatchRunResponse, CancelRunResponse, CliAgentRunParams, CliBrowserSessionCreateParams, CookieRecord, ProfileCreateResponse, ProfileUploadResponse, RunStepsResponse, VaultConnectRequest, VaultConnectResponse, VaultConnectionsListResponse, VaultDisconnectResponse, VaultItemsListResponse, VaultItemsSyncResponse } from './types.js';
|
|
1
|
+
import { type AgentRunAsyncResponse, type AgentRunResponse, type AgentRunWithStreamingResponse, type BrowserSession, type Run, type RunListParams, type RunListResponse, type SearchQueryParams, type SearchQueryResponse, type WalletResponse } from '@tiny-fish/sdk';
|
|
2
|
+
import type { BatchCancelResponse, BatchGetResponse, BatchRunRequest, BatchRunResponse, CancelRunResponse, CliAgentRunParams, CliBrowserSessionCreateParams, CliFetchGetContentsParams, CookieRecord, ProfileCreateResponse, ProfileUploadResponse, RunStepsResponse, VaultConnectRequest, VaultConnectResponse, VaultConnectionsListResponse, VaultDisconnectResponse, VaultItemsListResponse, VaultItemsSyncResponse } from './types.js';
|
|
3
|
+
import { z } from 'zod';
|
|
3
4
|
type ConnectMap = Record<string, {
|
|
4
5
|
attempt_id: string;
|
|
5
6
|
}> | undefined;
|
|
@@ -12,7 +13,116 @@ export declare function listRuns(opts: RunListParams, apiKey: string, timeout?:
|
|
|
12
13
|
export declare function getRun(runId: string, apiKey: string): Promise<Run>;
|
|
13
14
|
export declare function getRunSteps(runId: string, apiKey: string): Promise<RunStepsResponse>;
|
|
14
15
|
export declare function searchQuery(params: SearchQueryParams, apiKey: string): Promise<SearchQueryResponse>;
|
|
15
|
-
|
|
16
|
+
declare const cliFetchResponseSchema: z.ZodObject<{
|
|
17
|
+
results: z.ZodArray<z.ZodIntersection<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
18
|
+
url: z.ZodString;
|
|
19
|
+
final_url: z.ZodNullable<z.ZodString>;
|
|
20
|
+
title: z.ZodNullable<z.ZodString>;
|
|
21
|
+
description: z.ZodNullable<z.ZodString>;
|
|
22
|
+
language: z.ZodNullable<z.ZodString>;
|
|
23
|
+
author: z.ZodNullable<z.ZodString>;
|
|
24
|
+
published_date: z.ZodNullable<z.ZodString>;
|
|
25
|
+
links: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
|
+
image_links: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
27
|
+
latency_ms: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
28
|
+
not_modified: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
etag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
|
+
last_modified: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
31
|
+
format: z.ZodLiteral<"markdown">;
|
|
32
|
+
text: z.ZodNullable<z.ZodString>;
|
|
33
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
34
|
+
url: z.ZodString;
|
|
35
|
+
final_url: z.ZodNullable<z.ZodString>;
|
|
36
|
+
title: z.ZodNullable<z.ZodString>;
|
|
37
|
+
description: z.ZodNullable<z.ZodString>;
|
|
38
|
+
language: z.ZodNullable<z.ZodString>;
|
|
39
|
+
author: z.ZodNullable<z.ZodString>;
|
|
40
|
+
published_date: z.ZodNullable<z.ZodString>;
|
|
41
|
+
links: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
42
|
+
image_links: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
43
|
+
latency_ms: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
44
|
+
not_modified: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
+
etag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
46
|
+
last_modified: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
47
|
+
format: z.ZodLiteral<"html">;
|
|
48
|
+
text: z.ZodNullable<z.ZodString>;
|
|
49
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
50
|
+
url: z.ZodString;
|
|
51
|
+
final_url: z.ZodNullable<z.ZodString>;
|
|
52
|
+
title: z.ZodNullable<z.ZodString>;
|
|
53
|
+
description: z.ZodNullable<z.ZodString>;
|
|
54
|
+
language: z.ZodNullable<z.ZodString>;
|
|
55
|
+
author: z.ZodNullable<z.ZodString>;
|
|
56
|
+
published_date: z.ZodNullable<z.ZodString>;
|
|
57
|
+
links: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
58
|
+
image_links: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
59
|
+
latency_ms: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
60
|
+
not_modified: z.ZodOptional<z.ZodBoolean>;
|
|
61
|
+
etag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
62
|
+
last_modified: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
63
|
+
format: z.ZodLiteral<"json">;
|
|
64
|
+
text: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodType<string | number | boolean | {
|
|
65
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
66
|
+
} | (string | number | boolean | {
|
|
67
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
68
|
+
} | (string | number | boolean | {
|
|
69
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
70
|
+
} | (string | number | boolean | {
|
|
71
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
72
|
+
} | (string | number | boolean | {
|
|
73
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
74
|
+
} | (string | number | boolean | {
|
|
75
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
76
|
+
} | (string | number | boolean | {
|
|
77
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
78
|
+
} | (string | number | boolean | {
|
|
79
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null;
|
|
80
|
+
} | (string | number | boolean | {
|
|
81
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null;
|
|
82
|
+
} | (string | number | boolean | {
|
|
83
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null;
|
|
84
|
+
} | (string | number | boolean | {
|
|
85
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null;
|
|
86
|
+
} | (string | number | boolean | {
|
|
87
|
+
[key: string]: string | number | boolean | /*elided*/ any | /*elided*/ any | null;
|
|
88
|
+
} | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null, unknown, z.core.$ZodTypeInternals<string | number | boolean | {
|
|
89
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
90
|
+
} | (string | number | boolean | {
|
|
91
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
92
|
+
} | (string | number | boolean | {
|
|
93
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
94
|
+
} | (string | number | boolean | {
|
|
95
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
96
|
+
} | (string | number | boolean | {
|
|
97
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
98
|
+
} | (string | number | boolean | {
|
|
99
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
100
|
+
} | (string | number | boolean | {
|
|
101
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null;
|
|
102
|
+
} | (string | number | boolean | {
|
|
103
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null;
|
|
104
|
+
} | (string | number | boolean | {
|
|
105
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null;
|
|
106
|
+
} | (string | number | boolean | {
|
|
107
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null;
|
|
108
|
+
} | (string | number | boolean | {
|
|
109
|
+
[key: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null;
|
|
110
|
+
} | (string | number | boolean | {
|
|
111
|
+
[key: string]: string | number | boolean | /*elided*/ any | /*elided*/ any | null;
|
|
112
|
+
} | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null, unknown>>>>;
|
|
113
|
+
}, z.core.$strip>], "format">, z.ZodObject<{
|
|
114
|
+
highlights: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
115
|
+
text: z.ZodString;
|
|
116
|
+
rank: z.ZodNumber;
|
|
117
|
+
}, z.core.$strip>>>>;
|
|
118
|
+
}, z.core.$strip>>>;
|
|
119
|
+
errors: z.ZodArray<z.ZodObject<{
|
|
120
|
+
url: z.ZodString;
|
|
121
|
+
error: z.ZodString;
|
|
122
|
+
}, z.core.$strip>>;
|
|
123
|
+
}, z.core.$strip>;
|
|
124
|
+
export type CliFetchResponse = z.infer<typeof cliFetchResponseSchema>;
|
|
125
|
+
export declare function fetchContentGet(params: CliFetchGetContentsParams, apiKey: string): Promise<CliFetchResponse>;
|
|
16
126
|
export declare function browserSessionCreate(params: CliBrowserSessionCreateParams, apiKey: string): Promise<BrowserSession>;
|
|
17
127
|
export declare function cancelRun(runId: string, apiKey: string): Promise<CancelRunResponse>;
|
|
18
128
|
export declare function getWallet(apiKey: string): Promise<WalletResponse>;
|
package/dist/lib/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { APIStatusError, agentRunAsyncResponseSchema, agentRunResponseSchema, agentRunWithStreamingResponseSchema,
|
|
1
|
+
import { APIStatusError, agentRunAsyncResponseSchema, agentRunResponseSchema, agentRunWithStreamingResponseSchema, fetchErrorSchema, fetchResultSchema, browserSessionSchema, searchQueryResponseSchema, TinyFish, runSchema, runStatusSchema, } from '@tiny-fish/sdk';
|
|
2
2
|
import { CONNECT_SOURCE, loadConfig } from './auth.js';
|
|
3
3
|
import { API_URL_OVERRIDE, CLI_AGENT_IDENTITY, CLI_VERSION } from './constants.js';
|
|
4
4
|
import { detectHarness, detectHumanInitiated } from './harness.js';
|
|
@@ -281,10 +281,21 @@ export function searchQuery(params, apiKey) {
|
|
|
281
281
|
return searchQueryResponseSchema.parse(response);
|
|
282
282
|
});
|
|
283
283
|
}
|
|
284
|
+
const highlightSnippetSchema = z.object({
|
|
285
|
+
text: z.string(),
|
|
286
|
+
rank: z.number().int().min(1),
|
|
287
|
+
});
|
|
288
|
+
// Published SDK result schema strips `highlights`; intersect to keep it.
|
|
289
|
+
// nullish, not optional: a null must not fail the whole response parse.
|
|
290
|
+
const cliFetchResponseSchema = z.object({
|
|
291
|
+
results: z.array(z.intersection(fetchResultSchema, z.object({ highlights: z.array(highlightSnippetSchema).nullish() }))),
|
|
292
|
+
errors: z.array(fetchErrorSchema),
|
|
293
|
+
});
|
|
284
294
|
export function fetchContentGet(params, apiKey) {
|
|
285
295
|
return sdk(apiKey, async (client) => {
|
|
286
|
-
|
|
287
|
-
|
|
296
|
+
// TODO(PROD-4260): use client.fetch.getContents once #4786 lands in an SDK release.
|
|
297
|
+
const response = await client.post(client.productUrl('fetch'), { json: params });
|
|
298
|
+
return parseWithSchema(cliFetchResponseSchema, response, 'Invalid fetch response');
|
|
288
299
|
});
|
|
289
300
|
}
|
|
290
301
|
export function browserSessionCreate(params, apiKey) {
|
|
@@ -9,7 +9,7 @@ const HIDDEN_ONCE_SOMETHING_WORKED = new Set([
|
|
|
9
9
|
]);
|
|
10
10
|
/** A stale config dir is a detection signal with nothing behind it. */
|
|
11
11
|
export function actionable(result) {
|
|
12
|
-
return result.detected && result.outcome !== 'stale_config';
|
|
12
|
+
return (result.detected && result.outcome !== 'stale_config' && result.outcome !== 'needs_first_run');
|
|
13
13
|
}
|
|
14
14
|
function absentSummaryLine(result, uninstall) {
|
|
15
15
|
const name = DISPLAY_NAMES[result.harness];
|
|
@@ -29,6 +29,9 @@ function skippedSummaryLine(result) {
|
|
|
29
29
|
if (result.outcome === 'stale_config') {
|
|
30
30
|
return `… ${name} — config at ${result.configPath} but no \`${HARNESS_COMMANDS[result.harness]}\` on PATH, skipped. Install ${name}, then run: ${result.fixCommand}`;
|
|
31
31
|
}
|
|
32
|
+
if (result.outcome === 'needs_first_run') {
|
|
33
|
+
return `… ${name} — installed but never run, so ${result.configPath} does not exist yet, skipped. Run \`${HARNESS_COMMANDS[result.harness]}\` once, then: ${result.fixCommand}`;
|
|
34
|
+
}
|
|
32
35
|
if (result.outcome === 'not_reached') {
|
|
33
36
|
return `… ${name} — not reached, the run stopped early. Fix: ${result.fixCommand}`;
|
|
34
37
|
}
|
|
@@ -3,6 +3,7 @@ import { NATIVE_BY_HARNESS, openclawSkillUninstall } from './connect-clients.js'
|
|
|
3
3
|
import { ConnectInterruptedError, spawnRemoval } from './connect-runtime.js';
|
|
4
4
|
import { removeCursorMcpServer, planCursorWrite } from './cursor-config.js';
|
|
5
5
|
import { planOmpWrite, removeOmpMcpServer } from './omp-config.js';
|
|
6
|
+
import { piMcpPath, planPiWrite, removePiMcpServer } from './pi-config.js';
|
|
6
7
|
import { HERMES_KEY_VAR, hermesEnvPath, removeHermesKey, resolveHermesHome } from './hermes-env.js';
|
|
7
8
|
import { clearHermesWebBackends, HERMES_PLUGIN_SHA } from './hermes-plugin.js';
|
|
8
9
|
import { HARNESS_DISPLAY_NAMES as DISPLAY_NAMES } from './harness-detect.js';
|
|
@@ -14,6 +15,8 @@ export function planText(harness, mcpUrl, apiKey) {
|
|
|
14
15
|
return planCursorWrite(mcpUrl, apiKey);
|
|
15
16
|
if (harness === 'omp')
|
|
16
17
|
return planOmpWrite(mcpUrl, apiKey);
|
|
18
|
+
if (harness === 'pi')
|
|
19
|
+
return planPiWrite(mcpUrl, apiKey);
|
|
17
20
|
// Only the keyed path seeds the .env; a keyless Hermes install writes nothing.
|
|
18
21
|
if (harness === 'hermes' && apiKey) {
|
|
19
22
|
return (`would run \`tinyfish connect hermes\` (harness-owned MCP write; the CLI writes ` +
|
|
@@ -29,6 +32,9 @@ export function uninstallPlanText(harness) {
|
|
|
29
32
|
if (harness === 'omp') {
|
|
30
33
|
return "would remove the tinyfish entry from omp's mcp.json (located via `omp config path`)";
|
|
31
34
|
}
|
|
35
|
+
if (harness === 'pi') {
|
|
36
|
+
return `would remove the tinyfish entry from ${piMcpPath()}`;
|
|
37
|
+
}
|
|
32
38
|
if (!uninstallRuns(harness)) {
|
|
33
39
|
return `would print \`${uninstallPointer(harness)}\` (no removal command exists)`;
|
|
34
40
|
}
|
|
@@ -62,7 +68,8 @@ function uninstallPointer(harness) {
|
|
|
62
68
|
}
|
|
63
69
|
/** `mcp remove` clears neither the key nor the backends we wrote. */
|
|
64
70
|
function clearHermesLocalState() {
|
|
65
|
-
const
|
|
71
|
+
const resolved = resolveHermesHome();
|
|
72
|
+
const home = typeof resolved === 'string' ? resolved : undefined;
|
|
66
73
|
const advisories = [clearHermesEnvKey(home), clearHermesBackends(home)].filter((advisory) => advisory !== undefined);
|
|
67
74
|
return advisories.length > 0 ? advisories.join(' ') : undefined;
|
|
68
75
|
}
|
|
@@ -186,6 +193,10 @@ const MCP_JSON_UNINSTALLS = {
|
|
|
186
193
|
remove: removeOmpMcpServer,
|
|
187
194
|
fix: "Fix omp's mcp.json (at `omp config path`) by hand, then re-run: tinyfish connect --all --uninstall",
|
|
188
195
|
},
|
|
196
|
+
pi: {
|
|
197
|
+
remove: removePiMcpServer,
|
|
198
|
+
fix: `Fix ${piMcpPath()} by hand, then re-run: tinyfish connect --all --uninstall`,
|
|
199
|
+
},
|
|
189
200
|
};
|
|
190
201
|
function uninstallMcpJson(detection, spec) {
|
|
191
202
|
let result;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Harness } from './harness-detect.js';
|
|
1
|
+
import { type Harness, type HarnessDetection } from './harness-detect.js';
|
|
2
2
|
import { type VerifyDepth } from './verify.js';
|
|
3
|
-
export type Outcome = 'not_detected' | 'not_installed' | 'stale_config' | 'installed' | 'failed' | 'harness_too_old' | 'interrupted' | 'auth_pending' | 'no_tty_auth_skip' | 'uninstalled' | 'uninstall_noop' | 'uninstall_pointer' | 'dry_run' | 'not_reached' | 'deselected';
|
|
3
|
+
export type Outcome = 'not_detected' | 'not_installed' | 'stale_config' | 'needs_first_run' | 'installed' | 'failed' | 'harness_too_old' | 'interrupted' | 'auth_pending' | 'no_tty_auth_skip' | 'uninstalled' | 'uninstall_noop' | 'uninstall_pointer' | 'dry_run' | 'not_reached' | 'deselected';
|
|
4
4
|
export type HarnessResultBase = {
|
|
5
5
|
harness: Harness;
|
|
6
6
|
detected: boolean;
|
|
@@ -34,5 +34,7 @@ export interface ConnectAllOptions {
|
|
|
34
34
|
/** Setup-page id riding in on --url; joins "copied the command" to this run. */
|
|
35
35
|
attemptId?: string;
|
|
36
36
|
}
|
|
37
|
+
/** The harness has never created its dir, so connecting would build one it does not own. */
|
|
38
|
+
export declare function needsFirstRun(detection: HarnessDetection): boolean;
|
|
37
39
|
export declare function runConnectAll(opts: ConnectAllOptions): Promise<number>;
|
|
38
40
|
export declare const FIRST_TASK_PROMPT: string;
|