@akasecurity/ai-tc-claude-code 0.9.7 → 0.9.8
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/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/package.json +4 -4
- package/scripts/apply-suppressions.js +283 -132
- package/scripts/backfill.js +1711 -250
- package/scripts/filescan.js +1713 -235
- package/scripts/firstrun.js +1724 -206
- package/scripts/intro.js +171 -47
- package/scripts/message-display.js +279 -128
- package/scripts/onboard.js +261 -110
- package/scripts/post-tool-use.js +1842 -272
- package/scripts/pre-tool-use.js +1841 -284
- package/scripts/query.js +1726 -208
- package/scripts/reconcile.js +1698 -237
- package/scripts/remediate.js +1704 -243
- package/scripts/scan-worker.js +118 -0
- package/scripts/session-start.js +1951 -299
- package/scripts/start-light.js +169 -45
- package/scripts/statusline.js +1726 -206
- package/scripts/stop.js +374 -67
- package/scripts/sync.js +19413 -0
- package/scripts/user-prompt-submit.js +1841 -284
package/scripts/firstrun.js
CHANGED
|
@@ -50,7 +50,7 @@ var require_ignore = __commonJS({
|
|
|
50
50
|
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
51
51
|
var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
|
|
52
52
|
var REGEX_TEST_TRAILING_SLASH = /\/$/;
|
|
53
|
-
var
|
|
53
|
+
var SLASH2 = "/";
|
|
54
54
|
var TMP_KEY_IGNORE = "node-ignore";
|
|
55
55
|
if (typeof Symbol !== "undefined") {
|
|
56
56
|
TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
|
|
@@ -422,11 +422,11 @@ var require_ignore = __commonJS({
|
|
|
422
422
|
if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
|
|
423
423
|
return this.test(path);
|
|
424
424
|
}
|
|
425
|
-
const slices = path.split(
|
|
425
|
+
const slices = path.split(SLASH2).filter(Boolean);
|
|
426
426
|
slices.pop();
|
|
427
427
|
if (slices.length) {
|
|
428
428
|
const parent = this._t(
|
|
429
|
-
slices.join(
|
|
429
|
+
slices.join(SLASH2) + SLASH2,
|
|
430
430
|
this._testCache,
|
|
431
431
|
true,
|
|
432
432
|
slices
|
|
@@ -442,14 +442,14 @@ var require_ignore = __commonJS({
|
|
|
442
442
|
return cache[path];
|
|
443
443
|
}
|
|
444
444
|
if (!slices) {
|
|
445
|
-
slices = path.split(
|
|
445
|
+
slices = path.split(SLASH2).filter(Boolean);
|
|
446
446
|
}
|
|
447
447
|
slices.pop();
|
|
448
448
|
if (!slices.length) {
|
|
449
449
|
return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE);
|
|
450
450
|
}
|
|
451
451
|
const parent = this._t(
|
|
452
|
-
slices.join(
|
|
452
|
+
slices.join(SLASH2) + SLASH2,
|
|
453
453
|
cache,
|
|
454
454
|
checkUnignored,
|
|
455
455
|
slices
|
|
@@ -491,10 +491,9 @@ var require_ignore = __commonJS({
|
|
|
491
491
|
}
|
|
492
492
|
});
|
|
493
493
|
|
|
494
|
-
// ../../packages/persistence/src/
|
|
495
|
-
import {
|
|
496
|
-
import { join
|
|
497
|
-
import { DatabaseSync } from "node:sqlite";
|
|
494
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
495
|
+
import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync2, statSync } from "fs";
|
|
496
|
+
import { join } from "path";
|
|
498
497
|
|
|
499
498
|
// ../../packages/schema/src/drizzle/sqlite-ddl.ts
|
|
500
499
|
var SQLITE_MIGRATIONS = [
|
|
@@ -16198,6 +16197,125 @@ var ConfigScanRecord = external_exports.object({
|
|
|
16198
16197
|
findings: external_exports.array(ConfigPostureFindingInput).optional()
|
|
16199
16198
|
});
|
|
16200
16199
|
|
|
16200
|
+
// ../../packages/schema/src/zod/control-plane.ts
|
|
16201
|
+
var ATTACHED_CREDENTIAL_FILENAME = "control-plane-credential.json";
|
|
16202
|
+
var ATTACHED_CREDENTIAL_SPEC_VERSION = 1;
|
|
16203
|
+
var AttachedCredential = external_exports.object({
|
|
16204
|
+
specVersion: external_exports.literal(ATTACHED_CREDENTIAL_SPEC_VERSION),
|
|
16205
|
+
// The control-plane endpoint this credential was minted against.
|
|
16206
|
+
endpoint: external_exports.string().min(1),
|
|
16207
|
+
// The bearer credential itself. Never logged, never rendered — status
|
|
16208
|
+
// surfaces show `keyPrefix` and nothing else.
|
|
16209
|
+
apiKey: external_exports.string().min(1),
|
|
16210
|
+
// First few characters of the key, safe to display so a user can match the
|
|
16211
|
+
// credential against their organization's key list.
|
|
16212
|
+
keyPrefix: external_exports.string().min(1).max(16).optional(),
|
|
16213
|
+
mintedAt: external_exports.iso.datetime().optional()
|
|
16214
|
+
});
|
|
16215
|
+
var MAX_DATE_MS = 253402300799999;
|
|
16216
|
+
var MAX_INT4 = 2147483647;
|
|
16217
|
+
var StorePosturePack = external_exports.object({
|
|
16218
|
+
packId: external_exports.string().min(1),
|
|
16219
|
+
// 'namespace/packId'
|
|
16220
|
+
version: external_exports.string().min(1),
|
|
16221
|
+
enabled: external_exports.boolean(),
|
|
16222
|
+
// Stringified pass-through of the local store's `installed_packs.updated_at`
|
|
16223
|
+
// — the column format is store-version-dependent (epoch millis vs ISO), so
|
|
16224
|
+
// the wire shape assumes neither.
|
|
16225
|
+
updatedAt: external_exports.string().nullable()
|
|
16226
|
+
}).meta({ id: "StorePosturePack" });
|
|
16227
|
+
var StorePosturePolicyCounts = external_exports.object({
|
|
16228
|
+
total: external_exports.number().int().min(0),
|
|
16229
|
+
disabled: external_exports.number().int().min(0),
|
|
16230
|
+
// Exhaustive per-action map; the builder pre-fills every action with 0.
|
|
16231
|
+
//
|
|
16232
|
+
// Spelled out member-by-member rather than `z.record(ActionTaken, …)`. Zod
|
|
16233
|
+
// enforces exhaustiveness either way, but z.record emits `propertyNames` +
|
|
16234
|
+
// `additionalProperties` into a generated schema document, and a type
|
|
16235
|
+
// generator renders THAT with every key optional — a sender built against
|
|
16236
|
+
// the generated type would typecheck and still be rejected at runtime. An
|
|
16237
|
+
// explicit object emits `properties` + `required`, so generated types
|
|
16238
|
+
// demand all five.
|
|
16239
|
+
//
|
|
16240
|
+
// `satisfies Record<ActionTaken, …>` keeps the link to the enum: adding an
|
|
16241
|
+
// ActionTaken member is a COMPILE error here instead of silent drift.
|
|
16242
|
+
// `.strict()` is load-bearing — it rejects an unknown action key, which a
|
|
16243
|
+
// bare object would silently STRIP, accepting a miscounted map as valid.
|
|
16244
|
+
byAction: external_exports.object({
|
|
16245
|
+
warn: external_exports.number().int().min(0),
|
|
16246
|
+
redact: external_exports.number().int().min(0),
|
|
16247
|
+
block: external_exports.number().int().min(0),
|
|
16248
|
+
allow: external_exports.number().int().min(0),
|
|
16249
|
+
log: external_exports.number().int().min(0)
|
|
16250
|
+
}).strict()
|
|
16251
|
+
}).meta({ id: "StorePosturePolicyCounts" });
|
|
16252
|
+
var StorePosturePlugin = external_exports.object({
|
|
16253
|
+
/** Package name of the reporting plugin. */
|
|
16254
|
+
package: external_exports.string().min(1).max(200),
|
|
16255
|
+
version: external_exports.string().min(1).max(64),
|
|
16256
|
+
/** Version of the bundled core, when the build records one separately. */
|
|
16257
|
+
ossVersion: external_exports.string().max(64).nullable(),
|
|
16258
|
+
/**
|
|
16259
|
+
* `version` of the policy bundle this machine last fetched. Bounded at 200
|
|
16260
|
+
* rather than the 64 a bare sha256 hex digest needs today, so a later
|
|
16261
|
+
* format with an algorithm prefix does not start rejecting the channel.
|
|
16262
|
+
*/
|
|
16263
|
+
policyBundleVersion: external_exports.string().max(200).nullable(),
|
|
16264
|
+
/** Epoch millis, on the CLIENT clock, of that fetch. */
|
|
16265
|
+
policyFetchedAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable()
|
|
16266
|
+
}).meta({ id: "StorePosturePlugin" });
|
|
16267
|
+
var StorePostureSnapshot = external_exports.object({
|
|
16268
|
+
deviceId: external_exports.guid(),
|
|
16269
|
+
hostname: external_exports.string().min(1).max(253),
|
|
16270
|
+
// Epoch millis on the CLIENT clock. Bounded by what a receiving store
|
|
16271
|
+
// accepts (see MAX_DATE_MS), not by what a JavaScript Date can hold.
|
|
16272
|
+
capturedAt: external_exports.number().int().min(0).max(MAX_DATE_MS),
|
|
16273
|
+
// False is a measurement, not an error state: "no local store exists on
|
|
16274
|
+
// this machine".
|
|
16275
|
+
storePresent: external_exports.boolean(),
|
|
16276
|
+
schemaVersion: external_exports.number().int().min(0).max(MAX_INT4).nullable(),
|
|
16277
|
+
// PRAGMA user_version
|
|
16278
|
+
findingsTotal: external_exports.number().int().min(0).max(MAX_INT4),
|
|
16279
|
+
// Epoch millis, bounded like `capturedAt` — see MAX_DATE_MS on what that
|
|
16280
|
+
// bound does and does not do. Worth stating for these two specifically:
|
|
16281
|
+
// they are read from the local store's own ROWS rather than from this
|
|
16282
|
+
// machine's clock, so a damaged or hand-edited store is enough to produce
|
|
16283
|
+
// an out-of-range value with no clock skew involved.
|
|
16284
|
+
findingsFirstAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16285
|
+
findingsLastAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16286
|
+
packs: external_exports.array(StorePosturePack).max(500),
|
|
16287
|
+
policyCounts: StorePosturePolicyCounts,
|
|
16288
|
+
// OPTIONAL, not nullable: a reporter that predates this member keeps
|
|
16289
|
+
// getting its 200 without a payload change.
|
|
16290
|
+
plugin: StorePosturePlugin.optional()
|
|
16291
|
+
}).meta({ id: "StorePostureSnapshot" });
|
|
16292
|
+
var CAPTURE_VERSION_PREFIX = "capture/";
|
|
16293
|
+
var RecordAuditEventRequest = AuditEventInput.extend({
|
|
16294
|
+
inspections: external_exports.array(ToolCallInspection).default([])
|
|
16295
|
+
}).refine((v) => v.inspections.every((i) => !i.ruleVersion.startsWith(CAPTURE_VERSION_PREFIX)), {
|
|
16296
|
+
message: `inspections[].ruleVersion must not start with \`${CAPTURE_VERSION_PREFIX}\` \u2014 that namespace is reserved for capture definitions the control plane mints itself`,
|
|
16297
|
+
path: ["inspections"]
|
|
16298
|
+
}).meta({ id: "RecordAuditEventRequest" });
|
|
16299
|
+
var IngestAck = external_exports.object({
|
|
16300
|
+
accepted: external_exports.number().int().nonnegative(),
|
|
16301
|
+
duplicates: external_exports.number().int().nonnegative()
|
|
16302
|
+
});
|
|
16303
|
+
var PRINTABLE = /^[^\p{Cc}\p{Cf}]*$/u;
|
|
16304
|
+
var printable = (max) => external_exports.string().max(max).regex(PRINTABLE, "must not contain control characters");
|
|
16305
|
+
var PluginWhoami = external_exports.object({
|
|
16306
|
+
tenantName: printable(200),
|
|
16307
|
+
userEmail: printable(320),
|
|
16308
|
+
role: printable(64),
|
|
16309
|
+
keyKind: printable(64),
|
|
16310
|
+
serverTime: printable(64)
|
|
16311
|
+
});
|
|
16312
|
+
var ControlPlaneErrorBody = external_exports.object({
|
|
16313
|
+
error: external_exports.object({
|
|
16314
|
+
code: external_exports.string().optional(),
|
|
16315
|
+
message: external_exports.string().optional()
|
|
16316
|
+
}).optional()
|
|
16317
|
+
});
|
|
16318
|
+
|
|
16201
16319
|
// ../../packages/schema/src/zod/registry.ts
|
|
16202
16320
|
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16203
16321
|
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
@@ -16500,15 +16618,15 @@ function summaryToDetectionListItem(s) {
|
|
|
16500
16618
|
}
|
|
16501
16619
|
function rowToDetectionDetail(row, findingsLast30d, update) {
|
|
16502
16620
|
const rules = row.rules.flatMap((r) => {
|
|
16503
|
-
const
|
|
16504
|
-
if (!
|
|
16621
|
+
const parsed2 = Matcher.safeParse(r.matcher);
|
|
16622
|
+
if (!parsed2.success) return [];
|
|
16505
16623
|
return [
|
|
16506
16624
|
{
|
|
16507
16625
|
id: r.id,
|
|
16508
16626
|
name: r.name,
|
|
16509
16627
|
category: r.category,
|
|
16510
16628
|
severity: r.severity,
|
|
16511
|
-
matcher:
|
|
16629
|
+
matcher: parsed2.data
|
|
16512
16630
|
}
|
|
16513
16631
|
];
|
|
16514
16632
|
});
|
|
@@ -17154,8 +17272,8 @@ function toApiAction(dbVal) {
|
|
|
17154
17272
|
}
|
|
17155
17273
|
function toApiCategory(dbVal) {
|
|
17156
17274
|
if (dbVal === "code_context") return "source_code";
|
|
17157
|
-
const
|
|
17158
|
-
return
|
|
17275
|
+
const parsed2 = FindingCategory.safeParse(dbVal);
|
|
17276
|
+
return parsed2.success ? parsed2.data : "custom";
|
|
17159
17277
|
}
|
|
17160
17278
|
function toApiProvider(sourceTool) {
|
|
17161
17279
|
return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
|
|
@@ -17782,6 +17900,9 @@ var WorkspaceSettings = external_exports.object({
|
|
|
17782
17900
|
function defaultWorkspaceSettings() {
|
|
17783
17901
|
return WorkspaceSettings.parse({});
|
|
17784
17902
|
}
|
|
17903
|
+
function isAttached(settings) {
|
|
17904
|
+
return settings.runMode === "attached" && settings.controlPlane !== void 0;
|
|
17905
|
+
}
|
|
17785
17906
|
function toInventoryRow(input, id, now) {
|
|
17786
17907
|
return {
|
|
17787
17908
|
id,
|
|
@@ -18049,8 +18170,8 @@ function builtinPolicyIsReversible(id) {
|
|
|
18049
18170
|
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
18050
18171
|
}
|
|
18051
18172
|
function policyIdIsReversible(policyId) {
|
|
18052
|
-
const
|
|
18053
|
-
const id =
|
|
18173
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18174
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18054
18175
|
return builtinPolicyIsReversible(id);
|
|
18055
18176
|
}
|
|
18056
18177
|
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
@@ -18061,8 +18182,8 @@ var BUILTIN_POLICIES = Object.fromEntries(
|
|
|
18061
18182
|
);
|
|
18062
18183
|
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
18063
18184
|
function policyIdToAction(policyId) {
|
|
18064
|
-
const
|
|
18065
|
-
const id =
|
|
18185
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18186
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18066
18187
|
return BUILTIN_POLICIES[id].action;
|
|
18067
18188
|
}
|
|
18068
18189
|
var UsedByItem = external_exports.object({
|
|
@@ -18505,53 +18626,6 @@ function reviewSeverityRank(reasons) {
|
|
|
18505
18626
|
return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
|
|
18506
18627
|
}
|
|
18507
18628
|
|
|
18508
|
-
// ../../packages/persistence/src/ids.ts
|
|
18509
|
-
import { createHash } from "crypto";
|
|
18510
|
-
function sha256Hex(input) {
|
|
18511
|
-
return createHash("sha256").update(input).digest("hex");
|
|
18512
|
-
}
|
|
18513
|
-
function inventoryId(objectType, identityKey) {
|
|
18514
|
-
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18515
|
-
}
|
|
18516
|
-
function sourceProjectId(url2) {
|
|
18517
|
-
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18518
|
-
}
|
|
18519
|
-
function classifiedDataId(cls) {
|
|
18520
|
-
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18521
|
-
}
|
|
18522
|
-
function inspectionDefinitionId(ruleId, version2) {
|
|
18523
|
-
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18524
|
-
}
|
|
18525
|
-
function llmCallId(sessionId, messageId) {
|
|
18526
|
-
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18527
|
-
}
|
|
18528
|
-
function toolCallId(sessionId, toolUseId) {
|
|
18529
|
-
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18530
|
-
}
|
|
18531
|
-
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18532
|
-
return sha256Hex(
|
|
18533
|
-
canonicalIdentity([
|
|
18534
|
-
"inspection_finding",
|
|
18535
|
-
auditEventId,
|
|
18536
|
-
ruleId,
|
|
18537
|
-
String(spanStart),
|
|
18538
|
-
String(spanEnd)
|
|
18539
|
-
])
|
|
18540
|
-
);
|
|
18541
|
-
}
|
|
18542
|
-
var NO_SESSION = "no_session";
|
|
18543
|
-
var NO_PATH = "no_path";
|
|
18544
|
-
function captureId(sessionId, contentHash, filePath = null) {
|
|
18545
|
-
return sha256Hex(
|
|
18546
|
-
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18547
|
-
);
|
|
18548
|
-
}
|
|
18549
|
-
|
|
18550
|
-
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18551
|
-
import { randomUUID } from "crypto";
|
|
18552
|
-
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
|
|
18553
|
-
import { basename, dirname, join } from "path";
|
|
18554
|
-
|
|
18555
18629
|
// ../../packages/persistence/src/paths.ts
|
|
18556
18630
|
import {
|
|
18557
18631
|
chmodSync,
|
|
@@ -18599,8 +18673,181 @@ function tightenFile(file2) {
|
|
|
18599
18673
|
function tightenPerms(file2) {
|
|
18600
18674
|
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18601
18675
|
}
|
|
18676
|
+
function writeExclusiveOwnerOnlySync(file2, data) {
|
|
18677
|
+
writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
|
|
18678
|
+
}
|
|
18679
|
+
function writeOwnerOnlyFileSync(file2, data) {
|
|
18680
|
+
const tmp = `${file2}.${String(process.pid)}.tmp`;
|
|
18681
|
+
try {
|
|
18682
|
+
rmSync(tmp, { force: true });
|
|
18683
|
+
} catch {
|
|
18684
|
+
}
|
|
18685
|
+
try {
|
|
18686
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18687
|
+
renameSync(tmp, file2);
|
|
18688
|
+
} finally {
|
|
18689
|
+
try {
|
|
18690
|
+
rmSync(tmp, { force: true });
|
|
18691
|
+
} catch {
|
|
18692
|
+
}
|
|
18693
|
+
}
|
|
18694
|
+
tightenFile(file2);
|
|
18695
|
+
}
|
|
18696
|
+
function createOwnerOnlyFileSync(file2, data) {
|
|
18697
|
+
const tmp = `${file2}.${String(process.pid)}.${String(threadId)}.new`;
|
|
18698
|
+
try {
|
|
18699
|
+
rmSync(tmp, { force: true });
|
|
18700
|
+
} catch {
|
|
18701
|
+
}
|
|
18702
|
+
let created;
|
|
18703
|
+
try {
|
|
18704
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18705
|
+
created = publishByLink(tmp, file2, data);
|
|
18706
|
+
} finally {
|
|
18707
|
+
try {
|
|
18708
|
+
rmSync(tmp, { force: true });
|
|
18709
|
+
} catch {
|
|
18710
|
+
}
|
|
18711
|
+
}
|
|
18712
|
+
if (created) tightenFile(file2);
|
|
18713
|
+
return created;
|
|
18714
|
+
}
|
|
18715
|
+
var LINK_UNSUPPORTED = /* @__PURE__ */ new Set(["EPERM", "ENOSYS", "ENOTSUP", "EOPNOTSUPP", "EINVAL"]);
|
|
18716
|
+
function publishByLink(tmp, file2, data) {
|
|
18717
|
+
try {
|
|
18718
|
+
linkSync(tmp, file2);
|
|
18719
|
+
return true;
|
|
18720
|
+
} catch (err) {
|
|
18721
|
+
const code = err.code;
|
|
18722
|
+
if (code === "EEXIST") return false;
|
|
18723
|
+
if (!LINK_UNSUPPORTED.has(code ?? "")) throw err;
|
|
18724
|
+
}
|
|
18725
|
+
try {
|
|
18726
|
+
writeExclusiveOwnerOnlySync(file2, data);
|
|
18727
|
+
return true;
|
|
18728
|
+
} catch (err) {
|
|
18729
|
+
if (err.code === "EEXIST") return false;
|
|
18730
|
+
throw err;
|
|
18731
|
+
}
|
|
18732
|
+
}
|
|
18733
|
+
|
|
18734
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
18735
|
+
function controlPlaneCredentialPath(settingsDir2) {
|
|
18736
|
+
return join(settingsDir2, ATTACHED_CREDENTIAL_FILENAME);
|
|
18737
|
+
}
|
|
18738
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
18739
|
+
function isSafeEndpoint(endpoint) {
|
|
18740
|
+
let parsed2;
|
|
18741
|
+
try {
|
|
18742
|
+
parsed2 = new URL(endpoint);
|
|
18743
|
+
} catch {
|
|
18744
|
+
return false;
|
|
18745
|
+
}
|
|
18746
|
+
if (parsed2.protocol === "https:") return true;
|
|
18747
|
+
return parsed2.protocol === "http:" && LOOPBACK_HOSTS.has(parsed2.hostname);
|
|
18748
|
+
}
|
|
18749
|
+
function repairOrRefuseMode(file2) {
|
|
18750
|
+
const link = lstatSync2(file2, { throwIfNoEntry: false });
|
|
18751
|
+
if (link === void 0) return "absent";
|
|
18752
|
+
if (link.isSymbolicLink()) return "untrusted";
|
|
18753
|
+
const stat = statSync(file2, { throwIfNoEntry: false });
|
|
18754
|
+
if (stat === void 0) return "absent";
|
|
18755
|
+
const uid = process.getuid?.();
|
|
18756
|
+
if (uid !== void 0 && stat.uid !== uid) return "untrusted";
|
|
18757
|
+
if (process.platform !== "win32" && (stat.mode & 511) !== DATA_FILE_MODE) {
|
|
18758
|
+
try {
|
|
18759
|
+
chmodSync2(file2, DATA_FILE_MODE);
|
|
18760
|
+
} catch {
|
|
18761
|
+
return "untrusted";
|
|
18762
|
+
}
|
|
18763
|
+
}
|
|
18764
|
+
return "ok";
|
|
18765
|
+
}
|
|
18766
|
+
function readControlPlaneCredentialState(settingsDir2, connection) {
|
|
18767
|
+
const file2 = controlPlaneCredentialPath(settingsDir2);
|
|
18768
|
+
let raw;
|
|
18769
|
+
const gate = repairOrRefuseMode(file2);
|
|
18770
|
+
if (gate === "absent") return { usable: false, reason: "absent" };
|
|
18771
|
+
if (gate === "untrusted") return { usable: false, reason: "untrusted-file" };
|
|
18772
|
+
try {
|
|
18773
|
+
raw = readFileSync(file2, "utf8");
|
|
18774
|
+
} catch (err) {
|
|
18775
|
+
const code = err.code;
|
|
18776
|
+
return { usable: false, reason: code === "ENOENT" ? "absent" : "unreadable" };
|
|
18777
|
+
}
|
|
18778
|
+
let parsed2;
|
|
18779
|
+
try {
|
|
18780
|
+
parsed2 = JSON.parse(raw);
|
|
18781
|
+
} catch {
|
|
18782
|
+
return { usable: false, reason: "malformed" };
|
|
18783
|
+
}
|
|
18784
|
+
const result = AttachedCredential.safeParse(parsed2);
|
|
18785
|
+
if (!result.success) return { usable: false, reason: "malformed" };
|
|
18786
|
+
if (!isSafeEndpoint(result.data.endpoint)) {
|
|
18787
|
+
return { usable: false, reason: "unsafe-endpoint" };
|
|
18788
|
+
}
|
|
18789
|
+
if (connection !== void 0 && connection.endpoint !== result.data.endpoint) {
|
|
18790
|
+
return {
|
|
18791
|
+
usable: false,
|
|
18792
|
+
reason: "endpoint-mismatch",
|
|
18793
|
+
credentialEndpoint: result.data.endpoint,
|
|
18794
|
+
settingsEndpoint: connection.endpoint
|
|
18795
|
+
};
|
|
18796
|
+
}
|
|
18797
|
+
return { usable: true, credential: result.data };
|
|
18798
|
+
}
|
|
18799
|
+
|
|
18800
|
+
// ../../packages/persistence/src/database.ts
|
|
18801
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
18802
|
+
import { join as join3, sep } from "path";
|
|
18803
|
+
import { DatabaseSync } from "node:sqlite";
|
|
18804
|
+
|
|
18805
|
+
// ../../packages/persistence/src/ids.ts
|
|
18806
|
+
import { createHash } from "crypto";
|
|
18807
|
+
function sha256Hex(input) {
|
|
18808
|
+
return createHash("sha256").update(input).digest("hex");
|
|
18809
|
+
}
|
|
18810
|
+
function inventoryId(objectType, identityKey) {
|
|
18811
|
+
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18812
|
+
}
|
|
18813
|
+
function sourceProjectId(url2) {
|
|
18814
|
+
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18815
|
+
}
|
|
18816
|
+
function classifiedDataId(cls) {
|
|
18817
|
+
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18818
|
+
}
|
|
18819
|
+
function inspectionDefinitionId(ruleId, version2) {
|
|
18820
|
+
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18821
|
+
}
|
|
18822
|
+
function llmCallId(sessionId, messageId) {
|
|
18823
|
+
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18824
|
+
}
|
|
18825
|
+
function toolCallId(sessionId, toolUseId) {
|
|
18826
|
+
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18827
|
+
}
|
|
18828
|
+
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18829
|
+
return sha256Hex(
|
|
18830
|
+
canonicalIdentity([
|
|
18831
|
+
"inspection_finding",
|
|
18832
|
+
auditEventId,
|
|
18833
|
+
ruleId,
|
|
18834
|
+
String(spanStart),
|
|
18835
|
+
String(spanEnd)
|
|
18836
|
+
])
|
|
18837
|
+
);
|
|
18838
|
+
}
|
|
18839
|
+
var NO_SESSION = "no_session";
|
|
18840
|
+
var NO_PATH = "no_path";
|
|
18841
|
+
function captureId(sessionId, contentHash, filePath = null) {
|
|
18842
|
+
return sha256Hex(
|
|
18843
|
+
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18844
|
+
);
|
|
18845
|
+
}
|
|
18602
18846
|
|
|
18603
18847
|
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18848
|
+
import { randomUUID } from "crypto";
|
|
18849
|
+
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
18850
|
+
import { basename, dirname, join as join2 } from "path";
|
|
18604
18851
|
function backupPath(file2, tag) {
|
|
18605
18852
|
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18606
18853
|
}
|
|
@@ -18610,15 +18857,15 @@ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
|
|
|
18610
18857
|
var SNAPSHOT_STAGING_COPY = "copy";
|
|
18611
18858
|
function createSnapshotStaging(backup) {
|
|
18612
18859
|
const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18613
|
-
|
|
18860
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18614
18861
|
mkdirOwnerOnlySync(stage);
|
|
18615
18862
|
tightenDir(stage);
|
|
18616
|
-
return { stage, copy:
|
|
18863
|
+
return { stage, copy: join2(stage, SNAPSHOT_STAGING_COPY) };
|
|
18617
18864
|
}
|
|
18618
18865
|
function idleMs(entry) {
|
|
18619
|
-
for (const candidate of [
|
|
18866
|
+
for (const candidate of [join2(entry, SNAPSHOT_STAGING_COPY), entry]) {
|
|
18620
18867
|
try {
|
|
18621
|
-
return Date.now() -
|
|
18868
|
+
return Date.now() - statSync2(candidate).mtimeMs;
|
|
18622
18869
|
} catch {
|
|
18623
18870
|
}
|
|
18624
18871
|
}
|
|
@@ -18635,11 +18882,11 @@ function reapStalePartials(file2) {
|
|
|
18635
18882
|
}
|
|
18636
18883
|
for (const name of entries) {
|
|
18637
18884
|
if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
|
|
18638
|
-
const staging =
|
|
18885
|
+
const staging = join2(dir, name);
|
|
18639
18886
|
try {
|
|
18640
18887
|
const idle = idleMs(staging);
|
|
18641
18888
|
if (idle !== null && idle > STALE_PARTIAL_MS) {
|
|
18642
|
-
|
|
18889
|
+
rmSync3(staging, { recursive: true, force: true });
|
|
18643
18890
|
}
|
|
18644
18891
|
} catch {
|
|
18645
18892
|
}
|
|
@@ -18653,13 +18900,13 @@ function snapshotStore(db, backup) {
|
|
|
18653
18900
|
renameSync2(copy, backup);
|
|
18654
18901
|
} catch (error51) {
|
|
18655
18902
|
try {
|
|
18656
|
-
|
|
18903
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18657
18904
|
} catch {
|
|
18658
18905
|
}
|
|
18659
18906
|
throw error51;
|
|
18660
18907
|
}
|
|
18661
18908
|
try {
|
|
18662
|
-
|
|
18909
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18663
18910
|
} catch {
|
|
18664
18911
|
}
|
|
18665
18912
|
}
|
|
@@ -18674,7 +18921,7 @@ function moveStoreAside(file2, backup) {
|
|
|
18674
18921
|
renameSync2(sidecar, moved);
|
|
18675
18922
|
undo.push([moved, sidecar]);
|
|
18676
18923
|
} catch {
|
|
18677
|
-
|
|
18924
|
+
rmSync3(sidecar, { force: true });
|
|
18678
18925
|
}
|
|
18679
18926
|
}
|
|
18680
18927
|
} catch (error51) {
|
|
@@ -18690,14 +18937,14 @@ function moveStoreAside(file2, backup) {
|
|
|
18690
18937
|
}
|
|
18691
18938
|
function discardStore(file2, backup) {
|
|
18692
18939
|
try {
|
|
18693
|
-
|
|
18940
|
+
rmSync3(file2, { force: true });
|
|
18694
18941
|
for (const sidecar of dbSidecars(file2)) {
|
|
18695
|
-
|
|
18942
|
+
rmSync3(sidecar, { force: true });
|
|
18696
18943
|
}
|
|
18697
18944
|
} catch (error51) {
|
|
18698
18945
|
if (existsSync(file2)) {
|
|
18699
18946
|
try {
|
|
18700
|
-
|
|
18947
|
+
rmSync3(backup, { force: true });
|
|
18701
18948
|
} catch {
|
|
18702
18949
|
}
|
|
18703
18950
|
}
|
|
@@ -18929,10 +19176,31 @@ function applyMigrations(db, file2) {
|
|
|
18929
19176
|
if (drained) applyLegacyDropMigration(db, file2);
|
|
18930
19177
|
}
|
|
18931
19178
|
}
|
|
19179
|
+
function readLegacyTables(db) {
|
|
19180
|
+
let holdsRows = false;
|
|
19181
|
+
const marks = [];
|
|
19182
|
+
for (const table2 of ["events", "findings"]) {
|
|
19183
|
+
try {
|
|
19184
|
+
const row = db.prepare(`SELECT count(*) AS n, ifnull(max(rowid), -1) AS hi FROM ${table2}`).get();
|
|
19185
|
+
if (row === void 0) {
|
|
19186
|
+
holdsRows = true;
|
|
19187
|
+
marks.push(`${table2}:unreadable`);
|
|
19188
|
+
continue;
|
|
19189
|
+
}
|
|
19190
|
+
if (row.n > 0) holdsRows = true;
|
|
19191
|
+
marks.push(`${table2}:${String(row.n)}:${String(row.hi)}`);
|
|
19192
|
+
} catch {
|
|
19193
|
+
holdsRows = true;
|
|
19194
|
+
marks.push(`${table2}:unreadable`);
|
|
19195
|
+
}
|
|
19196
|
+
}
|
|
19197
|
+
return { holdsRows, mark: marks.join("|") };
|
|
19198
|
+
}
|
|
18932
19199
|
function applyLegacyDropMigration(db, file2) {
|
|
18933
19200
|
const migration = SQLITE_MIGRATIONS.find((m) => m.tag === LEGACY_DROP_MIGRATION_TAG);
|
|
18934
19201
|
if (!migration) return;
|
|
18935
|
-
|
|
19202
|
+
const before = file2 === void 0 ? void 0 : readLegacyTables(db);
|
|
19203
|
+
if (file2 !== void 0 && before?.holdsRows === true) {
|
|
18936
19204
|
try {
|
|
18937
19205
|
backupBeforeLegacyDrop(db, file2);
|
|
18938
19206
|
} catch (error51) {
|
|
@@ -18946,6 +19214,12 @@ function applyLegacyDropMigration(db, file2) {
|
|
|
18946
19214
|
() => {
|
|
18947
19215
|
const alreadyDropped = db.prepare("SELECT 1 FROM migration_ledger WHERE tag = ?").get(migration.tag);
|
|
18948
19216
|
if (alreadyDropped) return;
|
|
19217
|
+
if (before !== void 0 && readLegacyTables(db).mark !== before.mark) {
|
|
19218
|
+
akaWarn(
|
|
19219
|
+
"legacy events/findings rows changed after the pre-drop snapshot decision; deferring the drop so the next open can copy them first."
|
|
19220
|
+
);
|
|
19221
|
+
return;
|
|
19222
|
+
}
|
|
18949
19223
|
for (const statement of splitStatements(migration.sql)) {
|
|
18950
19224
|
db.exec(statement);
|
|
18951
19225
|
}
|
|
@@ -19300,8 +19574,8 @@ function safeJson(s, fallback) {
|
|
|
19300
19574
|
function parseJsonObject(s) {
|
|
19301
19575
|
if (s == null) return void 0;
|
|
19302
19576
|
try {
|
|
19303
|
-
const
|
|
19304
|
-
if (typeof
|
|
19577
|
+
const parsed2 = JSON.parse(s);
|
|
19578
|
+
if (typeof parsed2 === "object" && parsed2 !== null) return parsed2;
|
|
19305
19579
|
} catch {
|
|
19306
19580
|
}
|
|
19307
19581
|
return void 0;
|
|
@@ -19312,16 +19586,16 @@ function encodeKeysetCursor(payload) {
|
|
|
19312
19586
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
19313
19587
|
}
|
|
19314
19588
|
function decodeKeysetCursor(cursor) {
|
|
19315
|
-
const
|
|
19316
|
-
if (
|
|
19589
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
19590
|
+
if (parsed2 !== void 0 && "startedAtMs" in parsed2 && "id" in parsed2 && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
|
|
19317
19591
|
// resumes from is epoch millis, and a payload carrying ±Infinity or a
|
|
19318
19592
|
// fraction binds cleanly rather than failing — returning an EMPTY page with
|
|
19319
19593
|
// a null cursor, which a caller reads as "end of list". That is the one
|
|
19320
19594
|
// outcome a cursor that does not decode must never produce, since the
|
|
19321
19595
|
// documented behaviour above is to restart from the top. (`1e999` is valid
|
|
19322
19596
|
// JSON and parses to Infinity; a bare `NaN` is not, so it cannot arrive.)
|
|
19323
|
-
Number.isInteger(
|
|
19324
|
-
return
|
|
19597
|
+
Number.isInteger(parsed2.startedAtMs) && typeof parsed2.id === "string") {
|
|
19598
|
+
return parsed2;
|
|
19325
19599
|
}
|
|
19326
19600
|
return null;
|
|
19327
19601
|
}
|
|
@@ -19386,18 +19660,18 @@ var DB_EVENT_TYPE_TO_KIND = {
|
|
|
19386
19660
|
};
|
|
19387
19661
|
function safeParseStringArray(raw) {
|
|
19388
19662
|
if (!raw) return [];
|
|
19389
|
-
const
|
|
19390
|
-
return Array.isArray(
|
|
19663
|
+
const parsed2 = safeJson(raw, null);
|
|
19664
|
+
return Array.isArray(parsed2) ? parsed2 : [];
|
|
19391
19665
|
}
|
|
19392
19666
|
var DEFAULT_HARNESS = HARNESS.ClaudeCode;
|
|
19393
19667
|
function toHarness(raw) {
|
|
19394
|
-
const
|
|
19395
|
-
return
|
|
19668
|
+
const parsed2 = Harness.safeParse(raw);
|
|
19669
|
+
return parsed2.success ? parsed2.data : DEFAULT_HARNESS;
|
|
19396
19670
|
}
|
|
19397
19671
|
function resolveLifecycle(row, lastActivityMs, nowMs) {
|
|
19398
19672
|
if (row.status) {
|
|
19399
|
-
const
|
|
19400
|
-
if (
|
|
19673
|
+
const parsed2 = SessionStatus.safeParse(row.status);
|
|
19674
|
+
if (parsed2.success) return { status: parsed2.data, endedAtMs: row.ended_at };
|
|
19401
19675
|
}
|
|
19402
19676
|
if (row.ended_at !== null) return { status: "completed", endedAtMs: row.ended_at };
|
|
19403
19677
|
if (lastActivityMs >= nowMs - LIVE_ACTIVITY_WINDOW_MS) {
|
|
@@ -20356,9 +20630,9 @@ var SqliteDetectionsRepository = class {
|
|
|
20356
20630
|
const ruleIds = /* @__PURE__ */ new Set();
|
|
20357
20631
|
for (const r of rows) {
|
|
20358
20632
|
if (intToBool(r.enabled)) active += 1;
|
|
20359
|
-
const
|
|
20360
|
-
rules +=
|
|
20361
|
-
for (const rule of
|
|
20633
|
+
const parsed2 = parseRules(r.rulesJson);
|
|
20634
|
+
rules += parsed2.length;
|
|
20635
|
+
for (const rule of parsed2) {
|
|
20362
20636
|
if (typeof rule.id === "string") ruleIds.add(rule.id);
|
|
20363
20637
|
}
|
|
20364
20638
|
}
|
|
@@ -20892,12 +21166,12 @@ function encodeGroupCursor(group) {
|
|
|
20892
21166
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
20893
21167
|
}
|
|
20894
21168
|
function decodeGroupCursor(cursor) {
|
|
20895
|
-
const
|
|
20896
|
-
if (
|
|
21169
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
21170
|
+
if (parsed2 !== void 0 && typeof parsed2.sev === "string" && typeof parsed2.t === "string" && typeof parsed2.id === "string") {
|
|
20897
21171
|
return {
|
|
20898
|
-
severity:
|
|
20899
|
-
latestDetectedAt:
|
|
20900
|
-
id:
|
|
21172
|
+
severity: parsed2.sev,
|
|
21173
|
+
latestDetectedAt: parsed2.t,
|
|
21174
|
+
id: parsed2.id
|
|
20901
21175
|
};
|
|
20902
21176
|
}
|
|
20903
21177
|
return null;
|
|
@@ -22031,16 +22305,16 @@ var SqliteInstalledPacksRepository = class {
|
|
|
22031
22305
|
continue;
|
|
22032
22306
|
}
|
|
22033
22307
|
for (const entry of raw) {
|
|
22034
|
-
const
|
|
22035
|
-
if (
|
|
22036
|
-
out.rules.push(
|
|
22037
|
-
out.ruleActions.set(
|
|
22038
|
-
out.ruleVersions.set(
|
|
22039
|
-
if (reversible) out.reversibleRules.add(
|
|
22040
|
-
else out.reversibleRules.delete(
|
|
22308
|
+
const parsed2 = Rule.safeParse(entry);
|
|
22309
|
+
if (parsed2.success) {
|
|
22310
|
+
out.rules.push(parsed2.data);
|
|
22311
|
+
out.ruleActions.set(parsed2.data.id, action);
|
|
22312
|
+
out.ruleVersions.set(parsed2.data.id, row.version);
|
|
22313
|
+
if (reversible) out.reversibleRules.add(parsed2.data.id);
|
|
22314
|
+
else out.reversibleRules.delete(parsed2.data.id);
|
|
22041
22315
|
} else {
|
|
22042
22316
|
out.invalidRules += 1;
|
|
22043
|
-
reject(pack, printableRuleId(entry), firstIssueReason(
|
|
22317
|
+
reject(pack, printableRuleId(entry), firstIssueReason(parsed2.error));
|
|
22044
22318
|
}
|
|
22045
22319
|
}
|
|
22046
22320
|
}
|
|
@@ -23430,15 +23704,15 @@ function encodeReuseCursor(payload) {
|
|
|
23430
23704
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
23431
23705
|
}
|
|
23432
23706
|
function decodeReuseCursor(cursor) {
|
|
23433
|
-
const
|
|
23434
|
-
if (
|
|
23707
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
23708
|
+
if (parsed2 !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
|
|
23435
23709
|
// ±Infinity or a fraction binds cleanly and returns an EMPTY page with a
|
|
23436
23710
|
// null cursor, which the caller reads as "end of list" — the one outcome a
|
|
23437
23711
|
// malformed cursor must never produce, since restarting from the top is the
|
|
23438
23712
|
// documented behaviour and the only recoverable one. (`1e999` is valid JSON
|
|
23439
23713
|
// and parses to Infinity; a bare `NaN` is not, so it cannot arrive here.)
|
|
23440
|
-
Number.isInteger(
|
|
23441
|
-
return { occurrences:
|
|
23714
|
+
Number.isInteger(parsed2.occurrences) && typeof parsed2.pointerId === "string") {
|
|
23715
|
+
return { occurrences: parsed2.occurrences, pointerId: parsed2.pointerId };
|
|
23442
23716
|
}
|
|
23443
23717
|
return null;
|
|
23444
23718
|
}
|
|
@@ -25167,7 +25441,7 @@ function openAndInitialize(file2) {
|
|
|
25167
25441
|
}
|
|
25168
25442
|
function openLocalDatabase(dir) {
|
|
25169
25443
|
ensureDataDirSync(dir);
|
|
25170
|
-
const file2 =
|
|
25444
|
+
const file2 = join3(dir, DB_FILENAME);
|
|
25171
25445
|
reapStalePartials(file2);
|
|
25172
25446
|
const {
|
|
25173
25447
|
db,
|
|
@@ -25403,9 +25677,9 @@ import {
|
|
|
25403
25677
|
closeSync,
|
|
25404
25678
|
existsSync as existsSync2,
|
|
25405
25679
|
openSync,
|
|
25406
|
-
readFileSync,
|
|
25407
|
-
rmSync as
|
|
25408
|
-
statSync as
|
|
25680
|
+
readFileSync as readFileSync2,
|
|
25681
|
+
rmSync as rmSync4,
|
|
25682
|
+
statSync as statSync3,
|
|
25409
25683
|
writeFileSync as writeFileSync2
|
|
25410
25684
|
} from "fs";
|
|
25411
25685
|
import { hostname as hostname3 } from "os";
|
|
@@ -25416,20 +25690,20 @@ import { createHash as createHash3 } from "crypto";
|
|
|
25416
25690
|
|
|
25417
25691
|
// ../../packages/persistence/src/fingerprint.ts
|
|
25418
25692
|
import { createHmac, randomBytes } from "crypto";
|
|
25419
|
-
import { existsSync as existsSync3, readFileSync as
|
|
25420
|
-
import { join as
|
|
25693
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
25694
|
+
import { join as join4 } from "path";
|
|
25421
25695
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
25422
25696
|
var EXCEPTION_KEY_FILENAME = "exception.key";
|
|
25423
25697
|
var KEY_MATERIAL_BYTES = 32;
|
|
25424
25698
|
function keyFilePath(dataDir2) {
|
|
25425
|
-
return
|
|
25699
|
+
return join4(dataDir2, EXCEPTION_KEY_FILENAME);
|
|
25426
25700
|
}
|
|
25427
25701
|
function parseKeyFile(raw) {
|
|
25428
|
-
const
|
|
25429
|
-
if (typeof
|
|
25702
|
+
const parsed2 = JSON.parse(raw);
|
|
25703
|
+
if (typeof parsed2 !== "object" || parsed2 === null) {
|
|
25430
25704
|
throw new Error("exception key file is corrupt: not a JSON object");
|
|
25431
25705
|
}
|
|
25432
|
-
const { version: version2, material } =
|
|
25706
|
+
const { version: version2, material } = parsed2;
|
|
25433
25707
|
if (typeof version2 !== "number" || !Number.isInteger(version2) || version2 < 1) {
|
|
25434
25708
|
throw new Error("exception key file is corrupt: bad version");
|
|
25435
25709
|
}
|
|
@@ -25445,7 +25719,7 @@ function parseKeyFile(raw) {
|
|
|
25445
25719
|
function readFingerprintKey(dataDir2) {
|
|
25446
25720
|
let raw;
|
|
25447
25721
|
try {
|
|
25448
|
-
raw =
|
|
25722
|
+
raw = readFileSync3(keyFilePath(dataDir2), "utf8");
|
|
25449
25723
|
} catch (err) {
|
|
25450
25724
|
if (err.code === "ENOENT") return null;
|
|
25451
25725
|
throw err instanceof Error ? err : new Error(String(err));
|
|
@@ -25457,18 +25731,22 @@ function readFingerprintKey(dataDir2) {
|
|
|
25457
25731
|
import { renameSync as renameSync3 } from "fs";
|
|
25458
25732
|
import { mkdir } from "fs/promises";
|
|
25459
25733
|
import { homedir } from "os";
|
|
25460
|
-
import { join as
|
|
25734
|
+
import { join as join5 } from "path";
|
|
25461
25735
|
function defaultDataDir() {
|
|
25462
|
-
return
|
|
25736
|
+
return join5(homedir(), ".aka");
|
|
25463
25737
|
}
|
|
25464
25738
|
function settingsDir(base = defaultDataDir()) {
|
|
25465
|
-
return
|
|
25739
|
+
return join5(base, "settings");
|
|
25466
25740
|
}
|
|
25467
25741
|
function dataDir(base = defaultDataDir()) {
|
|
25468
|
-
return
|
|
25742
|
+
return join5(base, "data");
|
|
25469
25743
|
}
|
|
25470
25744
|
function dbPath(base = defaultDataDir()) {
|
|
25471
|
-
return
|
|
25745
|
+
return join5(dataDir(base), "aka.db");
|
|
25746
|
+
}
|
|
25747
|
+
async function ensureDataDir(dir = defaultDataDir()) {
|
|
25748
|
+
await mkdir(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
25749
|
+
tightenDir(dir);
|
|
25472
25750
|
}
|
|
25473
25751
|
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
25474
25752
|
ensureDataDirSync(dir);
|
|
@@ -25481,8 +25759,8 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25481
25759
|
for (const { name, dest } of moves) {
|
|
25482
25760
|
try {
|
|
25483
25761
|
ensureDataDirSync(dest);
|
|
25484
|
-
const moved =
|
|
25485
|
-
renameSync3(
|
|
25762
|
+
const moved = join5(dest, name);
|
|
25763
|
+
renameSync3(join5(base, name), moved);
|
|
25486
25764
|
tightenFile(moved);
|
|
25487
25765
|
} catch {
|
|
25488
25766
|
}
|
|
@@ -25490,7 +25768,7 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25490
25768
|
}
|
|
25491
25769
|
|
|
25492
25770
|
// ../../packages/persistence/src/managed-settings.ts
|
|
25493
|
-
import { readFileSync as
|
|
25771
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
25494
25772
|
import { posix, win32 } from "path";
|
|
25495
25773
|
function managedSettingsPaths(platform2 = process.platform) {
|
|
25496
25774
|
if (platform2 === "darwin") {
|
|
@@ -25508,14 +25786,14 @@ function readManagedSettings(paths = managedSettingsPaths()) {
|
|
|
25508
25786
|
for (const path of paths) {
|
|
25509
25787
|
let text;
|
|
25510
25788
|
try {
|
|
25511
|
-
text =
|
|
25789
|
+
text = readFileSync4(path, "utf8");
|
|
25512
25790
|
} catch {
|
|
25513
25791
|
continue;
|
|
25514
25792
|
}
|
|
25515
25793
|
const record2 = parseJsonObject(text);
|
|
25516
25794
|
if (!record2) continue;
|
|
25517
|
-
const
|
|
25518
|
-
if (
|
|
25795
|
+
const parsed2 = ManagedSettings.safeParse(record2);
|
|
25796
|
+
if (parsed2.success) return parsed2.data;
|
|
25519
25797
|
}
|
|
25520
25798
|
return null;
|
|
25521
25799
|
}
|
|
@@ -25555,14 +25833,14 @@ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ n
|
|
|
25555
25833
|
}
|
|
25556
25834
|
|
|
25557
25835
|
// ../../packages/persistence/src/settings.ts
|
|
25558
|
-
import { readFileSync as
|
|
25559
|
-
import { join as
|
|
25836
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
25837
|
+
import { join as join6 } from "path";
|
|
25560
25838
|
var SETTINGS_FILENAME = "settings.json";
|
|
25561
25839
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25562
25840
|
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
25563
25841
|
}
|
|
25564
25842
|
function readUserSettings(base) {
|
|
25565
|
-
const record2 = readJson(
|
|
25843
|
+
const record2 = readJson(join6(settingsDir(base), SETTINGS_FILENAME));
|
|
25566
25844
|
if (!record2) return defaultWorkspaceSettings();
|
|
25567
25845
|
try {
|
|
25568
25846
|
return WorkspaceSettings.parse(record2);
|
|
@@ -25573,13 +25851,17 @@ function readUserSettings(base) {
|
|
|
25573
25851
|
function readJson(file2) {
|
|
25574
25852
|
let text;
|
|
25575
25853
|
try {
|
|
25576
|
-
text =
|
|
25854
|
+
text = readFileSync5(file2, "utf8");
|
|
25577
25855
|
} catch {
|
|
25578
25856
|
return null;
|
|
25579
25857
|
}
|
|
25580
25858
|
return parseJsonObject(text) ?? null;
|
|
25581
25859
|
}
|
|
25582
25860
|
|
|
25861
|
+
// ../../packages/persistence/src/store-symlinks.ts
|
|
25862
|
+
import { existsSync as existsSync4, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
|
|
25863
|
+
import { dirname as dirname2, join as join7, resolve } from "path";
|
|
25864
|
+
|
|
25583
25865
|
// ../../packages/persistence/src/vault/crypto.ts
|
|
25584
25866
|
import {
|
|
25585
25867
|
createCipheriv,
|
|
@@ -25592,29 +25874,92 @@ import {
|
|
|
25592
25874
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
25593
25875
|
import { execFileSync } from "child_process";
|
|
25594
25876
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
25595
|
-
import { chmodSync as
|
|
25596
|
-
import { join as
|
|
25877
|
+
import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
25878
|
+
import { join as join8 } from "path";
|
|
25597
25879
|
|
|
25598
25880
|
// ../../packages/persistence/src/vault/vault.ts
|
|
25599
25881
|
import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
|
|
25600
25882
|
|
|
25601
25883
|
// ../../packages/persistence/src/warn-era-cap.ts
|
|
25602
|
-
import { existsSync as
|
|
25603
|
-
import { join as
|
|
25884
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
25885
|
+
import { join as join9 } from "path";
|
|
25604
25886
|
var MARKER = "warn-era-capped";
|
|
25605
25887
|
function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
|
|
25606
25888
|
if (policyMode !== "warn") return { capped: 0, skipped: "not-warn" };
|
|
25607
|
-
const marker =
|
|
25608
|
-
if (
|
|
25889
|
+
const marker = join9(dataDir2, MARKER);
|
|
25890
|
+
if (existsSync5(marker)) return { capped: 0, skipped: "already-run" };
|
|
25609
25891
|
const capped = db.policies.capCategoryActions();
|
|
25610
25892
|
writeFileSync4(marker, `${new Date(Date.now()).toISOString()}
|
|
25611
25893
|
`, { mode: DATA_FILE_MODE });
|
|
25612
25894
|
return { capped };
|
|
25613
25895
|
}
|
|
25614
25896
|
|
|
25897
|
+
// ../../packages/plugin-runtime/src/attached/failure.ts
|
|
25898
|
+
function statusOf(err) {
|
|
25899
|
+
if (typeof err !== "object" || err === null || !("status" in err)) return null;
|
|
25900
|
+
const { status } = err;
|
|
25901
|
+
if (typeof status !== "number" || !Number.isInteger(status)) return null;
|
|
25902
|
+
return status >= 100 && status <= 599 ? status : null;
|
|
25903
|
+
}
|
|
25904
|
+
function classifyFailure(err) {
|
|
25905
|
+
switch (statusOf(err)) {
|
|
25906
|
+
case 401:
|
|
25907
|
+
return "unauthorized";
|
|
25908
|
+
case 403:
|
|
25909
|
+
return "forbidden";
|
|
25910
|
+
default:
|
|
25911
|
+
return "unreachable";
|
|
25912
|
+
}
|
|
25913
|
+
}
|
|
25914
|
+
|
|
25915
|
+
// ../../packages/plugin-runtime/src/attached/forward-drops.ts
|
|
25916
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
25917
|
+
import { join as join10 } from "path";
|
|
25918
|
+
var FORWARD_DROPS_FILENAME = "attached-forward-drops.json";
|
|
25919
|
+
function forwardDropsPath(dataDir2) {
|
|
25920
|
+
return join10(dataDir2, FORWARD_DROPS_FILENAME);
|
|
25921
|
+
}
|
|
25922
|
+
function recordForwardDrops(dataDir2, count, nowMs) {
|
|
25923
|
+
if (count <= 0) return;
|
|
25924
|
+
try {
|
|
25925
|
+
ensureDataDirSync(dataDir2);
|
|
25926
|
+
const previous = readForwardDrops(dataDir2);
|
|
25927
|
+
const next = {
|
|
25928
|
+
droppedForwards: (previous?.droppedForwards ?? 0) + count,
|
|
25929
|
+
lastDropAtMs: nowMs
|
|
25930
|
+
};
|
|
25931
|
+
writeOwnerOnlyFileSync(forwardDropsPath(dataDir2), `${JSON.stringify(next)}
|
|
25932
|
+
`);
|
|
25933
|
+
} catch {
|
|
25934
|
+
}
|
|
25935
|
+
}
|
|
25936
|
+
function readForwardDrops(dataDir2) {
|
|
25937
|
+
try {
|
|
25938
|
+
const parsed2 = JSON.parse(readFileSync7(forwardDropsPath(dataDir2), "utf8"));
|
|
25939
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
25940
|
+
const record2 = parsed2;
|
|
25941
|
+
if (typeof record2.droppedForwards !== "number" || !Number.isFinite(record2.droppedForwards)) {
|
|
25942
|
+
return null;
|
|
25943
|
+
}
|
|
25944
|
+
if (record2.droppedForwards <= 0) return null;
|
|
25945
|
+
if (typeof record2.lastDropAtMs !== "number" || !Number.isFinite(record2.lastDropAtMs)) {
|
|
25946
|
+
return null;
|
|
25947
|
+
}
|
|
25948
|
+
return { droppedForwards: record2.droppedForwards, lastDropAtMs: record2.lastDropAtMs };
|
|
25949
|
+
} catch {
|
|
25950
|
+
return null;
|
|
25951
|
+
}
|
|
25952
|
+
}
|
|
25953
|
+
|
|
25954
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
25955
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
25956
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
25957
|
+
import { readFile, rename, writeFile } from "fs/promises";
|
|
25958
|
+
import { join as join18 } from "path";
|
|
25959
|
+
|
|
25615
25960
|
// ../../packages/plugin-sdk/src/config.ts
|
|
25616
|
-
import { existsSync as
|
|
25617
|
-
import { join as
|
|
25961
|
+
import { existsSync as existsSync6 } from "fs";
|
|
25962
|
+
import { join as join11 } from "path";
|
|
25618
25963
|
|
|
25619
25964
|
// ../../packages/plugin-sdk/src/provider-env.ts
|
|
25620
25965
|
var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
|
|
@@ -25650,8 +25995,8 @@ function hostOf(url2) {
|
|
|
25650
25995
|
}
|
|
25651
25996
|
}
|
|
25652
25997
|
function resolveProvider() {
|
|
25653
|
-
const
|
|
25654
|
-
const env =
|
|
25998
|
+
const parsed2 = ProviderEnvSchema.safeParse(process.env);
|
|
25999
|
+
const env = parsed2.success ? parsed2.data : ProviderEnvSchema.parse({});
|
|
25655
26000
|
if (env.CLAUDE_CODE_USE_BEDROCK === true) return { provider: "bedrock" };
|
|
25656
26001
|
if (env.CLAUDE_CODE_USE_VERTEX === true) return { provider: "vertex" };
|
|
25657
26002
|
const baseUrl = env.ANTHROPIC_BASE_URL;
|
|
@@ -25668,8 +26013,8 @@ function resolveProvider() {
|
|
|
25668
26013
|
function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
|
|
25669
26014
|
try {
|
|
25670
26015
|
ensureLayoutDirSync(base);
|
|
25671
|
-
const settingsFile =
|
|
25672
|
-
if (
|
|
26016
|
+
const settingsFile = join11(settingsDir(base), "settings.json");
|
|
26017
|
+
if (existsSync6(settingsFile)) tightenFile(settingsFile);
|
|
25673
26018
|
} catch {
|
|
25674
26019
|
}
|
|
25675
26020
|
migrateLegacyLayout(base);
|
|
@@ -25692,9 +26037,9 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
25692
26037
|
}
|
|
25693
26038
|
|
|
25694
26039
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
25695
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
26040
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
|
|
25696
26041
|
import { homedir as homedir2 } from "os";
|
|
25697
|
-
import { basename as basename3, join as
|
|
26042
|
+
import { basename as basename3, join as join13 } from "path";
|
|
25698
26043
|
|
|
25699
26044
|
// ../../packages/detections/src/egress/registry.ts
|
|
25700
26045
|
var EXTRACTOR_VERSION = "1";
|
|
@@ -27172,10 +27517,10 @@ var localhost_ref_default = {
|
|
|
27172
27517
|
severity: "low",
|
|
27173
27518
|
matcher: {
|
|
27174
27519
|
type: "regex",
|
|
27175
|
-
pattern: "
|
|
27520
|
+
pattern: "(?<![A-Za-z0-9_])(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|(?<![A-Za-z0-9_]\\[)::1(?!:|\\.[0-9]))(?![A-Za-z0-9_])",
|
|
27176
27521
|
flags: "g"
|
|
27177
27522
|
},
|
|
27178
|
-
examples: ["localhost", "127.0.0.1"]
|
|
27523
|
+
examples: ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
|
|
27179
27524
|
};
|
|
27180
27525
|
|
|
27181
27526
|
// ../../rules/core-code-context/stack-trace.json
|
|
@@ -28477,36 +28822,36 @@ function bundledDetections() {
|
|
|
28477
28822
|
}
|
|
28478
28823
|
|
|
28479
28824
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
28480
|
-
import { existsSync as
|
|
28481
|
-
import { basename as basename2, dirname as
|
|
28825
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
28826
|
+
import { basename as basename2, dirname as dirname3, isAbsolute, join as join12, sep as sep2 } from "path";
|
|
28482
28827
|
|
|
28483
28828
|
// ../../packages/plugin-sdk/src/events.ts
|
|
28484
28829
|
import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
|
|
28485
28830
|
|
|
28486
28831
|
// ../../packages/plugin-sdk/src/isolated-scan.ts
|
|
28487
|
-
import { existsSync as
|
|
28832
|
+
import { existsSync as existsSync8 } from "fs";
|
|
28488
28833
|
import { fileURLToPath } from "url";
|
|
28489
28834
|
import { Worker } from "worker_threads";
|
|
28490
28835
|
|
|
28491
28836
|
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
28492
28837
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
28493
|
-
import { readFileSync as
|
|
28494
|
-
import { join as
|
|
28838
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
28839
|
+
import { join as join14 } from "path";
|
|
28495
28840
|
|
|
28496
28841
|
// ../../packages/plugin-sdk/src/inventory-resolver.ts
|
|
28497
28842
|
import { arch, hostname as hostname4, platform, release } from "os";
|
|
28498
28843
|
|
|
28499
28844
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
28500
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
28501
|
-
import { join as
|
|
28845
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
28846
|
+
import { join as join15 } from "path";
|
|
28502
28847
|
|
|
28503
28848
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
28504
|
-
import { readdirSync as readdirSync3, realpathSync as
|
|
28505
|
-
import { basename as basename4, dirname as
|
|
28849
|
+
import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
|
|
28850
|
+
import { basename as basename4, dirname as dirname4, sep as sep3 } from "path";
|
|
28506
28851
|
|
|
28507
28852
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
28508
|
-
import { existsSync as
|
|
28509
|
-
import { basename as basename5, join as
|
|
28853
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
|
|
28854
|
+
import { basename as basename5, join as join16 } from "path";
|
|
28510
28855
|
|
|
28511
28856
|
// ../../packages/plugin-sdk/src/provider-env-antigravity.ts
|
|
28512
28857
|
var optionalBaseUrl2 = external_exports.preprocess((v) => {
|
|
@@ -28541,41 +28886,1168 @@ import { randomUUID as randomUUID14 } from "crypto";
|
|
|
28541
28886
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
28542
28887
|
|
|
28543
28888
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
28544
|
-
import { mkdirSync as mkdirSync3, statSync as
|
|
28545
|
-
import { join as
|
|
28546
|
-
|
|
28547
|
-
// ../../packages/plugin-runtime/src/
|
|
28548
|
-
|
|
28549
|
-
|
|
28550
|
-
|
|
28551
|
-
|
|
28889
|
+
import { mkdirSync as mkdirSync3, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
28890
|
+
import { join as join17 } from "path";
|
|
28891
|
+
|
|
28892
|
+
// ../../packages/plugin-runtime/src/attached/with-timeout.ts
|
|
28893
|
+
var REQUEST_TIMEOUT_MS = 2e3;
|
|
28894
|
+
function withTimeout(promise2, ms) {
|
|
28895
|
+
let timer;
|
|
28896
|
+
const timeout = new Promise((_, reject) => {
|
|
28897
|
+
timer = setTimeout(() => {
|
|
28898
|
+
reject(new Error("attached gateway request timed out"));
|
|
28899
|
+
}, ms);
|
|
28900
|
+
});
|
|
28901
|
+
promise2.catch(() => void 0);
|
|
28902
|
+
return Promise.race([promise2, timeout]).finally(() => {
|
|
28903
|
+
clearTimeout(timer);
|
|
28904
|
+
});
|
|
28905
|
+
}
|
|
28552
28906
|
|
|
28553
|
-
// ../../packages/plugin-runtime/src/
|
|
28554
|
-
|
|
28555
|
-
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
|
|
28559
|
-
|
|
28560
|
-
|
|
28561
|
-
|
|
28562
|
-
|
|
28563
|
-
|
|
28907
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
28908
|
+
function isInvalidRequest(err) {
|
|
28909
|
+
return typeof err === "object" && err !== null && err.name === "RemoteRequestInvalid";
|
|
28910
|
+
}
|
|
28911
|
+
var FORWARD_BUDGET_MS = 1500;
|
|
28912
|
+
var DECISION_PATH_BUDGET_MS = 800;
|
|
28913
|
+
var BREAKER_FAILURE_THRESHOLD = 3;
|
|
28914
|
+
var BREAKER_COOLDOWN_MS = 3e4;
|
|
28915
|
+
var CLOSED = { consecutiveFailures: 0, openedAtMs: null, lastFailure: null };
|
|
28916
|
+
var FAILURES = /* @__PURE__ */ new Set([
|
|
28917
|
+
"unauthorized",
|
|
28918
|
+
"forbidden",
|
|
28919
|
+
"unreachable"
|
|
28920
|
+
]);
|
|
28921
|
+
var FORWARD_STATE_FILENAME = "attached-state.json";
|
|
28922
|
+
var STATE_FILENAME = FORWARD_STATE_FILENAME;
|
|
28923
|
+
function parseBreakerState(raw, nowMs) {
|
|
28924
|
+
try {
|
|
28925
|
+
const parsed2 = JSON.parse(raw);
|
|
28926
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
28927
|
+
const record2 = parsed2;
|
|
28928
|
+
const failures = typeof record2.consecutiveFailures === "number" && record2.consecutiveFailures >= 0 ? record2.consecutiveFailures : 0;
|
|
28929
|
+
const openedAtMs = typeof record2.openedAtMs === "number" && Number.isFinite(record2.openedAtMs) && record2.openedAtMs <= nowMs ? record2.openedAtMs : null;
|
|
28930
|
+
const lastFailure = typeof record2.lastFailure === "string" && FAILURES.has(record2.lastFailure) ? record2.lastFailure : null;
|
|
28931
|
+
return { consecutiveFailures: failures, openedAtMs, lastFailure };
|
|
28932
|
+
} catch {
|
|
28933
|
+
return null;
|
|
28564
28934
|
}
|
|
28565
|
-
|
|
28566
|
-
|
|
28567
|
-
|
|
28935
|
+
}
|
|
28936
|
+
function createForwardPolicy(deps) {
|
|
28937
|
+
const now = deps.now ?? (() => Date.now());
|
|
28938
|
+
const file2 = join18(deps.dir, STATE_FILENAME);
|
|
28939
|
+
let state = null;
|
|
28940
|
+
let loading = null;
|
|
28941
|
+
async function readState() {
|
|
28942
|
+
let raw;
|
|
28943
|
+
try {
|
|
28944
|
+
raw = await readFile(file2, "utf8");
|
|
28945
|
+
} catch {
|
|
28946
|
+
return { ...CLOSED };
|
|
28947
|
+
}
|
|
28948
|
+
return parseBreakerState(raw, now()) ?? { ...CLOSED };
|
|
28568
28949
|
}
|
|
28569
|
-
|
|
28570
|
-
|
|
28950
|
+
async function load() {
|
|
28951
|
+
if (state !== null) return state;
|
|
28952
|
+
loading ??= readState().then((loaded) => {
|
|
28953
|
+
state = loaded;
|
|
28954
|
+
loading = null;
|
|
28955
|
+
return loaded;
|
|
28956
|
+
});
|
|
28957
|
+
return loading;
|
|
28571
28958
|
}
|
|
28572
|
-
|
|
28573
|
-
|
|
28574
|
-
|
|
28959
|
+
async function persist(next) {
|
|
28960
|
+
state = next;
|
|
28961
|
+
try {
|
|
28962
|
+
await ensureDataDir(deps.dir);
|
|
28963
|
+
const tmp = `${file2}.${randomUUID15()}.tmp`;
|
|
28964
|
+
await writeFile(tmp, JSON.stringify(next), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
28965
|
+
await rename(tmp, file2);
|
|
28966
|
+
} catch {
|
|
28967
|
+
}
|
|
28575
28968
|
}
|
|
28576
|
-
|
|
28577
|
-
|
|
28578
|
-
|
|
28969
|
+
return {
|
|
28970
|
+
async run(op, opts) {
|
|
28971
|
+
const budget = opts?.decisionPath === true ? DECISION_PATH_BUDGET_MS : FORWARD_BUDGET_MS;
|
|
28972
|
+
let current;
|
|
28973
|
+
try {
|
|
28974
|
+
current = await load();
|
|
28975
|
+
} catch {
|
|
28976
|
+
current = { ...CLOSED };
|
|
28977
|
+
}
|
|
28978
|
+
const at = now();
|
|
28979
|
+
if (current.openedAtMs !== null) {
|
|
28980
|
+
if (at - current.openedAtMs < BREAKER_COOLDOWN_MS) {
|
|
28981
|
+
return { ok: false, reason: "breaker-open" };
|
|
28982
|
+
}
|
|
28983
|
+
await persist({
|
|
28984
|
+
consecutiveFailures: current.consecutiveFailures,
|
|
28985
|
+
openedAtMs: at,
|
|
28986
|
+
lastFailure: current.lastFailure
|
|
28987
|
+
});
|
|
28988
|
+
}
|
|
28989
|
+
try {
|
|
28990
|
+
const value = await withTimeout(op(), budget);
|
|
28991
|
+
if (current.openedAtMs !== null || current.consecutiveFailures > 0) {
|
|
28992
|
+
await persist({ ...CLOSED });
|
|
28993
|
+
}
|
|
28994
|
+
return { ok: true, value };
|
|
28995
|
+
} catch (err) {
|
|
28996
|
+
if (isInvalidRequest(err)) return { ok: false, reason: "invalid-request" };
|
|
28997
|
+
const reason = classifyFailure(err);
|
|
28998
|
+
const failures = current.consecutiveFailures + 1;
|
|
28999
|
+
const shouldOpen = current.openedAtMs !== null || failures >= BREAKER_FAILURE_THRESHOLD;
|
|
29000
|
+
await persist({
|
|
29001
|
+
consecutiveFailures: failures,
|
|
29002
|
+
openedAtMs: shouldOpen ? now() : null,
|
|
29003
|
+
lastFailure: reason
|
|
29004
|
+
});
|
|
29005
|
+
return { ok: false, reason };
|
|
29006
|
+
}
|
|
29007
|
+
}
|
|
29008
|
+
};
|
|
29009
|
+
}
|
|
29010
|
+
|
|
29011
|
+
// ../../packages/plugin-runtime/src/attached/gateway.ts
|
|
29012
|
+
var ACTION_STRENGTH = {
|
|
29013
|
+
allow: 0,
|
|
29014
|
+
log: 1,
|
|
29015
|
+
warn: 2,
|
|
29016
|
+
redact: 3,
|
|
29017
|
+
block: 4
|
|
29018
|
+
};
|
|
29019
|
+
function ruleCategoryMap(wireRules, localRules) {
|
|
29020
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
29021
|
+
for (const rule of wireRules ?? []) map2.set(rule.id, rule.category);
|
|
29022
|
+
for (const rule of localRules ?? []) map2.set(rule.id, rule.category);
|
|
29023
|
+
for (const pack of bundledDetections()) {
|
|
29024
|
+
for (const rule of pack.rules) map2.set(rule.id, rule.category);
|
|
29025
|
+
}
|
|
29026
|
+
return map2;
|
|
29027
|
+
}
|
|
29028
|
+
function strongerOf(a, b) {
|
|
29029
|
+
if (a === null) return b;
|
|
29030
|
+
if (b === null) return a;
|
|
29031
|
+
return ACTION_STRENGTH[a] >= ACTION_STRENGTH[b] ? a : b;
|
|
29032
|
+
}
|
|
29033
|
+
function policyKey(policy) {
|
|
29034
|
+
return "ruleId" in policy.target ? `rule:${policy.target.ruleId}` : `category:${policy.target.category}`;
|
|
29035
|
+
}
|
|
29036
|
+
function floorFor(policy, categoryByRuleId) {
|
|
29037
|
+
const category = "category" in policy.target ? policy.target.category : categoryByRuleId.get(policy.target.ruleId);
|
|
29038
|
+
return category === void 0 ? null : DEFAULT_ACTIONS[category];
|
|
29039
|
+
}
|
|
29040
|
+
function mergeRaiseOnly(localPolicies, remotePolicies, categoryByRuleId) {
|
|
29041
|
+
const merged = /* @__PURE__ */ new Map();
|
|
29042
|
+
const disabled = [];
|
|
29043
|
+
const remoteCategoryAction = /* @__PURE__ */ new Map();
|
|
29044
|
+
for (const policy of remotePolicies) {
|
|
29045
|
+
if (!policy.enabled) continue;
|
|
29046
|
+
if (!("category" in policy.target)) continue;
|
|
29047
|
+
if (remoteCategoryAction.has(policy.target.category)) continue;
|
|
29048
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
29049
|
+
remoteCategoryAction.set(
|
|
29050
|
+
policy.target.category,
|
|
29051
|
+
floor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[floor] ? floor : policy.action
|
|
29052
|
+
);
|
|
29053
|
+
}
|
|
29054
|
+
for (const policy of localPolicies) {
|
|
29055
|
+
if (!policy.enabled) {
|
|
29056
|
+
disabled.push(policy);
|
|
29057
|
+
continue;
|
|
29058
|
+
}
|
|
29059
|
+
const key = policyKey(policy);
|
|
29060
|
+
if (merged.has(key)) continue;
|
|
29061
|
+
let remoteFloor = null;
|
|
29062
|
+
if ("ruleId" in policy.target) {
|
|
29063
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
29064
|
+
if (category !== void 0) remoteFloor = remoteCategoryAction.get(category) ?? null;
|
|
29065
|
+
}
|
|
29066
|
+
merged.set(
|
|
29067
|
+
key,
|
|
29068
|
+
remoteFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[remoteFloor] ? { ...policy, action: remoteFloor } : policy
|
|
29069
|
+
);
|
|
29070
|
+
}
|
|
29071
|
+
const localCategoryAction = /* @__PURE__ */ new Map();
|
|
29072
|
+
for (const policy of merged.values()) {
|
|
29073
|
+
if ("category" in policy.target) localCategoryAction.set(policy.target.category, policy.action);
|
|
29074
|
+
}
|
|
29075
|
+
for (const policy of remotePolicies) {
|
|
29076
|
+
if (!policy.enabled) {
|
|
29077
|
+
disabled.push(policy);
|
|
29078
|
+
continue;
|
|
29079
|
+
}
|
|
29080
|
+
const key = policyKey(policy);
|
|
29081
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
29082
|
+
let localFloor = null;
|
|
29083
|
+
if ("ruleId" in policy.target) {
|
|
29084
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
29085
|
+
if (category !== void 0) localFloor = localCategoryAction.get(category) ?? null;
|
|
29086
|
+
}
|
|
29087
|
+
const effectiveFloor = strongerOf(floor, localFloor);
|
|
29088
|
+
const clamped = effectiveFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[effectiveFloor] ? { ...policy, action: effectiveFloor } : policy;
|
|
29089
|
+
const existing = merged.get(key);
|
|
29090
|
+
if (existing === void 0) {
|
|
29091
|
+
merged.set(key, clamped);
|
|
29092
|
+
continue;
|
|
29093
|
+
}
|
|
29094
|
+
if (ACTION_STRENGTH[clamped.action] > ACTION_STRENGTH[existing.action]) {
|
|
29095
|
+
merged.set(key, clamped);
|
|
29096
|
+
}
|
|
29097
|
+
}
|
|
29098
|
+
return [...merged.values(), ...disabled];
|
|
29099
|
+
}
|
|
29100
|
+
var AttachedDataGateway = class {
|
|
29101
|
+
constructor(deps) {
|
|
29102
|
+
this.deps = deps;
|
|
29103
|
+
}
|
|
29104
|
+
deps;
|
|
29105
|
+
/**
|
|
29106
|
+
* The control plane's OWN resolution of this session's inventory, captured by
|
|
29107
|
+
* ensureInventory. Null until the first successful forward — and it stays
|
|
29108
|
+
* null for the whole session when the control plane is unreachable, which is fine:
|
|
29109
|
+
* reKeyForForward then leaves the event's ids alone and the control plane resolves
|
|
29110
|
+
* what it can from the descriptors it already has.
|
|
29111
|
+
*/
|
|
29112
|
+
remoteInventory = null;
|
|
29113
|
+
// ---------------------------------------------------------------------
|
|
29114
|
+
// Writes: local first, then forward.
|
|
29115
|
+
// ---------------------------------------------------------------------
|
|
29116
|
+
async recordCapture(record2) {
|
|
29117
|
+
await this.deps.local.recordCapture(record2);
|
|
29118
|
+
await this.deps.forward.run(
|
|
29119
|
+
() => this.deps.client.ingestEvents({
|
|
29120
|
+
events: [record2.event],
|
|
29121
|
+
...record2.dedupe ? { dedupe: record2.dedupe } : {}
|
|
29122
|
+
}),
|
|
29123
|
+
{ decisionPath: true }
|
|
29124
|
+
);
|
|
29125
|
+
}
|
|
29126
|
+
async ensureInventory(ctx) {
|
|
29127
|
+
const resolved = await this.deps.local.ensureInventory(ctx);
|
|
29128
|
+
const remote = await this.deps.forward.run(() => this.deps.client.ingestInventory(ctx));
|
|
29129
|
+
this.remoteInventory = remote.ok ? remote.value : null;
|
|
29130
|
+
const snapshot = await (async () => {
|
|
29131
|
+
try {
|
|
29132
|
+
return await this.deps.posture?.prepare() ?? null;
|
|
29133
|
+
} catch {
|
|
29134
|
+
return null;
|
|
29135
|
+
}
|
|
29136
|
+
})();
|
|
29137
|
+
if (snapshot) {
|
|
29138
|
+
try {
|
|
29139
|
+
await withTimeout(
|
|
29140
|
+
this.deps.posture?.send(snapshot) ?? Promise.resolve(),
|
|
29141
|
+
REQUEST_TIMEOUT_MS
|
|
29142
|
+
);
|
|
29143
|
+
} catch {
|
|
29144
|
+
}
|
|
29145
|
+
}
|
|
29146
|
+
return resolved;
|
|
29147
|
+
}
|
|
29148
|
+
// The id is minted CLIENT-side and stored verbatim: the control plane does NOT
|
|
29149
|
+
// re-key it. `pgAuditValues` writes `id: event.id` and carries tenancy in
|
|
29150
|
+
// its own scoping columns, so the device and the forwarded copy
|
|
29151
|
+
// share one id space — which is what makes a re-post idempotent at all.
|
|
29152
|
+
//
|
|
29153
|
+
// Re-posts collapse via `onConflictDoUpdate` on the `id` PK, guarded by
|
|
29154
|
+
// `setWhere eventType = 'session'` (NOT onConflictDoNothing). That guard is
|
|
29155
|
+
// what makes an attached retry safe: a capture-stubbed session row can still
|
|
29156
|
+
// be HEALED by the authoritative root, while a duplicate non-session event —
|
|
29157
|
+
// a retried tool_call, exactly this path — can never stomp a populated row.
|
|
29158
|
+
async recordAuditEvent(event) {
|
|
29159
|
+
await this.deps.local.recordAuditEvent(event);
|
|
29160
|
+
await this.deps.forward.run(
|
|
29161
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(event, this.remoteInventory))
|
|
29162
|
+
);
|
|
29163
|
+
}
|
|
29164
|
+
// Attached `llm_call` is written locally by the inner gateway, then routed to
|
|
29165
|
+
// the control plane through the existing `recordAuditEvent` ingest (no dedicated
|
|
29166
|
+
// client method yet) by pre-building the audit event from the natural key.
|
|
29167
|
+
// The forward goes DIRECTLY to the client rather than through this.recordAuditEvent,
|
|
29168
|
+
// which would write the event to the local store a second time.
|
|
29169
|
+
async recordLlmCall(input) {
|
|
29170
|
+
await this.deps.local.recordLlmCall(input);
|
|
29171
|
+
await this.deps.forward.run(
|
|
29172
|
+
() => this.deps.client.recordAuditEvent(
|
|
29173
|
+
reKeyForForward(llmAuditEvent(input), this.remoteInventory)
|
|
29174
|
+
)
|
|
29175
|
+
);
|
|
29176
|
+
}
|
|
29177
|
+
/**
|
|
29178
|
+
* Forward one batch, item by item, under ONE aggregate deadline.
|
|
29179
|
+
*
|
|
29180
|
+
* Per-item budgets bound each request and nothing bounded their sum — see
|
|
29181
|
+
* BATCH_FORWARD_BUDGET_MS. When the deadline passes the remainder is dropped
|
|
29182
|
+
* rather than sent: the local write has already succeeded, so every caller
|
|
29183
|
+
* has a correct result to return, and a drop is the outcome this path is
|
|
29184
|
+
* built to accept (G8) where a blown hook timeout is not.
|
|
29185
|
+
*
|
|
29186
|
+
* Serial rather than concurrent on purpose. Firing N requests at once would
|
|
29187
|
+
* trade a latency problem for a burst the plane's own per-key rate limiting
|
|
29188
|
+
* would answer with the refusals the breaker then counts.
|
|
29189
|
+
*
|
|
29190
|
+
* WHAT IS DROPPED IS COUNTED. Every other forward failure ends in
|
|
29191
|
+
* `ForwardPolicy.run`'s catch and moves the breaker's file, which is what
|
|
29192
|
+
* lets status call the forward unhealthy; this path returns BEFORE `run` is
|
|
29193
|
+
* reached, so without the tally in `forward-drops.ts` a slow-but-answering
|
|
29194
|
+
* plane produces no failures, keeps the breaker closed, renders a healthy
|
|
29195
|
+
* block, and discards the tail of every batch indefinitely.
|
|
29196
|
+
*/
|
|
29197
|
+
async forwardBatch(inputs, toEvent) {
|
|
29198
|
+
const deadline = Date.now() + BATCH_FORWARD_BUDGET_MS;
|
|
29199
|
+
for (let i = 0; i < inputs.length; i += 1) {
|
|
29200
|
+
const now = Date.now();
|
|
29201
|
+
if (now >= deadline) {
|
|
29202
|
+
recordForwardDrops(this.deps.dataDir, inputs.length - i, now);
|
|
29203
|
+
return;
|
|
29204
|
+
}
|
|
29205
|
+
const input = inputs[i];
|
|
29206
|
+
await this.deps.forward.run(
|
|
29207
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(toEvent(input), this.remoteInventory))
|
|
29208
|
+
);
|
|
29209
|
+
}
|
|
29210
|
+
}
|
|
29211
|
+
// Delegated as a BATCH rather than looped over recordLlmCall: the inner
|
|
29212
|
+
// gateway may write the whole batch in one local transaction, and looping
|
|
29213
|
+
// here would replace that with N separate local writes.
|
|
29214
|
+
async recordLlmCalls(inputs) {
|
|
29215
|
+
await this.deps.local.recordLlmCalls(inputs);
|
|
29216
|
+
await this.forwardBatch(inputs, (input) => llmAuditEvent(input));
|
|
29217
|
+
}
|
|
29218
|
+
// `input.inspections` (secrets detected client-side in the tool's masked
|
|
29219
|
+
// target) ride along on the request's `inspections` field — the control plane
|
|
29220
|
+
// persists each as an inspection_findings row linked to this audit event
|
|
29221
|
+
// (see RecordAuditEventRequest in @akasecurity/schema). The masked
|
|
29222
|
+
// `target` already rides `input.attributes`, so no raw secret leaks either
|
|
29223
|
+
// way — this only stops the FINDING row itself from being dropped.
|
|
29224
|
+
async recordToolCalls(inputs) {
|
|
29225
|
+
await this.deps.local.recordToolCalls(inputs);
|
|
29226
|
+
await this.forwardBatch(inputs, (input) => toolAuditEvent(input));
|
|
29227
|
+
}
|
|
29228
|
+
// Forwarded as a `config_scan` audit event: there is no dedicated
|
|
29229
|
+
// config-scan ingest endpoint, and the audit-event door is the one the
|
|
29230
|
+
// control plane already opens for client-minted, idempotent records.
|
|
29231
|
+
//
|
|
29232
|
+
// ONLY `scanEvent` CROSSES, and unlike `recordCapture` the plane cannot
|
|
29233
|
+
// re-derive the rest. A `ConfigScanRecord` is four things committed together
|
|
29234
|
+
// locally — the inventory `items`, this audit event, and the posture
|
|
29235
|
+
// `definitions`/`findings` that reference it — and three of them stay on the
|
|
29236
|
+
// device. Say that plainly rather than let the asymmetry with `recordCapture`
|
|
29237
|
+
// read as the same argument: there, findings are omitted BECAUSE the plane
|
|
29238
|
+
// re-derives them from `Event.content`; here there is no content to re-derive
|
|
29239
|
+
// from, so what is omitted is simply not sent.
|
|
29240
|
+
//
|
|
29241
|
+
// That is the wire contract as it stands rather than an oversight to patch
|
|
29242
|
+
// here. `items` has no route at all, and `RecordAuditEventRequest.inspections`
|
|
29243
|
+
// is documented as tool-call findings — widening it to carry config-scan
|
|
29244
|
+
// findings is an egress change (a posture finding's `maskedMatch` holds the
|
|
29245
|
+
// matched command) and a decision about what an attached deployment is
|
|
29246
|
+
// entitled to, not a bug fix. An attached machine's config posture therefore
|
|
29247
|
+
// reaches the plane as the event only; the dashboard's own view of it is the
|
|
29248
|
+
// local store.
|
|
29249
|
+
async recordConfigScan(record2) {
|
|
29250
|
+
await this.deps.local.recordConfigScan(record2);
|
|
29251
|
+
await this.deps.forward.run(
|
|
29252
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(record2.scanEvent, this.remoteInventory))
|
|
29253
|
+
);
|
|
29254
|
+
}
|
|
29255
|
+
async recordBlockedDetection(entry) {
|
|
29256
|
+
return this.deps.local.recordBlockedDetection(entry);
|
|
29257
|
+
}
|
|
29258
|
+
/**
|
|
29259
|
+
* LOCAL-ONLY, deliberately. The shares API is read-plus-decision-override
|
|
29260
|
+
* with no egress ingest endpoint, so there is nothing to forward to; adding a
|
|
29261
|
+
* forward here would be inventing a wire contract that does not exist. The
|
|
29262
|
+
* local write is the whole operation, and its summary is the real one — the
|
|
29263
|
+
* scanner reads a throw as a FAILED WRITE and skips its ledger commit, so
|
|
29264
|
+
* returning the inner gateway's result keeps the retry semantics honest.
|
|
29265
|
+
*/
|
|
29266
|
+
async recordProjectEgress(input) {
|
|
29267
|
+
return this.deps.local.recordProjectEgress(input);
|
|
29268
|
+
}
|
|
29269
|
+
// ---------------------------------------------------------------------
|
|
29270
|
+
// Reads and device-local ledgers: pure delegation.
|
|
29271
|
+
// ---------------------------------------------------------------------
|
|
29272
|
+
async configInventoryReport() {
|
|
29273
|
+
return this.deps.local.configInventoryReport();
|
|
29274
|
+
}
|
|
29275
|
+
async readSessionProvider(sessionId) {
|
|
29276
|
+
return this.deps.local.readSessionProvider(sessionId);
|
|
29277
|
+
}
|
|
29278
|
+
async facets() {
|
|
29279
|
+
return this.deps.local.facets();
|
|
29280
|
+
}
|
|
29281
|
+
/**
|
|
29282
|
+
* Delegated UNMODIFIED — including its refusals.
|
|
29283
|
+
*
|
|
29284
|
+
* This is a fail-secure boundary: it decides whether an approved exception
|
|
29285
|
+
* lets a blocked action through. Under local-first the local store owns the
|
|
29286
|
+
* exception ledger, so the honest answer is whatever it says; wrapping this
|
|
29287
|
+
* in a fallback (`catch { return true }`, or defaulting on a timeout) would
|
|
29288
|
+
* turn a store error into a granted bypass. If the inner gateway rejects,
|
|
29289
|
+
* this rejects, and the runtime's own handling decides — which is asserted
|
|
29290
|
+
* end-to-end through runtime.capture rather than here.
|
|
29291
|
+
*/
|
|
29292
|
+
async consumeException(id) {
|
|
29293
|
+
return this.deps.local.consumeException(id);
|
|
29294
|
+
}
|
|
29295
|
+
async recentFindings(opts) {
|
|
29296
|
+
return this.deps.local.recentFindings(opts);
|
|
29297
|
+
}
|
|
29298
|
+
async healthSummary() {
|
|
29299
|
+
return this.deps.local.healthSummary();
|
|
29300
|
+
}
|
|
29301
|
+
async activityByDay(days) {
|
|
29302
|
+
return this.deps.local.activityByDay(days);
|
|
29303
|
+
}
|
|
29304
|
+
async tokenReports() {
|
|
29305
|
+
return this.deps.local.tokenReports();
|
|
29306
|
+
}
|
|
29307
|
+
async knownContentHashes() {
|
|
29308
|
+
return this.deps.local.knownContentHashes();
|
|
29309
|
+
}
|
|
29310
|
+
async scanLedger(rulesetHash) {
|
|
29311
|
+
return this.deps.local.scanLedger(rulesetHash);
|
|
29312
|
+
}
|
|
29313
|
+
async recordScanned(entries) {
|
|
29314
|
+
return this.deps.local.recordScanned(entries);
|
|
29315
|
+
}
|
|
29316
|
+
async getRuleProbeVerdict(ruleKey) {
|
|
29317
|
+
return this.deps.local.getRuleProbeVerdict(ruleKey);
|
|
29318
|
+
}
|
|
29319
|
+
async setRuleProbeVerdict(ruleKey, verdict, worstProbeMs) {
|
|
29320
|
+
return this.deps.local.setRuleProbeVerdict(ruleKey, verdict, worstProbeMs);
|
|
29321
|
+
}
|
|
29322
|
+
async openAtRestKeysForPath(path) {
|
|
29323
|
+
return this.deps.local.openAtRestKeysForPath(path);
|
|
29324
|
+
}
|
|
29325
|
+
async resolvedAtRestKeysForPath(path) {
|
|
29326
|
+
return this.deps.local.resolvedAtRestKeysForPath(path);
|
|
29327
|
+
}
|
|
29328
|
+
async insertResolution(input) {
|
|
29329
|
+
return this.deps.local.insertResolution(input);
|
|
29330
|
+
}
|
|
29331
|
+
async close() {
|
|
29332
|
+
return this.deps.local.close();
|
|
29333
|
+
}
|
|
29334
|
+
// ---------------------------------------------------------------------
|
|
29335
|
+
// Policy
|
|
29336
|
+
// ---------------------------------------------------------------------
|
|
29337
|
+
async getPolicyBundle() {
|
|
29338
|
+
const local = await this.deps.local.getPolicyBundle();
|
|
29339
|
+
const cached2 = await (async () => {
|
|
29340
|
+
try {
|
|
29341
|
+
return await this.deps.readCachedBundle();
|
|
29342
|
+
} catch {
|
|
29343
|
+
return null;
|
|
29344
|
+
}
|
|
29345
|
+
})();
|
|
29346
|
+
if (cached2 === null) return local;
|
|
29347
|
+
const byRuleId = /* @__PURE__ */ new Map();
|
|
29348
|
+
for (const rule of [...local.rules ?? [], ...cached2.rules ?? []]) {
|
|
29349
|
+
if (!byRuleId.has(rule.id)) byRuleId.set(rule.id, rule);
|
|
29350
|
+
}
|
|
29351
|
+
const rules = [...byRuleId.values()];
|
|
29352
|
+
return {
|
|
29353
|
+
...local,
|
|
29354
|
+
// The remote version identifies the composed bundle for the poller.
|
|
29355
|
+
version: cached2.version,
|
|
29356
|
+
rules,
|
|
29357
|
+
policies: mergeRaiseOnly(
|
|
29358
|
+
local.policies,
|
|
29359
|
+
cached2.policies,
|
|
29360
|
+
ruleCategoryMap(cached2.rules, local.rules)
|
|
29361
|
+
),
|
|
29362
|
+
customKeywords: [...local.customKeywords, ...cached2.customKeywords]
|
|
29363
|
+
// `rulesComplete` is a STANDALONE-ONLY signal (the user's local installed
|
|
29364
|
+
// snapshot) and is taken from the LOCAL bundle only — never from the wire
|
|
29365
|
+
// or the on-disk cache. Honoring a cached one would hand the control plane, or
|
|
29366
|
+
// anything able to write policy-cache.json, a kill-switch over the
|
|
29367
|
+
// compiled-in bundled packs: `{ rulesComplete: true, rules: [] }` would
|
|
29368
|
+
// zero local detection. Spread from `local` above, and deliberately not
|
|
29369
|
+
// re-read from `cached` here.
|
|
29370
|
+
//
|
|
29371
|
+
// THREE MORE OF THE CACHED BUNDLE'S FIELDS ARE DROPPED, each on purpose,
|
|
29372
|
+
// and each named here so a reader can tell a decision from an omission:
|
|
29373
|
+
//
|
|
29374
|
+
// `exceptions` — an exception SUPPRESSES a detection, so honoring
|
|
29375
|
+
// one from an unsigned on-disk cache would let
|
|
29376
|
+
// anything able to write that file turn rules off.
|
|
29377
|
+
// Every other field this merge accepts can only
|
|
29378
|
+
// RAISE enforcement; this is the one that cannot,
|
|
29379
|
+
// so it stays local-only until the bundle is
|
|
29380
|
+
// signed. Exceptions remain a device-local ledger.
|
|
29381
|
+
// `reversibleRuleIds` — the Redact & Vault archetype makes a redaction
|
|
29382
|
+
// recoverable, which is a CUSTODY change: it puts
|
|
29383
|
+
// the detected value in the local vault instead of
|
|
29384
|
+
// destroying it. Taking that instruction from the
|
|
29385
|
+
// cache would let a remote party turn one-way
|
|
29386
|
+
// redaction into retention. Dropping it keeps the
|
|
29387
|
+
// one-way behaviour, which the schema itself calls
|
|
29388
|
+
// "the safe direction to default".
|
|
29389
|
+
// `ruleVersions` — remote rules fall back to their own spec version.
|
|
29390
|
+
// Cosmetic rather than protective: it only affects
|
|
29391
|
+
// how a finding is version-attributed, and the two
|
|
29392
|
+
// sides may therefore attribute org rules
|
|
29393
|
+
// differently. Worth carrying once there is a
|
|
29394
|
+
// reader that needs it; nothing reads it today.
|
|
29395
|
+
};
|
|
29396
|
+
}
|
|
29397
|
+
// ---------------------------------------------------------------------
|
|
29398
|
+
// LocalStoreMaintenance — by delegation (D3).
|
|
29399
|
+
//
|
|
29400
|
+
// Implementing these is what actually closes the skipped-local-maintenance
|
|
29401
|
+
// gap: the OSS structural guard `hasLocalStoreMaintenance()` is satisfied by
|
|
29402
|
+
// any object carrying all five, so the composite qualifies and SessionStart
|
|
29403
|
+
// runs maintenance on the device's real store.
|
|
29404
|
+
//
|
|
29405
|
+
// ⚠ Two of the five are SYNCHRONOUS and must stay that way. `handle-session-start`
|
|
29406
|
+
// calls `capWarnEraEnforcement` without `await` and uses `staleBinaryNotice`'s
|
|
29407
|
+
// return value directly; declaring them `async` here would hand those call
|
|
29408
|
+
// sites a Promise and silently break both.
|
|
29409
|
+
// ---------------------------------------------------------------------
|
|
29410
|
+
async sweepTerminalExceptions(retentionMs) {
|
|
29411
|
+
return this.deps.local.sweepTerminalExceptions(retentionMs);
|
|
29412
|
+
}
|
|
29413
|
+
capWarnEraEnforcement(policyMode) {
|
|
29414
|
+
return this.deps.local.capWarnEraEnforcement(policyMode);
|
|
29415
|
+
}
|
|
29416
|
+
async recordProjectFiles(projectId, scan2) {
|
|
29417
|
+
return this.deps.local.recordProjectFiles(projectId, scan2);
|
|
29418
|
+
}
|
|
29419
|
+
async reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
|
|
29420
|
+
return this.deps.local.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
|
|
29421
|
+
}
|
|
29422
|
+
staleBinaryNotice(currentVersion) {
|
|
29423
|
+
return this.deps.local.staleBinaryNotice(currentVersion);
|
|
29424
|
+
}
|
|
29425
|
+
};
|
|
29426
|
+
function reKeyForForward(event, remote) {
|
|
29427
|
+
if (remote === null) {
|
|
29428
|
+
const stripped = { ...event };
|
|
29429
|
+
delete stripped.hostId;
|
|
29430
|
+
delete stripped.harnessId;
|
|
29431
|
+
delete stripped.sourceProjectId;
|
|
29432
|
+
return stripped;
|
|
29433
|
+
}
|
|
29434
|
+
const rekeyed = { ...event };
|
|
29435
|
+
delete rekeyed.hostId;
|
|
29436
|
+
delete rekeyed.harnessId;
|
|
29437
|
+
delete rekeyed.sourceProjectId;
|
|
29438
|
+
if (remote.hostId !== void 0) rekeyed.hostId = remote.hostId;
|
|
29439
|
+
if (remote.harnessId !== void 0) rekeyed.harnessId = remote.harnessId;
|
|
29440
|
+
if (remote.sourceProjectId !== void 0) rekeyed.sourceProjectId = remote.sourceProjectId;
|
|
29441
|
+
return rekeyed;
|
|
29442
|
+
}
|
|
29443
|
+
var BATCH_FORWARD_BUDGET_MS = 3e3;
|
|
29444
|
+
function llmAuditEvent(input) {
|
|
29445
|
+
return {
|
|
29446
|
+
id: llmCallId(input.sessionId, input.messageId),
|
|
29447
|
+
eventType: "llm_call",
|
|
29448
|
+
startedAt: input.startedAt,
|
|
29449
|
+
parentId: input.parentId,
|
|
29450
|
+
rootSessionId: input.rootSessionId,
|
|
29451
|
+
attributes: input.attributes
|
|
29452
|
+
};
|
|
29453
|
+
}
|
|
29454
|
+
function toolAuditEvent(input) {
|
|
29455
|
+
return {
|
|
29456
|
+
id: toolCallId(input.sessionId, input.toolUseId),
|
|
29457
|
+
eventType: "tool_call",
|
|
29458
|
+
startedAt: input.startedAt,
|
|
29459
|
+
parentId: input.parentId,
|
|
29460
|
+
rootSessionId: input.rootSessionId,
|
|
29461
|
+
attributes: input.attributes,
|
|
29462
|
+
inspections: input.inspections
|
|
29463
|
+
};
|
|
29464
|
+
}
|
|
29465
|
+
|
|
29466
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
29467
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
29468
|
+
import { readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
29469
|
+
import { join as join19 } from "path";
|
|
29470
|
+
|
|
29471
|
+
// ../../packages/plugin-runtime/src/attached/atomic-publish.ts
|
|
29472
|
+
import { rename as rename2 } from "fs/promises";
|
|
29473
|
+
var RETRYABLE = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
29474
|
+
var ATTEMPTS = 5;
|
|
29475
|
+
var delay = (ms) => new Promise((resolve2) => {
|
|
29476
|
+
setTimeout(resolve2, ms);
|
|
29477
|
+
});
|
|
29478
|
+
async function publishByRename(tmp, file2, move = rename2) {
|
|
29479
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
29480
|
+
try {
|
|
29481
|
+
await move(tmp, file2);
|
|
29482
|
+
return;
|
|
29483
|
+
} catch (err) {
|
|
29484
|
+
const code = err.code;
|
|
29485
|
+
if (attempt >= ATTEMPTS || code === void 0 || !RETRYABLE.has(code)) throw err;
|
|
29486
|
+
await delay(attempt * 10);
|
|
29487
|
+
}
|
|
29488
|
+
}
|
|
29489
|
+
}
|
|
29490
|
+
|
|
29491
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
29492
|
+
function createPolicyStore(dir = dataDir()) {
|
|
29493
|
+
const file2 = join19(dir, "policy-cache.json");
|
|
29494
|
+
async function read() {
|
|
29495
|
+
try {
|
|
29496
|
+
const raw = await readFile2(file2, "utf8");
|
|
29497
|
+
const parsed2 = JSON.parse(raw);
|
|
29498
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
29499
|
+
const record2 = parsed2;
|
|
29500
|
+
const bundle = PolicyBundle.parse(record2.bundle);
|
|
29501
|
+
const fetchedAtMs = typeof record2.fetchedAtMs === "number" ? record2.fetchedAtMs : 0;
|
|
29502
|
+
const etag = typeof record2.etag === "string" ? record2.etag : void 0;
|
|
29503
|
+
return { bundle, fetchedAtMs, ...etag === void 0 ? {} : { etag } };
|
|
29504
|
+
} catch {
|
|
29505
|
+
return null;
|
|
29506
|
+
}
|
|
29507
|
+
}
|
|
29508
|
+
async function write(bundle, etag) {
|
|
29509
|
+
await ensureDataDir(dir);
|
|
29510
|
+
const stored = {
|
|
29511
|
+
bundle,
|
|
29512
|
+
fetchedAtMs: Date.now(),
|
|
29513
|
+
...etag === void 0 ? {} : { etag }
|
|
29514
|
+
};
|
|
29515
|
+
const tmp = `${file2}.${randomUUID16()}.tmp`;
|
|
29516
|
+
try {
|
|
29517
|
+
await writeFile2(tmp, JSON.stringify(stored), {
|
|
29518
|
+
encoding: "utf8",
|
|
29519
|
+
mode: DATA_FILE_MODE,
|
|
29520
|
+
flag: "wx"
|
|
29521
|
+
});
|
|
29522
|
+
await publishByRename(tmp, file2);
|
|
29523
|
+
} catch (err) {
|
|
29524
|
+
await rm(tmp, { force: true }).catch(() => void 0);
|
|
29525
|
+
throw err;
|
|
29526
|
+
}
|
|
29527
|
+
}
|
|
29528
|
+
return { read, write, file: file2 };
|
|
29529
|
+
}
|
|
29530
|
+
|
|
29531
|
+
// ../../packages/remote/src/http.ts
|
|
29532
|
+
import { request as httpRequest } from "http";
|
|
29533
|
+
import { request as httpsRequest } from "https";
|
|
29534
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
29535
|
+
var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
29536
|
+
var RemoteRequestError = class extends Error {
|
|
29537
|
+
constructor(status) {
|
|
29538
|
+
super(`control-plane request failed with status ${String(status)}`);
|
|
29539
|
+
this.status = status;
|
|
29540
|
+
this.name = "RemoteRequestError";
|
|
29541
|
+
}
|
|
29542
|
+
status;
|
|
29543
|
+
};
|
|
29544
|
+
var RemoteRequestInvalid = class extends Error {
|
|
29545
|
+
constructor(route, cause) {
|
|
29546
|
+
super(`refusing to send a malformed body to ${route}`);
|
|
29547
|
+
this.cause = cause;
|
|
29548
|
+
this.name = "RemoteRequestInvalid";
|
|
29549
|
+
}
|
|
29550
|
+
cause;
|
|
29551
|
+
};
|
|
29552
|
+
var RemoteResponseInvalid = class extends Error {
|
|
29553
|
+
constructor(route, detail) {
|
|
29554
|
+
super(`control plane answered ${route} with ${detail}`);
|
|
29555
|
+
this.name = "RemoteResponseInvalid";
|
|
29556
|
+
}
|
|
29557
|
+
};
|
|
29558
|
+
var RemoteTransportError = class extends Error {
|
|
29559
|
+
/**
|
|
29560
|
+
* The status the peer sent, when headers arrived and only the BODY was
|
|
29561
|
+
* refused.
|
|
29562
|
+
*
|
|
29563
|
+
* Undefined for the ordinary case this class was written for — no answer at
|
|
29564
|
+
* all. It exists because two paths reject after a status has already been
|
|
29565
|
+
* delivered: an oversized body and an aborted response. Discarding it there
|
|
29566
|
+
* reported a deployment answering 401 with a verbose body as a network
|
|
29567
|
+
* outage, which sends the reader to look at their network instead of their
|
|
29568
|
+
* credential.
|
|
29569
|
+
*/
|
|
29570
|
+
constructor(reason, status) {
|
|
29571
|
+
super(`control-plane request did not complete: ${reason}`);
|
|
29572
|
+
this.status = status;
|
|
29573
|
+
this.name = "RemoteTransportError";
|
|
29574
|
+
}
|
|
29575
|
+
status;
|
|
29576
|
+
};
|
|
29577
|
+
async function send(options) {
|
|
29578
|
+
const url2 = new URL(options.url);
|
|
29579
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
29580
|
+
const send_ = url2.protocol === "http:" ? httpRequest : httpsRequest;
|
|
29581
|
+
const requestOptions = {
|
|
29582
|
+
method: options.method,
|
|
29583
|
+
headers: {
|
|
29584
|
+
// CALLER HEADERS FIRST, so this module's own are not overridable. Spread
|
|
29585
|
+
// last they win, and two of the values below are ones no caller may
|
|
29586
|
+
// replace: `x-api-key` is the credential, and `content-length` is the
|
|
29587
|
+
// byte count that stops a multi-byte body being truncated by the
|
|
29588
|
+
// receiver. `SendOptions.headers` is a free-form record on an exported
|
|
29589
|
+
// function, so "no caller does that today" is not the guarantee to rely
|
|
29590
|
+
// on. The one header any caller actually passes — `if-none-match` on the
|
|
29591
|
+
// conditional GET — is untouched by this order.
|
|
29592
|
+
...options.headers,
|
|
29593
|
+
// The credential. One header, matching what the deployment authenticates
|
|
29594
|
+
// on; a second copy in an `Authorization` header would be one more place
|
|
29595
|
+
// it can be logged by an intermediary for no gain.
|
|
29596
|
+
"x-api-key": options.apiKey,
|
|
29597
|
+
accept: "application/json",
|
|
29598
|
+
...options.body === void 0 ? {} : {
|
|
29599
|
+
"content-type": "application/json",
|
|
29600
|
+
// Byte length, not string length: a multi-byte body sent with a
|
|
29601
|
+
// character count is truncated by the receiver.
|
|
29602
|
+
"content-length": String(Buffer.byteLength(options.body))
|
|
29603
|
+
}
|
|
29604
|
+
}
|
|
29605
|
+
};
|
|
29606
|
+
return new Promise((resolve2, reject) => {
|
|
29607
|
+
let settled = false;
|
|
29608
|
+
const fail = (reason, status) => {
|
|
29609
|
+
if (settled) return;
|
|
29610
|
+
settled = true;
|
|
29611
|
+
reject(new RemoteTransportError(reason, status));
|
|
29612
|
+
};
|
|
29613
|
+
const req = send_(url2, requestOptions, (res) => {
|
|
29614
|
+
const chunks = [];
|
|
29615
|
+
let size = 0;
|
|
29616
|
+
res.on("data", (chunk) => {
|
|
29617
|
+
size += chunk.length;
|
|
29618
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
29619
|
+
fail(`response exceeded ${String(MAX_RESPONSE_BYTES)} bytes`, res.statusCode);
|
|
29620
|
+
res.destroy();
|
|
29621
|
+
req.destroy();
|
|
29622
|
+
return;
|
|
29623
|
+
}
|
|
29624
|
+
chunks.push(chunk);
|
|
29625
|
+
});
|
|
29626
|
+
res.on("aborted", () => {
|
|
29627
|
+
fail("the response was aborted", res.statusCode);
|
|
29628
|
+
});
|
|
29629
|
+
res.on("end", () => {
|
|
29630
|
+
if (settled) return;
|
|
29631
|
+
settled = true;
|
|
29632
|
+
resolve2({
|
|
29633
|
+
status: res.statusCode ?? 0,
|
|
29634
|
+
headers: res.headers,
|
|
29635
|
+
body: Buffer.concat(chunks).toString("utf8")
|
|
29636
|
+
});
|
|
29637
|
+
});
|
|
29638
|
+
});
|
|
29639
|
+
const deadline = setTimeout(() => {
|
|
29640
|
+
fail(`no response within ${String(timeoutMs)}ms`);
|
|
29641
|
+
req.destroy();
|
|
29642
|
+
}, timeoutMs);
|
|
29643
|
+
deadline.unref();
|
|
29644
|
+
req.on("upgrade", (_res, socket) => {
|
|
29645
|
+
fail("the deployment answered with a protocol upgrade");
|
|
29646
|
+
socket.destroy();
|
|
29647
|
+
});
|
|
29648
|
+
req.on("close", () => {
|
|
29649
|
+
fail("the connection closed before a response was read");
|
|
29650
|
+
clearTimeout(deadline);
|
|
29651
|
+
});
|
|
29652
|
+
req.on("error", (err) => {
|
|
29653
|
+
fail(err.message);
|
|
29654
|
+
});
|
|
29655
|
+
if (options.body !== void 0) req.write(options.body);
|
|
29656
|
+
req.end();
|
|
29657
|
+
});
|
|
29658
|
+
}
|
|
29659
|
+
|
|
29660
|
+
// ../../packages/remote/src/client.ts
|
|
29661
|
+
var ROUTES = {
|
|
29662
|
+
events: "/v1/events",
|
|
29663
|
+
auditEvents: "/v1/audit-events",
|
|
29664
|
+
inventory: "/v1/inventory",
|
|
29665
|
+
storePosture: "/v1/store-posture",
|
|
29666
|
+
policyBundle: "/v1/policy-bundle",
|
|
29667
|
+
whoami: "/v1/plugin/whoami"
|
|
29668
|
+
};
|
|
29669
|
+
function headerValue(response, name) {
|
|
29670
|
+
const raw = response.headers[name];
|
|
29671
|
+
if (raw === void 0) return void 0;
|
|
29672
|
+
return Array.isArray(raw) ? raw[0] : raw;
|
|
29673
|
+
}
|
|
29674
|
+
function okBody(response) {
|
|
29675
|
+
if (response.status < 200 || response.status >= 300) {
|
|
29676
|
+
throw new RemoteRequestError(response.status);
|
|
29677
|
+
}
|
|
29678
|
+
return response.body;
|
|
29679
|
+
}
|
|
29680
|
+
function parsed(schema, body, route) {
|
|
29681
|
+
let json2;
|
|
29682
|
+
try {
|
|
29683
|
+
json2 = JSON.parse(body);
|
|
29684
|
+
} catch {
|
|
29685
|
+
throw new RemoteResponseInvalid(route, "a body that is not JSON");
|
|
29686
|
+
}
|
|
29687
|
+
const result = schema.safeParse(json2);
|
|
29688
|
+
if (!result.success) {
|
|
29689
|
+
throw new RemoteResponseInvalid(route, "a body this client cannot read");
|
|
29690
|
+
}
|
|
29691
|
+
return result.data;
|
|
29692
|
+
}
|
|
29693
|
+
function withoutTrailingSlashes(endpoint) {
|
|
29694
|
+
let end = endpoint.length;
|
|
29695
|
+
while (end > 0 && endpoint.charCodeAt(end - 1) === SLASH) end -= 1;
|
|
29696
|
+
return endpoint.slice(0, end);
|
|
29697
|
+
}
|
|
29698
|
+
var SLASH = "/".charCodeAt(0);
|
|
29699
|
+
function createRemoteClient(options) {
|
|
29700
|
+
const base = withoutTrailingSlashes(options.endpoint);
|
|
29701
|
+
const url2 = (route) => `${base}${route}`;
|
|
29702
|
+
const common = { apiKey: options.apiKey, timeoutMs: options.timeoutMs };
|
|
29703
|
+
return {
|
|
29704
|
+
async ingestEvents(batch) {
|
|
29705
|
+
const response = await send({
|
|
29706
|
+
...common,
|
|
29707
|
+
method: "POST",
|
|
29708
|
+
url: url2(ROUTES.events),
|
|
29709
|
+
body: JSON.stringify(batch)
|
|
29710
|
+
});
|
|
29711
|
+
return parsed(IngestAck, okBody(response), ROUTES.events);
|
|
29712
|
+
},
|
|
29713
|
+
async ingestInventory(context) {
|
|
29714
|
+
const response = await send({
|
|
29715
|
+
...common,
|
|
29716
|
+
method: "POST",
|
|
29717
|
+
url: url2(ROUTES.inventory),
|
|
29718
|
+
body: JSON.stringify(context)
|
|
29719
|
+
});
|
|
29720
|
+
return parsed(ResolvedInventory, okBody(response), ROUTES.inventory);
|
|
29721
|
+
},
|
|
29722
|
+
async recordAuditEvent(event) {
|
|
29723
|
+
const validated = RecordAuditEventRequest.safeParse(event);
|
|
29724
|
+
if (!validated.success) throw new RemoteRequestInvalid(ROUTES.auditEvents, validated.error);
|
|
29725
|
+
const submission = validated.data;
|
|
29726
|
+
const response = await send({
|
|
29727
|
+
...common,
|
|
29728
|
+
method: "POST",
|
|
29729
|
+
url: url2(ROUTES.auditEvents),
|
|
29730
|
+
body: JSON.stringify(submission)
|
|
29731
|
+
});
|
|
29732
|
+
okBody(response);
|
|
29733
|
+
},
|
|
29734
|
+
async reportStorePosture(snapshot) {
|
|
29735
|
+
const response = await send({
|
|
29736
|
+
...common,
|
|
29737
|
+
method: "POST",
|
|
29738
|
+
url: url2(ROUTES.storePosture),
|
|
29739
|
+
body: JSON.stringify(snapshot)
|
|
29740
|
+
});
|
|
29741
|
+
okBody(response);
|
|
29742
|
+
},
|
|
29743
|
+
async getPolicyBundle(etag) {
|
|
29744
|
+
const response = await send({
|
|
29745
|
+
...common,
|
|
29746
|
+
method: "GET",
|
|
29747
|
+
url: url2(ROUTES.policyBundle),
|
|
29748
|
+
...etag === void 0 ? {} : { headers: { "if-none-match": etag } }
|
|
29749
|
+
});
|
|
29750
|
+
if (response.status === 304) {
|
|
29751
|
+
return { changed: false, etag: headerValue(response, "etag") ?? etag };
|
|
29752
|
+
}
|
|
29753
|
+
const bundle = parsed(PolicyBundle, okBody(response), ROUTES.policyBundle);
|
|
29754
|
+
return { changed: true, bundle, etag: headerValue(response, "etag") };
|
|
29755
|
+
},
|
|
29756
|
+
async whoami() {
|
|
29757
|
+
const response = await send({ ...common, method: "GET", url: url2(ROUTES.whoami) });
|
|
29758
|
+
return parsed(PluginWhoami, okBody(response), ROUTES.whoami);
|
|
29759
|
+
}
|
|
29760
|
+
};
|
|
29761
|
+
}
|
|
29762
|
+
|
|
29763
|
+
// ../../packages/plugin-runtime/src/attached/posture-reporter.ts
|
|
29764
|
+
var POSTURE_REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
29765
|
+
function createPostureReporter(deps) {
|
|
29766
|
+
async function prepare() {
|
|
29767
|
+
try {
|
|
29768
|
+
const state = await withTimeout(deps.store.read(), REQUEST_TIMEOUT_MS);
|
|
29769
|
+
if (state === null) return null;
|
|
29770
|
+
const nowMs = deps.now();
|
|
29771
|
+
const elapsed = nowMs - state.lastAttemptedAtMs;
|
|
29772
|
+
if (elapsed >= 0 && elapsed < POSTURE_REPORT_INTERVAL_MS) return null;
|
|
29773
|
+
try {
|
|
29774
|
+
await withTimeout(deps.store.markAttempted(state.deviceId, nowMs), REQUEST_TIMEOUT_MS);
|
|
29775
|
+
} catch {
|
|
29776
|
+
}
|
|
29777
|
+
const { readError, ...measurement } = deps.readStore();
|
|
29778
|
+
if (readError) return null;
|
|
29779
|
+
let plugin;
|
|
29780
|
+
try {
|
|
29781
|
+
plugin = await deps.pluginBlock?.();
|
|
29782
|
+
} catch {
|
|
29783
|
+
plugin = void 0;
|
|
29784
|
+
}
|
|
29785
|
+
return {
|
|
29786
|
+
deviceId: state.deviceId,
|
|
29787
|
+
hostname: deps.hostname(),
|
|
29788
|
+
capturedAt: nowMs,
|
|
29789
|
+
...measurement,
|
|
29790
|
+
// Omit the key rather than spread an explicit `undefined` —
|
|
29791
|
+
// exactOptionalPropertyTypes distinguishes the two, and the bridge in
|
|
29792
|
+
// factory.ts keys on presence.
|
|
29793
|
+
...plugin === void 0 ? {} : { plugin }
|
|
29794
|
+
};
|
|
29795
|
+
} catch {
|
|
29796
|
+
return null;
|
|
29797
|
+
}
|
|
29798
|
+
}
|
|
29799
|
+
async function send2(snapshot) {
|
|
29800
|
+
try {
|
|
29801
|
+
await deps.report(snapshot);
|
|
29802
|
+
} catch {
|
|
29803
|
+
}
|
|
29804
|
+
}
|
|
29805
|
+
return { prepare, send: send2 };
|
|
29806
|
+
}
|
|
29807
|
+
|
|
29808
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
29809
|
+
import { statSync as statSync9 } from "fs";
|
|
29810
|
+
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
29811
|
+
|
|
29812
|
+
// ../../packages/plugin-runtime/src/attached/action-counts.ts
|
|
29813
|
+
function emptyActionCounts() {
|
|
29814
|
+
return Object.fromEntries(ACTION_TAKEN_KEYS.map((a) => [a, 0]));
|
|
29815
|
+
}
|
|
29816
|
+
function isActionTaken(value) {
|
|
29817
|
+
return ACTION_TAKEN_KEYS.includes(value);
|
|
29818
|
+
}
|
|
29819
|
+
|
|
29820
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
29821
|
+
var CAPTURE_EVENT_TYPES_SQL2 = `('prompt','response','code_change','tool_use')`;
|
|
29822
|
+
function isSchemaAbsent(err) {
|
|
29823
|
+
return err instanceof Error && /no such table/i.test(err.message);
|
|
29824
|
+
}
|
|
29825
|
+
function emptyReadout(readError = false) {
|
|
29826
|
+
const byAction = emptyActionCounts();
|
|
29827
|
+
return {
|
|
29828
|
+
storePresent: false,
|
|
29829
|
+
schemaVersion: null,
|
|
29830
|
+
findingsTotal: 0,
|
|
29831
|
+
findingsFirstAt: null,
|
|
29832
|
+
findingsLastAt: null,
|
|
29833
|
+
packs: [],
|
|
29834
|
+
policyCounts: { total: 0, disabled: 0, byAction },
|
|
29835
|
+
readError
|
|
29836
|
+
};
|
|
29837
|
+
}
|
|
29838
|
+
function readStorePosture(dbPath2) {
|
|
29839
|
+
try {
|
|
29840
|
+
statSync9(dbPath2);
|
|
29841
|
+
} catch (err) {
|
|
29842
|
+
const code = err.code;
|
|
29843
|
+
if (code === "ENOENT" || code === "ENOTDIR") return emptyReadout();
|
|
29844
|
+
return emptyReadout(true);
|
|
29845
|
+
}
|
|
29846
|
+
let db = null;
|
|
29847
|
+
let version2 = null;
|
|
29848
|
+
let packs = [];
|
|
29849
|
+
let policyCounts = {
|
|
29850
|
+
total: 0,
|
|
29851
|
+
disabled: 0,
|
|
29852
|
+
byAction: emptyActionCounts()
|
|
29853
|
+
};
|
|
29854
|
+
let findingsTotal = 0;
|
|
29855
|
+
let findingsFirstAt = null;
|
|
29856
|
+
let findingsLastAt = null;
|
|
29857
|
+
const currentReadout = () => ({
|
|
29858
|
+
storePresent: true,
|
|
29859
|
+
schemaVersion: version2,
|
|
29860
|
+
findingsTotal,
|
|
29861
|
+
findingsFirstAt,
|
|
29862
|
+
findingsLastAt,
|
|
29863
|
+
packs,
|
|
29864
|
+
policyCounts,
|
|
29865
|
+
readError: false
|
|
29866
|
+
});
|
|
29867
|
+
try {
|
|
29868
|
+
db = new DatabaseSync3(dbPath2, { readOnly: true });
|
|
29869
|
+
db.exec("PRAGMA busy_timeout = 2000");
|
|
29870
|
+
version2 = db.prepare("PRAGMA user_version").get().user_version;
|
|
29871
|
+
try {
|
|
29872
|
+
const packRows = db.prepare(
|
|
29873
|
+
`SELECT namespace, pack_id, version, enabled, updated_at FROM installed_packs ORDER BY namespace, pack_id`
|
|
29874
|
+
).all();
|
|
29875
|
+
packs = packRows.map((r) => ({
|
|
29876
|
+
packId: `${r.namespace}/${r.pack_id}`,
|
|
29877
|
+
version: r.version,
|
|
29878
|
+
enabled: r.enabled !== 0,
|
|
29879
|
+
updatedAt: r.updated_at == null ? null : String(r.updated_at)
|
|
29880
|
+
}));
|
|
29881
|
+
} catch (err) {
|
|
29882
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
29883
|
+
}
|
|
29884
|
+
try {
|
|
29885
|
+
const policyRows = db.prepare(`SELECT action, enabled FROM policies`).all();
|
|
29886
|
+
const byAction = emptyActionCounts();
|
|
29887
|
+
let disabled = 0;
|
|
29888
|
+
for (const row of policyRows) {
|
|
29889
|
+
if (row.enabled === 0) disabled += 1;
|
|
29890
|
+
if (isActionTaken(row.action)) byAction[row.action] += 1;
|
|
29891
|
+
}
|
|
29892
|
+
policyCounts = { total: policyRows.length, disabled, byAction };
|
|
29893
|
+
} catch (err) {
|
|
29894
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
29895
|
+
}
|
|
29896
|
+
try {
|
|
29897
|
+
const agg = db.prepare(
|
|
29898
|
+
`SELECT count(*) AS n, min(f.first_detected_at) AS firstAt, max(f.first_detected_at) AS lastAt
|
|
29899
|
+
FROM inspection_findings f JOIN audit_events e ON e.id = f.audit_event_id
|
|
29900
|
+
WHERE e.event_type IN ${CAPTURE_EVENT_TYPES_SQL2}`
|
|
29901
|
+
).get();
|
|
29902
|
+
findingsTotal = agg.n;
|
|
29903
|
+
findingsFirstAt = agg.firstAt;
|
|
29904
|
+
findingsLastAt = agg.lastAt;
|
|
29905
|
+
} catch (err) {
|
|
29906
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
29907
|
+
}
|
|
29908
|
+
return currentReadout();
|
|
29909
|
+
} catch {
|
|
29910
|
+
return emptyReadout(true);
|
|
29911
|
+
} finally {
|
|
29912
|
+
try {
|
|
29913
|
+
db?.close();
|
|
29914
|
+
} catch {
|
|
29915
|
+
}
|
|
29916
|
+
}
|
|
29917
|
+
}
|
|
29918
|
+
|
|
29919
|
+
// ../../packages/plugin-runtime/src/attached/posture-store.ts
|
|
29920
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
29921
|
+
import { readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
29922
|
+
import { join as join20 } from "path";
|
|
29923
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
29924
|
+
function createPostureStore(dir = settingsDir(), legacyDir) {
|
|
29925
|
+
const file2 = join20(dir, "posture-state.json");
|
|
29926
|
+
const legacyFile = legacyDir === void 0 ? null : join20(legacyDir, "posture-state.json");
|
|
29927
|
+
async function persist(state) {
|
|
29928
|
+
await ensureDataDir(dir);
|
|
29929
|
+
const tmp = `${file2}.${randomUUID17()}.tmp`;
|
|
29930
|
+
try {
|
|
29931
|
+
await writeFile3(tmp, JSON.stringify(state), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
29932
|
+
await publishByRename(tmp, file2);
|
|
29933
|
+
} catch (err) {
|
|
29934
|
+
await rm2(tmp, { force: true }).catch(() => void 0);
|
|
29935
|
+
throw err;
|
|
29936
|
+
}
|
|
29937
|
+
}
|
|
29938
|
+
async function readFrom(path) {
|
|
29939
|
+
let raw;
|
|
29940
|
+
try {
|
|
29941
|
+
raw = await readFile3(path, "utf8");
|
|
29942
|
+
} catch (err) {
|
|
29943
|
+
const code = err.code;
|
|
29944
|
+
if (code === "ENOENT" || code === "ENOTDIR") return null;
|
|
29945
|
+
throw err;
|
|
29946
|
+
}
|
|
29947
|
+
try {
|
|
29948
|
+
const parsed2 = JSON.parse(raw);
|
|
29949
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
29950
|
+
const record2 = parsed2;
|
|
29951
|
+
if (typeof record2.deviceId === "string" && UUID_RE.test(record2.deviceId)) {
|
|
29952
|
+
const stamp = typeof record2.lastAttemptedAtMs === "number" ? record2.lastAttemptedAtMs : typeof record2.lastReportedAtMs === "number" ? record2.lastReportedAtMs : 0;
|
|
29953
|
+
return { deviceId: record2.deviceId, lastAttemptedAtMs: stamp };
|
|
29954
|
+
}
|
|
29955
|
+
}
|
|
29956
|
+
} catch {
|
|
29957
|
+
}
|
|
29958
|
+
return null;
|
|
29959
|
+
}
|
|
29960
|
+
async function read() {
|
|
29961
|
+
const current = await readFrom(file2);
|
|
29962
|
+
if (current) return current;
|
|
29963
|
+
const legacy = legacyFile === null || legacyFile === file2 ? null : await readFrom(legacyFile).catch(() => null);
|
|
29964
|
+
if (legacy) {
|
|
29965
|
+
try {
|
|
29966
|
+
await persist(legacy);
|
|
29967
|
+
} catch {
|
|
29968
|
+
}
|
|
29969
|
+
return legacy;
|
|
29970
|
+
}
|
|
29971
|
+
const fresh = { deviceId: randomUUID17(), lastAttemptedAtMs: 0 };
|
|
29972
|
+
try {
|
|
29973
|
+
await ensureDataDir(dir);
|
|
29974
|
+
if (createOwnerOnlyFileSync(file2, JSON.stringify(fresh))) return fresh;
|
|
29975
|
+
} catch {
|
|
29976
|
+
return null;
|
|
29977
|
+
}
|
|
29978
|
+
const winner = await readFrom(file2).catch(() => null);
|
|
29979
|
+
if (winner) return winner;
|
|
29980
|
+
try {
|
|
29981
|
+
await persist(fresh);
|
|
29982
|
+
} catch {
|
|
29983
|
+
return null;
|
|
29984
|
+
}
|
|
29985
|
+
return fresh;
|
|
29986
|
+
}
|
|
29987
|
+
async function markAttempted(deviceId, atMs) {
|
|
29988
|
+
await persist({ deviceId, lastAttemptedAtMs: atMs });
|
|
29989
|
+
}
|
|
29990
|
+
return { read, markAttempted, file: file2 };
|
|
29991
|
+
}
|
|
29992
|
+
|
|
29993
|
+
// ../../packages/plugin-runtime/src/attached/sync-state.ts
|
|
29994
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
29995
|
+
import { join as join21 } from "path";
|
|
29996
|
+
|
|
29997
|
+
// ../../packages/plugin-runtime/src/attached/status.ts
|
|
29998
|
+
var REFUSAL_LINES = {
|
|
29999
|
+
unauthorized: "KEY REJECTED \u2014 re-attach with a valid plugin key",
|
|
30000
|
+
forbidden: "ACCESS REFUSED \u2014 key is valid but not permitted; ask your org admin"
|
|
30001
|
+
};
|
|
30002
|
+
var OUTCOME_LINES = {
|
|
30003
|
+
ok: "policy synced",
|
|
30004
|
+
"not-modified": "policy up to date",
|
|
30005
|
+
unauthorized: REFUSAL_LINES.unauthorized,
|
|
30006
|
+
forbidden: REFUSAL_LINES.forbidden,
|
|
30007
|
+
unreachable: "control plane unreachable at last attempt",
|
|
30008
|
+
"invalid-bundle": "control plane sent a policy bundle this build cannot read"
|
|
30009
|
+
};
|
|
30010
|
+
|
|
30011
|
+
// ../../packages/plugin-runtime/src/attached/sync-trigger.ts
|
|
30012
|
+
import { spawn } from "child_process";
|
|
30013
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
30014
|
+
var SYNC_THROTTLE_MS = 15 * 60 * 1e3;
|
|
30015
|
+
|
|
30016
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
30017
|
+
import { hostname as hostname5 } from "os";
|
|
30018
|
+
|
|
30019
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
30020
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
30021
|
+
|
|
30022
|
+
// ../../packages/plugin-runtime/src/recorder.ts
|
|
30023
|
+
var PLUGIN_RECORDER_BINARY = "plugin";
|
|
30024
|
+
|
|
30025
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
30026
|
+
var StandaloneDataGateway = class {
|
|
30027
|
+
db;
|
|
30028
|
+
// Kept for the fingerprint key lookup (exception.key lives beside the store).
|
|
30029
|
+
dataDir;
|
|
30030
|
+
// One notice per gateway — see warnRulesetDiscarded.
|
|
30031
|
+
warnedRulesetDiscarded = false;
|
|
30032
|
+
constructor(dataDir2, detections = [], meta3) {
|
|
30033
|
+
this.db = openLocalDatabase(dataDir2);
|
|
30034
|
+
this.dataDir = dataDir2;
|
|
30035
|
+
this.db.installedPacks.recordInventory(detections, meta3);
|
|
30036
|
+
}
|
|
30037
|
+
recordCapture(record2) {
|
|
30038
|
+
this.db.recordCapture(record2.event, record2.findings);
|
|
30039
|
+
return Promise.resolve();
|
|
30040
|
+
}
|
|
30041
|
+
ensureInventory(ctx) {
|
|
30042
|
+
return Promise.resolve(this.db.ensureInventory(ctx));
|
|
30043
|
+
}
|
|
30044
|
+
recordAuditEvent(event) {
|
|
30045
|
+
this.db.auditEvents.insertAuditEvent(event);
|
|
30046
|
+
return Promise.resolve();
|
|
30047
|
+
}
|
|
30048
|
+
// The id is minted inside the repository from the natural key — the plugin can't
|
|
30049
|
+
// import @akasecurity/persistence to compute it, so the gateway is the boundary that
|
|
30050
|
+
// hands the natural key across. UPSERT-take-MAX → idempotent re-reads that also
|
|
28579
30051
|
// converge a streaming partial/final split (see insertLlmCall).
|
|
28580
30052
|
recordLlmCall(input) {
|
|
28581
30053
|
this.db.auditEvents.insertLlmCall(input);
|
|
@@ -28586,12 +30058,12 @@ var StandaloneDataGateway = class {
|
|
|
28586
30058
|
// reconciler drops the whole pass and recovers it idempotently on the next read.
|
|
28587
30059
|
recordLlmCalls(inputs) {
|
|
28588
30060
|
if (inputs.length === 0) return Promise.resolve();
|
|
28589
|
-
return new Promise((
|
|
30061
|
+
return new Promise((resolve2, reject) => {
|
|
28590
30062
|
try {
|
|
28591
30063
|
this.db.auditEvents.runInTransaction(() => {
|
|
28592
30064
|
for (const input of inputs) this.db.auditEvents.insertLlmCall(input);
|
|
28593
30065
|
});
|
|
28594
|
-
|
|
30066
|
+
resolve2();
|
|
28595
30067
|
} catch (err) {
|
|
28596
30068
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
28597
30069
|
}
|
|
@@ -28603,12 +30075,12 @@ var StandaloneDataGateway = class {
|
|
|
28603
30075
|
// drops the whole pass and recovers it idempotently next time.
|
|
28604
30076
|
recordToolCalls(inputs) {
|
|
28605
30077
|
if (inputs.length === 0) return Promise.resolve();
|
|
28606
|
-
return new Promise((
|
|
30078
|
+
return new Promise((resolve2, reject) => {
|
|
28607
30079
|
try {
|
|
28608
30080
|
this.db.auditEvents.runInTransaction(() => {
|
|
28609
30081
|
for (const input of inputs) this.writeToolCall(input);
|
|
28610
30082
|
});
|
|
28611
|
-
|
|
30083
|
+
resolve2();
|
|
28612
30084
|
} catch (err) {
|
|
28613
30085
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
28614
30086
|
}
|
|
@@ -28750,7 +30222,7 @@ var StandaloneDataGateway = class {
|
|
|
28750
30222
|
const customKeywords = [...new Set(policies.flatMap((p) => p.customKeywords ?? []))];
|
|
28751
30223
|
const installed = this.installedScanRules();
|
|
28752
30224
|
const rulePolicies = installed ? [...installed.ruleActions].map(([ruleId, action]) => ({
|
|
28753
|
-
id:
|
|
30225
|
+
id: randomUUID18(),
|
|
28754
30226
|
scope: "global",
|
|
28755
30227
|
target: { ruleId },
|
|
28756
30228
|
action,
|
|
@@ -28904,22 +30376,68 @@ var StandaloneDataGateway = class {
|
|
|
28904
30376
|
}
|
|
28905
30377
|
};
|
|
28906
30378
|
|
|
30379
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
30380
|
+
function resolveGatewayForConfig(config2, meta3) {
|
|
30381
|
+
const local = new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
|
|
30382
|
+
try {
|
|
30383
|
+
if (!isAttached(config2.settings)) return local;
|
|
30384
|
+
const connection = config2.settings.controlPlane;
|
|
30385
|
+
if (connection === void 0) return local;
|
|
30386
|
+
const state = readControlPlaneCredentialState(config2.settingsDir, connection);
|
|
30387
|
+
if (!state.usable) return local;
|
|
30388
|
+
const client = createRemoteClient({
|
|
30389
|
+
endpoint: connection.endpoint,
|
|
30390
|
+
apiKey: state.credential.apiKey
|
|
30391
|
+
});
|
|
30392
|
+
const store = createPolicyStore(config2.dataDir);
|
|
30393
|
+
const postureStore = createPostureStore(config2.settingsDir, config2.dataDir);
|
|
30394
|
+
const forward = createForwardPolicy({ dir: config2.dataDir });
|
|
30395
|
+
return new AttachedDataGateway({
|
|
30396
|
+
local,
|
|
30397
|
+
client,
|
|
30398
|
+
dataDir: config2.dataDir,
|
|
30399
|
+
readCachedBundle: () => store.read().then((cached2) => cached2?.bundle ?? null),
|
|
30400
|
+
forward,
|
|
30401
|
+
posture: createPostureReporter({
|
|
30402
|
+
// THROUGH THE BREAKER, and wrapped HERE rather than around
|
|
30403
|
+
// `PostureReporter.send`. The reporter swallows every error by
|
|
30404
|
+
// contract, so a wrap outside it would hand `forward.run` a resolved
|
|
30405
|
+
// promise for a send that failed — recording a SUCCESS, clearing
|
|
30406
|
+
// `consecutiveFailures` and `lastFailure`, and telling `aka status` the
|
|
30407
|
+
// forward recovered when nothing did. Wrapping the raw client call puts
|
|
30408
|
+
// the breaker above the swallow, where it can see the truth.
|
|
30409
|
+
//
|
|
30410
|
+
// What it buys: once the breaker is open — the plane already confirmed
|
|
30411
|
+
// down by the gateway's own writes — this stops paying a request
|
|
30412
|
+
// timeout per throttle interval to re-learn it.
|
|
30413
|
+
report: (snapshot) => forward.run(() => client.reportStorePosture(snapshot)).then(() => void 0),
|
|
30414
|
+
store: postureStore,
|
|
30415
|
+
readStore: () => readStorePosture(config2.dbPath),
|
|
30416
|
+
hostname: () => hostname5(),
|
|
30417
|
+
now: () => Date.now()
|
|
30418
|
+
})
|
|
30419
|
+
});
|
|
30420
|
+
} catch {
|
|
30421
|
+
return local;
|
|
30422
|
+
}
|
|
30423
|
+
}
|
|
30424
|
+
|
|
28907
30425
|
// ../../packages/plugin-runtime/src/resolve.ts
|
|
28908
|
-
var
|
|
28909
|
-
var defaultGatewayFactory =
|
|
30426
|
+
var configuredGatewayFactory = (config2, meta3) => resolveGatewayForConfig(config2, meta3);
|
|
30427
|
+
var defaultGatewayFactory = configuredGatewayFactory;
|
|
28910
30428
|
function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
|
|
28911
30429
|
return gatewayFactory(config2, meta3);
|
|
28912
30430
|
}
|
|
28913
30431
|
|
|
28914
30432
|
// ../../packages/plugin-runtime/src/handle-session-start.ts
|
|
28915
|
-
import { randomUUID as
|
|
30433
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
28916
30434
|
var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
28917
30435
|
|
|
28918
30436
|
// src/command-registry.ts
|
|
28919
30437
|
import { readdirSync as readdirSync5 } from "fs";
|
|
28920
|
-
import { fileURLToPath as
|
|
30438
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
28921
30439
|
var COMMAND_NAMESPACE = "aka";
|
|
28922
|
-
var COMMANDS_DIR =
|
|
30440
|
+
var COMMANDS_DIR = fileURLToPath3(new URL("../commands", import.meta.url));
|
|
28923
30441
|
function readRegisteredCommands() {
|
|
28924
30442
|
return readdirSync5(COMMANDS_DIR).filter((f) => f.endsWith(".md")).map((f) => `/${COMMAND_NAMESPACE}:${f.replace(/\.md$/, "")}`);
|
|
28925
30443
|
}
|
|
@@ -29029,7 +30547,7 @@ function show(body) {
|
|
|
29029
30547
|
|
|
29030
30548
|
// ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
|
|
29031
30549
|
import { writeFileSync as writeFileSync7 } from "fs";
|
|
29032
|
-
import { join as
|
|
30550
|
+
import { join as join22 } from "path";
|
|
29033
30551
|
|
|
29034
30552
|
// ../../packages/setup-wizard/src/triage/merge.ts
|
|
29035
30553
|
var RANK = Object.fromEntries(
|
|
@@ -29037,9 +30555,9 @@ var RANK = Object.fromEntries(
|
|
|
29037
30555
|
);
|
|
29038
30556
|
|
|
29039
30557
|
// ../../packages/setup-wizard/src/triage/plan-file.ts
|
|
29040
|
-
import { mkdtempSync, readFileSync as
|
|
30558
|
+
import { mkdtempSync, readFileSync as readFileSync14, rmdirSync, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
29041
30559
|
import { tmpdir } from "os";
|
|
29042
|
-
import { basename as basename6, dirname as
|
|
30560
|
+
import { basename as basename6, dirname as dirname5, join as join23 } from "path";
|
|
29043
30561
|
var SuppressionEntrySchema = external_exports.object({
|
|
29044
30562
|
ruleId: external_exports.string(),
|
|
29045
30563
|
category: DetectionCategory,
|