volute 0.15.0 → 0.16.0

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
@@ -161,6 +161,36 @@ volute schedule list --agent atlas
161
161
  volute schedule remove --agent atlas --id <schedule-id>
162
162
  ```
163
163
 
164
+ ## Pages
165
+
166
+ Publish a mind's `home/pages/` directory to the web via [volute.systems](https://volute.systems).
167
+
168
+ ### Setup
169
+
170
+ ```sh
171
+ # Register a system name (one-time)
172
+ volute pages register --name my-system
173
+
174
+ # Or log in with an existing key
175
+ volute pages login --key vp_...
176
+ ```
177
+
178
+ ### Publishing
179
+
180
+ ```sh
181
+ volute pages publish --mind atlas
182
+ # Published 3 file(s) to https://my-system.volute.systems/~atlas/
183
+ ```
184
+
185
+ The command uploads everything in the mind's `home/pages/` directory. Minds can publish their own pages since `VOLUTE_MIND` is set automatically.
186
+
187
+ ### Status & Logout
188
+
189
+ ```sh
190
+ volute pages status --mind atlas # show published URL, file count, last publish time
191
+ volute pages logout # remove stored credentials
192
+ ```
193
+
164
194
  ## Environment variables
165
195
 
166
196
  Manage secrets and config. Supports shared (all agents) and per-agent scoping.
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ voluteHome
4
+ } from "./chunk-M77QBTEH.js";
5
+
6
+ // src/lib/systems-config.ts
7
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
8
+ import { resolve } from "path";
9
+ var DEFAULT_API_URL = "https://volute.systems";
10
+ function configPath() {
11
+ return resolve(voluteHome(), "systems.json");
12
+ }
13
+ function readSystemsConfig() {
14
+ const path = configPath();
15
+ if (!existsSync(path)) return null;
16
+ const raw = readFileSync(path, "utf-8");
17
+ let data;
18
+ try {
19
+ data = JSON.parse(raw);
20
+ } catch {
21
+ console.error(`Warning: ${path} contains invalid JSON. Run "volute logout" and re-login.`);
22
+ return null;
23
+ }
24
+ if (!data.apiKey || !data.system) return null;
25
+ return {
26
+ apiKey: data.apiKey,
27
+ system: data.system,
28
+ apiUrl: data.apiUrl || DEFAULT_API_URL
29
+ };
30
+ }
31
+ function writeSystemsConfig(config) {
32
+ mkdirSync(voluteHome(), { recursive: true });
33
+ writeFileSync(configPath(), `${JSON.stringify(config, null, 2)}
34
+ `, { mode: 384 });
35
+ }
36
+ function deleteSystemsConfig() {
37
+ try {
38
+ unlinkSync(configPath());
39
+ return true;
40
+ } catch (err) {
41
+ if (err.code === "ENOENT") return false;
42
+ throw err;
43
+ }
44
+ }
45
+
46
+ export {
47
+ readSystemsConfig,
48
+ writeSystemsConfig,
49
+ deleteSystemsConfig
50
+ };
@@ -548,6 +548,7 @@ var CHANNELS = {
548
548
  showToolCalls: false,
549
549
  driver: telegram_exports
550
550
  },
551
+ mail: { name: "mail", displayName: "Email", showToolCalls: false },
551
552
  system: { name: "system", displayName: "System", showToolCalls: false }
552
553
  };
553
554
  function getChannelDriver(platform) {
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/prompt.ts
4
+ function promptLine(prompt) {
5
+ process.stderr.write(prompt);
6
+ return new Promise((resolve) => {
7
+ let value = "";
8
+ const onData = (buf) => {
9
+ for (const byte of buf) {
10
+ if (byte === 3) {
11
+ process.stderr.write("\n");
12
+ process.exit(1);
13
+ }
14
+ if (byte === 13 || byte === 10) {
15
+ process.stderr.write("\n");
16
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
17
+ process.stdin.removeListener("data", onData);
18
+ process.stdin.pause();
19
+ resolve(value);
20
+ return;
21
+ }
22
+ if (byte === 127 || byte === 8) {
23
+ value = value.slice(0, -1);
24
+ } else {
25
+ value += String.fromCharCode(byte);
26
+ }
27
+ }
28
+ };
29
+ if (process.stdin.isTTY) process.stdin.setRawMode(true);
30
+ process.stdin.resume();
31
+ process.stdin.on("data", onData);
32
+ });
33
+ }
34
+
35
+ export {
36
+ promptLine
37
+ };
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/systems-fetch.ts
4
+ async function systemsFetch(url, init) {
5
+ try {
6
+ return await fetch(url, init);
7
+ } catch (err) {
8
+ const cause = err.cause;
9
+ const code = cause?.code;
10
+ const host = new URL(url).host;
11
+ if (code === "ENOTFOUND") {
12
+ console.error(`Could not resolve ${host}. Check your internet connection.`);
13
+ } else if (code === "ECONNREFUSED") {
14
+ console.error(`Connection refused by ${host}. The service may be down.`);
15
+ } else {
16
+ console.error(
17
+ `Network error connecting to ${host}: ${cause?.message ?? err.message}`
18
+ );
19
+ }
20
+ process.exit(1);
21
+ }
22
+ }
23
+
24
+ export {
25
+ systemsFetch
26
+ };
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ if (!process.env.VOLUTE_HOME) {
9
9
  var command = process.argv[2];
10
10
  var args = process.argv.slice(3);
11
11
  if (command === "--version" || command === "-v") {
12
- const { default: pkg } = await import("./package-TDCEK6C2.js");
12
+ const { default: pkg } = await import("./package-3QGV3KX6.js");
13
13
  console.log(pkg.version);
14
14
  process.exit(0);
15
15
  }
@@ -18,7 +18,7 @@ switch (command) {
18
18
  await import("./mind-OJN6RBZW.js").then((m) => m.run(args));
19
19
  break;
20
20
  case "send":
21
- await import("./send-SV4K2TDE.js").then((m) => m.run(args));
21
+ await import("./send-KBBZNYG6.js").then((m) => m.run(args));
22
22
  break;
23
23
  case "history":
24
24
  await import("./history-LKCJJMUV.js").then((m) => m.run(args));
@@ -27,7 +27,7 @@ switch (command) {
27
27
  await import("./variant-X5QFG6KK.js").then((m) => m.run(args));
28
28
  break;
29
29
  case "connector":
30
- await import("./connector-JFAHYFQX.js").then((m) => m.run(args));
30
+ await import("./connector-3ELFMI2R.js").then((m) => m.run(args));
31
31
  break;
32
32
  case "channel":
33
33
  await import("./channel-SLURLIRV.js").then((m) => m.run(args));
@@ -36,16 +36,16 @@ switch (command) {
36
36
  await import("./schedule-AGYLDMNS.js").then((m) => m.run(args));
37
37
  break;
38
38
  case "env":
39
- await import("./env-6LXDUZDA.js").then((m) => m.run(args));
39
+ await import("./env-6IDWGBUH.js").then((m) => m.run(args));
40
40
  break;
41
41
  case "up":
42
- await import("./up-C4MV6EXV.js").then((m) => m.run(args));
42
+ await import("./up-GZLWZAQE.js").then((m) => m.run(args));
43
43
  break;
44
44
  case "down":
45
45
  await import("./down-A56B5JLK.js").then((m) => m.run(args));
46
46
  break;
47
47
  case "restart":
48
- await import("./daemon-restart-IZGEF4NA.js").then((m) => m.run(args));
48
+ await import("./daemon-restart-MS5FI44G.js").then((m) => m.run(args));
49
49
  break;
50
50
  case "setup":
51
51
  await import("./setup-DJKIZKGW.js").then((m) => m.run(args));
@@ -65,6 +65,18 @@ switch (command) {
65
65
  case "sprout":
66
66
  await import("./sprout-TJ3BHVOG.js").then((m) => m.run(args));
67
67
  break;
68
+ case "pages":
69
+ await import("./pages-6IV4VQTU.js").then((m) => m.run(args));
70
+ break;
71
+ case "register":
72
+ await import("./register-LDE6LRXY.js").then((m) => m.run(args));
73
+ break;
74
+ case "login":
75
+ await import("./login-ORQDXLBM.js").then((m) => m.run(args));
76
+ break;
77
+ case "logout":
78
+ await import("./logout-XC5AUO5I.js").then((m) => m.run());
79
+ break;
68
80
  case "--help":
69
81
  case "-h":
70
82
  case void 0:
@@ -114,6 +126,13 @@ Commands:
114
126
  volute setup [--port N] [--host H] Install system service with user isolation
115
127
  volute setup uninstall [--force] Remove system service + isolation
116
128
 
129
+ volute register [--name <name>] Register a system on volute.systems
130
+ volute login [--key <key>] Log in with an existing API key
131
+ volute logout Remove stored credentials
132
+
133
+ volute pages publish [--mind <name>] Publish mind's pages/ directory
134
+ volute pages status [--mind <name>] Show publish status
135
+
117
136
  volute seed <name> Plant a seed mind (orientation mode)
118
137
  volute sprout Complete orientation, become a full mind
119
138
 
@@ -124,7 +143,7 @@ Options:
124
143
  --version, -v Show version number
125
144
  --help, -h Show this help message
126
145
 
127
- Mind-scoped commands (send, history, variant, connector, schedule, channel)
146
+ Mind-scoped commands (send, history, variant, connector, schedule, channel, pages)
128
147
  use --mind <name> or VOLUTE_MIND env var to identify the mind.`);
