@miruzero/zero-agent 0.3.0 → 0.3.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/README.md CHANGED
@@ -2,11 +2,18 @@
2
2
 
3
3
  Local worker agent for Zero MCP multi-device execution.
4
4
 
5
+ ## Quick start
6
+
7
+ ```powershell
8
+ npx @miruzero/zero-agent@latest start --once
9
+ ```
10
+
11
+ On first run, `start` creates local config, a launcher, and a Windows user-logon autostart file automatically.
12
+
5
13
  ## Commands
6
14
 
7
15
  ```powershell
8
- zero-agent init --server https://zero.miru.work
9
- zero-agent pair --server https://zero.miru.work --code ZERO-XXXX
16
+ zero-agent setup --name TON --code ZERO-XXXX
10
17
  zero-agent start --once
11
18
  zero-agent start
12
19
  zero-agent status
@@ -16,15 +23,17 @@ zero-agent tools
16
23
  zero-agent capabilities
17
24
  ```
18
25
 
19
- ## State
26
+ ## Autostart
20
27
 
21
28
  Default state path:
22
29
 
23
30
  ```text
24
31
  %USERPROFILE%\.zero-agent\device.json
32
+ %USERPROFILE%\.zero-agent\start-zero-agent.cmd
33
+ %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\MiruZeroAgent.vbs
25
34
  ```
26
35
 
27
- Device names default to the local PC hostname. Use --name only when you want a custom display name.
36
+ The launcher uses `npx -y @miruzero/zero-agent@<current-version> start` so the user-logon agent does not depend on a stale global install.
28
37
 
29
38
  Device tokens are local secrets and must not be printed or committed.
