@appchy/jarvis 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,10 +1,14 @@
1
1
  // src/bin.ts
2
2
  import dotenv from "dotenv";
3
- import fs7 from "fs";
4
- import path7 from "path";
3
+ import fs8 from "fs";
4
+ import path8 from "path";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
8
+ import { spawn as spawn2, execSync } from "child_process";
9
+ import fs7 from "fs";
10
+ import path7 from "path";
11
+ import os4 from "os";
8
12
 
9
13
  // src/config.ts
10
14
  import fs from "fs";
@@ -4660,6 +4664,34 @@ async function waitForAgent(port, maxAttempts = 30) {
4660
4664
  }
4661
4665
 
4662
4666
  // src/cli.ts
4667
+ var LOG_DIR = path7.join(os4.homedir(), ".jarvis");
4668
+ var LOG_FILE = path7.join(LOG_DIR, "agent.log");
4669
+ var PID_FILE = path7.join(LOG_DIR, "agent.pid");
4670
+ function savePid(pid) {
4671
+ fs7.mkdirSync(LOG_DIR, { recursive: true });
4672
+ fs7.writeFileSync(PID_FILE, String(pid));
4673
+ }
4674
+ function readPid() {
4675
+ try {
4676
+ const pid = parseInt(fs7.readFileSync(PID_FILE, "utf-8").trim(), 10);
4677
+ if (isNaN(pid)) return null;
4678
+ try {
4679
+ process.kill(pid, 0);
4680
+ return pid;
4681
+ } catch {
4682
+ fs7.unlinkSync(PID_FILE);
4683
+ return null;
4684
+ }
4685
+ } catch {
4686
+ return null;
4687
+ }
4688
+ }
4689
+ function clearPid() {
4690
+ try {
4691
+ fs7.unlinkSync(PID_FILE);
4692
+ } catch {
4693
+ }
4694
+ }
4663
4695
  function createCli() {
4664
4696
  const program = new Command().name("jarvis").description("Jarvis local agent \u2014 runs Claude Code on your machine").version("0.0.0");
4665
4697
  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) => {
@@ -4687,15 +4719,13 @@ function createCli() {
4687
4719
  process.exit(1);
4688
4720
  }
4689
4721
  });
