gsd-pi 2.69.0-dev.232905b → 2.69.0-dev.87298d1
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/resources/extensions/gsd/bootstrap/system-context.js +2 -6
- package/dist/resources/extensions/gsd/commands-cmux.js +1 -30
- package/dist/resources/extensions/gsd/workflow-mcp.js +6 -53
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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/api/onboarding/route.js +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 +9 -9
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +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/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/daemon/src/orchestrator.ts +84 -9
- package/packages/mcp-server/README.md +3 -25
- package/packages/mcp-server/dist/cli.d.ts +1 -0
- package/packages/mcp-server/dist/cli.d.ts.map +1 -1
- package/packages/mcp-server/dist/cli.js +2 -4
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts +1 -32
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +1 -118
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +0 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +2 -274
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/cli.ts +3 -5
- package/packages/mcp-server/src/mcp-server.test.ts +1 -85
- package/packages/mcp-server/src/server.ts +1 -188
- package/packages/mcp-server/src/workflow-tools.test.ts +25 -32
- package/packages/mcp-server/src/workflow-tools.ts +2 -365
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +23 -1
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts +17 -0
- package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +106 -0
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/index.d.ts +2 -3
- package/packages/pi-ai/dist/utils/oauth/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/index.js +5 -3
- package/packages/pi-ai/dist/utils/oauth/index.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic.ts +31 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +140 -0
- package/packages/pi-ai/src/utils/oauth/index.ts +5 -3
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +5 -9
- package/src/resources/extensions/gsd/commands-cmux.ts +1 -32
- package/src/resources/extensions/gsd/tests/cmux.test.ts +1 -67
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +2 -6
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +7 -23
- package/src/resources/extensions/gsd/workflow-mcp.ts +5 -59
- package/packages/mcp-server/dist/tool-credentials.d.ts +0 -6
- package/packages/mcp-server/dist/tool-credentials.d.ts.map +0 -1
- package/packages/mcp-server/dist/tool-credentials.js +0 -90
- package/packages/mcp-server/dist/tool-credentials.js.map +0 -1
- package/packages/mcp-server/src/tool-credentials.test.ts +0 -95
- package/packages/mcp-server/src/tool-credentials.ts +0 -97
- /package/dist/web/standalone/.next/static/{CHeIRZw-E97oO51g7zDvv → PYwFks7YuKWnl48wsvjo4}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{CHeIRZw-E97oO51g7zDvv → PYwFks7YuKWnl48wsvjo4}/_ssgManifest.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/utils/oauth/anthropic.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Anthropic OAuth flow (Claude Pro/Max)\n */\n\nimport { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl\");\nconst AUTHORIZE_URL = \"https://claude.ai/oauth/authorize\";\nconst TOKEN_URL = \"https://platform.claude.com/v1/oauth/token\";\nconst REDIRECT_URI = \"https://platform.claude.com/oauth/code/callback\";\nconst SCOPES = \"org:create_api_key user:profile user:inference\";\n\n/**\n * Login with Anthropic OAuth (device code flow)\n *\n * @param onAuthUrl - Callback to handle the authorization URL (e.g., open browser)\n * @param onPromptCode - Callback to prompt user for the authorization code\n */\nexport async function loginAnthropic(\n\tonAuthUrl: (url: string) => void,\n\tonPromptCode: () => Promise<string>,\n): Promise<OAuthCredentials> {\n\tconst { verifier, challenge } = await generatePKCE();\n\n\t// Build authorization URL\n\tconst authParams = new URLSearchParams({\n\t\tcode: \"true\",\n\t\tclient_id: CLIENT_ID,\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: REDIRECT_URI,\n\t\tscope: SCOPES,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\tstate: verifier,\n\t});\n\n\tconst authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;\n\n\t// Notify caller with URL to open\n\tonAuthUrl(authUrl);\n\n\t// Wait for user to paste authorization code (format: code#state)\n\tconst authCode = await onPromptCode();\n\tconst splits = authCode.split(\"#\");\n\tconst code = splits[0];\n\tconst state = splits[1];\n\n\t// Exchange code for tokens\n\tconst tokenResponse = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tcode: code,\n\t\t\tstate: state,\n\t\t\tredirect_uri: REDIRECT_URI,\n\t\t\tcode_verifier: verifier,\n\t\t}),\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\n\tif (!tokenResponse.ok) {\n\t\tconst error = await tokenResponse.text();\n\t\tthrow new Error(`Token exchange failed: ${error}`);\n\t}\n\n\tconst tokenData = (await tokenResponse.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t};\n\n\t// Calculate expiry time (current time + expires_in seconds - 5 min buffer)\n\tconst expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000;\n\n\t// Save credentials\n\treturn {\n\t\trefresh: tokenData.refresh_token,\n\t\taccess: tokenData.access_token,\n\t\texpires: expiresAt,\n\t};\n}\n\n/**\n * Refresh Anthropic OAuth token\n */\nexport async function refreshAnthropicToken(refreshToken: string): Promise<OAuthCredentials> {\n\tconst response = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t}),\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\n\tif (!response.ok) {\n\t\tconst error = await response.text();\n\t\tthrow new Error(`Anthropic token refresh failed: ${error}`);\n\t}\n\n\tconst data = (await response.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t};\n\n\treturn {\n\t\trefresh: data.refresh_token,\n\t\taccess: data.access_token,\n\t\texpires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nexport const anthropicOAuthProvider: OAuthProviderInterface = {\n\tid: \"anthropic\",\n\tname: \"Anthropic (Claude Pro/Max)\",\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginAnthropic(\n\t\t\t(url) => callbacks.onAuth({ url }),\n\t\t\t() => callbacks.onPrompt({ message: \"Paste the authorization code:\" }),\n\t\t);\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshAnthropicToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n"]}
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This module handles login, token refresh, and credential storage
|
|
5
5
|
* for OAuth-based providers:
|
|
6
|
+
* - Anthropic (Claude Pro/Max)
|
|
6
7
|
* - GitHub Copilot
|
|
7
8
|
* - Google Cloud Code Assist (Gemini CLI)
|
|
8
9
|
* - Antigravity (Gemini 3, Claude, GPT-OSS via Google Cloud)
|
|
9
|
-
*
|
|
10
|
-
* Note: Anthropic OAuth was removed per TOS compliance (see docs/user-docs/claude-code-auth-compliance.md).
|
|
11
|
-
* Use API keys or the local Claude Code CLI for Anthropic access.
|
|
12
10
|
*/
|
|
11
|
+
export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
|
|
13
12
|
export { getGitHubCopilotBaseUrl, githubCopilotOAuthProvider, loginGitHubCopilot, normalizeDomain, refreshGitHubCopilotToken, } from "./github-copilot.js";
|
|
14
13
|
export { antigravityOAuthProvider, loginAntigravity, refreshAntigravityToken } from "./google-antigravity.js";
|
|
15
14
|
export { geminiCliOAuthProvider, loginGeminiCli, refreshGoogleCloudToken } from "./google-gemini-cli.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE/F,OAAO,EACN,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,eAAe,EACf,yBAAyB,GACzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAE9G,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEzG,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAExG,cAAc,YAAY,CAAC;AAW3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAc5F;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,eAAe,GAAG,sBAAsB,GAAG,SAAS,CAExF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAOxD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,sBAAsB,EAAE,CAE5D;AAMD;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC3C,OAAO,CAAC;IAAE,cAAc,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAsBtE"}
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This module handles login, token refresh, and credential storage
|
|
5
5
|
* for OAuth-based providers:
|
|
6
|
+
* - Anthropic (Claude Pro/Max)
|
|
6
7
|
* - GitHub Copilot
|
|
7
8
|
* - Google Cloud Code Assist (Gemini CLI)
|
|
8
9
|
* - Antigravity (Gemini 3, Claude, GPT-OSS via Google Cloud)
|
|
9
|
-
*
|
|
10
|
-
* Note: Anthropic OAuth was removed per TOS compliance (see docs/user-docs/claude-code-auth-compliance.md).
|
|
11
|
-
* Use API keys or the local Claude Code CLI for Anthropic access.
|
|
12
10
|
*/
|
|
11
|
+
// Anthropic
|
|
12
|
+
export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
|
|
13
13
|
// GitHub Copilot
|
|
14
14
|
export { getGitHubCopilotBaseUrl, githubCopilotOAuthProvider, loginGitHubCopilot, normalizeDomain, refreshGitHubCopilotToken, } from "./github-copilot.js";
|
|
15
15
|
// Google Antigravity
|
|
@@ -22,11 +22,13 @@ export * from "./types.js";
|
|
|
22
22
|
// ============================================================================
|
|
23
23
|
// Provider Registry
|
|
24
24
|
// ============================================================================
|
|
25
|
+
import { anthropicOAuthProvider } from "./anthropic.js";
|
|
25
26
|
import { githubCopilotOAuthProvider } from "./github-copilot.js";
|
|
26
27
|
import { antigravityOAuthProvider } from "./google-antigravity.js";
|
|
27
28
|
import { geminiCliOAuthProvider } from "./google-gemini-cli.js";
|
|
28
29
|
import { openaiCodexOAuthProvider } from "./openai-codex.js";
|
|
29
30
|
const BUILT_IN_OAUTH_PROVIDERS = [
|
|
31
|
+
anthropicOAuthProvider,
|
|
30
32
|
githubCopilotOAuthProvider,
|
|
31
33
|
geminiCliOAuthProvider,
|
|
32
34
|
antigravityOAuthProvider,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/oauth/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OAuth credential management for AI providers.\n *\n * This module handles login, token refresh, and credential storage\n * for OAuth-based providers:\n * - Anthropic (Claude Pro/Max)\n * - GitHub Copilot\n * - Google Cloud Code Assist (Gemini CLI)\n * - Antigravity (Gemini 3, Claude, GPT-OSS via Google Cloud)\n */\n\n// Anthropic\nexport { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from \"./anthropic.js\";\n// GitHub Copilot\nexport {\n\tgetGitHubCopilotBaseUrl,\n\tgithubCopilotOAuthProvider,\n\tloginGitHubCopilot,\n\tnormalizeDomain,\n\trefreshGitHubCopilotToken,\n} from \"./github-copilot.js\";\n// Google Antigravity\nexport { antigravityOAuthProvider, loginAntigravity, refreshAntigravityToken } from \"./google-antigravity.js\";\n// Google Gemini CLI\nexport { geminiCliOAuthProvider, loginGeminiCli, refreshGoogleCloudToken } from \"./google-gemini-cli.js\";\n// OpenAI Codex (ChatGPT OAuth)\nexport { loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from \"./openai-codex.js\";\n\nexport * from \"./types.js\";\n\n// ============================================================================\n// Provider Registry\n// ============================================================================\n\nimport { anthropicOAuthProvider } from \"./anthropic.js\";\nimport { githubCopilotOAuthProvider } from \"./github-copilot.js\";\nimport { antigravityOAuthProvider } from \"./google-antigravity.js\";\nimport { geminiCliOAuthProvider } from \"./google-gemini-cli.js\";\nimport { openaiCodexOAuthProvider } from \"./openai-codex.js\";\nimport type { OAuthCredentials, OAuthProviderId, OAuthProviderInterface } from \"./types.js\";\n\nconst BUILT_IN_OAUTH_PROVIDERS: OAuthProviderInterface[] = [\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\tgeminiCliOAuthProvider,\n\tantigravityOAuthProvider,\n\topenaiCodexOAuthProvider,\n];\n\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>(\n\tBUILT_IN_OAUTH_PROVIDERS.map((provider) => [provider.id, provider]),\n);\n\n/**\n * Get an OAuth provider by ID\n */\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n\treturn oauthProviderRegistry.get(id);\n}\n\n/**\n * Register a custom OAuth provider\n */\nexport function registerOAuthProvider(provider: OAuthProviderInterface): void {\n\toauthProviderRegistry.set(provider.id, provider);\n}\n\n/**\n * Unregister an OAuth provider.\n *\n * If the provider is built-in, restores the built-in implementation.\n * Custom providers are removed completely.\n */\nexport function unregisterOAuthProvider(id: string): void {\n\tconst builtInProvider = BUILT_IN_OAUTH_PROVIDERS.find((provider) => provider.id === id);\n\tif (builtInProvider) {\n\t\toauthProviderRegistry.set(id, builtInProvider);\n\t\treturn;\n\t}\n\toauthProviderRegistry.delete(id);\n}\n\n/**\n * Reset OAuth providers to built-ins.\n */\nexport function resetOAuthProviders(): void {\n\toauthProviderRegistry.clear();\n\tfor (const provider of BUILT_IN_OAUTH_PROVIDERS) {\n\t\toauthProviderRegistry.set(provider.id, provider);\n\t}\n}\n\n/**\n * Get all registered OAuth providers\n */\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n\treturn Array.from(oauthProviderRegistry.values());\n}\n\n// ============================================================================\n// High-level API (uses provider registry)\n// ============================================================================\n\n/**\n * Get API key for a provider from OAuth credentials.\n * Automatically refreshes expired tokens.\n *\n * @returns API key string and updated credentials, or null if no credentials\n * @throws Error if refresh fails\n */\nexport async function getOAuthApiKey(\n\tproviderId: OAuthProviderId,\n\tcredentials: Record<string, OAuthCredentials>,\n): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null> {\n\tconst provider = getOAuthProvider(providerId);\n\tif (!provider) {\n\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t}\n\n\tlet creds = credentials[providerId];\n\tif (!creds) {\n\t\treturn null;\n\t}\n\n\t// Refresh if expired\n\tif (Date.now() >= creds.expires) {\n\t\ttry {\n\t\t\tcreds = await provider.refreshToken(creds);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Failed to refresh OAuth token for ${providerId}`, { cause: error });\n\t\t}\n\t}\n\n\tconst apiKey = provider.getApiKey(creds);\n\treturn { newCredentials: creds, apiKey };\n}\n"]}
|
|
@@ -34,6 +34,9 @@ async function getAnthropicClass(): Promise<typeof Anthropic> {
|
|
|
34
34
|
return _AnthropicClass;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// Stealth mode: Mimic Claude Code's tool naming exactly
|
|
38
|
+
const claudeCodeVersion = "2.1.62";
|
|
39
|
+
|
|
37
40
|
function mergeHeaders(...headerSources: (Record<string, string> | undefined)[]): Record<string, string> {
|
|
38
41
|
const merged: Record<string, string> = {};
|
|
39
42
|
for (const headers of headerSources) {
|
|
@@ -44,6 +47,10 @@ function mergeHeaders(...headerSources: (Record<string, string> | undefined)[]):
|
|
|
44
47
|
return merged;
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
function isOAuthToken(apiKey: string): boolean {
|
|
51
|
+
return apiKey.includes("sk-ant-oat");
|
|
52
|
+
}
|
|
53
|
+
|
|
47
54
|
async function createClient(
|
|
48
55
|
model: Model<"anthropic-messages">,
|
|
49
56
|
apiKey: string,
|
|
@@ -90,7 +97,30 @@ async function createClient(
|
|
|
90
97
|
betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
91
98
|
}
|
|
92
99
|
|
|
93
|
-
//
|
|
100
|
+
// OAuth: Bearer auth, Claude Code identity headers
|
|
101
|
+
if (isOAuthToken(apiKey)) {
|
|
102
|
+
const client = new AnthropicClass({
|
|
103
|
+
apiKey: null,
|
|
104
|
+
authToken: apiKey,
|
|
105
|
+
baseURL: model.baseUrl,
|
|
106
|
+
dangerouslyAllowBrowser: true,
|
|
107
|
+
defaultHeaders: mergeHeaders(
|
|
108
|
+
{
|
|
109
|
+
accept: "application/json",
|
|
110
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
111
|
+
...(betaFeatures.length > 0 ? { "anthropic-beta": `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}` } : {}),
|
|
112
|
+
"user-agent": `claude-cli/${claudeCodeVersion}`,
|
|
113
|
+
"x-app": "cli",
|
|
114
|
+
},
|
|
115
|
+
model.headers,
|
|
116
|
+
optionsHeaders,
|
|
117
|
+
),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return { client, isOAuthToken: true };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// API key auth
|
|
94
124
|
// Alibaba Coding Plan uses Bearer token auth instead of x-api-key
|
|
95
125
|
const isAlibabaProvider = model.provider === "alibaba-coding-plan";
|
|
96
126
|
const client = new AnthropicClass({
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic OAuth flow (Claude Pro/Max)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { generatePKCE } from "./pkce.js";
|
|
6
|
+
import type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from "./types.js";
|
|
7
|
+
|
|
8
|
+
const decode = (s: string) => atob(s);
|
|
9
|
+
const CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
|
|
10
|
+
const AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
|
|
11
|
+
const TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
|
|
12
|
+
const REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
|
|
13
|
+
const SCOPES = "org:create_api_key user:profile user:inference";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Login with Anthropic OAuth (device code flow)
|
|
17
|
+
*
|
|
18
|
+
* @param onAuthUrl - Callback to handle the authorization URL (e.g., open browser)
|
|
19
|
+
* @param onPromptCode - Callback to prompt user for the authorization code
|
|
20
|
+
*/
|
|
21
|
+
export async function loginAnthropic(
|
|
22
|
+
onAuthUrl: (url: string) => void,
|
|
23
|
+
onPromptCode: () => Promise<string>,
|
|
24
|
+
): Promise<OAuthCredentials> {
|
|
25
|
+
const { verifier, challenge } = await generatePKCE();
|
|
26
|
+
|
|
27
|
+
// Build authorization URL
|
|
28
|
+
const authParams = new URLSearchParams({
|
|
29
|
+
code: "true",
|
|
30
|
+
client_id: CLIENT_ID,
|
|
31
|
+
response_type: "code",
|
|
32
|
+
redirect_uri: REDIRECT_URI,
|
|
33
|
+
scope: SCOPES,
|
|
34
|
+
code_challenge: challenge,
|
|
35
|
+
code_challenge_method: "S256",
|
|
36
|
+
state: verifier,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;
|
|
40
|
+
|
|
41
|
+
// Notify caller with URL to open
|
|
42
|
+
onAuthUrl(authUrl);
|
|
43
|
+
|
|
44
|
+
// Wait for user to paste authorization code (format: code#state)
|
|
45
|
+
const authCode = await onPromptCode();
|
|
46
|
+
const splits = authCode.split("#");
|
|
47
|
+
const code = splits[0];
|
|
48
|
+
const state = splits[1];
|
|
49
|
+
|
|
50
|
+
// Exchange code for tokens
|
|
51
|
+
const tokenResponse = await fetch(TOKEN_URL, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
grant_type: "authorization_code",
|
|
58
|
+
client_id: CLIENT_ID,
|
|
59
|
+
code: code,
|
|
60
|
+
state: state,
|
|
61
|
+
redirect_uri: REDIRECT_URI,
|
|
62
|
+
code_verifier: verifier,
|
|
63
|
+
}),
|
|
64
|
+
signal: AbortSignal.timeout(30_000),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!tokenResponse.ok) {
|
|
68
|
+
const error = await tokenResponse.text();
|
|
69
|
+
throw new Error(`Token exchange failed: ${error}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const tokenData = (await tokenResponse.json()) as {
|
|
73
|
+
access_token: string;
|
|
74
|
+
refresh_token: string;
|
|
75
|
+
expires_in: number;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Calculate expiry time (current time + expires_in seconds - 5 min buffer)
|
|
79
|
+
const expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000;
|
|
80
|
+
|
|
81
|
+
// Save credentials
|
|
82
|
+
return {
|
|
83
|
+
refresh: tokenData.refresh_token,
|
|
84
|
+
access: tokenData.access_token,
|
|
85
|
+
expires: expiresAt,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Refresh Anthropic OAuth token
|
|
91
|
+
*/
|
|
92
|
+
export async function refreshAnthropicToken(refreshToken: string): Promise<OAuthCredentials> {
|
|
93
|
+
const response = await fetch(TOKEN_URL, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: { "Content-Type": "application/json" },
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
grant_type: "refresh_token",
|
|
98
|
+
client_id: CLIENT_ID,
|
|
99
|
+
refresh_token: refreshToken,
|
|
100
|
+
}),
|
|
101
|
+
signal: AbortSignal.timeout(30_000),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (!response.ok) {
|
|
105
|
+
const error = await response.text();
|
|
106
|
+
throw new Error(`Anthropic token refresh failed: ${error}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const data = (await response.json()) as {
|
|
110
|
+
access_token: string;
|
|
111
|
+
refresh_token: string;
|
|
112
|
+
expires_in: number;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
refresh: data.refresh_token,
|
|
117
|
+
access: data.access_token,
|
|
118
|
+
expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const anthropicOAuthProvider: OAuthProviderInterface = {
|
|
123
|
+
id: "anthropic",
|
|
124
|
+
name: "Anthropic (Claude Pro/Max)",
|
|
125
|
+
|
|
126
|
+
async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
127
|
+
return loginAnthropic(
|
|
128
|
+
(url) => callbacks.onAuth({ url }),
|
|
129
|
+
() => callbacks.onPrompt({ message: "Paste the authorization code:" }),
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
async refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {
|
|
134
|
+
return refreshAnthropicToken(credentials.refresh);
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
getApiKey(credentials: OAuthCredentials): string {
|
|
138
|
+
return credentials.access;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This module handles login, token refresh, and credential storage
|
|
5
5
|
* for OAuth-based providers:
|
|
6
|
+
* - Anthropic (Claude Pro/Max)
|
|
6
7
|
* - GitHub Copilot
|
|
7
8
|
* - Google Cloud Code Assist (Gemini CLI)
|
|
8
9
|
* - Antigravity (Gemini 3, Claude, GPT-OSS via Google Cloud)
|
|
9
|
-
*
|
|
10
|
-
* Note: Anthropic OAuth was removed per TOS compliance (see docs/user-docs/claude-code-auth-compliance.md).
|
|
11
|
-
* Use API keys or the local Claude Code CLI for Anthropic access.
|
|
12
10
|
*/
|
|
13
11
|
|
|
12
|
+
// Anthropic
|
|
13
|
+
export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
|
|
14
14
|
// GitHub Copilot
|
|
15
15
|
export {
|
|
16
16
|
getGitHubCopilotBaseUrl,
|
|
@@ -32,6 +32,7 @@ export * from "./types.js";
|
|
|
32
32
|
// Provider Registry
|
|
33
33
|
// ============================================================================
|
|
34
34
|
|
|
35
|
+
import { anthropicOAuthProvider } from "./anthropic.js";
|
|
35
36
|
import { githubCopilotOAuthProvider } from "./github-copilot.js";
|
|
36
37
|
import { antigravityOAuthProvider } from "./google-antigravity.js";
|
|
37
38
|
import { geminiCliOAuthProvider } from "./google-gemini-cli.js";
|
|
@@ -39,6 +40,7 @@ import { openaiCodexOAuthProvider } from "./openai-codex.js";
|
|
|
39
40
|
import type { OAuthCredentials, OAuthProviderId, OAuthProviderInterface } from "./types.js";
|
|
40
41
|
|
|
41
42
|
const BUILT_IN_OAUTH_PROVIDERS: OAuthProviderInterface[] = [
|
|
43
|
+
anthropicOAuthProvider,
|
|
42
44
|
githubCopilotOAuthProvider,
|
|
43
45
|
geminiCliOAuthProvider,
|
|
44
46
|
antigravityOAuthProvider,
|
|
@@ -19,7 +19,6 @@ import { deriveState } from "../state.js";
|
|
|
19
19
|
import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
|
|
20
20
|
import { toPosixPath } from "../../shared/mod.js";
|
|
21
21
|
import { markCmuxPromptShown, shouldPromptToEnableCmux } from "../../cmux/index.js";
|
|
22
|
-
import { autoEnableCmuxPreferences } from "../commands-cmux.js";
|
|
23
22
|
|
|
24
23
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
25
24
|
|
|
@@ -77,16 +76,13 @@ export async function buildBeforeAgentStartResult(
|
|
|
77
76
|
shortcutDashboard: formatShortcut("Ctrl+Alt+G"),
|
|
78
77
|
shortcutShell: formatShortcut("Ctrl+Alt+B"),
|
|
79
78
|
});
|
|
80
|
-
|
|
79
|
+
const loadedPreferences = loadEffectiveGSDPreferences();
|
|
81
80
|
if (shouldPromptToEnableCmux(loadedPreferences?.preferences)) {
|
|
82
81
|
markCmuxPromptShown();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"info",
|
|
88
|
-
);
|
|
89
|
-
}
|
|
82
|
+
ctx.ui.notify(
|
|
83
|
+
"cmux detected. Run /gsd cmux on to enable sidebar metadata, notifications, and visual subagent splits for this project.",
|
|
84
|
+
"info",
|
|
85
|
+
);
|
|
90
86
|
}
|
|
91
87
|
|
|
92
88
|
let preferenceBlock = "";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
2
|
-
import { existsSync, readFileSync
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { clearCmuxSidebar, CmuxClient, detectCmuxEnvironment, resolveCmuxConfig } from "../cmux/index.js";
|
|
4
4
|
import { saveFile } from "./files.js";
|
|
5
5
|
import {
|
|
@@ -9,37 +9,6 @@ import {
|
|
|
9
9
|
} from "./preferences.js";
|
|
10
10
|
import { ensurePreferencesFile, serializePreferencesToFrontmatter } from "./commands-prefs-wizard.js";
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Auto-enable cmux in project preferences when detected but never configured.
|
|
14
|
-
* Called at boot (before agent start) — no ExtensionCommandContext needed.
|
|
15
|
-
* Returns true if preferences were written, false if skipped.
|
|
16
|
-
*/
|
|
17
|
-
export function autoEnableCmuxPreferences(): boolean {
|
|
18
|
-
const path = getProjectGSDPreferencesPath();
|
|
19
|
-
if (!existsSync(path)) return false;
|
|
20
|
-
|
|
21
|
-
const existing = loadProjectGSDPreferences();
|
|
22
|
-
const prefs: Record<string, unknown> = existing?.preferences ? { ...existing.preferences } : { version: 1 };
|
|
23
|
-
prefs.cmux = {
|
|
24
|
-
enabled: true,
|
|
25
|
-
notifications: true,
|
|
26
|
-
sidebar: true,
|
|
27
|
-
splits: false,
|
|
28
|
-
browser: false,
|
|
29
|
-
...((prefs.cmux as Record<string, unknown> | undefined) ?? {}),
|
|
30
|
-
};
|
|
31
|
-
(prefs.cmux as Record<string, unknown>).enabled = true;
|
|
32
|
-
prefs.version = prefs.version || 1;
|
|
33
|
-
|
|
34
|
-
const frontmatter = serializePreferencesToFrontmatter(prefs);
|
|
35
|
-
let body = "\n# GSD Skill Preferences\n\nSee `~/.gsd/agent/extensions/gsd/docs/preferences-reference.md` for full field documentation and examples.\n";
|
|
36
|
-
const preserved = extractBodyAfterFrontmatter(readFileSync(path, "utf-8"));
|
|
37
|
-
if (preserved) body = preserved;
|
|
38
|
-
|
|
39
|
-
writeFileSync(path, `---\n${frontmatter}---${body}`, "utf-8");
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
12
|
function extractBodyAfterFrontmatter(content: string): string | null {
|
|
44
13
|
const start = content.startsWith("---\n") ? 4 : content.startsWith("---\r\n") ? 5 : -1;
|
|
45
14
|
if (start === -1) return null;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import test, { describe
|
|
1
|
+
import test, { describe } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
7
6
|
import {
|
|
8
7
|
buildCmuxProgress,
|
|
@@ -13,7 +12,6 @@ import {
|
|
|
13
12
|
resolveCmuxConfig,
|
|
14
13
|
shouldPromptToEnableCmux,
|
|
15
14
|
} from "../../cmux/index.ts";
|
|
16
|
-
import { autoEnableCmuxPreferences } from "../commands-cmux.ts";
|
|
17
15
|
import type { GSDState } from "../types.ts";
|
|
18
16
|
|
|
19
17
|
test("detectCmuxEnvironment requires workspace, surface, and socket", () => {
|
|
@@ -81,70 +79,6 @@ test("shouldPromptToEnableCmux only prompts once per session", () => {
|
|
|
81
79
|
resetCmuxPromptState();
|
|
82
80
|
});
|
|
83
81
|
|
|
84
|
-
describe("autoEnableCmuxPreferences", () => {
|
|
85
|
-
let tmp: string;
|
|
86
|
-
let originalCwd: string;
|
|
87
|
-
|
|
88
|
-
beforeEach(() => {
|
|
89
|
-
originalCwd = process.cwd();
|
|
90
|
-
tmp = fs.mkdtempSync(path.join(tmpdir(), "cmux-auto-test-"));
|
|
91
|
-
fs.mkdirSync(path.join(tmp, ".gsd"), { recursive: true });
|
|
92
|
-
process.chdir(tmp);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
afterEach(() => {
|
|
96
|
-
process.chdir(originalCwd);
|
|
97
|
-
fs.rmSync(tmp, { recursive: true, force: true });
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("writes cmux.enabled true when preferences file exists with no cmux config", () => {
|
|
101
|
-
const prefsPath = path.join(tmp, ".gsd", "preferences.md");
|
|
102
|
-
fs.writeFileSync(prefsPath, [
|
|
103
|
-
"---",
|
|
104
|
-
"version: 1",
|
|
105
|
-
"---",
|
|
106
|
-
"",
|
|
107
|
-
"# GSD Skill Preferences",
|
|
108
|
-
].join("\n"));
|
|
109
|
-
|
|
110
|
-
const result = autoEnableCmuxPreferences();
|
|
111
|
-
assert.equal(result, true);
|
|
112
|
-
|
|
113
|
-
const content = fs.readFileSync(prefsPath, "utf-8");
|
|
114
|
-
assert.ok(content.includes("enabled: true"), "should write enabled: true");
|
|
115
|
-
assert.ok(content.includes("notifications: true"), "should default notifications on");
|
|
116
|
-
assert.ok(content.includes("sidebar: true"), "should default sidebar on");
|
|
117
|
-
assert.ok(content.includes("splits: false"), "should default splits off");
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test("returns false when preferences file does not exist", () => {
|
|
121
|
-
const result = autoEnableCmuxPreferences();
|
|
122
|
-
assert.equal(result, false);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("preserves existing cmux sub-preferences when auto-enabling", () => {
|
|
126
|
-
const prefsPath = path.join(tmp, ".gsd", "preferences.md");
|
|
127
|
-
fs.writeFileSync(prefsPath, [
|
|
128
|
-
"---",
|
|
129
|
-
"version: 1",
|
|
130
|
-
"cmux:",
|
|
131
|
-
" splits: true",
|
|
132
|
-
" browser: true",
|
|
133
|
-
"---",
|
|
134
|
-
"",
|
|
135
|
-
"# GSD Skill Preferences",
|
|
136
|
-
].join("\n"));
|
|
137
|
-
|
|
138
|
-
const result = autoEnableCmuxPreferences();
|
|
139
|
-
assert.equal(result, true);
|
|
140
|
-
|
|
141
|
-
const content = fs.readFileSync(prefsPath, "utf-8");
|
|
142
|
-
assert.ok(content.includes("enabled: true"), "should set enabled: true");
|
|
143
|
-
assert.ok(content.includes("splits: true"), "should preserve existing splits: true");
|
|
144
|
-
assert.ok(content.includes("browser: true"), "should preserve existing browser: true");
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
82
|
test("buildCmuxStatusLabel and progress prefer deepest active unit", () => {
|
|
149
83
|
const state: GSDState = {
|
|
150
84
|
activeMilestone: { id: "M001", title: "Milestone" },
|
|
@@ -26,12 +26,8 @@ test("ensureProjectWorkflowMcpConfig creates .mcp.json with the workflow server"
|
|
|
26
26
|
assert.equal(typeof server?.command, "string");
|
|
27
27
|
assert.equal(Array.isArray(server?.args), true);
|
|
28
28
|
assert.equal(server?.env?.GSD_WORKFLOW_PROJECT_ROOT, projectRoot);
|
|
29
|
-
assert.match(server?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.
|
|
30
|
-
assert.match(server?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.
|
|
31
|
-
if ((server?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "").endsWith(".ts")) {
|
|
32
|
-
assert.match(server?.env?.NODE_OPTIONS ?? "", /--experimental-strip-types/);
|
|
33
|
-
assert.match(server?.env?.NODE_OPTIONS ?? "", /resolve-ts\.mjs/);
|
|
34
|
-
}
|
|
29
|
+
assert.match(server?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.js$/);
|
|
30
|
+
assert.match(server?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.js$/);
|
|
35
31
|
} finally {
|
|
36
32
|
rmSync(projectRoot, { recursive: true, force: true });
|
|
37
33
|
}
|
|
@@ -141,11 +141,7 @@ test("detectWorkflowMcpLaunchConfig resolves the bundled server relative to the
|
|
|
141
141
|
assert.match(launch?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
|
|
142
142
|
assert.match(launch?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
|
|
143
143
|
assert.equal(typeof launch?.args?.[0], "string");
|
|
144
|
-
assert.match(launch?.args?.[0] ?? "", /packages[\/\\]mcp-server[\/\\]
|
|
145
|
-
if ((launch?.args?.[0] ?? "").endsWith(".ts")) {
|
|
146
|
-
assert.match(launch?.env?.NODE_OPTIONS ?? "", /--experimental-strip-types/);
|
|
147
|
-
assert.match(launch?.env?.NODE_OPTIONS ?? "", /resolve-ts\.mjs/);
|
|
148
|
-
}
|
|
144
|
+
assert.match(launch?.args?.[0] ?? "", /packages[\/\\]mcp-server[\/\\]dist[\/\\]cli\.js$/);
|
|
149
145
|
});
|
|
150
146
|
|
|
151
147
|
test("detectWorkflowMcpLaunchConfig resolves the bundled server relative to the package without env hints", () => {
|
|
@@ -158,11 +154,7 @@ test("detectWorkflowMcpLaunchConfig resolves the bundled server relative to the
|
|
|
158
154
|
assert.match(launch?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
|
|
159
155
|
assert.match(launch?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
|
|
160
156
|
assert.equal(typeof launch?.args?.[0], "string");
|
|
161
|
-
assert.match(launch?.args?.[0] ?? "", /packages[\/\\]mcp-server[\/\\]
|
|
162
|
-
if ((launch?.args?.[0] ?? "").endsWith(".ts")) {
|
|
163
|
-
assert.match(launch?.env?.NODE_OPTIONS ?? "", /--experimental-strip-types/);
|
|
164
|
-
assert.match(launch?.env?.NODE_OPTIONS ?? "", /resolve-ts\.mjs/);
|
|
165
|
-
}
|
|
157
|
+
assert.match(launch?.args?.[0] ?? "", /packages[\/\\]mcp-server[\/\\]dist[\/\\]cli\.js$/);
|
|
166
158
|
});
|
|
167
159
|
|
|
168
160
|
test("workflow MCP launch config reaches mutation tools over stdio", async () => {
|
|
@@ -173,16 +165,12 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
|
|
|
173
165
|
assert.ok(launch, "expected a workflow MCP launch config");
|
|
174
166
|
assert.match(
|
|
175
167
|
launch.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "",
|
|
176
|
-
/
|
|
168
|
+
/dist[\/\\]resources[\/\\]extensions[\/\\]gsd[\/\\]tools[\/\\]workflow-tool-executors\.js$/,
|
|
177
169
|
);
|
|
178
170
|
assert.match(
|
|
179
171
|
launch.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "",
|
|
180
|
-
/
|
|
172
|
+
/dist[\/\\]resources[\/\\]extensions[\/\\]gsd[\/\\]bootstrap[\/\\]write-gate\.js$/,
|
|
181
173
|
);
|
|
182
|
-
if ((launch.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "").endsWith(".ts")) {
|
|
183
|
-
assert.match(launch.env?.NODE_OPTIONS ?? "", /--experimental-strip-types/);
|
|
184
|
-
assert.match(launch.env?.NODE_OPTIONS ?? "", /resolve-ts\.mjs/);
|
|
185
|
-
}
|
|
186
174
|
|
|
187
175
|
const client = new Client({ name: "workflow-mcp-transport-test", version: "1.0.0" });
|
|
188
176
|
const transport = new StdioClientTransport({
|
|
@@ -201,10 +189,6 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
|
|
|
201
189
|
(tools.tools ?? []).some((tool) => tool.name === "gsd_plan_slice"),
|
|
202
190
|
"expected workflow MCP surface to expose gsd_plan_slice",
|
|
203
191
|
);
|
|
204
|
-
assert.ok(
|
|
205
|
-
(tools.tools ?? []).some((tool) => tool.name === "ask_user_questions"),
|
|
206
|
-
"expected workflow MCP surface to expose ask_user_questions",
|
|
207
|
-
);
|
|
208
192
|
|
|
209
193
|
const milestoneResult = await client.callTool(
|
|
210
194
|
{
|
|
@@ -481,18 +465,18 @@ test("transport compatibility now allows replan-slice over workflow MCP surface"
|
|
|
481
465
|
test("transport compatibility still blocks units whose MCP tools are not exposed", () => {
|
|
482
466
|
const error = getWorkflowTransportSupportError(
|
|
483
467
|
"claude-code",
|
|
484
|
-
["
|
|
468
|
+
["gsd_skip_slice"],
|
|
485
469
|
{
|
|
486
470
|
projectRoot: "/tmp/project",
|
|
487
471
|
env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
|
|
488
472
|
surface: "auto-mode",
|
|
489
|
-
unitType: "
|
|
473
|
+
unitType: "skip-slice",
|
|
490
474
|
authMode: "externalCli",
|
|
491
475
|
baseUrl: "local://claude-code",
|
|
492
476
|
},
|
|
493
477
|
);
|
|
494
478
|
|
|
495
|
-
assert.match(error ?? "", /requires
|
|
479
|
+
assert.match(error ?? "", /requires gsd_skip_slice/);
|
|
496
480
|
assert.match(error ?? "", /currently exposes only/);
|
|
497
481
|
});
|
|
498
482
|
|