@sofer_agent/cli 0.7.0 → 0.7.2

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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { agentPaths } from "@sofer_agent/core";
3
3
  import { chatCommand } from "./chat.js";
4
- import { daemonStart, daemonStop } from "./daemon.js";
4
+ import { daemonLogs, daemonRestart, daemonRun, daemonStart, daemonStatus, daemonStop } from "./daemon.js";
5
5
  import { infoCommand } from "./info.js";
6
6
  import { initCommand } from "./init.js";
7
7
  import { telegramCommand } from "./telegram-start.js";
@@ -12,8 +12,12 @@ function printHelp() {
12
12
  sofer --resume <id> resume a previous session
13
13
  sofer init interactive setup — mint agent + provision memory
14
14
  sofer info print on-chain agent state
15
- sofer daemon start start persistent background agent
16
- sofer daemon stop stop the background agent
15
+ sofer daemon run start daemon in foreground
16
+ sofer daemon start start daemon in background (fork)
17
+ sofer daemon stop stop the daemon
18
+ sofer daemon restart stop + start
19
+ sofer daemon status show PID, uptime, socket
20
+ sofer daemon logs tail daemon logs
17
21
  sofer telegram start the Telegram bot
18
22
  sofer help show this help
19
23
 
@@ -42,12 +46,29 @@ async function main() {
42
46
  break;
43
47
  case "daemon": {
44
48
  const sub = args[1];
45
- if (sub === "start")
46
- await daemonStart();
47
- else if (sub === "stop")
48
- await daemonStop();
49
- else
50
- console.error("usage: sofer daemon <start|stop>");
49
+ switch (sub) {
50
+ case "run":
51
+ await daemonRun();
52
+ break;
53
+ case "start":
54
+ await daemonStart();
55
+ break;
56
+ case "stop":
57
+ await daemonStop();
58
+ break;
59
+ case "restart":
60
+ await daemonRestart();
61
+ break;
62
+ case "status":
63
+ await daemonStatus();
64
+ break;
65
+ case "logs":
66
+ await daemonLogs();
67
+ break;
68
+ default:
69
+ console.error("usage: sofer daemon <run|start|stop|restart|status|logs>");
70
+ break;
71
+ }
51
72
  break;
52
73
  }
53
74
  case "help":
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CACT;;;;;;;;;;;;6BAYyB,UAAU,CAAC,IAAI;gDACI,CAC7C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IAE9B,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,UAAU;YACb,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACnD,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,UAAU;YACb,MAAM,eAAe,EAAE,CAAC;YACxB,MAAM;QACR,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,GAAG,KAAK,OAAO;gBAAE,MAAM,WAAW,EAAE,CAAC;iBACpC,IAAI,GAAG,KAAK,MAAM;gBAAE,MAAM,UAAU,EAAE,CAAC;;gBACvC,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;YACvD,MAAM;QACR,CAAC;QACD,KAAK,MAAM,CAAC;QACZ,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,SAAS,EAAE,CAAC;YACZ,MAAM;QACR;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;YAC3C,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,IACE,GAAG,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QACpC,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QACjC,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAChC,CAAC;QACD,OAAO,CAAC,KAAK,CACX,qEAAqE,UAAU,CAAC,IAAI,KAAK,CAC1F,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
package/dist/daemon.d.ts CHANGED
@@ -1,3 +1,7 @@
1
- export declare function daemonStop(): Promise<void>;
1
+ export declare function daemonRun(): Promise<void>;
2
2
  export declare function daemonStart(): Promise<void>;
3
+ export declare function daemonStop(): Promise<void>;
4
+ export declare function daemonStatus(): Promise<void>;
5
+ export declare function daemonRestart(): Promise<void>;
6
+ export declare function daemonLogs(tail?: number, follow?: boolean): Promise<void>;
3
7
  //# sourceMappingURL=daemon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AA6BA,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAoIjD"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAwCA,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAM/C;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBjD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhD;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBlD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAInD;AAED,wBAAsB,UAAU,CAAC,IAAI,SAAK,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBzE"}
package/dist/daemon.js CHANGED
@@ -1,118 +1,223 @@
1
1
  import { createServer } from "node:http";
2
- import { readFileSync, writeFileSync, unlinkSync, existsSync } from "node:fs";
2
+ import { chmodSync, existsSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
+ import { spawn } from "node:child_process";
4
5
  import { agentPaths, decryptWithKey, loadConfig, readSecret, SoferAgent, } from "@sofer_agent/core";
5
6
  import { TelegramListener } from "@sofer_agent/plugin-telegram";
6
7
  import { loadSecrets } from "./env.js";
7
- const PID_FILE = join(agentPaths.root, "daemon.pid");
8
- function writePid(pid) {
9
- writeFileSync(PID_FILE, String(pid), "utf8");
10
- }
11
- function readPid() {
8
+ const LOCK_FILE = join(agentPaths.root, "daemon.lock");
9
+ const SOCK_PATH = join(agentPaths.root, "daemon.sock");
10
+ const LOG_FILE = join(agentPaths.root, "daemon.log");
11
+ function acquireLock() {
12
12
  try {
13
- return Number(readFileSync(PID_FILE, "utf8").trim());
13
+ if (existsSync(LOCK_FILE)) {
14
+ const pid = Number(readFileSync(LOCK_FILE, "utf8").trim());
15
+ try {
16
+ process.kill(pid, 0);
17
+ return false;
18
+ }
19
+ catch { /* stale */ }
20
+ }
21
+ writeFileSync(LOCK_FILE, String(process.pid), "utf8");
22
+ return true;
14
23
  }
15
24
  catch {
16
- return null;
25
+ return false;
26
+ }
27
+ }
28
+ function releaseLock() {
29
+ try {
30
+ if (existsSync(LOCK_FILE))
31
+ unlinkSync(LOCK_FILE);
32
+ }
33
+ catch { /* */ }
34
+ }
35
+ let state = "bootstrapping";
36
+ // ── Commands ─────────────────────────────────────────────────────────────
37
+ export async function daemonRun() {
38
+ if (!acquireLock()) {
39
+ console.error("Daemon already running. Use `sofer daemon stop` first.");
40
+ process.exit(1);
41
+ }
42
+ await runServer();
43
+ }
44
+ export async function daemonStart() {
45
+ if (!acquireLock()) {
46
+ console.error("Daemon already running. Use `sofer daemon stop` first.");
47
+ process.exit(1);
17
48
  }
49
+ releaseLock(); // let the child take the lock
50
+ const logFd = existsSync(LOG_FILE)
51
+ ? require("node:fs").openSync(LOG_FILE, "a")
52
+ : require("node:fs").openSync(LOG_FILE, "w");
53
+ const execPath = process.argv[1] ?? process.execPath;
54
+ const child = spawn(process.execPath, [...process.execArgv, execPath, "daemon", "run"], {
55
+ detached: true,
56
+ stdio: ["ignore", logFd, logFd],
57
+ env: { ...process.env },
58
+ });
59
+ child.unref();
60
+ console.log(`[daemon] started (PID ${child.pid})`);
61
+ console.log(` socket: ${SOCK_PATH}`);
62
+ console.log(` logs: ${LOG_FILE}`);
18
63
  }
19
64
  export async function daemonStop() {
20
- const pid = readPid();
21
- if (!pid) {
65
+ if (!existsSync(LOCK_FILE)) {
22
66
  console.log("No daemon running.");
23
67
  return;
24
68
  }
69
+ const pid = Number(readFileSync(LOCK_FILE, "utf8").trim());
25
70
  try {
26
71
  process.kill(pid, "SIGTERM");
27
- console.log(`Sent stop signal to PID ${pid}.`);
72
+ console.log(`Stopped daemon (PID ${pid}).`);
28
73
  }
29
74
  catch {
30
- console.log("Daemon not running (stale PID).");
31
- unlinkSync(PID_FILE);
75
+ console.log("Stale lock. Cleaning up.");
76
+ releaseLock();
32
77
  }
33
78
  }
34
- export async function daemonStart() {
79
+ export async function daemonStatus() {
80
+ if (!existsSync(LOCK_FILE)) {
81
+ console.log("Daemon not running.");
82
+ return;
83
+ }
84
+ const pid = Number(readFileSync(LOCK_FILE, "utf8").trim());
85
+ let alive = false;
86
+ try {
87
+ process.kill(pid, 0);
88
+ alive = true;
89
+ }
90
+ catch { /* dead */ }
91
+ if (!alive) {
92
+ console.log("Daemon not running (stale lock).");
93
+ return;
94
+ }
95
+ let uptime = "unknown";
96
+ try {
97
+ const stat = statSync(LOCK_FILE);
98
+ const sec = Math.floor((Date.now() - stat.mtimeMs) / 1000);
99
+ uptime = sec < 60 ? `${sec}s` : sec < 3600 ? `${Math.floor(sec / 60)}m` : `${Math.floor(sec / 3600)}h`;
100
+ }
101
+ catch { /* */ }
102
+ console.log(` PID: ${pid}`);
103
+ console.log(` Status: running`);
104
+ console.log(` Uptime: ${uptime}`);
105
+ console.log(` Socket: ${SOCK_PATH}`);
106
+ console.log(` Lock: ${LOCK_FILE}`);
107
+ console.log(` Logs: ${LOG_FILE}`);
108
+ }
109
+ export async function daemonRestart() {
110
+ await daemonStop();
111
+ await new Promise((r) => setTimeout(r, 500));
112
+ await daemonStart();
113
+ }
114
+ export async function daemonLogs(tail = 50, follow = false) {
115
+ if (!existsSync(LOG_FILE)) {
116
+ console.log("No daemon logs found.");
117
+ return;
118
+ }
119
+ const lines = readFileSync(LOG_FILE, "utf8").split("\n").filter(Boolean);
120
+ const recent = lines.slice(-tail);
121
+ console.log(recent.join("\n"));
122
+ if (follow) {
123
+ const { watchFile } = require("node:fs");
124
+ let count = lines.length;
125
+ watchFile(LOG_FILE, () => {
126
+ const all = readFileSync(LOG_FILE, "utf8").split("\n").filter(Boolean);
127
+ for (let i = count; i < all.length; i++) {
128
+ console.log(all[i]);
129
+ }
130
+ count = all.length;
131
+ });
132
+ // Keep alive
133
+ await new Promise(() => { });
134
+ }
135
+ }
136
+ // ── Server (shared by run + start) ───────────────────────────────────────
137
+ async function runServer() {
35
138
  const secrets = loadSecrets();
36
139
  if (!secrets.suiSecretKey || !secrets.anthropicApiKey) {
37
- console.error("Missing secrets. Run `sofer init` first.");
140
+ console.error("[daemon] missing secrets. Run `sofer init` first.");
38
141
  process.exit(1);
39
142
  }
143
+ console.log("[daemon] bootstrapping…");
40
144
  const config = loadConfig();
41
- const agent = await SoferAgent.create(config, {
42
- suiSecretKey: secrets.suiSecretKey,
43
- anthropicApiKey: secrets.anthropicApiKey,
44
- });
45
- // SSE clients
145
+ let agent;
146
+ try {
147
+ agent = await SoferAgent.create(config, {
148
+ suiSecretKey: secrets.suiSecretKey,
149
+ anthropicApiKey: secrets.anthropicApiKey,
150
+ });
151
+ }
152
+ catch (e) {
153
+ console.error("[daemon] failed:", e.message);
154
+ releaseLock();
155
+ process.exit(1);
156
+ }
46
157
  let sseId = 0;
47
158
  const sseClients = new Set();
48
159
  function broadcast(event, data) {
49
- for (const c of sseClients) {
50
- c.res.write(`event: ${event}\ndata: ${data}\n\n`);
51
- }
160
+ const payload = JSON.stringify(data);
161
+ for (const c of sseClients)
162
+ c.res.write(`event: ${event}\ndata: ${payload}\n\n`);
52
163
  }
53
- // HTTP server
54
164
  const server = createServer(async (req, res) => {
55
165
  res.setHeader("Access-Control-Allow-Origin", "*");
56
166
  if (req.method === "OPTIONS") {
57
167
  res.writeHead(204).end();
58
168
  return;
59
169
  }
60
- // GET /healthz
61
- if (req.url === "/healthz" || req.url === "/") {
62
- res.writeHead(200, { "content-type": "application/json" });
63
- res.end(JSON.stringify({ status: "ok", agent: agent.address.slice(0, 10), network: config.network }));
170
+ const url = req.url ?? "/";
171
+ if (url === "/healthz") {
172
+ res.writeHead(200).end(JSON.stringify({ status: state, agent: agent.address.slice(0, 10), network: config.network }));
64
173
  return;
65
174
  }
66
- // GET /events (SSE)
67
- if (req.url === "/events") {
68
- res.writeHead(200, {
69
- "content-type": "text/event-stream",
70
- "cache-control": "no-cache",
71
- "connection": "keep-alive",
72
- });
175
+ if (url === "/events") {
176
+ res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
73
177
  const client = { id: ++sseId, res };
74
178
  sseClients.add(client);
75
179
  res.write(`data: ${JSON.stringify({ event: "connected", agent: agent.address })}\n\n`);
76
180
  req.on("close", () => sseClients.delete(client));
77
181
  return;
78
182
  }
79
- // POST /chat
80
- if (req.method === "POST" && req.url === "/chat") {
183
+ if (req.method === "POST" && url === "/chat") {
81
184
  const body = await readBody(req);
82
185
  try {
83
186
  const { message } = JSON.parse(body);
84
187
  if (!message) {
85
- res.writeHead(400).end("missing message");
188
+ res.writeHead(400).end();
86
189
  return;
87
190
  }
88
- broadcast("thinking", JSON.stringify({ status: "thinking" }));
191
+ broadcast("thinking", { status: "thinking" });
89
192
  const { text } = await agent.chat(message);
90
- broadcast("response", JSON.stringify({ text }));
91
- broadcast("idle", JSON.stringify({ status: "idle" }));
92
- res.writeHead(200, { "content-type": "application/json" });
193
+ broadcast("response", { text });
194
+ broadcast("idle", { status: "idle" });
93
195
  res.end(JSON.stringify({ text }));
94
196
  }
95
197
  catch (e) {
96
- broadcast("error", JSON.stringify({ error: e.message }));
198
+ broadcast("error", { error: e.message });
199
+ res.writeHead(500).end(e.message);
200
+ }
201
+ return;
202
+ }
203
+ if (req.method === "POST" && url === "/sync") {
204
+ try {
205
+ const bal = await agent.getBalance();
206
+ broadcast("sync", { status: "done", balance: (Number(bal) / 1e9).toFixed(4) });
207
+ res.writeHead(200).end("synced");
208
+ }
209
+ catch (e) {
97
210
  res.writeHead(500).end(e.message);
98
211
  }
99
212
  return;
100
213
  }
101
- // GET /status
102
- if (req.url === "/status") {
103
- res.writeHead(200, { "content-type": "application/json" });
104
- res.end(JSON.stringify({
105
- agent: agent.address,
106
- name: agent.name,
107
- network: config.network,
108
- tokens: agent.usage.tokens,
109
- cost: agent.usage.costUsd.toFixed(6),
110
- }));
214
+ if (url === "/status") {
215
+ res.end(JSON.stringify({ state, agent: agent.address, name: agent.name, network: config.network, tokens: agent.usage.tokens, cost: agent.usage.costUsd.toFixed(6) }));
111
216
  return;
112
217
  }
113
- res.writeHead(404).end("not found");
218
+ res.writeHead(404).end();
114
219
  });
115
- // Telegram listener
220
+ // Telegram
116
221
  const tgPath = `${agentPaths.root}/telegram.enc`;
117
222
  const tgEnc = readSecret(tgPath);
118
223
  let tgListener = null;
@@ -121,39 +226,49 @@ export async function daemonStart() {
121
226
  const raw = decryptWithKey(secrets.suiSecretKey, tgEnc);
122
227
  if (raw) {
123
228
  const tgConfig = JSON.parse(raw);
124
- tgListener = new TelegramListener({
125
- botToken: tgConfig.botToken,
126
- allowedUserIds: tgConfig.allowedUserIds,
127
- agentName: agent.name.toLowerCase(),
128
- });
229
+ tgListener = new TelegramListener({ botToken: tgConfig.botToken, allowedUserIds: tgConfig.allowedUserIds, agentName: agent.name.toLowerCase() });
129
230
  tgListener.onDispatch(async (input) => {
130
- broadcast("tg-message", JSON.stringify({ from: input.displayName, text: input.text.slice(0, 100) }));
231
+ broadcast("tg-message", { from: input.displayName, text: input.text.slice(0, 200) });
131
232
  const { text } = await agent.chat(input.text);
132
- broadcast("tg-response", JSON.stringify({ text: text.slice(0, 100) }));
233
+ broadcast("tg-response", { text: text.slice(0, 200) });
133
234
  return text;
134
235
  });
135
236
  }
136
237
  }
137
- catch { /* skip */ }
238
+ catch { /* */ }
138
239
  }
139
240
  const PORT = Number(process.env.SOFER_DAEMON_PORT ?? 4242);
140
- server.listen(PORT, () => {
141
- writePid(process.pid);
142
- console.log(`[daemon] sofer running on http://localhost:${PORT}`);
143
- console.log(`[daemon] agent: ${agent.address.slice(0, 10)}… network: ${config.network}`);
144
- if (tgListener) {
145
- console.log("[daemon] telegram: starting bot…");
146
- tgListener.start().catch((e) => console.error("[daemon] telegram failed:", e.message));
147
- }
148
- });
149
- // Graceful shutdown
241
+ try {
242
+ if (existsSync(SOCK_PATH))
243
+ unlinkSync(SOCK_PATH);
244
+ server.listen(SOCK_PATH, () => {
245
+ chmodSync(SOCK_PATH, 0o600);
246
+ state = "ready";
247
+ console.log(`[daemon] ready ${SOCK_PATH}`);
248
+ console.log(`[daemon] agent: ${agent.address.slice(0, 10)}… ${config.network}`);
249
+ if (tgListener)
250
+ tgListener.start().catch((e) => console.error("[daemon] telegram:", e.message));
251
+ });
252
+ }
253
+ catch {
254
+ server.listen(PORT, () => {
255
+ state = "ready";
256
+ console.log(`[daemon] ready — tcp :${PORT}`);
257
+ console.log(`[daemon] agent: ${agent.address.slice(0, 10)}… ${config.network}`);
258
+ if (tgListener)
259
+ tgListener.start().catch((e) => console.error("[daemon] telegram:", e.message));
260
+ });
261
+ }
150
262
  const shutdown = async () => {
151
- console.log("\n[daemon] shutting down…");
263
+ state = "shutting_down";
264
+ console.log("[daemon] shutting down…");
152
265
  if (tgListener)
153
- await tgListener.stop();
266
+ await tgListener.stop().catch(() => { });
154
267
  server.close();
268
+ releaseLock();
155
269
  try {
156
- unlinkSync(PID_FILE);
270
+ if (existsSync(SOCK_PATH))
271
+ unlinkSync(SOCK_PATH);
157
272
  }
158
273
  catch { /* */ }
159
274
  process.exit(0);
@@ -164,7 +279,7 @@ export async function daemonStart() {
164
279
  function readBody(req) {
165
280
  return new Promise((resolve) => {
166
281
  let data = "";
167
- req.on("data", (chunk) => { data += chunk; });
282
+ req.on("data", (c) => { data += c; });
168
283
  req.on("end", () => resolve(data));
169
284
  });
170
285
  }
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofer_agent/cli",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sofer": "./bin/sofer"
package/src/bin.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { agentPaths } from "@sofer_agent/core";
3
3
  import { chatCommand } from "./chat.js";
4
- import { daemonStart, daemonStop } from "./daemon.js";
4
+ import { daemonLogs, daemonRestart, daemonRun, daemonStart, daemonStatus, daemonStop } from "./daemon.js";
5
5
  import { infoCommand } from "./info.js";
6
6
  import { initCommand } from "./init.js";
7
7
  import { telegramCommand } from "./telegram-start.js";
@@ -14,8 +14,12 @@ function printHelp(): void {
14
14
  sofer --resume <id> resume a previous session
15
15
  sofer init interactive setup — mint agent + provision memory
16
16
  sofer info print on-chain agent state
17
- sofer daemon start start persistent background agent
18
- sofer daemon stop stop the background agent
17
+ sofer daemon run start daemon in foreground
18
+ sofer daemon start start daemon in background (fork)
19
+ sofer daemon stop stop the daemon
20
+ sofer daemon restart stop + start
21
+ sofer daemon status show PID, uptime, socket
22
+ sofer daemon logs tail daemon logs
19
23
  sofer telegram start the Telegram bot
20
24
  sofer help show this help
21
25
 
@@ -47,9 +51,15 @@ async function main(): Promise<void> {
47
51
  break;
48
52
  case "daemon": {
49
53
  const sub = args[1];
50
- if (sub === "start") await daemonStart();
51
- else if (sub === "stop") await daemonStop();
52
- else console.error("usage: sofer daemon <start|stop>");
54
+ switch (sub) {
55
+ case "run": await daemonRun(); break;
56
+ case "start": await daemonStart(); break;
57
+ case "stop": await daemonStop(); break;
58
+ case "restart": await daemonRestart(); break;
59
+ case "status": await daemonStatus(); break;
60
+ case "logs": await daemonLogs(); break;
61
+ default: console.error("usage: sofer daemon <run|start|stop|restart|status|logs>"); break;
62
+ }
53
63
  break;
54
64
  }
55
65
  case "help":
package/src/daemon.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
2
- import { readFileSync, writeFileSync, unlinkSync, existsSync } from "node:fs";
2
+ import { chmodSync, existsSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
+ import { spawn } from "node:child_process";
4
5
  import {
5
- type SoferConfig,
6
6
  agentPaths,
7
7
  decryptWithKey,
8
8
  loadConfig,
@@ -12,116 +12,199 @@ import {
12
12
  import { TelegramListener } from "@sofer_agent/plugin-telegram";
13
13
  import { loadSecrets } from "./env.js";
14
14
 
15
- const PID_FILE = join(agentPaths.root, "daemon.pid");
15
+ const LOCK_FILE = join(agentPaths.root, "daemon.lock");
16
+ const SOCK_PATH = join(agentPaths.root, "daemon.sock");
17
+ const LOG_FILE = join(agentPaths.root, "daemon.log");
16
18
 
17
- interface SSEClient {
18
- id: number;
19
- res: ServerResponse;
19
+ interface SSEClient { id: number; res: ServerResponse; }
20
+
21
+ function acquireLock(): boolean {
22
+ try {
23
+ if (existsSync(LOCK_FILE)) {
24
+ const pid = Number(readFileSync(LOCK_FILE, "utf8").trim());
25
+ try { process.kill(pid, 0); return false; } catch { /* stale */ }
26
+ }
27
+ writeFileSync(LOCK_FILE, String(process.pid), "utf8");
28
+ return true;
29
+ } catch { return false; }
20
30
  }
21
31
 
22
- function writePid(pid: number): void {
23
- writeFileSync(PID_FILE, String(pid), "utf8");
32
+ function releaseLock(): void {
33
+ try { if (existsSync(LOCK_FILE)) unlinkSync(LOCK_FILE); } catch { /* */ }
24
34
  }
25
35
 
26
- function readPid(): number | null {
27
- try { return Number(readFileSync(PID_FILE, "utf8").trim()); } catch { return null; }
36
+ type DaemonState = "bootstrapping" | "ready" | "shutting_down";
37
+ let state: DaemonState = "bootstrapping";
38
+
39
+ // ── Commands ─────────────────────────────────────────────────────────────
40
+
41
+ export async function daemonRun(): Promise<void> {
42
+ if (!acquireLock()) {
43
+ console.error("Daemon already running. Use `sofer daemon stop` first.");
44
+ process.exit(1);
45
+ }
46
+ await runServer();
47
+ }
48
+
49
+ export async function daemonStart(): Promise<void> {
50
+ if (!acquireLock()) {
51
+ console.error("Daemon already running. Use `sofer daemon stop` first.");
52
+ process.exit(1);
53
+ }
54
+ releaseLock(); // let the child take the lock
55
+
56
+ const logFd = existsSync(LOG_FILE)
57
+ ? require("node:fs").openSync(LOG_FILE, "a")
58
+ : require("node:fs").openSync(LOG_FILE, "w");
59
+
60
+ const execPath = process.argv[1] ?? process.execPath;
61
+ const child = spawn(process.execPath, [...process.execArgv, execPath, "daemon", "run"], {
62
+ detached: true,
63
+ stdio: ["ignore", logFd, logFd] as const,
64
+ env: { ...process.env },
65
+ });
66
+ child.unref();
67
+ console.log(`[daemon] started (PID ${child.pid})`);
68
+ console.log(` socket: ${SOCK_PATH}`);
69
+ console.log(` logs: ${LOG_FILE}`);
28
70
  }
29
71
 
30
72
  export async function daemonStop(): Promise<void> {
31
- const pid = readPid();
32
- if (!pid) { console.log("No daemon running."); return; }
33
- try { process.kill(pid, "SIGTERM"); console.log(`Sent stop signal to PID ${pid}.`); }
34
- catch { console.log("Daemon not running (stale PID)."); unlinkSync(PID_FILE); }
73
+ if (!existsSync(LOCK_FILE)) { console.log("No daemon running."); return; }
74
+ const pid = Number(readFileSync(LOCK_FILE, "utf8").trim());
75
+ try { process.kill(pid, "SIGTERM"); console.log(`Stopped daemon (PID ${pid}).`); }
76
+ catch { console.log("Stale lock. Cleaning up."); releaseLock(); }
35
77
  }
36
78
 
37
- export async function daemonStart(): Promise<void> {
79
+ export async function daemonStatus(): Promise<void> {
80
+ if (!existsSync(LOCK_FILE)) { console.log("Daemon not running."); return; }
81
+ const pid = Number(readFileSync(LOCK_FILE, "utf8").trim());
82
+ let alive = false;
83
+ try { process.kill(pid, 0); alive = true; } catch { /* dead */ }
84
+ if (!alive) { console.log("Daemon not running (stale lock)."); return; }
85
+
86
+ let uptime = "unknown";
87
+ try {
88
+ const stat = statSync(LOCK_FILE);
89
+ const sec = Math.floor((Date.now() - stat.mtimeMs) / 1000);
90
+ uptime = sec < 60 ? `${sec}s` : sec < 3600 ? `${Math.floor(sec / 60)}m` : `${Math.floor(sec / 3600)}h`;
91
+ } catch { /* */ }
92
+
93
+ console.log(` PID: ${pid}`);
94
+ console.log(` Status: running`);
95
+ console.log(` Uptime: ${uptime}`);
96
+ console.log(` Socket: ${SOCK_PATH}`);
97
+ console.log(` Lock: ${LOCK_FILE}`);
98
+ console.log(` Logs: ${LOG_FILE}`);
99
+ }
100
+
101
+ export async function daemonRestart(): Promise<void> {
102
+ await daemonStop();
103
+ await new Promise((r) => setTimeout(r, 500));
104
+ await daemonStart();
105
+ }
106
+
107
+ export async function daemonLogs(tail = 50, follow = false): Promise<void> {
108
+ if (!existsSync(LOG_FILE)) { console.log("No daemon logs found."); return; }
109
+ const lines = readFileSync(LOG_FILE, "utf8").split("\n").filter(Boolean);
110
+ const recent = lines.slice(-tail);
111
+ console.log(recent.join("\n"));
112
+
113
+ if (follow) {
114
+ const { watchFile } = require("node:fs") as typeof import("node:fs");
115
+ let count = lines.length;
116
+ watchFile(LOG_FILE, () => {
117
+ const all = readFileSync(LOG_FILE, "utf8").split("\n").filter(Boolean);
118
+ for (let i = count; i < all.length; i++) {
119
+ console.log(all[i]);
120
+ }
121
+ count = all.length;
122
+ });
123
+ // Keep alive
124
+ await new Promise(() => {});
125
+ }
126
+ }
127
+
128
+ // ── Server (shared by run + start) ───────────────────────────────────────
129
+
130
+ async function runServer(): Promise<void> {
38
131
  const secrets = loadSecrets();
39
132
  if (!secrets.suiSecretKey || !secrets.anthropicApiKey) {
40
- console.error("Missing secrets. Run `sofer init` first.");
133
+ console.error("[daemon] missing secrets. Run `sofer init` first.");
41
134
  process.exit(1);
42
135
  }
43
136
 
137
+ console.log("[daemon] bootstrapping…");
44
138
  const config = loadConfig();
45
- const agent = await SoferAgent.create(config, {
46
- suiSecretKey: secrets.suiSecretKey,
47
- anthropicApiKey: secrets.anthropicApiKey,
48
- });
139
+ let agent: SoferAgent;
140
+ try {
141
+ agent = await SoferAgent.create(config, {
142
+ suiSecretKey: secrets.suiSecretKey,
143
+ anthropicApiKey: secrets.anthropicApiKey,
144
+ });
145
+ } catch (e) {
146
+ console.error("[daemon] failed:", (e as Error).message);
147
+ releaseLock();
148
+ process.exit(1);
149
+ }
49
150
 
50
- // SSE clients
51
151
  let sseId = 0;
52
152
  const sseClients = new Set<SSEClient>();
53
153
 
54
- function broadcast(event: string, data: string): void {
55
- for (const c of sseClients) {
56
- c.res.write(`event: ${event}\ndata: ${data}\n\n`);
57
- }
154
+ function broadcast(event: string, data: Record<string, unknown>): void {
155
+ const payload = JSON.stringify(data);
156
+ for (const c of sseClients) c.res.write(`event: ${event}\ndata: ${payload}\n\n`);
58
157
  }
59
158
 
60
- // HTTP server
61
- const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
159
+ const server = createServer(async (req, res) => {
62
160
  res.setHeader("Access-Control-Allow-Origin", "*");
63
-
64
161
  if (req.method === "OPTIONS") { res.writeHead(204).end(); return; }
162
+ const url = req.url ?? "/";
65
163
 
66
- // GET /healthz
67
- if (req.url === "/healthz" || req.url === "/") {
68
- res.writeHead(200, { "content-type": "application/json" });
69
- res.end(JSON.stringify({ status: "ok", agent: agent.address.slice(0, 10), network: config.network }));
164
+ if (url === "/healthz") {
165
+ res.writeHead(200).end(JSON.stringify({ status: state, agent: agent.address.slice(0, 10), network: config.network }));
70
166
  return;
71
167
  }
72
-
73
- // GET /events (SSE)
74
- if (req.url === "/events") {
75
- res.writeHead(200, {
76
- "content-type": "text/event-stream",
77
- "cache-control": "no-cache",
78
- "connection": "keep-alive",
79
- });
168
+ if (url === "/events") {
169
+ res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
80
170
  const client: SSEClient = { id: ++sseId, res };
81
171
  sseClients.add(client);
82
172
  res.write(`data: ${JSON.stringify({ event: "connected", agent: agent.address })}\n\n`);
83
173
  req.on("close", () => sseClients.delete(client));
84
174
  return;
85
175
  }
86
-
87
- // POST /chat
88
- if (req.method === "POST" && req.url === "/chat") {
176
+ if (req.method === "POST" && url === "/chat") {
89
177
  const body = await readBody(req);
90
178
  try {
91
179
  const { message } = JSON.parse(body);
92
- if (!message) { res.writeHead(400).end("missing message"); return; }
93
-
94
- broadcast("thinking", JSON.stringify({ status: "thinking" }));
180
+ if (!message) { res.writeHead(400).end(); return; }
181
+ broadcast("thinking", { status: "thinking" });
95
182
  const { text } = await agent.chat(message);
96
- broadcast("response", JSON.stringify({ text }));
97
- broadcast("idle", JSON.stringify({ status: "idle" }));
98
-
99
- res.writeHead(200, { "content-type": "application/json" });
183
+ broadcast("response", { text });
184
+ broadcast("idle", { status: "idle" });
100
185
  res.end(JSON.stringify({ text }));
101
186
  } catch (e) {
102
- broadcast("error", JSON.stringify({ error: (e as Error).message }));
187
+ broadcast("error", { error: (e as Error).message });
103
188
  res.writeHead(500).end((e as Error).message);
104
189
  }
105
190
  return;
106
191
  }
107
-
108
- // GET /status
109
- if (req.url === "/status") {
110
- res.writeHead(200, { "content-type": "application/json" });
111
- res.end(JSON.stringify({
112
- agent: agent.address,
113
- name: agent.name,
114
- network: config.network,
115
- tokens: agent.usage.tokens,
116
- cost: agent.usage.costUsd.toFixed(6),
117
- }));
192
+ if (req.method === "POST" && url === "/sync") {
193
+ try {
194
+ const bal = await agent.getBalance();
195
+ broadcast("sync", { status: "done", balance: (Number(bal) / 1e9).toFixed(4) });
196
+ res.writeHead(200).end("synced");
197
+ } catch (e) { res.writeHead(500).end((e as Error).message); }
118
198
  return;
119
199
  }
120
-
121
- res.writeHead(404).end("not found");
200
+ if (url === "/status") {
201
+ res.end(JSON.stringify({ state, agent: agent.address, name: agent.name, network: config.network, tokens: agent.usage.tokens, cost: agent.usage.costUsd.toFixed(6) }));
202
+ return;
203
+ }
204
+ res.writeHead(404).end();
122
205
  });
123
206
 
124
- // Telegram listener
207
+ // Telegram
125
208
  const tgPath = `${agentPaths.root}/telegram.enc`;
126
209
  const tgEnc = readSecret(tgPath);
127
210
  let tgListener: TelegramListener | null = null;
@@ -130,38 +213,43 @@ export async function daemonStart(): Promise<void> {
130
213
  const raw = decryptWithKey(secrets.suiSecretKey, tgEnc);
131
214
  if (raw) {
132
215
  const tgConfig = JSON.parse(raw) as { botToken: string; allowedUserIds: number[] };
133
- tgListener = new TelegramListener({
134
- botToken: tgConfig.botToken,
135
- allowedUserIds: tgConfig.allowedUserIds,
136
- agentName: agent.name.toLowerCase(),
137
- });
216
+ tgListener = new TelegramListener({ botToken: tgConfig.botToken, allowedUserIds: tgConfig.allowedUserIds, agentName: agent.name.toLowerCase() });
138
217
  tgListener.onDispatch(async (input) => {
139
- broadcast("tg-message", JSON.stringify({ from: input.displayName, text: input.text.slice(0, 100) }));
218
+ broadcast("tg-message", { from: input.displayName, text: input.text.slice(0, 200) });
140
219
  const { text } = await agent.chat(input.text);
141
- broadcast("tg-response", JSON.stringify({ text: text.slice(0, 100) }));
220
+ broadcast("tg-response", { text: text.slice(0, 200) });
142
221
  return text;
143
222
  });
144
223
  }
145
- } catch { /* skip */ }
224
+ } catch { /* */ }
146
225
  }
147
226
 
148
227
  const PORT = Number(process.env.SOFER_DAEMON_PORT ?? 4242);
149
- server.listen(PORT, () => {
150
- writePid(process.pid);
151
- console.log(`[daemon] sofer running on http://localhost:${PORT}`);
152
- console.log(`[daemon] agent: ${agent.address.slice(0, 10)}… network: ${config.network}`);
153
- if (tgListener) {
154
- console.log("[daemon] telegram: starting bot…");
155
- tgListener.start().catch((e) => console.error("[daemon] telegram failed:", e.message));
156
- }
157
- });
228
+ try {
229
+ if (existsSync(SOCK_PATH)) unlinkSync(SOCK_PATH);
230
+ server.listen(SOCK_PATH, () => {
231
+ chmodSync(SOCK_PATH, 0o600);
232
+ state = "ready";
233
+ console.log(`[daemon] ready ${SOCK_PATH}`);
234
+ console.log(`[daemon] agent: ${agent.address.slice(0, 10)}… ${config.network}`);
235
+ if (tgListener) tgListener.start().catch((e) => console.error("[daemon] telegram:", e.message));
236
+ });
237
+ } catch {
238
+ server.listen(PORT, () => {
239
+ state = "ready";
240
+ console.log(`[daemon] ready — tcp :${PORT}`);
241
+ console.log(`[daemon] agent: ${agent.address.slice(0, 10)}… ${config.network}`);
242
+ if (tgListener) tgListener.start().catch((e) => console.error("[daemon] telegram:", e.message));
243
+ });
244
+ }
158
245
 
159
- // Graceful shutdown
160
246
  const shutdown = async () => {
161
- console.log("\n[daemon] shutting down…");
162
- if (tgListener) await tgListener.stop();
247
+ state = "shutting_down";
248
+ console.log("[daemon] shutting down…");
249
+ if (tgListener) await tgListener.stop().catch(() => {});
163
250
  server.close();
164
- try { unlinkSync(PID_FILE); } catch { /* */ }
251
+ releaseLock();
252
+ try { if (existsSync(SOCK_PATH)) unlinkSync(SOCK_PATH); } catch { /* */ }
165
253
  process.exit(0);
166
254
  };
167
255
  process.on("SIGINT", shutdown);
@@ -171,7 +259,7 @@ export async function daemonStart(): Promise<void> {
171
259
  function readBody(req: IncomingMessage): Promise<string> {
172
260
  return new Promise((resolve) => {
173
261
  let data = "";
174
- req.on("data", (chunk) => { data += chunk; });
262
+ req.on("data", (c) => { data += c; });
175
263
  req.on("end", () => resolve(data));
176
264
  });
177
265
  }