@chat-js/cli 0.6.2 → 0.6.3
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/index.js +646 -400
- package/package.json +1 -1
- package/templates/chat-app/lib/ai/gateway-model-defaults.ts +24 -0
- package/templates/chat-app/lib/config-schema.ts +11 -0
- package/templates/chat-app/tools/platform/tools.ts +34 -10
- package/templates/electron/CHANGELOG.md +9 -1
- package/templates/electron/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3844,7 +3844,7 @@ var {
|
|
|
3844
3844
|
// package.json
|
|
3845
3845
|
var package_default = {
|
|
3846
3846
|
name: "@chat-js/cli",
|
|
3847
|
-
version: "0.6.
|
|
3847
|
+
version: "0.6.3",
|
|
3848
3848
|
description: "CLI for creating and extending ChatJS apps",
|
|
3849
3849
|
license: "Apache-2.0",
|
|
3850
3850
|
repository: {
|
|
@@ -3895,8 +3895,8 @@ var package_default = {
|
|
|
3895
3895
|
};
|
|
3896
3896
|
|
|
3897
3897
|
// src/commands/add.ts
|
|
3898
|
-
import
|
|
3899
|
-
import
|
|
3898
|
+
import fs6 from "node:fs/promises";
|
|
3899
|
+
import path8 from "node:path";
|
|
3900
3900
|
|
|
3901
3901
|
// ../../node_modules/.bun/@clack+core@1.2.0/node_modules/@clack/core/dist/index.mjs
|
|
3902
3902
|
import { styleText as y } from "node:util";
|
|
@@ -5184,9 +5184,96 @@ ${c2}
|
|
|
5184
5184
|
}
|
|
5185
5185
|
} }).prompt();
|
|
5186
5186
|
|
|
5187
|
-
// src/
|
|
5188
|
-
import
|
|
5187
|
+
// src/utils/get-config.ts
|
|
5188
|
+
import { spawn } from "node:child_process";
|
|
5189
|
+
import path2 from "node:path";
|
|
5190
|
+
|
|
5191
|
+
// src/utils/get-package-manager.ts
|
|
5192
|
+
import fs from "node:fs";
|
|
5189
5193
|
import path from "node:path";
|
|
5194
|
+
function inferPackageManager(cwd = process.cwd()) {
|
|
5195
|
+
let currentDir = path.resolve(cwd);
|
|
5196
|
+
while (true) {
|
|
5197
|
+
if (fs.existsSync(path.join(currentDir, "pnpm-lock.yaml")))
|
|
5198
|
+
return "pnpm";
|
|
5199
|
+
if (fs.existsSync(path.join(currentDir, "yarn.lock")))
|
|
5200
|
+
return "yarn";
|
|
5201
|
+
if (fs.existsSync(path.join(currentDir, "package-lock.json")))
|
|
5202
|
+
return "npm";
|
|
5203
|
+
if (fs.existsSync(path.join(currentDir, "bun.lock")) || fs.existsSync(path.join(currentDir, "bun.lockb"))) {
|
|
5204
|
+
return "bun";
|
|
5205
|
+
}
|
|
5206
|
+
const parentDir = path.dirname(currentDir);
|
|
5207
|
+
if (parentDir === currentDir) {
|
|
5208
|
+
break;
|
|
5209
|
+
}
|
|
5210
|
+
currentDir = parentDir;
|
|
5211
|
+
}
|
|
5212
|
+
const ua = process.env.npm_config_user_agent ?? "";
|
|
5213
|
+
if (ua.startsWith("pnpm/"))
|
|
5214
|
+
return "pnpm";
|
|
5215
|
+
if (ua.startsWith("yarn/"))
|
|
5216
|
+
return "yarn";
|
|
5217
|
+
if (ua.startsWith("npm/"))
|
|
5218
|
+
return "npm";
|
|
5219
|
+
if (ua.startsWith("bun/"))
|
|
5220
|
+
return "bun";
|
|
5221
|
+
return "npm";
|
|
5222
|
+
}
|
|
5223
|
+
|
|
5224
|
+
// src/utils/get-config.ts
|
|
5225
|
+
var EVAL_SCRIPT = `
|
|
5226
|
+
import userConfig from "./chat.config.ts";
|
|
5227
|
+
import { applyDefaults } from "./lib/config-schema";
|
|
5228
|
+
process.stdout.write(JSON.stringify(applyDefaults(userConfig)));
|
|
5229
|
+
`;
|
|
5230
|
+
function getTsEvalCommand(pm) {
|
|
5231
|
+
switch (pm) {
|
|
5232
|
+
case "bun":
|
|
5233
|
+
return ["bun", ["--eval", EVAL_SCRIPT]];
|
|
5234
|
+
case "pnpm":
|
|
5235
|
+
return ["pnpm", ["dlx", "tsx", "--eval", EVAL_SCRIPT]];
|
|
5236
|
+
case "yarn":
|
|
5237
|
+
return ["yarn", ["dlx", "tsx", "--eval", EVAL_SCRIPT]];
|
|
5238
|
+
default:
|
|
5239
|
+
return ["npx", ["tsx", "--eval", EVAL_SCRIPT]];
|
|
5240
|
+
}
|
|
5241
|
+
}
|
|
5242
|
+
async function loadProjectConfig(cwd) {
|
|
5243
|
+
const pm = inferPackageManager(cwd);
|
|
5244
|
+
const [cmd, args] = getTsEvalCommand(pm);
|
|
5245
|
+
const stdout = await new Promise((resolve, reject) => {
|
|
5246
|
+
const child = spawn(cmd, args, {
|
|
5247
|
+
cwd,
|
|
5248
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
5249
|
+
});
|
|
5250
|
+
const out = [];
|
|
5251
|
+
const err = [];
|
|
5252
|
+
child.stdout?.on("data", (d3) => out.push(String(d3)));
|
|
5253
|
+
child.stderr?.on("data", (d3) => err.push(String(d3)));
|
|
5254
|
+
child.on("error", (e) => reject(new Error(`Could not spawn ${cmd}. Make sure ${pm} is installed.
|
|
5255
|
+
${e.message}`)));
|
|
5256
|
+
child.on("close", (code) => {
|
|
5257
|
+
if (code === 0)
|
|
5258
|
+
resolve(out.join(""));
|
|
5259
|
+
else
|
|
5260
|
+
reject(new Error(`Failed to load config:
|
|
5261
|
+
${err.join("").trim()}`));
|
|
5262
|
+
});
|
|
5263
|
+
});
|
|
5264
|
+
const parsed = JSON.parse(stdout);
|
|
5265
|
+
return {
|
|
5266
|
+
paths: {
|
|
5267
|
+
tools: parsed?.paths?.tools ?? "@/tools/chatjs"
|
|
5268
|
+
}
|
|
5269
|
+
};
|
|
5270
|
+
}
|
|
5271
|
+
function resolveToolsPath(alias, cwd) {
|
|
5272
|
+
if (alias.startsWith("@/")) {
|
|
5273
|
+
return path2.join(cwd, alias.slice(2));
|
|
5274
|
+
}
|
|
5275
|
+
return path2.resolve(cwd, alias);
|
|
5276
|
+
}
|
|
5190
5277
|
|
|
5191
5278
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
5192
5279
|
var exports_external = {};
|
|
@@ -5953,10 +6040,10 @@ function mergeDefs(...defs) {
|
|
|
5953
6040
|
function cloneDef(schema) {
|
|
5954
6041
|
return mergeDefs(schema._zod.def);
|
|
5955
6042
|
}
|
|
5956
|
-
function getElementAtPath(obj,
|
|
5957
|
-
if (!
|
|
6043
|
+
function getElementAtPath(obj, path3) {
|
|
6044
|
+
if (!path3)
|
|
5958
6045
|
return obj;
|
|
5959
|
-
return
|
|
6046
|
+
return path3.reduce((acc, key) => acc?.[key], obj);
|
|
5960
6047
|
}
|
|
5961
6048
|
function promiseAllObject(promisesObj) {
|
|
5962
6049
|
const keys = Object.keys(promisesObj);
|
|
@@ -6337,11 +6424,11 @@ function aborted(x, startIndex = 0) {
|
|
|
6337
6424
|
}
|
|
6338
6425
|
return false;
|
|
6339
6426
|
}
|
|
6340
|
-
function prefixIssues(
|
|
6427
|
+
function prefixIssues(path3, issues) {
|
|
6341
6428
|
return issues.map((iss) => {
|
|
6342
6429
|
var _a;
|
|
6343
6430
|
(_a = iss).path ?? (_a.path = []);
|
|
6344
|
-
iss.path.unshift(
|
|
6431
|
+
iss.path.unshift(path3);
|
|
6345
6432
|
return iss;
|
|
6346
6433
|
});
|
|
6347
6434
|
}
|
|
@@ -6524,7 +6611,7 @@ function formatError(error, mapper = (issue2) => issue2.message) {
|
|
|
6524
6611
|
}
|
|
6525
6612
|
function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
6526
6613
|
const result = { errors: [] };
|
|
6527
|
-
const processError = (error2,
|
|
6614
|
+
const processError = (error2, path3 = []) => {
|
|
6528
6615
|
var _a, _b;
|
|
6529
6616
|
for (const issue2 of error2.issues) {
|
|
6530
6617
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -6534,7 +6621,7 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
6534
6621
|
} else if (issue2.code === "invalid_element") {
|
|
6535
6622
|
processError({ issues: issue2.issues }, issue2.path);
|
|
6536
6623
|
} else {
|
|
6537
|
-
const fullpath = [...
|
|
6624
|
+
const fullpath = [...path3, ...issue2.path];
|
|
6538
6625
|
if (fullpath.length === 0) {
|
|
6539
6626
|
result.errors.push(mapper(issue2));
|
|
6540
6627
|
continue;
|
|
@@ -6566,8 +6653,8 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
|
|
|
6566
6653
|
}
|
|
6567
6654
|
function toDotPath(_path) {
|
|
6568
6655
|
const segs = [];
|
|
6569
|
-
const
|
|
6570
|
-
for (const seg of
|
|
6656
|
+
const path3 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
6657
|
+
for (const seg of path3) {
|
|
6571
6658
|
if (typeof seg === "number")
|
|
6572
6659
|
segs.push(`[${seg}]`);
|
|
6573
6660
|
else if (typeof seg === "symbol")
|
|
@@ -18314,13 +18401,13 @@ function resolveRef(ref, ctx) {
|
|
|
18314
18401
|
if (!ref.startsWith("#")) {
|
|
18315
18402
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
18316
18403
|
}
|
|
18317
|
-
const
|
|
18318
|
-
if (
|
|
18404
|
+
const path3 = ref.slice(1).split("/").filter(Boolean);
|
|
18405
|
+
if (path3.length === 0) {
|
|
18319
18406
|
return ctx.rootSchema;
|
|
18320
18407
|
}
|
|
18321
18408
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
18322
|
-
if (
|
|
18323
|
-
const key =
|
|
18409
|
+
if (path3[0] === defsKey) {
|
|
18410
|
+
const key = path3[1];
|
|
18324
18411
|
if (!key || !ctx.defs[key]) {
|
|
18325
18412
|
throw new Error(`Reference not found: ${ref}`);
|
|
18326
18413
|
}
|
|
@@ -18720,173 +18807,6 @@ function date4(params) {
|
|
|
18720
18807
|
|
|
18721
18808
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
18722
18809
|
config(en_default());
|
|
18723
|
-
// src/registry/schema.ts
|
|
18724
|
-
var envRequirementSchema = exports_external.object({
|
|
18725
|
-
description: exports_external.string().optional(),
|
|
18726
|
-
options: exports_external.array(exports_external.array(exports_external.string()))
|
|
18727
|
-
});
|
|
18728
|
-
var registryToolFileSchema = exports_external.object({
|
|
18729
|
-
path: exports_external.string(),
|
|
18730
|
-
content: exports_external.string(),
|
|
18731
|
-
type: exports_external.enum(["tool", "renderer", "lib", "component", "hook"]),
|
|
18732
|
-
target: exports_external.string()
|
|
18733
|
-
});
|
|
18734
|
-
var registryToolItemSchema = exports_external.object({
|
|
18735
|
-
name: exports_external.string(),
|
|
18736
|
-
description: exports_external.string().optional(),
|
|
18737
|
-
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
18738
|
-
devDependencies: exports_external.array(exports_external.string()).optional(),
|
|
18739
|
-
registryDependencies: exports_external.array(exports_external.string()).optional(),
|
|
18740
|
-
envRequirements: exports_external.array(envRequirementSchema).optional(),
|
|
18741
|
-
files: exports_external.array(registryToolFileSchema)
|
|
18742
|
-
});
|
|
18743
|
-
|
|
18744
|
-
// src/registry/fetch.ts
|
|
18745
|
-
var DEFAULT_REGISTRY_URL = "https://registry.chatjs.dev/items/{name}.json";
|
|
18746
|
-
function getRegistryUrl(override) {
|
|
18747
|
-
return override ?? process.env.CHATJS_REGISTRY_URL ?? DEFAULT_REGISTRY_URL;
|
|
18748
|
-
}
|
|
18749
|
-
async function fetchRegistryItem(name, registryUrl) {
|
|
18750
|
-
const template = getRegistryUrl(registryUrl);
|
|
18751
|
-
const resolved = template.replace("{name}", encodeURIComponent(name));
|
|
18752
|
-
const isLocalPath = resolved.startsWith(".") || path.isAbsolute(resolved);
|
|
18753
|
-
const filePath = resolved.startsWith(".") ? path.resolve(process.cwd(), resolved) : resolved;
|
|
18754
|
-
let raw;
|
|
18755
|
-
if (isLocalPath) {
|
|
18756
|
-
const content = await fs.readFile(filePath, "utf8").catch(() => {
|
|
18757
|
-
throw new Error(`Tool "${name}" not found at ${filePath}`);
|
|
18758
|
-
});
|
|
18759
|
-
raw = JSON.parse(content);
|
|
18760
|
-
} else {
|
|
18761
|
-
const res = await fetch(filePath).catch(() => {
|
|
18762
|
-
throw new Error(`Could not reach registry. Check your internet connection.`);
|
|
18763
|
-
});
|
|
18764
|
-
if (res.status === 404)
|
|
18765
|
-
throw new Error(`Tool "${name}" not found in registry.`);
|
|
18766
|
-
if (!res.ok)
|
|
18767
|
-
throw new Error(`Registry fetch failed: ${res.status} ${res.statusText}`);
|
|
18768
|
-
raw = await res.json();
|
|
18769
|
-
}
|
|
18770
|
-
return registryToolItemSchema.parse(raw);
|
|
18771
|
-
}
|
|
18772
|
-
|
|
18773
|
-
// src/registry/resolve.ts
|
|
18774
|
-
async function resolveRegistryItems(names, registryUrl) {
|
|
18775
|
-
const seen = new Set;
|
|
18776
|
-
const items = [];
|
|
18777
|
-
async function visit(name) {
|
|
18778
|
-
if (seen.has(name)) {
|
|
18779
|
-
return;
|
|
18780
|
-
}
|
|
18781
|
-
seen.add(name);
|
|
18782
|
-
const item = await fetchRegistryItem(name, registryUrl);
|
|
18783
|
-
for (const dependency of item.registryDependencies ?? []) {
|
|
18784
|
-
await visit(dependency);
|
|
18785
|
-
}
|
|
18786
|
-
items.push(item);
|
|
18787
|
-
}
|
|
18788
|
-
for (const name of names) {
|
|
18789
|
-
await visit(name);
|
|
18790
|
-
}
|
|
18791
|
-
return {
|
|
18792
|
-
items,
|
|
18793
|
-
dependencies: Array.from(new Set(items.flatMap((item) => item.dependencies ?? []))),
|
|
18794
|
-
devDependencies: Array.from(new Set(items.flatMap((item) => item.devDependencies ?? []))),
|
|
18795
|
-
files: items.flatMap((item) => item.files)
|
|
18796
|
-
};
|
|
18797
|
-
}
|
|
18798
|
-
|
|
18799
|
-
// src/utils/get-config.ts
|
|
18800
|
-
import { spawn } from "node:child_process";
|
|
18801
|
-
import path3 from "node:path";
|
|
18802
|
-
|
|
18803
|
-
// src/utils/get-package-manager.ts
|
|
18804
|
-
import fs2 from "node:fs";
|
|
18805
|
-
import path2 from "node:path";
|
|
18806
|
-
function inferPackageManager(cwd = process.cwd()) {
|
|
18807
|
-
let currentDir = path2.resolve(cwd);
|
|
18808
|
-
while (true) {
|
|
18809
|
-
if (fs2.existsSync(path2.join(currentDir, "pnpm-lock.yaml")))
|
|
18810
|
-
return "pnpm";
|
|
18811
|
-
if (fs2.existsSync(path2.join(currentDir, "yarn.lock")))
|
|
18812
|
-
return "yarn";
|
|
18813
|
-
if (fs2.existsSync(path2.join(currentDir, "package-lock.json")))
|
|
18814
|
-
return "npm";
|
|
18815
|
-
if (fs2.existsSync(path2.join(currentDir, "bun.lock")) || fs2.existsSync(path2.join(currentDir, "bun.lockb"))) {
|
|
18816
|
-
return "bun";
|
|
18817
|
-
}
|
|
18818
|
-
const parentDir = path2.dirname(currentDir);
|
|
18819
|
-
if (parentDir === currentDir) {
|
|
18820
|
-
break;
|
|
18821
|
-
}
|
|
18822
|
-
currentDir = parentDir;
|
|
18823
|
-
}
|
|
18824
|
-
const ua = process.env.npm_config_user_agent ?? "";
|
|
18825
|
-
if (ua.startsWith("pnpm/"))
|
|
18826
|
-
return "pnpm";
|
|
18827
|
-
if (ua.startsWith("yarn/"))
|
|
18828
|
-
return "yarn";
|
|
18829
|
-
if (ua.startsWith("npm/"))
|
|
18830
|
-
return "npm";
|
|
18831
|
-
if (ua.startsWith("bun/"))
|
|
18832
|
-
return "bun";
|
|
18833
|
-
return "npm";
|
|
18834
|
-
}
|
|
18835
|
-
|
|
18836
|
-
// src/utils/get-config.ts
|
|
18837
|
-
var EVAL_SCRIPT = `
|
|
18838
|
-
import userConfig from "./chat.config.ts";
|
|
18839
|
-
import { applyDefaults } from "./lib/config-schema";
|
|
18840
|
-
process.stdout.write(JSON.stringify(applyDefaults(userConfig)));
|
|
18841
|
-
`;
|
|
18842
|
-
function getTsEvalCommand(pm) {
|
|
18843
|
-
switch (pm) {
|
|
18844
|
-
case "bun":
|
|
18845
|
-
return ["bun", ["--eval", EVAL_SCRIPT]];
|
|
18846
|
-
case "pnpm":
|
|
18847
|
-
return ["pnpm", ["dlx", "tsx", "--eval", EVAL_SCRIPT]];
|
|
18848
|
-
case "yarn":
|
|
18849
|
-
return ["yarn", ["dlx", "tsx", "--eval", EVAL_SCRIPT]];
|
|
18850
|
-
default:
|
|
18851
|
-
return ["npx", ["tsx", "--eval", EVAL_SCRIPT]];
|
|
18852
|
-
}
|
|
18853
|
-
}
|
|
18854
|
-
async function loadProjectConfig(cwd) {
|
|
18855
|
-
const pm = inferPackageManager(cwd);
|
|
18856
|
-
const [cmd, args] = getTsEvalCommand(pm);
|
|
18857
|
-
const stdout = await new Promise((resolve, reject) => {
|
|
18858
|
-
const child = spawn(cmd, args, {
|
|
18859
|
-
cwd,
|
|
18860
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
18861
|
-
});
|
|
18862
|
-
const out = [];
|
|
18863
|
-
const err = [];
|
|
18864
|
-
child.stdout?.on("data", (d3) => out.push(String(d3)));
|
|
18865
|
-
child.stderr?.on("data", (d3) => err.push(String(d3)));
|
|
18866
|
-
child.on("error", (e) => reject(new Error(`Could not spawn ${cmd}. Make sure ${pm} is installed.
|
|
18867
|
-
${e.message}`)));
|
|
18868
|
-
child.on("close", (code) => {
|
|
18869
|
-
if (code === 0)
|
|
18870
|
-
resolve(out.join(""));
|
|
18871
|
-
else
|
|
18872
|
-
reject(new Error(`Failed to load config:
|
|
18873
|
-
${err.join("").trim()}`));
|
|
18874
|
-
});
|
|
18875
|
-
});
|
|
18876
|
-
const parsed = JSON.parse(stdout);
|
|
18877
|
-
return {
|
|
18878
|
-
paths: {
|
|
18879
|
-
tools: parsed?.paths?.tools ?? "@/tools/chatjs"
|
|
18880
|
-
}
|
|
18881
|
-
};
|
|
18882
|
-
}
|
|
18883
|
-
function resolveToolsPath(alias, cwd) {
|
|
18884
|
-
if (alias.startsWith("@/")) {
|
|
18885
|
-
return path3.join(cwd, alias.slice(2));
|
|
18886
|
-
}
|
|
18887
|
-
return path3.resolve(cwd, alias);
|
|
18888
|
-
}
|
|
18889
|
-
|
|
18890
18810
|
// ../../node_modules/.bun/kleur@4.1.5/node_modules/kleur/colors.mjs
|
|
18891
18811
|
var FORCE_COLOR;
|
|
18892
18812
|
var NODE_DISABLE_COLORS;
|
|
@@ -18993,6 +18913,118 @@ function handleError(error48) {
|
|
|
18993
18913
|
process.exit(1);
|
|
18994
18914
|
}
|
|
18995
18915
|
|
|
18916
|
+
// src/utils/install-registry-tools.ts
|
|
18917
|
+
import fs5 from "node:fs/promises";
|
|
18918
|
+
import path7 from "node:path";
|
|
18919
|
+
|
|
18920
|
+
// src/registry/fetch.ts
|
|
18921
|
+
import fs2 from "node:fs/promises";
|
|
18922
|
+
import path3 from "node:path";
|
|
18923
|
+
|
|
18924
|
+
// src/registry/schema.ts
|
|
18925
|
+
var envRequirementSchema = exports_external.object({
|
|
18926
|
+
description: exports_external.string().optional(),
|
|
18927
|
+
options: exports_external.array(exports_external.array(exports_external.string()))
|
|
18928
|
+
});
|
|
18929
|
+
var registryToolFileSchema = exports_external.object({
|
|
18930
|
+
path: exports_external.string(),
|
|
18931
|
+
content: exports_external.string(),
|
|
18932
|
+
type: exports_external.enum(["tool", "renderer", "lib", "component", "hook"]),
|
|
18933
|
+
target: exports_external.string()
|
|
18934
|
+
});
|
|
18935
|
+
var registryToolItemSchema = exports_external.object({
|
|
18936
|
+
name: exports_external.string(),
|
|
18937
|
+
description: exports_external.string().optional(),
|
|
18938
|
+
hidden: exports_external.boolean().optional(),
|
|
18939
|
+
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
18940
|
+
devDependencies: exports_external.array(exports_external.string()).optional(),
|
|
18941
|
+
registryDependencies: exports_external.array(exports_external.string()).optional(),
|
|
18942
|
+
envRequirements: exports_external.array(envRequirementSchema).optional(),
|
|
18943
|
+
files: exports_external.array(registryToolFileSchema)
|
|
18944
|
+
});
|
|
18945
|
+
var registryIndexItemSchema = exports_external.object({
|
|
18946
|
+
name: exports_external.string(),
|
|
18947
|
+
description: exports_external.string().optional(),
|
|
18948
|
+
hidden: exports_external.boolean().optional()
|
|
18949
|
+
});
|
|
18950
|
+
|
|
18951
|
+
// src/registry/fetch.ts
|
|
18952
|
+
var DEFAULT_REGISTRY_URL = "https://unpkg.com/@chat-js/registry@0/items/{name}.json";
|
|
18953
|
+
function getRegistryUrl(override) {
|
|
18954
|
+
return override ?? process.env.CHATJS_REGISTRY_URL ?? DEFAULT_REGISTRY_URL;
|
|
18955
|
+
}
|
|
18956
|
+
function getRegistryIndexUrl(registryUrl) {
|
|
18957
|
+
const template = getRegistryUrl(registryUrl);
|
|
18958
|
+
if (template.includes("{name}")) {
|
|
18959
|
+
return template.replace(/(\{name\}\.json|\{name\})$/, "index.json");
|
|
18960
|
+
}
|
|
18961
|
+
if (template.startsWith(".") || path3.isAbsolute(template)) {
|
|
18962
|
+
return path3.join(path3.dirname(template), "index.json");
|
|
18963
|
+
}
|
|
18964
|
+
return new URL("../index.json", template).toString();
|
|
18965
|
+
}
|
|
18966
|
+
async function fetchJson(source) {
|
|
18967
|
+
const isLocalPath = source.startsWith(".") || path3.isAbsolute(source);
|
|
18968
|
+
const filePath = source.startsWith(".") ? path3.resolve(process.cwd(), source) : source;
|
|
18969
|
+
if (isLocalPath) {
|
|
18970
|
+
const content = await fs2.readFile(filePath, "utf8").catch(() => {
|
|
18971
|
+
throw new Error(`Registry resource not found at ${filePath}`);
|
|
18972
|
+
});
|
|
18973
|
+
return JSON.parse(content);
|
|
18974
|
+
}
|
|
18975
|
+
const res = await fetch(filePath).catch(() => {
|
|
18976
|
+
throw new Error(`Could not reach registry. Check your internet connection.`);
|
|
18977
|
+
});
|
|
18978
|
+
if (res.status === 404) {
|
|
18979
|
+
throw new Error(`Registry resource not found: ${filePath}`);
|
|
18980
|
+
}
|
|
18981
|
+
if (!res.ok) {
|
|
18982
|
+
throw new Error(`Registry fetch failed: ${res.status} ${res.statusText}`);
|
|
18983
|
+
}
|
|
18984
|
+
return res.json();
|
|
18985
|
+
}
|
|
18986
|
+
async function fetchRegistryIndex(registryUrl) {
|
|
18987
|
+
const raw = await fetchJson(getRegistryIndexUrl(registryUrl));
|
|
18988
|
+
return registryIndexItemSchema.array().parse(raw);
|
|
18989
|
+
}
|
|
18990
|
+
async function fetchRegistryItem(name, registryUrl) {
|
|
18991
|
+
const template = getRegistryUrl(registryUrl);
|
|
18992
|
+
const resolved = template.replace("{name}", encodeURIComponent(name));
|
|
18993
|
+
const raw = await fetchJson(resolved).catch((error48) => {
|
|
18994
|
+
if (error48 instanceof Error && error48.message.startsWith("Registry resource not found")) {
|
|
18995
|
+
throw new Error(`Tool "${name}" not found in registry.`);
|
|
18996
|
+
}
|
|
18997
|
+
throw error48;
|
|
18998
|
+
});
|
|
18999
|
+
return registryToolItemSchema.parse(raw);
|
|
19000
|
+
}
|
|
19001
|
+
|
|
19002
|
+
// src/registry/resolve.ts
|
|
19003
|
+
async function resolveRegistryItems(names, registryUrl) {
|
|
19004
|
+
const seen = new Set;
|
|
19005
|
+
const items = [];
|
|
19006
|
+
async function visit(name) {
|
|
19007
|
+
if (seen.has(name)) {
|
|
19008
|
+
return;
|
|
19009
|
+
}
|
|
19010
|
+
seen.add(name);
|
|
19011
|
+
const item = await fetchRegistryItem(name, registryUrl);
|
|
19012
|
+
for (const dependency of item.registryDependencies ?? []) {
|
|
19013
|
+
await visit(dependency);
|
|
19014
|
+
}
|
|
19015
|
+
items.push(item);
|
|
19016
|
+
}
|
|
19017
|
+
for (const name of names) {
|
|
19018
|
+
await visit(name);
|
|
19019
|
+
}
|
|
19020
|
+
return {
|
|
19021
|
+
items,
|
|
19022
|
+
dependencies: Array.from(new Set(items.flatMap((item) => item.dependencies ?? []))),
|
|
19023
|
+
devDependencies: Array.from(new Set(items.flatMap((item) => item.devDependencies ?? []))),
|
|
19024
|
+
files: items.flatMap((item) => item.files)
|
|
19025
|
+
};
|
|
19026
|
+
}
|
|
19027
|
+
|
|
18996
19028
|
// src/utils/inject-tool.ts
|
|
18997
19029
|
var MARKERS = {
|
|
18998
19030
|
toolImports: {
|
|
@@ -19085,6 +19117,10 @@ function createEmptyUiTemplate() {
|
|
|
19085
19117
|
`;
|
|
19086
19118
|
}
|
|
19087
19119
|
|
|
19120
|
+
// src/utils/install-deps.ts
|
|
19121
|
+
import fs3 from "node:fs/promises";
|
|
19122
|
+
import path4 from "node:path";
|
|
19123
|
+
|
|
19088
19124
|
// src/utils/run-command.ts
|
|
19089
19125
|
import { spawn as spawn2 } from "node:child_process";
|
|
19090
19126
|
async function runCommand(command, args, cwd) {
|
|
@@ -19106,13 +19142,31 @@ ${stderr.join("")}`.trim()));
|
|
|
19106
19142
|
}
|
|
19107
19143
|
|
|
19108
19144
|
// src/utils/install-deps.ts
|
|
19109
|
-
async function
|
|
19145
|
+
async function updatePackageJsonDependencies(deps, devDeps, cwd) {
|
|
19146
|
+
const packageJsonPath = path4.join(cwd, "package.json");
|
|
19147
|
+
const packageJson = JSON.parse(await fs3.readFile(packageJsonPath, "utf8"));
|
|
19148
|
+
packageJson.dependencies ??= {};
|
|
19149
|
+
packageJson.devDependencies ??= {};
|
|
19150
|
+
for (const dependency of deps) {
|
|
19151
|
+
packageJson.dependencies[dependency] ??= "latest";
|
|
19152
|
+
}
|
|
19153
|
+
for (const dependency of devDeps) {
|
|
19154
|
+
packageJson.devDependencies[dependency] ??= "latest";
|
|
19155
|
+
}
|
|
19156
|
+
await fs3.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
|
|
19157
|
+
`);
|
|
19158
|
+
}
|
|
19159
|
+
async function installDependencies(deps, devDeps, cwd, installNow = true, packageManager) {
|
|
19110
19160
|
const dependencies = Array.from(new Set(deps));
|
|
19111
19161
|
const developmentDependencies = Array.from(new Set(devDeps));
|
|
19112
19162
|
if (!dependencies.length && !developmentDependencies.length) {
|
|
19113
19163
|
return;
|
|
19114
19164
|
}
|
|
19115
|
-
|
|
19165
|
+
if (!installNow) {
|
|
19166
|
+
await updatePackageJsonDependencies(dependencies, developmentDependencies, cwd);
|
|
19167
|
+
return;
|
|
19168
|
+
}
|
|
19169
|
+
const pm = packageManager ?? inferPackageManager(cwd);
|
|
19116
19170
|
if (dependencies.length) {
|
|
19117
19171
|
const args = pm === "yarn" ? ["add", ...dependencies] : ["add", ...dependencies];
|
|
19118
19172
|
await runCommand(pm, args, cwd);
|
|
@@ -20499,11 +20553,11 @@ function spinner(text, options) {
|
|
|
20499
20553
|
}
|
|
20500
20554
|
|
|
20501
20555
|
// src/utils/write-files.ts
|
|
20502
|
-
import
|
|
20503
|
-
import
|
|
20556
|
+
import fs4 from "node:fs/promises";
|
|
20557
|
+
import path6 from "node:path";
|
|
20504
20558
|
|
|
20505
20559
|
// src/utils/is-safe-target.ts
|
|
20506
|
-
import
|
|
20560
|
+
import path5 from "node:path";
|
|
20507
20561
|
function isSafeTarget(targetPath, root) {
|
|
20508
20562
|
if (targetPath.includes("\x00")) {
|
|
20509
20563
|
return false;
|
|
@@ -20518,8 +20572,8 @@ function isSafeTarget(targetPath, root) {
|
|
|
20518
20572
|
} catch {
|
|
20519
20573
|
return false;
|
|
20520
20574
|
}
|
|
20521
|
-
const normalizedTarget =
|
|
20522
|
-
const normalizedRoot =
|
|
20575
|
+
const normalizedTarget = path5.normalize(decodedPath.replace(/\\/g, "/"));
|
|
20576
|
+
const normalizedRoot = path5.normalize(root);
|
|
20523
20577
|
const targetSegments = decodedPath.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
20524
20578
|
const normalizedSegments = normalizedTarget.split(/[\\/]+/).filter(Boolean);
|
|
20525
20579
|
if (targetSegments.includes("..") || normalizedSegments.includes("..")) {
|
|
@@ -20528,8 +20582,8 @@ function isSafeTarget(targetPath, root) {
|
|
|
20528
20582
|
if (/^[a-zA-Z]:[\\/]/.test(decodedPath)) {
|
|
20529
20583
|
return false;
|
|
20530
20584
|
}
|
|
20531
|
-
const resolvedPath =
|
|
20532
|
-
return resolvedPath === normalizedRoot || resolvedPath.startsWith(`${normalizedRoot}${
|
|
20585
|
+
const resolvedPath = path5.isAbsolute(normalizedTarget) ? normalizedTarget : path5.resolve(normalizedRoot, normalizedTarget);
|
|
20586
|
+
return resolvedPath === normalizedRoot || resolvedPath.startsWith(`${normalizedRoot}${path5.sep}`);
|
|
20533
20587
|
}
|
|
20534
20588
|
|
|
20535
20589
|
// src/utils/write-files.ts
|
|
@@ -20537,14 +20591,14 @@ function rewriteToolkitImports(content, toolsAlias) {
|
|
|
20537
20591
|
return content.replace(/(["'])@toolkit\/(lib|components|hooks)\/([^"'`]+)\1/g, (_match, quote, kind, rest) => `${quote}${toolsAlias}/_shared/${kind}/${rest}${quote}`);
|
|
20538
20592
|
}
|
|
20539
20593
|
async function assertNoSymlinkTraversal(dest, toolsDir) {
|
|
20540
|
-
const relativeParent =
|
|
20541
|
-
const segments = relativeParent.split(
|
|
20594
|
+
const relativeParent = path6.relative(toolsDir, path6.dirname(dest));
|
|
20595
|
+
const segments = relativeParent.split(path6.sep).filter((segment) => segment.length > 0);
|
|
20542
20596
|
let currentPath = toolsDir;
|
|
20543
20597
|
for (const segment of segments) {
|
|
20544
|
-
currentPath =
|
|
20545
|
-
const stat = await
|
|
20598
|
+
currentPath = path6.join(currentPath, segment);
|
|
20599
|
+
const stat = await fs4.lstat(currentPath).catch(() => null);
|
|
20546
20600
|
if (stat?.isSymbolicLink()) {
|
|
20547
|
-
throw new Error(`Refusing to write through symlinked path "${
|
|
20601
|
+
throw new Error(`Refusing to write through symlinked path "${path6.relative(toolsDir, currentPath)}"`);
|
|
20548
20602
|
}
|
|
20549
20603
|
}
|
|
20550
20604
|
}
|
|
@@ -20559,45 +20613,166 @@ async function writeToolFiles(files, {
|
|
|
20559
20613
|
if (!isSafeTarget(file2.target, toolsDir)) {
|
|
20560
20614
|
throw new Error(`Refusing to write "${file2.target}" outside the tools directory`);
|
|
20561
20615
|
}
|
|
20562
|
-
const dest =
|
|
20616
|
+
const dest = path6.resolve(toolsDir, file2.target);
|
|
20563
20617
|
await assertNoSymlinkTraversal(dest, toolsDir);
|
|
20564
|
-
const exists = await
|
|
20618
|
+
const exists = await fs4.access(dest).then(() => true).catch(() => false);
|
|
20565
20619
|
if (exists && !overwrite) {
|
|
20566
20620
|
existing.push(dest);
|
|
20567
20621
|
continue;
|
|
20568
20622
|
}
|
|
20569
|
-
await
|
|
20570
|
-
const realToolsDir = await
|
|
20571
|
-
const realParentDir = await
|
|
20572
|
-
if (realParentDir !== realToolsDir && !realParentDir.startsWith(`${realToolsDir}${
|
|
20623
|
+
await fs4.mkdir(path6.dirname(dest), { recursive: true });
|
|
20624
|
+
const realToolsDir = await fs4.realpath(toolsDir).catch(() => path6.resolve(toolsDir));
|
|
20625
|
+
const realParentDir = await fs4.realpath(path6.dirname(dest));
|
|
20626
|
+
if (realParentDir !== realToolsDir && !realParentDir.startsWith(`${realToolsDir}${path6.sep}`)) {
|
|
20573
20627
|
throw new Error(`Refusing to write "${file2.target}" outside the tools directory`);
|
|
20574
20628
|
}
|
|
20575
20629
|
if (exists) {
|
|
20576
|
-
const stat = await
|
|
20630
|
+
const stat = await fs4.lstat(dest);
|
|
20577
20631
|
if (stat.isSymbolicLink()) {
|
|
20578
20632
|
throw new Error(`Refusing to overwrite symlinked file "${file2.target}"`);
|
|
20579
20633
|
}
|
|
20580
20634
|
}
|
|
20581
20635
|
const content = dest.endsWith(".ts") || dest.endsWith(".tsx") || dest.endsWith(".js") ? rewriteToolkitImports(file2.content, toolsAlias) : file2.content;
|
|
20582
|
-
await
|
|
20636
|
+
await fs4.writeFile(dest, content, "utf8");
|
|
20583
20637
|
written.push(dest);
|
|
20584
20638
|
}
|
|
20585
20639
|
return { written, existing };
|
|
20586
20640
|
}
|
|
20587
20641
|
|
|
20588
|
-
// src/
|
|
20642
|
+
// src/utils/install-registry-tools.ts
|
|
20643
|
+
function isRequirementSatisfied(requirement, env2) {
|
|
20644
|
+
return requirement.options.some((option) => option.every((name) => Boolean(env2[name])));
|
|
20645
|
+
}
|
|
20589
20646
|
function formatRequirementDescription(requirement) {
|
|
20590
20647
|
return requirement.description ?? requirement.options.map((option) => option.join(" + ")).join(" or ");
|
|
20591
20648
|
}
|
|
20649
|
+
async function installRegistryTools({
|
|
20650
|
+
tools,
|
|
20651
|
+
cwd,
|
|
20652
|
+
toolsDir,
|
|
20653
|
+
toolsAlias,
|
|
20654
|
+
overwrite = false,
|
|
20655
|
+
registryUrl,
|
|
20656
|
+
installDependenciesNow = true,
|
|
20657
|
+
packageManager,
|
|
20658
|
+
confirmOverwrite
|
|
20659
|
+
}) {
|
|
20660
|
+
if (tools.length === 0) {
|
|
20661
|
+
return;
|
|
20662
|
+
}
|
|
20663
|
+
const toolsIndexPath = path7.join(toolsDir, "tools.ts");
|
|
20664
|
+
const uiIndexPath = path7.join(toolsDir, "ui.ts");
|
|
20665
|
+
const fetchSpinner = spinner(tools.length === 1 ? `Fetching ${tools[0]}...` : "Fetching tools...");
|
|
20666
|
+
fetchSpinner.start();
|
|
20667
|
+
let resolution;
|
|
20668
|
+
try {
|
|
20669
|
+
resolution = await resolveRegistryItems(tools, registryUrl);
|
|
20670
|
+
fetchSpinner.succeed(tools.length === 1 ? `Fetched ${tools[0]}` : `Fetched ${tools.length} tools`);
|
|
20671
|
+
} catch (err) {
|
|
20672
|
+
fetchSpinner.fail("Failed to fetch tools");
|
|
20673
|
+
throw err;
|
|
20674
|
+
}
|
|
20675
|
+
const filesToWrite = resolution.files;
|
|
20676
|
+
const dependencies = resolution.dependencies;
|
|
20677
|
+
const devDependencies = resolution.devDependencies;
|
|
20678
|
+
const missingEnvRequirements = resolution.items.flatMap((item) => (item.envRequirements ?? []).filter((requirement) => !isRequirementSatisfied(requirement, process.env)).map((requirement) => ({
|
|
20679
|
+
tool: item.name,
|
|
20680
|
+
requirement: formatRequirementDescription(requirement)
|
|
20681
|
+
})));
|
|
20682
|
+
let effectiveOverwrite = overwrite;
|
|
20683
|
+
const writeSpinner = spinner("Writing files...");
|
|
20684
|
+
writeSpinner.start();
|
|
20685
|
+
const initialWrite = await writeToolFiles(filesToWrite, {
|
|
20686
|
+
overwrite: effectiveOverwrite,
|
|
20687
|
+
toolsDir,
|
|
20688
|
+
toolsAlias
|
|
20689
|
+
});
|
|
20690
|
+
if (initialWrite.existing.length > 0 && !effectiveOverwrite) {
|
|
20691
|
+
writeSpinner.stop();
|
|
20692
|
+
const shouldOverwrite = confirmOverwrite ? await confirmOverwrite(initialWrite.existing) : false;
|
|
20693
|
+
if (!shouldOverwrite) {
|
|
20694
|
+
throw new Error(`Tool install would overwrite existing files. Re-run with overwrite enabled.`);
|
|
20695
|
+
}
|
|
20696
|
+
effectiveOverwrite = true;
|
|
20697
|
+
}
|
|
20698
|
+
let writtenFiles = initialWrite.written;
|
|
20699
|
+
if (effectiveOverwrite && initialWrite.existing.length > 0) {
|
|
20700
|
+
writeSpinner.start();
|
|
20701
|
+
const secondWrite = await writeToolFiles(filesToWrite, {
|
|
20702
|
+
overwrite: true,
|
|
20703
|
+
toolsDir,
|
|
20704
|
+
toolsAlias
|
|
20705
|
+
});
|
|
20706
|
+
writtenFiles = secondWrite.written;
|
|
20707
|
+
}
|
|
20708
|
+
writeSpinner.succeed(writtenFiles.length > 0 ? `Wrote ${writtenFiles.map((file2) => path7.relative(cwd, file2)).join(", ")}` : `No file changes needed for ${tools.join(", ")}`);
|
|
20709
|
+
if (dependencies.length > 0 || devDependencies.length > 0) {
|
|
20710
|
+
const depsSpinner = spinner(installDependenciesNow ? "Installing dependencies..." : "Updating package.json dependencies...");
|
|
20711
|
+
depsSpinner.start();
|
|
20712
|
+
try {
|
|
20713
|
+
await installDependencies(dependencies, devDependencies, cwd, installDependenciesNow, packageManager);
|
|
20714
|
+
const installed = [
|
|
20715
|
+
...dependencies,
|
|
20716
|
+
...devDependencies.map((dep) => `${dep} (dev)`)
|
|
20717
|
+
];
|
|
20718
|
+
depsSpinner.succeed(`${installDependenciesNow ? "Installed" : "Recorded"}: ${installed.join(", ")}`);
|
|
20719
|
+
} catch (err) {
|
|
20720
|
+
depsSpinner.fail(installDependenciesNow ? "Failed to install dependencies" : "Failed to update package.json dependencies");
|
|
20721
|
+
throw err;
|
|
20722
|
+
}
|
|
20723
|
+
}
|
|
20724
|
+
if (missingEnvRequirements.length > 0) {
|
|
20725
|
+
const details = missingEnvRequirements.map(({ tool, requirement }) => `${tool}: ${requirement}`).join(", ");
|
|
20726
|
+
O2.warn(`Missing env vars for installed tools: ${details}`);
|
|
20727
|
+
}
|
|
20728
|
+
const injectSpinner = spinner("Updating tool registry index...");
|
|
20729
|
+
injectSpinner.start();
|
|
20730
|
+
try {
|
|
20731
|
+
let toolsSource;
|
|
20732
|
+
let uiSource;
|
|
20733
|
+
try {
|
|
20734
|
+
toolsSource = await fs5.readFile(toolsIndexPath, "utf8");
|
|
20735
|
+
} catch {
|
|
20736
|
+
toolsSource = createEmptyToolsTemplate();
|
|
20737
|
+
}
|
|
20738
|
+
try {
|
|
20739
|
+
uiSource = await fs5.readFile(uiIndexPath, "utf8");
|
|
20740
|
+
} catch {
|
|
20741
|
+
uiSource = createEmptyUiTemplate();
|
|
20742
|
+
}
|
|
20743
|
+
let updated = { toolsSource, uiSource };
|
|
20744
|
+
for (const name of tools) {
|
|
20745
|
+
const mainItem = resolution.items.find((item) => item.name === name);
|
|
20746
|
+
const shouldInject = mainItem?.files.some((file2) => file2.type === "tool" || file2.type === "renderer") ?? false;
|
|
20747
|
+
if (!shouldInject)
|
|
20748
|
+
continue;
|
|
20749
|
+
updated = injectTool({
|
|
20750
|
+
toolsSource: updated.toolsSource,
|
|
20751
|
+
uiSource: updated.uiSource,
|
|
20752
|
+
name,
|
|
20753
|
+
toolsAlias
|
|
20754
|
+
});
|
|
20755
|
+
}
|
|
20756
|
+
await fs5.mkdir(path7.dirname(toolsIndexPath), { recursive: true });
|
|
20757
|
+
await fs5.writeFile(toolsIndexPath, updated.toolsSource, "utf8");
|
|
20758
|
+
await fs5.writeFile(uiIndexPath, updated.uiSource, "utf8");
|
|
20759
|
+
injectSpinner.succeed("Updated tool registry index");
|
|
20760
|
+
} catch (err) {
|
|
20761
|
+
injectSpinner.fail("Failed to update tool registry index");
|
|
20762
|
+
throw err;
|
|
20763
|
+
}
|
|
20764
|
+
}
|
|
20765
|
+
|
|
20766
|
+
// src/commands/add.ts
|
|
20592
20767
|
var add = new Command().name("add").description("add a tool to an existing ChatJS project").argument("[tools...]", "tool names to add (e.g. word-count)").option("-y, --yes", "skip confirmation prompt", false).option("-o, --overwrite", "overwrite existing files without prompting", false).option("-c, --cwd <cwd>", "the working directory (defaults to current directory)", process.cwd()).option("-r, --registry <url>", "registry URL or local path template (e.g. ./packages/registry/items/{name}.json)").action(async (tools, opts) => {
|
|
20593
20768
|
try {
|
|
20594
|
-
const cwd =
|
|
20769
|
+
const cwd = path8.resolve(opts.cwd);
|
|
20595
20770
|
if (tools.length === 0) {
|
|
20596
20771
|
O2.error("Please specify one or more tool names, e.g. chatjs add word-count");
|
|
20597
20772
|
process.exit(1);
|
|
20598
20773
|
}
|
|
20599
20774
|
try {
|
|
20600
|
-
await
|
|
20775
|
+
await fs6.stat(path8.join(cwd, "chat.config.ts"));
|
|
20601
20776
|
} catch {
|
|
20602
20777
|
O2.error("No chat.config.ts found. Run `chat-js create` first or specify --cwd.");
|
|
20603
20778
|
process.exit(1);
|
|
@@ -20614,129 +20789,29 @@ var add = new Command().name("add").description("add a tool to an existing ChatJ
|
|
|
20614
20789
|
throw err;
|
|
20615
20790
|
}
|
|
20616
20791
|
const toolsDir = resolveToolsPath(config2.paths.tools, cwd);
|
|
20617
|
-
const toolsIndexPath = path6.join(toolsDir, "tools.ts");
|
|
20618
|
-
const uiIndexPath = path6.join(toolsDir, "ui.ts");
|
|
20619
20792
|
if (!opts.yes) {
|
|
20620
20793
|
const answer = await ot2({
|
|
20621
|
-
message: `Install ${tools.join(", ")} into ${
|
|
20794
|
+
message: `Install ${tools.join(", ")} into ${path8.relative(process.cwd(), toolsDir)}/?`
|
|
20622
20795
|
});
|
|
20623
20796
|
if (q(answer) || !answer) {
|
|
20624
20797
|
gt("Cancelled.");
|
|
20625
20798
|
process.exit(0);
|
|
20626
20799
|
}
|
|
20627
20800
|
}
|
|
20628
|
-
|
|
20629
|
-
|
|
20630
|
-
|
|
20631
|
-
|
|
20632
|
-
|
|
20633
|
-
|
|
20634
|
-
|
|
20635
|
-
|
|
20636
|
-
|
|
20637
|
-
|
|
20638
|
-
fetchSpinner.fail(`Failed to fetch ${name}`);
|
|
20639
|
-
throw err;
|
|
20640
|
-
}
|
|
20641
|
-
const itemsToInstall = resolution.items.filter((item) => {
|
|
20642
|
-
if (processedItems.has(item.name)) {
|
|
20643
|
-
return false;
|
|
20644
|
-
}
|
|
20645
|
-
processedItems.add(item.name);
|
|
20646
|
-
return true;
|
|
20647
|
-
});
|
|
20648
|
-
const filesToWrite = itemsToInstall.flatMap((item) => item.files);
|
|
20649
|
-
const dependencies = Array.from(new Set(itemsToInstall.flatMap((item) => item.dependencies ?? [])));
|
|
20650
|
-
const devDependencies = Array.from(new Set(itemsToInstall.flatMap((item) => item.devDependencies ?? [])));
|
|
20651
|
-
const requiredEnvRequirements = itemsToInstall.flatMap((item) => (item.envRequirements ?? []).map((requirement) => ({
|
|
20652
|
-
tool: item.name,
|
|
20653
|
-
requirement: formatRequirementDescription(requirement)
|
|
20654
|
-
})));
|
|
20655
|
-
const mainItem = resolution.items.find((item) => item.name === name);
|
|
20656
|
-
const overwrite = opts.overwrite;
|
|
20657
|
-
try {
|
|
20658
|
-
const writeSpinner = spinner("Writing files...");
|
|
20659
|
-
writeSpinner.start();
|
|
20660
|
-
const { written, existing } = await writeToolFiles(filesToWrite, {
|
|
20661
|
-
overwrite,
|
|
20662
|
-
toolsDir,
|
|
20663
|
-
toolsAlias: config2.paths.tools
|
|
20801
|
+
await installRegistryTools({
|
|
20802
|
+
tools,
|
|
20803
|
+
cwd,
|
|
20804
|
+
toolsDir,
|
|
20805
|
+
toolsAlias: config2.paths.tools,
|
|
20806
|
+
overwrite: opts.overwrite,
|
|
20807
|
+
registryUrl: opts.registry,
|
|
20808
|
+
confirmOverwrite: async (existingFiles) => {
|
|
20809
|
+
const answer = await ot2({
|
|
20810
|
+
message: `${existingFiles.map((file2) => path8.relative(cwd, file2)).join(", ")} already exist. Overwrite?`
|
|
20664
20811
|
});
|
|
20665
|
-
|
|
20666
|
-
if (existing.length > 0 && !overwrite) {
|
|
20667
|
-
const answer = await ot2({
|
|
20668
|
-
message: `${existing.map((f) => path6.relative(cwd, f)).join(", ")} already exist. Overwrite?`
|
|
20669
|
-
});
|
|
20670
|
-
if (q(answer) || !answer) {
|
|
20671
|
-
O2.warn(`Kept existing files for ${name}; installed remaining new files`);
|
|
20672
|
-
} else {
|
|
20673
|
-
const { written: rest } = await writeToolFiles(filesToWrite, {
|
|
20674
|
-
overwrite: true,
|
|
20675
|
-
toolsDir,
|
|
20676
|
-
toolsAlias: config2.paths.tools
|
|
20677
|
-
});
|
|
20678
|
-
written.push(...rest);
|
|
20679
|
-
}
|
|
20680
|
-
}
|
|
20681
|
-
if (written.length > 0) {
|
|
20682
|
-
O2.step(written.map((f) => path6.relative(cwd, f)).join(", "));
|
|
20683
|
-
} else {
|
|
20684
|
-
O2.step(`No file changes needed for ${name}`);
|
|
20685
|
-
}
|
|
20686
|
-
} catch (err) {
|
|
20687
|
-
O2.error("Failed to write files");
|
|
20688
|
-
throw err;
|
|
20812
|
+
return !(q(answer) || !answer);
|
|
20689
20813
|
}
|
|
20690
|
-
|
|
20691
|
-
const depsSpinner = spinner("Installing dependencies...");
|
|
20692
|
-
depsSpinner.start();
|
|
20693
|
-
try {
|
|
20694
|
-
await installDependencies(dependencies, devDependencies, cwd);
|
|
20695
|
-
const installed = [
|
|
20696
|
-
...dependencies,
|
|
20697
|
-
...devDependencies.map((dep) => `${dep} (dev)`)
|
|
20698
|
-
];
|
|
20699
|
-
depsSpinner.succeed(`Installed: ${installed.join(", ")}`);
|
|
20700
|
-
} catch (err) {
|
|
20701
|
-
depsSpinner.fail("Failed to install dependencies");
|
|
20702
|
-
throw err;
|
|
20703
|
-
}
|
|
20704
|
-
}
|
|
20705
|
-
if (requiredEnvRequirements.length > 0) {
|
|
20706
|
-
const details = requiredEnvRequirements.map(({ tool, requirement }) => `${tool}: ${requirement}`).join(", ");
|
|
20707
|
-
O2.warn(`Required env vars for installed tools: ${details}`);
|
|
20708
|
-
}
|
|
20709
|
-
const injectSpinner = spinner("Updating tool registry index...");
|
|
20710
|
-
injectSpinner.start();
|
|
20711
|
-
try {
|
|
20712
|
-
let toolsSource;
|
|
20713
|
-
let uiSource;
|
|
20714
|
-
try {
|
|
20715
|
-
toolsSource = await fs4.readFile(toolsIndexPath, "utf8");
|
|
20716
|
-
} catch {
|
|
20717
|
-
toolsSource = createEmptyToolsTemplate();
|
|
20718
|
-
}
|
|
20719
|
-
try {
|
|
20720
|
-
uiSource = await fs4.readFile(uiIndexPath, "utf8");
|
|
20721
|
-
} catch {
|
|
20722
|
-
uiSource = createEmptyUiTemplate();
|
|
20723
|
-
}
|
|
20724
|
-
const shouldInject = mainItem?.files.some((file2) => file2.type === "tool" || file2.type === "renderer") ?? false;
|
|
20725
|
-
const updated = shouldInject ? injectTool({
|
|
20726
|
-
toolsSource,
|
|
20727
|
-
uiSource,
|
|
20728
|
-
name,
|
|
20729
|
-
toolsAlias: config2.paths.tools
|
|
20730
|
-
}) : { toolsSource, uiSource };
|
|
20731
|
-
await fs4.mkdir(path6.dirname(toolsIndexPath), { recursive: true });
|
|
20732
|
-
await fs4.writeFile(toolsIndexPath, updated.toolsSource, "utf8");
|
|
20733
|
-
await fs4.writeFile(uiIndexPath, updated.uiSource, "utf8");
|
|
20734
|
-
injectSpinner.succeed("Updated tool registry index");
|
|
20735
|
-
} catch (err) {
|
|
20736
|
-
injectSpinner.fail("Failed to update tool registry index");
|
|
20737
|
-
throw err;
|
|
20738
|
-
}
|
|
20739
|
-
}
|
|
20814
|
+
});
|
|
20740
20815
|
gt(`Done! ${tools.length === 1 ? `"${tools[0]}"` : `${tools.length} tools`} installed successfully.`);
|
|
20741
20816
|
} catch (error48) {
|
|
20742
20817
|
handleError(error48);
|
|
@@ -20745,7 +20820,7 @@ var add = new Command().name("add").description("add a tool to an existing ChatJ
|
|
|
20745
20820
|
|
|
20746
20821
|
// src/commands/config.ts
|
|
20747
20822
|
import { spawn as spawn3 } from "node:child_process";
|
|
20748
|
-
import
|
|
20823
|
+
import path9 from "node:path";
|
|
20749
20824
|
var EVAL_SCRIPT2 = `
|
|
20750
20825
|
import userConfig from "./chat.config.ts";
|
|
20751
20826
|
import { applyDefaults } from "./lib/config-schema";
|
|
@@ -20765,7 +20840,7 @@ function getTsEvalCommand2(pm) {
|
|
|
20765
20840
|
}
|
|
20766
20841
|
var config2 = new Command().name("config").description("print the resolved configuration for the current ChatJS project").option("-c, --cwd <cwd>", "the working directory (defaults to current directory)", process.cwd()).action(async (opts) => {
|
|
20767
20842
|
try {
|
|
20768
|
-
const cwd =
|
|
20843
|
+
const cwd = path9.resolve(opts.cwd);
|
|
20769
20844
|
const pm = inferPackageManager();
|
|
20770
20845
|
const [cmd, args] = getTsEvalCommand2(pm);
|
|
20771
20846
|
await new Promise((resolve, reject) => {
|
|
@@ -20812,6 +20887,16 @@ var DESCRIPTIONS = new Map([
|
|
|
20812
20887
|
"authentication.vercel",
|
|
20813
20888
|
"Vercel OAuth (requires VERCEL_APP_CLIENT_ID + VERCEL_APP_CLIENT_SECRET)"
|
|
20814
20889
|
],
|
|
20890
|
+
["ai.tools.mcp.enabled", "Requires MCP_ENCRYPTION_KEY"],
|
|
20891
|
+
["ai.tools.documents.enabled", "Document create/edit/review support"],
|
|
20892
|
+
["ai.tools.webSearch.enabled", "Requires TAVILY_API_KEY or FIRECRAWL_API_KEY"],
|
|
20893
|
+
["ai.tools.urlRetrieval.enabled", "Requires FIRECRAWL_API_KEY"],
|
|
20894
|
+
[
|
|
20895
|
+
"ai.tools.codeExecution.enabled",
|
|
20896
|
+
"Requires Vercel sandbox credentials outside Vercel"
|
|
20897
|
+
],
|
|
20898
|
+
["ai.tools.image.enabled", "Requires BLOB_READ_WRITE_TOKEN"],
|
|
20899
|
+
["ai.tools.deepResearch.enabled", "Requires web search access"],
|
|
20815
20900
|
[
|
|
20816
20901
|
"paths.tools",
|
|
20817
20902
|
"Import alias for the installable tools registry index and tool files"
|
|
@@ -20853,14 +20938,14 @@ ${spaces}}`;
|
|
|
20853
20938
|
function generateConfig(obj, indent, pathPrefix) {
|
|
20854
20939
|
const spaces = " ".repeat(indent);
|
|
20855
20940
|
return Object.entries(obj).map(([key, value]) => {
|
|
20856
|
-
const
|
|
20857
|
-
const desc = DESCRIPTIONS.get(
|
|
20941
|
+
const path10 = pathPrefix ? `${pathPrefix}.${key}` : key;
|
|
20942
|
+
const desc = DESCRIPTIONS.get(path10);
|
|
20858
20943
|
const comment = desc ? ` // ${desc}` : "";
|
|
20859
20944
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
20860
|
-
const nested = generateConfig(value, indent + 1,
|
|
20945
|
+
const nested = generateConfig(value, indent + 1, path10);
|
|
20861
20946
|
return `${spaces}${formatKey(key)}: {
|
|
20862
20947
|
${nested}
|
|
20863
|
-
${spaces}}
|
|
20948
|
+
${spaces}},${comment}`;
|
|
20864
20949
|
}
|
|
20865
20950
|
return `${spaces}${formatKey(key)}: ${formatValue(value, indent)},${comment}`;
|
|
20866
20951
|
}).join(`
|
|
@@ -20884,7 +20969,10 @@ function buildConfigTs(input) {
|
|
|
20884
20969
|
aiProviders: ["OpenAI", "Anthropic", "Google"],
|
|
20885
20970
|
paymentProcessors: []
|
|
20886
20971
|
},
|
|
20887
|
-
features:
|
|
20972
|
+
features: {
|
|
20973
|
+
attachments: input.coreFeatures.attachments,
|
|
20974
|
+
parallelResponses: input.coreFeatures.parallelResponses
|
|
20975
|
+
},
|
|
20888
20976
|
legal: {
|
|
20889
20977
|
minimumAge: 13,
|
|
20890
20978
|
governingLaw: "United States",
|
|
@@ -20898,7 +20986,39 @@ function buildConfigTs(input) {
|
|
|
20898
20986
|
desktopApp: {
|
|
20899
20987
|
enabled: input.withElectron
|
|
20900
20988
|
},
|
|
20901
|
-
ai: {
|
|
20989
|
+
ai: {
|
|
20990
|
+
gateway: input.gateway,
|
|
20991
|
+
tools: {
|
|
20992
|
+
mcp: {
|
|
20993
|
+
enabled: input.coreFeatures.mcp
|
|
20994
|
+
},
|
|
20995
|
+
followupSuggestions: {
|
|
20996
|
+
enabled: input.coreFeatures.followupSuggestions
|
|
20997
|
+
},
|
|
20998
|
+
documents: {
|
|
20999
|
+
enabled: input.coreFeatures.documents,
|
|
21000
|
+
types: input.documentTypes
|
|
21001
|
+
},
|
|
21002
|
+
webSearch: {
|
|
21003
|
+
enabled: input.builtInTools.webSearch
|
|
21004
|
+
},
|
|
21005
|
+
urlRetrieval: {
|
|
21006
|
+
enabled: input.builtInTools.urlRetrieval
|
|
21007
|
+
},
|
|
21008
|
+
deepResearch: {
|
|
21009
|
+
enabled: input.builtInTools.deepResearch
|
|
21010
|
+
},
|
|
21011
|
+
codeExecution: {
|
|
21012
|
+
enabled: input.builtInTools.codeExecution
|
|
21013
|
+
},
|
|
21014
|
+
image: {
|
|
21015
|
+
enabled: input.builtInTools.imageGeneration
|
|
21016
|
+
},
|
|
21017
|
+
video: {
|
|
21018
|
+
enabled: input.builtInTools.videoGeneration
|
|
21019
|
+
}
|
|
21020
|
+
}
|
|
21021
|
+
},
|
|
20902
21022
|
paths: {
|
|
20903
21023
|
tools: "@/tools/chatjs"
|
|
20904
21024
|
},
|
|
@@ -20973,7 +21093,17 @@ var gatewayEnvRequirements = {
|
|
|
20973
21093
|
description: "OPENAI_COMPATIBLE_BASE_URL + OPENAI_COMPATIBLE_API_KEY"
|
|
20974
21094
|
}
|
|
20975
21095
|
};
|
|
20976
|
-
var
|
|
21096
|
+
var coreFeatureEnvRequirements = {
|
|
21097
|
+
mcp: {
|
|
21098
|
+
options: [["MCP_ENCRYPTION_KEY"]],
|
|
21099
|
+
description: "MCP_ENCRYPTION_KEY"
|
|
21100
|
+
},
|
|
21101
|
+
attachments: {
|
|
21102
|
+
options: [["BLOB_READ_WRITE_TOKEN"]],
|
|
21103
|
+
description: "BLOB_READ_WRITE_TOKEN"
|
|
21104
|
+
}
|
|
21105
|
+
};
|
|
21106
|
+
var builtInToolEnvRequirements = {
|
|
20977
21107
|
webSearch: {
|
|
20978
21108
|
options: [["TAVILY_API_KEY"], ["FIRECRAWL_API_KEY"]],
|
|
20979
21109
|
description: "TAVILY_API_KEY or FIRECRAWL_API_KEY"
|
|
@@ -20986,24 +21116,20 @@ var featureEnvRequirements = {
|
|
|
20986
21116
|
options: [["TAVILY_API_KEY"], ["FIRECRAWL_API_KEY"]],
|
|
20987
21117
|
description: "TAVILY_API_KEY or FIRECRAWL_API_KEY"
|
|
20988
21118
|
},
|
|
20989
|
-
|
|
20990
|
-
options: [
|
|
20991
|
-
|
|
21119
|
+
codeExecution: {
|
|
21120
|
+
options: [
|
|
21121
|
+
["VERCEL_OIDC_TOKEN"],
|
|
21122
|
+
["VERCEL_TEAM_ID", "VERCEL_PROJECT_ID", "VERCEL_TOKEN"]
|
|
21123
|
+
],
|
|
21124
|
+
description: "VERCEL_OIDC_TOKEN or VERCEL_TEAM_ID + VERCEL_PROJECT_ID + VERCEL_TOKEN"
|
|
20992
21125
|
},
|
|
20993
21126
|
imageGeneration: {
|
|
20994
21127
|
options: [["BLOB_READ_WRITE_TOKEN"]],
|
|
20995
21128
|
description: "BLOB_READ_WRITE_TOKEN"
|
|
20996
21129
|
},
|
|
20997
|
-
|
|
21130
|
+
videoGeneration: {
|
|
20998
21131
|
options: [["BLOB_READ_WRITE_TOKEN"]],
|
|
20999
21132
|
description: "BLOB_READ_WRITE_TOKEN"
|
|
21000
|
-
},
|
|
21001
|
-
sandbox: {
|
|
21002
|
-
options: [
|
|
21003
|
-
["VERCEL_OIDC_TOKEN"],
|
|
21004
|
-
["VERCEL_TEAM_ID", "VERCEL_PROJECT_ID", "VERCEL_TOKEN"]
|
|
21005
|
-
],
|
|
21006
|
-
description: "VERCEL_OIDC_TOKEN or VERCEL_TEAM_ID + VERCEL_PROJECT_ID + VERCEL_TOKEN"
|
|
21007
21133
|
}
|
|
21008
21134
|
};
|
|
21009
21135
|
var authEnvRequirements = {
|
|
@@ -21052,16 +21178,21 @@ var GATEWAYS = [
|
|
|
21052
21178
|
"openai-compatible"
|
|
21053
21179
|
];
|
|
21054
21180
|
var AUTH_PROVIDERS = ["google", "github", "vercel"];
|
|
21055
|
-
var
|
|
21056
|
-
"
|
|
21181
|
+
var CORE_FEATURE_KEYS = [
|
|
21182
|
+
"attachments",
|
|
21183
|
+
"parallelResponses",
|
|
21184
|
+
"documents",
|
|
21185
|
+
"mcp",
|
|
21186
|
+
"followupSuggestions"
|
|
21187
|
+
];
|
|
21188
|
+
var DOCUMENT_TYPE_KEYS = ["text", "code", "sheet"];
|
|
21189
|
+
var BUILT_IN_TOOL_KEYS = [
|
|
21057
21190
|
"webSearch",
|
|
21058
21191
|
"urlRetrieval",
|
|
21059
21192
|
"deepResearch",
|
|
21060
|
-
"
|
|
21193
|
+
"codeExecution",
|
|
21061
21194
|
"imageGeneration",
|
|
21062
|
-
"
|
|
21063
|
-
"followupSuggestions",
|
|
21064
|
-
"parallelResponses"
|
|
21195
|
+
"videoGeneration"
|
|
21065
21196
|
];
|
|
21066
21197
|
|
|
21067
21198
|
// src/helpers/env-checklist.ts
|
|
@@ -21095,10 +21226,21 @@ function collectEnvChecklist(input) {
|
|
|
21095
21226
|
entries.push(...gwEntries);
|
|
21096
21227
|
const featureItems = [];
|
|
21097
21228
|
const seen = new Set;
|
|
21098
|
-
for (const feature of
|
|
21099
|
-
if (!input.
|
|
21229
|
+
for (const feature of CORE_FEATURE_KEYS) {
|
|
21230
|
+
if (!input.coreFeatures[feature])
|
|
21231
|
+
continue;
|
|
21232
|
+
const requirement = coreFeatureEnvRequirements[feature];
|
|
21233
|
+
if (!requirement)
|
|
21234
|
+
continue;
|
|
21235
|
+
if (seen.has(requirement.description))
|
|
21100
21236
|
continue;
|
|
21101
|
-
|
|
21237
|
+
seen.add(requirement.description);
|
|
21238
|
+
featureItems.push(...requirementToEntries(requirement));
|
|
21239
|
+
}
|
|
21240
|
+
for (const tool of BUILT_IN_TOOL_KEYS) {
|
|
21241
|
+
if (!input.builtInTools[tool])
|
|
21242
|
+
continue;
|
|
21243
|
+
const requirement = builtInToolEnvRequirements[tool];
|
|
21102
21244
|
if (!requirement)
|
|
21103
21245
|
continue;
|
|
21104
21246
|
if (seen.has(requirement.description))
|
|
@@ -21118,32 +21260,63 @@ function collectEnvChecklist(input) {
|
|
|
21118
21260
|
}
|
|
21119
21261
|
|
|
21120
21262
|
// src/helpers/prompts.ts
|
|
21121
|
-
var
|
|
21122
|
-
|
|
21263
|
+
var CORE_FEATURE_DEFAULTS = {
|
|
21264
|
+
attachments: false,
|
|
21265
|
+
parallelResponses: true,
|
|
21266
|
+
documents: true,
|
|
21267
|
+
mcp: false,
|
|
21268
|
+
followupSuggestions: true
|
|
21269
|
+
};
|
|
21270
|
+
var DOCUMENT_TYPE_DEFAULTS = {
|
|
21271
|
+
text: true,
|
|
21272
|
+
code: true,
|
|
21273
|
+
sheet: true
|
|
21274
|
+
};
|
|
21275
|
+
var BUILT_IN_TOOL_DEFAULTS = {
|
|
21123
21276
|
webSearch: false,
|
|
21124
21277
|
urlRetrieval: false,
|
|
21125
21278
|
deepResearch: false,
|
|
21126
|
-
|
|
21279
|
+
codeExecution: false,
|
|
21127
21280
|
imageGeneration: false,
|
|
21128
|
-
|
|
21129
|
-
followupSuggestions: true,
|
|
21130
|
-
parallelResponses: true
|
|
21281
|
+
videoGeneration: false
|
|
21131
21282
|
};
|
|
21132
21283
|
var AUTH_DEFAULTS = {
|
|
21133
21284
|
google: false,
|
|
21134
21285
|
github: true,
|
|
21135
21286
|
vercel: false
|
|
21136
21287
|
};
|
|
21137
|
-
var
|
|
21138
|
-
|
|
21288
|
+
var CORE_FEATURE_LABELS = {
|
|
21289
|
+
attachments: "Attachments",
|
|
21290
|
+
parallelResponses: "Parallel Responses",
|
|
21291
|
+
documents: "Documents",
|
|
21292
|
+
mcp: "MCP Tool Servers",
|
|
21293
|
+
followupSuggestions: "Follow-up Suggestions"
|
|
21294
|
+
};
|
|
21295
|
+
var DOCUMENT_TYPE_LABELS = {
|
|
21296
|
+
text: "Text Documents",
|
|
21297
|
+
code: "Code Documents",
|
|
21298
|
+
sheet: "Spreadsheet Documents"
|
|
21299
|
+
};
|
|
21300
|
+
var DOCUMENT_TYPE_HINTS = {
|
|
21301
|
+
text: "Notes, guides, markdown, and long-form writing",
|
|
21302
|
+
code: "Code files and snippets",
|
|
21303
|
+
sheet: "CSV-based tables and structured data"
|
|
21304
|
+
};
|
|
21305
|
+
var BUILT_IN_TOOL_LABELS = {
|
|
21139
21306
|
webSearch: "Web Search",
|
|
21140
21307
|
urlRetrieval: "URL Retrieval",
|
|
21141
21308
|
deepResearch: "Deep Research",
|
|
21142
|
-
|
|
21309
|
+
codeExecution: "Code Sandbox",
|
|
21143
21310
|
imageGeneration: "Image Generation",
|
|
21144
|
-
|
|
21145
|
-
|
|
21146
|
-
|
|
21311
|
+
videoGeneration: "Video Generation"
|
|
21312
|
+
};
|
|
21313
|
+
var BUILT_IN_TOOL_HINTS = {
|
|
21314
|
+
webSearch: "Search the web from chat",
|
|
21315
|
+
urlRetrieval: "Fetch structured content from a specific URL",
|
|
21316
|
+
deepResearch: "Run multi-step web research and generate reports",
|
|
21317
|
+
codeExecution: "Execute code in a sandboxed environment",
|
|
21318
|
+
imageGeneration: "Generate images inside chat",
|
|
21319
|
+
videoGeneration: "Generate videos inside chat"
|
|
21147
21320
|
};
|
|
21148
21321
|
var AUTH_LABELS = {
|
|
21149
21322
|
google: "Google OAuth",
|
|
@@ -21159,9 +21332,13 @@ function handleCancel(value) {
|
|
|
21159
21332
|
function toKebabCase(value) {
|
|
21160
21333
|
return (value ?? "").trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
21161
21334
|
}
|
|
21335
|
+
function toSelectionRecord(keys, selected) {
|
|
21336
|
+
return Object.fromEntries(keys.map((key) => [key, selected.includes(key)]));
|
|
21337
|
+
}
|
|
21162
21338
|
async function promptProjectName(targetArg, skipPrompt) {
|
|
21163
|
-
if (skipPrompt)
|
|
21339
|
+
if (skipPrompt) {
|
|
21164
21340
|
return toKebabCase(targetArg ?? "my-chat-app") || "my-chat-app";
|
|
21341
|
+
}
|
|
21165
21342
|
const name = await Ot({
|
|
21166
21343
|
message: "What is your project named?",
|
|
21167
21344
|
initialValue: targetArg ?? "my-chat-app",
|
|
@@ -21189,32 +21366,76 @@ async function promptGateway(skipPrompt) {
|
|
|
21189
21366
|
handleCancel(gateway);
|
|
21190
21367
|
return gateway;
|
|
21191
21368
|
}
|
|
21192
|
-
async function
|
|
21369
|
+
async function promptCoreFeatures(skipPrompt) {
|
|
21193
21370
|
if (skipPrompt)
|
|
21194
|
-
return { ...
|
|
21195
|
-
const
|
|
21196
|
-
|
|
21197
|
-
|
|
21198
|
-
options: FEATURE_KEYS.map((key) => ({
|
|
21371
|
+
return { ...CORE_FEATURE_DEFAULTS };
|
|
21372
|
+
const selected = await yt({
|
|
21373
|
+
message: `Which ${highlighter.info("core features")} would you like to enable? ${highlighter.dim("(space to toggle, enter to submit)")}`,
|
|
21374
|
+
options: CORE_FEATURE_KEYS.map((key) => ({
|
|
21199
21375
|
value: key,
|
|
21200
|
-
label:
|
|
21201
|
-
hint:
|
|
21376
|
+
label: CORE_FEATURE_LABELS[key],
|
|
21377
|
+
hint: key === "documents" ? "Create, edit, and review documents in chat" : coreFeatureEnvRequirements[key]?.description
|
|
21202
21378
|
})),
|
|
21203
|
-
initialValues:
|
|
21379
|
+
initialValues: CORE_FEATURE_KEYS.filter((key) => CORE_FEATURE_DEFAULTS[key]),
|
|
21204
21380
|
required: false
|
|
21205
21381
|
});
|
|
21206
|
-
handleCancel(
|
|
21207
|
-
|
|
21208
|
-
|
|
21209
|
-
|
|
21382
|
+
handleCancel(selected);
|
|
21383
|
+
return toSelectionRecord(CORE_FEATURE_KEYS, selected);
|
|
21384
|
+
}
|
|
21385
|
+
async function promptDocumentTypes(skipPrompt, documentsEnabled) {
|
|
21386
|
+
if (!documentsEnabled) {
|
|
21387
|
+
return toSelectionRecord(DOCUMENT_TYPE_KEYS, []);
|
|
21210
21388
|
}
|
|
21211
|
-
|
|
21212
|
-
|
|
21389
|
+
if (skipPrompt)
|
|
21390
|
+
return { ...DOCUMENT_TYPE_DEFAULTS };
|
|
21391
|
+
const selected = await yt({
|
|
21392
|
+
message: `Which ${highlighter.info("document types")} would you like to enable? ${highlighter.dim("(space to toggle, enter to submit)")}`,
|
|
21393
|
+
options: DOCUMENT_TYPE_KEYS.map((key) => ({
|
|
21394
|
+
value: key,
|
|
21395
|
+
label: DOCUMENT_TYPE_LABELS[key],
|
|
21396
|
+
hint: DOCUMENT_TYPE_HINTS[key]
|
|
21397
|
+
})),
|
|
21398
|
+
initialValues: DOCUMENT_TYPE_KEYS.filter((key) => DOCUMENT_TYPE_DEFAULTS[key]),
|
|
21399
|
+
required: false
|
|
21400
|
+
});
|
|
21401
|
+
handleCancel(selected);
|
|
21402
|
+
return toSelectionRecord(DOCUMENT_TYPE_KEYS, selected);
|
|
21403
|
+
}
|
|
21404
|
+
async function promptAssistantTools(registryItems, skipPrompt) {
|
|
21405
|
+
const installableItems = registryItems.filter((item) => !item.hidden);
|
|
21406
|
+
if (skipPrompt) {
|
|
21407
|
+
return {
|
|
21408
|
+
builtInTools: { ...BUILT_IN_TOOL_DEFAULTS },
|
|
21409
|
+
installableTools: []
|
|
21410
|
+
};
|
|
21213
21411
|
}
|
|
21214
|
-
|
|
21215
|
-
|
|
21412
|
+
const selected = await yt({
|
|
21413
|
+
message: `Which ${highlighter.info("assistant tools")} would you like to enable? ${highlighter.dim("(space to toggle, enter to submit)")}`,
|
|
21414
|
+
options: [
|
|
21415
|
+
...BUILT_IN_TOOL_KEYS.map((key) => ({
|
|
21416
|
+
value: key,
|
|
21417
|
+
label: BUILT_IN_TOOL_LABELS[key],
|
|
21418
|
+
hint: builtInToolEnvRequirements[key]?.description ?? BUILT_IN_TOOL_HINTS[key]
|
|
21419
|
+
})),
|
|
21420
|
+
...installableItems.map((item) => ({
|
|
21421
|
+
value: item.name,
|
|
21422
|
+
label: item.name,
|
|
21423
|
+
hint: item.description
|
|
21424
|
+
}))
|
|
21425
|
+
],
|
|
21426
|
+
initialValues: BUILT_IN_TOOL_KEYS.filter((key) => BUILT_IN_TOOL_DEFAULTS[key]),
|
|
21427
|
+
required: false
|
|
21428
|
+
});
|
|
21429
|
+
handleCancel(selected);
|
|
21430
|
+
const selectedValues = selected;
|
|
21431
|
+
const builtInTools = toSelectionRecord(BUILT_IN_TOOL_KEYS, selectedValues.filter((value) => BUILT_IN_TOOL_KEYS.includes(value)));
|
|
21432
|
+
if (builtInTools.deepResearch) {
|
|
21433
|
+
builtInTools.webSearch = true;
|
|
21216
21434
|
}
|
|
21217
|
-
return
|
|
21435
|
+
return {
|
|
21436
|
+
builtInTools,
|
|
21437
|
+
installableTools: selectedValues.filter((value) => !BUILT_IN_TOOL_KEYS.includes(value))
|
|
21438
|
+
};
|
|
21218
21439
|
}
|
|
21219
21440
|
async function promptAuth(skipPrompt) {
|
|
21220
21441
|
if (skipPrompt)
|
|
@@ -21238,15 +21459,7 @@ async function promptAuth(skipPrompt) {
|
|
|
21238
21459
|
logger.warn("At least one auth provider is required. Please select one.");
|
|
21239
21460
|
}
|
|
21240
21461
|
}
|
|
21241
|
-
|
|
21242
|
-
google: false,
|
|
21243
|
-
github: false,
|
|
21244
|
-
vercel: false
|
|
21245
|
-
};
|
|
21246
|
-
for (const p2 of selectedProviders) {
|
|
21247
|
-
auth[p2] = true;
|
|
21248
|
-
}
|
|
21249
|
-
return auth;
|
|
21462
|
+
return toSelectionRecord(AUTH_PROVIDERS, selectedProviders);
|
|
21250
21463
|
}
|
|
21251
21464
|
async function promptElectron(skipPrompt, explicitChoice) {
|
|
21252
21465
|
if (typeof explicitChoice === "boolean") {
|
|
@@ -21527,7 +21740,7 @@ async function applyElectronTemplateSourceTransforms(destination) {
|
|
|
21527
21740
|
await replaceInFile(tsconfigPath, [['"../chat/*"', '"../*"']]);
|
|
21528
21741
|
const packageJsonPath = join(destination, "package.json");
|
|
21529
21742
|
await replaceInFile(packageJsonPath, [
|
|
21530
|
-
['"name": "@
|
|
21743
|
+
['"name": "@chat-js/electron"', '"name": "__PROJECT_NAME__-electron"'],
|
|
21531
21744
|
[
|
|
21532
21745
|
'"url": "https://github.com/FranciscoMoretti/chat-js.git"',
|
|
21533
21746
|
'"url": "https://github.com/__GITHUB_OWNER__/__GITHUB_REPO__.git"'
|
|
@@ -21720,9 +21933,10 @@ var createOptionsSchema = exports_external.object({
|
|
|
21720
21933
|
install: exports_external.boolean(),
|
|
21721
21934
|
electron: exports_external.boolean().optional(),
|
|
21722
21935
|
fromGit: exports_external.string().optional(),
|
|
21936
|
+
registry: exports_external.string().optional(),
|
|
21723
21937
|
packageManager: exports_external.enum(["bun", "npm", "pnpm", "yarn"]).optional()
|
|
21724
21938
|
});
|
|
21725
|
-
var create = new Command().name("create").description("scaffold a new ChatJS chat application").argument("[directory]", "target directory for the project").option("-y, --yes", "skip prompts and use defaults", false).option("--no-install", "skip dependency installation").option("--electron", "include the Electron desktop app").option("--no-electron", "do not include the Electron desktop app").option("--package-manager <manager>", "package manager for install + next steps (bun, npm, pnpm, yarn)").option("--from-git <url>", "clone from a git repository instead of the built-in scaffold").action(async (directory, opts) => {
|
|
21939
|
+
var create = new Command().name("create").description("scaffold a new ChatJS chat application").argument("[directory]", "target directory for the project").option("-y, --yes", "skip prompts and use defaults", false).option("--no-install", "skip dependency installation").option("--electron", "include the Electron desktop app").option("--no-electron", "do not include the Electron desktop app").option("-r, --registry <url>", "registry URL or local path template (e.g. ./packages/registry/items/{name}.json)").option("--package-manager <manager>", "package manager for install + next steps (bun, npm, pnpm, yarn)").option("--from-git <url>", "clone from a git repository instead of the built-in scaffold").action(async (directory, opts) => {
|
|
21726
21940
|
try {
|
|
21727
21941
|
const options = createOptionsSchema.parse({
|
|
21728
21942
|
target: directory,
|
|
@@ -21733,16 +21947,29 @@ var create = new Command().name("create").description("scaffold a new ChatJS cha
|
|
|
21733
21947
|
mt("Create ChatJS App");
|
|
21734
21948
|
}
|
|
21735
21949
|
const initialTarget = resolveCreateTarget(options.target);
|
|
21736
|
-
const
|
|
21737
|
-
const projectName = promptedProjectName;
|
|
21950
|
+
const projectName = await promptProjectName(initialTarget.projectName, options.yes);
|
|
21738
21951
|
const targetDir = options.target ? initialTarget.targetDir : resolve2(process.cwd(), projectName);
|
|
21739
21952
|
const displayPath = options.target ? initialTarget.displayPath : projectName;
|
|
21740
21953
|
await ensureTargetEmpty(targetDir);
|
|
21741
|
-
const appName = projectName.split("-").map((
|
|
21954
|
+
const appName = projectName.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
21742
21955
|
const appPrefix = projectName;
|
|
21743
21956
|
const appUrl = "http://localhost:3000";
|
|
21744
21957
|
const gateway = await promptGateway(options.yes);
|
|
21745
|
-
const
|
|
21958
|
+
const coreFeatures = await promptCoreFeatures(options.yes);
|
|
21959
|
+
const documentTypes = await promptDocumentTypes(options.yes, coreFeatures.documents);
|
|
21960
|
+
let registryItems = [];
|
|
21961
|
+
if (!options.yes) {
|
|
21962
|
+
const registrySpinner = spinner("Loading installable tools...");
|
|
21963
|
+
registrySpinner.start();
|
|
21964
|
+
try {
|
|
21965
|
+
registryItems = await fetchRegistryIndex(options.registry);
|
|
21966
|
+
registrySpinner.succeed("Installable tools loaded.");
|
|
21967
|
+
} catch (error48) {
|
|
21968
|
+
registrySpinner.fail("Could not load installable tools.");
|
|
21969
|
+
logger.warn(error48 instanceof Error ? error48.message : "Continuing with built-in tools only.");
|
|
21970
|
+
}
|
|
21971
|
+
}
|
|
21972
|
+
const assistantTools = await promptAssistantTools(registryItems, options.yes);
|
|
21746
21973
|
const auth = await promptAuth(options.yes);
|
|
21747
21974
|
const withElectron = await promptElectron(options.yes, options.electron);
|
|
21748
21975
|
logger.break();
|
|
@@ -21777,7 +22004,9 @@ var create = new Command().name("create").description("scaffold a new ChatJS cha
|
|
|
21777
22004
|
appUrl,
|
|
21778
22005
|
withElectron,
|
|
21779
22006
|
gateway,
|
|
21780
|
-
|
|
22007
|
+
coreFeatures,
|
|
22008
|
+
documentTypes,
|
|
22009
|
+
builtInTools: assistantTools.builtInTools,
|
|
21781
22010
|
auth
|
|
21782
22011
|
});
|
|
21783
22012
|
await writeFile2(join2(targetDir, "chat.config.ts"), configSource);
|
|
@@ -21787,6 +22016,18 @@ var create = new Command().name("create").description("scaffold a new ChatJS cha
|
|
|
21787
22016
|
throw error48;
|
|
21788
22017
|
}
|
|
21789
22018
|
const installNow = !options.install ? false : await promptInstall(packageManager, options.yes);
|
|
22019
|
+
if (assistantTools.installableTools.length > 0) {
|
|
22020
|
+
const toolsDir = resolveToolsPath("@/tools/chatjs", targetDir);
|
|
22021
|
+
await installRegistryTools({
|
|
22022
|
+
tools: assistantTools.installableTools,
|
|
22023
|
+
cwd: targetDir,
|
|
22024
|
+
toolsDir,
|
|
22025
|
+
toolsAlias: "@/tools/chatjs",
|
|
22026
|
+
registryUrl: options.registry,
|
|
22027
|
+
installDependenciesNow: false,
|
|
22028
|
+
packageManager
|
|
22029
|
+
});
|
|
22030
|
+
}
|
|
21790
22031
|
if (installNow) {
|
|
21791
22032
|
const installSpinner = spinner(`Installing dependencies with ${highlighter.info(packageManager)}...`).start();
|
|
21792
22033
|
try {
|
|
@@ -21797,7 +22038,12 @@ var create = new Command().name("create").description("scaffold a new ChatJS cha
|
|
|
21797
22038
|
throw error48;
|
|
21798
22039
|
}
|
|
21799
22040
|
}
|
|
21800
|
-
const envEntries = collectEnvChecklist({
|
|
22041
|
+
const envEntries = collectEnvChecklist({
|
|
22042
|
+
gateway,
|
|
22043
|
+
coreFeatures,
|
|
22044
|
+
builtInTools: assistantTools.builtInTools,
|
|
22045
|
+
auth
|
|
22046
|
+
});
|
|
21801
22047
|
gt("Your ChatJS app is ready!");
|
|
21802
22048
|
logger.info("Next steps:");
|
|
21803
22049
|
logger.break();
|