@contextecf/guardian-cli 0.1.5 → 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 +2448 -128
- package/dist/packages/guardian-cli/src/index.js +665 -86
- package/dist/packages/guardian-cli/src/runtime.d.ts +49 -4
- package/package.json +1 -1
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
|
|
4
|
+
var __esm = (fn, res, err) => function __init() {
|
|
5
|
+
if (err) throw err[0];
|
|
6
|
+
try {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
throw err = [e], e;
|
|
10
|
+
}
|
|
6
11
|
};
|
|
7
12
|
var __export = (target, all) => {
|
|
8
13
|
for (var name in all)
|
|
@@ -73,7 +78,7 @@ var init_core = __esm({
|
|
|
73
78
|
NEVER = /* @__PURE__ */ Object.freeze({
|
|
74
79
|
status: "aborted"
|
|
75
80
|
});
|
|
76
|
-
$brand = Symbol("zod_brand");
|
|
81
|
+
$brand = /* @__PURE__ */ Symbol("zod_brand");
|
|
77
82
|
$ZodAsyncError = class extends Error {
|
|
78
83
|
constructor() {
|
|
79
84
|
super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);
|
|
@@ -10242,8 +10247,8 @@ function registry() {
|
|
|
10242
10247
|
var _a2, $output, $input, $ZodRegistry, globalRegistry;
|
|
10243
10248
|
var init_registries = __esm({
|
|
10244
10249
|
"node_modules/zod/v4/core/registries.js"() {
|
|
10245
|
-
$output = Symbol("ZodOutput");
|
|
10246
|
-
$input = Symbol("ZodInput");
|
|
10250
|
+
$output = /* @__PURE__ */ Symbol("ZodOutput");
|
|
10251
|
+
$input = /* @__PURE__ */ Symbol("ZodInput");
|
|
10247
10252
|
$ZodRegistry = class {
|
|
10248
10253
|
constructor() {
|
|
10249
10254
|
this._map = /* @__PURE__ */ new WeakMap();
|
|
@@ -17056,6 +17061,8 @@ async function runGuardianCli(argv, options = {}) {
|
|
|
17056
17061
|
return runReleaseCommand(argv.slice(1), flags, options);
|
|
17057
17062
|
case "open":
|
|
17058
17063
|
return runControlTowerOpenCommand(flags, options);
|
|
17064
|
+
case "connect":
|
|
17065
|
+
return runExtensionConnectCommand(flags, options);
|
|
17059
17066
|
case "status":
|
|
17060
17067
|
write(stdout, await statusGuardian(flags, options));
|
|
17061
17068
|
return 0;
|
|
@@ -17067,6 +17074,14 @@ async function runGuardianCli(argv, options = {}) {
|
|
|
17067
17074
|
return runLaunchCommand(flags, options);
|
|
17068
17075
|
case "stop":
|
|
17069
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
|
+
}
|
|
17070
17085
|
case "daemon":
|
|
17071
17086
|
return runDaemonCommand(argv.slice(1), flags, options);
|
|
17072
17087
|
case "service":
|
|
@@ -17089,6 +17104,9 @@ async function runGuardianCli(argv, options = {}) {
|
|
|
17089
17104
|
return runPrivacyCommand(argv.slice(1), flags, options);
|
|
17090
17105
|
case "mcp":
|
|
17091
17106
|
return runMcpCommand(argv.slice(1), flags, options);
|
|
17107
|
+
case "coach":
|
|
17108
|
+
case "prompt-coach":
|
|
17109
|
+
return runCoachCommand(argv.slice(1), flags, options);
|
|
17092
17110
|
case "policy":
|
|
17093
17111
|
return runPolicyCommand(argv.slice(1), flags, options);
|
|
17094
17112
|
case "marketplace":
|
|
@@ -17111,7 +17129,7 @@ async function runGuardianCli(argv, options = {}) {
|
|
|
17111
17129
|
case "help":
|
|
17112
17130
|
case "--help":
|
|
17113
17131
|
case "-h":
|
|
17114
|
-
write(stdout, `${HELP_TEXT}
|
|
17132
|
+
write(stdout, `${flags.has("advanced") ? ADVANCED_HELP_TEXT : HELP_TEXT}
|
|
17115
17133
|
`);
|
|
17116
17134
|
return 0;
|
|
17117
17135
|
default:
|
|
@@ -17190,11 +17208,13 @@ async function installGuardian(flags, options = {}) {
|
|
|
17190
17208
|
"guardian start",
|
|
17191
17209
|
"guardian open",
|
|
17192
17210
|
"guardian mcp install",
|
|
17193
|
-
"guardian daemon pair --json",
|
|
17194
17211
|
"guardian service install",
|
|
17195
17212
|
"guardian key-store install --yes",
|
|
17196
|
-
"guardian
|
|
17197
|
-
|
|
17213
|
+
"guardian connect"
|
|
17214
|
+
],
|
|
17215
|
+
advancedCommands: [
|
|
17216
|
+
`guardian extension native-host install --extension-id=${GUARDIAN_HALO_CHROME_EXTENSION_ID}`,
|
|
17217
|
+
"guardian pair --json"
|
|
17198
17218
|
]
|
|
17199
17219
|
};
|
|
17200
17220
|
return flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
@@ -17207,7 +17227,8 @@ async function installGuardian(flags, options = {}) {
|
|
|
17207
17227
|
`First run: ${GUARDIAN_FIRST_RUN_COMMAND}`,
|
|
17208
17228
|
`Open Control Tower: ${GUARDIAN_OPEN_CONTROL_TOWER_COMMAND}`,
|
|
17209
17229
|
`No global install: ${GUARDIAN_NPX_SETUP_COMMAND}`,
|
|
17210
|
-
"Next: guardian setup;
|
|
17230
|
+
"Next: guardian setup; open ContextECF Halo and click Connect; guardian open",
|
|
17231
|
+
"Help: run guardian doctor if setup needs attention."
|
|
17211
17232
|
].join("\n")}
|
|
17212
17233
|
`;
|
|
17213
17234
|
}
|
|
@@ -17215,8 +17236,9 @@ async function runSetupCommand(flags, options) {
|
|
|
17215
17236
|
const stdout = options.stdout ?? process.stdout;
|
|
17216
17237
|
const stderr = options.stderr ?? process.stderr;
|
|
17217
17238
|
const home = resolveGuardianHome(options.env, options.platform);
|
|
17218
|
-
const
|
|
17219
|
-
|
|
17239
|
+
const requestedExtensionId = stringFlag(flags, "extension-id") ?? stringFlag(flags, "id");
|
|
17240
|
+
const extensionId = requestedExtensionId ?? GUARDIAN_HALO_CHROME_EXTENSION_ID;
|
|
17241
|
+
if (requestedExtensionId && !isValidChromeExtensionId(requestedExtensionId)) {
|
|
17220
17242
|
write(
|
|
17221
17243
|
stderr,
|
|
17222
17244
|
"Invalid --extension-id. Chrome extension ids must be 32 lowercase letters from a-p.\n"
|
|
@@ -17272,10 +17294,80 @@ async function runSetupCommand(flags, options) {
|
|
|
17272
17294
|
}
|
|
17273
17295
|
launch = JSON.parse(launchOutput);
|
|
17274
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`;
|
|
17275
17300
|
const resolvedControlTowerUrl = resolveControlTowerUrl(/* @__PURE__ */ new Map(), options.env, profile);
|
|
17276
|
-
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
|
+
}
|
|
17277
17309
|
const setupUrl = buildControlTowerSetupUrl(controlTowerUrl);
|
|
17278
|
-
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
|
+
}
|
|
17279
17371
|
const result = {
|
|
17280
17372
|
schema_version: "contextecf/project-guardian-setup/v1",
|
|
17281
17373
|
success: true,
|
|
@@ -17325,10 +17417,14 @@ async function runSetupCommand(flags, options) {
|
|
|
17325
17417
|
},
|
|
17326
17418
|
browserSetup: {
|
|
17327
17419
|
setupUrl,
|
|
17328
|
-
extensionId
|
|
17420
|
+
extensionId,
|
|
17421
|
+
defaultExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
17329
17422
|
opened: false,
|
|
17330
17423
|
tokenPrinted: false,
|
|
17331
|
-
nativeHostCommand
|
|
17424
|
+
nativeHostCommand,
|
|
17425
|
+
connectInstruction: "Open ContextECF Halo and click Connect. No setup code is needed.",
|
|
17426
|
+
manualFallbackCommand: "guardian pair --json",
|
|
17427
|
+
nativeHost: browserHelper
|
|
17332
17428
|
},
|
|
17333
17429
|
nextCommands: [
|
|
17334
17430
|
"guardian status",
|
|
@@ -17337,12 +17433,10 @@ async function runSetupCommand(flags, options) {
|
|
|
17337
17433
|
"guardian policy list",
|
|
17338
17434
|
"guardian posture list",
|
|
17339
17435
|
"guardian preferences show",
|
|
17340
|
-
"guardian extension open-setup",
|
|
17341
|
-
"guardian daemon pair --json",
|
|
17342
17436
|
"guardian service install",
|
|
17343
|
-
"guardian key-store install --yes"
|
|
17344
|
-
|
|
17345
|
-
]
|
|
17437
|
+
"guardian key-store install --yes"
|
|
17438
|
+
],
|
|
17439
|
+
advancedCommands: ["guardian extension connect", nativeHostCommand, "guardian pair --json"]
|
|
17346
17440
|
};
|
|
17347
17441
|
write(
|
|
17348
17442
|
stdout,
|
|
@@ -17355,8 +17449,11 @@ async function runSetupCommand(flags, options) {
|
|
|
17355
17449
|
result.launch.opened ? `Opening: ${result.launch.controlTowerUrl}` : void 0,
|
|
17356
17450
|
`MCP artifacts: ${result.mcp.writtenFiles.length > 0 ? result.mcp.writtenFiles.join(", ") : "skipped" in result.mcp && result.mcp.skipped ? "skipped" : "none"}`,
|
|
17357
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.",
|
|
17358
17454
|
"Token printed: no",
|
|
17359
|
-
`Next: ${result.nextCommands.join("; ")}
|
|
17455
|
+
`Next: ${result.nextCommands.join("; ")}`,
|
|
17456
|
+
"Help: run guardian connect if the browser says Local Guardian is not connected."
|
|
17360
17457
|
].filter(Boolean).join("\n")}
|
|
17361
17458
|
`
|
|
17362
17459
|
);
|
|
@@ -17431,8 +17528,8 @@ function buildGuardianDistributionReadiness(npmPreviewReady) {
|
|
|
17431
17528
|
},
|
|
17432
17529
|
aiToolIntegration: {
|
|
17433
17530
|
mcpInstallCommand: "guardian mcp install --client=all --write",
|
|
17434
|
-
browserSetupCommand: "guardian
|
|
17435
|
-
nativeHostInstallCommand:
|
|
17531
|
+
browserSetupCommand: "guardian connect",
|
|
17532
|
+
nativeHostInstallCommand: `guardian extension native-host install --extension-id=${GUARDIAN_HALO_CHROME_EXTENSION_ID}`
|
|
17436
17533
|
},
|
|
17437
17534
|
browserStoreHandoff: {
|
|
17438
17535
|
status: npmPreviewReady ? "operator_handoff_ready_not_submitted" : "blocked_until_preview_ready",
|
|
@@ -17456,7 +17553,7 @@ function buildGuardianDistributionReadiness(npmPreviewReady) {
|
|
|
17456
17553
|
boundaries: [
|
|
17457
17554
|
"Use dry_run=true before public publication.",
|
|
17458
17555
|
"dry_run=false requires the repository secret NPM_TOKEN and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
17459
|
-
"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.",
|
|
17460
17557
|
"GitHub Actions OIDC/provenance applies to CI release context; manual local publishes are recorded separately and should not claim CI provenance."
|
|
17461
17558
|
]
|
|
17462
17559
|
},
|
|
@@ -17477,12 +17574,16 @@ function buildGuardianReleaseDispatchCommand(dryRun) {
|
|
|
17477
17574
|
});
|
|
17478
17575
|
}
|
|
17479
17576
|
function buildGuardianReleaseDispatchCommandFor(input2) {
|
|
17577
|
+
const publishAuth = input2.publishAuth ?? "token";
|
|
17578
|
+
const browserStorePublish = input2.browserStorePublish ?? "manual";
|
|
17480
17579
|
return [
|
|
17481
17580
|
`gh workflow run ${GUARDIAN_RELEASE_DISPATCH_WORKFLOW} \\`,
|
|
17482
17581
|
` -f release_version=${input2.releaseVersion} \\`,
|
|
17483
17582
|
` -f channels=${input2.channels} \\`,
|
|
17484
17583
|
` -f dry_run=${input2.dryRun ? "true" : "false"} \\`,
|
|
17485
|
-
` -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}`
|
|
17486
17587
|
].join("\n");
|
|
17487
17588
|
}
|
|
17488
17589
|
async function runReleaseCommand(argv, flags, options) {
|
|
@@ -17589,11 +17690,23 @@ async function runReleaseCommand(argv, flags, options) {
|
|
|
17589
17690
|
}
|
|
17590
17691
|
const dryRun = flags.has("publish") ? false : booleanFlag(flags, "dry-run", true);
|
|
17591
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
|
+
}
|
|
17592
17703
|
const result = buildGuardianReleaseDispatchCommandResult({
|
|
17593
17704
|
releaseVersion,
|
|
17594
17705
|
channels,
|
|
17595
17706
|
dryRun,
|
|
17596
|
-
runDockerSmoke
|
|
17707
|
+
runDockerSmoke,
|
|
17708
|
+
publishAuth,
|
|
17709
|
+
browserStorePublish
|
|
17597
17710
|
});
|
|
17598
17711
|
write(stdout, renderGuardianReleaseDispatchCommand(result, flags));
|
|
17599
17712
|
return 0;
|
|
@@ -17661,6 +17774,7 @@ function renderGuardianReleaseHelp(result, flags) {
|
|
|
17661
17774
|
`;
|
|
17662
17775
|
}
|
|
17663
17776
|
function buildGuardianReleaseDispatchCommandResult(input2) {
|
|
17777
|
+
const npmLaneSelected = input2.channels === "all" || input2.channels === "npm";
|
|
17664
17778
|
return {
|
|
17665
17779
|
schema_version: "contextecf/project-guardian-release-dispatch-command/v1",
|
|
17666
17780
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
@@ -17669,13 +17783,21 @@ function buildGuardianReleaseDispatchCommandResult(input2) {
|
|
|
17669
17783
|
channels: input2.channels,
|
|
17670
17784
|
dryRun: input2.dryRun,
|
|
17671
17785
|
runDockerSmoke: input2.runDockerSmoke,
|
|
17786
|
+
publishAuth: input2.publishAuth,
|
|
17787
|
+
browserStorePublish: input2.browserStorePublish,
|
|
17672
17788
|
command: buildGuardianReleaseDispatchCommandFor(input2),
|
|
17673
|
-
...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
|
+
} : {},
|
|
17674
17793
|
provenance: "github_actions_oidc_supported",
|
|
17675
17794
|
boundaries: [
|
|
17676
17795
|
"Use dry_run=true before public publication.",
|
|
17677
|
-
"dry_run=false requires the repository secret NPM_TOKEN and must not print npm tokens, OTP values, or .npmrc contents.",
|
|
17678
|
-
"
|
|
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.",
|
|
17679
17801
|
"A release-dispatch command is operator guidance, not production-gate proof."
|
|
17680
17802
|
],
|
|
17681
17803
|
tokenPrinted: false,
|
|
@@ -17694,7 +17816,10 @@ function renderGuardianReleaseDispatchCommand(result, flags) {
|
|
|
17694
17816
|
`Workflow: ${result.workflow}`,
|
|
17695
17817
|
`Channels: ${result.channels}`,
|
|
17696
17818
|
`Docker smoke: ${result.runDockerSmoke ? "yes" : "no"}`,
|
|
17819
|
+
`Publish auth: ${result.publishAuth}`,
|
|
17820
|
+
`Browser store publish: ${result.browserStorePublish}`,
|
|
17697
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",
|
|
17698
17823
|
"Command:",
|
|
17699
17824
|
result.command,
|
|
17700
17825
|
"Boundaries:",
|
|
@@ -17709,6 +17834,7 @@ function buildGuardianBrowserStoreHandoffCommandResult() {
|
|
|
17709
17834
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
17710
17835
|
status: "chrome_distribution_proven_next_submission_pack_ready",
|
|
17711
17836
|
primaryStore: "chrome_web_store",
|
|
17837
|
+
publicListingUrl: GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL,
|
|
17712
17838
|
productionGate: {
|
|
17713
17839
|
id: "browser-store-distribution",
|
|
17714
17840
|
status: "proven",
|
|
@@ -17775,6 +17901,7 @@ function renderGuardianBrowserStoreHandoffCommand(result, flags) {
|
|
|
17775
17901
|
"Project Guardian Browser Store Handoff",
|
|
17776
17902
|
`Status: ${result.status}`,
|
|
17777
17903
|
`Primary store: ${result.primaryStore}`,
|
|
17904
|
+
`Public listing: ${result.publicListingUrl}`,
|
|
17778
17905
|
`Production gate: ${result.productionGate.id} (${result.productionGate.status})`,
|
|
17779
17906
|
"1. Build submission pack:",
|
|
17780
17907
|
result.commands.submissionPack,
|
|
@@ -17795,15 +17922,23 @@ function renderGuardianBrowserStoreHandoffCommand(result, flags) {
|
|
|
17795
17922
|
`;
|
|
17796
17923
|
}
|
|
17797
17924
|
function buildGuardianReleaseSecretSetup() {
|
|
17925
|
+
const chromeWebStoreSecretMatcher = GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES.join("|");
|
|
17798
17926
|
return {
|
|
17799
17927
|
schema_version: "contextecf/project-guardian-release-secret-setup/v1",
|
|
17800
17928
|
cli_version: GUARDIAN_CLI_VERSION,
|
|
17801
17929
|
repository: GUARDIAN_RELEASE_REPOSITORY,
|
|
17802
17930
|
requiredForPublish: "NPM_TOKEN",
|
|
17931
|
+
requiredForBrowserStoreApi: GUARDIAN_CHROME_WEB_STORE_API_SECRET_NAMES,
|
|
17803
17932
|
githubActionsSecretUrl: `https://github.com/${GUARDIAN_RELEASE_REPOSITORY}/settings/secrets/actions/new`,
|
|
17804
17933
|
commands: {
|
|
17805
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`,
|
|
17806
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'`,
|
|
17807
17942
|
dryRunDispatch: buildGuardianReleaseDispatchCommandFor({
|
|
17808
17943
|
dryRun: true,
|
|
17809
17944
|
releaseVersion: "<version>",
|
|
@@ -17815,13 +17950,23 @@ function buildGuardianReleaseSecretSetup() {
|
|
|
17815
17950
|
releaseVersion: "<version>",
|
|
17816
17951
|
channels: "all",
|
|
17817
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"
|
|
17818
17961
|
})
|
|
17819
17962
|
},
|
|
17820
17963
|
boundaries: [
|
|
17821
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.",
|
|
17822
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.",
|
|
17823
17967
|
"Use dry_run=true before dry_run=false; dry-run dispatch does not require NPM_TOKEN.",
|
|
17824
|
-
"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."
|
|
17825
17970
|
],
|
|
17826
17971
|
tokenPrinted: false,
|
|
17827
17972
|
rawContentIncluded: false
|
|
@@ -17835,16 +17980,27 @@ function renderGuardianReleaseSecretSetup(result, flags) {
|
|
|
17835
17980
|
return `${[
|
|
17836
17981
|
"Project Guardian Release Secret Setup",
|
|
17837
17982
|
`Repository: ${result.repository}`,
|
|
17838
|
-
`Required for publish: ${result.requiredForPublish}`,
|
|
17983
|
+
`Required for npm publish: ${result.requiredForPublish}`,
|
|
17984
|
+
`Required for Chrome Web Store API: ${result.requiredForBrowserStoreApi.join(", ")}`,
|
|
17839
17985
|
`GitHub Actions secret page: ${result.githubActionsSecretUrl}`,
|
|
17840
|
-
"
|
|
17986
|
+
"NPM publish secret:",
|
|
17841
17987
|
result.commands.setNpmToken,
|
|
17842
|
-
"Verify secret presence:",
|
|
17988
|
+
"Verify npm secret presence:",
|
|
17843
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,
|
|
17844
17998
|
"Dry-run release command:",
|
|
17845
17999
|
result.commands.dryRunDispatch,
|
|
17846
|
-
"
|
|
18000
|
+
"Default publish command with manual browser-store upload:",
|
|
17847
18001
|
result.commands.publishDispatch,
|
|
18002
|
+
"Browser-store API publish command:",
|
|
18003
|
+
result.commands.browserStoreApiPublishDispatch,
|
|
17848
18004
|
"Boundaries:",
|
|
17849
18005
|
...result.boundaries.map((boundary) => `- ${boundary}`),
|
|
17850
18006
|
"Token printed: no"
|
|
@@ -18065,9 +18221,9 @@ function buildGuardianReleaseVerify(releaseVersion) {
|
|
|
18065
18221
|
commands: {
|
|
18066
18222
|
registryVersion: `npm view ${GUARDIAN_NPM_PACKAGE_NAME}@${releaseVersion} version --registry=${GUARDIAN_NPM_REGISTRY}`,
|
|
18067
18223
|
publicInstall: `npm install -g ${GUARDIAN_NPM_PACKAGE_NAME}@${releaseVersion}`,
|
|
18224
|
+
chromeWebStoreListing: GUARDIAN_HALO_CHROME_WEB_STORE_LISTING_URL,
|
|
18068
18225
|
installedVersion: "guardian --version",
|
|
18069
18226
|
setup: "guardian setup",
|
|
18070
|
-
launch: "guardian launch",
|
|
18071
18227
|
status: "guardian status",
|
|
18072
18228
|
openControlTower: "guardian open",
|
|
18073
18229
|
copyPasteLocalSmoke,
|
|
@@ -18096,9 +18252,8 @@ function buildGuardianReleasePaste(releaseVersion) {
|
|
|
18096
18252
|
"guardian --version",
|
|
18097
18253
|
"guardian stop",
|
|
18098
18254
|
"guardian setup",
|
|
18099
|
-
"guardian
|
|
18100
|
-
"guardian open"
|
|
18101
|
-
"guardian status"
|
|
18255
|
+
"guardian status",
|
|
18256
|
+
"guardian open"
|
|
18102
18257
|
];
|
|
18103
18258
|
return {
|
|
18104
18259
|
schema_version: "contextecf/project-guardian-release-paste/v1",
|
|
@@ -18109,9 +18264,9 @@ function buildGuardianReleasePaste(releaseVersion) {
|
|
|
18109
18264
|
command: commands.join("\n"),
|
|
18110
18265
|
commands,
|
|
18111
18266
|
boundaries: [
|
|
18112
|
-
"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.",
|
|
18113
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.",
|
|
18114
|
-
"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."
|
|
18115
18270
|
],
|
|
18116
18271
|
tokenPrinted: false,
|
|
18117
18272
|
rawContentIncluded: false
|
|
@@ -18138,18 +18293,19 @@ function renderGuardianReleaseVerify(result, flags) {
|
|
|
18138
18293
|
`Status: ${result.status}`,
|
|
18139
18294
|
"1. Confirm registry visibility:",
|
|
18140
18295
|
result.commands.registryVersion,
|
|
18141
|
-
"2.
|
|
18296
|
+
"2. Confirm public Chrome Web Store listing:",
|
|
18297
|
+
result.commands.chromeWebStoreListing,
|
|
18298
|
+
"3. Install the published version:",
|
|
18142
18299
|
result.commands.publicInstall,
|
|
18143
|
-
"
|
|
18300
|
+
"4. Confirm installed CLI version:",
|
|
18144
18301
|
result.commands.installedVersion,
|
|
18145
|
-
"
|
|
18302
|
+
"5. Run first-use smoke:",
|
|
18146
18303
|
result.commands.setup,
|
|
18147
|
-
result.commands.launch,
|
|
18148
18304
|
result.commands.status,
|
|
18149
18305
|
result.commands.openControlTower,
|
|
18150
|
-
"
|
|
18306
|
+
"6. Copy-paste local smoke block:",
|
|
18151
18307
|
result.commands.copyPasteLocalSmoke,
|
|
18152
|
-
"
|
|
18308
|
+
"7. Verify release evidence:",
|
|
18153
18309
|
result.commands.publishStatusVerification,
|
|
18154
18310
|
result.commands.finishLineStatus,
|
|
18155
18311
|
"Evidence outputs:",
|
|
@@ -18166,6 +18322,12 @@ function isValidReleaseVersion(value) {
|
|
|
18166
18322
|
function isValidReleaseChannels(value) {
|
|
18167
18323
|
return ["all", "npm", "browser", "evidence"].includes(value);
|
|
18168
18324
|
}
|
|
18325
|
+
function isValidReleasePublishAuth(value) {
|
|
18326
|
+
return value === "token" || value === "trusted_publisher";
|
|
18327
|
+
}
|
|
18328
|
+
function isValidBrowserStorePublish(value) {
|
|
18329
|
+
return value === "manual" || value === "api";
|
|
18330
|
+
}
|
|
18169
18331
|
function renderGuardianProductionReadiness(result, flags) {
|
|
18170
18332
|
if (flags.has("json")) {
|
|
18171
18333
|
return `${JSON.stringify(result, null, 2)}
|
|
@@ -18268,9 +18430,9 @@ async function statusGuardian(flags, options) {
|
|
|
18268
18430
|
`Profile: ${home}`,
|
|
18269
18431
|
`Runtime profile: ${result.runtime.profileDaemonStatus}`,
|
|
18270
18432
|
`Control Tower: ${result.controlTowerUrl ?? "not configured"}`,
|
|
18271
|
-
`
|
|
18272
|
-
result.daemon.error ? `
|
|
18273
|
-
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,
|
|
18274
18436
|
`MCP stdio: ${result.runtime.mcpStdioAvailable ? "available" : "unavailable"}`,
|
|
18275
18437
|
`Posture Profile: ${result.postureProfile.selected}`,
|
|
18276
18438
|
`Prompt Coach: ${result.preferences.promptCoachMode}`,
|
|
@@ -18377,7 +18539,7 @@ async function runLaunchCommand(flags, options) {
|
|
|
18377
18539
|
nextCommands: [
|
|
18378
18540
|
"guardian open",
|
|
18379
18541
|
"guardian status",
|
|
18380
|
-
"
|
|
18542
|
+
"Open ContextECF Halo and click Connect",
|
|
18381
18543
|
"guardian policy list"
|
|
18382
18544
|
]
|
|
18383
18545
|
};
|
|
@@ -18386,7 +18548,7 @@ async function runLaunchCommand(flags, options) {
|
|
|
18386
18548
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18387
18549
|
` : `${[
|
|
18388
18550
|
"Project Guardian launched.",
|
|
18389
|
-
`
|
|
18551
|
+
`Local Guardian: ${result.daemonUrl}${result.reusedExistingDaemon ? " (already running)" : ""}`,
|
|
18390
18552
|
`Control Tower: ${result.controlTowerUrl}`,
|
|
18391
18553
|
shouldOpen ? `Opening: ${result.controlTowerUrl}` : void 0,
|
|
18392
18554
|
"Next: guardian open; guardian status"
|
|
@@ -18497,14 +18659,14 @@ async function runStartCommand(flags, options) {
|
|
|
18497
18659
|
controlTowerUrl: requestedControlTowerUrl,
|
|
18498
18660
|
healthUrl,
|
|
18499
18661
|
healthStatus: "reachable",
|
|
18500
|
-
nextCommands: ["guardian open", "guardian status", "
|
|
18662
|
+
nextCommands: ["guardian open", "guardian status", "Open ContextECF Halo and click Connect"]
|
|
18501
18663
|
};
|
|
18502
18664
|
write(
|
|
18503
18665
|
stdout,
|
|
18504
18666
|
flags.has("json") ? `${JSON.stringify(result2, null, 2)}
|
|
18505
18667
|
` : `${[
|
|
18506
|
-
"Guardian
|
|
18507
|
-
`
|
|
18668
|
+
"Local Guardian already running.",
|
|
18669
|
+
`Local Guardian: ${requestedDaemonUrl}`,
|
|
18508
18670
|
`Control Tower: ${requestedControlTowerUrl}`,
|
|
18509
18671
|
"Next: guardian open"
|
|
18510
18672
|
].join("\n")}
|
|
@@ -18513,7 +18675,7 @@ async function runStartCommand(flags, options) {
|
|
|
18513
18675
|
return 0;
|
|
18514
18676
|
}
|
|
18515
18677
|
if (!options.startDetachedDaemon) {
|
|
18516
|
-
write(stderr, "Guardian
|
|
18678
|
+
write(stderr, "Guardian local background launcher is unavailable in this runtime.\n");
|
|
18517
18679
|
return 1;
|
|
18518
18680
|
}
|
|
18519
18681
|
const started = await options.startDetachedDaemon({ home, host, port: selected.port });
|
|
@@ -18534,14 +18696,14 @@ async function runStartCommand(flags, options) {
|
|
|
18534
18696
|
pid: started.pid,
|
|
18535
18697
|
healthUrl,
|
|
18536
18698
|
healthStatus: existing.error ? "unreachable" : "not_reachable",
|
|
18537
|
-
nextCommands: ["guardian open", "guardian status", "
|
|
18699
|
+
nextCommands: ["guardian open", "guardian status", "Open ContextECF Halo and click Connect"]
|
|
18538
18700
|
};
|
|
18539
18701
|
write(
|
|
18540
18702
|
stdout,
|
|
18541
18703
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18542
18704
|
` : `${[
|
|
18543
|
-
"Guardian
|
|
18544
|
-
`
|
|
18705
|
+
"Local Guardian start requested.",
|
|
18706
|
+
`Local Guardian: ${daemonUrl}`,
|
|
18545
18707
|
`Control Tower: ${controlTowerUrl}`,
|
|
18546
18708
|
started.pid ? `PID: ${started.pid}` : void 0,
|
|
18547
18709
|
"Next: guardian open"
|
|
@@ -18552,8 +18714,8 @@ async function runStartCommand(flags, options) {
|
|
|
18552
18714
|
}
|
|
18553
18715
|
function renderDaemonTokenMismatchMessage(requestedDaemonUrl, port) {
|
|
18554
18716
|
return [
|
|
18555
|
-
`Guardian found another
|
|
18556
|
-
"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.",
|
|
18557
18719
|
`Recovery: stop the other process/container, or run Guardian on a different port with guardian launch --port=${port + 1}.`,
|
|
18558
18720
|
`Diagnostics: guardian status; docker ps --filter publish=${port}`,
|
|
18559
18721
|
""
|
|
@@ -18586,7 +18748,7 @@ async function runStopCommand(flags, options) {
|
|
|
18586
18748
|
if (!stopped.ok) {
|
|
18587
18749
|
write(
|
|
18588
18750
|
stderr,
|
|
18589
|
-
`Guardian
|
|
18751
|
+
`Local Guardian did not stop cleanly${stopped.statusCode ? ` (HTTP ${stopped.statusCode})` : ""}: ${stopped.error ?? "unknown_error"}
|
|
18590
18752
|
`
|
|
18591
18753
|
);
|
|
18592
18754
|
return 1;
|
|
@@ -18597,12 +18759,12 @@ async function runStopCommand(flags, options) {
|
|
|
18597
18759
|
success: true,
|
|
18598
18760
|
daemonStatus: profile.runtime.daemon_status,
|
|
18599
18761
|
stopUrl: stopUrl.url,
|
|
18600
|
-
note: "Guardian
|
|
18762
|
+
note: "Local Guardian stop requested."
|
|
18601
18763
|
};
|
|
18602
18764
|
write(
|
|
18603
18765
|
stdout,
|
|
18604
18766
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
18605
|
-
` : `${["Guardian
|
|
18767
|
+
` : `${["Local Guardian stop requested.", "Status: not_started_mvp"].join("\n")}
|
|
18606
18768
|
`
|
|
18607
18769
|
);
|
|
18608
18770
|
return 0;
|
|
@@ -18674,7 +18836,7 @@ Proof: ${result2.proofPath}
|
|
|
18674
18836
|
}
|
|
18675
18837
|
const host = stringFlag(flags, "host") ?? "127.0.0.1";
|
|
18676
18838
|
if (!isLoopbackHost(host)) {
|
|
18677
|
-
write(stderr, "Guardian service supervision only accepts loopback
|
|
18839
|
+
write(stderr, "Guardian service supervision only accepts loopback Local Guardian hosts.\n");
|
|
18678
18840
|
return 1;
|
|
18679
18841
|
}
|
|
18680
18842
|
const port = numberFlag(flags, "port") ?? 4317;
|
|
@@ -18931,7 +19093,7 @@ async function daemonPair(flags, options) {
|
|
|
18931
19093
|
[GUARDIAN_BRIDGE_CONFIG_STORAGE_KEY]: bridgeConfig,
|
|
18932
19094
|
[GUARDIAN_BRIDGE_TOKEN_STORAGE_KEY]: token
|
|
18933
19095
|
},
|
|
18934
|
-
note: "
|
|
19096
|
+
note: "Advanced fallback only. Most users should run guardian setup, open ContextECF Halo, and click Connect."
|
|
18935
19097
|
};
|
|
18936
19098
|
if (flags.has("json")) {
|
|
18937
19099
|
return { exitCode: 0, message: `${JSON.stringify(pairing, null, 2)}
|
|
@@ -18940,13 +19102,10 @@ async function daemonPair(flags, options) {
|
|
|
18940
19102
|
return {
|
|
18941
19103
|
exitCode: 0,
|
|
18942
19104
|
message: `${[
|
|
18943
|
-
"Guardian browser
|
|
18944
|
-
|
|
18945
|
-
|
|
18946
|
-
|
|
18947
|
-
`Native host: ${pairing.nativeHostName}`,
|
|
18948
|
-
`Token: ${pairing.token}`,
|
|
18949
|
-
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"
|
|
18950
19109
|
].join("\n")}
|
|
18951
19110
|
`
|
|
18952
19111
|
};
|
|
@@ -18955,6 +19114,9 @@ async function runExtensionCommand(argv, flags, options) {
|
|
|
18955
19114
|
const subcommand = argv[0] ?? "native-host";
|
|
18956
19115
|
const stdout = options.stdout ?? process.stdout;
|
|
18957
19116
|
const stderr = options.stderr ?? process.stderr;
|
|
19117
|
+
if (subcommand === "connect") {
|
|
19118
|
+
return runExtensionConnectCommand(flags, options);
|
|
19119
|
+
}
|
|
18958
19120
|
if (subcommand === "open-setup") {
|
|
18959
19121
|
return runExtensionOpenSetupCommand(flags, options);
|
|
18960
19122
|
}
|
|
@@ -19142,13 +19304,18 @@ Re-run with --force to replace it.
|
|
|
19142
19304
|
registryMutationRequested: writeRegistry,
|
|
19143
19305
|
registryProofPath,
|
|
19144
19306
|
tokenPrinted: false,
|
|
19145
|
-
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"]
|
|
19146
19313
|
};
|
|
19147
19314
|
write(
|
|
19148
19315
|
stdout,
|
|
19149
19316
|
flags.has("json") ? `${JSON.stringify(result, null, 2)}
|
|
19150
19317
|
` : `${[
|
|
19151
|
-
"Guardian
|
|
19318
|
+
"Guardian browser connection installed.",
|
|
19152
19319
|
`Browser: ${browser}`,
|
|
19153
19320
|
`Host: ${plan.hostName}`,
|
|
19154
19321
|
`Manifest: ${plan.manifestPath}`,
|
|
@@ -19163,6 +19330,121 @@ Re-run with --force to replace it.
|
|
|
19163
19330
|
);
|
|
19164
19331
|
return 0;
|
|
19165
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
|
+
}
|
|
19166
19448
|
async function runExtensionOpenSetupCommand(flags, options) {
|
|
19167
19449
|
const stdout = options.stdout ?? process.stdout;
|
|
19168
19450
|
const stderr = options.stderr ?? process.stderr;
|
|
@@ -19172,8 +19454,9 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19172
19454
|
write(stderr, "Guardian is not installed. Run guardian install first.\n");
|
|
19173
19455
|
return 1;
|
|
19174
19456
|
}
|
|
19175
|
-
const
|
|
19176
|
-
|
|
19457
|
+
const requestedExtensionId = stringFlag(flags, "extension-id") ?? stringFlag(flags, "id");
|
|
19458
|
+
const extensionId = requestedExtensionId ?? GUARDIAN_HALO_CHROME_EXTENSION_ID;
|
|
19459
|
+
if (requestedExtensionId && !isValidChromeExtensionId(requestedExtensionId)) {
|
|
19177
19460
|
write(
|
|
19178
19461
|
stderr,
|
|
19179
19462
|
"Invalid --extension-id. Chrome extension ids must be 32 lowercase letters from a-p.\n"
|
|
@@ -19187,22 +19470,24 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19187
19470
|
return 1;
|
|
19188
19471
|
}
|
|
19189
19472
|
const setupUrl = buildControlTowerSetupUrl(resolved.url);
|
|
19190
|
-
const nativeHostCommand = `guardian extension native-host install --extension-id=${extensionId
|
|
19473
|
+
const nativeHostCommand = `guardian extension native-host install --extension-id=${extensionId}`;
|
|
19191
19474
|
const result = {
|
|
19192
19475
|
schema_version: "contextecf/project-guardian-extension-setup/v1",
|
|
19193
19476
|
success: true,
|
|
19194
19477
|
opened: !flags.has("print"),
|
|
19195
19478
|
controlTowerUrl: resolved.url,
|
|
19196
19479
|
setupUrl,
|
|
19197
|
-
extensionId
|
|
19480
|
+
extensionId,
|
|
19481
|
+
defaultExtensionId: GUARDIAN_HALO_CHROME_EXTENSION_ID,
|
|
19198
19482
|
tokenPrinted: false,
|
|
19199
19483
|
nextCommands: [
|
|
19200
19484
|
"guardian launch",
|
|
19201
|
-
"guardian daemon pair --json",
|
|
19202
19485
|
nativeHostCommand,
|
|
19203
|
-
"guardian extension native-host status"
|
|
19486
|
+
"guardian extension native-host status",
|
|
19487
|
+
"Open ContextECF Halo and click Connect"
|
|
19204
19488
|
],
|
|
19205
|
-
|
|
19489
|
+
advancedCommands: ["guardian pair --json"],
|
|
19490
|
+
note: extensionId ? "Guardian browser setup opened for ContextECF Halo." : "Guardian browser setup opened."
|
|
19206
19491
|
};
|
|
19207
19492
|
if (result.opened) {
|
|
19208
19493
|
const opener = options.openUrl ?? ((url2) => openUrlInDefaultBrowser(url2, options));
|
|
@@ -19214,7 +19499,8 @@ async function runExtensionOpenSetupCommand(flags, options) {
|
|
|
19214
19499
|
` : `${[
|
|
19215
19500
|
result.opened ? `Opening Guardian browser setup: ${setupUrl}` : `Guardian browser setup: ${setupUrl}`,
|
|
19216
19501
|
"Token printed: no",
|
|
19217
|
-
`Next: ${result.nextCommands.join("; ")}
|
|
19502
|
+
`Next: ${result.nextCommands.join("; ")}`,
|
|
19503
|
+
`Advanced fallback: ${result.advancedCommands.join("; ")}`
|
|
19218
19504
|
].join("\n")}
|
|
19219
19505
|
`
|
|
19220
19506
|
);
|
|
@@ -19741,7 +20027,7 @@ function buildGuardianServiceArtifact(input2) {
|
|
|
19741
20027
|
if (input2.platform === "systemd") {
|
|
19742
20028
|
return `${[
|
|
19743
20029
|
"[Unit]",
|
|
19744
|
-
"Description=Project Guardian
|
|
20030
|
+
"Description=Project Guardian Local Guardian background service",
|
|
19745
20031
|
"After=network.target",
|
|
19746
20032
|
"",
|
|
19747
20033
|
"[Service]",
|
|
@@ -19760,7 +20046,7 @@ function buildGuardianServiceArtifact(input2) {
|
|
|
19760
20046
|
'<?xml version="1.0" encoding="UTF-16"?>',
|
|
19761
20047
|
'<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">',
|
|
19762
20048
|
" <RegistrationInfo>",
|
|
19763
|
-
" <Description>Project Guardian
|
|
20049
|
+
" <Description>Project Guardian Local Guardian background service</Description>",
|
|
19764
20050
|
" </RegistrationInfo>",
|
|
19765
20051
|
" <Triggers>",
|
|
19766
20052
|
" <LogonTrigger><Enabled>true</Enabled></LogonTrigger>",
|
|
@@ -20742,6 +21028,59 @@ async function runReceiptsCommand(argv, flags, options) {
|
|
|
20742
21028
|
write(stdout, await verifyReceipts(flags, options));
|
|
20743
21029
|
return 0;
|
|
20744
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
|
+
}
|
|
20745
21084
|
async function runPolicyCommand(argv, flags, options) {
|
|
20746
21085
|
const subcommand = argv[0] ?? "list";
|
|
20747
21086
|
const stdout = options.stdout ?? process.stdout;
|
|
@@ -23161,13 +23500,13 @@ function openUrlInDefaultBrowser(url2, options = {}) {
|
|
|
23161
23500
|
});
|
|
23162
23501
|
});
|
|
23163
23502
|
}
|
|
23164
|
-
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;
|
|
23165
23504
|
var init_runtime = __esm({
|
|
23166
23505
|
"packages/guardian-cli/src/runtime.ts"() {
|
|
23167
23506
|
"use strict";
|
|
23168
23507
|
init_src();
|
|
23169
23508
|
init_src2();
|
|
23170
|
-
GUARDIAN_CLI_VERSION = "0.1.
|
|
23509
|
+
GUARDIAN_CLI_VERSION = "0.1.8";
|
|
23171
23510
|
GUARDIAN_PROFILE_SCHEMA_VERSION = "contextecf/project-guardian-profile/v1";
|
|
23172
23511
|
GUARDIAN_NPM_PACKAGE_NAME = "@contextecf/guardian-cli";
|
|
23173
23512
|
GUARDIAN_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
@@ -23175,8 +23514,17 @@ var init_runtime = __esm({
|
|
|
23175
23514
|
GUARDIAN_NPX_SETUP_COMMAND = `npx ${GUARDIAN_NPM_PACKAGE_NAME}@latest setup`;
|
|
23176
23515
|
GUARDIAN_FIRST_RUN_COMMAND = "guardian setup";
|
|
23177
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}`;
|
|
23178
23519
|
GUARDIAN_RELEASE_DISPATCH_WORKFLOW = "guardian-release-dispatch.yml";
|
|
23179
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
|
+
];
|
|
23180
23528
|
GUARDIAN_DEFAULT_CONTROL_TOWER_URL = "http://127.0.0.1:4317/control-tower";
|
|
23181
23529
|
GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ENV = "GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY";
|
|
23182
23530
|
GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ID_ENV = "GUARDIAN_LOCAL_DATA_ENCRYPTION_KEY_ID";
|
|
@@ -23192,6 +23540,195 @@ var init_runtime = __esm({
|
|
|
23192
23540
|
PERSONAL_FABRIC_CONTRACTS_SCHEMA_VERSION2 = "contextecf/personal-fabric-contracts/v1";
|
|
23193
23541
|
PROMPT_COACH_MODES = ["off", "quiet", "balanced", "hands_on"];
|
|
23194
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
|
+
];
|
|
23195
23732
|
GUARDIAN_POSTURE_PROFILE_IDS = [
|
|
23196
23733
|
"calm",
|
|
23197
23734
|
"balanced",
|
|
@@ -23684,10 +24221,49 @@ Open Control Tower:
|
|
|
23684
24221
|
${GUARDIAN_OPEN_CONTROL_TOWER_COMMAND}
|
|
23685
24222
|
start aliases: guardian launch, guardian tower, guardian control-tower
|
|
23686
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
|
+
|
|
23687
24263
|
Usage:
|
|
23688
24264
|
guardian --version
|
|
23689
24265
|
guardian version [--json]
|
|
23690
|
-
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>]
|
|
23691
24267
|
guardian install [--json]
|
|
23692
24268
|
guardian doctor [--json]
|
|
23693
24269
|
guardian readiness [--json] [--strict]
|
|
@@ -23695,7 +24271,7 @@ Usage:
|
|
|
23695
24271
|
guardian release flow --version=<semver> [--json]
|
|
23696
24272
|
guardian release verify --version=<semver> [--json]
|
|
23697
24273
|
guardian release paste --version=<semver> [--json]
|
|
23698
|
-
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]
|
|
23699
24275
|
guardian release secrets [--json]
|
|
23700
24276
|
guardian release browser-store [--json]
|
|
23701
24277
|
guardian open [--json] [--print] [--url=http://127.0.0.1:<port>]
|
|
@@ -23703,6 +24279,8 @@ Usage:
|
|
|
23703
24279
|
guardian start [--json] [--host=127.0.0.1] [--port=4317]
|
|
23704
24280
|
guardian launch|tower|control-tower [--json] [--open] [--print] [--host=127.0.0.1] [--port=4317]
|
|
23705
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]
|
|
23706
24284
|
guardian service install [--json] [--platform=auto|launchd|systemd|windows-task-scheduler] [--force]
|
|
23707
24285
|
guardian service status [--json]
|
|
23708
24286
|
guardian service uninstall [--yes] [--json]
|
|
@@ -23711,6 +24289,7 @@ Usage:
|
|
|
23711
24289
|
guardian key-store uninstall [--yes] [--json]
|
|
23712
24290
|
guardian daemon serve [--host=127.0.0.1] [--port=4317]
|
|
23713
24291
|
guardian daemon pair [--json]
|
|
24292
|
+
guardian extension connect [--json] [--browser=chrome|chromium|edge] [--extension-id=<chrome-extension-id>]
|
|
23714
24293
|
guardian extension open-setup [--json] [--print] [--url=http://127.0.0.1:<port>/control-tower] [--extension-id=<chrome-extension-id>]
|
|
23715
24294
|
guardian extension native-host install --extension-id=<chrome-extension-id> [--browser=chrome|chromium|edge] [--target=/path/to/manifest] [--force] [--write-registry --yes] [--json]
|
|
23716
24295
|
guardian extension native-host status [--browser=chrome|chromium|edge] [--target=/path/to/manifest] [--json]
|
|
@@ -23726,6 +24305,7 @@ Usage:
|
|
|
23726
24305
|
guardian privacy mode on [--json]
|
|
23727
24306
|
guardian mcp install [--json] [--client=generic|codex|claude-desktop|cursor|all] [--write] [--target=/path/to/config] [--force]
|
|
23728
24307
|
guardian mcp serve
|
|
24308
|
+
guardian coach options [--json]
|
|
23729
24309
|
guardian policy list [--json]
|
|
23730
24310
|
guardian policy enable <pack-id> [--json]
|
|
23731
24311
|
guardian policy disable <pack-id> [--json]
|
|
@@ -24062,11 +24642,12 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24062
24642
|
return void 0;
|
|
24063
24643
|
}
|
|
24064
24644
|
const fullSsnRisk = hasFullSsnRisk(riskReport.matched_policy_refs);
|
|
24645
|
+
const riskCopy = buildPromptCoachRiskCopy(riskReport, fullSsnRisk);
|
|
24065
24646
|
const card = {
|
|
24066
24647
|
card_id: `coach:${riskReport.risk_report_id}`,
|
|
24067
24648
|
intervention_level: coachDecision.intervention_level,
|
|
24068
|
-
title: disposition === "block" ? "Guardian blocked this prompt" : disposition === "confirm" ? "Guardian needs your confirmation" : coachDecision.intervention_level === "soft_tag_along" ? "Guardian found useful context" :
|
|
24069
|
-
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,
|
|
24070
24651
|
recommended_choice_id: disposition === "confirm" ? "confirm-after-review" : coachDecision.intervention_level === "soft_tag_along" ? "add-context" : "use-guardian-version",
|
|
24071
24652
|
choices: disposition === "confirm" ? [
|
|
24072
24653
|
{
|
|
@@ -24091,8 +24672,8 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24091
24672
|
resulting_disposition: "warn"
|
|
24092
24673
|
},
|
|
24093
24674
|
{
|
|
24094
|
-
choice_id: "
|
|
24095
|
-
label: "
|
|
24675
|
+
choice_id: "send-as-is",
|
|
24676
|
+
label: "Send as-is",
|
|
24096
24677
|
description: "Send without adding context.",
|
|
24097
24678
|
resulting_disposition: "allow"
|
|
24098
24679
|
}
|
|
@@ -24109,6 +24690,12 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24109
24690
|
label: "Cancel",
|
|
24110
24691
|
description: "Do not send this prompt.",
|
|
24111
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"
|
|
24112
24699
|
}
|
|
24113
24700
|
] : [
|
|
24114
24701
|
{
|
|
@@ -24129,6 +24716,60 @@ function buildCoachCard(disposition, riskReport, policyPacks, learningSignals) {
|
|
|
24129
24716
|
const parsedCard = PromptCoachCardSchema.parse(card);
|
|
24130
24717
|
return shouldQuietPromptCoachCard(parsedCard, disposition, learningSignals) ? void 0 : parsedCard;
|
|
24131
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
|
+
}
|
|
24132
24773
|
function shouldQuietPromptCoachCard(card, disposition, learningSignals) {
|
|
24133
24774
|
if (!learningSignals || disposition === "block" || disposition === "confirm") {
|
|
24134
24775
|
return false;
|
|
@@ -25776,10 +26417,14 @@ __export(daemon_exports, {
|
|
|
25776
26417
|
processGuardianControlTowerActivityRequest: () => processGuardianControlTowerActivityRequest,
|
|
25777
26418
|
processGuardianControlTowerAppPermissionRequest: () => processGuardianControlTowerAppPermissionRequest,
|
|
25778
26419
|
processGuardianControlTowerDataRequest: () => processGuardianControlTowerDataRequest,
|
|
26420
|
+
processGuardianControlTowerMarketplaceRequest: () => processGuardianControlTowerMarketplaceRequest,
|
|
26421
|
+
processGuardianControlTowerNoticeRequest: () => processGuardianControlTowerNoticeRequest,
|
|
25779
26422
|
processGuardianControlTowerPolicyRequest: () => processGuardianControlTowerPolicyRequest,
|
|
25780
26423
|
processGuardianControlTowerPreferenceRequest: () => processGuardianControlTowerPreferenceRequest,
|
|
26424
|
+
processGuardianControlTowerPrimePackRequest: () => processGuardianControlTowerPrimePackRequest,
|
|
25781
26425
|
processGuardianControlTowerProviderPermissionRequest: () => processGuardianControlTowerProviderPermissionRequest,
|
|
25782
26426
|
processGuardianControlTowerSourcePermissionRequest: () => processGuardianControlTowerSourcePermissionRequest,
|
|
26427
|
+
processGuardianControlTowerViewModelRequest: () => processGuardianControlTowerViewModelRequest,
|
|
25783
26428
|
processGuardianDaemonHealthRequest: () => processGuardianDaemonHealthRequest,
|
|
25784
26429
|
processGuardianDaemonStopRequest: () => processGuardianDaemonStopRequest,
|
|
25785
26430
|
processGuardianLiveCoachEventRequest: () => processGuardianLiveCoachEventRequest,
|
|
@@ -25789,7 +26434,7 @@ __export(daemon_exports, {
|
|
|
25789
26434
|
});
|
|
25790
26435
|
import { createHash as createHash5, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
25791
26436
|
import { existsSync as existsSync2 } from "node:fs";
|
|
25792
|
-
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";
|
|
25793
26438
|
import { createServer } from "node:http";
|
|
25794
26439
|
import path4 from "node:path";
|
|
25795
26440
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -25915,12 +26560,22 @@ async function handleRequest(input2) {
|
|
|
25915
26560
|
return;
|
|
25916
26561
|
}
|
|
25917
26562
|
if (input2.request.method === "GET" && (input2.request.url === "/control-tower" || input2.request.url === "/control-tower/")) {
|
|
25918
|
-
const [
|
|
26563
|
+
const [
|
|
26564
|
+
receipts,
|
|
26565
|
+
searchGraph,
|
|
26566
|
+
learningEvents,
|
|
26567
|
+
localDataStatus,
|
|
26568
|
+
releaseDoctorStatus,
|
|
26569
|
+
adminNotices,
|
|
26570
|
+
marketplaceRequests
|
|
26571
|
+
] = await Promise.all([
|
|
25919
26572
|
input2.store.listReceipts(),
|
|
25920
26573
|
input2.store.searchGraph({ limit: CONTROL_TOWER_GRAPH_NODE_LIMIT }),
|
|
25921
26574
|
input2.store.listLearningEvents({ limit: CONTROL_TOWER_LEARNING_EVENT_LIMIT }),
|
|
25922
26575
|
buildControlTowerLocalDataStatus(input2.profile, input2.env),
|
|
25923
|
-
buildControlTowerReleaseDoctorStatus(input2.profile, input2.env)
|
|
26576
|
+
buildControlTowerReleaseDoctorStatus(input2.profile, input2.env),
|
|
26577
|
+
readControlTowerAdminNotices(input2.profile.profile_dir),
|
|
26578
|
+
readControlTowerMarketplaceRequests(input2.profile.profile_dir)
|
|
25924
26579
|
]);
|
|
25925
26580
|
writeHtml(
|
|
25926
26581
|
input2.response,
|
|
@@ -25931,7 +26586,9 @@ async function handleRequest(input2) {
|
|
|
25931
26586
|
searchGraph,
|
|
25932
26587
|
learningEvents,
|
|
25933
26588
|
localDataStatus,
|
|
25934
|
-
releaseDoctorStatus
|
|
26589
|
+
releaseDoctorStatus,
|
|
26590
|
+
adminNotices,
|
|
26591
|
+
marketplaceRequests
|
|
25935
26592
|
),
|
|
25936
26593
|
{
|
|
25937
26594
|
controlToken: input2.controlToken
|
|
@@ -25940,14 +26597,45 @@ async function handleRequest(input2) {
|
|
|
25940
26597
|
return;
|
|
25941
26598
|
}
|
|
25942
26599
|
const activityUrl = parseGuardianUrl(input2.request.url);
|
|
25943
|
-
if (input2.request.method === "GET" && activityUrl?.pathname === "/v1/guardian/
|
|
25944
|
-
const
|
|
25945
|
-
|
|
25946
|
-
|
|
25947
|
-
|
|
25948
|
-
|
|
25949
|
-
|
|
25950
|
-
|
|
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
|
+
}
|
|
26631
|
+
if (input2.request.method === "GET" && activityUrl?.pathname === "/v1/guardian/activity") {
|
|
26632
|
+
const filter = parseActivityFilter(activityUrl.searchParams.get("filter") ?? "all");
|
|
26633
|
+
const receipts = (await input2.store.listReceipts()).slice().sort((left, right) => right.created_at.localeCompare(left.created_at)).slice(0, CONTROL_TOWER_RECEIPT_LIMIT);
|
|
26634
|
+
const result2 = processGuardianControlTowerActivityRequest(receipts, filter);
|
|
26635
|
+
writeJson(input2.response, result2.statusCode, result2.body);
|
|
26636
|
+
return;
|
|
26637
|
+
}
|
|
26638
|
+
if (input2.request.method === "GET" && input2.request.url === "/v1/guardian/admin/health") {
|
|
25951
26639
|
const result2 = processGuardianDaemonHealthRequest({
|
|
25952
26640
|
token: input2.token,
|
|
25953
26641
|
suppliedToken: headerValue(input2.request, ADMIN_HEADER)
|
|
@@ -25984,6 +26672,24 @@ async function handleRequest(input2) {
|
|
|
25984
26672
|
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
25985
26673
|
return;
|
|
25986
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
|
+
}
|
|
25987
26693
|
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/admin/preferences") {
|
|
25988
26694
|
const parsedBody2 = await readGuardianDaemonAdminBody(input2.request);
|
|
25989
26695
|
if (!parsedBody2.ok) {
|
|
@@ -26064,6 +26770,42 @@ async function handleRequest(input2) {
|
|
|
26064
26770
|
writeAdminResponse(input2.response, result2.statusCode, result2.body, parsedBody2.redirect);
|
|
26065
26771
|
return;
|
|
26066
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
|
+
}
|
|
26067
26809
|
if (input2.request.method === "POST" && input2.request.url === "/v1/guardian/live-coach-event") {
|
|
26068
26810
|
const suppliedToken2 = headerValue(input2.request, ADMIN_HEADER);
|
|
26069
26811
|
const parsedBody2 = await readGuardianDaemonJsonBody(input2.request);
|
|
@@ -26136,6 +26878,23 @@ function processGuardianControlTowerActivityRequest(receipts, filterValue) {
|
|
|
26136
26878
|
}
|
|
26137
26879
|
};
|
|
26138
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
|
+
}
|
|
26139
26898
|
function processGuardianDaemonStopRequest(input2) {
|
|
26140
26899
|
if (!isAuthorized(input2.token, input2.suppliedToken)) {
|
|
26141
26900
|
return {
|
|
@@ -26201,6 +26960,105 @@ async function processGuardianControlTowerPolicyRequest(input2) {
|
|
|
26201
26960
|
};
|
|
26202
26961
|
}
|
|
26203
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
|
+
}
|
|
26204
27062
|
async function processGuardianControlTowerPreferenceRequest(input2) {
|
|
26205
27063
|
if (!isAdminAuthorized(input2)) {
|
|
26206
27064
|
return {
|
|
@@ -26487,6 +27345,190 @@ async function processGuardianControlTowerDataRequest(input2) {
|
|
|
26487
27345
|
};
|
|
26488
27346
|
}
|
|
26489
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
|
+
}
|
|
26490
27532
|
function parseAppPermissionAction(value) {
|
|
26491
27533
|
return value === "revoke" || value === "allow" ? value : void 0;
|
|
26492
27534
|
}
|
|
@@ -26502,6 +27544,25 @@ function parseGuardianSourceType2(value) {
|
|
|
26502
27544
|
}
|
|
26503
27545
|
return GUARDIAN_SOURCE_TYPES.includes(value) ? value : void 0;
|
|
26504
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
|
+
}
|
|
26505
27566
|
async function processGuardianBrowserBridgeRequest(input2) {
|
|
26506
27567
|
if (!isAuthorized(input2.token, input2.suppliedToken)) {
|
|
26507
27568
|
return {
|
|
@@ -26642,6 +27703,11 @@ async function processBrowserPrompt(input2) {
|
|
|
26642
27703
|
visible_message: decision.visible_message,
|
|
26643
27704
|
required_confirmation: decision.required_confirmation,
|
|
26644
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
|
+
},
|
|
26645
27711
|
receipt_id: decision.receipt.receipt_id
|
|
26646
27712
|
};
|
|
26647
27713
|
}
|
|
@@ -26832,6 +27898,114 @@ function buildLiveCoachEvent(request, transactionId, timestamp, receipt) {
|
|
|
26832
27898
|
redaction_state: "metadata_only"
|
|
26833
27899
|
};
|
|
26834
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
|
+
}
|
|
26835
28009
|
function liveCoachCategoryLabel(category) {
|
|
26836
28010
|
if (category === "ssn") return "possible SSN sharing";
|
|
26837
28011
|
if (category === "password") return "possible credential sharing";
|
|
@@ -26947,6 +28121,218 @@ function isAdminAuthorized(input2) {
|
|
|
26947
28121
|
function parsePolicyPackAction(value) {
|
|
26948
28122
|
return value === "enable" || value === "disable" ? value : void 0;
|
|
26949
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
|
+
}
|
|
26950
28336
|
function parsePromptCoachMode2(value) {
|
|
26951
28337
|
return PROMPT_COACH_MODES2.includes(value) ? value : void 0;
|
|
26952
28338
|
}
|
|
@@ -27026,7 +28412,7 @@ function writeHtml(response, statusCode, body, options = {}) {
|
|
|
27026
28412
|
response.writeHead(statusCode, {
|
|
27027
28413
|
"Content-Type": "text/html; charset=utf-8",
|
|
27028
28414
|
"Referrer-Policy": "no-referrer",
|
|
27029
|
-
"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'",
|
|
27030
28416
|
"X-Content-Type-Options": "nosniff"
|
|
27031
28417
|
});
|
|
27032
28418
|
response.end(body);
|
|
@@ -27053,19 +28439,168 @@ function closeGuardianDaemonServer(server) {
|
|
|
27053
28439
|
} catch {
|
|
27054
28440
|
}
|
|
27055
28441
|
}
|
|
27056
|
-
function parseGuardianUrl(url2) {
|
|
27057
|
-
if (!url2) {
|
|
27058
|
-
return void 0;
|
|
27059
|
-
}
|
|
27060
|
-
try {
|
|
27061
|
-
return new URL(url2, "http://127.0.0.1");
|
|
27062
|
-
} catch {
|
|
27063
|
-
return void 0;
|
|
27064
|
-
}
|
|
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
|
+
});
|
|
27065
28600
|
}
|
|
27066
28601
|
function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { nodes: [], edges: [] }, learningEvents = [], localDataStatus = createEmptyControlTowerLocalDataStatus(profile), releaseDoctorStatus = createEmptyControlTowerReleaseDoctorStatus(
|
|
27067
28602
|
profile
|
|
27068
|
-
)) {
|
|
28603
|
+
), adminNotices = [], marketplaceRequests = []) {
|
|
27069
28604
|
const policyPacks = profile.policy_packs.available.length;
|
|
27070
28605
|
const activePolicyPacks = profile.policy_packs.active.join(", ") || "none";
|
|
27071
28606
|
const recentReceipts = receipts.slice().sort((left, right) => right.created_at.localeCompare(left.created_at)).slice(0, CONTROL_TOWER_RECEIPT_LIMIT);
|
|
@@ -27243,9 +28778,28 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27243
28778
|
<button type="submit" class="provider-pill" aria-label="Revoke ${escapeHtml(providerDisplayName(row.id))}"><span></span>${escapeHtml(providerDisplayName(row.id))}</button>
|
|
27244
28779
|
</form>`
|
|
27245
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);
|
|
27246
28800
|
const rows = [
|
|
27247
28801
|
["Profile", profile.profile_dir],
|
|
27248
|
-
["
|
|
28802
|
+
["Local Guardian", localGuardianStatus],
|
|
27249
28803
|
[
|
|
27250
28804
|
"Posture Profile",
|
|
27251
28805
|
`${profile.posture_profile.selected}; managed baseline ${profile.posture_profile.managed_baseline_id ?? "none"}`
|
|
@@ -27413,6 +28967,161 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27413
28967
|
background: rgba(15, 23, 42, 0.78);
|
|
27414
28968
|
font-size: 16px;
|
|
27415
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
|
+
}
|
|
27416
29125
|
.eyebrow {
|
|
27417
29126
|
width: fit-content;
|
|
27418
29127
|
margin-bottom: 16px;
|
|
@@ -27594,6 +29303,22 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27594
29303
|
}
|
|
27595
29304
|
.provider-pill:hover { background: rgba(30, 41, 59, 0.78); }
|
|
27596
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; }
|
|
27597
29322
|
.activity-heading { justify-content: space-between; gap: 16px; margin-bottom: 12px; }
|
|
27598
29323
|
.activity-heading h2 { margin: 0; }
|
|
27599
29324
|
.filter-chips { gap: 8px; flex-wrap: wrap; }
|
|
@@ -27614,6 +29339,14 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27614
29339
|
.chip:hover { color: #f5f7fb; background: rgba(30, 41, 59, 0.78); }
|
|
27615
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); }
|
|
27616
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
|
+
}
|
|
27617
29350
|
.empty-filter-state {
|
|
27618
29351
|
padding: 22px;
|
|
27619
29352
|
border: 1px dashed rgba(148, 163, 184, 0.24);
|
|
@@ -27671,6 +29404,7 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27671
29404
|
.protection-card { min-height: 320px; }
|
|
27672
29405
|
.activity-card { grid-template-columns: 48px 1fr; }
|
|
27673
29406
|
.activity-badge { grid-column: 2; width: fit-content; }
|
|
29407
|
+
.surface-tabs, .role-grid, .admin-grid, .marketplace-grid, .builder-grid { grid-template-columns: 1fr; }
|
|
27674
29408
|
section { padding: 16px; overflow-x: auto; }
|
|
27675
29409
|
table { min-width: 720px; }
|
|
27676
29410
|
.activity-heading { align-items: flex-start; flex-direction: column; }
|
|
@@ -27683,7 +29417,7 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27683
29417
|
<header class="topbar">
|
|
27684
29418
|
<div class="brand"><span class="brand-mark">G</span><span>Guardian</span></div>
|
|
27685
29419
|
<div class="topbar-actions">
|
|
27686
|
-
<span class="status-pill"><span class="status-dot"></span>${escapeHtml(
|
|
29420
|
+
<span class="status-pill"><span class="status-dot"></span>${escapeHtml(localGuardianStatus)}</span>
|
|
27687
29421
|
<span class="icon-button" aria-label="Settings">⚙</span>
|
|
27688
29422
|
</div>
|
|
27689
29423
|
</header>
|
|
@@ -27691,6 +29425,25 @@ function renderControlTowerStatusPage(profile, receipts = [], searchGraph = { no
|
|
|
27691
29425
|
<div class="eyebrow">Local-first status</div>
|
|
27692
29426
|
<h1>Project Guardian Control Tower</h1>
|
|
27693
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">
|
|
27694
29447
|
<div class="hero-grid" aria-label="Guardian protection overview">
|
|
27695
29448
|
<article class="protection-card">
|
|
27696
29449
|
<div class="${protectionRingClass}"><div class="ring-core">G</div></div>
|
|
@@ -27712,6 +29465,7 @@ ${heroMetricCards}
|
|
|
27712
29465
|
</div>
|
|
27713
29466
|
</div>
|
|
27714
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>
|
|
27715
29469
|
<div class="activity-list" data-activity-list>
|
|
27716
29470
|
${activityFeedHtml}
|
|
27717
29471
|
<div class="empty-filter-state" data-activity-empty hidden>No activity matches this filter yet.</div>
|
|
@@ -27880,7 +29634,7 @@ ${releaseDoctorRows}
|
|
|
27880
29634
|
</tbody>
|
|
27881
29635
|
</table>
|
|
27882
29636
|
</section>
|
|
27883
|
-
<section aria-labelledby="receipt-drilldowns">
|
|
29637
|
+
<section id="receipts" aria-labelledby="receipt-drilldowns">
|
|
27884
29638
|
<div class="activity-heading">
|
|
27885
29639
|
<h2 id="receipt-drilldowns">What Guardian did today</h2>
|
|
27886
29640
|
<div class="filter-chips" aria-label="Activity filters">
|
|
@@ -27890,7 +29644,7 @@ ${releaseDoctorRows}
|
|
|
27890
29644
|
<span class="chip">Allowed</span>
|
|
27891
29645
|
</div>
|
|
27892
29646
|
</div>
|
|
27893
|
-
<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>
|
|
27894
29648
|
<table aria-label="Guardian receipt drilldowns">
|
|
27895
29649
|
<thead>
|
|
27896
29650
|
<tr><th scope="col">Receipt</th><th scope="col">Type</th><th scope="col">Decision</th><th scope="col">Details</th></tr>
|
|
@@ -27970,9 +29724,47 @@ ${learningEventRows}
|
|
|
27970
29724
|
</section>
|
|
27971
29725
|
</div>
|
|
27972
29726
|
</details>
|
|
29727
|
+
</div>
|
|
29728
|
+
<div class="surface-panel" data-surface-panel="admin" hidden>
|
|
29729
|
+
${adminPanelsHtml}
|
|
29730
|
+
</div>
|
|
27973
29731
|
</main>
|
|
27974
29732
|
<script>
|
|
27975
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();
|
|
27976
29768
|
const filters = Array.from(document.querySelectorAll('[data-activity-filter]'));
|
|
27977
29769
|
const list = document.querySelector('[data-activity-list]');
|
|
27978
29770
|
const emptyState = document.querySelector('[data-activity-empty]');
|
|
@@ -28008,6 +29800,85 @@ ${learningEventRows}
|
|
|
28008
29800
|
}
|
|
28009
29801
|
setFilterCopy(selectedFilter);
|
|
28010
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
|
+
};
|
|
28011
29882
|
const createActivityCard = (activity) => {
|
|
28012
29883
|
const card = document.createElement('article');
|
|
28013
29884
|
card.className = 'activity-card ' + activity.tone;
|
|
@@ -28021,7 +29892,16 @@ ${learningEventRows}
|
|
|
28021
29892
|
title.textContent = activity.title;
|
|
28022
29893
|
const meta = document.createElement('div');
|
|
28023
29894
|
meta.className = 'activity-meta';
|
|
28024
|
-
|
|
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
|
+
}
|
|
28025
29905
|
body.append(title, meta);
|
|
28026
29906
|
const badge = document.createElement('div');
|
|
28027
29907
|
badge.className = 'activity-badge ' + activity.tone;
|
|
@@ -28064,6 +29944,11 @@ ${learningEventRows}
|
|
|
28064
29944
|
applyFilter(filter.getAttribute('data-activity-filter') || 'all');
|
|
28065
29945
|
});
|
|
28066
29946
|
}
|
|
29947
|
+
window.addEventListener('hashchange', highlightReceiptFromHash);
|
|
29948
|
+
hydrateLocalTimeZoneLabels();
|
|
29949
|
+
hydrateLocalTimes();
|
|
29950
|
+
window.setInterval(hydrateLocalTimes, 60 * 1000);
|
|
29951
|
+
highlightReceiptFromHash();
|
|
28067
29952
|
})();
|
|
28068
29953
|
</script>
|
|
28069
29954
|
</body>
|
|
@@ -28200,6 +30085,120 @@ function renderReleaseDoctorRows(status) {
|
|
|
28200
30085
|
function renderDoctorCounts(doctor) {
|
|
28201
30086
|
return `${doctor.checkCounts.pass} pass; ${doctor.checkCounts.warn} warn; ${doctor.checkCounts.fail} fail`;
|
|
28202
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
|
+
}
|
|
28203
30202
|
async function buildControlTowerLocalDataStatus(profile, env = process.env) {
|
|
28204
30203
|
const profileDir = profile.profile_dir;
|
|
28205
30204
|
const encryption = buildControlTowerEncryptionStatus(
|
|
@@ -28464,6 +30463,8 @@ function buildActivityItems(receipts, filter) {
|
|
|
28464
30463
|
tone: activityTone(receipt.disposition),
|
|
28465
30464
|
icon: activityPlainIcon(receipt.disposition),
|
|
28466
30465
|
title: receipt.summary,
|
|
30466
|
+
created_at: receipt.created_at,
|
|
30467
|
+
receipt_type: receipt.receipt_type,
|
|
28467
30468
|
meta: `${formatActivityTime(receipt.created_at)} \xB7 ${receipt.receipt_type}`,
|
|
28468
30469
|
label: activityLabel(receipt.disposition),
|
|
28469
30470
|
raw_content_included: false
|
|
@@ -28476,14 +30477,14 @@ function renderActivityCard(receipt) {
|
|
|
28476
30477
|
<div class="activity-icon">${activityIcon(receipt.disposition)}</div>
|
|
28477
30478
|
<div>
|
|
28478
30479
|
<div class="activity-title">${escapeHtml(receipt.summary)}</div>
|
|
28479
|
-
<div class="activity-meta">${
|
|
30480
|
+
<div class="activity-meta">${renderLocalTime(receipt.created_at)} \xB7 ${escapeHtml(receipt.receipt_type)}</div>
|
|
28480
30481
|
</div>
|
|
28481
30482
|
<div class="activity-badge ${tone}">${escapeHtml(activityLabel(receipt.disposition))}</div>
|
|
28482
30483
|
</article>`;
|
|
28483
30484
|
}
|
|
28484
30485
|
function renderReceiptRow(receipt) {
|
|
28485
30486
|
const policyBasis = receipt.policy_basis.length > 0 ? receipt.policy_basis.join(", ") : "none recorded";
|
|
28486
|
-
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>`;
|
|
28487
30488
|
}
|
|
28488
30489
|
function activityTone(disposition) {
|
|
28489
30490
|
if (disposition === "block") return "block";
|
|
@@ -28512,17 +30513,321 @@ function activityFilterGroup(disposition) {
|
|
|
28512
30513
|
if (disposition === "warn" || disposition === "confirm") return "flagged";
|
|
28513
30514
|
return "allowed";
|
|
28514
30515
|
}
|
|
28515
|
-
function formatActivityTime(createdAt) {
|
|
30516
|
+
function formatActivityTime(createdAt, nowMs = Date.now()) {
|
|
28516
30517
|
const parsed = Date.parse(createdAt);
|
|
28517
30518
|
if (Number.isNaN(parsed)) {
|
|
28518
30519
|
return createdAt;
|
|
28519
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
|
+
}
|
|
28520
30538
|
return new Date(parsed).toLocaleString("en-US", {
|
|
28521
|
-
|
|
28522
|
-
|
|
28523
|
-
|
|
30539
|
+
month: "short",
|
|
30540
|
+
day: "numeric",
|
|
30541
|
+
year: "numeric",
|
|
30542
|
+
hour: "numeric",
|
|
30543
|
+
minute: "2-digit",
|
|
30544
|
+
timeZoneName: "short"
|
|
28524
30545
|
});
|
|
28525
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
|
+
}
|
|
28526
30831
|
function providerDisplayName(providerId) {
|
|
28527
30832
|
if (providerId === "chatgpt") return "ChatGPT";
|
|
28528
30833
|
if (providerId === "claude") return "Claude";
|
|
@@ -28818,7 +31123,7 @@ function listen(server, host, port) {
|
|
|
28818
31123
|
});
|
|
28819
31124
|
});
|
|
28820
31125
|
}
|
|
28821
|
-
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;
|
|
28822
31127
|
var init_daemon = __esm({
|
|
28823
31128
|
"packages/guardian-cli/src/daemon.ts"() {
|
|
28824
31129
|
"use strict";
|
|
@@ -28834,12 +31139,25 @@ var init_daemon = __esm({
|
|
|
28834
31139
|
CONTROL_TOWER_GRAPH_NODE_LIMIT = 12;
|
|
28835
31140
|
CONTROL_TOWER_GRAPH_EDGE_LIMIT = 16;
|
|
28836
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;
|
|
28837
31152
|
CONTROL_TOWER_DATA_FILE_NAMES = [
|
|
28838
31153
|
"profile.json",
|
|
28839
31154
|
"policy-packs.json",
|
|
28840
31155
|
"posture-profile.json",
|
|
28841
31156
|
"preferences.json",
|
|
28842
31157
|
"permissions.json",
|
|
31158
|
+
CONTROL_TOWER_ADMIN_NOTICE_FILE_NAME,
|
|
31159
|
+
CONTROL_TOWER_MARKETPLACE_REQUEST_FILE_NAME,
|
|
31160
|
+
CONTROL_TOWER_PRIME_PACK_FILE_NAME,
|
|
28843
31161
|
"runtime-token"
|
|
28844
31162
|
];
|
|
28845
31163
|
CONTROL_TOWER_DATA_DIRECTORY_NAMES = ["ecl", "receipts", "mcp"];
|
|
@@ -28850,6 +31168,8 @@ var init_daemon = __esm({
|
|
|
28850
31168
|
"restore",
|
|
28851
31169
|
"delete"
|
|
28852
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`;
|
|
28853
31173
|
}
|
|
28854
31174
|
});
|
|
28855
31175
|
|