@astrasyncai/verification-gateway 5.18.0 → 5.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +178 -0
- package/README.md +168 -4
- package/dist/adapters/express.d.mts +9 -1
- package/dist/adapters/express.d.ts +9 -1
- package/dist/adapters/express.js +96 -37
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +96 -37
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/http-pdlss.d.mts +7 -1
- package/dist/adapters/http-pdlss.d.ts +7 -1
- package/dist/adapters/http-pdlss.js +18 -2
- package/dist/adapters/http-pdlss.js.map +1 -1
- package/dist/adapters/http-pdlss.mjs +16 -1
- package/dist/adapters/http-pdlss.mjs.map +1 -1
- package/dist/adapters/kit-express.d.mts +93 -1
- package/dist/adapters/kit-express.d.ts +93 -1
- package/dist/adapters/kit-express.js +181 -2
- package/dist/adapters/kit-express.js.map +1 -1
- package/dist/adapters/kit-express.mjs +180 -2
- package/dist/adapters/kit-express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +80 -9
- package/dist/adapters/mcp.d.ts +80 -9
- package/dist/adapters/mcp.js +153 -5
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +151 -5
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +1 -1
- package/dist/adapters/nextjs.d.ts +1 -1
- package/dist/adapters/nextjs.js +41 -4
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +41 -4
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +44 -1
- package/dist/adapters/sdk.d.ts +44 -1
- package/dist/adapters/sdk.js +76 -2
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +76 -2
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.js +1 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +46 -6
- package/dist/bin/astrasync-codex-hook.js +46 -6
- package/dist/bin/astrasync-guard.js +375 -117
- package/dist/bin/astrasync.js +417 -117
- package/dist/browser/background.js +46 -6
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +46 -6
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.js +46 -6
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +46 -6
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.js +46 -6
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +46 -6
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +1 -1
- package/dist/edge-config.d.ts +1 -1
- package/dist/edge-config.js +1 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +1 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +1 -1
- package/dist/edge-core/index.d.ts +1 -1
- package/dist/edge-core/index.js +21 -2
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +21 -2
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/gateway/gateway.js +46 -6
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +46 -6
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +1 -1
- package/dist/git-trigger/git-hooks.d.ts +1 -1
- package/dist/index.d.mts +542 -30
- package/dist/index.d.ts +542 -30
- package/dist/index.js +1171 -183
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1157 -173
- package/dist/index.mjs.map +1 -1
- package/dist/registration/index.d.mts +1 -1
- package/dist/registration/index.d.ts +1 -1
- package/dist/registration/index.js +1 -1
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +1 -1
- package/dist/registration/index.mjs.map +1 -1
- package/dist/transport/index.js +3 -2
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +3 -2
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-3ObBpsnf.d.mts → types-BoGjyon0.d.mts} +81 -2
- package/dist/{types-Cns4l8gX.d.mts → types-DqWPkiYl.d.mts} +90 -12
- package/dist/{types-Cns4l8gX.d.ts → types-DqWPkiYl.d.ts} +90 -12
- package/dist/{types-BoxZEYPS.d.ts → types-eWyZuz-3.d.ts} +81 -2
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +23 -2
- package/dist/verify.d.ts +23 -2
- package/dist/verify.js +23 -4
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +23 -4
- package/dist/verify.mjs.map +1 -1
- package/dist/webhooks.d.mts +16 -3
- package/dist/webhooks.d.ts +16 -3
- package/dist/webhooks.js +24 -13
- package/dist/webhooks.js.map +1 -1
- package/dist/webhooks.mjs +24 -13
- package/dist/webhooks.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli/guard.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
27
|
+
var fs2 = __toESM(require("fs"));
|
|
28
|
+
var os2 = __toESM(require("os"));
|
|
29
|
+
var path2 = __toESM(require("path"));
|
|
30
30
|
var readline = __toESM(require("readline"));
|
|
31
31
|
|
|
32
32
|
// ../../node_modules/js-yaml/dist/js-yaml.mjs
|
|
@@ -3129,115 +3129,115 @@ function validatePolicy(policy) {
|
|
|
3129
3129
|
}
|
|
3130
3130
|
return { valid: errors.length === 0, errors };
|
|
3131
3131
|
}
|
|
3132
|
-
function validatePurpose(purpose,
|
|
3132
|
+
function validatePurpose(purpose, path3) {
|
|
3133
3133
|
const errors = [];
|
|
3134
3134
|
if (!purpose || typeof purpose !== "object") {
|
|
3135
|
-
return [{ path:
|
|
3135
|
+
return [{ path: path3, message: "purpose must be an object" }];
|
|
3136
3136
|
}
|
|
3137
3137
|
const p = purpose;
|
|
3138
3138
|
if (typeof p.id !== "string" || !p.id) {
|
|
3139
|
-
errors.push({ path: `${
|
|
3139
|
+
errors.push({ path: `${path3}.id`, message: "id is required and must be a non-empty string" });
|
|
3140
3140
|
}
|
|
3141
3141
|
if (typeof p.allowed !== "boolean") {
|
|
3142
|
-
errors.push({ path: `${
|
|
3142
|
+
errors.push({ path: `${path3}.allowed`, message: "allowed is required and must be a boolean" });
|
|
3143
3143
|
}
|
|
3144
3144
|
if (p.targets !== void 0 && !isStringArray(p.targets)) {
|
|
3145
|
-
errors.push({ path: `${
|
|
3145
|
+
errors.push({ path: `${path3}.targets`, message: "targets must be an array of strings" });
|
|
3146
3146
|
}
|
|
3147
3147
|
if (p.blockedPatterns !== void 0 && !isStringArray(p.blockedPatterns)) {
|
|
3148
3148
|
errors.push({
|
|
3149
|
-
path: `${
|
|
3149
|
+
path: `${path3}.blockedPatterns`,
|
|
3150
3150
|
message: "blockedPatterns must be an array of strings"
|
|
3151
3151
|
});
|
|
3152
3152
|
}
|
|
3153
3153
|
if (p.requiresApproval !== void 0 && typeof p.requiresApproval !== "boolean") {
|
|
3154
3154
|
errors.push({
|
|
3155
|
-
path: `${
|
|
3155
|
+
path: `${path3}.requiresApproval`,
|
|
3156
3156
|
message: "requiresApproval must be a boolean"
|
|
3157
3157
|
});
|
|
3158
3158
|
}
|
|
3159
3159
|
return errors;
|
|
3160
3160
|
}
|
|
3161
|
-
function validateScope(scope,
|
|
3161
|
+
function validateScope(scope, path3) {
|
|
3162
3162
|
const errors = [];
|
|
3163
3163
|
if (typeof scope !== "object" || scope === null) {
|
|
3164
|
-
return [{ path:
|
|
3164
|
+
return [{ path: path3, message: "scope must be an object" }];
|
|
3165
3165
|
}
|
|
3166
3166
|
const s = scope;
|
|
3167
3167
|
for (const field of ["allowedDomains", "blockedDomains", "blockedResources"]) {
|
|
3168
3168
|
if (s[field] !== void 0 && !isStringArray(s[field])) {
|
|
3169
|
-
errors.push({ path: `${
|
|
3169
|
+
errors.push({ path: `${path3}.${field}`, message: `${field} must be an array of strings` });
|
|
3170
3170
|
}
|
|
3171
3171
|
}
|
|
3172
3172
|
return errors;
|
|
3173
3173
|
}
|
|
3174
|
-
function validateLimits(limits,
|
|
3174
|
+
function validateLimits(limits, path3) {
|
|
3175
3175
|
const errors = [];
|
|
3176
3176
|
if (typeof limits !== "object" || limits === null) {
|
|
3177
|
-
return [{ path:
|
|
3177
|
+
return [{ path: path3, message: "limits must be an object" }];
|
|
3178
3178
|
}
|
|
3179
3179
|
const l = limits;
|
|
3180
3180
|
if (l.autonomousThreshold !== void 0 && typeof l.autonomousThreshold !== "number") {
|
|
3181
3181
|
errors.push({
|
|
3182
|
-
path: `${
|
|
3182
|
+
path: `${path3}.autonomousThreshold`,
|
|
3183
3183
|
message: "autonomousThreshold must be a number"
|
|
3184
3184
|
});
|
|
3185
3185
|
}
|
|
3186
3186
|
if (l.maxTransactionAmount !== void 0 && typeof l.maxTransactionAmount !== "number") {
|
|
3187
3187
|
errors.push({
|
|
3188
|
-
path: `${
|
|
3188
|
+
path: `${path3}.maxTransactionAmount`,
|
|
3189
3189
|
message: "maxTransactionAmount must be a number"
|
|
3190
3190
|
});
|
|
3191
3191
|
}
|
|
3192
3192
|
if (typeof l.autonomousThreshold === "number" && typeof l.maxTransactionAmount === "number" && l.autonomousThreshold > l.maxTransactionAmount) {
|
|
3193
3193
|
errors.push({
|
|
3194
|
-
path: `${
|
|
3194
|
+
path: `${path3}.autonomousThreshold`,
|
|
3195
3195
|
message: "autonomousThreshold (Autonomous Limit) must be <= maxTransactionAmount (Hard Limit)"
|
|
3196
3196
|
});
|
|
3197
3197
|
}
|
|
3198
3198
|
if (l.maxRequestsPerHour !== void 0 && typeof l.maxRequestsPerHour !== "number") {
|
|
3199
3199
|
errors.push({
|
|
3200
|
-
path: `${
|
|
3200
|
+
path: `${path3}.maxRequestsPerHour`,
|
|
3201
3201
|
message: "maxRequestsPerHour must be a number"
|
|
3202
3202
|
});
|
|
3203
3203
|
}
|
|
3204
3204
|
if (l.currency !== void 0 && typeof l.currency !== "string") {
|
|
3205
|
-
errors.push({ path: `${
|
|
3205
|
+
errors.push({ path: `${path3}.currency`, message: "currency must be a string" });
|
|
3206
3206
|
}
|
|
3207
3207
|
return errors;
|
|
3208
3208
|
}
|
|
3209
|
-
function validateRiskThresholds(thresholds,
|
|
3209
|
+
function validateRiskThresholds(thresholds, path3) {
|
|
3210
3210
|
const errors = [];
|
|
3211
3211
|
if (typeof thresholds !== "object" || thresholds === null) {
|
|
3212
|
-
return [{ path:
|
|
3212
|
+
return [{ path: path3, message: "riskThresholds must be an object" }];
|
|
3213
3213
|
}
|
|
3214
3214
|
const t = thresholds;
|
|
3215
3215
|
for (const range of ["autoAllow", "requireApproval", "autoBlock"]) {
|
|
3216
3216
|
if (!t[range] || typeof t[range] !== "object") {
|
|
3217
3217
|
errors.push({
|
|
3218
|
-
path: `${
|
|
3218
|
+
path: `${path3}.${range}`,
|
|
3219
3219
|
message: `${range} is required and must be an object with min and max`
|
|
3220
3220
|
});
|
|
3221
3221
|
continue;
|
|
3222
3222
|
}
|
|
3223
3223
|
const r = t[range];
|
|
3224
3224
|
if (typeof r.min !== "number" || typeof r.max !== "number") {
|
|
3225
|
-
errors.push({ path: `${
|
|
3225
|
+
errors.push({ path: `${path3}.${range}`, message: `${range} must have numeric min and max` });
|
|
3226
3226
|
} else if (r.min > r.max) {
|
|
3227
|
-
errors.push({ path: `${
|
|
3227
|
+
errors.push({ path: `${path3}.${range}`, message: `${range}.min must be <= ${range}.max` });
|
|
3228
3228
|
}
|
|
3229
3229
|
}
|
|
3230
3230
|
if (isValidThresholdShape(t)) {
|
|
3231
3231
|
const thresholdObj = t;
|
|
3232
3232
|
if (thresholdObj.autoAllow.max + 1 !== thresholdObj.requireApproval.min) {
|
|
3233
3233
|
errors.push({
|
|
3234
|
-
path: `${
|
|
3234
|
+
path: `${path3}`,
|
|
3235
3235
|
message: `Gap between autoAllow.max (${thresholdObj.autoAllow.max}) and requireApproval.min (${thresholdObj.requireApproval.min}). Ranges must be contiguous.`
|
|
3236
3236
|
});
|
|
3237
3237
|
}
|
|
3238
3238
|
if (thresholdObj.requireApproval.max + 1 !== thresholdObj.autoBlock.min) {
|
|
3239
3239
|
errors.push({
|
|
3240
|
-
path: `${
|
|
3240
|
+
path: `${path3}`,
|
|
3241
3241
|
message: `Gap between requireApproval.max (${thresholdObj.requireApproval.max}) and autoBlock.min (${thresholdObj.autoBlock.min}). Ranges must be contiguous.`
|
|
3242
3242
|
});
|
|
3243
3243
|
}
|
|
@@ -3597,8 +3597,8 @@ ${category.label}
|
|
|
3597
3597
|
async runAndSave(outputPath) {
|
|
3598
3598
|
const policy = await this.run();
|
|
3599
3599
|
const yaml2 = toYaml(policy);
|
|
3600
|
-
const
|
|
3601
|
-
|
|
3600
|
+
const fs3 = await import("fs");
|
|
3601
|
+
fs3.writeFileSync(outputPath, yaml2, "utf-8");
|
|
3602
3602
|
this.print(`
|
|
3603
3603
|
Policy saved to ${outputPath}`);
|
|
3604
3604
|
this.print(" AstraSync Local Guard is now protecting your agent.\n");
|
|
@@ -3713,7 +3713,7 @@ Policy saved to ${outputPath}`);
|
|
|
3713
3713
|
};
|
|
3714
3714
|
|
|
3715
3715
|
// src/version.ts
|
|
3716
|
-
var SDK_VERSION = "5.
|
|
3716
|
+
var SDK_VERSION = "5.19.0";
|
|
3717
3717
|
|
|
3718
3718
|
// src/http.ts
|
|
3719
3719
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -4772,7 +4772,12 @@ function getCacheKey(request2, counterpartyId) {
|
|
|
4772
4772
|
// its start_checkout quote was served the cached quote-leg grant (no
|
|
4773
4773
|
// settlementOutcome) and never reached the backend. Same F-A1-07 bug class.
|
|
4774
4774
|
request2.commercePhase || "",
|
|
4775
|
-
request2.checkoutSessionId || ""
|
|
4775
|
+
request2.checkoutSessionId || "",
|
|
4776
|
+
// 5.19.0: a route_config purpose claim is checked server-side against
|
|
4777
|
+
// the stored rule for this method + path, so it changes the verdict.
|
|
4778
|
+
request2.purposeSource || "",
|
|
4779
|
+
request2.requestMethod || "",
|
|
4780
|
+
request2.requestPath || ""
|
|
4776
4781
|
].join("|");
|
|
4777
4782
|
}
|
|
4778
4783
|
function getCachedResult(request2, counterpartyId) {
|
|
@@ -4946,6 +4951,11 @@ async function callVerifyAccessAPI(config, request2) {
|
|
|
4946
4951
|
if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
|
|
4947
4952
|
if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
|
|
4948
4953
|
if (requestData.attemptId) body.attemptId = requestData.attemptId;
|
|
4954
|
+
if (requestData.purposeSource) body.purposeSource = requestData.purposeSource;
|
|
4955
|
+
if (requestData.agentArtifacts) body.agentArtifacts = requestData.agentArtifacts;
|
|
4956
|
+
if (requestData.metadata) body.metadata = requestData.metadata;
|
|
4957
|
+
if (requestData.requestMethod) body.requestMethod = requestData.requestMethod;
|
|
4958
|
+
if (requestData.requestPath) body.requestPath = requestData.requestPath;
|
|
4949
4959
|
if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
|
|
4950
4960
|
body.sdkVersion = SDK_VERSION;
|
|
4951
4961
|
if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
|
|
@@ -5135,9 +5145,18 @@ async function verify(config, request2, options) {
|
|
|
5135
5145
|
if (!apiResponse.access?.allowed) {
|
|
5136
5146
|
const aggregatedFailures = apiResponse.access?.failures;
|
|
5137
5147
|
const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
|
|
5148
|
+
const denyAgent = idVerifiedFromBackend && apiResponse.agent ? {
|
|
5149
|
+
astraId: apiResponse.agent.astraId,
|
|
5150
|
+
name: apiResponse.agent.name,
|
|
5151
|
+
trustScore: apiResponse.agent.trustScore,
|
|
5152
|
+
trustLevel: getTrustLevel(apiResponse.agent.trustScore),
|
|
5153
|
+
blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
|
|
5154
|
+
status: apiResponse.agent.agentStatus
|
|
5155
|
+
} : void 0;
|
|
5138
5156
|
const result2 = {
|
|
5139
5157
|
identityVerified: idVerifiedFromBackend,
|
|
5140
5158
|
policyAllowed: false,
|
|
5159
|
+
...denyAgent && { agent: denyAgent },
|
|
5141
5160
|
// 3.6.0: the server now emits top-level denialReasons on every deny
|
|
5142
5161
|
// path — prefer it verbatim; the failure-message reconstruction remains
|
|
5143
5162
|
// as the fallback for older backends.
|
|
@@ -5283,6 +5302,9 @@ var MAX_TOTAL_BYTES = 16 * 1024;
|
|
|
5283
5302
|
// src/adapters/commerce-detect.ts
|
|
5284
5303
|
var MAX_BODY_BYTES = 64 * 1024;
|
|
5285
5304
|
|
|
5305
|
+
// src/adapters/routes-cache.ts
|
|
5306
|
+
var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
|
|
5307
|
+
|
|
5286
5308
|
// src/edge-config.ts
|
|
5287
5309
|
var DEFAULT_EDGE_CONFIG = Object.freeze({
|
|
5288
5310
|
version: 1,
|
|
@@ -5524,9 +5546,6 @@ var BeaconBuffer = class {
|
|
|
5524
5546
|
};
|
|
5525
5547
|
var beaconBuffer = new BeaconBuffer();
|
|
5526
5548
|
|
|
5527
|
-
// src/adapters/express.ts
|
|
5528
|
-
var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
|
|
5529
|
-
|
|
5530
5549
|
// src/adapters/nextjs.ts
|
|
5531
5550
|
var DEFAULT_ROUTES_REFRESH_MS2 = 5 * 60 * 1e3;
|
|
5532
5551
|
|
|
@@ -5612,6 +5631,25 @@ var import_jose2 = require("jose");
|
|
|
5612
5631
|
// src/idempotency/index.ts
|
|
5613
5632
|
var DEFAULT_IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5614
5633
|
|
|
5634
|
+
// src/ucp/approval-events.ts
|
|
5635
|
+
var MemorySeenEvents = class {
|
|
5636
|
+
constructor(max = 1e4) {
|
|
5637
|
+
this.max = max;
|
|
5638
|
+
this.ids = /* @__PURE__ */ new Set();
|
|
5639
|
+
}
|
|
5640
|
+
async has(eventId) {
|
|
5641
|
+
return this.ids.has(eventId);
|
|
5642
|
+
}
|
|
5643
|
+
async add(eventId) {
|
|
5644
|
+
if (this.ids.size >= this.max) {
|
|
5645
|
+
const first = this.ids.values().next().value;
|
|
5646
|
+
if (first !== void 0) this.ids.delete(first);
|
|
5647
|
+
}
|
|
5648
|
+
this.ids.add(eventId);
|
|
5649
|
+
}
|
|
5650
|
+
};
|
|
5651
|
+
var defaultSeen = new MemorySeenEvents();
|
|
5652
|
+
|
|
5615
5653
|
// src/payments/kyapay.ts
|
|
5616
5654
|
var import_jose4 = require("jose");
|
|
5617
5655
|
|
|
@@ -5651,7 +5689,9 @@ function toDecision(result) {
|
|
|
5651
5689
|
if (result.identityVerified && result.policyAllowed) {
|
|
5652
5690
|
return {
|
|
5653
5691
|
recommendation: "ALLOW",
|
|
5654
|
-
|
|
5692
|
+
// 5.19.0: the score is on `trustScore` for the merchant; `reason` is
|
|
5693
|
+
// text a merchant may relay to the agent, so it never embeds the number.
|
|
5694
|
+
reason: "Verified",
|
|
5655
5695
|
trustScore: result.agent?.trustScore,
|
|
5656
5696
|
sessionId: "sessionId" in result ? result.sessionId : void 0
|
|
5657
5697
|
};
|
|
@@ -6574,29 +6614,29 @@ function loadEnrolment(paths) {
|
|
|
6574
6614
|
[paths.machineDevice, "machine"],
|
|
6575
6615
|
[paths.userDevice, "user"]
|
|
6576
6616
|
];
|
|
6577
|
-
for (const [
|
|
6578
|
-
if (!(0, import_fs6.existsSync)(
|
|
6617
|
+
for (const [path3, scope] of candidates) {
|
|
6618
|
+
if (!(0, import_fs6.existsSync)(path3)) continue;
|
|
6579
6619
|
try {
|
|
6580
|
-
const parsed = JSON.parse((0, import_fs6.readFileSync)(
|
|
6620
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(path3, "utf-8"));
|
|
6581
6621
|
if (isEnrolment(parsed)) return { enrolment: parsed, scope };
|
|
6582
6622
|
} catch {
|
|
6583
6623
|
}
|
|
6584
|
-
return { corrupt: true, path:
|
|
6624
|
+
return { corrupt: true, path: path3 };
|
|
6585
6625
|
}
|
|
6586
6626
|
return null;
|
|
6587
6627
|
}
|
|
6588
|
-
function writeFileMode(
|
|
6589
|
-
(0, import_fs6.mkdirSync)((0, import_path5.dirname)(
|
|
6590
|
-
(0, import_fs6.writeFileSync)(
|
|
6628
|
+
function writeFileMode(path3, content, mode) {
|
|
6629
|
+
(0, import_fs6.mkdirSync)((0, import_path5.dirname)(path3), { recursive: true, mode: 448 });
|
|
6630
|
+
(0, import_fs6.writeFileSync)(path3, content, { encoding: "utf-8", mode });
|
|
6591
6631
|
try {
|
|
6592
|
-
(0, import_fs6.chmodSync)(
|
|
6632
|
+
(0, import_fs6.chmodSync)(path3, mode);
|
|
6593
6633
|
} catch {
|
|
6594
6634
|
}
|
|
6595
6635
|
}
|
|
6596
6636
|
function saveEnrolment(paths, enrolment, scope) {
|
|
6597
|
-
const
|
|
6598
|
-
writeFileMode(
|
|
6599
|
-
return
|
|
6637
|
+
const path3 = scope === "machine" ? paths.machineDevice : paths.userDevice;
|
|
6638
|
+
writeFileMode(path3, JSON.stringify(enrolment, null, 2), scope === "machine" ? 420 : 384);
|
|
6639
|
+
return path3;
|
|
6600
6640
|
}
|
|
6601
6641
|
function readCachedPolicies(paths) {
|
|
6602
6642
|
const out = [];
|
|
@@ -6918,12 +6958,12 @@ var MANAGED_SETTINGS_LOCATIONS = {
|
|
|
6918
6958
|
win32: "%ProgramData%\\OpenAI\\Codex\\requirements.toml"
|
|
6919
6959
|
}
|
|
6920
6960
|
};
|
|
6921
|
-
function managedHookCommand(target,
|
|
6961
|
+
function managedHookCommand(target, os3) {
|
|
6922
6962
|
const bin = target === "claude" ? "astrasync-claude-hook" : "astrasync-codex-hook";
|
|
6923
|
-
if (
|
|
6963
|
+
if (os3 === "win32") return `"C:\\Program Files\\nodejs\\${bin}.cmd"`;
|
|
6924
6964
|
return `/usr/local/bin/${bin}`;
|
|
6925
6965
|
}
|
|
6926
|
-
function buildClaudeManagedSettings(
|
|
6966
|
+
function buildClaudeManagedSettings(os3) {
|
|
6927
6967
|
return {
|
|
6928
6968
|
allowManagedHooksOnly: true,
|
|
6929
6969
|
env: { ASTRASYNC_FLEET_REQUIRED: "1" },
|
|
@@ -6931,7 +6971,7 @@ function buildClaudeManagedSettings(os2) {
|
|
|
6931
6971
|
PreToolUse: [
|
|
6932
6972
|
{
|
|
6933
6973
|
matcher: HOOK_MATCHER,
|
|
6934
|
-
hooks: [{ type: "command", command: managedHookCommand("claude",
|
|
6974
|
+
hooks: [{ type: "command", command: managedHookCommand("claude", os3), timeout: 10 }]
|
|
6935
6975
|
}
|
|
6936
6976
|
]
|
|
6937
6977
|
},
|
|
@@ -6941,11 +6981,11 @@ function buildClaudeManagedSettings(os2) {
|
|
|
6941
6981
|
function tomlString(value) {
|
|
6942
6982
|
return JSON.stringify(value);
|
|
6943
6983
|
}
|
|
6944
|
-
function buildCodexRequirementsToml(
|
|
6945
|
-
const command =
|
|
6984
|
+
function buildCodexRequirementsToml(os3) {
|
|
6985
|
+
const command = os3 === "win32" ? `set ASTRASYNC_FLEET_REQUIRED=1&& ${managedHookCommand("codex", os3)}` : `ASTRASYNC_FLEET_REQUIRED=1 ${managedHookCommand("codex", os3)}`;
|
|
6946
6986
|
return [
|
|
6947
6987
|
"# AstraSync Local Guard \u2014 managed hook (generated by `astrasync guard managed-settings --codex`)",
|
|
6948
|
-
`# Install at: ${MANAGED_SETTINGS_LOCATIONS.codex[
|
|
6988
|
+
`# Install at: ${MANAGED_SETTINGS_LOCATIONS.codex[os3]}`,
|
|
6949
6989
|
"allow_managed_hooks_only = true",
|
|
6950
6990
|
"",
|
|
6951
6991
|
"[features]",
|
|
@@ -6965,14 +7005,14 @@ function buildCodexRequirementsToml(os2) {
|
|
|
6965
7005
|
function buildManagedSettingsFiles(targets) {
|
|
6966
7006
|
const out = [];
|
|
6967
7007
|
for (const target of targets) {
|
|
6968
|
-
for (const
|
|
6969
|
-
const content = target === "claude" ? `${JSON.stringify(buildClaudeManagedSettings(
|
|
6970
|
-
` : buildCodexRequirementsToml(
|
|
7008
|
+
for (const os3 of ["darwin", "linux", "win32"]) {
|
|
7009
|
+
const content = target === "claude" ? `${JSON.stringify(buildClaudeManagedSettings(os3), null, 2)}
|
|
7010
|
+
` : buildCodexRequirementsToml(os3);
|
|
6971
7011
|
out.push({
|
|
6972
7012
|
target,
|
|
6973
|
-
os:
|
|
6974
|
-
installPath: MANAGED_SETTINGS_LOCATIONS[target][
|
|
6975
|
-
fileName: `${target}-${
|
|
7013
|
+
os: os3,
|
|
7014
|
+
installPath: MANAGED_SETTINGS_LOCATIONS[target][os3],
|
|
7015
|
+
fileName: `${target}-${os3}-${target === "claude" ? "managed-settings.json" : "requirements.toml"}`,
|
|
6976
7016
|
content
|
|
6977
7017
|
});
|
|
6978
7018
|
}
|
|
@@ -7151,10 +7191,170 @@ function createLocalCert(hosts, days = 30) {
|
|
|
7151
7191
|
${b64}
|
|
7152
7192
|
-----END CERTIFICATE-----
|
|
7153
7193
|
`,
|
|
7154
|
-
key: privateKey.export({ type: "pkcs8", format: "pem" }).toString()
|
|
7194
|
+
key: privateKey.export({ type: "pkcs8", format: "pem" }).toString(),
|
|
7195
|
+
spkiSha256: import_node_crypto5.default.createHash("sha256").update(spki).digest("base64")
|
|
7155
7196
|
};
|
|
7156
7197
|
}
|
|
7157
7198
|
|
|
7199
|
+
// src/fleet/open-browser.ts
|
|
7200
|
+
var import_node_child_process = require("child_process");
|
|
7201
|
+
var import_node_fs = __toESM(require("fs"));
|
|
7202
|
+
var import_node_os = __toESM(require("os"));
|
|
7203
|
+
var import_node_path = __toESM(require("path"));
|
|
7204
|
+
function isWsl(env = process.env, platform2 = process.platform, readVersion = () => import_node_fs.default.readFileSync("/proc/version", "utf8")) {
|
|
7205
|
+
if (platform2 !== "linux") return false;
|
|
7206
|
+
if (env.WSL_DISTRO_NAME) return true;
|
|
7207
|
+
try {
|
|
7208
|
+
return /microsoft/i.test(readVersion());
|
|
7209
|
+
} catch {
|
|
7210
|
+
return false;
|
|
7211
|
+
}
|
|
7212
|
+
}
|
|
7213
|
+
var MAC_PATHS = [
|
|
7214
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
7215
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
7216
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium"
|
|
7217
|
+
];
|
|
7218
|
+
var LINUX_NAMES = [
|
|
7219
|
+
"google-chrome",
|
|
7220
|
+
"google-chrome-stable",
|
|
7221
|
+
"chromium",
|
|
7222
|
+
"chromium-browser",
|
|
7223
|
+
"microsoft-edge"
|
|
7224
|
+
];
|
|
7225
|
+
var WSL_WINDOWS_PATHS = [
|
|
7226
|
+
"/mnt/c/Program Files/Google/Chrome/Application/chrome.exe",
|
|
7227
|
+
"/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
|
|
7228
|
+
"/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe",
|
|
7229
|
+
"/mnt/c/Program Files/Microsoft/Edge/Application/msedge.exe"
|
|
7230
|
+
];
|
|
7231
|
+
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)) {
|
|
7232
|
+
let candidates;
|
|
7233
|
+
if (platform2 === "darwin") {
|
|
7234
|
+
candidates = MAC_PATHS;
|
|
7235
|
+
} else if (platform2 === "win32") {
|
|
7236
|
+
const bases = [
|
|
7237
|
+
env.ProgramFiles,
|
|
7238
|
+
env["ProgramFiles(x86)"],
|
|
7239
|
+
env.LOCALAPPDATA ?? env.LocalAppData
|
|
7240
|
+
].filter((b) => !!b);
|
|
7241
|
+
candidates = [
|
|
7242
|
+
...bases.map((b) => import_node_path.default.win32.join(b, "Google", "Chrome", "Application", "chrome.exe")),
|
|
7243
|
+
...bases.map((b) => import_node_path.default.win32.join(b, "Microsoft", "Edge", "Application", "msedge.exe"))
|
|
7244
|
+
];
|
|
7245
|
+
} else {
|
|
7246
|
+
const dirs = (env.PATH ?? "").split(":").filter(Boolean);
|
|
7247
|
+
candidates = LINUX_NAMES.flatMap((n) => dirs.map((d) => import_node_path.default.posix.join(d, n)));
|
|
7248
|
+
const cache2 = env.PLAYWRIGHT_BROWSERS_PATH ?? (env.HOME ? `${env.HOME}/.cache/ms-playwright` : "");
|
|
7249
|
+
if (cache2) {
|
|
7250
|
+
let builds = [];
|
|
7251
|
+
try {
|
|
7252
|
+
builds = listDir(cache2).filter((d) => /^chromium-\d+$/.test(d));
|
|
7253
|
+
} catch {
|
|
7254
|
+
}
|
|
7255
|
+
builds.sort((a, b) => Number(b.split("-")[1]) - Number(a.split("-")[1]));
|
|
7256
|
+
for (const b of builds)
|
|
7257
|
+
candidates.push(
|
|
7258
|
+
import_node_path.default.posix.join(cache2, b, "chrome-linux64", "chrome"),
|
|
7259
|
+
import_node_path.default.posix.join(cache2, b, "chrome-linux", "chrome")
|
|
7260
|
+
);
|
|
7261
|
+
}
|
|
7262
|
+
if (wsl) candidates = [...candidates, ...WSL_WINDOWS_PATHS];
|
|
7263
|
+
}
|
|
7264
|
+
return candidates.find((c) => exists(c)) ?? null;
|
|
7265
|
+
}
|
|
7266
|
+
function isWindowsExeFromWsl(browserPath) {
|
|
7267
|
+
return /^\/mnt\/[a-z]\//i.test(browserPath) && /\.exe$/i.test(browserPath);
|
|
7268
|
+
}
|
|
7269
|
+
function browserName(browserPath) {
|
|
7270
|
+
const b = browserPath.toLowerCase();
|
|
7271
|
+
if (b.includes("edge")) return "Edge";
|
|
7272
|
+
if (b.includes("chromium")) return "Chromium";
|
|
7273
|
+
return "Chrome";
|
|
7274
|
+
}
|
|
7275
|
+
function profileDirFor(proxyPort) {
|
|
7276
|
+
return import_node_path.default.join(import_node_os.default.tmpdir(), `astrasync-agent-browser-${proxyPort}`);
|
|
7277
|
+
}
|
|
7278
|
+
function windowsProfileDir(profileDir) {
|
|
7279
|
+
if (/^\/mnt\/[a-z]\//i.test(profileDir)) {
|
|
7280
|
+
try {
|
|
7281
|
+
return (0, import_node_child_process.execFileSync)("wslpath", ["-w", profileDir], { encoding: "utf8" }).trim();
|
|
7282
|
+
} catch {
|
|
7283
|
+
}
|
|
7284
|
+
}
|
|
7285
|
+
return `C:\\Temp\\${import_node_path.default.basename(profileDir)}`;
|
|
7286
|
+
}
|
|
7287
|
+
function certFlag(certSpki) {
|
|
7288
|
+
return `--ignore-certificate-errors-spki-list=${certSpki}`;
|
|
7289
|
+
}
|
|
7290
|
+
var 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.";
|
|
7291
|
+
function launchBrowser(browserPath, proxyPort, url, certSpki, profileDir = profileDirFor(proxyPort), onError) {
|
|
7292
|
+
const dir = isWindowsExeFromWsl(browserPath) ? windowsProfileDir(profileDir) : profileDir;
|
|
7293
|
+
const child = (0, import_node_child_process.spawn)(
|
|
7294
|
+
browserPath,
|
|
7295
|
+
[
|
|
7296
|
+
`--proxy-server=http://127.0.0.1:${proxyPort}`,
|
|
7297
|
+
certFlag(certSpki),
|
|
7298
|
+
`--user-data-dir=${dir}`,
|
|
7299
|
+
"--no-first-run",
|
|
7300
|
+
"--new-window",
|
|
7301
|
+
url
|
|
7302
|
+
],
|
|
7303
|
+
{ detached: true, stdio: "ignore" }
|
|
7304
|
+
);
|
|
7305
|
+
child.on("error", (err) => onError?.(err));
|
|
7306
|
+
child.unref();
|
|
7307
|
+
return child;
|
|
7308
|
+
}
|
|
7309
|
+
function browserCommand(platform2, port, url, certSpki, wsl = false) {
|
|
7310
|
+
const proxy = `--proxy-server="http://127.0.0.1:${port}" ${certFlag(certSpki)}`;
|
|
7311
|
+
if (platform2 === "win32" || wsl) {
|
|
7312
|
+
return `& "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" ${proxy} --user-data-dir="$env:TEMP\\astrasync-agent-${port}" '${url}'`;
|
|
7313
|
+
}
|
|
7314
|
+
const exe = platform2 === "darwin" ? '"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"' : "google-chrome";
|
|
7315
|
+
return `${exe} ${proxy} --user-data-dir="\${TMPDIR:-/tmp}/astrasync-agent-${port}" '${url}'`;
|
|
7316
|
+
}
|
|
7317
|
+
function browserCommandLines(platform2, port, url, certSpki, wsl = isWsl(process.env, platform2)) {
|
|
7318
|
+
if (wsl) {
|
|
7319
|
+
return [
|
|
7320
|
+
" In PowerShell (Windows):",
|
|
7321
|
+
` ${browserCommand("win32", port, url, certSpki)}`,
|
|
7322
|
+
" Or inside WSL (with Chromium installed):",
|
|
7323
|
+
` ${browserCommand("linux", port, url, certSpki)}`,
|
|
7324
|
+
` ${CERT_FLAG_NOTE}`
|
|
7325
|
+
];
|
|
7326
|
+
}
|
|
7327
|
+
return [` ${browserCommand(platform2, port, url, certSpki)}`, ` ${CERT_FLAG_NOTE}`];
|
|
7328
|
+
}
|
|
7329
|
+
function openThroughProxy(opts) {
|
|
7330
|
+
const log = opts.log ?? ((l) => console.log(l));
|
|
7331
|
+
const platform2 = opts.platform ?? process.platform;
|
|
7332
|
+
const env = opts.env ?? process.env;
|
|
7333
|
+
const wsl = opts.wsl ?? isWsl(env, platform2);
|
|
7334
|
+
const found = opts.find ? opts.find() : findChromium(env, platform2, void 0, wsl);
|
|
7335
|
+
if (!found) {
|
|
7336
|
+
log("No Chrome/Edge/Chromium found. Start one through the proxy with:");
|
|
7337
|
+
for (const l of browserCommandLines(platform2, opts.port, opts.url, opts.certSpki, wsl)) log(l);
|
|
7338
|
+
return false;
|
|
7339
|
+
}
|
|
7340
|
+
(opts.launch ?? launchBrowser)(
|
|
7341
|
+
found,
|
|
7342
|
+
opts.port,
|
|
7343
|
+
opts.url,
|
|
7344
|
+
opts.certSpki,
|
|
7345
|
+
profileDirFor(opts.port),
|
|
7346
|
+
(err) => log(`Couldn't start ${browserName(found)}: ${err.message}`)
|
|
7347
|
+
);
|
|
7348
|
+
log(`Opened ${browserName(found)} through the proxy.`);
|
|
7349
|
+
log(CERT_FLAG_NOTE);
|
|
7350
|
+
if (wsl && isWindowsExeFromWsl(found)) {
|
|
7351
|
+
log(
|
|
7352
|
+
"If the Windows browser can't reach the proxy, install Chromium inside WSL (e.g. npx playwright install chromium)."
|
|
7353
|
+
);
|
|
7354
|
+
}
|
|
7355
|
+
return true;
|
|
7356
|
+
}
|
|
7357
|
+
|
|
7158
7358
|
// src/fleet/browse-proxy.ts
|
|
7159
7359
|
var HOP_BY_HOP = /* @__PURE__ */ new Set([
|
|
7160
7360
|
"connection",
|
|
@@ -7171,10 +7371,33 @@ function cleanHeaders(h) {
|
|
|
7171
7371
|
for (const [k, v] of Object.entries(h)) if (!HOP_BY_HOP.has(k) && v !== void 0) out[k] = v;
|
|
7172
7372
|
return out;
|
|
7173
7373
|
}
|
|
7374
|
+
function helpPage(port, certSpki) {
|
|
7375
|
+
const proxy = `http://127.0.0.1:${port}`;
|
|
7376
|
+
return `<!doctype html>
|
|
7377
|
+
<html lang="en"><head><meta charset="utf-8"><title>AstraSync device proxy</title>
|
|
7378
|
+
<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>
|
|
7379
|
+
</head><body>
|
|
7380
|
+
<h1>AstraSync device proxy</h1>
|
|
7381
|
+
<p>This is AstraSync's Local Guard device proxy. It only works as a browser proxy — it isn't a page to open.</p>
|
|
7382
|
+
<h2>Open the sign-in link</h2>
|
|
7383
|
+
<ul>
|
|
7384
|
+
<li>Run <code>astrasync guard browse --open</code> — it opens a browser that goes through this proxy.</li>
|
|
7385
|
+
<li>Or start Chrome, Edge or Chromium with <code>--proxy-server=${proxy} ${certFlag(certSpki)}</code> and a separate profile (<code>--user-data-dir=<an empty folder></code>), then open the sign-in link in it.</li>
|
|
7386
|
+
</ul>
|
|
7387
|
+
<p>Chrome shows a warning bar about that flag. That is expected — it trusts only this proxy's certificate — so close the bar.</p>
|
|
7388
|
+
<p>Keep <code>astrasync guard browse</code> running while you use the session.</p>
|
|
7389
|
+
</body></html>
|
|
7390
|
+
`;
|
|
7391
|
+
}
|
|
7174
7392
|
async function startBrowseProxy(opts) {
|
|
7175
7393
|
const hosts = new Set(opts.hosts.map((h) => h.toLowerCase()));
|
|
7176
7394
|
const deviceHeader = `Device ${opts.deviceCredential}`;
|
|
7177
|
-
const { cert, key } = createLocalCert([...hosts]);
|
|
7395
|
+
const { cert, key, spkiSha256 } = createLocalCert([...hosts]);
|
|
7396
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
7397
|
+
const track = (s) => {
|
|
7398
|
+
sockets.add(s);
|
|
7399
|
+
s.once("close", () => sockets.delete(s));
|
|
7400
|
+
};
|
|
7178
7401
|
function forward(req, res, target) {
|
|
7179
7402
|
const headers = cleanHeaders(req.headers);
|
|
7180
7403
|
headers["x-astra-device"] = deviceHeader;
|
|
@@ -7226,6 +7449,7 @@ async function startBrowseProxy(opts) {
|
|
|
7226
7449
|
},
|
|
7227
7450
|
onConnect
|
|
7228
7451
|
) : import_node_net.default.connect(target.port, target.host, onConnect);
|
|
7452
|
+
track(upstream);
|
|
7229
7453
|
upstream.on("error", () => socket.destroy());
|
|
7230
7454
|
socket.on("error", () => upstream.destroy());
|
|
7231
7455
|
}
|
|
@@ -7257,6 +7481,16 @@ async function startBrowseProxy(opts) {
|
|
|
7257
7481
|
});
|
|
7258
7482
|
});
|
|
7259
7483
|
const server = import_node_http.default.createServer((req, res) => {
|
|
7484
|
+
if ((req.url ?? "").startsWith("/")) {
|
|
7485
|
+
if (req.url === "/favicon.ico") {
|
|
7486
|
+
res.writeHead(404).end();
|
|
7487
|
+
return;
|
|
7488
|
+
}
|
|
7489
|
+
const listening = server.address()?.port ?? 0;
|
|
7490
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
7491
|
+
res.end(helpPage(listening, spkiSha256));
|
|
7492
|
+
return;
|
|
7493
|
+
}
|
|
7260
7494
|
let url;
|
|
7261
7495
|
try {
|
|
7262
7496
|
url = new URL(req.url ?? "");
|
|
@@ -7349,18 +7583,22 @@ async function startBrowseProxy(opts) {
|
|
|
7349
7583
|
upstream.pipe(socket);
|
|
7350
7584
|
socket.pipe(upstream);
|
|
7351
7585
|
});
|
|
7586
|
+
track(upstream);
|
|
7352
7587
|
upstream.on("error", () => socket.destroy());
|
|
7353
7588
|
socket.on("error", () => upstream.destroy());
|
|
7354
7589
|
});
|
|
7590
|
+
server.on("connection", track);
|
|
7355
7591
|
await new Promise((resolve) => server.listen(opts.port ?? 0, "127.0.0.1", resolve));
|
|
7356
7592
|
const port = server.address().port;
|
|
7357
7593
|
return {
|
|
7358
7594
|
port,
|
|
7595
|
+
certSpki: spkiSha256,
|
|
7359
7596
|
close: () => new Promise((resolve) => {
|
|
7360
7597
|
inner.close();
|
|
7361
7598
|
plainInner.close();
|
|
7362
7599
|
server.close(() => resolve());
|
|
7363
7600
|
server.closeAllConnections?.();
|
|
7601
|
+
for (const s of sockets) s.destroy();
|
|
7364
7602
|
})
|
|
7365
7603
|
};
|
|
7366
7604
|
}
|
|
@@ -7447,6 +7685,18 @@ async function runBrowse(paths, opts) {
|
|
|
7447
7685
|
log(
|
|
7448
7686
|
`Device proxy: http://127.0.0.1:${proxy.port} (adds this machine's credential for ${hosts.join(", ")})`
|
|
7449
7687
|
);
|
|
7688
|
+
if (!opts.open) {
|
|
7689
|
+
log("Open it in a browser through the proxy:");
|
|
7690
|
+
for (const l of browserCommandLines(
|
|
7691
|
+
opts.platform ?? process.platform,
|
|
7692
|
+
proxy.port,
|
|
7693
|
+
openUrl,
|
|
7694
|
+
proxy.certSpki,
|
|
7695
|
+
opts.wsl
|
|
7696
|
+
))
|
|
7697
|
+
log(l);
|
|
7698
|
+
log(" (or re-run with --open)");
|
|
7699
|
+
}
|
|
7450
7700
|
log("Playwright:");
|
|
7451
7701
|
log(
|
|
7452
7702
|
` chromium.launch({ proxy: { server: 'http://127.0.0.1:${proxy.port}' } }) + newContext({ ignoreHTTPSErrors: true })`
|
|
@@ -7486,7 +7736,7 @@ async function cmdSetup() {
|
|
|
7486
7736
|
}
|
|
7487
7737
|
async function cmdUpgrade() {
|
|
7488
7738
|
const policyPath = process.argv[4] || DEFAULT_POLICY_PATH;
|
|
7489
|
-
if (!
|
|
7739
|
+
if (!fs2.existsSync(policyPath)) {
|
|
7490
7740
|
console.error(`No policy file found at ${policyPath}`);
|
|
7491
7741
|
console.error('Run "astrasync guard setup" first to create a policy.');
|
|
7492
7742
|
process.exit(1);
|
|
@@ -7514,13 +7764,13 @@ async function cmdUpgrade() {
|
|
|
7514
7764
|
prompt: (message) => prompt.question(message),
|
|
7515
7765
|
print: (message) => console.log(message),
|
|
7516
7766
|
writeConfig: async (filePath, content) => {
|
|
7517
|
-
const dir =
|
|
7518
|
-
if (!
|
|
7519
|
-
|
|
7767
|
+
const dir = path2.dirname(filePath);
|
|
7768
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
7769
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
7520
7770
|
},
|
|
7521
7771
|
readConfig: async (filePath) => {
|
|
7522
7772
|
try {
|
|
7523
|
-
return
|
|
7773
|
+
return fs2.readFileSync(filePath, "utf-8");
|
|
7524
7774
|
} catch {
|
|
7525
7775
|
return null;
|
|
7526
7776
|
}
|
|
@@ -7541,13 +7791,13 @@ async function cmdStart() {
|
|
|
7541
7791
|
let gatewayConfig;
|
|
7542
7792
|
const upgraded = await isUpgraded(DEFAULT_CONFIG_DIR, async (p) => {
|
|
7543
7793
|
try {
|
|
7544
|
-
return
|
|
7794
|
+
return fs2.readFileSync(p, "utf-8");
|
|
7545
7795
|
} catch {
|
|
7546
7796
|
return null;
|
|
7547
7797
|
}
|
|
7548
7798
|
});
|
|
7549
7799
|
if (upgraded) {
|
|
7550
|
-
const config = JSON.parse(
|
|
7800
|
+
const config = JSON.parse(fs2.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
|
|
7551
7801
|
gatewayConfig = {
|
|
7552
7802
|
mode: config.mode,
|
|
7553
7803
|
apiBaseUrl: config.platformUrl ? `${config.platformUrl}/api/v1` : void 0,
|
|
@@ -7555,7 +7805,7 @@ async function cmdStart() {
|
|
|
7555
7805
|
};
|
|
7556
7806
|
console.log(`[AstraSync] Starting in ${config.mode} mode (agent: ${config.agentId})`);
|
|
7557
7807
|
} else {
|
|
7558
|
-
if (!
|
|
7808
|
+
if (!fs2.existsSync(policyPath)) {
|
|
7559
7809
|
console.error(`No policy file found at ${policyPath}`);
|
|
7560
7810
|
console.error('Run "astrasync guard setup" first.');
|
|
7561
7811
|
process.exit(1);
|
|
@@ -7592,7 +7842,7 @@ async function cmdStatus() {
|
|
|
7592
7842
|
const policyPath = process.argv[4] || DEFAULT_POLICY_PATH;
|
|
7593
7843
|
const upgraded = await isUpgraded(DEFAULT_CONFIG_DIR, async (p) => {
|
|
7594
7844
|
try {
|
|
7595
|
-
return
|
|
7845
|
+
return fs2.readFileSync(p, "utf-8");
|
|
7596
7846
|
} catch {
|
|
7597
7847
|
return null;
|
|
7598
7848
|
}
|
|
@@ -7602,7 +7852,7 @@ async function cmdStatus() {
|
|
|
7602
7852
|
console.log(` ${line}`);
|
|
7603
7853
|
}
|
|
7604
7854
|
if (upgraded) {
|
|
7605
|
-
const config = JSON.parse(
|
|
7855
|
+
const config = JSON.parse(fs2.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
|
|
7606
7856
|
console.log(` Mode: ${config.mode}`);
|
|
7607
7857
|
console.log(` Agent: ${config.agentId}`);
|
|
7608
7858
|
console.log(` Platform: ${config.platformUrl}`);
|
|
@@ -7610,7 +7860,7 @@ async function cmdStatus() {
|
|
|
7610
7860
|
} else {
|
|
7611
7861
|
console.log(" Mode: local (free tier)");
|
|
7612
7862
|
}
|
|
7613
|
-
if (
|
|
7863
|
+
if (fs2.existsSync(policyPath)) {
|
|
7614
7864
|
const parsed = parseFile(policyPath);
|
|
7615
7865
|
if (parsed.validation.valid && parsed.policy) {
|
|
7616
7866
|
const p = parsed.policy;
|
|
@@ -7635,7 +7885,7 @@ async function cmdCheck() {
|
|
|
7635
7885
|
(a) => !a.startsWith("--") && a !== "guard" && a !== "check" && a !== process.argv[0] && a !== process.argv[1]
|
|
7636
7886
|
) || DEFAULT_POLICY_PATH;
|
|
7637
7887
|
const trigger = process.argv.find((a) => a.startsWith("--trigger="))?.split("=")[1] || "pre-push";
|
|
7638
|
-
if (!
|
|
7888
|
+
if (!fs2.existsSync(policyPath)) {
|
|
7639
7889
|
console.log("[AstraSync] No policy file \u2014 skipping check");
|
|
7640
7890
|
process.exit(0);
|
|
7641
7891
|
}
|
|
@@ -7647,9 +7897,9 @@ async function cmdCheck() {
|
|
|
7647
7897
|
}
|
|
7648
7898
|
let enforcement = "warn";
|
|
7649
7899
|
const configPath = `${DEFAULT_CONFIG_DIR}/config`;
|
|
7650
|
-
if (
|
|
7900
|
+
if (fs2.existsSync(configPath)) {
|
|
7651
7901
|
try {
|
|
7652
|
-
const config = JSON.parse(
|
|
7902
|
+
const config = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
|
|
7653
7903
|
enforcement = config.gitTriggerEnforcement || "warn";
|
|
7654
7904
|
} catch {
|
|
7655
7905
|
}
|
|
@@ -7712,8 +7962,8 @@ async function cmdUninstallHooks() {
|
|
|
7712
7962
|
}
|
|
7713
7963
|
}
|
|
7714
7964
|
function resolveClaudeHookCommand() {
|
|
7715
|
-
const sibling =
|
|
7716
|
-
if (
|
|
7965
|
+
const sibling = path2.join(__dirname, "astrasync-claude-hook.js");
|
|
7966
|
+
if (fs2.existsSync(sibling)) {
|
|
7717
7967
|
return `"${process.execPath}" "${sibling}"`;
|
|
7718
7968
|
}
|
|
7719
7969
|
return "astrasync-claude-hook";
|
|
@@ -7729,19 +7979,19 @@ function parseClaudeCodeFlags() {
|
|
|
7729
7979
|
};
|
|
7730
7980
|
}
|
|
7731
7981
|
function claudeCodePaths(project) {
|
|
7732
|
-
const root = project ? process.cwd() :
|
|
7982
|
+
const root = project ? process.cwd() : os2.homedir();
|
|
7733
7983
|
return {
|
|
7734
|
-
settingsPath:
|
|
7735
|
-
astrasyncDir:
|
|
7984
|
+
settingsPath: path2.join(root, ".claude", "settings.json"),
|
|
7985
|
+
astrasyncDir: path2.join(root, ".astrasync")
|
|
7736
7986
|
};
|
|
7737
7987
|
}
|
|
7738
7988
|
async function cmdInstallClaudeCode() {
|
|
7739
7989
|
const flags = parseClaudeCodeFlags();
|
|
7740
7990
|
const { settingsPath, astrasyncDir } = claudeCodePaths(flags.project);
|
|
7741
|
-
const policyPath = flags.policyOverride ||
|
|
7742
|
-
const guardJsonPath =
|
|
7991
|
+
const policyPath = flags.policyOverride || path2.join(astrasyncDir, "policy.yaml");
|
|
7992
|
+
const guardJsonPath = path2.join(astrasyncDir, "guard.json");
|
|
7743
7993
|
console.log("\nAstraSync Local Guard for Claude Code \u2014 install\n");
|
|
7744
|
-
if (
|
|
7994
|
+
if (fs2.existsSync(policyPath)) {
|
|
7745
7995
|
console.log(` Policy: using existing ${policyPath}`);
|
|
7746
7996
|
if (ensureGuardSelfProtection(policyPath)) {
|
|
7747
7997
|
console.log(" added guard self-protection patterns to file.write");
|
|
@@ -7749,7 +7999,7 @@ async function cmdInstallClaudeCode() {
|
|
|
7749
7999
|
} else if (flags.wizard) {
|
|
7750
8000
|
const prompt = createPrompt();
|
|
7751
8001
|
try {
|
|
7752
|
-
|
|
8002
|
+
fs2.mkdirSync(path2.dirname(policyPath), { recursive: true });
|
|
7753
8003
|
const wizard = new SetupWizard(prompt);
|
|
7754
8004
|
await wizard.runAndSave(policyPath);
|
|
7755
8005
|
} finally {
|
|
@@ -7758,17 +8008,17 @@ async function cmdInstallClaudeCode() {
|
|
|
7758
8008
|
ensureGuardSelfProtection(policyPath);
|
|
7759
8009
|
console.log(` Policy: created via wizard at ${policyPath}`);
|
|
7760
8010
|
} else {
|
|
7761
|
-
|
|
7762
|
-
|
|
8011
|
+
fs2.mkdirSync(path2.dirname(policyPath), { recursive: true });
|
|
8012
|
+
fs2.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
|
|
7763
8013
|
console.log(` Policy: default coding-agent policy written to ${policyPath}`);
|
|
7764
8014
|
console.log(" (re-run with --wizard for the interactive setup instead)");
|
|
7765
8015
|
}
|
|
7766
|
-
if (!
|
|
8016
|
+
if (!fs2.existsSync(guardJsonPath)) {
|
|
7767
8017
|
const guardSettings = {
|
|
7768
8018
|
posture: flags.active ? "active" : "passive",
|
|
7769
8019
|
governMcp: false
|
|
7770
8020
|
};
|
|
7771
|
-
|
|
8021
|
+
fs2.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
|
|
7772
8022
|
`, "utf-8");
|
|
7773
8023
|
console.log(
|
|
7774
8024
|
` Guard: ${guardJsonPath} (posture: ${guardSettings.posture}, governMcp: false)`
|
|
@@ -7798,7 +8048,7 @@ async function cmdInstallClaudeCode() {
|
|
|
7798
8048
|
}
|
|
7799
8049
|
console.log("\n Next steps:");
|
|
7800
8050
|
console.log(" 1. Start a NEW Claude Code session (settings load at startup).");
|
|
7801
|
-
console.log(` 2. Watch decisions: tail -f ${
|
|
8051
|
+
console.log(` 2. Watch decisions: tail -f ${path2.join(astrasyncDir, "traces")}/*.jsonl`);
|
|
7802
8052
|
console.log(` 3. When the traces look right, set "posture": "active" in ${guardJsonPath}`);
|
|
7803
8053
|
console.log(" to enforce (denies block, approvals use Claude Code's own prompt).");
|
|
7804
8054
|
console.log(' 4. To govern MCP tools too, set "governMcp": true and add');
|
|
@@ -7828,26 +8078,26 @@ async function cmdUninstallClaudeCode() {
|
|
|
7828
8078
|
);
|
|
7829
8079
|
}
|
|
7830
8080
|
function resolveCodexHookCommand() {
|
|
7831
|
-
const sibling =
|
|
7832
|
-
if (
|
|
8081
|
+
const sibling = path2.join(__dirname, "astrasync-codex-hook.js");
|
|
8082
|
+
if (fs2.existsSync(sibling)) {
|
|
7833
8083
|
return `"${process.execPath}" "${sibling}"`;
|
|
7834
8084
|
}
|
|
7835
8085
|
return "astrasync-codex-hook";
|
|
7836
8086
|
}
|
|
7837
8087
|
function codexPaths(project) {
|
|
7838
|
-
const root = project ? process.cwd() :
|
|
8088
|
+
const root = project ? process.cwd() : os2.homedir();
|
|
7839
8089
|
return {
|
|
7840
|
-
hooksPath:
|
|
7841
|
-
astrasyncDir:
|
|
8090
|
+
hooksPath: path2.join(root, ".codex", "hooks.json"),
|
|
8091
|
+
astrasyncDir: path2.join(root, ".astrasync")
|
|
7842
8092
|
};
|
|
7843
8093
|
}
|
|
7844
8094
|
async function cmdInstallCodex() {
|
|
7845
8095
|
const flags = parseClaudeCodeFlags();
|
|
7846
8096
|
const { hooksPath, astrasyncDir } = codexPaths(flags.project);
|
|
7847
|
-
const policyPath = flags.policyOverride ||
|
|
7848
|
-
const guardJsonPath =
|
|
8097
|
+
const policyPath = flags.policyOverride || path2.join(astrasyncDir, "policy.yaml");
|
|
8098
|
+
const guardJsonPath = path2.join(astrasyncDir, "guard.json");
|
|
7849
8099
|
console.log("\nAstraSync Local Guard for Codex CLI \u2014 install\n");
|
|
7850
|
-
if (
|
|
8100
|
+
if (fs2.existsSync(policyPath)) {
|
|
7851
8101
|
console.log(` Policy: using existing ${policyPath} (shared across adapters)`);
|
|
7852
8102
|
if (ensureGuardSelfProtection(policyPath)) {
|
|
7853
8103
|
console.log(" added guard self-protection patterns to file.write");
|
|
@@ -7855,7 +8105,7 @@ async function cmdInstallCodex() {
|
|
|
7855
8105
|
} else if (flags.wizard) {
|
|
7856
8106
|
const prompt = createPrompt();
|
|
7857
8107
|
try {
|
|
7858
|
-
|
|
8108
|
+
fs2.mkdirSync(path2.dirname(policyPath), { recursive: true });
|
|
7859
8109
|
const wizard = new SetupWizard(prompt);
|
|
7860
8110
|
await wizard.runAndSave(policyPath);
|
|
7861
8111
|
} finally {
|
|
@@ -7864,17 +8114,17 @@ async function cmdInstallCodex() {
|
|
|
7864
8114
|
ensureGuardSelfProtection(policyPath);
|
|
7865
8115
|
console.log(` Policy: created via wizard at ${policyPath}`);
|
|
7866
8116
|
} else {
|
|
7867
|
-
|
|
7868
|
-
|
|
8117
|
+
fs2.mkdirSync(path2.dirname(policyPath), { recursive: true });
|
|
8118
|
+
fs2.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
|
|
7869
8119
|
console.log(` Policy: default coding-agent policy written to ${policyPath}`);
|
|
7870
8120
|
console.log(" (re-run with --wizard for the interactive setup instead)");
|
|
7871
8121
|
}
|
|
7872
|
-
if (!
|
|
8122
|
+
if (!fs2.existsSync(guardJsonPath)) {
|
|
7873
8123
|
const guardSettings = {
|
|
7874
8124
|
posture: flags.active ? "active" : "passive",
|
|
7875
8125
|
governMcp: false
|
|
7876
8126
|
};
|
|
7877
|
-
|
|
8127
|
+
fs2.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
|
|
7878
8128
|
`, "utf-8");
|
|
7879
8129
|
console.log(
|
|
7880
8130
|
` Guard: ${guardJsonPath} (posture: ${guardSettings.posture}, governMcp: false)`
|
|
@@ -7893,7 +8143,7 @@ async function cmdInstallCodex() {
|
|
|
7893
8143
|
}
|
|
7894
8144
|
console.log("\n Next steps:");
|
|
7895
8145
|
console.log(" 1. Start a NEW Codex session (hooks load at startup; /hooks to inspect).");
|
|
7896
|
-
console.log(` 2. Watch decisions: tail -f ${
|
|
8146
|
+
console.log(` 2. Watch decisions: tail -f ${path2.join(astrasyncDir, "traces")}/*.jsonl`);
|
|
7897
8147
|
console.log(` 3. When the traces look right, set "posture": "active" in ${guardJsonPath}.`);
|
|
7898
8148
|
console.log(" Denies block; approvals fall through to Codex's own prompt \u2014");
|
|
7899
8149
|
console.log(" except in dontAsk/bypassPermissions modes, where they deny.");
|
|
@@ -7918,13 +8168,13 @@ function printCodexStatus() {
|
|
|
7918
8168
|
const { hooksPath, astrasyncDir } = codexPaths(scope === "project");
|
|
7919
8169
|
let hookInstalled = false;
|
|
7920
8170
|
try {
|
|
7921
|
-
const content =
|
|
8171
|
+
const content = fs2.readFileSync(hooksPath, "utf-8");
|
|
7922
8172
|
hookInstalled = content.includes(ASTRASYNC_CODEX_HOOK_MARKER);
|
|
7923
8173
|
} catch {
|
|
7924
8174
|
}
|
|
7925
8175
|
if (hookInstalled) {
|
|
7926
|
-
const policyPath =
|
|
7927
|
-
const policy =
|
|
8176
|
+
const policyPath = path2.join(astrasyncDir, "policy.yaml");
|
|
8177
|
+
const policy = fs2.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
|
|
7928
8178
|
console.log(` Codex CLI (${scope}): hook installed, ${policy}`);
|
|
7929
8179
|
}
|
|
7930
8180
|
}
|
|
@@ -7934,24 +8184,24 @@ function printClaudeCodeStatus() {
|
|
|
7934
8184
|
const { settingsPath, astrasyncDir } = claudeCodePaths(scope === "project");
|
|
7935
8185
|
let hookInstalled = false;
|
|
7936
8186
|
try {
|
|
7937
|
-
const content =
|
|
8187
|
+
const content = fs2.readFileSync(settingsPath, "utf-8");
|
|
7938
8188
|
hookInstalled = content.includes(ASTRASYNC_HOOK_MARKER);
|
|
7939
8189
|
} catch {
|
|
7940
8190
|
}
|
|
7941
|
-
const policyPath =
|
|
7942
|
-
const guardJsonPath =
|
|
8191
|
+
const policyPath = path2.join(astrasyncDir, "policy.yaml");
|
|
8192
|
+
const guardJsonPath = path2.join(astrasyncDir, "guard.json");
|
|
7943
8193
|
let posture = "";
|
|
7944
|
-
if (
|
|
8194
|
+
if (fs2.existsSync(guardJsonPath)) {
|
|
7945
8195
|
try {
|
|
7946
|
-
const settings = JSON.parse(
|
|
8196
|
+
const settings = JSON.parse(fs2.readFileSync(guardJsonPath, "utf-8"));
|
|
7947
8197
|
posture = settings.posture ? `, posture: ${settings.posture}` : "";
|
|
7948
8198
|
} catch {
|
|
7949
8199
|
posture = ", guard.json unreadable";
|
|
7950
8200
|
}
|
|
7951
8201
|
}
|
|
7952
|
-
if (hookInstalled ||
|
|
8202
|
+
if (hookInstalled || fs2.existsSync(policyPath)) {
|
|
7953
8203
|
const hook = hookInstalled ? "hook installed" : "hook NOT installed";
|
|
7954
|
-
const policy =
|
|
8204
|
+
const policy = fs2.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
|
|
7955
8205
|
console.log(` Claude Code (${scope}): ${hook}, ${policy}${posture}`);
|
|
7956
8206
|
}
|
|
7957
8207
|
}
|
|
@@ -7997,14 +8247,21 @@ async function cmdFleetStatus() {
|
|
|
7997
8247
|
async function cmdBrowse() {
|
|
7998
8248
|
try {
|
|
7999
8249
|
const port = flagValue("--port");
|
|
8000
|
-
const
|
|
8250
|
+
const open = process.argv.includes("--open");
|
|
8251
|
+
const { proxy, openUrl } = await runBrowse(fleetPaths(), {
|
|
8001
8252
|
checkpoint: flagValue("--checkpoint") ?? "",
|
|
8002
8253
|
slug: flagValue("--slug") ?? "",
|
|
8003
8254
|
upstream: flagValue("--upstream"),
|
|
8004
8255
|
port: port ? Number(port) : void 0,
|
|
8005
|
-
extraHosts: flagValue("--host") ? [flagValue("--host")] : []
|
|
8256
|
+
extraHosts: flagValue("--host") ? [flagValue("--host")] : [],
|
|
8257
|
+
open
|
|
8006
8258
|
});
|
|
8259
|
+
if (open) openThroughProxy({ port: proxy.port, url: openUrl, certSpki: proxy.certSpki });
|
|
8260
|
+
let stopping = false;
|
|
8007
8261
|
const stop = async () => {
|
|
8262
|
+
if (stopping) process.exit(0);
|
|
8263
|
+
stopping = true;
|
|
8264
|
+
setTimeout(() => process.exit(0), 2e3).unref();
|
|
8008
8265
|
await proxy.close();
|
|
8009
8266
|
process.exit(0);
|
|
8010
8267
|
};
|
|
@@ -8039,7 +8296,7 @@ async function cmdGitRoute() {
|
|
|
8039
8296
|
}
|
|
8040
8297
|
async function cmdGitCredential() {
|
|
8041
8298
|
const action = process.argv[process.argv.indexOf("git-credential") + 1] ?? "";
|
|
8042
|
-
const input =
|
|
8299
|
+
const input = fs2.readFileSync(0, "utf-8");
|
|
8043
8300
|
process.stdout.write(runGitCredential(action, input));
|
|
8044
8301
|
}
|
|
8045
8302
|
async function cmdManagedSettings() {
|
|
@@ -8143,8 +8400,9 @@ async function main() {
|
|
|
8143
8400
|
" browse Sign an agent in to your dashboard from this machine and"
|
|
8144
8401
|
);
|
|
8145
8402
|
console.log(
|
|
8146
|
-
" run its device proxy (--checkpoint <url> --slug <agent
|
|
8403
|
+
" run its device proxy (--checkpoint <url> --slug <agent>;"
|
|
8147
8404
|
);
|
|
8405
|
+
console.log(" --open to start a browser through the proxy)");
|
|
8148
8406
|
console.log(" enroll <token> Enrol this machine in your organisation fleet");
|
|
8149
8407
|
console.log(" (--api-base-url <url>, --system for machine scope)");
|
|
8150
8408
|
console.log(" fleet-status Show fleet enrolment, signed policy and heartbeat");
|