@contextecf/guardian-cli 0.1.6 → 0.1.9
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/README.md +19 -14
- package/dist/packages/guardian-cli/src/bin.js +2602 -143
- package/dist/packages/guardian-cli/src/index.js +686 -95
- package/dist/packages/guardian-cli/src/runtime.d.ts +59 -7
- package/package.json +1 -1
|
@@ -17061,6 +17061,8 @@ async function runGuardianCli(argv, options = {}) {
|
|
|
17061
17061
|
return runReleaseCommand(argv.slice(1), flags, options);
|
|
17062
17062
|
case "open":
|
|
17063
17063
|
return runControlTowerOpenCommand(flags, options);
|
|
17064
|
+
case "connect":
|
|
17065
|
+
return runExtensionConnectCommand(flags, options);
|
|
17064
17066
|
case "status":
|
|
17065
17067
|
write(stdout, await statusGuardian(flags, options));
|
|
17066
17068
|
return 0;
|
|
@@ -17072,6 +17074,14 @@ async function runGuardianCli(argv, options = {}) {
|
|
|
17072
17074
|
return runLaunchCommand(flags, options);
|
|
17073
17075
|
case "stop":
|
|
17074
17076
|
return runStopCommand(flags, options);
|
|
17077
|
+
case "pair": {
|
|
17078
|
+
if (!flags.has("json")) {
|
|
17079
|
+
return runExtensionConnectCommand(flags, options);
|
|
17080
|
+
}
|
|
17081
|
+
const result = await daemonPair(flags, options);
|
|
17082
|
+
write(result.exitCode === 0 ? stdout : stderr, result.message);
|
|
17083
|
+
return result.exitCode;
|
|
17084
|
+
}
|
|
17075
17085
|
case "daemon":
|
|
17076
17086
|
return runDaemonCommand(argv.slice(1), flags, options);
|
|
17077
17087
|
case "service":
|
|
@@ -17094,6 +17104,9 @@ async function runGuardianCli(argv, options = {}) {
|
|
|
17094
17104
|
return runPrivacyCommand(argv.slice(1), flags, options);
|
|
17095
17105
|
case "mcp":
|
|
17096
17106
|
return runMcpCommand(argv.slice(1), flags, options);
|
|
17107
|
+
case "coach":
|
|
17108
|
+
case "prompt-coach":
|
|
17109
|
+
return runCoachCommand(argv.slice(1), flags, options);
|
|
17097
17110
|
case "policy":
|
|
17098
17111
|
return runPolicyCommand(argv.slice(1), flags, options);
|
|
17099
17112
|
case "marketplace":
|
|
@@ -17116,7 +17129,7 @@ async function runGuardianCli(argv, options = {}) {
|
|
|
17116
17129
|
case "help":
|
|
17117
17130
|
case "--help":
|
|
17118
17131
|
case "-h":
|
|
17119
|
-
write(stdout, `${HELP_TEXT}
|
|
17132
|
+
write(stdout, `${flags.has("advanced") ? ADVANCED_HELP_TEXT : HELP_TEXT}
|
|
17120
17133
|
`);
|
|
17121
17134
|
return 0;
|
|
17122
17135
|
default:
|
|
@@ -17195,11 +17208,13 @@ async function installGuardian(flags, options = {}) {
|
|
|
17195
17208
|
"guardian start",
|
|
17196
17209
|
"guardian open",
|
|
17197
17210
|
"guardian mcp install",
|
|
17198
|
-
"guardian daemon pair --json",
|
|
17199
17211
|
"guardian service install",
|
|
17200
17212
|
"guardian key-store install --yes",
|
|
17201
|
-
"guardian
|
|
17202
|
-
|
|
17213
|
+
"guardian connect"
|
|
17214
|
+
],
|
|
17215
|
+
advancedCommands: [
|
|
17216
|
+
`guardian extension native-host install --extension-id=${GUARDIAN_HALO_CHROME_EXTENSION_ID}`,
|
|
17217
|
+
"guardian pair --json"
|
|
17203
17218
|
]
|
|
17204
17219
|
};
|
|
17205
17220
|
return flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
@@ -17212,7 +17227,8 @@ async function installGuardian(flags, options = {}) {
|
|
|
17212
17227
|
`First run: ${GUARDIAN_FIRST_RUN_COMMAND}`,
|
|
17213
17228
|
`Open Control Tower: ${GUARDIAN_OPEN_CONTROL_TOWER_COMMAND}`,
|
|
17214
17229
|
`No global install: ${GUARDIAN_NPX_SETUP_COMMAND}`,
|
|
17215
|
-
"Next: guardian setup;
|
|
17230
|
+
"Next: guardian setup; open ContextECF Halo and click Connect; guardian open",
|
|
17231
|
+
"Help: run guardian doctor if setup needs attention."
|
|
17216
17232
|
].join("\n")}
|
|
17217
17233
|
`;
|
|
17218
17234
|
}
|
|
@@ -17220,8 +17236,9 @@ async function runSetupCommand(flags, options) {
|
|
|
17220
17236
|
const stdout = options.stdout ?? process.stdout;
|
|
17221
17237
|
const stderr = options.stderr ?? process.stderr;
|
|
17222
17238
|
const home = resolveGuardianHome(options.env, options.platform);
|
|
17223
|
-
const
|
|
17224
|
-
|
|
17239
|
+
const requestedExtensionId = stringFlag(flags, "extension-id") ?? stringFlag(flags, "id");
|
|
17240
|
+
const extensionId = requestedExtensionId ?? GUARDIAN_HALO_CHROME_EXTENSION_ID;
|
|
17241
|
+
if (requestedExtensionId && !isValidChromeExtensionId(requestedExtensionId)) {
|
|
17225
17242
|
write(
|
|
17226
17243
|
stderr,
|
|
17227
17244
|
"Invalid --extension-id. Chrome extension ids must be 32 lowercase letters from a-p.\n"
|
|
@@ -17277,10 +17294,80 @@ async function runSetupCommand(flags, options) {
|
|
|
17277
17294
|
}
|
|
17278
17295
|
launch = JSON.parse(launchOutput);
|
|
17279
17296
|
}
|
|
17297
|
+
const requestedHost = stringFlag(flags, "host") ?? "127.0.0.1";
|
|
17298
|
+
const requestedPort = numberFlag(flags, "port") ?? 4317;
|
|
17299
|
+
const requestedControlTowerUrl = `${formatDaemonBaseUrl(requestedHost, requestedPort)}/control-tower`;
|
|
17280
17300
|
const resolvedControlTowerUrl = resolveControlTowerUrl(/* @__PURE__ */ new Map(), options.env, profile);
|
|
17281
|
-
const controlTowerUrl = launch?.controlTowerUrl ?? (resolvedControlTowerUrl.ok ? resolvedControlTowerUrl.url : GUARDIAN_DEFAULT_CONTROL_TOWER_URL);
|
|
17301
|
+
const controlTowerUrl = launch?.controlTowerUrl ?? (flags.has("host") || flags.has("port") ? requestedControlTowerUrl : resolvedControlTowerUrl.ok ? resolvedControlTowerUrl.url : GUARDIAN_DEFAULT_CONTROL_TOWER_URL);
|
|
17302
|
+
if (!launch && (flags.has("host") || flags.has("port"))) {
|
|
17303
|
+
profile.control_tower = {
|
|
17304
|
+
url: controlTowerUrl,
|
|
17305
|
+
source: "env_or_flag"
|
|
17306
|
+
};
|
|
17307
|
+
await writeProfile(home, profile);
|
|
17308
|
+
}
|
|
17282
17309
|
const setupUrl = buildControlTowerSetupUrl(controlTowerUrl);
|
|
17283
|
-
const nativeHostCommand = `guardian extension native-host install --extension-id=${extensionId
|
|
17310
|
+
const nativeHostCommand = `guardian extension native-host install --extension-id=${extensionId}`;
|
|
17311
|
+
let browserHelper;
|
|
17312
|
+
if (flags.has("no-browser-connection") || flags.has("no-browser-helper")) {
|
|
17313
|
+
browserHelper = {
|
|
17314
|
+
success: false,
|
|
17315
|
+
skipped: true,
|
|
17316
|
+
tokenPrinted: false
|
|
17317
|
+
};
|
|
17318
|
+
} else {
|
|
17319
|
+
let nativeHostOutput = "";
|
|
17320
|
+
let nativeHostError = "";
|
|
17321
|
+
const nativeHostFlags = new Map([
|
|
17322
|
+
["json", true],
|
|
17323
|
+
["force", true],
|
|
17324
|
+
["extension-id", extensionId],
|
|
17325
|
+
...copyStringFlags(flags, ["browser", "target"])
|
|
17326
|
+
]);
|
|
17327
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
17328
|
+
nativeHostFlags.set("write-registry", true);
|
|
17329
|
+
nativeHostFlags.set("yes", true);
|
|
17330
|
+
}
|
|
17331
|
+
const nativeHostExitCode = await runExtensionCommand(
|
|
17332
|
+
["native-host", "install"],
|
|
17333
|
+
nativeHostFlags,
|
|
17334
|
+
{
|
|
17335
|
+
...options,
|
|
17336
|
+
stdout: {
|
|
17337
|
+
write: (chunk) => {
|
|
17338
|
+
nativeHostOutput += chunk.toString();
|
|
17339
|
+
return true;
|
|
17340
|
+
}
|
|
17341
|
+
},
|
|
17342
|
+
stderr: {
|
|
17343
|
+
write: (chunk) => {
|
|
17344
|
+
nativeHostError += chunk.toString();
|
|
17345
|
+
return true;
|
|
17346
|
+
}
|
|
17347
|
+
}
|
|
17348
|
+
}
|
|
17349
|
+
);
|
|
17350
|
+
if (nativeHostExitCode === 0) {
|
|
17351
|
+
const nativeHost = JSON.parse(nativeHostOutput);
|
|
17352
|
+
browserHelper = {
|
|
17353
|
+
success: true,
|
|
17354
|
+
skipped: false,
|
|
17355
|
+
browser: nativeHost.browser,
|
|
17356
|
+
hostName: nativeHost.hostName,
|
|
17357
|
+
manifestPath: nativeHost.manifestPath,
|
|
17358
|
+
allowedOrigins: nativeHost.allowedOrigins,
|
|
17359
|
+
tokenPrinted: nativeHost.tokenPrinted
|
|
17360
|
+
};
|
|
17361
|
+
} else {
|
|
17362
|
+
browserHelper = {
|
|
17363
|
+
success: false,
|
|
17364
|
+
skipped: false,
|
|
17365
|
+
error: nativeHostError.trim() || "Guardian browser connection setup failed.",
|
|
17366
|
+
exitCode: nativeHostExitCode,
|
|
17367
|
+
tokenPrinted: false
|
|
17368
|
+
};
|
|
17369
|
+
}
|
|
17370
|
+
}
|
|
17284
17371
|
const result = {
|
|
17285
17372
|
schema_version: "contextecf/project-guardian-setup/v1",
|
|
17286
17373
|
success: true,
|
|
@@ -17330,10 +17417,14 @@ async function runSetupCommand(flags, options) {
|
|
|
17330
17417
|
},
|
|
17331
17418
|
browserSetup: {
|
|
17332
17419
|
setupUrl,
|
|
17333
|
-
extensionId
|
|
17420
|
+
extensionId,
|
|
17421
|
+
defaultExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
17334
17422
|
opened: false,
|
|
17335
17423
|
tokenPrinted: false,
|
|
17336
|
-
nativeHostCommand
|
|
17424
|
+
nativeHostCommand,
|
|
17425
|
+
connectInstruction: "Open ContextECF Halo and click Connect. No setup code is needed.",
|
|
17426
|
+
manualFallbackCommand: "guardian pair --json",
|
|
17427
|
+
nativeHost: browserHelper
|
|
17337
17428
|
},
|
|
17338
17429
|
nextCommands: [
|
|
17339
17430
|
"guardian status",
|
|
@@ -17342,12 +17433,10 @@ async function runSetupCommand(flags, options) {
|
|
|
17342
17433
|
"guardian policy list",
|
|
17343
17434
|
"guardian posture list",
|
|
17344
17435
|
"guardian preferences show",
|
|
17345
|
-
"guardian extension open-setup",
|
|
17346
|
-
"guardian daemon pair --json",
|
|
17347
17436
|
"guardian service install",
|
|
17348
|
-
"guardian key-store install --yes"
|
|
17349
|
-
|
|
17350
|
-
]
|
|
17437
|
+
"guardian key-store install --yes"
|
|
17438
|
+
],
|
|
17439
|
+
advancedCommands: ["guardian extension connect", nativeHostCommand, "guardian pair --json"]
|
|
17351
17440
|
};
|
|
17352
17441
|
write(
|
|
17353
17442
|
stdout,
|
|
@@ -17360,8 +17449,11 @@ async function runSetupCommand(flags, options) {
|
|
|
17360
17449
|
result.launch.opened ? `Opening: ${result.launch.controlTowerUrl}` : void 0,
|
|
17361
17450
|
`MCP artifacts: ${result.mcp.writtenFiles.length > 0 ? result.mcp.writtenFiles.join(", ") : "skipped" in result.mcp && result.mcp.skipped ? "skipped" : "none"}`,
|
|
17362
17451
|
`Browser setup: ${result.browserSetup.setupUrl}`,
|
|
17452
|
+
result.browserSetup.nativeHost.success ? `Browser connection: ready for ContextECF Halo (${result.browserSetup.extensionId})` : result.browserSetup.nativeHost.skipped ? "Browser connection: skipped" : `Browser connection: needs attention${result.browserSetup.nativeHost.error ? ` (${result.browserSetup.nativeHost.error})` : ""}`,
|
|
17453
|
+
"Extension setup: open ContextECF Halo and click Connect. No setup code needed.",
|
|
17363
17454
|
"Token printed: no",
|
|
17364
|
-
`Next: ${result.nextCommands.join("; ")}
|
|
17455
|
+
`Next: ${result.nextCommands.join("; ")}`,
|
|
17456
|
+
"Help: run guardian connect if the browser says Local Guardian is not connected."
|
|
17365
17457
|
].filter(Boolean).join("\n")}
|
|
17366
17458
|
`
|
|
17367
17459
|
);
|
|
@@ -17436,8 +17528,8 @@ function buildGuardianDistributionReadiness(npmPreviewReady) {
|
|
|
17436
17528
|
},
|
|
17437
17529
|
aiToolIntegration: {
|
|
17438
17530
|
mcpInstallCommand: "guardian mcp install --client=all --write",
|
|
17439
|
-
browserSetupCommand: "guardian
|
|
17440
|
-
nativeHostInstallCommand:
|
|
17531
|
+
browserSetupCommand: "guardian connect",
|
|
17532
|
+
nativeHostInstallCommand: `guardian extension native-host install --extension-id=${GUARDIAN_HALO_CHROME_EXTENSION_ID}`
|
|
17441
17533
|
},
|
|
17442
17534
|
browserStoreHandoff: {
|
|
17443
17535
|
status: npmPreviewReady ? "operator_handoff_ready_not_submitted" : "blocked_until_preview_ready",
|
|
@@ -17453,15 +17545,17 @@ function buildGuardianDistributionReadiness(npmPreviewReady) {
|
|
|
17453
17545
|
workflow: GUARDIAN_RELEASE_DISPATCH_WORKFLOW,
|
|
17454
17546
|
dryRunCommand: buildGuardianReleaseDispatchCommand(true),
|
|
17455
17547
|
publishCommand: buildGuardianReleaseDispatchCommand(false),
|
|
17456
|
-
|
|
17548
|
+
defaultPublishAuth: GUARDIAN_DEFAULT_NPM_PUBLISH_AUTH,
|
|
17549
|
+
tokenFallbackCredentialName: "NPM_TOKEN",
|
|
17457
17550
|
channels: ["all", "npm", "browser", "evidence"],
|
|
17458
17551
|
defaultDockerSmoke: true,
|
|
17459
17552
|
provenance: "github_actions_oidc_supported",
|
|
17460
17553
|
status: npmPreviewReady ? "ready_for_dispatch_after_preview_evidence" : "blocked_until_preview_release_evidence_passes",
|
|
17461
17554
|
boundaries: [
|
|
17462
17555
|
"Use dry_run=true before public publication.",
|
|
17463
|
-
"dry_run=false
|
|
17464
|
-
"The
|
|
17556
|
+
"dry_run=false defaults to npm Trusted Publishing and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
17557
|
+
"The token fallback route remains available with publish_auth=token and requires the repository secret NPM_TOKEN.",
|
|
17558
|
+
"The workflow can publish the npm preview package, upload evidence, submit future Chrome Web Store updates for review when browser_store_publish=api, and run public install smokes; it does not sign native installers or prove future browser-store approval.",
|
|
17465
17559
|
"GitHub Actions OIDC/provenance applies to CI release context; manual local publishes are recorded separately and should not claim CI provenance."
|
|
17466
17560
|
]
|
|
17467
17561
|
},
|
|
@@ -17482,12 +17576,16 @@ function buildGuardianReleaseDispatchCommand(dryRun) {
|
|
|
17482
17576
|
});
|
|
17483
17577
|
}
|
|
17484
17578
|
function buildGuardianReleaseDispatchCommandFor(input2) {
|
|
17579
|
+
const publishAuth = input2.publishAuth ?? GUARDIAN_DEFAULT_NPM_PUBLISH_AUTH;
|
|
17580
|
+
const browserStorePublish = input2.browserStorePublish ?? "manual";
|
|
17485
17581
|
return [
|
|
17486
17582
|
`gh workflow run ${GUARDIAN_RELEASE_DISPATCH_WORKFLOW} \\`,
|
|
17487
17583
|
` -f release_version=${input2.releaseVersion} \\`,
|
|
17488
17584
|
` -f channels=${input2.channels} \\`,
|
|
17489
17585
|
` -f dry_run=${input2.dryRun ? "true" : "false"} \\`,
|
|
17490
|
-
` -f run_docker_smoke=${input2.runDockerSmoke ? "true" : "false"}
|
|
17586
|
+
` -f run_docker_smoke=${input2.runDockerSmoke ? "true" : "false"} \\`,
|
|
17587
|
+
` -f publish_auth=${publishAuth} \\`,
|
|
17588
|
+
` -f browser_store_publish=${browserStorePublish}`
|
|
17491
17589
|
].join("\n");
|
|
17492
17590
|
}
|
|
17493
17591
|
async function runReleaseCommand(argv, flags, options) {
|
|
@@ -17594,11 +17692,23 @@ async function runReleaseCommand(argv, flags, options) {
|
|
|
17594
17692
|
}
|
|
17595
17693
|
const dryRun = flags.has("publish") ? false : booleanFlag(flags, "dry-run", true);
|
|
17596
17694
|
const runDockerSmoke = booleanFlag(flags, "run-docker-smoke", true);
|
|
17695
|
+
const publishAuth = stringFlag(flags, "publish-auth") ?? GUARDIAN_DEFAULT_NPM_PUBLISH_AUTH;
|
|
17696
|
+
if (!isValidReleasePublishAuth(publishAuth)) {
|
|
17697
|
+
write(stderr, "Invalid --publish-auth. Expected one of: token, trusted_publisher.\n");
|
|
17698
|
+
return 1;
|
|
17699
|
+
}
|
|
17700
|
+
const browserStorePublish = stringFlag(flags, "browser-store-publish") ?? "manual";
|
|
17701
|
+
if (!isValidBrowserStorePublish(browserStorePublish)) {
|
|
17702
|
+
write(stderr, "Invalid --browser-store-publish. Expected one of: manual, api.\n");
|
|
17703
|
+
return 1;
|
|
17704
|
+
}
|
|
17597
17705
|
const result = buildGuardianReleaseDispatchCommandResult({
|
|
17598
17706
|
releaseVersion,
|
|
17599
17707
|
channels,
|
|
17600
17708
|
dryRun,
|
|
17601
|
-
runDockerSmoke
|
|
17709
|
+
runDockerSmoke,
|
|
17710
|
+
publishAuth,
|
|
17711
|
+
browserStorePublish
|
|
17602
17712
|
});
|
|
17603
17713
|
write(stdout, renderGuardianReleaseDispatchCommand(result, flags));
|
|
17604
17714
|
return 0;
|
|
@@ -17666,6 +17776,7 @@ function renderGuardianReleaseHelp(result, flags) {
|
|
|
17666
17776
|
`;
|
|
17667
17777
|
}
|
|
17668
17778
|
function buildGuardianReleaseDispatchCommandResult(input2) {
|
|
17779
|
+
const npmLaneSelected = input2.channels === "all" || input2.channels === "npm";
|
|
17669
17780
|
return {
|
|
17670
17781
|
schema_version: "contextecf/project-guardian-release-dispatch-command/v1",
|
|
17671
17782
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
@@ -17674,13 +17785,21 @@ function buildGuardianReleaseDispatchCommandResult(input2) {
|
|
|
17674
17785
|
channels: input2.channels,
|
|
17675
17786
|
dryRun: input2.dryRun,
|
|
17676
17787
|
runDockerSmoke: input2.runDockerSmoke,
|
|
17788
|
+
publishAuth: input2.publishAuth,
|
|
17789
|
+
browserStorePublish: input2.browserStorePublish,
|
|
17677
17790
|
command: buildGuardianReleaseDispatchCommandFor(input2),
|
|
17678
|
-
...input2.dryRun ? {} : { requiredPublishCredentialName: "NPM_TOKEN" },
|
|
17791
|
+
...input2.dryRun || input2.publishAuth === "trusted_publisher" || !npmLaneSelected ? {} : { requiredPublishCredentialName: "NPM_TOKEN" },
|
|
17792
|
+
...input2.browserStorePublish === "api" && !input2.dryRun ? {
|
|
17793
|
+
requiredBrowserStoreCredentialNames: [...GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES]
|
|
17794
|
+
} : {},
|
|
17679
17795
|
provenance: "github_actions_oidc_supported",
|
|
17680
17796
|
boundaries: [
|
|
17681
17797
|
"Use dry_run=true before public publication.",
|
|
17682
|
-
"dry_run=false requires the repository secret NPM_TOKEN and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
17683
|
-
"
|
|
17798
|
+
"dry_run=false with publish_auth=token requires the repository secret NPM_TOKEN and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
17799
|
+
"dry_run=false with publish_auth=trusted_publisher requires npm Trusted Publishing to be configured for this GitHub Actions workflow and package.",
|
|
17800
|
+
"browser_store_publish=manual keeps Chrome Web Store upload as an operator ZIP handoff.",
|
|
17801
|
+
"browser_store_publish=api uploads an existing Chrome Web Store item update and submits it for review when Chrome Web Store API secrets are configured.",
|
|
17802
|
+
"This dispatch can publish the npm preview package, upload evidence, submit future Chrome Web Store updates for review, and run public install smokes; it does not sign native installers or prove future browser-store approval.",
|
|
17684
17803
|
"A release-dispatch command is operator guidance, not production-gate proof."
|
|
17685
17804
|
],
|
|
17686
17805
|
tokenPrinted: false,
|
|
@@ -17699,7 +17818,10 @@ function renderGuardianReleaseDispatchCommand(result, flags) {
|
|
|
17699
17818
|
`Workflow: ${result.workflow}`,
|
|
17700
17819
|
`Channels: ${result.channels}`,
|
|
17701
17820
|
`Docker smoke: ${result.runDockerSmoke ? "yes" : "no"}`,
|
|
17702
|
-
|
|
17821
|
+
`Publish auth: ${result.publishAuth}`,
|
|
17822
|
+
`Browser store publish: ${result.browserStorePublish}`,
|
|
17823
|
+
result.requiredPublishCredentialName ? `Required repository secret: ${result.requiredPublishCredentialName}` : result.publishAuth === "trusted_publisher" && !result.dryRun ? "Required npm setup: Trusted Publisher connection" : "Required repository secret: not needed for dry run",
|
|
17824
|
+
result.requiredBrowserStoreCredentialNames ? `Required browser-store secrets: ${result.requiredBrowserStoreCredentialNames.join(", ")}` : "Required browser-store secrets: not needed for manual browser handoff",
|
|
17703
17825
|
"Command:",
|
|
17704
17826
|
result.command,
|
|
17705
17827
|
"Boundaries:",
|
|
@@ -17714,6 +17836,7 @@ function buildGuardianBrowserStoreHandoffCommandResult() {
|
|
|
17714
17836
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
17715
17837
|
status: "chrome_distribution_proven_next_submission_pack_ready",
|
|
17716
17838
|
primaryStore: "chrome_web_store",
|
|
17839
|
+
publicListingUrl: GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL,
|
|
17717
17840
|
productionGate: {
|
|
17718
17841
|
id: "browser-store-distribution",
|
|
17719
17842
|
status: "proven",
|
|
@@ -17780,6 +17903,7 @@ function renderGuardianBrowserStoreHandoffCommand(result, flags) {
|
|
|
17780
17903
|
"Project Guardian Browser Store Handoff",
|
|
17781
17904
|
`Status: ${result.status}`,
|
|
17782
17905
|
`Primary store: ${result.primaryStore}`,
|
|
17906
|
+
`Public listing: ${result.publicListingUrl}`,
|
|
17783
17907
|
`Production gate: ${result.productionGate.id} (${result.productionGate.status})`,
|
|
17784
17908
|
"1. Build submission pack:",
|
|
17785
17909
|
result.commands.submissionPack,
|
|
@@ -17800,15 +17924,25 @@ function renderGuardianBrowserStoreHandoffCommand(result, flags) {
|
|
|
17800
17924
|
`;
|
|
17801
17925
|
}
|
|
17802
17926
|
function buildGuardianReleaseSecretSetup() {
|
|
17927
|
+
const chromeWebStoreSecretMatcher = GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES.join("|");
|
|
17803
17928
|
return {
|
|
17804
17929
|
schema_version: "contextecf/project-guardian-release-secret-setup/v1",
|
|
17805
17930
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
17806
17931
|
repository: GUARDIAN_RELEASE_REPOSITORY,
|
|
17807
|
-
|
|
17932
|
+
defaultPublishAuth: GUARDIAN_DEFAULT_NPM_PUBLISH_AUTH,
|
|
17933
|
+
requiredForPublish: "npm_trusted_publisher_connection",
|
|
17934
|
+
tokenFallbackCredentialName: "NPM_TOKEN",
|
|
17935
|
+
requiredForBrowserStoreApi: GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES,
|
|
17808
17936
|
githubActionsSecretUrl: `https://github.com/${GUARDIAN_RELEASE_REPOSITORY}/settings/secrets/actions/new`,
|
|
17809
17937
|
commands: {
|
|
17810
17938
|
setNpmToken: `gh secret set NPM_TOKEN --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17939
|
+
setChromeWebStoreClientId: `gh secret set CHROME_WEB_STORE_CLIENT_ID --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17940
|
+
setChromeWebStoreClientSecret: `gh secret set CHROME_WEB_STORE_CLIENT_SECRET --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17941
|
+
setChromeWebStoreRefreshToken: `gh secret set CHROME_WEB_STORE_REFRESH_TOKEN --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17942
|
+
setChromeWebStorePublisherId: `gh secret set CHROME_WEB_STORE_PUBLISHER_ID --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17943
|
+
setChromeWebStoreExtensionId: `gh secret set CHROME_WEB_STORE_EXTENSION_ID --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17811
17944
|
verifyNpmTokenPresence: `gh secret list --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions | rg '^NPM_TOKEN\\b'`,
|
|
17945
|
+
verifyChromeWebStoreSecretsPresence: `gh secret list --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions | rg '^(${chromeWebStoreSecretMatcher})\\b'`,
|
|
17812
17946
|
dryRunDispatch: buildGuardianReleaseDispatchCommandFor({
|
|
17813
17947
|
dryRun: true,
|
|
17814
17948
|
releaseVersion: "<version>",
|
|
@@ -17820,13 +17954,24 @@ function buildGuardianReleaseSecretSetup() {
|
|
|
17820
17954
|
releaseVersion: "<version>",
|
|
17821
17955
|
channels: "all",
|
|
17822
17956
|
runDockerSmoke: true
|
|
17957
|
+
}),
|
|
17958
|
+
browserStoreApiPublishDispatch: buildGuardianReleaseDispatchCommandFor({
|
|
17959
|
+
dryRun: false,
|
|
17960
|
+
releaseVersion: "<version>",
|
|
17961
|
+
channels: "browser",
|
|
17962
|
+
runDockerSmoke: true,
|
|
17963
|
+
publishAuth: "trusted_publisher",
|
|
17964
|
+
browserStorePublish: "api"
|
|
17823
17965
|
})
|
|
17824
17966
|
},
|
|
17825
17967
|
boundaries: [
|
|
17826
|
-
"
|
|
17968
|
+
"npm Trusted Publishing is the default Guardian release path and uses this GitHub Actions workflow identity.",
|
|
17969
|
+
"NPM_TOKEN is a fallback credential only; when used, it belongs only in GitHub Actions repository secrets for guarded dry_run=false npm publication.",
|
|
17970
|
+
"Chrome Web Store API OAuth values belong only in GitHub Actions repository secrets for guarded browser_store_publish=api upload/submission.",
|
|
17827
17971
|
"Do not paste npm tokens, OTP values, .npmrc contents, signing keys, browser-store credentials, raw prompts, raw responses, private policy packs, or customer content into chat, PR bodies, release evidence, or CLI output.",
|
|
17828
|
-
"Use dry_run=true before dry_run=false; dry-run dispatch does not require
|
|
17829
|
-
"Adding NPM_TOKEN enables the npm preview publish lane only; it does not close signed-installer, browser-store, high-risk desktop, or autonomous app production gates."
|
|
17972
|
+
"Use dry_run=true before dry_run=false; dry-run dispatch does not require npm publish credentials.",
|
|
17973
|
+
"Adding NPM_TOKEN enables the npm preview publish lane only; it does not close signed-installer, browser-store, high-risk desktop, or autonomous app production gates.",
|
|
17974
|
+
"Chrome Web Store API secrets enable browser update upload/submission only; official review approval evidence is still required for production browser-store claims."
|
|
17830
17975
|
],
|
|
17831
17976
|
tokenPrinted: false,
|
|
17832
17977
|
rawContentIncluded: false
|
|
@@ -17840,16 +17985,28 @@ function renderGuardianReleaseSecretSetup(result, flags) {
|
|
|
17840
17985
|
return `${[
|
|
17841
17986
|
"Project Guardian Release Secret Setup",
|
|
17842
17987
|
`Repository: ${result.repository}`,
|
|
17843
|
-
|
|
17988
|
+
"Default npm publish setup: Trusted Publisher connection",
|
|
17989
|
+
`Fallback npm publish secret: ${result.tokenFallbackCredentialName}`,
|
|
17990
|
+
`Required for Chrome Web Store API: ${result.requiredForBrowserStoreApi.join(", ")}`,
|
|
17844
17991
|
`GitHub Actions secret page: ${result.githubActionsSecretUrl}`,
|
|
17845
|
-
"
|
|
17992
|
+
"NPM publish secret:",
|
|
17846
17993
|
result.commands.setNpmToken,
|
|
17847
|
-
"Verify secret presence:",
|
|
17994
|
+
"Verify npm secret presence:",
|
|
17848
17995
|
result.commands.verifyNpmTokenPresence,
|
|
17996
|
+
"Chrome Web Store API secrets:",
|
|
17997
|
+
result.commands.setChromeWebStoreClientId,
|
|
17998
|
+
result.commands.setChromeWebStoreClientSecret,
|
|
17999
|
+
result.commands.setChromeWebStoreRefreshToken,
|
|
18000
|
+
result.commands.setChromeWebStorePublisherId,
|
|
18001
|
+
result.commands.setChromeWebStoreExtensionId,
|
|
18002
|
+
"Verify Chrome Web Store API secret presence:",
|
|
18003
|
+
result.commands.verifyChromeWebStoreSecretsPresence,
|
|
17849
18004
|
"Dry-run release command:",
|
|
17850
18005
|
result.commands.dryRunDispatch,
|
|
17851
|
-
"
|
|
18006
|
+
"Default publish command with manual browser-store upload:",
|
|
17852
18007
|
result.commands.publishDispatch,
|
|
18008
|
+
"Browser-store API publish command:",
|
|
18009
|
+
result.commands.browserStoreApiPublishDispatch,
|
|
17853
18010
|
"Boundaries:",
|
|
17854
18011
|
...result.boundaries.map((boundary) => `- ${boundary}`),
|
|
17855
18012
|
"Token printed: no"
|
|
@@ -17880,7 +18037,8 @@ function buildGuardianReleaseChecklist(releaseVersion) {
|
|
|
17880
18037
|
browserStoreHandoff: "guardian release browser-store",
|
|
17881
18038
|
readiness: "guardian readiness"
|
|
17882
18039
|
},
|
|
17883
|
-
|
|
18040
|
+
defaultPublishAuth: GUARDIAN_DEFAULT_NPM_PUBLISH_AUTH,
|
|
18041
|
+
tokenFallbackCredentialName: "NPM_TOKEN",
|
|
17884
18042
|
evidenceOutputs: [
|
|
17885
18043
|
"release/evidence/guardian-release-dispatch-summary.json",
|
|
17886
18044
|
"release/evidence/guardian-npm-publish-execution-summary.json",
|
|
@@ -17897,7 +18055,8 @@ function buildGuardianReleaseChecklist(releaseVersion) {
|
|
|
17897
18055
|
boundaries: [
|
|
17898
18056
|
"This checklist is an operator command surface for Guardian preview releases; it is not production-gate proof.",
|
|
17899
18057
|
"Run the dry-run dispatch before any dry_run=false publish dispatch.",
|
|
17900
|
-
"dry_run=false
|
|
18058
|
+
"dry_run=false defaults to npm Trusted Publishing and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
18059
|
+
"The token fallback route remains available with publish_auth=token and requires NPM_TOKEN in GitHub Actions repository secrets.",
|
|
17901
18060
|
"The checklist does not sign native installers, submit browser-store packages, execute high-risk desktop actions, or enable autonomous local app execution.",
|
|
17902
18061
|
"Full production claims remain blocked until every not-proven production gate has real validated external evidence."
|
|
17903
18062
|
],
|
|
@@ -17935,7 +18094,7 @@ function buildGuardianReleaseFlow(releaseVersion) {
|
|
|
17935
18094
|
},
|
|
17936
18095
|
{
|
|
17937
18096
|
id: "verify-secrets",
|
|
17938
|
-
label: "Verify npm
|
|
18097
|
+
label: "Verify npm Trusted Publisher setup or token fallback",
|
|
17939
18098
|
command: "guardian release secrets",
|
|
17940
18099
|
credentialRequired: false
|
|
17941
18100
|
},
|
|
@@ -17949,7 +18108,7 @@ function buildGuardianReleaseFlow(releaseVersion) {
|
|
|
17949
18108
|
id: "publish-dispatch",
|
|
17950
18109
|
label: "Run the guarded publish dispatch after dry-run evidence passes",
|
|
17951
18110
|
command: publishDispatch,
|
|
17952
|
-
credentialRequired:
|
|
18111
|
+
credentialRequired: false
|
|
17953
18112
|
},
|
|
17954
18113
|
{
|
|
17955
18114
|
id: "verify-public-release",
|
|
@@ -17985,7 +18144,8 @@ function buildGuardianReleaseFlow(releaseVersion) {
|
|
|
17985
18144
|
})),
|
|
17986
18145
|
boundaries: [
|
|
17987
18146
|
"This release flow is copy-paste operator guidance only; it is not release evidence or production-gate proof.",
|
|
17988
|
-
"Publish dispatch
|
|
18147
|
+
"Publish dispatch defaults to npm Trusted Publishing and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
18148
|
+
"The token fallback route remains available with publish_auth=token and requires NPM_TOKEN in GitHub Actions repository secrets.",
|
|
17989
18149
|
"Run the dry-run dispatch before the guarded dry_run=false publish dispatch.",
|
|
17990
18150
|
"Public npm preview publication and local install smoke prove the CLI preview lane only.",
|
|
17991
18151
|
"Signed native installers, high-risk desktop action execution, and autonomous local app execution remain separate not-proven production gates until real external evidence is collected and validated."
|
|
@@ -18034,7 +18194,8 @@ function renderGuardianReleaseChecklist(result, flags) {
|
|
|
18034
18194
|
`Release: ${result.releaseVersion}`,
|
|
18035
18195
|
`Repository: ${result.repository}`,
|
|
18036
18196
|
`Status: ${result.status}`,
|
|
18037
|
-
|
|
18197
|
+
"Default npm publish setup: Trusted Publisher connection",
|
|
18198
|
+
`Fallback npm publish secret: ${result.tokenFallbackCredentialName}`,
|
|
18038
18199
|
"1. Secret setup:",
|
|
18039
18200
|
result.operatorCommands.secretSetup,
|
|
18040
18201
|
"2. Dry-run dispatch:",
|
|
@@ -18070,9 +18231,9 @@ function buildGuardianReleaseVerify(releaseVersion) {
|
|
|
18070
18231
|
commands: {
|
|
18071
18232
|
registryVersion: `npm view ${GUARDIAN_NPM_PACKAGE_NAME}@${releaseVersion} version --registry=${GUARDIAN_NPM_REGISTRY}`,
|
|
18072
18233
|
publicInstall: `npm install -g ${GUARDIAN_NPM_PACKAGE_NAME}@${releaseVersion}`,
|
|
18234
|
+
chromeWebStoreListing: GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL,
|
|
18073
18235
|
installedVersion: "guardian --version",
|
|
18074
18236
|
setup: "guardian setup",
|
|
18075
|
-
launch: "guardian launch",
|
|
18076
18237
|
status: "guardian status",
|
|
18077
18238
|
openControlTower: "guardian open",
|
|
18078
18239
|
copyPasteLocalSmoke,
|
|
@@ -18101,9 +18262,8 @@ function buildGuardianReleasePaste(releaseVersion) {
|
|
|
18101
18262
|
"guardian --version",
|
|
18102
18263
|
"guardian stop",
|
|
18103
18264
|
"guardian setup",
|
|
18104
|
-
"guardian
|
|
18105
|
-
"guardian open"
|
|
18106
|
-
"guardian status"
|
|
18265
|
+
"guardian status",
|
|
18266
|
+
"guardian open"
|
|
18107
18267
|
];
|
|
18108
18268
|
return {
|
|
18109
18269
|
schema_version: "contextecf/project-guardian-release-paste/v1",
|
|
@@ -18114,9 +18274,9 @@ function buildGuardianReleasePaste(releaseVersion) {
|
|
|
18114
18274
|
command: commands.join("\n"),
|
|
18115
18275
|
commands,
|
|
18116
18276
|
boundaries: [
|
|
18117
|
-
"This paste block verifies public npm visibility, global install, first-run setup,
|
|
18277
|
+
"This paste block verifies public npm visibility, global install, stale-process cleanup, first-run setup, Local Guardian status, and Control Tower open.",
|
|
18118
18278
|
"It does not publish to npm and must not include npm tokens, OTP values, .npmrc contents, signing keys, browser-store credentials, raw prompts, raw responses, private policy packs, or customer content.",
|
|
18119
|
-
"guardian stop is included before setup so stale
|
|
18279
|
+
"guardian stop is included before setup so stale Local Guardian processes or local token mismatches are cleared before setup relaunches the local service."
|
|
18120
18280
|
],
|
|
18121
18281
|
tokenPrinted: false,
|
|
18122
18282
|
rawContentIncluded: false
|
|
@@ -18143,18 +18303,19 @@ function renderGuardianReleaseVerify(result, flags) {
|
|
|
18143
18303
|
`Status: ${result.status}`,
|
|
18144
18304
|
"1. Confirm registry visibility:",
|
|
18145
18305
|
result.commands.registryVersion,
|
|
18146
|
-
"2.
|
|
18306
|
+
"2. Confirm public Chrome Web Store listing:",
|
|
18307
|
+
result.commands.chromeWebStoreListing,
|
|
18308
|
+
"3. Install the published version:",
|
|
18147
18309
|
result.commands.publicInstall,
|
|
18148
|
-
"
|
|
18310
|
+
"4. Confirm installed CLI version:",
|
|
18149
18311
|
result.commands.installedVersion,
|
|
18150
|
-
"
|
|
18312
|
+
"5. Run first-use smoke:",
|
|
18151
18313
|
result.commands.setup,
|
|
18152
|
-
result.commands.launch,
|
|
18153
18314
|
result.commands.status,
|
|
18154
18315
|
result.commands.openControlTower,
|
|
18155
|
-
"
|
|
18316
|
+
"6. Copy-paste local smoke block:",
|
|
18156
18317
|
result.commands.copyPasteLocalSmoke,
|
|
18157
|
-
"
|
|
18318
|
+
"7. Verify release evidence:",
|
|
18158
18319
|
result.commands.publishStatusVerification,
|
|
18159
18320
|
result.commands.finishLineStatus,
|
|
18160
18321
|
"Evidence outputs:",
|
|
@@ -18171,6 +18332,12 @@ function isValidReleaseVersion(value) {
|
|
|
18171
18332
|
function isValidReleaseChannels(value) {
|
|
18172
18333
|
return ["all", "npm", "browser", "evidence"].includes(value);
|
|
18173
18334
|
}
|
|
18335
|
+
function isValidReleasePublishAuth(value) {
|
|
18336
|
+
return value === "token" || value === "trusted_publisher";
|
|
18337
|
+
}
|
|
18338
|
+
function isValidBrowserStorePublish(value) {
|
|
18339
|
+
return value === "manual" || value === "api";
|
|
18340
|
+
}
|
|
18174
18341
|
function renderGuardianProductionReadiness(result, flags) {
|
|
18175
18342
|
if (flags.has("json")) {
|
|
18176
18343
|
return `${JSON.stringify(result, null, 2)}
|
|
@@ -18273,9 +18440,9 @@ async function statusGuardian(flags, options) {
|
|
|
18273
18440
|
`Profile: ${home}`,
|
|
18274
18441
|
`Runtime profile: ${result.runtime.profileDaemonStatus}`,
|
|
18275
18442
|
`Control Tower: ${result.controlTowerUrl ?? "not configured"}`,
|
|
18276
|
-
`
|
|
18277
|
-
result.daemon.error ? `
|
|
18278
|
-
result.daemonVersionMismatch ? `
|
|
18443
|
+
`Local Guardian: ${result.daemon.status}${result.daemon.statusCode ? ` (HTTP ${result.daemon.statusCode})` : ""}`,
|
|
18444
|
+
result.daemon.error ? `Connection detail: ${result.daemon.error}` : void 0,
|
|
18445
|
+
result.daemonVersionMismatch ? `Local Guardian version: ${result.daemonVersionMismatch.daemonVersion} (this CLI is ${result.daemonVersionMismatch.cliVersion} \u2014 versions differ, likely because the CLI was reinstalled or changed version while Local Guardian kept running; run guardian stop && guardian launch to restart it on the current version)` : void 0,
|
|
18279
18446
|
`MCP stdio: ${result.runtime.mcpStdioAvailable ? "available" : "unavailable"}`,
|
|
18280
18447
|
`Posture Profile: ${result.postureProfile.selected}`,
|
|
18281
18448
|
`Prompt Coach: ${result.preferences.promptCoachMode}`,
|
|
@@ -18382,7 +18549,7 @@ async function runLaunchCommand(flags, options) {
|
|
|
18382
18549
|
nextCommands: [
|
|
18383
18550
|
"guardian open",
|
|
18384
18551
|
"guardian status",
|
|
18385
|
-
"
|
|
18552
|
+
"Open ContextECF Halo and click Connect",
|
|
18386
18553
|
"guardian policy list"
|
|
18387
18554
|
]
|
|
18388
18555
|
};
|
|
@@ -18391,7 +18558,7 @@ async function runLaunchCommand(flags, options) {
|
|
|
18391
18558
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18392
18559
|
` : `${[
|
|
18393
18560
|
"Project Guardian launched.",
|
|
18394
|
-
`
|
|
18561
|
+
`Local Guardian: ${result.daemonUrl}${result.reusedExistingDaemon ? " (already running)" : ""}`,
|
|
18395
18562
|
`Control Tower: ${result.controlTowerUrl}`,
|
|
18396
18563
|
shouldOpen ? `Opening: ${result.controlTowerUrl}` : void 0,
|
|
18397
18564
|
"Next: guardian open; guardian status"
|
|
@@ -18502,14 +18669,14 @@ async function runStartCommand(flags, options) {
|
|
|
18502
18669
|
controlTowerUrl: requestedControlTowerUrl,
|
|
18503
18670
|
healthUrl,
|
|
18504
18671
|
healthStatus: "reachable",
|
|
18505
|
-
nextCommands: ["guardian open", "guardian status", "
|
|
18672
|
+
nextCommands: ["guardian open", "guardian status", "Open ContextECF Halo and click Connect"]
|
|
18506
18673
|
};
|
|
18507
18674
|
write(
|
|
18508
18675
|
stdout,
|
|
18509
18676
|
flags.has("json") ? `${JSON.stringify(result2, null, 2)}
|
|
18510
18677
|
` : `${[
|
|
18511
|
-
"Guardian
|
|
18512
|
-
`
|
|
18678
|
+
"Local Guardian already running.",
|
|
18679
|
+
`Local Guardian: ${requestedDaemonUrl}`,
|
|
18513
18680
|
`Control Tower: ${requestedControlTowerUrl}`,
|
|
18514
18681
|
"Next: guardian open"
|
|
18515
18682
|
].join("\n")}
|
|
@@ -18518,7 +18685,7 @@ async function runStartCommand(flags, options) {
|
|
|
18518
18685
|
return 0;
|
|
18519
18686
|
}
|
|
18520
18687
|
if (!options.startDetachedDaemon) {
|
|
18521
|
-
write(stderr, "Guardian
|
|
18688
|
+
write(stderr, "Guardian local background launcher is unavailable in this runtime.\n");
|
|
18522
18689
|
return 1;
|
|
18523
18690
|
}
|
|
18524
18691
|
const started = await options.startDetachedDaemon({ home, host, port: selected.port });
|
|
@@ -18539,14 +18706,14 @@ async function runStartCommand(flags, options) {
|
|
|
18539
18706
|
pid: started.pid,
|
|
18540
18707
|
healthUrl,
|
|
18541
18708
|
healthStatus: existing.error ? "unreachable" : "not_reachable",
|
|
18542
|
-
nextCommands: ["guardian open", "guardian status", "
|
|
18709
|
+
nextCommands: ["guardian open", "guardian status", "Open ContextECF Halo and click Connect"]
|
|
18543
18710
|
};
|
|
18544
18711
|
write(
|
|
18545
18712
|
stdout,
|
|
18546
18713
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18547
18714
|
` : `${[
|
|
18548
|
-
"Guardian
|
|
18549
|
-
`
|
|
18715
|
+
"Local Guardian start requested.",
|
|
18716
|
+
`Local Guardian: ${daemonUrl}`,
|
|
18550
18717
|
`Control Tower: ${controlTowerUrl}`,
|
|
18551
18718
|
started.pid ? `PID: ${started.pid}` : void 0,
|
|
18552
18719
|
"Next: guardian open"
|
|
@@ -18557,8 +18724,8 @@ async function runStartCommand(flags, options) {
|
|
|
18557
18724
|
}
|
|
18558
18725
|
function renderDaemonTokenMismatchMessage(requestedDaemonUrl, port) {
|
|
18559
18726
|
return [
|
|
18560
|
-
`Guardian found another
|
|
18561
|
-
"This usually means an older Guardian
|
|
18727
|
+
`Guardian found another local service or container at ${requestedDaemonUrl}, but it rejected this profile's local connection token.`,
|
|
18728
|
+
"This usually means an older Guardian background service or Docker demo is still using the port.",
|
|
18562
18729
|
`Recovery: stop the other process/container, or run Guardian on a different port with guardian launch --port=${port + 1}.`,
|
|
18563
18730
|
`Diagnostics: guardian status; docker ps --filter publish=${port}`,
|
|
18564
18731
|
""
|
|
@@ -18591,7 +18758,7 @@ async function runStopCommand(flags, options) {
|
|
|
18591
18758
|
if (!stopped.ok) {
|
|
18592
18759
|
write(
|
|
18593
18760
|
stderr,
|
|
18594
|
-
`Guardian
|
|
18761
|
+
`Local Guardian did not stop cleanly${stopped.statusCode ? ` (HTTP ${stopped.statusCode})` : ""}: ${stopped.error ?? "unknown_error"}
|
|
18595
18762
|
`
|
|
18596
18763
|
);
|
|
18597
18764
|
return 1;
|
|
@@ -18602,12 +18769,12 @@ async function runStopCommand(flags, options) {
|
|
|
18602
18769
|
success: true,
|
|
18603
18770
|
daemonStatus: profile.runtime.daemon_status,
|
|
18604
18771
|
stopUrl: stopUrl.url,
|
|
18605
|
-
note: "Guardian
|
|
18772
|
+
note: "Local Guardian stop requested."
|
|
18606
18773
|
};
|
|
18607
18774
|
write(
|
|
18608
18775
|
stdout,
|
|
18609
18776
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18610
|
-
` : `${["Guardian
|
|
18777
|
+
` : `${["Local Guardian stop requested.", "Status: not_started_mvp"].join("\n")}
|
|
18611
18778
|
`
|
|
18612
18779
|
);
|
|
18613
18780
|
return 0;
|
|
@@ -18679,7 +18846,7 @@ Proof: ${result2.proofPath}
|
|
|
18679
18846
|
}
|
|
18680
18847
|
const host = stringFlag(flags, "host") ?? "127.0.0.1";
|
|
18681
18848
|
if (!isLoopbackHost(host)) {
|
|
18682
|
-
write(stderr, "Guardian service supervision only accepts loopback
|
|
18849
|
+
write(stderr, "Guardian service supervision only accepts loopback Local Guardian hosts.\n");
|
|
18683
18850
|
return 1;
|
|
18684
18851
|
}
|
|
18685
18852
|
const port = numberFlag(flags, "port") ?? 4317;
|
|
@@ -18936,7 +19103,7 @@ async function daemonPair(flags, options) {
|
|
|
18936
19103
|
[GUARDIAN_BRIDGE_CONFIG_STORAGE_KEY]: bridgeConfig,
|
|
18937
19104
|
[GUARDIAN_BRIDGE_TOKEN_STORAGE_KEY]: token
|
|
18938
19105
|
},
|
|
18939
|
-
note: "
|
|
19106
|
+
note: "Advanced fallback only. Most users should run guardian setup, open ContextECF Halo, and click Connect."
|
|
18940
19107
|
};
|
|
18941
19108
|
if (flags.has("json")) {
|
|
18942
19109
|
return { exitCode: 0, message: `${JSON.stringify(pairing, null, 2)}
|
|
@@ -18945,13 +19112,10 @@ async function daemonPair(flags, options) {
|
|
|
18945
19112
|
return {
|
|
18946
19113
|
exitCode: 0,
|
|
18947
19114
|
message: `${[
|
|
18948
|
-
"Guardian browser
|
|
18949
|
-
|
|
18950
|
-
|
|
18951
|
-
|
|
18952
|
-
`Native host: ${pairing.nativeHostName}`,
|
|
18953
|
-
`Token: ${pairing.token}`,
|
|
18954
|
-
pairing.note
|
|
19115
|
+
"Guardian browser connection uses one-click setup now.",
|
|
19116
|
+
"Run guardian connect, then open ContextECF Halo and click Connect.",
|
|
19117
|
+
"Support setup codes are only printed by guardian pair --json.",
|
|
19118
|
+
"Token printed: no"
|
|
18955
19119
|
].join("\n")}
|
|
18956
19120
|
`
|
|
18957
19121
|
};
|
|
@@ -18960,6 +19124,9 @@ async function runExtensionCommand(argv, flags, options) {
|
|
|
18960
19124
|
const subcommand = argv[0] ?? "native-host";
|
|
18961
19125
|
const stdout = options.stdout ?? process.stdout;
|
|
18962
19126
|
const stderr = options.stderr ?? process.stderr;
|
|
19127
|
+
if (subcommand === "connect") {
|
|
19128
|
+
return runExtensionConnectCommand(flags, options);
|
|
19129
|
+
}
|
|
18963
19130
|
if (subcommand === "open-setup") {
|
|
18964
19131
|
return runExtensionOpenSetupCommand(flags, options);
|
|
18965
19132
|
}
|
|
@@ -19147,13 +19314,18 @@ Re-run with --force to replace it.
|
|
|
19147
19314
|
registryMutationRequested: writeRegistry,
|
|
19148
19315
|
registryProofPath,
|
|
19149
19316
|
tokenPrinted: false,
|
|
19150
|
-
nextCommands: [
|
|
19317
|
+
nextCommands: [
|
|
19318
|
+
"guardian launch",
|
|
19319
|
+
"Open ContextECF Halo and click Connect",
|
|
19320
|
+
"guardian extension native-host status"
|
|
19321
|
+
],
|
|
19322
|
+
advancedCommands: ["guardian pair --json"]
|
|
19151
19323
|
};
|
|
19152
19324
|
write(
|
|
19153
19325
|
stdout,
|
|
19154
19326
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
19155
19327
|
` : `${[
|
|
19156
|
-
"Guardian
|
|
19328
|
+
"Guardian browser connection installed.",
|
|
19157
19329
|
`Browser: ${browser}`,
|
|
19158
19330
|
`Host: ${plan.hostName}`,
|
|
19159
19331
|
`Manifest: ${plan.manifestPath}`,
|
|
@@ -19168,6 +19340,121 @@ Re-run with --force to replace it.
|
|
|
19168
19340
|
);
|
|
19169
19341
|
return 0;
|
|
19170
19342
|
}
|
|
19343
|
+
async function runExtensionConnectCommand(flags, options) {
|
|
19344
|
+
const stdout = options.stdout ?? process.stdout;
|
|
19345
|
+
const stderr = options.stderr ?? process.stderr;
|
|
19346
|
+
const requestedExtensionId = stringFlag(flags, "extension-id") ?? stringFlag(flags, "id");
|
|
19347
|
+
const extensionId = requestedExtensionId ?? GUARDIAN_HALO_CHROME_EXTENSION_ID;
|
|
19348
|
+
if (requestedExtensionId && !isValidChromeExtensionId(requestedExtensionId)) {
|
|
19349
|
+
write(
|
|
19350
|
+
stderr,
|
|
19351
|
+
"Invalid --extension-id. Chrome extension ids must be 32 lowercase letters from a-p.\n"
|
|
19352
|
+
);
|
|
19353
|
+
return 1;
|
|
19354
|
+
}
|
|
19355
|
+
const install = JSON.parse(await installGuardian(/* @__PURE__ */ new Map([["json", true]]), options));
|
|
19356
|
+
let launchOutput = "";
|
|
19357
|
+
const launchFlags = new Map([
|
|
19358
|
+
["json", true],
|
|
19359
|
+
["print", true],
|
|
19360
|
+
...copyStringFlags(flags, ["host", "port"])
|
|
19361
|
+
]);
|
|
19362
|
+
const launchExitCode = await runLaunchCommand(launchFlags, {
|
|
19363
|
+
...options,
|
|
19364
|
+
stdout: {
|
|
19365
|
+
write: (chunk) => {
|
|
19366
|
+
launchOutput += chunk.toString();
|
|
19367
|
+
return true;
|
|
19368
|
+
}
|
|
19369
|
+
},
|
|
19370
|
+
stderr
|
|
19371
|
+
});
|
|
19372
|
+
if (launchExitCode !== 0) {
|
|
19373
|
+
return launchExitCode;
|
|
19374
|
+
}
|
|
19375
|
+
const launch = JSON.parse(launchOutput);
|
|
19376
|
+
const helperFlags = new Map([
|
|
19377
|
+
["json", true],
|
|
19378
|
+
["force", true],
|
|
19379
|
+
["extension-id", extensionId],
|
|
19380
|
+
...copyStringFlags(flags, ["browser", "target"])
|
|
19381
|
+
]);
|
|
19382
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
19383
|
+
helperFlags.set("write-registry", true);
|
|
19384
|
+
helperFlags.set("yes", true);
|
|
19385
|
+
}
|
|
19386
|
+
let helperOutput = "";
|
|
19387
|
+
let helperError = "";
|
|
19388
|
+
const helperExitCode = await runExtensionCommand(["native-host", "install"], helperFlags, {
|
|
19389
|
+
...options,
|
|
19390
|
+
stdout: {
|
|
19391
|
+
write: (chunk) => {
|
|
19392
|
+
helperOutput += chunk.toString();
|
|
19393
|
+
return true;
|
|
19394
|
+
}
|
|
19395
|
+
},
|
|
19396
|
+
stderr: {
|
|
19397
|
+
write: (chunk) => {
|
|
19398
|
+
helperError += chunk.toString();
|
|
19399
|
+
return true;
|
|
19400
|
+
}
|
|
19401
|
+
}
|
|
19402
|
+
});
|
|
19403
|
+
if (helperExitCode !== 0) {
|
|
19404
|
+
write(
|
|
19405
|
+
stderr,
|
|
19406
|
+
helperError.trim() ? `${helperError.trim()}
|
|
19407
|
+
` : "Guardian could not prepare the browser connection. Run guardian setup, then try again.\n"
|
|
19408
|
+
);
|
|
19409
|
+
return helperExitCode;
|
|
19410
|
+
}
|
|
19411
|
+
const helper = JSON.parse(helperOutput);
|
|
19412
|
+
const result = {
|
|
19413
|
+
schema_version: "contextecf/project-guardian-browser-connect/v1",
|
|
19414
|
+
success: true,
|
|
19415
|
+
extensionId,
|
|
19416
|
+
defaultExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
19417
|
+
install: {
|
|
19418
|
+
profileDir: install.profileDir,
|
|
19419
|
+
tokenCreated: install.tokenCreated,
|
|
19420
|
+
tokenPrinted: false
|
|
19421
|
+
},
|
|
19422
|
+
localGuardian: {
|
|
19423
|
+
running: true,
|
|
19424
|
+
opened: launch.opened,
|
|
19425
|
+
reusedExistingDaemon: launch.reusedExistingDaemon,
|
|
19426
|
+
daemonUrl: launch.daemonUrl,
|
|
19427
|
+
controlTowerUrl: launch.controlTowerUrl,
|
|
19428
|
+
pid: launch.pid,
|
|
19429
|
+
healthStatus: launch.healthStatus
|
|
19430
|
+
},
|
|
19431
|
+
browser: helper.browser,
|
|
19432
|
+
helperInstalled: true,
|
|
19433
|
+
helperStatus: {
|
|
19434
|
+
hostName: helper.hostName,
|
|
19435
|
+
manifestPath: helper.manifestPath,
|
|
19436
|
+
allowedOrigins: helper.allowedOrigins,
|
|
19437
|
+
tokenPrinted: helper.tokenPrinted
|
|
19438
|
+
},
|
|
19439
|
+
tokenPrinted: false,
|
|
19440
|
+
nextCommands: ["Open ContextECF Halo and click Connect", "guardian open", "guardian status"],
|
|
19441
|
+
supportCommands: ["guardian extension open-setup --print", "guardian pair --json"]
|
|
19442
|
+
};
|
|
19443
|
+
write(
|
|
19444
|
+
stdout,
|
|
19445
|
+
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
19446
|
+
` : `${[
|
|
19447
|
+
"Guardian browser connection is ready.",
|
|
19448
|
+
`Local Guardian: ${result.localGuardian.daemonUrl}${result.localGuardian.reusedExistingDaemon ? " (already running)" : ""}`,
|
|
19449
|
+
`Browser: ${result.browser}`,
|
|
19450
|
+
"Next: open ContextECF Halo and click Connect.",
|
|
19451
|
+
"No setup code needed.",
|
|
19452
|
+
"Token printed: no"
|
|
19453
|
+
].join("\n")}
|
|
19454
|
+
`
|
|
19455
|
+
);
|
|
19456
|
+
return 0;
|
|
19457
|
+
}
|
|
19171
19458
|
async function runExtensionOpenSetupCommand(flags, options) {
|
|
19172
19459
|
const stdout = options.stdout ?? process.stdout;
|
|
19173
19460
|
const stderr = options.stderr ?? process.stderr;
|
|
@@ -19177,8 +19464,9 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19177
19464
|
write(stderr, "Guardian is not installed. Run guardian install first.\n");
|
|
19178
19465
|
return 1;
|
|
19179
19466
|
}
|
|
19180
|
-
const
|
|
19181
|
-
|
|
19467
|
+
const requestedExtensionId = stringFlag(flags, "extension-id") ?? stringFlag(flags, "id");
|
|
19468
|
+
const extensionId = requestedExtensionId ?? GUARDIAN_HALO_CHROME_EXTENSION_ID;
|
|
19469
|
+
if (requestedExtensionId && !isValidChromeExtensionId(requestedExtensionId)) {
|
|
19182
19470
|
write(
|
|
19183
19471
|
stderr,
|
|
19184
19472
|
"Invalid --extension-id. Chrome extension ids must be 32 lowercase letters from a-p.\n"
|
|
@@ -19192,22 +19480,24 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19192
19480
|
return 1;
|
|
19193
19481
|
}
|
|
19194
19482
|
const setupUrl = buildControlTowerSetupUrl(resolved.url);
|
|
19195
|
-
const nativeHostCommand = `guardian extension native-host install --extension-id=${extensionId
|
|
19483
|
+
const nativeHostCommand = `guardian extension native-host install --extension-id=${extensionId}`;
|
|
19196
19484
|
const result = {
|
|
19197
19485
|
schema_version: "contextecf/project-guardian-extension-setup/v1",
|
|
19198
19486
|
success: true,
|
|
19199
19487
|
opened: !flags.has("print"),
|
|
19200
19488
|
controlTowerUrl: resolved.url,
|
|
19201
19489
|
setupUrl,
|
|
19202
|
-
extensionId
|
|
19490
|
+
extensionId,
|
|
19491
|
+
defaultExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
19203
19492
|
tokenPrinted: false,
|
|
19204
19493
|
nextCommands: [
|
|
19205
19494
|
"guardian launch",
|
|
19206
|
-
"guardian daemon pair --json",
|
|
19207
19495
|
nativeHostCommand,
|
|
19208
|
-
"guardian extension native-host status"
|
|
19496
|
+
"guardian extension native-host status",
|
|
19497
|
+
"Open ContextECF Halo and click Connect"
|
|
19209
19498
|
],
|
|
19210
|
-
|
|
19499
|
+
advancedCommands: ["guardian pair --json"],
|
|
19500
|
+
note: extensionId ? "Guardian browser setup opened for ContextECF Halo." : "Guardian browser setup opened."
|
|
19211
19501
|
};
|
|
19212
19502
|
if (result.opened) {
|
|
19213
19503
|
const opener = options.openUrl ?? ((url2) => openUrlInDefaultBrowser(url2, options));
|
|
@@ -19219,7 +19509,8 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19219
19509
|
` : `${[
|
|
19220
19510
|
result.opened ? `Opening Guardian browser setup: ${setupUrl}` : `Guardian browser setup: ${setupUrl}`,
|
|
19221
19511
|
"Token printed: no",
|
|
19222
|
-
`Next: ${result.nextCommands.join("; ")}
|
|
19512
|
+
`Next: ${result.nextCommands.join("; ")}`,
|
|
19513
|
+
`Advanced fallback: ${result.advancedCommands.join("; ")}`
|
|
19223
19514
|
].join("\n")}
|
|
19224
19515
|
`
|
|
19225
19516
|
);
|
|
@@ -19746,7 +20037,7 @@ function buildGuardianServiceArtifact(input2) {
|
|
|
19746
20037
|
if (input2.platform === "systemd") {
|
|
19747
20038
|
return `${[
|
|
19748
20039
|
"[Unit]",
|
|
19749
|
-
"Description=Project Guardian
|
|
20040
|
+
"Description=Project Guardian Local Guardian background service",
|
|
19750
20041
|
"After=network.target",
|
|
19751
20042
|
"",
|
|
19752
20043
|
"[Service]",
|
|
@@ -19765,7 +20056,7 @@ function buildGuardianServiceArtifact(input2) {
|
|
|
19765
20056
|
'<?xml version="1.0" encoding="UTF-16"?>',
|
|
19766
20057
|
'<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">',
|
|
19767
20058
|
" <RegistrationInfo>",
|
|
19768
|
-
" <Description>Project Guardian
|
|
20059
|
+
" <Description>Project Guardian Local Guardian background service</Description>",
|
|
19769
20060
|
" </RegistrationInfo>",
|
|
19770
20061
|
" <Triggers>",
|
|
19771
20062
|
" <LogonTrigger><Enabled>true</Enabled></LogonTrigger>",
|
|
@@ -20747,6 +21038,59 @@ async function runReceiptsCommand(argv, flags, options) {
|
|
|
20747
21038
|
write(stdout, await verifyReceipts(flags, options));
|
|
20748
21039
|
return 0;
|
|
20749
21040
|
}
|
|
21041
|
+
async function runCoachCommand(argv, flags, options) {
|
|
21042
|
+
const subcommand = argv[0] ?? "options";
|
|
21043
|
+
const stdout = options.stdout ?? process.stdout;
|
|
21044
|
+
const stderr = options.stderr ?? process.stderr;
|
|
21045
|
+
if (subcommand === "options" || subcommand === "catalog") {
|
|
21046
|
+
write(stdout, renderGuardianPromptCardOptions(buildGuardianPromptCardOptions(), flags));
|
|
21047
|
+
return 0;
|
|
21048
|
+
}
|
|
21049
|
+
write(stderr, `Unknown Guardian coach command: ${subcommand}
|
|
21050
|
+
`);
|
|
21051
|
+
return 1;
|
|
21052
|
+
}
|
|
21053
|
+
function buildGuardianPromptCardOptions() {
|
|
21054
|
+
return {
|
|
21055
|
+
schema_version: "contextecf/project-guardian-prompt-card-options/v1",
|
|
21056
|
+
cli_version: GUARDIAN_CLI_VERSION,
|
|
21057
|
+
status: "catalog_ready",
|
|
21058
|
+
summary: "Guardian prompt cards keep the user in control: warnings and safer rewrites can be sent as-is, while hard blocks cannot.",
|
|
21059
|
+
catalog: GUARDIAN_PROMPT_CARD_OPTIONS_CATALOG,
|
|
21060
|
+
rules: [
|
|
21061
|
+
"Show a clear reason before asking the user to decide.",
|
|
21062
|
+
"Offer Send as-is for warnings and safer rewrites when policy allows override.",
|
|
21063
|
+
"Never offer Send as-is for hard blocks or unavailable Local Guardian checks.",
|
|
21064
|
+
"Keep Edit prompt available as the safe path for every visible send-time card.",
|
|
21065
|
+
"Do not store raw sensitive prompt text in this catalog or CLI output."
|
|
21066
|
+
],
|
|
21067
|
+
tokenPrinted: false,
|
|
21068
|
+
rawContentIncluded: false
|
|
21069
|
+
};
|
|
21070
|
+
}
|
|
21071
|
+
function renderGuardianPromptCardOptions(result, flags) {
|
|
21072
|
+
if (flags.has("json")) {
|
|
21073
|
+
return `${JSON.stringify(result, null, 2)}
|
|
21074
|
+
`;
|
|
21075
|
+
}
|
|
21076
|
+
return `${[
|
|
21077
|
+
"Guardian Prompt Card Options",
|
|
21078
|
+
result.summary,
|
|
21079
|
+
"",
|
|
21080
|
+
"Programmed cards:",
|
|
21081
|
+
...result.catalog.map((entry) => {
|
|
21082
|
+
const visibleActions = entry.actions.filter((action) => action.visibleToUser).map((action) => action.label);
|
|
21083
|
+
const labels = visibleActions.length > 0 ? visibleActions.join(", ") : "Prompt sends automatically";
|
|
21084
|
+
return `- ${entry.cardStyle}: ${labels}`;
|
|
21085
|
+
}),
|
|
21086
|
+
"",
|
|
21087
|
+
"Rules:",
|
|
21088
|
+
...result.rules.map((rule) => `- ${rule}`),
|
|
21089
|
+
"Token printed: no",
|
|
21090
|
+
"Raw prompt content included: no"
|
|
21091
|
+
].join("\n")}
|
|
21092
|
+
`;
|
|
21093
|
+
}
|
|
20750
21094
|
async function runPolicyCommand(argv, flags, options) {
|
|
20751
21095
|
const subcommand = argv[0] ?? "list";
|
|
20752
21096
|
const stdout = options.stdout ?? process.stdout;
|
|
@@ -23166,22 +23510,32 @@ function openUrlInDefaultBrowser(url2, options = {}) {
|
|
|
23166
23510
|
});
|
|
23167
23511
|
});
|
|
23168
23512
|
}
|
|
23169
|
-
var GUARDIAN_CLI_VERSION, GUARDIAN_PROFILE_SCHEMA_VERSION, GUARDIAN_NPM_PACKAGE_NAME, GUARDIAN_NPM_REGISTRY, GUARDIAN_GLOBAL_INSTALL_COMMAND, GUARDIAN_NPX_SETUP_COMMAND, GUARDIAN_FIRST_RUN_COMMAND, GUARDIAN_OPEN_CONTROL_TOWER_COMMAND, GUARDIAN_RELEASE_DISPATCH_WORKFLOW, GUARDIAN_RELEASE_REPOSITORY, GUARDIAN_DEFAULT_CONTROL_TOWER_URL, GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ENV, GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ID_ENV, GUARDIAN_SQLITE_PAGE_ENCRYPTION_KEY_ENV, GUARDIAN_SQLITE_PAGE_ENCRYPTION_KEY_ID_ENV, GUARDIAN_DESKTOP_ADAPTER_SIMULATE_OPEN_ENV, GUARDIAN_DESKTOP_URL_OPEN_ADAPTER_ID, GUARDIAN_DESKTOP_URL_OPEN_ADAPTER_VERSION, GUARDIAN_DESKTOP_URL_OPEN_ADAPTER_PROOF_SCHEMA_VERSION, GUARDIAN_SERVICE_PROOF_SCHEMA_VERSION, GUARDIAN_OS_KEY_STORAGE_PROOF_SCHEMA_VERSION, GUARDIAN_WINDOWS_NATIVE_HOST_REGISTRY_PROOF_SCHEMA_VERSION, PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION2, PROMPT_COACH_MODES, DEFAULT_PROMPT_COACH_DAILY_SOFT_CARD_LIMIT, GUARDIAN_POSTURE_PROFILE_IDS, GUARDIAN_POSTURE_PROFILE_CATALOG, GUARDIAN_POLICY_PACK_MARKETPLACE_CATALOG, GUARDIAN_PROVIDER_IDS, GUARDIAN_SOURCE_TYPES, GUARDIAN_NOT_PROVEN_PRODUCTION_GATES, DEFAULT_POLICY_PACKS, GUARDIAN_BRIDGE_CONFIG_STORAGE_KEY, GUARDIAN_BRIDGE_TOKEN_STORAGE_KEY, GUARDIAN_BRIDGE_CONFIG_SCHEMA_VERSION, GUARDIAN_NATIVE_HOST_NAME, MCP_CLIENTS, NATIVE_HOST_BROWSERS, HELP_TEXT, normalizeGuardianPreferences, GUARDIAN_DATA_FILE_NAMES, GUARDIAN_DATA_DIR_NAMES, GUARDIAN_MIN_SUPPORTED_NODE_VERSION;
|
|
23513
|
+
var GUARDIAN_CLI_VERSION, GUARDIAN_PROFILE_SCHEMA_VERSION, GUARDIAN_NPM_PACKAGE_NAME, GUARDIAN_NPM_REGISTRY, GUARDIAN_DEFAULT_NPM_PUBLISH_AUTH, GUARDIAN_GLOBAL_INSTALL_COMMAND, GUARDIAN_NPX_SETUP_COMMAND, GUARDIAN_FIRST_RUN_COMMAND, GUARDIAN_OPEN_CONTROL_TOWER_COMMAND, GUARDIAN_HALO_CHROME_EXTENSION_ID, GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL, GUARDIAN_RELEASE_DISPATCH_WORKFLOW, GUARDIAN_RELEASE_REPOSITORY, GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES, GUARDIAN_DEFAULT_CONTROL_TOWER_URL, GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ENV, GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ID_ENV, GUARDIAN_SQLITE_PAGE_ENCRYPTION_KEY_ENV, GUARDIAN_SQLITE_PAGE_ENCRYPTION_KEY_ID_ENV, GUARDIAN_DESKTOP_ADAPTER_SIMULATE_OPEN_ENV, GUARDIAN_DESKTOP_URL_OPEN_ADAPTER_ID, GUARDIAN_DESKTOP_URL_OPEN_ADAPTER_VERSION, GUARDIAN_DESKTOP_URL_OPEN_ADAPTER_PROOF_SCHEMA_VERSION, GUARDIAN_SERVICE_PROOF_SCHEMA_VERSION, GUARDIAN_OS_KEY_STORAGE_PROOF_SCHEMA_VERSION, GUARDIAN_WINDOWS_NATIVE_HOST_REGISTRY_PROOF_SCHEMA_VERSION, PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION2, PROMPT_COACH_MODES, DEFAULT_PROMPT_COACH_DAILY_SOFT_CARD_LIMIT, GUARDIAN_PROMPT_CARD_OPTIONS_CATALOG, GUARDIAN_POSTURE_PROFILE_IDS, GUARDIAN_POSTURE_PROFILE_CATALOG, GUARDIAN_POLICY_PACK_MARKETPLACE_CATALOG, GUARDIAN_PROVIDER_IDS, GUARDIAN_SOURCE_TYPES, GUARDIAN_NOT_PROVEN_PRODUCTION_GATES, DEFAULT_POLICY_PACKS, GUARDIAN_BRIDGE_CONFIG_STORAGE_KEY, GUARDIAN_BRIDGE_TOKEN_STORAGE_KEY, GUARDIAN_BRIDGE_CONFIG_SCHEMA_VERSION, GUARDIAN_NATIVE_HOST_NAME, MCP_CLIENTS, NATIVE_HOST_BROWSERS, HELP_TEXT, ADVANCED_HELP_TEXT, normalizeGuardianPreferences, GUARDIAN_DATA_FILE_NAMES, GUARDIAN_DATA_DIR_NAMES, GUARDIAN_MIN_SUPPORTED_NODE_VERSION;
|
|
23170
23514
|
var init_runtime = __esm({
|
|
23171
23515
|
"packages/guardian-cli/src/runtime.ts"() {
|
|
23172
23516
|
"use strict";
|
|
23173
23517
|
init_src();
|
|
23174
23518
|
init_src2();
|
|
23175
|
-
GUARDIAN_CLI_VERSION = "0.1.
|
|
23519
|
+
GUARDIAN_CLI_VERSION = "0.1.9";
|
|
23176
23520
|
GUARDIAN_PROFILE_SCHEMA_VERSION = "contextecf/project-guardian-profile/v1";
|
|
23177
23521
|
GUARDIAN_NPM_PACKAGE_NAME = "@contextecf/guardian-cli";
|
|
23178
23522
|
GUARDIAN_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
23523
|
+
GUARDIAN_DEFAULT_NPM_PUBLISH_AUTH = "trusted_publisher";
|
|
23179
23524
|
GUARDIAN_GLOBAL_INSTALL_COMMAND = `npm install -g ${GUARDIAN_NPM_PACKAGE_NAME}`;
|
|
23180
23525
|
GUARDIAN_NPX_SETUP_COMMAND = `npx ${GUARDIAN_NPM_PACKAGE_NAME}@latest setup`;
|
|
23181
23526
|
GUARDIAN_FIRST_RUN_COMMAND = "guardian setup";
|
|
23182
23527
|
GUARDIAN_OPEN_CONTROL_TOWER_COMMAND = "guardian open";
|
|
23528
|
+
GUARDIAN_HALO_CHROME_EXTENSION_ID = "dopkdppbiakhbglhgjfacfdhpheejkck";
|
|
23529
|
+
GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL = `https://chromewebstore.google.com/detail/contextecf-halo/${GUARDIAN_HALO_CHROME_EXTENSION_ID}`;
|
|
23183
23530
|
GUARDIAN_RELEASE_DISPATCH_WORKFLOW = "guardian-release-dispatch.yml";
|
|
23184
23531
|
GUARDIAN_RELEASE_REPOSITORY = "Intelligent-Context-AI-Inc/ContextECF-GITHUB";
|
|
23532
|
+
GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES = [
|
|
23533
|
+
"CHROME_WEB_STORE_CLIENT_ID",
|
|
23534
|
+
"CHROME_WEB_STORE_CLIENT_SECRET",
|
|
23535
|
+
"CHROME_WEB_STORE_REFRESH_TOKEN",
|
|
23536
|
+
"CHROME_WEB_STORE_PUBLISHER_ID",
|
|
23537
|
+
"CHROME_WEB_STORE_EXTENSION_ID"
|
|
23538
|
+
];
|
|
23185
23539
|
GUARDIAN_DEFAULT_CONTROL_TOWER_URL = "http://127.0.0.1:4317/control-tower";
|
|
23186
23540
|
GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ENV = "GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY";
|
|
23187
23541
|
GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ID_ENV = "GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ID";
|
|
@@ -23197,6 +23551,195 @@ var init_runtime = __esm({
|
|
|
23197
23551
|
PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION2 = "contextecf/personal-fabric-contracts/v1";
|
|
23198
23552
|
PROMPT_COACH_MODES = ["off", "quiet", "balanced", "hands_on"];
|
|
23199
23553
|
DEFAULT_PROMPT_COACH_DAILY_SOFT_CARD_LIMIT = 6;
|
|
23554
|
+
GUARDIAN_PROMPT_CARD_OPTIONS_CATALOG = [
|
|
23555
|
+
{
|
|
23556
|
+
disposition: "allow",
|
|
23557
|
+
cardStyle: "No visible card",
|
|
23558
|
+
whenShown: "Local Guardian approves the prompt before it leaves the browser.",
|
|
23559
|
+
actions: [
|
|
23560
|
+
{
|
|
23561
|
+
id: "auto-send",
|
|
23562
|
+
label: "Send prompt",
|
|
23563
|
+
role: "none",
|
|
23564
|
+
sendsPrompt: true,
|
|
23565
|
+
sendsRevisedPrompt: false,
|
|
23566
|
+
requiresConfirmation: false,
|
|
23567
|
+
visibleToUser: false
|
|
23568
|
+
}
|
|
23569
|
+
],
|
|
23570
|
+
sendAsIsAllowed: true,
|
|
23571
|
+
hardBlock: false,
|
|
23572
|
+
receiptExpected: true
|
|
23573
|
+
},
|
|
23574
|
+
{
|
|
23575
|
+
disposition: "warn",
|
|
23576
|
+
cardStyle: "Quick check",
|
|
23577
|
+
whenShown: "Guardian found a low or medium-risk concern, but policy still allows the user to continue.",
|
|
23578
|
+
actions: [
|
|
23579
|
+
{
|
|
23580
|
+
id: "send-as-is",
|
|
23581
|
+
label: "Send as-is",
|
|
23582
|
+
role: "allowed_override",
|
|
23583
|
+
sendsPrompt: true,
|
|
23584
|
+
sendsRevisedPrompt: false,
|
|
23585
|
+
requiresConfirmation: false,
|
|
23586
|
+
visibleToUser: true
|
|
23587
|
+
},
|
|
23588
|
+
{
|
|
23589
|
+
id: "edit-prompt",
|
|
23590
|
+
label: "Edit prompt",
|
|
23591
|
+
role: "safe_escape",
|
|
23592
|
+
sendsPrompt: false,
|
|
23593
|
+
sendsRevisedPrompt: false,
|
|
23594
|
+
requiresConfirmation: false,
|
|
23595
|
+
visibleToUser: true
|
|
23596
|
+
}
|
|
23597
|
+
],
|
|
23598
|
+
sendAsIsAllowed: true,
|
|
23599
|
+
hardBlock: false,
|
|
23600
|
+
receiptExpected: true
|
|
23601
|
+
},
|
|
23602
|
+
{
|
|
23603
|
+
disposition: "rewrite",
|
|
23604
|
+
cardStyle: "Safer version ready",
|
|
23605
|
+
whenShown: "Guardian prepared a safer prompt, usually by masking sensitive text or narrowing scope.",
|
|
23606
|
+
actions: [
|
|
23607
|
+
{
|
|
23608
|
+
id: "send-revised-prompt",
|
|
23609
|
+
label: "Send revised prompt",
|
|
23610
|
+
role: "recommended",
|
|
23611
|
+
sendsPrompt: true,
|
|
23612
|
+
sendsRevisedPrompt: true,
|
|
23613
|
+
requiresConfirmation: false,
|
|
23614
|
+
visibleToUser: true
|
|
23615
|
+
},
|
|
23616
|
+
{
|
|
23617
|
+
id: "send-as-is",
|
|
23618
|
+
label: "Send as-is",
|
|
23619
|
+
role: "allowed_override",
|
|
23620
|
+
sendsPrompt: true,
|
|
23621
|
+
sendsRevisedPrompt: false,
|
|
23622
|
+
requiresConfirmation: false,
|
|
23623
|
+
visibleToUser: true
|
|
23624
|
+
},
|
|
23625
|
+
{
|
|
23626
|
+
id: "edit-prompt",
|
|
23627
|
+
label: "Edit prompt",
|
|
23628
|
+
role: "safe_escape",
|
|
23629
|
+
sendsPrompt: false,
|
|
23630
|
+
sendsRevisedPrompt: false,
|
|
23631
|
+
requiresConfirmation: false,
|
|
23632
|
+
visibleToUser: true
|
|
23633
|
+
}
|
|
23634
|
+
],
|
|
23635
|
+
sendAsIsAllowed: true,
|
|
23636
|
+
hardBlock: false,
|
|
23637
|
+
receiptExpected: true
|
|
23638
|
+
},
|
|
23639
|
+
{
|
|
23640
|
+
disposition: "confirm",
|
|
23641
|
+
cardStyle: "Review needed",
|
|
23642
|
+
whenShown: "Guardian needs deliberate approval before sending higher-risk content or action requests.",
|
|
23643
|
+
actions: [
|
|
23644
|
+
{
|
|
23645
|
+
id: "approve-and-send",
|
|
23646
|
+
label: "Approve and send",
|
|
23647
|
+
role: "recommended",
|
|
23648
|
+
sendsPrompt: true,
|
|
23649
|
+
sendsRevisedPrompt: false,
|
|
23650
|
+
requiresConfirmation: true,
|
|
23651
|
+
visibleToUser: true
|
|
23652
|
+
},
|
|
23653
|
+
{
|
|
23654
|
+
id: "edit-prompt",
|
|
23655
|
+
label: "Edit prompt",
|
|
23656
|
+
role: "safe_escape",
|
|
23657
|
+
sendsPrompt: false,
|
|
23658
|
+
sendsRevisedPrompt: false,
|
|
23659
|
+
requiresConfirmation: false,
|
|
23660
|
+
visibleToUser: true
|
|
23661
|
+
}
|
|
23662
|
+
],
|
|
23663
|
+
sendAsIsAllowed: false,
|
|
23664
|
+
hardBlock: false,
|
|
23665
|
+
receiptExpected: true
|
|
23666
|
+
},
|
|
23667
|
+
{
|
|
23668
|
+
disposition: "block",
|
|
23669
|
+
cardStyle: "Blocked",
|
|
23670
|
+
whenShown: "The prompt violates policy, contains protected secrets, or targets a denied destination.",
|
|
23671
|
+
actions: [
|
|
23672
|
+
{
|
|
23673
|
+
id: "edit-prompt",
|
|
23674
|
+
label: "Edit prompt",
|
|
23675
|
+
role: "safe_escape",
|
|
23676
|
+
sendsPrompt: false,
|
|
23677
|
+
sendsRevisedPrompt: false,
|
|
23678
|
+
requiresConfirmation: false,
|
|
23679
|
+
visibleToUser: true
|
|
23680
|
+
}
|
|
23681
|
+
],
|
|
23682
|
+
sendAsIsAllowed: false,
|
|
23683
|
+
hardBlock: true,
|
|
23684
|
+
receiptExpected: true
|
|
23685
|
+
},
|
|
23686
|
+
{
|
|
23687
|
+
disposition: "unavailable",
|
|
23688
|
+
cardStyle: "Setup needed",
|
|
23689
|
+
whenShown: "The browser cannot reach Local Guardian, so the prompt is held back.",
|
|
23690
|
+
actions: [
|
|
23691
|
+
{
|
|
23692
|
+
id: "edit-prompt",
|
|
23693
|
+
label: "Edit prompt",
|
|
23694
|
+
role: "safe_escape",
|
|
23695
|
+
sendsPrompt: false,
|
|
23696
|
+
sendsRevisedPrompt: false,
|
|
23697
|
+
requiresConfirmation: false,
|
|
23698
|
+
visibleToUser: true
|
|
23699
|
+
}
|
|
23700
|
+
],
|
|
23701
|
+
sendAsIsAllowed: false,
|
|
23702
|
+
hardBlock: false,
|
|
23703
|
+
receiptExpected: false
|
|
23704
|
+
},
|
|
23705
|
+
{
|
|
23706
|
+
disposition: "live_sensitive_warning",
|
|
23707
|
+
cardStyle: "Early heads-up",
|
|
23708
|
+
whenShown: "Guardian notices sensitive text while the user is typing, before the formal send-time check.",
|
|
23709
|
+
actions: [
|
|
23710
|
+
{
|
|
23711
|
+
id: "use-placeholder",
|
|
23712
|
+
label: "Use placeholder",
|
|
23713
|
+
role: "recommended",
|
|
23714
|
+
sendsPrompt: false,
|
|
23715
|
+
sendsRevisedPrompt: true,
|
|
23716
|
+
requiresConfirmation: false,
|
|
23717
|
+
visibleToUser: true
|
|
23718
|
+
},
|
|
23719
|
+
{
|
|
23720
|
+
id: "send-as-is",
|
|
23721
|
+
label: "Send as-is",
|
|
23722
|
+
role: "allowed_override",
|
|
23723
|
+
sendsPrompt: true,
|
|
23724
|
+
sendsRevisedPrompt: false,
|
|
23725
|
+
requiresConfirmation: false,
|
|
23726
|
+
visibleToUser: true
|
|
23727
|
+
},
|
|
23728
|
+
{
|
|
23729
|
+
id: "keep-drafting",
|
|
23730
|
+
label: "Keep drafting",
|
|
23731
|
+
role: "safe_escape",
|
|
23732
|
+
sendsPrompt: false,
|
|
23733
|
+
sendsRevisedPrompt: false,
|
|
23734
|
+
requiresConfirmation: false,
|
|
23735
|
+
visibleToUser: true
|
|
23736
|
+
}
|
|
23737
|
+
],
|
|
23738
|
+
sendAsIsAllowed: true,
|
|
23739
|
+
hardBlock: false,
|
|
23740
|
+
receiptExpected: false
|
|
23741
|
+
}
|
|
23742
|
+
];
|
|
23200
23743
|
GUARDIAN_POSTURE_PROFILE_IDS = [
|
|
23201
23744
|
"calm",
|
|
23202
23745
|
"balanced",
|
|
@@ -23689,10 +24232,49 @@ Open Control Tower:
|
|
|
23689
24232
|
${GUARDIAN_OPEN_CONTROL_TOWER_COMMAND}
|
|
23690
24233
|
start aliases: guardian launch, guardian tower, guardian control-tower
|
|
23691
24234
|
|
|
24235
|
+
Everyday commands:
|
|
24236
|
+
guardian --version
|
|
24237
|
+
guardian setup
|
|
24238
|
+
guardian launch
|
|
24239
|
+
guardian open
|
|
24240
|
+
guardian status
|
|
24241
|
+
guardian doctor
|
|
24242
|
+
guardian connect
|
|
24243
|
+
guardian coach options
|
|
24244
|
+
guardian policy list
|
|
24245
|
+
guardian marketplace list
|
|
24246
|
+
guardian posture list
|
|
24247
|
+
guardian privacy show
|
|
24248
|
+
|
|
24249
|
+
Recommended first run:
|
|
24250
|
+
${GUARDIAN_GLOBAL_INSTALL_COMMAND}
|
|
24251
|
+
guardian setup
|
|
24252
|
+
Open ContextECF Halo and click Connect.
|
|
24253
|
+
guardian open
|
|
24254
|
+
|
|
24255
|
+
Support and developer commands:
|
|
24256
|
+
guardian help --advanced
|
|
24257
|
+
|
|
24258
|
+
Defaults are local-first: raw capture, cloud sync, learning graph, and app autopilot stay off.`;
|
|
24259
|
+
ADVANCED_HELP_TEXT = `guardian \u2014 Project Guardian Personal Context Fabric
|
|
24260
|
+
|
|
24261
|
+
Global install:
|
|
24262
|
+
${GUARDIAN_GLOBAL_INSTALL_COMMAND}
|
|
24263
|
+
|
|
24264
|
+
First run:
|
|
24265
|
+
${GUARDIAN_FIRST_RUN_COMMAND}
|
|
24266
|
+
|
|
24267
|
+
No global install:
|
|
24268
|
+
${GUARDIAN_NPX_SETUP_COMMAND}
|
|
24269
|
+
|
|
24270
|
+
Open Control Tower:
|
|
24271
|
+
${GUARDIAN_OPEN_CONTROL_TOWER_COMMAND}
|
|
24272
|
+
start aliases: guardian launch, guardian tower, guardian control-tower
|
|
24273
|
+
|
|
23692
24274
|
Usage:
|
|
23693
24275
|
guardian --version
|
|
23694
24276
|
guardian version [--json]
|
|
23695
|
-
guardian setup [--json] [--open] [--print] [--host=127.0.0.1] [--port=4317] [--no-launch] [--no-mcp] [--extension-id=<chrome-extension-id>]
|
|
24277
|
+
guardian setup [--json] [--open] [--print] [--host=127.0.0.1] [--port=4317] [--no-launch] [--no-mcp] [--no-browser-connection] [--extension-id=<chrome-extension-id>]
|
|
23696
24278
|
guardian install [--json]
|
|
23697
24279
|
guardian doctor [--json]
|
|
23698
24280
|
guardian readiness [--json] [--strict]
|
|
@@ -23700,7 +24282,7 @@ Usage:
|
|
|
23700
24282
|
guardian release flow --version=<semver> [--json]
|
|
23701
24283
|
guardian release verify --version=<semver> [--json]
|
|
23702
24284
|
guardian release paste --version=<semver> [--json]
|
|
23703
|
-
guardian release command --version=<semver> [--publish|--dry-run=false] [--channels=all|npm|browser|evidence] [--run-docker-smoke=true|false] [--json]
|
|
24285
|
+
guardian release command --version=<semver> [--publish|--dry-run=false] [--channels=all|npm|browser|evidence] [--run-docker-smoke=true|false] [--publish-auth=token|trusted_publisher] [--browser-store-publish=manual|api] [--json]
|
|
23704
24286
|
guardian release secrets [--json]
|
|
23705
24287
|
guardian release browser-store [--json]
|
|
23706
24288
|
guardian open [--json] [--print] [--url=http://127.0.0.1:<port>]
|
|
@@ -23708,6 +24290,8 @@ Usage:
|
|
|
23708
24290
|
guardian start [--json] [--host=127.0.0.1] [--port=4317]
|
|
23709
24291
|
guardian launch|tower|control-tower [--json] [--open] [--print] [--host=127.0.0.1] [--port=4317]
|
|
23710
24292
|
guardian stop [--json] [--url=http://127.0.0.1:<port>/control-tower]
|
|
24293
|
+
guardian pair [--json]
|
|
24294
|
+
guardian connect [--json] [--browser=chrome|chromium|edge] [--extension-id=<chrome-extension-id>] [--host=127.0.0.1] [--port=4317]
|
|
23711
24295
|
guardian service install [--json] [--platform=auto|launchd|systemd|windows-task-scheduler] [--force]
|
|
23712
24296
|
guardian service status [--json]
|
|
23713
24297
|
guardian service uninstall [--yes] [--json]
|
|
@@ -23716,6 +24300,7 @@ Usage:
|
|
|
23716
24300
|
guardian key-store uninstall [--yes] [--json]
|
|
23717
24301
|
guardian daemon serve [--host=127.0.0.1] [--port=4317]
|
|
23718
24302
|
guardian daemon pair [--json]
|
|
24303
|
+
guardian extension connect [--json] [--browser=chrome|chromium|edge] [--extension-id=<chrome-extension-id>]
|
|
23719
24304
|
guardian extension open-setup [--json] [--print] [--url=http://127.0.0.1:<port>/control-tower] [--extension-id=<chrome-extension-id>]
|
|
23720
24305
|
guardian extension native-host install --extension-id=<chrome-extension-id> [--browser=chrome|chromium|edge] [--target=/path/to/manifest] [--force] [--write-registry --yes] [--json]
|
|
23721
24306
|
guardian extension native-host status [--browser=chrome|chromium|edge] [--target=/path/to/manifest] [--json]
|
|
@@ -23731,6 +24316,7 @@ Usage:
|
|
|
23731
24316
|
guardian privacy mode on [--json]
|
|
23732
24317
|
guardian mcp install [--json] [--client=generic|codex|claude-desktop|cursor|all] [--write] [--target=/path/to/config] [--force]
|
|
23733
24318
|
guardian mcp serve
|
|
24319
|
+
guardian coach options [--json]
|
|
23734
24320
|
guardian policy list [--json]
|
|
23735
24321
|
guardian policy enable <pack-id> [--json]
|
|
23736
24322
|
guardian policy disable <pack-id> [--json]
|
|
@@ -23940,7 +24526,7 @@ function detectRisk(promptManifest, policyPacks) {
|
|
|
23940
24526
|
risks.add("healthcare_data");
|
|
23941
24527
|
matchedPolicyRefs.push("guardian:builtin:healthcare-data");
|
|
23942
24528
|
}
|
|
23943
|
-
if (
|
|
24529
|
+
if (containsSensitiveWorkData(prompt)) {
|
|
23944
24530
|
risks.add("work_data");
|
|
23945
24531
|
matchedPolicyRefs.push("guardian:builtin:work-data");
|
|
23946
24532
|
}
|
|
@@ -23952,7 +24538,7 @@ function detectRisk(promptManifest, policyPacks) {
|
|
|
23952
24538
|
risks.add("destructive_action");
|
|
23953
24539
|
matchedPolicyRefs.push("guardian:builtin:destructive-action");
|
|
23954
24540
|
}
|
|
23955
|
-
if (promptManifest
|
|
24541
|
+
if (shouldOfferContextHelp(promptManifest, policyPacks)) {
|
|
23956
24542
|
risks.add("insufficient_context");
|
|
23957
24543
|
matchedPolicyRefs.push("guardian:builtin:context-sufficiency");
|
|
23958
24544
|
}
|
|
@@ -24067,11 +24653,12 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24067
24653
|
return void 0;
|
|
24068
24654
|
}
|
|
24069
24655
|
const fullSsnRisk = hasFullSsnRisk(riskReport.matched_policy_refs);
|
|
24656
|
+
const riskCopy = buildPromptCoachRiskCopy(riskReport, fullSsnRisk);
|
|
24070
24657
|
const card = {
|
|
24071
24658
|
card_id: `coach:${riskReport.risk_report_id}`,
|
|
24072
24659
|
intervention_level: coachDecision.intervention_level,
|
|
24073
|
-
title: disposition === "block" ? "Guardian blocked this prompt" : disposition === "confirm" ? "Guardian needs your confirmation" : coachDecision.intervention_level === "soft_tag_along" ? "Guardian found useful context" :
|
|
24074
|
-
body: disposition === "confirm" ?
|
|
24660
|
+
title: disposition === "block" ? "Guardian blocked this prompt" : disposition === "confirm" ? "Guardian needs your confirmation" : coachDecision.intervention_level === "soft_tag_along" ? "Guardian found useful context" : riskCopy.title,
|
|
24661
|
+
body: disposition === "confirm" ? `${riskCopy.body} Please review before it leaves your machine.` : coachDecision.intervention_level === "soft_tag_along" ? "This prompt may work better with a little more local context attached first." : riskCopy.body,
|
|
24075
24662
|
recommended_choice_id: disposition === "confirm" ? "confirm-after-review" : coachDecision.intervention_level === "soft_tag_along" ? "add-context" : "use-guardian-version",
|
|
24076
24663
|
choices: disposition === "confirm" ? [
|
|
24077
24664
|
{
|
|
@@ -24096,8 +24683,8 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24096
24683
|
resulting_disposition: "warn"
|
|
24097
24684
|
},
|
|
24098
24685
|
{
|
|
24099
|
-
choice_id: "
|
|
24100
|
-
label: "
|
|
24686
|
+
choice_id: "send-as-is",
|
|
24687
|
+
label: "Send as-is",
|
|
24101
24688
|
description: "Send without adding context.",
|
|
24102
24689
|
resulting_disposition: "allow"
|
|
24103
24690
|
}
|
|
@@ -24114,6 +24701,12 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24114
24701
|
label: "Cancel",
|
|
24115
24702
|
description: "Do not send this prompt.",
|
|
24116
24703
|
resulting_disposition: "block"
|
|
24704
|
+
},
|
|
24705
|
+
{
|
|
24706
|
+
choice_id: "send-as-is",
|
|
24707
|
+
label: "Send as-is",
|
|
24708
|
+
description: "Send the original prompt after reviewing the identity-number risk.",
|
|
24709
|
+
resulting_disposition: "allow"
|
|
24117
24710
|
}
|
|
24118
24711
|
] : [
|
|
24119
24712
|
{
|
|
@@ -24134,6 +24727,60 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24134
24727
|
const parsedCard = PromptCoachCardSchema.parse(card);
|
|
24135
24728
|
return shouldQuietPromptCoachCard(parsedCard, disposition, learningSignals) ? void 0 : parsedCard;
|
|
24136
24729
|
}
|
|
24730
|
+
function buildPromptCoachRiskCopy(riskReport, fullSsnRisk) {
|
|
24731
|
+
if (fullSsnRisk) {
|
|
24732
|
+
return {
|
|
24733
|
+
title: "Guardian found an identity number",
|
|
24734
|
+
body: "Guardian can mask the Social Security number before anything leaves your machine."
|
|
24735
|
+
};
|
|
24736
|
+
}
|
|
24737
|
+
if (riskReport.detected_risks.includes("secret")) {
|
|
24738
|
+
return {
|
|
24739
|
+
title: "Guardian found a secret or credential",
|
|
24740
|
+
body: "Guardian can remove tokens, keys, or passwords before this prompt leaves your machine."
|
|
24741
|
+
};
|
|
24742
|
+
}
|
|
24743
|
+
if (riskReport.detected_risks.includes("financial_data")) {
|
|
24744
|
+
return {
|
|
24745
|
+
title: "Guardian found financial information",
|
|
24746
|
+
body: "Guardian can mask financial details or help you keep them out of the prompt."
|
|
24747
|
+
};
|
|
24748
|
+
}
|
|
24749
|
+
if (riskReport.detected_risks.includes("healthcare_data")) {
|
|
24750
|
+
return {
|
|
24751
|
+
title: "Guardian found health information",
|
|
24752
|
+
body: "Guardian can help remove identifying health details before you send this prompt."
|
|
24753
|
+
};
|
|
24754
|
+
}
|
|
24755
|
+
if (riskReport.detected_risks.includes("prompt_injection")) {
|
|
24756
|
+
return {
|
|
24757
|
+
title: "Guardian found prompt-injection language",
|
|
24758
|
+
body: "Guardian noticed instructions that may try to override your intended task or safety rules."
|
|
24759
|
+
};
|
|
24760
|
+
}
|
|
24761
|
+
if (riskReport.detected_risks.includes("unsafe_destination")) {
|
|
24762
|
+
return {
|
|
24763
|
+
title: "Guardian found an unfamiliar destination",
|
|
24764
|
+
body: "Guardian noticed this prompt may be going somewhere that needs an extra check."
|
|
24765
|
+
};
|
|
24766
|
+
}
|
|
24767
|
+
if (riskReport.detected_risks.includes("pii")) {
|
|
24768
|
+
return {
|
|
24769
|
+
title: "Guardian found personal information",
|
|
24770
|
+
body: "Guardian can mask personal details before this prompt leaves your machine."
|
|
24771
|
+
};
|
|
24772
|
+
}
|
|
24773
|
+
if (riskReport.detected_risks.includes("work_data")) {
|
|
24774
|
+
return {
|
|
24775
|
+
title: "Guardian found work or customer data",
|
|
24776
|
+
body: "Guardian can help keep private work details out of the prompt or replace them with placeholders."
|
|
24777
|
+
};
|
|
24778
|
+
}
|
|
24779
|
+
return {
|
|
24780
|
+
title: "Guardian can improve this prompt",
|
|
24781
|
+
body: "Guardian found context or privacy improvements that may make this prompt safer and more useful."
|
|
24782
|
+
};
|
|
24783
|
+
}
|
|
24137
24784
|
function shouldQuietPromptCoachCard(card, disposition, learningSignals) {
|
|
24138
24785
|
if (!learningSignals || disposition === "block" || disposition === "confirm") {
|
|
24139
24786
|
return false;
|
|
@@ -24218,6 +24865,19 @@ function containsFullSsn(prompt) {
|
|
|
24218
24865
|
SSN_PATTERN.lastIndex = 0;
|
|
24219
24866
|
return SSN_PATTERN.test(prompt);
|
|
24220
24867
|
}
|
|
24868
|
+
function containsSensitiveWorkData(prompt) {
|
|
24869
|
+
return WORK_SENSITIVE_PATTERN.test(prompt) || WORK_ENTITY_CONTEXT_PATTERN.test(prompt) || WORK_CONTEXT_ENTITY_PATTERN.test(prompt);
|
|
24870
|
+
}
|
|
24871
|
+
function shouldOfferContextHelp(promptManifest, policyPacks) {
|
|
24872
|
+
if (promptManifest.source_permissions_snapshot.length > 0) {
|
|
24873
|
+
return false;
|
|
24874
|
+
}
|
|
24875
|
+
if (effectivePromptCoachMode(policyPacks) !== "hands_on") {
|
|
24876
|
+
return false;
|
|
24877
|
+
}
|
|
24878
|
+
const prompt = promptManifest.prompt_text;
|
|
24879
|
+
return CONTEXT_HELP_ACTION_PATTERN.test(prompt) && CONTEXT_HELP_REFERENCE_PATTERN.test(prompt);
|
|
24880
|
+
}
|
|
24221
24881
|
function hasFullSsnRisk(policyRefs) {
|
|
24222
24882
|
return policyRefs.includes("guardian:builtin:ssn-full-value");
|
|
24223
24883
|
}
|
|
@@ -24316,7 +24976,7 @@ function digest2(value) {
|
|
|
24316
24976
|
function isPresent(value) {
|
|
24317
24977
|
return value !== void 0;
|
|
24318
24978
|
}
|
|
24319
|
-
var DEFAULT_RISK_THRESHOLDS, POLICY_PACK_PRESET_VERSION, HIGH_RISK_TERMS, SECRET_PATTERNS, EMAIL_PATTERN, SSN_PATTERN, SSN_CONTEXT_PATTERN, FINANCIAL_PATTERN, HEALTHCARE_PATTERN,
|
|
24979
|
+
var DEFAULT_RISK_THRESHOLDS, POLICY_PACK_PRESET_VERSION, HIGH_RISK_TERMS, SECRET_PATTERNS, EMAIL_PATTERN, SSN_PATTERN, SSN_CONTEXT_PATTERN, FINANCIAL_PATTERN, HEALTHCARE_PATTERN, WORK_SENSITIVE_PATTERN, WORK_ENTITY_CONTEXT_PATTERN, WORK_CONTEXT_ENTITY_PATTERN, PROMPT_INJECTION_PATTERN, CONTEXT_HELP_ACTION_PATTERN, CONTEXT_HELP_REFERENCE_PATTERN, GUARDIAN_POLICY_PACK_PRESETS;
|
|
24320
24980
|
var init_src3 = __esm({
|
|
24321
24981
|
"packages/personal-fabric-runtime/src/index.ts"() {
|
|
24322
24982
|
"use strict";
|
|
@@ -24341,8 +25001,12 @@ var init_src3 = __esm({
|
|
|
24341
25001
|
SSN_CONTEXT_PATTERN = /\b(ssn|social security(?: number)?)\b/i;
|
|
24342
25002
|
FINANCIAL_PATTERN = /\b(bank|wire|routing number|account number|payroll|invoice|revenue|forecast|contract value|credit card)\b/i;
|
|
24343
25003
|
HEALTHCARE_PATTERN = /\b(patient|diagnosis|medication|hipaa|medical record|prescription|clinical)\b/i;
|
|
24344
|
-
|
|
25004
|
+
WORK_SENSITIVE_PATTERN = /\b(confidential|internal|non[-\s]?public|private|proprietary|salesforce|pipeline|renewal)\b/i;
|
|
25005
|
+
WORK_ENTITY_CONTEXT_PATTERN = /\b(?:client|customer|employee|vendor|partner)\b[\s\S]{0,80}\b(?:contract|account|record|data|list|pricing|renewal|pipeline|payroll|confidential|private|proprietary)\b/i;
|
|
25006
|
+
WORK_CONTEXT_ENTITY_PATTERN = /\b(?:contract|account|record|data|list|pricing|renewal|pipeline|payroll|confidential|private|proprietary)\b[\s\S]{0,80}\b(?:client|customer|employee|vendor|partner)\b/i;
|
|
24345
25007
|
PROMPT_INJECTION_PATTERN = /\b(ignore previous instructions|system prompt|developer message|jailbreak|exfiltrate|bypass policy|reveal hidden)\b/i;
|
|
25008
|
+
CONTEXT_HELP_ACTION_PATTERN = /\b(summarize|summarise|analyze|analyse|review|compare|extract|rewrite|improve|turn|convert|update)\b/i;
|
|
25009
|
+
CONTEXT_HELP_REFERENCE_PATTERN = /\b(this|that|these|those|attached|uploaded|latest version|current version|file|document|deck|slide|email|thread|conversation|transcript|meeting|pdf)\b/i;
|
|
24346
25010
|
GUARDIAN_POLICY_PACK_PRESETS = [
|
|
24347
25011
|
createPolicyPackPreset({
|
|
24348
25012
|
pack_id: "privacy-first",
|
|
@@ -25781,10 +26445,14 @@ __export(daemon_exports, {
|
|
|
25781
26445
|
processGuardianControlTowerActivityRequest: () => processGuardianControlTowerActivityRequest,
|
|
25782
26446
|
processGuardianControlTowerAppPermissionRequest: () => processGuardianControlTowerAppPermissionRequest,
|
|
25783
26447
|
processGuardianControlTowerDataRequest: () => processGuardianControlTowerDataRequest,
|
|
26448
|
+
processGuardianControlTowerMarketplaceRequest: () => processGuardianControlTowerMarketplaceRequest,
|
|
26449
|
+
processGuardianControlTowerNoticeRequest: () => processGuardianControlTowerNoticeRequest,
|
|
25784
26450
|
processGuardianControlTowerPolicyRequest: () => processGuardianControlTowerPolicyRequest,
|
|
25785
26451
|
processGuardianControlTowerPreferenceRequest: () => processGuardianControlTowerPreferenceRequest,
|
|
26452
|
+
processGuardianControlTowerPrimePackRequest: () => processGuardianControlTowerPrimePackRequest,
|
|
25786
26453
|
processGuardianControlTowerProviderPermissionRequest: () => processGuardianControlTowerProviderPermissionRequest,
|
|
25787
26454
|
processGuardianControlTowerSourcePermissionRequest: () => processGuardianControlTowerSourcePermissionRequest,
|
|
26455
|
+
processGuardianControlTowerViewModelRequest: () => processGuardianControlTowerViewModelRequest,
|
|
25788
26456
|
processGuardianDaemonHealthRequest: () => processGuardianDaemonHealthRequest,
|
|
25789
26457
|
processGuardianDaemonStopRequest: () => processGuardianDaemonStopRequest,
|
|
25790
26458
|
processGuardianLiveCoachEventRequest: () => processGuardianLiveCoachEventRequest,
|
|
@@ -25794,7 +26462,7 @@ __export(daemon_exports, {
|
|
|
25794
26462
|
});
|
|
25795
26463
|
import { createHash as createHash5, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
25796
26464
|
import { existsSync as existsSync2 } from "node:fs";
|
|
25797
|
-
import { mkdir as mkdir4, readFile as readFile4, readdir as readdir2, stat as stat3 } from "node:fs/promises";
|
|
26465
|
+
import { mkdir as mkdir4, readFile as readFile4, readdir as readdir2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
|
|
25798
26466
|
import { createServer } from "node:http";
|
|
25799
26467
|
import path4 from "node:path";
|
|
25800
26468
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -25920,12 +26588,22 @@ async function handleRequest(input2) {
|
|
|
25920
26588
|
return;
|
|
25921
26589
|
}
|
|
25922
26590
|
if (input2.request.method === "GET" && (input2.request.url === "/control-tower" || input2.request.url === "/control-tower/")) {
|
|
25923
|
-
const [
|
|
26591
|
+
const [
|
|
26592
|
+
receipts,
|
|
26593
|
+
searchGraph,
|
|
26594
|
+
learningEvents,
|
|
26595
|
+
localDataStatus,
|
|
26596
|
+
releaseDoctorStatus,
|
|
26597
|
+
adminNotices,
|
|
26598
|
+
marketplaceRequests
|
|
26599
|
+
] = await Promise.all([
|
|
25924
26600
|
input2.store.listReceipts(),
|
|
25925
26601
|
input2.store.searchGraph({ limit: CONTROL_TOWER_GRAPH_NODE_LIMIT }),
|
|
25926
26602
|
input2.store.listLearningEvents({ limit: CONTROL_TOWER_LEARNING_EVENT_LIMIT }),
|
|
25927
26603
|
buildControlTowerLocalDataStatus(input2.profile, input2.env),
|
|
25928
|
-
buildControlTowerReleaseDoctorStatus(input2.profile, input2.env)
|
|
26604
|
+
buildControlTowerReleaseDoctorStatus(input2.profile, input2.env),
|
|
26605
|
+
readControlTowerAdminNotices(input2.profile.profile_dir),
|
|
26606
|
+
readControlTowerMarketplaceRequests(input2.profile.profile_dir)
|
|
25929
26607
|
]);
|
|
25930
26608
|
writeHtml(
|
|
25931
26609
|
input2.response,
|
|
@@ -25936,7 +26614,9 @@ async function handleRequest(input2) {
|
|
|
25936
26614
|
searchGraph,
|
|
25937
26615
|
learningEvents,
|
|
25938
26616
|
localDataStatus,
|
|
25939
|
-
releaseDoctorStatus
|
|
26617
|
+
releaseDoctorStatus,
|
|
26618
|
+
adminNotices,
|
|
26619
|
+
marketplaceRequests
|
|
25940
26620
|
),
|
|
25941
26621
|
{
|
|
25942
26622
|
controlToken: input2.controlToken
|
|
@@ -25945,10 +26625,45 @@ async function handleRequest(input2) {
|
|
|
25945
26625
|
return;
|
|
25946
26626
|
}
|
|
25947
26627
|
const activityUrl = parseGuardianUrl(input2.request.url);
|
|
25948
|
-
if (input2.request.method === "GET" && activityUrl?.pathname === "/v1/guardian/
|
|
25949
|
-
const
|
|
25950
|
-
|
|
25951
|
-
|
|
26628
|
+
if (input2.request.method === "GET" && activityUrl?.pathname === "/v1/guardian/control-tower/view-model") {
|
|
26629
|
+
const [
|
|
26630
|
+
receipts,
|
|
26631
|
+
searchGraph,
|
|
26632
|
+
learningEvents,
|
|
26633
|
+
localDataStatus,
|
|
26634
|
+
releaseDoctorStatus,
|
|
26635
|
+
adminNotices,
|
|
26636
|
+
marketplaceRequests
|
|
26637
|
+
] = await Promise.all([
|
|
26638
|
+
input2.store.listReceipts(),
|
|
26639
|
+
input2.store.searchGraph({ limit: CONTROL_TOWER_GRAPH_NODE_LIMIT }),
|
|
26640
|
+
input2.store.listLearningEvents({ limit: CONTROL_TOWER_LEARNING_EVENT_LIMIT }),
|
|
26641
|
+
buildControlTowerLocalDataStatus(input2.profile, input2.env),
|
|
26642
|
+
buildControlTowerReleaseDoctorStatus(input2.profile, input2.env),
|
|
26643
|
+
readControlTowerAdminNotices(input2.profile.profile_dir),
|
|
26644
|
+
readControlTowerMarketplaceRequests(input2.profile.profile_dir)
|
|
26645
|
+
]);
|
|
26646
|
+
const result2 = processGuardianControlTowerViewModelRequest(
|
|
26647
|
+
input2.profile,
|
|
26648
|
+
receipts,
|
|
26649
|
+
searchGraph,
|
|
26650
|
+
learningEvents,
|
|
26651
|
+
localDataStatus,
|
|
26652
|
+
releaseDoctorStatus,
|
|
26653
|
+
adminNotices,
|
|
26654
|
+
marketplaceRequests
|
|
26655
|
+
);
|
|
26656
|
+
writeJson(input2.response, result2.statusCode, result2.body);
|
|
26657
|
+
return;
|
|
26658
|
+
}
|
|
26659
|
+
if (input2.request.method === "GET" && activityUrl?.pathname === "/v1/guardian/activity") {
|
|
26660
|
+
const filter = parseActivityFilter(activityUrl.searchParams.get("filter") ?? "all");
|
|
26661
|
+
const [receipts, searchGraph] = await Promise.all([
|
|
26662
|
+
input2.store.listReceipts(),
|
|
26663
|
+
input2.store.searchGraph({ limit: 100 })
|
|
26664
|
+
]);
|
|
26665
|
+
const recentReceipts = receipts.slice().sort((left, right) => right.created_at.localeCompare(left.created_at)).slice(0, CONTROL_TOWER_RECEIPT_LIMIT);
|
|
26666
|
+
const result2 = processGuardianControlTowerActivityRequest(recentReceipts, filter, searchGraph);
|
|
25952
26667
|
writeJson(input2.response, result2.statusCode, result2.body);
|
|
25953
26668
|
return;
|
|
25954
26669
|
}
|
|
@@ -25989,6 +26704,24 @@ async function handleRequest(input2) {
|
|
|
25989
26704
|
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
25990
26705
|
return;
|
|
25991
26706
|
}
|
|
26707
|
+
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/prime-pack") {
|
|
26708
|
+
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
26709
|
+
if (!parsedBody2.ok) {
|
|
26710
|
+
writeJson(input2.response, parsedBody2.statusCode, parsedBody2.body);
|
|
26711
|
+
return;
|
|
26712
|
+
}
|
|
26713
|
+
const result2 = await processGuardianControlTowerPrimePackRequest({
|
|
26714
|
+
profile: input2.profile,
|
|
26715
|
+
token: input2.token,
|
|
26716
|
+
controlToken: input2.controlToken,
|
|
26717
|
+
suppliedToken: headerValue(input2.request, ADMIN_HEADER) ?? readCookieValue(input2.request, CONTROL_TOWER_COOKIE),
|
|
26718
|
+
body: parsedBody2.body,
|
|
26719
|
+
now: input2.now,
|
|
26720
|
+
store: input2.store
|
|
26721
|
+
});
|
|
26722
|
+
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26723
|
+
return;
|
|
26724
|
+
}
|
|
25992
26725
|
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/preferences") {
|
|
25993
26726
|
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
25994
26727
|
if (!parsedBody2.ok) {
|
|
@@ -26069,6 +26802,42 @@ async function handleRequest(input2) {
|
|
|
26069
26802
|
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26070
26803
|
return;
|
|
26071
26804
|
}
|
|
26805
|
+
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/notice") {
|
|
26806
|
+
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
26807
|
+
if (!parsedBody2.ok) {
|
|
26808
|
+
writeJson(input2.response, parsedBody2.statusCode, parsedBody2.body);
|
|
26809
|
+
return;
|
|
26810
|
+
}
|
|
26811
|
+
const result2 = await processGuardianControlTowerNoticeRequest({
|
|
26812
|
+
profile: input2.profile,
|
|
26813
|
+
token: input2.token,
|
|
26814
|
+
controlToken: input2.controlToken,
|
|
26815
|
+
suppliedToken: headerValue(input2.request, ADMIN_HEADER) ?? readCookieValue(input2.request, CONTROL_TOWER_COOKIE),
|
|
26816
|
+
body: parsedBody2.body,
|
|
26817
|
+
now: input2.now,
|
|
26818
|
+
store: input2.store
|
|
26819
|
+
});
|
|
26820
|
+
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26821
|
+
return;
|
|
26822
|
+
}
|
|
26823
|
+
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/marketplace-request") {
|
|
26824
|
+
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
26825
|
+
if (!parsedBody2.ok) {
|
|
26826
|
+
writeJson(input2.response, parsedBody2.statusCode, parsedBody2.body);
|
|
26827
|
+
return;
|
|
26828
|
+
}
|
|
26829
|
+
const result2 = await processGuardianControlTowerMarketplaceRequest({
|
|
26830
|
+
profile: input2.profile,
|
|
26831
|
+
token: input2.token,
|
|
26832
|
+
controlToken: input2.controlToken,
|
|
26833
|
+
suppliedToken: headerValue(input2.request, ADMIN_HEADER) ?? readCookieValue(input2.request, CONTROL_TOWER_COOKIE),
|
|
26834
|
+
body: parsedBody2.body,
|
|
26835
|
+
now: input2.now,
|
|
26836
|
+
store: input2.store
|
|
26837
|
+
});
|
|
26838
|
+
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26839
|
+
return;
|
|
26840
|
+
}
|
|
26072
26841
|
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/live-coach-event") {
|
|
26073
26842
|
const suppliedToken2 = headerValue(input2.request, ADMIN_HEADER);
|
|
26074
26843
|
const parsedBody2 = await readGuardianDaemonJsonBody(input2.request);
|
|
@@ -26127,7 +26896,7 @@ function processGuardianDaemonHealthRequest(input2) {
|
|
|
26127
26896
|
}
|
|
26128
26897
|
};
|
|
26129
26898
|
}
|
|
26130
|
-
function processGuardianControlTowerActivityRequest(receipts, filterValue) {
|
|
26899
|
+
function processGuardianControlTowerActivityRequest(receipts, filterValue, searchGraph = { nodes: [], edges: [] }) {
|
|
26131
26900
|
const filter = parseActivityFilter(filterValue);
|
|
26132
26901
|
return {
|
|
26133
26902
|
statusCode: 200,
|
|
@@ -26137,10 +26906,27 @@ function processGuardianControlTowerActivityRequest(receipts, filterValue) {
|
|
|
26137
26906
|
filter,
|
|
26138
26907
|
raw_content_included: false,
|
|
26139
26908
|
token_printed: false,
|
|
26140
|
-
activities: buildActivityItems(receipts, filter)
|
|
26909
|
+
activities: buildActivityItems(receipts, filter, searchGraph)
|
|
26141
26910
|
}
|
|
26142
26911
|
};
|
|
26143
26912
|
}
|
|
26913
|
+
function processGuardianControlTowerViewModelRequest(profile, receipts = [], searchGraph = { nodes: [], edges: [] }, learningEvents = [], localDataStatus = createEmptyControlTowerLocalDataStatus(profile), releaseDoctorStatus = createEmptyControlTowerReleaseDoctorStatus(
|
|
26914
|
+
profile
|
|
26915
|
+
), adminNotices = [], marketplaceRequests = []) {
|
|
26916
|
+
return {
|
|
26917
|
+
statusCode: 200,
|
|
26918
|
+
body: buildControlTowerViewModel(
|
|
26919
|
+
profile,
|
|
26920
|
+
receipts,
|
|
26921
|
+
searchGraph,
|
|
26922
|
+
learningEvents,
|
|
26923
|
+
localDataStatus,
|
|
26924
|
+
releaseDoctorStatus,
|
|
26925
|
+
adminNotices,
|
|
26926
|
+
marketplaceRequests
|
|
26927
|
+
)
|
|
26928
|
+
};
|
|
26929
|
+
}
|
|
26144
26930
|
function processGuardianDaemonStopRequest(input2) {
|
|
26145
26931
|
if (!isAuthorized(input2.token, input2.suppliedToken)) {
|
|
26146
26932
|
return {
|
|
@@ -26206,6 +26992,105 @@ async function processGuardianControlTowerPolicyRequest(input2) {
|
|
|
26206
26992
|
};
|
|
26207
26993
|
}
|
|
26208
26994
|
}
|
|
26995
|
+
async function processGuardianControlTowerPrimePackRequest(input2) {
|
|
26996
|
+
if (!isAdminAuthorized(input2)) {
|
|
26997
|
+
return {
|
|
26998
|
+
statusCode: 401,
|
|
26999
|
+
body: {
|
|
27000
|
+
ok: false,
|
|
27001
|
+
error: "unauthorized"
|
|
27002
|
+
}
|
|
27003
|
+
};
|
|
27004
|
+
}
|
|
27005
|
+
const action = parsePrimePackAction(input2.body.action);
|
|
27006
|
+
if (!action) {
|
|
27007
|
+
return {
|
|
27008
|
+
statusCode: 400,
|
|
27009
|
+
body: {
|
|
27010
|
+
ok: false,
|
|
27011
|
+
error: "invalid_prime_pack_action"
|
|
27012
|
+
}
|
|
27013
|
+
};
|
|
27014
|
+
}
|
|
27015
|
+
const inputs = {
|
|
27016
|
+
sensitive_terms: parsePrimePackEntries([
|
|
27017
|
+
input2.body.sensitiveTerms,
|
|
27018
|
+
parsePrimePackImportEntries(input2.body.importContent, "sensitive_terms")
|
|
27019
|
+
]),
|
|
27020
|
+
approved_domains: parsePrimePackEntries(
|
|
27021
|
+
[
|
|
27022
|
+
input2.body.approvedDomains,
|
|
27023
|
+
parsePrimePackImportEntries(input2.body.importContent, "approved_domains")
|
|
27024
|
+
],
|
|
27025
|
+
{ lowercase: true }
|
|
27026
|
+
),
|
|
27027
|
+
blocked_destinations: parsePrimePackEntries(
|
|
27028
|
+
[
|
|
27029
|
+
input2.body.blockedDestinations,
|
|
27030
|
+
parsePrimePackImportEntries(input2.body.importContent, "blocked_destinations")
|
|
27031
|
+
],
|
|
27032
|
+
{
|
|
27033
|
+
lowercase: true
|
|
27034
|
+
}
|
|
27035
|
+
),
|
|
27036
|
+
escalation_phrases: parsePrimePackEntries([
|
|
27037
|
+
input2.body.escalationPhrases,
|
|
27038
|
+
parsePrimePackImportEntries(input2.body.importContent, "escalation_phrases")
|
|
27039
|
+
])
|
|
27040
|
+
};
|
|
27041
|
+
const preview = buildPrimePackPreview(inputs);
|
|
27042
|
+
if (preview.total_inputs === 0) {
|
|
27043
|
+
return {
|
|
27044
|
+
statusCode: 400,
|
|
27045
|
+
body: {
|
|
27046
|
+
ok: false,
|
|
27047
|
+
error: "empty_prime_pack"
|
|
27048
|
+
}
|
|
27049
|
+
};
|
|
27050
|
+
}
|
|
27051
|
+
const now = (input2.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
27052
|
+
const draftSeed = JSON.stringify(inputs);
|
|
27053
|
+
const transactionId = `txn:prime-pack:${digest4(`${draftSeed}:${now}`).slice(0, 20)}`;
|
|
27054
|
+
const guardianReceipt = action === "deploy" ? buildPrimePackDeployGuardianReceipt(inputs, preview, transactionId, now) : void 0;
|
|
27055
|
+
const draft = {
|
|
27056
|
+
schema_version: "contextecf/project-guardian-prime-pack-draft/v1",
|
|
27057
|
+
draft_id: `prime-pack:${digest4(draftSeed).slice(0, 16)}`,
|
|
27058
|
+
status: action === "deploy" ? "deployed_metadata_only" : "preview_ready",
|
|
27059
|
+
updated_at: now,
|
|
27060
|
+
...action === "deploy" ? { deployed_at: now } : {},
|
|
27061
|
+
inputs,
|
|
27062
|
+
preview,
|
|
27063
|
+
...guardianReceipt ? {
|
|
27064
|
+
local_receipt: buildPrimePackDeployReceipt(guardianReceipt)
|
|
27065
|
+
} : {},
|
|
27066
|
+
raw_prompt_content_included: false,
|
|
27067
|
+
token_printed: false
|
|
27068
|
+
};
|
|
27069
|
+
if (guardianReceipt && input2.store) {
|
|
27070
|
+
await input2.store.putReceipt(guardianReceipt);
|
|
27071
|
+
await input2.store.appendEvent(
|
|
27072
|
+
buildPrimePackDeployEvent(inputs, preview, transactionId, now, guardianReceipt)
|
|
27073
|
+
);
|
|
27074
|
+
}
|
|
27075
|
+
await writeControlTowerPrimePackDraft(input2.profile.profile_dir, draft);
|
|
27076
|
+
return {
|
|
27077
|
+
statusCode: 200,
|
|
27078
|
+
body: {
|
|
27079
|
+
ok: true,
|
|
27080
|
+
schema_version: "contextecf/project-guardian-control-tower-prime-pack/v1",
|
|
27081
|
+
action,
|
|
27082
|
+
status: draft.status,
|
|
27083
|
+
draft_id: draft.draft_id,
|
|
27084
|
+
file: CONTROL_TOWER_PRIME_PACK_FILE_NAME,
|
|
27085
|
+
preview,
|
|
27086
|
+
local_receipt: draft.local_receipt,
|
|
27087
|
+
receipt_id: guardianReceipt?.receipt_id,
|
|
27088
|
+
stored_locally: true,
|
|
27089
|
+
raw_prompt_content_included: false,
|
|
27090
|
+
token_printed: false
|
|
27091
|
+
}
|
|
27092
|
+
};
|
|
27093
|
+
}
|
|
26209
27094
|
async function processGuardianControlTowerPreferenceRequest(input2) {
|
|
26210
27095
|
if (!isAdminAuthorized(input2)) {
|
|
26211
27096
|
return {
|
|
@@ -26492,6 +27377,190 @@ async function processGuardianControlTowerDataRequest(input2) {
|
|
|
26492
27377
|
};
|
|
26493
27378
|
}
|
|
26494
27379
|
}
|
|
27380
|
+
async function processGuardianControlTowerNoticeRequest(input2) {
|
|
27381
|
+
if (!isAdminAuthorized(input2)) {
|
|
27382
|
+
return {
|
|
27383
|
+
statusCode: 401,
|
|
27384
|
+
body: {
|
|
27385
|
+
ok: false,
|
|
27386
|
+
error: "unauthorized"
|
|
27387
|
+
}
|
|
27388
|
+
};
|
|
27389
|
+
}
|
|
27390
|
+
const action = input2.body.action?.trim() || "publish";
|
|
27391
|
+
if (action === "clear") {
|
|
27392
|
+
const clearedAt = input2.now().toISOString();
|
|
27393
|
+
const transactionId2 = `txn:admin-notice:${digest4(`clear:${clearedAt}`).slice(0, 20)}`;
|
|
27394
|
+
const receipt2 = buildAdminNoticeReceipt({
|
|
27395
|
+
action,
|
|
27396
|
+
transactionId: transactionId2,
|
|
27397
|
+
timestamp: clearedAt
|
|
27398
|
+
});
|
|
27399
|
+
await input2.store.putReceipt(receipt2);
|
|
27400
|
+
await input2.store.appendEvent(
|
|
27401
|
+
buildAdminNoticeEvent({ action, transactionId: transactionId2, timestamp: clearedAt, receipt: receipt2 })
|
|
27402
|
+
);
|
|
27403
|
+
await writeControlTowerAdminNotices(input2.profile.profile_dir, []);
|
|
27404
|
+
return {
|
|
27405
|
+
statusCode: 200,
|
|
27406
|
+
body: {
|
|
27407
|
+
ok: true,
|
|
27408
|
+
schema_version: "contextecf/project-guardian-control-tower-admin-notice/v1",
|
|
27409
|
+
action,
|
|
27410
|
+
notices: [],
|
|
27411
|
+
receipt_id: receipt2.receipt_id,
|
|
27412
|
+
raw_content_included: false,
|
|
27413
|
+
token_printed: false
|
|
27414
|
+
}
|
|
27415
|
+
};
|
|
27416
|
+
}
|
|
27417
|
+
if (action !== "publish") {
|
|
27418
|
+
return {
|
|
27419
|
+
statusCode: 400,
|
|
27420
|
+
body: {
|
|
27421
|
+
ok: false,
|
|
27422
|
+
error: "invalid_notice_action"
|
|
27423
|
+
}
|
|
27424
|
+
};
|
|
27425
|
+
}
|
|
27426
|
+
const title = boundedNoticeText(input2.body.title, CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT);
|
|
27427
|
+
const message = boundedNoticeText(input2.body.message, CONTROL_TOWER_ADMIN_NOTICE_MESSAGE_LIMIT);
|
|
27428
|
+
const audience = parseControlTowerAdminNoticeAudience(input2.body.audience);
|
|
27429
|
+
const tone = parseControlTowerAdminNoticeTone(input2.body.tone);
|
|
27430
|
+
if (!title || !message || !audience || !tone) {
|
|
27431
|
+
return {
|
|
27432
|
+
statusCode: 400,
|
|
27433
|
+
body: {
|
|
27434
|
+
ok: false,
|
|
27435
|
+
error: "invalid_notice_request"
|
|
27436
|
+
}
|
|
27437
|
+
};
|
|
27438
|
+
}
|
|
27439
|
+
const existingNotices = await readControlTowerAdminNotices(input2.profile.profile_dir);
|
|
27440
|
+
const createdAt = input2.now().toISOString();
|
|
27441
|
+
const transactionId = `txn:admin-notice:${digest4(
|
|
27442
|
+
`${title}:${message}:${audience}:${tone}:${createdAt}`
|
|
27443
|
+
).slice(0, 20)}`;
|
|
27444
|
+
const receipt = buildAdminNoticeReceipt({
|
|
27445
|
+
action,
|
|
27446
|
+
transactionId,
|
|
27447
|
+
timestamp: createdAt,
|
|
27448
|
+
title,
|
|
27449
|
+
message,
|
|
27450
|
+
audience,
|
|
27451
|
+
tone
|
|
27452
|
+
});
|
|
27453
|
+
const notice = {
|
|
27454
|
+
id: `notice:${digest4(`${title}:${message}:${createdAt}`).slice(0, 16)}`,
|
|
27455
|
+
title,
|
|
27456
|
+
message,
|
|
27457
|
+
audience,
|
|
27458
|
+
tone,
|
|
27459
|
+
created_at: createdAt,
|
|
27460
|
+
receipt_id: receipt.receipt_id,
|
|
27461
|
+
receipt_hash: receipt.receipt_hash,
|
|
27462
|
+
raw_content_included: false
|
|
27463
|
+
};
|
|
27464
|
+
const notices = [notice, ...existingNotices].slice(0, CONTROL_TOWER_ADMIN_NOTICE_LIMIT);
|
|
27465
|
+
await input2.store.putReceipt(receipt);
|
|
27466
|
+
await input2.store.appendEvent(
|
|
27467
|
+
buildAdminNoticeEvent({
|
|
27468
|
+
action,
|
|
27469
|
+
transactionId,
|
|
27470
|
+
timestamp: createdAt,
|
|
27471
|
+
receipt,
|
|
27472
|
+
title,
|
|
27473
|
+
message,
|
|
27474
|
+
audience,
|
|
27475
|
+
tone
|
|
27476
|
+
})
|
|
27477
|
+
);
|
|
27478
|
+
await writeControlTowerAdminNotices(input2.profile.profile_dir, notices);
|
|
27479
|
+
return {
|
|
27480
|
+
statusCode: 200,
|
|
27481
|
+
body: {
|
|
27482
|
+
ok: true,
|
|
27483
|
+
schema_version: "contextecf/project-guardian-control-tower-admin-notice/v1",
|
|
27484
|
+
action,
|
|
27485
|
+
notice,
|
|
27486
|
+
notices,
|
|
27487
|
+
receipt_id: receipt.receipt_id,
|
|
27488
|
+
raw_content_included: false,
|
|
27489
|
+
token_printed: false
|
|
27490
|
+
}
|
|
27491
|
+
};
|
|
27492
|
+
}
|
|
27493
|
+
async function processGuardianControlTowerMarketplaceRequest(input2) {
|
|
27494
|
+
if (!isAdminAuthorized(input2)) {
|
|
27495
|
+
return {
|
|
27496
|
+
statusCode: 401,
|
|
27497
|
+
body: {
|
|
27498
|
+
ok: false,
|
|
27499
|
+
error: "unauthorized"
|
|
27500
|
+
}
|
|
27501
|
+
};
|
|
27502
|
+
}
|
|
27503
|
+
const action = parseMarketplaceRequestAction(input2.body.action);
|
|
27504
|
+
const targetId = input2.body.packId?.trim();
|
|
27505
|
+
const manifest = targetId ? findControlTowerMarketplaceManifest(targetId) : void 0;
|
|
27506
|
+
if (!action || !manifest) {
|
|
27507
|
+
return {
|
|
27508
|
+
statusCode: 400,
|
|
27509
|
+
body: {
|
|
27510
|
+
ok: false,
|
|
27511
|
+
error: "invalid_marketplace_request"
|
|
27512
|
+
}
|
|
27513
|
+
};
|
|
27514
|
+
}
|
|
27515
|
+
const requestedAt = input2.now().toISOString();
|
|
27516
|
+
const transactionId = `txn:marketplace-request:${digest4(
|
|
27517
|
+
`${manifest.marketplace_manifest_id}:${action}:${requestedAt}`
|
|
27518
|
+
).slice(0, 20)}`;
|
|
27519
|
+
const receipt = buildMarketplaceRequestReceipt(manifest, action, transactionId, requestedAt);
|
|
27520
|
+
const request = {
|
|
27521
|
+
schema_version: "contextecf/project-guardian-marketplace-request/v1",
|
|
27522
|
+
request_id: `request:${transactionId}`,
|
|
27523
|
+
pack_id: manifest.pack_id,
|
|
27524
|
+
marketplace_manifest_id: manifest.marketplace_manifest_id,
|
|
27525
|
+
pack_version: manifest.pack_version,
|
|
27526
|
+
pack_name: manifest.display_name,
|
|
27527
|
+
publisher: manifest.publisher_display_name,
|
|
27528
|
+
action,
|
|
27529
|
+
status: "requested_metadata_only",
|
|
27530
|
+
requested_at: requestedAt,
|
|
27531
|
+
receipt_id: receipt.receipt_id,
|
|
27532
|
+
receipt_hash: receipt.receipt_hash,
|
|
27533
|
+
raw_content_included: false,
|
|
27534
|
+
token_printed: false
|
|
27535
|
+
};
|
|
27536
|
+
const existingRequests = await readControlTowerMarketplaceRequests(input2.profile.profile_dir);
|
|
27537
|
+
const requests = [
|
|
27538
|
+
request,
|
|
27539
|
+
...existingRequests.filter(
|
|
27540
|
+
(existing) => existing.marketplace_manifest_id !== manifest.marketplace_manifest_id
|
|
27541
|
+
)
|
|
27542
|
+
].slice(0, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT);
|
|
27543
|
+
await input2.store.putReceipt(receipt);
|
|
27544
|
+
await input2.store.appendEvent(
|
|
27545
|
+
buildMarketplaceRequestEvent(manifest, action, transactionId, requestedAt, receipt)
|
|
27546
|
+
);
|
|
27547
|
+
await writeControlTowerMarketplaceRequests(input2.profile.profile_dir, requests);
|
|
27548
|
+
return {
|
|
27549
|
+
statusCode: 200,
|
|
27550
|
+
body: {
|
|
27551
|
+
ok: true,
|
|
27552
|
+
schema_version: "contextecf/project-guardian-control-tower-marketplace-request/v1",
|
|
27553
|
+
action,
|
|
27554
|
+
status: request.status,
|
|
27555
|
+
request,
|
|
27556
|
+
request_file: CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME,
|
|
27557
|
+
receipt_id: receipt.receipt_id,
|
|
27558
|
+
stored_locally: true,
|
|
27559
|
+
raw_content_included: false,
|
|
27560
|
+
token_printed: false
|
|
27561
|
+
}
|
|
27562
|
+
};
|
|
27563
|
+
}
|
|
26495
27564
|
function parseAppPermissionAction(value) {
|
|
26496
27565
|
return value === "revoke" || value === "allow" ? value : void 0;
|
|
26497
27566
|
}
|
|
@@ -26507,6 +27576,25 @@ function parseGuardianSourceType2(value) {
|
|
|
26507
27576
|
}
|
|
26508
27577
|
return GUARDIAN_SOURCE_TYPES.includes(value) ? value : void 0;
|
|
26509
27578
|
}
|
|
27579
|
+
function parseControlTowerAdminNoticeAudience(value) {
|
|
27580
|
+
return value === "all_users" || value === "admins" || value === "developers" ? value : void 0;
|
|
27581
|
+
}
|
|
27582
|
+
function parseControlTowerAdminNoticeTone(value) {
|
|
27583
|
+
return value === "info" || value === "warning" || value === "success" ? value : void 0;
|
|
27584
|
+
}
|
|
27585
|
+
function boundedNoticeText(value, limit) {
|
|
27586
|
+
if (typeof value !== "string") {
|
|
27587
|
+
return void 0;
|
|
27588
|
+
}
|
|
27589
|
+
const trimmed = value.replace(/\s+/gu, " ").trim();
|
|
27590
|
+
if (!trimmed) {
|
|
27591
|
+
return void 0;
|
|
27592
|
+
}
|
|
27593
|
+
return trimmed.slice(0, limit);
|
|
27594
|
+
}
|
|
27595
|
+
function isRecord(value) {
|
|
27596
|
+
return typeof value === "object" && value !== null;
|
|
27597
|
+
}
|
|
26510
27598
|
async function processGuardianBrowserBridgeRequest(input2) {
|
|
26511
27599
|
if (!isAuthorized(input2.token, input2.suppliedToken)) {
|
|
26512
27600
|
return {
|
|
@@ -26647,6 +27735,11 @@ async function processBrowserPrompt(input2) {
|
|
|
26647
27735
|
visible_message: decision.visible_message,
|
|
26648
27736
|
required_confirmation: decision.required_confirmation,
|
|
26649
27737
|
coach_card: decision.coach_card,
|
|
27738
|
+
risk_report: {
|
|
27739
|
+
risk_tier: decision.risk_report.risk_tier,
|
|
27740
|
+
detected_risks: decision.risk_report.detected_risks,
|
|
27741
|
+
matched_policy_refs: decision.risk_report.matched_policy_refs
|
|
27742
|
+
},
|
|
26650
27743
|
receipt_id: decision.receipt.receipt_id
|
|
26651
27744
|
};
|
|
26652
27745
|
}
|
|
@@ -26837,6 +27930,114 @@ function buildLiveCoachEvent(request, transactionId, timestamp, receipt) {
|
|
|
26837
27930
|
redaction_state: "metadata_only"
|
|
26838
27931
|
};
|
|
26839
27932
|
}
|
|
27933
|
+
function parseMarketplaceRequestAction(value) {
|
|
27934
|
+
return value === "request_review" ? value : void 0;
|
|
27935
|
+
}
|
|
27936
|
+
function findControlTowerMarketplaceManifest(targetId) {
|
|
27937
|
+
return GUARDIAN_POLICY_PACK_MARKETPLACE_CATALOG.find(
|
|
27938
|
+
(manifest) => manifest.pack_id === targetId || manifest.marketplace_manifest_id === targetId
|
|
27939
|
+
);
|
|
27940
|
+
}
|
|
27941
|
+
function buildMarketplaceRequestReceipt(manifest, action, transactionId, timestamp) {
|
|
27942
|
+
const core = {
|
|
27943
|
+
transaction_id: transactionId,
|
|
27944
|
+
pack_id: manifest.pack_id,
|
|
27945
|
+
marketplace_manifest_id: manifest.marketplace_manifest_id,
|
|
27946
|
+
pack_version: manifest.pack_version,
|
|
27947
|
+
action,
|
|
27948
|
+
created_at: timestamp,
|
|
27949
|
+
raw_content_included: false
|
|
27950
|
+
};
|
|
27951
|
+
return {
|
|
27952
|
+
schema_version: PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION,
|
|
27953
|
+
receipt_id: `receipt:${transactionId}:marketplace-request`,
|
|
27954
|
+
transaction_id: transactionId,
|
|
27955
|
+
receipt_type: "policy",
|
|
27956
|
+
disposition: "allow",
|
|
27957
|
+
policy_basis: [
|
|
27958
|
+
"marketplace-request-metadata-only",
|
|
27959
|
+
`pack:${manifest.pack_id}`,
|
|
27960
|
+
`manifest:${manifest.marketplace_manifest_id}`
|
|
27961
|
+
],
|
|
27962
|
+
receipt_hash: `sha256:${digest4(JSON.stringify(core))}`,
|
|
27963
|
+
raw_content_included: false,
|
|
27964
|
+
created_at: timestamp,
|
|
27965
|
+
summary: `Guardian recorded a marketplace review request for ${manifest.display_name}.`
|
|
27966
|
+
};
|
|
27967
|
+
}
|
|
27968
|
+
function buildMarketplaceRequestEvent(manifest, action, transactionId, timestamp, receipt) {
|
|
27969
|
+
const payload = {
|
|
27970
|
+
pack_id: manifest.pack_id,
|
|
27971
|
+
marketplace_manifest_id: manifest.marketplace_manifest_id,
|
|
27972
|
+
pack_version: manifest.pack_version,
|
|
27973
|
+
action,
|
|
27974
|
+
status: "requested_metadata_only",
|
|
27975
|
+
raw_content_included: false
|
|
27976
|
+
};
|
|
27977
|
+
return {
|
|
27978
|
+
event_id: `event:${transactionId}:marketplace-request`,
|
|
27979
|
+
transaction_id: transactionId,
|
|
27980
|
+
event_type: "policy_decided",
|
|
27981
|
+
occurred_at: timestamp,
|
|
27982
|
+
actor: "guardian-daemon",
|
|
27983
|
+
summary: `Marketplace pack review requested for ${manifest.display_name}.`,
|
|
27984
|
+
payload_hash: `sha256:${digest4(JSON.stringify(payload))}`,
|
|
27985
|
+
receipt_id: receipt.receipt_id,
|
|
27986
|
+
graph_refs: [`policy-pack:${manifest.pack_id}`, `receipt:${receipt.receipt_id}`],
|
|
27987
|
+
redaction_state: "metadata_only"
|
|
27988
|
+
};
|
|
27989
|
+
}
|
|
27990
|
+
function buildAdminNoticeReceipt(input2) {
|
|
27991
|
+
const core = {
|
|
27992
|
+
transaction_id: input2.transactionId,
|
|
27993
|
+
action: input2.action,
|
|
27994
|
+
title_hash: input2.title ? digest4(input2.title) : void 0,
|
|
27995
|
+
message_hash: input2.message ? digest4(input2.message) : void 0,
|
|
27996
|
+
audience: input2.audience ?? "all_users",
|
|
27997
|
+
tone: input2.tone ?? "info",
|
|
27998
|
+
created_at: input2.timestamp,
|
|
27999
|
+
raw_content_included: false
|
|
28000
|
+
};
|
|
28001
|
+
const audienceLabel = adminNoticeAudienceLabel(input2.audience ?? "all_users");
|
|
28002
|
+
return {
|
|
28003
|
+
schema_version: PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION,
|
|
28004
|
+
receipt_id: `receipt:${input2.transactionId}:admin-notice`,
|
|
28005
|
+
transaction_id: input2.transactionId,
|
|
28006
|
+
receipt_type: "policy",
|
|
28007
|
+
disposition: "allow",
|
|
28008
|
+
policy_basis: [
|
|
28009
|
+
"admin-notice-metadata-only",
|
|
28010
|
+
`action:${input2.action}`,
|
|
28011
|
+
`audience:${input2.audience ?? "all_users"}`
|
|
28012
|
+
],
|
|
28013
|
+
receipt_hash: `sha256:${digest4(JSON.stringify(core))}`,
|
|
28014
|
+
raw_content_included: false,
|
|
28015
|
+
created_at: input2.timestamp,
|
|
28016
|
+
summary: input2.action === "clear" ? "Guardian cleared local admin notices." : `Guardian published a local admin notice for ${audienceLabel}.`
|
|
28017
|
+
};
|
|
28018
|
+
}
|
|
28019
|
+
function buildAdminNoticeEvent(input2) {
|
|
28020
|
+
const payload = {
|
|
28021
|
+
action: input2.action,
|
|
28022
|
+
title_hash: input2.title ? digest4(input2.title) : void 0,
|
|
28023
|
+
message_hash: input2.message ? digest4(input2.message) : void 0,
|
|
28024
|
+
audience: input2.audience ?? "all_users",
|
|
28025
|
+
tone: input2.tone ?? "info",
|
|
28026
|
+
raw_content_included: false
|
|
28027
|
+
};
|
|
28028
|
+
return {
|
|
28029
|
+
event_id: `event:${input2.transactionId}:admin-notice`,
|
|
28030
|
+
transaction_id: input2.transactionId,
|
|
28031
|
+
event_type: "policy_decided",
|
|
28032
|
+
occurred_at: input2.timestamp,
|
|
28033
|
+
actor: "guardian-daemon",
|
|
28034
|
+
summary: input2.action === "clear" ? "Admin notice bar cleared." : `Admin notice bar updated for ${adminNoticeAudienceLabel(input2.audience ?? "all_users")}.`,
|
|
28035
|
+
payload_hash: `sha256:${digest4(JSON.stringify(payload))}`,
|
|
28036
|
+
receipt_id: input2.receipt.receipt_id,
|
|
28037
|
+
graph_refs: [`admin-notice:${input2.action}`, `receipt:${input2.receipt.receipt_id}`],
|
|
28038
|
+
redaction_state: "metadata_only"
|
|
28039
|
+
};
|
|
28040
|
+
}
|
|
26840
28041
|
function liveCoachCategoryLabel(category) {
|
|
26841
28042
|
if (category === "ssn") return "possible SSN sharing";
|
|
26842
28043
|
if (category === "password") return "possible credential sharing";
|
|
@@ -26952,6 +28153,218 @@ function isAdminAuthorized(input2) {
|
|
|
26952
28153
|
function parsePolicyPackAction(value) {
|
|
26953
28154
|
return value === "enable" || value === "disable" ? value : void 0;
|
|
26954
28155
|
}
|
|
28156
|
+
function parsePrimePackAction(value) {
|
|
28157
|
+
return value === "preview" || value === "deploy" ? value : void 0;
|
|
28158
|
+
}
|
|
28159
|
+
function parsePrimePackEntries(value, options = {}) {
|
|
28160
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28161
|
+
const entries = [];
|
|
28162
|
+
for (const rawEntry of flattenPrimePackEntryValues(value)) {
|
|
28163
|
+
const normalized = rawEntry.replace(/\s+/gu, " ").trim();
|
|
28164
|
+
if (!normalized) {
|
|
28165
|
+
continue;
|
|
28166
|
+
}
|
|
28167
|
+
const bounded = normalized.slice(0, CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT);
|
|
28168
|
+
const entry = options.lowercase ? bounded.toLowerCase() : bounded;
|
|
28169
|
+
const key = entry.toLowerCase();
|
|
28170
|
+
if (seen.has(key)) {
|
|
28171
|
+
continue;
|
|
28172
|
+
}
|
|
28173
|
+
seen.add(key);
|
|
28174
|
+
entries.push(entry);
|
|
28175
|
+
if (entries.length >= CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT) {
|
|
28176
|
+
break;
|
|
28177
|
+
}
|
|
28178
|
+
}
|
|
28179
|
+
return entries;
|
|
28180
|
+
}
|
|
28181
|
+
function flattenPrimePackEntryValues(value) {
|
|
28182
|
+
if (Array.isArray(value)) {
|
|
28183
|
+
return value.flatMap((entry) => flattenPrimePackEntryValues(entry));
|
|
28184
|
+
}
|
|
28185
|
+
if (typeof value !== "string") {
|
|
28186
|
+
return [];
|
|
28187
|
+
}
|
|
28188
|
+
return value.split(/[\n,;]+/u);
|
|
28189
|
+
}
|
|
28190
|
+
function parsePrimePackImportEntries(value, key) {
|
|
28191
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
28192
|
+
return [];
|
|
28193
|
+
}
|
|
28194
|
+
const bounded = value.slice(0, CONTROL_TOWER_PRIME_PACK_IMPORT_LIMIT);
|
|
28195
|
+
const parsedJson = parsePrimePackImportJson(bounded, key);
|
|
28196
|
+
if (parsedJson.length > 0) {
|
|
28197
|
+
return parsedJson;
|
|
28198
|
+
}
|
|
28199
|
+
const rows = parsePrimePackImportRows(bounded);
|
|
28200
|
+
return rows.filter((row) => normalizePrimePackImportKey(row[0]) === key).flatMap((row) => row.slice(1));
|
|
28201
|
+
}
|
|
28202
|
+
function parsePrimePackImportJson(value, key) {
|
|
28203
|
+
const trimmed = value.trim();
|
|
28204
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
28205
|
+
return [];
|
|
28206
|
+
}
|
|
28207
|
+
try {
|
|
28208
|
+
const parsed = JSON.parse(trimmed);
|
|
28209
|
+
return extractPrimePackImportJsonValues(parsed, key);
|
|
28210
|
+
} catch {
|
|
28211
|
+
return [];
|
|
28212
|
+
}
|
|
28213
|
+
}
|
|
28214
|
+
function extractPrimePackImportJsonValues(value, key) {
|
|
28215
|
+
if (Array.isArray(value)) {
|
|
28216
|
+
return value.flatMap((entry) => extractPrimePackImportJsonValues(entry, key));
|
|
28217
|
+
}
|
|
28218
|
+
if (!isRecord(value)) {
|
|
28219
|
+
return [];
|
|
28220
|
+
}
|
|
28221
|
+
const direct = value[key];
|
|
28222
|
+
if (Array.isArray(direct)) {
|
|
28223
|
+
return direct.filter((entry) => typeof entry === "string");
|
|
28224
|
+
}
|
|
28225
|
+
if (typeof direct === "string") {
|
|
28226
|
+
return [direct];
|
|
28227
|
+
}
|
|
28228
|
+
const type = normalizePrimePackImportKey(value.type);
|
|
28229
|
+
const term = value.value ?? value.term ?? value.pattern ?? value.phrase ?? value.domain ?? value.destination;
|
|
28230
|
+
if (type === key && typeof term === "string") {
|
|
28231
|
+
return [term];
|
|
28232
|
+
}
|
|
28233
|
+
return [];
|
|
28234
|
+
}
|
|
28235
|
+
function parsePrimePackImportRows(value) {
|
|
28236
|
+
return value.split(/\r?\n/u).map((line) => splitPrimePackImportRow(line).map((cell) => cell.trim())).filter((row) => row.some(Boolean));
|
|
28237
|
+
}
|
|
28238
|
+
function splitPrimePackImportRow(line) {
|
|
28239
|
+
const delimiter = line.includes(" ") ? " " : ",";
|
|
28240
|
+
return line.split(delimiter);
|
|
28241
|
+
}
|
|
28242
|
+
function normalizePrimePackImportKey(value) {
|
|
28243
|
+
if (typeof value !== "string") {
|
|
28244
|
+
return void 0;
|
|
28245
|
+
}
|
|
28246
|
+
const normalized = value.trim().toLowerCase().replace(/[\s-]+/gu, "_");
|
|
28247
|
+
switch (normalized) {
|
|
28248
|
+
case "sensitive":
|
|
28249
|
+
case "sensitive_term":
|
|
28250
|
+
case "sensitive_terms":
|
|
28251
|
+
case "term":
|
|
28252
|
+
case "terms":
|
|
28253
|
+
return "sensitive_terms";
|
|
28254
|
+
case "approved_domain":
|
|
28255
|
+
case "approved_domains":
|
|
28256
|
+
case "allow_domain":
|
|
28257
|
+
case "allowed_domain":
|
|
28258
|
+
case "domain":
|
|
28259
|
+
return "approved_domains";
|
|
28260
|
+
case "blocked_destination":
|
|
28261
|
+
case "blocked_destinations":
|
|
28262
|
+
case "blocked_domain":
|
|
28263
|
+
case "deny_destination":
|
|
28264
|
+
case "destination":
|
|
28265
|
+
return "blocked_destinations";
|
|
28266
|
+
case "escalation":
|
|
28267
|
+
case "escalation_phrase":
|
|
28268
|
+
case "escalation_phrases":
|
|
28269
|
+
case "confirmation_phrase":
|
|
28270
|
+
case "phrase":
|
|
28271
|
+
return "escalation_phrases";
|
|
28272
|
+
default:
|
|
28273
|
+
return void 0;
|
|
28274
|
+
}
|
|
28275
|
+
}
|
|
28276
|
+
function buildPrimePackPreview(inputs) {
|
|
28277
|
+
const totalInputs = inputs.sensitive_terms.length + inputs.approved_domains.length + inputs.blocked_destinations.length + inputs.escalation_phrases.length;
|
|
28278
|
+
const examples = [
|
|
28279
|
+
inputs.sensitive_terms.length > 0 ? `${inputs.sensitive_terms.length} sensitive term(s) will be flagged before prompt send.` : "",
|
|
28280
|
+
inputs.blocked_destinations.length > 0 ? `${inputs.blocked_destinations.length} blocked destination rule(s) will stop risky sharing.` : "",
|
|
28281
|
+
inputs.escalation_phrases.length > 0 ? `${inputs.escalation_phrases.length} escalation phrase(s) will require confirmation.` : "",
|
|
28282
|
+
inputs.approved_domains.length > 0 ? `${inputs.approved_domains.length} approved domain(s) will lower false positives.` : ""
|
|
28283
|
+
].filter(Boolean);
|
|
28284
|
+
return {
|
|
28285
|
+
total_inputs: totalInputs,
|
|
28286
|
+
flag_rules: inputs.sensitive_terms.length,
|
|
28287
|
+
block_rules: inputs.blocked_destinations.length,
|
|
28288
|
+
confirm_rules: inputs.escalation_phrases.length,
|
|
28289
|
+
examples
|
|
28290
|
+
};
|
|
28291
|
+
}
|
|
28292
|
+
function buildPrimePackDeployReceipt(receipt) {
|
|
28293
|
+
return {
|
|
28294
|
+
receipt_id: receipt.receipt_id,
|
|
28295
|
+
action: "deploy",
|
|
28296
|
+
created_at: receipt.created_at,
|
|
28297
|
+
receipt_hash: receipt.receipt_hash,
|
|
28298
|
+
raw_prompt_content_included: false,
|
|
28299
|
+
token_printed: false
|
|
28300
|
+
};
|
|
28301
|
+
}
|
|
28302
|
+
function buildPrimePackDeployGuardianReceipt(inputs, preview, transactionId, timestamp) {
|
|
28303
|
+
const core = {
|
|
28304
|
+
transaction_id: transactionId,
|
|
28305
|
+
action: "deploy",
|
|
28306
|
+
input_counts: buildPrimePackInputCounts(inputs),
|
|
28307
|
+
preview,
|
|
28308
|
+
input_hashes: buildPrimePackInputHashes(inputs),
|
|
28309
|
+
created_at: timestamp,
|
|
28310
|
+
raw_content_included: false
|
|
28311
|
+
};
|
|
28312
|
+
return {
|
|
28313
|
+
schema_version: PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION,
|
|
28314
|
+
receipt_id: `receipt:${transactionId}:prime-pack-deploy`,
|
|
28315
|
+
transaction_id: transactionId,
|
|
28316
|
+
receipt_type: "policy",
|
|
28317
|
+
disposition: "allow",
|
|
28318
|
+
policy_basis: [
|
|
28319
|
+
"prime-pack-deploy-metadata-only",
|
|
28320
|
+
`inputs:${preview.total_inputs}`,
|
|
28321
|
+
`flag_rules:${preview.flag_rules}`,
|
|
28322
|
+
`block_rules:${preview.block_rules}`,
|
|
28323
|
+
`confirm_rules:${preview.confirm_rules}`
|
|
28324
|
+
],
|
|
28325
|
+
receipt_hash: `sha256:${digest4(JSON.stringify(core))}`,
|
|
28326
|
+
raw_content_included: false,
|
|
28327
|
+
created_at: timestamp,
|
|
28328
|
+
summary: `Guardian deployed a local Prime Pack with ${preview.total_inputs} metadata rule input(s).`
|
|
28329
|
+
};
|
|
28330
|
+
}
|
|
28331
|
+
function buildPrimePackDeployEvent(inputs, preview, transactionId, timestamp, receipt) {
|
|
28332
|
+
const payload = {
|
|
28333
|
+
action: "deploy",
|
|
28334
|
+
input_counts: buildPrimePackInputCounts(inputs),
|
|
28335
|
+
input_hashes: buildPrimePackInputHashes(inputs),
|
|
28336
|
+
preview,
|
|
28337
|
+
raw_content_included: false
|
|
28338
|
+
};
|
|
28339
|
+
return {
|
|
28340
|
+
event_id: `event:${transactionId}:prime-pack-deploy`,
|
|
28341
|
+
transaction_id: transactionId,
|
|
28342
|
+
event_type: "policy_decided",
|
|
28343
|
+
occurred_at: timestamp,
|
|
28344
|
+
actor: "guardian-daemon",
|
|
28345
|
+
summary: "Prime Pack deployment recorded with metadata-only hashes.",
|
|
28346
|
+
payload_hash: `sha256:${digest4(JSON.stringify(payload))}`,
|
|
28347
|
+
receipt_id: receipt.receipt_id,
|
|
28348
|
+
graph_refs: ["policy-pack:prime-pack", `receipt:${receipt.receipt_id}`],
|
|
28349
|
+
redaction_state: "metadata_only"
|
|
28350
|
+
};
|
|
28351
|
+
}
|
|
28352
|
+
function buildPrimePackInputCounts(inputs) {
|
|
28353
|
+
return {
|
|
28354
|
+
sensitive_terms: inputs.sensitive_terms.length,
|
|
28355
|
+
approved_domains: inputs.approved_domains.length,
|
|
28356
|
+
blocked_destinations: inputs.blocked_destinations.length,
|
|
28357
|
+
escalation_phrases: inputs.escalation_phrases.length
|
|
28358
|
+
};
|
|
28359
|
+
}
|
|
28360
|
+
function buildPrimePackInputHashes(inputs) {
|
|
28361
|
+
return {
|
|
28362
|
+
sensitive_terms: inputs.sensitive_terms.map((entry) => `sha256:${digest4(entry)}`),
|
|
28363
|
+
approved_domains: inputs.approved_domains.map((entry) => `sha256:${digest4(entry)}`),
|
|
28364
|
+
blocked_destinations: inputs.blocked_destinations.map((entry) => `sha256:${digest4(entry)}`),
|
|
28365
|
+
escalation_phrases: inputs.escalation_phrases.map((entry) => `sha256:${digest4(entry)}`)
|
|
28366
|
+
};
|
|
28367
|
+
}
|
|
26955
28368
|
function parsePromptCoachMode2(value) {
|
|
26956
28369
|
return PROMPT_COACH_MODES2.includes(value) ? value : void 0;
|
|
26957
28370
|
}
|
|
@@ -27031,7 +28444,7 @@ function writeHtml(response, statusCode, body, options = {}) {
|
|
|
27031
28444
|
response.writeHead(statusCode, {
|
|
27032
28445
|
"Content-Type": "text/html; charset=utf-8",
|
|
27033
28446
|
"Referrer-Policy": "no-referrer",
|
|
27034
|
-
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'",
|
|
28447
|
+
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; connect-src 'self'",
|
|
27035
28448
|
"X-Content-Type-Options": "nosniff"
|
|
27036
28449
|
});
|
|
27037
28450
|
response.end(body);
|
|
@@ -27068,9 +28481,158 @@ function parseGuardianUrl(url2) {
|
|
|
27068
28481
|
return void 0;
|
|
27069
28482
|
}
|
|
27070
28483
|
}
|
|
28484
|
+
function buildControlTowerViewModel(profile, receipts, searchGraph, learningEvents, localDataStatus, releaseDoctorStatus, adminNotices = [], marketplaceRequests = []) {
|
|
28485
|
+
const recentReceipts = receipts.slice().sort((left, right) => right.created_at.localeCompare(left.created_at)).slice(0, CONTROL_TOWER_RECEIPT_LIMIT);
|
|
28486
|
+
const blocked = recentReceipts.filter((receipt) => receipt.disposition === "block").length;
|
|
28487
|
+
const flagged = recentReceipts.filter(
|
|
28488
|
+
(receipt) => receipt.disposition === "warn" || receipt.disposition === "confirm"
|
|
28489
|
+
).length;
|
|
28490
|
+
const allowed = recentReceipts.filter(
|
|
28491
|
+
(receipt) => receipt.disposition === "allow" || receipt.disposition === "rewrite"
|
|
28492
|
+
).length;
|
|
28493
|
+
const privacyDefaults = profile.privacy.privacy_mode_enabled && !profile.privacy.raw_content_capture_enabled && !profile.privacy.cloud_sync_enabled && !profile.privacy.local_app_autopilot_enabled ? "on" : "needs review";
|
|
28494
|
+
const protectedState = releaseDoctorStatus.doctor.checkCounts.fail === 0 && privacyDefaults === "on";
|
|
28495
|
+
return {
|
|
28496
|
+
ok: true,
|
|
28497
|
+
schema_version: "contextecf/project-guardian-control-tower-view-model/v1",
|
|
28498
|
+
surfaces: [
|
|
28499
|
+
{
|
|
28500
|
+
id: "personal",
|
|
28501
|
+
label: "Personal Guardian",
|
|
28502
|
+
audience: "Individual users",
|
|
28503
|
+
purpose: "Protection status, prompt coaching, privacy, receipts, and browser connection."
|
|
28504
|
+
},
|
|
28505
|
+
{
|
|
28506
|
+
id: "admin",
|
|
28507
|
+
label: "Guardian Admin",
|
|
28508
|
+
audience: "Organizations",
|
|
28509
|
+
purpose: "Groups, policy packs, marketplace approvals, Prime Pack, scope, and audit."
|
|
28510
|
+
},
|
|
28511
|
+
{
|
|
28512
|
+
id: "power_user",
|
|
28513
|
+
label: "Power User",
|
|
28514
|
+
audience: "Developers and technical operators",
|
|
28515
|
+
purpose: "MCP, local data, doctor checks, receipts, and release evidence."
|
|
28516
|
+
},
|
|
28517
|
+
{
|
|
28518
|
+
id: "marketplace_contributor",
|
|
28519
|
+
label: "Marketplace Contributor",
|
|
28520
|
+
audience: "Policy pack creators",
|
|
28521
|
+
purpose: "Submission metadata, examples, license terms, and review state."
|
|
28522
|
+
}
|
|
28523
|
+
],
|
|
28524
|
+
personal: {
|
|
28525
|
+
protectionLabel: protectedState ? "You're Protected" : "Needs Review",
|
|
28526
|
+
protectionDetail: protectedState ? "Guardian is running locally with metadata-only privacy defaults." : "Guardian is running, but one or more install checks need attention.",
|
|
28527
|
+
privacyDefaults,
|
|
28528
|
+
activePolicyPacks: [...profile.policy_packs.active],
|
|
28529
|
+
activityCounts: { blocked, flagged, allowed }
|
|
28530
|
+
},
|
|
28531
|
+
admin: {
|
|
28532
|
+
organizationLabel: "Example organization deployment",
|
|
28533
|
+
notices: adminNotices.slice(0, CONTROL_TOWER_ADMIN_NOTICE_LIMIT),
|
|
28534
|
+
marketplaceRequests: marketplaceRequests.slice(0, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT),
|
|
28535
|
+
managedGroups: buildControlTowerDeploymentGroups(profile),
|
|
28536
|
+
primePack: {
|
|
28537
|
+
status: "draft_ready",
|
|
28538
|
+
supportedInputs: ["typed terms", "text upload", "CSV upload", "JSON upload"],
|
|
28539
|
+
deployable: true,
|
|
28540
|
+
receiptRequired: true
|
|
28541
|
+
}
|
|
28542
|
+
},
|
|
28543
|
+
marketplace: buildControlTowerMarketplaceRows(profile, marketplaceRequests),
|
|
28544
|
+
releaseAutomation: {
|
|
28545
|
+
repository: GUARDIAN_RELEASE_REPOSITORY,
|
|
28546
|
+
workflow: GUARDIAN_RELEASE_DISPATCH_WORKFLOW,
|
|
28547
|
+
npmPackage: GUARDIAN_NPM_PACKAGE_NAME,
|
|
28548
|
+
registry: GUARDIAN_NPM_REGISTRY,
|
|
28549
|
+
chromeExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
28550
|
+
dryRunCommand: CONTROL_TOWER_DRY_RUN_COMMAND_TEMPLATE,
|
|
28551
|
+
publishCommand: CONTROL_TOWER_RELEASE_COMMAND_TEMPLATE,
|
|
28552
|
+
humanIntervention: [
|
|
28553
|
+
"Chrome Web Store upload/review remains a store-console step until Google approval APIs are wired.",
|
|
28554
|
+
"Native installer signing still requires platform signing credentials.",
|
|
28555
|
+
`Latest doctor status: ${releaseDoctorStatus.doctor.overall}.`,
|
|
28556
|
+
`Search Graph has ${searchGraph.nodes.length} node(s); Learning Graph has ${learningEvents.length} event(s).`,
|
|
28557
|
+
`Local data encryption: ${localDataStatus.encryptionStatus}.`
|
|
28558
|
+
]
|
|
28559
|
+
},
|
|
28560
|
+
raw_content_included: false,
|
|
28561
|
+
token_printed: false
|
|
28562
|
+
};
|
|
28563
|
+
}
|
|
28564
|
+
function buildControlTowerDeploymentGroups(profile) {
|
|
28565
|
+
return [
|
|
28566
|
+
{
|
|
28567
|
+
id: "group:individual-default",
|
|
28568
|
+
label: "Personal default",
|
|
28569
|
+
users: 1,
|
|
28570
|
+
posture: profile.posture_profile.selected,
|
|
28571
|
+
packs: [...profile.policy_packs.active]
|
|
28572
|
+
},
|
|
28573
|
+
{
|
|
28574
|
+
id: "group:employees",
|
|
28575
|
+
label: "Employees",
|
|
28576
|
+
users: 24,
|
|
28577
|
+
posture: "balanced",
|
|
28578
|
+
packs: ["privacy-first", "executive"]
|
|
28579
|
+
},
|
|
28580
|
+
{
|
|
28581
|
+
id: "group:technical-users",
|
|
28582
|
+
label: "Technical users",
|
|
28583
|
+
users: 8,
|
|
28584
|
+
posture: "guided",
|
|
28585
|
+
packs: ["privacy-first", "developer"]
|
|
28586
|
+
},
|
|
28587
|
+
{
|
|
28588
|
+
id: "group:high-risk-functions",
|
|
28589
|
+
label: "High-risk functions",
|
|
28590
|
+
users: 5,
|
|
28591
|
+
posture: "high_assurance",
|
|
28592
|
+
packs: ["privacy-first", "finance"]
|
|
28593
|
+
}
|
|
28594
|
+
];
|
|
28595
|
+
}
|
|
28596
|
+
function buildControlTowerMarketplaceRows(profile, marketplaceRequests = []) {
|
|
28597
|
+
const activePacks = new Set(profile.policy_packs.active);
|
|
28598
|
+
const requestsByManifest = new Map(
|
|
28599
|
+
marketplaceRequests.map((request) => [request.marketplace_manifest_id, request])
|
|
28600
|
+
);
|
|
28601
|
+
return GUARDIAN_POLICY_PACK_MARKETPLACE_CATALOG.map((manifest) => {
|
|
28602
|
+
const request = requestsByManifest.get(manifest.marketplace_manifest_id);
|
|
28603
|
+
const connected = activePacks.has(manifest.pack_id);
|
|
28604
|
+
return {
|
|
28605
|
+
packId: manifest.pack_id,
|
|
28606
|
+
name: manifest.display_name,
|
|
28607
|
+
version: manifest.pack_version,
|
|
28608
|
+
audience: manifest.audiences.join(", "),
|
|
28609
|
+
category: manifest.policy_domains.join(", "),
|
|
28610
|
+
publisher: manifest.publisher_display_name,
|
|
28611
|
+
price: manifest.price_model,
|
|
28612
|
+
licenseRef: manifest.license_ref,
|
|
28613
|
+
supportRef: manifest.support_ref ?? "Not provided",
|
|
28614
|
+
state: connected ? "connected" : request?.status ?? manifest.listing_status,
|
|
28615
|
+
permissions: [
|
|
28616
|
+
manifest.provider_rules_summary,
|
|
28617
|
+
manifest.source_rules_summary,
|
|
28618
|
+
manifest.app_permission_summary
|
|
28619
|
+
],
|
|
28620
|
+
protectedDataTypes: manifest.protected_data_types,
|
|
28621
|
+
examples: [
|
|
28622
|
+
manifest.short_description,
|
|
28623
|
+
manifest.confirmation_rules_summary,
|
|
28624
|
+
manifest.data_handling_summary
|
|
28625
|
+
],
|
|
28626
|
+
risk: manifest.limitations.length > 0 ? manifest.limitations[0] : "Standard false-positive review recommended before wide deployment.",
|
|
28627
|
+
requestState: connected ? "connected" : request?.status ?? "not_requested",
|
|
28628
|
+
requestReceiptId: request?.receipt_id,
|
|
28629
|
+
requestedAt: request?.requested_at
|
|
28630
|
+
};
|
|
28631
|
+
});
|
|
28632
|
+
}
|
|
27071
28633
|
function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { nodes: [], edges: [] }, learningEvents = [], localDataStatus = createEmptyControlTowerLocalDataStatus(profile), releaseDoctorStatus = createEmptyControlTowerReleaseDoctorStatus(
|
|
27072
28634
|
profile
|
|
27073
|
-
)) {
|
|
28635
|
+
), adminNotices = [], marketplaceRequests = []) {
|
|
27074
28636
|
const policyPacks = profile.policy_packs.available.length;
|
|
27075
28637
|
const activePolicyPacks = profile.policy_packs.active.join(", ") || "none";
|
|
27076
28638
|
const recentReceipts = receipts.slice().sort((left, right) => right.created_at.localeCompare(left.created_at)).slice(0, CONTROL_TOWER_RECEIPT_LIMIT);
|
|
@@ -27199,7 +28761,7 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27199
28761
|
<div class="metric-detail">${escapeHtml(metric.detail)}</div>
|
|
27200
28762
|
</article>`
|
|
27201
28763
|
).join("\n");
|
|
27202
|
-
const activityFeedHtml = renderActivityFeed(recentReceipts);
|
|
28764
|
+
const activityFeedHtml = renderActivityFeed(recentReceipts, searchGraph);
|
|
27203
28765
|
const postureCardsHtml = GUARDIAN_POSTURE_PROFILE_CATALOG.map((postureProfile) => {
|
|
27204
28766
|
const selected = profile.posture_profile.selected === postureProfile.id;
|
|
27205
28767
|
const icon = postureProfile.id === "calm" ? "☾" : postureProfile.id === "balanced" ? "⚖" : postureProfile.id === "guided" ? "🎓" : postureProfile.id === "high_assurance" ? "🔒" : "⚙";
|
|
@@ -27248,9 +28810,28 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27248
28810
|
<button type="submit" class="provider-pill" aria-label="Revoke ${escapeHtml(providerDisplayName(row.id))}"><span></span>${escapeHtml(providerDisplayName(row.id))}</button>
|
|
27249
28811
|
</form>`
|
|
27250
28812
|
).join("\n");
|
|
28813
|
+
const viewModel = buildControlTowerViewModel(
|
|
28814
|
+
profile,
|
|
28815
|
+
recentReceipts,
|
|
28816
|
+
searchGraph,
|
|
28817
|
+
learningEvents,
|
|
28818
|
+
localDataStatus,
|
|
28819
|
+
releaseDoctorStatus,
|
|
28820
|
+
adminNotices,
|
|
28821
|
+
marketplaceRequests
|
|
28822
|
+
);
|
|
28823
|
+
const roleCardsHtml = renderControlTowerRoleCards(viewModel);
|
|
28824
|
+
const adminNoticeBarHtml = renderAdminNoticeBar(viewModel.admin.notices);
|
|
28825
|
+
const adminPanelsHtml = renderGuardianAdminSurface(
|
|
28826
|
+
viewModel,
|
|
28827
|
+
profile,
|
|
28828
|
+
recentReceipts,
|
|
28829
|
+
appPermissionRows
|
|
28830
|
+
);
|
|
28831
|
+
const localGuardianStatus = renderLocalGuardianStatus(profile.runtime.daemon_status);
|
|
27251
28832
|
const rows = [
|
|
27252
28833
|
["Profile", profile.profile_dir],
|
|
27253
|
-
["
|
|
28834
|
+
["Local Guardian", localGuardianStatus],
|
|
27254
28835
|
[
|
|
27255
28836
|
"Posture Profile",
|
|
27256
28837
|
`${profile.posture_profile.selected}; managed baseline ${profile.posture_profile.managed_baseline_id ?? "none"}`
|
|
@@ -27418,6 +28999,161 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27418
28999
|
background: rgba(15, 23, 42, 0.78);
|
|
27419
29000
|
font-size: 16px;
|
|
27420
29001
|
}
|
|
29002
|
+
.surface-tabs, .role-grid, .admin-grid, .marketplace-grid {
|
|
29003
|
+
display: grid;
|
|
29004
|
+
gap: 12px;
|
|
29005
|
+
}
|
|
29006
|
+
.surface-tabs {
|
|
29007
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
29008
|
+
margin: 28px 0 16px;
|
|
29009
|
+
}
|
|
29010
|
+
.surface-tab {
|
|
29011
|
+
display: grid;
|
|
29012
|
+
gap: 6px;
|
|
29013
|
+
width: 100%;
|
|
29014
|
+
min-height: 78px;
|
|
29015
|
+
padding: 15px 16px;
|
|
29016
|
+
border-color: rgba(148, 163, 184, 0.2);
|
|
29017
|
+
background: rgba(13, 24, 40, 0.84);
|
|
29018
|
+
color: #eff6ff;
|
|
29019
|
+
text-align: left;
|
|
29020
|
+
}
|
|
29021
|
+
.surface-tab strong, .role-card strong, .admin-card strong { font-size: 17px; }
|
|
29022
|
+
.surface-tab span, .role-card span, .admin-card span, .marketplace-card span {
|
|
29023
|
+
color: #98a7bc;
|
|
29024
|
+
font-size: 13px;
|
|
29025
|
+
line-height: 1.35;
|
|
29026
|
+
}
|
|
29027
|
+
.surface-tab.is-active, .surface-tab[aria-pressed="true"] {
|
|
29028
|
+
border-color: rgba(34, 197, 94, 0.5);
|
|
29029
|
+
background: rgba(6, 45, 37, 0.78);
|
|
29030
|
+
box-shadow: inset 0 0 0 1px rgba(34, 197, 94, 0.12);
|
|
29031
|
+
}
|
|
29032
|
+
.surface-panel[hidden] { display: none; }
|
|
29033
|
+
.role-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
29034
|
+
.role-card, .admin-card, .marketplace-card, .prime-pack-builder, .release-command-card {
|
|
29035
|
+
border: 1px solid rgba(148, 163, 184, 0.18);
|
|
29036
|
+
border-radius: 8px;
|
|
29037
|
+
background: rgba(11, 24, 40, 0.82);
|
|
29038
|
+
padding: 18px;
|
|
29039
|
+
}
|
|
29040
|
+
.admin-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
29041
|
+
.marketplace-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
29042
|
+
.marketplace-card h3, .admin-card h3, .prime-pack-builder h3, .release-command-card h3 {
|
|
29043
|
+
margin: 0 0 8px;
|
|
29044
|
+
color: #f6f8ff;
|
|
29045
|
+
font-size: 18px;
|
|
29046
|
+
}
|
|
29047
|
+
.marketplace-card p, .admin-card p, .prime-pack-builder p, .release-command-card p {
|
|
29048
|
+
margin-bottom: 12px;
|
|
29049
|
+
}
|
|
29050
|
+
.marketplace-detail-drawer {
|
|
29051
|
+
margin-top: 14px;
|
|
29052
|
+
border-top: 1px solid rgba(148, 163, 184, 0.16);
|
|
29053
|
+
padding-top: 12px;
|
|
29054
|
+
}
|
|
29055
|
+
.marketplace-detail-drawer summary {
|
|
29056
|
+
cursor: pointer;
|
|
29057
|
+
color: #dce7f7;
|
|
29058
|
+
font-size: 14px;
|
|
29059
|
+
font-weight: 800;
|
|
29060
|
+
}
|
|
29061
|
+
.marketplace-detail-drawer dl {
|
|
29062
|
+
display: grid;
|
|
29063
|
+
gap: 8px;
|
|
29064
|
+
margin: 12px 0 0;
|
|
29065
|
+
}
|
|
29066
|
+
.marketplace-detail-drawer dt {
|
|
29067
|
+
color: #f6f8ff;
|
|
29068
|
+
font-size: 13px;
|
|
29069
|
+
font-weight: 800;
|
|
29070
|
+
}
|
|
29071
|
+
.marketplace-detail-drawer dd {
|
|
29072
|
+
margin: 0;
|
|
29073
|
+
color: #98a7bc;
|
|
29074
|
+
font-size: 13px;
|
|
29075
|
+
line-height: 1.45;
|
|
29076
|
+
}
|
|
29077
|
+
.marketplace-request-form {
|
|
29078
|
+
display: grid;
|
|
29079
|
+
gap: 8px;
|
|
29080
|
+
margin-top: 14px;
|
|
29081
|
+
}
|
|
29082
|
+
.marketplace-request-form span,
|
|
29083
|
+
.marketplace-request-state {
|
|
29084
|
+
margin: 14px 0 0;
|
|
29085
|
+
color: #9fb0c6;
|
|
29086
|
+
font-size: 13px;
|
|
29087
|
+
line-height: 1.45;
|
|
29088
|
+
}
|
|
29089
|
+
.marketplace-request-list {
|
|
29090
|
+
display: grid;
|
|
29091
|
+
gap: 10px;
|
|
29092
|
+
margin-top: 16px;
|
|
29093
|
+
}
|
|
29094
|
+
.marketplace-request-card {
|
|
29095
|
+
display: grid;
|
|
29096
|
+
gap: 6px;
|
|
29097
|
+
padding: 12px 14px;
|
|
29098
|
+
border: 1px solid rgba(34, 197, 94, 0.28);
|
|
29099
|
+
border-radius: 8px;
|
|
29100
|
+
background: rgba(6, 45, 37, 0.52);
|
|
29101
|
+
}
|
|
29102
|
+
.marketplace-request-card span {
|
|
29103
|
+
color: #9fb0c6;
|
|
29104
|
+
font-size: 13px;
|
|
29105
|
+
}
|
|
29106
|
+
.metadata-list {
|
|
29107
|
+
display: grid;
|
|
29108
|
+
gap: 8px;
|
|
29109
|
+
margin: 14px 0 0;
|
|
29110
|
+
padding: 0;
|
|
29111
|
+
list-style: none;
|
|
29112
|
+
}
|
|
29113
|
+
.metadata-list li {
|
|
29114
|
+
display: flex;
|
|
29115
|
+
gap: 8px;
|
|
29116
|
+
justify-content: space-between;
|
|
29117
|
+
border-top: 1px solid rgba(148, 163, 184, 0.12);
|
|
29118
|
+
padding-top: 8px;
|
|
29119
|
+
color: #c8d2e3;
|
|
29120
|
+
font-size: 13px;
|
|
29121
|
+
}
|
|
29122
|
+
.metadata-list b { color: #eff6ff; }
|
|
29123
|
+
.builder-grid {
|
|
29124
|
+
display: grid;
|
|
29125
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
29126
|
+
gap: 12px;
|
|
29127
|
+
margin-top: 12px;
|
|
29128
|
+
}
|
|
29129
|
+
.button-row {
|
|
29130
|
+
display: flex;
|
|
29131
|
+
flex-wrap: wrap;
|
|
29132
|
+
gap: 10px;
|
|
29133
|
+
margin-top: 12px;
|
|
29134
|
+
}
|
|
29135
|
+
textarea {
|
|
29136
|
+
width: 100%;
|
|
29137
|
+
min-height: 104px;
|
|
29138
|
+
border: 1px solid rgba(148, 163, 184, 0.24);
|
|
29139
|
+
border-radius: 8px;
|
|
29140
|
+
background: rgba(8, 13, 24, 0.92);
|
|
29141
|
+
color: #f6f8ff;
|
|
29142
|
+
padding: 10px;
|
|
29143
|
+
font: inherit;
|
|
29144
|
+
resize: vertical;
|
|
29145
|
+
}
|
|
29146
|
+
.command-box {
|
|
29147
|
+
display: block;
|
|
29148
|
+
width: 100%;
|
|
29149
|
+
padding: 12px;
|
|
29150
|
+
border: 1px solid rgba(148, 163, 184, 0.2);
|
|
29151
|
+
border-radius: 8px;
|
|
29152
|
+
background: rgba(5, 10, 20, 0.88);
|
|
29153
|
+
color: #d8f8e7;
|
|
29154
|
+
overflow-x: auto;
|
|
29155
|
+
white-space: nowrap;
|
|
29156
|
+
}
|
|
27421
29157
|
.eyebrow {
|
|
27422
29158
|
width: fit-content;
|
|
27423
29159
|
margin-bottom: 16px;
|
|
@@ -27599,6 +29335,22 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27599
29335
|
}
|
|
27600
29336
|
.provider-pill:hover { background: rgba(30, 41, 59, 0.78); }
|
|
27601
29337
|
.provider-pill span { width: 8px; height: 8px; border-radius: 999px; background: #22c55e; }
|
|
29338
|
+
.notice-bar { display: grid; gap: 10px; margin: 20px 0 26px; }
|
|
29339
|
+
.notice-card {
|
|
29340
|
+
display: grid;
|
|
29341
|
+
gap: 6px;
|
|
29342
|
+
padding: 14px 16px;
|
|
29343
|
+
border: 1px solid rgba(96, 165, 250, 0.32);
|
|
29344
|
+
border-radius: 8px;
|
|
29345
|
+
color: #d8e1f0;
|
|
29346
|
+
background: rgba(15, 23, 42, 0.78);
|
|
29347
|
+
}
|
|
29348
|
+
.notice-card.warning { border-color: rgba(245, 158, 11, 0.46); background: rgba(39, 26, 9, 0.82); }
|
|
29349
|
+
.notice-card.success { border-color: rgba(34, 197, 94, 0.46); background: rgba(6, 45, 37, 0.82); }
|
|
29350
|
+
.notice-card strong { color: #f6f8ff; font-size: 16px; }
|
|
29351
|
+
.notice-card span { color: #91a1b8; font-size: 13px; font-weight: 700; }
|
|
29352
|
+
.notice-card p { margin: 0; color: #d8e1f0; line-height: 1.45; }
|
|
29353
|
+
.notice-form { display: grid; gap: 12px; }
|
|
27602
29354
|
.activity-heading { justify-content: space-between; gap: 16px; margin-bottom: 12px; }
|
|
27603
29355
|
.activity-heading h2 { margin: 0; }
|
|
27604
29356
|
.filter-chips { gap: 8px; flex-wrap: wrap; }
|
|
@@ -27619,6 +29371,14 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27619
29371
|
.chip:hover { color: #f5f7fb; background: rgba(30, 41, 59, 0.78); }
|
|
27620
29372
|
.chip.is-active, .chip[aria-pressed="true"] { border-color: rgba(34, 197, 94, 0.38); color: #d7ffe5; background: rgba(21, 128, 61, 0.22); }
|
|
27621
29373
|
.activity-card[hidden], .empty-filter-state[hidden] { display: none; }
|
|
29374
|
+
.receipt-row.is-selected-receipt {
|
|
29375
|
+
outline: 2px solid rgba(56, 230, 120, 0.7);
|
|
29376
|
+
outline-offset: 4px;
|
|
29377
|
+
background: rgba(34, 197, 94, 0.08);
|
|
29378
|
+
}
|
|
29379
|
+
.receipt-row.is-selected-receipt details {
|
|
29380
|
+
border-radius: 8px;
|
|
29381
|
+
}
|
|
27622
29382
|
.empty-filter-state {
|
|
27623
29383
|
padding: 22px;
|
|
27624
29384
|
border: 1px dashed rgba(148, 163, 184, 0.24);
|
|
@@ -27676,6 +29436,7 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27676
29436
|
.protection-card { min-height: 320px; }
|
|
27677
29437
|
.activity-card { grid-template-columns: 48px 1fr; }
|
|
27678
29438
|
.activity-badge { grid-column: 2; width: fit-content; }
|
|
29439
|
+
.surface-tabs, .role-grid, .admin-grid, .marketplace-grid, .builder-grid { grid-template-columns: 1fr; }
|
|
27679
29440
|
section { padding: 16px; overflow-x: auto; }
|
|
27680
29441
|
table { min-width: 720px; }
|
|
27681
29442
|
.activity-heading { align-items: flex-start; flex-direction: column; }
|
|
@@ -27688,7 +29449,7 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27688
29449
|
<header class="topbar">
|
|
27689
29450
|
<div class="brand"><span class="brand-mark">G</span><span>Guardian</span></div>
|
|
27690
29451
|
<div class="topbar-actions">
|
|
27691
|
-
<span class="status-pill"><span class="status-dot"></span>${escapeHtml(
|
|
29452
|
+
<span class="status-pill"><span class="status-dot"></span>${escapeHtml(localGuardianStatus)}</span>
|
|
27692
29453
|
<span class="icon-button" aria-label="Settings">⚙</span>
|
|
27693
29454
|
</div>
|
|
27694
29455
|
</header>
|
|
@@ -27696,6 +29457,25 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27696
29457
|
<div class="eyebrow">Local-first status</div>
|
|
27697
29458
|
<h1>Project Guardian Control Tower</h1>
|
|
27698
29459
|
<p class="intro">This local page confirms Guardian is watching your AI surface with privacy-first, metadata-only governance. It never exposes the runtime token, raw prompts, raw responses, or local action payloads.</p>
|
|
29460
|
+
${adminNoticeBarHtml}
|
|
29461
|
+
<div class="surface-tabs" aria-label="Control Tower surfaces">
|
|
29462
|
+
<button type="button" class="surface-tab is-active" data-surface-tab="personal" aria-pressed="true">
|
|
29463
|
+
<strong>Personal Guardian</strong>
|
|
29464
|
+
<span>Simple protection, privacy, coaching, browser connection, and receipts.</span>
|
|
29465
|
+
</button>
|
|
29466
|
+
<button type="button" class="surface-tab" data-surface-tab="admin" aria-pressed="false">
|
|
29467
|
+
<strong>Guardian Admin</strong>
|
|
29468
|
+
<span>Policy packs, marketplace approvals, Prime Pack, deployment, audit, and releases.</span>
|
|
29469
|
+
</button>
|
|
29470
|
+
</div>
|
|
29471
|
+
<section aria-labelledby="role-experiences">
|
|
29472
|
+
<h2 id="role-experiences">Role Experiences</h2>
|
|
29473
|
+
<p>Guardian ships one local brain with different surfaces for individuals, power users, enterprise admins, and marketplace contributors.</p>
|
|
29474
|
+
<div class="role-grid">
|
|
29475
|
+
${roleCardsHtml}
|
|
29476
|
+
</div>
|
|
29477
|
+
</section>
|
|
29478
|
+
<div class="surface-panel" data-surface-panel="personal">
|
|
27699
29479
|
<div class="hero-grid" aria-label="Guardian protection overview">
|
|
27700
29480
|
<article class="protection-card">
|
|
27701
29481
|
<div class="${protectionRingClass}"><div class="ring-core">G</div></div>
|
|
@@ -27717,6 +29497,7 @@ ${heroMetricCards}
|
|
|
27717
29497
|
</div>
|
|
27718
29498
|
</div>
|
|
27719
29499
|
<p class="activity-filter-status" data-activity-filter-status aria-live="polite">Showing all local Guardian activity.</p>
|
|
29500
|
+
<p class="activity-filter-status" data-guardian-time-zone-label>Times use your browser's local timezone.</p>
|
|
27720
29501
|
<div class="activity-list" data-activity-list>
|
|
27721
29502
|
${activityFeedHtml}
|
|
27722
29503
|
<div class="empty-filter-state" data-activity-empty hidden>No activity matches this filter yet.</div>
|
|
@@ -27885,7 +29666,7 @@ ${releaseDoctorRows}
|
|
|
27885
29666
|
</tbody>
|
|
27886
29667
|
</table>
|
|
27887
29668
|
</section>
|
|
27888
|
-
<section aria-labelledby="receipt-drilldowns">
|
|
29669
|
+
<section id="receipts" aria-labelledby="receipt-drilldowns">
|
|
27889
29670
|
<div class="activity-heading">
|
|
27890
29671
|
<h2 id="receipt-drilldowns">What Guardian did today</h2>
|
|
27891
29672
|
<div class="filter-chips" aria-label="Activity filters">
|
|
@@ -27895,7 +29676,7 @@ ${releaseDoctorRows}
|
|
|
27895
29676
|
<span class="chip">Allowed</span>
|
|
27896
29677
|
</div>
|
|
27897
29678
|
</div>
|
|
27898
|
-
<p><span class="visually-hidden">Activity & Receipts.</span>Inspect recent Guardian decisions without raw prompt, response, or local-action payload content.</p>
|
|
29679
|
+
<p><span class="visually-hidden">Activity & Receipts.</span>Inspect recent Guardian decisions without raw prompt, response, or local-action payload content. Links from Halo open the matching receipt here.</p>
|
|
27899
29680
|
<table aria-label="Guardian receipt drilldowns">
|
|
27900
29681
|
<thead>
|
|
27901
29682
|
<tr><th scope="col">Receipt</th><th scope="col">Type</th><th scope="col">Decision</th><th scope="col">Details</th></tr>
|
|
@@ -27975,9 +29756,47 @@ ${learningEventRows}
|
|
|
27975
29756
|
</section>
|
|
27976
29757
|
</div>
|
|
27977
29758
|
</details>
|
|
29759
|
+
</div>
|
|
29760
|
+
<div class="surface-panel" data-surface-panel="admin" hidden>
|
|
29761
|
+
${adminPanelsHtml}
|
|
29762
|
+
</div>
|
|
27978
29763
|
</main>
|
|
27979
29764
|
<script>
|
|
27980
29765
|
(() => {
|
|
29766
|
+
const surfaceTabs = Array.from(document.querySelectorAll('[data-surface-tab]'));
|
|
29767
|
+
const surfacePanels = Array.from(document.querySelectorAll('[data-surface-panel]'));
|
|
29768
|
+
const selectSurface = (surface) => {
|
|
29769
|
+
for (const tab of surfaceTabs) {
|
|
29770
|
+
const selected = tab.getAttribute('data-surface-tab') === surface;
|
|
29771
|
+
tab.classList.toggle('is-active', selected);
|
|
29772
|
+
tab.setAttribute('aria-pressed', selected ? 'true' : 'false');
|
|
29773
|
+
}
|
|
29774
|
+
for (const panel of surfacePanels) {
|
|
29775
|
+
panel.hidden = panel.getAttribute('data-surface-panel') !== surface;
|
|
29776
|
+
}
|
|
29777
|
+
};
|
|
29778
|
+
for (const tab of surfaceTabs) {
|
|
29779
|
+
tab.addEventListener('click', () => selectSurface(tab.getAttribute('data-surface-tab') || 'personal'));
|
|
29780
|
+
}
|
|
29781
|
+
const hydrateViewModel = async () => {
|
|
29782
|
+
const controller = new AbortController();
|
|
29783
|
+
const timeoutId = window.setTimeout(() => controller.abort(), 2500);
|
|
29784
|
+
try {
|
|
29785
|
+
const response = await fetch('/v1/guardian/control-tower/view-model', {
|
|
29786
|
+
headers: { accept: 'application/json' },
|
|
29787
|
+
signal: controller.signal,
|
|
29788
|
+
});
|
|
29789
|
+
if (!response.ok) return;
|
|
29790
|
+
const body = await response.json();
|
|
29791
|
+
if (!body || body.ok !== true) return;
|
|
29792
|
+
document.documentElement.setAttribute('data-view-model', body.schema_version || 'loaded');
|
|
29793
|
+
} catch {
|
|
29794
|
+
// Keep the server-rendered Control Tower if local hydration is unavailable.
|
|
29795
|
+
} finally {
|
|
29796
|
+
window.clearTimeout(timeoutId);
|
|
29797
|
+
}
|
|
29798
|
+
};
|
|
29799
|
+
hydrateViewModel();
|
|
27981
29800
|
const filters = Array.from(document.querySelectorAll('[data-activity-filter]'));
|
|
27982
29801
|
const list = document.querySelector('[data-activity-list]');
|
|
27983
29802
|
const emptyState = document.querySelector('[data-activity-empty]');
|
|
@@ -28013,6 +29832,85 @@ ${learningEventRows}
|
|
|
28013
29832
|
}
|
|
28014
29833
|
setFilterCopy(selectedFilter);
|
|
28015
29834
|
};
|
|
29835
|
+
const absoluteLocalTimeFormatter = new Intl.DateTimeFormat(navigator.language || undefined, {
|
|
29836
|
+
month: 'short',
|
|
29837
|
+
day: 'numeric',
|
|
29838
|
+
year: 'numeric',
|
|
29839
|
+
hour: 'numeric',
|
|
29840
|
+
minute: '2-digit',
|
|
29841
|
+
timeZoneName: 'short',
|
|
29842
|
+
});
|
|
29843
|
+
const relativeTimeFormatter = new Intl.RelativeTimeFormat(navigator.language || undefined, {
|
|
29844
|
+
numeric: 'auto',
|
|
29845
|
+
});
|
|
29846
|
+
const formatAbsoluteLocalTime = (createdAt) => {
|
|
29847
|
+
const date = new Date(createdAt);
|
|
29848
|
+
if (Number.isNaN(date.getTime())) return createdAt || '';
|
|
29849
|
+
return absoluteLocalTimeFormatter.format(date);
|
|
29850
|
+
};
|
|
29851
|
+
const formatLocalTime = (createdAt) => {
|
|
29852
|
+
const date = new Date(createdAt);
|
|
29853
|
+
if (Number.isNaN(date.getTime())) return createdAt || '';
|
|
29854
|
+
const diffMs = date.getTime() - Date.now();
|
|
29855
|
+
const absMs = Math.abs(diffMs);
|
|
29856
|
+
if (absMs < 60 * 1000) return 'just now';
|
|
29857
|
+
if (absMs < 60 * 60 * 1000) {
|
|
29858
|
+
return relativeTimeFormatter.format(Math.round(diffMs / (60 * 1000)), 'minute');
|
|
29859
|
+
}
|
|
29860
|
+
if (absMs < 24 * 60 * 60 * 1000) {
|
|
29861
|
+
return relativeTimeFormatter.format(Math.round(diffMs / (60 * 60 * 1000)), 'hour');
|
|
29862
|
+
}
|
|
29863
|
+
if (absMs < 7 * 24 * 60 * 60 * 1000) {
|
|
29864
|
+
return relativeTimeFormatter.format(Math.round(diffMs / (24 * 60 * 60 * 1000)), 'day');
|
|
29865
|
+
}
|
|
29866
|
+
return formatAbsoluteLocalTime(createdAt);
|
|
29867
|
+
};
|
|
29868
|
+
const hydrateLocalTimes = () => {
|
|
29869
|
+
for (const element of Array.from(document.querySelectorAll('[data-guardian-local-time]'))) {
|
|
29870
|
+
const createdAt = element.getAttribute('datetime') || element.getAttribute('data-created-at') || '';
|
|
29871
|
+
const formatted = formatLocalTime(createdAt);
|
|
29872
|
+
if (formatted) element.textContent = formatted;
|
|
29873
|
+
if (createdAt) {
|
|
29874
|
+
element.setAttribute('title', 'Your local time: ' + formatAbsoluteLocalTime(createdAt) + ' \xB7 Stored by Guardian as ' + createdAt);
|
|
29875
|
+
}
|
|
29876
|
+
}
|
|
29877
|
+
};
|
|
29878
|
+
const hydrateLocalTimeZoneLabels = () => {
|
|
29879
|
+
let timeZone = '';
|
|
29880
|
+
try {
|
|
29881
|
+
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || '';
|
|
29882
|
+
} catch {
|
|
29883
|
+
timeZone = '';
|
|
29884
|
+
}
|
|
29885
|
+
const label = timeZone
|
|
29886
|
+
? 'Times use your browser timezone: ' + timeZone + '.'
|
|
29887
|
+
: "Times use your browser's local timezone.";
|
|
29888
|
+
for (const element of Array.from(document.querySelectorAll('[data-guardian-time-zone-label]'))) {
|
|
29889
|
+
element.textContent = label;
|
|
29890
|
+
}
|
|
29891
|
+
};
|
|
29892
|
+
const receiptIdFromHash = () => {
|
|
29893
|
+
const hash = window.location.hash || '';
|
|
29894
|
+
if (!hash.startsWith('#receipt=')) return '';
|
|
29895
|
+
try {
|
|
29896
|
+
return decodeURIComponent(hash.slice('#receipt='.length));
|
|
29897
|
+
} catch {
|
|
29898
|
+
return hash.slice('#receipt='.length);
|
|
29899
|
+
}
|
|
29900
|
+
};
|
|
29901
|
+
const highlightReceiptFromHash = () => {
|
|
29902
|
+
const selectedReceiptId = receiptIdFromHash();
|
|
29903
|
+
let matchedRow = null;
|
|
29904
|
+
for (const row of Array.from(document.querySelectorAll('[data-receipt-row]'))) {
|
|
29905
|
+
const selected = Boolean(selectedReceiptId) && row.getAttribute('data-receipt-id') === selectedReceiptId;
|
|
29906
|
+
row.classList.toggle('is-selected-receipt', selected);
|
|
29907
|
+
if (selected) matchedRow = row;
|
|
29908
|
+
}
|
|
29909
|
+
if (!matchedRow) return;
|
|
29910
|
+
const details = matchedRow.querySelector('details');
|
|
29911
|
+
if (details) details.open = true;
|
|
29912
|
+
matchedRow.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
29913
|
+
};
|
|
28016
29914
|
const createActivityCard = (activity) => {
|
|
28017
29915
|
const card = document.createElement('article');
|
|
28018
29916
|
card.className = 'activity-card ' + activity.tone;
|
|
@@ -28026,7 +29924,16 @@ ${learningEventRows}
|
|
|
28026
29924
|
title.textContent = activity.title;
|
|
28027
29925
|
const meta = document.createElement('div');
|
|
28028
29926
|
meta.className = 'activity-meta';
|
|
28029
|
-
|
|
29927
|
+
if (activity.created_at) {
|
|
29928
|
+
const time = document.createElement('time');
|
|
29929
|
+
time.setAttribute('datetime', activity.created_at);
|
|
29930
|
+
time.setAttribute('data-guardian-local-time', '');
|
|
29931
|
+
time.textContent = formatLocalTime(activity.created_at);
|
|
29932
|
+
time.title = 'Your local time: ' + formatAbsoluteLocalTime(activity.created_at) + ' \xB7 Stored by Guardian as ' + activity.created_at;
|
|
29933
|
+
meta.append(time, ' \xB7 ' + (activity.meta_detail || activity.receipt_type || 'receipt'));
|
|
29934
|
+
} else {
|
|
29935
|
+
meta.textContent = activity.meta;
|
|
29936
|
+
}
|
|
28030
29937
|
body.append(title, meta);
|
|
28031
29938
|
const badge = document.createElement('div');
|
|
28032
29939
|
badge.className = 'activity-badge ' + activity.tone;
|
|
@@ -28069,6 +29976,11 @@ ${learningEventRows}
|
|
|
28069
29976
|
applyFilter(filter.getAttribute('data-activity-filter') || 'all');
|
|
28070
29977
|
});
|
|
28071
29978
|
}
|
|
29979
|
+
window.addEventListener('hashchange', highlightReceiptFromHash);
|
|
29980
|
+
hydrateLocalTimeZoneLabels();
|
|
29981
|
+
hydrateLocalTimes();
|
|
29982
|
+
window.setInterval(hydrateLocalTimes, 60 * 1000);
|
|
29983
|
+
highlightReceiptFromHash();
|
|
28072
29984
|
})();
|
|
28073
29985
|
</script>
|
|
28074
29986
|
</body>
|
|
@@ -28205,6 +30117,120 @@ function renderReleaseDoctorRows(status) {
|
|
|
28205
30117
|
function renderDoctorCounts(doctor) {
|
|
28206
30118
|
return `${doctor.checkCounts.pass} pass; ${doctor.checkCounts.warn} warn; ${doctor.checkCounts.fail} fail`;
|
|
28207
30119
|
}
|
|
30120
|
+
async function readControlTowerAdminNotices(profileDir) {
|
|
30121
|
+
try {
|
|
30122
|
+
const raw = await readFile4(path4.join(profileDir, CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME), "utf8");
|
|
30123
|
+
const parsed = JSON.parse(raw);
|
|
30124
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.notices)) {
|
|
30125
|
+
return [];
|
|
30126
|
+
}
|
|
30127
|
+
return parsed.notices.map(parseControlTowerAdminNotice).filter((notice) => Boolean(notice)).slice(0, CONTROL_TOWER_ADMIN_NOTICE_LIMIT);
|
|
30128
|
+
} catch {
|
|
30129
|
+
return [];
|
|
30130
|
+
}
|
|
30131
|
+
}
|
|
30132
|
+
async function writeControlTowerAdminNotices(profileDir, notices) {
|
|
30133
|
+
await writeFile2(
|
|
30134
|
+
path4.join(profileDir, CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME),
|
|
30135
|
+
`${JSON.stringify(
|
|
30136
|
+
{
|
|
30137
|
+
schema_version: "contextecf/project-guardian-control-tower-admin-notices/v1",
|
|
30138
|
+
notices: notices.slice(0, CONTROL_TOWER_ADMIN_NOTICE_LIMIT),
|
|
30139
|
+
raw_content_included: false,
|
|
30140
|
+
token_printed: false
|
|
30141
|
+
},
|
|
30142
|
+
null,
|
|
30143
|
+
2
|
|
30144
|
+
)}
|
|
30145
|
+
`,
|
|
30146
|
+
"utf8"
|
|
30147
|
+
);
|
|
30148
|
+
}
|
|
30149
|
+
async function readControlTowerMarketplaceRequests(profileDir) {
|
|
30150
|
+
try {
|
|
30151
|
+
const raw = await readFile4(
|
|
30152
|
+
path4.join(profileDir, CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME),
|
|
30153
|
+
"utf8"
|
|
30154
|
+
);
|
|
30155
|
+
const parsed = JSON.parse(raw);
|
|
30156
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.requests)) {
|
|
30157
|
+
return [];
|
|
30158
|
+
}
|
|
30159
|
+
return parsed.requests.map(parseControlTowerMarketplaceRequest).filter((request) => Boolean(request)).slice(0, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT);
|
|
30160
|
+
} catch {
|
|
30161
|
+
return [];
|
|
30162
|
+
}
|
|
30163
|
+
}
|
|
30164
|
+
async function writeControlTowerMarketplaceRequests(profileDir, requests) {
|
|
30165
|
+
await writeFile2(
|
|
30166
|
+
path4.join(profileDir, CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME),
|
|
30167
|
+
`${JSON.stringify(
|
|
30168
|
+
{
|
|
30169
|
+
schema_version: "contextecf/project-guardian-marketplace-requests/v1",
|
|
30170
|
+
requests: requests.slice(0, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT),
|
|
30171
|
+
raw_content_included: false,
|
|
30172
|
+
token_printed: false
|
|
30173
|
+
},
|
|
30174
|
+
null,
|
|
30175
|
+
2
|
|
30176
|
+
)}
|
|
30177
|
+
`,
|
|
30178
|
+
"utf8"
|
|
30179
|
+
);
|
|
30180
|
+
}
|
|
30181
|
+
async function writeControlTowerPrimePackDraft(profileDir, draft) {
|
|
30182
|
+
await writeFile2(
|
|
30183
|
+
path4.join(profileDir, CONTROL_TOWER_PRIME_PACK_FILE_NAME),
|
|
30184
|
+
`${JSON.stringify(draft, null, 2)}
|
|
30185
|
+
`,
|
|
30186
|
+
"utf8"
|
|
30187
|
+
);
|
|
30188
|
+
}
|
|
30189
|
+
function parseControlTowerAdminNotice(value) {
|
|
30190
|
+
if (!isRecord(value)) {
|
|
30191
|
+
return null;
|
|
30192
|
+
}
|
|
30193
|
+
const audience = parseControlTowerAdminNoticeAudience(value.audience);
|
|
30194
|
+
const tone = parseControlTowerAdminNoticeTone(value.tone);
|
|
30195
|
+
if (typeof value.id !== "string" || typeof value.title !== "string" || typeof value.message !== "string" || typeof value.created_at !== "string" || !audience || !tone) {
|
|
30196
|
+
return null;
|
|
30197
|
+
}
|
|
30198
|
+
return {
|
|
30199
|
+
id: value.id.slice(0, 80),
|
|
30200
|
+
title: value.title.slice(0, CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT),
|
|
30201
|
+
message: value.message.slice(0, CONTROL_TOWER_ADMIN_NOTICE_MESSAGE_LIMIT),
|
|
30202
|
+
audience,
|
|
30203
|
+
tone,
|
|
30204
|
+
created_at: value.created_at,
|
|
30205
|
+
receipt_id: typeof value.receipt_id === "string" ? value.receipt_id.slice(0, 160) : void 0,
|
|
30206
|
+
receipt_hash: typeof value.receipt_hash === "string" ? value.receipt_hash.slice(0, 80) : void 0,
|
|
30207
|
+
raw_content_included: false
|
|
30208
|
+
};
|
|
30209
|
+
}
|
|
30210
|
+
function parseControlTowerMarketplaceRequest(value) {
|
|
30211
|
+
if (!isRecord(value)) {
|
|
30212
|
+
return null;
|
|
30213
|
+
}
|
|
30214
|
+
if (value.schema_version !== "contextecf/project-guardian-marketplace-request/v1" || value.action !== "request_review" || value.status !== "requested_metadata_only" || typeof value.request_id !== "string" || typeof value.pack_id !== "string" || typeof value.marketplace_manifest_id !== "string" || typeof value.pack_version !== "string" || typeof value.pack_name !== "string" || typeof value.publisher !== "string" || typeof value.requested_at !== "string" || typeof value.receipt_id !== "string" || typeof value.receipt_hash !== "string") {
|
|
30215
|
+
return null;
|
|
30216
|
+
}
|
|
30217
|
+
return {
|
|
30218
|
+
schema_version: "contextecf/project-guardian-marketplace-request/v1",
|
|
30219
|
+
request_id: value.request_id.slice(0, 120),
|
|
30220
|
+
pack_id: value.pack_id.slice(0, 120),
|
|
30221
|
+
marketplace_manifest_id: value.marketplace_manifest_id.slice(0, 160),
|
|
30222
|
+
pack_version: value.pack_version.slice(0, 40),
|
|
30223
|
+
pack_name: value.pack_name.slice(0, 120),
|
|
30224
|
+
publisher: value.publisher.slice(0, 120),
|
|
30225
|
+
action: "request_review",
|
|
30226
|
+
status: "requested_metadata_only",
|
|
30227
|
+
requested_at: value.requested_at,
|
|
30228
|
+
receipt_id: value.receipt_id.slice(0, 160),
|
|
30229
|
+
receipt_hash: value.receipt_hash.slice(0, 80),
|
|
30230
|
+
raw_content_included: false,
|
|
30231
|
+
token_printed: false
|
|
30232
|
+
};
|
|
30233
|
+
}
|
|
28208
30234
|
async function buildControlTowerLocalDataStatus(profile, env = process.env) {
|
|
28209
30235
|
const profileDir = profile.profile_dir;
|
|
28210
30236
|
const encryption = buildControlTowerEncryptionStatus(
|
|
@@ -28440,7 +30466,7 @@ function renderReceiptRows(receipts) {
|
|
|
28440
30466
|
}
|
|
28441
30467
|
return receipts.map((receipt) => renderReceiptRow(receipt)).join("\n");
|
|
28442
30468
|
}
|
|
28443
|
-
function renderActivityFeed(receipts) {
|
|
30469
|
+
function renderActivityFeed(receipts, searchGraph = { nodes: [], edges: [] }) {
|
|
28444
30470
|
if (receipts.length === 0) {
|
|
28445
30471
|
return ` <article class="activity-card allow" data-activity-state="allowed">
|
|
28446
30472
|
<div class="activity-icon">✅</div>
|
|
@@ -28451,7 +30477,8 @@ function renderActivityFeed(receipts) {
|
|
|
28451
30477
|
<div class="activity-badge allow">READY</div>
|
|
28452
30478
|
</article>`;
|
|
28453
30479
|
}
|
|
28454
|
-
|
|
30480
|
+
const attribution = buildActivityAttributionIndex(searchGraph);
|
|
30481
|
+
return receipts.map((receipt) => renderActivityCard(receipt, attribution)).join("\n");
|
|
28455
30482
|
}
|
|
28456
30483
|
function parseActivityFilter(value) {
|
|
28457
30484
|
if (value === "blocked" || value === "flagged" || value === "allowed") {
|
|
@@ -28459,36 +30486,149 @@ function parseActivityFilter(value) {
|
|
|
28459
30486
|
}
|
|
28460
30487
|
return "all";
|
|
28461
30488
|
}
|
|
28462
|
-
function buildActivityItems(receipts, filter) {
|
|
30489
|
+
function buildActivityItems(receipts, filter, searchGraph = { nodes: [], edges: [] }) {
|
|
30490
|
+
const attribution = buildActivityAttributionIndex(searchGraph);
|
|
28463
30491
|
return receipts.filter((receipt) => {
|
|
28464
30492
|
const group = activityFilterGroup(receipt.disposition);
|
|
28465
30493
|
return filter === "all" || group === filter;
|
|
28466
|
-
}).map((receipt) =>
|
|
28467
|
-
|
|
28468
|
-
|
|
28469
|
-
|
|
28470
|
-
|
|
28471
|
-
|
|
28472
|
-
|
|
28473
|
-
|
|
28474
|
-
|
|
28475
|
-
|
|
30494
|
+
}).map((receipt) => {
|
|
30495
|
+
const detail = activityDetailForReceipt(receipt, attribution);
|
|
30496
|
+
return {
|
|
30497
|
+
id: receipt.receipt_id,
|
|
30498
|
+
state: activityFilterGroup(receipt.disposition),
|
|
30499
|
+
tone: activityTone(receipt.disposition),
|
|
30500
|
+
icon: activityPlainIcon(receipt.disposition),
|
|
30501
|
+
title: receipt.summary,
|
|
30502
|
+
created_at: receipt.created_at,
|
|
30503
|
+
receipt_type: receipt.receipt_type,
|
|
30504
|
+
source_label: detail.surfaceLabel,
|
|
30505
|
+
provider_label: detail.providerLabel,
|
|
30506
|
+
meta_detail: detail.metaDetail,
|
|
30507
|
+
meta: `${formatActivityTime(receipt.created_at)} \xB7 ${detail.metaDetail}`,
|
|
30508
|
+
label: activityLabel(receipt.disposition),
|
|
30509
|
+
raw_content_included: false
|
|
30510
|
+
};
|
|
30511
|
+
});
|
|
28476
30512
|
}
|
|
28477
|
-
function renderActivityCard(receipt) {
|
|
30513
|
+
function renderActivityCard(receipt, attribution = /* @__PURE__ */ new Map()) {
|
|
28478
30514
|
const tone = activityTone(receipt.disposition);
|
|
28479
30515
|
const filterGroup = activityFilterGroup(receipt.disposition);
|
|
30516
|
+
const detail = activityDetailForReceipt(receipt, attribution);
|
|
28480
30517
|
return ` <article class="activity-card ${tone}" data-activity-state="${escapeHtml(filterGroup)}">
|
|
28481
30518
|
<div class="activity-icon">${activityIcon(receipt.disposition)}</div>
|
|
28482
30519
|
<div>
|
|
28483
30520
|
<div class="activity-title">${escapeHtml(receipt.summary)}</div>
|
|
28484
|
-
<div class="activity-meta">${
|
|
30521
|
+
<div class="activity-meta">${renderLocalTime(receipt.created_at)} \xB7 ${escapeHtml(detail.metaDetail)}</div>
|
|
28485
30522
|
</div>
|
|
28486
30523
|
<div class="activity-badge ${tone}">${escapeHtml(activityLabel(receipt.disposition))}</div>
|
|
28487
30524
|
</article>`;
|
|
28488
30525
|
}
|
|
30526
|
+
function buildActivityAttributionIndex(searchGraph) {
|
|
30527
|
+
const index = /* @__PURE__ */ new Map();
|
|
30528
|
+
for (const node of searchGraph.nodes) {
|
|
30529
|
+
if (node.node_type !== "prompt" || !node.summary) {
|
|
30530
|
+
continue;
|
|
30531
|
+
}
|
|
30532
|
+
const attribution = parsePromptNodeAttribution(node.summary);
|
|
30533
|
+
if (!attribution) {
|
|
30534
|
+
continue;
|
|
30535
|
+
}
|
|
30536
|
+
for (const evidenceRef of node.evidence_refs) {
|
|
30537
|
+
if (evidenceRef.startsWith("receipt:")) {
|
|
30538
|
+
index.set(evidenceRef, attribution);
|
|
30539
|
+
}
|
|
30540
|
+
}
|
|
30541
|
+
}
|
|
30542
|
+
return index;
|
|
30543
|
+
}
|
|
30544
|
+
function parsePromptNodeAttribution(summary) {
|
|
30545
|
+
const match = /^Prompt sent from ([a-z_]+) to ([a-z_]+)\.$/.exec(summary.trim());
|
|
30546
|
+
if (!match) {
|
|
30547
|
+
return null;
|
|
30548
|
+
}
|
|
30549
|
+
return {
|
|
30550
|
+
surfaceLabel: friendlySurfaceLabel(match[1]),
|
|
30551
|
+
providerLabel: friendlyProviderLabel(match[2])
|
|
30552
|
+
};
|
|
30553
|
+
}
|
|
30554
|
+
function activityDetailForReceipt(receipt, attribution) {
|
|
30555
|
+
const graphAttribution = attribution.get(receipt.receipt_id);
|
|
30556
|
+
const surfaceLabel = graphAttribution?.surfaceLabel ?? (receipt.policy_basis.some((basis) => basis.includes("guardian:mcp:")) ? "MCP" : void 0);
|
|
30557
|
+
const providerLabel = graphAttribution?.providerLabel;
|
|
30558
|
+
const parts = [
|
|
30559
|
+
surfaceLabel,
|
|
30560
|
+
providerLabel,
|
|
30561
|
+
friendlyReceiptTypeLabel(receipt.receipt_type)
|
|
30562
|
+
].filter((part) => Boolean(part));
|
|
30563
|
+
return {
|
|
30564
|
+
surfaceLabel,
|
|
30565
|
+
providerLabel,
|
|
30566
|
+
metaDetail: parts.join(" \xB7 ") || friendlyReceiptTypeLabel(receipt.receipt_type)
|
|
30567
|
+
};
|
|
30568
|
+
}
|
|
30569
|
+
function friendlySurfaceLabel(surface) {
|
|
30570
|
+
switch (surface) {
|
|
30571
|
+
case "browser":
|
|
30572
|
+
return "Browser";
|
|
30573
|
+
case "mcp":
|
|
30574
|
+
return "MCP";
|
|
30575
|
+
case "ide":
|
|
30576
|
+
return "IDE";
|
|
30577
|
+
case "cli":
|
|
30578
|
+
return "CLI";
|
|
30579
|
+
case "desktop":
|
|
30580
|
+
return "Desktop app";
|
|
30581
|
+
case "rest":
|
|
30582
|
+
return "API";
|
|
30583
|
+
case "voice":
|
|
30584
|
+
return "Voice";
|
|
30585
|
+
default:
|
|
30586
|
+
return void 0;
|
|
30587
|
+
}
|
|
30588
|
+
}
|
|
30589
|
+
function friendlyProviderLabel(provider) {
|
|
30590
|
+
switch (provider) {
|
|
30591
|
+
case "chatgpt":
|
|
30592
|
+
return "ChatGPT";
|
|
30593
|
+
case "claude":
|
|
30594
|
+
return "Claude";
|
|
30595
|
+
case "gemini":
|
|
30596
|
+
return "Gemini";
|
|
30597
|
+
case "perplexity":
|
|
30598
|
+
return "Perplexity";
|
|
30599
|
+
case "cursor":
|
|
30600
|
+
return "Cursor";
|
|
30601
|
+
case "windsurf":
|
|
30602
|
+
return "Windsurf";
|
|
30603
|
+
case "openrouter":
|
|
30604
|
+
return "OpenRouter";
|
|
30605
|
+
case "local_model":
|
|
30606
|
+
return "Local AI";
|
|
30607
|
+
case "unknown":
|
|
30608
|
+
return void 0;
|
|
30609
|
+
default:
|
|
30610
|
+
return provider ? provider.split(/[_-]+/g).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ") : void 0;
|
|
30611
|
+
}
|
|
30612
|
+
}
|
|
30613
|
+
function friendlyReceiptTypeLabel(receiptType) {
|
|
30614
|
+
switch (receiptType) {
|
|
30615
|
+
case "prompt":
|
|
30616
|
+
return "Prompt check";
|
|
30617
|
+
case "policy":
|
|
30618
|
+
return "Policy receipt";
|
|
30619
|
+
case "context":
|
|
30620
|
+
return "Context receipt";
|
|
30621
|
+
case "local_action":
|
|
30622
|
+
return "Local action";
|
|
30623
|
+
case "learning":
|
|
30624
|
+
return "Learning receipt";
|
|
30625
|
+
case "output":
|
|
30626
|
+
return "Output check";
|
|
30627
|
+
}
|
|
30628
|
+
}
|
|
28489
30629
|
function renderReceiptRow(receipt) {
|
|
28490
30630
|
const policyBasis = receipt.policy_basis.length > 0 ? receipt.policy_basis.join(", ") : "none recorded";
|
|
28491
|
-
return ` <tr><th scope="row"><code>${escapeHtml(receipt.receipt_id)}</code></th><td>${escapeHtml(receipt.receipt_type)}</td><td>${escapeHtml(receipt.disposition)}</td><td><details><summary>${escapeHtml(receipt.summary)}</summary><dl><dt>Transaction</dt><dd><code>${escapeHtml(receipt.transaction_id)}</code></dd><dt>Created</dt><dd>${
|
|
30631
|
+
return ` <tr class="receipt-row" data-receipt-row data-receipt-id="${escapeHtml(receipt.receipt_id)}"><th scope="row"><code>${escapeHtml(receipt.receipt_id)}</code></th><td>${escapeHtml(receipt.receipt_type)}</td><td>${escapeHtml(receipt.disposition)}</td><td><details><summary>${escapeHtml(receipt.summary)}</summary><dl><dt>Transaction</dt><dd><code>${escapeHtml(receipt.transaction_id)}</code></dd><dt>Created</dt><dd>${renderLocalTime(receipt.created_at)}</dd><dt>Policy basis</dt><dd>${escapeHtml(policyBasis)}</dd><dt>Receipt hash</dt><dd><code>${escapeHtml(receipt.receipt_hash)}</code></dd><dt>Raw content</dt><dd>${receipt.raw_content_included ? "included" : "metadata-only"}</dd></dl></details></td></tr>`;
|
|
28492
30632
|
}
|
|
28493
30633
|
function activityTone(disposition) {
|
|
28494
30634
|
if (disposition === "block") return "block";
|
|
@@ -28517,17 +30657,321 @@ function activityFilterGroup(disposition) {
|
|
|
28517
30657
|
if (disposition === "warn" || disposition === "confirm") return "flagged";
|
|
28518
30658
|
return "allowed";
|
|
28519
30659
|
}
|
|
28520
|
-
function formatActivityTime(createdAt) {
|
|
30660
|
+
function formatActivityTime(createdAt, nowMs = Date.now()) {
|
|
28521
30661
|
const parsed = Date.parse(createdAt);
|
|
28522
30662
|
if (Number.isNaN(parsed)) {
|
|
28523
30663
|
return createdAt;
|
|
28524
30664
|
}
|
|
30665
|
+
const diffMs = parsed - nowMs;
|
|
30666
|
+
const absMs = Math.abs(diffMs);
|
|
30667
|
+
if (absMs < 60 * 1e3) {
|
|
30668
|
+
return "just now";
|
|
30669
|
+
}
|
|
30670
|
+
if (absMs < 60 * 60 * 1e3) {
|
|
30671
|
+
const minutes = Math.round(absMs / (60 * 1e3));
|
|
30672
|
+
return diffMs < 0 ? `${minutes} min ago` : `in ${minutes} min`;
|
|
30673
|
+
}
|
|
30674
|
+
if (absMs < 24 * 60 * 60 * 1e3) {
|
|
30675
|
+
const hours = Math.round(absMs / (60 * 60 * 1e3));
|
|
30676
|
+
return diffMs < 0 ? `${hours} hr ago` : `in ${hours} hr`;
|
|
30677
|
+
}
|
|
30678
|
+
if (absMs < 7 * 24 * 60 * 60 * 1e3) {
|
|
30679
|
+
const days = Math.round(absMs / (24 * 60 * 60 * 1e3));
|
|
30680
|
+
return diffMs < 0 ? `${days} day${days === 1 ? "" : "s"} ago` : `in ${days} day${days === 1 ? "" : "s"}`;
|
|
30681
|
+
}
|
|
28525
30682
|
return new Date(parsed).toLocaleString("en-US", {
|
|
28526
|
-
|
|
28527
|
-
|
|
28528
|
-
|
|
30683
|
+
month: "short",
|
|
30684
|
+
day: "numeric",
|
|
30685
|
+
year: "numeric",
|
|
30686
|
+
hour: "numeric",
|
|
30687
|
+
minute: "2-digit",
|
|
30688
|
+
timeZoneName: "short"
|
|
28529
30689
|
});
|
|
28530
30690
|
}
|
|
30691
|
+
function renderLocalTime(createdAt) {
|
|
30692
|
+
const parsed = Date.parse(createdAt);
|
|
30693
|
+
if (Number.isNaN(parsed)) {
|
|
30694
|
+
return escapeHtml(createdAt);
|
|
30695
|
+
}
|
|
30696
|
+
return `<time data-guardian-local-time datetime="${escapeHtml(createdAt)}" title="${escapeHtml(
|
|
30697
|
+
`Stored by Guardian as ${createdAt}`
|
|
30698
|
+
)}">${escapeHtml(formatActivityTime(createdAt))}</time>`;
|
|
30699
|
+
}
|
|
30700
|
+
function renderLocalGuardianStatus(status) {
|
|
30701
|
+
switch (status) {
|
|
30702
|
+
case "start_requested":
|
|
30703
|
+
return "Running";
|
|
30704
|
+
case "not_started_mvp":
|
|
30705
|
+
return "Not running";
|
|
30706
|
+
}
|
|
30707
|
+
}
|
|
30708
|
+
function renderControlTowerRoleCards(viewModel) {
|
|
30709
|
+
return viewModel.surfaces.map(
|
|
30710
|
+
(surface) => ` <article class="role-card">
|
|
30711
|
+
<strong>${escapeHtml(surface.label)}</strong>
|
|
30712
|
+
<span>${escapeHtml(surface.audience)}</span>
|
|
30713
|
+
<span>${escapeHtml(surface.purpose)}</span>
|
|
30714
|
+
</article>`
|
|
30715
|
+
).join("\n");
|
|
30716
|
+
}
|
|
30717
|
+
function renderAdminNoticeBar(notices) {
|
|
30718
|
+
if (notices.length === 0) {
|
|
30719
|
+
return "";
|
|
30720
|
+
}
|
|
30721
|
+
return ` <section class="notice-bar" aria-label="Guardian notifications">
|
|
30722
|
+
${notices.map((notice) => renderAdminNoticeCard(notice)).join("\n")}
|
|
30723
|
+
</section>`;
|
|
30724
|
+
}
|
|
30725
|
+
function renderAdminNoticeList(notices) {
|
|
30726
|
+
if (notices.length === 0) {
|
|
30727
|
+
return " <p>No local admin notices are published.</p>";
|
|
30728
|
+
}
|
|
30729
|
+
return ` <div class="notice-bar" aria-label="Current admin notices">
|
|
30730
|
+
${notices.map((notice) => renderAdminNoticeCard(notice)).join("\n")}
|
|
30731
|
+
</div>`;
|
|
30732
|
+
}
|
|
30733
|
+
function renderAdminNoticeCard(notice) {
|
|
30734
|
+
return ` <article class="notice-card ${escapeHtml(notice.tone)}">
|
|
30735
|
+
<strong>${escapeHtml(notice.title)}</strong>
|
|
30736
|
+
<p>${escapeHtml(notice.message)}</p>
|
|
30737
|
+
<span>${escapeHtml(adminNoticeAudienceLabel(notice.audience))} \xB7 ${renderLocalTime(notice.created_at)}</span>
|
|
30738
|
+
${notice.receipt_id ? `<code>${escapeHtml(notice.receipt_id)}</code>` : ""}
|
|
30739
|
+
</article>`;
|
|
30740
|
+
}
|
|
30741
|
+
function adminNoticeAudienceLabel(audience) {
|
|
30742
|
+
switch (audience) {
|
|
30743
|
+
case "admins":
|
|
30744
|
+
return "Admins";
|
|
30745
|
+
case "developers":
|
|
30746
|
+
return "Developers";
|
|
30747
|
+
case "all_users":
|
|
30748
|
+
return "Everyone";
|
|
30749
|
+
}
|
|
30750
|
+
}
|
|
30751
|
+
function renderGuardianAdminSurface(viewModel, profile, receipts, appPermissionRows) {
|
|
30752
|
+
return ` <section aria-labelledby="guardian-admin-overview">
|
|
30753
|
+
<h2 id="guardian-admin-overview">Guardian Admin</h2>
|
|
30754
|
+
<p>Manage policy packs, marketplace approvals, Prime Pack custom rules, deployment scope, receipts, and release automation from the same local Guardian brain used by the browser extension and MCP clients.</p>
|
|
30755
|
+
<div class="admin-grid">
|
|
30756
|
+
${renderAdminSummaryCards(viewModel, profile, receipts, appPermissionRows)}
|
|
30757
|
+
</div>
|
|
30758
|
+
</section>
|
|
30759
|
+
<section aria-labelledby="admin-notifications">
|
|
30760
|
+
<h2 id="admin-notifications">Admin Notifications</h2>
|
|
30761
|
+
<p>Send a short local notice into the Control Tower notification bar. Notices are metadata-only and stay on this device in <code>${escapeHtml(CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME)}</code>.</p>
|
|
30762
|
+
<form class="notice-form" method="post" action="/v1/guardian/admin/notice">
|
|
30763
|
+
<input type="hidden" name="action" value="publish">
|
|
30764
|
+
<label>Title<input name="title" maxlength="${CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT}" placeholder="Policy reminder" required></label>
|
|
30765
|
+
<label>Message<textarea name="message" maxlength="${CONTROL_TOWER_ADMIN_NOTICE_MESSAGE_LIMIT}" placeholder="Use approved customer-data placeholders in external AI tools." required></textarea></label>
|
|
30766
|
+
<label>Audience<select name="audience"><option value="all_users">Everyone</option><option value="admins">Admins</option><option value="developers">Developers</option></select></label>
|
|
30767
|
+
<label>Tone<select name="tone"><option value="info">Helpful note</option><option value="warning">Watch out</option><option value="success">Good news</option></select></label>
|
|
30768
|
+
<button type="submit">Publish local notice</button>
|
|
30769
|
+
</form>
|
|
30770
|
+
<form method="post" action="/v1/guardian/admin/notice">
|
|
30771
|
+
<input type="hidden" name="action" value="clear">
|
|
30772
|
+
<button type="submit">Clear local notices</button>
|
|
30773
|
+
</form>
|
|
30774
|
+
${renderAdminNoticeList(viewModel.admin.notices)}
|
|
30775
|
+
</section>
|
|
30776
|
+
<section aria-labelledby="admin-policy-packs">
|
|
30777
|
+
<h2 id="admin-policy-packs">Policy Pack Management</h2>
|
|
30778
|
+
<p>Turn built-in packs on or off for this device now. In enterprise deployments, the same model becomes group assignment.</p>
|
|
30779
|
+
<table aria-label="Admin policy pack assignment">
|
|
30780
|
+
<thead>
|
|
30781
|
+
<tr><th scope="col">Pack</th><th scope="col">State</th><th scope="col">Local action</th></tr>
|
|
30782
|
+
</thead>
|
|
30783
|
+
<tbody>
|
|
30784
|
+
${profile.policy_packs.available.map((packId) => {
|
|
30785
|
+
const active = profile.policy_packs.active.includes(packId);
|
|
30786
|
+
const disabled = active && profile.policy_packs.active.length <= 1;
|
|
30787
|
+
return ` <tr><th scope="row">${escapeHtml(packId)}</th><td>${active ? "active" : "available"}</td><td>${renderPolicyPackForm(active ? "disable" : "enable", packId, disabled)}</td></tr>`;
|
|
30788
|
+
}).join("\n")}
|
|
30789
|
+
</tbody>
|
|
30790
|
+
</table>
|
|
30791
|
+
</section>
|
|
30792
|
+
<section aria-labelledby="admin-marketplace">
|
|
30793
|
+
<h2 id="admin-marketplace">Policy Pack Marketplace</h2>
|
|
30794
|
+
<p>Review pack metadata before connecting it to a deployment. Packs can start as request/approval metadata before billing automation is added.</p>
|
|
30795
|
+
<div class="marketplace-grid">
|
|
30796
|
+
${renderMarketplaceCards(viewModel)}
|
|
30797
|
+
</div>
|
|
30798
|
+
${renderMarketplaceRequestList(viewModel.admin.marketplaceRequests)}
|
|
30799
|
+
</section>
|
|
30800
|
+
<section aria-labelledby="admin-prime-pack">
|
|
30801
|
+
<h2 id="admin-prime-pack">Prime Pack Builder</h2>
|
|
30802
|
+
<p>Create one organization-specific pack from terms, domains, blocked destinations, and escalation phrases. Upload support is designed for text, CSV, and JSON sources.</p>
|
|
30803
|
+
<article class="prime-pack-builder">
|
|
30804
|
+
<h3>Custom protection inputs</h3>
|
|
30805
|
+
<form method="post" action="/v1/guardian/admin/prime-pack">
|
|
30806
|
+
<div class="builder-grid">
|
|
30807
|
+
<label>Sensitive terms<textarea name="sensitiveTerms" maxlength="${CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT * CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT}" placeholder="Customer IDs, internal code names, regulated phrases"></textarea></label>
|
|
30808
|
+
<label>Approved domains<textarea name="approvedDomains" maxlength="${CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT * CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT}" placeholder="company.com, secure.vendor.com"></textarea></label>
|
|
30809
|
+
<label>Blocked destinations<textarea name="blockedDestinations" maxlength="${CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT * CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT}" placeholder="paste sites, personal email domains, unknown file shares"></textarea></label>
|
|
30810
|
+
<label>Escalation phrases<textarea name="escalationPhrases" maxlength="${CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT * CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT}" placeholder="payment approval, publish externally, send to customer"></textarea></label>
|
|
30811
|
+
</div>
|
|
30812
|
+
<label>Import from text, CSV, or JSON<textarea name="importContent" maxlength="${CONTROL_TOWER_PRIME_PACK_IMPORT_LIMIT}" placeholder="Examples: sensitive_terms,Project Falcon blocked_destinations,paste.example or JSON with sensitive_terms, approved_domains, blocked_destinations, and escalation_phrases arrays"></textarea></label>
|
|
30813
|
+
<div class="button-row">
|
|
30814
|
+
<button type="submit" name="action" value="preview">Preview impact</button>
|
|
30815
|
+
<button type="submit" name="action" value="deploy">Deploy locally</button>
|
|
30816
|
+
</div>
|
|
30817
|
+
</form>
|
|
30818
|
+
<ul class="metadata-list">
|
|
30819
|
+
<li><b>Input formats</b><span>${escapeHtml(viewModel.admin.primePack.supportedInputs.join(", "))}</span></li>
|
|
30820
|
+
<li><b>Preview</b><span>stores counts and sample impact in <code>${escapeHtml(CONTROL_TOWER_PRIME_PACK_FILE_NAME)}</code></span></li>
|
|
30821
|
+
<li><b>Deploy</b><span>writes a local admin receipt before assignment</span></li>
|
|
30822
|
+
</ul>
|
|
30823
|
+
</article>
|
|
30824
|
+
</section>
|
|
30825
|
+
<section aria-labelledby="admin-deployment-scope">
|
|
30826
|
+
<h2 id="admin-deployment-scope">Deployment Scope</h2>
|
|
30827
|
+
<p>Assign posture and packs by user or group. Personal Guardian uses the first row; enterprise deployments add managed groups.</p>
|
|
30828
|
+
<table aria-label="Guardian deployment groups">
|
|
30829
|
+
<thead>
|
|
30830
|
+
<tr><th scope="col">Group</th><th scope="col">Users</th><th scope="col">Posture</th><th scope="col">Packs</th></tr>
|
|
30831
|
+
</thead>
|
|
30832
|
+
<tbody>
|
|
30833
|
+
${viewModel.admin.managedGroups.map(
|
|
30834
|
+
(group) => ` <tr><th scope="row">${escapeHtml(group.label)}</th><td>${group.users}</td><td>${escapeHtml(group.posture)}</td><td>${escapeHtml(group.packs.join(", ") || "none")}</td></tr>`
|
|
30835
|
+
).join("\n")}
|
|
30836
|
+
</tbody>
|
|
30837
|
+
</table>
|
|
30838
|
+
</section>
|
|
30839
|
+
<section aria-labelledby="admin-release-automation">
|
|
30840
|
+
<h2 id="admin-release-automation">Release Automation</h2>
|
|
30841
|
+
<p>Use GitHub Actions as the default release path so package evidence, browser artifacts, Docker smoke tests, and npm publication stay repeatable.</p>
|
|
30842
|
+
<div class="builder-grid">
|
|
30843
|
+
<article class="release-command-card">
|
|
30844
|
+
<h3>Dry run</h3>
|
|
30845
|
+
<code class="command-box">${escapeHtml(viewModel.releaseAutomation.dryRunCommand)}</code>
|
|
30846
|
+
</article>
|
|
30847
|
+
<article class="release-command-card">
|
|
30848
|
+
<h3>Publish</h3>
|
|
30849
|
+
<code class="command-box">${escapeHtml(viewModel.releaseAutomation.publishCommand)}</code>
|
|
30850
|
+
</article>
|
|
30851
|
+
</div>
|
|
30852
|
+
<ul class="metadata-list">
|
|
30853
|
+
<li><b>Repository</b><span>${escapeHtml(viewModel.releaseAutomation.repository)}</span></li>
|
|
30854
|
+
<li><b>Workflow</b><span>${escapeHtml(viewModel.releaseAutomation.workflow)}</span></li>
|
|
30855
|
+
<li><b>NPM package</b><span>${escapeHtml(viewModel.releaseAutomation.npmPackage)}</span></li>
|
|
30856
|
+
<li><b>Chrome extension</b><span>${escapeHtml(viewModel.releaseAutomation.chromeExtensionId)}</span></li>
|
|
30857
|
+
<li><b>Still manual</b><span>${escapeHtml(viewModel.releaseAutomation.humanIntervention[0])}</span></li>
|
|
30858
|
+
</ul>
|
|
30859
|
+
</section>
|
|
30860
|
+
<section aria-labelledby="admin-audit">
|
|
30861
|
+
<h2 id="admin-audit">Receipts And Audit</h2>
|
|
30862
|
+
<p>Admins can review metadata-only decisions without exposing raw prompt text, raw responses, local action payloads, or runtime tokens.</p>
|
|
30863
|
+
<table aria-label="Guardian admin receipt audit">
|
|
30864
|
+
<thead>
|
|
30865
|
+
<tr><th scope="col">Receipt</th><th scope="col">Type</th><th scope="col">Decision</th><th scope="col">Details</th></tr>
|
|
30866
|
+
</thead>
|
|
30867
|
+
<tbody>
|
|
30868
|
+
${renderReceiptRows(receipts)}
|
|
30869
|
+
</tbody>
|
|
30870
|
+
</table>
|
|
30871
|
+
</section>`;
|
|
30872
|
+
}
|
|
30873
|
+
function renderAdminSummaryCards(viewModel, profile, receipts, appPermissionRows) {
|
|
30874
|
+
const cards = [
|
|
30875
|
+
{
|
|
30876
|
+
title: "Managed groups",
|
|
30877
|
+
value: String(viewModel.admin.managedGroups.length),
|
|
30878
|
+
detail: "Group-level posture and pack assignment model."
|
|
30879
|
+
},
|
|
30880
|
+
{
|
|
30881
|
+
title: "Marketplace packs",
|
|
30882
|
+
value: String(viewModel.marketplace.length),
|
|
30883
|
+
detail: "Metadata listings ready for approval review."
|
|
30884
|
+
},
|
|
30885
|
+
{
|
|
30886
|
+
title: "Prime Pack",
|
|
30887
|
+
value: viewModel.admin.primePack.status.replace(/_/gu, " "),
|
|
30888
|
+
detail: "Custom strings, domains, destinations, and escalation phrases."
|
|
30889
|
+
},
|
|
30890
|
+
{
|
|
30891
|
+
title: "Active local packs",
|
|
30892
|
+
value: String(profile.policy_packs.active.length),
|
|
30893
|
+
detail: profile.policy_packs.active.join(", ") || "none"
|
|
30894
|
+
},
|
|
30895
|
+
{
|
|
30896
|
+
title: "Governed apps",
|
|
30897
|
+
value: String(appPermissionRows.length),
|
|
30898
|
+
detail: "Local app access remains confirmation-gated."
|
|
30899
|
+
},
|
|
30900
|
+
{
|
|
30901
|
+
title: "Audit receipts",
|
|
30902
|
+
value: String(receipts.length),
|
|
30903
|
+
detail: "Metadata-only receipt chain."
|
|
30904
|
+
}
|
|
30905
|
+
];
|
|
30906
|
+
return cards.map(
|
|
30907
|
+
(card) => ` <article class="admin-card">
|
|
30908
|
+
<h3>${escapeHtml(card.title)}</h3>
|
|
30909
|
+
<strong>${escapeHtml(card.value)}</strong>
|
|
30910
|
+
<span>${escapeHtml(card.detail)}</span>
|
|
30911
|
+
</article>`
|
|
30912
|
+
).join("\n");
|
|
30913
|
+
}
|
|
30914
|
+
function renderMarketplaceCards(viewModel) {
|
|
30915
|
+
return viewModel.marketplace.map(
|
|
30916
|
+
(pack) => ` <article class="marketplace-card" data-marketplace-pack-id="${escapeHtml(pack.packId)}">
|
|
30917
|
+
<h3>${escapeHtml(pack.name)}</h3>
|
|
30918
|
+
<p>${escapeHtml(pack.examples[0] ?? "Policy pack metadata listing.")}</p>
|
|
30919
|
+
<ul class="metadata-list">
|
|
30920
|
+
<li><b>Audience</b><span>${escapeHtml(pack.audience)}</span></li>
|
|
30921
|
+
<li><b>Category</b><span>${escapeHtml(pack.category)}</span></li>
|
|
30922
|
+
<li><b>Maintainer</b><span>${escapeHtml(pack.publisher)}</span></li>
|
|
30923
|
+
<li><b>Version</b><span>${escapeHtml(pack.version)}</span></li>
|
|
30924
|
+
<li><b>Price/license</b><span>${escapeHtml(pack.price)}</span></li>
|
|
30925
|
+
<li><b>Approval state</b><span>${escapeHtml(pack.state)}</span></li>
|
|
30926
|
+
<li><b>False-positive risk</b><span>${escapeHtml(pack.risk)}</span></li>
|
|
30927
|
+
</ul>
|
|
30928
|
+
${renderMarketplaceRequestAction(pack)}
|
|
30929
|
+
<details class="marketplace-detail-drawer">
|
|
30930
|
+
<summary>Review details before connecting</summary>
|
|
30931
|
+
<dl>
|
|
30932
|
+
<dt>Permissions requested</dt>
|
|
30933
|
+
<dd>${escapeHtml(pack.permissions.join(" "))}</dd>
|
|
30934
|
+
<dt>Protected data</dt>
|
|
30935
|
+
<dd>${escapeHtml(pack.protectedDataTypes.join(", "))}</dd>
|
|
30936
|
+
<dt>Examples</dt>
|
|
30937
|
+
<dd>${escapeHtml(pack.examples.join(" "))}</dd>
|
|
30938
|
+
<dt>License reference</dt>
|
|
30939
|
+
<dd><code>${escapeHtml(pack.licenseRef)}</code></dd>
|
|
30940
|
+
<dt>Support reference</dt>
|
|
30941
|
+
<dd><code>${escapeHtml(pack.supportRef)}</code></dd>
|
|
30942
|
+
</dl>
|
|
30943
|
+
</details>
|
|
30944
|
+
</article>`
|
|
30945
|
+
).join("\n");
|
|
30946
|
+
}
|
|
30947
|
+
function renderMarketplaceRequestAction(pack) {
|
|
30948
|
+
if (pack.requestState === "connected") {
|
|
30949
|
+
return ' <p class="marketplace-request-state">Connected locally through active policy packs.</p>';
|
|
30950
|
+
}
|
|
30951
|
+
if (pack.requestState === "requested_metadata_only") {
|
|
30952
|
+
return ` <p class="marketplace-request-state">Review requested ${pack.requestedAt ? renderLocalTime(pack.requestedAt) : "locally"}.</p>`;
|
|
30953
|
+
}
|
|
30954
|
+
return ` <form class="marketplace-request-form" method="post" action="/v1/guardian/admin/marketplace-request">
|
|
30955
|
+
<input type="hidden" name="action" value="request_review">
|
|
30956
|
+
<input type="hidden" name="packId" value="${escapeHtml(pack.packId)}">
|
|
30957
|
+
<button type="submit">Request review</button>
|
|
30958
|
+
<span>Writes a local receipt; paid pack connection remains approval-gated.</span>
|
|
30959
|
+
</form>`;
|
|
30960
|
+
}
|
|
30961
|
+
function renderMarketplaceRequestList(requests) {
|
|
30962
|
+
if (requests.length === 0) {
|
|
30963
|
+
return " <p>No marketplace pack reviews have been requested on this device yet.</p>";
|
|
30964
|
+
}
|
|
30965
|
+
return ` <div class="marketplace-request-list" aria-label="Marketplace review requests">
|
|
30966
|
+
${requests.map(
|
|
30967
|
+
(request) => ` <article class="marketplace-request-card">
|
|
30968
|
+
<strong>${escapeHtml(request.pack_name)}</strong>
|
|
30969
|
+
<span>${escapeHtml(request.publisher)} \xB7 ${renderLocalTime(request.requested_at)}</span>
|
|
30970
|
+
<code>${escapeHtml(request.receipt_id)}</code>
|
|
30971
|
+
</article>`
|
|
30972
|
+
).join("\n")}
|
|
30973
|
+
</div>`;
|
|
30974
|
+
}
|
|
28531
30975
|
function providerDisplayName(providerId) {
|
|
28532
30976
|
if (providerId === "chatgpt") return "ChatGPT";
|
|
28533
30977
|
if (providerId === "claude") return "Claude";
|
|
@@ -28823,7 +31267,7 @@ function listen(server, host, port) {
|
|
|
28823
31267
|
});
|
|
28824
31268
|
});
|
|
28825
31269
|
}
|
|
28826
|
-
var MAX_BODY_BYTES, ADMIN_HEADER, CONTROL_TOWER_COOKIE, PROMPT_COACH_MODES2, CONTROL_TOWER_RECEIPT_LIMIT, CONTROL_TOWER_GRAPH_NODE_LIMIT, CONTROL_TOWER_GRAPH_EDGE_LIMIT, CONTROL_TOWER_LEARNING_EVENT_LIMIT, CONTROL_TOWER_DATA_FILE_NAMES, CONTROL_TOWER_DATA_DIRECTORY_NAMES, CONTROL_TOWER_SUPPORTED_LIFECYCLE_ACTIONS;
|
|
31270
|
+
var MAX_BODY_BYTES, ADMIN_HEADER, CONTROL_TOWER_COOKIE, PROMPT_COACH_MODES2, CONTROL_TOWER_RECEIPT_LIMIT, CONTROL_TOWER_GRAPH_NODE_LIMIT, CONTROL_TOWER_GRAPH_EDGE_LIMIT, CONTROL_TOWER_LEARNING_EVENT_LIMIT, CONTROL_TOWER_ADMIN_NOTICE_LIMIT, CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME, CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT, CONTROL_TOWER_ADMIN_NOTICE_MESSAGE_LIMIT, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT, CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME, CONTROL_TOWER_PRIME_PACK_FILE_NAME, CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT, CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT, CONTROL_TOWER_PRIME_PACK_IMPORT_LIMIT, CONTROL_TOWER_DATA_FILE_NAMES, CONTROL_TOWER_DATA_DIRECTORY_NAMES, CONTROL_TOWER_SUPPORTED_LIFECYCLE_ACTIONS, CONTROL_TOWER_RELEASE_COMMAND_TEMPLATE, CONTROL_TOWER_DRY_RUN_COMMAND_TEMPLATE;
|
|
28827
31271
|
var init_daemon = __esm({
|
|
28828
31272
|
"packages/guardian-cli/src/daemon.ts"() {
|
|
28829
31273
|
"use strict";
|
|
@@ -28839,12 +31283,25 @@ var init_daemon = __esm({
|
|
|
28839
31283
|
CONTROL_TOWER_GRAPH_NODE_LIMIT = 12;
|
|
28840
31284
|
CONTROL_TOWER_GRAPH_EDGE_LIMIT = 16;
|
|
28841
31285
|
CONTROL_TOWER_LEARNING_EVENT_LIMIT = 12;
|
|
31286
|
+
CONTROL_TOWER_ADMIN_NOTICE_LIMIT = 5;
|
|
31287
|
+
CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME = "admin-notices.json";
|
|
31288
|
+
CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT = 80;
|
|
31289
|
+
CONTROL_TOWER_ADMIN_NOTICE_MESSAGE_LIMIT = 240;
|
|
31290
|
+
CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT = 20;
|
|
31291
|
+
CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME = "marketplace-requests.json";
|
|
31292
|
+
CONTROL_TOWER_PRIME_PACK_FILE_NAME = "prime-pack-draft.json";
|
|
31293
|
+
CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT = 120;
|
|
31294
|
+
CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT = 100;
|
|
31295
|
+
CONTROL_TOWER_PRIME_PACK_IMPORT_LIMIT = 24 * 1024;
|
|
28842
31296
|
CONTROL_TOWER_DATA_FILE_NAMES = [
|
|
28843
31297
|
"profile.json",
|
|
28844
31298
|
"policy-packs.json",
|
|
28845
31299
|
"posture-profile.json",
|
|
28846
31300
|
"preferences.json",
|
|
28847
31301
|
"permissions.json",
|
|
31302
|
+
CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME,
|
|
31303
|
+
CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME,
|
|
31304
|
+
CONTROL_TOWER_PRIME_PACK_FILE_NAME,
|
|
28848
31305
|
"runtime-token"
|
|
28849
31306
|
];
|
|
28850
31307
|
CONTROL_TOWER_DATA_DIRECTORY_NAMES = ["ecl", "receipts", "mcp"];
|
|
@@ -28855,6 +31312,8 @@ var init_daemon = __esm({
|
|
|
28855
31312
|
"restore",
|
|
28856
31313
|
"delete"
|
|
28857
31314
|
];
|
|
31315
|
+
CONTROL_TOWER_RELEASE_COMMAND_TEMPLATE = `gh workflow run ${GUARDIAN_RELEASE_DISPATCH_WORKFLOW} -f release_version=<version> -f channels=all -f dry_run=false -f run_docker_smoke=true -f publish_auth=token -f browser_store_publish=manual`;
|
|
31316
|
+
CONTROL_TOWER_DRY_RUN_COMMAND_TEMPLATE = `gh workflow run ${GUARDIAN_RELEASE_DISPATCH_WORKFLOW} -f release_version=<version> -f channels=all -f dry_run=true -f run_docker_smoke=true -f publish_auth=token -f browser_store_publish=manual`;
|
|
28858
31317
|
}
|
|
28859
31318
|
});
|
|
28860
31319
|
|