@astrasyncai/verification-gateway 5.9.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 +19 -1
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +19 -1
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.js +19 -1
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +19 -1
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.js +19 -1
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +19 -1
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.js +19 -1
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +19 -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 +540 -10
- package/dist/bin/astrasync-codex-hook.js +540 -10
- package/dist/bin/astrasync-guard.js +629 -65
- package/dist/bin/astrasync.js +673 -66
- package/dist/browser/background.js +19 -1
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +19 -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 +556 -18
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +558 -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 +522 -5
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +524 -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 +20 -2
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +20 -2
- 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 +19 -1
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +19 -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 +3289 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3287 -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 +19 -1
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +19 -1
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -3713,7 +3713,7 @@ Policy saved to ${outputPath}`);
|
|
|
3713
3713
|
};
|
|
3714
3714
|
|
|
3715
3715
|
// src/version.ts
|
|
3716
|
-
var SDK_VERSION = "5.
|
|
3716
|
+
var SDK_VERSION = "5.11.0";
|
|
3717
3717
|
|
|
3718
3718
|
// src/http.ts
|
|
3719
3719
|
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
@@ -3793,27 +3793,23 @@ var UpgradeFlow = class {
|
|
|
3793
3793
|
*/
|
|
3794
3794
|
async continueUpgrade(apiKey, kydId) {
|
|
3795
3795
|
const { print, prompt } = this.callbacks;
|
|
3796
|
-
print("\n[3/5]
|
|
3797
|
-
print(
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
]
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
return uploadResult;
|
|
3814
|
-
}
|
|
3815
|
-
print(" Policy uploaded. Same rules, now with trust scoring and audit.");
|
|
3816
|
-
print(` Agent registered: ${uploadResult.agentId}`);
|
|
3796
|
+
print("\n[3/5] Link your agent");
|
|
3797
|
+
print(
|
|
3798
|
+
` Local policy ${this.config.policyPath} stays on this machine (${this.config.policy.purposes.length} purpose rules).`
|
|
3799
|
+
);
|
|
3800
|
+
print(" Register the agent in the AstraSync dashboard (or with `astrasync register`) and");
|
|
3801
|
+
print(" paste its ASTRA- id so synced decisions are attributed to it.\n");
|
|
3802
|
+
const agentInput = (await prompt(" Agent ASTRA- id (Enter to skip): ")).trim();
|
|
3803
|
+
let agentId;
|
|
3804
|
+
if (agentInput) {
|
|
3805
|
+
if (!/^ASTRA-[A-Za-z0-9_-]+$/.test(agentInput)) {
|
|
3806
|
+
return { success: false, error: `Not an ASTRA- agent id: ${agentInput}` };
|
|
3807
|
+
}
|
|
3808
|
+
agentId = agentInput;
|
|
3809
|
+
print(` Agent: ${agentId}`);
|
|
3810
|
+
} else {
|
|
3811
|
+
print(" No agent linked \u2014 decisions sync under your API key only.");
|
|
3812
|
+
}
|
|
3817
3813
|
if (kydId) {
|
|
3818
3814
|
print(` Developer: ${kydId}`);
|
|
3819
3815
|
}
|
|
@@ -3824,23 +3820,23 @@ var UpgradeFlow = class {
|
|
|
3824
3820
|
const mode = modeInput.trim().toLowerCase() === "online" ? "online" : "hybrid";
|
|
3825
3821
|
print(`
|
|
3826
3822
|
[5/5] Activating ${mode} verification`);
|
|
3827
|
-
await this.writeLocalConfig(apiKey,
|
|
3823
|
+
await this.writeLocalConfig(apiKey, agentId, mode, kydId);
|
|
3828
3824
|
print(` Config written to ${this.configDir}/config`);
|
|
3829
3825
|
print(` Mode: ${mode}`);
|
|
3830
3826
|
print(" Done!\n");
|
|
3831
|
-
print(` Your agent (${
|
|
3827
|
+
if (agentId) print(` Your agent (${agentId}) is verified on the AstraSync network.`);
|
|
3832
3828
|
if (mode === "hybrid") {
|
|
3833
3829
|
print(" Local evaluation active for speed. Cloud sync for audit and trust scoring.");
|
|
3834
3830
|
} else {
|
|
3835
3831
|
print(" Trust scoring, audit trails, and blockchain recording are active.");
|
|
3836
3832
|
}
|
|
3837
3833
|
print(`
|
|
3838
|
-
Dashboard: ${this.platformUrl}/agents
|
|
3834
|
+
Dashboard: ${this.platformUrl}/agents${agentId ? `/${agentId}` : ""}
|
|
3839
3835
|
`);
|
|
3840
3836
|
return {
|
|
3841
3837
|
success: true,
|
|
3842
3838
|
apiKey,
|
|
3843
|
-
agentId
|
|
3839
|
+
agentId,
|
|
3844
3840
|
kydId,
|
|
3845
3841
|
mode
|
|
3846
3842
|
};
|
|
@@ -3893,44 +3889,6 @@ var UpgradeFlow = class {
|
|
|
3893
3889
|
return { valid: true, error: `Could not reach platform (proceeding anyway)` };
|
|
3894
3890
|
}
|
|
3895
3891
|
}
|
|
3896
|
-
/**
|
|
3897
|
-
* Upload local policy to the AstraSync cloud.
|
|
3898
|
-
* YAML -> cloud PDLSS boundary, 1:1 mapping, no lossy transformation.
|
|
3899
|
-
*/
|
|
3900
|
-
async uploadPolicy(apiKey) {
|
|
3901
|
-
const policyYaml = toYaml(this.config.policy);
|
|
3902
|
-
try {
|
|
3903
|
-
const response = await sdkFetch(`${this.platformUrl}/api/v1/agents/policies/upload`, {
|
|
3904
|
-
method: "POST",
|
|
3905
|
-
headers: {
|
|
3906
|
-
"Content-Type": "application/json",
|
|
3907
|
-
Authorization: `Bearer ${apiKey}`
|
|
3908
|
-
},
|
|
3909
|
-
body: JSON.stringify({
|
|
3910
|
-
policy: policyYaml,
|
|
3911
|
-
policyObject: this.config.policy,
|
|
3912
|
-
format: "yaml",
|
|
3913
|
-
source: "local-guard-upgrade",
|
|
3914
|
-
name: this.config.policy.name
|
|
3915
|
-
})
|
|
3916
|
-
});
|
|
3917
|
-
if (!response.ok) {
|
|
3918
|
-
const body = await response.text();
|
|
3919
|
-
return { success: false, error: `Upload failed (${response.status}): ${body}` };
|
|
3920
|
-
}
|
|
3921
|
-
const data = await response.json();
|
|
3922
|
-
return {
|
|
3923
|
-
success: true,
|
|
3924
|
-
agentId: data.agentId || "ASTRA-PENDING",
|
|
3925
|
-
kydId: data.kydId
|
|
3926
|
-
};
|
|
3927
|
-
} catch (err) {
|
|
3928
|
-
return {
|
|
3929
|
-
success: false,
|
|
3930
|
-
error: `Network error: ${err instanceof Error ? err.message : String(err)}`
|
|
3931
|
-
};
|
|
3932
|
-
}
|
|
3933
|
-
}
|
|
3934
3892
|
/**
|
|
3935
3893
|
* Write the config file for the chosen mode.
|
|
3936
3894
|
*/
|
|
@@ -4978,6 +4936,24 @@ async function callVerifyAccessAPI(config, request2) {
|
|
|
4978
4936
|
}
|
|
4979
4937
|
};
|
|
4980
4938
|
}
|
|
4939
|
+
if (response.status === 400 && data?.error === "INVALID_AGENT_ID") {
|
|
4940
|
+
const message = typeof data?.message === "string" ? data.message : "Agent identifier must be a canonical ASTRA-* id.";
|
|
4941
|
+
return {
|
|
4942
|
+
success: true,
|
|
4943
|
+
attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
|
|
4944
|
+
access: {
|
|
4945
|
+
allowed: false,
|
|
4946
|
+
reason: "invalid_agent_id",
|
|
4947
|
+
failures: [
|
|
4948
|
+
{
|
|
4949
|
+
dimension: "identity.invalid_agent_id",
|
|
4950
|
+
message,
|
|
4951
|
+
guidance: typeof data?.guidance === "string" ? data.guidance : "Present your canonical ASTRA-* id (discover it via GET /api/agents/request-registration/{requestId} after owner approval)."
|
|
4952
|
+
}
|
|
4953
|
+
]
|
|
4954
|
+
}
|
|
4955
|
+
};
|
|
4956
|
+
}
|
|
4981
4957
|
if (!response.ok) {
|
|
4982
4958
|
if (response.status === 401) {
|
|
4983
4959
|
return {
|
|
@@ -6007,7 +5983,15 @@ var PERMISSION_DENY_PATHS = [
|
|
|
6007
5983
|
"~/.claude/settings.json",
|
|
6008
5984
|
"~/.claude/settings.local.json",
|
|
6009
5985
|
"**/.astrasync/**",
|
|
6010
|
-
"~/.astrasync/**"
|
|
5986
|
+
"~/.astrasync/**",
|
|
5987
|
+
// 5.11.0 fleet mode: enrolment + signed-policy cache (user scope is inside
|
|
5988
|
+
// ~/.astrasync already; listed explicitly so the intent survives edits) and
|
|
5989
|
+
// the machine-scope enrolment dirs. `//` = absolute from filesystem root;
|
|
5990
|
+
// Windows paths are matched in POSIX form (C:\ → /c/).
|
|
5991
|
+
"~/.astrasync/fleet/**",
|
|
5992
|
+
"//etc/astrasync/**",
|
|
5993
|
+
"//Library/Application Support/AstraSync/**",
|
|
5994
|
+
"//c/ProgramData/AstraSync/**"
|
|
6011
5995
|
];
|
|
6012
5996
|
var GUARD_PERMISSION_DENY_RULES = PERMISSION_DENY_PATHS.map(
|
|
6013
5997
|
(p) => `Edit(${p})`
|
|
@@ -6138,6 +6122,509 @@ function uninstallCodexHooks(hooksPath) {
|
|
|
6138
6122
|
return uninstallClaudeHooks(hooksPath, ASTRASYNC_CODEX_HOOK_MARKER);
|
|
6139
6123
|
}
|
|
6140
6124
|
|
|
6125
|
+
// src/fleet/cli.ts
|
|
6126
|
+
var import_crypto2 = require("crypto");
|
|
6127
|
+
var import_fs7 = require("fs");
|
|
6128
|
+
var import_os2 = require("os");
|
|
6129
|
+
var import_path6 = require("path");
|
|
6130
|
+
|
|
6131
|
+
// src/fleet/client.ts
|
|
6132
|
+
var FleetApiError = class extends Error {
|
|
6133
|
+
constructor(message, status, code) {
|
|
6134
|
+
super(message);
|
|
6135
|
+
this.status = status;
|
|
6136
|
+
this.code = code;
|
|
6137
|
+
this.name = "FleetApiError";
|
|
6138
|
+
}
|
|
6139
|
+
};
|
|
6140
|
+
function base(url) {
|
|
6141
|
+
return url.replace(/\/+$/, "");
|
|
6142
|
+
}
|
|
6143
|
+
async function call(url, init, timeoutMs) {
|
|
6144
|
+
const controller = new AbortController();
|
|
6145
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
6146
|
+
try {
|
|
6147
|
+
const res = await sdkFetch(url, { ...init, signal: controller.signal });
|
|
6148
|
+
const text = await res.text();
|
|
6149
|
+
let body = text;
|
|
6150
|
+
try {
|
|
6151
|
+
body = text ? JSON.parse(text) : null;
|
|
6152
|
+
} catch {
|
|
6153
|
+
}
|
|
6154
|
+
return { status: res.status, body, headers: res.headers };
|
|
6155
|
+
} finally {
|
|
6156
|
+
clearTimeout(timer);
|
|
6157
|
+
}
|
|
6158
|
+
}
|
|
6159
|
+
function apiError(status, body, what) {
|
|
6160
|
+
const b = body ?? {};
|
|
6161
|
+
const code = b.code ?? (typeof b.error === "string" ? b.error : void 0);
|
|
6162
|
+
return new FleetApiError(`${what} failed (${status}${code ? ` ${code}` : ""})`, status, code);
|
|
6163
|
+
}
|
|
6164
|
+
async function enrolDevice(apiBaseUrl, request2, timeoutMs = 15e3) {
|
|
6165
|
+
const r = await call(
|
|
6166
|
+
`${base(apiBaseUrl)}/fleet/enrol`,
|
|
6167
|
+
{
|
|
6168
|
+
method: "POST",
|
|
6169
|
+
headers: { "Content-Type": "application/json" },
|
|
6170
|
+
body: JSON.stringify(request2)
|
|
6171
|
+
},
|
|
6172
|
+
timeoutMs
|
|
6173
|
+
);
|
|
6174
|
+
if (r.status !== 201 && r.status !== 200) throw apiError(r.status, r.body, "Enrolment");
|
|
6175
|
+
const data = r.body;
|
|
6176
|
+
if (!data?.deviceId || !data.credential || !data.jwks || !data.policyJws) {
|
|
6177
|
+
throw new FleetApiError("Enrolment response is missing required fields", r.status);
|
|
6178
|
+
}
|
|
6179
|
+
return data;
|
|
6180
|
+
}
|
|
6181
|
+
|
|
6182
|
+
// src/fleet/enrolment-store.ts
|
|
6183
|
+
var import_fs6 = require("fs");
|
|
6184
|
+
var import_os = require("os");
|
|
6185
|
+
var import_path5 = require("path");
|
|
6186
|
+
function machineScopeDir(platform2 = process.platform, env = process.env) {
|
|
6187
|
+
if (platform2 === "darwin") return "/Library/Application Support/AstraSync";
|
|
6188
|
+
if (platform2 === "win32") return (0, import_path5.join)(env.ProgramData || "C:\\ProgramData", "AstraSync");
|
|
6189
|
+
return "/etc/astrasync";
|
|
6190
|
+
}
|
|
6191
|
+
function fleetPaths(options = {}) {
|
|
6192
|
+
const home = options.home ?? (0, import_os.homedir)();
|
|
6193
|
+
const machineDir = options.machineDir ?? options.env?.ASTRASYNC_FLEET_MACHINE_DIR ?? machineScopeDir(options.platform, options.env);
|
|
6194
|
+
const userDir = (0, import_path5.join)(home, ".astrasync", "fleet");
|
|
6195
|
+
return {
|
|
6196
|
+
machineDevice: (0, import_path5.join)(machineDir, "fleet.json"),
|
|
6197
|
+
machinePolicy: (0, import_path5.join)(machineDir, "policy.jws"),
|
|
6198
|
+
userDir,
|
|
6199
|
+
userDevice: (0, import_path5.join)(userDir, "device.json"),
|
|
6200
|
+
userPolicy: (0, import_path5.join)(userDir, "policy.jws"),
|
|
6201
|
+
userKeyChain: (0, import_path5.join)(userDir, "keychain.json"),
|
|
6202
|
+
state: (0, import_path5.join)(userDir, "state.json")
|
|
6203
|
+
};
|
|
6204
|
+
}
|
|
6205
|
+
function isEnrolment(value) {
|
|
6206
|
+
const v = value;
|
|
6207
|
+
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);
|
|
6208
|
+
}
|
|
6209
|
+
function loadEnrolment(paths) {
|
|
6210
|
+
const candidates = [
|
|
6211
|
+
[paths.machineDevice, "machine"],
|
|
6212
|
+
[paths.userDevice, "user"]
|
|
6213
|
+
];
|
|
6214
|
+
for (const [path2, scope] of candidates) {
|
|
6215
|
+
if (!(0, import_fs6.existsSync)(path2)) continue;
|
|
6216
|
+
try {
|
|
6217
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(path2, "utf-8"));
|
|
6218
|
+
if (isEnrolment(parsed)) return { enrolment: parsed, scope };
|
|
6219
|
+
} catch {
|
|
6220
|
+
}
|
|
6221
|
+
return { corrupt: true, path: path2 };
|
|
6222
|
+
}
|
|
6223
|
+
return null;
|
|
6224
|
+
}
|
|
6225
|
+
function writeFileMode(path2, content, mode) {
|
|
6226
|
+
(0, import_fs6.mkdirSync)((0, import_path5.dirname)(path2), { recursive: true, mode: 448 });
|
|
6227
|
+
(0, import_fs6.writeFileSync)(path2, content, { encoding: "utf-8", mode });
|
|
6228
|
+
try {
|
|
6229
|
+
(0, import_fs6.chmodSync)(path2, mode);
|
|
6230
|
+
} catch {
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
function saveEnrolment(paths, enrolment, scope) {
|
|
6234
|
+
const path2 = scope === "machine" ? paths.machineDevice : paths.userDevice;
|
|
6235
|
+
writeFileMode(path2, JSON.stringify(enrolment, null, 2), scope === "machine" ? 420 : 384);
|
|
6236
|
+
return path2;
|
|
6237
|
+
}
|
|
6238
|
+
function readCachedPolicies(paths) {
|
|
6239
|
+
const out = [];
|
|
6240
|
+
for (const p of [paths.userPolicy, paths.machinePolicy]) {
|
|
6241
|
+
try {
|
|
6242
|
+
if ((0, import_fs6.existsSync)(p)) out.push((0, import_fs6.readFileSync)(p, "utf-8").trim());
|
|
6243
|
+
} catch {
|
|
6244
|
+
}
|
|
6245
|
+
}
|
|
6246
|
+
return out.filter(Boolean);
|
|
6247
|
+
}
|
|
6248
|
+
function writeCachedPolicy(paths, jws) {
|
|
6249
|
+
writeFileMode(paths.userPolicy, jws, 384);
|
|
6250
|
+
}
|
|
6251
|
+
function readKeyChain(paths) {
|
|
6252
|
+
try {
|
|
6253
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(paths.userKeyChain, "utf-8"));
|
|
6254
|
+
return Array.isArray(parsed) ? parsed.filter((j) => typeof j === "string") : [];
|
|
6255
|
+
} catch {
|
|
6256
|
+
return [];
|
|
6257
|
+
}
|
|
6258
|
+
}
|
|
6259
|
+
function writeKeyChain(paths, chain) {
|
|
6260
|
+
writeFileMode(paths.userKeyChain, JSON.stringify(chain), 384);
|
|
6261
|
+
}
|
|
6262
|
+
function writeMachinePolicy(paths, jws) {
|
|
6263
|
+
writeFileMode(paths.machinePolicy, jws, 420);
|
|
6264
|
+
}
|
|
6265
|
+
function readState(paths) {
|
|
6266
|
+
try {
|
|
6267
|
+
const parsed = JSON.parse((0, import_fs6.readFileSync)(paths.state, "utf-8"));
|
|
6268
|
+
const c = parsed.counts;
|
|
6269
|
+
return {
|
|
6270
|
+
...parsed,
|
|
6271
|
+
counts: { allow: c?.allow ?? 0, deny: c?.deny ?? 0, ask: c?.ask ?? 0 }
|
|
6272
|
+
};
|
|
6273
|
+
} catch {
|
|
6274
|
+
return { counts: { allow: 0, deny: 0, ask: 0 } };
|
|
6275
|
+
}
|
|
6276
|
+
}
|
|
6277
|
+
function writeState(paths, state) {
|
|
6278
|
+
try {
|
|
6279
|
+
writeFileMode(paths.state, JSON.stringify(state, null, 2), 384);
|
|
6280
|
+
} catch {
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
|
|
6284
|
+
// src/fleet/policy-verify.ts
|
|
6285
|
+
var import_crypto = require("crypto");
|
|
6286
|
+
var import_jose2 = require("jose");
|
|
6287
|
+
|
|
6288
|
+
// src/fleet/types.ts
|
|
6289
|
+
var FLEET_POLICY_TYP = "fleet-policy+jwt";
|
|
6290
|
+
var FLEET_POLICY_AUD = "astrasync:fleet-policy";
|
|
6291
|
+
var FLEET_POLICY_ISS = "astrasync.ai";
|
|
6292
|
+
var FLEET_KEYS_TYP = "fleet-keys+jwt";
|
|
6293
|
+
var FLEET_KEYS_AUD = "astrasync:fleet-keys";
|
|
6294
|
+
var DEFAULT_HEARTBEAT_INTERVAL_SEC = 900;
|
|
6295
|
+
|
|
6296
|
+
// src/fleet/policy-verify.ts
|
|
6297
|
+
var FleetPolicyError = class extends Error {
|
|
6298
|
+
constructor(message) {
|
|
6299
|
+
super(message);
|
|
6300
|
+
this.name = "FleetPolicyError";
|
|
6301
|
+
}
|
|
6302
|
+
};
|
|
6303
|
+
function sha256Hex(text) {
|
|
6304
|
+
return (0, import_crypto.createHash)("sha256").update(text, "utf8").digest("hex");
|
|
6305
|
+
}
|
|
6306
|
+
function parseSettings(raw) {
|
|
6307
|
+
if (!raw || typeof raw !== "object") throw new FleetPolicyError("settings missing");
|
|
6308
|
+
const s = raw;
|
|
6309
|
+
if (s.posture !== "active" && s.posture !== "passive") {
|
|
6310
|
+
throw new FleetPolicyError("settings.posture must be active or passive");
|
|
6311
|
+
}
|
|
6312
|
+
if (s.failOpen !== void 0 && typeof s.failOpen !== "boolean") {
|
|
6313
|
+
throw new FleetPolicyError("settings.failOpen must be a boolean");
|
|
6314
|
+
}
|
|
6315
|
+
if (s.governMcp !== void 0 && typeof s.governMcp !== "boolean") {
|
|
6316
|
+
throw new FleetPolicyError("settings.governMcp must be a boolean");
|
|
6317
|
+
}
|
|
6318
|
+
if (s.skipTools !== void 0 && !(Array.isArray(s.skipTools) && s.skipTools.every((t) => typeof t === "string"))) {
|
|
6319
|
+
throw new FleetPolicyError("settings.skipTools must be a string array");
|
|
6320
|
+
}
|
|
6321
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
|
|
6322
|
+
return {
|
|
6323
|
+
posture: s.posture,
|
|
6324
|
+
failOpen: s.failOpen === true,
|
|
6325
|
+
...typeof s.governMcp === "boolean" && { governMcp: s.governMcp },
|
|
6326
|
+
...Array.isArray(s.skipTools) && { skipTools: s.skipTools },
|
|
6327
|
+
...num(s.heartbeatIntervalSec) && { heartbeatIntervalSec: num(s.heartbeatIntervalSec) },
|
|
6328
|
+
...num(s.staleAfterSec) && { staleAfterSec: num(s.staleAfterSec) }
|
|
6329
|
+
};
|
|
6330
|
+
}
|
|
6331
|
+
async function verifyFleetPolicy(jws, jwks, options) {
|
|
6332
|
+
let payloadBytes;
|
|
6333
|
+
let header;
|
|
6334
|
+
try {
|
|
6335
|
+
const keySet = (0, import_jose2.createLocalJWKSet)(jwks);
|
|
6336
|
+
const result = await (0, import_jose2.compactVerify)(jws, keySet, { algorithms: ["ES256"] });
|
|
6337
|
+
payloadBytes = result.payload;
|
|
6338
|
+
header = result.protectedHeader;
|
|
6339
|
+
} catch (err) {
|
|
6340
|
+
throw new FleetPolicyError(
|
|
6341
|
+
`signature verification failed (${err instanceof Error ? err.message : String(err)})`
|
|
6342
|
+
);
|
|
6343
|
+
}
|
|
6344
|
+
if (header.typ !== FLEET_POLICY_TYP) throw new FleetPolicyError(`unexpected typ ${header.typ}`);
|
|
6345
|
+
let claims;
|
|
6346
|
+
try {
|
|
6347
|
+
claims = JSON.parse(new TextDecoder().decode(payloadBytes));
|
|
6348
|
+
} catch {
|
|
6349
|
+
throw new FleetPolicyError("payload is not JSON");
|
|
6350
|
+
}
|
|
6351
|
+
if (claims.aud !== FLEET_POLICY_AUD) throw new FleetPolicyError(`unexpected aud ${claims.aud}`);
|
|
6352
|
+
if (claims.iss !== FLEET_POLICY_ISS) throw new FleetPolicyError(`unexpected iss ${claims.iss}`);
|
|
6353
|
+
if (!options.expectedSubs.includes(claims.sub)) {
|
|
6354
|
+
throw new FleetPolicyError("policy was issued for a different organisation or device");
|
|
6355
|
+
}
|
|
6356
|
+
if (!Number.isInteger(claims.ver) || claims.ver < 1) {
|
|
6357
|
+
throw new FleetPolicyError("ver must be a positive integer");
|
|
6358
|
+
}
|
|
6359
|
+
if (options.minVer !== void 0 && claims.ver < options.minVer) {
|
|
6360
|
+
throw new FleetPolicyError(
|
|
6361
|
+
`policy version ${claims.ver} is older than the accepted version ${options.minVer} (rollback)`
|
|
6362
|
+
);
|
|
6363
|
+
}
|
|
6364
|
+
if (typeof claims.yaml !== "string" || sha256Hex(claims.yaml) !== claims.sha256) {
|
|
6365
|
+
throw new FleetPolicyError("policy hash mismatch");
|
|
6366
|
+
}
|
|
6367
|
+
const settings = parseSettings(claims.settings);
|
|
6368
|
+
const parsed = parseYaml(claims.yaml);
|
|
6369
|
+
if (!parsed.policy) {
|
|
6370
|
+
const first = parsed.validation.errors[0];
|
|
6371
|
+
throw new FleetPolicyError(
|
|
6372
|
+
`signed policy is invalid${first ? ` (${first.path}: ${first.message})` : ""}`
|
|
6373
|
+
);
|
|
6374
|
+
}
|
|
6375
|
+
return { kid: header.kid, claims: { ...claims, settings }, policy: parsed.policy, settings, jws };
|
|
6376
|
+
}
|
|
6377
|
+
async function resolveFleetJwks(pinned, chain, ownerId) {
|
|
6378
|
+
let trusted = pinned;
|
|
6379
|
+
let lastSeq = 0;
|
|
6380
|
+
for (const link of chain) {
|
|
6381
|
+
try {
|
|
6382
|
+
const { payload, protectedHeader } = await (0, import_jose2.compactVerify)(
|
|
6383
|
+
link,
|
|
6384
|
+
(0, import_jose2.createLocalJWKSet)(trusted),
|
|
6385
|
+
{ algorithms: ["ES256"] }
|
|
6386
|
+
);
|
|
6387
|
+
if (protectedHeader.typ !== FLEET_KEYS_TYP) continue;
|
|
6388
|
+
const c = JSON.parse(new TextDecoder().decode(payload));
|
|
6389
|
+
if (c.aud !== FLEET_KEYS_AUD || c.iss !== FLEET_POLICY_ISS || c.sub !== ownerId) continue;
|
|
6390
|
+
if (!Number.isInteger(c.seq) || c.seq <= lastSeq) continue;
|
|
6391
|
+
const keys = Array.isArray(c.keys) ? c.keys.filter((k) => k && k.kty === "EC" && k.crv === "P-256" && !("d" in k)) : [];
|
|
6392
|
+
if (keys.length === 0) continue;
|
|
6393
|
+
trusted = { keys };
|
|
6394
|
+
lastSeq = c.seq;
|
|
6395
|
+
} catch {
|
|
6396
|
+
}
|
|
6397
|
+
}
|
|
6398
|
+
return trusted;
|
|
6399
|
+
}
|
|
6400
|
+
async function pickNewestValid(candidates, jwks, options) {
|
|
6401
|
+
let best = null;
|
|
6402
|
+
let lastError = new FleetPolicyError("no cached fleet policy");
|
|
6403
|
+
for (const jws of candidates) {
|
|
6404
|
+
try {
|
|
6405
|
+
const v = await verifyFleetPolicy(jws, jwks, options);
|
|
6406
|
+
if (!best || v.claims.ver > best.claims.ver) best = v;
|
|
6407
|
+
} catch (err) {
|
|
6408
|
+
lastError = err instanceof Error ? err : new FleetPolicyError(String(err));
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
if (!best) throw lastError;
|
|
6412
|
+
return best;
|
|
6413
|
+
}
|
|
6414
|
+
|
|
6415
|
+
// src/fleet/cli.ts
|
|
6416
|
+
var DEFAULT_FLEET_API_BASE_URL = "https://astrasync.ai/api";
|
|
6417
|
+
function machineFingerprint() {
|
|
6418
|
+
const candidates = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
|
|
6419
|
+
let raw = "";
|
|
6420
|
+
for (const c of candidates) {
|
|
6421
|
+
try {
|
|
6422
|
+
if ((0, import_fs7.existsSync)(c)) {
|
|
6423
|
+
raw = (0, import_fs7.readFileSync)(c, "utf-8").trim();
|
|
6424
|
+
if (raw) break;
|
|
6425
|
+
}
|
|
6426
|
+
} catch {
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
if (!raw) raw = `${(0, import_os2.hostname)()}|${(0, import_os2.platform)()}|${(0, import_os2.arch)()}`;
|
|
6430
|
+
return (0, import_crypto2.createHash)("sha256").update(`astrasync-fleet:${raw}`).digest("hex");
|
|
6431
|
+
}
|
|
6432
|
+
async function runEnroll(options) {
|
|
6433
|
+
const print = options.print ?? ((l) => console.log(l));
|
|
6434
|
+
const token = options.token.trim();
|
|
6435
|
+
if (!token)
|
|
6436
|
+
throw new Error("Usage: astrasync guard enroll <token> [--api-base-url <url>] [--system]");
|
|
6437
|
+
const apiBaseUrl = (options.apiBaseUrl ?? DEFAULT_FLEET_API_BASE_URL).replace(/\/+$/, "");
|
|
6438
|
+
const paths = options.paths ?? fleetPaths();
|
|
6439
|
+
const response = await enrolDevice(apiBaseUrl, {
|
|
6440
|
+
token,
|
|
6441
|
+
fingerprint: options.fingerprint ?? machineFingerprint(),
|
|
6442
|
+
hostname: (0, import_os2.hostname)(),
|
|
6443
|
+
os: { platform: (0, import_os2.platform)(), release: (0, import_os2.release)(), arch: (0, import_os2.arch)() },
|
|
6444
|
+
guardVersion: SDK_VERSION,
|
|
6445
|
+
adapters: options.adapters ?? [
|
|
6446
|
+
{ kind: "claude-code", managed: false },
|
|
6447
|
+
{ kind: "codex", managed: false }
|
|
6448
|
+
]
|
|
6449
|
+
});
|
|
6450
|
+
const enrolment = {
|
|
6451
|
+
version: 1,
|
|
6452
|
+
deviceId: response.deviceId,
|
|
6453
|
+
credential: response.credential,
|
|
6454
|
+
org: response.org,
|
|
6455
|
+
apiBaseUrl: response.apiBaseUrl || apiBaseUrl,
|
|
6456
|
+
jwks: response.jwks,
|
|
6457
|
+
heartbeatIntervalSec: response.heartbeatIntervalSec || DEFAULT_HEARTBEAT_INTERVAL_SEC,
|
|
6458
|
+
enrolledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6459
|
+
};
|
|
6460
|
+
const verified = await verifyFleetPolicy(response.policyJws, enrolment.jwks, {
|
|
6461
|
+
expectedSubs: [enrolment.org.ownerId, enrolment.deviceId]
|
|
6462
|
+
});
|
|
6463
|
+
const scope = options.system ? "machine" : "user";
|
|
6464
|
+
const savedTo = saveEnrolment(paths, enrolment, scope);
|
|
6465
|
+
if (options.system) writeMachinePolicy(paths, response.policyJws);
|
|
6466
|
+
writeCachedPolicy(paths, response.policyJws);
|
|
6467
|
+
writeKeyChain(paths, []);
|
|
6468
|
+
const state = readState(paths);
|
|
6469
|
+
writeState(paths, {
|
|
6470
|
+
...state,
|
|
6471
|
+
appliedSha256: verified.claims.sha256,
|
|
6472
|
+
maxVer: Math.max(state.maxVer ?? 0, verified.claims.ver),
|
|
6473
|
+
revoked: false
|
|
6474
|
+
});
|
|
6475
|
+
print(
|
|
6476
|
+
`Enrolled in ${enrolment.org.name ?? enrolment.org.ownerId} (device ${enrolment.deviceId}).`
|
|
6477
|
+
);
|
|
6478
|
+
print(` Enrolment: ${savedTo} (${scope} scope)`);
|
|
6479
|
+
print(` Policy: v${verified.claims.ver}, posture ${verified.settings.posture}`);
|
|
6480
|
+
print(
|
|
6481
|
+
" Local policy.yaml / guard.json files are now ignored \u2014 the organisation policy governs."
|
|
6482
|
+
);
|
|
6483
|
+
return enrolment;
|
|
6484
|
+
}
|
|
6485
|
+
async function getFleetStatus(paths = fleetPaths()) {
|
|
6486
|
+
const loaded = loadEnrolment(paths);
|
|
6487
|
+
if (!loaded) return { enrolled: false };
|
|
6488
|
+
if ("corrupt" in loaded) return { enrolled: true, corrupt: loaded.path };
|
|
6489
|
+
const { enrolment, scope } = loaded;
|
|
6490
|
+
const state = readState(paths);
|
|
6491
|
+
let policy;
|
|
6492
|
+
try {
|
|
6493
|
+
const jwks = await resolveFleetJwks(enrolment.jwks, readKeyChain(paths), enrolment.org.ownerId);
|
|
6494
|
+
const v = await pickNewestValid(readCachedPolicies(paths), jwks, {
|
|
6495
|
+
expectedSubs: [enrolment.org.ownerId, enrolment.deviceId],
|
|
6496
|
+
minVer: state.maxVer
|
|
6497
|
+
});
|
|
6498
|
+
policy = {
|
|
6499
|
+
ver: v.claims.ver,
|
|
6500
|
+
sha256: v.claims.sha256,
|
|
6501
|
+
posture: v.settings.posture,
|
|
6502
|
+
failOpen: v.settings.failOpen
|
|
6503
|
+
};
|
|
6504
|
+
} catch (err) {
|
|
6505
|
+
policy = { error: err instanceof Error ? err.message : String(err) };
|
|
6506
|
+
}
|
|
6507
|
+
return {
|
|
6508
|
+
enrolled: true,
|
|
6509
|
+
scope,
|
|
6510
|
+
org: enrolment.org,
|
|
6511
|
+
deviceId: enrolment.deviceId,
|
|
6512
|
+
apiBaseUrl: enrolment.apiBaseUrl,
|
|
6513
|
+
policy,
|
|
6514
|
+
lastHeartbeatAt: state.lastHeartbeatAt,
|
|
6515
|
+
revoked: state.revoked === true,
|
|
6516
|
+
lastError: state.lastError,
|
|
6517
|
+
pendingCounts: state.counts
|
|
6518
|
+
};
|
|
6519
|
+
}
|
|
6520
|
+
function formatFleetStatus(status) {
|
|
6521
|
+
if (!status.enrolled) return ["Fleet: not enrolled (local policy mode)."];
|
|
6522
|
+
if (status.corrupt) {
|
|
6523
|
+
return [`Fleet: enrolment file ${status.corrupt} is unreadable \u2014 the guard fails closed.`];
|
|
6524
|
+
}
|
|
6525
|
+
const lines = [
|
|
6526
|
+
`Fleet: enrolled in ${status.org?.name ?? status.org?.ownerId} (${status.scope} scope)`,
|
|
6527
|
+
` Device: ${status.deviceId}`,
|
|
6528
|
+
` Platform: ${status.apiBaseUrl}`
|
|
6529
|
+
];
|
|
6530
|
+
if (status.policy && "error" in status.policy) {
|
|
6531
|
+
lines.push(` Policy: NO VALID SIGNED POLICY \u2014 ${status.policy.error} (guard denies)`);
|
|
6532
|
+
} else if (status.policy) {
|
|
6533
|
+
lines.push(
|
|
6534
|
+
` Policy: v${status.policy.ver} (${status.policy.sha256.slice(0, 12)}\u2026), posture ${status.policy.posture}${status.policy.failOpen ? ", fail-open" : ""}`
|
|
6535
|
+
);
|
|
6536
|
+
}
|
|
6537
|
+
lines.push(` Heartbeat: ${status.lastHeartbeatAt ?? "never"}`);
|
|
6538
|
+
if (status.revoked) {
|
|
6539
|
+
lines.push(" Status: REVOKED by the organisation \u2014 enforcing the last good policy");
|
|
6540
|
+
}
|
|
6541
|
+
if (status.lastError) lines.push(` Last sync error: ${status.lastError}`);
|
|
6542
|
+
return lines;
|
|
6543
|
+
}
|
|
6544
|
+
var MANAGED_SETTINGS_LOCATIONS = {
|
|
6545
|
+
// code.claude.com/docs/en/managed-settings
|
|
6546
|
+
claude: {
|
|
6547
|
+
darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
|
|
6548
|
+
linux: "/etc/claude-code/managed-settings.json",
|
|
6549
|
+
win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
|
|
6550
|
+
},
|
|
6551
|
+
// learn.chatgpt.com/docs/enterprise/managed-configuration
|
|
6552
|
+
codex: {
|
|
6553
|
+
darwin: "/etc/codex/requirements.toml",
|
|
6554
|
+
linux: "/etc/codex/requirements.toml",
|
|
6555
|
+
win32: "%ProgramData%\\OpenAI\\Codex\\requirements.toml"
|
|
6556
|
+
}
|
|
6557
|
+
};
|
|
6558
|
+
function managedHookCommand(target, os2) {
|
|
6559
|
+
const bin = target === "claude" ? "astrasync-claude-hook" : "astrasync-codex-hook";
|
|
6560
|
+
if (os2 === "win32") return `"C:\\Program Files\\nodejs\\${bin}.cmd"`;
|
|
6561
|
+
return `/usr/local/bin/${bin}`;
|
|
6562
|
+
}
|
|
6563
|
+
function buildClaudeManagedSettings(os2) {
|
|
6564
|
+
return {
|
|
6565
|
+
allowManagedHooksOnly: true,
|
|
6566
|
+
env: { ASTRASYNC_FLEET_REQUIRED: "1" },
|
|
6567
|
+
hooks: {
|
|
6568
|
+
PreToolUse: [
|
|
6569
|
+
{
|
|
6570
|
+
matcher: HOOK_MATCHER,
|
|
6571
|
+
hooks: [{ type: "command", command: managedHookCommand("claude", os2), timeout: 10 }]
|
|
6572
|
+
}
|
|
6573
|
+
]
|
|
6574
|
+
},
|
|
6575
|
+
permissions: { deny: [...GUARD_PERMISSION_DENY_RULES] }
|
|
6576
|
+
};
|
|
6577
|
+
}
|
|
6578
|
+
function tomlString(value) {
|
|
6579
|
+
return JSON.stringify(value);
|
|
6580
|
+
}
|
|
6581
|
+
function buildCodexRequirementsToml(os2) {
|
|
6582
|
+
const command = os2 === "win32" ? `set ASTRASYNC_FLEET_REQUIRED=1&& ${managedHookCommand("codex", os2)}` : `ASTRASYNC_FLEET_REQUIRED=1 ${managedHookCommand("codex", os2)}`;
|
|
6583
|
+
return [
|
|
6584
|
+
"# AstraSync Local Guard \u2014 managed hook (generated by `astrasync guard managed-settings --codex`)",
|
|
6585
|
+
`# Install at: ${MANAGED_SETTINGS_LOCATIONS.codex[os2]}`,
|
|
6586
|
+
"allow_managed_hooks_only = true",
|
|
6587
|
+
"",
|
|
6588
|
+
"[features]",
|
|
6589
|
+
"hooks = true",
|
|
6590
|
+
"",
|
|
6591
|
+
"[[hooks.PreToolUse]]",
|
|
6592
|
+
`matcher = ${tomlString(CODEX_HOOK_MATCHER)}`,
|
|
6593
|
+
"",
|
|
6594
|
+
"[[hooks.PreToolUse.hooks]]",
|
|
6595
|
+
'type = "command"',
|
|
6596
|
+
`command = ${tomlString(command)}`,
|
|
6597
|
+
"timeout = 10",
|
|
6598
|
+
'statusMessage = "AstraSync Local Guard"',
|
|
6599
|
+
""
|
|
6600
|
+
].join("\n");
|
|
6601
|
+
}
|
|
6602
|
+
function buildManagedSettingsFiles(targets) {
|
|
6603
|
+
const out = [];
|
|
6604
|
+
for (const target of targets) {
|
|
6605
|
+
for (const os2 of ["darwin", "linux", "win32"]) {
|
|
6606
|
+
const content = target === "claude" ? `${JSON.stringify(buildClaudeManagedSettings(os2), null, 2)}
|
|
6607
|
+
` : buildCodexRequirementsToml(os2);
|
|
6608
|
+
out.push({
|
|
6609
|
+
target,
|
|
6610
|
+
os: os2,
|
|
6611
|
+
installPath: MANAGED_SETTINGS_LOCATIONS[target][os2],
|
|
6612
|
+
fileName: `${target}-${os2}-${target === "claude" ? "managed-settings.json" : "requirements.toml"}`,
|
|
6613
|
+
content
|
|
6614
|
+
});
|
|
6615
|
+
}
|
|
6616
|
+
}
|
|
6617
|
+
return out;
|
|
6618
|
+
}
|
|
6619
|
+
function writeManagedSettings(files, outDir) {
|
|
6620
|
+
(0, import_fs7.mkdirSync)(outDir, { recursive: true });
|
|
6621
|
+
return files.map((f) => {
|
|
6622
|
+
const p = (0, import_path6.join)(outDir, f.fileName);
|
|
6623
|
+
(0, import_fs7.writeFileSync)(p, f.content, "utf-8");
|
|
6624
|
+
return p;
|
|
6625
|
+
});
|
|
6626
|
+
}
|
|
6627
|
+
|
|
6141
6628
|
// src/cli/guard.ts
|
|
6142
6629
|
var DEFAULT_POLICY_PATH = "./local-policy.yaml";
|
|
6143
6630
|
var DEFAULT_CONFIG_DIR = ".astrasync";
|
|
@@ -6277,6 +6764,9 @@ async function cmdStatus() {
|
|
|
6277
6764
|
}
|
|
6278
6765
|
});
|
|
6279
6766
|
console.log("\nAstraSync Local Guard Status\n");
|
|
6767
|
+
for (const line of formatFleetStatus(await getFleetStatus(fleetPaths()))) {
|
|
6768
|
+
console.log(` ${line}`);
|
|
6769
|
+
}
|
|
6280
6770
|
if (upgraded) {
|
|
6281
6771
|
const config = JSON.parse(fs.readFileSync(`${DEFAULT_CONFIG_DIR}/config`, "utf-8"));
|
|
6282
6772
|
console.log(` Mode: ${config.mode}`);
|
|
@@ -6632,6 +7122,66 @@ function printClaudeCodeStatus() {
|
|
|
6632
7122
|
}
|
|
6633
7123
|
}
|
|
6634
7124
|
}
|
|
7125
|
+
function flagValue(flag) {
|
|
7126
|
+
const i = process.argv.indexOf(flag);
|
|
7127
|
+
return i !== -1 ? process.argv[i + 1] : void 0;
|
|
7128
|
+
}
|
|
7129
|
+
function positionalAfter(command) {
|
|
7130
|
+
const argv = process.argv;
|
|
7131
|
+
const start = argv.indexOf(command) + 1;
|
|
7132
|
+
for (let i = start; i < argv.length; i++) {
|
|
7133
|
+
if (argv[i].startsWith("--")) {
|
|
7134
|
+
if (argv[i] === "--api-base-url" || argv[i] === "--out") i++;
|
|
7135
|
+
continue;
|
|
7136
|
+
}
|
|
7137
|
+
return argv[i];
|
|
7138
|
+
}
|
|
7139
|
+
return void 0;
|
|
7140
|
+
}
|
|
7141
|
+
async function cmdEnroll() {
|
|
7142
|
+
const token = positionalAfter("enroll") ?? "";
|
|
7143
|
+
try {
|
|
7144
|
+
await runEnroll({
|
|
7145
|
+
token,
|
|
7146
|
+
apiBaseUrl: flagValue("--api-base-url"),
|
|
7147
|
+
system: process.argv.includes("--system")
|
|
7148
|
+
});
|
|
7149
|
+
} catch (err) {
|
|
7150
|
+
console.error(`Enrolment failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7151
|
+
if (process.argv.includes("--system")) {
|
|
7152
|
+
console.error(
|
|
7153
|
+
" (--system writes the machine-scope enrolment and needs administrator rights)"
|
|
7154
|
+
);
|
|
7155
|
+
}
|
|
7156
|
+
process.exit(1);
|
|
7157
|
+
}
|
|
7158
|
+
}
|
|
7159
|
+
async function cmdFleetStatus() {
|
|
7160
|
+
const status = await getFleetStatus(fleetPaths());
|
|
7161
|
+
for (const line of formatFleetStatus(status)) console.log(line);
|
|
7162
|
+
}
|
|
7163
|
+
async function cmdManagedSettings() {
|
|
7164
|
+
const targets = [];
|
|
7165
|
+
if (process.argv.includes("--claude")) targets.push("claude");
|
|
7166
|
+
if (process.argv.includes("--codex")) targets.push("codex");
|
|
7167
|
+
if (targets.length === 0) targets.push("claude", "codex");
|
|
7168
|
+
const files = buildManagedSettingsFiles(targets);
|
|
7169
|
+
const outDir = flagValue("--out");
|
|
7170
|
+
if (outDir) {
|
|
7171
|
+
const written = writeManagedSettings(files, outDir);
|
|
7172
|
+
console.log("Managed-settings templates written:");
|
|
7173
|
+
files.forEach((f, i) => console.log(` ${written[i]} \u2192 deploy to ${f.installPath}`));
|
|
7174
|
+
} else {
|
|
7175
|
+
for (const f of files) {
|
|
7176
|
+
console.log(`===== ${f.target} \xB7 ${f.os} \u2192 ${f.installPath}`);
|
|
7177
|
+
console.log(f.content);
|
|
7178
|
+
}
|
|
7179
|
+
}
|
|
7180
|
+
console.log(
|
|
7181
|
+
"Hook commands assume npm\u2019s default global prefix; adjust if your MDM installs elsewhere."
|
|
7182
|
+
);
|
|
7183
|
+
console.log("Machines must also be enrolled: astrasync guard enroll <token> --system");
|
|
7184
|
+
}
|
|
6635
7185
|
async function main() {
|
|
6636
7186
|
const command = process.argv[2] === "guard" ? process.argv[3] : process.argv[2];
|
|
6637
7187
|
switch (command) {
|
|
@@ -6668,6 +7218,16 @@ async function main() {
|
|
|
6668
7218
|
case "uninstall-codex":
|
|
6669
7219
|
await cmdUninstallCodex();
|
|
6670
7220
|
break;
|
|
7221
|
+
case "enroll":
|
|
7222
|
+
case "enrol":
|
|
7223
|
+
await cmdEnroll();
|
|
7224
|
+
break;
|
|
7225
|
+
case "fleet-status":
|
|
7226
|
+
await cmdFleetStatus();
|
|
7227
|
+
break;
|
|
7228
|
+
case "managed-settings":
|
|
7229
|
+
await cmdManagedSettings();
|
|
7230
|
+
break;
|
|
6671
7231
|
default:
|
|
6672
7232
|
console.log("AstraSync Local Guard\n");
|
|
6673
7233
|
console.log("Usage: astrasync guard <command>\n");
|
|
@@ -6688,6 +7248,10 @@ async function main() {
|
|
|
6688
7248
|
console.log(" install-codex Install the Local Guard hook for OpenAI Codex CLI");
|
|
6689
7249
|
console.log(" (same flags; policy shared with Claude Code)");
|
|
6690
7250
|
console.log(" uninstall-codex Remove the Codex hook (--project for project scope)");
|
|
7251
|
+
console.log(" enroll <token> Enrol this machine in your organisation fleet");
|
|
7252
|
+
console.log(" (--api-base-url <url>, --system for machine scope)");
|
|
7253
|
+
console.log(" fleet-status Show fleet enrolment, signed policy and heartbeat");
|
|
7254
|
+
console.log(" managed-settings Print MDM templates (--claude, --codex, --out <dir>)");
|
|
6691
7255
|
console.log("");
|
|
6692
7256
|
process.exit(command ? 1 : 0);
|
|
6693
7257
|
}
|