30
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miruzero/zero-agent",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "MiruZero local worker agent for multi-device tool execution.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+ const config = require('./config');
7
+ const pkg = require('../package.json');
8
+
9
+ const DEFAULT_INTERVAL = 30000;
10
+
11
+ const q = (value) => '"' + String(value).replace(/"/g, '""') + '"';
12
+ const winPath = (value) => String(value).replace(/\//g, '\\');
13
+
14
+ const defaultRoots = (env = process.env) => {
15
+ if (env.ZERO_AGENT_ROOTS) return env.ZERO_AGENT_ROOTS;
16
+ const home = env.USERPROFILE || os.homedir();
17
+ return [
18
+ path.join(home, 'Desktop', 'zero'),
19
+ path.join(home, 'Downloads')
20
+ ].join(path.delimiter);
21
+ };
22
+
23
+ const startupDir = (env = process.env) => {
24
+ const appData = env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
25
+ return path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
26
+ };
27
+ const paths = (env = process.env) => {
28
+ const home = config.agentHome(env);
29
+ return {
30
+ home,
31
+ launcherFile: path.join(home, 'start-zero-agent.cmd'),
32
+ startupFile: path.join(startupDir(env), 'MiruZeroAgent.vbs'),
33
+ logFile: path.join(config.logDir(env), 'zero-agent.log')
34
+ };
35
+ };
36
+
37
+ const packageSpec = () => pkg.name + '@' + pkg.version;
38
+
39
+ const launcherContent = ({ interval = DEFAULT_INTERVAL, roots, logFile }, env = process.env) => {
40
+ const home = config.agentHome(env);
41
+ const spec = packageSpec();
42
+ return [
43
+ '@echo off',
44
+ 'setlocal',
45
+ 'set "ZERO_TRACE=1"',
46
+ 'set "ZERO_AGENT_HOME=' + winPath(home) + '"',
47
+ 'set "ZERO_AGENT_ROOTS=' + winPath(roots) + '"',
48
+ 'set "ZERO_AGENT_INTERVAL=' + Number(interval || DEFAULT_INTERVAL) + '"',
49
+ 'rem interval=' + Number(interval || DEFAULT_INTERVAL) + 'ms',
50
+ 'if not exist ' + q(winPath(path.dirname(logFile))) + ' mkdir ' + q(winPath(path.dirname(logFile))),
51
+ 'echo [%date% %time%] starting ' + spec + ' >> ' + q(winPath(logFile)),
52
+ 'npx -y ' + spec + ' start --interval %ZERO_AGENT_INTERVAL% >> ' + q(winPath(logFile)) + ' 2>&1',
53
+ ''
54
+ ].join('\r\n');
55
+ };
56
+
57
+ const vbsContent = (launcherFile) => [
58
+ 'Set WshShell = CreateObject("WScript.Shell")',
59
+ 'WshShell.Run Chr(34) & "' + winPath(launcherFile).replace(/"/g, '""') + '" & Chr(34), 0, False',
60
+ ''
61
+ ].join('\r\n');
62
+
63
+ const prepare = (options = {}, env = process.env) => {
64
+ config.ensureDirs(env);
65
+ const p = paths(env);
66
+ const interval = Number(options.interval || env.ZERO_AGENT_INTERVAL || DEFAULT_INTERVAL);
67
+ const roots = options.roots || defaultRoots(env);
68
+ fs.mkdirSync(path.dirname(p.launcherFile), { recursive: true });
69
+ fs.mkdirSync(path.dirname(p.startupFile), { recursive: true });
70
+ fs.mkdirSync(path.dirname(p.logFile), { recursive: true });
71
+ fs.writeFileSync(p.launcherFile, launcherContent({ interval, roots, logFile: p.logFile }, env), 'utf8');
72
+ fs.writeFileSync(p.startupFile, vbsContent(p.launcherFile), 'utf8');
73
+ return { ok: true, interval, roots, ...p };
74
+ };
75
+
76
+ const autostart = {
77
+ DEFAULT_INTERVAL,
78
+ defaultRoots,
79
+ paths,
80
+ prepare,
81
+ launcherContent
82
+ };
83
+
84
+ Object.assign(exports, autostart);
package/src/cli.js CHANGED
@@ -4,6 +4,7 @@ const childProcess = require('node:child_process');
4
4
  const os = require('node:os');
5
5
  const pkg = require('../package.json');
6
6
  const config = require('./config');
7
+ const autostart = require('./autostart');
7
8
  const caps = require('./capabilities');
8
9
  const agent = require('./agent');
9
10
  const { parseArgs } = require('./args');
@@ -14,7 +15,7 @@ Usage:
14
15
  zero-agent setup [--server <url>] [--name <device>] [--code <pairing-code>] [--no-global]
15
16
  zero-agent init --server <url> [--name <device>]
16
17
  zero-agent pair --server <url> --code <pairing-code> [--name <device>]
17
- zero-agent start [--once] [--interval <ms>]
18
+ zero-agent start [--once] [--interval <ms>] [--code <pairing-code>] [--name <device>]
18
19
  zero-agent status
19
20
  zero-agent devices
20
21
  zero-agent doctor
@@ -27,6 +28,42 @@ const printJson = (value) => console.log(JSON.stringify(value, null, 2));
27
28
  const okMark = (ok) => ok ? 'OK' : 'FAIL';
28
29
  const DEFAULT_SERVER = 'https://zero.miru.work';
29
30
 
31
+ const autostartInterval = (parsed) => Number(parsed.interval || autostart.DEFAULT_INTERVAL);
32
+
33
+ const printAutostartInfo = (prepared) => {
34
+ console.log('Autostart file');
35
+ console.log(prepared.startupFile);
36
+ console.log('ตัวนี้จะรันตอน user logon และเปิด:');
37
+ console.log(prepared.launcherFile);
38
+ console.log('Launcher');
39
+ console.log(prepared.launcherFile);
40
+ console.log('ตั้งค่า:');
41
+ console.log('ZERO_TRACE=1');
42
+ console.log('ZERO_AGENT_ROOTS=' + prepared.roots);
43
+ console.log('interval=' + prepared.interval + 'ms');
44
+ console.log('log=' + prepared.logFile);
45
+ };
46
+
47
+ const ensureLocalBootstrap = async (parsed, env) => {
48
+ let device = config.loadDevice(env);
49
+ if (!device) {
50
+ const init = agent.initDevice({ name: parsed.name, server: parsed.server || DEFAULT_SERVER }, env);
51
+ console.log('autoInit=OK file=' + init.file);
52
+ device = config.loadDevice(env);
53
+ }
54
+ const prepared = autostart.prepare({ interval: autostartInterval(parsed) }, env);
55
+ console.log('autoSetup=OK launcher=' + prepared.launcherFile);
56
+ if (parsed.code && (!device || !device.device_id)) {
57
+ const paired = await agent.pairDevice({
58
+ name: parsed.name || device?.name,
59
+ server: parsed.server || device?.server || DEFAULT_SERVER,
60
+ code: parsed.code
61
+ }, env);
62
+ console.log('autoPair=OK file=' + paired.file);
63
+ }
64
+ return prepared;
65
+ };
66
+
30
67
  const runGlobalInstall = (parsed, env) => {
31
68
  if (parsed['no-global'] || env.ZERO_AGENT_SKIP_GLOBAL_INSTALL === '1') {
32
69
  console.log('globalInstall=SKIPPED');
@@ -53,6 +90,8 @@ const runSetup = async (parsed, env) => {
53
90
  runGlobalInstall(parsed, env);
54
91
  const init = agent.initDevice({ name, server }, env);
55
92
  console.log('init=OK file=' + init.file);
93
+ const prepared = autostart.prepare({ interval: autostartInterval(parsed) }, env);
94
+ printAutostartInfo(prepared);
56
95
  if (!parsed.code) {
57
96
  console.log('pairing=NEEDS_CODE');
58
97
  console.log('Next: zero-agent pair --server ' + server + ' --code ZERO-XXXXXX --name ' + init.device.name);
@@ -122,7 +161,7 @@ const printLoopResult = (result) => {
122
161
  status: device?.status || null,
123
162
  name: device?.name || null,
124
163
  host: device?.host || null,
125
- providers: device?.provider_count || null,
164
+ providers: device?.provider_count ?? null,
126
165
  last_seen: device?.last_seen || null,
127
166
  task: taskState
128
167
  }));
@@ -176,11 +215,12 @@ async function run(argv = [], env = process.env) {
176
215
  return 0;
177
216
  }
178
217
  if (command === 'start') {
218
+ await ensureLocalBootstrap(parsed, env);
179
219
  if (parsed.once) {
180
220
  printJson(await runStartOnce(env));
181
221
  return 0;
182
222
  }
183
- const interval = Number(parsed.interval || 10000);
223
+ const interval = Number(parsed.interval || autostart.DEFAULT_INTERVAL);
184
224
  printStartBanner(env);
185
225
  console.log('⏱️ Heartbeat interval: ' + interval + 'ms');
186
226
  console.log('🟢 Device ready; press Ctrl+C to stop');