@blaxel/core 0.3.35-preview.344 → 0.3.36-preview.347

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 (35) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/common/coding-agent.js +75 -0
  3. package/dist/cjs/common/coding-agent.test.js +50 -0
  4. package/dist/cjs/common/sentry.js +20 -2
  5. package/dist/cjs/common/sentry.test.js +22 -2
  6. package/dist/cjs/common/settings.js +33 -4
  7. package/dist/cjs/common/settings.test.js +132 -0
  8. package/dist/cjs/types/common/coding-agent.d.ts +7 -0
  9. package/dist/cjs/types/common/coding-agent.test.d.ts +1 -0
  10. package/dist/cjs/types/common/settings.d.ts +6 -0
  11. package/dist/cjs-browser/.tsbuildinfo +1 -1
  12. package/dist/cjs-browser/common/coding-agent.js +75 -0
  13. package/dist/cjs-browser/common/coding-agent.test.js +50 -0
  14. package/dist/cjs-browser/common/sentry.js +20 -2
  15. package/dist/cjs-browser/common/sentry.test.js +22 -2
  16. package/dist/cjs-browser/common/settings.js +33 -4
  17. package/dist/cjs-browser/common/settings.test.js +132 -0
  18. package/dist/cjs-browser/types/common/coding-agent.d.ts +7 -0
  19. package/dist/cjs-browser/types/common/coding-agent.test.d.ts +1 -0
  20. package/dist/cjs-browser/types/common/settings.d.ts +6 -0
  21. package/dist/esm/.tsbuildinfo +1 -1
  22. package/dist/esm/common/coding-agent.js +71 -0
  23. package/dist/esm/common/coding-agent.test.js +48 -0
  24. package/dist/esm/common/sentry.js +20 -2
  25. package/dist/esm/common/sentry.test.js +22 -2
  26. package/dist/esm/common/settings.js +33 -4
  27. package/dist/esm/common/settings.test.js +132 -0
  28. package/dist/esm-browser/.tsbuildinfo +1 -1
  29. package/dist/esm-browser/common/coding-agent.js +71 -0
  30. package/dist/esm-browser/common/coding-agent.test.js +48 -0
  31. package/dist/esm-browser/common/sentry.js +20 -2
  32. package/dist/esm-browser/common/sentry.test.js +22 -2
  33. package/dist/esm-browser/common/settings.js +33 -4
  34. package/dist/esm-browser/common/settings.test.js +132 -0
  35. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Environment variables coding agents set for the commands they run, first
