@swarmclawai/swarmclaw 1.0.0 → 1.0.3
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 +15 -2
- package/bin/doctor-cmd.js +155 -0
- package/bin/doctor-cmd.test.js +50 -0
- package/bin/install-root.js +39 -0
- package/bin/install-root.test.js +60 -0
- package/bin/server-cmd.js +160 -38
- package/bin/swarmclaw.js +83 -3
- package/bin/update-cmd.js +1 -6
- package/bin/update-cmd.test.js +1 -36
- package/bin/worker-cmd.js +13 -6
- package/package.json +16 -15
- package/scripts/postinstall.mjs +17 -13
- package/src/app/api/gateways/[id]/health/route.ts +2 -32
- package/src/app/api/gateways/health-route.test.ts +1 -1
- package/src/app/api/setup/check-provider/helpers.ts +28 -0
- package/src/app/api/setup/check-provider/route.test.ts +1 -1
- package/src/app/api/setup/check-provider/route.ts +5 -32
- package/src/app/api/tasks/import/github/helpers.ts +100 -0
- package/src/app/api/tasks/import/github/route.test.ts +1 -1
- package/src/app/api/tasks/import/github/route.ts +2 -92
- package/src/app/api/webhooks/[id]/helpers.ts +253 -0
- package/src/app/api/webhooks/[id]/route.ts +2 -243
- package/src/app/api/webhooks/route.test.ts +4 -2
- package/src/app/usage/page.tsx +22 -12
- package/src/cli/binary.test.js +57 -0
- package/src/cli/index.js +13 -1
- package/src/cli/server-cmd.test.js +77 -0
- package/src/lib/server/data-dir.test.ts +38 -3
- package/src/lib/server/data-dir.ts +11 -0
- package/src/lib/server/openclaw/health.ts +30 -1
- package/src/lib/server/session-tools/file-send.test.ts +18 -2
- package/src/lib/server/session-tools/file.ts +11 -7
- package/src/lib/server/skills/skill-discovery.test.ts +34 -1
- package/src/lib/server/skills/skill-discovery.ts +9 -4
package/bin/server-cmd.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
const fs = require('node:fs')
|
|
6
6
|
const path = require('node:path')
|
|
7
7
|
const { spawn, execFileSync } = require('node:child_process')
|
|
8
|
-
const os = require('node:os')
|
|
9
8
|
const {
|
|
10
9
|
detectPackageManager,
|
|
11
10
|
getInstallCommand,
|
|
@@ -13,21 +12,31 @@ const {
|
|
|
13
12
|
const {
|
|
14
13
|
readPackageVersion,
|
|
15
14
|
resolvePackageRoot,
|
|
15
|
+
resolveStateHome,
|
|
16
16
|
} = require('./install-root.js')
|
|
17
17
|
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
// Paths
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
|
|
22
|
-
const SWARMCLAW_HOME = process.env.SWARMCLAW_HOME || path.join(os.homedir(), '.swarmclaw')
|
|
23
22
|
const PKG_ROOT = resolvePackageRoot({
|
|
24
23
|
moduleDir: __dirname,
|
|
25
24
|
argv1: process.argv[1],
|
|
26
25
|
cwd: process.cwd(),
|
|
27
26
|
})
|
|
27
|
+
const SWARMCLAW_HOME = resolveStateHome({
|
|
28
|
+
pkgRoot: PKG_ROOT,
|
|
29
|
+
moduleDir: __dirname,
|
|
30
|
+
argv1: process.argv[1],
|
|
31
|
+
cwd: process.cwd(),
|
|
32
|
+
env: process.env,
|
|
33
|
+
})
|
|
28
34
|
const PID_FILE = path.join(SWARMCLAW_HOME, 'server.pid')
|
|
29
35
|
const LOG_FILE = path.join(SWARMCLAW_HOME, 'server.log')
|
|
30
36
|
const DATA_DIR = path.join(SWARMCLAW_HOME, 'data')
|
|
37
|
+
const WORKSPACE_DIR = path.join(SWARMCLAW_HOME, 'workspace')
|
|
38
|
+
const BROWSER_PROFILES_DIR = path.join(SWARMCLAW_HOME, 'browser-profiles')
|
|
39
|
+
const BUILD_WORKSPACES_DIR = path.join(SWARMCLAW_HOME, 'builds')
|
|
31
40
|
|
|
32
41
|
// ---------------------------------------------------------------------------
|
|
33
42
|
// Helpers
|
|
@@ -67,19 +76,120 @@ function resolveStandaloneBase(pkgRoot = PKG_ROOT) {
|
|
|
67
76
|
return path.join(pkgRoot, '.next', 'standalone')
|
|
68
77
|
}
|
|
69
78
|
|
|
79
|
+
function isGitCheckout(pkgRoot = PKG_ROOT) {
|
|
80
|
+
return fs.existsSync(path.join(pkgRoot, '.git'))
|
|
81
|
+
}
|
|
82
|
+
|
|
70
83
|
function getVersion() {
|
|
71
84
|
return readPackageVersion(PKG_ROOT) || 'unknown'
|
|
72
85
|
}
|
|
73
86
|
|
|
87
|
+
function resolveInstalledNext(pkgRoot = PKG_ROOT) {
|
|
88
|
+
try {
|
|
89
|
+
const nextPackageJson = require.resolve('next/package.json', { paths: [pkgRoot] })
|
|
90
|
+
const nextPackageDir = path.dirname(nextPackageJson)
|
|
91
|
+
return {
|
|
92
|
+
nextCli: path.join(nextPackageDir, 'dist', 'bin', 'next'),
|
|
93
|
+
nodeModulesDir: path.dirname(nextPackageDir),
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
74
100
|
function ensurePackageDependencies(pkgRoot = PKG_ROOT) {
|
|
75
|
-
const
|
|
76
|
-
if (fs.existsSync(nextCli)) return
|
|
101
|
+
const resolved = resolveInstalledNext(pkgRoot)
|
|
102
|
+
if (resolved && fs.existsSync(resolved.nextCli)) return resolved
|
|
77
103
|
|
|
78
104
|
const packageManager = detectPackageManager(pkgRoot, process.env)
|
|
79
105
|
const install = getInstallCommand(packageManager)
|
|
80
106
|
log(`Installing dependencies with ${packageManager}...`)
|
|
81
107
|
execFileSync(install.command, install.args, { cwd: pkgRoot, stdio: 'inherit' })
|
|
82
|
-
|
|
108
|
+
|
|
109
|
+
const installed = resolveInstalledNext(pkgRoot)
|
|
110
|
+
if (installed && fs.existsSync(installed.nextCli)) return installed
|
|
111
|
+
|
|
112
|
+
throw new Error('Next.js CLI was not found after installing dependencies.')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function resolvePackageBuildRoot(pkgRoot = PKG_ROOT) {
|
|
116
|
+
if (isGitCheckout(pkgRoot)) return pkgRoot
|
|
117
|
+
const version = readPackageVersion(pkgRoot) || 'unknown'
|
|
118
|
+
return path.join(BUILD_WORKSPACES_DIR, `package-${version}`)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function copyBuildWorkspaceContents(sourceRoot, targetRoot) {
|
|
122
|
+
const excluded = new Set([
|
|
123
|
+
'.git',
|
|
124
|
+
'.next',
|
|
125
|
+
'data',
|
|
126
|
+
'node_modules',
|
|
127
|
+
])
|
|
128
|
+
|
|
129
|
+
ensureDir(targetRoot)
|
|
130
|
+
|
|
131
|
+
for (const entry of fs.readdirSync(sourceRoot, { withFileTypes: true })) {
|
|
132
|
+
if (excluded.has(entry.name)) continue
|
|
133
|
+
|
|
134
|
+
const sourcePath = path.join(sourceRoot, entry.name)
|
|
135
|
+
const targetPath = path.join(targetRoot, entry.name)
|
|
136
|
+
fs.rmSync(targetPath, { recursive: true, force: true })
|
|
137
|
+
fs.cpSync(sourcePath, targetPath, {
|
|
138
|
+
recursive: true,
|
|
139
|
+
force: true,
|
|
140
|
+
dereference: true,
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function symlinkDir(targetPath, linkPath) {
|
|
146
|
+
fs.rmSync(linkPath, { recursive: true, force: true })
|
|
147
|
+
fs.symlinkSync(targetPath, linkPath, process.platform === 'win32' ? 'junction' : 'dir')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function prepareBuildWorkspace({ pkgRoot = PKG_ROOT, buildRoot = resolvePackageBuildRoot(pkgRoot), nodeModulesDir } = {}) {
|
|
151
|
+
copyBuildWorkspaceContents(pkgRoot, buildRoot)
|
|
152
|
+
symlinkDir(nodeModulesDir, path.join(buildRoot, 'node_modules'))
|
|
153
|
+
return buildRoot
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function resolveStandaloneCandidateRoots(pkgRoot = PKG_ROOT) {
|
|
157
|
+
const roots = [pkgRoot]
|
|
158
|
+
const buildRoot = resolvePackageBuildRoot(pkgRoot)
|
|
159
|
+
if (buildRoot !== pkgRoot) roots.push(buildRoot)
|
|
160
|
+
return roots
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function locateStandaloneServer({ pkgRoot = PKG_ROOT } = {}) {
|
|
164
|
+
for (const root of resolveStandaloneCandidateRoots(pkgRoot)) {
|
|
165
|
+
const standaloneBase = resolveStandaloneBase(root)
|
|
166
|
+
if (!fs.existsSync(standaloneBase)) continue
|
|
167
|
+
|
|
168
|
+
const direct = path.join(standaloneBase, 'server.js')
|
|
169
|
+
if (fs.existsSync(direct)) {
|
|
170
|
+
return { root, serverJs: direct }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function search(dir) {
|
|
174
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
const full = path.join(dir, entry.name)
|
|
177
|
+
if (entry.isFile() && entry.name === 'server.js') return full
|
|
178
|
+
if (entry.isDirectory() && entry.name !== 'node_modules') {
|
|
179
|
+
const found = search(full)
|
|
180
|
+
if (found) return found
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return null
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const nested = search(standaloneBase)
|
|
187
|
+
if (nested) {
|
|
188
|
+
return { root, serverJs: nested }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return null
|
|
83
193
|
}
|
|
84
194
|
|
|
85
195
|
// ---------------------------------------------------------------------------
|
|
@@ -96,14 +206,21 @@ function runBuild({ pkgRoot = PKG_ROOT } = {}) {
|
|
|
96
206
|
ensureDir(SWARMCLAW_HOME)
|
|
97
207
|
ensureDir(DATA_DIR)
|
|
98
208
|
|
|
99
|
-
const nextCli = ensurePackageDependencies(pkgRoot)
|
|
209
|
+
const { nextCli, nodeModulesDir } = ensurePackageDependencies(pkgRoot)
|
|
210
|
+
const buildRoot = resolvePackageBuildRoot(pkgRoot)
|
|
211
|
+
|
|
212
|
+
if (buildRoot !== pkgRoot) {
|
|
213
|
+
prepareBuildWorkspace({ pkgRoot, buildRoot, nodeModulesDir })
|
|
214
|
+
log(`Using build workspace: ${buildRoot}`)
|
|
215
|
+
}
|
|
100
216
|
|
|
101
217
|
log('Building Next.js application (this may take a minute)...')
|
|
102
|
-
execFileSync(process.execPath, [nextCli, 'build'], {
|
|
103
|
-
cwd:
|
|
218
|
+
execFileSync(process.execPath, [nextCli, 'build', '--webpack'], {
|
|
219
|
+
cwd: buildRoot,
|
|
104
220
|
stdio: 'inherit',
|
|
105
221
|
env: {
|
|
106
222
|
...process.env,
|
|
223
|
+
SWARMCLAW_HOME,
|
|
107
224
|
DATA_DIR,
|
|
108
225
|
SWARMCLAW_BUILD_MODE: '1',
|
|
109
226
|
},
|
|
@@ -117,29 +234,7 @@ function runBuild({ pkgRoot = PKG_ROOT } = {}) {
|
|
|
117
234
|
// ---------------------------------------------------------------------------
|
|
118
235
|
|
|
119
236
|
function findStandaloneServer({ pkgRoot = PKG_ROOT } = {}) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (!fs.existsSync(standaloneBase)) {
|
|
123
|
-
return null
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const direct = path.join(standaloneBase, 'server.js')
|
|
127
|
-
if (fs.existsSync(direct)) return direct
|
|
128
|
-
|
|
129
|
-
function search(dir) {
|
|
130
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
131
|
-
for (const entry of entries) {
|
|
132
|
-
const full = path.join(dir, entry.name)
|
|
133
|
-
if (entry.isFile() && entry.name === 'server.js') return full
|
|
134
|
-
if (entry.isDirectory() && entry.name !== 'node_modules') {
|
|
135
|
-
const found = search(full)
|
|
136
|
-
if (found) return found
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return null
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return search(standaloneBase)
|
|
237
|
+
return locateStandaloneServer({ pkgRoot })?.serverJs || null
|
|
143
238
|
}
|
|
144
239
|
|
|
145
240
|
// ---------------------------------------------------------------------------
|
|
@@ -147,11 +242,12 @@ function findStandaloneServer({ pkgRoot = PKG_ROOT } = {}) {
|
|
|
147
242
|
// ---------------------------------------------------------------------------
|
|
148
243
|
|
|
149
244
|
function startServer(opts, { pkgRoot = PKG_ROOT } = {}) {
|
|
150
|
-
const
|
|
151
|
-
if (!
|
|
245
|
+
const standalone = locateStandaloneServer({ pkgRoot })
|
|
246
|
+
if (!standalone) {
|
|
152
247
|
logError('Standalone server.js not found in the installed package. Try running: swarmclaw server --build')
|
|
153
248
|
process.exit(1)
|
|
154
249
|
}
|
|
250
|
+
const { root: runtimeRoot, serverJs } = standalone
|
|
155
251
|
|
|
156
252
|
ensureDir(SWARMCLAW_HOME)
|
|
157
253
|
ensureDir(DATA_DIR)
|
|
@@ -162,7 +258,10 @@ function startServer(opts, { pkgRoot = PKG_ROOT } = {}) {
|
|
|
162
258
|
|
|
163
259
|
const env = {
|
|
164
260
|
...process.env,
|
|
261
|
+
SWARMCLAW_HOME,
|
|
165
262
|
DATA_DIR,
|
|
263
|
+
WORKSPACE_DIR,
|
|
264
|
+
BROWSER_PROFILES_DIR,
|
|
166
265
|
HOSTNAME: host,
|
|
167
266
|
PORT: port,
|
|
168
267
|
WS_PORT: wsPort,
|
|
@@ -170,12 +269,14 @@ function startServer(opts, { pkgRoot = PKG_ROOT } = {}) {
|
|
|
170
269
|
|
|
171
270
|
log(`Starting server on ${host}:${port} (WebSocket: ${wsPort})...`)
|
|
172
271
|
log(`Package root: ${pkgRoot}`)
|
|
272
|
+
log(`Runtime root: ${runtimeRoot}`)
|
|
273
|
+
log(`Home: ${SWARMCLAW_HOME}`)
|
|
173
274
|
log(`Data directory: ${DATA_DIR}`)
|
|
174
275
|
|
|
175
276
|
if (opts.detach) {
|
|
176
277
|
const logStream = fs.openSync(LOG_FILE, 'a')
|
|
177
278
|
const child = spawn(process.execPath, [serverJs], {
|
|
178
|
-
cwd:
|
|
279
|
+
cwd: runtimeRoot,
|
|
179
280
|
detached: true,
|
|
180
281
|
env,
|
|
181
282
|
stdio: ['ignore', logStream, logStream],
|
|
@@ -188,7 +289,7 @@ function startServer(opts, { pkgRoot = PKG_ROOT } = {}) {
|
|
|
188
289
|
process.exit(0)
|
|
189
290
|
} else {
|
|
190
291
|
const child = spawn(process.execPath, [serverJs], {
|
|
191
|
-
cwd:
|
|
292
|
+
cwd: runtimeRoot,
|
|
192
293
|
env,
|
|
193
294
|
stdio: 'inherit',
|
|
194
295
|
})
|
|
@@ -246,8 +347,11 @@ function showStatus() {
|
|
|
246
347
|
}
|
|
247
348
|
|
|
248
349
|
log(`Package: ${PKG_ROOT}`)
|
|
350
|
+
log(`Build workspace: ${resolvePackageBuildRoot()}`)
|
|
249
351
|
log(`Home: ${SWARMCLAW_HOME}`)
|
|
250
352
|
log(`Data: ${DATA_DIR}`)
|
|
353
|
+
log(`Workspace: ${WORKSPACE_DIR}`)
|
|
354
|
+
log(`Browser profiles: ${BROWSER_PROFILES_DIR}`)
|
|
251
355
|
log(`WebSocket port: ${process.env.WS_PORT || '(PORT + 1)'}`)
|
|
252
356
|
|
|
253
357
|
const serverJs = findStandaloneServer()
|
|
@@ -282,8 +386,7 @@ Options:
|
|
|
282
386
|
console.log(help)
|
|
283
387
|
}
|
|
284
388
|
|
|
285
|
-
function main() {
|
|
286
|
-
const args = process.argv.slice(3)
|
|
389
|
+
function main(args = process.argv.slice(3)) {
|
|
287
390
|
let command = 'start'
|
|
288
391
|
let forceBuild = false
|
|
289
392
|
let detach = false
|
|
@@ -330,7 +433,17 @@ function main() {
|
|
|
330
433
|
}
|
|
331
434
|
|
|
332
435
|
if (needsBuild(forceBuild)) {
|
|
333
|
-
|
|
436
|
+
if (!forceBuild) {
|
|
437
|
+
const installKind = isGitCheckout() ? 'checkout' : 'installed package'
|
|
438
|
+
log(`Standalone server bundle not found in this ${installKind}. Building locally...`)
|
|
439
|
+
}
|
|
440
|
+
try {
|
|
441
|
+
runBuild()
|
|
442
|
+
} catch (err) {
|
|
443
|
+
logError(`Build failed: ${err.message}`)
|
|
444
|
+
logError('Retry manually with: swarmclaw server --build')
|
|
445
|
+
process.exit(1)
|
|
446
|
+
}
|
|
334
447
|
}
|
|
335
448
|
|
|
336
449
|
startServer({ port, wsPort, host, detach })
|
|
@@ -342,12 +455,21 @@ if (require.main === module) {
|
|
|
342
455
|
|
|
343
456
|
module.exports = {
|
|
344
457
|
DATA_DIR,
|
|
458
|
+
BUILD_WORKSPACES_DIR,
|
|
459
|
+
BROWSER_PROFILES_DIR,
|
|
345
460
|
PKG_ROOT,
|
|
346
461
|
SWARMCLAW_HOME,
|
|
462
|
+
WORKSPACE_DIR,
|
|
347
463
|
findStandaloneServer,
|
|
348
464
|
getVersion,
|
|
465
|
+
isGitCheckout,
|
|
466
|
+
locateStandaloneServer,
|
|
349
467
|
main,
|
|
350
468
|
needsBuild,
|
|
469
|
+
prepareBuildWorkspace,
|
|
470
|
+
resolveInstalledNext,
|
|
471
|
+
resolvePackageBuildRoot,
|
|
472
|
+
resolveStandaloneCandidateRoots,
|
|
351
473
|
resolveStandaloneBase,
|
|
352
474
|
runBuild,
|
|
353
475
|
}
|
package/bin/swarmclaw.js
CHANGED
|
@@ -96,6 +96,19 @@ function normalizeLegacyCliEnv(env) {
|
|
|
96
96
|
return nextEnv
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
function printPackageVersion() {
|
|
100
|
+
const pkg = require('../package.json')
|
|
101
|
+
process.stdout.write(`${pkg.name || 'swarmclaw'} ${pkg.version || '0.0.0'}\n`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function printVersionHelp() {
|
|
105
|
+
process.stdout.write(`
|
|
106
|
+
Usage: swarmclaw version
|
|
107
|
+
|
|
108
|
+
Show the installed SwarmClaw package version.
|
|
109
|
+
`.trim() + '\n')
|
|
110
|
+
}
|
|
111
|
+
|
|
99
112
|
async function runMappedCli(argv) {
|
|
100
113
|
const cliPath = path.join(__dirname, '..', 'src', 'cli', 'index.js')
|
|
101
114
|
const cliModule = await import(cliPath)
|
|
@@ -106,25 +119,91 @@ async function runMappedCli(argv) {
|
|
|
106
119
|
return runCli(argv)
|
|
107
120
|
}
|
|
108
121
|
|
|
122
|
+
async function runHelp(argv) {
|
|
123
|
+
const [target, ...rest] = argv
|
|
124
|
+
if (!target) {
|
|
125
|
+
const code = await runMappedCli(['--help'])
|
|
126
|
+
process.exitCode = typeof code === 'number' ? code : 1
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (target === 'run' || target === 'start' || target === 'stop' || target === 'status' || target === 'server') {
|
|
131
|
+
require('./server-cmd.js').main(['--help'])
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
if (target === 'worker') {
|
|
135
|
+
require('./worker-cmd.js').main(['--help'])
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
if (target === 'doctor') {
|
|
139
|
+
require('./doctor-cmd.js').main(['--help'])
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
if (target === 'update') {
|
|
143
|
+
require('./update-cmd.js').main(['--help'])
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
if (target === 'version') {
|
|
147
|
+
printVersionHelp()
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const forwarded = rest.includes('--help') || rest.includes('-h')
|
|
152
|
+
? [target, ...rest]
|
|
153
|
+
: [target, ...rest, '--help']
|
|
154
|
+
const code = shouldUseLegacyTsCli(forwarded)
|
|
155
|
+
? runLegacyTsCli(forwarded)
|
|
156
|
+
: await runMappedCli(forwarded)
|
|
157
|
+
|
|
158
|
+
process.exitCode = typeof code === 'number' ? code : 1
|
|
159
|
+
}
|
|
160
|
+
|
|
109
161
|
async function main() {
|
|
110
162
|
const argv = process.argv.slice(2)
|
|
111
163
|
const top = argv[0]
|
|
112
164
|
|
|
113
165
|
// Default to 'server' when invoked with no arguments.
|
|
114
166
|
if (!top) {
|
|
115
|
-
require('./server-cmd.js').main()
|
|
167
|
+
require('./server-cmd.js').main([])
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (top === '-v') {
|
|
172
|
+
printPackageVersion()
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (top === 'version' && argv.length === 1) {
|
|
177
|
+
printPackageVersion()
|
|
116
178
|
return
|
|
117
179
|
}
|
|
118
180
|
|
|
119
|
-
|
|
181
|
+
if (top === 'help') {
|
|
182
|
+
await runHelp(argv.slice(1))
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Route local lifecycle/maintenance commands to CJS scripts (no TS dependency).
|
|
120
187
|
if (top === 'server') {
|
|
121
|
-
require('./server-cmd.js').main()
|
|
188
|
+
require('./server-cmd.js').main(argv.slice(1))
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
if (top === 'run' || top === 'start') {
|
|
192
|
+
require('./server-cmd.js').main(argv.slice(1))
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
if (top === 'status' || top === 'stop') {
|
|
196
|
+
require('./server-cmd.js').main([top, ...argv.slice(1)])
|
|
122
197
|
return
|
|
123
198
|
}
|
|
124
199
|
if (top === 'worker') {
|
|
125
200
|
require('./worker-cmd.js').main()
|
|
126
201
|
return
|
|
127
202
|
}
|
|
203
|
+
if (top === 'doctor') {
|
|
204
|
+
require('./doctor-cmd.js').main(argv.slice(1))
|
|
205
|
+
return
|
|
206
|
+
}
|
|
128
207
|
if (top === 'update') {
|
|
129
208
|
require('./update-cmd.js').main()
|
|
130
209
|
return
|
|
@@ -146,6 +225,7 @@ module.exports = {
|
|
|
146
225
|
hasTsxRuntime,
|
|
147
226
|
TS_CLI_ACTIONS,
|
|
148
227
|
normalizeLegacyCliEnv,
|
|
228
|
+
printPackageVersion,
|
|
149
229
|
supportsStripTypes,
|
|
150
230
|
shouldUseLegacyTsCli,
|
|
151
231
|
}
|
package/bin/update-cmd.js
CHANGED
|
@@ -75,7 +75,6 @@ function runRegistrySelfUpdate(
|
|
|
75
75
|
packageManager = resolveRegistryPackageManager(),
|
|
76
76
|
execImpl = execFileSync,
|
|
77
77
|
logger = { log, logError },
|
|
78
|
-
rebuildImpl = execFileSync,
|
|
79
78
|
) {
|
|
80
79
|
const update = getGlobalUpdateSpec(packageManager, PACKAGE_NAME)
|
|
81
80
|
logger.log(`No git checkout detected. Updating the global ${PACKAGE_NAME} install via ${packageManager}...`)
|
|
@@ -92,15 +91,11 @@ function runRegistrySelfUpdate(
|
|
|
92
91
|
return 1
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
const rebuildExitCode = rebuildStandaloneServer(rebuildImpl, logger)
|
|
96
|
-
if (rebuildExitCode !== 0) return rebuildExitCode
|
|
97
|
-
|
|
98
94
|
logger.log('Restart the server to apply changes: swarmclaw server stop && swarmclaw server start')
|
|
99
95
|
return 0
|
|
100
96
|
}
|
|
101
97
|
|
|
102
|
-
function main() {
|
|
103
|
-
const args = process.argv.slice(3)
|
|
98
|
+
function main(args = process.argv.slice(3)) {
|
|
104
99
|
if (args.includes('-h') || args.includes('--help')) {
|
|
105
100
|
console.log(`
|
|
106
101
|
Usage: swarmclaw update
|
package/bin/update-cmd.test.js
CHANGED
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
const test = require('node:test')
|
|
5
5
|
const assert = require('node:assert/strict')
|
|
6
|
-
const path = require('node:path')
|
|
7
6
|
|
|
8
7
|
const { runRegistrySelfUpdate } = require('./update-cmd.js')
|
|
9
8
|
|
|
10
|
-
test('runRegistrySelfUpdate executes the manager-specific global update command
|
|
9
|
+
test('runRegistrySelfUpdate executes the manager-specific global update command', () => {
|
|
11
10
|
const messages = []
|
|
12
11
|
const captured = []
|
|
13
12
|
|
|
@@ -20,9 +19,6 @@ test('runRegistrySelfUpdate executes the manager-specific global update command
|
|
|
20
19
|
log: (message) => messages.push(`log:${message}`),
|
|
21
20
|
logError: (message) => messages.push(`err:${message}`),
|
|
22
21
|
},
|
|
23
|
-
(command, args, options) => {
|
|
24
|
-
captured.push({ command, args, options })
|
|
25
|
-
},
|
|
26
22
|
)
|
|
27
23
|
|
|
28
24
|
assert.equal(exitCode, 0)
|
|
@@ -36,20 +32,9 @@ test('runRegistrySelfUpdate executes the manager-specific global update command
|
|
|
36
32
|
timeout: 120_000,
|
|
37
33
|
},
|
|
38
34
|
},
|
|
39
|
-
{
|
|
40
|
-
command: process.execPath,
|
|
41
|
-
args: [path.join(process.cwd(), 'bin', 'server-cmd.js'), '--build'],
|
|
42
|
-
options: {
|
|
43
|
-
cwd: process.cwd(),
|
|
44
|
-
stdio: 'inherit',
|
|
45
|
-
timeout: 600_000,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
35
|
])
|
|
49
36
|
assert.match(messages.join('\n'), /updating the global @swarmclawai\/swarmclaw install via pnpm/i)
|
|
50
37
|
assert.match(messages.join('\n'), /global update complete via pnpm/i)
|
|
51
|
-
assert.match(messages.join('\n'), /rebuilding the standalone server bundle/i)
|
|
52
|
-
assert.match(messages.join('\n'), /standalone server bundle rebuilt/i)
|
|
53
38
|
})
|
|
54
39
|
|
|
55
40
|
test('runRegistrySelfUpdate reports a manual retry command when the registry update fails', () => {
|
|
@@ -70,23 +55,3 @@ test('runRegistrySelfUpdate reports a manual retry command when the registry upd
|
|
|
70
55
|
assert.match(messages.join('\n'), /registry update failed: spawn bun ENOENT/i)
|
|
71
56
|
assert.match(messages.join('\n'), /retry manually with: bun add -g @swarmclawai\/swarmclaw@latest/i)
|
|
72
57
|
})
|
|
73
|
-
|
|
74
|
-
test('runRegistrySelfUpdate reports a manual rebuild command when the rebuild step fails', () => {
|
|
75
|
-
const messages = []
|
|
76
|
-
|
|
77
|
-
const exitCode = runRegistrySelfUpdate(
|
|
78
|
-
'npm',
|
|
79
|
-
() => {},
|
|
80
|
-
{
|
|
81
|
-
log: (message) => messages.push(`log:${message}`),
|
|
82
|
-
logError: (message) => messages.push(`err:${message}`),
|
|
83
|
-
},
|
|
84
|
-
() => {
|
|
85
|
-
throw new Error('build failed')
|
|
86
|
-
},
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
assert.equal(exitCode, 1)
|
|
90
|
-
assert.match(messages.join('\n'), /standalone rebuild failed: build failed/i)
|
|
91
|
-
assert.match(messages.join('\n'), /retry manually with: swarmclaw server --build/i)
|
|
92
|
-
})
|
package/bin/worker-cmd.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
const { spawn } = require('node:child_process')
|
|
5
5
|
|
|
6
6
|
const {
|
|
7
|
+
BROWSER_PROFILES_DIR,
|
|
7
8
|
DATA_DIR,
|
|
8
9
|
PKG_ROOT,
|
|
9
10
|
SWARMCLAW_HOME,
|
|
10
|
-
|
|
11
|
+
WORKSPACE_DIR,
|
|
12
|
+
locateStandaloneServer,
|
|
11
13
|
} = require('./server-cmd.js')
|
|
12
14
|
|
|
13
15
|
function printHelp() {
|
|
@@ -23,8 +25,7 @@ Options:
|
|
|
23
25
|
console.log(help)
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
function main() {
|
|
27
|
-
const args = process.argv.slice(3)
|
|
28
|
+
function main(args = process.argv.slice(3)) {
|
|
28
29
|
for (const arg of args) {
|
|
29
30
|
if (arg === '-h' || arg === '--help') {
|
|
30
31
|
printHelp()
|
|
@@ -36,7 +37,10 @@ function main() {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
process.env.SWARMCLAW_HOME = SWARMCLAW_HOME
|
|
39
41
|
process.env.DATA_DIR = DATA_DIR
|
|
42
|
+
process.env.WORKSPACE_DIR = WORKSPACE_DIR
|
|
43
|
+
process.env.BROWSER_PROFILES_DIR = BROWSER_PROFILES_DIR
|
|
40
44
|
process.env.SWARMCLAW_DAEMON_BACKGROUND_SERVICES = '1'
|
|
41
45
|
process.env.SWARMCLAW_WORKER_ONLY = '1'
|
|
42
46
|
|
|
@@ -44,15 +48,18 @@ function main() {
|
|
|
44
48
|
console.log(`[swarmclaw] Package root: ${PKG_ROOT}`)
|
|
45
49
|
console.log(`[swarmclaw] Home: ${SWARMCLAW_HOME}`)
|
|
46
50
|
console.log(`[swarmclaw] Data directory: ${DATA_DIR}`)
|
|
51
|
+
console.log(`[swarmclaw] Workspace directory: ${WORKSPACE_DIR}`)
|
|
52
|
+
console.log(`[swarmclaw] Browser profiles: ${BROWSER_PROFILES_DIR}`)
|
|
47
53
|
|
|
48
|
-
const
|
|
49
|
-
if (!
|
|
54
|
+
const standalone = locateStandaloneServer()
|
|
55
|
+
if (!standalone) {
|
|
50
56
|
console.error('[swarmclaw] Standalone server.js not found in the installed package. Try running: swarmclaw server --build')
|
|
51
57
|
process.exit(1)
|
|
52
58
|
}
|
|
59
|
+
const { root: runtimeRoot, serverJs } = standalone
|
|
53
60
|
|
|
54
61
|
const child = spawn(process.execPath, [serverJs], {
|
|
55
|
-
cwd:
|
|
62
|
+
cwd: runtimeRoot,
|
|
56
63
|
env: process.env,
|
|
57
64
|
stdio: 'inherit',
|
|
58
65
|
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmclawai/swarmclaw",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Self-hosted AI agent orchestration dashboard
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Self-hosted AI agent orchestration dashboard with OpenClaw integration, multi-provider support, LangGraph workflows, and chat platform connectors.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"dev": "next dev --turbopack --hostname 0.0.0.0 -p 3456",
|
|
52
52
|
"dev:webpack": "next dev --webpack --hostname 0.0.0.0 -p 3456",
|
|
53
53
|
"dev:clean": "rm -rf .next && next dev --turbopack --hostname 0.0.0.0 -p 3456",
|
|
54
|
-
"build": "next build",
|
|
55
|
-
"build:ci": "NEXT_DISABLE_ESLINT=1 next build",
|
|
54
|
+
"build": "next build --webpack",
|
|
55
|
+
"build:ci": "NEXT_DISABLE_ESLINT=1 next build --webpack",
|
|
56
56
|
"start": "node .next/standalone/server.js",
|
|
57
57
|
"start:standalone": "node .next/standalone/server.js",
|
|
58
58
|
"smoke:browser": "node ./scripts/browser-route-smoke.mjs",
|
|
@@ -65,10 +65,11 @@
|
|
|
65
65
|
"lint:baseline": "node ./scripts/lint-baseline.mjs check",
|
|
66
66
|
"lint:baseline:update": "node ./scripts/lint-baseline.mjs update",
|
|
67
67
|
"cli": "node ./bin/swarmclaw.js",
|
|
68
|
-
"test:cli": "node --test src/cli/*.test.js bin/*.test.js",
|
|
68
|
+
"test:cli": "node --test src/cli/*.test.js bin/*.test.js scripts/postinstall.test.mjs",
|
|
69
69
|
"test:setup": "tsx --test src/app/api/setup/check-provider/route.test.ts src/lib/server/provider-model-discovery.test.ts src/components/auth/setup-wizard/utils.test.ts src/components/auth/setup-wizard/types.test.ts src/hooks/setup-done-detection.test.ts src/lib/setup-defaults.test.ts",
|
|
70
70
|
"test:openclaw": "tsx --test src/lib/openclaw/openclaw-agent-id.test.ts src/lib/openclaw/openclaw-endpoint.test.ts src/lib/server/agents/agent-runtime-config.test.ts src/lib/server/build-llm.test.ts src/lib/server/connectors/connector-routing.test.ts src/lib/server/connectors/openclaw.test.ts src/lib/server/gateway/protocol.test.ts src/lib/server/llm-response-cache.test.ts src/lib/server/mcp-conformance.test.ts src/lib/server/openclaw/agent-resolver.test.ts src/lib/server/openclaw/deploy.test.ts src/lib/server/openclaw/skills-normalize.test.ts src/lib/server/session-tools/openclaw-nodes.test.ts src/lib/server/tasks/task-quality-gate.test.ts src/lib/server/tasks/task-validation.test.ts src/lib/server/tool-capability-policy.test.ts src/lib/providers/openclaw-exports.test.ts src/app/api/openclaw/dashboard-url/route.test.ts",
|
|
71
71
|
"test:mcp:conformance": "node --import tsx ./scripts/mcp-conformance-check.ts",
|
|
72
|
+
"prepack": "npm run build:ci",
|
|
72
73
|
"postinstall": "node ./scripts/postinstall.mjs"
|
|
73
74
|
},
|
|
74
75
|
"dependencies": {
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
"@langchain/anthropic": "^1.3.18",
|
|
77
78
|
"@langchain/core": "^1.1.31",
|
|
78
79
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
80
|
+
"@tailwindcss/postcss": "^4",
|
|
79
81
|
"@langchain/langgraph": "^1.2.2",
|
|
80
82
|
"@langchain/openai": "^1.2.8",
|
|
81
83
|
"@multiavatar/multiavatar": "^1.0.7",
|
|
@@ -116,14 +118,15 @@
|
|
|
116
118
|
"rehype-highlight": "^7.0.2",
|
|
117
119
|
"remark-gfm": "^4.0.1",
|
|
118
120
|
"remove-markdown": "^0.6.3",
|
|
121
|
+
"shadcn": "^3.8.5",
|
|
119
122
|
"sonner": "^2.0.7",
|
|
123
|
+
"tailwindcss": "^4",
|
|
120
124
|
"tailwind-merge": "^3.4.1",
|
|
125
|
+
"typescript": "^5",
|
|
126
|
+
"tw-animate-css": "^1.4.0",
|
|
121
127
|
"ws": "^8.19.0",
|
|
122
128
|
"zod": "^4.3.6",
|
|
123
|
-
"zustand": "^5.0.11"
|
|
124
|
-
},
|
|
125
|
-
"devDependencies": {
|
|
126
|
-
"@tailwindcss/postcss": "^4",
|
|
129
|
+
"zustand": "^5.0.11",
|
|
127
130
|
"@types/better-sqlite3": "^7.6.13",
|
|
128
131
|
"@types/mailparser": "^3.4.6",
|
|
129
132
|
"@types/node": "^20",
|
|
@@ -131,14 +134,12 @@
|
|
|
131
134
|
"@types/qrcode": "^1.5.6",
|
|
132
135
|
"@types/react": "^19",
|
|
133
136
|
"@types/react-dom": "^19",
|
|
134
|
-
"@types/ws": "^8.18.1"
|
|
137
|
+
"@types/ws": "^8.18.1"
|
|
138
|
+
},
|
|
139
|
+
"devDependencies": {
|
|
135
140
|
"eslint": "^9",
|
|
136
141
|
"eslint-config-next": "16.1.6",
|
|
137
|
-
"
|
|
138
|
-
"tailwindcss": "^4",
|
|
139
|
-
"tsx": "^4.20.6",
|
|
140
|
-
"tw-animate-css": "^1.4.0",
|
|
141
|
-
"typescript": "^5"
|
|
142
|
+
"tsx": "^4.20.6"
|
|
142
143
|
},
|
|
143
144
|
"optionalDependencies": {
|
|
144
145
|
"botbuilder": "^4.23.3",
|