@agentskit/cli 0.13.9 → 0.13.12
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 +13 -0
- package/dist/bin.cjs +774 -7
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-K6CNOFIY.js → chunk-RNOYLQCV.js} +778 -11
- package/dist/chunk-RNOYLQCV.js.map +1 -0
- package/dist/index.cjs +774 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +14 -14
- package/dist/chunk-K6CNOFIY.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readdir, stat, mkdir, writeFile, glob, readFile } from 'fs/promises';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
|
-
import path, { join, resolve, isAbsolute, basename, dirname } from 'path';
|
|
4
|
+
import path, { join, resolve, isAbsolute, basename, dirname, sep } from 'path';
|
|
5
5
|
import { kimi, grok, deepseek, ollama, gemini, anthropic, openai } from '@agentskit/adapters';
|
|
6
6
|
import { randomBytes, createHash } from 'crypto';
|
|
7
|
-
import { writeFileSync, existsSync, readdirSync, statSync, readFileSync, mkdirSync } from 'fs';
|
|
7
|
+
import { writeFileSync, existsSync, readdirSync, statSync, readFileSync, mkdirSync, rmSync } from 'fs';
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
9
|
import React2, { useMemo, useState, useEffect, useRef } from 'react';
|
|
10
10
|
import { Box, Text, render } from 'ink';
|
|
@@ -146,7 +146,7 @@ function createDemoAdapter(provider, model) {
|
|
|
146
146
|
].join(" ");
|
|
147
147
|
for (const chunk of reply.match(/.{1,18}/g) ?? []) {
|
|
148
148
|
if (cancelled) return;
|
|
149
|
-
await new Promise((
|
|
149
|
+
await new Promise((resolve7) => setTimeout(resolve7, 35));
|
|
150
150
|
yield { type: "text", content: chunk };
|
|
151
151
|
}
|
|
152
152
|
yield { type: "done" };
|
|
@@ -4035,11 +4035,11 @@ function scaffoldAgent(schema) {
|
|
|
4035
4035
|
}
|
|
4036
4036
|
async function writeScaffold(files, outDir, opts = {}) {
|
|
4037
4037
|
const { writeFile: writeFile5, mkdir: mkdir5, access } = await import('fs/promises');
|
|
4038
|
-
const { dirname:
|
|
4038
|
+
const { dirname: dirname5, join: join11 } = await import('path');
|
|
4039
4039
|
const written = [];
|
|
4040
4040
|
for (const f of files) {
|
|
4041
|
-
const full =
|
|
4042
|
-
await mkdir5(
|
|
4041
|
+
const full = join11(outDir, f.path);
|
|
4042
|
+
await mkdir5(dirname5(full), { recursive: true });
|
|
4043
4043
|
if (!opts.overwrite) {
|
|
4044
4044
|
try {
|
|
4045
4045
|
await access(full);
|
|
@@ -4708,8 +4708,22 @@ async function defaultExists(path5) {
|
|
|
4708
4708
|
return false;
|
|
4709
4709
|
}
|
|
4710
4710
|
}
|
|
4711
|
+
function assertInstallable(agent) {
|
|
4712
|
+
if (agent.status === "draft" || agent.installable === false) {
|
|
4713
|
+
throw new Error(
|
|
4714
|
+
`"${agent.id}" is a catalog draft \u2014 not installable yet. Browse the roadmap at https://registry.agentskit.io/r/catalog.json`
|
|
4715
|
+
);
|
|
4716
|
+
}
|
|
4717
|
+
if (agent.status === "alpha") {
|
|
4718
|
+
console.warn(`[agentskit] "${agent.id}" is alpha \u2014 experimental; review before production use.`);
|
|
4719
|
+
}
|
|
4720
|
+
if (agent.status === "deprecated") {
|
|
4721
|
+
throw new Error(`"${agent.id}" is deprecated and no longer installable from the registry.`);
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4711
4724
|
async function addAgent(id, options = {}) {
|
|
4712
4725
|
const agent = await fetchAgent(id, options);
|
|
4726
|
+
assertInstallable(agent);
|
|
4713
4727
|
const baseDir = options.outDir ?? "agents";
|
|
4714
4728
|
const targetDir = join(baseDir, id);
|
|
4715
4729
|
const exists = options.existsImpl ?? defaultExists;
|
|
@@ -4781,14 +4795,767 @@ async function diffAgent(id, options = {}) {
|
|
|
4781
4795
|
}
|
|
4782
4796
|
return { agent, targetDir, files };
|
|
4783
4797
|
}
|
|
4798
|
+
function allDeps(pkg) {
|
|
4799
|
+
if (!pkg) return {};
|
|
4800
|
+
return { ...pkg.peerDependencies, ...pkg.devDependencies, ...pkg.dependencies };
|
|
4801
|
+
}
|
|
4802
|
+
function readJson(fs, path5) {
|
|
4803
|
+
const raw = fs.readFile(path5);
|
|
4804
|
+
if (raw == null) return null;
|
|
4805
|
+
try {
|
|
4806
|
+
return JSON.parse(raw);
|
|
4807
|
+
} catch {
|
|
4808
|
+
return null;
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
function detectPackageManager(fs, root) {
|
|
4812
|
+
if (fs.exists(join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
4813
|
+
if (fs.exists(join(root, "bun.lockb")) || fs.exists(join(root, "bun.lock"))) return "bun";
|
|
4814
|
+
if (fs.exists(join(root, "yarn.lock"))) return "yarn";
|
|
4815
|
+
return "npm";
|
|
4816
|
+
}
|
|
4817
|
+
function detectUiBinding(deps) {
|
|
4818
|
+
const has = (name) => name in deps;
|
|
4819
|
+
if (has("react-native") || has("expo")) return "react-native";
|
|
4820
|
+
if (has("@angular/core")) return "angular";
|
|
4821
|
+
if (has("svelte")) return "svelte";
|
|
4822
|
+
if (has("nuxt") || has("vue")) return "vue";
|
|
4823
|
+
if (has("solid-js")) return "solid";
|
|
4824
|
+
if (has("ink")) return "ink";
|
|
4825
|
+
if (has("react")) return "react";
|
|
4826
|
+
return "unknown";
|
|
4827
|
+
}
|
|
4828
|
+
function detectMetaFramework(fs, root, deps, uiBinding) {
|
|
4829
|
+
const has = (name) => name in deps;
|
|
4830
|
+
const hasPrefix = (prefix) => Object.keys(deps).some((d) => d.startsWith(prefix));
|
|
4831
|
+
if (has("next")) {
|
|
4832
|
+
if (fs.exists(join(root, "app")) || fs.exists(join(root, "src/app"))) return "next-app";
|
|
4833
|
+
return "next-pages";
|
|
4834
|
+
}
|
|
4835
|
+
if (hasPrefix("@remix-run/")) return "remix";
|
|
4836
|
+
if (has("@tanstack/react-start") || has("@tanstack/start")) return "tanstack-start";
|
|
4837
|
+
if (has("@sveltejs/kit")) return "sveltekit";
|
|
4838
|
+
if (has("nuxt")) return "nuxt";
|
|
4839
|
+
if (has("astro")) return "astro";
|
|
4840
|
+
if (uiBinding === "angular") {
|
|
4841
|
+
return has("@angular/ssr") || has("@angular/platform-server") ? "angular-ssr" : "angular-spa";
|
|
4842
|
+
}
|
|
4843
|
+
if (has("expo")) return "expo";
|
|
4844
|
+
if (has("react-scripts")) return "cra";
|
|
4845
|
+
if (has("vite")) return "vite";
|
|
4846
|
+
if (uiBinding === "ink") return "node";
|
|
4847
|
+
if (uiBinding === "unknown") return "unknown";
|
|
4848
|
+
return "none";
|
|
4849
|
+
}
|
|
4850
|
+
function detectImportAlias(fs, root) {
|
|
4851
|
+
const tsconfig = readJson(
|
|
4852
|
+
fs,
|
|
4853
|
+
join(root, "tsconfig.json")
|
|
4854
|
+
);
|
|
4855
|
+
const paths = tsconfig?.compilerOptions?.paths;
|
|
4856
|
+
if (!paths) return null;
|
|
4857
|
+
for (const key of Object.keys(paths)) {
|
|
4858
|
+
if (key.endsWith("/*")) return key.slice(0, -2);
|
|
4859
|
+
}
|
|
4860
|
+
return null;
|
|
4861
|
+
}
|
|
4862
|
+
var CSS_ENTRY_CANDIDATES = [
|
|
4863
|
+
"app/globals.css",
|
|
4864
|
+
"app/global.css",
|
|
4865
|
+
"src/app/globals.css",
|
|
4866
|
+
"src/index.css",
|
|
4867
|
+
"src/styles/globals.css",
|
|
4868
|
+
"styles/globals.css",
|
|
4869
|
+
"src/app.css",
|
|
4870
|
+
"src/styles.css"
|
|
4871
|
+
];
|
|
4872
|
+
function detectStyling(fs, root, deps) {
|
|
4873
|
+
const tailwind = "tailwindcss" in deps || fs.exists(join(root, "tailwind.config.ts")) || fs.exists(join(root, "tailwind.config.js")) || fs.exists(join(root, "tailwind.config.cjs")) || fs.exists(join(root, "tailwind.config.mjs"));
|
|
4874
|
+
let cssEntry = null;
|
|
4875
|
+
for (const candidate of CSS_ENTRY_CANDIDATES) {
|
|
4876
|
+
if (fs.exists(join(root, candidate))) {
|
|
4877
|
+
cssEntry = candidate;
|
|
4878
|
+
break;
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4881
|
+
return { mode: tailwind ? "tailwind-preset" : "data-attrs-only", cssEntry };
|
|
4882
|
+
}
|
|
4883
|
+
function detectMonorepo(fs, root) {
|
|
4884
|
+
if (fs.exists(join(root, "pnpm-workspace.yaml"))) return { tool: "pnpm", root };
|
|
4885
|
+
if (fs.exists(join(root, "turbo.json"))) return { tool: "turbo", root };
|
|
4886
|
+
if (fs.exists(join(root, "nx.json"))) return { tool: "nx", root };
|
|
4887
|
+
return null;
|
|
4888
|
+
}
|
|
4889
|
+
function scanProject(fs, root = ".") {
|
|
4890
|
+
const pkg = readJson(fs, join(root, "package.json"));
|
|
4891
|
+
const deps = allDeps(pkg);
|
|
4892
|
+
const uiBinding = detectUiBinding(deps);
|
|
4893
|
+
const metaFramework = detectMetaFramework(fs, root, deps, uiBinding);
|
|
4894
|
+
const srcDir = fs.exists(join(root, "src")) ? "src" : null;
|
|
4895
|
+
return {
|
|
4896
|
+
uiBinding,
|
|
4897
|
+
metaFramework,
|
|
4898
|
+
packageManager: detectPackageManager(fs, root),
|
|
4899
|
+
typescript: fs.exists(join(root, "tsconfig.json")),
|
|
4900
|
+
srcDir,
|
|
4901
|
+
importAlias: detectImportAlias(fs, root),
|
|
4902
|
+
styling: detectStyling(fs, root, deps),
|
|
4903
|
+
monorepo: detectMonorepo(fs, root)
|
|
4904
|
+
};
|
|
4905
|
+
}
|
|
4906
|
+
var CONFIG_PATH = ".agentskit/components.json";
|
|
4907
|
+
var SCHEMA_URL = "https://raw.githubusercontent.com/AgentsKit-io/agentskit-registry/v1/schema/components.json";
|
|
4908
|
+
var DEFAULT_REGISTRY = "https://registry.agentskit.io";
|
|
4909
|
+
var SERVER_DIR_BY_META = {
|
|
4910
|
+
"next-app": "app/api",
|
|
4911
|
+
"next-pages": "pages/api",
|
|
4912
|
+
remix: "app/routes",
|
|
4913
|
+
"tanstack-start": "app/routes",
|
|
4914
|
+
astro: "src/pages/api",
|
|
4915
|
+
sveltekit: "src/routes/api",
|
|
4916
|
+
nuxt: "server/api",
|
|
4917
|
+
"angular-ssr": "src/server",
|
|
4918
|
+
expo: "server",
|
|
4919
|
+
vite: "server",
|
|
4920
|
+
cra: "server",
|
|
4921
|
+
"angular-spa": "server",
|
|
4922
|
+
node: "server",
|
|
4923
|
+
none: "server"
|
|
4924
|
+
};
|
|
4925
|
+
function joinPath(base, sub) {
|
|
4926
|
+
return base ? `${base}/${sub}` : sub;
|
|
4927
|
+
}
|
|
4928
|
+
function buildConfig(scan) {
|
|
4929
|
+
const uiBinding = scan.uiBinding === "unknown" ? "react" : scan.uiBinding;
|
|
4930
|
+
const metaFramework = scan.metaFramework === "unknown" ? "none" : scan.metaFramework;
|
|
4931
|
+
const srcBase = scan.srcDir ?? "";
|
|
4932
|
+
const componentsPath = joinPath(srcBase, "components");
|
|
4933
|
+
const libPath = joinPath(srcBase, "lib");
|
|
4934
|
+
const rawServer = SERVER_DIR_BY_META[metaFramework];
|
|
4935
|
+
const serverPath = srcBase && (metaFramework === "next-app" || metaFramework === "next-pages") && !rawServer.startsWith("src/") ? joinPath(srcBase, rawServer) : rawServer;
|
|
4936
|
+
const alias = scan.importAlias;
|
|
4937
|
+
return {
|
|
4938
|
+
$schema: SCHEMA_URL,
|
|
4939
|
+
schemaVersion: 1,
|
|
4940
|
+
uiBinding,
|
|
4941
|
+
metaFramework,
|
|
4942
|
+
typescript: scan.typescript,
|
|
4943
|
+
styling: {
|
|
4944
|
+
mode: scan.styling.mode,
|
|
4945
|
+
css: scan.styling.cssEntry ?? "app/globals.css",
|
|
4946
|
+
tailwindConfig: null
|
|
4947
|
+
},
|
|
4948
|
+
aliases: {
|
|
4949
|
+
// components + lib are import-aliased when the project has one; the server
|
|
4950
|
+
// route is a framework-routed filesystem location, so it mirrors its path.
|
|
4951
|
+
components: alias ? `${alias}/components` : `./${componentsPath}`,
|
|
4952
|
+
lib: alias ? `${alias}/lib` : `./${libPath}`,
|
|
4953
|
+
server: serverPath
|
|
4954
|
+
},
|
|
4955
|
+
paths: {
|
|
4956
|
+
root: scan.monorepo?.root ?? ".",
|
|
4957
|
+
components: componentsPath,
|
|
4958
|
+
lib: libPath,
|
|
4959
|
+
server: serverPath
|
|
4960
|
+
},
|
|
4961
|
+
registries: { default: DEFAULT_REGISTRY }
|
|
4962
|
+
};
|
|
4963
|
+
}
|
|
4964
|
+
function serializeConfig(config) {
|
|
4965
|
+
return `${JSON.stringify(config, null, 2)}
|
|
4966
|
+
`;
|
|
4967
|
+
}
|
|
4968
|
+
function parseConfig(raw) {
|
|
4969
|
+
let data;
|
|
4970
|
+
try {
|
|
4971
|
+
data = JSON.parse(raw);
|
|
4972
|
+
} catch {
|
|
4973
|
+
return null;
|
|
4974
|
+
}
|
|
4975
|
+
if (!data || typeof data !== "object") return null;
|
|
4976
|
+
const c = data;
|
|
4977
|
+
if (typeof c.schemaVersion !== "number" || !c.uiBinding || !c.metaFramework || !c.registries) {
|
|
4978
|
+
return null;
|
|
4979
|
+
}
|
|
4980
|
+
return c;
|
|
4981
|
+
}
|
|
4982
|
+
function readConfig(io, root = ".") {
|
|
4983
|
+
const raw = io.read(join(root, CONFIG_PATH));
|
|
4984
|
+
return raw == null ? null : parseConfig(raw);
|
|
4985
|
+
}
|
|
4986
|
+
function writeConfig(io, config, root = ".") {
|
|
4987
|
+
const path5 = join(root, CONFIG_PATH);
|
|
4988
|
+
io.write(path5, serializeConfig(config));
|
|
4989
|
+
return path5;
|
|
4990
|
+
}
|
|
4991
|
+
function resolveConfig(io, scan, root = ".") {
|
|
4992
|
+
const existing = readConfig(io, root);
|
|
4993
|
+
if (existing) return { config: existing, fromDisk: true };
|
|
4994
|
+
return { config: buildConfig(scan), fromDisk: false };
|
|
4995
|
+
}
|
|
4996
|
+
var IntegrityError = class extends Error {
|
|
4997
|
+
constructor(message) {
|
|
4998
|
+
super(message);
|
|
4999
|
+
this.name = "IntegrityError";
|
|
5000
|
+
}
|
|
5001
|
+
};
|
|
5002
|
+
function isSafeRegistryPath(rel) {
|
|
5003
|
+
if (rel === "" || isAbsolute(rel)) return false;
|
|
5004
|
+
const segments = rel.split(/[/\\]/);
|
|
5005
|
+
for (const s of segments) {
|
|
5006
|
+
if (s === "" || s === "." || s === "..") return false;
|
|
5007
|
+
}
|
|
5008
|
+
return true;
|
|
5009
|
+
}
|
|
5010
|
+
function assertContained(targetDir, rel) {
|
|
5011
|
+
if (!isSafeRegistryPath(rel)) {
|
|
5012
|
+
throw new IntegrityError(`unsafe registry path rejected: ${JSON.stringify(rel)}`);
|
|
5013
|
+
}
|
|
5014
|
+
const base = resolve(targetDir);
|
|
5015
|
+
const dest = resolve(base, rel);
|
|
5016
|
+
if (dest !== base && !dest.startsWith(base + sep)) {
|
|
5017
|
+
throw new IntegrityError(`path escapes the target directory: ${JSON.stringify(rel)}`);
|
|
5018
|
+
}
|
|
5019
|
+
return dest;
|
|
5020
|
+
}
|
|
5021
|
+
function commitFiles(fs, targetDir, files, options = {}) {
|
|
5022
|
+
const planned = files.map((f) => ({ dest: assertContained(targetDir, f.path), content: f.content, rel: f.path }));
|
|
5023
|
+
if (!options.force) {
|
|
5024
|
+
const conflicts = planned.filter((p) => fs.exists(p.dest)).map((p) => p.rel);
|
|
5025
|
+
if (conflicts.length > 0) {
|
|
5026
|
+
throw new IntegrityError(
|
|
5027
|
+
`refusing to overwrite ${conflicts.length} existing file(s) (use --force): ${conflicts.join(", ")}`
|
|
5028
|
+
);
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
const written = [];
|
|
5032
|
+
try {
|
|
5033
|
+
for (const p of planned) {
|
|
5034
|
+
fs.write(p.dest, p.content);
|
|
5035
|
+
written.push(p.dest);
|
|
5036
|
+
}
|
|
5037
|
+
} catch (err) {
|
|
5038
|
+
for (const dest of written.reverse()) {
|
|
5039
|
+
try {
|
|
5040
|
+
fs.remove(dest);
|
|
5041
|
+
} catch {
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5045
|
+
throw new IntegrityError(`install failed and was rolled back (${written.length} file(s) removed): ${message}`);
|
|
5046
|
+
}
|
|
5047
|
+
return { written };
|
|
5048
|
+
}
|
|
5049
|
+
|
|
5050
|
+
// src/components/fetch.ts
|
|
5051
|
+
var SUPPORTED_SCHEMA_VERSION = 1;
|
|
5052
|
+
var DEFAULT_REGISTRY2 = "https://registry.agentskit.io";
|
|
5053
|
+
function resolveIdentifier(identifier, config, options = {}) {
|
|
5054
|
+
const registries = config?.registries ?? {};
|
|
5055
|
+
if (/^https?:\/\//.test(identifier)) {
|
|
5056
|
+
const m = identifier.match(/^(.*?)\/r\/([^/]+?)(?:\.json)?$/);
|
|
5057
|
+
if (m) return { base: m[1], itemId: m[2] };
|
|
5058
|
+
const url = new URL(identifier);
|
|
5059
|
+
const id = url.pathname.split("/").filter(Boolean).pop()?.replace(/\.json$/, "") ?? identifier;
|
|
5060
|
+
return { base: `${url.origin}`, itemId: id };
|
|
5061
|
+
}
|
|
5062
|
+
if (identifier.startsWith("@")) {
|
|
5063
|
+
const slash = identifier.indexOf("/");
|
|
5064
|
+
if (slash < 0) throw new IntegrityError(`invalid namespaced identifier: ${identifier}`);
|
|
5065
|
+
const org = identifier.slice(1, slash);
|
|
5066
|
+
const name = identifier.slice(slash + 1);
|
|
5067
|
+
const base2 = registries[org];
|
|
5068
|
+
if (!base2) throw new IntegrityError(`unknown registry namespace "@${org}" \u2014 add it to components.json registries`);
|
|
5069
|
+
return { base: base2, itemId: name };
|
|
5070
|
+
}
|
|
5071
|
+
const base = options.registryBase ?? registries.default ?? DEFAULT_REGISTRY2;
|
|
5072
|
+
return { base, itemId: identifier };
|
|
5073
|
+
}
|
|
5074
|
+
function resolveAuthHeader(base, config, env = {}) {
|
|
5075
|
+
const map = config?.registryAuth;
|
|
5076
|
+
if (!map) return {};
|
|
5077
|
+
for (const [registryBase, envVar] of Object.entries(map)) {
|
|
5078
|
+
if (base.startsWith(registryBase)) {
|
|
5079
|
+
const token = env[envVar];
|
|
5080
|
+
if (token) return { authorization: `Bearer ${token}` };
|
|
5081
|
+
}
|
|
5082
|
+
}
|
|
5083
|
+
return {};
|
|
5084
|
+
}
|
|
5085
|
+
function sha256Hex(content) {
|
|
5086
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
5087
|
+
}
|
|
5088
|
+
function verifyChecksums(files) {
|
|
5089
|
+
const bad = files.filter((f) => sha256Hex(f.content) !== f.sha256).map((f) => f.path);
|
|
5090
|
+
if (bad.length > 0) {
|
|
5091
|
+
throw new IntegrityError(`checksum mismatch \u2014 install aborted for ${bad.length} file(s): ${bad.join(", ")}`);
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
5094
|
+
function manifestUrl(ref) {
|
|
5095
|
+
return `${ref.base}/r/${ref.itemId}.json`;
|
|
5096
|
+
}
|
|
5097
|
+
function fileUrl(ref, path5) {
|
|
5098
|
+
return `${ref.base}/r/${ref.itemId}/${path5}`;
|
|
5099
|
+
}
|
|
5100
|
+
async function getText2(fetchImpl, url, headers) {
|
|
5101
|
+
const res = await fetchImpl(url, { headers });
|
|
5102
|
+
if (!res.ok) throw new IntegrityError(`fetch failed (${res.status}) for ${url}`);
|
|
5103
|
+
return res.text();
|
|
5104
|
+
}
|
|
5105
|
+
async function fetchManifest(identifier, options = {}) {
|
|
5106
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
5107
|
+
const ref = resolveIdentifier(identifier, options.config, options);
|
|
5108
|
+
const headers = resolveAuthHeader(ref.base, options.config, options.env);
|
|
5109
|
+
const raw = await getText2(fetchImpl, manifestUrl(ref), headers);
|
|
5110
|
+
if (options.signatureVerifier) {
|
|
5111
|
+
const sig = await getText2(fetchImpl, `${manifestUrl(ref)}.minisig`, headers);
|
|
5112
|
+
const ok = await options.signatureVerifier(raw, sig);
|
|
5113
|
+
if (!ok) throw new IntegrityError(`manifest signature verification failed for ${ref.itemId}`);
|
|
5114
|
+
}
|
|
5115
|
+
let parsed;
|
|
5116
|
+
try {
|
|
5117
|
+
parsed = JSON.parse(raw);
|
|
5118
|
+
} catch {
|
|
5119
|
+
throw new IntegrityError(`manifest is not valid JSON for ${ref.itemId}`);
|
|
5120
|
+
}
|
|
5121
|
+
const component = parsed;
|
|
5122
|
+
if (component?.kind !== "component") {
|
|
5123
|
+
throw new IntegrityError(`"${ref.itemId}" is not a component (kind=${String(component?.kind)})`);
|
|
5124
|
+
}
|
|
5125
|
+
if (typeof component.schemaVersion !== "number" || component.schemaVersion > SUPPORTED_SCHEMA_VERSION) {
|
|
5126
|
+
throw new IntegrityError(
|
|
5127
|
+
`"${ref.itemId}" needs a newer CLI (schemaVersion ${component.schemaVersion} > ${SUPPORTED_SCHEMA_VERSION})`
|
|
5128
|
+
);
|
|
5129
|
+
}
|
|
5130
|
+
return { ref, component };
|
|
5131
|
+
}
|
|
5132
|
+
async function fetchPortFiles(ref, port, options = {}) {
|
|
5133
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
5134
|
+
const headers = resolveAuthHeader(ref.base, options.config, options.env);
|
|
5135
|
+
const resolved = await Promise.all(
|
|
5136
|
+
port.files.map(async (f) => {
|
|
5137
|
+
const content = f.content ?? await getText2(fetchImpl, fileUrl(ref, f.path), headers);
|
|
5138
|
+
return { path: f.path, content, sha256: f.sha256 };
|
|
5139
|
+
})
|
|
5140
|
+
);
|
|
5141
|
+
verifyChecksums(resolved);
|
|
5142
|
+
return resolved.map((f) => ({ path: f.path, content: f.content }));
|
|
5143
|
+
}
|
|
5144
|
+
|
|
5145
|
+
// src/components/validate.ts
|
|
5146
|
+
var NO_NODE_SERVER = /* @__PURE__ */ new Set([
|
|
5147
|
+
"expo",
|
|
5148
|
+
"vite",
|
|
5149
|
+
"cra",
|
|
5150
|
+
"angular-spa",
|
|
5151
|
+
"none"
|
|
5152
|
+
]);
|
|
5153
|
+
var SERVERLESS_CAPABLE = /* @__PURE__ */ new Set([
|
|
5154
|
+
"next-app",
|
|
5155
|
+
"next-pages",
|
|
5156
|
+
"remix",
|
|
5157
|
+
"tanstack-start",
|
|
5158
|
+
"sveltekit",
|
|
5159
|
+
"nuxt"
|
|
5160
|
+
]);
|
|
5161
|
+
function parseVersion(input2) {
|
|
5162
|
+
const cleaned = input2.trim().replace(/^[v=]+/, "").split(/[-+]/)[0] ?? "";
|
|
5163
|
+
const parts = cleaned.split(".");
|
|
5164
|
+
if (parts.length === 0 || parts[0] === "") return null;
|
|
5165
|
+
const nums = parts.map((p) => Number.parseInt(p, 10));
|
|
5166
|
+
if (nums.some((n) => Number.isNaN(n))) return null;
|
|
5167
|
+
return { major: nums[0] ?? 0, minor: nums[1] ?? 0, patch: nums[2] ?? 0 };
|
|
5168
|
+
}
|
|
5169
|
+
function cmp(a, b) {
|
|
5170
|
+
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
|
5171
|
+
}
|
|
5172
|
+
function satisfiesRange(version, range) {
|
|
5173
|
+
const r = range.trim();
|
|
5174
|
+
if (r === "" || r === "*" || r === "x" || r.startsWith("workspace:")) return true;
|
|
5175
|
+
if (r.includes("||")) return r.split("||").some((part) => satisfiesRange(version, part));
|
|
5176
|
+
const v = parseVersion(version);
|
|
5177
|
+
if (!v) return true;
|
|
5178
|
+
const m = r.match(/^(>=|<=|>|<|=|\^|~)?\s*(.+)$/);
|
|
5179
|
+
if (!m) return true;
|
|
5180
|
+
const op = m[1] ?? "=";
|
|
5181
|
+
const target = parseVersion(m[2]);
|
|
5182
|
+
if (!target) return true;
|
|
5183
|
+
switch (op) {
|
|
5184
|
+
case ">=":
|
|
5185
|
+
return cmp(v, target) >= 0;
|
|
5186
|
+
case ">":
|
|
5187
|
+
return cmp(v, target) > 0;
|
|
5188
|
+
case "<=":
|
|
5189
|
+
return cmp(v, target) <= 0;
|
|
5190
|
+
case "<":
|
|
5191
|
+
return cmp(v, target) < 0;
|
|
5192
|
+
case "^": {
|
|
5193
|
+
if (cmp(v, target) < 0) return false;
|
|
5194
|
+
if (target.major > 0) return v.major === target.major;
|
|
5195
|
+
if (target.minor > 0) return v.major === 0 && v.minor === target.minor;
|
|
5196
|
+
return v.major === 0 && v.minor === 0 && v.patch === target.patch;
|
|
5197
|
+
}
|
|
5198
|
+
case "~":
|
|
5199
|
+
return v.major === target.major && v.minor === target.minor && cmp(v, target) >= 0;
|
|
5200
|
+
default:
|
|
5201
|
+
return cmp(v, target) === 0;
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
function validateInstall(input2) {
|
|
5205
|
+
const { scan, component, installed = {} } = input2;
|
|
5206
|
+
const issues = [];
|
|
5207
|
+
const err = (code, message) => issues.push({ severity: "error", code, message });
|
|
5208
|
+
const warn = (code, message) => issues.push({ severity: "warning", code, message });
|
|
5209
|
+
const supported = Object.keys(component.ports);
|
|
5210
|
+
if (scan.uiBinding === "unknown") {
|
|
5211
|
+
err("framework-unknown", `Could not detect the UI framework. ${component.id} supports: ${supported.join(", ")}.`);
|
|
5212
|
+
return { ok: false, issues };
|
|
5213
|
+
}
|
|
5214
|
+
const port = component.ports[scan.uiBinding];
|
|
5215
|
+
if (!port) {
|
|
5216
|
+
err(
|
|
5217
|
+
"binding-unsupported",
|
|
5218
|
+
`${component.id} has no "${scan.uiBinding}" port. Supported bindings: ${supported.join(", ")}.`
|
|
5219
|
+
);
|
|
5220
|
+
return { ok: false, issues };
|
|
5221
|
+
}
|
|
5222
|
+
const pairShipped = component.frameworks.some(
|
|
5223
|
+
(t) => t.uiBinding === scan.uiBinding && (scan.metaFramework === "unknown" || t.metaFramework === scan.metaFramework)
|
|
5224
|
+
);
|
|
5225
|
+
if (scan.metaFramework !== "unknown" && !pairShipped) {
|
|
5226
|
+
err(
|
|
5227
|
+
"target-unsupported",
|
|
5228
|
+
`${component.id} does not ship a ${scan.uiBinding} \xD7 ${scan.metaFramework} target.`
|
|
5229
|
+
);
|
|
5230
|
+
}
|
|
5231
|
+
const peerRanges = { ...component.peerRanges, ...port.peerRanges };
|
|
5232
|
+
for (const [name, range] of Object.entries(peerRanges)) {
|
|
5233
|
+
const have = installed[name];
|
|
5234
|
+
if (have == null) continue;
|
|
5235
|
+
if (!satisfiesRange(have, range)) {
|
|
5236
|
+
err("peer-mismatch", `${name}@${have} does not satisfy required ${range}.`);
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
const server = port.server;
|
|
5240
|
+
if (server) {
|
|
5241
|
+
const noServer = NO_NODE_SERVER.has(scan.metaFramework);
|
|
5242
|
+
if (server.runtimeRequirement === "nodejs" && noServer && server.delivery === "bundled") {
|
|
5243
|
+
err(
|
|
5244
|
+
"runtime-unsatisfiable",
|
|
5245
|
+
`${scan.metaFramework} has no Node server, but this port needs a bundled Node handler. Use a hosted endpoint instead.`
|
|
5246
|
+
);
|
|
5247
|
+
}
|
|
5248
|
+
if (server.embeddingBackend === "onnx-node" && noServer) {
|
|
5249
|
+
err(
|
|
5250
|
+
"embedding-unsatisfiable",
|
|
5251
|
+
`The onnx-node embedder needs a Node runtime; ${scan.metaFramework} can't run it. Use a BYO/api-remote embedder.`
|
|
5252
|
+
);
|
|
5253
|
+
}
|
|
5254
|
+
if (noServer && server.delivery === "bundled") {
|
|
5255
|
+
for (const e of component.env ?? []) {
|
|
5256
|
+
if (e.scope === "server" && e.required) {
|
|
5257
|
+
err("env-client-leak", `Server-only env "${e.name}" can't be bundled into a client-only ${scan.metaFramework} target.`);
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
}
|
|
5261
|
+
if (server.rateLimitBackend === "memory" && SERVERLESS_CAPABLE.has(scan.metaFramework)) {
|
|
5262
|
+
warn(
|
|
5263
|
+
"ratelimit-memory",
|
|
5264
|
+
`In-memory rate-limit is per-instance and ineffective on serverless ${scan.metaFramework}. Set UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN for a durable limiter.`
|
|
5265
|
+
);
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
if (port.language === "ts" && !scan.typescript) {
|
|
5269
|
+
err("language-ts-only", `${component.id}'s ${scan.uiBinding} port is TypeScript-only, but this is a JavaScript project.`);
|
|
5270
|
+
}
|
|
5271
|
+
if (port.stylingMode === "tailwind-preset" && scan.styling.mode !== "tailwind-preset") {
|
|
5272
|
+
warn("styling-tailwind", `This port expects Tailwind, which wasn't detected. The component still renders via its --ak-* tokens; wire the preset manually.`);
|
|
5273
|
+
}
|
|
5274
|
+
return { ok: !issues.some((i) => i.severity === "error"), port, issues };
|
|
5275
|
+
}
|
|
5276
|
+
|
|
5277
|
+
// src/components/marker.ts
|
|
5278
|
+
function assertUniquePaths(files) {
|
|
5279
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5280
|
+
for (const f of files) {
|
|
5281
|
+
if (seen.has(f.path)) {
|
|
5282
|
+
throw new IntegrityError(`duplicate file path in install marker: ${JSON.stringify(f.path)}`);
|
|
5283
|
+
}
|
|
5284
|
+
seen.add(f.path);
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
function sameKey(entry, id, installPath) {
|
|
5288
|
+
return entry.id === id && entry.installPath === installPath;
|
|
5289
|
+
}
|
|
5290
|
+
function upsertInstalled(config, entry) {
|
|
5291
|
+
const current = config.installed ?? [];
|
|
5292
|
+
const idx = current.findIndex((e) => sameKey(e, entry.id, entry.installPath));
|
|
5293
|
+
const installed = idx === -1 ? [...current, entry] : current.map((e, i) => i === idx ? entry : e);
|
|
5294
|
+
return { ...config, installed };
|
|
5295
|
+
}
|
|
5296
|
+
function buildInstalledComponent(args) {
|
|
5297
|
+
assertUniquePaths(args.files);
|
|
5298
|
+
const files = {};
|
|
5299
|
+
for (const f of args.files) {
|
|
5300
|
+
files[f.path] = { sha: sha256Hex(f.content), installedAt: args.now };
|
|
5301
|
+
}
|
|
5302
|
+
return {
|
|
5303
|
+
id: args.id,
|
|
5304
|
+
kind: "component",
|
|
5305
|
+
framework: args.framework,
|
|
5306
|
+
installPath: args.installPath,
|
|
5307
|
+
ref: args.ref,
|
|
5308
|
+
version: args.version,
|
|
5309
|
+
files
|
|
5310
|
+
};
|
|
5311
|
+
}
|
|
5312
|
+
function canonicalize(value) {
|
|
5313
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
5314
|
+
if (value !== null && typeof value === "object") {
|
|
5315
|
+
const sorted = {};
|
|
5316
|
+
for (const key of Object.keys(value).sort()) {
|
|
5317
|
+
sorted[key] = canonicalize(value[key]);
|
|
5318
|
+
}
|
|
5319
|
+
return sorted;
|
|
5320
|
+
}
|
|
5321
|
+
return value;
|
|
5322
|
+
}
|
|
5323
|
+
function canonicalJson(value) {
|
|
5324
|
+
return JSON.stringify(canonicalize(value));
|
|
5325
|
+
}
|
|
5326
|
+
function hashEntry(entry) {
|
|
5327
|
+
return sha256Hex(canonicalJson(entry));
|
|
5328
|
+
}
|
|
5329
|
+
function appendAudit(prevEntries, entry) {
|
|
5330
|
+
const prev = prevEntries.length > 0 ? prevEntries[prevEntries.length - 1] : void 0;
|
|
5331
|
+
const prevEntryHash = prev ? hashEntry(prev) : "";
|
|
5332
|
+
return { ...entry, prevEntryHash };
|
|
5333
|
+
}
|
|
5334
|
+
function serializeAuditLog(entries) {
|
|
5335
|
+
if (entries.length === 0) return "";
|
|
5336
|
+
return `${entries.map((e) => JSON.stringify(e)).join("\n")}
|
|
5337
|
+
`;
|
|
5338
|
+
}
|
|
5339
|
+
function parseAuditLog(raw) {
|
|
5340
|
+
const out = [];
|
|
5341
|
+
for (const line of raw.split("\n")) {
|
|
5342
|
+
const trimmed = line.trim();
|
|
5343
|
+
if (trimmed === "") continue;
|
|
5344
|
+
let parsed;
|
|
5345
|
+
try {
|
|
5346
|
+
parsed = JSON.parse(trimmed);
|
|
5347
|
+
} catch {
|
|
5348
|
+
continue;
|
|
5349
|
+
}
|
|
5350
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
5351
|
+
out.push(parsed);
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
return out;
|
|
5355
|
+
}
|
|
5356
|
+
|
|
5357
|
+
// src/components/flow.ts
|
|
5358
|
+
var AUDIT_PATH = ".agentskit/install-log.jsonl";
|
|
5359
|
+
function installedVersions(scanFs, root) {
|
|
5360
|
+
const raw = scanFs.readFile(join(root, "package.json"));
|
|
5361
|
+
if (raw == null) return {};
|
|
5362
|
+
try {
|
|
5363
|
+
const pkg = JSON.parse(raw);
|
|
5364
|
+
return { ...pkg.peerDependencies, ...pkg.devDependencies, ...pkg.dependencies };
|
|
5365
|
+
} catch {
|
|
5366
|
+
return {};
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
5369
|
+
async function addComponent(options, deps) {
|
|
5370
|
+
const root = options.root ?? ".";
|
|
5371
|
+
const scan = scanProject(deps.scanFs, root);
|
|
5372
|
+
const { config, fromDisk } = resolveConfig(deps.io, scan, root);
|
|
5373
|
+
const { ref, component } = await fetchManifest(options.identifier, {
|
|
5374
|
+
fetchImpl: deps.fetchImpl,
|
|
5375
|
+
env: deps.env,
|
|
5376
|
+
config,
|
|
5377
|
+
registryBase: options.registryBase,
|
|
5378
|
+
signatureVerifier: options.signatureVerifier
|
|
5379
|
+
});
|
|
5380
|
+
const validation = validateInstall({
|
|
5381
|
+
scan,
|
|
5382
|
+
component,
|
|
5383
|
+
installed: installedVersions(deps.scanFs, root)
|
|
5384
|
+
});
|
|
5385
|
+
if (!validation.ok || !validation.port) {
|
|
5386
|
+
const errors = validation.issues.filter((i) => i.severity === "error").map((i) => i.message);
|
|
5387
|
+
throw new IntegrityError(`cannot install ${component.id}: ${errors.join("; ") || "no compatible port"}`);
|
|
5388
|
+
}
|
|
5389
|
+
const port = validation.port;
|
|
5390
|
+
const files = await fetchPortFiles(ref, port, { fetchImpl: deps.fetchImpl, env: deps.env, config });
|
|
5391
|
+
const installPath = options.outDir ?? port.defaultTarget;
|
|
5392
|
+
const { written } = commitFiles(deps.writeFs, installPath, files, { force: options.force });
|
|
5393
|
+
const framework = {
|
|
5394
|
+
uiBinding: port.uiBinding,
|
|
5395
|
+
metaFramework: scan.metaFramework === "unknown" ? config.metaFramework : scan.metaFramework
|
|
5396
|
+
};
|
|
5397
|
+
const now = deps.now();
|
|
5398
|
+
const marker = buildInstalledComponent({
|
|
5399
|
+
id: component.id,
|
|
5400
|
+
framework,
|
|
5401
|
+
installPath,
|
|
5402
|
+
ref: ref.base,
|
|
5403
|
+
version: component.version,
|
|
5404
|
+
files,
|
|
5405
|
+
now
|
|
5406
|
+
});
|
|
5407
|
+
writeConfig(deps.io, upsertInstalled(config, marker), root);
|
|
5408
|
+
const auditPath = join(root, AUDIT_PATH);
|
|
5409
|
+
const prev = parseAuditLog(deps.io.read(auditPath) ?? "");
|
|
5410
|
+
const entry = appendAudit(prev, {
|
|
5411
|
+
schemaVersion: 1,
|
|
5412
|
+
eventType: "install",
|
|
5413
|
+
id: component.id,
|
|
5414
|
+
version: component.version,
|
|
5415
|
+
ref: ref.base,
|
|
5416
|
+
files: files.map((f) => ({ path: f.path, sha256: marker.files[f.path].sha })),
|
|
5417
|
+
manifestSigRef: options.signatureVerifier ? `${ref.itemId}.minisig` : "",
|
|
5418
|
+
timestamp: now
|
|
5419
|
+
});
|
|
5420
|
+
deps.io.write(auditPath, serializeAuditLog([...prev, entry]));
|
|
5421
|
+
return {
|
|
5422
|
+
id: component.id,
|
|
5423
|
+
version: component.version,
|
|
5424
|
+
installPath,
|
|
5425
|
+
framework,
|
|
5426
|
+
written,
|
|
5427
|
+
packages: [...component.packages, ...port.packages ?? []],
|
|
5428
|
+
env: component.env ?? [],
|
|
5429
|
+
warnings: validation.issues.filter((i) => i.severity === "warning"),
|
|
5430
|
+
configFromDisk: fromDisk
|
|
5431
|
+
};
|
|
5432
|
+
}
|
|
5433
|
+
function nodeScanFs() {
|
|
5434
|
+
return {
|
|
5435
|
+
readFile(path5) {
|
|
5436
|
+
try {
|
|
5437
|
+
return readFileSync(path5, "utf8");
|
|
5438
|
+
} catch {
|
|
5439
|
+
return null;
|
|
5440
|
+
}
|
|
5441
|
+
},
|
|
5442
|
+
exists(path5) {
|
|
5443
|
+
return existsSync(path5);
|
|
5444
|
+
}
|
|
5445
|
+
};
|
|
5446
|
+
}
|
|
5447
|
+
function nodeWriteFs() {
|
|
5448
|
+
return {
|
|
5449
|
+
exists(path5) {
|
|
5450
|
+
return existsSync(path5);
|
|
5451
|
+
},
|
|
5452
|
+
write(path5, content) {
|
|
5453
|
+
mkdirSync(dirname(path5), { recursive: true });
|
|
5454
|
+
writeFileSync(path5, content, "utf8");
|
|
5455
|
+
},
|
|
5456
|
+
remove(path5) {
|
|
5457
|
+
rmSync(path5, { force: true });
|
|
5458
|
+
}
|
|
5459
|
+
};
|
|
5460
|
+
}
|
|
5461
|
+
function nodeConfigIo() {
|
|
5462
|
+
return {
|
|
5463
|
+
read(path5) {
|
|
5464
|
+
try {
|
|
5465
|
+
return readFileSync(path5, "utf8");
|
|
5466
|
+
} catch {
|
|
5467
|
+
return null;
|
|
5468
|
+
}
|
|
5469
|
+
},
|
|
5470
|
+
write(path5, content) {
|
|
5471
|
+
mkdirSync(dirname(path5), { recursive: true });
|
|
5472
|
+
writeFileSync(path5, content, "utf8");
|
|
5473
|
+
}
|
|
5474
|
+
};
|
|
5475
|
+
}
|
|
5476
|
+
var nodeFetch = async (url, init) => {
|
|
5477
|
+
const res = await globalThis.fetch(url, { headers: init?.headers });
|
|
5478
|
+
return {
|
|
5479
|
+
ok: res.ok,
|
|
5480
|
+
status: res.status,
|
|
5481
|
+
text: () => res.text()
|
|
5482
|
+
};
|
|
5483
|
+
};
|
|
5484
|
+
|
|
5485
|
+
// src/commands/add-component.ts
|
|
5486
|
+
function isNotAComponent(err) {
|
|
5487
|
+
return err instanceof IntegrityError && /fetch failed|is not a component/i.test(err.message);
|
|
5488
|
+
}
|
|
5489
|
+
function printReport(result) {
|
|
5490
|
+
const { id, version, installPath, framework, written, packages, env, warnings, configFromDisk } = result;
|
|
5491
|
+
const out = process.stdout;
|
|
5492
|
+
out.write(`
|
|
5493
|
+
\u2713 Installed ${id}@${version} \u2192 ${installPath}/ (${framework.uiBinding} \xD7 ${framework.metaFramework})
|
|
5494
|
+
`);
|
|
5495
|
+
out.write(` wrote ${written.length} file${written.length === 1 ? "" : "s"}
|
|
5496
|
+
`);
|
|
5497
|
+
if (packages.length > 0) {
|
|
5498
|
+
out.write(`
|
|
5499
|
+
Install the packages it composes:
|
|
5500
|
+
npm install ${packages.join(" ")}
|
|
5501
|
+
`);
|
|
5502
|
+
}
|
|
5503
|
+
const required = env.filter((e) => e.required);
|
|
5504
|
+
if (required.length > 0) {
|
|
5505
|
+
out.write(`
|
|
5506
|
+
Required environment:
|
|
5507
|
+
`);
|
|
5508
|
+
for (const e of required) out.write(` ${e.name} \u2014 ${e.description}
|
|
5509
|
+
`);
|
|
5510
|
+
}
|
|
5511
|
+
if (warnings.length > 0) {
|
|
5512
|
+
out.write(`
|
|
5513
|
+
Warnings:
|
|
5514
|
+
`);
|
|
5515
|
+
for (const w of warnings) out.write(` \u26A0 ${w.message}
|
|
5516
|
+
`);
|
|
5517
|
+
}
|
|
5518
|
+
if (!configFromDisk) {
|
|
5519
|
+
out.write(`
|
|
5520
|
+
Tip: run \`agentskit init\` to commit .agentskit/components.json and skip the scan next time.
|
|
5521
|
+
`);
|
|
5522
|
+
}
|
|
5523
|
+
out.write("\n");
|
|
5524
|
+
}
|
|
5525
|
+
async function tryComponentAdd(name, options) {
|
|
5526
|
+
try {
|
|
5527
|
+
const result = await addComponent(
|
|
5528
|
+
{ identifier: name, outDir: options.out, force: options.force === true, registryBase: options.registry },
|
|
5529
|
+
{
|
|
5530
|
+
scanFs: nodeScanFs(),
|
|
5531
|
+
io: nodeConfigIo(),
|
|
5532
|
+
writeFs: nodeWriteFs(),
|
|
5533
|
+
fetchImpl: nodeFetch,
|
|
5534
|
+
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
5535
|
+
env: process.env
|
|
5536
|
+
}
|
|
5537
|
+
);
|
|
5538
|
+
printReport(result);
|
|
5539
|
+
return "installed";
|
|
5540
|
+
} catch (err) {
|
|
5541
|
+
if (isNotAComponent(err)) return "not-a-component";
|
|
5542
|
+
throw err;
|
|
5543
|
+
}
|
|
5544
|
+
}
|
|
4784
5545
|
|
|
4785
5546
|
// src/commands/add.ts
|
|
4786
5547
|
function registerAddCommand(program) {
|
|
4787
|
-
program.command("add <
|
|
4788
|
-
'Add a ready-made agent from the AgentsKit registry (registry.agentskit.io).
|
|
4789
|
-
).option("--out <dir>", "Directory to
|
|
5548
|
+
program.command("add <name>").description(
|
|
5549
|
+
'Add a ready-made agent or UI component from the AgentsKit registry (registry.agentskit.io). Auto-detects which: a component is scanned/validated against your project and installed transactionally; an agent is copied into ./agents. You own the code. e.g. `agentskit add docs-chat` (component) or `agentskit add research --run "\u2026"` (agent).'
|
|
5550
|
+
).option("--out <dir>", "Directory to install into (component: the port dir; agent: ./agents)").option("-f, --force", "Overwrite existing files").option("--registry <url>", "Registry base URL override (component install)").option("--run <task>", "Run the agent on this task right after adding it").option("--provider <provider>", "Provider for --run (openai, anthropic, gemini, ollama, demo)", "demo").option("--model <model>", "Model for --run").option("--api-key <key>", "API key for --run (else read from the provider env var)").action(
|
|
4790
5551
|
async (agent, options) => {
|
|
4791
5552
|
try {
|
|
5553
|
+
const outcome = await tryComponentAdd(agent, {
|
|
5554
|
+
out: options.out,
|
|
5555
|
+
force: options.force,
|
|
5556
|
+
registry: options.registry
|
|
5557
|
+
});
|
|
5558
|
+
if (outcome === "installed") return;
|
|
4792
5559
|
const result = await addAgent(agent, { outDir: options.out, force: options.force === true });
|
|
4793
5560
|
process.stdout.write(`
|
|
4794
5561
|
Added "${result.agent.title}" \u2192 ${result.targetDir}/
|
|
@@ -4942,5 +5709,5 @@ function createCli() {
|
|
|
4942
5709
|
}
|
|
4943
5710
|
|
|
4944
5711
|
export { ChatApp, HookDispatcher, McpClient, applyPolicyToTool, applyPolicyToTools, bridgeMcpServers, buildRagFromConfig, computeCost, configHooksToHandlers, createCli, createOpenAiEmbedder, defaultPolicy, derivePreview, disposeMcpClients, evaluatePolicy, findLatestSession, findSession, forkSession, generateSessionId, getPricing, indexSources, listSessions, loadConfig, loadPlugins, mergePluginsIntoBundle, registerPricing, renameSession, renderChatHeader, renderReport, resolveChatProvider, resolveSession, runAgent, runDoctor, sessionFilePath, startDev, startTunnel, writeSessionMeta, writeStarterProject };
|
|
4945
|
-
//# sourceMappingURL=chunk-
|
|
4946
|
-
//# sourceMappingURL=chunk-
|
|
5712
|
+
//# sourceMappingURL=chunk-RNOYLQCV.js.map
|
|
5713
|
+
//# sourceMappingURL=chunk-RNOYLQCV.js.map
|