@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 +52 -3
- package/dist/context-api.d.ts +4 -0
- package/dist/context-api.d.ts.map +1 -0
- package/dist/context-api.js +4 -0
- package/dist/context-api.js.map +1 -0
- package/dist/context-types.d.ts +95 -0
- package/dist/context-types.d.ts.map +1 -0
- package/dist/context-types.js +2 -0
- package/dist/context-types.js.map +1 -0
- package/dist/context.d.ts +56 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +216 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/release.json +5 -2
- package/native/darwin-arm64/THIRD_PARTY_NOTICES.txt +6836 -0
- package/native/darwin-arm64/cigar-context-worker +0 -0
- package/native/darwin-arm64/dependencies.json +212 -0
- package/native/darwin-arm64/manifest.json +8 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# `@hol-org/cigar`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
0.10.0 beta 1 for Node.js 24 ESM applications:
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
|
-
npm install
|
|
6
|
+
npm install ./hol-org-cigar-0.10.0-beta.1.tgz
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"]}
|
package/dist/context.js
ADDED
|
@@ -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
package/dist/index.d.ts.map
CHANGED
|
@@ -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
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.
|
|
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
|
}
|