3
+ * match wins, with the agent id the SDK reports. Ids match the Blaxel CLI's.
4
+ */
5
+ const CODING_AGENTS = [
6
+ ["CLAUDECODE", "claude-code"],
7
+ ["CODEX_THREAD_ID", "codex"],
8
+ ["CODEX_SANDBOX", "codex"],
9
+ ["CURSOR_AGENT", "cursor"],
10
+ ["GEMINI_CLI", "gemini-cli"],
11
+ ["QWEN_CODE", "qwen-code"],
12
+ ["OPENCODE_TERMINAL", "opencode"],
13
+ ["OPENCODE", "opencode"],
14
+ ["GOOSE_TERMINAL", "goose"],
15
+ ["AUGMENT_AGENT", "augment"],
16
+ ];
17
+ /**
18
+ * Agent names found in the generic `AI_AGENT` and `AGENT` variables, mapped to
19
+ * the same ids. Any other `AI_AGENT` name is reported as "other".
20
+ */
21
+ const CODING_AGENT_NAMES = {
22
+ "claude-code": "claude-code",
23
+ claude: "claude-code",
24
+ codex: "codex",
25
+ cursor: "cursor",
26
+ "cursor-cli": "cursor",
27
+ "gemini-cli": "gemini-cli",
28
+ gemini: "gemini-cli",
29
+ "qwen-code": "qwen-code",
30
+ opencode: "opencode",
31
+ goose: "goose",
32
+ augment: "augment",
33
+ "augment-cli": "augment",
34
+ crush: "crush",
35
+ "github-copilot": "github-copilot",
36
+ "github-copilot-cli": "github-copilot",
37
+ };
38
+ /** Every environment variable `detectCodingAgent` reads. */
39
+ export const CODING_AGENT_ENV_VARS = [
40
+ ...CODING_AGENTS.map(([name]) => name),
41
+ "AI_AGENT",
42
+ "AGENT",
43
+ ];
44
+ function agentName(value) {
45
+ const name = value?.trim().toLowerCase() ?? "";
46
+ // Claude Code sets AI_AGENT=claude-code_<version>_agent: keep the name.
47
+ const end = name.search(/[_/@ ]/);
48
+ return end > 0 ? name.slice(0, end) : name;
49
+ }
50
+ function knownAgentId(name) {
51
+ return Object.prototype.hasOwnProperty.call(CODING_AGENT_NAMES, name)
52
+ ? CODING_AGENT_NAMES[name]
53
+ : undefined;
54
+ }
55
+ /**
56
+ * Id of the coding agent whose environment the SDK runs in, "other" for an
57
+ * agent without a known name, or "" for none.
58
+ */
59
+ export function detectCodingAgent(getenv) {
60
+ for (const [name, id] of CODING_AGENTS) {
61
+ if (getenv(name)?.trim()) {
62
+ return id;
63
+ }
64
+ }
65
+ const name = agentName(getenv("AI_AGENT"));
66
+ if (name) {
67
+ return knownAgentId(name) ?? "other";
68
+ }
69
+ // AGENT is a generic name, so only a known agent name counts.
70
+ return knownAgentId(agentName(getenv("AGENT"))) ?? "";
71
+ }
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { detectCodingAgent } from "./coding-agent.js";
3
+ function detect(vars) {
4
+ return detectCodingAgent((name) => vars[name]);
5
+ }
6
+ describe("detectCodingAgent", () => {
7
+ it("returns nothing outside a coding agent", () => {
8
+ expect(detect({})).toBe("");
9
+ expect(detect({ CLAUDECODE: "", AI_AGENT: " " })).toBe("");
10
+ });
11
+ it.each([
12
+ [{ CLAUDECODE: "1" }, "claude-code"],
13
+ [{ CODEX_THREAD_ID: "thread" }, "codex"],
14
+ [{ CODEX_SANDBOX: "seatbelt" }, "codex"],
15
+ [{ CURSOR_AGENT: "1" }, "cursor"],
16
+ [{ GEMINI_CLI: "1" }, "gemini-cli"],
17
+ [{ QWEN_CODE: "1" }, "qwen-code"],
18
+ [{ OPENCODE_TERMINAL: "1" }, "opencode"],
19
+ [{ OPENCODE: "1", AGENT: "1" }, "opencode"],
20
+ [{ GOOSE_TERMINAL: "1" }, "goose"],
21
+ [{ AUGMENT_AGENT: "1" }, "augment"],
22
+ ])("detects the marker %j", (vars, id) => {
23
+ expect(detect(vars)).toBe(id);
24
+ });
25
+ it("prefers an agent-specific marker over the generic variables", () => {
26
+ expect(detect({ AI_AGENT: "something-else", CODEX_SANDBOX: "seatbelt" })).toBe("codex");
27
+ });
28
+ it.each([
29
+ ["claude-code_2-1-306_agent", "claude-code"],
30
+ ["Cursor", "cursor"],
31
+ ["crush", "crush"],
32
+ ["github-copilot-cli", "github-copilot"],
33
+ ["some-new-agent", "other"],
34
+ ])("maps AI_AGENT=%j to %j", (value, id) => {
35
+ expect(detect({ AI_AGENT: value })).toBe(id);
36
+ });
37
+ it("reads AGENT only for a known agent name", () => {
38
+ expect(detect({ AGENT: "goose" })).toBe("goose");
39
+ expect(detect({ AGENT: "1" })).toBe("");
40
+ expect(detect({ AGENT: "build-runner" })).toBe("");
41
+ expect(detect({ AGENT: "goose", AI_AGENT: "claude" })).toBe("claude-code");
42
+ });
43
+ it("never reports a free-form name", () => {
44
+ expect(detect({ AI_AGENT: "Jane Doe's laptop" })).toBe("other");
45
+ expect(detect({ AI_AGENT: "toString" })).toBe("other");
46
+ expect(detect({ AGENT: "toString" })).toBe("");
47
+ });
48
+ });
@@ -181,9 +181,25 @@ function generateEventId() {
181
181
  return value.toString(16);
182
182
  });
