@tiny-fish/cli 0.9.1-next.124 → 0.10.1
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.js +6 -2
- package/dist/lib/auth.d.ts +7 -0
- package/dist/lib/auth.js +38 -6
- package/dist/lib/client.d.ts +5 -0
- package/dist/lib/client.js +33 -0
- package/package.json +2 -2
package/dist/commands/connect.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import spawn from "cross-spawn";
|
|
4
|
-
import { persistApiKeyToEnvironment, saveConfig, validateKeyFormat } from "../lib/auth.js";
|
|
4
|
+
import { CONNECT_SOURCE, persistApiKeyToEnvironment, saveConfig, saveConnectContext, validateKeyFormat, } from "../lib/auth.js";
|
|
5
5
|
import { CLI_VERSION } from "../lib/constants.js";
|
|
6
6
|
import { errLine } from "../lib/output.js";
|
|
7
7
|
const DEFAULT_MCP_URL = "https://agent.tinyfish.ai/mcp";
|
|
@@ -347,7 +347,7 @@ async function connectNativeMcpClient(client, options) {
|
|
|
347
347
|
for (const removal of client.removals)
|
|
348
348
|
removeExistingRegistration(client, removal);
|
|
349
349
|
const mcpUrl = new URL(options.mcpUrl);
|
|
350
|
-
mcpUrl.searchParams.set("source",
|
|
350
|
+
mcpUrl.searchParams.set("source", CONNECT_SOURCE);
|
|
351
351
|
mcpUrl.searchParams.set("client", client.connectClient);
|
|
352
352
|
mcpUrl.searchParams.set("connect_attempt_id", telemetry.attemptId);
|
|
353
353
|
failureStage = client.loginArgs ? "registration" : "registration_or_authentication";
|
|
@@ -378,6 +378,9 @@ async function connectNativeMcpClient(client, options) {
|
|
|
378
378
|
installWebSkill(client);
|
|
379
379
|
failureStage = "authentication";
|
|
380
380
|
ensureCliAuthenticated(client.connectClient, options.apiKey);
|
|
381
|
+
// Persist once here (skill installed + authenticated) so both success paths, launch
|
|
382
|
+
// or not, record the install; buildConnectHeaders reads it on subsequent requests.
|
|
383
|
+
saveConnectContext(client.connectClient, telemetry.attemptId);
|
|
381
384
|
if (!options.launch) {
|
|
382
385
|
errLine(`TinyFish is connected. Open ${client.displayName} to start using it.`);
|
|
383
386
|
await telemetry.track("completed");
|
|
@@ -449,6 +452,7 @@ export async function connectOpenClaw(options) {
|
|
|
449
452
|
});
|
|
450
453
|
}
|
|
451
454
|
}
|
|
455
|
+
saveConnectContext("openclaw", telemetry.attemptId);
|
|
452
456
|
if (!options.launch) {
|
|
453
457
|
errLine("TinyFish is connected. Open OpenClaw to start using it.");
|
|
454
458
|
await telemetry.track("completed");
|
package/dist/lib/auth.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
export { DASHBOARD_URL, getDashboardUrl } from "./constants.js";
|
|
2
2
|
export declare function configDir(): string;
|
|
3
|
+
/** Install channel baked onto connect context; the connect flow is always CLI-driven. */
|
|
4
|
+
export declare const CONNECT_SOURCE = "tinyfish_cli";
|
|
5
|
+
interface ConnectEntry {
|
|
6
|
+
attempt_id: string;
|
|
7
|
+
}
|
|
3
8
|
interface TinyfishConfig {
|
|
4
9
|
api_key?: string;
|
|
10
|
+
connect?: Record<string, ConnectEntry>;
|
|
5
11
|
}
|
|
6
12
|
export declare function loadConfig(): TinyfishConfig;
|
|
7
13
|
export declare function saveConfig(apiKey: string): void;
|
|
14
|
+
export declare function saveConnectContext(client: string, attemptId: string): void;
|
|
8
15
|
interface EnvironmentOptions {
|
|
9
16
|
platform?: typeof process.platform;
|
|
10
17
|
shell?: string;
|
package/dist/lib/auth.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as os from "os";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import spawn from "cross-spawn";
|
|
5
|
-
import { err } from "./output.js";
|
|
5
|
+
import { err, errLine } from "./output.js";
|
|
6
6
|
const KEY_PREFIXES = ["sk-tinyfish-", "sk-mino-"];
|
|
7
7
|
const MIN_KEY_LENGTH = 20;
|
|
8
8
|
export { DASHBOARD_URL, getDashboardUrl } from "./constants.js";
|
|
@@ -13,19 +13,37 @@ export function configDir() {
|
|
|
13
13
|
function configFile() {
|
|
14
14
|
return path.join(configDir(), "config.json");
|
|
15
15
|
}
|
|
16
|
+
/** Install channel baked onto connect context; the connect flow is always CLI-driven. */
|
|
17
|
+
export const CONNECT_SOURCE = "tinyfish_cli";
|
|
16
18
|
export function loadConfig() {
|
|
17
19
|
try {
|
|
18
20
|
const raw = JSON.parse(fs.readFileSync(configFile(), "utf8"));
|
|
19
|
-
if (raw
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
21
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
22
|
+
return {};
|
|
23
|
+
const obj = raw;
|
|
24
|
+
const config = {};
|
|
25
|
+
if (typeof obj["api_key"] === "string")
|
|
26
|
+
config.api_key = obj["api_key"];
|
|
27
|
+
const connect = parseConnectMap(obj["connect"]);
|
|
28
|
+
if (connect)
|
|
29
|
+
config.connect = connect;
|
|
30
|
+
return config;
|
|
24
31
|
}
|
|
25
32
|
catch {
|
|
26
33
|
return {};
|
|
27
34
|
}
|
|
28
35
|
}
|
|
36
|
+
function parseConnectMap(value) {
|
|
37
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
38
|
+
return undefined;
|
|
39
|
+
const out = {};
|
|
40
|
+
for (const [client, entry] of Object.entries(value)) {
|
|
41
|
+
const attemptId = entry?.attempt_id;
|
|
42
|
+
if (typeof attemptId === "string")
|
|
43
|
+
out[client] = { attempt_id: attemptId };
|
|
44
|
+
}
|
|
45
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
46
|
+
}
|
|
29
47
|
export function saveConfig(apiKey) {
|
|
30
48
|
const dir = configDir();
|
|
31
49
|
try {
|
|
@@ -42,6 +60,20 @@ export function saveConfig(apiKey) {
|
|
|
42
60
|
process.exit(1);
|
|
43
61
|
}
|
|
44
62
|
}
|
|
63
|
+
export function saveConnectContext(client, attemptId) {
|
|
64
|
+
const dir = configDir();
|
|
65
|
+
try {
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
67
|
+
const existing = loadConfig();
|
|
68
|
+
const connect = { ...existing.connect, [client]: { attempt_id: attemptId } };
|
|
69
|
+
fs.writeFileSync(configFile(), JSON.stringify({ ...existing, connect }), { mode: 0o600 });
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
// Best-effort attribution marker: a write failure must not fail a connect that has
|
|
73
|
+
// already succeeded (MCP registered, skill installed, authenticated). Warn and continue.
|
|
74
|
+
errLine(`Warning: could not persist connect context: ${e instanceof Error ? e.message : String(e)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
45
77
|
export function persistApiKeyToEnvironment(apiKey, options = {}) {
|
|
46
78
|
const platform = options.platform ?? process.platform;
|
|
47
79
|
if (platform === "win32") {
|
package/dist/lib/client.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type AgentRunAsyncResponse, type AgentRunResponse, type AgentRunWithStreamingResponse, type FetchGetContentsParams, type FetchResponse, type BrowserSession, type Run, type RunListParams, type RunListResponse, type SearchQueryParams, type SearchQueryResponse } from "@tiny-fish/sdk";
|
|
2
2
|
import type { BatchCancelResponse, BatchGetResponse, BatchRunRequest, BatchRunResponse, CancelRunResponse, CliAgentRunParams, CliBrowserSessionCreateParams, CookieRecord, ProfileCreateResponse, ProfileUploadResponse, RunStepsResponse, VaultConnectRequest, VaultConnectResponse, VaultConnectionsListResponse, VaultDisconnectResponse, VaultItemsListResponse, VaultItemsSyncResponse } from "./types.js";
|
|
3
|
+
type ConnectMap = Record<string, {
|
|
4
|
+
attempt_id: string;
|
|
5
|
+
}> | undefined;
|
|
6
|
+
export declare function buildConnectHeaders(connect: ConnectMap, caller: string | null): Record<string, string>;
|
|
3
7
|
export declare function runSync(req: CliAgentRunParams, apiKey: string): Promise<AgentRunResponse>;
|
|
4
8
|
export declare function runAsync(req: CliAgentRunParams, apiKey: string): Promise<AgentRunAsyncResponse>;
|
|
5
9
|
export declare function runStream(req: CliAgentRunParams, apiKey: string, signal?: AbortSignal): AsyncGenerator<AgentRunWithStreamingResponse>;
|
|
@@ -20,3 +24,4 @@ export declare function vaultConnect(req: VaultConnectRequest, apiKey: string):
|
|
|
20
24
|
export declare function vaultDisconnect(connectionId: string, apiKey: string): Promise<VaultDisconnectResponse>;
|
|
21
25
|
export declare function vaultItems(apiKey: string): Promise<VaultItemsListResponse>;
|
|
22
26
|
export declare function vaultSync(apiKey: string): Promise<VaultItemsSyncResponse>;
|
|
27
|
+
export {};
|
package/dist/lib/client.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
import { APIStatusError, agentRunAsyncResponseSchema, agentRunResponseSchema, agentRunWithStreamingResponseSchema, fetchResponseSchema, browserSessionSchema, searchQueryResponseSchema, TinyFish, runSchema, runStatusSchema, } from "@tiny-fish/sdk";
|
|
2
|
+
import { CONNECT_SOURCE, loadConfig } from "./auth.js";
|
|
2
3
|
import { API_URL_OVERRIDE, CLI_VERSION } from "./constants.js";
|
|
3
4
|
import { detectHarness, detectHumanInitiated } from "./harness.js";
|
|
4
5
|
import { ApiError } from "./output.js";
|
|
5
6
|
import { z } from "zod";
|
|
7
|
+
// Install-level connect context (PF-3169). A known caller is credited only to its own
|
|
8
|
+
// install; under an unknown caller we credit a lone install, else send source only.
|
|
9
|
+
// Never guess a client. Request-level harness truth is client_name (PF-3168).
|
|
10
|
+
export function buildConnectHeaders(connect, caller) {
|
|
11
|
+
if (!connect)
|
|
12
|
+
return {};
|
|
13
|
+
const clients = Object.keys(connect);
|
|
14
|
+
if (clients.length === 0)
|
|
15
|
+
return {};
|
|
16
|
+
if (caller) {
|
|
17
|
+
const entry = connect[caller];
|
|
18
|
+
return entry ? connectHeadersFor(caller, entry.attempt_id) : {};
|
|
19
|
+
}
|
|
20
|
+
if (clients.length === 1)
|
|
21
|
+
return connectHeadersFor(clients[0], connect[clients[0]].attempt_id);
|
|
22
|
+
return { "X-TF-Connect-Source": CONNECT_SOURCE };
|
|
23
|
+
}
|
|
24
|
+
function connectHeadersFor(client, attemptId) {
|
|
25
|
+
return {
|
|
26
|
+
"X-TF-Connect-Source": CONNECT_SOURCE,
|
|
27
|
+
"X-TF-Connect-Client": client,
|
|
28
|
+
"X-TF-Connect-Attempt-Id": attemptId,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
6
31
|
class TinyFishCliClient extends TinyFish {
|
|
7
32
|
_buildHeaders() {
|
|
8
33
|
const base = super._buildHeaders();
|
|
@@ -28,6 +53,14 @@ class TinyFishCliClient extends TinyFish {
|
|
|
28
53
|
headers["X-TF-Client-Version"] = clientVersion;
|
|
29
54
|
// Agent-vs-human signal, independent of harness detection or skill install.
|
|
30
55
|
headers["X-TF-Human-Initiated"] = detectHumanInitiated() ? "true" : "false";
|
|
56
|
+
// Reuse the already-resolved client_name so connect_client can never diverge from it
|
|
57
|
+
// (same sanitize + precedence, not a second raw read of TF_CLIENT_NAME).
|
|
58
|
+
const connectCaller = clientName || null;
|
|
59
|
+
for (const [key, value] of Object.entries(buildConnectHeaders(loadConfig().connect, connectCaller))) {
|
|
60
|
+
const clean = sanitize(value);
|
|
61
|
+
if (clean)
|
|
62
|
+
headers[key] = clean;
|
|
63
|
+
}
|
|
31
64
|
return headers;
|
|
32
65
|
}
|
|
33
66
|
// The SDK base client only exposes get/post/postStream; DELETE is needed for
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiny-fish/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "TinyFish CLI — run web automations from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"prepublishOnly": "npm run build"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@tiny-fish/sdk": "^0.0
|
|
35
|
+
"@tiny-fish/sdk": "^0.2.0",
|
|
36
36
|
"chrome-cookies-secure": "^3.0.2",
|
|
37
37
|
"commander": "^12.0.0",
|
|
38
38
|
"cross-spawn": "^7.0.6",
|