@posthog/wizard 2.11.0 → 2.13.0
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 +48 -7
- package/dist/{McpScreen-DvUncZBi.js → AuditChecksViewer-B0J7zcY2.js} +434 -22
- package/dist/AuditChecksViewer-B0J7zcY2.js.map +1 -0
- package/dist/{add-mcp-server-to-clients-Br1hDRiB.js → add-mcp-server-to-clients-CUNR00bB.js} +5 -5
- package/dist/{add-mcp-server-to-clients-Br1hDRiB.js.map → add-mcp-server-to-clients-CUNR00bB.js.map} +1 -1
- package/dist/{readiness-gQvQNCeL.js → agent-interface-CV0-vtxj.js} +328 -462
- package/dist/agent-interface-CV0-vtxj.js.map +1 -0
- package/dist/{agent-runner-fWYFO4H0.js → agent-runner-LvVQH31D.js} +21 -31
- package/dist/{agent-runner-fWYFO4H0.js.map → agent-runner-LvVQH31D.js.map} +1 -1
- package/dist/analytics-BH7bEHQR.js +2 -0
- package/dist/analytics-VM7laaFx.js +123 -0
- package/dist/analytics-VM7laaFx.js.map +1 -0
- package/dist/bin.js +529 -42
- package/dist/bin.js.map +1 -1
- package/dist/{debug-D-0xueVl.js → debug-BdcTB7EF.js} +1 -1
- package/dist/debug-Cqi6nVfX.js +686 -0
- package/dist/debug-Cqi6nVfX.js.map +1 -0
- package/dist/{defaults-CPH6eWhN.js → defaults-GbLPuHxj.js} +1 -1
- package/dist/{defaults-CPH6eWhN.js.map → defaults-GbLPuHxj.js.map} +1 -1
- package/dist/{detection-B7GNzve-.js → detection-CSjmal-X.js} +3 -3
- package/dist/{detection-B7GNzve-.js.map → detection-CSjmal-X.js.map} +1 -1
- package/dist/{env-api-key-DU8uIEvo.js → env-api-key-D5G2PrXW.js} +1 -1
- package/dist/{env-api-key-DU8uIEvo.js.map → env-api-key-D5G2PrXW.js.map} +1 -1
- package/dist/{file-DhSBlq-x.js → file-8iNrXHkG.js} +2 -2
- package/dist/{file-DhSBlq-x.js.map → file-8iNrXHkG.js.map} +1 -1
- package/dist/{file-utils-Dy9JncCo.js → file-utils-DnTSiTJw.js} +1 -1
- package/dist/{file-utils-Dy9JncCo.js.map → file-utils-DnTSiTJw.js.map} +1 -1
- package/dist/{package-manager-D3Lo6nXf.js → package-manager-CD8RQW-e.js} +2 -2
- package/dist/{package-manager-D3Lo6nXf.js.map → package-manager-CD8RQW-e.js.map} +1 -1
- package/dist/paths-DJS47p5x.js +26 -0
- package/dist/paths-DJS47p5x.js.map +1 -0
- package/dist/{posthog-integration-D4SRhJIQ.js → posthog-integration-BL21S3T6.js} +41 -13
- package/dist/posthog-integration-BL21S3T6.js.map +1 -0
- package/dist/{posthog-ByrpqEjN.js → posthog-vm0k9PKS.js} +1 -1
- package/dist/{posthog-ByrpqEjN.js.map → posthog-vm0k9PKS.js.map} +1 -1
- package/dist/provisioning-BdQ1ONIg.js +2 -0
- package/dist/provisioning-g9aoVIEd.js +166 -0
- package/dist/provisioning-g9aoVIEd.js.map +1 -0
- package/dist/{registry-DaPKstG3.js → registry-BaMEaAKd.js} +4 -5
- package/dist/{registry-DaPKstG3.js.map → registry-BaMEaAKd.js.map} +1 -1
- package/dist/{router-SgzmfLGi.js → router-COhhuIW3.js} +4 -3
- package/dist/router-COhhuIW3.js.map +1 -0
- package/dist/{setup-utils-y4s-3uKT.js → setup-utils-CNV7FSlY.js} +11 -150
- package/dist/setup-utils-CNV7FSlY.js.map +1 -0
- package/dist/setup-utils-CU4FIqjB.js +2 -0
- package/dist/{start-playground-g1TxpCZ5.js → start-playground-C9GWnVdM.js} +102 -7
- package/dist/start-playground-C9GWnVdM.js.map +1 -0
- package/dist/start-tui-B_zwutLe.js +4195 -0
- package/dist/start-tui-B_zwutLe.js.map +1 -0
- package/dist/{steps-D1zKDqAo.js → steps-Dawz7k3T.js} +8 -8
- package/dist/steps-Dawz7k3T.js.map +1 -0
- package/dist/{task-stream-DX_jKDQu.js → task-stream-CX7Uf6EM.js} +4 -4
- package/dist/{task-stream-DX_jKDQu.js.map → task-stream-CX7Uf6EM.js.map} +1 -1
- package/dist/{telemetry-CyUUSAYy.js → telemetry-D6bjWA-A.js} +2 -2
- package/dist/{telemetry-CyUUSAYy.js.map → telemetry-D6bjWA-A.js.map} +1 -1
- package/dist/{wizard-abort-Buodno3f.js → wizard-abort-CJkNkSjT.js} +6 -4
- package/dist/{wizard-abort-Buodno3f.js.map → wizard-abort-CJkNkSjT.js.map} +1 -1
- package/dist/{wizard-abort-DZmO_sIZ.js → wizard-abort-Dl0BkqhT.js} +1 -1
- package/dist/wizard-session-BQC9vy9Z.js +2 -0
- package/dist/{wizard-session-D5bggSsu.js → wizard-session-BcNJTl2I.js} +1 -1
- package/dist/{wizard-session-D5bggSsu.js.map → wizard-session-BcNJTl2I.js.map} +1 -1
- package/dist/{wizard-ui-BExOjdjA.js → wizard-ui-YdGFRyu_.js} +1 -1
- package/dist/wizard-ui-YdGFRyu_.js.map +1 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/McpScreen-DvUncZBi.js.map +0 -1
- package/dist/agent-skill-DJOzDaQV.js +0 -59
- package/dist/agent-skill-DJOzDaQV.js.map +0 -1
- package/dist/analytics-CfAUlt6-.js +0 -2
- package/dist/analytics-D3rY3TaN.js +0 -210
- package/dist/analytics-D3rY3TaN.js.map +0 -1
- package/dist/debug-gWEjmYVV.js +0 -203
- package/dist/debug-gWEjmYVV.js.map +0 -1
- package/dist/paths-BL-x2rFy.js +0 -16
- package/dist/paths-BL-x2rFy.js.map +0 -1
- package/dist/posthog-integration-D4SRhJIQ.js.map +0 -1
- package/dist/readiness-gQvQNCeL.js.map +0 -1
- package/dist/router-SgzmfLGi.js.map +0 -1
- package/dist/setup-utils-_ONxN-TT.js +0 -2
- package/dist/setup-utils-y4s-3uKT.js.map +0 -1
- package/dist/start-playground-g1TxpCZ5.js.map +0 -1
- package/dist/start-tui-CQef69NR.js +0 -2167
- package/dist/start-tui-CQef69NR.js.map +0 -1
- package/dist/steps-D1zKDqAo.js.map +0 -1
- package/dist/wizard-session-COhklXAF.js +0 -2
- package/dist/wizard-ui-BExOjdjA.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { n as __require } from "./rolldown-runtime-B_-DWIq7.js";
|
|
2
|
-
import { a as getLogFilePath, c as getUI, o as initLogFile, r as debug, s as logToFile } from "./debug-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { f as getLlmGatewayUrlFromHost } from "./setup-utils-
|
|
6
|
-
import { n as ADDITIONAL_FEATURE_PROMPTS } from "./wizard-session-
|
|
7
|
-
import { n as registerCleanup, r as wizardAbort, t as WizardError } from "./wizard-abort-
|
|
2
|
+
import { A as POSTHOG_PROPERTY_HEADER_PREFIX, B as WIZARD_VARIANTS, L as WIZARD_REMARK_EVENT_NAME, O as POSTHOG_FLAG_HEADER_PREFIX, V as WIZARD_VARIANT_FLAG_KEY, a as getLogFilePath, c as getUI, o as initLogFile, r as debug, s as logToFile, z as WIZARD_USER_AGENT } from "./debug-Cqi6nVfX.js";
|
|
3
|
+
import { i as WIZARD_YARA_REPORT_FILE, o as skillTmpPath } from "./paths-DJS47p5x.js";
|
|
4
|
+
import { n as analytics } from "./analytics-VM7laaFx.js";
|
|
5
|
+
import { f as getLlmGatewayUrlFromHost } from "./setup-utils-CNV7FSlY.js";
|
|
6
|
+
import { n as ADDITIONAL_FEATURE_PROMPTS } from "./wizard-session-BcNJTl2I.js";
|
|
7
|
+
import { n as registerCleanup, r as wizardAbort, t as WizardError } from "./wizard-abort-CJkNkSjT.js";
|
|
8
8
|
import * as fs$1 from "fs";
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import path from "path";
|
|
@@ -175,6 +175,49 @@ const LINTING_TOOLS = [
|
|
|
175
175
|
"yamllint"
|
|
176
176
|
];
|
|
177
177
|
//#endregion
|
|
178
|
+
//#region src/lib/workflows/audit/types.ts
|
|
179
|
+
/** Single source of truth for status glyph + color across audit views. */
|
|
180
|
+
const AUDIT_SEVERITY_STYLE = {
|
|
181
|
+
pending: {
|
|
182
|
+
glyph: "◌",
|
|
183
|
+
color: "gray"
|
|
184
|
+
},
|
|
185
|
+
pass: {
|
|
186
|
+
glyph: "✔",
|
|
187
|
+
color: "green"
|
|
188
|
+
},
|
|
189
|
+
error: {
|
|
190
|
+
glyph: "✘",
|
|
191
|
+
color: "red"
|
|
192
|
+
},
|
|
193
|
+
warning: {
|
|
194
|
+
glyph: "⚠",
|
|
195
|
+
color: "yellow"
|
|
196
|
+
},
|
|
197
|
+
suggestion: {
|
|
198
|
+
glyph: "•",
|
|
199
|
+
color: "cyan"
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const AUDIT_CHECKS_FILE = ".posthog-audit-checks.json";
|
|
203
|
+
const AUDIT_REPORT_FILE = "posthog-audit-report.md";
|
|
204
|
+
const AUDIT_CHECKS_KEY = "auditChecks";
|
|
205
|
+
function getAuditChecks(session) {
|
|
206
|
+
const raw = session.frameworkContext[AUDIT_CHECKS_KEY];
|
|
207
|
+
return Array.isArray(raw) ? raw : [];
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Read the audit checks ledger off disk. Validation lives at write time —
|
|
211
|
+
* every writer (`audit_seed_checks` / `audit_add_checks` / `audit_resolve_checks`
|
|
212
|
+
* MCP tools, `seedAuditLedger`) zod-parses entries before the atomic write,
|
|
213
|
+
* so by the time the file watcher fires we trust the shape and only guard
|
|
214
|
+
* against the file not being a JSON array (corrupted / hand-edited / not yet
|
|
215
|
+
* seeded).
|
|
216
|
+
*/
|
|
217
|
+
function coerceAuditChecks(parsed) {
|
|
218
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
178
221
|
//#region src/lib/wizard-tools.ts
|
|
179
222
|
/**
|
|
180
223
|
* Unified in-process MCP server for the PostHog wizard.
|
|
@@ -183,6 +226,8 @@ const LINTING_TOOLS = [
|
|
|
183
226
|
* - check_env_keys: Check which env var keys exist in a .env file
|
|
184
227
|
* - set_env_values: Create/update env vars in a .env file
|
|
185
228
|
* - detect_package_manager: Detect the project's package manager(s)
|
|
229
|
+
* - load_skill_menu / install_skill: Skill installation
|
|
230
|
+
* - audit_seed_checks / audit_add_checks / audit_resolve_checks: Audit ledger ownership
|
|
186
231
|
*/
|
|
187
232
|
let _sdkModule$1 = null;
|
|
188
233
|
async function getSDKModule$1() {
|
|
@@ -232,6 +277,7 @@ function downloadSkill(skillEntry, installDir, skillsRoot) {
|
|
|
232
277
|
"-d",
|
|
233
278
|
skillDir
|
|
234
279
|
], { timeout: 3e4 });
|
|
280
|
+
fs.writeFileSync(path.join(skillDir, ".posthog-wizard"), "");
|
|
235
281
|
try {
|
|
236
282
|
fs.unlinkSync(tmpFile);
|
|
237
283
|
} catch {}
|
|
@@ -321,6 +367,122 @@ function mergeEnvValues(content, values) {
|
|
|
321
367
|
}
|
|
322
368
|
return result;
|
|
323
369
|
}
|
|
370
|
+
const AUDIT_STATUSES = [
|
|
371
|
+
"pending",
|
|
372
|
+
"pass",
|
|
373
|
+
"error",
|
|
374
|
+
"warning",
|
|
375
|
+
"suggestion"
|
|
376
|
+
];
|
|
377
|
+
const auditCheckSchema = z.object({
|
|
378
|
+
id: z.string().min(1),
|
|
379
|
+
area: z.string().min(1),
|
|
380
|
+
label: z.string().min(1),
|
|
381
|
+
status: z.enum(AUDIT_STATUSES),
|
|
382
|
+
file: z.string().optional(),
|
|
383
|
+
details: z.string().optional()
|
|
384
|
+
});
|
|
385
|
+
const auditUpdateSchema = z.object({
|
|
386
|
+
id: z.string().min(1),
|
|
387
|
+
status: z.enum(AUDIT_STATUSES),
|
|
388
|
+
file: z.string().optional(),
|
|
389
|
+
details: z.string().optional()
|
|
390
|
+
});
|
|
391
|
+
/**
|
|
392
|
+
* Atomically write JSON: write to .tmp then rename. The rename is what bumps
|
|
393
|
+
* the file's mtime, which is what the UI's file watcher polls on.
|
|
394
|
+
*/
|
|
395
|
+
function writeLedgerAtomic(targetPath, checks) {
|
|
396
|
+
const tmpPath = `${targetPath}.tmp`;
|
|
397
|
+
fs.writeFileSync(tmpPath, JSON.stringify(checks, null, 2), "utf8");
|
|
398
|
+
fs.renameSync(tmpPath, targetPath);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Apply a batch of patches to the ledger by id. Returns the new array and the
|
|
402
|
+
* list of update ids that didn't match any existing check.
|
|
403
|
+
*/
|
|
404
|
+
function applyAuditUpdates(current, updates) {
|
|
405
|
+
const byId = new Map(current.map((c) => [c.id, c]));
|
|
406
|
+
const unknown = [];
|
|
407
|
+
for (const u of updates) {
|
|
408
|
+
const existing = byId.get(u.id);
|
|
409
|
+
if (!existing) {
|
|
410
|
+
unknown.push(u.id);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
byId.set(u.id, {
|
|
414
|
+
...existing,
|
|
415
|
+
status: u.status,
|
|
416
|
+
...u.file !== void 0 ? { file: u.file } : {},
|
|
417
|
+
...u.details !== void 0 ? { details: u.details } : {}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
next: current.map((c) => byId.get(c.id) ?? c),
|
|
422
|
+
unknown
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Append new checks to a seeded ledger. Duplicate ids are reported without
|
|
427
|
+
* mutating the current ledger, including duplicates inside the additions.
|
|
428
|
+
*/
|
|
429
|
+
function applyAuditAdditions(current, additions) {
|
|
430
|
+
const existingIds = new Set(current.map((c) => c.id));
|
|
431
|
+
const additionIds = /* @__PURE__ */ new Set();
|
|
432
|
+
const duplicates = [];
|
|
433
|
+
for (const check of additions) {
|
|
434
|
+
if (existingIds.has(check.id) || additionIds.has(check.id)) {
|
|
435
|
+
duplicates.push(check.id);
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
additionIds.add(check.id);
|
|
439
|
+
}
|
|
440
|
+
if (duplicates.length > 0) return {
|
|
441
|
+
next: current,
|
|
442
|
+
duplicates
|
|
443
|
+
};
|
|
444
|
+
return {
|
|
445
|
+
next: [...current, ...additions],
|
|
446
|
+
duplicates: []
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function readLedger(targetPath) {
|
|
450
|
+
if (!fs.existsSync(targetPath)) return [];
|
|
451
|
+
try {
|
|
452
|
+
return coerceAuditChecks(JSON.parse(fs.readFileSync(targetPath, "utf8")));
|
|
453
|
+
} catch {
|
|
454
|
+
return [];
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function appendAuditChecksToLedger(targetPath, additions) {
|
|
458
|
+
if (!fs.existsSync(targetPath)) return {
|
|
459
|
+
ok: false,
|
|
460
|
+
reason: "missing-ledger"
|
|
461
|
+
};
|
|
462
|
+
const { next, duplicates } = applyAuditAdditions(readLedger(targetPath), additions);
|
|
463
|
+
if (duplicates.length > 0) return {
|
|
464
|
+
ok: false,
|
|
465
|
+
reason: "duplicate-ids",
|
|
466
|
+
ids: duplicates
|
|
467
|
+
};
|
|
468
|
+
writeLedgerAtomic(targetPath, next);
|
|
469
|
+
return {
|
|
470
|
+
ok: true,
|
|
471
|
+
added: additions.length
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Single async mutex shared by audit tools — guarantees a read-modify-write
|
|
476
|
+
* cycle on the ledger is atomic across concurrent tool calls (e.g. future subagents).
|
|
477
|
+
*/
|
|
478
|
+
function makeMutex() {
|
|
479
|
+
let chain = Promise.resolve();
|
|
480
|
+
return async function run(fn) {
|
|
481
|
+
const next = chain.then(() => fn());
|
|
482
|
+
chain = next.catch(() => void 0);
|
|
483
|
+
return next;
|
|
484
|
+
};
|
|
485
|
+
}
|
|
324
486
|
const SERVER_NAME = "wizard-tools";
|
|
325
487
|
/**
|
|
326
488
|
* Create the unified in-process MCP server with all wizard tools.
|
|
@@ -335,101 +497,162 @@ async function createWizardToolsServer(options) {
|
|
|
335
497
|
if (menu) cachedSkillMenu = menu.categories;
|
|
336
498
|
const keys = Object.keys(cachedSkillMenu);
|
|
337
499
|
if (keys.length > 0) categoryNames = keys;
|
|
500
|
+
const checkEnvKeys = tool("check_env_keys", "Check which environment variable keys are present or missing in a .env file. Never reveals values.", {
|
|
501
|
+
filePath: z.string().describe("Path to the .env file, relative to the project root"),
|
|
502
|
+
keys: z.array(z.string()).describe("Environment variable key names to check")
|
|
503
|
+
}, (args) => {
|
|
504
|
+
const resolved = resolveEnvPath(workingDirectory, args.filePath);
|
|
505
|
+
logToFile(`check_env_keys: ${resolved}, keys: ${args.keys.join(", ")}`);
|
|
506
|
+
const existingKeys = fs.existsSync(resolved) ? parseEnvKeys(fs.readFileSync(resolved, "utf8")) : /* @__PURE__ */ new Set();
|
|
507
|
+
const results = {};
|
|
508
|
+
for (const key of args.keys) results[key] = existingKeys.has(key) ? "present" : "missing";
|
|
509
|
+
return { content: [{
|
|
510
|
+
type: "text",
|
|
511
|
+
text: JSON.stringify(results, null, 2)
|
|
512
|
+
}] };
|
|
513
|
+
});
|
|
514
|
+
const setEnvValues = tool("set_env_values", "Create or update environment variable keys in a .env file. Creates the file if it does not exist. Ensures .gitignore coverage.", {
|
|
515
|
+
filePath: z.string().describe("Path to the .env file, relative to the project root"),
|
|
516
|
+
values: z.record(z.string(), z.string()).describe("Key-value pairs to set")
|
|
517
|
+
}, (args) => {
|
|
518
|
+
const forbidden = Object.keys(args.values).find((k) => k.toUpperCase() === "POSTHOG_KEY");
|
|
519
|
+
if (forbidden) return {
|
|
520
|
+
content: [{
|
|
521
|
+
type: "text",
|
|
522
|
+
text: `Error: "${forbidden}" is not a valid PostHog env var name. Use the project-specific key name from your framework's integration guide (e.g. NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN).`
|
|
523
|
+
}],
|
|
524
|
+
isError: true
|
|
525
|
+
};
|
|
526
|
+
const resolved = resolveEnvPath(workingDirectory, args.filePath);
|
|
527
|
+
logToFile(`set_env_values: ${resolved}, keys: ${Object.keys(args.values).join(", ")}`);
|
|
528
|
+
const content = mergeEnvValues(fs.existsSync(resolved) ? fs.readFileSync(resolved, "utf8") : "", args.values);
|
|
529
|
+
const dir = path.dirname(resolved);
|
|
530
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
531
|
+
fs.writeFileSync(resolved, content, "utf8");
|
|
532
|
+
ensureGitignoreCoverage(workingDirectory, path.basename(resolved));
|
|
533
|
+
return { content: [{
|
|
534
|
+
type: "text",
|
|
535
|
+
text: `Updated ${Object.keys(args.values).length} key(s) in ${args.filePath}`
|
|
536
|
+
}] };
|
|
537
|
+
});
|
|
538
|
+
const detectPM = tool("detect_package_manager", "Detect which package manager(s) the project uses. Returns the name, install command, and run command for each detected package manager. Call this before running any install commands.", {}, async () => {
|
|
539
|
+
logToFile(`detect_package_manager: scanning ${workingDirectory}`);
|
|
540
|
+
const result = await detectPackageManager(workingDirectory);
|
|
541
|
+
logToFile(`detect_package_manager: detected ${result.detected.length} package manager(s)`);
|
|
542
|
+
return { content: [{
|
|
543
|
+
type: "text",
|
|
544
|
+
text: JSON.stringify(result, null, 2)
|
|
545
|
+
}] };
|
|
546
|
+
});
|
|
547
|
+
const loadSkillMenu = tool("load_skill_menu", "Load available PostHog skills for a category. Returns skill IDs and names. Call this first, then use install_skill with the chosen ID.", { category: z.enum(categoryNames).describe("Skill category") }, (args) => {
|
|
548
|
+
const skills = cachedSkillMenu[args.category];
|
|
549
|
+
if (!skills || skills.length === 0) return {
|
|
550
|
+
content: [{
|
|
551
|
+
type: "text",
|
|
552
|
+
text: `No skills found for category "${args.category}".`
|
|
553
|
+
}],
|
|
554
|
+
isError: true
|
|
555
|
+
};
|
|
556
|
+
const menuText = skills.map((s) => `- ${s.id}: ${s.name}`).join("\n");
|
|
557
|
+
logToFile(`load_skill_menu: returning ${skills.length} skills for "${args.category}"`);
|
|
558
|
+
return { content: [{
|
|
559
|
+
type: "text",
|
|
560
|
+
text: menuText
|
|
561
|
+
}] };
|
|
562
|
+
});
|
|
563
|
+
const installSkill = tool("install_skill", "Download and install a PostHog skill by ID. Call load_skill_menu first to see available skills. Extracts the skill to .claude/skills/<skillId>/.", { skillId: z.string().describe("Skill ID from the skill menu (e.g., \"integration-nextjs-app-router\")") }, (args) => {
|
|
564
|
+
if (!/^[a-z0-9][a-z0-9_-]*$/.test(args.skillId)) return {
|
|
565
|
+
content: [{
|
|
566
|
+
type: "text",
|
|
567
|
+
text: "Error: skillId must be lowercase alphanumeric with hyphens."
|
|
568
|
+
}],
|
|
569
|
+
isError: true
|
|
570
|
+
};
|
|
571
|
+
const skill = Object.values(cachedSkillMenu).flat().find((s) => s.id === args.skillId);
|
|
572
|
+
if (!skill) return {
|
|
573
|
+
content: [{
|
|
574
|
+
type: "text",
|
|
575
|
+
text: `Error: skill "${args.skillId}" not found. Use load_skill_menu to see available skills.`
|
|
576
|
+
}],
|
|
577
|
+
isError: true
|
|
578
|
+
};
|
|
579
|
+
const result = downloadSkill(skill, workingDirectory);
|
|
580
|
+
if (result.success) return { content: [{
|
|
581
|
+
type: "text",
|
|
582
|
+
text: `Skill installed to .claude/skills/${args.skillId}/`
|
|
583
|
+
}] };
|
|
584
|
+
else return {
|
|
585
|
+
content: [{
|
|
586
|
+
type: "text",
|
|
587
|
+
text: `Error installing skill: ${result.error}`
|
|
588
|
+
}],
|
|
589
|
+
isError: true
|
|
590
|
+
};
|
|
591
|
+
});
|
|
592
|
+
const auditLedgerPath = path.join(workingDirectory, AUDIT_CHECKS_FILE);
|
|
593
|
+
const auditMutex = makeMutex();
|
|
338
594
|
return createSdkMcpServer({
|
|
339
595
|
name: SERVER_NAME,
|
|
340
596
|
version: "1.0.0",
|
|
341
597
|
tools: [
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
type: "text",
|
|
353
|
-
text: JSON.stringify(results, null, 2)
|
|
354
|
-
}] };
|
|
355
|
-
}),
|
|
356
|
-
tool("set_env_values", "Create or update environment variable keys in a .env file. Creates the file if it does not exist. Ensures .gitignore coverage.", {
|
|
357
|
-
filePath: z.string().describe("Path to the .env file, relative to the project root"),
|
|
358
|
-
values: z.record(z.string(), z.string()).describe("Key-value pairs to set")
|
|
359
|
-
}, (args) => {
|
|
360
|
-
const forbidden = Object.keys(args.values).find((k) => k.toUpperCase() === "POSTHOG_KEY");
|
|
361
|
-
if (forbidden) return {
|
|
362
|
-
content: [{
|
|
598
|
+
checkEnvKeys,
|
|
599
|
+
setEnvValues,
|
|
600
|
+
detectPM,
|
|
601
|
+
loadSkillMenu,
|
|
602
|
+
installSkill,
|
|
603
|
+
tool("audit_seed_checks", "Seed the audit ledger at .posthog-audit-checks.json with the full set of pending checks. Call this once at the start of the audit. Atomically replaces any existing ledger.", { checks: z.array(auditCheckSchema).describe("Full pending checklist to write to the ledger") }, async (args) => {
|
|
604
|
+
return auditMutex(() => {
|
|
605
|
+
writeLedgerAtomic(auditLedgerPath, args.checks);
|
|
606
|
+
logToFile(`audit_seed_checks: wrote ${args.checks.length} entries`);
|
|
607
|
+
return { content: [{
|
|
363
608
|
type: "text",
|
|
364
|
-
text: `
|
|
365
|
-
}]
|
|
366
|
-
|
|
367
|
-
};
|
|
368
|
-
const resolved = resolveEnvPath(workingDirectory, args.filePath);
|
|
369
|
-
logToFile(`set_env_values: ${resolved}, keys: ${Object.keys(args.values).join(", ")}`);
|
|
370
|
-
const content = mergeEnvValues(fs.existsSync(resolved) ? fs.readFileSync(resolved, "utf8") : "", args.values);
|
|
371
|
-
const dir = path.dirname(resolved);
|
|
372
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
373
|
-
fs.writeFileSync(resolved, content, "utf8");
|
|
374
|
-
ensureGitignoreCoverage(workingDirectory, path.basename(resolved));
|
|
375
|
-
return { content: [{
|
|
376
|
-
type: "text",
|
|
377
|
-
text: `Updated ${Object.keys(args.values).length} key(s) in ${args.filePath}`
|
|
378
|
-
}] };
|
|
379
|
-
}),
|
|
380
|
-
tool("detect_package_manager", "Detect which package manager(s) the project uses. Returns the name, install command, and run command for each detected package manager. Call this before running any install commands.", {}, async () => {
|
|
381
|
-
logToFile(`detect_package_manager: scanning ${workingDirectory}`);
|
|
382
|
-
const result = await detectPackageManager(workingDirectory);
|
|
383
|
-
logToFile(`detect_package_manager: detected ${result.detected.length} package manager(s)`);
|
|
384
|
-
return { content: [{
|
|
385
|
-
type: "text",
|
|
386
|
-
text: JSON.stringify(result, null, 2)
|
|
387
|
-
}] };
|
|
609
|
+
text: `Seeded ${args.checks.length} audit checks.`
|
|
610
|
+
}] };
|
|
611
|
+
});
|
|
388
612
|
}),
|
|
389
|
-
tool("
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
613
|
+
tool("audit_add_checks", "Append one or more pending checks to the existing audit ledger at .posthog-audit-checks.json. Call audit_seed_checks first. Atomically rejects duplicate ids without changing the ledger.", { checks: z.array(auditCheckSchema).min(1).describe("Additional checks to append to the existing ledger") }, async (args) => {
|
|
614
|
+
return auditMutex(() => {
|
|
615
|
+
const result = appendAuditChecksToLedger(auditLedgerPath, args.checks);
|
|
616
|
+
if (!result.ok) {
|
|
617
|
+
if (result.reason === "missing-ledger") return {
|
|
618
|
+
content: [{
|
|
619
|
+
type: "text",
|
|
620
|
+
text: "Error: audit ledger does not exist. Run audit_seed_checks first."
|
|
621
|
+
}],
|
|
622
|
+
isError: true
|
|
623
|
+
};
|
|
624
|
+
return {
|
|
625
|
+
content: [{
|
|
626
|
+
type: "text",
|
|
627
|
+
text: `Error: duplicate check id(s): ${result.ids.join(", ")}. Check ids must be unique.`
|
|
628
|
+
}],
|
|
629
|
+
isError: true
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
logToFile(`audit_add_checks: added ${result.added} entries`);
|
|
633
|
+
return { content: [{
|
|
393
634
|
type: "text",
|
|
394
|
-
text: `
|
|
395
|
-
}]
|
|
396
|
-
|
|
397
|
-
};
|
|
398
|
-
const menuText = skills.map((s) => `- ${s.id}: ${s.name}`).join("\n");
|
|
399
|
-
logToFile(`load_skill_menu: returning ${skills.length} skills for "${args.category}"`);
|
|
400
|
-
return { content: [{
|
|
401
|
-
type: "text",
|
|
402
|
-
text: menuText
|
|
403
|
-
}] };
|
|
635
|
+
text: `Added ${result.added} audit check(s).`
|
|
636
|
+
}] };
|
|
637
|
+
});
|
|
404
638
|
}),
|
|
405
|
-
tool("
|
|
406
|
-
|
|
407
|
-
|
|
639
|
+
tool("audit_resolve_checks", "Resolve one or more audit checks by id. Patches each entry's status (and optional file/details) and writes the ledger back atomically. Concurrent calls serialize.", { updates: z.array(auditUpdateSchema).min(1).describe("Patches to apply, keyed by check id") }, async (args) => {
|
|
640
|
+
return auditMutex(() => {
|
|
641
|
+
const { next, unknown } = applyAuditUpdates(readLedger(auditLedgerPath), args.updates);
|
|
642
|
+
if (unknown.length > 0) return {
|
|
643
|
+
content: [{
|
|
644
|
+
type: "text",
|
|
645
|
+
text: `Error: unknown check id(s): ${unknown.join(", ")}. Run audit_seed_checks first or check the id.`
|
|
646
|
+
}],
|
|
647
|
+
isError: true
|
|
648
|
+
};
|
|
649
|
+
writeLedgerAtomic(auditLedgerPath, next);
|
|
650
|
+
logToFile(`audit_resolve_checks: applied ${args.updates.length} update(s)`);
|
|
651
|
+
return { content: [{
|
|
408
652
|
type: "text",
|
|
409
|
-
text:
|
|
410
|
-
}]
|
|
411
|
-
|
|
412
|
-
};
|
|
413
|
-
const skill = Object.values(cachedSkillMenu).flat().find((s) => s.id === args.skillId);
|
|
414
|
-
if (!skill) return {
|
|
415
|
-
content: [{
|
|
416
|
-
type: "text",
|
|
417
|
-
text: `Error: skill "${args.skillId}" not found. Use load_skill_menu to see available skills.`
|
|
418
|
-
}],
|
|
419
|
-
isError: true
|
|
420
|
-
};
|
|
421
|
-
const result = downloadSkill(skill, workingDirectory);
|
|
422
|
-
if (result.success) return { content: [{
|
|
423
|
-
type: "text",
|
|
424
|
-
text: `Skill installed to .claude/skills/${args.skillId}/`
|
|
425
|
-
}] };
|
|
426
|
-
else return {
|
|
427
|
-
content: [{
|
|
428
|
-
type: "text",
|
|
429
|
-
text: `Error installing skill: ${result.error}`
|
|
430
|
-
}],
|
|
431
|
-
isError: true
|
|
432
|
-
};
|
|
653
|
+
text: `Resolved ${args.updates.length} check(s).`
|
|
654
|
+
}] };
|
|
655
|
+
});
|
|
433
656
|
})
|
|
434
657
|
]
|
|
435
658
|
});
|
|
@@ -440,7 +663,10 @@ const WIZARD_TOOL_NAMES = [
|
|
|
440
663
|
`${SERVER_NAME}:set_env_values`,
|
|
441
664
|
`${SERVER_NAME}:detect_package_manager`,
|
|
442
665
|
`${SERVER_NAME}:load_skill_menu`,
|
|
443
|
-
`${SERVER_NAME}:install_skill
|
|
666
|
+
`${SERVER_NAME}:install_skill`,
|
|
667
|
+
`${SERVER_NAME}:audit_seed_checks`,
|
|
668
|
+
`${SERVER_NAME}:audit_add_checks`,
|
|
669
|
+
`${SERVER_NAME}:audit_resolve_checks`
|
|
444
670
|
];
|
|
445
671
|
//#endregion
|
|
446
672
|
//#region src/lib/yara-scanner.ts
|
|
@@ -1187,10 +1413,13 @@ function buildWizardMetadata(flags = {}) {
|
|
|
1187
1413
|
return { ...(variantKey && WIZARD_VARIANTS[variantKey]) ?? WIZARD_VARIANTS["base"] };
|
|
1188
1414
|
}
|
|
1189
1415
|
/**
|
|
1190
|
-
* Build env for the SDK subprocess: process.env plus ANTHROPIC_CUSTOM_HEADERS
|
|
1416
|
+
* Build env for the SDK subprocess: process.env plus ANTHROPIC_CUSTOM_HEADERS, which always
|
|
1417
|
+
* includes `x-posthog-use-bedrock-fallback: true` so the LLM gateway falls back to Bedrock on
|
|
1418
|
+
* Anthropic 5xx, plus any wizard metadata/flags.
|
|
1191
1419
|
*/
|
|
1192
1420
|
function buildAgentEnv(wizardMetadata, wizardFlags) {
|
|
1193
1421
|
const headers = createCustomHeaders();
|
|
1422
|
+
headers.add("x-posthog-use-bedrock-fallback", "true");
|
|
1194
1423
|
for (const [key, value] of Object.entries(wizardMetadata)) headers.add(key.startsWith("X-POSTHOG-PROPERTY-") ? key : `${POSTHOG_PROPERTY_HEADER_PREFIX}${key}`, value);
|
|
1195
1424
|
for (const [flagKey, variant] of Object.entries(wizardFlags)) {
|
|
1196
1425
|
if (!flagKey.toLowerCase().startsWith("wizard")) continue;
|
|
@@ -1394,10 +1623,11 @@ async function initializeAgent(config, options) {
|
|
|
1394
1623
|
detectPackageManager: config.detectPackageManager,
|
|
1395
1624
|
skillsBaseUrl: config.skillsBaseUrl
|
|
1396
1625
|
});
|
|
1626
|
+
const model = config.integrationLabel === "audit-3000" ? "anthropic/claude-opus-4-7" : "anthropic/claude-sonnet-4-6";
|
|
1397
1627
|
const agentRunConfig = {
|
|
1398
1628
|
workingDirectory: config.workingDirectory,
|
|
1399
1629
|
mcpServers,
|
|
1400
|
-
model
|
|
1630
|
+
model,
|
|
1401
1631
|
wizardFlags: config.wizardFlags,
|
|
1402
1632
|
wizardMetadata: config.wizardMetadata
|
|
1403
1633
|
};
|
|
@@ -1496,8 +1726,6 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
|
|
|
1496
1726
|
spinner.stop(successMessage);
|
|
1497
1727
|
return {};
|
|
1498
1728
|
};
|
|
1499
|
-
let eventPlanWatcher;
|
|
1500
|
-
let eventPlanInterval;
|
|
1501
1729
|
const abortController = new AbortController();
|
|
1502
1730
|
let abortReason = null;
|
|
1503
1731
|
try {
|
|
@@ -1508,6 +1736,7 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
|
|
|
1508
1736
|
"Glob",
|
|
1509
1737
|
"Grep",
|
|
1510
1738
|
"Bash",
|
|
1739
|
+
"Task",
|
|
1511
1740
|
"ListMcpResourcesTool",
|
|
1512
1741
|
"Skill",
|
|
1513
1742
|
...WIZARD_TOOL_NAMES
|
|
@@ -1586,31 +1815,6 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
|
|
|
1586
1815
|
}
|
|
1587
1816
|
}
|
|
1588
1817
|
});
|
|
1589
|
-
const eventPlanPath = path.join(agentConfig.workingDirectory, ".posthog-events.json");
|
|
1590
|
-
const readEventPlan = () => {
|
|
1591
|
-
try {
|
|
1592
|
-
const content = fs$1.readFileSync(eventPlanPath, "utf-8");
|
|
1593
|
-
const parsed = JSON.parse(content);
|
|
1594
|
-
if (Array.isArray(parsed)) getUI().setEventPlan(parsed.map((e) => ({
|
|
1595
|
-
name: e.name ?? e.event ?? "",
|
|
1596
|
-
description: e.description ?? ""
|
|
1597
|
-
})));
|
|
1598
|
-
} catch {}
|
|
1599
|
-
};
|
|
1600
|
-
try {
|
|
1601
|
-
eventPlanWatcher = fs$1.watch(eventPlanPath, () => readEventPlan());
|
|
1602
|
-
readEventPlan();
|
|
1603
|
-
} catch {
|
|
1604
|
-
eventPlanInterval = setInterval(() => {
|
|
1605
|
-
try {
|
|
1606
|
-
fs$1.accessSync(eventPlanPath);
|
|
1607
|
-
readEventPlan();
|
|
1608
|
-
clearInterval(eventPlanInterval);
|
|
1609
|
-
eventPlanInterval = void 0;
|
|
1610
|
-
eventPlanWatcher = fs$1.watch(eventPlanPath, () => readEventPlan());
|
|
1611
|
-
} catch {}
|
|
1612
|
-
}, 1e3);
|
|
1613
|
-
}
|
|
1614
1818
|
for await (const message of response) {
|
|
1615
1819
|
if (!loggedInitialContext && message.type === "assistant") {
|
|
1616
1820
|
const usage = message.message?.usage;
|
|
@@ -1744,8 +1948,6 @@ async function runAgent(agentConfig, prompt, options, spinner, config, middlewar
|
|
|
1744
1948
|
debug("Full error:", error);
|
|
1745
1949
|
throw error;
|
|
1746
1950
|
} finally {
|
|
1747
|
-
eventPlanWatcher?.close();
|
|
1748
|
-
if (eventPlanInterval) clearInterval(eventPlanInterval);
|
|
1749
1951
|
if (!receivedSuccessResult) {
|
|
1750
1952
|
const durationMs = Date.now() - startTime;
|
|
1751
1953
|
analytics.wizardCapture("agent aborted", {
|
|
@@ -1815,342 +2017,6 @@ function handleSDKMessage(message, options, spinner, collectedText, receivedSucc
|
|
|
1815
2017
|
}
|
|
1816
2018
|
}
|
|
1817
2019
|
//#endregion
|
|
1818
|
-
|
|
1819
|
-
function mapIndicator(v) {
|
|
1820
|
-
switch (v) {
|
|
1821
|
-
case "none": return "healthy";
|
|
1822
|
-
case "minor": return "degraded";
|
|
1823
|
-
case "major":
|
|
1824
|
-
case "critical": return "down";
|
|
1825
|
-
default: return "degraded";
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
function mapComponentRaw(v) {
|
|
1829
|
-
switch (v) {
|
|
1830
|
-
case "operational": return "healthy";
|
|
1831
|
-
case "degraded_performance":
|
|
1832
|
-
case "under_maintenance": return "degraded";
|
|
1833
|
-
case "partial_outage":
|
|
1834
|
-
case "major_outage": return "down";
|
|
1835
|
-
default: return "degraded";
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
function errResult$1(error) {
|
|
1839
|
-
return {
|
|
1840
|
-
status: "degraded",
|
|
1841
|
-
error
|
|
1842
|
-
};
|
|
1843
|
-
}
|
|
1844
|
-
async function fetchStatuspageIndicator(url, timeoutMs = 5e3) {
|
|
1845
|
-
try {
|
|
1846
|
-
const controller = new AbortController();
|
|
1847
|
-
const tid = setTimeout(() => controller.abort(), timeoutMs);
|
|
1848
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
1849
|
-
clearTimeout(tid);
|
|
1850
|
-
if (!res.ok) return errResult$1(`HTTP ${res.status}`);
|
|
1851
|
-
const indicator = (await res.json()).status?.indicator ?? null;
|
|
1852
|
-
return {
|
|
1853
|
-
status: mapIndicator(indicator),
|
|
1854
|
-
rawIndicator: indicator ?? void 0
|
|
1855
|
-
};
|
|
1856
|
-
} catch (e) {
|
|
1857
|
-
if (e instanceof Error && e.name === "AbortError") return errResult$1("Request timed out");
|
|
1858
|
-
return errResult$1(e instanceof Error ? e.message : "Unknown error");
|
|
1859
|
-
}
|
|
1860
|
-
}
|
|
1861
|
-
async function fetchStatuspageSummary(url, timeoutMs = 5e3) {
|
|
1862
|
-
try {
|
|
1863
|
-
const controller = new AbortController();
|
|
1864
|
-
const tid = setTimeout(() => controller.abort(), timeoutMs);
|
|
1865
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
1866
|
-
clearTimeout(tid);
|
|
1867
|
-
if (!res.ok) return errResult$1(`HTTP ${res.status}`);
|
|
1868
|
-
const data = await res.json();
|
|
1869
|
-
const indicator = data.status?.indicator ?? null;
|
|
1870
|
-
const overall = mapIndicator(indicator);
|
|
1871
|
-
const affected = (data.components ?? []).map((c) => ({
|
|
1872
|
-
name: c.name,
|
|
1873
|
-
status: mapComponentRaw(c.status),
|
|
1874
|
-
rawStatus: c.status
|
|
1875
|
-
})).filter((c) => c.status !== "healthy");
|
|
1876
|
-
return {
|
|
1877
|
-
status: affected.length > 0 ? "degraded" : overall,
|
|
1878
|
-
rawIndicator: indicator ?? void 0,
|
|
1879
|
-
degradedOrDownComponents: affected.length > 0 ? affected : void 0
|
|
1880
|
-
};
|
|
1881
|
-
} catch (e) {
|
|
1882
|
-
if (e instanceof Error && e.name === "AbortError") return errResult$1("Request timed out");
|
|
1883
|
-
return errResult$1(e instanceof Error ? e.message : "Unknown error");
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
const checkAnthropicHealth = () => fetchStatuspageIndicator("https://status.claude.com/api/v2/status.json");
|
|
1887
|
-
const checkGithubHealth = () => fetchStatuspageIndicator("https://www.githubstatus.com/api/v2/status.json");
|
|
1888
|
-
const checkNpmOverallHealth = () => fetchStatuspageIndicator("https://status.npmjs.org/api/v2/status.json");
|
|
1889
|
-
const checkNpmComponentHealth = () => fetchStatuspageSummary("https://status.npmjs.org/api/v2/summary.json");
|
|
1890
|
-
const checkCloudflareOverallHealth = () => fetchStatuspageIndicator("https://www.cloudflarestatus.com/api/v2/status.json");
|
|
1891
|
-
const checkCloudflareComponentHealth = () => fetchStatuspageSummary("https://www.cloudflarestatus.com/api/v2/summary.json");
|
|
1892
|
-
//#endregion
|
|
1893
|
-
//#region src/lib/health-checks/incidentio.ts
|
|
1894
|
-
function mapIncidentImpact(impact) {
|
|
1895
|
-
switch (impact) {
|
|
1896
|
-
case "full_outage": return "down";
|
|
1897
|
-
case "partial_outage":
|
|
1898
|
-
case "degraded_performance": return "degraded";
|
|
1899
|
-
default: return "degraded";
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
function mapComponentStatus(status) {
|
|
1903
|
-
switch (status) {
|
|
1904
|
-
case "operational": return "healthy";
|
|
1905
|
-
case "full_outage": return "down";
|
|
1906
|
-
case "partial_outage":
|
|
1907
|
-
case "degraded_performance": return "degraded";
|
|
1908
|
-
default: return "degraded";
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
function errResult(error) {
|
|
1912
|
-
return {
|
|
1913
|
-
status: "degraded",
|
|
1914
|
-
error
|
|
1915
|
-
};
|
|
1916
|
-
}
|
|
1917
|
-
const POSTHOG_STATUS_URL = "https://www.posthogstatus.com/api/v1/summary";
|
|
1918
|
-
async function fetchPosthogStatus(timeoutMs = 5e3) {
|
|
1919
|
-
try {
|
|
1920
|
-
const controller = new AbortController();
|
|
1921
|
-
const tid = setTimeout(() => controller.abort(), timeoutMs);
|
|
1922
|
-
const res = await fetch(POSTHOG_STATUS_URL, { signal: controller.signal });
|
|
1923
|
-
clearTimeout(tid);
|
|
1924
|
-
if (!res.ok) {
|
|
1925
|
-
const err = errResult(`HTTP ${res.status}`);
|
|
1926
|
-
return {
|
|
1927
|
-
overall: err,
|
|
1928
|
-
components: err
|
|
1929
|
-
};
|
|
1930
|
-
}
|
|
1931
|
-
const incidents = (await res.json()).ongoing_incidents ?? [];
|
|
1932
|
-
if (incidents.length === 0) return {
|
|
1933
|
-
overall: { status: "healthy" },
|
|
1934
|
-
components: { status: "healthy" }
|
|
1935
|
-
};
|
|
1936
|
-
let worstOverall = "degraded";
|
|
1937
|
-
const affected = [];
|
|
1938
|
-
for (const incident of incidents) {
|
|
1939
|
-
if (mapIncidentImpact(incident.current_worst_impact) === "down") worstOverall = "down";
|
|
1940
|
-
for (const comp of incident.affected_components ?? []) {
|
|
1941
|
-
const compStatus = mapComponentStatus(comp.current_status);
|
|
1942
|
-
if (compStatus !== "healthy") affected.push({
|
|
1943
|
-
name: comp.group_name ? `${comp.group_name} — ${comp.name}` : comp.name,
|
|
1944
|
-
status: compStatus,
|
|
1945
|
-
rawStatus: comp.current_status
|
|
1946
|
-
});
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
return {
|
|
1950
|
-
overall: { status: worstOverall },
|
|
1951
|
-
components: {
|
|
1952
|
-
status: affected.length > 0 ? "degraded" : worstOverall,
|
|
1953
|
-
degradedOrDownComponents: affected.length > 0 ? affected : void 0
|
|
1954
|
-
}
|
|
1955
|
-
};
|
|
1956
|
-
} catch (e) {
|
|
1957
|
-
if (e instanceof Error && e.name === "AbortError") {
|
|
1958
|
-
const err = errResult("Request timed out");
|
|
1959
|
-
return {
|
|
1960
|
-
overall: err,
|
|
1961
|
-
components: err
|
|
1962
|
-
};
|
|
1963
|
-
}
|
|
1964
|
-
const err = errResult(e instanceof Error ? e.message : "Unknown error");
|
|
1965
|
-
return {
|
|
1966
|
-
overall: err,
|
|
1967
|
-
components: err
|
|
1968
|
-
};
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
|
-
let _cache = null;
|
|
1972
|
-
function getPosthogHealth() {
|
|
1973
|
-
if (!_cache) _cache = fetchPosthogStatus();
|
|
1974
|
-
return _cache;
|
|
1975
|
-
}
|
|
1976
|
-
const checkPosthogOverallHealth = async () => (await getPosthogHealth()).overall;
|
|
1977
|
-
const checkPosthogComponentHealth = async () => (await getPosthogHealth()).components;
|
|
1978
|
-
//#endregion
|
|
1979
|
-
//#region src/lib/health-checks/endpoints.ts
|
|
1980
|
-
function downResult(error) {
|
|
1981
|
-
return {
|
|
1982
|
-
status: "down",
|
|
1983
|
-
error
|
|
1984
|
-
};
|
|
1985
|
-
}
|
|
1986
|
-
async function fetchEndpointHealth(url, timeoutMs = 5e3, expectedStatus = 200) {
|
|
1987
|
-
try {
|
|
1988
|
-
const controller = new AbortController();
|
|
1989
|
-
const tid = setTimeout(() => controller.abort(), timeoutMs);
|
|
1990
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
1991
|
-
clearTimeout(tid);
|
|
1992
|
-
if (res.status === expectedStatus) return {
|
|
1993
|
-
status: "healthy",
|
|
1994
|
-
rawIndicator: `HTTP ${res.status}`
|
|
1995
|
-
};
|
|
1996
|
-
return downResult(`HTTP ${res.status}`);
|
|
1997
|
-
} catch (e) {
|
|
1998
|
-
if (e instanceof Error && e.name === "AbortError") return downResult("Request timed out");
|
|
1999
|
-
return downResult(e instanceof Error ? e.message : "Unknown error");
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
const checkLlmGatewayHealth = () => fetchEndpointHealth("https://gateway.us.posthog.com/_liveness");
|
|
2003
|
-
const checkMcpHealth = () => fetchEndpointHealth("https://mcp.posthog.com/");
|
|
2004
|
-
const checkGithubReleasesHealth = () => fetchEndpointHealth(`${REMOTE_SKILLS_BASE_URL}/skill-menu.json`);
|
|
2005
|
-
//#endregion
|
|
2006
|
-
//#region src/lib/health-checks/readiness.ts
|
|
2007
|
-
const SERVICE_LABELS = {
|
|
2008
|
-
anthropic: "Anthropic",
|
|
2009
|
-
posthogOverall: "PostHog",
|
|
2010
|
-
posthogComponents: "PostHog (components)",
|
|
2011
|
-
github: "GitHub",
|
|
2012
|
-
npmOverall: "npm",
|
|
2013
|
-
npmComponents: "npm (components)",
|
|
2014
|
-
cloudflareOverall: "Cloudflare",
|
|
2015
|
-
cloudflareComponents: "Cloudflare (components)",
|
|
2016
|
-
llmGateway: "LLM Gateway",
|
|
2017
|
-
mcp: "MCP",
|
|
2018
|
-
githubReleases: "GitHub Releases"
|
|
2019
|
-
};
|
|
2020
|
-
/**
|
|
2021
|
-
* See README section "Health checks" for the full rationale.
|
|
2022
|
-
* Adjust these arrays to change what blocks a wizard run.
|
|
2023
|
-
*/
|
|
2024
|
-
const DEFAULT_WIZARD_READINESS_CONFIG = {
|
|
2025
|
-
downBlocksRun: [
|
|
2026
|
-
"anthropic",
|
|
2027
|
-
"npmOverall",
|
|
2028
|
-
"llmGateway",
|
|
2029
|
-
"mcp",
|
|
2030
|
-
"githubReleases"
|
|
2031
|
-
],
|
|
2032
|
-
degradedBlocksRun: ["anthropic"]
|
|
2033
|
-
};
|
|
2034
|
-
async function checkAllExternalServices() {
|
|
2035
|
-
const [anthropic, posthogOverall, posthogComponents, github, npmOverall, npmComponents, cloudflareOverall, cloudflareComponents, llmGateway, mcp, githubReleases] = await Promise.all([
|
|
2036
|
-
checkAnthropicHealth(),
|
|
2037
|
-
checkPosthogOverallHealth(),
|
|
2038
|
-
checkPosthogComponentHealth(),
|
|
2039
|
-
checkGithubHealth(),
|
|
2040
|
-
checkNpmOverallHealth(),
|
|
2041
|
-
checkNpmComponentHealth(),
|
|
2042
|
-
checkCloudflareOverallHealth(),
|
|
2043
|
-
checkCloudflareComponentHealth(),
|
|
2044
|
-
checkLlmGatewayHealth(),
|
|
2045
|
-
checkMcpHealth(),
|
|
2046
|
-
checkGithubReleasesHealth()
|
|
2047
|
-
]);
|
|
2048
|
-
return {
|
|
2049
|
-
anthropic,
|
|
2050
|
-
posthogOverall,
|
|
2051
|
-
posthogComponents,
|
|
2052
|
-
github,
|
|
2053
|
-
npmOverall,
|
|
2054
|
-
npmComponents,
|
|
2055
|
-
cloudflareOverall,
|
|
2056
|
-
cloudflareComponents,
|
|
2057
|
-
llmGateway,
|
|
2058
|
-
mcp,
|
|
2059
|
-
githubReleases
|
|
2060
|
-
};
|
|
2061
|
-
}
|
|
2062
|
-
function describeResult(label, h) {
|
|
2063
|
-
const parts = [`${label}: ${h.status}`];
|
|
2064
|
-
if (h.rawIndicator) parts.push(`indicator=${h.rawIndicator}`);
|
|
2065
|
-
if (h.error) parts.push(h.error);
|
|
2066
|
-
return parts.join(" — ");
|
|
2067
|
-
}
|
|
2068
|
-
const MAX_COMPONENT_NAMES = 8;
|
|
2069
|
-
function describeComponents(label, h) {
|
|
2070
|
-
const affected = h.degradedOrDownComponents;
|
|
2071
|
-
if (!affected || affected.length === 0) return `${label} components: all operational`;
|
|
2072
|
-
const shown = affected.slice(0, MAX_COMPONENT_NAMES).map((c) => `${c.name} (${c.status})`);
|
|
2073
|
-
const suffix = affected.length > MAX_COMPONENT_NAMES ? `, +${affected.length - MAX_COMPONENT_NAMES} more` : "";
|
|
2074
|
-
return `${label} components impacted: ${shown.join(", ")}${suffix}`;
|
|
2075
|
-
}
|
|
2076
|
-
const READINESS_TIMEOUT_MS = 1e4;
|
|
2077
|
-
async function evaluateWizardReadiness(config = DEFAULT_WIZARD_READINESS_CONFIG) {
|
|
2078
|
-
try {
|
|
2079
|
-
const health = await Promise.race([checkAllExternalServices(), new Promise((resolve) => setTimeout(() => resolve(allUnknown("Health check timed out")), READINESS_TIMEOUT_MS))]);
|
|
2080
|
-
const reasons = [];
|
|
2081
|
-
for (const key of Object.keys(health)) {
|
|
2082
|
-
const result = health[key];
|
|
2083
|
-
const label = SERVICE_LABELS[key];
|
|
2084
|
-
reasons.push(describeResult(label, result));
|
|
2085
|
-
if ("degradedOrDownComponents" in result) reasons.push(describeComponents(label, result));
|
|
2086
|
-
}
|
|
2087
|
-
const blockingKeys = getBlockingServiceKeys(health, config);
|
|
2088
|
-
if (blockingKeys.length > 0) {
|
|
2089
|
-
logToFile(`[health-checks] blocked by: ${blockingKeys.join(", ")}`);
|
|
2090
|
-
return {
|
|
2091
|
-
decision: "no",
|
|
2092
|
-
health,
|
|
2093
|
-
reasons
|
|
2094
|
-
};
|
|
2095
|
-
}
|
|
2096
|
-
if (Object.values(health).some((h) => h.status !== "healthy")) return {
|
|
2097
|
-
decision: "yes_with_warnings",
|
|
2098
|
-
health,
|
|
2099
|
-
reasons
|
|
2100
|
-
};
|
|
2101
|
-
return {
|
|
2102
|
-
decision: "yes",
|
|
2103
|
-
health,
|
|
2104
|
-
reasons
|
|
2105
|
-
};
|
|
2106
|
-
} catch (err) {
|
|
2107
|
-
logToFile(`[health-checks] error: ${err instanceof Error ? err.message : err}`);
|
|
2108
|
-
return {
|
|
2109
|
-
decision: "yes",
|
|
2110
|
-
health: allUnknown("Unexpected error"),
|
|
2111
|
-
reasons: ["Health check failed unexpectedly — proceeding anyway"]
|
|
2112
|
-
};
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
/** Keys that are component-level detail, not top-level services. */
|
|
2116
|
-
const COMPONENT_KEYS = [
|
|
2117
|
-
"posthogComponents",
|
|
2118
|
-
"npmComponents",
|
|
2119
|
-
"cloudflareComponents"
|
|
2120
|
-
];
|
|
2121
|
-
/**
|
|
2122
|
-
* Get the keys of services that would block a wizard run per the given config.
|
|
2123
|
-
*/
|
|
2124
|
-
function getBlockingServiceKeys(health, config = DEFAULT_WIZARD_READINESS_CONFIG) {
|
|
2125
|
-
return Object.keys(health).filter((key) => {
|
|
2126
|
-
if (COMPONENT_KEYS.includes(key)) return false;
|
|
2127
|
-
const result = health[key];
|
|
2128
|
-
if (config.downBlocksRun.includes(key) && result.status === "down") return true;
|
|
2129
|
-
if ((config.degradedBlocksRun ?? []).includes(key) && result.status !== "healthy") return true;
|
|
2130
|
-
return false;
|
|
2131
|
-
});
|
|
2132
|
-
}
|
|
2133
|
-
/** Build an AllServicesHealth where every service is Degraded with the given error. */
|
|
2134
|
-
function allUnknown(error) {
|
|
2135
|
-
const base = {
|
|
2136
|
-
status: "degraded",
|
|
2137
|
-
error
|
|
2138
|
-
};
|
|
2139
|
-
return {
|
|
2140
|
-
anthropic: base,
|
|
2141
|
-
posthogOverall: base,
|
|
2142
|
-
posthogComponents: { ...base },
|
|
2143
|
-
github: base,
|
|
2144
|
-
npmOverall: base,
|
|
2145
|
-
npmComponents: { ...base },
|
|
2146
|
-
cloudflareOverall: base,
|
|
2147
|
-
cloudflareComponents: { ...base },
|
|
2148
|
-
llmGateway: base,
|
|
2149
|
-
mcp: base,
|
|
2150
|
-
githubReleases: base
|
|
2151
|
-
};
|
|
2152
|
-
}
|
|
2153
|
-
//#endregion
|
|
2154
|
-
export { backupAndFixClaudeSettings as a, initializeAgent as c, formatScanReport as d, writeScanReport as f, installSkillById as h, AgentSignals as i, restoreClaudeSettings as l, fetchSkillMenu as m, evaluateWizardReadiness as n, buildWizardMetadata as o, downloadSkill as p, getBlockingServiceKeys as r, checkAllSettingsConflicts as s, SERVICE_LABELS as t, runAgent as u };
|
|
2020
|
+
export { coerceAuditChecks as _, initializeAgent as a, formatScanReport as c, fetchSkillMenu as d, installSkillById as f, AUDIT_SEVERITY_STYLE as g, AUDIT_REPORT_FILE as h, checkAllSettingsConflicts as i, writeScanReport as l, AUDIT_CHECKS_KEY as m, backupAndFixClaudeSettings as n, restoreClaudeSettings as o, AUDIT_CHECKS_FILE as p, buildWizardMetadata as r, runAgent as s, AgentSignals as t, downloadSkill as u, getAuditChecks as v };
|
|
2155
2021
|
|
|
2156
|
-
//# sourceMappingURL=
|
|
2022
|
+
//# sourceMappingURL=agent-interface-CV0-vtxj.js.map
|