@tokenwarden/opencode 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.
Files changed (31) hide show
  1. package/README.md +53 -0
  2. package/dist/src/cli/index.js +105 -0
  3. package/dist/src/core/billing.js +252 -0
  4. package/dist/src/core/build-config.js +45 -0
  5. package/dist/src/core/code-summary.js +162 -0
  6. package/dist/src/core/format.js +71 -0
  7. package/dist/src/core/log-reducer.js +97 -0
  8. package/dist/src/core/protection/engine.js +226 -0
  9. package/dist/src/core/protection/license-public-key.generated.js +2 -0
  10. package/dist/src/core/protection/license.js +27 -0
  11. package/dist/src/core/protection/rule-pack.js +38 -0
  12. package/dist/src/core/protection/sidecar-public-key.generated.js +2 -0
  13. package/dist/src/core/protection/sidecar.js +197 -0
  14. package/dist/src/core/protection/signing.js +28 -0
  15. package/dist/src/core/protection/tamper.js +18 -0
  16. package/dist/src/core/savings.js +91 -0
  17. package/dist/src/core/storage.js +123 -0
  18. package/dist/src/core/tokens.js +40 -0
  19. package/dist/src/plugin/index.js +492 -0
  20. package/native/bin/tokenwarden-build-config.json +4 -0
  21. package/native/bin/tokenwarden-engine-darwin-arm64 +0 -0
  22. package/native/bin/tokenwarden-engine-darwin-arm64.manifest.json +17 -0
  23. package/native/bin/tokenwarden-engine-darwin-x64 +0 -0
  24. package/native/bin/tokenwarden-engine-darwin-x64.manifest.json +17 -0
  25. package/native/bin/tokenwarden-engine-linux-arm64 +0 -0
  26. package/native/bin/tokenwarden-engine-linux-arm64.manifest.json +17 -0
  27. package/native/bin/tokenwarden-engine-linux-x64 +0 -0
  28. package/native/bin/tokenwarden-engine-linux-x64.manifest.json +17 -0
  29. package/native/bin/tokenwarden-engine-win32-x64.exe +0 -0
  30. package/native/bin/tokenwarden-engine-win32-x64.exe.manifest.json +17 -0
  31. package/package.json +33 -0
