@astrasyncai/verification-gateway 5.10.0 → 5.12.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/README.md +4 -0
- package/dist/adapters/express.d.mts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +359 -30
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +359 -30
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +5 -1
- package/dist/adapters/mcp.d.ts +5 -1
- package/dist/adapters/mcp.js +168 -41
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +168 -41
- 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 +352 -13
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +352 -13
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +9 -1
- package/dist/adapters/sdk.d.ts +9 -1
- package/dist/adapters/sdk.js +100 -8
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +100 -8
- 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 +805 -16
- package/dist/bin/astrasync-codex-hook.js +805 -16
- package/dist/bin/astrasync-guard.js +894 -71
- package/dist/bin/astrasync.js +951 -74
- package/dist/browser/background.js +284 -7
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +284 -7
- package/dist/browser/background.mjs.map +1 -1
- package/dist/cli/index.d.mts +8 -8
- package/dist/cli/index.d.ts +8 -8
- package/dist/cli/index.js +22 -64
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +22 -64
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.d.mts +5 -0
- package/dist/codex/index.d.ts +5 -0
- package/dist/codex/index.js +821 -24
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +823 -26
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/extension.d.mts +9 -2
- package/dist/cursor/extension.d.ts +9 -2
- package/dist/cursor/extension.js +787 -11
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +789 -11
- 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 +128 -13
- package/dist/edge-core/index.d.ts +128 -13
- package/dist/edge-core/index.js +337 -55
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +334 -55
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/enrolment-store-8DyaIOLe.d.mts +26 -0
- package/dist/enrolment-store-8DyaIOLe.d.ts +26 -0
- package/dist/gateway/gateway.js +284 -7
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +284 -7
- 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 +215 -5
- package/dist/index.d.ts +215 -5
- package/dist/index.js +3756 -80
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3748 -80
- package/dist/index.mjs.map +1 -1
- package/dist/platform-signatures.js +4 -1
- package/dist/platform-signatures.js.map +1 -1
- package/dist/platform-signatures.mjs +4 -1
- package/dist/platform-signatures.mjs.map +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 +1 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +1 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-CTmUpNpS.d.mts → types-BCkwBA84.d.mts} +12 -0
- package/dist/{types-CTmUpNpS.d.ts → types-BCkwBA84.d.ts} +12 -0
- package/dist/{types-CtFXmDzm.d.ts → types-C_b4QTyO.d.ts} +18 -0
- package/dist/{types-DQrn_kei.d.mts → types-DlshIIR-.d.mts} +18 -0
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +35 -4
- package/dist/verify.d.ts +35 -4
- package/dist/verify.js +130 -16
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +129 -16
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bin/astrasync.js
CHANGED
|
@@ -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.
|
|
39
|
+
SDK_VERSION = "5.12.0";
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -4095,7 +4095,6 @@ var init_upgrade = __esm({
|
|
|
4095
4095
|
"src/cli/upgrade.ts"() {
|
|
4096
4096
|
"use strict";
|
|
4097
4097
|
init_http();
|
|
4098
|
-
init_pdlss_parser();
|
|
4099
4098
|
UpgradeFlow = class {
|
|
4100
4099
|
constructor(config, callbacks, trigger) {
|
|
4101
4100
|
this.config = config;
|
|
@@ -4155,27 +4154,23 @@ var init_upgrade = __esm({
|
|
|
4155
4154
|
*/
|
|
4156
4155
|
async continueUpgrade(apiKey, kydId) {
|
|
4157
4156
|
const { print, prompt } = this.callbacks;
|
|
4158
|
-
print("\n[3/5]
|
|
4159
|
-
print(
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
]
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
return uploadResult;
|
|
4176
|
-
}
|
|
4177
|
-
print(" Policy uploaded. Same rules, now with trust scoring and audit.");
|
|
4178
|
-
print(` Agent registered: ${uploadResult.agentId}`);
|
|
4157
|
+
print("\n[3/5] Link your agent");
|
|
4158
|
+
print(
|
|
4159
|
+
` Local policy ${this.config.policyPath} stays on this machine (${this.config.policy.purposes.length} purpose rules).`
|
|
4160
|
+
);
|
|
4161
|
+
print(" Register the agent in the AstraSync dashboard (or with `astrasync register`) and");
|
|
4162
|
+
print(" paste its ASTRA- id so synced decisions are attributed to it.\n");
|
|
4163
|
+
const agentInput = (await prompt(" Agent ASTRA- id (Enter to skip): ")).trim();
|
|
4164
|
+
let agentId;
|
|
4165
|
+
if (agentInput) {
|
|
4166
|
+
if (!/^ASTRA-[A-Za-z0-9_-]+$/.test(agentInput)) {
|
|
4167
|
+
return { success: false, error: `Not an ASTRA- agent id: ${agentInput}` };
|
|
4168
|
+
}
|
|
4169
|
+
agentId = agentInput;
|
|
4170
|
+
print(` Agent: ${agentId}`);
|
|
4171
|
+
} else {
|
|
4172
|
+
print(" No agent linked \u2014 decisions sync under your API key only.");
|
|
4173
|
+
}
|
|
4179
4174
|
if (kydId) {
|
|
4180
4175
|
print(` Developer: ${kydId}`);
|
|
4181
4176
|
}
|
|
@@ -4186,23 +4181,23 @@ var init_upgrade = __esm({
|
|
|
4186
4181
|
const mode = modeInput.trim().toLowerCase() === "online" ? "online" : "hybrid";
|
|
4187
4182
|
print(`
|
|
4188
4183
|
[5/5] Activating ${mode} verification`);
|
|
4189
|
-
await this.writeLocalConfig(apiKey,
|
|
4184
|
+
await this.writeLocalConfig(apiKey, agentId, mode, kydId);
|
|
4190
4185
|
print(` Config written to ${this.configDir}/config`);
|
|
4191
4186
|
print(` Mode: ${mode}`);
|
|
4192
4187
|
print(" Done!\n");
|
|
4193
|
-
print(` Your agent (${
|
|
4188
|
+
if (agentId) print(` Your agent (${agentId}) is verified on the AstraSync network.`);
|
|
4194
4189
|
if (mode === "hybrid") {
|
|
4195
4190
|
print(" Local evaluation active for speed. Cloud sync for audit and trust scoring.");
|
|
4196
4191
|
} else {
|
|
4197
4192
|
print(" Trust scoring, audit trails, and blockchain recording are active.");
|
|
4198
4193
|
}
|
|
4199
4194
|
print(`
|
|
4200
|
-
Dashboard: ${this.platformUrl}/agents
|
|
4195
|
+
Dashboard: ${this.platformUrl}/agents${agentId ? `/${agentId}` : ""}
|
|
4201
4196
|
`);
|
|
4202
4197
|
return {
|
|
4203
4198
|
success: true,
|
|
4204
4199
|
apiKey,
|
|
4205
|
-
agentId
|
|
4200
|
+
agentId,
|
|
4206
4201
|
kydId,
|
|
4207
4202
|
mode
|
|
4208
4203
|
};
|
|
@@ -4255,44 +4250,6 @@ var init_upgrade = __esm({
|
|
|
4255
4250
|
return { valid: true, error: `Could not reach platform (proceeding anyway)` };
|
|
4256
4251
|
}
|
|
4257
4252
|
}
|
|
4258
|
-
/**
|
|
4259
|
-
* Upload local policy to the AstraSync cloud.
|
|
4260
|
-
* YAML -> cloud PDLSS boundary, 1:1 mapping, no lossy transformation.
|
|
4261
|
-
*/
|
|
4262
|
-
async uploadPolicy(apiKey) {
|
|
4263
|
-
const policyYaml = toYaml(this.config.policy);
|
|
4264
|
-
try {
|
|
4265
|
-
const response = await sdkFetch(`${this.platformUrl}/api/v1/agents/policies/upload`, {
|
|
4266
|
-
method: "POST",
|
|
4267
|
-
headers: {
|
|
4268
|
-
"Content-Type": "application/json",
|
|
4269
|
-
Authorization: `Bearer ${apiKey}`
|
|
4270
|
-
},
|
|
4271
|
-
body: JSON.stringify({
|
|
4272
|
-
policy: policyYaml,
|
|
4273
|
-
policyObject: this.config.policy,
|
|
4274
|
-
format: "yaml",
|
|
4275
|
-
source: "local-guard-upgrade",
|
|
4276
|
-
name: this.config.policy.name
|
|
4277
|
-
})
|
|
4278
|
-
});
|
|
4279
|
-
if (!response.ok) {
|
|
4280
|
-
const body = await response.text();
|
|
4281
|
-
return { success: false, error: `Upload failed (${response.status}): ${body}` };
|
|
4282
|
-
}
|
|
4283
|
-
const data = await response.json();
|
|
4284
|
-
return {
|
|
4285
|
-
success: true,
|
|
4286
|
-
agentId: data.agentId || "ASTRA-PENDING",
|
|
4287
|
-
kydId: data.kydId
|
|
4288
|
-
};
|
|
4289
|
-
} catch (err) {
|
|
4290
|
-
return {
|
|
4291
|
-
success: false,
|
|
4292
|
-
error: `Network error: ${err instanceof Error ? err.message : String(err)}`
|
|
4293
|
-
};
|
|
4294
|
-
}
|
|
4295
|
-
}
|
|
4296
4253
|
/**
|
|
4297
4254
|
* Write the config file for the chosen mode.
|
|
4298
4255
|
*/
|
|
@@ -5100,6 +5057,20 @@ var init_well_known = __esm({
|
|
|
5100
5057
|
}
|
|
5101
5058
|
});
|
|
5102
5059
|
|
|
5060
|
+
// src/failure-posture.ts
|
|
5061
|
+
var REJECTED_IDENTITY_DIMENSIONS, UNKNOWN_AGENT_GUIDANCE;
|
|
5062
|
+
var init_failure_posture = __esm({
|
|
5063
|
+
"src/failure-posture.ts"() {
|
|
5064
|
+
"use strict";
|
|
5065
|
+
REJECTED_IDENTITY_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
5066
|
+
"identity.invalid_agent_id",
|
|
5067
|
+
"identity.unknown_agent",
|
|
5068
|
+
"agent.lookup"
|
|
5069
|
+
]);
|
|
5070
|
+
UNKNOWN_AGENT_GUIDANCE = "Register your agent with AstraSync (or, inside an MCP host, add the AstraSync MCP connector for keyless, human-approved registration), then present the canonical ASTRA-* id issued after owner approval.";
|
|
5071
|
+
}
|
|
5072
|
+
});
|
|
5073
|
+
|
|
5103
5074
|
// src/verify.ts
|
|
5104
5075
|
async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
5105
5076
|
initCheckPerformed = true;
|
|
@@ -5298,11 +5269,43 @@ function createGuidanceResponse(_config, reason, options = {}) {
|
|
|
5298
5269
|
// coercing the absent recommendation to a policy 'deny'. Misconfig is
|
|
5299
5270
|
// deterministic and intentionally NOT marked retryable.
|
|
5300
5271
|
...isApiError && { retryable: true },
|
|
5272
|
+
...options.httpStatus !== void 0 && { httpStatus: options.httpStatus },
|
|
5273
|
+
...options.retryAfterMs !== void 0 && { retryAfterMs: options.retryAfterMs },
|
|
5301
5274
|
correlationId: options.correlationId,
|
|
5302
5275
|
attemptId: options.attemptId,
|
|
5303
5276
|
verifiedAt: /* @__PURE__ */ new Date()
|
|
5304
5277
|
};
|
|
5305
5278
|
}
|
|
5279
|
+
async function readJsonSafe(response) {
|
|
5280
|
+
try {
|
|
5281
|
+
const parsed = await response.json();
|
|
5282
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
5283
|
+
} catch {
|
|
5284
|
+
return {};
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
function rejectedIdentityGuidance(failures) {
|
|
5288
|
+
const f = failures?.find((x) => REJECTED_IDENTITY_DIMENSIONS.has(x.dimension));
|
|
5289
|
+
if (!f) return void 0;
|
|
5290
|
+
if (f.dimension === "agent.lookup") {
|
|
5291
|
+
return `The presented agent identifier is not registered with AstraSync. ${UNKNOWN_AGENT_GUIDANCE}`;
|
|
5292
|
+
}
|
|
5293
|
+
return f.guidance ? `${f.message} ${f.guidance}` : f.message;
|
|
5294
|
+
}
|
|
5295
|
+
function parseRetryAfterMs(response) {
|
|
5296
|
+
const raw = response.headers?.get?.("retry-after");
|
|
5297
|
+
if (!raw) return void 0;
|
|
5298
|
+
const trimmed = raw.trim();
|
|
5299
|
+
let ms;
|
|
5300
|
+
if (/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
5301
|
+
ms = Number(trimmed) * 1e3;
|
|
5302
|
+
} else {
|
|
5303
|
+
const at = Date.parse(trimmed);
|
|
5304
|
+
if (Number.isNaN(at)) return void 0;
|
|
5305
|
+
ms = at - Date.now();
|
|
5306
|
+
}
|
|
5307
|
+
return Math.min(MAX_RETRY_AFTER_MS, Math.max(0, Math.round(ms)));
|
|
5308
|
+
}
|
|
5306
5309
|
async function callVerifyAccessAPI(config, request2) {
|
|
5307
5310
|
const { credentials, ...requestData } = request2;
|
|
5308
5311
|
const body = {
|
|
@@ -5359,7 +5362,8 @@ async function callVerifyAccessAPI(config, request2) {
|
|
|
5359
5362
|
body: JSON.stringify(body),
|
|
5360
5363
|
signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 1e4)
|
|
5361
5364
|
});
|
|
5362
|
-
const data = await response
|
|
5365
|
+
const data = await readJsonSafe(response);
|
|
5366
|
+
const retryAfterMs = parseRetryAfterMs(response);
|
|
5363
5367
|
if (response.status === 410) {
|
|
5364
5368
|
return {
|
|
5365
5369
|
success: true,
|
|
@@ -5397,6 +5401,23 @@ async function callVerifyAccessAPI(config, request2) {
|
|
|
5397
5401
|
}
|
|
5398
5402
|
};
|
|
5399
5403
|
}
|
|
5404
|
+
if (response.status === 404 && credentials.astraId && (data?.error === "AGENT_NOT_FOUND" || typeof data?.message === "string" && /agent/i.test(data.message))) {
|
|
5405
|
+
return {
|
|
5406
|
+
success: true,
|
|
5407
|
+
attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
|
|
5408
|
+
access: {
|
|
5409
|
+
allowed: false,
|
|
5410
|
+
reason: "unknown_agent",
|
|
5411
|
+
failures: [
|
|
5412
|
+
{
|
|
5413
|
+
dimension: "identity.unknown_agent",
|
|
5414
|
+
message: "The presented agent identifier is not registered with AstraSync.",
|
|
5415
|
+
guidance: UNKNOWN_AGENT_GUIDANCE
|
|
5416
|
+
}
|
|
5417
|
+
]
|
|
5418
|
+
}
|
|
5419
|
+
};
|
|
5420
|
+
}
|
|
5400
5421
|
if (!response.ok) {
|
|
5401
5422
|
if (response.status === 401) {
|
|
5402
5423
|
return {
|
|
@@ -5411,9 +5432,11 @@ async function callVerifyAccessAPI(config, request2) {
|
|
|
5411
5432
|
}
|
|
5412
5433
|
return {
|
|
5413
5434
|
success: false,
|
|
5414
|
-
error: data.message || data.error || `API returned ${response.status}`,
|
|
5435
|
+
error: typeof data?.message === "string" && data.message || typeof data?.error === "string" && data.error || `API returned ${response.status}`,
|
|
5415
5436
|
correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
|
|
5416
|
-
attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
|
|
5437
|
+
attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
|
|
5438
|
+
httpStatus: response.status,
|
|
5439
|
+
...retryAfterMs !== void 0 && { retryAfterMs }
|
|
5417
5440
|
};
|
|
5418
5441
|
}
|
|
5419
5442
|
return data;
|
|
@@ -5485,12 +5508,14 @@ async function verify(config, request2, options) {
|
|
|
5485
5508
|
correlationId: apiResponse.correlationId,
|
|
5486
5509
|
attemptId: apiResponse.attemptId,
|
|
5487
5510
|
remediation: apiResponse.remediation,
|
|
5511
|
+
httpStatus: apiResponse.httpStatus,
|
|
5512
|
+
retryAfterMs: apiResponse.retryAfterMs,
|
|
5488
5513
|
urls
|
|
5489
5514
|
});
|
|
5490
5515
|
}
|
|
5491
5516
|
if (!apiResponse.access?.allowed) {
|
|
5492
5517
|
const aggregatedFailures = apiResponse.access?.failures;
|
|
5493
|
-
const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true;
|
|
5518
|
+
const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
|
|
5494
5519
|
const result2 = {
|
|
5495
5520
|
identityVerified: idVerifiedFromBackend,
|
|
5496
5521
|
policyAllowed: false,
|
|
@@ -5502,7 +5527,10 @@ async function verify(config, request2, options) {
|
|
|
5502
5527
|
requiresStepUp: apiResponse.access?.requiresStepUp,
|
|
5503
5528
|
requiresApproval: apiResponse.access?.requiresApproval,
|
|
5504
5529
|
guidance: {
|
|
5505
|
-
|
|
5530
|
+
// 5.12.0: a presented-but-rejected id (malformed / unknown) carries
|
|
5531
|
+
// actionable registration guidance on its failure — surface it
|
|
5532
|
+
// instead of the bare machine reason ('unknown_agent').
|
|
5533
|
+
message: rejectedIdentityGuidance(aggregatedFailures) ?? (apiResponse.access?.reason || "Access denied by PDLSS policy"),
|
|
5506
5534
|
registrationUrl: urls?.registrationUrl ?? "",
|
|
5507
5535
|
documentationUrl: urls?.documentationUrl ?? "",
|
|
5508
5536
|
// 5.1.0: anonymous callers land on this branch — steer MCP-hosted
|
|
@@ -5602,7 +5630,7 @@ async function verify(config, request2, options) {
|
|
|
5602
5630
|
registrationUrl: urls?.registrationUrl ?? "",
|
|
5603
5631
|
documentationUrl: urls?.documentationUrl ?? ""
|
|
5604
5632
|
};
|
|
5605
|
-
} else if (result.recommendation === "step_up_required") {
|
|
5633
|
+
} else if (result.recommendation === "step_up_required" && result.identityVerified) {
|
|
5606
5634
|
result.requiresStepUp = true;
|
|
5607
5635
|
result.denialReasons = serverDenialReasons ?? result.recommendationReasons ?? ["Step-up verification required"];
|
|
5608
5636
|
}
|
|
@@ -5612,7 +5640,7 @@ async function verify(config, request2, options) {
|
|
|
5612
5640
|
}
|
|
5613
5641
|
return result;
|
|
5614
5642
|
}
|
|
5615
|
-
var DEFAULT_CONFIG, initCheckPerformed, deprecationWarningShown, verificationCache, DEFAULT_CACHE_TTL_SECONDS;
|
|
5643
|
+
var DEFAULT_CONFIG, initCheckPerformed, deprecationWarningShown, verificationCache, DEFAULT_CACHE_TTL_SECONDS, MAX_RETRY_AFTER_MS;
|
|
5616
5644
|
var init_verify = __esm({
|
|
5617
5645
|
"src/verify.ts"() {
|
|
5618
5646
|
"use strict";
|
|
@@ -5620,6 +5648,7 @@ var init_verify = __esm({
|
|
|
5620
5648
|
init_trust_levels();
|
|
5621
5649
|
init_version();
|
|
5622
5650
|
init_well_known();
|
|
5651
|
+
init_failure_posture();
|
|
5623
5652
|
DEFAULT_CONFIG = {
|
|
5624
5653
|
apiBaseUrl: "https://astrasync.ai/api",
|
|
5625
5654
|
// minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.
|
|
@@ -5633,6 +5662,7 @@ var init_verify = __esm({
|
|
|
5633
5662
|
deprecationWarningShown = false;
|
|
5634
5663
|
verificationCache = /* @__PURE__ */ new Map();
|
|
5635
5664
|
DEFAULT_CACHE_TTL_SECONDS = 60;
|
|
5665
|
+
MAX_RETRY_AFTER_MS = 15 * 60 * 1e3;
|
|
5636
5666
|
}
|
|
5637
5667
|
});
|
|
5638
5668
|
|
|
@@ -5755,12 +5785,223 @@ var init_classify = __esm({
|
|
|
5755
5785
|
});
|
|
5756
5786
|
|
|
5757
5787
|
// src/edge-core/events.ts
|
|
5788
|
+
async function withBudget(promise, ms) {
|
|
5789
|
+
let timer;
|
|
5790
|
+
try {
|
|
5791
|
+
return await Promise.race([
|
|
5792
|
+
promise,
|
|
5793
|
+
new Promise((resolve) => {
|
|
5794
|
+
timer = setTimeout(() => resolve(void 0), ms);
|
|
5795
|
+
})
|
|
5796
|
+
]);
|
|
5797
|
+
} catch {
|
|
5798
|
+
return void 0;
|
|
5799
|
+
} finally {
|
|
5800
|
+
if (timer) clearTimeout(timer);
|
|
5801
|
+
}
|
|
5802
|
+
}
|
|
5803
|
+
var destKeyOf, BeaconBuffer, beaconBuffer;
|
|
5758
5804
|
var init_events = __esm({
|
|
5759
5805
|
"src/edge-core/events.ts"() {
|
|
5760
5806
|
"use strict";
|
|
5807
|
+
init_http();
|
|
5761
5808
|
init_metadata_capture();
|
|
5762
|
-
|
|
5809
|
+
init_version();
|
|
5763
5810
|
init_request();
|
|
5811
|
+
destKeyOf = (d) => `${d.apiBaseUrl}\0${d.apiKey ?? ""}\0${d.counterparty}\0${d.adapter ?? ""}`;
|
|
5812
|
+
BeaconBuffer = class {
|
|
5813
|
+
constructor(options = {}) {
|
|
5814
|
+
this.queue = [];
|
|
5815
|
+
this.droppedSinceLast = 0;
|
|
5816
|
+
this.droppedTotal = 0;
|
|
5817
|
+
this.deliveredTotal = 0;
|
|
5818
|
+
this.backoffUntil = 0;
|
|
5819
|
+
this.collapse = /* @__PURE__ */ new Map();
|
|
5820
|
+
/** Keys the backend answered 401 for — later batches go unauthenticated. */
|
|
5821
|
+
this.rejectedKeys = /* @__PURE__ */ new Set();
|
|
5822
|
+
this.opts = {
|
|
5823
|
+
maxItems: options.maxItems ?? 1e3,
|
|
5824
|
+
maxBatch: options.maxBatch ?? 100,
|
|
5825
|
+
maxAttempts: options.maxAttempts ?? 3,
|
|
5826
|
+
collapseWindowMs: options.collapseWindowMs ?? 6e4,
|
|
5827
|
+
collapseMaxKeys: options.collapseMaxKeys ?? 1e3,
|
|
5828
|
+
requestTimeoutMs: options.requestTimeoutMs ?? 1e4,
|
|
5829
|
+
retryBackoffMs: options.retryBackoffMs ?? 1e3,
|
|
5830
|
+
now: options.now ?? Date.now
|
|
5831
|
+
};
|
|
5832
|
+
}
|
|
5833
|
+
/**
|
|
5834
|
+
* Queue one beacon. With a `collapseKey`, a repeat inside the window is
|
|
5835
|
+
* merged into a held aggregate (sent when the window closes) instead of
|
|
5836
|
+
* becoming its own item.
|
|
5837
|
+
*/
|
|
5838
|
+
enqueue(dest, item, collapseKey) {
|
|
5839
|
+
const now = this.opts.now();
|
|
5840
|
+
if (collapseKey) {
|
|
5841
|
+
const entry = this.collapse.get(collapseKey);
|
|
5842
|
+
if (entry && now - entry.firstSeenAt < this.opts.collapseWindowMs) {
|
|
5843
|
+
for (const target of [entry.first, entry.held]) {
|
|
5844
|
+
if (target && !target.inFlight && this.queue.includes(target)) {
|
|
5845
|
+
target.item.repeatCount = (target.item.repeatCount ?? 1) + 1;
|
|
5846
|
+
return;
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
5849
|
+
const held = this.push(
|
|
5850
|
+
dest,
|
|
5851
|
+
{ ...item, repeatCount: 1 },
|
|
5852
|
+
entry.firstSeenAt + this.opts.collapseWindowMs
|
|
5853
|
+
);
|
|
5854
|
+
entry.held = held;
|
|
5855
|
+
return;
|
|
5856
|
+
}
|
|
5857
|
+
this.collapse.delete(collapseKey);
|
|
5858
|
+
if (this.collapse.size >= this.opts.collapseMaxKeys) {
|
|
5859
|
+
const oldest = this.collapse.keys().next().value;
|
|
5860
|
+
if (oldest !== void 0) this.collapse.delete(oldest);
|
|
5861
|
+
}
|
|
5862
|
+
const fresh = { firstSeenAt: now };
|
|
5863
|
+
this.collapse.set(collapseKey, fresh);
|
|
5864
|
+
fresh.first = this.push(dest, item, now);
|
|
5865
|
+
return;
|
|
5866
|
+
}
|
|
5867
|
+
this.push(dest, item, now);
|
|
5868
|
+
}
|
|
5869
|
+
/**
|
|
5870
|
+
* Deliver every eligible queued item. Concurrent callers share ONE
|
|
5871
|
+
* in-flight drain (it survives the caller's budget and a Lambda freeze),
|
|
5872
|
+
* so nothing in flight is ever sent twice; items queued while it runs are
|
|
5873
|
+
* picked up by the same drain.
|
|
5874
|
+
*/
|
|
5875
|
+
flush() {
|
|
5876
|
+
if (!this.inFlight) {
|
|
5877
|
+
this.inFlight = this.drain().finally(() => {
|
|
5878
|
+
this.inFlight = void 0;
|
|
5879
|
+
});
|
|
5880
|
+
}
|
|
5881
|
+
return this.inFlight;
|
|
5882
|
+
}
|
|
5883
|
+
/** Counters for tests and diagnostics. */
|
|
5884
|
+
stats() {
|
|
5885
|
+
return {
|
|
5886
|
+
queueDepth: this.queue.length,
|
|
5887
|
+
droppedSinceLast: this.droppedSinceLast,
|
|
5888
|
+
droppedTotal: this.droppedTotal,
|
|
5889
|
+
deliveredTotal: this.deliveredTotal,
|
|
5890
|
+
flushing: this.inFlight !== void 0
|
|
5891
|
+
};
|
|
5892
|
+
}
|
|
5893
|
+
push(dest, item, eligibleAt) {
|
|
5894
|
+
while (this.queue.length >= this.opts.maxItems) this.dropOldest();
|
|
5895
|
+
const entry = {
|
|
5896
|
+
dest,
|
|
5897
|
+
destKey: destKeyOf(dest),
|
|
5898
|
+
item,
|
|
5899
|
+
attempts: 0,
|
|
5900
|
+
eligibleAt,
|
|
5901
|
+
inFlight: false
|
|
5902
|
+
};
|
|
5903
|
+
this.queue.push(entry);
|
|
5904
|
+
return entry;
|
|
5905
|
+
}
|
|
5906
|
+
dropOldest() {
|
|
5907
|
+
if (this.queue.shift()) this.countDropped(1);
|
|
5908
|
+
}
|
|
5909
|
+
countDropped(n) {
|
|
5910
|
+
this.droppedSinceLast += n;
|
|
5911
|
+
this.droppedTotal += n;
|
|
5912
|
+
}
|
|
5913
|
+
async drain() {
|
|
5914
|
+
for (; ; ) {
|
|
5915
|
+
const now = this.opts.now();
|
|
5916
|
+
if (now < this.backoffUntil) return;
|
|
5917
|
+
const batch = this.takeBatch(now);
|
|
5918
|
+
if (batch.length === 0) return;
|
|
5919
|
+
const ok = await this.send(batch);
|
|
5920
|
+
if (!ok) return;
|
|
5921
|
+
}
|
|
5922
|
+
}
|
|
5923
|
+
/** Oldest eligible item + up to maxBatch-1 more for the same destination. */
|
|
5924
|
+
takeBatch(now) {
|
|
5925
|
+
const first = this.queue.find((q) => q.eligibleAt <= now);
|
|
5926
|
+
if (!first) return [];
|
|
5927
|
+
const batch = [];
|
|
5928
|
+
const rest = [];
|
|
5929
|
+
for (const q of this.queue) {
|
|
5930
|
+
if (batch.length < this.opts.maxBatch && q.destKey === first.destKey && q.eligibleAt <= now) {
|
|
5931
|
+
q.inFlight = true;
|
|
5932
|
+
batch.push(q);
|
|
5933
|
+
} else {
|
|
5934
|
+
rest.push(q);
|
|
5935
|
+
}
|
|
5936
|
+
}
|
|
5937
|
+
this.queue = rest;
|
|
5938
|
+
return batch;
|
|
5939
|
+
}
|
|
5940
|
+
/** POST one batch. Returns false when the drain should pause (backoff). */
|
|
5941
|
+
async send(batch) {
|
|
5942
|
+
const dest = batch[0].dest;
|
|
5943
|
+
const useKey = dest.apiKey && !this.rejectedKeys.has(dest.apiKey) ? dest.apiKey : void 0;
|
|
5944
|
+
const reportedDropped = this.droppedSinceLast;
|
|
5945
|
+
const meta = {
|
|
5946
|
+
droppedSinceLast: reportedDropped,
|
|
5947
|
+
queueDepth: this.queue.length,
|
|
5948
|
+
adapter: dest.adapter ?? "edge-core",
|
|
5949
|
+
version: SDK_VERSION
|
|
5950
|
+
};
|
|
5951
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
5952
|
+
const response = await withBudget(
|
|
5953
|
+
sdkFetch(`${dest.apiBaseUrl}/verification-activity/edge-events`, {
|
|
5954
|
+
method: "POST",
|
|
5955
|
+
headers: {
|
|
5956
|
+
"Content-Type": "application/json",
|
|
5957
|
+
...useKey && { Authorization: `Bearer ${useKey}` }
|
|
5958
|
+
},
|
|
5959
|
+
body: JSON.stringify({ events: batch.map((q) => q.item), meta }),
|
|
5960
|
+
...controller && { signal: controller.signal }
|
|
5961
|
+
}),
|
|
5962
|
+
this.opts.requestTimeoutMs
|
|
5963
|
+
);
|
|
5964
|
+
if (!response) controller?.abort();
|
|
5965
|
+
const status = response?.status;
|
|
5966
|
+
for (const q of batch) q.inFlight = false;
|
|
5967
|
+
if (status !== void 0 && status >= 200 && status < 300) {
|
|
5968
|
+
this.droppedSinceLast = Math.max(0, this.droppedSinceLast - reportedDropped);
|
|
5969
|
+
this.deliveredTotal += batch.length;
|
|
5970
|
+
return true;
|
|
5971
|
+
}
|
|
5972
|
+
if (status === 401 && useKey) {
|
|
5973
|
+
this.rejectedKeys.add(useKey);
|
|
5974
|
+
this.requeue(batch, false);
|
|
5975
|
+
return true;
|
|
5976
|
+
}
|
|
5977
|
+
if (status === 429) {
|
|
5978
|
+
const retryAfter = Number(response?.headers.get("retry-after"));
|
|
5979
|
+
const waitMs = Number.isFinite(retryAfter) && retryAfter > 0 ? Math.min(retryAfter, 120) * 1e3 : 3e4;
|
|
5980
|
+
this.backoffUntil = this.opts.now() + waitMs;
|
|
5981
|
+
this.requeue(batch, false);
|
|
5982
|
+
return false;
|
|
5983
|
+
}
|
|
5984
|
+
if (status !== void 0 && status >= 400 && status < 500) {
|
|
5985
|
+
this.countDropped(batch.length);
|
|
5986
|
+
return true;
|
|
5987
|
+
}
|
|
5988
|
+
this.backoffUntil = this.opts.now() + this.opts.retryBackoffMs;
|
|
5989
|
+
this.requeue(batch, true);
|
|
5990
|
+
return false;
|
|
5991
|
+
}
|
|
5992
|
+
/** Put a failed batch back at the head of the queue (order preserved). */
|
|
5993
|
+
requeue(batch, countAttempt) {
|
|
5994
|
+
const keep = [];
|
|
5995
|
+
for (const q of batch) {
|
|
5996
|
+
if (countAttempt) q.attempts += 1;
|
|
5997
|
+
if (q.attempts >= this.opts.maxAttempts) this.countDropped(1);
|
|
5998
|
+
else keep.push(q);
|
|
5999
|
+
}
|
|
6000
|
+
this.queue = [...keep, ...this.queue];
|
|
6001
|
+
while (this.queue.length > this.opts.maxItems) this.dropOldest();
|
|
6002
|
+
}
|
|
6003
|
+
};
|
|
6004
|
+
beaconBuffer = new BeaconBuffer();
|
|
5764
6005
|
}
|
|
5765
6006
|
});
|
|
5766
6007
|
|
|
@@ -5792,6 +6033,7 @@ var init_express = __esm({
|
|
|
5792
6033
|
init_well_known();
|
|
5793
6034
|
init_metadata_capture();
|
|
5794
6035
|
init_anonymous_beacon();
|
|
6036
|
+
init_failure_posture();
|
|
5795
6037
|
DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
|
|
5796
6038
|
}
|
|
5797
6039
|
});
|
|
@@ -5807,6 +6049,7 @@ var init_nextjs = __esm({
|
|
|
5807
6049
|
init_pdlss_pre_check();
|
|
5808
6050
|
init_metadata_capture();
|
|
5809
6051
|
init_anonymous_beacon();
|
|
6052
|
+
init_failure_posture();
|
|
5810
6053
|
init_http_pdlss();
|
|
5811
6054
|
init_approval_gate();
|
|
5812
6055
|
init_verified_hop();
|
|
@@ -6128,6 +6371,7 @@ var init_mcp2 = __esm({
|
|
|
6128
6371
|
init_approval_gate();
|
|
6129
6372
|
init_stepup_poll();
|
|
6130
6373
|
init_well_known();
|
|
6374
|
+
init_failure_posture();
|
|
6131
6375
|
init_mcp_server();
|
|
6132
6376
|
}
|
|
6133
6377
|
});
|
|
@@ -6216,6 +6460,7 @@ var init_src = __esm({
|
|
|
6216
6460
|
"use strict";
|
|
6217
6461
|
init_trust_levels();
|
|
6218
6462
|
init_verify();
|
|
6463
|
+
init_failure_posture();
|
|
6219
6464
|
init_stepup_poll();
|
|
6220
6465
|
init_settlement();
|
|
6221
6466
|
init_express();
|
|
@@ -6238,6 +6483,8 @@ var init_src = __esm({
|
|
|
6238
6483
|
init_runtime_signatures();
|
|
6239
6484
|
init_verified_hop();
|
|
6240
6485
|
init_http();
|
|
6486
|
+
init_pdlss_parser();
|
|
6487
|
+
init_schema();
|
|
6241
6488
|
init_version();
|
|
6242
6489
|
}
|
|
6243
6490
|
});
|
|
@@ -7116,7 +7363,15 @@ var init_settings_installer = __esm({
|
|
|
7116
7363
|
"~/.claude/settings.json",
|
|
7117
7364
|
"~/.claude/settings.local.json",
|
|
7118
7365
|
"**/.astrasync/**",
|
|
7119
|
-
"~/.astrasync/**"
|
|
7366
|
+
"~/.astrasync/**",
|
|
7367
|
+
// 5.11.0 fleet mode: enrolment + signed-policy cache (user scope is inside
|
|
7368
|
+
// ~/.astrasync already; listed explicitly so the intent survives edits) and
|
|
7369
|
+
// the machine-scope enrolment dirs. `//` = absolute from filesystem root;
|
|
7370
|
+
// Windows paths are matched in POSIX form (C:\ → /c/).
|
|
7371
|
+
"~/.astrasync/fleet/**",
|
|
7372
|
+
"//etc/astrasync/**",
|
|
7373
|
+
"//Library/Application Support/AstraSync/**",
|
|
7374
|
+
"//c/ProgramData/AstraSync/**"
|
|
7120
7375
|
];
|
|
7121
7376
|
GUARD_PERMISSION_DENY_RULES = PERMISSION_DENY_PATHS.map(
|
|
7122
7377
|
(p) => `Edit(${p})`
|
|
@@ -7158,6 +7413,545 @@ var init_settings_installer2 = __esm({
|
|
|
7158
7413
|
}
|
|
7159
7414
|
});
|
|
7160
7415
|
|
|
7416
|
+
// src/fleet/client.ts
|
|
7417
|
+
function base(url) {
|
|
7418
|
+
return url.replace(/\/+$/, "");
|
|
7419
|
+
}
|
|
7420
|
+
async function call(url, init, timeoutMs) {
|
|
7421
|
+
const controller = new AbortController();
|
|
7422
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7423
|
+
try {
|
|
7424
|
+
const res = await sdkFetch(url, { ...init, signal: controller.signal });
|
|
7425
|
+
const text = await res.text();
|
|
7426
|
+
let body = text;
|
|
7427
|
+
try {
|
|
7428
|
+
body = text ? JSON.parse(text) : null;
|
|
7429
|
+
} catch {
|
|
7430
|
+
}
|
|
7431
|
+
return { status: res.status, body, headers: res.headers };
|
|
7432
|
+
} finally {
|
|
7433
|
+
clearTimeout(timer);
|
|
7434
|
+
}
|
|
7435
|
+
}
|
|
7436
|
+
function apiError(status, body, what) {
|
|
7437
|
+
const b = body ?? {};
|
|
7438
|
+
const code = b.code ?? (typeof b.error === "string" ? b.error : void 0);
|
|
7439
|
+
return new FleetApiError(`${what} failed (${status}${code ? ` ${code}` : ""})`, status, code);
|
|
7440
|
+
}
|
|
7441
|
+
async function enrolDevice(apiBaseUrl, request2, timeoutMs = 15e3) {
|
|
7442
|
+
const r = await call(
|
|
7443
|
+
`${base(apiBaseUrl)}/fleet/enrol`,
|
|
7444
|
+
{
|
|
7445
|
+
method: "POST",
|
|
7446
|
+
headers: { "Content-Type": "application/json" },
|
|
7447
|
+
body: JSON.stringify(request2)
|
|
7448
|
+
},
|
|
7449
|
+
timeoutMs
|
|
7450
|
+
);
|
|
7451
|
+
if (r.status !== 201 && r.status !== 200) throw apiError(r.status, r.body, "Enrolment");
|
|
7452
|
+
const data = r.body;
|
|
7453
|
+
if (!data?.deviceId || !data.credential || !data.jwks || !data.policyJws) {
|
|
7454
|
+
throw new FleetApiError("Enrolment response is missing required fields", r.status);
|
|
7455
|
+
}
|
|
7456
|
+
return data;
|
|
7457
|
+
}
|
|
7458
|
+
var FleetApiError;
|
|
7459
|
+
var init_client2 = __esm({
|
|
7460
|
+
"src/fleet/client.ts"() {
|
|
7461
|
+
"use strict";
|
|
7462
|
+
init_http();
|
|
7463
|
+
FleetApiError = class extends Error {
|
|
7464
|
+
constructor(message, status, code) {
|
|
7465
|
+
super(message);
|
|
7466
|
+
this.status = status;
|
|
7467
|
+
this.code = code;
|
|
7468
|
+
this.name = "FleetApiError";
|
|
7469
|
+
}
|
|
7470
|
+
};
|
|
7471
|
+
}
|
|
7472
|
+
});
|
|
7473
|
+
|
|
7474
|
+
// src/fleet/enrolment-store.ts
|
|
7475
|
+
function machineScopeDir(platform2 = process.platform, env = process.env) {
|
|
7476
|
+
if (platform2 === "darwin") return "/Library/Application Support/AstraSync";
|
|
7477
|
+
if (platform2 === "win32") return (0, import_path5.join)(env.ProgramData || "C:\\ProgramData", "AstraSync");
|
|
7478
|
+
return "/etc/astrasync";
|
|
7479
|
+
}
|
|
7480
|
+
function fleetPaths(options = {}) {
|
|
7481
|
+
const home = options.home ?? (0, import_os.homedir)();
|
|
7482
|
+
const machineDir = options.machineDir ?? options.env?.ASTRASYNC_FLEET_MACHINE_DIR ?? machineScopeDir(options.platform, options.env);
|
|
7483
|
+
const userDir = (0, import_path5.join)(home, ".astrasync", "fleet");
|
|
7484
|
+
return {
|
|
7485
|
+
machineDevice: (0, import_path5.join)(machineDir, "fleet.json"),
|
|
7486
|
+
machinePolicy: (0, import_path5.join)(machineDir, "policy.jws"),
|
|
7487
|
+
userDir,
|
|
7488
|
+
userDevice: (0, import_path5.join)(userDir, "device.json"),
|
|
7489
|
+
userPolicy: (0, import_path5.join)(userDir, "policy.jws"),
|
|
7490
|
+
userKeyChain: (0, import_path5.join)(userDir, "keychain.json"),
|
|
7491
|
+
state: (0, import_path5.join)(userDir, "state.json")
|
|
7492
|
+
};
|
|
7493
|
+
}
|
|
7494
|
+
function isEnrolment(value) {
|
|
7495
|
+
const v = value;
|
|
7496
|
+
return !!v && typeof v.deviceId === "string" && typeof v.credential === "string" && typeof v.apiBaseUrl === "string" && !!v.org && typeof v.org.ownerId === "string" && !!v.jwks && Array.isArray(v.jwks.keys);
|
|
7497
|
+
}
|
|
7498
|
+
function loadEnrolment(paths) {
|
|
7499
|
+
const candidates = [
|
|
7500
|
+
[paths.machineDevice, "machine"],
|
|
7501
|
+
[paths.userDevice, "user"]
|
|
7502
|
+
];
|
|
7503
|
+
for (const [path2, scope] of candidates) {
|
|
7504
|
+
if (!(0, import_fs6.existsSync)(path2)) continue;
|
|
7505
|
+
try {
|
|
7506
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(path2, "utf-8"));
|
|
7507
|
+
if (isEnrolment(parsed)) return { enrolment: parsed, scope };
|
|
7508
|
+
} catch {
|
|
7509
|
+
}
|
|
7510
|
+
return { corrupt: true, path: path2 };
|
|
7511
|
+
}
|
|
7512
|
+
return null;
|
|
7513
|
+
}
|
|
7514
|
+
function writeFileMode(path2, content, mode) {
|
|
7515
|
+
(0, import_fs6.mkdirSync)((0, import_path5.dirname)(path2), { recursive: true, mode: 448 });
|
|
7516
|
+
(0, import_fs6.writeFileSync)(path2, content, { encoding: "utf-8", mode });
|
|
7517
|
+
try {
|
|
7518
|
+
(0, import_fs6.chmodSync)(path2, mode);
|
|
7519
|
+
} catch {
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
7522
|
+
function saveEnrolment(paths, enrolment, scope) {
|
|
7523
|
+
const path2 = scope === "machine" ? paths.machineDevice : paths.userDevice;
|
|
7524
|
+
writeFileMode(path2, JSON.stringify(enrolment, null, 2), scope === "machine" ? 420 : 384);
|
|
7525
|
+
return path2;
|
|
7526
|
+
}
|
|
7527
|
+
function readCachedPolicies(paths) {
|
|
7528
|
+
const out = [];
|
|
7529
|
+
for (const p of [paths.userPolicy, paths.machinePolicy]) {
|
|
7530
|
+
try {
|
|
7531
|
+
if ((0, import_fs6.existsSync)(p)) out.push((0, import_fs6.readFileSync)(p, "utf-8").trim());
|
|
7532
|
+
} catch {
|
|
7533
|
+
}
|
|
7534
|
+
}
|
|
7535
|
+
return out.filter(Boolean);
|
|
7536
|
+
}
|
|
7537
|
+
function writeCachedPolicy(paths, jws) {
|
|
7538
|
+
writeFileMode(paths.userPolicy, jws, 384);
|
|
7539
|
+
}
|
|
7540
|
+
function readKeyChain(paths) {
|
|
7541
|
+
try {
|
|
7542
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(paths.userKeyChain, "utf-8"));
|
|
7543
|
+
return Array.isArray(parsed) ? parsed.filter((j) => typeof j === "string") : [];
|
|
7544
|
+
} catch {
|
|
7545
|
+
return [];
|
|
7546
|
+
}
|
|
7547
|
+
}
|
|
7548
|
+
function writeKeyChain(paths, chain) {
|
|
7549
|
+
writeFileMode(paths.userKeyChain, JSON.stringify(chain), 384);
|
|
7550
|
+
}
|
|
7551
|
+
function writeMachinePolicy(paths, jws) {
|
|
7552
|
+
writeFileMode(paths.machinePolicy, jws, 420);
|
|
7553
|
+
}
|
|
7554
|
+
function readState(paths) {
|
|
7555
|
+
try {
|
|
7556
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(paths.state, "utf-8"));
|
|
7557
|
+
const c = parsed.counts;
|
|
7558
|
+
return {
|
|
7559
|
+
...parsed,
|
|
7560
|
+
counts: { allow: c?.allow ?? 0, deny: c?.deny ?? 0, ask: c?.ask ?? 0 }
|
|
7561
|
+
};
|
|
7562
|
+
} catch {
|
|
7563
|
+
return { counts: { allow: 0, deny: 0, ask: 0 } };
|
|
7564
|
+
}
|
|
7565
|
+
}
|
|
7566
|
+
function writeState(paths, state) {
|
|
7567
|
+
try {
|
|
7568
|
+
writeFileMode(paths.state, JSON.stringify(state, null, 2), 384);
|
|
7569
|
+
} catch {
|
|
7570
|
+
}
|
|
7571
|
+
}
|
|
7572
|
+
var import_fs6, import_os, import_path5;
|
|
7573
|
+
var init_enrolment_store = __esm({
|
|
7574
|
+
"src/fleet/enrolment-store.ts"() {
|
|
7575
|
+
"use strict";
|
|
7576
|
+
import_fs6 = require("fs");
|
|
7577
|
+
import_os = require("os");
|
|
7578
|
+
import_path5 = require("path");
|
|
7579
|
+
}
|
|
7580
|
+
});
|
|
7581
|
+
|
|
7582
|
+
// src/fleet/types.ts
|
|
7583
|
+
var FLEET_POLICY_TYP, FLEET_POLICY_AUD, FLEET_POLICY_ISS, FLEET_KEYS_TYP, FLEET_KEYS_AUD, DEFAULT_HEARTBEAT_INTERVAL_SEC;
|
|
7584
|
+
var init_types3 = __esm({
|
|
7585
|
+
"src/fleet/types.ts"() {
|
|
7586
|
+
"use strict";
|
|
7587
|
+
FLEET_POLICY_TYP = "fleet-policy+jwt";
|
|
7588
|
+
FLEET_POLICY_AUD = "astrasync:fleet-policy";
|
|
7589
|
+
FLEET_POLICY_ISS = "astrasync.ai";
|
|
7590
|
+
FLEET_KEYS_TYP = "fleet-keys+jwt";
|
|
7591
|
+
FLEET_KEYS_AUD = "astrasync:fleet-keys";
|
|
7592
|
+
DEFAULT_HEARTBEAT_INTERVAL_SEC = 900;
|
|
7593
|
+
}
|
|
7594
|
+
});
|
|
7595
|
+
|
|
7596
|
+
// src/fleet/policy-verify.ts
|
|
7597
|
+
function sha256Hex(text) {
|
|
7598
|
+
return (0, import_crypto.createHash)("sha256").update(text, "utf8").digest("hex");
|
|
7599
|
+
}
|
|
7600
|
+
function parseSettings(raw) {
|
|
7601
|
+
if (!raw || typeof raw !== "object") throw new FleetPolicyError("settings missing");
|
|
7602
|
+
const s = raw;
|
|
7603
|
+
if (s.posture !== "active" && s.posture !== "passive") {
|
|
7604
|
+
throw new FleetPolicyError("settings.posture must be active or passive");
|
|
7605
|
+
}
|
|
7606
|
+
if (s.failOpen !== void 0 && typeof s.failOpen !== "boolean") {
|
|
7607
|
+
throw new FleetPolicyError("settings.failOpen must be a boolean");
|
|
7608
|
+
}
|
|
7609
|
+
if (s.governMcp !== void 0 && typeof s.governMcp !== "boolean") {
|
|
7610
|
+
throw new FleetPolicyError("settings.governMcp must be a boolean");
|
|
7611
|
+
}
|
|
7612
|
+
if (s.skipTools !== void 0 && !(Array.isArray(s.skipTools) && s.skipTools.every((t) => typeof t === "string"))) {
|
|
7613
|
+
throw new FleetPolicyError("settings.skipTools must be a string array");
|
|
7614
|
+
}
|
|
7615
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
|
|
7616
|
+
return {
|
|
7617
|
+
posture: s.posture,
|
|
7618
|
+
failOpen: s.failOpen === true,
|
|
7619
|
+
...typeof s.governMcp === "boolean" && { governMcp: s.governMcp },
|
|
7620
|
+
...Array.isArray(s.skipTools) && { skipTools: s.skipTools },
|
|
7621
|
+
...num(s.heartbeatIntervalSec) && { heartbeatIntervalSec: num(s.heartbeatIntervalSec) },
|
|
7622
|
+
...num(s.staleAfterSec) && { staleAfterSec: num(s.staleAfterSec) }
|
|
7623
|
+
};
|
|
7624
|
+
}
|
|
7625
|
+
async function verifyFleetPolicy(jws, jwks, options) {
|
|
7626
|
+
let payloadBytes;
|
|
7627
|
+
let header;
|
|
7628
|
+
try {
|
|
7629
|
+
const keySet = (0, import_jose2.createLocalJWKSet)(jwks);
|
|
7630
|
+
const result = await (0, import_jose2.compactVerify)(jws, keySet, { algorithms: ["ES256"] });
|
|
7631
|
+
payloadBytes = result.payload;
|
|
7632
|
+
header = result.protectedHeader;
|
|
7633
|
+
} catch (err) {
|
|
7634
|
+
throw new FleetPolicyError(
|
|
7635
|
+
`signature verification failed (${err instanceof Error ? err.message : String(err)})`
|
|
7636
|
+
);
|
|
7637
|
+
}
|
|
7638
|
+
if (header.typ !== FLEET_POLICY_TYP) throw new FleetPolicyError(`unexpected typ ${header.typ}`);
|
|
7639
|
+
let claims;
|
|
7640
|
+
try {
|
|
7641
|
+
claims = JSON.parse(new TextDecoder().decode(payloadBytes));
|
|
7642
|
+
} catch {
|
|
7643
|
+
throw new FleetPolicyError("payload is not JSON");
|
|
7644
|
+
}
|
|
7645
|
+
if (claims.aud !== FLEET_POLICY_AUD) throw new FleetPolicyError(`unexpected aud ${claims.aud}`);
|
|
7646
|
+
if (claims.iss !== FLEET_POLICY_ISS) throw new FleetPolicyError(`unexpected iss ${claims.iss}`);
|
|
7647
|
+
if (!options.expectedSubs.includes(claims.sub)) {
|
|
7648
|
+
throw new FleetPolicyError("policy was issued for a different organisation or device");
|
|
7649
|
+
}
|
|
7650
|
+
if (!Number.isInteger(claims.ver) || claims.ver < 1) {
|
|
7651
|
+
throw new FleetPolicyError("ver must be a positive integer");
|
|
7652
|
+
}
|
|
7653
|
+
if (options.minVer !== void 0 && claims.ver < options.minVer) {
|
|
7654
|
+
throw new FleetPolicyError(
|
|
7655
|
+
`policy version ${claims.ver} is older than the accepted version ${options.minVer} (rollback)`
|
|
7656
|
+
);
|
|
7657
|
+
}
|
|
7658
|
+
if (typeof claims.yaml !== "string" || sha256Hex(claims.yaml) !== claims.sha256) {
|
|
7659
|
+
throw new FleetPolicyError("policy hash mismatch");
|
|
7660
|
+
}
|
|
7661
|
+
const settings = parseSettings(claims.settings);
|
|
7662
|
+
const parsed = parseYaml(claims.yaml);
|
|
7663
|
+
if (!parsed.policy) {
|
|
7664
|
+
const first = parsed.validation.errors[0];
|
|
7665
|
+
throw new FleetPolicyError(
|
|
7666
|
+
`signed policy is invalid${first ? ` (${first.path}: ${first.message})` : ""}`
|
|
7667
|
+
);
|
|
7668
|
+
}
|
|
7669
|
+
return { kid: header.kid, claims: { ...claims, settings }, policy: parsed.policy, settings, jws };
|
|
7670
|
+
}
|
|
7671
|
+
async function resolveFleetJwks(pinned, chain, ownerId) {
|
|
7672
|
+
let trusted = pinned;
|
|
7673
|
+
let lastSeq = 0;
|
|
7674
|
+
for (const link of chain) {
|
|
7675
|
+
try {
|
|
7676
|
+
const { payload, protectedHeader } = await (0, import_jose2.compactVerify)(
|
|
7677
|
+
link,
|
|
7678
|
+
(0, import_jose2.createLocalJWKSet)(trusted),
|
|
7679
|
+
{ algorithms: ["ES256"] }
|
|
7680
|
+
);
|
|
7681
|
+
if (protectedHeader.typ !== FLEET_KEYS_TYP) continue;
|
|
7682
|
+
const c = JSON.parse(new TextDecoder().decode(payload));
|
|
7683
|
+
if (c.aud !== FLEET_KEYS_AUD || c.iss !== FLEET_POLICY_ISS || c.sub !== ownerId) continue;
|
|
7684
|
+
if (!Number.isInteger(c.seq) || c.seq <= lastSeq) continue;
|
|
7685
|
+
const keys = Array.isArray(c.keys) ? c.keys.filter((k) => k && k.kty === "EC" && k.crv === "P-256" && !("d" in k)) : [];
|
|
7686
|
+
if (keys.length === 0) continue;
|
|
7687
|
+
trusted = { keys };
|
|
7688
|
+
lastSeq = c.seq;
|
|
7689
|
+
} catch {
|
|
7690
|
+
}
|
|
7691
|
+
}
|
|
7692
|
+
return trusted;
|
|
7693
|
+
}
|
|
7694
|
+
async function pickNewestValid(candidates, jwks, options) {
|
|
7695
|
+
let best = null;
|
|
7696
|
+
let lastError = new FleetPolicyError("no cached fleet policy");
|
|
7697
|
+
for (const jws of candidates) {
|
|
7698
|
+
try {
|
|
7699
|
+
const v = await verifyFleetPolicy(jws, jwks, options);
|
|
7700
|
+
if (!best || v.claims.ver > best.claims.ver) best = v;
|
|
7701
|
+
} catch (err) {
|
|
7702
|
+
lastError = err instanceof Error ? err : new FleetPolicyError(String(err));
|
|
7703
|
+
}
|
|
7704
|
+
}
|
|
7705
|
+
if (!best) throw lastError;
|
|
7706
|
+
return best;
|
|
7707
|
+
}
|
|
7708
|
+
var import_crypto, import_jose2, FleetPolicyError;
|
|
7709
|
+
var init_policy_verify = __esm({
|
|
7710
|
+
"src/fleet/policy-verify.ts"() {
|
|
7711
|
+
"use strict";
|
|
7712
|
+
import_crypto = require("crypto");
|
|
7713
|
+
import_jose2 = require("jose");
|
|
7714
|
+
init_pdlss_parser();
|
|
7715
|
+
init_types3();
|
|
7716
|
+
FleetPolicyError = class extends Error {
|
|
7717
|
+
constructor(message) {
|
|
7718
|
+
super(message);
|
|
7719
|
+
this.name = "FleetPolicyError";
|
|
7720
|
+
}
|
|
7721
|
+
};
|
|
7722
|
+
}
|
|
7723
|
+
});
|
|
7724
|
+
|
|
7725
|
+
// src/fleet/cli.ts
|
|
7726
|
+
function machineFingerprint() {
|
|
7727
|
+
const candidates = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
|
|
7728
|
+
let raw = "";
|
|
7729
|
+
for (const c of candidates) {
|
|
7730
|
+
try {
|
|
7731
|
+
if ((0, import_fs7.existsSync)(c)) {
|
|
7732
|
+
raw = (0, import_fs7.readFileSync)(c, "utf-8").trim();
|
|
7733
|
+
if (raw) break;
|
|
7734
|
+
}
|
|
7735
|
+
} catch {
|
|
7736
|
+
}
|
|
7737
|
+
}
|
|
7738
|
+
if (!raw) raw = `${(0, import_os2.hostname)()}|${(0, import_os2.platform)()}|${(0, import_os2.arch)()}`;
|
|
7739
|
+
return (0, import_crypto2.createHash)("sha256").update(`astrasync-fleet:${raw}`).digest("hex");
|
|
7740
|
+
}
|
|
7741
|
+
async function runEnroll(options) {
|
|
7742
|
+
const print = options.print ?? ((l) => console.log(l));
|
|
7743
|
+
const token = options.token.trim();
|
|
7744
|
+
if (!token)
|
|
7745
|
+
throw new Error("Usage: astrasync guard enroll <token> [--api-base-url <url>] [--system]");
|
|
7746
|
+
const apiBaseUrl = (options.apiBaseUrl ?? DEFAULT_FLEET_API_BASE_URL).replace(/\/+$/, "");
|
|
7747
|
+
const paths = options.paths ?? fleetPaths();
|
|
7748
|
+
const response = await enrolDevice(apiBaseUrl, {
|
|
7749
|
+
token,
|
|
7750
|
+
fingerprint: options.fingerprint ?? machineFingerprint(),
|
|
7751
|
+
hostname: (0, import_os2.hostname)(),
|
|
7752
|
+
os: { platform: (0, import_os2.platform)(), release: (0, import_os2.release)(), arch: (0, import_os2.arch)() },
|
|
7753
|
+
guardVersion: SDK_VERSION,
|
|
7754
|
+
adapters: options.adapters ?? [
|
|
7755
|
+
{ kind: "claude-code", managed: false },
|
|
7756
|
+
{ kind: "codex", managed: false }
|
|
7757
|
+
]
|
|
7758
|
+
});
|
|
7759
|
+
const enrolment = {
|
|
7760
|
+
version: 1,
|
|
7761
|
+
deviceId: response.deviceId,
|
|
7762
|
+
credential: response.credential,
|
|
7763
|
+
org: response.org,
|
|
7764
|
+
apiBaseUrl: response.apiBaseUrl || apiBaseUrl,
|
|
7765
|
+
jwks: response.jwks,
|
|
7766
|
+
heartbeatIntervalSec: response.heartbeatIntervalSec || DEFAULT_HEARTBEAT_INTERVAL_SEC,
|
|
7767
|
+
enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7768
|
+
};
|
|
7769
|
+
const verified = await verifyFleetPolicy(response.policyJws, enrolment.jwks, {
|
|
7770
|
+
expectedSubs: [enrolment.org.ownerId, enrolment.deviceId]
|
|
7771
|
+
});
|
|
7772
|
+
const scope = options.system ? "machine" : "user";
|
|
7773
|
+
const savedTo = saveEnrolment(paths, enrolment, scope);
|
|
7774
|
+
if (options.system) writeMachinePolicy(paths, response.policyJws);
|
|
7775
|
+
writeCachedPolicy(paths, response.policyJws);
|
|
7776
|
+
writeKeyChain(paths, []);
|
|
7777
|
+
const state = readState(paths);
|
|
7778
|
+
writeState(paths, {
|
|
7779
|
+
...state,
|
|
7780
|
+
appliedSha256: verified.claims.sha256,
|
|
7781
|
+
maxVer: Math.max(state.maxVer ?? 0, verified.claims.ver),
|
|
7782
|
+
revoked: false
|
|
7783
|
+
});
|
|
7784
|
+
print(
|
|
7785
|
+
`Enrolled in ${enrolment.org.name ?? enrolment.org.ownerId} (device ${enrolment.deviceId}).`
|
|
7786
|
+
);
|
|
7787
|
+
print(` Enrolment: ${savedTo} (${scope} scope)`);
|
|
7788
|
+
print(` Policy: v${verified.claims.ver}, posture ${verified.settings.posture}`);
|
|
7789
|
+
print(
|
|
7790
|
+
" Local policy.yaml / guard.json files are now ignored \u2014 the organisation policy governs."
|
|
7791
|
+
);
|
|
7792
|
+
return enrolment;
|
|
7793
|
+
}
|
|
7794
|
+
async function getFleetStatus(paths = fleetPaths()) {
|
|
7795
|
+
const loaded = loadEnrolment(paths);
|
|
7796
|
+
if (!loaded) return { enrolled: false };
|
|
7797
|
+
if ("corrupt" in loaded) return { enrolled: true, corrupt: loaded.path };
|
|
7798
|
+
const { enrolment, scope } = loaded;
|
|
7799
|
+
const state = readState(paths);
|
|
7800
|
+
let policy;
|
|
7801
|
+
try {
|
|
7802
|
+
const jwks = await resolveFleetJwks(enrolment.jwks, readKeyChain(paths), enrolment.org.ownerId);
|
|
7803
|
+
const v = await pickNewestValid(readCachedPolicies(paths), jwks, {
|
|
7804
|
+
expectedSubs: [enrolment.org.ownerId, enrolment.deviceId],
|
|
7805
|
+
minVer: state.maxVer
|
|
7806
|
+
});
|
|
7807
|
+
policy = {
|
|
7808
|
+
ver: v.claims.ver,
|
|
7809
|
+
sha256: v.claims.sha256,
|
|
7810
|
+
posture: v.settings.posture,
|
|
7811
|
+
failOpen: v.settings.failOpen
|
|
7812
|
+
};
|
|
7813
|
+
} catch (err) {
|
|
7814
|
+
policy = { error: err instanceof Error ? err.message : String(err) };
|
|
7815
|
+
}
|
|
7816
|
+
return {
|
|
7817
|
+
enrolled: true,
|
|
7818
|
+
scope,
|
|
7819
|
+
org: enrolment.org,
|
|
7820
|
+
deviceId: enrolment.deviceId,
|
|
7821
|
+
apiBaseUrl: enrolment.apiBaseUrl,
|
|
7822
|
+
policy,
|
|
7823
|
+
lastHeartbeatAt: state.lastHeartbeatAt,
|
|
7824
|
+
revoked: state.revoked === true,
|
|
7825
|
+
lastError: state.lastError,
|
|
7826
|
+
pendingCounts: state.counts
|
|
7827
|
+
};
|
|
7828
|
+
}
|
|
7829
|
+
function formatFleetStatus(status) {
|
|
7830
|
+
if (!status.enrolled) return ["Fleet: not enrolled (local policy mode)."];
|
|
7831
|
+
if (status.corrupt) {
|
|
7832
|
+
return [`Fleet: enrolment file ${status.corrupt} is unreadable \u2014 the guard fails closed.`];
|
|
7833
|
+
}
|
|
7834
|
+
const lines = [
|
|
7835
|
+
`Fleet: enrolled in ${status.org?.name ?? status.org?.ownerId} (${status.scope} scope)`,
|
|
7836
|
+
` Device: ${status.deviceId}`,
|
|
7837
|
+
` Platform: ${status.apiBaseUrl}`
|
|
7838
|
+
];
|
|
7839
|
+
if (status.policy && "error" in status.policy) {
|
|
7840
|
+
lines.push(` Policy: NO VALID SIGNED POLICY \u2014 ${status.policy.error} (guard denies)`);
|
|
7841
|
+
} else if (status.policy) {
|
|
7842
|
+
lines.push(
|
|
7843
|
+
` Policy: v${status.policy.ver} (${status.policy.sha256.slice(0, 12)}\u2026), posture ${status.policy.posture}${status.policy.failOpen ? ", fail-open" : ""}`
|
|
7844
|
+
);
|
|
7845
|
+
}
|
|
7846
|
+
lines.push(` Heartbeat: ${status.lastHeartbeatAt ?? "never"}`);
|
|
7847
|
+
if (status.revoked) {
|
|
7848
|
+
lines.push(" Status: REVOKED by the organisation \u2014 enforcing the last good policy");
|
|
7849
|
+
}
|
|
7850
|
+
if (status.lastError) lines.push(` Last sync error: ${status.lastError}`);
|
|
7851
|
+
return lines;
|
|
7852
|
+
}
|
|
7853
|
+
function managedHookCommand(target, os2) {
|
|
7854
|
+
const bin = target === "claude" ? "astrasync-claude-hook" : "astrasync-codex-hook";
|
|
7855
|
+
if (os2 === "win32") return `"C:\\Program Files\\nodejs\\${bin}.cmd"`;
|
|
7856
|
+
return `/usr/local/bin/${bin}`;
|
|
7857
|
+
}
|
|
7858
|
+
function buildClaudeManagedSettings(os2) {
|
|
7859
|
+
return {
|
|
7860
|
+
allowManagedHooksOnly: true,
|
|
7861
|
+
env: { ASTRASYNC_FLEET_REQUIRED: "1" },
|
|
7862
|
+
hooks: {
|
|
7863
|
+
PreToolUse: [
|
|
7864
|
+
{
|
|
7865
|
+
matcher: HOOK_MATCHER,
|
|
7866
|
+
hooks: [{ type: "command", command: managedHookCommand("claude", os2), timeout: 10 }]
|
|
7867
|
+
}
|
|
7868
|
+
]
|
|
7869
|
+
},
|
|
7870
|
+
permissions: { deny: [...GUARD_PERMISSION_DENY_RULES] }
|
|
7871
|
+
};
|
|
7872
|
+
}
|
|
7873
|
+
function tomlString(value) {
|
|
7874
|
+
return JSON.stringify(value);
|
|
7875
|
+
}
|
|
7876
|
+
function buildCodexRequirementsToml(os2) {
|
|
7877
|
+
const command = os2 === "win32" ? `set ASTRASYNC_FLEET_REQUIRED=1&& ${managedHookCommand("codex", os2)}` : `ASTRASYNC_FLEET_REQUIRED=1 ${managedHookCommand("codex", os2)}`;
|
|
7878
|
+
return [
|
|
7879
|
+
"# AstraSync Local Guard \u2014 managed hook (generated by `astrasync guard managed-settings --codex`)",
|
|
7880
|
+
`# Install at: ${MANAGED_SETTINGS_LOCATIONS.codex[os2]}`,
|
|
7881
|
+
"allow_managed_hooks_only = true",
|
|
7882
|
+
"",
|
|
7883
|
+
"[features]",
|
|
7884
|
+
"hooks = true",
|
|
7885
|
+
"",
|
|
7886
|
+
"[[hooks.PreToolUse]]",
|
|
7887
|
+
`matcher = ${tomlString(CODEX_HOOK_MATCHER)}`,
|
|
7888
|
+
"",
|
|
7889
|
+
"[[hooks.PreToolUse.hooks]]",
|
|
7890
|
+
'type = "command"',
|
|
7891
|
+
`command = ${tomlString(command)}`,
|
|
7892
|
+
"timeout = 10",
|
|
7893
|
+
'statusMessage = "AstraSync Local Guard"',
|
|
7894
|
+
""
|
|
7895
|
+
].join("\n");
|
|
7896
|
+
}
|
|
7897
|
+
function buildManagedSettingsFiles(targets) {
|
|
7898
|
+
const out = [];
|
|
7899
|
+
for (const target of targets) {
|
|
7900
|
+
for (const os2 of ["darwin", "linux", "win32"]) {
|
|
7901
|
+
const content = target === "claude" ? `${JSON.stringify(buildClaudeManagedSettings(os2), null, 2)}
|
|
7902
|
+
` : buildCodexRequirementsToml(os2);
|
|
7903
|
+
out.push({
|
|
7904
|
+
target,
|
|
7905
|
+
os: os2,
|
|
7906
|
+
installPath: MANAGED_SETTINGS_LOCATIONS[target][os2],
|
|
7907
|
+
fileName: `${target}-${os2}-${target === "claude" ? "managed-settings.json" : "requirements.toml"}`,
|
|
7908
|
+
content
|
|
7909
|
+
});
|
|
7910
|
+
}
|
|
7911
|
+
}
|
|
7912
|
+
return out;
|
|
7913
|
+
}
|
|
7914
|
+
function writeManagedSettings(files, outDir) {
|
|
7915
|
+
(0, import_fs7.mkdirSync)(outDir, { recursive: true });
|
|
7916
|
+
return files.map((f) => {
|
|
7917
|
+
const p = (0, import_path6.join)(outDir, f.fileName);
|
|
7918
|
+
(0, import_fs7.writeFileSync)(p, f.content, "utf-8");
|
|
7919
|
+
return p;
|
|
7920
|
+
});
|
|
7921
|
+
}
|
|
7922
|
+
var import_crypto2, import_fs7, import_os2, import_path6, DEFAULT_FLEET_API_BASE_URL, MANAGED_SETTINGS_LOCATIONS;
|
|
7923
|
+
var init_cli = __esm({
|
|
7924
|
+
"src/fleet/cli.ts"() {
|
|
7925
|
+
"use strict";
|
|
7926
|
+
import_crypto2 = require("crypto");
|
|
7927
|
+
import_fs7 = require("fs");
|
|
7928
|
+
import_os2 = require("os");
|
|
7929
|
+
import_path6 = require("path");
|
|
7930
|
+
init_settings_installer();
|
|
7931
|
+
init_settings_installer2();
|
|
7932
|
+
init_version();
|
|
7933
|
+
init_client2();
|
|
7934
|
+
init_enrolment_store();
|
|
7935
|
+
init_policy_verify();
|
|
7936
|
+
init_types3();
|
|
7937
|
+
DEFAULT_FLEET_API_BASE_URL = "https://astrasync.ai/api";
|
|
7938
|
+
MANAGED_SETTINGS_LOCATIONS = {
|
|
7939
|
+
// code.claude.com/docs/en/managed-settings
|
|
7940
|
+
claude: {
|
|
7941
|
+
darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
|
|
7942
|
+
linux: "/etc/claude-code/managed-settings.json",
|
|
7943
|
+
win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
|
|
7944
|
+
},
|
|
7945
|
+
// learn.chatgpt.com/docs/enterprise/managed-configuration
|
|
7946
|
+
codex: {
|
|
7947
|
+
darwin: "/etc/codex/requirements.toml",
|
|
7948
|
+
linux: "/etc/codex/requirements.toml",
|
|
7949
|
+
win32: "%ProgramData%\\OpenAI\\Codex\\requirements.toml"
|
|
7950
|
+
}
|
|
7951
|
+
};
|
|
7952
|
+
}
|
|
7953
|
+
});
|
|
7954
|
+
|
|
7161
7955
|
// src/cli/guard.ts
|
|
7162
7956
|
var guard_exports = {};
|
|
7163
7957
|
function createPrompt() {
|
|
@@ -7296,6 +8090,9 @@ async function cmdStatus() {
|
|
|
7296
8090
|
}
|
|
7297
8091
|
});
|
|
7298
8092
|
console.log("\nAstraSync Local Guard Status\n");
|
|
8093
|
+
for (const line of formatFleetStatus(await getFleetStatus(fleetPaths()))) {
|
|
8094
|
+
console.log(` ${line}`);
|
|
8095
|
+
}
|
|
7299
8096
|
if (upgraded) {
|
|
7300
8097
|
const config = JSON.parse(fs.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
|
|
7301
8098
|
console.log(` Mode: ${config.mode}`);
|
|
@@ -7651,6 +8448,66 @@ function printClaudeCodeStatus() {
|
|
|
7651
8448
|
}
|
|
7652
8449
|
}
|
|
7653
8450
|
}
|
|
8451
|
+
function flagValue(flag) {
|
|
8452
|
+
const i = process.argv.indexOf(flag);
|
|
8453
|
+
return i !== -1 ? process.argv[i + 1] : void 0;
|
|
8454
|
+
}
|
|
8455
|
+
function positionalAfter(command) {
|
|
8456
|
+
const argv = process.argv;
|
|
8457
|
+
const start = argv.indexOf(command) + 1;
|
|
8458
|
+
for (let i = start; i < argv.length; i++) {
|
|
8459
|
+
if (argv[i].startsWith("--")) {
|
|
8460
|
+
if (argv[i] === "--api-base-url" || argv[i] === "--out") i++;
|
|
8461
|
+
continue;
|
|
8462
|
+
}
|
|
8463
|
+
return argv[i];
|
|
8464
|
+
}
|
|
8465
|
+
return void 0;
|
|
8466
|
+
}
|
|
8467
|
+
async function cmdEnroll() {
|
|
8468
|
+
const token = positionalAfter("enroll") ?? "";
|
|
8469
|
+
try {
|
|
8470
|
+
await runEnroll({
|
|
8471
|
+
token,
|
|
8472
|
+
apiBaseUrl: flagValue("--api-base-url"),
|
|
8473
|
+
system: process.argv.includes("--system")
|
|
8474
|
+
});
|
|
8475
|
+
} catch (err) {
|
|
8476
|
+
console.error(`Enrolment failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8477
|
+
if (process.argv.includes("--system")) {
|
|
8478
|
+
console.error(
|
|
8479
|
+
" (--system writes the machine-scope enrolment and needs administrator rights)"
|
|
8480
|
+
);
|
|
8481
|
+
}
|
|
8482
|
+
process.exit(1);
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8485
|
+
async function cmdFleetStatus() {
|
|
8486
|
+
const status = await getFleetStatus(fleetPaths());
|
|
8487
|
+
for (const line of formatFleetStatus(status)) console.log(line);
|
|
8488
|
+
}
|
|
8489
|
+
async function cmdManagedSettings() {
|
|
8490
|
+
const targets = [];
|
|
8491
|
+
if (process.argv.includes("--claude")) targets.push("claude");
|
|
8492
|
+
if (process.argv.includes("--codex")) targets.push("codex");
|
|
8493
|
+
if (targets.length === 0) targets.push("claude", "codex");
|
|
8494
|
+
const files = buildManagedSettingsFiles(targets);
|
|
8495
|
+
const outDir = flagValue("--out");
|
|
8496
|
+
if (outDir) {
|
|
8497
|
+
const written = writeManagedSettings(files, outDir);
|
|
8498
|
+
console.log("Managed-settings templates written:");
|
|
8499
|
+
files.forEach((f, i) => console.log(` ${written[i]} \u2192 deploy to ${f.installPath}`));
|
|
8500
|
+
} else {
|
|
8501
|
+
for (const f of files) {
|
|
8502
|
+
console.log(`===== ${f.target} \xB7 ${f.os} \u2192 ${f.installPath}`);
|
|
8503
|
+
console.log(f.content);
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8506
|
+
console.log(
|
|
8507
|
+
"Hook commands assume npm\u2019s default global prefix; adjust if your MDM installs elsewhere."
|
|
8508
|
+
);
|
|
8509
|
+
console.log("Machines must also be enrolled: astrasync guard enroll <token> --system");
|
|
8510
|
+
}
|
|
7654
8511
|
async function main() {
|
|
7655
8512
|
const command = process.argv[2] === "guard" ? process.argv[3] : process.argv[2];
|
|
7656
8513
|
switch (command) {
|
|
@@ -7687,6 +8544,16 @@ async function main() {
|
|
|
7687
8544
|
case "uninstall-codex":
|
|
7688
8545
|
await cmdUninstallCodex();
|
|
7689
8546
|
break;
|
|
8547
|
+
case "enroll":
|
|
8548
|
+
case "enrol":
|
|
8549
|
+
await cmdEnroll();
|
|
8550
|
+
break;
|
|
8551
|
+
case "fleet-status":
|
|
8552
|
+
await cmdFleetStatus();
|
|
8553
|
+
break;
|
|
8554
|
+
case "managed-settings":
|
|
8555
|
+
await cmdManagedSettings();
|
|
8556
|
+
break;
|
|
7690
8557
|
default:
|
|
7691
8558
|
console.log("AstraSync Local Guard\n");
|
|
7692
8559
|
console.log("Usage: astrasync guard <command>\n");
|
|
@@ -7707,6 +8574,10 @@ async function main() {
|
|
|
7707
8574
|
console.log(" install-codex Install the Local Guard hook for OpenAI Codex CLI");
|
|
7708
8575
|
console.log(" (same flags; policy shared with Claude Code)");
|
|
7709
8576
|
console.log(" uninstall-codex Remove the Codex hook (--project for project scope)");
|
|
8577
|
+
console.log(" enroll <token> Enrol this machine in your organisation fleet");
|
|
8578
|
+
console.log(" (--api-base-url <url>, --system for machine scope)");
|
|
8579
|
+
console.log(" fleet-status Show fleet enrolment, signed policy and heartbeat");
|
|
8580
|
+
console.log(" managed-settings Print MDM templates (--claude, --codex, --out <dir>)");
|
|
7710
8581
|
console.log("");
|
|
7711
8582
|
process.exit(command ? 1 : 0);
|
|
7712
8583
|
}
|
|
@@ -7727,6 +8598,8 @@ var init_guard = __esm({
|
|
|
7727
8598
|
init_git_hooks();
|
|
7728
8599
|
init_settings_installer();
|
|
7729
8600
|
init_settings_installer2();
|
|
8601
|
+
init_cli();
|
|
8602
|
+
init_enrolment_store();
|
|
7730
8603
|
DEFAULT_POLICY_PATH = "./local-policy.yaml";
|
|
7731
8604
|
DEFAULT_CONFIG_DIR = ".astrasync";
|
|
7732
8605
|
main().catch((err) => {
|
|
@@ -7759,6 +8632,10 @@ COMMANDS:
|
|
|
7759
8632
|
verify <id> Look up an agent by ASTRA ID or UUID
|
|
7760
8633
|
health Check API health
|
|
7761
8634
|
guard <subcommand> Local Guard CLI (setup, start, status, install-claude-code, ...)
|
|
8635
|
+
guard enroll <token> Enrol this machine in your organisation's fleet
|
|
8636
|
+
(--api-base-url <url>, --system for machine scope)
|
|
8637
|
+
guard fleet-status Fleet enrolment, signed policy and heartbeat state
|
|
8638
|
+
guard managed-settings MDM templates for Claude Code / Codex (--claude, --codex, --out <dir>)
|
|
7762
8639
|
|
|
7763
8640
|
REGISTER OPTIONS:
|
|
7764
8641
|
--name <name> Agent name (required)
|