gsd-pi 2.78.0 → 2.78.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/README.md +52 -16
- package/dist/claude-cli-check.js +91 -32
- package/dist/resources/extensions/claude-code-cli/readiness.js +115 -31
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/package.json +2 -2
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +116 -29
- /package/dist/web/standalone/.next/static/{C1zT2kEfoLhDdbWPWKrXd → 7afp7gq8-DVbxum83zRQ-}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{C1zT2kEfoLhDdbWPWKrXd → 7afp7gq8-DVbxum83zRQ-}/_ssgManifest.js +0 -0
|
@@ -5,8 +5,13 @@
|
|
|
5
5
|
* Results are cached for 30 seconds to avoid shelling out on every
|
|
6
6
|
* model-availability check.
|
|
7
7
|
*
|
|
8
|
-
* Auth verification
|
|
9
|
-
*
|
|
8
|
+
* Auth verification runs `claude auth status --json` and inspects the
|
|
9
|
+
* `loggedIn` field, falling back to plain `claude auth status` and a text
|
|
10
|
+
* heuristic when the JSON shape is unavailable (older Claude CLI builds).
|
|
11
|
+
*
|
|
12
|
+
* Set GSD_CLAUDE_DEBUG=1 to print the probe's binary selection and auth
|
|
13
|
+
* outputs to stderr — useful when diagnosing platform-specific detection
|
|
14
|
+
* failures (Issue #4997).
|
|
10
15
|
*/
|
|
11
16
|
|
|
12
17
|
import { execFileSync } from "node:child_process";
|
|
@@ -27,27 +32,109 @@ const CLAUDE_COMMAND = process.platform === "win32" ? "claude.cmd" : "claude";
|
|
|
27
32
|
*/
|
|
28
33
|
const CLAUDE_COMMAND_CANDIDATES = process.platform === "win32" ? [CLAUDE_COMMAND, "claude.exe", "claude"] : [CLAUDE_COMMAND];
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
// Keep the version probe snappy — `claude --version` is a quick path.
|
|
36
|
+
const VERSION_TIMEOUT_MS = 5_000;
|
|
37
|
+
// Auth status can be much slower on Windows because the spawn goes through
|
|
38
|
+
// cmd.exe → claude.cmd → node → Claude CLI. 15s leaves headroom on cold spawns
|
|
39
|
+
// without making startup feel hung when the CLI is genuinely missing.
|
|
40
|
+
const AUTH_TIMEOUT_MS = 15_000;
|
|
41
|
+
|
|
42
|
+
function debugLog(...parts: unknown[]): void {
|
|
43
|
+
if (process.env.GSD_CLAUDE_DEBUG) {
|
|
44
|
+
process.stderr.write(`[claude-readiness] ${parts.map((p) => (typeof p === "string" ? p : JSON.stringify(p))).join(" ")}\n`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Find the first candidate that responds to `--version`. Returns the
|
|
50
|
+
* candidate name on success, null if none worked.
|
|
51
|
+
*
|
|
52
|
+
* On Windows with `shell: true`, a missing candidate surfaces as a
|
|
53
|
+
* non-zero exit from cmd.exe rather than ENOENT — so we cannot rely on
|
|
54
|
+
* the error code to decide "try next". Treat any failure as "try next"
|
|
55
|
+
* for the version probe; the only thing that matters for binary
|
|
56
|
+
* detection is whether *some* candidate produces a `claude --version`
|
|
57
|
+
* line.
|
|
58
|
+
*/
|
|
59
|
+
function findWorkingCommand(): string | null {
|
|
32
60
|
for (const command of CLAUDE_COMMAND_CANDIDATES) {
|
|
33
61
|
try {
|
|
34
|
-
|
|
35
|
-
timeout:
|
|
62
|
+
execFileSync(command, ["--version"], {
|
|
63
|
+
timeout: VERSION_TIMEOUT_MS,
|
|
36
64
|
stdio: "pipe",
|
|
37
65
|
shell: process.platform === "win32",
|
|
38
66
|
});
|
|
67
|
+
debugLog("version probe ok via", command);
|
|
68
|
+
return command;
|
|
39
69
|
} catch (error) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
70
|
+
debugLog("version probe failed for", command, "code=", (error as NodeJS.ErrnoException | undefined)?.code);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Decide auth state from `claude auth status` output.
|
|
79
|
+
*
|
|
80
|
+
* Newer Claude CLI builds emit JSON by default with a `loggedIn` boolean.
|
|
81
|
+
* Older builds emit free-form text. We prefer the structured signal and fall
|
|
82
|
+
* back to a text heuristic. Note: the text heuristic only covers English
|
|
83
|
+
* phrasing — the JSON path is the durable signal.
|
|
84
|
+
*/
|
|
85
|
+
function parseAuthStatus(output: string): boolean | null {
|
|
86
|
+
const trimmed = output.trim();
|
|
87
|
+
if (!trimmed) return null;
|
|
88
|
+
|
|
89
|
+
if (trimmed.startsWith("{")) {
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(trimmed) as { loggedIn?: unknown };
|
|
92
|
+
if (typeof parsed.loggedIn === "boolean") {
|
|
93
|
+
return parsed.loggedIn;
|
|
46
94
|
}
|
|
47
|
-
|
|
95
|
+
} catch {
|
|
96
|
+
// Fall through to text heuristic.
|
|
48
97
|
}
|
|
49
98
|
}
|
|
50
|
-
|
|
99
|
+
|
|
100
|
+
const lower = trimmed.toLowerCase();
|
|
101
|
+
if (/not logged in|no credentials|unauthenticated|not authenticated/.test(lower)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
if (/logged in|authenticated|signed in|email|subscription/.test(lower)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function probeAuth(command: string): boolean | null {
|
|
111
|
+
// Try --json first (newer CLIs).
|
|
112
|
+
try {
|
|
113
|
+
const out = execFileSync(command, ["auth", "status", "--json"], {
|
|
114
|
+
timeout: AUTH_TIMEOUT_MS,
|
|
115
|
+
stdio: "pipe",
|
|
116
|
+
shell: process.platform === "win32",
|
|
117
|
+
}).toString();
|
|
118
|
+
debugLog("auth status --json output:", out.slice(0, 200));
|
|
119
|
+
const parsed = parseAuthStatus(out);
|
|
120
|
+
if (parsed !== null) return parsed;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
debugLog("auth status --json threw:", (error as Error).message?.slice(0, 200));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Fallback: plain `auth status` (older CLIs that don't accept --json).
|
|
126
|
+
try {
|
|
127
|
+
const out = execFileSync(command, ["auth", "status"], {
|
|
128
|
+
timeout: AUTH_TIMEOUT_MS,
|
|
129
|
+
stdio: "pipe",
|
|
130
|
+
shell: process.platform === "win32",
|
|
131
|
+
}).toString();
|
|
132
|
+
debugLog("auth status output:", out.slice(0, 200));
|
|
133
|
+
return parseAuthStatus(out);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
debugLog("auth status threw:", (error as Error).message?.slice(0, 200));
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
51
138
|
}
|
|
52
139
|
|
|
53
140
|
let cachedBinaryPresent: boolean | null = null;
|
|
@@ -55,6 +142,10 @@ let cachedAuthed: boolean | null = null;
|
|
|
55
142
|
let lastCheckMs = 0;
|
|
56
143
|
const CHECK_INTERVAL_MS = 30_000;
|
|
57
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Refresh the cached binary/auth state when the cache window has expired.
|
|
147
|
+
* Preserves a known auth state across soft-fail auth probes.
|
|
148
|
+
*/
|
|
58
149
|
function refreshCache(): void {
|
|
59
150
|
const now = Date.now();
|
|
60
151
|
if (cachedBinaryPresent !== null && now - lastCheckMs < CHECK_INTERVAL_MS) {
|
|
@@ -64,27 +155,23 @@ function refreshCache(): void {
|
|
|
64
155
|
// Set timestamp first to prevent re-entrant checks during the same window
|
|
65
156
|
lastCheckMs = now;
|
|
66
157
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
execClaude(["--version"]);
|
|
70
|
-
cachedBinaryPresent = true;
|
|
71
|
-
} catch {
|
|
158
|
+
const command = findWorkingCommand();
|
|
159
|
+
if (!command) {
|
|
72
160
|
cachedBinaryPresent = false;
|
|
73
161
|
cachedAuthed = false;
|
|
74
162
|
return;
|
|
75
163
|
}
|
|
164
|
+
cachedBinaryPresent = true;
|
|
76
165
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
} catch {
|
|
85
|
-
// Non-zero exit code means not authenticated
|
|
86
|
-
cachedAuthed = false;
|
|
166
|
+
const authed = probeAuth(command);
|
|
167
|
+
if (authed === null) {
|
|
168
|
+
// Couldn't determine auth state from CLI output. Don't clobber a
|
|
169
|
+
// previously known-good cache; otherwise default to false so we don't
|
|
170
|
+
// silently route requests to an unauthenticated CLI.
|
|
171
|
+
if (cachedAuthed === null) cachedAuthed = false;
|
|
172
|
+
return;
|
|
87
173
|
}
|
|
174
|
+
cachedAuthed = authed;
|
|
88
175
|
}
|
|
89
176
|
|
|
90
177
|
/**
|
|
File without changes
|
|
File without changes
|