tokenfold 0.3.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 +19 -0
- package/bin/tokenfold.js +29 -0
- package/dist/binary.d.ts +1 -0
- package/dist/binary.js +30 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +14 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +69 -0
- package/dist/process.d.ts +14 -0
- package/dist/process.js +61 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# tokenfold for Node.js
|
|
2
|
+
|
|
3
|
+
Zero-runtime-dependency TypeScript bindings for the tokenfold Rust CLI.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install tokenfold
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { compress } from "tokenfold";
|
|
11
|
+
|
|
12
|
+
const { payload, report } = await compress(input, {
|
|
13
|
+
format: "json",
|
|
14
|
+
mode: "balanced",
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires Node.js 22 or newer. The matching native CLI is installed through an
|
|
19
|
+
optional platform package; set `TOKENFOLD_BINARY_PATH` to use a custom binary.
|
package/bin/tokenfold.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { binaryPath } from "../dist/index.js";
|
|
4
|
+
|
|
5
|
+
let child;
|
|
6
|
+
try {
|
|
7
|
+
child = spawn(binaryPath(), process.argv.slice(2), { shell: false, stdio: "inherit" });
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
10
|
+
process.exitCode = 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (child) {
|
|
14
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
15
|
+
process.on(signal, () => child?.kill(signal));
|
|
16
|
+
}
|
|
17
|
+
child.once("error", (error) => {
|
|
18
|
+
console.error(`Could not run tokenfold: ${error.message}`);
|
|
19
|
+
process.exitCode = 1;
|
|
20
|
+
});
|
|
21
|
+
child.once("close", (code, signal) => {
|
|
22
|
+
if (signal) {
|
|
23
|
+
process.removeAllListeners(signal);
|
|
24
|
+
process.kill(process.pid, signal);
|
|
25
|
+
} else {
|
|
26
|
+
process.exitCode = code ?? 1;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
package/dist/binary.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function binaryPath(): string;
|
package/dist/binary.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { TokenFoldProcessError } from "./errors.js";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
const PLATFORM_PACKAGES = {
|
|
6
|
+
"darwin-x64": ["@tokenfold/cli-darwin-x64", "tokenfold"],
|
|
7
|
+
"darwin-arm64": ["@tokenfold/cli-darwin-arm64", "tokenfold"],
|
|
8
|
+
"linux-x64": ["@tokenfold/cli-linux-x64", "tokenfold"],
|
|
9
|
+
"linux-arm64": ["@tokenfold/cli-linux-arm64", "tokenfold"],
|
|
10
|
+
"win32-x64": ["@tokenfold/cli-win32-x64", "tokenfold.exe"],
|
|
11
|
+
};
|
|
12
|
+
export function binaryPath() {
|
|
13
|
+
const override = process.env.TOKENFOLD_BINARY_PATH;
|
|
14
|
+
if (override)
|
|
15
|
+
return resolve(override);
|
|
16
|
+
const key = `${process.platform}-${process.arch}`;
|
|
17
|
+
const platformPackage = PLATFORM_PACKAGES[key];
|
|
18
|
+
if (!platformPackage) {
|
|
19
|
+
throw new TokenFoldProcessError(`Unsupported tokenfold platform: ${key}`, {
|
|
20
|
+
code: "binary_not_found",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const [packageName, binaryName] = platformPackage;
|
|
24
|
+
try {
|
|
25
|
+
return resolve(dirname(require.resolve(`${packageName}/package.json`)), "bin", binaryName);
|
|
26
|
+
}
|
|
27
|
+
catch (cause) {
|
|
28
|
+
throw new TokenFoldProcessError(`The optional package ${packageName} is not installed for ${key}`, { code: "binary_not_found", cause });
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type TokenFoldErrorCode = "binary_not_found" | "spawn_failed" | "tokenfold_exit" | "invalid_report";
|
|
2
|
+
export declare class TokenFoldProcessError extends Error {
|
|
3
|
+
readonly code: TokenFoldErrorCode;
|
|
4
|
+
readonly exitCode: number | null;
|
|
5
|
+
readonly signal: NodeJS.Signals | null;
|
|
6
|
+
readonly stderr: Uint8Array;
|
|
7
|
+
constructor(message: string, options: {
|
|
8
|
+
code: TokenFoldErrorCode;
|
|
9
|
+
exitCode?: number | null;
|
|
10
|
+
signal?: NodeJS.Signals | null;
|
|
11
|
+
stderr?: Uint8Array;
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
});
|
|
14
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class TokenFoldProcessError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
exitCode;
|
|
4
|
+
signal;
|
|
5
|
+
stderr;
|
|
6
|
+
constructor(message, options) {
|
|
7
|
+
super(message, { cause: options.cause });
|
|
8
|
+
this.name = "TokenFoldProcessError";
|
|
9
|
+
this.code = options.code;
|
|
10
|
+
this.exitCode = options.exitCode ?? null;
|
|
11
|
+
this.signal = options.signal ?? null;
|
|
12
|
+
this.stderr = options.stderr ?? new Uint8Array();
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { binaryPath } from "./binary.js";
|
|
2
|
+
import { TokenFoldProcessError } from "./errors.js";
|
|
3
|
+
import { run, type Input, type ProcessResult, type RunOptions } from "./process.js";
|
|
4
|
+
export { binaryPath, run, TokenFoldProcessError };
|
|
5
|
+
export type { Input, ProcessResult, RunOptions };
|
|
6
|
+
export type CompressionMode = "conservative" | "balanced" | "aggressive";
|
|
7
|
+
export type TaskScope = "all" | "general" | "code_review" | "change_summary" | "debugging" | "generation" | "api_overview" | "retrieval_qa" | "agent_history";
|
|
8
|
+
export interface CompressionOptions {
|
|
9
|
+
format?: "auto" | "openai" | "anthropic" | "json" | "text" | "command" | "diff";
|
|
10
|
+
mode?: CompressionMode;
|
|
11
|
+
targetTokens?: number;
|
|
12
|
+
disable?: readonly string[];
|
|
13
|
+
taskScope?: TaskScope;
|
|
14
|
+
experimental?: boolean;
|
|
15
|
+
storeOriginals?: boolean;
|
|
16
|
+
retrieveNamespace?: string;
|
|
17
|
+
configPath?: string;
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}
|
|
20
|
+
export interface EstimatorInfo {
|
|
21
|
+
backend: string;
|
|
22
|
+
model: string | null;
|
|
23
|
+
is_exact: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface BudgetReport {
|
|
26
|
+
target_tokens: number | null;
|
|
27
|
+
protected_floor: number;
|
|
28
|
+
achieved_tokens: number;
|
|
29
|
+
}
|
|
30
|
+
export interface QualityReport {
|
|
31
|
+
eval_profile_id: string;
|
|
32
|
+
task_scope: string;
|
|
33
|
+
validated_ratio_band: string | null;
|
|
34
|
+
quality_retention: number;
|
|
35
|
+
contrastive_failure_rate: number;
|
|
36
|
+
gate_passed: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface Warning {
|
|
39
|
+
code: string;
|
|
40
|
+
severity: "info" | "warn" | "critical";
|
|
41
|
+
transform: string | null;
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
export interface TransformReport {
|
|
45
|
+
id: string;
|
|
46
|
+
version: string;
|
|
47
|
+
tokens_before: number;
|
|
48
|
+
tokens_after: number;
|
|
49
|
+
saved_tokens: number;
|
|
50
|
+
savings_ratio: number;
|
|
51
|
+
elapsed_micros: number | null;
|
|
52
|
+
status: "applied" | "no_op" | "skipped" | "rolled_back";
|
|
53
|
+
skipped_reason: string | null;
|
|
54
|
+
warnings: readonly Warning[];
|
|
55
|
+
}
|
|
56
|
+
export interface CacheReport {
|
|
57
|
+
boundary_kind: string | null;
|
|
58
|
+
protected_bytes: number;
|
|
59
|
+
prefix_byte_identical: boolean;
|
|
60
|
+
warnings: readonly Warning[];
|
|
61
|
+
}
|
|
62
|
+
export interface RetrievalReport {
|
|
63
|
+
store_namespace: string;
|
|
64
|
+
hash_algorithm: string;
|
|
65
|
+
marker_count: number;
|
|
66
|
+
ttl_seconds: number | null;
|
|
67
|
+
persisted_original_bytes: number;
|
|
68
|
+
skipped_original_bytes: number;
|
|
69
|
+
}
|
|
70
|
+
export interface OutputSavingsReport {
|
|
71
|
+
profile: string;
|
|
72
|
+
estimated_output_tokens_saved: number | null;
|
|
73
|
+
measured_output_tokens_saved: number | null;
|
|
74
|
+
provenance: string;
|
|
75
|
+
}
|
|
76
|
+
export interface BypassReport {
|
|
77
|
+
reason: string;
|
|
78
|
+
source: string;
|
|
79
|
+
}
|
|
80
|
+
export interface CommandReport {
|
|
81
|
+
command_family: string | null;
|
|
82
|
+
child_exit_code: number | null;
|
|
83
|
+
duration_ms: number;
|
|
84
|
+
raw_output_bytes: number;
|
|
85
|
+
stdout_bytes: number;
|
|
86
|
+
stderr_bytes: number;
|
|
87
|
+
stderr_mode: string;
|
|
88
|
+
stderr_truncated: boolean;
|
|
89
|
+
compressed_output_bytes: number;
|
|
90
|
+
filter_pack_id: string | null;
|
|
91
|
+
filter_version: string | null;
|
|
92
|
+
never_worse_applied: boolean;
|
|
93
|
+
bypass_reason: string | null;
|
|
94
|
+
}
|
|
95
|
+
export interface LedgerReport {
|
|
96
|
+
recorded: boolean;
|
|
97
|
+
scope: string | null;
|
|
98
|
+
project_hash: string | null;
|
|
99
|
+
record_id: string | null;
|
|
100
|
+
}
|
|
101
|
+
export interface CompressionReport {
|
|
102
|
+
schema_version: string;
|
|
103
|
+
original_tokens: number;
|
|
104
|
+
compressed_tokens: number;
|
|
105
|
+
saved_tokens: number;
|
|
106
|
+
savings_ratio: number;
|
|
107
|
+
savings_pct: number;
|
|
108
|
+
estimator: EstimatorInfo;
|
|
109
|
+
status: "compressed" | "passthrough" | "best_effort" | "unreachable_target";
|
|
110
|
+
mode: string;
|
|
111
|
+
format: string;
|
|
112
|
+
task_scope: string;
|
|
113
|
+
request_id: string | null;
|
|
114
|
+
quality: QualityReport | null;
|
|
115
|
+
budget: BudgetReport | null;
|
|
116
|
+
cache: CacheReport | null;
|
|
117
|
+
retrieval: RetrievalReport | null;
|
|
118
|
+
output_savings: OutputSavingsReport | null;
|
|
119
|
+
bypass: BypassReport | null;
|
|
120
|
+
command: CommandReport | null;
|
|
121
|
+
ledger: LedgerReport | null;
|
|
122
|
+
transforms: readonly TransformReport[];
|
|
123
|
+
warnings: readonly Warning[];
|
|
124
|
+
}
|
|
125
|
+
export interface CompressionResult {
|
|
126
|
+
payload: Uint8Array;
|
|
127
|
+
report: CompressionReport;
|
|
128
|
+
}
|
|
129
|
+
export declare function compress(input: Input, options?: CompressionOptions): Promise<CompressionResult>;
|
|
130
|
+
export declare function inspect(input: Input, options?: CompressionOptions): Promise<CompressionResult>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { binaryPath } from "./binary.js";
|
|
2
|
+
import { TokenFoldProcessError } from "./errors.js";
|
|
3
|
+
import { run } from "./process.js";
|
|
4
|
+
export { binaryPath, run, TokenFoldProcessError };
|
|
5
|
+
function argumentsFor(command, options) {
|
|
6
|
+
const args = [command, "--json"];
|
|
7
|
+
if (options.format)
|
|
8
|
+
args.push("--format", options.format);
|
|
9
|
+
if (options.mode)
|
|
10
|
+
args.push("--mode", options.mode);
|
|
11
|
+
if (options.targetTokens !== undefined)
|
|
12
|
+
args.push("--target-tokens", String(options.targetTokens));
|
|
13
|
+
if (options.disable?.length && command === "compress")
|
|
14
|
+
args.push("--disable", options.disable.join(","));
|
|
15
|
+
if (options.taskScope)
|
|
16
|
+
args.push("--task-scope", options.taskScope);
|
|
17
|
+
if (options.experimental)
|
|
18
|
+
args.push("--experimental");
|
|
19
|
+
if (options.storeOriginals && command === "compress")
|
|
20
|
+
args.push("--store-originals");
|
|
21
|
+
if (options.retrieveNamespace && command === "compress") {
|
|
22
|
+
args.push("--retrieve-namespace", options.retrieveNamespace);
|
|
23
|
+
}
|
|
24
|
+
if (options.configPath)
|
|
25
|
+
args.push("--config", options.configPath);
|
|
26
|
+
return args;
|
|
27
|
+
}
|
|
28
|
+
function parseReport(bytes, result) {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(Buffer.from(bytes).toString("utf8"));
|
|
31
|
+
}
|
|
32
|
+
catch (cause) {
|
|
33
|
+
throw new TokenFoldProcessError("tokenfold returned an invalid JSON report", {
|
|
34
|
+
code: "invalid_report",
|
|
35
|
+
exitCode: result.exitCode,
|
|
36
|
+
signal: result.signal,
|
|
37
|
+
stderr: result.stderr,
|
|
38
|
+
cause,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function execute(command, input, options) {
|
|
43
|
+
const runOptions = {
|
|
44
|
+
stdin: input,
|
|
45
|
+
env: { TOKENFOLD_ANALYTICS_ENABLED: "false" },
|
|
46
|
+
};
|
|
47
|
+
if (options.signal)
|
|
48
|
+
runOptions.signal = options.signal;
|
|
49
|
+
const result = await run(argumentsFor(command, options), runOptions);
|
|
50
|
+
if (result.exitCode !== 0) {
|
|
51
|
+
throw new TokenFoldProcessError(`tokenfold exited with status ${result.exitCode ?? result.signal}`, {
|
|
52
|
+
code: "tokenfold_exit",
|
|
53
|
+
exitCode: result.exitCode,
|
|
54
|
+
signal: result.signal,
|
|
55
|
+
stderr: result.stderr,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const reportBytes = command === "compress" ? result.stderr : result.stdout;
|
|
59
|
+
return {
|
|
60
|
+
payload: command === "compress" ? result.stdout : Uint8Array.from(Buffer.from(input)),
|
|
61
|
+
report: parseReport(reportBytes, result),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function compress(input, options = {}) {
|
|
65
|
+
return execute("compress", input, options);
|
|
66
|
+
}
|
|
67
|
+
export function inspect(input, options = {}) {
|
|
68
|
+
return execute("inspect", input, options);
|
|
69
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type Input = string | Uint8Array;
|
|
2
|
+
export interface ProcessResult {
|
|
3
|
+
stdout: Uint8Array;
|
|
4
|
+
stderr: Uint8Array;
|
|
5
|
+
exitCode: number | null;
|
|
6
|
+
signal: NodeJS.Signals | null;
|
|
7
|
+
}
|
|
8
|
+
export interface RunOptions {
|
|
9
|
+
stdin?: Input;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
export declare function run(args: readonly string[], options?: RunOptions): Promise<ProcessResult>;
|
package/dist/process.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { binaryPath } from "./binary.js";
|
|
3
|
+
import { TokenFoldProcessError } from "./errors.js";
|
|
4
|
+
function environment(overrides) {
|
|
5
|
+
const env = { ...process.env };
|
|
6
|
+
for (const [key, value] of Object.entries(overrides ?? {})) {
|
|
7
|
+
if (value === undefined)
|
|
8
|
+
delete env[key];
|
|
9
|
+
else
|
|
10
|
+
env[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return env;
|
|
13
|
+
}
|
|
14
|
+
export function run(args, options = {}) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
let child;
|
|
17
|
+
try {
|
|
18
|
+
child = spawn(binaryPath(), [...args], {
|
|
19
|
+
cwd: options.cwd,
|
|
20
|
+
env: environment(options.env),
|
|
21
|
+
signal: options.signal,
|
|
22
|
+
shell: false,
|
|
23
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
catch (cause) {
|
|
27
|
+
reject(new TokenFoldProcessError("Could not start tokenfold", { code: "spawn_failed", cause }));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const stdout = [];
|
|
31
|
+
const stderr = [];
|
|
32
|
+
let settled = false;
|
|
33
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
34
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
35
|
+
// A process may exit before consuming all input. Its close/error event is
|
|
36
|
+
// authoritative; avoid turning the resulting broken pipe into an unhandled event.
|
|
37
|
+
child.stdin.on("error", () => { });
|
|
38
|
+
child.once("error", (cause) => {
|
|
39
|
+
if (settled)
|
|
40
|
+
return;
|
|
41
|
+
settled = true;
|
|
42
|
+
reject(new TokenFoldProcessError("Could not run tokenfold", {
|
|
43
|
+
code: "spawn_failed",
|
|
44
|
+
stderr: Buffer.concat(stderr),
|
|
45
|
+
cause,
|
|
46
|
+
}));
|
|
47
|
+
});
|
|
48
|
+
child.once("close", (exitCode, signal) => {
|
|
49
|
+
if (settled)
|
|
50
|
+
return;
|
|
51
|
+
settled = true;
|
|
52
|
+
resolve({
|
|
53
|
+
stdout: Buffer.concat(stdout),
|
|
54
|
+
stderr: Buffer.concat(stderr),
|
|
55
|
+
exitCode,
|
|
56
|
+
signal,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
child.stdin.end(options.stdin === undefined ? undefined : Buffer.from(options.stdin));
|
|
60
|
+
});
|
|
61
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tokenfold",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Token-aware compression for LLM payloads, backed by the tokenfold Rust CLI.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/snchimata/tokenfold.git",
|
|
9
|
+
"directory": "packages/tokenfold"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/snchimata/tokenfold#readme",
|
|
12
|
+
"bugs": "https://github.com/snchimata/tokenfold/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"llm",
|
|
15
|
+
"tokens",
|
|
16
|
+
"compression",
|
|
17
|
+
"cli"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=22"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"tokenfold": "./bin/tokenfold.js"
|
|
25
|
+
},
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"bin",
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.json",
|
|
40
|
+
"test": "npm run build && node --test test/*.test.mjs ../../scripts/*.test.mjs",
|
|
41
|
+
"prepack": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"optionalDependencies": {
|
|
44
|
+
"@tokenfold/cli-darwin-x64": "0.3.1",
|
|
45
|
+
"@tokenfold/cli-darwin-arm64": "0.3.1",
|
|
46
|
+
"@tokenfold/cli-linux-x64": "0.3.1",
|
|
47
|
+
"@tokenfold/cli-linux-arm64": "0.3.1",
|
|
48
|
+
"@tokenfold/cli-win32-x64": "0.3.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^24.0.0",
|
|
52
|
+
"typescript": "^5.9.0"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"provenance": true
|
|
57
|
+
}
|
|
58
|
+
}
|