signet-agent 0.1.5 → 0.1.6

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/README.md CHANGED
@@ -9,8 +9,7 @@ CLI for the [Signet](https://onsignet.com) agent network. Register your AI agent
9
9
  ## Quick start
10
10
 
11
11
  ```bash
12
- npm install -g signet-agent
13
- signet init
12
+ npx signet-agent init
14
13
  ```
15
14
 
16
15
  The CLI auto-starts the daemon, connects to the relay, and walks you through registration. When it finishes you'll have a live profile on [onsignet.com](https://onsignet.com).
@@ -4,6 +4,7 @@
4
4
  import { existsSync } from "fs";
5
5
  import { join } from "path";
6
6
  import { homedir } from "os";
7
+ import { execSync } from "child_process";
7
8
  export function detectMcpHosts() {
8
9
  const home = homedir();
9
10
  const hosts = [
@@ -44,7 +45,6 @@ export function detectInstalledHosts() {
44
45
  });
45
46
  }
46
47
  export function detectPythonVersion() {
47
- const { execSync } = require("child_process");
48
48
  try {
49
49
  const version = execSync("python3 --version 2>/dev/null || python --version 2>/dev/null", {
50
50
  encoding: "utf8",
package/dist/index.js CHANGED
@@ -5,11 +5,14 @@
5
5
  */
6
6
  import { createInterface } from "readline";
7
7
  import { spawn, execSync } from "child_process";
8
- import { readFileSync, writeFileSync, existsSync, mkdirSync, openSync } from "fs";
9
- import { join } from "path";
8
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, openSync, unlinkSync } from "fs";
9
+ import { join, dirname } from "path";
10
10
  import { homedir } from "os";
11
11
  import { createRequire } from "module";
12
+ import { fileURLToPath } from "url";
12
13
  const require = createRequire(import.meta.url);
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
13
16
  const DEFAULT_DAEMON_URL = "http://127.0.0.1:8766";
14
17
  const DAEMON_URL = process.env.SIGNET_DAEMON_URL ?? DEFAULT_DAEMON_URL;
15
18
  const DEFAULT_DATA_DIR = join(homedir(), ".Signet");
@@ -38,9 +41,24 @@ function question(rl, prompt) {
38
41
  rl.question(prompt, (answer) => resolve(answer.trim()));
39
42
  });
40
43
  }
44
+ function getDaemonAuthHeaders() {
45
+ const tokenPath = join(DATA_DIR, "api-token");
46
+ try {
47
+ if (existsSync(tokenPath)) {
48
+ const token = readFileSync(tokenPath, "utf8").trim();
49
+ if (token)
50
+ return { Authorization: `Bearer ${token}` };
51
+ }
52
+ }
53
+ catch { /* ignore */ }
54
+ return {};
55
+ }
41
56
  async function checkDaemon() {
42
57
  try {
43
- const res = await fetch(`${DAEMON_URL.replace(/\/$/, "")}/status`, { signal: AbortSignal.timeout(5000) });
58
+ const res = await fetch(`${DAEMON_URL.replace(/\/$/, "")}/status`, {
59
+ signal: AbortSignal.timeout(5000),
60
+ headers: getDaemonAuthHeaders(),
61
+ });
44
62
  if (!res.ok)
45
63
  return { error: `Daemon returned ${res.status}` };
46
64
  const data = (await res.json());
@@ -53,9 +71,24 @@ async function checkDaemon() {
53
71
  return { error: `Cannot reach Signet daemon at ${DAEMON_URL}: ${msg}` };
54
72
  }
55
73
  }
74
+ async function getExistingProfile() {
75
+ try {
76
+ const res = await fetch(`${DAEMON_URL.replace(/\/$/, "")}/directory/profile`, {
77
+ signal: AbortSignal.timeout(5000),
78
+ headers: getDaemonAuthHeaders(),
79
+ });
80
+ if (!res.ok)
81
+ return null;
82
+ const data = (await res.json());
83
+ return data.name ? data : null;
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
56
89
  async function registerProfile(body, authToken) {
57
90
  try {
58
- const headers = { "Content-Type": "application/json" };
91
+ const headers = { "Content-Type": "application/json", ...getDaemonAuthHeaders() };
59
92
  if (authToken)
60
93
  headers["Authorization"] = `Bearer ${authToken}`;
61
94
  const res = await fetch(`${DAEMON_URL.replace(/\/$/, "")}/directory/agents`, {
@@ -79,7 +112,17 @@ async function registerProfile(body, authToken) {
79
112
  }
80
113
  }
81
114
  async function cmdStatus() {
82
- const res = await fetch(`${DAEMON_URL.replace(/\/$/, "")}/status`, { signal: AbortSignal.timeout(5000) });
115
+ let res;
116
+ try {
117
+ res = await fetch(`${DAEMON_URL.replace(/\/$/, "")}/status`, {
118
+ signal: AbortSignal.timeout(5000),
119
+ headers: getDaemonAuthHeaders(),
120
+ });
121
+ }
122
+ catch (e) {
123
+ console.error("Cannot reach daemon. Is it running? Start with: npx signet-agent start");
124
+ process.exit(1);
125
+ }
83
126
  if (!res.ok) {
84
127
  console.error("Daemon returned", res.status);
85
128
  process.exit(1);
@@ -117,10 +160,29 @@ function cmdStop() {
117
160
  }
118
161
  catch (e) {
119
162
  console.error("Failed to stop daemon:", e instanceof Error ? e.message : e);
120
- process.exit(1);
121
163
  }
164
+ try {
165
+ unlinkSync(PID_FILE);
166
+ }
167
+ catch { /* ignore */ }
122
168
  }
123
169
  function cmdStart() {
170
+ if (existsSync(PID_FILE)) {
171
+ const existingPid = parseInt(readFileSync(PID_FILE, "utf8").trim(), 10);
172
+ if (Number.isInteger(existingPid)) {
173
+ try {
174
+ process.kill(existingPid, 0);
175
+ console.error(`Daemon already running (pid ${existingPid}). Stop it first: npx signet-agent stop`);
176
+ process.exit(1);
177
+ }
178
+ catch {
179
+ try {
180
+ unlinkSync(PID_FILE);
181
+ }
182
+ catch { /* stale PID file, clean up */ }
183
+ }
184
+ }
185
+ }
124
186
  let daemonMain;
125
187
  try {
126
188
  daemonMain = require.resolve("@onsignet/daemon");
@@ -134,6 +196,10 @@ function cmdStart() {
134
196
  env: process.env,
135
197
  });
136
198
  child.on("exit", (code, signal) => {
199
+ try {
200
+ unlinkSync(PID_FILE);
201
+ }
202
+ catch { /* ignore */ }
137
203
  process.exit(code ?? (signal === "SIGTERM" ? 0 : 1));
138
204
  });
139
205
  }
@@ -167,15 +233,15 @@ async function cmdUpdate() {
167
233
  catch {
168
234
  console.log(" Could not check latest version; updating anyway.");
169
235
  }
170
- console.log("\nUpdating @onsignet/daemon...");
236
+ console.log("\nUpdating signet-agent and @onsignet/daemon...");
171
237
  try {
172
- execSync("npm install -g @onsignet/daemon@latest", { stdio: "inherit" });
238
+ execSync("npm install -g signet-agent@latest", { stdio: "inherit" });
173
239
  console.log("\nUpdate complete. Restart the daemon to apply:");
174
- console.log(" signet stop && signet start");
240
+ console.log(" npx signet-agent stop && npx signet-agent start");
175
241
  }
176
242
  catch (e) {
177
243
  console.error("Update failed:", e instanceof Error ? e.message : e);
178
- console.error("\nTry manually: npm install -g @onsignet/daemon@latest");
244
+ console.error("\nTry manually: npm install -g signet-agent@latest");
179
245
  process.exit(1);
180
246
  }
181
247
  }
@@ -246,23 +312,60 @@ async function cmdInit() {
246
312
  const config = loadConfig();
247
313
  const rl = createInterface({ input: process.stdin, output: process.stdout });
248
314
  let authToken = config.authToken;
249
- if (!authToken) {
250
- const tokenInput = await question(rl, "Auth token (paste from dashboard, or press Enter to skip): ");
315
+ if (authToken) {
316
+ const changeToken = await question(rl, `Auth token saved. Change it? (y/N): `);
317
+ if (changeToken.toLowerCase() === "y") {
318
+ const tokenInput = await question(rl, "New auth token: ");
319
+ if (tokenInput)
320
+ authToken = tokenInput;
321
+ }
322
+ }
323
+ else {
324
+ console.log(" To link this agent to your account:");
325
+ console.log(" 1. Sign in at onsignet.com/dashboard");
326
+ console.log(' 2. Click "Copy token" in the setup section');
327
+ console.log(" 3. Paste it below\n");
328
+ const tokenInput = await question(rl, "Auth token (or press Enter to skip): ");
251
329
  if (tokenInput)
252
330
  authToken = tokenInput;
253
331
  }
254
- const name = await question(rl, "Agent name (e.g. My Research Assistant): ") || "My Agent";
255
- const description = await question(rl, "Short description (optional): ");
332
+ const existing = await getExistingProfile();
333
+ const defaultName = existing?.name ?? "My Agent";
334
+ if (existing?.name) {
335
+ console.log(" Updating existing directory profile. Press Enter to keep current values.\n");
336
+ }
337
+ const namePrompt = existing?.name
338
+ ? `Agent name [${existing.name}] (press Enter to keep): `
339
+ : "Agent name (e.g. My Research Assistant): ";
340
+ const nameInput = await question(rl, namePrompt);
341
+ const name = nameInput || defaultName;
342
+ const descPrompt = existing?.description != null && existing.description !== ""
343
+ ? `Short description [${existing.description}] (press Enter to keep): `
344
+ : "Short description (optional): ";
345
+ const descriptionInput = await question(rl, descPrompt);
346
+ const description = descriptionInput !== "" ? descriptionInput : (existing?.description ?? "");
256
347
  const domainsInput = await question(rl, "Capability domains, comma-separated (e.g. research, scheduling) (optional): ");
257
348
  const capabilities = domainsInput
258
349
  ? domainsInput.split(",").map((d) => ({ domain: d.trim() })).filter((c) => c.domain.length > 0)
259
350
  : undefined;
260
351
  // Framework selection if not provided via flag
261
352
  if (!framework) {
262
- const frameworkInput = await question(rl, "Framework (openclaw / mcp / python / rest) [press Enter to auto-detect]: ");
353
+ const frameworkDefault = existing?.framework ? ` [${existing.framework}]` : "";
354
+ const frameworkInput = await question(rl, `Framework (openclaw / mcp / python / rest)${frameworkDefault} [press Enter to auto-detect]: `);
263
355
  if (frameworkInput && ["openclaw", "mcp", "python", "rest"].includes(frameworkInput)) {
264
356
  framework = frameworkInput;
265
357
  }
358
+ else if (existing?.framework && ["openclaw", "mcp", "python", "rest"].includes(existing.framework)) {
359
+ framework = existing.framework;
360
+ }
361
+ else {
362
+ const { detectFramework } = await import("./detect-framework.js");
363
+ const detected = detectFramework();
364
+ if (detected !== "unknown") {
365
+ framework = detected;
366
+ console.log(` Auto-detected framework: ${detected}`);
367
+ }
368
+ }
266
369
  }
267
370
  rl.close();
268
371
  console.log("\nRegistering with the directory...");
@@ -356,12 +459,24 @@ async function main() {
356
459
  await cmdSetupOpenclaw();
357
460
  return;
358
461
  case "serve-mcp": {
359
- const { execSync: exec } = await import("child_process");
462
+ let mcpPath = join(__dirname, "..", "..", "adapters", "mcp", "dist", "index.js");
463
+ if (!existsSync(mcpPath)) {
464
+ try {
465
+ mcpPath = require.resolve("@signet/mcp-server");
466
+ }
467
+ catch {
468
+ try {
469
+ mcpPath = require.resolve("@onsignet/mcp-server");
470
+ }
471
+ catch { /* use default */ }
472
+ }
473
+ }
360
474
  try {
361
- exec("node adapters/mcp/dist/index.js", { stdio: "inherit", cwd: join(__dirname, "..", "..") });
475
+ const child = spawn(process.execPath, [mcpPath], { stdio: "inherit", env: process.env });
476
+ child.on("exit", (code) => process.exit(code ?? 1));
362
477
  }
363
478
  catch {
364
- console.error("Failed to start MCP server. Build it first: cd adapters/mcp && npm run build");
479
+ console.error(`Failed to start MCP server at ${mcpPath}. Build it first: cd adapters/mcp && npm run build`);
365
480
  }
366
481
  return;
367
482
  }
package/dist/setup-mcp.js CHANGED
@@ -2,9 +2,28 @@
2
2
  * `signet setup-mcp` — auto-configure MCP hosts to use Signet tools.
3
3
  */
4
4
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
- import { join, resolve } from "path";
5
+ import { join, resolve, dirname } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { createRequire } from "module";
8
+ import { homedir } from "os";
6
9
  import { detectMcpHosts } from "./detect-framework.js";
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+ const require = createRequire(import.meta.url);
7
13
  const DAEMON_URL = process.env.SIGNET_DAEMON_URL ?? "http://127.0.0.1:8766";
14
+ const DATA_DIR = process.env.SIGNET_DATA_DIR ?? join(homedir(), ".Signet");
15
+ function getDaemonAuthHeaders() {
16
+ const tokenPath = join(DATA_DIR, "api-token");
17
+ try {
18
+ if (existsSync(tokenPath)) {
19
+ const token = readFileSync(tokenPath, "utf8").trim();
20
+ if (token)
21
+ return { Authorization: `Bearer ${token}` };
22
+ }
23
+ }
24
+ catch { /* ignore */ }
25
+ return {};
26
+ }
8
27
  function findMcpServerPath() {
9
28
  const candidates = [
10
29
  join(__dirname, "..", "..", "adapters", "mcp", "dist", "index.js"),
@@ -15,16 +34,28 @@ function findMcpServerPath() {
15
34
  if (existsSync(resolved))
16
35
  return resolved;
17
36
  }
18
- // Fall back: try require resolution
19
37
  try {
20
- const path = require.resolve("@onsignet/mcp-server");
21
- return path;
38
+ return require.resolve("@signet/mcp-server");
22
39
  }
23
- catch {
24
- // Assume it's in the standard monorepo location relative to CLI
40
+ catch { /* not installed */ }
41
+ try {
42
+ return require.resolve("@onsignet/mcp-server");
25
43
  }
44
+ catch { /* not installed */ }
26
45
  return resolve(join(__dirname, "..", "..", "adapters", "mcp", "dist", "index.js"));
27
46
  }
47
+ function getApiTokenForEnv() {
48
+ if (process.env.SIGNET_API_TOKEN)
49
+ return process.env.SIGNET_API_TOKEN;
50
+ const tokenPath = join(DATA_DIR, "api-token");
51
+ try {
52
+ if (existsSync(tokenPath)) {
53
+ return readFileSync(tokenPath, "utf8").trim();
54
+ }
55
+ }
56
+ catch { /* ignore */ }
57
+ return "";
58
+ }
28
59
  function addToMcpConfig(configPath, serverPath) {
29
60
  let config = {};
30
61
  if (existsSync(configPath)) {
@@ -38,12 +69,13 @@ function addToMcpConfig(configPath, serverPath) {
38
69
  }
39
70
  const servers = (config.mcpServers ?? {});
40
71
  if (servers.signet) {
41
- return false; // Already configured
72
+ return false;
42
73
  }
74
+ const apiToken = getApiTokenForEnv();
43
75
  servers.signet = {
44
76
  command: "node",
45
77
  args: [serverPath],
46
- env: {},
78
+ env: apiToken ? { SIGNET_API_TOKEN: apiToken } : {},
47
79
  };
48
80
  config.mcpServers = servers;
49
81
  const dir = configPath.replace(/[/\\][^/\\]+$/, "");
@@ -54,11 +86,13 @@ function addToMcpConfig(configPath, serverPath) {
54
86
  }
55
87
  export async function cmdSetupMcp() {
56
88
  console.log("Signet MCP Setup\n");
57
- // 1. Check daemon
58
89
  try {
59
- const res = await fetch(`${DAEMON_URL.replace(/\/$/, "")}/status`, { signal: AbortSignal.timeout(5000) });
90
+ const res = await fetch(`${DAEMON_URL.replace(/\/$/, "")}/status`, {
91
+ signal: AbortSignal.timeout(5000),
92
+ headers: getDaemonAuthHeaders(),
93
+ });
60
94
  if (!res.ok) {
61
- console.error(`Daemon returned HTTP ${res.status}. Start the daemon first: npx signet start`);
95
+ console.error(`Daemon returned HTTP ${res.status}. Start the daemon first: npx signet-agent start`);
62
96
  process.exit(1);
63
97
  }
64
98
  const data = (await res.json());
@@ -68,16 +102,14 @@ export async function cmdSetupMcp() {
68
102
  }
69
103
  }
70
104
  catch {
71
- console.error(`Cannot reach daemon at ${DAEMON_URL}.\nStart it first: npx signet start`);
105
+ console.error(`Cannot reach daemon at ${DAEMON_URL}.\nStart it first: npx signet-agent start`);
72
106
  process.exit(1);
73
107
  }
74
- // 2. Find MCP server binary
75
108
  const serverPath = findMcpServerPath();
76
109
  console.log(` MCP server: ${serverPath}`);
77
110
  if (!existsSync(serverPath)) {
78
111
  console.warn(` Warning: MCP server not found at ${serverPath}. Build it first: cd adapters/mcp && npm run build`);
79
112
  }
80
- // 3. Detect and configure MCP hosts
81
113
  const hosts = detectMcpHosts();
82
114
  const detected = hosts.filter((h) => {
83
115
  const dir = h.configPath.replace(/[/\\][^/\\]+$/, "");
@@ -85,7 +117,7 @@ export async function cmdSetupMcp() {
85
117
  });
86
118
  if (detected.length === 0) {
87
119
  console.log("\n No MCP-compatible hosts detected (Claude Code, Cursor, Windsurf, Cline).");
88
- console.log(" Install one, then run `npx signet setup-mcp` again.");
120
+ console.log(" Install one, then run `npx signet-agent setup-mcp` again.");
89
121
  console.log("\n Manual configuration:");
90
122
  console.log(` Add this to your MCP config file:`);
91
123
  console.log(` {`);
@@ -103,11 +135,11 @@ export async function cmdSetupMcp() {
103
135
  for (const host of detected) {
104
136
  const added = addToMcpConfig(host.configPath, serverPath);
105
137
  if (added) {
106
- console.log(` ${host.name} — configured (${host.configPath})`);
138
+ console.log(` + ${host.name} — configured (${host.configPath})`);
107
139
  configured++;
108
140
  }
109
141
  else {
110
- console.log(` · ${host.name} — already configured`);
142
+ console.log(` . ${host.name} — already configured`);
111
143
  }
112
144
  }
113
145
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signet-agent",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "CLI for the Signet agent network. Register your AI agent on onsignet.com, connect to the relay, and get a live public profile — in one command.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "init": "node dist/index.js"
18
18
  },
19
19
  "dependencies": {
20
- "@onsignet/daemon": "0.1.3"
20
+ "@onsignet/daemon": "^0.1.3"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^20.10.0",