anywhere-ai 0.0.28 → 0.0.30

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.
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ export {
10
+ __require
11
+ };
package/dist/cli.js CHANGED
@@ -2,47 +2,168 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import fs from "fs/promises";
5
- import { readFileSync } from "fs";
5
+ import { readFileSync, existsSync, writeFileSync, unlinkSync, openSync } from "fs";
6
6
  import os from "os";
7
7
  import path from "path";
8
8
  import crypto from "crypto";
9
9
  import { spawn, spawnSync, execSync } from "child_process";
10
10
  import readline from "readline";
11
11
  import qrcode from "qrcode";
12
+ var ANYWHERE_DIR = path.join(os.homedir(), ".anywhere");
13
+ var CONFIG_PATH = path.join(ANYWHERE_DIR, "config.json");
14
+ var PID_PATH = path.join(ANYWHERE_DIR, "server.pid");
15
+ var LOG_PATH = path.join(ANYWHERE_DIR, "server.log");
16
+ var STATUS_PATH = path.join(ANYWHERE_DIR, "status.json");
12
17
  var args = process.argv.slice(2);
13
18
  var command = args.find((a) => !a.startsWith("-"));
14
19
  if (args.includes("--version") || args.includes("-v")) {
15
- console.log(`anywhere-ai v${"0.0.28"}`);
20
+ console.log(`anywhere-ai v${"0.0.30"}`);
16
21
  process.exit(0);
17
22
  }
