@workbench-ai/workbench 0.0.96 → 0.0.98

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.
Files changed (2) hide show
  1. package/dist/index.js +42 -22
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -1850,6 +1850,9 @@ async function requestDeviceAuthorization(baseUrl) {
1850
1850
  });
1851
1851
  }
1852
1852
  if (!response.ok) {
1853
+ if (isRetryableHttpStatus(response.status)) {
1854
+ throw deviceLoginUnavailableError("start", response.status, response.statusText, text);
1855
+ }
1853
1856
  const excerpt = readResponseError(text);
1854
1857
  throw new WorkbenchCodedError("login_denied", `Device login failed: ${response.status}${excerpt ? ` ${excerpt}` : response.statusText ? ` ${response.statusText}` : ""}`, {
1855
1858
  exitCode: 1,
@@ -1889,6 +1892,9 @@ async function pollDeviceToken(baseUrl, authorization, timeoutSeconds) {
1889
1892
  if (response.ok) {
1890
1893
  return JSON.parse(text);
1891
1894
  }
1895
+ if (isRetryableHttpStatus(response.status)) {
1896
+ throw deviceLoginUnavailableError("wait", response.status, response.statusText, text);
1897
+ }
1892
1898
  const error = readResponseError(text) ?? "authorization_pending";
1893
1899
  if (error === "slow_down") {
1894
1900
  intervalMs += 5000;
@@ -1913,6 +1919,18 @@ async function pollDeviceToken(baseUrl, authorization, timeoutSeconds) {
1913
1919
  exitCode: 1,
1914
1920
  });
1915
1921
  }
1922
+ function deviceLoginUnavailableError(phase, status, statusText, text) {
1923
+ const excerpt = readResponseError(text);
1924
+ const detail = `${status}${excerpt ? ` ${excerpt}` : statusText ? ` ${statusText}` : ""}`;
1925
+ const command = phase === "start"
1926
+ ? "workbench login --start-only --no-open"
1927
+ : `workbench login --wait --timeout ${LOGIN_WAIT_TIMEOUT_SECONDS}`;
1928
+ return new WorkbenchCodedError("service_unavailable", `Workbench Cloud login is temporarily unavailable: ${detail}`, {
1929
+ retryable: true,
1930
+ remediation: `Retry ${command}.`,
1931
+ exitCode: 1,
1932
+ });
1933
+ }
1916
1934
  async function fetchWorkbenchUsername(baseUrl, accessToken) {
1917
1935
  const response = await fetch(`${baseUrl}/api/workbench/profile`, {
1918
1936
  headers: { authorization: `Bearer ${accessToken}` },
@@ -2150,7 +2168,10 @@ function isTransientFetchError(error) {
2150
2168
  return /(?:fetch failed|socket hang up|ECONNRESET|EPIPE|UND_ERR_SOCKET|terminated)/iu.test(errorMessage(error));
2151
2169
  }
2152
2170
  function isTransientApiRequestError(error) {
2153
- return error instanceof WorkbenchApiRequestError && (error.status === 429 || error.status >= 500);
2171
+ return error instanceof WorkbenchApiRequestError && isRetryableHttpStatus(error.status);
2172
+ }
2173
+ function isRetryableHttpStatus(status) {
2174
+ return status === 429 || status >= 500;
2154
2175
  }
2155
2176
  function isIdempotentApiRequestMethod(method) {
2156
2177
  return method === "GET" || method === "PUT" || method === "DELETE";
@@ -2246,21 +2267,10 @@ async function collectAdapterAuthBundle(args) {
2246
2267
  });
2247
2268
  }
2248
2269
  if (args.method === "oauth") {
2249
- const profile = await requiredAuthFile(args.profileRoot, ".claude.json", {
2250
- provider: "Claude auth",
2251
- remediation: "claude setup-token && workbench login claude --method oauth",
2252
- });
2253
- const portableToken = await requiredAnyAuthFile(args.profileRoot, [
2254
- ".claude/oauth-token",
2255
- ".claude/.credentials.json",
2256
- ], {
2257
- provider: "Claude portable OAuth token",
2258
- remediation: "claude setup-token && workbench login claude --method oauth",
2259
- });
2260
2270
  return createWorkbenchAdapterAuthBundle({
2261
2271
  target: args.target,
2262
2272
  method: args.method,
2263
- files: [profile, portableToken],
2273
+ files: await collectClaudeOAuthFiles(args.profileRoot),
2264
2274
  });
2265
2275
  }
2266
2276
  if (args.method === "bedrock") {
@@ -2309,15 +2319,25 @@ async function requiredAuthFile(root, relativePath, guidance) {
2309
2319
  }
2310
2320
  return file;
2311
2321
  }
2312
- async function requiredAnyAuthFile(root, paths, guidance) {
2313
- const files = await optionalAuthFiles(root, paths);
2314
- const [file] = files;
2315
- if (file) {
2316
- return file;
2317
- }
2318
- throw new WorkbenchCodedError("provider_oauth_missing", `Missing ${guidance.provider} file: ${paths.map((entry) => path.join(root, entry)).join(" or ")}`, {
2319
- remediation: guidance.remediation,
2320
- subject: { paths: paths.map((entry) => path.join(root, entry)), relativePaths: [...paths] },
2322
+ const CLAUDE_OAUTH_PROFILE_PATH = ".claude.json";
2323
+ const CLAUDE_OAUTH_TOKEN_PATHS = [".claude/oauth-token", ".claude/.credentials.json"];
2324
+ const CLAUDE_OAUTH_REMEDIATION = "claude setup-token && workbench login claude --method oauth";
2325
+ async function collectClaudeOAuthFiles(root) {
2326
+ const [profile] = await optionalAuthFiles(root, [CLAUDE_OAUTH_PROFILE_PATH]);
2327
+ const [portableToken] = await optionalAuthFiles(root, CLAUDE_OAUTH_TOKEN_PATHS);
2328
+ if (profile && portableToken) {
2329
+ return [profile, portableToken];
2330
+ }
2331
+ const missingRelativePaths = [
2332
+ ...(!profile ? [CLAUDE_OAUTH_PROFILE_PATH] : []),
2333
+ ...(!portableToken ? [...CLAUDE_OAUTH_TOKEN_PATHS] : []),
2334
+ ];
2335
+ throw new WorkbenchCodedError("provider_oauth_missing", "Claude OAuth capture requires Claude Code's profile and portable OAuth token.", {
2336
+ remediation: CLAUDE_OAUTH_REMEDIATION,
2337
+ subject: {
2338
+ paths: missingRelativePaths.map((entry) => path.join(root, entry)),
2339
+ relativePaths: missingRelativePaths,
2340
+ },
2321
2341
  exitCode: 2,
2322
2342
  });
2323
2343
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workbench-ai/workbench",
3
- "version": "0.0.96",
3
+ "version": "0.0.98",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/workbench-ai/workbench.git",
@@ -21,10 +21,10 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "yaml": "^2.8.2",
24
- "@workbench-ai/workbench-built-in-adapters": "0.0.96",
25
- "@workbench-ai/workbench-protocol": "0.0.96",
26
- "@workbench-ai/workbench-core": "0.0.96",
27
- "@workbench-ai/workbench-contract": "0.0.96"
24
+ "@workbench-ai/workbench-built-in-adapters": "0.0.98",
25
+ "@workbench-ai/workbench-protocol": "0.0.98",
26
+ "@workbench-ai/workbench-core": "0.0.98",
27
+ "@workbench-ai/workbench-contract": "0.0.98"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@tailwindcss/postcss": "^4.2.2",
@@ -35,7 +35,7 @@
35
35
  "react-dom": "^19.2.0",
36
36
  "typescript": "^5.9.2",
37
37
  "vitest": "^3.2.4",
38
- "@workbench-ai/workbench-ui": "0.0.96"
38
+ "@workbench-ai/workbench-ui": "0.0.98"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "rm -rf dist && tsc -p tsconfig.json && chmod 755 dist/workbench.js && node ./scripts/build-dev-open-assets.mjs",