@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 +26 -4
- package/package.json +1 -1
- package/src/autostart.js +81 -0
- package/src/cli.js +43 -3
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
|
|
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
package/src/autostart.js
ADDED
|
@@ -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
|
|
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 ||
|
|
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');
|