toc-ocra 0.2.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/LICENSE +21 -0
- package/README.md +70 -0
- package/assets/icon.svg +1 -0
- package/dist/plugin/renderer.css +2 -0
- package/dist/plugin/renderer.css.map +7 -0
- package/dist/plugin/renderer.js +2 -0
- package/dist/plugin/renderer.js.map +7 -0
- package/dist/src/config.d.ts +66 -0
- package/dist/src/config.js +173 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/control/pairing.d.ts +44 -0
- package/dist/src/control/pairing.js +99 -0
- package/dist/src/control/pairing.js.map +1 -0
- package/dist/src/exec/script-runner.d.ts +9 -0
- package/dist/src/exec/script-runner.js +110 -0
- package/dist/src/exec/script-runner.js.map +1 -0
- package/dist/src/health/server.d.ts +74 -0
- package/dist/src/health/server.js +248 -0
- package/dist/src/health/server.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +173 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logging/diagnostic-log.d.ts +16 -0
- package/dist/src/logging/diagnostic-log.js +128 -0
- package/dist/src/logging/diagnostic-log.js.map +1 -0
- package/dist/src/providers/cli/claude.d.ts +2 -0
- package/dist/src/providers/cli/claude.js +48 -0
- package/dist/src/providers/cli/claude.js.map +1 -0
- package/dist/src/providers/cli/codex.d.ts +2 -0
- package/dist/src/providers/cli/codex.js +67 -0
- package/dist/src/providers/cli/codex.js.map +1 -0
- package/dist/src/providers/cli/gemini.d.ts +2 -0
- package/dist/src/providers/cli/gemini.js +30 -0
- package/dist/src/providers/cli/gemini.js.map +1 -0
- package/dist/src/providers/cli/grok.d.ts +2 -0
- package/dist/src/providers/cli/grok.js +29 -0
- package/dist/src/providers/cli/grok.js.map +1 -0
- package/dist/src/providers/cli/opencode.d.ts +2 -0
- package/dist/src/providers/cli/opencode.js +29 -0
- package/dist/src/providers/cli/opencode.js.map +1 -0
- package/dist/src/providers/detection.d.ts +11 -0
- package/dist/src/providers/detection.js +96 -0
- package/dist/src/providers/detection.js.map +1 -0
- package/dist/src/providers/model-catalog-collector.d.ts +22 -0
- package/dist/src/providers/model-catalog-collector.js +66 -0
- package/dist/src/providers/model-catalog-collector.js.map +1 -0
- package/dist/src/providers/model-catalog.d.ts +45 -0
- package/dist/src/providers/model-catalog.js +382 -0
- package/dist/src/providers/model-catalog.js.map +1 -0
- package/dist/src/providers/provider.d.ts +49 -0
- package/dist/src/providers/provider.js +63 -0
- package/dist/src/providers/provider.js.map +1 -0
- package/dist/src/providers/registry.d.ts +31 -0
- package/dist/src/providers/registry.js +60 -0
- package/dist/src/providers/registry.js.map +1 -0
- package/dist/src/runs/orphan-registry.d.ts +24 -0
- package/dist/src/runs/orphan-registry.js +63 -0
- package/dist/src/runs/orphan-registry.js.map +1 -0
- package/dist/src/runs/process-tree.d.ts +21 -0
- package/dist/src/runs/process-tree.js +95 -0
- package/dist/src/runs/process-tree.js.map +1 -0
- package/dist/src/runs/run-supervisor.d.ts +63 -0
- package/dist/src/runs/run-supervisor.js +285 -0
- package/dist/src/runs/run-supervisor.js.map +1 -0
- package/dist/src/runtime/agent-runtime.d.ts +68 -0
- package/dist/src/runtime/agent-runtime.js +340 -0
- package/dist/src/runtime/agent-runtime.js.map +1 -0
- package/dist/src/runtime/agent-service.d.ts +33 -0
- package/dist/src/runtime/agent-service.js +181 -0
- package/dist/src/runtime/agent-service.js.map +1 -0
- package/dist/src/runtime/cli/hydrate-shell-path.d.ts +33 -0
- package/dist/src/runtime/cli/hydrate-shell-path.js +173 -0
- package/dist/src/runtime/cli/hydrate-shell-path.js.map +1 -0
- package/dist/src/session/terminal-line-sanitizer.d.ts +7 -0
- package/dist/src/session/terminal-line-sanitizer.js +21 -0
- package/dist/src/session/terminal-line-sanitizer.js.map +1 -0
- package/dist/src/session/terminal-tail-buffer.d.ts +70 -0
- package/dist/src/session/terminal-tail-buffer.js +149 -0
- package/dist/src/session/terminal-tail-buffer.js.map +1 -0
- package/dist/src/shared/cli-detection-types.d.ts +19 -0
- package/dist/src/shared/cli-detection-types.js +2 -0
- package/dist/src/shared/cli-detection-types.js.map +1 -0
- package/dist/src/shared/node-cli-command-resolution.d.ts +19 -0
- package/dist/src/shared/node-cli-command-resolution.js +173 -0
- package/dist/src/shared/node-cli-command-resolution.js.map +1 -0
- package/dist/src/transport/gateway-client.d.ts +38 -0
- package/dist/src/transport/gateway-client.js +73 -0
- package/dist/src/transport/gateway-client.js.map +1 -0
- package/dist/src/types/protocol.d.ts +172 -0
- package/dist/src/types/protocol.js +55 -0
- package/dist/src/types/protocol.js.map +1 -0
- package/dist/src/workspace/directory-browser.d.ts +17 -0
- package/dist/src/workspace/directory-browser.js +47 -0
- package/dist/src/workspace/directory-browser.js.map +1 -0
- package/dist/src/workspace/workspace-manager.d.ts +41 -0
- package/dist/src/workspace/workspace-manager.js +436 -0
- package/dist/src/workspace/workspace-manager.js.map +1 -0
- package/locales/en.json +66 -0
- package/locales/zh-CN.json +66 -0
- package/package.json +90 -0
- package/scripts/service.mjs +174 -0
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "toc-ocra",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Echo device agent: local CLI execution service plus its desktop plugin (heartbeat + long-poll, headless CLIs)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"toc-ocra": "./dist/src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/src/",
|
|
11
|
+
"dist/plugin/",
|
|
12
|
+
"assets/",
|
|
13
|
+
"locales/",
|
|
14
|
+
"scripts/service.mjs",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json && npm run build:plugin",
|
|
20
|
+
"dev": "tsx src/index.ts run",
|
|
21
|
+
"start": "node dist/src/index.js run",
|
|
22
|
+
"doctor": "tsx src/index.ts doctor",
|
|
23
|
+
"service:install": "node scripts/service.mjs install",
|
|
24
|
+
"service:uninstall": "node scripts/service.mjs uninstall",
|
|
25
|
+
"publish:latest": "bash ./publish-latest.sh",
|
|
26
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.plugin.json",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"build:plugin": "toc-plugin build --entry plugin/index.tsx",
|
|
30
|
+
"dev:plugin": "toc-plugin build --entry plugin/index.tsx --no-minify --watch",
|
|
31
|
+
"validate:plugin": "toc-plugin validate"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"zod": "^4.1.12"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@toc-starter/plugin-sdk": "file:../echo_web/packages/plugin-sdk",
|
|
41
|
+
"@types/node": "^22.15.0",
|
|
42
|
+
"@types/react": "18.3.12",
|
|
43
|
+
"tsx": "^4.20.0",
|
|
44
|
+
"typescript": "^5.8.0",
|
|
45
|
+
"vitest": "^3.2.0"
|
|
46
|
+
},
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"tocPlugin": {
|
|
49
|
+
"id": "echo.device-agent",
|
|
50
|
+
"displayName": "设备代理",
|
|
51
|
+
"description": "让这台电脑接收任务链派发的任务:连接本机 toc-ocra 服务,一键绑定到当前服务器,查看在线状态和本机 CLI。",
|
|
52
|
+
"publisher": "Echo",
|
|
53
|
+
"apiVersion": "1.x",
|
|
54
|
+
"platforms": [
|
|
55
|
+
"desktop"
|
|
56
|
+
],
|
|
57
|
+
"icon": "./assets/icon.svg",
|
|
58
|
+
"entries": {
|
|
59
|
+
"renderer": "./dist/plugin/renderer.js",
|
|
60
|
+
"style": "./dist/plugin/renderer.css"
|
|
61
|
+
},
|
|
62
|
+
"locales": {
|
|
63
|
+
"en": "./locales/en.json",
|
|
64
|
+
"zh-CN": "./locales/zh-CN.json"
|
|
65
|
+
},
|
|
66
|
+
"permissions": [
|
|
67
|
+
"storage:plugin",
|
|
68
|
+
"secureStorage:plugin",
|
|
69
|
+
"notifications:publish",
|
|
70
|
+
"api:user"
|
|
71
|
+
],
|
|
72
|
+
"contributes": {
|
|
73
|
+
"navigation": [
|
|
74
|
+
{
|
|
75
|
+
"id": "agent.nav",
|
|
76
|
+
"title": "设备代理",
|
|
77
|
+
"titleKey": "nav.title",
|
|
78
|
+
"path": "/plugins/device-agent",
|
|
79
|
+
"order": 30
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"routes": [
|
|
83
|
+
{
|
|
84
|
+
"id": "agent.home",
|
|
85
|
+
"path": "/plugins/device-agent"
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { chmod, mkdir, unlink, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { homedir } from 'node:os'
|
|
5
|
+
import { dirname, join, resolve } from 'node:path'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { spawnSync } from 'node:child_process'
|
|
8
|
+
|
|
9
|
+
const SERVICE_NAME = 'toc-ocra'
|
|
10
|
+
const LAUNCHD_LABEL = 'top.stormapex.toc-ocra'
|
|
11
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
12
|
+
const agentEntry = join(root, 'dist', 'src', 'index.js')
|
|
13
|
+
const action = process.argv[2]
|
|
14
|
+
const configPath = resolve(readOption('--config') ?? defaultConfigPath())
|
|
15
|
+
|
|
16
|
+
if (action !== 'install' && action !== 'uninstall') usage()
|
|
17
|
+
if (action === 'install' && !existsSync(agentEntry)) fail('Build output is missing. Run npm run build first.')
|
|
18
|
+
// The service may start unenrolled: it then waits to be paired from the plugin.
|
|
19
|
+
|
|
20
|
+
if (process.platform === 'darwin') await manageLaunchd(action)
|
|
21
|
+
else if (process.platform === 'linux') await manageSystemd(action)
|
|
22
|
+
else if (process.platform === 'win32') await manageWindowsTask(action)
|
|
23
|
+
else fail(`Unsupported platform: ${process.platform}`)
|
|
24
|
+
|
|
25
|
+
function readOption(name) {
|
|
26
|
+
const index = process.argv.indexOf(name)
|
|
27
|
+
return index >= 0 ? process.argv[index + 1] : undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function defaultDataDir() {
|
|
31
|
+
if (process.platform === 'darwin') return join(homedir(), 'Library', 'Application Support', SERVICE_NAME)
|
|
32
|
+
if (process.platform === 'win32') return join(process.env.APPDATA ?? homedir(), SERVICE_NAME)
|
|
33
|
+
return join(process.env.XDG_STATE_HOME ?? join(homedir(), '.local', 'state'), SERVICE_NAME)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function defaultConfigPath() {
|
|
37
|
+
return join(defaultDataDir(), 'config.json')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function manageLaunchd(mode) {
|
|
41
|
+
const target = join(homedir(), 'Library', 'LaunchAgents', `${LAUNCHD_LABEL}.plist`)
|
|
42
|
+
const domain = `gui/${process.getuid()}`
|
|
43
|
+
if (mode === 'uninstall') {
|
|
44
|
+
command('launchctl', ['bootout', domain, target], true)
|
|
45
|
+
await unlink(target).catch(ignoreMissing)
|
|
46
|
+
process.stdout.write(`Removed ${target}\n`)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const logDir = join(defaultDataDir(), 'logs')
|
|
51
|
+
const path = servicePath()
|
|
52
|
+
await mkdir(dirname(target), { recursive: true })
|
|
53
|
+
await mkdir(logDir, { recursive: true, mode: 0o700 })
|
|
54
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
55
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
56
|
+
<plist version="1.0"><dict>
|
|
57
|
+
<key>Label</key><string>${LAUNCHD_LABEL}</string>
|
|
58
|
+
<key>ProgramArguments</key><array>
|
|
59
|
+
<string>${xml(process.execPath)}</string><string>${xml(agentEntry)}</string><string>run</string>
|
|
60
|
+
</array>
|
|
61
|
+
<key>EnvironmentVariables</key><dict>
|
|
62
|
+
<key>TOC_OCRA_CONFIG</key><string>${xml(configPath)}</string>
|
|
63
|
+
<key>HOME</key><string>${xml(homedir())}</string>
|
|
64
|
+
<key>SHELL</key><string>${xml(process.env.SHELL ?? '/bin/zsh')}</string>
|
|
65
|
+
<key>PATH</key><string>${xml(path)}</string>
|
|
66
|
+
</dict>
|
|
67
|
+
<key>RunAtLoad</key><true/><key>KeepAlive</key><true/>
|
|
68
|
+
<key>StandardOutPath</key><string>${xml(join(logDir, 'service.stdout.log'))}</string>
|
|
69
|
+
<key>StandardErrorPath</key><string>${xml(join(logDir, 'service.stderr.log'))}</string>
|
|
70
|
+
</dict></plist>
|
|
71
|
+
`
|
|
72
|
+
await writeFile(target, plist, { mode: 0o600 })
|
|
73
|
+
command('launchctl', ['bootout', domain, target], true)
|
|
74
|
+
command('launchctl', ['bootstrap', domain, target])
|
|
75
|
+
command('launchctl', ['enable', `${domain}/${LAUNCHD_LABEL}`])
|
|
76
|
+
command('launchctl', ['kickstart', '-k', `${domain}/${LAUNCHD_LABEL}`])
|
|
77
|
+
process.stdout.write(`Installed ${target}\n`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function manageSystemd(mode) {
|
|
81
|
+
const target = join(homedir(), '.config', 'systemd', 'user', `${SERVICE_NAME}.service`)
|
|
82
|
+
if (mode === 'uninstall') {
|
|
83
|
+
command('systemctl', ['--user', 'disable', '--now', SERVICE_NAME], true)
|
|
84
|
+
await unlink(target).catch(ignoreMissing)
|
|
85
|
+
command('systemctl', ['--user', 'daemon-reload'])
|
|
86
|
+
process.stdout.write(`Removed ${target}\n`)
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await mkdir(dirname(target), { recursive: true })
|
|
91
|
+
const unit = `[Unit]
|
|
92
|
+
Description=echo_cli_agent TaskMCP device agent
|
|
93
|
+
After=network-online.target
|
|
94
|
+
Wants=network-online.target
|
|
95
|
+
|
|
96
|
+
[Service]
|
|
97
|
+
Type=simple
|
|
98
|
+
Environment=TOC_OCRA_CONFIG=${systemdQuote(configPath)}
|
|
99
|
+
Environment=PATH=${systemdQuote(servicePath())}
|
|
100
|
+
ExecStart=${systemdQuote(process.execPath)} ${systemdQuote(agentEntry)} run
|
|
101
|
+
Restart=always
|
|
102
|
+
RestartSec=3
|
|
103
|
+
NoNewPrivileges=true
|
|
104
|
+
|
|
105
|
+
[Install]
|
|
106
|
+
WantedBy=default.target
|
|
107
|
+
`
|
|
108
|
+
await writeFile(target, unit, { mode: 0o600 })
|
|
109
|
+
command('systemctl', ['--user', 'daemon-reload'])
|
|
110
|
+
command('systemctl', ['--user', 'enable', '--now', SERVICE_NAME])
|
|
111
|
+
process.stdout.write(`Installed ${target}\n`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function manageWindowsTask(mode) {
|
|
115
|
+
const launcher = join(defaultDataDir(), 'run-agent.cmd')
|
|
116
|
+
if (mode === 'uninstall') {
|
|
117
|
+
command('schtasks.exe', ['/Delete', '/F', '/TN', SERVICE_NAME], true)
|
|
118
|
+
await unlink(launcher).catch(ignoreMissing)
|
|
119
|
+
process.stdout.write(`Removed scheduled task ${SERVICE_NAME}\n`)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
rejectCmdMetacharacters(configPath)
|
|
124
|
+
rejectCmdMetacharacters(process.execPath)
|
|
125
|
+
rejectCmdMetacharacters(agentEntry)
|
|
126
|
+
await mkdir(dirname(launcher), { recursive: true })
|
|
127
|
+
await writeFile(launcher, `@echo off\r\nset "TOC_OCRA_CONFIG=${configPath}"\r\n"${process.execPath}" "${agentEntry}" run\r\n`, { mode: 0o600 })
|
|
128
|
+
await chmod(launcher, 0o700).catch(() => undefined)
|
|
129
|
+
command('schtasks.exe', ['/Create', '/F', '/TN', SERVICE_NAME, '/SC', 'ONLOGON', '/RL', 'LIMITED', '/TR', `"${launcher}"`])
|
|
130
|
+
command('schtasks.exe', ['/Run', '/TN', SERVICE_NAME])
|
|
131
|
+
process.stdout.write(`Installed scheduled task ${SERVICE_NAME}\n`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function command(executable, args, allowFailure = false) {
|
|
135
|
+
const result = spawnSync(executable, args, { stdio: 'inherit', shell: false })
|
|
136
|
+
if (!allowFailure && result.status !== 0) fail(`${executable} failed with exit code ${result.status ?? 'unknown'}`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function xml(value) {
|
|
140
|
+
return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function servicePath() {
|
|
144
|
+
const entries = [
|
|
145
|
+
dirname(process.execPath),
|
|
146
|
+
join(homedir(), '.local', 'bin'),
|
|
147
|
+
'/opt/homebrew/bin',
|
|
148
|
+
'/usr/local/bin',
|
|
149
|
+
process.env.PATH ?? ''
|
|
150
|
+
]
|
|
151
|
+
return [...new Set(entries.flatMap((entry) => entry.split(':')).filter(Boolean))].join(':')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function systemdQuote(value) {
|
|
155
|
+
return `"${value.replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('%', '%%')}"`
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function rejectCmdMetacharacters(value) {
|
|
159
|
+
if (/[\r\n&|<>^]/.test(value)) fail('Windows service paths contain unsupported command characters')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function ignoreMissing(error) {
|
|
163
|
+
if (error?.code !== 'ENOENT') throw error
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function usage() {
|
|
167
|
+
process.stderr.write('Usage: node scripts/service.mjs <install|uninstall> [--config /path/config.json]\n')
|
|
168
|
+
process.exit(2)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function fail(message) {
|
|
172
|
+
process.stderr.write(`${message}\n`)
|
|
173
|
+
process.exit(1)
|
|
174
|
+
}
|