@hol-org/cigar 0.9.4 → 0.10.0-beta.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 CHANGED
@@ -1,12 +1,14 @@
1
1
  # `@hol-org/cigar`
2
2
 
3
- Developer preview for Node.js 24 ESM applications:
3
+ 0.10.0 beta 1 for Node.js 24 ESM applications:
4
4
 
5
5
  ```text
6
- npm install @hol-org/cigar@alpha
6
+ npm install ./hol-org-cigar-0.10.0-beta.1.tgz
7
7
  ```
8
8
 
9
- The 0.9.4 developer preview is intended for the `alpha` channel. The package intentionally does
9
+ Verify the signed GitHub assets before installation. Registry publication requires a separate
10
+ maintainer approval; once listed, use `npm install '@hol-org/cigar@0.10.0-beta.1'`.
11
+ The prerelease channel is `beta`, never `latest`. The package intentionally does
10
12
  not claim CommonJS or browser-runtime support, and consumers should pin an exact version for
11
13
  reproducible workflow execution.
12
14
 
@@ -15,6 +17,53 @@ bounded deadlines, abort signals, typed problems, pagination, idempotency-bound
15
17
  and local bundle/delta verification. It has no install script and downloads no binaries.
16
18
  The exported `CONTEXT_ABI` constant is the exact string `cigar.context.v1`.
17
19
 