129
148
  break;
130
149
  default:
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ promptLine
4
+ } from "./chunk-RVKR2R7F.js";
2
5
  import {
3
6
  resolveMindName
4
7
  } from "./chunk-NAOW2CLO.js";
@@ -40,37 +43,6 @@ function printUsage() {
40
43
  volute connector connect <type> [--mind <name>]
41
44
  volute connector disconnect <type> [--mind <name>]`);
42
45
  }
43
- async function promptValue(key, description) {
44
- process.stderr.write(`${description}
45
- Enter value for ${key}: `);
46
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
47
- return new Promise((resolve) => {
48
- let value = "";
49
- const onData = (buf) => {
50
- for (const byte of buf) {
51
- if (byte === 3) {
52
- process.stderr.write("\n");
53
- process.exit(1);
54
- }
55
- if (byte === 13 || byte === 10) {
56
- process.stderr.write("\n");
57
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
58
- process.stdin.removeListener("data", onData);
59
- process.stdin.pause();
60
- resolve(value);
61
- return;
62
- }
63
- if (byte === 127 || byte === 8) {
64
- value = value.slice(0, -1);
65
- } else {
66
- value += String.fromCharCode(byte);
67
- }
68
- }
69
- };
70
- process.stdin.resume();
71
- process.stdin.on("data", onData);
72
- });
73
- }
74
46
  async function connectConnector(args) {
75
47
  const { positional, flags } = parseArgs(args, {
76
48
  mind: { type: "string" }
@@ -104,7 +76,8 @@ Set them with: volute env set <KEY> --mind ${mindName}`);
104
76
  console.error(`${connectorName} connector requires some environment variables.
105
77
  `);
