@astrasyncai/verification-gateway 5.10.0 → 5.11.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/dist/adapters/express.js +1 -1
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +1 -1
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.js +1 -1
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +1 -1
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.js +1 -1
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +1 -1
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.js +1 -1
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +1 -1
- 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 +522 -10
- package/dist/bin/astrasync-codex-hook.js +522 -10
- package/dist/bin/astrasync-guard.js +611 -65
- package/dist/bin/astrasync.js +655 -66
- package/dist/browser/background.js +1 -1
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +1 -1
- 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 +538 -18
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +540 -20
- 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 +504 -5
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +506 -5
- package/dist/cursor/extension.mjs.map +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.js +1 -1
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +1 -1
- 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 +1 -1
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +1 -1
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/index.d.mts +100 -3
- package/dist/index.d.ts +100 -3
- package/dist/index.js +3271 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3269 -18
- package/dist/index.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/verify.js +1 -1
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +1 -1
- 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.11.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
|
*/
|
|
@@ -6238,6 +6195,8 @@ var init_src = __esm({
|
|
|
6238
6195
|
init_runtime_signatures();
|
|
6239
6196
|
init_verified_hop();
|
|
6240
6197
|
init_http();
|
|
6198
|
+
init_pdlss_parser();
|
|
6199
|
+
init_schema();
|
|
6241
6200
|
init_version();
|
|
6242
6201
|
}
|
|
6243
6202
|
});
|
|
@@ -7116,7 +7075,15 @@ var init_settings_installer = __esm({
|
|
|
7116
7075
|
"~/.claude/settings.json",
|
|
7117
7076
|
"~/.claude/settings.local.json",
|
|
7118
7077
|
"**/.astrasync/**",
|
|
7119
|
-
"~/.astrasync/**"
|
|
7078
|
+
"~/.astrasync/**",
|
|
7079
|
+
// 5.11.0 fleet mode: enrolment + signed-policy cache (user scope is inside
|
|
7080
|
+
// ~/.astrasync already; listed explicitly so the intent survives edits) and
|
|
7081
|
+
// the machine-scope enrolment dirs. `//` = absolute from filesystem root;
|
|
7082
|
+
// Windows paths are matched in POSIX form (C:\ → /c/).
|
|
7083
|
+
"~/.astrasync/fleet/**",
|
|
7084
|
+
"//etc/astrasync/**",
|
|
7085
|
+
"//Library/Application Support/AstraSync/**",
|
|
7086
|
+
"//c/ProgramData/AstraSync/**"
|
|
7120
7087
|
];
|
|
7121
7088
|
GUARD_PERMISSION_DENY_RULES = PERMISSION_DENY_PATHS.map(
|
|
7122
7089
|
(p) => `Edit(${p})`
|
|
@@ -7158,6 +7125,545 @@ var init_settings_installer2 = __esm({
|
|
|
7158
7125
|
}
|
|
7159
7126
|
});
|
|
7160
7127
|
|
|
7128
|
+
// src/fleet/client.ts
|
|
7129
|
+
function base(url) {
|
|
7130
|
+
return url.replace(/\/+$/, "");
|
|
7131
|
+
}
|
|
7132
|
+
async function call(url, init, timeoutMs) {
|
|
7133
|
+
const controller = new AbortController();
|
|
7134
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7135
|
+
try {
|
|
7136
|
+
const res = await sdkFetch(url, { ...init, signal: controller.signal });
|
|
7137
|
+
const text = await res.text();
|
|
7138
|
+
let body = text;
|
|
7139
|
+
try {
|
|
7140
|
+
body = text ? JSON.parse(text) : null;
|
|
7141
|
+
} catch {
|
|
7142
|
+
}
|
|
7143
|
+
return { status: res.status, body, headers: res.headers };
|
|
7144
|
+
} finally {
|
|
7145
|
+
clearTimeout(timer);
|
|
7146
|
+
}
|
|
7147
|
+
}
|
|
7148
|
+
function apiError(status, body, what) {
|
|
7149
|
+
const b = body ?? {};
|
|
7150
|
+
const code = b.code ?? (typeof b.error === "string" ? b.error : void 0);
|
|
7151
|
+
return new FleetApiError(`${what} failed (${status}${code ? ` ${code}` : ""})`, status, code);
|
|
7152
|
+
}
|
|
7153
|
+
async function enrolDevice(apiBaseUrl, request2, timeoutMs = 15e3) {
|
|
7154
|
+
const r = await call(
|
|
7155
|
+
`${base(apiBaseUrl)}/fleet/enrol`,
|
|
7156
|
+
{
|
|
7157
|
+
method: "POST",
|
|
7158
|
+
headers: { "Content-Type": "application/json" },
|
|
7159
|
+
body: JSON.stringify(request2)
|
|
7160
|
+
},
|
|
7161
|
+
timeoutMs
|
|
7162
|
+
);
|
|
7163
|
+
if (r.status !== 201 && r.status !== 200) throw apiError(r.status, r.body, "Enrolment");
|
|
7164
|
+
const data = r.body;
|
|
7165
|
+
if (!data?.deviceId || !data.credential || !data.jwks || !data.policyJws) {
|
|
7166
|
+
throw new FleetApiError("Enrolment response is missing required fields", r.status);
|
|
7167
|
+
}
|
|
7168
|
+
return data;
|
|
7169
|
+
}
|
|
7170
|
+
var FleetApiError;
|
|
7171
|
+
var init_client2 = __esm({
|
|
7172
|
+
"src/fleet/client.ts"() {
|
|
7173
|
+
"use strict";
|
|
7174
|
+
init_http();
|
|
7175
|
+
FleetApiError = class extends Error {
|
|
7176
|
+
constructor(message, status, code) {
|
|
7177
|
+
super(message);
|
|
7178
|
+
this.status = status;
|
|
7179
|
+
this.code = code;
|
|
7180
|
+
this.name = "FleetApiError";
|
|
7181
|
+
}
|
|
7182
|
+
};
|
|
7183
|
+
}
|
|
7184
|
+
});
|
|
7185
|
+
|
|
7186
|
+
// src/fleet/enrolment-store.ts
|
|
7187
|
+
function machineScopeDir(platform2 = process.platform, env = process.env) {
|
|
7188
|
+
if (platform2 === "darwin") return "/Library/Application Support/AstraSync";
|
|
7189
|
+
if (platform2 === "win32") return (0, import_path5.join)(env.ProgramData || "C:\\ProgramData", "AstraSync");
|
|
7190
|
+
return "/etc/astrasync";
|
|
7191
|
+
}
|
|
7192
|
+
function fleetPaths(options = {}) {
|
|
7193
|
+
const home = options.home ?? (0, import_os.homedir)();
|
|
7194
|
+
const machineDir = options.machineDir ?? options.env?.ASTRASYNC_FLEET_MACHINE_DIR ?? machineScopeDir(options.platform, options.env);
|
|
7195
|
+
const userDir = (0, import_path5.join)(home, ".astrasync", "fleet");
|
|
7196
|
+
return {
|
|
7197
|
+
machineDevice: (0, import_path5.join)(machineDir, "fleet.json"),
|
|
7198
|
+
machinePolicy: (0, import_path5.join)(machineDir, "policy.jws"),
|
|
7199
|
+
userDir,
|
|
7200
|
+
userDevice: (0, import_path5.join)(userDir, "device.json"),
|
|
7201
|
+
userPolicy: (0, import_path5.join)(userDir, "policy.jws"),
|
|
7202
|
+
userKeyChain: (0, import_path5.join)(userDir, "keychain.json"),
|
|
7203
|
+
state: (0, import_path5.join)(userDir, "state.json")
|
|
7204
|
+
};
|
|
7205
|
+
}
|
|
7206
|
+
function isEnrolment(value) {
|
|
7207
|
+
const v = value;
|
|
7208
|
+
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);
|
|
7209
|
+
}
|
|
7210
|
+
function loadEnrolment(paths) {
|
|
7211
|
+
const candidates = [
|
|
7212
|
+
[paths.machineDevice, "machine"],
|
|
7213
|
+
[paths.userDevice, "user"]
|
|
7214
|
+
];
|
|
7215
|
+
for (const [path2, scope] of candidates) {
|
|
7216
|
+
if (!(0, import_fs6.existsSync)(path2)) continue;
|
|
7217
|
+
try {
|
|
7218
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(path2, "utf-8"));
|
|
7219
|
+
if (isEnrolment(parsed)) return { enrolment: parsed, scope };
|
|
7220
|
+
} catch {
|
|
7221
|
+
}
|
|
7222
|
+
return { corrupt: true, path: path2 };
|
|
7223
|
+
}
|
|
7224
|
+
return null;
|
|
7225
|
+
}
|
|
7226
|
+
function writeFileMode(path2, content, mode) {
|
|
7227
|
+
(0, import_fs6.mkdirSync)((0, import_path5.dirname)(path2), { recursive: true, mode: 448 });
|
|
7228
|
+
(0, import_fs6.writeFileSync)(path2, content, { encoding: "utf-8", mode });
|
|
7229
|
+
try {
|
|
7230
|
+
(0, import_fs6.chmodSync)(path2, mode);
|
|
7231
|
+
} catch {
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7234
|
+
function saveEnrolment(paths, enrolment, scope) {
|
|
7235
|
+
const path2 = scope === "machine" ? paths.machineDevice : paths.userDevice;
|
|
7236
|
+
writeFileMode(path2, JSON.stringify(enrolment, null, 2), scope === "machine" ? 420 : 384);
|
|
7237
|
+
return path2;
|
|
7238
|
+
}
|
|
7239
|
+
function readCachedPolicies(paths) {
|
|
7240
|
+
const out = [];
|
|
7241
|
+
for (const p of [paths.userPolicy, paths.machinePolicy]) {
|
|
7242
|
+
try {
|
|
7243
|
+
if ((0, import_fs6.existsSync)(p)) out.push((0, import_fs6.readFileSync)(p, "utf-8").trim());
|
|
7244
|
+
} catch {
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
return out.filter(Boolean);
|
|
7248
|
+
}
|
|
7249
|
+
function writeCachedPolicy(paths, jws) {
|
|
7250
|
+
writeFileMode(paths.userPolicy, jws, 384);
|
|
7251
|
+
}
|
|
7252
|
+
function readKeyChain(paths) {
|
|
7253
|
+
try {
|
|
7254
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(paths.userKeyChain, "utf-8"));
|
|
7255
|
+
return Array.isArray(parsed) ? parsed.filter((j) => typeof j === "string") : [];
|
|
7256
|
+
} catch {
|
|
7257
|
+
return [];
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
function writeKeyChain(paths, chain) {
|
|
7261
|
+
writeFileMode(paths.userKeyChain, JSON.stringify(chain), 384);
|
|
7262
|
+
}
|
|
7263
|
+
function writeMachinePolicy(paths, jws) {
|
|
7264
|
+
writeFileMode(paths.machinePolicy, jws, 420);
|
|
7265
|
+
}
|
|
7266
|
+
function readState(paths) {
|
|
7267
|
+
try {
|
|
7268
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(paths.state, "utf-8"));
|
|
7269
|
+
const c = parsed.counts;
|
|
7270
|
+
return {
|
|
7271
|
+
...parsed,
|
|
7272
|
+
counts: { allow: c?.allow ?? 0, deny: c?.deny ?? 0, ask: c?.ask ?? 0 }
|
|
7273
|
+
};
|
|
7274
|
+
} catch {
|
|
7275
|
+
return { counts: { allow: 0, deny: 0, ask: 0 } };
|
|
7276
|
+
}
|
|
7277
|
+
}
|
|
7278
|
+
function writeState(paths, state) {
|
|
7279
|
+
try {
|
|
7280
|
+
writeFileMode(paths.state, JSON.stringify(state, null, 2), 384);
|
|
7281
|
+
} catch {
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
var import_fs6, import_os, import_path5;
|
|
7285
|
+
var init_enrolment_store = __esm({
|
|
7286
|
+
"src/fleet/enrolment-store.ts"() {
|
|
7287
|
+
"use strict";
|
|
7288
|
+
import_fs6 = require("fs");
|
|
7289
|
+
import_os = require("os");
|
|
7290
|
+
import_path5 = require("path");
|
|
7291
|
+
}
|
|
7292
|
+
});
|
|
7293
|
+
|
|
7294
|
+
// src/fleet/types.ts
|
|
7295
|
+
var FLEET_POLICY_TYP, FLEET_POLICY_AUD, FLEET_POLICY_ISS, FLEET_KEYS_TYP, FLEET_KEYS_AUD, DEFAULT_HEARTBEAT_INTERVAL_SEC;
|
|
7296
|
+
var init_types3 = __esm({
|
|
7297
|
+
"src/fleet/types.ts"() {
|
|
7298
|
+
"use strict";
|
|
7299
|
+
FLEET_POLICY_TYP = "fleet-policy+jwt";
|
|
7300
|
+
FLEET_POLICY_AUD = "astrasync:fleet-policy";
|
|
7301
|
+
FLEET_POLICY_ISS = "astrasync.ai";
|
|
7302
|
+
FLEET_KEYS_TYP = "fleet-keys+jwt";
|
|
7303
|
+
FLEET_KEYS_AUD = "astrasync:fleet-keys";
|
|
7304
|
+
DEFAULT_HEARTBEAT_INTERVAL_SEC = 900;
|
|
7305
|
+
}
|
|
7306
|
+
});
|
|
7307
|
+
|
|
7308
|
+
// src/fleet/policy-verify.ts
|
|
7309
|
+
function sha256Hex(text) {
|
|
7310
|
+
return (0, import_crypto.createHash)("sha256").update(text, "utf8").digest("hex");
|
|
7311
|
+
}
|
|
7312
|
+
function parseSettings(raw) {
|
|
7313
|
+
if (!raw || typeof raw !== "object") throw new FleetPolicyError("settings missing");
|
|
7314
|
+
const s = raw;
|
|
7315
|
+
if (s.posture !== "active" && s.posture !== "passive") {
|
|
7316
|
+
throw new FleetPolicyError("settings.posture must be active or passive");
|
|
7317
|
+
}
|
|
7318
|
+
if (s.failOpen !== void 0 && typeof s.failOpen !== "boolean") {
|
|
7319
|
+
throw new FleetPolicyError("settings.failOpen must be a boolean");
|
|
7320
|
+
}
|
|
7321
|
+
if (s.governMcp !== void 0 && typeof s.governMcp !== "boolean") {
|
|
7322
|
+
throw new FleetPolicyError("settings.governMcp must be a boolean");
|
|
7323
|
+
}
|
|
7324
|
+
if (s.skipTools !== void 0 && !(Array.isArray(s.skipTools) && s.skipTools.every((t) => typeof t === "string"))) {
|
|
7325
|
+
throw new FleetPolicyError("settings.skipTools must be a string array");
|
|
7326
|
+
}
|
|
7327
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
|
|
7328
|
+
return {
|
|
7329
|
+
posture: s.posture,
|
|
7330
|
+
failOpen: s.failOpen === true,
|
|
7331
|
+
...typeof s.governMcp === "boolean" && { governMcp: s.governMcp },
|
|
7332
|
+
...Array.isArray(s.skipTools) && { skipTools: s.skipTools },
|
|
7333
|
+
...num(s.heartbeatIntervalSec) && { heartbeatIntervalSec: num(s.heartbeatIntervalSec) },
|
|
7334
|
+
...num(s.staleAfterSec) && { staleAfterSec: num(s.staleAfterSec) }
|
|
7335
|
+
};
|
|
7336
|
+
}
|
|
7337
|
+
async function verifyFleetPolicy(jws, jwks, options) {
|
|
7338
|
+
let payloadBytes;
|
|
7339
|
+
let header;
|
|
7340
|
+
try {
|
|
7341
|
+
const keySet = (0, import_jose2.createLocalJWKSet)(jwks);
|
|
7342
|
+
const result = await (0, import_jose2.compactVerify)(jws, keySet, { algorithms: ["ES256"] });
|
|
7343
|
+
payloadBytes = result.payload;
|
|
7344
|
+
header = result.protectedHeader;
|
|
7345
|
+
} catch (err) {
|
|
7346
|
+
throw new FleetPolicyError(
|
|
7347
|
+
`signature verification failed (${err instanceof Error ? err.message : String(err)})`
|
|
7348
|
+
);
|
|
7349
|
+
}
|
|
7350
|
+
if (header.typ !== FLEET_POLICY_TYP) throw new FleetPolicyError(`unexpected typ ${header.typ}`);
|
|
7351
|
+
let claims;
|
|
7352
|
+
try {
|
|
7353
|
+
claims = JSON.parse(new TextDecoder().decode(payloadBytes));
|
|
7354
|
+
} catch {
|
|
7355
|
+
throw new FleetPolicyError("payload is not JSON");
|
|
7356
|
+
}
|
|
7357
|
+
if (claims.aud !== FLEET_POLICY_AUD) throw new FleetPolicyError(`unexpected aud ${claims.aud}`);
|
|
7358
|
+
if (claims.iss !== FLEET_POLICY_ISS) throw new FleetPolicyError(`unexpected iss ${claims.iss}`);
|
|
7359
|
+
if (!options.expectedSubs.includes(claims.sub)) {
|
|
7360
|
+
throw new FleetPolicyError("policy was issued for a different organisation or device");
|
|
7361
|
+
}
|
|
7362
|
+
if (!Number.isInteger(claims.ver) || claims.ver < 1) {
|
|
7363
|
+
throw new FleetPolicyError("ver must be a positive integer");
|
|
7364
|
+
}
|
|
7365
|
+
if (options.minVer !== void 0 && claims.ver < options.minVer) {
|
|
7366
|
+
throw new FleetPolicyError(
|
|
7367
|
+
`policy version ${claims.ver} is older than the accepted version ${options.minVer} (rollback)`
|
|
7368
|
+
);
|
|
7369
|
+
}
|
|
7370
|
+
if (typeof claims.yaml !== "string" || sha256Hex(claims.yaml) !== claims.sha256) {
|
|
7371
|
+
throw new FleetPolicyError("policy hash mismatch");
|
|
7372
|
+
}
|
|
7373
|
+
const settings = parseSettings(claims.settings);
|
|
7374
|
+
const parsed = parseYaml(claims.yaml);
|
|
7375
|
+
if (!parsed.policy) {
|
|
7376
|
+
const first = parsed.validation.errors[0];
|
|
7377
|
+
throw new FleetPolicyError(
|
|
7378
|
+
`signed policy is invalid${first ? ` (${first.path}: ${first.message})` : ""}`
|
|
7379
|
+
);
|
|
7380
|
+
}
|
|
7381
|
+
return { kid: header.kid, claims: { ...claims, settings }, policy: parsed.policy, settings, jws };
|
|
7382
|
+
}
|
|
7383
|
+
async function resolveFleetJwks(pinned, chain, ownerId) {
|
|
7384
|
+
let trusted = pinned;
|
|
7385
|
+
let lastSeq = 0;
|
|
7386
|
+
for (const link of chain) {
|
|
7387
|
+
try {
|
|
7388
|
+
const { payload, protectedHeader } = await (0, import_jose2.compactVerify)(
|
|
7389
|
+
link,
|
|
7390
|
+
(0, import_jose2.createLocalJWKSet)(trusted),
|
|
7391
|
+
{ algorithms: ["ES256"] }
|
|
7392
|
+
);
|
|
7393
|
+
if (protectedHeader.typ !== FLEET_KEYS_TYP) continue;
|
|
7394
|
+
const c = JSON.parse(new TextDecoder().decode(payload));
|
|
7395
|
+
if (c.aud !== FLEET_KEYS_AUD || c.iss !== FLEET_POLICY_ISS || c.sub !== ownerId) continue;
|
|
7396
|
+
if (!Number.isInteger(c.seq) || c.seq <= lastSeq) continue;
|
|
7397
|
+
const keys = Array.isArray(c.keys) ? c.keys.filter((k) => k && k.kty === "EC" && k.crv === "P-256" && !("d" in k)) : [];
|
|
7398
|
+
if (keys.length === 0) continue;
|
|
7399
|
+
trusted = { keys };
|
|
7400
|
+
lastSeq = c.seq;
|
|
7401
|
+
} catch {
|
|
7402
|
+
}
|
|
7403
|
+
}
|
|
7404
|
+
return trusted;
|
|
7405
|
+
}
|
|
7406
|
+
async function pickNewestValid(candidates, jwks, options) {
|
|
7407
|
+
let best = null;
|
|
7408
|
+
let lastError = new FleetPolicyError("no cached fleet policy");
|
|
7409
|
+
for (const jws of candidates) {
|
|
7410
|
+
try {
|
|
7411
|
+
const v = await verifyFleetPolicy(jws, jwks, options);
|
|
7412
|
+
if (!best || v.claims.ver > best.claims.ver) best = v;
|
|
7413
|
+
} catch (err) {
|
|
7414
|
+
lastError = err instanceof Error ? err : new FleetPolicyError(String(err));
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
if (!best) throw lastError;
|
|
7418
|
+
return best;
|
|
7419
|
+
}
|
|
7420
|
+
var import_crypto, import_jose2, FleetPolicyError;
|
|
7421
|
+
var init_policy_verify = __esm({
|
|
7422
|
+
"src/fleet/policy-verify.ts"() {
|
|
7423
|
+
"use strict";
|
|
7424
|
+
import_crypto = require("crypto");
|
|
7425
|
+
import_jose2 = require("jose");
|
|
7426
|
+
init_pdlss_parser();
|
|
7427
|
+
init_types3();
|
|
7428
|
+
FleetPolicyError = class extends Error {
|
|
7429
|
+
constructor(message) {
|
|
7430
|
+
super(message);
|
|
7431
|
+
this.name = "FleetPolicyError";
|
|
7432
|
+
}
|
|
7433
|
+
};
|
|
7434
|
+
}
|
|
7435
|
+
});
|
|
7436
|
+
|
|
7437
|
+
// src/fleet/cli.ts
|
|
7438
|
+
function machineFingerprint() {
|
|
7439
|
+
const candidates = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
|
|
7440
|
+
let raw = "";
|
|
7441
|
+
for (const c of candidates) {
|
|
7442
|
+
try {
|
|
7443
|
+
if ((0, import_fs7.existsSync)(c)) {
|
|
7444
|
+
raw = (0, import_fs7.readFileSync)(c, "utf-8").trim();
|
|
7445
|
+
if (raw) break;
|
|
7446
|
+
}
|
|
7447
|
+
} catch {
|
|
7448
|
+
}
|
|
7449
|
+
}
|
|
7450
|
+
if (!raw) raw = `${(0, import_os2.hostname)()}|${(0, import_os2.platform)()}|${(0, import_os2.arch)()}`;
|
|
7451
|
+
return (0, import_crypto2.createHash)("sha256").update(`astrasync-fleet:${raw}`).digest("hex");
|
|
7452
|
+
}
|
|
7453
|
+
async function runEnroll(options) {
|
|
7454
|
+
const print = options.print ?? ((l) => console.log(l));
|
|
7455
|
+
const token = options.token.trim();
|
|
7456
|
+
if (!token)
|
|
7457
|
+
throw new Error("Usage: astrasync guard enroll <token> [--api-base-url <url>] [--system]");
|
|
7458
|
+
const apiBaseUrl = (options.apiBaseUrl ?? DEFAULT_FLEET_API_BASE_URL).replace(/\/+$/, "");
|
|
7459
|
+
const paths = options.paths ?? fleetPaths();
|
|
7460
|
+
const response = await enrolDevice(apiBaseUrl, {
|
|
7461
|
+
token,
|
|
7462
|
+
fingerprint: options.fingerprint ?? machineFingerprint(),
|
|
7463
|
+
hostname: (0, import_os2.hostname)(),
|
|
7464
|
+
os: { platform: (0, import_os2.platform)(), release: (0, import_os2.release)(), arch: (0, import_os2.arch)() },
|
|
7465
|
+
guardVersion: SDK_VERSION,
|
|
7466
|
+
adapters: options.adapters ?? [
|
|
7467
|
+
{ kind: "claude-code", managed: false },
|
|
7468
|
+
{ kind: "codex", managed: false }
|
|
7469
|
+
]
|
|
7470
|
+
});
|
|
7471
|
+
const enrolment = {
|
|
7472
|
+
version: 1,
|
|
7473
|
+
deviceId: response.deviceId,
|
|
7474
|
+
credential: response.credential,
|
|
7475
|
+
org: response.org,
|
|
7476
|
+
apiBaseUrl: response.apiBaseUrl || apiBaseUrl,
|
|
7477
|
+
jwks: response.jwks,
|
|
7478
|
+
heartbeatIntervalSec: response.heartbeatIntervalSec || DEFAULT_HEARTBEAT_INTERVAL_SEC,
|
|
7479
|
+
enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7480
|
+
};
|
|
7481
|
+
const verified = await verifyFleetPolicy(response.policyJws, enrolment.jwks, {
|
|
7482
|
+
expectedSubs: [enrolment.org.ownerId, enrolment.deviceId]
|
|
7483
|
+
});
|
|
7484
|
+
const scope = options.system ? "machine" : "user";
|
|
7485
|
+
const savedTo = saveEnrolment(paths, enrolment, scope);
|
|
7486
|
+
if (options.system) writeMachinePolicy(paths, response.policyJws);
|
|
7487
|
+
writeCachedPolicy(paths, response.policyJws);
|
|
7488
|
+
writeKeyChain(paths, []);
|
|
7489
|
+
const state = readState(paths);
|
|
7490
|
+
writeState(paths, {
|
|
7491
|
+
...state,
|
|
7492
|
+
appliedSha256: verified.claims.sha256,
|
|
7493
|
+
maxVer: Math.max(state.maxVer ?? 0, verified.claims.ver),
|
|
7494
|
+
revoked: false
|
|
7495
|
+
});
|
|
7496
|
+
print(
|
|
7497
|
+
`Enrolled in ${enrolment.org.name ?? enrolment.org.ownerId} (device ${enrolment.deviceId}).`
|
|
7498
|
+
);
|
|
7499
|
+
print(` Enrolment: ${savedTo} (${scope} scope)`);
|
|
7500
|
+
print(` Policy: v${verified.claims.ver}, posture ${verified.settings.posture}`);
|
|
7501
|
+
print(
|
|
7502
|
+
" Local policy.yaml / guard.json files are now ignored \u2014 the organisation policy governs."
|
|
7503
|
+
);
|
|
7504
|
+
return enrolment;
|
|
7505
|
+
}
|
|
7506
|
+
async function getFleetStatus(paths = fleetPaths()) {
|
|
7507
|
+
const loaded = loadEnrolment(paths);
|
|
7508
|
+
if (!loaded) return { enrolled: false };
|
|
7509
|
+
if ("corrupt" in loaded) return { enrolled: true, corrupt: loaded.path };
|
|
7510
|
+
const { enrolment, scope } = loaded;
|
|
7511
|
+
const state = readState(paths);
|
|
7512
|
+
let policy;
|
|
7513
|
+
try {
|
|
7514
|
+
const jwks = await resolveFleetJwks(enrolment.jwks, readKeyChain(paths), enrolment.org.ownerId);
|
|
7515
|
+
const v = await pickNewestValid(readCachedPolicies(paths), jwks, {
|
|
7516
|
+
expectedSubs: [enrolment.org.ownerId, enrolment.deviceId],
|
|
7517
|
+
minVer: state.maxVer
|
|
7518
|
+
});
|
|
7519
|
+
policy = {
|
|
7520
|
+
ver: v.claims.ver,
|
|
7521
|
+
sha256: v.claims.sha256,
|
|
7522
|
+
posture: v.settings.posture,
|
|
7523
|
+
failOpen: v.settings.failOpen
|
|
7524
|
+
};
|
|
7525
|
+
} catch (err) {
|
|
7526
|
+
policy = { error: err instanceof Error ? err.message : String(err) };
|
|
7527
|
+
}
|
|
7528
|
+
return {
|
|
7529
|
+
enrolled: true,
|
|
7530
|
+
scope,
|
|
7531
|
+
org: enrolment.org,
|
|
7532
|
+
deviceId: enrolment.deviceId,
|
|
7533
|
+
apiBaseUrl: enrolment.apiBaseUrl,
|
|
7534
|
+
policy,
|
|
7535
|
+
lastHeartbeatAt: state.lastHeartbeatAt,
|
|
7536
|
+
revoked: state.revoked === true,
|
|
7537
|
+
lastError: state.lastError,
|
|
7538
|
+
pendingCounts: state.counts
|
|
7539
|
+
};
|
|
7540
|
+
}
|
|
7541
|
+
function formatFleetStatus(status) {
|
|
7542
|
+
if (!status.enrolled) return ["Fleet: not enrolled (local policy mode)."];
|
|
7543
|
+
if (status.corrupt) {
|
|
7544
|
+
return [`Fleet: enrolment file ${status.corrupt} is unreadable \u2014 the guard fails closed.`];
|
|
7545
|
+
}
|
|
7546
|
+
const lines = [
|
|
7547
|
+
`Fleet: enrolled in ${status.org?.name ?? status.org?.ownerId} (${status.scope} scope)`,
|
|
7548
|
+
` Device: ${status.deviceId}`,
|
|
7549
|
+
` Platform: ${status.apiBaseUrl}`
|
|
7550
|
+
];
|
|
7551
|
+
if (status.policy && "error" in status.policy) {
|
|
7552
|
+
lines.push(` Policy: NO VALID SIGNED POLICY \u2014 ${status.policy.error} (guard denies)`);
|
|
7553
|
+
} else if (status.policy) {
|
|
7554
|
+
lines.push(
|
|
7555
|
+
` Policy: v${status.policy.ver} (${status.policy.sha256.slice(0, 12)}\u2026), posture ${status.policy.posture}${status.policy.failOpen ? ", fail-open" : ""}`
|
|
7556
|
+
);
|
|
7557
|
+
}
|
|
7558
|
+
lines.push(` Heartbeat: ${status.lastHeartbeatAt ?? "never"}`);
|
|
7559
|
+
if (status.revoked) {
|
|
7560
|
+
lines.push(" Status: REVOKED by the organisation \u2014 enforcing the last good policy");
|
|
7561
|
+
}
|
|
7562
|
+
if (status.lastError) lines.push(` Last sync error: ${status.lastError}`);
|
|
7563
|
+
return lines;
|
|
7564
|
+
}
|
|
7565
|
+
function managedHookCommand(target, os2) {
|
|
7566
|
+
const bin = target === "claude" ? "astrasync-claude-hook" : "astrasync-codex-hook";
|
|
7567
|
+
if (os2 === "win32") return `"C:\\Program Files\\nodejs\\${bin}.cmd"`;
|
|
7568
|
+
return `/usr/local/bin/${bin}`;
|
|
7569
|
+
}
|
|
7570
|
+
function buildClaudeManagedSettings(os2) {
|
|
7571
|
+
return {
|
|
7572
|
+
allowManagedHooksOnly: true,
|
|
7573
|
+
env: { ASTRASYNC_FLEET_REQUIRED: "1" },
|
|
7574
|
+
hooks: {
|
|
7575
|
+
PreToolUse: [
|
|
7576
|
+
{
|
|
7577
|
+
matcher: HOOK_MATCHER,
|
|
7578
|
+
hooks: [{ type: "command", command: managedHookCommand("claude", os2), timeout: 10 }]
|
|
7579
|
+
}
|
|
7580
|
+
]
|
|
7581
|
+
},
|
|
7582
|
+
permissions: { deny: [...GUARD_PERMISSION_DENY_RULES] }
|
|
7583
|
+
};
|
|
7584
|
+
}
|
|
7585
|
+
function tomlString(value) {
|
|
7586
|
+
return JSON.stringify(value);
|
|
7587
|
+
}
|
|
7588
|
+
function buildCodexRequirementsToml(os2) {
|
|
7589
|
+
const command = os2 === "win32" ? `set ASTRASYNC_FLEET_REQUIRED=1&& ${managedHookCommand("codex", os2)}` : `ASTRASYNC_FLEET_REQUIRED=1 ${managedHookCommand("codex", os2)}`;
|
|
7590
|
+
return [
|
|
7591
|
+
"# AstraSync Local Guard \u2014 managed hook (generated by `astrasync guard managed-settings --codex`)",
|
|
7592
|
+
`# Install at: ${MANAGED_SETTINGS_LOCATIONS.codex[os2]}`,
|
|
7593
|
+
"allow_managed_hooks_only = true",
|
|
7594
|
+
"",
|
|
7595
|
+
"[features]",
|
|
7596
|
+
"hooks = true",
|
|
7597
|
+
"",
|
|
7598
|
+
"[[hooks.PreToolUse]]",
|
|
7599
|
+
`matcher = ${tomlString(CODEX_HOOK_MATCHER)}`,
|
|
7600
|
+
"",
|
|
7601
|
+
"[[hooks.PreToolUse.hooks]]",
|
|
7602
|
+
'type = "command"',
|
|
7603
|
+
`command = ${tomlString(command)}`,
|
|
7604
|
+
"timeout = 10",
|
|
7605
|
+
'statusMessage = "AstraSync Local Guard"',
|
|
7606
|
+
""
|
|
7607
|
+
].join("\n");
|
|
7608
|
+
}
|
|
7609
|
+
function buildManagedSettingsFiles(targets) {
|
|
7610
|
+
const out = [];
|
|
7611
|
+
for (const target of targets) {
|
|
7612
|
+
for (const os2 of ["darwin", "linux", "win32"]) {
|
|
7613
|
+
const content = target === "claude" ? `${JSON.stringify(buildClaudeManagedSettings(os2), null, 2)}
|
|
7614
|
+
` : buildCodexRequirementsToml(os2);
|
|
7615
|
+
out.push({
|
|
7616
|
+
target,
|
|
7617
|
+
os: os2,
|
|
7618
|
+
installPath: MANAGED_SETTINGS_LOCATIONS[target][os2],
|
|
7619
|
+
fileName: `${target}-${os2}-${target === "claude" ? "managed-settings.json" : "requirements.toml"}`,
|
|
7620
|
+
content
|
|
7621
|
+
});
|
|
7622
|
+
}
|
|
7623
|
+
}
|
|
7624
|
+
return out;
|
|
7625
|
+
}
|
|
7626
|
+
function writeManagedSettings(files, outDir) {
|
|
7627
|
+
(0, import_fs7.mkdirSync)(outDir, { recursive: true });
|
|
7628
|
+
return files.map((f) => {
|
|
7629
|
+
const p = (0, import_path6.join)(outDir, f.fileName);
|
|
7630
|
+
(0, import_fs7.writeFileSync)(p, f.content, "utf-8");
|
|
7631
|
+
return p;
|
|
7632
|
+
});
|
|
7633
|
+
}
|
|
7634
|
+
var import_crypto2, import_fs7, import_os2, import_path6, DEFAULT_FLEET_API_BASE_URL, MANAGED_SETTINGS_LOCATIONS;
|
|
7635
|
+
var init_cli = __esm({
|
|
7636
|
+
"src/fleet/cli.ts"() {
|
|
7637
|
+
"use strict";
|
|
7638
|
+
import_crypto2 = require("crypto");
|
|
7639
|
+
import_fs7 = require("fs");
|
|
7640
|
+
import_os2 = require("os");
|
|
7641
|
+
import_path6 = require("path");
|
|
7642
|
+
init_settings_installer();
|
|
7643
|
+
init_settings_installer2();
|
|
7644
|
+
init_version();
|
|
7645
|
+
init_client2();
|
|
7646
|
+
init_enrolment_store();
|
|
7647
|
+
init_policy_verify();
|
|
7648
|
+
init_types3();
|
|
7649
|
+
DEFAULT_FLEET_API_BASE_URL = "https://astrasync.ai/api";
|
|
7650
|
+
MANAGED_SETTINGS_LOCATIONS = {
|
|
7651
|
+
// code.claude.com/docs/en/managed-settings
|
|
7652
|
+
claude: {
|
|
7653
|
+
darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
|
|
7654
|
+
linux: "/etc/claude-code/managed-settings.json",
|
|
7655
|
+
win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
|
|
7656
|
+
},
|
|
7657
|
+
// learn.chatgpt.com/docs/enterprise/managed-configuration
|
|
7658
|
+
codex: {
|
|
7659
|
+
darwin: "/etc/codex/requirements.toml",
|
|
7660
|
+
linux: "/etc/codex/requirements.toml",
|
|
7661
|
+
win32: "%ProgramData%\\OpenAI\\Codex\\requirements.toml"
|
|
7662
|
+
}
|
|
7663
|
+
};
|
|
7664
|
+
}
|
|
7665
|
+
});
|
|
7666
|
+
|
|
7161
7667
|
// src/cli/guard.ts
|
|
7162
7668
|
var guard_exports = {};
|
|
7163
7669
|
function createPrompt() {
|
|
@@ -7296,6 +7802,9 @@ async function cmdStatus() {
|
|
|
7296
7802
|
}
|
|
7297
7803
|
});
|
|
7298
7804
|
console.log("\nAstraSync Local Guard Status\n");
|
|
7805
|
+
for (const line of formatFleetStatus(await getFleetStatus(fleetPaths()))) {
|
|
7806
|
+
console.log(` ${line}`);
|
|
7807
|
+
}
|
|
7299
7808
|
if (upgraded) {
|
|
7300
7809
|
const config = JSON.parse(fs.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
|
|
7301
7810
|
console.log(` Mode: ${config.mode}`);
|
|
@@ -7651,6 +8160,66 @@ function printClaudeCodeStatus() {
|
|
|
7651
8160
|
}
|
|
7652
8161
|
}
|
|
7653
8162
|
}
|
|
8163
|
+
function flagValue(flag) {
|
|
8164
|
+
const i = process.argv.indexOf(flag);
|
|
8165
|
+
return i !== -1 ? process.argv[i + 1] : void 0;
|
|
8166
|
+
}
|
|
8167
|
+
function positionalAfter(command) {
|
|
8168
|
+
const argv = process.argv;
|
|
8169
|
+
const start = argv.indexOf(command) + 1;
|
|
8170
|
+
for (let i = start; i < argv.length; i++) {
|
|
8171
|
+
if (argv[i].startsWith("--")) {
|
|
8172
|
+
if (argv[i] === "--api-base-url" || argv[i] === "--out") i++;
|
|
8173
|
+
continue;
|
|
8174
|
+
}
|
|
8175
|
+
return argv[i];
|
|
8176
|
+
}
|
|
8177
|
+
return void 0;
|
|
8178
|
+
}
|
|
8179
|
+
async function cmdEnroll() {
|
|
8180
|
+
const token = positionalAfter("enroll") ?? "";
|
|
8181
|
+
try {
|
|
8182
|
+
await runEnroll({
|
|
8183
|
+
token,
|
|
8184
|
+
apiBaseUrl: flagValue("--api-base-url"),
|
|
8185
|
+
system: process.argv.includes("--system")
|
|
8186
|
+
});
|
|
8187
|
+
} catch (err) {
|
|
8188
|
+
console.error(`Enrolment failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8189
|
+
if (process.argv.includes("--system")) {
|
|
8190
|
+
console.error(
|
|
8191
|
+
" (--system writes the machine-scope enrolment and needs administrator rights)"
|
|
8192
|
+
);
|
|
8193
|
+
}
|
|
8194
|
+
process.exit(1);
|
|
8195
|
+
}
|
|
8196
|
+
}
|
|
8197
|
+
async function cmdFleetStatus() {
|
|
8198
|
+
const status = await getFleetStatus(fleetPaths());
|
|
8199
|
+
for (const line of formatFleetStatus(status)) console.log(line);
|
|
8200
|
+
}
|
|
8201
|
+
async function cmdManagedSettings() {
|
|
8202
|
+
const targets = [];
|
|
8203
|
+
if (process.argv.includes("--claude")) targets.push("claude");
|
|
8204
|
+
if (process.argv.includes("--codex")) targets.push("codex");
|
|
8205
|
+
if (targets.length === 0) targets.push("claude", "codex");
|
|
8206
|
+
const files = buildManagedSettingsFiles(targets);
|
|
8207
|
+
const outDir = flagValue("--out");
|
|
8208
|
+
if (outDir) {
|
|
8209
|
+
const written = writeManagedSettings(files, outDir);
|
|
8210
|
+
console.log("Managed-settings templates written:");
|
|
8211
|
+
files.forEach((f, i) => console.log(` ${written[i]} \u2192 deploy to ${f.installPath}`));
|
|
8212
|
+
} else {
|
|
8213
|
+
for (const f of files) {
|
|
8214
|
+
console.log(`===== ${f.target} \xB7 ${f.os} \u2192 ${f.installPath}`);
|
|
8215
|
+
console.log(f.content);
|
|
8216
|
+
}
|
|
8217
|
+
}
|
|
8218
|
+
console.log(
|
|
8219
|
+
"Hook commands assume npm\u2019s default global prefix; adjust if your MDM installs elsewhere."
|
|
8220
|
+
);
|
|
8221
|
+
console.log("Machines must also be enrolled: astrasync guard enroll <token> --system");
|
|
8222
|
+
}
|
|
7654
8223
|
async function main() {
|
|
7655
8224
|
const command = process.argv[2] === "guard" ? process.argv[3] : process.argv[2];
|
|
7656
8225
|
switch (command) {
|
|
@@ -7687,6 +8256,16 @@ async function main() {
|
|
|
7687
8256
|
case "uninstall-codex":
|
|
7688
8257
|
await cmdUninstallCodex();
|
|
7689
8258
|
break;
|
|
8259
|
+
case "enroll":
|
|
8260
|
+
case "enrol":
|
|
8261
|
+
await cmdEnroll();
|
|
8262
|
+
break;
|
|
8263
|
+
case "fleet-status":
|
|
8264
|
+
await cmdFleetStatus();
|
|
8265
|
+
break;
|
|
8266
|
+
case "managed-settings":
|
|
8267
|
+
await cmdManagedSettings();
|
|
8268
|
+
break;
|
|
7690
8269
|
default:
|
|
7691
8270
|
console.log("AstraSync Local Guard\n");
|
|
7692
8271
|
console.log("Usage: astrasync guard <command>\n");
|
|
@@ -7707,6 +8286,10 @@ async function main() {
|
|
|
7707
8286
|
console.log(" install-codex Install the Local Guard hook for OpenAI Codex CLI");
|
|
7708
8287
|
console.log(" (same flags; policy shared with Claude Code)");
|
|
7709
8288
|
console.log(" uninstall-codex Remove the Codex hook (--project for project scope)");
|
|
8289
|
+
console.log(" enroll <token> Enrol this machine in your organisation fleet");
|
|
8290
|
+
console.log(" (--api-base-url <url>, --system for machine scope)");
|
|
8291
|
+
console.log(" fleet-status Show fleet enrolment, signed policy and heartbeat");
|
|
8292
|
+
console.log(" managed-settings Print MDM templates (--claude, --codex, --out <dir>)");
|
|
7710
8293
|
console.log("");
|
|
7711
8294
|
process.exit(command ? 1 : 0);
|
|
7712
8295
|
}
|
|
@@ -7727,6 +8310,8 @@ var init_guard = __esm({
|
|
|
7727
8310
|
init_git_hooks();
|
|
7728
8311
|
init_settings_installer();
|
|
7729
8312
|
init_settings_installer2();
|
|
8313
|
+
init_cli();
|
|
8314
|
+
init_enrolment_store();
|
|
7730
8315
|
DEFAULT_POLICY_PATH = "./local-policy.yaml";
|
|
7731
8316
|
DEFAULT_CONFIG_DIR = ".astrasync";
|
|
7732
8317
|
main().catch((err) => {
|
|
@@ -7759,6 +8344,10 @@ COMMANDS:
|
|
|
7759
8344
|
verify <id> Look up an agent by ASTRA ID or UUID
|
|
7760
8345
|
health Check API health
|
|
7761
8346
|
guard <subcommand> Local Guard CLI (setup, start, status, install-claude-code, ...)
|
|
8347
|
+
guard enroll <token> Enrol this machine in your organisation's fleet
|
|
8348
|
+
(--api-base-url <url>, --system for machine scope)
|
|
8349
|
+
guard fleet-status Fleet enrolment, signed policy and heartbeat state
|
|
8350
|
+
guard managed-settings MDM templates for Claude Code / Codex (--claude, --codex, --out <dir>)
|
|
7762
8351
|
|
|
7763
8352
|
REGISTER OPTIONS:
|
|
7764
8353
|
--name <name> Agent name (required)
|