@bike4mind/cli 2.1.0 → 3.0.0

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 (30) hide show
  1. package/README.md +1 -1
  2. package/dist/{AgentHistoryStore-DdYKfx7C.mjs → AgentHistoryStore-DVxv1kSO.mjs} +7905 -7029
  3. package/dist/{ConfigStore-BygzEI6H.mjs → ConfigStore-51UaP9pJ.mjs} +10645 -6760
  4. package/dist/{ImageStore-kVo-oHoS.mjs → ImageStore-DuI3tgwQ.mjs} +8 -6
  5. package/dist/SandboxRuntimeAdapter-Bt1wPpld.mjs +2 -0
  6. package/dist/{WorkItemsClient-ClBC49y0.mjs → WorkItemsClient-BMY0Qq1s.mjs} +27 -2
  7. package/dist/{buildAgent-B7cMwRAy.mjs → buildAgent-CrSRKYU_.mjs} +75 -43
  8. package/dist/commands/acpCommand.mjs +26 -35
  9. package/dist/commands/apiCommand.mjs +1 -1
  10. package/dist/commands/doctorCommand.mjs +3 -3
  11. package/dist/commands/envCommand.mjs +1 -1
  12. package/dist/commands/headlessCommand.mjs +15 -24
  13. package/dist/commands/mcpCommand.mjs +3 -3
  14. package/dist/commands/pluginCommand.mjs +2 -2
  15. package/dist/commands/updateCommand.mjs +5 -15
  16. package/dist/dist-BsG2Hlp-.mjs +739 -0
  17. package/dist/index.mjs +602 -324
  18. package/dist/{package-CRNnNXxq.mjs → package-DEsNSP54.mjs} +1 -1
  19. package/dist/{serve-DM7ld3-o.mjs → serve-BaHzVbcm.mjs} +300 -154
  20. package/dist/store-BkMur6uk.mjs +2 -0
  21. package/dist/{store-CvjTpQPs.mjs → store-BxmPEI3v.mjs} +10 -0
  22. package/dist/types-I45lMh7Q.mjs +2 -0
  23. package/dist/{updateChecker-CQW8bxo6.mjs → updateChecker-CCLVVdH0.mjs} +36 -13
  24. package/package.json +24 -23
  25. package/dist/ApiClient-dIB3YQc2.mjs +0 -316
  26. package/dist/SandboxRuntimeAdapter-BmHELLuM.mjs +0 -3
  27. package/dist/store-BG3e54c8.mjs +0 -3
  28. package/dist/types-CdIKgWWe.mjs +0 -3
  29. package/dist/{bashExecute-CrdPpBqk-DCATrE-D.mjs → bashExecute-CrdPpBqk-CgK1gZa2.mjs} +1 -1
  30. package/dist/{grepSearch-BgoOOwGe-DtlV8Gn-.mjs → grepSearch-BgoOOwGe-B388GxRD.mjs} +2 -2
@@ -75,6 +75,16 @@ const useCliStore = create((set) => ({
75
75
  }
76
76
  };
77
77
  }),
78
+ sessionHolds: 0,
79
+ holdSession: () => {
80
+ set((state) => ({ sessionHolds: state.sessionHolds + 1 }));
81
+ let released = false;
82
+ return () => {
83
+ if (released) return;
84
+ released = true;
85
+ set((state) => ({ sessionHolds: state.sessionHolds - 1 }));
86
+ };
87
+ },
78
88
  messageQueue: [],
79
89
  enqueueMessage: (message) => set((state) => ({ messageQueue: [...state.messageQueue, message] })),