18
23
  if (args.includes("--help") || args.includes("-h") || command === "help") {
19
- console.log(`anywhere-ai v${"0.0.28"} \u2014 Mobile coding agent
24
+ console.log(`anywhere-ai v${"0.0.30"} \u2014 Mobile coding agent
20
25
 
21
26
  Usage: anywhere-ai [command] [options]
22
27
 
23
28
  Commands:
29
+ status Show server status and connection info
30
+ stop Stop the background server
31
+ logs Tail server logs
24
32
  regenerate-token Generate a new auth token
25
33
 
26
34
  Options:
27
35
  --help, -h Show this help message
28
36
  --version, -v Show version
37
+ --daemon, -d Run server in background
29
38
  --no-tunnel Skip Cloudflare tunnel`);
30
39
  process.exit(0);
31
40
  }
41
+ function getDaemonPid() {
42
+ try {
43
+ const pid = parseInt(readFileSync(PID_PATH, "utf-8").trim(), 10);
44
+ process.kill(pid, 0);
45
+ return pid;
46
+ } catch {
47
+ try {
48
+ unlinkSync(PID_PATH);
49
+ } catch {
50
+ }
51
+ return null;
52
+ }
53
+ }
54
+ if (command === "stop" || args.includes("--stop")) {
55
+ const pid = getDaemonPid();
56
+ if (!pid) {
57
+ console.log("No server running.");
58
+ process.exit(0);
59
+ }
60
+ process.kill(pid, "SIGTERM");
61
+ try {
62
+ unlinkSync(PID_PATH);
63
+ } catch {
64
+ }
65
+ try {
66
+ unlinkSync(STATUS_PATH);
67
+ } catch {
68
+ }
69
+ console.log(`Server stopped (PID ${pid}).`);
70
+ process.exit(0);
71
+ }
72
+ if (command === "status") {
73
+ const pid = getDaemonPid();
74
+ if (!pid) {
75
+ console.log("No server running.");
76
+ process.exit(0);
77
+ }
78
+ try {
79
+ const status = JSON.parse(readFileSync(STATUS_PATH, "utf-8"));
80
+ const uptimeMs = Date.now() - status.startedAt;
81
+ const hours = Math.floor(uptimeMs / 36e5);
82
+ const mins = Math.floor(uptimeMs % 36e5 / 6e4);
83
+ console.log(`Anywhere server running (PID ${pid})`);
84
+ console.log(` Tunnel: ${status.serverURL}`);
85
+ if (status.localURL && status.localURL !== status.serverURL) {
86
+ console.log(` Direct: ${status.localURL}`);
87
+ }
88
+ console.log(` Token: ${status.authToken}`);
89
+ console.log(` Uptime: ${hours}h ${mins}m`);
90
+ console.log(` Logs: ${LOG_PATH}`);
91
+ } catch {
92
+ console.log(`Server running (PID ${pid}) but no status info available.`);
93
+ console.log(` Logs: ${LOG_PATH}`);
94
+ }
95
+ process.exit(0);
96
+ }
97
+ if (command === "logs") {
98
+ if (!existsSync(LOG_PATH)) {
99
+ console.log("No log file found. Start the server first.");
100
+ process.exit(0);
101
+ }
102
+ const tail = spawn("tail", ["-f", "-n", "50", LOG_PATH], { stdio: "inherit" });
103
+ tail.on("exit", () => process.exit(0));
104
+ process.on("SIGINT", () => {
105
+ tail.kill();
106
+ process.exit(0);
107
+ });
108
+ await new Promise(() => {
109
+ });
110
+ }
32
111
  if (command === "regenerate-token") {
33
- const configPath = path.join(os.homedir(), ".anywhere", "config.json");
34
112
  try {
35
- await fs.access(configPath);
113
+ await fs.access(CONFIG_PATH);
36
114
  } catch {
37
115
  console.error("No config found. Run `anywhere-ai` first to set up.");
38
116
  process.exit(1);
39
117
  }
40
- const config2 = JSON.parse(await fs.readFile(configPath, "utf-8"));
118
+ const config2 = JSON.parse(await fs.readFile(CONFIG_PATH, "utf-8"));
41
119
  config2.authToken = crypto.randomBytes(16).toString("hex");
42
- await fs.writeFile(configPath, JSON.stringify(config2, null, 2));
120
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(config2, null, 2));
43
121
  console.log("New auth token: " + config2.authToken);
44
122
  process.exit(0);
45
123
  }
124
+ var isDaemon = args.includes("--daemon") || args.includes("-d");
125
+ if (isDaemon && !args.includes("--_daemonized")) {
126
+ await fs.mkdir(ANYWHERE_DIR, { recursive: true });
127
+ const existingPid2 = getDaemonPid();
128
+ if (existingPid2) {
129
+ console.log(`Server already running (PID ${existingPid2}). Use 'anywhere-ai stop' first.`);
130
+ process.exit(1);
131
+ }
132
+ const logFd = openSync(LOG_PATH, "a");
133
+ const daemonArgs = args.filter((a) => a !== "--daemon" && a !== "-d");
134
+ daemonArgs.push("--_daemonized");
135
+ const child = spawn(process.execPath, [new URL(import.meta.url).pathname, ...daemonArgs], {
136
+ detached: true,
137
+ stdio: ["ignore", logFd, logFd],
138
+ env: { ...process.env }
139
+ });
140
+ child.unref();
141
+ writeFileSync(PID_PATH, String(child.pid));
142
+ console.log(`Starting server in background (PID ${child.pid})...`);
143
+ console.log(` Logs: ${LOG_PATH}`);
144
+ console.log(` Status: anywhere-ai status`);
145
+ console.log(` Stop: anywhere-ai stop`);
146
+ process.exit(0);
147
+ }
148
+ var existingPid = getDaemonPid();
149
+ if (existingPid && !args.includes("--_daemonized")) {
150
+ console.log(`Server already running (PID ${existingPid}). Use 'anywhere-ai stop' first.`);
151
+ process.exit(1);
152
+ }
153
+ async function checkForUpdate() {
154
+ try {
155
+ const res = await fetch("https://registry.npmjs.org/anywhere-ai/latest", { signal: AbortSignal.timeout(3e3) });
156
+ const data = await res.json();
157
+ if (data.version && data.version !== "0.0.30") {
158
+ console.log(`
159
+ Update available: v${"0.0.30"} \u2192 v${data.version}`);
160
+ console.log(` Run: npx anywhere-ai@latest
161
+ `);
162
+ }
163
+ } catch {
164
+ }
165
+ }
166
+ checkForUpdate();
46
167
  function ask(question, preserveCase = false) {
47
168
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
48
169
  return new Promise((resolve) => {
@@ -52,8 +173,6 @@ function ask(question, preserveCase = false) {
52
173
  });
53
174
  });
54
175
  }
55
- var ANYWHERE_DIR = path.join(os.homedir(), ".anywhere");
56
- var CONFIG_PATH = path.join(ANYWHERE_DIR, "config.json");
57
176
  async function createConfigFile() {
58
177
  try {
59
178
  await fs.access(CONFIG_PATH);
@@ -319,7 +438,18 @@ if (!args.includes("--no-tunnel")) {
319
438
  }
320
439
  var localURL = isVPS ? "http://" + publicIP + ":" + port : "http://" + (localIP || "localhost") + ":" + port;
321
440
  var serverURL = tunnelURL || localURL;
322
- await printBanner(serverURL, localURL);
441
+ var isDaemonized = args.includes("--_daemonized");
442
+ if (!isDaemonized) {
443
+ await printBanner(serverURL, localURL);
444
+ }
445
+ writeFileSync(PID_PATH, String(process.pid));
446
+ writeFileSync(STATUS_PATH, JSON.stringify({
447
+ serverURL,
448
+ localURL,
449
+ authToken: config.authToken,
450
+ startedAt: Date.now(),
451
+ pid: process.pid
452
+ }, null, 2));
323
453
  function cleanup() {
324
454
  intentionalShutdown = true;
325
455
  if (serverProcess) {
@@ -330,6 +460,14 @@ function cleanup() {
330
460
  tunnelProcess.kill();
331
461
  tunnelProcess = null;
332
462
  }
463
+ try {
464
+ unlinkSync(PID_PATH);
465
+ } catch {
466
+ }
467
+ try {
468
+ unlinkSync(STATUS_PATH);
469
+ } catch {
470
+ }
333
471
  process.exit();
334
472
  }
335
473
  process.on("SIGINT", cleanup);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anywhere-ai",
3
- "version": "0.0.28",
3
+ "version": "0.0.30",
4
4
  "type": "module",
5
5
  "description": "Code on any repo from your phone",
6
6
  "bin": {