@skein-js/server-kit 0.8.0 → 0.9.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 +14 -1
- package/dist/index.d.ts +48 -3
- package/dist/index.js +58 -10
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -32,6 +32,8 @@ handler table.
|
|
|
32
32
|
(an unset origin resolves to `*`, never a reflected origin, so it can't pair with credentials).
|
|
33
33
|
- **Node transport** — `sendNodeResponse` / `sendNodeError`: serialize a `ProtocolResponse` (JSON / 204
|
|
34
34
|
/ SSE) onto a Node `ServerResponse`, shared by the NestJS + Next.js Pages Router adapters.
|
|
35
|
+
- **Mount prefix** — `stripBasePath`: strip the path an adapter is mounted under before matching the
|
|
36
|
+
route table, for adapters that mount a catch-all and match by hand (NestJS, Next.js).
|
|
35
37
|
|
|
36
38
|
> The route table itself (`skeinRoutes`) is **not** here — it lives with the engine in
|
|
37
39
|
> [`@skein-js/agent-protocol`](../agent-protocol), since it references the handler names. Adapters
|
|
@@ -73,7 +75,14 @@ Pass `overrides` to swap in production drivers or an auth engine while keeping t
|
|
|
73
75
|
graph→`GraphResolver` helpers.
|
|
74
76
|
- **`resolveProtocolRuntime(options): Promise<ResolvedProtocolRuntime>`** — turn a
|
|
75
77
|
`{ config } | { deps }` bag (`SkeinRuntimeOptions`) into a live runtime (assistants seeded, worker
|
|
76
|
-
started) — the step every adapter runs before mounting routes.
|
|
78
|
+
started) — the step every adapter runs before mounting routes. `options.worker`
|
|
79
|
+
(`RunWorkerOptions`) tunes the background worker; `worker.maxConcurrency` is how many queued runs
|
|
80
|
+
run at once.
|
|
81
|
+
- **`resolveRunConcurrency(explicit?, env?): number`** — the one precedence chain behind
|
|
82
|
+
`worker.maxConcurrency`: explicit value → `SKEIN_RUN_CONCURRENCY` → `N_JOBS_PER_WORKER` →
|
|
83
|
+
`DEFAULT_RUN_CONCURRENCY` (10, matching the LangGraph CLI). The environment is validated even when
|
|
84
|
+
an explicit value is given, so the two sources can't silently disagree. `skein dev`/`start` use it
|
|
85
|
+
to resolve the number they print in the startup banner.
|
|
77
86
|
- **`loadInMemoryRuntime` / `loadReloadableInMemoryRuntime`** — assemble a `ProtocolDeps` from a
|
|
78
87
|
`langgraph.json` using in-process drivers. The reloadable variant adds `reloadGraphs` /
|
|
79
88
|
`snapshotState` / `hydrateState` (what powers `skein dev`'s hot reload + cross-restart persistence).
|
|
@@ -85,6 +94,10 @@ Pass `overrides` to swap in production drivers or an auth engine while keeping t
|
|
|
85
94
|
derive CORS headers for adapters without a CORS middleware of their own.
|
|
86
95
|
- **Node transport** — `sendNodeResponse` / `sendNodeError` serialize a `ProtocolResponse`
|
|
87
96
|
(JSON / 204 / SSE) onto a Node `ServerResponse`, shared by the NestJS + Next.js Pages Router adapters.
|
|
97
|
+
- **`stripBasePath(pathname, basePath): string | null`** — the pathname relative to a mount prefix, or
|
|
98
|
+
`null` when the path is not under it (the caller passes those through untouched). The prefix may be
|
|
99
|
+
written any way the host wrote it (`api`, `/api`, `/api/`); an empty one passes everything through.
|
|
100
|
+
Needed only by adapters that mount a catch-all — Express/Fastify get this from their router.
|
|
88
101
|
|
|
89
102
|
## Learn more
|
|
90
103
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ProtocolRuntime, Logger,
|
|
2
|
-
export { CompiledGraphFactory, GraphResolver, ProtocolDeps, ResolvedGraph } from '@skein-js/agent-protocol';
|
|
1
|
+
import { ProtocolRuntime, ProtocolDeps, Logger, RunWorkerOptions, GraphResolver, ProtocolResponse } from '@skein-js/agent-protocol';
|
|
2
|
+
export { CompiledGraphFactory, DEFAULT_RUN_CONCURRENCY, GraphResolver, ProtocolDeps, ResolvedGraph, RunWorkerOptions } from '@skein-js/agent-protocol';
|
|
3
3
|
import { ModuleImporter, GraphSchemas } from '@skein-js/config';
|
|
4
4
|
import { CorsOptions } from 'cors';
|
|
5
5
|
export { CorsOptions } from 'cors';
|
|
@@ -23,6 +23,18 @@ interface SkeinRuntimeCommonOptions {
|
|
|
23
23
|
* server down.
|
|
24
24
|
*/
|
|
25
25
|
warm?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Background run-worker tuning. `maxConcurrency` is how many **queued** runs this instance executes
|
|
28
|
+
* at once (default `DEFAULT_RUN_CONCURRENCY`, matching the LangGraph CLI); omit it and skein reads
|
|
29
|
+
* `SKEIN_RUN_CONCURRENCY`, else the LangGraph-compatible `N_JOBS_PER_WORKER`. An explicit value
|
|
30
|
+
* wins, but the environment is still validated so the two sources can't silently disagree.
|
|
31
|
+
*
|
|
32
|
+
* Raising it never weakens per-thread ordering — two runs on one thread are still serialized by the
|
|
33
|
+
* engine's execution lock — but see the head-of-line note in docs/runs-and-redis.md if your workload
|
|
34
|
+
* leans on `multitask_strategy: "enqueue"`. Ignored by the invoke-only surface, which starts no
|
|
35
|
+
* worker.
|
|
36
|
+
*/
|
|
37
|
+
worker?: RunWorkerOptions;
|
|
26
38
|
}
|
|
27
39
|
/** Either point at a `langgraph.json` (in-memory runtime) or inject a ready `ProtocolDeps`. */
|
|
28
40
|
type SkeinRuntimeOptions = SkeinRuntimeCommonOptions & ({
|
|
@@ -44,6 +56,20 @@ interface ResolvedProtocolRuntime {
|
|
|
44
56
|
/** CORS mapped from the config's `http.cors`, or `undefined` for the injected-`deps` path. */
|
|
45
57
|
cors?: CorsOptions;
|
|
46
58
|
}
|
|
59
|
+
/** Just the dependencies behind a `{ config } | { deps }` bag — no runtime, no worker. */
|
|
60
|
+
interface ResolvedRuntimeDeps {
|
|
61
|
+
deps: ProtocolDeps;
|
|
62
|
+
/** CORS mapped from the config's `http.cors`, or `undefined` for the injected-`deps` path. */
|
|
63
|
+
cors?: CorsOptions;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve the `{ config } | { deps }` seam down to a `ProtocolDeps` — injected as-is, or fresh
|
|
67
|
+
* in-memory drivers loaded from a `langgraph.json`. This is the half of
|
|
68
|
+
* {@link resolveProtocolRuntime} that stops short of building the engine, so the simplified invoke
|
|
69
|
+
* surface (which needs only graphs + store) doesn't seed assistants or start a run worker it will
|
|
70
|
+
* never use.
|
|
71
|
+
*/
|
|
72
|
+
declare function resolveRuntimeDeps(options: SkeinRuntimeOptions): Promise<ResolvedRuntimeDeps>;
|
|
47
73
|
/**
|
|
48
74
|
* Build a `ProtocolRuntime` from adapter options: resolve `deps` (injected, or fresh in-memory
|
|
49
75
|
* drivers from a `langgraph.json`), seed one assistant per declared graph, optionally warm the
|
|
@@ -52,6 +78,18 @@ interface ResolvedProtocolRuntime {
|
|
|
52
78
|
*/
|
|
53
79
|
declare function resolveProtocolRuntime(options: SkeinRuntimeOptions): Promise<ResolvedProtocolRuntime>;
|
|
54
80
|
|
|
81
|
+
/**
|
|
82
|
+
* The one precedence chain for background-run concurrency: an explicit value (a `worker.maxConcurrency`
|
|
83
|
+
* option, or the CLI's `--concurrency` / `-n`) wins, else `SKEIN_RUN_CONCURRENCY`, else
|
|
84
|
+
* `N_JOBS_PER_WORKER`, else {@link DEFAULT_RUN_CONCURRENCY}. The environment is read and validated
|
|
85
|
+
* even when `explicit` is given, so a bad `SKEIN_RUN_CONCURRENCY` can't sit unnoticed in a deployment
|
|
86
|
+
* that also passes the option.
|
|
87
|
+
*
|
|
88
|
+
* `env` is injected (defaulting to `process.env`) so callers — and tests — can resolve against an
|
|
89
|
+
* environment they control.
|
|
90
|
+
*/
|
|
91
|
+
declare function resolveRunConcurrency(explicit?: number, env?: NodeJS.ProcessEnv): number;
|
|
92
|
+
|
|
55
93
|
/** The checkpoint tuple `[checkpoint, metadata, parentId?]` with the blobs base64-encoded. */
|
|
56
94
|
type SerializedCheckpointTuple = [string, string, string | undefined];
|
|
57
95
|
/** `MemorySaver.storage` (thread → namespace → checkpointId → tuple) with blobs base64-encoded. */
|
|
@@ -214,4 +252,11 @@ declare function sendNodeResponse(response: ProtocolResponse, res: ServerRespons
|
|
|
214
252
|
/** Serialize a caught error onto the Node `res`, using the protocol status when the error carries one. */
|
|
215
253
|
declare function sendNodeError(error: unknown, res: ServerResponse, logger?: Logger, adapterName?: string): void;
|
|
216
254
|
|
|
217
|
-
|
|
255
|
+
/**
|
|
256
|
+
* The pathname relative to `basePath`, or `null` when the path is not under the mount — which the
|
|
257
|
+
* caller should treat as "not ours" and pass through untouched, so the host app's own routes still
|
|
258
|
+
* resolve. An empty (or `/`) base path passes the pathname through unchanged.
|
|
259
|
+
*/
|
|
260
|
+
declare function stripBasePath(pathname: string, basePath: string): string | null;
|
|
261
|
+
|
|
262
|
+
export { type CorsSetting, type DevStateCounts, type DevStateSnapshot, type EmbeddableGraph, type InMemoryRuntimeConfig, type LanggraphCorsConfig, type ReloadableInMemoryRuntime, type ResolvedProtocolRuntime, type ResolvedRuntimeDeps, type SkeinRuntimeCommonOptions, type SkeinRuntimeOptions, allowedOrigin, applyNodeCors, corsFromHttpConfig, corsPreflightHeaders, corsResponseHeaders, createInMemoryDeps, describeSnapshot, embedInMemoryGraphs, graphMapToResolver, joinList, loadInMemoryRuntime, loadReloadableInMemoryRuntime, loadSnapshotIntoStore, normalizeEmbeddableGraphs, readLanggraphDevState, resolveProtocolRuntime, resolveRunConcurrency, resolveRuntimeDeps, sendNodeError, sendNodePreflight, sendNodeResponse, stripBasePath, toCorsOptions };
|
package/dist/index.js
CHANGED
|
@@ -189,18 +189,45 @@ async function loadReloadableInMemoryRuntime(configPath, importModule, staticSch
|
|
|
189
189
|
};
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
// src/run-concurrency.ts
|
|
193
|
+
import { DEFAULT_RUN_CONCURRENCY } from "@skein-js/agent-protocol";
|
|
194
|
+
import { SkeinConfigError } from "@skein-js/config";
|
|
195
|
+
var CONCURRENCY_ENV_VARS = ["SKEIN_RUN_CONCURRENCY", "N_JOBS_PER_WORKER"];
|
|
196
|
+
function requirePositiveInteger(source, raw) {
|
|
197
|
+
const parsed = Number(raw);
|
|
198
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
199
|
+
throw new SkeinConfigError(`${source} must be a positive integer (got "${String(raw)}").`);
|
|
200
|
+
}
|
|
201
|
+
return parsed;
|
|
202
|
+
}
|
|
203
|
+
function runConcurrencyFromEnv(env) {
|
|
204
|
+
for (const name of CONCURRENCY_ENV_VARS) {
|
|
205
|
+
const raw = env[name];
|
|
206
|
+
if (raw === void 0 || raw.trim() === "") continue;
|
|
207
|
+
return requirePositiveInteger(name, raw);
|
|
208
|
+
}
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
function resolveRunConcurrency(explicit, env = process.env) {
|
|
212
|
+
const fromEnv = runConcurrencyFromEnv(env);
|
|
213
|
+
if (explicit === void 0) return fromEnv ?? DEFAULT_RUN_CONCURRENCY;
|
|
214
|
+
return requirePositiveInteger("worker.maxConcurrency", explicit);
|
|
215
|
+
}
|
|
216
|
+
|
|
192
217
|
// src/resolve-runtime.ts
|
|
218
|
+
async function resolveRuntimeDeps(options) {
|
|
219
|
+
if (options.deps) return { deps: options.deps };
|
|
220
|
+
const loaded = await loadInMemoryRuntime(options.config, options.importModule);
|
|
221
|
+
return { deps: loaded.deps, cors: loaded.cors };
|
|
222
|
+
}
|
|
193
223
|
async function resolveProtocolRuntime(options) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
corsFromConfig = loaded.cors;
|
|
202
|
-
}
|
|
203
|
-
const runtime = createProtocolRuntime(deps);
|
|
224
|
+
const { deps, cors: corsFromConfig } = await resolveRuntimeDeps(options);
|
|
225
|
+
const runtime = createProtocolRuntime(deps, {
|
|
226
|
+
worker: {
|
|
227
|
+
...options.worker,
|
|
228
|
+
maxConcurrency: resolveRunConcurrency(options.worker?.maxConcurrency)
|
|
229
|
+
}
|
|
230
|
+
});
|
|
204
231
|
await runtime.service.assistants.registerGraphAssistants();
|
|
205
232
|
if (options.warm) {
|
|
206
233
|
await Promise.all(
|
|
@@ -215,6 +242,9 @@ async function resolveProtocolRuntime(options) {
|
|
|
215
242
|
return { runtime, cors: corsFromConfig };
|
|
216
243
|
}
|
|
217
244
|
|
|
245
|
+
// src/index.ts
|
|
246
|
+
import { DEFAULT_RUN_CONCURRENCY as DEFAULT_RUN_CONCURRENCY2 } from "@skein-js/agent-protocol";
|
|
247
|
+
|
|
218
248
|
// src/langgraph-import.ts
|
|
219
249
|
import { readFile } from "fs/promises";
|
|
220
250
|
import path from "path";
|
|
@@ -639,7 +669,22 @@ function sendNodeError(error, res, logger, adapterName = "skein") {
|
|
|
639
669
|
res.writeHead(500, { "content-type": "application/json" });
|
|
640
670
|
res.end(JSON.stringify({ status: 500, message: "Internal Server Error" }));
|
|
641
671
|
}
|
|
672
|
+
|
|
673
|
+
// src/base-path.ts
|
|
674
|
+
function normalizeBasePath(basePath) {
|
|
675
|
+
if (basePath === "" || basePath === "/") return "";
|
|
676
|
+
const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
|
|
677
|
+
return withLeadingSlash.replace(/\/+$/, "");
|
|
678
|
+
}
|
|
679
|
+
function stripBasePath(pathname, basePath) {
|
|
680
|
+
const prefix = normalizeBasePath(basePath);
|
|
681
|
+
if (prefix === "") return pathname;
|
|
682
|
+
if (pathname === prefix) return "/";
|
|
683
|
+
if (pathname.startsWith(`${prefix}/`)) return pathname.slice(prefix.length);
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
642
686
|
export {
|
|
687
|
+
DEFAULT_RUN_CONCURRENCY2 as DEFAULT_RUN_CONCURRENCY,
|
|
643
688
|
allowedOrigin,
|
|
644
689
|
applyNodeCors,
|
|
645
690
|
corsFromHttpConfig,
|
|
@@ -656,8 +701,11 @@ export {
|
|
|
656
701
|
normalizeEmbeddableGraphs,
|
|
657
702
|
readLanggraphDevState,
|
|
658
703
|
resolveProtocolRuntime,
|
|
704
|
+
resolveRunConcurrency,
|
|
705
|
+
resolveRuntimeDeps,
|
|
659
706
|
sendNodeError,
|
|
660
707
|
sendNodePreflight,
|
|
661
708
|
sendNodeResponse,
|
|
709
|
+
stripBasePath,
|
|
662
710
|
toCorsOptions
|
|
663
711
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skein-js/server-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Shared, framework-agnostic building blocks for skein-js HTTP adapters — in-memory dev runtime, LangGraph dev-state import, and langgraph.json CORS mapping.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Maina Wycliffe <wmmaina7@gmail.com>",
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
"cors": "^2.8.5",
|
|
44
44
|
"superjson": "^2.2.6",
|
|
45
45
|
"zod": "^3.25.76",
|
|
46
|
-
"@skein-js/
|
|
47
|
-
"@skein-js/
|
|
48
|
-
"@skein-js/
|
|
49
|
-
"@skein-js/
|
|
46
|
+
"@skein-js/agent-protocol": "0.9.1",
|
|
47
|
+
"@skein-js/core": "0.9.1",
|
|
48
|
+
"@skein-js/storage-memory": "0.9.1",
|
|
49
|
+
"@skein-js/config": "0.9.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"@langchain/langgraph": "^1.4.0"
|