skein-js 0.5.0 → 0.7.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 +10 -6
- package/dist/index.js +53 -14
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -52,15 +52,19 @@ LangGraph Studio — any Agent Protocol client works with only a URL change. See
|
|
|
52
52
|
|
|
53
53
|
## Commands
|
|
54
54
|
|
|
55
|
-
| Command | What it does | LangGraph CLI equivalent | Key flags
|
|
56
|
-
| ------------------ | -------------------------------------------------------------- | ------------------------ |
|
|
57
|
-
| `skein dev` | In-process dev server, hot reload, `.skein/` state, no Docker. | `langgraph dev` | see [`skein dev` flags](#skein-dev-flags)
|
|
58
|
-
| `skein up` | Self-hosted stack via Docker Compose (app + Postgres + Redis). | `langgraph up` | `-p, --port` (8123) · `--host` (0.0.0.0) |
|
|
59
|
-
| `skein build` | Build a deployable Docker image from the config. | `langgraph build` | `-t, --tag` (defaults to the project dir name) |
|
|
60
|
-
| `skein dockerfile` | Emit a standalone Dockerfile (stdout by default). | `langgraph dockerfile` | `-o, --output <path>`
|
|
55
|
+
| Command | What it does | LangGraph CLI equivalent | Key flags |
|
|
56
|
+
| ------------------ | -------------------------------------------------------------- | ------------------------ | --------------------------------------------------------------------- |
|
|
57
|
+
| `skein dev` | In-process dev server, hot reload, `.skein/` state, no Docker. | `langgraph dev` | see [`skein dev` flags](#skein-dev-flags) |
|
|
58
|
+
| `skein up` | Self-hosted stack via Docker Compose (app + Postgres + Redis). | `langgraph up` | `-p, --port` (8123) · `--host` (0.0.0.0) · `-n, --npmrc <path>` |
|
|
59
|
+
| `skein build` | Build a deployable Docker image from the config. | `langgraph build` | `-t, --tag` (defaults to the project dir name) · `-n, --npmrc <path>` |
|
|
60
|
+
| `skein dockerfile` | Emit a standalone Dockerfile (stdout by default). | `langgraph dockerfile` | `-o, --output <path>` |
|
|
61
61
|
|
|
62
62
|
All commands take `-c, --config <path>` (default `langgraph.json`).
|
|
63
63
|
|
|
64
|
+
`--npmrc <path>` (on `build`/`up`) mounts an `.npmrc` as a BuildKit secret so the image's dependency
|
|
65
|
+
install can authenticate against a **private/authenticated npm registry** without baking a token into
|
|
66
|
+
any layer. Public-registry builds don't need it.
|
|
67
|
+
|
|
64
68
|
## `skein dev` flags
|
|
65
69
|
|
|
66
70
|
| Flag | Values | Default | Notes |
|
package/dist/index.js
CHANGED
|
@@ -359,7 +359,7 @@ async function runDev(options) {
|
|
|
359
359
|
|
|
360
360
|
// src/docker/commands.ts
|
|
361
361
|
import { spawn, spawnSync } from "child_process";
|
|
362
|
-
import { writeFileSync as writeFileSync2 } from "fs";
|
|
362
|
+
import { existsSync as existsSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
363
363
|
import { createRequire as createRequire2 } from "module";
|
|
364
364
|
import path5 from "path";
|
|
365
365
|
import { loadConfig as loadConfig3 } from "@skein-js/config";
|
|
@@ -370,7 +370,7 @@ import { mkdir, writeFile } from "fs/promises";
|
|
|
370
370
|
import { createRequire, isBuiltin } from "module";
|
|
371
371
|
import path4 from "path";
|
|
372
372
|
import { loadConfig as loadConfig2, parseGraphSpec } from "@skein-js/config";
|
|
373
|
-
import {
|
|
373
|
+
import { providerEmbedPackage, isCustomFunctionPath } from "@skein-js/runtime";
|
|
374
374
|
|
|
375
375
|
// src/bundle/precompute-schemas.ts
|
|
376
376
|
async function precomputeSchemas(graphs) {
|
|
@@ -535,7 +535,7 @@ async function bundleProject(options) {
|
|
|
535
535
|
for (const peer of SKEIN_RUNTIME_PEERS) {
|
|
536
536
|
dependencies[peer] = resolveInstalledVersion(peer, workspaceRoot);
|
|
537
537
|
}
|
|
538
|
-
const embedPkg = config.store?.index?.embed &&
|
|
538
|
+
const embedPkg = config.store?.index?.embed && providerEmbedPackage(config.store.index.embed);
|
|
539
539
|
if (embedPkg) dependencies[embedPkg] = resolveInstalledVersion(embedPkg, workspaceRoot);
|
|
540
540
|
for (const dep of config.dependencies ?? []) {
|
|
541
541
|
if (dep.startsWith(".") || dep.startsWith("/")) continue;
|
|
@@ -568,10 +568,19 @@ function portMapping(options) {
|
|
|
568
568
|
}
|
|
569
569
|
function generateCompose(options) {
|
|
570
570
|
const ports = portMapping(options);
|
|
571
|
+
const appBuild = options.npmrcPath ? `build:
|
|
572
|
+
context: .
|
|
573
|
+
secrets:
|
|
574
|
+
- npmrc` : `build: .`;
|
|
575
|
+
const secretsBlock = options.npmrcPath ? `
|
|
576
|
+
secrets:
|
|
577
|
+
npmrc:
|
|
578
|
+
file: ${JSON.stringify(options.npmrcPath)}
|
|
579
|
+
` : "";
|
|
571
580
|
return `# Generated by skein \u2014 regenerated by \`skein up\`. Edit with care.
|
|
572
581
|
services:
|
|
573
582
|
app:
|
|
574
|
-
|
|
583
|
+
${appBuild}
|
|
575
584
|
# Reap zombies + forward signals with an init process (PID 1), so graph-spawned children don't
|
|
576
585
|
# accumulate; the app itself still handles SIGTERM for graceful shutdown.
|
|
577
586
|
init: true
|
|
@@ -616,7 +625,7 @@ services:
|
|
|
616
625
|
|
|
617
626
|
volumes:
|
|
618
627
|
skein-pgdata:
|
|
619
|
-
`;
|
|
628
|
+
${secretsBlock}`;
|
|
620
629
|
}
|
|
621
630
|
|
|
622
631
|
// src/docker/dockerfile.ts
|
|
@@ -631,6 +640,9 @@ function generateDockerignore() {
|
|
|
631
640
|
"node_modules",
|
|
632
641
|
".env",
|
|
633
642
|
".env.*",
|
|
643
|
+
// Never bake a registry credential into a layer — it's supplied via a BuildKit secret at install.
|
|
644
|
+
".npmrc",
|
|
645
|
+
".npmrc.*",
|
|
634
646
|
"Dockerfile",
|
|
635
647
|
"compose.yaml",
|
|
636
648
|
".dockerignore",
|
|
@@ -653,10 +665,15 @@ function generateDockerfile(options) {
|
|
|
653
665
|
``,
|
|
654
666
|
// Install prod deps first for layer caching: the artifact's pinned package.json changes rarely,
|
|
655
667
|
// so this layer is reused across rebuilds while only the bundle layer below re-runs. The BuildKit
|
|
656
|
-
// cache mount keeps the npm cache out of the committed layer.
|
|
668
|
+
// cache mount keeps the npm cache out of the committed layer. The `npmrc` secret mount lets private
|
|
669
|
+
// scoped deps authenticate against a private/authenticated registry without baking a token into any
|
|
670
|
+
// layer; it is optional (BuildKit secrets are not required by default), so a public-registry build
|
|
671
|
+
// that passes no secret is unaffected. Supply it via `skein build --npmrc <path>` or, for the
|
|
672
|
+
// standalone Dockerfile, `docker build --secret id=npmrc,src=<path>`.
|
|
657
673
|
`# Install the artifact's pinned production dependencies (exact versions \u2014 deterministic).`,
|
|
658
674
|
`COPY package.json ./`,
|
|
659
675
|
`RUN --mount=type=cache,target=/root/.npm \\`,
|
|
676
|
+
` --mount=type=secret,id=npmrc,target=/app/.npmrc \\`,
|
|
660
677
|
` npm install --omit=dev --omit=optional --no-audit --no-fund`,
|
|
661
678
|
``,
|
|
662
679
|
`# Copy the pre-built artifact (bundled JS graphs, production langgraph.json, baked schemas).`,
|
|
@@ -723,6 +740,16 @@ function defaultImageTag(configDir) {
|
|
|
723
740
|
const trimmed = base.replace(/^[-.]+|[-.]+$/g, "");
|
|
724
741
|
return trimmed.length > 0 ? trimmed : "skein-app";
|
|
725
742
|
}
|
|
743
|
+
function resolveNpmrcSecret(npmrc) {
|
|
744
|
+
if (npmrc === void 0) return void 0;
|
|
745
|
+
const resolved = path5.resolve(process.cwd(), npmrc);
|
|
746
|
+
if (!existsSync4(resolved)) {
|
|
747
|
+
console.error(`skein: --npmrc file not found: ${resolved}`);
|
|
748
|
+
process.exitCode = 1;
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
return resolved;
|
|
752
|
+
}
|
|
726
753
|
function dockerAvailable() {
|
|
727
754
|
const probe = spawnSync("docker", ["--version"], { stdio: "ignore" });
|
|
728
755
|
return probe.status === 0;
|
|
@@ -768,6 +795,8 @@ async function runDockerfile(options) {
|
|
|
768
795
|
}
|
|
769
796
|
async function runBuild(options) {
|
|
770
797
|
const context = await loadConfigContext(options.config);
|
|
798
|
+
const npmrcPath = resolveNpmrcSecret(options.npmrc);
|
|
799
|
+
if (npmrcPath === null) return;
|
|
771
800
|
if (!requireDocker()) return;
|
|
772
801
|
let artifactDir;
|
|
773
802
|
try {
|
|
@@ -778,10 +807,11 @@ async function runBuild(options) {
|
|
|
778
807
|
return;
|
|
779
808
|
}
|
|
780
809
|
const tag = options.tag ?? defaultImageTag(context.configDir);
|
|
810
|
+
const secretArgs = npmrcPath !== void 0 ? ["--secret", `id=npmrc,src=${npmrcPath}`] : [];
|
|
781
811
|
console.log(`skein: building image "${tag}"\u2026`);
|
|
782
812
|
const result = await runToCompletion(
|
|
783
813
|
"docker",
|
|
784
|
-
["build", "-t", tag, "-f", path5.join(artifactDir, DOCKERFILE_NAME), "."],
|
|
814
|
+
["build", "-t", tag, "-f", path5.join(artifactDir, DOCKERFILE_NAME), ...secretArgs, "."],
|
|
785
815
|
artifactDir
|
|
786
816
|
);
|
|
787
817
|
if (!succeeded(result)) {
|
|
@@ -793,6 +823,8 @@ async function runBuild(options) {
|
|
|
793
823
|
}
|
|
794
824
|
async function runUp(options) {
|
|
795
825
|
const context = await loadConfigContext(options.config);
|
|
826
|
+
const npmrcPath = resolveNpmrcSecret(options.npmrc);
|
|
827
|
+
if (npmrcPath === null) return;
|
|
796
828
|
if (!requireDocker()) return;
|
|
797
829
|
let artifactDir;
|
|
798
830
|
try {
|
|
@@ -802,7 +834,8 @@ async function runUp(options) {
|
|
|
802
834
|
contents: generateCompose({
|
|
803
835
|
hostPort: options.port,
|
|
804
836
|
host: options.host,
|
|
805
|
-
containerPort: CONTAINER_PORT
|
|
837
|
+
containerPort: CONTAINER_PORT,
|
|
838
|
+
npmrcPath
|
|
806
839
|
})
|
|
807
840
|
}
|
|
808
841
|
]);
|
|
@@ -826,7 +859,7 @@ async function runUp(options) {
|
|
|
826
859
|
}
|
|
827
860
|
|
|
828
861
|
// src/import-command.ts
|
|
829
|
-
import { existsSync as
|
|
862
|
+
import { existsSync as existsSync5 } from "fs";
|
|
830
863
|
import path6 from "path";
|
|
831
864
|
import { loadConfig as loadConfig4 } from "@skein-js/config";
|
|
832
865
|
import {
|
|
@@ -850,7 +883,7 @@ async function runImportLanggraph(options) {
|
|
|
850
883
|
const counts = describeSnapshot2(snapshot);
|
|
851
884
|
if (options.store === "memory") {
|
|
852
885
|
const stateFile = devStateFile(configDir);
|
|
853
|
-
if (
|
|
886
|
+
if (existsSync5(stateFile) && !options.force) {
|
|
854
887
|
console.error(
|
|
855
888
|
`skein: ${path6.relative(process.cwd(), stateFile)} already exists. Re-run with --force to overwrite it (or delete it first).`
|
|
856
889
|
);
|
|
@@ -885,7 +918,7 @@ async function runImportLanggraph(options) {
|
|
|
885
918
|
}
|
|
886
919
|
|
|
887
920
|
// src/start-command.ts
|
|
888
|
-
import { existsSync as
|
|
921
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
889
922
|
import path7 from "path";
|
|
890
923
|
import { loadConfig as loadConfig5 } from "@skein-js/config";
|
|
891
924
|
import { createExpressServer as createExpressServer2 } from "@skein-js/express";
|
|
@@ -896,7 +929,7 @@ var FORCE_EXIT_MS2 = 5e3;
|
|
|
896
929
|
var logger = createDevLogger();
|
|
897
930
|
function readBakedSchemas(configDir) {
|
|
898
931
|
const schemasFile = path7.join(configDir, "schemas.json");
|
|
899
|
-
if (!
|
|
932
|
+
if (!existsSync6(schemasFile)) {
|
|
900
933
|
throw new Error(
|
|
901
934
|
`no schemas.json next to the config \u2014 \`skein start\` expects a built artifact. Run \`skein build\` (or \`skein up\`), or use \`skein dev\` for a source project.`
|
|
902
935
|
);
|
|
@@ -1000,8 +1033,14 @@ program.command("start").description("Serve a pre-built .skein/build artifact (t
|
|
|
1000
1033
|
hostExplicit: command.getOptionValueSource("host") === "cli"
|
|
1001
1034
|
})
|
|
1002
1035
|
);
|
|
1003
|
-
program.command("up").description("Bring up the production stack (Docker Compose: app + Postgres + Redis).").option("-c, --config <path>", "Path to langgraph.json", "langgraph.json").option("-p, --port <port>", "Port to expose", parsePort, 8123).option("--host <host>", "Host to bind", "0.0.0.0").
|
|
1004
|
-
|
|
1036
|
+
program.command("up").description("Bring up the production stack (Docker Compose: app + Postgres + Redis).").option("-c, --config <path>", "Path to langgraph.json", "langgraph.json").option("-p, --port <port>", "Port to expose", parsePort, 8123).option("--host <host>", "Host to bind", "0.0.0.0").option(
|
|
1037
|
+
"-n, --npmrc <path>",
|
|
1038
|
+
"Path to an .npmrc for authenticating private-registry installs (wired in as a build secret)"
|
|
1039
|
+
).action((options) => runUp(options));
|
|
1040
|
+
program.command("build").description("Build a deployable Docker image from the config.").option("-c, --config <path>", "Path to langgraph.json", "langgraph.json").option("-t, --tag <tag>", "Image tag (defaults to the project directory name)").option(
|
|
1041
|
+
"-n, --npmrc <path>",
|
|
1042
|
+
"Path to an .npmrc for authenticating private-registry installs (passed to docker build as a BuildKit secret)"
|
|
1043
|
+
).action((options) => runBuild(options));
|
|
1005
1044
|
program.command("dockerfile").description("Emit a standalone Dockerfile from the config.").option("-c, --config <path>", "Path to langgraph.json", "langgraph.json").option("-o, --output <path>", "Write the Dockerfile here instead of stdout").action((options) => runDockerfile(options));
|
|
1006
1045
|
program.command("import-langgraph").description("Import an existing LangGraph in-memory dev state (.langgraph_api/) into skein.").option("-c, --config <path>", "Path to langgraph.json", "langgraph.json").option(
|
|
1007
1046
|
"--store <driver>",
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skein-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "The skein-js CLI — a drop-in replacement for the LangGraph CLI (dev/up/build/dockerfile).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Maina Wycliffe <wmmaina7@gmail.com>",
|
|
7
|
-
"homepage": "https://github.com/
|
|
7
|
+
"homepage": "https://github.com/skein-js/skein-js/tree/main/packages/cli#readme",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/
|
|
10
|
+
"url": "git+https://github.com/skein-js/skein-js.git",
|
|
11
11
|
"directory": "packages/cli"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/
|
|
14
|
+
"url": "https://github.com/skein-js/skein-js/issues"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"typescript",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"@commander-js/extra-typings": "~13.1.0",
|
|
48
48
|
"@langchain/langgraph-checkpoint-postgres": "^1.0.4",
|
|
49
49
|
"commander": "~13.1.0",
|
|
50
|
-
"@skein-js/
|
|
51
|
-
"@skein-js/
|
|
52
|
-
"@skein-js/
|
|
53
|
-
"@skein-js/
|
|
50
|
+
"@skein-js/config": "0.7.0",
|
|
51
|
+
"@skein-js/core": "0.7.0",
|
|
52
|
+
"@skein-js/runtime": "0.7.0",
|
|
53
|
+
"@skein-js/express": "0.7.0"
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
56
|
"vite": "^8.1.0"
|