80
90
  dequeueAllMessages: () => {
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { t as DEFAULT_SANDBOX_CONFIG } from "./types-F61_hxmG.mjs";
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from "child_process";
3
2
  import { constants, promises } from "fs";
4
3
  import { homedir } from "os";
5
4
  import path from "path";
6
5
  import axios from "axios";
6
+ import { execSync } from "child_process";
7
7
  //#region src/utils/updateChecker.ts
8
8
  /**
9
9
  * Update checker utility for B4M CLI
@@ -163,9 +163,40 @@ async function readCache() {
163
163
  async function writeCache(cache) {
164
164
  try {
165
165
  await promises.mkdir(path.dirname(CACHE_FILE), { recursive: true });
166
- await promises.writeFile(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
166
+ const tempFile = `${CACHE_FILE}.${process.pid}.tmp`;
167
+ await promises.writeFile(tempFile, JSON.stringify(cache, null, 2), "utf-8");
168
+ await promises.rename(tempFile, CACHE_FILE);
167
169
  } catch {}
168
170
  }
171
+ function toResult(currentVersion, latestVersion) {
172
+ return {
173
+ currentVersion,
174
+ latestVersion,
175
+ updateAvailable: compareSemver(latestVersion, currentVersion) > 0
176
+ };
177
+ }
178
+ /**
179
+ * The last known update status, from the cache alone. Never touches the network,
180
+ * so it is safe to await before first paint. A cache of any age is used: a newer
181
+ * published version stays newer, and checkForUpdate() refreshes the cache for the
182
+ * next launch. Null when nothing is cached for this version.
183
+ */
184
+ async function readCachedUpdate(currentVersion) {
185
+ const cache = await readCache();
186
+ if (!cache || cache.currentVersion !== currentVersion) return null;
187
+ return toResult(currentVersion, cache.latestVersion);
188
+ }
189
+ /**
190
+ * Startup update status: the cached result now, plus a background refresh of
191
+ * the cache (registry fetch only when it is stale) for the next launch. This
192
+ * refresh is the only automatic writer of the cache, which both the banner and
193
+ * the launch auto-update prompt read.
194
+ */
195
+ async function getStartupUpdateStatus(currentVersion) {
196
+ const cached = await readCachedUpdate(currentVersion);
197
+ checkForUpdate(currentVersion);
198
+ return cached;
199
+ }
169
200
  /**
170
201
  * Check for updates using cache when fresh.
171
202
  * Non-throwing - returns null on any error.
@@ -174,11 +205,7 @@ async function checkForUpdate(currentVersion) {
174
205
  try {
175
206
  const cache = await readCache();
176
207
  if (cache && cache.currentVersion === currentVersion) {
177
- if (Date.now() - new Date(cache.lastChecked).getTime() < CACHE_TTL_MS) return {
178
- currentVersion,
179
- latestVersion: cache.latestVersion,
180
- updateAvailable: compareSemver(cache.latestVersion, currentVersion) > 0
181
- };
208
+ if (Date.now() - new Date(cache.lastChecked).getTime() < CACHE_TTL_MS) return toResult(currentVersion, cache.latestVersion);
182
209
  }
183
210
  const latestVersion = await fetchLatestVersion();
184
211
  if (!latestVersion) return null;
@@ -187,11 +214,7 @@ async function checkForUpdate(currentVersion) {
187
214
  latestVersion,
188
215
  currentVersion
189
216
  });
190
- return {
191
- currentVersion,
192
- latestVersion,
193
- updateAvailable: compareSemver(latestVersion, currentVersion) > 0
194
- };
217
+ return toResult(currentVersion, latestVersion);
195
218
  } catch {
196
219
  return null;
197
220
  }
@@ -215,4 +238,4 @@ async function forceCheckForUpdate(currentVersion) {
215
238
  };
216
239
  }
217
240
  //#endregion
218
- export { fetchLatestVersion as a, isNpmPrefixWritable as c, compareSemver as i, setAutoUpdatePreference as l, REEXEC_GUARD_ENV as n, forceCheckForUpdate as o, checkForUpdate as r, getAutoUpdatePreference as s, INSTALL_CMD as t, shouldAttemptAutoUpdate as u };
241
+ export { forceCheckForUpdate as a, isNpmPrefixWritable as c, shouldAttemptAutoUpdate as d, fetchLatestVersion as i, readCachedUpdate as l, REEXEC_GUARD_ENV as n, getAutoUpdatePreference as o, compareSemver as r, getStartupUpdateStatus as s, INSTALL_CMD as t, setAutoUpdatePreference as u };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "2.1.0",
3
+ "version": "3.0.0",
4
4
  "mcpName": "io.github.bike4mind/bike4mind",
5
5
  "type": "module",
6
6
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
7
- "license": "UNLICENSED",
7
+ "license": "BUSL-1.1",
8
8
  "author": "Bike4Mind",
9
9
  "repository": {
10
10
  "type": "git",
@@ -37,15 +37,15 @@
37
37
  "dependencies": {
38
38
  "@agentclientprotocol/sdk": "1.2.1",
39
39
  "@anthropic-ai/sdk": "^0.111.0",
40
- "@aws-sdk/client-apigatewaymanagementapi": "^3.1080.0",
41
- "@aws-sdk/client-bedrock-runtime": "^3.1080.0",
42
- "@aws-sdk/client-cloudwatch": "^3.1080.0",
43
- "@aws-sdk/client-lambda": "^3.1080.0",
44
- "@aws-sdk/client-s3": "^3.1080.0",
45
- "@aws-sdk/client-sqs": "^3.1080.0",
46
- "@aws-sdk/client-transcribe": "^3.1080.0",
47
- "@aws-sdk/credential-provider-node": "^3.972.63",
48
- "@aws-sdk/s3-request-presigner": "^3.1080.0",
40
+ "@aws-sdk/client-apigatewaymanagementapi": "^3.1150.0",
41
+ "@aws-sdk/client-bedrock-runtime": "^3.1150.0",
42
+ "@aws-sdk/client-cloudwatch": "^3.1150.0",
43
+ "@aws-sdk/client-lambda": "^3.1150.0",
44
+ "@aws-sdk/client-s3": "^3.1150.0",
45
+ "@aws-sdk/client-sqs": "^3.1150.0",
46
+ "@aws-sdk/client-transcribe": "^3.1150.0",
47
+ "@aws-sdk/credential-provider-node": "^3.972.86",
48
+ "@aws-sdk/s3-request-presigner": "^3.1150.0",
49
49
  "@casl/ability": "^6.8.1",
50
50
  "@google/genai": "^1.46.0",
51
51
  "@joplin/turndown-plugin-gfm": "^1.0.67",
@@ -53,7 +53,7 @@
53
53
  "@modelcontextprotocol/sdk": "1.32.1",
54
54
  "@octokit/rest": "^22.0.1",
55
55
  "@opensearch-project/opensearch": "2.11.0",
56
- "@smithy/node-http-handler": "^4.9.3",
56
+ "@smithy/node-http-handler": "^4.12.1",
57
57
  "async-mutex": "^0.5.0",
58
58
  "axios": "1.20.0",
59
59
  "bcryptjs": "^3.0.3",
@@ -65,7 +65,6 @@
65
65
  "dayjs": "^1.11.23",
66
66
  "diff": "^9.0.0",
67
67
  "eventsource-parser": "^3.1.0",
68
- "fdir": "^6.5.0",
69
68
  "file-type": "^22.1.0",
70
69
  "fuse.js": "^7.5.0",
71
70
  "fzf": "^0.5.2",
@@ -90,7 +89,7 @@
90
89
  "p-limit": "^7.3.2",
91
90
  "picomatch": "^4.0.7",
92
91
  "qrcode": "^1.5.4",
93
- "react": "^19.2.7",
92
+ "react": "^19.3.0",
94
93
  "sharp": "^0.35.4",
95
94
  "shell-quote": "^1.12.0",
96
95
  "speakeasy": "^2.0.0",
@@ -107,7 +106,7 @@
107
106
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
108
107
  "yargs": "^18.1.0",
109
108
  "yauzl": "^3.4.0",
110
- "zod": "^4.4.3",
109
+ "zod": "^4.6.6",
111
110
  "zod-validation-error": "^5.0.0",
112
111
  "zustand": "^5.0.13"
113
112
  },
@@ -116,7 +115,7 @@
116
115
  "@types/jsonwebtoken": "^9.0.4",
117
116
  "@types/node": "^24.0.0",
118
117
  "@types/picomatch": "^4.0.3",
119
- "@types/react": "^19.2.17",
118
+ "@types/react": "^19.3.0",
120
119
  "@types/shell-quote": "^1.7.5",
121
120
  "@types/ws": "^8.18.1",
122
121
  "@types/yargs": "^17.0.35",
@@ -125,15 +124,17 @@
125
124
  "tsx": "^4.23.0",
126
125
  "typescript": "^5.9.3",
127
126
  "vitest": "^4.1.11",
128
- "@bike4mind/agents": "0.0.0-snapshot-changeset-release-main-20261009092832",
129
- "@bike4mind/fab-pipeline": "0.0.0-snapshot-changeset-release-main-20261009092832",
130
- "@bike4mind/common": "0.0.0-snapshot-changeset-release-main-20261009092832",
127
+ "@bike4mind/agents": "1.2.0",
128
+ "@bike4mind/client-auth": "0.2.0",
129
+ "@bike4mind/common": "15.0.0",
130
+ "@bike4mind/fab-pipeline": "2.1.0",
131
131
  "@bike4mind/hearth": "0.3.1",
132
- "@bike4mind/mcp": "0.0.0-snapshot-changeset-release-main-20261009092832",
133
- "@bike4mind/llm-adapters": "0.0.0-snapshot-changeset-release-main-20261009092832",
132
+ "@bike4mind/llm-adapters": "1.1.0",
133
+ "@bike4mind/mcp": "2.1.0",
134
134
  "@bike4mind/observability": "0.2.3",
135
- "@bike4mind/services": "0.0.0-snapshot-changeset-release-main-20261009092832",
136
- "@bike4mind/utils": "0.0.0-snapshot-changeset-release-main-20261009092832"
135
+ "@bike4mind/sdk": "1.0.0",
136
+ "@bike4mind/services": "17.0.0",
137
+ "@bike4mind/utils": "7.1.0"
137
138
  },
138
139
  "optionalDependencies": {
139
140
  "@vscode/ripgrep": "^1.18.0"
@@ -1,316 +0,0 @@
1
- #!/usr/bin/env node
2
- import { n as logger, t as ConfigStore } from "./ConfigStore-BygzEI6H.mjs";
3
- import { t as version } from "./package-CRNnNXxq.mjs";
4
- import axios, { isAxiosError } from "axios";
5
- //#region src/auth/OAuthClient.ts
6
- /**
7
- * OAuth 2.0 Device Authorization Flow client
8
- * Implements RFC 8628 for CLI authentication
9
- */
10
- var OAuthClient = class {
11
- constructor(baseURL = "http://localhost:3000") {
12
- this.clientId = "b4m-cli";
13
- this.apiClient = axios.create({
14
- baseURL,
15
- headers: { "Content-Type": "application/json" }
16
- });
17
- }
18
- /**
19
- * Initiate device authorization flow
20
- * Returns device code, user code, and verification URL
21
- */
22
- async initiateDeviceFlow() {
23
- return (await this.apiClient.post("/api/oauth/device/initiate", { client_id: this.clientId })).data;
24
- }
25
- /**
26
- * Poll for access token
27
- * Returns token response if approved, or throws error with status
28
- */
29
- async pollForToken(deviceCode) {
30
- try {
31
- const response = await this.apiClient.post("/api/oauth/device/token", {
32
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
33
- device_code: deviceCode,
34
- client_id: this.clientId
35
- }, { validateStatus: () => true });
36
- if ("error" in response.data) throw new Error(response.data.error);
37
- return response.data;
38
- } catch (error) {
39
- if (axios.isAxiosError(error) && error.response?.data?.error) throw new Error(error.response.data.error);
40
- throw error;
41
- }
42
- }
43
- /**
44
- * Wait for user authorization with automatic polling
45
- * Implements exponential backoff and respects server's interval
46
- */
47
- async waitForAuthorization(deviceCode, interval, onStatus) {
48
- let pollInterval = interval * 1e3;
49
- const maxInterval = 3e4;
50
- onStatus?.("Waiting for user authorization...");
51
- await this.sleep(pollInterval);
52
- while (true) try {
53
- return await this.pollForToken(deviceCode);
54
- } catch (error) {
55
- if (error instanceof Error) {
56
- const errorMessage = error.message;
57
- if (errorMessage === "authorization_pending") {
58
- onStatus?.("Waiting for user authorization...");
59
- await this.sleep(pollInterval);
60
- continue;
61
- }
62
- if (errorMessage === "slow_down") {
63
- pollInterval = Math.min(pollInterval + 5e3, maxInterval);
64
- onStatus?.("Slowing down polling...");
65
- await this.sleep(pollInterval);
66
- continue;
67
- }
68
- if (errorMessage === "access_denied") throw new Error("User denied the authorization request");
69
- if (errorMessage === "expired_token") throw new Error("Authorization code has expired");
70
- throw error;
71
- }
72
- throw error;
73
- }
74
- }
75
- /**
76
- * Refresh an expired access token
77
- */
78
- async refreshToken(refreshToken) {
79
- return (await this.apiClient.post("/api/oauth/refresh", {
80
- grant_type: "refresh_token",
81
- refresh_token: refreshToken,
82
- client_id: this.clientId
83
- })).data;
84
- }
85
- /**
86
- * Sleep for specified milliseconds
87
- */
88
- sleep(ms) {
89
- return new Promise((resolve) => setTimeout(resolve, ms));
90
- }
91
- };
92
- //#endregion
93
- //#region src/auth/providerKeyFailure.ts
94
- const PROVIDER_KEY_ERROR_CODES = /* @__PURE__ */ new Set(["provider_not_configured", "provider_rejected"]);
95
- /**
96
- * A 401 whose body names a missing or rejected AI-provider key (e.g. /api/ai/tts). The
97
- * Bike4Mind credential was accepted, so refreshing or re-authenticating cannot fix it.
98
- * Shared by the ApiClient refresh interceptor and mapApiError in mcp/b4mApiClient.ts.
99
- */
100
- function isProviderKeyFailure(data) {
101
- const body = decodeBinaryBody(data);
102
- if (!body || typeof body !== "object") return false;
103
- const { errorCode } = body;
104
- return typeof errorCode === "string" && PROVIDER_KEY_ERROR_CODES.has(errorCode);
105
- }
106
- /**
107
- * An arraybuffer-typed request (the generated-audio calls) gets its error body as raw
108
- * bytes, and the refresh interceptor sees it before any caller can decode it.
109
- */
110
- function decodeBinaryBody(data) {
111
- const bytes = data instanceof ArrayBuffer ? Buffer.from(data) : ArrayBuffer.isView(data) ? Buffer.from(data.buffer, data.byteOffset, data.byteLength) : void 0;
112
- if (!bytes) return data;
113
- try {
114
- return JSON.parse(bytes.toString("utf8"));
115
- } catch {
116
- return;
117
- }
118
- }
119
- //#endregion
120
- //#region src/auth/ApiClient.ts
121
- const USER_AGENT = `b4m-cli/${version}`;
122
- /**
123
- * Per-request timeout. Generous by design: a waited chat (POST /api/chat with wait:true)
124
- * runs a full server-side quest, so the bound only exists to stop a hung backend from
125
- * wedging a caller forever, not to cap a normal long quest. Override with B4M_API_TIMEOUT_MS
126
- * (0 disables the timeout entirely).
127
- */
128
- const DEFAULT_API_TIMEOUT_MS = 6e5;
129
- function resolveTimeoutMs() {
130
- const raw = process.env.B4M_API_TIMEOUT_MS;
131
- if (raw === void 0 || raw.trim() === "") return DEFAULT_API_TIMEOUT_MS;
132
- const parsed = Number(raw);
133
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_API_TIMEOUT_MS;
134
- }
135
- /**
136
- * Thrown by the response interceptor only when the session is DEFINITIVELY revoked - the
137
- * refresh token was rejected (400/401 invalid_grant), or a request still 401s after a
138
- * successful refresh. A transient refresh outage (5xx / network / timeout) throws a plain
139
- * Error instead, so callers that must distinguish "log out" from "retry" (e.g.
140
- * checkSessionValid / the WS reconnect loop) can key on the type. The human-readable
141
- * message is preserved on both paths so existing `error.message.includes(...)` callers are
142
- * unaffected.
143
- */
144
- var SessionRevokedError = class extends Error {
145
- constructor(message) {
146
- super(message);
147
- this.name = "SessionRevokedError";
148
- }
149
- };
150
- /**
151
- * 401 with no credential at all (no API key, no stored tokens): a config gap, not an expiry.
152
- * Not a SessionRevokedError, so checkSessionValid still reports "not revoked". The message keeps
153
- * `Authentication failed` for string-matching callers (turnController, handoff, ServerLlmBackend).
154
- */
155
- var NotAuthenticatedError = class extends Error {
156
- constructor(message) {
157
- super(message);
158
- this.name = "NotAuthenticatedError";
159
- }
160
- };
161
- /**
162
- * Authenticated API client for B4M services
163
- * Automatically injects access tokens from ConfigStore
164
- */
165
- var ApiClient = class {
166
- /**
167
- * @param apiKey - When set, requests authenticate with this instance API key via
168
- * the `x-api-key` header and the OAuth-JWT path (Bearer injection + refresh-on-401)
169
- * is bypassed entirely. Omit to keep the default stored-JWT behavior unchanged.
170
- */
171
- constructor(baseURL = "http://localhost:3000", configStore, apiKey) {
172
- this.configStore = configStore || new ConfigStore();
173
- this.oauthClient = new OAuthClient(baseURL);
174
- this.apiKey = apiKey;
175
- this.client = axios.create({
176
- baseURL,
177
- timeout: resolveTimeoutMs(),
178
- headers: {
179
- "Content-Type": "application/json",
180
- "User-Agent": USER_AGENT,
181
- "X-B4M-Client": USER_AGENT
182
- }
183
- });
184
- this.client.interceptors.request.use(async (config) => {
185
- if (this.apiKey) {
186
- config.headers["x-api-key"] = this.apiKey;
187
- return config;
188
- }
189
- const tokens = await this.configStore.getAuthTokens();
190
- if (tokens) config.headers.Authorization = `Bearer ${tokens.accessToken}`;
191
- return config;
192
- }, (error) => Promise.reject(error));
193
- this.client.interceptors.response.use((response) => response, async (error) => {
194
- const originalRequest = error.config;
195
- if (error.response?.status === 401) logger.debug("AUTH: Received 401 Unauthorized");
196
- else if (error.response?.status === 403) logger.error("403 Forbidden", error);
197
- if (this.apiKey) return Promise.reject(error);
198
- if (error.response?.status === 401 && isProviderKeyFailure(error.response.data)) return Promise.reject(error);
199
- if (error.response?.status === 401 && !originalRequest._retry) {
200
- originalRequest._retry = true;
201
- const tokens = await this.configStore.getAuthTokens();
202
- if (!tokens) throw new NotAuthenticatedError("Authentication failed: not logged in. Please run `b4m login` to authenticate.");
203
- try {
204
- if (new Date(tokens.expiresAt).getTime() > Date.now()) {
205
- logger.debug("AUTH: Access token has not expired, skipping refresh - 401 is likely transient");
206
- return Promise.reject(error);
207
- }
208
- logger.debug("AUTH: Attempting token refresh");
209
- const newTokens = await this.oauthClient.refreshToken(tokens.refreshToken);
210
- logger.debug("AUTH: Token refresh successful");
211
- const expiresAt = new Date(Date.now() + newTokens.expires_in * 1e3).toISOString();
212
- await this.configStore.setAuthTokens({
213
- accessToken: newTokens.access_token,
214
- refreshToken: newTokens.refresh_token ?? tokens.refreshToken,
215
- expiresAt,
216
- userId: tokens.userId
217
- });
218
- originalRequest.headers.Authorization = `Bearer ${newTokens.access_token}`;
219
- logger.debug("AUTH: Retrying request with new token");
220
- return this.client(originalRequest);
221
- } catch (refreshError) {
222
- const refreshMsg = refreshError instanceof Error ? refreshError.message : "Unknown error";
223
- logger.warn(`AUTH: Token refresh failed: ${refreshMsg}`);
224
- const tokens = await this.configStore.getAuthTokens();
225
- if (tokens && new Date(tokens.expiresAt) <= /* @__PURE__ */ new Date()) await this.configStore.clearAuthTokens();
226
- const msg = "Authentication expired. Please run `b4m login` again.";
227
- const refreshStatus = isAxiosError(refreshError) ? refreshError.response?.status : void 0;
228
- if (refreshStatus === 400 || refreshStatus === 401) throw new SessionRevokedError(msg);
229
- throw new Error(msg);
230
- }
231
- }
232
- if (error.response?.status === 401 && originalRequest._retry) {
233
- logger.debug("AUTH: Token refresh retry failed");
234
- const tokens = await this.configStore.getAuthTokens();
235
- if (tokens && new Date(tokens.expiresAt) <= /* @__PURE__ */ new Date()) await this.configStore.clearAuthTokens();
236
- throw new SessionRevokedError("Authentication failed. Please run /login to authenticate.");
237
- }
238
- return Promise.reject(error);
239
- });
240
- }
241
- /**
242
- * Make a GET request
243
- */
244
- async get(url, config) {
245
- return (await this.client.get(url, config)).data;
246
- }
247
- /**
248
- * Make a POST request
249
- */
250
- async post(url, data, config) {
251
- logger.debug(`[ApiClient] POST ${this.client.defaults.baseURL}${url}`);
252
- logger.debug(`[ApiClient] Request body: ${JSON.stringify(data)}`);
253
- const response = await this.client.post(url, data, config);
254
- logger.debug(`[ApiClient] Response status: ${response.status}`);
255
- return response.data;
256
- }
257
- /**
258
- * Make a PUT request
259
- */
260
- async put(url, data, config) {
261
- return (await this.client.put(url, data, config)).data;
262
- }
263
- /**
264
- * Make a DELETE request
265
- */
266
- async delete(url, config) {
267
- return (await this.client.delete(url, config)).data;
268
- }
269
- /**
270
- * Get the underlying axios instance for advanced use cases (e.g., streaming)
271
- */
272
- getAxiosInstance() {
273
- return this.client;
274
- }
275
- /**
276
- * Check if user is authenticated
277
- */
278
- async isAuthenticated() {
279
- return this.configStore.isAuthenticated();
280
- }
281
- /**
282
- * Get current user information
283
- */
284
- async getCurrentUser() {
285
- try {
286
- const tokens = await this.configStore.getAuthTokens();
287
- if (!tokens) return null;
288
- return { id: tokens.userId };
289
- } catch {
290
- return null;
291
- }
292
- }
293
- /**
294
- * Verifies the session is still valid via a cheap authed GET. The response interceptor
295
- * above already attempts a token refresh and retries on 401, so a resolved call means the
296
- * session is valid (fresh or transparently refreshed). Returns false ONLY on a
297
- * `SessionRevokedError` (the refresh token was rejected, or a 401 survived a refresh);
298
- * every other outcome - a transient refresh outage, a network blip, or the interceptor's
299
- * fresh-token 401 skip - is treated as transient and returns true, so callers keep
300
- * retrying rather than tearing down on a blip.
301
- *
302
- * Used by WebSocketConnectionManager to distinguish "session revoked" from "transient
303
- * network issue" when a WS connect attempt fails to open - a WS close event carries no
304
- * HTTP status, so this is the only way to tell the two apart.
305
- */
306
- async checkSessionValid() {
307
- try {
308
- await this.get("/api/identify");
309
- return true;
310
- } catch (err) {
311
- return !(err instanceof SessionRevokedError);
312
- }
313
- }
314
- };
315
- //#endregion
316
- export { OAuthClient as i, NotAuthenticatedError as n, isProviderKeyFailure as r, ApiClient as t };
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import { i as isBinaryAvailable, n as detectPlatform, r as expandPath, t as createSandboxRuntime } from "./SandboxRuntimeAdapter-BgLUVTJL.mjs";
3
- export { createSandboxRuntime, detectPlatform, expandPath, isBinaryAvailable };
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import { o as useCliStore } from "./store-CvjTpQPs.mjs";
3
- export { useCliStore };
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as DEFAULT_SANDBOX_CONFIG } from "./types-F61_hxmG.mjs";
3
- export { DEFAULT_SANDBOX_CONFIG };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as isTerminalShellStatus, t as getShellSessionManager } from "./ShellSessionManager-6o8KZzl1-vrbPAUTq.mjs";
3
- import { spawn } from "child_process";
4
3
  import path from "path";
4
+ import { spawn } from "child_process";
5
5
  import { StringDecoder } from "string_decoder";
6
6
  //#region ../../b4m-core/services/dist/bashExecute-CrdPpBqk.mjs
7
7
  const DEFAULT_TIMEOUT_MS = 6e4;
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as isPathAllowed } from "./pathValidation-BRqf4HFX-CHwtwp3O.mjs";
3
- import { execFile } from "child_process";
4
3
  import { existsSync } from "fs";
5
4
  import path from "path";
6
- import { promisify } from "util";
7
5
  import { stat } from "fs/promises";
6
+ import { execFile } from "child_process";
7
+ import { promisify } from "util";
8
8
  //#region ../../b4m-core/services/dist/grepSearch-BgoOOwGe.mjs
9
9
  const execFileAsync = promisify(execFile);
10
10
  /** Cached ripgrep binary path after first resolution */