grokbox 0.1.0-alpha.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3201,9 +3201,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3201
3201
  helpWidth: context.helpWidth,
3202
3202
  outputHasColors: context.hasColors
3203
3203
  });
3204
- const text = helper.formatHelp(this, helper);
3205
- if (context.hasColors) return text;
3206
- return this._outputConfiguration.stripColor(text);
3204
+ const text2 = helper.formatHelp(this, helper);
3205
+ if (context.hasColors) return text2;
3206
+ return this._outputConfiguration.stripColor(text2);
3207
3207
  }
3208
3208
  /**
3209
3209
  * @typedef HelpContext
@@ -3363,7 +3363,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3363
3363
  * @param {(string | Function)} text - string to add, or a function returning a string
3364
3364
  * @return {Command} `this` command for chaining
3365
3365
  */
3366
- addHelpText(position, text) {
3366
+ addHelpText(position, text2) {
3367
3367
  const allowedValues = ["beforeAll", "before", "after", "afterAll"];
3368
3368
  if (!allowedValues.includes(position)) {
3369
3369
  throw new Error(`Unexpected value for position to addHelpText.
@@ -3372,10 +3372,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
3372
3372
  const helpEvent = `${position}Help`;
3373
3373
  this.on(helpEvent, (context) => {
3374
3374
  let helpStr;
3375
- if (typeof text === "function") {
3376
- helpStr = text({ error: context.error, command: context.command });
3375
+ if (typeof text2 === "function") {
3376
+ helpStr = text2({ error: context.error, command: context.command });
3377
3377
  } else {
3378
- helpStr = text;
3378
+ helpStr = text2;
3379
3379
  }
3380
3380
  if (helpStr) {
3381
3381
  context.write(`${helpStr}
@@ -3474,7 +3474,7 @@ import { fileURLToPath } from "node:url";
3474
3474
  // package.json
3475
3475
  var package_default = {
3476
3476
  name: "grokbox",
3477
- version: "0.1.0-alpha.9",
3477
+ version: "0.1.1",
3478
3478
  packageManager: "bun@1.3.14",
3479
3479
  description: "Unofficial CLI and control plane for Grok Bot cloud computers",
3480
3480
  license: "MIT",
@@ -3675,6 +3675,8 @@ var LEAF_COMMANDS = [
3675
3675
  { flags: "--keepalive-interval-ms <n>", description: "Sandbox keeper interval" },
3676
3676
  { flags: "--quota-source <source>", description: "Explicit quota source (cursor-web)" },
3677
3677
  { flags: "--quota-access-token-ref <ref>", description: "Quota-only env:/file:/keychain: token reference" },
3678
+ { flags: "--server-access-token-ref <ref>", description: "Grok Bot Server account token reference" },
3679
+ { flags: "--server-backend-url <url>", description: "Grok Bot Server backend URL" },
3678
3680
  { flags: "--secret-stdin-file <field=path>", description: "Read one secret from stdin into a protected file" }
3679
3681
  ]),
3680
3682
  stdin: "secret",
@@ -3704,6 +3706,8 @@ var LEAF_COMMANDS = [
3704
3706
  { flags: "--keepalive-interval-ms <n>", description: "Sandbox keeper interval" },
3705
3707
  { flags: "--quota-source <source>", description: "Explicit quota source (cursor-web)" },
3706
3708
  { flags: "--quota-access-token-ref <ref>", description: "Quota-only env:/file:/keychain: token reference" },
3709
+ { flags: "--server-access-token-ref <ref>", description: "Grok Bot Server account token reference" },
3710
+ { flags: "--server-backend-url <url>", description: "Grok Bot Server backend URL" },
3707
3711
  { flags: "--secret-stdin-file <field=path>", description: "Read one secret from stdin into a protected file" }
3708
3712
  ]),
3709
3713
  stdin: "secret",
@@ -3995,7 +3999,7 @@ var LEAF_COMMANDS = [
3995
3999
  {
3996
4000
  path: ["agents", "routines", "list"],
3997
4001
  usage: "grokbox agents routines list <agent> [--json|--table]",
3998
- summary: "List one agent's bounded native Routine window without prompts or credentials.",
4002
+ summary: "List one agent's cloud-backed Routine metadata without prompts or credentials.",
3999
4003
  arguments: [
4000
4004
  { syntax: "<agent>", description: "Agent ID or unambiguous name/title", role: "agent", kinds: ["agent"] }
4001
4005
  ],
@@ -4071,7 +4075,7 @@ var LEAF_COMMANDS = [
4071
4075
  },
4072
4076
  {
4073
4077
  path: ["agents", "routines", "webhook", "credential"],
4074
- usage: "grokbox agents routines webhook credential <agent> <routine-id> --confirm [--reveal-key]",
4078
+ usage: "grokbox agents routines webhook credential <agent> <routine-id> --confirm [--reveal-key] [--verify]",
4075
4079
  summary: "Read or mint one webhook Routine credential; key is hidden unless explicitly revealed.",
4076
4080
  arguments: [
4077
4081
  { syntax: "<agent>", description: "Agent ID or unambiguous name/title", role: "agent", kinds: ["agent"] },
@@ -4079,7 +4083,8 @@ var LEAF_COMMANDS = [
4079
4083
  ],
4080
4084
  options: options([
4081
4085
  { flags: "--confirm", description: "Confirm credential access/mint", required: true },
4082
- { flags: "--reveal-key", description: "Include the webhook key and routine prompt in stdout" }
4086
+ { flags: "--reveal-key", description: "Include the webhook key and routine prompt in stdout" },
4087
+ { flags: "--verify", description: "Check the webhook key without invoking this Routine" }
4083
4088
  ], { timeout: true }),
4084
4089
  stdin: "none",
4085
4090
  table: false,
@@ -5153,18 +5158,18 @@ function asNumber(value, fallback = 0) {
5153
5158
  function asString(value, fallback = "") {
5154
5159
  return typeof value === "string" ? value : fallback;
5155
5160
  }
5156
- function stripOneTrailingNewline(text) {
5157
- if (text.endsWith("\r\n")) return text.slice(0, -2);
5158
- if (text.endsWith("\n")) return text.slice(0, -1);
5159
- return text;
5161
+ function stripOneTrailingNewline(text2) {
5162
+ if (text2.endsWith("\r\n")) return text2.slice(0, -2);
5163
+ if (text2.endsWith("\n")) return text2.slice(0, -1);
5164
+ return text2;
5160
5165
  }
5161
5166
  var UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
5162
5167
  function assertUuidV4(value, name) {
5163
5168
  if (!UUID_V4.test(value)) throw usage(`${name} must be a UUID v4.`);
5164
5169
  return value;
5165
5170
  }
5166
- function utf8Bytes(text) {
5167
- return new TextEncoder().encode(text).byteLength;
5171
+ function utf8Bytes(text2) {
5172
+ return new TextEncoder().encode(text2).byteLength;
5168
5173
  }
5169
5174
 
5170
5175
  // src/config/profile.ts
@@ -5192,10 +5197,12 @@ var PROFILE_KEYS = /* @__PURE__ */ new Set([
5192
5197
  "daemon_socket",
5193
5198
  "ssh_host",
5194
5199
  "sandbox",
5195
- "quota"
5200
+ "quota",
5201
+ "server"
5196
5202
  ]);
5197
5203
  var SANDBOX_KEYS = /* @__PURE__ */ new Set(["access_token_ref", "keepalive_interval_ms"]);
5198
5204
  var QUOTA_KEYS = /* @__PURE__ */ new Set(["source", "access_token_ref"]);
5205
+ var SERVER_KEYS = /* @__PURE__ */ new Set(["access_token_ref", "backend_url"]);
5199
5206
  var GLOBAL_KEYS = /* @__PURE__ */ new Set(["version", "current_profile"]);
5200
5207
  var PROFILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
5201
5208
  var SSH_HOST = /^[A-Za-z0-9][A-Za-z0-9._:@-]{0,254}$/;
@@ -5313,6 +5320,19 @@ function validateProfile(input) {
5313
5320
  if (accessTokenRef === void 0) throw invalid("quota.access_token_ref is required.");
5314
5321
  quota = { source, access_token_ref: accessTokenRef };
5315
5322
  }
5323
+ let server;
5324
+ if (input.server !== void 0) {
5325
+ if (!isRecord(input.server)) throw invalid("server must be an object.");
5326
+ assertKnownKeys(input.server, SERVER_KEYS, "server");
5327
+ const accessTokenRef = validateSecretRef(
5328
+ optionalString(input.server, "access_token_ref"),
5329
+ "server.access_token_ref"
5330
+ );
5331
+ if (accessTokenRef === void 0) throw invalid("server.access_token_ref is required.");
5332
+ const backendUrl = validateUrl(optionalString(input.server, "backend_url"), "server.backend_url");
5333
+ server = { access_token_ref: accessTokenRef, backend_url: backendUrl };
5334
+ if (server.backend_url === void 0) delete server.backend_url;
5335
+ }
5316
5336
  const profile = {
5317
5337
  version: 1,
5318
5338
  transport,
@@ -5328,7 +5348,8 @@ function validateProfile(input) {
5328
5348
  return value === void 0 ? void 0 : assertSshHost(value);
5329
5349
  })(),
5330
5350
  sandbox,
5331
- quota
5351
+ quota,
5352
+ server
5332
5353
  };
5333
5354
  for (const key of Object.keys(profile)) {
5334
5355
  if (profile[key] === void 0) delete profile[key];
@@ -5344,9 +5365,9 @@ function validateGlobalConfig(input) {
5344
5365
  return current === void 0 ? { version: 1 } : { version: 1, current_profile: current };
5345
5366
  }
5346
5367
  async function readJson(path, missing) {
5347
- let text;
5368
+ let text2;
5348
5369
  try {
5349
- text = await readFile2(path, "utf8");
5370
+ text2 = await readFile2(path, "utf8");
5350
5371
  } catch (error) {
5351
5372
  if (error.code === "ENOENT" && missing === "null") return null;
5352
5373
  if (error.code === "ENOENT") {
@@ -5355,7 +5376,7 @@ async function readJson(path, missing) {
5355
5376
  throw invalid("Configuration file is unreadable.");
5356
5377
  }
5357
5378
  try {
5358
- return JSON.parse(text);
5379
+ return JSON.parse(text2);
5359
5380
  } catch {
5360
5381
  throw invalid("Configuration file is not valid JSON.");
5361
5382
  }
@@ -5695,13 +5716,13 @@ function lostResponse(method) {
5695
5716
  }
5696
5717
  return new CliError("daemon_unreachable", "Daemon is unreachable.", { retryable: true });
5697
5718
  }
5698
- function parseResponse(text, status) {
5719
+ function parseResponse(text2, status) {
5699
5720
  if (status === 401 || status === 403) {
5700
5721
  throw new CliError("daemon_unauthorized", "Daemon rejected the shared credential.");
5701
5722
  }
5702
5723
  let parsed;
5703
5724
  try {
5704
- parsed = JSON.parse(text);
5725
+ parsed = JSON.parse(text2);
5705
5726
  } catch {
5706
5727
  throw new CliError("daemon_unreachable", "Daemon returned invalid JSON.");
5707
5728
  }
@@ -5795,7 +5816,7 @@ var LocalDaemonClient = class extends VersionedDaemonClient {
5795
5816
  headers: { "content-type": "application/json", "content-length": Buffer.byteLength(body) }
5796
5817
  },
5797
5818
  (res) => {
5798
- let text = "";
5819
+ let text2 = "";
5799
5820
  let bytes = 0;
5800
5821
  res.setEncoding("utf8");
5801
5822
  res.on("data", (chunk) => {
@@ -5804,12 +5825,12 @@ var LocalDaemonClient = class extends VersionedDaemonClient {
5804
5825
  req.destroy(new Error("response too large"));
5805
5826
  return;
5806
5827
  }
5807
- text += chunk;
5828
+ text2 += chunk;
5808
5829
  });
5809
5830
  res.on("end", () => {
5810
5831
  cleanup();
5811
5832
  try {
5812
- resolve5(parseResponse(text, res.statusCode ?? 0));
5833
+ resolve5(parseResponse(text2, res.statusCode ?? 0));
5813
5834
  } catch (error) {
5814
5835
  if (error instanceof CliError && error.code !== "daemon_unreachable") reject(error);
5815
5836
  else reject(lostResponse(method));
@@ -6511,10 +6532,10 @@ var GatewayClient = class {
6511
6532
  if (input.stream) {
6512
6533
  clearTimeout(timer);
6513
6534
  if (!response.ok) {
6514
- const text2 = await response.text();
6535
+ const text3 = await response.text();
6515
6536
  let parsed;
6516
6537
  try {
6517
- parsed = text2.length === 0 ? null : JSON.parse(text2);
6538
+ parsed = text3.length === 0 ? null : JSON.parse(text3);
6518
6539
  } catch {
6519
6540
  parsed = null;
6520
6541
  }
@@ -6522,9 +6543,9 @@ var GatewayClient = class {
6522
6543
  }
6523
6544
  return { status: response.status, body: null, text: "", response };
6524
6545
  }
6525
- let text = "";
6546
+ let text2 = "";
6526
6547
  try {
6527
- text = await response.text();
6548
+ text2 = await response.text();
6528
6549
  } catch {
6529
6550
  clearTimeout(timer);
6530
6551
  if (write) {
@@ -6538,14 +6559,14 @@ var GatewayClient = class {
6538
6559
  clearTimeout(timer);
6539
6560
  }
6540
6561
  let body = null;
6541
- if (text.length > 0) {
6562
+ if (text2.length > 0) {
6542
6563
  try {
6543
- body = JSON.parse(text);
6564
+ body = JSON.parse(text2);
6544
6565
  } catch {
6545
6566
  body = null;
6546
6567
  }
6547
6568
  }
6548
- return { status: response.status, body, text, response };
6569
+ return { status: response.status, body, text: text2, response };
6549
6570
  }
6550
6571
  interpret(result) {
6551
6572
  if (result.status >= 200 && result.status < 300) return result;
@@ -6666,10 +6687,10 @@ function ioFromOpts(opts) {
6666
6687
  function rejectTable(table, allowed) {
6667
6688
  if (table && !allowed) throw usage("This command does not support --table.");
6668
6689
  }
6669
- async function readPrompt(text, deps) {
6690
+ async function readPrompt(text2, deps) {
6670
6691
  let prompt;
6671
- if (text !== void 0) {
6672
- prompt = text;
6692
+ if (text2 !== void 0) {
6693
+ prompt = text2;
6673
6694
  } else if (deps.stdinIsTTY) {
6674
6695
  throw usage("Provide --text or pipe stdin. Interactive prompts are not supported.");
6675
6696
  } else {
@@ -7536,11 +7557,11 @@ function jitteredInterval(intervalMs, randomUUID7) {
7536
7557
  const factor = 1 - JITTER_RATIO + sample * JITTER_RATIO * 2;
7537
7558
  return Math.max(1e3, Math.round(intervalMs * factor));
7538
7559
  }
7539
- function failureDelay(intervalMs, failure, consecutiveFailures, retryAfterMs) {
7540
- if (failure === "rate_limited") {
7560
+ function failureDelay(intervalMs, failure2, consecutiveFailures, retryAfterMs) {
7561
+ if (failure2 === "rate_limited") {
7541
7562
  return Math.max(intervalMs, retryAfterMs ?? FAILURE_BASE_BACKOFF_MS);
7542
7563
  }
7543
- if (failure === "provider_unavailable" || failure === "request_timeout") {
7564
+ if (failure2 === "provider_unavailable" || failure2 === "request_timeout") {
7544
7565
  const backoff = Math.min(
7545
7566
  FAILURE_BASE_BACKOFF_MS * 2 ** Math.max(0, consecutiveFailures - 1),
7546
7567
  FAILURE_MAX_BACKOFF_MS
@@ -8427,15 +8448,15 @@ function validateDaemonConfig(input) {
8427
8448
  };
8428
8449
  }
8429
8450
  async function readDaemonConfig(configDir) {
8430
- let text;
8451
+ let text2;
8431
8452
  try {
8432
- text = await readFile5(daemonConfigPath(configDir), "utf8");
8453
+ text2 = await readFile5(daemonConfigPath(configDir), "utf8");
8433
8454
  } catch (error) {
8434
8455
  if (error.code === "ENOENT") return { version: 1 };
8435
8456
  throw new CliError("profile_invalid", "Daemon config is unreadable.");
8436
8457
  }
8437
8458
  try {
8438
- return validateDaemonConfig(JSON.parse(text));
8459
+ return validateDaemonConfig(JSON.parse(text2));
8439
8460
  } catch (error) {
8440
8461
  if (error instanceof CliError) throw error;
8441
8462
  throw new CliError("profile_invalid", "Daemon config is not valid JSON.");
@@ -8733,7 +8754,7 @@ function displayFromEnviron(environ) {
8733
8754
  }
8734
8755
  function inspectDesktopProc(cmdline) {
8735
8756
  const args = cmdline.split("\0").filter((part) => part.length > 0);
8736
- const text = args.join(" ");
8757
+ const text2 = args.join(" ");
8737
8758
  let startWindow = NaN;
8738
8759
  for (const [index, arg] of args.entries()) {
8739
8760
  if (arg === "start-window" || arg.endsWith("/start-window") || arg.endsWith("/start-window.sh")) {
@@ -8745,7 +8766,7 @@ function inspectDesktopProc(cmdline) {
8745
8766
  const slash = Math.max(argv0.lastIndexOf("/"), argv0.lastIndexOf("\\"));
8746
8767
  const base = argv0.slice(slash + 1);
8747
8768
  const grok = base === "grok" || args.some((arg) => arg === "grok" || arg.endsWith("/grok"));
8748
- const taskTool = /\bTask\(/.test(text);
8769
+ const taskTool = /\bTask\(/.test(text2);
8749
8770
  return {
8750
8771
  grok,
8751
8772
  task: taskTool,
@@ -10820,15 +10841,15 @@ var GovernedFilesystem = class _GovernedFilesystem {
10820
10841
  }
10821
10842
  const sha256 = createHash5("sha256").update(content).digest("hex");
10822
10843
  try {
10823
- const text = new TextDecoder("utf-8", { fatal: true }).decode(content);
10824
- if (!text.includes("\0")) {
10844
+ const text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
10845
+ if (!text2.includes("\0")) {
10825
10846
  return {
10826
10847
  path: target.remotePath,
10827
10848
  root: target.root.name,
10828
10849
  size: content.length,
10829
10850
  sha256,
10830
10851
  encoding: "utf8",
10831
- content: text
10852
+ content: text2
10832
10853
  };
10833
10854
  }
10834
10855
  } catch {
@@ -11452,17 +11473,17 @@ async function readBody(req) {
11452
11473
  return body;
11453
11474
  }
11454
11475
  function writeResponse(res, status, body) {
11455
- const text = JSON.stringify(body);
11476
+ const text2 = JSON.stringify(body);
11456
11477
  res.writeHead(status, {
11457
11478
  "content-type": "application/json",
11458
- "content-length": Buffer.byteLength(text)
11479
+ "content-length": Buffer.byteLength(text2)
11459
11480
  });
11460
- res.end(text);
11481
+ res.end(text2);
11461
11482
  }
11462
- function parseRequest(text) {
11483
+ function parseRequest(text2) {
11463
11484
  let parsed;
11464
11485
  try {
11465
- parsed = JSON.parse(text);
11486
+ parsed = JSON.parse(text2);
11466
11487
  } catch {
11467
11488
  throw new CliError("gateway_bad_request", "Daemon request is not valid JSON.");
11468
11489
  }
@@ -12848,10 +12869,10 @@ async function queryCursorWebQuota(deps, token, timeoutMs) {
12848
12869
  { failureCode: "unexpected_status", httpStatus: response.status }
12849
12870
  );
12850
12871
  }
12851
- const text = await readBoundedText(response);
12872
+ const text2 = await readBoundedText(response);
12852
12873
  let payload;
12853
12874
  try {
12854
- payload = JSON.parse(text);
12875
+ payload = JSON.parse(text2);
12855
12876
  } catch {
12856
12877
  throw protocolFailure("response_not_json");
12857
12878
  }
@@ -13342,8 +13363,8 @@ function redactJson(value, redacted) {
13342
13363
  }
13343
13364
  async function readJsonFile(path) {
13344
13365
  try {
13345
- const text = await readFile8(path, "utf8");
13346
- return JSON.parse(text);
13366
+ const text2 = await readFile8(path, "utf8");
13367
+ return JSON.parse(text2);
13347
13368
  } catch (error) {
13348
13369
  if (error.code === "ENOENT") return void 0;
13349
13370
  if (error instanceof SyntaxError) return void 0;
@@ -13429,19 +13450,19 @@ async function copyMemoryShard(sourceRoot, destRoot, shardDir, exportPrefix, lay
13429
13450
  });
13430
13451
  }
13431
13452
  }
13432
- function tokenMatches(text, name) {
13453
+ function tokenMatches(text2, name) {
13433
13454
  if (name.length < 3) return false;
13434
13455
  const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13435
- return new RegExp(`(^|[^A-Za-z0-9_-])${escaped}([^A-Za-z0-9_-]|$)`).test(text);
13456
+ return new RegExp(`(^|[^A-Za-z0-9_-])${escaped}([^A-Za-z0-9_-]|$)`).test(text2);
13436
13457
  }
13437
- function collectWorkflowNames(text, known) {
13458
+ function collectWorkflowNames(text2, known) {
13438
13459
  const found = /* @__PURE__ */ new Set();
13439
- for (const match of text.matchAll(WORKFLOW_REF)) {
13460
+ for (const match of text2.matchAll(WORKFLOW_REF)) {
13440
13461
  const name = match[1];
13441
13462
  if (name) found.add(name);
13442
13463
  }
13443
13464
  for (const name of known) {
13444
- if (tokenMatches(text, name)) found.add(name);
13465
+ if (tokenMatches(text2, name)) found.add(name);
13445
13466
  }
13446
13467
  return [...found].sort();
13447
13468
  }
@@ -14143,18 +14164,18 @@ async function runFsWrite(deps, remotePath, raw) {
14143
14164
  const io = ioFromOpts(raw);
14144
14165
  const expectedSha256 = expectedHash(raw.expectedSha256);
14145
14166
  const client2 = await filesystemClient(deps, io.timeoutMs, FS_WRITE_CAPABILITY);
14146
- let text;
14147
- if (raw.text !== void 0) text = raw.text;
14167
+ let text2;
14168
+ if (raw.text !== void 0) text2 = raw.text;
14148
14169
  else if (deps.stdinIsTTY) throw usage("Provide --text or pipe stdin.");
14149
- else text = await deps.readStdin();
14150
- const content = Buffer.from(text, "utf8");
14170
+ else text2 = await deps.readStdin();
14171
+ const content = Buffer.from(text2, "utf8");
14151
14172
  if (content.length > FS_WRITE_MAX_BYTES) throw usage("Text write exceeds the 1 MiB limit.");
14152
14173
  const sha256 = createHash7("sha256").update(content).digest("hex");
14153
14174
  const operationId2 = deps.randomUUID();
14154
14175
  writeSuccess(deps.stdout, await mutationCall(client2, "fsWrite", {
14155
14176
  operationId: operationId2,
14156
14177
  path: remotePath,
14157
- contentUtf8: text,
14178
+ contentUtf8: text2,
14158
14179
  ...expectedSha256 ? { expectedSha256 } : {}
14159
14180
  }, remotePath, { size: content.length, sha256 }));
14160
14181
  }
@@ -14789,6 +14810,15 @@ function patchFromOptions(raw) {
14789
14810
  });
14790
14811
  }
14791
14812
  }
14813
+ if (raw.serverAccessTokenRef !== void 0 || raw.serverBackendUrl !== void 0) {
14814
+ patch.server = {};
14815
+ if (raw.serverAccessTokenRef !== void 0) {
14816
+ patch.server.access_token_ref = raw.serverAccessTokenRef;
14817
+ }
14818
+ if (raw.serverBackendUrl !== void 0) {
14819
+ patch.server.backend_url = raw.serverBackendUrl;
14820
+ }
14821
+ }
14792
14822
  const quotaSecret = raw.secretStdinFile?.startsWith("quota-access-token=") ?? false;
14793
14823
  if (raw.quotaSource !== void 0 || raw.quotaAccessTokenRef !== void 0 || quotaSecret) {
14794
14824
  if (raw.quotaSource !== "cursor-web") {
@@ -14863,7 +14893,8 @@ async function runProfileUpdate(deps, name, raw) {
14863
14893
  if (Object.keys(patch).length === 0) throw usage("profile update requires at least one connection option.");
14864
14894
  const sandbox = patch.sandbox ? { ...current?.sandbox ?? {}, ...patch.sandbox } : current?.sandbox;
14865
14895
  const quota = patch.quota ? { ...current?.quota ?? {}, ...patch.quota } : current?.quota;
14866
- const next = validateProfile({ version: 1, ...current ?? {}, ...patch, sandbox, quota });
14896
+ const server = patch.server ? { ...current?.server ?? {}, ...patch.server } : current?.server;
14897
+ const next = validateProfile({ version: 1, ...current ?? {}, ...patch, sandbox, quota, server });
14867
14898
  await persistSecretInput(deps, secretInput);
14868
14899
  await writeProfileFile(deps.configDir, name, next);
14869
14900
  writeSuccess(deps.stdout, { name, updated: true });
@@ -14882,6 +14913,8 @@ async function runProfileCapabilities(deps, name, raw) {
14882
14913
  const sandboxConfigured = Boolean(profile.sandbox?.access_token_ref);
14883
14914
  const sandboxCapability = sandboxConfigured ? "provider-authorization-dependent" : false;
14884
14915
  const quotaCapability = profile.quota?.access_token_ref ? "provider-authorization-dependent" : false;
14916
+ const serverCapability = profile.server?.access_token_ref ? "provider-authorization-dependent" : false;
14917
+ const routineCapability = serverCapability || (local || directGateway || daemon ? "runtime-host-dependent" : false);
14885
14918
  writeSuccess(deps.stdout, {
14886
14919
  profile: profile.name,
14887
14920
  transport: profile.transport,
@@ -14908,11 +14941,671 @@ async function runProfileCapabilities(deps, name, raw) {
14908
14941
  "sandbox.inspect": sandboxCapability,
14909
14942
  "sandbox.wake": sandboxCapability,
14910
14943
  "sandbox.keepalive": sandboxCapability,
14911
- "quota.read": quotaCapability
14944
+ "quota.read": quotaCapability,
14945
+ "grok.routine.read": routineCapability,
14946
+ "grok.routine.webhook": routineCapability
14912
14947
  }
14913
14948
  });
14914
14949
  }
14915
14950
 
14951
+ // src/box-routine-auth.ts
14952
+ import { createHash as createHash9 } from "node:crypto";
14953
+ import { constants as constants5 } from "node:fs";
14954
+ import { open as open7, stat as stat6 } from "node:fs/promises";
14955
+
14956
+ // src/server-routines.ts
14957
+ import { createHash as createHash8 } from "node:crypto";
14958
+ import { constants as constants4 } from "node:fs";
14959
+ import { mkdir as mkdir9, open as open6, lstat as lstat5, rename as rename6, unlink as unlink3 } from "node:fs/promises";
14960
+ import { join as join11 } from "node:path";
14961
+ var SERVER_ROUTINE_PATHS = {
14962
+ listAgents: "/aiserver.v1.GrokBotService/ListGrokBotAgents",
14963
+ listAutomations: "/aiserver.v1.GrokBotService/ListGrokBotAgentAutomations",
14964
+ mintWebhookKey: "/aiserver.v1.DashboardService/CreateAutomationWebhookApiKey"
14965
+ };
14966
+ var DEFAULT_SERVER_BACKEND_URL = "https://api2.cursor.sh";
14967
+ var MAX_RESPONSE_BYTES2 = 8 * 1024 * 1024;
14968
+ var MAX_RECORD_BYTES = 512 * 1024;
14969
+ var MAX_KEY_BYTES = 4096;
14970
+ var MAX_AGENTS = 5e3;
14971
+ var MAX_ROUTINES = 100;
14972
+ var NIL_AUTOMATION_ID = "00000000-0000-5000-8000-000000000000";
14973
+ function protocolFailure2(failureCode) {
14974
+ return new CliError(
14975
+ "capability_unavailable",
14976
+ "The Server Routine surface did not satisfy the supported bounded protocol.",
14977
+ { failureCode }
14978
+ );
14979
+ }
14980
+ function authorizationFailure2(failureCode) {
14981
+ return new CliError(
14982
+ "credential_invalid",
14983
+ "The configured Server credential was not authorized for Routine access.",
14984
+ { failureCode }
14985
+ );
14986
+ }
14987
+ function varint(value) {
14988
+ const bytes = [];
14989
+ let rest = value;
14990
+ while (rest > 127) {
14991
+ bytes.push(rest & 127 | 128);
14992
+ rest = Math.floor(rest / 128);
14993
+ }
14994
+ bytes.push(rest);
14995
+ return Buffer.from(bytes);
14996
+ }
14997
+ function lengthDelimited(field, payload) {
14998
+ return Buffer.concat([varint(field << 3 | 2), varint(payload.length), payload]);
14999
+ }
15000
+ function readVarint(buffer, offset) {
15001
+ let value = 0n;
15002
+ for (let index = 0; index < 10; index += 1) {
15003
+ const byte = buffer[offset + index];
15004
+ if (byte === void 0) throw protocolFailure2("truncated_varint");
15005
+ if (index === 9 && byte > 1) throw protocolFailure2("varint_overflow");
15006
+ value |= BigInt(byte & 127) << BigInt(index * 7);
15007
+ if ((byte & 128) === 0) return { value, next: offset + index + 1 };
15008
+ }
15009
+ throw protocolFailure2("varint_overflow");
15010
+ }
15011
+ function decodeFields(buffer) {
15012
+ const fields = [];
15013
+ let offset = 0;
15014
+ while (offset < buffer.length) {
15015
+ const key = readVarint(buffer, offset);
15016
+ offset = key.next;
15017
+ if (key.value < 8n || key.value > 0xffffffffn) throw protocolFailure2("invalid_tag");
15018
+ const number = Number(key.value >> 3n);
15019
+ const wire = Number(key.value & 7n);
15020
+ if (wire === 0) {
15021
+ const value = readVarint(buffer, offset);
15022
+ offset = value.next;
15023
+ fields.push({ number, wire, value: value.value });
15024
+ } else if (wire === 2) {
15025
+ const size = readVarint(buffer, offset);
15026
+ offset = size.next;
15027
+ if (size.value > BigInt(buffer.length - offset)) throw protocolFailure2("truncated_field");
15028
+ const length = Number(size.value);
15029
+ fields.push({ number, wire, value: buffer.subarray(offset, offset + length) });
15030
+ offset += length;
15031
+ } else if (wire === 5) {
15032
+ if (offset + 4 > buffer.length) throw protocolFailure2("truncated_field");
15033
+ fields.push({ number, wire, value: buffer.subarray(offset, offset + 4) });
15034
+ offset += 4;
15035
+ } else if (wire === 1) {
15036
+ if (offset + 8 > buffer.length) throw protocolFailure2("truncated_field");
15037
+ fields.push({ number, wire, value: buffer.subarray(offset, offset + 8) });
15038
+ offset += 8;
15039
+ } else {
15040
+ if (wire === 3) {
15041
+ offset = skipGroup(buffer, offset, number, 0);
15042
+ fields.push({ number, wire, value: 0n });
15043
+ continue;
15044
+ }
15045
+ throw protocolFailure2("unsupported_wire_type");
15046
+ }
15047
+ }
15048
+ return fields;
15049
+ }
15050
+ function skipGroup(buffer, start, expected, depth) {
15051
+ if (depth > 32) throw protocolFailure2("group_depth_exceeded");
15052
+ let offset = start;
15053
+ while (offset < buffer.length) {
15054
+ const tag = readVarint(buffer, offset);
15055
+ if (tag.value < 8n || tag.value > 0xffffffffn) throw protocolFailure2("invalid_tag");
15056
+ const number = Number(tag.value >> 3n);
15057
+ const wire = Number(tag.value & 7n);
15058
+ offset = tag.next;
15059
+ if (wire === 4) {
15060
+ if (number !== expected) throw protocolFailure2("group_mismatch");
15061
+ return offset;
15062
+ }
15063
+ if (wire === 3) offset = skipGroup(buffer, offset, number, depth + 1);
15064
+ else if (wire === 0) offset = readVarint(buffer, offset).next;
15065
+ else if (wire === 1 || wire === 5) offset += wire === 1 ? 8 : 4;
15066
+ else if (wire === 2) {
15067
+ const size = readVarint(buffer, offset);
15068
+ if (size.value > BigInt(buffer.length - size.next)) throw protocolFailure2("truncated_field");
15069
+ offset = size.next + Number(size.value);
15070
+ } else throw protocolFailure2("unsupported_wire_type");
15071
+ if (offset > buffer.length) throw protocolFailure2("truncated_field");
15072
+ }
15073
+ throw protocolFailure2("truncated_group");
15074
+ }
15075
+ function firstBytes(fields, number) {
15076
+ const hits = fields.filter((field) => field.number === number);
15077
+ if (hits.length > 1 || hits.length === 1 && hits[0].wire !== 2) {
15078
+ throw protocolFailure2("invalid_required_field");
15079
+ }
15080
+ const value = hits[0]?.value;
15081
+ return value instanceof Buffer ? value : void 0;
15082
+ }
15083
+ function text(buffer) {
15084
+ try {
15085
+ return buffer === void 0 ? "" : new TextDecoder("utf-8", { fatal: true }).decode(buffer);
15086
+ } catch {
15087
+ throw protocolFailure2("invalid_utf8");
15088
+ }
15089
+ }
15090
+ function webhookUrlFor(backendUrl, serverId) {
15091
+ return new URL(`/automations/webhook/${encodeURIComponent(serverId)}`, normalizedBackend(backendUrl)).toString();
15092
+ }
15093
+ function normalizedBackend(backendUrl) {
15094
+ let parsed;
15095
+ try {
15096
+ parsed = new URL(backendUrl);
15097
+ } catch {
15098
+ throw new CliError("profile_invalid", "Server backend URL is invalid.");
15099
+ }
15100
+ const loopback = parsed.hostname === "localhost" || parsed.hostname === "[::1]" || /^127(?:\.[0-9]{1,3}){3}$/.test(parsed.hostname);
15101
+ if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && loopback) || parsed.username || parsed.password || backendUrl.includes("?") || backendUrl.includes("#") || parsed.pathname !== "/") {
15102
+ throw new CliError("profile_invalid", "Server backend must be an HTTPS origin (HTTP only on loopback), without credentials, query, or fragment.");
15103
+ }
15104
+ return parsed.toString();
15105
+ }
15106
+ async function readBounded2(response, limit) {
15107
+ const declared = response.headers.get("content-length");
15108
+ if (declared !== null) {
15109
+ const bytes = Number(declared);
15110
+ if (!Number.isSafeInteger(bytes) || bytes < 0 || bytes > limit) {
15111
+ throw protocolFailure2("response_oversized");
15112
+ }
15113
+ }
15114
+ if (!response.body) return Buffer.alloc(0);
15115
+ const reader = response.body.getReader();
15116
+ const chunks = [];
15117
+ let total = 0;
15118
+ try {
15119
+ for (; ; ) {
15120
+ const { done, value } = await reader.read();
15121
+ if (done) break;
15122
+ total += value.byteLength;
15123
+ if (total > limit) {
15124
+ await reader.cancel().catch(() => void 0);
15125
+ throw protocolFailure2("response_oversized");
15126
+ }
15127
+ chunks.push(Buffer.from(value));
15128
+ }
15129
+ } finally {
15130
+ reader.releaseLock();
15131
+ }
15132
+ return Buffer.concat(chunks);
15133
+ }
15134
+ async function call(deps, backendUrl, token, path, body, timeoutMs) {
15135
+ const controller2 = new AbortController();
15136
+ const abort2 = () => controller2.abort();
15137
+ if (deps.signal?.aborted) controller2.abort();
15138
+ else deps.signal?.addEventListener("abort", abort2, { once: true });
15139
+ const timer = setTimeout(() => controller2.abort(), timeoutMs);
15140
+ const cleanup = () => {
15141
+ clearTimeout(timer);
15142
+ deps.signal?.removeEventListener("abort", abort2);
15143
+ };
15144
+ let response;
15145
+ try {
15146
+ if (controller2.signal.aborted) throw new Error("aborted");
15147
+ response = await deps.fetch(new URL(path, normalizedBackend(backendUrl)).toString(), {
15148
+ method: "POST",
15149
+ headers: {
15150
+ accept: "application/proto",
15151
+ "content-type": "application/proto",
15152
+ authorization: `Bearer ${token}`
15153
+ },
15154
+ body: new Uint8Array(body),
15155
+ cache: "no-store",
15156
+ redirect: "manual",
15157
+ signal: controller2.signal
15158
+ });
15159
+ } catch {
15160
+ cleanup();
15161
+ throw new CliError(
15162
+ "gateway_unreachable",
15163
+ controller2.signal.aborted ? "The Server Routine request timed out." : "The Server Routine endpoint is unreachable.",
15164
+ { failureCode: controller2.signal.aborted ? "request_timeout" : "network_failure", retryable: true }
15165
+ );
15166
+ }
15167
+ try {
15168
+ if (controller2.signal.aborted) {
15169
+ await response.body?.cancel().catch(() => void 0);
15170
+ throw protocolFailure2("request_cancelled");
15171
+ }
15172
+ if (response.redirected || response.status >= 300 && response.status < 400) {
15173
+ await response.body?.cancel().catch(() => void 0);
15174
+ throw protocolFailure2("redirect_refused");
15175
+ }
15176
+ if (response.status === 401 || response.status === 403) {
15177
+ const detail = await readBounded2(response, MAX_RESPONSE_BYTES2).then((buffer) => buffer.toString("utf8")).catch(() => "");
15178
+ throw authorizationFailure2(authorizationFailureCode(detail));
15179
+ }
15180
+ if (response.status === 404) {
15181
+ await response.body?.cancel().catch(() => void 0);
15182
+ throw new CliError("target_not_found", "The Server does not know that Routine.", {
15183
+ failureCode: "routine_not_found",
15184
+ httpStatus: 404
15185
+ });
15186
+ }
15187
+ if (response.status === 408 || response.status === 429 || response.status >= 500) {
15188
+ await response.body?.cancel().catch(() => void 0);
15189
+ throw new CliError("gateway_unreachable", "The Server Routine endpoint refused the request.", {
15190
+ failureCode: "provider_refused",
15191
+ httpStatus: response.status,
15192
+ retryable: true
15193
+ });
15194
+ }
15195
+ if (response.status !== 200) {
15196
+ await response.body?.cancel().catch(() => void 0);
15197
+ throw new CliError(
15198
+ "capability_unavailable",
15199
+ "The Server Routine method is unavailable for this credential.",
15200
+ { failureCode: "unexpected_status", httpStatus: response.status }
15201
+ );
15202
+ }
15203
+ if (response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/proto") {
15204
+ await response.body?.cancel().catch(() => void 0);
15205
+ throw protocolFailure2("response_content_type_invalid");
15206
+ }
15207
+ return await readBounded2(response, MAX_RESPONSE_BYTES2);
15208
+ } catch (error) {
15209
+ if (error instanceof CliError) throw error;
15210
+ throw new CliError(
15211
+ "gateway_unreachable",
15212
+ controller2.signal.aborted ? "The Server Routine request timed out." : "The Server Routine response could not be read.",
15213
+ { failureCode: controller2.signal.aborted ? "request_timeout" : "response_unreadable", retryable: true }
15214
+ );
15215
+ } finally {
15216
+ cleanup();
15217
+ }
15218
+ }
15219
+ function authorizationFailureCode(detail) {
15220
+ if (/Only the automation owner/i.test(detail)) return "not_routine_owner";
15221
+ if (/missing required scope/i.test(detail)) return "scope_mismatch";
15222
+ if (/Invalid API key/i.test(detail)) return "invalid_api_key";
15223
+ if (/ERROR_NOT_LOGGED_IN|not logged in/i.test(detail)) return "not_signed_in";
15224
+ return "credential_refused";
15225
+ }
15226
+ async function listServerAgents(deps, options2) {
15227
+ const body = await call(deps, options2.backendUrl, options2.token, SERVER_ROUTINE_PATHS.listAgents, Buffer.alloc(0), options2.timeoutMs);
15228
+ const agents = [];
15229
+ const seen = /* @__PURE__ */ new Set();
15230
+ const fields = decodeFields(body);
15231
+ const truncated = fields.filter((field) => field.number === 3);
15232
+ if (truncated.length > 1 || truncated[0] && truncated[0].wire !== 0) throw protocolFailure2("agent_truncation_invalid");
15233
+ if (truncated[0] && truncated[0].value !== 0n) throw protocolFailure2("agent_roster_truncated");
15234
+ for (const field of fields) {
15235
+ if (field.number !== 1) continue;
15236
+ if (!(field.value instanceof Buffer) || field.wire !== 2) throw protocolFailure2("agent_envelope_invalid");
15237
+ const inner = decodeFields(field.value);
15238
+ const id = text(firstBytes(inner, 12));
15239
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id) || seen.has(id)) {
15240
+ throw protocolFailure2("agent_id_invalid");
15241
+ }
15242
+ seen.add(id);
15243
+ const kinds = inner.filter((field2) => field2.number === 19);
15244
+ if (kinds.length > 1 || kinds[0] && kinds[0].wire !== 0) throw protocolFailure2("agent_kind_invalid");
15245
+ const kind = kinds[0]?.value ?? 0n;
15246
+ if (kind !== 0n && kind !== 1n && kind !== 2n) throw protocolFailure2("agent_kind_unknown");
15247
+ const harness = text(firstBytes(inner, 13));
15248
+ const name = text(firstBytes(inner, 3));
15249
+ if (name.length > 512 || /[\x00-\x1f\x7f]/.test(name)) throw protocolFailure2("agent_name_invalid");
15250
+ agents.push({
15251
+ id,
15252
+ name,
15253
+ harness: harness === "temporal" || harness === "box" ? harness : null,
15254
+ isGroup: kind === 2n
15255
+ });
15256
+ if (agents.length > MAX_AGENTS) throw protocolFailure2("too_many_agents");
15257
+ }
15258
+ return agents;
15259
+ }
15260
+ async function listServerRoutines(deps, options2) {
15261
+ const body = await call(
15262
+ deps,
15263
+ options2.backendUrl,
15264
+ options2.token,
15265
+ SERVER_ROUTINE_PATHS.listAutomations,
15266
+ lengthDelimited(1, Buffer.from(options2.agentId, "utf8")),
15267
+ options2.timeoutMs
15268
+ );
15269
+ const records = [];
15270
+ const serverIds = [];
15271
+ const seen = /* @__PURE__ */ new Set();
15272
+ for (const field of decodeFields(body)) {
15273
+ if (field.number !== 1) continue;
15274
+ if (!(field.value instanceof Buffer) || field.wire !== 2) throw protocolFailure2("routine_envelope_invalid");
15275
+ const inner = decodeFields(field.value);
15276
+ const serverId = text(firstBytes(inner, 1));
15277
+ const recordBytes = firstBytes(inner, 2);
15278
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,255}$/.test(serverId) || seen.has(serverId)) {
15279
+ throw protocolFailure2("server_id_invalid");
15280
+ }
15281
+ if (recordBytes === void 0) throw protocolFailure2("record_missing");
15282
+ if (recordBytes.length > MAX_RECORD_BYTES) throw protocolFailure2("record_oversized");
15283
+ const json = text(recordBytes);
15284
+ let record3;
15285
+ try {
15286
+ record3 = JSON.parse(json);
15287
+ } catch {
15288
+ throw protocolFailure2("record_not_json");
15289
+ }
15290
+ if (!isRecord(record3)) throw protocolFailure2("record_not_object");
15291
+ seen.add(serverId);
15292
+ records.push(record3);
15293
+ serverIds.push(serverId);
15294
+ if (records.length > MAX_ROUTINES) throw protocolFailure2("too_many_routines");
15295
+ }
15296
+ try {
15297
+ return projectNativeRoutines(options2.agentId, records).catalog.routines.map((routine, index) => ({
15298
+ ...routine,
15299
+ mutable: false,
15300
+ serverId: serverIds[index],
15301
+ webhookUrl: routine.trigger.type === "webhook" ? webhookUrlFor(options2.backendUrl, serverIds[index]) : null
15302
+ }));
15303
+ } catch (error) {
15304
+ if (error instanceof CliError) throw error;
15305
+ throw protocolFailure2("routine_record_invalid");
15306
+ }
15307
+ }
15308
+ function assertWebhookKey(value) {
15309
+ if (value.length < 1 || Buffer.byteLength(value, "utf8") > MAX_KEY_BYTES || /[\x00-\x20\x7f]/.test(value)) {
15310
+ throw protocolFailure2("key_invalid");
15311
+ }
15312
+ return value;
15313
+ }
15314
+ async function mintWebhookKey(deps, options2) {
15315
+ const body = await call(
15316
+ deps,
15317
+ options2.backendUrl,
15318
+ options2.token,
15319
+ SERVER_ROUTINE_PATHS.mintWebhookKey,
15320
+ lengthDelimited(1, Buffer.from(options2.serverId, "utf8")),
15321
+ options2.timeoutMs
15322
+ );
15323
+ const key = text(firstBytes(decodeFields(body), 1));
15324
+ if (key.length === 0) throw protocolFailure2("key_missing");
15325
+ return assertWebhookKey(key);
15326
+ }
15327
+ async function probeWebhookKey(deps, options2) {
15328
+ if (options2.serverId === NIL_AUTOMATION_ID) throw protocolFailure2("unsafe_probe_target");
15329
+ const controller2 = new AbortController();
15330
+ const abort2 = () => controller2.abort();
15331
+ if (deps.signal?.aborted) controller2.abort();
15332
+ else deps.signal?.addEventListener("abort", abort2, { once: true });
15333
+ const timer = setTimeout(abort2, options2.timeoutMs);
15334
+ try {
15335
+ if (controller2.signal.aborted) throw protocolFailure2("request_cancelled");
15336
+ const response = await deps.fetch(webhookUrlFor(options2.backendUrl, NIL_AUTOMATION_ID), {
15337
+ method: "POST",
15338
+ headers: { authorization: `Bearer ${options2.key}`, "content-type": "application/json" },
15339
+ body: "{}",
15340
+ cache: "no-store",
15341
+ redirect: "manual",
15342
+ signal: controller2.signal
15343
+ });
15344
+ const detail = JSON.parse(text(await readBounded2(response, 64 * 1024)));
15345
+ if (controller2.signal.aborted) throw protocolFailure2("request_cancelled");
15346
+ if (!response.redirected && response.status === 401 && isRecord(detail) && detail.code === "error") {
15347
+ if (detail.message === `User API key missing required scope: automation:${NIL_AUTOMATION_ID}`) return true;
15348
+ if (detail.message === "Invalid API key") return false;
15349
+ }
15350
+ throw protocolFailure2("verification_unknown");
15351
+ } catch (error) {
15352
+ if (error instanceof CliError) throw error;
15353
+ throw protocolFailure2(controller2.signal.aborted ? "request_cancelled" : "verification_unknown");
15354
+ } finally {
15355
+ clearTimeout(timer);
15356
+ deps.signal?.removeEventListener("abort", abort2);
15357
+ }
15358
+ }
15359
+ async function cachedWebhookKey(deps, options2) {
15360
+ if (options2.routine.createdAtMs === null) throw protocolFailure2("routine_incarnation_missing");
15361
+ const scope = createHash8("sha256").update(JSON.stringify([
15362
+ normalizedBackend(options2.backendUrl),
15363
+ options2.credentialScope ?? createHash8("sha256").update(options2.token).digest("hex"),
15364
+ options2.agentId,
15365
+ options2.routine.serverId,
15366
+ options2.routine.createdAtMs
15367
+ ])).digest("hex");
15368
+ const directory = join11(options2.configDir, "server-webhook-keys");
15369
+ const path = join11(directory, `${scope}.json`);
15370
+ const lockPath = join11(directory, `${scope}.lock`);
15371
+ let lock;
15372
+ let temporary;
15373
+ let preserveLock = false;
15374
+ try {
15375
+ await mkdir9(directory, { recursive: true, mode: 448 });
15376
+ const dir = await lstat5(directory);
15377
+ if (!dir.isDirectory() || (dir.mode & 63) !== 0 || process.getuid && dir.uid !== process.getuid()) throw protocolFailure2("key_store_permissions");
15378
+ lock = await open6(lockPath, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY | constants4.O_NOFOLLOW, 384);
15379
+ let cached;
15380
+ try {
15381
+ cached = await open6(path, constants4.O_RDONLY | constants4.O_NOFOLLOW);
15382
+ } catch (error) {
15383
+ if (!isRecord(error) || error.code !== "ENOENT") throw error;
15384
+ }
15385
+ if (cached) {
15386
+ try {
15387
+ const stat7 = await cached.stat();
15388
+ if (!stat7.isFile() || (stat7.mode & 63) !== 0 || stat7.nlink !== 1 || stat7.size > 8192 || process.getuid && stat7.uid !== process.getuid()) throw protocolFailure2("key_store_permissions");
15389
+ const bytes = Buffer.alloc(8193);
15390
+ const { bytesRead } = await cached.read(bytes, 0, bytes.length, 0);
15391
+ if (bytesRead > 8192) throw protocolFailure2("key_store_corrupt");
15392
+ const record3 = JSON.parse(text(bytes.subarray(0, bytesRead)));
15393
+ if (!isRecord(record3) || record3.version !== 1 || record3.scope !== scope || typeof record3.key !== "string" || Object.keys(record3).length !== 3) throw protocolFailure2("key_store_corrupt");
15394
+ return assertWebhookKey(record3.key);
15395
+ } finally {
15396
+ await cached.close();
15397
+ }
15398
+ }
15399
+ if (deps.signal?.aborted) throw protocolFailure2("request_cancelled");
15400
+ temporary = join11(directory, `${scope}.${process.pid}.${crypto.randomUUID()}.tmp`);
15401
+ const output = await (deps.openWebhookKeyStore ?? open6)(temporary, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY | constants4.O_NOFOLLOW, 384);
15402
+ try {
15403
+ if (deps.signal?.aborted) throw protocolFailure2("request_cancelled");
15404
+ preserveLock = true;
15405
+ const key = await mintWebhookKey(deps, { ...options2, serverId: options2.routine.serverId });
15406
+ await output.writeFile(JSON.stringify({ version: 1, scope, key }), "utf8");
15407
+ await output.sync();
15408
+ await output.close();
15409
+ await rename6(temporary, path);
15410
+ temporary = void 0;
15411
+ preserveLock = false;
15412
+ return key;
15413
+ } finally {
15414
+ await output.close().catch(() => void 0);
15415
+ }
15416
+ } catch (error) {
15417
+ if (error instanceof CliError && (error.code === "credential_invalid" || error.code === "target_not_found" || error.httpStatus !== void 0 && error.httpStatus >= 400 && error.httpStatus < 500 && error.httpStatus !== 408)) {
15418
+ preserveLock = false;
15419
+ }
15420
+ if (preserveLock) {
15421
+ throw new CliError("operation_outcome_unknown", "Webhook key mint outcome is unknown; no automatic retry is safe. Inspect the protected store lock before another attempt.", {
15422
+ failureCode: "mint_outcome_unknown",
15423
+ retryable: false
15424
+ });
15425
+ }
15426
+ if (error instanceof CliError) throw error;
15427
+ throw protocolFailure2("key_store_unavailable");
15428
+ } finally {
15429
+ if (temporary) await unlink3(temporary).catch(() => void 0);
15430
+ if (lock) {
15431
+ await lock.close();
15432
+ if (!preserveLock) await unlink3(lockPath).catch(() => void 0);
15433
+ }
15434
+ }
15435
+ }
15436
+
15437
+ // src/box-routine-auth.ts
15438
+ var ENV_LIMIT = 256 * 1024;
15439
+ var RESPONSE_LIMIT = 32 * 1024;
15440
+ async function boundedFile(path, limit) {
15441
+ const handle = await open7(path, constants5.O_RDONLY | constants5.O_NOFOLLOW);
15442
+ try {
15443
+ const buffer = Buffer.alloc(limit + 1);
15444
+ let length = 0;
15445
+ while (length < buffer.length) {
15446
+ const { bytesRead } = await handle.read(buffer, length, buffer.length - length, null);
15447
+ if (!bytesRead) break;
15448
+ length += bytesRead;
15449
+ }
15450
+ if (length > limit) throw new Error("bounded process read");
15451
+ return buffer.subarray(0, length);
15452
+ } finally {
15453
+ await handle.close();
15454
+ }
15455
+ }
15456
+ var productionIO = {
15457
+ platform: process.platform,
15458
+ uid: process.getuid?.() ?? -1,
15459
+ async process(pid) {
15460
+ const root = `/proc/${pid}`;
15461
+ const metadata = await stat6(root);
15462
+ const status = (await boundedFile(`${root}/status`, 32 * 1024)).toString("utf8");
15463
+ const uids = /^Uid:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$/m.exec(status);
15464
+ if (!uids || uids.slice(1).some((uid) => Number(uid) !== metadata.uid)) throw new Error("process owner");
15465
+ const raw = (await boundedFile(`${root}/stat`, 8192)).toString("utf8");
15466
+ const start = raw.slice(raw.lastIndexOf(")") + 2).split(" ")[19];
15467
+ if (!start || !/^\d+$/.test(start)) throw new Error("process start");
15468
+ return { uid: metadata.uid, start, cmdline: (await boundedFile(`${root}/cmdline`, 8192)).toString("utf8") };
15469
+ },
15470
+ environment: async (pid) => await boundedFile(`/proc/${pid}/environ`, ENV_LIMIT)
15471
+ };
15472
+ function nativeRoutineAuthEligible(deps) {
15473
+ return (deps.boxRoutineAuthIO ?? productionIO).platform === "linux" && (deps.transport === "auto" || deps.transport === "local") && !deps.gatewayServerUrl && !deps.daemonServerUrl && !deps.sshHost;
15474
+ }
15475
+ function validHost(value, uid) {
15476
+ const args = value.cmdline.split("\0");
15477
+ if (args.at(-1) === "") args.pop();
15478
+ return uid >= 0 && value.uid === uid && /^\d+$/.test(value.start) && args[0] === "/exec-daemon/node" && args.at(-1) === "/opt/sand/sand-host/host-main.cjs" && args.slice(1, -1).every((arg) => arg === "--disable-warning=ExperimentalWarning");
15479
+ }
15480
+ function sameDiscovery(a, b) {
15481
+ return a.pid === b.pid && a.startedAt === b.startedAt && a.baseUrl === b.baseUrl && a.token === b.token;
15482
+ }
15483
+ function failure(code) {
15484
+ return new CliError("credential_invalid", "Current-box Routine authentication could not be established safely.", { failureCode: code });
15485
+ }
15486
+ async function resolveBoxRoutineAuth(deps, timeoutMs) {
15487
+ if (!nativeRoutineAuthEligible(deps)) return null;
15488
+ if (deps.signal?.aborted) throw failure("request_cancelled");
15489
+ const io = deps.boxRoutineAuthIO ?? productionIO;
15490
+ let discovery;
15491
+ let environment;
15492
+ try {
15493
+ discovery = await readDiscovery(deps);
15494
+ if (!Number.isSafeInteger(discovery.pid) || discovery.pid <= 0 || !Number.isSafeInteger(discovery.startedAt) || discovery.startedAt <= 0) return null;
15495
+ const before = await io.process(discovery.pid);
15496
+ if (!validHost(before, io.uid)) return null;
15497
+ const healthy = await new GatewayClient({ ...deps, transport: "local" }).health(timeoutMs);
15498
+ if (!sameDiscovery(discovery, healthy.discovery) || healthy.health.pid !== discovery.pid || healthy.health.startedAt !== discovery.startedAt) return null;
15499
+ environment = await io.environment(discovery.pid);
15500
+ const after = await io.process(discovery.pid);
15501
+ if (!validHost(after, io.uid) || before.start !== after.start || before.cmdline !== after.cmdline || !sameDiscovery(discovery, await readDiscovery(deps))) return null;
15502
+ } catch {
15503
+ if (deps.signal?.aborted) throw failure("request_cancelled");
15504
+ return null;
15505
+ }
15506
+ if (environment.length > ENV_LIMIT) throw failure("renewal_credential_oversized");
15507
+ const entries = environment.toString("utf8").split("\0").filter((entry) => entry.startsWith("SAND_INFERENCE_RENEWAL_CREDENTIAL="));
15508
+ if (entries.length === 0) return null;
15509
+ const credential = entries[0].slice("SAND_INFERENCE_RENEWAL_CREDENTIAL=".length).trim();
15510
+ if (entries.length !== 1 || !credential || Buffer.byteLength(credential) > 16 * 1024 || /[\r\n]/.test(credential)) {
15511
+ throw failure("renewal_credential_invalid");
15512
+ }
15513
+ const metadata = {};
15514
+ for (const entry of environment.toString("utf8").split("\0")) {
15515
+ const separator = entry.indexOf("=");
15516
+ const key = entry.slice(0, separator);
15517
+ if (!["SAND_CLIENT_APP_VERSION", "SAND_BOX_OWNER_NAMESPACE", "SAND_BACKEND_URL", "CURSOR_API_BASE_URL", "SAND_PACKAGED", "SAND_LAB"].includes(key)) continue;
15518
+ const value = entry.slice(separator + 1);
15519
+ if (metadata[key] !== void 0 || value.length > 1024 || /[^\x20-\x7e]/.test(value)) throw failure("renewal_metadata_invalid");
15520
+ metadata[key] = value;
15521
+ }
15522
+ for (const key of ["SAND_BACKEND_URL", "CURSOR_API_BASE_URL"]) {
15523
+ const origin = metadata[key];
15524
+ if (origin && origin !== DEFAULT_SERVER_BACKEND_URL && origin !== `${DEFAULT_SERVER_BACKEND_URL}/`) throw failure("renewal_backend_unsupported");
15525
+ }
15526
+ const ownerNamespace = metadata.SAND_BOX_OWNER_NAMESPACE?.trim();
15527
+ if (metadata.SAND_PACKAGED !== "1" || metadata.SAND_LAB === "1" || ownerNamespace === "dev" || ownerNamespace === "lab") {
15528
+ throw failure("renewal_namespace_unsupported");
15529
+ }
15530
+ const namespace = "prod";
15531
+ const clientVersion = /^(\d+\.\d+\.\d+)(?:-.+)?$/.exec(metadata.SAND_CLIENT_APP_VERSION?.trim() ?? "")?.[1] ?? "0.1.0";
15532
+ const credentialScope = createHash9("sha256").update(JSON.stringify([
15533
+ "box-routine-auth-v1",
15534
+ DEFAULT_SERVER_BACKEND_URL,
15535
+ namespace,
15536
+ credential
15537
+ ])).digest("hex");
15538
+ const controller2 = new AbortController();
15539
+ const abort2 = () => controller2.abort();
15540
+ deps.signal?.addEventListener("abort", abort2, { once: true });
15541
+ const timer = setTimeout(abort2, timeoutMs);
15542
+ try {
15543
+ if (deps.signal?.aborted) controller2.abort();
15544
+ if (controller2.signal.aborted) throw failure("request_cancelled");
15545
+ const response = await deps.fetch(`${DEFAULT_SERVER_BACKEND_URL}/sand-box/inference-credential`, {
15546
+ method: "POST",
15547
+ headers: {
15548
+ "content-type": "application/json",
15549
+ "x-cursor-client-type": "sand",
15550
+ "x-cursor-client-source": "sand-desktop",
15551
+ "x-cursor-client-version": clientVersion,
15552
+ "x-sand-box-namespace": namespace
15553
+ },
15554
+ body: JSON.stringify({ credential }),
15555
+ redirect: "manual",
15556
+ cache: "no-store",
15557
+ signal: controller2.signal
15558
+ });
15559
+ if (response.redirected || !response.ok) {
15560
+ await response.body?.cancel().catch(() => void 0);
15561
+ throw failure(response.status >= 300 && response.status < 400 ? "renewal_redirect_refused" : response.status === 401 || response.status === 403 ? "renewal_unauthorized" : "renewal_unavailable");
15562
+ }
15563
+ const declared = response.headers.get("content-length");
15564
+ if (declared !== null && (!Number.isSafeInteger(Number(declared)) || Number(declared) < 0 || Number(declared) > RESPONSE_LIMIT)) {
15565
+ await response.body?.cancel().catch(() => void 0);
15566
+ throw failure("renewal_response_oversized");
15567
+ }
15568
+ const reader = response.body?.getReader();
15569
+ const chunks = [];
15570
+ let size = 0;
15571
+ try {
15572
+ if (reader) for (; ; ) {
15573
+ const next = await reader.read();
15574
+ if (next.done) break;
15575
+ size += next.value.byteLength;
15576
+ if (size > RESPONSE_LIMIT) {
15577
+ await reader.cancel().catch(() => void 0);
15578
+ throw failure("renewal_response_oversized");
15579
+ }
15580
+ chunks.push(Buffer.from(next.value));
15581
+ }
15582
+ } finally {
15583
+ reader?.releaseLock();
15584
+ }
15585
+ if (controller2.signal.aborted) throw failure("request_cancelled");
15586
+ const result = JSON.parse(Buffer.concat(chunks).toString("utf8"));
15587
+ let expiresAtMs = isRecord(result) ? result.expiresAtMs : void 0;
15588
+ if (expiresAtMs === void 0 && isRecord(result) && typeof result.accessToken === "string") {
15589
+ try {
15590
+ const payload = JSON.parse(Buffer.from(result.accessToken.split(".")[1] ?? "", "base64url").toString("utf8"));
15591
+ if (isRecord(payload) && typeof payload.exp === "number") expiresAtMs = payload.exp * 1e3;
15592
+ } catch {
15593
+ }
15594
+ }
15595
+ if (!isRecord(result) || typeof result.accessToken !== "string" || !result.accessToken || Buffer.byteLength(result.accessToken) > 16 * 1024 || /[\s\0]/.test(result.accessToken) || typeof expiresAtMs !== "number" || !Number.isSafeInteger(expiresAtMs) || expiresAtMs <= deps.now() + 3e4) {
15596
+ throw failure("renewal_response_invalid");
15597
+ }
15598
+ return { backendUrl: DEFAULT_SERVER_BACKEND_URL, token: result.accessToken, credentialScope };
15599
+ } catch (error) {
15600
+ if (controller2.signal.aborted) throw failure("request_cancelled");
15601
+ if (error instanceof CliError) throw error;
15602
+ throw failure("renewal_response_invalid");
15603
+ } finally {
15604
+ clearTimeout(timer);
15605
+ deps.signal?.removeEventListener("abort", abort2);
15606
+ }
15607
+ }
15608
+
14916
15609
  // src/commands/routines.ts
14917
15610
  function routineCliError(error) {
14918
15611
  if (error instanceof CliError) return error;
@@ -14984,7 +15677,8 @@ async function resolveRoutineAuthority(client2, agent, timeoutMs) {
14984
15677
  if (error instanceof CliError && ["gateway_not_found", "gateway_bad_request", "capability_unavailable"].includes(error.code)) {
14985
15678
  throw new CliError(
14986
15679
  "capability_unavailable",
14987
- "This Temporal Bot requires the Grokbox Temporal Routine bridge before Routine state can be read safely."
15680
+ "This Temporal Bot requires the Grokbox Temporal Routine bridge, or an explicit server.access_token_ref in the selected Profile, before Routine state can be read safely.",
15681
+ { failureCode: "routine_bridge_missing" }
14988
15682
  );
14989
15683
  }
14990
15684
  throw error;
@@ -15034,16 +15728,21 @@ function nativePrompts(value, catalog) {
15034
15728
  }
15035
15729
  return prompts;
15036
15730
  }
15037
- function findRoutine(snapshot, id) {
15038
- let rid;
15039
- try {
15040
- rid = routineId(id);
15041
- } catch (error) {
15042
- throw routineCliError(error);
15731
+ function findRoutine(snapshot, target, allowName = false) {
15732
+ const exact = snapshot.catalog.routines.find((routine) => routine.id === target);
15733
+ if (exact) return exact;
15734
+ if (!allowName) {
15735
+ try {
15736
+ routineId(target);
15737
+ } catch (error) {
15738
+ throw routineCliError(error);
15739
+ }
15740
+ throw routineCliError(new RoutineError("not_found_in_window"));
15043
15741
  }
15044
- const row = snapshot.catalog.routines.find((routine) => routine.id === rid);
15045
- if (!row) throw routineCliError(new RoutineError("not_found_in_window"));
15046
- return row;
15742
+ const named = snapshot.catalog.routines.filter((routine) => routine.name.toLocaleLowerCase() === target.toLocaleLowerCase());
15743
+ if (named.length > 1) throw new CliError("target_ambiguous", "Routine name matched more than one Routine.");
15744
+ if (!named[0]) throw routineCliError(new RoutineError("not_found_in_window"));
15745
+ return named[0];
15047
15746
  }
15048
15747
  function mutableRoutine(snapshot, id) {
15049
15748
  const row = findRoutine(snapshot, id);
@@ -15104,7 +15803,7 @@ async function listOrShow(deps, client2, action, agentTarget, routineTarget, raw
15104
15803
  writeSuccess(deps.stdout, { ...snapshot.catalog, authority: snapshot.authority }, snapshot.discovery);
15105
15804
  return;
15106
15805
  }
15107
- const routine = findRoutine(snapshot, routineTarget ?? "");
15806
+ const routine = findRoutine(snapshot, routineTarget ?? "", true);
15108
15807
  writeSuccess(
15109
15808
  deps.stdout,
15110
15809
  { schemaVersion: 1, agentId: agent.id, authority: snapshot.authority, routine, coverage: snapshot.catalog.coverage },
@@ -15297,10 +15996,13 @@ async function setRoutineEnabled(deps, client2, agentTarget, routineTarget, acti
15297
15996
  }
15298
15997
  async function showWebhookCredential(deps, client2, agentTarget, routineTarget, raw) {
15299
15998
  requireConfirmed(raw);
15999
+ if (raw.verify === true) {
16000
+ throw new CliError("capability_unavailable", "Webhook key verification requires an explicit cloud credential in the selected Profile; Host-provided credentials are not sent to additional endpoints.");
16001
+ }
15300
16002
  const io = ioFromOpts(raw);
15301
16003
  const agent = await resolveAgent(client2, agentTarget, io.timeoutMs);
15302
16004
  const snapshot = await readSnapshot(client2, agent, io.timeoutMs);
15303
- const routine = findRoutine(snapshot, routineTarget);
16005
+ const routine = findRoutine(snapshot, routineTarget, true);
15304
16006
  if (routine.trigger.type !== "webhook") {
15305
16007
  throw new CliError("capability_unavailable", "Routine is not webhook-triggered.");
15306
16008
  }
@@ -15380,7 +16082,66 @@ async function deleteRoutine(deps, client2, agentTarget, routineTarget, raw) {
15380
16082
  })
15381
16083
  }, after.discovery);
15382
16084
  }
16085
+ async function cloudRoutines(deps, action, agentTarget, routineTarget, raw, nativeAuth) {
16086
+ if (action === "webhook-credential") requireConfirmed(raw);
16087
+ const io = ioFromOpts(raw);
16088
+ const context = nativeAuth ? { ...nativeAuth, timeoutMs: io.timeoutMs } : {
16089
+ backendUrl: normalizedBackend(deps.serverBackendUrl ?? DEFAULT_SERVER_BACKEND_URL),
16090
+ token: await resolveSecretRef(deps, deps.serverAccessTokenRef),
16091
+ timeoutMs: io.timeoutMs
16092
+ };
16093
+ const agents = await listServerAgents(deps, context);
16094
+ const agent = findRosterRow(agents, agentTarget, ["agent"]);
16095
+ const agentId = asString(agent.id);
16096
+ const routines = await listServerRoutines(deps, { ...context, agentId });
16097
+ const coverage = { kind: "native_returned_window", limit: 100, atLimit: routines.length === 100, complete: false };
16098
+ const catalog = { schemaVersion: 1, agentId, routines, coverage };
16099
+ if (action === "list") {
16100
+ if (io.table) deps.stdout.write(listTable(catalog));
16101
+ else writeSuccess(deps.stdout, { ...catalog, authority: "server-temporal" });
16102
+ return;
16103
+ }
16104
+ let selected = routines.filter((routine2) => routine2.id === routineTarget || routine2.serverId === routineTarget);
16105
+ if (selected.length === 0) {
16106
+ selected = routines.filter((routine2) => routine2.name.toLocaleLowerCase() === (routineTarget ?? "").toLocaleLowerCase());
16107
+ }
16108
+ if (selected.length > 1) throw new CliError("target_ambiguous", "Routine target matched more than one Routine.");
16109
+ const routine = selected[0];
16110
+ if (!routine) throw new CliError("target_not_found", "No Routine matched that ID or name in the configured cloud account; no other identity was searched.");
16111
+ if (action === "show") {
16112
+ writeSuccess(deps.stdout, { schemaVersion: 1, agentId, authority: "server-temporal", routine, coverage });
16113
+ return;
16114
+ }
16115
+ if (routine.trigger.type !== "webhook") throw new CliError("capability_unavailable", "Routine is not webhook-triggered.");
16116
+ const key = await cachedWebhookKey(deps, { ...context, configDir: deps.configDir, agentId, routine });
16117
+ const keyLive = raw.verify === true ? await probeWebhookKey(deps, { ...context, key, serverId: routine.serverId }) : void 0;
16118
+ writeSuccess(deps.stdout, {
16119
+ schemaVersion: 1,
16120
+ agentId,
16121
+ routineId: routine.id,
16122
+ serverId: routine.serverId,
16123
+ authority: "server-temporal",
16124
+ url: routine.webhookUrl,
16125
+ keyPresent: true,
16126
+ keyIncluded: raw.revealKey === true,
16127
+ promptIncluded: false,
16128
+ ...keyLive === void 0 ? {} : { keyLive },
16129
+ ...raw.revealKey === true ? { key } : {}
16130
+ });
16131
+ }
15383
16132
  async function runRoutines(deps, action, agentTarget, routineTarget, raw) {
16133
+ if (deps.serverAccessTokenRef !== void 0 && (action === "list" || action === "show" || action === "webhook-credential")) {
16134
+ await cloudRoutines(deps, action, agentTarget, routineTarget, raw);
16135
+ return;
16136
+ }
16137
+ if (action === "webhook-credential") requireConfirmed(raw);
16138
+ if (action === "list" || action === "show" || action === "webhook-credential") {
16139
+ const nativeAuth = await resolveBoxRoutineAuth(deps, ioFromOpts(raw).timeoutMs);
16140
+ if (nativeAuth) {
16141
+ await cloudRoutines(deps, action, agentTarget, routineTarget, raw, nativeAuth);
16142
+ return;
16143
+ }
16144
+ }
15384
16145
  const client2 = new GatewayClient(deps);
15385
16146
  if (action === "list" || action === "show") {
15386
16147
  await listOrShow(deps, client2, action, agentTarget, routineTarget, raw);
@@ -15450,11 +16211,11 @@ async function runSend(deps, target, raw) {
15450
16211
  }
15451
16212
 
15452
16213
  // src/skills.ts
15453
- import { join as join11 } from "node:path";
16214
+ import { join as join12 } from "node:path";
15454
16215
  var CORE_SKILL_NAME = "core";
15455
16216
  var CORE_SKILL_SUMMARY = "Core grokbox usage guide.";
15456
16217
  async function loadCoreMarkdown(deps, full) {
15457
- const overview = await deps.readFile(join11(deps.skillsDir, "core.md"));
16218
+ const overview = await deps.readFile(join12(deps.skillsDir, "core.md"));
15458
16219
  if (!full) return overview.trimEnd() + "\n";
15459
16220
  return `${overview.trimEnd()}
15460
16221
 
@@ -15517,9 +16278,9 @@ var FAMILY_DESCRIPTIONS = {
15517
16278
  is: "Read state projections"
15518
16279
  };
15519
16280
  function publicCommanderMessage(error) {
15520
- const text = error.message.replace(/^error: /i, "").trim();
15521
- if (/bearer|authorization|token/i.test(text)) return "Invalid usage.";
15522
- return text.length > 0 ? text : "Invalid usage.";
16281
+ const text2 = error.message.replace(/^error: /i, "").trim();
16282
+ if (/bearer|authorization|token/i.test(text2)) return "Invalid usage.";
16283
+ return text2.length > 0 ? text2 : "Invalid usage.";
15523
16284
  }
15524
16285
  function unexpectedMessage(error) {
15525
16286
  if (error instanceof Error) {
@@ -15658,6 +16419,8 @@ function addLeaf(deps, program2, parents, leaf, action) {
15658
16419
  sandboxKeepaliveIntervalMs: profile.sandbox?.keepalive_interval_ms,
15659
16420
  quotaSource: profile.quota?.source,
15660
16421
  quotaAccessTokenRef: profile.quota?.access_token_ref,
16422
+ serverAccessTokenRef: profile.server?.access_token_ref,
16423
+ serverBackendUrl: profile.server?.backend_url,
15661
16424
  gatewayServerUrl: profile.gateway_url,
15662
16425
  gatewayTokenRef: profile.gateway_token_ref,
15663
16426
  gatewayHeadersRef: profile.gateway_headers_ref,