@astrasyncai/verification-gateway 5.17.2 → 5.18.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +1878 -0
  2. package/README.md +141 -7
  3. package/dist/adapters/express.d.mts +1 -1
  4. package/dist/adapters/express.d.ts +1 -1
  5. package/dist/adapters/express.js +30 -12
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +30 -12
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/kit-express.d.mts +63 -0
  10. package/dist/adapters/kit-express.d.ts +63 -0
  11. package/dist/adapters/kit-express.js +88 -0
  12. package/dist/adapters/kit-express.js.map +1 -0
  13. package/dist/adapters/kit-express.mjs +61 -0
  14. package/dist/adapters/kit-express.mjs.map +1 -0
  15. package/dist/adapters/mcp.d.mts +1 -1
  16. package/dist/adapters/mcp.d.ts +1 -1
  17. package/dist/adapters/mcp.js +1 -1
  18. package/dist/adapters/mcp.js.map +1 -1
  19. package/dist/adapters/mcp.mjs +1 -1
  20. package/dist/adapters/mcp.mjs.map +1 -1
  21. package/dist/adapters/nextjs.d.mts +1 -1
  22. package/dist/adapters/nextjs.d.ts +1 -1
  23. package/dist/adapters/nextjs.js +18 -8
  24. package/dist/adapters/nextjs.js.map +1 -1
  25. package/dist/adapters/nextjs.mjs +18 -8
  26. package/dist/adapters/nextjs.mjs.map +1 -1
  27. package/dist/adapters/sdk.d.mts +1 -1
  28. package/dist/adapters/sdk.d.ts +1 -1
  29. package/dist/adapters/sdk.js +1 -1
  30. package/dist/adapters/sdk.js.map +1 -1
  31. package/dist/adapters/sdk.mjs +1 -1
  32. package/dist/adapters/sdk.mjs.map +1 -1
  33. package/dist/agent/index.js +1 -1
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/index.mjs +1 -1
  36. package/dist/agent/index.mjs.map +1 -1
  37. package/dist/bin/astrasync-claude-hook.js +1 -1
  38. package/dist/bin/astrasync-codex-hook.js +1 -1
  39. package/dist/bin/astrasync-guard.js +330 -112
  40. package/dist/bin/astrasync.js +359 -113
  41. package/dist/browser/background.js +1 -1
  42. package/dist/browser/background.js.map +1 -1
  43. package/dist/browser/background.mjs +1 -1
  44. package/dist/browser/background.mjs.map +1 -1
  45. package/dist/cli/index.js +1 -1
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/cli/index.mjs +1 -1
  48. package/dist/cli/index.mjs.map +1 -1
  49. package/dist/codex/index.js +1 -1
  50. package/dist/codex/index.js.map +1 -1
  51. package/dist/codex/index.mjs +1 -1
  52. package/dist/codex/index.mjs.map +1 -1
  53. package/dist/cursor/extension.js +1 -1
  54. package/dist/cursor/extension.js.map +1 -1
  55. package/dist/cursor/extension.mjs +1 -1
  56. package/dist/cursor/extension.mjs.map +1 -1
  57. package/dist/edge-config.d.mts +1 -1
  58. package/dist/edge-config.d.ts +1 -1
  59. package/dist/edge-config.js +1 -1
  60. package/dist/edge-config.js.map +1 -1
  61. package/dist/edge-config.mjs +1 -1
  62. package/dist/edge-config.mjs.map +1 -1
  63. package/dist/edge-core/index.d.mts +5 -4
  64. package/dist/edge-core/index.d.ts +5 -4
  65. package/dist/edge-core/index.js +18 -8
  66. package/dist/edge-core/index.js.map +1 -1
  67. package/dist/edge-core/index.mjs +18 -8
  68. package/dist/edge-core/index.mjs.map +1 -1
  69. package/dist/gateway/gateway.js +1 -1
  70. package/dist/gateway/gateway.js.map +1 -1
  71. package/dist/gateway/gateway.mjs +1 -1
  72. package/dist/gateway/gateway.mjs.map +1 -1
  73. package/dist/git-trigger/git-hooks.d.mts +1 -1
  74. package/dist/git-trigger/git-hooks.d.ts +1 -1
  75. package/dist/index.d.mts +391 -13
  76. package/dist/index.d.ts +391 -13
  77. package/dist/index.js +766 -230
  78. package/dist/index.js.map +1 -1
  79. package/dist/index.mjs +749 -222
  80. package/dist/index.mjs.map +1 -1
  81. package/dist/registration/index.d.mts +1 -1
  82. package/dist/registration/index.d.ts +1 -1
  83. package/dist/registration/index.js +1 -1
  84. package/dist/registration/index.js.map +1 -1
  85. package/dist/registration/index.mjs +1 -1
  86. package/dist/registration/index.mjs.map +1 -1
  87. package/dist/transport/index.d.mts +4 -3
  88. package/dist/transport/index.d.ts +4 -3
  89. package/dist/transport/index.js +1 -1
  90. package/dist/transport/index.js.map +1 -1
  91. package/dist/transport/index.mjs +1 -1
  92. package/dist/transport/index.mjs.map +1 -1
  93. package/dist/{types-vNBvYD7Z.d.mts → types-3ObBpsnf.d.mts} +7 -0
  94. package/dist/{types-DA8LzAOB.d.ts → types-BoxZEYPS.d.ts} +7 -0
  95. package/dist/{types-DRb_plOh.d.mts → types-Cns4l8gX.d.mts} +17 -0
  96. package/dist/{types-DRb_plOh.d.ts → types-Cns4l8gX.d.ts} +17 -0
  97. package/dist/ui/index.d.mts +1 -1
  98. package/dist/ui/index.d.ts +1 -1
  99. package/dist/verify.d.mts +1 -1
  100. package/dist/verify.d.ts +1 -1
  101. package/dist/verify.js +1 -1
  102. package/dist/verify.js.map +1 -1
  103. package/dist/verify.mjs +1 -1
  104. package/dist/verify.mjs.map +1 -1
  105. package/package.json +8 -2
@@ -36,7 +36,7 @@ var SDK_VERSION;
36
36
  var init_version = __esm({
37
37
  "src/version.ts"() {
38
38
  "use strict";
39
- SDK_VERSION = "5.17.2";
39
+ SDK_VERSION = "5.18.1";
40
40
  }
41
41
  });
42
42
 
@@ -3474,115 +3474,115 @@ function validatePolicy(policy) {
3474
3474
  }
3475
3475
  return { valid: errors.length === 0, errors };
3476
3476
  }
