@appchy/jarvis 0.1.14 → 0.1.16
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 +678 -130
- package/dist/bin.js.map +1 -1
- package/package.json +3 -3
package/dist/bin.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
// src/bin.ts
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import fs9 from "fs";
|
|
4
|
+
import path9 from "path";
|
|
5
|
+
|
|
1
6
|
// src/cli.ts
|
|
2
7
|
import { Command } from "commander";
|
|
3
|
-
import { spawn as
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
8
|
+
import { spawn as spawn3, execSync as execSync2 } from "child_process";
|
|
9
|
+
import fs8 from "fs";
|
|
10
|
+
import path8 from "path";
|
|
11
|
+
import os5 from "os";
|
|
7
12
|
|
|
8
13
|
// src/config.ts
|
|
9
14
|
import fs from "fs";
|
|
@@ -2556,7 +2561,7 @@ function createUpstreamClient(config) {
|
|
|
2556
2561
|
if (!config.refreshToken) return null;
|
|
2557
2562
|
return config.apiUrl.replace(/^wss:/, "https:").replace(/^ws:/, "http:") + "/refresh";
|
|
2558
2563
|
}
|
|
2559
|
-
function
|
|
2564
|
+
function getTokenExpiry2(token) {
|
|
2560
2565
|
try {
|
|
2561
2566
|
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
|
|
2562
2567
|
return payload.exp ?? null;
|
|
@@ -2567,7 +2572,7 @@ function createUpstreamClient(config) {
|
|
|
2567
2572
|
function scheduleRefresh() {
|
|
2568
2573
|
if (refreshTimer) clearTimeout(refreshTimer);
|
|
2569
2574
|
if (!config.refreshToken) return;
|
|
2570
|
-
const exp =
|
|
2575
|
+
const exp = getTokenExpiry2(currentToken);
|
|
2571
2576
|
if (!exp) return;
|
|
2572
2577
|
const msUntilExpiry = exp * 1e3 - Date.now();
|
|
2573
2578
|
const refreshIn = Math.max(msUntilExpiry - REFRESH_BUFFER_MS, 0);
|
|
@@ -4798,26 +4803,449 @@ async function waitForAgent(port, maxAttempts = 30) {
|
|
|
4798
4803
|
}
|
|
4799
4804
|
}
|
|
4800
4805
|
|
|
4806
|
+
// src/service.ts
|
|
4807
|
+
import { execSync, spawn as spawn2 } from "child_process";
|
|
4808
|
+
import fs7 from "fs";
|
|
4809
|
+
import path7 from "path";
|
|
4810
|
+
import os4 from "os";
|
|
4811
|
+
function createServiceManager() {
|
|
4812
|
+
if (process.platform === "darwin") return new MacOSService();
|
|
4813
|
+
if (process.platform === "win32") return new WindowsService();
|
|
4814
|
+
if (process.platform === "linux") return new LinuxService();
|
|
4815
|
+
return new FallbackService();
|
|
4816
|
+
}
|
|
4817
|
+
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`);
|
|
4820
|
+
function escapeXml(s) {
|
|
4821
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4822
|
+
}
|
|
4823
|
+
var MacOSService = class {
|
|
4824
|
+
install(opts) {
|
|
4825
|
+
const args = [
|
|
4826
|
+
opts.nodePath,
|
|
4827
|
+
opts.entryPath,
|
|
4828
|
+
"start",
|
|
4829
|
+
"--foreground",
|
|
4830
|
+
"--port",
|
|
4831
|
+
String(opts.port),
|
|
4832
|
+
"--workspace",
|
|
4833
|
+
opts.workspacePath
|
|
4834
|
+
];
|
|
4835
|
+
if (!opts.upstream) {
|
|
4836
|
+
args.push("--no-upstream");
|
|
4837
|
+
}
|
|
4838
|
+
const logFile = path7.join(os4.homedir(), ".jarvis", "agent.log");
|
|
4839
|
+
const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
|
|
4840
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
4841
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4842
|
+
<plist version="1.0">
|
|
4843
|
+
<dict>
|
|
4844
|
+
<key>Label</key>
|
|
4845
|
+
<string>${PLIST_LABEL}</string>
|
|
4846
|
+
|
|
4847
|
+
<key>ProgramArguments</key>
|
|
4848
|
+
<array>
|
|
4849
|
+
${args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n")}
|
|
4850
|
+
</array>
|
|
4851
|
+
|
|
4852
|
+
<key>WorkingDirectory</key>
|
|
4853
|
+
<string>${escapeXml(opts.workspacePath)}</string>
|
|
4854
|
+
|
|
4855
|
+
<key>EnvironmentVariables</key>
|
|
4856
|
+
<dict>
|
|
4857
|
+
<key>PATH</key>
|
|
4858
|
+
<string>${escapeXml(envPath)}</string>
|
|
4859
|
+
<key>HOME</key>
|
|
4860
|
+
<string>${escapeXml(os4.homedir())}</string>
|
|
4861
|
+
<key>NODE_NO_WARNINGS</key>
|
|
4862
|
+
<string>1</string>
|
|
4863
|
+
</dict>
|
|
4864
|
+
|
|
4865
|
+
<key>RunAtLoad</key>
|
|
4866
|
+
<true/>
|
|
4867
|
+
|
|
4868
|
+
<key>KeepAlive</key>
|
|
4869
|
+
<true/>
|
|
4870
|
+
|
|
4871
|
+
<key>ProcessType</key>
|
|
4872
|
+
<string>Interactive</string>
|
|
4873
|
+
|
|
4874
|
+
<key>ThrottleInterval</key>
|
|
4875
|
+
<integer>5</integer>
|
|
4876
|
+
|
|
4877
|
+
<key>StandardOutPath</key>
|
|
4878
|
+
<string>${escapeXml(logFile)}</string>
|
|
4879
|
+
|
|
4880
|
+
<key>StandardErrorPath</key>
|
|
4881
|
+
<string>${escapeXml(logFile)}</string>
|
|
4882
|
+
</dict>
|
|
4883
|
+
</plist>
|
|
4884
|
+
`;
|
|
4885
|
+
fs7.mkdirSync(PLIST_DIR, { recursive: true });
|
|
4886
|
+
fs7.mkdirSync(path7.dirname(logFile), { recursive: true });
|
|
4887
|
+
if (this.isInstalled()) {
|
|
4888
|
+
try {
|
|
4889
|
+
execSync(`launchctl unload -w "${PLIST_PATH}"`, { stdio: "ignore" });
|
|
4890
|
+
} catch {
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
fs7.writeFileSync(PLIST_PATH, plist);
|
|
4894
|
+
execSync(`launchctl load -w "${PLIST_PATH}"`);
|
|
4895
|
+
}
|
|
4896
|
+
uninstall() {
|
|
4897
|
+
try {
|
|
4898
|
+
execSync(`launchctl unload -w "${PLIST_PATH}"`, { stdio: "ignore" });
|
|
4899
|
+
} catch {
|
|
4900
|
+
}
|
|
4901
|
+
try {
|
|
4902
|
+
fs7.unlinkSync(PLIST_PATH);
|
|
4903
|
+
} catch {
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
isInstalled() {
|
|
4907
|
+
return fs7.existsSync(PLIST_PATH);
|
|
4908
|
+
}
|
|
4909
|
+
start() {
|
|
4910
|
+
try {
|
|
4911
|
+
execSync(`launchctl load -w "${PLIST_PATH}"`);
|
|
4912
|
+
} catch {
|
|
4913
|
+
execSync(`launchctl start ${PLIST_LABEL}`);
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
stop() {
|
|
4917
|
+
try {
|
|
4918
|
+
execSync(`launchctl unload -w "${PLIST_PATH}"`, { stdio: "ignore" });
|
|
4919
|
+
} catch {
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
status() {
|
|
4923
|
+
const installed = this.isInstalled();
|
|
4924
|
+
if (!installed) return { installed: false, running: false };
|
|
4925
|
+
try {
|
|
4926
|
+
const output = execSync(`launchctl list ${PLIST_LABEL}`, {
|
|
4927
|
+
encoding: "utf-8",
|
|
4928
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
4929
|
+
});
|
|
4930
|
+
const pidMatch = output.match(/"PID"\s*=\s*(\d+)/);
|
|
4931
|
+
const pid = pidMatch ? parseInt(pidMatch[1], 10) : void 0;
|
|
4932
|
+
return { installed: true, running: pid !== void 0, pid, os: "macOS" };
|
|
4933
|
+
} catch {
|
|
4934
|
+
return { installed: true, running: false, os: "macOS" };
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
};
|
|
4938
|
+
var TASK_NAME = "JarvisAgent";
|
|
4939
|
+
var WindowsService = class {
|
|
4940
|
+
install(opts) {
|
|
4941
|
+
const args = [
|
|
4942
|
+
opts.entryPath,
|
|
4943
|
+
"start",
|
|
4944
|
+
"--foreground",
|
|
4945
|
+
"--port",
|
|
4946
|
+
String(opts.port),
|
|
4947
|
+
"--workspace",
|
|
4948
|
+
`"${opts.workspacePath}"`
|
|
4949
|
+
];
|
|
4950
|
+
if (!opts.upstream) {
|
|
4951
|
+
args.push("--no-upstream");
|
|
4952
|
+
}
|
|
4953
|
+
const xml = `<?xml version="1.0" encoding="UTF-16"?>
|
|
4954
|
+
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
4955
|
+
<RegistrationInfo>
|
|
4956
|
+
<Description>Jarvis AI Agent \u2014 always-on background service</Description>
|
|
4957
|
+
</RegistrationInfo>
|
|
4958
|
+
<Triggers>
|
|
4959
|
+
<LogonTrigger>
|
|
4960
|
+
<Enabled>true</Enabled>
|
|
4961
|
+
</LogonTrigger>
|
|
4962
|
+
</Triggers>
|
|
4963
|
+
<Principals>
|
|
4964
|
+
<Principal id="Author">
|
|
4965
|
+
<LogonType>InteractiveToken</LogonType>
|
|
4966
|
+
<RunLevel>LeastPrivilege</RunLevel>
|
|
4967
|
+
</Principal>
|
|
4968
|
+
</Principals>
|
|
4969
|
+
<Settings>
|
|
4970
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
4971
|
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
4972
|
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
4973
|
+
<AllowHardTerminate>true</AllowHardTerminate>
|
|
4974
|
+
<StartWhenAvailable>true</StartWhenAvailable>
|
|
4975
|
+
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
4976
|
+
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
4977
|
+
<Enabled>true</Enabled>
|
|
4978
|
+
<Hidden>false</Hidden>
|
|
4979
|
+
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
4980
|
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
4981
|
+
<Priority>7</Priority>
|
|
4982
|
+
<RestartOnFailure>
|
|
4983
|
+
<Interval>PT1M</Interval>
|
|
4984
|
+
<Count>999</Count>
|
|
4985
|
+
</RestartOnFailure>
|
|
4986
|
+
</Settings>
|
|
4987
|
+
<Actions Context="Author">
|
|
4988
|
+
<Exec>
|
|
4989
|
+
<Command>${escapeXml(opts.nodePath)}</Command>
|
|
4990
|
+
<Arguments>${escapeXml(args.join(" "))}</Arguments>
|
|
4991
|
+
<WorkingDirectory>${escapeXml(opts.workspacePath)}</WorkingDirectory>
|
|
4992
|
+
</Exec>
|
|
4993
|
+
</Actions>
|
|
4994
|
+
</Task>
|
|
4995
|
+
`;
|
|
4996
|
+
const tmpDir = os4.tmpdir();
|
|
4997
|
+
const tmpFile = path7.join(tmpDir, `jarvis-task-${Date.now()}.xml`);
|
|
4998
|
+
fs7.writeFileSync(tmpFile, xml, { encoding: "utf-16le" });
|
|
4999
|
+
try {
|
|
5000
|
+
execSync(`schtasks /Create /TN "${TASK_NAME}" /XML "${tmpFile}" /F`, {
|
|
5001
|
+
stdio: "ignore"
|
|
5002
|
+
});
|
|
5003
|
+
} finally {
|
|
5004
|
+
try {
|
|
5005
|
+
fs7.unlinkSync(tmpFile);
|
|
5006
|
+
} catch {
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
uninstall() {
|
|
5011
|
+
try {
|
|
5012
|
+
execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
|
|
5013
|
+
} catch {
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
isInstalled() {
|
|
5017
|
+
try {
|
|
5018
|
+
execSync(`schtasks /Query /TN "${TASK_NAME}"`, { stdio: "ignore" });
|
|
5019
|
+
return true;
|
|
5020
|
+
} catch {
|
|
5021
|
+
return false;
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
5024
|
+
start() {
|
|
5025
|
+
execSync(`schtasks /Run /TN "${TASK_NAME}"`, { stdio: "ignore" });
|
|
5026
|
+
}
|
|
5027
|
+
stop() {
|
|
5028
|
+
execSync(`schtasks /End /TN "${TASK_NAME}"`, { stdio: "ignore" });
|
|
5029
|
+
}
|
|
5030
|
+
status() {
|
|
5031
|
+
if (!this.isInstalled()) return { installed: false, running: false };
|
|
5032
|
+
try {
|
|
5033
|
+
const output = execSync(`schtasks /Query /TN "${TASK_NAME}" /FO CSV /NH`, {
|
|
5034
|
+
encoding: "utf-8"
|
|
5035
|
+
});
|
|
5036
|
+
const running = output.includes("Running");
|
|
5037
|
+
return { installed: true, running, os: "windows" };
|
|
5038
|
+
} catch {
|
|
5039
|
+
return { installed: true, running: false, os: "windows" };
|
|
5040
|
+
}
|
|
5041
|
+
}
|
|
5042
|
+
};
|
|
5043
|
+
var SYSTEMD_DIR = path7.join(os4.homedir(), ".config", "systemd", "user");
|
|
5044
|
+
var UNIT_NAME = "jarvis.service";
|
|
5045
|
+
var UNIT_PATH = path7.join(SYSTEMD_DIR, UNIT_NAME);
|
|
5046
|
+
var LinuxService = class {
|
|
5047
|
+
install(opts) {
|
|
5048
|
+
const args = [
|
|
5049
|
+
opts.entryPath,
|
|
5050
|
+
"start",
|
|
5051
|
+
"--foreground",
|
|
5052
|
+
"--port",
|
|
5053
|
+
String(opts.port),
|
|
5054
|
+
"--workspace",
|
|
5055
|
+
opts.workspacePath
|
|
5056
|
+
];
|
|
5057
|
+
if (!opts.upstream) {
|
|
5058
|
+
args.push("--no-upstream");
|
|
5059
|
+
}
|
|
5060
|
+
const logFile = path7.join(os4.homedir(), ".jarvis", "agent.log");
|
|
5061
|
+
const envPath = process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin";
|
|
5062
|
+
const unit = `[Unit]
|
|
5063
|
+
Description=Jarvis AI Agent
|
|
5064
|
+
After=network.target
|
|
5065
|
+
|
|
5066
|
+
[Service]
|
|
5067
|
+
Type=simple
|
|
5068
|
+
ExecStart=${opts.nodePath} ${args.join(" ")}
|
|
5069
|
+
WorkingDirectory=${opts.workspacePath}
|
|
5070
|
+
Environment=PATH=${envPath}
|
|
5071
|
+
Environment=HOME=${os4.homedir()}
|
|
5072
|
+
Environment=NODE_NO_WARNINGS=1
|
|
5073
|
+
Restart=always
|
|
5074
|
+
RestartSec=5
|
|
5075
|
+
StandardOutput=append:${logFile}
|
|
5076
|
+
StandardError=append:${logFile}
|
|
5077
|
+
|
|
5078
|
+
[Install]
|
|
5079
|
+
WantedBy=default.target
|
|
5080
|
+
`;
|
|
5081
|
+
fs7.mkdirSync(SYSTEMD_DIR, { recursive: true });
|
|
5082
|
+
fs7.mkdirSync(path7.dirname(logFile), { recursive: true });
|
|
5083
|
+
if (this.isInstalled()) {
|
|
5084
|
+
try {
|
|
5085
|
+
execSync("systemctl --user stop jarvis.service", { stdio: "ignore" });
|
|
5086
|
+
} catch {
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
fs7.writeFileSync(UNIT_PATH, unit);
|
|
5090
|
+
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
5091
|
+
execSync("systemctl --user enable jarvis.service", { stdio: "ignore" });
|
|
5092
|
+
execSync("systemctl --user start jarvis.service", { stdio: "ignore" });
|
|
5093
|
+
try {
|
|
5094
|
+
execSync(`loginctl enable-linger ${os4.userInfo().username}`, { stdio: "ignore" });
|
|
5095
|
+
} catch {
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
uninstall() {
|
|
5099
|
+
try {
|
|
5100
|
+
execSync("systemctl --user stop jarvis.service", { stdio: "ignore" });
|
|
5101
|
+
} catch {
|
|
5102
|
+
}
|
|
5103
|
+
try {
|
|
5104
|
+
execSync("systemctl --user disable jarvis.service", { stdio: "ignore" });
|
|
5105
|
+
} catch {
|
|
5106
|
+
}
|
|
5107
|
+
try {
|
|
5108
|
+
fs7.unlinkSync(UNIT_PATH);
|
|
5109
|
+
} catch {
|
|
5110
|
+
}
|
|
5111
|
+
try {
|
|
5112
|
+
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
5113
|
+
} catch {
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
isInstalled() {
|
|
5117
|
+
return fs7.existsSync(UNIT_PATH);
|
|
5118
|
+
}
|
|
5119
|
+
start() {
|
|
5120
|
+
execSync("systemctl --user start jarvis.service");
|
|
5121
|
+
}
|
|
5122
|
+
stop() {
|
|
5123
|
+
execSync("systemctl --user stop jarvis.service", { stdio: "ignore" });
|
|
5124
|
+
}
|
|
5125
|
+
status() {
|
|
5126
|
+
if (!this.isInstalled()) return { installed: false, running: false };
|
|
5127
|
+
try {
|
|
5128
|
+
const output = execSync("systemctl --user show jarvis.service --property=ActiveState,MainPID", {
|
|
5129
|
+
encoding: "utf-8"
|
|
5130
|
+
});
|
|
5131
|
+
const active = output.includes("ActiveState=active");
|
|
5132
|
+
const pidMatch = output.match(/MainPID=(\d+)/);
|
|
5133
|
+
const pid = pidMatch ? parseInt(pidMatch[1], 10) : void 0;
|
|
5134
|
+
return {
|
|
5135
|
+
installed: true,
|
|
5136
|
+
running: active && pid !== void 0 && pid > 0,
|
|
5137
|
+
pid: pid && pid > 0 ? pid : void 0,
|
|
5138
|
+
os: "linux"
|
|
5139
|
+
};
|
|
5140
|
+
} catch {
|
|
5141
|
+
return { installed: true, running: false, os: "linux" };
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
};
|
|
5145
|
+
var FallbackService = class {
|
|
5146
|
+
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");
|
|
5150
|
+
}
|
|
5151
|
+
install(opts) {
|
|
5152
|
+
const jarvisDir = path7.join(os4.homedir(), ".jarvis");
|
|
5153
|
+
fs7.mkdirSync(jarvisDir, { recursive: true });
|
|
5154
|
+
this.stop();
|
|
5155
|
+
const args = [
|
|
5156
|
+
opts.entryPath,
|
|
5157
|
+
"start",
|
|
5158
|
+
"--foreground",
|
|
5159
|
+
"--port",
|
|
5160
|
+
String(opts.port),
|
|
5161
|
+
"--workspace",
|
|
5162
|
+
opts.workspacePath
|
|
5163
|
+
];
|
|
5164
|
+
if (!opts.upstream) {
|
|
5165
|
+
args.push("--no-upstream");
|
|
5166
|
+
}
|
|
5167
|
+
const logFd = fs7.openSync(this.logFile, "a");
|
|
5168
|
+
const child = spawn2(opts.nodePath, args, {
|
|
5169
|
+
detached: true,
|
|
5170
|
+
stdio: ["ignore", logFd, logFd],
|
|
5171
|
+
cwd: opts.workspacePath,
|
|
5172
|
+
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
5173
|
+
});
|
|
5174
|
+
child.unref();
|
|
5175
|
+
fs7.closeSync(logFd);
|
|
5176
|
+
fs7.writeFileSync(this.pidFile, String(child.pid));
|
|
5177
|
+
fs7.writeFileSync(this.markerFile, JSON.stringify({
|
|
5178
|
+
nodePath: opts.nodePath,
|
|
5179
|
+
entryPath: opts.entryPath,
|
|
5180
|
+
port: opts.port,
|
|
5181
|
+
workspacePath: opts.workspacePath,
|
|
5182
|
+
upstream: opts.upstream
|
|
5183
|
+
}));
|
|
5184
|
+
console.log(
|
|
5185
|
+
`\x1B[33mNote:\x1B[0m OS-level service not available on ${process.platform}.`
|
|
5186
|
+
);
|
|
5187
|
+
console.log(" Agent is running as a background process (PID: " + child.pid + ").");
|
|
5188
|
+
console.log(" It will NOT auto-start on reboot or recover from crashes.");
|
|
5189
|
+
}
|
|
5190
|
+
uninstall() {
|
|
5191
|
+
this.stop();
|
|
5192
|
+
try {
|
|
5193
|
+
fs7.unlinkSync(this.markerFile);
|
|
5194
|
+
} catch {
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
isInstalled() {
|
|
5198
|
+
return fs7.existsSync(this.markerFile);
|
|
5199
|
+
}
|
|
5200
|
+
start() {
|
|
5201
|
+
if (!this.isInstalled()) {
|
|
5202
|
+
throw new Error("Service not installed. Run 'jarvis install' first.");
|
|
5203
|
+
}
|
|
5204
|
+
const saved = JSON.parse(fs7.readFileSync(this.markerFile, "utf-8"));
|
|
5205
|
+
this.install(saved);
|
|
5206
|
+
}
|
|
5207
|
+
stop() {
|
|
5208
|
+
try {
|
|
5209
|
+
const pid = parseInt(fs7.readFileSync(this.pidFile, "utf-8").trim(), 10);
|
|
5210
|
+
if (!isNaN(pid)) {
|
|
5211
|
+
process.kill(pid, "SIGTERM");
|
|
5212
|
+
}
|
|
5213
|
+
} catch {
|
|
5214
|
+
}
|
|
5215
|
+
try {
|
|
5216
|
+
fs7.unlinkSync(this.pidFile);
|
|
5217
|
+
} catch {
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5220
|
+
status() {
|
|
5221
|
+
if (!this.isInstalled()) return { installed: false, running: false };
|
|
5222
|
+
try {
|
|
5223
|
+
const pid = parseInt(fs7.readFileSync(this.pidFile, "utf-8").trim(), 10);
|
|
5224
|
+
if (isNaN(pid)) return { installed: true, running: false };
|
|
5225
|
+
process.kill(pid, 0);
|
|
5226
|
+
return { installed: true, running: true, pid };
|
|
5227
|
+
} catch {
|
|
5228
|
+
return { installed: true, running: false };
|
|
5229
|
+
}
|
|
5230
|
+
}
|
|
5231
|
+
};
|
|
5232
|
+
|
|
4801
5233
|
// src/cli.ts
|
|
4802
5234
|
import { createRequire } from "module";
|
|
4803
5235
|
var _require = createRequire(import.meta.url);
|
|
4804
5236
|
var PKG_VERSION = _require("../package.json").version ?? "dev";
|
|
4805
|
-
var LOG_DIR =
|
|
4806
|
-
var LOG_FILE =
|
|
4807
|
-
var PID_FILE =
|
|
4808
|
-
function savePid(pid) {
|
|
4809
|
-
fs7.mkdirSync(LOG_DIR, { recursive: true });
|
|
4810
|
-
fs7.writeFileSync(PID_FILE, String(pid));
|
|
4811
|
-
}
|
|
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");
|
|
4812
5240
|
function readPid() {
|
|
4813
5241
|
try {
|
|
4814
|
-
const pid = parseInt(
|
|
5242
|
+
const pid = parseInt(fs8.readFileSync(PID_FILE, "utf-8").trim(), 10);
|
|
4815
5243
|
if (isNaN(pid)) return null;
|
|
4816
5244
|
try {
|
|
4817
5245
|
process.kill(pid, 0);
|
|
4818
5246
|
return pid;
|
|
4819
5247
|
} catch {
|
|
4820
|
-
|
|
5248
|
+
fs8.unlinkSync(PID_FILE);
|
|
4821
5249
|
return null;
|
|
4822
5250
|
}
|
|
4823
5251
|
} catch {
|
|
@@ -4826,7 +5254,7 @@ function readPid() {
|
|
|
4826
5254
|
}
|
|
4827
5255
|
function clearPid() {
|
|
4828
5256
|
try {
|
|
4829
|
-
|
|
5257
|
+
fs8.unlinkSync(PID_FILE);
|
|
4830
5258
|
} catch {
|
|
4831
5259
|
}
|
|
4832
5260
|
}
|
|
@@ -4876,7 +5304,7 @@ async function findFreePort() {
|
|
|
4876
5304
|
async function browserAuth(appUrl) {
|
|
4877
5305
|
const config = loadConfig();
|
|
4878
5306
|
const callbackPort = await findFreePort();
|
|
4879
|
-
const loginUrl = `${appUrl}/
|
|
5307
|
+
const loginUrl = `${appUrl}/auth/cli?port=${callbackPort}`;
|
|
4880
5308
|
console.log();
|
|
4881
5309
|
console.log("Opening browser for sign in...");
|
|
4882
5310
|
console.log(` If it doesn't open, visit: ${loginUrl}`);
|
|
@@ -4906,24 +5334,59 @@ async function browserAuth(appUrl) {
|
|
|
4906
5334
|
return false;
|
|
4907
5335
|
}
|
|
4908
5336
|
}
|
|
4909
|
-
|
|
5337
|
+
function getAppUrl() {
|
|
5338
|
+
return "https://jarvis.appchy.com";
|
|
5339
|
+
}
|
|
5340
|
+
function getTokenExpiry(token) {
|
|
5341
|
+
try {
|
|
5342
|
+
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
|
|
5343
|
+
return payload.exp ?? null;
|
|
5344
|
+
} catch {
|
|
5345
|
+
return null;
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
5348
|
+
function isTokenExpired(token, bufferMs = 6e4) {
|
|
5349
|
+
const exp = getTokenExpiry(token);
|
|
5350
|
+
if (!exp) return true;
|
|
5351
|
+
return exp * 1e3 - Date.now() < bufferMs;
|
|
5352
|
+
}
|
|
5353
|
+
async function tryRefreshToken(config) {
|
|
5354
|
+
if (!config.apiUrl || !config.refreshToken) return null;
|
|
5355
|
+
const refreshUrl = config.apiUrl.replace(/^wss:/, "https:").replace(/^ws:/, "http:") + "/refresh";
|
|
5356
|
+
try {
|
|
5357
|
+
const res = await fetch(refreshUrl, {
|
|
5358
|
+
method: "POST",
|
|
5359
|
+
headers: { "Content-Type": "application/json" },
|
|
5360
|
+
body: JSON.stringify({ refreshToken: config.refreshToken })
|
|
5361
|
+
});
|
|
5362
|
+
if (!res.ok) return null;
|
|
5363
|
+
const { token } = await res.json();
|
|
5364
|
+
return token;
|
|
5365
|
+
} catch {
|
|
5366
|
+
return null;
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
4910
5369
|
async function ensureSetup() {
|
|
4911
5370
|
const config = loadConfig();
|
|
4912
|
-
if (config?.
|
|
5371
|
+
if (config?.anthropicApiKey || config?.useSubscription) {
|
|
4913
5372
|
return true;
|
|
4914
5373
|
}
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
const pid = readPid();
|
|
4918
|
-
if (pid) {
|
|
4919
|
-
try {
|
|
4920
|
-
process.kill(pid, "SIGTERM");
|
|
4921
|
-
} catch {
|
|
4922
|
-
}
|
|
4923
|
-
clearPid();
|
|
4924
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
5374
|
+
if (!config?.token) {
|
|
5375
|
+
return browserAuth(getAppUrl());
|
|
4925
5376
|
}
|
|
4926
|
-
|
|
5377
|
+
if (!isTokenExpired(config.token)) {
|
|
5378
|
+
return true;
|
|
5379
|
+
}
|
|
5380
|
+
console.log("Token expired, attempting refresh...");
|
|
5381
|
+
const newToken = await tryRefreshToken(config);
|
|
5382
|
+
if (newToken) {
|
|
5383
|
+
saveConfig({ ...config, token: newToken, connectedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5384
|
+
console.log("Token refreshed successfully.");
|
|
5385
|
+
return true;
|
|
5386
|
+
}
|
|
5387
|
+
console.log("Token refresh failed. Re-authenticating...");
|
|
5388
|
+
clearConfig();
|
|
5389
|
+
return browserAuth(getAppUrl());
|
|
4927
5390
|
}
|
|
4928
5391
|
function createCli() {
|
|
4929
5392
|
const program = new Command().name("jarvis").description("Jarvis local agent \u2014 runs Claude Code on your machine").version(PKG_VERSION);
|
|
@@ -4953,98 +5416,106 @@ function createCli() {
|
|
|
4953
5416
|
process.exit(1);
|
|
4954
5417
|
}
|
|
4955
5418
|
});
|
|
4956
|
-
program.command("start").description("Start the local agent (
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
5419
|
+
program.command("start").description("Start the local agent (authenticates, cleans up stale processes, starts fresh)").option("-p, --port <port>", "Local WS server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--api-key <key>", "Anthropic API key (for local-only use)").option("--no-upstream", "Don't connect to cloud (local-only mode)").option("--foreground", "Run in foreground (don't daemonize)").action(
|
|
5420
|
+
async (opts) => {
|
|
5421
|
+
if (!opts.foreground) {
|
|
5422
|
+
const ok = await ensureSetup();
|
|
5423
|
+
if (!ok) {
|
|
5424
|
+
console.error("Setup failed. Cannot start agent.");
|
|
5425
|
+
process.exit(1);
|
|
5426
|
+
}
|
|
5427
|
+
}
|
|
5428
|
+
const config = loadConfig();
|
|
5429
|
+
const port = parseInt(opts.port, 10);
|
|
5430
|
+
const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
|
|
5431
|
+
const userId = config?.userId ?? process.env.JARVIS_USER_ID ?? "local";
|
|
5432
|
+
const explicitApiKey = opts.apiKey ?? config?.anthropicApiKey;
|
|
5433
|
+
const useSubscription = !explicitApiKey;
|
|
5434
|
+
const anthropicApiKey = explicitApiKey;
|
|
5435
|
+
if (opts.foreground) {
|
|
5436
|
+
if (useSubscription && process.env.ANTHROPIC_API_KEY) {
|
|
5437
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
5438
|
+
}
|
|
5439
|
+
await startAgent({
|
|
5440
|
+
port,
|
|
5441
|
+
workspacePath,
|
|
5442
|
+
anthropicApiKey,
|
|
5443
|
+
useSubscription,
|
|
5444
|
+
userId,
|
|
5445
|
+
upstream: (() => {
|
|
5446
|
+
const apiUrl = process.env.JARVIS_UPSTREAM_URL ?? config?.apiUrl;
|
|
5447
|
+
const token = process.env.JARVIS_UPSTREAM_TOKEN ?? config?.token;
|
|
5448
|
+
if (opts.upstream && apiUrl && token) {
|
|
5449
|
+
return { apiUrl, token, refreshToken: config?.refreshToken };
|
|
5450
|
+
}
|
|
5451
|
+
return void 0;
|
|
5452
|
+
})()
|
|
5453
|
+
});
|
|
4967
5454
|
return;
|
|
4968
5455
|
}
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
5456
|
+
const service = createServiceManager();
|
|
5457
|
+
const stalePid = readPid();
|
|
5458
|
+
if (stalePid) {
|
|
5459
|
+
try {
|
|
5460
|
+
process.kill(stalePid, "SIGTERM");
|
|
5461
|
+
} catch {
|
|
5462
|
+
}
|
|
5463
|
+
clearPid();
|
|
5464
|
+
}
|
|
5465
|
+
if (service.isInstalled()) {
|
|
5466
|
+
try {
|
|
5467
|
+
service.stop();
|
|
5468
|
+
} catch {
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
if (await isPortInUse(port)) {
|
|
5472
|
+
try {
|
|
5473
|
+
const output = execSync2(`lsof -ti :${port}`, { encoding: "utf-8" }).trim();
|
|
5474
|
+
if (output) {
|
|
5475
|
+
for (const p of output.split("\n")) {
|
|
5476
|
+
try {
|
|
5477
|
+
process.kill(parseInt(p, 10), "SIGTERM");
|
|
5478
|
+
} catch {
|
|
5479
|
+
}
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
} catch {
|
|
5483
|
+
}
|
|
5484
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
4978
5485
|
}
|
|
4979
|
-
|
|
5486
|
+
const binPath = process.argv[1];
|
|
5487
|
+
const cliRoot = path8.resolve(path8.dirname(binPath), "..");
|
|
5488
|
+
const distEntry = path8.join(cliRoot, "dist", "bin.js");
|
|
5489
|
+
const entryPath = fs8.existsSync(distEntry) ? distEntry : binPath;
|
|
5490
|
+
service.install({
|
|
4980
5491
|
port,
|
|
4981
5492
|
workspacePath,
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
upstream: (() => {
|
|
4986
|
-
const apiUrl = process.env.JARVIS_UPSTREAM_URL ?? config?.apiUrl;
|
|
4987
|
-
const token = process.env.JARVIS_UPSTREAM_TOKEN ?? config?.token;
|
|
4988
|
-
if (opts.upstream && apiUrl && token) {
|
|
4989
|
-
return { apiUrl, token, refreshToken: config?.refreshToken };
|
|
4990
|
-
}
|
|
4991
|
-
return void 0;
|
|
4992
|
-
})()
|
|
5493
|
+
nodePath: process.execPath,
|
|
5494
|
+
entryPath,
|
|
5495
|
+
upstream: opts.upstream
|
|
4993
5496
|
});
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
workspacePath
|
|
5005
|
-
];
|
|
5006
|
-
if (!opts.upstream) {
|
|
5007
|
-
args.push("--no-upstream");
|
|
5008
|
-
}
|
|
5009
|
-
if (anthropicApiKey) {
|
|
5010
|
-
args.push("--api-key", anthropicApiKey);
|
|
5011
|
-
}
|
|
5012
|
-
const binPath = process.argv[1];
|
|
5013
|
-
const cliRoot = path7.resolve(path7.dirname(binPath), "..");
|
|
5014
|
-
const distEntry = path7.join(cliRoot, "dist", "bin.js");
|
|
5015
|
-
const useDistEntry = fs7.existsSync(distEntry);
|
|
5016
|
-
const child = spawn2(
|
|
5017
|
-
process.execPath,
|
|
5018
|
-
useDistEntry ? [distEntry, ...args] : [binPath, ...args],
|
|
5019
|
-
{
|
|
5020
|
-
detached: true,
|
|
5021
|
-
stdio: ["ignore", logFd, logFd],
|
|
5022
|
-
cwd: workspacePath,
|
|
5023
|
-
env: {
|
|
5024
|
-
...process.env,
|
|
5025
|
-
NODE_NO_WARNINGS: "1",
|
|
5026
|
-
// Strip API key when using subscription to prevent SDK from picking it up
|
|
5027
|
-
...useSubscription ? { ANTHROPIC_API_KEY: "" } : {}
|
|
5028
|
-
}
|
|
5497
|
+
const status = service.status();
|
|
5498
|
+
console.log(`Jarvis agent started`);
|
|
5499
|
+
console.log(` Local: ws://127.0.0.1:${port}`);
|
|
5500
|
+
console.log(` Workspace: ${workspacePath}`);
|
|
5501
|
+
console.log(` Logs: ${LOG_FILE}`);
|
|
5502
|
+
if (config?.apiUrl) {
|
|
5503
|
+
console.log(` Cloud: ${config.apiUrl}`);
|
|
5504
|
+
}
|
|
5505
|
+
if (status.os) {
|
|
5506
|
+
console.log(` Service: ${status.os} (auto-start on login, crash recovery)`);
|
|
5029
5507
|
}
|
|
5030
|
-
);
|
|
5031
|
-
child.unref();
|
|
5032
|
-
fs7.closeSync(logFd);
|
|
5033
|
-
savePid(child.pid);
|
|
5034
|
-
console.log(`Jarvis agent started (PID: ${child.pid})`);
|
|
5035
|
-
console.log(` Local: ws://127.0.0.1:${port}`);
|
|
5036
|
-
console.log(` Workspace: ${workspacePath}`);
|
|
5037
|
-
console.log(` Logs: ${LOG_FILE}`);
|
|
5038
|
-
if (config?.apiUrl) {
|
|
5039
|
-
console.log(` Cloud: ${config.apiUrl}`);
|
|
5040
5508
|
}
|
|
5041
|
-
|
|
5042
|
-
console.log(`Commands:`);
|
|
5043
|
-
console.log(` jarvis logs \u2014 View agent logs`);
|
|
5044
|
-
console.log(` jarvis stop \u2014 Stop the agent`);
|
|
5045
|
-
console.log(` jarvis restart \u2014 Restart the agent`);
|
|
5046
|
-
});
|
|
5509
|
+
);
|
|
5047
5510
|
program.command("stop").description("Stop the running agent").action(async () => {
|
|
5511
|
+
const service = createServiceManager();
|
|
5512
|
+
if (service.isInstalled()) {
|
|
5513
|
+
service.stop();
|
|
5514
|
+
clearPid();
|
|
5515
|
+
console.log("Agent stopped via OS service.");
|
|
5516
|
+
console.log("Use 'jarvis start' to re-enable, or 'jarvis uninstall' to remove auto-start.");
|
|
5517
|
+
return;
|
|
5518
|
+
}
|
|
5048
5519
|
const pid = readPid();
|
|
5049
5520
|
if (pid) {
|
|
5050
5521
|
try {
|
|
@@ -5060,7 +5531,7 @@ function createCli() {
|
|
|
5060
5531
|
const running = await isPortInUse(7862);
|
|
5061
5532
|
if (running) {
|
|
5062
5533
|
try {
|
|
5063
|
-
const output =
|
|
5534
|
+
const output = execSync2("lsof -ti :7862", { encoding: "utf-8" }).trim();
|
|
5064
5535
|
if (output) {
|
|
5065
5536
|
const pids = output.split("\n");
|
|
5066
5537
|
for (const p of pids) {
|
|
@@ -5077,27 +5548,16 @@ function createCli() {
|
|
|
5077
5548
|
}
|
|
5078
5549
|
console.log("No agent is running.");
|
|
5079
5550
|
});
|
|
5080
|
-
program.command("restart").description("Restart the agent").action(async () => {
|
|
5081
|
-
const pid = readPid();
|
|
5082
|
-
if (pid) {
|
|
5083
|
-
try {
|
|
5084
|
-
process.kill(pid, "SIGTERM");
|
|
5085
|
-
} catch {
|
|
5086
|
-
}
|
|
5087
|
-
clearPid();
|
|
5088
|
-
console.log(`Stopped agent (PID: ${pid})`);
|
|
5089
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
5090
|
-
}
|
|
5091
|
-
console.log("Starting agent...");
|
|
5551
|
+
program.command("restart").description("Restart the agent (alias for start \u2014 start always does a clean restart)").action(async () => {
|
|
5092
5552
|
await program.parseAsync(["node", "jarvis", "start"]);
|
|
5093
5553
|
});
|
|
5094
5554
|
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) => {
|
|
5095
|
-
if (!
|
|
5555
|
+
if (!fs8.existsSync(LOG_FILE)) {
|
|
5096
5556
|
console.log("No log file found. Start the agent first: jarvis start");
|
|
5097
5557
|
return;
|
|
5098
5558
|
}
|
|
5099
5559
|
if (opts.follow) {
|
|
5100
|
-
const tail =
|
|
5560
|
+
const tail = spawn3("tail", ["-f", "-n", opts.lines, LOG_FILE], {
|
|
5101
5561
|
stdio: "inherit"
|
|
5102
5562
|
});
|
|
5103
5563
|
process.on("SIGINT", () => {
|
|
@@ -5106,7 +5566,7 @@ function createCli() {
|
|
|
5106
5566
|
});
|
|
5107
5567
|
tail.on("exit", () => process.exit(0));
|
|
5108
5568
|
} else {
|
|
5109
|
-
const content =
|
|
5569
|
+
const content = execSync2(`tail -n ${opts.lines} "${LOG_FILE}"`, { encoding: "utf-8" });
|
|
5110
5570
|
process.stdout.write(content);
|
|
5111
5571
|
}
|
|
5112
5572
|
});
|
|
@@ -5116,7 +5576,9 @@ function createCli() {
|
|
|
5116
5576
|
const port = config?.port ?? 7862;
|
|
5117
5577
|
const running = await isPortInUse(port);
|
|
5118
5578
|
console.log(`Jarvis Agent v${PKG_VERSION}:`);
|
|
5119
|
-
console.log(
|
|
5579
|
+
console.log(
|
|
5580
|
+
` Status: ${running ? `\x1B[32mrunning\x1B[0m` : `\x1B[31mstopped\x1B[0m`}${pid ? ` (PID: ${pid})` : ""}`
|
|
5581
|
+
);
|
|
5120
5582
|
console.log(` Port: ${port}`);
|
|
5121
5583
|
console.log(` Config: ${getConfigPath()}`);
|
|
5122
5584
|
if (config) {
|
|
@@ -5130,8 +5592,79 @@ function createCli() {
|
|
|
5130
5592
|
} else {
|
|
5131
5593
|
console.log(` Config: Not set up. Run 'jarvis connect <token>'`);
|
|
5132
5594
|
}
|
|
5595
|
+
const svc = createServiceManager();
|
|
5596
|
+
const svcStatus = svc.status();
|
|
5597
|
+
if (svcStatus.installed) {
|
|
5598
|
+
const label = svcStatus.os ?? "unknown";
|
|
5599
|
+
const state2 = svcStatus.running ? `\x1B[32minstalled & running\x1B[0m (${label})` : `\x1B[33minstalled, stopped\x1B[0m (${label})`;
|
|
5600
|
+
console.log(` Service: ${state2}`);
|
|
5601
|
+
} else {
|
|
5602
|
+
console.log(` Service: not installed (run 'jarvis install' for always-on)`);
|
|
5603
|
+
}
|
|
5133
5604
|
console.log(` Logs: ${LOG_FILE}`);
|
|
5134
5605
|
});
|
|
5606
|
+
program.command("install").description("Install as OS service for auto-start on login and crash recovery").option("-p, --port <port>", "Local WS server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--no-upstream", "Don't connect to cloud").action(async (opts) => {
|
|
5607
|
+
await ensureSetup();
|
|
5608
|
+
const config = loadConfig();
|
|
5609
|
+
const port = parseInt(opts.port, 10);
|
|
5610
|
+
const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
|
|
5611
|
+
const service = createServiceManager();
|
|
5612
|
+
const binPath = process.argv[1];
|
|
5613
|
+
const cliRoot = path8.resolve(path8.dirname(binPath), "..");
|
|
5614
|
+
const distEntry = path8.join(cliRoot, "dist", "bin.js");
|
|
5615
|
+
const entryPath = fs8.existsSync(distEntry) ? distEntry : binPath;
|
|
5616
|
+
if (!fs8.existsSync(distEntry)) {
|
|
5617
|
+
console.log("\x1B[33mWarning:\x1B[0m Using dev mode entry point (tsx).");
|
|
5618
|
+
console.log(" For reliability, build first: pnpm build");
|
|
5619
|
+
console.log();
|
|
5620
|
+
}
|
|
5621
|
+
const pid = readPid();
|
|
5622
|
+
if (pid) {
|
|
5623
|
+
try {
|
|
5624
|
+
process.kill(pid, "SIGTERM");
|
|
5625
|
+
} catch {
|
|
5626
|
+
}
|
|
5627
|
+
clearPid();
|
|
5628
|
+
}
|
|
5629
|
+
try {
|
|
5630
|
+
service.install({
|
|
5631
|
+
port,
|
|
5632
|
+
workspacePath,
|
|
5633
|
+
nodePath: process.execPath,
|
|
5634
|
+
entryPath,
|
|
5635
|
+
upstream: opts.upstream
|
|
5636
|
+
});
|
|
5637
|
+
} catch (err) {
|
|
5638
|
+
console.error(`Failed to install service: ${err instanceof Error ? err.message : err}`);
|
|
5639
|
+
process.exit(1);
|
|
5640
|
+
}
|
|
5641
|
+
const status = service.status();
|
|
5642
|
+
console.log(`Jarvis agent installed as OS service`);
|
|
5643
|
+
console.log(` Platform: ${status.os}`);
|
|
5644
|
+
console.log(` Port: ${port}`);
|
|
5645
|
+
console.log(` Workspace: ${workspacePath}`);
|
|
5646
|
+
console.log(` Auto-start on login: \x1B[32menabled\x1B[0m`);
|
|
5647
|
+
console.log(` Crash recovery: \x1B[32menabled\x1B[0m`);
|
|
5648
|
+
console.log();
|
|
5649
|
+
console.log(`Commands:`);
|
|
5650
|
+
console.log(` jarvis status \u2014 Check service status`);
|
|
5651
|
+
console.log(` jarvis uninstall \u2014 Remove OS service`);
|
|
5652
|
+
});
|
|
5653
|
+
program.command("uninstall").description("Remove OS service (stops auto-start and crash recovery)").action(() => {
|
|
5654
|
+
const service = createServiceManager();
|
|
5655
|
+
if (!service.isInstalled()) {
|
|
5656
|
+
console.log("No service is installed. Nothing to do.");
|
|
5657
|
+
return;
|
|
5658
|
+
}
|
|
5659
|
+
try {
|
|
5660
|
+
service.stop();
|
|
5661
|
+
} catch {
|
|
5662
|
+
}
|
|
5663
|
+
service.uninstall();
|
|
5664
|
+
clearPid();
|
|
5665
|
+
console.log("Jarvis OS service removed.");
|
|
5666
|
+
console.log("The agent will no longer auto-start on login or restart on crash.");
|
|
5667
|
+
});
|
|
5135
5668
|
program.command("logout").description("Clear saved configuration").action(() => {
|
|
5136
5669
|
clearConfig();
|
|
5137
5670
|
console.log("Configuration cleared.");
|
|
@@ -5144,5 +5677,20 @@ function createCli() {
|
|
|
5144
5677
|
}
|
|
5145
5678
|
|
|
5146
5679
|
// src/bin.ts
|
|
5680
|
+
function findEnv() {
|
|
5681
|
+
let dir = process.cwd();
|
|
5682
|
+
while (dir !== path9.dirname(dir)) {
|
|
5683
|
+
const envPath = path9.join(dir, ".env");
|
|
5684
|
+
if (fs9.existsSync(envPath)) return envPath;
|
|
5685
|
+
dir = path9.dirname(dir);
|
|
5686
|
+
}
|
|
5687
|
+
return void 0;
|
|
5688
|
+
}
|
|
5689
|
+
var envFile = findEnv();
|
|
5690
|
+
if (envFile) {
|
|
5691
|
+
const prev = process.env.ANTHROPIC_API_KEY;
|
|
5692
|
+
dotenv.config({ path: envFile });
|
|
5693
|
+
if (prev === void 0) delete process.env.ANTHROPIC_API_KEY;
|
|
5694
|
+
}
|
|
5147
5695
|
createCli().parse();
|
|
5148
5696
|
//# sourceMappingURL=bin.js.map
|