@lobehub/cli 0.0.63 → 0.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import { finished, pipeline } from "node:stream/promises";
14
14
  import { Duplex, PassThrough, Readable, Transform, Writable, getDefaultHighWaterMark, pipeline as pipeline$1 } from "node:stream";
15
15
  import readline, { createInterface } from "node:readline";
16
16
  import { EventEmitter, addAbortListener, on, once, setMaxListeners } from "node:events";
17
- import fs$1, { access, chmod, copyFile, cp, lstat, mkdir, open, readFile, readdir, readlink, realpath, rename, rm, stat, unlink as unlink$1, writeFile } from "node:fs/promises";
17
+ import fs$1, { access, chmod, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, realpath, rename, rm, stat, unlink as unlink$1, writeFile } from "node:fs/promises";
18
18
  import crypto$1, { X509Certificate, createHash, randomBytes, randomUUID, sign } from "node:crypto";
19
19
  import { text } from "node:stream/consumers";
20
20
  import { ReadableStream as ReadableStream$1 } from "node:stream/web";
@@ -23324,6 +23324,7 @@ const TRPC_NAMESPACE_API_KEY_RULES = {
23324
23324
  user: rw("user:read", "user:write"),
23325
23325
  userMemories: rw("user:read", "user:write"),
23326
23326
  userMemory: rw("user:read", "user:write"),
23327
+ userSkill: rw("agent:read", "agent:write"),
23327
23328
  verify: "blocked",
23328
23329
  video: { any: "model:invoke" },
23329
23330
  waitlist: "blocked",
@@ -292724,21 +292725,56 @@ var init_src = __esmMin((() => {
292724
292725
  //#endregion
292725
292726
  //#region ../../packages/local-file-shell/src/shell/runner.ts
292726
292727
  /**
292727
- * Node reports a missing spawn cwd as `spawn <shell> ENOENT` — blaming the
292728
- * shell binary — so the model goes off debugging a healthy shell. Check the
292729
- * directory first and name the real problem, and the machine it happened on
292730
- * (a cwd pinned on another device is the usual cause).
292728
+ * `true` = resolves to a directory; `false` = resolves to a non-directory
292729
+ * (a regular file); `undefined` = cannot be resolved at all (missing, an
292730
+ * unreadable parent, a symlink loop). Keeping the three cases apart lets the
292731
+ * report name what actually happened instead of guessing.
292731
292732
  */
292732
- const checkWorkingDirectory = async (cwd) => {
292733
+ const statIsDirectory = async (dir) => {
292733
292734
  try {
292734
- if ((await stat(cwd)).isDirectory()) return;
292735
- return `Working directory is not a directory on ${os.hostname()}: ${cwd}`;
292736
- } catch (error) {
292737
- if (error.code !== "ENOENT") return;
292738
- return `Working directory does not exist on ${os.hostname()}: ${cwd}. The shell is fine — run the command from a directory that exists on this device.`;
292735
+ return (await stat(dir)).isDirectory();
292736
+ } catch {
292737
+ return;
292739
292738
  }
292740
292739
  };
292741
- async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_background, timeout = DEFAULT_OBSERVATION_TIMEOUT_MS }, { backend: backendOverride, processManager, logger, onSandboxUnavailable, sandboxPolicy, spawnProcess }) {
292740
+ /**
292741
+ * Resolve a requested cwd to a directory the shell can actually start in.
292742
+ *
292743
+ * A cwd pinned on another machine (a stale repo path, a moved worktree) would
292744
+ * otherwise kill every command in the run: the run cannot change its own pinned
292745
+ * cwd, callers strip a model-supplied cwd before dispatch, and re-running
292746
+ * `cd` inside the command does not help because the check happens before the
292747
+ * shell exists — so the same failure repeats forever. Un-sandboxed runs fall
292748
+ * back to a fresh isolated directory created under the OS temp root, and the
292749
+ * fallback is reported so nothing downstream mistakes it for the requested
292750
+ * directory.
292751
+ *
292752
+ * Sandboxed runs do NOT fall back: the sandbox fence (`createLocalSandboxPolicy`)
292753
+ * is built on the requested cwd, so silently spawning in another directory
292754
+ * would grant writes somewhere the user never approved. Those fail loudly.
292755
+ */
292756
+ const resolveWorkingDirectory = async (requestedCwd, sandboxRequested, fallbackRoot) => {
292757
+ const usable = await statIsDirectory(requestedCwd);
292758
+ if (usable) return { cwd: requestedCwd };
292759
+ const reason = usable === false ? `Working directory is not a directory on ${os.hostname()}: ${requestedCwd}` : `Working directory does not exist on ${os.hostname()}: ${requestedCwd}`;
292760
+ if (sandboxRequested) return { error: `${reason}. A sandboxed run must start in its requested directory — the sandbox fence is built on it — so the command was not run.` };
292761
+ try {
292762
+ const fallback = await mkdtemp(path.join(fallbackRoot ?? os.tmpdir(), "lobehub-cwd-fallback-"));
292763
+ return {
292764
+ cwd: fallback,
292765
+ notice: `${reason}. Falling back to ${fallback}.`,
292766
+ remove: async () => {
292767
+ await rm(fallback, {
292768
+ force: true,
292769
+ recursive: true
292770
+ });
292771
+ }
292772
+ };
292773
+ } catch {
292774
+ return { error: `${reason}. The shell is fine — run the command from a directory that exists on this device.` };
292775
+ }
292776
+ };
292777
+ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_background, timeout = DEFAULT_OBSERVATION_TIMEOUT_MS }, { backend: backendOverride, processManager, logger, onSandboxUnavailable, sandboxPolicy, spawnProcess, tmpdirOverride }) {
292742
292778
  if (!command) return {
292743
292779
  error: "command is required",
292744
292780
  success: false
@@ -292750,12 +292786,18 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
292750
292786
  cwd,
292751
292787
  timeout
292752
292788
  });
292789
+ let effectiveCwd = cwd;
292790
+ let cwdNotice;
292791
+ let removeCwdFallback;
292753
292792
  if (cwd) {
292754
- const cwdError = await checkWorkingDirectory(cwd);
292755
- if (cwdError) return {
292756
- error: cwdError,
292793
+ const resolution = await resolveWorkingDirectory(cwd, !!sandboxPolicy, tmpdirOverride);
292794
+ if (resolution.error) return {
292795
+ error: resolution.error,
292757
292796
  success: false
292758
292797
  };
292798
+ effectiveCwd = resolution.cwd;
292799
+ cwdNotice = resolution.notice;
292800
+ removeCwdFallback = resolution.remove;
292759
292801
  }
292760
292802
  const requestedEnv = extraEnv ? {
292761
292803
  ...process.env,
@@ -292763,6 +292805,7 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
292763
292805
  } : process.env;
292764
292806
  const shellConfig = await getShellConfig(process.platform === "win32" ? normalizeEnvVarRefs(command, requestedEnv, (await detectWindowsShell()).type) : command);
292765
292807
  let outputFiles;
292808
+ let registered = false;
292766
292809
  let releaseSandbox;
292767
292810
  let sandboxed;
292768
292811
  try {
@@ -292774,7 +292817,7 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
292774
292817
  try {
292775
292818
  launchPlan = await createSandboxLaunchPlan({
292776
292819
  command: shellConfig,
292777
- cwd,
292820
+ cwd: effectiveCwd,
292778
292821
  env: requestedEnv,
292779
292822
  policy: sandboxPolicy
292780
292823
  });
@@ -292794,7 +292837,7 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
292794
292837
  args: launchCommand.args,
292795
292838
  cmd: launchCommand.cmd
292796
292839
  }, {
292797
- cwd,
292840
+ cwd: effectiveCwd,
292798
292841
  env: launchEnv,
292799
292842
  outputFiles: shellOutputFiles,
292800
292843
  shellId
@@ -292818,21 +292861,27 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
292818
292861
  shellProcess.spawnError = new Error(`Failed to start command${cwdContext}: ${error.message}`, { cause: error });
292819
292862
  shellProcess.exitCode = 1;
292820
292863
  });
292821
- handle.once("close", () => releaseSandbox?.());
292864
+ handle.once("close", () => {
292865
+ releaseSandbox?.();
292866
+ if (removeCwdFallback) removeCwdFallback().catch((error) => {
292867
+ logger?.debug(`${logPrefix} Fallback directory cleanup failed`, { error });
292868
+ });
292869
+ });
292822
292870
  processManager.register(shellId, shellProcess);
292871
+ registered = true;
292823
292872
  processManager.closeOutputFiles(shellOutputFiles);
292824
292873
  logger?.info?.(`${logPrefix} Started session`, {
292825
292874
  background: run_in_background,
292826
292875
  shellId
292827
292876
  });
292828
292877
  if (run_in_background) return {
292829
- output: "",
292878
+ output: cwdNotice ?? "",
292830
292879
  output_files: processManager.getOutputFilesInfo(shellOutputFiles),
292831
292880
  sandboxed,
292832
292881
  shell_id: shellId,
292833
292882
  success: true
292834
292883
  };
292835
- return {
292884
+ const result = {
292836
292885
  ...await processManager.getRunCommandOutput({
292837
292886
  shell_id: shellId,
292838
292887
  timeout
@@ -292840,9 +292889,17 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
292840
292889
  sandboxed,
292841
292890
  shell_id: shellId
292842
292891
  };
292892
+ if (!cwdNotice) return result;
292893
+ const prefix = `${cwdNotice}\n\n`;
292894
+ return {
292895
+ ...result,
292896
+ output: `${prefix}${result.output ?? ""}`,
292897
+ stdout: `${prefix}${result.stdout ?? ""}`
292898
+ };
292843
292899
  } catch (error) {
292844
292900
  releaseSandbox?.();
292845
292901
  if (outputFiles) processManager.closeOutputFiles(outputFiles);
292902
+ if (removeCwdFallback && !registered) await removeCwdFallback().catch(() => {});
292846
292903
  return {
292847
292904
  error: error.message,
292848
292905
  success: false
@@ -512440,6 +512497,7 @@ const admissionHint = {
512440
512497
  pending_decision: "a decision gate is still waiting on an answer",
512441
512498
  review_changed: "task review feedback changed after this turn started",
512442
512499
  stale_input: "the graph moved after this turn started, so the plan would apply to stale input",
512500
+ started_from_draft: "the Goal was started from its draft, which settled this turn; the next turn plans further work",
512443
512501
  turn_owner: "this Goal has no matching planning turn",
512444
512502
  turn_settled: "this turn's run is no longer running",
512445
512503
  wait_unsettled: "unfinished work must be settled before waiting",
@@ -512695,10 +512753,12 @@ function registerGoalCommand$1(program) {
512695
512753
  };
512696
512754
  const result = fromTopic ? hasOperationToken() ? await client.goal.createConversationGoal.mutate({
512697
512755
  ...goalInput,
512756
+ draft: true,
512698
512757
  operationId
512699
512758
  }) : await client.goal.create.mutate({
512700
512759
  ...goalInput,
512701
512760
  agentId: process.env.LOBEHUB_AGENT_ID,
512761
+ draft: true,
512702
512762
  conversationOperationId: operationId,
512703
512763
  conversationTopicId: process.env.LOBEHUB_TOPIC_ID
512704
512764
  }) : await client.goal.create.mutate({
@@ -512726,7 +512786,11 @@ function registerGoalCommand$1(program) {
512726
512786
  "first; every other seed task (or only --units) leaves the graph as a roster unit, its description",
512727
512787
  "kept as that unit's note, and is released wave by wave once the probes ran through without a person.",
512728
512788
  "A rejection revises the recipe and re-probes. Tasks that differ only in names, files or details are",
512729
- "one recipe; tasks whose steps differ are not, so start those without --rollout."
512789
+ "one recipe; tasks whose steps differ are not, so start those without --rollout.",
512790
+ "Inside a batch a seed may depend only on a probe, and folded seeds need distinct titles.",
512791
+ "",
512792
+ "Starting a draft that holds tasks settles the planning turn that created it: submit any plan with",
512793
+ "lh goal plan before starting, not after."
512730
512794
  ].join("\n")).option("--rollout <mode>", "Fold the seed tasks into a batch: canary or full").option("--probes <nodeIds...>", "Seed task node ids that run first (2-5 for canary)").option("--units <nodeIds...>", "Seed task node ids that become batch units (default: every other seed)").option("--recipe <text>", "The procedure every unit follows").option("--recipe-file <path>", "Read the recipe from a markdown file").option("--variant <axis=values...>", "An axis the units differ along, e.g. mechanism=replica,swr (repeatable); the gate holds until the probes cover each value").option("--operation <id>", "Defaults to LOBEHUB_OPERATION_ID").option("--json", "Output JSON").action(async (id, options) => {
512731
512795
  let rollout;
512732
512796
  if (options.rollout) {
package/man/man1/lh.1 CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" Code generated by `npm run man:generate`; DO NOT EDIT.
2
2
  .\" Manual command details come from the Commander command tree.
3
- .TH LH 1 "" "@lobehub/cli 0.0.63" "User Commands"
3
+ .TH LH 1 "" "@lobehub/cli 0.0.64" "User Commands"
4
4
  .SH NAME
5
5
  lh \- LobeHub CLI \- manage and connect to LobeHub services
6
6
  .SH SYNOPSIS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/cli",
3
- "version": "0.0.63",
3
+ "version": "0.0.64",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "lh": "dist/index.js",
@@ -41,18 +41,18 @@
41
41
  "url-join": "^5.0.0",
42
42
  "vitest": "5.0.0",
43
43
  "@lobechat/const": "1.0.0",
44
- "@lobechat/device-control": "1.0.0",
45
44
  "@lobechat/agent-gateway-client": "1.0.0",
46
45
  "@lobechat/agent-tracing": "1.0.0",
47
- "@lobechat/eval-rubric": "1.0.0",
48
46
  "@lobechat/device-gateway-client": "1.0.0",
49
47
  "@lobechat/device-identity": "1.0.0",
48
+ "@lobechat/eval-rubric": "1.0.0",
49
+ "@lobechat/device-control": "1.0.0",
50
+ "@lobechat/heterogeneous-agents": "1.0.0",
50
51
  "@lobechat/html-artifact": "1.0.0",
51
52
  "@lobechat/local-file-shell": "1.0.0",
52
- "@lobechat/heterogeneous-agents": "1.0.0",
53
53
  "@lobechat/tool-runtime": "1.0.0",
54
- "@lobechat/types": "1.0.0",
55
- "@lobechat/utils": "1.0.0"
54
+ "@lobechat/utils": "1.0.0",
55
+ "@lobechat/types": "1.0.0"
56
56
  },
57
57
  "engines": {
58
58
  "node": ">=22.15"