@contextecf/guardian-cli 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -14
- package/dist/packages/guardian-cli/src/bin.js +2430 -115
- package/dist/packages/guardian-cli/src/index.js +662 -83
- package/dist/packages/guardian-cli/src/runtime.d.ts +49 -4
- 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",
|
|
@@ -17461,7 +17553,7 @@ function buildGuardianDistributionReadiness(npmPreviewReady) {
|
|
|
17461
17553
|
boundaries: [
|
|
17462
17554
|
"Use dry_run=true before public publication.",
|
|
17463
17555
|
"dry_run=false requires the repository secret NPM_TOKEN and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
17464
|
-
"The workflow can publish the npm preview package, upload evidence, and run public install smokes; it does not sign native installers or
|
|
17556
|
+
"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
17557
|
"GitHub Actions OIDC/provenance applies to CI release context; manual local publishes are recorded separately and should not claim CI provenance."
|
|
17466
17558
|
]
|
|
17467
17559
|
},
|
|
@@ -17482,12 +17574,16 @@ function buildGuardianReleaseDispatchCommand(dryRun) {
|
|
|
17482
17574
|
});
|
|
17483
17575
|
}
|
|
17484
17576
|
function buildGuardianReleaseDispatchCommandFor(input2) {
|
|
17577
|
+
const publishAuth = input2.publishAuth ?? "token";
|
|
17578
|
+
const browserStorePublish = input2.browserStorePublish ?? "manual";
|
|
17485
17579
|
return [
|
|
17486
17580
|
`gh workflow run ${GUARDIAN_RELEASE_DISPATCH_WORKFLOW} \\`,
|
|
17487
17581
|
` -f release_version=${input2.releaseVersion} \\`,
|
|
17488
17582
|
` -f channels=${input2.channels} \\`,
|
|
17489
17583
|
` -f dry_run=${input2.dryRun ? "true" : "false"} \\`,
|
|
17490
|
-
` -f run_docker_smoke=${input2.runDockerSmoke ? "true" : "false"}
|
|
17584
|
+
` -f run_docker_smoke=${input2.runDockerSmoke ? "true" : "false"} \\`,
|
|
17585
|
+
` -f publish_auth=${publishAuth} \\`,
|
|
17586
|
+
` -f browser_store_publish=${browserStorePublish}`
|
|
17491
17587
|
].join("\n");
|
|
17492
17588
|
}
|
|
17493
17589
|
async function runReleaseCommand(argv, flags, options) {
|
|
@@ -17594,11 +17690,23 @@ async function runReleaseCommand(argv, flags, options) {
|
|
|
17594
17690
|
}
|
|
17595
17691
|
const dryRun = flags.has("publish") ? false : booleanFlag(flags, "dry-run", true);
|
|
17596
17692
|
const runDockerSmoke = booleanFlag(flags, "run-docker-smoke", true);
|
|
17693
|
+
const publishAuth = stringFlag(flags, "publish-auth") ?? "token";
|
|
17694
|
+
if (!isValidReleasePublishAuth(publishAuth)) {
|
|
17695
|
+
write(stderr, "Invalid --publish-auth. Expected one of: token, trusted_publisher.\n");
|
|
17696
|
+
return 1;
|
|
17697
|
+
}
|
|
17698
|
+
const browserStorePublish = stringFlag(flags, "browser-store-publish") ?? "manual";
|
|
17699
|
+
if (!isValidBrowserStorePublish(browserStorePublish)) {
|
|
17700
|
+
write(stderr, "Invalid --browser-store-publish. Expected one of: manual, api.\n");
|
|
17701
|
+
return 1;
|
|
17702
|
+
}
|
|
17597
17703
|
const result = buildGuardianReleaseDispatchCommandResult({
|
|
17598
17704
|
releaseVersion,
|
|
17599
17705
|
channels,
|
|
17600
17706
|
dryRun,
|
|
17601
|
-
runDockerSmoke
|
|
17707
|
+
runDockerSmoke,
|
|
17708
|
+
publishAuth,
|
|
17709
|
+
browserStorePublish
|
|
17602
17710
|
});
|
|
17603
17711
|
write(stdout, renderGuardianReleaseDispatchCommand(result, flags));
|
|
17604
17712
|
return 0;
|
|
@@ -17666,6 +17774,7 @@ function renderGuardianReleaseHelp(result, flags) {
|
|
|
17666
17774
|
`;
|
|
17667
17775
|
}
|
|
17668
17776
|
function buildGuardianReleaseDispatchCommandResult(input2) {
|
|
17777
|
+
const npmLaneSelected = input2.channels === "all" || input2.channels === "npm";
|
|
17669
17778
|
return {
|
|
17670
17779
|
schema_version: "contextecf/project-guardian-release-dispatch-command/v1",
|
|
17671
17780
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
@@ -17674,13 +17783,21 @@ function buildGuardianReleaseDispatchCommandResult(input2) {
|
|
|
17674
17783
|
channels: input2.channels,
|
|
17675
17784
|
dryRun: input2.dryRun,
|
|
17676
17785
|
runDockerSmoke: input2.runDockerSmoke,
|
|
17786
|
+
publishAuth: input2.publishAuth,
|
|
17787
|
+
browserStorePublish: input2.browserStorePublish,
|
|
17677
17788
|
command: buildGuardianReleaseDispatchCommandFor(input2),
|
|
17678
|
-
...input2.dryRun ? {} : { requiredPublishCredentialName: "NPM_TOKEN" },
|
|
17789
|
+
...input2.dryRun || input2.publishAuth === "trusted_publisher" || !npmLaneSelected ? {} : { requiredPublishCredentialName: "NPM_TOKEN" },
|
|
17790
|
+
...input2.browserStorePublish === "api" && !input2.dryRun ? {
|
|
17791
|
+
requiredBrowserStoreCredentialNames: [...GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES]
|
|
17792
|
+
} : {},
|
|
17679
17793
|
provenance: "github_actions_oidc_supported",
|
|
17680
17794
|
boundaries: [
|
|
17681
17795
|
"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
|
-
"
|
|
17796
|
+
"dry_run=false with publish_auth=token requires the repository secret NPM_TOKEN and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
17797
|
+
"dry_run=false with publish_auth=trusted_publisher requires npm Trusted Publishing to be configured for this GitHub Actions workflow and package.",
|
|
17798
|
+
"browser_store_publish=manual keeps Chrome Web Store upload as an operator ZIP handoff.",
|
|
17799
|
+
"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.",
|
|
17800
|
+
"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
17801
|
"A release-dispatch command is operator guidance, not production-gate proof."
|
|
17685
17802
|
],
|
|
17686
17803
|
tokenPrinted: false,
|
|
@@ -17699,7 +17816,10 @@ function renderGuardianReleaseDispatchCommand(result, flags) {
|
|
|
17699
17816
|
`Workflow: ${result.workflow}`,
|
|
17700
17817
|
`Channels: ${result.channels}`,
|
|
17701
17818
|
`Docker smoke: ${result.runDockerSmoke ? "yes" : "no"}`,
|
|
17819
|
+
`Publish auth: ${result.publishAuth}`,
|
|
17820
|
+
`Browser store publish: ${result.browserStorePublish}`,
|
|
17702
17821
|
result.requiredPublishCredentialName ? `Required repository secret: ${result.requiredPublishCredentialName}` : "Required repository secret: not needed for dry run",
|
|
17822
|
+
result.requiredBrowserStoreCredentialNames ? `Required browser-store secrets: ${result.requiredBrowserStoreCredentialNames.join(", ")}` : "Required browser-store secrets: not needed for manual browser handoff",
|
|
17703
17823
|
"Command:",
|
|
17704
17824
|
result.command,
|
|
17705
17825
|
"Boundaries:",
|
|
@@ -17714,6 +17834,7 @@ function buildGuardianBrowserStoreHandoffCommandResult() {
|
|
|
17714
17834
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
17715
17835
|
status: "chrome_distribution_proven_next_submission_pack_ready",
|
|
17716
17836
|
primaryStore: "chrome_web_store",
|
|
17837
|
+
publicListingUrl: GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL,
|
|
17717
17838
|
productionGate: {
|
|
17718
17839
|
id: "browser-store-distribution",
|
|
17719
17840
|
status: "proven",
|
|
@@ -17780,6 +17901,7 @@ function renderGuardianBrowserStoreHandoffCommand(result, flags) {
|
|
|
17780
17901
|
"Project Guardian Browser Store Handoff",
|
|
17781
17902
|
`Status: ${result.status}`,
|
|
17782
17903
|
`Primary store: ${result.primaryStore}`,
|
|
17904
|
+
`Public listing: ${result.publicListingUrl}`,
|
|
17783
17905
|
`Production gate: ${result.productionGate.id} (${result.productionGate.status})`,
|
|
17784
17906
|
"1. Build submission pack:",
|
|
17785
17907
|
result.commands.submissionPack,
|
|
@@ -17800,15 +17922,23 @@ function renderGuardianBrowserStoreHandoffCommand(result, flags) {
|
|
|
17800
17922
|
`;
|
|
17801
17923
|
}
|
|
17802
17924
|
function buildGuardianReleaseSecretSetup() {
|
|
17925
|
+
const chromeWebStoreSecretMatcher = GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES.join("|");
|
|
17803
17926
|
return {
|
|
17804
17927
|
schema_version: "contextecf/project-guardian-release-secret-setup/v1",
|
|
17805
17928
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
17806
17929
|
repository: GUARDIAN_RELEASE_REPOSITORY,
|
|
17807
17930
|
requiredForPublish: "NPM_TOKEN",
|
|
17931
|
+
requiredForBrowserStoreApi: GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES,
|
|
17808
17932
|
githubActionsSecretUrl: `https://github.com/${GUARDIAN_RELEASE_REPOSITORY}/settings/secrets/actions/new`,
|
|
17809
17933
|
commands: {
|
|
17810
17934
|
setNpmToken: `gh secret set NPM_TOKEN --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17935
|
+
setChromeWebStoreClientId: `gh secret set CHROME_WEB_STORE_CLIENT_ID --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17936
|
+
setChromeWebStoreClientSecret: `gh secret set CHROME_WEB_STORE_CLIENT_SECRET --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17937
|
+
setChromeWebStoreRefreshToken: `gh secret set CHROME_WEB_STORE_REFRESH_TOKEN --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17938
|
+
setChromeWebStorePublisherId: `gh secret set CHROME_WEB_STORE_PUBLISHER_ID --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17939
|
+
setChromeWebStoreExtensionId: `gh secret set CHROME_WEB_STORE_EXTENSION_ID --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions`,
|
|
17811
17940
|
verifyNpmTokenPresence: `gh secret list --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions | rg '^NPM_TOKEN\\b'`,
|
|
17941
|
+
verifyChromeWebStoreSecretsPresence: `gh secret list --repo ${GUARDIAN_RELEASE_REPOSITORY} --app actions | rg '^(${chromeWebStoreSecretMatcher})\\b'`,
|
|
17812
17942
|
dryRunDispatch: buildGuardianReleaseDispatchCommandFor({
|
|
17813
17943
|
dryRun: true,
|
|
17814
17944
|
releaseVersion: "<version>",
|
|
@@ -17820,13 +17950,23 @@ function buildGuardianReleaseSecretSetup() {
|
|
|
17820
17950
|
releaseVersion: "<version>",
|
|
17821
17951
|
channels: "all",
|
|
17822
17952
|
runDockerSmoke: true
|
|
17953
|
+
}),
|
|
17954
|
+
browserStoreApiPublishDispatch: buildGuardianReleaseDispatchCommandFor({
|
|
17955
|
+
dryRun: false,
|
|
17956
|
+
releaseVersion: "<version>",
|
|
17957
|
+
channels: "browser",
|
|
17958
|
+
runDockerSmoke: true,
|
|
17959
|
+
publishAuth: "trusted_publisher",
|
|
17960
|
+
browserStorePublish: "api"
|
|
17823
17961
|
})
|
|
17824
17962
|
},
|
|
17825
17963
|
boundaries: [
|
|
17826
17964
|
"NPM_TOKEN belongs only in GitHub Actions repository secrets for guarded dry_run=false npm publication.",
|
|
17965
|
+
"Chrome Web Store API OAuth values belong only in GitHub Actions repository secrets for guarded browser_store_publish=api upload/submission.",
|
|
17827
17966
|
"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
17967
|
"Use dry_run=true before dry_run=false; dry-run dispatch does not require NPM_TOKEN.",
|
|
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."
|
|
17968
|
+
"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.",
|
|
17969
|
+
"Chrome Web Store API secrets enable browser update upload/submission only; official review approval evidence is still required for production browser-store claims."
|
|
17830
17970
|
],
|
|
17831
17971
|
tokenPrinted: false,
|
|
17832
17972
|
rawContentIncluded: false
|
|
@@ -17840,16 +17980,27 @@ function renderGuardianReleaseSecretSetup(result, flags) {
|
|
|
17840
17980
|
return `${[
|
|
17841
17981
|
"Project Guardian Release Secret Setup",
|
|
17842
17982
|
`Repository: ${result.repository}`,
|
|
17843
|
-
`Required for publish: ${result.requiredForPublish}`,
|
|
17983
|
+
`Required for npm publish: ${result.requiredForPublish}`,
|
|
17984
|
+
`Required for Chrome Web Store API: ${result.requiredForBrowserStoreApi.join(", ")}`,
|
|
17844
17985
|
`GitHub Actions secret page: ${result.githubActionsSecretUrl}`,
|
|
17845
|
-
"
|
|
17986
|
+
"NPM publish secret:",
|
|
17846
17987
|
result.commands.setNpmToken,
|
|
17847
|
-
"Verify secret presence:",
|
|
17988
|
+
"Verify npm secret presence:",
|
|
17848
17989
|
result.commands.verifyNpmTokenPresence,
|
|
17990
|
+
"Chrome Web Store API secrets:",
|
|
17991
|
+
result.commands.setChromeWebStoreClientId,
|
|
17992
|
+
result.commands.setChromeWebStoreClientSecret,
|
|
17993
|
+
result.commands.setChromeWebStoreRefreshToken,
|
|
17994
|
+
result.commands.setChromeWebStorePublisherId,
|
|
17995
|
+
result.commands.setChromeWebStoreExtensionId,
|
|
17996
|
+
"Verify Chrome Web Store API secret presence:",
|
|
17997
|
+
result.commands.verifyChromeWebStoreSecretsPresence,
|
|
17849
17998
|
"Dry-run release command:",
|
|
17850
17999
|
result.commands.dryRunDispatch,
|
|
17851
|
-
"
|
|
18000
|
+
"Default publish command with manual browser-store upload:",
|
|
17852
18001
|
result.commands.publishDispatch,
|
|
18002
|
+
"Browser-store API publish command:",
|
|
18003
|
+
result.commands.browserStoreApiPublishDispatch,
|
|
17853
18004
|
"Boundaries:",
|
|
17854
18005
|
...result.boundaries.map((boundary) => `- ${boundary}`),
|
|
17855
18006
|
"Token printed: no"
|
|
@@ -18070,9 +18221,9 @@ function buildGuardianReleaseVerify(releaseVersion) {
|
|
|
18070
18221
|
commands: {
|
|
18071
18222
|
registryVersion: `npm view ${GUARDIAN_NPM_PACKAGE_NAME}@${releaseVersion} version --registry=${GUARDIAN_NPM_REGISTRY}`,
|
|
18072
18223
|
publicInstall: `npm install -g ${GUARDIAN_NPM_PACKAGE_NAME}@${releaseVersion}`,
|
|
18224
|
+
chromeWebStoreListing: GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL,
|
|
18073
18225
|
installedVersion: "guardian --version",
|
|
18074
18226
|
setup: "guardian setup",
|
|
18075
|
-
launch: "guardian launch",
|
|
18076
18227
|
status: "guardian status",
|
|
18077
18228
|
openControlTower: "guardian open",
|
|
18078
18229
|
copyPasteLocalSmoke,
|
|
@@ -18101,9 +18252,8 @@ function buildGuardianReleasePaste(releaseVersion) {
|
|
|
18101
18252
|
"guardian --version",
|
|
18102
18253
|
"guardian stop",
|
|
18103
18254
|
"guardian setup",
|
|
18104
|
-
"guardian
|
|
18105
|
-
"guardian open"
|
|
18106
|
-
"guardian status"
|
|
18255
|
+
"guardian status",
|
|
18256
|
+
"guardian open"
|
|
18107
18257
|
];
|
|
18108
18258
|
return {
|
|
18109
18259
|
schema_version: "contextecf/project-guardian-release-paste/v1",
|
|
@@ -18114,9 +18264,9 @@ function buildGuardianReleasePaste(releaseVersion) {
|
|
|
18114
18264
|
command: commands.join("\n"),
|
|
18115
18265
|
commands,
|
|
18116
18266
|
boundaries: [
|
|
18117
|
-
"This paste block verifies public npm visibility, global install, first-run setup,
|
|
18267
|
+
"This paste block verifies public npm visibility, global install, stale-process cleanup, first-run setup, Local Guardian status, and Control Tower open.",
|
|
18118
18268
|
"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
|
|
18269
|
+
"guardian stop is included before setup so stale Local Guardian processes or local token mismatches are cleared before setup relaunches the local service."
|
|
18120
18270
|
],
|
|
18121
18271
|
tokenPrinted: false,
|
|
18122
18272
|
rawContentIncluded: false
|
|
@@ -18143,18 +18293,19 @@ function renderGuardianReleaseVerify(result, flags) {
|
|
|
18143
18293
|
`Status: ${result.status}`,
|
|
18144
18294
|
"1. Confirm registry visibility:",
|
|
18145
18295
|
result.commands.registryVersion,
|
|
18146
|
-
"2.
|
|
18296
|
+
"2. Confirm public Chrome Web Store listing:",
|
|
18297
|
+
result.commands.chromeWebStoreListing,
|
|
18298
|
+
"3. Install the published version:",
|
|
18147
18299
|
result.commands.publicInstall,
|
|
18148
|
-
"
|
|
18300
|
+
"4. Confirm installed CLI version:",
|
|
18149
18301
|
result.commands.installedVersion,
|
|
18150
|
-
"
|
|
18302
|
+
"5. Run first-use smoke:",
|
|
18151
18303
|
result.commands.setup,
|
|
18152
|
-
result.commands.launch,
|
|
18153
18304
|
result.commands.status,
|
|
18154
18305
|
result.commands.openControlTower,
|
|
18155
|
-
"
|
|
18306
|
+
"6. Copy-paste local smoke block:",
|
|
18156
18307
|
result.commands.copyPasteLocalSmoke,
|
|
18157
|
-
"
|
|
18308
|
+
"7. Verify release evidence:",
|
|
18158
18309
|
result.commands.publishStatusVerification,
|
|
18159
18310
|
result.commands.finishLineStatus,
|
|
18160
18311
|
"Evidence outputs:",
|
|
@@ -18171,6 +18322,12 @@ function isValidReleaseVersion(value) {
|
|
|
18171
18322
|
function isValidReleaseChannels(value) {
|
|
18172
18323
|
return ["all", "npm", "browser", "evidence"].includes(value);
|
|
18173
18324
|
}
|
|
18325
|
+
function isValidReleasePublishAuth(value) {
|
|
18326
|
+
return value === "token" || value === "trusted_publisher";
|
|
18327
|
+
}
|
|
18328
|
+
function isValidBrowserStorePublish(value) {
|
|
18329
|
+
return value === "manual" || value === "api";
|
|
18330
|
+
}
|
|
18174
18331
|
function renderGuardianProductionReadiness(result, flags) {
|
|
18175
18332
|
if (flags.has("json")) {
|
|
18176
18333
|
return `${JSON.stringify(result, null, 2)}
|
|
@@ -18273,9 +18430,9 @@ async function statusGuardian(flags, options) {
|
|
|
18273
18430
|
`Profile: ${home}`,
|
|
18274
18431
|
`Runtime profile: ${result.runtime.profileDaemonStatus}`,
|
|
18275
18432
|
`Control Tower: ${result.controlTowerUrl ?? "not configured"}`,
|
|
18276
|
-
`
|
|
18277
|
-
result.daemon.error ? `
|
|
18278
|
-
result.daemonVersionMismatch ? `
|
|
18433
|
+
`Local Guardian: ${result.daemon.status}${result.daemon.statusCode ? ` (HTTP ${result.daemon.statusCode})` : ""}`,
|
|
18434
|
+
result.daemon.error ? `Connection detail: ${result.daemon.error}` : void 0,
|
|
18435
|
+
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
18436
|
`MCP stdio: ${result.runtime.mcpStdioAvailable ? "available" : "unavailable"}`,
|
|
18280
18437
|
`Posture Profile: ${result.postureProfile.selected}`,
|
|
18281
18438
|
`Prompt Coach: ${result.preferences.promptCoachMode}`,
|
|
@@ -18382,7 +18539,7 @@ async function runLaunchCommand(flags, options) {
|
|
|
18382
18539
|
nextCommands: [
|
|
18383
18540
|
"guardian open",
|
|
18384
18541
|
"guardian status",
|
|
18385
|
-
"
|
|
18542
|
+
"Open ContextECF Halo and click Connect",
|
|
18386
18543
|
"guardian policy list"
|
|
18387
18544
|
]
|
|
18388
18545
|
};
|
|
@@ -18391,7 +18548,7 @@ async function runLaunchCommand(flags, options) {
|
|
|
18391
18548
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18392
18549
|
` : `${[
|
|
18393
18550
|
"Project Guardian launched.",
|
|
18394
|
-
`
|
|
18551
|
+
`Local Guardian: ${result.daemonUrl}${result.reusedExistingDaemon ? " (already running)" : ""}`,
|
|
18395
18552
|
`Control Tower: ${result.controlTowerUrl}`,
|
|
18396
18553
|
shouldOpen ? `Opening: ${result.controlTowerUrl}` : void 0,
|
|
18397
18554
|
"Next: guardian open; guardian status"
|
|
@@ -18502,14 +18659,14 @@ async function runStartCommand(flags, options) {
|
|
|
18502
18659
|
controlTowerUrl: requestedControlTowerUrl,
|
|
18503
18660
|
healthUrl,
|
|
18504
18661
|
healthStatus: "reachable",
|
|
18505
|
-
nextCommands: ["guardian open", "guardian status", "
|
|
18662
|
+
nextCommands: ["guardian open", "guardian status", "Open ContextECF Halo and click Connect"]
|
|
18506
18663
|
};
|
|
18507
18664
|
write(
|
|
18508
18665
|
stdout,
|
|
18509
18666
|
flags.has("json") ? `${JSON.stringify(result2, null, 2)}
|
|
18510
18667
|
` : `${[
|
|
18511
|
-
"Guardian
|
|
18512
|
-
`
|
|
18668
|
+
"Local Guardian already running.",
|
|
18669
|
+
`Local Guardian: ${requestedDaemonUrl}`,
|
|
18513
18670
|
`Control Tower: ${requestedControlTowerUrl}`,
|
|
18514
18671
|
"Next: guardian open"
|
|
18515
18672
|
].join("\n")}
|
|
@@ -18518,7 +18675,7 @@ async function runStartCommand(flags, options) {
|
|
|
18518
18675
|
return 0;
|
|
18519
18676
|
}
|
|
18520
18677
|
if (!options.startDetachedDaemon) {
|
|
18521
|
-
write(stderr, "Guardian
|
|
18678
|
+
write(stderr, "Guardian local background launcher is unavailable in this runtime.\n");
|
|
18522
18679
|
return 1;
|
|
18523
18680
|
}
|
|
18524
18681
|
const started = await options.startDetachedDaemon({ home, host, port: selected.port });
|
|
@@ -18539,14 +18696,14 @@ async function runStartCommand(flags, options) {
|
|
|
18539
18696
|
pid: started.pid,
|
|
18540
18697
|
healthUrl,
|
|
18541
18698
|
healthStatus: existing.error ? "unreachable" : "not_reachable",
|
|
18542
|
-
nextCommands: ["guardian open", "guardian status", "
|
|
18699
|
+
nextCommands: ["guardian open", "guardian status", "Open ContextECF Halo and click Connect"]
|
|
18543
18700
|
};
|
|
18544
18701
|
write(
|
|
18545
18702
|
stdout,
|
|
18546
18703
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18547
18704
|
` : `${[
|
|
18548
|
-
"Guardian
|
|
18549
|
-
`
|
|
18705
|
+
"Local Guardian start requested.",
|
|
18706
|
+
`Local Guardian: ${daemonUrl}`,
|
|
18550
18707
|
`Control Tower: ${controlTowerUrl}`,
|
|
18551
18708
|
started.pid ? `PID: ${started.pid}` : void 0,
|
|
18552
18709
|
"Next: guardian open"
|
|
@@ -18557,8 +18714,8 @@ async function runStartCommand(flags, options) {
|
|
|
18557
18714
|
}
|
|
18558
18715
|
function renderDaemonTokenMismatchMessage(requestedDaemonUrl, port) {
|
|
18559
18716
|
return [
|
|
18560
|
-
`Guardian found another
|
|
18561
|
-
"This usually means an older Guardian
|
|
18717
|
+
`Guardian found another local service or container at ${requestedDaemonUrl}, but it rejected this profile's local connection token.`,
|
|
18718
|
+
"This usually means an older Guardian background service or Docker demo is still using the port.",
|
|
18562
18719
|
`Recovery: stop the other process/container, or run Guardian on a different port with guardian launch --port=${port + 1}.`,
|
|
18563
18720
|
`Diagnostics: guardian status; docker ps --filter publish=${port}`,
|
|
18564
18721
|
""
|
|
@@ -18591,7 +18748,7 @@ async function runStopCommand(flags, options) {
|
|
|
18591
18748
|
if (!stopped.ok) {
|
|
18592
18749
|
write(
|
|
18593
18750
|
stderr,
|
|
18594
|
-
`Guardian
|
|
18751
|
+
`Local Guardian did not stop cleanly${stopped.statusCode ? ` (HTTP ${stopped.statusCode})` : ""}: ${stopped.error ?? "unknown_error"}
|
|
18595
18752
|
`
|
|
18596
18753
|
);
|
|
18597
18754
|
return 1;
|
|
@@ -18602,12 +18759,12 @@ async function runStopCommand(flags, options) {
|
|
|
18602
18759
|
success: true,
|
|
18603
18760
|
daemonStatus: profile.runtime.daemon_status,
|
|
18604
18761
|
stopUrl: stopUrl.url,
|
|
18605
|
-
note: "Guardian
|
|
18762
|
+
note: "Local Guardian stop requested."
|
|
18606
18763
|
};
|
|
18607
18764
|
write(
|
|
18608
18765
|
stdout,
|
|
18609
18766
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18610
|
-
` : `${["Guardian
|
|
18767
|
+
` : `${["Local Guardian stop requested.", "Status: not_started_mvp"].join("\n")}
|
|
18611
18768
|
`
|
|
18612
18769
|
);
|
|
18613
18770
|
return 0;
|
|
@@ -18679,7 +18836,7 @@ Proof: ${result2.proofPath}
|
|
|
18679
18836
|
}
|
|
18680
18837
|
const host = stringFlag(flags, "host") ?? "127.0.0.1";
|
|
18681
18838
|
if (!isLoopbackHost(host)) {
|
|
18682
|
-
write(stderr, "Guardian service supervision only accepts loopback
|
|
18839
|
+
write(stderr, "Guardian service supervision only accepts loopback Local Guardian hosts.\n");
|
|
18683
18840
|
return 1;
|
|
18684
18841
|
}
|
|
18685
18842
|
const port = numberFlag(flags, "port") ?? 4317;
|
|
@@ -18936,7 +19093,7 @@ async function daemonPair(flags, options) {
|
|
|
18936
19093
|
[GUARDIAN_BRIDGE_CONFIG_STORAGE_KEY]: bridgeConfig,
|
|
18937
19094
|
[GUARDIAN_BRIDGE_TOKEN_STORAGE_KEY]: token
|
|
18938
19095
|
},
|
|
18939
|
-
note: "
|
|
19096
|
+
note: "Advanced fallback only. Most users should run guardian setup, open ContextECF Halo, and click Connect."
|
|
18940
19097
|
};
|
|
18941
19098
|
if (flags.has("json")) {
|
|
18942
19099
|
return { exitCode: 0, message: `${JSON.stringify(pairing, null, 2)}
|
|
@@ -18945,13 +19102,10 @@ async function daemonPair(flags, options) {
|
|
|
18945
19102
|
return {
|
|
18946
19103
|
exitCode: 0,
|
|
18947
19104
|
message: `${[
|
|
18948
|
-
"Guardian browser
|
|
18949
|
-
|
|
18950
|
-
|
|
18951
|
-
|
|
18952
|
-
`Native host: ${pairing.nativeHostName}`,
|
|
18953
|
-
`Token: ${pairing.token}`,
|
|
18954
|
-
pairing.note
|
|
19105
|
+
"Guardian browser connection uses one-click setup now.",
|
|
19106
|
+
"Run guardian connect, then open ContextECF Halo and click Connect.",
|
|
19107
|
+
"Support setup codes are only printed by guardian pair --json.",
|
|
19108
|
+
"Token printed: no"
|
|
18955
19109
|
].join("\n")}
|
|
18956
19110
|
`
|
|
18957
19111
|
};
|
|
@@ -18960,6 +19114,9 @@ async function runExtensionCommand(argv, flags, options) {
|
|
|
18960
19114
|
const subcommand = argv[0] ?? "native-host";
|
|
18961
19115
|
const stdout = options.stdout ?? process.stdout;
|
|
18962
19116
|
const stderr = options.stderr ?? process.stderr;
|
|
19117
|
+
if (subcommand === "connect") {
|
|
19118
|
+
return runExtensionConnectCommand(flags, options);
|
|
19119
|
+
}
|
|
18963
19120
|
if (subcommand === "open-setup") {
|
|
18964
19121
|
return runExtensionOpenSetupCommand(flags, options);
|
|
18965
19122
|
}
|
|
@@ -19147,13 +19304,18 @@ Re-run with --force to replace it.
|
|
|
19147
19304
|
registryMutationRequested: writeRegistry,
|
|
19148
19305
|
registryProofPath,
|
|
19149
19306
|
tokenPrinted: false,
|
|
19150
|
-
nextCommands: [
|
|
19307
|
+
nextCommands: [
|
|
19308
|
+
"guardian launch",
|
|
19309
|
+
"Open ContextECF Halo and click Connect",
|
|
19310
|
+
"guardian extension native-host status"
|
|
19311
|
+
],
|
|
19312
|
+
advancedCommands: ["guardian pair --json"]
|
|
19151
19313
|
};
|
|
19152
19314
|
write(
|
|
19153
19315
|
stdout,
|
|
19154
19316
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
19155
19317
|
` : `${[
|
|
19156
|
-
"Guardian
|
|
19318
|
+
"Guardian browser connection installed.",
|
|
19157
19319
|
`Browser: ${browser}`,
|
|
19158
19320
|
`Host: ${plan.hostName}`,
|
|
19159
19321
|
`Manifest: ${plan.manifestPath}`,
|
|
@@ -19168,6 +19330,121 @@ Re-run with --force to replace it.
|
|
|
19168
19330
|
);
|
|
19169
19331
|
return 0;
|
|
19170
19332
|
}
|
|
19333
|
+
async function runExtensionConnectCommand(flags, options) {
|
|
19334
|
+
const stdout = options.stdout ?? process.stdout;
|
|
19335
|
+
const stderr = options.stderr ?? process.stderr;
|
|
19336
|
+
const requestedExtensionId = stringFlag(flags, "extension-id") ?? stringFlag(flags, "id");
|
|
19337
|
+
const extensionId = requestedExtensionId ?? GUARDIAN_HALO_CHROME_EXTENSION_ID;
|
|
19338
|
+
if (requestedExtensionId && !isValidChromeExtensionId(requestedExtensionId)) {
|
|
19339
|
+
write(
|
|
19340
|
+
stderr,
|
|
19341
|
+
"Invalid --extension-id. Chrome extension ids must be 32 lowercase letters from a-p.\n"
|
|
19342
|
+
);
|
|
19343
|
+
return 1;
|
|
19344
|
+
}
|
|
19345
|
+
const install = JSON.parse(await installGuardian(/* @__PURE__ */ new Map([["json", true]]), options));
|
|
19346
|
+
let launchOutput = "";
|
|
19347
|
+
const launchFlags = new Map([
|
|
19348
|
+
["json", true],
|
|
19349
|
+
["print", true],
|
|
19350
|
+
...copyStringFlags(flags, ["host", "port"])
|
|
19351
|
+
]);
|
|
19352
|
+
const launchExitCode = await runLaunchCommand(launchFlags, {
|
|
19353
|
+
...options,
|
|
19354
|
+
stdout: {
|
|
19355
|
+
write: (chunk) => {
|
|
19356
|
+
launchOutput += chunk.toString();
|
|
19357
|
+
return true;
|
|
19358
|
+
}
|
|
19359
|
+
},
|
|
19360
|
+
stderr
|
|
19361
|
+
});
|
|
19362
|
+
if (launchExitCode !== 0) {
|
|
19363
|
+
return launchExitCode;
|
|
19364
|
+
}
|
|
19365
|
+
const launch = JSON.parse(launchOutput);
|
|
19366
|
+
const helperFlags = new Map([
|
|
19367
|
+
["json", true],
|
|
19368
|
+
["force", true],
|
|
19369
|
+
["extension-id", extensionId],
|
|
19370
|
+
...copyStringFlags(flags, ["browser", "target"])
|
|
19371
|
+
]);
|
|
19372
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
19373
|
+
helperFlags.set("write-registry", true);
|
|
19374
|
+
helperFlags.set("yes", true);
|
|
19375
|
+
}
|
|
19376
|
+
let helperOutput = "";
|
|
19377
|
+
let helperError = "";
|
|
19378
|
+
const helperExitCode = await runExtensionCommand(["native-host", "install"], helperFlags, {
|
|
19379
|
+
...options,
|
|
19380
|
+
stdout: {
|
|
19381
|
+
write: (chunk) => {
|
|
19382
|
+
helperOutput += chunk.toString();
|
|
19383
|
+
return true;
|
|
19384
|
+
}
|
|
19385
|
+
},
|
|
19386
|
+
stderr: {
|
|
19387
|
+
write: (chunk) => {
|
|
19388
|
+
helperError += chunk.toString();
|
|
19389
|
+
return true;
|
|
19390
|
+
}
|
|
19391
|
+
}
|
|
19392
|
+
});
|
|
19393
|
+
if (helperExitCode !== 0) {
|
|
19394
|
+
write(
|
|
19395
|
+
stderr,
|
|
19396
|
+
helperError.trim() ? `${helperError.trim()}
|
|
19397
|
+
` : "Guardian could not prepare the browser connection. Run guardian setup, then try again.\n"
|
|
19398
|
+
);
|
|
19399
|
+
return helperExitCode;
|
|
19400
|
+
}
|
|
19401
|
+
const helper = JSON.parse(helperOutput);
|
|
19402
|
+
const result = {
|
|
19403
|
+
schema_version: "contextecf/project-guardian-browser-connect/v1",
|
|
19404
|
+
success: true,
|
|
19405
|
+
extensionId,
|
|
19406
|
+
defaultExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
19407
|
+
install: {
|
|
19408
|
+
profileDir: install.profileDir,
|
|
19409
|
+
tokenCreated: install.tokenCreated,
|
|
19410
|
+
tokenPrinted: false
|
|
19411
|
+
},
|
|
19412
|
+
localGuardian: {
|
|
19413
|
+
running: true,
|
|
19414
|
+
opened: launch.opened,
|
|
19415
|
+
reusedExistingDaemon: launch.reusedExistingDaemon,
|
|
19416
|
+
daemonUrl: launch.daemonUrl,
|
|
19417
|
+
controlTowerUrl: launch.controlTowerUrl,
|
|
19418
|
+
pid: launch.pid,
|
|
19419
|
+
healthStatus: launch.healthStatus
|
|
19420
|
+
},
|
|
19421
|
+
browser: helper.browser,
|
|
19422
|
+
helperInstalled: true,
|
|
19423
|
+
helperStatus: {
|
|
19424
|
+
hostName: helper.hostName,
|
|
19425
|
+
manifestPath: helper.manifestPath,
|
|
19426
|
+
allowedOrigins: helper.allowedOrigins,
|
|
19427
|
+
tokenPrinted: helper.tokenPrinted
|
|
19428
|
+
},
|
|
19429
|
+
tokenPrinted: false,
|
|
19430
|
+
nextCommands: ["Open ContextECF Halo and click Connect", "guardian open", "guardian status"],
|
|
19431
|
+
supportCommands: ["guardian extension open-setup --print", "guardian pair --json"]
|
|
19432
|
+
};
|
|
19433
|
+
write(
|
|
19434
|
+
stdout,
|
|
19435
|
+
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
19436
|
+
` : `${[
|
|
19437
|
+
"Guardian browser connection is ready.",
|
|
19438
|
+
`Local Guardian: ${result.localGuardian.daemonUrl}${result.localGuardian.reusedExistingDaemon ? " (already running)" : ""}`,
|
|
19439
|
+
`Browser: ${result.browser}`,
|
|
19440
|
+
"Next: open ContextECF Halo and click Connect.",
|
|
19441
|
+
"No setup code needed.",
|
|
19442
|
+
"Token printed: no"
|
|
19443
|
+
].join("\n")}
|
|
19444
|
+
`
|
|
19445
|
+
);
|
|
19446
|
+
return 0;
|
|
19447
|
+
}
|
|
19171
19448
|
async function runExtensionOpenSetupCommand(flags, options) {
|
|
19172
19449
|
const stdout = options.stdout ?? process.stdout;
|
|
19173
19450
|
const stderr = options.stderr ?? process.stderr;
|
|
@@ -19177,8 +19454,9 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19177
19454
|
write(stderr, "Guardian is not installed. Run guardian install first.\n");
|
|
19178
19455
|
return 1;
|
|
19179
19456
|
}
|
|
19180
|
-
const
|
|
19181
|
-
|
|
19457
|
+
const requestedExtensionId = stringFlag(flags, "extension-id") ?? stringFlag(flags, "id");
|
|
19458
|
+
const extensionId = requestedExtensionId ?? GUARDIAN_HALO_CHROME_EXTENSION_ID;
|
|
19459
|
+
if (requestedExtensionId && !isValidChromeExtensionId(requestedExtensionId)) {
|
|
19182
19460
|
write(
|
|
19183
19461
|
stderr,
|
|
19184
19462
|
"Invalid --extension-id. Chrome extension ids must be 32 lowercase letters from a-p.\n"
|
|
@@ -19192,22 +19470,24 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19192
19470
|
return 1;
|
|
19193
19471
|
}
|
|
19194
19472
|
const setupUrl = buildControlTowerSetupUrl(resolved.url);
|
|
19195
|
-
const nativeHostCommand = `guardian extension native-host install --extension-id=${extensionId
|
|
19473
|
+
const nativeHostCommand = `guardian extension native-host install --extension-id=${extensionId}`;
|
|
19196
19474
|
const result = {
|
|
19197
19475
|
schema_version: "contextecf/project-guardian-extension-setup/v1",
|
|
19198
19476
|
success: true,
|
|
19199
19477
|
opened: !flags.has("print"),
|
|
19200
19478
|
controlTowerUrl: resolved.url,
|
|
19201
19479
|
setupUrl,
|
|
19202
|
-
extensionId
|
|
19480
|
+
extensionId,
|
|
19481
|
+
defaultExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
19203
19482
|
tokenPrinted: false,
|
|
19204
19483
|
nextCommands: [
|
|
19205
19484
|
"guardian launch",
|
|
19206
|
-
"guardian daemon pair --json",
|
|
19207
19485
|
nativeHostCommand,
|
|
19208
|
-
"guardian extension native-host status"
|
|
19486
|
+
"guardian extension native-host status",
|
|
19487
|
+
"Open ContextECF Halo and click Connect"
|
|
19209
19488
|
],
|
|
19210
|
-
|
|
19489
|
+
advancedCommands: ["guardian pair --json"],
|
|
19490
|
+
note: extensionId ? "Guardian browser setup opened for ContextECF Halo." : "Guardian browser setup opened."
|
|
19211
19491
|
};
|
|
19212
19492
|
if (result.opened) {
|
|
19213
19493
|
const opener = options.openUrl ?? ((url2) => openUrlInDefaultBrowser(url2, options));
|
|
@@ -19219,7 +19499,8 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19219
19499
|
` : `${[
|
|
19220
19500
|
result.opened ? `Opening Guardian browser setup: ${setupUrl}` : `Guardian browser setup: ${setupUrl}`,
|
|
19221
19501
|
"Token printed: no",
|
|
19222
|
-
`Next: ${result.nextCommands.join("; ")}
|
|
19502
|
+
`Next: ${result.nextCommands.join("; ")}`,
|
|
19503
|
+
`Advanced fallback: ${result.advancedCommands.join("; ")}`
|
|
19223
19504
|
].join("\n")}
|
|
19224
19505
|
`
|
|
19225
19506
|
);
|
|
@@ -19746,7 +20027,7 @@ function buildGuardianServiceArtifact(input2) {
|
|
|
19746
20027
|
if (input2.platform === "systemd") {
|
|
19747
20028
|
return `${[
|
|
19748
20029
|
"[Unit]",
|
|
19749
|
-
"Description=Project Guardian
|
|
20030
|
+
"Description=Project Guardian Local Guardian background service",
|
|
19750
20031
|
"After=network.target",
|
|
19751
20032
|
"",
|
|
19752
20033
|
"[Service]",
|
|
@@ -19765,7 +20046,7 @@ function buildGuardianServiceArtifact(input2) {
|
|
|
19765
20046
|
'<?xml version="1.0" encoding="UTF-16"?>',
|
|
19766
20047
|
'<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">',
|
|
19767
20048
|
" <RegistrationInfo>",
|
|
19768
|
-
" <Description>Project Guardian
|
|
20049
|
+
" <Description>Project Guardian Local Guardian background service</Description>",
|
|
19769
20050
|
" </RegistrationInfo>",
|
|
19770
20051
|
" <Triggers>",
|
|
19771
20052
|
" <LogonTrigger><Enabled>true</Enabled></LogonTrigger>",
|
|
@@ -20747,6 +21028,59 @@ async function runReceiptsCommand(argv, flags, options) {
|
|
|
20747
21028
|
write(stdout, await verifyReceipts(flags, options));
|
|
20748
21029
|
return 0;
|
|
20749
21030
|
}
|
|
21031
|
+
async function runCoachCommand(argv, flags, options) {
|
|
21032
|
+
const subcommand = argv[0] ?? "options";
|
|
21033
|
+
const stdout = options.stdout ?? process.stdout;
|
|
21034
|
+
const stderr = options.stderr ?? process.stderr;
|
|
21035
|
+
if (subcommand === "options" || subcommand === "catalog") {
|
|
21036
|
+
write(stdout, renderGuardianPromptCardOptions(buildGuardianPromptCardOptions(), flags));
|
|
21037
|
+
return 0;
|
|
21038
|
+
}
|
|
21039
|
+
write(stderr, `Unknown Guardian coach command: ${subcommand}
|
|
21040
|
+
`);
|
|
21041
|
+
return 1;
|
|
21042
|
+
}
|
|
21043
|
+
function buildGuardianPromptCardOptions() {
|
|
21044
|
+
return {
|
|
21045
|
+
schema_version: "contextecf/project-guardian-prompt-card-options/v1",
|
|
21046
|
+
cli_version: GUARDIAN_CLI_VERSION,
|
|
21047
|
+
status: "catalog_ready",
|
|
21048
|
+
summary: "Guardian prompt cards keep the user in control: warnings and safer rewrites can be sent as-is, while hard blocks cannot.",
|
|
21049
|
+
catalog: GUARDIAN_PROMPT_CARD_OPTIONS_CATALOG,
|
|
21050
|
+
rules: [
|
|
21051
|
+
"Show a clear reason before asking the user to decide.",
|
|
21052
|
+
"Offer Send as-is for warnings and safer rewrites when policy allows override.",
|
|
21053
|
+
"Never offer Send as-is for hard blocks or unavailable Local Guardian checks.",
|
|
21054
|
+
"Keep Edit prompt available as the safe path for every visible send-time card.",
|
|
21055
|
+
"Do not store raw sensitive prompt text in this catalog or CLI output."
|
|
21056
|
+
],
|
|
21057
|
+
tokenPrinted: false,
|
|
21058
|
+
rawContentIncluded: false
|
|
21059
|
+
};
|
|
21060
|
+
}
|
|
21061
|
+
function renderGuardianPromptCardOptions(result, flags) {
|
|
21062
|
+
if (flags.has("json")) {
|
|
21063
|
+
return `${JSON.stringify(result, null, 2)}
|
|
21064
|
+
`;
|
|
21065
|
+
}
|
|
21066
|
+
return `${[
|
|
21067
|
+
"Guardian Prompt Card Options",
|
|
21068
|
+
result.summary,
|
|
21069
|
+
"",
|
|
21070
|
+
"Programmed cards:",
|
|
21071
|
+
...result.catalog.map((entry) => {
|
|
21072
|
+
const visibleActions = entry.actions.filter((action) => action.visibleToUser).map((action) => action.label);
|
|
21073
|
+
const labels = visibleActions.length > 0 ? visibleActions.join(", ") : "Prompt sends automatically";
|
|
21074
|
+
return `- ${entry.cardStyle}: ${labels}`;
|
|
21075
|
+
}),
|
|
21076
|
+
"",
|
|
21077
|
+
"Rules:",
|
|
21078
|
+
...result.rules.map((rule) => `- ${rule}`),
|
|
21079
|
+
"Token printed: no",
|
|
21080
|
+
"Raw prompt content included: no"
|
|
21081
|
+
].join("\n")}
|
|
21082
|
+
`;
|
|
21083
|
+
}
|
|
20750
21084
|
async function runPolicyCommand(argv, flags, options) {
|
|
20751
21085
|
const subcommand = argv[0] ?? "list";
|
|
20752
21086
|
const stdout = options.stdout ?? process.stdout;
|
|
@@ -23166,13 +23500,13 @@ function openUrlInDefaultBrowser(url2, options = {}) {
|
|
|
23166
23500
|
});
|
|
23167
23501
|
});
|
|
23168
23502
|
}
|
|
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;
|
|
23503
|
+
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_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
23504
|
var init_runtime = __esm({
|
|
23171
23505
|
"packages/guardian-cli/src/runtime.ts"() {
|
|
23172
23506
|
"use strict";
|
|
23173
23507
|
init_src();
|
|
23174
23508
|
init_src2();
|
|
23175
|
-
GUARDIAN_CLI_VERSION = "0.1.
|
|
23509
|
+
GUARDIAN_CLI_VERSION = "0.1.8";
|
|
23176
23510
|
GUARDIAN_PROFILE_SCHEMA_VERSION = "contextecf/project-guardian-profile/v1";
|
|
23177
23511
|
GUARDIAN_NPM_PACKAGE_NAME = "@contextecf/guardian-cli";
|
|
23178
23512
|
GUARDIAN_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
@@ -23180,8 +23514,17 @@ var init_runtime = __esm({
|
|
|
23180
23514
|
GUARDIAN_NPX_SETUP_COMMAND = `npx ${GUARDIAN_NPM_PACKAGE_NAME}@latest setup`;
|
|
23181
23515
|
GUARDIAN_FIRST_RUN_COMMAND = "guardian setup";
|
|
23182
23516
|
GUARDIAN_OPEN_CONTROL_TOWER_COMMAND = "guardian open";
|
|
23517
|
+
GUARDIAN_HALO_CHROME_EXTENSION_ID = "dopkdppbiakhbglhgjfacfdhpheejkck";
|
|
23518
|
+
GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL = `https://chromewebstore.google.com/detail/contextecf-halo/${GUARDIAN_HALO_CHROME_EXTENSION_ID}`;
|
|
23183
23519
|
GUARDIAN_RELEASE_DISPATCH_WORKFLOW = "guardian-release-dispatch.yml";
|
|
23184
23520
|
GUARDIAN_RELEASE_REPOSITORY = "Intelligent-Context-AI-Inc/ContextECF-GITHUB";
|
|
23521
|
+
GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES = [
|
|
23522
|
+
"CHROME_WEB_STORE_CLIENT_ID",
|
|
23523
|
+
"CHROME_WEB_STORE_CLIENT_SECRET",
|
|
23524
|
+
"CHROME_WEB_STORE_REFRESH_TOKEN",
|
|
23525
|
+
"CHROME_WEB_STORE_PUBLISHER_ID",
|
|
23526
|
+
"CHROME_WEB_STORE_EXTENSION_ID"
|
|
23527
|
+
];
|
|
23185
23528
|
GUARDIAN_DEFAULT_CONTROL_TOWER_URL = "http://127.0.0.1:4317/control-tower";
|
|
23186
23529
|
GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ENV = "GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY";
|
|
23187
23530
|
GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ID_ENV = "GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ID";
|
|
@@ -23197,6 +23540,195 @@ var init_runtime = __esm({
|
|
|
23197
23540
|
PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION2 = "contextecf/personal-fabric-contracts/v1";
|
|
23198
23541
|
PROMPT_COACH_MODES = ["off", "quiet", "balanced", "hands_on"];
|
|
23199
23542
|
DEFAULT_PROMPT_COACH_DAILY_SOFT_CARD_LIMIT = 6;
|
|
23543
|
+
GUARDIAN_PROMPT_CARD_OPTIONS_CATALOG = [
|
|
23544
|
+
{
|
|
23545
|
+
disposition: "allow",
|
|
23546
|
+
cardStyle: "No visible card",
|
|
23547
|
+
whenShown: "Local Guardian approves the prompt before it leaves the browser.",
|
|
23548
|
+
actions: [
|
|
23549
|
+
{
|
|
23550
|
+
id: "auto-send",
|
|
23551
|
+
label: "Send prompt",
|
|
23552
|
+
role: "none",
|
|
23553
|
+
sendsPrompt: true,
|
|
23554
|
+
sendsRevisedPrompt: false,
|
|
23555
|
+
requiresConfirmation: false,
|
|
23556
|
+
visibleToUser: false
|
|
23557
|
+
}
|
|
23558
|
+
],
|
|
23559
|
+
sendAsIsAllowed: true,
|
|
23560
|
+
hardBlock: false,
|
|
23561
|
+
receiptExpected: true
|
|
23562
|
+
},
|
|
23563
|
+
{
|
|
23564
|
+
disposition: "warn",
|
|
23565
|
+
cardStyle: "Quick check",
|
|
23566
|
+
whenShown: "Guardian found a low or medium-risk concern, but policy still allows the user to continue.",
|
|
23567
|
+
actions: [
|
|
23568
|
+
{
|
|
23569
|
+
id: "send-as-is",
|
|
23570
|
+
label: "Send as-is",
|
|
23571
|
+
role: "allowed_override",
|
|
23572
|
+
sendsPrompt: true,
|
|
23573
|
+
sendsRevisedPrompt: false,
|
|
23574
|
+
requiresConfirmation: false,
|
|
23575
|
+
visibleToUser: true
|
|
23576
|
+
},
|
|
23577
|
+
{
|
|
23578
|
+
id: "edit-prompt",
|
|
23579
|
+
label: "Edit prompt",
|
|
23580
|
+
role: "safe_escape",
|
|
23581
|
+
sendsPrompt: false,
|
|
23582
|
+
sendsRevisedPrompt: false,
|
|
23583
|
+
requiresConfirmation: false,
|
|
23584
|
+
visibleToUser: true
|
|
23585
|
+
}
|
|
23586
|
+
],
|
|
23587
|
+
sendAsIsAllowed: true,
|
|
23588
|
+
hardBlock: false,
|
|
23589
|
+
receiptExpected: true
|
|
23590
|
+
},
|
|
23591
|
+
{
|
|
23592
|
+
disposition: "rewrite",
|
|
23593
|
+
cardStyle: "Safer version ready",
|
|
23594
|
+
whenShown: "Guardian prepared a safer prompt, usually by masking sensitive text or narrowing scope.",
|
|
23595
|
+
actions: [
|
|
23596
|
+
{
|
|
23597
|
+
id: "send-revised-prompt",
|
|
23598
|
+
label: "Send revised prompt",
|
|
23599
|
+
role: "recommended",
|
|
23600
|
+
sendsPrompt: true,
|
|
23601
|
+
sendsRevisedPrompt: true,
|
|
23602
|
+
requiresConfirmation: false,
|
|
23603
|
+
visibleToUser: true
|
|
23604
|
+
},
|
|
23605
|
+
{
|
|
23606
|
+
id: "send-as-is",
|
|
23607
|
+
label: "Send as-is",
|
|
23608
|
+
role: "allowed_override",
|
|
23609
|
+
sendsPrompt: true,
|
|
23610
|
+
sendsRevisedPrompt: false,
|
|
23611
|
+
requiresConfirmation: false,
|
|
23612
|
+
visibleToUser: true
|
|
23613
|
+
},
|
|
23614
|
+
{
|
|
23615
|
+
id: "edit-prompt",
|
|
23616
|
+
label: "Edit prompt",
|
|
23617
|
+
role: "safe_escape",
|
|
23618
|
+
sendsPrompt: false,
|
|
23619
|
+
sendsRevisedPrompt: false,
|
|
23620
|
+
requiresConfirmation: false,
|
|
23621
|
+
visibleToUser: true
|
|
23622
|
+
}
|
|
23623
|
+
],
|
|
23624
|
+
sendAsIsAllowed: true,
|
|
23625
|
+
hardBlock: false,
|
|
23626
|
+
receiptExpected: true
|
|
23627
|
+
},
|
|
23628
|
+
{
|
|
23629
|
+
disposition: "confirm",
|
|
23630
|
+
cardStyle: "Review needed",
|
|
23631
|
+
whenShown: "Guardian needs deliberate approval before sending higher-risk content or action requests.",
|
|
23632
|
+
actions: [
|
|
23633
|
+
{
|
|
23634
|
+
id: "approve-and-send",
|
|
23635
|
+
label: "Approve and send",
|
|
23636
|
+
role: "recommended",
|
|
23637
|
+
sendsPrompt: true,
|
|
23638
|
+
sendsRevisedPrompt: false,
|
|
23639
|
+
requiresConfirmation: true,
|
|
23640
|
+
visibleToUser: true
|
|
23641
|
+
},
|
|
23642
|
+
{
|
|
23643
|
+
id: "edit-prompt",
|
|
23644
|
+
label: "Edit prompt",
|
|
23645
|
+
role: "safe_escape",
|
|
23646
|
+
sendsPrompt: false,
|
|
23647
|
+
sendsRevisedPrompt: false,
|
|
23648
|
+
requiresConfirmation: false,
|
|
23649
|
+
visibleToUser: true
|
|
23650
|
+
}
|
|
23651
|
+
],
|
|
23652
|
+
sendAsIsAllowed: false,
|
|
23653
|
+
hardBlock: false,
|
|
23654
|
+
receiptExpected: true
|
|
23655
|
+
},
|
|
23656
|
+
{
|
|
23657
|
+
disposition: "block",
|
|
23658
|
+
cardStyle: "Blocked",
|
|
23659
|
+
whenShown: "The prompt violates policy, contains protected secrets, or targets a denied destination.",
|
|
23660
|
+
actions: [
|
|
23661
|
+
{
|
|
23662
|
+
id: "edit-prompt",
|
|
23663
|
+
label: "Edit prompt",
|
|
23664
|
+
role: "safe_escape",
|
|
23665
|
+
sendsPrompt: false,
|
|
23666
|
+
sendsRevisedPrompt: false,
|
|
23667
|
+
requiresConfirmation: false,
|
|
23668
|
+
visibleToUser: true
|
|
23669
|
+
}
|
|
23670
|
+
],
|
|
23671
|
+
sendAsIsAllowed: false,
|
|
23672
|
+
hardBlock: true,
|
|
23673
|
+
receiptExpected: true
|
|
23674
|
+
},
|
|
23675
|
+
{
|
|
23676
|
+
disposition: "unavailable",
|
|
23677
|
+
cardStyle: "Setup needed",
|
|
23678
|
+
whenShown: "The browser cannot reach Local Guardian, so the prompt is held back.",
|
|
23679
|
+
actions: [
|
|
23680
|
+
{
|
|
23681
|
+
id: "edit-prompt",
|
|
23682
|
+
label: "Edit prompt",
|
|
23683
|
+
role: "safe_escape",
|
|
23684
|
+
sendsPrompt: false,
|
|
23685
|
+
sendsRevisedPrompt: false,
|
|
23686
|
+
requiresConfirmation: false,
|
|
23687
|
+
visibleToUser: true
|
|
23688
|
+
}
|
|
23689
|
+
],
|
|
23690
|
+
sendAsIsAllowed: false,
|
|
23691
|
+
hardBlock: false,
|
|
23692
|
+
receiptExpected: false
|
|
23693
|
+
},
|
|
23694
|
+
{
|
|
23695
|
+
disposition: "live_sensitive_warning",
|
|
23696
|
+
cardStyle: "Early heads-up",
|
|
23697
|
+
whenShown: "Guardian notices sensitive text while the user is typing, before the formal send-time check.",
|
|
23698
|
+
actions: [
|
|
23699
|
+
{
|
|
23700
|
+
id: "use-placeholder",
|
|
23701
|
+
label: "Use placeholder",
|
|
23702
|
+
role: "recommended",
|
|
23703
|
+
sendsPrompt: false,
|
|
23704
|
+
sendsRevisedPrompt: true,
|
|
23705
|
+
requiresConfirmation: false,
|
|
23706
|
+
visibleToUser: true
|
|
23707
|
+
},
|
|
23708
|
+
{
|
|
23709
|
+
id: "send-as-is",
|
|
23710
|
+
label: "Send as-is",
|
|
23711
|
+
role: "allowed_override",
|
|
23712
|
+
sendsPrompt: true,
|
|
23713
|
+
sendsRevisedPrompt: false,
|
|
23714
|
+
requiresConfirmation: false,
|
|
23715
|
+
visibleToUser: true
|
|
23716
|
+
},
|
|
23717
|
+
{
|
|
23718
|
+
id: "keep-drafting",
|
|
23719
|
+
label: "Keep drafting",
|
|
23720
|
+
role: "safe_escape",
|
|
23721
|
+
sendsPrompt: false,
|
|
23722
|
+
sendsRevisedPrompt: false,
|
|
23723
|
+
requiresConfirmation: false,
|
|
23724
|
+
visibleToUser: true
|
|
23725
|
+
}
|
|
23726
|
+
],
|
|
23727
|
+
sendAsIsAllowed: true,
|
|
23728
|
+
hardBlock: false,
|
|
23729
|
+
receiptExpected: false
|
|
23730
|
+
}
|
|
23731
|
+
];
|
|
23200
23732
|
GUARDIAN_POSTURE_PROFILE_IDS = [
|
|
23201
23733
|
"calm",
|
|
23202
23734
|
"balanced",
|
|
@@ -23689,10 +24221,49 @@ Open Control Tower:
|
|
|
23689
24221
|
${GUARDIAN_OPEN_CONTROL_TOWER_COMMAND}
|
|
23690
24222
|
start aliases: guardian launch, guardian tower, guardian control-tower
|
|
23691
24223
|
|
|
24224
|
+
Everyday commands:
|
|
24225
|
+
guardian --version
|
|
24226
|
+
guardian setup
|
|
24227
|
+
guardian launch
|
|
24228
|
+
guardian open
|
|
24229
|
+
guardian status
|
|
24230
|
+
guardian doctor
|
|
24231
|
+
guardian connect
|
|
24232
|
+
guardian coach options
|
|
24233
|
+
guardian policy list
|
|
24234
|
+
guardian marketplace list
|
|
24235
|
+
guardian posture list
|
|
24236
|
+
guardian privacy show
|
|
24237
|
+
|
|
24238
|
+
Recommended first run:
|
|
24239
|
+
${GUARDIAN_GLOBAL_INSTALL_COMMAND}
|
|
24240
|
+
guardian setup
|
|
24241
|
+
Open ContextECF Halo and click Connect.
|
|
24242
|
+
guardian open
|
|
24243
|
+
|
|
24244
|
+
Support and developer commands:
|
|
24245
|
+
guardian help --advanced
|
|
24246
|
+
|
|
24247
|
+
Defaults are local-first: raw capture, cloud sync, learning graph, and app autopilot stay off.`;
|
|
24248
|
+
ADVANCED_HELP_TEXT = `guardian \u2014 Project Guardian Personal Context Fabric
|
|
24249
|
+
|
|
24250
|
+
Global install:
|
|
24251
|
+
${GUARDIAN_GLOBAL_INSTALL_COMMAND}
|
|
24252
|
+
|
|
24253
|
+
First run:
|
|
24254
|
+
${GUARDIAN_FIRST_RUN_COMMAND}
|
|
24255
|
+
|
|
24256
|
+
No global install:
|
|
24257
|
+
${GUARDIAN_NPX_SETUP_COMMAND}
|
|
24258
|
+
|
|
24259
|
+
Open Control Tower:
|
|
24260
|
+
${GUARDIAN_OPEN_CONTROL_TOWER_COMMAND}
|
|
24261
|
+
start aliases: guardian launch, guardian tower, guardian control-tower
|
|
24262
|
+
|
|
23692
24263
|
Usage:
|
|
23693
24264
|
guardian --version
|
|
23694
24265
|
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>]
|
|
24266
|
+
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
24267
|
guardian install [--json]
|
|
23697
24268
|
guardian doctor [--json]
|
|
23698
24269
|
guardian readiness [--json] [--strict]
|
|
@@ -23700,7 +24271,7 @@ Usage:
|
|
|
23700
24271
|
guardian release flow --version=<semver> [--json]
|
|
23701
24272
|
guardian release verify --version=<semver> [--json]
|
|
23702
24273
|
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]
|
|
24274
|
+
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
24275
|
guardian release secrets [--json]
|
|
23705
24276
|
guardian release browser-store [--json]
|
|
23706
24277
|
guardian open [--json] [--print] [--url=http://127.0.0.1:<port>]
|
|
@@ -23708,6 +24279,8 @@ Usage:
|
|
|
23708
24279
|
guardian start [--json] [--host=127.0.0.1] [--port=4317]
|
|
23709
24280
|
guardian launch|tower|control-tower [--json] [--open] [--print] [--host=127.0.0.1] [--port=4317]
|
|
23710
24281
|
guardian stop [--json] [--url=http://127.0.0.1:<port>/control-tower]
|
|
24282
|
+
guardian pair [--json]
|
|
24283
|
+
guardian connect [--json] [--browser=chrome|chromium|edge] [--extension-id=<chrome-extension-id>] [--host=127.0.0.1] [--port=4317]
|
|
23711
24284
|
guardian service install [--json] [--platform=auto|launchd|systemd|windows-task-scheduler] [--force]
|
|
23712
24285
|
guardian service status [--json]
|
|
23713
24286
|
guardian service uninstall [--yes] [--json]
|
|
@@ -23716,6 +24289,7 @@ Usage:
|
|
|
23716
24289
|
guardian key-store uninstall [--yes] [--json]
|
|
23717
24290
|
guardian daemon serve [--host=127.0.0.1] [--port=4317]
|
|
23718
24291
|
guardian daemon pair [--json]
|
|
24292
|
+
guardian extension connect [--json] [--browser=chrome|chromium|edge] [--extension-id=<chrome-extension-id>]
|
|
23719
24293
|
guardian extension open-setup [--json] [--print] [--url=http://127.0.0.1:<port>/control-tower] [--extension-id=<chrome-extension-id>]
|
|
23720
24294
|
guardian extension native-host install --extension-id=<chrome-extension-id> [--browser=chrome|chromium|edge] [--target=/path/to/manifest] [--force] [--write-registry --yes] [--json]
|
|
23721
24295
|
guardian extension native-host status [--browser=chrome|chromium|edge] [--target=/path/to/manifest] [--json]
|
|
@@ -23731,6 +24305,7 @@ Usage:
|
|
|
23731
24305
|
guardian privacy mode on [--json]
|
|
23732
24306
|
guardian mcp install [--json] [--client=generic|codex|claude-desktop|cursor|all] [--write] [--target=/path/to/config] [--force]
|
|
23733
24307
|
guardian mcp serve
|
|
24308
|
+
guardian coach options [--json]
|
|
23734
24309
|
guardian policy list [--json]
|
|
23735
24310
|
guardian policy enable <pack-id> [--json]
|
|
23736
24311
|
guardian policy disable <pack-id> [--json]
|
|
@@ -24067,11 +24642,12 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24067
24642
|
return void 0;
|
|
24068
24643
|
}
|
|
24069
24644
|
const fullSsnRisk = hasFullSsnRisk(riskReport.matched_policy_refs);
|
|
24645
|
+
const riskCopy = buildPromptCoachRiskCopy(riskReport, fullSsnRisk);
|
|
24070
24646
|
const card = {
|
|
24071
24647
|
card_id: `coach:${riskReport.risk_report_id}`,
|
|
24072
24648
|
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" ?
|
|
24649
|
+
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,
|
|
24650
|
+
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
24651
|
recommended_choice_id: disposition === "confirm" ? "confirm-after-review" : coachDecision.intervention_level === "soft_tag_along" ? "add-context" : "use-guardian-version",
|
|
24076
24652
|
choices: disposition === "confirm" ? [
|
|
24077
24653
|
{
|
|
@@ -24096,8 +24672,8 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24096
24672
|
resulting_disposition: "warn"
|
|
24097
24673
|
},
|
|
24098
24674
|
{
|
|
24099
|
-
choice_id: "
|
|
24100
|
-
label: "
|
|
24675
|
+
choice_id: "send-as-is",
|
|
24676
|
+
label: "Send as-is",
|
|
24101
24677
|
description: "Send without adding context.",
|
|
24102
24678
|
resulting_disposition: "allow"
|
|
24103
24679
|
}
|
|
@@ -24114,6 +24690,12 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24114
24690
|
label: "Cancel",
|
|
24115
24691
|
description: "Do not send this prompt.",
|
|
24116
24692
|
resulting_disposition: "block"
|
|
24693
|
+
},
|
|
24694
|
+
{
|
|
24695
|
+
choice_id: "send-as-is",
|
|
24696
|
+
label: "Send as-is",
|
|
24697
|
+
description: "Send the original prompt after reviewing the identity-number risk.",
|
|
24698
|
+
resulting_disposition: "allow"
|
|
24117
24699
|
}
|
|
24118
24700
|
] : [
|
|
24119
24701
|
{
|
|
@@ -24134,6 +24716,60 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24134
24716
|
const parsedCard = PromptCoachCardSchema.parse(card);
|
|
24135
24717
|
return shouldQuietPromptCoachCard(parsedCard, disposition, learningSignals) ? void 0 : parsedCard;
|
|
24136
24718
|
}
|
|
24719
|
+
function buildPromptCoachRiskCopy(riskReport, fullSsnRisk) {
|
|
24720
|
+
if (fullSsnRisk) {
|
|
24721
|
+
return {
|
|
24722
|
+
title: "Guardian found an identity number",
|
|
24723
|
+
body: "Guardian can mask the Social Security number before anything leaves your machine."
|
|
24724
|
+
};
|
|
24725
|
+
}
|
|
24726
|
+
if (riskReport.detected_risks.includes("secret")) {
|
|
24727
|
+
return {
|
|
24728
|
+
title: "Guardian found a secret or credential",
|
|
24729
|
+
body: "Guardian can remove tokens, keys, or passwords before this prompt leaves your machine."
|
|
24730
|
+
};
|
|
24731
|
+
}
|
|
24732
|
+
if (riskReport.detected_risks.includes("financial_data")) {
|
|
24733
|
+
return {
|
|
24734
|
+
title: "Guardian found financial information",
|
|
24735
|
+
body: "Guardian can mask financial details or help you keep them out of the prompt."
|
|
24736
|
+
};
|
|
24737
|
+
}
|
|
24738
|
+
if (riskReport.detected_risks.includes("healthcare_data")) {
|
|
24739
|
+
return {
|
|
24740
|
+
title: "Guardian found health information",
|
|
24741
|
+
body: "Guardian can help remove identifying health details before you send this prompt."
|
|
24742
|
+
};
|
|
24743
|
+
}
|
|
24744
|
+
if (riskReport.detected_risks.includes("prompt_injection")) {
|
|
24745
|
+
return {
|
|
24746
|
+
title: "Guardian found prompt-injection language",
|
|
24747
|
+
body: "Guardian noticed instructions that may try to override your intended task or safety rules."
|
|
24748
|
+
};
|
|
24749
|
+
}
|
|
24750
|
+
if (riskReport.detected_risks.includes("unsafe_destination")) {
|
|
24751
|
+
return {
|
|
24752
|
+
title: "Guardian found an unfamiliar destination",
|
|
24753
|
+
body: "Guardian noticed this prompt may be going somewhere that needs an extra check."
|
|
24754
|
+
};
|
|
24755
|
+
}
|
|
24756
|
+
if (riskReport.detected_risks.includes("pii")) {
|
|
24757
|
+
return {
|
|
24758
|
+
title: "Guardian found personal information",
|
|
24759
|
+
body: "Guardian can mask personal details before this prompt leaves your machine."
|
|
24760
|
+
};
|
|
24761
|
+
}
|
|
24762
|
+
if (riskReport.detected_risks.includes("work_data")) {
|
|
24763
|
+
return {
|
|
24764
|
+
title: "Guardian found work or customer data",
|
|
24765
|
+
body: "Guardian can help keep private work details out of the prompt or replace them with placeholders."
|
|
24766
|
+
};
|
|
24767
|
+
}
|
|
24768
|
+
return {
|
|
24769
|
+
title: "Guardian can improve this prompt",
|
|
24770
|
+
body: "Guardian found context or privacy improvements that may make this prompt safer and more useful."
|
|
24771
|
+
};
|
|
24772
|
+
}
|
|
24137
24773
|
function shouldQuietPromptCoachCard(card, disposition, learningSignals) {
|
|
24138
24774
|
if (!learningSignals || disposition === "block" || disposition === "confirm") {
|
|
24139
24775
|
return false;
|
|
@@ -25781,10 +26417,14 @@ __export(daemon_exports, {
|
|
|
25781
26417
|
processGuardianControlTowerActivityRequest: () => processGuardianControlTowerActivityRequest,
|
|
25782
26418
|
processGuardianControlTowerAppPermissionRequest: () => processGuardianControlTowerAppPermissionRequest,
|
|
25783
26419
|
processGuardianControlTowerDataRequest: () => processGuardianControlTowerDataRequest,
|
|
26420
|
+
processGuardianControlTowerMarketplaceRequest: () => processGuardianControlTowerMarketplaceRequest,
|
|
26421
|
+
processGuardianControlTowerNoticeRequest: () => processGuardianControlTowerNoticeRequest,
|
|
25784
26422
|
processGuardianControlTowerPolicyRequest: () => processGuardianControlTowerPolicyRequest,
|
|
25785
26423
|
processGuardianControlTowerPreferenceRequest: () => processGuardianControlTowerPreferenceRequest,
|
|
26424
|
+
processGuardianControlTowerPrimePackRequest: () => processGuardianControlTowerPrimePackRequest,
|
|
25786
26425
|
processGuardianControlTowerProviderPermissionRequest: () => processGuardianControlTowerProviderPermissionRequest,
|
|
25787
26426
|
processGuardianControlTowerSourcePermissionRequest: () => processGuardianControlTowerSourcePermissionRequest,
|
|
26427
|
+
processGuardianControlTowerViewModelRequest: () => processGuardianControlTowerViewModelRequest,
|
|
25788
26428
|
processGuardianDaemonHealthRequest: () => processGuardianDaemonHealthRequest,
|
|
25789
26429
|
processGuardianDaemonStopRequest: () => processGuardianDaemonStopRequest,
|
|
25790
26430
|
processGuardianLiveCoachEventRequest: () => processGuardianLiveCoachEventRequest,
|
|
@@ -25794,7 +26434,7 @@ __export(daemon_exports, {
|
|
|
25794
26434
|
});
|
|
25795
26435
|
import { createHash as createHash5, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
25796
26436
|
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";
|
|
26437
|
+
import { mkdir as mkdir4, readFile as readFile4, readdir as readdir2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
|
|
25798
26438
|
import { createServer } from "node:http";
|
|
25799
26439
|
import path4 from "node:path";
|
|
25800
26440
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -25920,12 +26560,22 @@ async function handleRequest(input2) {
|
|
|
25920
26560
|
return;
|
|
25921
26561
|
}
|
|
25922
26562
|
if (input2.request.method === "GET" && (input2.request.url === "/control-tower" || input2.request.url === "/control-tower/")) {
|
|
25923
|
-
const [
|
|
26563
|
+
const [
|
|
26564
|
+
receipts,
|
|
26565
|
+
searchGraph,
|
|
26566
|
+
learningEvents,
|
|
26567
|
+
localDataStatus,
|
|
26568
|
+
releaseDoctorStatus,
|
|
26569
|
+
adminNotices,
|
|
26570
|
+
marketplaceRequests
|
|
26571
|
+
] = await Promise.all([
|
|
25924
26572
|
input2.store.listReceipts(),
|
|
25925
26573
|
input2.store.searchGraph({ limit: CONTROL_TOWER_GRAPH_NODE_LIMIT }),
|
|
25926
26574
|
input2.store.listLearningEvents({ limit: CONTROL_TOWER_LEARNING_EVENT_LIMIT }),
|
|
25927
26575
|
buildControlTowerLocalDataStatus(input2.profile, input2.env),
|
|
25928
|
-
buildControlTowerReleaseDoctorStatus(input2.profile, input2.env)
|
|
26576
|
+
buildControlTowerReleaseDoctorStatus(input2.profile, input2.env),
|
|
26577
|
+
readControlTowerAdminNotices(input2.profile.profile_dir),
|
|
26578
|
+
readControlTowerMarketplaceRequests(input2.profile.profile_dir)
|
|
25929
26579
|
]);
|
|
25930
26580
|
writeHtml(
|
|
25931
26581
|
input2.response,
|
|
@@ -25936,7 +26586,9 @@ async function handleRequest(input2) {
|
|
|
25936
26586
|
searchGraph,
|
|
25937
26587
|
learningEvents,
|
|
25938
26588
|
localDataStatus,
|
|
25939
|
-
releaseDoctorStatus
|
|
26589
|
+
releaseDoctorStatus,
|
|
26590
|
+
adminNotices,
|
|
26591
|
+
marketplaceRequests
|
|
25940
26592
|
),
|
|
25941
26593
|
{
|
|
25942
26594
|
controlToken: input2.controlToken
|
|
@@ -25945,6 +26597,37 @@ async function handleRequest(input2) {
|
|
|
25945
26597
|
return;
|
|
25946
26598
|
}
|
|
25947
26599
|
const activityUrl = parseGuardianUrl(input2.request.url);
|
|
26600
|
+
if (input2.request.method === "GET" && activityUrl?.pathname === "/v1/guardian/control-tower/view-model") {
|
|
26601
|
+
const [
|
|
26602
|
+
receipts,
|
|
26603
|
+
searchGraph,
|
|
26604
|
+
learningEvents,
|
|
26605
|
+
localDataStatus,
|
|
26606
|
+
releaseDoctorStatus,
|
|
26607
|
+
adminNotices,
|
|
26608
|
+
marketplaceRequests
|
|
26609
|
+
] = await Promise.all([
|
|
26610
|
+
input2.store.listReceipts(),
|
|
26611
|
+
input2.store.searchGraph({ limit: CONTROL_TOWER_GRAPH_NODE_LIMIT }),
|
|
26612
|
+
input2.store.listLearningEvents({ limit: CONTROL_TOWER_LEARNING_EVENT_LIMIT }),
|
|
26613
|
+
buildControlTowerLocalDataStatus(input2.profile, input2.env),
|
|
26614
|
+
buildControlTowerReleaseDoctorStatus(input2.profile, input2.env),
|
|
26615
|
+
readControlTowerAdminNotices(input2.profile.profile_dir),
|
|
26616
|
+
readControlTowerMarketplaceRequests(input2.profile.profile_dir)
|
|
26617
|
+
]);
|
|
26618
|
+
const result2 = processGuardianControlTowerViewModelRequest(
|
|
26619
|
+
input2.profile,
|
|
26620
|
+
receipts,
|
|
26621
|
+
searchGraph,
|
|
26622
|
+
learningEvents,
|
|
26623
|
+
localDataStatus,
|
|
26624
|
+
releaseDoctorStatus,
|
|
26625
|
+
adminNotices,
|
|
26626
|
+
marketplaceRequests
|
|
26627
|
+
);
|
|
26628
|
+
writeJson(input2.response, result2.statusCode, result2.body);
|
|
26629
|
+
return;
|
|
26630
|
+
}
|
|
25948
26631
|
if (input2.request.method === "GET" && activityUrl?.pathname === "/v1/guardian/activity") {
|
|
25949
26632
|
const filter = parseActivityFilter(activityUrl.searchParams.get("filter") ?? "all");
|
|
25950
26633
|
const receipts = (await input2.store.listReceipts()).slice().sort((left, right) => right.created_at.localeCompare(left.created_at)).slice(0, CONTROL_TOWER_RECEIPT_LIMIT);
|
|
@@ -25989,6 +26672,24 @@ async function handleRequest(input2) {
|
|
|
25989
26672
|
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
25990
26673
|
return;
|
|
25991
26674
|
}
|
|
26675
|
+
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/prime-pack") {
|
|
26676
|
+
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
26677
|
+
if (!parsedBody2.ok) {
|
|
26678
|
+
writeJson(input2.response, parsedBody2.statusCode, parsedBody2.body);
|
|
26679
|
+
return;
|
|
26680
|
+
}
|
|
26681
|
+
const result2 = await processGuardianControlTowerPrimePackRequest({
|
|
26682
|
+
profile: input2.profile,
|
|
26683
|
+
token: input2.token,
|
|
26684
|
+
controlToken: input2.controlToken,
|
|
26685
|
+
suppliedToken: headerValue(input2.request, ADMIN_HEADER) ?? readCookieValue(input2.request, CONTROL_TOWER_COOKIE),
|
|
26686
|
+
body: parsedBody2.body,
|
|
26687
|
+
now: input2.now,
|
|
26688
|
+
store: input2.store
|
|
26689
|
+
});
|
|
26690
|
+
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26691
|
+
return;
|
|
26692
|
+
}
|
|
25992
26693
|
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/preferences") {
|
|
25993
26694
|
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
25994
26695
|
if (!parsedBody2.ok) {
|
|
@@ -26069,6 +26770,42 @@ async function handleRequest(input2) {
|
|
|
26069
26770
|
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26070
26771
|
return;
|
|
26071
26772
|
}
|
|
26773
|
+
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/notice") {
|
|
26774
|
+
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
26775
|
+
if (!parsedBody2.ok) {
|
|
26776
|
+
writeJson(input2.response, parsedBody2.statusCode, parsedBody2.body);
|
|
26777
|
+
return;
|
|
26778
|
+
}
|
|
26779
|
+
const result2 = await processGuardianControlTowerNoticeRequest({
|
|
26780
|
+
profile: input2.profile,
|
|
26781
|
+
token: input2.token,
|
|
26782
|
+
controlToken: input2.controlToken,
|
|
26783
|
+
suppliedToken: headerValue(input2.request, ADMIN_HEADER) ?? readCookieValue(input2.request, CONTROL_TOWER_COOKIE),
|
|
26784
|
+
body: parsedBody2.body,
|
|
26785
|
+
now: input2.now,
|
|
26786
|
+
store: input2.store
|
|
26787
|
+
});
|
|
26788
|
+
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26789
|
+
return;
|
|
26790
|
+
}
|
|
26791
|
+
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/marketplace-request") {
|
|
26792
|
+
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
26793
|
+
if (!parsedBody2.ok) {
|
|
26794
|
+
writeJson(input2.response, parsedBody2.statusCode, parsedBody2.body);
|
|
26795
|
+
return;
|
|
26796
|
+
}
|
|
26797
|
+
const result2 = await processGuardianControlTowerMarketplaceRequest({
|
|
26798
|
+
profile: input2.profile,
|
|
26799
|
+
token: input2.token,
|
|
26800
|
+
controlToken: input2.controlToken,
|
|
26801
|
+
suppliedToken: headerValue(input2.request, ADMIN_HEADER) ?? readCookieValue(input2.request, CONTROL_TOWER_COOKIE),
|
|
26802
|
+
body: parsedBody2.body,
|
|
26803
|
+
now: input2.now,
|
|
26804
|
+
store: input2.store
|
|
26805
|
+
});
|
|
26806
|
+
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26807
|
+
return;
|
|
26808
|
+
}
|
|
26072
26809
|
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/live-coach-event") {
|
|
26073
26810
|
const suppliedToken2 = headerValue(input2.request, ADMIN_HEADER);
|
|
26074
26811
|
const parsedBody2 = await readGuardianDaemonJsonBody(input2.request);
|
|
@@ -26141,6 +26878,23 @@ function processGuardianControlTowerActivityRequest(receipts, filterValue) {
|
|
|
26141
26878
|
}
|
|
26142
26879
|
};
|
|
26143
26880
|
}
|
|
26881
|
+
function processGuardianControlTowerViewModelRequest(profile, receipts = [], searchGraph = { nodes: [], edges: [] }, learningEvents = [], localDataStatus = createEmptyControlTowerLocalDataStatus(profile), releaseDoctorStatus = createEmptyControlTowerReleaseDoctorStatus(
|
|
26882
|
+
profile
|
|
26883
|
+
), adminNotices = [], marketplaceRequests = []) {
|
|
26884
|
+
return {
|
|
26885
|
+
statusCode: 200,
|
|
26886
|
+
body: buildControlTowerViewModel(
|
|
26887
|
+
profile,
|
|
26888
|
+
receipts,
|
|
26889
|
+
searchGraph,
|
|
26890
|
+
learningEvents,
|
|
26891
|
+
localDataStatus,
|
|
26892
|
+
releaseDoctorStatus,
|
|
26893
|
+
adminNotices,
|
|
26894
|
+
marketplaceRequests
|
|
26895
|
+
)
|
|
26896
|
+
};
|
|
26897
|
+
}
|
|
26144
26898
|
function processGuardianDaemonStopRequest(input2) {
|
|
26145
26899
|
if (!isAuthorized(input2.token, input2.suppliedToken)) {
|
|
26146
26900
|
return {
|
|
@@ -26206,6 +26960,105 @@ async function processGuardianControlTowerPolicyRequest(input2) {
|
|
|
26206
26960
|
};
|
|
26207
26961
|
}
|
|
26208
26962
|
}
|
|
26963
|
+
async function processGuardianControlTowerPrimePackRequest(input2) {
|
|
26964
|
+
if (!isAdminAuthorized(input2)) {
|
|
26965
|
+
return {
|
|
26966
|
+
statusCode: 401,
|
|
26967
|
+
body: {
|
|
26968
|
+
ok: false,
|
|
26969
|
+
error: "unauthorized"
|
|
26970
|
+
}
|
|
26971
|
+
};
|
|
26972
|
+
}
|
|
26973
|
+
const action = parsePrimePackAction(input2.body.action);
|
|
26974
|
+
if (!action) {
|
|
26975
|
+
return {
|
|
26976
|
+
statusCode: 400,
|
|
26977
|
+
body: {
|
|
26978
|
+
ok: false,
|
|
26979
|
+
error: "invalid_prime_pack_action"
|
|
26980
|
+
}
|
|
26981
|
+
};
|
|
26982
|
+
}
|
|
26983
|
+
const inputs = {
|
|
26984
|
+
sensitive_terms: parsePrimePackEntries([
|
|
26985
|
+
input2.body.sensitiveTerms,
|
|
26986
|
+
parsePrimePackImportEntries(input2.body.importContent, "sensitive_terms")
|
|
26987
|
+
]),
|
|
26988
|
+
approved_domains: parsePrimePackEntries(
|
|
26989
|
+
[
|
|
26990
|
+
input2.body.approvedDomains,
|
|
26991
|
+
parsePrimePackImportEntries(input2.body.importContent, "approved_domains")
|
|
26992
|
+
],
|
|
26993
|
+
{ lowercase: true }
|
|
26994
|
+
),
|
|
26995
|
+
blocked_destinations: parsePrimePackEntries(
|
|
26996
|
+
[
|
|
26997
|
+
input2.body.blockedDestinations,
|
|
26998
|
+
parsePrimePackImportEntries(input2.body.importContent, "blocked_destinations")
|
|
26999
|
+
],
|
|
27000
|
+
{
|
|
27001
|
+
lowercase: true
|
|
27002
|
+
}
|
|
27003
|
+
),
|
|
27004
|
+
escalation_phrases: parsePrimePackEntries([
|
|
27005
|
+
input2.body.escalationPhrases,
|
|
27006
|
+
parsePrimePackImportEntries(input2.body.importContent, "escalation_phrases")
|
|
27007
|
+
])
|
|
27008
|
+
};
|
|
27009
|
+
const preview = buildPrimePackPreview(inputs);
|
|
27010
|
+
if (preview.total_inputs === 0) {
|
|
27011
|
+
return {
|
|
27012
|
+
statusCode: 400,
|
|
27013
|
+
body: {
|
|
27014
|
+
ok: false,
|
|
27015
|
+
error: "empty_prime_pack"
|
|
27016
|
+
}
|
|
27017
|
+
};
|
|
27018
|
+
}
|
|
27019
|
+
const now = (input2.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
27020
|
+
const draftSeed = JSON.stringify(inputs);
|
|
27021
|
+
const transactionId = `txn:prime-pack:${digest4(`${draftSeed}:${now}`).slice(0, 20)}`;
|
|
27022
|
+
const guardianReceipt = action === "deploy" ? buildPrimePackDeployGuardianReceipt(inputs, preview, transactionId, now) : void 0;
|
|
27023
|
+
const draft = {
|
|
27024
|
+
schema_version: "contextecf/project-guardian-prime-pack-draft/v1",
|
|
27025
|
+
draft_id: `prime-pack:${digest4(draftSeed).slice(0, 16)}`,
|
|
27026
|
+
status: action === "deploy" ? "deployed_metadata_only" : "preview_ready",
|
|
27027
|
+
updated_at: now,
|
|
27028
|
+
...action === "deploy" ? { deployed_at: now } : {},
|
|
27029
|
+
inputs,
|
|
27030
|
+
preview,
|
|
27031
|
+
...guardianReceipt ? {
|
|
27032
|
+
local_receipt: buildPrimePackDeployReceipt(guardianReceipt)
|
|
27033
|
+
} : {},
|
|
27034
|
+
raw_prompt_content_included: false,
|
|
27035
|
+
token_printed: false
|
|
27036
|
+
};
|
|
27037
|
+
if (guardianReceipt && input2.store) {
|
|
27038
|
+
await input2.store.putReceipt(guardianReceipt);
|
|
27039
|
+
await input2.store.appendEvent(
|
|
27040
|
+
buildPrimePackDeployEvent(inputs, preview, transactionId, now, guardianReceipt)
|
|
27041
|
+
);
|
|
27042
|
+
}
|
|
27043
|
+
await writeControlTowerPrimePackDraft(input2.profile.profile_dir, draft);
|
|
27044
|
+
return {
|
|
27045
|
+
statusCode: 200,
|
|
27046
|
+
body: {
|
|
27047
|
+
ok: true,
|
|
27048
|
+
schema_version: "contextecf/project-guardian-control-tower-prime-pack/v1",
|
|
27049
|
+
action,
|
|
27050
|
+
status: draft.status,
|
|
27051
|
+
draft_id: draft.draft_id,
|
|
27052
|
+
file: CONTROL_TOWER_PRIME_PACK_FILE_NAME,
|
|
27053
|
+
preview,
|
|
27054
|
+
local_receipt: draft.local_receipt,
|
|
27055
|
+
receipt_id: guardianReceipt?.receipt_id,
|
|
27056
|
+
stored_locally: true,
|
|
27057
|
+
raw_prompt_content_included: false,
|
|
27058
|
+
token_printed: false
|
|
27059
|
+
}
|
|
27060
|
+
};
|
|
27061
|
+
}
|
|
26209
27062
|
async function processGuardianControlTowerPreferenceRequest(input2) {
|
|
26210
27063
|
if (!isAdminAuthorized(input2)) {
|
|
26211
27064
|
return {
|
|
@@ -26492,6 +27345,190 @@ async function processGuardianControlTowerDataRequest(input2) {
|
|
|
26492
27345
|
};
|
|
26493
27346
|
}
|
|
26494
27347
|
}
|
|
27348
|
+
async function processGuardianControlTowerNoticeRequest(input2) {
|
|
27349
|
+
if (!isAdminAuthorized(input2)) {
|
|
27350
|
+
return {
|
|
27351
|
+
statusCode: 401,
|
|
27352
|
+
body: {
|
|
27353
|
+
ok: false,
|
|
27354
|
+
error: "unauthorized"
|
|
27355
|
+
}
|
|
27356
|
+
};
|
|
27357
|
+
}
|
|
27358
|
+
const action = input2.body.action?.trim() || "publish";
|
|
27359
|
+
if (action === "clear") {
|
|
27360
|
+
const clearedAt = input2.now().toISOString();
|
|
27361
|
+
const transactionId2 = `txn:admin-notice:${digest4(`clear:${clearedAt}`).slice(0, 20)}`;
|
|
27362
|
+
const receipt2 = buildAdminNoticeReceipt({
|
|
27363
|
+
action,
|
|
27364
|
+
transactionId: transactionId2,
|
|
27365
|
+
timestamp: clearedAt
|
|
27366
|
+
});
|
|
27367
|
+
await input2.store.putReceipt(receipt2);
|
|
27368
|
+
await input2.store.appendEvent(
|
|
27369
|
+
buildAdminNoticeEvent({ action, transactionId: transactionId2, timestamp: clearedAt, receipt: receipt2 })
|
|
27370
|
+
);
|
|
27371
|
+
await writeControlTowerAdminNotices(input2.profile.profile_dir, []);
|
|
27372
|
+
return {
|
|
27373
|
+
statusCode: 200,
|
|
27374
|
+
body: {
|
|
27375
|
+
ok: true,
|
|
27376
|
+
schema_version: "contextecf/project-guardian-control-tower-admin-notice/v1",
|
|
27377
|
+
action,
|
|
27378
|
+
notices: [],
|
|
27379
|
+
receipt_id: receipt2.receipt_id,
|
|
27380
|
+
raw_content_included: false,
|
|
27381
|
+
token_printed: false
|
|
27382
|
+
}
|
|
27383
|
+
};
|
|
27384
|
+
}
|
|
27385
|
+
if (action !== "publish") {
|
|
27386
|
+
return {
|
|
27387
|
+
statusCode: 400,
|
|
27388
|
+
body: {
|
|
27389
|
+
ok: false,
|
|
27390
|
+
error: "invalid_notice_action"
|
|
27391
|
+
}
|
|
27392
|
+
};
|
|
27393
|
+
}
|
|
27394
|
+
const title = boundedNoticeText(input2.body.title, CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT);
|
|
27395
|
+
const message = boundedNoticeText(input2.body.message, CONTROL_TOWER_ADMIN_NOTICE_MESSAGE_LIMIT);
|
|
27396
|
+
const audience = parseControlTowerAdminNoticeAudience(input2.body.audience);
|
|
27397
|
+
const tone = parseControlTowerAdminNoticeTone(input2.body.tone);
|
|
27398
|
+
if (!title || !message || !audience || !tone) {
|
|
27399
|
+
return {
|
|
27400
|
+
statusCode: 400,
|
|
27401
|
+
body: {
|
|
27402
|
+
ok: false,
|
|
27403
|
+
error: "invalid_notice_request"
|
|
27404
|
+
}
|
|
27405
|
+
};
|
|
27406
|
+
}
|
|
27407
|
+
const existingNotices = await readControlTowerAdminNotices(input2.profile.profile_dir);
|
|
27408
|
+
const createdAt = input2.now().toISOString();
|
|
27409
|
+
const transactionId = `txn:admin-notice:${digest4(
|
|
27410
|
+
`${title}:${message}:${audience}:${tone}:${createdAt}`
|
|
27411
|
+
).slice(0, 20)}`;
|
|
27412
|
+
const receipt = buildAdminNoticeReceipt({
|
|
27413
|
+
action,
|
|
27414
|
+
transactionId,
|
|
27415
|
+
timestamp: createdAt,
|
|
27416
|
+
title,
|
|
27417
|
+
message,
|
|
27418
|
+
audience,
|
|
27419
|
+
tone
|
|
27420
|
+
});
|
|
27421
|
+
const notice = {
|
|
27422
|
+
id: `notice:${digest4(`${title}:${message}:${createdAt}`).slice(0, 16)}`,
|
|
27423
|
+
title,
|
|
27424
|
+
message,
|
|
27425
|
+
audience,
|
|
27426
|
+
tone,
|
|
27427
|
+
created_at: createdAt,
|
|
27428
|
+
receipt_id: receipt.receipt_id,
|
|
27429
|
+
receipt_hash: receipt.receipt_hash,
|
|
27430
|
+
raw_content_included: false
|
|
27431
|
+
};
|
|
27432
|
+
const notices = [notice, ...existingNotices].slice(0, CONTROL_TOWER_ADMIN_NOTICE_LIMIT);
|
|
27433
|
+
await input2.store.putReceipt(receipt);
|
|
27434
|
+
await input2.store.appendEvent(
|
|
27435
|
+
buildAdminNoticeEvent({
|
|
27436
|
+
action,
|
|
27437
|
+
transactionId,
|
|
27438
|
+
timestamp: createdAt,
|
|
27439
|
+
receipt,
|
|
27440
|
+
title,
|
|
27441
|
+
message,
|
|
27442
|
+
audience,
|
|
27443
|
+
tone
|
|
27444
|
+
})
|
|
27445
|
+
);
|
|
27446
|
+
await writeControlTowerAdminNotices(input2.profile.profile_dir, notices);
|
|
27447
|
+
return {
|
|
27448
|
+
statusCode: 200,
|
|
27449
|
+
body: {
|
|
27450
|
+
ok: true,
|
|
27451
|
+
schema_version: "contextecf/project-guardian-control-tower-admin-notice/v1",
|
|
27452
|
+
action,
|
|
27453
|
+
notice,
|
|
27454
|
+
notices,
|
|
27455
|
+
receipt_id: receipt.receipt_id,
|
|
27456
|
+
raw_content_included: false,
|
|
27457
|
+
token_printed: false
|
|
27458
|
+
}
|
|
27459
|
+
};
|
|
27460
|
+
}
|
|
27461
|
+
async function processGuardianControlTowerMarketplaceRequest(input2) {
|
|
27462
|
+
if (!isAdminAuthorized(input2)) {
|
|
27463
|
+
return {
|
|
27464
|
+
statusCode: 401,
|
|
27465
|
+
body: {
|
|
27466
|
+
ok: false,
|
|
27467
|
+
error: "unauthorized"
|
|
27468
|
+
}
|
|
27469
|
+
};
|
|
27470
|
+
}
|
|
27471
|
+
const action = parseMarketplaceRequestAction(input2.body.action);
|
|
27472
|
+
const targetId = input2.body.packId?.trim();
|
|
27473
|
+
const manifest = targetId ? findControlTowerMarketplaceManifest(targetId) : void 0;
|
|
27474
|
+
if (!action || !manifest) {
|
|
27475
|
+
return {
|
|
27476
|
+
statusCode: 400,
|
|
27477
|
+
body: {
|
|
27478
|
+
ok: false,
|
|
27479
|
+
error: "invalid_marketplace_request"
|
|
27480
|
+
}
|
|
27481
|
+
};
|
|
27482
|
+
}
|
|
27483
|
+
const requestedAt = input2.now().toISOString();
|
|
27484
|
+
const transactionId = `txn:marketplace-request:${digest4(
|
|
27485
|
+
`${manifest.marketplace_manifest_id}:${action}:${requestedAt}`
|
|
27486
|
+
).slice(0, 20)}`;
|
|
27487
|
+
const receipt = buildMarketplaceRequestReceipt(manifest, action, transactionId, requestedAt);
|
|
27488
|
+
const request = {
|
|
27489
|
+
schema_version: "contextecf/project-guardian-marketplace-request/v1",
|
|
27490
|
+
request_id: `request:${transactionId}`,
|
|
27491
|
+
pack_id: manifest.pack_id,
|
|
27492
|
+
marketplace_manifest_id: manifest.marketplace_manifest_id,
|
|
27493
|
+
pack_version: manifest.pack_version,
|
|
27494
|
+
pack_name: manifest.display_name,
|
|
27495
|
+
publisher: manifest.publisher_display_name,
|
|
27496
|
+
action,
|
|
27497
|
+
status: "requested_metadata_only",
|
|
27498
|
+
requested_at: requestedAt,
|
|
27499
|
+
receipt_id: receipt.receipt_id,
|
|
27500
|
+
receipt_hash: receipt.receipt_hash,
|
|
27501
|
+
raw_content_included: false,
|
|
27502
|
+
token_printed: false
|
|
27503
|
+
};
|
|
27504
|
+
const existingRequests = await readControlTowerMarketplaceRequests(input2.profile.profile_dir);
|
|
27505
|
+
const requests = [
|
|
27506
|
+
request,
|
|
27507
|
+
...existingRequests.filter(
|
|
27508
|
+
(existing) => existing.marketplace_manifest_id !== manifest.marketplace_manifest_id
|
|
27509
|
+
)
|
|
27510
|
+
].slice(0, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT);
|
|
27511
|
+
await input2.store.putReceipt(receipt);
|
|
27512
|
+
await input2.store.appendEvent(
|
|
27513
|
+
buildMarketplaceRequestEvent(manifest, action, transactionId, requestedAt, receipt)
|
|
27514
|
+
);
|
|
27515
|
+
await writeControlTowerMarketplaceRequests(input2.profile.profile_dir, requests);
|
|
27516
|
+
return {
|
|
27517
|
+
statusCode: 200,
|
|
27518
|
+
body: {
|
|
27519
|
+
ok: true,
|
|
27520
|
+
schema_version: "contextecf/project-guardian-control-tower-marketplace-request/v1",
|
|
27521
|
+
action,
|
|
27522
|
+
status: request.status,
|
|
27523
|
+
request,
|
|
27524
|
+
request_file: CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME,
|
|
27525
|
+
receipt_id: receipt.receipt_id,
|
|
27526
|
+
stored_locally: true,
|
|
27527
|
+
raw_content_included: false,
|
|
27528
|
+
token_printed: false
|
|
27529
|
+
}
|
|
27530
|
+
};
|
|
27531
|
+
}
|
|
26495
27532
|
function parseAppPermissionAction(value) {
|
|
26496
27533
|
return value === "revoke" || value === "allow" ? value : void 0;
|
|
26497
27534
|
}
|
|
@@ -26507,6 +27544,25 @@ function parseGuardianSourceType2(value) {
|
|
|
26507
27544
|
}
|
|
26508
27545
|
return GUARDIAN_SOURCE_TYPES.includes(value) ? value : void 0;
|
|
26509
27546
|
}
|
|
27547
|
+
function parseControlTowerAdminNoticeAudience(value) {
|
|
27548
|
+
return value === "all_users" || value === "admins" || value === "developers" ? value : void 0;
|
|
27549
|
+
}
|
|
27550
|
+
function parseControlTowerAdminNoticeTone(value) {
|
|
27551
|
+
return value === "info" || value === "warning" || value === "success" ? value : void 0;
|
|
27552
|
+
}
|
|
27553
|
+
function boundedNoticeText(value, limit) {
|
|
27554
|
+
if (typeof value !== "string") {
|
|
27555
|
+
return void 0;
|
|
27556
|
+
}
|
|
27557
|
+
const trimmed = value.replace(/\s+/gu, " ").trim();
|
|
27558
|
+
if (!trimmed) {
|
|
27559
|
+
return void 0;
|
|
27560
|
+
}
|
|
27561
|
+
return trimmed.slice(0, limit);
|
|
27562
|
+
}
|
|
27563
|
+
function isRecord(value) {
|
|
27564
|
+
return typeof value === "object" && value !== null;
|
|
27565
|
+
}
|
|
26510
27566
|
async function processGuardianBrowserBridgeRequest(input2) {
|
|
26511
27567
|
if (!isAuthorized(input2.token, input2.suppliedToken)) {
|
|
26512
27568
|
return {
|
|
@@ -26647,6 +27703,11 @@ async function processBrowserPrompt(input2) {
|
|
|
26647
27703
|
visible_message: decision.visible_message,
|
|
26648
27704
|
required_confirmation: decision.required_confirmation,
|
|
26649
27705
|
coach_card: decision.coach_card,
|
|
27706
|
+
risk_report: {
|
|
27707
|
+
risk_tier: decision.risk_report.risk_tier,
|
|
27708
|
+
detected_risks: decision.risk_report.detected_risks,
|
|
27709
|
+
matched_policy_refs: decision.risk_report.matched_policy_refs
|
|
27710
|
+
},
|
|
26650
27711
|
receipt_id: decision.receipt.receipt_id
|
|
26651
27712
|
};
|
|
26652
27713
|
}
|
|
@@ -26837,6 +27898,114 @@ function buildLiveCoachEvent(request, transactionId, timestamp, receipt) {
|
|
|
26837
27898
|
redaction_state: "metadata_only"
|
|
26838
27899
|
};
|
|
26839
27900
|
}
|
|
27901
|
+
function parseMarketplaceRequestAction(value) {
|
|
27902
|
+
return value === "request_review" ? value : void 0;
|
|
27903
|
+
}
|
|
27904
|
+
function findControlTowerMarketplaceManifest(targetId) {
|
|
27905
|
+
return GUARDIAN_POLICY_PACK_MARKETPLACE_CATALOG.find(
|
|
27906
|
+
(manifest) => manifest.pack_id === targetId || manifest.marketplace_manifest_id === targetId
|
|
27907
|
+
);
|
|
27908
|
+
}
|
|
27909
|
+
function buildMarketplaceRequestReceipt(manifest, action, transactionId, timestamp) {
|
|
27910
|
+
const core = {
|
|
27911
|
+
transaction_id: transactionId,
|
|
27912
|
+
pack_id: manifest.pack_id,
|
|
27913
|
+
marketplace_manifest_id: manifest.marketplace_manifest_id,
|
|
27914
|
+
pack_version: manifest.pack_version,
|
|
27915
|
+
action,
|
|
27916
|
+
created_at: timestamp,
|
|
27917
|
+
raw_content_included: false
|
|
27918
|
+
};
|
|
27919
|
+
return {
|
|
27920
|
+
schema_version: PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION,
|
|
27921
|
+
receipt_id: `receipt:${transactionId}:marketplace-request`,
|
|
27922
|
+
transaction_id: transactionId,
|
|
27923
|
+
receipt_type: "policy",
|
|
27924
|
+
disposition: "allow",
|
|
27925
|
+
policy_basis: [
|
|
27926
|
+
"marketplace-request-metadata-only",
|
|
27927
|
+
`pack:${manifest.pack_id}`,
|
|
27928
|
+
`manifest:${manifest.marketplace_manifest_id}`
|
|
27929
|
+
],
|
|
27930
|
+
receipt_hash: `sha256:${digest4(JSON.stringify(core))}`,
|
|
27931
|
+
raw_content_included: false,
|
|
27932
|
+
created_at: timestamp,
|
|
27933
|
+
summary: `Guardian recorded a marketplace review request for ${manifest.display_name}.`
|
|
27934
|
+
};
|
|
27935
|
+
}
|
|
27936
|
+
function buildMarketplaceRequestEvent(manifest, action, transactionId, timestamp, receipt) {
|
|
27937
|
+
const payload = {
|
|
27938
|
+
pack_id: manifest.pack_id,
|
|
27939
|
+
marketplace_manifest_id: manifest.marketplace_manifest_id,
|
|
27940
|
+
pack_version: manifest.pack_version,
|
|
27941
|
+
action,
|
|
27942
|
+
status: "requested_metadata_only",
|
|
27943
|
+
raw_content_included: false
|
|
27944
|
+
};
|
|
27945
|
+
return {
|
|
27946
|
+
event_id: `event:${transactionId}:marketplace-request`,
|
|
27947
|
+
transaction_id: transactionId,
|
|
27948
|
+
event_type: "policy_decided",
|
|
27949
|
+
occurred_at: timestamp,
|
|
27950
|
+
actor: "guardian-daemon",
|
|
27951
|
+
summary: `Marketplace pack review requested for ${manifest.display_name}.`,
|
|
27952
|
+
payload_hash: `sha256:${digest4(JSON.stringify(payload))}`,
|
|
27953
|
+
receipt_id: receipt.receipt_id,
|
|
27954
|
+
graph_refs: [`policy-pack:${manifest.pack_id}`, `receipt:${receipt.receipt_id}`],
|
|
27955
|
+
redaction_state: "metadata_only"
|
|
27956
|
+
};
|
|
27957
|
+
}
|
|
27958
|
+
function buildAdminNoticeReceipt(input2) {
|
|
27959
|
+
const core = {
|
|
27960
|
+
transaction_id: input2.transactionId,
|
|
27961
|
+
action: input2.action,
|
|
27962
|
+
title_hash: input2.title ? digest4(input2.title) : void 0,
|
|
27963
|
+
message_hash: input2.message ? digest4(input2.message) : void 0,
|
|
27964
|
+
audience: input2.audience ?? "all_users",
|
|
27965
|
+
tone: input2.tone ?? "info",
|
|
27966
|
+
created_at: input2.timestamp,
|
|
27967
|
+
raw_content_included: false
|
|
27968
|
+
};
|
|
27969
|
+
const audienceLabel = adminNoticeAudienceLabel(input2.audience ?? "all_users");
|
|
27970
|
+
return {
|
|
27971
|
+
schema_version: PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION,
|
|
27972
|
+
receipt_id: `receipt:${input2.transactionId}:admin-notice`,
|
|
27973
|
+
transaction_id: input2.transactionId,
|
|
27974
|
+
receipt_type: "policy",
|
|
27975
|
+
disposition: "allow",
|
|
27976
|
+
policy_basis: [
|
|
27977
|
+
"admin-notice-metadata-only",
|
|
27978
|
+
`action:${input2.action}`,
|
|
27979
|
+
`audience:${input2.audience ?? "all_users"}`
|
|
27980
|
+
],
|
|
27981
|
+
receipt_hash: `sha256:${digest4(JSON.stringify(core))}`,
|
|
27982
|
+
raw_content_included: false,
|
|
27983
|
+
created_at: input2.timestamp,
|
|
27984
|
+
summary: input2.action === "clear" ? "Guardian cleared local admin notices." : `Guardian published a local admin notice for ${audienceLabel}.`
|
|
27985
|
+
};
|
|
27986
|
+
}
|
|
27987
|
+
function buildAdminNoticeEvent(input2) {
|
|
27988
|
+
const payload = {
|
|
27989
|
+
action: input2.action,
|
|
27990
|
+
title_hash: input2.title ? digest4(input2.title) : void 0,
|
|
27991
|
+
message_hash: input2.message ? digest4(input2.message) : void 0,
|
|
27992
|
+
audience: input2.audience ?? "all_users",
|
|
27993
|
+
tone: input2.tone ?? "info",
|
|
27994
|
+
raw_content_included: false
|
|
27995
|
+
};
|
|
27996
|
+
return {
|
|
27997
|
+
event_id: `event:${input2.transactionId}:admin-notice`,
|
|
27998
|
+
transaction_id: input2.transactionId,
|
|
27999
|
+
event_type: "policy_decided",
|
|
28000
|
+
occurred_at: input2.timestamp,
|
|
28001
|
+
actor: "guardian-daemon",
|
|
28002
|
+
summary: input2.action === "clear" ? "Admin notice bar cleared." : `Admin notice bar updated for ${adminNoticeAudienceLabel(input2.audience ?? "all_users")}.`,
|
|
28003
|
+
payload_hash: `sha256:${digest4(JSON.stringify(payload))}`,
|
|
28004
|
+
receipt_id: input2.receipt.receipt_id,
|
|
28005
|
+
graph_refs: [`admin-notice:${input2.action}`, `receipt:${input2.receipt.receipt_id}`],
|
|
28006
|
+
redaction_state: "metadata_only"
|
|
28007
|
+
};
|
|
28008
|
+
}
|
|
26840
28009
|
function liveCoachCategoryLabel(category) {
|
|
26841
28010
|
if (category === "ssn") return "possible SSN sharing";
|
|
26842
28011
|
if (category === "password") return "possible credential sharing";
|
|
@@ -26952,6 +28121,218 @@ function isAdminAuthorized(input2) {
|
|
|
26952
28121
|
function parsePolicyPackAction(value) {
|
|
26953
28122
|
return value === "enable" || value === "disable" ? value : void 0;
|
|
26954
28123
|
}
|
|
28124
|
+
function parsePrimePackAction(value) {
|
|
28125
|
+
return value === "preview" || value === "deploy" ? value : void 0;
|
|
28126
|
+
}
|
|
28127
|
+
function parsePrimePackEntries(value, options = {}) {
|
|
28128
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28129
|
+
const entries = [];
|
|
28130
|
+
for (const rawEntry of flattenPrimePackEntryValues(value)) {
|
|
28131
|
+
const normalized = rawEntry.replace(/\s+/gu, " ").trim();
|
|
28132
|
+
if (!normalized) {
|
|
28133
|
+
continue;
|
|
28134
|
+
}
|
|
28135
|
+
const bounded = normalized.slice(0, CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT);
|
|
28136
|
+
const entry = options.lowercase ? bounded.toLowerCase() : bounded;
|
|
28137
|
+
const key = entry.toLowerCase();
|
|
28138
|
+
if (seen.has(key)) {
|
|
28139
|
+
continue;
|
|
28140
|
+
}
|
|
28141
|
+
seen.add(key);
|
|
28142
|
+
entries.push(entry);
|
|
28143
|
+
if (entries.length >= CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT) {
|
|
28144
|
+
break;
|
|
28145
|
+
}
|
|
28146
|
+
}
|
|
28147
|
+
return entries;
|
|
28148
|
+
}
|
|
28149
|
+
function flattenPrimePackEntryValues(value) {
|
|
28150
|
+
if (Array.isArray(value)) {
|
|
28151
|
+
return value.flatMap((entry) => flattenPrimePackEntryValues(entry));
|
|
28152
|
+
}
|
|
28153
|
+
if (typeof value !== "string") {
|
|
28154
|
+
return [];
|
|
28155
|
+
}
|
|
28156
|
+
return value.split(/[\n,;]+/u);
|
|
28157
|
+
}
|
|
28158
|
+
function parsePrimePackImportEntries(value, key) {
|
|
28159
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
28160
|
+
return [];
|
|
28161
|
+
}
|
|
28162
|
+
const bounded = value.slice(0, CONTROL_TOWER_PRIME_PACK_IMPORT_LIMIT);
|
|
28163
|
+
const parsedJson = parsePrimePackImportJson(bounded, key);
|
|
28164
|
+
if (parsedJson.length > 0) {
|
|
28165
|
+
return parsedJson;
|
|
28166
|
+
}
|
|
28167
|
+
const rows = parsePrimePackImportRows(bounded);
|
|
28168
|
+
return rows.filter((row) => normalizePrimePackImportKey(row[0]) === key).flatMap((row) => row.slice(1));
|
|
28169
|
+
}
|
|
28170
|
+
function parsePrimePackImportJson(value, key) {
|
|
28171
|
+
const trimmed = value.trim();
|
|
28172
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
28173
|
+
return [];
|
|
28174
|
+
}
|
|
28175
|
+
try {
|
|
28176
|
+
const parsed = JSON.parse(trimmed);
|
|
28177
|
+
return extractPrimePackImportJsonValues(parsed, key);
|
|
28178
|
+
} catch {
|
|
28179
|
+
return [];
|
|
28180
|
+
}
|
|
28181
|
+
}
|
|
28182
|
+
function extractPrimePackImportJsonValues(value, key) {
|
|
28183
|
+
if (Array.isArray(value)) {
|
|
28184
|
+
return value.flatMap((entry) => extractPrimePackImportJsonValues(entry, key));
|
|
28185
|
+
}
|
|
28186
|
+
if (!isRecord(value)) {
|
|
28187
|
+
return [];
|
|
28188
|
+
}
|
|
28189
|
+
const direct = value[key];
|
|
28190
|
+
if (Array.isArray(direct)) {
|
|
28191
|
+
return direct.filter((entry) => typeof entry === "string");
|
|
28192
|
+
}
|
|
28193
|
+
if (typeof direct === "string") {
|
|
28194
|
+
return [direct];
|
|
28195
|
+
}
|
|
28196
|
+
const type = normalizePrimePackImportKey(value.type);
|
|
28197
|
+
const term = value.value ?? value.term ?? value.pattern ?? value.phrase ?? value.domain ?? value.destination;
|
|
28198
|
+
if (type === key && typeof term === "string") {
|
|
28199
|
+
return [term];
|
|
28200
|
+
}
|
|
28201
|
+
return [];
|
|
28202
|
+
}
|
|
28203
|
+
function parsePrimePackImportRows(value) {
|
|
28204
|
+
return value.split(/\r?\n/u).map((line) => splitPrimePackImportRow(line).map((cell) => cell.trim())).filter((row) => row.some(Boolean));
|
|
28205
|
+
}
|
|
28206
|
+
function splitPrimePackImportRow(line) {
|
|
28207
|
+
const delimiter = line.includes(" ") ? " " : ",";
|
|
28208
|
+
return line.split(delimiter);
|
|
28209
|
+
}
|
|
28210
|
+
function normalizePrimePackImportKey(value) {
|
|
28211
|
+
if (typeof value !== "string") {
|
|
28212
|
+
return void 0;
|
|
28213
|
+
}
|
|
28214
|
+
const normalized = value.trim().toLowerCase().replace(/[\s-]+/gu, "_");
|
|
28215
|
+
switch (normalized) {
|
|
28216
|
+
case "sensitive":
|
|
28217
|
+
case "sensitive_term":
|
|
28218
|
+
case "sensitive_terms":
|
|
28219
|
+
case "term":
|
|
28220
|
+
case "terms":
|
|
28221
|
+
return "sensitive_terms";
|
|
28222
|
+
case "approved_domain":
|
|
28223
|
+
case "approved_domains":
|
|
28224
|
+
case "allow_domain":
|
|
28225
|
+
case "allowed_domain":
|
|
28226
|
+
case "domain":
|
|
28227
|
+
return "approved_domains";
|
|
28228
|
+
case "blocked_destination":
|
|
28229
|
+
case "blocked_destinations":
|
|
28230
|
+
case "blocked_domain":
|
|
28231
|
+
case "deny_destination":
|
|
28232
|
+
case "destination":
|
|
28233
|
+
return "blocked_destinations";
|
|
28234
|
+
case "escalation":
|
|
28235
|
+
case "escalation_phrase":
|
|
28236
|
+
case "escalation_phrases":
|
|
28237
|
+
case "confirmation_phrase":
|
|
28238
|
+
case "phrase":
|
|
28239
|
+
return "escalation_phrases";
|
|
28240
|
+
default:
|
|
28241
|
+
return void 0;
|
|
28242
|
+
}
|
|
28243
|
+
}
|
|
28244
|
+
function buildPrimePackPreview(inputs) {
|
|
28245
|
+
const totalInputs = inputs.sensitive_terms.length + inputs.approved_domains.length + inputs.blocked_destinations.length + inputs.escalation_phrases.length;
|
|
28246
|
+
const examples = [
|
|
28247
|
+
inputs.sensitive_terms.length > 0 ? `${inputs.sensitive_terms.length} sensitive term(s) will be flagged before prompt send.` : "",
|
|
28248
|
+
inputs.blocked_destinations.length > 0 ? `${inputs.blocked_destinations.length} blocked destination rule(s) will stop risky sharing.` : "",
|
|
28249
|
+
inputs.escalation_phrases.length > 0 ? `${inputs.escalation_phrases.length} escalation phrase(s) will require confirmation.` : "",
|
|
28250
|
+
inputs.approved_domains.length > 0 ? `${inputs.approved_domains.length} approved domain(s) will lower false positives.` : ""
|
|
28251
|
+
].filter(Boolean);
|
|
28252
|
+
return {
|
|
28253
|
+
total_inputs: totalInputs,
|
|
28254
|
+
flag_rules: inputs.sensitive_terms.length,
|
|
28255
|
+
block_rules: inputs.blocked_destinations.length,
|
|
28256
|
+
confirm_rules: inputs.escalation_phrases.length,
|
|
28257
|
+
examples
|
|
28258
|
+
};
|
|
28259
|
+
}
|
|
28260
|
+
function buildPrimePackDeployReceipt(receipt) {
|
|
28261
|
+
return {
|
|
28262
|
+
receipt_id: receipt.receipt_id,
|
|
28263
|
+
action: "deploy",
|
|
28264
|
+
created_at: receipt.created_at,
|
|
28265
|
+
receipt_hash: receipt.receipt_hash,
|
|
28266
|
+
raw_prompt_content_included: false,
|
|
28267
|
+
token_printed: false
|
|
28268
|
+
};
|
|
28269
|
+
}
|
|
28270
|
+
function buildPrimePackDeployGuardianReceipt(inputs, preview, transactionId, timestamp) {
|
|
28271
|
+
const core = {
|
|
28272
|
+
transaction_id: transactionId,
|
|
28273
|
+
action: "deploy",
|
|
28274
|
+
input_counts: buildPrimePackInputCounts(inputs),
|
|
28275
|
+
preview,
|
|
28276
|
+
input_hashes: buildPrimePackInputHashes(inputs),
|
|
28277
|
+
created_at: timestamp,
|
|
28278
|
+
raw_content_included: false
|
|
28279
|
+
};
|
|
28280
|
+
return {
|
|
28281
|
+
schema_version: PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION,
|
|
28282
|
+
receipt_id: `receipt:${transactionId}:prime-pack-deploy`,
|
|
28283
|
+
transaction_id: transactionId,
|
|
28284
|
+
receipt_type: "policy",
|
|
28285
|
+
disposition: "allow",
|
|
28286
|
+
policy_basis: [
|
|
28287
|
+
"prime-pack-deploy-metadata-only",
|
|
28288
|
+
`inputs:${preview.total_inputs}`,
|
|
28289
|
+
`flag_rules:${preview.flag_rules}`,
|
|
28290
|
+
`block_rules:${preview.block_rules}`,
|
|
28291
|
+
`confirm_rules:${preview.confirm_rules}`
|
|
28292
|
+
],
|
|
28293
|
+
receipt_hash: `sha256:${digest4(JSON.stringify(core))}`,
|
|
28294
|
+
raw_content_included: false,
|
|
28295
|
+
created_at: timestamp,
|
|
28296
|
+
summary: `Guardian deployed a local Prime Pack with ${preview.total_inputs} metadata rule input(s).`
|
|
28297
|
+
};
|
|
28298
|
+
}
|
|
28299
|
+
function buildPrimePackDeployEvent(inputs, preview, transactionId, timestamp, receipt) {
|
|
28300
|
+
const payload = {
|
|
28301
|
+
action: "deploy",
|
|
28302
|
+
input_counts: buildPrimePackInputCounts(inputs),
|
|
28303
|
+
input_hashes: buildPrimePackInputHashes(inputs),
|
|
28304
|
+
preview,
|
|
28305
|
+
raw_content_included: false
|
|
28306
|
+
};
|
|
28307
|
+
return {
|
|
28308
|
+
event_id: `event:${transactionId}:prime-pack-deploy`,
|
|
28309
|
+
transaction_id: transactionId,
|
|
28310
|
+
event_type: "policy_decided",
|
|
28311
|
+
occurred_at: timestamp,
|
|
28312
|
+
actor: "guardian-daemon",
|
|
28313
|
+
summary: "Prime Pack deployment recorded with metadata-only hashes.",
|
|
28314
|
+
payload_hash: `sha256:${digest4(JSON.stringify(payload))}`,
|
|
28315
|
+
receipt_id: receipt.receipt_id,
|
|
28316
|
+
graph_refs: ["policy-pack:prime-pack", `receipt:${receipt.receipt_id}`],
|
|
28317
|
+
redaction_state: "metadata_only"
|
|
28318
|
+
};
|
|
28319
|
+
}
|
|
28320
|
+
function buildPrimePackInputCounts(inputs) {
|
|
28321
|
+
return {
|
|
28322
|
+
sensitive_terms: inputs.sensitive_terms.length,
|
|
28323
|
+
approved_domains: inputs.approved_domains.length,
|
|
28324
|
+
blocked_destinations: inputs.blocked_destinations.length,
|
|
28325
|
+
escalation_phrases: inputs.escalation_phrases.length
|
|
28326
|
+
};
|
|
28327
|
+
}
|
|
28328
|
+
function buildPrimePackInputHashes(inputs) {
|
|
28329
|
+
return {
|
|
28330
|
+
sensitive_terms: inputs.sensitive_terms.map((entry) => `sha256:${digest4(entry)}`),
|
|
28331
|
+
approved_domains: inputs.approved_domains.map((entry) => `sha256:${digest4(entry)}`),
|
|
28332
|
+
blocked_destinations: inputs.blocked_destinations.map((entry) => `sha256:${digest4(entry)}`),
|
|
28333
|
+
escalation_phrases: inputs.escalation_phrases.map((entry) => `sha256:${digest4(entry)}`)
|
|
28334
|
+
};
|
|
28335
|
+
}
|
|
26955
28336
|
function parsePromptCoachMode2(value) {
|
|
26956
28337
|
return PROMPT_COACH_MODES2.includes(value) ? value : void 0;
|
|
26957
28338
|
}
|
|
@@ -27031,7 +28412,7 @@ function writeHtml(response, statusCode, body, options = {}) {
|
|
|
27031
28412
|
response.writeHead(statusCode, {
|
|
27032
28413
|
"Content-Type": "text/html; charset=utf-8",
|
|
27033
28414
|
"Referrer-Policy": "no-referrer",
|
|
27034
|
-
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'",
|
|
28415
|
+
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; connect-src 'self'",
|
|
27035
28416
|
"X-Content-Type-Options": "nosniff"
|
|
27036
28417
|
});
|
|
27037
28418
|
response.end(body);
|
|
@@ -27058,19 +28439,168 @@ function closeGuardianDaemonServer(server) {
|
|
|
27058
28439
|
} catch {
|
|
27059
28440
|
}
|
|
27060
28441
|
}
|
|
27061
|
-
function parseGuardianUrl(url2) {
|
|
27062
|
-
if (!url2) {
|
|
27063
|
-
return void 0;
|
|
27064
|
-
}
|
|
27065
|
-
try {
|
|
27066
|
-
return new URL(url2, "http://127.0.0.1");
|
|
27067
|
-
} catch {
|
|
27068
|
-
return void 0;
|
|
27069
|
-
}
|
|
28442
|
+
function parseGuardianUrl(url2) {
|
|
28443
|
+
if (!url2) {
|
|
28444
|
+
return void 0;
|
|
28445
|
+
}
|
|
28446
|
+
try {
|
|
28447
|
+
return new URL(url2, "http://127.0.0.1");
|
|
28448
|
+
} catch {
|
|
28449
|
+
return void 0;
|
|
28450
|
+
}
|
|
28451
|
+
}
|
|
28452
|
+
function buildControlTowerViewModel(profile, receipts, searchGraph, learningEvents, localDataStatus, releaseDoctorStatus, adminNotices = [], marketplaceRequests = []) {
|
|
28453
|
+
const recentReceipts = receipts.slice().sort((left, right) => right.created_at.localeCompare(left.created_at)).slice(0, CONTROL_TOWER_RECEIPT_LIMIT);
|
|
28454
|
+
const blocked = recentReceipts.filter((receipt) => receipt.disposition === "block").length;
|
|
28455
|
+
const flagged = recentReceipts.filter(
|
|
28456
|
+
(receipt) => receipt.disposition === "warn" || receipt.disposition === "confirm"
|
|
28457
|
+
).length;
|
|
28458
|
+
const allowed = recentReceipts.filter(
|
|
28459
|
+
(receipt) => receipt.disposition === "allow" || receipt.disposition === "rewrite"
|
|
28460
|
+
).length;
|
|
28461
|
+
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";
|
|
28462
|
+
const protectedState = releaseDoctorStatus.doctor.checkCounts.fail === 0 && privacyDefaults === "on";
|
|
28463
|
+
return {
|
|
28464
|
+
ok: true,
|
|
28465
|
+
schema_version: "contextecf/project-guardian-control-tower-view-model/v1",
|
|
28466
|
+
surfaces: [
|
|
28467
|
+
{
|
|
28468
|
+
id: "personal",
|
|
28469
|
+
label: "Personal Guardian",
|
|
28470
|
+
audience: "Individual users",
|
|
28471
|
+
purpose: "Protection status, prompt coaching, privacy, receipts, and browser connection."
|
|
28472
|
+
},
|
|
28473
|
+
{
|
|
28474
|
+
id: "admin",
|
|
28475
|
+
label: "Guardian Admin",
|
|
28476
|
+
audience: "Organizations",
|
|
28477
|
+
purpose: "Groups, policy packs, marketplace approvals, Prime Pack, scope, and audit."
|
|
28478
|
+
},
|
|
28479
|
+
{
|
|
28480
|
+
id: "power_user",
|
|
28481
|
+
label: "Power User",
|
|
28482
|
+
audience: "Developers and technical operators",
|
|
28483
|
+
purpose: "MCP, local data, doctor checks, receipts, and release evidence."
|
|
28484
|
+
},
|
|
28485
|
+
{
|
|
28486
|
+
id: "marketplace_contributor",
|
|
28487
|
+
label: "Marketplace Contributor",
|
|
28488
|
+
audience: "Policy pack creators",
|
|
28489
|
+
purpose: "Submission metadata, examples, license terms, and review state."
|
|
28490
|
+
}
|
|
28491
|
+
],
|
|
28492
|
+
personal: {
|
|
28493
|
+
protectionLabel: protectedState ? "You're Protected" : "Needs Review",
|
|
28494
|
+
protectionDetail: protectedState ? "Guardian is running locally with metadata-only privacy defaults." : "Guardian is running, but one or more install checks need attention.",
|
|
28495
|
+
privacyDefaults,
|
|
28496
|
+
activePolicyPacks: [...profile.policy_packs.active],
|
|
28497
|
+
activityCounts: { blocked, flagged, allowed }
|
|
28498
|
+
},
|
|
28499
|
+
admin: {
|
|
28500
|
+
organizationLabel: "Example organization deployment",
|
|
28501
|
+
notices: adminNotices.slice(0, CONTROL_TOWER_ADMIN_NOTICE_LIMIT),
|
|
28502
|
+
marketplaceRequests: marketplaceRequests.slice(0, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT),
|
|
28503
|
+
managedGroups: buildControlTowerDeploymentGroups(profile),
|
|
28504
|
+
primePack: {
|
|
28505
|
+
status: "draft_ready",
|
|
28506
|
+
supportedInputs: ["typed terms", "text upload", "CSV upload", "JSON upload"],
|
|
28507
|
+
deployable: true,
|
|
28508
|
+
receiptRequired: true
|
|
28509
|
+
}
|
|
28510
|
+
},
|
|
28511
|
+
marketplace: buildControlTowerMarketplaceRows(profile, marketplaceRequests),
|
|
28512
|
+
releaseAutomation: {
|
|
28513
|
+
repository: GUARDIAN_RELEASE_REPOSITORY,
|
|
28514
|
+
workflow: GUARDIAN_RELEASE_DISPATCH_WORKFLOW,
|
|
28515
|
+
npmPackage: GUARDIAN_NPM_PACKAGE_NAME,
|
|
28516
|
+
registry: GUARDIAN_NPM_REGISTRY,
|
|
28517
|
+
chromeExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
28518
|
+
dryRunCommand: CONTROL_TOWER_DRY_RUN_COMMAND_TEMPLATE,
|
|
28519
|
+
publishCommand: CONTROL_TOWER_RELEASE_COMMAND_TEMPLATE,
|
|
28520
|
+
humanIntervention: [
|
|
28521
|
+
"Chrome Web Store upload/review remains a store-console step until Google approval APIs are wired.",
|
|
28522
|
+
"Native installer signing still requires platform signing credentials.",
|
|
28523
|
+
`Latest doctor status: ${releaseDoctorStatus.doctor.overall}.`,
|
|
28524
|
+
`Search Graph has ${searchGraph.nodes.length} node(s); Learning Graph has ${learningEvents.length} event(s).`,
|
|
28525
|
+
`Local data encryption: ${localDataStatus.encryptionStatus}.`
|
|
28526
|
+
]
|
|
28527
|
+
},
|
|
28528
|
+
raw_content_included: false,
|
|
28529
|
+
token_printed: false
|
|
28530
|
+
};
|
|
28531
|
+
}
|
|
28532
|
+
function buildControlTowerDeploymentGroups(profile) {
|
|
28533
|
+
return [
|
|
28534
|
+
{
|
|
28535
|
+
id: "group:individual-default",
|
|
28536
|
+
label: "Personal default",
|
|
28537
|
+
users: 1,
|
|
28538
|
+
posture: profile.posture_profile.selected,
|
|
28539
|
+
packs: [...profile.policy_packs.active]
|
|
28540
|
+
},
|
|
28541
|
+
{
|
|
28542
|
+
id: "group:employees",
|
|
28543
|
+
label: "Employees",
|
|
28544
|
+
users: 24,
|
|
28545
|
+
posture: "balanced",
|
|
28546
|
+
packs: ["privacy-first", "executive"]
|
|
28547
|
+
},
|
|
28548
|
+
{
|
|
28549
|
+
id: "group:technical-users",
|
|
28550
|
+
label: "Technical users",
|
|
28551
|
+
users: 8,
|
|
28552
|
+
posture: "guided",
|
|
28553
|
+
packs: ["privacy-first", "developer"]
|
|
28554
|
+
},
|
|
28555
|
+
{
|
|
28556
|
+
id: "group:high-risk-functions",
|
|
28557
|
+
label: "High-risk functions",
|
|
28558
|
+
users: 5,
|
|
28559
|
+
posture: "high_assurance",
|
|
28560
|
+
packs: ["privacy-first", "finance"]
|
|
28561
|
+
}
|
|
28562
|
+
];
|
|
28563
|
+
}
|
|
28564
|
+
function buildControlTowerMarketplaceRows(profile, marketplaceRequests = []) {
|
|
28565
|
+
const activePacks = new Set(profile.policy_packs.active);
|
|
28566
|
+
const requestsByManifest = new Map(
|
|
28567
|
+
marketplaceRequests.map((request) => [request.marketplace_manifest_id, request])
|
|
28568
|
+
);
|
|
28569
|
+
return GUARDIAN_POLICY_PACK_MARKETPLACE_CATALOG.map((manifest) => {
|
|
28570
|
+
const request = requestsByManifest.get(manifest.marketplace_manifest_id);
|
|
28571
|
+
const connected = activePacks.has(manifest.pack_id);
|
|
28572
|
+
return {
|
|
28573
|
+
packId: manifest.pack_id,
|
|
28574
|
+
name: manifest.display_name,
|
|
28575
|
+
version: manifest.pack_version,
|
|
28576
|
+
audience: manifest.audiences.join(", "),
|
|
28577
|
+
category: manifest.policy_domains.join(", "),
|
|
28578
|
+
publisher: manifest.publisher_display_name,
|
|
28579
|
+
price: manifest.price_model,
|
|
28580
|
+
licenseRef: manifest.license_ref,
|
|
28581
|
+
supportRef: manifest.support_ref ?? "Not provided",
|
|
28582
|
+
state: connected ? "connected" : request?.status ?? manifest.listing_status,
|
|
28583
|
+
permissions: [
|
|
28584
|
+
manifest.provider_rules_summary,
|
|
28585
|
+
manifest.source_rules_summary,
|
|
28586
|
+
manifest.app_permission_summary
|
|
28587
|
+
],
|
|
28588
|
+
protectedDataTypes: manifest.protected_data_types,
|
|
28589
|
+
examples: [
|
|
28590
|
+
manifest.short_description,
|
|
28591
|
+
manifest.confirmation_rules_summary,
|
|
28592
|
+
manifest.data_handling_summary
|
|
28593
|
+
],
|
|
28594
|
+
risk: manifest.limitations.length > 0 ? manifest.limitations[0] : "Standard false-positive review recommended before wide deployment.",
|
|
28595
|
+
requestState: connected ? "connected" : request?.status ?? "not_requested",
|
|
28596
|
+
requestReceiptId: request?.receipt_id,
|
|
28597
|
+
requestedAt: request?.requested_at
|
|
28598
|
+
};
|
|
28599
|
+
});
|
|
27070
28600
|
}
|
|
27071
28601
|
function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { nodes: [], edges: [] }, learningEvents = [], localDataStatus = createEmptyControlTowerLocalDataStatus(profile), releaseDoctorStatus = createEmptyControlTowerReleaseDoctorStatus(
|
|
27072
28602
|
profile
|
|
27073
|
-
)) {
|
|
28603
|
+
), adminNotices = [], marketplaceRequests = []) {
|
|
27074
28604
|
const policyPacks = profile.policy_packs.available.length;
|
|
27075
28605
|
const activePolicyPacks = profile.policy_packs.active.join(", ") || "none";
|
|
27076
28606
|
const recentReceipts = receipts.slice().sort((left, right) => right.created_at.localeCompare(left.created_at)).slice(0, CONTROL_TOWER_RECEIPT_LIMIT);
|
|
@@ -27248,9 +28778,28 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27248
28778
|
<button type="submit" class="provider-pill" aria-label="Revoke ${escapeHtml(providerDisplayName(row.id))}"><span></span>${escapeHtml(providerDisplayName(row.id))}</button>
|
|
27249
28779
|
</form>`
|
|
27250
28780
|
).join("\n");
|
|
28781
|
+
const viewModel = buildControlTowerViewModel(
|
|
28782
|
+
profile,
|
|
28783
|
+
recentReceipts,
|
|
28784
|
+
searchGraph,
|
|
28785
|
+
learningEvents,
|
|
28786
|
+
localDataStatus,
|
|
28787
|
+
releaseDoctorStatus,
|
|
28788
|
+
adminNotices,
|
|
28789
|
+
marketplaceRequests
|
|
28790
|
+
);
|
|
28791
|
+
const roleCardsHtml = renderControlTowerRoleCards(viewModel);
|
|
28792
|
+
const adminNoticeBarHtml = renderAdminNoticeBar(viewModel.admin.notices);
|
|
28793
|
+
const adminPanelsHtml = renderGuardianAdminSurface(
|
|
28794
|
+
viewModel,
|
|
28795
|
+
profile,
|
|
28796
|
+
recentReceipts,
|
|
28797
|
+
appPermissionRows
|
|
28798
|
+
);
|
|
28799
|
+
const localGuardianStatus = renderLocalGuardianStatus(profile.runtime.daemon_status);
|
|
27251
28800
|
const rows = [
|
|
27252
28801
|
["Profile", profile.profile_dir],
|
|
27253
|
-
["
|
|
28802
|
+
["Local Guardian", localGuardianStatus],
|
|
27254
28803
|
[
|
|
27255
28804
|
"Posture Profile",
|
|
27256
28805
|
`${profile.posture_profile.selected}; managed baseline ${profile.posture_profile.managed_baseline_id ?? "none"}`
|
|
@@ -27418,6 +28967,161 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27418
28967
|
background: rgba(15, 23, 42, 0.78);
|
|
27419
28968
|
font-size: 16px;
|
|
27420
28969
|
}
|
|
28970
|
+
.surface-tabs, .role-grid, .admin-grid, .marketplace-grid {
|
|
28971
|
+
display: grid;
|
|
28972
|
+
gap: 12px;
|
|
28973
|
+
}
|
|
28974
|
+
.surface-tabs {
|
|
28975
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
28976
|
+
margin: 28px 0 16px;
|
|
28977
|
+
}
|
|
28978
|
+
.surface-tab {
|
|
28979
|
+
display: grid;
|
|
28980
|
+
gap: 6px;
|
|
28981
|
+
width: 100%;
|
|
28982
|
+
min-height: 78px;
|
|
28983
|
+
padding: 15px 16px;
|
|
28984
|
+
border-color: rgba(148, 163, 184, 0.2);
|
|
28985
|
+
background: rgba(13, 24, 40, 0.84);
|
|
28986
|
+
color: #eff6ff;
|
|
28987
|
+
text-align: left;
|
|
28988
|
+
}
|
|
28989
|
+
.surface-tab strong, .role-card strong, .admin-card strong { font-size: 17px; }
|
|
28990
|
+
.surface-tab span, .role-card span, .admin-card span, .marketplace-card span {
|
|
28991
|
+
color: #98a7bc;
|
|
28992
|
+
font-size: 13px;
|
|
28993
|
+
line-height: 1.35;
|
|
28994
|
+
}
|
|
28995
|
+
.surface-tab.is-active, .surface-tab[aria-pressed="true"] {
|
|
28996
|
+
border-color: rgba(34, 197, 94, 0.5);
|
|
28997
|
+
background: rgba(6, 45, 37, 0.78);
|
|
28998
|
+
box-shadow: inset 0 0 0 1px rgba(34, 197, 94, 0.12);
|
|
28999
|
+
}
|
|
29000
|
+
.surface-panel[hidden] { display: none; }
|
|
29001
|
+
.role-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
29002
|
+
.role-card, .admin-card, .marketplace-card, .prime-pack-builder, .release-command-card {
|
|
29003
|
+
border: 1px solid rgba(148, 163, 184, 0.18);
|
|
29004
|
+
border-radius: 8px;
|
|
29005
|
+
background: rgba(11, 24, 40, 0.82);
|
|
29006
|
+
padding: 18px;
|
|
29007
|
+
}
|
|
29008
|
+
.admin-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
29009
|
+
.marketplace-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
29010
|
+
.marketplace-card h3, .admin-card h3, .prime-pack-builder h3, .release-command-card h3 {
|
|
29011
|
+
margin: 0 0 8px;
|
|
29012
|
+
color: #f6f8ff;
|
|
29013
|
+
font-size: 18px;
|
|
29014
|
+
}
|
|
29015
|
+
.marketplace-card p, .admin-card p, .prime-pack-builder p, .release-command-card p {
|
|
29016
|
+
margin-bottom: 12px;
|
|
29017
|
+
}
|
|
29018
|
+
.marketplace-detail-drawer {
|
|
29019
|
+
margin-top: 14px;
|
|
29020
|
+
border-top: 1px solid rgba(148, 163, 184, 0.16);
|
|
29021
|
+
padding-top: 12px;
|
|
29022
|
+
}
|
|
29023
|
+
.marketplace-detail-drawer summary {
|
|
29024
|
+
cursor: pointer;
|
|
29025
|
+
color: #dce7f7;
|
|
29026
|
+
font-size: 14px;
|
|
29027
|
+
font-weight: 800;
|
|
29028
|
+
}
|
|
29029
|
+
.marketplace-detail-drawer dl {
|
|
29030
|
+
display: grid;
|
|
29031
|
+
gap: 8px;
|
|
29032
|
+
margin: 12px 0 0;
|
|
29033
|
+
}
|
|
29034
|
+
.marketplace-detail-drawer dt {
|
|
29035
|
+
color: #f6f8ff;
|
|
29036
|
+
font-size: 13px;
|
|
29037
|
+
font-weight: 800;
|
|
29038
|
+
}
|
|
29039
|
+
.marketplace-detail-drawer dd {
|
|
29040
|
+
margin: 0;
|
|
29041
|
+
color: #98a7bc;
|
|
29042
|
+
font-size: 13px;
|
|
29043
|
+
line-height: 1.45;
|
|
29044
|
+
}
|
|
29045
|
+
.marketplace-request-form {
|
|
29046
|
+
display: grid;
|
|
29047
|
+
gap: 8px;
|
|
29048
|
+
margin-top: 14px;
|
|
29049
|
+
}
|
|
29050
|
+
.marketplace-request-form span,
|
|
29051
|
+
.marketplace-request-state {
|
|
29052
|
+
margin: 14px 0 0;
|
|
29053
|
+
color: #9fb0c6;
|
|
29054
|
+
font-size: 13px;
|
|
29055
|
+
line-height: 1.45;
|
|
29056
|
+
}
|
|
29057
|
+
.marketplace-request-list {
|
|
29058
|
+
display: grid;
|
|
29059
|
+
gap: 10px;
|
|
29060
|
+
margin-top: 16px;
|
|
29061
|
+
}
|
|
29062
|
+
.marketplace-request-card {
|
|
29063
|
+
display: grid;
|
|
29064
|
+
gap: 6px;
|
|
29065
|
+
padding: 12px 14px;
|
|
29066
|
+
border: 1px solid rgba(34, 197, 94, 0.28);
|
|
29067
|
+
border-radius: 8px;
|
|
29068
|
+
background: rgba(6, 45, 37, 0.52);
|
|
29069
|
+
}
|
|
29070
|
+
.marketplace-request-card span {
|
|
29071
|
+
color: #9fb0c6;
|
|
29072
|
+
font-size: 13px;
|
|
29073
|
+
}
|
|
29074
|
+
.metadata-list {
|
|
29075
|
+
display: grid;
|
|
29076
|
+
gap: 8px;
|
|
29077
|
+
margin: 14px 0 0;
|
|
29078
|
+
padding: 0;
|
|
29079
|
+
list-style: none;
|
|
29080
|
+
}
|
|
29081
|
+
.metadata-list li {
|
|
29082
|
+
display: flex;
|
|
29083
|
+
gap: 8px;
|
|
29084
|
+
justify-content: space-between;
|
|
29085
|
+
border-top: 1px solid rgba(148, 163, 184, 0.12);
|
|
29086
|
+
padding-top: 8px;
|
|
29087
|
+
color: #c8d2e3;
|
|
29088
|
+
font-size: 13px;
|
|
29089
|
+
}
|
|
29090
|
+
.metadata-list b { color: #eff6ff; }
|
|
29091
|
+
.builder-grid {
|
|
29092
|
+
display: grid;
|
|
29093
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
29094
|
+
gap: 12px;
|
|
29095
|
+
margin-top: 12px;
|
|
29096
|
+
}
|
|
29097
|
+
.button-row {
|
|
29098
|
+
display: flex;
|
|
29099
|
+
flex-wrap: wrap;
|
|
29100
|
+
gap: 10px;
|
|
29101
|
+
margin-top: 12px;
|
|
29102
|
+
}
|
|
29103
|
+
textarea {
|
|
29104
|
+
width: 100%;
|
|
29105
|
+
min-height: 104px;
|
|
29106
|
+
border: 1px solid rgba(148, 163, 184, 0.24);
|
|
29107
|
+
border-radius: 8px;
|
|
29108
|
+
background: rgba(8, 13, 24, 0.92);
|
|
29109
|
+
color: #f6f8ff;
|
|
29110
|
+
padding: 10px;
|
|
29111
|
+
font: inherit;
|
|
29112
|
+
resize: vertical;
|
|
29113
|
+
}
|
|
29114
|
+
.command-box {
|
|
29115
|
+
display: block;
|
|
29116
|
+
width: 100%;
|
|
29117
|
+
padding: 12px;
|
|
29118
|
+
border: 1px solid rgba(148, 163, 184, 0.2);
|
|
29119
|
+
border-radius: 8px;
|
|
29120
|
+
background: rgba(5, 10, 20, 0.88);
|
|
29121
|
+
color: #d8f8e7;
|
|
29122
|
+
overflow-x: auto;
|
|
29123
|
+
white-space: nowrap;
|
|
29124
|
+
}
|
|
27421
29125
|
.eyebrow {
|
|
27422
29126
|
width: fit-content;
|
|
27423
29127
|
margin-bottom: 16px;
|
|
@@ -27599,6 +29303,22 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27599
29303
|
}
|
|
27600
29304
|
.provider-pill:hover { background: rgba(30, 41, 59, 0.78); }
|
|
27601
29305
|
.provider-pill span { width: 8px; height: 8px; border-radius: 999px; background: #22c55e; }
|
|
29306
|
+
.notice-bar { display: grid; gap: 10px; margin: 20px 0 26px; }
|
|
29307
|
+
.notice-card {
|
|
29308
|
+
display: grid;
|
|
29309
|
+
gap: 6px;
|
|
29310
|
+
padding: 14px 16px;
|
|
29311
|
+
border: 1px solid rgba(96, 165, 250, 0.32);
|
|
29312
|
+
border-radius: 8px;
|
|
29313
|
+
color: #d8e1f0;
|
|
29314
|
+
background: rgba(15, 23, 42, 0.78);
|
|
29315
|
+
}
|
|
29316
|
+
.notice-card.warning { border-color: rgba(245, 158, 11, 0.46); background: rgba(39, 26, 9, 0.82); }
|
|
29317
|
+
.notice-card.success { border-color: rgba(34, 197, 94, 0.46); background: rgba(6, 45, 37, 0.82); }
|
|
29318
|
+
.notice-card strong { color: #f6f8ff; font-size: 16px; }
|
|
29319
|
+
.notice-card span { color: #91a1b8; font-size: 13px; font-weight: 700; }
|
|
29320
|
+
.notice-card p { margin: 0; color: #d8e1f0; line-height: 1.45; }
|
|
29321
|
+
.notice-form { display: grid; gap: 12px; }
|
|
27602
29322
|
.activity-heading { justify-content: space-between; gap: 16px; margin-bottom: 12px; }
|
|
27603
29323
|
.activity-heading h2 { margin: 0; }
|
|
27604
29324
|
.filter-chips { gap: 8px; flex-wrap: wrap; }
|
|
@@ -27619,6 +29339,14 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27619
29339
|
.chip:hover { color: #f5f7fb; background: rgba(30, 41, 59, 0.78); }
|
|
27620
29340
|
.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
29341
|
.activity-card[hidden], .empty-filter-state[hidden] { display: none; }
|
|
29342
|
+
.receipt-row.is-selected-receipt {
|
|
29343
|
+
outline: 2px solid rgba(56, 230, 120, 0.7);
|
|
29344
|
+
outline-offset: 4px;
|
|
29345
|
+
background: rgba(34, 197, 94, 0.08);
|
|
29346
|
+
}
|
|
29347
|
+
.receipt-row.is-selected-receipt details {
|
|
29348
|
+
border-radius: 8px;
|
|
29349
|
+
}
|
|
27622
29350
|
.empty-filter-state {
|
|
27623
29351
|
padding: 22px;
|
|
27624
29352
|
border: 1px dashed rgba(148, 163, 184, 0.24);
|
|
@@ -27676,6 +29404,7 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27676
29404
|
.protection-card { min-height: 320px; }
|
|
27677
29405
|
.activity-card { grid-template-columns: 48px 1fr; }
|
|
27678
29406
|
.activity-badge { grid-column: 2; width: fit-content; }
|
|
29407
|
+
.surface-tabs, .role-grid, .admin-grid, .marketplace-grid, .builder-grid { grid-template-columns: 1fr; }
|
|
27679
29408
|
section { padding: 16px; overflow-x: auto; }
|
|
27680
29409
|
table { min-width: 720px; }
|
|
27681
29410
|
.activity-heading { align-items: flex-start; flex-direction: column; }
|
|
@@ -27688,7 +29417,7 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27688
29417
|
<header class="topbar">
|
|
27689
29418
|
<div class="brand"><span class="brand-mark">G</span><span>Guardian</span></div>
|
|
27690
29419
|
<div class="topbar-actions">
|
|
27691
|
-
<span class="status-pill"><span class="status-dot"></span>${escapeHtml(
|
|
29420
|
+
<span class="status-pill"><span class="status-dot"></span>${escapeHtml(localGuardianStatus)}</span>
|
|
27692
29421
|
<span class="icon-button" aria-label="Settings">⚙</span>
|
|
27693
29422
|
</div>
|
|
27694
29423
|
</header>
|
|
@@ -27696,6 +29425,25 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27696
29425
|
<div class="eyebrow">Local-first status</div>
|
|
27697
29426
|
<h1>Project Guardian Control Tower</h1>
|
|
27698
29427
|
<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>
|
|
29428
|
+
${adminNoticeBarHtml}
|
|
29429
|
+
<div class="surface-tabs" aria-label="Control Tower surfaces">
|
|
29430
|
+
<button type="button" class="surface-tab is-active" data-surface-tab="personal" aria-pressed="true">
|
|
29431
|
+
<strong>Personal Guardian</strong>
|
|
29432
|
+
<span>Simple protection, privacy, coaching, browser connection, and receipts.</span>
|
|
29433
|
+
</button>
|
|
29434
|
+
<button type="button" class="surface-tab" data-surface-tab="admin" aria-pressed="false">
|
|
29435
|
+
<strong>Guardian Admin</strong>
|
|
29436
|
+
<span>Policy packs, marketplace approvals, Prime Pack, deployment, audit, and releases.</span>
|
|
29437
|
+
</button>
|
|
29438
|
+
</div>
|
|
29439
|
+
<section aria-labelledby="role-experiences">
|
|
29440
|
+
<h2 id="role-experiences">Role Experiences</h2>
|
|
29441
|
+
<p>Guardian ships one local brain with different surfaces for individuals, power users, enterprise admins, and marketplace contributors.</p>
|
|
29442
|
+
<div class="role-grid">
|
|
29443
|
+
${roleCardsHtml}
|
|
29444
|
+
</div>
|
|
29445
|
+
</section>
|
|
29446
|
+
<div class="surface-panel" data-surface-panel="personal">
|
|
27699
29447
|
<div class="hero-grid" aria-label="Guardian protection overview">
|
|
27700
29448
|
<article class="protection-card">
|
|
27701
29449
|
<div class="${protectionRingClass}"><div class="ring-core">G</div></div>
|
|
@@ -27717,6 +29465,7 @@ ${heroMetricCards}
|
|
|
27717
29465
|
</div>
|
|
27718
29466
|
</div>
|
|
27719
29467
|
<p class="activity-filter-status" data-activity-filter-status aria-live="polite">Showing all local Guardian activity.</p>
|
|
29468
|
+
<p class="activity-filter-status" data-guardian-time-zone-label>Times use your browser's local timezone.</p>
|
|
27720
29469
|
<div class="activity-list" data-activity-list>
|
|
27721
29470
|
${activityFeedHtml}
|
|
27722
29471
|
<div class="empty-filter-state" data-activity-empty hidden>No activity matches this filter yet.</div>
|
|
@@ -27885,7 +29634,7 @@ ${releaseDoctorRows}
|
|
|
27885
29634
|
</tbody>
|
|
27886
29635
|
</table>
|
|
27887
29636
|
</section>
|
|
27888
|
-
<section aria-labelledby="receipt-drilldowns">
|
|
29637
|
+
<section id="receipts" aria-labelledby="receipt-drilldowns">
|
|
27889
29638
|
<div class="activity-heading">
|
|
27890
29639
|
<h2 id="receipt-drilldowns">What Guardian did today</h2>
|
|
27891
29640
|
<div class="filter-chips" aria-label="Activity filters">
|
|
@@ -27895,7 +29644,7 @@ ${releaseDoctorRows}
|
|
|
27895
29644
|
<span class="chip">Allowed</span>
|
|
27896
29645
|
</div>
|
|
27897
29646
|
</div>
|
|
27898
|
-
<p><span class="visually-hidden">Activity & Receipts.</span>Inspect recent Guardian decisions without raw prompt, response, or local-action payload content.</p>
|
|
29647
|
+
<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
29648
|
<table aria-label="Guardian receipt drilldowns">
|
|
27900
29649
|
<thead>
|
|
27901
29650
|
<tr><th scope="col">Receipt</th><th scope="col">Type</th><th scope="col">Decision</th><th scope="col">Details</th></tr>
|
|
@@ -27975,9 +29724,47 @@ ${learningEventRows}
|
|
|
27975
29724
|
</section>
|
|
27976
29725
|
</div>
|
|
27977
29726
|
</details>
|
|
29727
|
+
</div>
|
|
29728
|
+
<div class="surface-panel" data-surface-panel="admin" hidden>
|
|
29729
|
+
${adminPanelsHtml}
|
|
29730
|
+
</div>
|
|
27978
29731
|
</main>
|
|
27979
29732
|
<script>
|
|
27980
29733
|
(() => {
|
|
29734
|
+
const surfaceTabs = Array.from(document.querySelectorAll('[data-surface-tab]'));
|
|
29735
|
+
const surfacePanels = Array.from(document.querySelectorAll('[data-surface-panel]'));
|
|
29736
|
+
const selectSurface = (surface) => {
|
|
29737
|
+
for (const tab of surfaceTabs) {
|
|
29738
|
+
const selected = tab.getAttribute('data-surface-tab') === surface;
|
|
29739
|
+
tab.classList.toggle('is-active', selected);
|
|
29740
|
+
tab.setAttribute('aria-pressed', selected ? 'true' : 'false');
|
|
29741
|
+
}
|
|
29742
|
+
for (const panel of surfacePanels) {
|
|
29743
|
+
panel.hidden = panel.getAttribute('data-surface-panel') !== surface;
|
|
29744
|
+
}
|
|
29745
|
+
};
|
|
29746
|
+
for (const tab of surfaceTabs) {
|
|
29747
|
+
tab.addEventListener('click', () => selectSurface(tab.getAttribute('data-surface-tab') || 'personal'));
|
|
29748
|
+
}
|
|
29749
|
+
const hydrateViewModel = async () => {
|
|
29750
|
+
const controller = new AbortController();
|
|
29751
|
+
const timeoutId = window.setTimeout(() => controller.abort(), 2500);
|
|
29752
|
+
try {
|
|
29753
|
+
const response = await fetch('/v1/guardian/control-tower/view-model', {
|
|
29754
|
+
headers: { accept: 'application/json' },
|
|
29755
|
+
signal: controller.signal,
|
|
29756
|
+
});
|
|
29757
|
+
if (!response.ok) return;
|
|
29758
|
+
const body = await response.json();
|
|
29759
|
+
if (!body || body.ok !== true) return;
|
|
29760
|
+
document.documentElement.setAttribute('data-view-model', body.schema_version || 'loaded');
|
|
29761
|
+
} catch {
|
|
29762
|
+
// Keep the server-rendered Control Tower if local hydration is unavailable.
|
|
29763
|
+
} finally {
|
|
29764
|
+
window.clearTimeout(timeoutId);
|
|
29765
|
+
}
|
|
29766
|
+
};
|
|
29767
|
+
hydrateViewModel();
|
|
27981
29768
|
const filters = Array.from(document.querySelectorAll('[data-activity-filter]'));
|
|
27982
29769
|
const list = document.querySelector('[data-activity-list]');
|
|
27983
29770
|
const emptyState = document.querySelector('[data-activity-empty]');
|
|
@@ -28013,6 +29800,85 @@ ${learningEventRows}
|
|
|
28013
29800
|
}
|
|
28014
29801
|
setFilterCopy(selectedFilter);
|
|
28015
29802
|
};
|
|
29803
|
+
const absoluteLocalTimeFormatter = new Intl.DateTimeFormat(navigator.language || undefined, {
|
|
29804
|
+
month: 'short',
|
|
29805
|
+
day: 'numeric',
|
|
29806
|
+
year: 'numeric',
|
|
29807
|
+
hour: 'numeric',
|
|
29808
|
+
minute: '2-digit',
|
|
29809
|
+
timeZoneName: 'short',
|
|
29810
|
+
});
|
|
29811
|
+
const relativeTimeFormatter = new Intl.RelativeTimeFormat(navigator.language || undefined, {
|
|
29812
|
+
numeric: 'auto',
|
|
29813
|
+
});
|
|
29814
|
+
const formatAbsoluteLocalTime = (createdAt) => {
|
|
29815
|
+
const date = new Date(createdAt);
|
|
29816
|
+
if (Number.isNaN(date.getTime())) return createdAt || '';
|
|
29817
|
+
return absoluteLocalTimeFormatter.format(date);
|
|
29818
|
+
};
|
|
29819
|
+
const formatLocalTime = (createdAt) => {
|
|
29820
|
+
const date = new Date(createdAt);
|
|
29821
|
+
if (Number.isNaN(date.getTime())) return createdAt || '';
|
|
29822
|
+
const diffMs = date.getTime() - Date.now();
|
|
29823
|
+
const absMs = Math.abs(diffMs);
|
|
29824
|
+
if (absMs < 60 * 1000) return 'just now';
|
|
29825
|
+
if (absMs < 60 * 60 * 1000) {
|
|
29826
|
+
return relativeTimeFormatter.format(Math.round(diffMs / (60 * 1000)), 'minute');
|
|
29827
|
+
}
|
|
29828
|
+
if (absMs < 24 * 60 * 60 * 1000) {
|
|
29829
|
+
return relativeTimeFormatter.format(Math.round(diffMs / (60 * 60 * 1000)), 'hour');
|
|
29830
|
+
}
|
|
29831
|
+
if (absMs < 7 * 24 * 60 * 60 * 1000) {
|
|
29832
|
+
return relativeTimeFormatter.format(Math.round(diffMs / (24 * 60 * 60 * 1000)), 'day');
|
|
29833
|
+
}
|
|
29834
|
+
return formatAbsoluteLocalTime(createdAt);
|
|
29835
|
+
};
|
|
29836
|
+
const hydrateLocalTimes = () => {
|
|
29837
|
+
for (const element of Array.from(document.querySelectorAll('[data-guardian-local-time]'))) {
|
|
29838
|
+
const createdAt = element.getAttribute('datetime') || element.getAttribute('data-created-at') || '';
|
|
29839
|
+
const formatted = formatLocalTime(createdAt);
|
|
29840
|
+
if (formatted) element.textContent = formatted;
|
|
29841
|
+
if (createdAt) {
|
|
29842
|
+
element.setAttribute('title', 'Your local time: ' + formatAbsoluteLocalTime(createdAt) + ' \xB7 Stored by Guardian as ' + createdAt);
|
|
29843
|
+
}
|
|
29844
|
+
}
|
|
29845
|
+
};
|
|
29846
|
+
const hydrateLocalTimeZoneLabels = () => {
|
|
29847
|
+
let timeZone = '';
|
|
29848
|
+
try {
|
|
29849
|
+
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || '';
|
|
29850
|
+
} catch {
|
|
29851
|
+
timeZone = '';
|
|
29852
|
+
}
|
|
29853
|
+
const label = timeZone
|
|
29854
|
+
? 'Times use your browser timezone: ' + timeZone + '.'
|
|
29855
|
+
: "Times use your browser's local timezone.";
|
|
29856
|
+
for (const element of Array.from(document.querySelectorAll('[data-guardian-time-zone-label]'))) {
|
|
29857
|
+
element.textContent = label;
|
|
29858
|
+
}
|
|
29859
|
+
};
|
|
29860
|
+
const receiptIdFromHash = () => {
|
|
29861
|
+
const hash = window.location.hash || '';
|
|
29862
|
+
if (!hash.startsWith('#receipt=')) return '';
|
|
29863
|
+
try {
|
|
29864
|
+
return decodeURIComponent(hash.slice('#receipt='.length));
|
|
29865
|
+
} catch {
|
|
29866
|
+
return hash.slice('#receipt='.length);
|
|
29867
|
+
}
|
|
29868
|
+
};
|
|
29869
|
+
const highlightReceiptFromHash = () => {
|
|
29870
|
+
const selectedReceiptId = receiptIdFromHash();
|
|
29871
|
+
let matchedRow = null;
|
|
29872
|
+
for (const row of Array.from(document.querySelectorAll('[data-receipt-row]'))) {
|
|
29873
|
+
const selected = Boolean(selectedReceiptId) && row.getAttribute('data-receipt-id') === selectedReceiptId;
|
|
29874
|
+
row.classList.toggle('is-selected-receipt', selected);
|
|
29875
|
+
if (selected) matchedRow = row;
|
|
29876
|
+
}
|
|
29877
|
+
if (!matchedRow) return;
|
|
29878
|
+
const details = matchedRow.querySelector('details');
|
|
29879
|
+
if (details) details.open = true;
|
|
29880
|
+
matchedRow.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
29881
|
+
};
|
|
28016
29882
|
const createActivityCard = (activity) => {
|
|
28017
29883
|
const card = document.createElement('article');
|
|
28018
29884
|
card.className = 'activity-card ' + activity.tone;
|
|
@@ -28026,7 +29892,16 @@ ${learningEventRows}
|
|
|
28026
29892
|
title.textContent = activity.title;
|
|
28027
29893
|
const meta = document.createElement('div');
|
|
28028
29894
|
meta.className = 'activity-meta';
|
|
28029
|
-
|
|
29895
|
+
if (activity.created_at) {
|
|
29896
|
+
const time = document.createElement('time');
|
|
29897
|
+
time.setAttribute('datetime', activity.created_at);
|
|
29898
|
+
time.setAttribute('data-guardian-local-time', '');
|
|
29899
|
+
time.textContent = formatLocalTime(activity.created_at);
|
|
29900
|
+
time.title = 'Your local time: ' + formatAbsoluteLocalTime(activity.created_at) + ' \xB7 Stored by Guardian as ' + activity.created_at;
|
|
29901
|
+
meta.append(time, ' \xB7 ' + (activity.receipt_type || 'receipt'));
|
|
29902
|
+
} else {
|
|
29903
|
+
meta.textContent = activity.meta;
|
|
29904
|
+
}
|
|
28030
29905
|
body.append(title, meta);
|
|
28031
29906
|
const badge = document.createElement('div');
|
|
28032
29907
|
badge.className = 'activity-badge ' + activity.tone;
|
|
@@ -28069,6 +29944,11 @@ ${learningEventRows}
|
|
|
28069
29944
|
applyFilter(filter.getAttribute('data-activity-filter') || 'all');
|
|
28070
29945
|
});
|
|
28071
29946
|
}
|
|
29947
|
+
window.addEventListener('hashchange', highlightReceiptFromHash);
|
|
29948
|
+
hydrateLocalTimeZoneLabels();
|
|
29949
|
+
hydrateLocalTimes();
|
|
29950
|
+
window.setInterval(hydrateLocalTimes, 60 * 1000);
|
|
29951
|
+
highlightReceiptFromHash();
|
|
28072
29952
|
})();
|
|
28073
29953
|
</script>
|
|
28074
29954
|
</body>
|
|
@@ -28205,6 +30085,120 @@ function renderReleaseDoctorRows(status) {
|
|
|
28205
30085
|
function renderDoctorCounts(doctor) {
|
|
28206
30086
|
return `${doctor.checkCounts.pass} pass; ${doctor.checkCounts.warn} warn; ${doctor.checkCounts.fail} fail`;
|
|
28207
30087
|
}
|
|
30088
|
+
async function readControlTowerAdminNotices(profileDir) {
|
|
30089
|
+
try {
|
|
30090
|
+
const raw = await readFile4(path4.join(profileDir, CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME), "utf8");
|
|
30091
|
+
const parsed = JSON.parse(raw);
|
|
30092
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.notices)) {
|
|
30093
|
+
return [];
|
|
30094
|
+
}
|
|
30095
|
+
return parsed.notices.map(parseControlTowerAdminNotice).filter((notice) => Boolean(notice)).slice(0, CONTROL_TOWER_ADMIN_NOTICE_LIMIT);
|
|
30096
|
+
} catch {
|
|
30097
|
+
return [];
|
|
30098
|
+
}
|
|
30099
|
+
}
|
|
30100
|
+
async function writeControlTowerAdminNotices(profileDir, notices) {
|
|
30101
|
+
await writeFile2(
|
|
30102
|
+
path4.join(profileDir, CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME),
|
|
30103
|
+
`${JSON.stringify(
|
|
30104
|
+
{
|
|
30105
|
+
schema_version: "contextecf/project-guardian-control-tower-admin-notices/v1",
|
|
30106
|
+
notices: notices.slice(0, CONTROL_TOWER_ADMIN_NOTICE_LIMIT),
|
|
30107
|
+
raw_content_included: false,
|
|
30108
|
+
token_printed: false
|
|
30109
|
+
},
|
|
30110
|
+
null,
|
|
30111
|
+
2
|
|
30112
|
+
)}
|
|
30113
|
+
`,
|
|
30114
|
+
"utf8"
|
|
30115
|
+
);
|
|
30116
|
+
}
|
|
30117
|
+
async function readControlTowerMarketplaceRequests(profileDir) {
|
|
30118
|
+
try {
|
|
30119
|
+
const raw = await readFile4(
|
|
30120
|
+
path4.join(profileDir, CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME),
|
|
30121
|
+
"utf8"
|
|
30122
|
+
);
|
|
30123
|
+
const parsed = JSON.parse(raw);
|
|
30124
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.requests)) {
|
|
30125
|
+
return [];
|
|
30126
|
+
}
|
|
30127
|
+
return parsed.requests.map(parseControlTowerMarketplaceRequest).filter((request) => Boolean(request)).slice(0, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT);
|
|
30128
|
+
} catch {
|
|
30129
|
+
return [];
|
|
30130
|
+
}
|
|
30131
|
+
}
|
|
30132
|
+
async function writeControlTowerMarketplaceRequests(profileDir, requests) {
|
|
30133
|
+
await writeFile2(
|
|
30134
|
+
path4.join(profileDir, CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME),
|
|
30135
|
+
`${JSON.stringify(
|
|
30136
|
+
{
|
|
30137
|
+
schema_version: "contextecf/project-guardian-marketplace-requests/v1",
|
|
30138
|
+
requests: requests.slice(0, CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT),
|
|
30139
|
+
raw_content_included: false,
|
|
30140
|
+
token_printed: false
|
|
30141
|
+
},
|
|
30142
|
+
null,
|
|
30143
|
+
2
|
|
30144
|
+
)}
|
|
30145
|
+
`,
|
|
30146
|
+
"utf8"
|
|
30147
|
+
);
|
|
30148
|
+
}
|
|
30149
|
+
async function writeControlTowerPrimePackDraft(profileDir, draft) {
|
|
30150
|
+
await writeFile2(
|
|
30151
|
+
path4.join(profileDir, CONTROL_TOWER_PRIME_PACK_FILE_NAME),
|
|
30152
|
+
`${JSON.stringify(draft, null, 2)}
|
|
30153
|
+
`,
|
|
30154
|
+
"utf8"
|
|
30155
|
+
);
|
|
30156
|
+
}
|
|
30157
|
+
function parseControlTowerAdminNotice(value) {
|
|
30158
|
+
if (!isRecord(value)) {
|
|
30159
|
+
return null;
|
|
30160
|
+
}
|
|
30161
|
+
const audience = parseControlTowerAdminNoticeAudience(value.audience);
|
|
30162
|
+
const tone = parseControlTowerAdminNoticeTone(value.tone);
|
|
30163
|
+
if (typeof value.id !== "string" || typeof value.title !== "string" || typeof value.message !== "string" || typeof value.created_at !== "string" || !audience || !tone) {
|
|
30164
|
+
return null;
|
|
30165
|
+
}
|
|
30166
|
+
return {
|
|
30167
|
+
id: value.id.slice(0, 80),
|
|
30168
|
+
title: value.title.slice(0, CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT),
|
|
30169
|
+
message: value.message.slice(0, CONTROL_TOWER_ADMIN_NOTICE_MESSAGE_LIMIT),
|
|
30170
|
+
audience,
|
|
30171
|
+
tone,
|
|
30172
|
+
created_at: value.created_at,
|
|
30173
|
+
receipt_id: typeof value.receipt_id === "string" ? value.receipt_id.slice(0, 160) : void 0,
|
|
30174
|
+
receipt_hash: typeof value.receipt_hash === "string" ? value.receipt_hash.slice(0, 80) : void 0,
|
|
30175
|
+
raw_content_included: false
|
|
30176
|
+
};
|
|
30177
|
+
}
|
|
30178
|
+
function parseControlTowerMarketplaceRequest(value) {
|
|
30179
|
+
if (!isRecord(value)) {
|
|
30180
|
+
return null;
|
|
30181
|
+
}
|
|
30182
|
+
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") {
|
|
30183
|
+
return null;
|
|
30184
|
+
}
|
|
30185
|
+
return {
|
|
30186
|
+
schema_version: "contextecf/project-guardian-marketplace-request/v1",
|
|
30187
|
+
request_id: value.request_id.slice(0, 120),
|
|
30188
|
+
pack_id: value.pack_id.slice(0, 120),
|
|
30189
|
+
marketplace_manifest_id: value.marketplace_manifest_id.slice(0, 160),
|
|
30190
|
+
pack_version: value.pack_version.slice(0, 40),
|
|
30191
|
+
pack_name: value.pack_name.slice(0, 120),
|
|
30192
|
+
publisher: value.publisher.slice(0, 120),
|
|
30193
|
+
action: "request_review",
|
|
30194
|
+
status: "requested_metadata_only",
|
|
30195
|
+
requested_at: value.requested_at,
|
|
30196
|
+
receipt_id: value.receipt_id.slice(0, 160),
|
|
30197
|
+
receipt_hash: value.receipt_hash.slice(0, 80),
|
|
30198
|
+
raw_content_included: false,
|
|
30199
|
+
token_printed: false
|
|
30200
|
+
};
|
|
30201
|
+
}
|
|
28208
30202
|
async function buildControlTowerLocalDataStatus(profile, env = process.env) {
|
|
28209
30203
|
const profileDir = profile.profile_dir;
|
|
28210
30204
|
const encryption = buildControlTowerEncryptionStatus(
|
|
@@ -28469,6 +30463,8 @@ function buildActivityItems(receipts, filter) {
|
|
|
28469
30463
|
tone: activityTone(receipt.disposition),
|
|
28470
30464
|
icon: activityPlainIcon(receipt.disposition),
|
|
28471
30465
|
title: receipt.summary,
|
|
30466
|
+
created_at: receipt.created_at,
|
|
30467
|
+
receipt_type: receipt.receipt_type,
|
|
28472
30468
|
meta: `${formatActivityTime(receipt.created_at)} \xB7 ${receipt.receipt_type}`,
|
|
28473
30469
|
label: activityLabel(receipt.disposition),
|
|
28474
30470
|
raw_content_included: false
|
|
@@ -28481,14 +30477,14 @@ function renderActivityCard(receipt) {
|
|
|
28481
30477
|
<div class="activity-icon">${activityIcon(receipt.disposition)}</div>
|
|
28482
30478
|
<div>
|
|
28483
30479
|
<div class="activity-title">${escapeHtml(receipt.summary)}</div>
|
|
28484
|
-
<div class="activity-meta">${
|
|
30480
|
+
<div class="activity-meta">${renderLocalTime(receipt.created_at)} \xB7 ${escapeHtml(receipt.receipt_type)}</div>
|
|
28485
30481
|
</div>
|
|
28486
30482
|
<div class="activity-badge ${tone}">${escapeHtml(activityLabel(receipt.disposition))}</div>
|
|
28487
30483
|
</article>`;
|
|
28488
30484
|
}
|
|
28489
30485
|
function renderReceiptRow(receipt) {
|
|
28490
30486
|
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>${
|
|
30487
|
+
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
30488
|
}
|
|
28493
30489
|
function activityTone(disposition) {
|
|
28494
30490
|
if (disposition === "block") return "block";
|
|
@@ -28517,17 +30513,321 @@ function activityFilterGroup(disposition) {
|
|
|
28517
30513
|
if (disposition === "warn" || disposition === "confirm") return "flagged";
|
|
28518
30514
|
return "allowed";
|
|
28519
30515
|
}
|
|
28520
|
-
function formatActivityTime(createdAt) {
|
|
30516
|
+
function formatActivityTime(createdAt, nowMs = Date.now()) {
|
|
28521
30517
|
const parsed = Date.parse(createdAt);
|
|
28522
30518
|
if (Number.isNaN(parsed)) {
|
|
28523
30519
|
return createdAt;
|
|
28524
30520
|
}
|
|
30521
|
+
const diffMs = parsed - nowMs;
|
|
30522
|
+
const absMs = Math.abs(diffMs);
|
|
30523
|
+
if (absMs < 60 * 1e3) {
|
|
30524
|
+
return "just now";
|
|
30525
|
+
}
|
|
30526
|
+
if (absMs < 60 * 60 * 1e3) {
|
|
30527
|
+
const minutes = Math.round(absMs / (60 * 1e3));
|
|
30528
|
+
return diffMs < 0 ? `${minutes} min ago` : `in ${minutes} min`;
|
|
30529
|
+
}
|
|
30530
|
+
if (absMs < 24 * 60 * 60 * 1e3) {
|
|
30531
|
+
const hours = Math.round(absMs / (60 * 60 * 1e3));
|
|
30532
|
+
return diffMs < 0 ? `${hours} hr ago` : `in ${hours} hr`;
|
|
30533
|
+
}
|
|
30534
|
+
if (absMs < 7 * 24 * 60 * 60 * 1e3) {
|
|
30535
|
+
const days = Math.round(absMs / (24 * 60 * 60 * 1e3));
|
|
30536
|
+
return diffMs < 0 ? `${days} day${days === 1 ? "" : "s"} ago` : `in ${days} day${days === 1 ? "" : "s"}`;
|
|
30537
|
+
}
|
|
28525
30538
|
return new Date(parsed).toLocaleString("en-US", {
|
|
28526
|
-
|
|
28527
|
-
|
|
28528
|
-
|
|
30539
|
+
month: "short",
|
|
30540
|
+
day: "numeric",
|
|
30541
|
+
year: "numeric",
|
|
30542
|
+
hour: "numeric",
|
|
30543
|
+
minute: "2-digit",
|
|
30544
|
+
timeZoneName: "short"
|
|
28529
30545
|
});
|
|
28530
30546
|
}
|
|
30547
|
+
function renderLocalTime(createdAt) {
|
|
30548
|
+
const parsed = Date.parse(createdAt);
|
|
30549
|
+
if (Number.isNaN(parsed)) {
|
|
30550
|
+
return escapeHtml(createdAt);
|
|
30551
|
+
}
|
|
30552
|
+
return `<time data-guardian-local-time datetime="${escapeHtml(createdAt)}" title="${escapeHtml(
|
|
30553
|
+
`Stored by Guardian as ${createdAt}`
|
|
30554
|
+
)}">${escapeHtml(formatActivityTime(createdAt))}</time>`;
|
|
30555
|
+
}
|
|
30556
|
+
function renderLocalGuardianStatus(status) {
|
|
30557
|
+
switch (status) {
|
|
30558
|
+
case "start_requested":
|
|
30559
|
+
return "Running";
|
|
30560
|
+
case "not_started_mvp":
|
|
30561
|
+
return "Not running";
|
|
30562
|
+
}
|
|
30563
|
+
}
|
|
30564
|
+
function renderControlTowerRoleCards(viewModel) {
|
|
30565
|
+
return viewModel.surfaces.map(
|
|
30566
|
+
(surface) => ` <article class="role-card">
|
|
30567
|
+
<strong>${escapeHtml(surface.label)}</strong>
|
|
30568
|
+
<span>${escapeHtml(surface.audience)}</span>
|
|
30569
|
+
<span>${escapeHtml(surface.purpose)}</span>
|
|
30570
|
+
</article>`
|
|
30571
|
+
).join("\n");
|
|
30572
|
+
}
|
|
30573
|
+
function renderAdminNoticeBar(notices) {
|
|
30574
|
+
if (notices.length === 0) {
|
|
30575
|
+
return "";
|
|
30576
|
+
}
|
|
30577
|
+
return ` <section class="notice-bar" aria-label="Guardian notifications">
|
|
30578
|
+
${notices.map((notice) => renderAdminNoticeCard(notice)).join("\n")}
|
|
30579
|
+
</section>`;
|
|
30580
|
+
}
|
|
30581
|
+
function renderAdminNoticeList(notices) {
|
|
30582
|
+
if (notices.length === 0) {
|
|
30583
|
+
return " <p>No local admin notices are published.</p>";
|
|
30584
|
+
}
|
|
30585
|
+
return ` <div class="notice-bar" aria-label="Current admin notices">
|
|
30586
|
+
${notices.map((notice) => renderAdminNoticeCard(notice)).join("\n")}
|
|
30587
|
+
</div>`;
|
|
30588
|
+
}
|
|
30589
|
+
function renderAdminNoticeCard(notice) {
|
|
30590
|
+
return ` <article class="notice-card ${escapeHtml(notice.tone)}">
|
|
30591
|
+
<strong>${escapeHtml(notice.title)}</strong>
|
|
30592
|
+
<p>${escapeHtml(notice.message)}</p>
|
|
30593
|
+
<span>${escapeHtml(adminNoticeAudienceLabel(notice.audience))} \xB7 ${renderLocalTime(notice.created_at)}</span>
|
|
30594
|
+
${notice.receipt_id ? `<code>${escapeHtml(notice.receipt_id)}</code>` : ""}
|
|
30595
|
+
</article>`;
|
|
30596
|
+
}
|
|
30597
|
+
function adminNoticeAudienceLabel(audience) {
|
|
30598
|
+
switch (audience) {
|
|
30599
|
+
case "admins":
|
|
30600
|
+
return "Admins";
|
|
30601
|
+
case "developers":
|
|
30602
|
+
return "Developers";
|
|
30603
|
+
case "all_users":
|
|
30604
|
+
return "Everyone";
|
|
30605
|
+
}
|
|
30606
|
+
}
|
|
30607
|
+
function renderGuardianAdminSurface(viewModel, profile, receipts, appPermissionRows) {
|
|
30608
|
+
return ` <section aria-labelledby="guardian-admin-overview">
|
|
30609
|
+
<h2 id="guardian-admin-overview">Guardian Admin</h2>
|
|
30610
|
+
<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>
|
|
30611
|
+
<div class="admin-grid">
|
|
30612
|
+
${renderAdminSummaryCards(viewModel, profile, receipts, appPermissionRows)}
|
|
30613
|
+
</div>
|
|
30614
|
+
</section>
|
|
30615
|
+
<section aria-labelledby="admin-notifications">
|
|
30616
|
+
<h2 id="admin-notifications">Admin Notifications</h2>
|
|
30617
|
+
<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>
|
|
30618
|
+
<form class="notice-form" method="post" action="/v1/guardian/admin/notice">
|
|
30619
|
+
<input type="hidden" name="action" value="publish">
|
|
30620
|
+
<label>Title<input name="title" maxlength="${CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT}" placeholder="Policy reminder" required></label>
|
|
30621
|
+
<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>
|
|
30622
|
+
<label>Audience<select name="audience"><option value="all_users">Everyone</option><option value="admins">Admins</option><option value="developers">Developers</option></select></label>
|
|
30623
|
+
<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>
|
|
30624
|
+
<button type="submit">Publish local notice</button>
|
|
30625
|
+
</form>
|
|
30626
|
+
<form method="post" action="/v1/guardian/admin/notice">
|
|
30627
|
+
<input type="hidden" name="action" value="clear">
|
|
30628
|
+
<button type="submit">Clear local notices</button>
|
|
30629
|
+
</form>
|
|
30630
|
+
${renderAdminNoticeList(viewModel.admin.notices)}
|
|
30631
|
+
</section>
|
|
30632
|
+
<section aria-labelledby="admin-policy-packs">
|
|
30633
|
+
<h2 id="admin-policy-packs">Policy Pack Management</h2>
|
|
30634
|
+
<p>Turn built-in packs on or off for this device now. In enterprise deployments, the same model becomes group assignment.</p>
|
|
30635
|
+
<table aria-label="Admin policy pack assignment">
|
|
30636
|
+
<thead>
|
|
30637
|
+
<tr><th scope="col">Pack</th><th scope="col">State</th><th scope="col">Local action</th></tr>
|
|
30638
|
+
</thead>
|
|
30639
|
+
<tbody>
|
|
30640
|
+
${profile.policy_packs.available.map((packId) => {
|
|
30641
|
+
const active = profile.policy_packs.active.includes(packId);
|
|
30642
|
+
const disabled = active && profile.policy_packs.active.length <= 1;
|
|
30643
|
+
return ` <tr><th scope="row">${escapeHtml(packId)}</th><td>${active ? "active" : "available"}</td><td>${renderPolicyPackForm(active ? "disable" : "enable", packId, disabled)}</td></tr>`;
|
|
30644
|
+
}).join("\n")}
|
|
30645
|
+
</tbody>
|
|
30646
|
+
</table>
|
|
30647
|
+
</section>
|
|
30648
|
+
<section aria-labelledby="admin-marketplace">
|
|
30649
|
+
<h2 id="admin-marketplace">Policy Pack Marketplace</h2>
|
|
30650
|
+
<p>Review pack metadata before connecting it to a deployment. Packs can start as request/approval metadata before billing automation is added.</p>
|
|
30651
|
+
<div class="marketplace-grid">
|
|
30652
|
+
${renderMarketplaceCards(viewModel)}
|
|
30653
|
+
</div>
|
|
30654
|
+
${renderMarketplaceRequestList(viewModel.admin.marketplaceRequests)}
|
|
30655
|
+
</section>
|
|
30656
|
+
<section aria-labelledby="admin-prime-pack">
|
|
30657
|
+
<h2 id="admin-prime-pack">Prime Pack Builder</h2>
|
|
30658
|
+
<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>
|
|
30659
|
+
<article class="prime-pack-builder">
|
|
30660
|
+
<h3>Custom protection inputs</h3>
|
|
30661
|
+
<form method="post" action="/v1/guardian/admin/prime-pack">
|
|
30662
|
+
<div class="builder-grid">
|
|
30663
|
+
<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>
|
|
30664
|
+
<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>
|
|
30665
|
+
<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>
|
|
30666
|
+
<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>
|
|
30667
|
+
</div>
|
|
30668
|
+
<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>
|
|
30669
|
+
<div class="button-row">
|
|
30670
|
+
<button type="submit" name="action" value="preview">Preview impact</button>
|
|
30671
|
+
<button type="submit" name="action" value="deploy">Deploy locally</button>
|
|
30672
|
+
</div>
|
|
30673
|
+
</form>
|
|
30674
|
+
<ul class="metadata-list">
|
|
30675
|
+
<li><b>Input formats</b><span>${escapeHtml(viewModel.admin.primePack.supportedInputs.join(", "))}</span></li>
|
|
30676
|
+
<li><b>Preview</b><span>stores counts and sample impact in <code>${escapeHtml(CONTROL_TOWER_PRIME_PACK_FILE_NAME)}</code></span></li>
|
|
30677
|
+
<li><b>Deploy</b><span>writes a local admin receipt before assignment</span></li>
|
|
30678
|
+
</ul>
|
|
30679
|
+
</article>
|
|
30680
|
+
</section>
|
|
30681
|
+
<section aria-labelledby="admin-deployment-scope">
|
|
30682
|
+
<h2 id="admin-deployment-scope">Deployment Scope</h2>
|
|
30683
|
+
<p>Assign posture and packs by user or group. Personal Guardian uses the first row; enterprise deployments add managed groups.</p>
|
|
30684
|
+
<table aria-label="Guardian deployment groups">
|
|
30685
|
+
<thead>
|
|
30686
|
+
<tr><th scope="col">Group</th><th scope="col">Users</th><th scope="col">Posture</th><th scope="col">Packs</th></tr>
|
|
30687
|
+
</thead>
|
|
30688
|
+
<tbody>
|
|
30689
|
+
${viewModel.admin.managedGroups.map(
|
|
30690
|
+
(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>`
|
|
30691
|
+
).join("\n")}
|
|
30692
|
+
</tbody>
|
|
30693
|
+
</table>
|
|
30694
|
+
</section>
|
|
30695
|
+
<section aria-labelledby="admin-release-automation">
|
|
30696
|
+
<h2 id="admin-release-automation">Release Automation</h2>
|
|
30697
|
+
<p>Use GitHub Actions as the default release path so package evidence, browser artifacts, Docker smoke tests, and npm publication stay repeatable.</p>
|
|
30698
|
+
<div class="builder-grid">
|
|
30699
|
+
<article class="release-command-card">
|
|
30700
|
+
<h3>Dry run</h3>
|
|
30701
|
+
<code class="command-box">${escapeHtml(viewModel.releaseAutomation.dryRunCommand)}</code>
|
|
30702
|
+
</article>
|
|
30703
|
+
<article class="release-command-card">
|
|
30704
|
+
<h3>Publish</h3>
|
|
30705
|
+
<code class="command-box">${escapeHtml(viewModel.releaseAutomation.publishCommand)}</code>
|
|
30706
|
+
</article>
|
|
30707
|
+
</div>
|
|
30708
|
+
<ul class="metadata-list">
|
|
30709
|
+
<li><b>Repository</b><span>${escapeHtml(viewModel.releaseAutomation.repository)}</span></li>
|
|
30710
|
+
<li><b>Workflow</b><span>${escapeHtml(viewModel.releaseAutomation.workflow)}</span></li>
|
|
30711
|
+
<li><b>NPM package</b><span>${escapeHtml(viewModel.releaseAutomation.npmPackage)}</span></li>
|
|
30712
|
+
<li><b>Chrome extension</b><span>${escapeHtml(viewModel.releaseAutomation.chromeExtensionId)}</span></li>
|
|
30713
|
+
<li><b>Still manual</b><span>${escapeHtml(viewModel.releaseAutomation.humanIntervention[0])}</span></li>
|
|
30714
|
+
</ul>
|
|
30715
|
+
</section>
|
|
30716
|
+
<section aria-labelledby="admin-audit">
|
|
30717
|
+
<h2 id="admin-audit">Receipts And Audit</h2>
|
|
30718
|
+
<p>Admins can review metadata-only decisions without exposing raw prompt text, raw responses, local action payloads, or runtime tokens.</p>
|
|
30719
|
+
<table aria-label="Guardian admin receipt audit">
|
|
30720
|
+
<thead>
|
|
30721
|
+
<tr><th scope="col">Receipt</th><th scope="col">Type</th><th scope="col">Decision</th><th scope="col">Details</th></tr>
|
|
30722
|
+
</thead>
|
|
30723
|
+
<tbody>
|
|
30724
|
+
${renderReceiptRows(receipts)}
|
|
30725
|
+
</tbody>
|
|
30726
|
+
</table>
|
|
30727
|
+
</section>`;
|
|
30728
|
+
}
|
|
30729
|
+
function renderAdminSummaryCards(viewModel, profile, receipts, appPermissionRows) {
|
|
30730
|
+
const cards = [
|
|
30731
|
+
{
|
|
30732
|
+
title: "Managed groups",
|
|
30733
|
+
value: String(viewModel.admin.managedGroups.length),
|
|
30734
|
+
detail: "Group-level posture and pack assignment model."
|
|
30735
|
+
},
|
|
30736
|
+
{
|
|
30737
|
+
title: "Marketplace packs",
|
|
30738
|
+
value: String(viewModel.marketplace.length),
|
|
30739
|
+
detail: "Metadata listings ready for approval review."
|
|
30740
|
+
},
|
|
30741
|
+
{
|
|
30742
|
+
title: "Prime Pack",
|
|
30743
|
+
value: viewModel.admin.primePack.status.replace(/_/gu, " "),
|
|
30744
|
+
detail: "Custom strings, domains, destinations, and escalation phrases."
|
|
30745
|
+
},
|
|
30746
|
+
{
|
|
30747
|
+
title: "Active local packs",
|
|
30748
|
+
value: String(profile.policy_packs.active.length),
|
|
30749
|
+
detail: profile.policy_packs.active.join(", ") || "none"
|
|
30750
|
+
},
|
|
30751
|
+
{
|
|
30752
|
+
title: "Governed apps",
|
|
30753
|
+
value: String(appPermissionRows.length),
|
|
30754
|
+
detail: "Local app access remains confirmation-gated."
|
|
30755
|
+
},
|
|
30756
|
+
{
|
|
30757
|
+
title: "Audit receipts",
|
|
30758
|
+
value: String(receipts.length),
|
|
30759
|
+
detail: "Metadata-only receipt chain."
|
|
30760
|
+
}
|
|
30761
|
+
];
|
|
30762
|
+
return cards.map(
|
|
30763
|
+
(card) => ` <article class="admin-card">
|
|
30764
|
+
<h3>${escapeHtml(card.title)}</h3>
|
|
30765
|
+
<strong>${escapeHtml(card.value)}</strong>
|
|
30766
|
+
<span>${escapeHtml(card.detail)}</span>
|
|
30767
|
+
</article>`
|
|
30768
|
+
).join("\n");
|
|
30769
|
+
}
|
|
30770
|
+
function renderMarketplaceCards(viewModel) {
|
|
30771
|
+
return viewModel.marketplace.map(
|
|
30772
|
+
(pack) => ` <article class="marketplace-card" data-marketplace-pack-id="${escapeHtml(pack.packId)}">
|
|
30773
|
+
<h3>${escapeHtml(pack.name)}</h3>
|
|
30774
|
+
<p>${escapeHtml(pack.examples[0] ?? "Policy pack metadata listing.")}</p>
|
|
30775
|
+
<ul class="metadata-list">
|
|
30776
|
+
<li><b>Audience</b><span>${escapeHtml(pack.audience)}</span></li>
|
|
30777
|
+
<li><b>Category</b><span>${escapeHtml(pack.category)}</span></li>
|
|
30778
|
+
<li><b>Maintainer</b><span>${escapeHtml(pack.publisher)}</span></li>
|
|
30779
|
+
<li><b>Version</b><span>${escapeHtml(pack.version)}</span></li>
|
|
30780
|
+
<li><b>Price/license</b><span>${escapeHtml(pack.price)}</span></li>
|
|
30781
|
+
<li><b>Approval state</b><span>${escapeHtml(pack.state)}</span></li>
|
|
30782
|
+
<li><b>False-positive risk</b><span>${escapeHtml(pack.risk)}</span></li>
|
|
30783
|
+
</ul>
|
|
30784
|
+
${renderMarketplaceRequestAction(pack)}
|
|
30785
|
+
<details class="marketplace-detail-drawer">
|
|
30786
|
+
<summary>Review details before connecting</summary>
|
|
30787
|
+
<dl>
|
|
30788
|
+
<dt>Permissions requested</dt>
|
|
30789
|
+
<dd>${escapeHtml(pack.permissions.join(" "))}</dd>
|
|
30790
|
+
<dt>Protected data</dt>
|
|
30791
|
+
<dd>${escapeHtml(pack.protectedDataTypes.join(", "))}</dd>
|
|
30792
|
+
<dt>Examples</dt>
|
|
30793
|
+
<dd>${escapeHtml(pack.examples.join(" "))}</dd>
|
|
30794
|
+
<dt>License reference</dt>
|
|
30795
|
+
<dd><code>${escapeHtml(pack.licenseRef)}</code></dd>
|
|
30796
|
+
<dt>Support reference</dt>
|
|
30797
|
+
<dd><code>${escapeHtml(pack.supportRef)}</code></dd>
|
|
30798
|
+
</dl>
|
|
30799
|
+
</details>
|
|
30800
|
+
</article>`
|
|
30801
|
+
).join("\n");
|
|
30802
|
+
}
|
|
30803
|
+
function renderMarketplaceRequestAction(pack) {
|
|
30804
|
+
if (pack.requestState === "connected") {
|
|
30805
|
+
return ' <p class="marketplace-request-state">Connected locally through active policy packs.</p>';
|
|
30806
|
+
}
|
|
30807
|
+
if (pack.requestState === "requested_metadata_only") {
|
|
30808
|
+
return ` <p class="marketplace-request-state">Review requested ${pack.requestedAt ? renderLocalTime(pack.requestedAt) : "locally"}.</p>`;
|
|
30809
|
+
}
|
|
30810
|
+
return ` <form class="marketplace-request-form" method="post" action="/v1/guardian/admin/marketplace-request">
|
|
30811
|
+
<input type="hidden" name="action" value="request_review">
|
|
30812
|
+
<input type="hidden" name="packId" value="${escapeHtml(pack.packId)}">
|
|
30813
|
+
<button type="submit">Request review</button>
|
|
30814
|
+
<span>Writes a local receipt; paid pack connection remains approval-gated.</span>
|
|
30815
|
+
</form>`;
|
|
30816
|
+
}
|
|
30817
|
+
function renderMarketplaceRequestList(requests) {
|
|
30818
|
+
if (requests.length === 0) {
|
|
30819
|
+
return " <p>No marketplace pack reviews have been requested on this device yet.</p>";
|
|
30820
|
+
}
|
|
30821
|
+
return ` <div class="marketplace-request-list" aria-label="Marketplace review requests">
|
|
30822
|
+
${requests.map(
|
|
30823
|
+
(request) => ` <article class="marketplace-request-card">
|
|
30824
|
+
<strong>${escapeHtml(request.pack_name)}</strong>
|
|
30825
|
+
<span>${escapeHtml(request.publisher)} \xB7 ${renderLocalTime(request.requested_at)}</span>
|
|
30826
|
+
<code>${escapeHtml(request.receipt_id)}</code>
|
|
30827
|
+
</article>`
|
|
30828
|
+
).join("\n")}
|
|
30829
|
+
</div>`;
|
|
30830
|
+
}
|
|
28531
30831
|
function providerDisplayName(providerId) {
|
|
28532
30832
|
if (providerId === "chatgpt") return "ChatGPT";
|
|
28533
30833
|
if (providerId === "claude") return "Claude";
|
|
@@ -28823,7 +31123,7 @@ function listen(server, host, port) {
|
|
|
28823
31123
|
});
|
|
28824
31124
|
});
|
|
28825
31125
|
}
|
|
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;
|
|
31126
|
+
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
31127
|
var init_daemon = __esm({
|
|
28828
31128
|
"packages/guardian-cli/src/daemon.ts"() {
|
|
28829
31129
|
"use strict";
|
|
@@ -28839,12 +31139,25 @@ var init_daemon = __esm({
|
|
|
28839
31139
|
CONTROL_TOWER_GRAPH_NODE_LIMIT = 12;
|
|
28840
31140
|
CONTROL_TOWER_GRAPH_EDGE_LIMIT = 16;
|
|
28841
31141
|
CONTROL_TOWER_LEARNING_EVENT_LIMIT = 12;
|
|
31142
|
+
CONTROL_TOWER_ADMIN_NOTICE_LIMIT = 5;
|
|
31143
|
+
CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME = "admin-notices.json";
|
|
31144
|
+
CONTROL_TOWER_ADMIN_NOTICE_TITLE_LIMIT = 80;
|
|
31145
|
+
CONTROL_TOWER_ADMIN_NOTICE_MESSAGE_LIMIT = 240;
|
|
31146
|
+
CONTROL_TOWER_MARKETPLACE_REQUEST_LIMIT = 20;
|
|
31147
|
+
CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME = "marketplace-requests.json";
|
|
31148
|
+
CONTROL_TOWER_PRIME_PACK_FILE_NAME = "prime-pack-draft.json";
|
|
31149
|
+
CONTROL_TOWER_PRIME_PACK_VALUE_LIMIT = 120;
|
|
31150
|
+
CONTROL_TOWER_PRIME_PACK_ENTRY_LIMIT = 100;
|
|
31151
|
+
CONTROL_TOWER_PRIME_PACK_IMPORT_LIMIT = 24 * 1024;
|
|
28842
31152
|
CONTROL_TOWER_DATA_FILE_NAMES = [
|
|
28843
31153
|
"profile.json",
|
|
28844
31154
|
"policy-packs.json",
|
|
28845
31155
|
"posture-profile.json",
|
|
28846
31156
|
"preferences.json",
|
|
28847
31157
|
"permissions.json",
|
|
31158
|
+
CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME,
|
|
31159
|
+
CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME,
|
|
31160
|
+
CONTROL_TOWER_PRIME_PACK_FILE_NAME,
|
|
28848
31161
|
"runtime-token"
|
|
28849
31162
|
];
|
|
28850
31163
|
CONTROL_TOWER_DATA_DIRECTORY_NAMES = ["ecl", "receipts", "mcp"];
|
|
@@ -28855,6 +31168,8 @@ var init_daemon = __esm({
|
|
|
28855
31168
|
"restore",
|
|
28856
31169
|
"delete"
|
|
28857
31170
|
];
|
|
31171
|
+
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`;
|
|
31172
|
+
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
31173
|
}
|
|
28859
31174
|
});
|
|
28860
31175
|
|