@tiny-fish/cli 0.29.1-next.241 → 0.29.1-next.242

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.
@@ -199,7 +199,7 @@ function runPostInstallSteps(client, options, state, telemetry, signInDeferred)
199
199
  });
200
200
  }
201
201
  // Before auth: a failed sign-in must not lose the harness record.
202
- saveConnectContext(client.connectClient, telemetry.attemptId);
202
+ saveConnectContext(client.connectClient, telemetry.attemptId, state.authMode);
203
203
  attempt("authentication", () => {
204
204
  // A set timeout means headless; never shell an interactive login.
205
205
  ensureCliAuthenticated(client.connectClient, options.apiKey, {
@@ -130,6 +130,14 @@ function registrationVerdict(status, mcpUrl, keyAuth, cliKeyMissing) {
130
130
  // Widening past api-key would warn every healthy oauth install.
131
131
  if (status.authMode === AuthMode.ApiKey)
132
132
  return keyedVerdict(status, keyAuth, cliKeyMissing);
133
+ // Recorded, not verified: it never feeds provesHarnessReach.
134
+ if (status.recordedAuthMode) {
135
+ return {
136
+ status: "pass",
137
+ detail: `registered, auth mode ${status.recordedAuthMode} ` +
138
+ "(recorded at connect; this harness does not report its headers)",
139
+ };
140
+ }
133
141
  // A probe that had to explain itself to reach `yes` is the only thing that explains `unknown`.
134
142
  const because = status.reason ? ` (${status.reason})` : "";
135
143
  return { status: "pass", detail: `registered, auth mode ${status.authMode}${because}` };
@@ -258,6 +266,7 @@ export async function runDoctor(options) {
258
266
  detected: status.detected,
259
267
  registered: status.registered,
260
268
  auth_mode: status.authMode,
269
+ ...(status.recordedAuthMode ? { recorded_auth_mode: status.recordedAuthMode } : {}),
261
270
  proves_harness_reach: provesHarnessReach(status, keyAuths[i], healthOk, options.mcpUrl),
262
271
  }));
263
272
  const body = {
@@ -3,8 +3,11 @@ export declare function configDir(): string;
3
3
  /** Install channel baked onto connect context; the connect flow is always CLI-driven. */
4
4
  export declare const CONNECT_SOURCE = "tinyfish_cli";
5
5
  export declare const CONNECT_ATTEMPT_ENV = "TINYFISH_CONNECT_ATTEMPT_ID";
6
- interface ConnectEntry {
6
+ /** Mirrors ConnectAuthMode; duplicated to keep auth.ts free of connect-runtime imports. */
7
+ export type RecordedAuthMode = "api-key" | "oauth" | "deferred";
8
+ export interface ConnectEntry {
7
9
  attempt_id: string;
10
+ auth_mode?: RecordedAuthMode;
8
11
  }
9
12
  interface PendingConnectAttempt {
10
13
  id: string;
@@ -27,7 +30,8 @@ export declare function takePendingConnectAttempt(): string | undefined;
27
30
  /** Throwing variant for callers with their own cleanup (connect's telemetry flush). */
28
31
  export declare function writeConfig(apiKey: string): void;
29
32
  export declare function saveConfig(apiKey: string): void;
30
- export declare function saveConnectContext(client: string, attemptId: string): void;
33
+ /** `authMode` omitted rewrites the entry without one: a reconnect that degraded must not keep a stale key claim. */
34
+ export declare function saveConnectContext(client: string, attemptId: string, authMode?: RecordedAuthMode): void;
31
35
  interface EnvironmentOptions {
32
36
  platform?: typeof process.platform;
33
37
  shell?: string;
package/dist/lib/auth.js CHANGED
@@ -19,6 +19,7 @@ export const CONNECT_SOURCE = "tinyfish_cli";
19
19
  // An env var, not a flag: released CLIs ignore an unknown one, and the installer's PATH
20
20
  // prepend means a copied command can still run a CLI far older than the page that wrote it.
21
21
  export const CONNECT_ATTEMPT_ENV = "TINYFISH_CONNECT_ATTEMPT_ID";
22
+ const RECORDED_AUTH_MODES = ["api-key", "oauth", "deferred"];
22
23
  export function loadConfig() {
23
24
  try {
24
25
  const raw = JSON.parse(fs.readFileSync(configFile(), "utf8"));
@@ -45,9 +46,15 @@ function parseConnectMap(value) {
45
46
  return undefined;
46
47
  const out = {};
47
48
  for (const [client, entry] of Object.entries(value)) {
48
- const attemptId = entry?.attempt_id;
49
- if (typeof attemptId === "string")
50
- out[client] = { attempt_id: attemptId };
49
+ const record = entry;
50
+ const attemptId = record?.attempt_id;
51
+ if (typeof attemptId !== "string")
52
+ continue;
53
+ const authMode = record?.auth_mode;
54
+ out[client] =
55
+ typeof authMode === "string" && RECORDED_AUTH_MODES.includes(authMode)
56
+ ? { attempt_id: attemptId, auth_mode: authMode }
57
+ : { attempt_id: attemptId };
51
58
  }
52
59
  return Object.keys(out).length > 0 ? out : undefined;
53
60
  }
@@ -118,11 +125,15 @@ export function saveConfig(apiKey) {
118
125
  process.exit(1);
119
126
  }
120
127
  }
121
- export function saveConnectContext(client, attemptId) {
128
+ /** `authMode` omitted rewrites the entry without one: a reconnect that degraded must not keep a stale key claim. */
129
+ export function saveConnectContext(client, attemptId, authMode) {
130
+ const entry = authMode
131
+ ? { attempt_id: attemptId, auth_mode: authMode }
132
+ : { attempt_id: attemptId };
122
133
  try {
123
134
  updateConfig((config) => ({
124
135
  ...config,
125
- connect: { ...config.connect, [client]: { attempt_id: attemptId } },
136
+ connect: { ...config.connect, [client]: entry },
126
137
  }));
127
138
  }
128
139
  catch (e) {
@@ -51,6 +51,11 @@ declare const doctorHarnessSchema: z.ZodObject<{
51
51
  detected: z.ZodBoolean;
52
52
  registered: z.ZodEnum<typeof Registered>;
53
53
  auth_mode: z.ZodEnum<typeof AuthMode>;
54
+ recorded_auth_mode: z.ZodOptional<z.ZodEnum<{
55
+ "api-key": "api-key";
56
+ oauth: "oauth";
57
+ deferred: "deferred";
58
+ }>>;
54
59
  proves_harness_reach: z.ZodBoolean;
55
60
  }, z.core.$strip>;
56
61
  declare const doctorRepairSchema: z.ZodObject<{
@@ -114,6 +119,11 @@ export declare const doctorReportSchema: z.ZodObject<{
114
119
  detected: z.ZodBoolean;
115
120
  registered: z.ZodEnum<typeof Registered>;
116
121
  auth_mode: z.ZodEnum<typeof AuthMode>;
122
+ recorded_auth_mode: z.ZodOptional<z.ZodEnum<{
123
+ "api-key": "api-key";
124
+ oauth: "oauth";
125
+ deferred: "deferred";
126
+ }>>;
117
127
  proves_harness_reach: z.ZodBoolean;
118
128
  }, z.core.$strip>>;
119
129
  repairs: z.ZodArray<z.ZodObject<{
@@ -19,6 +19,8 @@ const doctorHarnessSchema = z.object({
19
19
  detected: z.boolean(),
20
20
  registered: z.enum(Registered),
21
21
  auth_mode: z.enum(AuthMode),
22
+ // Additive under schema 3: what connect wrote when the harness cannot echo it back.
23
+ recorded_auth_mode: z.enum(["api-key", "oauth", "deferred"]).optional(),
22
24
  proves_harness_reach: z.boolean(),
23
25
  });
24
26
  // `action` is what `--fix` dispatches on, not the harness field: keying off a null harness
@@ -1,3 +1,4 @@
1
+ import { type RecordedAuthMode } from "./auth.js";
1
2
  import type { AgentClient } from "./connect-runtime.js";
2
3
  import { AuthMode, type Harness, Registered } from "./harness-detect.js";
3
4
  export interface RegistrationStatus {
@@ -15,6 +16,8 @@ export interface RegistrationStatus {
15
16
  connected?: boolean;
16
17
  /** Why a keyed registration's key could not be read; not `reason`. */
17
18
  keyReason?: string;
19
+ /** What connect wrote, for harnesses whose CLI never echoes it back. Recorded, not verified. */
20
+ recordedAuthMode?: RecordedAuthMode;
18
21
  reason?: string;
19
22
  }
20
23
  export declare function codexOauthCompleted(): boolean | undefined;
@@ -458,10 +458,12 @@ export function detectRegistrations(harnesses) {
458
458
  return detectInstalledHarnesses()
459
459
  .filter((detection) => !harnesses || harnesses.includes(detection.harness))
460
460
  .map((detection) => {
461
+ const recordedAuthMode = connect[detection.harness]?.auth_mode;
461
462
  const base = {
462
463
  harness: detection.harness,
463
464
  detected: detection.detected,
464
465
  connectedBefore: detection.harness in connect,
466
+ ...(recordedAuthMode ? { recordedAuthMode } : {}),
465
467
  };
466
468
  // Undetected means nothing to probe: no spawn cost, and no misleading "unknown".
467
469
  if (!detection.detected) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-fish/cli",
3
- "version": "0.29.1-next.241",
3
+ "version": "0.29.1-next.242",
4
4
  "description": "TinyFish CLI — run web automations from your terminal",
5
5
  "type": "module",
6
6
  "bin": {