106
78
  for (const v of missing) {
107
- const value = await promptValue(v.name, v.description);
79
+ const value = await promptLine(`${v.description}
80
+ Enter value for ${v.name}: `);
108
81
  if (!value) {
109
82
  console.error(`No value provided for ${v.name}. Aborting.`);
110
83
  process.exit(1);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "./chunk-BEFIBW5B.js";
4
+ } from "./chunk-LLBBVTEY.js";
5
5
  import {
6
6
  stopDaemon
7
7
  } from "./chunk-QJIIHU32.js";
package/dist/daemon.js CHANGED
@@ -6,6 +6,9 @@ import {
6
6
  findTemplatesRoot,
7
7
  listFiles
8
8
  } from "./chunk-PO5Q2AYN.js";
9
+ import {
10
+ readSystemsConfig
11
+ } from "./chunk-37X7ECMF.js";
9
12
  import {
10
13
  RotatingLog,
11
14
  clearJsonMap,
@@ -32,7 +35,7 @@ import {
32
35
  import {
33
36
  CHANNELS,
34
37
  getChannelDriver
35
- } from "./chunk-PDLAZJGC.js";
38
+ } from "./chunk-3FD4ZZUL.js";
36
39
  import {
37
40
  exec,
38
41
  gitExec,
@@ -431,6 +434,159 @@ function getConnectorManager() {
431
434
  return instance;
432
435
  }
433
436
 
437
+ // src/lib/mail-poller.ts
438
+ function formatEmailContent(email) {
439
+ if (email.body) {
440
+ return email.subject ? `Subject: ${email.subject}
441
+
442
+ ${email.body}` : email.body;
443
+ }
444
+ if (email.html) {
445
+ return email.subject ? `Subject: ${email.subject}
446
+
447
+ [HTML email \u2014 plain text not available]` : "[HTML email \u2014 plain text not available]";
448
+ }
449
+ return email.subject ? `Subject: ${email.subject}` : "[Empty email]";
450
+ }
451
+ var MailPoller = class {
452
+ interval = null;
453
+ daemonPort = null;
454
+ daemonToken = null;
455
+ lastPoll = null;
456
+ running = false;
457
+ start(daemonPort, daemonToken) {
458
+ if (this.running) {
459
+ console.error("[mail] already running \u2014 ignoring duplicate start");
460
+ return;
461
+ }
462
+ const config = readSystemsConfig();
463
+ if (!config) {
464
+ console.error("[mail] no systems config \u2014 mail polling disabled");
465
+ return;
466
+ }
467
+ this.daemonPort = daemonPort ?? null;
468
+ this.daemonToken = daemonToken ?? null;
469
+ this.lastPoll = (/* @__PURE__ */ new Date()).toISOString();
470
+ this.running = true;
471
+ this.interval = setInterval(() => this.poll(), 3e4);
472
+ console.error("[mail] polling started");
473
+ }
474
+ stop() {
475
+ if (this.interval) clearInterval(this.interval);
476
+ this.interval = null;
477
+ this.running = false;
478
+ }
479
+ isRunning() {
480
+ return this.running;
481
+ }
482
+ async poll() {
483
+ const config = readSystemsConfig();
484
+ if (!config) {
485
+ console.error("[mail] systems config removed \u2014 stopping mail polling");
486
+ this.stop();
487
+ return;
488
+ }
489
+ const since = this.lastPoll ?? (/* @__PURE__ */ new Date()).toISOString();
490
+ const url = `${config.apiUrl}/api/mail/system/poll?since=${encodeURIComponent(since)}`;
491
+ try {
492
+ const res = await fetch(url, {
493
+ headers: { Authorization: `Bearer ${config.apiKey}` }
494
+ });
495
+ if (!res.ok) {
496
+ console.error(`[mail] poll failed: HTTP ${res.status}`);
497
+ return;
498
+ }
499
+ const data = await res.json();
500
+ if (!Array.isArray(data.emails)) {
501
+ console.error("[mail] poll response missing emails array");
502
+ return;
503
+ }
504
+ for (const email of data.emails) {
505
+ await this.deliver(email.mind, email);
506
+ }
507
+ if (data.emails.length > 0) {
508
+ this.lastPoll = data.emails[data.emails.length - 1].receivedAt;
509
+ } else {
510
+ this.lastPoll = (/* @__PURE__ */ new Date()).toISOString();
511
+ }
512
+ } catch (err) {
513
+ console.error("[mail] poll error:", err);
514
+ }
515
+ }
516
+ async deliver(mind, email) {
517
+ const entry = findMind(mind);
518
+ if (!entry || !entry.running) {
519
+ console.error(`[mail] skipping delivery to ${mind}: ${!entry ? "not found" : "not running"}`);
520
+ return;
521
+ }
522
+ const channel = `mail:${email.from.address}`;
523
+ const sender = email.from.name || email.from.address;
524
+ const text2 = formatEmailContent(email);
525
+ const body = JSON.stringify({
526
+ content: [{ type: "text", text: text2 }],
527
+ channel,
528
+ sender,
529
+ platform: "Email",
530
+ isDM: true
531
+ });
532
+ if (!this.daemonPort || !this.daemonToken) {
533
+ console.error(`[mail] cannot deliver to ${mind}: daemon port/token not set`);
534
+ return;
535
+ }
536
+ const daemonUrl = `http://${daemonLoopback()}:${this.daemonPort}`;
537
+ const controller = new AbortController();
538
+ const timeout = setTimeout(() => controller.abort(), 12e4);
539
+ try {
540
+ const res = await fetch(`${daemonUrl}/api/minds/${encodeURIComponent(mind)}/message`, {
541
+ method: "POST",
542
+ headers: {
543
+ "Content-Type": "application/json",
544
+ Authorization: `Bearer ${this.daemonToken}`,
545
+ Origin: daemonUrl
546
+ },
547
+ body,
548
+ signal: controller.signal
549
+ });
550
+ if (!res.ok) {
551
+ console.error(`[mail] deliver to ${mind} got HTTP ${res.status}`);
552
+ } else {
553
+ console.error(`[mail] delivered email from ${email.from.address} to ${mind}`);
554
+ }
555
+ await res.text().catch(() => {
556
+ });
557
+ } catch (err) {
558
+ console.error(`[mail] failed to deliver to ${mind}:`, err);
559
+ } finally {
560
+ clearTimeout(timeout);
561
+ }
562
+ }
563
+ };
564
+ var instance2 = null;
565
+ function getMailPoller() {
566
+ if (!instance2) instance2 = new MailPoller();
567
+ return instance2;
568
+ }
569
+ async function ensureMailAddress(mindName) {
570
+ const config = readSystemsConfig();
571
+ if (!config) return;
572
+ try {
573
+ const res = await fetch(`${config.apiUrl}/api/mail/addresses/${encodeURIComponent(mindName)}`, {
574
+ method: "PUT",
575
+ headers: {
576
+ Authorization: `Bearer ${config.apiKey}`,
577
+ "Content-Type": "application/json"
578
+ }
579
+ });
580
+ if (!res.ok) {
581
+ console.error(`[mail] failed to ensure address for ${mindName}: HTTP ${res.status}`);
582
+ }
583
+ await res.text().catch(() => {
584
+ });
585
+ } catch (err) {
586
+ console.error(`[mail] failed to ensure address for ${mindName}:`, err);
587
+ }
588
+ }
589
+
434
590
  // src/lib/migrate-agents-to-minds.ts
435
591
  import { execFileSync } from "child_process";
436
592
  import { existsSync as existsSync3, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync2 } from "fs";
@@ -718,10 +874,10 @@ var Scheduler = class {
718
874
  }
719
875
  }
720
876
  };
721
- var instance2 = null;
877
+ var instance3 = null;
722
878
  function getScheduler() {
723
- if (!instance2) instance2 = new Scheduler();
724
- return instance2;
879
+ if (!instance3) instance3 = new Scheduler();
880
+ return instance3;
725
881
  }
726
882
 
727
883
  // src/lib/token-budget.ts
@@ -882,10 +1038,10 @@ ${summary}`
882
1038
  }
883
1039
  }
884
1040
  };
885
- var instance3 = null;
1041
+ var instance4 = null;
886
1042
  function getTokenBudget() {
887
- if (!instance3) instance3 = new TokenBudget();
888
- return instance3;
1043
+ if (!instance4) instance4 = new TokenBudget();
1044
+ return instance4;
889
1045
  }
890
1046
 
891
1047
  // src/web/middleware/auth.ts
@@ -1427,10 +1583,16 @@ var app3 = new Hono3().get("/:name/connectors", (c) => {
1427
1583
  const configured = config.connectors ?? [];
1428
1584
  const manager = getConnectorManager();
1429
1585
  const runningStatus = manager.getConnectorStatus(name);
1430
- const connectors = configured.map((type) => {
1431
- const status = runningStatus.find((s) => s.type === type);
1432
- return { type, running: status?.running ?? false };
1433
- });
1586
+ const connectors = configured.map(
1587
+ (type) => {
1588
+ const status = runningStatus.find((s) => s.type === type);
1589
+ return { type, running: status?.running ?? false };
1590
+ }
1591
+ );
1592
+ const systemsConfig = readSystemsConfig();
1593
+ if (systemsConfig && getMailPoller().isRunning()) {
1594
+ connectors.push({ type: "mail", running: true, auto: true });
1595
+ }
1434
1596
  return c.json(connectors);
1435
1597
  }).post("/:name/connectors/:type", requireAdmin, async (c) => {
1436
1598
  const name = c.req.param("name");
@@ -2188,7 +2350,7 @@ var TypingMap = class {
2188
2350
  dispose() {
2189
2351
  clearInterval(this.sweepTimer);
2190
2352
  this.channels.clear();
2191
- if (instance4 === this) instance4 = void 0;
2353
+ if (instance5 === this) instance5 = void 0;
2192
2354
  }
2193
2355
  sweep() {
2194
2356
  const now = Date.now();
@@ -2204,12 +2366,12 @@ var TypingMap = class {
2204
2366
  }
2205
2367
  }
2206
2368
  };
2207
- var instance4;
2369
+ var instance5;
2208
2370
  function getTypingMap() {
2209
- if (!instance4) {
2210
- instance4 = new TypingMap();
2371
+ if (!instance5) {
2372
+ instance5 = new TypingMap();
2211
2373
  }
2212
- return instance4;
2374
+ return instance5;
2213
2375
  }
2214
2376
 
2215
2377
  // src/web/routes/minds.ts
@@ -2221,6 +2383,9 @@ async function startMindFull(name, baseName, variantName) {
2221
2383
  const entry = findMind(baseName);
2222
2384
  await getConnectorManager().startConnectors(baseName, dir, entry.port, getDaemonPort());
2223
2385
  getScheduler().loadSchedules(baseName);
2386
+ ensureMailAddress(baseName).catch(
2387
+ (err) => console.error(`[mail] failed to ensure address for ${baseName}:`, err)
2388
+ );
2224
2389
  const config = readVoluteConfig(dir);
2225
2390
  if (config?.tokenBudget) {
2226
2391
  getTokenBudget().setBudget(
@@ -3311,6 +3476,9 @@ var app10 = new Hono10().post("/restart", requireAdmin, (c) => {
3311
3476
  });
3312
3477
  });
3313
3478
  });
3479
+ }).get("/info", (c) => {
3480
+ const config = readSystemsConfig();
3481
+ return c.json({ system: config?.system ?? null });
3314
3482
  });
3315
3483
  var system_default = app10;
3316
3484
 
@@ -4200,6 +4368,8 @@ async function startDaemon(opts) {
4200
4368
  const connectors = initConnectorManager();
4201
4369
  const scheduler = getScheduler();
4202
4370
  scheduler.start(port, token);
4371
+ const mailPoller = getMailPoller();
4372
+ mailPoller.start(port, token);
4203
4373
  const tokenBudget = getTokenBudget();
4204
4374
  tokenBudget.start(port, token);
4205
4375
  const registry = readRegistry();
@@ -4218,6 +4388,9 @@ async function startDaemon(opts) {
4218
4388
  const dir = mindDir(entry.name);
4219
4389
  await connectors.startConnectors(entry.name, dir, entry.port, port);
4220
4390
  scheduler.loadSchedules(entry.name);
4391
+ ensureMailAddress(entry.name).catch(
4392
+ (err) => console.error(`[mail] failed to ensure address for ${entry.name}:`, err)
4393
+ );
4221
4394
  const config = readVoluteConfig(dir);
4222
4395
  if (config?.tokenBudget) {
4223
4396
  tokenBudget.setBudget(
@@ -4266,6 +4439,7 @@ async function startDaemon(opts) {
4266
4439
  console.error("[daemon] shutting down...");
4267
4440
  scheduler.stop();
4268
4441
  scheduler.saveState();
4442
+ mailPoller.stop();
4269
4443
  tokenBudget.stop();
4270
4444
  await connectors.stopAll();
4271
4445
  await manager.stopAll();
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ promptLine
4
+ } from "./chunk-RVKR2R7F.js";
2
5
  import {
3
6
  parseArgs
4
7
  } from "./chunk-D424ZQGI.js";
@@ -13,36 +16,6 @@ import {
13
16
  import "./chunk-K3NQKI34.js";
14
17
 
15
18
  // src/commands/env.ts
16
- async function promptValue(key) {
17
- process.stderr.write(`Enter value for ${key}: `);
18
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
19
- return new Promise((resolve) => {
20
- let value = "";
21
- const onData = (buf) => {
22
- for (const byte of buf) {
23
- if (byte === 3) {
24
- process.stderr.write("\n");
25
- process.exit(1);
26
- }
27
- if (byte === 13 || byte === 10) {
28
- process.stderr.write("\n");
29
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
30
- process.stdin.removeListener("data", onData);
31
- process.stdin.pause();
32
- resolve(value);
33
- return;
34
- }
35
- if (byte === 127 || byte === 8) {
36
- value = value.slice(0, -1);
37
- } else {
38
- value += String.fromCharCode(byte);
39
- }
40
- }
41
- };
42
- process.stdin.resume();
43
- process.stdin.on("data", onData);
44
- });
45
- }
46
19
  function maskValue(value) {
47
20
  if (value.length <= 6) return "***";
48
21
  return `${value.slice(0, 3)}...${value.slice(-3)}`;
@@ -61,7 +34,7 @@ async function run(args) {
61
34
  console.error("Usage: volute env set <KEY> [<VALUE>] [--mind <name>]");
62
35
  process.exit(1);
63
36
  }
64
- const value = positional[2] ?? await promptValue(key);
37
+ const value = positional[2] ?? await promptLine(`Enter value for ${key}: `);
65
38
  let res;
66
39
  if (flags.mind) {
67
40
  res = await daemonFetch(