@appchy/jarvis 0.1.15 → 0.1.17

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
@@ -1,20 +1,29 @@
1
1
  // src/bin.ts
2
2
  import dotenv from "dotenv";
3
- import fs9 from "fs";
4
- import path9 from "path";
3
+ import fs11 from "fs";
4
+ import path11 from "path";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
8
8
  import { spawn as spawn3, execSync as execSync2 } from "child_process";
9
- import fs8 from "fs";
10
- import path8 from "path";
9
+ import fs10 from "fs";
10
+ import path10 from "path";
11
11
  import os5 from "os";
12
12
 
13
13
  // src/config.ts
14
14
  import fs from "fs";
15
15
  import path from "path";
16
16
  import os from "os";
17
- var CONFIG_DIR = process.env.JARVIS_CONFIG_DIR ?? path.join(os.homedir(), ".jarvis");
17
+ function resolveConfigDir() {
18
+ if (process.env.JARVIS_CONFIG_DIR) return process.env.JARVIS_CONFIG_DIR;
19
+ const base = path.join(os.homedir(), ".jarvis");
20
+ const appUrl = process.env.APP_URL ?? "";
21
+ if (appUrl && !appUrl.includes("appchy.com") && appUrl !== "") {
22
+ return path.join(base, "dev");
23
+ }
24
+ return base;
25
+ }
26
+ var CONFIG_DIR = resolveConfigDir();
18
27
  var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
