@lobehub/cli 0.0.1-canary.8 → 0.0.1-canary.9

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.
Files changed (2) hide show
  1. package/dist/index.js +996 -249
  2. package/package.json +7 -5
package/dist/index.js CHANGED
@@ -348,27 +348,27 @@ var noop = () => {
348
348
  var freezeIfAvailable = (obj) => {
349
349
  if (Object.freeze) Object.freeze(obj);
350
350
  };
351
- function createInnerProxy(callback, path12, memo) {
351
+ function createInnerProxy(callback, path13, memo) {
352
352
  var _memo$cacheKey;
353
- const cacheKey = path12.join(".");
353
+ const cacheKey = path13.join(".");
354
354
  (_memo$cacheKey = memo[cacheKey]) !== null && _memo$cacheKey !== void 0 || (memo[cacheKey] = new Proxy(noop, {
355
355
  get(_obj, key) {
356
356
  if (typeof key !== "string" || key === "then") return void 0;
357
- return createInnerProxy(callback, [...path12, key], memo);
357
+ return createInnerProxy(callback, [...path13, key], memo);
358
358
  },
359
359
  apply(_1, _2, args) {
360
- const lastOfPath = path12[path12.length - 1];
360
+ const lastOfPath = path13[path13.length - 1];
361
361
  let opts = {
362
362
  args,
363
- path: path12
363
+ path: path13
364
364
  };
365
365
  if (lastOfPath === "call") opts = {
366
366
  args: args.length >= 2 ? [args[1]] : [],
367
- path: path12.slice(0, -1)
367
+ path: path13.slice(0, -1)
368
368
  };
369
369
  else if (lastOfPath === "apply") opts = {
370
370
  args: args.length >= 2 ? args[1] : [],
371
- path: path12.slice(0, -1)
371
+ path: path13.slice(0, -1)
372
372
  };
373
373
  freezeIfAvailable(opts.args);
374
374
  freezeIfAvailable(opts.path);
@@ -700,11 +700,11 @@ function httpLink(opts) {
700
700
  return (operationOpts) => {
701
701
  const { op } = operationOpts;
702
702
  return observable((observer) => {
703
- const { path: path12, input, type } = op;
703
+ const { path: path13, input, type } = op;
704
704
  if (type === "subscription") throw new Error("Subscriptions are unsupported by `httpLink` - use `httpSubscriptionLink` or `wsLink`");
705
705
  const request = universalRequester((0, import_objectSpread25.default)((0, import_objectSpread25.default)({}, resolvedOpts), {}, {
706
706
  type,
707
- path: path12,
707
+ path: path13,
708
708
  input,
709
709
  signal: op.signal,
710
710
  headers() {
@@ -925,28 +925,28 @@ var TRPCUntypedClient = class {
925
925
  throw TRPCClientError.from(err);
926
926
  }
927
927
  }
928
- query(path12, input, opts) {
928
+ query(path13, input, opts) {
929
929
  return this.requestAsPromise({
930
930
  type: "query",
931
- path: path12,
931
+ path: path13,
932
932
  input,
933
933
  context: opts === null || opts === void 0 ? void 0 : opts.context,
934
934
  signal: opts === null || opts === void 0 ? void 0 : opts.signal
935
935
  });
936
936
  }
937
- mutation(path12, input, opts) {
937
+ mutation(path13, input, opts) {
938
938
  return this.requestAsPromise({
939
939
  type: "mutation",
940
- path: path12,
940
+ path: path13,
941
941
  input,
942
942
  context: opts === null || opts === void 0 ? void 0 : opts.context,
943
943
  signal: opts === null || opts === void 0 ? void 0 : opts.signal
944
944
  });
945
945
  }
946
- subscription(path12, input, opts) {
946
+ subscription(path13, input, opts) {
947
947
  const observable$ = this.$request({
948
948
  type: "subscription",
949
- path: path12,
949
+ path: path13,
950
950
  input,
951
951
  context: opts.context,
952
952
  signal: opts.signal
@@ -998,8 +998,8 @@ var clientCallTypeToProcedureType = (clientCallType) => {
998
998
  return clientCallTypeMap[clientCallType];
999
999
  };
1000
1000
  function createTRPCClientProxy(client) {
1001
- const proxy = createRecursiveProxy(({ path: path12, args }) => {
1002
- const pathCopy = [...path12];
1001
+ const proxy = createRecursiveProxy(({ path: path13, args }) => {
1002
+ const pathCopy = [...path13];
1003
1003
  const procedureType = clientCallTypeToProcedureType(pathCopy.pop());
1004
1004
  const fullPath = pathCopy.join(".");
1005
1005
  return client[procedureType](fullPath, ...args);
@@ -1367,7 +1367,7 @@ var isURL = (payload) => payload instanceof URL;
1367
1367
 
1368
1368
  // node_modules/.pnpm/superjson@2.2.6/node_modules/superjson/dist/pathstringifier.js
1369
1369
  var escapeKey = (key) => key.replace(/\\/g, "\\\\").replace(/\./g, "\\.");
1370
- var stringifyPath = (path12) => path12.map(String).map(escapeKey).join(".");
1370
+ var stringifyPath = (path13) => path13.map(String).map(escapeKey).join(".");
1371
1371
  var parsePath = (string, legacyPaths) => {
1372
1372
  const result = [];
1373
1373
  let segment = "";
@@ -1614,26 +1614,26 @@ var getNthKey = (value, n) => {
1614
1614
  }
1615
1615
  return keys.next().value;
1616
1616
  };
1617
- function validatePath(path12) {
1618
- if (includes(path12, "__proto__")) {
1617
+ function validatePath(path13) {
1618
+ if (includes(path13, "__proto__")) {
1619
1619
  throw new Error("__proto__ is not allowed as a property");
1620
1620
  }
1621
- if (includes(path12, "prototype")) {
1621
+ if (includes(path13, "prototype")) {
1622
1622
  throw new Error("prototype is not allowed as a property");
1623
1623
  }
1624
- if (includes(path12, "constructor")) {
1624
+ if (includes(path13, "constructor")) {
1625
1625
  throw new Error("constructor is not allowed as a property");
1626
1626
  }
1627
1627
  }
1628
- var getDeep = (object, path12) => {
1629
- validatePath(path12);
1630
- for (let i = 0; i < path12.length; i++) {
1631
- const key = path12[i];
1628
+ var getDeep = (object, path13) => {
1629
+ validatePath(path13);
1630
+ for (let i = 0; i < path13.length; i++) {
1631
+ const key = path13[i];
1632
1632
  if (isSet(object)) {
1633
1633
  object = getNthKey(object, +key);
1634
1634
  } else if (isMap(object)) {
1635
1635
  const row = +key;
1636
- const type = +path12[++i] === 0 ? "key" : "value";
1636
+ const type = +path13[++i] === 0 ? "key" : "value";
1637
1637
  const keyOfRow = getNthKey(object, row);
1638
1638
  switch (type) {
1639
1639
  case "key":
@@ -1649,14 +1649,14 @@ var getDeep = (object, path12) => {
1649
1649
  }
1650
1650
  return object;
1651
1651
  };
1652
- var setDeep = (object, path12, mapper) => {
1653
- validatePath(path12);
1654
- if (path12.length === 0) {
1652
+ var setDeep = (object, path13, mapper) => {
1653
+ validatePath(path13);
1654
+ if (path13.length === 0) {
1655
1655
  return mapper(object);
1656
1656
  }
1657
1657
  let parent = object;
1658
- for (let i = 0; i < path12.length - 1; i++) {
1659
- const key = path12[i];
1658
+ for (let i = 0; i < path13.length - 1; i++) {
1659
+ const key = path13[i];
1660
1660
  if (isArray(parent)) {
1661
1661
  const index = +key;
1662
1662
  parent = parent[index];
@@ -1666,12 +1666,12 @@ var setDeep = (object, path12, mapper) => {
1666
1666
  const row = +key;
1667
1667
  parent = getNthKey(parent, row);
1668
1668
  } else if (isMap(parent)) {
1669
- const isEnd = i === path12.length - 2;
1669
+ const isEnd = i === path13.length - 2;
1670
1670
  if (isEnd) {
1671
1671
  break;
1672
1672
  }
1673
1673
  const row = +key;
1674
- const type = +path12[++i] === 0 ? "key" : "value";
1674
+ const type = +path13[++i] === 0 ? "key" : "value";
1675
1675
  const keyOfRow = getNthKey(parent, row);
1676
1676
  switch (type) {
1677
1677
  case "key":
@@ -1683,7 +1683,7 @@ var setDeep = (object, path12, mapper) => {
1683
1683
  }
1684
1684
  }
1685
1685
  }
1686
- const lastKey = path12[path12.length - 1];
1686
+ const lastKey = path13[path13.length - 1];
1687
1687
  if (isArray(parent)) {
1688
1688
  parent[+lastKey] = mapper(parent[+lastKey]);
1689
1689
  } else if (isPlainObject(parent)) {
@@ -1698,7 +1698,7 @@ var setDeep = (object, path12, mapper) => {
1698
1698
  }
1699
1699
  }
1700
1700
  if (isMap(parent)) {
1701
- const row = +path12[path12.length - 2];
1701
+ const row = +path13[path13.length - 2];
1702
1702
  const keyToRow = getNthKey(parent, row);
1703
1703
  const type = +lastKey === 0 ? "key" : "value";
1704
1704
  switch (type) {
@@ -1745,16 +1745,16 @@ function traverse(tree, walker2, version2, origin = []) {
1745
1745
  walker2(nodeValue, origin);
1746
1746
  }
1747
1747
  function applyValueAnnotations(plain, annotations, version2, superJson) {
1748
- traverse(annotations, (type, path12) => {
1749
- plain = setDeep(plain, path12, (v) => untransformValue(v, type, superJson));
1748
+ traverse(annotations, (type, path13) => {
1749
+ plain = setDeep(plain, path13, (v) => untransformValue(v, type, superJson));
1750
1750
  }, version2);
1751
1751
  return plain;
1752
1752
  }
1753
1753
  function applyReferentialEqualityAnnotations(plain, annotations, version2) {
1754
1754
  const legacyPaths = enableLegacyPaths(version2);
1755
- function apply(identicalPaths, path12) {
1756
- const object = getDeep(plain, parsePath(path12, legacyPaths));
1757
- identicalPaths.map((path13) => parsePath(path13, legacyPaths)).forEach((identicalObjectPath) => {
1755
+ function apply(identicalPaths, path13) {
1756
+ const object = getDeep(plain, parsePath(path13, legacyPaths));
1757
+ identicalPaths.map((path14) => parsePath(path14, legacyPaths)).forEach((identicalObjectPath) => {
1758
1758
  plain = setDeep(plain, identicalObjectPath, () => object);
1759
1759
  });
1760
1760
  }
@@ -1772,12 +1772,12 @@ function applyReferentialEqualityAnnotations(plain, annotations, version2) {
1772
1772
  return plain;
1773
1773
  }
1774
1774
  var isDeep = (object, superJson) => isPlainObject(object) || isArray(object) || isMap(object) || isSet(object) || isError(object) || isInstanceOfRegisteredClass(object, superJson);
1775
- function addIdentity(object, path12, identities) {
1775
+ function addIdentity(object, path13, identities) {
1776
1776
  const existingSet = identities.get(object);
1777
1777
  if (existingSet) {
1778
- existingSet.push(path12);
1778
+ existingSet.push(path13);
1779
1779
  } else {
1780
- identities.set(object, [path12]);
1780
+ identities.set(object, [path13]);
1781
1781
  }
1782
1782
  }
1783
1783
  function generateReferentialEqualityAnnotations(identitites, dedupe) {
@@ -1788,7 +1788,7 @@ function generateReferentialEqualityAnnotations(identitites, dedupe) {
1788
1788
  return;
1789
1789
  }
1790
1790
  if (!dedupe) {
1791
- paths = paths.map((path12) => path12.map(String)).sort((a, b) => a.length - b.length);
1791
+ paths = paths.map((path13) => path13.map(String)).sort((a, b) => a.length - b.length);
1792
1792
  }
1793
1793
  const [representativePath, ...identicalPaths] = paths;
1794
1794
  if (representativePath.length === 0) {
@@ -1807,10 +1807,10 @@ function generateReferentialEqualityAnnotations(identitites, dedupe) {
1807
1807
  return isEmptyObject(result) ? void 0 : result;
1808
1808
  }
1809
1809
  }
1810
- var walker = (object, identities, superJson, dedupe, path12 = [], objectsInThisPath = [], seenObjects = /* @__PURE__ */ new Map()) => {
1810
+ var walker = (object, identities, superJson, dedupe, path13 = [], objectsInThisPath = [], seenObjects = /* @__PURE__ */ new Map()) => {
1811
1811
  const primitive = isPrimitive(object);
1812
1812
  if (!primitive) {
1813
- addIdentity(object, path12, identities);
1813
+ addIdentity(object, path13, identities);
1814
1814
  const seen = seenObjects.get(object);
1815
1815
  if (seen) {
1816
1816
  return dedupe ? {
@@ -1844,7 +1844,7 @@ var walker = (object, identities, superJson, dedupe, path12 = [], objectsInThisP
1844
1844
  if (index === "__proto__" || index === "constructor" || index === "prototype") {
1845
1845
  throw new Error(`Detected property ${index}. This is a prototype pollution risk, please remove it from your object.`);
1846
1846
  }
1847
- const recursiveResult = walker(value, identities, superJson, dedupe, [...path12, index], [...objectsInThisPath, object], seenObjects);
1847
+ const recursiveResult = walker(value, identities, superJson, dedupe, [...path13, index], [...objectsInThisPath, object], seenObjects);
1848
1848
  transformedValue[index] = recursiveResult.transformedValue;
1849
1849
  if (isArray(recursiveResult.annotations)) {
1850
1850
  innerAnnotations[escapeKey(index)] = recursiveResult.annotations;
@@ -2072,7 +2072,8 @@ var log = {
2072
2072
  };
2073
2073
 
2074
2074
  // src/settings/index.ts
2075
- var SETTINGS_DIR = path.join(os.homedir(), ".lobehub");
2075
+ var LOBEHUB_DIR_NAME = process.env.LOBEHUB_CLI_HOME || ".lobehub";
2076
+ var SETTINGS_DIR = path.join(os.homedir(), LOBEHUB_DIR_NAME);
2076
2077
  var SETTINGS_FILE = path.join(SETTINGS_DIR, "settings.json");
2077
2078
  function normalizeUrl(url) {
2078
2079
  return url ? url.replace(/\/$/, "") : void 0;
@@ -2120,7 +2121,8 @@ import crypto from "crypto";
2120
2121
  import fs2 from "fs";
2121
2122
  import os2 from "os";
2122
2123
  import path2 from "path";
2123
- var CREDENTIALS_DIR = path2.join(os2.homedir(), ".lobehub");
2124
+ var LOBEHUB_DIR_NAME2 = process.env.LOBEHUB_CLI_HOME || ".lobehub";
2125
+ var CREDENTIALS_DIR = path2.join(os2.homedir(), LOBEHUB_DIR_NAME2);
2124
2126
  var CREDENTIALS_FILE = path2.join(CREDENTIALS_DIR, "credentials.json");
2125
2127
  function deriveKey() {
2126
2128
  const material = `lobehub-cli:${os2.hostname()}:${os2.userInfo().username}`;
@@ -2718,6 +2720,20 @@ function confirm(message) {
2718
2720
  }
2719
2721
 
2720
2722
  // src/commands/agent.ts
2723
+ async function resolveAgentId(client, opts) {
2724
+ if (opts.agentId) return opts.agentId;
2725
+ if (opts.slug) {
2726
+ const agent = await client.agent.getBuiltinAgent.query({ slug: opts.slug });
2727
+ if (!agent) {
2728
+ log.error(`Agent not found for slug: ${opts.slug}`);
2729
+ process.exit(1);
2730
+ }
2731
+ return agent.id || agent.agentId;
2732
+ }
2733
+ log.error("Either <agentId> or --slug is required.");
2734
+ process.exit(1);
2735
+ return "";
2736
+ }
2721
2737
  function registerAgentCommand(program2) {
2722
2738
  const agent = program2.command("agent").description("Manage agents");
2723
2739
  agent.command("list").description("List agents").option("-L, --limit <n>", "Maximum number of items", "30").option("-k, --keyword <keyword>", "Filter by keyword").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
@@ -2744,32 +2760,35 @@ function registerAgentCommand(program2) {
2744
2760
  ]);
2745
2761
  printTable(rows, ["ID", "TITLE", "DESCRIPTION", "MODEL"]);
2746
2762
  });
2747
- agent.command("view <agentId>").description("View agent configuration").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (agentId, options) => {
2748
- const client = await getTrpcClient();
2749
- const result = await client.agent.getAgentConfigById.query({ agentId });
2750
- if (!result) {
2751
- log.error(`Agent not found: ${agentId}`);
2752
- process.exit(1);
2753
- return;
2754
- }
2755
- if (options.json !== void 0) {
2756
- const fields = typeof options.json === "string" ? options.json : void 0;
2757
- outputJson(result, fields);
2758
- return;
2759
- }
2760
- const r = result;
2761
- console.log(pc4.bold(r.title || r.meta?.title || "Untitled"));
2762
- const meta = [];
2763
- if (r.description || r.meta?.description) meta.push(r.description || r.meta.description);
2764
- if (r.model) meta.push(`Model: ${r.model}`);
2765
- if (r.provider) meta.push(`Provider: ${r.provider}`);
2766
- if (meta.length > 0) console.log(pc4.dim(meta.join(" \xB7 ")));
2767
- if (r.systemRole) {
2768
- console.log();
2769
- console.log(pc4.bold("System Role:"));
2770
- console.log(r.systemRole);
2763
+ agent.command("view [agentId]").description("View agent configuration").option("-s, --slug <slug>", "Agent slug (e.g. inbox)").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(
2764
+ async (agentIdArg, options) => {
2765
+ const client = await getTrpcClient();
2766
+ const agentId = await resolveAgentId(client, { agentId: agentIdArg, slug: options.slug });
2767
+ const result = await client.agent.getAgentConfigById.query({ agentId });
2768
+ if (!result) {
2769
+ log.error(`Agent not found: ${agentId}`);
2770
+ process.exit(1);
2771
+ return;
2772
+ }
2773
+ if (options.json !== void 0) {
2774
+ const fields = typeof options.json === "string" ? options.json : void 0;
2775
+ outputJson(result, fields);
2776
+ return;
2777
+ }
2778
+ const r = result;
2779
+ console.log(pc4.bold(r.title || r.meta?.title || "Untitled"));
2780
+ const meta = [];
2781
+ if (r.description || r.meta?.description) meta.push(r.description || r.meta.description);
2782
+ if (r.model) meta.push(`Model: ${r.model}`);
2783
+ if (r.provider) meta.push(`Provider: ${r.provider}`);
2784
+ if (meta.length > 0) console.log(pc4.dim(meta.join(" \xB7 ")));
2785
+ if (r.systemRole) {
2786
+ console.log();
2787
+ console.log(pc4.bold("System Role:"));
2788
+ console.log(r.systemRole);
2789
+ }
2771
2790
  }
2772
- });
2791
+ );
2773
2792
  agent.command("create").description("Create a new agent").option("-t, --title <title>", "Agent title").option("-d, --description <desc>", "Agent description").option("-m, --model <model>", "Model ID").option("-p, --provider <provider>", "Provider ID").option("-s, --system-role <role>", "System role prompt").option("--group <groupId>", "Group ID").action(
2774
2793
  async (options) => {
2775
2794
  const client = await getTrpcClient();
@@ -2787,8 +2806,8 @@ function registerAgentCommand(program2) {
2787
2806
  if (r.sessionId) console.log(` Session: ${r.sessionId}`);
2788
2807
  }
2789
2808
  );
2790
- agent.command("edit <agentId>").description("Update agent configuration").option("-t, --title <title>", "New title").option("-d, --description <desc>", "New description").option("-m, --model <model>", "New model ID").option("-p, --provider <provider>", "New provider ID").option("-s, --system-role <role>", "New system role prompt").action(
2791
- async (agentId, options) => {
2809
+ agent.command("edit [agentId]").description("Update agent configuration").option("--slug <slug>", "Agent slug (e.g. inbox)").option("-t, --title <title>", "New title").option("-d, --description <desc>", "New description").option("-m, --model <model>", "New model ID").option("-p, --provider <provider>", "New provider ID").option("-s, --system-role <role>", "New system role prompt").action(
2810
+ async (agentIdArg, options) => {
2792
2811
  const value = {};
2793
2812
  if (options.title) value.title = options.title;
2794
2813
  if (options.description) value.description = options.description;
@@ -2802,6 +2821,7 @@ function registerAgentCommand(program2) {
2802
2821
  process.exit(1);
2803
2822
  }
2804
2823
  const client = await getTrpcClient();
2824
+ const agentId = await resolveAgentId(client, { agentId: agentIdArg, slug: options.slug });
2805
2825
  await client.agent.updateAgentConfig.mutate({ agentId, value });
2806
2826
  console.log(`${pc4.green("\u2713")} Updated agent ${pc4.bold(agentId)}`);
2807
2827
  }
@@ -3018,13 +3038,33 @@ function registerConfigCommand(program2) {
3018
3038
  const monthLabel = options.month || (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
3019
3039
  const mode = options.daily ? "Daily" : "Monthly";
3020
3040
  printBoxTable(columns, rows, `LobeHub Token Usage Report - ${mode} (${monthLabel})`);
3021
- const calendarData = activeLogs.map((log3) => ({
3022
- day: log3.day || "",
3041
+ const now = /* @__PURE__ */ new Date();
3042
+ const rangeStart = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate() + 1);
3043
+ let yearLogs;
3044
+ try {
3045
+ yearLogs = await client.usage.findAndGroupByDateRange.query({
3046
+ endAt: now.toISOString().slice(0, 10),
3047
+ startAt: rangeStart.toISOString().slice(0, 10)
3048
+ });
3049
+ } catch {
3050
+ const monthKeys = [];
3051
+ for (let i = 11; i >= 0; i--) {
3052
+ const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
3053
+ monthKeys.push(d.toISOString().slice(0, 7));
3054
+ }
3055
+ const results = await Promise.all(
3056
+ monthKeys.map((mo) => client.usage.findAndGroupByDay.query({ mo }))
3057
+ );
3058
+ yearLogs = results.flat();
3059
+ }
3060
+ const calendarData = (Array.isArray(yearLogs) ? yearLogs : []).filter((log3) => log3.day).map((log3) => ({
3061
+ day: log3.day,
3023
3062
  value: log3.totalTokens || 0
3024
3063
  }));
3064
+ const yearTotal = calendarData.reduce((acc, d) => acc + d.value, 0);
3025
3065
  printCalendarHeatmap(calendarData, {
3026
- label: `Total: ${formatNumber(sumTotal)} tokens \xB7 ${formatCost(sumCost)}`,
3027
- title: "Activity"
3066
+ label: `Past 12 months: ${formatNumber(yearTotal)} tokens`,
3067
+ title: "Activity (past 12 months)"
3028
3068
  });
3029
3069
  });
3030
3070
  }
@@ -4150,15 +4190,16 @@ var ShellProcessManager = class {
4150
4190
  // ../../packages/local-file-shell/src/shell/runner.ts
4151
4191
  import { spawn as spawn3 } from "child_process";
4152
4192
  import { randomUUID as randomUUID2 } from "crypto";
4153
- async function runCommand({ command, description, run_in_background, timeout = 12e4 }, { processManager: processManager2, logger }) {
4193
+ async function runCommand({ command, cwd, description, run_in_background, timeout = 12e4 }, { processManager: processManager2, logger }) {
4154
4194
  const logPrefix = `[runCommand: ${description || command.slice(0, 50)}]`;
4155
- logger?.debug(`${logPrefix} Starting`, { background: run_in_background, timeout });
4195
+ logger?.debug(`${logPrefix} Starting`, { background: run_in_background, cwd, timeout });
4156
4196
  const effectiveTimeout = Math.min(Math.max(timeout, 1e3), 6e5);
4157
4197
  const shellConfig = getShellConfig(command);
4158
4198
  try {
4159
4199
  if (run_in_background) {
4160
4200
  const shellId = randomUUID2();
4161
4201
  const childProcess = spawn3(shellConfig.cmd, shellConfig.args, {
4202
+ cwd,
4162
4203
  env: process.env,
4163
4204
  shell: false
4164
4205
  });
@@ -4184,6 +4225,7 @@ async function runCommand({ command, description, run_in_background, timeout = 1
4184
4225
  } else {
4185
4226
  return new Promise((resolve) => {
4186
4227
  const childProcess = spawn3(shellConfig.cmd, shellConfig.args, {
4228
+ cwd,
4187
4229
  env: process.env,
4188
4230
  shell: false
4189
4231
  });
@@ -4210,6 +4252,7 @@ async function runCommand({ command, description, run_in_background, timeout = 1
4210
4252
  if (!killed) {
4211
4253
  clearTimeout(timeoutHandle);
4212
4254
  const success = code === 0;
4255
+ logger?.info?.(`${logPrefix} Command completed`, { code, success });
4213
4256
  resolve({
4214
4257
  exit_code: code || 0,
4215
4258
  output: truncateOutput(stdout + stderr),
@@ -4221,6 +4264,7 @@ async function runCommand({ command, description, run_in_background, timeout = 1
4221
4264
  });
4222
4265
  childProcess.on("error", (error) => {
4223
4266
  clearTimeout(timeoutHandle);
4267
+ logger?.error(`${logPrefix} Command failed:`, error);
4224
4268
  resolve({
4225
4269
  error: error.message,
4226
4270
  stderr: truncateOutput(stderr),
@@ -4542,30 +4586,35 @@ function readBodyContent(options) {
4542
4586
  }
4543
4587
  function registerDocCommand(program2) {
4544
4588
  const doc = program2.command("doc").description("Manage documents");
4545
- doc.command("list").description("List documents").option("-L, --limit <n>", "Maximum number of items to fetch", "30").option("--file-type <type>", "Filter by file type").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
4546
- const client = await getTrpcClient();
4547
- const pageSize = Number.parseInt(options.limit || "30", 10);
4548
- const query = { pageSize };
4549
- if (options.fileType) query.fileTypes = [options.fileType];
4550
- const result = await client.document.queryDocuments.query(query);
4551
- const docs = Array.isArray(result) ? result : result.items ?? [];
4552
- if (options.json !== void 0) {
4553
- const fields = typeof options.json === "string" ? options.json : void 0;
4554
- outputJson(docs, fields);
4555
- return;
4556
- }
4557
- if (docs.length === 0) {
4558
- console.log("No documents found.");
4559
- return;
4589
+ doc.command("list").description("List documents").option("-L, --limit <n>", "Maximum number of items to fetch", "30").option("--file-type <type>", "Filter by file type").option("--source-type <type>", "Filter by source type (file, web, api, topic)").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(
4590
+ async (options) => {
4591
+ const client = await getTrpcClient();
4592
+ const pageSize = Number.parseInt(options.limit || "30", 10);
4593
+ const query = {
4594
+ pageSize
4595
+ };
4596
+ if (options.fileType) query.fileTypes = [options.fileType];
4597
+ if (options.sourceType) query.sourceTypes = [options.sourceType];
4598
+ const result = await client.document.queryDocuments.query(query);
4599
+ const docs = Array.isArray(result) ? result : result.items ?? [];
4600
+ if (options.json !== void 0) {
4601
+ const fields = typeof options.json === "string" ? options.json : void 0;
4602
+ outputJson(docs, fields);
4603
+ return;
4604
+ }
4605
+ if (docs.length === 0) {
4606
+ console.log("No documents found.");
4607
+ return;
4608
+ }
4609
+ const rows = docs.map((d) => [
4610
+ d.id,
4611
+ truncate(d.title || d.filename || "Untitled", 120),
4612
+ d.fileType || "",
4613
+ d.updatedAt ? timeAgo(d.updatedAt) : ""
4614
+ ]);
4615
+ printTable(rows, ["ID", "TITLE", "TYPE", "UPDATED"]);
4560
4616
  }
4561
- const rows = docs.map((d) => [
4562
- d.id,
4563
- truncate(d.title || d.filename || "Untitled", 120),
4564
- d.fileType || "",
4565
- d.updatedAt ? timeAgo(d.updatedAt) : ""
4566
- ]);
4567
- printTable(rows, ["ID", "TITLE", "TYPE", "UPDATED"]);
4568
- });
4617
+ );
4569
4618
  doc.command("view <id>").description("View a document").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (id, options) => {
4570
4619
  const client = await getTrpcClient();
4571
4620
  const document = await client.document.getDocumentById.query({ id });
@@ -4582,6 +4631,7 @@ function registerDocCommand(program2) {
4582
4631
  console.log(pc6.bold(document.title || "Untitled"));
4583
4632
  const meta = [];
4584
4633
  if (document.fileType) meta.push(document.fileType);
4634
+ if (document.knowledgeBaseId) meta.push(`KB: ${document.knowledgeBaseId}`);
4585
4635
  if (document.updatedAt) meta.push(`Updated ${timeAgo(document.updatedAt)}`);
4586
4636
  if (meta.length > 0) console.log(pc6.dim(meta.join(" \xB7 ")));
4587
4637
  console.log();
@@ -4591,13 +4641,15 @@ function registerDocCommand(program2) {
4591
4641
  console.log(pc6.dim("(no content)"));
4592
4642
  }
4593
4643
  });
4594
- doc.command("create").description("Create a new document").requiredOption("-t, --title <title>", "Document title").option("-b, --body <content>", "Document content").option("-F, --body-file <path>", "Read content from file").option("--parent <id>", "Parent document or folder ID").option("--slug <slug>", "Custom slug").action(
4644
+ doc.command("create").description("Create a new document").requiredOption("-t, --title <title>", "Document title").option("-b, --body <content>", "Document content").option("-F, --body-file <path>", "Read content from file").option("--parent <id>", "Parent document or folder ID").option("--slug <slug>", "Custom slug").option("--kb <id>", "Knowledge base ID to associate with").option("--file-type <type>", "File type (e.g. custom/document, custom/folder)").action(
4595
4645
  async (options) => {
4596
4646
  const content = readBodyContent(options);
4597
4647
  const client = await getTrpcClient();
4598
4648
  const result = await client.document.createDocument.mutate({
4599
4649
  content,
4600
4650
  editorData: JSON.stringify({ content: content || "", type: "doc" }),
4651
+ fileType: options.fileType,
4652
+ knowledgeBaseId: options.kb,
4601
4653
  parentId: options.parent,
4602
4654
  slug: options.slug,
4603
4655
  title: options.title
@@ -4605,11 +4657,50 @@ function registerDocCommand(program2) {
4605
4657
  console.log(`${pc6.green("\u2713")} Created document ${pc6.bold(result.id)}`);
4606
4658
  }
4607
4659
  );
4608
- doc.command("edit <id>").description("Edit a document").option("-t, --title <title>", "New title").option("-b, --body <content>", "New content").option("-F, --body-file <path>", "Read new content from file").option("--parent <id>", "Move to parent document (empty string for root)").action(
4660
+ doc.command("batch-create <file>").description("Batch create documents from a JSON file").action(async (file) => {
4661
+ if (!fs5.existsSync(file)) {
4662
+ log.error(`File not found: ${file}`);
4663
+ process.exit(1);
4664
+ return;
4665
+ }
4666
+ let documents;
4667
+ try {
4668
+ const raw = fs5.readFileSync(file, "utf8");
4669
+ documents = JSON.parse(raw);
4670
+ } catch {
4671
+ log.error("Failed to parse JSON file. Expected an array of document objects.");
4672
+ process.exit(1);
4673
+ return;
4674
+ }
4675
+ if (!Array.isArray(documents) || documents.length === 0) {
4676
+ log.error("JSON file must contain a non-empty array of document objects.");
4677
+ process.exit(1);
4678
+ return;
4679
+ }
4680
+ const client = await getTrpcClient();
4681
+ const items = documents.map((d) => ({
4682
+ content: d.content,
4683
+ editorData: JSON.stringify({ content: d.content || "", type: "doc" }),
4684
+ fileType: d.fileType,
4685
+ knowledgeBaseId: d.knowledgeBaseId,
4686
+ parentId: d.parentId,
4687
+ slug: d.slug,
4688
+ title: d.title
4689
+ }));
4690
+ const result = await client.document.createDocuments.mutate({ documents: items });
4691
+ const created = Array.isArray(result) ? result : [result];
4692
+ console.log(`${pc6.green("\u2713")} Created ${created.length} document(s)`);
4693
+ for (const doc2 of created) {
4694
+ console.log(` ${pc6.dim("\u2022")} ${doc2.id} \u2014 ${doc2.title || "Untitled"}`);
4695
+ }
4696
+ });
4697
+ doc.command("edit <id>").description("Edit a document").option("-t, --title <title>", "New title").option("-b, --body <content>", "New content").option("-F, --body-file <path>", "Read new content from file").option("--parent <id>", "Move to parent document (empty string for root)").option("--file-type <type>", "Change file type").action(
4609
4698
  async (id, options) => {
4610
4699
  const content = readBodyContent(options);
4611
- if (!options.title && !content && options.parent === void 0) {
4612
- log.error("No changes specified. Use --title, --body, --body-file, or --parent.");
4700
+ if (!options.title && !content && options.parent === void 0 && !options.fileType) {
4701
+ log.error(
4702
+ "No changes specified. Use --title, --body, --body-file, --parent, or --file-type."
4703
+ );
4613
4704
  process.exit(1);
4614
4705
  }
4615
4706
  const client = await getTrpcClient();
@@ -4622,6 +4713,7 @@ function registerDocCommand(program2) {
4622
4713
  if (options.parent !== void 0) {
4623
4714
  params.parentId = options.parent || null;
4624
4715
  }
4716
+ if (options.fileType) params.fileType = options.fileType;
4625
4717
  await client.document.updateDocument.mutate(params);
4626
4718
  console.log(`${pc6.green("\u2713")} Updated document ${pc6.bold(id)}`);
4627
4719
  }
@@ -4644,6 +4736,62 @@ function registerDocCommand(program2) {
4644
4736
  }
4645
4737
  console.log(`${pc6.green("\u2713")} Deleted ${ids.length} document(s)`);
4646
4738
  });
4739
+ doc.command("parse <fileId>").description("Parse an uploaded file into a document").option("--with-pages", "Preserve page structure").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (fileId, options) => {
4740
+ const client = await getTrpcClient();
4741
+ const result = options.withPages ? await client.document.parseFileContent.mutate({ id: fileId }) : await client.document.parseDocument.mutate({ id: fileId });
4742
+ if (options.json !== void 0) {
4743
+ const fields = typeof options.json === "string" ? options.json : void 0;
4744
+ outputJson(result, fields);
4745
+ return;
4746
+ }
4747
+ console.log(`${pc6.green("\u2713")} Parsed file ${pc6.bold(fileId)}`);
4748
+ if (result.title) console.log(` Title: ${result.title}`);
4749
+ if (result.content) {
4750
+ const preview = truncate(result.content, 200);
4751
+ console.log(` Content: ${pc6.dim(preview)}`);
4752
+ }
4753
+ });
4754
+ doc.command("link-topic <docId> <topicId>").description("Associate a document with a topic").action(async (docId, topicId) => {
4755
+ const client = await getTrpcClient();
4756
+ const document = await client.document.getDocumentById.query({ id: docId });
4757
+ if (!document) {
4758
+ log.error(`Document not found: ${docId}`);
4759
+ process.exit(1);
4760
+ return;
4761
+ }
4762
+ const result = await client.notebook.createDocument.mutate({
4763
+ content: document.content || "",
4764
+ description: document.description || "",
4765
+ title: document.title || "Untitled",
4766
+ topicId
4767
+ });
4768
+ console.log(
4769
+ `${pc6.green("\u2713")} Linked document ${pc6.bold(result.id)} to topic ${pc6.bold(topicId)}`
4770
+ );
4771
+ });
4772
+ doc.command("topic-docs <topicId>").description("List documents associated with a topic").option("--type <type>", "Filter by document type (article, markdown, note, report)").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (topicId, options) => {
4773
+ const client = await getTrpcClient();
4774
+ const query = { topicId };
4775
+ if (options.type) query.type = options.type;
4776
+ const result = await client.notebook.listDocuments.query(query);
4777
+ const docs = Array.isArray(result) ? result : result.data ?? [];
4778
+ if (options.json !== void 0) {
4779
+ const fields = typeof options.json === "string" ? options.json : void 0;
4780
+ outputJson(docs, fields);
4781
+ return;
4782
+ }
4783
+ if (docs.length === 0) {
4784
+ console.log("No documents found for this topic.");
4785
+ return;
4786
+ }
4787
+ const rows = docs.map((d) => [
4788
+ d.id,
4789
+ truncate(d.title || "Untitled", 120),
4790
+ d.fileType || "",
4791
+ d.updatedAt ? timeAgo(d.updatedAt) : ""
4792
+ ]);
4793
+ printTable(rows, ["ID", "TITLE", "TYPE", "UPDATED"]);
4794
+ });
4647
4795
  }
4648
4796
 
4649
4797
  // src/commands/file.ts
@@ -4867,6 +5015,9 @@ ${stdinContent}`;
4867
5015
  const payload = {
4868
5016
  messages,
4869
5017
  model,
5018
+ // For non-streaming, use responseMode 'json' to get a plain JSON response
5019
+ // instead of SSE (the backend converts non-stream to SSE by default)
5020
+ responseMode: useStream ? "stream" : "json",
4870
5021
  stream: useStream
4871
5022
  };
4872
5023
  if (options.temperature) payload.temperature = Number.parseFloat(options.temperature);
@@ -4888,7 +5039,7 @@ ${stdinContent}`;
4888
5039
  if (options.json) {
4889
5040
  console.log(JSON.stringify(body, null, 2));
4890
5041
  } else {
4891
- const content = body.choices?.[0]?.message?.content || JSON.stringify(body);
5042
+ const content = body.choices?.[0]?.message?.content || body.content?.[0]?.text || JSON.stringify(body);
4892
5043
  process.stdout.write(content);
4893
5044
  process.stdout.write("\n");
4894
5045
  }
@@ -4925,9 +5076,10 @@ async function streamSSEResponse(body, json) {
4925
5076
  const parsed = JSON.parse(data);
4926
5077
  if (json) {
4927
5078
  console.log(JSON.stringify(parsed));
4928
- } else {
4929
- const content = parsed.choices?.[0]?.delta?.content;
4930
- if (content) process.stdout.write(content);
5079
+ } else if (typeof parsed === "string" && parsed !== "stop") {
5080
+ process.stdout.write(parsed);
5081
+ } else if (parsed?.choices?.[0]?.delta?.content) {
5082
+ process.stdout.write(parsed.choices[0].delta.content);
4931
5083
  }
4932
5084
  } catch {
4933
5085
  if (!json) process.stdout.write(data);
@@ -5008,12 +5160,19 @@ function registerVideoCommand(parent) {
5008
5160
  }
5009
5161
  const data = r.data || r;
5010
5162
  console.log(`${pc10.green("\u2713")} Video generation started`);
5011
- if (data.generationId) {
5012
- console.log(` Generation ID: ${pc10.bold(data.generationId)}`);
5163
+ if (data.batch?.id) console.log(` Batch ID: ${pc10.bold(data.batch.id)}`);
5164
+ const generations = data.generations || [];
5165
+ if (generations.length > 0) {
5166
+ for (const gen of generations) {
5167
+ if (gen.asyncTaskId) {
5168
+ console.log(` Generation ${pc10.bold(gen.id)} \u2192 Task ${pc10.dim(gen.asyncTaskId)}`);
5169
+ }
5170
+ }
5171
+ console.log();
5172
+ console.log(
5173
+ pc10.dim('Use "lh generate status <generationId> <taskId>" to check progress.')
5174
+ );
5013
5175
  }
5014
- console.log(
5015
- pc10.dim("Video generation runs asynchronously. Check status or wait for notification.")
5016
- );
5017
5176
  }
5018
5177
  );
5019
5178
  }
@@ -5048,6 +5207,63 @@ function registerGenerateCommand(program2) {
5048
5207
  if (gen.asset?.thumbnailUrl) console.log(` Thumb: ${gen.asset.thumbnailUrl}`);
5049
5208
  }
5050
5209
  });
5210
+ generate.command("download <generationId> <taskId>").description("Wait for generation to complete and download the result").option("-o, --output <path>", "Output file path (default: auto-detect from asset)").option("--interval <sec>", "Polling interval in seconds", "5").option("--timeout <sec>", "Timeout in seconds (0 = no timeout)", "300").action(
5211
+ async (generationId, taskId, options) => {
5212
+ const client = await getTrpcClient();
5213
+ const interval = Number.parseInt(options.interval || "5", 10) * 1e3;
5214
+ const timeout = Number.parseInt(options.timeout || "300", 10) * 1e3;
5215
+ const startTime = Date.now();
5216
+ console.log(`${pc11.yellow("\u22EF")} Waiting for generation ${pc11.bold(generationId)}...`);
5217
+ while (true) {
5218
+ const result = await client.generation.getGenerationStatus.query({
5219
+ asyncTaskId: taskId,
5220
+ generationId
5221
+ });
5222
+ if (result.status === "success" && result.generation) {
5223
+ const gen = result.generation;
5224
+ const url = gen.asset?.url;
5225
+ if (!url) {
5226
+ console.log(`${pc11.red("\u2717")} Generation succeeded but no asset URL found.`);
5227
+ process.exit(1);
5228
+ }
5229
+ const ext = url.split("?")[0].split(".").pop() || "bin";
5230
+ const outputPath = options.output || `${generationId}.${ext}`;
5231
+ console.log(`${pc11.green("\u2713")} Generation complete. Downloading...`);
5232
+ const res = await fetch(url);
5233
+ if (!res.ok) {
5234
+ console.log(`${pc11.red("\u2717")} Download failed: ${res.status} ${res.statusText}`);
5235
+ process.exit(1);
5236
+ }
5237
+ const { writeFile: writeFile3 } = await import("fs/promises");
5238
+ const buffer = Buffer.from(await res.arrayBuffer());
5239
+ await writeFile3(outputPath, buffer);
5240
+ console.log(
5241
+ `${pc11.green("\u2713")} Saved to ${pc11.bold(outputPath)} (${(buffer.length / 1024).toFixed(1)} KB)`
5242
+ );
5243
+ if (gen.asset?.thumbnailUrl) {
5244
+ console.log(` Thumbnail: ${pc11.dim(gen.asset.thumbnailUrl)}`);
5245
+ }
5246
+ return;
5247
+ }
5248
+ if (result.status === "error") {
5249
+ const errMsg = result.error?.body?.detail || result.error?.message || JSON.stringify(result.error);
5250
+ console.log(`${pc11.red("\u2717")} Generation failed: ${errMsg}`);
5251
+ process.exit(1);
5252
+ }
5253
+ if (timeout > 0 && Date.now() - startTime > timeout) {
5254
+ console.log(
5255
+ `${pc11.red("\u2717")} Timed out after ${options.timeout}s. Task still ${result.status}.`
5256
+ );
5257
+ console.log(pc11.dim(`Run "lh gen status ${generationId} ${taskId}" to check later.`));
5258
+ process.exit(1);
5259
+ }
5260
+ process.stdout.write(
5261
+ `\r${pc11.yellow("\u22EF")} Status: ${colorStatus2(result.status)}... (${Math.round((Date.now() - startTime) / 1e3)}s)`
5262
+ );
5263
+ await new Promise((r) => setTimeout(r, interval));
5264
+ }
5265
+ }
5266
+ );
5051
5267
  generate.command("list").description("List generation topics").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
5052
5268
  const client = await getTrpcClient();
5053
5269
  const result = await client.generationTopic.getAllGenerationTopics.query();
@@ -5091,9 +5307,28 @@ function colorStatus2(status) {
5091
5307
  }
5092
5308
 
5093
5309
  // src/commands/kb.ts
5310
+ import crypto2 from "crypto";
5311
+ import fs6 from "fs";
5312
+ import path11 from "path";
5094
5313
  import pc12 from "picocolors";
5314
+ function formatFileType(fileType) {
5315
+ if (!fileType) return "";
5316
+ const map2 = {
5317
+ "application/msword": "doc",
5318
+ "application/pdf": "pdf",
5319
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
5320
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
5321
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
5322
+ "custom/folder": "folder",
5323
+ "text/markdown": "md",
5324
+ "text/plain": "txt"
5325
+ };
5326
+ if (map2[fileType]) return map2[fileType];
5327
+ const parts = fileType.split("/");
5328
+ return parts.length > 1 ? parts[1] : fileType;
5329
+ }
5095
5330
  function registerKbCommand(program2) {
5096
- const kb = program2.command("kb").description("Manage knowledge bases");
5331
+ const kb = program2.command("kb").description("Manage knowledge bases, folders, documents, and files");
5097
5332
  kb.command("list").description("List knowledge bases").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
5098
5333
  const client = await getTrpcClient();
5099
5334
  const result = await client.knowledgeBase.getKnowledgeBases.query();
@@ -5123,9 +5358,33 @@ function registerKbCommand(program2) {
5123
5358
  process.exit(1);
5124
5359
  return;
5125
5360
  }
5361
+ const allItems = [];
5362
+ async function fetchItems(parentId, depth = 0) {
5363
+ const PAGE_SIZE = 100;
5364
+ let offset = 0;
5365
+ let hasMore = true;
5366
+ while (hasMore) {
5367
+ const query = { knowledgeBaseId: id, limit: PAGE_SIZE, offset, parentId };
5368
+ const result2 = await client.file.getKnowledgeItems.query(query);
5369
+ const list = Array.isArray(result2) ? result2 : result2.items ?? [];
5370
+ hasMore = Array.isArray(result2) ? false : result2.hasMore ?? false;
5371
+ offset += list.length;
5372
+ const folders = [];
5373
+ for (const item of list) {
5374
+ allItems.push({ ...item, _depth: depth });
5375
+ if (item.fileType === "custom/folder") {
5376
+ folders.push(item);
5377
+ }
5378
+ }
5379
+ if (folders.length > 0) {
5380
+ await Promise.all(folders.map((f) => fetchItems(f.id, depth + 1)));
5381
+ }
5382
+ }
5383
+ }
5384
+ await fetchItems(null);
5126
5385
  if (options.json !== void 0) {
5127
5386
  const fields = typeof options.json === "string" ? options.json : void 0;
5128
- outputJson(result, fields);
5387
+ outputJson({ ...result, files: allItems }, fields);
5129
5388
  return;
5130
5389
  }
5131
5390
  console.log(pc12.bold(result.name || "Untitled"));
@@ -5133,18 +5392,23 @@ function registerKbCommand(program2) {
5133
5392
  if (result.description) meta.push(result.description);
5134
5393
  if (result.updatedAt) meta.push(`Updated ${timeAgo(result.updatedAt)}`);
5135
5394
  if (meta.length > 0) console.log(pc12.dim(meta.join(" \xB7 ")));
5136
- if (result.files && Array.isArray(result.files)) {
5137
- const files = result.files;
5138
- if (files.length > 0) {
5139
- console.log();
5140
- console.log(pc12.bold(`Files (${files.length}):`));
5141
- const rows = files.map((f) => [
5395
+ if (allItems.length > 0) {
5396
+ console.log();
5397
+ console.log(pc12.bold(`Items (${allItems.length}):`));
5398
+ const rows = allItems.map((f) => {
5399
+ const indent = " ".repeat(f._depth);
5400
+ const name = f.name || f.filename || "";
5401
+ return [
5142
5402
  f.id,
5143
- truncate(f.name || f.filename || "", 50),
5144
- f.fileType || ""
5145
- ]);
5146
- printTable(rows, ["ID", "NAME", "TYPE"]);
5147
- }
5403
+ f.sourceType === "document" ? "Doc" : "File",
5404
+ truncate(`${indent}${name}`, 45),
5405
+ formatFileType(f.fileType || ""),
5406
+ f.size ? `${Math.round(f.size / 1024)}KB` : ""
5407
+ ];
5408
+ });
5409
+ printTable(rows, ["ID", "SOURCE", "NAME", "TYPE", "SIZE"]);
5410
+ } else {
5411
+ console.log(pc12.dim("\nNo files in this knowledge base."));
5148
5412
  }
5149
5413
  });
5150
5414
  kb.command("create").description("Create a knowledge base").requiredOption("-n, --name <name>", "Knowledge base name").option("-d, --description <desc>", "Description").option("--avatar <url>", "Avatar URL").action(async (options) => {
@@ -5154,8 +5418,8 @@ function registerKbCommand(program2) {
5154
5418
  };
5155
5419
  if (options.description) input.description = options.description;
5156
5420
  if (options.avatar) input.avatar = options.avatar;
5157
- const result = await client.knowledgeBase.createKnowledgeBase.mutate(input);
5158
- console.log(`${pc12.green("\u2713")} Created knowledge base ${pc12.bold(result.id)}`);
5421
+ const id = await client.knowledgeBase.createKnowledgeBase.mutate(input);
5422
+ console.log(`${pc12.green("\u2713")} Created knowledge base ${pc12.bold(String(id))}`);
5159
5423
  });
5160
5424
  kb.command("edit <id>").description("Update a knowledge base").option("-n, --name <name>", "New name").option("-d, --description <desc>", "New description").option("--avatar <url>", "New avatar URL").action(
5161
5425
  async (id, options) => {
@@ -5216,12 +5480,285 @@ function registerKbCommand(program2) {
5216
5480
  `${pc12.green("\u2713")} Removed ${options.ids.length} file(s) from knowledge base ${pc12.bold(knowledgeBaseId)}`
5217
5481
  );
5218
5482
  });
5483
+ kb.command("mkdir <knowledgeBaseId>").description("Create a folder in a knowledge base").requiredOption("-n, --name <name>", "Folder name").option("--parent <parentId>", "Parent folder ID").action(async (knowledgeBaseId, options) => {
5484
+ const client = await getTrpcClient();
5485
+ const result = await client.document.createDocument.mutate({
5486
+ editorData: JSON.stringify({}),
5487
+ fileType: "custom/folder",
5488
+ knowledgeBaseId,
5489
+ parentId: options.parent,
5490
+ title: options.name
5491
+ });
5492
+ console.log(`${pc12.green("\u2713")} Created folder ${pc12.bold(result.id)}`);
5493
+ });
5494
+ kb.command("create-doc <knowledgeBaseId>").description("Create a document in a knowledge base").requiredOption("-t, --title <title>", "Document title").option("-c, --content <content>", "Document content (text)").option("--parent <parentId>", "Parent folder ID").action(
5495
+ async (knowledgeBaseId, options) => {
5496
+ const client = await getTrpcClient();
5497
+ const result = await client.document.createDocument.mutate({
5498
+ content: options.content,
5499
+ editorData: JSON.stringify({}),
5500
+ fileType: "custom/document",
5501
+ knowledgeBaseId,
5502
+ parentId: options.parent,
5503
+ title: options.title
5504
+ });
5505
+ console.log(`${pc12.green("\u2713")} Created document ${pc12.bold(result.id)}`);
5506
+ }
5507
+ );
5508
+ kb.command("move <id>").description("Move a file or document to a different folder").option("--parent <parentId>", "Target folder ID (omit to move to root)").option("--type <type>", "Item type: file or doc", "file").action(async (id, options) => {
5509
+ const client = await getTrpcClient();
5510
+ const parentId = options.parent ?? null;
5511
+ if (options.type === "doc") {
5512
+ await client.document.updateDocument.mutate({ id, parentId });
5513
+ } else {
5514
+ await client.file.updateFile.mutate({ id, parentId });
5515
+ }
5516
+ const dest = parentId ? `folder ${pc12.bold(parentId)}` : "root";
5517
+ console.log(`${pc12.green("\u2713")} Moved ${pc12.bold(id)} to ${dest}`);
5518
+ });
5519
+ kb.command("upload <knowledgeBaseId> <filePath>").description("Upload a file to a knowledge base").option("--parent <parentId>", "Parent folder ID").action(async (knowledgeBaseId, filePath, options) => {
5520
+ const resolved = path11.resolve(filePath);
5521
+ if (!fs6.existsSync(resolved)) {
5522
+ log.error(`File not found: ${resolved}`);
5523
+ process.exit(1);
5524
+ }
5525
+ const stat4 = fs6.statSync(resolved);
5526
+ const fileName = path11.basename(resolved);
5527
+ const fileBuffer = fs6.readFileSync(resolved);
5528
+ const hash = crypto2.createHash("sha256").update(fileBuffer).digest("hex");
5529
+ const ext = path11.extname(fileName).toLowerCase().slice(1);
5530
+ const mimeMap = {
5531
+ csv: "text/csv",
5532
+ doc: "application/msword",
5533
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
5534
+ gif: "image/gif",
5535
+ jpeg: "image/jpeg",
5536
+ jpg: "image/jpeg",
5537
+ json: "application/json",
5538
+ md: "text/markdown",
5539
+ mp3: "audio/mpeg",
5540
+ mp4: "video/mp4",
5541
+ pdf: "application/pdf",
5542
+ png: "image/png",
5543
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
5544
+ svg: "image/svg+xml",
5545
+ txt: "text/plain",
5546
+ webp: "image/webp",
5547
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
5548
+ };
5549
+ const fileType = mimeMap[ext] || "application/octet-stream";
5550
+ const client = await getTrpcClient();
5551
+ const { serverUrl, headers } = await getAuthInfo();
5552
+ const date = (/* @__PURE__ */ new Date()).toLocaleDateString("en-CA");
5553
+ const pathname = `files/${date}/${hash}.${ext}`;
5554
+ const presigned = await client.upload.createS3PreSignedUrl.mutate({ pathname });
5555
+ const presignedUrl = typeof presigned === "string" ? presigned : presigned.url;
5556
+ const uploadRes = await fetch(presignedUrl, {
5557
+ body: fileBuffer,
5558
+ headers: { "Content-Type": fileType },
5559
+ method: "PUT"
5560
+ });
5561
+ if (!uploadRes.ok) {
5562
+ log.error(`Upload failed: ${uploadRes.status} ${uploadRes.statusText}`);
5563
+ process.exit(1);
5564
+ }
5565
+ const result = await client.file.createFile.mutate({
5566
+ fileType,
5567
+ hash,
5568
+ knowledgeBaseId,
5569
+ metadata: {
5570
+ date,
5571
+ dirname: "",
5572
+ filename: fileName,
5573
+ path: pathname
5574
+ },
5575
+ name: fileName,
5576
+ parentId: options.parent,
5577
+ size: stat4.size,
5578
+ url: pathname
5579
+ });
5580
+ console.log(
5581
+ `${pc12.green("\u2713")} Uploaded ${pc12.bold(fileName)} \u2192 ${pc12.bold(result.id)}`
5582
+ );
5583
+ });
5584
+ }
5585
+
5586
+ // src/commands/eval.ts
5587
+ import { InvalidArgumentError } from "commander";
5588
+ import pc13 from "picocolors";
5589
+ var JSON_VERSION = "v1";
5590
+ var printJson = (data) => {
5591
+ console.log(JSON.stringify(data, null, 2));
5592
+ };
5593
+ var outputJsonSuccess = (data) => {
5594
+ const payload = {
5595
+ data,
5596
+ error: null,
5597
+ ok: true,
5598
+ version: JSON_VERSION
5599
+ };
5600
+ printJson(payload);
5601
+ };
5602
+ var isRecord = (value) => typeof value === "object" && value !== null;
5603
+ var toJsonError = (error) => {
5604
+ if (error instanceof Error) {
5605
+ const maybeData = error.data;
5606
+ const code = maybeData?.code;
5607
+ return {
5608
+ code: typeof code === "string" ? code : void 0,
5609
+ message: error.message
5610
+ };
5611
+ }
5612
+ if (isRecord(error)) {
5613
+ const code = typeof error.code === "string" ? error.code : void 0;
5614
+ const message = typeof error.message === "string" ? error.message : "Unknown error";
5615
+ return { code, message };
5616
+ }
5617
+ return { message: String(error) };
5618
+ };
5619
+ var handleCommandError = (error, json) => {
5620
+ const normalized = toJsonError(error);
5621
+ if (json) {
5622
+ const payload = {
5623
+ data: null,
5624
+ error: normalized,
5625
+ ok: false,
5626
+ version: JSON_VERSION
5627
+ };
5628
+ printJson(payload);
5629
+ } else {
5630
+ log.error(normalized.message);
5631
+ }
5632
+ process.exit(1);
5633
+ };
5634
+ var parseScore = (value) => {
5635
+ const score = Number(value);
5636
+ if (!Number.isFinite(score)) {
5637
+ throw new InvalidArgumentError(`Invalid score: ${value}`);
5638
+ }
5639
+ return score;
5640
+ };
5641
+ var parseBoolean = (value) => {
5642
+ const normalized = value.trim().toLowerCase();
5643
+ if (["1", "true", "yes"].includes(normalized)) return true;
5644
+ if (["0", "false", "no"].includes(normalized)) return false;
5645
+ throw new InvalidArgumentError(`Invalid boolean value: ${value}`);
5646
+ };
5647
+ var parseResultJson = (value) => {
5648
+ let parsed;
5649
+ try {
5650
+ parsed = JSON.parse(value);
5651
+ } catch {
5652
+ throw new InvalidArgumentError("Invalid JSON value for --result-json");
5653
+ }
5654
+ if (!isRecord(parsed) || Array.isArray(parsed)) {
5655
+ throw new InvalidArgumentError("--result-json must be a JSON object");
5656
+ }
5657
+ return parsed;
5658
+ };
5659
+ var parseRunStatus = (value) => {
5660
+ if (value !== "completed" && value !== "external") {
5661
+ throw new InvalidArgumentError("Only 'completed' and 'external' are supported");
5662
+ }
5663
+ return value;
5664
+ };
5665
+ var executeCommand = async (options, action, successMessage) => {
5666
+ try {
5667
+ const data = await action();
5668
+ if (options.json) {
5669
+ outputJsonSuccess(data);
5670
+ return;
5671
+ }
5672
+ if (successMessage) {
5673
+ console.log(`${pc13.green("OK")} ${successMessage}`);
5674
+ return;
5675
+ }
5676
+ printJson(data);
5677
+ } catch (error) {
5678
+ handleCommandError(error, Boolean(options.json));
5679
+ }
5680
+ };
5681
+ function registerEvalCommand(program2) {
5682
+ const evalCmd = program2.command("eval").description("Manage external evaluation workflows");
5683
+ const runCmd = evalCmd.command("run").description("Manage evaluation runs");
5684
+ runCmd.command("get").description("Get run information").requiredOption("--run-id <id>", "Run ID").option("--json", "Output JSON envelope").action(
5685
+ async (options) => executeCommand(options, async () => {
5686
+ const client = await getTrpcClient();
5687
+ return client.agentEvalExternal.runGet.query({ runId: options.runId });
5688
+ })
5689
+ );
5690
+ runCmd.command("set-status").description("Set run status (external API supports completed or external)").requiredOption("--run-id <id>", "Run ID").requiredOption("--status <status>", "Status (completed | external)", parseRunStatus).option("--json", "Output JSON envelope").action(
5691
+ async (options) => executeCommand(
5692
+ options,
5693
+ async () => {
5694
+ const client = await getTrpcClient();
5695
+ return client.agentEvalExternal.runSetStatus.mutate({
5696
+ runId: options.runId,
5697
+ status: options.status
5698
+ });
5699
+ },
5700
+ `Run ${pc13.bold(options.runId)} status updated to ${pc13.bold(options.status)}`
5701
+ )
5702
+ );
5703
+ evalCmd.command("dataset").description("Manage evaluation datasets").command("get").description("Get dataset information").requiredOption("--dataset-id <id>", "Dataset ID").option("--json", "Output JSON envelope").action(
5704
+ async (options) => executeCommand(options, async () => {
5705
+ const client = await getTrpcClient();
5706
+ return client.agentEvalExternal.datasetGet.query({ datasetId: options.datasetId });
5707
+ })
5708
+ );
5709
+ evalCmd.command("run-topics").description("Manage run topics").command("list").description("List topics in a run").requiredOption("--run-id <id>", "Run ID").option("--only-external", "Only return topics pending external evaluation").option("--json", "Output JSON envelope").action(
5710
+ async (options) => executeCommand(options, async () => {
5711
+ const client = await getTrpcClient();
5712
+ return client.agentEvalExternal.runTopicsList.query({
5713
+ onlyExternal: Boolean(options.onlyExternal),
5714
+ runId: options.runId
5715
+ });
5716
+ })
5717
+ );
5718
+ evalCmd.command("threads").description("Manage evaluation threads").command("list").description("List threads by topic").requiredOption("--topic-id <id>", "Topic ID").option("--json", "Output JSON envelope").action(
5719
+ async (options) => executeCommand(options, async () => {
5720
+ const client = await getTrpcClient();
5721
+ return client.agentEvalExternal.threadsList.query({ topicId: options.topicId });
5722
+ })
5723
+ );
5724
+ evalCmd.command("messages").description("Manage evaluation messages").command("list").description("List messages by topic and optional thread").requiredOption("--topic-id <id>", "Topic ID").option("--thread-id <id>", "Thread ID").option("--json", "Output JSON envelope").action(
5725
+ async (options) => executeCommand(options, async () => {
5726
+ const client = await getTrpcClient();
5727
+ return client.agentEvalExternal.messagesList.query({
5728
+ threadId: options.threadId,
5729
+ topicId: options.topicId
5730
+ });
5731
+ })
5732
+ );
5733
+ evalCmd.command("test-cases").description("Manage evaluation test cases").command("count").description("Count test cases by dataset").requiredOption("--dataset-id <id>", "Dataset ID").option("--json", "Output JSON envelope").action(
5734
+ async (options) => executeCommand(options, async () => {
5735
+ const client = await getTrpcClient();
5736
+ return client.agentEvalExternal.testCasesCount.query({ datasetId: options.datasetId });
5737
+ })
5738
+ );
5739
+ evalCmd.command("run-topic").description("Manage evaluation run-topic reporting").command("report-result").description("Report one evaluation result for a run topic").requiredOption("--run-id <id>", "Run ID").requiredOption("--topic-id <id>", "Topic ID").option("--thread-id <id>", "Thread ID (required for k > 1)").requiredOption("--score <score>", "Evaluation score", parseScore).requiredOption("--correct <boolean>", "Whether the result is correct", parseBoolean).requiredOption("--result-json <json>", "Raw evaluation result JSON object", parseResultJson).option("--json", "Output JSON envelope").action(
5740
+ async (options) => executeCommand(
5741
+ options,
5742
+ async () => {
5743
+ const client = await getTrpcClient();
5744
+ return client.agentEvalExternal.runTopicReportResult.mutate({
5745
+ correct: options.correct,
5746
+ result: options.resultJson,
5747
+ runId: options.runId,
5748
+ score: options.score,
5749
+ threadId: options.threadId,
5750
+ topicId: options.topicId
5751
+ });
5752
+ },
5753
+ `Reported result for topic ${pc13.bold(options.topicId)}`
5754
+ )
5755
+ );
5219
5756
  }
5220
5757
 
5221
5758
  // src/commands/login.ts
5222
5759
  import { execFile } from "child_process";
5223
- import fs6 from "fs";
5224
- import path11 from "path";
5760
+ import fs7 from "fs";
5761
+ import path12 from "path";
5225
5762
  var CLIENT_ID2 = "lobehub-cli";
5226
5763
  var SCOPES = "openid profile email offline_access";
5227
5764
  async function parseJsonResponse(res, endpoint) {
@@ -5352,31 +5889,31 @@ function sleep2(ms) {
5352
5889
  function resolveCommandExecutable(cmd, platform = process.platform) {
5353
5890
  if (!cmd) return void 0;
5354
5891
  if (cmd.includes("/") || cmd.includes("\\")) {
5355
- return fs6.existsSync(cmd) ? cmd : void 0;
5892
+ return fs7.existsSync(cmd) ? cmd : void 0;
5356
5893
  }
5357
5894
  const pathValue = process.env.PATH || "";
5358
5895
  if (!pathValue) return void 0;
5359
5896
  if (platform === "win32") {
5360
5897
  const pathEntries2 = pathValue.split(";").filter(Boolean);
5361
5898
  const pathext = (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
5362
- const hasExtension = path11.win32.extname(cmd).length > 0;
5899
+ const hasExtension = path12.win32.extname(cmd).length > 0;
5363
5900
  const candidateNames = hasExtension ? [cmd] : [cmd, ...pathext.map((ext) => `${cmd}${ext}`)];
5364
5901
  const systemRoot = process.env.SystemRoot || process.env.WINDIR;
5365
5902
  if (systemRoot) {
5366
- pathEntries2.push(path11.win32.join(systemRoot, "System32"));
5903
+ pathEntries2.push(path12.win32.join(systemRoot, "System32"));
5367
5904
  }
5368
5905
  for (const entry of pathEntries2) {
5369
5906
  for (const candidate of candidateNames) {
5370
- const resolved = path11.win32.join(entry, candidate);
5371
- if (fs6.existsSync(resolved)) return resolved;
5907
+ const resolved = path12.win32.join(entry, candidate);
5908
+ if (fs7.existsSync(resolved)) return resolved;
5372
5909
  }
5373
5910
  }
5374
5911
  return void 0;
5375
5912
  }
5376
- const pathEntries = pathValue.split(path11.delimiter).filter(Boolean);
5913
+ const pathEntries = pathValue.split(path12.delimiter).filter(Boolean);
5377
5914
  for (const entry of pathEntries) {
5378
- const resolved = path11.join(entry, cmd);
5379
- if (fs6.existsSync(resolved)) return resolved;
5915
+ const resolved = path12.join(entry, cmd);
5916
+ if (fs7.existsSync(resolved)) return resolved;
5380
5917
  }
5381
5918
  return void 0;
5382
5919
  }
@@ -5422,7 +5959,7 @@ function registerLogoutCommand(program2) {
5422
5959
  }
5423
5960
 
5424
5961
  // src/commands/memory.ts
5425
- import pc13 from "picocolors";
5962
+ import pc14 from "picocolors";
5426
5963
  var CATEGORIES = ["identity", "activity", "context", "experience", "preference"];
5427
5964
  function capitalize(s) {
5428
5965
  return s.charAt(0).toUpperCase() + s.slice(1);
@@ -5438,18 +5975,8 @@ function registerMemoryCommand(program2) {
5438
5975
  const categoriesToFetch = category ? [category] : [...CATEGORIES];
5439
5976
  const allResults = {};
5440
5977
  for (const cat of categoriesToFetch) {
5441
- const getter = `get${capitalize(cat)}`;
5442
- const getterPlural = `${getter}s`;
5443
- const router = client.userMemory;
5444
5978
  try {
5445
- if (router[getterPlural]) {
5446
- allResults[cat] = await router[getterPlural].query();
5447
- } else if (router[getter]) {
5448
- allResults[cat] = await router[getter].query();
5449
- } else {
5450
- const items = await fetchCategory(client, cat);
5451
- allResults[cat] = items;
5452
- }
5979
+ allResults[cat] = await fetchCategory(client, cat);
5453
5980
  } catch {
5454
5981
  allResults[cat] = [];
5455
5982
  }
@@ -5465,7 +5992,7 @@ function registerMemoryCommand(program2) {
5465
5992
  continue;
5466
5993
  }
5467
5994
  console.log();
5468
- console.log(pc13.bold(pc13.cyan(`\u2500\u2500 ${capitalize(cat)} (${items.length}) \u2500\u2500`)));
5995
+ console.log(pc14.bold(pc14.cyan(`\u2500\u2500 ${capitalize(cat)} (${items.length}) \u2500\u2500`)));
5469
5996
  const rows = items.map((item) => {
5470
5997
  const desc = item.description || item.narrative || item.title || item.situation || item.conclusionDirectives || item.content || "";
5471
5998
  return [
@@ -5487,8 +6014,11 @@ function registerMemoryCommand(program2) {
5487
6014
  if (options.labels) input.extractedLabels = options.labels;
5488
6015
  try {
5489
6016
  const result = await client.userMemory.createIdentity.mutate(input);
5490
- const id = result?.id || "unknown";
5491
- console.log(`${pc13.green("\u2713")} Created identity memory ${pc13.bold(id)}`);
6017
+ const memoryId = result?.userMemoryId || "unknown";
6018
+ const identityId = result?.identityId || "unknown";
6019
+ console.log(
6020
+ `${pc14.green("\u2713")} Created identity memory ${pc14.bold(memoryId)} (identity: ${pc14.bold(identityId)})`
6021
+ );
5492
6022
  } catch (error) {
5493
6023
  log.error(`Failed to create identity: ${error.message}`);
5494
6024
  process.exit(1);
@@ -5506,7 +6036,7 @@ function registerMemoryCommand(program2) {
5506
6036
  const data = buildCategoryInput(category, options);
5507
6037
  try {
5508
6038
  await router[mutationName].mutate({ data, id });
5509
- console.log(`${pc13.green("\u2713")} Updated ${category} memory ${pc13.bold(id)}`);
6039
+ console.log(`${pc14.green("\u2713")} Updated ${category} memory ${pc14.bold(id)}`);
5510
6040
  } catch (error) {
5511
6041
  log.error(`Failed to update ${category}: ${error.message}`);
5512
6042
  process.exit(1);
@@ -5530,7 +6060,7 @@ function registerMemoryCommand(program2) {
5530
6060
  const mutationName = `delete${capitalize(category)}`;
5531
6061
  try {
5532
6062
  await router[mutationName].mutate({ id });
5533
- console.log(`${pc13.green("\u2713")} Deleted ${category} memory ${pc13.bold(id)}`);
6063
+ console.log(`${pc14.green("\u2713")} Deleted ${category} memory ${pc14.bold(id)}`);
5534
6064
  } catch (error) {
5535
6065
  log.error(`Failed to delete ${category}: ${error.message}`);
5536
6066
  process.exit(1);
@@ -5549,7 +6079,7 @@ function registerMemoryCommand(program2) {
5549
6079
  console.log("No persona data available.");
5550
6080
  return;
5551
6081
  }
5552
- console.log(pc13.bold("User Persona"));
6082
+ console.log(pc14.bold("User Persona"));
5553
6083
  console.log();
5554
6084
  console.log(typeof persona === "string" ? persona : JSON.stringify(persona, null, 2));
5555
6085
  });
@@ -5559,11 +6089,11 @@ function registerMemoryCommand(program2) {
5559
6089
  if (options.from) input.fromDate = new Date(options.from);
5560
6090
  if (options.to) input.toDate = new Date(options.to);
5561
6091
  const result = await client.userMemory.requestMemoryFromChatTopic.mutate(input);
5562
- console.log(`${pc13.green("\u2713")} Memory extraction started`);
6092
+ console.log(`${pc14.green("\u2713")} Memory extraction started`);
5563
6093
  if (result?.id) {
5564
- console.log(`Task ID: ${pc13.bold(result.id)}`);
6094
+ console.log(`Task ID: ${pc14.bold(result.id)}`);
5565
6095
  }
5566
- console.log(pc13.dim('Use "lh memory extract-status" to check progress.'));
6096
+ console.log(pc14.dim('Use "lh memory extract-status" to check progress.'));
5567
6097
  });
5568
6098
  memory.command("extract-status").description("Check memory extraction task status").option("--task-id <id>", "Specific task ID to check").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
5569
6099
  const client = await getTrpcClient();
@@ -5580,7 +6110,7 @@ function registerMemoryCommand(program2) {
5580
6110
  return;
5581
6111
  }
5582
6112
  const r = result;
5583
- console.log(pc13.bold("Memory Extraction Task"));
6113
+ console.log(pc14.bold("Memory Extraction Task"));
5584
6114
  if (r.id) console.log(` ID: ${r.id}`);
5585
6115
  if (r.status) console.log(` Status: ${r.status}`);
5586
6116
  if (r.metadata) console.log(` Detail: ${JSON.stringify(r.metadata)}`);
@@ -5648,20 +6178,34 @@ function buildCategoryInput(category, options) {
5648
6178
  }
5649
6179
 
5650
6180
  // src/commands/message.ts
5651
- import pc14 from "picocolors";
6181
+ import pc15 from "picocolors";
5652
6182
  function registerMessageCommand(program2) {
5653
6183
  const message = program2.command("message").description("Manage messages");
5654
- message.command("list").description("List messages").option("--topic-id <id>", "Filter by topic ID").option("--agent-id <id>", "Filter by agent ID").option("--session-id <id>", "Filter by session ID").option("-L, --limit <n>", "Page size", "30").option("--page <n>", "Page number", "1").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(
6184
+ message.command("list").description("List messages").option("--topic-id <id>", "Filter by topic ID").option("--agent-id <id>", "Filter by agent ID").option("-L, --limit <n>", "Page size", "30").option("--page <n>", "Page number", "1").option("--user", "Only show user messages").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(
5655
6185
  async (options) => {
5656
6186
  const client = await getTrpcClient();
5657
- const input = {};
5658
- if (options.topicId) input.topicId = options.topicId;
5659
- if (options.agentId) input.agentId = options.agentId;
5660
- if (options.sessionId) input.sessionId = options.sessionId;
5661
- if (options.limit) input.pageSize = Number.parseInt(options.limit, 10);
5662
- if (options.page) input.current = Number.parseInt(options.page, 10);
5663
- const result = await client.message.getMessages.query(input);
5664
- const items = Array.isArray(result) ? result : result.items ?? [];
6187
+ const hasFilter = options.topicId || options.agentId;
6188
+ const pageSize = options.limit ? Number.parseInt(options.limit, 10) : void 0;
6189
+ const current = options.page ? Number.parseInt(options.page, 10) : void 0;
6190
+ let items;
6191
+ if (hasFilter) {
6192
+ const input = {};
6193
+ if (options.topicId) input.topicId = options.topicId;
6194
+ if (options.agentId) input.agentId = options.agentId;
6195
+ if (pageSize) input.pageSize = pageSize;
6196
+ if (current) input.current = current;
6197
+ const result = await client.message.getMessages.query(input);
6198
+ items = Array.isArray(result) ? result : result.items ?? [];
6199
+ } else {
6200
+ const input = {};
6201
+ if (pageSize) input.pageSize = pageSize;
6202
+ if (current) input.current = current;
6203
+ const result = await client.message.listAll.query(input);
6204
+ items = Array.isArray(result) ? result : [];
6205
+ }
6206
+ if (options.user) {
6207
+ items = items.filter((m) => m.role === "user");
6208
+ }
5665
6209
  if (options.json !== void 0) {
5666
6210
  const fields = typeof options.json === "string" ? options.json : void 0;
5667
6211
  outputJson(items, fields);
@@ -5712,7 +6256,7 @@ function registerMessageCommand(program2) {
5712
6256
  } else {
5713
6257
  await client.message.removeMessages.mutate({ ids });
5714
6258
  }
5715
- console.log(`${pc14.green("\u2713")} Deleted ${ids.length} message(s)`);
6259
+ console.log(`${pc15.green("\u2713")} Deleted ${ids.length} message(s)`);
5716
6260
  });
5717
6261
  message.command("count").description("Count messages").option("--start <date>", "Start date (ISO format)").option("--end <date>", "End date (ISO format)").option("--json", "Output JSON").action(async (options) => {
5718
6262
  const client = await getTrpcClient();
@@ -5724,7 +6268,7 @@ function registerMessageCommand(program2) {
5724
6268
  console.log(JSON.stringify({ count }));
5725
6269
  return;
5726
6270
  }
5727
- console.log(`Messages: ${pc14.bold(String(count))}`);
6271
+ console.log(`Messages: ${pc15.bold(String(count))}`);
5728
6272
  });
5729
6273
  message.command("heatmap").description("Get message activity heatmap").option("--json", "Output JSON").action(async (options) => {
5730
6274
  const client = await getTrpcClient();
@@ -5740,23 +6284,30 @@ function registerMessageCommand(program2) {
5740
6284
  const items = Array.isArray(result) ? result : [result];
5741
6285
  for (const entry of items) {
5742
6286
  const e = entry;
5743
- console.log(`${e.date || e.day || ""}: ${pc14.bold(String(e.count || e.value || 0))}`);
6287
+ console.log(`${e.date || e.day || ""}: ${pc15.bold(String(e.count || e.value || 0))}`);
5744
6288
  }
5745
6289
  });
5746
6290
  }
5747
6291
 
5748
6292
  // src/commands/model.ts
5749
- import pc15 from "picocolors";
6293
+ import pc16 from "picocolors";
5750
6294
  function registerModelCommand(program2) {
5751
6295
  const model = program2.command("model").description("Manage AI models");
5752
- model.command("list <providerId>").description("List models for a provider").option("-L, --limit <n>", "Maximum number of items", "50").option("--enabled", "Only show enabled models").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(
6296
+ model.command("list <providerId>").description("List models for a provider").option("-L, --limit <n>", "Maximum number of items", "50").option("--enabled", "Only show enabled models").option(
6297
+ "--type <type>",
6298
+ "Filter by model type (chat|embedding|tts|stt|image|video|text2music|realtime)"
6299
+ ).option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(
5753
6300
  async (providerId, options) => {
5754
6301
  const client = await getTrpcClient();
5755
6302
  const input = { id: providerId };
5756
6303
  if (options.limit) input.limit = Number.parseInt(options.limit, 10);
5757
6304
  if (options.enabled) input.enabled = true;
6305
+ if (options.type) input.type = options.type;
5758
6306
  const result = await client.aiModel.getAiProviderModelList.query(input);
5759
- const items = Array.isArray(result) ? result : result.items ?? [];
6307
+ let items = Array.isArray(result) ? result : result.items ?? [];
6308
+ if (options.type) {
6309
+ items = items.filter((m) => m.type === options.type);
6310
+ }
5760
6311
  if (options.json !== void 0) {
5761
6312
  const fields = typeof options.json === "string" ? options.json : void 0;
5762
6313
  outputJson(items, fields);
@@ -5769,7 +6320,7 @@ function registerModelCommand(program2) {
5769
6320
  const rows = items.map((m) => [
5770
6321
  m.id || "",
5771
6322
  truncate(m.displayName || m.id || "", 40),
5772
- m.enabled ? pc15.green("\u2713") : pc15.dim("\u2717"),
6323
+ m.enabled ? pc16.green("\u2713") : pc16.dim("\u2717"),
5773
6324
  m.type || ""
5774
6325
  ]);
5775
6326
  printTable(rows, ["ID", "NAME", "ENABLED", "TYPE"]);
@@ -5789,13 +6340,48 @@ function registerModelCommand(program2) {
5789
6340
  return;
5790
6341
  }
5791
6342
  const r = result;
5792
- console.log(pc15.bold(r.displayName || r.id || "Unknown"));
6343
+ console.log(pc16.bold(r.displayName || r.id || "Unknown"));
5793
6344
  const meta = [];
5794
6345
  if (r.providerId) meta.push(`Provider: ${r.providerId}`);
5795
6346
  if (r.type) meta.push(`Type: ${r.type}`);
5796
6347
  if (r.enabled !== void 0) meta.push(r.enabled ? "Enabled" : "Disabled");
5797
- if (meta.length > 0) console.log(pc15.dim(meta.join(" \xB7 ")));
6348
+ if (meta.length > 0) console.log(pc16.dim(meta.join(" \xB7 ")));
5798
6349
  });
6350
+ model.command("create").description("Create a new model").requiredOption("--id <id>", "Model ID").requiredOption("--provider <providerId>", "Provider ID").option("--display-name <name>", "Display name").option(
6351
+ "--type <type>",
6352
+ "Model type (chat|embedding|tts|stt|image|video|text2music|realtime)",
6353
+ "chat"
6354
+ ).action(
6355
+ async (options) => {
6356
+ const client = await getTrpcClient();
6357
+ const input = {
6358
+ id: options.id,
6359
+ providerId: options.provider,
6360
+ type: options.type || "chat"
6361
+ };
6362
+ if (options.displayName) input.displayName = options.displayName;
6363
+ const resultId = await client.aiModel.createAiModel.mutate(input);
6364
+ console.log(`${pc16.green("\u2713")} Created model ${pc16.bold(resultId || options.id)}`);
6365
+ }
6366
+ );
6367
+ model.command("edit <id>").description("Update model info").requiredOption("--provider <providerId>", "Provider ID").option("--display-name <name>", "Display name").option("--type <type>", "Model type (chat|embedding|tts|stt|image|video|text2music|realtime)").action(
6368
+ async (id, options) => {
6369
+ if (!options.displayName && !options.type) {
6370
+ log.error("No changes specified. Use --display-name or --type.");
6371
+ process.exit(1);
6372
+ }
6373
+ const client = await getTrpcClient();
6374
+ const value = {};
6375
+ if (options.displayName) value.displayName = options.displayName;
6376
+ if (options.type) value.type = options.type;
6377
+ await client.aiModel.updateAiModel.mutate({
6378
+ id,
6379
+ providerId: options.provider,
6380
+ value
6381
+ });
6382
+ console.log(`${pc16.green("\u2713")} Updated model ${pc16.bold(id)}`);
6383
+ }
6384
+ );
5799
6385
  model.command("toggle <id>").description("Enable or disable a model").requiredOption("--provider <providerId>", "Provider ID").option("--enable", "Enable the model").option("--disable", "Disable the model").action(
5800
6386
  async (id, options) => {
5801
6387
  if (options.enable === void 0 && options.disable === void 0) {
@@ -5809,7 +6395,7 @@ function registerModelCommand(program2) {
5809
6395
  id,
5810
6396
  providerId: options.provider
5811
6397
  });
5812
- console.log(`${pc15.green("\u2713")} Model ${pc15.bold(id)} ${enabled ? "enabled" : "disabled"}`);
6398
+ console.log(`${pc16.green("\u2713")} Model ${pc16.bold(id)} ${enabled ? "enabled" : "disabled"}`);
5813
6399
  }
5814
6400
  );
5815
6401
  model.command("delete <id>").description("Delete a model").requiredOption("--provider <providerId>", "Provider ID").option("--yes", "Skip confirmation prompt").action(async (id, options) => {
@@ -5822,12 +6408,49 @@ function registerModelCommand(program2) {
5822
6408
  }
5823
6409
  const client = await getTrpcClient();
5824
6410
  await client.aiModel.removeAiModel.mutate({ id, providerId: options.provider });
5825
- console.log(`${pc15.green("\u2713")} Deleted model ${pc15.bold(id)}`);
6411
+ console.log(`${pc16.green("\u2713")} Deleted model ${pc16.bold(id)}`);
6412
+ });
6413
+ model.command("batch-toggle <ids...>").description("Enable or disable multiple models at once").requiredOption("--provider <providerId>", "Provider ID").option("--enable", "Enable the models").option("--disable", "Disable the models").action(
6414
+ async (ids, options) => {
6415
+ if (options.enable === void 0 && options.disable === void 0) {
6416
+ log.error("Specify --enable or --disable.");
6417
+ process.exit(1);
6418
+ }
6419
+ const client = await getTrpcClient();
6420
+ const enabled = options.enable === true;
6421
+ await client.aiModel.batchToggleAiModels.mutate({
6422
+ enabled,
6423
+ id: options.provider,
6424
+ models: ids
6425
+ });
6426
+ console.log(
6427
+ `${pc16.green("\u2713")} ${enabled ? "Enabled" : "Disabled"} ${ids.length} model(s) for provider ${pc16.bold(options.provider)}`
6428
+ );
6429
+ }
6430
+ );
6431
+ model.command("clear").description("Clear models for a provider").requiredOption("--provider <providerId>", "Provider ID").option("--remote", "Only clear remote/fetched models").option("--yes", "Skip confirmation prompt").action(async (options) => {
6432
+ const label = options.remote ? "remote models" : "all models";
6433
+ if (!options.yes) {
6434
+ const confirmed = await confirm(
6435
+ `Are you sure you want to clear ${label} for provider ${options.provider}?`
6436
+ );
6437
+ if (!confirmed) {
6438
+ console.log("Cancelled.");
6439
+ return;
6440
+ }
6441
+ }
6442
+ const client = await getTrpcClient();
6443
+ if (options.remote) {
6444
+ await client.aiModel.clearRemoteModels.mutate({ providerId: options.provider });
6445
+ } else {
6446
+ await client.aiModel.clearModelsByProvider.mutate({ providerId: options.provider });
6447
+ }
6448
+ console.log(`${pc16.green("\u2713")} Cleared ${label} for provider ${pc16.bold(options.provider)}`);
5826
6449
  });
5827
6450
  }
5828
6451
 
5829
6452
  // src/commands/plugin.ts
5830
- import pc16 from "picocolors";
6453
+ import pc17 from "picocolors";
5831
6454
  function registerPluginCommand(program2) {
5832
6455
  const plugin = program2.command("plugin").description("Manage plugins");
5833
6456
  plugin.command("list").description("List installed plugins").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
@@ -5877,7 +6500,7 @@ function registerPluginCommand(program2) {
5877
6500
  settings,
5878
6501
  type: options.type
5879
6502
  });
5880
- console.log(`${pc16.green("\u2713")} Installed plugin ${pc16.bold(options.identifier)}`);
6503
+ console.log(`${pc17.green("\u2713")} Installed plugin ${pc17.bold(options.identifier)}`);
5881
6504
  }
5882
6505
  );
5883
6506
  plugin.command("uninstall <id>").description("Uninstall a plugin").option("--yes", "Skip confirmation prompt").action(async (id, options) => {
@@ -5890,7 +6513,7 @@ function registerPluginCommand(program2) {
5890
6513
  }
5891
6514
  const client = await getTrpcClient();
5892
6515
  await client.plugin.removePlugin.mutate({ id });
5893
- console.log(`${pc16.green("\u2713")} Uninstalled plugin ${pc16.bold(id)}`);
6516
+ console.log(`${pc17.green("\u2713")} Uninstalled plugin ${pc17.bold(id)}`);
5894
6517
  });
5895
6518
  plugin.command("update <id>").description("Update plugin settings or manifest").option("--manifest <json>", "New manifest JSON").option("--settings <json>", "New settings JSON").action(async (id, options) => {
5896
6519
  const input = { id };
@@ -5916,12 +6539,12 @@ function registerPluginCommand(program2) {
5916
6539
  }
5917
6540
  const client = await getTrpcClient();
5918
6541
  await client.plugin.updatePlugin.mutate(input);
5919
- console.log(`${pc16.green("\u2713")} Updated plugin ${pc16.bold(id)}`);
6542
+ console.log(`${pc17.green("\u2713")} Updated plugin ${pc17.bold(id)}`);
5920
6543
  });
5921
6544
  }
5922
6545
 
5923
6546
  // src/commands/provider.ts
5924
- import pc17 from "picocolors";
6547
+ import pc18 from "picocolors";
5925
6548
  function registerProviderCommand(program2) {
5926
6549
  const provider = program2.command("provider").description("Manage AI providers");
5927
6550
  provider.command("list").description("List AI providers").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
@@ -5940,7 +6563,7 @@ function registerProviderCommand(program2) {
5940
6563
  const rows = items.map((p) => [
5941
6564
  p.id || "",
5942
6565
  truncate(p.name || p.id || "", 30),
5943
- p.enabled ? pc17.green("\u2713") : pc17.dim("\u2717"),
6566
+ p.enabled ? pc18.green("\u2713") : pc18.dim("\u2717"),
5944
6567
  p.source || ""
5945
6568
  ]);
5946
6569
  printTable(rows, ["ID", "NAME", "ENABLED", "SOURCE"]);
@@ -5948,7 +6571,7 @@ function registerProviderCommand(program2) {
5948
6571
  provider.command("view <id>").description("View provider details").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (id, options) => {
5949
6572
  const client = await getTrpcClient();
5950
6573
  const result = await client.aiProvider.getAiProviderById.query({ id });
5951
- if (!result) {
6574
+ if (!result || !result.id) {
5952
6575
  log.error(`Provider not found: ${id}`);
5953
6576
  process.exit(1);
5954
6577
  return;
@@ -5959,11 +6582,124 @@ function registerProviderCommand(program2) {
5959
6582
  return;
5960
6583
  }
5961
6584
  const r = result;
5962
- console.log(pc17.bold(r.name || r.id || "Unknown"));
6585
+ console.log(pc18.bold(r.name || r.id));
5963
6586
  const meta = [];
5964
6587
  if (r.enabled !== void 0) meta.push(r.enabled ? "Enabled" : "Disabled");
5965
6588
  if (r.source) meta.push(`Source: ${r.source}`);
5966
- if (meta.length > 0) console.log(pc17.dim(meta.join(" \xB7 ")));
6589
+ if (meta.length > 0) console.log(pc18.dim(meta.join(" \xB7 ")));
6590
+ });
6591
+ provider.command("create").description("Create a new AI provider").requiredOption("--id <id>", "Provider ID").requiredOption("-n, --name <name>", "Provider name").option("-s, --source <source>", "Source type (builtin|custom)", "custom").option("-d, --description <desc>", "Provider description").option("--logo <logo>", "Provider logo URL").option("--sdk-type <sdkType>", "SDK type (openai|anthropic|azure|bedrock|...)").action(
6592
+ async (options) => {
6593
+ const client = await getTrpcClient();
6594
+ const input = {
6595
+ id: options.id,
6596
+ name: options.name,
6597
+ source: options.source || "custom"
6598
+ };
6599
+ if (options.description) input.description = options.description;
6600
+ if (options.logo) input.logo = options.logo;
6601
+ if (options.sdkType) input.sdkType = options.sdkType;
6602
+ const resultId = await client.aiProvider.createAiProvider.mutate(input);
6603
+ console.log(`${pc18.green("\u2713")} Created provider ${pc18.bold(resultId || options.id)}`);
6604
+ }
6605
+ );
6606
+ provider.command("edit <id>").description("Update provider info").option("-n, --name <name>", "Provider name").option("-d, --description <desc>", "Provider description").option("--logo <logo>", "Provider logo URL").option("--sdk-type <sdkType>", "SDK type").action(
6607
+ async (id, options) => {
6608
+ if (!options.name && !options.description && !options.logo && !options.sdkType) {
6609
+ log.error("No changes specified. Use --name, --description, --logo, or --sdk-type.");
6610
+ process.exit(1);
6611
+ }
6612
+ const client = await getTrpcClient();
6613
+ const value = {};
6614
+ if (options.name) value.name = options.name;
6615
+ if (options.description !== void 0) value.description = options.description;
6616
+ if (options.logo !== void 0) value.logo = options.logo;
6617
+ if (options.sdkType) value.sdkType = options.sdkType;
6618
+ await client.aiProvider.updateAiProvider.mutate({ id, value });
6619
+ console.log(`${pc18.green("\u2713")} Updated provider ${pc18.bold(id)}`);
6620
+ }
6621
+ );
6622
+ provider.command("config <id>").description("Configure provider settings (API key, base URL, etc.)").option("--api-key <key>", "Set API key").option("--base-url <url>", "Set base URL").option("--check-model <model>", "Set connectivity check model").option("--enable-response-api", "Enable Response API mode (OpenAI)").option("--disable-response-api", "Disable Response API mode").option("--fetch-on-client", "Enable fetching models on client side").option("--no-fetch-on-client", "Disable fetching models on client side").option("--show", "Show current config").option("--json [fields]", "Output JSON (with --show)").action(
6623
+ async (id, options) => {
6624
+ if (id === "lobehub" && (options.apiKey !== void 0 || options.baseUrl !== void 0)) {
6625
+ log.error(
6626
+ `Provider "lobehub" is managed by the LobeHub platform. You cannot set --api-key or --base-url for it.`
6627
+ );
6628
+ process.exit(1);
6629
+ }
6630
+ const client = await getTrpcClient();
6631
+ if (options.show) {
6632
+ const detail = await client.aiProvider.getAiProviderById.query({ id });
6633
+ if (!detail) {
6634
+ log.error(`Provider not found: ${id}`);
6635
+ process.exit(1);
6636
+ return;
6637
+ }
6638
+ const config = {
6639
+ checkModel: detail.checkModel || "",
6640
+ fetchOnClient: detail.fetchOnClient ?? false,
6641
+ keyVaults: detail.keyVaults || {}
6642
+ };
6643
+ if (options.json !== void 0) {
6644
+ const fields = typeof options.json === "string" ? options.json : void 0;
6645
+ outputJson(config, fields);
6646
+ } else {
6647
+ console.log(pc18.bold(`Config for ${id}`));
6648
+ if (config.checkModel) console.log(` Check Model: ${config.checkModel}`);
6649
+ console.log(` Fetch on Client: ${config.fetchOnClient ? pc18.green("\u2713") : pc18.dim("\u2717")}`);
6650
+ const vaults = config.keyVaults;
6651
+ if (vaults.apiKey)
6652
+ console.log(` API Key: ${pc18.dim(vaults.apiKey.slice(0, 8) + "...")}`);
6653
+ if (vaults.baseURL) console.log(` Base URL: ${vaults.baseURL}`);
6654
+ }
6655
+ return;
6656
+ }
6657
+ const hasKeyVaults = options.apiKey !== void 0 || options.baseUrl !== void 0;
6658
+ const hasConfig = options.enableResponseApi || options.disableResponseApi;
6659
+ const hasOther = options.checkModel !== void 0 || options.fetchOnClient !== void 0;
6660
+ if (!hasKeyVaults && !hasConfig && !hasOther) {
6661
+ log.error(
6662
+ "No config specified. Use --api-key, --base-url, --check-model, --enable-response-api, --fetch-on-client, or --show."
6663
+ );
6664
+ process.exit(1);
6665
+ }
6666
+ const input = {};
6667
+ if (hasKeyVaults) {
6668
+ const keyVaults = {};
6669
+ if (options.apiKey !== void 0) keyVaults.apiKey = options.apiKey;
6670
+ if (options.baseUrl !== void 0) keyVaults.baseURL = options.baseUrl;
6671
+ input.keyVaults = keyVaults;
6672
+ }
6673
+ if (hasConfig) {
6674
+ input.config = { enableResponseApi: !!options.enableResponseApi };
6675
+ }
6676
+ if (options.checkModel !== void 0) input.checkModel = options.checkModel;
6677
+ if (options.fetchOnClient !== void 0) input.fetchOnClient = options.fetchOnClient;
6678
+ await client.aiProvider.updateAiProviderConfig.mutate({ id, value: input });
6679
+ console.log(`${pc18.green("\u2713")} Updated config for provider ${pc18.bold(id)}`);
6680
+ }
6681
+ );
6682
+ provider.command("test <id>").description("Test provider connectivity").option("-m, --model <model>", "Model to test with (defaults to provider checkModel)").option("--json", "Output result as JSON").action(async (id, options) => {
6683
+ const client = await getTrpcClient();
6684
+ console.log(`${pc18.yellow("\u22EF")} Testing provider ${pc18.bold(id)}...`);
6685
+ const result = await client.aiProvider.checkProviderConnectivity.mutate({
6686
+ id,
6687
+ model: options.model
6688
+ });
6689
+ if (options.json) {
6690
+ outputJson(result);
6691
+ return;
6692
+ }
6693
+ if (result.ok) {
6694
+ console.log(
6695
+ `${pc18.green("\u2713")} Provider ${pc18.bold(id)} is reachable (model: ${result.model})`
6696
+ );
6697
+ } else {
6698
+ console.log(`${pc18.red("\u2717")} Provider ${pc18.bold(id)} check failed`);
6699
+ if (result.model) console.log(` Model: ${result.model}`);
6700
+ if (result.error) console.log(` Error: ${pc18.dim(result.error)}`);
6701
+ process.exit(1);
6702
+ }
5967
6703
  });
5968
6704
  provider.command("toggle <id>").description("Enable or disable a provider").option("--enable", "Enable the provider").option("--disable", "Disable the provider").action(async (id, options) => {
5969
6705
  if (options.enable === void 0 && options.disable === void 0) {
@@ -5973,7 +6709,7 @@ function registerProviderCommand(program2) {
5973
6709
  const client = await getTrpcClient();
5974
6710
  const enabled = options.enable === true;
5975
6711
  await client.aiProvider.toggleProviderEnabled.mutate({ enabled, id });
5976
- console.log(`${pc17.green("\u2713")} Provider ${pc17.bold(id)} ${enabled ? "enabled" : "disabled"}`);
6712
+ console.log(`${pc18.green("\u2713")} Provider ${pc18.bold(id)} ${enabled ? "enabled" : "disabled"}`);
5977
6713
  });
5978
6714
  provider.command("delete <id>").description("Delete a provider").option("--yes", "Skip confirmation prompt").action(async (id, options) => {
5979
6715
  if (!options.yes) {
@@ -5985,12 +6721,12 @@ function registerProviderCommand(program2) {
5985
6721
  }
5986
6722
  const client = await getTrpcClient();
5987
6723
  await client.aiProvider.removeAiProvider.mutate({ id });
5988
- console.log(`${pc17.green("\u2713")} Deleted provider ${pc17.bold(id)}`);
6724
+ console.log(`${pc18.green("\u2713")} Deleted provider ${pc18.bold(id)}`);
5989
6725
  });
5990
6726
  }
5991
6727
 
5992
6728
  // src/commands/search.ts
5993
- import pc18 from "picocolors";
6729
+ import pc19 from "picocolors";
5994
6730
  var SEARCH_TYPES = [
5995
6731
  "agent",
5996
6732
  "topic",
@@ -6007,7 +6743,7 @@ var SEARCH_TYPES = [
6007
6743
  function renderResultGroup(type, items) {
6008
6744
  if (items.length === 0) return;
6009
6745
  console.log();
6010
- console.log(pc18.bold(pc18.cyan(`\u2500\u2500 ${type} (${items.length}) \u2500\u2500`)));
6746
+ console.log(pc19.bold(pc19.cyan(`\u2500\u2500 ${type} (${items.length}) \u2500\u2500`)));
6011
6747
  const rows = items.map((item) => [
6012
6748
  item.id || "",
6013
6749
  truncate(item.title || item.name || item.content || "Untitled", 80),
@@ -6066,7 +6802,19 @@ function registerSearchCommand(program2) {
6066
6802
  }
6067
6803
 
6068
6804
  // src/commands/skill.ts
6069
- import pc19 from "picocolors";
6805
+ import pc20 from "picocolors";
6806
+ function detectSourceType(source) {
6807
+ if (source.startsWith("https://github.com/") || source.startsWith("http://github.com/")) {
6808
+ return "github";
6809
+ }
6810
+ if (/^[\w-]+\/[\w.-]+$/.test(source)) {
6811
+ return "github";
6812
+ }
6813
+ if (source.startsWith("https://") || source.startsWith("http://")) {
6814
+ return "url";
6815
+ }
6816
+ return "market";
6817
+ }
6070
6818
  function registerSkillCommand(program2) {
6071
6819
  const skill = program2.command("skill").description("Manage agent skills");
6072
6820
  skill.command("list").description("List skills").option("--source <source>", "Filter by source: builtin, market, user").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
@@ -6112,15 +6860,15 @@ function registerSkillCommand(program2) {
6112
6860
  return;
6113
6861
  }
6114
6862
  const r = result;
6115
- console.log(pc19.bold(r.name || "Untitled"));
6863
+ console.log(pc20.bold(r.name || "Untitled"));
6116
6864
  const meta = [];
6117
6865
  if (r.description) meta.push(r.description);
6118
6866
  if (r.source) meta.push(`Source: ${r.source}`);
6119
6867
  if (r.identifier) meta.push(`ID: ${r.identifier}`);
6120
- if (meta.length > 0) console.log(pc19.dim(meta.join(" \xB7 ")));
6868
+ if (meta.length > 0) console.log(pc20.dim(meta.join(" \xB7 ")));
6121
6869
  if (r.content) {
6122
6870
  console.log();
6123
- console.log(pc19.bold("Content:"));
6871
+ console.log(pc20.bold("Content:"));
6124
6872
  console.log(r.content);
6125
6873
  }
6126
6874
  });
@@ -6135,7 +6883,7 @@ function registerSkillCommand(program2) {
6135
6883
  if (options.identifier) input.identifier = options.identifier;
6136
6884
  const result = await client.agentSkills.create.mutate(input);
6137
6885
  const r = result;
6138
- console.log(`${pc19.green("\u2713")} Created skill ${pc19.bold(r.id || r)}`);
6886
+ console.log(`${pc20.green("\u2713")} Created skill ${pc20.bold(r.id || r)}`);
6139
6887
  }
6140
6888
  );
6141
6889
  skill.command("edit <id>").description("Update a skill").option("-c, --content <content>", "New content").option("-n, --name <name>", "New name (via manifest)").option("-d, --description <desc>", "New description (via manifest)").action(
@@ -6155,7 +6903,7 @@ function registerSkillCommand(program2) {
6155
6903
  input.manifest = manifest;
6156
6904
  }
6157
6905
  await client.agentSkills.update.mutate(input);
6158
- console.log(`${pc19.green("\u2713")} Updated skill ${pc19.bold(id)}`);
6906
+ console.log(`${pc20.green("\u2713")} Updated skill ${pc20.bold(id)}`);
6159
6907
  }
6160
6908
  );
6161
6909
  skill.command("delete <id>").description("Delete a skill").option("--yes", "Skip confirmation prompt").action(async (id, options) => {
@@ -6168,7 +6916,7 @@ function registerSkillCommand(program2) {
6168
6916
  }
6169
6917
  const client = await getTrpcClient();
6170
6918
  await client.agentSkills.delete.mutate({ id });
6171
- console.log(`${pc19.green("\u2713")} Deleted skill ${pc19.bold(id)}`);
6919
+ console.log(`${pc20.green("\u2713")} Deleted skill ${pc20.bold(id)}`);
6172
6920
  });
6173
6921
  skill.command("search <query>").description("Search skills").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (query, options) => {
6174
6922
  const client = await getTrpcClient();
@@ -6190,29 +6938,31 @@ function registerSkillCommand(program2) {
6190
6938
  ]);
6191
6939
  printTable(rows, ["ID", "NAME", "DESCRIPTION"]);
6192
6940
  });
6193
- skill.command("import-github").description("Import a skill from GitHub").requiredOption("--url <gitUrl>", "GitHub repository URL").option("--branch <branch>", "Branch name").action(async (options) => {
6194
- const client = await getTrpcClient();
6195
- const input = { gitUrl: options.url };
6196
- if (options.branch) input.branch = options.branch;
6197
- const result = await client.agentSkills.importFromGitHub.mutate(input);
6198
- const r = result;
6199
- console.log(`${pc19.green("\u2713")} Imported skill from GitHub ${pc19.bold(r.id || r.name || "")}`);
6200
- });
6201
- skill.command("import-url").description("Import a skill from a ZIP URL").requiredOption("--url <zipUrl>", "URL to skill ZIP file").action(async (options) => {
6202
- const client = await getTrpcClient();
6203
- const result = await client.agentSkills.importFromUrl.mutate({ url: options.url });
6204
- const r = result;
6205
- console.log(`${pc19.green("\u2713")} Imported skill from URL ${pc19.bold(r.id || r.name || "")}`);
6206
- });
6207
- skill.command("import-market").description("Install a skill from the marketplace").requiredOption("-i, --identifier <id>", "Skill identifier in marketplace").action(async (options) => {
6941
+ skill.command("install <source>").alias("i").description(
6942
+ "Install a skill (auto-detects: GitHub URL/shorthand, ZIP URL, or marketplace identifier)"
6943
+ ).option("--branch <branch>", "Branch name (GitHub only)").action(async (source, options) => {
6208
6944
  const client = await getTrpcClient();
6209
- const result = await client.agentSkills.importFromMarket.mutate({
6210
- identifier: options.identifier
6211
- });
6212
- const r = result;
6213
- console.log(
6214
- `${pc19.green("\u2713")} Installed skill ${pc19.bold(options.identifier)} ${r.id ? `(${r.id})` : ""}`
6215
- );
6945
+ const sourceType = detectSourceType(source);
6946
+ if (sourceType === "github") {
6947
+ const gitUrl = source.startsWith("https://") ? source : `https://github.com/${source}`;
6948
+ const input = { gitUrl };
6949
+ if (options.branch) input.branch = options.branch;
6950
+ const result = await client.agentSkills.importFromGitHub.mutate(input);
6951
+ const r = result;
6952
+ console.log(
6953
+ `${pc20.green("\u2713")} Installed skill from GitHub ${pc20.bold(r.id || r.name || "")}`
6954
+ );
6955
+ } else if (sourceType === "url") {
6956
+ const result = await client.agentSkills.importFromUrl.mutate({ url: source });
6957
+ const r = result;
6958
+ console.log(`${pc20.green("\u2713")} Installed skill from URL ${pc20.bold(r.id || r.name || "")}`);
6959
+ } else {
6960
+ const result = await client.agentSkills.importFromMarket.mutate({ identifier: source });
6961
+ const r = result;
6962
+ console.log(
6963
+ `${pc20.green("\u2713")} Installed skill ${pc20.bold(source)} ${r.id ? `(${r.id})` : ""}`
6964
+ );
6965
+ }
6216
6966
  });
6217
6967
  skill.command("resources <id>").description("List skill resource files").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (id, options) => {
6218
6968
  const client = await getTrpcClient();
@@ -6234,11 +6984,11 @@ function registerSkillCommand(program2) {
6234
6984
  ]);
6235
6985
  printTable(rows, ["PATH", "TYPE", "SIZE"]);
6236
6986
  });
6237
- skill.command("read-resource <id> <path>").description("Read a skill resource file").action(async (id, path12) => {
6987
+ skill.command("read-resource <id> <path>").description("Read a skill resource file").action(async (id, path13) => {
6238
6988
  const client = await getTrpcClient();
6239
- const result = await client.agentSkills.readResource.query({ id, path: path12 });
6989
+ const result = await client.agentSkills.readResource.query({ id, path: path13 });
6240
6990
  if (!result) {
6241
- log.error(`Resource not found: ${path12}`);
6991
+ log.error(`Resource not found: ${path13}`);
6242
6992
  process.exit(1);
6243
6993
  return;
6244
6994
  }
@@ -6311,15 +7061,14 @@ function registerStatusCommand(program2) {
6311
7061
  }
6312
7062
 
6313
7063
  // src/commands/topic.ts
6314
- import pc20 from "picocolors";
7064
+ import pc21 from "picocolors";
6315
7065
  function registerTopicCommand(program2) {
6316
7066
  const topic = program2.command("topic").description("Manage conversation topics");
6317
- topic.command("list").description("List topics").option("--agent-id <id>", "Filter by agent ID").option("--session-id <id>", "Filter by session ID").option("-L, --limit <n>", "Page size", "30").option("--page <n>", "Page number", "1").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(
7067
+ topic.command("list").description("List topics").option("--agent-id <id>", "Filter by agent ID").option("-L, --limit <n>", "Page size", "30").option("--page <n>", "Page number", "1").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(
6318
7068
  async (options) => {
6319
7069
  const client = await getTrpcClient();
6320
7070
  const input = {};
6321
7071
  if (options.agentId) input.agentId = options.agentId;
6322
- if (options.sessionId) input.sessionId = options.sessionId;
6323
7072
  if (options.limit) input.pageSize = Number.parseInt(options.limit, 10);
6324
7073
  if (options.page) input.current = Number.parseInt(options.page, 10);
6325
7074
  const result = await client.topic.getTopics.query(input);
@@ -6360,18 +7109,15 @@ function registerTopicCommand(program2) {
6360
7109
  const rows = items.map((t) => [t.id || "", truncate(t.title || "Untitled", 50)]);
6361
7110
  printTable(rows, ["ID", "TITLE"]);
6362
7111
  });
6363
- topic.command("create").description("Create a topic").requiredOption("-t, --title <title>", "Topic title").option("--agent-id <id>", "Agent ID").option("--session-id <id>", "Session ID").option("--favorite", "Mark as favorite").action(
6364
- async (options) => {
6365
- const client = await getTrpcClient();
6366
- const input = { title: options.title };
6367
- if (options.agentId) input.agentId = options.agentId;
6368
- if (options.sessionId) input.sessionId = options.sessionId;
6369
- if (options.favorite) input.favorite = true;
6370
- const result = await client.topic.createTopic.mutate(input);
6371
- const r = result;
6372
- console.log(`${pc20.green("\u2713")} Created topic ${pc20.bold(r.id || r)}`);
6373
- }
6374
- );
7112
+ topic.command("create").description("Create a topic").requiredOption("-t, --title <title>", "Topic title").option("--agent-id <id>", "Agent ID").option("--favorite", "Mark as favorite").action(async (options) => {
7113
+ const client = await getTrpcClient();
7114
+ const input = { title: options.title };
7115
+ if (options.agentId) input.agentId = options.agentId;
7116
+ if (options.favorite) input.favorite = true;
7117
+ const result = await client.topic.createTopic.mutate(input);
7118
+ const r = result;
7119
+ console.log(`${pc21.green("\u2713")} Created topic ${pc21.bold(r.id || r)}`);
7120
+ });
6375
7121
  topic.command("edit <id>").description("Update a topic").option("-t, --title <title>", "New title").option("--favorite", "Mark as favorite").option("--no-favorite", "Unmark as favorite").action(async (id, options) => {
6376
7122
  const value = {};
6377
7123
  if (options.title) value.title = options.title;
@@ -6382,7 +7128,7 @@ function registerTopicCommand(program2) {
6382
7128
  }
6383
7129
  const client = await getTrpcClient();
6384
7130
  await client.topic.updateTopic.mutate({ id, value });
6385
- console.log(`${pc20.green("\u2713")} Updated topic ${pc20.bold(id)}`);
7131
+ console.log(`${pc21.green("\u2713")} Updated topic ${pc21.bold(id)}`);
6386
7132
  });
6387
7133
  topic.command("delete <ids...>").description("Delete one or more topics").option("--yes", "Skip confirmation prompt").action(async (ids, options) => {
6388
7134
  if (!options.yes) {
@@ -6398,7 +7144,7 @@ function registerTopicCommand(program2) {
6398
7144
  } else {
6399
7145
  await client.topic.batchDelete.mutate({ ids });
6400
7146
  }
6401
- console.log(`${pc20.green("\u2713")} Deleted ${ids.length} topic(s)`);
7147
+ console.log(`${pc21.green("\u2713")} Deleted ${ids.length} topic(s)`);
6402
7148
  });
6403
7149
  topic.command("recent").description("List recent topics").option("-L, --limit <n>", "Number of items", "10").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
6404
7150
  const client = await getTrpcClient();
@@ -6446,6 +7192,7 @@ registerModelCommand(program);
6446
7192
  registerProviderCommand(program);
6447
7193
  registerPluginCommand(program);
6448
7194
  registerConfigCommand(program);
7195
+ registerEvalCommand(program);
6449
7196
  program.parse();
6450
7197
  /*! Bundled license information:
6451
7198