20
+ ## Local Rust context graph
21
+
22
+ ```ts
23
+ import { LocalContextGraph } from "@hol-org/cigar/context";
24
+
25
+ await using graph = await LocalContextGraph.create("my-project");
26
+ await graph.replaceSource("src/auth.ts", [
27
+ {id: "authorize", source: "src/auth.ts", text: "function authorize(user) { return user.active; }"},
28
+ ]);
29
+ const result = await graph.compile({query: "authorize", max_tokens: 1024, reserve_tokens: 128});
30
+ const contextText = result.rendered; // ordinary DATA/context, never an instruction/authority grant
31
+ console.assert(result.snapshot.stats.rendered_tokens <= 896);
32
+ ```
33
+
34
+ The local-only `/context` entry point avoids loading remote/protobuf code. Root exports also
35
+ include the new API without removing existing exports. `upsert`, `remove`, `link`/`unlink`,
36
+ atomic `replaceSource`, line-preserving `chunks`, `compile`, `verify`, `delta`, `applyDelta`,
37
+ `stats`, and `clearCache` use the same Rust selector, citations, exact `o200k_base` accounting,
38
+ incremental indexes, and bounded token cache as the Rust library. No server or model is needed.
39
+
40
+ Beta 1 bundles a worker only for macOS ARM64. Other platforms retain the remote SDK; local graphs
41
+ require an explicitly supplied, trusted absolute `workerPath` built from the matching Rust
42
+ 0.10.0-beta.1 source (`cargo build --locked --release -p cigar-context --features bpe --bin cigar-context-worker`).
43
+ They are not natively qualified by this beta. There is no install script, runtime download, PATH
44
+ lookup, shell, or implicit file ingestion. The worker is a persistent subprocess, not a native
45
+ Node addon or sandbox; it inherits your environment and OS privileges. Bundled bytes are checked
46
+ against their package manifest, not independently authenticated. Reuse a graph to amortize
47
+ startup; IPC and process memory cost more than embedding Rust directly.
48
+
49
+ Pass `allowed` IDs for each query when source permissions differ (`[]` authorizes none).
50
+ `required` evidence includes complete hard dependencies/counterclaims. Source withdrawal leaves
51
+ hard edges fail-closed until explicitly repaired. Full text is default; `excerpt_mode:
52
+ "query_windows"` opts into source-line excerpts. Digests detect changes; they are not signatures.
53
+ Delta reuse saves transport/storage bytes, not stateless prompt tokens. Budgets count complete
54
+ Rust-rendered context; reserve provider framing/history/output separately.
55
+
56
+ Use `await using` or `await graph.close()`. Calls are ordered, with at most 32 pending (configurable
57
+ `maxPending` 1..128), 64 MiB aggregate queued requests, 32 MiB per request, and 64 MiB per response.
58
+ `timeoutMs` defaults to 30 seconds **per active exchange**, including pipe writes; queued calls
59
+ wait their turn. `close()` aborts the active call and rejects the queue. Unsafe numeric integers
60
+ are rejected, not silently rounded. Customize graph/cache `limits`; default cache is 1024
61
+ strings/8 MiB. `clearCache` drops retained text but does not promise zeroization.
62
+ Errors expose a content-free `LocalContextError.code`. Timeout, malformed wire fields, and
63
+ protocol/pipe failures permanently close the graph; no mutations are automatically retried.
64
+
65
+ ## Compatible remote client
66
+
18
67
  ```ts
19
68
  import { CigarClient, createIdempotencyKey } from "@hol-org/cigar";
20
69
 
@@ -0,0 +1,4 @@
1
+ /** Lightweight local-only entry point; does not import the remote client/protobuf graph. */
2
+ export * from "./context.js";
3
+ export * from "./context-types.js";
4
+ //# sourceMappingURL=context-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-api.d.ts","sourceRoot":"","sources":["../src/context-api.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC","sourcesContent":["/** Lightweight local-only entry point; does not import the remote client/protobuf graph. */\nexport * from \"./context.js\";\nexport * from \"./context-types.js\";\n"]}
@@ -0,0 +1,4 @@
1
+ /** Lightweight local-only entry point; does not import the remote client/protobuf graph. */
2
+ export * from "./context.js";
3
+ export * from "./context-types.js";
4
+ //# sourceMappingURL=context-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-api.js","sourceRoot":"","sources":["../src/context-api.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC","sourcesContent":["/** Lightweight local-only entry point; does not import the remote client/protobuf graph. */\nexport * from \"./context.js\";\nexport * from \"./context-types.js\";\n"]}
@@ -0,0 +1,95 @@
1
+ /** Local graph wire values; these do not replace the frozen remote Context ABI. */
2
+ export type LocalEdgeKind = "requires" | "contradicts" | "supports" | "related";
3
+ export type LocalDocument = Readonly<{
4
+ id: string;
5
+ source: string;
6
+ text: string;
7
+ start_line?: number;
8
+ }>;
9
+ export type LocalContextLimits = Readonly<{
10
+ max_documents?: number;
11
+ max_document_bytes?: number;
12
+ max_total_bytes?: number;
13
+ max_edges_per_document?: number;
14
+ max_edges?: number;
15
+ cache_entries?: number;
16
+ cache_text_bytes?: number;
17
+ }>;
18
+ export type LocalContextRequest = Readonly<{
19
+ query?: string;
20
+ max_tokens?: number;
21
+ reserve_tokens?: number;
22
+ required?: readonly string[];
23
+ allowed?: readonly string[] | null;
24
+ semantic_candidates?: readonly string[];
25
+ policy_revision?: string;
26
+ max_candidates?: number;
27
+ max_blocks?: number;
28
+ graph_depth?: number;
29
+ evidence_per_term?: number;
30
+ excerpt_mode?: "full" | "query_windows";
31
+ }>;
32
+ export type LocalCitation = Readonly<{
33
+ node_id: string;
34
+ source: string;
35
+ document_digest: string;
36
+ start_line: number;
37
+ end_line: number;
38
+ }>;
39
+ export type LocalEvidenceBlock = Readonly<{
40
+ text: string;
41
+ citations: readonly LocalCitation[];
42
+ }>;
43
+ export type LocalSelectionStats = Readonly<{
44
+ documents: number;
45
+ lexical_matches: number;
46
+ candidates: number;
47
+ selected_blocks: number;
48
+ selected_sources: number;
49
+ query_terms: number;
50
+ covered_terms: number;
51
+ rendered_tokens: number;
52
+ available_tokens: number;
53
+ unavailable_roots: number;
54
+ limit_rejected_roots: number;
55
+ budget_rejected_roots: number;
56
+ }>;
57
+ export type LocalContextSnapshot = Readonly<{
58
+ id: string;
59
+ domain: string;
60
+ policy_revision: string;
61
+ tokenizer: string;
62
+ graph_revision: number;
63
+ blocks: readonly LocalEvidenceBlock[];
64
+ stats: LocalSelectionStats;
65
+ }>;
66
+ export type LocalContextResult = Readonly<{
67
+ snapshot: LocalContextSnapshot;
68
+ rendered: string;
69
+ }>;
70
+ export type LocalContextDelta = Readonly<{
71
+ base_id: string;
72
+ target_id: string;
73
+ graph_revision: number;
74
+ stats: LocalSelectionStats;
75
+ order: readonly string[];
76
+ added: readonly LocalEvidenceBlock[];
77
+ }>;
78
+ export type LocalSourceUpdate = Readonly<{
79
+ inserted: number;
80
+ replaced: number;
81
+ removed: number;
82
+ unchanged: number;
83
+ revision: number;
84
+ }>;
85
+ export type LocalGraphStats = Readonly<{
86
+ documents: number;
87
+ revision: number;
88
+ cache: Readonly<{
89
+ hits: number;
90
+ misses: number;
91
+ entries: number;
92
+ text_bytes: number;
93
+ }>;
94
+ }>;
95
+ //# sourceMappingURL=context-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-types.d.ts","sourceRoot":"","sources":["../src/context-types.ts"],"names":[],"mappings":"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","sourcesContent":["/** Local graph wire values; these do not replace the frozen remote Context ABI. */\nexport type LocalEdgeKind = \"requires\" | \"contradicts\" | \"supports\" | \"related\";\nexport type LocalDocument = Readonly<{id: string; source: string; text: string; start_line?: number}>;\nexport type LocalContextLimits = Readonly<{\n max_documents?: number; max_document_bytes?: number; max_total_bytes?: number;\n max_edges_per_document?: number; max_edges?: number; cache_entries?: number; cache_text_bytes?: number;\n}>;\nexport type LocalContextRequest = Readonly<{\n query?: string; max_tokens?: number; reserve_tokens?: number; required?: readonly string[];\n allowed?: readonly string[] | null; semantic_candidates?: readonly string[]; policy_revision?: string;\n max_candidates?: number; max_blocks?: number; graph_depth?: number; evidence_per_term?: number;\n excerpt_mode?: \"full\" | \"query_windows\";\n}>;\nexport type LocalCitation = Readonly<{\n node_id: string; source: string; document_digest: string; start_line: number; end_line: number;\n}>;\nexport type LocalEvidenceBlock = Readonly<{text: string; citations: readonly LocalCitation[]}>;\nexport type LocalSelectionStats = Readonly<{\n documents: number; lexical_matches: number; candidates: number; selected_blocks: number;\n selected_sources: number; query_terms: number; covered_terms: number; rendered_tokens: number;\n available_tokens: number; unavailable_roots: number; limit_rejected_roots: number; budget_rejected_roots: number;\n}>;\nexport type LocalContextSnapshot = Readonly<{\n id: string; domain: string; policy_revision: string; tokenizer: string; graph_revision: number;\n blocks: readonly LocalEvidenceBlock[]; stats: LocalSelectionStats;\n}>;\nexport type LocalContextResult = Readonly<{snapshot: LocalContextSnapshot; rendered: string}>;\nexport type LocalContextDelta = Readonly<{\n base_id: string; target_id: string; graph_revision: number; stats: LocalSelectionStats;\n order: readonly string[]; added: readonly LocalEvidenceBlock[];\n}>;\nexport type LocalSourceUpdate = Readonly<{\n inserted: number; replaced: number; removed: number; unchanged: number; revision: number;\n}>;\nexport type LocalGraphStats = Readonly<{\n documents: number; revision: number;\n cache: Readonly<{hits: number; misses: number; entries: number; text_bytes: number}>;\n}>;\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=context-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-types.js","sourceRoot":"","sources":["../src/context-types.ts"],"names":[],"mappings":"","sourcesContent":["/** Local graph wire values; these do not replace the frozen remote Context ABI. */\nexport type LocalEdgeKind = \"requires\" | \"contradicts\" | \"supports\" | \"related\";\nexport type LocalDocument = Readonly<{id: string; source: string; text: string; start_line?: number}>;\nexport type LocalContextLimits = Readonly<{\n max_documents?: number; max_document_bytes?: number; max_total_bytes?: number;\n max_edges_per_document?: number; max_edges?: number; cache_entries?: number; cache_text_bytes?: number;\n}>;\nexport type LocalContextRequest = Readonly<{\n query?: string; max_tokens?: number; reserve_tokens?: number; required?: readonly string[];\n allowed?: readonly string[] | null; semantic_candidates?: readonly string[]; policy_revision?: string;\n max_candidates?: number; max_blocks?: number; graph_depth?: number; evidence_per_term?: number;\n excerpt_mode?: \"full\" | \"query_windows\";\n}>;\nexport type LocalCitation = Readonly<{\n node_id: string; source: string; document_digest: string; start_line: number; end_line: number;\n}>;\nexport type LocalEvidenceBlock = Readonly<{text: string; citations: readonly LocalCitation[]}>;\nexport type LocalSelectionStats = Readonly<{\n documents: number; lexical_matches: number; candidates: number; selected_blocks: number;\n selected_sources: number; query_terms: number; covered_terms: number; rendered_tokens: number;\n available_tokens: number; unavailable_roots: number; limit_rejected_roots: number; budget_rejected_roots: number;\n}>;\nexport type LocalContextSnapshot = Readonly<{\n id: string; domain: string; policy_revision: string; tokenizer: string; graph_revision: number;\n blocks: readonly LocalEvidenceBlock[]; stats: LocalSelectionStats;\n}>;\nexport type LocalContextResult = Readonly<{snapshot: LocalContextSnapshot; rendered: string}>;\nexport type LocalContextDelta = Readonly<{\n base_id: string; target_id: string; graph_revision: number; stats: LocalSelectionStats;\n order: readonly string[]; added: readonly LocalEvidenceBlock[];\n}>;\nexport type LocalSourceUpdate = Readonly<{\n inserted: number; replaced: number; removed: number; unchanged: number; revision: number;\n}>;\nexport type LocalGraphStats = Readonly<{\n documents: number; revision: number;\n cache: Readonly<{hits: number; misses: number; entries: number; text_bytes: number}>;\n}>;\n"]}
@@ -0,0 +1,56 @@
1
+ import type { LocalContextDelta, LocalContextLimits, LocalContextRequest, LocalContextResult, LocalContextSnapshot, LocalDocument, LocalEdgeKind, LocalGraphStats, LocalSourceUpdate } from "./context-types.js";
2
+ export declare const LOCAL_CONTEXT_PROTOCOL: "cigar.context-worker.v1";
3
+ export declare const LOCAL_CONTEXT_CORE_VERSION: "0.10.0-beta.1";
4
+ export declare class LocalContextError extends Error {
5
+ readonly code: string;
6
+ constructor(code: string);
7
+ }
8
+ export type LocalContextOptions = Readonly<{
9
+ /** Explicit trusted executable; never searched in PATH or downloaded. */
10
+ workerPath?: string;
11
+ limits?: LocalContextLimits;
12
+ /** Active exchange deadline, including pipe writes. Queue capacity is separately bounded. */
13
+ timeoutMs?: number;
14
+ /** Includes the active request; bounded to 1..128. Default 32. */
15
+ maxPending?: number;
16
+ }>;
17
+ /** One persistent Rust graph/cache per privacy domain. Use await using or close().
18
+ * No automatic retry/restart: transport failure makes mutation outcome uncertain.
19
+ * Calls are serialized, with bounded pending count and exact-input byte limits.
20
+ */
21
+ export declare class LocalContextGraph implements AsyncDisposable {
22
+ private readonly child;
23
+ private readonly exited;
24
+ private readonly timeoutMs;
25
+ private readonly maxPending;
26
+ private closed;
27
+ private sequence;
28
+ private queued;
29
+ private queuedBytes;
30
+ private tail;
31
+ private pending;
32
+ private fragments;
33
+ private received;
34
+ private constructor();
35
+ static create(domain: string, options?: LocalContextOptions): Promise<LocalContextGraph>;
36
+ private fail;
37
+ private receive;
38
+ private call;
39
+ upsert(document: LocalDocument): Promise<boolean>;
40
+ /** Atomic; [] withdraws a source. Hard edges remain and fail closed until explicitly repaired. */
41
+ replaceSource(source: string, documents: readonly LocalDocument[]): Promise<LocalSourceUpdate>;
42
+ remove(nodeId: string): Promise<boolean>;
43
+ link(from: string, to: string, kind: LocalEdgeKind): Promise<boolean>;
44
+ unlink(from: string, to: string, kind: LocalEdgeKind): Promise<boolean>;
45
+ /** Rust-rendered context is ordinary data, not an instruction or authorization grant. */
46
+ compile(request: LocalContextRequest): Promise<LocalContextResult>;
47
+ chunks(document: LocalDocument, maxLines: number, overlapLines?: number): Promise<LocalDocument[]>;
48
+ verify(snapshot: LocalContextSnapshot): Promise<LocalContextResult>;
49
+ delta(base: LocalContextSnapshot, target: LocalContextSnapshot): Promise<LocalContextDelta>;
50
+ applyDelta(base: LocalContextSnapshot, delta: LocalContextDelta): Promise<LocalContextResult>;
51
+ stats(): Promise<LocalGraphStats>;
52
+ clearCache(): Promise<null>;
53
+ close(): Promise<void>;
54
+ [Symbol.asyncDispose](): Promise<void>;
55
+ }
56
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport type { ChildProcessByStdio } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { readFileSync, statSync } from \"node:fs\";\nimport { isAbsolute } from \"node:path\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { fileURLToPath } from \"node:url\";\nimport type {\n LocalContextDelta, LocalContextLimits, LocalContextRequest, LocalContextResult, LocalContextSnapshot,\n LocalDocument, LocalEdgeKind, LocalGraphStats, LocalSourceUpdate,\n} from \"./context-types.js\";\n\nexport const LOCAL_CONTEXT_PROTOCOL = \"cigar.context-worker.v1\" as const;\nexport const LOCAL_CONTEXT_CORE_VERSION = \"0.10.0-beta.1\" as const;\nconst MAX_FRAME = 32 * 1024 * 1024;\nconst MAX_RESPONSE = 64 * 1024 * 1024;\nconst CORE_ERRORS = new Set([\"InvalidInput\", \"LimitExceeded\", \"RequiredUnavailable\", \"BudgetUnsatisfiable\",\n \"Tokenizer\", \"Integrity\", \"BaseMismatch\"]);\n\nexport class LocalContextError extends Error {\n constructor(readonly code: string) {\n super(`local context error: ${code}`);\n this.name = \"LocalContextError\";\n }\n}\n\nexport type LocalContextOptions = Readonly<{\n /** Explicit trusted executable; never searched in PATH or downloaded. */\n workerPath?: string;\n limits?: LocalContextLimits;\n /** Active exchange deadline, including pipe writes. Queue capacity is separately bounded. */\n timeoutMs?: number;\n /** Includes the active request; bounded to 1..128. Default 32. */\n maxPending?: number;\n}>;\n\nfunction bundledWorker(): string {\n const directory = new URL(`../native/${process.platform}-${process.arch}/`, import.meta.url);\n const binary = new URL(process.platform === \"win32\" ? \"cigar-context-worker.exe\" : \"cigar-context-worker\", directory);\n let manifest: Record<string, unknown>;\n let digest: string;\n try {\n manifest = JSON.parse(readFileSync(new URL(\"manifest.json\", directory), \"utf8\")) as Record<string, unknown>;\n digest = createHash(\"sha256\").update(readFileSync(binary)).digest(\"hex\");\n } catch { throw new LocalContextError(\"WorkerUnavailable\"); }\n if (!manifest || manifest.protocol !== LOCAL_CONTEXT_PROTOCOL || manifest.core_version !== LOCAL_CONTEXT_CORE_VERSION ||\n manifest.sha256 !== digest) throw new LocalContextError(\"WorkerIntegrity\");\n return fileURLToPath(binary);\n}\n\ntype Pending = {id: number; resolve: (value: unknown) => void; reject: (error: Error) => void; timer: NodeJS.Timeout};\n\n/** One persistent Rust graph/cache per privacy domain. Use await using or close().\n * No automatic retry/restart: transport failure makes mutation outcome uncertain.\n * Calls are serialized, with bounded pending count and exact-input byte limits.\n */\nexport class LocalContextGraph implements AsyncDisposable {\n private readonly child: ChildProcessByStdio<Writable, Readable, null>;\n private readonly exited: Promise<void>;\n private readonly timeoutMs: number;\n private readonly maxPending: number;\n private closed = false;\n private sequence = 0;\n private queued = 0;\n private queuedBytes = 0;\n private tail: Promise<unknown> = Promise.resolve();\n private pending: Pending | undefined;\n private fragments: Buffer[] = [];\n private received = 0;\n\n private constructor(options: LocalContextOptions) {\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.maxPending = options.maxPending ?? 32;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0 || this.timeoutMs > 2_147_483_647 ||\n !Number.isInteger(this.maxPending) || this.maxPending < 1 || this.maxPending > 128) {\n throw new LocalContextError(\"InvalidInput\");\n }\n const binary = options.workerPath ?? bundledWorker();\n try {\n if (!isAbsolute(binary) || !statSync(binary).isFile()) throw new Error();\n } catch { throw new LocalContextError(\"WorkerUnavailable\"); }\n this.child = spawn(binary, [], {stdio: [\"pipe\", \"pipe\", \"ignore\"], shell: false, windowsHide: true});\n this.exited = new Promise((resolve) => { this.child.once(\"close\", resolve); });\n this.child.on(\"error\", () => this.fail(\"WorkerUnavailable\"));\n this.child.on(\"exit\", () => this.fail(\"Transport\"));\n this.child.stdin.on(\"error\", () => this.fail(\"Transport\"));\n this.child.stdout.on(\"error\", () => this.fail(\"Transport\"));\n this.child.stdout.on(\"data\", (data: Buffer) => this.receive(data));\n }\n\n static async create(domain: string, options: LocalContextOptions = {}): Promise<LocalContextGraph> {\n const graph = new LocalContextGraph(options);\n try {\n const hello = await graph.call<Record<string, unknown>>({op: \"init\", domain, limits: options.limits ?? {}});\n if (!hello || hello.protocol !== LOCAL_CONTEXT_PROTOCOL || hello.core_version !== LOCAL_CONTEXT_CORE_VERSION ||\n hello.max_frame_bytes !== MAX_FRAME || hello.max_response_bytes !== MAX_RESPONSE) {\n throw new LocalContextError(\"IncompatibleWorker\");\n }\n return graph;\n } catch (error) { await graph.close(); throw error; }\n }\n\n private fail(code: string): void {\n this.closed = true;\n const pending = this.pending;\n this.pending = undefined;\n if (pending) { clearTimeout(pending.timer); pending.reject(new LocalContextError(code)); }\n this.fragments = [];\n this.received = 0;\n this.child.kill(\"SIGKILL\");\n }\n\n private receive(data: Buffer): void {\n if (this.closed) return;\n if (!this.pending) { this.fail(\"Transport\"); return; }\n this.received += data.length;\n if (this.received > MAX_RESPONSE) { this.fail(\"Transport\"); return; }\n this.fragments.push(data);\n const newline = data.indexOf(10);\n if (newline < 0) return;\n if (newline !== data.length - 1) { this.fail(\"Transport\"); return; }\n const pending = this.pending;\n let reply: {id: number; ok: boolean; result?: unknown; error?: string};\n try {\n // Fatal UTF-8 decoding avoids silently replacing malformed worker bytes.\n const text = new TextDecoder(\"utf-8\", {fatal: true}).decode(Buffer.concat(this.fragments, this.received));\n reply = JSON.parse(text) as typeof reply;\n if (!reply || reply.id !== pending.id || typeof reply.ok !== \"boolean\" ||\n (reply.ok ? !(\"result\" in reply) : !CORE_ERRORS.has(reply.error ?? \"\"))) throw new Error();\n } catch { this.fail(\"Transport\"); return; }\n this.pending = undefined;\n clearTimeout(pending.timer);\n this.fragments = [];\n this.received = 0;\n if (reply.ok) pending.resolve(reply.result);\n else pending.reject(new LocalContextError(reply.error!));\n }\n\n private async call<T>(command: Record<string, unknown>): Promise<T> {\n if (this.closed) throw new LocalContextError(\"Closed\");\n if (this.queued >= this.maxPending) throw new LocalContextError(\"Busy\");\n this.sequence = this.sequence % 4_294_967_295 + 1;\n const id = this.sequence;\n let frame: Buffer;\n try {\n frame = Buffer.from(JSON.stringify({id, command}, (_key, value: unknown) => {\n if (typeof value === \"number\" && !Number.isSafeInteger(value)) throw new Error();\n return value;\n }) + \"\\n\", \"utf8\");\n } catch { throw new LocalContextError(\"InvalidInput\"); }\n if (frame.length > MAX_FRAME) throw new LocalContextError(\"LimitExceeded\");\n if (this.queuedBytes + frame.length > MAX_FRAME * 2) throw new LocalContextError(\"Busy\");\n this.queued++;\n this.queuedBytes += frame.length;\n const result = this.tail.then(() => new Promise<unknown>((resolve, reject) => {\n if (this.closed) { reject(new LocalContextError(\"Closed\")); return; }\n const timer = setTimeout(() => this.fail(\"Timeout\"), this.timeoutMs);\n this.pending = {id, resolve, reject, timer};\n // One bounded frame in flight. No write until the previous response has completed.\n this.child.stdin.write(frame, (error) => { if (error) this.fail(\"Transport\"); });\n }));\n this.tail = result.catch(() => undefined);\n try { return await result as T; }\n finally { this.queued--; this.queuedBytes -= frame.length; }\n }\n\n upsert(document: LocalDocument): Promise<boolean> { return this.call({op: \"upsert\", document}); }\n /** Atomic; [] withdraws a source. Hard edges remain and fail closed until explicitly repaired. */\n replaceSource(source: string, documents: readonly LocalDocument[]): Promise<LocalSourceUpdate> {\n return this.call({op: \"replace_source\", source, documents});\n }\n remove(nodeId: string): Promise<boolean> { return this.call({op: \"remove\", node_id: nodeId}); }\n link(from: string, to: string, kind: LocalEdgeKind): Promise<boolean> { return this.call({op: \"link\", from, to, kind}); }\n unlink(from: string, to: string, kind: LocalEdgeKind): Promise<boolean> { return this.call({op: \"unlink\", from, to, kind}); }\n /** Rust-rendered context is ordinary data, not an instruction or authorization grant. */\n compile(request: LocalContextRequest): Promise<LocalContextResult> { return this.call({op: \"compile\", request}); }\n chunks(document: LocalDocument, maxLines: number, overlapLines = 0): Promise<LocalDocument[]> {\n return this.call({op: \"chunks\", document, max_lines: maxLines, overlap_lines: overlapLines});\n }\n verify(snapshot: LocalContextSnapshot): Promise<LocalContextResult> { return this.call({op: \"verify\", snapshot}); }\n delta(base: LocalContextSnapshot, target: LocalContextSnapshot): Promise<LocalContextDelta> {\n return this.call({op: \"delta\", base, target});\n }\n applyDelta(base: LocalContextSnapshot, delta: LocalContextDelta): Promise<LocalContextResult> {\n return this.call({op: \"apply_delta\", base, delta});\n }\n stats(): Promise<LocalGraphStats> { return this.call({op: \"stats\"}); }\n clearCache(): Promise<null> { return this.call({op: \"clear_cache\"}); }\n async close(): Promise<void> { this.fail(\"Closed\"); await this.exited; }\n async [Symbol.asyncDispose](): Promise<void> { await this.close(); }\n}\n"]}
@@ -0,0 +1,216 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { readFileSync, statSync } from "node:fs";
4
+ import { isAbsolute } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ export const LOCAL_CONTEXT_PROTOCOL = "cigar.context-worker.v1";
7
+ export const LOCAL_CONTEXT_CORE_VERSION = "0.10.0-beta.1";
8
+ const MAX_FRAME = 32 * 1024 * 1024;
9
+ const MAX_RESPONSE = 64 * 1024 * 1024;
10
+ const CORE_ERRORS = new Set(["InvalidInput", "LimitExceeded", "RequiredUnavailable", "BudgetUnsatisfiable",
11
+ "Tokenizer", "Integrity", "BaseMismatch"]);
12
+ export class LocalContextError extends Error {
13
+ code;
14
+ constructor(code) {
15
+ super(`local context error: ${code}`);
16
+ this.code = code;
17
+ this.name = "LocalContextError";
18
+ }
19
+ }
20
+ function bundledWorker() {
21
+ const directory = new URL(`../native/${process.platform}-${process.arch}/`, import.meta.url);
22
+ const binary = new URL(process.platform === "win32" ? "cigar-context-worker.exe" : "cigar-context-worker", directory);
23
+ let manifest;
24
+ let digest;
25
+ try {
26
+ manifest = JSON.parse(readFileSync(new URL("manifest.json", directory), "utf8"));
27
+ digest = createHash("sha256").update(readFileSync(binary)).digest("hex");
28
+ }
29
+ catch {
30
+ throw new LocalContextError("WorkerUnavailable");
31
+ }
32
+ if (!manifest || manifest.protocol !== LOCAL_CONTEXT_PROTOCOL || manifest.core_version !== LOCAL_CONTEXT_CORE_VERSION ||
33
+ manifest.sha256 !== digest)
34
+ throw new LocalContextError("WorkerIntegrity");
35
+ return fileURLToPath(binary);
36
+ }
37
+ /** One persistent Rust graph/cache per privacy domain. Use await using or close().
38
+ * No automatic retry/restart: transport failure makes mutation outcome uncertain.
39
+ * Calls are serialized, with bounded pending count and exact-input byte limits.
40
+ */
41
+ export class LocalContextGraph {
42
+ child;
43
+ exited;
44
+ timeoutMs;
45
+ maxPending;
46
+ closed = false;
47
+ sequence = 0;
48
+ queued = 0;
49
+ queuedBytes = 0;
50
+ tail = Promise.resolve();
51
+ pending;
52
+ fragments = [];
53
+ received = 0;
54
+ constructor(options) {
55
+ this.timeoutMs = options.timeoutMs ?? 30_000;
56
+ this.maxPending = options.maxPending ?? 32;
57
+ if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0 || this.timeoutMs > 2_147_483_647 ||
58
+ !Number.isInteger(this.maxPending) || this.maxPending < 1 || this.maxPending > 128) {
59
+ throw new LocalContextError("InvalidInput");
60
+ }
61
+ const binary = options.workerPath ?? bundledWorker();
62
+ try {
63
+ if (!isAbsolute(binary) || !statSync(binary).isFile())
64
+ throw new Error();
65
+ }
66
+ catch {
67
+ throw new LocalContextError("WorkerUnavailable");
68
+ }
69
+ this.child = spawn(binary, [], { stdio: ["pipe", "pipe", "ignore"], shell: false, windowsHide: true });
70
+ this.exited = new Promise((resolve) => { this.child.once("close", resolve); });
71
+ this.child.on("error", () => this.fail("WorkerUnavailable"));
72
+ this.child.on("exit", () => this.fail("Transport"));
73
+ this.child.stdin.on("error", () => this.fail("Transport"));
74
+ this.child.stdout.on("error", () => this.fail("Transport"));
75
+ this.child.stdout.on("data", (data) => this.receive(data));
76
+ }
77
+ static async create(domain, options = {}) {
78
+ const graph = new LocalContextGraph(options);
79
+ try {
80
+ const hello = await graph.call({ op: "init", domain, limits: options.limits ?? {} });
81
+ if (!hello || hello.protocol !== LOCAL_CONTEXT_PROTOCOL || hello.core_version !== LOCAL_CONTEXT_CORE_VERSION ||
82
+ hello.max_frame_bytes !== MAX_FRAME || hello.max_response_bytes !== MAX_RESPONSE) {
83
+ throw new LocalContextError("IncompatibleWorker");
84
+ }
85
+ return graph;
86
+ }
87
+ catch (error) {
88
+ await graph.close();
89
+ throw error;
90
+ }
91
+ }
92
+ fail(code) {
93
+ this.closed = true;
94
+ const pending = this.pending;
95
+ this.pending = undefined;
96
+ if (pending) {
97
+ clearTimeout(pending.timer);
98
+ pending.reject(new LocalContextError(code));
99
+ }
100
+ this.fragments = [];
101
+ this.received = 0;
102
+ this.child.kill("SIGKILL");
103
+ }
104
+ receive(data) {
105
+ if (this.closed)
106
+ return;
107
+ if (!this.pending) {
108
+ this.fail("Transport");
109
+ return;
110
+ }
111
+ this.received += data.length;
112
+ if (this.received > MAX_RESPONSE) {
113
+ this.fail("Transport");
114
+ return;
115
+ }
116
+ this.fragments.push(data);
117
+ const newline = data.indexOf(10);
118
+ if (newline < 0)
119
+ return;
120
+ if (newline !== data.length - 1) {
121
+ this.fail("Transport");
122
+ return;
123
+ }
124
+ const pending = this.pending;
125
+ let reply;
126
+ try {
127
+ // Fatal UTF-8 decoding avoids silently replacing malformed worker bytes.
128
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(this.fragments, this.received));
129
+ reply = JSON.parse(text);
130
+ if (!reply || reply.id !== pending.id || typeof reply.ok !== "boolean" ||
131
+ (reply.ok ? !("result" in reply) : !CORE_ERRORS.has(reply.error ?? "")))
132
+ throw new Error();
133
+ }
134
+ catch {
135
+ this.fail("Transport");
136
+ return;
137
+ }
138
+ this.pending = undefined;
139
+ clearTimeout(pending.timer);
140
+ this.fragments = [];
141
+ this.received = 0;
142
+ if (reply.ok)
143
+ pending.resolve(reply.result);
144
+ else
145
+ pending.reject(new LocalContextError(reply.error));
146
+ }
147
+ async call(command) {
148
+ if (this.closed)
149
+ throw new LocalContextError("Closed");
150
+ if (this.queued >= this.maxPending)
151
+ throw new LocalContextError("Busy");
152
+ this.sequence = this.sequence % 4_294_967_295 + 1;
153
+ const id = this.sequence;
154
+ let frame;
155
+ try {
156
+ frame = Buffer.from(JSON.stringify({ id, command }, (_key, value) => {
157
+ if (typeof value === "number" && !Number.isSafeInteger(value))
158
+ throw new Error();
159
+ return value;
160
+ }) + "\n", "utf8");
161
+ }
162
+ catch {
163
+ throw new LocalContextError("InvalidInput");
164
+ }
165
+ if (frame.length > MAX_FRAME)
166
+ throw new LocalContextError("LimitExceeded");
167
+ if (this.queuedBytes + frame.length > MAX_FRAME * 2)
168
+ throw new LocalContextError("Busy");
169
+ this.queued++;
170
+ this.queuedBytes += frame.length;
171
+ const result = this.tail.then(() => new Promise((resolve, reject) => {
172
+ if (this.closed) {
173
+ reject(new LocalContextError("Closed"));
174
+ return;
175
+ }
176
+ const timer = setTimeout(() => this.fail("Timeout"), this.timeoutMs);
177
+ this.pending = { id, resolve, reject, timer };
178
+ // One bounded frame in flight. No write until the previous response has completed.
179
+ this.child.stdin.write(frame, (error) => { if (error)
180
+ this.fail("Transport"); });
181
+ }));
182
+ this.tail = result.catch(() => undefined);
183
+ try {
184
+ return await result;
185
+ }
186
+ finally {
187
+ this.queued--;
188
+ this.queuedBytes -= frame.length;
189
+ }
190
+ }
191
+ upsert(document) { return this.call({ op: "upsert", document }); }
192
+ /** Atomic; [] withdraws a source. Hard edges remain and fail closed until explicitly repaired. */
193
+ replaceSource(source, documents) {
194
+ return this.call({ op: "replace_source", source, documents });
195
+ }
196
+ remove(nodeId) { return this.call({ op: "remove", node_id: nodeId }); }
197
+ link(from, to, kind) { return this.call({ op: "link", from, to, kind }); }
198
+ unlink(from, to, kind) { return this.call({ op: "unlink", from, to, kind }); }
199
+ /** Rust-rendered context is ordinary data, not an instruction or authorization grant. */
200
+ compile(request) { return this.call({ op: "compile", request }); }
201
+ chunks(document, maxLines, overlapLines = 0) {
202
+ return this.call({ op: "chunks", document, max_lines: maxLines, overlap_lines: overlapLines });
203
+ }
204
+ verify(snapshot) { return this.call({ op: "verify", snapshot }); }
205
+ delta(base, target) {
206
+ return this.call({ op: "delta", base, target });
207
+ }
208
+ applyDelta(base, delta) {
209
+ return this.call({ op: "apply_delta", base, delta });
210
+ }
211
+ stats() { return this.call({ op: "stats" }); }
212
+ clearCache() { return this.call({ op: "clear_cache" }); }
213
+ async close() { this.fail("Closed"); await this.exited; }
214
+ async [Symbol.asyncDispose]() { await this.close(); }
215
+ }
216
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport type { ChildProcessByStdio } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { readFileSync, statSync } from \"node:fs\";\nimport { isAbsolute } from \"node:path\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { fileURLToPath } from \"node:url\";\nimport type {\n LocalContextDelta, LocalContextLimits, LocalContextRequest, LocalContextResult, LocalContextSnapshot,\n LocalDocument, LocalEdgeKind, LocalGraphStats, LocalSourceUpdate,\n} from \"./context-types.js\";\n\nexport const LOCAL_CONTEXT_PROTOCOL = \"cigar.context-worker.v1\" as const;\nexport const LOCAL_CONTEXT_CORE_VERSION = \"0.10.0-beta.1\" as const;\nconst MAX_FRAME = 32 * 1024 * 1024;\nconst MAX_RESPONSE = 64 * 1024 * 1024;\nconst CORE_ERRORS = new Set([\"InvalidInput\", \"LimitExceeded\", \"RequiredUnavailable\", \"BudgetUnsatisfiable\",\n \"Tokenizer\", \"Integrity\", \"BaseMismatch\"]);\n\nexport class LocalContextError extends Error {\n constructor(readonly code: string) {\n super(`local context error: ${code}`);\n this.name = \"LocalContextError\";\n }\n}\n\nexport type LocalContextOptions = Readonly<{\n /** Explicit trusted executable; never searched in PATH or downloaded. */\n workerPath?: string;\n limits?: LocalContextLimits;\n /** Active exchange deadline, including pipe writes. Queue capacity is separately bounded. */\n timeoutMs?: number;\n /** Includes the active request; bounded to 1..128. Default 32. */\n maxPending?: number;\n}>;\n\nfunction bundledWorker(): string {\n const directory = new URL(`../native/${process.platform}-${process.arch}/`, import.meta.url);\n const binary = new URL(process.platform === \"win32\" ? \"cigar-context-worker.exe\" : \"cigar-context-worker\", directory);\n let manifest: Record<string, unknown>;\n let digest: string;\n try {\n manifest = JSON.parse(readFileSync(new URL(\"manifest.json\", directory), \"utf8\")) as Record<string, unknown>;\n digest = createHash(\"sha256\").update(readFileSync(binary)).digest(\"hex\");\n } catch { throw new LocalContextError(\"WorkerUnavailable\"); }\n if (!manifest || manifest.protocol !== LOCAL_CONTEXT_PROTOCOL || manifest.core_version !== LOCAL_CONTEXT_CORE_VERSION ||\n manifest.sha256 !== digest) throw new LocalContextError(\"WorkerIntegrity\");\n return fileURLToPath(binary);\n}\n\ntype Pending = {id: number; resolve: (value: unknown) => void; reject: (error: Error) => void; timer: NodeJS.Timeout};\n\n/** One persistent Rust graph/cache per privacy domain. Use await using or close().\n * No automatic retry/restart: transport failure makes mutation outcome uncertain.\n * Calls are serialized, with bounded pending count and exact-input byte limits.\n */\nexport class LocalContextGraph implements AsyncDisposable {\n private readonly child: ChildProcessByStdio<Writable, Readable, null>;\n private readonly exited: Promise<void>;\n private readonly timeoutMs: number;\n private readonly maxPending: number;\n private closed = false;\n private sequence = 0;\n private queued = 0;\n private queuedBytes = 0;\n private tail: Promise<unknown> = Promise.resolve();\n private pending: Pending | undefined;\n private fragments: Buffer[] = [];\n private received = 0;\n\n private constructor(options: LocalContextOptions) {\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.maxPending = options.maxPending ?? 32;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0 || this.timeoutMs > 2_147_483_647 ||\n !Number.isInteger(this.maxPending) || this.maxPending < 1 || this.maxPending > 128) {\n throw new LocalContextError(\"InvalidInput\");\n }\n const binary = options.workerPath ?? bundledWorker();\n try {\n if (!isAbsolute(binary) || !statSync(binary).isFile()) throw new Error();\n } catch { throw new LocalContextError(\"WorkerUnavailable\"); }\n this.child = spawn(binary, [], {stdio: [\"pipe\", \"pipe\", \"ignore\"], shell: false, windowsHide: true});\n this.exited = new Promise((resolve) => { this.child.once(\"close\", resolve); });\n this.child.on(\"error\", () => this.fail(\"WorkerUnavailable\"));\n this.child.on(\"exit\", () => this.fail(\"Transport\"));\n this.child.stdin.on(\"error\", () => this.fail(\"Transport\"));\n this.child.stdout.on(\"error\", () => this.fail(\"Transport\"));\n this.child.stdout.on(\"data\", (data: Buffer) => this.receive(data));\n }\n\n static async create(domain: string, options: LocalContextOptions = {}): Promise<LocalContextGraph> {\n const graph = new LocalContextGraph(options);\n try {\n const hello = await graph.call<Record<string, unknown>>({op: \"init\", domain, limits: options.limits ?? {}});\n if (!hello || hello.protocol !== LOCAL_CONTEXT_PROTOCOL || hello.core_version !== LOCAL_CONTEXT_CORE_VERSION ||\n hello.max_frame_bytes !== MAX_FRAME || hello.max_response_bytes !== MAX_RESPONSE) {\n throw new LocalContextError(\"IncompatibleWorker\");\n }\n return graph;\n } catch (error) { await graph.close(); throw error; }\n }\n\n private fail(code: string): void {\n this.closed = true;\n const pending = this.pending;\n this.pending = undefined;\n if (pending) { clearTimeout(pending.timer); pending.reject(new LocalContextError(code)); }\n this.fragments = [];\n this.received = 0;\n this.child.kill(\"SIGKILL\");\n }\n\n private receive(data: Buffer): void {\n if (this.closed) return;\n if (!this.pending) { this.fail(\"Transport\"); return; }\n this.received += data.length;\n if (this.received > MAX_RESPONSE) { this.fail(\"Transport\"); return; }\n this.fragments.push(data);\n const newline = data.indexOf(10);\n if (newline < 0) return;\n if (newline !== data.length - 1) { this.fail(\"Transport\"); return; }\n const pending = this.pending;\n let reply: {id: number; ok: boolean; result?: unknown; error?: string};\n try {\n // Fatal UTF-8 decoding avoids silently replacing malformed worker bytes.\n const text = new TextDecoder(\"utf-8\", {fatal: true}).decode(Buffer.concat(this.fragments, this.received));\n reply = JSON.parse(text) as typeof reply;\n if (!reply || reply.id !== pending.id || typeof reply.ok !== \"boolean\" ||\n (reply.ok ? !(\"result\" in reply) : !CORE_ERRORS.has(reply.error ?? \"\"))) throw new Error();\n } catch { this.fail(\"Transport\"); return; }\n this.pending = undefined;\n clearTimeout(pending.timer);\n this.fragments = [];\n this.received = 0;\n if (reply.ok) pending.resolve(reply.result);\n else pending.reject(new LocalContextError(reply.error!));\n }\n\n private async call<T>(command: Record<string, unknown>): Promise<T> {\n if (this.closed) throw new LocalContextError(\"Closed\");\n if (this.queued >= this.maxPending) throw new LocalContextError(\"Busy\");\n this.sequence = this.sequence % 4_294_967_295 + 1;\n const id = this.sequence;\n let frame: Buffer;\n try {\n frame = Buffer.from(JSON.stringify({id, command}, (_key, value: unknown) => {\n if (typeof value === \"number\" && !Number.isSafeInteger(value)) throw new Error();\n return value;\n }) + \"\\n\", \"utf8\");\n } catch { throw new LocalContextError(\"InvalidInput\"); }\n if (frame.length > MAX_FRAME) throw new LocalContextError(\"LimitExceeded\");\n if (this.queuedBytes + frame.length > MAX_FRAME * 2) throw new LocalContextError(\"Busy\");\n this.queued++;\n this.queuedBytes += frame.length;\n const result = this.tail.then(() => new Promise<unknown>((resolve, reject) => {\n if (this.closed) { reject(new LocalContextError(\"Closed\")); return; }\n const timer = setTimeout(() => this.fail(\"Timeout\"), this.timeoutMs);\n this.pending = {id, resolve, reject, timer};\n // One bounded frame in flight. No write until the previous response has completed.\n this.child.stdin.write(frame, (error) => { if (error) this.fail(\"Transport\"); });\n }));\n this.tail = result.catch(() => undefined);\n try { return await result as T; }\n finally { this.queued--; this.queuedBytes -= frame.length; }\n }\n\n upsert(document: LocalDocument): Promise<boolean> { return this.call({op: \"upsert\", document}); }\n /** Atomic; [] withdraws a source. Hard edges remain and fail closed until explicitly repaired. */\n replaceSource(source: string, documents: readonly LocalDocument[]): Promise<LocalSourceUpdate> {\n return this.call({op: \"replace_source\", source, documents});\n }\n remove(nodeId: string): Promise<boolean> { return this.call({op: \"remove\", node_id: nodeId}); }\n link(from: string, to: string, kind: LocalEdgeKind): Promise<boolean> { return this.call({op: \"link\", from, to, kind}); }\n unlink(from: string, to: string, kind: LocalEdgeKind): Promise<boolean> { return this.call({op: \"unlink\", from, to, kind}); }\n /** Rust-rendered context is ordinary data, not an instruction or authorization grant. */\n compile(request: LocalContextRequest): Promise<LocalContextResult> { return this.call({op: \"compile\", request}); }\n chunks(document: LocalDocument, maxLines: number, overlapLines = 0): Promise<LocalDocument[]> {\n return this.call({op: \"chunks\", document, max_lines: maxLines, overlap_lines: overlapLines});\n }\n verify(snapshot: LocalContextSnapshot): Promise<LocalContextResult> { return this.call({op: \"verify\", snapshot}); }\n delta(base: LocalContextSnapshot, target: LocalContextSnapshot): Promise<LocalContextDelta> {\n return this.call({op: \"delta\", base, target});\n }\n applyDelta(base: LocalContextSnapshot, delta: LocalContextDelta): Promise<LocalContextResult> {\n return this.call({op: \"apply_delta\", base, delta});\n }\n stats(): Promise<LocalGraphStats> { return this.call({op: \"stats\"}); }\n clearCache(): Promise<null> { return this.call({op: \"clear_cache\"}); }\n async close(): Promise<void> { this.fail(\"Closed\"); await this.exited; }\n async [Symbol.asyncDispose](): Promise<void> { await this.close(); }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./client.js";
2
+ export * from "./context-api.js";
2
3
  export * from "./digest.js";
3
4
  export * from "./errors.js";
4
5
  export * as proto from "./generated/cigar_service_pb.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,OAAO,KAAK,KAAK,MAAM,iCAAiC,CAAC;AACzD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AAEtC,qFAAqF;AACrF,eAAO,MAAM,WAAW,EAAG,kBAA2B,CAAC","sourcesContent":["export * from \"./client.js\";\nexport * from \"./digest.js\";\nexport * from \"./errors.js\";\nexport * as proto from \"./generated/cigar_service_pb.js\";\nexport * from \"./generated/operations.js\";\nexport * from \"./generated/errors.js\";\nexport * from \"./generated/models.js\";\nexport * from \"./idempotency.js\";\nexport * from \"./types.js\";\nexport * from \"./workflow-session.js\";\n\n/** Exact Protobuf package name of the stable Context ABI implemented by this SDK. */\nexport const CONTEXT_ABI = \"cigar.context.v1\" as const;\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,OAAO,KAAK,KAAK,MAAM,iCAAiC,CAAC;AACzD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AAEtC,qFAAqF;AACrF,eAAO,MAAM,WAAW,EAAG,kBAA2B,CAAC","sourcesContent":["export * from \"./client.js\";\nexport * from \"./context-api.js\";\nexport * from \"./digest.js\";\nexport * from \"./errors.js\";\nexport * as proto from \"./generated/cigar_service_pb.js\";\nexport * from \"./generated/operations.js\";\nexport * from \"./generated/errors.js\";\nexport * from \"./generated/models.js\";\nexport * from \"./idempotency.js\";\nexport * from \"./types.js\";\nexport * from \"./workflow-session.js\";\n\n/** Exact Protobuf package name of the stable Context ABI implemented by this SDK. */\nexport const CONTEXT_ABI = \"cigar.context.v1\" as const;\n"]}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./client.js";
2
+ export * from "./context-api.js";
2
3
  export * from "./digest.js";
3
4
  export * from "./errors.js";
4
5
  export * as proto from "./generated/cigar_service_pb.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,OAAO,KAAK,KAAK,MAAM,iCAAiC,CAAC;AACzD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AAEtC,qFAAqF;AACrF,MAAM,CAAC,MAAM,WAAW,GAAG,kBAA2B,CAAC","sourcesContent":["export * from \"./client.js\";\nexport * from \"./digest.js\";\nexport * from \"./errors.js\";\nexport * as proto from \"./generated/cigar_service_pb.js\";\nexport * from \"./generated/operations.js\";\nexport * from \"./generated/errors.js\";\nexport * from \"./generated/models.js\";\nexport * from \"./idempotency.js\";\nexport * from \"./types.js\";\nexport * from \"./workflow-session.js\";\n\n/** Exact Protobuf package name of the stable Context ABI implemented by this SDK. */\nexport const CONTEXT_ABI = \"cigar.context.v1\" as const;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,OAAO,KAAK,KAAK,MAAM,iCAAiC,CAAC;AACzD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AAEtC,qFAAqF;AACrF,MAAM,CAAC,MAAM,WAAW,GAAG,kBAA2B,CAAC","sourcesContent":["export * from \"./client.js\";\nexport * from \"./context-api.js\";\nexport * from \"./digest.js\";\nexport * from \"./errors.js\";\nexport * as proto from \"./generated/cigar_service_pb.js\";\nexport * from \"./generated/operations.js\";\nexport * from \"./generated/errors.js\";\nexport * from \"./generated/models.js\";\nexport * from \"./idempotency.js\";\nexport * from \"./types.js\";\nexport * from \"./workflow-session.js\";\n\n/** Exact Protobuf package name of the stable Context ABI implemented by this SDK. */\nexport const CONTEXT_ABI = \"cigar.context.v1\" as const;\n"]}
package/dist/release.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "schema_version": "cigar.sdk-release.v1",
3
3
  "name": "@hol-org/cigar",
4
- "version": "0.9.4",
5
- "context_abi": "cigar.context.v1"
4
+ "version": "0.10.0-beta.1",
5
+ "context_abi": "cigar.context.v1",
6
+ "channel": "beta",
7
+ "local_context_core_version": "0.10.0-beta.1",
8
+ "local_context_protocol": "cigar.context-worker.v1"
6
9
  }