19
28
  function loadConfig() {
20
29
  try {
@@ -53,30 +62,6 @@ function getConfigPath() {
53
62
  return CONFIG_FILE;
54
63
  }
55
64
 
56
- // ../../providers/anthropic/src/anthropic.provider.ts
57
- import Anthropic from "@anthropic-ai/sdk";
58
-
59
- // ../../providers/anthropic/src/anthropic.models.ts
60
- var claudeCodeSonnet = {
61
- id: "claude-sonnet-4-5-20250929",
62
- name: "Claude Code (Sonnet)",
63
- provider: "claude-code",
64
- maxTokens: { input: 2e5, output: 16384 },
65
- cost: { multiplier: 20, inputTokens: 3, outputTokens: 15 }
66
- };
67
- var claudeCodeOpus = {
68
- id: "claude-opus-4-6-20250515",
69
- name: "Claude Code (Opus)",
70
- provider: "claude-code",
71
- maxTokens: { input: 2e5, output: 16384 },
72
- cost: { multiplier: 50, inputTokens: 5, outputTokens: 25 }
73
- };
74
- var claudeCodeModels = {
75
- claudeCodeSonnet,
76
- claudeCodeOpus,
77
- list: () => [claudeCodeSonnet, claudeCodeOpus]
78
- };
79
-
80
65
  // ../../packages/logger/src/logger.ts
81
66
  function resolveError(meta) {
82
67
  if (!meta) return {};
@@ -168,6 +153,30 @@ var logger = {
168
153
  }
169
154
  };
170
155
 
156
+ // ../../providers/anthropic/src/anthropic.provider.ts
157
+ import Anthropic from "@anthropic-ai/sdk";
158
+
159
+ // ../../providers/anthropic/src/anthropic.models.ts
160
+ var claudeCodeSonnet = {
161
+ id: "claude-sonnet-4-5-20250929",
162
+ name: "Claude Code (Sonnet)",
163
+ provider: "claude-code",
164
+ maxTokens: { input: 2e5, output: 16384 },
165
+ cost: { multiplier: 20, inputTokens: 3, outputTokens: 15 }
166
+ };
167
+ var claudeCodeOpus = {
168
+ id: "claude-opus-4-6-20250515",
169
+ name: "Claude Code (Opus)",
170
+ provider: "claude-code",
171
+ maxTokens: { input: 2e5, output: 16384 },
172
+ cost: { multiplier: 50, inputTokens: 5, outputTokens: 25 }
173
+ };
174
+ var claudeCodeModels = {
175
+ claudeCodeSonnet,
176
+ claudeCodeOpus,
177
+ list: () => [claudeCodeSonnet, claudeCodeOpus]
178
+ };
179
+
171
180
  // ../../packages/types/src/agents/llm.types.ts
172
181
  function hasOutputFormat(output, format) {
173
182
  return output?.formats?.includes(format) ?? false;
@@ -2235,6 +2244,81 @@ function createWsProgressHandlers(deps) {
2235
2244
  return { onMessage, completeTools, waitForInput, handleUserInput, handleProgress };
2236
2245
  }
2237
2246
 
2247
+ // src/attachments.ts
2248
+ import fs3 from "fs/promises";
2249
+ import path3 from "path";
2250
+ var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/xml", "application/javascript"];
2251
+ var IMAGE_MIME_PREFIXES = ["image/"];
2252
+ function isTextFile(mimeType) {
2253
+ return TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p));
2254
+ }
2255
+ function isImageFile(mimeType) {
2256
+ return IMAGE_MIME_PREFIXES.some((p) => mimeType.startsWith(p));
2257
+ }
2258
+ function resolveUrl(url, baseUrl) {
2259
+ if (url.startsWith("http://") || url.startsWith("https://")) return url;
2260
+ const origin = baseUrl || process.env.APP_URL || "http://localhost:3000";
2261
+ return `${origin}${url}`;
2262
+ }
2263
+ async function resolveAttachments(attachments, workspacePath, baseUrl) {
2264
+ if (!attachments.length) return "";
2265
+ const parts = [];
2266
+ const uploadDir = path3.join(workspacePath, ".jarvis-uploads");
2267
+ for (const att of attachments) {
2268
+ try {
2269
+ const fullUrl = resolveUrl(att.url, baseUrl);
2270
+ if (isTextFile(att.mimeType)) {
2271
+ const res = await fetch(fullUrl);
2272
+ if (!res.ok) {
2273
+ logger.sys.warn("[Attachments] Failed to fetch text file", {
2274
+ fileName: att.fileName,
2275
+ status: res.status
2276
+ });
2277
+ continue;
2278
+ }
2279
+ const text = await res.text();
2280
+ parts.push(`<file name="${att.fileName}">
2281
+ ${text}
2282
+ </file>`);
2283
+ } else if (isImageFile(att.mimeType)) {
2284
+ await fs3.mkdir(uploadDir, { recursive: true });
2285
+ const localPath = path3.join(uploadDir, att.fileName);
2286
+ const res = await fetch(fullUrl);
2287
+ if (!res.ok) {
2288
+ logger.sys.warn("[Attachments] Failed to fetch image", {
2289
+ fileName: att.fileName,
2290
+ status: res.status
2291
+ });
2292
+ continue;
2293
+ }
2294
+ const buffer = Buffer.from(await res.arrayBuffer());
2295
+ await fs3.writeFile(localPath, buffer);
2296
+ parts.push(`[Image attached: ${localPath}]`);
2297
+ } else {
2298
+ await fs3.mkdir(uploadDir, { recursive: true });
2299
+ const localPath = path3.join(uploadDir, att.fileName);
2300
+ const res = await fetch(fullUrl);
2301
+ if (!res.ok) {
2302
+ logger.sys.warn("[Attachments] Failed to fetch file", {
2303
+ fileName: att.fileName,
2304
+ status: res.status
2305
+ });
2306
+ continue;
2307
+ }
2308
+ const buffer = Buffer.from(await res.arrayBuffer());
2309
+ await fs3.writeFile(localPath, buffer);
2310
+ parts.push(`[File attached: ${localPath}]`);
2311
+ }
2312
+ } catch (err) {
2313
+ logger.sys.warn("[Attachments] Error processing attachment", {
2314
+ fileName: att.fileName,
2315
+ error: err instanceof Error ? err.message : String(err)
2316
+ });
2317
+ }
2318
+ }
2319
+ return parts.length ? parts.join("\n\n") : "";
2320
+ }
2321
+
2238
2322
  // src/agent.ts
2239
2323
  function createAgent(deps) {
2240
2324
  const activeTasks = /* @__PURE__ */ new Map();
@@ -2253,6 +2337,27 @@ function createAgent(deps) {
2253
2337
  const wsPath = msg.worktreePath || deps.workspacePath;
2254
2338
  const systemMsg = msg.messages.find((m) => m.role === "system");
2255
2339
  const nonSystemMessages = msg.messages.filter((m) => m.role !== "system");
2340
+ if (msg.attachments?.length) {
2341
+ const attachmentContext = await resolveAttachments(msg.attachments, wsPath);
2342
+ if (attachmentContext) {
2343
+ let lastUserIdx = -1;
2344
+ for (let i = nonSystemMessages.length - 1; i >= 0; i--) {
2345
+ if (nonSystemMessages[i].role === "user") {
2346
+ lastUserIdx = i;
2347
+ break;
2348
+ }
2349
+ }
2350
+ if (lastUserIdx >= 0) {
2351
+ const orig = nonSystemMessages[lastUserIdx];
2352
+ nonSystemMessages[lastUserIdx] = {
2353
+ ...orig,
2354
+ content: `${attachmentContext}
2355
+
2356
+ ${orig.content}`
2357
+ };
2358
+ }
2359
+ }
2360
+ }
2256
2361
  logger.sys.info("[Agent] Running task", {
2257
2362
  taskId,
2258
2363
  model: msg.model,
@@ -2328,11 +2433,17 @@ function createAgent(deps) {
2328
2433
  });
2329
2434
  } catch (err) {
2330
2435
  if (abortController.signal.aborted) {
2331
- logger.sys.info("[Agent] Task aborted", { taskId });
2436
+ logger.sys.info("[Agent] Task interrupted", { taskId });
2332
2437
  deps.broadcast({
2333
2438
  type: "agent:output",
2334
2439
  taskId,
2335
- output: { success: false, content: "", error: "Aborted", sessionId: msg.session?.resume }
2440
+ output: {
2441
+ success: false,
2442
+ content: "",
2443
+ error: "Interrupted",
2444
+ interrupted: true,
2445
+ sessionId: msg.session?.resume
2446
+ }
2336
2447
  });
2337
2448
  } else {
2338
2449
  const error = err instanceof Error ? err.message : String(err);
@@ -2364,12 +2475,92 @@ function createAgent(deps) {
2364
2475
  return { runTask, cancelTask, isRunning, activeTaskCount };
2365
2476
  }
2366
2477
 
2478
+ // src/files.ts
2479
+ import { execFile } from "child_process";
2480
+ import { promisify as promisify2 } from "util";
2481
+ import path4 from "path";
2482
+ import fs4 from "fs/promises";
2483
+ var execFileAsync = promisify2(execFile);
2484
+ var MAX_RESULTS = 50;
2485
+ async function searchFiles(workspacePath, query) {
2486
+ try {
2487
+ const { stdout } = await execFileAsync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], {
2488
+ cwd: workspacePath,
2489
+ maxBuffer: 1024 * 1024
2490
+ });
2491
+ const allFiles = stdout.split("\n").filter(Boolean);
2492
+ const dirSet = /* @__PURE__ */ new Set();
2493
+ for (const f of allFiles) {
2494
+ const parts = f.split("/");
2495
+ for (let i = 1; i < parts.length; i++) {
2496
+ dirSet.add(parts.slice(0, i).join("/"));
2497
+ }
2498
+ }
2499
+ const allEntries = [
2500
+ ...[...dirSet].map((d) => ({ path: d, name: path4.basename(d), type: "directory" })),
2501
+ ...allFiles.map((f) => ({ path: f, name: path4.basename(f), type: "file" }))
2502
+ ];
2503
+ if (!query.trim()) {
2504
+ const sorted = [...allEntries].sort((a, b) => {
2505
+ const depthA = a.path.split("/").length;
2506
+ const depthB = b.path.split("/").length;
2507
+ if (depthA !== depthB) return depthA - depthB;
2508
+ if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
2509
+ return a.path.localeCompare(b.path);
2510
+ });
2511
+ return sorted.slice(0, MAX_RESULTS);
2512
+ }
2513
+ const lowerQuery = query.toLowerCase();
2514
+ const matched = allEntries.filter((e) => e.path.toLowerCase().includes(lowerQuery)).sort((a, b) => {
2515
+ const aExact = a.name.toLowerCase() === lowerQuery ? 0 : 1;
2516
+ const bExact = b.name.toLowerCase() === lowerQuery ? 0 : 1;
2517
+ if (aExact !== bExact) return aExact - bExact;
2518
+ if (a.type !== b.type) return a.type === "directory" ? -1 : 1;
2519
+ return a.path.localeCompare(b.path);
2520
+ }).slice(0, MAX_RESULTS);
2521
+ return matched;
2522
+ } catch (err) {
2523
+ logger.sys.warn("[Files] git ls-files failed, falling back to fs scan", {
2524
+ error: err instanceof Error ? err.message : String(err)
2525
+ });
2526
+ return fallbackSearch(workspacePath, query);
2527
+ }
2528
+ }
2529
+ async function fallbackSearch(workspacePath, query) {
2530
+ const results = [];
2531
+ const lowerQuery = query.toLowerCase();
2532
+ async function walk(dir, depth) {
2533
+ if (depth > 4 || results.length >= MAX_RESULTS) return;
2534
+ try {
2535
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
2536
+ for (const entry of entries) {
2537
+ if (results.length >= MAX_RESULTS) break;
2538
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
2539
+ const relPath = path4.relative(workspacePath, path4.join(dir, entry.name));
2540
+ if (relPath.toLowerCase().includes(lowerQuery)) {
2541
+ results.push({
2542
+ path: relPath,
2543
+ name: entry.name,
2544
+ type: entry.isDirectory() ? "directory" : "file"
2545
+ });
2546
+ }
2547
+ if (entry.isDirectory()) {
2548
+ await walk(path4.join(dir, entry.name), depth + 1);
2549
+ }
2550
+ }
2551
+ } catch {
2552
+ }
2553
+ }
2554
+ await walk(workspacePath, 0);
2555
+ return results;
2556
+ }
2557
+
2367
2558
  // src/git.ts
2368
2559
  import { exec as execCb2 } from "child_process";
2369
- import fs3 from "fs/promises";
2370
- import path3 from "path";
2371
- import { promisify as promisify2 } from "util";
2372
- var exec2 = promisify2(execCb2);
2560
+ import fs5 from "fs/promises";
2561
+ import path5 from "path";
2562
+ import { promisify as promisify3 } from "util";
2563
+ var exec2 = promisify3(execCb2);
2373
2564
  function createGitHandler(workspacePath, broadcast) {
2374
2565
  async function handle(msg) {
2375
2566
  switch (msg.type) {
@@ -2394,11 +2585,11 @@ function createGitHandler(workspacePath, broadcast) {
2394
2585
  }
2395
2586
  async function handleResolveRepo(msg) {
2396
2587
  try {
2397
- const repoDir = path3.join(workspacePath, msg.owner, msg.name);
2398
- const isCloned = await fs3.access(path3.join(repoDir, ".git")).then(() => true).catch(() => false);
2588
+ const repoDir = path5.join(workspacePath, msg.owner, msg.name);
2589
+ const isCloned = await fs5.access(path5.join(repoDir, ".git")).then(() => true).catch(() => false);
2399
2590
  if (!isCloned) {
2400
2591
  logger.sys.info("[Git] Cloning repo...", { repoDir });
2401
- await fs3.mkdir(path3.join(workspacePath, msg.owner), { recursive: true });
2592
+ await fs5.mkdir(path5.join(workspacePath, msg.owner), { recursive: true });
2402
2593
  const cloneUrl = `git@github.com:${msg.owner}/${msg.name}.git`;
2403
2594
  await exec2(`git clone "${cloneUrl}" "${repoDir}"`, { timeout: 3e5 });
2404
2595
  }
@@ -2561,7 +2752,7 @@ function createUpstreamClient(config) {
2561
2752
  if (!config.refreshToken) return null;
2562
2753
  return config.apiUrl.replace(/^wss:/, "https:").replace(/^ws:/, "http:") + "/refresh";
2563
2754
  }
2564
- function getTokenExpiry2(token) {
2755
+ function getTokenExpiry(token) {
2565
2756
  try {
2566
2757
  const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
2567
2758
  return payload.exp ?? null;
@@ -2572,7 +2763,7 @@ function createUpstreamClient(config) {
2572
2763
  function scheduleRefresh() {
2573
2764
  if (refreshTimer) clearTimeout(refreshTimer);
2574
2765
  if (!config.refreshToken) return;
2575
- const exp = getTokenExpiry2(currentToken);
2766
+ const exp = getTokenExpiry(currentToken);
2576
2767
  if (!exp) return;
2577
2768
  const msUntilExpiry = exp * 1e3 - Date.now();
2578
2769
  const refreshIn = Math.max(msUntilExpiry - REFRESH_BUFFER_MS, 0);
@@ -2759,6 +2950,18 @@ async function startAgent(options) {
2759
2950
  }
2760
2951
  break;
2761
2952
  }
2953
+ case "files:search": {
2954
+ const filesMsg = msg;
2955
+ searchFiles(options.workspacePath, filesMsg.query).then((files) => {
2956
+ broadcast({ type: "files:response", requestId: filesMsg.requestId, files });
2957
+ }).catch((err) => {
2958
+ logger.sys.error("[Agent] File search failed", {
2959
+ error: err instanceof Error ? err.message : String(err)
2960
+ });
2961
+ broadcast({ type: "files:response", requestId: filesMsg.requestId, files: [] });
2962
+ });
2963
+ break;
2964
+ }
2762
2965
  case "ping":
2763
2966
  break;
2764
2967
  }
@@ -2792,14 +2995,14 @@ import React6 from "react";
2792
2995
  import { render as render2 } from "ink";
2793
2996
  import { spawn } from "child_process";
2794
2997
  import { fileURLToPath } from "url";
2795
- import path6 from "path";
2796
- import fs6 from "fs";
2998
+ import path8 from "path";
2999
+ import fs8 from "fs";
2797
3000
  import os3 from "os";
2798
3001
  import WebSocket4 from "ws";
2799
3002
 
2800
3003
  // src/tui/settings.ts
2801
- import fs4 from "fs";
2802
- import path4 from "path";
3004
+ import fs6 from "fs";
3005
+ import path6 from "path";
2803
3006
  import os2 from "os";
2804
3007
  var DEFAULT_SETTINGS = {
2805
3008
  model: "claude-sonnet-4-20250514",
@@ -2809,11 +3012,11 @@ var DEFAULT_SETTINGS = {
2809
3012
  theme: "dark",
2810
3013
  disallowedTools: []
2811
3014
  };
2812
- var SETTINGS_DIR = path4.join(os2.homedir(), ".jarvis");
2813
- var SETTINGS_FILE = path4.join(SETTINGS_DIR, "settings.json");
3015
+ var SETTINGS_DIR = path6.join(os2.homedir(), ".jarvis");
3016
+ var SETTINGS_FILE = path6.join(SETTINGS_DIR, "settings.json");
2814
3017
  function loadSettings() {
2815
3018
  try {
2816
- const raw = fs4.readFileSync(SETTINGS_FILE, "utf-8");
3019
+ const raw = fs6.readFileSync(SETTINGS_FILE, "utf-8");
2817
3020
  const parsed = JSON.parse(raw);
2818
3021
  return { ...DEFAULT_SETTINGS, ...parsed };
2819
3022
  } catch {
@@ -2821,8 +3024,8 @@ function loadSettings() {
2821
3024
  }
2822
3025
  }
2823
3026
  function saveSettings(settings) {
2824
- fs4.mkdirSync(SETTINGS_DIR, { recursive: true });
2825
- fs4.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
3027
+ fs6.mkdirSync(SETTINGS_DIR, { recursive: true });
3028
+ fs6.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n");
2826
3029
  }
2827
3030
  function mergeCliFlags(settings, flags) {
2828
3031
  const merged = { ...settings };
@@ -3532,8 +3735,8 @@ function Suggestions({
3532
3735
 
3533
3736
  // src/tui/components/file-picker.tsx
3534
3737
  import { Box as Box6, Text as Text6 } from "ink";
3535
- import path5 from "path";
3536
- import fs5 from "fs";
3738
+ import path7 from "path";
3739
+ import fs7 from "fs";
3537
3740
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
3538
3741
  var cachedFiles = null;
3539
3742
  function scanFiles(workspacePath) {
@@ -3554,11 +3757,11 @@ function scanFiles(workspacePath) {
3554
3757
  function walk(dir, depth) {
3555
3758
  if (depth > 4) return;
3556
3759
  try {
3557
- const entries = fs5.readdirSync(dir, { withFileTypes: true });
3760
+ const entries = fs7.readdirSync(dir, { withFileTypes: true });
3558
3761
  for (const entry of entries) {
3559
3762
  if (ignoreSet.has(entry.name) || entry.name.startsWith(".")) continue;
3560
- const full = path5.join(dir, entry.name);
3561
- const rel = path5.relative(workspacePath, full);
3763
+ const full = path7.join(dir, entry.name);
3764
+ const rel = path7.relative(workspacePath, full);
3562
3765
  if (entry.isDirectory()) {
3563
3766
  results.push(rel + "/");
3564
3767
  walk(full, depth + 1);
@@ -4747,13 +4950,13 @@ async function launchChat(opts) {
4747
4950
  }
4748
4951
  function spawnAgentProcess(port, workspacePath) {
4749
4952
  const config = loadConfig();
4750
- const logDir = path6.join(os3.homedir(), ".jarvis");
4751
- fs6.mkdirSync(logDir, { recursive: true });
4752
- const logFile = path6.join(logDir, "agent.log");
4753
- const logFd = fs6.openSync(logFile, "a");
4953
+ const logDir = path8.join(os3.homedir(), ".jarvis");
4954
+ fs8.mkdirSync(logDir, { recursive: true });
4955
+ const logFile = path8.join(logDir, "agent.log");
4956
+ const logFd = fs8.openSync(logFile, "a");
4754
4957
  const __filename = fileURLToPath(import.meta.url);
4755
- const cliRoot = path6.resolve(path6.dirname(__filename), "../..");
4756
- const binPath = path6.join(cliRoot, "bin", "jarvis.mjs");
4958
+ const cliRoot = path8.resolve(path8.dirname(__filename), "../..");
4959
+ const binPath = path8.join(cliRoot, "bin", "jarvis.mjs");
4757
4960
  const args = ["start", "--port", String(port), "--workspace", workspacePath];
4758
4961
  if (config?.apiUrl && config?.token) {
4759
4962
  } else {
@@ -4768,7 +4971,7 @@ function spawnAgentProcess(port, workspacePath) {
4768
4971
  cwd: workspacePath,
4769
4972
  env: { ...process.env }
4770
4973
  });
4771
- fs6.closeSync(logFd);
4974
+ fs8.closeSync(logFd);
4772
4975
  }
4773
4976
  async function checkAgentRunning(port) {
4774
4977
  return new Promise((resolve) => {
@@ -4805,8 +5008,8 @@ async function waitForAgent(port, maxAttempts = 30) {
4805
5008
 
4806
5009
  // src/service.ts
4807
5010
  import { execSync, spawn as spawn2 } from "child_process";
4808
- import fs7 from "fs";
4809
- import path7 from "path";
5011
+ import fs9 from "fs";
5012
+ import path9 from "path";
4810
5013
  import os4 from "os";
4811
5014
  function createServiceManager() {
4812
5015
  if (process.platform === "darwin") return new MacOSService();
@@ -4815,8 +5018,8 @@ function createServiceManager() {
4815
5018
  return new FallbackService();
4816
5019
  }
4817
5020
  var PLIST_LABEL = "com.appchy.jarvis";
4818
- var PLIST_DIR = path7.join(os4.homedir(), "Library", "LaunchAgents");
4819
- var PLIST_PATH = path7.join(PLIST_DIR, `${PLIST_LABEL}.plist`);
5021
+ var PLIST_DIR = path9.join(os4.homedir(), "Library", "LaunchAgents");
5022
+ var PLIST_PATH = path9.join(PLIST_DIR, `${PLIST_LABEL}.plist`);
4820
5023
  function escapeXml(s) {
4821
5024
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
4822
5025
  }
@@ -4835,7 +5038,7 @@ var MacOSService = class {
4835
5038
  if (!opts.upstream) {
4836
5039
  args.push("--no-upstream");
4837
5040
  }
4838
- const logFile = path7.join(os4.homedir(), ".jarvis", "agent.log");
5041
+ const logFile = path9.join(os4.homedir(), ".jarvis", "agent.log");
4839
5042
  const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
4840
5043
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
4841
5044
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -4860,6 +5063,8 @@ ${args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n")}
4860
5063
  <string>${escapeXml(os4.homedir())}</string>
4861
5064
  <key>NODE_NO_WARNINGS</key>
4862
5065
  <string>1</string>
5066
+ ${Object.entries(opts.env ?? {}).map(([k, v]) => ` <key>${escapeXml(k)}</key>
5067
+ <string>${escapeXml(v)}</string>`).join("\n")}
4863
5068
  </dict>
4864
5069
 
4865
5070
  <key>RunAtLoad</key>
@@ -4882,15 +5087,15 @@ ${args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n")}
4882
5087
  </dict>
4883
5088
  </plist>
4884
5089
  `;
4885
- fs7.mkdirSync(PLIST_DIR, { recursive: true });
4886
- fs7.mkdirSync(path7.dirname(logFile), { recursive: true });
5090
+ fs9.mkdirSync(PLIST_DIR, { recursive: true });
5091
+ fs9.mkdirSync(path9.dirname(logFile), { recursive: true });
4887
5092
  if (this.isInstalled()) {
4888
5093
  try {
4889
5094
  execSync(`launchctl unload -w "${PLIST_PATH}"`, { stdio: "ignore" });
4890
5095
  } catch {
4891
5096
  }
4892
5097
  }
4893
- fs7.writeFileSync(PLIST_PATH, plist);
5098
+ fs9.writeFileSync(PLIST_PATH, plist);
4894
5099
  execSync(`launchctl load -w "${PLIST_PATH}"`);
4895
5100
  }
4896
5101
  uninstall() {
@@ -4899,12 +5104,12 @@ ${args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n")}
4899
5104
  } catch {
4900
5105
  }
4901
5106
  try {
4902
- fs7.unlinkSync(PLIST_PATH);
5107
+ fs9.unlinkSync(PLIST_PATH);
4903
5108
  } catch {
4904
5109
  }
4905
5110
  }
4906
5111
  isInstalled() {
4907
- return fs7.existsSync(PLIST_PATH);
5112
+ return fs9.existsSync(PLIST_PATH);
4908
5113
  }
4909
5114
  start() {
4910
5115
  try {
@@ -4994,15 +5199,15 @@ var WindowsService = class {
4994
5199
  </Task>
4995
5200
  `;
4996
5201
  const tmpDir = os4.tmpdir();
4997
- const tmpFile = path7.join(tmpDir, `jarvis-task-${Date.now()}.xml`);
4998
- fs7.writeFileSync(tmpFile, xml, { encoding: "utf-16le" });
5202
+ const tmpFile = path9.join(tmpDir, `jarvis-task-${Date.now()}.xml`);
5203
+ fs9.writeFileSync(tmpFile, xml, { encoding: "utf-16le" });
4999
5204
  try {
5000
5205
  execSync(`schtasks /Create /TN "${TASK_NAME}" /XML "${tmpFile}" /F`, {
5001
5206
  stdio: "ignore"
5002
5207
  });
5003
5208
  } finally {
5004
5209
  try {
5005
- fs7.unlinkSync(tmpFile);
5210
+ fs9.unlinkSync(tmpFile);
5006
5211
  } catch {
5007
5212
  }
5008
5213
  }
@@ -5040,9 +5245,9 @@ var WindowsService = class {
5040
5245
  }
5041
5246
  }
5042
5247
  };
5043
- var SYSTEMD_DIR = path7.join(os4.homedir(), ".config", "systemd", "user");
5248
+ var SYSTEMD_DIR = path9.join(os4.homedir(), ".config", "systemd", "user");
5044
5249
  var UNIT_NAME = "jarvis.service";
5045
- var UNIT_PATH = path7.join(SYSTEMD_DIR, UNIT_NAME);
5250
+ var UNIT_PATH = path9.join(SYSTEMD_DIR, UNIT_NAME);
5046
5251
  var LinuxService = class {
5047
5252
  install(opts) {
5048
5253
  const args = [
@@ -5057,7 +5262,7 @@ var LinuxService = class {
5057
5262
  if (!opts.upstream) {
5058
5263
  args.push("--no-upstream");
5059
5264
  }
5060
- const logFile = path7.join(os4.homedir(), ".jarvis", "agent.log");
5265
+ const logFile = path9.join(os4.homedir(), ".jarvis", "agent.log");
5061
5266
  const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
5062
5267
  const unit = `[Unit]
5063
5268
  Description=Jarvis AI Agent
@@ -5078,15 +5283,15 @@ StandardError=append:${logFile}
5078
5283
  [Install]
5079
5284
  WantedBy=default.target
5080
5285
  `;
5081
- fs7.mkdirSync(SYSTEMD_DIR, { recursive: true });
5082
- fs7.mkdirSync(path7.dirname(logFile), { recursive: true });
5286
+ fs9.mkdirSync(SYSTEMD_DIR, { recursive: true });
5287
+ fs9.mkdirSync(path9.dirname(logFile), { recursive: true });
5083
5288
  if (this.isInstalled()) {
5084
5289
  try {
5085
5290
  execSync("systemctl --user stop jarvis.service", { stdio: "ignore" });
5086
5291
  } catch {
5087
5292
  }
5088
5293
  }
5089
- fs7.writeFileSync(UNIT_PATH, unit);
5294
+ fs9.writeFileSync(UNIT_PATH, unit);
5090
5295
  execSync("systemctl --user daemon-reload", { stdio: "ignore" });
5091
5296
  execSync("systemctl --user enable jarvis.service", { stdio: "ignore" });
5092
5297
  execSync("systemctl --user start jarvis.service", { stdio: "ignore" });
@@ -5105,7 +5310,7 @@ WantedBy=default.target
5105
5310
  } catch {
5106
5311
  }
5107
5312
  try {
5108
- fs7.unlinkSync(UNIT_PATH);
5313
+ fs9.unlinkSync(UNIT_PATH);
5109
5314
  } catch {
5110
5315
  }
5111
5316
  try {
@@ -5114,7 +5319,7 @@ WantedBy=default.target
5114
5319
  }
5115
5320
  }
5116
5321
  isInstalled() {
5117
- return fs7.existsSync(UNIT_PATH);
5322
+ return fs9.existsSync(UNIT_PATH);
5118
5323
  }
5119
5324
  start() {
5120
5325
  execSync("systemctl --user start jarvis.service");
@@ -5144,13 +5349,13 @@ WantedBy=default.target
5144
5349
  };
5145
5350
  var FallbackService = class {
5146
5351
  constructor() {
5147
- this.pidFile = path7.join(os4.homedir(), ".jarvis", "agent.pid");
5148
- this.logFile = path7.join(os4.homedir(), ".jarvis", "agent.log");
5149
- this.markerFile = path7.join(os4.homedir(), ".jarvis", "service-installed");
5352
+ this.pidFile = path9.join(os4.homedir(), ".jarvis", "agent.pid");
5353
+ this.logFile = path9.join(os4.homedir(), ".jarvis", "agent.log");
5354
+ this.markerFile = path9.join(os4.homedir(), ".jarvis", "service-installed");
5150
5355
  }
5151
5356
  install(opts) {
5152
- const jarvisDir = path7.join(os4.homedir(), ".jarvis");
5153
- fs7.mkdirSync(jarvisDir, { recursive: true });
5357
+ const jarvisDir = path9.join(os4.homedir(), ".jarvis");
5358
+ fs9.mkdirSync(jarvisDir, { recursive: true });
5154
5359
  this.stop();
5155
5360
  const args = [
5156
5361
  opts.entryPath,
@@ -5164,7 +5369,7 @@ var FallbackService = class {
5164
5369
  if (!opts.upstream) {
5165
5370
  args.push("--no-upstream");
5166
5371
  }
5167
- const logFd = fs7.openSync(this.logFile, "a");
5372
+ const logFd = fs9.openSync(this.logFile, "a");
5168
5373
  const child = spawn2(opts.nodePath, args, {
5169
5374
  detached: true,
5170
5375
  stdio: ["ignore", logFd, logFd],
@@ -5172,9 +5377,9 @@ var FallbackService = class {
5172
5377
  env: { ...process.env, NODE_NO_WARNINGS: "1" }
5173
5378
  });
5174
5379
  child.unref();
5175
- fs7.closeSync(logFd);
5176
- fs7.writeFileSync(this.pidFile, String(child.pid));
5177
- fs7.writeFileSync(this.markerFile, JSON.stringify({
5380
+ fs9.closeSync(logFd);
5381
+ fs9.writeFileSync(this.pidFile, String(child.pid));
5382
+ fs9.writeFileSync(this.markerFile, JSON.stringify({
5178
5383
  nodePath: opts.nodePath,
5179
5384
  entryPath: opts.entryPath,
5180
5385
  port: opts.port,
@@ -5190,37 +5395,37 @@ var FallbackService = class {
5190
5395
  uninstall() {
5191
5396
  this.stop();
5192
5397
  try {
5193
- fs7.unlinkSync(this.markerFile);
5398
+ fs9.unlinkSync(this.markerFile);
5194
5399
  } catch {
5195
5400
  }
5196
5401
  }
5197
5402
  isInstalled() {
5198
- return fs7.existsSync(this.markerFile);
5403
+ return fs9.existsSync(this.markerFile);
5199
5404
  }
5200
5405
  start() {
5201
5406
  if (!this.isInstalled()) {
5202
5407
  throw new Error("Service not installed. Run 'jarvis install' first.");
5203
5408
  }
5204
- const saved = JSON.parse(fs7.readFileSync(this.markerFile, "utf-8"));
5409
+ const saved = JSON.parse(fs9.readFileSync(this.markerFile, "utf-8"));
5205
5410
  this.install(saved);
5206
5411
  }
5207
5412
  stop() {
5208
5413
  try {
5209
- const pid = parseInt(fs7.readFileSync(this.pidFile, "utf-8").trim(), 10);
5414
+ const pid = parseInt(fs9.readFileSync(this.pidFile, "utf-8").trim(), 10);
5210
5415
  if (!isNaN(pid)) {
5211
5416
  process.kill(pid, "SIGTERM");
5212
5417
  }
5213
5418
  } catch {
5214
5419
  }
5215
5420
  try {
5216
- fs7.unlinkSync(this.pidFile);
5421
+ fs9.unlinkSync(this.pidFile);
5217
5422
  } catch {
5218
5423
  }
5219
5424
  }
5220
5425
  status() {
5221
5426
  if (!this.isInstalled()) return { installed: false, running: false };
5222
5427
  try {
5223
- const pid = parseInt(fs7.readFileSync(this.pidFile, "utf-8").trim(), 10);
5428
+ const pid = parseInt(fs9.readFileSync(this.pidFile, "utf-8").trim(), 10);
5224
5429
  if (isNaN(pid)) return { installed: true, running: false };
5225
5430
  process.kill(pid, 0);
5226
5431
  return { installed: true, running: true, pid };
@@ -5234,22 +5439,18 @@ var FallbackService = class {
5234
5439
  import { createRequire } from "module";
5235
5440
  var _require = createRequire(import.meta.url);
5236
5441
  var PKG_VERSION = _require("../package.json").version ?? "dev";
5237
- var LOG_DIR = path8.join(os5.homedir(), ".jarvis");
5238
- var LOG_FILE = path8.join(LOG_DIR, "agent.log");
5239
- var PID_FILE = path8.join(LOG_DIR, "agent.pid");
5240
- function savePid(pid) {
5241
- fs8.mkdirSync(LOG_DIR, { recursive: true });
5242
- fs8.writeFileSync(PID_FILE, String(pid));
5243
- }
5442
+ var LOG_DIR = path10.join(os5.homedir(), ".jarvis");
5443
+ var LOG_FILE = path10.join(LOG_DIR, "agent.log");
5444
+ var PID_FILE = path10.join(LOG_DIR, "agent.pid");
5244
5445
  function readPid() {
5245
5446
  try {
5246
- const pid = parseInt(fs8.readFileSync(PID_FILE, "utf-8").trim(), 10);
5447
+ const pid = parseInt(fs10.readFileSync(PID_FILE, "utf-8").trim(), 10);
5247
5448
  if (isNaN(pid)) return null;
5248
5449
  try {
5249
5450
  process.kill(pid, 0);
5250
5451
  return pid;
5251
5452
  } catch {
5252
- fs8.unlinkSync(PID_FILE);
5453
+ fs10.unlinkSync(PID_FILE);
5253
5454
  return null;
5254
5455
  }
5255
5456
  } catch {
@@ -5258,7 +5459,7 @@ function readPid() {
5258
5459
  }
5259
5460
  function clearPid() {
5260
5461
  try {
5261
- fs8.unlinkSync(PID_FILE);
5462
+ fs10.unlinkSync(PID_FILE);
5262
5463
  } catch {
5263
5464
  }
5264
5465
  }
@@ -5308,7 +5509,7 @@ async function findFreePort() {
5308
5509
  async function browserAuth(appUrl) {
5309
5510
  const config = loadConfig();
5310
5511
  const callbackPort = await findFreePort();
5311
- const loginUrl = `${appUrl}/api/auth/cli?port=${callbackPort}`;
5512
+ const loginUrl = `${appUrl}/auth/cli?port=${callbackPort}`;
5312
5513
  console.log();
5313
5514
  console.log("Opening browser for sign in...");
5314
5515
  console.log(` If it doesn't open, visit: ${loginUrl}`);
@@ -5339,57 +5540,17 @@ async function browserAuth(appUrl) {
5339
5540
  }
5340
5541
  }
5341
5542
  function getAppUrl() {
5342
- return process.env.APP_URL ?? "https://jarvis.appchy.com" ?? "https://jarvis.appchy.com";
5343
- }
5344
- function getTokenExpiry(token) {
5345
- try {
5346
- const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
5347
- return payload.exp ?? null;
5348
- } catch {
5349
- return null;
5350
- }
5351
- }
5352
- function isTokenExpired(token, bufferMs = 6e4) {
5353
- const exp = getTokenExpiry(token);
5354
- if (!exp) return true;
5355
- return exp * 1e3 - Date.now() < bufferMs;
5356
- }
5357
- async function tryRefreshToken(config) {
5358
- if (!config.apiUrl || !config.refreshToken) return null;
5359
- const refreshUrl = config.apiUrl.replace(/^wss:/, "https:").replace(/^ws:/, "http:") + "/refresh";
5360
- try {
5361
- const res = await fetch(refreshUrl, {
5362
- method: "POST",
5363
- headers: { "Content-Type": "application/json" },
5364
- body: JSON.stringify({ refreshToken: config.refreshToken })
5365
- });
5366
- if (!res.ok) return null;
5367
- const { token } = await res.json();
5368
- return token;
5369
- } catch {
5370
- return null;
5371
- }
5543
+ if ("https://jarvis.appchy.com") return "https://jarvis.appchy.com";
5544
+ if (process.env.APP_URL) return process.env.APP_URL;
5545
+ return "https://jarvis.appchy.com";
5372
5546
  }
5373
5547
  async function ensureSetup() {
5374
5548
  const config = loadConfig();
5375
- if (!config?.token && !config?.anthropicApiKey && !config?.useSubscription) {
5376
- return browserAuth(getAppUrl());
5377
- }
5378
5549
  if (config?.anthropicApiKey || config?.useSubscription) {
5379
5550
  return true;
5380
5551
  }
5381
- if (config?.token && isTokenExpired(config.token)) {
5382
- console.log("Token expired, attempting refresh...");
5383
- const newToken = await tryRefreshToken(config);
5384
- if (newToken) {
5385
- saveConfig({ ...config, token: newToken, connectedAt: (/* @__PURE__ */ new Date()).toISOString() });
5386
- console.log("Token refreshed successfully.");
5387
- return true;
5388
- }
5389
- console.log("Token refresh failed. Re-authenticating...");
5390
- return browserAuth(getAppUrl());
5391
- }
5392
- return true;
5552
+ clearConfig();
5553
+ return browserAuth(getAppUrl());
5393
5554
  }
5394
5555
  function createCli() {
5395
5556
  const program = new Command().name("jarvis").description("Jarvis local agent \u2014 runs Claude Code on your machine").version(PKG_VERSION);
@@ -5459,13 +5620,9 @@ function createCli() {
5459
5620
  const service = createServiceManager();
5460
5621
  if (service.isInstalled()) {
5461
5622
  try {
5462
- service.stop();
5623
+ service.uninstall();
5463
5624
  } catch {
5464
5625
  }
5465
- await new Promise((r) => setTimeout(r, 1e3));
5466
- service.start();
5467
- console.log("Jarvis agent started via OS service.");
5468
- return;
5469
5626
  }
5470
5627
  const stalePid = readPid();
5471
5628
  if (stalePid) {
@@ -5475,13 +5632,13 @@ function createCli() {
5475
5632
  }
5476
5633
  clearPid();
5477
5634
  }
5478
- if (await isPortInUse(port)) {
5635
+ const killPort = async (p) => {
5479
5636
  try {
5480
- const output = execSync2(`lsof -ti :${port}`, { encoding: "utf-8" }).trim();
5637
+ const output = execSync2(`lsof -ti :${p}`, { encoding: "utf-8" }).trim();
5481
5638
  if (output) {
5482
- for (const p of output.split("\n")) {
5639
+ for (const pid of output.split("\n")) {
5483
5640
  try {
5484
- process.kill(parseInt(p, 10), "SIGTERM");
5641
+ process.kill(parseInt(pid, 10), "SIGTERM");
5485
5642
  } catch {
5486
5643
  }
5487
5644
  }
@@ -5489,52 +5646,66 @@ function createCli() {
5489
5646
  } catch {
5490
5647
  }
5491
5648
  await new Promise((r) => setTimeout(r, 1e3));
5492
- }
5493
- fs8.mkdirSync(LOG_DIR, { recursive: true });
5494
- const logFd = fs8.openSync(LOG_FILE, "a");
5495
- const args = [
5496
- "start",
5497
- "--foreground",
5498
- "--port",
5499
- String(port),
5500
- "--workspace",
5501
- workspacePath
5502
- ];
5503
- if (!opts.upstream) {
5504
- args.push("--no-upstream");
5505
- }
5506
- if (anthropicApiKey) {
5507
- args.push("--api-key", anthropicApiKey);
5508
- }
5509
- const binPath = process.argv[1];
5510
- const cliRoot = path8.resolve(path8.dirname(binPath), "..");
5511
- const distEntry = path8.join(cliRoot, "dist", "bin.js");
5512
- const useDistEntry = fs8.existsSync(distEntry);
5513
- const child = spawn3(
5514
- process.execPath,
5515
- useDistEntry ? [distEntry, ...args] : [binPath, ...args],
5516
- {
5517
- detached: true,
5518
- stdio: ["ignore", logFd, logFd],
5519
- cwd: workspacePath,
5520
- env: {
5521
- ...process.env,
5522
- NODE_NO_WARNINGS: "1",
5523
- // Strip API key when using subscription to prevent SDK from picking it up
5524
- ...useSubscription ? { ANTHROPIC_API_KEY: "" } : {}
5649
+ if (await isPortInUse(p)) {
5650
+ try {
5651
+ const output = execSync2(`lsof -ti :${p}`, { encoding: "utf-8" }).trim();
5652
+ if (output) {
5653
+ for (const pid of output.split("\n")) {
5654
+ try {
5655
+ process.kill(parseInt(pid, 10), "SIGKILL");
5656
+ } catch {
5657
+ }
5658
+ }
5659
+ }
5660
+ } catch {
5525
5661
  }
5662
+ await new Promise((r) => setTimeout(r, 1e3));
5526
5663
  }
5527
- );
5528
- child.unref();
5529
- fs8.closeSync(logFd);
5530
- savePid(child.pid);
5531
- console.log(`Jarvis agent started (PID: ${child.pid})`);
5664
+ };
5665
+ await killPort(port);
5666
+ const binPath = process.argv[1];
5667
+ const cliRoot = path10.resolve(path10.dirname(binPath), "..");
5668
+ const distEntry = path10.join(cliRoot, "dist", "bin.js");
5669
+ const entryPath = fs10.existsSync(distEntry) ? distEntry : binPath;
5670
+ const serviceEnv = {};
5671
+ if (process.env.APP_URL) serviceEnv.APP_URL = process.env.APP_URL;
5672
+ if ("https://jarvis.appchy.com") serviceEnv.JARVIS_APP_URL = "https://jarvis.appchy.com";
5673
+ if (process.env.JARVIS_CONFIG_DIR) serviceEnv.JARVIS_CONFIG_DIR = process.env.JARVIS_CONFIG_DIR;
5674
+ if (process.env.JARVIS_USER_ID) serviceEnv.JARVIS_USER_ID = process.env.JARVIS_USER_ID;
5675
+ if (process.env.JARVIS_ENV_ID) serviceEnv.JARVIS_ENV_ID = process.env.JARVIS_ENV_ID;
5676
+ service.install({
5677
+ port,
5678
+ workspacePath,
5679
+ nodePath: process.execPath,
5680
+ entryPath,
5681
+ upstream: opts.upstream,
5682
+ ...Object.keys(serviceEnv).length > 0 ? { env: serviceEnv } : {}
5683
+ });
5684
+ const startTime = Date.now();
5685
+ let agentReady = false;
5686
+ while (Date.now() - startTime < 15e3) {
5687
+ if (await isPortInUse(port)) {
5688
+ agentReady = true;
5689
+ break;
5690
+ }
5691
+ await new Promise((r) => setTimeout(r, 500));
5692
+ }
5693
+ if (!agentReady) {
5694
+ console.error("Agent failed to start. Check logs:");
5695
+ console.error(` ${LOG_FILE}`);
5696
+ process.exit(1);
5697
+ }
5698
+ const status = service.status();
5699
+ console.log(`Jarvis agent started`);
5532
5700
  console.log(` Local: ws://127.0.0.1:${port}`);
5533
5701
  console.log(` Workspace: ${workspacePath}`);
5534
5702
  console.log(` Logs: ${LOG_FILE}`);
5535
5703
  if (config?.apiUrl) {
5536
5704
  console.log(` Cloud: ${config.apiUrl}`);
5537
5705
  }
5706
+ if (status.os) {
5707
+ console.log(` Service: ${status.os} (auto-start on login, crash recovery)`);
5708
+ }
5538
5709
  }
5539
5710
  );
5540
5711
  program.command("stop").description("Stop the running agent").action(async () => {
@@ -5582,7 +5753,7 @@ function createCli() {
5582
5753
  await program.parseAsync(["node", "jarvis", "start"]);
5583
5754
  });
5584
5755
  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) => {
5585
- if (!fs8.existsSync(LOG_FILE)) {
5756
+ if (!fs10.existsSync(LOG_FILE)) {
5586
5757
  console.log("No log file found. Start the agent first: jarvis start");
5587
5758
  return;
5588
5759
  }
@@ -5640,10 +5811,10 @@ function createCli() {
5640
5811
  const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
5641
5812
  const service = createServiceManager();
5642
5813
  const binPath = process.argv[1];
5643
- const cliRoot = path8.resolve(path8.dirname(binPath), "..");
5644
- const distEntry = path8.join(cliRoot, "dist", "bin.js");
5645
- const entryPath = fs8.existsSync(distEntry) ? distEntry : binPath;
5646
- if (!fs8.existsSync(distEntry)) {
5814
+ const cliRoot = path10.resolve(path10.dirname(binPath), "..");
5815
+ const distEntry = path10.join(cliRoot, "dist", "bin.js");
5816
+ const entryPath = fs10.existsSync(distEntry) ? distEntry : binPath;
5817
+ if (!fs10.existsSync(distEntry)) {
5647
5818
  console.log("\x1B[33mWarning:\x1B[0m Using dev mode entry point (tsx).");
5648
5819
  console.log(" For reliability, build first: pnpm build");
5649
5820
  console.log();
@@ -5656,13 +5827,20 @@ function createCli() {
5656
5827
  }
5657
5828
  clearPid();
5658
5829
  }
5830
+ const installEnv = {};
5831
+ if (process.env.APP_URL) installEnv.APP_URL = process.env.APP_URL;
5832
+ if ("https://jarvis.appchy.com") installEnv.JARVIS_APP_URL = "https://jarvis.appchy.com";
5833
+ if (process.env.JARVIS_CONFIG_DIR) installEnv.JARVIS_CONFIG_DIR = process.env.JARVIS_CONFIG_DIR;
5834
+ if (process.env.JARVIS_USER_ID) installEnv.JARVIS_USER_ID = process.env.JARVIS_USER_ID;
5835
+ if (process.env.JARVIS_ENV_ID) installEnv.JARVIS_ENV_ID = process.env.JARVIS_ENV_ID;
5659
5836
  try {
5660
5837
  service.install({
5661
5838
  port,
5662
5839
  workspacePath,
5663
5840
  nodePath: process.execPath,
5664
5841
  entryPath,
5665
- upstream: opts.upstream
5842
+ upstream: opts.upstream,
5843
+ ...Object.keys(installEnv).length > 0 ? { env: installEnv } : {}
5666
5844
  });
5667
5845
  } catch (err) {
5668
5846
  console.error(`Failed to install service: ${err instanceof Error ? err.message : err}`);
@@ -5709,10 +5887,10 @@ function createCli() {
5709
5887
  // src/bin.ts
5710
5888
  function findEnv() {
5711
5889
  let dir = process.cwd();
5712
- while (dir !== path9.dirname(dir)) {
5713
- const envPath = path9.join(dir, ".env");
5714
- if (fs9.existsSync(envPath)) return envPath;
5715
- dir = path9.dirname(dir);
5890
+ while (dir !== path11.dirname(dir)) {
5891
+ const envPath = path11.join(dir, ".env");
5892
+ if (fs11.existsSync(envPath)) return envPath;
5893
+ dir = path11.dirname(dir);
5716
5894
  }
5717
5895
  return void 0;
5718
5896
  }