183
183
  }
184
+ const SAFE_WORKSPACE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
184
185
  /**
185
- * Build an allowlisted event. Raw messages, response bodies, workspace/resource
186
- * context, and absolute host paths are deliberately excluded.
186
+ * The workspace name is the only user identifier, so an issue shows how many
187
+ * workspaces it affects. Omitted when unknown or not a workspace name.
188
+ */
189
+ function sentryUser() {
190
+ try {
191
+ const workspace = settings.workspace;
192
+ return typeof workspace === "string" && SAFE_WORKSPACE_NAME.test(workspace)
193
+ ? { id: workspace }
194
+ : undefined;
195
+ }
196
+ catch {
197
+ return undefined;
198
+ }
199
+ }
200
+ /**
201
+ * Build an allowlisted event. Raw messages, response bodies, resource context,
202
+ * and absolute host paths are deliberately excluded.
187
203
  */
188
204
  function errorToSentryEvent(error, ownedFrames) {
189
205
  return {
@@ -193,6 +209,7 @@ function errorToSentryEvent(error, ownedFrames) {
193
209
  level: "error",
194
210
  environment: settings.env,
195
211
  release: `sdk-typescript@${settings.version}`,
212
+ user: sentryUser(),
196
213
  tags: {
197
214
  "blaxel.version": settings.version,
198
215
  "blaxel.commit": settings.commit,
@@ -286,6 +303,7 @@ function h2DegradationToSentryEvent(reason, domainTag, count) {
286
303
  release: `sdk-typescript@${settings.version}`,
287
304
  message: `h2 transport degradation: ${reason}`,
288
305
  fingerprint: ["h2-degradation", reason, domainTag],
306
+ user: sentryUser(),
289
307
  tags: {
290
308
  "blaxel.version": settings.version,
291
309
  "blaxel.commit": settings.commit,
@@ -5,7 +5,7 @@ const mockSettings = vi.hoisted(() => ({
5
5
  env: "prod",
6
6
  version: "9.9.9",
7
7
  commit: "abcdef0",
8
- workspace: "private-workspace",
8
+ workspace: "acme-staging",
9
9
  }));
10
10
  vi.mock("./settings.js", () => ({ settings: mockSettings }));
11
11
  const appFrameUrl = "file:///Users/customer/private-project/app.ts";
@@ -129,6 +129,8 @@ describe("SDK Sentry boundary", () => {
129
129
  "blaxel.error_source": "unhandled-sdk-exception",
130
130
  });
131
131
  expect(event.tags).not.toHaveProperty("blaxel.workspace");
132
+ // The workspace is the only user identifier, so issues count affected workspaces.
133
+ expect(event.user).toEqual({ id: "acme-staging" });
132
134
  const frames = event.exception?.values[0]?.stacktrace.frames ?? [];
133
135
  expect(frames.length).toBeGreaterThan(0);
134
136
  for (const frame of frames) {
@@ -137,7 +139,6 @@ describe("SDK Sentry boundary", () => {
137
139
  const serialized = JSON.stringify(event);
138
140
  expect(serialized).not.toContain("secret response body");
139
141
  expect(serialized).not.toContain("customer-123");
140
- expect(serialized).not.toContain("private-workspace");
141
142
  expect(serialized).not.toContain("/Users/customer");
142
143
  }
143
144
  finally {
@@ -234,8 +235,27 @@ describe("SDK Sentry boundary", () => {
234
235
  domainTag,
235
236
  },
236
237
  extra: { count: 100 },
238
+ user: { id: "acme-staging" },
237
239
  });
238
240
  });
241
+ it("leaves the user out when the workspace is not a workspace name", async () => {
242
+ const fetchMock = createFetchMock();
243
+ vi.stubGlobal("fetch", fetchMock);
244
+ const workspace = mockSettings.workspace;
245
+ mockSettings.workspace = "/Users/customer/private project";
246
+ try {
247
+ const { flushSentry, initSentry } = await import("./sentry.js");
248
+ initSentry();
249
+ emitUncaughtExceptionMonitor(makeSdkError());
250
+ await flushSentry();
251
+ const event = eventFromFetch(fetchMock);
252
+ expect(event.user).toBeUndefined();
253
+ expect(JSON.stringify(event)).not.toContain("/Users/customer");
254
+ }
255
+ finally {
256
+ mockSettings.workspace = workspace;
257
+ }
258
+ });
239
259
  it("groups keys independently and hashes external domains", async () => {
240
260
  const fetchMock = createFetchMock();
241
261
  vi.stubGlobal("fetch", fetchMock);
@@ -3,6 +3,7 @@ import { ApiKey } from "../authentication/apikey.js";
3
3
  import { ClientCredentials } from "../authentication/clientcredentials.js";
4
4
  import { MissingCredentials } from "../authentication/credentials.js";
5
5
  import { authentication } from "../authentication/index.js";
6
+ import { detectCodingAgent } from "../common/coding-agent.js";
6
7
  import { env } from "../common/env.js";
7
8
  import { CredentialsError } from "../common/errors.js";
8
9
  import { isBrokenBunH2Runtime } from "../common/h2-runtime.js";
@@ -24,8 +25,8 @@ function missingCredentialsMessage() {
24
25
  return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
25
26
  }
26
27
  // Build info - these placeholders are replaced at build time by build:replace-imports
27
- const BUILD_VERSION = "0.3.35-preview.344";
28
- const BUILD_COMMIT = "5f8cd5a9c6ba61ebc883d5c583b198b67da4167d";
28
+ const BUILD_VERSION = "0.3.36-preview.347";
29
+ const BUILD_COMMIT = "bb3cd5806f537daac42970497c263d4ccb4b2455";
29
30
  const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
30
31
  const BLAXEL_API_VERSION = "2026-09-22";
31
32
  // Bun < 1.3.11 never sends connection-level WINDOW_UPDATE: the pooled h2
@@ -62,6 +63,18 @@ function getConfigTracking() {
62
63
  }
63
64
  return null;
64
65
  }
66
+ /**
67
+ * Whether `DO_NOT_TRACK` (`1` or `true`) opts out of the coding agent token.
68
+ * The process environment is checked on its own as well, so a project `.env`
69
+ * or `blaxel.toml` can't undo an opt-out set for the process. The saved
70
+ * `tracking` setting is not read: it is an opt-in for error reports, and
71
+ * `bl login` saves it as false until the user turns it on.
72
+ */
73
+ function trackingOptedOut() {
74
+ const optsOut = (value) => value === "true" || value === "1";
75
+ const processValue = typeof process !== "undefined" ? process.env?.DO_NOT_TRACK : undefined;
76
+ return optsOut(processValue) || optsOut(env.DO_NOT_TRACK);
77
+ }
65
78
  // Function to get OS and architecture
66
79
  function getOsArch() {
67
80
  try {
@@ -218,16 +231,32 @@ class Settings {
218
231
  set integration(value) {
219
232
  this.config.integration = value;
220
233
  }
234
+ /**
235
+ * Id of the coding agent (Claude Code, Codex, Cursor, ...) whose environment
236
+ * runs the SDK, `"other"` for an agent without a known name, or `""` for none
237
+ * or when telemetry is opted out. Sent as an `agent/<id>` User-Agent token.
238
+ */
239
+ get codingAgent() {
240
+ const agent = detectCodingAgent((name) => env[name]);
241
+ return agent && !trackingOptedOut() ? agent : "";
242
+ }
221
243
  get headers() {
222
244
  this.assertCredentials();
223
245
  const osArch = getOsArch();
224
- const sdkUserAgent = `blaxel/sdk/typescript/${this.version} (${osArch}) blaxel/${this.commit}`;
246
+ let userAgent = `blaxel/sdk/typescript/${this.version} (${osArch}) blaxel/${this.commit}`;
225
247
  const integration = this.integration;
248
+ if (integration) {
249
+ userAgent += ` ${integration}`;
250
+ }
251
+ const codingAgent = this.codingAgent;
252
+ if (codingAgent) {
253
+ userAgent += ` agent/${codingAgent}`;
254
+ }
226
255
  return {
227
256
  "x-blaxel-authorization": this.authorization,
228
257
  "x-blaxel-workspace": this.workspace || "",
229
258
  "Blaxel-Version": this.apiVersion,
230
- "User-Agent": integration ? `${sdkUserAgent} ${integration}` : sdkUserAgent,
259
+ "User-Agent": userAgent,
231
260
  };
232
261
  }
233
262
  get name() {
@@ -1,7 +1,39 @@
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'fs';
2
+ import { tmpdir } from 'os';
3
+ import { join } from 'path';
1
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
5
  import { ApiKey } from '../authentication/apikey.js';
6
+ import { CODING_AGENT_ENV_VARS } from './coding-agent.js';
3
7
  import { env } from './env.js';
4
8
  import { isBrokenBunH2Runtime } from './h2-runtime.js';
9
+ /**
10
+ * These tests may run inside a coding agent, which sets the variables the SDK
11
+ * reads to tag its User-Agent. Clear them (and DO_NOT_TRACK) for each test.
12
+ */
13
+ function withoutCodingAgentEnv() {
14
+ const names = [...CODING_AGENT_ENV_VARS, 'DO_NOT_TRACK', 'HOME', 'USERPROFILE'];
15
+ const saved = {};
16
+ return {
17
+ clear() {
18
+ for (const name of names) {
19
+ saved[name] = process.env[name];
20
+ }
21
+ for (const name of [...CODING_AGENT_ENV_VARS, 'DO_NOT_TRACK']) {
22
+ delete process.env[name];
23
+ }
24
+ },
25
+ restore() {
26
+ for (const name of names) {
27
+ if (saved[name] === undefined) {
28
+ delete process.env[name];
29
+ }
30
+ else {
31
+ process.env[name] = saved[name];
32
+ }
33
+ }
34
+ },
35
+ };
36
+ }
5
37
  describe('Settings.apiVersion', () => {
6
38
  beforeEach(() => {
7
39
  // Reset the module-level settings singleton between tests if needed
@@ -218,6 +250,7 @@ describe('Settings H2 tuning knobs (config > env > default)', () => {
218
250
  });
219
251
  describe('Settings.integration', () => {
220
252
  const sdkUserAgent = /^blaxel\/sdk\/typescript\/\S+ \([^)]+\) blaxel\/\S+/;
253
+ const agentEnv = withoutCodingAgentEnv();
221
254
  async function authenticated() {
222
255
  const { settings } = await import('./settings.js');
223
256
  settings.credentials = new ApiKey({ apiKey: 'test-key', workspace: 'test-ws' });
@@ -225,7 +258,9 @@ describe('Settings.integration', () => {
225
258
  delete process.env.BL_INTEGRATION;
226
259
  return settings;
227
260
  }
261
+ beforeEach(() => agentEnv.clear());
228
262
  afterEach(async () => {
263
+ agentEnv.restore();
229
264
  const { settings } = await import('./settings.js');
230
265
  delete settings.config.integration;
231
266
  delete process.env.BL_INTEGRATION;
@@ -286,3 +321,100 @@ describe('Settings.integration warnings', () => {
286
321
  }
287
322
  });
288
323
  });
324
+ describe('Settings.codingAgent', () => {
325
+ const sdkUserAgent = /^blaxel\/sdk\/typescript\/\S+ \([^)]+\) blaxel\/\S+/;
326
+ const agentEnv = withoutCodingAgentEnv();
327
+ let home = '';
328
+ /** A fresh settings module, so it reads the empty test home directory. */
329
+ async function authenticated() {
330
+ const { settings } = await import('./settings.js');
331
+ settings.credentials = new ApiKey({ apiKey: 'test-key', workspace: 'test-ws' });
332
+ delete settings.config.integration;
333
+ delete process.env.BL_INTEGRATION;
334
+ return settings;
335
+ }
336
+ /** A fresh settings module whose home directory holds this config.yaml. */
337
+ async function withConfig(config) {
338
+ mkdirSync(join(home, '.blaxel'));
339
+ writeFileSync(join(home, '.blaxel', 'config.yaml'), config);
340
+ return authenticated();
341
+ }
342
+ // Keep the tracking choice saved on the machine running the tests out.
343
+ beforeEach(() => {
344
+ agentEnv.clear();
345
+ home = mkdtempSync(join(tmpdir(), 'blaxel-home-'));
346
+ process.env.HOME = home;
347
+ process.env.USERPROFILE = home;
348
+ vi.resetModules();
349
+ });
350
+ afterEach(() => {
351
+ agentEnv.restore();
352
+ delete process.env.BL_INTEGRATION;
353
+ rmSync(home, { recursive: true, force: true });
354
+ vi.resetModules();
355
+ });
356
+ it('sends no agent token outside a coding agent', async () => {
357
+ const settings = await authenticated();
358
+ expect(settings.codingAgent).toBe('');
359
+ expect(settings.headers['User-Agent']).toMatch(new RegExp(sdkUserAgent.source + '$'));
360
+ });
361
+ it('appends the agent token detected from the environment', async () => {
362
+ const settings = await authenticated();
363
+ process.env.CLAUDECODE = '1';
364
+ expect(settings.codingAgent).toBe('claude-code');
365
+ const userAgent = settings.headers['User-Agent'];
366
+ expect(userAgent).toMatch(sdkUserAgent);
367
+ expect(userAgent.endsWith(' agent/claude-code')).toBe(true);
368
+ });
369
+ it('puts the agent token after the integration token', async () => {
370
+ const settings = await authenticated();
371
+ process.env.CODEX_SANDBOX = 'seatbelt';
372
+ settings.integration = 'herdr/1.4.0';
373
+ expect(settings.headers['User-Agent'].endsWith(' herdr/1.4.0 agent/codex')).toBe(true);
374
+ });
375
+ it.each(['1', 'true'])('drops the token when DO_NOT_TRACK=%s', async (value) => {
376
+ const settings = await authenticated();
377
+ process.env.CLAUDECODE = '1';
378
+ process.env.DO_NOT_TRACK = value;
379
+ expect(settings.codingAgent).toBe('');
380
+ expect(settings.headers['User-Agent']).toMatch(new RegExp(sdkUserAgent.source + '$'));
381
+ });
382
+ it('drops the token when the process sets DO_NOT_TRACK=1 and the project .env says false', async () => {
383
+ vi.doMock('./node.js', async (importOriginal) => {
384
+ const original = await importOriginal();
385
+ const fs = original.fs;
386
+ const readFileSync = ((file, ...rest) => file === '.env' ? 'DO_NOT_TRACK=false\n' : fs.readFileSync(file, ...rest));
387
+ return { ...original, fs: { ...fs, readFileSync } };
388
+ });
389
+ try {
390
+ const settings = await authenticated();
391
+ const { env: projectEnv } = await import('./env.js');
392
+ expect(projectEnv.DO_NOT_TRACK).toBe('false');
393
+ process.env.CLAUDECODE = '1';
394
+ process.env.DO_NOT_TRACK = '1';
395
+ expect(settings.codingAgent).toBe('');
396
+ expect(settings.headers['User-Agent']).toMatch(new RegExp(sdkUserAgent.source + '$'));
397
+ }
398
+ finally {
399
+ vi.doUnmock('./node.js');
400
+ }
401
+ });
402
+ it('keeps the token when DO_NOT_TRACK turns tracking on', async () => {
403
+ const settings = await authenticated();
404
+ process.env.CLAUDECODE = '1';
405
+ process.env.DO_NOT_TRACK = '0';
406
+ expect(settings.codingAgent).toBe('claude-code');
407
+ });
408
+ it('keeps the token when config.yaml says tracking: false', async () => {
409
+ // bl login saves tracking: false until the user opts in to error reports.
410
+ const settings = await withConfig('tracking: false\n');
411
+ process.env.CURSOR_AGENT = '1';
412
+ expect(settings.codingAgent).toBe('cursor');
413
+ });
414
+ it('keeps the token when no tracking choice was saved', async () => {
415
+ const settings = await withConfig('context:\n workspace: test-ws\n');
416
+ process.env.CURSOR_AGENT = '1';
417
+ expect(settings.tracking).toBe(false);
418
+ expect(settings.codingAgent).toBe('cursor');
419
+ });
420
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blaxel/core",
3
- "version": "0.3.35-preview.344",
3
+ "version": "0.3.36-preview.347",
4
4
  "description": "Blaxel Core SDK for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "Blaxel, INC (https://blaxel.ai)",