@astrasyncai/verification-gateway 3.10.1 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-interface/interface.d.mts +1 -1
- package/dist/adapter-interface/interface.d.ts +1 -1
- 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/bin/astrasync-claude-hook.js +105 -27
- package/dist/bin/astrasync-codex-hook.js +5151 -0
- package/dist/bin/astrasync-guard.js +234 -32
- package/dist/bin/astrasync.js +242 -32
- package/dist/browser/background.js +84 -17
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +84 -17
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +1 -1
- package/dist/browser/browser-adapter.d.ts +1 -1
- package/dist/claude-code/claude-code-adapter.d.mts +1 -1
- package/dist/claude-code/claude-code-adapter.d.ts +1 -1
- package/dist/claude-code/claude-code-adapter.js +9 -1
- package/dist/claude-code/claude-code-adapter.js.map +1 -1
- package/dist/claude-code/claude-code-adapter.mjs +9 -1
- package/dist/claude-code/claude-code-adapter.mjs.map +1 -1
- package/dist/cli/index.d.mts +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/codex/index.d.mts +230 -0
- package/dist/codex/index.d.ts +230 -0
- package/dist/codex/index.js +5269 -0
- package/dist/codex/index.js.map +1 -0
- package/dist/codex/index.mjs +5241 -0
- package/dist/codex/index.mjs.map +1 -0
- package/dist/cursor/cursor-adapter.d.mts +1 -1
- package/dist/cursor/cursor-adapter.d.ts +1 -1
- package/dist/cursor/extension.d.mts +1 -1
- package/dist/cursor/extension.d.ts +1 -1
- package/dist/cursor/extension.js +84 -17
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +84 -17
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/gateway/gateway.d.mts +6 -1
- package/dist/gateway/gateway.d.ts +6 -1
- package/dist/gateway/gateway.js +84 -17
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +84 -17
- 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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +10 -1
- package/dist/local-evaluator/evaluator.d.ts +10 -1
- package/dist/local-evaluator/evaluator.js +20 -9
- package/dist/local-evaluator/evaluator.js.map +1 -1
- package/dist/local-evaluator/evaluator.mjs +20 -9
- package/dist/local-evaluator/evaluator.mjs.map +1 -1
- package/dist/{types-C23_uaY8.d.ts → types-DhFPiZ3t.d.ts} +6 -1
- package/dist/{types-BV6abBnF.d.mts → types-cQVCMeNV.d.mts} +6 -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 +8 -2
|
@@ -4288,8 +4288,9 @@ function extractBacktickSubstitutions(text, collect) {
|
|
|
4288
4288
|
return kept.join(" ");
|
|
4289
4289
|
}
|
|
4290
4290
|
function basename(token) {
|
|
4291
|
-
const slash = token.lastIndexOf("/");
|
|
4292
|
-
|
|
4291
|
+
const slash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
|
|
4292
|
+
const name = slash === -1 ? token : token.slice(slash + 1);
|
|
4293
|
+
return name.replace(/\.(exe|cmd|bat|ps1)$/i, "");
|
|
4293
4294
|
}
|
|
4294
4295
|
function matchSegment(segment, list) {
|
|
4295
4296
|
const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
|
|
@@ -4422,7 +4423,9 @@ var LocalEvaluator = class {
|
|
|
4422
4423
|
}
|
|
4423
4424
|
return {
|
|
4424
4425
|
recommendation: "ALLOW",
|
|
4425
|
-
|
|
4426
|
+
// "evaluated": dimensions absent from the policy are skipped, not
|
|
4427
|
+
// failed — evaluatedDimensions below records which ones ran.
|
|
4428
|
+
reason: "All evaluated PDLSS checks passed",
|
|
4426
4429
|
evaluatedDimensions: {
|
|
4427
4430
|
purpose: true,
|
|
4428
4431
|
scope: !!this.policy.scope,
|
|
@@ -4521,7 +4524,7 @@ var LocalEvaluator = class {
|
|
|
4521
4524
|
}
|
|
4522
4525
|
}
|
|
4523
4526
|
if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
|
|
4524
|
-
const targetLower = context.target.toLowerCase();
|
|
4527
|
+
const targetLower = context.target.toLowerCase().replace(/\\/g, "/");
|
|
4525
4528
|
for (const sensitivePath of SENSITIVE_PATHS) {
|
|
4526
4529
|
if (targetLower.includes(sensitivePath.toLowerCase())) {
|
|
4527
4530
|
score = Math.max(score, 50);
|
|
@@ -4550,12 +4553,20 @@ var LocalEvaluator = class {
|
|
|
4550
4553
|
matchesAnyPattern(value, patterns) {
|
|
4551
4554
|
return patterns.some((pattern) => this.matchGlob(value, pattern));
|
|
4552
4555
|
}
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4556
|
+
homePrefix() {
|
|
4557
|
+
if (this.cachedHomePrefix !== void 0) return this.cachedHomePrefix;
|
|
4558
|
+
const env = typeof process !== "undefined" ? process.env : void 0;
|
|
4559
|
+
const home = env?.HOME || env?.USERPROFILE;
|
|
4560
|
+
this.cachedHomePrefix = home ? home.replace(/\\/g, "/") : null;
|
|
4561
|
+
return this.cachedHomePrefix;
|
|
4562
|
+
}
|
|
4558
4563
|
matchGlob(value, pattern) {
|
|
4564
|
+
value = value.replace(/\\/g, "/");
|
|
4565
|
+
pattern = pattern.replace(/\\/g, "/");
|
|
4566
|
+
if (pattern === "~" || pattern.startsWith("~/")) {
|
|
4567
|
+
const home = this.homePrefix();
|
|
4568
|
+
if (home) pattern = home + pattern.slice(1);
|
|
4569
|
+
}
|
|
4559
4570
|
if (pattern === value) return true;
|
|
4560
4571
|
const starCount = (pattern.match(/\*/g) ?? []).length;
|
|
4561
4572
|
if (starCount > 8) {
|
|
@@ -4598,7 +4609,7 @@ function getTrustLevel(score) {
|
|
|
4598
4609
|
}
|
|
4599
4610
|
|
|
4600
4611
|
// src/version.ts
|
|
4601
|
-
var SDK_VERSION = "3.
|
|
4612
|
+
var SDK_VERSION = "3.11.1";
|
|
4602
4613
|
|
|
4603
4614
|
// src/well-known.ts
|
|
4604
4615
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -5113,10 +5124,12 @@ function toGatewayConfig(config) {
|
|
|
5113
5124
|
};
|
|
5114
5125
|
}
|
|
5115
5126
|
function toVerificationRequest(context, astraId) {
|
|
5127
|
+
const dotIndex = context.purpose.indexOf(".");
|
|
5128
|
+
const dotted = dotIndex > 0;
|
|
5116
5129
|
return {
|
|
5117
5130
|
credentials: { astraId },
|
|
5118
|
-
purpose: context.purpose,
|
|
5119
|
-
action: context.action,
|
|
5131
|
+
purpose: dotted ? context.purpose.slice(0, dotIndex) : context.purpose,
|
|
5132
|
+
action: dotted ? context.purpose : context.action,
|
|
5120
5133
|
resourceType: context.resourceType,
|
|
5121
5134
|
resource: context.target,
|
|
5122
5135
|
transactionValue: context.transactionValue,
|
|
@@ -5359,13 +5372,43 @@ var TraceLogger = class {
|
|
|
5359
5372
|
* Log an evaluation event.
|
|
5360
5373
|
*/
|
|
5361
5374
|
logEvaluation(context, decision) {
|
|
5375
|
+
if (!this.enabled) return;
|
|
5376
|
+
let event;
|
|
5377
|
+
try {
|
|
5378
|
+
event = {
|
|
5379
|
+
id: this.generateId(),
|
|
5380
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
5381
|
+
type: "evaluation",
|
|
5382
|
+
context: redactDeep(context),
|
|
5383
|
+
decision: redactDeep(decision)
|
|
5384
|
+
};
|
|
5385
|
+
} catch (err) {
|
|
5386
|
+
event = {
|
|
5387
|
+
id: this.generateId(),
|
|
5388
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
5389
|
+
type: "error",
|
|
5390
|
+
metadata: {
|
|
5391
|
+
error: `trace redaction failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
5392
|
+
recommendation: decision.recommendation,
|
|
5393
|
+
purpose: context.purpose,
|
|
5394
|
+
action: context.action
|
|
5395
|
+
}
|
|
5396
|
+
};
|
|
5397
|
+
}
|
|
5398
|
+
this.write(event);
|
|
5399
|
+
}
|
|
5400
|
+
/**
|
|
5401
|
+
* Log a tool call the guard saw but did not govern. One line per skip
|
|
5402
|
+
* keeps coverage holes (unmapped platform tools) visible in the traces
|
|
5403
|
+
* instead of silently indistinguishable from ALLOW.
|
|
5404
|
+
*/
|
|
5405
|
+
logSkipped(toolName, reason) {
|
|
5362
5406
|
if (!this.enabled) return;
|
|
5363
5407
|
this.write({
|
|
5364
5408
|
id: this.generateId(),
|
|
5365
5409
|
timestamp: /* @__PURE__ */ new Date(),
|
|
5366
|
-
type: "
|
|
5367
|
-
|
|
5368
|
-
decision: redactDeep(decision)
|
|
5410
|
+
type: "skipped",
|
|
5411
|
+
metadata: { toolName, reason }
|
|
5369
5412
|
});
|
|
5370
5413
|
}
|
|
5371
5414
|
/**
|
|
@@ -5394,17 +5437,34 @@ var TraceLogger = class {
|
|
|
5394
5437
|
});
|
|
5395
5438
|
}
|
|
5396
5439
|
write(event) {
|
|
5440
|
+
let line;
|
|
5441
|
+
try {
|
|
5442
|
+
line = JSON.stringify(event) + "\n";
|
|
5443
|
+
} catch {
|
|
5444
|
+
return;
|
|
5445
|
+
}
|
|
5397
5446
|
try {
|
|
5398
5447
|
const filePath = this.getCurrentFilePath();
|
|
5399
|
-
const line = JSON.stringify(event) + "\n";
|
|
5400
5448
|
if ((0, import_fs3.existsSync)(filePath)) {
|
|
5401
5449
|
const stats = (0, import_fs3.statSync)(filePath);
|
|
5402
5450
|
if (stats.size > MAX_FILE_SIZE) {
|
|
5403
5451
|
this.rotate();
|
|
5404
5452
|
}
|
|
5405
5453
|
}
|
|
5406
|
-
(0
|
|
5454
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
5455
|
+
try {
|
|
5456
|
+
(0, import_fs3.appendFileSync)(filePath, line);
|
|
5457
|
+
return;
|
|
5458
|
+
} catch {
|
|
5459
|
+
}
|
|
5460
|
+
}
|
|
5461
|
+
const fallbackPath = filePath.replace(/\.jsonl$/, `-r${process.pid}.jsonl`);
|
|
5462
|
+
(0, import_fs3.appendFileSync)(fallbackPath, line);
|
|
5407
5463
|
} catch {
|
|
5464
|
+
try {
|
|
5465
|
+
console.error(`[AstraSync TraceLogger] failed to persist trace line: ${line.trim()}`);
|
|
5466
|
+
} catch {
|
|
5467
|
+
}
|
|
5408
5468
|
}
|
|
5409
5469
|
}
|
|
5410
5470
|
getCurrentFilePath() {
|
|
@@ -5565,6 +5625,13 @@ var AstraSyncGateway = class {
|
|
|
5565
5625
|
getSyncStatus() {
|
|
5566
5626
|
return this.syncQueue?.getStatus() || null;
|
|
5567
5627
|
}
|
|
5628
|
+
/**
|
|
5629
|
+
* Record a tool call the calling adapter saw but did not govern
|
|
5630
|
+
* (unmapped tool, skipTools, MCP with governMcp off). Trace-only.
|
|
5631
|
+
*/
|
|
5632
|
+
logSkippedTool(toolName, reason) {
|
|
5633
|
+
this.traceLogger?.logSkipped(toolName, reason);
|
|
5634
|
+
}
|
|
5568
5635
|
};
|
|
5569
5636
|
|
|
5570
5637
|
// src/git-trigger/git-hooks.ts
|
|
@@ -5693,11 +5760,11 @@ async function evaluateChangedFiles(gateway, files, config = {}) {
|
|
|
5693
5760
|
var import_fs5 = require("fs");
|
|
5694
5761
|
var import_path4 = require("path");
|
|
5695
5762
|
var ASTRASYNC_HOOK_MARKER = "astrasync-claude-hook";
|
|
5696
|
-
var HOOK_MATCHER = "Bash|Write|Edit|MultiEdit|NotebookEdit|Read|Glob|Grep|WebFetch|WebSearch|Task|Agent|mcp__.*";
|
|
5763
|
+
var HOOK_MATCHER = "Bash|PowerShell|Write|Edit|MultiEdit|NotebookEdit|Read|Glob|Grep|WebFetch|WebSearch|Task|Agent|mcp__.*";
|
|
5697
5764
|
var HOOK_TIMEOUT_SECONDS = 10;
|
|
5698
|
-
function isGuardEntry(entry) {
|
|
5765
|
+
function isGuardEntry(entry, marker2) {
|
|
5699
5766
|
return (entry.hooks ?? []).some(
|
|
5700
|
-
(h) => typeof h.command === "string" && h.command.includes(
|
|
5767
|
+
(h) => typeof h.command === "string" && h.command.includes(marker2)
|
|
5701
5768
|
);
|
|
5702
5769
|
}
|
|
5703
5770
|
function readSettings(settingsPath) {
|
|
@@ -5711,24 +5778,26 @@ function writeSettings(settingsPath, settings) {
|
|
|
5711
5778
|
(0, import_fs5.writeFileSync)(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
5712
5779
|
`, "utf-8");
|
|
5713
5780
|
}
|
|
5714
|
-
function buildHookEntry(hookCommand) {
|
|
5781
|
+
function buildHookEntry(hookCommand, matcher = HOOK_MATCHER, extraHookFields = {}) {
|
|
5715
5782
|
return {
|
|
5716
|
-
matcher
|
|
5717
|
-
hooks: [
|
|
5783
|
+
matcher,
|
|
5784
|
+
hooks: [
|
|
5785
|
+
{ type: "command", command: hookCommand, timeout: HOOK_TIMEOUT_SECONDS, ...extraHookFields }
|
|
5786
|
+
]
|
|
5718
5787
|
};
|
|
5719
5788
|
}
|
|
5720
|
-
function installClaudeHooks(settingsPath, hookCommand) {
|
|
5721
|
-
if (!hookCommand.includes(
|
|
5789
|
+
function installClaudeHooks(settingsPath, hookCommand, marker2 = ASTRASYNC_HOOK_MARKER, matcher = HOOK_MATCHER, extraHookFields = {}) {
|
|
5790
|
+
if (!hookCommand.includes(marker2)) {
|
|
5722
5791
|
throw new Error(
|
|
5723
|
-
`Hook command must reference ${
|
|
5792
|
+
`Hook command must reference ${marker2} (got: ${hookCommand}) \u2014 the marker is how install/uninstall identify the guard entry.`
|
|
5724
5793
|
);
|
|
5725
5794
|
}
|
|
5726
5795
|
const settings = readSettings(settingsPath);
|
|
5727
5796
|
settings.hooks = settings.hooks ?? {};
|
|
5728
5797
|
const preToolUse = settings.hooks.PreToolUse = settings.hooks.PreToolUse ?? [];
|
|
5729
|
-
const existing = preToolUse.find(isGuardEntry);
|
|
5798
|
+
const existing = preToolUse.find((entry) => isGuardEntry(entry, marker2));
|
|
5730
5799
|
if (existing) {
|
|
5731
|
-
const fresh = buildHookEntry(hookCommand);
|
|
5800
|
+
const fresh = buildHookEntry(hookCommand, matcher, extraHookFields);
|
|
5732
5801
|
const changed = JSON.stringify(existing) !== JSON.stringify(fresh);
|
|
5733
5802
|
if (changed) {
|
|
5734
5803
|
preToolUse[preToolUse.indexOf(existing)] = fresh;
|
|
@@ -5736,11 +5805,11 @@ function installClaudeHooks(settingsPath, hookCommand) {
|
|
|
5736
5805
|
}
|
|
5737
5806
|
return { installed: false, alreadyPresent: true, updated: changed };
|
|
5738
5807
|
}
|
|
5739
|
-
preToolUse.push(buildHookEntry(hookCommand));
|
|
5808
|
+
preToolUse.push(buildHookEntry(hookCommand, matcher, extraHookFields));
|
|
5740
5809
|
writeSettings(settingsPath, settings);
|
|
5741
5810
|
return { installed: true, alreadyPresent: false, updated: false };
|
|
5742
5811
|
}
|
|
5743
|
-
function uninstallClaudeHooks(settingsPath) {
|
|
5812
|
+
function uninstallClaudeHooks(settingsPath, marker2 = ASTRASYNC_HOOK_MARKER) {
|
|
5744
5813
|
if (!(0, import_fs5.existsSync)(settingsPath)) return false;
|
|
5745
5814
|
let settings;
|
|
5746
5815
|
try {
|
|
@@ -5750,7 +5819,7 @@ function uninstallClaudeHooks(settingsPath) {
|
|
|
5750
5819
|
}
|
|
5751
5820
|
const preToolUse = settings.hooks?.PreToolUse;
|
|
5752
5821
|
if (!preToolUse) return false;
|
|
5753
|
-
const remaining = preToolUse.filter((entry) => !isGuardEntry(entry));
|
|
5822
|
+
const remaining = preToolUse.filter((entry) => !isGuardEntry(entry, marker2));
|
|
5754
5823
|
if (remaining.length === preToolUse.length) return false;
|
|
5755
5824
|
if (remaining.length > 0) {
|
|
5756
5825
|
settings.hooks.PreToolUse = remaining;
|
|
@@ -5764,7 +5833,9 @@ function uninstallClaudeHooks(settingsPath) {
|
|
|
5764
5833
|
var GUARD_SELF_PROTECTION_PATTERNS = [
|
|
5765
5834
|
"**/.claude/settings.json",
|
|
5766
5835
|
"**/.claude/settings.local.json",
|
|
5767
|
-
"**/.astrasync/**"
|
|
5836
|
+
"**/.astrasync/**",
|
|
5837
|
+
"**/.codex/hooks.json",
|
|
5838
|
+
"**/.codex/config.toml"
|
|
5768
5839
|
];
|
|
5769
5840
|
function ensureGuardSelfProtection(policyPath) {
|
|
5770
5841
|
const parsed = parseFile(policyPath);
|
|
@@ -5811,6 +5882,25 @@ function buildDefaultClaudeCodePolicy(name = "claude-code-local-guard") {
|
|
|
5811
5882
|
};
|
|
5812
5883
|
}
|
|
5813
5884
|
|
|
5885
|
+
// src/codex/settings-installer.ts
|
|
5886
|
+
var ASTRASYNC_CODEX_HOOK_MARKER = "astrasync-codex-hook";
|
|
5887
|
+
var CODEX_HOOK_MATCHER = "Bash|apply_patch|Edit|Write|web_search|mcp__.*";
|
|
5888
|
+
var CODEX_STATUS_MESSAGE = "AstraSync Local Guard";
|
|
5889
|
+
function installCodexHooks(hooksPath, hookCommand) {
|
|
5890
|
+
return installClaudeHooks(
|
|
5891
|
+
hooksPath,
|
|
5892
|
+
hookCommand,
|
|
5893
|
+
ASTRASYNC_CODEX_HOOK_MARKER,
|
|
5894
|
+
CODEX_HOOK_MATCHER,
|
|
5895
|
+
{
|
|
5896
|
+
statusMessage: CODEX_STATUS_MESSAGE
|
|
5897
|
+
}
|
|
5898
|
+
);
|
|
5899
|
+
}
|
|
5900
|
+
function uninstallCodexHooks(hooksPath) {
|
|
5901
|
+
return uninstallClaudeHooks(hooksPath, ASTRASYNC_CODEX_HOOK_MARKER);
|
|
5902
|
+
}
|
|
5903
|
+
|
|
5814
5904
|
// src/cli/guard.ts
|
|
5815
5905
|
var DEFAULT_POLICY_PATH = "./local-policy.yaml";
|
|
5816
5906
|
var DEFAULT_CONFIG_DIR = ".astrasync";
|
|
@@ -5976,6 +6066,7 @@ async function cmdStatus() {
|
|
|
5976
6066
|
console.log(' Policy: not found \u2014 run "astrasync guard setup"');
|
|
5977
6067
|
}
|
|
5978
6068
|
printClaudeCodeStatus();
|
|
6069
|
+
printCodexStatus();
|
|
5979
6070
|
console.log("");
|
|
5980
6071
|
}
|
|
5981
6072
|
async function cmdCheck() {
|
|
@@ -6154,6 +6245,108 @@ async function cmdUninstallClaudeCode() {
|
|
|
6154
6245
|
`[AstraSync] Policy and traces left in place at ${astrasyncDir} \u2014 delete manually if unwanted.`
|
|
6155
6246
|
);
|
|
6156
6247
|
}
|
|
6248
|
+
function resolveCodexHookCommand() {
|
|
6249
|
+
const sibling = path.join(__dirname, "astrasync-codex-hook.js");
|
|
6250
|
+
if (fs.existsSync(sibling)) {
|
|
6251
|
+
return `"${process.execPath}" "${sibling}"`;
|
|
6252
|
+
}
|
|
6253
|
+
return "astrasync-codex-hook";
|
|
6254
|
+
}
|
|
6255
|
+
function codexPaths(project) {
|
|
6256
|
+
const root = project ? process.cwd() : os.homedir();
|
|
6257
|
+
return {
|
|
6258
|
+
hooksPath: path.join(root, ".codex", "hooks.json"),
|
|
6259
|
+
astrasyncDir: path.join(root, ".astrasync")
|
|
6260
|
+
};
|
|
6261
|
+
}
|
|
6262
|
+
async function cmdInstallCodex() {
|
|
6263
|
+
const flags = parseClaudeCodeFlags();
|
|
6264
|
+
const { hooksPath, astrasyncDir } = codexPaths(flags.project);
|
|
6265
|
+
const policyPath = flags.policyOverride || path.join(astrasyncDir, "policy.yaml");
|
|
6266
|
+
const guardJsonPath = path.join(astrasyncDir, "guard.json");
|
|
6267
|
+
console.log("\nAstraSync Local Guard for Codex CLI \u2014 install\n");
|
|
6268
|
+
if (fs.existsSync(policyPath)) {
|
|
6269
|
+
console.log(` Policy: using existing ${policyPath} (shared across adapters)`);
|
|
6270
|
+
if (ensureGuardSelfProtection(policyPath)) {
|
|
6271
|
+
console.log(" added guard self-protection patterns to file.write");
|
|
6272
|
+
}
|
|
6273
|
+
} else if (flags.wizard) {
|
|
6274
|
+
const prompt = createPrompt();
|
|
6275
|
+
try {
|
|
6276
|
+
fs.mkdirSync(path.dirname(policyPath), { recursive: true });
|
|
6277
|
+
const wizard = new SetupWizard(prompt);
|
|
6278
|
+
await wizard.runAndSave(policyPath);
|
|
6279
|
+
} finally {
|
|
6280
|
+
prompt.close();
|
|
6281
|
+
}
|
|
6282
|
+
ensureGuardSelfProtection(policyPath);
|
|
6283
|
+
console.log(` Policy: created via wizard at ${policyPath}`);
|
|
6284
|
+
} else {
|
|
6285
|
+
fs.mkdirSync(path.dirname(policyPath), { recursive: true });
|
|
6286
|
+
fs.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
|
|
6287
|
+
console.log(` Policy: default coding-agent policy written to ${policyPath}`);
|
|
6288
|
+
console.log(" (re-run with --wizard for the interactive setup instead)");
|
|
6289
|
+
}
|
|
6290
|
+
if (!fs.existsSync(guardJsonPath)) {
|
|
6291
|
+
const guardSettings = {
|
|
6292
|
+
posture: flags.active ? "active" : "passive",
|
|
6293
|
+
governMcp: false
|
|
6294
|
+
};
|
|
6295
|
+
fs.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
|
|
6296
|
+
`, "utf-8");
|
|
6297
|
+
console.log(
|
|
6298
|
+
` Guard: ${guardJsonPath} (posture: ${guardSettings.posture}, governMcp: false)`
|
|
6299
|
+
);
|
|
6300
|
+
} else {
|
|
6301
|
+
console.log(` Guard: using existing ${guardJsonPath}`);
|
|
6302
|
+
}
|
|
6303
|
+
const hookCommand = resolveCodexHookCommand();
|
|
6304
|
+
const result = installCodexHooks(hooksPath, hookCommand);
|
|
6305
|
+
if (result.installed) {
|
|
6306
|
+
console.log(` Hook: installed in ${hooksPath}`);
|
|
6307
|
+
} else if (result.updated) {
|
|
6308
|
+
console.log(` Hook: updated in ${hooksPath}`);
|
|
6309
|
+
} else {
|
|
6310
|
+
console.log(` Hook: already present in ${hooksPath}`);
|
|
6311
|
+
}
|
|
6312
|
+
console.log("\n Next steps:");
|
|
6313
|
+
console.log(" 1. Start a NEW Codex session (hooks load at startup; /hooks to inspect).");
|
|
6314
|
+
console.log(` 2. Watch decisions: tail -f ${path.join(astrasyncDir, "traces")}/*.jsonl`);
|
|
6315
|
+
console.log(` 3. When the traces look right, set "posture": "active" in ${guardJsonPath}.`);
|
|
6316
|
+
console.log(" Denies block; approvals fall through to Codex's own prompt \u2014");
|
|
6317
|
+
console.log(" except in dontAsk/bypassPermissions modes, where they deny.");
|
|
6318
|
+
console.log(" 4. Note: Codex fails OPEN if the hook itself times out; the guard");
|
|
6319
|
+
console.log(" fails closed on every failure it can see.\n");
|
|
6320
|
+
}
|
|
6321
|
+
async function cmdUninstallCodex() {
|
|
6322
|
+
const flags = parseClaudeCodeFlags();
|
|
6323
|
+
const { hooksPath, astrasyncDir } = codexPaths(flags.project);
|
|
6324
|
+
const removed = uninstallCodexHooks(hooksPath);
|
|
6325
|
+
if (removed) {
|
|
6326
|
+
console.log(`[AstraSync] Removed Codex hook from ${hooksPath}`);
|
|
6327
|
+
} else {
|
|
6328
|
+
console.log(`[AstraSync] No AstraSync hook found in ${hooksPath}`);
|
|
6329
|
+
}
|
|
6330
|
+
console.log(
|
|
6331
|
+
`[AstraSync] Policy and traces left in place at ${astrasyncDir} \u2014 delete manually if unwanted.`
|
|
6332
|
+
);
|
|
6333
|
+
}
|
|
6334
|
+
function printCodexStatus() {
|
|
6335
|
+
for (const scope of ["user", "project"]) {
|
|
6336
|
+
const { hooksPath, astrasyncDir } = codexPaths(scope === "project");
|
|
6337
|
+
let hookInstalled = false;
|
|
6338
|
+
try {
|
|
6339
|
+
const content = fs.readFileSync(hooksPath, "utf-8");
|
|
6340
|
+
hookInstalled = content.includes(ASTRASYNC_CODEX_HOOK_MARKER);
|
|
6341
|
+
} catch {
|
|
6342
|
+
}
|
|
6343
|
+
if (hookInstalled) {
|
|
6344
|
+
const policyPath = path.join(astrasyncDir, "policy.yaml");
|
|
6345
|
+
const policy = fs.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
|
|
6346
|
+
console.log(` Codex CLI (${scope}): hook installed, ${policy}`);
|
|
6347
|
+
}
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6157
6350
|
function printClaudeCodeStatus() {
|
|
6158
6351
|
for (const scope of ["user", "project"]) {
|
|
6159
6352
|
const { settingsPath, astrasyncDir } = claudeCodePaths(scope === "project");
|
|
@@ -6211,6 +6404,12 @@ async function main() {
|
|
|
6211
6404
|
case "uninstall-claude-code":
|
|
6212
6405
|
await cmdUninstallClaudeCode();
|
|
6213
6406
|
break;
|
|
6407
|
+
case "install-codex":
|
|
6408
|
+
await cmdInstallCodex();
|
|
6409
|
+
break;
|
|
6410
|
+
case "uninstall-codex":
|
|
6411
|
+
await cmdUninstallCodex();
|
|
6412
|
+
break;
|
|
6214
6413
|
default:
|
|
6215
6414
|
console.log("AstraSync Local Guard\n");
|
|
6216
6415
|
console.log("Usage: astrasync guard <command>\n");
|
|
@@ -6228,6 +6427,9 @@ async function main() {
|
|
|
6228
6427
|
console.log(
|
|
6229
6428
|
" uninstall-claude-code Remove the Claude Code hook (--project for project scope)"
|
|
6230
6429
|
);
|
|
6430
|
+
console.log(" install-codex Install the Local Guard hook for OpenAI Codex CLI");
|
|
6431
|
+
console.log(" (same flags; policy shared with Claude Code)");
|
|
6432
|
+
console.log(" uninstall-codex Remove the Codex hook (--project for project scope)");
|
|
6231
6433
|
console.log("");
|
|
6232
6434
|
process.exit(command ? 1 : 0);
|
|
6233
6435
|
}
|