@astrasyncai/verification-gateway 3.10.0 → 3.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/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 +317 -35
- package/dist/bin/astrasync-codex-hook.js +5107 -0
- package/dist/bin/astrasync-guard.js +430 -40
- package/dist/bin/astrasync.js +453 -41
- package/dist/browser/background.js +279 -24
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +279 -24
- 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 +5225 -0
- package/dist/codex/index.js.map +1 -0
- package/dist/codex/index.mjs +5197 -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 +279 -24
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +279 -24
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/gateway/gateway.d.mts +32 -2
- package/dist/gateway/gateway.d.ts +32 -2
- package/dist/gateway/gateway.js +279 -24
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +279 -24
- 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 +1 -1
- package/dist/local-evaluator/evaluator.d.ts +1 -1
- package/dist/local-evaluator/evaluator.js +165 -7
- package/dist/local-evaluator/evaluator.js.map +1 -1
- package/dist/local-evaluator/evaluator.mjs +165 -7
- package/dist/local-evaluator/evaluator.mjs.map +1 -1
- package/dist/{types-D0lCtFQ7.d.mts → types-BV6abBnF.d.mts} +6 -0
- package/dist/{types-YbiXYKx1.d.ts → types-C23_uaY8.d.ts} +6 -0
- 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
|
@@ -4543,7 +4543,9 @@ var init_types = __esm({
|
|
|
4543
4543
|
"git push",
|
|
4544
4544
|
"npm publish",
|
|
4545
4545
|
"docker",
|
|
4546
|
-
"kubectl"
|
|
4546
|
+
"kubectl",
|
|
4547
|
+
"gh pr create",
|
|
4548
|
+
"gh pr merge"
|
|
4547
4549
|
];
|
|
4548
4550
|
SENSITIVE_PATHS = [
|
|
4549
4551
|
".ssh",
|
|
@@ -4564,12 +4566,178 @@ var init_types = __esm({
|
|
|
4564
4566
|
}
|
|
4565
4567
|
});
|
|
4566
4568
|
|
|
4569
|
+
// src/local-evaluator/command-matcher.ts
|
|
4570
|
+
function buildRiskList(entries) {
|
|
4571
|
+
const singleWord = /* @__PURE__ */ new Set();
|
|
4572
|
+
const multiWord = [];
|
|
4573
|
+
for (const entry of entries) {
|
|
4574
|
+
const words = entry.toLowerCase().trim().split(/\s+/);
|
|
4575
|
+
if (words.length === 1) singleWord.add(words[0]);
|
|
4576
|
+
else multiWord.push(words);
|
|
4577
|
+
}
|
|
4578
|
+
multiWord.sort((a, b) => b.length - a.length);
|
|
4579
|
+
return { singleWord, multiWord };
|
|
4580
|
+
}
|
|
4581
|
+
function stripHeredocBodies(text) {
|
|
4582
|
+
const lines = text.split("\n");
|
|
4583
|
+
const kept = [];
|
|
4584
|
+
let terminator = null;
|
|
4585
|
+
for (const line of lines) {
|
|
4586
|
+
if (terminator !== null) {
|
|
4587
|
+
if (line.trim() === terminator) terminator = null;
|
|
4588
|
+
continue;
|
|
4589
|
+
}
|
|
4590
|
+
kept.push(line);
|
|
4591
|
+
const heredoc = line.match(/<<-?\s*['"]?(\w+)['"]?/);
|
|
4592
|
+
if (heredoc) terminator = heredoc[1];
|
|
4593
|
+
}
|
|
4594
|
+
return kept.join("\n");
|
|
4595
|
+
}
|
|
4596
|
+
function extractDollarSubstitutions(text, collect) {
|
|
4597
|
+
let result = text;
|
|
4598
|
+
let start = result.indexOf("$(");
|
|
4599
|
+
let iterations = 0;
|
|
4600
|
+
while (start !== -1 && iterations < 50) {
|
|
4601
|
+
iterations++;
|
|
4602
|
+
let depth = 0;
|
|
4603
|
+
let end = result.length;
|
|
4604
|
+
for (let i = start + 1; i < result.length; i++) {
|
|
4605
|
+
if (result[i] === "(") depth++;
|
|
4606
|
+
else if (result[i] === ")") {
|
|
4607
|
+
depth--;
|
|
4608
|
+
if (depth === 0) {
|
|
4609
|
+
end = i;
|
|
4610
|
+
break;
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
collect(result.slice(start + 2, end));
|
|
4615
|
+
result = `${result.slice(0, start)} ${result.slice(Math.min(end + 1, result.length))}`;
|
|
4616
|
+
start = result.indexOf("$(");
|
|
4617
|
+
}
|
|
4618
|
+
return result;
|
|
4619
|
+
}
|
|
4620
|
+
function extractBacktickSubstitutions(text, collect) {
|
|
4621
|
+
if (!text.includes("`")) return text;
|
|
4622
|
+
const chunks = text.split("`");
|
|
4623
|
+
const kept = [];
|
|
4624
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
4625
|
+
if (i % 2 === 1) collect(chunks[i]);
|
|
4626
|
+
else kept.push(chunks[i]);
|
|
4627
|
+
}
|
|
4628
|
+
return kept.join(" ");
|
|
4629
|
+
}
|
|
4630
|
+
function basename(token) {
|
|
4631
|
+
const slash = token.lastIndexOf("/");
|
|
4632
|
+
return slash === -1 ? token : token.slice(slash + 1);
|
|
4633
|
+
}
|
|
4634
|
+
function matchSegment(segment, list) {
|
|
4635
|
+
const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
|
|
4636
|
+
let i = 0;
|
|
4637
|
+
let hops = 0;
|
|
4638
|
+
while (i < tokens.length) {
|
|
4639
|
+
while (i < tokens.length && (tokens[i] === "export" || ASSIGN_RE.test(tokens[i]))) i++;
|
|
4640
|
+
if (i >= tokens.length) return null;
|
|
4641
|
+
const cmd = basename(tokens[i]);
|
|
4642
|
+
const significant = [cmd, ...tokens.slice(i + 1).filter((t) => !t.startsWith("-"))];
|
|
4643
|
+
for (const entry of list.multiWord) {
|
|
4644
|
+
if (entry.length <= significant.length && entry.every((w, j) => significant[j] === w)) {
|
|
4645
|
+
return entry.join(" ");
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
if (list.singleWord.has(cmd)) return cmd;
|
|
4649
|
+
if (WRAPPERS.has(cmd) && hops < MAX_WRAPPER_HOPS) {
|
|
4650
|
+
hops++;
|
|
4651
|
+
i++;
|
|
4652
|
+
while (i < tokens.length && (tokens[i].startsWith("-") || ASSIGN_RE.test(tokens[i]))) i++;
|
|
4653
|
+
continue;
|
|
4654
|
+
}
|
|
4655
|
+
return null;
|
|
4656
|
+
}
|
|
4657
|
+
return null;
|
|
4658
|
+
}
|
|
4659
|
+
function collectMatches(command, list, matches, depth) {
|
|
4660
|
+
if (depth > MAX_RECURSION_DEPTH || command.length > MAX_COMMAND_LENGTH) return;
|
|
4661
|
+
let text = command.toLowerCase();
|
|
4662
|
+
text = stripHeredocBodies(text);
|
|
4663
|
+
const recurse = (inner) => collectMatches(inner, list, matches, depth + 1);
|
|
4664
|
+
text = extractDollarSubstitutions(text, recurse);
|
|
4665
|
+
text = extractBacktickSubstitutions(text, recurse);
|
|
4666
|
+
text = text.replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
|
|
4667
|
+
text = text.replace(/(^|\s)#[^\n]*/g, " ");
|
|
4668
|
+
const segments = text.split(/\|\||&&|[;|&\n]|[({]/);
|
|
4669
|
+
for (const segment of segments) {
|
|
4670
|
+
const matched = matchSegment(segment, list);
|
|
4671
|
+
if (matched) matches.add(matched);
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
function matchHighRiskCommands(command, highRiskCommands = HIGH_RISK_COMMANDS) {
|
|
4675
|
+
if (!command) return [];
|
|
4676
|
+
const matches = /* @__PURE__ */ new Set();
|
|
4677
|
+
collectMatches(command, buildRiskList(highRiskCommands), matches, 0);
|
|
4678
|
+
return [...matches];
|
|
4679
|
+
}
|
|
4680
|
+
function isLocalhostOnlyNetworkCommand(command) {
|
|
4681
|
+
const text = command.toLowerCase();
|
|
4682
|
+
const tokens = text.split(/\s+/).map((t) => t.replace(/^["'`]+|["'`,;)]+$/g, ""));
|
|
4683
|
+
let localCount = 0;
|
|
4684
|
+
for (const token of tokens) {
|
|
4685
|
+
let host = null;
|
|
4686
|
+
if (token.includes("://")) {
|
|
4687
|
+
let rest = token.slice(token.indexOf("://") + 3);
|
|
4688
|
+
const at = rest.indexOf("@");
|
|
4689
|
+
if (at !== -1) rest = rest.slice(at + 1);
|
|
4690
|
+
host = rest.split(/[/?#]/)[0];
|
|
4691
|
+
} else if (/^[a-z0-9.[\]:-]+:\d+(\/.*)?$/.test(token)) {
|
|
4692
|
+
host = token.split("/")[0];
|
|
4693
|
+
} else if (/^(localhost|127\.0\.0\.1|\[?::1\]?)(\/.*)?$/.test(token)) {
|
|
4694
|
+
host = token.split("/")[0];
|
|
4695
|
+
}
|
|
4696
|
+
if (host === null) continue;
|
|
4697
|
+
const bare = host.replace(/:\d+$/, "").replace(/^\[|\]$/g, "");
|
|
4698
|
+
if (LOCAL_HOST_RE.test(bare)) {
|
|
4699
|
+
localCount++;
|
|
4700
|
+
} else {
|
|
4701
|
+
return false;
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
return localCount > 0;
|
|
4705
|
+
}
|
|
4706
|
+
var WRAPPERS, ASSIGN_RE, MAX_WRAPPER_HOPS, MAX_RECURSION_DEPTH, MAX_COMMAND_LENGTH, LOCAL_HOST_RE;
|
|
4707
|
+
var init_command_matcher = __esm({
|
|
4708
|
+
"src/local-evaluator/command-matcher.ts"() {
|
|
4709
|
+
"use strict";
|
|
4710
|
+
init_types();
|
|
4711
|
+
WRAPPERS = /* @__PURE__ */ new Set([
|
|
4712
|
+
"sudo",
|
|
4713
|
+
"doas",
|
|
4714
|
+
"env",
|
|
4715
|
+
"nohup",
|
|
4716
|
+
"time",
|
|
4717
|
+
"timeout",
|
|
4718
|
+
"nice",
|
|
4719
|
+
"ionice",
|
|
4720
|
+
"stdbuf",
|
|
4721
|
+
"xargs",
|
|
4722
|
+
"command",
|
|
4723
|
+
"exec",
|
|
4724
|
+
"builtin"
|
|
4725
|
+
]);
|
|
4726
|
+
ASSIGN_RE = /^[a-z_][a-z0-9_]*=/;
|
|
4727
|
+
MAX_WRAPPER_HOPS = 4;
|
|
4728
|
+
MAX_RECURSION_DEPTH = 3;
|
|
4729
|
+
MAX_COMMAND_LENGTH = 1e5;
|
|
4730
|
+
LOCAL_HOST_RE = /^(localhost|127\.0\.0\.1|::1|[a-z0-9.-]*\.localhost)$/;
|
|
4731
|
+
}
|
|
4732
|
+
});
|
|
4733
|
+
|
|
4567
4734
|
// src/local-evaluator/evaluator.ts
|
|
4568
4735
|
var LocalEvaluator;
|
|
4569
4736
|
var init_evaluator = __esm({
|
|
4570
4737
|
"src/local-evaluator/evaluator.ts"() {
|
|
4571
4738
|
"use strict";
|
|
4572
4739
|
init_types();
|
|
4740
|
+
init_command_matcher();
|
|
4573
4741
|
LocalEvaluator = class {
|
|
4574
4742
|
constructor(policy) {
|
|
4575
4743
|
this.requestCounts = /* @__PURE__ */ new Map();
|
|
@@ -4719,12 +4887,10 @@ var init_evaluator = __esm({
|
|
|
4719
4887
|
return score;
|
|
4720
4888
|
}
|
|
4721
4889
|
if (context.purpose === "shell.exec" && context.target) {
|
|
4722
|
-
const
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
break;
|
|
4727
|
-
}
|
|
4890
|
+
const matched = matchHighRiskCommands(context.target);
|
|
4891
|
+
const localProbeOnly = matched.length > 0 && matched.every((cmd) => cmd === "curl" || cmd === "wget") && isLocalhostOnlyNetworkCommand(context.target);
|
|
4892
|
+
if (matched.length > 0 && !localProbeOnly) {
|
|
4893
|
+
score = Math.max(score, 80);
|
|
4728
4894
|
}
|
|
4729
4895
|
}
|
|
4730
4896
|
if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
|
|
@@ -4823,7 +4989,7 @@ var SDK_VERSION;
|
|
|
4823
4989
|
var init_version = __esm({
|
|
4824
4990
|
"src/version.ts"() {
|
|
4825
4991
|
"use strict";
|
|
4826
|
-
SDK_VERSION = "3.
|
|
4992
|
+
SDK_VERSION = "3.11.0";
|
|
4827
4993
|
}
|
|
4828
4994
|
});
|
|
4829
4995
|
|
|
@@ -5793,10 +5959,12 @@ function toGatewayConfig(config) {
|
|
|
5793
5959
|
};
|
|
5794
5960
|
}
|
|
5795
5961
|
function toVerificationRequest(context, astraId) {
|
|
5962
|
+
const dotIndex = context.purpose.indexOf(".");
|
|
5963
|
+
const dotted = dotIndex > 0;
|
|
5796
5964
|
return {
|
|
5797
5965
|
credentials: { astraId },
|
|
5798
|
-
purpose: context.purpose,
|
|
5799
|
-
action: context.action,
|
|
5966
|
+
purpose: dotted ? context.purpose.slice(0, dotIndex) : context.purpose,
|
|
5967
|
+
action: dotted ? context.purpose : context.action,
|
|
5800
5968
|
resourceType: context.resourceType,
|
|
5801
5969
|
resource: context.target,
|
|
5802
5970
|
transactionValue: context.transactionValue,
|
|
@@ -5839,21 +6007,93 @@ var init_online = __esm({
|
|
|
5839
6007
|
}
|
|
5840
6008
|
});
|
|
5841
6009
|
|
|
6010
|
+
// src/gateway/redact.ts
|
|
6011
|
+
function redactSecrets(text) {
|
|
6012
|
+
let result = text;
|
|
6013
|
+
for (const { type: type2, re, replacement } of REDACTION_PATTERNS) {
|
|
6014
|
+
result = result.replace(re, replacement ?? marker(type2));
|
|
6015
|
+
}
|
|
6016
|
+
return result;
|
|
6017
|
+
}
|
|
6018
|
+
function redactDeep(value) {
|
|
6019
|
+
if (typeof value === "string") {
|
|
6020
|
+
return redactSecrets(value);
|
|
6021
|
+
}
|
|
6022
|
+
if (Array.isArray(value)) {
|
|
6023
|
+
return value.map((item) => redactDeep(item));
|
|
6024
|
+
}
|
|
6025
|
+
if (value !== null && typeof value === "object" && !(value instanceof Date)) {
|
|
6026
|
+
const out = {};
|
|
6027
|
+
for (const [k, v] of Object.entries(value)) {
|
|
6028
|
+
out[k] = redactDeep(v);
|
|
6029
|
+
}
|
|
6030
|
+
return out;
|
|
6031
|
+
}
|
|
6032
|
+
return value;
|
|
6033
|
+
}
|
|
6034
|
+
var marker, REDACTION_PATTERNS;
|
|
6035
|
+
var init_redact = __esm({
|
|
6036
|
+
"src/gateway/redact.ts"() {
|
|
6037
|
+
"use strict";
|
|
6038
|
+
marker = (type2) => `[REDACTED:${type2}]`;
|
|
6039
|
+
REDACTION_PATTERNS = [
|
|
6040
|
+
// Multi-line blocks first so later single-line patterns don't shred them.
|
|
6041
|
+
{
|
|
6042
|
+
type: "private-key",
|
|
6043
|
+
re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g
|
|
6044
|
+
},
|
|
6045
|
+
// URL userinfo credentials (https://user:pass@, x-access-token:ghp_…@).
|
|
6046
|
+
{
|
|
6047
|
+
type: "url-credentials",
|
|
6048
|
+
re: /([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+:[^\s/@]+@/gi,
|
|
6049
|
+
replacement: `$1${marker("url-credentials")}@`
|
|
6050
|
+
},
|
|
6051
|
+
// Known token formats.
|
|
6052
|
+
{ type: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,255}\b/g },
|
|
6053
|
+
{ type: "github-token", re: /\bgithub_pat_[A-Za-z0-9_]{20,255}\b/g },
|
|
6054
|
+
{ type: "astrasync-key", re: /\bkya_[A-Fa-f0-9]{32,128}\b/g },
|
|
6055
|
+
{ type: "openai-key", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g },
|
|
6056
|
+
{ type: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b/g },
|
|
6057
|
+
{ type: "jwt", re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g },
|
|
6058
|
+
// Bearer/token headers.
|
|
6059
|
+
{
|
|
6060
|
+
type: "bearer-token",
|
|
6061
|
+
re: /\b(bearer\s+)[a-z0-9._~+/=-]{16,}/gi,
|
|
6062
|
+
replacement: `$1${marker("bearer-token")}`
|
|
6063
|
+
},
|
|
6064
|
+
// Generic KEY=value / key: value assignments with secret-ish names.
|
|
6065
|
+
{
|
|
6066
|
+
type: "assigned-secret",
|
|
6067
|
+
re: /\b((?:password|passwd|secret|token|api[_-]?key|apikey|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[=:]\s*)(['"]?)[^\s'"&;]{8,}\2/gi,
|
|
6068
|
+
replacement: `$1$2${marker("assigned-secret")}$2`
|
|
6069
|
+
}
|
|
6070
|
+
];
|
|
6071
|
+
}
|
|
6072
|
+
});
|
|
6073
|
+
|
|
5842
6074
|
// src/gateway/modes/hybrid.ts
|
|
6075
|
+
function delay(ms) {
|
|
6076
|
+
return new Promise((resolve) => {
|
|
6077
|
+
const timer = setTimeout(() => resolve(FLUSH_TIMEOUT), ms);
|
|
6078
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
6079
|
+
});
|
|
6080
|
+
}
|
|
5843
6081
|
function verifyHybrid(evaluator, syncQueue, context) {
|
|
5844
6082
|
const decision = evaluator.evaluate(context);
|
|
5845
6083
|
syncQueue.enqueue(context, decision);
|
|
5846
6084
|
return decision;
|
|
5847
6085
|
}
|
|
5848
|
-
var import_fs2, import_path, MAX_QUEUE_SIZE, MAX_RETRIES, HybridSyncQueue;
|
|
6086
|
+
var import_fs2, import_path, MAX_QUEUE_SIZE, MAX_RETRIES, FLUSH_TIMEOUT, HybridSyncQueue;
|
|
5849
6087
|
var init_hybrid = __esm({
|
|
5850
6088
|
"src/gateway/modes/hybrid.ts"() {
|
|
5851
6089
|
"use strict";
|
|
5852
6090
|
import_fs2 = require("fs");
|
|
5853
6091
|
import_path = require("path");
|
|
5854
6092
|
init_online();
|
|
6093
|
+
init_redact();
|
|
5855
6094
|
MAX_QUEUE_SIZE = 1e3;
|
|
5856
6095
|
MAX_RETRIES = 3;
|
|
6096
|
+
FLUSH_TIMEOUT = /* @__PURE__ */ Symbol("flush-timeout");
|
|
5857
6097
|
HybridSyncQueue = class {
|
|
5858
6098
|
constructor(config, persistPath) {
|
|
5859
6099
|
this.queue = [];
|
|
@@ -5874,12 +6114,12 @@ var init_hybrid = __esm({
|
|
|
5874
6114
|
/**
|
|
5875
6115
|
* Stop periodic sync and flush remaining entries.
|
|
5876
6116
|
*/
|
|
5877
|
-
async stop() {
|
|
6117
|
+
async stop(flushOptions = {}) {
|
|
5878
6118
|
if (this.timer) {
|
|
5879
6119
|
clearInterval(this.timer);
|
|
5880
6120
|
this.timer = null;
|
|
5881
6121
|
}
|
|
5882
|
-
await this.flush();
|
|
6122
|
+
await this.flush(flushOptions);
|
|
5883
6123
|
this.saveToDisk();
|
|
5884
6124
|
}
|
|
5885
6125
|
/**
|
|
@@ -5891,8 +6131,8 @@ var init_hybrid = __esm({
|
|
|
5891
6131
|
}
|
|
5892
6132
|
this.queue.push({
|
|
5893
6133
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
5894
|
-
context,
|
|
5895
|
-
decision,
|
|
6134
|
+
context: redactDeep(context),
|
|
6135
|
+
decision: redactDeep(decision),
|
|
5896
6136
|
timestamp: /* @__PURE__ */ new Date(),
|
|
5897
6137
|
retryCount: 0,
|
|
5898
6138
|
status: "pending"
|
|
@@ -5902,16 +6142,21 @@ var init_hybrid = __esm({
|
|
|
5902
6142
|
/**
|
|
5903
6143
|
* Flush pending entries to the API.
|
|
5904
6144
|
*/
|
|
5905
|
-
async flush() {
|
|
6145
|
+
async flush(options = {}) {
|
|
5906
6146
|
if (this.processing) return;
|
|
5907
6147
|
this.processing = true;
|
|
5908
6148
|
try {
|
|
5909
6149
|
const pending = this.queue.filter((e) => e.status === "pending");
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
6150
|
+
const batch = typeof options.limit === "number" ? pending.slice(0, options.limit) : pending;
|
|
6151
|
+
for (const entry of batch) {
|
|
6152
|
+
const attempt = verifyOnline(this.config, entry.context, this.config.agentId).then(() => "synced").catch(() => "error");
|
|
6153
|
+
const result = options.perEntryTimeoutMs ? await Promise.race([attempt, delay(options.perEntryTimeoutMs)]) : await attempt;
|
|
6154
|
+
if (result === FLUSH_TIMEOUT) {
|
|
6155
|
+
break;
|
|
6156
|
+
}
|
|
6157
|
+
if (result === "synced") {
|
|
5913
6158
|
entry.status = "synced";
|
|
5914
|
-
}
|
|
6159
|
+
} else {
|
|
5915
6160
|
entry.retryCount++;
|
|
5916
6161
|
if (entry.retryCount >= MAX_RETRIES) {
|
|
5917
6162
|
entry.status = "failed";
|
|
@@ -5970,6 +6215,7 @@ var init_trace_logger = __esm({
|
|
|
5970
6215
|
"use strict";
|
|
5971
6216
|
import_fs3 = require("fs");
|
|
5972
6217
|
import_path2 = require("path");
|
|
6218
|
+
init_redact();
|
|
5973
6219
|
MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
5974
6220
|
MAX_FILES = 10;
|
|
5975
6221
|
TraceLogger = class {
|
|
@@ -5989,8 +6235,22 @@ var init_trace_logger = __esm({
|
|
|
5989
6235
|
id: this.generateId(),
|
|
5990
6236
|
timestamp: /* @__PURE__ */ new Date(),
|
|
5991
6237
|
type: "evaluation",
|
|
5992
|
-
context,
|
|
5993
|
-
decision
|
|
6238
|
+
context: redactDeep(context),
|
|
6239
|
+
decision: redactDeep(decision)
|
|
6240
|
+
});
|
|
6241
|
+
}
|
|
6242
|
+
/**
|
|
6243
|
+
* Log a tool call the guard saw but did not govern. One line per skip
|
|
6244
|
+
* keeps coverage holes (unmapped platform tools) visible in the traces
|
|
6245
|
+
* instead of silently indistinguishable from ALLOW.
|
|
6246
|
+
*/
|
|
6247
|
+
logSkipped(toolName, reason) {
|
|
6248
|
+
if (!this.enabled) return;
|
|
6249
|
+
this.write({
|
|
6250
|
+
id: this.generateId(),
|
|
6251
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
6252
|
+
type: "skipped",
|
|
6253
|
+
metadata: { toolName, reason }
|
|
5994
6254
|
});
|
|
5995
6255
|
}
|
|
5996
6256
|
/**
|
|
@@ -6118,10 +6378,12 @@ var init_gateway = __esm({
|
|
|
6118
6378
|
}
|
|
6119
6379
|
/**
|
|
6120
6380
|
* Graceful shutdown. Flushes sync queue and cleans up resources.
|
|
6381
|
+
* Stateless callers (hooks) pass bounded flush options so sync never
|
|
6382
|
+
* sits on a tool call's critical path.
|
|
6121
6383
|
*/
|
|
6122
|
-
async shutdown() {
|
|
6384
|
+
async shutdown(options) {
|
|
6123
6385
|
if (this.syncQueue) {
|
|
6124
|
-
await this.syncQueue.stop();
|
|
6386
|
+
await this.syncQueue.stop(options?.syncFlush ?? {});
|
|
6125
6387
|
this.syncQueue = null;
|
|
6126
6388
|
}
|
|
6127
6389
|
this.evaluator = null;
|
|
@@ -6144,7 +6406,7 @@ var init_gateway = __esm({
|
|
|
6144
6406
|
let decision;
|
|
6145
6407
|
switch (this.config.mode) {
|
|
6146
6408
|
case "online":
|
|
6147
|
-
decision = await verifyOnline(this.config, context, astraId);
|
|
6409
|
+
decision = await verifyOnline(this.config, context, astraId ?? this.config.agentId);
|
|
6148
6410
|
break;
|
|
6149
6411
|
case "local":
|
|
6150
6412
|
if (!this.evaluator) throw new Error("Local evaluator not initialized");
|
|
@@ -6199,6 +6461,13 @@ var init_gateway = __esm({
|
|
|
6199
6461
|
getSyncStatus() {
|
|
6200
6462
|
return this.syncQueue?.getStatus() || null;
|
|
6201
6463
|
}
|
|
6464
|
+
/**
|
|
6465
|
+
* Record a tool call the calling adapter saw but did not govern
|
|
6466
|
+
* (unmapped tool, skipTools, MCP with governMcp off). Trace-only.
|
|
6467
|
+
*/
|
|
6468
|
+
logSkippedTool(toolName, reason) {
|
|
6469
|
+
this.traceLogger?.logSkipped(toolName, reason);
|
|
6470
|
+
}
|
|
6202
6471
|
};
|
|
6203
6472
|
}
|
|
6204
6473
|
});
|
|
@@ -6333,9 +6602,9 @@ var init_git_hooks = __esm({
|
|
|
6333
6602
|
});
|
|
6334
6603
|
|
|
6335
6604
|
// src/claude-code/settings-installer.ts
|
|
6336
|
-
function isGuardEntry(entry) {
|
|
6605
|
+
function isGuardEntry(entry, marker2) {
|
|
6337
6606
|
return (entry.hooks ?? []).some(
|
|
6338
|
-
(h) => typeof h.command === "string" && h.command.includes(
|
|
6607
|
+
(h) => typeof h.command === "string" && h.command.includes(marker2)
|
|
6339
6608
|
);
|
|
6340
6609
|
}
|
|
6341
6610
|
function readSettings(settingsPath) {
|
|
@@ -6349,24 +6618,26 @@ function writeSettings(settingsPath, settings) {
|
|
|
6349
6618
|
(0, import_fs5.writeFileSync)(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
6350
6619
|
`, "utf-8");
|
|
6351
6620
|
}
|
|
6352
|
-
function buildHookEntry(hookCommand) {
|
|
6621
|
+
function buildHookEntry(hookCommand, matcher = HOOK_MATCHER, extraHookFields = {}) {
|
|
6353
6622
|
return {
|
|
6354
|
-
matcher
|
|
6355
|
-
hooks: [
|
|
6623
|
+
matcher,
|
|
6624
|
+
hooks: [
|
|
6625
|
+
{ type: "command", command: hookCommand, timeout: HOOK_TIMEOUT_SECONDS, ...extraHookFields }
|
|
6626
|
+
]
|
|
6356
6627
|
};
|
|
6357
6628
|
}
|
|
6358
|
-
function installClaudeHooks(settingsPath, hookCommand) {
|
|
6359
|
-
if (!hookCommand.includes(
|
|
6629
|
+
function installClaudeHooks(settingsPath, hookCommand, marker2 = ASTRASYNC_HOOK_MARKER, matcher = HOOK_MATCHER, extraHookFields = {}) {
|
|
6630
|
+
if (!hookCommand.includes(marker2)) {
|
|
6360
6631
|
throw new Error(
|
|
6361
|
-
`Hook command must reference ${
|
|
6632
|
+
`Hook command must reference ${marker2} (got: ${hookCommand}) \u2014 the marker is how install/uninstall identify the guard entry.`
|
|
6362
6633
|
);
|
|
6363
6634
|
}
|
|
6364
6635
|
const settings = readSettings(settingsPath);
|
|
6365
6636
|
settings.hooks = settings.hooks ?? {};
|
|
6366
6637
|
const preToolUse = settings.hooks.PreToolUse = settings.hooks.PreToolUse ?? [];
|
|
6367
|
-
const existing = preToolUse.find(isGuardEntry);
|
|
6638
|
+
const existing = preToolUse.find((entry) => isGuardEntry(entry, marker2));
|
|
6368
6639
|
if (existing) {
|
|
6369
|
-
const fresh = buildHookEntry(hookCommand);
|
|
6640
|
+
const fresh = buildHookEntry(hookCommand, matcher, extraHookFields);
|
|
6370
6641
|
const changed = JSON.stringify(existing) !== JSON.stringify(fresh);
|
|
6371
6642
|
if (changed) {
|
|
6372
6643
|
preToolUse[preToolUse.indexOf(existing)] = fresh;
|
|
@@ -6374,11 +6645,11 @@ function installClaudeHooks(settingsPath, hookCommand) {
|
|
|
6374
6645
|
}
|
|
6375
6646
|
return { installed: false, alreadyPresent: true, updated: changed };
|
|
6376
6647
|
}
|
|
6377
|
-
preToolUse.push(buildHookEntry(hookCommand));
|
|
6648
|
+
preToolUse.push(buildHookEntry(hookCommand, matcher, extraHookFields));
|
|
6378
6649
|
writeSettings(settingsPath, settings);
|
|
6379
6650
|
return { installed: true, alreadyPresent: false, updated: false };
|
|
6380
6651
|
}
|
|
6381
|
-
function uninstallClaudeHooks(settingsPath) {
|
|
6652
|
+
function uninstallClaudeHooks(settingsPath, marker2 = ASTRASYNC_HOOK_MARKER) {
|
|
6382
6653
|
if (!(0, import_fs5.existsSync)(settingsPath)) return false;
|
|
6383
6654
|
let settings;
|
|
6384
6655
|
try {
|
|
@@ -6388,7 +6659,7 @@ function uninstallClaudeHooks(settingsPath) {
|
|
|
6388
6659
|
}
|
|
6389
6660
|
const preToolUse = settings.hooks?.PreToolUse;
|
|
6390
6661
|
if (!preToolUse) return false;
|
|
6391
|
-
const remaining = preToolUse.filter((entry) => !isGuardEntry(entry));
|
|
6662
|
+
const remaining = preToolUse.filter((entry) => !isGuardEntry(entry, marker2));
|
|
6392
6663
|
if (remaining.length === preToolUse.length) return false;
|
|
6393
6664
|
if (remaining.length > 0) {
|
|
6394
6665
|
settings.hooks.PreToolUse = remaining;
|
|
@@ -6452,16 +6723,44 @@ var init_settings_installer = __esm({
|
|
|
6452
6723
|
init_wizard();
|
|
6453
6724
|
init_pdlss_parser();
|
|
6454
6725
|
ASTRASYNC_HOOK_MARKER = "astrasync-claude-hook";
|
|
6455
|
-
HOOK_MATCHER = "Bash|Write|Edit|MultiEdit|NotebookEdit|Read|Glob|Grep|WebFetch|WebSearch|Task|Agent|mcp__.*";
|
|
6726
|
+
HOOK_MATCHER = "Bash|PowerShell|Write|Edit|MultiEdit|NotebookEdit|Read|Glob|Grep|WebFetch|WebSearch|Task|Agent|mcp__.*";
|
|
6456
6727
|
HOOK_TIMEOUT_SECONDS = 10;
|
|
6457
6728
|
GUARD_SELF_PROTECTION_PATTERNS = [
|
|
6458
6729
|
"**/.claude/settings.json",
|
|
6459
6730
|
"**/.claude/settings.local.json",
|
|
6460
|
-
"**/.astrasync/**"
|
|
6731
|
+
"**/.astrasync/**",
|
|
6732
|
+
"**/.codex/hooks.json",
|
|
6733
|
+
"**/.codex/config.toml"
|
|
6461
6734
|
];
|
|
6462
6735
|
}
|
|
6463
6736
|
});
|
|
6464
6737
|
|
|
6738
|
+
// src/codex/settings-installer.ts
|
|
6739
|
+
function installCodexHooks(hooksPath, hookCommand) {
|
|
6740
|
+
return installClaudeHooks(
|
|
6741
|
+
hooksPath,
|
|
6742
|
+
hookCommand,
|
|
6743
|
+
ASTRASYNC_CODEX_HOOK_MARKER,
|
|
6744
|
+
CODEX_HOOK_MATCHER,
|
|
6745
|
+
{
|
|
6746
|
+
statusMessage: CODEX_STATUS_MESSAGE
|
|
6747
|
+
}
|
|
6748
|
+
);
|
|
6749
|
+
}
|
|
6750
|
+
function uninstallCodexHooks(hooksPath) {
|
|
6751
|
+
return uninstallClaudeHooks(hooksPath, ASTRASYNC_CODEX_HOOK_MARKER);
|
|
6752
|
+
}
|
|
6753
|
+
var ASTRASYNC_CODEX_HOOK_MARKER, CODEX_HOOK_MATCHER, CODEX_STATUS_MESSAGE;
|
|
6754
|
+
var init_settings_installer2 = __esm({
|
|
6755
|
+
"src/codex/settings-installer.ts"() {
|
|
6756
|
+
"use strict";
|
|
6757
|
+
init_settings_installer();
|
|
6758
|
+
ASTRASYNC_CODEX_HOOK_MARKER = "astrasync-codex-hook";
|
|
6759
|
+
CODEX_HOOK_MATCHER = "Bash|apply_patch|Edit|Write|web_search|mcp__.*";
|
|
6760
|
+
CODEX_STATUS_MESSAGE = "AstraSync Local Guard";
|
|
6761
|
+
}
|
|
6762
|
+
});
|
|
6763
|
+
|
|
6465
6764
|
// src/cli/guard.ts
|
|
6466
6765
|
var guard_exports = {};
|
|
6467
6766
|
function createPrompt() {
|
|
@@ -6626,6 +6925,7 @@ async function cmdStatus() {
|
|
|
6626
6925
|
console.log(' Policy: not found \u2014 run "astrasync guard setup"');
|
|
6627
6926
|
}
|
|
6628
6927
|
printClaudeCodeStatus();
|
|
6928
|
+
printCodexStatus();
|
|
6629
6929
|
console.log("");
|
|
6630
6930
|
}
|
|
6631
6931
|
async function cmdCheck() {
|
|
@@ -6712,7 +7012,7 @@ async function cmdUninstallHooks() {
|
|
|
6712
7012
|
function resolveClaudeHookCommand() {
|
|
6713
7013
|
const sibling = path.join(__dirname, "astrasync-claude-hook.js");
|
|
6714
7014
|
if (fs.existsSync(sibling)) {
|
|
6715
|
-
return `
|
|
7015
|
+
return `"${process.execPath}" "${sibling}"`;
|
|
6716
7016
|
}
|
|
6717
7017
|
return "astrasync-claude-hook";
|
|
6718
7018
|
}
|
|
@@ -6804,6 +7104,108 @@ async function cmdUninstallClaudeCode() {
|
|
|
6804
7104
|
`[AstraSync] Policy and traces left in place at ${astrasyncDir} \u2014 delete manually if unwanted.`
|
|
6805
7105
|
);
|
|
6806
7106
|
}
|
|
7107
|
+
function resolveCodexHookCommand() {
|
|
7108
|
+
const sibling = path.join(__dirname, "astrasync-codex-hook.js");
|
|
7109
|
+
if (fs.existsSync(sibling)) {
|
|
7110
|
+
return `"${process.execPath}" "${sibling}"`;
|
|
7111
|
+
}
|
|
7112
|
+
return "astrasync-codex-hook";
|
|
7113
|
+
}
|
|
7114
|
+
function codexPaths(project) {
|
|
7115
|
+
const root = project ? process.cwd() : os.homedir();
|
|
7116
|
+
return {
|
|
7117
|
+
hooksPath: path.join(root, ".codex", "hooks.json"),
|
|
7118
|
+
astrasyncDir: path.join(root, ".astrasync")
|
|
7119
|
+
};
|
|
7120
|
+
}
|
|
7121
|
+
async function cmdInstallCodex() {
|
|
7122
|
+
const flags = parseClaudeCodeFlags();
|
|
7123
|
+
const { hooksPath, astrasyncDir } = codexPaths(flags.project);
|
|
7124
|
+
const policyPath = flags.policyOverride || path.join(astrasyncDir, "policy.yaml");
|
|
7125
|
+
const guardJsonPath = path.join(astrasyncDir, "guard.json");
|
|
7126
|
+
console.log("\nAstraSync Local Guard for Codex CLI \u2014 install\n");
|
|
7127
|
+
if (fs.existsSync(policyPath)) {
|
|
7128
|
+
console.log(` Policy: using existing ${policyPath} (shared across adapters)`);
|
|
7129
|
+
if (ensureGuardSelfProtection(policyPath)) {
|
|
7130
|
+
console.log(" added guard self-protection patterns to file.write");
|
|
7131
|
+
}
|
|
7132
|
+
} else if (flags.wizard) {
|
|
7133
|
+
const prompt = createPrompt();
|
|
7134
|
+
try {
|
|
7135
|
+
fs.mkdirSync(path.dirname(policyPath), { recursive: true });
|
|
7136
|
+
const wizard = new SetupWizard(prompt);
|
|
7137
|
+
await wizard.runAndSave(policyPath);
|
|
7138
|
+
} finally {
|
|
7139
|
+
prompt.close();
|
|
7140
|
+
}
|
|
7141
|
+
ensureGuardSelfProtection(policyPath);
|
|
7142
|
+
console.log(` Policy: created via wizard at ${policyPath}`);
|
|
7143
|
+
} else {
|
|
7144
|
+
fs.mkdirSync(path.dirname(policyPath), { recursive: true });
|
|
7145
|
+
fs.writeFileSync(policyPath, toYaml(buildDefaultClaudeCodePolicy()), "utf-8");
|
|
7146
|
+
console.log(` Policy: default coding-agent policy written to ${policyPath}`);
|
|
7147
|
+
console.log(" (re-run with --wizard for the interactive setup instead)");
|
|
7148
|
+
}
|
|
7149
|
+
if (!fs.existsSync(guardJsonPath)) {
|
|
7150
|
+
const guardSettings = {
|
|
7151
|
+
posture: flags.active ? "active" : "passive",
|
|
7152
|
+
governMcp: false
|
|
7153
|
+
};
|
|
7154
|
+
fs.writeFileSync(guardJsonPath, `${JSON.stringify(guardSettings, null, 2)}
|
|
7155
|
+
`, "utf-8");
|
|
7156
|
+
console.log(
|
|
7157
|
+
` Guard: ${guardJsonPath} (posture: ${guardSettings.posture}, governMcp: false)`
|
|
7158
|
+
);
|
|
7159
|
+
} else {
|
|
7160
|
+
console.log(` Guard: using existing ${guardJsonPath}`);
|
|
7161
|
+
}
|
|
7162
|
+
const hookCommand = resolveCodexHookCommand();
|
|
7163
|
+
const result = installCodexHooks(hooksPath, hookCommand);
|
|
7164
|
+
if (result.installed) {
|
|
7165
|
+
console.log(` Hook: installed in ${hooksPath}`);
|
|
7166
|
+
} else if (result.updated) {
|
|
7167
|
+
console.log(` Hook: updated in ${hooksPath}`);
|
|
7168
|
+
} else {
|
|
7169
|
+
console.log(` Hook: already present in ${hooksPath}`);
|
|
7170
|
+
}
|
|
7171
|
+
console.log("\n Next steps:");
|
|
7172
|
+
console.log(" 1. Start a NEW Codex session (hooks load at startup; /hooks to inspect).");
|
|
7173
|
+
console.log(` 2. Watch decisions: tail -f ${path.join(astrasyncDir, "traces")}/*.jsonl`);
|
|
7174
|
+
console.log(` 3. When the traces look right, set "posture": "active" in ${guardJsonPath}.`);
|
|
7175
|
+
console.log(" Denies block; approvals fall through to Codex's own prompt \u2014");
|
|
7176
|
+
console.log(" except in dontAsk/bypassPermissions modes, where they deny.");
|
|
7177
|
+
console.log(" 4. Note: Codex fails OPEN if the hook itself times out; the guard");
|
|
7178
|
+
console.log(" fails closed on every failure it can see.\n");
|
|
7179
|
+
}
|
|
7180
|
+
async function cmdUninstallCodex() {
|
|
7181
|
+
const flags = parseClaudeCodeFlags();
|
|
7182
|
+
const { hooksPath, astrasyncDir } = codexPaths(flags.project);
|
|
7183
|
+
const removed = uninstallCodexHooks(hooksPath);
|
|
7184
|
+
if (removed) {
|
|
7185
|
+
console.log(`[AstraSync] Removed Codex hook from ${hooksPath}`);
|
|
7186
|
+
} else {
|
|
7187
|
+
console.log(`[AstraSync] No AstraSync hook found in ${hooksPath}`);
|
|
7188
|
+
}
|
|
7189
|
+
console.log(
|
|
7190
|
+
`[AstraSync] Policy and traces left in place at ${astrasyncDir} \u2014 delete manually if unwanted.`
|
|
7191
|
+
);
|
|
7192
|
+
}
|
|
7193
|
+
function printCodexStatus() {
|
|
7194
|
+
for (const scope of ["user", "project"]) {
|
|
7195
|
+
const { hooksPath, astrasyncDir } = codexPaths(scope === "project");
|
|
7196
|
+
let hookInstalled = false;
|
|
7197
|
+
try {
|
|
7198
|
+
const content = fs.readFileSync(hooksPath, "utf-8");
|
|
7199
|
+
hookInstalled = content.includes(ASTRASYNC_CODEX_HOOK_MARKER);
|
|
7200
|
+
} catch {
|
|
7201
|
+
}
|
|
7202
|
+
if (hookInstalled) {
|
|
7203
|
+
const policyPath = path.join(astrasyncDir, "policy.yaml");
|
|
7204
|
+
const policy = fs.existsSync(policyPath) ? `policy: ${policyPath}` : "policy: missing";
|
|
7205
|
+
console.log(` Codex CLI (${scope}): hook installed, ${policy}`);
|
|
7206
|
+
}
|
|
7207
|
+
}
|
|
7208
|
+
}
|
|
6807
7209
|
function printClaudeCodeStatus() {
|
|
6808
7210
|
for (const scope of ["user", "project"]) {
|
|
6809
7211
|
const { settingsPath, astrasyncDir } = claudeCodePaths(scope === "project");
|
|
@@ -6861,6 +7263,12 @@ async function main() {
|
|
|
6861
7263
|
case "uninstall-claude-code":
|
|
6862
7264
|
await cmdUninstallClaudeCode();
|
|
6863
7265
|
break;
|
|
7266
|
+
case "install-codex":
|
|
7267
|
+
await cmdInstallCodex();
|
|
7268
|
+
break;
|
|
7269
|
+
case "uninstall-codex":
|
|
7270
|
+
await cmdUninstallCodex();
|
|
7271
|
+
break;
|
|
6864
7272
|
default:
|
|
6865
7273
|
console.log("AstraSync Local Guard\n");
|
|
6866
7274
|
console.log("Usage: astrasync guard <command>\n");
|
|
@@ -6878,6 +7286,9 @@ async function main() {
|
|
|
6878
7286
|
console.log(
|
|
6879
7287
|
" uninstall-claude-code Remove the Claude Code hook (--project for project scope)"
|
|
6880
7288
|
);
|
|
7289
|
+
console.log(" install-codex Install the Local Guard hook for OpenAI Codex CLI");
|
|
7290
|
+
console.log(" (same flags; policy shared with Claude Code)");
|
|
7291
|
+
console.log(" uninstall-codex Remove the Codex hook (--project for project scope)");
|
|
6881
7292
|
console.log("");
|
|
6882
7293
|
process.exit(command ? 1 : 0);
|
|
6883
7294
|
}
|
|
@@ -6897,6 +7308,7 @@ var init_guard = __esm({
|
|
|
6897
7308
|
init_pdlss_parser();
|
|
6898
7309
|
init_git_hooks();
|
|
6899
7310
|
init_settings_installer();
|
|
7311
|
+
init_settings_installer2();
|
|
6900
7312
|
DEFAULT_POLICY_PATH = "./local-policy.yaml";
|
|
6901
7313
|
DEFAULT_CONFIG_DIR = ".astrasync";
|
|
6902
7314
|
main().catch((err) => {
|