4690
- program.command("start").description("Start the local agent").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)").action(async (opts) => {
4722
+ program.command("start").description("Start the local agent (runs as background daemon)").option("-p, --port <port>", "Local WS server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--api-key <key>", "Anthropic API key (for local-only use)").option("--no-upstream", "Don't connect to cloud (local-only mode)").option("--foreground", "Run in foreground (don't daemonize)").action(async (opts) => {
4691
4723
  const config = loadConfig();
4692
4724
  const port = parseInt(opts.port, 10);
4693
4725
  const alreadyRunning = await isPortInUse(port);
4694
4726
  if (alreadyRunning) {
4695
- console.log(`
4696
- Jarvis agent is already running on ws://127.0.0.1:${port}`);
4697
- console.log(`Clients (jarvis chat, VSCode) can connect to it.
4698
- `);
4727
+ console.log(`Jarvis agent is already running on ws://127.0.0.1:${port}`);
4728
+ console.log(`Use 'jarvis restart' to restart, or 'jarvis logs' to view logs.`);
4699
4729
  return;
4700
4730
  }
4701
4731
  const workspacePath = opts.workspace ?? config?.workspacePath ?? process.cwd();
@@ -4704,35 +4734,146 @@ Jarvis agent is already running on ws://127.0.0.1:${port}`);
4704
4734
  if (!anthropicApiKey && !config?.useSubscription) {
4705
4735
  console.warn("Warning: No Anthropic API key set. Using subscription mode.");
4706
4736
  }
4707
- await startAgent({
4708
- port,
4709
- workspacePath,
4710
- anthropicApiKey,
4711
- useSubscription: !anthropicApiKey,
4712
- userId,
4713
- upstream: opts.upstream && config?.apiUrl && config?.token ? { apiUrl: config.apiUrl, token: config.token } : void 0
4737
+ if (opts.foreground) {
4738
+ await startAgent({
4739
+ port,
4740
+ workspacePath,
4741
+ anthropicApiKey,
4742
+ useSubscription: !anthropicApiKey,
4743
+ userId,
4744
+ upstream: opts.upstream && config?.apiUrl && config?.token ? { apiUrl: config.apiUrl, token: config.token } : void 0
4745
+ });
4746
+ return;
4747
+ }
4748
+ fs7.mkdirSync(LOG_DIR, { recursive: true });
4749
+ const logFd = fs7.openSync(LOG_FILE, "a");
4750
+ const args = [
4751
+ "start",
4752
+ "--foreground",
4753
+ "--port",
4754
+ String(port),
4755
+ "--workspace",
4756
+ workspacePath
4757
+ ];
4758
+ if (!opts.upstream) {
4759
+ args.push("--no-upstream");
4760
+ }
4761
+ if (anthropicApiKey) {
4762
+ args.push("--api-key", anthropicApiKey);
4763
+ }
4764
+ const binPath = process.argv[1];
4765
+ const child = spawn2(process.execPath, [binPath, ...args], {
4766
+ detached: true,
4767
+ stdio: ["ignore", logFd, logFd],
4768
+ cwd: workspacePath,
4769
+ env: { ...process.env, NODE_NO_WARNINGS: "1" }
4714
4770
  });
4771
+ child.unref();
4772
+ fs7.closeSync(logFd);
4773
+ savePid(child.pid);
4774
+ console.log(`Jarvis agent started (PID: ${child.pid})`);
4775
+ console.log(` Local: ws://127.0.0.1:${port}`);
4776
+ console.log(` Workspace: ${workspacePath}`);
4777
+ console.log(` Logs: ${LOG_FILE}`);
4778
+ if (config?.apiUrl) {
4779
+ console.log(` Cloud: ${config.apiUrl}`);
4780
+ }
4781
+ console.log();
4782
+ console.log(`Commands:`);
4783
+ console.log(` jarvis logs \u2014 View agent logs`);
4784
+ console.log(` jarvis stop \u2014 Stop the agent`);
4785
+ console.log(` jarvis restart \u2014 Restart the agent`);
4786
+ });
4787
+ program.command("stop").description("Stop the running agent").action(async () => {
4788
+ const pid = readPid();
4789
+ if (pid) {
4790
+ try {
4791
+ process.kill(pid, "SIGTERM");
4792
+ clearPid();
4793
+ console.log(`Agent stopped (PID: ${pid})`);
4794
+ } catch {
4795
+ clearPid();
4796
+ console.log("Agent process not found. Cleaned up PID file.");
4797
+ }
4798
+ return;
4799
+ }
4800
+ const running = await isPortInUse(7862);
4801
+ if (running) {
4802
+ try {
4803
+ const output = execSync("lsof -ti :7862", { encoding: "utf-8" }).trim();
4804
+ if (output) {
4805
+ const pids = output.split("\n");
4806
+ for (const p of pids) {
4807
+ try {
4808
+ process.kill(parseInt(p, 10), "SIGTERM");
4809
+ } catch {
4810
+ }
4811
+ }
4812
+ console.log("Agent stopped.");
4813
+ return;
4814
+ }
4815
+ } catch {
4816
+ }
4817
+ }
4818
+ console.log("No agent is running.");
4819
+ });
4820
+ program.command("restart").description("Restart the agent").action(async () => {
4821
+ const pid = readPid();
4822
+ if (pid) {
4823
+ try {
4824
+ process.kill(pid, "SIGTERM");
4825
+ } catch {
4826
+ }
4827
+ clearPid();
4828
+ console.log(`Stopped agent (PID: ${pid})`);
4829
+ await new Promise((r) => setTimeout(r, 1e3));
4830
+ }
4831
+ console.log("Starting agent...");
4832
+ await program.parseAsync(["node", "jarvis", "start"]);
4833
+ });
4834
+ program.command("logs").description("View agent logs").option("-f, --follow", "Follow log output (like tail -f)").option("-n, --lines <n>", "Number of lines to show", "50").action((opts) => {
4835
+ if (!fs7.existsSync(LOG_FILE)) {
4836
+ console.log("No log file found. Start the agent first: jarvis start");
4837
+ return;
4838
+ }
4839
+ if (opts.follow) {
4840
+ const tail = spawn2("tail", ["-f", "-n", opts.lines, LOG_FILE], {
4841
+ stdio: "inherit"
4842
+ });
4843
+ process.on("SIGINT", () => {
4844
+ tail.kill();
4845
+ process.exit(0);
4846
+ });
4847
+ tail.on("exit", () => process.exit(0));
4848
+ } else {
4849
+ const content = execSync(`tail -n ${opts.lines} "${LOG_FILE}"`, { encoding: "utf-8" });
4850
+ process.stdout.write(content);
4851
+ }
4715
4852
  });
4716
4853
  program.command("chat").description("Interactive TUI \u2014 chat with Jarvis in your terminal").option("--model <id>", "Model (e.g., claude-opus-4-20250514)").option("--mode <mode>", "Permission mode: supervised|auto|plan|yolo").option("--thinking <config>", "Thinking: adaptive|enabled|disabled").option("--thinking-budget <n>", "Token budget when thinking=enabled").option("--max-turns <n>", "Max turns per task").option("-p, --port <port>", "Agent server port", "7862").option("-w, --workspace <path>", "Workspace root path").option("--no-server", "Connect to existing server, don't auto-start").action(async (opts) => {
4717
4854
  await launchChat(opts);
4718
4855
  });
4719
- program.command("status").description("Show agent configuration and connection status").action(() => {
4856
+ program.command("status").description("Show agent configuration and connection status").action(async () => {
4720
4857
  const config = loadConfig();
4721
- if (!config) {
4722
- console.log("No configuration found.");
4723
- console.log(`Run 'jarvis connect <token>' to set up.`);
4724
- return;
4725
- }
4726
- console.log("Jarvis Agent Config:");
4858
+ const pid = readPid();
4859
+ const port = config?.port ?? 7862;
4860
+ const running = await isPortInUse(port);
4861
+ console.log("Jarvis Agent:");
4862
+ console.log(` Status: ${running ? `\x1B[32mrunning\x1B[0m` : `\x1B[31mstopped\x1B[0m`}${pid ? ` (PID: ${pid})` : ""}`);
4863
+ console.log(` Port: ${port}`);
4727
4864
  console.log(` Config: ${getConfigPath()}`);
4728
- console.log(` User: ${config.userId}`);
4729
- console.log(` Env: ${config.envId ?? "local"}`);
4730
- console.log(` Workspace: ${config.workspacePath ?? "(cwd)"}`);
4731
- console.log(` Port: ${config.port ?? 7862}`);
4732
- console.log(` Cloud: ${config.apiUrl ?? "(not connected)"}`);
4733
- if (config.connectedAt) {
4734
- console.log(` Connected: ${config.connectedAt}`);
4865
+ if (config) {
4866
+ console.log(` User: ${config.userId}`);
4867
+ console.log(` Env: ${config.envId ?? "local"}`);
4868
+ console.log(` Workspace: ${config.workspacePath ?? "(cwd)"}`);
4869
+ console.log(` Cloud: ${config.apiUrl ?? "(not connected)"}`);
4870
+ if (config.connectedAt) {
4871
+ console.log(` Connected: ${config.connectedAt}`);
4872
+ }
4873
+ } else {
4874
+ console.log(` Config: Not set up. Run 'jarvis connect <token>'`);
4735
4875
  }
4876
+ console.log(` Logs: ${LOG_FILE}`);
4736
4877
  });
4737
4878
  program.command("logout").description("Clear saved configuration").action(() => {
4738
4879
  clearConfig();
@@ -4744,10 +4885,10 @@ Jarvis agent is already running on ws://127.0.0.1:${port}`);
4744
4885
  // src/bin.ts
4745
4886
  function findEnv() {
4746
4887
  let dir = process.cwd();
4747
- while (dir !== path7.dirname(dir)) {
4748
- const envPath = path7.join(dir, ".env");
4749
- if (fs7.existsSync(envPath)) return envPath;
4750
- dir = path7.dirname(dir);
4888
+ while (dir !== path8.dirname(dir)) {
4889
+ const envPath = path8.join(dir, ".env");
4890
+ if (fs8.existsSync(envPath)) return envPath;
4891
+ dir = path8.dirname(dir);
4751
4892
  }
4752
4893
  return void 0;
4753
4894
  }