@appchy/jarvis 0.1.8 → 0.1.10

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/bin.js CHANGED
@@ -400,6 +400,7 @@ function claudeCodeProvider(config = {}) {
400
400
  } : {},
401
401
  ...config.disallowedTools?.length ? { disallowedTools: config.disallowedTools } : {},
402
402
  ...config.abortController ? { abortController: config.abortController } : {},
403
+ ...config.thinking ? { thinking: config.thinking } : {},
403
404
  // Session resume/persistence options
404
405
  ...config.session?.sessionId ? { sessionId: config.session.sessionId } : {},
405
406
  ...config.session?.resume ? { resume: config.session.resume } : {},
@@ -569,6 +570,7 @@ function claudeCodeProvider(config = {}) {
569
570
  } : {},
570
571
  ...config.disallowedTools?.length ? { disallowedTools: config.disallowedTools } : {},
571
572
  ...config.abortController ? { abortController: config.abortController } : {},
573
+ ...config.thinking ? { thinking: config.thinking } : {},
572
574
  // Session resume/persistence options
573
575
  ...config.session?.sessionId ? { sessionId: config.session.sessionId } : {},
574
576
  ...config.session?.resume ? { resume: config.session.resume } : {},
@@ -2018,6 +2020,8 @@ function createWsProgressHandlers(deps) {
2018
2020
  let currentThinkingId = null;
2019
2021
  let currentTextId = null;
2020
2022
  let currentTextContent = "";
2023
+ let planFileContent = null;
2024
+ let planFilePath = null;
2021
2025
  async function onMessage(msg) {
2022
2026
  broadcast({ type: "agent:onMessage", taskId, message: msg });
2023
2027
  }
@@ -2045,10 +2049,27 @@ function createWsProgressHandlers(deps) {
2045
2049
  const now = (/* @__PURE__ */ new Date()).toISOString();
2046
2050
  if (toolName === "askUser") return waitForInput(input);
2047
2051
  if (toolName === "ExitPlanMode") {
2052
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
2053
+ if (planFileContent) {
2054
+ await onMessage({
2055
+ id: `plan-${crypto2.randomUUID()}`,
2056
+ role: "assistant",
2057
+ type: "ui",
2058
+ content: planFileContent,
2059
+ ui: {
2060
+ uri: "ui://agent/plan",
2061
+ title: "Plan",
2062
+ data: { content: planFileContent, path: planFilePath }
2063
+ },
2064
+ isPartial: false,
2065
+ createdAt: now2
2066
+ });
2067
+ }
2048
2068
  return waitForInput({
2049
2069
  ...input,
2050
2070
  type: "plan",
2051
- reason: input.input?.allowedPrompts || "Plan ready for review"
2071
+ reason: input.input?.allowedPrompts || "Plan ready for review",
2072
+ output: planFileContent ? { content: planFileContent, path: planFilePath } : input.output
2052
2073
  });
2053
2074
  }
2054
2075
  if (toolName === "EnterPlanMode") {
@@ -2108,6 +2129,11 @@ function createWsProgressHandlers(deps) {
2108
2129
  isComplete: false
2109
2130
  });
2110
2131
  }
2132
+ if (block.type === "tool_use" && block.name === "Write" && typeof block.input?.file_path === "string" && block.input.file_path.endsWith(".md")) {
2133
+ const input = block.input;
2134
+ planFileContent = input.content ?? null;
2135
+ planFilePath = input.file_path ?? null;
2136
+ }
2111
2137
  if (block.type === "tool_use" && block.id && block.name) {
2112
2138
  if (currentTextId) {
2113
2139
  broadcast({
@@ -2233,6 +2259,8 @@ function createAgent(deps) {
2233
2259
  worktreePath: wsPath,
2234
2260
  messageCount: msg.messages.length
2235
2261
  });
2262
+ const askModeTools = msg.permissionMode === "ask" ? ["Edit", "Write", "MultiEdit", "NotebookEdit"] : [];
2263
+ const allDisallowed = [...msg.disallowedTools ?? [], ...askModeTools];
2236
2264
  const provider2 = claudeCodeProvider({
2237
2265
  ...deps.useSubscription ? { useSubscription: true } : { apiKey: deps.anthropicApiKey },
2238
2266
  systemPrompt: systemMsg?.content ?? "",
@@ -2247,7 +2275,8 @@ function createAgent(deps) {
2247
2275
  onProgress: handlers.handleProgress,
2248
2276
  onUserInput: handlers.handleUserInput,
2249
2277
  permissionMode: msg.permissionMode === "plan" ? "plan" : "default",
2250
- ...msg.disallowedTools?.length ? { disallowedTools: msg.disallowedTools } : {},
2278
+ ...allDisallowed.length ? { disallowedTools: allDisallowed } : {},
2279
+ ...msg.thinking ? { thinking: msg.thinking } : {},
2251
2280
  ...msg.session ? { session: msg.session } : {}
2252
2281
  });
2253
2282
  const ctx = { requestId: taskId, userId: deps.userId };
@@ -2303,7 +2332,7 @@ function createAgent(deps) {
2303
2332
  deps.broadcast({
2304
2333
  type: "agent:output",
2305
2334
  taskId,
2306
- output: { success: false, content: "", error: "Aborted" }
2335
+ output: { success: false, content: "", error: "Aborted", sessionId: msg.session?.resume }
2307
2336
  });
2308
2337
  } else {
2309
2338
  const error = err instanceof Error ? err.message : String(err);
@@ -4804,8 +4833,99 @@ function clearPid() {
4804
4833
  } catch {
4805
4834
  }
4806
4835
  }
4836
+ async function waitForBrowserAuth(port) {
4837
+ const http = await import("http");
4838
+ return new Promise((resolve, reject) => {
4839
+ const timeout = setTimeout(() => {
4840
+ server.close();
4841
+ reject(new Error("Browser authentication timed out (2 minutes)"));
4842
+ }, 12e4);
4843
+ const server = http.createServer((req, res) => {
4844
+ const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
4845
+ if (url.pathname === "/callback") {
4846
+ const token = url.searchParams.get("token");
4847
+ res.setHeader("Access-Control-Allow-Origin", "*");
4848
+ res.setHeader("Access-Control-Allow-Methods", "GET");
4849
+ if (token) {
4850
+ res.writeHead(200, { "Content-Type": "text/plain" });
4851
+ res.end("OK");
4852
+ clearTimeout(timeout);
4853
+ server.close();
4854
+ resolve(token);
4855
+ } else {
4856
+ res.writeHead(400, { "Content-Type": "text/plain" });
4857
+ res.end("Missing token");
4858
+ }
4859
+ return;
4860
+ }
4861
+ res.writeHead(404);
4862
+ res.end();
4863
+ });
4864
+ server.listen(port, "127.0.0.1");
4865
+ });
4866
+ }
4867
+ async function findFreePort() {
4868
+ const net = await import("net");
4869
+ return new Promise((resolve, reject) => {
4870
+ const server = net.createServer();
4871
+ server.listen(0, "127.0.0.1", () => {
4872
+ const addr = server.address();
4873
+ const port = typeof addr === "object" && addr ? addr.port : 0;
4874
+ server.close(() => resolve(port));
4875
+ });
4876
+ server.on("error", reject);
4877
+ });
4878
+ }
4879
+ async function browserAuth(appUrl) {
4880
+ const config = loadConfig();
4881
+ const callbackPort = await findFreePort();
4882
+ const loginUrl = `${appUrl}/login?callback=cli&port=${callbackPort}`;
4883
+ console.log();
4884
+ console.log("Opening browser for sign in...");
4885
+ console.log(` If it doesn't open, visit: ${loginUrl}`);
4886
+ console.log();
4887
+ const { exec: exec3 } = await import("child_process");
4888
+ const platform = process.platform;
4889
+ const openCmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
4890
+ exec3(`${openCmd} "${loginUrl}"`);
4891
+ console.log("Waiting for authentication...");
4892
+ try {
4893
+ const token = await waitForBrowserAuth(callbackPort);
4894
+ const parsed = parseConnectToken(token);
4895
+ saveConfig({
4896
+ ...config,
4897
+ apiUrl: parsed.apiUrl,
4898
+ token: parsed.jwt,
4899
+ refreshToken: parsed.refreshToken,
4900
+ userId: parsed.userId,
4901
+ envId: parsed.envId,
4902
+ connectedAt: (/* @__PURE__ */ new Date()).toISOString()
4903
+ });
4904
+ console.log();
4905
+ console.log(`Signed in as ${parsed.userId}`);
4906
+ return true;
4907
+ } catch (err) {
4908
+ console.error(`Authentication failed: ${err instanceof Error ? err.message : err}`);
4909
+ return false;
4910
+ }
4911
+ }
4912
+ var DEFAULT_APP_URL = process.env.APP_URL ?? "http://localhost:3000";
4913
+ async function ensureSetup() {
4914
+ const config = loadConfig();
4915
+ if (config?.token || config?.anthropicApiKey || config?.useSubscription) {
4916
+ return true;
4917
+ }
4918
+ return browserAuth(config?.apiUrl ?? DEFAULT_APP_URL);
4919
+ }
4807
4920
  function createCli() {
4808
4921
  const program = new Command().name("jarvis").description("Jarvis local agent \u2014 runs Claude Code on your machine").version(PKG_VERSION);
4922
+ program.argument("[message]", "Optional initial message to send").option("--model <id>", "Model (e.g., claude-opus-4-20250514)").option("--mode <mode>", "Permission mode: supervised|auto|plan|yolo").option("--thinking <config>", "Thinking: adaptive|enabled|disabled").option("--thinking-budget <n>", "Token budget when thinking=enabled").option("--max-turns <n>", "Max turns per task").option("-p, --port <port>", "Agent server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--no-server", "Connect to existing server, don't auto-start").action(async (message, opts) => {
4923
+ if (message && program.commands.some((c) => c.name() === message)) {
4924
+ return;
4925
+ }
4926
+ await ensureSetup();
4927
+ await launchChat(opts);
4928
+ });
4809
4929
  program.command("connect <token>").description("Connect to Jarvis cloud using a token from the web UI").option("-w, --workspace <path>", "Workspace root path for repo operations").action((token, opts) => {
4810
4930
  try {
4811
4931
  const parsed = parseConnectToken(token);
@@ -4833,6 +4953,7 @@ function createCli() {
4833
4953
  }
4834
4954
  });
4835
4955
  program.command("start").description("Start the local agent (runs as background daemon)").option("-p, --port <port>", "Local WS server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--api-key <key>", "Anthropic API key (for local-only use)").option("--no-upstream", "Don't connect to cloud (local-only mode)").option("--foreground", "Run in foreground (don't daemonize)").action(async (opts) => {
4956
+ await ensureSetup();
4836
4957
  const config = loadConfig();
4837
4958
  const port = parseInt(opts.port, 10);
4838
4959
  const alreadyRunning = await isPortInUse(port);
@@ -4842,17 +4963,16 @@ function createCli() {
4842
4963
  return;
4843
4964
  }
4844
4965
  const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
4845
- const anthropicApiKey = opts.apiKey ?? config?.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
4846
4966
  const userId = config?.userId ?? process.env.JARVIS_USER_ID ?? "local";
4847
- if (!anthropicApiKey && !config?.useSubscription) {
4848
- console.warn("Warning: No Anthropic API key set. Using subscription mode.");
4849
- }
4967
+ const explicitApiKey = opts.apiKey ?? config?.anthropicApiKey;
4968
+ const useSubscription = !explicitApiKey;
4969
+ const anthropicApiKey = explicitApiKey;
4850
4970
  if (opts.foreground) {
4851
4971
  await startAgent({
4852
4972
  port,
4853
4973
  workspacePath,
4854
4974
  anthropicApiKey,
4855
- useSubscription: !anthropicApiKey,
4975
+ useSubscription,
4856
4976
  userId,
4857
4977
  upstream: (() => {
4858
4978
  const apiUrl = process.env.JARVIS_UPSTREAM_URL ?? config?.apiUrl;
@@ -4958,7 +5078,7 @@ function createCli() {
4958
5078
  console.log("Starting agent...");
4959
5079
  await program.parseAsync(["node", "jarvis", "start"]);
4960
5080
  });
4961
- program.command("logs").description("View agent logs").option("-f, --follow", "Follow log output (like tail -f)").option("-n, --lines <n>", "Number of lines to show", "50").action((opts) => {
5081
+ program.command("logs").description("View agent logs").option("-f, --follow", "Follow log output (like tail -f)", true).option("-n, --lines <n>", "Number of lines to show", "50").action((opts) => {
4962
5082
  if (!fs7.existsSync(LOG_FILE)) {
4963
5083
  console.log("No log file found. Start the agent first: jarvis start");
4964
5084
  return;
@@ -4977,9 +5097,6 @@ function createCli() {
4977
5097
  process.stdout.write(content);
4978
5098
  }
4979
5099
  });
4980
- program.command("chat").description("Interactive TUI \u2014 chat with Jarvis in your terminal").option("--model <id>", "Model (e.g., claude-opus-4-20250514)").option("--mode <mode>", "Permission mode: supervised|auto|plan|yolo").option("--thinking <config>", "Thinking: adaptive|enabled|disabled").option("--thinking-budget <n>", "Token budget when thinking=enabled").option("--max-turns <n>", "Max turns per task").option("-p, --port <port>", "Agent server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--no-server", "Connect to existing server, don't auto-start").action(async (opts) => {
4981
- await launchChat(opts);
4982
- });
4983
5100
  program.command("status").description("Show agent configuration and connection status").action(async () => {
4984
5101
  const config = loadConfig();
4985
5102
  const pid = readPid();