tokenfold 0.4.1 → 0.5.1
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 +31 -15
- package/dist/index.d.ts +98 -95
- package/dist/index.js +106 -86
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,38 +1,54 @@
|
|
|
1
|
-
# tokenfold for Node.js
|
|
1
|
+
# tokenfold for Node.js
|
|
2
2
|
|
|
3
|
-
Zero-runtime-dependency TypeScript bindings for the
|
|
3
|
+
Zero-runtime-dependency TypeScript bindings for the Tokenfold Rust CLI.
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
6
|
npm install tokenfold
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
import { compress } from "tokenfold";
|
|
10
|
+
import { compress, decode, inspect } from "tokenfold";
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const receipt = await inspect(input, { format: "json", preset: "balanced" });
|
|
13
|
+
const { payload, report, text } = await compress(input, {
|
|
13
14
|
format: "json",
|
|
14
|
-
|
|
15
|
+
preset: "balanced",
|
|
15
16
|
});
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
Payloads are `Uint8Array`; the `text` convenience getter decodes UTF-8 strictly.
|
|
20
|
+
`inspect` returns only the side-effect-free receipt.
|
|
21
|
+
|
|
22
|
+
`parseReport(jsonOrBytes)` reads archived v1 and current v2 receipts. It preserves the
|
|
23
|
+
source schema version, normalizes v1 `mode` to `preset`, and leaves unavailable sections
|
|
24
|
+
as `null`. Unknown versions and invalid required top-level fields raise
|
|
25
|
+
`TokenFoldProcessError` with `code: "invalid_report"`; this is not recursive JSON Schema
|
|
26
|
+
validation. `compress` and `inspect` use the same versioned reader.
|
|
27
|
+
|
|
28
|
+
Recoverable pruning is explicit and generic-JSON-only:
|
|
21
29
|
|
|
22
30
|
```ts
|
|
23
31
|
import { compress, retrieve } from "tokenfold";
|
|
24
32
|
|
|
25
|
-
const
|
|
33
|
+
const result = await compress(feed, {
|
|
26
34
|
format: "json",
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
targetTokens: 2_000,
|
|
36
|
+
pruning: {
|
|
37
|
+
keepRatio: 0.35,
|
|
38
|
+
preservePaths: ["meta"],
|
|
39
|
+
retrievalStore: ".tokenfold/retrieve",
|
|
40
|
+
},
|
|
30
41
|
});
|
|
31
|
-
const
|
|
42
|
+
const marker = JSON.parse(result.text).items.find((item) => item.$tf_ref);
|
|
43
|
+
const original = await retrieve(marker, { retrievalStore: ".tokenfold/retrieve" });
|
|
32
44
|
```
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
Explicit TOON output is verified before emission and restored with `decode`:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const encoded = await compress(input, { format: "json", encoding: "toon" });
|
|
50
|
+
const jsonBytes = await decode(encoded.payload, { from: "toon" });
|
|
51
|
+
```
|
|
36
52
|
|
|
37
53
|
Requires Node.js 22 or newer. The matching native CLI is installed through an
|
|
38
54
|
optional platform package; set `TOKENFOLD_BINARY_PATH` to use a custom binary.
|
package/dist/index.d.ts
CHANGED
|
@@ -3,35 +3,23 @@ import { TokenFoldProcessError } from "./errors.js";
|
|
|
3
3
|
import { run, type Input, type ProcessResult, type RunOptions } from "./process.js";
|
|
4
4
|
export { binaryPath, run, TokenFoldProcessError };
|
|
5
5
|
export type { Input, ProcessResult, RunOptions };
|
|
6
|
-
export type
|
|
7
|
-
export type
|
|
8
|
-
|
|
9
|
-
export type
|
|
6
|
+
export type Preset = "conservative" | "balanced" | "aggressive";
|
|
7
|
+
export type OutputEncoding = "json" | "toon";
|
|
8
|
+
export type InputFormat = "auto" | "openai" | "anthropic" | "json" | "text" | "command" | "diff";
|
|
9
|
+
export type DecodeFormat = "auto" | "json" | "toon" | "text";
|
|
10
|
+
export interface PruningPolicy {
|
|
11
|
+
keepRatio?: number;
|
|
12
|
+
preservePaths?: readonly string[];
|
|
13
|
+
retrievalStore?: string;
|
|
14
|
+
retrievalNamespace?: string;
|
|
15
|
+
}
|
|
10
16
|
export interface CompressionOptions {
|
|
11
|
-
format?:
|
|
12
|
-
|
|
17
|
+
format?: InputFormat;
|
|
18
|
+
preset?: Preset;
|
|
13
19
|
targetTokens?: number;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
storeOriginals?: boolean;
|
|
18
|
-
retrieveNamespace?: string;
|
|
19
|
-
/**
|
|
20
|
-
* Opt-in LOSSY JSON array-item pruning (`--lossy`): drops whole array items to hit a token
|
|
21
|
-
* budget instead of only restructuring them. Generic JSON only — on any other format the run
|
|
22
|
-
* is a no-op and `json_prune` comes back as `status: "skipped"` with
|
|
23
|
-
* `skipped_reason: "not_applicable_to_format"`, persisting nothing. Dropped items are replaced
|
|
24
|
-
* by `$tf_ref` markers and stay recoverable via {@link retrieve}; pruning needs a durable
|
|
25
|
-
* filesystem retrieval store, so pass `configPath` when you want one other than the default.
|
|
26
|
-
*/
|
|
27
|
-
lossy?: LossyPath;
|
|
28
|
-
/**
|
|
29
|
-
* `--lossy-ratio`: a best-effort selection hint (0.0..=1.0), not an enforced budget — the
|
|
30
|
-
* achieved ratio differs by design. Use `targetTokens` for a real ceiling.
|
|
31
|
-
*/
|
|
32
|
-
lossyRatio?: number;
|
|
33
|
-
/** `--lossy-preserve`: dot-separated paths (e.g. `data.results`) whose arrays are never pruned. */
|
|
34
|
-
lossyPreserve?: readonly string[];
|
|
20
|
+
requireTarget?: boolean;
|
|
21
|
+
encoding?: OutputEncoding;
|
|
22
|
+
pruning?: PruningPolicy;
|
|
35
23
|
configPath?: string;
|
|
36
24
|
signal?: AbortSignal;
|
|
37
25
|
}
|
|
@@ -40,19 +28,6 @@ export interface EstimatorInfo {
|
|
|
40
28
|
model: string | null;
|
|
41
29
|
is_exact: boolean;
|
|
42
30
|
}
|
|
43
|
-
export interface BudgetReport {
|
|
44
|
-
target_tokens: number | null;
|
|
45
|
-
protected_floor: number;
|
|
46
|
-
achieved_tokens: number;
|
|
47
|
-
}
|
|
48
|
-
export interface QualityReport {
|
|
49
|
-
eval_profile_id: string;
|
|
50
|
-
task_scope: string;
|
|
51
|
-
validated_ratio_band: string | null;
|
|
52
|
-
quality_retention: number;
|
|
53
|
-
contrastive_failure_rate: number;
|
|
54
|
-
gate_passed: boolean;
|
|
55
|
-
}
|
|
56
31
|
export interface Warning {
|
|
57
32
|
code: string;
|
|
58
33
|
severity: "info" | "warn" | "critical";
|
|
@@ -71,12 +46,39 @@ export interface TransformReport {
|
|
|
71
46
|
skipped_reason: string | null;
|
|
72
47
|
warnings: readonly Warning[];
|
|
73
48
|
}
|
|
74
|
-
export interface
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
49
|
+
export interface BudgetReport {
|
|
50
|
+
status: "not_requested" | "met" | "best_effort" | "unreachable";
|
|
51
|
+
target_tokens: number | null;
|
|
52
|
+
protected_floor: number;
|
|
53
|
+
achieved_tokens: number;
|
|
54
|
+
}
|
|
55
|
+
export interface EncodingReport {
|
|
56
|
+
codec: string;
|
|
57
|
+
version: string;
|
|
58
|
+
roundtrip_verified: boolean;
|
|
59
|
+
tokens_before: number;
|
|
60
|
+
tokens_after: number;
|
|
61
|
+
token_delta: number;
|
|
78
62
|
warnings: readonly Warning[];
|
|
79
63
|
}
|
|
64
|
+
export interface PruningReport {
|
|
65
|
+
requested: boolean;
|
|
66
|
+
applied: boolean;
|
|
67
|
+
preview: boolean;
|
|
68
|
+
candidate_items: number;
|
|
69
|
+
retained_items: number;
|
|
70
|
+
pruned_items: number;
|
|
71
|
+
evidence_refs: number;
|
|
72
|
+
preserve_paths: readonly string[];
|
|
73
|
+
}
|
|
74
|
+
export interface QualityReport {
|
|
75
|
+
eval_profile_id: string;
|
|
76
|
+
task_scope: string;
|
|
77
|
+
validated_ratio_band: string | null;
|
|
78
|
+
quality_retention: number | null;
|
|
79
|
+
contrastive_failure_rate: number | null;
|
|
80
|
+
gate_passed: boolean;
|
|
81
|
+
}
|
|
80
82
|
export interface RetrievalReport {
|
|
81
83
|
store_namespace: string;
|
|
82
84
|
hash_algorithm: string;
|
|
@@ -85,82 +87,83 @@ export interface RetrievalReport {
|
|
|
85
87
|
persisted_original_bytes: number;
|
|
86
88
|
skipped_original_bytes: number;
|
|
87
89
|
}
|
|
88
|
-
export interface
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
child_exit_code: number | null;
|
|
101
|
-
duration_ms: number;
|
|
102
|
-
raw_output_bytes: number;
|
|
103
|
-
stdout_bytes: number;
|
|
104
|
-
stderr_bytes: number;
|
|
105
|
-
stderr_mode: string;
|
|
106
|
-
stderr_truncated: boolean;
|
|
107
|
-
compressed_output_bytes: number;
|
|
108
|
-
filter_pack_id: string | null;
|
|
109
|
-
filter_version: string | null;
|
|
110
|
-
never_worse_applied: boolean;
|
|
90
|
+
export interface PipelineStageReport {
|
|
91
|
+
id: string;
|
|
92
|
+
version: string | null;
|
|
93
|
+
input_bytes: number | null;
|
|
94
|
+
output_bytes: number | null;
|
|
95
|
+
saved_bytes: number | null;
|
|
96
|
+
input_tokens: number | null;
|
|
97
|
+
output_tokens: number | null;
|
|
98
|
+
saved_tokens: number | null;
|
|
99
|
+
estimator: EstimatorInfo | null;
|
|
100
|
+
status: string;
|
|
101
|
+
duration_ms: number | null;
|
|
111
102
|
bypass_reason: string | null;
|
|
103
|
+
provenance: string;
|
|
104
|
+
recoverability: string;
|
|
105
|
+
evidence_ref: string | null;
|
|
112
106
|
}
|
|
113
|
-
export interface
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
export interface PipelineReport {
|
|
108
|
+
raw_input_bytes: number | null;
|
|
109
|
+
raw_input_tokens: number | null;
|
|
110
|
+
final_output_bytes: number;
|
|
111
|
+
final_output_tokens: number;
|
|
112
|
+
total_saved_tokens: number | null;
|
|
113
|
+
raw_capture: string;
|
|
114
|
+
upstream_recoverability: string;
|
|
115
|
+
stages: readonly PipelineStageReport[];
|
|
118
116
|
}
|
|
119
|
-
export interface
|
|
117
|
+
export interface CompressionReceipt {
|
|
120
118
|
schema_version: string;
|
|
119
|
+
status: "compressed" | "passthrough";
|
|
121
120
|
original_tokens: number;
|
|
122
121
|
compressed_tokens: number;
|
|
123
122
|
saved_tokens: number;
|
|
124
123
|
savings_ratio: number;
|
|
125
124
|
savings_pct: number;
|
|
126
125
|
estimator: EstimatorInfo;
|
|
127
|
-
|
|
128
|
-
mode: string;
|
|
126
|
+
preset: Preset;
|
|
129
127
|
format: string;
|
|
128
|
+
output_encoding: string;
|
|
130
129
|
task_scope: string;
|
|
131
130
|
request_id: string | null;
|
|
131
|
+
pipeline: PipelineReport | null;
|
|
132
132
|
quality: QualityReport | null;
|
|
133
133
|
budget: BudgetReport | null;
|
|
134
|
-
|
|
134
|
+
encoding: EncodingReport | null;
|
|
135
|
+
pruning: PruningReport | null;
|
|
135
136
|
retrieval: RetrievalReport | null;
|
|
136
|
-
output_savings: OutputSavingsReport | null;
|
|
137
|
-
bypass: BypassReport | null;
|
|
138
|
-
command: CommandReport | null;
|
|
139
|
-
ledger: LedgerReport | null;
|
|
140
137
|
transforms: readonly TransformReport[];
|
|
141
138
|
warnings: readonly Warning[];
|
|
139
|
+
cache: unknown;
|
|
140
|
+
output_savings: unknown;
|
|
141
|
+
bypass: unknown;
|
|
142
|
+
command: unknown;
|
|
143
|
+
ledger: unknown;
|
|
142
144
|
}
|
|
145
|
+
export type CompressionReport = CompressionReceipt;
|
|
143
146
|
export interface CompressionResult {
|
|
144
147
|
payload: Uint8Array;
|
|
145
|
-
|
|
148
|
+
readonly text: string;
|
|
149
|
+
report: CompressionReceipt;
|
|
146
150
|
}
|
|
151
|
+
export declare class BudgetUnmetError extends Error {
|
|
152
|
+
readonly receipt: CompressionReceipt;
|
|
153
|
+
constructor(receipt: CompressionReceipt);
|
|
154
|
+
}
|
|
155
|
+
/** Read archived v1 or current v2 receipts; unavailable sections remain null. */
|
|
156
|
+
export declare function parseReport(input: string | Uint8Array): CompressionReceipt;
|
|
147
157
|
export declare function compress(input: Input, options?: CompressionOptions): Promise<CompressionResult>;
|
|
148
|
-
export declare function inspect(input: Input, options?: CompressionOptions): Promise<
|
|
158
|
+
export declare function inspect(input: Input, options?: CompressionOptions): Promise<CompressionReceipt>;
|
|
159
|
+
export declare function decode(input: Input, options?: {
|
|
160
|
+
from?: DecodeFormat;
|
|
161
|
+
signal?: AbortSignal;
|
|
162
|
+
}): Promise<Uint8Array>;
|
|
149
163
|
export interface RetrieveOptions {
|
|
150
|
-
|
|
164
|
+
retrievalStore?: string;
|
|
151
165
|
namespace?: string;
|
|
152
166
|
configPath?: string;
|
|
153
167
|
signal?: AbortSignal;
|
|
154
168
|
}
|
|
155
|
-
|
|
156
|
-
* Restores the original bytes of something a lossy run dropped, or a `storeOriginals` run saved,
|
|
157
|
-
* mirroring `tokenfold retrieve`. `reference` is either a raw hex SHA-256 hash — a compressed
|
|
158
|
-
* payload's `$tf_ref.hash` — or a `[tokenfold:retrieve hash=... namespace=...]` text marker,
|
|
159
|
-
* whose embedded namespace is used when `options.namespace` is omitted. A CompressionReport
|
|
160
|
-
* path is NOT a valid reference: the current report schema carries no per-entry hash, and the
|
|
161
|
-
* CLI rejects it rather than guessing.
|
|
162
|
-
*
|
|
163
|
-
* Throws `TokenFoldProcessError` (`code: "tokenfold_exit"`) when the hash is unknown, its TTL
|
|
164
|
-
* has elapsed, or the reference is malformed — the CLI reports all of those as a non-zero exit.
|
|
165
|
-
*/
|
|
166
|
-
export declare function retrieve(reference: string, options?: RetrieveOptions): Promise<Uint8Array>;
|
|
169
|
+
export declare function retrieve(reference: string | Record<string, unknown>, options?: RetrieveOptions): Promise<Uint8Array>;
|
package/dist/index.js
CHANGED
|
@@ -2,114 +2,134 @@ import { binaryPath } from "./binary.js";
|
|
|
2
2
|
import { TokenFoldProcessError } from "./errors.js";
|
|
3
3
|
import { run } from "./process.js";
|
|
4
4
|
export { binaryPath, run, TokenFoldProcessError };
|
|
5
|
+
export class BudgetUnmetError extends Error {
|
|
6
|
+
receipt;
|
|
7
|
+
constructor(receipt) { super(`token budget unmet: achieved ${receipt.compressed_tokens} tokens`); this.name = "BudgetUnmetError"; this.receipt = receipt; }
|
|
8
|
+
}
|
|
5
9
|
function argumentsFor(command, options) {
|
|
6
|
-
|
|
7
|
-
// `compress --dry-run`, which routes to the same code path and, exactly like `inspect --json`,
|
|
8
|
-
// writes the report to stdout and no payload. So a lossy inspect() becomes that instead.
|
|
9
|
-
const wantsLossy = options.lossy !== undefined ||
|
|
10
|
-
options.lossyRatio !== undefined ||
|
|
11
|
-
(options.lossyPreserve?.length ?? 0) > 0;
|
|
12
|
-
const previewLossy = command === "inspect" && wantsLossy;
|
|
13
|
-
const args = previewLossy ? ["compress", "--json", "--dry-run"] : [command, "--json"];
|
|
14
|
-
const compressing = command === "compress" || previewLossy;
|
|
10
|
+
const args = [command, "--receipt-format", "json"];
|
|
15
11
|
if (options.format)
|
|
16
12
|
args.push("--format", options.format);
|
|
17
|
-
if (options.
|
|
18
|
-
args.push("--
|
|
13
|
+
if (options.preset)
|
|
14
|
+
args.push("--preset", options.preset);
|
|
19
15
|
if (options.targetTokens !== undefined)
|
|
20
16
|
args.push("--target-tokens", String(options.targetTokens));
|
|
21
|
-
if (options.
|
|
22
|
-
args.push("--
|
|
23
|
-
if (options.
|
|
24
|
-
args.push("--
|
|
25
|
-
if (options.
|
|
26
|
-
args.push("--
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// knob is worse than the CLI's own "the following required arguments were not provided".
|
|
36
|
-
if (options.lossy)
|
|
37
|
-
args.push("--lossy", options.lossy);
|
|
38
|
-
if (options.lossyRatio !== undefined)
|
|
39
|
-
args.push("--lossy-ratio", String(options.lossyRatio));
|
|
40
|
-
for (const path of options.lossyPreserve ?? [])
|
|
41
|
-
args.push("--lossy-preserve", path);
|
|
17
|
+
if (options.requireTarget)
|
|
18
|
+
args.push("--require-target");
|
|
19
|
+
if (options.encoding)
|
|
20
|
+
args.push("--encoding", options.encoding);
|
|
21
|
+
if (options.pruning) {
|
|
22
|
+
args.push("--prune");
|
|
23
|
+
if (options.pruning.keepRatio !== undefined)
|
|
24
|
+
args.push("--keep-ratio", String(options.pruning.keepRatio));
|
|
25
|
+
for (const path of options.pruning.preservePaths ?? [])
|
|
26
|
+
args.push("--preserve", path);
|
|
27
|
+
if (options.pruning.retrievalStore)
|
|
28
|
+
args.push("--retrieval-store", options.pruning.retrievalStore);
|
|
29
|
+
if (options.pruning.retrievalNamespace)
|
|
30
|
+
args.push("--retrieval-namespace", options.pruning.retrievalNamespace);
|
|
42
31
|
}
|
|
43
32
|
if (options.configPath)
|
|
44
33
|
args.push("--config", options.configPath);
|
|
45
34
|
return args;
|
|
46
35
|
}
|
|
47
|
-
function
|
|
36
|
+
function parseReceipt(bytes, result) {
|
|
37
|
+
const text = Buffer.from(bytes).toString("utf8");
|
|
38
|
+
const json = text.split("\ntokenfold:", 1)[0] ?? "";
|
|
39
|
+
try {
|
|
40
|
+
return parseReport(json);
|
|
41
|
+
}
|
|
42
|
+
catch (cause) {
|
|
43
|
+
throw new TokenFoldProcessError("tokenfold returned an invalid JSON receipt", { code: "invalid_report", exitCode: result.exitCode, signal: result.signal, stderr: result.stderr, cause });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Read archived v1 or current v2 receipts; unavailable sections remain null. */
|
|
47
|
+
export function parseReport(input) {
|
|
48
48
|
try {
|
|
49
|
-
|
|
49
|
+
const value = JSON.parse(typeof input === "string" ? input : new TextDecoder("utf-8", { fatal: true }).decode(input));
|
|
50
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
51
|
+
throw new Error("not a receipt object");
|
|
52
|
+
if (!["1.0", "2.0"].includes(value.schema_version))
|
|
53
|
+
throw new Error("unsupported receipt schema_version");
|
|
54
|
+
if (value.schema_version === "1.0") {
|
|
55
|
+
if (!Object.hasOwn(value, "preset")) {
|
|
56
|
+
value.preset = value.mode;
|
|
57
|
+
delete value.mode;
|
|
58
|
+
}
|
|
59
|
+
if (!Object.hasOwn(value, "output_encoding"))
|
|
60
|
+
value.output_encoding = "native";
|
|
61
|
+
}
|
|
62
|
+
for (const key of ["original_tokens", "compressed_tokens", "saved_tokens", "savings_ratio", "savings_pct"]) {
|
|
63
|
+
if (typeof value[key] !== "number" || !Number.isFinite(value[key]) || value[key] < 0)
|
|
64
|
+
throw new Error(`invalid ${key}`);
|
|
65
|
+
}
|
|
66
|
+
for (const key of ["status", "preset", "format", "output_encoding", "task_scope"]) {
|
|
67
|
+
if (typeof value[key] !== "string")
|
|
68
|
+
throw new Error(`invalid ${key}`);
|
|
69
|
+
}
|
|
70
|
+
if (!value.estimator || typeof value.estimator.backend !== "string" || typeof value.estimator.is_exact !== "boolean"
|
|
71
|
+
|| !Array.isArray(value.transforms) || !Array.isArray(value.warnings))
|
|
72
|
+
throw new Error("invalid receipt fields");
|
|
73
|
+
for (const key of ["request_id", "pipeline", "quality", "budget", "encoding", "pruning", "cache", "retrieval", "output_savings", "bypass", "command", "ledger"]) {
|
|
74
|
+
value[key] ??= null;
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
50
77
|
}
|
|
51
78
|
catch (cause) {
|
|
52
|
-
throw new TokenFoldProcessError("
|
|
53
|
-
code: "invalid_report",
|
|
54
|
-
exitCode: result.exitCode,
|
|
55
|
-
signal: result.signal,
|
|
56
|
-
stderr: result.stderr,
|
|
57
|
-
cause,
|
|
58
|
-
});
|
|
79
|
+
throw new TokenFoldProcessError("not a supported compression receipt", { code: "invalid_report", cause });
|
|
59
80
|
}
|
|
60
81
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
stderr: result.stderr,
|
|
69
|
-
});
|
|
82
|
+
function optionsFor(input, signal) {
|
|
83
|
+
const options = { env: { TOKENFOLD_ANALYTICS_ENABLED: "false" } };
|
|
84
|
+
if (input !== undefined)
|
|
85
|
+
options.stdin = input;
|
|
86
|
+
if (signal)
|
|
87
|
+
options.signal = signal;
|
|
88
|
+
return options;
|
|
70
89
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
stdin: input,
|
|
74
|
-
env: { TOKENFOLD_ANALYTICS_ENABLED: "false" },
|
|
75
|
-
};
|
|
76
|
-
if (options.signal)
|
|
77
|
-
runOptions.signal = options.signal;
|
|
78
|
-
const result = await run(argumentsFor(command, options), runOptions);
|
|
79
|
-
throwIfFailed(result);
|
|
80
|
-
const reportBytes = command === "compress" ? result.stderr : result.stdout;
|
|
81
|
-
return {
|
|
82
|
-
payload: command === "compress" ? result.stdout : Uint8Array.from(Buffer.from(input)),
|
|
83
|
-
report: parseReport(reportBytes, result),
|
|
84
|
-
};
|
|
90
|
+
function withText(payload, report) {
|
|
91
|
+
return { payload, report, get text() { return new TextDecoder("utf-8", { fatal: true }).decode(payload); } };
|
|
85
92
|
}
|
|
86
|
-
export function compress(input, options = {}) {
|
|
87
|
-
|
|
93
|
+
export async function compress(input, options = {}) {
|
|
94
|
+
const result = await run(argumentsFor("compress", options), optionsFor(input, options.signal));
|
|
95
|
+
if (result.exitCode !== 0 && result.exitCode !== 7)
|
|
96
|
+
throwProcess(result);
|
|
97
|
+
const receipt = parseReceipt(result.stderr, result);
|
|
98
|
+
if (result.exitCode === 7)
|
|
99
|
+
throw new BudgetUnmetError(receipt);
|
|
100
|
+
return withText(result.stdout, receipt);
|
|
88
101
|
}
|
|
89
|
-
export function inspect(input, options = {}) {
|
|
90
|
-
|
|
102
|
+
export async function inspect(input, options = {}) {
|
|
103
|
+
const result = await run(argumentsFor("inspect", options), optionsFor(input, options.signal));
|
|
104
|
+
if (result.exitCode !== 0 && result.exitCode !== 7)
|
|
105
|
+
throwProcess(result);
|
|
106
|
+
const receipt = parseReceipt(result.stdout, result);
|
|
107
|
+
if (result.exitCode === 7)
|
|
108
|
+
throw new BudgetUnmetError(receipt);
|
|
109
|
+
return receipt;
|
|
110
|
+
}
|
|
111
|
+
export async function decode(input, options = {}) {
|
|
112
|
+
const args = ["decode"];
|
|
113
|
+
if (options.from)
|
|
114
|
+
args.push("--from", options.from);
|
|
115
|
+
const result = await run(args, optionsFor(input, options.signal));
|
|
116
|
+
if (result.exitCode !== 0)
|
|
117
|
+
throwProcess(result);
|
|
118
|
+
return result.stdout;
|
|
91
119
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Restores the original bytes of something a lossy run dropped, or a `storeOriginals` run saved,
|
|
94
|
-
* mirroring `tokenfold retrieve`. `reference` is either a raw hex SHA-256 hash — a compressed
|
|
95
|
-
* payload's `$tf_ref.hash` — or a `[tokenfold:retrieve hash=... namespace=...]` text marker,
|
|
96
|
-
* whose embedded namespace is used when `options.namespace` is omitted. A CompressionReport
|
|
97
|
-
* path is NOT a valid reference: the current report schema carries no per-entry hash, and the
|
|
98
|
-
* CLI rejects it rather than guessing.
|
|
99
|
-
*
|
|
100
|
-
* Throws `TokenFoldProcessError` (`code: "tokenfold_exit"`) when the hash is unknown, its TTL
|
|
101
|
-
* has elapsed, or the reference is malformed — the CLI reports all of those as a non-zero exit.
|
|
102
|
-
*/
|
|
103
120
|
export async function retrieve(reference, options = {}) {
|
|
104
|
-
const args = ["retrieve", reference];
|
|
121
|
+
const args = ["retrieve", typeof reference === "string" ? reference : JSON.stringify(reference)];
|
|
122
|
+
if (options.retrievalStore)
|
|
123
|
+
args.push("--retrieval-store", options.retrievalStore);
|
|
105
124
|
if (options.namespace)
|
|
106
|
-
args.push("--
|
|
125
|
+
args.push("--retrieval-namespace", options.namespace);
|
|
107
126
|
if (options.configPath)
|
|
108
127
|
args.push("--config", options.configPath);
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
const result = await run(args, runOptions);
|
|
113
|
-
throwIfFailed(result);
|
|
128
|
+
const result = await run(args, optionsFor(undefined, options.signal));
|
|
129
|
+
if (result.exitCode !== 0)
|
|
130
|
+
throwProcess(result);
|
|
114
131
|
return result.stdout;
|
|
115
132
|
}
|
|
133
|
+
function throwProcess(result) {
|
|
134
|
+
throw new TokenFoldProcessError(`tokenfold exited with status ${result.exitCode ?? result.signal}`, { code: "tokenfold_exit", exitCode: result.exitCode, signal: result.signal, stderr: result.stderr });
|
|
135
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenfold",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Token-aware compression for LLM payloads, backed by the tokenfold Rust CLI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"prepack": "npm run build"
|
|
50
50
|
},
|
|
51
51
|
"optionalDependencies": {
|
|
52
|
-
"@tokenfold/cli-darwin-x64": "0.
|
|
53
|
-
"@tokenfold/cli-darwin-arm64": "0.
|
|
54
|
-
"@tokenfold/cli-linux-x64": "0.
|
|
55
|
-
"@tokenfold/cli-linux-arm64": "0.
|
|
56
|
-
"@tokenfold/cli-win32-x64": "0.
|
|
52
|
+
"@tokenfold/cli-darwin-x64": "0.5.1",
|
|
53
|
+
"@tokenfold/cli-darwin-arm64": "0.5.1",
|
|
54
|
+
"@tokenfold/cli-linux-x64": "0.5.1",
|
|
55
|
+
"@tokenfold/cli-linux-arm64": "0.5.1",
|
|
56
|
+
"@tokenfold/cli-win32-x64": "0.5.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/node": "^24.0.0",
|