@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
package/dist/bin/astrasync.js
CHANGED
|
@@ -4628,8 +4628,9 @@ function extractBacktickSubstitutions(text, collect) {
|
|
|
4628
4628
|
return kept.join(" ");
|
|
4629
4629
|
}
|
|
4630
4630
|
function basename(token) {
|
|
4631
|
-
const slash = token.lastIndexOf("/");
|
|
4632
|
-
|
|
4631
|
+
const slash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
|
|
4632
|
+
const name = slash === -1 ? token : token.slice(slash + 1);
|
|
4633
|
+
return name.replace(/\.(exe|cmd|bat|ps1)$/i, "");
|
|
4633
4634
|
}
|
|
4634
4635
|
function matchSegment(segment, list) {
|
|
4635
4636
|
const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
|
|
@@ -4795,7 +4796,9 @@ var init_evaluator = __esm({
|
|
|
4795
4796
|
}
|
|
4796
4797
|
return {
|
|
4797
4798
|
recommendation: "ALLOW",
|
|
4798
|
-
|
|
4799
|
+
// "evaluated": dimensions absent from the policy are skipped, not
|
|
4800
|
+
// failed — evaluatedDimensions below records which ones ran.
|
|
4801
|
+
reason: "All evaluated PDLSS checks passed",
|
|
4799
4802
|
evaluatedDimensions: {
|
|
4800
4803
|
purpose: true,
|
|
4801
4804
|
scope: !!this.policy.scope,
|
|
@@ -4894,7 +4897,7 @@ var init_evaluator = __esm({
|
|
|
4894
4897
|
}
|
|
4895
4898
|
}
|
|
4896
4899
|
if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
|
|
4897
|
-
const targetLower = context.target.toLowerCase();
|
|
4900
|
+
const targetLower = context.target.toLowerCase().replace(/\\/g, "/");
|
|
4898
4901
|
for (const sensitivePath of SENSITIVE_PATHS) {
|
|
4899
4902
|
if (targetLower.includes(sensitivePath.toLowerCase())) {
|
|
4900
4903
|
score = Math.max(score, 50);
|
|
@@ -4923,12 +4926,20 @@ var init_evaluator = __esm({
|
|
|
4923
4926
|
matchesAnyPattern(value, patterns) {
|
|
4924
4927
|
return patterns.some((pattern) => this.matchGlob(value, pattern));
|
|
4925
4928
|
}
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4929
|
+
homePrefix() {
|
|
4930
|
+
if (this.cachedHomePrefix !== void 0) return this.cachedHomePrefix;
|
|
4931
|
+
const env = typeof process !== "undefined" ? process.env : void 0;
|
|
4932
|
+
const home = env?.HOME || env?.USERPROFILE;
|
|
4933
|
+
this.cachedHomePrefix = home ? home.replace(/\\/g, "/") : null;
|
|
4934
|
+
return this.cachedHomePrefix;
|
|
4935
|
+
}
|
|
4931
4936
|
matchGlob(value, pattern) {
|
|
4937
|
+
value = value.replace(/\\/g, "/");
|
|
4938
|
+
pattern = pattern.replace(/\\/g, "/");
|
|
4939
|
+
if (pattern === "~" || pattern.startsWith("~/")) {
|
|
4940
|
+
const home = this.homePrefix();
|
|
4941
|
+
if (home) pattern = home + pattern.slice(1);
|
|
4942
|
+
}
|
|
4932
4943
|
if (pattern === value) return true;
|
|
4933
4944
|
const starCount = (pattern.match(/\*/g) ?? []).length;
|
|
4934
4945
|
if (starCount > 8) {
|
|
@@ -4989,7 +5000,7 @@ var SDK_VERSION;
|
|
|
4989
5000
|
var init_version = __esm({
|
|
4990
5001
|
"src/version.ts"() {
|
|
4991
5002
|
"use strict";
|
|
4992
|
-
SDK_VERSION = "3.
|
|
5003
|
+
SDK_VERSION = "3.11.1";
|
|
4993
5004
|
}
|
|
4994
5005
|
});
|
|
4995
5006
|
|
|
@@ -5959,10 +5970,12 @@ function toGatewayConfig(config) {
|
|
|
5959
5970
|
};
|
|
5960
5971
|
}
|
|
5961
5972
|
function toVerificationRequest(context, astraId) {
|
|
5973
|
+
const dotIndex = context.purpose.indexOf(".");
|
|
5974
|
+
const dotted = dotIndex > 0;
|
|
5962
5975
|
return {
|
|
5963
5976
|
credentials: { astraId },
|
|
5964
|
-
purpose: context.purpose,
|
|
5965
|
-
action: context.action,
|
|
5977
|
+
purpose: dotted ? context.purpose.slice(0, dotIndex) : context.purpose,
|
|
5978
|
+
action: dotted ? context.purpose : context.action,
|
|
5966
5979
|
resourceType: context.resourceType,
|
|
5967
5980
|
resource: context.target,
|
|
5968
5981
|
transactionValue: context.transactionValue,
|
|
@@ -6228,13 +6241,43 @@ var init_trace_logger = __esm({
|
|
|
6228
6241
|
* Log an evaluation event.
|
|
6229
6242
|
*/
|
|
6230
6243
|
logEvaluation(context, decision) {
|
|
6244
|
+
if (!this.enabled) return;
|
|
6245
|
+
let event;
|
|
6246
|
+
try {
|
|
6247
|
+
event = {
|
|
6248
|
+
id: this.generateId(),
|
|
6249
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
6250
|
+
type: "evaluation",
|
|
6251
|
+
context: redactDeep(context),
|
|
6252
|
+
decision: redactDeep(decision)
|
|
6253
|
+
};
|
|
6254
|
+
} catch (err) {
|
|
6255
|
+
event = {
|
|
6256
|
+
id: this.generateId(),
|
|
6257
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
6258
|
+
type: "error",
|
|
6259
|
+
metadata: {
|
|
6260
|
+
error: `trace redaction failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
6261
|
+
recommendation: decision.recommendation,
|
|
6262
|
+
purpose: context.purpose,
|
|
6263
|
+
action: context.action
|
|
6264
|
+
}
|
|
6265
|
+
};
|
|
6266
|
+
}
|
|
6267
|
+
this.write(event);
|
|
6268
|
+
}
|
|
6269
|
+
/**
|
|
6270
|
+
* Log a tool call the guard saw but did not govern. One line per skip
|
|
6271
|
+
* keeps coverage holes (unmapped platform tools) visible in the traces
|
|
6272
|
+
* instead of silently indistinguishable from ALLOW.
|
|
6273
|
+
*/
|
|
6274
|
+
logSkipped(toolName, reason) {
|
|
6231
6275
|
if (!this.enabled) return;
|
|
6232
6276
|
this.write({
|
|
6233
6277
|
id: this.generateId(),
|
|
6234
6278
|
timestamp: /* @__PURE__ */ new Date(),
|
|
6235
|
-
type: "
|
|
6236
|
-
|
|
6237
|
-
decision: redactDeep(decision)
|
|
6279
|
+
type: "skipped",
|
|
6280
|
+
metadata: { toolName, reason }
|
|
6238
6281
|
});
|
|
6239
6282
|
}
|
|
6240
6283
|
/**
|
|
@@ -6263,17 +6306,34 @@ var init_trace_logger = __esm({
|
|
|
6263
6306
|
});
|
|
6264
6307
|
}
|
|
6265
6308
|
write(event) {
|
|
6309
|
+
let line;
|
|
6310
|
+
try {
|
|
6311
|
+
line = JSON.stringify(event) + "\n";
|
|
6312
|
+
} catch {
|
|
6313
|
+
return;
|
|
6314
|
+
}
|
|
6266
6315
|
try {
|
|
6267
6316
|
const filePath = this.getCurrentFilePath();
|
|
6268
|
-
const line = JSON.stringify(event) + "\n";
|
|
6269
6317
|
if ((0, import_fs3.existsSync)(filePath)) {
|
|
6270
6318
|
const stats = (0, import_fs3.statSync)(filePath);
|
|
6271
6319
|
if (stats.size > MAX_FILE_SIZE) {
|
|
6272
6320
|
this.rotate();
|
|
6273
6321
|
}
|
|
6274
6322
|
}
|
|
6275
|
-
(0
|
|
6323
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
6324
|
+
try {
|
|
6325
|
+
(0, import_fs3.appendFileSync)(filePath, line);
|
|
6326
|
+
return;
|
|
6327
|
+
} catch {
|
|
6328
|
+
}
|
|
6329
|
+
}
|
|
6330
|
+
const fallbackPath = filePath.replace(/\.jsonl$/, `-r${process.pid}.jsonl`);
|
|
6331
|
+
(0, import_fs3.appendFileSync)(fallbackPath, line);
|
|
6276
6332
|
} catch {
|
|
6333
|
+
try {
|
|
6334
|
+
console.error(`[AstraSync TraceLogger] failed to persist trace line: ${line.trim()}`);
|
|
6335
|
+
} catch {
|
|
6336
|
+
}
|
|
6277
6337
|
}
|
|
6278
6338
|
}
|
|
6279
6339
|
getCurrentFilePath() {
|
|
@@ -6445,6 +6505,13 @@ var init_gateway = __esm({
|
|
|
6445
6505
|
getSyncStatus() {
|
|
6446
6506
|
return this.syncQueue?.getStatus() || null;
|
|
6447
6507
|
}
|
|
6508
|
+
/**
|
|
6509
|
+
* Record a tool call the calling adapter saw but did not govern
|
|
6510
|
+
* (unmapped tool, skipTools, MCP with governMcp off). Trace-only.
|
|
6511
|
+
*/
|
|
6512
|
+
logSkippedTool(toolName, reason) {
|
|
6513
|
+
this.traceLogger?.logSkipped(toolName, reason);
|
|
6514
|
+
}
|
|
6448
6515
|
};
|
|
6449
6516
|
}
|
|
6450
6517
|
});
|
|
@@ -6579,9 +6646,9 @@ var init_git_hooks = __esm({
|
|
|
6579
6646
|
});
|
|
6580
6647
|
|
|
6581
6648
|
// src/claude-code/settings-installer.ts
|
|
6582
|
-
function isGuardEntry(entry) {
|
|
6649
|
+
function isGuardEntry(entry, marker2) {
|
|
6583
6650
|
return (entry.hooks ?? []).some(
|
|
6584
|
-
(h) => typeof h.command === "string" && h.command.includes(
|
|
6651
|
+
(h) => typeof h.command === "string" && h.command.includes(marker2)
|
|
6585
6652
|
);
|
|
6586
6653
|
}
|
|
6587
6654
|
function readSettings(settingsPath) {
|
|
@@ -6595,24 +6662,26 @@ function writeSettings(settingsPath, settings) {
|
|
|
6595
6662
|
(0, import_fs5.writeFileSync)(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
6596
6663
|
`, "utf-8");
|
|
6597
6664
|
}
|
|
6598
|
-
function buildHookEntry(hookCommand) {
|
|
6665
|
+
function buildHookEntry(hookCommand, matcher = HOOK_MATCHER, extraHookFields = {}) {
|
|
6599
6666
|
return {
|
|
6600
|
-
matcher
|
|
6601
|
-
hooks: [
|
|
6667
|
+
matcher,
|
|
6668
|
+
hooks: [
|
|
6669
|
+
{ type: "command", command: hookCommand, timeout: HOOK_TIMEOUT_SECONDS, ...extraHookFields }
|
|
6670
|
+
]
|
|
6602
6671
|
};
|
|
6603
6672
|
}
|
|
6604
|
-
function installClaudeHooks(settingsPath, hookCommand) {
|
|
6605
|
-
if (!hookCommand.includes(
|
|
6673
|
+
function installClaudeHooks(settingsPath, hookCommand, marker2 = ASTRASYNC_HOOK_MARKER, matcher = HOOK_MATCHER, extraHookFields = {}) {
|
|
6674
|
+
if (!hookCommand.includes(marker2)) {
|
|
6606
6675
|
throw new Error(
|
|
6607
|
-
`Hook command must reference ${
|
|
6676
|
+
`Hook command must reference ${marker2} (got: ${hookCommand}) \u2014 the marker is how install/uninstall identify the guard entry.`
|
|
6608
6677
|
);
|
|
6609
6678
|
}
|
|
6610
6679
|
const settings = readSettings(settingsPath);
|
|
6611
6680
|
settings.hooks = settings.hooks ?? {};
|
|
6612
6681
|
const preToolUse = settings.hooks.PreToolUse = settings.hooks.PreToolUse ?? [];
|
|
6613
|
-
const existing = preToolUse.find(isGuardEntry);
|
|
6682
|
+
const existing = preToolUse.find((entry) => isGuardEntry(entry, marker2));
|
|
6614
6683
|
if (existing) {
|
|
6615
|
-
const fresh = buildHookEntry(hookCommand);
|
|
6684
|
+
const fresh = buildHookEntry(hookCommand, matcher, extraHookFields);
|
|
6616
6685
|
const changed = JSON.stringify(existing) !== JSON.stringify(fresh);
|
|
6617
6686
|
if (changed) {
|
|
6618
6687
|
preToolUse[preToolUse.indexOf(existing)] = fresh;
|
|
@@ -6620,11 +6689,11 @@ function installClaudeHooks(settingsPath, hookCommand) {
|
|
|
6620
6689
|
}
|
|
6621
6690
|
return { installed: false, alreadyPresent: true, updated: changed };
|
|
6622
6691
|
}
|
|
6623
|
-
preToolUse.push(buildHookEntry(hookCommand));
|
|
6692
|
+
preToolUse.push(buildHookEntry(hookCommand, matcher, extraHookFields));
|
|
6624
6693
|
writeSettings(settingsPath, settings);
|
|
6625
6694
|
return { installed: true, alreadyPresent: false, updated: false };
|
|
6626
6695
|
}
|
|
6627
|
-
function uninstallClaudeHooks(settingsPath) {
|
|
6696
|
+
function uninstallClaudeHooks(settingsPath, marker2 = ASTRASYNC_HOOK_MARKER) {
|
|
6628
6697
|
if (!(0, import_fs5.existsSync)(settingsPath)) return false;
|
|
6629
6698
|
let settings;
|
|
6630
6699
|
try {
|
|
@@ -6634,7 +6703,7 @@ function uninstallClaudeHooks(settingsPath) {
|
|
|
6634
6703
|
}
|
|
6635
6704
|
const preToolUse = settings.hooks?.PreToolUse;
|
|
6636
6705
|
if (!preToolUse) return false;
|
|
6637
|
-
const remaining = preToolUse.filter((entry) => !isGuardEntry(entry));
|
|
6706
|
+
const remaining = preToolUse.filter((entry) => !isGuardEntry(entry, marker2));
|
|
6638
6707
|
if (remaining.length === preToolUse.length) return false;
|
|
6639
6708
|
if (remaining.length > 0) {
|
|
6640
6709
|
settings.hooks.PreToolUse = remaining;
|
|
@@ -6698,16 +6767,44 @@ var init_settings_installer = __esm({
|
|
|
6698
6767
|
init_wizard();
|
|
6699
6768
|
init_pdlss_parser();
|
|
6700
6769
|
ASTRASYNC_HOOK_MARKER = "astrasync-claude-hook";
|
|
6701
|
-
HOOK_MATCHER = "Bash|Write|Edit|MultiEdit|NotebookEdit|Read|Glob|Grep|WebFetch|WebSearch|Task|Agent|mcp__.*";
|
|
6770
|
+
HOOK_MATCHER = "Bash|PowerShell|Write|Edit|MultiEdit|NotebookEdit|Read|Glob|Grep|WebFetch|WebSearch|Task|Agent|mcp__.*";
|
|
6702
6771
|
HOOK_TIMEOUT_SECONDS = 10;
|
|
6703
6772
|
GUARD_SELF_PROTECTION_PATTERNS = [
|
|
6704
6773
|
"**/.claude/settings.json",
|
|
6705
6774
|
"**/.claude/settings.local.json",
|
|
6706
|
-
"**/.astrasync/**"
|
|
6775
|
+
"**/.astrasync/**",
|
|
6776
|
+
"**/.codex/hooks.json",
|
|
6777
|
+
"**/.codex/config.toml"
|
|
6707
6778
|
];
|
|
6708
6779
|
}
|
|
6709
6780
|
});
|
|
6710
6781
|
|
|
6782
|
+
// src/codex/settings-installer.ts
|
|
6783
|
+
function installCodexHooks(hooksPath, hookCommand) {
|
|
6784
|
+
return installClaudeHooks(
|
|
6785
|
+
hooksPath,
|
|
6786
|
+
hookCommand,
|
|
6787
|
+
ASTRASYNC_CODEX_HOOK_MARKER,
|
|
6788
|
+
CODEX_HOOK_MATCHER,
|
|
6789
|
+
{
|
|
6790
|
+
statusMessage: CODEX_STATUS_MESSAGE
|
|
6791
|
+
}
|
|
6792
|
+
);
|
|
6793
|
+
}
|
|
6794
|
+
function uninstallCodexHooks(hooksPath) {
|
|
6795
|
+
return uninstallClaudeHooks(hooksPath, ASTRASYNC_CODEX_HOOK_MARKER);
|
|
6796
|
+
}
|
|
6797
|
+
var ASTRASYNC_CODEX_HOOK_MARKER, CODEX_HOOK_MATCHER, CODEX_STATUS_MESSAGE;
|
|
6798
|
+
var init_settings_installer2 = __esm({
|
|
6799
|
+
"src/codex/settings-installer.ts"() {
|
|
6800
|
+
"use strict";
|
|
6801
|
+
init_settings_installer();
|
|
6802
|
+
ASTRASYNC_CODEX_HOOK_MARKER = "astrasync-codex-hook";
|
|
6803
|
+
CODEX_HOOK_MATCHER = "Bash|apply_patch|Edit|Write|web_search|mcp__.*";
|
|
6804
|
+
CODEX_STATUS_MESSAGE = "AstraSync Local Guard";
|
|
6805
|
+
}
|
|
6806
|
+
});
|
|
6807
|
+
|
|
6711
6808
|
// src/cli/guard.ts
|
|
6712
6809
|
var guard_exports = {};
|
|
6713
6810
|
function createPrompt() {
|
|
@@ -6872,6 +6969,7 @@ async function cmdStatus() {
|
|
|
6872
6969
|
console.log(' Policy: not found \u2014 run "astrasync guard setup"');
|
|
6873
6970
|
}
|
|
6874
6971
|
printClaudeCodeStatus();
|
|
6972
|
+
printCodexStatus();
|
|
6875
6973
|
console.log("");
|
|
6876
6974
|
}
|
|
6877
6975
|
async function cmdCheck() {
|
|
@@ -7050,6 +7148,108 @@ async function cmdUninstallClaudeCode() {
|
|
|
7050
7148
|
`[AstraSync] Policy and traces left in place at ${astrasyncDir} \u2014 delete manually if unwanted.`
|
|
7051
7149
|
);
|
|
7052
7150
|
}
|
|
7151
|
+
function resolveCodexHookCommand() {
|
|
7152
|
+
const sibling = path.join(__dirname, "astrasync-codex-hook.js");
|
|
7153
|
+
if (fs.existsSync(sibling)) {
|
|
7154
|
+
return `"${process.execPath}" "${sibling}"`;
|
|
7155
|
+
}
|
|
7156
|
+
return "astrasync-codex-hook";
|
|
7157
|
+
}
|
|
7158
|
+
function codexPaths(project) {
|
|
7159
|
+
const root = project ? process.cwd() : os.homedir();
|
|
7160
|
+
return {
|
|
7161
|
+
hooksPath: path.join(root, ".codex", "hooks.json"),
|
|
7162
|
+
astrasyncDir: path.join(root, ".astrasync")
|
|
7163
|
+
};
|
|
7164
|
+
}
|
|
7165
|
+
async function cmdInstallCodex() {
|
|
7166
|
+
const flags = parseClaudeCodeFlags();
|
|
7167
|
+
const { hooksPath, astrasyncDir } = codexPaths(flags.project);
|
|
7168
|
+
const policyPath = flags.policyOverride || path.join(astrasyncDir, "policy.yaml");
|
|
7169
|
+
const guardJsonPath = path.join(astrasyncDir, "guard.json");
|
|
7170
|
+
console.log("\nAstraSync Local Guard for Codex CLI \u2014 install\n");
|
|
7171
|
+
if (fs.existsSync(policyPath)) {
|
|
7172
|
+
console.log(` Policy: using existing ${policyPath} (shared across adapters)`);
|
|
7173
|
+
if (ensureGuardSelfProtection(policyPath)) {
|
|
7174
|
+
console.log(" added guard self-protection patterns to file.write");
|
|
7175
|
+
}
|
|
7176
|
+
} else if (flags.wizard) {
|
|
7177
|
+
const prompt = createPrompt();
|
|
7178
|
+
try {
|
|
7179
|
+
fs.mkdirSync(path.dirname(policyPath), { recursive: true });
|
|
7180
|
+
const wizard = new SetupWizard(prompt);
|
|
7181
|
+
await wizard.runAndSave(policyPath);
|
|
7182
|
+
} finally {
|
|
7183
|
+
prompt.close();
|
|
7184
|
+
}
|
|
7185
|
+
ensureGuardSelfProtection(policyPath);
|
|
7186
|
+
console.log(` Policy: created via wizard at ${policyPath}`);
|
|
7187
|
+
} else {
|
|
7188
|
+
fs.mkdirSync(path.dirname(policyPath), { recursive: true });
|
|
7189
|
+
fs.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
|
|
7190
|
+
console.log(` Policy: default coding-agent policy written to ${policyPath}`);
|
|
7191
|
+
console.log(" (re-run with --wizard for the interactive setup instead)");
|
|
7192
|
+
}
|
|
7193
|
+
if (!fs.existsSync(guardJsonPath)) {
|
|
7194
|
+
const guardSettings = {
|
|
7195
|
+
posture: flags.active ? "active" : "passive",
|
|
7196
|
+
governMcp: false
|
|
7197
|
+
};
|
|
7198
|
+
fs.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
|
|
7199
|
+
`, "utf-8");
|
|
7200
|
+
console.log(
|
|
7201
|
+
` Guard: ${guardJsonPath} (posture: ${guardSettings.posture}, governMcp: false)`
|
|
7202
|
+
);
|
|
7203
|
+
} else {
|
|
7204
|
+
console.log(` Guard: using existing ${guardJsonPath}`);
|
|
7205
|
+
}
|
|
7206
|
+
const hookCommand = resolveCodexHookCommand();
|
|
7207
|
+
const result = installCodexHooks(hooksPath, hookCommand);
|
|
7208
|
+
if (result.installed) {
|
|
7209
|
+
console.log(` Hook: installed in ${hooksPath}`);
|
|
7210
|
+
} else if (result.updated) {
|
|
7211
|
+
console.log(` Hook: updated in ${hooksPath}`);
|
|
7212
|
+
} else {
|
|
7213
|
+
console.log(` Hook: already present in ${hooksPath}`);
|
|
7214
|
+
}
|
|
7215
|
+
console.log("\n Next steps:");
|
|
7216
|
+
console.log(" 1. Start a NEW Codex session (hooks load at startup; /hooks to inspect).");
|
|
7217
|
+
console.log(` 2. Watch decisions: tail -f ${path.join(astrasyncDir, "traces")}/*.jsonl`);
|
|
7218
|
+
console.log(` 3. When the traces look right, set "posture": "active" in ${guardJsonPath}.`);
|
|
7219
|
+
console.log(" Denies block; approvals fall through to Codex's own prompt \u2014");
|
|
7220
|
+
console.log(" except in dontAsk/bypassPermissions modes, where they deny.");
|
|
7221
|
+
console.log(" 4. Note: Codex fails OPEN if the hook itself times out; the guard");
|
|
7222
|
+
console.log(" fails closed on every failure it can see.\n");
|
|
7223
|
+
}
|
|
7224
|
+
async function cmdUninstallCodex() {
|
|
7225
|
+
const flags = parseClaudeCodeFlags();
|
|
7226
|
+
const { hooksPath, astrasyncDir } = codexPaths(flags.project);
|
|
7227
|
+
const removed = uninstallCodexHooks(hooksPath);
|
|
7228
|
+
if (removed) {
|
|
7229
|
+
console.log(`[AstraSync] Removed Codex hook from ${hooksPath}`);
|
|
7230
|
+
} else {
|
|
7231
|
+
console.log(`[AstraSync] No AstraSync hook found in ${hooksPath}`);
|
|
7232
|
+
}
|
|
7233
|
+
console.log(
|
|
7234
|
+
`[AstraSync] Policy and traces left in place at ${astrasyncDir} \u2014 delete manually if unwanted.`
|
|
7235
|
+
);
|
|
7236
|
+
}
|
|
7237
|
+
function printCodexStatus() {
|
|
7238
|
+
for (const scope of ["user", "project"]) {
|
|
7239
|
+
const { hooksPath, astrasyncDir } = codexPaths(scope === "project");
|
|
7240
|
+
let hookInstalled = false;
|
|
7241
|
+
try {
|
|
7242
|
+
const content = fs.readFileSync(hooksPath, "utf-8");
|
|
7243
|
+
hookInstalled = content.includes(ASTRASYNC_CODEX_HOOK_MARKER);
|
|
7244
|
+
} catch {
|
|
7245
|
+
}
|
|
7246
|
+
if (hookInstalled) {
|
|
7247
|
+
const policyPath = path.join(astrasyncDir, "policy.yaml");
|
|
7248
|
+
const policy = fs.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
|
|
7249
|
+
console.log(` Codex CLI (${scope}): hook installed, ${policy}`);
|
|
7250
|
+
}
|
|
7251
|
+
}
|
|
7252
|
+
}
|
|
7053
7253
|
function printClaudeCodeStatus() {
|
|
7054
7254
|
for (const scope of ["user", "project"]) {
|
|
7055
7255
|
const { settingsPath, astrasyncDir } = claudeCodePaths(scope === "project");
|
|
@@ -7107,6 +7307,12 @@ async function main() {
|
|
|
7107
7307
|
case "uninstall-claude-code":
|
|
7108
7308
|
await cmdUninstallClaudeCode();
|
|
7109
7309
|
break;
|
|
7310
|
+
case "install-codex":
|
|
7311
|
+
await cmdInstallCodex();
|
|
7312
|
+
break;
|
|
7313
|
+
case "uninstall-codex":
|
|
7314
|
+
await cmdUninstallCodex();
|
|
7315
|
+
break;
|
|
7110
7316
|
default:
|
|
7111
7317
|
console.log("AstraSync Local Guard\n");
|
|
7112
7318
|
console.log("Usage: astrasync guard <command>\n");
|
|
@@ -7124,6 +7330,9 @@ async function main() {
|
|
|
7124
7330
|
console.log(
|
|
7125
7331
|
" uninstall-claude-code Remove the Claude Code hook (--project for project scope)"
|
|
7126
7332
|
);
|
|
7333
|
+
console.log(" install-codex Install the Local Guard hook for OpenAI Codex CLI");
|
|
7334
|
+
console.log(" (same flags; policy shared with Claude Code)");
|
|
7335
|
+
console.log(" uninstall-codex Remove the Codex hook (--project for project scope)");
|
|
7127
7336
|
console.log("");
|
|
7128
7337
|
process.exit(command ? 1 : 0);
|
|
7129
7338
|
}
|
|
@@ -7143,6 +7352,7 @@ var init_guard = __esm({
|
|
|
7143
7352
|
init_pdlss_parser();
|
|
7144
7353
|
init_git_hooks();
|
|
7145
7354
|
init_settings_installer();
|
|
7355
|
+
init_settings_installer2();
|
|
7146
7356
|
DEFAULT_POLICY_PATH = "./local-policy.yaml";
|
|
7147
7357
|
DEFAULT_CONFIG_DIR = ".astrasync";
|
|
7148
7358
|
main().catch((err) => {
|
|
@@ -387,8 +387,9 @@ function extractBacktickSubstitutions(text, collect) {
|
|
|
387
387
|
return kept.join(" ");
|
|
388
388
|
}
|
|
389
389
|
function basename(token) {
|
|
390
|
-
const slash = token.lastIndexOf("/");
|
|
391
|
-
|
|
390
|
+
const slash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
|
|
391
|
+
const name = slash === -1 ? token : token.slice(slash + 1);
|
|
392
|
+
return name.replace(/\.(exe|cmd|bat|ps1)$/i, "");
|
|
392
393
|
}
|
|
393
394
|
function matchSegment(segment, list) {
|
|
394
395
|
const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
|
|
@@ -521,7 +522,9 @@ var LocalEvaluator = class {
|
|
|
521
522
|
}
|
|
522
523
|
return {
|
|
523
524
|
recommendation: "ALLOW",
|
|
524
|
-
|
|
525
|
+
// "evaluated": dimensions absent from the policy are skipped, not
|
|
526
|
+
// failed — evaluatedDimensions below records which ones ran.
|
|
527
|
+
reason: "All evaluated PDLSS checks passed",
|
|
525
528
|
evaluatedDimensions: {
|
|
526
529
|
purpose: true,
|
|
527
530
|
scope: !!this.policy.scope,
|
|
@@ -620,7 +623,7 @@ var LocalEvaluator = class {
|
|
|
620
623
|
}
|
|
621
624
|
}
|
|
622
625
|
if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
|
|
623
|
-
const targetLower = context.target.toLowerCase();
|
|
626
|
+
const targetLower = context.target.toLowerCase().replace(/\\/g, "/");
|
|
624
627
|
for (const sensitivePath of SENSITIVE_PATHS) {
|
|
625
628
|
if (targetLower.includes(sensitivePath.toLowerCase())) {
|
|
626
629
|
score = Math.max(score, 50);
|
|
@@ -649,12 +652,20 @@ var LocalEvaluator = class {
|
|
|
649
652
|
matchesAnyPattern(value, patterns) {
|
|
650
653
|
return patterns.some((pattern) => this.matchGlob(value, pattern));
|
|
651
654
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
655
|
+
homePrefix() {
|
|
656
|
+
if (this.cachedHomePrefix !== void 0) return this.cachedHomePrefix;
|
|
657
|
+
const env = typeof process !== "undefined" ? process.env : void 0;
|
|
658
|
+
const home = env?.HOME || env?.USERPROFILE;
|
|
659
|
+
this.cachedHomePrefix = home ? home.replace(/\\/g, "/") : null;
|
|
660
|
+
return this.cachedHomePrefix;
|
|
661
|
+
}
|
|
657
662
|
matchGlob(value, pattern) {
|
|
663
|
+
value = value.replace(/\\/g, "/");
|
|
664
|
+
pattern = pattern.replace(/\\/g, "/");
|
|
665
|
+
if (pattern === "~" || pattern.startsWith("~/")) {
|
|
666
|
+
const home = this.homePrefix();
|
|
667
|
+
if (home) pattern = home + pattern.slice(1);
|
|
668
|
+
}
|
|
658
669
|
if (pattern === value) return true;
|
|
659
670
|
const starCount = (pattern.match(/\*/g) ?? []).length;
|
|
660
671
|
if (starCount > 8) {
|
|
@@ -3927,7 +3938,7 @@ function getTrustLevel(score) {
|
|
|
3927
3938
|
}
|
|
3928
3939
|
|
|
3929
3940
|
// src/version.ts
|
|
3930
|
-
var SDK_VERSION = "3.
|
|
3941
|
+
var SDK_VERSION = "3.11.1";
|
|
3931
3942
|
|
|
3932
3943
|
// src/well-known.ts
|
|
3933
3944
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -4442,10 +4453,12 @@ function toGatewayConfig(config) {
|
|
|
4442
4453
|
};
|
|
4443
4454
|
}
|
|
4444
4455
|
function toVerificationRequest(context, astraId) {
|
|
4456
|
+
const dotIndex = context.purpose.indexOf(".");
|
|
4457
|
+
const dotted = dotIndex > 0;
|
|
4445
4458
|
return {
|
|
4446
4459
|
credentials: { astraId },
|
|
4447
|
-
purpose: context.purpose,
|
|
4448
|
-
action: context.action,
|
|
4460
|
+
purpose: dotted ? context.purpose.slice(0, dotIndex) : context.purpose,
|
|
4461
|
+
action: dotted ? context.purpose : context.action,
|
|
4449
4462
|
resourceType: context.resourceType,
|
|
4450
4463
|
resource: context.target,
|
|
4451
4464
|
transactionValue: context.transactionValue,
|
|
@@ -4688,13 +4701,43 @@ var TraceLogger = class {
|
|
|
4688
4701
|
* Log an evaluation event.
|
|
4689
4702
|
*/
|
|
4690
4703
|
logEvaluation(context, decision) {
|
|
4704
|
+
if (!this.enabled) return;
|
|
4705
|
+
let event;
|
|
4706
|
+
try {
|
|
4707
|
+
event = {
|
|
4708
|
+
id: this.generateId(),
|
|
4709
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
4710
|
+
type: "evaluation",
|
|
4711
|
+
context: redactDeep(context),
|
|
4712
|
+
decision: redactDeep(decision)
|
|
4713
|
+
};
|
|
4714
|
+
} catch (err) {
|
|
4715
|
+
event = {
|
|
4716
|
+
id: this.generateId(),
|
|
4717
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
4718
|
+
type: "error",
|
|
4719
|
+
metadata: {
|
|
4720
|
+
error: `trace redaction failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4721
|
+
recommendation: decision.recommendation,
|
|
4722
|
+
purpose: context.purpose,
|
|
4723
|
+
action: context.action
|
|
4724
|
+
}
|
|
4725
|
+
};
|
|
4726
|
+
}
|
|
4727
|
+
this.write(event);
|
|
4728
|
+
}
|
|
4729
|
+
/**
|
|
4730
|
+
* Log a tool call the guard saw but did not govern. One line per skip
|
|
4731
|
+
* keeps coverage holes (unmapped platform tools) visible in the traces
|
|
4732
|
+
* instead of silently indistinguishable from ALLOW.
|
|
4733
|
+
*/
|
|
4734
|
+
logSkipped(toolName, reason) {
|
|
4691
4735
|
if (!this.enabled) return;
|
|
4692
4736
|
this.write({
|
|
4693
4737
|
id: this.generateId(),
|
|
4694
4738
|
timestamp: /* @__PURE__ */ new Date(),
|
|
4695
|
-
type: "
|
|
4696
|
-
|
|
4697
|
-
decision: redactDeep(decision)
|
|
4739
|
+
type: "skipped",
|
|
4740
|
+
metadata: { toolName, reason }
|
|
4698
4741
|
});
|
|
4699
4742
|
}
|
|
4700
4743
|
/**
|
|
@@ -4723,17 +4766,34 @@ var TraceLogger = class {
|
|
|
4723
4766
|
});
|
|
4724
4767
|
}
|
|
4725
4768
|
write(event) {
|
|
4769
|
+
let line;
|
|
4770
|
+
try {
|
|
4771
|
+
line = JSON.stringify(event) + "\n";
|
|
4772
|
+
} catch {
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4726
4775
|
try {
|
|
4727
4776
|
const filePath = this.getCurrentFilePath();
|
|
4728
|
-
const line = JSON.stringify(event) + "\n";
|
|
4729
4777
|
if ((0, import_fs3.existsSync)(filePath)) {
|
|
4730
4778
|
const stats = (0, import_fs3.statSync)(filePath);
|
|
4731
4779
|
if (stats.size > MAX_FILE_SIZE) {
|
|
4732
4780
|
this.rotate();
|
|
4733
4781
|
}
|
|
4734
4782
|
}
|
|
4735
|
-
(0
|
|
4783
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
4784
|
+
try {
|
|
4785
|
+
(0, import_fs3.appendFileSync)(filePath, line);
|
|
4786
|
+
return;
|
|
4787
|
+
} catch {
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4790
|
+
const fallbackPath = filePath.replace(/\.jsonl$/, `-r${process.pid}.jsonl`);
|
|
4791
|
+
(0, import_fs3.appendFileSync)(fallbackPath, line);
|
|
4736
4792
|
} catch {
|
|
4793
|
+
try {
|
|
4794
|
+
console.error(`[AstraSync TraceLogger] failed to persist trace line: ${line.trim()}`);
|
|
4795
|
+
} catch {
|
|
4796
|
+
}
|
|
4737
4797
|
}
|
|
4738
4798
|
}
|
|
4739
4799
|
getCurrentFilePath() {
|
|
@@ -4894,6 +4954,13 @@ var AstraSyncGateway = class {
|
|
|
4894
4954
|
getSyncStatus() {
|
|
4895
4955
|
return this.syncQueue?.getStatus() || null;
|
|
4896
4956
|
}
|
|
4957
|
+
/**
|
|
4958
|
+
* Record a tool call the calling adapter saw but did not govern
|
|
4959
|
+
* (unmapped tool, skipTools, MCP with governMcp off). Trace-only.
|
|
4960
|
+
*/
|
|
4961
|
+
logSkippedTool(toolName, reason) {
|
|
4962
|
+
this.traceLogger?.logSkipped(toolName, reason);
|
|
4963
|
+
}
|
|
4897
4964
|
};
|
|
4898
4965
|
|
|
4899
4966
|
// src/browser/background.ts
|