@appchy/jarvis 0.1.18 → 0.1.20
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 +264 -122
- package/dist/bin.js.map +1 -1
- package/dist/env.json +1 -0
- package/package.json +4 -4
package/dist/bin.js
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
// src/bin.ts
|
|
2
|
-
import dotenv from "dotenv";
|
|
3
|
-
import fs11 from "fs";
|
|
4
|
-
import path11 from "path";
|
|
5
|
-
|
|
6
|
-
// src/cli.ts
|
|
7
|
-
import { Command } from "commander";
|
|
8
|
-
import { spawn as spawn3, execSync as execSync2 } from "child_process";
|
|
9
|
-
import fs10 from "fs";
|
|
10
|
-
import path10 from "path";
|
|
11
|
-
import os5 from "os";
|
|
12
|
-
|
|
13
1
|
// src/config.ts
|
|
14
2
|
import fs from "fs";
|
|
15
3
|
import path from "path";
|
|
16
4
|
import os from "os";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
var __dirname = path.dirname(__filename);
|
|
8
|
+
function isDev() {
|
|
9
|
+
try {
|
|
10
|
+
const envFile = path.join(__dirname, "..", "env.json");
|
|
11
|
+
const data = JSON.parse(fs.readFileSync(envFile, "utf-8"));
|
|
12
|
+
return data.env === "development";
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
var PROD_APP_URL = "https://jarvis.appchy.com";
|
|
18
|
+
var DEV_APP_URL = "http://localhost:3000";
|
|
19
|
+
function getDefaultAppUrl() {
|
|
20
|
+
return isDev() ? process.env.APP_URL || DEV_APP_URL : PROD_APP_URL;
|
|
21
|
+
}
|
|
17
22
|
function resolveConfigDir() {
|
|
18
23
|
if (process.env.JARVIS_CONFIG_DIR) return process.env.JARVIS_CONFIG_DIR;
|
|
19
24
|
const base = path.join(os.homedir(), ".jarvis");
|
|
20
|
-
|
|
21
|
-
if (appUrl && !appUrl.includes("appchy.com") && appUrl !== "") {
|
|
22
|
-
return path.join(base, "dev");
|
|
23
|
-
}
|
|
24
|
-
return base;
|
|
25
|
+
return isDev() ? path.join(base, "dev") : base;
|
|
25
26
|
}
|
|
26
27
|
var CONFIG_DIR = resolveConfigDir();
|
|
27
28
|
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
@@ -62,6 +63,13 @@ function getConfigPath() {
|
|
|
62
63
|
return CONFIG_FILE;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
// src/cli.ts
|
|
67
|
+
import { Command } from "commander";
|
|
68
|
+
import { spawn as spawn3, execSync as execSync2 } from "child_process";
|
|
69
|
+
import fs10 from "fs";
|
|
70
|
+
import path10 from "path";
|
|
71
|
+
import os6 from "os";
|
|
72
|
+
|
|
65
73
|
// ../../packages/logger/src/logger.ts
|
|
66
74
|
function resolveError(meta) {
|
|
67
75
|
if (!meta) return {};
|
|
@@ -1982,42 +1990,58 @@ function formatToolDescription(toolName, input) {
|
|
|
1982
1990
|
// ../../packages/agent/src/worktree.ts
|
|
1983
1991
|
import { exec as execCb } from "child_process";
|
|
1984
1992
|
import fs2 from "fs/promises";
|
|
1993
|
+
import os2 from "os";
|
|
1985
1994
|
import path2 from "path";
|
|
1986
1995
|
import { promisify } from "util";
|
|
1987
1996
|
var exec = promisify(execCb);
|
|
1988
|
-
var
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
await
|
|
1999
|
-
|
|
1997
|
+
var DEFAULT_WORKTREE_DIR = path2.join(os2.homedir(), ".jarvis", "worktrees");
|
|
1998
|
+
function resolveDir(worktreeDir) {
|
|
1999
|
+
if (!worktreeDir) return DEFAULT_WORKTREE_DIR;
|
|
2000
|
+
return worktreeDir.replace(/^~/, os2.homedir());
|
|
2001
|
+
}
|
|
2002
|
+
async function createWorktree(req) {
|
|
2003
|
+
const branchName = `jarvis/task-${req.taskId}`;
|
|
2004
|
+
const baseDir = resolveDir(req.worktreeDir);
|
|
2005
|
+
const worktreePath = path2.join(baseDir, `task-${req.taskId}`);
|
|
2006
|
+
await fs2.mkdir(path2.dirname(worktreePath), { recursive: true });
|
|
2007
|
+
if (await worktreeExists({ taskId: req.taskId, worktreeDir: req.worktreeDir })) {
|
|
2008
|
+
return { worktreePath, branchName };
|
|
2009
|
+
}
|
|
2010
|
+
const startPoint = req.branch ?? "HEAD";
|
|
2011
|
+
await exec(`git worktree add -b "${branchName}" "${worktreePath}" "${startPoint}"`, {
|
|
2012
|
+
cwd: req.repoPath
|
|
2000
2013
|
});
|
|
2001
|
-
return
|
|
2014
|
+
return { worktreePath, branchName };
|
|
2002
2015
|
}
|
|
2003
|
-
async function
|
|
2004
|
-
const
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2016
|
+
async function listWorktrees(req) {
|
|
2017
|
+
const { stdout } = await exec("git worktree list --porcelain", { cwd: req.repoPath });
|
|
2018
|
+
const worktrees = [];
|
|
2019
|
+
let currentPath = "";
|
|
2020
|
+
let currentBranch = "";
|
|
2021
|
+
for (const line of stdout.split("\n")) {
|
|
2022
|
+
if (line.startsWith("worktree ")) {
|
|
2023
|
+
currentPath = line.slice(9);
|
|
2024
|
+
} else if (line.startsWith("branch refs/heads/")) {
|
|
2025
|
+
currentBranch = line.slice(18);
|
|
2026
|
+
if (currentBranch.startsWith("jarvis/task-")) {
|
|
2027
|
+
worktrees.push({
|
|
2028
|
+
taskId: currentBranch.replace("jarvis/task-", ""),
|
|
2029
|
+
path: currentPath,
|
|
2030
|
+
branch: currentBranch
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2010
2034
|
}
|
|
2035
|
+
return worktrees;
|
|
2011
2036
|
}
|
|
2012
|
-
async function
|
|
2013
|
-
const
|
|
2037
|
+
async function worktreeExists(req) {
|
|
2038
|
+
const baseDir = resolveDir(req.worktreeDir);
|
|
2039
|
+
const worktreePath = path2.join(baseDir, `task-${req.taskId}`);
|
|
2014
2040
|
try {
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
await fs2.appendFile(gitignorePath, "\n.jarvis/\n");
|
|
2018
|
-
}
|
|
2041
|
+
await fs2.access(worktreePath);
|
|
2042
|
+
return true;
|
|
2019
2043
|
} catch {
|
|
2020
|
-
|
|
2044
|
+
return false;
|
|
2021
2045
|
}
|
|
2022
2046
|
}
|
|
2023
2047
|
|
|
@@ -2257,7 +2281,7 @@ function isImageFile(mimeType) {
|
|
|
2257
2281
|
}
|
|
2258
2282
|
function resolveUrl(url, baseUrl) {
|
|
2259
2283
|
if (url.startsWith("http://") || url.startsWith("https://")) return url;
|
|
2260
|
-
const origin = baseUrl ||
|
|
2284
|
+
const origin = baseUrl || getDefaultAppUrl();
|
|
2261
2285
|
return `${origin}${url}`;
|
|
2262
2286
|
}
|
|
2263
2287
|
async function resolveAttachments(attachments, workspacePath, baseUrl) {
|
|
@@ -2338,7 +2362,7 @@ function createAgent(deps) {
|
|
|
2338
2362
|
const systemMsg = msg.messages.find((m) => m.role === "system");
|
|
2339
2363
|
const nonSystemMessages = msg.messages.filter((m) => m.role !== "system");
|
|
2340
2364
|
if (msg.attachments?.length) {
|
|
2341
|
-
const attachmentContext = await resolveAttachments(msg.attachments, wsPath);
|
|
2365
|
+
const attachmentContext = await resolveAttachments(msg.attachments, wsPath, getDefaultAppUrl());
|
|
2342
2366
|
if (attachmentContext) {
|
|
2343
2367
|
let lastUserIdx = -1;
|
|
2344
2368
|
for (let i = nonSystemMessages.length - 1; i >= 0; i--) {
|
|
@@ -2576,6 +2600,12 @@ function createGitHandler(workspacePath, broadcast) {
|
|
|
2576
2600
|
case "git:diffStats":
|
|
2577
2601
|
await handleDiffStats(msg);
|
|
2578
2602
|
return true;
|
|
2603
|
+
case "git:listWorktrees":
|
|
2604
|
+
await handleListWorktrees(msg);
|
|
2605
|
+
return true;
|
|
2606
|
+
case "git:browseDir":
|
|
2607
|
+
await handleBrowseDir(msg);
|
|
2608
|
+
return true;
|
|
2579
2609
|
default:
|
|
2580
2610
|
return false;
|
|
2581
2611
|
}
|
|
@@ -2583,34 +2613,55 @@ function createGitHandler(workspacePath, broadcast) {
|
|
|
2583
2613
|
function respond(requestId, result, error) {
|
|
2584
2614
|
broadcast({ type: "git:response", requestId, result, error });
|
|
2585
2615
|
}
|
|
2616
|
+
async function hasGitDir(dir) {
|
|
2617
|
+
return fs5.access(path5.join(dir, ".git")).then(() => true).catch(() => false);
|
|
2618
|
+
}
|
|
2619
|
+
async function fetchAndCheckout(repoDir, branch) {
|
|
2620
|
+
await exec2("git fetch origin", { cwd: repoDir, timeout: 12e4 });
|
|
2621
|
+
try {
|
|
2622
|
+
await exec2(`git checkout "${branch}"`, { cwd: repoDir });
|
|
2623
|
+
} catch {
|
|
2624
|
+
await exec2(`git checkout -b "${branch}" "origin/${branch}"`, { cwd: repoDir });
|
|
2625
|
+
}
|
|
2626
|
+
await exec2("git pull --ff-only", { cwd: repoDir }).catch(() => {
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2586
2629
|
async function handleResolveRepo(msg) {
|
|
2587
2630
|
try {
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
const cloneUrl = `git@github.com:${msg.owner}/${msg.name}.git`;
|
|
2594
|
-
await exec2(`git clone "${cloneUrl}" "${repoDir}"`, { timeout: 3e5 });
|
|
2631
|
+
if (msg.repoPath && await hasGitDir(msg.repoPath)) {
|
|
2632
|
+
logger.sys.info("[Git] Using explicit repo path", { repoPath: msg.repoPath });
|
|
2633
|
+
await fetchAndCheckout(msg.repoPath, msg.branch);
|
|
2634
|
+
respond(msg.requestId, { repoPath: msg.repoPath });
|
|
2635
|
+
return;
|
|
2595
2636
|
}
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2637
|
+
const byName = path5.join(workspacePath, msg.name);
|
|
2638
|
+
if (await hasGitDir(byName)) {
|
|
2639
|
+
logger.sys.info("[Git] Found repo by name", { repoPath: byName });
|
|
2640
|
+
await fetchAndCheckout(byName, msg.branch);
|
|
2641
|
+
respond(msg.requestId, { repoPath: byName });
|
|
2642
|
+
return;
|
|
2601
2643
|
}
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2644
|
+
const byOwnerName = path5.join(workspacePath, msg.owner, msg.name);
|
|
2645
|
+
if (await hasGitDir(byOwnerName)) {
|
|
2646
|
+
logger.sys.info("[Git] Found repo by owner/name", { repoPath: byOwnerName });
|
|
2647
|
+
await fetchAndCheckout(byOwnerName, msg.branch);
|
|
2648
|
+
respond(msg.requestId, { repoPath: byOwnerName });
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
logger.sys.info("[Git] Cloning repo...", { repoDir: byOwnerName });
|
|
2652
|
+
await fs5.mkdir(path5.join(workspacePath, msg.owner), { recursive: true });
|
|
2653
|
+
const cloneUrl = `git@github.com:${msg.owner}/${msg.name}.git`;
|
|
2654
|
+
await exec2(`git clone "${cloneUrl}" "${byOwnerName}"`, { timeout: 3e5 });
|
|
2655
|
+
await fetchAndCheckout(byOwnerName, msg.branch);
|
|
2656
|
+
respond(msg.requestId, { repoPath: byOwnerName });
|
|
2606
2657
|
} catch (err) {
|
|
2607
2658
|
respond(msg.requestId, null, err instanceof Error ? err.message : String(err));
|
|
2608
2659
|
}
|
|
2609
2660
|
}
|
|
2610
2661
|
async function handleCreateWorktree(msg) {
|
|
2611
2662
|
try {
|
|
2612
|
-
const
|
|
2613
|
-
respond(msg.requestId,
|
|
2663
|
+
const result = await createWorktree({ repoPath: msg.repoPath, taskId: msg.taskId, branch: msg.branch });
|
|
2664
|
+
respond(msg.requestId, result);
|
|
2614
2665
|
} catch (err) {
|
|
2615
2666
|
respond(msg.requestId, null, err instanceof Error ? err.message : String(err));
|
|
2616
2667
|
}
|
|
@@ -2652,6 +2703,26 @@ function createGitHandler(workspacePath, broadcast) {
|
|
|
2652
2703
|
respond(msg.requestId, { files: [], additions: 0, deletions: 0 });
|
|
2653
2704
|
}
|
|
2654
2705
|
}
|
|
2706
|
+
async function handleListWorktrees(msg) {
|
|
2707
|
+
try {
|
|
2708
|
+
const worktrees = await listWorktrees({ repoPath: msg.repoPath });
|
|
2709
|
+
respond(msg.requestId, worktrees);
|
|
2710
|
+
} catch (err) {
|
|
2711
|
+
respond(msg.requestId, null, err instanceof Error ? err.message : String(err));
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
async function handleBrowseDir(msg) {
|
|
2715
|
+
try {
|
|
2716
|
+
const { stdout } = await exec2(
|
|
2717
|
+
`osascript -e 'set theFolder to POSIX path of (choose folder with prompt "Select worktree directory")' -e 'return theFolder'`,
|
|
2718
|
+
{ timeout: 6e4 }
|
|
2719
|
+
);
|
|
2720
|
+
const selectedPath = stdout.trim();
|
|
2721
|
+
respond(msg.requestId, { path: selectedPath });
|
|
2722
|
+
} catch (err) {
|
|
2723
|
+
respond(msg.requestId, null, err instanceof Error ? err.message : String(err));
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2655
2726
|
return { handle };
|
|
2656
2727
|
}
|
|
2657
2728
|
|
|
@@ -2754,7 +2825,11 @@ function createUpstreamClient(config) {
|
|
|
2754
2825
|
}
|
|
2755
2826
|
function getTokenExpiry(token) {
|
|
2756
2827
|
try {
|
|
2757
|
-
const
|
|
2828
|
+
const parts = token.split(".");
|
|
2829
|
+
if (parts.length === 5) {
|
|
2830
|
+
return Math.floor(Date.now() / 1e3) + 3600;
|
|
2831
|
+
}
|
|
2832
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64").toString());
|
|
2758
2833
|
return payload.exp ?? null;
|
|
2759
2834
|
} catch {
|
|
2760
2835
|
return null;
|
|
@@ -2800,6 +2875,29 @@ function createUpstreamClient(config) {
|
|
|
2800
2875
|
return false;
|
|
2801
2876
|
}
|
|
2802
2877
|
}
|
|
2878
|
+
function attemptReauth() {
|
|
2879
|
+
if (!config.onAuthExhausted) {
|
|
2880
|
+
logger.sys.error("[Upstream] Auth failed after max retries, giving up");
|
|
2881
|
+
return;
|
|
2882
|
+
}
|
|
2883
|
+
logger.sys.info("[Upstream] Auth exhausted, attempting re-authentication...");
|
|
2884
|
+
config.onAuthExhausted().then((result) => {
|
|
2885
|
+
if (!result) {
|
|
2886
|
+
logger.sys.error("[Upstream] Re-authentication failed, giving up");
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
currentToken = result.token;
|
|
2890
|
+
if (result.refreshToken) config.refreshToken = result.refreshToken;
|
|
2891
|
+
authFailures = 0;
|
|
2892
|
+
retries = 0;
|
|
2893
|
+
logger.sys.info("[Upstream] Re-authenticated, reconnecting...");
|
|
2894
|
+
reconnectTimer = setTimeout(connect, 1e3);
|
|
2895
|
+
}).catch((err) => {
|
|
2896
|
+
logger.sys.error("[Upstream] Re-authentication error", {
|
|
2897
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2898
|
+
});
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2803
2901
|
function connect() {
|
|
2804
2902
|
if (closed) return;
|
|
2805
2903
|
ws = new WebSocket2(config.apiUrl, {
|
|
@@ -2833,10 +2931,7 @@ function createUpstreamClient(config) {
|
|
|
2833
2931
|
} else {
|
|
2834
2932
|
authFailures++;
|
|
2835
2933
|
if (authFailures >= MAX_AUTH_RETRIES) {
|
|
2836
|
-
|
|
2837
|
-
code,
|
|
2838
|
-
attempts: authFailures
|
|
2839
|
-
});
|
|
2934
|
+
attemptReauth();
|
|
2840
2935
|
return;
|
|
2841
2936
|
}
|
|
2842
2937
|
reconnectTimer = setTimeout(connect, 3e3);
|
|
@@ -2846,10 +2941,7 @@ function createUpstreamClient(config) {
|
|
|
2846
2941
|
}
|
|
2847
2942
|
authFailures++;
|
|
2848
2943
|
if (authFailures >= MAX_AUTH_RETRIES) {
|
|
2849
|
-
|
|
2850
|
-
code,
|
|
2851
|
-
attempts: authFailures
|
|
2852
|
-
});
|
|
2944
|
+
attemptReauth();
|
|
2853
2945
|
return;
|
|
2854
2946
|
}
|
|
2855
2947
|
logger.sys.warn("[Upstream] Auth failed, retrying...", {
|
|
@@ -2994,16 +3086,16 @@ async function startAgent(options) {
|
|
|
2994
3086
|
import React6 from "react";
|
|
2995
3087
|
import { render as render2 } from "ink";
|
|
2996
3088
|
import { spawn } from "child_process";
|
|
2997
|
-
import { fileURLToPath } from "url";
|
|
3089
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2998
3090
|
import path8 from "path";
|
|
2999
3091
|
import fs8 from "fs";
|
|
3000
|
-
import
|
|
3092
|
+
import os4 from "os";
|
|
3001
3093
|
import WebSocket4 from "ws";
|
|
3002
3094
|
|
|
3003
3095
|
// src/tui/settings.ts
|
|
3004
3096
|
import fs6 from "fs";
|
|
3005
3097
|
import path6 from "path";
|
|
3006
|
-
import
|
|
3098
|
+
import os3 from "os";
|
|
3007
3099
|
var DEFAULT_SETTINGS = {
|
|
3008
3100
|
model: "claude-sonnet-4-20250514",
|
|
3009
3101
|
thinking: { type: "adaptive" },
|
|
@@ -3012,7 +3104,7 @@ var DEFAULT_SETTINGS = {
|
|
|
3012
3104
|
theme: "dark",
|
|
3013
3105
|
disallowedTools: []
|
|
3014
3106
|
};
|
|
3015
|
-
var SETTINGS_DIR = path6.join(
|
|
3107
|
+
var SETTINGS_DIR = path6.join(os3.homedir(), ".jarvis");
|
|
3016
3108
|
var SETTINGS_FILE = path6.join(SETTINGS_DIR, "settings.json");
|
|
3017
3109
|
function loadSettings() {
|
|
3018
3110
|
try {
|
|
@@ -4950,12 +5042,12 @@ async function launchChat(opts) {
|
|
|
4950
5042
|
}
|
|
4951
5043
|
function spawnAgentProcess(port, workspacePath) {
|
|
4952
5044
|
const config = loadConfig();
|
|
4953
|
-
const logDir = path8.join(
|
|
5045
|
+
const logDir = path8.join(os4.homedir(), ".jarvis");
|
|
4954
5046
|
fs8.mkdirSync(logDir, { recursive: true });
|
|
4955
5047
|
const logFile = path8.join(logDir, "agent.log");
|
|
4956
5048
|
const logFd = fs8.openSync(logFile, "a");
|
|
4957
|
-
const
|
|
4958
|
-
const cliRoot = path8.resolve(path8.dirname(
|
|
5049
|
+
const __filename2 = fileURLToPath2(import.meta.url);
|
|
5050
|
+
const cliRoot = path8.resolve(path8.dirname(__filename2), "../..");
|
|
4959
5051
|
const binPath = path8.join(cliRoot, "bin", "jarvis.mjs");
|
|
4960
5052
|
const args = ["start", "--port", String(port), "--workspace", workspacePath];
|
|
4961
5053
|
if (config?.apiUrl && config?.token) {
|
|
@@ -5010,7 +5102,7 @@ async function waitForAgent(port, maxAttempts = 30) {
|
|
|
5010
5102
|
import { execSync, spawn as spawn2 } from "child_process";
|
|
5011
5103
|
import fs9 from "fs";
|
|
5012
5104
|
import path9 from "path";
|
|
5013
|
-
import
|
|
5105
|
+
import os5 from "os";
|
|
5014
5106
|
function createServiceManager() {
|
|
5015
5107
|
if (process.platform === "darwin") return new MacOSService();
|
|
5016
5108
|
if (process.platform === "win32") return new WindowsService();
|
|
@@ -5018,7 +5110,7 @@ function createServiceManager() {
|
|
|
5018
5110
|
return new FallbackService();
|
|
5019
5111
|
}
|
|
5020
5112
|
var PLIST_LABEL = "com.appchy.jarvis";
|
|
5021
|
-
var PLIST_DIR = path9.join(
|
|
5113
|
+
var PLIST_DIR = path9.join(os5.homedir(), "Library", "LaunchAgents");
|
|
5022
5114
|
var PLIST_PATH = path9.join(PLIST_DIR, `${PLIST_LABEL}.plist`);
|
|
5023
5115
|
function escapeXml(s) {
|
|
5024
5116
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -5038,7 +5130,7 @@ var MacOSService = class {
|
|
|
5038
5130
|
if (!opts.upstream) {
|
|
5039
5131
|
args.push("--no-upstream");
|
|
5040
5132
|
}
|
|
5041
|
-
const logFile = path9.join(
|
|
5133
|
+
const logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
|
|
5042
5134
|
const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
|
|
5043
5135
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
5044
5136
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -5060,7 +5152,7 @@ ${args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n")}
|
|
|
5060
5152
|
<key>PATH</key>
|
|
5061
5153
|
<string>${escapeXml(envPath)}</string>
|
|
5062
5154
|
<key>HOME</key>
|
|
5063
|
-
<string>${escapeXml(
|
|
5155
|
+
<string>${escapeXml(os5.homedir())}</string>
|
|
5064
5156
|
<key>NODE_NO_WARNINGS</key>
|
|
5065
5157
|
<string>1</string>
|
|
5066
5158
|
${Object.entries(opts.env ?? {}).map(([k, v]) => ` <key>${escapeXml(k)}</key>
|
|
@@ -5198,7 +5290,7 @@ var WindowsService = class {
|
|
|
5198
5290
|
</Actions>
|
|
5199
5291
|
</Task>
|
|
5200
5292
|
`;
|
|
5201
|
-
const tmpDir =
|
|
5293
|
+
const tmpDir = os5.tmpdir();
|
|
5202
5294
|
const tmpFile = path9.join(tmpDir, `jarvis-task-${Date.now()}.xml`);
|
|
5203
5295
|
fs9.writeFileSync(tmpFile, xml, { encoding: "utf-16le" });
|
|
5204
5296
|
try {
|
|
@@ -5245,7 +5337,7 @@ var WindowsService = class {
|
|
|
5245
5337
|
}
|
|
5246
5338
|
}
|
|
5247
5339
|
};
|
|
5248
|
-
var SYSTEMD_DIR = path9.join(
|
|
5340
|
+
var SYSTEMD_DIR = path9.join(os5.homedir(), ".config", "systemd", "user");
|
|
5249
5341
|
var UNIT_NAME = "jarvis.service";
|
|
5250
5342
|
var UNIT_PATH = path9.join(SYSTEMD_DIR, UNIT_NAME);
|
|
5251
5343
|
var LinuxService = class {
|
|
@@ -5262,7 +5354,7 @@ var LinuxService = class {
|
|
|
5262
5354
|
if (!opts.upstream) {
|
|
5263
5355
|
args.push("--no-upstream");
|
|
5264
5356
|
}
|
|
5265
|
-
const logFile = path9.join(
|
|
5357
|
+
const logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
|
|
5266
5358
|
const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
|
|
5267
5359
|
const unit = `[Unit]
|
|
5268
5360
|
Description=Jarvis AI Agent
|
|
@@ -5273,7 +5365,7 @@ Type=simple
|
|
|
5273
5365
|
ExecStart=${opts.nodePath} ${args.join(" ")}
|
|
5274
5366
|
WorkingDirectory=${opts.workspacePath}
|
|
5275
5367
|
Environment=PATH=${envPath}
|
|
5276
|
-
Environment=HOME=${
|
|
5368
|
+
Environment=HOME=${os5.homedir()}
|
|
5277
5369
|
Environment=NODE_NO_WARNINGS=1
|
|
5278
5370
|
Restart=always
|
|
5279
5371
|
RestartSec=5
|
|
@@ -5296,7 +5388,7 @@ WantedBy=default.target
|
|
|
5296
5388
|
execSync("systemctl --user enable jarvis.service", { stdio: "ignore" });
|
|
5297
5389
|
execSync("systemctl --user start jarvis.service", { stdio: "ignore" });
|
|
5298
5390
|
try {
|
|
5299
|
-
execSync(`loginctl enable-linger ${
|
|
5391
|
+
execSync(`loginctl enable-linger ${os5.userInfo().username}`, { stdio: "ignore" });
|
|
5300
5392
|
} catch {
|
|
5301
5393
|
}
|
|
5302
5394
|
}
|
|
@@ -5349,12 +5441,12 @@ WantedBy=default.target
|
|
|
5349
5441
|
};
|
|
5350
5442
|
var FallbackService = class {
|
|
5351
5443
|
constructor() {
|
|
5352
|
-
this.pidFile = path9.join(
|
|
5353
|
-
this.logFile = path9.join(
|
|
5354
|
-
this.markerFile = path9.join(
|
|
5444
|
+
this.pidFile = path9.join(os5.homedir(), ".jarvis", "agent.pid");
|
|
5445
|
+
this.logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
|
|
5446
|
+
this.markerFile = path9.join(os5.homedir(), ".jarvis", "service-installed");
|
|
5355
5447
|
}
|
|
5356
5448
|
install(opts) {
|
|
5357
|
-
const jarvisDir = path9.join(
|
|
5449
|
+
const jarvisDir = path9.join(os5.homedir(), ".jarvis");
|
|
5358
5450
|
fs9.mkdirSync(jarvisDir, { recursive: true });
|
|
5359
5451
|
this.stop();
|
|
5360
5452
|
const args = [
|
|
@@ -5439,7 +5531,7 @@ var FallbackService = class {
|
|
|
5439
5531
|
import { createRequire } from "module";
|
|
5440
5532
|
var _require = createRequire(import.meta.url);
|
|
5441
5533
|
var PKG_VERSION = _require("../package.json").version ?? "dev";
|
|
5442
|
-
var LOG_DIR = path10.join(
|
|
5534
|
+
var LOG_DIR = path10.join(os6.homedir(), ".jarvis");
|
|
5443
5535
|
var LOG_FILE = path10.join(LOG_DIR, "agent.log");
|
|
5444
5536
|
var PID_FILE = path10.join(LOG_DIR, "agent.pid");
|
|
5445
5537
|
function readPid() {
|
|
@@ -5540,9 +5632,8 @@ async function browserAuth(appUrl) {
|
|
|
5540
5632
|
}
|
|
5541
5633
|
}
|
|
5542
5634
|
function getAppUrl() {
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
return "https://jarvis.appchy.com";
|
|
5635
|
+
const config = loadConfig();
|
|
5636
|
+
return config?.appUrl ?? getDefaultAppUrl();
|
|
5546
5637
|
}
|
|
5547
5638
|
async function ensureSetup() {
|
|
5548
5639
|
const config = loadConfig();
|
|
@@ -5552,6 +5643,45 @@ async function ensureSetup() {
|
|
|
5552
5643
|
clearConfig();
|
|
5553
5644
|
return browserAuth(getAppUrl());
|
|
5554
5645
|
}
|
|
5646
|
+
function parseGitRemote(url) {
|
|
5647
|
+
const sshMatch = url.match(/[:\/]([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
5648
|
+
if (sshMatch) return { owner: sshMatch[1], name: sshMatch[2] };
|
|
5649
|
+
return null;
|
|
5650
|
+
}
|
|
5651
|
+
async function autoRegisterRepo(workspacePath, config) {
|
|
5652
|
+
try {
|
|
5653
|
+
const remoteUrl = execSync2("git config --get remote.origin.url", {
|
|
5654
|
+
cwd: workspacePath,
|
|
5655
|
+
encoding: "utf-8",
|
|
5656
|
+
timeout: 5e3
|
|
5657
|
+
}).trim();
|
|
5658
|
+
if (!remoteUrl) return;
|
|
5659
|
+
const parsed = parseGitRemote(remoteUrl);
|
|
5660
|
+
if (!parsed) return;
|
|
5661
|
+
const slug = `${parsed.owner}/${parsed.name}`;
|
|
5662
|
+
const appUrl = config?.appUrl ?? getDefaultAppUrl();
|
|
5663
|
+
const token = config?.token;
|
|
5664
|
+
if (!token) return;
|
|
5665
|
+
console.log(`Registering repo ${slug} \u2192 ${workspacePath}`);
|
|
5666
|
+
const envId = config?.envId ?? "local";
|
|
5667
|
+
const res = await fetch(`${appUrl}/api/v1/settings/preferences/repos?envId=${envId}`, {
|
|
5668
|
+
method: "PATCH",
|
|
5669
|
+
headers: {
|
|
5670
|
+
"Content-Type": "application/json",
|
|
5671
|
+
Authorization: `Bearer ${token}`
|
|
5672
|
+
},
|
|
5673
|
+
body: JSON.stringify({ [slug]: workspacePath })
|
|
5674
|
+
});
|
|
5675
|
+
if (res.ok) {
|
|
5676
|
+
console.log(` Repo registered`);
|
|
5677
|
+
} else {
|
|
5678
|
+
const body = await res.text().catch(() => "");
|
|
5679
|
+
console.error(` Failed to register repo (${res.status}): ${body}`);
|
|
5680
|
+
}
|
|
5681
|
+
} catch (err) {
|
|
5682
|
+
console.error(` Failed to register repo: ${err instanceof Error ? err.message : err}`);
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5555
5685
|
function createCli() {
|
|
5556
5686
|
const program = new Command().name("jarvis").description("Jarvis local agent \u2014 runs Claude Code on your machine").version(PKG_VERSION);
|
|
5557
5687
|
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) => {
|
|
@@ -5593,6 +5723,9 @@ function createCli() {
|
|
|
5593
5723
|
const port = parseInt(opts.port, 10);
|
|
5594
5724
|
const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
|
|
5595
5725
|
const userId = config?.userId ?? process.env.JARVIS_USER_ID ?? "local";
|
|
5726
|
+
if (!opts.foreground) {
|
|
5727
|
+
await autoRegisterRepo(workspacePath, config);
|
|
5728
|
+
}
|
|
5596
5729
|
const explicitApiKey = opts.apiKey ?? config?.anthropicApiKey;
|
|
5597
5730
|
const useSubscription = !explicitApiKey;
|
|
5598
5731
|
const anthropicApiKey = explicitApiKey;
|
|
@@ -5610,7 +5743,20 @@ function createCli() {
|
|
|
5610
5743
|
const apiUrl = process.env.JARVIS_UPSTREAM_URL ?? config?.apiUrl;
|
|
5611
5744
|
const token = process.env.JARVIS_UPSTREAM_TOKEN ?? config?.token;
|
|
5612
5745
|
if (opts.upstream && apiUrl && token) {
|
|
5613
|
-
|
|
5746
|
+
const appUrl = getAppUrl();
|
|
5747
|
+
return {
|
|
5748
|
+
apiUrl,
|
|
5749
|
+
token,
|
|
5750
|
+
refreshToken: config?.refreshToken,
|
|
5751
|
+
onAuthExhausted: async () => {
|
|
5752
|
+
console.log("\n[Auth] Token expired \u2014 re-authenticating...");
|
|
5753
|
+
const ok = await browserAuth(appUrl);
|
|
5754
|
+
if (!ok) return null;
|
|
5755
|
+
const fresh = loadConfig();
|
|
5756
|
+
if (!fresh?.token) return null;
|
|
5757
|
+
return { token: fresh.token, refreshToken: fresh.refreshToken };
|
|
5758
|
+
}
|
|
5759
|
+
};
|
|
5614
5760
|
}
|
|
5615
5761
|
return void 0;
|
|
5616
5762
|
})()
|
|
@@ -5666,17 +5812,19 @@ function createCli() {
|
|
|
5666
5812
|
const binPath = process.argv[1];
|
|
5667
5813
|
const cliRoot = path10.resolve(path10.dirname(binPath), "..");
|
|
5668
5814
|
const distEntry = path10.join(cliRoot, "dist", "bin.js");
|
|
5669
|
-
const
|
|
5815
|
+
const srcEntry = path10.join(cliRoot, "src", "bin.ts");
|
|
5816
|
+
const useSrc = isDev() && fs10.existsSync(srcEntry);
|
|
5817
|
+
const entryPath = useSrc ? srcEntry : fs10.existsSync(distEntry) ? distEntry : binPath;
|
|
5818
|
+
const tsxBin = path10.join(cliRoot, "node_modules", ".bin", "tsx");
|
|
5819
|
+
const nodePath = useSrc && fs10.existsSync(tsxBin) ? tsxBin : process.execPath;
|
|
5670
5820
|
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
5821
|
if (process.env.JARVIS_CONFIG_DIR) serviceEnv.JARVIS_CONFIG_DIR = process.env.JARVIS_CONFIG_DIR;
|
|
5674
5822
|
if (process.env.JARVIS_USER_ID) serviceEnv.JARVIS_USER_ID = process.env.JARVIS_USER_ID;
|
|
5675
5823
|
if (process.env.JARVIS_ENV_ID) serviceEnv.JARVIS_ENV_ID = process.env.JARVIS_ENV_ID;
|
|
5676
5824
|
service.install({
|
|
5677
5825
|
port,
|
|
5678
5826
|
workspacePath,
|
|
5679
|
-
nodePath
|
|
5827
|
+
nodePath,
|
|
5680
5828
|
entryPath,
|
|
5681
5829
|
upstream: opts.upstream,
|
|
5682
5830
|
...Object.keys(serviceEnv).length > 0 ? { env: serviceEnv } : {}
|
|
@@ -5813,12 +5961,11 @@ function createCli() {
|
|
|
5813
5961
|
const binPath = process.argv[1];
|
|
5814
5962
|
const cliRoot = path10.resolve(path10.dirname(binPath), "..");
|
|
5815
5963
|
const distEntry = path10.join(cliRoot, "dist", "bin.js");
|
|
5816
|
-
const
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
}
|
|
5964
|
+
const srcEntry = path10.join(cliRoot, "src", "bin.ts");
|
|
5965
|
+
const useSrc = isDev() && fs10.existsSync(srcEntry);
|
|
5966
|
+
const entryPath = useSrc ? srcEntry : fs10.existsSync(distEntry) ? distEntry : binPath;
|
|
5967
|
+
const tsxBin = path10.join(cliRoot, "node_modules", ".bin", "tsx");
|
|
5968
|
+
const nodePath = useSrc && fs10.existsSync(tsxBin) ? tsxBin : process.execPath;
|
|
5822
5969
|
const pid = readPid();
|
|
5823
5970
|
if (pid) {
|
|
5824
5971
|
try {
|
|
@@ -5828,8 +5975,6 @@ function createCli() {
|
|
|
5828
5975
|
clearPid();
|
|
5829
5976
|
}
|
|
5830
5977
|
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
5978
|
if (process.env.JARVIS_CONFIG_DIR) installEnv.JARVIS_CONFIG_DIR = process.env.JARVIS_CONFIG_DIR;
|
|
5834
5979
|
if (process.env.JARVIS_USER_ID) installEnv.JARVIS_USER_ID = process.env.JARVIS_USER_ID;
|
|
5835
5980
|
if (process.env.JARVIS_ENV_ID) installEnv.JARVIS_ENV_ID = process.env.JARVIS_ENV_ID;
|
|
@@ -5837,7 +5982,7 @@ function createCli() {
|
|
|
5837
5982
|
service.install({
|
|
5838
5983
|
port,
|
|
5839
5984
|
workspacePath,
|
|
5840
|
-
nodePath
|
|
5985
|
+
nodePath,
|
|
5841
5986
|
entryPath,
|
|
5842
5987
|
upstream: opts.upstream,
|
|
5843
5988
|
...Object.keys(installEnv).length > 0 ? { env: installEnv } : {}
|
|
@@ -5885,22 +6030,19 @@ function createCli() {
|
|
|
5885
6030
|
}
|
|
5886
6031
|
|
|
5887
6032
|
// src/bin.ts
|
|
5888
|
-
|
|
6033
|
+
if (isDev()) {
|
|
6034
|
+
const dotenv = await import("dotenv");
|
|
6035
|
+
const fs11 = await import("fs");
|
|
6036
|
+
const path11 = await import("path");
|
|
5889
6037
|
let dir = process.cwd();
|
|
5890
6038
|
while (dir !== path11.dirname(dir)) {
|
|
5891
6039
|
const envPath = path11.join(dir, ".env");
|
|
5892
|
-
if (fs11.existsSync(envPath))
|
|
6040
|
+
if (fs11.existsSync(envPath)) {
|
|
6041
|
+
dotenv.config({ path: envPath });
|
|
6042
|
+
break;
|
|
6043
|
+
}
|
|
5893
6044
|
dir = path11.dirname(dir);
|
|
5894
6045
|
}
|
|
5895
|
-
return void 0;
|
|
5896
|
-
}
|
|
5897
|
-
var envFile = findEnv();
|
|
5898
|
-
if (envFile) {
|
|
5899
|
-
const prevApiKey = process.env.ANTHROPIC_API_KEY;
|
|
5900
|
-
const prevAppUrl = process.env.APP_URL;
|
|
5901
|
-
dotenv.config({ path: envFile });
|
|
5902
|
-
if (prevApiKey === void 0) delete process.env.ANTHROPIC_API_KEY;
|
|
5903
|
-
if (prevAppUrl === void 0) delete process.env.APP_URL;
|
|
5904
6046
|
}
|
|
5905
6047
|
createCli().parse();
|
|
5906
6048
|
//# sourceMappingURL=bin.js.map
|