@akasecurity/ai-tc-claude-code 0.9.4 → 0.9.5
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 +4 -0
- package/package.json +4 -3
- package/scripts/apply-suppressions.js +1607 -534
- package/scripts/backfill.js +1969 -390
- package/scripts/filescan.js +1863 -346
- package/scripts/firstrun.js +1307 -223
- package/scripts/intro.js +370 -54
- package/scripts/message-display.js +1406 -297
- package/scripts/onboard.js +1532 -245
- package/scripts/post-tool-use.js +1885 -356
- package/scripts/pre-tool-use.js +1900 -365
- package/scripts/query.js +1306 -222
- package/scripts/reconcile.js +1435 -326
- package/scripts/remediate.js +2121 -546
- package/scripts/scan-worker.js +18006 -0
- package/scripts/session-start.js +1350 -312
- package/scripts/start-light.js +390 -74
- package/scripts/statusline.js +1306 -222
- package/scripts/stop.js +352 -79
- package/scripts/user-prompt-submit.js +1897 -368
|
@@ -492,15 +492,14 @@ var require_ignore = __commonJS({
|
|
|
492
492
|
});
|
|
493
493
|
|
|
494
494
|
// src/apply-suppressions.ts
|
|
495
|
-
import { existsSync as
|
|
495
|
+
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
|
|
496
496
|
import { userInfo } from "os";
|
|
497
|
-
import { dirname as
|
|
498
|
-
import { fileURLToPath as
|
|
497
|
+
import { dirname as dirname6, join as join17 } from "path";
|
|
498
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
499
499
|
|
|
500
500
|
// ../../packages/persistence/src/database.ts
|
|
501
|
-
import { randomUUID as
|
|
502
|
-
import {
|
|
503
|
-
import { join, sep } from "path";
|
|
501
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
502
|
+
import { join as join2, sep } from "path";
|
|
504
503
|
import { DatabaseSync } from "node:sqlite";
|
|
505
504
|
|
|
506
505
|
// ../../packages/schema/src/drizzle/sqlite-ddl.ts
|
|
@@ -580,6 +579,14 @@ var SQLITE_MIGRATIONS = [
|
|
|
580
579
|
{
|
|
581
580
|
tag: "0018_serious_tana_nile",
|
|
582
581
|
sql: "ALTER TABLE `secret_vault` ADD `format_version` integer DEFAULT 2 NOT NULL;"
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
tag: "0019_audit_started_at_index",
|
|
585
|
+
sql: "CREATE INDEX `idx_audit_started_at` ON `audit_events` (`started_at`);"
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
tag: "0020_secret_vault_pagination_indexes",
|
|
589
|
+
sql: "CREATE INDEX `idx_secret_vault_last_seen` ON `secret_vault` (`last_seen`,`pointer_id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_reuse` ON `secret_vault` (`occurrence_count`,`pointer_id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_deref_at` ON `secret_vault_deref` (`at`,`id`);--> statement-breakpoint\nCREATE INDEX `idx_secret_vault_deref_signal` ON `secret_vault_deref` (`at`,`id`) WHERE `reason` NOT IN ('display', 'view-render');"
|
|
583
590
|
}
|
|
584
591
|
];
|
|
585
592
|
|
|
@@ -15317,7 +15324,17 @@ var Finding = external_exports.object({
|
|
|
15317
15324
|
}).meta({ id: "Finding" });
|
|
15318
15325
|
var DetectedFinding = Finding.meta({ id: "DetectedFinding" });
|
|
15319
15326
|
var FindingAction = external_exports.enum(["blocked", "redacted", "warned", "allowed", "quarantined", "monitored"]).meta({ id: "FindingAction" });
|
|
15320
|
-
var FindingProvider = external_exports.enum([
|
|
15327
|
+
var FindingProvider = external_exports.enum([
|
|
15328
|
+
"claudecode",
|
|
15329
|
+
"claudedesktop",
|
|
15330
|
+
"cursor",
|
|
15331
|
+
"copilot",
|
|
15332
|
+
"chatgpt",
|
|
15333
|
+
"claudeai",
|
|
15334
|
+
"codex",
|
|
15335
|
+
"antigravity",
|
|
15336
|
+
"api"
|
|
15337
|
+
]).meta({ id: "FindingProvider" });
|
|
15321
15338
|
var FindingCategory = external_exports.enum([
|
|
15322
15339
|
"secret",
|
|
15323
15340
|
"pii",
|
|
@@ -15371,7 +15388,16 @@ var FindingInstance = external_exports.object({
|
|
|
15371
15388
|
confidence: external_exports.number().min(0).max(1),
|
|
15372
15389
|
// Lifecycle status (see FindingStatus). Optional so legacy callers/rows
|
|
15373
15390
|
// that predate the resolution feature stay valid.
|
|
15374
|
-
status: FindingStatus.optional()
|
|
15391
|
+
status: FindingStatus.optional(),
|
|
15392
|
+
// The audit event this finding was captured from. Optional so callers that
|
|
15393
|
+
// do not project it stay valid. An at-rest finding is content-addressed by
|
|
15394
|
+
// finding_key and its row is upserted on re-detection, so this names the
|
|
15395
|
+
// MOST RECENT detection event, not the first.
|
|
15396
|
+
eventId: external_exports.string().optional(),
|
|
15397
|
+
// The session that event belongs to, when it has one — the seam a
|
|
15398
|
+
// per-instance "view session" link needs. Absent for events captured
|
|
15399
|
+
// outside a session.
|
|
15400
|
+
sessionId: external_exports.string().optional()
|
|
15375
15401
|
}).meta({ id: "FindingInstance" });
|
|
15376
15402
|
var FindingGroup = external_exports.object({
|
|
15377
15403
|
id: external_exports.string(),
|
|
@@ -15415,7 +15441,11 @@ var FindingFacets = external_exports.object({
|
|
|
15415
15441
|
// for every instance, so every group lands in a bucket; a status-less
|
|
15416
15442
|
// group (possible only for callers whose rows carry no statuses) is
|
|
15417
15443
|
// counted under no value.
|
|
15418
|
-
status: external_exports.array(FindingFacetItem)
|
|
15444
|
+
status: external_exports.array(FindingFacetItem),
|
|
15445
|
+
// Host tool (attributes.tool_name). Present only on the instance-level
|
|
15446
|
+
// reads, which can filter by it; the grouped read omits the dimension
|
|
15447
|
+
// because a group spans tools.
|
|
15448
|
+
tool: external_exports.array(FindingFacetItem).optional()
|
|
15419
15449
|
}).meta({ id: "FindingFacets" });
|
|
15420
15450
|
var DEFAULT_GROUPED_FINDINGS_LIMIT = 50;
|
|
15421
15451
|
var ListGroupedFindingsQuery = external_exports.object({
|
|
@@ -15433,6 +15463,16 @@ var ListGroupedFindingsQuery = external_exports.object({
|
|
|
15433
15463
|
// Scope to findings whose event carries this session id (the Activity page's
|
|
15434
15464
|
// session → findings drilldown). Findings without a session never match.
|
|
15435
15465
|
sessionId: external_exports.string().optional(),
|
|
15466
|
+
// Inclusive lower bound on the parent event's timestamp, so a caller arriving
|
|
15467
|
+
// from a time-scoped page (Activity's range) can carry that scope. Absent
|
|
15468
|
+
// means all time — this list has no default window.
|
|
15469
|
+
from: external_exports.iso.datetime().optional(),
|
|
15470
|
+
// A group or instance id that must appear in the page even when the cursor
|
|
15471
|
+
// has already advanced past its sort position. This is what keeps the
|
|
15472
|
+
// Findings page's one-shot ?finding= deep link resolving once the list
|
|
15473
|
+
// paginates: the target group is appended out of sort order rather than
|
|
15474
|
+
// scanning forward for it. Never affects totals, facets or the cursor.
|
|
15475
|
+
includeId: external_exports.string().optional(),
|
|
15436
15476
|
groupBy: external_exports.literal("type").optional(),
|
|
15437
15477
|
limit: external_exports.coerce.number().int().min(1).max(100).optional(),
|
|
15438
15478
|
cursor: external_exports.string().optional()
|
|
@@ -15477,15 +15517,110 @@ var FindingInstanceDetail = FindingInstance.extend({
|
|
|
15477
15517
|
detection: FindingDetectionRef,
|
|
15478
15518
|
policy: FindingPolicyRef
|
|
15479
15519
|
}).meta({ id: "FindingInstanceDetail" });
|
|
15520
|
+
var DEFAULT_FLAT_FINDINGS_LIMIT = 50;
|
|
15521
|
+
var ListFindingInstancesQuery = external_exports.object({
|
|
15522
|
+
severity: external_exports.array(Severity).optional(),
|
|
15523
|
+
// Rule ids, the same vocabulary the grouped list's `subtype` carries.
|
|
15524
|
+
subtype: external_exports.array(external_exports.string()).optional(),
|
|
15525
|
+
provider: external_exports.array(FindingProvider).optional(),
|
|
15526
|
+
action: external_exports.array(FindingAction).optional(),
|
|
15527
|
+
// Matches each instance's OWN derived status (deriveFindingStatus), unlike
|
|
15528
|
+
// the grouped query's group-level fold.
|
|
15529
|
+
status: external_exports.array(FindingStatus).optional(),
|
|
15530
|
+
// Exact host-tool names (attributes.tool_name, e.g. 'Bash'). A real filter,
|
|
15531
|
+
// where the free-text `q` can only match the rendered "via Bash" label.
|
|
15532
|
+
tool: external_exports.array(external_exports.string()).optional(),
|
|
15533
|
+
// Exact repository / file-path matches, for the drill-down out of the
|
|
15534
|
+
// locations view. A row whose event carries no repo/file matches neither.
|
|
15535
|
+
repo: external_exports.string().optional(),
|
|
15536
|
+
file: external_exports.string().optional(),
|
|
15537
|
+
q: external_exports.string().optional(),
|
|
15538
|
+
sessionId: external_exports.string().optional(),
|
|
15539
|
+
from: external_exports.iso.datetime().optional(),
|
|
15540
|
+
limit: external_exports.coerce.number().int().min(1).max(200).optional(),
|
|
15541
|
+
cursor: external_exports.string().optional()
|
|
15542
|
+
});
|
|
15543
|
+
var ListFindingInstancesResponse = external_exports.object({
|
|
15544
|
+
// Instances matching the filters across the whole scope, not just this
|
|
15545
|
+
// page — cursor-independent, like the grouped list's totals.
|
|
15546
|
+
totals: external_exports.object({ findings: external_exports.number().int().nonnegative() }),
|
|
15547
|
+
// Counts in INSTANCES here, where the grouped response counts groups. Each
|
|
15548
|
+
// dimension still excludes its own filter.
|
|
15549
|
+
facets: FindingFacets,
|
|
15550
|
+
items: external_exports.array(FindingInstanceDetail),
|
|
15551
|
+
nextCursor: external_exports.string().nullable()
|
|
15552
|
+
}).meta({ id: "ListFindingInstancesResponse" });
|
|
15553
|
+
var FindingLocationFile = external_exports.object({
|
|
15554
|
+
// Empty when the instances carried no file path (a prompt or a tool call
|
|
15555
|
+
// with no file attribution).
|
|
15556
|
+
file: external_exports.string(),
|
|
15557
|
+
instanceCount: external_exports.number().int().nonnegative(),
|
|
15558
|
+
maxSeverity: Severity,
|
|
15559
|
+
latestDetectedAt: external_exports.iso.datetime(),
|
|
15560
|
+
// Folded from the instances' derived statuses with the same
|
|
15561
|
+
// open-dominates precedence a group uses.
|
|
15562
|
+
status: FindingStatus.optional(),
|
|
15563
|
+
// Distinct rules seen at this location, capped — the row shows them as
|
|
15564
|
+
// chips, and the count is what conveys scale.
|
|
15565
|
+
ruleIds: external_exports.array(external_exports.string())
|
|
15566
|
+
}).meta({ id: "FindingLocationFile" });
|
|
15567
|
+
var FindingLocationRepo = external_exports.object({
|
|
15568
|
+
/** Empty when the instances carried no repo attribute. */
|
|
15569
|
+
repo: external_exports.string(),
|
|
15570
|
+
instanceCount: external_exports.number().int().nonnegative(),
|
|
15571
|
+
maxSeverity: Severity,
|
|
15572
|
+
latestDetectedAt: external_exports.iso.datetime(),
|
|
15573
|
+
status: FindingStatus.optional(),
|
|
15574
|
+
files: external_exports.array(FindingLocationFile)
|
|
15575
|
+
}).meta({ id: "FindingLocationRepo" });
|
|
15576
|
+
var ListFindingLocationsQuery = external_exports.object({
|
|
15577
|
+
severity: external_exports.array(Severity).optional(),
|
|
15578
|
+
subtype: external_exports.array(external_exports.string()).optional(),
|
|
15579
|
+
provider: external_exports.array(FindingProvider).optional(),
|
|
15580
|
+
action: external_exports.array(FindingAction).optional(),
|
|
15581
|
+
// Per-instance, as in ListFindingInstancesQuery: a location keeps the
|
|
15582
|
+
// instances that match, and folds its status from those.
|
|
15583
|
+
status: external_exports.array(FindingStatus).optional(),
|
|
15584
|
+
tool: external_exports.array(external_exports.string()).optional(),
|
|
15585
|
+
q: external_exports.string().optional(),
|
|
15586
|
+
sessionId: external_exports.string().optional(),
|
|
15587
|
+
from: external_exports.iso.datetime().optional(),
|
|
15588
|
+
limit: external_exports.coerce.number().int().min(1).max(500).optional()
|
|
15589
|
+
});
|
|
15590
|
+
var ListFindingLocationsResponse = external_exports.object({
|
|
15591
|
+
totals: external_exports.object({
|
|
15592
|
+
findings: external_exports.number().int().nonnegative(),
|
|
15593
|
+
repos: external_exports.number().int().nonnegative(),
|
|
15594
|
+
files: external_exports.number().int().nonnegative()
|
|
15595
|
+
}),
|
|
15596
|
+
/** Sorted by max severity, then most recent. */
|
|
15597
|
+
items: external_exports.array(FindingLocationRepo),
|
|
15598
|
+
/** Whether `limit` truncated the repo list. */
|
|
15599
|
+
hasMore: external_exports.boolean()
|
|
15600
|
+
}).meta({ id: "ListFindingLocationsResponse" });
|
|
15480
15601
|
|
|
15481
15602
|
// ../../packages/schema/src/zod/harness-map.ts
|
|
15482
|
-
var Harness = external_exports.enum([
|
|
15603
|
+
var Harness = external_exports.enum([
|
|
15604
|
+
"claudecode",
|
|
15605
|
+
"cursor",
|
|
15606
|
+
"copilot",
|
|
15607
|
+
"codex",
|
|
15608
|
+
"antigravity",
|
|
15609
|
+
"windsurf",
|
|
15610
|
+
"claudedesktop",
|
|
15611
|
+
"chatgpt",
|
|
15612
|
+
"claudeai",
|
|
15613
|
+
"api"
|
|
15614
|
+
]).meta({ id: "Harness" });
|
|
15483
15615
|
var TOOL_TO_HARNESS = {
|
|
15484
15616
|
"claude-code": "claudecode",
|
|
15485
15617
|
"claude-desktop": "claudedesktop",
|
|
15486
15618
|
"github-copilot": "copilot",
|
|
15487
15619
|
cursor: "cursor",
|
|
15488
|
-
chatgpt: "chatgpt"
|
|
15620
|
+
chatgpt: "chatgpt",
|
|
15621
|
+
codex: "codex",
|
|
15622
|
+
antigravity: "antigravity",
|
|
15623
|
+
"claude-ai": "claudeai"
|
|
15489
15624
|
};
|
|
15490
15625
|
|
|
15491
15626
|
// ../../packages/schema/src/zod/meta.ts
|
|
@@ -15943,7 +16078,18 @@ var ActivityOverviewResponse = external_exports.object({
|
|
|
15943
16078
|
// ../../packages/schema/src/zod/event.ts
|
|
15944
16079
|
var EventKind = external_exports.enum(["prompt", "response", "code_change", "tool_use"]).meta({ id: "EventKind" });
|
|
15945
16080
|
var CAPTURE_EVENT_TYPES_SQL = EventKind.options.map((k) => `'${k}'`).join(",");
|
|
15946
|
-
var SourceTool = external_exports.enum([
|
|
16081
|
+
var SourceTool = external_exports.enum([
|
|
16082
|
+
"claude-code",
|
|
16083
|
+
"claude-desktop",
|
|
16084
|
+
"cursor",
|
|
16085
|
+
"chatgpt",
|
|
16086
|
+
"claude-ai",
|
|
16087
|
+
"github-copilot",
|
|
16088
|
+
"codex",
|
|
16089
|
+
"antigravity",
|
|
16090
|
+
"cli",
|
|
16091
|
+
"unknown"
|
|
16092
|
+
]).meta({ id: "SourceTool" });
|
|
15947
16093
|
var EventMetadata = external_exports.object({
|
|
15948
16094
|
sessionId: external_exports.string().optional(),
|
|
15949
16095
|
repo: external_exports.string().optional(),
|
|
@@ -16014,7 +16160,7 @@ var TrustLevel = external_exports.enum(["known-good", "risky", "unapproved"]).me
|
|
|
16014
16160
|
var Flag = external_exports.enum(["update", "stale", "conflict", "unknown", "change", "untracked", "risk", "findings"]).meta({ id: "Flag" });
|
|
16015
16161
|
var Visibility = external_exports.enum(["public", "private"]).meta({ id: "Visibility" });
|
|
16016
16162
|
var HarnessEventKind = external_exports.enum(["block", "redact", "warn"]).meta({ id: "HarnessEventKind" });
|
|
16017
|
-
var HarnessId = external_exports.enum(["claudecode", "cursor", "codex"]).meta({ id: "HarnessId" });
|
|
16163
|
+
var HarnessId = external_exports.enum(["claudecode", "cursor", "codex", "antigravity"]).meta({ id: "HarnessId" });
|
|
16018
16164
|
var AccessCounts = external_exports.object({
|
|
16019
16165
|
open: external_exports.number().int().nonnegative(),
|
|
16020
16166
|
approved: external_exports.number().int().nonnegative(),
|
|
@@ -16283,6 +16429,7 @@ var ExceptionBundleEntry = DetectionException.pick({
|
|
|
16283
16429
|
useCount: true,
|
|
16284
16430
|
conditions: true
|
|
16285
16431
|
});
|
|
16432
|
+
var ExceptionDescriptor = DetectionException.omit({ valueFingerprint: true });
|
|
16286
16433
|
|
|
16287
16434
|
// ../../packages/schema/src/zod/rule.ts
|
|
16288
16435
|
var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
|
|
@@ -17098,6 +17245,35 @@ var EgressWriteSummary = external_exports.object({
|
|
|
17098
17245
|
droppedFiles: external_exports.array(external_exports.string()).default([])
|
|
17099
17246
|
}).meta({ id: "EgressWriteSummary" });
|
|
17100
17247
|
|
|
17248
|
+
// ../../packages/schema/src/zod/exception-action.ts
|
|
17249
|
+
var confirmation = external_exports.string().optional();
|
|
17250
|
+
var ApproveBlockedInput = external_exports.object({
|
|
17251
|
+
reference: external_exports.string(),
|
|
17252
|
+
scope: external_exports.string(),
|
|
17253
|
+
reason: external_exports.string(),
|
|
17254
|
+
confirmation
|
|
17255
|
+
});
|
|
17256
|
+
var AddExceptionInput = external_exports.object({
|
|
17257
|
+
ruleId: external_exports.string(),
|
|
17258
|
+
value: external_exports.string(),
|
|
17259
|
+
scope: external_exports.string(),
|
|
17260
|
+
reason: external_exports.string(),
|
|
17261
|
+
confirmation
|
|
17262
|
+
});
|
|
17263
|
+
var GrantRevealInput = external_exports.object({
|
|
17264
|
+
pointer: external_exports.string(),
|
|
17265
|
+
scope: external_exports.string(),
|
|
17266
|
+
justification: external_exports.string(),
|
|
17267
|
+
confirmation
|
|
17268
|
+
});
|
|
17269
|
+
var RevokeExceptionInput = external_exports.object({
|
|
17270
|
+
id: external_exports.string(),
|
|
17271
|
+
reason: external_exports.string()
|
|
17272
|
+
});
|
|
17273
|
+
var RotateKeyInput = external_exports.object({
|
|
17274
|
+
confirmation: external_exports.string()
|
|
17275
|
+
});
|
|
17276
|
+
|
|
17101
17277
|
// ../../packages/schema/src/zod/findings-group-build.ts
|
|
17102
17278
|
function toApiAction(dbVal) {
|
|
17103
17279
|
const map2 = {
|
|
@@ -17153,6 +17329,8 @@ function buildFindingGroups(rows, opts = {}) {
|
|
|
17153
17329
|
repo: r.repo,
|
|
17154
17330
|
file: r.file,
|
|
17155
17331
|
...r.toolName === void 0 ? {} : { toolName: r.toolName },
|
|
17332
|
+
...r.eventId === void 0 ? {} : { eventId: r.eventId },
|
|
17333
|
+
...r.sessionId === void 0 ? {} : { sessionId: r.sessionId },
|
|
17156
17334
|
action: toApiAction(effectiveDbAction),
|
|
17157
17335
|
detectedAt: r.occurredAt,
|
|
17158
17336
|
confidence: r.confidence,
|
|
@@ -17284,14 +17462,17 @@ function applyFindingFilters(groups, opts) {
|
|
|
17284
17462
|
}
|
|
17285
17463
|
var SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
17286
17464
|
var SEVERITY_RANK = SEVERITY_ORDER;
|
|
17465
|
+
function compareFindingGroupOrder(a, b) {
|
|
17466
|
+
const rankA = SEVERITY_RANK[a.severity] ?? -1;
|
|
17467
|
+
const rankB = SEVERITY_RANK[b.severity] ?? -1;
|
|
17468
|
+
const severityDiff = rankA - rankB;
|
|
17469
|
+
if (severityDiff !== 0) return severityDiff;
|
|
17470
|
+
const recencyDiff = b.latestDetectedAt.localeCompare(a.latestDetectedAt);
|
|
17471
|
+
if (recencyDiff !== 0) return recencyDiff;
|
|
17472
|
+
return a.id.localeCompare(b.id);
|
|
17473
|
+
}
|
|
17287
17474
|
function sortFindingGroups(groups) {
|
|
17288
|
-
return [...groups].sort(
|
|
17289
|
-
const rankA = SEVERITY_RANK[a.severity] ?? -1;
|
|
17290
|
-
const rankB = SEVERITY_RANK[b.severity] ?? -1;
|
|
17291
|
-
const severityDiff = rankA - rankB;
|
|
17292
|
-
if (severityDiff !== 0) return severityDiff;
|
|
17293
|
-
return b.latestDetectedAt.localeCompare(a.latestDetectedAt);
|
|
17294
|
-
});
|
|
17475
|
+
return [...groups].sort(compareFindingGroupOrder);
|
|
17295
17476
|
}
|
|
17296
17477
|
function computeFindingFacets(allGroups, opts) {
|
|
17297
17478
|
const forSeverity = applyFindingFilters(allGroups, {
|
|
@@ -17347,15 +17528,158 @@ function computeFindingFacets(allGroups, opts) {
|
|
|
17347
17528
|
for (const g of forStatus) {
|
|
17348
17529
|
if (g.status !== void 0) statusMap.set(g.status, (statusMap.get(g.status) ?? 0) + 1);
|
|
17349
17530
|
}
|
|
17350
|
-
const
|
|
17531
|
+
const toItems2 = (m) => [...m.entries()].map(([value, count]) => ({ value, count }));
|
|
17532
|
+
return {
|
|
17533
|
+
severity: toItems2(severityMap),
|
|
17534
|
+
provider: toItems2(providerMap),
|
|
17535
|
+
action: toItems2(actionMap),
|
|
17536
|
+
subtype: toItems2(subtypeMap),
|
|
17537
|
+
status: toItems2(statusMap)
|
|
17538
|
+
};
|
|
17539
|
+
}
|
|
17540
|
+
|
|
17541
|
+
// ../../packages/schema/src/zod/findings-flat-build.ts
|
|
17542
|
+
function rowHaystack(row) {
|
|
17543
|
+
return [
|
|
17544
|
+
row.ruleId,
|
|
17545
|
+
row.category,
|
|
17546
|
+
row.maskedMatch,
|
|
17547
|
+
row.repo,
|
|
17548
|
+
row.file,
|
|
17549
|
+
row.toolName ? `via ${row.toolName}` : "",
|
|
17550
|
+
row.id
|
|
17551
|
+
].join(" ").toLowerCase();
|
|
17552
|
+
}
|
|
17553
|
+
function matchesDimension(row, opts, dimension) {
|
|
17554
|
+
switch (dimension) {
|
|
17555
|
+
case "severity":
|
|
17556
|
+
return !opts.severity?.length || opts.severity.includes(row.severity);
|
|
17557
|
+
case "subtype":
|
|
17558
|
+
return !opts.subtype?.length || opts.subtype.includes(row.ruleId);
|
|
17559
|
+
case "providers":
|
|
17560
|
+
return !opts.providers?.length || opts.providers.includes(toApiProvider(row.sourceTool));
|
|
17561
|
+
case "actions":
|
|
17562
|
+
return !opts.actions?.length || opts.actions.includes(toApiAction(row.actionTaken));
|
|
17563
|
+
case "statuses":
|
|
17564
|
+
return !opts.statuses?.length || row.status !== void 0 && opts.statuses.includes(row.status);
|
|
17565
|
+
case "tools":
|
|
17566
|
+
return !opts.tools?.length || row.toolName !== void 0 && opts.tools.includes(row.toolName);
|
|
17567
|
+
case "repo":
|
|
17568
|
+
return opts.repo === void 0 || opts.repo === "" || row.repo === opts.repo;
|
|
17569
|
+
case "file":
|
|
17570
|
+
return opts.file === void 0 || opts.file === "" || row.file === opts.file;
|
|
17571
|
+
case "q":
|
|
17572
|
+
return !opts.q || rowHaystack(row).includes(opts.q.toLowerCase());
|
|
17573
|
+
}
|
|
17574
|
+
}
|
|
17575
|
+
var DIMENSIONS = [
|
|
17576
|
+
"severity",
|
|
17577
|
+
"subtype",
|
|
17578
|
+
"providers",
|
|
17579
|
+
"actions",
|
|
17580
|
+
"statuses",
|
|
17581
|
+
"tools",
|
|
17582
|
+
"repo",
|
|
17583
|
+
"file",
|
|
17584
|
+
"q"
|
|
17585
|
+
];
|
|
17586
|
+
function matchesInstanceFilters(row, opts, except) {
|
|
17587
|
+
for (const dimension of DIMENSIONS) {
|
|
17588
|
+
if (dimension === except) continue;
|
|
17589
|
+
if (!matchesDimension(row, opts, dimension)) return false;
|
|
17590
|
+
}
|
|
17591
|
+
return true;
|
|
17592
|
+
}
|
|
17593
|
+
function toItems(counts) {
|
|
17594
|
+
return [...counts.entries()].map(([value, count]) => ({ value, count })).sort((a, b) => b.count - a.count || a.value.localeCompare(b.value));
|
|
17595
|
+
}
|
|
17596
|
+
function bump(counts, value) {
|
|
17597
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
17598
|
+
}
|
|
17599
|
+
function createInstanceFacetAccumulator(opts) {
|
|
17600
|
+
const severity = /* @__PURE__ */ new Map();
|
|
17601
|
+
const subtype = /* @__PURE__ */ new Map();
|
|
17602
|
+
const provider = /* @__PURE__ */ new Map();
|
|
17603
|
+
const action = /* @__PURE__ */ new Map();
|
|
17604
|
+
const status = /* @__PURE__ */ new Map();
|
|
17605
|
+
const tool = /* @__PURE__ */ new Map();
|
|
17351
17606
|
return {
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17356
|
-
|
|
17607
|
+
add(row) {
|
|
17608
|
+
if (matchesInstanceFilters(row, opts, "severity")) bump(severity, row.severity);
|
|
17609
|
+
if (matchesInstanceFilters(row, opts, "subtype")) bump(subtype, row.ruleId);
|
|
17610
|
+
if (matchesInstanceFilters(row, opts, "providers")) {
|
|
17611
|
+
bump(provider, toApiProvider(row.sourceTool));
|
|
17612
|
+
}
|
|
17613
|
+
if (matchesInstanceFilters(row, opts, "actions")) bump(action, toApiAction(row.actionTaken));
|
|
17614
|
+
if (row.status !== void 0 && matchesInstanceFilters(row, opts, "statuses")) {
|
|
17615
|
+
bump(status, row.status);
|
|
17616
|
+
}
|
|
17617
|
+
if (row.toolName !== void 0 && matchesInstanceFilters(row, opts, "tools")) {
|
|
17618
|
+
bump(tool, row.toolName);
|
|
17619
|
+
}
|
|
17620
|
+
},
|
|
17621
|
+
facets: () => ({
|
|
17622
|
+
severity: toItems(severity),
|
|
17623
|
+
subtype: toItems(subtype),
|
|
17624
|
+
provider: toItems(provider),
|
|
17625
|
+
action: toItems(action),
|
|
17626
|
+
status: toItems(status),
|
|
17627
|
+
tool: toItems(tool)
|
|
17628
|
+
})
|
|
17629
|
+
};
|
|
17630
|
+
}
|
|
17631
|
+
function toInstanceDetail(row) {
|
|
17632
|
+
const category = toApiCategory(row.category);
|
|
17633
|
+
return {
|
|
17634
|
+
id: row.id,
|
|
17635
|
+
provider: toApiProvider(row.sourceTool),
|
|
17636
|
+
repo: row.repo,
|
|
17637
|
+
file: row.file,
|
|
17638
|
+
...row.toolName === void 0 ? {} : { toolName: row.toolName },
|
|
17639
|
+
eventId: row.eventId,
|
|
17640
|
+
...row.sessionId === void 0 ? {} : { sessionId: row.sessionId },
|
|
17641
|
+
action: toApiAction(row.actionTaken),
|
|
17642
|
+
detectedAt: row.occurredAt,
|
|
17643
|
+
confidence: row.confidence,
|
|
17644
|
+
...row.status === void 0 ? {} : { status: row.status },
|
|
17645
|
+
groupId: row.ruleId,
|
|
17646
|
+
category,
|
|
17647
|
+
subtype: row.ruleId,
|
|
17648
|
+
severity: row.severity,
|
|
17649
|
+
match: { maskedValue: row.maskedMatch, contextPrefix: "" },
|
|
17650
|
+
detection: { id: row.ruleId, name: null },
|
|
17651
|
+
policy: { id: `category:${category}`, name: category }
|
|
17652
|
+
};
|
|
17653
|
+
}
|
|
17654
|
+
var SEVERITY_ORDER2 = {
|
|
17655
|
+
critical: 0,
|
|
17656
|
+
high: 1,
|
|
17657
|
+
medium: 2,
|
|
17658
|
+
low: 3
|
|
17659
|
+
};
|
|
17660
|
+
function newLocationAccumulator() {
|
|
17661
|
+
return {
|
|
17662
|
+
instanceCount: 0,
|
|
17663
|
+
// Sorts after every known severity, so the first row always wins the
|
|
17664
|
+
// comparison below rather than an unknown value pinning the location.
|
|
17665
|
+
maxSeverityRank: Number.MAX_SAFE_INTEGER,
|
|
17666
|
+
maxSeverity: "low",
|
|
17667
|
+
latestDetectedAt: "",
|
|
17668
|
+
statuses: [],
|
|
17669
|
+
ruleIds: /* @__PURE__ */ new Set()
|
|
17357
17670
|
};
|
|
17358
17671
|
}
|
|
17672
|
+
function addToLocation(acc, row) {
|
|
17673
|
+
acc.instanceCount += 1;
|
|
17674
|
+
const rank = SEVERITY_ORDER2[row.severity] ?? Number.MAX_SAFE_INTEGER - 1;
|
|
17675
|
+
if (rank < acc.maxSeverityRank) {
|
|
17676
|
+
acc.maxSeverityRank = rank;
|
|
17677
|
+
acc.maxSeverity = row.severity;
|
|
17678
|
+
}
|
|
17679
|
+
if (row.occurredAt > acc.latestDetectedAt) acc.latestDetectedAt = row.occurredAt;
|
|
17680
|
+
acc.statuses.push(row.status);
|
|
17681
|
+
acc.ruleIds.add(row.ruleId);
|
|
17682
|
+
}
|
|
17359
17683
|
|
|
17360
17684
|
// ../../packages/schema/src/zod/installed-pack.ts
|
|
17361
17685
|
var InstalledPack = external_exports.object({
|
|
@@ -17492,6 +17816,50 @@ var VaultInventoryEntry = external_exports.object({
|
|
|
17492
17816
|
revealGrantId: external_exports.string().nullable(),
|
|
17493
17817
|
sightings: external_exports.array(VaultSighting)
|
|
17494
17818
|
});
|
|
17819
|
+
var DEFAULT_VAULT_INVENTORY_LIMIT = 50;
|
|
17820
|
+
var DEFAULT_VAULT_DEREFS_LIMIT = 50;
|
|
17821
|
+
var MAX_VAULT_PAGE_LIMIT = 200;
|
|
17822
|
+
var ListVaultInventoryQuery = external_exports.object({
|
|
17823
|
+
limit: external_exports.coerce.number().int().min(1).max(MAX_VAULT_PAGE_LIMIT).optional(),
|
|
17824
|
+
// Opaque; names the last row of the page just served.
|
|
17825
|
+
cursor: external_exports.string().optional()
|
|
17826
|
+
});
|
|
17827
|
+
var ListVaultInventoryResponse = external_exports.object({
|
|
17828
|
+
// Vaulted values across the whole store, not just this page — cursor-
|
|
17829
|
+
// independent, so paging never changes what the count claims.
|
|
17830
|
+
totals: external_exports.object({ values: external_exports.number().int().nonnegative() }),
|
|
17831
|
+
items: external_exports.array(VaultInventoryEntry),
|
|
17832
|
+
// `null` once the last page is reached.
|
|
17833
|
+
nextCursor: external_exports.string().nullable()
|
|
17834
|
+
});
|
|
17835
|
+
var ListVaultReuseQuery = external_exports.object({
|
|
17836
|
+
limit: external_exports.coerce.number().int().min(1).max(MAX_VAULT_PAGE_LIMIT).optional(),
|
|
17837
|
+
cursor: external_exports.string().optional()
|
|
17838
|
+
});
|
|
17839
|
+
var ListVaultReuseResponse = external_exports.object({
|
|
17840
|
+
// Reused values across the whole store — the number the section's claim
|
|
17841
|
+
// ("values detected in more than one place") is about.
|
|
17842
|
+
totals: external_exports.object({ reused: external_exports.number().int().nonnegative() }),
|
|
17843
|
+
items: external_exports.array(VaultInventoryEntry),
|
|
17844
|
+
nextCursor: external_exports.string().nullable()
|
|
17845
|
+
});
|
|
17846
|
+
var ListVaultDerefsQuery = external_exports.object({
|
|
17847
|
+
// Include the batched, high-volume reasons (display, view-render). Omitted
|
|
17848
|
+
// hides them and counts them into `hiddenBatched` instead, so the model
|
|
17849
|
+
// crossings stay visible. A real boolean, not `z.stringbool()`: this arrives
|
|
17850
|
+
// over a Server Action, which preserves the type, never as a URL param.
|
|
17851
|
+
includeBatched: external_exports.boolean().optional(),
|
|
17852
|
+
limit: external_exports.coerce.number().int().min(1).max(MAX_VAULT_PAGE_LIMIT).optional(),
|
|
17853
|
+
cursor: external_exports.string().optional()
|
|
17854
|
+
});
|
|
17855
|
+
var ListVaultDerefsResponse = external_exports.object({
|
|
17856
|
+
items: external_exports.array(VaultDeref),
|
|
17857
|
+
nextCursor: external_exports.string().nullable(),
|
|
17858
|
+
// Display/view-render rows the query hid, over the WHOLE trail rather than
|
|
17859
|
+
// this page — it is the count the "N hidden" line and its toggle speak for.
|
|
17860
|
+
// Always 0 when `includeBatched` was set, since nothing was hidden.
|
|
17861
|
+
hiddenBatched: external_exports.number().int().nonnegative()
|
|
17862
|
+
});
|
|
17495
17863
|
var VaultKeyCustody = external_exports.string();
|
|
17496
17864
|
var VaultInlineReveal = external_exports.enum(["masked", "full", "off"]);
|
|
17497
17865
|
var VaultConsent = external_exports.object({
|
|
@@ -17868,7 +18236,7 @@ var TopSourcesQuery = external_exports.object({
|
|
|
17868
18236
|
// Omit for both kinds.
|
|
17869
18237
|
kind: external_exports.enum(SOURCE_KINDS).optional()
|
|
17870
18238
|
});
|
|
17871
|
-
var Provider = external_exports.enum(["claudecode", "cursor", "codex", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
|
|
18239
|
+
var Provider = external_exports.enum(["claudecode", "cursor", "codex", "antigravity", "claudeai", "chatgpt", "copilot", "api"]).meta({ id: "Provider" });
|
|
17872
18240
|
var ScanCoverageProvider = external_exports.object({
|
|
17873
18241
|
provider: Provider,
|
|
17874
18242
|
// Percent of that provider's traffic scanned in the window. 0 when unsupported.
|
|
@@ -18110,6 +18478,138 @@ function captureId(sessionId, contentHash, filePath = null) {
|
|
|
18110
18478
|
);
|
|
18111
18479
|
}
|
|
18112
18480
|
|
|
18481
|
+
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18482
|
+
import { randomUUID } from "crypto";
|
|
18483
|
+
import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
|
|
18484
|
+
import { basename, dirname, join } from "path";
|
|
18485
|
+
|
|
18486
|
+
// ../../packages/persistence/src/paths.ts
|
|
18487
|
+
import {
|
|
18488
|
+
chmodSync,
|
|
18489
|
+
linkSync,
|
|
18490
|
+
lstatSync,
|
|
18491
|
+
mkdirSync,
|
|
18492
|
+
renameSync,
|
|
18493
|
+
rmSync,
|
|
18494
|
+
writeFileSync
|
|
18495
|
+
} from "fs";
|
|
18496
|
+
import { threadId } from "worker_threads";
|
|
18497
|
+
var DATA_DIR_MODE = 448;
|
|
18498
|
+
var DATA_FILE_MODE = 384;
|
|
18499
|
+
var DB_FILENAME = "aka.db";
|
|
18500
|
+
function isSymlink(path) {
|
|
18501
|
+
try {
|
|
18502
|
+
return lstatSync(path).isSymbolicLink();
|
|
18503
|
+
} catch {
|
|
18504
|
+
return false;
|
|
18505
|
+
}
|
|
18506
|
+
}
|
|
18507
|
+
function chmodBestEffort(path, mode) {
|
|
18508
|
+
if (isSymlink(path)) return;
|
|
18509
|
+
try {
|
|
18510
|
+
chmodSync(path, mode);
|
|
18511
|
+
} catch {
|
|
18512
|
+
}
|
|
18513
|
+
}
|
|
18514
|
+
function tightenDir(dir) {
|
|
18515
|
+
chmodBestEffort(dir, DATA_DIR_MODE);
|
|
18516
|
+
}
|
|
18517
|
+
function ensureDataDirSync(dir) {
|
|
18518
|
+
mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
18519
|
+
tightenDir(dir);
|
|
18520
|
+
}
|
|
18521
|
+
function dbSidecars(file2) {
|
|
18522
|
+
return [`${file2}-wal`, `${file2}-shm`, `${file2}-journal`];
|
|
18523
|
+
}
|
|
18524
|
+
function tightenFile(file2) {
|
|
18525
|
+
chmodBestEffort(file2, DATA_FILE_MODE);
|
|
18526
|
+
}
|
|
18527
|
+
function tightenPerms(file2) {
|
|
18528
|
+
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18529
|
+
}
|
|
18530
|
+
|
|
18531
|
+
// ../../packages/persistence/src/internal/snapshot.ts
|
|
18532
|
+
function backupPath(file2, tag) {
|
|
18533
|
+
return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
|
|
18534
|
+
}
|
|
18535
|
+
var STALE_PARTIAL_MS = 5 * 6e4;
|
|
18536
|
+
function reapStalePartials(file2) {
|
|
18537
|
+
const dir = dirname(file2);
|
|
18538
|
+
const prefix = `${basename(file2)}.`;
|
|
18539
|
+
let entries;
|
|
18540
|
+
try {
|
|
18541
|
+
entries = readdirSync(dir);
|
|
18542
|
+
} catch {
|
|
18543
|
+
return;
|
|
18544
|
+
}
|
|
18545
|
+
for (const name of entries) {
|
|
18546
|
+
if (!name.startsWith(prefix) || !name.endsWith(".bak.partial")) continue;
|
|
18547
|
+
const partial2 = join(dir, name);
|
|
18548
|
+
try {
|
|
18549
|
+
if (Date.now() - statSync(partial2).mtimeMs > STALE_PARTIAL_MS) {
|
|
18550
|
+
rmSync2(partial2, { force: true });
|
|
18551
|
+
}
|
|
18552
|
+
} catch {
|
|
18553
|
+
}
|
|
18554
|
+
}
|
|
18555
|
+
}
|
|
18556
|
+
function snapshotStore(db, backup) {
|
|
18557
|
+
const partial2 = `${backup}.partial`;
|
|
18558
|
+
try {
|
|
18559
|
+
rmSync2(partial2, { force: true });
|
|
18560
|
+
db.prepare("VACUUM INTO ?").run(partial2);
|
|
18561
|
+
tightenFile(partial2);
|
|
18562
|
+
renameSync2(partial2, backup);
|
|
18563
|
+
} catch (error51) {
|
|
18564
|
+
try {
|
|
18565
|
+
rmSync2(partial2, { force: true });
|
|
18566
|
+
} catch {
|
|
18567
|
+
}
|
|
18568
|
+
throw error51;
|
|
18569
|
+
}
|
|
18570
|
+
}
|
|
18571
|
+
function moveStoreAside(file2, backup) {
|
|
18572
|
+
const undo = [];
|
|
18573
|
+
renameSync2(file2, backup);
|
|
18574
|
+
undo.push([backup, file2]);
|
|
18575
|
+
try {
|
|
18576
|
+
for (const sidecar of dbSidecars(file2)) {
|
|
18577
|
+
const moved = `${backup}${sidecar.slice(file2.length)}`;
|
|
18578
|
+
try {
|
|
18579
|
+
renameSync2(sidecar, moved);
|
|
18580
|
+
undo.push([moved, sidecar]);
|
|
18581
|
+
} catch {
|
|
18582
|
+
rmSync2(sidecar, { force: true });
|
|
18583
|
+
}
|
|
18584
|
+
}
|
|
18585
|
+
} catch (error51) {
|
|
18586
|
+
for (const [from, to] of undo.reverse()) {
|
|
18587
|
+
try {
|
|
18588
|
+
renameSync2(from, to);
|
|
18589
|
+
} catch {
|
|
18590
|
+
}
|
|
18591
|
+
}
|
|
18592
|
+
throw error51;
|
|
18593
|
+
}
|
|
18594
|
+
tightenPerms(backup);
|
|
18595
|
+
}
|
|
18596
|
+
function discardStore(file2, backup) {
|
|
18597
|
+
try {
|
|
18598
|
+
rmSync2(file2, { force: true });
|
|
18599
|
+
for (const sidecar of dbSidecars(file2)) {
|
|
18600
|
+
rmSync2(sidecar, { force: true });
|
|
18601
|
+
}
|
|
18602
|
+
} catch (error51) {
|
|
18603
|
+
if (existsSync(file2)) {
|
|
18604
|
+
try {
|
|
18605
|
+
rmSync2(backup, { force: true });
|
|
18606
|
+
} catch {
|
|
18607
|
+
}
|
|
18608
|
+
}
|
|
18609
|
+
throw error51;
|
|
18610
|
+
}
|
|
18611
|
+
}
|
|
18612
|
+
|
|
18113
18613
|
// ../../packages/persistence/src/internal/sql-text.ts
|
|
18114
18614
|
function escapeLikePattern(s) {
|
|
18115
18615
|
return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
@@ -18251,38 +18751,6 @@ function mapRowsTolerant(rows, map2) {
|
|
|
18251
18751
|
return out;
|
|
18252
18752
|
}
|
|
18253
18753
|
|
|
18254
|
-
// ../../packages/persistence/src/paths.ts
|
|
18255
|
-
import { chmodSync, lstatSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
18256
|
-
var DATA_DIR_MODE = 448;
|
|
18257
|
-
var DATA_FILE_MODE = 384;
|
|
18258
|
-
var DB_FILENAME = "aka.db";
|
|
18259
|
-
function chmodBestEffort(path, mode) {
|
|
18260
|
-
try {
|
|
18261
|
-
chmodSync(path, mode);
|
|
18262
|
-
} catch {
|
|
18263
|
-
}
|
|
18264
|
-
}
|
|
18265
|
-
function tightenDir(dir) {
|
|
18266
|
-
chmodBestEffort(dir, DATA_DIR_MODE);
|
|
18267
|
-
}
|
|
18268
|
-
function ensureDataDirSync(dir) {
|
|
18269
|
-
mkdirSync(dir, { recursive: true, mode: DATA_DIR_MODE });
|
|
18270
|
-
tightenDir(dir);
|
|
18271
|
-
}
|
|
18272
|
-
function dbSidecars(file2) {
|
|
18273
|
-
return [`${file2}-wal`, `${file2}-shm`, `${file2}-journal`];
|
|
18274
|
-
}
|
|
18275
|
-
function tightenFile(file2) {
|
|
18276
|
-
try {
|
|
18277
|
-
if (lstatSync(file2).isSymbolicLink()) return;
|
|
18278
|
-
} catch {
|
|
18279
|
-
}
|
|
18280
|
-
chmodBestEffort(file2, DATA_FILE_MODE);
|
|
18281
|
-
}
|
|
18282
|
-
function tightenPerms(file2) {
|
|
18283
|
-
for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
|
|
18284
|
-
}
|
|
18285
|
-
|
|
18286
18754
|
// ../../packages/persistence/src/migrations.ts
|
|
18287
18755
|
function describeObject(object2) {
|
|
18288
18756
|
return object2.kind === "column" ? `column ${object2.table}.${object2.name}` : `table ${object2.name}`;
|
|
@@ -18398,9 +18866,9 @@ function applyLegacyDropMigration(db, file2) {
|
|
|
18398
18866
|
}
|
|
18399
18867
|
}
|
|
18400
18868
|
function backupBeforeLegacyDrop(db, file2) {
|
|
18401
|
-
|
|
18402
|
-
|
|
18403
|
-
|
|
18869
|
+
reapStalePartials(file2);
|
|
18870
|
+
const backup = backupPath(file2, "pre-drop");
|
|
18871
|
+
snapshotStore(db, backup);
|
|
18404
18872
|
return backup;
|
|
18405
18873
|
}
|
|
18406
18874
|
var TOKEN_USAGE_COLUMNS = [
|
|
@@ -18744,6 +19212,25 @@ function parseJsonObject(s) {
|
|
|
18744
19212
|
return void 0;
|
|
18745
19213
|
}
|
|
18746
19214
|
|
|
19215
|
+
// ../../packages/persistence/src/internal/keyset-cursor.ts
|
|
19216
|
+
function encodeKeysetCursor(payload) {
|
|
19217
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
19218
|
+
}
|
|
19219
|
+
function decodeKeysetCursor(cursor) {
|
|
19220
|
+
const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
19221
|
+
if (parsed !== void 0 && "startedAtMs" in parsed && "id" in parsed && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
|
|
19222
|
+
// resumes from is epoch millis, and a payload carrying ±Infinity or a
|
|
19223
|
+
// fraction binds cleanly rather than failing — returning an EMPTY page with
|
|
19224
|
+
// a null cursor, which a caller reads as "end of list". That is the one
|
|
19225
|
+
// outcome a cursor that does not decode must never produce, since the
|
|
19226
|
+
// documented behaviour above is to restart from the top. (`1e999` is valid
|
|
19227
|
+
// JSON and parses to Infinity; a bare `NaN` is not, so it cannot arrive.)
|
|
19228
|
+
Number.isInteger(parsed.startedAtMs) && typeof parsed.id === "string") {
|
|
19229
|
+
return parsed;
|
|
19230
|
+
}
|
|
19231
|
+
return null;
|
|
19232
|
+
}
|
|
19233
|
+
|
|
18747
19234
|
// ../../packages/persistence/src/repositories/activity.ts
|
|
18748
19235
|
var DAY_MS = 864e5;
|
|
18749
19236
|
var LIVE_ACTIVITY_WINDOW_MS = 30 * 6e4;
|
|
@@ -18789,16 +19276,6 @@ function utcWindow(nowMs) {
|
|
|
18789
19276
|
const startMs = Math.floor(nowMs / DAY_MS) * DAY_MS;
|
|
18790
19277
|
return { startMs, endMs: startMs + DAY_MS };
|
|
18791
19278
|
}
|
|
18792
|
-
function encodeCursor(payload) {
|
|
18793
|
-
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
18794
|
-
}
|
|
18795
|
-
function decodeCursor(cursor) {
|
|
18796
|
-
const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
18797
|
-
if (parsed !== void 0 && "startedAtMs" in parsed && "id" in parsed && typeof parsed.startedAtMs === "number" && typeof parsed.id === "string") {
|
|
18798
|
-
return parsed;
|
|
18799
|
-
}
|
|
18800
|
-
return null;
|
|
18801
|
-
}
|
|
18802
19279
|
var DB_EVENT_TYPE_TO_KIND = {
|
|
18803
19280
|
session: "session",
|
|
18804
19281
|
prompt: "prompt",
|
|
@@ -18943,7 +19420,7 @@ var SqliteActivityRepository = class {
|
|
|
18943
19420
|
return Promise.resolve({ sessionsToday, liveNow, toolCallsToday, findingsToday, egressToday });
|
|
18944
19421
|
}
|
|
18945
19422
|
listSessions(query) {
|
|
18946
|
-
const cursor = query.cursor ?
|
|
19423
|
+
const cursor = query.cursor ? decodeKeysetCursor(query.cursor) : null;
|
|
18947
19424
|
const toMs = query.to ? isoToEpochMillis(query.to) : this.now();
|
|
18948
19425
|
const fromMs = query.from ? isoToEpochMillis(query.from) : void 0;
|
|
18949
19426
|
const conditions = [SESSION_ROOT];
|
|
@@ -19017,7 +19494,7 @@ var SqliteActivityRepository = class {
|
|
|
19017
19494
|
)
|
|
19018
19495
|
);
|
|
19019
19496
|
const last = page[page.length - 1];
|
|
19020
|
-
const nextCursor = hasMore && last ?
|
|
19497
|
+
const nextCursor = hasMore && last ? encodeKeysetCursor({ startedAtMs: last.started_at, id: last.id }) : null;
|
|
19021
19498
|
return Promise.resolve({ items, nextCursor, emptyCount });
|
|
19022
19499
|
}
|
|
19023
19500
|
getSession(sessionId) {
|
|
@@ -19890,7 +20367,7 @@ var SqliteEventsRepository = class {
|
|
|
19890
20367
|
};
|
|
19891
20368
|
|
|
19892
20369
|
// ../../packages/persistence/src/repositories/exceptions.ts
|
|
19893
|
-
import { randomUUID } from "crypto";
|
|
20370
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
19894
20371
|
|
|
19895
20372
|
// ../../packages/persistence/src/internal/sqlite-errors.ts
|
|
19896
20373
|
var SQLITE_CONSTRAINT_UNIQUE = 2067;
|
|
@@ -19926,8 +20403,9 @@ var ACTIVE_REVEAL_GRANT_PREDICATE = `capability = 'reveal_to_model'
|
|
|
19926
20403
|
AND conditions IS NULL
|
|
19927
20404
|
AND ${ACTIVE_PREDICATE}`;
|
|
19928
20405
|
var SqliteExceptionsRepository = class {
|
|
19929
|
-
constructor(db) {
|
|
20406
|
+
constructor(db, now = () => Date.now()) {
|
|
19930
20407
|
this.db = db;
|
|
20408
|
+
this.now = now;
|
|
19931
20409
|
this.consumeStmt = db.prepare(
|
|
19932
20410
|
`UPDATE exceptions
|
|
19933
20411
|
SET use_count = use_count + 1, last_used_at = :now, updated_at = :now
|
|
@@ -19945,6 +20423,7 @@ var SqliteExceptionsRepository = class {
|
|
|
19945
20423
|
);
|
|
19946
20424
|
}
|
|
19947
20425
|
db;
|
|
20426
|
+
now;
|
|
19948
20427
|
consumeStmt;
|
|
19949
20428
|
insertBlockedStmt;
|
|
19950
20429
|
sweepBlockedStmt;
|
|
@@ -19971,8 +20450,8 @@ var SqliteExceptionsRepository = class {
|
|
|
19971
20450
|
"provider conditions are not supported yet \u2014 a grant with one would never apply"
|
|
19972
20451
|
);
|
|
19973
20452
|
}
|
|
19974
|
-
const id =
|
|
19975
|
-
const now =
|
|
20453
|
+
const id = randomUUID2();
|
|
20454
|
+
const now = this.now();
|
|
19976
20455
|
try {
|
|
19977
20456
|
this.insertExceptionRow(id, input, now);
|
|
19978
20457
|
} catch (err) {
|
|
@@ -20050,7 +20529,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20050
20529
|
const where = opts?.includeTerminal ? "" : `WHERE ${ACTIVE_PREDICATE}`;
|
|
20051
20530
|
const rows = allRows(
|
|
20052
20531
|
this.db.prepare(`SELECT * FROM exceptions ${where} ORDER BY created_at DESC, rowid DESC`),
|
|
20053
|
-
opts?.includeTerminal ? {} : { now:
|
|
20532
|
+
opts?.includeTerminal ? {} : { now: this.now() }
|
|
20054
20533
|
);
|
|
20055
20534
|
const exceptions = mapRowsTolerant(rows, parseExceptionRow);
|
|
20056
20535
|
return Promise.resolve(exceptions);
|
|
@@ -20085,7 +20564,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20085
20564
|
* already revoked.
|
|
20086
20565
|
*/
|
|
20087
20566
|
revoke(id, revokedBy, reason) {
|
|
20088
|
-
const now =
|
|
20567
|
+
const now = this.now();
|
|
20089
20568
|
const result = this.db.prepare(
|
|
20090
20569
|
`UPDATE exceptions
|
|
20091
20570
|
SET revoked_at = :now, revoked_by = :revokedBy, revoke_reason = :reason, updated_at = :now
|
|
@@ -20099,7 +20578,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20099
20578
|
* callers must treat identically — means it does not and the detection is
|
|
20100
20579
|
* enforced as usual. Deliberately NOT wrapped in try/catch.
|
|
20101
20580
|
*/
|
|
20102
|
-
consume(id, now =
|
|
20581
|
+
consume(id, now = this.now()) {
|
|
20103
20582
|
const result = this.consumeStmt.run({ id, now });
|
|
20104
20583
|
return Promise.resolve(Number(result.changes) === 1);
|
|
20105
20584
|
}
|
|
@@ -20108,7 +20587,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20108
20587
|
* version — what rides the policy bundle to the hook. Grants written under
|
|
20109
20588
|
* a different (rotated-away) key never match, so they are excluded at read.
|
|
20110
20589
|
*/
|
|
20111
|
-
activeBundleEntries(keyVersion, now =
|
|
20590
|
+
activeBundleEntries(keyVersion, now = this.now()) {
|
|
20112
20591
|
const rows = allRows(
|
|
20113
20592
|
this.db.prepare(
|
|
20114
20593
|
`SELECT * FROM exceptions
|
|
@@ -20140,7 +20619,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20140
20619
|
* than the retention window on every write, so the ledger self-limits.
|
|
20141
20620
|
*/
|
|
20142
20621
|
recordBlocked(entry) {
|
|
20143
|
-
const now =
|
|
20622
|
+
const now = this.now();
|
|
20144
20623
|
this.sweepBlockedStmt.run({ cutoff: now - BLOCKED_DETECTIONS_RETENTION_MS });
|
|
20145
20624
|
this.insertBlockedStmt.run({
|
|
20146
20625
|
reference: entry.reference,
|
|
@@ -20163,7 +20642,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20163
20642
|
WHERE blocked_at > :cutoff
|
|
20164
20643
|
ORDER BY blocked_at DESC, rowid DESC`
|
|
20165
20644
|
),
|
|
20166
|
-
{ cutoff:
|
|
20645
|
+
{ cutoff: this.now() - windowMs }
|
|
20167
20646
|
);
|
|
20168
20647
|
return Promise.resolve(
|
|
20169
20648
|
rows.map((row) => ({
|
|
@@ -20191,8 +20670,9 @@ var SqliteExceptionsRepository = class {
|
|
|
20191
20670
|
* evaluate conditions, and a narrowing clause that is ignored would WIDEN the
|
|
20192
20671
|
* grant instead. Fail closed until reveal-side condition evaluation exists.
|
|
20193
20672
|
*/
|
|
20194
|
-
activeRevealGrant(ruleId, valueFingerprint, keyVersion, now
|
|
20673
|
+
activeRevealGrant(ruleId, valueFingerprint, keyVersion, now) {
|
|
20195
20674
|
try {
|
|
20675
|
+
const at = now ?? this.now();
|
|
20196
20676
|
const row = getRow(
|
|
20197
20677
|
this.db.prepare(
|
|
20198
20678
|
`SELECT id FROM exceptions
|
|
@@ -20201,7 +20681,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20201
20681
|
AND ${ACTIVE_REVEAL_GRANT_PREDICATE}
|
|
20202
20682
|
LIMIT 1`
|
|
20203
20683
|
),
|
|
20204
|
-
{ ruleId, valueFingerprint, keyVersion, now }
|
|
20684
|
+
{ ruleId, valueFingerprint, keyVersion, now: at }
|
|
20205
20685
|
);
|
|
20206
20686
|
return Promise.resolve(row ?? null);
|
|
20207
20687
|
} catch (err) {
|
|
@@ -20215,7 +20695,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20215
20695
|
* predicate, so correctness never depends on this sweep; it only bounds how
|
|
20216
20696
|
* long the audit evidence is kept locally. Returns the deleted count.
|
|
20217
20697
|
*/
|
|
20218
|
-
sweepTerminal(retentionMs, now =
|
|
20698
|
+
sweepTerminal(retentionMs, now = this.now()) {
|
|
20219
20699
|
const result = this.db.prepare(
|
|
20220
20700
|
`DELETE FROM exceptions
|
|
20221
20701
|
WHERE updated_at < :cutoff
|
|
@@ -20278,6 +20758,23 @@ var LATEST_RESOLUTION_BY_KEY_SQL = `(
|
|
|
20278
20758
|
|
|
20279
20759
|
// ../../packages/persistence/src/repositories/findings.ts
|
|
20280
20760
|
var PREVIEW_INSTANCES_PER_GROUP = 200;
|
|
20761
|
+
var SCAN_BATCH_ROWS = 1e3;
|
|
20762
|
+
var DEFAULT_LOCATIONS_LIMIT = 100;
|
|
20763
|
+
var LOCATION_RULE_IDS_CAP = 20;
|
|
20764
|
+
function compareLocationOrder(a, b) {
|
|
20765
|
+
return compareFindingGroupOrder(
|
|
20766
|
+
{
|
|
20767
|
+
severity: a.maxSeverity,
|
|
20768
|
+
latestDetectedAt: a.latestDetectedAt,
|
|
20769
|
+
id: ""
|
|
20770
|
+
},
|
|
20771
|
+
{
|
|
20772
|
+
severity: b.maxSeverity,
|
|
20773
|
+
latestDetectedAt: b.latestDetectedAt,
|
|
20774
|
+
id: ""
|
|
20775
|
+
}
|
|
20776
|
+
);
|
|
20777
|
+
}
|
|
20281
20778
|
var CONCAT_SEP = ",";
|
|
20282
20779
|
var TUPLE_SEP = "|";
|
|
20283
20780
|
function splitConcat(value) {
|
|
@@ -20290,6 +20787,33 @@ function deriveInstanceStatus(row) {
|
|
|
20290
20787
|
latestResolutionStatus: row.latest_status
|
|
20291
20788
|
});
|
|
20292
20789
|
}
|
|
20790
|
+
function encodeGroupCursor(group) {
|
|
20791
|
+
const payload = {
|
|
20792
|
+
sev: group.severity,
|
|
20793
|
+
t: group.latestDetectedAt,
|
|
20794
|
+
id: group.id
|
|
20795
|
+
};
|
|
20796
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
20797
|
+
}
|
|
20798
|
+
function decodeGroupCursor(cursor) {
|
|
20799
|
+
const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
20800
|
+
if (parsed !== void 0 && typeof parsed.sev === "string" && typeof parsed.t === "string" && typeof parsed.id === "string") {
|
|
20801
|
+
return {
|
|
20802
|
+
severity: parsed.sev,
|
|
20803
|
+
latestDetectedAt: parsed.t,
|
|
20804
|
+
id: parsed.id
|
|
20805
|
+
};
|
|
20806
|
+
}
|
|
20807
|
+
return null;
|
|
20808
|
+
}
|
|
20809
|
+
function firstAfter(sorted, cursor) {
|
|
20810
|
+
const index = sorted.findIndex((g) => compareFindingGroupOrder(g, cursor) > 0);
|
|
20811
|
+
return index === -1 ? sorted.length : index;
|
|
20812
|
+
}
|
|
20813
|
+
function findDeepLinked(sorted, page, id) {
|
|
20814
|
+
if (page.some((g) => g.id === id || g.instances.some((i) => i.id === id))) return void 0;
|
|
20815
|
+
return sorted.find((g) => g.id === id || g.instances.some((i) => i.id === id));
|
|
20816
|
+
}
|
|
20293
20817
|
var DAY_MS3 = 864e5;
|
|
20294
20818
|
var SqliteFindingsRepository = class {
|
|
20295
20819
|
constructor(db) {
|
|
@@ -20399,8 +20923,13 @@ var SqliteFindingsRepository = class {
|
|
|
20399
20923
|
*/
|
|
20400
20924
|
listGroupedFindings(query) {
|
|
20401
20925
|
const sessionPredicate = query.sessionId ? ` AND e.root_session_id = :sessionId` : "";
|
|
20402
|
-
const
|
|
20403
|
-
const
|
|
20926
|
+
const fromMs = query.from === void 0 ? void 0 : isoToEpochMillis(query.from);
|
|
20927
|
+
const fromPredicate = fromMs === void 0 ? "" : ` AND e.started_at >= :fromMs`;
|
|
20928
|
+
const predicate = `WHERE e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})${sessionPredicate}${fromPredicate}`;
|
|
20929
|
+
const sessionParams = {
|
|
20930
|
+
...query.sessionId ? { sessionId: query.sessionId } : {},
|
|
20931
|
+
...fromMs === void 0 ? {} : { fromMs }
|
|
20932
|
+
};
|
|
20404
20933
|
const aggregates = this.groupAggregates(query.q !== void 0 && query.q !== "", {
|
|
20405
20934
|
predicate,
|
|
20406
20935
|
params: sessionParams
|
|
@@ -20408,7 +20937,8 @@ var SqliteFindingsRepository = class {
|
|
|
20408
20937
|
const rows = allRows(
|
|
20409
20938
|
this.db.prepare(
|
|
20410
20939
|
`SELECT id, rule_id, category, severity, masked_match, action_taken, confidence,
|
|
20411
|
-
occurred_at, source_tool, repo, file, tool_name,
|
|
20940
|
+
occurred_at, source_tool, repo, file, tool_name, event_id, session_id,
|
|
20941
|
+
kind, finding_key, latest_status
|
|
20412
20942
|
FROM (
|
|
20413
20943
|
SELECT f.id AS id, d.rule_id AS rule_id, d.category AS category,
|
|
20414
20944
|
d.severity AS severity, f.masked_match AS masked_match,
|
|
@@ -20418,6 +20948,7 @@ var SqliteFindingsRepository = class {
|
|
|
20418
20948
|
json_extract(e.attributes, '$.repo') AS repo,
|
|
20419
20949
|
json_extract(e.attributes, '$.file_path') AS file,
|
|
20420
20950
|
json_extract(e.attributes, '$.tool_name') AS tool_name,
|
|
20951
|
+
f.audit_event_id AS event_id, e.root_session_id AS session_id,
|
|
20421
20952
|
e.event_type AS kind, f.finding_key AS finding_key,
|
|
20422
20953
|
latest.status AS latest_status,
|
|
20423
20954
|
ROW_NUMBER() OVER (
|
|
@@ -20449,6 +20980,8 @@ var SqliteFindingsRepository = class {
|
|
|
20449
20980
|
repo: r.repo ?? "",
|
|
20450
20981
|
file: r.file ?? "",
|
|
20451
20982
|
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
20983
|
+
eventId: r.event_id,
|
|
20984
|
+
...r.session_id === null ? {} : { sessionId: r.session_id },
|
|
20452
20985
|
status: deriveInstanceStatus(r)
|
|
20453
20986
|
}));
|
|
20454
20987
|
const allGroups = buildFindingGroups(groupable, { aggregates });
|
|
@@ -20472,18 +21005,23 @@ var SqliteFindingsRepository = class {
|
|
|
20472
21005
|
groups: sorted.length
|
|
20473
21006
|
};
|
|
20474
21007
|
const limit = query.limit ?? DEFAULT_GROUPED_FINDINGS_LIMIT;
|
|
21008
|
+
const cursor = query.cursor === void 0 ? null : decodeGroupCursor(query.cursor);
|
|
21009
|
+
const start = cursor === null ? 0 : firstAfter(sorted, cursor);
|
|
21010
|
+
const page = sorted.slice(start, start + limit);
|
|
21011
|
+
const lastOnPage = page.at(-1);
|
|
21012
|
+
const nextCursor = start + limit < sorted.length && lastOnPage ? encodeGroupCursor(lastOnPage) : null;
|
|
21013
|
+
const deepLinked = query.includeId === void 0 || query.includeId === "" ? void 0 : findDeepLinked(sorted, page, query.includeId);
|
|
20475
21014
|
const statusSet = statusFilter.length > 0 ? new Set(statusFilter) : null;
|
|
20476
|
-
const
|
|
20477
|
-
|
|
20478
|
-
|
|
20479
|
-
|
|
20480
|
-
|
|
20481
|
-
);
|
|
21015
|
+
const narrow = (g) => statusSet ? {
|
|
21016
|
+
...g,
|
|
21017
|
+
instances: g.instances.filter((i) => i.status !== void 0 && statusSet.has(i.status))
|
|
21018
|
+
} : g;
|
|
21019
|
+
const items = [...page, ...deepLinked ? [deepLinked] : []].map(narrow);
|
|
20482
21020
|
return Promise.resolve({
|
|
20483
21021
|
totals,
|
|
20484
21022
|
facets,
|
|
20485
21023
|
items,
|
|
20486
|
-
nextCursor
|
|
21024
|
+
nextCursor,
|
|
20487
21025
|
...query.sessionId ? { sessionFirings: this.sessionFirings(query.sessionId) } : {}
|
|
20488
21026
|
});
|
|
20489
21027
|
}
|
|
@@ -20515,14 +21053,274 @@ var SqliteFindingsRepository = class {
|
|
|
20515
21053
|
* request actually carries a `q`. (Substring matching is unaffected by a
|
|
20516
21054
|
* path repeating across tuples.)
|
|
20517
21055
|
*/
|
|
20518
|
-
|
|
20519
|
-
|
|
20520
|
-
|
|
20521
|
-
|
|
20522
|
-
|
|
20523
|
-
|
|
20524
|
-
|
|
20525
|
-
|
|
21056
|
+
/**
|
|
21057
|
+
* The instance-level (flat) findings list: one row per finding, newest first,
|
|
21058
|
+
* paged by keyset.
|
|
21059
|
+
*
|
|
21060
|
+
* SQL owns SCOPE, JS owns every FILTER DIMENSION. The session and the time
|
|
21061
|
+
* bound are SQL predicates: nothing counts them, so narrowing the scan by
|
|
21062
|
+
* them changes no reported number. Severity, subtype, provider, action,
|
|
21063
|
+
* status, tool, repo, file and `q` all stay in JS — each has a facet, and a
|
|
21064
|
+
* facet excludes its own filter, so a row the filter rejects still has to be
|
|
21065
|
+
* counted. Pushing any of them into SQL would silently empty its own facet.
|
|
21066
|
+
* Several could not be expressed there anyway: status comes from the one
|
|
21067
|
+
* shared classifier (deriveFindingStatus), and provider 'api' means "a tool
|
|
21068
|
+
* none of the mappers names", which no IN-list can say.
|
|
21069
|
+
*
|
|
21070
|
+
* The scan runs from the top of the scope on every request, not from the
|
|
21071
|
+
* cursor: `totals` and `facets` describe the whole filtered scope and must not
|
|
21072
|
+
* move as the caller pages. Rows are pulled in batches so memory stays flat
|
|
21073
|
+
* while the counting runs, and only the page itself is retained.
|
|
21074
|
+
*/
|
|
21075
|
+
listFindingInstances(query) {
|
|
21076
|
+
const opts = {
|
|
21077
|
+
severity: query.severity,
|
|
21078
|
+
subtype: query.subtype,
|
|
21079
|
+
providers: query.provider,
|
|
21080
|
+
actions: query.action,
|
|
21081
|
+
statuses: query.status,
|
|
21082
|
+
tools: query.tool,
|
|
21083
|
+
repo: query.repo,
|
|
21084
|
+
file: query.file,
|
|
21085
|
+
q: query.q
|
|
21086
|
+
};
|
|
21087
|
+
const limit = query.limit ?? DEFAULT_FLAT_FINDINGS_LIMIT;
|
|
21088
|
+
const cursor = query.cursor === void 0 ? null : decodeKeysetCursor(query.cursor);
|
|
21089
|
+
const accumulator = createInstanceFacetAccumulator(opts);
|
|
21090
|
+
const items = [];
|
|
21091
|
+
let total = 0;
|
|
21092
|
+
let last;
|
|
21093
|
+
let hasMore = false;
|
|
21094
|
+
for (const row of this.scanFindingRows({
|
|
21095
|
+
sessionId: query.sessionId,
|
|
21096
|
+
from: query.from
|
|
21097
|
+
})) {
|
|
21098
|
+
accumulator.add(row);
|
|
21099
|
+
if (!matchesInstanceFilters(row, opts)) continue;
|
|
21100
|
+
total += 1;
|
|
21101
|
+
if (items.length < limit) {
|
|
21102
|
+
items.push(toInstanceDetail(row));
|
|
21103
|
+
last = row;
|
|
21104
|
+
} else {
|
|
21105
|
+
hasMore = true;
|
|
21106
|
+
}
|
|
21107
|
+
}
|
|
21108
|
+
const nextCursor = hasMore && last ? encodeKeysetCursor({ startedAtMs: isoToEpochMillis(last.occurredAt), id: last.id }) : null;
|
|
21109
|
+
if (cursor !== null) {
|
|
21110
|
+
const resumed = this.pageAfter(cursor, opts, limit, query);
|
|
21111
|
+
return Promise.resolve({
|
|
21112
|
+
totals: { findings: total },
|
|
21113
|
+
facets: accumulator.facets(),
|
|
21114
|
+
items: resumed.items,
|
|
21115
|
+
nextCursor: resumed.nextCursor
|
|
21116
|
+
});
|
|
21117
|
+
}
|
|
21118
|
+
return Promise.resolve({
|
|
21119
|
+
totals: { findings: total },
|
|
21120
|
+
facets: accumulator.facets(),
|
|
21121
|
+
items,
|
|
21122
|
+
nextCursor
|
|
21123
|
+
});
|
|
21124
|
+
}
|
|
21125
|
+
/**
|
|
21126
|
+
* The page of matching rows strictly after `cursor`. Separate from the
|
|
21127
|
+
* counting pass because that one starts at the top of the scope by design;
|
|
21128
|
+
* this one narrows the scan with the same keyset predicate the activity list
|
|
21129
|
+
* uses, so a later page costs less than the first rather than more.
|
|
21130
|
+
*/
|
|
21131
|
+
pageAfter(cursor, opts, limit, query) {
|
|
21132
|
+
const items = [];
|
|
21133
|
+
let last;
|
|
21134
|
+
let hasMore = false;
|
|
21135
|
+
for (const row of this.scanFindingRows({
|
|
21136
|
+
sessionId: query.sessionId,
|
|
21137
|
+
from: query.from,
|
|
21138
|
+
after: cursor
|
|
21139
|
+
})) {
|
|
21140
|
+
if (!matchesInstanceFilters(row, opts)) continue;
|
|
21141
|
+
if (items.length < limit) {
|
|
21142
|
+
items.push(toInstanceDetail(row));
|
|
21143
|
+
last = row;
|
|
21144
|
+
} else {
|
|
21145
|
+
hasMore = true;
|
|
21146
|
+
break;
|
|
21147
|
+
}
|
|
21148
|
+
}
|
|
21149
|
+
return {
|
|
21150
|
+
items,
|
|
21151
|
+
nextCursor: hasMore && last ? encodeKeysetCursor({ startedAtMs: isoToEpochMillis(last.occurredAt), id: last.id }) : null
|
|
21152
|
+
};
|
|
21153
|
+
}
|
|
21154
|
+
/**
|
|
21155
|
+
* The same findings folded by location: repository, then file within it.
|
|
21156
|
+
*
|
|
21157
|
+
* The grouping keys come from the capturing event's attributes, which is what
|
|
21158
|
+
* the local store relates a finding to — there is no finding↔asset row to
|
|
21159
|
+
* group by instead. A repo or file the event did not record folds into the
|
|
21160
|
+
* empty-string bucket, which the view renders but does not link, since no
|
|
21161
|
+
* filter can name it.
|
|
21162
|
+
*/
|
|
21163
|
+
listFindingLocations(query) {
|
|
21164
|
+
const opts = {
|
|
21165
|
+
severity: query.severity,
|
|
21166
|
+
subtype: query.subtype,
|
|
21167
|
+
providers: query.provider,
|
|
21168
|
+
actions: query.action,
|
|
21169
|
+
statuses: query.status,
|
|
21170
|
+
tools: query.tool,
|
|
21171
|
+
q: query.q
|
|
21172
|
+
};
|
|
21173
|
+
const limit = query.limit ?? DEFAULT_LOCATIONS_LIMIT;
|
|
21174
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
21175
|
+
let total = 0;
|
|
21176
|
+
for (const row of this.scanFindingRows({
|
|
21177
|
+
sessionId: query.sessionId,
|
|
21178
|
+
from: query.from
|
|
21179
|
+
})) {
|
|
21180
|
+
if (!matchesInstanceFilters(row, opts)) continue;
|
|
21181
|
+
total += 1;
|
|
21182
|
+
let files = byRepo.get(row.repo);
|
|
21183
|
+
if (files === void 0) {
|
|
21184
|
+
files = /* @__PURE__ */ new Map();
|
|
21185
|
+
byRepo.set(row.repo, files);
|
|
21186
|
+
}
|
|
21187
|
+
let acc = files.get(row.file);
|
|
21188
|
+
if (acc === void 0) {
|
|
21189
|
+
acc = newLocationAccumulator();
|
|
21190
|
+
files.set(row.file, acc);
|
|
21191
|
+
}
|
|
21192
|
+
addToLocation(acc, row);
|
|
21193
|
+
}
|
|
21194
|
+
let fileCount = 0;
|
|
21195
|
+
const repos = [...byRepo.entries()].map(([repo, files]) => {
|
|
21196
|
+
fileCount += files.size;
|
|
21197
|
+
const fileRows = [...files.entries()].map(([file2, acc]) => ({
|
|
21198
|
+
file: file2,
|
|
21199
|
+
instanceCount: acc.instanceCount,
|
|
21200
|
+
maxSeverity: acc.maxSeverity,
|
|
21201
|
+
latestDetectedAt: acc.latestDetectedAt,
|
|
21202
|
+
...foldGroupStatus(acc.statuses) === void 0 ? {} : { status: foldGroupStatus(acc.statuses) },
|
|
21203
|
+
ruleIds: [...acc.ruleIds].slice(0, LOCATION_RULE_IDS_CAP)
|
|
21204
|
+
})).sort(compareLocationOrder);
|
|
21205
|
+
const rollup = fileRows.reduce(
|
|
21206
|
+
(a, f) => ({
|
|
21207
|
+
instanceCount: a.instanceCount + f.instanceCount,
|
|
21208
|
+
maxSeverity: compareLocationOrder(f, a) < 0 ? f.maxSeverity : a.maxSeverity,
|
|
21209
|
+
latestDetectedAt: f.latestDetectedAt > a.latestDetectedAt ? f.latestDetectedAt : a.latestDetectedAt
|
|
21210
|
+
}),
|
|
21211
|
+
{
|
|
21212
|
+
instanceCount: 0,
|
|
21213
|
+
maxSeverity: fileRows[0]?.maxSeverity ?? "low",
|
|
21214
|
+
latestDetectedAt: ""
|
|
21215
|
+
}
|
|
21216
|
+
);
|
|
21217
|
+
const statuses = fileRows.map((f) => f.status);
|
|
21218
|
+
const folded = foldGroupStatus(statuses);
|
|
21219
|
+
return {
|
|
21220
|
+
repo,
|
|
21221
|
+
instanceCount: rollup.instanceCount,
|
|
21222
|
+
maxSeverity: rollup.maxSeverity,
|
|
21223
|
+
latestDetectedAt: rollup.latestDetectedAt,
|
|
21224
|
+
...folded === void 0 ? {} : { status: folded },
|
|
21225
|
+
files: fileRows
|
|
21226
|
+
};
|
|
21227
|
+
});
|
|
21228
|
+
repos.sort(compareLocationOrder);
|
|
21229
|
+
return Promise.resolve({
|
|
21230
|
+
totals: { findings: total, repos: repos.length, files: fileCount },
|
|
21231
|
+
items: repos.slice(0, limit),
|
|
21232
|
+
hasMore: repos.length > limit
|
|
21233
|
+
});
|
|
21234
|
+
}
|
|
21235
|
+
/**
|
|
21236
|
+
* Every finding in scope as a FlatFindingRow, newest first, pulled in batches.
|
|
21237
|
+
*
|
|
21238
|
+
* A generator so a caller streams the scope without it ever being an array:
|
|
21239
|
+
* the flat list counts and facets the whole filtered scope, which on a large
|
|
21240
|
+
* store is far more rows than any page. Each batch advances the same keyset
|
|
21241
|
+
* predicate the page read uses, so the scan is a sequence of bounded reads
|
|
21242
|
+
* rather than one unbounded result set.
|
|
21243
|
+
*
|
|
21244
|
+
* The latest-resolution lookup is the CORRELATED form, not the derived table
|
|
21245
|
+
* the grouped path joins: only `status` is needed, idx_finding_resolution_key
|
|
21246
|
+
* makes it a point lookup per row, and the derived table would re-materialize
|
|
21247
|
+
* a window over the whole resolution table once per batch.
|
|
21248
|
+
*
|
|
21249
|
+
* `scope` carries ONLY what no facet counts. A filter dimension narrowed here
|
|
21250
|
+
* would be missing from its own facet, which is computed by excluding that
|
|
21251
|
+
* dimension — see listFindingInstances.
|
|
21252
|
+
*/
|
|
21253
|
+
*scanFindingRows(scope) {
|
|
21254
|
+
const conditions = [`e.event_type IN (${CAPTURE_EVENT_TYPES_SQL})`];
|
|
21255
|
+
const params = [];
|
|
21256
|
+
if (scope.sessionId !== void 0 && scope.sessionId !== "") {
|
|
21257
|
+
conditions.push("e.root_session_id = ?");
|
|
21258
|
+
params.push(scope.sessionId);
|
|
21259
|
+
}
|
|
21260
|
+
if (scope.from !== void 0) {
|
|
21261
|
+
conditions.push("e.started_at >= ?");
|
|
21262
|
+
params.push(isoToEpochMillis(scope.from));
|
|
21263
|
+
}
|
|
21264
|
+
const sql = `SELECT f.id AS id, d.rule_id AS rule_id, d.category AS category,
|
|
21265
|
+
d.severity AS severity, f.masked_match AS masked_match,
|
|
21266
|
+
f.action_taken AS action_taken, f.confidence AS confidence,
|
|
21267
|
+
e.started_at AS occurred_at,
|
|
21268
|
+
json_extract(e.attributes, '$.source_tool') AS source_tool,
|
|
21269
|
+
json_extract(e.attributes, '$.repo') AS repo,
|
|
21270
|
+
json_extract(e.attributes, '$.file_path') AS file,
|
|
21271
|
+
json_extract(e.attributes, '$.tool_name') AS tool_name,
|
|
21272
|
+
f.audit_event_id AS event_id, e.root_session_id AS session_id,
|
|
21273
|
+
e.event_type AS kind, f.finding_key AS finding_key,
|
|
21274
|
+
${latestResolutionStatusSql("f")} AS latest_status
|
|
21275
|
+
FROM inspection_findings f
|
|
21276
|
+
JOIN audit_events e ON e.id = f.audit_event_id
|
|
21277
|
+
JOIN inspection_definitions d ON d.id = f.inspection_definition_id
|
|
21278
|
+
WHERE ${conditions.join(" AND ")}
|
|
21279
|
+
AND (e.started_at < ? OR (e.started_at = ? AND f.id < ?))
|
|
21280
|
+
ORDER BY e.started_at DESC, f.id DESC
|
|
21281
|
+
LIMIT ?`;
|
|
21282
|
+
let after = scope.after ?? { startedAtMs: Number.MAX_SAFE_INTEGER, id: "\uFFFF" };
|
|
21283
|
+
for (; ; ) {
|
|
21284
|
+
const rows = allRows(this.db.prepare(sql), [
|
|
21285
|
+
...params,
|
|
21286
|
+
after.startedAtMs,
|
|
21287
|
+
after.startedAtMs,
|
|
21288
|
+
after.id,
|
|
21289
|
+
SCAN_BATCH_ROWS
|
|
21290
|
+
]);
|
|
21291
|
+
for (const r of rows) {
|
|
21292
|
+
yield {
|
|
21293
|
+
id: r.id,
|
|
21294
|
+
ruleId: r.rule_id,
|
|
21295
|
+
category: r.category,
|
|
21296
|
+
severity: r.severity,
|
|
21297
|
+
maskedMatch: r.masked_match,
|
|
21298
|
+
actionTaken: r.action_taken,
|
|
21299
|
+
confidence: r.confidence,
|
|
21300
|
+
occurredAt: epochMillisToIso(r.occurred_at),
|
|
21301
|
+
sourceTool: r.source_tool,
|
|
21302
|
+
repo: r.repo ?? "",
|
|
21303
|
+
file: r.file ?? "",
|
|
21304
|
+
...r.tool_name === null ? {} : { toolName: r.tool_name },
|
|
21305
|
+
eventId: r.event_id,
|
|
21306
|
+
...r.session_id === null ? {} : { sessionId: r.session_id },
|
|
21307
|
+
status: deriveInstanceStatus(r)
|
|
21308
|
+
};
|
|
21309
|
+
}
|
|
21310
|
+
if (rows.length < SCAN_BATCH_ROWS) return;
|
|
21311
|
+
const lastRow = rows[rows.length - 1];
|
|
21312
|
+
if (lastRow === void 0) return;
|
|
21313
|
+
after = { startedAtMs: lastRow.occurred_at, id: lastRow.id };
|
|
21314
|
+
}
|
|
21315
|
+
}
|
|
21316
|
+
groupAggregates(withSearchText, scope) {
|
|
21317
|
+
const innerSearchColumns = withSearchText ? `, group_concat(DISTINCT json_extract(e.attributes, '$.repo')) AS repos,
|
|
21318
|
+
group_concat(DISTINCT json_extract(e.attributes, '$.file_path')) AS files,
|
|
21319
|
+
group_concat(DISTINCT 'via ' || json_extract(e.attributes, '$.tool_name')) AS tool_names` : `, NULL AS repos, NULL AS files, NULL AS tool_names`;
|
|
21320
|
+
const rows = this.db.prepare(
|
|
21321
|
+
`SELECT rule_id,
|
|
21322
|
+
sum(tuple_count) AS instance_count,
|
|
21323
|
+
max(latest_at) AS latest_at,
|
|
20526
21324
|
group_concat(source_tools) AS source_tools,
|
|
20527
21325
|
group_concat(actions_taken) AS actions_taken,
|
|
20528
21326
|
group_concat(status_tuple || '${TUPLE_SEP}' || tuple_count) AS status_inputs,
|
|
@@ -20775,7 +21573,7 @@ var SqliteInspectionFindingsRepository = class {
|
|
|
20775
21573
|
};
|
|
20776
21574
|
|
|
20777
21575
|
// ../../packages/persistence/src/repositories/installed-packs.ts
|
|
20778
|
-
import { createHash as createHash2, randomUUID as
|
|
21576
|
+
import { createHash as createHash2, randomUUID as randomUUID3 } from "crypto";
|
|
20779
21577
|
|
|
20780
21578
|
// ../../packages/persistence/src/semver.ts
|
|
20781
21579
|
function parse3(version2) {
|
|
@@ -20926,7 +21724,7 @@ var SqliteInstalledPacksRepository = class {
|
|
|
20926
21724
|
let behind = false;
|
|
20927
21725
|
for (const row of rows) {
|
|
20928
21726
|
const params = {
|
|
20929
|
-
id:
|
|
21727
|
+
id: randomUUID3(),
|
|
20930
21728
|
namespace: row.namespace,
|
|
20931
21729
|
packId: row.packId,
|
|
20932
21730
|
version: row.version,
|
|
@@ -20938,7 +21736,7 @@ var SqliteInstalledPacksRepository = class {
|
|
|
20938
21736
|
if (stored === void 0 || !isMirrorDowngrade(row, stored)) {
|
|
20939
21737
|
this.upsertAvailableStmt.run({
|
|
20940
21738
|
...params,
|
|
20941
|
-
id:
|
|
21739
|
+
id: randomUUID3(),
|
|
20942
21740
|
recordedBy: meta3?.recordedBy ?? null
|
|
20943
21741
|
});
|
|
20944
21742
|
} else {
|
|
@@ -21261,14 +22059,15 @@ var SqliteInventoryRepository = class {
|
|
|
21261
22059
|
};
|
|
21262
22060
|
|
|
21263
22061
|
// ../../packages/persistence/src/repositories/inventory-assets.ts
|
|
21264
|
-
import { randomUUID as
|
|
22062
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
21265
22063
|
var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
|
|
21266
22064
|
var VALID_HARNESS_IDS = new Set(HarnessId.options);
|
|
21267
22065
|
var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
|
|
21268
22066
|
var HARNESS_LABELS = {
|
|
21269
22067
|
claudecode: "Claude Code",
|
|
21270
22068
|
cursor: "Cursor",
|
|
21271
|
-
codex: "Codex"
|
|
22069
|
+
codex: "Codex",
|
|
22070
|
+
antigravity: "Antigravity"
|
|
21272
22071
|
};
|
|
21273
22072
|
var HARNESS_LIVENESS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
21274
22073
|
var EMPTY_PROJECT_AGG = {
|
|
@@ -21283,6 +22082,7 @@ function resolveHarnessId(attrs, row) {
|
|
|
21283
22082
|
if (t.includes("claudecode") || t === "claude") return "claudecode";
|
|
21284
22083
|
if (t.includes("cursor")) return "cursor";
|
|
21285
22084
|
if (t.includes("codex")) return "codex";
|
|
22085
|
+
if (t.includes("antigravity")) return "antigravity";
|
|
21286
22086
|
return null;
|
|
21287
22087
|
}
|
|
21288
22088
|
function isLiveRealClaudeCode(rows) {
|
|
@@ -21741,7 +22541,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
21741
22541
|
`INSERT INTO file_access_override (id, project_id, path, access, created_at, updated_at)
|
|
21742
22542
|
VALUES (:id, :projectId, :path, :access, :now, :now)
|
|
21743
22543
|
ON CONFLICT (project_id, path) DO UPDATE SET access = excluded.access, updated_at = excluded.updated_at`
|
|
21744
|
-
).run({ id:
|
|
22544
|
+
).run({ id: randomUUID4(), projectId, path, access, now: Date.now() });
|
|
21745
22545
|
}
|
|
21746
22546
|
return true;
|
|
21747
22547
|
}
|
|
@@ -21762,7 +22562,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
21762
22562
|
`INSERT INTO mcp_trust_override (id, asset_id, trust, created_at, updated_at)
|
|
21763
22563
|
VALUES (:id, :assetId, :trust, :now, :now)
|
|
21764
22564
|
ON CONFLICT (asset_id) DO UPDATE SET trust = excluded.trust, updated_at = excluded.updated_at`
|
|
21765
|
-
).run({ id:
|
|
22565
|
+
).run({ id: randomUUID4(), assetId, trust, now: Date.now() });
|
|
21766
22566
|
}
|
|
21767
22567
|
this.configRowsCache = void 0;
|
|
21768
22568
|
return "ok";
|
|
@@ -22059,7 +22859,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
22059
22859
|
};
|
|
22060
22860
|
|
|
22061
22861
|
// ../../packages/persistence/src/repositories/policies.ts
|
|
22062
|
-
import { randomUUID as
|
|
22862
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
22063
22863
|
var SqlitePoliciesRepository = class {
|
|
22064
22864
|
constructor(db) {
|
|
22065
22865
|
this.db = db;
|
|
@@ -22094,7 +22894,7 @@ var SqlitePoliciesRepository = class {
|
|
|
22094
22894
|
failOpenTransaction(this.db, () => {
|
|
22095
22895
|
for (const [category, action] of Object.entries(DEFAULT_ACTIONS)) {
|
|
22096
22896
|
stmt.run({
|
|
22097
|
-
id:
|
|
22897
|
+
id: randomUUID5(),
|
|
22098
22898
|
target: JSON.stringify({ category }),
|
|
22099
22899
|
action,
|
|
22100
22900
|
now: Date.now()
|
|
@@ -22114,7 +22914,7 @@ var SqlitePoliciesRepository = class {
|
|
|
22114
22914
|
`INSERT INTO policies (id, scope, target, action, enabled, created_at, updated_at)
|
|
22115
22915
|
VALUES (:id, 'global', :target, :action, 1, :now, :now)
|
|
22116
22916
|
ON CONFLICT(scope, target) DO UPDATE SET action = excluded.action, enabled = 1, updated_at = excluded.updated_at`
|
|
22117
|
-
).run({ id:
|
|
22917
|
+
).run({ id: randomUUID5(), target: JSON.stringify({ category }), action, now });
|
|
22118
22918
|
}
|
|
22119
22919
|
// Caps every global per-category policy currently set to block/redact down
|
|
22120
22920
|
// to warn (see warn-era-cap.ts). Rule-targeted policies are untouched.
|
|
@@ -22182,7 +22982,7 @@ var SqlitePolicyCatalogRepository = class {
|
|
|
22182
22982
|
};
|
|
22183
22983
|
|
|
22184
22984
|
// ../../packages/persistence/src/repositories/project-files.ts
|
|
22185
|
-
import { randomUUID as
|
|
22985
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
22186
22986
|
var SqliteProjectFilesRepository = class {
|
|
22187
22987
|
constructor(db) {
|
|
22188
22988
|
this.db = db;
|
|
@@ -22214,7 +23014,7 @@ var SqliteProjectFilesRepository = class {
|
|
|
22214
23014
|
const stamp = Math.max(now, maxStamp + 1);
|
|
22215
23015
|
for (const file2 of scan2.files) {
|
|
22216
23016
|
this.upsertStmt.run({
|
|
22217
|
-
id:
|
|
23017
|
+
id: randomUUID6(),
|
|
22218
23018
|
projectId,
|
|
22219
23019
|
path: file2.path,
|
|
22220
23020
|
name: file2.name,
|
|
@@ -22228,7 +23028,7 @@ var SqliteProjectFilesRepository = class {
|
|
|
22228
23028
|
};
|
|
22229
23029
|
|
|
22230
23030
|
// ../../packages/persistence/src/repositories/resolutions.ts
|
|
22231
|
-
import { randomUUID as
|
|
23031
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
22232
23032
|
var SqliteResolutionsRepository = class {
|
|
22233
23033
|
constructor(db, now = () => Date.now()) {
|
|
22234
23034
|
this.db = db;
|
|
@@ -22282,7 +23082,7 @@ var SqliteResolutionsRepository = class {
|
|
|
22282
23082
|
*/
|
|
22283
23083
|
insertResolution(r) {
|
|
22284
23084
|
this.insertStmt.run({
|
|
22285
|
-
id:
|
|
23085
|
+
id: randomUUID7(),
|
|
22286
23086
|
findingKey: r.findingKey,
|
|
22287
23087
|
status: FindingStatus.parse(r.status),
|
|
22288
23088
|
method: ResolutionMethod.parse(r.method),
|
|
@@ -22341,13 +23141,51 @@ var SqliteRuleProbeCacheRepository = class {
|
|
|
22341
23141
|
this.readStmt = db.prepare(
|
|
22342
23142
|
`SELECT verdict, worst_probe_ms AS worstProbeMs FROM rule_probe_cache WHERE rule_key = :ruleKey`
|
|
22343
23143
|
);
|
|
23144
|
+
this.countQuarantinedStmt = db.prepare(
|
|
23145
|
+
`SELECT COUNT(*) AS n FROM rule_probe_cache WHERE verdict = 'quarantined'`
|
|
23146
|
+
);
|
|
23147
|
+
this.clearQuarantinedStmt = db.prepare(
|
|
23148
|
+
`DELETE FROM rule_probe_cache WHERE verdict = 'quarantined'`
|
|
23149
|
+
);
|
|
22344
23150
|
}
|
|
22345
23151
|
db;
|
|
22346
23152
|
upsertStmt;
|
|
22347
23153
|
readStmt;
|
|
23154
|
+
countQuarantinedStmt;
|
|
23155
|
+
clearQuarantinedStmt;
|
|
22348
23156
|
getVerdict(ruleKey) {
|
|
22349
23157
|
return getRow(this.readStmt, { ruleKey });
|
|
22350
23158
|
}
|
|
23159
|
+
/** How many rules are currently excluded by a cached quarantine verdict. */
|
|
23160
|
+
countQuarantined() {
|
|
23161
|
+
return getRow(this.countQuarantinedStmt, {})?.n ?? 0;
|
|
23162
|
+
}
|
|
23163
|
+
/**
|
|
23164
|
+
* Forgets every quarantine verdict, so the rules behind them are measured
|
|
23165
|
+
* again on the next load. This is the undo for a verdict the machine reached
|
|
23166
|
+
* on its own: a rule terminated mid-scan is cached forever and dropped from
|
|
23167
|
+
* every later scan, and a timing verdict is a wall-clock judgement that a
|
|
23168
|
+
* loaded or slow machine can reach about a rule that is in fact fine.
|
|
23169
|
+
*
|
|
23170
|
+
* Only 'quarantined' rows go — a 'safe' verdict is a measurement worth
|
|
23171
|
+
* keeping, and dropping it would make every rule pay the battery again.
|
|
23172
|
+
*
|
|
23173
|
+
* Reports `refused` from the write's own result rather than inferring it from
|
|
23174
|
+
* the row count. The two are NOT the same answer: `failOpenTransaction`
|
|
23175
|
+
* swallows a contended DELETE (another writer holding the lock past
|
|
23176
|
+
* `busy_timeout` — reads are unaffected in WAL), and a swallowed refusal
|
|
23177
|
+
* leaves the count unchanged, which is indistinguishable from "there was
|
|
23178
|
+
* nothing to clear". An undo that reports success while the quarantines are
|
|
23179
|
+
* still in place is worse than one that fails, because the rules it claimed
|
|
23180
|
+
* to restore are silently still disabled.
|
|
23181
|
+
*/
|
|
23182
|
+
clearQuarantined() {
|
|
23183
|
+
const before = this.countQuarantined();
|
|
23184
|
+
const committed = failOpenTransaction(this.db, () => {
|
|
23185
|
+
this.clearQuarantinedStmt.run();
|
|
23186
|
+
});
|
|
23187
|
+
return { refused: !committed, cleared: before - this.countQuarantined() };
|
|
23188
|
+
}
|
|
22351
23189
|
setVerdict(ruleKey, verdict, worstProbeMs) {
|
|
22352
23190
|
failOpenTransaction(this.db, () => {
|
|
22353
23191
|
this.upsertStmt.run({ ruleKey, verdict, worstProbeMs, checkedAt: Date.now() });
|
|
@@ -22402,7 +23240,39 @@ var SqliteScanLedgerRepository = class {
|
|
|
22402
23240
|
};
|
|
22403
23241
|
|
|
22404
23242
|
// ../../packages/persistence/src/repositories/secret-vault.ts
|
|
22405
|
-
import { randomUUID as
|
|
23243
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
23244
|
+
function pageLimit(requested, fallback) {
|
|
23245
|
+
if (requested === void 0) return fallback;
|
|
23246
|
+
return Math.min(Math.max(1, Math.trunc(requested)), MAX_VAULT_PAGE_LIMIT);
|
|
23247
|
+
}
|
|
23248
|
+
function encodeReuseCursor(payload) {
|
|
23249
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
23250
|
+
}
|
|
23251
|
+
function decodeReuseCursor(cursor) {
|
|
23252
|
+
const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
23253
|
+
if (parsed !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
|
|
23254
|
+
// ±Infinity or a fraction binds cleanly and returns an EMPTY page with a
|
|
23255
|
+
// null cursor, which the caller reads as "end of list" — the one outcome a
|
|
23256
|
+
// malformed cursor must never produce, since restarting from the top is the
|
|
23257
|
+
// documented behaviour and the only recoverable one. (`1e999` is valid JSON
|
|
23258
|
+
// and parses to Infinity; a bare `NaN` is not, so it cannot arrive here.)
|
|
23259
|
+
Number.isInteger(parsed.occurrences) && typeof parsed.pointerId === "string") {
|
|
23260
|
+
return { occurrences: parsed.occurrences, pointerId: parsed.pointerId };
|
|
23261
|
+
}
|
|
23262
|
+
return null;
|
|
23263
|
+
}
|
|
23264
|
+
var REUSED_PREDICATE = `(v.occurrence_count > 1
|
|
23265
|
+
OR (SELECT count(*) FROM secret_vault_sighting s WHERE s.pointer_id = v.pointer_id) > 1)`;
|
|
23266
|
+
var INVENTORY_COLUMNS = `v.pointer_id, v.category, v.rule_id, v.masked_match, v.provider,
|
|
23267
|
+
v.occurrence_count, v.first_seen, v.last_seen`;
|
|
23268
|
+
function toSighting(row) {
|
|
23269
|
+
return {
|
|
23270
|
+
location: row.location,
|
|
23271
|
+
kind: row.kind,
|
|
23272
|
+
firstSeen: new Date(row.first_seen).toISOString(),
|
|
23273
|
+
lastSeen: new Date(row.last_seen).toISOString()
|
|
23274
|
+
};
|
|
23275
|
+
}
|
|
22406
23276
|
var SELECT_COLUMNS = `
|
|
22407
23277
|
pointer_id AS pointerId,
|
|
22408
23278
|
value_fingerprint AS valueFingerprint,
|
|
@@ -22586,39 +23456,67 @@ var SqliteSecretVaultRepository = class {
|
|
|
22586
23456
|
VALUES (:id, :pointerId, :location, :kind, :now, :now)
|
|
22587
23457
|
ON CONFLICT (pointer_id, location) DO UPDATE SET last_seen = :now`
|
|
22588
23458
|
).run({
|
|
22589
|
-
id:
|
|
23459
|
+
id: randomUUID8(),
|
|
22590
23460
|
pointerId: entry.pointerId,
|
|
22591
23461
|
location: entry.location,
|
|
22592
23462
|
kind: entry.kind,
|
|
22593
23463
|
now
|
|
22594
23464
|
});
|
|
22595
23465
|
}
|
|
22596
|
-
|
|
23466
|
+
/**
|
|
23467
|
+
* Sightings for a whole page of pointers, in ONE query grouped in JS rather
|
|
23468
|
+
* than one query per row. A pointer with no sightings still gets an entry, so
|
|
23469
|
+
* the caller never has to distinguish "none" from "missing".
|
|
23470
|
+
*
|
|
23471
|
+
* The `IN` list is sized to the page, so this statement cannot be cached on
|
|
23472
|
+
* the instance the way the fixed-shape ones in the constructor are.
|
|
23473
|
+
*/
|
|
23474
|
+
sightingsFor(pointerIds) {
|
|
23475
|
+
const byPointer = new Map(pointerIds.map((id) => [id, []]));
|
|
23476
|
+
if (pointerIds.length === 0) return byPointer;
|
|
22597
23477
|
const rows = allRows(
|
|
22598
23478
|
this.db.prepare(
|
|
22599
|
-
`SELECT location, kind, first_seen, last_seen
|
|
22600
|
-
|
|
23479
|
+
`SELECT pointer_id, location, kind, first_seen, last_seen
|
|
23480
|
+
FROM secret_vault_sighting
|
|
23481
|
+
WHERE pointer_id IN (${placeholders(pointerIds.length)})
|
|
23482
|
+
ORDER BY last_seen DESC`
|
|
22601
23483
|
),
|
|
22602
|
-
|
|
23484
|
+
pointerIds
|
|
22603
23485
|
);
|
|
23486
|
+
for (const row of rows) byPointer.get(row.pointer_id)?.push(toSighting(row));
|
|
23487
|
+
return byPointer;
|
|
23488
|
+
}
|
|
23489
|
+
/** Hydrate a page of raw inventory rows with their sightings, batched. */
|
|
23490
|
+
toInventoryEntries(rows) {
|
|
23491
|
+
const sightings = this.sightingsFor(rows.map((r) => r.pointer_id));
|
|
22604
23492
|
return rows.map((r) => ({
|
|
22605
|
-
|
|
22606
|
-
|
|
23493
|
+
pointerId: r.pointer_id,
|
|
23494
|
+
category: r.category,
|
|
23495
|
+
...r.provider === null ? {} : { provider: r.provider },
|
|
23496
|
+
maskedMatch: r.masked_match,
|
|
23497
|
+
occurrences: r.occurrence_count,
|
|
22607
23498
|
firstSeen: new Date(r.first_seen).toISOString(),
|
|
22608
|
-
lastSeen: new Date(r.last_seen).toISOString()
|
|
23499
|
+
lastSeen: new Date(r.last_seen).toISOString(),
|
|
23500
|
+
revealGrantId: r.grant_id,
|
|
23501
|
+
sightings: sightings.get(r.pointer_id) ?? []
|
|
22609
23502
|
}));
|
|
22610
23503
|
}
|
|
22611
23504
|
/**
|
|
22612
|
-
* The dashboard inventory
|
|
22613
|
-
* its sightings and the active
|
|
22614
|
-
* Raw-free by construction — neither
|
|
22615
|
-
* columns are selected.
|
|
23505
|
+
* The dashboard inventory, newest-first, ONE PAGE at a time: every vaulted
|
|
23506
|
+
* value's descriptor data joined with its sightings and the active
|
|
23507
|
+
* reveal-to-model grant when one exists. Raw-free by construction — neither
|
|
23508
|
+
* the fingerprint nor the ciphertext columns are selected.
|
|
23509
|
+
*
|
|
23510
|
+
* `totals.values` counts the whole store, not the page, so the count a reader
|
|
23511
|
+
* sees never depends on how far they have paged.
|
|
22616
23512
|
*/
|
|
22617
|
-
listInventory(now = Date.now()) {
|
|
23513
|
+
listInventory(query = {}, now = Date.now()) {
|
|
23514
|
+
const limit = pageLimit(query.limit, DEFAULT_VAULT_INVENTORY_LIMIT);
|
|
23515
|
+
const cursor = query.cursor === void 0 ? null : decodeKeysetCursor(query.cursor);
|
|
23516
|
+
const where = cursor === null ? "" : "WHERE (v.last_seen < :cursorLastSeen OR (v.last_seen = :cursorLastSeen AND v.pointer_id < :cursorPointerId))";
|
|
22618
23517
|
const rows = allRows(
|
|
22619
23518
|
this.db.prepare(
|
|
22620
|
-
`SELECT
|
|
22621
|
-
v.occurrence_count, v.first_seen, v.last_seen,
|
|
23519
|
+
`SELECT ${INVENTORY_COLUMNS},
|
|
22622
23520
|
(SELECT e.id FROM exceptions e
|
|
22623
23521
|
WHERE e.rule_id = v.rule_id
|
|
22624
23522
|
AND e.value_fingerprint = v.value_fingerprint
|
|
@@ -22626,45 +23524,109 @@ var SqliteSecretVaultRepository = class {
|
|
|
22626
23524
|
AND ${ACTIVE_REVEAL_GRANT_PREDICATE}
|
|
22627
23525
|
LIMIT 1) AS grant_id
|
|
22628
23526
|
FROM secret_vault v
|
|
22629
|
-
|
|
23527
|
+
${where}
|
|
23528
|
+
ORDER BY v.last_seen DESC, v.pointer_id DESC
|
|
23529
|
+
LIMIT :limit`
|
|
22630
23530
|
),
|
|
22631
|
-
{
|
|
23531
|
+
bindParams({
|
|
23532
|
+
now,
|
|
23533
|
+
limit: limit + 1,
|
|
23534
|
+
...cursor === null ? {} : { cursorLastSeen: cursor.startedAtMs, cursorPointerId: cursor.id }
|
|
23535
|
+
})
|
|
22632
23536
|
);
|
|
22633
|
-
|
|
22634
|
-
|
|
22635
|
-
|
|
22636
|
-
|
|
22637
|
-
|
|
22638
|
-
|
|
22639
|
-
|
|
22640
|
-
|
|
22641
|
-
|
|
22642
|
-
|
|
22643
|
-
|
|
23537
|
+
const hasMore = rows.length > limit;
|
|
23538
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
23539
|
+
const last = page[page.length - 1];
|
|
23540
|
+
return {
|
|
23541
|
+
totals: { values: this.countEntries() },
|
|
23542
|
+
items: this.toInventoryEntries(page),
|
|
23543
|
+
// Minted from the last row of the PAGE, never the extra probe row.
|
|
23544
|
+
nextCursor: hasMore && last ? encodeKeysetCursor({ startedAtMs: last.last_seen, id: last.pointer_id }) : null
|
|
23545
|
+
};
|
|
23546
|
+
}
|
|
23547
|
+
/**
|
|
23548
|
+
* Values reused on this machine — detected more than once, or written to more
|
|
23549
|
+
* than one location — most-reused first, one page at a time.
|
|
23550
|
+
*
|
|
23551
|
+
* Its own read rather than a filter over an inventory page: reuse is a
|
|
23552
|
+
* property of the whole store, and deriving it from 50 newest rows would
|
|
23553
|
+
* under-report exactly the values a reader most needs to see.
|
|
23554
|
+
*/
|
|
23555
|
+
listReuse(query = {}, now = Date.now()) {
|
|
23556
|
+
const limit = pageLimit(query.limit, DEFAULT_VAULT_INVENTORY_LIMIT);
|
|
23557
|
+
const cursor = query.cursor === void 0 ? null : decodeReuseCursor(query.cursor);
|
|
23558
|
+
const after = cursor === null ? "" : `AND (v.occurrence_count < :cursorOccurrences
|
|
23559
|
+
OR (v.occurrence_count = :cursorOccurrences AND v.pointer_id < :cursorPointerId))`;
|
|
23560
|
+
const rows = allRows(
|
|
23561
|
+
this.db.prepare(
|
|
23562
|
+
`SELECT ${INVENTORY_COLUMNS},
|
|
23563
|
+
(SELECT e.id FROM exceptions e
|
|
23564
|
+
WHERE e.rule_id = v.rule_id
|
|
23565
|
+
AND e.value_fingerprint = v.value_fingerprint
|
|
23566
|
+
AND e.key_version = v.fingerprint_key_version
|
|
23567
|
+
AND ${ACTIVE_REVEAL_GRANT_PREDICATE}
|
|
23568
|
+
LIMIT 1) AS grant_id
|
|
23569
|
+
FROM secret_vault v
|
|
23570
|
+
WHERE ${REUSED_PREDICATE} ${after}
|
|
23571
|
+
ORDER BY v.occurrence_count DESC, v.pointer_id DESC
|
|
23572
|
+
LIMIT :limit`
|
|
23573
|
+
),
|
|
23574
|
+
bindParams({
|
|
23575
|
+
now,
|
|
23576
|
+
limit: limit + 1,
|
|
23577
|
+
...cursor === null ? {} : { cursorOccurrences: cursor.occurrences, cursorPointerId: cursor.pointerId }
|
|
23578
|
+
})
|
|
23579
|
+
);
|
|
23580
|
+
const hasMore = rows.length > limit;
|
|
23581
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
23582
|
+
const last = page[page.length - 1];
|
|
23583
|
+
return {
|
|
23584
|
+
totals: { reused: this.countReused() },
|
|
23585
|
+
items: this.toInventoryEntries(page),
|
|
23586
|
+
nextCursor: hasMore && last ? encodeReuseCursor({ occurrences: last.occurrence_count, pointerId: last.pointer_id }) : null
|
|
23587
|
+
};
|
|
22644
23588
|
}
|
|
22645
23589
|
/**
|
|
22646
|
-
* The de-reference trail, newest first. By default the
|
|
22647
|
-
* reasons (display, view-render) are hidden and counted
|
|
22648
|
-
* that matter as a signal are the model crossings, and
|
|
22649
|
-
* render noise would defeat the audit's purpose.
|
|
23590
|
+
* The de-reference trail, newest first, one page at a time. By default the
|
|
23591
|
+
* batched, high-volume reasons (display, view-render) are hidden and counted
|
|
23592
|
+
* instead — the rows that matter as a signal are the model crossings, and
|
|
23593
|
+
* burying them under render noise would defeat the audit's purpose.
|
|
23594
|
+
*
|
|
23595
|
+
* `hiddenBatched` counts the whole trail rather than the page: it is what the
|
|
23596
|
+
* view's "N hidden" line and its toggle speak for, so it must not shrink as
|
|
23597
|
+
* the reader pages.
|
|
22650
23598
|
*/
|
|
22651
|
-
listDerefs(
|
|
22652
|
-
const limit =
|
|
22653
|
-
const
|
|
23599
|
+
listDerefs(query = {}) {
|
|
23600
|
+
const limit = pageLimit(query.limit, DEFAULT_VAULT_DEREFS_LIMIT);
|
|
23601
|
+
const cursor = query.cursor === void 0 ? null : decodeKeysetCursor(query.cursor);
|
|
23602
|
+
const conditions = [];
|
|
23603
|
+
if (query.includeBatched !== true) {
|
|
23604
|
+
conditions.push(`reason NOT IN ('display', 'view-render')`);
|
|
23605
|
+
}
|
|
23606
|
+
if (cursor !== null) {
|
|
23607
|
+
conditions.push("(at < :cursorAt OR (at = :cursorAt AND id < :cursorId))");
|
|
23608
|
+
}
|
|
23609
|
+
const where = conditions.length === 0 ? "" : `WHERE ${conditions.join(" AND ")}`;
|
|
22654
23610
|
const rows = allRows(
|
|
22655
23611
|
this.db.prepare(
|
|
22656
23612
|
`SELECT id, pointer_id, at, target, reason, outcome, grant_id, pointer_count
|
|
22657
23613
|
FROM secret_vault_deref ${where}
|
|
22658
|
-
ORDER BY at DESC,
|
|
23614
|
+
ORDER BY at DESC, id DESC LIMIT :limit`
|
|
22659
23615
|
),
|
|
22660
|
-
{
|
|
23616
|
+
bindParams({
|
|
23617
|
+
limit: limit + 1,
|
|
23618
|
+
...cursor === null ? {} : { cursorAt: cursor.startedAtMs, cursorId: cursor.id }
|
|
23619
|
+
})
|
|
22661
23620
|
);
|
|
22662
|
-
const
|
|
23621
|
+
const hasMore = rows.length > limit;
|
|
23622
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
23623
|
+
const last = page[page.length - 1];
|
|
23624
|
+
const hiddenBatched = query.includeBatched === true ? 0 : countScalar(
|
|
22663
23625
|
this.db,
|
|
22664
23626
|
`SELECT count(*) AS n FROM secret_vault_deref WHERE reason IN ('display', 'view-render')`
|
|
22665
23627
|
);
|
|
22666
23628
|
return {
|
|
22667
|
-
|
|
23629
|
+
items: page.map((r) => ({
|
|
22668
23630
|
id: r.id,
|
|
22669
23631
|
pointerId: r.pointer_id,
|
|
22670
23632
|
at: new Date(r.at).toISOString(),
|
|
@@ -22674,12 +23636,20 @@ var SqliteSecretVaultRepository = class {
|
|
|
22674
23636
|
...r.grant_id === null ? {} : { grantId: r.grant_id },
|
|
22675
23637
|
pointerCount: r.pointer_count
|
|
22676
23638
|
})),
|
|
23639
|
+
nextCursor: hasMore && last ? encodeKeysetCursor({ startedAtMs: last.at, id: last.id }) : null,
|
|
22677
23640
|
hiddenBatched
|
|
22678
23641
|
};
|
|
22679
23642
|
}
|
|
22680
23643
|
countEntries() {
|
|
22681
23644
|
return countScalar(this.db, "SELECT COUNT(*) AS n FROM secret_vault");
|
|
22682
23645
|
}
|
|
23646
|
+
/** Values reused on this machine — the reuse list's page-independent total. */
|
|
23647
|
+
countReused() {
|
|
23648
|
+
return countScalar(
|
|
23649
|
+
this.db,
|
|
23650
|
+
`SELECT COUNT(*) AS n FROM secret_vault v WHERE ${REUSED_PREDICATE}`
|
|
23651
|
+
);
|
|
23652
|
+
}
|
|
22683
23653
|
};
|
|
22684
23654
|
|
|
22685
23655
|
// ../../packages/persistence/src/repositories/security.ts
|
|
@@ -22694,7 +23664,9 @@ var ENFORCEMENT_KINDS = ["blocked", "redacted", "warned"];
|
|
|
22694
23664
|
var SCAN_COVERAGE = [
|
|
22695
23665
|
{ provider: "claudecode", coverage: 100, supported: true },
|
|
22696
23666
|
{ provider: "cursor", coverage: 0, supported: false },
|
|
22697
|
-
{ provider: "codex", coverage:
|
|
23667
|
+
{ provider: "codex", coverage: 80, supported: true },
|
|
23668
|
+
{ provider: "antigravity", coverage: 60, supported: true },
|
|
23669
|
+
{ provider: "claudeai", coverage: 0, supported: false },
|
|
22698
23670
|
{ provider: "chatgpt", coverage: 0, supported: false },
|
|
22699
23671
|
{ provider: "copilot", coverage: 0, supported: false },
|
|
22700
23672
|
{ provider: "api", coverage: 0, supported: false }
|
|
@@ -23027,7 +23999,7 @@ var SqliteSecurityRepository = class {
|
|
|
23027
23999
|
};
|
|
23028
24000
|
|
|
23029
24001
|
// ../../packages/persistence/src/repositories/shares.ts
|
|
23030
|
-
import { randomUUID as
|
|
24002
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
23031
24003
|
var MAX_EGRESS_CALL_SITES_PER_PROJECT = 5e3;
|
|
23032
24004
|
var IN_CHUNK = 500;
|
|
23033
24005
|
var KIND_ORDER = ["provider", "internal", "external", "ip"];
|
|
@@ -23283,7 +24255,7 @@ var SqliteSharesRepository = class {
|
|
|
23283
24255
|
(id, destination_id, host, decision, created_at, updated_at)
|
|
23284
24256
|
VALUES (:id, :destinationId, :host, :decision, :now, :now)`
|
|
23285
24257
|
).run({
|
|
23286
|
-
id:
|
|
24258
|
+
id: randomUUID9(),
|
|
23287
24259
|
destinationId,
|
|
23288
24260
|
host: dest.host,
|
|
23289
24261
|
decision,
|
|
@@ -23432,7 +24404,7 @@ var SqliteSharesRepository = class {
|
|
|
23432
24404
|
let destinationId = destIds.get(hit.host);
|
|
23433
24405
|
if (destinationId === void 0) {
|
|
23434
24406
|
destStmt.run({
|
|
23435
|
-
id:
|
|
24407
|
+
id: randomUUID9(),
|
|
23436
24408
|
kind: hit.kind,
|
|
23437
24409
|
name: hit.name,
|
|
23438
24410
|
host: hit.host,
|
|
@@ -23448,7 +24420,7 @@ var SqliteSharesRepository = class {
|
|
|
23448
24420
|
let endpointId = endpointIds.get(endpointKey);
|
|
23449
24421
|
if (endpointId === void 0) {
|
|
23450
24422
|
endpointStmt.run({
|
|
23451
|
-
id:
|
|
24423
|
+
id: randomUUID9(),
|
|
23452
24424
|
destinationId,
|
|
23453
24425
|
method: hit.method,
|
|
23454
24426
|
transport: hit.transport,
|
|
@@ -23461,7 +24433,7 @@ var SqliteSharesRepository = class {
|
|
|
23461
24433
|
endpointIds.set(endpointKey, endpointId);
|
|
23462
24434
|
}
|
|
23463
24435
|
siteStmt.run({
|
|
23464
|
-
id:
|
|
24436
|
+
id: randomUUID9(),
|
|
23465
24437
|
endpointId,
|
|
23466
24438
|
project: input.project,
|
|
23467
24439
|
projectKey: input.projectKey,
|
|
@@ -23826,6 +24798,9 @@ function purgeSampleData(db) {
|
|
|
23826
24798
|
}
|
|
23827
24799
|
|
|
23828
24800
|
// ../../packages/persistence/src/database.ts
|
|
24801
|
+
var UNSAFE_TEST_ONLY_RAW_HANDLE = /* @__PURE__ */ Symbol(
|
|
24802
|
+
"aka.persistence.unsafeTestOnlyRawHandle"
|
|
24803
|
+
);
|
|
23829
24804
|
function linkHost(input, hostId) {
|
|
23830
24805
|
return hostId ? { ...input, hostId } : input;
|
|
23831
24806
|
}
|
|
@@ -23847,21 +24822,34 @@ function openWithPragmas(file2) {
|
|
|
23847
24822
|
}
|
|
23848
24823
|
return db;
|
|
23849
24824
|
}
|
|
23850
|
-
function backupLegacyStore(file2) {
|
|
23851
|
-
|
|
23852
|
-
|
|
23853
|
-
|
|
23854
|
-
|
|
23855
|
-
|
|
24825
|
+
function backupLegacyStore(db, file2) {
|
|
24826
|
+
reapStalePartials(file2);
|
|
24827
|
+
const backup = backupPath(file2, "legacy");
|
|
24828
|
+
let snapshotted = false;
|
|
24829
|
+
let snapshotError;
|
|
24830
|
+
try {
|
|
24831
|
+
snapshotStore(db, backup);
|
|
24832
|
+
snapshotted = true;
|
|
24833
|
+
} catch (error51) {
|
|
24834
|
+
snapshotError = error51;
|
|
24835
|
+
} finally {
|
|
24836
|
+
db.close();
|
|
24837
|
+
}
|
|
24838
|
+
if (!snapshotted) {
|
|
24839
|
+
akaWarn(
|
|
24840
|
+
`Could not snapshot the incompatible ${DB_FILENAME} (${String(snapshotError)}); moving the store aside with its sidecars instead.`
|
|
24841
|
+
);
|
|
24842
|
+
moveStoreAside(file2, backup);
|
|
24843
|
+
return backup;
|
|
23856
24844
|
}
|
|
24845
|
+
discardStore(file2, backup);
|
|
23857
24846
|
return backup;
|
|
23858
24847
|
}
|
|
23859
24848
|
function openAndInitialize(file2) {
|
|
23860
24849
|
let db = openWithPragmas(file2);
|
|
23861
24850
|
try {
|
|
23862
24851
|
if (isForeignSqliteLineage(db)) {
|
|
23863
|
-
db
|
|
23864
|
-
const backup = backupLegacyStore(file2);
|
|
24852
|
+
const backup = backupLegacyStore(db, file2);
|
|
23865
24853
|
db = openWithPragmas(file2);
|
|
23866
24854
|
akaWarn(
|
|
23867
24855
|
`Detected an older, incompatible (tenant-bearing) ${DB_FILENAME}; backed it up to ${backup} and created a fresh store.`
|
|
@@ -23905,7 +24893,7 @@ function openAndInitialize(file2) {
|
|
|
23905
24893
|
}
|
|
23906
24894
|
function openLocalDatabase(dir) {
|
|
23907
24895
|
ensureDataDirSync(dir);
|
|
23908
|
-
const file2 =
|
|
24896
|
+
const file2 = join2(dir, DB_FILENAME);
|
|
23909
24897
|
const {
|
|
23910
24898
|
db,
|
|
23911
24899
|
events,
|
|
@@ -24022,7 +25010,7 @@ function openLocalDatabase(dir) {
|
|
|
24022
25010
|
const definitionId = definitionIds.get(`${finding.ruleId}@${finding.version}`);
|
|
24023
25011
|
if (!definitionId) continue;
|
|
24024
25012
|
inspectionFindings.insertFinding({
|
|
24025
|
-
id:
|
|
25013
|
+
id: randomUUID10(),
|
|
24026
25014
|
auditEventId: record2.scanEvent.id,
|
|
24027
25015
|
inspectionDefinitionId: definitionId,
|
|
24028
25016
|
span: finding.span,
|
|
@@ -24128,35 +25116,51 @@ function openLocalDatabase(dir) {
|
|
|
24128
25116
|
transaction,
|
|
24129
25117
|
close: () => {
|
|
24130
25118
|
db.close();
|
|
24131
|
-
}
|
|
25119
|
+
},
|
|
25120
|
+
// Last, and a plain value rather than a getter, so `{ ...db }` carries it.
|
|
25121
|
+
[UNSAFE_TEST_ONLY_RAW_HANDLE]: db
|
|
24132
25122
|
};
|
|
24133
25123
|
}
|
|
24134
25124
|
|
|
25125
|
+
// ../../packages/persistence/src/file-lock.ts
|
|
25126
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
25127
|
+
import {
|
|
25128
|
+
closeSync,
|
|
25129
|
+
existsSync as existsSync2,
|
|
25130
|
+
openSync,
|
|
25131
|
+
readFileSync,
|
|
25132
|
+
rmSync as rmSync3,
|
|
25133
|
+
statSync as statSync2,
|
|
25134
|
+
writeFileSync as writeFileSync2
|
|
25135
|
+
} from "fs";
|
|
25136
|
+
import { hostname as hostname3 } from "os";
|
|
25137
|
+
var PARK = new Int32Array(new SharedArrayBuffer(4));
|
|
25138
|
+
|
|
24135
25139
|
// ../../packages/persistence/src/finding-key.ts
|
|
24136
25140
|
import { createHash as createHash3 } from "crypto";
|
|
24137
25141
|
|
|
24138
25142
|
// ../../packages/persistence/src/fingerprint.ts
|
|
24139
25143
|
import { createHmac, randomBytes } from "crypto";
|
|
24140
|
-
import { existsSync as
|
|
24141
|
-
import { join as
|
|
25144
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
25145
|
+
import { join as join3 } from "path";
|
|
24142
25146
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
24143
25147
|
|
|
24144
25148
|
// ../../packages/persistence/src/local-layout.ts
|
|
24145
25149
|
import { renameSync as renameSync3 } from "fs";
|
|
24146
25150
|
import { mkdir } from "fs/promises";
|
|
24147
25151
|
import { homedir } from "os";
|
|
24148
|
-
import { join as
|
|
25152
|
+
import { join as join4 } from "path";
|
|
24149
25153
|
function defaultDataDir() {
|
|
24150
|
-
return
|
|
25154
|
+
return join4(homedir(), ".aka");
|
|
24151
25155
|
}
|
|
24152
25156
|
function settingsDir(base = defaultDataDir()) {
|
|
24153
|
-
return
|
|
25157
|
+
return join4(base, "settings");
|
|
24154
25158
|
}
|
|
24155
25159
|
function dataDir(base = defaultDataDir()) {
|
|
24156
|
-
return
|
|
25160
|
+
return join4(base, "data");
|
|
24157
25161
|
}
|
|
24158
25162
|
function dbPath(base = defaultDataDir()) {
|
|
24159
|
-
return
|
|
25163
|
+
return join4(dataDir(base), "aka.db");
|
|
24160
25164
|
}
|
|
24161
25165
|
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
24162
25166
|
ensureDataDirSync(dir);
|
|
@@ -24169,8 +25173,8 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
24169
25173
|
for (const { name, dest } of moves) {
|
|
24170
25174
|
try {
|
|
24171
25175
|
ensureDataDirSync(dest);
|
|
24172
|
-
const moved =
|
|
24173
|
-
renameSync3(
|
|
25176
|
+
const moved = join4(dest, name);
|
|
25177
|
+
renameSync3(join4(base, name), moved);
|
|
24174
25178
|
tightenFile(moved);
|
|
24175
25179
|
} catch {
|
|
24176
25180
|
}
|
|
@@ -24178,10 +25182,11 @@ function migrateLegacyLayout(base = defaultDataDir()) {
|
|
|
24178
25182
|
}
|
|
24179
25183
|
|
|
24180
25184
|
// ../../packages/persistence/src/settings.ts
|
|
24181
|
-
import { readFileSync as
|
|
24182
|
-
import { join as
|
|
25185
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
25186
|
+
import { join as join5 } from "path";
|
|
25187
|
+
var SETTINGS_FILENAME = "settings.json";
|
|
24183
25188
|
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
24184
|
-
const record2 = readJson(
|
|
25189
|
+
const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
|
|
24185
25190
|
if (!record2) return defaultWorkspaceSettings();
|
|
24186
25191
|
try {
|
|
24187
25192
|
return WorkspaceSettings.parse(record2);
|
|
@@ -24192,7 +25197,7 @@ function readWorkspaceSettings(base = defaultDataDir()) {
|
|
|
24192
25197
|
function readJson(file2) {
|
|
24193
25198
|
let text;
|
|
24194
25199
|
try {
|
|
24195
|
-
text =
|
|
25200
|
+
text = readFileSync3(file2, "utf8");
|
|
24196
25201
|
} catch {
|
|
24197
25202
|
return null;
|
|
24198
25203
|
}
|
|
@@ -24214,24 +25219,24 @@ import { randomBytes as randomBytes2 } from "crypto";
|
|
|
24214
25219
|
import {
|
|
24215
25220
|
chmodSync as chmodSync2,
|
|
24216
25221
|
mkdirSync as mkdirSync2,
|
|
24217
|
-
readFileSync as
|
|
25222
|
+
readFileSync as readFileSync4,
|
|
24218
25223
|
renameSync as renameSync4,
|
|
24219
|
-
rmSync as
|
|
24220
|
-
statSync,
|
|
24221
|
-
writeFileSync as
|
|
25224
|
+
rmSync as rmSync4,
|
|
25225
|
+
statSync as statSync3,
|
|
25226
|
+
writeFileSync as writeFileSync3
|
|
24222
25227
|
} from "fs";
|
|
24223
|
-
import { join as
|
|
25228
|
+
import { join as join6 } from "path";
|
|
24224
25229
|
|
|
24225
25230
|
// ../../packages/persistence/src/vault/vault.ts
|
|
24226
|
-
import { randomBytes as randomBytes3, randomUUID as
|
|
25231
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
|
|
24227
25232
|
|
|
24228
25233
|
// ../../packages/persistence/src/warn-era-cap.ts
|
|
24229
|
-
import { existsSync as
|
|
24230
|
-
import { join as
|
|
25234
|
+
import { existsSync as existsSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
25235
|
+
import { join as join7 } from "path";
|
|
24231
25236
|
|
|
24232
25237
|
// ../../packages/plugin-sdk/src/config.ts
|
|
24233
|
-
import { existsSync as
|
|
24234
|
-
import { join as
|
|
25238
|
+
import { existsSync as existsSync5 } from "fs";
|
|
25239
|
+
import { join as join8 } from "path";
|
|
24235
25240
|
|
|
24236
25241
|
// ../../packages/plugin-sdk/src/provider-env.ts
|
|
24237
25242
|
var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
|
|
@@ -24282,11 +25287,11 @@ function resolveProvider() {
|
|
|
24282
25287
|
}
|
|
24283
25288
|
|
|
24284
25289
|
// ../../packages/plugin-sdk/src/config.ts
|
|
24285
|
-
function loadConfig(base = defaultDataDir()) {
|
|
25290
|
+
function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
|
|
24286
25291
|
try {
|
|
24287
25292
|
ensureLayoutDirSync(base);
|
|
24288
|
-
const settingsFile =
|
|
24289
|
-
if (
|
|
25293
|
+
const settingsFile = join8(settingsDir(base), "settings.json");
|
|
25294
|
+
if (existsSync5(settingsFile)) tightenFile(settingsFile);
|
|
24290
25295
|
} catch {
|
|
24291
25296
|
}
|
|
24292
25297
|
migrateLegacyLayout(base);
|
|
@@ -24297,21 +25302,21 @@ function loadConfig(base = defaultDataDir()) {
|
|
|
24297
25302
|
dbPath: dbPath(base),
|
|
24298
25303
|
settingsDir: settingsDir(base),
|
|
24299
25304
|
onboarded: settings.onboardedAt != null,
|
|
24300
|
-
provider: resolveProviderSafe()
|
|
25305
|
+
provider: resolveProviderSafe(resolveProviderFn)
|
|
24301
25306
|
};
|
|
24302
25307
|
}
|
|
24303
|
-
function resolveProviderSafe() {
|
|
25308
|
+
function resolveProviderSafe(resolveProviderFn) {
|
|
24304
25309
|
try {
|
|
24305
|
-
return
|
|
25310
|
+
return resolveProviderFn();
|
|
24306
25311
|
} catch {
|
|
24307
25312
|
return { provider: "anthropic" };
|
|
24308
25313
|
}
|
|
24309
25314
|
}
|
|
24310
25315
|
|
|
24311
25316
|
// ../../packages/plugin-sdk/src/config-inventory.ts
|
|
24312
|
-
import { readdirSync, readFileSync as
|
|
25317
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync5 } from "fs";
|
|
24313
25318
|
import { homedir as homedir2 } from "os";
|
|
24314
|
-
import { basename as
|
|
25319
|
+
import { basename as basename3, join as join10 } from "path";
|
|
24315
25320
|
|
|
24316
25321
|
// ../../packages/detections/src/egress/registry.ts
|
|
24317
25322
|
var EXTRACTOR_VERSION = "1";
|
|
@@ -26887,7 +27892,7 @@ var gcp_service_account_default = {
|
|
|
26887
27892
|
severity: "critical",
|
|
26888
27893
|
matcher: {
|
|
26889
27894
|
type: "regex",
|
|
26890
|
-
pattern: "
|
|
27895
|
+
pattern: "(?<![a-z0-9-])[a-z0-9-]{3,}@[a-z0-9][a-z0-9-]{2,60}\\.iam\\.gserviceaccount\\.com\\b",
|
|
26891
27896
|
flags: "g"
|
|
26892
27897
|
},
|
|
26893
27898
|
examples: [
|
|
@@ -27299,22 +28304,27 @@ function maskText(text) {
|
|
|
27299
28304
|
}
|
|
27300
28305
|
|
|
27301
28306
|
// ../../packages/plugin-sdk/src/repo.ts
|
|
27302
|
-
import { existsSync as
|
|
27303
|
-
import { basename, dirname, isAbsolute, join as
|
|
28307
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
28308
|
+
import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
|
|
27304
28309
|
|
|
27305
28310
|
// ../../packages/plugin-sdk/src/events.ts
|
|
27306
|
-
import { createHash as createHash4, randomUUID as
|
|
28311
|
+
import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
|
|
28312
|
+
|
|
28313
|
+
// ../../packages/plugin-sdk/src/isolated-scan.ts
|
|
28314
|
+
import { existsSync as existsSync7 } from "fs";
|
|
28315
|
+
import { fileURLToPath } from "url";
|
|
28316
|
+
import { Worker } from "worker_threads";
|
|
27307
28317
|
|
|
27308
28318
|
// ../../packages/plugin-sdk/src/inventory-resolver.ts
|
|
27309
|
-
import { arch, hostname as
|
|
28319
|
+
import { arch, hostname as hostname4, platform, release } from "os";
|
|
27310
28320
|
|
|
27311
28321
|
// ../../packages/plugin-sdk/src/nudge.ts
|
|
27312
|
-
import { mkdirSync as mkdirSync3, readFileSync as
|
|
27313
|
-
import { join as
|
|
28322
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
28323
|
+
import { join as join11 } from "path";
|
|
27314
28324
|
|
|
27315
28325
|
// ../../packages/plugin-sdk/src/paths.ts
|
|
27316
|
-
import { readdirSync as
|
|
27317
|
-
import { basename as
|
|
28326
|
+
import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
|
|
28327
|
+
import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
|
|
27318
28328
|
|
|
27319
28329
|
// ../../packages/plugin-sdk/src/posture.ts
|
|
27320
28330
|
function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
|
|
@@ -27328,8 +28338,34 @@ function applyCategoryPosture(posture, repo, mode = "fill-gaps") {
|
|
|
27328
28338
|
|
|
27329
28339
|
// ../../packages/plugin-sdk/src/project-files.ts
|
|
27330
28340
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
27331
|
-
import { existsSync as
|
|
27332
|
-
import { basename as
|
|
28341
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
|
|
28342
|
+
import { basename as basename5, join as join12, relative, sep as sep4 } from "path";
|
|
28343
|
+
|
|
28344
|
+
// ../../packages/plugin-sdk/src/provider-env-antigravity.ts
|
|
28345
|
+
var optionalBaseUrl2 = external_exports.preprocess((v) => {
|
|
28346
|
+
if (typeof v === "string" && v.trim() === "") return void 0;
|
|
28347
|
+
return v;
|
|
28348
|
+
}, external_exports.string().optional()).catch(void 0);
|
|
28349
|
+
var optionalFlag = external_exports.preprocess((v) => {
|
|
28350
|
+
if (typeof v !== "string") return false;
|
|
28351
|
+
const normalized = v.trim().toLowerCase();
|
|
28352
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
28353
|
+
}, external_exports.boolean()).catch(false);
|
|
28354
|
+
var antigravityProviderEnvShape = {
|
|
28355
|
+
GOOGLE_GENAI_USE_VERTEXAI: optionalFlag,
|
|
28356
|
+
GOOGLE_GEMINI_BASE_URL: optionalBaseUrl2
|
|
28357
|
+
};
|
|
28358
|
+
var AntigravityProviderEnvSchema = external_exports.object(antigravityProviderEnvShape);
|
|
28359
|
+
|
|
28360
|
+
// ../../packages/plugin-sdk/src/provider-env-codex.ts
|
|
28361
|
+
var optionalBaseUrl3 = external_exports.preprocess((v) => {
|
|
28362
|
+
if (typeof v === "string" && v.trim() === "") return void 0;
|
|
28363
|
+
return v;
|
|
28364
|
+
}, external_exports.string().optional()).catch(void 0);
|
|
28365
|
+
var codexProviderEnvShape = {
|
|
28366
|
+
OPENAI_BASE_URL: optionalBaseUrl3
|
|
28367
|
+
};
|
|
28368
|
+
var CodexProviderEnvSchema = external_exports.object(codexProviderEnvShape);
|
|
27333
28369
|
|
|
27334
28370
|
// ../../packages/plugin-sdk/src/raw-egress.ts
|
|
27335
28371
|
var RawEgressError = class extends Error {
|
|
@@ -27380,7 +28416,7 @@ function assertRawFree(text, rawValues) {
|
|
|
27380
28416
|
}
|
|
27381
28417
|
|
|
27382
28418
|
// ../../packages/plugin-sdk/src/runtime.ts
|
|
27383
|
-
import { randomUUID as
|
|
28419
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
27384
28420
|
|
|
27385
28421
|
// ../../packages/plugin-sdk/src/suppressions.ts
|
|
27386
28422
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -27420,128 +28456,72 @@ async function applySetupTriageSuppressions(entries, writer, opts) {
|
|
|
27420
28456
|
}
|
|
27421
28457
|
|
|
27422
28458
|
// ../../packages/plugin-sdk/src/throttle.ts
|
|
27423
|
-
import { mkdirSync as mkdirSync4, statSync as
|
|
27424
|
-
import { join as
|
|
28459
|
+
import { mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
28460
|
+
import { join as join13 } from "path";
|
|
27425
28461
|
|
|
27426
|
-
// src/
|
|
27427
|
-
import {
|
|
27428
|
-
import {
|
|
27429
|
-
|
|
27430
|
-
|
|
27431
|
-
function
|
|
27432
|
-
|
|
28462
|
+
// ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
|
|
28463
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
28464
|
+
import { join as join14 } from "path";
|
|
28465
|
+
|
|
28466
|
+
// ../../packages/setup-wizard/src/triage/dedupe.ts
|
|
28467
|
+
function dedupeKey(hit) {
|
|
28468
|
+
if (hit.valueFingerprint === void 0) return void 0;
|
|
28469
|
+
return `${hit.ruleId}\u2588${hit.valueFingerprint}`;
|
|
27433
28470
|
}
|
|
27434
|
-
function
|
|
27435
|
-
const
|
|
27436
|
-
const
|
|
27437
|
-
|
|
27438
|
-
|
|
27439
|
-
|
|
27440
|
-
|
|
28471
|
+
function dedupeForJudge(hits) {
|
|
28472
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28473
|
+
const out = [];
|
|
28474
|
+
for (const h of hits) {
|
|
28475
|
+
const key = dedupeKey(h);
|
|
28476
|
+
if (key === void 0) {
|
|
28477
|
+
out.push(h);
|
|
28478
|
+
continue;
|
|
28479
|
+
}
|
|
28480
|
+
if (seen.has(key)) continue;
|
|
28481
|
+
seen.add(key);
|
|
28482
|
+
out.push(h);
|
|
27441
28483
|
}
|
|
27442
|
-
return
|
|
28484
|
+
return out;
|
|
27443
28485
|
}
|
|
27444
28486
|
|
|
27445
|
-
//
|
|
27446
|
-
|
|
27447
|
-
|
|
27448
|
-
|
|
27449
|
-
|
|
27450
|
-
|
|
27451
|
-
|
|
27452
|
-
|
|
27453
|
-
|
|
27454
|
-
|
|
27455
|
-
// src/setup-show.ts
|
|
27456
|
-
var SHOW_BEGIN = "<<<AKA_SHOW";
|
|
27457
|
-
var SHOW_END = "AKA_SHOW>>>";
|
|
27458
|
-
function showBlock(body) {
|
|
27459
|
-
return `${SHOW_BEGIN}
|
|
27460
|
-
${body}
|
|
27461
|
-
${SHOW_END}
|
|
27462
|
-
`;
|
|
27463
|
-
}
|
|
27464
|
-
|
|
27465
|
-
// src/present.ts
|
|
27466
|
-
var SHADE = {
|
|
27467
|
-
light: "\u2591",
|
|
27468
|
-
medium: "\u2592",
|
|
27469
|
-
dark: "\u2593",
|
|
27470
|
-
full: "\u2588"
|
|
27471
|
-
};
|
|
27472
|
-
var ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
27473
|
-
function visibleLength(text) {
|
|
27474
|
-
return text.replace(ANSI_RE, "").length;
|
|
27475
|
-
}
|
|
27476
|
-
var fg = (hex3) => (text) => {
|
|
27477
|
-
const r = Number.parseInt(hex3.slice(1, 3), 16);
|
|
27478
|
-
const g = Number.parseInt(hex3.slice(3, 5), 16);
|
|
27479
|
-
const b = Number.parseInt(hex3.slice(5, 7), 16);
|
|
27480
|
-
return `\x1B[38;2;${String(r)};${String(g)};${String(b)}m${text}\x1B[0m`;
|
|
27481
|
-
};
|
|
27482
|
-
var paint = {
|
|
27483
|
-
brand: fg("#33e6c6"),
|
|
27484
|
-
// --color-brand · ▸▸ AKA wordmark (accent text)
|
|
27485
|
-
dim: fg("#838995"),
|
|
27486
|
-
// --color-text-3 · separators · "/100" · the "unreviewed" label
|
|
27487
|
-
bold: (text) => `\x1B[1m${text}\x1B[0m`,
|
|
27488
|
-
// the health score number
|
|
27489
|
-
ok: fg("#0db15f"),
|
|
27490
|
-
// --color-ok · healthy ● dot
|
|
27491
|
-
critical: fg("#e63448"),
|
|
27492
|
-
// --color-sev-critical · ■ and the open-findings flag
|
|
27493
|
-
high: fg("#e97a0a"),
|
|
27494
|
-
// --color-sev-high · ■ and the mid-health dot
|
|
27495
|
-
medium: fg("#f7bd00"),
|
|
27496
|
-
// --color-sev-medium · ■
|
|
27497
|
-
low: fg("#0581d4")
|
|
27498
|
-
// --color-sev-low · ■ (azure blue, not purple)
|
|
27499
|
-
};
|
|
27500
|
-
function padEnd(text, width) {
|
|
27501
|
-
const pad = width - visibleLength(text);
|
|
27502
|
-
return pad > 0 ? text + " ".repeat(pad) : text;
|
|
27503
|
-
}
|
|
27504
|
-
function indent(text, spaces = 2) {
|
|
27505
|
-
const pad = " ".repeat(spaces);
|
|
27506
|
-
return text.split("\n").map((line) => pad + line).join("\n");
|
|
27507
|
-
}
|
|
27508
|
-
function table(headers, rows, opts = {}) {
|
|
27509
|
-
const gap = opts.gap ?? 3;
|
|
27510
|
-
const widths = headers.map(
|
|
27511
|
-
(h, i) => Math.max(visibleLength(h), ...rows.map((r) => visibleLength(r[i] ?? "")))
|
|
27512
|
-
);
|
|
27513
|
-
const sep5 = " ".repeat(gap);
|
|
27514
|
-
const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep5);
|
|
27515
|
-
const headerLine = fmt(headers.map((h) => h.toUpperCase()));
|
|
27516
|
-
if (opts.rowSep === true) {
|
|
27517
|
-
const fullWidth = widths.reduce((n, w) => n + w, 0) + gap * Math.max(0, widths.length - 1);
|
|
27518
|
-
const rule = "\u2500".repeat(fullWidth);
|
|
27519
|
-
const body = [];
|
|
27520
|
-
rows.forEach((row, i) => {
|
|
27521
|
-
if (i > 0) body.push(rule);
|
|
27522
|
-
body.push(fmt(row));
|
|
27523
|
-
});
|
|
27524
|
-
return [headerLine, rule, ...body].join("\n");
|
|
28487
|
+
// ../../packages/setup-wizard/src/triage/false-positive-patterns.ts
|
|
28488
|
+
function deriveFalsePositivePatterns(hits, rec, plan) {
|
|
28489
|
+
const hitById = new Map(hits.filter((h) => h.id !== void 0).map((h) => [h.id, h]));
|
|
28490
|
+
const seenCategory = /* @__PURE__ */ new Set();
|
|
28491
|
+
const markedIds = /* @__PURE__ */ new Set();
|
|
28492
|
+
for (const c of rec.perCategory) {
|
|
28493
|
+
if (seenCategory.has(c.category)) continue;
|
|
28494
|
+
seenCategory.add(c.category);
|
|
28495
|
+
if (plan.posture[c.category] === void 0) continue;
|
|
28496
|
+
for (const id of c.fpIds) markedIds.add(id);
|
|
27525
28497
|
}
|
|
27526
|
-
const
|
|
27527
|
-
|
|
27528
|
-
|
|
27529
|
-
|
|
27530
|
-
|
|
27531
|
-
|
|
27532
|
-
|
|
27533
|
-
|
|
27534
|
-
|
|
27535
|
-
|
|
28498
|
+
const groups = /* @__PURE__ */ new Map();
|
|
28499
|
+
for (const id of markedIds) {
|
|
28500
|
+
const h = hitById.get(id);
|
|
28501
|
+
if (h === void 0) continue;
|
|
28502
|
+
const pattern = safeMaskedMatch(h.rawMatch);
|
|
28503
|
+
const group = groups.get(pattern) ?? { count: 0, values: [] };
|
|
28504
|
+
group.count += 1;
|
|
28505
|
+
if (h.valueFingerprint !== void 0 && h.keyVersion !== void 0) {
|
|
28506
|
+
group.values.push({
|
|
28507
|
+
ruleId: h.ruleId,
|
|
28508
|
+
category: h.category,
|
|
28509
|
+
valueFingerprint: h.valueFingerprint,
|
|
28510
|
+
keyVersion: h.keyVersion
|
|
28511
|
+
});
|
|
28512
|
+
}
|
|
28513
|
+
groups.set(pattern, group);
|
|
28514
|
+
}
|
|
28515
|
+
return [...groups.entries()].filter(([, g]) => g.values.length > 0).map(([pattern, g]) => ({ pattern, count: g.count, values: g.values }));
|
|
27536
28516
|
}
|
|
27537
28517
|
|
|
27538
|
-
// src/triage/gate-display.ts
|
|
27539
|
-
function findContext(entry,
|
|
27540
|
-
const byFingerprint =
|
|
28518
|
+
// ../../packages/setup-wizard/src/triage/gate-display.ts
|
|
28519
|
+
function findContext(entry, join18) {
|
|
28520
|
+
const byFingerprint = join18.find(
|
|
27541
28521
|
(j) => j.valueFingerprint !== void 0 && j.valueFingerprint === entry.valueFingerprint
|
|
27542
28522
|
);
|
|
27543
28523
|
if (byFingerprint) return byFingerprint.maskedContext;
|
|
27544
|
-
const byRuleAndMask =
|
|
28524
|
+
const byRuleAndMask = join18.find(
|
|
27545
28525
|
(j) => j.ruleId === entry.ruleId && j.maskedMatch === entry.maskedValue
|
|
27546
28526
|
);
|
|
27547
28527
|
return byRuleAndMask?.maskedContext;
|
|
@@ -27612,13 +28592,13 @@ function renderShowcase(showcase) {
|
|
|
27612
28592
|
|
|
27613
28593
|
${blocks.join("\n\n")}`;
|
|
27614
28594
|
}
|
|
27615
|
-
function renderSuppressionGate(entries,
|
|
28595
|
+
function renderSuppressionGate(entries, join18) {
|
|
27616
28596
|
if (entries.length === 0) {
|
|
27617
28597
|
return "No false-positive suppressions to confirm \u2014 nothing will be written.";
|
|
27618
28598
|
}
|
|
27619
28599
|
const header = entries.length === 1 ? "This looks like a false positive \u2014 take a look before I suppress it:" : `These ${String(entries.length)} look like false positives \u2014 take a look before I suppress them:`;
|
|
27620
28600
|
const blocks = entries.map((entry, i) => {
|
|
27621
|
-
const context = findContext(entry,
|
|
28601
|
+
const context = findContext(entry, join18);
|
|
27622
28602
|
const lines = [
|
|
27623
28603
|
`${String(i + 1)}. ${entry.ruleId} [${entry.category}]`,
|
|
27624
28604
|
` value: ${entry.maskedValue}`,
|
|
@@ -27632,153 +28612,7 @@ function renderSuppressionGate(entries, join16) {
|
|
|
27632
28612
|
${blocks.join("\n\n")}`;
|
|
27633
28613
|
}
|
|
27634
28614
|
|
|
27635
|
-
// src/
|
|
27636
|
-
var STORE_UNAVAILABLE_NOTE = "I couldn't check my records just now \u2014 we can check again soon. Your Claude session keeps going, and I'll fill in as you work.";
|
|
27637
|
-
var SEVERITY_GLYPH = {
|
|
27638
|
-
critical: SHADE.full,
|
|
27639
|
-
high: SHADE.dark,
|
|
27640
|
-
medium: SHADE.medium,
|
|
27641
|
-
low: SHADE.light
|
|
27642
|
-
};
|
|
27643
|
-
var CATEGORY_ORDER2 = DetectionCategory.options;
|
|
27644
|
-
function categoryRank(category) {
|
|
27645
|
-
const i = CATEGORY_ORDER2.indexOf(category);
|
|
27646
|
-
return i === -1 ? CATEGORY_ORDER2.length : i;
|
|
27647
|
-
}
|
|
27648
|
-
function renderRecommendedPosture(posture) {
|
|
27649
|
-
const rows = Object.keys(posture).map((category) => ({
|
|
27650
|
-
category,
|
|
27651
|
-
level: posture[category] ?? ""
|
|
27652
|
-
}));
|
|
27653
|
-
const width = Math.max(0, ...rows.map((r) => r.category.length));
|
|
27654
|
-
return rows.sort((a, b) => categoryRank(a.category) - categoryRank(b.category)).map((r) => ` ${r.category.padEnd(width)} ${r.level}`).join("\n");
|
|
27655
|
-
}
|
|
27656
|
-
var GRID_MARK = "\u25CF";
|
|
27657
|
-
function renderPostureGrid(posture) {
|
|
27658
|
-
const packs2 = Object.keys(posture).sort(
|
|
27659
|
-
(a, b) => categoryRank(a) - categoryRank(b)
|
|
27660
|
-
);
|
|
27661
|
-
const rows = packs2.map((category) => [
|
|
27662
|
-
category,
|
|
27663
|
-
...BUILTIN_ORDER.map((level) => posture[category] === level ? GRID_MARK : "")
|
|
27664
|
-
]);
|
|
27665
|
-
return indent(table(["Category", ...BUILTIN_ORDER], rows));
|
|
27666
|
-
}
|
|
27667
|
-
var READY_COMMANDS = ["/aka:health", "/aka:findings", "/aka:recommend"];
|
|
27668
|
-
function renderCategoriesTuned(categoriesTuned) {
|
|
27669
|
-
return `\u2713 Set all ${String(categoriesTuned)} detection categories`;
|
|
27670
|
-
}
|
|
27671
|
-
function renderApplied(categoriesTuned, dismissed, registry2) {
|
|
27672
|
-
const routine = dismissed > 0 ? `set aside ${String(dismissed)} routine result${dismissed === 1 ? "" : "s"}` : "nothing routine to set aside";
|
|
27673
|
-
const ready = `Ready: ${selectRegisteredCommands(READY_COMMANDS, registry2).join(" \xB7 ")}`;
|
|
27674
|
-
return `${renderCategoriesTuned(categoriesTuned)} \xB7 ${routine} \xB7 ${ready}`;
|
|
27675
|
-
}
|
|
27676
|
-
|
|
27677
|
-
// src/calibration.ts
|
|
27678
|
-
var SURFACED_KIND_LABEL = {
|
|
27679
|
-
secret: "live keys",
|
|
27680
|
-
pii: "personal data",
|
|
27681
|
-
financial: "financial records",
|
|
27682
|
-
phi: "health records",
|
|
27683
|
-
code_context: "source context",
|
|
27684
|
-
code_flaw: "code flaws",
|
|
27685
|
-
custom: "custom matches",
|
|
27686
|
-
config: "configuration secrets"
|
|
27687
|
-
};
|
|
27688
|
-
function frameCalibration(preview, maskedFindings = [], falsePositivePatterns = []) {
|
|
27689
|
-
const important = preview.categories.reduce((n, c) => n + c.genuineCount, 0);
|
|
27690
|
-
const routine = preview.categories.reduce((n, c) => n + c.fpCount, 0);
|
|
27691
|
-
const total = important + routine;
|
|
27692
|
-
const surfacedCategories = preview.categories.filter((c) => c.genuineCount > 0).map((c) => c.category);
|
|
27693
|
-
const routineCategories = preview.categories.filter((c) => c.fpCount > 0).map((c) => c.category);
|
|
27694
|
-
const findingKinds = preview.categories.filter((c) => c.genuineCount + c.fpCount > 0).map((c) => ({ category: c.category, count: c.genuineCount + c.fpCount, egress: c.egress }));
|
|
27695
|
-
const frame = {
|
|
27696
|
-
counts: { total, important, routine },
|
|
27697
|
-
routineCategories,
|
|
27698
|
-
surfacedCategories,
|
|
27699
|
-
findingKinds,
|
|
27700
|
-
posture: preview.posture,
|
|
27701
|
-
...maskedFindings.length > 0 ? { maskedFindings: [...maskedFindings] } : {},
|
|
27702
|
-
...falsePositivePatterns.length > 0 ? { falsePositivePatterns: [...falsePositivePatterns] } : {}
|
|
27703
|
-
};
|
|
27704
|
-
const kind = surfacedCategories.map((c) => SURFACED_KIND_LABEL[c]).join(", ");
|
|
27705
|
-
const parenthetical = kind ? ` (${kind})` : "";
|
|
27706
|
-
const headline = `I went through Claude's recent work \u2014 ${String(total)} detection${total === 1 ? "" : "s"}, ${String(important)} result${important === 1 ? "" : "s"} worth a look.${parenthetical}`;
|
|
27707
|
-
const copy = headline;
|
|
27708
|
-
return { frame, copy };
|
|
27709
|
-
}
|
|
27710
|
-
var SCAN_CLEAN_HEADLINE = "I looked over Claude's recent work \u2014 nothing needs your attention right now. You're starting clean; here's what I'd recommend:";
|
|
27711
|
-
var NO_HISTORY_HEADLINE = "Nothing to learn from yet \u2014 Claude hasn't left any work on this machine. I'll start each detection category at a careful default:";
|
|
27712
|
-
function zeroCountFrame(posture) {
|
|
27713
|
-
return {
|
|
27714
|
-
counts: { total: 0, important: 0, routine: 0 },
|
|
27715
|
-
routineCategories: [],
|
|
27716
|
-
surfacedCategories: [],
|
|
27717
|
-
findingKinds: [],
|
|
27718
|
-
posture
|
|
27719
|
-
};
|
|
27720
|
-
}
|
|
27721
|
-
function frameEmptyState(cause, posture) {
|
|
27722
|
-
const frame = zeroCountFrame(posture);
|
|
27723
|
-
const copy = cause === "scan-clean" ? `${SCAN_CLEAN_HEADLINE}
|
|
27724
|
-
${renderRecommendedPosture(posture)}` : `${NO_HISTORY_HEADLINE}
|
|
27725
|
-
${renderPostureGrid(posture)}`;
|
|
27726
|
-
return { frame, copy };
|
|
27727
|
-
}
|
|
27728
|
-
|
|
27729
|
-
// src/triage/dedupe.ts
|
|
27730
|
-
function dedupeKey(hit) {
|
|
27731
|
-
if (hit.valueFingerprint === void 0) return void 0;
|
|
27732
|
-
return `${hit.ruleId}\u2588${hit.valueFingerprint}`;
|
|
27733
|
-
}
|
|
27734
|
-
function dedupeForJudge(hits) {
|
|
27735
|
-
const seen = /* @__PURE__ */ new Set();
|
|
27736
|
-
const out = [];
|
|
27737
|
-
for (const h of hits) {
|
|
27738
|
-
const key = dedupeKey(h);
|
|
27739
|
-
if (key === void 0) {
|
|
27740
|
-
out.push(h);
|
|
27741
|
-
continue;
|
|
27742
|
-
}
|
|
27743
|
-
if (seen.has(key)) continue;
|
|
27744
|
-
seen.add(key);
|
|
27745
|
-
out.push(h);
|
|
27746
|
-
}
|
|
27747
|
-
return out;
|
|
27748
|
-
}
|
|
27749
|
-
|
|
27750
|
-
// src/triage/false-positive-patterns.ts
|
|
27751
|
-
function deriveFalsePositivePatterns(hits, rec, plan) {
|
|
27752
|
-
const hitById = new Map(hits.filter((h) => h.id !== void 0).map((h) => [h.id, h]));
|
|
27753
|
-
const seenCategory = /* @__PURE__ */ new Set();
|
|
27754
|
-
const markedIds = /* @__PURE__ */ new Set();
|
|
27755
|
-
for (const c of rec.perCategory) {
|
|
27756
|
-
if (seenCategory.has(c.category)) continue;
|
|
27757
|
-
seenCategory.add(c.category);
|
|
27758
|
-
if (plan.posture[c.category] === void 0) continue;
|
|
27759
|
-
for (const id of c.fpIds) markedIds.add(id);
|
|
27760
|
-
}
|
|
27761
|
-
const groups = /* @__PURE__ */ new Map();
|
|
27762
|
-
for (const id of markedIds) {
|
|
27763
|
-
const h = hitById.get(id);
|
|
27764
|
-
if (h === void 0) continue;
|
|
27765
|
-
const pattern = safeMaskedMatch(h.rawMatch);
|
|
27766
|
-
const group = groups.get(pattern) ?? { count: 0, values: [] };
|
|
27767
|
-
group.count += 1;
|
|
27768
|
-
if (h.valueFingerprint !== void 0 && h.keyVersion !== void 0) {
|
|
27769
|
-
group.values.push({
|
|
27770
|
-
ruleId: h.ruleId,
|
|
27771
|
-
category: h.category,
|
|
27772
|
-
valueFingerprint: h.valueFingerprint,
|
|
27773
|
-
keyVersion: h.keyVersion
|
|
27774
|
-
});
|
|
27775
|
-
}
|
|
27776
|
-
groups.set(pattern, group);
|
|
27777
|
-
}
|
|
27778
|
-
return [...groups.entries()].filter(([, g]) => g.values.length > 0).map(([pattern, g]) => ({ pattern, count: g.count, values: g.values }));
|
|
27779
|
-
}
|
|
27780
|
-
|
|
27781
|
-
// src/triage/merge.ts
|
|
28615
|
+
// ../../packages/setup-wizard/src/triage/merge.ts
|
|
27782
28616
|
var RANK = { monitor: 0, warn: 1, redact: 2, block: 3 };
|
|
27783
28617
|
function chunkForJudge(hits, maxBytes = 262144) {
|
|
27784
28618
|
const chunks = [];
|
|
@@ -27847,10 +28681,10 @@ function mergeRecommendations(verdicts) {
|
|
|
27847
28681
|
};
|
|
27848
28682
|
}
|
|
27849
28683
|
|
|
27850
|
-
// src/triage/plan-file.ts
|
|
27851
|
-
import { mkdtempSync, readFileSync as
|
|
28684
|
+
// ../../packages/setup-wizard/src/triage/plan-file.ts
|
|
28685
|
+
import { mkdtempSync, readFileSync as readFileSync9, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
27852
28686
|
import { tmpdir } from "os";
|
|
27853
|
-
import { basename as
|
|
28687
|
+
import { basename as basename6, dirname as dirname4, join as join15 } from "path";
|
|
27854
28688
|
var SuppressionEntrySchema = external_exports.object({
|
|
27855
28689
|
ruleId: external_exports.string(),
|
|
27856
28690
|
category: DetectionCategory,
|
|
@@ -27905,27 +28739,27 @@ function serializePlan(plan, current) {
|
|
|
27905
28739
|
function writePlanFile(plan, current, rawValues, deps = {}) {
|
|
27906
28740
|
const serialized = serializePlan(plan, current);
|
|
27907
28741
|
assertRawFree(serialized, rawValues);
|
|
27908
|
-
const dir = (deps.mkTempDir ?? (() => mkdtempSync(
|
|
27909
|
-
const path =
|
|
27910
|
-
|
|
28742
|
+
const dir = (deps.mkTempDir ?? (() => mkdtempSync(join15(tmpdir(), "aka-plan-"))))();
|
|
28743
|
+
const path = join15(dir, "setup-plan.json");
|
|
28744
|
+
writeFileSync8(path, serialized, { encoding: "utf8", mode: 384 });
|
|
27911
28745
|
return path;
|
|
27912
28746
|
}
|
|
27913
28747
|
function readPlanFile(path) {
|
|
27914
|
-
const text =
|
|
28748
|
+
const text = readFileSync9(path, "utf8");
|
|
27915
28749
|
const json2 = JSON.parse(text);
|
|
27916
28750
|
return PersistedPlanSchema.parse(json2);
|
|
27917
28751
|
}
|
|
27918
28752
|
function deletePlanFile(path) {
|
|
27919
|
-
|
|
27920
|
-
const dir =
|
|
27921
|
-
if (!
|
|
28753
|
+
rmSync5(path, { force: true });
|
|
28754
|
+
const dir = dirname4(path);
|
|
28755
|
+
if (!basename6(dir).startsWith("aka-plan-")) return;
|
|
27922
28756
|
try {
|
|
27923
28757
|
rmdirSync(dir);
|
|
27924
28758
|
} catch {
|
|
27925
28759
|
}
|
|
27926
28760
|
}
|
|
27927
28761
|
|
|
27928
|
-
// src/triage/surfaced-secrets.ts
|
|
28762
|
+
// ../../packages/setup-wizard/src/triage/surfaced-secrets.ts
|
|
27929
28763
|
var DEFAULT_STATE = "unknown";
|
|
27930
28764
|
var UNKNOWN_LOCATION = "(location unavailable)";
|
|
27931
28765
|
function deriveProvider(ruleId) {
|
|
@@ -27957,7 +28791,7 @@ function deriveSurfacedSecretFindings(hits, rec, plan) {
|
|
|
27957
28791
|
}));
|
|
27958
28792
|
}
|
|
27959
28793
|
|
|
27960
|
-
// src/triage/join-file.ts
|
|
28794
|
+
// ../../packages/setup-wizard/src/triage/join-file.ts
|
|
27961
28795
|
function buildJoinEntries(hits) {
|
|
27962
28796
|
const rawValues = hits.map((h) => h.rawMatch);
|
|
27963
28797
|
return hits.map((h) => {
|
|
@@ -27984,9 +28818,9 @@ function buildJoinEntries(hits) {
|
|
|
27984
28818
|
});
|
|
27985
28819
|
}
|
|
27986
28820
|
|
|
27987
|
-
// src/triage/resolve.ts
|
|
27988
|
-
function resolveSuppressions(rec,
|
|
27989
|
-
const byId = new Map(
|
|
28821
|
+
// ../../packages/setup-wizard/src/triage/resolve.ts
|
|
28822
|
+
function resolveSuppressions(rec, join18) {
|
|
28823
|
+
const byId = new Map(join18.map((e) => [e.id, e]));
|
|
27990
28824
|
const entries = [];
|
|
27991
28825
|
const skipped = [];
|
|
27992
28826
|
for (const cat of rec.perCategory) {
|
|
@@ -28024,7 +28858,7 @@ function resolveSuppressions(rec, join16) {
|
|
|
28024
28858
|
return { entries, skipped };
|
|
28025
28859
|
}
|
|
28026
28860
|
|
|
28027
|
-
// src/triage/writeback.ts
|
|
28861
|
+
// ../../packages/setup-wizard/src/triage/writeback.ts
|
|
28028
28862
|
var SCRUBBED_NOTES = "[notes withheld: model text referenced a raw detected value]";
|
|
28029
28863
|
var TRIAGE_STATUSES = ["complete", "complete:no-history", "skipped:no-consent"];
|
|
28030
28864
|
function isSentinel(v) {
|
|
@@ -28088,7 +28922,7 @@ function parseTriageStream(text) {
|
|
|
28088
28922
|
return { hits, status: "complete" };
|
|
28089
28923
|
}
|
|
28090
28924
|
function planTriageWriteback(hits, rec) {
|
|
28091
|
-
const
|
|
28925
|
+
const join18 = buildJoinEntries(hits);
|
|
28092
28926
|
const rawValues = hits.map((h) => h.rawMatch);
|
|
28093
28927
|
const skipped = [];
|
|
28094
28928
|
const posture = {};
|
|
@@ -28128,7 +28962,7 @@ function planTriageWriteback(hits, rec) {
|
|
|
28128
28962
|
}
|
|
28129
28963
|
const { entries, skipped: resolveSkips } = resolveSuppressions(
|
|
28130
28964
|
{ perCategory: safeCategories, notes: rec.notes },
|
|
28131
|
-
|
|
28965
|
+
join18
|
|
28132
28966
|
);
|
|
28133
28967
|
skipped.push(...resolveSkips);
|
|
28134
28968
|
let notes = rec.notes;
|
|
@@ -28138,7 +28972,7 @@ function planTriageWriteback(hits, rec) {
|
|
|
28138
28972
|
if (err instanceof RawEgressError) notes = SCRUBBED_NOTES;
|
|
28139
28973
|
else throw err;
|
|
28140
28974
|
}
|
|
28141
|
-
return { entries, posture, showcase, join:
|
|
28975
|
+
return { entries, posture, showcase, join: join18, notes, skipped };
|
|
28142
28976
|
}
|
|
28143
28977
|
function recommendedPosture(evidence) {
|
|
28144
28978
|
return { ...severityFloorPosture(), ...evidence };
|
|
@@ -28158,7 +28992,7 @@ async function performTriageWriteback(plan, writers, opts) {
|
|
|
28158
28992
|
return { written, skippedDuplicate, categoriesWritten: tuned.size };
|
|
28159
28993
|
}
|
|
28160
28994
|
|
|
28161
|
-
// src/triage/adapter.ts
|
|
28995
|
+
// ../../packages/setup-wizard/src/triage/adapter.ts
|
|
28162
28996
|
var DEFAULT_PLAN_IO = {
|
|
28163
28997
|
write: writePlanFile,
|
|
28164
28998
|
read: readPlanFile,
|
|
@@ -28184,28 +29018,31 @@ async function runApply(deps) {
|
|
|
28184
29018
|
return confirmed ? runConfirm(deps, planIO) : runPreview(deps, planIO);
|
|
28185
29019
|
}
|
|
28186
29020
|
function runPreview(deps, planIO) {
|
|
29021
|
+
const { present } = deps;
|
|
28187
29022
|
const streamPath = getFlag(deps.argv, "stream");
|
|
28188
29023
|
const streamText = deps.readStream(streamPath === "" ? void 0 : streamPath);
|
|
28189
29024
|
const { hits, status } = parseTriageStream(streamText);
|
|
28190
29025
|
if (hits.length === 0) {
|
|
28191
29026
|
if (status === "complete") {
|
|
28192
|
-
const empty = frameEmptyState("scan-clean", severityFloorPosture());
|
|
28193
|
-
deps.stdout(show(fenced(empty.copy)));
|
|
28194
|
-
deps.stdout(frameJsonBlock(empty.frame));
|
|
29027
|
+
const empty = present.frameEmptyState("scan-clean", severityFloorPosture());
|
|
29028
|
+
deps.stdout(present.show(present.fenced(empty.copy)));
|
|
29029
|
+
deps.stdout(present.frameJsonBlock(empty.frame));
|
|
28195
29030
|
return 0;
|
|
28196
29031
|
}
|
|
28197
29032
|
if (status === "complete:no-history") {
|
|
28198
|
-
const empty = frameEmptyState("no-history", severityFloorPosture());
|
|
28199
|
-
deps.stdout(show(fenced(empty.copy)));
|
|
28200
|
-
deps.stdout(frameJsonBlock(empty.frame));
|
|
29033
|
+
const empty = present.frameEmptyState("no-history", severityFloorPosture());
|
|
29034
|
+
deps.stdout(present.show(present.fenced(empty.copy)));
|
|
29035
|
+
deps.stdout(present.frameJsonBlock(empty.frame));
|
|
28201
29036
|
return 0;
|
|
28202
29037
|
}
|
|
28203
|
-
deps.stdout(show("I didn't review anything \u2014 historical access wasn't granted."));
|
|
29038
|
+
deps.stdout(present.show("I didn't review anything \u2014 historical access wasn't granted."));
|
|
28204
29039
|
return 0;
|
|
28205
29040
|
}
|
|
28206
29041
|
if (!deps.modelJudgeConsent()) {
|
|
28207
|
-
deps.stdout(
|
|
28208
|
-
|
|
29042
|
+
deps.stdout(
|
|
29043
|
+
present.show("I didn't send anything to the model \u2014 model-judge consent wasn't granted.")
|
|
29044
|
+
);
|
|
29045
|
+
deps.stdout(present.frameJsonBlock(present.zeroCountFrame(severityFloorPosture())));
|
|
28209
29046
|
return 0;
|
|
28210
29047
|
}
|
|
28211
29048
|
const rawValues = hits.map((h) => h.rawMatch);
|
|
@@ -28214,7 +29051,7 @@ function runPreview(deps, planIO) {
|
|
|
28214
29051
|
const chunks = chunkForJudge(reps, resolveMaxJudgeBytes(deps));
|
|
28215
29052
|
if (chunks.length > 1) {
|
|
28216
29053
|
deps.stdout(
|
|
28217
|
-
show(
|
|
29054
|
+
present.show(
|
|
28218
29055
|
`Reviewing ${String(reps.length)} distinct values in ${String(chunks.length)} batches \u2014 this is the large-history path, so give it a moment.`
|
|
28219
29056
|
)
|
|
28220
29057
|
);
|
|
@@ -28245,7 +29082,7 @@ function runPreview(deps, planIO) {
|
|
|
28245
29082
|
}
|
|
28246
29083
|
const gate = [];
|
|
28247
29084
|
if (storeUnavailable) {
|
|
28248
|
-
gate.push(
|
|
29085
|
+
gate.push(present.storeUnavailableNote);
|
|
28249
29086
|
}
|
|
28250
29087
|
gate.push(renderPosturePlan(plan.posture, storeUnavailable ? {} : current));
|
|
28251
29088
|
gate.push(renderShowcase(plan.showcase));
|
|
@@ -28267,11 +29104,11 @@ function runPreview(deps, planIO) {
|
|
|
28267
29104
|
};
|
|
28268
29105
|
const maskedFindings = deriveSurfacedSecretFindings(hits, rec, plan);
|
|
28269
29106
|
const falsePositivePatterns = deriveFalsePositivePatterns(reps, rec, plan);
|
|
28270
|
-
const calibration = frameCalibration(preview, maskedFindings, falsePositivePatterns);
|
|
29107
|
+
const calibration = present.frameCalibration(preview, maskedFindings, falsePositivePatterns);
|
|
28271
29108
|
gate.push(calibration.copy);
|
|
28272
|
-
gate.push(renderRecommendedPosture(preview.posture));
|
|
28273
|
-
deps.stdout(show(fenced(gate.join("\n\n"))));
|
|
28274
|
-
deps.stdout(frameJsonBlock(calibration.frame));
|
|
29109
|
+
gate.push(present.renderRecommendedPosture(preview.posture));
|
|
29110
|
+
deps.stdout(present.show(present.fenced(gate.join("\n\n"))));
|
|
29111
|
+
deps.stdout(present.frameJsonBlock(calibration.frame));
|
|
28275
29112
|
const planPath = planIO.write(plan, storeUnavailable ? {} : current, rawValues);
|
|
28276
29113
|
deps.stdout(`
|
|
28277
29114
|
Plan saved to: ${planPath}
|
|
@@ -28328,7 +29165,7 @@ async function runConfirm(deps, planIO) {
|
|
|
28328
29165
|
if (drifted.length > 0) {
|
|
28329
29166
|
closeOnce();
|
|
28330
29167
|
deps.stderr(
|
|
28331
|
-
`AKA apply-suppressions failed: the detection store changed since this plan was previewed (${drifted.join(", ")}). Refusing to apply a stale plan \u2014 re-run
|
|
29168
|
+
`AKA apply-suppressions failed: the detection store changed since this plan was previewed (${drifted.join(", ")}). Refusing to apply a stale plan \u2014 re-run ${deps.present.rerunHint} to review against the current store.
|
|
28332
29169
|
`
|
|
28333
29170
|
);
|
|
28334
29171
|
return 1;
|
|
@@ -28378,7 +29215,7 @@ async function runConfirm(deps, planIO) {
|
|
|
28378
29215
|
}
|
|
28379
29216
|
try {
|
|
28380
29217
|
deps.stdout(
|
|
28381
|
-
show(renderApplied(res.categoriesWritten, res.written
|
|
29218
|
+
deps.present.show(deps.present.renderApplied(res.categoriesWritten, res.written))
|
|
28382
29219
|
);
|
|
28383
29220
|
} catch {
|
|
28384
29221
|
}
|
|
@@ -28393,14 +29230,7 @@ async function runConfirm(deps, planIO) {
|
|
|
28393
29230
|
}
|
|
28394
29231
|
}
|
|
28395
29232
|
|
|
28396
|
-
// src/triage/
|
|
28397
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
28398
|
-
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync9, rmSync as rmSync5 } from "fs";
|
|
28399
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
28400
|
-
import { dirname as dirname4, join as join14 } from "path";
|
|
28401
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
28402
|
-
|
|
28403
|
-
// src/triage/parse-verdict.ts
|
|
29233
|
+
// ../../packages/setup-wizard/src/triage/parse-verdict.ts
|
|
28404
29234
|
var FENCE_RE = /```json\s*([\s\S]*?)```/g;
|
|
28405
29235
|
function parseRecommendation(text) {
|
|
28406
29236
|
const fences = [...text.matchAll(FENCE_RE)];
|
|
@@ -28410,8 +29240,23 @@ function parseRecommendation(text) {
|
|
|
28410
29240
|
}
|
|
28411
29241
|
|
|
28412
29242
|
// src/triage/judge.ts
|
|
28413
|
-
|
|
28414
|
-
|
|
29243
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
29244
|
+
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync10, rmSync as rmSync6 } from "fs";
|
|
29245
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
29246
|
+
import { dirname as dirname5, join as join16 } from "path";
|
|
29247
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
29248
|
+
var TRIAGE_DIR = dirname5(fileURLToPath2(import.meta.url));
|
|
29249
|
+
var DEFAULT_RUBRIC_PATH = join16(
|
|
29250
|
+
TRIAGE_DIR,
|
|
29251
|
+
"..",
|
|
29252
|
+
"..",
|
|
29253
|
+
"..",
|
|
29254
|
+
"..",
|
|
29255
|
+
"packages",
|
|
29256
|
+
"setup-wizard",
|
|
29257
|
+
"assets",
|
|
29258
|
+
"triage-rubric.md"
|
|
29259
|
+
);
|
|
28415
29260
|
function parseVerdict(stdout) {
|
|
28416
29261
|
let envelope;
|
|
28417
29262
|
try {
|
|
@@ -28438,7 +29283,7 @@ function judgeEnv(platform2 = process.platform) {
|
|
|
28438
29283
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
|
|
28439
29284
|
};
|
|
28440
29285
|
if (platform2 === "darwin") {
|
|
28441
|
-
env.CLAUDE_CONFIG_DIR = mkdtempSync2(
|
|
29286
|
+
env.CLAUDE_CONFIG_DIR = mkdtempSync2(join16(tmpdir2(), "aka-judge-cfg-"));
|
|
28442
29287
|
}
|
|
28443
29288
|
return env;
|
|
28444
29289
|
}
|
|
@@ -28470,7 +29315,7 @@ function runJudge(hits, deps) {
|
|
|
28470
29315
|
if (typeof deps.spawn !== "function") {
|
|
28471
29316
|
throw new TypeError("runJudge requires deps.spawn \u2014 there is no live-spawn fallback");
|
|
28472
29317
|
}
|
|
28473
|
-
const rubric = deps.loadRubric?.() ??
|
|
29318
|
+
const rubric = deps.loadRubric?.() ?? readFileSync10(DEFAULT_RUBRIC_PATH, "utf8");
|
|
28474
29319
|
const hitsJsonl = hits.map((h) => JSON.stringify(toJudgePayload(h))).join("\n");
|
|
28475
29320
|
const fullPrompt = `${rubric}
|
|
28476
29321
|
|
|
@@ -28494,13 +29339,237 @@ ${hitsJsonl}
|
|
|
28494
29339
|
} finally {
|
|
28495
29340
|
if (platform2 === "darwin" && env.CLAUDE_CONFIG_DIR) {
|
|
28496
29341
|
try {
|
|
28497
|
-
|
|
29342
|
+
rmSync6(env.CLAUDE_CONFIG_DIR, { recursive: true, force: true });
|
|
28498
29343
|
} catch {
|
|
28499
29344
|
}
|
|
28500
29345
|
}
|
|
28501
29346
|
}
|
|
28502
29347
|
}
|
|
28503
29348
|
|
|
29349
|
+
// src/command-registry.ts
|
|
29350
|
+
import { readdirSync as readdirSync5 } from "fs";
|
|
29351
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
29352
|
+
var COMMAND_NAMESPACE = "aka";
|
|
29353
|
+
var COMMANDS_DIR = fileURLToPath3(new URL("../commands", import.meta.url));
|
|
29354
|
+
function readRegisteredCommands() {
|
|
29355
|
+
return readdirSync5(COMMANDS_DIR).filter((f) => f.endsWith(".md")).map((f) => `/${COMMAND_NAMESPACE}:${f.replace(/\.md$/, "")}`);
|
|
29356
|
+
}
|
|
29357
|
+
function selectRegisteredCommands(curated, registry2) {
|
|
29358
|
+
const registered = new Set(registry2);
|
|
29359
|
+
const missing = curated.filter((c) => !registered.has(c));
|
|
29360
|
+
if (missing.length > 0) {
|
|
29361
|
+
throw new Error(
|
|
29362
|
+
`Curated command(s) not registered in the installed plugin: ${missing.join(", ")}`
|
|
29363
|
+
);
|
|
29364
|
+
}
|
|
29365
|
+
return [...curated];
|
|
29366
|
+
}
|
|
29367
|
+
|
|
29368
|
+
// src/setup-frame-json.ts
|
|
29369
|
+
var FRAME_JSON_BEGIN = "<<<AKA_FRAME_JSON";
|
|
29370
|
+
var FRAME_JSON_END = "AKA_FRAME_JSON>>>";
|
|
29371
|
+
function frameJsonBlock(payload) {
|
|
29372
|
+
return `${FRAME_JSON_BEGIN}
|
|
29373
|
+
${JSON.stringify(payload)}
|
|
29374
|
+
${FRAME_JSON_END}
|
|
29375
|
+
`;
|
|
29376
|
+
}
|
|
29377
|
+
|
|
29378
|
+
// src/setup-show.ts
|
|
29379
|
+
var SHOW_BEGIN = "<<<AKA_SHOW";
|
|
29380
|
+
var SHOW_END = "AKA_SHOW>>>";
|
|
29381
|
+
function showBlock(body) {
|
|
29382
|
+
return `${SHOW_BEGIN}
|
|
29383
|
+
${body}
|
|
29384
|
+
${SHOW_END}
|
|
29385
|
+
`;
|
|
29386
|
+
}
|
|
29387
|
+
|
|
29388
|
+
// src/present.ts
|
|
29389
|
+
var SHADE = {
|
|
29390
|
+
light: "\u2591",
|
|
29391
|
+
medium: "\u2592",
|
|
29392
|
+
dark: "\u2593",
|
|
29393
|
+
full: "\u2588"
|
|
29394
|
+
};
|
|
29395
|
+
var ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
29396
|
+
function visibleLength(text) {
|
|
29397
|
+
return text.replace(ANSI_RE, "").length;
|
|
29398
|
+
}
|
|
29399
|
+
var fg = (hex3) => (text) => {
|
|
29400
|
+
const r = Number.parseInt(hex3.slice(1, 3), 16);
|
|
29401
|
+
const g = Number.parseInt(hex3.slice(3, 5), 16);
|
|
29402
|
+
const b = Number.parseInt(hex3.slice(5, 7), 16);
|
|
29403
|
+
return `\x1B[38;2;${String(r)};${String(g)};${String(b)}m${text}\x1B[0m`;
|
|
29404
|
+
};
|
|
29405
|
+
var paint = {
|
|
29406
|
+
brand: fg("#33e6c6"),
|
|
29407
|
+
// --color-brand · ▸▸ AKA wordmark (accent text)
|
|
29408
|
+
dim: fg("#838995"),
|
|
29409
|
+
// --color-text-3 · separators · "/100" · the "unreviewed" label
|
|
29410
|
+
bold: (text) => `\x1B[1m${text}\x1B[0m`,
|
|
29411
|
+
// the health score number
|
|
29412
|
+
ok: fg("#0db15f"),
|
|
29413
|
+
// --color-ok · healthy ● dot
|
|
29414
|
+
critical: fg("#e63448"),
|
|
29415
|
+
// --color-sev-critical · ■ and the open-findings flag
|
|
29416
|
+
high: fg("#e97a0a"),
|
|
29417
|
+
// --color-sev-high · ■ and the mid-health dot
|
|
29418
|
+
medium: fg("#f7bd00"),
|
|
29419
|
+
// --color-sev-medium · ■
|
|
29420
|
+
low: fg("#0581d4")
|
|
29421
|
+
// --color-sev-low · ■ (azure blue, not purple)
|
|
29422
|
+
};
|
|
29423
|
+
function padEnd(text, width) {
|
|
29424
|
+
const pad = width - visibleLength(text);
|
|
29425
|
+
return pad > 0 ? text + " ".repeat(pad) : text;
|
|
29426
|
+
}
|
|
29427
|
+
function indent(text, spaces = 2) {
|
|
29428
|
+
const pad = " ".repeat(spaces);
|
|
29429
|
+
return text.split("\n").map((line) => pad + line).join("\n");
|
|
29430
|
+
}
|
|
29431
|
+
function table(headers, rows, opts = {}) {
|
|
29432
|
+
const gap = opts.gap ?? 3;
|
|
29433
|
+
const widths = headers.map(
|
|
29434
|
+
(h, i) => Math.max(visibleLength(h), ...rows.map((r) => visibleLength(r[i] ?? "")))
|
|
29435
|
+
);
|
|
29436
|
+
const sep5 = " ".repeat(gap);
|
|
29437
|
+
const fmt = (cells) => cells.map((cell, i) => padEnd(cell, widths[i] ?? 0)).join(sep5);
|
|
29438
|
+
const headerLine = fmt(headers.map((h) => h.toUpperCase()));
|
|
29439
|
+
if (opts.rowSep === true) {
|
|
29440
|
+
const fullWidth = widths.reduce((n, w) => n + w, 0) + gap * Math.max(0, widths.length - 1);
|
|
29441
|
+
const rule = "\u2500".repeat(fullWidth);
|
|
29442
|
+
const body = [];
|
|
29443
|
+
rows.forEach((row, i) => {
|
|
29444
|
+
if (i > 0) body.push(rule);
|
|
29445
|
+
body.push(fmt(row));
|
|
29446
|
+
});
|
|
29447
|
+
return [headerLine, rule, ...body].join("\n");
|
|
29448
|
+
}
|
|
29449
|
+
const ruleLine = widths.map((w) => "\u2500".repeat(w)).join(sep5);
|
|
29450
|
+
return [headerLine, ruleLine, ...rows.map(fmt)].join("\n");
|
|
29451
|
+
}
|
|
29452
|
+
function fenced(body) {
|
|
29453
|
+
const longestRun = Math.max(0, ...[...body.matchAll(/`+/g)].map((m) => m[0].length));
|
|
29454
|
+
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
29455
|
+
return [fence, body, fence].join("\n");
|
|
29456
|
+
}
|
|
29457
|
+
function show(body) {
|
|
29458
|
+
return showBlock(body);
|
|
29459
|
+
}
|
|
29460
|
+
|
|
29461
|
+
// src/render.ts
|
|
29462
|
+
var STORE_UNAVAILABLE_NOTE = "I couldn't check my records just now \u2014 we can check again soon. Your Claude session keeps going, and I'll fill in as you work.";
|
|
29463
|
+
var SEVERITY_GLYPH = {
|
|
29464
|
+
critical: SHADE.full,
|
|
29465
|
+
high: SHADE.dark,
|
|
29466
|
+
medium: SHADE.medium,
|
|
29467
|
+
low: SHADE.light
|
|
29468
|
+
};
|
|
29469
|
+
var CATEGORY_ORDER2 = DetectionCategory.options;
|
|
29470
|
+
function categoryRank(category) {
|
|
29471
|
+
const i = CATEGORY_ORDER2.indexOf(category);
|
|
29472
|
+
return i === -1 ? CATEGORY_ORDER2.length : i;
|
|
29473
|
+
}
|
|
29474
|
+
function renderRecommendedPosture(posture) {
|
|
29475
|
+
const rows = Object.keys(posture).map((category) => ({
|
|
29476
|
+
category,
|
|
29477
|
+
level: posture[category] ?? ""
|
|
29478
|
+
}));
|
|
29479
|
+
const width = Math.max(0, ...rows.map((r) => r.category.length));
|
|
29480
|
+
return rows.sort((a, b) => categoryRank(a.category) - categoryRank(b.category)).map((r) => ` ${r.category.padEnd(width)} ${r.level}`).join("\n");
|
|
29481
|
+
}
|
|
29482
|
+
var GRID_MARK = "\u25CF";
|
|
29483
|
+
function renderPostureGrid(posture) {
|
|
29484
|
+
const packs2 = Object.keys(posture).sort(
|
|
29485
|
+
(a, b) => categoryRank(a) - categoryRank(b)
|
|
29486
|
+
);
|
|
29487
|
+
const rows = packs2.map((category) => [
|
|
29488
|
+
category,
|
|
29489
|
+
...BUILTIN_ORDER.map((level) => posture[category] === level ? GRID_MARK : "")
|
|
29490
|
+
]);
|
|
29491
|
+
return indent(table(["Category", ...BUILTIN_ORDER], rows));
|
|
29492
|
+
}
|
|
29493
|
+
var READY_COMMANDS = ["/aka:health", "/aka:findings", "/aka:recommend"];
|
|
29494
|
+
function renderCategoriesTuned(categoriesTuned) {
|
|
29495
|
+
return `\u2713 Set all ${String(categoriesTuned)} detection categories`;
|
|
29496
|
+
}
|
|
29497
|
+
function renderApplied(categoriesTuned, dismissed, registry2) {
|
|
29498
|
+
const routine = dismissed > 0 ? `set aside ${String(dismissed)} routine result${dismissed === 1 ? "" : "s"}` : "nothing routine to set aside";
|
|
29499
|
+
const ready = `Ready: ${selectRegisteredCommands(READY_COMMANDS, registry2).join(" \xB7 ")}`;
|
|
29500
|
+
return `${renderCategoriesTuned(categoriesTuned)} \xB7 ${routine} \xB7 ${ready}`;
|
|
29501
|
+
}
|
|
29502
|
+
|
|
29503
|
+
// src/calibration.ts
|
|
29504
|
+
var SURFACED_KIND_LABEL = {
|
|
29505
|
+
secret: "live keys",
|
|
29506
|
+
pii: "personal data",
|
|
29507
|
+
financial: "financial records",
|
|
29508
|
+
phi: "health records",
|
|
29509
|
+
code_context: "source context",
|
|
29510
|
+
code_flaw: "code flaws",
|
|
29511
|
+
custom: "custom matches",
|
|
29512
|
+
config: "configuration secrets"
|
|
29513
|
+
};
|
|
29514
|
+
function frameCalibration(preview, maskedFindings = [], falsePositivePatterns = []) {
|
|
29515
|
+
const important = preview.categories.reduce((n, c) => n + c.genuineCount, 0);
|
|
29516
|
+
const routine = preview.categories.reduce((n, c) => n + c.fpCount, 0);
|
|
29517
|
+
const total = important + routine;
|
|
29518
|
+
const surfacedCategories = preview.categories.filter((c) => c.genuineCount > 0).map((c) => c.category);
|
|
29519
|
+
const routineCategories = preview.categories.filter((c) => c.fpCount > 0).map((c) => c.category);
|
|
29520
|
+
const findingKinds = preview.categories.filter((c) => c.genuineCount + c.fpCount > 0).map((c) => ({ category: c.category, count: c.genuineCount + c.fpCount, egress: c.egress }));
|
|
29521
|
+
const frame = {
|
|
29522
|
+
counts: { total, important, routine },
|
|
29523
|
+
routineCategories,
|
|
29524
|
+
surfacedCategories,
|
|
29525
|
+
findingKinds,
|
|
29526
|
+
posture: preview.posture,
|
|
29527
|
+
...maskedFindings.length > 0 ? { maskedFindings: [...maskedFindings] } : {},
|
|
29528
|
+
...falsePositivePatterns.length > 0 ? { falsePositivePatterns: [...falsePositivePatterns] } : {}
|
|
29529
|
+
};
|
|
29530
|
+
const kind = surfacedCategories.map((c) => SURFACED_KIND_LABEL[c]).join(", ");
|
|
29531
|
+
const parenthetical = kind ? ` (${kind})` : "";
|
|
29532
|
+
const headline = `I went through Claude's recent work \u2014 ${String(total)} detection${total === 1 ? "" : "s"}, ${String(important)} result${important === 1 ? "" : "s"} worth a look.${parenthetical}`;
|
|
29533
|
+
const copy = headline;
|
|
29534
|
+
return { frame, copy };
|
|
29535
|
+
}
|
|
29536
|
+
var SCAN_CLEAN_HEADLINE = "I looked over Claude's recent work \u2014 nothing needs your attention right now. You're starting clean; here's what I'd recommend:";
|
|
29537
|
+
var NO_HISTORY_HEADLINE = "Nothing to learn from yet \u2014 Claude hasn't left any work on this machine. I'll start each detection category at a careful default:";
|
|
29538
|
+
function zeroCountFrame(posture) {
|
|
29539
|
+
return {
|
|
29540
|
+
counts: { total: 0, important: 0, routine: 0 },
|
|
29541
|
+
routineCategories: [],
|
|
29542
|
+
surfacedCategories: [],
|
|
29543
|
+
findingKinds: [],
|
|
29544
|
+
posture
|
|
29545
|
+
};
|
|
29546
|
+
}
|
|
29547
|
+
function frameEmptyState(cause, posture) {
|
|
29548
|
+
const frame = zeroCountFrame(posture);
|
|
29549
|
+
const copy = cause === "scan-clean" ? `${SCAN_CLEAN_HEADLINE}
|
|
29550
|
+
${renderRecommendedPosture(posture)}` : `${NO_HISTORY_HEADLINE}
|
|
29551
|
+
${renderPostureGrid(posture)}`;
|
|
29552
|
+
return { frame, copy };
|
|
29553
|
+
}
|
|
29554
|
+
|
|
29555
|
+
// src/triage/presenter.ts
|
|
29556
|
+
var adapterPresenter = {
|
|
29557
|
+
show,
|
|
29558
|
+
fenced,
|
|
29559
|
+
frameJsonBlock,
|
|
29560
|
+
frameEmptyState,
|
|
29561
|
+
frameCalibration,
|
|
29562
|
+
zeroCountFrame,
|
|
29563
|
+
renderRecommendedPosture,
|
|
29564
|
+
// The applied card's Ready line resolves against the installed command
|
|
29565
|
+
// registry — closed over here so the shared core never reads it.
|
|
29566
|
+
renderApplied: (categoriesTuned, dismissed) => renderApplied(categoriesTuned, dismissed, readRegisteredCommands()),
|
|
29567
|
+
storeUnavailableNote: STORE_UNAVAILABLE_NOTE,
|
|
29568
|
+
// The stale-plan refusal tells the user to restart the wizard by its
|
|
29569
|
+
// Claude Code command name.
|
|
29570
|
+
rerunHint: "/aka:setup"
|
|
29571
|
+
};
|
|
29572
|
+
|
|
28504
29573
|
// src/apply-suppressions.ts
|
|
28505
29574
|
function fail(message) {
|
|
28506
29575
|
process.stderr.write(`AKA apply-suppressions failed: ${message}
|
|
@@ -28515,10 +29584,13 @@ function resolveCreatedBy() {
|
|
|
28515
29584
|
}
|
|
28516
29585
|
}
|
|
28517
29586
|
function loadRubric() {
|
|
28518
|
-
const here =
|
|
28519
|
-
const shipped =
|
|
28520
|
-
if (
|
|
28521
|
-
return
|
|
29587
|
+
const here = dirname6(fileURLToPath4(import.meta.url));
|
|
29588
|
+
const shipped = join17(here, "triage-rubric.md");
|
|
29589
|
+
if (existsSync9(shipped)) return readFileSync11(shipped, "utf8");
|
|
29590
|
+
return readFileSync11(
|
|
29591
|
+
join17(here, "..", "..", "..", "packages", "setup-wizard", "assets", "triage-rubric.md"),
|
|
29592
|
+
"utf8"
|
|
29593
|
+
);
|
|
28522
29594
|
}
|
|
28523
29595
|
async function main() {
|
|
28524
29596
|
const argv = process.argv.slice(2);
|
|
@@ -28526,7 +29598,7 @@ async function main() {
|
|
|
28526
29598
|
argv,
|
|
28527
29599
|
// fd 0 = stdin; the wizard pipes `backfill.js --triage` into this on preview.
|
|
28528
29600
|
// Called only on the preview path — the confirm path never reads a stream.
|
|
28529
|
-
readStream: (streamPath) => streamPath !== void 0 ?
|
|
29601
|
+
readStream: (streamPath) => streamPath !== void 0 ? readFileSync11(streamPath, "utf8") : readFileSync11(0, "utf8"),
|
|
28530
29602
|
runJudge: (hits) => runJudge(hits, { spawn: spawnClaude, loadRubric }),
|
|
28531
29603
|
// The distinct model-judge egress consent, read from settings.json. When it
|
|
28532
29604
|
// is absent or stale the preview skips the judge instead of sending findings
|
|
@@ -28548,6 +29620,7 @@ async function main() {
|
|
|
28548
29620
|
now: () => Date.now(),
|
|
28549
29621
|
createdBy: resolveCreatedBy,
|
|
28550
29622
|
stdout: (s) => process.stdout.write(s),
|
|
29623
|
+
present: adapterPresenter,
|
|
28551
29624
|
stderr: (s) => process.stderr.write(s)
|
|
28552
29625
|
});
|
|
28553
29626
|
process.exit(code);
|