@langchain/langgraph-cli 0.0.6 → 0.0.7
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/dist/cli/dev.mjs +16 -5
- package/dist/cli/dev.node.mjs +1 -1
- package/dist/docker/docker.mjs +76 -111
- package/dist/logging.mjs +25 -20
- package/dist/utils/config.mjs +52 -28
- package/package.json +1 -1
package/dist/cli/dev.mjs
CHANGED
|
@@ -37,11 +37,12 @@ builder
|
|
|
37
37
|
});
|
|
38
38
|
let hasOpenedFlag = false;
|
|
39
39
|
let child = undefined;
|
|
40
|
+
let hostUrl = "https://smith.langchain.com";
|
|
40
41
|
server.on("data", (data) => {
|
|
41
42
|
const response = z.object({ queryParams: z.string() }).parse(data);
|
|
42
43
|
if (options.browser && !hasOpenedFlag) {
|
|
43
44
|
hasOpenedFlag = true;
|
|
44
|
-
open(
|
|
45
|
+
open(`${hostUrl}/studio${response.queryParams}`);
|
|
45
46
|
}
|
|
46
47
|
});
|
|
47
48
|
// check if .gitignore already contains .langgraph-api
|
|
@@ -78,20 +79,30 @@ builder
|
|
|
78
79
|
const addedTarget = newWatch.filter((target) => !oldWatch.includes(target));
|
|
79
80
|
const removedTarget = oldWatch.filter((target) => !newWatch.includes(target));
|
|
80
81
|
watcher.unwatch(removedTarget).add(addedTarget);
|
|
81
|
-
|
|
82
|
+
try {
|
|
83
|
+
const { Client } = await import("langsmith");
|
|
84
|
+
const apiUrl = env?.["LANGSMITH_ENDPOINT"] ||
|
|
85
|
+
env?.["LANGCHAIN_ENDPOINT"] ||
|
|
86
|
+
undefined;
|
|
87
|
+
hostUrl = new Client({ apiUrl }).getHostUrl() || hostUrl;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// pass
|
|
91
|
+
}
|
|
92
|
+
return { config, env, hostUrl };
|
|
82
93
|
};
|
|
83
94
|
const launchServer = async () => {
|
|
84
|
-
const { config, env } = await prepareContext();
|
|
95
|
+
const { config, env, hostUrl } = await prepareContext();
|
|
85
96
|
if (child != null)
|
|
86
97
|
child.kill();
|
|
87
98
|
if ("python_version" in config) {
|
|
88
99
|
logger.warn("Launching Python server from @langchain/langgraph-cli is experimental. Please use the `langgraph-cli` package from PyPi instead.");
|
|
89
100
|
const { spawnPythonServer } = await import("./dev.python.mjs");
|
|
90
|
-
child = await spawnPythonServer({ ...options, rest: args }, { configPath, config, env }, { pid, projectCwd });
|
|
101
|
+
child = await spawnPythonServer({ ...options, rest: args }, { configPath, config, env, hostUrl }, { pid, projectCwd });
|
|
91
102
|
}
|
|
92
103
|
else {
|
|
93
104
|
const { spawnNodeServer } = await import("./dev.node.mjs");
|
|
94
|
-
child = await spawnNodeServer({ ...options, rest: args }, { configPath, config, env }, { pid, projectCwd });
|
|
105
|
+
child = await spawnNodeServer({ ...options, rest: args }, { configPath, config, env, hostUrl }, { pid, projectCwd });
|
|
95
106
|
}
|
|
96
107
|
};
|
|
97
108
|
watcher.on("all", async (_name, path) => {
|
package/dist/cli/dev.node.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import {} from "../utils/config.mjs";
|
|
4
4
|
export async function spawnNodeServer(args, context, options) {
|
|
5
5
|
const localUrl = `http://${args.host}:${args.port}`;
|
|
6
|
-
const studioUrl =
|
|
6
|
+
const studioUrl = `${context.hostUrl}/studio?baseUrl=${localUrl}`;
|
|
7
7
|
console.log(`
|
|
8
8
|
Welcome to
|
|
9
9
|
|
package/dist/docker/docker.mjs
CHANGED
|
@@ -12,21 +12,6 @@ async function exists(path) {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
export async function assembleLocalDeps(configPath, config) {
|
|
15
|
-
if ("node_version" in config) {
|
|
16
|
-
const rebuildFiles = [];
|
|
17
|
-
const workingDir = `/deps/${path.basename(path.dirname(configPath))}`;
|
|
18
|
-
rebuildFiles.push(path.resolve(path.dirname(configPath), "package.json"));
|
|
19
|
-
rebuildFiles.push(path.resolve(path.dirname(configPath), "package-lock.json"));
|
|
20
|
-
rebuildFiles.push(path.resolve(path.dirname(configPath), "yarn.lock"));
|
|
21
|
-
rebuildFiles.push(path.resolve(path.dirname(configPath), "pnpm-lock.yaml"));
|
|
22
|
-
return {
|
|
23
|
-
pipReqs: [],
|
|
24
|
-
realPkgs: {},
|
|
25
|
-
fauxPkgs: {},
|
|
26
|
-
rebuildFiles,
|
|
27
|
-
workingDir,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
15
|
const reserved = new Set([
|
|
31
16
|
"src",
|
|
32
17
|
"langgraph-api",
|
|
@@ -54,7 +39,8 @@ export async function assembleLocalDeps(configPath, config) {
|
|
|
54
39
|
const rebuildFiles = [];
|
|
55
40
|
let workingDir;
|
|
56
41
|
let reloadDir;
|
|
57
|
-
|
|
42
|
+
const dependencies = "dependencies" in config ? config.dependencies : [];
|
|
43
|
+
for (const localDep of dependencies) {
|
|
58
44
|
if (!localDep.startsWith("."))
|
|
59
45
|
continue;
|
|
60
46
|
const resolved = path.resolve(path.dirname(configPath), localDep);
|
|
@@ -130,6 +116,19 @@ export async function assembleLocalDeps(configPath, config) {
|
|
|
130
116
|
}
|
|
131
117
|
}
|
|
132
118
|
}
|
|
119
|
+
if ("node_version" in config) {
|
|
120
|
+
for (const name of [
|
|
121
|
+
"package.json",
|
|
122
|
+
"package-lock.json",
|
|
123
|
+
"yarn.lock",
|
|
124
|
+
"pnpm-lock.yaml",
|
|
125
|
+
"bun.lockb",
|
|
126
|
+
]) {
|
|
127
|
+
const jsFile = path.resolve(path.dirname(configPath), name);
|
|
128
|
+
rebuildFiles.push(jsFile);
|
|
129
|
+
}
|
|
130
|
+
workingDir ??= `/deps/${path.basename(path.dirname(configPath))}`;
|
|
131
|
+
}
|
|
133
132
|
return { pipReqs, realPkgs, fauxPkgs, workingDir, reloadDir, rebuildFiles };
|
|
134
133
|
}
|
|
135
134
|
async function updateGraphPaths(configPath, config, localDeps) {
|
|
@@ -168,30 +167,59 @@ async function updateGraphPaths(configPath, config, localDeps) {
|
|
|
168
167
|
}
|
|
169
168
|
}
|
|
170
169
|
export function getBaseImage(config) {
|
|
171
|
-
if ("python_version" in config) {
|
|
172
|
-
return `langchain/langgraph-api:${config._INTERNAL_docker_tag || config.python_version}`;
|
|
173
|
-
}
|
|
174
170
|
if ("node_version" in config) {
|
|
175
171
|
return `langchain/langgraphjs-api:${config._INTERNAL_docker_tag || config.node_version}`;
|
|
176
172
|
}
|
|
177
|
-
throw new Error("Invalid config type");
|
|
178
|
-
}
|
|
179
|
-
export async function configToDocker(configPath, config, localDeps, options) {
|
|
180
173
|
if ("python_version" in config) {
|
|
181
|
-
return
|
|
182
|
-
}
|
|
183
|
-
if ("node_version" in config) {
|
|
184
|
-
return nodeConfigToDocker(configPath, config, localDeps, options);
|
|
174
|
+
return `langchain/langgraph-api:${config._INTERNAL_docker_tag || config.python_version}`;
|
|
185
175
|
}
|
|
186
176
|
throw new Error("Invalid config type");
|
|
187
177
|
}
|
|
188
|
-
export async function
|
|
178
|
+
export async function configToDocker(configPath, config, localDeps, options) {
|
|
189
179
|
// figure out the package manager used here
|
|
190
|
-
const projectFolder = path.dirname(configPath);
|
|
191
180
|
const testFile = async (file) => fs
|
|
192
|
-
.stat(path.resolve(
|
|
181
|
+
.stat(path.resolve(path.dirname(configPath), file))
|
|
193
182
|
.then((a) => a.isFile())
|
|
194
183
|
.catch(() => false);
|
|
184
|
+
let pipInstall = `PYTHONDONTWRITEBYTECODE=1 pip install -c /api/constraints.txt`;
|
|
185
|
+
if ("python_version" in config && config.pip_config_file) {
|
|
186
|
+
pipInstall = `PIP_CONFIG_FILE=/pipconfig.txt ${pipInstall}`;
|
|
187
|
+
}
|
|
188
|
+
pipInstall = `--mount=type=cache,target=/root/.cache/pip ${pipInstall}`;
|
|
189
|
+
const pipConfigFile = "python_version" in config && config.pip_config_file
|
|
190
|
+
? `ADD ${config.pip_config_file} /pipconfig.txt`
|
|
191
|
+
: undefined;
|
|
192
|
+
const _pypiDeps = "python_version" in config
|
|
193
|
+
? config.dependencies.filter((dep) => !dep.startsWith("."))
|
|
194
|
+
: [];
|
|
195
|
+
await updateGraphPaths(configPath, config, localDeps);
|
|
196
|
+
const pipPkgs = _pypiDeps.length
|
|
197
|
+
? `RUN ${pipInstall} ${_pypiDeps.join(" ")}`
|
|
198
|
+
: undefined;
|
|
199
|
+
const pipReqs = localDeps.pipReqs.map(([reqpath, destpath]) => `ADD ${reqpath} ${destpath}`);
|
|
200
|
+
if (pipReqs.length) {
|
|
201
|
+
pipReqs.push(`RUN ${pipInstall} ${localDeps.pipReqs.map(([, r]) => `-r ${r}`).join(" ")}`);
|
|
202
|
+
}
|
|
203
|
+
const localPkg = Object.entries(localDeps.realPkgs).map(([fullpath, relpath]) => `ADD ${relpath} /deps/${path.basename(fullpath)}`);
|
|
204
|
+
const fauxPkgs = Object.entries(localDeps.fauxPkgs).flatMap(([fullpath, [relpath, destpath]]) => [
|
|
205
|
+
`ADD ${relpath} ${destpath}`,
|
|
206
|
+
dedenter `
|
|
207
|
+
RUN set -ex && \
|
|
208
|
+
for line in '[project]' \
|
|
209
|
+
'name = "${path.basename(fullpath)}"' \
|
|
210
|
+
'version = "0.1"' \
|
|
211
|
+
'[tool.setuptools.package-data]' \
|
|
212
|
+
'"*" = ["**/*"]'; do \
|
|
213
|
+
echo "${options?.dockerCommand === "build" ? "$line" : "$$line"}" >> /deps/__outer_${path.basename(fullpath)}/pyproject.toml; \
|
|
214
|
+
done
|
|
215
|
+
`,
|
|
216
|
+
]);
|
|
217
|
+
if (!pipReqs.length &&
|
|
218
|
+
!localPkg.length &&
|
|
219
|
+
!fauxPkgs.length &&
|
|
220
|
+
"node_version" in config) {
|
|
221
|
+
pipReqs.push(`ADD . ${localDeps.workingDir}`);
|
|
222
|
+
}
|
|
195
223
|
const [npm, yarn, pnpm, bun] = await Promise.all([
|
|
196
224
|
testFile("package-lock.json"),
|
|
197
225
|
testFile("yarn.lock"),
|
|
@@ -213,94 +241,31 @@ export async function nodeConfigToDocker(configPath, config, localDeps, options)
|
|
|
213
241
|
}
|
|
214
242
|
const lines = [
|
|
215
243
|
`FROM ${getBaseImage(config)}`,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
...(config.auth
|
|
224
|
-
? [`ENV LANGGRAPH_AUTH='${JSON.stringify(config.auth)}'`]
|
|
225
|
-
: []),
|
|
226
|
-
`WORKDIR ${localDeps.workingDir}`,
|
|
227
|
-
`RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts`,
|
|
228
|
-
];
|
|
229
|
-
if (options?.watch) {
|
|
230
|
-
// TODO: hacky, should add as entrypoint to the langgraph-api base image
|
|
231
|
-
lines.push(`CMD exec uvicorn langgraph_api.server:app --log-config /api/logging.json --no-access-log --host 0.0.0.0 --port 8000 --reload --reload-dir ${localDeps.workingDir}`);
|
|
232
|
-
}
|
|
233
|
-
return lines.filter(Boolean).join("\n");
|
|
234
|
-
}
|
|
235
|
-
export async function pythonConfigToDocker(configPath, config, localDeps, options) {
|
|
236
|
-
let pipInstall = `PYTHONDONTWRITEBYTECODE=1 pip install -c /api/constraints.txt`;
|
|
237
|
-
if (config.pip_config_file) {
|
|
238
|
-
pipInstall = `PIP_CONFIG_FILE=/pipconfig.txt ${pipInstall}`;
|
|
239
|
-
}
|
|
240
|
-
pipInstall = `--mount=type=cache,target=/root/.cache/pip ${pipInstall}`;
|
|
241
|
-
const pipConfigFileStr = config.pip_config_file
|
|
242
|
-
? [`ADD ${config.pip_config_file} /pipconfig.txt`].join("\n")
|
|
243
|
-
: "";
|
|
244
|
-
const pypiDeps = config.dependencies.filter((dep) => !dep.startsWith("."));
|
|
245
|
-
await updateGraphPaths(configPath, config, localDeps);
|
|
246
|
-
const pipPkgsStr = pypiDeps.length
|
|
247
|
-
? `RUN ${pipInstall} ${pypiDeps.join(" ")}`
|
|
248
|
-
: "";
|
|
249
|
-
let pipReqStr;
|
|
250
|
-
if (localDeps.pipReqs.length) {
|
|
251
|
-
const pipReqsStr = localDeps.pipReqs
|
|
252
|
-
.map(([reqpath, destpath]) => `ADD ${reqpath} ${destpath}`)
|
|
253
|
-
.join("\n");
|
|
254
|
-
pipReqStr = `${pipReqsStr}\nRUN ${pipInstall} ${localDeps.pipReqs.map(([, r]) => `-r ${r}`)}`;
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
pipReqStr = "";
|
|
258
|
-
}
|
|
259
|
-
const localPkgStr = Object.entries(localDeps.realPkgs)
|
|
260
|
-
.map(([fullpath, relpath]) => `ADD ${relpath} /deps/${path.basename(fullpath)}`)
|
|
261
|
-
.join("\n");
|
|
262
|
-
const fauxPkgsStr = Object.entries(localDeps.fauxPkgs)
|
|
263
|
-
.map(([fullpath, [relpath, destpath]]) => {
|
|
264
|
-
const x = dedenter `
|
|
265
|
-
ADD ${relpath} ${destpath}
|
|
266
|
-
RUN set -ex && \
|
|
267
|
-
for line in '[project]' \
|
|
268
|
-
'name = "${path.basename(fullpath)}"' \
|
|
269
|
-
'version = "0.1"' \
|
|
270
|
-
'[tool.setuptools.package-data]' \
|
|
271
|
-
'"*" = ["**/*"]'; do \
|
|
272
|
-
echo "${options?.dockerCommand === "build" ? "$line" : "$$line"}" >> /deps/__outer_${path.basename(fullpath)}/pyproject.toml; \
|
|
273
|
-
done
|
|
274
|
-
`;
|
|
275
|
-
return x;
|
|
276
|
-
})
|
|
277
|
-
.join("\n");
|
|
278
|
-
const lines = [
|
|
279
|
-
`FROM ${getBaseImage(config)}`,
|
|
280
|
-
...config.dockerfile_lines,
|
|
281
|
-
pipConfigFileStr,
|
|
282
|
-
pipPkgsStr,
|
|
283
|
-
pipReqStr,
|
|
284
|
-
localPkgStr,
|
|
285
|
-
fauxPkgsStr,
|
|
286
|
-
`RUN ${pipInstall} -e /deps/*`,
|
|
244
|
+
config.dockerfile_lines,
|
|
245
|
+
pipConfigFile,
|
|
246
|
+
pipPkgs,
|
|
247
|
+
pipReqs,
|
|
248
|
+
localPkg,
|
|
249
|
+
fauxPkgs,
|
|
250
|
+
"python_version" in config ? `RUN ${pipInstall} -e /deps/*` : undefined,
|
|
287
251
|
`ENV LANGSERVE_GRAPHS='${JSON.stringify(config.graphs)}'`,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
? [
|
|
293
|
-
|
|
294
|
-
|
|
252
|
+
!!config.store && `ENV LANGGRAPH_STORE='${JSON.stringify(config.store)}'`,
|
|
253
|
+
!!config.auth && `ENV LANGGRAPH_AUTH='${JSON.stringify(config.auth)}'`,
|
|
254
|
+
!!localDeps.workingDir && `WORKDIR ${localDeps.workingDir}`,
|
|
255
|
+
"node_version" in config
|
|
256
|
+
? [
|
|
257
|
+
`RUN ${installCmd}`,
|
|
258
|
+
`RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts`,
|
|
259
|
+
]
|
|
260
|
+
: undefined,
|
|
295
261
|
];
|
|
296
262
|
if (options?.watch && (localDeps.workingDir || localDeps.reloadDir)) {
|
|
297
263
|
// TODO: hacky, should add as entrypoint to the langgraph-api base image
|
|
298
264
|
lines.push(`CMD exec uvicorn langgraph_api.server:app --log-config /api/logging.json --no-access-log --host 0.0.0.0 --port 8000 --reload --reload-dir ${localDeps.workingDir || localDeps.reloadDir}`);
|
|
299
265
|
}
|
|
300
|
-
return lines.filter(Boolean).join("\n");
|
|
266
|
+
return lines.flat().filter(Boolean).join("\n");
|
|
301
267
|
}
|
|
302
|
-
export async function configToWatch(configPath, config) {
|
|
303
|
-
const localDeps = await assembleLocalDeps(configPath, config);
|
|
268
|
+
export async function configToWatch(configPath, config, localDeps) {
|
|
304
269
|
const projectDir = path.dirname(configPath);
|
|
305
270
|
const watch = [];
|
|
306
271
|
const watchSources = "python_version" in config
|
|
@@ -367,7 +332,7 @@ export async function configToCompose(configPath, config, options) {
|
|
|
367
332
|
});
|
|
368
333
|
}
|
|
369
334
|
if (options?.watch) {
|
|
370
|
-
const watch = await configToWatch(configPath, config);
|
|
335
|
+
const watch = await configToWatch(configPath, config, localDeps);
|
|
371
336
|
if (watch)
|
|
372
337
|
result.develop = { watch };
|
|
373
338
|
}
|
package/dist/logging.mjs
CHANGED
|
@@ -50,27 +50,32 @@ const formatStack = (stack) => {
|
|
|
50
50
|
const [firstFile] = stacktraceParser(stack).filter((item) => !item.file?.split(path.sep).includes("node_modules") &&
|
|
51
51
|
!item.file?.startsWith("node:"));
|
|
52
52
|
if (firstFile?.file && firstFile?.lineNumber) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
try {
|
|
54
|
+
const filePath = firstFile.file;
|
|
55
|
+
const line = firstFile.lineNumber;
|
|
56
|
+
const column = firstFile.column ?? 0;
|
|
57
|
+
const messageLines = stack.split("\n");
|
|
58
|
+
const spliceIndex = messageLines.findIndex((i) => i.includes(filePath));
|
|
59
|
+
const padding = " ".repeat(Math.max(0, messageLines[spliceIndex].indexOf("at")));
|
|
60
|
+
const highlightCode = process.stdout.isTTY;
|
|
61
|
+
let codeFrame = codeFrameColumns(readFileSync(filePath, "utf-8"), { start: { line, column } }, { highlightCode });
|
|
62
|
+
codeFrame = codeFrame
|
|
63
|
+
.split("\n")
|
|
64
|
+
.map((i) => padding + i + "\x1b[0m")
|
|
65
|
+
.join("\n");
|
|
66
|
+
if (highlightCode) {
|
|
67
|
+
codeFrame = "\x1b[36m" + codeFrame + "\x1b[31m";
|
|
68
|
+
}
|
|
69
|
+
// insert codeframe after the line but dont lose the stack
|
|
70
|
+
return [
|
|
71
|
+
...messageLines.slice(0, spliceIndex + 1),
|
|
72
|
+
codeFrame,
|
|
73
|
+
...messageLines.slice(spliceIndex + 1),
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// pass
|
|
67
78
|
}
|
|
68
|
-
// insert codeframe after the line but dont lose the stack
|
|
69
|
-
return [
|
|
70
|
-
...messageLines.slice(0, spliceIndex + 1),
|
|
71
|
-
codeFrame,
|
|
72
|
-
...messageLines.slice(spliceIndex + 1),
|
|
73
|
-
].join("\n");
|
|
74
79
|
}
|
|
75
80
|
return stack;
|
|
76
81
|
};
|
package/dist/utils/config.mjs
CHANGED
|
@@ -1,47 +1,71 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
path: z.string().optional(),
|
|
4
|
-
disable_studio_auth: z.boolean().default(false),
|
|
5
|
-
});
|
|
6
|
-
const IndexConfigSchema = z.object({
|
|
7
|
-
dims: z.number().optional(),
|
|
8
|
-
embed: z.string().optional(),
|
|
9
|
-
fields: z.array(z.string()).optional(),
|
|
10
|
-
});
|
|
11
|
-
const StoreConfigSchema = z.object({
|
|
12
|
-
index: IndexConfigSchema.optional(),
|
|
13
|
-
});
|
|
2
|
+
import { extname } from "node:path";
|
|
14
3
|
const BaseConfigSchema = z.object({
|
|
15
4
|
docker_compose_file: z.string().optional(),
|
|
16
5
|
dockerfile_lines: z.array(z.string()).default([]),
|
|
17
|
-
graphs: z.record(z.string()),
|
|
6
|
+
graphs: z.record(z.string().refine((i) => i.includes(":"), {
|
|
7
|
+
message: "Import string must be in format '<file>:<export>'",
|
|
8
|
+
})),
|
|
9
|
+
_INTERNAL_docker_tag: z.string().optional(),
|
|
18
10
|
env: z
|
|
19
11
|
.union([z.array(z.string()), z.record(z.string()), z.string()])
|
|
20
12
|
.default({}),
|
|
21
|
-
store:
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
store: z
|
|
14
|
+
.object({
|
|
15
|
+
index: z
|
|
16
|
+
.object({
|
|
17
|
+
dims: z.number().optional(),
|
|
18
|
+
embed: z.string().optional(),
|
|
19
|
+
fields: z.array(z.string()).optional(),
|
|
20
|
+
})
|
|
21
|
+
.optional(),
|
|
22
|
+
})
|
|
23
|
+
.optional(),
|
|
24
|
+
auth: z
|
|
25
|
+
.object({
|
|
26
|
+
path: z.string().optional(),
|
|
27
|
+
disable_studio_auth: z.boolean().default(false),
|
|
28
|
+
})
|
|
29
|
+
.optional(),
|
|
24
30
|
});
|
|
31
|
+
const DEFAULT_PYTHON_VERSION = "3.11";
|
|
32
|
+
const DEFAULT_NODE_VERSION = "20";
|
|
33
|
+
const PYTHON_EXTENSIONS = [".py", ".pyx", ".pyd", ".pyi"];
|
|
34
|
+
const PythonVersionSchema = z.union([z.literal("3.11"), z.literal("3.12")]);
|
|
35
|
+
const NodeVersionSchema = z.literal("20");
|
|
25
36
|
const PythonConfigSchema = BaseConfigSchema.merge(z.object({
|
|
26
|
-
python_version: z
|
|
27
|
-
.union([z.literal("3.11"), z.literal("3.12")])
|
|
28
|
-
.default("3.11"),
|
|
29
37
|
pip_config_file: z.string().optional(),
|
|
30
38
|
dependencies: z
|
|
31
39
|
.array(z.string())
|
|
32
40
|
.nonempty("You need to specify at least one dependency"),
|
|
41
|
+
})).merge(z.object({
|
|
42
|
+
python_version: PythonVersionSchema.default(DEFAULT_PYTHON_VERSION),
|
|
43
|
+
node_version: NodeVersionSchema.optional(),
|
|
33
44
|
}));
|
|
34
|
-
const NodeConfigSchema = BaseConfigSchema.merge(z.object({ node_version:
|
|
45
|
+
const NodeConfigSchema = BaseConfigSchema.merge(z.object({ node_version: NodeVersionSchema.default(DEFAULT_NODE_VERSION) }));
|
|
35
46
|
const ConfigSchema = z.union([NodeConfigSchema, PythonConfigSchema]);
|
|
36
|
-
import * as path from "node:path";
|
|
37
|
-
const PYTHON_EXTENSIONS = [".py", ".pyx", ".pyd", ".pyi"];
|
|
38
47
|
// TODO: implement this in Python CLI
|
|
39
48
|
export const getConfig = (config) => {
|
|
40
|
-
|
|
41
|
-
const { graphs } = BaseConfigSchema.parse(
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
let input = typeof config === "string" ? JSON.parse(config) : config;
|
|
50
|
+
const { graphs } = BaseConfigSchema.parse(input);
|
|
51
|
+
const isPython = Object.values(graphs).map((i) => PYTHON_EXTENSIONS.includes(extname(i.split(":")[0])));
|
|
52
|
+
const somePython = isPython.some((i) => i);
|
|
53
|
+
const someNode = !isPython.every((i) => i);
|
|
54
|
+
const node_version = someNode
|
|
55
|
+
? input.node_version || DEFAULT_NODE_VERSION
|
|
56
|
+
: undefined;
|
|
57
|
+
const python_version = somePython
|
|
58
|
+
? input.python_version || (someNode ? "3.12" : DEFAULT_PYTHON_VERSION)
|
|
59
|
+
: undefined;
|
|
60
|
+
if (node_version && python_version && python_version !== "3.12") {
|
|
61
|
+
throw new Error("Only Python 3.12 is supported with Node.js");
|
|
45
62
|
}
|
|
46
|
-
|
|
63
|
+
input = { ...input, node_version, python_version };
|
|
64
|
+
if (!input.node_version)
|
|
65
|
+
delete input.node_version;
|
|
66
|
+
if (!input.python_version)
|
|
67
|
+
delete input.python_version;
|
|
68
|
+
if (python_version)
|
|
69
|
+
return PythonConfigSchema.parse(input);
|
|
70
|
+
return NodeConfigSchema.parse(input);
|
|
47
71
|
};
|