@miruzero/zero-agent 0.3.0 → 0.3.1

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
@@ -1,12 +1,34 @@
1
- # Zero Agent
1
+ # Zero Agent
2
2
 
3
3
  Local worker agent for Zero MCP multi-device execution.
4
4
 
5
+ ## One-command start
6
+
7
+ For a normal user, start with npm/npx and let the agent create local files automatically:
8
+
9
+ ```powershell
10
+ npx @miruzero/zero-agent@latest start --name TON --code ZERO-XXXX
11
+ ```
12
+
13
+ Or inside this package:
14
+
15
+ ```powershell
16
+ npm start
17
+ ```
18
+
19
+ On first start, the agent creates:
20
+
21
+ ```text
22
+ %USERPROFILE%\.zero-agent\device.json
23
+ %USERPROFILE%\.zero-agent\start-zero-agent.cmd
24
+ %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\MiruZeroAgent.vbs
25
+ %USERPROFILE%\.zero-agent\logs\zero-agent.log
26
+ ```
27
+
5
28
  ## Commands
6
29
 
7
30
  ```powershell
8
- zero-agent init --server https://zero.miru.work
9
- zero-agent pair --server https://zero.miru.work --code ZERO-XXXX
31
+ zero-agent setup --server https://zero.miru.work --name TON --code ZERO-XXXX
10
32
  zero-agent start --once
11
33
  zero-agent start
12
34
  zero-agent status
@@ -33,4 +55,4 @@ Device tokens are local secrets and must not be printed or committed.
33
55
  ```powershell
34
56
  npm test
35
57
  npm pack --dry-run
36
- ```
58
+ ```
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.1",
4
4
  "description": "MiruZero local worker agent for multi-device tool execution.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -0,0 +1,81 @@
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
+
8
+ const DEFAULT_INTERVAL = 30000;
9
+
10
+ const q = (value) => '"' + String(value).replace(/"/g, '""') + '"';
11
+ const winPath = (value) => String(value).replace(/\//g, '\\');
12
+
13
+ const defaultRoots = (env = process.env) => {
14
+ if (env.ZERO_AGENT_ROOTS) return env.ZERO_AGENT_ROOTS;
15
+ const home = env.USERPROFILE || os.homedir();
16
+ return [
17
+ path.join(home, 'Desktop', 'zero'),
18
+ path.join(home, 'Downloads')
19
+ ].join(path.delimiter);
20
+ };
21
+
22
+ const startupDir = (env = process.env) => {
23
+ const appData = env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
24
+ return path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
25
+ };
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 launcherContent = ({ interval = DEFAULT_INTERVAL, roots, logFile }, env = process.env) => {
38
+ const home = config.agentHome(env);
39
+ return [
40
+ '@echo off',
41
+ 'setlocal',
42
+ 'set "ZERO_TRACE=1"',
43
+ 'set "ZERO_AGENT_HOME=' + winPath(home) + '"',
44
+ 'set "ZERO_AGENT_ROOTS=' + winPath(roots) + '"',
45
+ 'set "ZERO_AGENT_INTERVAL=' + Number(interval || DEFAULT_INTERVAL) + '"',
46
+ 'rem interval=' + Number(interval || DEFAULT_INTERVAL) + 'ms',
47
+ 'if not exist ' + q(winPath(path.dirname(logFile))) + ' mkdir ' + q(winPath(path.dirname(logFile))),
48
+ 'echo [%date% %time%] starting zero-agent >> ' + q(winPath(logFile)),
49
+ 'zero-agent start --interval %ZERO_AGENT_INTERVAL% >> ' + q(winPath(logFile)) + ' 2>&1',
50
+ ''
51
+ ].join('\r\n');
52
+ };
53
+
54
+ const vbsContent = (launcherFile) => [
55
+ 'Set WshShell = CreateObject("WScript.Shell")',
56
+ 'WshShell.Run Chr(34) & "' + winPath(launcherFile).replace(/"/g, '""') + '" & Chr(34), 0, False',
57
+ ''
58
+ ].join('\r\n');
59
+
60
+ const prepare = (options = {}, env = process.env) => {
61
+ config.ensureDirs(env);
62
+ const p = paths(env);
63
+ const interval = Number(options.interval || env.ZERO_AGENT_INTERVAL || DEFAULT_INTERVAL);
64
+ const roots = options.roots || defaultRoots(env);
65
+ fs.mkdirSync(path.dirname(p.launcherFile), { recursive: true });
66
+ fs.mkdirSync(path.dirname(p.startupFile), { recursive: true });
67
+ fs.mkdirSync(path.dirname(p.logFile), { recursive: true });
68
+ fs.writeFileSync(p.launcherFile, launcherContent({ interval, roots, logFile: p.logFile }, env), 'utf8');
69
+ fs.writeFileSync(p.startupFile, vbsContent(p.launcherFile), 'utf8');
70
+ return { ok: true, interval, roots, ...p };
71
+ };
72
+
73
+ const autostart = {
74
+ DEFAULT_INTERVAL,
75
+ defaultRoots,
76
+ paths,
77
+ prepare
78
+ };
79
+
80
+ Object.assign(exports, autostart);
81
+
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');