qodewk 0.2.1 → 0.2.2
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 +4 -0
- package/dist/index.cjs +314 -132
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -114,10 +114,14 @@ Unlike single-vendor utilities (like Chit, which only support Claude Code CLI),
|
|
|
114
114
|
| `npx qodewk --platform <platform>` | Filter telemetry to a specific platform (`antigravity`, `claude`, `cursor`, `all`) | Single-platform audit |
|
|
115
115
|
| `npx qodewk --json` | Export machine-readable telemetry conforming to canonical `ReceiptV1` schema | Formatted JSON output |
|
|
116
116
|
| `npx qodewk share` | Publish privacy-safe metadata to Qodewk Cloud and generate short link | `https://qodewk.dev/r/rec_...` |
|
|
117
|
+
| `npx qodewk share --today` | Share today's harvested footprint window | Public URL + claim token |
|
|
117
118
|
| `npx qodewk audit --base <branch>` | Calculate aggregate diff and telemetry across an entire PR branch range | Git revision delta receipt |
|
|
119
|
+
| `npx qodewk hook install` / `hooks install` | Install non-blocking `post-commit` + `post-rewrite` hooks | Local auto-record |
|
|
120
|
+
| `npx qodewk record` / `record-event` | Silent SQLite record (hook entrypoint) | exit 0 always |
|
|
118
121
|
| `npx qodewk -f markdown -o receipt.md` | Export sticky Markdown receipt directly formatted for GitHub PR comments | File `receipt.md` |
|
|
119
122
|
| `npx qodewk -p <provider> -m <model>` | Override detected provider & frontier model pricing rate card | Custom model cost estimate |
|
|
120
123
|
| `npx qodewk --anon` | Redact sensitive repository and branch identifiers | Privacy-hardened receipt |
|
|
124
|
+
| `npx qodewk --local` | Force local-only mode (no cloud sockets on share) | Local thermal receipt |
|
|
121
125
|
|
|
122
126
|
---
|
|
123
127
|
|
package/dist/index.cjs
CHANGED
|
@@ -15332,9 +15332,15 @@ async function extractGitMetrics(optionsOrPath = process.cwd()) {
|
|
|
15332
15332
|
let renames = 0;
|
|
15333
15333
|
const extCounts = {};
|
|
15334
15334
|
let totalTrackedFiles = 0;
|
|
15335
|
+
const changedFiles = [];
|
|
15335
15336
|
for (const f of diffSummary.files) {
|
|
15336
15337
|
if (f.file.includes(" => ")) {
|
|
15337
15338
|
renames++;
|
|
15339
|
+
const dest = f.file.split(" => ").pop()?.trim();
|
|
15340
|
+
if (dest)
|
|
15341
|
+
changedFiles.push(dest.replace(/\\/g, "/"));
|
|
15342
|
+
} else {
|
|
15343
|
+
changedFiles.push(f.file.replace(/\\/g, "/"));
|
|
15338
15344
|
}
|
|
15339
15345
|
const ext = path.extname(f.file).replace(/^\./, "").toLowerCase();
|
|
15340
15346
|
const lang = mapExtensionToLanguage(ext);
|
|
@@ -15358,6 +15364,7 @@ async function extractGitMetrics(optionsOrPath = process.cwd()) {
|
|
|
15358
15364
|
netLines,
|
|
15359
15365
|
renames,
|
|
15360
15366
|
languages,
|
|
15367
|
+
changedFiles,
|
|
15361
15368
|
branch,
|
|
15362
15369
|
headSha,
|
|
15363
15370
|
baseSha,
|
|
@@ -15367,6 +15374,21 @@ async function extractGitMetrics(optionsOrPath = process.cwd()) {
|
|
|
15367
15374
|
commitsCount
|
|
15368
15375
|
};
|
|
15369
15376
|
}
|
|
15377
|
+
function computeAiWrittenRatio(changedFiles, aiEditedFiles) {
|
|
15378
|
+
if (changedFiles.length === 0 || aiEditedFiles.length === 0) {
|
|
15379
|
+
return void 0;
|
|
15380
|
+
}
|
|
15381
|
+
const norm = (f) => f.replace(/\\/g, "/").replace(/^\.\//, "").toLowerCase();
|
|
15382
|
+
const aiNorm = aiEditedFiles.map(norm);
|
|
15383
|
+
let hit = 0;
|
|
15384
|
+
for (const cf of changedFiles) {
|
|
15385
|
+
const c3 = norm(cf);
|
|
15386
|
+
const matched = aiNorm.some((a) => a === c3 || c3.endsWith("/" + a) || a.endsWith("/" + c3) || c3.endsWith(a) || a.endsWith(c3));
|
|
15387
|
+
if (matched)
|
|
15388
|
+
hit++;
|
|
15389
|
+
}
|
|
15390
|
+
return Math.round(hit / changedFiles.length * 100) / 100;
|
|
15391
|
+
}
|
|
15370
15392
|
function mapExtensionToLanguage(ext) {
|
|
15371
15393
|
const map = {
|
|
15372
15394
|
ts: "TypeScript",
|
|
@@ -16296,9 +16318,12 @@ function formatMarkdownReceipt(receipt, publicUrl) {
|
|
|
16296
16318
|
const costPrefix = receipt.ai.mode === "verified" ? "$" : "~$";
|
|
16297
16319
|
const confidencePercent = `${Math.round(receipt.ai.confidence * 100)}%`;
|
|
16298
16320
|
const confidenceTier = receipt.ai.confidence >= 0.8 ? "High" : receipt.ai.confidence >= 0.4 ? "Medium" : "Low";
|
|
16321
|
+
const tokenProvenance = receipt.ai.mode === "verified" || receipt.ai.mode === "observed" ? "Observed" : "Estimated";
|
|
16322
|
+
const costProvenance = receipt.ai.mode === "verified" ? "Verified" : receipt.ai.mode === "observed" ? "Observed" : "Estimated";
|
|
16323
|
+
const tokenDisplay = receipt.ai.mode === "verified" || receipt.ai.mode === "observed" ? totalTokens : `~${totalTokens}`;
|
|
16299
16324
|
const lines = [
|
|
16300
16325
|
`<!-- QODEWK_RECEIPT_START:${receipt.receipt.id} -->`,
|
|
16301
|
-
`###
|
|
16326
|
+
`### Qodewk Telemetry Receipt`,
|
|
16302
16327
|
``,
|
|
16303
16328
|
`| Metric | Measurement | Provenance |`,
|
|
16304
16329
|
`| :--- | :--- | :--- |`,
|
|
@@ -16307,19 +16332,42 @@ function formatMarkdownReceipt(receipt, publicUrl) {
|
|
|
16307
16332
|
`| **Lines Deleted** | \`-${receipt.mutation.deletions}\` | Observed |`,
|
|
16308
16333
|
`| **Net Delta** | \`${receipt.mutation.netLines >= 0 ? "+" : ""}${receipt.mutation.netLines}\` | Observed |`,
|
|
16309
16334
|
`| **AI Model** | \`${receipt.ai.provider} \xB7 ${receipt.ai.model || "Unknown"}\` | ${receipt.ai.mode} |`,
|
|
16310
|
-
`| **Tokens Consumed** |
|
|
16311
|
-
`| **AI Spend** | \`${costPrefix}${receipt.ai.cost.toFixed(2)}\` |
|
|
16335
|
+
`| **Tokens Consumed** | \`${tokenDisplay}\` | ${tokenProvenance} |`,
|
|
16336
|
+
`| **AI Spend** | \`${costPrefix}${receipt.ai.cost.toFixed(2)}\` | ${costProvenance} |`,
|
|
16312
16337
|
`| **Confidence** | \`${confidencePercent}\` (${confidenceTier}) | Dual-Engine |`,
|
|
16313
16338
|
``,
|
|
16314
|
-
`>
|
|
16339
|
+
`> **Privacy Guarantee:** *Source code was never uploaded to Qodewk. Telemetry computed strictly from cryptographic Git metadata.*`,
|
|
16315
16340
|
``,
|
|
16316
|
-
|
|
16341
|
+
`[**View Full Digital Receipt \u2192**](${url})`,
|
|
16317
16342
|
`<!-- QODEWK_RECEIPT_END -->`
|
|
16318
16343
|
];
|
|
16319
16344
|
return lines.join("\n");
|
|
16320
16345
|
}
|
|
16321
16346
|
|
|
16322
16347
|
// ../core/dist/index.js
|
|
16348
|
+
function sanitizeReceiptForShare(receipt) {
|
|
16349
|
+
const sessions = receipt.ai.sessions?.map((s) => ({
|
|
16350
|
+
provider: s.provider,
|
|
16351
|
+
model: s.model,
|
|
16352
|
+
task: s.task,
|
|
16353
|
+
tokens: s.tokens,
|
|
16354
|
+
cost: s.cost,
|
|
16355
|
+
confidence: s.confidence,
|
|
16356
|
+
mode: s.mode
|
|
16357
|
+
}));
|
|
16358
|
+
return {
|
|
16359
|
+
...receipt,
|
|
16360
|
+
privacy: {
|
|
16361
|
+
...receipt.privacy,
|
|
16362
|
+
sourceExcluded: true,
|
|
16363
|
+
isPublic: true
|
|
16364
|
+
},
|
|
16365
|
+
ai: {
|
|
16366
|
+
...receipt.ai,
|
|
16367
|
+
sessions
|
|
16368
|
+
}
|
|
16369
|
+
};
|
|
16370
|
+
}
|
|
16323
16371
|
async function generateReceipt(options = {}) {
|
|
16324
16372
|
const metrics = await extractGitMetrics({
|
|
16325
16373
|
repoPath: options.repoPath || process.cwd(),
|
|
@@ -16335,7 +16383,7 @@ async function generateReceipt(options = {}) {
|
|
|
16335
16383
|
platform: options.platform
|
|
16336
16384
|
});
|
|
16337
16385
|
const primaryFp = harvestResult.primaryFootprint;
|
|
16338
|
-
|
|
16386
|
+
const detected = detectProviderFromCommit(metrics.commitMessage);
|
|
16339
16387
|
const provider = options.provider || primaryFp?.platform || detected?.provider || "unknown";
|
|
16340
16388
|
const model = options.model || primaryFp?.model || detected?.model;
|
|
16341
16389
|
const task = options.task || primaryFp?.taskTitle || (harvestResult.tasks.length > 0 ? harvestResult.tasks[0] : void 0);
|
|
@@ -16349,10 +16397,7 @@ async function generateReceipt(options = {}) {
|
|
|
16349
16397
|
confidence: fp.confidence,
|
|
16350
16398
|
mode: fp.mode
|
|
16351
16399
|
}));
|
|
16352
|
-
|
|
16353
|
-
if (primaryFp) {
|
|
16354
|
-
aiWrittenRatio = 0.88;
|
|
16355
|
-
}
|
|
16400
|
+
const aiWrittenRatio = computeAiWrittenRatio(metrics.changedFiles, harvestResult.filesEdited);
|
|
16356
16401
|
const costResult = estimateCost({
|
|
16357
16402
|
files: metrics.files,
|
|
16358
16403
|
insertions: metrics.insertions,
|
|
@@ -16362,18 +16407,22 @@ async function generateReceipt(options = {}) {
|
|
|
16362
16407
|
sessions: sessions.length > 0 ? sessions : void 0,
|
|
16363
16408
|
confidence: primaryFp?.confidence || detected?.confidence
|
|
16364
16409
|
});
|
|
16365
|
-
const receiptId = `rec_${crypto2.randomBytes(12).toString("hex")}`;
|
|
16366
16410
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16367
16411
|
const payloadToHash = {
|
|
16368
16412
|
repoHash: metrics.repoHash,
|
|
16369
16413
|
headSha: metrics.headSha,
|
|
16414
|
+
baseSha: metrics.baseSha,
|
|
16370
16415
|
files: metrics.files,
|
|
16371
16416
|
insertions: metrics.insertions,
|
|
16372
16417
|
deletions: metrics.deletions,
|
|
16373
16418
|
cost: costResult.cost,
|
|
16374
|
-
tokens: costResult.tokens
|
|
16419
|
+
tokens: costResult.tokens,
|
|
16420
|
+
mode: costResult.mode,
|
|
16421
|
+
provider: costResult.provider,
|
|
16422
|
+
model: costResult.model
|
|
16375
16423
|
};
|
|
16376
16424
|
const contentHash = crypto2.createHash("sha256").update(JSON.stringify(payloadToHash)).digest("hex");
|
|
16425
|
+
const receiptId = `rec_${contentHash.slice(0, 24)}`;
|
|
16377
16426
|
const receipt = {
|
|
16378
16427
|
version: "1.0",
|
|
16379
16428
|
receipt: {
|
|
@@ -16410,7 +16459,7 @@ async function generateReceipt(options = {}) {
|
|
|
16410
16459
|
},
|
|
16411
16460
|
privacy: {
|
|
16412
16461
|
sourceExcluded: true,
|
|
16413
|
-
isPublic: options.isPublic ??
|
|
16462
|
+
isPublic: options.isPublic ?? false,
|
|
16414
16463
|
anonymizeBranch: options.anonymizeBranch ?? false
|
|
16415
16464
|
}
|
|
16416
16465
|
};
|
|
@@ -16418,154 +16467,279 @@ async function generateReceipt(options = {}) {
|
|
|
16418
16467
|
}
|
|
16419
16468
|
|
|
16420
16469
|
// src/index.ts
|
|
16470
|
+
var SHARE_PAYLOAD_MAX_BYTES = 5e4;
|
|
16471
|
+
function resolveCliVersion() {
|
|
16472
|
+
try {
|
|
16473
|
+
const argv1 = process.argv[1] ? path7.dirname(path7.resolve(process.argv[1])) : process.cwd();
|
|
16474
|
+
const candidates = [
|
|
16475
|
+
path7.join(argv1, "..", "package.json"),
|
|
16476
|
+
path7.join(argv1, "package.json")
|
|
16477
|
+
];
|
|
16478
|
+
for (const candidate of candidates) {
|
|
16479
|
+
if (!fs6.existsSync(candidate)) continue;
|
|
16480
|
+
const pkg = JSON.parse(fs6.readFileSync(candidate, "utf-8"));
|
|
16481
|
+
if (pkg.name === "qodewk" && pkg.version) return pkg.version;
|
|
16482
|
+
}
|
|
16483
|
+
} catch {
|
|
16484
|
+
}
|
|
16485
|
+
return "0.2.1";
|
|
16486
|
+
}
|
|
16487
|
+
function resolveSince(options) {
|
|
16488
|
+
return options.today ? "today" : options.since;
|
|
16489
|
+
}
|
|
16490
|
+
function persistReceipt(receipt, claimToken) {
|
|
16491
|
+
try {
|
|
16492
|
+
const db = new LocalStateDB();
|
|
16493
|
+
db.saveReceipt(receipt, claimToken);
|
|
16494
|
+
db.close();
|
|
16495
|
+
} catch {
|
|
16496
|
+
}
|
|
16497
|
+
}
|
|
16498
|
+
function resolveGitHooksDir(cwd = process.cwd()) {
|
|
16499
|
+
const gitPath = path7.join(cwd, ".git");
|
|
16500
|
+
if (!fs6.existsSync(gitPath)) return null;
|
|
16501
|
+
let gitCommonDir;
|
|
16502
|
+
const stat = fs6.statSync(gitPath);
|
|
16503
|
+
if (stat.isDirectory()) {
|
|
16504
|
+
gitCommonDir = gitPath;
|
|
16505
|
+
} else {
|
|
16506
|
+
const content = fs6.readFileSync(gitPath, "utf-8");
|
|
16507
|
+
const match = content.match(/gitdir:\s*(.+)/i);
|
|
16508
|
+
if (!match?.[1]) return null;
|
|
16509
|
+
let gitDir = match[1].trim();
|
|
16510
|
+
if (!path7.isAbsolute(gitDir)) {
|
|
16511
|
+
gitDir = path7.resolve(cwd, gitDir);
|
|
16512
|
+
}
|
|
16513
|
+
const commonFile = path7.join(gitDir, "commondir");
|
|
16514
|
+
if (fs6.existsSync(commonFile)) {
|
|
16515
|
+
let common = fs6.readFileSync(commonFile, "utf-8").trim();
|
|
16516
|
+
if (!path7.isAbsolute(common)) {
|
|
16517
|
+
common = path7.resolve(gitDir, common);
|
|
16518
|
+
}
|
|
16519
|
+
gitCommonDir = common;
|
|
16520
|
+
} else if (path7.basename(path7.dirname(gitDir)) === "worktrees") {
|
|
16521
|
+
gitCommonDir = path7.dirname(path7.dirname(gitDir));
|
|
16522
|
+
} else {
|
|
16523
|
+
gitCommonDir = gitDir;
|
|
16524
|
+
}
|
|
16525
|
+
}
|
|
16526
|
+
return path7.join(gitCommonDir, "hooks");
|
|
16527
|
+
}
|
|
16528
|
+
var HOOK_MARKER_BEGIN = "# --- BEGIN QODEWK HOOK ---";
|
|
16529
|
+
var HOOK_MARKER_END = "# --- END QODEWK HOOK ---";
|
|
16530
|
+
var HOOK_SNIPPET = `
|
|
16531
|
+
${HOOK_MARKER_BEGIN}
|
|
16532
|
+
# Non-blocking Qodewk background recorder (< 5ms)
|
|
16533
|
+
if command -v qodewk >/dev/null 2>&1 || command -v pnpm >/dev/null 2>&1 || [ -f "./node_modules/.bin/qodewk" ]; then
|
|
16534
|
+
( ( qodewk record || pnpm qodewk record || npx qodewk record ) >/dev/null 2>&1 & )
|
|
16535
|
+
fi
|
|
16536
|
+
${HOOK_MARKER_END}
|
|
16537
|
+
`;
|
|
16538
|
+
function installHookFile(hookFile) {
|
|
16539
|
+
let content = "";
|
|
16540
|
+
if (fs6.existsSync(hookFile)) {
|
|
16541
|
+
content = fs6.readFileSync(hookFile, "utf-8");
|
|
16542
|
+
} else {
|
|
16543
|
+
content = "#!/bin/sh\n";
|
|
16544
|
+
}
|
|
16545
|
+
if (content.includes(HOOK_MARKER_BEGIN)) {
|
|
16546
|
+
return "exists";
|
|
16547
|
+
}
|
|
16548
|
+
fs6.writeFileSync(hookFile, content + HOOK_SNIPPET, { mode: 493 });
|
|
16549
|
+
return "installed";
|
|
16550
|
+
}
|
|
16551
|
+
function uninstallHookFile(hookFile) {
|
|
16552
|
+
if (!fs6.existsSync(hookFile)) return "absent";
|
|
16553
|
+
const content = fs6.readFileSync(hookFile, "utf-8");
|
|
16554
|
+
if (!content.includes(HOOK_MARKER_BEGIN)) return "missing";
|
|
16555
|
+
const regex = new RegExp(`\\n?${HOOK_MARKER_BEGIN}[\\s\\S]*?${HOOK_MARKER_END}\\n?`, "g");
|
|
16556
|
+
const updated = content.replace(regex, "");
|
|
16557
|
+
fs6.writeFileSync(hookFile, updated, { mode: 493 });
|
|
16558
|
+
return "removed";
|
|
16559
|
+
}
|
|
16421
16560
|
var program2 = new Command();
|
|
16422
|
-
program2.name("qodewk").description("Universal telemetry and digital receipt generator for the AI coding agent era").version(
|
|
16561
|
+
program2.name("qodewk").description("Universal telemetry and digital receipt generator for the AI coding agent era").version(resolveCliVersion()).option("-j, --json", "Output receipt in machine-readable JSON format").option("-f, --format <format>", "Output format (terminal, json, markdown)", "terminal").option("-o, --out <path>", "Write receipt output to specified file path").option("-p, --provider <provider>", "Specify AI provider (antigravity, claude, cursor, etc.)").option("-m, --model <model>", "Specify AI model identifier").option("-s, --since <duration>", "Harvest agent footprints since duration (e.g. today, 24h, 7d)").option("--today", "Harvest agent footprints for today").option("--platform <platform>", "Filter agent platform (antigravity, claude, cursor, all)").option("--anon", "Anonymize branch name in output").option("--local", "Force local-only mode (never open network sockets)").option("--barcode", "Render 1D barcode simulation instead of scannable 2D QR code").action(async (options) => {
|
|
16423
16562
|
try {
|
|
16424
|
-
const since = options.today ? "today" : options.since;
|
|
16425
16563
|
const receipt = await generateReceipt({
|
|
16426
16564
|
provider: options.provider,
|
|
16427
16565
|
model: options.model,
|
|
16428
|
-
since,
|
|
16566
|
+
since: resolveSince(options),
|
|
16429
16567
|
platform: options.platform,
|
|
16430
|
-
anonymizeBranch: options.anon
|
|
16568
|
+
anonymizeBranch: options.anon,
|
|
16569
|
+
isPublic: false
|
|
16431
16570
|
});
|
|
16432
|
-
|
|
16433
|
-
const db = new LocalStateDB();
|
|
16434
|
-
db.saveReceipt(receipt);
|
|
16435
|
-
db.close();
|
|
16436
|
-
} catch {
|
|
16437
|
-
}
|
|
16571
|
+
persistReceipt(receipt);
|
|
16438
16572
|
await outputReceipt(receipt, options);
|
|
16439
16573
|
} catch (err) {
|
|
16440
|
-
|
|
16574
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16575
|
+
console.error(import_picocolors.default.red(`Error generating receipt: ${message}`));
|
|
16441
16576
|
process.exit(1);
|
|
16442
16577
|
}
|
|
16443
16578
|
});
|
|
16444
|
-
program2.command("audit").description("Audit PR or branch diff against a target base commit").option("-b, --base <base>", "Base git ref or commit SHA (e.g. origin/main)").option("-h, --head <head>", "Head git ref or commit SHA").option("-f, --format <format>", "Output format (terminal, json, markdown)", "terminal").option("-o, --out <path>", "Write receipt output to specified file path").option("-p, --provider <provider>", "Specify AI provider").option("-m, --model <model>", "Specify AI model").option("--barcode", "Render 1D barcode simulation instead of scannable 2D QR code").action(async (options) => {
|
|
16579
|
+
program2.command("audit").description("Audit PR or branch diff against a target base commit").option("-b, --base <base>", "Base git ref or commit SHA (e.g. origin/main)").option("-h, --head <head>", "Head git ref or commit SHA").option("-f, --format <format>", "Output format (terminal, json, markdown)", "terminal").option("-o, --out <path>", "Write receipt output to specified file path").option("-p, --provider <provider>", "Specify AI provider").option("-m, --model <model>", "Specify AI model").option("-s, --since <duration>", "Harvest agent footprints since duration").option("--today", "Harvest agent footprints for today").option("--platform <platform>", "Filter agent platform").option("--anon", "Anonymize branch name in output").option("--barcode", "Render 1D barcode simulation instead of scannable 2D QR code").action(async (options) => {
|
|
16445
16580
|
try {
|
|
16446
16581
|
const receipt = await generateReceipt({
|
|
16447
16582
|
baseSha: options.base,
|
|
16448
16583
|
headSha: options.head,
|
|
16449
16584
|
provider: options.provider,
|
|
16450
|
-
model: options.model
|
|
16585
|
+
model: options.model,
|
|
16586
|
+
since: resolveSince(options),
|
|
16587
|
+
platform: options.platform,
|
|
16588
|
+
anonymizeBranch: options.anon,
|
|
16589
|
+
isPublic: false
|
|
16451
16590
|
});
|
|
16591
|
+
persistReceipt(receipt);
|
|
16452
16592
|
await outputReceipt(receipt, options);
|
|
16453
16593
|
} catch (err) {
|
|
16454
|
-
|
|
16594
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16595
|
+
console.error(import_picocolors.default.red(`Error auditing git range: ${message}`));
|
|
16455
16596
|
process.exit(1);
|
|
16456
16597
|
}
|
|
16457
16598
|
});
|
|
16458
|
-
program2.command("share").description("Publish privacy-safe receipt to Qodewk cloud and get a shareable URL").option("-p, --provider <provider>", "Specify AI provider").option("-m, --model <model>", "Specify AI model").option("--barcode", "Render 1D barcode simulation instead of scannable 2D QR code").action(async (options) => {
|
|
16599
|
+
program2.command("share").description("Publish privacy-safe receipt to Qodewk cloud and get a shareable URL").option("-p, --provider <provider>", "Specify AI provider").option("-m, --model <model>", "Specify AI model").option("-s, --since <duration>", "Harvest agent footprints since duration").option("--today", "Harvest agent footprints for today").option("--platform <platform>", "Filter agent platform").option("--anon", "Anonymize branch name in output").option("-f, --format <format>", "Local fallback output format", "terminal").option("-j, --json", "Also print JSON after share").option("--barcode", "Render 1D barcode simulation instead of scannable 2D QR code").action(async (options) => {
|
|
16459
16600
|
try {
|
|
16460
16601
|
const receipt = await generateReceipt({
|
|
16461
16602
|
provider: options.provider,
|
|
16462
|
-
model: options.model
|
|
16603
|
+
model: options.model,
|
|
16604
|
+
since: resolveSince(options),
|
|
16605
|
+
platform: options.platform,
|
|
16606
|
+
anonymizeBranch: options.anon,
|
|
16607
|
+
isPublic: false
|
|
16463
16608
|
});
|
|
16464
|
-
|
|
16465
|
-
|
|
16609
|
+
const localFlag = program2.opts().local === true;
|
|
16610
|
+
if (process.env.QODEWK_TELEMETRY === "off" || localFlag) {
|
|
16611
|
+
console.log(
|
|
16612
|
+
import_picocolors.default.yellow(
|
|
16613
|
+
`
|
|
16614
|
+
Cloud publishing is disabled (${localFlag ? "--local" : "QODEWK_TELEMETRY=off"}).`
|
|
16615
|
+
)
|
|
16616
|
+
);
|
|
16466
16617
|
console.log(import_picocolors.default.dim("Telemetry remains strictly stored in local SQLite (~/.qodewk/state.db).\n"));
|
|
16618
|
+
persistReceipt(receipt);
|
|
16467
16619
|
await renderTerminalReceipt(receipt, void 0, options.barcode);
|
|
16468
16620
|
return;
|
|
16469
16621
|
}
|
|
16622
|
+
const sanitized = sanitizeReceiptForShare(receipt);
|
|
16623
|
+
let body = JSON.stringify(sanitized);
|
|
16624
|
+
if (Buffer.byteLength(body, "utf-8") > SHARE_PAYLOAD_MAX_BYTES && sanitized.ai.sessions) {
|
|
16625
|
+
const trimmed2 = {
|
|
16626
|
+
...sanitized,
|
|
16627
|
+
ai: { ...sanitized.ai, sessions: void 0 }
|
|
16628
|
+
};
|
|
16629
|
+
body = JSON.stringify(trimmed2);
|
|
16630
|
+
}
|
|
16631
|
+
if (Buffer.byteLength(body, "utf-8") > SHARE_PAYLOAD_MAX_BYTES) {
|
|
16632
|
+
console.error(import_picocolors.default.red("Sanitized receipt still exceeds the 50 KB cloud payload limit."));
|
|
16633
|
+
process.exit(1);
|
|
16634
|
+
}
|
|
16470
16635
|
const endpoint = process.env.QODEWK_API_URL || `${process.env.NEXT_PUBLIC_APP_URL || process.env.QODEWK_APP_URL || "https://qodewk.flinkeo.online"}/api/receipts`;
|
|
16471
|
-
console.log(import_picocolors.default.dim(`Publishing receipt ${
|
|
16636
|
+
console.log(import_picocolors.default.dim(`Publishing receipt ${sanitized.receipt.id} to ${endpoint}...`));
|
|
16472
16637
|
try {
|
|
16473
16638
|
const response = await fetch(endpoint, {
|
|
16474
16639
|
method: "POST",
|
|
16475
16640
|
headers: {
|
|
16476
|
-
"Content-Type": "application/json"
|
|
16641
|
+
"Content-Type": "application/json",
|
|
16642
|
+
"Content-Length": String(Buffer.byteLength(body, "utf-8"))
|
|
16477
16643
|
},
|
|
16478
|
-
body
|
|
16644
|
+
body
|
|
16479
16645
|
});
|
|
16480
16646
|
if (!response.ok) {
|
|
16481
|
-
|
|
16647
|
+
const errText = await response.text().catch(() => "");
|
|
16648
|
+
throw new Error(`API responded with status ${response.status}${errText ? `: ${errText.slice(0, 200)}` : ""}`);
|
|
16482
16649
|
}
|
|
16483
16650
|
const data = await response.json();
|
|
16484
|
-
|
|
16485
|
-
|
|
16486
|
-
|
|
16487
|
-
|
|
16651
|
+
persistReceipt(sanitized, data.claimToken);
|
|
16652
|
+
console.log("");
|
|
16653
|
+
console.log(import_picocolors.default.green(" Published successfully."));
|
|
16654
|
+
console.log(` ${import_picocolors.default.dim("Public URL:")} ${import_picocolors.default.underline(import_picocolors.default.cyan(data.url))}`);
|
|
16655
|
+
console.log(` ${import_picocolors.default.bold(import_picocolors.default.yellow(" Claim token (store securely \u2014 shown once):"))}`);
|
|
16656
|
+
console.log(` ${import_picocolors.default.yellow(data.claimToken)}`);
|
|
16657
|
+
console.log(import_picocolors.default.dim(" Saved to ~/.qodewk/state.db \u2014 required to prove authorship later."));
|
|
16658
|
+
console.log("");
|
|
16659
|
+
await renderTerminalReceipt(sanitized, data.url, options.barcode);
|
|
16660
|
+
if (options.json) {
|
|
16661
|
+
console.log(JSON.stringify({ url: data.url, claimToken: data.claimToken, receipt: sanitized }, null, 2));
|
|
16662
|
+
}
|
|
16488
16663
|
} catch (networkErr) {
|
|
16664
|
+
const message = networkErr instanceof Error ? networkErr.message : String(networkErr);
|
|
16489
16665
|
console.log(import_picocolors.default.yellow(`
|
|
16490
|
-
Could not reach cloud API (${
|
|
16666
|
+
Could not reach cloud API (${message}). Rendered locally:`));
|
|
16667
|
+
persistReceipt(receipt);
|
|
16491
16668
|
await renderTerminalReceipt(receipt, void 0, options.barcode);
|
|
16492
16669
|
}
|
|
16493
16670
|
} catch (err) {
|
|
16494
|
-
|
|
16671
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16672
|
+
console.error(import_picocolors.default.red(`Error sharing receipt: ${message}`));
|
|
16495
16673
|
process.exit(1);
|
|
16496
16674
|
}
|
|
16497
16675
|
});
|
|
16498
|
-
program2.command("record").description("Silently record local telemetry receipt into SQLite (used by git hooks)").action(async () => {
|
|
16676
|
+
program2.command("record").alias("record-event").description("Silently record local telemetry receipt into SQLite (used by git hooks)").action(async () => {
|
|
16499
16677
|
try {
|
|
16500
|
-
const receipt = await generateReceipt();
|
|
16501
|
-
|
|
16502
|
-
db.saveReceipt(receipt);
|
|
16503
|
-
db.close();
|
|
16678
|
+
const receipt = await generateReceipt({ isPublic: false });
|
|
16679
|
+
persistReceipt(receipt);
|
|
16504
16680
|
process.exit(0);
|
|
16505
16681
|
} catch {
|
|
16506
16682
|
process.exit(0);
|
|
16507
16683
|
}
|
|
16508
16684
|
});
|
|
16509
|
-
var hookCommand = program2.command("hook").description("Manage non-blocking Git hooks for automatic telemetry recording");
|
|
16510
|
-
hookCommand.command("install").description("Install non-blocking post-commit
|
|
16685
|
+
var hookCommand = program2.command("hook").aliases(["hooks"]).description("Manage non-blocking Git hooks for automatic telemetry recording");
|
|
16686
|
+
hookCommand.command("install").description("Install non-blocking post-commit and post-rewrite Git hooks").action(async () => {
|
|
16511
16687
|
try {
|
|
16512
|
-
const
|
|
16513
|
-
if (!
|
|
16514
|
-
console.error(import_picocolors.default.red("Error: Current directory is not a Git repository
|
|
16688
|
+
const hooksDir = resolveGitHooksDir();
|
|
16689
|
+
if (!hooksDir) {
|
|
16690
|
+
console.error(import_picocolors.default.red("Error: Current directory is not a Git repository (.git not found)."));
|
|
16515
16691
|
process.exit(1);
|
|
16516
16692
|
}
|
|
16517
|
-
const hooksDir = path7.join(gitDir, "hooks");
|
|
16518
16693
|
if (!fs6.existsSync(hooksDir)) {
|
|
16519
16694
|
fs6.mkdirSync(hooksDir, { recursive: true });
|
|
16520
16695
|
}
|
|
16521
|
-
const
|
|
16522
|
-
|
|
16523
|
-
|
|
16524
|
-
const
|
|
16525
|
-
|
|
16526
|
-
|
|
16527
|
-
|
|
16528
|
-
( ( qodewk record || pnpm qodewk record || npx qodewk record ) >/dev/null 2>&1 & )
|
|
16529
|
-
fi
|
|
16530
|
-
${hookMarkerEnd}
|
|
16531
|
-
`;
|
|
16532
|
-
let content = "";
|
|
16533
|
-
if (fs6.existsSync(hookFile)) {
|
|
16534
|
-
content = fs6.readFileSync(hookFile, "utf-8");
|
|
16535
|
-
} else {
|
|
16536
|
-
content = "#!/bin/sh\n";
|
|
16696
|
+
const targets = ["post-commit", "post-rewrite"];
|
|
16697
|
+
let installed = 0;
|
|
16698
|
+
let existing = 0;
|
|
16699
|
+
for (const name of targets) {
|
|
16700
|
+
const result = installHookFile(path7.join(hooksDir, name));
|
|
16701
|
+
if (result === "installed") installed++;
|
|
16702
|
+
else existing++;
|
|
16537
16703
|
}
|
|
16538
|
-
if (
|
|
16539
|
-
console.log(import_picocolors.default.yellow("Qodewk
|
|
16704
|
+
if (installed === 0 && existing > 0) {
|
|
16705
|
+
console.log(import_picocolors.default.yellow("Qodewk Git hooks are already installed (post-commit, post-rewrite)."));
|
|
16540
16706
|
return;
|
|
16541
16707
|
}
|
|
16542
|
-
|
|
16543
|
-
|
|
16708
|
+
console.log(
|
|
16709
|
+
import_picocolors.default.green(
|
|
16710
|
+
`\u2713 Non-blocking Qodewk hooks installed in ${hooksDir} (post-commit${installed > 1 || existing > 0 ? ", post-rewrite" : installed === 1 && targets.length === 2 ? " + post-rewrite" : ""})`
|
|
16711
|
+
)
|
|
16712
|
+
);
|
|
16713
|
+
if (existing > 0) {
|
|
16714
|
+
console.log(import_picocolors.default.dim(` (${existing} hook file(s) already contained the Qodewk marker)`));
|
|
16715
|
+
}
|
|
16544
16716
|
} catch (err) {
|
|
16545
|
-
|
|
16717
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16718
|
+
console.error(import_picocolors.default.red(`Failed to install Git hook: ${message}`));
|
|
16546
16719
|
process.exit(1);
|
|
16547
16720
|
}
|
|
16548
16721
|
});
|
|
16549
|
-
hookCommand.command("uninstall").description("Remove Qodewk post-commit Git
|
|
16722
|
+
hookCommand.command("uninstall").description("Remove Qodewk post-commit and post-rewrite Git hooks").action(async () => {
|
|
16550
16723
|
try {
|
|
16551
|
-
const
|
|
16552
|
-
if (!
|
|
16553
|
-
console.log(import_picocolors.default.yellow("No
|
|
16724
|
+
const hooksDir = resolveGitHooksDir();
|
|
16725
|
+
if (!hooksDir) {
|
|
16726
|
+
console.log(import_picocolors.default.yellow("No Git repository found."));
|
|
16554
16727
|
return;
|
|
16555
16728
|
}
|
|
16556
|
-
const
|
|
16557
|
-
|
|
16558
|
-
const
|
|
16559
|
-
|
|
16560
|
-
|
|
16729
|
+
const targets = ["post-commit", "post-rewrite"];
|
|
16730
|
+
let removed = 0;
|
|
16731
|
+
for (const name of targets) {
|
|
16732
|
+
const result = uninstallHookFile(path7.join(hooksDir, name));
|
|
16733
|
+
if (result === "removed") removed++;
|
|
16734
|
+
}
|
|
16735
|
+
if (removed === 0) {
|
|
16736
|
+
console.log(import_picocolors.default.yellow("Qodewk hooks are not installed."));
|
|
16561
16737
|
return;
|
|
16562
16738
|
}
|
|
16563
|
-
|
|
16564
|
-
const updated = content.replace(regex, "");
|
|
16565
|
-
fs6.writeFileSync(hookFile, updated, { mode: 493 });
|
|
16566
|
-
console.log(import_picocolors.default.green("\u2713 Qodewk post-commit hook removed."));
|
|
16739
|
+
console.log(import_picocolors.default.green(`\u2713 Qodewk hook markers removed from ${removed} file(s).`));
|
|
16567
16740
|
} catch (err) {
|
|
16568
|
-
|
|
16741
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16742
|
+
console.error(import_picocolors.default.red(`Failed to uninstall hook: ${message}`));
|
|
16569
16743
|
process.exit(1);
|
|
16570
16744
|
}
|
|
16571
16745
|
});
|
|
@@ -16576,64 +16750,48 @@ var ansiHex = (hexColor) => {
|
|
|
16576
16750
|
const b2 = num & 255;
|
|
16577
16751
|
return (text) => `\x1B[38;2;${r2};${g};${b2}m${text}\x1B[39m`;
|
|
16578
16752
|
};
|
|
16579
|
-
async function outputReceipt(receipt, options, publicUrl) {
|
|
16580
|
-
let content = "";
|
|
16581
|
-
const format = options.json ? "json" : options.format || "terminal";
|
|
16582
|
-
if (format === "json") {
|
|
16583
|
-
content = JSON.stringify(receipt, null, 2);
|
|
16584
|
-
} else if (format === "markdown") {
|
|
16585
|
-
content = formatMarkdownReceipt(receipt, publicUrl);
|
|
16586
|
-
}
|
|
16587
|
-
if (options.out) {
|
|
16588
|
-
fs6.writeFileSync(options.out, content || formatMarkdownReceipt(receipt, publicUrl), "utf-8");
|
|
16589
|
-
console.log(import_picocolors.default.green(`\u2713 Receipt written to ${options.out}`));
|
|
16590
|
-
}
|
|
16591
|
-
if (format === "terminal" && !options.out) {
|
|
16592
|
-
await renderTerminalReceipt(receipt, publicUrl, options.barcode);
|
|
16593
|
-
} else if (format !== "terminal" && !options.out) {
|
|
16594
|
-
console.log(content);
|
|
16595
|
-
}
|
|
16596
|
-
}
|
|
16597
16753
|
var stripAnsi = (str) => {
|
|
16598
16754
|
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1B\([B0]/g, "");
|
|
16599
16755
|
};
|
|
16600
16756
|
var visibleWidth = (str) => {
|
|
16601
16757
|
return stripAnsi(str).length;
|
|
16602
16758
|
};
|
|
16603
|
-
|
|
16759
|
+
function buildTerminalReceipt(receipt, publicUrl, useBarcode) {
|
|
16760
|
+
const lines = [];
|
|
16604
16761
|
const coral = ansiHex("#cc785c");
|
|
16605
16762
|
const green = ansiHex("#5db872");
|
|
16606
16763
|
const red = ansiHex("#c64545");
|
|
16607
16764
|
const teal = ansiHex("#5db8a6");
|
|
16608
16765
|
const INNER_WIDTH = 52;
|
|
16766
|
+
const push = (line) => lines.push(line);
|
|
16609
16767
|
const printRow = (content, width = INNER_WIDTH) => {
|
|
16610
16768
|
const visLen = visibleWidth(content);
|
|
16611
16769
|
const pad = Math.max(0, width - visLen);
|
|
16612
|
-
|
|
16770
|
+
push(` | ${content}${" ".repeat(pad)} |`);
|
|
16613
16771
|
};
|
|
16614
16772
|
const printRowSplit = (left, right, width = INNER_WIDTH) => {
|
|
16615
16773
|
const leftVis = visibleWidth(left);
|
|
16616
16774
|
const rightVis = visibleWidth(right);
|
|
16617
16775
|
const pad = Math.max(1, width - leftVis - rightVis);
|
|
16618
|
-
|
|
16776
|
+
push(` | ${left}${" ".repeat(pad)}${right} |`);
|
|
16619
16777
|
};
|
|
16620
16778
|
const printCenteredRow = (content, width = INNER_WIDTH) => {
|
|
16621
16779
|
const visLen = visibleWidth(content);
|
|
16622
16780
|
const totalPad = Math.max(0, width - visLen);
|
|
16623
16781
|
const leftPad = Math.floor(totalPad / 2);
|
|
16624
16782
|
const rightPad = totalPad - leftPad;
|
|
16625
|
-
|
|
16783
|
+
push(` | ${" ".repeat(leftPad)}${content}${" ".repeat(rightPad)} |`);
|
|
16626
16784
|
};
|
|
16627
16785
|
const printDivider = (char = "=", width = INNER_WIDTH) => {
|
|
16628
|
-
|
|
16786
|
+
push(` | ${char.repeat(width)} |`);
|
|
16629
16787
|
};
|
|
16630
16788
|
const totalTokens = (receipt.ai.tokens.input + receipt.ai.tokens.output).toLocaleString();
|
|
16631
16789
|
const inputK = `${Math.round(receipt.ai.tokens.input / 1e3)}k`;
|
|
16632
16790
|
const outputK = `${Math.round(receipt.ai.tokens.output / 1e3)}k`;
|
|
16633
16791
|
const costPrefix = receipt.ai.mode === "verified" ? "$" : "~$";
|
|
16634
16792
|
const confidencePercent = `${Math.round(receipt.ai.confidence * 100)}%`;
|
|
16635
|
-
|
|
16636
|
-
|
|
16793
|
+
push("");
|
|
16794
|
+
push(" " + import_picocolors.default.bold(coral("/\\".repeat(28))));
|
|
16637
16795
|
printCenteredRow(import_picocolors.default.bold(import_picocolors.default.white("Q O D E W K")));
|
|
16638
16796
|
printCenteredRow(coral("*** PROOF OF SHIPMENT ***"));
|
|
16639
16797
|
printRow("");
|
|
@@ -16675,19 +16833,12 @@ async function renderTerminalReceipt(receipt, publicUrl, useBarcode) {
|
|
|
16675
16833
|
printRowSplit("Total Tokens:", totalTokens);
|
|
16676
16834
|
printDivider("=");
|
|
16677
16835
|
const costLabel = receipt.ai.mode === "verified" ? "VERIFIED AI COST" : "ESTIMATED AI COST";
|
|
16678
|
-
printRowSplit(
|
|
16679
|
-
|
|
16680
|
-
import_picocolors.default.bold(coral(costPrefix + receipt.ai.cost.toFixed(2)))
|
|
16681
|
-
);
|
|
16682
|
-
printRowSplit(
|
|
16683
|
-
`CONFIDENCE: ${confidencePercent}`,
|
|
16684
|
-
`[Mode: ${receipt.ai.mode}]`
|
|
16685
|
-
);
|
|
16836
|
+
printRowSplit(costLabel, import_picocolors.default.bold(coral(costPrefix + receipt.ai.cost.toFixed(2))));
|
|
16837
|
+
printRowSplit(`CONFIDENCE: ${confidencePercent}`, `[Mode: ${receipt.ai.mode}]`);
|
|
16686
16838
|
printDivider("=");
|
|
16687
16839
|
printRow("");
|
|
16688
16840
|
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || process.env.QODEWK_APP_URL || "https://qodewk.flinkeo.online";
|
|
16689
16841
|
const isPublished = Boolean(publicUrl);
|
|
16690
|
-
const urlToDisplay = publicUrl || `${baseUrl}/r/${receipt.receipt.id}`;
|
|
16691
16842
|
const displayHost = baseUrl.replace(/^https?:\/\//, "");
|
|
16692
16843
|
if (useBarcode) {
|
|
16693
16844
|
const barChars = ["||| ", "| | ", "|||| ", "|| | ", "|| || "];
|
|
@@ -16698,10 +16849,10 @@ async function renderTerminalReceipt(receipt, publicUrl, useBarcode) {
|
|
|
16698
16849
|
}
|
|
16699
16850
|
barcodePattern += "|||";
|
|
16700
16851
|
printCenteredRow(import_picocolors.default.bold(barcodePattern));
|
|
16701
|
-
} else {
|
|
16702
|
-
const qrRows = renderTerminalQr(
|
|
16852
|
+
} else if (isPublished && publicUrl) {
|
|
16853
|
+
const qrRows = renderTerminalQr(publicUrl);
|
|
16703
16854
|
if (qrRows.length > 0) {
|
|
16704
|
-
printCenteredRow(import_picocolors.default.dim(
|
|
16855
|
+
printCenteredRow(import_picocolors.default.dim("--- SCAN PUBLIC RECEIPT ---"));
|
|
16705
16856
|
printRow("");
|
|
16706
16857
|
for (const qrRow of qrRows) {
|
|
16707
16858
|
printCenteredRow(qrRow);
|
|
@@ -16710,6 +16861,9 @@ async function renderTerminalReceipt(receipt, publicUrl, useBarcode) {
|
|
|
16710
16861
|
} else {
|
|
16711
16862
|
printCenteredRow(import_picocolors.default.bold("||| | ||||| ||| |||| |||||| |||| ||| ||||||| |||"));
|
|
16712
16863
|
}
|
|
16864
|
+
} else {
|
|
16865
|
+
printCenteredRow(import_picocolors.default.dim("--- LOCAL PROOF (NOT PUBLISHED) ---"));
|
|
16866
|
+
printCenteredRow(import_picocolors.default.dim(`id ${receipt.receipt.id.slice(0, 28)}`));
|
|
16713
16867
|
}
|
|
16714
16868
|
if (isPublished) {
|
|
16715
16869
|
printCenteredRow(import_picocolors.default.cyan(displayHost));
|
|
@@ -16722,17 +16876,45 @@ async function renderTerminalReceipt(receipt, publicUrl, useBarcode) {
|
|
|
16722
16876
|
printRow(` ${teal("[\u2713]")} QODEWK_TELEMETRY=off (Cloud sync disabled)`);
|
|
16723
16877
|
}
|
|
16724
16878
|
printRow(` ${green("[\u2713]")} Source code was never uploaded to Qodewk`);
|
|
16725
|
-
|
|
16726
|
-
|
|
16727
|
-
if (isPublished) {
|
|
16728
|
-
|
|
16879
|
+
push(" " + import_picocolors.default.bold(coral("\\/".repeat(28))));
|
|
16880
|
+
push("");
|
|
16881
|
+
if (isPublished && publicUrl) {
|
|
16882
|
+
push(` ${import_picocolors.default.dim("Public Receipt:")} ${import_picocolors.default.underline(import_picocolors.default.cyan(publicUrl))}`);
|
|
16729
16883
|
} else {
|
|
16730
|
-
|
|
16731
|
-
|
|
16884
|
+
push(` ${import_picocolors.default.dim("Saved to local DB:")} ${import_picocolors.default.dim("~/.qodewk/state.db")}`);
|
|
16885
|
+
push(` ${import_picocolors.default.dim("To publish & get shareable URL:")} ${import_picocolors.default.cyan("qodewk share")}`);
|
|
16732
16886
|
}
|
|
16733
16887
|
if (!useBarcode) {
|
|
16734
|
-
|
|
16888
|
+
push(` ${import_picocolors.default.dim("View classic 1D barcode:")} ${import_picocolors.default.cyan("qodewk --barcode")}`);
|
|
16889
|
+
}
|
|
16890
|
+
push("");
|
|
16891
|
+
return lines.join("\n");
|
|
16892
|
+
}
|
|
16893
|
+
async function renderTerminalReceipt(receipt, publicUrl, useBarcode) {
|
|
16894
|
+
console.log(buildTerminalReceipt(receipt, publicUrl, useBarcode));
|
|
16895
|
+
}
|
|
16896
|
+
async function outputReceipt(receipt, options, publicUrl) {
|
|
16897
|
+
const format = options.json ? "json" : options.format || "terminal";
|
|
16898
|
+
let content;
|
|
16899
|
+
if (format === "json") {
|
|
16900
|
+
content = JSON.stringify(receipt, null, 2);
|
|
16901
|
+
} else if (format === "markdown") {
|
|
16902
|
+
content = formatMarkdownReceipt(receipt, publicUrl);
|
|
16903
|
+
} else {
|
|
16904
|
+
content = buildTerminalReceipt(receipt, publicUrl, options.barcode);
|
|
16905
|
+
}
|
|
16906
|
+
if (options.out) {
|
|
16907
|
+
const fileContent = format === "terminal" ? stripAnsi(content) : content;
|
|
16908
|
+
fs6.writeFileSync(options.out, fileContent.endsWith("\n") ? fileContent : fileContent + "\n", "utf-8");
|
|
16909
|
+
console.log(import_picocolors.default.green(`\u2713 Receipt written to ${options.out}`));
|
|
16910
|
+
}
|
|
16911
|
+
if (!options.out) {
|
|
16912
|
+
if (format === "terminal") {
|
|
16913
|
+
console.log(content);
|
|
16914
|
+
} else {
|
|
16915
|
+
console.log(content);
|
|
16916
|
+
}
|
|
16917
|
+
} else if (format !== "terminal") {
|
|
16735
16918
|
}
|
|
16736
|
-
console.log("");
|
|
16737
16919
|
}
|
|
16738
16920
|
program2.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qodewk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Universal telemetry and digital receipt generator for the AI coding agent era",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsup",
|
|
16
16
|
"typecheck": "tsc --noEmit",
|
|
17
|
+
"test": "node --test test/smoke.test.mjs",
|
|
17
18
|
"prepublishOnly": "pnpm run build"
|
|
18
19
|
},
|
|
19
20
|
"publishConfig": {
|
|
@@ -43,8 +44,7 @@
|
|
|
43
44
|
"dependencies": {
|
|
44
45
|
"commander": "^13.1.0",
|
|
45
46
|
"picocolors": "^1.1.1",
|
|
46
|
-
"qrcode": "^1.5.4"
|
|
47
|
-
"simple-git": "^3.27.0"
|
|
47
|
+
"qrcode": "^1.5.4"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@qodewk/core": "workspace:*",
|