0101-agents 0.1.3 → 0.1.5
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/bin/cli.js +81 -31
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// 0101-agents logout
|
|
7
7
|
// 0101-agents whoami
|
|
8
8
|
// 0101-agents install <agent> [--force]
|
|
9
|
-
// 0101-agents update <agent>
|
|
9
|
+
// 0101-agents update <agent> [-f]
|
|
10
10
|
// 0101-agents list
|
|
11
11
|
// 0101-agents help
|
|
12
12
|
|
|
@@ -131,31 +131,64 @@ function ensureUnzipAvailable() {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
function unzipInto(zipPath, destDir, {
|
|
134
|
+
function unzipInto(zipPath, destDir, { exclude = [] } = {}) {
|
|
135
135
|
// Use the system unzip binary. Available everywhere except bare Windows.
|
|
136
|
+
// -o overwrites; a single -x takes the whole pattern list (after -d), used on
|
|
137
|
+
// update to keep the manifest's `preserve` paths (e.g. data/, .env) intact.
|
|
136
138
|
mkdirSync(destDir, { recursive: true })
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
139
|
+
const xs = exclude.length ? `-x ${exclude.map((p) => `"${p}"`).join(' ')}` : ''
|
|
140
|
+
execSync(`unzip -q -o "${zipPath}" -d "${destDir}" ${xs}`.trim(), { stdio: 'inherit' })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Read the installed artifact's own manifest.json (NOT the release manifest
|
|
144
|
+
// from the API). Drives per-artifact behavior: which paths to keep on update
|
|
145
|
+
// (`preserve`), an optional post-install command (`postInstall`), and how
|
|
146
|
+
// `start` launches. Fails soft to {} so agents without these fields behave
|
|
147
|
+
// exactly as before.
|
|
148
|
+
function readManifest(agent) {
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(readFileSync(join(agentInstallDir(agent), 'manifest.json'), 'utf8'))
|
|
151
|
+
} catch {
|
|
152
|
+
return {}
|
|
147
153
|
}
|
|
148
154
|
}
|
|
149
155
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
// Paths an update must NOT overwrite. Agents keep their data/ by default; the
|
|
157
|
+
// runner declares [".env", ".runner-state"]. Always returns a non-empty array.
|
|
158
|
+
function preserveList(manifest) {
|
|
159
|
+
const p = manifest?.preserve
|
|
160
|
+
return Array.isArray(p) && p.length ? p : ['data']
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// unzip -x patterns: drop both the entry and everything under it, so a
|
|
164
|
+
// preserved file (.env) and a preserved dir (data/) are each left untouched.
|
|
165
|
+
function excludePatterns(preserve) {
|
|
166
|
+
return preserve.flatMap((p) => [p, `${p}/*`])
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Run the manifest's postInstall command (if any) in the install dir — e.g. the
|
|
170
|
+
// runner's `npm ci && node scripts/postinstall.mjs`. Trusted: the zip is the
|
|
171
|
+
// license-gated artifact we shipped. No-op for agents (no postInstall field).
|
|
172
|
+
function runPostInstall(agent, manifest) {
|
|
173
|
+
const cmd = manifest?.postInstall
|
|
174
|
+
if (!cmd || typeof cmd !== 'string') return
|
|
175
|
+
info('Running post-install…')
|
|
176
|
+
execSync(cmd, { cwd: agentInstallDir(agent), stdio: 'inherit' })
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Back up the paths an update would replace, so a bad release is recoverable.
|
|
180
|
+
// Tars only the preserve paths that actually exist (data/ for agents; .env +
|
|
181
|
+
// .runner-state/ for the runner).
|
|
182
|
+
function backupPreserved(agent, preserve) {
|
|
183
|
+
const dir = agentInstallDir(agent)
|
|
184
|
+
const existing = preserve.filter((p) => existsSync(join(dir, p)))
|
|
185
|
+
if (!existing.length) return null
|
|
153
186
|
mkdirSync(BACKUPS_DIR, { recursive: true })
|
|
154
187
|
const ts = new Date().toISOString().replace(/[:.]/g, '-')
|
|
155
188
|
const oldVersion = installedVersion(agent) ?? 'unknown'
|
|
156
189
|
const backupPath = join(BACKUPS_DIR, `${agent}-${oldVersion}-${ts}.tar.gz`)
|
|
157
190
|
execSync(
|
|
158
|
-
`tar -czf "${backupPath}" -C "${
|
|
191
|
+
`tar -czf "${backupPath}" -C "${dir}" ${existing.map((p) => `"${p}"`).join(' ')}`,
|
|
159
192
|
{ stdio: 'inherit' },
|
|
160
193
|
)
|
|
161
194
|
return backupPath
|
|
@@ -220,8 +253,9 @@ async function cmdInstall(args) {
|
|
|
220
253
|
}
|
|
221
254
|
mkdirSync(installDir, { recursive: true })
|
|
222
255
|
unzipInto(zipPath, installDir)
|
|
223
|
-
writeInstalledVersion(agent, manifest.latest)
|
|
224
256
|
rmSync(zipPath, { force: true })
|
|
257
|
+
runPostInstall(agent, readManifest(agent))
|
|
258
|
+
writeInstalledVersion(agent, manifest.latest)
|
|
225
259
|
|
|
226
260
|
console.log('')
|
|
227
261
|
ok(`Installed ${bold(agent)} ${manifest.latest} → ${installDir}`)
|
|
@@ -230,8 +264,9 @@ async function cmdInstall(args) {
|
|
|
230
264
|
}
|
|
231
265
|
|
|
232
266
|
async function cmdUpdate(args) {
|
|
233
|
-
const agent = args
|
|
234
|
-
|
|
267
|
+
const agent = args.find((a) => !a.startsWith('-'))
|
|
268
|
+
const assumeYes = args.some((a) => ['-f', '--force', '-y', '--yes'].includes(a))
|
|
269
|
+
if (!agent) die('Usage: 0101-agents update <agent> [-f]')
|
|
235
270
|
|
|
236
271
|
ensureUnzipAvailable()
|
|
237
272
|
const installDir = agentInstallDir(agent)
|
|
@@ -249,25 +284,29 @@ async function cmdUpdate(args) {
|
|
|
249
284
|
return
|
|
250
285
|
}
|
|
251
286
|
|
|
252
|
-
console.log(dim('Your data
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
287
|
+
console.log(dim('Your local data is preserved across the update.'))
|
|
288
|
+
if (!assumeYes) {
|
|
289
|
+
const ans = await prompt(`Update ${agent} ${current ?? '?'} → ${manifest.latest}? [Y/n] `)
|
|
290
|
+
if (ans && !/^y(es)?$/i.test(ans)) {
|
|
291
|
+
info('Cancelled.')
|
|
292
|
+
return
|
|
293
|
+
}
|
|
257
294
|
}
|
|
258
295
|
|
|
259
|
-
const
|
|
260
|
-
|
|
296
|
+
const preserve = preserveList(readManifest(agent))
|
|
297
|
+
const backup = backupPreserved(agent, preserve)
|
|
298
|
+
if (backup) info(`Backed up ${preserve.join(', ')} → ${backup}`)
|
|
261
299
|
|
|
262
300
|
const zipPath = join(tmpdir(), `0101-${agent}-${manifest.latest}.zip`)
|
|
263
301
|
info(`Downloading…`)
|
|
264
302
|
await downloadZip(manifest.download_url, zipPath)
|
|
265
303
|
|
|
266
|
-
// Extract
|
|
267
|
-
// survives
|
|
268
|
-
unzipInto(zipPath, installDir, {
|
|
269
|
-
writeInstalledVersion(agent, manifest.latest)
|
|
304
|
+
// Extract over the install, skipping the manifest's `preserve` paths so user
|
|
305
|
+
// state survives (agents: data/; runner: .env + .runner-state/).
|
|
306
|
+
unzipInto(zipPath, installDir, { exclude: excludePatterns(preserve) })
|
|
270
307
|
rmSync(zipPath, { force: true })
|
|
308
|
+
runPostInstall(agent, readManifest(agent))
|
|
309
|
+
writeInstalledVersion(agent, manifest.latest)
|
|
271
310
|
|
|
272
311
|
console.log('')
|
|
273
312
|
ok(`Updated ${bold(agent)} → ${manifest.latest}`)
|
|
@@ -308,6 +347,17 @@ async function cmdStart(args) {
|
|
|
308
347
|
// ignore
|
|
309
348
|
}
|
|
310
349
|
|
|
350
|
+
// Artifacts can declare how `start` launches via manifest.start. The runner
|
|
351
|
+
// sets "npm start"; agents leave it unset and fall through to launching
|
|
352
|
+
// Claude Code below.
|
|
353
|
+
const startCmd = readManifest(agent).start
|
|
354
|
+
if (startCmd) {
|
|
355
|
+
const child = spawn(startCmd, { cwd: dir, stdio: 'inherit', shell: true })
|
|
356
|
+
child.on('error', (e) => die(`Failed to start: ${e.message}`))
|
|
357
|
+
child.on('exit', (code) => process.exit(code ?? 0))
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
|
|
311
361
|
// Everything after the agent name passes through to `claude`. Supports both:
|
|
312
362
|
// 0101-agents start marketer --resume
|
|
313
363
|
// 0101-agents start marketer -- --resume (explicit separator)
|
|
@@ -349,7 +399,7 @@ Commands:
|
|
|
349
399
|
${bold('logout')} Forget your license key
|
|
350
400
|
${bold('whoami')} Show current license
|
|
351
401
|
${bold('install')} <agent> [--force] Install an agent
|
|
352
|
-
${bold('update')} <agent>
|
|
402
|
+
${bold('update')} <agent> [-f] Update an installed agent (preserves data/). -f skips the prompt
|
|
353
403
|
${bold('start')} <agent> [--tg] [-- args] Open the agent (runs claude in its dir)
|
|
354
404
|
--tg adds the right flags to run as a
|
|
355
405
|
Telegram channel bot.
|