@appchy/jarvis 0.1.18 → 0.1.19
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 +220 -112
- package/dist/bin.js.map +1 -1
- package/dist/env.json +1 -0
- package/package.json +1 -1
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
|
|
|
@@ -2994,16 +3065,16 @@ async function startAgent(options) {
|
|
|
2994
3065
|
import React6 from "react";
|
|
2995
3066
|
import { render as render2 } from "ink";
|
|
2996
3067
|
import { spawn } from "child_process";
|
|
2997
|
-
import { fileURLToPath } from "url";
|
|
3068
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2998
3069
|
import path8 from "path";
|
|
2999
3070
|
import fs8 from "fs";
|
|
3000
|
-
import
|
|
3071
|
+
import os4 from "os";
|
|
3001
3072
|
import WebSocket4 from "ws";
|
|
3002
3073
|
|
|
3003
3074
|
// src/tui/settings.ts
|
|
3004
3075
|
import fs6 from "fs";
|
|
3005
3076
|
import path6 from "path";
|
|
3006
|
-
import
|
|
3077
|
+
import os3 from "os";
|
|
3007
3078
|
var DEFAULT_SETTINGS = {
|
|
3008
3079
|
model: "claude-sonnet-4-20250514",
|
|
3009
3080
|
thinking: { type: "adaptive" },
|
|
@@ -3012,7 +3083,7 @@ var DEFAULT_SETTINGS = {
|
|
|
3012
3083
|
theme: "dark",
|
|
3013
3084
|
disallowedTools: []
|
|
3014
3085
|
};
|
|
3015
|
-
var SETTINGS_DIR = path6.join(
|
|
3086
|
+
var SETTINGS_DIR = path6.join(os3.homedir(), ".jarvis");
|
|
3016
3087
|
var SETTINGS_FILE = path6.join(SETTINGS_DIR, "settings.json");
|
|
3017
3088
|
function loadSettings() {
|
|
3018
3089
|
try {
|
|
@@ -4950,12 +5021,12 @@ async function launchChat(opts) {
|
|
|
4950
5021
|
}
|
|
4951
5022
|
function spawnAgentProcess(port, workspacePath) {
|
|
4952
5023
|
const config = loadConfig();
|
|
4953
|
-
const logDir = path8.join(
|
|
5024
|
+
const logDir = path8.join(os4.homedir(), ".jarvis");
|
|
4954
5025
|
fs8.mkdirSync(logDir, { recursive: true });
|
|
4955
5026
|
const logFile = path8.join(logDir, "agent.log");
|
|
4956
5027
|
const logFd = fs8.openSync(logFile, "a");
|
|
4957
|
-
const
|
|
4958
|
-
const cliRoot = path8.resolve(path8.dirname(
|
|
5028
|
+
const __filename2 = fileURLToPath2(import.meta.url);
|
|
5029
|
+
const cliRoot = path8.resolve(path8.dirname(__filename2), "../..");
|
|
4959
5030
|
const binPath = path8.join(cliRoot, "bin", "jarvis.mjs");
|
|
4960
5031
|
const args = ["start", "--port", String(port), "--workspace", workspacePath];
|
|
4961
5032
|
if (config?.apiUrl && config?.token) {
|
|
@@ -5010,7 +5081,7 @@ async function waitForAgent(port, maxAttempts = 30) {
|
|
|
5010
5081
|
import { execSync, spawn as spawn2 } from "child_process";
|
|
5011
5082
|
import fs9 from "fs";
|
|
5012
5083
|
import path9 from "path";
|
|
5013
|
-
import
|
|
5084
|
+
import os5 from "os";
|
|
5014
5085
|
function createServiceManager() {
|
|
5015
5086
|
if (process.platform === "darwin") return new MacOSService();
|
|
5016
5087
|
if (process.platform === "win32") return new WindowsService();
|
|
@@ -5018,7 +5089,7 @@ function createServiceManager() {
|
|
|
5018
5089
|
return new FallbackService();
|
|
5019
5090
|
}
|
|
5020
5091
|
var PLIST_LABEL = "com.appchy.jarvis";
|
|
5021
|
-
var PLIST_DIR = path9.join(
|
|
5092
|
+
var PLIST_DIR = path9.join(os5.homedir(), "Library", "LaunchAgents");
|
|
5022
5093
|
var PLIST_PATH = path9.join(PLIST_DIR, `${PLIST_LABEL}.plist`);
|
|
5023
5094
|
function escapeXml(s) {
|
|
5024
5095
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -5038,7 +5109,7 @@ var MacOSService = class {
|
|
|
5038
5109
|
if (!opts.upstream) {
|
|
5039
5110
|
args.push("--no-upstream");
|
|
5040
5111
|
}
|
|
5041
|
-
const logFile = path9.join(
|
|
5112
|
+
const logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
|
|
5042
5113
|
const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
|
|
5043
5114
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
5044
5115
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -5060,7 +5131,7 @@ ${args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n")}
|
|
|
5060
5131
|
<key>PATH</key>
|
|
5061
5132
|
<string>${escapeXml(envPath)}</string>
|
|
5062
5133
|
<key>HOME</key>
|
|
5063
|
-
<string>${escapeXml(
|
|
5134
|
+
<string>${escapeXml(os5.homedir())}</string>
|
|
5064
5135
|
<key>NODE_NO_WARNINGS</key>
|
|
5065
5136
|
<string>1</string>
|
|
5066
5137
|
${Object.entries(opts.env ?? {}).map(([k, v]) => ` <key>${escapeXml(k)}</key>
|
|
@@ -5198,7 +5269,7 @@ var WindowsService = class {
|
|
|
5198
5269
|
</Actions>
|
|
5199
5270
|
</Task>
|
|
5200
5271
|
`;
|
|
5201
|
-
const tmpDir =
|
|
5272
|
+
const tmpDir = os5.tmpdir();
|
|
5202
5273
|
const tmpFile = path9.join(tmpDir, `jarvis-task-${Date.now()}.xml`);
|
|
5203
5274
|
fs9.writeFileSync(tmpFile, xml, { encoding: "utf-16le" });
|
|
5204
5275
|
try {
|
|
@@ -5245,7 +5316,7 @@ var WindowsService = class {
|
|
|
5245
5316
|
}
|
|
5246
5317
|
}
|
|
5247
5318
|
};
|
|
5248
|
-
var SYSTEMD_DIR = path9.join(
|
|
5319
|
+
var SYSTEMD_DIR = path9.join(os5.homedir(), ".config", "systemd", "user");
|
|
5249
5320
|
var UNIT_NAME = "jarvis.service";
|
|
5250
5321
|
var UNIT_PATH = path9.join(SYSTEMD_DIR, UNIT_NAME);
|
|
5251
5322
|
var LinuxService = class {
|
|
@@ -5262,7 +5333,7 @@ var LinuxService = class {
|
|
|
5262
5333
|
if (!opts.upstream) {
|
|
5263
5334
|
args.push("--no-upstream");
|
|
5264
5335
|
}
|
|
5265
|
-
const logFile = path9.join(
|
|
5336
|
+
const logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
|
|
5266
5337
|
const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
|
|
5267
5338
|
const unit = `[Unit]
|
|
5268
5339
|
Description=Jarvis AI Agent
|
|
@@ -5273,7 +5344,7 @@ Type=simple
|
|
|
5273
5344
|
ExecStart=${opts.nodePath} ${args.join(" ")}
|
|
5274
5345
|
WorkingDirectory=${opts.workspacePath}
|
|
5275
5346
|
Environment=PATH=${envPath}
|
|
5276
|
-
Environment=HOME=${
|
|
5347
|
+
Environment=HOME=${os5.homedir()}
|
|
5277
5348
|
Environment=NODE_NO_WARNINGS=1
|
|
5278
5349
|
Restart=always
|
|
5279
5350
|
RestartSec=5
|
|
@@ -5296,7 +5367,7 @@ WantedBy=default.target
|
|
|
5296
5367
|
execSync("systemctl --user enable jarvis.service", { stdio: "ignore" });
|
|
5297
5368
|
execSync("systemctl --user start jarvis.service", { stdio: "ignore" });
|
|
5298
5369
|
try {
|
|
5299
|
-
execSync(`loginctl enable-linger ${
|
|
5370
|
+
execSync(`loginctl enable-linger ${os5.userInfo().username}`, { stdio: "ignore" });
|
|
5300
5371
|
} catch {
|
|
5301
5372
|
}
|
|
5302
5373
|
}
|
|
@@ -5349,12 +5420,12 @@ WantedBy=default.target
|
|
|
5349
5420
|
};
|
|
5350
5421
|
var FallbackService = class {
|
|
5351
5422
|
constructor() {
|
|
5352
|
-
this.pidFile = path9.join(
|
|
5353
|
-
this.logFile = path9.join(
|
|
5354
|
-
this.markerFile = path9.join(
|
|
5423
|
+
this.pidFile = path9.join(os5.homedir(), ".jarvis", "agent.pid");
|
|
5424
|
+
this.logFile = path9.join(os5.homedir(), ".jarvis", "agent.log");
|
|
5425
|
+
this.markerFile = path9.join(os5.homedir(), ".jarvis", "service-installed");
|
|
5355
5426
|
}
|
|
5356
5427
|
install(opts) {
|
|
5357
|
-
const jarvisDir = path9.join(
|
|
5428
|
+
const jarvisDir = path9.join(os5.homedir(), ".jarvis");
|
|
5358
5429
|
fs9.mkdirSync(jarvisDir, { recursive: true });
|
|
5359
5430
|
this.stop();
|
|
5360
5431
|
const args = [
|
|
@@ -5439,7 +5510,7 @@ var FallbackService = class {
|
|
|
5439
5510
|
import { createRequire } from "module";
|
|
5440
5511
|
var _require = createRequire(import.meta.url);
|
|
5441
5512
|
var PKG_VERSION = _require("../package.json").version ?? "dev";
|
|
5442
|
-
var LOG_DIR = path10.join(
|
|
5513
|
+
var LOG_DIR = path10.join(os6.homedir(), ".jarvis");
|
|
5443
5514
|
var LOG_FILE = path10.join(LOG_DIR, "agent.log");
|
|
5444
5515
|
var PID_FILE = path10.join(LOG_DIR, "agent.pid");
|
|
5445
5516
|
function readPid() {
|
|
@@ -5540,9 +5611,8 @@ async function browserAuth(appUrl) {
|
|
|
5540
5611
|
}
|
|
5541
5612
|
}
|
|
5542
5613
|
function getAppUrl() {
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
return "https://jarvis.appchy.com";
|
|
5614
|
+
const config = loadConfig();
|
|
5615
|
+
return config?.appUrl ?? getDefaultAppUrl();
|
|
5546
5616
|
}
|
|
5547
5617
|
async function ensureSetup() {
|
|
5548
5618
|
const config = loadConfig();
|
|
@@ -5552,6 +5622,45 @@ async function ensureSetup() {
|
|
|
5552
5622
|
clearConfig();
|
|
5553
5623
|
return browserAuth(getAppUrl());
|
|
5554
5624
|
}
|
|
5625
|
+
function parseGitRemote(url) {
|
|
5626
|
+
const sshMatch = url.match(/[:\/]([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
5627
|
+
if (sshMatch) return { owner: sshMatch[1], name: sshMatch[2] };
|
|
5628
|
+
return null;
|
|
5629
|
+
}
|
|
5630
|
+
async function autoRegisterRepo(workspacePath, config) {
|
|
5631
|
+
try {
|
|
5632
|
+
const remoteUrl = execSync2("git config --get remote.origin.url", {
|
|
5633
|
+
cwd: workspacePath,
|
|
5634
|
+
encoding: "utf-8",
|
|
5635
|
+
timeout: 5e3
|
|
5636
|
+
}).trim();
|
|
5637
|
+
if (!remoteUrl) return;
|
|
5638
|
+
const parsed = parseGitRemote(remoteUrl);
|
|
5639
|
+
if (!parsed) return;
|
|
5640
|
+
const slug = `${parsed.owner}/${parsed.name}`;
|
|
5641
|
+
const appUrl = config?.appUrl ?? getDefaultAppUrl();
|
|
5642
|
+
const token = config?.token;
|
|
5643
|
+
if (!token) return;
|
|
5644
|
+
console.log(`Registering repo ${slug} \u2192 ${workspacePath}`);
|
|
5645
|
+
const envId = config?.envId ?? "local";
|
|
5646
|
+
const res = await fetch(`${appUrl}/api/v1/settings/preferences/repos?envId=${envId}`, {
|
|
5647
|
+
method: "PATCH",
|
|
5648
|
+
headers: {
|
|
5649
|
+
"Content-Type": "application/json",
|
|
5650
|
+
Authorization: `Bearer ${token}`
|
|
5651
|
+
},
|
|
5652
|
+
body: JSON.stringify({ [slug]: workspacePath })
|
|
5653
|
+
});
|
|
5654
|
+
if (res.ok) {
|
|
5655
|
+
console.log(` Repo registered`);
|
|
5656
|
+
} else {
|
|
5657
|
+
const body = await res.text().catch(() => "");
|
|
5658
|
+
console.error(` Failed to register repo (${res.status}): ${body}`);
|
|
5659
|
+
}
|
|
5660
|
+
} catch (err) {
|
|
5661
|
+
console.error(` Failed to register repo: ${err instanceof Error ? err.message : err}`);
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5555
5664
|
function createCli() {
|
|
5556
5665
|
const program = new Command().name("jarvis").description("Jarvis local agent \u2014 runs Claude Code on your machine").version(PKG_VERSION);
|
|
5557
5666
|
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 +5702,9 @@ function createCli() {
|
|
|
5593
5702
|
const port = parseInt(opts.port, 10);
|
|
5594
5703
|
const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
|
|
5595
5704
|
const userId = config?.userId ?? process.env.JARVIS_USER_ID ?? "local";
|
|
5705
|
+
if (!opts.foreground) {
|
|
5706
|
+
await autoRegisterRepo(workspacePath, config);
|
|
5707
|
+
}
|
|
5596
5708
|
const explicitApiKey = opts.apiKey ?? config?.anthropicApiKey;
|
|
5597
5709
|
const useSubscription = !explicitApiKey;
|
|
5598
5710
|
const anthropicApiKey = explicitApiKey;
|
|
@@ -5666,17 +5778,19 @@ function createCli() {
|
|
|
5666
5778
|
const binPath = process.argv[1];
|
|
5667
5779
|
const cliRoot = path10.resolve(path10.dirname(binPath), "..");
|
|
5668
5780
|
const distEntry = path10.join(cliRoot, "dist", "bin.js");
|
|
5669
|
-
const
|
|
5781
|
+
const srcEntry = path10.join(cliRoot, "src", "bin.ts");
|
|
5782
|
+
const useSrc = isDev() && fs10.existsSync(srcEntry);
|
|
5783
|
+
const entryPath = useSrc ? srcEntry : fs10.existsSync(distEntry) ? distEntry : binPath;
|
|
5784
|
+
const tsxBin = path10.join(cliRoot, "node_modules", ".bin", "tsx");
|
|
5785
|
+
const nodePath = useSrc && fs10.existsSync(tsxBin) ? tsxBin : process.execPath;
|
|
5670
5786
|
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
5787
|
if (process.env.JARVIS_CONFIG_DIR) serviceEnv.JARVIS_CONFIG_DIR = process.env.JARVIS_CONFIG_DIR;
|
|
5674
5788
|
if (process.env.JARVIS_USER_ID) serviceEnv.JARVIS_USER_ID = process.env.JARVIS_USER_ID;
|
|
5675
5789
|
if (process.env.JARVIS_ENV_ID) serviceEnv.JARVIS_ENV_ID = process.env.JARVIS_ENV_ID;
|
|
5676
5790
|
service.install({
|
|
5677
5791
|
port,
|
|
5678
5792
|
workspacePath,
|
|
5679
|
-
nodePath
|
|
5793
|
+
nodePath,
|
|
5680
5794
|
entryPath,
|
|
5681
5795
|
upstream: opts.upstream,
|
|
5682
5796
|
...Object.keys(serviceEnv).length > 0 ? { env: serviceEnv } : {}
|
|
@@ -5813,12 +5927,11 @@ function createCli() {
|
|
|
5813
5927
|
const binPath = process.argv[1];
|
|
5814
5928
|
const cliRoot = path10.resolve(path10.dirname(binPath), "..");
|
|
5815
5929
|
const distEntry = path10.join(cliRoot, "dist", "bin.js");
|
|
5816
|
-
const
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
}
|
|
5930
|
+
const srcEntry = path10.join(cliRoot, "src", "bin.ts");
|
|
5931
|
+
const useSrc = isDev() && fs10.existsSync(srcEntry);
|
|
5932
|
+
const entryPath = useSrc ? srcEntry : fs10.existsSync(distEntry) ? distEntry : binPath;
|
|
5933
|
+
const tsxBin = path10.join(cliRoot, "node_modules", ".bin", "tsx");
|
|
5934
|
+
const nodePath = useSrc && fs10.existsSync(tsxBin) ? tsxBin : process.execPath;
|
|
5822
5935
|
const pid = readPid();
|
|
5823
5936
|
if (pid) {
|
|
5824
5937
|
try {
|
|
@@ -5828,8 +5941,6 @@ function createCli() {
|
|
|
5828
5941
|
clearPid();
|
|
5829
5942
|
}
|
|
5830
5943
|
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
5944
|
if (process.env.JARVIS_CONFIG_DIR) installEnv.JARVIS_CONFIG_DIR = process.env.JARVIS_CONFIG_DIR;
|
|
5834
5945
|
if (process.env.JARVIS_USER_ID) installEnv.JARVIS_USER_ID = process.env.JARVIS_USER_ID;
|
|
5835
5946
|
if (process.env.JARVIS_ENV_ID) installEnv.JARVIS_ENV_ID = process.env.JARVIS_ENV_ID;
|
|
@@ -5837,7 +5948,7 @@ function createCli() {
|
|
|
5837
5948
|
service.install({
|
|
5838
5949
|
port,
|
|
5839
5950
|
workspacePath,
|
|
5840
|
-
nodePath
|
|
5951
|
+
nodePath,
|
|
5841
5952
|
entryPath,
|
|
5842
5953
|
upstream: opts.upstream,
|
|
5843
5954
|
...Object.keys(installEnv).length > 0 ? { env: installEnv } : {}
|
|
@@ -5885,22 +5996,19 @@ function createCli() {
|
|
|
5885
5996
|
}
|
|
5886
5997
|
|
|
5887
5998
|
// src/bin.ts
|
|
5888
|
-
|
|
5999
|
+
if (isDev()) {
|
|
6000
|
+
const dotenv = await import("dotenv");
|
|
6001
|
+
const fs11 = await import("fs");
|
|
6002
|
+
const path11 = await import("path");
|
|
5889
6003
|
let dir = process.cwd();
|
|
5890
6004
|
while (dir !== path11.dirname(dir)) {
|
|
5891
6005
|
const envPath = path11.join(dir, ".env");
|
|
5892
|
-
if (fs11.existsSync(envPath))
|
|
6006
|
+
if (fs11.existsSync(envPath)) {
|
|
6007
|
+
dotenv.config({ path: envPath });
|
|
6008
|
+
break;
|
|
6009
|
+
}
|
|
5893
6010
|
dir = path11.dirname(dir);
|
|
5894
6011
|
}
|
|
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
6012
|
}
|
|
5905
6013
|
createCli().parse();
|
|
5906
6014
|
//# sourceMappingURL=bin.js.map
|