@znt/sdk-nodejs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,445 @@
1
+ # znt-sdk-nodejs
2
+
3
+ Dependency-free, Node.js-only client SDK for the `znt-core` JSON-RPC 2.0 IPC
4
+ API. The SDK uses one reusable Unix socket or Windows named-pipe connection,
5
+ supports concurrent requests, preserves structured RPC errors, and includes
6
+ TypeScript declarations.
7
+
8
+ Requirements: Node.js 18 or newer. The SDK owns the `znt-core` daemon lifecycle:
9
+ it reuses a running daemon or starts the installed binary when requested.
10
+
11
+ This package runs only in a trusted Node.js environment. It imports Node's
12
+ `node:net` module and cannot run directly in browsers, Web Workers, or Vue/React
13
+ client bundles. For a web application, use it in a local Node.js backend or an
14
+ Electron main/preload process and expose only the required operations to the
15
+ frontend.
16
+
17
+ ```text
18
+ Vue/browser -> local Node.js host -> znt-sdk-nodejs -> znt-core
19
+ ```
20
+
21
+ | Runtime | Supported |
22
+ |---|---|
23
+ | Node.js 18+ backend or local host | Yes |
24
+ | Electron main/preload process | Yes |
25
+ | Browser or Vue/React renderer | No |
26
+ | Web Worker | No |
27
+ | Deno or Bun | Not officially supported |
28
+
29
+ ## Install
30
+
31
+ For local development in this repository:
32
+
33
+ ```bash
34
+ cd znt-sdk-nodejs
35
+ npm install
36
+ npm run build
37
+ ```
38
+
39
+ From another local package:
40
+
41
+ ```bash
42
+ npm install ../znt-sdk-nodejs
43
+ ```
44
+
45
+ ## Quick start
46
+
47
+ Start or reuse the daemon from Node.js:
48
+
49
+ ```js
50
+ import { ZntClient } from "znt-sdk-nodejs";
51
+
52
+ const znt = new ZntClient();
53
+
54
+ try {
55
+ await znt.startCore();
56
+
57
+ const info = await znt.info();
58
+ console.log(info.languages);
59
+
60
+ const status = await znt.status();
61
+ console.log(status);
62
+
63
+ const results = await znt.semanticSearch({
64
+ query: "SemanticService.SearchWithOptions",
65
+ mode: "lexical",
66
+ limit: 10,
67
+ compact: true,
68
+ });
69
+ console.log(results);
70
+ } finally {
71
+ znt.disconnect();
72
+ }
73
+ ```
74
+
75
+ `semanticSearch()` normalizes the server's valid `null` no-results response to
76
+ an empty array.
77
+
78
+ `startCore()` is concurrency-safe per client instance. If no daemon answers at
79
+ the configured endpoint, it checks the managed installation and starts
80
+ `znt-core daemon`. It never downloads a binary automatically.
81
+
82
+ ## Managed core installation
83
+
84
+ The SDK checks a per-user installation directory, so application startup does
85
+ not require administrator privileges:
86
+
87
+ | Platform | Default directory |
88
+ |---|---|
89
+ | Linux | `$XDG_DATA_HOME/znt/bin`, or `~/.local/share/znt/bin` |
90
+ | macOS | `~/Library/Application Support/Znt/bin` |
91
+ | Windows | `%LOCALAPPDATA%\\Znt\\bin` |
92
+
93
+ The executable is named `znt-core` on Linux/macOS and `znt-core.exe` on
94
+ Windows. `ZNT_CORE_HOME` overrides the managed directory;
95
+ `ZNT_CORE_BINARY` overrides the complete executable path for development.
96
+
97
+ Daemon options can also be provided directly to `ZntClient`:
98
+
99
+ ```js
100
+ const znt = new ZntClient({
101
+ configPath: "/absolute/path/to/config.yaml",
102
+ daemonStartTimeoutMs: 10_000,
103
+ });
104
+ ```
105
+
106
+ Downloading is always a separate, explicit operation:
107
+
108
+ ```js
109
+ await znt.downloadCore(); // Latest release for the current OS and CPU architecture.
110
+ ```
111
+
112
+ The SDK reads the release manifest from `znt-app/core`, selects the binary for
113
+ the current OS and CPU architecture, verifies its SHA-256 checksum, makes it
114
+ executable on Unix, and moves it into the managed installation path. Downloading
115
+ the binary does not create or overwrite `config.yaml`; the MCP setup flow creates
116
+ the working YAML next to the managed binary. To
117
+ install a specific release, pass `{ version: "1.0" }`. The release source is fixed to
118
+ the official `znt-app/core` GitHub repository and cannot be overridden by a URL
119
+ or environment variable. Call `startCore()` separately after installation.
120
+
121
+ The complete lifecycle is explicit:
122
+
123
+ ```js
124
+ if (!(await znt.isCoreInstalled("1.0"))) {
125
+ await znt.downloadCore({ version: "1.0" });
126
+ }
127
+
128
+ const defaults = await znt.getDefaultConfig(); // YAML from znt-core.
129
+ // A setup UI writes the selected YAML to znt.resolveManagedConfigPath().
130
+ await znt.validateConfig(znt.resolveManagedConfigPath(), { checkProvider: true });
131
+ await znt.startCore();
132
+ // Use the RPC API.
133
+ await znt.stopCore();
134
+ await znt.removeCore(); // Optional uninstall; does not stop a running daemon.
135
+ ```
136
+
137
+ `isCoreInstalled()` checks the executable independently from configuration.
138
+ `isCoreConfigured()` checks `ZNT_CONFIG` or the adjacent managed `config.yaml`.
139
+ When an expected version is supplied, `isCoreInstalled()` also compares the version
140
+ stored by `downloadCore()` in the adjacent `znt-core.metadata.json` file. A
141
+ manually copied executable without version metadata does not satisfy a versioned check.
142
+ `removeCore()` removes both the executable and its metadata and returns whether
143
+ an executable was present. Stop the daemon before removing it.
144
+
145
+ ## Configuration and setup
146
+
147
+ The SDK provides methods to inspect and configure `znt-core` managed configuration, store API keys securely in the OS keyring via `znt-core config secret set`, and validate the provider setup:
148
+
149
+ ```js
150
+ // Inspect configuration status
151
+ const status = await znt.getSetupStatus();
152
+ console.log(status.status); // "ready" | "unconfigured"
153
+ console.log(status.recommendations);
154
+
155
+ // Configure managed config with selected provider and options
156
+ const result = await znt.setupManagedConfig({
157
+ mode: "openapi", // "openapi" | "ollama" | "bm25"
158
+ provider_url: "https://openrouter.ai/api/v1",
159
+ api_key: "sk-or-...",
160
+ model: "qwen/qwen-2.5-coder-32b-instruct",
161
+ embed_model: "bge-m3",
162
+ semantic_mode: "llm", // "llm" | "fast"
163
+ description_language: "ru",
164
+ exclude: ["**/dist/**"],
165
+ languages: {
166
+ go: { exclude: ["**/*_test.go"] },
167
+ },
168
+ check_provider: true,
169
+ });
170
+
171
+ console.log(result.status); // "ready"
172
+ ```
173
+
174
+ The setup helper:
175
+ 1. Loads default YAML template from `znt-core config defaults` or merges with existing configuration.
176
+ 2. Applies optimized concurrency, throttling, retry, and payload bounds (`retry_delays_seconds: [60, 120, 300]`, `max_bytes: 25000`, `max_embed_bytes: 63768`, `semantic_workers: 6`, `delay_ms: 10`).
177
+ 3. Writes the configuration atomically to `config.yaml` and validates it with `znt-core config validate`.
178
+ 4. Saves API keys directly to the OS keyring using `znt-core config secret set` over stdin (no plain text secret in the YAML file).
179
+ 5. Validates provider connectivity when `check_provider: true`.
180
+
181
+
182
+ ## Endpoint selection
183
+
184
+ ```js
185
+ const znt = new ZntClient({
186
+ endpoint: "/custom/path/znt.sock",
187
+ connectTimeoutMs: 5_000,
188
+ requestTimeoutMs: 30_000,
189
+ });
190
+ ```
191
+
192
+ Resolution order when `endpoint` is omitted:
193
+
194
+ 1. `ZNT_ENDPOINT`;
195
+ 2. `~/.znt/znt.sock` on Linux/macOS;
196
+ 3. `\\.\pipe\znt-core` on Windows.
197
+
198
+ `ZNT_RUNTIME_DIR` changes znt-core process files such as `znt.log`, but does
199
+ not change the default endpoint. Set `ZNT_ENDPOINT` on both processes when a
200
+ custom endpoint is required.
201
+
202
+ ## Scanning
203
+
204
+ `scan()` returns immediately with a `scan_id`:
205
+
206
+ ```js
207
+ const started = await znt.scan({
208
+ file_path: "/absolute/path/to/workspace",
209
+ language: "auto",
210
+ });
211
+ console.log(started.scan_id);
212
+ ```
213
+
214
+ Set `restart: true` to remove only the workspace `.znt` index and rebuild it
215
+ from scratch:
216
+
217
+ ```js
218
+ await znt.scanAndWait({
219
+ file_path: "/absolute/path/to/workspace",
220
+ language: "auto",
221
+ restart: true,
222
+ });
223
+ ```
224
+
225
+ `info().config` contains the absolute path of the configuration file loaded by
226
+ the active daemon. Pass `configPath` to `ZntClient` or set `ZNT_CONFIG` before
227
+ calling `startCore()`. The SDK forwards the path when it launches core but
228
+ does not own or modify the configuration file.
229
+
230
+ Use `scanAndWait()` when the caller must wait for semantic indexing and HNSW:
231
+
232
+ ```js
233
+ const completed = await znt.scanAndWait(
234
+ { file_path: "/absolute/path/to/workspace", language: "auto" },
235
+ { timeoutMs: 10 * 60_000, pollIntervalMs: 250 },
236
+ );
237
+ console.log(completed.phase); // completed
238
+ ```
239
+
240
+ `scan_status` describes the daemon's current or most recent global scan. The
241
+ SDK verifies that its `scan_id` remains current while `scanAndWait()` polls.
242
+
243
+ ## API
244
+
245
+ | SDK method | JSON-RPC method |
246
+ |---|---|
247
+ | `startCore()` | Reuse or start the installed `znt-core` process |
248
+ | `stopCore()` | Request daemon shutdown and disconnect the SDK client |
249
+ | `downloadCore(options)` | Explicitly download and install a core artifact |
250
+ | `isCoreInstalled(expectedVersion?)` | Check executable presence and optionally its installed version |
251
+ | `isCoreConfigured()` | Check whether the selected YAML configuration exists |
252
+ | `resolveManagedConfigPath()` | Return the adjacent managed `config.yaml` path |
253
+ | `getDefaultConfig()` | Return canonical default YAML emitted by core |
254
+ | `validateConfig(path, options)` | Validate YAML and optionally check the provider |
255
+ | `storeSecret(reference, secret)` | Store a credential through core stdin without process arguments |
256
+ | `removeCore()` | Remove the installed executable and version metadata |
257
+ | `info()` | `info` |
258
+ | `status()` | `status` |
259
+ | `logs()` | `logs` |
260
+ | `watchStatus(options)` | Repeated `status` polling |
261
+ | `watchScanStatus(options)` | Repeated `scan_status` polling |
262
+ | `watchLogs(options)` | Repeated `logs` polling |
263
+ | `scan(params)` | `scan` |
264
+ | `scanStatus()` | `scan_status` |
265
+ | `semanticSearch(params)` | `znatok_semantic_search` |
266
+ | `findSimilar(params)` | `znatok_find_similar` |
267
+ | `getSubgraph(params)` | `znatok_get_subgraph` |
268
+ | `fileOutline(params)` | `znatok_file_outline` |
269
+ | `shutdown()` | `shutdown` |
270
+ | `call(method, params)` | Any current or future JSON-RPC method |
271
+
272
+ Parameter names intentionally match the wire specification (`file_path`,
273
+ `include_code`, `edge_types`, and so on), while SDK method names follow normal
274
+ JavaScript camelCase conventions.
275
+
276
+ ### Logs
277
+
278
+ ```js
279
+ const snapshot = await znt.logs();
280
+ for (const entry of snapshot.entries) {
281
+ console.log(`${entry.time} [${entry.level}] ${entry.message}`);
282
+ }
283
+
284
+ const delta = await znt.logs({
285
+ stream_id: snapshot.stream_id,
286
+ after_id: snapshot.next_cursor,
287
+ });
288
+ ```
289
+
290
+ The first `logs()` call returns the current in-memory snapshot. Subsequent calls
291
+ must pass both `stream_id` and `after_id` and return only newer entries. A
292
+ changed `stream_id` or an expired cursor returns the complete retained buffer
293
+ with `truncated: true`. The buffer contains up to 100 entries and is reset when
294
+ the daemon restarts; the persistent `znt.log` file is not read by this method.
295
+
296
+ ### Polling streams
297
+
298
+ The SDK can expose status, scan progress, and log snapshots as async iterables.
299
+ These helpers use repeated JSON-RPC calls over the existing persistent IPC
300
+ connection; they do not require WebSocket support in znt-core.
301
+
302
+ ```js
303
+ const controller = new AbortController();
304
+
305
+ for await (const status of znt.watchStatus({
306
+ intervalMs: 1_000,
307
+ signal: controller.signal,
308
+ })) {
309
+ console.log(status.status, status.files);
310
+ }
311
+ ```
312
+
313
+ The same pattern is available through `watchScanStatus()` and `watchLogs()`:
314
+
315
+ ```js
316
+ for await (const batch of znt.watchLogs({ intervalMs: 500 })) {
317
+ if (batch.truncated) {
318
+ replaceActivityLog(batch.entries);
319
+ } else {
320
+ appendActivityLog(batch.entries);
321
+ }
322
+ }
323
+ ```
324
+
325
+ `watchLogs()` manages `stream_id` and `after_id` automatically. Its first value
326
+ is the current snapshot; later values contain only new entries. With the
327
+ default `distinct: true`, empty unchanged polling responses are not yielded.
328
+ Polling options are:
329
+
330
+ - `intervalMs`: delay between requests; defaults to 1 second for status/logs
331
+ and 250 ms for scan progress;
332
+ - `emitInitial`: when `true` (default), request the first snapshot immediately;
333
+ - `distinct`: when `true` (default), skip snapshots identical to the last one;
334
+ - `timeoutMs`: timeout of each individual JSON-RPC request;
335
+ - `signal`: stops polling with `ZntAbortError` when aborted.
336
+
337
+ Leaving a `for await` loop stops that iterator cleanly. For prompt external
338
+ cancellation while it is sleeping or waiting for a response, use an
339
+ `AbortController`. A transport, protocol, RPC, or request-timeout error ends the
340
+ iterator and is propagated to the consumer.
341
+
342
+ Each `watchLogs()` value retains the server metadata (`stream_id`,
343
+ `next_cursor`, and `truncated`) so consumers can replace their local state when
344
+ continuity was lost.
345
+
346
+ ### Similar implementations
347
+
348
+ ```js
349
+ const similar = await znt.findSimilar({
350
+ target: "pkg/semantic/service.go::SemanticService.SearchWithOptions",
351
+ edge_types: "contains,call",
352
+ include_code: true,
353
+ });
354
+ ```
355
+
356
+ ### Subgraph
357
+
358
+ ```js
359
+ const graph = await znt.getSubgraph({
360
+ from: "SemanticService.SearchWithOptions",
361
+ depth: 2,
362
+ edge_types: "call,contains",
363
+ format: "mermaid",
364
+ });
365
+
366
+ if (graph.format === "mermaid") {
367
+ console.log(graph.mermaid);
368
+ }
369
+ ```
370
+
371
+ The TypeScript result is a discriminated union: `text` has only `text`, `json`
372
+ has `nodes` and `edges`, and `mermaid` has only `mermaid`.
373
+
374
+ ### File outline
375
+
376
+ ```js
377
+ const outline = await znt.fileOutline({
378
+ file_path: "internal/engine/watcher.go",
379
+ include_code: true,
380
+ });
381
+ console.log(outline.formatted_text);
382
+ ```
383
+
384
+ ## Errors, timeout, and cancellation
385
+
386
+ ```js
387
+ import { ZntRpcError, ZntTimeoutError } from "znt-sdk-nodejs";
388
+
389
+ try {
390
+ await znt.semanticSearch({ query: "Search", mode: "bogus" });
391
+ } catch (error) {
392
+ if (error instanceof ZntRpcError) {
393
+ console.error(error.code, error.message, error.data);
394
+ if (error.retryable) {
395
+ // Only -32001 Busy is marked retryable.
396
+ }
397
+ } else if (error instanceof ZntTimeoutError) {
398
+ console.error(error.timeoutMs);
399
+ }
400
+ }
401
+ ```
402
+
403
+ Every request accepts `timeoutMs` and `AbortSignal`:
404
+
405
+ ```js
406
+ const controller = new AbortController();
407
+ const request = znt.status({ timeoutMs: 2_000, signal: controller.signal });
408
+ controller.abort();
409
+ await request;
410
+ ```
411
+
412
+ Error classes:
413
+
414
+ - `ZntRpcError`: server error with `code`, `data`, `method`, and `requestId`;
415
+ - `ZntTransportError`: connection or socket failure;
416
+ - `ZntProtocolError`: malformed response or oversized request;
417
+ - `ZntTimeoutError`: client-side request or scan timeout;
418
+ - `ZntAbortError`: cancellation through `AbortSignal`;
419
+ - `ZntScanError`: terminal scan failure with the complete progress DTO.
420
+
421
+ ## Low-level connection
422
+
423
+ `ZntConnection` is exported for clients that only need raw typed calls:
424
+
425
+ ```js
426
+ import { ZntConnection } from "znt-sdk-nodejs";
427
+
428
+ const connection = new ZntConnection();
429
+ const info = await connection.call("info", {});
430
+ connection.disconnect();
431
+ ```
432
+
433
+ ## Tests
434
+
435
+ ```bash
436
+ npm test
437
+ npm run test:live
438
+ npm pack --dry-run
439
+ ```
440
+
441
+ `test:live` uses the endpoint selected by `ZNT_ENDPOINT` or the platform
442
+ default and never calls `scan` or `shutdown`.
443
+
444
+ The authoritative wire contract is
445
+ [`../znt-core/docs/sdk-specification.md`](../znt-core/docs/sdk-specification.md).
@@ -0,0 +1,48 @@
1
+ import { ZntConnection, type ZntConnectionOptions } from "./connection.js";
2
+ import { type ConfigValidationResult, type CoreDownloadResult, type DownloadCoreOptions, type ZntDaemonOptions } from "./daemon.js";
3
+ import { type GetSetupStatusOptions, type SetupConfigOptions, type SetupConfigResult, type SetupStatusResult } from "./config.js";
4
+ import type { FileOutlineParams, FileOutlineResult, FindSimilarParams, InfoResult, JsonObject, LogsParams, LogsResult, PollingOptions, RequestOptions, ScanParams, ScanProgress, ScanResult, SemanticSearchParams, SearchResultItem, ShutdownResult, SimilarResultItem, StatusResult, SubgraphParams, SubgraphResult, WaitForScanOptions } from "./types.js";
5
+ export type ZntClientOptions = ZntConnectionOptions & ZntDaemonOptions;
6
+ export declare class ZntClient {
7
+ readonly connection: ZntConnection;
8
+ private readonly daemonOptions;
9
+ private daemonStartPromise;
10
+ constructor(options?: ZntClientOptions);
11
+ get endpoint(): string;
12
+ get connected(): boolean;
13
+ connect(): Promise<void>;
14
+ startCore(): Promise<void>;
15
+ downloadCore(options?: DownloadCoreOptions): Promise<CoreDownloadResult>;
16
+ isCoreInstalled(expectedVersion?: string): Promise<boolean>;
17
+ isCoreConfigured(): Promise<boolean>;
18
+ resolveManagedConfigPath(): string;
19
+ getDefaultConfig(): Promise<string>;
20
+ validateConfig(configPath?: string, options?: {
21
+ checkProvider?: boolean;
22
+ signal?: AbortSignal;
23
+ }): Promise<ConfigValidationResult>;
24
+ storeSecret(reference: string, secret: string, signal?: AbortSignal): Promise<void>;
25
+ deleteSecret(reference: string, signal?: AbortSignal): Promise<void>;
26
+ getSetupStatus(options?: GetSetupStatusOptions): Promise<SetupStatusResult>;
27
+ setupManagedConfig(options: SetupConfigOptions): Promise<SetupConfigResult>;
28
+ removeCore(): Promise<boolean>;
29
+ stopCore(options?: RequestOptions): Promise<ShutdownResult>;
30
+ disconnect(): void;
31
+ call<TResult>(method: string, params?: JsonObject, options?: RequestOptions): Promise<TResult>;
32
+ info(options?: RequestOptions): Promise<InfoResult>;
33
+ status(options?: RequestOptions): Promise<StatusResult>;
34
+ logs(params?: LogsParams, options?: RequestOptions): Promise<LogsResult>;
35
+ watchStatus(options?: PollingOptions): AsyncIterable<StatusResult>;
36
+ watchScanStatus(options?: PollingOptions): AsyncIterable<ScanProgress>;
37
+ watchLogs(options?: PollingOptions): AsyncIterable<LogsResult>;
38
+ scan(params?: ScanParams, options?: RequestOptions): Promise<ScanResult>;
39
+ scanStatus(options?: RequestOptions): Promise<ScanProgress>;
40
+ semanticSearch(params: SemanticSearchParams, options?: RequestOptions): Promise<SearchResultItem[]>;
41
+ findSimilar(params: FindSimilarParams, options?: RequestOptions): Promise<SimilarResultItem[]>;
42
+ getSubgraph(params: SubgraphParams, options?: RequestOptions): Promise<SubgraphResult>;
43
+ fileOutline(params: FileOutlineParams, options?: RequestOptions): Promise<FileOutlineResult>;
44
+ shutdown(options?: RequestOptions): Promise<ShutdownResult>;
45
+ waitForScan(options?: WaitForScanOptions): Promise<ScanProgress>;
46
+ scanAndWait(params?: ScanParams, options?: WaitForScanOptions): Promise<ScanProgress>;
47
+ }
48
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}