typeclaw 0.11.1 → 0.12.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/README.md +1 -1
- package/package.json +1 -1
- package/src/cli/builtins.ts +1 -0
- package/src/cli/index.ts +1 -0
- package/src/cli/update.ts +82 -0
- package/src/update/index.ts +86 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# TypeClaw
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="./docs/public/
|
|
4
|
+
<img src="./docs/public/typeclaw.png" alt="TypeClaw logo" width="240" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
> A TypeScript-native, Bun-powered, Docker-friendly general-purpose agent runtime.
|
package/package.json
CHANGED
package/src/cli/builtins.ts
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -33,6 +33,7 @@ const main = defineCommand({
|
|
|
33
33
|
model: () => import('./model').then((m) => m.modelCommand),
|
|
34
34
|
doctor: () => import('./doctor').then((m) => m.doctorCommand),
|
|
35
35
|
usage: () => import('./usage').then((m) => m.usageCommand),
|
|
36
|
+
update: () => import('./update').then((m) => m.updateCommand),
|
|
36
37
|
_hostd: () => import('./hostd').then((m) => m.hostdCommand),
|
|
37
38
|
},
|
|
38
39
|
})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { defineCommand } from 'citty'
|
|
2
|
+
|
|
3
|
+
import { formatCommand, planSelfUpdate, type UpdateManagerSelection } from '@/update'
|
|
4
|
+
|
|
5
|
+
import { c, errorLine, successLine } from './ui'
|
|
6
|
+
|
|
7
|
+
const MANAGERS = ['auto', 'bun', 'npm', 'pnpm', 'yarn'] as const
|
|
8
|
+
|
|
9
|
+
export const updateCommand = defineCommand({
|
|
10
|
+
meta: {
|
|
11
|
+
name: 'update',
|
|
12
|
+
description: 'update the globally installed typeclaw CLI',
|
|
13
|
+
},
|
|
14
|
+
args: {
|
|
15
|
+
manager: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'package manager to use: auto, bun, npm, pnpm, or yarn',
|
|
18
|
+
default: 'auto',
|
|
19
|
+
},
|
|
20
|
+
'dry-run': {
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
description: 'print the update command without running it',
|
|
23
|
+
default: false,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
async run({ args }) {
|
|
27
|
+
const manager = parseManager(args.manager)
|
|
28
|
+
if (manager === null) {
|
|
29
|
+
console.error(errorLine(`Invalid --manager=${args.manager}. Expected auto, bun, npm, pnpm, or yarn.`))
|
|
30
|
+
process.exit(2)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const plan = planSelfUpdate({ manager })
|
|
34
|
+
if (!plan.ok) {
|
|
35
|
+
console.error(errorLine(plan.reason))
|
|
36
|
+
process.exit(1)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const rendered = formatCommand(plan.command)
|
|
40
|
+
if (args['dry-run']) {
|
|
41
|
+
process.stdout.write(`${rendered}\n`)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
process.stdout.write(`${c.cyan('Updating TypeClaw with:')} ${rendered}\n`)
|
|
46
|
+
const exitCode = await runUpdateCommand(plan.command)
|
|
47
|
+
if (exitCode !== 0) {
|
|
48
|
+
console.error(errorLine(`Update command exited with code ${exitCode}.`))
|
|
49
|
+
process.exit(exitCode)
|
|
50
|
+
}
|
|
51
|
+
process.stdout.write(`${successLine('TypeClaw update command completed.')}\n`)
|
|
52
|
+
process.stdout.write(`${c.dim('Restart running agent containers to pick up the new CLI runtime.')}\n`)
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
function parseManager(value: string | undefined): UpdateManagerSelection | null {
|
|
57
|
+
if (value === undefined) return 'auto'
|
|
58
|
+
return (MANAGERS as readonly string[]).includes(value) ? (value as UpdateManagerSelection) : null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function runUpdateCommand(command: string[]): Promise<number> {
|
|
62
|
+
const bun = (globalThis as { Bun?: { spawn: typeof Bun.spawn } }).Bun
|
|
63
|
+
if (!bun) {
|
|
64
|
+
console.error(errorLine('bun runtime not available'))
|
|
65
|
+
return 1
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const proc = bun.spawn({
|
|
69
|
+
cmd: command,
|
|
70
|
+
stdin: 'inherit',
|
|
71
|
+
stdout: 'inherit',
|
|
72
|
+
stderr: 'inherit',
|
|
73
|
+
})
|
|
74
|
+
return await proc.exited
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if ((error as NodeJS.ErrnoException)?.code === 'ENOENT') {
|
|
77
|
+
console.error(errorLine(`${command[0]} not found in $PATH.`))
|
|
78
|
+
return 127
|
|
79
|
+
}
|
|
80
|
+
throw error
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
|
|
3
|
+
export type UpdateManager = 'bun' | 'npm' | 'pnpm' | 'yarn'
|
|
4
|
+
export type UpdateManagerSelection = 'auto' | UpdateManager
|
|
5
|
+
|
|
6
|
+
export type SelfUpdatePlan =
|
|
7
|
+
| { ok: true; manager: UpdateManager; command: string[]; detectedFrom: string }
|
|
8
|
+
| { ok: false; reason: string }
|
|
9
|
+
|
|
10
|
+
export function resolveSelfPackageJsonPath(): string {
|
|
11
|
+
return join(import.meta.dir, '..', '..', 'package.json')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function planSelfUpdate(options: { manager: UpdateManagerSelection; packageJsonPath?: string }): SelfUpdatePlan {
|
|
15
|
+
const packageJsonPath = options.packageJsonPath ?? resolveSelfPackageJsonPath()
|
|
16
|
+
const manager = options.manager === 'auto' ? detectInstallManager(packageJsonPath) : options.manager
|
|
17
|
+
if (manager === null) {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
reason:
|
|
21
|
+
'Cannot auto-detect how TypeClaw was installed from this checkout. Re-run with --manager=bun, --manager=npm, --manager=pnpm, or --manager=yarn if you want to update a global install.',
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
ok: true,
|
|
26
|
+
manager,
|
|
27
|
+
command: commandForManager(manager),
|
|
28
|
+
detectedFrom: packageJsonPath,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function commandForManager(manager: UpdateManager): string[] {
|
|
33
|
+
switch (manager) {
|
|
34
|
+
case 'bun':
|
|
35
|
+
return ['bun', 'update', '-g', 'typeclaw', '--latest']
|
|
36
|
+
case 'npm':
|
|
37
|
+
return ['npm', 'install', '-g', 'typeclaw@latest']
|
|
38
|
+
case 'pnpm':
|
|
39
|
+
return ['pnpm', 'add', '-g', 'typeclaw@latest']
|
|
40
|
+
case 'yarn':
|
|
41
|
+
return ['yarn', 'global', 'upgrade', 'typeclaw', '--latest']
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function formatCommand(command: readonly string[]): string {
|
|
46
|
+
return command.map(shellQuote).join(' ')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function detectInstallManager(packageJsonPath: string): UpdateManager | null {
|
|
50
|
+
const parts = packageJsonPath.split(/[\\/]+/).filter(Boolean)
|
|
51
|
+
const packageJson = parts[parts.length - 1]
|
|
52
|
+
const packageName = parts[parts.length - 2]
|
|
53
|
+
const nodeModulesIdx = parts.lastIndexOf('node_modules')
|
|
54
|
+
if (packageJson !== 'package.json' || packageName !== 'typeclaw' || nodeModulesIdx === -1) return null
|
|
55
|
+
|
|
56
|
+
// Bun global: .bun/install/global/node_modules/typeclaw
|
|
57
|
+
const bunGlobalIdx = parts.lastIndexOf('.bun')
|
|
58
|
+
if (
|
|
59
|
+
bunGlobalIdx !== -1 &&
|
|
60
|
+
parts[bunGlobalIdx + 1] === 'install' &&
|
|
61
|
+
parts[bunGlobalIdx + 2] === 'global' &&
|
|
62
|
+
parts[bunGlobalIdx + 3] === 'node_modules'
|
|
63
|
+
) {
|
|
64
|
+
return 'bun'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// pnpm shards globals under a numeric major-version segment, e.g.
|
|
68
|
+
// ~/Library/pnpm/global/5/node_modules or legacy ~/.pnpm-global/5/node_modules.
|
|
69
|
+
if (nodeModulesIdx >= 2 && /^\d+$/.test(parts[nodeModulesIdx - 1] ?? '')) {
|
|
70
|
+
const anchor = parts[nodeModulesIdx - 2]
|
|
71
|
+
if (anchor === 'pnpm-global' || anchor === '.pnpm-global') return 'pnpm'
|
|
72
|
+
if (anchor === 'global' && parts[nodeModulesIdx - 3] === 'pnpm') return 'pnpm'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (nodeModulesIdx >= 2 && parts[nodeModulesIdx - 1] === 'global' && parts[nodeModulesIdx - 2] === 'yarn') {
|
|
76
|
+
return 'yarn'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (parts[nodeModulesIdx - 1] === 'lib') return 'npm'
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function shellQuote(arg: string): string {
|
|
84
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(arg)) return arg
|
|
85
|
+
return `'${arg.replaceAll("'", "'\\''")}'`
|
|
86
|
+
}
|