3477
- function validatePurpose(purpose, path2) {
3477
+ function validatePurpose(purpose, path3) {
3478
3478
  const errors = [];
3479
3479
  if (!purpose || typeof purpose !== "object") {
3480
- return [{ path: path2, message: "purpose must be an object" }];
3480
+ return [{ path: path3, message: "purpose must be an object" }];
3481
3481
  }
3482
3482
  const p = purpose;
3483
3483
  if (typeof p.id !== "string" || !p.id) {
3484
- errors.push({ path: `${path2}.id`, message: "id is required and must be a non-empty string" });
3484
+ errors.push({ path: `${path3}.id`, message: "id is required and must be a non-empty string" });
3485
3485
  }
3486
3486
  if (typeof p.allowed !== "boolean") {
3487
- errors.push({ path: `${path2}.allowed`, message: "allowed is required and must be a boolean" });
3487
+ errors.push({ path: `${path3}.allowed`, message: "allowed is required and must be a boolean" });
3488
3488
  }
3489
3489
  if (p.targets !== void 0 && !isStringArray(p.targets)) {
3490
- errors.push({ path: `${path2}.targets`, message: "targets must be an array of strings" });
3490
+ errors.push({ path: `${path3}.targets`, message: "targets must be an array of strings" });
3491
3491
  }
3492
3492
  if (p.blockedPatterns !== void 0 && !isStringArray(p.blockedPatterns)) {
3493
3493
  errors.push({
3494
- path: `${path2}.blockedPatterns`,
3494
+ path: `${path3}.blockedPatterns`,
3495
3495
  message: "blockedPatterns must be an array of strings"
3496
3496
  });
3497
3497
  }
3498
3498
  if (p.requiresApproval !== void 0 && typeof p.requiresApproval !== "boolean") {
3499
3499
  errors.push({
3500
- path: `${path2}.requiresApproval`,
3500
+ path: `${path3}.requiresApproval`,
3501
3501
  message: "requiresApproval must be a boolean"
3502
3502
  });
3503
3503
  }
3504
3504
  return errors;
3505
3505
  }
3506
- function validateScope(scope, path2) {
3506
+ function validateScope(scope, path3) {
3507
3507
  const errors = [];
3508
3508
  if (typeof scope !== "object" || scope === null) {
3509
- return [{ path: path2, message: "scope must be an object" }];
3509
+ return [{ path: path3, message: "scope must be an object" }];
3510
3510
  }
3511
3511
  const s = scope;
3512
3512
  for (const field of ["allowedDomains", "blockedDomains", "blockedResources"]) {
3513
3513
  if (s[field] !== void 0 && !isStringArray(s[field])) {
3514
- errors.push({ path: `${path2}.${field}`, message: `${field} must be an array of strings` });
3514
+ errors.push({ path: `${path3}.${field}`, message: `${field} must be an array of strings` });
3515
3515
  }
3516
3516
  }
3517
3517
  return errors;
3518
3518
  }
3519
- function validateLimits(limits, path2) {
3519
+ function validateLimits(limits, path3) {
3520
3520
  const errors = [];
3521
3521
  if (typeof limits !== "object" || limits === null) {
3522
- return [{ path: path2, message: "limits must be an object" }];
3522
+ return [{ path: path3, message: "limits must be an object" }];
3523
3523
  }
3524
3524
  const l = limits;
3525
3525
  if (l.autonomousThreshold !== void 0 && typeof l.autonomousThreshold !== "number") {
3526
3526
  errors.push({
3527
- path: `${path2}.autonomousThreshold`,
3527
+ path: `${path3}.autonomousThreshold`,
3528
3528
  message: "autonomousThreshold must be a number"
3529
3529
  });
3530
3530
  }
3531
3531
  if (l.maxTransactionAmount !== void 0 && typeof l.maxTransactionAmount !== "number") {
3532
3532
  errors.push({
3533
- path: `${path2}.maxTransactionAmount`,
3533
+ path: `${path3}.maxTransactionAmount`,
3534
3534
  message: "maxTransactionAmount must be a number"
3535
3535
  });
3536
3536
  }
3537
3537
  if (typeof l.autonomousThreshold === "number" && typeof l.maxTransactionAmount === "number" && l.autonomousThreshold > l.maxTransactionAmount) {
3538
3538
  errors.push({
3539
- path: `${path2}.autonomousThreshold`,
3539
+ path: `${path3}.autonomousThreshold`,
3540
3540
  message: "autonomousThreshold (Autonomous Limit) must be <= maxTransactionAmount (Hard Limit)"
3541
3541
  });
3542
3542
  }
3543
3543
  if (l.maxRequestsPerHour !== void 0 && typeof l.maxRequestsPerHour !== "number") {
3544
3544
  errors.push({
3545
- path: `${path2}.maxRequestsPerHour`,
3545
+ path: `${path3}.maxRequestsPerHour`,
3546
3546
  message: "maxRequestsPerHour must be a number"
3547
3547
  });
3548
3548
  }
3549
3549
  if (l.currency !== void 0 && typeof l.currency !== "string") {
3550
- errors.push({ path: `${path2}.currency`, message: "currency must be a string" });
3550
+ errors.push({ path: `${path3}.currency`, message: "currency must be a string" });
3551
3551
  }
3552
3552
  return errors;
3553
3553
  }
3554
- function validateRiskThresholds(thresholds, path2) {
3554
+ function validateRiskThresholds(thresholds, path3) {
3555
3555
  const errors = [];
3556
3556
  if (typeof thresholds !== "object" || thresholds === null) {
3557
- return [{ path: path2, message: "riskThresholds must be an object" }];
3557
+ return [{ path: path3, message: "riskThresholds must be an object" }];
3558
3558
  }
3559
3559
  const t = thresholds;
3560
3560
  for (const range of ["autoAllow", "requireApproval", "autoBlock"]) {
3561
3561
  if (!t[range] || typeof t[range] !== "object") {
3562
3562
  errors.push({
3563
- path: `${path2}.${range}`,
3563
+ path: `${path3}.${range}`,
3564
3564
  message: `${range} is required and must be an object with min and max`
3565
3565
  });
3566
3566
  continue;
3567
3567
  }
3568
3568
  const r = t[range];
3569
3569
  if (typeof r.min !== "number" || typeof r.max !== "number") {
3570
- errors.push({ path: `${path2}.${range}`, message: `${range} must have numeric min and max` });
3570
+ errors.push({ path: `${path3}.${range}`, message: `${range} must have numeric min and max` });
3571
3571
  } else if (r.min > r.max) {
3572
- errors.push({ path: `${path2}.${range}`, message: `${range}.min must be <= ${range}.max` });
3572
+ errors.push({ path: `${path3}.${range}`, message: `${range}.min must be <= ${range}.max` });
3573
3573
  }
3574
3574
  }
3575
3575
  if (isValidThresholdShape(t)) {
3576
3576
  const thresholdObj = t;
3577
3577
  if (thresholdObj.autoAllow.max + 1 !== thresholdObj.requireApproval.min) {
3578
3578
  errors.push({
3579
- path: `${path2}`,
3579
+ path: `${path3}`,
3580
3580
  message: `Gap between autoAllow.max (${thresholdObj.autoAllow.max}) and requireApproval.min (${thresholdObj.requireApproval.min}). Ranges must be contiguous.`
3581
3581
  });
3582
3582
  }
3583
3583
  if (thresholdObj.requireApproval.max + 1 !== thresholdObj.autoBlock.min) {
3584
3584
  errors.push({
3585
- path: `${path2}`,
3585
+ path: `${path3}`,
3586
3586
  message: `Gap between requireApproval.max (${thresholdObj.requireApproval.max}) and autoBlock.min (${thresholdObj.autoBlock.min}). Ranges must be contiguous.`
3587
3587
  });
3588
3588
  }
@@ -3962,8 +3962,8 @@ ${category.label}
3962
3962
  async runAndSave(outputPath) {
3963
3963
  const policy = await this.run();
3964
3964
  const yaml2 = toYaml(policy);
3965
- const fs2 = await import("fs");
3966
- fs2.writeFileSync(outputPath, yaml2, "utf-8");
3965
+ const fs3 = await import("fs");
3966
+ fs3.writeFileSync(outputPath, yaml2, "utf-8");
3967
3967
  this.print(`
3968
3968
  Policy saved to ${outputPath}`);
3969
3969
  this.print(" AstraSync Local Guard is now protecting your agent.\n");
@@ -6644,6 +6644,13 @@ var init_report_amount = __esm({
6644
6644
  }
6645
6645
  });
6646
6646
 
6647
+ // src/ucp/kit-shared.ts
6648
+ var init_kit_shared = __esm({
6649
+ "src/ucp/kit-shared.ts"() {
6650
+ "use strict";
6651
+ }
6652
+ });
6653
+
6647
6654
  // src/ucp/checkout-kit.ts
6648
6655
  var import_jose3;
6649
6656
  var init_checkout_kit = __esm({
@@ -6659,6 +6666,7 @@ var init_checkout_kit = __esm({
6659
6666
  init_profile();
6660
6667
  init_signatures();
6661
6668
  init_report_amount();
6669
+ init_kit_shared();
6662
6670
  }
6663
6671
  });
6664
6672
 
@@ -6733,6 +6741,7 @@ var init_checkout_kit2 = __esm({
6733
6741
  "use strict";
6734
6742
  init_idempotency();
6735
6743
  init_report_amount();
6744
+ init_kit_shared();
6736
6745
  }
6737
6746
  });
6738
6747
 
@@ -6744,6 +6753,14 @@ var init_acp2 = __esm({
6744
6753
  }
6745
6754
  });
6746
6755
 
6756
+ // src/adapters/kit-express.ts
6757
+ var init_kit_express = __esm({
6758
+ "src/adapters/kit-express.ts"() {
6759
+ "use strict";
6760
+ init_kit_shared();
6761
+ }
6762
+ });
6763
+
6747
6764
  // src/http-signatures/index.ts
6748
6765
  var init_http_signatures = __esm({
6749
6766
  "src/http-signatures/index.ts"() {
@@ -6790,6 +6807,7 @@ var init_src = __esm({
6790
6807
  init_payments();
6791
6808
  init_aep();
6792
6809
  init_acp2();
6810
+ init_kit_express();
6793
6811
  init_mandates();
6794
6812
  init_http_signatures();
6795
6813
  init_idempotency();
@@ -7807,29 +7825,29 @@ function loadEnrolment(paths) {
7807
7825
  [paths.machineDevice, "machine"],
7808
7826
  [paths.userDevice, "user"]
7809
7827
  ];
7810
- for (const [path2, scope] of candidates) {
7811
- if (!(0, import_fs6.existsSync)(path2)) continue;
7828
+ for (const [path3, scope] of candidates) {
7829
+ if (!(0, import_fs6.existsSync)(path3)) continue;
7812
7830
  try {
7813
- const parsed = JSON.parse((0, import_fs6.readFileSync)(path2, "utf-8"));
7831
+ const parsed = JSON.parse((0, import_fs6.readFileSync)(path3, "utf-8"));
7814
7832
  if (isEnrolment(parsed)) return { enrolment: parsed, scope };
7815
7833
  } catch {
7816
7834
  }
7817
- return { corrupt: true, path: path2 };
7835
+ return { corrupt: true, path: path3 };
7818
7836
  }
7819
7837
  return null;
7820
7838
  }
7821
- function writeFileMode(path2, content, mode) {
7822
- (0, import_fs6.mkdirSync)((0, import_path5.dirname)(path2), { recursive: true, mode: 448 });
7823
- (0, import_fs6.writeFileSync)(path2, content, { encoding: "utf-8", mode });
7839
+ function writeFileMode(path3, content, mode) {
7840
+ (0, import_fs6.mkdirSync)((0, import_path5.dirname)(path3), { recursive: true, mode: 448 });
7841
+ (0, import_fs6.writeFileSync)(path3, content, { encoding: "utf-8", mode });
7824
7842
  try {
7825
- (0, import_fs6.chmodSync)(path2, mode);
7843
+ (0, import_fs6.chmodSync)(path3, mode);
7826
7844
  } catch {
7827
7845
  }
7828
7846
  }
7829
7847
  function saveEnrolment(paths, enrolment, scope) {
7830
- const path2 = scope === "machine" ? paths.machineDevice : paths.userDevice;
7831
- writeFileMode(path2, JSON.stringify(enrolment, null, 2), scope === "machine" ? 420 : 384);
7832
- return path2;
7848
+ const path3 = scope === "machine" ? paths.machineDevice : paths.userDevice;
7849
+ writeFileMode(path3, JSON.stringify(enrolment, null, 2), scope === "machine" ? 420 : 384);
7850
+ return path3;
7833
7851
  }
7834
7852
  function readCachedPolicies(paths) {
7835
7853
  const out = [];
@@ -8157,12 +8175,12 @@ function formatFleetStatus(status) {
8157
8175
  if (status.lastError) lines.push(` Last sync error: ${status.lastError}`);
8158
8176
  return lines;
8159
8177
  }
8160
- function managedHookCommand(target, os2) {
8178
+ function managedHookCommand(target, os3) {
8161
8179
  const bin = target === "claude" ? "astrasync-claude-hook" : "astrasync-codex-hook";
8162
- if (os2 === "win32") return `"C:\\Program Files\\nodejs\\${bin}.cmd"`;
8180
+ if (os3 === "win32") return `"C:\\Program Files\\nodejs\\${bin}.cmd"`;
8163
8181
  return `/usr/local/bin/${bin}`;
8164
8182
  }
8165
- function buildClaudeManagedSettings(os2) {
8183
+ function buildClaudeManagedSettings(os3) {
8166
8184
  return {
8167
8185
  allowManagedHooksOnly: true,
8168
8186
  env: { ASTRASYNC_FLEET_REQUIRED: "1" },
@@ -8170,7 +8188,7 @@ function buildClaudeManagedSettings(os2) {
8170
8188
  PreToolUse: [
8171
8189
  {
8172
8190
  matcher: HOOK_MATCHER,
8173
- hooks: [{ type: "command", command: managedHookCommand("claude", os2), timeout: 10 }]
8191
+ hooks: [{ type: "command", command: managedHookCommand("claude", os3), timeout: 10 }]
8174
8192
  }
8175
8193
  ]
8176
8194
  },
@@ -8180,11 +8198,11 @@ function buildClaudeManagedSettings(os2) {
8180
8198
  function tomlString(value) {
8181
8199
  return JSON.stringify(value);
8182
8200
  }
8183
- function buildCodexRequirementsToml(os2) {
8184
- const command = os2 === "win32" ? `set ASTRASYNC_FLEET_REQUIRED=1&& ${managedHookCommand("codex", os2)}` : `ASTRASYNC_FLEET_REQUIRED=1 ${managedHookCommand("codex", os2)}`;
8201
+ function buildCodexRequirementsToml(os3) {
8202
+ const command = os3 === "win32" ? `set ASTRASYNC_FLEET_REQUIRED=1&& ${managedHookCommand("codex", os3)}` : `ASTRASYNC_FLEET_REQUIRED=1 ${managedHookCommand("codex", os3)}`;
8185
8203
  return [
8186
8204
  "# AstraSync Local Guard \u2014 managed hook (generated by `astrasync guard managed-settings --codex`)",
8187
- `# Install at: ${MANAGED_SETTINGS_LOCATIONS.codex[os2]}`,
8205
+ `# Install at: ${MANAGED_SETTINGS_LOCATIONS.codex[os3]}`,
8188
8206
  "allow_managed_hooks_only = true",
8189
8207
  "",
8190
8208
  "[features]",
@@ -8204,14 +8222,14 @@ function buildCodexRequirementsToml(os2) {
8204
8222
  function buildManagedSettingsFiles(targets) {
8205
8223
  const out = [];
8206
8224
  for (const target of targets) {
8207
- for (const os2 of ["darwin", "linux", "win32"]) {
8208
- const content = target === "claude" ? `${JSON.stringify(buildClaudeManagedSettings(os2), null, 2)}
8209
- ` : buildCodexRequirementsToml(os2);
8225
+ for (const os3 of ["darwin", "linux", "win32"]) {
8226
+ const content = target === "claude" ? `${JSON.stringify(buildClaudeManagedSettings(os3), null, 2)}
8227
+ ` : buildCodexRequirementsToml(os3);
8210
8228
  out.push({
8211
8229
  target,
8212
- os: os2,
8213
- installPath: MANAGED_SETTINGS_LOCATIONS[target][os2],
8214
- fileName: `${target}-${os2}-${target === "claude" ? "managed-settings.json" : "requirements.toml"}`,
8230
+ os: os3,
8231
+ installPath: MANAGED_SETTINGS_LOCATIONS[target][os3],
8232
+ fileName: `${target}-${os3}-${target === "claude" ? "managed-settings.json" : "requirements.toml"}`,
8215
8233
  content
8216
8234
  });
8217
8235
  }
@@ -8417,7 +8435,8 @@ function createLocalCert(hosts, days = 30) {
8417
8435
  ${b64}
8418
8436
  -----END CERTIFICATE-----
8419
8437
  `,
8420
- key: privateKey.export({ type: "pkcs8", format: "pem" }).toString()
8438
+ key: privateKey.export({ type: "pkcs8", format: "pem" }).toString(),
8439
+ spkiSha256: import_node_crypto5.default.createHash("sha256").update(spki).digest("base64")
8421
8440
  };
8422
8441
  }
8423
8442
  var import_node_crypto5, tlv, seq2, set2, ctx, ECDSA_SHA256;
@@ -8433,16 +8452,204 @@ var init_local_cert = __esm({
8433
8452
  }
8434
8453
  });
8435
8454
 
8455
+ // src/fleet/open-browser.ts
8456
+ function isWsl(env = process.env, platform2 = process.platform, readVersion = () => import_node_fs.default.readFileSync("/proc/version", "utf8")) {
8457
+ if (platform2 !== "linux") return false;
8458
+ if (env.WSL_DISTRO_NAME) return true;
8459
+ try {
8460
+ return /microsoft/i.test(readVersion());
8461
+ } catch {
8462
+ return false;
8463
+ }
8464
+ }
8465
+ function findChromium(env = process.env, platform2 = process.platform, exists = (p) => import_node_fs.default.existsSync(p), wsl = isWsl(env, platform2), listDir = (dir) => import_node_fs.default.readdirSync(dir)) {
8466
+ let candidates;
8467
+ if (platform2 === "darwin") {
8468
+ candidates = MAC_PATHS;
8469
+ } else if (platform2 === "win32") {
8470
+ const bases = [
8471
+ env.ProgramFiles,
8472
+ env["ProgramFiles(x86)"],
8473
+ env.LOCALAPPDATA ?? env.LocalAppData
8474
+ ].filter((b) => !!b);
8475
+ candidates = [
8476
+ ...bases.map((b) => import_node_path.default.win32.join(b, "Google", "Chrome", "Application", "chrome.exe")),
8477
+ ...bases.map((b) => import_node_path.default.win32.join(b, "Microsoft", "Edge", "Application", "msedge.exe"))
8478
+ ];
8479
+ } else {
8480
+ const dirs = (env.PATH ?? "").split(":").filter(Boolean);
8481
+ candidates = LINUX_NAMES.flatMap((n) => dirs.map((d) => import_node_path.default.posix.join(d, n)));
8482
+ const cache2 = env.PLAYWRIGHT_BROWSERS_PATH ?? (env.HOME ? `${env.HOME}/.cache/ms-playwright` : "");
8483
+ if (cache2) {
8484
+ let builds = [];
8485
+ try {
8486
+ builds = listDir(cache2).filter((d) => /^chromium-\d+$/.test(d));
8487
+ } catch {
8488
+ }
8489
+ builds.sort((a, b) => Number(b.split("-")[1]) - Number(a.split("-")[1]));
8490
+ for (const b of builds)
8491
+ candidates.push(
8492
+ import_node_path.default.posix.join(cache2, b, "chrome-linux64", "chrome"),
8493
+ import_node_path.default.posix.join(cache2, b, "chrome-linux", "chrome")
8494
+ );
8495
+ }
8496
+ if (wsl) candidates = [...candidates, ...WSL_WINDOWS_PATHS];
8497
+ }
8498
+ return candidates.find((c) => exists(c)) ?? null;
8499
+ }
8500
+ function isWindowsExeFromWsl(browserPath) {
8501
+ return /^\/mnt\/[a-z]\//i.test(browserPath) && /\.exe$/i.test(browserPath);
8502
+ }
8503
+ function browserName(browserPath) {
8504
+ const b = browserPath.toLowerCase();
8505
+ if (b.includes("edge")) return "Edge";
8506
+ if (b.includes("chromium")) return "Chromium";
8507
+ return "Chrome";
8508
+ }
8509
+ function profileDirFor(proxyPort) {
8510
+ return import_node_path.default.join(import_node_os.default.tmpdir(), `astrasync-agent-browser-${proxyPort}`);
8511
+ }
8512
+ function windowsProfileDir(profileDir) {
8513
+ if (/^\/mnt\/[a-z]\//i.test(profileDir)) {
8514
+ try {
8515
+ return (0, import_node_child_process.execFileSync)("wslpath", ["-w", profileDir], { encoding: "utf8" }).trim();
8516
+ } catch {
8517
+ }
8518
+ }
8519
+ return `C:\\Temp\\${import_node_path.default.basename(profileDir)}`;
8520
+ }
8521
+ function certFlag(certSpki) {
8522
+ return `--ignore-certificate-errors-spki-list=${certSpki}`;
8523
+ }
8524
+ function launchBrowser(browserPath, proxyPort, url, certSpki, profileDir = profileDirFor(proxyPort), onError) {
8525
+ const dir = isWindowsExeFromWsl(browserPath) ? windowsProfileDir(profileDir) : profileDir;
8526
+ const child = (0, import_node_child_process.spawn)(
8527
+ browserPath,
8528
+ [
8529
+ `--proxy-server=http://127.0.0.1:${proxyPort}`,
8530
+ certFlag(certSpki),
8531
+ `--user-data-dir=${dir}`,
8532
+ "--no-first-run",
8533
+ "--new-window",
8534
+ url
8535
+ ],
8536
+ { detached: true, stdio: "ignore" }
8537
+ );
8538
+ child.on("error", (err) => onError?.(err));
8539
+ child.unref();
8540
+ return child;
8541
+ }
8542
+ function browserCommand(platform2, port, url, certSpki, wsl = false) {
8543
+ const proxy = `--proxy-server="http://127.0.0.1:${port}" ${certFlag(certSpki)}`;
8544
+ if (platform2 === "win32" || wsl) {
8545
+ return `& "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" ${proxy} --user-data-dir="$env:TEMP\\astrasync-agent-${port}" '${url}'`;
8546
+ }
8547
+ const exe = platform2 === "darwin" ? '"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"' : "google-chrome";
8548
+ return `${exe} ${proxy} --user-data-dir="\${TMPDIR:-/tmp}/astrasync-agent-${port}" '${url}'`;
8549
+ }
8550
+ function browserCommandLines(platform2, port, url, certSpki, wsl = isWsl(process.env, platform2)) {
8551
+ if (wsl) {
8552
+ return [
8553
+ " In PowerShell (Windows):",
8554
+ ` ${browserCommand("win32", port, url, certSpki)}`,
8555
+ " Or inside WSL (with Chromium installed):",
8556
+ ` ${browserCommand("linux", port, url, certSpki)}`,
8557
+ ` ${CERT_FLAG_NOTE}`
8558
+ ];
8559
+ }
8560
+ return [` ${browserCommand(platform2, port, url, certSpki)}`, ` ${CERT_FLAG_NOTE}`];
8561
+ }
8562
+ function openThroughProxy(opts) {
8563
+ const log = opts.log ?? ((l) => console.log(l));
8564
+ const platform2 = opts.platform ?? process.platform;
8565
+ const env = opts.env ?? process.env;
8566
+ const wsl = opts.wsl ?? isWsl(env, platform2);
8567
+ const found = opts.find ? opts.find() : findChromium(env, platform2, void 0, wsl);
8568
+ if (!found) {
8569
+ log("No Chrome/Edge/Chromium found. Start one through the proxy with:");
8570
+ for (const l of browserCommandLines(platform2, opts.port, opts.url, opts.certSpki, wsl)) log(l);
8571
+ return false;
8572
+ }
8573
+ (opts.launch ?? launchBrowser)(
8574
+ found,
8575
+ opts.port,
8576
+ opts.url,
8577
+ opts.certSpki,
8578
+ profileDirFor(opts.port),
8579
+ (err) => log(`Couldn't start ${browserName(found)}: ${err.message}`)
8580
+ );
8581
+ log(`Opened ${browserName(found)} through the proxy.`);
8582
+ log(CERT_FLAG_NOTE);
8583
+ if (wsl && isWindowsExeFromWsl(found)) {
8584
+ log(
8585
+ "If the Windows browser can't reach the proxy, install Chromium inside WSL (e.g. npx playwright install chromium)."
8586
+ );
8587
+ }
8588
+ return true;
8589
+ }
8590
+ var import_node_child_process, import_node_fs, import_node_os, import_node_path, MAC_PATHS, LINUX_NAMES, WSL_WINDOWS_PATHS, CERT_FLAG_NOTE;
8591
+ var init_open_browser = __esm({
8592
+ "src/fleet/open-browser.ts"() {
8593
+ "use strict";
8594
+ import_node_child_process = require("child_process");
8595
+ import_node_fs = __toESM(require("fs"));
8596
+ import_node_os = __toESM(require("os"));
8597
+ import_node_path = __toESM(require("path"));
8598
+ MAC_PATHS = [
8599
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
8600
+ "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
8601
+ "/Applications/Chromium.app/Contents/MacOS/Chromium"
8602
+ ];
8603
+ LINUX_NAMES = [
8604
+ "google-chrome",
8605
+ "google-chrome-stable",
8606
+ "chromium",
8607
+ "chromium-browser",
8608
+ "microsoft-edge"
8609
+ ];
8610
+ WSL_WINDOWS_PATHS = [
8611
+ "/mnt/c/Program Files/Google/Chrome/Application/chrome.exe",
8612
+ "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
8613
+ "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe",
8614
+ "/mnt/c/Program Files/Microsoft/Edge/Application/msedge.exe"
8615
+ ];
8616
+ CERT_FLAG_NOTE = "Chrome shows a warning bar about the --ignore-certificate-errors-spki-list flag. That is expected (it trusts only this proxy's certificate); close the bar.";
8617
+ }
8618
+ });
8619
+
8436
8620
  // src/fleet/browse-proxy.ts
8437
8621
  function cleanHeaders(h) {
8438
8622
  const out = {};
8439
8623
  for (const [k, v] of Object.entries(h)) if (!HOP_BY_HOP.has(k) && v !== void 0) out[k] = v;
8440
8624
  return out;
8441
8625
  }
8626
+ function helpPage(port, certSpki) {
8627
+ const proxy = `http://127.0.0.1:${port}`;
8628
+ return `<!doctype html>
8629
+ <html lang="en"><head><meta charset="utf-8"><title>AstraSync device proxy</title>
8630
+ <style>body{font:16px/1.5 system-ui,sans-serif;max-width:40rem;margin:2rem auto;padding:0 1rem}code{background:#f2f2f2;padding:0 .25rem}</style>
8631
+ </head><body>
8632
+ <h1>AstraSync device proxy</h1>
8633
+ <p>This is AstraSync's Local Guard device proxy. It only works as a browser proxy &mdash; it isn't a page to open.</p>
8634
+ <h2>Open the sign-in link</h2>
8635
+ <ul>
8636
+ <li>Run <code>astrasync guard browse --open</code> &mdash; it opens a browser that goes through this proxy.</li>
8637
+ <li>Or start Chrome, Edge or Chromium with <code>--proxy-server=${proxy} ${certFlag(certSpki)}</code> and a separate profile (<code>--user-data-dir=&lt;an empty folder&gt;</code>), then open the sign-in link in it.</li>
8638
+ </ul>
8639
+ <p>Chrome shows a warning bar about that flag. That is expected &mdash; it trusts only this proxy&apos;s certificate &mdash; so close the bar.</p>
8640
+ <p>Keep <code>astrasync guard browse</code> running while you use the session.</p>
8641
+ </body></html>
8642
+ `;
8643
+ }
8442
8644
  async function startBrowseProxy(opts) {
8443
8645
  const hosts = new Set(opts.hosts.map((h) => h.toLowerCase()));
8444
8646
  const deviceHeader = `Device ${opts.deviceCredential}`;
8445
- const { cert, key } = createLocalCert([...hosts]);
8647
+ const { cert, key, spkiSha256 } = createLocalCert([...hosts]);
8648
+ const sockets = /* @__PURE__ */ new Set();
8649
+ const track = (s) => {
8650
+ sockets.add(s);
8651
+ s.once("close", () => sockets.delete(s));
8652
+ };
8446
8653
  function forward(req, res, target) {
8447
8654
  const headers = cleanHeaders(req.headers);
8448
8655
  headers["x-astra-device"] = deviceHeader;
@@ -8494,6 +8701,7 @@ async function startBrowseProxy(opts) {
8494
8701
  },
8495
8702
  onConnect
8496
8703
  ) : import_node_net.default.connect(target.port, target.host, onConnect);
8704
+ track(upstream);
8497
8705
  upstream.on("error", () => socket.destroy());
8498
8706
  socket.on("error", () => upstream.destroy());
8499
8707
  }
@@ -8525,6 +8733,16 @@ async function startBrowseProxy(opts) {
8525
8733
  });
8526
8734
  });
8527
8735
  const server = import_node_http.default.createServer((req, res) => {
8736
+ if ((req.url ?? "").startsWith("/")) {
8737
+ if (req.url === "/favicon.ico") {
8738
+ res.writeHead(404).end();
8739
+ return;
8740
+ }
8741
+ const listening = server.address()?.port ?? 0;
8742
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
8743
+ res.end(helpPage(listening, spkiSha256));
8744
+ return;
8745
+ }
8528
8746
  let url;
8529
8747
  try {
8530
8748
  url = new URL(req.url ?? "");
@@ -8617,18 +8835,22 @@ async function startBrowseProxy(opts) {
8617
8835
  upstream.pipe(socket);
8618
8836
  socket.pipe(upstream);
8619
8837
  });
8838
+ track(upstream);
8620
8839
  upstream.on("error", () => socket.destroy());
8621
8840
  socket.on("error", () => upstream.destroy());
8622
8841
  });
8842
+ server.on("connection", track);
8623
8843
  await new Promise((resolve) => server.listen(opts.port ?? 0, "127.0.0.1", resolve));
8624
8844
  const port = server.address().port;
8625
8845
  return {
8626
8846
  port,
8847
+ certSpki: spkiSha256,
8627
8848
  close: () => new Promise((resolve) => {
8628
8849
  inner.close();
8629
8850
  plainInner.close();
8630
8851
  server.close(() => resolve());
8631
8852
  server.closeAllConnections?.();
8853
+ for (const s of sockets) s.destroy();
8632
8854
  })
8633
8855
  };
8634
8856
  }
@@ -8641,6 +8863,7 @@ var init_browse_proxy = __esm({
8641
8863
  import_node_net = __toESM(require("net"));
8642
8864
  import_node_tls = __toESM(require("tls"));
8643
8865
  init_local_cert();
8866
+ init_open_browser();
8644
8867
  HOP_BY_HOP = /* @__PURE__ */ new Set([
8645
8868
  "connection",
8646
8869
  "proxy-connection",
@@ -8736,6 +8959,18 @@ async function runBrowse(paths, opts) {
8736
8959
  log(
8737
8960
  `Device proxy: http://127.0.0.1:${proxy.port} (adds this machine's credential for ${hosts.join(", ")})`
8738
8961
  );
8962
+ if (!opts.open) {
8963
+ log("Open it in a browser through the proxy:");
8964
+ for (const l of browserCommandLines(
8965
+ opts.platform ?? process.platform,
8966
+ proxy.port,
8967
+ openUrl,
8968
+ proxy.certSpki,
8969
+ opts.wsl
8970
+ ))
8971
+ log(l);
8972
+ log(" (or re-run with --open)");
8973
+ }
8739
8974
  log("Playwright:");
8740
8975
  log(
8741
8976
  ` chromium.launch({ proxy: { server: 'http://127.0.0.1:${proxy.port}' } }) + newContext({ ignoreHTTPSErrors: true })`
@@ -8752,6 +8987,7 @@ var init_browse = __esm({
8752
8987
  "use strict";
8753
8988
  init_enrolment_store();
8754
8989
  init_browse_proxy();
8990
+ init_open_browser();
8755
8991
  }
8756
8992
  });
8757
8993
 
@@ -8781,7 +9017,7 @@ async function cmdSetup() {
8781
9017
  }
8782
9018
  async function cmdUpgrade() {
8783
9019
  const policyPath = process.argv[4] || DEFAULT_POLICY_PATH;
8784
- if (!fs.existsSync(policyPath)) {
9020
+ if (!fs2.existsSync(policyPath)) {
8785
9021
  console.error(`No policy file found at ${policyPath}`);
8786
9022
  console.error('Run "astrasync guard setup" first to create a policy.');
8787
9023
  process.exit(1);
@@ -8809,13 +9045,13 @@ async function cmdUpgrade() {
8809
9045
  prompt: (message) => prompt.question(message),
8810
9046
  print: (message) => console.log(message),
8811
9047
  writeConfig: async (filePath, content) => {
8812
- const dir = path.dirname(filePath);
8813
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
8814
- fs.writeFileSync(filePath, content, "utf-8");
9048
+ const dir = path2.dirname(filePath);
9049
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
9050
+ fs2.writeFileSync(filePath, content, "utf-8");
8815
9051
  },
8816
9052
  readConfig: async (filePath) => {
8817
9053
  try {
8818
- return fs.readFileSync(filePath, "utf-8");
9054
+ return fs2.readFileSync(filePath, "utf-8");
8819
9055
  } catch {
8820
9056
  return null;
8821
9057
  }
@@ -8836,13 +9072,13 @@ async function cmdStart() {
8836
9072
  let gatewayConfig;
8837
9073
  const upgraded = await isUpgraded(DEFAULT_CONFIG_DIR, async (p) => {
8838
9074
  try {
8839
- return fs.readFileSync(p, "utf-8");
9075
+ return fs2.readFileSync(p, "utf-8");
8840
9076
  } catch {
8841
9077
  return null;
8842
9078
  }
8843
9079
  });
8844
9080
  if (upgraded) {
8845
- const config = JSON.parse(fs.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
9081
+ const config = JSON.parse(fs2.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
8846
9082
  gatewayConfig = {
8847
9083
  mode: config.mode,
8848
9084
  apiBaseUrl: config.platformUrl ? `${config.platformUrl}/api/v1` : void 0,
@@ -8850,7 +9086,7 @@ async function cmdStart() {
8850
9086
  };
8851
9087
  console.log(`[AstraSync] Starting in ${config.mode} mode (agent: ${config.agentId})`);
8852
9088
  } else {
8853
- if (!fs.existsSync(policyPath)) {
9089
+ if (!fs2.existsSync(policyPath)) {
8854
9090
  console.error(`No policy file found at ${policyPath}`);
8855
9091
  console.error('Run "astrasync guard setup" first.');
8856
9092
  process.exit(1);
@@ -8887,7 +9123,7 @@ async function cmdStatus() {
8887
9123
  const policyPath = process.argv[4] || DEFAULT_POLICY_PATH;
8888
9124
  const upgraded = await isUpgraded(DEFAULT_CONFIG_DIR, async (p) => {
8889
9125
  try {
8890
- return fs.readFileSync(p, "utf-8");
9126
+ return fs2.readFileSync(p, "utf-8");
8891
9127
  } catch {
8892
9128
  return null;
8893
9129
  }
@@ -8897,7 +9133,7 @@ async function cmdStatus() {
8897
9133
  console.log(` ${line}`);
8898
9134
  }
8899
9135
  if (upgraded) {
8900
- const config = JSON.parse(fs.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
9136
+ const config = JSON.parse(fs2.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
8901
9137
  console.log(` Mode: ${config.mode}`);
8902
9138
  console.log(` Agent: ${config.agentId}`);
8903
9139
  console.log(` Platform: ${config.platformUrl}`);
@@ -8905,7 +9141,7 @@ async function cmdStatus() {
8905
9141
  } else {
8906
9142
  console.log(" Mode: local (free tier)");
8907
9143
  }
8908
- if (fs.existsSync(policyPath)) {
9144
+ if (fs2.existsSync(policyPath)) {
8909
9145
  const parsed = parseFile(policyPath);
8910
9146
  if (parsed.validation.valid && parsed.policy) {
8911
9147
  const p = parsed.policy;
@@ -8930,7 +9166,7 @@ async function cmdCheck() {
8930
9166
  (a) => !a.startsWith("--") && a !== "guard" && a !== "check" && a !== process.argv[0] && a !== process.argv[1]
8931
9167
  ) || DEFAULT_POLICY_PATH;
8932
9168
  const trigger = process.argv.find((a) => a.startsWith("--trigger="))?.split("=")[1] || "pre-push";
8933
- if (!fs.existsSync(policyPath)) {
9169
+ if (!fs2.existsSync(policyPath)) {
8934
9170
  console.log("[AstraSync] No policy file \u2014 skipping check");
8935
9171
  process.exit(0);
8936
9172
  }
@@ -8942,9 +9178,9 @@ async function cmdCheck() {
8942
9178
  }
8943
9179
  let enforcement = "warn";
8944
9180
  const configPath = `${DEFAULT_CONFIG_DIR}/config`;
8945
- if (fs.existsSync(configPath)) {
9181
+ if (fs2.existsSync(configPath)) {
8946
9182
  try {
8947
- const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
9183
+ const config = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
8948
9184
  enforcement = config.gitTriggerEnforcement || "warn";
8949
9185
  } catch {
8950
9186
  }
@@ -9007,8 +9243,8 @@ async function cmdUninstallHooks() {
9007
9243
  }
9008
9244
  }
9009
9245
  function resolveClaudeHookCommand() {
9010
- const sibling = path.join(__dirname, "astrasync-claude-hook.js");
9011
- if (fs.existsSync(sibling)) {
9246
+ const sibling = path2.join(__dirname, "astrasync-claude-hook.js");
9247
+ if (fs2.existsSync(sibling)) {
9012
9248
  return `"${process.execPath}" "${sibling}"`;
9013
9249
  }
9014
9250
  return "astrasync-claude-hook";
@@ -9024,19 +9260,19 @@ function parseClaudeCodeFlags() {
9024
9260
  };
9025
9261
  }
9026
9262
  function claudeCodePaths(project) {
9027
- const root = project ? process.cwd() : os.homedir();
9263
+ const root = project ? process.cwd() : os2.homedir();
9028
9264
  return {
9029
- settingsPath: path.join(root, ".claude", "settings.json"),
9030
- astrasyncDir: path.join(root, ".astrasync")
9265
+ settingsPath: path2.join(root, ".claude", "settings.json"),
9266
+ astrasyncDir: path2.join(root, ".astrasync")
9031
9267
  };
9032
9268
  }
9033
9269
  async function cmdInstallClaudeCode() {
9034
9270
  const flags = parseClaudeCodeFlags();
9035
9271
  const { settingsPath, astrasyncDir } = claudeCodePaths(flags.project);
9036
- const policyPath = flags.policyOverride || path.join(astrasyncDir, "policy.yaml");
9037
- const guardJsonPath = path.join(astrasyncDir, "guard.json");
9272
+ const policyPath = flags.policyOverride || path2.join(astrasyncDir, "policy.yaml");
9273
+ const guardJsonPath = path2.join(astrasyncDir, "guard.json");
9038
9274
  console.log("\nAstraSync Local Guard for Claude Code \u2014 install\n");
9039
- if (fs.existsSync(policyPath)) {
9275
+ if (fs2.existsSync(policyPath)) {
9040
9276
  console.log(` Policy: using existing ${policyPath}`);
9041
9277
  if (ensureGuardSelfProtection(policyPath)) {
9042
9278
  console.log(" added guard self-protection patterns to file.write");
@@ -9044,7 +9280,7 @@ async function cmdInstallClaudeCode() {
9044
9280
  } else if (flags.wizard) {
9045
9281
  const prompt = createPrompt();
9046
9282
  try {
9047
- fs.mkdirSync(path.dirname(policyPath), { recursive: true });
9283
+ fs2.mkdirSync(path2.dirname(policyPath), { recursive: true });
9048
9284
  const wizard = new SetupWizard(prompt);
9049
9285
  await wizard.runAndSave(policyPath);
9050
9286
  } finally {
@@ -9053,17 +9289,17 @@ async function cmdInstallClaudeCode() {
9053
9289
  ensureGuardSelfProtection(policyPath);
9054
9290
  console.log(` Policy: created via wizard at ${policyPath}`);
9055
9291
  } else {
9056
- fs.mkdirSync(path.dirname(policyPath), { recursive: true });
9057
- fs.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
9292
+ fs2.mkdirSync(path2.dirname(policyPath), { recursive: true });
9293
+ fs2.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
9058
9294
  console.log(` Policy: default coding-agent policy written to ${policyPath}`);
9059
9295
  console.log(" (re-run with --wizard for the interactive setup instead)");
9060
9296
  }
9061
- if (!fs.existsSync(guardJsonPath)) {
9297
+ if (!fs2.existsSync(guardJsonPath)) {
9062
9298
  const guardSettings = {
9063
9299
  posture: flags.active ? "active" : "passive",
9064
9300
  governMcp: false
9065
9301
  };
9066
- fs.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
9302
+ fs2.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
9067
9303
  `, "utf-8");
9068
9304
  console.log(
9069
9305
  ` Guard: ${guardJsonPath} (posture: ${guardSettings.posture}, governMcp: false)`
@@ -9093,7 +9329,7 @@ async function cmdInstallClaudeCode() {
9093
9329
  }
9094
9330
  console.log("\n Next steps:");
9095
9331
  console.log(" 1. Start a NEW Claude Code session (settings load at startup).");
9096
- console.log(` 2. Watch decisions: tail -f ${path.join(astrasyncDir, "traces")}/*.jsonl`);
9332
+ console.log(` 2. Watch decisions: tail -f ${path2.join(astrasyncDir, "traces")}/*.jsonl`);
9097
9333
  console.log(` 3. When the traces look right, set "posture": "active" in ${guardJsonPath}`);
9098
9334
  console.log(" to enforce (denies block, approvals use Claude Code's own prompt).");
9099
9335
  console.log(' 4. To govern MCP tools too, set "governMcp": true and add');
@@ -9123,26 +9359,26 @@ async function cmdUninstallClaudeCode() {
9123
9359
  );
9124
9360
  }
9125
9361
  function resolveCodexHookCommand() {
9126
- const sibling = path.join(__dirname, "astrasync-codex-hook.js");
9127
- if (fs.existsSync(sibling)) {
9362
+ const sibling = path2.join(__dirname, "astrasync-codex-hook.js");
9363
+ if (fs2.existsSync(sibling)) {
9128
9364
  return `"${process.execPath}" "${sibling}"`;
9129
9365
  }
9130
9366
  return "astrasync-codex-hook";
9131
9367
  }
9132
9368
  function codexPaths(project) {
9133
- const root = project ? process.cwd() : os.homedir();
9369
+ const root = project ? process.cwd() : os2.homedir();
9134
9370
  return {
9135
- hooksPath: path.join(root, ".codex", "hooks.json"),
9136
- astrasyncDir: path.join(root, ".astrasync")
9371
+ hooksPath: path2.join(root, ".codex", "hooks.json"),
9372
+ astrasyncDir: path2.join(root, ".astrasync")
9137
9373
  };
9138
9374
  }
9139
9375
  async function cmdInstallCodex() {
9140
9376
  const flags = parseClaudeCodeFlags();
9141
9377
  const { hooksPath, astrasyncDir } = codexPaths(flags.project);
9142
- const policyPath = flags.policyOverride || path.join(astrasyncDir, "policy.yaml");
9143
- const guardJsonPath = path.join(astrasyncDir, "guard.json");
9378
+ const policyPath = flags.policyOverride || path2.join(astrasyncDir, "policy.yaml");
9379
+ const guardJsonPath = path2.join(astrasyncDir, "guard.json");
9144
9380
  console.log("\nAstraSync Local Guard for Codex CLI \u2014 install\n");
9145
- if (fs.existsSync(policyPath)) {
9381
+ if (fs2.existsSync(policyPath)) {
9146
9382
  console.log(` Policy: using existing ${policyPath} (shared across adapters)`);
9147
9383
  if (ensureGuardSelfProtection(policyPath)) {
9148
9384
  console.log(" added guard self-protection patterns to file.write");
@@ -9150,7 +9386,7 @@ async function cmdInstallCodex() {
9150
9386
  } else if (flags.wizard) {
9151
9387
  const prompt = createPrompt();
9152
9388
  try {
9153
- fs.mkdirSync(path.dirname(policyPath), { recursive: true });
9389
+ fs2.mkdirSync(path2.dirname(policyPath), { recursive: true });
9154
9390
  const wizard = new SetupWizard(prompt);
9155
9391
  await wizard.runAndSave(policyPath);
9156
9392
  } finally {
@@ -9159,17 +9395,17 @@ async function cmdInstallCodex() {
9159
9395
  ensureGuardSelfProtection(policyPath);
9160
9396
  console.log(` Policy: created via wizard at ${policyPath}`);
9161
9397
  } else {
9162
- fs.mkdirSync(path.dirname(policyPath), { recursive: true });
9163
- fs.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
9398
+ fs2.mkdirSync(path2.dirname(policyPath), { recursive: true });
9399
+ fs2.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
9164
9400
  console.log(` Policy: default coding-agent policy written to ${policyPath}`);
9165
9401
  console.log(" (re-run with --wizard for the interactive setup instead)");
9166
9402
  }
9167
- if (!fs.existsSync(guardJsonPath)) {
9403
+ if (!fs2.existsSync(guardJsonPath)) {
9168
9404
  const guardSettings = {
9169
9405
  posture: flags.active ? "active" : "passive",
9170
9406
  governMcp: false
9171
9407
  };
9172
- fs.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
9408
+ fs2.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
9173
9409
  `, "utf-8");
9174
9410
  console.log(
9175
9411
  ` Guard: ${guardJsonPath} (posture: ${guardSettings.posture}, governMcp: false)`
@@ -9188,7 +9424,7 @@ async function cmdInstallCodex() {
9188
9424
  }
9189
9425
  console.log("\n Next steps:");
9190
9426
  console.log(" 1. Start a NEW Codex session (hooks load at startup; /hooks to inspect).");
9191
- console.log(` 2. Watch decisions: tail -f ${path.join(astrasyncDir, "traces")}/*.jsonl`);
9427
+ console.log(` 2. Watch decisions: tail -f ${path2.join(astrasyncDir, "traces")}/*.jsonl`);
9192
9428
  console.log(` 3. When the traces look right, set "posture": "active" in ${guardJsonPath}.`);
9193
9429
  console.log(" Denies block; approvals fall through to Codex's own prompt \u2014");
9194
9430
  console.log(" except in dontAsk/bypassPermissions modes, where they deny.");
@@ -9213,13 +9449,13 @@ function printCodexStatus() {
9213
9449
  const { hooksPath, astrasyncDir } = codexPaths(scope === "project");
9214
9450
  let hookInstalled = false;
9215
9451
  try {
9216
- const content = fs.readFileSync(hooksPath, "utf-8");
9452
+ const content = fs2.readFileSync(hooksPath, "utf-8");
9217
9453
  hookInstalled = content.includes(ASTRASYNC_CODEX_HOOK_MARKER);
9218
9454
  } catch {
9219
9455
  }
9220
9456
  if (hookInstalled) {
9221
- const policyPath = path.join(astrasyncDir, "policy.yaml");
9222
- const policy = fs.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
9457
+ const policyPath = path2.join(astrasyncDir, "policy.yaml");
9458
+ const policy = fs2.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
9223
9459
  console.log(` Codex CLI (${scope}): hook installed, ${policy}`);
9224
9460
  }
9225
9461
  }
@@ -9229,24 +9465,24 @@ function printClaudeCodeStatus() {
9229
9465
  const { settingsPath, astrasyncDir } = claudeCodePaths(scope === "project");
9230
9466
  let hookInstalled = false;
9231
9467
  try {
9232
- const content = fs.readFileSync(settingsPath, "utf-8");
9468
+ const content = fs2.readFileSync(settingsPath, "utf-8");
9233
9469
  hookInstalled = content.includes(ASTRASYNC_HOOK_MARKER);
9234
9470
  } catch {
9235
9471
  }
9236
- const policyPath = path.join(astrasyncDir, "policy.yaml");
9237
- const guardJsonPath = path.join(astrasyncDir, "guard.json");
9472
+ const policyPath = path2.join(astrasyncDir, "policy.yaml");
9473
+ const guardJsonPath = path2.join(astrasyncDir, "guard.json");
9238
9474
  let posture = "";
9239
- if (fs.existsSync(guardJsonPath)) {
9475
+ if (fs2.existsSync(guardJsonPath)) {
9240
9476
  try {
9241
- const settings = JSON.parse(fs.readFileSync(guardJsonPath, "utf-8"));
9477
+ const settings = JSON.parse(fs2.readFileSync(guardJsonPath, "utf-8"));
9242
9478
  posture = settings.posture ? `, posture: ${settings.posture}` : "";
9243
9479
  } catch {
9244
9480
  posture = ", guard.json unreadable";
9245
9481
  }
9246
9482
  }
9247
- if (hookInstalled || fs.existsSync(policyPath)) {
9483
+ if (hookInstalled || fs2.existsSync(policyPath)) {
9248
9484
  const hook = hookInstalled ? "hook installed" : "hook NOT installed";
9249
- const policy = fs.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
9485
+ const policy = fs2.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
9250
9486
  console.log(` Claude Code (${scope}): ${hook}, ${policy}${posture}`);
9251
9487
  }
9252
9488
  }
@@ -9292,14 +9528,21 @@ async function cmdFleetStatus() {
9292
9528
  async function cmdBrowse() {
9293
9529
  try {
9294
9530
  const port = flagValue("--port");
9295
- const { proxy } = await runBrowse(fleetPaths(), {
9531
+ const open = process.argv.includes("--open");
9532
+ const { proxy, openUrl } = await runBrowse(fleetPaths(), {
9296
9533
  checkpoint: flagValue("--checkpoint") ?? "",
9297
9534
  slug: flagValue("--slug") ?? "",
9298
9535
  upstream: flagValue("--upstream"),
9299
9536
  port: port ? Number(port) : void 0,
9300
- extraHosts: flagValue("--host") ? [flagValue("--host")] : []
9537
+ extraHosts: flagValue("--host") ? [flagValue("--host")] : [],
9538
+ open
9301
9539
  });
9540
+ if (open) openThroughProxy({ port: proxy.port, url: openUrl, certSpki: proxy.certSpki });
9541
+ let stopping = false;
9302
9542
  const stop = async () => {
9543
+ if (stopping) process.exit(0);
9544
+ stopping = true;
9545
+ setTimeout(() => process.exit(0), 2e3).unref();
9303
9546
  await proxy.close();
9304
9547
  process.exit(0);
9305
9548
  };
@@ -9334,7 +9577,7 @@ async function cmdGitRoute() {
9334
9577
  }
9335
9578
  async function cmdGitCredential() {
9336
9579
  const action = process.argv[process.argv.indexOf("git-credential") + 1] ?? "";
9337
- const input = fs.readFileSync(0, "utf-8");
9580
+ const input = fs2.readFileSync(0, "utf-8");
9338
9581
  process.stdout.write(runGitCredential(action, input));
9339
9582
  }
9340
9583
  async function cmdManagedSettings() {
@@ -9438,8 +9681,9 @@ async function main() {
9438
9681
  " browse Sign an agent in to your dashboard from this machine and"
9439
9682
  );
9440
9683
  console.log(
9441
- " run its device proxy (--checkpoint <url> --slug <agent>)"
9684
+ " run its device proxy (--checkpoint <url> --slug <agent>;"
9442
9685
  );
9686
+ console.log(" --open to start a browser through the proxy)");
9443
9687
  console.log(" enroll <token> Enrol this machine in your organisation fleet");
9444
9688
  console.log(" (--api-base-url <url>, --system for machine scope)");
9445
9689
  console.log(" fleet-status Show fleet enrolment, signed policy and heartbeat");
@@ -9453,13 +9697,13 @@ async function main() {
9453
9697
  process.exit(command ? 1 : 0);
9454
9698
  }
9455
9699
  }
9456
- var fs, os, path, readline, DEFAULT_POLICY_PATH, DEFAULT_CONFIG_DIR;
9700
+ var fs2, os2, path2, readline, DEFAULT_POLICY_PATH, DEFAULT_CONFIG_DIR;
9457
9701
  var init_guard = __esm({
9458
9702
  "src/cli/guard.ts"() {
9459
9703
  "use strict";
9460
- fs = __toESM(require("fs"));
9461
- os = __toESM(require("os"));
9462
- path = __toESM(require("path"));
9704
+ fs2 = __toESM(require("fs"));
9705
+ os2 = __toESM(require("os"));
9706
+ path2 = __toESM(require("path"));
9463
9707
  readline = __toESM(require("readline"));
9464
9708
  init_wizard();
9465
9709
  init_upgrade();
@@ -9473,6 +9717,7 @@ var init_guard = __esm({
9473
9717
  init_enrolment_store();
9474
9718
  init_git_route();
9475
9719
  init_browse();
9720
+ init_open_browser();
9476
9721
  DEFAULT_POLICY_PATH = "./local-policy.yaml";
9477
9722
  DEFAULT_CONFIG_DIR = ".astrasync";
9478
9723
  main().catch((err) => {
@@ -9509,6 +9754,7 @@ COMMANDS:
9509
9754
  (--api-base-url <url>, --system for machine scope)
9510
9755
  guard fleet-status Fleet enrolment, signed policy and heartbeat state
9511
9756
  guard browse Sign an agent in to your dashboard via this machine's device proxy
9757
+ (--checkpoint <url> --slug <agent>; --open starts a browser through it)
9512
9758
  guard managed-settings MDM templates for Claude Code / Codex (--claude, --codex, --out <dir>)
9513
9759
 
9514
9760
  REGISTER OPTIONS: