@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/session-start.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
|
|
@@ -492,16 +492,33 @@ var require_ignore = __commonJS({
|
|
|
492
492
|
});
|
|
493
493
|
|
|
494
494
|
// src/hooks/session-start.ts
|
|
495
|
-
import { readFileSync as
|
|
495
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
496
|
+
|
|
497
|
+
// ../../packages/plugin-runtime/src/attached/failure.ts
|
|
498
|
+
function statusOf(err) {
|
|
499
|
+
if (typeof err !== "object" || err === null || !("status" in err)) return null;
|
|
500
|
+
const { status } = err;
|
|
501
|
+
if (typeof status !== "number" || !Number.isInteger(status)) return null;
|
|
502
|
+
return status >= 100 && status <= 599 ? status : null;
|
|
503
|
+
}
|
|
504
|
+
function classifyFailure(err) {
|
|
505
|
+
switch (statusOf(err)) {
|
|
506
|
+
case 401:
|
|
507
|
+
return "unauthorized";
|
|
508
|
+
case 403:
|
|
509
|
+
return "forbidden";
|
|
510
|
+
default:
|
|
511
|
+
return "unreachable";
|
|
512
|
+
}
|
|
513
|
+
}
|
|
496
514
|
|
|
497
|
-
// ../../packages/plugin-
|
|
498
|
-
import {
|
|
499
|
-
import { join as
|
|
515
|
+
// ../../packages/plugin-runtime/src/attached/forward-drops.ts
|
|
516
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
517
|
+
import { join as join10 } from "path";
|
|
500
518
|
|
|
501
|
-
// ../../packages/persistence/src/
|
|
502
|
-
import {
|
|
503
|
-
import { join
|
|
504
|
-
import { DatabaseSync } from "node:sqlite";
|
|
519
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
520
|
+
import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync2, statSync } from "fs";
|
|
521
|
+
import { join } from "path";
|
|
505
522
|
|
|
506
523
|
// ../../packages/schema/src/drizzle/sqlite-ddl.ts
|
|
507
524
|
var SQLITE_MIGRATIONS = [
|
|
@@ -16322,6 +16339,125 @@ var ConfigScanRecord = external_exports.object({
|
|
|
16322
16339
|
findings: external_exports.array(ConfigPostureFindingInput).optional()
|
|
16323
16340
|
});
|
|
16324
16341
|
|
|
16342
|
+
// ../../packages/schema/src/zod/control-plane.ts
|
|
16343
|
+
var ATTACHED_CREDENTIAL_FILENAME = "control-plane-credential.json";
|
|
16344
|
+
var ATTACHED_CREDENTIAL_SPEC_VERSION = 1;
|
|
16345
|
+
var AttachedCredential = external_exports.object({
|
|
16346
|
+
specVersion: external_exports.literal(ATTACHED_CREDENTIAL_SPEC_VERSION),
|
|
16347
|
+
// The control-plane endpoint this credential was minted against.
|
|
16348
|
+
endpoint: external_exports.string().min(1),
|
|
16349
|
+
// The bearer credential itself. Never logged, never rendered — status
|
|
16350
|
+
// surfaces show `keyPrefix` and nothing else.
|
|
16351
|
+
apiKey: external_exports.string().min(1),
|
|
16352
|
+
// First few characters of the key, safe to display so a user can match the
|
|
16353
|
+
// credential against their organization's key list.
|
|
16354
|
+
keyPrefix: external_exports.string().min(1).max(16).optional(),
|
|
16355
|
+
mintedAt: external_exports.iso.datetime().optional()
|
|
16356
|
+
});
|
|
16357
|
+
var MAX_DATE_MS = 253402300799999;
|
|
16358
|
+
var MAX_INT4 = 2147483647;
|
|
16359
|
+
var StorePosturePack = external_exports.object({
|
|
16360
|
+
packId: external_exports.string().min(1),
|
|
16361
|
+
// 'namespace/packId'
|
|
16362
|
+
version: external_exports.string().min(1),
|
|
16363
|
+
enabled: external_exports.boolean(),
|
|
16364
|
+
// Stringified pass-through of the local store's `installed_packs.updated_at`
|
|
16365
|
+
// — the column format is store-version-dependent (epoch millis vs ISO), so
|
|
16366
|
+
// the wire shape assumes neither.
|
|
16367
|
+
updatedAt: external_exports.string().nullable()
|
|
16368
|
+
}).meta({ id: "StorePosturePack" });
|
|
16369
|
+
var StorePosturePolicyCounts = external_exports.object({
|
|
16370
|
+
total: external_exports.number().int().min(0),
|
|
16371
|
+
disabled: external_exports.number().int().min(0),
|
|
16372
|
+
// Exhaustive per-action map; the builder pre-fills every action with 0.
|
|
16373
|
+
//
|
|
16374
|
+
// Spelled out member-by-member rather than `z.record(ActionTaken, …)`. Zod
|
|
16375
|
+
// enforces exhaustiveness either way, but z.record emits `propertyNames` +
|
|
16376
|
+
// `additionalProperties` into a generated schema document, and a type
|
|
16377
|
+
// generator renders THAT with every key optional — a sender built against
|
|
16378
|
+
// the generated type would typecheck and still be rejected at runtime. An
|
|
16379
|
+
// explicit object emits `properties` + `required`, so generated types
|
|
16380
|
+
// demand all five.
|
|
16381
|
+
//
|
|
16382
|
+
// `satisfies Record<ActionTaken, …>` keeps the link to the enum: adding an
|
|
16383
|
+
// ActionTaken member is a COMPILE error here instead of silent drift.
|
|
16384
|
+
// `.strict()` is load-bearing — it rejects an unknown action key, which a
|
|
16385
|
+
// bare object would silently STRIP, accepting a miscounted map as valid.
|
|
16386
|
+
byAction: external_exports.object({
|
|
16387
|
+
warn: external_exports.number().int().min(0),
|
|
16388
|
+
redact: external_exports.number().int().min(0),
|
|
16389
|
+
block: external_exports.number().int().min(0),
|
|
16390
|
+
allow: external_exports.number().int().min(0),
|
|
16391
|
+
log: external_exports.number().int().min(0)
|
|
16392
|
+
}).strict()
|
|
16393
|
+
}).meta({ id: "StorePosturePolicyCounts" });
|
|
16394
|
+
var StorePosturePlugin = external_exports.object({
|
|
16395
|
+
/** Package name of the reporting plugin. */
|
|
16396
|
+
package: external_exports.string().min(1).max(200),
|
|
16397
|
+
version: external_exports.string().min(1).max(64),
|
|
16398
|
+
/** Version of the bundled core, when the build records one separately. */
|
|
16399
|
+
ossVersion: external_exports.string().max(64).nullable(),
|
|
16400
|
+
/**
|
|
16401
|
+
* `version` of the policy bundle this machine last fetched. Bounded at 200
|
|
16402
|
+
* rather than the 64 a bare sha256 hex digest needs today, so a later
|
|
16403
|
+
* format with an algorithm prefix does not start rejecting the channel.
|
|
16404
|
+
*/
|
|
16405
|
+
policyBundleVersion: external_exports.string().max(200).nullable(),
|
|
16406
|
+
/** Epoch millis, on the CLIENT clock, of that fetch. */
|
|
16407
|
+
policyFetchedAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable()
|
|
16408
|
+
}).meta({ id: "StorePosturePlugin" });
|
|
16409
|
+
var StorePostureSnapshot = external_exports.object({
|
|
16410
|
+
deviceId: external_exports.guid(),
|
|
16411
|
+
hostname: external_exports.string().min(1).max(253),
|
|
16412
|
+
// Epoch millis on the CLIENT clock. Bounded by what a receiving store
|
|
16413
|
+
// accepts (see MAX_DATE_MS), not by what a JavaScript Date can hold.
|
|
16414
|
+
capturedAt: external_exports.number().int().min(0).max(MAX_DATE_MS),
|
|
16415
|
+
// False is a measurement, not an error state: "no local store exists on
|
|
16416
|
+
// this machine".
|
|
16417
|
+
storePresent: external_exports.boolean(),
|
|
16418
|
+
schemaVersion: external_exports.number().int().min(0).max(MAX_INT4).nullable(),
|
|
16419
|
+
// PRAGMA user_version
|
|
16420
|
+
findingsTotal: external_exports.number().int().min(0).max(MAX_INT4),
|
|
16421
|
+
// Epoch millis, bounded like `capturedAt` — see MAX_DATE_MS on what that
|
|
16422
|
+
// bound does and does not do. Worth stating for these two specifically:
|
|
16423
|
+
// they are read from the local store's own ROWS rather than from this
|
|
16424
|
+
// machine's clock, so a damaged or hand-edited store is enough to produce
|
|
16425
|
+
// an out-of-range value with no clock skew involved.
|
|
16426
|
+
findingsFirstAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16427
|
+
findingsLastAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
|
|
16428
|
+
packs: external_exports.array(StorePosturePack).max(500),
|
|
16429
|
+
policyCounts: StorePosturePolicyCounts,
|
|
16430
|
+
// OPTIONAL, not nullable: a reporter that predates this member keeps
|
|
16431
|
+
// getting its 200 without a payload change.
|
|
16432
|
+
plugin: StorePosturePlugin.optional()
|
|
16433
|
+
}).meta({ id: "StorePostureSnapshot" });
|
|
16434
|
+
var CAPTURE_VERSION_PREFIX = "capture/";
|
|
16435
|
+
var RecordAuditEventRequest = AuditEventInput.extend({
|
|
16436
|
+
inspections: external_exports.array(ToolCallInspection).default([])
|
|
16437
|
+
}).refine((v) => v.inspections.every((i) => !i.ruleVersion.startsWith(CAPTURE_VERSION_PREFIX)), {
|
|
16438
|
+
message: `inspections[].ruleVersion must not start with \`${CAPTURE_VERSION_PREFIX}\` \u2014 that namespace is reserved for capture definitions the control plane mints itself`,
|
|
16439
|
+
path: ["inspections"]
|
|
16440
|
+
}).meta({ id: "RecordAuditEventRequest" });
|
|
16441
|
+
var IngestAck = external_exports.object({
|
|
16442
|
+
accepted: external_exports.number().int().nonnegative(),
|
|
16443
|
+
duplicates: external_exports.number().int().nonnegative()
|
|
16444
|
+
});
|
|
16445
|
+
var PRINTABLE = /^[^\p{Cc}\p{Cf}]*$/u;
|
|
16446
|
+
var printable = (max) => external_exports.string().max(max).regex(PRINTABLE, "must not contain control characters");
|
|
16447
|
+
var PluginWhoami = external_exports.object({
|
|
16448
|
+
tenantName: printable(200),
|
|
16449
|
+
userEmail: printable(320),
|
|
16450
|
+
role: printable(64),
|
|
16451
|
+
keyKind: printable(64),
|
|
16452
|
+
serverTime: printable(64)
|
|
16453
|
+
});
|
|
16454
|
+
var ControlPlaneErrorBody = external_exports.object({
|
|
16455
|
+
error: external_exports.object({
|
|
16456
|
+
code: external_exports.string().optional(),
|
|
16457
|
+
message: external_exports.string().optional()
|
|
16458
|
+
}).optional()
|
|
16459
|
+
});
|
|
16460
|
+
|
|
16325
16461
|
// ../../packages/schema/src/zod/registry.ts
|
|
16326
16462
|
var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
16327
16463
|
var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
@@ -16624,15 +16760,15 @@ function summaryToDetectionListItem(s) {
|
|
|
16624
16760
|
}
|
|
16625
16761
|
function rowToDetectionDetail(row, findingsLast30d, update) {
|
|
16626
16762
|
const rules = row.rules.flatMap((r) => {
|
|
16627
|
-
const
|
|
16628
|
-
if (!
|
|
16763
|
+
const parsed2 = Matcher.safeParse(r.matcher);
|
|
16764
|
+
if (!parsed2.success) return [];
|
|
16629
16765
|
return [
|
|
16630
16766
|
{
|
|
16631
16767
|
id: r.id,
|
|
16632
16768
|
name: r.name,
|
|
16633
16769
|
category: r.category,
|
|
16634
16770
|
severity: r.severity,
|
|
16635
|
-
matcher:
|
|
16771
|
+
matcher: parsed2.data
|
|
16636
16772
|
}
|
|
16637
16773
|
];
|
|
16638
16774
|
});
|
|
@@ -17278,8 +17414,8 @@ function toApiAction(dbVal) {
|
|
|
17278
17414
|
}
|
|
17279
17415
|
function toApiCategory(dbVal) {
|
|
17280
17416
|
if (dbVal === "code_context") return "source_code";
|
|
17281
|
-
const
|
|
17282
|
-
return
|
|
17417
|
+
const parsed2 = FindingCategory.safeParse(dbVal);
|
|
17418
|
+
return parsed2.success ? parsed2.data : "custom";
|
|
17283
17419
|
}
|
|
17284
17420
|
function toApiProvider(sourceTool) {
|
|
17285
17421
|
return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
|
|
@@ -17912,6 +18048,9 @@ var WorkspaceSettings = external_exports.object({
|
|
|
17912
18048
|
function defaultWorkspaceSettings() {
|
|
17913
18049
|
return WorkspaceSettings.parse({});
|
|
17914
18050
|
}
|
|
18051
|
+
function isAttached(settings) {
|
|
18052
|
+
return settings.runMode === "attached" && settings.controlPlane !== void 0;
|
|
18053
|
+
}
|
|
17915
18054
|
function toInventoryRow(input, id, now) {
|
|
17916
18055
|
return {
|
|
17917
18056
|
id,
|
|
@@ -18179,8 +18318,8 @@ function builtinPolicyIsReversible(id) {
|
|
|
18179
18318
|
return BUILTIN_POLICY_SPECS[id].reversible;
|
|
18180
18319
|
}
|
|
18181
18320
|
function policyIdIsReversible(policyId) {
|
|
18182
|
-
const
|
|
18183
|
-
const id =
|
|
18321
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18322
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18184
18323
|
return builtinPolicyIsReversible(id);
|
|
18185
18324
|
}
|
|
18186
18325
|
var DEFAULT_ACTIONS = Object.fromEntries(
|
|
@@ -18191,8 +18330,8 @@ var BUILTIN_POLICIES = Object.fromEntries(
|
|
|
18191
18330
|
);
|
|
18192
18331
|
var DEFAULT_PACK_POLICY_ID = "monitor";
|
|
18193
18332
|
function policyIdToAction(policyId) {
|
|
18194
|
-
const
|
|
18195
|
-
const id =
|
|
18333
|
+
const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
|
|
18334
|
+
const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
|
|
18196
18335
|
return BUILTIN_POLICIES[id].action;
|
|
18197
18336
|
}
|
|
18198
18337
|
var UsedByItem = external_exports.object({
|
|
@@ -18635,53 +18774,6 @@ function reviewSeverityRank(reasons) {
|
|
|
18635
18774
|
return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
|
|
18636
18775
|
}
|
|
18637
18776
|
|
|
18638
|
-
// ../../packages/persistence/src/ids.ts
|
|
18639
|
-
import { createHash } from "crypto";
|
|
18640
|
-
function sha256Hex(input) {
|
|
18641
|
-
return createHash("sha256").update(input).digest("hex");
|
|
18642
|
-
}
|
|
18643
|
-
function inventoryId(objectType, identityKey) {
|
|
18644
|
-
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18645
|
-
}
|
|
18646
|
-
function sourceProjectId(url2) {
|
|
18647
|
-
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18648
|
-
}
|
|
18649
|
-
function classifiedDataId(cls) {
|
|
18650
|
-
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18651
|
-
}
|
|
18652
|
-
function inspectionDefinitionId(ruleId, version2) {
|
|
18653
|
-
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18654
|
-
}
|
|
18655
|
-
function llmCallId(sessionId, messageId) {
|
|
18656
|
-
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18657
|
-
}
|
|
18658
|
-
function toolCallId(sessionId, toolUseId) {
|
|
18659
|
-
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18660
|
-
}
|
|
18661
|
-
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18662
|
-
return sha256Hex(
|
|
18663
|
-
canonicalIdentity([
|
|
18664
|
-
"inspection_finding",
|
|
18665
|
-
auditEventId,
|
|
18666
|
-
ruleId,
|
|
18667
|
-
String(spanStart),
|
|
18668
|
-
String(spanEnd)
|
|
18669
|
-
])
|
|
18670
|
-
);
|
|
18671
|
-
}
|
|
18672
|
-
var NO_SESSION = "no_session";
|
|
18673
|
-
var NO_PATH = "no_path";
|
|
18674
|
-
function captureId(sessionId, contentHash, filePath = null) {
|
|
18675
|
-
return sha256Hex(
|
|
18676
|
-
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18677
|
-
);
|
|
18678
|
-
}
|
|
18679
|
-
|
|
18680
|
-
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18681
|
-
import { randomUUID } from "crypto";
|
|
18682
|
-
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
|
|
18683
|
-
import { basename, dirname, join } from "path";
|
|
18684
|
-
|
|
18685
18777
|
// ../../packages/persistence/src/paths.ts
|
|
18686
18778
|
import {
|
|
18687
18779
|
chmodSync,
|
|
@@ -18729,8 +18821,181 @@ function tightenFile(file2) {
|
|
|
18729
18821
|
function tightenPerms(file2) {
|
|
18730
18822
|
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18731
18823
|
}
|
|
18824
|
+
function writeExclusiveOwnerOnlySync(file2, data) {
|
|
18825
|
+
writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
|
|
18826
|
+
}
|
|
18827
|
+
function writeOwnerOnlyFileSync(file2, data) {
|
|
18828
|
+
const tmp = `${file2}.${String(process.pid)}.tmp`;
|
|
18829
|
+
try {
|
|
18830
|
+
rmSync(tmp, { force: true });
|
|
18831
|
+
} catch {
|
|
18832
|
+
}
|
|
18833
|
+
try {
|
|
18834
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18835
|
+
renameSync(tmp, file2);
|
|
18836
|
+
} finally {
|
|
18837
|
+
try {
|
|
18838
|
+
rmSync(tmp, { force: true });
|
|
18839
|
+
} catch {
|
|
18840
|
+
}
|
|
18841
|
+
}
|
|
18842
|
+
tightenFile(file2);
|
|
18843
|
+
}
|
|
18844
|
+
function createOwnerOnlyFileSync(file2, data) {
|
|
18845
|
+
const tmp = `${file2}.${String(process.pid)}.${String(threadId)}.new`;
|
|
18846
|
+
try {
|
|
18847
|
+
rmSync(tmp, { force: true });
|
|
18848
|
+
} catch {
|
|
18849
|
+
}
|
|
18850
|
+
let created;
|
|
18851
|
+
try {
|
|
18852
|
+
writeExclusiveOwnerOnlySync(tmp, data);
|
|
18853
|
+
created = publishByLink(tmp, file2, data);
|
|
18854
|
+
} finally {
|
|
18855
|
+
try {
|
|
18856
|
+
rmSync(tmp, { force: true });
|
|
18857
|
+
} catch {
|
|
18858
|
+
}
|
|
18859
|
+
}
|
|
18860
|
+
if (created) tightenFile(file2);
|
|
18861
|
+
return created;
|
|
18862
|
+
}
|
|
18863
|
+
var LINK_UNSUPPORTED = /* @__PURE__ */ new Set(["EPERM", "ENOSYS", "ENOTSUP", "EOPNOTSUPP", "EINVAL"]);
|
|
18864
|
+
function publishByLink(tmp, file2, data) {
|
|
18865
|
+
try {
|
|
18866
|
+
linkSync(tmp, file2);
|
|
18867
|
+
return true;
|
|
18868
|
+
} catch (err) {
|
|
18869
|
+
const code = err.code;
|
|
18870
|
+
if (code === "EEXIST") return false;
|
|
18871
|
+
if (!LINK_UNSUPPORTED.has(code ?? "")) throw err;
|
|
18872
|
+
}
|
|
18873
|
+
try {
|
|
18874
|
+
writeExclusiveOwnerOnlySync(file2, data);
|
|
18875
|
+
return true;
|
|
18876
|
+
} catch (err) {
|
|
18877
|
+
if (err.code === "EEXIST") return false;
|
|
18878
|
+
throw err;
|
|
18879
|
+
}
|
|
18880
|
+
}
|
|
18881
|
+
|
|
18882
|
+
// ../../packages/persistence/src/control-plane-credential.ts
|
|
18883
|
+
function controlPlaneCredentialPath(settingsDir2) {
|
|
18884
|
+
return join(settingsDir2, ATTACHED_CREDENTIAL_FILENAME);
|
|
18885
|
+
}
|
|
18886
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
18887
|
+
function isSafeEndpoint(endpoint) {
|
|
18888
|
+
let parsed2;
|
|
18889
|
+
try {
|
|
18890
|
+
parsed2 = new URL(endpoint);
|
|
18891
|
+
} catch {
|
|
18892
|
+
return false;
|
|
18893
|
+
}
|
|
18894
|
+
if (parsed2.protocol === "https:") return true;
|
|
18895
|
+
return parsed2.protocol === "http:" && LOOPBACK_HOSTS.has(parsed2.hostname);
|
|
18896
|
+
}
|
|
18897
|
+
function repairOrRefuseMode(file2) {
|
|
18898
|
+
const link = lstatSync2(file2, { throwIfNoEntry: false });
|
|
18899
|
+
if (link === void 0) return "absent";
|
|
18900
|
+
if (link.isSymbolicLink()) return "untrusted";
|
|
18901
|
+
const stat = statSync(file2, { throwIfNoEntry: false });
|
|
18902
|
+
if (stat === void 0) return "absent";
|
|
18903
|
+
const uid = process.getuid?.();
|
|
18904
|
+
if (uid !== void 0 && stat.uid !== uid) return "untrusted";
|
|
18905
|
+
if (process.platform !== "win32" && (stat.mode & 511) !== DATA_FILE_MODE) {
|
|
18906
|
+
try {
|
|
18907
|
+
chmodSync2(file2, DATA_FILE_MODE);
|
|
18908
|
+
} catch {
|
|
18909
|
+
return "untrusted";
|
|
18910
|
+
}
|
|
18911
|
+
}
|
|
18912
|
+
return "ok";
|
|
18913
|
+
}
|
|
18914
|
+
function readControlPlaneCredentialState(settingsDir2, connection) {
|
|
18915
|
+
const file2 = controlPlaneCredentialPath(settingsDir2);
|
|
18916
|
+
let raw;
|
|
18917
|
+
const gate = repairOrRefuseMode(file2);
|
|
18918
|
+
if (gate === "absent") return { usable: false, reason: "absent" };
|
|
18919
|
+
if (gate === "untrusted") return { usable: false, reason: "untrusted-file" };
|
|
18920
|
+
try {
|
|
18921
|
+
raw = readFileSync(file2, "utf8");
|
|
18922
|
+
} catch (err) {
|
|
18923
|
+
const code = err.code;
|
|
18924
|
+
return { usable: false, reason: code === "ENOENT" ? "absent" : "unreadable" };
|
|
18925
|
+
}
|
|
18926
|
+
let parsed2;
|
|
18927
|
+
try {
|
|
18928
|
+
parsed2 = JSON.parse(raw);
|
|
18929
|
+
} catch {
|
|
18930
|
+
return { usable: false, reason: "malformed" };
|
|
18931
|
+
}
|
|
18932
|
+
const result = AttachedCredential.safeParse(parsed2);
|
|
18933
|
+
if (!result.success) return { usable: false, reason: "malformed" };
|
|
18934
|
+
if (!isSafeEndpoint(result.data.endpoint)) {
|
|
18935
|
+
return { usable: false, reason: "unsafe-endpoint" };
|
|
18936
|
+
}
|
|
18937
|
+
if (connection !== void 0 && connection.endpoint !== result.data.endpoint) {
|
|
18938
|
+
return {
|
|
18939
|
+
usable: false,
|
|
18940
|
+
reason: "endpoint-mismatch",
|
|
18941
|
+
credentialEndpoint: result.data.endpoint,
|
|
18942
|
+
settingsEndpoint: connection.endpoint
|
|
18943
|
+
};
|
|
18944
|
+
}
|
|
18945
|
+
return { usable: true, credential: result.data };
|
|
18946
|
+
}
|
|
18947
|
+
|
|
18948
|
+
// ../../packages/persistence/src/database.ts
|
|
18949
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
18950
|
+
import { join as join3, sep } from "path";
|
|
18951
|
+
import { DatabaseSync } from "node:sqlite";
|
|
18952
|
+
|
|
18953
|
+
// ../../packages/persistence/src/ids.ts
|
|
18954
|
+
import { createHash } from "crypto";
|
|
18955
|
+
function sha256Hex(input) {
|
|
18956
|
+
return createHash("sha256").update(input).digest("hex");
|
|
18957
|
+
}
|
|
18958
|
+
function inventoryId(objectType, identityKey) {
|
|
18959
|
+
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
18960
|
+
}
|
|
18961
|
+
function sourceProjectId(url2) {
|
|
18962
|
+
return sha256Hex(canonicalIdentity(["source_project", url2]));
|
|
18963
|
+
}
|
|
18964
|
+
function classifiedDataId(cls) {
|
|
18965
|
+
return sha256Hex(canonicalIdentity(["classified_data", cls]));
|
|
18966
|
+
}
|
|
18967
|
+
function inspectionDefinitionId(ruleId, version2) {
|
|
18968
|
+
return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
|
|
18969
|
+
}
|
|
18970
|
+
function llmCallId(sessionId, messageId) {
|
|
18971
|
+
return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
|
|
18972
|
+
}
|
|
18973
|
+
function toolCallId(sessionId, toolUseId) {
|
|
18974
|
+
return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
|
|
18975
|
+
}
|
|
18976
|
+
function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
|
|
18977
|
+
return sha256Hex(
|
|
18978
|
+
canonicalIdentity([
|
|
18979
|
+
"inspection_finding",
|
|
18980
|
+
auditEventId,
|
|
18981
|
+
ruleId,
|
|
18982
|
+
String(spanStart),
|
|
18983
|
+
String(spanEnd)
|
|
18984
|
+
])
|
|
18985
|
+
);
|
|
18986
|
+
}
|
|
18987
|
+
var NO_SESSION = "no_session";
|
|
18988
|
+
var NO_PATH = "no_path";
|
|
18989
|
+
function captureId(sessionId, contentHash, filePath = null) {
|
|
18990
|
+
return sha256Hex(
|
|
18991
|
+
canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
|
|
18992
|
+
);
|
|
18993
|
+
}
|
|
18732
18994
|
|
|
18733
18995
|
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18996
|
+
import { randomUUID } from "crypto";
|
|
18997
|
+
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
18998
|
+
import { basename, dirname, join as join2 } from "path";
|
|
18734
18999
|
function backupPath(file2, tag) {
|
|
18735
19000
|
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18736
19001
|
}
|
|
@@ -18740,15 +19005,15 @@ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
|
|
|
18740
19005
|
var SNAPSHOT_STAGING_COPY = "copy";
|
|
18741
19006
|
function createSnapshotStaging(backup) {
|
|
18742
19007
|
const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
|
|
18743
|
-
|
|
19008
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18744
19009
|
mkdirOwnerOnlySync(stage);
|
|
18745
19010
|
tightenDir(stage);
|
|
18746
|
-
return { stage, copy:
|
|
19011
|
+
return { stage, copy: join2(stage, SNAPSHOT_STAGING_COPY) };
|
|
18747
19012
|
}
|
|
18748
19013
|
function idleMs(entry) {
|
|
18749
|
-
for (const candidate of [
|
|
19014
|
+
for (const candidate of [join2(entry, SNAPSHOT_STAGING_COPY), entry]) {
|
|
18750
19015
|
try {
|
|
18751
|
-
return Date.now() -
|
|
19016
|
+
return Date.now() - statSync2(candidate).mtimeMs;
|
|
18752
19017
|
} catch {
|
|
18753
19018
|
}
|
|
18754
19019
|
}
|
|
@@ -18765,11 +19030,11 @@ function reapStalePartials(file2) {
|
|
|
18765
19030
|
}
|
|
18766
19031
|
for (const name of entries) {
|
|
18767
19032
|
if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
|
|
18768
|
-
const staging =
|
|
19033
|
+
const staging = join2(dir, name);
|
|
18769
19034
|
try {
|
|
18770
19035
|
const idle = idleMs(staging);
|
|
18771
19036
|
if (idle !== null && idle > STALE_PARTIAL_MS) {
|
|
18772
|
-
|
|
19037
|
+
rmSync3(staging, { recursive: true, force: true });
|
|
18773
19038
|
}
|
|
18774
19039
|
} catch {
|
|
18775
19040
|
}
|
|
@@ -18783,13 +19048,13 @@ function snapshotStore(db, backup) {
|
|
|
18783
19048
|
renameSync2(copy, backup);
|
|
18784
19049
|
} catch (error51) {
|
|
18785
19050
|
try {
|
|
18786
|
-
|
|
19051
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18787
19052
|
} catch {
|
|
18788
19053
|
}
|
|
18789
19054
|
throw error51;
|
|
18790
19055
|
}
|
|
18791
19056
|
try {
|
|
18792
|
-
|
|
19057
|
+
rmSync3(stage, { recursive: true, force: true });
|
|
18793
19058
|
} catch {
|
|
18794
19059
|
}
|
|
18795
19060
|
}
|
|
@@ -18804,7 +19069,7 @@ function moveStoreAside(file2, backup) {
|
|
|
18804
19069
|
renameSync2(sidecar, moved);
|
|
18805
19070
|
undo.push([moved, sidecar]);
|
|
18806
19071
|
} catch {
|
|
18807
|
-
|
|
19072
|
+
rmSync3(sidecar, { force: true });
|
|
18808
19073
|
}
|
|
18809
19074
|
}
|
|
18810
19075
|
} catch (error51) {
|
|
@@ -18820,14 +19085,14 @@ function moveStoreAside(file2, backup) {
|
|
|
18820
19085
|
}
|
|
18821
19086
|
function discardStore(file2, backup) {
|
|
18822
19087
|
try {
|
|
18823
|
-
|
|
19088
|
+
rmSync3(file2, { force: true });
|
|
18824
19089
|
for (const sidecar of dbSidecars(file2)) {
|
|
18825
|
-
|
|
19090
|
+
rmSync3(sidecar, { force: true });
|
|
18826
19091
|
}
|
|
18827
19092
|
} catch (error51) {
|
|
18828
19093
|
if (existsSync(file2)) {
|
|
18829
19094
|
try {
|
|
18830
|
-
|
|
19095
|
+
rmSync3(backup, { force: true });
|
|
18831
19096
|
} catch {
|
|
18832
19097
|
}
|
|
18833
19098
|
}
|
|
@@ -19059,10 +19324,31 @@ function applyMigrations(db, file2) {
|
|
|
19059
19324
|
if (drained) applyLegacyDropMigration(db, file2);
|
|
19060
19325
|
}
|
|
19061
19326
|
}
|
|
19327
|
+
function readLegacyTables(db) {
|
|
19328
|
+
let holdsRows = false;
|
|
19329
|
+
const marks = [];
|
|
19330
|
+
for (const table of ["events", "findings"]) {
|
|
19331
|
+
try {
|
|
19332
|
+
const row = db.prepare(`SELECT count(*) AS n, ifnull(max(rowid), -1) AS hi FROM ${table}`).get();
|
|
19333
|
+
if (row === void 0) {
|
|
19334
|
+
holdsRows = true;
|
|
19335
|
+
marks.push(`${table}:unreadable`);
|
|
19336
|
+
continue;
|
|
19337
|
+
}
|
|
19338
|
+
if (row.n > 0) holdsRows = true;
|
|
19339
|
+
marks.push(`${table}:${String(row.n)}:${String(row.hi)}`);
|
|
19340
|
+
} catch {
|
|
19341
|
+
holdsRows = true;
|
|
19342
|
+
marks.push(`${table}:unreadable`);
|
|
19343
|
+
}
|
|
19344
|
+
}
|
|
19345
|
+
return { holdsRows, mark: marks.join("|") };
|
|
19346
|
+
}
|
|
19062
19347
|
function applyLegacyDropMigration(db, file2) {
|
|
19063
19348
|
const migration = SQLITE_MIGRATIONS.find((m) => m.tag === LEGACY_DROP_MIGRATION_TAG);
|
|
19064
19349
|
if (!migration) return;
|
|
19065
|
-
|
|
19350
|
+
const before = file2 === void 0 ? void 0 : readLegacyTables(db);
|
|
19351
|
+
if (file2 !== void 0 && before?.holdsRows === true) {
|
|
19066
19352
|
try {
|
|
19067
19353
|
backupBeforeLegacyDrop(db, file2);
|
|
19068
19354
|
} catch (error51) {
|
|
@@ -19076,6 +19362,12 @@ function applyLegacyDropMigration(db, file2) {
|
|
|
19076
19362
|
() => {
|
|
19077
19363
|
const alreadyDropped = db.prepare("SELECT 1 FROM migration_ledger WHERE tag = ?").get(migration.tag);
|
|
19078
19364
|
if (alreadyDropped) return;
|
|
19365
|
+
if (before !== void 0 && readLegacyTables(db).mark !== before.mark) {
|
|
19366
|
+
akaWarn(
|
|
19367
|
+
"legacy events/findings rows changed after the pre-drop snapshot decision; deferring the drop so the next open can copy them first."
|
|
19368
|
+
);
|
|
19369
|
+
return;
|
|
19370
|
+
}
|
|
19079
19371
|
for (const statement of splitStatements(migration.sql)) {
|
|
19080
19372
|
db.exec(statement);
|
|
19081
19373
|
}
|
|
@@ -19430,8 +19722,8 @@ function safeJson(s, fallback) {
|
|
|
19430
19722
|
function parseJsonObject(s) {
|
|
19431
19723
|
if (s == null) return void 0;
|
|
19432
19724
|
try {
|
|
19433
|
-
const
|
|
19434
|
-
if (typeof
|
|
19725
|
+
const parsed2 = JSON.parse(s);
|
|
19726
|
+
if (typeof parsed2 === "object" && parsed2 !== null) return parsed2;
|
|
19435
19727
|
} catch {
|
|
19436
19728
|
}
|
|
19437
19729
|
return void 0;
|
|
@@ -19442,16 +19734,16 @@ function encodeKeysetCursor(payload) {
|
|
|
19442
19734
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
19443
19735
|
}
|
|
19444
19736
|
function decodeKeysetCursor(cursor) {
|
|
19445
|
-
const
|
|
19446
|
-
if (
|
|
19737
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
19738
|
+
if (parsed2 !== void 0 && "startedAtMs" in parsed2 && "id" in parsed2 && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
|
|
19447
19739
|
// resumes from is epoch millis, and a payload carrying ±Infinity or a
|
|
19448
19740
|
// fraction binds cleanly rather than failing — returning an EMPTY page with
|
|
19449
19741
|
// a null cursor, which a caller reads as "end of list". That is the one
|
|
19450
19742
|
// outcome a cursor that does not decode must never produce, since the
|
|
19451
19743
|
// documented behaviour above is to restart from the top. (`1e999` is valid
|
|
19452
19744
|
// JSON and parses to Infinity; a bare `NaN` is not, so it cannot arrive.)
|
|
19453
|
-
Number.isInteger(
|
|
19454
|
-
return
|
|
19745
|
+
Number.isInteger(parsed2.startedAtMs) && typeof parsed2.id === "string") {
|
|
19746
|
+
return parsed2;
|
|
19455
19747
|
}
|
|
19456
19748
|
return null;
|
|
19457
19749
|
}
|
|
@@ -19516,18 +19808,18 @@ var DB_EVENT_TYPE_TO_KIND = {
|
|
|
19516
19808
|
};
|
|
19517
19809
|
function safeParseStringArray(raw) {
|
|
19518
19810
|
if (!raw) return [];
|
|
19519
|
-
const
|
|
19520
|
-
return Array.isArray(
|
|
19811
|
+
const parsed2 = safeJson(raw, null);
|
|
19812
|
+
return Array.isArray(parsed2) ? parsed2 : [];
|
|
19521
19813
|
}
|
|
19522
19814
|
var DEFAULT_HARNESS = HARNESS.ClaudeCode;
|
|
19523
19815
|
function toHarness(raw) {
|
|
19524
|
-
const
|
|
19525
|
-
return
|
|
19816
|
+
const parsed2 = Harness.safeParse(raw);
|
|
19817
|
+
return parsed2.success ? parsed2.data : DEFAULT_HARNESS;
|
|
19526
19818
|
}
|
|
19527
19819
|
function resolveLifecycle(row, lastActivityMs, nowMs) {
|
|
19528
19820
|
if (row.status) {
|
|
19529
|
-
const
|
|
19530
|
-
if (
|
|
19821
|
+
const parsed2 = SessionStatus.safeParse(row.status);
|
|
19822
|
+
if (parsed2.success) return { status: parsed2.data, endedAtMs: row.ended_at };
|
|
19531
19823
|
}
|
|
19532
19824
|
if (row.ended_at !== null) return { status: "completed", endedAtMs: row.ended_at };
|
|
19533
19825
|
if (lastActivityMs >= nowMs - LIVE_ACTIVITY_WINDOW_MS) {
|
|
@@ -20486,9 +20778,9 @@ var SqliteDetectionsRepository = class {
|
|
|
20486
20778
|
const ruleIds = /* @__PURE__ */ new Set();
|
|
20487
20779
|
for (const r of rows) {
|
|
20488
20780
|
if (intToBool(r.enabled)) active += 1;
|
|
20489
|
-
const
|
|
20490
|
-
rules +=
|
|
20491
|
-
for (const rule of
|
|
20781
|
+
const parsed2 = parseRules(r.rulesJson);
|
|
20782
|
+
rules += parsed2.length;
|
|
20783
|
+
for (const rule of parsed2) {
|
|
20492
20784
|
if (typeof rule.id === "string") ruleIds.add(rule.id);
|
|
20493
20785
|
}
|
|
20494
20786
|
}
|
|
@@ -21022,12 +21314,12 @@ function encodeGroupCursor(group) {
|
|
|
21022
21314
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
21023
21315
|
}
|
|
21024
21316
|
function decodeGroupCursor(cursor) {
|
|
21025
|
-
const
|
|
21026
|
-
if (
|
|
21317
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
21318
|
+
if (parsed2 !== void 0 && typeof parsed2.sev === "string" && typeof parsed2.t === "string" && typeof parsed2.id === "string") {
|
|
21027
21319
|
return {
|
|
21028
|
-
severity:
|
|
21029
|
-
latestDetectedAt:
|
|
21030
|
-
id:
|
|
21320
|
+
severity: parsed2.sev,
|
|
21321
|
+
latestDetectedAt: parsed2.t,
|
|
21322
|
+
id: parsed2.id
|
|
21031
21323
|
};
|
|
21032
21324
|
}
|
|
21033
21325
|
return null;
|
|
@@ -22161,16 +22453,16 @@ var SqliteInstalledPacksRepository = class {
|
|
|
22161
22453
|
continue;
|
|
22162
22454
|
}
|
|
22163
22455
|
for (const entry of raw) {
|
|
22164
|
-
const
|
|
22165
|
-
if (
|
|
22166
|
-
out.rules.push(
|
|
22167
|
-
out.ruleActions.set(
|
|
22168
|
-
out.ruleVersions.set(
|
|
22169
|
-
if (reversible) out.reversibleRules.add(
|
|
22170
|
-
else out.reversibleRules.delete(
|
|
22456
|
+
const parsed2 = Rule.safeParse(entry);
|
|
22457
|
+
if (parsed2.success) {
|
|
22458
|
+
out.rules.push(parsed2.data);
|
|
22459
|
+
out.ruleActions.set(parsed2.data.id, action);
|
|
22460
|
+
out.ruleVersions.set(parsed2.data.id, row.version);
|
|
22461
|
+
if (reversible) out.reversibleRules.add(parsed2.data.id);
|
|
22462
|
+
else out.reversibleRules.delete(parsed2.data.id);
|
|
22171
22463
|
} else {
|
|
22172
22464
|
out.invalidRules += 1;
|
|
22173
|
-
reject(pack, printableRuleId(entry), firstIssueReason(
|
|
22465
|
+
reject(pack, printableRuleId(entry), firstIssueReason(parsed2.error));
|
|
22174
22466
|
}
|
|
22175
22467
|
}
|
|
22176
22468
|
}
|
|
@@ -23560,15 +23852,15 @@ function encodeReuseCursor(payload) {
|
|
|
23560
23852
|
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
23561
23853
|
}
|
|
23562
23854
|
function decodeReuseCursor(cursor) {
|
|
23563
|
-
const
|
|
23564
|
-
if (
|
|
23855
|
+
const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
23856
|
+
if (parsed2 !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
|
|
23565
23857
|
// ±Infinity or a fraction binds cleanly and returns an EMPTY page with a
|
|
23566
23858
|
// null cursor, which the caller reads as "end of list" — the one outcome a
|
|
23567
23859
|
// malformed cursor must never produce, since restarting from the top is the
|
|
23568
23860
|
// documented behaviour and the only recoverable one. (`1e999` is valid JSON
|
|
23569
23861
|
// and parses to Infinity; a bare `NaN` is not, so it cannot arrive here.)
|
|
23570
|
-
Number.isInteger(
|
|
23571
|
-
return { occurrences:
|
|
23862
|
+
Number.isInteger(parsed2.occurrences) && typeof parsed2.pointerId === "string") {
|
|
23863
|
+
return { occurrences: parsed2.occurrences, pointerId: parsed2.pointerId };
|
|
23572
23864
|
}
|
|
23573
23865
|
return null;
|
|
23574
23866
|
}
|
|
@@ -25297,7 +25589,7 @@ function openAndInitialize(file2) {
|
|
|
25297
25589
|
}
|
|
25298
25590
|
function openLocalDatabase(dir) {
|
|
25299
25591
|
ensureDataDirSync(dir);
|
|
25300
|
-
const file2 =
|
|
25592
|
+
const file2 = join3(dir, DB_FILENAME);
|
|
25301
25593
|
reapStalePartials(file2);
|
|
25302
25594
|
const {
|
|
25303
25595
|
db,
|
|
@@ -25533,9 +25825,9 @@ import {
|
|
|
25533
25825
|
closeSync,
|
|
25534
25826
|
existsSync as existsSync2,
|
|
25535
25827
|
openSync,
|
|
25536
|
-
readFileSync,
|
|
25537
|
-
rmSync as
|
|
25538
|
-
statSync as
|
|
25828
|
+
readFileSync as readFileSync2,
|
|
25829
|
+
rmSync as rmSync4,
|
|
25830
|
+
statSync as statSync3,
|
|
25539
25831
|
writeFileSync as writeFileSync2
|
|
25540
25832
|
} from "fs";
|
|
25541
25833
|
import { hostname as hostname3 } from "os";
|
|
@@ -25546,20 +25838,20 @@ import { createHash as createHash3 } from "crypto";
|
|
|
25546
25838
|
|
|
25547
25839
|
// ../../packages/persistence/src/fingerprint.ts
|
|
25548
25840
|
import { createHmac, randomBytes } from "crypto";
|
|
25549
|
-
import { existsSync as existsSync3, readFileSync as
|
|
25550
|
-
import { join as
|
|
25841
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
25842
|
+
import { join as join4 } from "path";
|
|
25551
25843
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
25552
25844
|
var EXCEPTION_KEY_FILENAME = "exception.key";
|
|
25553
25845
|
var KEY_MATERIAL_BYTES = 32;
|
|
25554
25846
|
function keyFilePath(dataDir2) {
|
|
25555
|
-
return
|
|
25847
|
+
return join4(dataDir2, EXCEPTION_KEY_FILENAME);
|
|
25556
25848
|
}
|
|
25557
25849
|
function parseKeyFile(raw) {
|
|
25558
|
-
const
|
|
25559
|
-
if (typeof
|
|
25850
|
+
const parsed2 = JSON.parse(raw);
|
|
25851
|
+
if (typeof parsed2 !== "object" || parsed2 === null) {
|
|
25560
25852
|
throw new Error("exception key file is corrupt: not a JSON object");
|
|
25561
25853
|
}
|
|
25562
|
-
const { version: version2, material } =
|
|
25854
|
+
const { version: version2, material } = parsed2;
|
|
25563
25855
|
if (typeof version2 !== "number" || !Number.isInteger(version2) || version2 < 1) {
|
|
25564
25856
|
throw new Error("exception key file is corrupt: bad version");
|
|
25565
25857
|
}
|
|
@@ -25575,7 +25867,7 @@ function parseKeyFile(raw) {
|
|
|
25575
25867
|
function readFingerprintKey(dataDir2) {
|
|
25576
25868
|
let raw;
|
|
25577
25869
|
try {
|
|
25578
|
-
raw =
|
|
25870
|
+
raw = readFileSync3(keyFilePath(dataDir2), "utf8");
|
|
25579
25871
|
} catch (err) {
|
|
25580
25872
|
if (err.code === "ENOENT") return null;
|
|
25581
25873
|
throw err instanceof Error ? err : new Error(String(err));
|
|
@@ -25587,18 +25879,25 @@ function readFingerprintKey(dataDir2) {
|
|
|
25587
25879
|
import { renameSync as renameSync3 } from "fs";
|
|
25588
25880
|
import { mkdir } from "fs/promises";
|
|
25589
25881
|
import { homedir } from "os";
|
|
25590
|
-
import { join as
|
|
25882
|
+
import { join as join5 } from "path";
|
|
25591
25883
|
function defaultDataDir() {
|
|
25592
|
-
return
|
|
25884
|
+
return join5(homedir(), ".aka");
|
|
25593
25885
|
}
|
|
25594
25886
|
function settingsDir(base = defaultDataDir()) {
|
|
25595
|
-
return
|
|
25887
|
+
return join5(base, "settings");
|
|
25596
25888
|
}
|
|
25597
25889
|
function dataDir(base = defaultDataDir()) {
|
|
25598
|
-
return
|
|
25890
|
+
return join5(base, "data");
|
|
25599
25891
|
}
|
|
25600
25892
|
function dbPath(base = defaultDataDir()) {
|
|
25601
|
-
return
|
|
25893
|
+
return join5(dataDir(base), "aka.db");
|
|
25894
|
+
}
|
|
25895
|
+
function keysDir(base = defaultDataDir()) {
|
|
25896
|
+
return join5(base, "keys");
|
|
25897
|
+
}
|
|
25898
|
+
async function ensureDataDir(dir = defaultDataDir()) {
|
|
25899
|
+
await mkdir(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
25900
|
+
tightenDir(dir);
|
|
25602
25901
|
}
|
|
25603
25902
|
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
25604
25903
|
ensureDataDirSync(dir);
|
|
@@ -25611,8 +25910,8 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25611
25910
|
for (const { name, dest } of moves) {
|
|
25612
25911
|
try {
|
|
25613
25912
|
ensureDataDirSync(dest);
|
|
25614
|
-
const moved =
|
|
25615
|
-
renameSync3(
|
|
25913
|
+
const moved = join5(dest, name);
|
|
25914
|
+
renameSync3(join5(base, name), moved);
|
|
25616
25915
|
tightenFile(moved);
|
|
25617
25916
|
} catch {
|
|
25618
25917
|
}
|
|
@@ -25620,7 +25919,7 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
25620
25919
|
}
|
|
25621
25920
|
|
|
25622
25921
|
// ../../packages/persistence/src/managed-settings.ts
|
|
25623
|
-
import { readFileSync as
|
|
25922
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
25624
25923
|
import { posix, win32 } from "path";
|
|
25625
25924
|
function managedSettingsPaths(platform2 = process.platform) {
|
|
25626
25925
|
if (platform2 === "darwin") {
|
|
@@ -25638,14 +25937,14 @@ function readManagedSettings(paths = managedSettingsPaths()) {
|
|
|
25638
25937
|
for (const path of paths) {
|
|
25639
25938
|
let text;
|
|
25640
25939
|
try {
|
|
25641
|
-
text =
|
|
25940
|
+
text = readFileSync4(path, "utf8");
|
|
25642
25941
|
} catch {
|
|
25643
25942
|
continue;
|
|
25644
25943
|
}
|
|
25645
25944
|
const record2 = parseJsonObject(text);
|
|
25646
25945
|
if (!record2) continue;
|
|
25647
|
-
const
|
|
25648
|
-
if (
|
|
25946
|
+
const parsed2 = ManagedSettings.safeParse(record2);
|
|
25947
|
+
if (parsed2.success) return parsed2.data;
|
|
25649
25948
|
}
|
|
25650
25949
|
return null;
|
|
25651
25950
|
}
|
|
@@ -25685,14 +25984,14 @@ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ n
|
|
|
25685
25984
|
}
|
|
25686
25985
|
|
|
25687
25986
|
// ../../packages/persistence/src/settings.ts
|
|
25688
|
-
import { readFileSync as
|
|
25689
|
-
import { join as
|
|
25987
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
25988
|
+
import { join as join6 } from "path";
|
|
25690
25989
|
var SETTINGS_FILENAME = "settings.json";
|
|
25691
25990
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25692
25991
|
return overlayManagedSettings(readUserSettings(base), readManagedSettings());
|
|
25693
25992
|
}
|
|
25694
25993
|
function readUserSettings(base) {
|
|
25695
|
-
const record2 = readJson(
|
|
25994
|
+
const record2 = readJson(join6(settingsDir(base), SETTINGS_FILENAME));
|
|
25696
25995
|
if (!record2) return defaultWorkspaceSettings();
|
|
25697
25996
|
try {
|
|
25698
25997
|
return WorkspaceSettings.parse(record2);
|
|
@@ -25703,13 +26002,64 @@ function readUserSettings(base) {
|
|
|
25703
26002
|
function readJson(file2) {
|
|
25704
26003
|
let text;
|
|
25705
26004
|
try {
|
|
25706
|
-
text =
|
|
26005
|
+
text = readFileSync5(file2, "utf8");
|
|
25707
26006
|
} catch {
|
|
25708
26007
|
return null;
|
|
25709
26008
|
}
|
|
25710
26009
|
return parseJsonObject(text) ?? null;
|
|
25711
26010
|
}
|
|
25712
26011
|
|
|
26012
|
+
// ../../packages/persistence/src/store-symlinks.ts
|
|
26013
|
+
import { existsSync as existsSync4, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
|
|
26014
|
+
import { dirname as dirname2, join as join7, resolve } from "path";
|
|
26015
|
+
var STORE_DB = "the store database (including the prompt corpus)";
|
|
26016
|
+
var STORE_SETTINGS = "your settings file";
|
|
26017
|
+
function storeContents(home) {
|
|
26018
|
+
return /* @__PURE__ */ new Map([
|
|
26019
|
+
[home, "the store (including the prompt corpus in aka.db)"],
|
|
26020
|
+
[settingsDir(home), STORE_SETTINGS],
|
|
26021
|
+
[dataDir(home), STORE_DB],
|
|
26022
|
+
[keysDir(home), "the vault key"],
|
|
26023
|
+
[join7(settingsDir(home), "settings.json"), STORE_SETTINGS],
|
|
26024
|
+
[dbPath(home), STORE_DB]
|
|
26025
|
+
]);
|
|
26026
|
+
}
|
|
26027
|
+
function symlinkedStorePaths(home, platform2 = process.platform) {
|
|
26028
|
+
return [...storeContents(home)].flatMap(([path, holds]) => {
|
|
26029
|
+
try {
|
|
26030
|
+
if (!lstatSync3(path).isSymbolicLink()) return [];
|
|
26031
|
+
return [
|
|
26032
|
+
{
|
|
26033
|
+
path,
|
|
26034
|
+
target: linkTarget(path),
|
|
26035
|
+
holds,
|
|
26036
|
+
// existsSync follows the link, so a target that is gone reads as
|
|
26037
|
+
// absent here while lstat above still sees the link itself.
|
|
26038
|
+
missing: !existsSync4(path),
|
|
26039
|
+
mode: targetMode(path, platform2)
|
|
26040
|
+
}
|
|
26041
|
+
];
|
|
26042
|
+
} catch {
|
|
26043
|
+
return [];
|
|
26044
|
+
}
|
|
26045
|
+
});
|
|
26046
|
+
}
|
|
26047
|
+
function linkTarget(path) {
|
|
26048
|
+
try {
|
|
26049
|
+
return realpathSync(path);
|
|
26050
|
+
} catch {
|
|
26051
|
+
return resolve(dirname2(path), readlinkSync(path));
|
|
26052
|
+
}
|
|
26053
|
+
}
|
|
26054
|
+
function targetMode(path, platform2) {
|
|
26055
|
+
if (platform2 === "win32") return void 0;
|
|
26056
|
+
try {
|
|
26057
|
+
return statSync4(path).mode & 511;
|
|
26058
|
+
} catch {
|
|
26059
|
+
return void 0;
|
|
26060
|
+
}
|
|
26061
|
+
}
|
|
26062
|
+
|
|
25713
26063
|
// ../../packages/persistence/src/vault/crypto.ts
|
|
25714
26064
|
import {
|
|
25715
26065
|
createCipheriv,
|
|
@@ -25722,26 +26072,73 @@ import {
|
|
|
25722
26072
|
// ../../packages/persistence/src/vault/key-provider.ts
|
|
25723
26073
|
import { execFileSync } from "child_process";
|
|
25724
26074
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
25725
|
-
import { chmodSync as
|
|
25726
|
-
import { join as
|
|
26075
|
+
import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
26076
|
+
import { join as join8 } from "path";
|
|
25727
26077
|
|
|
25728
26078
|
// ../../packages/persistence/src/vault/vault.ts
|
|
25729
26079
|
import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
|
|
25730
26080
|
|
|
25731
26081
|
// ../../packages/persistence/src/warn-era-cap.ts
|
|
25732
|
-
import { existsSync as
|
|
25733
|
-
import { join as
|
|
26082
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
26083
|
+
import { join as join9 } from "path";
|
|
25734
26084
|
var MARKER = "warn-era-capped";
|
|
25735
26085
|
function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
|
|
25736
26086
|
if (policyMode !== "warn") return { capped: 0, skipped: "not-warn" };
|
|
25737
|
-
const marker =
|
|
25738
|
-
if (
|
|
26087
|
+
const marker = join9(dataDir2, MARKER);
|
|
26088
|
+
if (existsSync5(marker)) return { capped: 0, skipped: "already-run" };
|
|
25739
26089
|
const capped = db.policies.capCategoryActions();
|
|
25740
26090
|
writeFileSync4(marker, `${new Date(Date.now()).toISOString()}
|
|
25741
26091
|
`, { mode: DATA_FILE_MODE });
|
|
25742
26092
|
return { capped };
|
|
25743
26093
|
}
|
|
25744
26094
|
|
|
26095
|
+
// ../../packages/plugin-runtime/src/attached/forward-drops.ts
|
|
26096
|
+
var FORWARD_DROPS_FILENAME = "attached-forward-drops.json";
|
|
26097
|
+
function forwardDropsPath(dataDir2) {
|
|
26098
|
+
return join10(dataDir2, FORWARD_DROPS_FILENAME);
|
|
26099
|
+
}
|
|
26100
|
+
function recordForwardDrops(dataDir2, count, nowMs) {
|
|
26101
|
+
if (count <= 0) return;
|
|
26102
|
+
try {
|
|
26103
|
+
ensureDataDirSync(dataDir2);
|
|
26104
|
+
const previous = readForwardDrops(dataDir2);
|
|
26105
|
+
const next = {
|
|
26106
|
+
droppedForwards: (previous?.droppedForwards ?? 0) + count,
|
|
26107
|
+
lastDropAtMs: nowMs
|
|
26108
|
+
};
|
|
26109
|
+
writeOwnerOnlyFileSync(forwardDropsPath(dataDir2), `${JSON.stringify(next)}
|
|
26110
|
+
`);
|
|
26111
|
+
} catch {
|
|
26112
|
+
}
|
|
26113
|
+
}
|
|
26114
|
+
function readForwardDrops(dataDir2) {
|
|
26115
|
+
try {
|
|
26116
|
+
const parsed2 = JSON.parse(readFileSync7(forwardDropsPath(dataDir2), "utf8"));
|
|
26117
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
26118
|
+
const record2 = parsed2;
|
|
26119
|
+
if (typeof record2.droppedForwards !== "number" || !Number.isFinite(record2.droppedForwards)) {
|
|
26120
|
+
return null;
|
|
26121
|
+
}
|
|
26122
|
+
if (record2.droppedForwards <= 0) return null;
|
|
26123
|
+
if (typeof record2.lastDropAtMs !== "number" || !Number.isFinite(record2.lastDropAtMs)) {
|
|
26124
|
+
return null;
|
|
26125
|
+
}
|
|
26126
|
+
return { droppedForwards: record2.droppedForwards, lastDropAtMs: record2.lastDropAtMs };
|
|
26127
|
+
} catch {
|
|
26128
|
+
return null;
|
|
26129
|
+
}
|
|
26130
|
+
}
|
|
26131
|
+
|
|
26132
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
26133
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
26134
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
26135
|
+
import { readFile, rename, writeFile } from "fs/promises";
|
|
26136
|
+
import { join as join18 } from "path";
|
|
26137
|
+
|
|
26138
|
+
// ../../packages/plugin-sdk/src/config.ts
|
|
26139
|
+
import { existsSync as existsSync6 } from "fs";
|
|
26140
|
+
import { join as join11 } from "path";
|
|
26141
|
+
|
|
25745
26142
|
// ../../packages/plugin-sdk/src/provider-env.ts
|
|
25746
26143
|
var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
|
|
25747
26144
|
var booleanish = external_exports.string().optional().transform((v) => {
|
|
@@ -25776,8 +26173,8 @@ function hostOf(url2) {
|
|
|
25776
26173
|
}
|
|
25777
26174
|
}
|
|
25778
26175
|
function resolveProvider() {
|
|
25779
|
-
const
|
|
25780
|
-
const env =
|
|
26176
|
+
const parsed2 = ProviderEnvSchema.safeParse(process.env);
|
|
26177
|
+
const env = parsed2.success ? parsed2.data : ProviderEnvSchema.parse({});
|
|
25781
26178
|
if (env.CLAUDE_CODE_USE_BEDROCK === true) return { provider: "bedrock" };
|
|
25782
26179
|
if (env.CLAUDE_CODE_USE_VERTEX === true) return { provider: "vertex" };
|
|
25783
26180
|
const baseUrl = env.ANTHROPIC_BASE_URL;
|
|
@@ -25794,8 +26191,8 @@ function resolveProvider() {
|
|
|
25794
26191
|
function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
|
|
25795
26192
|
try {
|
|
25796
26193
|
ensureLayoutDirSync(base);
|
|
25797
|
-
const settingsFile =
|
|
25798
|
-
if (
|
|
26194
|
+
const settingsFile = join11(settingsDir(base), "settings.json");
|
|
26195
|
+
if (existsSync6(settingsFile)) tightenFile(settingsFile);
|
|
25799
26196
|
} catch {
|
|
25800
26197
|
}
|
|
25801
26198
|
migrateLegacyLayout(base);
|
|
@@ -25818,9 +26215,9 @@ function resolveProviderSafe(resolveProviderFn) {
|
|
|
25818
26215
|
}
|
|
25819
26216
|
|
|
25820
26217
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
25821
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
26218
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
|
|
25822
26219
|
import { homedir as homedir2 } from "os";
|
|
25823
|
-
import { basename as basename4, join as
|
|
26220
|
+
import { basename as basename4, join as join13 } from "path";
|
|
25824
26221
|
|
|
25825
26222
|
// ../../packages/detections/src/egress/registry.ts
|
|
25826
26223
|
var EXTRACTOR_VERSION = "1";
|
|
@@ -27592,10 +27989,10 @@ var localhost_ref_default = {
|
|
|
27592
27989
|
severity: "low",
|
|
27593
27990
|
matcher: {
|
|
27594
27991
|
type: "regex",
|
|
27595
|
-
pattern: "
|
|
27992
|
+
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_])",
|
|
27596
27993
|
flags: "g"
|
|
27597
27994
|
},
|
|
27598
|
-
examples: ["localhost", "127.0.0.1"]
|
|
27995
|
+
examples: ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
|
|
27599
27996
|
};
|
|
27600
27997
|
|
|
27601
27998
|
// ../../rules/core-code-context/stack-trace.json
|
|
@@ -28949,8 +29346,8 @@ function maskText(text) {
|
|
|
28949
29346
|
}
|
|
28950
29347
|
|
|
28951
29348
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
28952
|
-
import { existsSync as
|
|
28953
|
-
import { basename as basename3, dirname as
|
|
29349
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
29350
|
+
import { basename as basename3, dirname as dirname3, isAbsolute, join as join12, sep as sep2 } from "path";
|
|
28954
29351
|
function resolveRepoIdentity(cwd) {
|
|
28955
29352
|
try {
|
|
28956
29353
|
const root = findGitRoot(cwd);
|
|
@@ -29000,15 +29397,15 @@ function resolveGitBranch(cwd) {
|
|
|
29000
29397
|
try {
|
|
29001
29398
|
const root = findGitRoot(cwd);
|
|
29002
29399
|
if (!root) return void 0;
|
|
29003
|
-
const dotGit =
|
|
29400
|
+
const dotGit = join12(root, ".git");
|
|
29004
29401
|
let gitdir;
|
|
29005
29402
|
try {
|
|
29006
|
-
gitdir =
|
|
29403
|
+
gitdir = statSync6(dotGit).isDirectory() ? dotGit : resolveWorktreeGitdir(root, dotGit);
|
|
29007
29404
|
} catch {
|
|
29008
29405
|
return void 0;
|
|
29009
29406
|
}
|
|
29010
29407
|
if (gitdir === void 0) return void 0;
|
|
29011
|
-
const head = safeRead(
|
|
29408
|
+
const head = safeRead(join12(gitdir, "HEAD"));
|
|
29012
29409
|
if (!head) return void 0;
|
|
29013
29410
|
return /^ref:\s*refs\/heads\/(.+?)\s*$/m.exec(head)?.[1];
|
|
29014
29411
|
} catch {
|
|
@@ -29018,41 +29415,41 @@ function resolveGitBranch(cwd) {
|
|
|
29018
29415
|
function resolveWorktreeGitdir(root, dotGitFile) {
|
|
29019
29416
|
const target = /^gitdir:\s*(.+?)\s*$/m.exec(safeRead(dotGitFile) ?? "")?.[1];
|
|
29020
29417
|
if (!target) return void 0;
|
|
29021
|
-
return isAbsolute(target) ? target :
|
|
29418
|
+
return isAbsolute(target) ? target : join12(root, target);
|
|
29022
29419
|
}
|
|
29023
29420
|
function findGitRoot(start) {
|
|
29024
29421
|
let dir = start;
|
|
29025
29422
|
for (; ; ) {
|
|
29026
|
-
if (
|
|
29027
|
-
const parent =
|
|
29423
|
+
if (existsSync7(join12(dir, ".git"))) return dir;
|
|
29424
|
+
const parent = dirname3(dir);
|
|
29028
29425
|
if (parent === dir) return void 0;
|
|
29029
29426
|
dir = parent;
|
|
29030
29427
|
}
|
|
29031
29428
|
}
|
|
29032
29429
|
function resolveGitContext(root) {
|
|
29033
|
-
const dotGit =
|
|
29430
|
+
const dotGit = join12(root, ".git");
|
|
29034
29431
|
try {
|
|
29035
|
-
if (
|
|
29036
|
-
return { configPath:
|
|
29432
|
+
if (statSync6(dotGit).isDirectory()) {
|
|
29433
|
+
return { configPath: join12(dotGit, "config"), headRoot: root };
|
|
29037
29434
|
}
|
|
29038
29435
|
} catch {
|
|
29039
29436
|
return void 0;
|
|
29040
29437
|
}
|
|
29041
29438
|
const target = /^gitdir:\s*(.+?)\s*$/m.exec(safeRead(dotGit) ?? "")?.[1];
|
|
29042
29439
|
if (!target) return void 0;
|
|
29043
|
-
const gitdir = isAbsolute(target) ? target :
|
|
29044
|
-
if (
|
|
29045
|
-
return { configPath:
|
|
29440
|
+
const gitdir = isAbsolute(target) ? target : join12(root, target);
|
|
29441
|
+
if (existsSync7(join12(gitdir, "config"))) {
|
|
29442
|
+
return { configPath: join12(gitdir, "config"), headRoot: root };
|
|
29046
29443
|
}
|
|
29047
|
-
const commonRaw = safeRead(
|
|
29444
|
+
const commonRaw = safeRead(join12(gitdir, "commondir"))?.trim();
|
|
29048
29445
|
if (!commonRaw) return void 0;
|
|
29049
|
-
const commonGitDir = isAbsolute(commonRaw) ? commonRaw :
|
|
29050
|
-
const headRoot = basename3(commonGitDir) === ".git" ?
|
|
29051
|
-
return { configPath:
|
|
29446
|
+
const commonGitDir = isAbsolute(commonRaw) ? commonRaw : join12(gitdir, commonRaw);
|
|
29447
|
+
const headRoot = basename3(commonGitDir) === ".git" ? dirname3(commonGitDir) : root;
|
|
29448
|
+
return { configPath: join12(commonGitDir, "config"), headRoot };
|
|
29052
29449
|
}
|
|
29053
29450
|
function safeRead(path) {
|
|
29054
29451
|
try {
|
|
29055
|
-
return
|
|
29452
|
+
return readFileSync8(path, "utf8");
|
|
29056
29453
|
} catch {
|
|
29057
29454
|
return void 0;
|
|
29058
29455
|
}
|
|
@@ -29114,31 +29511,31 @@ function resolveConfigInventory(input) {
|
|
|
29114
29511
|
};
|
|
29115
29512
|
try {
|
|
29116
29513
|
const home = input.homeDir ?? homedir2();
|
|
29117
|
-
const claudeDir =
|
|
29514
|
+
const claudeDir = join13(home, ".claude");
|
|
29118
29515
|
const repo = resolveRepoIdentity(input.cwd);
|
|
29119
29516
|
const repoIdentity = repo?.url ?? input.cwd;
|
|
29120
29517
|
const projectSource = `project:${repoIdentity}`;
|
|
29121
|
-
collectSettingsHooks(scan2,
|
|
29122
|
-
collectSettingsHooks(scan2,
|
|
29123
|
-
collectSettingsHooks(scan2,
|
|
29518
|
+
collectSettingsHooks(scan2, join13(claudeDir, "settings.json"), "user");
|
|
29519
|
+
collectSettingsHooks(scan2, join13(input.cwd, ".claude", "settings.json"), "project");
|
|
29520
|
+
collectSettingsHooks(scan2, join13(input.cwd, ".claude", "settings.local.json"), "local");
|
|
29124
29521
|
const projectOrigin = { scope: "project", project: repoIdentity };
|
|
29125
|
-
collectMcpFile(scan2,
|
|
29126
|
-
collectUserClaudeJson(scan2,
|
|
29127
|
-
collectMcpFile(scan2,
|
|
29128
|
-
collectMcpFile(scan2,
|
|
29129
|
-
collectMcpFile(scan2,
|
|
29522
|
+
collectMcpFile(scan2, join13(input.cwd, ".mcp.json"), projectOrigin, { recordErrors: true });
|
|
29523
|
+
collectUserClaudeJson(scan2, join13(home, ".claude.json"), input.cwd, repoIdentity);
|
|
29524
|
+
collectMcpFile(scan2, join13(claudeDir, "settings.json"), { scope: "user" });
|
|
29525
|
+
collectMcpFile(scan2, join13(input.cwd, ".claude", "settings.json"), projectOrigin);
|
|
29526
|
+
collectMcpFile(scan2, join13(input.cwd, ".claude", "settings.local.json"), {
|
|
29130
29527
|
scope: "local",
|
|
29131
29528
|
project: repoIdentity
|
|
29132
29529
|
});
|
|
29133
29530
|
collectConfigFiles(scan2, claudeDir, input.cwd);
|
|
29134
|
-
collectSkillsDir(scan2,
|
|
29135
|
-
collectSkillsDir(scan2,
|
|
29531
|
+
collectSkillsDir(scan2, join13(claudeDir, "skills"), { source: "local", scope: "user" });
|
|
29532
|
+
collectSkillsDir(scan2, join13(input.cwd, ".claude", "skills"), {
|
|
29136
29533
|
source: projectSource,
|
|
29137
29534
|
scope: "project"
|
|
29138
29535
|
});
|
|
29139
29536
|
collectInstalledPlugins(scan2, claudeDir);
|
|
29140
29537
|
collectMarketplaceSkills(scan2, claudeDir);
|
|
29141
|
-
collectSkillsDir(scan2,
|
|
29538
|
+
collectSkillsDir(scan2, join13(input.cwd, "skills"), { source: projectSource, scope: "project" });
|
|
29142
29539
|
scan2.skills = dedupeSkills(scan2.skills);
|
|
29143
29540
|
scan2.mcpServers = dedupeMcpServers(scan2.mcpServers);
|
|
29144
29541
|
} catch (err) {
|
|
@@ -29173,9 +29570,9 @@ function collectSettingsHooks(scan2, path, scope) {
|
|
|
29173
29570
|
const raw = readOptional(path);
|
|
29174
29571
|
if (raw === void 0) return;
|
|
29175
29572
|
try {
|
|
29176
|
-
const
|
|
29177
|
-
if (typeof
|
|
29178
|
-
collectHooksObject(scan2,
|
|
29573
|
+
const parsed2 = JSON.parse(raw);
|
|
29574
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return;
|
|
29575
|
+
collectHooksObject(scan2, parsed2.hooks, path, scope);
|
|
29179
29576
|
} catch (err) {
|
|
29180
29577
|
scan2.errors.push({ source: path, reason: parseErrorReason(err) });
|
|
29181
29578
|
}
|
|
@@ -29221,9 +29618,9 @@ function collectMcpFile(scan2, path, origin, opts) {
|
|
|
29221
29618
|
const raw = readOptional(path);
|
|
29222
29619
|
if (raw === void 0) return;
|
|
29223
29620
|
try {
|
|
29224
|
-
const
|
|
29225
|
-
if (typeof
|
|
29226
|
-
collectMcpObject(scan2,
|
|
29621
|
+
const parsed2 = JSON.parse(raw);
|
|
29622
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return;
|
|
29623
|
+
collectMcpObject(scan2, parsed2.mcpServers, path, origin);
|
|
29227
29624
|
} catch (err) {
|
|
29228
29625
|
if (opts?.recordErrors ?? false) {
|
|
29229
29626
|
scan2.errors.push({ source: path, reason: parseErrorReason(err) });
|
|
@@ -29234,9 +29631,9 @@ function collectUserClaudeJson(scan2, path, cwd, repoIdentity) {
|
|
|
29234
29631
|
const raw = readOptional(path);
|
|
29235
29632
|
if (raw === void 0) return;
|
|
29236
29633
|
try {
|
|
29237
|
-
const
|
|
29238
|
-
if (typeof
|
|
29239
|
-
const rec =
|
|
29634
|
+
const parsed2 = JSON.parse(raw);
|
|
29635
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return;
|
|
29636
|
+
const rec = parsed2;
|
|
29240
29637
|
collectMcpObject(scan2, rec.mcpServers, path, { scope: "user" });
|
|
29241
29638
|
const projects = rec.projects;
|
|
29242
29639
|
if (typeof projects === "object" && projects !== null) {
|
|
@@ -29257,7 +29654,7 @@ function projectEntryFor(projects, cwd) {
|
|
|
29257
29654
|
const trimmed = cwd.replace(/\/+$/, "");
|
|
29258
29655
|
if (trimmed.length > 0) candidates.add(trimmed);
|
|
29259
29656
|
try {
|
|
29260
|
-
candidates.add(
|
|
29657
|
+
candidates.add(realpathSync2(cwd));
|
|
29261
29658
|
} catch {
|
|
29262
29659
|
}
|
|
29263
29660
|
for (const key of candidates) {
|
|
@@ -29267,15 +29664,15 @@ function projectEntryFor(projects, cwd) {
|
|
|
29267
29664
|
return void 0;
|
|
29268
29665
|
}
|
|
29269
29666
|
function collectPluginManifestMcp(scan2, installPath, origin) {
|
|
29270
|
-
const manifestPath =
|
|
29667
|
+
const manifestPath = join13(installPath, ".claude-plugin", "plugin.json");
|
|
29271
29668
|
const raw = readOptional(manifestPath);
|
|
29272
29669
|
if (raw === void 0) return;
|
|
29273
29670
|
try {
|
|
29274
|
-
const
|
|
29275
|
-
if (typeof
|
|
29276
|
-
const declared =
|
|
29671
|
+
const parsed2 = JSON.parse(raw);
|
|
29672
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return;
|
|
29673
|
+
const declared = parsed2.mcpServers;
|
|
29277
29674
|
if (typeof declared === "string" && declared.length > 0) {
|
|
29278
|
-
collectMcpFile(scan2,
|
|
29675
|
+
collectMcpFile(scan2, join13(installPath, declared), origin, { recordErrors: true });
|
|
29279
29676
|
} else {
|
|
29280
29677
|
collectMcpObject(scan2, declared, manifestPath, origin);
|
|
29281
29678
|
}
|
|
@@ -29292,18 +29689,18 @@ var SETTINGS_KEY_LABELS = [
|
|
|
29292
29689
|
["statusLine", "status line"]
|
|
29293
29690
|
];
|
|
29294
29691
|
function collectConfigFiles(scan2, claudeDir, cwd) {
|
|
29295
|
-
settingsConfigFile(scan2,
|
|
29296
|
-
settingsConfigFile(scan2,
|
|
29297
|
-
settingsConfigFile(scan2,
|
|
29298
|
-
memoryConfigFile(scan2,
|
|
29299
|
-
memoryConfigFile(scan2,
|
|
29300
|
-
mcpJsonConfigFile(scan2,
|
|
29301
|
-
dirConfigFile(scan2,
|
|
29302
|
-
dirConfigFile(scan2,
|
|
29692
|
+
settingsConfigFile(scan2, join13(claudeDir, "settings.json"), "user", "User settings");
|
|
29693
|
+
settingsConfigFile(scan2, join13(cwd, ".claude", "settings.json"), "project", "Project settings");
|
|
29694
|
+
settingsConfigFile(scan2, join13(cwd, ".claude", "settings.local.json"), "local", "Local overrides");
|
|
29695
|
+
memoryConfigFile(scan2, join13(claudeDir, "CLAUDE.md"), "user", "User memory");
|
|
29696
|
+
memoryConfigFile(scan2, join13(cwd, "CLAUDE.md"), "project", "Project memory");
|
|
29697
|
+
mcpJsonConfigFile(scan2, join13(cwd, ".mcp.json"));
|
|
29698
|
+
dirConfigFile(scan2, join13(cwd, ".claude", "commands"), "Slash commands", "command");
|
|
29699
|
+
dirConfigFile(scan2, join13(cwd, ".claude", "agents"), "Subagents", "subagent");
|
|
29303
29700
|
}
|
|
29304
29701
|
function configFileEntry(path, scope, kind) {
|
|
29305
29702
|
try {
|
|
29306
|
-
const stat =
|
|
29703
|
+
const stat = statSync7(path);
|
|
29307
29704
|
return { name: basename4(path), path, scope, kind, updatedAt: stat.mtime.toISOString() };
|
|
29308
29705
|
} catch {
|
|
29309
29706
|
return void 0;
|
|
@@ -29315,9 +29712,9 @@ function settingsConfigFile(scan2, path, scope, kind) {
|
|
|
29315
29712
|
const raw = readOptional(path);
|
|
29316
29713
|
if (raw !== void 0) {
|
|
29317
29714
|
try {
|
|
29318
|
-
const
|
|
29319
|
-
if (typeof
|
|
29320
|
-
const labels = SETTINGS_KEY_LABELS.filter(([key]) => key in
|
|
29715
|
+
const parsed2 = JSON.parse(raw);
|
|
29716
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
29717
|
+
const labels = SETTINGS_KEY_LABELS.filter(([key]) => key in parsed2).map(
|
|
29321
29718
|
([, label]) => label
|
|
29322
29719
|
);
|
|
29323
29720
|
if (labels.length > 0) entry.detail = labels.join(", ");
|
|
@@ -29344,8 +29741,8 @@ function mcpJsonConfigFile(scan2, path) {
|
|
|
29344
29741
|
const raw = readOptional(path);
|
|
29345
29742
|
if (raw !== void 0) {
|
|
29346
29743
|
try {
|
|
29347
|
-
const
|
|
29348
|
-
const servers =
|
|
29744
|
+
const parsed2 = JSON.parse(raw);
|
|
29745
|
+
const servers = parsed2?.mcpServers;
|
|
29349
29746
|
if (typeof servers === "object" && servers !== null) {
|
|
29350
29747
|
const count = Object.values(servers).filter(
|
|
29351
29748
|
(v) => typeof v === "object" && v !== null && (str2(v.command) !== void 0 || str2(v.url) !== void 0)
|
|
@@ -29374,7 +29771,7 @@ function countMarkdownFiles(dir, depth) {
|
|
|
29374
29771
|
let count = 0;
|
|
29375
29772
|
for (const dirent of readdirSync2(dir, { withFileTypes: true })) {
|
|
29376
29773
|
if (dirent.name.startsWith(".")) continue;
|
|
29377
|
-
if (dirent.isDirectory()) count += countMarkdownFiles(
|
|
29774
|
+
if (dirent.isDirectory()) count += countMarkdownFiles(join13(dir, dirent.name), depth + 1);
|
|
29378
29775
|
else if (dirent.name.endsWith(".md")) count += 1;
|
|
29379
29776
|
}
|
|
29380
29777
|
return count;
|
|
@@ -29387,7 +29784,7 @@ function collectSkillsDir(scan2, dir, origin) {
|
|
|
29387
29784
|
return;
|
|
29388
29785
|
}
|
|
29389
29786
|
for (const name of names) {
|
|
29390
|
-
const skillFile =
|
|
29787
|
+
const skillFile = join13(dir, name, "SKILL.md");
|
|
29391
29788
|
try {
|
|
29392
29789
|
const raw = readOptional(skillFile);
|
|
29393
29790
|
if (raw === void 0) continue;
|
|
@@ -29396,8 +29793,8 @@ function collectSkillsDir(scan2, dir, origin) {
|
|
|
29396
29793
|
name: front.name ?? name,
|
|
29397
29794
|
source: origin.source,
|
|
29398
29795
|
scope: origin.scope,
|
|
29399
|
-
location:
|
|
29400
|
-
updatedAt:
|
|
29796
|
+
location: join13(dir, name),
|
|
29797
|
+
updatedAt: statSync7(skillFile).mtime.toISOString()
|
|
29401
29798
|
};
|
|
29402
29799
|
const version2 = front.version ?? origin.defaultVersion;
|
|
29403
29800
|
if (version2 !== void 0) entry.version = version2;
|
|
@@ -29427,13 +29824,13 @@ function parseFrontmatter(raw) {
|
|
|
29427
29824
|
return out;
|
|
29428
29825
|
}
|
|
29429
29826
|
function collectInstalledPlugins(scan2, claudeDir) {
|
|
29430
|
-
const manifestPath =
|
|
29827
|
+
const manifestPath = join13(claudeDir, "plugins", "installed_plugins.json");
|
|
29431
29828
|
const raw = readOptional(manifestPath);
|
|
29432
29829
|
if (raw === void 0) return;
|
|
29433
29830
|
let plugins;
|
|
29434
29831
|
try {
|
|
29435
|
-
const
|
|
29436
|
-
const p =
|
|
29832
|
+
const parsed2 = JSON.parse(raw);
|
|
29833
|
+
const p = parsed2?.plugins;
|
|
29437
29834
|
if (typeof p !== "object" || p === null) return;
|
|
29438
29835
|
plugins = p;
|
|
29439
29836
|
} catch (err) {
|
|
@@ -29452,15 +29849,15 @@ function collectInstalledPlugins(scan2, claudeDir) {
|
|
|
29452
29849
|
if (typeof installPath !== "string" || seen.has(installPath)) continue;
|
|
29453
29850
|
seen.add(installPath);
|
|
29454
29851
|
const version2 = install.version;
|
|
29455
|
-
const hooksPath =
|
|
29852
|
+
const hooksPath = join13(installPath, "hooks", "hooks.json");
|
|
29456
29853
|
const hooksRaw = readOptional(hooksPath);
|
|
29457
29854
|
if (hooksRaw !== void 0) {
|
|
29458
29855
|
try {
|
|
29459
|
-
const
|
|
29460
|
-
if (typeof
|
|
29856
|
+
const parsed2 = JSON.parse(hooksRaw);
|
|
29857
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
29461
29858
|
collectHooksObject(
|
|
29462
29859
|
scan2,
|
|
29463
|
-
|
|
29860
|
+
parsed2.hooks,
|
|
29464
29861
|
hooksPath,
|
|
29465
29862
|
"plugin",
|
|
29466
29863
|
pluginName
|
|
@@ -29472,22 +29869,22 @@ function collectInstalledPlugins(scan2, claudeDir) {
|
|
|
29472
29869
|
}
|
|
29473
29870
|
const origin = { source: marketplace, scope: "plugin", pluginName };
|
|
29474
29871
|
if (typeof version2 === "string") origin.defaultVersion = version2;
|
|
29475
|
-
collectSkillsDir(scan2,
|
|
29872
|
+
collectSkillsDir(scan2, join13(installPath, "skills"), origin);
|
|
29476
29873
|
const mcpOrigin = { scope: "plugin", pluginName, marketplace };
|
|
29477
|
-
collectMcpFile(scan2,
|
|
29874
|
+
collectMcpFile(scan2, join13(installPath, ".mcp.json"), mcpOrigin, { recordErrors: true });
|
|
29478
29875
|
collectPluginManifestMcp(scan2, installPath, mcpOrigin);
|
|
29479
29876
|
}
|
|
29480
29877
|
}
|
|
29481
29878
|
}
|
|
29482
29879
|
function collectMarketplaceSkills(scan2, claudeDir) {
|
|
29483
|
-
for (const mp of readMarketplaces(
|
|
29880
|
+
for (const mp of readMarketplaces(join13(claudeDir, "plugins", "known_marketplaces.json"))) {
|
|
29484
29881
|
if (isClaudeOfficialMarketplace(mp.name, mp.repo)) continue;
|
|
29485
|
-
collectSkillsDir(scan2,
|
|
29882
|
+
collectSkillsDir(scan2, join13(mp.installLocation, "skills"), {
|
|
29486
29883
|
source: mp.name,
|
|
29487
29884
|
scope: "plugin"
|
|
29488
29885
|
});
|
|
29489
|
-
collectPluginSkillDirs(scan2,
|
|
29490
|
-
collectPluginSkillDirs(scan2,
|
|
29886
|
+
collectPluginSkillDirs(scan2, join13(mp.installLocation, "plugins"), mp.name);
|
|
29887
|
+
collectPluginSkillDirs(scan2, join13(mp.installLocation, "external_plugins"), mp.name);
|
|
29491
29888
|
}
|
|
29492
29889
|
}
|
|
29493
29890
|
function collectPluginSkillDirs(scan2, pluginsDir, marketplace) {
|
|
@@ -29498,7 +29895,7 @@ function collectPluginSkillDirs(scan2, pluginsDir, marketplace) {
|
|
|
29498
29895
|
return;
|
|
29499
29896
|
}
|
|
29500
29897
|
for (const plugin of plugins) {
|
|
29501
|
-
collectSkillsDir(scan2,
|
|
29898
|
+
collectSkillsDir(scan2, join13(pluginsDir, plugin, "skills"), {
|
|
29502
29899
|
source: marketplace,
|
|
29503
29900
|
scope: "plugin",
|
|
29504
29901
|
pluginName: plugin
|
|
@@ -29509,10 +29906,10 @@ function readMarketplaces(manifestPath) {
|
|
|
29509
29906
|
const raw = readOptional(manifestPath);
|
|
29510
29907
|
if (raw === void 0) return [];
|
|
29511
29908
|
try {
|
|
29512
|
-
const
|
|
29513
|
-
if (typeof
|
|
29909
|
+
const parsed2 = JSON.parse(raw);
|
|
29910
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return [];
|
|
29514
29911
|
const out = [];
|
|
29515
|
-
for (const [name, entry] of Object.entries(
|
|
29912
|
+
for (const [name, entry] of Object.entries(parsed2)) {
|
|
29516
29913
|
const rec = entry;
|
|
29517
29914
|
const loc = rec?.installLocation;
|
|
29518
29915
|
if (typeof loc !== "string" || loc.length === 0) continue;
|
|
@@ -29552,7 +29949,7 @@ function dedupeMcpServers(servers) {
|
|
|
29552
29949
|
}
|
|
29553
29950
|
function readOptional(path) {
|
|
29554
29951
|
try {
|
|
29555
|
-
return
|
|
29952
|
+
return readFileSync9(path, "utf8");
|
|
29556
29953
|
} catch {
|
|
29557
29954
|
return void 0;
|
|
29558
29955
|
}
|
|
@@ -29580,17 +29977,17 @@ function offersMaintenance(gateway, member) {
|
|
|
29580
29977
|
import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
|
|
29581
29978
|
|
|
29582
29979
|
// ../../packages/plugin-sdk/src/isolated-scan.ts
|
|
29583
|
-
import { existsSync as
|
|
29980
|
+
import { existsSync as existsSync8 } from "fs";
|
|
29584
29981
|
import { fileURLToPath } from "url";
|
|
29585
29982
|
import { Worker } from "worker_threads";
|
|
29586
29983
|
|
|
29587
29984
|
// ../../packages/plugin-sdk/src/ignore-layers.ts
|
|
29588
29985
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
29589
|
-
import { readFileSync as
|
|
29590
|
-
import { join as
|
|
29986
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
29987
|
+
import { join as join14 } from "path";
|
|
29591
29988
|
function readIgnoreLayer(dir, filename, anchorLen) {
|
|
29592
29989
|
try {
|
|
29593
|
-
return { matcher: (0, import_ignore.default)().add(
|
|
29990
|
+
return { matcher: (0, import_ignore.default)().add(readFileSync10(join14(dir, filename), "utf8")), anchorLen };
|
|
29594
29991
|
} catch {
|
|
29595
29992
|
return void 0;
|
|
29596
29993
|
}
|
|
@@ -29647,17 +30044,17 @@ function resolveInventoryContext(input) {
|
|
|
29647
30044
|
}
|
|
29648
30045
|
|
|
29649
30046
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
29650
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
29651
|
-
import { join as
|
|
30047
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
30048
|
+
import { join as join15 } from "path";
|
|
29652
30049
|
var SESSION_START_MARKER = "session-start-last";
|
|
29653
30050
|
function claimSessionStart(dataDir2, sessionId) {
|
|
29654
30051
|
return claimOncePerSession(dataDir2, SESSION_START_MARKER, sessionId);
|
|
29655
30052
|
}
|
|
29656
30053
|
function claimOncePerSession(dataDir2, marker, sessionId) {
|
|
29657
30054
|
if (!sessionId) return true;
|
|
29658
|
-
const path =
|
|
30055
|
+
const path = join15(dataDir2, marker);
|
|
29659
30056
|
try {
|
|
29660
|
-
if (
|
|
30057
|
+
if (readFileSync11(path, "utf8") === sessionId) return false;
|
|
29661
30058
|
} catch {
|
|
29662
30059
|
}
|
|
29663
30060
|
try {
|
|
@@ -29669,12 +30066,12 @@ function claimOncePerSession(dataDir2, marker, sessionId) {
|
|
|
29669
30066
|
}
|
|
29670
30067
|
|
|
29671
30068
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
29672
|
-
import { readdirSync as readdirSync3, realpathSync as
|
|
29673
|
-
import { basename as basename5, dirname as
|
|
30069
|
+
import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
|
|
30070
|
+
import { basename as basename5, dirname as dirname4, sep as sep3 } from "path";
|
|
29674
30071
|
|
|
29675
30072
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
29676
|
-
import { existsSync as
|
|
29677
|
-
import { basename as basename6, join as
|
|
30073
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
|
|
30074
|
+
import { basename as basename6, join as join16 } from "path";
|
|
29678
30075
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
29679
30076
|
".git",
|
|
29680
30077
|
"node_modules",
|
|
@@ -29771,8 +30168,8 @@ function resolveProjectFiles(cwd, opts = {}) {
|
|
|
29771
30168
|
}
|
|
29772
30169
|
if (entry.isDirectory()) {
|
|
29773
30170
|
if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, dirRel, entry.name, true)) continue;
|
|
29774
|
-
const fullPath =
|
|
29775
|
-
if (
|
|
30171
|
+
const fullPath = join16(dir, entry.name);
|
|
30172
|
+
if (existsSync9(join16(fullPath, ".git"))) continue;
|
|
29776
30173
|
if (depth >= bounds.maxDepth) {
|
|
29777
30174
|
walk.omitted = true;
|
|
29778
30175
|
continue;
|
|
@@ -29844,12 +30241,12 @@ import { randomUUID as randomUUID14 } from "crypto";
|
|
|
29844
30241
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
29845
30242
|
|
|
29846
30243
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
29847
|
-
import { mkdirSync as mkdirSync3, statSync as
|
|
29848
|
-
import { join as
|
|
30244
|
+
import { mkdirSync as mkdirSync3, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
30245
|
+
import { join as join17 } from "path";
|
|
29849
30246
|
function throttled(dataDir2, markerName, windowMs) {
|
|
29850
|
-
const marker =
|
|
30247
|
+
const marker = join17(dataDir2, markerName);
|
|
29851
30248
|
try {
|
|
29852
|
-
if (Date.now() -
|
|
30249
|
+
if (Date.now() - statSync8(marker).mtimeMs < windowMs) return true;
|
|
29853
30250
|
} catch {
|
|
29854
30251
|
}
|
|
29855
30252
|
try {
|
|
@@ -29860,25 +30257,1173 @@ function throttled(dataDir2, markerName, windowMs) {
|
|
|
29860
30257
|
return false;
|
|
29861
30258
|
}
|
|
29862
30259
|
|
|
29863
|
-
// ../../packages/plugin-runtime/src/
|
|
29864
|
-
|
|
29865
|
-
|
|
29866
|
-
|
|
29867
|
-
|
|
29868
|
-
|
|
29869
|
-
|
|
30260
|
+
// ../../packages/plugin-runtime/src/attached/with-timeout.ts
|
|
30261
|
+
var REQUEST_TIMEOUT_MS = 2e3;
|
|
30262
|
+
function withTimeout(promise2, ms) {
|
|
30263
|
+
let timer;
|
|
30264
|
+
const timeout = new Promise((_, reject) => {
|
|
30265
|
+
timer = setTimeout(() => {
|
|
30266
|
+
reject(new Error("attached gateway request timed out"));
|
|
30267
|
+
}, ms);
|
|
30268
|
+
});
|
|
30269
|
+
promise2.catch(() => void 0);
|
|
30270
|
+
return Promise.race([promise2, timeout]).finally(() => {
|
|
30271
|
+
clearTimeout(timer);
|
|
30272
|
+
});
|
|
29870
30273
|
}
|
|
29871
30274
|
|
|
29872
|
-
// ../../packages/plugin-runtime/src/
|
|
29873
|
-
|
|
29874
|
-
|
|
29875
|
-
|
|
29876
|
-
|
|
29877
|
-
|
|
29878
|
-
|
|
29879
|
-
|
|
29880
|
-
|
|
29881
|
-
|
|
30275
|
+
// ../../packages/plugin-runtime/src/attached/forward-policy.ts
|
|
30276
|
+
function isInvalidRequest(err) {
|
|
30277
|
+
return typeof err === "object" && err !== null && err.name === "RemoteRequestInvalid";
|
|
30278
|
+
}
|
|
30279
|
+
var FORWARD_BUDGET_MS = 1500;
|
|
30280
|
+
var DECISION_PATH_BUDGET_MS = 800;
|
|
30281
|
+
var BREAKER_FAILURE_THRESHOLD = 3;
|
|
30282
|
+
var BREAKER_COOLDOWN_MS = 3e4;
|
|
30283
|
+
var CLOSED = { consecutiveFailures: 0, openedAtMs: null, lastFailure: null };
|
|
30284
|
+
var FAILURES = /* @__PURE__ */ new Set([
|
|
30285
|
+
"unauthorized",
|
|
30286
|
+
"forbidden",
|
|
30287
|
+
"unreachable"
|
|
30288
|
+
]);
|
|
30289
|
+
var FORWARD_STATE_FILENAME = "attached-state.json";
|
|
30290
|
+
var STATE_FILENAME = FORWARD_STATE_FILENAME;
|
|
30291
|
+
function parseBreakerState(raw, nowMs) {
|
|
30292
|
+
try {
|
|
30293
|
+
const parsed2 = JSON.parse(raw);
|
|
30294
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
30295
|
+
const record2 = parsed2;
|
|
30296
|
+
const failures = typeof record2.consecutiveFailures === "number" && record2.consecutiveFailures >= 0 ? record2.consecutiveFailures : 0;
|
|
30297
|
+
const openedAtMs = typeof record2.openedAtMs === "number" && Number.isFinite(record2.openedAtMs) && record2.openedAtMs <= nowMs ? record2.openedAtMs : null;
|
|
30298
|
+
const lastFailure = typeof record2.lastFailure === "string" && FAILURES.has(record2.lastFailure) ? record2.lastFailure : null;
|
|
30299
|
+
return { consecutiveFailures: failures, openedAtMs, lastFailure };
|
|
30300
|
+
} catch {
|
|
30301
|
+
return null;
|
|
30302
|
+
}
|
|
30303
|
+
}
|
|
30304
|
+
function createForwardPolicy(deps) {
|
|
30305
|
+
const now = deps.now ?? (() => Date.now());
|
|
30306
|
+
const file2 = join18(deps.dir, STATE_FILENAME);
|
|
30307
|
+
let state = null;
|
|
30308
|
+
let loading = null;
|
|
30309
|
+
async function readState() {
|
|
30310
|
+
let raw;
|
|
30311
|
+
try {
|
|
30312
|
+
raw = await readFile(file2, "utf8");
|
|
30313
|
+
} catch {
|
|
30314
|
+
return { ...CLOSED };
|
|
30315
|
+
}
|
|
30316
|
+
return parseBreakerState(raw, now()) ?? { ...CLOSED };
|
|
30317
|
+
}
|
|
30318
|
+
async function load() {
|
|
30319
|
+
if (state !== null) return state;
|
|
30320
|
+
loading ??= readState().then((loaded) => {
|
|
30321
|
+
state = loaded;
|
|
30322
|
+
loading = null;
|
|
30323
|
+
return loaded;
|
|
30324
|
+
});
|
|
30325
|
+
return loading;
|
|
30326
|
+
}
|
|
30327
|
+
async function persist(next) {
|
|
30328
|
+
state = next;
|
|
30329
|
+
try {
|
|
30330
|
+
await ensureDataDir(deps.dir);
|
|
30331
|
+
const tmp = `${file2}.${randomUUID15()}.tmp`;
|
|
30332
|
+
await writeFile(tmp, JSON.stringify(next), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
30333
|
+
await rename(tmp, file2);
|
|
30334
|
+
} catch {
|
|
30335
|
+
}
|
|
30336
|
+
}
|
|
30337
|
+
return {
|
|
30338
|
+
async run(op, opts) {
|
|
30339
|
+
const budget = opts?.decisionPath === true ? DECISION_PATH_BUDGET_MS : FORWARD_BUDGET_MS;
|
|
30340
|
+
let current;
|
|
30341
|
+
try {
|
|
30342
|
+
current = await load();
|
|
30343
|
+
} catch {
|
|
30344
|
+
current = { ...CLOSED };
|
|
30345
|
+
}
|
|
30346
|
+
const at = now();
|
|
30347
|
+
if (current.openedAtMs !== null) {
|
|
30348
|
+
if (at - current.openedAtMs < BREAKER_COOLDOWN_MS) {
|
|
30349
|
+
return { ok: false, reason: "breaker-open" };
|
|
30350
|
+
}
|
|
30351
|
+
await persist({
|
|
30352
|
+
consecutiveFailures: current.consecutiveFailures,
|
|
30353
|
+
openedAtMs: at,
|
|
30354
|
+
lastFailure: current.lastFailure
|
|
30355
|
+
});
|
|
30356
|
+
}
|
|
30357
|
+
try {
|
|
30358
|
+
const value = await withTimeout(op(), budget);
|
|
30359
|
+
if (current.openedAtMs !== null || current.consecutiveFailures > 0) {
|
|
30360
|
+
await persist({ ...CLOSED });
|
|
30361
|
+
}
|
|
30362
|
+
return { ok: true, value };
|
|
30363
|
+
} catch (err) {
|
|
30364
|
+
if (isInvalidRequest(err)) return { ok: false, reason: "invalid-request" };
|
|
30365
|
+
const reason = classifyFailure(err);
|
|
30366
|
+
const failures = current.consecutiveFailures + 1;
|
|
30367
|
+
const shouldOpen = current.openedAtMs !== null || failures >= BREAKER_FAILURE_THRESHOLD;
|
|
30368
|
+
await persist({
|
|
30369
|
+
consecutiveFailures: failures,
|
|
30370
|
+
openedAtMs: shouldOpen ? now() : null,
|
|
30371
|
+
lastFailure: reason
|
|
30372
|
+
});
|
|
30373
|
+
return { ok: false, reason };
|
|
30374
|
+
}
|
|
30375
|
+
}
|
|
30376
|
+
};
|
|
30377
|
+
}
|
|
30378
|
+
|
|
30379
|
+
// ../../packages/plugin-runtime/src/attached/gateway.ts
|
|
30380
|
+
var ACTION_STRENGTH = {
|
|
30381
|
+
allow: 0,
|
|
30382
|
+
log: 1,
|
|
30383
|
+
warn: 2,
|
|
30384
|
+
redact: 3,
|
|
30385
|
+
block: 4
|
|
30386
|
+
};
|
|
30387
|
+
function ruleCategoryMap(wireRules, localRules) {
|
|
30388
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
30389
|
+
for (const rule of wireRules ?? []) map2.set(rule.id, rule.category);
|
|
30390
|
+
for (const rule of localRules ?? []) map2.set(rule.id, rule.category);
|
|
30391
|
+
for (const pack of bundledDetections()) {
|
|
30392
|
+
for (const rule of pack.rules) map2.set(rule.id, rule.category);
|
|
30393
|
+
}
|
|
30394
|
+
return map2;
|
|
30395
|
+
}
|
|
30396
|
+
function strongerOf(a, b) {
|
|
30397
|
+
if (a === null) return b;
|
|
30398
|
+
if (b === null) return a;
|
|
30399
|
+
return ACTION_STRENGTH[a] >= ACTION_STRENGTH[b] ? a : b;
|
|
30400
|
+
}
|
|
30401
|
+
function policyKey(policy) {
|
|
30402
|
+
return "ruleId" in policy.target ? `rule:${policy.target.ruleId}` : `category:${policy.target.category}`;
|
|
30403
|
+
}
|
|
30404
|
+
function floorFor(policy, categoryByRuleId) {
|
|
30405
|
+
const category = "category" in policy.target ? policy.target.category : categoryByRuleId.get(policy.target.ruleId);
|
|
30406
|
+
return category === void 0 ? null : DEFAULT_ACTIONS[category];
|
|
30407
|
+
}
|
|
30408
|
+
function mergeRaiseOnly(localPolicies, remotePolicies, categoryByRuleId) {
|
|
30409
|
+
const merged = /* @__PURE__ */ new Map();
|
|
30410
|
+
const disabled = [];
|
|
30411
|
+
const remoteCategoryAction = /* @__PURE__ */ new Map();
|
|
30412
|
+
for (const policy of remotePolicies) {
|
|
30413
|
+
if (!policy.enabled) continue;
|
|
30414
|
+
if (!("category" in policy.target)) continue;
|
|
30415
|
+
if (remoteCategoryAction.has(policy.target.category)) continue;
|
|
30416
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
30417
|
+
remoteCategoryAction.set(
|
|
30418
|
+
policy.target.category,
|
|
30419
|
+
floor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[floor] ? floor : policy.action
|
|
30420
|
+
);
|
|
30421
|
+
}
|
|
30422
|
+
for (const policy of localPolicies) {
|
|
30423
|
+
if (!policy.enabled) {
|
|
30424
|
+
disabled.push(policy);
|
|
30425
|
+
continue;
|
|
30426
|
+
}
|
|
30427
|
+
const key = policyKey(policy);
|
|
30428
|
+
if (merged.has(key)) continue;
|
|
30429
|
+
let remoteFloor = null;
|
|
30430
|
+
if ("ruleId" in policy.target) {
|
|
30431
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
30432
|
+
if (category !== void 0) remoteFloor = remoteCategoryAction.get(category) ?? null;
|
|
30433
|
+
}
|
|
30434
|
+
merged.set(
|
|
30435
|
+
key,
|
|
30436
|
+
remoteFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[remoteFloor] ? { ...policy, action: remoteFloor } : policy
|
|
30437
|
+
);
|
|
30438
|
+
}
|
|
30439
|
+
const localCategoryAction = /* @__PURE__ */ new Map();
|
|
30440
|
+
for (const policy of merged.values()) {
|
|
30441
|
+
if ("category" in policy.target) localCategoryAction.set(policy.target.category, policy.action);
|
|
30442
|
+
}
|
|
30443
|
+
for (const policy of remotePolicies) {
|
|
30444
|
+
if (!policy.enabled) {
|
|
30445
|
+
disabled.push(policy);
|
|
30446
|
+
continue;
|
|
30447
|
+
}
|
|
30448
|
+
const key = policyKey(policy);
|
|
30449
|
+
const floor = floorFor(policy, categoryByRuleId);
|
|
30450
|
+
let localFloor = null;
|
|
30451
|
+
if ("ruleId" in policy.target) {
|
|
30452
|
+
const category = categoryByRuleId.get(policy.target.ruleId);
|
|
30453
|
+
if (category !== void 0) localFloor = localCategoryAction.get(category) ?? null;
|
|
30454
|
+
}
|
|
30455
|
+
const effectiveFloor = strongerOf(floor, localFloor);
|
|
30456
|
+
const clamped = effectiveFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[effectiveFloor] ? { ...policy, action: effectiveFloor } : policy;
|
|
30457
|
+
const existing = merged.get(key);
|
|
30458
|
+
if (existing === void 0) {
|
|
30459
|
+
merged.set(key, clamped);
|
|
30460
|
+
continue;
|
|
30461
|
+
}
|
|
30462
|
+
if (ACTION_STRENGTH[clamped.action] > ACTION_STRENGTH[existing.action]) {
|
|
30463
|
+
merged.set(key, clamped);
|
|
30464
|
+
}
|
|
30465
|
+
}
|
|
30466
|
+
return [...merged.values(), ...disabled];
|
|
30467
|
+
}
|
|
30468
|
+
var AttachedDataGateway = class {
|
|
30469
|
+
constructor(deps) {
|
|
30470
|
+
this.deps = deps;
|
|
30471
|
+
}
|
|
30472
|
+
deps;
|
|
30473
|
+
/**
|
|
30474
|
+
* The control plane's OWN resolution of this session's inventory, captured by
|
|
30475
|
+
* ensureInventory. Null until the first successful forward — and it stays
|
|
30476
|
+
* null for the whole session when the control plane is unreachable, which is fine:
|
|
30477
|
+
* reKeyForForward then leaves the event's ids alone and the control plane resolves
|
|
30478
|
+
* what it can from the descriptors it already has.
|
|
30479
|
+
*/
|
|
30480
|
+
remoteInventory = null;
|
|
30481
|
+
// ---------------------------------------------------------------------
|
|
30482
|
+
// Writes: local first, then forward.
|
|
30483
|
+
// ---------------------------------------------------------------------
|
|
30484
|
+
async recordCapture(record2) {
|
|
30485
|
+
await this.deps.local.recordCapture(record2);
|
|
30486
|
+
await this.deps.forward.run(
|
|
30487
|
+
() => this.deps.client.ingestEvents({
|
|
30488
|
+
events: [record2.event],
|
|
30489
|
+
...record2.dedupe ? { dedupe: record2.dedupe } : {}
|
|
30490
|
+
}),
|
|
30491
|
+
{ decisionPath: true }
|
|
30492
|
+
);
|
|
30493
|
+
}
|
|
30494
|
+
async ensureInventory(ctx) {
|
|
30495
|
+
const resolved = await this.deps.local.ensureInventory(ctx);
|
|
30496
|
+
const remote = await this.deps.forward.run(() => this.deps.client.ingestInventory(ctx));
|
|
30497
|
+
this.remoteInventory = remote.ok ? remote.value : null;
|
|
30498
|
+
const snapshot = await (async () => {
|
|
30499
|
+
try {
|
|
30500
|
+
return await this.deps.posture?.prepare() ?? null;
|
|
30501
|
+
} catch {
|
|
30502
|
+
return null;
|
|
30503
|
+
}
|
|
30504
|
+
})();
|
|
30505
|
+
if (snapshot) {
|
|
30506
|
+
try {
|
|
30507
|
+
await withTimeout(
|
|
30508
|
+
this.deps.posture?.send(snapshot) ?? Promise.resolve(),
|
|
30509
|
+
REQUEST_TIMEOUT_MS
|
|
30510
|
+
);
|
|
30511
|
+
} catch {
|
|
30512
|
+
}
|
|
30513
|
+
}
|
|
30514
|
+
return resolved;
|
|
30515
|
+
}
|
|
30516
|
+
// The id is minted CLIENT-side and stored verbatim: the control plane does NOT
|
|
30517
|
+
// re-key it. `pgAuditValues` writes `id: event.id` and carries tenancy in
|
|
30518
|
+
// its own scoping columns, so the device and the forwarded copy
|
|
30519
|
+
// share one id space — which is what makes a re-post idempotent at all.
|
|
30520
|
+
//
|
|
30521
|
+
// Re-posts collapse via `onConflictDoUpdate` on the `id` PK, guarded by
|
|
30522
|
+
// `setWhere eventType = 'session'` (NOT onConflictDoNothing). That guard is
|
|
30523
|
+
// what makes an attached retry safe: a capture-stubbed session row can still
|
|
30524
|
+
// be HEALED by the authoritative root, while a duplicate non-session event —
|
|
30525
|
+
// a retried tool_call, exactly this path — can never stomp a populated row.
|
|
30526
|
+
async recordAuditEvent(event) {
|
|
30527
|
+
await this.deps.local.recordAuditEvent(event);
|
|
30528
|
+
await this.deps.forward.run(
|
|
30529
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(event, this.remoteInventory))
|
|
30530
|
+
);
|
|
30531
|
+
}
|
|
30532
|
+
// Attached `llm_call` is written locally by the inner gateway, then routed to
|
|
30533
|
+
// the control plane through the existing `recordAuditEvent` ingest (no dedicated
|
|
30534
|
+
// client method yet) by pre-building the audit event from the natural key.
|
|
30535
|
+
// The forward goes DIRECTLY to the client rather than through this.recordAuditEvent,
|
|
30536
|
+
// which would write the event to the local store a second time.
|
|
30537
|
+
async recordLlmCall(input) {
|
|
30538
|
+
await this.deps.local.recordLlmCall(input);
|
|
30539
|
+
await this.deps.forward.run(
|
|
30540
|
+
() => this.deps.client.recordAuditEvent(
|
|
30541
|
+
reKeyForForward(llmAuditEvent(input), this.remoteInventory)
|
|
30542
|
+
)
|
|
30543
|
+
);
|
|
30544
|
+
}
|
|
30545
|
+
/**
|
|
30546
|
+
* Forward one batch, item by item, under ONE aggregate deadline.
|
|
30547
|
+
*
|
|
30548
|
+
* Per-item budgets bound each request and nothing bounded their sum — see
|
|
30549
|
+
* BATCH_FORWARD_BUDGET_MS. When the deadline passes the remainder is dropped
|
|
30550
|
+
* rather than sent: the local write has already succeeded, so every caller
|
|
30551
|
+
* has a correct result to return, and a drop is the outcome this path is
|
|
30552
|
+
* built to accept (G8) where a blown hook timeout is not.
|
|
30553
|
+
*
|
|
30554
|
+
* Serial rather than concurrent on purpose. Firing N requests at once would
|
|
30555
|
+
* trade a latency problem for a burst the plane's own per-key rate limiting
|
|
30556
|
+
* would answer with the refusals the breaker then counts.
|
|
30557
|
+
*
|
|
30558
|
+
* WHAT IS DROPPED IS COUNTED. Every other forward failure ends in
|
|
30559
|
+
* `ForwardPolicy.run`'s catch and moves the breaker's file, which is what
|
|
30560
|
+
* lets status call the forward unhealthy; this path returns BEFORE `run` is
|
|
30561
|
+
* reached, so without the tally in `forward-drops.ts` a slow-but-answering
|
|
30562
|
+
* plane produces no failures, keeps the breaker closed, renders a healthy
|
|
30563
|
+
* block, and discards the tail of every batch indefinitely.
|
|
30564
|
+
*/
|
|
30565
|
+
async forwardBatch(inputs, toEvent) {
|
|
30566
|
+
const deadline = Date.now() + BATCH_FORWARD_BUDGET_MS;
|
|
30567
|
+
for (let i = 0; i < inputs.length; i += 1) {
|
|
30568
|
+
const now = Date.now();
|
|
30569
|
+
if (now >= deadline) {
|
|
30570
|
+
recordForwardDrops(this.deps.dataDir, inputs.length - i, now);
|
|
30571
|
+
return;
|
|
30572
|
+
}
|
|
30573
|
+
const input = inputs[i];
|
|
30574
|
+
await this.deps.forward.run(
|
|
30575
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(toEvent(input), this.remoteInventory))
|
|
30576
|
+
);
|
|
30577
|
+
}
|
|
30578
|
+
}
|
|
30579
|
+
// Delegated as a BATCH rather than looped over recordLlmCall: the inner
|
|
30580
|
+
// gateway may write the whole batch in one local transaction, and looping
|
|
30581
|
+
// here would replace that with N separate local writes.
|
|
30582
|
+
async recordLlmCalls(inputs) {
|
|
30583
|
+
await this.deps.local.recordLlmCalls(inputs);
|
|
30584
|
+
await this.forwardBatch(inputs, (input) => llmAuditEvent(input));
|
|
30585
|
+
}
|
|
30586
|
+
// `input.inspections` (secrets detected client-side in the tool's masked
|
|
30587
|
+
// target) ride along on the request's `inspections` field — the control plane
|
|
30588
|
+
// persists each as an inspection_findings row linked to this audit event
|
|
30589
|
+
// (see RecordAuditEventRequest in @akasecurity/schema). The masked
|
|
30590
|
+
// `target` already rides `input.attributes`, so no raw secret leaks either
|
|
30591
|
+
// way — this only stops the FINDING row itself from being dropped.
|
|
30592
|
+
async recordToolCalls(inputs) {
|
|
30593
|
+
await this.deps.local.recordToolCalls(inputs);
|
|
30594
|
+
await this.forwardBatch(inputs, (input) => toolAuditEvent(input));
|
|
30595
|
+
}
|
|
30596
|
+
// Forwarded as a `config_scan` audit event: there is no dedicated
|
|
30597
|
+
// config-scan ingest endpoint, and the audit-event door is the one the
|
|
30598
|
+
// control plane already opens for client-minted, idempotent records.
|
|
30599
|
+
//
|
|
30600
|
+
// ONLY `scanEvent` CROSSES, and unlike `recordCapture` the plane cannot
|
|
30601
|
+
// re-derive the rest. A `ConfigScanRecord` is four things committed together
|
|
30602
|
+
// locally — the inventory `items`, this audit event, and the posture
|
|
30603
|
+
// `definitions`/`findings` that reference it — and three of them stay on the
|
|
30604
|
+
// device. Say that plainly rather than let the asymmetry with `recordCapture`
|
|
30605
|
+
// read as the same argument: there, findings are omitted BECAUSE the plane
|
|
30606
|
+
// re-derives them from `Event.content`; here there is no content to re-derive
|
|
30607
|
+
// from, so what is omitted is simply not sent.
|
|
30608
|
+
//
|
|
30609
|
+
// That is the wire contract as it stands rather than an oversight to patch
|
|
30610
|
+
// here. `items` has no route at all, and `RecordAuditEventRequest.inspections`
|
|
30611
|
+
// is documented as tool-call findings — widening it to carry config-scan
|
|
30612
|
+
// findings is an egress change (a posture finding's `maskedMatch` holds the
|
|
30613
|
+
// matched command) and a decision about what an attached deployment is
|
|
30614
|
+
// entitled to, not a bug fix. An attached machine's config posture therefore
|
|
30615
|
+
// reaches the plane as the event only; the dashboard's own view of it is the
|
|
30616
|
+
// local store.
|
|
30617
|
+
async recordConfigScan(record2) {
|
|
30618
|
+
await this.deps.local.recordConfigScan(record2);
|
|
30619
|
+
await this.deps.forward.run(
|
|
30620
|
+
() => this.deps.client.recordAuditEvent(reKeyForForward(record2.scanEvent, this.remoteInventory))
|
|
30621
|
+
);
|
|
30622
|
+
}
|
|
30623
|
+
async recordBlockedDetection(entry) {
|
|
30624
|
+
return this.deps.local.recordBlockedDetection(entry);
|
|
30625
|
+
}
|
|
30626
|
+
/**
|
|
30627
|
+
* LOCAL-ONLY, deliberately. The shares API is read-plus-decision-override
|
|
30628
|
+
* with no egress ingest endpoint, so there is nothing to forward to; adding a
|
|
30629
|
+
* forward here would be inventing a wire contract that does not exist. The
|
|
30630
|
+
* local write is the whole operation, and its summary is the real one — the
|
|
30631
|
+
* scanner reads a throw as a FAILED WRITE and skips its ledger commit, so
|
|
30632
|
+
* returning the inner gateway's result keeps the retry semantics honest.
|
|
30633
|
+
*/
|
|
30634
|
+
async recordProjectEgress(input) {
|
|
30635
|
+
return this.deps.local.recordProjectEgress(input);
|
|
30636
|
+
}
|
|
30637
|
+
// ---------------------------------------------------------------------
|
|
30638
|
+
// Reads and device-local ledgers: pure delegation.
|
|
30639
|
+
// ---------------------------------------------------------------------
|
|
30640
|
+
async configInventoryReport() {
|
|
30641
|
+
return this.deps.local.configInventoryReport();
|
|
30642
|
+
}
|
|
30643
|
+
async readSessionProvider(sessionId) {
|
|
30644
|
+
return this.deps.local.readSessionProvider(sessionId);
|
|
30645
|
+
}
|
|
30646
|
+
async facets() {
|
|
30647
|
+
return this.deps.local.facets();
|
|
30648
|
+
}
|
|
30649
|
+
/**
|
|
30650
|
+
* Delegated UNMODIFIED — including its refusals.
|
|
30651
|
+
*
|
|
30652
|
+
* This is a fail-secure boundary: it decides whether an approved exception
|
|
30653
|
+
* lets a blocked action through. Under local-first the local store owns the
|
|
30654
|
+
* exception ledger, so the honest answer is whatever it says; wrapping this
|
|
30655
|
+
* in a fallback (`catch { return true }`, or defaulting on a timeout) would
|
|
30656
|
+
* turn a store error into a granted bypass. If the inner gateway rejects,
|
|
30657
|
+
* this rejects, and the runtime's own handling decides — which is asserted
|
|
30658
|
+
* end-to-end through runtime.capture rather than here.
|
|
30659
|
+
*/
|
|
30660
|
+
async consumeException(id) {
|
|
30661
|
+
return this.deps.local.consumeException(id);
|
|
30662
|
+
}
|
|
30663
|
+
async recentFindings(opts) {
|
|
30664
|
+
return this.deps.local.recentFindings(opts);
|
|
30665
|
+
}
|
|
30666
|
+
async healthSummary() {
|
|
30667
|
+
return this.deps.local.healthSummary();
|
|
30668
|
+
}
|
|
30669
|
+
async activityByDay(days) {
|
|
30670
|
+
return this.deps.local.activityByDay(days);
|
|
30671
|
+
}
|
|
30672
|
+
async tokenReports() {
|
|
30673
|
+
return this.deps.local.tokenReports();
|
|
30674
|
+
}
|
|
30675
|
+
async knownContentHashes() {
|
|
30676
|
+
return this.deps.local.knownContentHashes();
|
|
30677
|
+
}
|
|
30678
|
+
async scanLedger(rulesetHash) {
|
|
30679
|
+
return this.deps.local.scanLedger(rulesetHash);
|
|
30680
|
+
}
|
|
30681
|
+
async recordScanned(entries) {
|
|
30682
|
+
return this.deps.local.recordScanned(entries);
|
|
30683
|
+
}
|
|
30684
|
+
async getRuleProbeVerdict(ruleKey) {
|
|
30685
|
+
return this.deps.local.getRuleProbeVerdict(ruleKey);
|
|
30686
|
+
}
|
|
30687
|
+
async setRuleProbeVerdict(ruleKey, verdict, worstProbeMs) {
|
|
30688
|
+
return this.deps.local.setRuleProbeVerdict(ruleKey, verdict, worstProbeMs);
|
|
30689
|
+
}
|
|
30690
|
+
async openAtRestKeysForPath(path) {
|
|
30691
|
+
return this.deps.local.openAtRestKeysForPath(path);
|
|
30692
|
+
}
|
|
30693
|
+
async resolvedAtRestKeysForPath(path) {
|
|
30694
|
+
return this.deps.local.resolvedAtRestKeysForPath(path);
|
|
30695
|
+
}
|
|
30696
|
+
async insertResolution(input) {
|
|
30697
|
+
return this.deps.local.insertResolution(input);
|
|
30698
|
+
}
|
|
30699
|
+
async close() {
|
|
30700
|
+
return this.deps.local.close();
|
|
30701
|
+
}
|
|
30702
|
+
// ---------------------------------------------------------------------
|
|
30703
|
+
// Policy
|
|
30704
|
+
// ---------------------------------------------------------------------
|
|
30705
|
+
async getPolicyBundle() {
|
|
30706
|
+
const local = await this.deps.local.getPolicyBundle();
|
|
30707
|
+
const cached2 = await (async () => {
|
|
30708
|
+
try {
|
|
30709
|
+
return await this.deps.readCachedBundle();
|
|
30710
|
+
} catch {
|
|
30711
|
+
return null;
|
|
30712
|
+
}
|
|
30713
|
+
})();
|
|
30714
|
+
if (cached2 === null) return local;
|
|
30715
|
+
const byRuleId = /* @__PURE__ */ new Map();
|
|
30716
|
+
for (const rule of [...local.rules ?? [], ...cached2.rules ?? []]) {
|
|
30717
|
+
if (!byRuleId.has(rule.id)) byRuleId.set(rule.id, rule);
|
|
30718
|
+
}
|
|
30719
|
+
const rules = [...byRuleId.values()];
|
|
30720
|
+
return {
|
|
30721
|
+
...local,
|
|
30722
|
+
// The remote version identifies the composed bundle for the poller.
|
|
30723
|
+
version: cached2.version,
|
|
30724
|
+
rules,
|
|
30725
|
+
policies: mergeRaiseOnly(
|
|
30726
|
+
local.policies,
|
|
30727
|
+
cached2.policies,
|
|
30728
|
+
ruleCategoryMap(cached2.rules, local.rules)
|
|
30729
|
+
),
|
|
30730
|
+
customKeywords: [...local.customKeywords, ...cached2.customKeywords]
|
|
30731
|
+
// `rulesComplete` is a STANDALONE-ONLY signal (the user's local installed
|
|
30732
|
+
// snapshot) and is taken from the LOCAL bundle only — never from the wire
|
|
30733
|
+
// or the on-disk cache. Honoring a cached one would hand the control plane, or
|
|
30734
|
+
// anything able to write policy-cache.json, a kill-switch over the
|
|
30735
|
+
// compiled-in bundled packs: `{ rulesComplete: true, rules: [] }` would
|
|
30736
|
+
// zero local detection. Spread from `local` above, and deliberately not
|
|
30737
|
+
// re-read from `cached` here.
|
|
30738
|
+
//
|
|
30739
|
+
// THREE MORE OF THE CACHED BUNDLE'S FIELDS ARE DROPPED, each on purpose,
|
|
30740
|
+
// and each named here so a reader can tell a decision from an omission:
|
|
30741
|
+
//
|
|
30742
|
+
// `exceptions` — an exception SUPPRESSES a detection, so honoring
|
|
30743
|
+
// one from an unsigned on-disk cache would let
|
|
30744
|
+
// anything able to write that file turn rules off.
|
|
30745
|
+
// Every other field this merge accepts can only
|
|
30746
|
+
// RAISE enforcement; this is the one that cannot,
|
|
30747
|
+
// so it stays local-only until the bundle is
|
|
30748
|
+
// signed. Exceptions remain a device-local ledger.
|
|
30749
|
+
// `reversibleRuleIds` — the Redact & Vault archetype makes a redaction
|
|
30750
|
+
// recoverable, which is a CUSTODY change: it puts
|
|
30751
|
+
// the detected value in the local vault instead of
|
|
30752
|
+
// destroying it. Taking that instruction from the
|
|
30753
|
+
// cache would let a remote party turn one-way
|
|
30754
|
+
// redaction into retention. Dropping it keeps the
|
|
30755
|
+
// one-way behaviour, which the schema itself calls
|
|
30756
|
+
// "the safe direction to default".
|
|
30757
|
+
// `ruleVersions` — remote rules fall back to their own spec version.
|
|
30758
|
+
// Cosmetic rather than protective: it only affects
|
|
30759
|
+
// how a finding is version-attributed, and the two
|
|
30760
|
+
// sides may therefore attribute org rules
|
|
30761
|
+
// differently. Worth carrying once there is a
|
|
30762
|
+
// reader that needs it; nothing reads it today.
|
|
30763
|
+
};
|
|
30764
|
+
}
|
|
30765
|
+
// ---------------------------------------------------------------------
|
|
30766
|
+
// LocalStoreMaintenance — by delegation (D3).
|
|
30767
|
+
//
|
|
30768
|
+
// Implementing these is what actually closes the skipped-local-maintenance
|
|
30769
|
+
// gap: the OSS structural guard `hasLocalStoreMaintenance()` is satisfied by
|
|
30770
|
+
// any object carrying all five, so the composite qualifies and SessionStart
|
|
30771
|
+
// runs maintenance on the device's real store.
|
|
30772
|
+
//
|
|
30773
|
+
// ⚠ Two of the five are SYNCHRONOUS and must stay that way. `handle-session-start`
|
|
30774
|
+
// calls `capWarnEraEnforcement` without `await` and uses `staleBinaryNotice`'s
|
|
30775
|
+
// return value directly; declaring them `async` here would hand those call
|
|
30776
|
+
// sites a Promise and silently break both.
|
|
30777
|
+
// ---------------------------------------------------------------------
|
|
30778
|
+
async sweepTerminalExceptions(retentionMs) {
|
|
30779
|
+
return this.deps.local.sweepTerminalExceptions(retentionMs);
|
|
30780
|
+
}
|
|
30781
|
+
capWarnEraEnforcement(policyMode) {
|
|
30782
|
+
return this.deps.local.capWarnEraEnforcement(policyMode);
|
|
30783
|
+
}
|
|
30784
|
+
async recordProjectFiles(projectId, scan2) {
|
|
30785
|
+
return this.deps.local.recordProjectFiles(projectId, scan2);
|
|
30786
|
+
}
|
|
30787
|
+
async reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
|
|
30788
|
+
return this.deps.local.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
|
|
30789
|
+
}
|
|
30790
|
+
staleBinaryNotice(currentVersion) {
|
|
30791
|
+
return this.deps.local.staleBinaryNotice(currentVersion);
|
|
30792
|
+
}
|
|
30793
|
+
};
|
|
30794
|
+
function reKeyForForward(event, remote) {
|
|
30795
|
+
if (remote === null) {
|
|
30796
|
+
const stripped = { ...event };
|
|
30797
|
+
delete stripped.hostId;
|
|
30798
|
+
delete stripped.harnessId;
|
|
30799
|
+
delete stripped.sourceProjectId;
|
|
30800
|
+
return stripped;
|
|
30801
|
+
}
|
|
30802
|
+
const rekeyed = { ...event };
|
|
30803
|
+
delete rekeyed.hostId;
|
|
30804
|
+
delete rekeyed.harnessId;
|
|
30805
|
+
delete rekeyed.sourceProjectId;
|
|
30806
|
+
if (remote.hostId !== void 0) rekeyed.hostId = remote.hostId;
|
|
30807
|
+
if (remote.harnessId !== void 0) rekeyed.harnessId = remote.harnessId;
|
|
30808
|
+
if (remote.sourceProjectId !== void 0) rekeyed.sourceProjectId = remote.sourceProjectId;
|
|
30809
|
+
return rekeyed;
|
|
30810
|
+
}
|
|
30811
|
+
var BATCH_FORWARD_BUDGET_MS = 3e3;
|
|
30812
|
+
function llmAuditEvent(input) {
|
|
30813
|
+
return {
|
|
30814
|
+
id: llmCallId(input.sessionId, input.messageId),
|
|
30815
|
+
eventType: "llm_call",
|
|
30816
|
+
startedAt: input.startedAt,
|
|
30817
|
+
parentId: input.parentId,
|
|
30818
|
+
rootSessionId: input.rootSessionId,
|
|
30819
|
+
attributes: input.attributes
|
|
30820
|
+
};
|
|
30821
|
+
}
|
|
30822
|
+
function toolAuditEvent(input) {
|
|
30823
|
+
return {
|
|
30824
|
+
id: toolCallId(input.sessionId, input.toolUseId),
|
|
30825
|
+
eventType: "tool_call",
|
|
30826
|
+
startedAt: input.startedAt,
|
|
30827
|
+
parentId: input.parentId,
|
|
30828
|
+
rootSessionId: input.rootSessionId,
|
|
30829
|
+
attributes: input.attributes,
|
|
30830
|
+
inspections: input.inspections
|
|
30831
|
+
};
|
|
30832
|
+
}
|
|
30833
|
+
|
|
30834
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
30835
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
30836
|
+
import { readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
30837
|
+
import { join as join19 } from "path";
|
|
30838
|
+
|
|
30839
|
+
// ../../packages/plugin-runtime/src/attached/atomic-publish.ts
|
|
30840
|
+
import { rename as rename2 } from "fs/promises";
|
|
30841
|
+
var RETRYABLE = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
30842
|
+
var ATTEMPTS = 5;
|
|
30843
|
+
var delay = (ms) => new Promise((resolve2) => {
|
|
30844
|
+
setTimeout(resolve2, ms);
|
|
30845
|
+
});
|
|
30846
|
+
async function publishByRename(tmp, file2, move = rename2) {
|
|
30847
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
30848
|
+
try {
|
|
30849
|
+
await move(tmp, file2);
|
|
30850
|
+
return;
|
|
30851
|
+
} catch (err) {
|
|
30852
|
+
const code = err.code;
|
|
30853
|
+
if (attempt >= ATTEMPTS || code === void 0 || !RETRYABLE.has(code)) throw err;
|
|
30854
|
+
await delay(attempt * 10);
|
|
30855
|
+
}
|
|
30856
|
+
}
|
|
30857
|
+
}
|
|
30858
|
+
|
|
30859
|
+
// ../../packages/plugin-runtime/src/attached/policy-store.ts
|
|
30860
|
+
function createPolicyStore(dir = dataDir()) {
|
|
30861
|
+
const file2 = join19(dir, "policy-cache.json");
|
|
30862
|
+
async function read() {
|
|
30863
|
+
try {
|
|
30864
|
+
const raw = await readFile2(file2, "utf8");
|
|
30865
|
+
const parsed2 = JSON.parse(raw);
|
|
30866
|
+
if (typeof parsed2 !== "object" || parsed2 === null) return null;
|
|
30867
|
+
const record2 = parsed2;
|
|
30868
|
+
const bundle = PolicyBundle.parse(record2.bundle);
|
|
30869
|
+
const fetchedAtMs = typeof record2.fetchedAtMs === "number" ? record2.fetchedAtMs : 0;
|
|
30870
|
+
const etag = typeof record2.etag === "string" ? record2.etag : void 0;
|
|
30871
|
+
return { bundle, fetchedAtMs, ...etag === void 0 ? {} : { etag } };
|
|
30872
|
+
} catch {
|
|
30873
|
+
return null;
|
|
30874
|
+
}
|
|
30875
|
+
}
|
|
30876
|
+
async function write(bundle, etag) {
|
|
30877
|
+
await ensureDataDir(dir);
|
|
30878
|
+
const stored = {
|
|
30879
|
+
bundle,
|
|
30880
|
+
fetchedAtMs: Date.now(),
|
|
30881
|
+
...etag === void 0 ? {} : { etag }
|
|
30882
|
+
};
|
|
30883
|
+
const tmp = `${file2}.${randomUUID16()}.tmp`;
|
|
30884
|
+
try {
|
|
30885
|
+
await writeFile2(tmp, JSON.stringify(stored), {
|
|
30886
|
+
encoding: "utf8",
|
|
30887
|
+
mode: DATA_FILE_MODE,
|
|
30888
|
+
flag: "wx"
|
|
30889
|
+
});
|
|
30890
|
+
await publishByRename(tmp, file2);
|
|
30891
|
+
} catch (err) {
|
|
30892
|
+
await rm(tmp, { force: true }).catch(() => void 0);
|
|
30893
|
+
throw err;
|
|
30894
|
+
}
|
|
30895
|
+
}
|
|
30896
|
+
return { read, write, file: file2 };
|
|
30897
|
+
}
|
|
30898
|
+
|
|
30899
|
+
// ../../packages/remote/src/http.ts
|
|
30900
|
+
import { request as httpRequest } from "http";
|
|
30901
|
+
import { request as httpsRequest } from "https";
|
|
30902
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
30903
|
+
var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
30904
|
+
var RemoteRequestError = class extends Error {
|
|
30905
|
+
constructor(status) {
|
|
30906
|
+
super(`control-plane request failed with status ${String(status)}`);
|
|
30907
|
+
this.status = status;
|
|
30908
|
+
this.name = "RemoteRequestError";
|
|
30909
|
+
}
|
|
30910
|
+
status;
|
|
30911
|
+
};
|
|
30912
|
+
var RemoteRequestInvalid = class extends Error {
|
|
30913
|
+
constructor(route, cause) {
|
|
30914
|
+
super(`refusing to send a malformed body to ${route}`);
|
|
30915
|
+
this.cause = cause;
|
|
30916
|
+
this.name = "RemoteRequestInvalid";
|
|
30917
|
+
}
|
|
30918
|
+
cause;
|
|
30919
|
+
};
|
|
30920
|
+
var RemoteResponseInvalid = class extends Error {
|
|
30921
|
+
constructor(route, detail) {
|
|
30922
|
+
super(`control plane answered ${route} with ${detail}`);
|
|
30923
|
+
this.name = "RemoteResponseInvalid";
|
|
30924
|
+
}
|
|
30925
|
+
};
|
|
30926
|
+
var RemoteTransportError = class extends Error {
|
|
30927
|
+
/**
|
|
30928
|
+
* The status the peer sent, when headers arrived and only the BODY was
|
|
30929
|
+
* refused.
|
|
30930
|
+
*
|
|
30931
|
+
* Undefined for the ordinary case this class was written for — no answer at
|
|
30932
|
+
* all. It exists because two paths reject after a status has already been
|
|
30933
|
+
* delivered: an oversized body and an aborted response. Discarding it there
|
|
30934
|
+
* reported a deployment answering 401 with a verbose body as a network
|
|
30935
|
+
* outage, which sends the reader to look at their network instead of their
|
|
30936
|
+
* credential.
|
|
30937
|
+
*/
|
|
30938
|
+
constructor(reason, status) {
|
|
30939
|
+
super(`control-plane request did not complete: ${reason}`);
|
|
30940
|
+
this.status = status;
|
|
30941
|
+
this.name = "RemoteTransportError";
|
|
30942
|
+
}
|
|
30943
|
+
status;
|
|
30944
|
+
};
|
|
30945
|
+
async function send(options) {
|
|
30946
|
+
const url2 = new URL(options.url);
|
|
30947
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
30948
|
+
const send_ = url2.protocol === "http:" ? httpRequest : httpsRequest;
|
|
30949
|
+
const requestOptions = {
|
|
30950
|
+
method: options.method,
|
|
30951
|
+
headers: {
|
|
30952
|
+
// CALLER HEADERS FIRST, so this module's own are not overridable. Spread
|
|
30953
|
+
// last they win, and two of the values below are ones no caller may
|
|
30954
|
+
// replace: `x-api-key` is the credential, and `content-length` is the
|
|
30955
|
+
// byte count that stops a multi-byte body being truncated by the
|
|
30956
|
+
// receiver. `SendOptions.headers` is a free-form record on an exported
|
|
30957
|
+
// function, so "no caller does that today" is not the guarantee to rely
|
|
30958
|
+
// on. The one header any caller actually passes — `if-none-match` on the
|
|
30959
|
+
// conditional GET — is untouched by this order.
|
|
30960
|
+
...options.headers,
|
|
30961
|
+
// The credential. One header, matching what the deployment authenticates
|
|
30962
|
+
// on; a second copy in an `Authorization` header would be one more place
|
|
30963
|
+
// it can be logged by an intermediary for no gain.
|
|
30964
|
+
"x-api-key": options.apiKey,
|
|
30965
|
+
accept: "application/json",
|
|
30966
|
+
...options.body === void 0 ? {} : {
|
|
30967
|
+
"content-type": "application/json",
|
|
30968
|
+
// Byte length, not string length: a multi-byte body sent with a
|
|
30969
|
+
// character count is truncated by the receiver.
|
|
30970
|
+
"content-length": String(Buffer.byteLength(options.body))
|
|
30971
|
+
}
|
|
30972
|
+
}
|
|
30973
|
+
};
|
|
30974
|
+
return new Promise((resolve2, reject) => {
|
|
30975
|
+
let settled = false;
|
|
30976
|
+
const fail = (reason, status) => {
|
|
30977
|
+
if (settled) return;
|
|
30978
|
+
settled = true;
|
|
30979
|
+
reject(new RemoteTransportError(reason, status));
|
|
30980
|
+
};
|
|
30981
|
+
const req = send_(url2, requestOptions, (res) => {
|
|
30982
|
+
const chunks = [];
|
|
30983
|
+
let size = 0;
|
|
30984
|
+
res.on("data", (chunk) => {
|
|
30985
|
+
size += chunk.length;
|
|
30986
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
30987
|
+
fail(`response exceeded ${String(MAX_RESPONSE_BYTES)} bytes`, res.statusCode);
|
|
30988
|
+
res.destroy();
|
|
30989
|
+
req.destroy();
|
|
30990
|
+
return;
|
|
30991
|
+
}
|
|
30992
|
+
chunks.push(chunk);
|
|
30993
|
+
});
|
|
30994
|
+
res.on("aborted", () => {
|
|
30995
|
+
fail("the response was aborted", res.statusCode);
|
|
30996
|
+
});
|
|
30997
|
+
res.on("end", () => {
|
|
30998
|
+
if (settled) return;
|
|
30999
|
+
settled = true;
|
|
31000
|
+
resolve2({
|
|
31001
|
+
status: res.statusCode ?? 0,
|
|
31002
|
+
headers: res.headers,
|
|
31003
|
+
body: Buffer.concat(chunks).toString("utf8")
|
|
31004
|
+
});
|
|
31005
|
+
});
|
|
31006
|
+
});
|
|
31007
|
+
const deadline = setTimeout(() => {
|
|
31008
|
+
fail(`no response within ${String(timeoutMs)}ms`);
|
|
31009
|
+
req.destroy();
|
|
31010
|
+
}, timeoutMs);
|
|
31011
|
+
deadline.unref();
|
|
31012
|
+
req.on("upgrade", (_res, socket) => {
|
|
31013
|
+
fail("the deployment answered with a protocol upgrade");
|
|
31014
|
+
socket.destroy();
|
|
31015
|
+
});
|
|
31016
|
+
req.on("close", () => {
|
|
31017
|
+
fail("the connection closed before a response was read");
|
|
31018
|
+
clearTimeout(deadline);
|
|
31019
|
+
});
|
|
31020
|
+
req.on("error", (err) => {
|
|
31021
|
+
fail(err.message);
|
|
31022
|
+
});
|
|
31023
|
+
if (options.body !== void 0) req.write(options.body);
|
|
31024
|
+
req.end();
|
|
31025
|
+
});
|
|
31026
|
+
}
|
|
31027
|
+
|
|
31028
|
+
// ../../packages/remote/src/client.ts
|
|
31029
|
+
var ROUTES = {
|
|
31030
|
+
events: "/v1/events",
|
|
31031
|
+
auditEvents: "/v1/audit-events",
|
|
31032
|
+
inventory: "/v1/inventory",
|
|
31033
|
+
storePosture: "/v1/store-posture",
|
|
31034
|
+
policyBundle: "/v1/policy-bundle",
|
|
31035
|
+
whoami: "/v1/plugin/whoami"
|
|
31036
|
+
};
|
|
31037
|
+
function headerValue(response, name) {
|
|
31038
|
+
const raw = response.headers[name];
|
|
31039
|
+
if (raw === void 0) return void 0;
|
|
31040
|
+
return Array.isArray(raw) ? raw[0] : raw;
|
|
31041
|
+
}
|
|
31042
|
+
function okBody(response) {
|
|
31043
|
+
if (response.status < 200 || response.status >= 300) {
|
|
31044
|
+
throw new RemoteRequestError(response.status);
|
|
31045
|
+
}
|
|
31046
|
+
return response.body;
|
|
31047
|
+
}
|
|
31048
|
+
function parsed(schema, body, route) {
|
|
31049
|
+
let json2;
|
|
31050
|
+
try {
|
|
31051
|
+
json2 = JSON.parse(body);
|
|
31052
|
+
} catch {
|
|
31053
|
+
throw new RemoteResponseInvalid(route, "a body that is not JSON");
|
|
31054
|
+
}
|
|
31055
|
+
const result = schema.safeParse(json2);
|
|
31056
|
+
if (!result.success) {
|
|
31057
|
+
throw new RemoteResponseInvalid(route, "a body this client cannot read");
|
|
31058
|
+
}
|
|
31059
|
+
return result.data;
|
|
31060
|
+
}
|
|
31061
|
+
function withoutTrailingSlashes(endpoint) {
|
|
31062
|
+
let end = endpoint.length;
|
|
31063
|
+
while (end > 0 && endpoint.charCodeAt(end - 1) === SLASH) end -= 1;
|
|
31064
|
+
return endpoint.slice(0, end);
|
|
31065
|
+
}
|
|
31066
|
+
var SLASH = "/".charCodeAt(0);
|
|
31067
|
+
function createRemoteClient(options) {
|
|
31068
|
+
const base = withoutTrailingSlashes(options.endpoint);
|
|
31069
|
+
const url2 = (route) => `${base}${route}`;
|
|
31070
|
+
const common = { apiKey: options.apiKey, timeoutMs: options.timeoutMs };
|
|
31071
|
+
return {
|
|
31072
|
+
async ingestEvents(batch) {
|
|
31073
|
+
const response = await send({
|
|
31074
|
+
...common,
|
|
31075
|
+
method: "POST",
|
|
31076
|
+
url: url2(ROUTES.events),
|
|
31077
|
+
body: JSON.stringify(batch)
|
|
31078
|
+
});
|
|
31079
|
+
return parsed(IngestAck, okBody(response), ROUTES.events);
|
|
31080
|
+
},
|
|
31081
|
+
async ingestInventory(context) {
|
|
31082
|
+
const response = await send({
|
|
31083
|
+
...common,
|
|
31084
|
+
method: "POST",
|
|
31085
|
+
url: url2(ROUTES.inventory),
|
|
31086
|
+
body: JSON.stringify(context)
|
|
31087
|
+
});
|
|
31088
|
+
return parsed(ResolvedInventory, okBody(response), ROUTES.inventory);
|
|
31089
|
+
},
|
|
31090
|
+
async recordAuditEvent(event) {
|
|
31091
|
+
const validated = RecordAuditEventRequest.safeParse(event);
|
|
31092
|
+
if (!validated.success) throw new RemoteRequestInvalid(ROUTES.auditEvents, validated.error);
|
|
31093
|
+
const submission = validated.data;
|
|
31094
|
+
const response = await send({
|
|
31095
|
+
...common,
|
|
31096
|
+
method: "POST",
|
|
31097
|
+
url: url2(ROUTES.auditEvents),
|
|
31098
|
+
body: JSON.stringify(submission)
|
|
31099
|
+
});
|
|
31100
|
+
okBody(response);
|
|
31101
|
+
},
|
|
31102
|
+
async reportStorePosture(snapshot) {
|
|
31103
|
+
const response = await send({
|
|
31104
|
+
...common,
|
|
31105
|
+
method: "POST",
|
|
31106
|
+
url: url2(ROUTES.storePosture),
|
|
31107
|
+
body: JSON.stringify(snapshot)
|
|
31108
|
+
});
|
|
31109
|
+
okBody(response);
|
|
31110
|
+
},
|
|
31111
|
+
async getPolicyBundle(etag) {
|
|
31112
|
+
const response = await send({
|
|
31113
|
+
...common,
|
|
31114
|
+
method: "GET",
|
|
31115
|
+
url: url2(ROUTES.policyBundle),
|
|
31116
|
+
...etag === void 0 ? {} : { headers: { "if-none-match": etag } }
|
|
31117
|
+
});
|
|
31118
|
+
if (response.status === 304) {
|
|
31119
|
+
return { changed: false, etag: headerValue(response, "etag") ?? etag };
|
|
31120
|
+
}
|
|
31121
|
+
const bundle = parsed(PolicyBundle, okBody(response), ROUTES.policyBundle);
|
|
31122
|
+
return { changed: true, bundle, etag: headerValue(response, "etag") };
|
|
31123
|
+
},
|
|
31124
|
+
async whoami() {
|
|
31125
|
+
const response = await send({ ...common, method: "GET", url: url2(ROUTES.whoami) });
|
|
31126
|
+
return parsed(PluginWhoami, okBody(response), ROUTES.whoami);
|
|
31127
|
+
}
|
|
31128
|
+
};
|
|
31129
|
+
}
|
|
31130
|
+
|
|
31131
|
+
// ../../packages/plugin-runtime/src/attached/posture-reporter.ts
|
|
31132
|
+
var POSTURE_REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
31133
|
+
function createPostureReporter(deps) {
|
|
31134
|
+
async function prepare() {
|
|
31135
|
+
try {
|
|
31136
|
+
const state = await withTimeout(deps.store.read(), REQUEST_TIMEOUT_MS);
|
|
31137
|
+
if (state === null) return null;
|
|
31138
|
+
const nowMs = deps.now();
|
|
31139
|
+
const elapsed = nowMs - state.lastAttemptedAtMs;
|
|
31140
|
+
if (elapsed >= 0 && elapsed < POSTURE_REPORT_INTERVAL_MS) return null;
|
|
31141
|
+
try {
|
|
31142
|
+
await withTimeout(deps.store.markAttempted(state.deviceId, nowMs), REQUEST_TIMEOUT_MS);
|
|
31143
|
+
} catch {
|
|
31144
|
+
}
|
|
31145
|
+
const { readError, ...measurement } = deps.readStore();
|
|
31146
|
+
if (readError) return null;
|
|
31147
|
+
let plugin;
|
|
31148
|
+
try {
|
|
31149
|
+
plugin = await deps.pluginBlock?.();
|
|
31150
|
+
} catch {
|
|
31151
|
+
plugin = void 0;
|
|
31152
|
+
}
|
|
31153
|
+
return {
|
|
31154
|
+
deviceId: state.deviceId,
|
|
31155
|
+
hostname: deps.hostname(),
|
|
31156
|
+
capturedAt: nowMs,
|
|
31157
|
+
...measurement,
|
|
31158
|
+
// Omit the key rather than spread an explicit `undefined` —
|
|
31159
|
+
// exactOptionalPropertyTypes distinguishes the two, and the bridge in
|
|
31160
|
+
// factory.ts keys on presence.
|
|
31161
|
+
...plugin === void 0 ? {} : { plugin }
|
|
31162
|
+
};
|
|
31163
|
+
} catch {
|
|
31164
|
+
return null;
|
|
31165
|
+
}
|
|
31166
|
+
}
|
|
31167
|
+
async function send2(snapshot) {
|
|
31168
|
+
try {
|
|
31169
|
+
await deps.report(snapshot);
|
|
31170
|
+
} catch {
|
|
31171
|
+
}
|
|
31172
|
+
}
|
|
31173
|
+
return { prepare, send: send2 };
|
|
31174
|
+
}
|
|
31175
|
+
|
|
31176
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
31177
|
+
import { statSync as statSync9 } from "fs";
|
|
31178
|
+
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
31179
|
+
|
|
31180
|
+
// ../../packages/plugin-runtime/src/attached/action-counts.ts
|
|
31181
|
+
function emptyActionCounts() {
|
|
31182
|
+
return Object.fromEntries(ACTION_TAKEN_KEYS.map((a) => [a, 0]));
|
|
31183
|
+
}
|
|
31184
|
+
function isActionTaken(value) {
|
|
31185
|
+
return ACTION_TAKEN_KEYS.includes(value);
|
|
31186
|
+
}
|
|
31187
|
+
|
|
31188
|
+
// ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
|
|
31189
|
+
var CAPTURE_EVENT_TYPES_SQL2 = `('prompt','response','code_change','tool_use')`;
|
|
31190
|
+
function isSchemaAbsent(err) {
|
|
31191
|
+
return err instanceof Error && /no such table/i.test(err.message);
|
|
31192
|
+
}
|
|
31193
|
+
function emptyReadout(readError = false) {
|
|
31194
|
+
const byAction = emptyActionCounts();
|
|
31195
|
+
return {
|
|
31196
|
+
storePresent: false,
|
|
31197
|
+
schemaVersion: null,
|
|
31198
|
+
findingsTotal: 0,
|
|
31199
|
+
findingsFirstAt: null,
|
|
31200
|
+
findingsLastAt: null,
|
|
31201
|
+
packs: [],
|
|
31202
|
+
policyCounts: { total: 0, disabled: 0, byAction },
|
|
31203
|
+
readError
|
|
31204
|
+
};
|
|
31205
|
+
}
|
|
31206
|
+
function readStorePosture(dbPath2) {
|
|
31207
|
+
try {
|
|
31208
|
+
statSync9(dbPath2);
|
|
31209
|
+
} catch (err) {
|
|
31210
|
+
const code = err.code;
|
|
31211
|
+
if (code === "ENOENT" || code === "ENOTDIR") return emptyReadout();
|
|
31212
|
+
return emptyReadout(true);
|
|
31213
|
+
}
|
|
31214
|
+
let db = null;
|
|
31215
|
+
let version2 = null;
|
|
31216
|
+
let packs2 = [];
|
|
31217
|
+
let policyCounts = {
|
|
31218
|
+
total: 0,
|
|
31219
|
+
disabled: 0,
|
|
31220
|
+
byAction: emptyActionCounts()
|
|
31221
|
+
};
|
|
31222
|
+
let findingsTotal = 0;
|
|
31223
|
+
let findingsFirstAt = null;
|
|
31224
|
+
let findingsLastAt = null;
|
|
31225
|
+
const currentReadout = () => ({
|
|
31226
|
+
storePresent: true,
|
|
31227
|
+
schemaVersion: version2,
|
|
31228
|
+
findingsTotal,
|
|
31229
|
+
findingsFirstAt,
|
|
31230
|
+
findingsLastAt,
|
|
31231
|
+
packs: packs2,
|
|
31232
|
+
policyCounts,
|
|
31233
|
+
readError: false
|
|
31234
|
+
});
|
|
31235
|
+
try {
|
|
31236
|
+
db = new DatabaseSync3(dbPath2, { readOnly: true });
|
|
31237
|
+
db.exec("PRAGMA busy_timeout = 2000");
|
|
31238
|
+
version2 = db.prepare("PRAGMA user_version").get().user_version;
|
|
31239
|
+
try {
|
|
31240
|
+
const packRows = db.prepare(
|
|
31241
|
+
`SELECT namespace, pack_id, version, enabled, updated_at FROM installed_packs ORDER BY namespace, pack_id`
|
|
31242
|
+
).all();
|
|
31243
|
+
packs2 = packRows.map((r) => ({
|
|
31244
|
+
packId: `${r.namespace}/${r.pack_id}`,
|
|
31245
|
+
version: r.version,
|
|
31246
|
+
enabled: r.enabled !== 0,
|
|
31247
|
+
updatedAt: r.updated_at == null ? null : String(r.updated_at)
|
|
31248
|
+
}));
|
|
31249
|
+
} catch (err) {
|
|
31250
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
31251
|
+
}
|
|
31252
|
+
try {
|
|
31253
|
+
const policyRows = db.prepare(`SELECT action, enabled FROM policies`).all();
|
|
31254
|
+
const byAction = emptyActionCounts();
|
|
31255
|
+
let disabled = 0;
|
|
31256
|
+
for (const row of policyRows) {
|
|
31257
|
+
if (row.enabled === 0) disabled += 1;
|
|
31258
|
+
if (isActionTaken(row.action)) byAction[row.action] += 1;
|
|
31259
|
+
}
|
|
31260
|
+
policyCounts = { total: policyRows.length, disabled, byAction };
|
|
31261
|
+
} catch (err) {
|
|
31262
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
31263
|
+
}
|
|
31264
|
+
try {
|
|
31265
|
+
const agg = db.prepare(
|
|
31266
|
+
`SELECT count(*) AS n, min(f.first_detected_at) AS firstAt, max(f.first_detected_at) AS lastAt
|
|
31267
|
+
FROM inspection_findings f JOIN audit_events e ON e.id = f.audit_event_id
|
|
31268
|
+
WHERE e.event_type IN ${CAPTURE_EVENT_TYPES_SQL2}`
|
|
31269
|
+
).get();
|
|
31270
|
+
findingsTotal = agg.n;
|
|
31271
|
+
findingsFirstAt = agg.firstAt;
|
|
31272
|
+
findingsLastAt = agg.lastAt;
|
|
31273
|
+
} catch (err) {
|
|
31274
|
+
if (!isSchemaAbsent(err)) throw err;
|
|
31275
|
+
}
|
|
31276
|
+
return currentReadout();
|
|
31277
|
+
} catch {
|
|
31278
|
+
return emptyReadout(true);
|
|
31279
|
+
} finally {
|
|
31280
|
+
try {
|
|
31281
|
+
db?.close();
|
|
31282
|
+
} catch {
|
|
31283
|
+
}
|
|
31284
|
+
}
|
|
31285
|
+
}
|
|
31286
|
+
|
|
31287
|
+
// ../../packages/plugin-runtime/src/attached/posture-store.ts
|
|
31288
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
31289
|
+
import { readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
31290
|
+
import { join as join20 } from "path";
|
|
31291
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
31292
|
+
function createPostureStore(dir = settingsDir(), legacyDir) {
|
|
31293
|
+
const file2 = join20(dir, "posture-state.json");
|
|
31294
|
+
const legacyFile = legacyDir === void 0 ? null : join20(legacyDir, "posture-state.json");
|
|
31295
|
+
async function persist(state) {
|
|
31296
|
+
await ensureDataDir(dir);
|
|
31297
|
+
const tmp = `${file2}.${randomUUID17()}.tmp`;
|
|
31298
|
+
try {
|
|
31299
|
+
await writeFile3(tmp, JSON.stringify(state), { encoding: "utf8", mode: DATA_FILE_MODE });
|
|
31300
|
+
await publishByRename(tmp, file2);
|
|
31301
|
+
} catch (err) {
|
|
31302
|
+
await rm2(tmp, { force: true }).catch(() => void 0);
|
|
31303
|
+
throw err;
|
|
31304
|
+
}
|
|
31305
|
+
}
|
|
31306
|
+
async function readFrom(path) {
|
|
31307
|
+
let raw;
|
|
31308
|
+
try {
|
|
31309
|
+
raw = await readFile3(path, "utf8");
|
|
31310
|
+
} catch (err) {
|
|
31311
|
+
const code = err.code;
|
|
31312
|
+
if (code === "ENOENT" || code === "ENOTDIR") return null;
|
|
31313
|
+
throw err;
|
|
31314
|
+
}
|
|
31315
|
+
try {
|
|
31316
|
+
const parsed2 = JSON.parse(raw);
|
|
31317
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
31318
|
+
const record2 = parsed2;
|
|
31319
|
+
if (typeof record2.deviceId === "string" && UUID_RE.test(record2.deviceId)) {
|
|
31320
|
+
const stamp = typeof record2.lastAttemptedAtMs === "number" ? record2.lastAttemptedAtMs : typeof record2.lastReportedAtMs === "number" ? record2.lastReportedAtMs : 0;
|
|
31321
|
+
return { deviceId: record2.deviceId, lastAttemptedAtMs: stamp };
|
|
31322
|
+
}
|
|
31323
|
+
}
|
|
31324
|
+
} catch {
|
|
31325
|
+
}
|
|
31326
|
+
return null;
|
|
31327
|
+
}
|
|
31328
|
+
async function read() {
|
|
31329
|
+
const current = await readFrom(file2);
|
|
31330
|
+
if (current) return current;
|
|
31331
|
+
const legacy = legacyFile === null || legacyFile === file2 ? null : await readFrom(legacyFile).catch(() => null);
|
|
31332
|
+
if (legacy) {
|
|
31333
|
+
try {
|
|
31334
|
+
await persist(legacy);
|
|
31335
|
+
} catch {
|
|
31336
|
+
}
|
|
31337
|
+
return legacy;
|
|
31338
|
+
}
|
|
31339
|
+
const fresh = { deviceId: randomUUID17(), lastAttemptedAtMs: 0 };
|
|
31340
|
+
try {
|
|
31341
|
+
await ensureDataDir(dir);
|
|
31342
|
+
if (createOwnerOnlyFileSync(file2, JSON.stringify(fresh))) return fresh;
|
|
31343
|
+
} catch {
|
|
31344
|
+
return null;
|
|
31345
|
+
}
|
|
31346
|
+
const winner = await readFrom(file2).catch(() => null);
|
|
31347
|
+
if (winner) return winner;
|
|
31348
|
+
try {
|
|
31349
|
+
await persist(fresh);
|
|
31350
|
+
} catch {
|
|
31351
|
+
return null;
|
|
31352
|
+
}
|
|
31353
|
+
return fresh;
|
|
31354
|
+
}
|
|
31355
|
+
async function markAttempted(deviceId, atMs) {
|
|
31356
|
+
await persist({ deviceId, lastAttemptedAtMs: atMs });
|
|
31357
|
+
}
|
|
31358
|
+
return { read, markAttempted, file: file2 };
|
|
31359
|
+
}
|
|
31360
|
+
|
|
31361
|
+
// ../../packages/plugin-runtime/src/attached/sync-state.ts
|
|
31362
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
31363
|
+
import { join as join21 } from "path";
|
|
31364
|
+
|
|
31365
|
+
// ../../packages/plugin-runtime/src/attached/status.ts
|
|
31366
|
+
var REFUSAL_LINES = {
|
|
31367
|
+
unauthorized: "KEY REJECTED \u2014 re-attach with a valid plugin key",
|
|
31368
|
+
forbidden: "ACCESS REFUSED \u2014 key is valid but not permitted; ask your org admin"
|
|
31369
|
+
};
|
|
31370
|
+
var OUTCOME_LINES = {
|
|
31371
|
+
ok: "policy synced",
|
|
31372
|
+
"not-modified": "policy up to date",
|
|
31373
|
+
unauthorized: REFUSAL_LINES.unauthorized,
|
|
31374
|
+
forbidden: REFUSAL_LINES.forbidden,
|
|
31375
|
+
unreachable: "control plane unreachable at last attempt",
|
|
31376
|
+
"invalid-bundle": "control plane sent a policy bundle this build cannot read"
|
|
31377
|
+
};
|
|
31378
|
+
|
|
31379
|
+
// ../../packages/plugin-runtime/src/attached/sync-trigger.ts
|
|
31380
|
+
import { spawn } from "child_process";
|
|
31381
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
31382
|
+
var SYNC_MARKER_NAME = "sync-last-attempt";
|
|
31383
|
+
var SYNC_SCRIPT_NAME = "sync.js";
|
|
31384
|
+
var SYNC_THROTTLE_MS = 15 * 60 * 1e3;
|
|
31385
|
+
function triggerPolicySync(config2, deps = {}) {
|
|
31386
|
+
try {
|
|
31387
|
+
if (!isAttached(config2.settings)) return;
|
|
31388
|
+
const connection = config2.settings.controlPlane;
|
|
31389
|
+
if (connection === void 0) return;
|
|
31390
|
+
if (!readControlPlaneCredentialState(config2.settingsDir, connection).usable) return;
|
|
31391
|
+
const isThrottled = deps.isThrottled ?? ((dir) => throttled(dir, SYNC_MARKER_NAME, SYNC_THROTTLE_MS));
|
|
31392
|
+
if (isThrottled(config2.dataDir)) return;
|
|
31393
|
+
const scriptPath = fileURLToPath2(deps.scriptUrl ?? new URL(SYNC_SCRIPT_NAME, import.meta.url));
|
|
31394
|
+
(deps.spawnChild ?? spawnDetached)(scriptPath);
|
|
31395
|
+
} catch {
|
|
31396
|
+
}
|
|
31397
|
+
}
|
|
31398
|
+
function spawnDetached(scriptPath) {
|
|
31399
|
+
const child = spawn(process.execPath, [scriptPath], { detached: true, stdio: "ignore" });
|
|
31400
|
+
child.on("error", () => {
|
|
31401
|
+
});
|
|
31402
|
+
child.unref();
|
|
31403
|
+
}
|
|
31404
|
+
|
|
31405
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
31406
|
+
import { hostname as hostname5 } from "os";
|
|
31407
|
+
|
|
31408
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
31409
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
31410
|
+
|
|
31411
|
+
// ../../packages/plugin-runtime/src/recorder.ts
|
|
31412
|
+
var PLUGIN_RECORDER_BINARY = "plugin";
|
|
31413
|
+
function pluginRecordedBy(version2) {
|
|
31414
|
+
return `${PLUGIN_RECORDER_BINARY}@${version2}`;
|
|
31415
|
+
}
|
|
31416
|
+
|
|
31417
|
+
// ../../packages/plugin-runtime/src/standalone-gateway.ts
|
|
31418
|
+
var StandaloneDataGateway = class {
|
|
31419
|
+
db;
|
|
31420
|
+
// Kept for the fingerprint key lookup (exception.key lives beside the store).
|
|
31421
|
+
dataDir;
|
|
31422
|
+
// One notice per gateway — see warnRulesetDiscarded.
|
|
31423
|
+
warnedRulesetDiscarded = false;
|
|
31424
|
+
constructor(dataDir2, detections = [], meta3) {
|
|
31425
|
+
this.db = openLocalDatabase(dataDir2);
|
|
31426
|
+
this.dataDir = dataDir2;
|
|
29882
31427
|
this.db.installedPacks.recordInventory(detections, meta3);
|
|
29883
31428
|
}
|
|
29884
31429
|
recordCapture(record2) {
|
|
@@ -29905,12 +31450,12 @@ var StandaloneDataGateway = class {
|
|
|
29905
31450
|
// reconciler drops the whole pass and recovers it idempotently on the next read.
|
|
29906
31451
|
recordLlmCalls(inputs) {
|
|
29907
31452
|
if (inputs.length === 0) return Promise.resolve();
|
|
29908
|
-
return new Promise((
|
|
31453
|
+
return new Promise((resolve2, reject) => {
|
|
29909
31454
|
try {
|
|
29910
31455
|
this.db.auditEvents.runInTransaction(() => {
|
|
29911
31456
|
for (const input of inputs) this.db.auditEvents.insertLlmCall(input);
|
|
29912
31457
|
});
|
|
29913
|
-
|
|
31458
|
+
resolve2();
|
|
29914
31459
|
} catch (err) {
|
|
29915
31460
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
29916
31461
|
}
|
|
@@ -29922,12 +31467,12 @@ var StandaloneDataGateway = class {
|
|
|
29922
31467
|
// drops the whole pass and recovers it idempotently next time.
|
|
29923
31468
|
recordToolCalls(inputs) {
|
|
29924
31469
|
if (inputs.length === 0) return Promise.resolve();
|
|
29925
|
-
return new Promise((
|
|
31470
|
+
return new Promise((resolve2, reject) => {
|
|
29926
31471
|
try {
|
|
29927
31472
|
this.db.auditEvents.runInTransaction(() => {
|
|
29928
31473
|
for (const input of inputs) this.writeToolCall(input);
|
|
29929
31474
|
});
|
|
29930
|
-
|
|
31475
|
+
resolve2();
|
|
29931
31476
|
} catch (err) {
|
|
29932
31477
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
29933
31478
|
}
|
|
@@ -30069,7 +31614,7 @@ var StandaloneDataGateway = class {
|
|
|
30069
31614
|
const customKeywords = [...new Set(policies.flatMap((p) => p.customKeywords ?? []))];
|
|
30070
31615
|
const installed = this.installedScanRules();
|
|
30071
31616
|
const rulePolicies = installed ? [...installed.ruleActions].map(([ruleId, action]) => ({
|
|
30072
|
-
id:
|
|
31617
|
+
id: randomUUID18(),
|
|
30073
31618
|
scope: "global",
|
|
30074
31619
|
target: { ruleId },
|
|
30075
31620
|
action,
|
|
@@ -30223,15 +31768,61 @@ var StandaloneDataGateway = class {
|
|
|
30223
31768
|
}
|
|
30224
31769
|
};
|
|
30225
31770
|
|
|
31771
|
+
// ../../packages/plugin-runtime/src/attached/factory.ts
|
|
31772
|
+
function resolveGatewayForConfig(config2, meta3) {
|
|
31773
|
+
const local = new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
|
|
31774
|
+
try {
|
|
31775
|
+
if (!isAttached(config2.settings)) return local;
|
|
31776
|
+
const connection = config2.settings.controlPlane;
|
|
31777
|
+
if (connection === void 0) return local;
|
|
31778
|
+
const state = readControlPlaneCredentialState(config2.settingsDir, connection);
|
|
31779
|
+
if (!state.usable) return local;
|
|
31780
|
+
const client = createRemoteClient({
|
|
31781
|
+
endpoint: connection.endpoint,
|
|
31782
|
+
apiKey: state.credential.apiKey
|
|
31783
|
+
});
|
|
31784
|
+
const store = createPolicyStore(config2.dataDir);
|
|
31785
|
+
const postureStore = createPostureStore(config2.settingsDir, config2.dataDir);
|
|
31786
|
+
const forward = createForwardPolicy({ dir: config2.dataDir });
|
|
31787
|
+
return new AttachedDataGateway({
|
|
31788
|
+
local,
|
|
31789
|
+
client,
|
|
31790
|
+
dataDir: config2.dataDir,
|
|
31791
|
+
readCachedBundle: () => store.read().then((cached2) => cached2?.bundle ?? null),
|
|
31792
|
+
forward,
|
|
31793
|
+
posture: createPostureReporter({
|
|
31794
|
+
// THROUGH THE BREAKER, and wrapped HERE rather than around
|
|
31795
|
+
// `PostureReporter.send`. The reporter swallows every error by
|
|
31796
|
+
// contract, so a wrap outside it would hand `forward.run` a resolved
|
|
31797
|
+
// promise for a send that failed — recording a SUCCESS, clearing
|
|
31798
|
+
// `consecutiveFailures` and `lastFailure`, and telling `aka status` the
|
|
31799
|
+
// forward recovered when nothing did. Wrapping the raw client call puts
|
|
31800
|
+
// the breaker above the swallow, where it can see the truth.
|
|
31801
|
+
//
|
|
31802
|
+
// What it buys: once the breaker is open — the plane already confirmed
|
|
31803
|
+
// down by the gateway's own writes — this stops paying a request
|
|
31804
|
+
// timeout per throttle interval to re-learn it.
|
|
31805
|
+
report: (snapshot) => forward.run(() => client.reportStorePosture(snapshot)).then(() => void 0),
|
|
31806
|
+
store: postureStore,
|
|
31807
|
+
readStore: () => readStorePosture(config2.dbPath),
|
|
31808
|
+
hostname: () => hostname5(),
|
|
31809
|
+
now: () => Date.now()
|
|
31810
|
+
})
|
|
31811
|
+
});
|
|
31812
|
+
} catch {
|
|
31813
|
+
return local;
|
|
31814
|
+
}
|
|
31815
|
+
}
|
|
31816
|
+
|
|
30226
31817
|
// ../../packages/plugin-runtime/src/resolve.ts
|
|
30227
|
-
var
|
|
30228
|
-
var defaultGatewayFactory =
|
|
31818
|
+
var configuredGatewayFactory = (config2, meta3) => resolveGatewayForConfig(config2, meta3);
|
|
31819
|
+
var defaultGatewayFactory = configuredGatewayFactory;
|
|
30229
31820
|
function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
|
|
30230
31821
|
return gatewayFactory(config2, meta3);
|
|
30231
31822
|
}
|
|
30232
31823
|
|
|
30233
31824
|
// ../../packages/plugin-runtime/src/handle-session-start.ts
|
|
30234
|
-
import { randomUUID as
|
|
31825
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
30235
31826
|
var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
30236
31827
|
async function handleSessionStart(input, config2 = loadConfig()) {
|
|
30237
31828
|
const silent = { staleBinaryNotice: null };
|
|
@@ -30295,6 +31886,7 @@ async function handleSessionStart(input, config2 = loadConfig()) {
|
|
|
30295
31886
|
} catch {
|
|
30296
31887
|
}
|
|
30297
31888
|
}
|
|
31889
|
+
triggerPolicySync(config2);
|
|
30298
31890
|
if (input.harnessVersion !== void 0 && offersMaintenance(gateway, "staleBinaryNotice")) {
|
|
30299
31891
|
return { staleBinaryNotice: gateway.staleBinaryNotice(input.harnessVersion) };
|
|
30300
31892
|
}
|
|
@@ -30320,7 +31912,7 @@ async function recordConfigInventory(gateway, sessionId, cwd, homeDir) {
|
|
|
30320
31912
|
}
|
|
30321
31913
|
function buildConfigScanEvent(sessionId, scan2) {
|
|
30322
31914
|
return {
|
|
30323
|
-
id:
|
|
31915
|
+
id: randomUUID19(),
|
|
30324
31916
|
eventType: "config_scan",
|
|
30325
31917
|
startedAt: scan2.scannedAt,
|
|
30326
31918
|
parentId: sessionId,
|
|
@@ -30369,9 +31961,9 @@ function buildSessionRoot(sessionId, input, ctx, resolved, provider, branch) {
|
|
|
30369
31961
|
}
|
|
30370
31962
|
|
|
30371
31963
|
// src/history/reconcile-trigger.ts
|
|
30372
|
-
import { spawn } from "child_process";
|
|
30373
|
-
import { dirname as
|
|
30374
|
-
import { fileURLToPath as
|
|
31964
|
+
import { spawn as spawn2 } from "child_process";
|
|
31965
|
+
import { dirname as dirname5, join as join23 } from "path";
|
|
31966
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
30375
31967
|
|
|
30376
31968
|
// src/history/tail.ts
|
|
30377
31969
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -30380,11 +31972,11 @@ import {
|
|
|
30380
31972
|
fstatSync,
|
|
30381
31973
|
mkdirSync as mkdirSync4,
|
|
30382
31974
|
openSync as openSync2,
|
|
30383
|
-
readFileSync as
|
|
31975
|
+
readFileSync as readFileSync14,
|
|
30384
31976
|
readSync,
|
|
30385
31977
|
writeFileSync as writeFileSync7
|
|
30386
31978
|
} from "fs";
|
|
30387
|
-
import { join as
|
|
31979
|
+
import { join as join22 } from "path";
|
|
30388
31980
|
var SAFE_SESSION_ID = /^[A-Za-z0-9._-]+$/;
|
|
30389
31981
|
function safeSessionId(sessionId) {
|
|
30390
31982
|
if (SAFE_SESSION_ID.test(sessionId) && sessionId !== "." && sessionId !== "..") {
|
|
@@ -30400,8 +31992,8 @@ function triggerReconcile(dataDir2, sessionId, transcriptPath) {
|
|
|
30400
31992
|
try {
|
|
30401
31993
|
const marker = `${RECONCILE_MARKER_PREFIX}-${safeSessionId(sessionId)}`;
|
|
30402
31994
|
if (throttled(dataDir2, marker, RECONCILE_THROTTLE_MS)) return;
|
|
30403
|
-
const here =
|
|
30404
|
-
const child =
|
|
31995
|
+
const here = dirname5(fileURLToPath3(import.meta.url));
|
|
31996
|
+
const child = spawn2(process.execPath, [join23(here, "reconcile.js"), sessionId, transcriptPath], {
|
|
30405
31997
|
detached: true,
|
|
30406
31998
|
stdio: "ignore"
|
|
30407
31999
|
});
|
|
@@ -30412,17 +32004,17 @@ function triggerReconcile(dataDir2, sessionId, transcriptPath) {
|
|
|
30412
32004
|
|
|
30413
32005
|
// src/protocol/marker.ts
|
|
30414
32006
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
30415
|
-
import { mkdirSync as mkdirSync5, readFileSync as
|
|
30416
|
-
import { join as
|
|
32007
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync as renameSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
32008
|
+
import { join as join24 } from "path";
|
|
30417
32009
|
var MARKER_FILE = "protocol-marker";
|
|
30418
32010
|
function mintMarker() {
|
|
30419
32011
|
return randomBytes4(8).toString("hex");
|
|
30420
32012
|
}
|
|
30421
32013
|
function sessionProtocolMarker(dataDir2, sessionId) {
|
|
30422
32014
|
if (!sessionId) return mintMarker();
|
|
30423
|
-
const path =
|
|
32015
|
+
const path = join24(dataDir2, MARKER_FILE);
|
|
30424
32016
|
try {
|
|
30425
|
-
const stored = JSON.parse(
|
|
32017
|
+
const stored = JSON.parse(readFileSync15(path, "utf8"));
|
|
30426
32018
|
if (stored.sessionId === sessionId && typeof stored.marker === "string" && /^[0-9a-f]{16}$/.test(stored.marker)) {
|
|
30427
32019
|
return stored.marker;
|
|
30428
32020
|
}
|
|
@@ -30431,7 +32023,7 @@ function sessionProtocolMarker(dataDir2, sessionId) {
|
|
|
30431
32023
|
const marker = mintMarker();
|
|
30432
32024
|
try {
|
|
30433
32025
|
mkdirSync5(dataDir2, { recursive: true, mode: DATA_DIR_MODE });
|
|
30434
|
-
const tmp =
|
|
32026
|
+
const tmp = join24(dataDir2, `${MARKER_FILE}.tmp`);
|
|
30435
32027
|
writeFileSync8(tmp, JSON.stringify({ sessionId, marker }), { mode: DATA_FILE_MODE });
|
|
30436
32028
|
renameSync5(tmp, path);
|
|
30437
32029
|
} catch {
|
|
@@ -30447,7 +32039,7 @@ function standingBrief(opts) {
|
|
|
30447
32039
|
|
|
30448
32040
|
// src/hooks/shared.ts
|
|
30449
32041
|
async function readStdin() {
|
|
30450
|
-
return new Promise((
|
|
32042
|
+
return new Promise((resolve2) => {
|
|
30451
32043
|
let data = "";
|
|
30452
32044
|
let settled = false;
|
|
30453
32045
|
const finish = () => {
|
|
@@ -30456,7 +32048,7 @@ async function readStdin() {
|
|
|
30456
32048
|
clearTimeout(timer);
|
|
30457
32049
|
process.stdin.removeListener("data", onData);
|
|
30458
32050
|
process.stdin.removeListener("end", finish);
|
|
30459
|
-
|
|
32051
|
+
resolve2(data);
|
|
30460
32052
|
};
|
|
30461
32053
|
const onData = (chunk) => {
|
|
30462
32054
|
data += chunk;
|
|
@@ -30470,8 +32062,8 @@ async function readStdin() {
|
|
|
30470
32062
|
}
|
|
30471
32063
|
function parseJson(raw) {
|
|
30472
32064
|
try {
|
|
30473
|
-
const
|
|
30474
|
-
return typeof
|
|
32065
|
+
const parsed2 = JSON.parse(raw);
|
|
32066
|
+
return typeof parsed2 === "object" && parsed2 !== null ? parsed2 : null;
|
|
30475
32067
|
} catch {
|
|
30476
32068
|
return null;
|
|
30477
32069
|
}
|
|
@@ -30481,24 +32073,83 @@ function getString(record2, key) {
|
|
|
30481
32073
|
return typeof value === "string" ? value : void 0;
|
|
30482
32074
|
}
|
|
30483
32075
|
function emit(output) {
|
|
30484
|
-
return new Promise((
|
|
32076
|
+
return new Promise((resolve2) => {
|
|
30485
32077
|
let settled = false;
|
|
30486
32078
|
const finish = () => {
|
|
30487
32079
|
if (settled) return;
|
|
30488
32080
|
settled = true;
|
|
30489
|
-
|
|
32081
|
+
resolve2();
|
|
30490
32082
|
};
|
|
30491
32083
|
process.stdout.on("error", finish);
|
|
30492
32084
|
process.stdout.write(JSON.stringify(output), finish);
|
|
30493
32085
|
});
|
|
30494
32086
|
}
|
|
30495
32087
|
|
|
32088
|
+
// src/hooks/store-health.ts
|
|
32089
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
|
|
32090
|
+
import { dirname as dirname6, join as join25 } from "path";
|
|
32091
|
+
var STORE_REDIRECT_MARKER = "store-redirect-last-session";
|
|
32092
|
+
function markerDirs(dataDir2) {
|
|
32093
|
+
return [dataDir2, dirname6(dataDir2)];
|
|
32094
|
+
}
|
|
32095
|
+
function alreadyClaimed(dirs, marker, sessionId) {
|
|
32096
|
+
return dirs.some((dir) => {
|
|
32097
|
+
try {
|
|
32098
|
+
return readFileSync16(join25(dir, marker), "utf8") === sessionId;
|
|
32099
|
+
} catch {
|
|
32100
|
+
return false;
|
|
32101
|
+
}
|
|
32102
|
+
});
|
|
32103
|
+
}
|
|
32104
|
+
function recordClaim(dirs, marker, sessionId) {
|
|
32105
|
+
for (const dir of dirs) {
|
|
32106
|
+
try {
|
|
32107
|
+
mkdirSync6(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
32108
|
+
writeFileSync9(join25(dir, marker), sessionId, { mode: DATA_FILE_MODE });
|
|
32109
|
+
return;
|
|
32110
|
+
} catch {
|
|
32111
|
+
}
|
|
32112
|
+
}
|
|
32113
|
+
}
|
|
32114
|
+
function storeRedirectedMessage(paths, platform2 = process.platform) {
|
|
32115
|
+
const where = paths.map(({ path, target, holds, missing, mode }) => {
|
|
32116
|
+
if (missing) return `${path} -> ${target} (which does not exist; ${holds} cannot land there)`;
|
|
32117
|
+
const loose = mode !== void 0 && (mode & 63) !== 0 ? ", NOT owner-only" : "";
|
|
32118
|
+
const inherited = mode === void 0 ? "" : ` (${formatMode(mode)}${loose})`;
|
|
32119
|
+
return `${path} -> ${target}${inherited}, holding ${holds}`;
|
|
32120
|
+
}).join("; ");
|
|
32121
|
+
const subject = paths.length === 1 ? "a store path is a symlink" : `${String(paths.length)} store paths are symlinks`;
|
|
32122
|
+
const anyResolves = paths.some(({ missing }) => !missing);
|
|
32123
|
+
const lead = anyResolves ? `${subject}, so AKA is writing into the target instead: ${where}. ` : `${subject} resolving nowhere, so AKA cannot write there: ${where}. `;
|
|
32124
|
+
const kept = anyResolves && platform2 !== "win32" ? "Permissions are never changed through a symlink, so the store keeps whatever the target already had. " : "";
|
|
32125
|
+
return `[aka] ${lead}${kept}If you did not create that link, treat it as untrusted and run \`aka init\` for the full report.
|
|
32126
|
+
`;
|
|
32127
|
+
}
|
|
32128
|
+
function formatMode(mode) {
|
|
32129
|
+
return `0${mode.toString(8).padStart(3, "0")}`;
|
|
32130
|
+
}
|
|
32131
|
+
function warnIfStoreRedirected(config2, sessionId, write = (message2) => void process.stderr.write(message2)) {
|
|
32132
|
+
try {
|
|
32133
|
+
const paths = symlinkedStorePaths(dirname6(config2.dataDir));
|
|
32134
|
+
if (paths.length === 0) return;
|
|
32135
|
+
if (!sessionId) {
|
|
32136
|
+
write(storeRedirectedMessage(paths));
|
|
32137
|
+
return;
|
|
32138
|
+
}
|
|
32139
|
+
const dirs = markerDirs(config2.dataDir);
|
|
32140
|
+
if (alreadyClaimed(dirs, STORE_REDIRECT_MARKER, sessionId)) return;
|
|
32141
|
+
write(storeRedirectedMessage(paths));
|
|
32142
|
+
recordClaim(dirs, STORE_REDIRECT_MARKER, sessionId);
|
|
32143
|
+
} catch {
|
|
32144
|
+
}
|
|
32145
|
+
}
|
|
32146
|
+
|
|
30496
32147
|
// src/hooks/session-start.ts
|
|
30497
32148
|
function harnessVersion() {
|
|
30498
32149
|
const manifestPath = process.argv[2];
|
|
30499
32150
|
if (!manifestPath) return void 0;
|
|
30500
32151
|
try {
|
|
30501
|
-
const manifest = JSON.parse(
|
|
32152
|
+
const manifest = JSON.parse(readFileSync17(manifestPath, "utf8"));
|
|
30502
32153
|
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
30503
32154
|
} catch {
|
|
30504
32155
|
return void 0;
|
|
@@ -30524,6 +32175,7 @@ async function main() {
|
|
|
30524
32175
|
}
|
|
30525
32176
|
const transcriptPath = input ? getString(input, "transcript_path") : void 0;
|
|
30526
32177
|
const config2 = loadConfig();
|
|
32178
|
+
warnIfStoreRedirected(config2, sessionId);
|
|
30527
32179
|
if (sessionId !== void 0 && transcriptPath !== void 0) {
|
|
30528
32180
|
triggerReconcile(config2.dataDir, sessionId, transcriptPath);
|
|
30529
32181
|
}
|