@purix/cli 0.1.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/LICENSE +202 -0
- package/dist/cli/commands/auth.d.ts +3 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +130 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/backup.d.ts +3 -0
- package/dist/cli/commands/backup.d.ts.map +1 -0
- package/dist/cli/commands/backup.js +54 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/config.d.ts +3 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +35 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/connect.d.ts +16 -0
- package/dist/cli/commands/connect.d.ts.map +1 -0
- package/dist/cli/commands/connect.js +45 -0
- package/dist/cli/commands/connect.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +3 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +58 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/index.d.ts +3 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +35 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/lang.d.ts +3 -0
- package/dist/cli/commands/lang.d.ts.map +1 -0
- package/dist/cli/commands/lang.js +187 -0
- package/dist/cli/commands/lang.js.map +1 -0
- package/dist/cli/commands/lifecycle.d.ts +3 -0
- package/dist/cli/commands/lifecycle.d.ts.map +1 -0
- package/dist/cli/commands/lifecycle.js +782 -0
- package/dist/cli/commands/lifecycle.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +3 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +75 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/mcp_serve.d.ts +3 -0
- package/dist/cli/commands/mcp_serve.d.ts.map +1 -0
- package/dist/cli/commands/mcp_serve.js +16 -0
- package/dist/cli/commands/mcp_serve.js.map +1 -0
- package/dist/cli/commands/memory.d.ts +3 -0
- package/dist/cli/commands/memory.d.ts.map +1 -0
- package/dist/cli/commands/memory.js +12 -0
- package/dist/cli/commands/memory.js.map +1 -0
- package/dist/cli/commands/migrations.d.ts +3 -0
- package/dist/cli/commands/migrations.d.ts.map +1 -0
- package/dist/cli/commands/migrations.js +98 -0
- package/dist/cli/commands/migrations.js.map +1 -0
- package/dist/cli/commands/observability.d.ts +3 -0
- package/dist/cli/commands/observability.d.ts.map +1 -0
- package/dist/cli/commands/observability.js +141 -0
- package/dist/cli/commands/observability.js.map +1 -0
- package/dist/cli/commands/provider.d.ts +3 -0
- package/dist/cli/commands/provider.d.ts.map +1 -0
- package/dist/cli/commands/provider.js +82 -0
- package/dist/cli/commands/provider.js.map +1 -0
- package/dist/cli/commands/security.d.ts +3 -0
- package/dist/cli/commands/security.d.ts.map +1 -0
- package/dist/cli/commands/security.js +27 -0
- package/dist/cli/commands/security.js.map +1 -0
- package/dist/cli/commands/tier.d.ts +3 -0
- package/dist/cli/commands/tier.d.ts.map +1 -0
- package/dist/cli/commands/tier.js +27 -0
- package/dist/cli/commands/tier.js.map +1 -0
- package/dist/cli/commands/tools.d.ts +3 -0
- package/dist/cli/commands/tools.d.ts.map +1 -0
- package/dist/cli/commands/tools.js +18 -0
- package/dist/cli/commands/tools.js.map +1 -0
- package/dist/cli/output.d.ts +5 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +22 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/savings_output.d.ts +9 -0
- package/dist/cli/savings_output.d.ts.map +1 -0
- package/dist/cli/savings_output.js +62 -0
- package/dist/cli/savings_output.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +155 -0
- package/dist/cli.js.map +1 -0
- package/dist/telemetry/log.d.ts +4 -0
- package/dist/telemetry/log.d.ts.map +1 -0
- package/dist/telemetry/log.js +92 -0
- package/dist/telemetry/log.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { setSecret, deleteSecret, listSecretStatus } from "@purix/core/security/secrets_manager";
|
|
2
|
+
export function registerSecurityCommands(program) {
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Secrets Manager
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
program.command("secret-set <n> <value>").description("Store a credential, encrypted at rest").action((name, value) => {
|
|
7
|
+
setSecret(name, value);
|
|
8
|
+
console.log(`✅ Stored "${name}" encrypted. Remove it from any .env once you've confirmed this works.`);
|
|
9
|
+
});
|
|
10
|
+
program.command("secret-rotate <n> <newValue>").description("Rotate a stored credential").action((name, newValue) => {
|
|
11
|
+
setSecret(name, newValue);
|
|
12
|
+
console.log(`✅ Rotated "${name}".`);
|
|
13
|
+
});
|
|
14
|
+
program.command("secret-remove <n>").description("Delete a stored credential").action((name) => {
|
|
15
|
+
console.log(deleteSecret(name) ? `✅ Removed "${name}".` : `No secret stored as "${name}".`);
|
|
16
|
+
});
|
|
17
|
+
program.command("secrets-status").description("Show credential age / rotation status").action(() => {
|
|
18
|
+
const statuses = listSecretStatus();
|
|
19
|
+
if (statuses.length === 0) {
|
|
20
|
+
console.log("No secrets stored yet.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
for (const s of statuses)
|
|
24
|
+
console.log(`${s.name} set ${s.ageDays}d ago${s.rotationDue ? " ⚠ ROTATION DUE" : ""} (rotated ${s.rotationCount}x before)`);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/cli/commands/security.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAEjG,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,8EAA8E;IAC9E,kBAAkB;IAClB,8EAA8E;IAC9E,OAAO,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,WAAW,CAAC,uCAAuC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE;QACpI,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,wEAAwE,CAAC,CAAC;IACzG,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC,WAAW,CAAC,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC,IAAY,EAAE,QAAgB,EAAE,EAAE;QAClI,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE;QACrG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,wBAAwB,IAAI,IAAI,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,WAAW,CAAC,uCAAuC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE;QACjG,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;QACpC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC;QAC7E,KAAK,MAAM,CAAC,IAAI,QAAQ;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,aAAa,WAAW,CAAC,CAAC;IAC5J,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/tier.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAuBpD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getEntitlements, refreshEntitlements } from "@purix/core/licensing/tier";
|
|
2
|
+
export function registerTierCommands(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("tier-status")
|
|
5
|
+
.description("Show current tier and what it unlocks (refreshes from the server if logged in)")
|
|
6
|
+
.action(async () => {
|
|
7
|
+
try {
|
|
8
|
+
await refreshEntitlements();
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
// A real rejection (e.g. an expired/invalid session) still falls
|
|
12
|
+
// through to printing whatever's cached below, rather than
|
|
13
|
+
// aborting the whole command — tier-status should always show
|
|
14
|
+
// *something*, even a stale answer with a warning.
|
|
15
|
+
console.warn(` (couldn't refresh from the server: ${err instanceof Error ? err.message : err})`);
|
|
16
|
+
}
|
|
17
|
+
const ent = getEntitlements();
|
|
18
|
+
console.log(`Tier: ${ent.tier}`);
|
|
19
|
+
console.log(` Tracked components: ${ent.componentLimit === null ? "unlimited" : ent.componentLimit}`);
|
|
20
|
+
console.log(` Audit export: ${ent.auditExport ? "✅" : "❌"}`);
|
|
21
|
+
console.log(` Webhooks: ${ent.webhooks ? "✅" : "❌"}`);
|
|
22
|
+
if (ent.tier === "free") {
|
|
23
|
+
console.log(`\nRun "purix login" to upgrade.`);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=tier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier.js","sourceRoot":"","sources":["../../../src/cli/commands/tier.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAElF,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,gFAAgF,CAAC;SAC7F,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,mBAAmB,EAAE,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,iEAAiE;YACjE,2DAA2D;YAC3D,8DAA8D;YAC9D,mDAAmD;YACnD,OAAO,CAAC,IAAI,CAAC,wCAAwC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACpG,CAAC;QACD,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,6BAA6B,GAAG,CAAC,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;QAC3G,OAAO,CAAC,GAAG,CAAC,6BAA6B,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,6BAA6B,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACrE,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QAcrD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { suggestTools, formatSuggestions } from "@purix/core/tools/matchmaker";
|
|
2
|
+
export function registerToolsCommands(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("tools <purpose>")
|
|
5
|
+
.description("Tool Matchmaker: suggest vetted npm packages for a purpose (advisory only, never installs)")
|
|
6
|
+
.action(async (purpose) => {
|
|
7
|
+
try {
|
|
8
|
+
console.log(`Searching npm for vetted packages: "${purpose}"...`);
|
|
9
|
+
const suggestions = await suggestTools(purpose);
|
|
10
|
+
console.log(formatSuggestions(suggestions));
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
console.error(`\n🛑 Tool search failed: ${err instanceof Error ? err.message : err}`);
|
|
14
|
+
process.exitCode = 1;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/cli/commands/tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAE/E,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,4FAA4F,CAAC;SACzG,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,EAAE;QAChC,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,uCAAuC,OAAO,MAAM,CAAC,CAAC;YAClE,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACtF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Called once from cli.ts's preAction hook after Commander parses argv. */
|
|
2
|
+
export declare function setQuiet(value: boolean): void;
|
|
3
|
+
/** Checked by lifecycle.ts and any other command's narrative console.log calls. */
|
|
4
|
+
export declare function isQuiet(): boolean;
|
|
5
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAcA,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE7C;AAED,mFAAmF;AACnF,wBAAgB,OAAO,IAAI,OAAO,CAEjC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// packages/cli/src/cli/output.ts
|
|
2
|
+
//
|
|
3
|
+
// Part 2: "read it once in that same [preAction] hook and store it
|
|
4
|
+
// somewhere the output layer can check (a module-level flag or a small
|
|
5
|
+
// context object), rather than checking program.opts() inside every
|
|
6
|
+
// individual command." This is that module-level flag.
|
|
7
|
+
//
|
|
8
|
+
// Two ways quiet can be turned on, checked in this priority order:
|
|
9
|
+
// 1. PURIX_QUIET=1 in the environment — checked first so it's never
|
|
10
|
+
// silently overridden by option-parsing order.
|
|
11
|
+
// 2. -q/--quiet on the command line, read once in cli.ts's preAction
|
|
12
|
+
// hook via setQuiet().
|
|
13
|
+
let quiet = process.env.PURIX_QUIET === "1";
|
|
14
|
+
/** Called once from cli.ts's preAction hook after Commander parses argv. */
|
|
15
|
+
export function setQuiet(value) {
|
|
16
|
+
quiet = quiet || value;
|
|
17
|
+
}
|
|
18
|
+
/** Checked by lifecycle.ts and any other command's narrative console.log calls. */
|
|
19
|
+
export function isQuiet() {
|
|
20
|
+
return quiet;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,mEAAmE;AACnE,uEAAuE;AACvE,oEAAoE;AACpE,uDAAuD;AACvD,EAAE;AACF,mEAAmE;AACnE,sEAAsE;AACtE,oDAAoD;AACpD,uEAAuE;AACvE,4BAA4B;AAC5B,IAAI,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,GAAG,CAAC;AAE5C,4EAA4E;AAC5E,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,KAAK,GAAG,KAAK,IAAI,KAAK,CAAC;AACzB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,OAAO;IACrB,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Captures the all-time savings total/count BEFORE a command's action
|
|
3
|
+
* runs. Diffing against the total AFTER is how "Savings This Run" is
|
|
4
|
+
* derived — there's no other reliable way to scope a query to "just this
|
|
5
|
+
* process's LLM calls" against a shared, cumulative SQLite ledger.
|
|
6
|
+
*/
|
|
7
|
+
export declare function snapshotSavings(): import("@purix/core/llm/budget").SavingsSummary;
|
|
8
|
+
export declare function printRunSummary(before: ReturnType<typeof snapshotSavings>): void;
|
|
9
|
+
//# sourceMappingURL=savings_output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"savings_output.d.ts","sourceRoot":"","sources":["../../src/cli/savings_output.ts"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH,wBAAgB,eAAe,oDAE9B;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG,IAAI,CAyBhF"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// packages/cli/src/cli/savings_output.ts
|
|
2
|
+
//
|
|
3
|
+
// Part 2: the "Savings This Run" / "Total Savings This Week" terminal
|
|
4
|
+
// output and the milestone-upsell line. Shared by lifecycle.ts's command
|
|
5
|
+
// actions (create, modify) rather than duplicated in each — both are
|
|
6
|
+
// where an intent_refinement/escalation call could actually happen.
|
|
7
|
+
import { getSavingsSummary } from "@purix/core/llm/budget";
|
|
8
|
+
import { config } from "@purix/core/state/config";
|
|
9
|
+
import { isLoggedIn } from "@purix/core/security/session";
|
|
10
|
+
import { isQuiet } from "./output.js";
|
|
11
|
+
const DEFAULT_MILESTONE_THRESHOLD_USD = 50;
|
|
12
|
+
const MILESTONE_LAST_SHOWN_KEY = "milestone-upsell-last-shown";
|
|
13
|
+
const MILESTONE_THRESHOLD_KEY = "milestone-threshold";
|
|
14
|
+
/**
|
|
15
|
+
* Captures the all-time savings total/count BEFORE a command's action
|
|
16
|
+
* runs. Diffing against the total AFTER is how "Savings This Run" is
|
|
17
|
+
* derived — there's no other reliable way to scope a query to "just this
|
|
18
|
+
* process's LLM calls" against a shared, cumulative SQLite ledger.
|
|
19
|
+
*/
|
|
20
|
+
export function snapshotSavings() {
|
|
21
|
+
return getSavingsSummary(3650); // ~10 years ≈ "all time" for this project
|
|
22
|
+
}
|
|
23
|
+
export function printRunSummary(before) {
|
|
24
|
+
if (isQuiet() || config.get("show-savings") === "off")
|
|
25
|
+
return;
|
|
26
|
+
const after = getSavingsSummary(3650);
|
|
27
|
+
const thisRunDelta = after.totalSavingsUsd - before.totalSavingsUsd;
|
|
28
|
+
const thisRunCalls = after.callCount - before.callCount;
|
|
29
|
+
const weekly = getSavingsSummary(7);
|
|
30
|
+
// Per Part 2: on a run with no high-tier-eligible calls, omit the
|
|
31
|
+
// "Savings This Run" line entirely rather than printing a misleading
|
|
32
|
+
// $0.00 — a run that never touched intent_refinement/escalation, or
|
|
33
|
+
// touched them without a genuine downgrade, has nothing to report.
|
|
34
|
+
const lines = [];
|
|
35
|
+
if (thisRunCalls > 0) {
|
|
36
|
+
lines.push(`Savings This Run: $${thisRunDelta.toFixed(2)}`);
|
|
37
|
+
}
|
|
38
|
+
if (weekly.callCount > 0) {
|
|
39
|
+
lines.push(`Total Savings This Week: $${weekly.totalSavingsUsd.toFixed(2)}`);
|
|
40
|
+
}
|
|
41
|
+
if (lines.length === 0)
|
|
42
|
+
return; // nothing genuine to show at all
|
|
43
|
+
console.log(`\nPurix Verification Complete ✅`);
|
|
44
|
+
for (const line of lines)
|
|
45
|
+
console.log(line);
|
|
46
|
+
maybePrintMilestoneUpsell();
|
|
47
|
+
}
|
|
48
|
+
function maybePrintMilestoneUpsell() {
|
|
49
|
+
if (isLoggedIn())
|
|
50
|
+
return; // suppressed permanently once logged in
|
|
51
|
+
const threshold = Number(config.get(MILESTONE_THRESHOLD_KEY) ?? DEFAULT_MILESTONE_THRESHOLD_USD);
|
|
52
|
+
const allTime = getSavingsSummary(3650);
|
|
53
|
+
if (allTime.totalSavingsUsd < threshold)
|
|
54
|
+
return;
|
|
55
|
+
const lastShown = config.get(MILESTONE_LAST_SHOWN_KEY);
|
|
56
|
+
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
|
|
57
|
+
if (lastShown === today)
|
|
58
|
+
return; // rate-limited to once per day
|
|
59
|
+
console.log(`\nWant this data saved? Run \`purix login\` to unlock your web dashboard and get daily compute receipts sent to your inbox.`);
|
|
60
|
+
config.set(MILESTONE_LAST_SHOWN_KEY, today);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=savings_output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"savings_output.js","sourceRoot":"","sources":["../../src/cli/savings_output.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,sEAAsE;AACtE,yEAAyE;AACzE,qEAAqE;AACrE,oEAAoE;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,+BAA+B,GAAG,EAAE,CAAC;AAC3C,MAAM,wBAAwB,GAAG,6BAA6B,CAAC;AAC/D,MAAM,uBAAuB,GAAG,qBAAqB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,0CAA0C;AAC5E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAA0C;IACxE,IAAI,OAAO,EAAE,IAAI,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,KAAK;QAAE,OAAO;IAE9D,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;IACpE,MAAM,YAAY,GAAG,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IACxD,MAAM,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAEpC,kEAAkE;IAClE,qEAAqE;IACrE,oEAAoE;IACpE,mEAAmE;IACnE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,sBAAsB,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,6BAA6B,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,iCAAiC;IAEjE,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE5C,yBAAyB,EAAE,CAAC;AAC9B,CAAC;AAED,SAAS,yBAAyB;IAChC,IAAI,UAAU,EAAE;QAAE,OAAO,CAAC,wCAAwC;IAElE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,+BAA+B,CAAC,CAAC;IACjG,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS;QAAE,OAAO;IAEhD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa;IAClE,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,CAAC,+BAA+B;IAEhE,OAAO,CAAC,GAAG,CACT,6HAA6H,CAC9H,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
/**
|
|
5
|
+
* Builds the configured Commander program without running it. Split out
|
|
6
|
+
* from the parseAsync call below so this file is importable in a test —
|
|
7
|
+
* `program.parseAsync(process.argv)` as a bare top-level side effect
|
|
8
|
+
* would otherwise run (and try to interpret the test runner's own argv)
|
|
9
|
+
* the moment anything imported this module.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildProgram(): Command;
|
|
12
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAQA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyDpC;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAoDtC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// packages/cli/src/cli.ts>COM
|
|
3
|
+
// Must be the first import: loads .env from process.cwd() (the user's
|
|
4
|
+
// project, where they'd put a real .env per .env.example) into
|
|
5
|
+
// process.env before anything else runs. Several modules read
|
|
6
|
+
// process.env.* at module-evaluation time (e.g. cli/output.ts's `quiet`
|
|
7
|
+
// flag), so anything imported after this line sees the loaded values —
|
|
8
|
+
// anything imported before it would not.
|
|
9
|
+
import "dotenv/config";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import { reconcilePendingOperations } from "@purix/core/state/reconcile";
|
|
12
|
+
import { acquireRepoLock, releaseRepoLock } from "@purix/core/state/repo_lock";
|
|
13
|
+
import { startScheduler } from "@purix/core/state/scheduler";
|
|
14
|
+
import { setQuiet } from "./cli/output.js";
|
|
15
|
+
import { registerLifecycleCommands } from "./cli/commands/lifecycle.js";
|
|
16
|
+
import { registerMigrationsCommands } from "./cli/commands/migrations.js";
|
|
17
|
+
import { registerObservabilityCommands } from "./cli/commands/observability.js";
|
|
18
|
+
import { registerSecurityCommands } from "./cli/commands/security.js";
|
|
19
|
+
import { registerProviderCommands } from "./cli/commands/provider.js";
|
|
20
|
+
import { registerTierCommands } from "./cli/commands/tier.js";
|
|
21
|
+
// registerMcpCommands intentionally not wired in for v1.0 launch — see
|
|
22
|
+
// ADR-057 ("MCP Client Commands Governance Deferral"). The gateway/registry
|
|
23
|
+
// code is intact and untouched; re-enable this import + the
|
|
24
|
+
// registerMcpCommands(program) call below once the governance hardening
|
|
25
|
+
// (identity-backed provenance, DLP scrub, escalation budget caps) ships.
|
|
26
|
+
// import { registerMcpCommands } from "./cli/commands/mcp";
|
|
27
|
+
import { registerBackupCommands } from "./cli/commands/backup.js";
|
|
28
|
+
import { registerMemoryCommands } from "./cli/commands/memory.js";
|
|
29
|
+
import { registerToolsCommands } from "./cli/commands/tools.js";
|
|
30
|
+
import { registerConfigCommands } from "./cli/commands/config.js";
|
|
31
|
+
import { registerAuthCommands } from "./cli/commands/auth.js";
|
|
32
|
+
import { registerLangCommands } from "./cli/commands/lang.js";
|
|
33
|
+
// registerDevCommands intentionally not wired in for beta — `purix dev
|
|
34
|
+
// scaffold-language` is an internal codegen tool for Purix's own
|
|
35
|
+
// contributors (generates provider/pack/fixture stubs for a new
|
|
36
|
+
// language), not an end-user-facing command. It was previously
|
|
37
|
+
// unconditionally registered with no gate, unlike every other
|
|
38
|
+
// internal-only surface in this file (see registerMcpCommands above and
|
|
39
|
+
// PURIX_INTERNAL_LANGUAGES in language/registry.ts for the established
|
|
40
|
+
// pattern). Re-enable via an explicit internal/dev-mode flag if
|
|
41
|
+
// contributor tooling needs it exposed through the published binary;
|
|
42
|
+
// until then this is dead weight in the public `purix --help` output.
|
|
43
|
+
// import { registerDevCommands } from "./cli/commands/dev";
|
|
44
|
+
import { registerIndexCommands } from "./cli/commands/index.js";
|
|
45
|
+
import { registerMcpServeCommand } from "./cli/commands/mcp_serve.js";
|
|
46
|
+
import { fileURLToPath } from "node:url";
|
|
47
|
+
import { realpathSync } from "node:fs";
|
|
48
|
+
import { logError } from "./telemetry/log.js";
|
|
49
|
+
import { registerConnectCommand } from "./cli/commands/connect.js";
|
|
50
|
+
process.on("uncaughtException", (err) => {
|
|
51
|
+
logError(err, "uncaughtException");
|
|
52
|
+
console.error("\n🛑 Something went wrong. Details saved to ~/.purix/logs/. Run `purix diagnostics` to review or share them.");
|
|
53
|
+
releaseRepoLock();
|
|
54
|
+
process.exit(1);
|
|
55
|
+
});
|
|
56
|
+
process.on("unhandledRejection", (err) => {
|
|
57
|
+
logError(err, "unhandledRejection");
|
|
58
|
+
console.error("\n🛑 Something went wrong. Details saved to ~/.purix/logs/. Run `purix diagnostics` to review or share them.");
|
|
59
|
+
releaseRepoLock();
|
|
60
|
+
process.exit(1);
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Builds the configured Commander program without running it. Split out
|
|
64
|
+
* from the parseAsync call below so this file is importable in a test —
|
|
65
|
+
* `program.parseAsync(process.argv)` as a bare top-level side effect
|
|
66
|
+
* would otherwise run (and try to interpret the test runner's own argv)
|
|
67
|
+
* the moment anything imported this module.
|
|
68
|
+
*/
|
|
69
|
+
export function buildProgram() {
|
|
70
|
+
const program = new Command();
|
|
71
|
+
program
|
|
72
|
+
.name("purix")
|
|
73
|
+
.description("Verification & Governance Layer for AI-Generated Code (Purix v0.1.0)")
|
|
74
|
+
.version("0.1.0");
|
|
75
|
+
// Part 2: global quiet flag, alongside the existing preAction hook.
|
|
76
|
+
program.option("-q, --quiet", "suppress non-essential output");
|
|
77
|
+
// §7.3: startup reconciliation runs before ANY command touches the
|
|
78
|
+
// manifest — a crash-interrupted operation from a prior run must be
|
|
79
|
+
// resolved before new work starts, or state drifts further. Reading
|
|
80
|
+
// --quiet here too, once, rather than in every individual command.
|
|
81
|
+
program.hook("preAction", async (thisCommand) => {
|
|
82
|
+
setQuiet(Boolean(thisCommand.opts().quiet));
|
|
83
|
+
await reconcilePendingOperations();
|
|
84
|
+
acquireRepoLock();
|
|
85
|
+
startScheduler();
|
|
86
|
+
});
|
|
87
|
+
program.hook("postAction", async () => {
|
|
88
|
+
releaseRepoLock();
|
|
89
|
+
});
|
|
90
|
+
process.on("SIGINT", () => {
|
|
91
|
+
releaseRepoLock();
|
|
92
|
+
process.exit(130);
|
|
93
|
+
});
|
|
94
|
+
process.on("SIGTERM", () => {
|
|
95
|
+
releaseRepoLock();
|
|
96
|
+
process.exit(143);
|
|
97
|
+
});
|
|
98
|
+
registerLifecycleCommands(program);
|
|
99
|
+
registerMigrationsCommands(program);
|
|
100
|
+
registerObservabilityCommands(program);
|
|
101
|
+
registerSecurityCommands(program);
|
|
102
|
+
registerProviderCommands(program);
|
|
103
|
+
registerTierCommands(program);
|
|
104
|
+
// registerMcpCommands(program); — gated off for v1.0, see note above import
|
|
105
|
+
registerBackupCommands(program);
|
|
106
|
+
registerMemoryCommands(program);
|
|
107
|
+
registerToolsCommands(program);
|
|
108
|
+
registerConfigCommands(program);
|
|
109
|
+
registerAuthCommands(program);
|
|
110
|
+
registerLangCommands(program);
|
|
111
|
+
// registerDevCommands(program); — gated off for beta, see note above import
|
|
112
|
+
registerIndexCommands(program);
|
|
113
|
+
registerMcpServeCommand(program);
|
|
114
|
+
registerConnectCommand(program);
|
|
115
|
+
return program;
|
|
116
|
+
}
|
|
117
|
+
// Real bug, found while verifying the published-package install path (not
|
|
118
|
+
// hypothetical): `process.argv[1] === fileURLToPath(import.meta.url)` looks
|
|
119
|
+
// like the standard "am I the directly-executed entry point, or just
|
|
120
|
+
// imported for buildProgram() in tests" guard, but Node does NOT resolve
|
|
121
|
+
// symlinks for argv[1] while it DOES resolve them for import.meta.url when
|
|
122
|
+
// loading the module. Every real invocation of this CLI goes through a
|
|
123
|
+
// symlink: `npm install -g purix` creates one in the global bin dir, `npx
|
|
124
|
+
// purix` creates one in its cache, and even local `node_modules/.bin/purix`
|
|
125
|
+
// is one. Under all three, argv[1] stays the symlink path while
|
|
126
|
+
// import.meta.url resolves to the real target — they never match, the
|
|
127
|
+
// guard silently evaluates false, and the CLI exits 0 with zero output no
|
|
128
|
+
// matter what was asked for. realpath-ing argv[1] first makes the
|
|
129
|
+
// comparison symlink-agnostic on both sides; buildProgram() itself is
|
|
130
|
+
// untouched, so cli.test.ts / cli.integration.test.ts importing it directly
|
|
131
|
+
// still don't trigger this block (there's no argv[1] file to realpath to
|
|
132
|
+
// a match in that context).
|
|
133
|
+
function isDirectlyExecuted() {
|
|
134
|
+
if (!process.argv[1])
|
|
135
|
+
return false;
|
|
136
|
+
try {
|
|
137
|
+
return realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (isDirectlyExecuted()) {
|
|
144
|
+
const program = buildProgram();
|
|
145
|
+
if (process.argv.length <= 2) {
|
|
146
|
+
program.help();
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
150
|
+
console.error(`\n🛑 Unhandled error: ${err instanceof Error ? err.message : err}`);
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/telemetry/log.ts"],"names":[],"mappings":"AAoBA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAgB7D;AA0BD,wBAAgB,aAAa,CAAC,QAAQ,SAAK,GAAG,MAAM,EAAE,CAqBrD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrD"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// packages/cli/src/telemetry/log.ts
|
|
2
|
+
import { appendFileSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
function getLogDir() {
|
|
6
|
+
const dir = join(homedir(), ".purix", "logs");
|
|
7
|
+
if (!existsSync(dir)) {
|
|
8
|
+
try {
|
|
9
|
+
mkdirSync(dir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
catch { }
|
|
12
|
+
}
|
|
13
|
+
return dir;
|
|
14
|
+
}
|
|
15
|
+
function getLogFilePath() {
|
|
16
|
+
const dateStr = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
|
|
17
|
+
return join(getLogDir(), `purix-${dateStr}.log`);
|
|
18
|
+
}
|
|
19
|
+
export function logError(err, context) {
|
|
20
|
+
try {
|
|
21
|
+
const dir = getLogDir();
|
|
22
|
+
const p = getLogFilePath();
|
|
23
|
+
const entry = {
|
|
24
|
+
timestamp: new Date().toISOString(),
|
|
25
|
+
context: context ?? "cli",
|
|
26
|
+
message: err instanceof Error ? err.message : String(err),
|
|
27
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
28
|
+
version: "0.1.0",
|
|
29
|
+
};
|
|
30
|
+
appendFileSync(p, JSON.stringify(entry) + "\n", "utf-8");
|
|
31
|
+
// Clean up logs older than 7 days
|
|
32
|
+
cleanupOldLogs(dir);
|
|
33
|
+
}
|
|
34
|
+
catch { }
|
|
35
|
+
}
|
|
36
|
+
function cleanupOldLogs(dir) {
|
|
37
|
+
try {
|
|
38
|
+
const files = readdirNames(dir);
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
if (file.startsWith("purix-") && file.endsWith(".log")) {
|
|
42
|
+
const fp = join(dir, file);
|
|
43
|
+
const stats = statSync(fp);
|
|
44
|
+
if (now - stats.mtimeMs > 7 * 24 * 60 * 60 * 1000) {
|
|
45
|
+
rmSync(fp, { force: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
}
|
|
52
|
+
function readdirNames(dir) {
|
|
53
|
+
try {
|
|
54
|
+
return readdirSync(dir);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function getRecentLogs(maxLines = 50) {
|
|
61
|
+
try {
|
|
62
|
+
const dir = getLogDir();
|
|
63
|
+
const files = readdirNames(dir)
|
|
64
|
+
.filter((f) => f.startsWith("purix-") && f.endsWith(".log"))
|
|
65
|
+
.sort()
|
|
66
|
+
.reverse();
|
|
67
|
+
const lines = [];
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const content = readFileSync(join(dir, file), "utf-8");
|
|
70
|
+
const fileLines = content.trim().split("\n").filter(Boolean);
|
|
71
|
+
for (const line of fileLines.reverse()) {
|
|
72
|
+
lines.push(line);
|
|
73
|
+
if (lines.length >= maxLines)
|
|
74
|
+
return lines;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return lines;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export function redactLogContent(line) {
|
|
84
|
+
// Redact potential absolute paths, secrets, or api keys
|
|
85
|
+
return line
|
|
86
|
+
.replace(/[C-Z]:\\[^\s"']+/gi, "[REDACTED_PATH]")
|
|
87
|
+
.replace(/\/home\/[^\s"']+/gi, "[REDACTED_PATH]")
|
|
88
|
+
.replace(/\/Users\/[^\s"']+/gi, "[REDACTED_PATH]")
|
|
89
|
+
.replace(/AKIA[0-9A-Z]{16}/g, "[REDACTED_SECRET]")
|
|
90
|
+
.replace(/bearer\s+[a-zA-Z0-9_\-\.]+/gi, "bearer [REDACTED_TOKEN]");
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/telemetry/log.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@purix/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Governance-first AI coding agent CLI: classifies and verifies AI-driven code changes in a sandbox, against your test suite, before they land. Also runs as an MCP server for other coding agents.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"cli",
|
|
8
|
+
"code-verification",
|
|
9
|
+
"governance",
|
|
10
|
+
"mcp",
|
|
11
|
+
"model-context-protocol",
|
|
12
|
+
"sandbox",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"license": "Apache-2.0",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/purix-ai/purix.git",
|
|
20
|
+
"directory": "packages/cli"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"bin": {
|
|
26
|
+
"purix": "./dist/cli.js"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@purix/core": "^0.1.0",
|
|
30
|
+
"@purix/mcp-server": "^0.1.0",
|
|
31
|
+
"add-mcp": "^2.4.0",
|
|
32
|
+
"commander": "^15.0.0",
|
|
33
|
+
"dotenv": "^17.4.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "22.13.0",
|
|
37
|
+
"typescript": "5.7.2",
|
|
38
|
+
"expect": "^29.7.0",
|
|
39
|
+
"tsx": "^4.19.2"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc -p tsconfig.build.json",
|
|
43
|
+
"purix": "tsx src/cli.ts",
|
|
44
|
+
"test": "tsx --test \"src/**/*.test.ts\"",
|
|
45
|
+
"typecheck": "tsc --noEmit"
|
|
46
|
+
}
|
|
47
|
+
}
|