@@ -0,0 +1,71 @@
1
+ const ANSI = {
2
+ reset: "\x1b[0m",
3
+ bold: "\x1b[1m",
4
+ dim: "\x1b[2m",
5
+ red: "\x1b[31m",
6
+ green: "\x1b[32m",
7
+ cyan: "\x1b[36m",
8
+ };
9
+ export function formatTokenReport(summary, options = {}) {
10
+ const color = options.color ?? true;
11
+ const c = (name, text) => (color ? `${ANSI[name]}${text}${ANSI.reset}` : text);
12
+ const percent = `${formatPercent(summary.percentSaved)}%`;
13
+ const lines = [
14
+ c("bold", "TokenWarden observed optimized-context savings"),
15
+ "Scope: recorded optimization events only; this is not a full provider bill report.",
16
+ `Sessions counted: ${formatNumber(summary.sessions)}`,
17
+ `Events counted: ${formatNumber(summary.events)}`,
18
+ `${c("red", "Observed would-have-used")}: ${formatNumber(summary.wouldHaveUsedTokens)} tokens`,
19
+ `Observed actually used: ${formatNumber(summary.usedTokens)} tokens`,
20
+ `${c("green", "Observed saved")}: ${formatNumber(summary.savedTokens)} tokens`,
21
+ `${c("green", "Observed percent saved")}: ${percent}`,
22
+ ];
23
+ const sources = Object.entries(summary.bySource).sort(([, a], [, b]) => b.savedTokens - a.savedTokens);
24
+ if (sources.length > 0) {
25
+ lines.push("", c("cyan", "Observed savings by source"));
26
+ for (const [source, item] of sources) {
27
+ lines.push(`- ${source}: ${formatNumber(item.savedTokens)} saved / ${formatNumber(item.wouldHaveUsedTokens)} would-have-used (saved ${formatPercent(item.percentSaved)}%)`);
28
+ }
29
+ }
30
+ else {
31
+ lines.push("", c("dim", "No optimization savings recorded yet. Normal prompts may have no TokenWarden event."));
32
+ }
33
+ return lines.join("\n");
34
+ }
35
+ export function formatNumber(value) {
36
+ return new Intl.NumberFormat("en-US").format(safeRoundedNumber(value));
37
+ }
38
+ export function formatCompactNumber(value) {
39
+ const rounded = safeRoundedNumber(value);
40
+ const abs = Math.abs(rounded);
41
+ if (abs >= 1_000_000)
42
+ return `${trimFixed(rounded / 1_000_000)}M`;
43
+ if (abs >= 1_000)
44
+ return `${trimFixed(rounded / 1_000)}k`;
45
+ return String(rounded);
46
+ }
47
+ export function formatCompactSavingsToast(summary, entitlement) {
48
+ if (entitlement?.monthlyCapTokens !== null && entitlement?.monthlyCapTokens !== undefined) {
49
+ const credited = Math.min(entitlement.monthlySavedTokens, entitlement.monthlyCapTokens);
50
+ const capReached = (entitlement.remainingFreeTokens ?? 0) <= 0;
51
+ return {
52
+ title: capReached ? "TokenWarden: free cap reached" : "TokenWarden: free seat",
53
+ message: `${formatCompactNumber(credited)}/${formatCompactNumber(entitlement.monthlyCapTokens)} credited, observed ${formatCompactNumber(summary.savedTokens)}`,
54
+ variant: capReached ? "warning" : summary.savedTokens > 0 ? "success" : "info",
55
+ };
56
+ }
57
+ return {
58
+ title: `TokenWarden observed ${formatPercent(summary.percentSaved)}% saved`,
59
+ message: `${formatCompactNumber(summary.wouldHaveUsedTokens)} -> ${formatCompactNumber(summary.usedTokens)}, saved ${formatCompactNumber(summary.savedTokens)}`,
60
+ variant: summary.savedTokens > 0 ? "success" : "info",
61
+ };
62
+ }
63
+ function formatPercent(value) {
64
+ return (Number.isFinite(value) ? value : 0).toFixed(1);
65
+ }
66
+ function safeRoundedNumber(value) {
67
+ return Number.isFinite(value) ? Math.round(value) : 0;
68
+ }
69
+ function trimFixed(value) {
70
+ return value.toFixed(value >= 100 ? 0 : 1).replace(/\.0$/, "");
71
+ }
@@ -0,0 +1,97 @@
1
+ import { countTokens, trimToTokenBudget } from "./tokens.js";
2
+ import { createSavingsEvent } from "./savings.js";
3
+ import { writeRawOutput } from "./storage.js";
4
+ const IMPORTANT_LINE = /(error|failed|failure|exception|traceback|panic|fatal|warning|\bTS\d{4}\b|\bERR!\b|\b[A-Za-z0-9_./-]+\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|swift|kt):\d+)/i;
5
+ export async function reduceToolOutput(input) {
6
+ const rawTokens = countTokens(input.output);
7
+ if (rawTokens <= input.maxTokens) {
8
+ const event = createSavingsEvent({
9
+ sessionID: input.sessionID,
10
+ source: "tool-output",
11
+ label: input.tool,
12
+ wouldHaveUsedTokens: rawTokens,
13
+ usedTokens: rawTokens,
14
+ metadata: { callID: input.callID, reduced: false },
15
+ });
16
+ return { output: input.output, rawTokens, usedTokens: rawTokens, savedTokens: 0, event };
17
+ }
18
+ const rawPath = await writeRawOutput(input.store, {
19
+ sessionID: input.sessionID,
20
+ callID: input.callID,
21
+ output: input.output,
22
+ });
23
+ const relevant = extractRelevantLines(input.output);
24
+ const command = commandFromArgs(input.args);
25
+ const summary = [
26
+ "TokenWarden reduced large tool output.",
27
+ command ? `Command: ${command}` : undefined,
28
+ `Original: ${rawTokens} estimated tokens`,
29
+ `Full raw log: ${rawPath}`,
30
+ "",
31
+ "Relevant output:",
32
+ relevant || "No obvious error lines found. Showing the beginning and end of the output.",
33
+ relevant ? undefined : fallbackHeadTail(input.output),
34
+ ]
35
+ .filter(Boolean)
36
+ .join("\n");
37
+ const output = trimToTokenBudget(summary, input.maxTokens);
38
+ const usedTokens = countTokens(output);
39
+ const event = createSavingsEvent({
40
+ sessionID: input.sessionID,
41
+ source: "tool-output",
42
+ label: input.tool,
43
+ wouldHaveUsedTokens: rawTokens,
44
+ usedTokens,
45
+ metadata: { callID: input.callID, rawPath, reduced: true },
46
+ });
47
+ return { output, rawTokens, usedTokens, savedTokens: event.savedTokens, rawPath, event };
48
+ }
49
+ export function reducePlainTextForContext(input) {
50
+ const rawTokens = countTokens(input.text);
51
+ if (rawTokens <= input.maxTokens)
52
+ return undefined;
53
+ const reduced = trimToTokenBudget([
54
+ "TokenWarden reduced stale large context before the model call.",
55
+ `Original: ${rawTokens} estimated tokens`,
56
+ "",
57
+ extractRelevantLines(input.text) || fallbackHeadTail(input.text),
58
+ ].join("\n"), input.maxTokens);
59
+ const event = createSavingsEvent({
60
+ sessionID: input.sessionID,
61
+ source: "chat-history",
62
+ label: input.label,
63
+ wouldHaveUsedTokens: rawTokens,
64
+ usedTokens: countTokens(reduced),
65
+ metadata: { reduced: true },
66
+ });
67
+ return { text: reduced, event };
68
+ }
69
+ function extractRelevantLines(output) {
70
+ const lines = output.split(/\r?\n/);
71
+ const keep = new Set();
72
+ lines.forEach((line, index) => {
73
+ if (!IMPORTANT_LINE.test(line))
74
+ return;
75
+ keep.add(index - 1);
76
+ keep.add(index);
77
+ keep.add(index + 1);
78
+ });
79
+ return [...keep]
80
+ .filter((index) => index >= 0 && index < lines.length)
81
+ .sort((a, b) => a - b)
82
+ .map((index) => `${index + 1}: ${lines[index]}`)
83
+ .join("\n");
84
+ }
85
+ function fallbackHeadTail(output) {
86
+ const lines = output.split(/\r?\n/);
87
+ const head = lines.slice(0, 20);
88
+ const tail = lines.length > 40 ? lines.slice(-20) : [];
89
+ return [...head, ...(tail.length > 0 ? ["...", ...tail] : [])].join("\n");
90
+ }
91
+ function commandFromArgs(args) {
92
+ if (!args || typeof args !== "object")
93
+ return undefined;
94
+ const record = args;
95
+ const command = record.command ?? record.cmd;
96
+ return typeof command === "string" ? command : undefined;
97
+ }
@@ -0,0 +1,226 @@
1
+ import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
2
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import { smartPack, smartRead } from "../code-summary.js";
4
+ import { reduceToolOutput } from "../log-reducer.js";
5
+ import { createSavingsEvent } from "../savings.js";
6
+ import { appendSavingsEvent, createStore, summarizeStore, writeRawOutput } from "../storage.js";
7
+ import { activateHostedLicense, applySavingsCap, connectBillingClient, disconnectBillingClient, getBillingEntitlement } from "../billing.js";
8
+ import { defaultSidecarPaths, requestSidecarReduceToolOutput, requestSidecarSmartPack, requestSidecarSmartRead, requestSidecarStatus, } from "./sidecar.js";
9
+ export async function createProtectedEngine(input = {}) {
10
+ if (input.preferNativeSidecar === false)
11
+ return createDevelopmentEngine({ dataDir: input.dataDir });
12
+ try {
13
+ const status = await requestSidecarStatus(input.sidecarPaths ?? defaultSidecarPaths());
14
+ return createSidecarBackedEngine({ dataDir: input.dataDir, sidecarPaths: input.sidecarPaths ?? defaultSidecarPaths(), status });
15
+ }
16
+ catch {
17
+ return createDevelopmentEngine({
18
+ dataDir: input.dataDir,
19
+ protectionStatus: {
20
+ mode: "development-typescript",
21
+ tamper: "failed",
22
+ },
23
+ });
24
+ }
25
+ }
26
+ export function createSidecarBackedEngine(input) {
27
+ const store = createStore(input.dataDir);
28
+ return createEngineFromStore(store, {
29
+ mode: input.status.mode,
30
+ license: "not-configured",
31
+ rulePack: "development",
32
+ tamper: "passed",
33
+ }, input.sidecarPaths, input.status.freeMonthlySavedTokenCap);
34
+ }
35
+ export function createDevelopmentEngine(input = {}) {
36
+ const store = createStore(input.dataDir);
37
+ const status = {
38
+ mode: "development-typescript",
39
+ license: "not-configured",
40
+ rulePack: "development",
41
+ tamper: "not-checked",
42
+ ...input.protectionStatus,
43
+ };
44
+ return createEngineFromStore(store, status);
45
+ }
46
+ export function createEngineFromStore(store, status, sidecarPaths, freeCapTokens) {
47
+ const monthlyFreeCapTokens = freeCapTokens;
48
+ return {
49
+ async protectionStatus() {
50
+ return status;
51
+ },
52
+ async smartRead(input) {
53
+ if (!sidecarPaths)
54
+ return smartRead(input);
55
+ try {
56
+ const entitlement = await getBillingEntitlement(store, new Date(), monthlyFreeCapTokens);
57
+ const absPath = resolveInsideRoot(input.root, input.path);
58
+ const outputPath = sidecarOutputPath(store, input.sessionID, `smart-read-${Date.now()}.txt`);
59
+ await mkdir(dirname(outputPath), { recursive: true });
60
+ const result = await requestSidecarSmartRead({
61
+ inputPath: absPath,
62
+ outputPath,
63
+ query: input.query,
64
+ maxTokens: input.budget ?? 1600,
65
+ paths: sidecarPaths,
66
+ enforceFreeCap: entitlement.monthlyCapTokens !== null,
67
+ monthlySavedTokens: entitlement.monthlySavedTokens,
68
+ });
69
+ const output = await readFile(outputPath, "utf8");
70
+ await rm(outputPath, { force: true });
71
+ const relPath = relative(input.root, absPath) || input.path;
72
+ const event = createSavingsEvent({
73
+ sessionID: input.sessionID,
74
+ source: "smart-read",
75
+ label: relPath,
76
+ wouldHaveUsedTokens: result.rawTokens,
77
+ usedTokens: result.usedTokens,
78
+ metadata: nativeSavingsMetadata({ mode: input.mode ?? "symbols", query: input.query, reducedBy: "native-sidecar" }, result),
79
+ });
80
+ return { output, rawTokens: result.rawTokens, usedTokens: result.usedTokens, event };
81
+ }
82
+ catch {
83
+ return smartRead(input);
84
+ }
85
+ },
86
+ async smartPack(input) {
87
+ if (!sidecarPaths || input.paths.some(hasGlobSyntax))
88
+ return smartPack(input);
89
+ try {
90
+ const entitlement = await getBillingEntitlement(store, new Date(), monthlyFreeCapTokens);
91
+ const files = [];
92
+ for (const item of input.paths) {
93
+ const absPath = resolveInsideRoot(input.root, item);
94
+ if (!(await stat(absPath)).isFile())
95
+ return smartPack(input);
96
+ files.push(absPath);
97
+ }
98
+ const manifestPath = sidecarOutputPath(store, input.sessionID, `smart-pack-${Date.now()}.manifest.txt`);
99
+ const outputPath = sidecarOutputPath(store, input.sessionID, `smart-pack-${Date.now()}.txt`);
100
+ await mkdir(dirname(manifestPath), { recursive: true });
101
+ await writeFile(manifestPath, `${files.join("\n")}\n`, "utf8");
102
+ const result = await requestSidecarSmartPack({
103
+ manifestPath,
104
+ outputPath,
105
+ query: input.query,
106
+ maxTokens: input.budget ?? 4000,
107
+ paths: sidecarPaths,
108
+ enforceFreeCap: entitlement.monthlyCapTokens !== null,
109
+ monthlySavedTokens: entitlement.monthlySavedTokens,
110
+ });
111
+ const output = await readFile(outputPath, "utf8");
112
+ await rm(outputPath, { force: true });
113
+ await rm(manifestPath, { force: true });
114
+ const event = createSavingsEvent({
115
+ sessionID: input.sessionID,
116
+ source: "smart-pack",
117
+ label: input.paths.join(", "),
118
+ wouldHaveUsedTokens: result.rawTokens,
119
+ usedTokens: result.usedTokens,
120
+ metadata: nativeSavingsMetadata({ query: input.query, files: files.length, reducedBy: "native-sidecar" }, result),
121
+ });
122
+ return { output, rawTokens: result.rawTokens, usedTokens: result.usedTokens, event };
123
+ }
124
+ catch {
125
+ return smartPack(input);
126
+ }
127
+ },
128
+ async reduceToolOutput(input) {
129
+ const entitlement = await getBillingEntitlement(store, new Date(), monthlyFreeCapTokens);
130
+ if (!sidecarPaths && !entitlement.canOptimize) {
131
+ const event = createSavingsEvent({
132
+ sessionID: input.sessionID,
133
+ source: "tool-output",
134
+ label: `${input.tool} (free cap reached)`,
135
+ wouldHaveUsedTokens: 0,
136
+ usedTokens: 0,
137
+ metadata: { capped: true },
138
+ });
139
+ return { output: input.output, rawTokens: 0, usedTokens: 0, savedTokens: 0, event };
140
+ }
141
+ if (!sidecarPaths)
142
+ return reduceToolOutput({ store, ...input });
143
+ try {
144
+ const rawPath = await writeRawOutput(store, {
145
+ sessionID: input.sessionID,
146
+ callID: input.callID,
147
+ output: input.output,
148
+ });
149
+ const reducedPath = `${rawPath}.reduced.txt`;
150
+ const reduced = await requestSidecarReduceToolOutput({
151
+ inputPath: rawPath,
152
+ outputPath: reducedPath,
153
+ maxTokens: input.maxTokens,
154
+ paths: sidecarPaths,
155
+ enforceFreeCap: entitlement.monthlyCapTokens !== null,
156
+ monthlySavedTokens: entitlement.monthlySavedTokens,
157
+ });
158
+ const output = await readFile(reducedPath, "utf8");
159
+ await rm(reducedPath, { force: true });
160
+ const event = await applySavingsCap(store, createSavingsEvent({
161
+ sessionID: input.sessionID,
162
+ source: "tool-output",
163
+ label: input.tool,
164
+ wouldHaveUsedTokens: reduced.rawTokens,
165
+ usedTokens: reduced.usedTokens,
166
+ metadata: nativeSavingsMetadata({ callID: input.callID, rawPath, reducedBy: "native-sidecar" }, reduced),
167
+ }), monthlyFreeCapTokens);
168
+ return {
169
+ output,
170
+ rawTokens: reduced.rawTokens,
171
+ usedTokens: reduced.usedTokens,
172
+ savedTokens: event.savedTokens,
173
+ rawPath,
174
+ event,
175
+ };
176
+ }
177
+ catch {
178
+ return reduceToolOutput({ store, ...input });
179
+ }
180
+ },
181
+ async recordSavings(event) {
182
+ await appendSavingsEvent(store, await applySavingsCap(store, event, monthlyFreeCapTokens));
183
+ },
184
+ summarize(sessionID) {
185
+ return summarizeStore(store, sessionID ? { sessionID } : {});
186
+ },
187
+ billing() {
188
+ return getBillingEntitlement(store, new Date(), monthlyFreeCapTokens);
189
+ },
190
+ connect(input) {
191
+ return connectBillingClient(store, input);
192
+ },
193
+ disconnect() {
194
+ return disconnectBillingClient(store);
195
+ },
196
+ activate(input) {
197
+ return activateHostedLicense(store, input);
198
+ },
199
+ };
200
+ }
201
+ function sidecarOutputPath(store, sessionID, filename) {
202
+ return join(store.rawOutputDir, safePathPart(sessionID), filename);
203
+ }
204
+ function safePathPart(value) {
205
+ return value.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 120) || "unknown";
206
+ }
207
+ function hasGlobSyntax(value) {
208
+ return /[*?\[\]{}]/.test(value);
209
+ }
210
+ function nativeSavingsMetadata(metadata, result) {
211
+ return {
212
+ ...metadata,
213
+ capCheckedBy: result.capChecked ? "native-sidecar" : undefined,
214
+ freeMonthlyCapTokens: result.freeCapTokens,
215
+ capped: result.capReached || undefined,
216
+ };
217
+ }
218
+ function resolveInsideRoot(root, requestedPath) {
219
+ const absRoot = resolve(root);
220
+ const absPath = isAbsolute(requestedPath) ? resolve(requestedPath) : resolve(absRoot, requestedPath);
221
+ const rel = relative(absRoot, absPath);
222
+ if (rel.startsWith("..") || rel === ".." || rel.includes(`..${sep}`)) {
223
+ throw new Error(`Path is outside project root: ${requestedPath}`);
224
+ }
225
+ return absPath;
226
+ }
@@ -0,0 +1,2 @@
1
+ // Generated from the hosted license signing key. Do not edit by hand.
2
+ export const LICENSE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAR+Neoy7qucqnaSWEH2foZ710rZL+GcRUNmnUXDEbTxc=\n-----END PUBLIC KEY-----\n";
@@ -0,0 +1,27 @@
1
+ import { createHash } from "node:crypto";
2
+ import { hostname, userInfo } from "node:os";
3
+ import { verifyEnvelope } from "./signing.js";
4
+ export function verifyLicense(input) {
5
+ if (!verifyEnvelope(input.token, input.publicKeyPem)) {
6
+ return { ok: false, reason: "license signature is invalid", payload: input.token.payload };
7
+ }
8
+ const payload = input.token.payload;
9
+ const machineHash = input.machineHash ?? getMachineHash();
10
+ if (payload.machineHash !== machineHash) {
11
+ return { ok: false, reason: "license is for a different machine", payload };
12
+ }
13
+ if (payload.plan !== "Standard") {
14
+ return { ok: false, reason: "license plan is not Standard", payload };
15
+ }
16
+ const now = input.now ?? new Date();
17
+ const graceUntil = new Date(payload.offlineGraceUntil);
18
+ if (Number.isNaN(graceUntil.getTime()) || graceUntil < now) {
19
+ return { ok: false, reason: "offline grace period expired", payload };
20
+ }
21
+ if (payload.status === "active")
22
+ return { ok: true, payload, offline: true };
23
+ return { ok: false, reason: `license status is ${payload.status}`, payload };
24
+ }
25
+ export function getMachineHash(seed = `${hostname()}:${userInfo().username}`) {
26
+ return createHash("sha256").update(seed).digest("hex");
27
+ }
@@ -0,0 +1,38 @@
1
+ import { verifyEnvelope } from "./signing.js";
2
+ export function verifyRulePack(input) {
3
+ if (!verifyEnvelope(input.envelope, input.publicKeyPem)) {
4
+ return { ok: false, reason: "rule pack signature is invalid", rulePack: input.envelope.payload };
5
+ }
6
+ const rulePack = input.envelope.payload;
7
+ if (!rulePack.id || !rulePack.version) {
8
+ return { ok: false, reason: "rule pack is missing id or version", rulePack };
9
+ }
10
+ if (rulePack.expiresAt) {
11
+ const expiresAt = new Date(rulePack.expiresAt);
12
+ if (Number.isNaN(expiresAt.getTime()))
13
+ return { ok: false, reason: "rule pack expiry is invalid", rulePack };
14
+ if (expiresAt < (input.now ?? new Date()))
15
+ return { ok: false, reason: "rule pack expired", rulePack };
16
+ }
17
+ return { ok: true, rulePack };
18
+ }
19
+ export const developmentRulePack = {
20
+ id: "tokenwarden-development",
21
+ version: "0.1.0",
22
+ issuedAt: "2026-06-06T00:00:00.000Z",
23
+ modelPricing: {},
24
+ reducerPatterns: [
25
+ "error",
26
+ "failed",
27
+ "exception",
28
+ "traceback",
29
+ "panic",
30
+ "fatal",
31
+ "warning",
32
+ "TS\\d{4}",
33
+ ],
34
+ smartPack: {
35
+ maxFileBytes: 512000,
36
+ defaultBudgetTokens: 4000,
37
+ },
38
+ };
@@ -0,0 +1,2 @@
1
+ // Generated by scripts/build-sidecar.mjs. Do not edit by hand.
2
+ export const SIDECAR_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAmoM1wIcANnC/UDZx1jhxRu75SCtpFrFNWi5WNxEfot0=\n-----END PUBLIC KEY-----\n";
@@ -0,0 +1,197 @@
1
+ import { spawn } from "node:child_process";
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { verifyEnvelope } from "./signing.js";
6
+ import { verifyFileChecksum } from "./tamper.js";
7
+ import { SIDECAR_PUBLIC_KEY } from "./sidecar-public-key.generated.js";
8
+ import { tokenWardenSiteURL } from "../build-config.js";
9
+ export function defaultSidecarPaths(root = packageRoot()) {
10
+ const binaryPath = resolve(root, "native/bin", sidecarBinaryName());
11
+ return {
12
+ binaryPath,
13
+ manifestPath: `${binaryPath}.manifest.json`,
14
+ };
15
+ }
16
+ export function sidecarBinaryName(platform = process.platform, arch = process.arch) {
17
+ return `tokenwarden-engine-${platform}-${arch}${platform === "win32" ? ".exe" : ""}`;
18
+ }
19
+ function packageRoot() {
20
+ return fileURLToPath(new URL("../../../../", import.meta.url));
21
+ }
22
+ export async function verifySidecar(paths = defaultSidecarPaths()) {
23
+ let envelope;
24
+ try {
25
+ envelope = JSON.parse(await readFile(paths.manifestPath, "utf8"));
26
+ }
27
+ catch (error) {
28
+ return { ok: false, reason: `could not read sidecar manifest: ${error.message}` };
29
+ }
30
+ if (!verifyEnvelope(envelope, SIDECAR_PUBLIC_KEY)) {
31
+ return { ok: false, reason: "sidecar manifest signature is invalid", manifest: envelope.payload };
32
+ }
33
+ if (envelope.payload.platform !== process.platform || envelope.payload.arch !== process.arch) {
34
+ return { ok: false, reason: "sidecar manifest is for a different platform", manifest: envelope.payload };
35
+ }
36
+ const checksum = await verifyFileChecksum(paths.binaryPath, envelope.payload.sha256);
37
+ if (!checksum.ok)
38
+ return { ok: false, reason: `sidecar binary verification failed: ${checksum.reason}`, manifest: envelope.payload };
39
+ return { ok: true, manifest: envelope.payload };
40
+ }
41
+ export async function requestSidecarStatus(paths = defaultSidecarPaths()) {
42
+ const verification = await verifySidecar(paths);
43
+ if (!verification.ok)
44
+ throw new Error(verification.reason);
45
+ const response = await requestSidecar(paths.binaryPath, { id: 1, method: "status" });
46
+ if (!response.ok)
47
+ throw new Error(String(response.error ?? "sidecar status request failed"));
48
+ const result = response.result;
49
+ if (result.mode !== "native-sidecar" ||
50
+ typeof result.version !== "string" ||
51
+ typeof result.protocol !== "number" ||
52
+ typeof result.freeMonthlySavedTokenCap !== "number" ||
53
+ typeof result.siteURL !== "string") {
54
+ throw new Error("sidecar returned invalid status payload");
55
+ }
56
+ return result;
57
+ }
58
+ export async function requestSidecarReduceToolOutput(input) {
59
+ const paths = input.paths ?? defaultSidecarPaths();
60
+ const verification = await verifySidecar(paths);
61
+ if (!verification.ok)
62
+ throw new Error(verification.reason);
63
+ const response = await requestSidecar(paths.binaryPath, {
64
+ id: 1,
65
+ method: "reduce_tool_output",
66
+ inputPath: input.inputPath,
67
+ outputPath: input.outputPath,
68
+ maxTokens: input.maxTokens,
69
+ enforceFreeCap: input.enforceFreeCap ?? false,
70
+ monthlySavedTokens: input.monthlySavedTokens ?? 0,
71
+ });
72
+ if (!response.ok)
73
+ throw new Error(String(response.error ?? "sidecar reduce request failed"));
74
+ const result = response.result;
75
+ if (!isValidTokenCount(result.rawTokens) || !isValidTokenCount(result.usedTokens)) {
76
+ throw new Error("sidecar returned invalid reduce payload");
77
+ }
78
+ return result;
79
+ }
80
+ export async function requestSidecarSmartRead(input) {
81
+ const paths = input.paths ?? defaultSidecarPaths();
82
+ const verification = await verifySidecar(paths);
83
+ if (!verification.ok)
84
+ throw new Error(verification.reason);
85
+ const response = await requestSidecar(paths.binaryPath, {
86
+ id: 1,
87
+ method: "smart_read",
88
+ inputPath: input.inputPath,
89
+ outputPath: input.outputPath,
90
+ query: input.query ?? "",
91
+ maxTokens: input.maxTokens,
92
+ enforceFreeCap: input.enforceFreeCap ?? false,
93
+ monthlySavedTokens: input.monthlySavedTokens ?? 0,
94
+ });
95
+ if (!response.ok)
96
+ throw new Error(String(response.error ?? "sidecar smart_read request failed"));
97
+ return parseTokenResult(response.result, "smart_read");
98
+ }
99
+ export async function requestSidecarSmartPack(input) {
100
+ const paths = input.paths ?? defaultSidecarPaths();
101
+ const verification = await verifySidecar(paths);
102
+ if (!verification.ok)
103
+ throw new Error(verification.reason);
104
+ const response = await requestSidecar(paths.binaryPath, {
105
+ id: 1,
106
+ method: "smart_pack",
107
+ manifestPath: input.manifestPath,
108
+ outputPath: input.outputPath,
109
+ query: input.query ?? "",
110
+ maxTokens: input.maxTokens,
111
+ enforceFreeCap: input.enforceFreeCap ?? false,
112
+ monthlySavedTokens: input.monthlySavedTokens ?? 0,
113
+ });
114
+ if (!response.ok)
115
+ throw new Error(String(response.error ?? "sidecar smart_pack request failed"));
116
+ return parseTokenResult(response.result, "smart_pack");
117
+ }
118
+ export async function encryptedReadFile(path, paths = defaultSidecarPaths()) {
119
+ const response = await requestVerifiedSidecar(paths, { id: 1, method: "encrypted_read", path });
120
+ const result = response.result;
121
+ if (typeof result.found !== "boolean")
122
+ throw new Error("sidecar returned invalid encrypted_read payload");
123
+ if (!result.found)
124
+ return undefined;
125
+ if (typeof result.content !== "string")
126
+ throw new Error("sidecar returned invalid encrypted_read content");
127
+ return result.content;
128
+ }
129
+ export async function encryptedWriteFile(path, content, paths = defaultSidecarPaths()) {
130
+ await requestVerifiedSidecar(paths, { id: 1, method: "encrypted_write", path, content });
131
+ }
132
+ export async function encryptedAppendFile(path, content, paths = defaultSidecarPaths()) {
133
+ await requestVerifiedSidecar(paths, { id: 1, method: "encrypted_append", path, content });
134
+ }
135
+ export async function encryptedWriteFileFromPath(path, inputPath, paths = defaultSidecarPaths()) {
136
+ await requestVerifiedSidecar(paths, { id: 1, method: "encrypted_write_from_path", path, inputPath });
137
+ }
138
+ export async function encryptedDeleteFile(path, paths = defaultSidecarPaths()) {
139
+ await requestVerifiedSidecar(paths, { id: 1, method: "encrypted_delete", path });
140
+ }
141
+ function parseTokenResult(value, method) {
142
+ const result = value;
143
+ if (!isValidTokenCount(result.rawTokens) || !isValidTokenCount(result.usedTokens)) {
144
+ throw new Error(`sidecar returned invalid ${method} payload`);
145
+ }
146
+ return result;
147
+ }
148
+ function isValidTokenCount(value) {
149
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
150
+ }
151
+ async function requestVerifiedSidecar(paths, request) {
152
+ const verification = await verifySidecar(paths);
153
+ if (!verification.ok)
154
+ throw new Error(verification.reason);
155
+ const response = await requestSidecar(paths.binaryPath, request);
156
+ if (!response.ok)
157
+ throw new Error(String(response.error ?? "sidecar encrypted storage request failed"));
158
+ return response;
159
+ }
160
+ function requestSidecar(binaryPath, request) {
161
+ return new Promise((resolvePromise, reject) => {
162
+ const child = spawn(binaryPath, [], { stdio: ["pipe", "pipe", "pipe"], env: sidecarProcessEnv() });
163
+ let stdout = "";
164
+ let stderr = "";
165
+ child.stdout.setEncoding("utf8");
166
+ child.stderr.setEncoding("utf8");
167
+ child.stdout.on("data", (chunk) => {
168
+ stdout += chunk;
169
+ });
170
+ child.stderr.on("data", (chunk) => {
171
+ stderr += chunk;
172
+ });
173
+ child.on("error", reject);
174
+ child.on("close", (code) => {
175
+ const line = stdout.trim().split(/\r?\n/)[0];
176
+ if (!line) {
177
+ reject(new Error(stderr.trim() || `sidecar exited with code ${code}`));
178
+ return;
179
+ }
180
+ try {
181
+ resolvePromise(JSON.parse(line));
182
+ }
183
+ catch (error) {
184
+ reject(new Error(`sidecar returned invalid JSON: ${error.message}`));
185
+ }
186
+ });
187
+ child.stdin.end(`${JSON.stringify(request)}\n`);
188
+ });
189
+ }
190
+ export function sidecarProcessEnv() {
191
+ const siteURL = tokenWardenSiteURL();
192
+ return {
193
+ ...process.env,
194
+ TOKENWARDEN_SITE_URL: siteURL,
195
+ NEXT_PUBLIC_SITE_URL: siteURL,
196
+ };
197
+ }