@oneworks/cli 0.1.0-alpha.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/channel.js +7 -0
- package/cli.js +5 -0
- package/mem.js +7 -0
- package/package.json +59 -0
- package/postinstall.js +75 -0
- package/src/AGENTS.md +169 -0
- package/src/channel-cli.ts +19 -0
- package/src/cli-argv.ts +27 -0
- package/src/cli.ts +63 -0
- package/src/commands/@core/adapter-option.ts +85 -0
- package/src/commands/@core/extra-options.ts +12 -0
- package/src/commands/@core/plugin-install.ts +1 -0
- package/src/commands/@core/plugin-source.ts +1 -0
- package/src/commands/accounts.ts +204 -0
- package/src/commands/adapter/prepare-selection.ts +181 -0
- package/src/commands/adapter/prepare.ts +104 -0
- package/src/commands/adapter.ts +48 -0
- package/src/commands/agent/actions.ts +176 -0
- package/src/commands/agent/runtime-store-commands.ts +56 -0
- package/src/commands/agent/runtime-store-events.ts +23 -0
- package/src/commands/agent/runtime-store-session.ts +170 -0
- package/src/commands/agent/runtime-store-shared.ts +139 -0
- package/src/commands/agent/runtime-store.ts +4 -0
- package/src/commands/agent.ts +81 -0
- package/src/commands/benchmark.ts +198 -0
- package/src/commands/channel.ts +594 -0
- package/src/commands/clear.ts +140 -0
- package/src/commands/config/actions.ts +196 -0
- package/src/commands/config/display-state.ts +108 -0
- package/src/commands/config/index.ts +135 -0
- package/src/commands/config/interactive.ts +121 -0
- package/src/commands/config/read-state.ts +56 -0
- package/src/commands/config/section-state.ts +109 -0
- package/src/commands/config/shared.ts +195 -0
- package/src/commands/kill.ts +41 -0
- package/src/commands/list.ts +224 -0
- package/src/commands/memory/context.ts +76 -0
- package/src/commands/memory/entries.ts +131 -0
- package/src/commands/memory/shared.ts +89 -0
- package/src/commands/memory/store.ts +69 -0
- package/src/commands/memory/target.ts +54 -0
- package/src/commands/memory.ts +97 -0
- package/src/commands/plugin.ts +62 -0
- package/src/commands/report-targets.ts +149 -0
- package/src/commands/report.ts +232 -0
- package/src/commands/run/adapter-cli-version.ts +65 -0
- package/src/commands/run/command.ts +982 -0
- package/src/commands/run/input-bridge.ts +108 -0
- package/src/commands/run/input-control.ts +112 -0
- package/src/commands/run/input-decision.ts +88 -0
- package/src/commands/run/options.ts +104 -0
- package/src/commands/run/output.ts +179 -0
- package/src/commands/run/permission-decision.ts +19 -0
- package/src/commands/run/permission-recovery.ts +194 -0
- package/src/commands/run/permission-state.ts +177 -0
- package/src/commands/run/print-idle-timeout.ts +47 -0
- package/src/commands/run/protocol-envelope.ts +111 -0
- package/src/commands/run/protocol-stdio.ts +71 -0
- package/src/commands/run/protocol.ts +391 -0
- package/src/commands/run/runtime-command-bridge.ts +190 -0
- package/src/commands/run/runtime-event-sink.ts +560 -0
- package/src/commands/run/session-exit-controller.ts +45 -0
- package/src/commands/run/types.ts +65 -0
- package/src/commands/run.ts +62 -0
- package/src/commands/session-control.ts +133 -0
- package/src/commands/skills/add-command.ts +88 -0
- package/src/commands/skills/install-command.ts +105 -0
- package/src/commands/skills/install.ts +216 -0
- package/src/commands/skills/progress.ts +126 -0
- package/src/commands/skills/publish-command.ts +85 -0
- package/src/commands/skills/register.ts +17 -0
- package/src/commands/skills/remove-command.ts +102 -0
- package/src/commands/skills/shared.ts +117 -0
- package/src/commands/skills/sync.ts +571 -0
- package/src/commands/skills/types.ts +33 -0
- package/src/commands/skills.ts +1 -0
- package/src/commands/stop.ts +41 -0
- package/src/config.ts +1 -0
- package/src/default-skill-plugin.ts +29 -0
- package/src/env.ts +1 -0
- package/src/hooks/plugins/index.ts +66 -0
- package/src/mem-cli.ts +19 -0
- package/src/session-cache.ts +250 -0
- package/src/session-permission-cache.ts +40 -0
- package/src/utils.ts +25 -0
- package/src/workspace.ts +12 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import { buildConfigJsonVariables, loadConfig } from '@oneworks/config'
|
|
4
|
+
import type { AdapterCtx, AdapterManageAccountProgressEvent } from '@oneworks/types'
|
|
5
|
+
import { loadAdapter } from '@oneworks/types'
|
|
6
|
+
import {
|
|
7
|
+
mergeProcessEnvWithProjectEnv,
|
|
8
|
+
persistAdapterAccountArtifacts,
|
|
9
|
+
removeStoredAdapterAccount
|
|
10
|
+
} from '@oneworks/utils'
|
|
11
|
+
import { createLogger } from '@oneworks/utils/create-logger'
|
|
12
|
+
import type { Command } from 'commander'
|
|
13
|
+
|
|
14
|
+
import { resolveCliWorkspaceCwd } from '#~/workspace.js'
|
|
15
|
+
import { normalizeCliAdapterOptionValue } from './@core/adapter-option'
|
|
16
|
+
|
|
17
|
+
const createTransientCache = (): AdapterCtx['cache'] => {
|
|
18
|
+
const store = new Map<string, unknown>()
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
set: async (key, value) => {
|
|
22
|
+
store.set(String(key), value)
|
|
23
|
+
return { cachePath: '' }
|
|
24
|
+
},
|
|
25
|
+
get: async (key) => store.get(String(key)) as any
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const loadCliAdapterContext = async (adapterKey: string, cwd: string) => {
|
|
30
|
+
const env = mergeProcessEnvWithProjectEnv(undefined, { workspaceFolder: cwd })
|
|
31
|
+
const [projectConfig, userConfig] = await loadConfig({
|
|
32
|
+
cwd,
|
|
33
|
+
env,
|
|
34
|
+
jsonVariables: buildConfigJsonVariables(cwd, env)
|
|
35
|
+
})
|
|
36
|
+
const adapter = await loadAdapter(adapterKey)
|
|
37
|
+
const adapterCtx = {
|
|
38
|
+
ctxId: `cli-adapter-accounts-${adapterKey}`,
|
|
39
|
+
cwd,
|
|
40
|
+
env,
|
|
41
|
+
cache: createTransientCache(),
|
|
42
|
+
logger: createLogger(cwd, `cli/adapter-accounts/${adapterKey}`, 'cli', '', 'info', env),
|
|
43
|
+
configs: [projectConfig, userConfig]
|
|
44
|
+
} satisfies AdapterCtx
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
adapter,
|
|
48
|
+
adapterCtx
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const printAccountSummary = (prefix: string, summary: {
|
|
53
|
+
key: string
|
|
54
|
+
title: string
|
|
55
|
+
description?: string
|
|
56
|
+
quota?: { summary?: string }
|
|
57
|
+
status?: string
|
|
58
|
+
}) => {
|
|
59
|
+
console.log(`${prefix}: ${summary.title} (${summary.key})`)
|
|
60
|
+
if (summary.status != null && summary.status !== '') {
|
|
61
|
+
console.log(`Status: ${summary.status}`)
|
|
62
|
+
}
|
|
63
|
+
if (summary.description != null && summary.description !== '') {
|
|
64
|
+
console.log(`Description: ${summary.description}`)
|
|
65
|
+
}
|
|
66
|
+
if (summary.quota?.summary != null && summary.quota.summary !== '') {
|
|
67
|
+
console.log(`Quota: ${summary.quota.summary}`)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const printAccountActionProgress = (event: AdapterManageAccountProgressEvent) => {
|
|
72
|
+
if (event.stream === 'stdout') {
|
|
73
|
+
process.stdout.write(event.message)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
if (event.stream === 'stderr') {
|
|
77
|
+
process.stderr.write(event.message)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
console.log(event.message)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function registerAccountsCommand(program: Command) {
|
|
84
|
+
const accountsCommand = program
|
|
85
|
+
.command('accounts')
|
|
86
|
+
.description('Manage adapter accounts stored in project-home runtime data')
|
|
87
|
+
|
|
88
|
+
accountsCommand
|
|
89
|
+
.command('add <adapter> [account]')
|
|
90
|
+
.description('Run the adapter login flow and save credentials into project-private account storage')
|
|
91
|
+
.action(async (adapterInput: string, account: string | undefined) => {
|
|
92
|
+
try {
|
|
93
|
+
const adapterKey = normalizeCliAdapterOptionValue(adapterInput)
|
|
94
|
+
const cwd = resolveCliWorkspaceCwd()
|
|
95
|
+
const { adapter, adapterCtx } = await loadCliAdapterContext(adapterKey, cwd)
|
|
96
|
+
if (adapter.manageAccount == null) {
|
|
97
|
+
throw new Error(`Adapter "${adapterKey}" does not support account management.`)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const result = await adapter.manageAccount(adapterCtx, {
|
|
101
|
+
action: 'add',
|
|
102
|
+
account,
|
|
103
|
+
onProgress: printAccountActionProgress
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
if ((result.artifacts?.length ?? 0) > 0) {
|
|
107
|
+
if (result.accountKey == null || result.accountKey.trim() === '') {
|
|
108
|
+
throw new Error('Adapter returned account artifacts without an account key.')
|
|
109
|
+
}
|
|
110
|
+
await persistAdapterAccountArtifacts({
|
|
111
|
+
cwd,
|
|
112
|
+
env: adapterCtx.env,
|
|
113
|
+
adapter: adapterKey,
|
|
114
|
+
account: result.accountKey,
|
|
115
|
+
artifacts: result.artifacts!
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const detail = result.accountKey != null && adapter.getAccountDetail != null
|
|
120
|
+
? await adapter.getAccountDetail(adapterCtx, {
|
|
121
|
+
account: result.accountKey,
|
|
122
|
+
refresh: true
|
|
123
|
+
}).catch(() => undefined)
|
|
124
|
+
: undefined
|
|
125
|
+
|
|
126
|
+
if (result.message != null && result.message.trim() !== '') {
|
|
127
|
+
console.log(result.message)
|
|
128
|
+
}
|
|
129
|
+
if (detail?.account != null) {
|
|
130
|
+
printAccountSummary('Connected account', detail.account)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
if (result.account != null) {
|
|
134
|
+
printAccountSummary('Connected account', result.account)
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
if (result.accountKey != null) {
|
|
138
|
+
console.log(`Connected account key: ${result.accountKey}`)
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
142
|
+
process.exit(1)
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
accountsCommand
|
|
147
|
+
.command('show <adapter> <account>')
|
|
148
|
+
.description('Show the latest account detail and quota snapshot for one adapter account')
|
|
149
|
+
.action(async (adapterInput: string, account: string) => {
|
|
150
|
+
try {
|
|
151
|
+
const adapterKey = normalizeCliAdapterOptionValue(adapterInput)
|
|
152
|
+
const cwd = resolveCliWorkspaceCwd()
|
|
153
|
+
const { adapter, adapterCtx } = await loadCliAdapterContext(adapterKey, cwd)
|
|
154
|
+
if (adapter.getAccountDetail == null) {
|
|
155
|
+
throw new Error(`Adapter "${adapterKey}" does not support account detail.`)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const detail = await adapter.getAccountDetail(adapterCtx, {
|
|
159
|
+
account,
|
|
160
|
+
refresh: true
|
|
161
|
+
})
|
|
162
|
+
printAccountSummary('Account', detail.account)
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
165
|
+
process.exit(1)
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
accountsCommand
|
|
170
|
+
.command('remove <adapter> <account>')
|
|
171
|
+
.description('Remove the project-private credential snapshot for one adapter account')
|
|
172
|
+
.action(async (adapterInput: string, account: string) => {
|
|
173
|
+
try {
|
|
174
|
+
const adapterKey = normalizeCliAdapterOptionValue(adapterInput)
|
|
175
|
+
const cwd = resolveCliWorkspaceCwd()
|
|
176
|
+
const { adapter, adapterCtx } = await loadCliAdapterContext(adapterKey, cwd)
|
|
177
|
+
if (adapter.manageAccount == null) {
|
|
178
|
+
throw new Error(`Adapter "${adapterKey}" does not support account management.`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const result = await adapter.manageAccount(adapterCtx, {
|
|
182
|
+
action: 'remove',
|
|
183
|
+
account
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
if (result.removeStoredAccount === true) {
|
|
187
|
+
if (result.accountKey == null || result.accountKey.trim() === '') {
|
|
188
|
+
throw new Error('Adapter remove action requires an account key.')
|
|
189
|
+
}
|
|
190
|
+
await removeStoredAdapterAccount({
|
|
191
|
+
cwd,
|
|
192
|
+
env: adapterCtx.env,
|
|
193
|
+
adapter: adapterKey,
|
|
194
|
+
account: result.accountKey
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(result.message ?? `Removed adapter account "${account}".`)
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
201
|
+
process.exit(1)
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { loadAdapterCliPreparer, normalizeAdapterPackageId } from '@oneworks/types'
|
|
2
|
+
import type { AdapterCliPrepareTarget, AdapterCliPreparer, Config } from '@oneworks/types'
|
|
3
|
+
|
|
4
|
+
const KNOWN_PREPARE_ADAPTERS = [
|
|
5
|
+
'codex',
|
|
6
|
+
'claude-code',
|
|
7
|
+
'gemini',
|
|
8
|
+
'copilot',
|
|
9
|
+
'opencode',
|
|
10
|
+
'kimi'
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
const SPECIAL_TARGET_ALIASES: Record<string, { adapter: string; target: string }> = {
|
|
14
|
+
ccr: {
|
|
15
|
+
adapter: 'claude-code',
|
|
16
|
+
target: 'routerCli'
|
|
17
|
+
},
|
|
18
|
+
'claude-code-router': {
|
|
19
|
+
adapter: 'claude-code',
|
|
20
|
+
target: 'routerCli'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface AdapterPrepareRequest {
|
|
25
|
+
adapter: string
|
|
26
|
+
target: AdapterCliPrepareTarget
|
|
27
|
+
preparer: AdapterCliPreparer
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ParsedAdapterPrepareTarget =
|
|
31
|
+
| { all: true }
|
|
32
|
+
| { adapter: string; target?: string }
|
|
33
|
+
|
|
34
|
+
const isPlainRecord = (value: unknown): value is Record<string, unknown> => (
|
|
35
|
+
value != null && typeof value === 'object' && !Array.isArray(value)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const normalizeAdapterId = (value: string) => {
|
|
39
|
+
const normalized = normalizeAdapterPackageId(value)
|
|
40
|
+
return normalized.startsWith('adapter-') ? normalized.slice('adapter-'.length) : normalized
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const readPath = (value: unknown, path: string[]) => {
|
|
44
|
+
let current = value
|
|
45
|
+
for (const segment of path) {
|
|
46
|
+
if (!isPlainRecord(current)) return undefined
|
|
47
|
+
current = current[segment]
|
|
48
|
+
}
|
|
49
|
+
return current
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const isTargetPrepareOnInstallEnabled = (
|
|
53
|
+
config: Config,
|
|
54
|
+
adapter: string,
|
|
55
|
+
target: AdapterCliPrepareTarget
|
|
56
|
+
) => {
|
|
57
|
+
const adapterConfig = (config.adapters as Record<string, unknown> | undefined)?.[adapter]
|
|
58
|
+
const cliConfig = readPath(adapterConfig, target.configPath ?? [target.key])
|
|
59
|
+
return isPlainRecord(cliConfig) && cliConfig.prepareOnInstall === true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const targetMatches = (target: AdapterCliPrepareTarget, value: string) => (
|
|
63
|
+
target.key === value || target.aliases?.includes(value) === true
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
export const parseAdapterPrepareTargetInput = (rawValue: string): ParsedAdapterPrepareTarget | undefined => {
|
|
67
|
+
const value = rawValue.trim()
|
|
68
|
+
if (value === '') return undefined
|
|
69
|
+
if (value === 'all') return { all: true as const }
|
|
70
|
+
|
|
71
|
+
const specialTarget = SPECIAL_TARGET_ALIASES[value]
|
|
72
|
+
if (specialTarget != null) return specialTarget
|
|
73
|
+
|
|
74
|
+
const [rawAdapter, rawTarget] = value.split('.', 2)
|
|
75
|
+
return {
|
|
76
|
+
adapter: normalizeAdapterId(rawAdapter),
|
|
77
|
+
target: rawTarget
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const pushUniqueRequest = (
|
|
82
|
+
requests: AdapterPrepareRequest[],
|
|
83
|
+
request: AdapterPrepareRequest
|
|
84
|
+
) => {
|
|
85
|
+
const key = `${request.adapter}.${request.target.key}`
|
|
86
|
+
if (requests.some(item => `${item.adapter}.${item.target.key}` === key)) return
|
|
87
|
+
requests.push(request)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const resolveAdapterPrepareRequests = (params: {
|
|
91
|
+
all?: boolean
|
|
92
|
+
config: Config
|
|
93
|
+
preparers: AdapterCliPreparer[]
|
|
94
|
+
targets: string[]
|
|
95
|
+
}): AdapterPrepareRequest[] => {
|
|
96
|
+
const requests: AdapterPrepareRequest[] = []
|
|
97
|
+
|
|
98
|
+
if (params.all === true) {
|
|
99
|
+
for (const preparer of params.preparers) {
|
|
100
|
+
for (const target of preparer.targets) {
|
|
101
|
+
pushUniqueRequest(requests, {
|
|
102
|
+
adapter: preparer.adapter,
|
|
103
|
+
preparer,
|
|
104
|
+
target
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return requests
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (params.targets.length === 0) {
|
|
112
|
+
for (const preparer of params.preparers) {
|
|
113
|
+
for (const target of preparer.targets) {
|
|
114
|
+
if (!isTargetPrepareOnInstallEnabled(params.config, preparer.adapter, target)) continue
|
|
115
|
+
pushUniqueRequest(requests, {
|
|
116
|
+
adapter: preparer.adapter,
|
|
117
|
+
preparer,
|
|
118
|
+
target
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return requests
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const rawTarget of params.targets) {
|
|
126
|
+
const parsedTarget = parseAdapterPrepareTargetInput(rawTarget)
|
|
127
|
+
if (parsedTarget == null) continue
|
|
128
|
+
if ('all' in parsedTarget) {
|
|
129
|
+
return resolveAdapterPrepareRequests({
|
|
130
|
+
...params,
|
|
131
|
+
all: true,
|
|
132
|
+
targets: []
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const preparer = params.preparers.find(item => item.adapter === parsedTarget.adapter)
|
|
137
|
+
if (preparer == null) {
|
|
138
|
+
throw new Error(`Unknown adapter CLI prepare target: ${rawTarget}`)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const targets = parsedTarget.target == null
|
|
142
|
+
? preparer.targets
|
|
143
|
+
: preparer.targets.filter(target => targetMatches(target, parsedTarget.target!))
|
|
144
|
+
if (targets.length === 0) {
|
|
145
|
+
throw new Error(`Unknown adapter CLI prepare target: ${rawTarget}`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const target of targets) {
|
|
149
|
+
pushUniqueRequest(requests, {
|
|
150
|
+
adapter: preparer.adapter,
|
|
151
|
+
preparer,
|
|
152
|
+
target
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return requests
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const loadAdapterPreparePreparers = async (params: {
|
|
161
|
+
config: Config
|
|
162
|
+
requiredTargets: string[]
|
|
163
|
+
}) => {
|
|
164
|
+
const configuredAdapters = Object.keys(params.config.adapters ?? {})
|
|
165
|
+
const requestedAdapters = params.requiredTargets
|
|
166
|
+
.map(parseAdapterPrepareTargetInput)
|
|
167
|
+
.filter((value): value is { adapter: string; target?: string } => value != null && !('all' in value))
|
|
168
|
+
.map(value => value.adapter)
|
|
169
|
+
const adapterIds = Array.from(new Set([...KNOWN_PREPARE_ADAPTERS, ...configuredAdapters, ...requestedAdapters]))
|
|
170
|
+
const preparers: AdapterCliPreparer[] = []
|
|
171
|
+
|
|
172
|
+
for (const adapterId of adapterIds) {
|
|
173
|
+
try {
|
|
174
|
+
preparers.push(await loadAdapterCliPreparer(adapterId))
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (requestedAdapters.includes(adapterId)) throw error
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return preparers
|
|
181
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import { buildConfigJsonVariables, loadConfigState } from '@oneworks/config'
|
|
4
|
+
import type { AdapterCliPrepareContext, AdapterCliPrepareResult } from '@oneworks/types'
|
|
5
|
+
import { mergeProcessEnvWithProjectEnv } from '@oneworks/utils'
|
|
6
|
+
import {
|
|
7
|
+
PROJECT_PRIMARY_WORKSPACE_FOLDER_ENV,
|
|
8
|
+
resolveProjectPrimaryWorkspaceFolder
|
|
9
|
+
} from '@oneworks/utils/project-cache-path'
|
|
10
|
+
|
|
11
|
+
import { resolveCliWorkspaceCwd } from '#~/workspace.js'
|
|
12
|
+
|
|
13
|
+
import { loadAdapterPreparePreparers, resolveAdapterPrepareRequests } from './prepare-selection'
|
|
14
|
+
|
|
15
|
+
export interface AdapterPrepareCommandOptions {
|
|
16
|
+
all?: boolean
|
|
17
|
+
fromPostinstall?: boolean
|
|
18
|
+
json?: boolean
|
|
19
|
+
quiet?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const toPrepareEnv = (cwd: string) => {
|
|
23
|
+
const primaryWorkspaceFolder = resolveProjectPrimaryWorkspaceFolder(cwd, process.env) ?? cwd
|
|
24
|
+
return mergeProcessEnvWithProjectEnv({
|
|
25
|
+
__ONEWORKS_PROJECT_WORKSPACE_FOLDER__: cwd,
|
|
26
|
+
[PROJECT_PRIMARY_WORKSPACE_FOLDER_ENV]: primaryWorkspaceFolder
|
|
27
|
+
}, { workspaceFolder: cwd })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const createPrepareContext = async (quiet: boolean | undefined): Promise<AdapterCliPrepareContext> => {
|
|
31
|
+
const cwd = resolveCliWorkspaceCwd()
|
|
32
|
+
const env = toPrepareEnv(cwd)
|
|
33
|
+
const configState = await loadConfigState({
|
|
34
|
+
cwd,
|
|
35
|
+
env,
|
|
36
|
+
jsonVariables: buildConfigJsonVariables(cwd, env)
|
|
37
|
+
})
|
|
38
|
+
return {
|
|
39
|
+
cwd,
|
|
40
|
+
env,
|
|
41
|
+
configs: [configState.effectiveProjectConfig ?? configState.projectConfig, configState.userConfig],
|
|
42
|
+
configState,
|
|
43
|
+
logger: {
|
|
44
|
+
info: (...args: unknown[]) => {
|
|
45
|
+
if (quiet === true) return
|
|
46
|
+
console.error(args.map(arg => typeof arg === 'string' ? arg : JSON.stringify(arg)).join(' '))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const printPrepareResults = (
|
|
53
|
+
results: AdapterCliPrepareResult[],
|
|
54
|
+
options: AdapterPrepareCommandOptions
|
|
55
|
+
) => {
|
|
56
|
+
if (options.json === true) {
|
|
57
|
+
console.log(JSON.stringify({ ok: true, prepared: results }, null, 2))
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (options.quiet === true) return
|
|
62
|
+
for (const result of results) {
|
|
63
|
+
console.log(`Prepared ${result.title}: ${result.binaryPath}`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const runAdapterPrepareCommand = async (
|
|
68
|
+
targets: string[],
|
|
69
|
+
options: AdapterPrepareCommandOptions
|
|
70
|
+
) => {
|
|
71
|
+
const ctx = await createPrepareContext(options.quiet)
|
|
72
|
+
const preparers = await loadAdapterPreparePreparers({
|
|
73
|
+
config: ctx.configState?.mergedConfig ?? {},
|
|
74
|
+
requiredTargets: targets
|
|
75
|
+
})
|
|
76
|
+
const requests = resolveAdapterPrepareRequests({
|
|
77
|
+
all: options.all,
|
|
78
|
+
config: ctx.configState?.mergedConfig ?? {},
|
|
79
|
+
preparers,
|
|
80
|
+
targets
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
if (requests.length === 0) {
|
|
84
|
+
if (options.json === true) {
|
|
85
|
+
console.log(JSON.stringify({ ok: true, prepared: [] }, null, 2))
|
|
86
|
+
} else if (options.quiet !== true) {
|
|
87
|
+
console.log('No adapter CLI prepare targets selected.')
|
|
88
|
+
console.log('Set adapters.<adapter>.cli.prepareOnInstall: true, pass --all, or pass target names.')
|
|
89
|
+
}
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const results: AdapterCliPrepareResult[] = []
|
|
94
|
+
for (const request of requests) {
|
|
95
|
+
results.push(
|
|
96
|
+
await request.preparer.prepare(ctx, {
|
|
97
|
+
target: request.target.key
|
|
98
|
+
})
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
printPrepareResults(results, options)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { resolveAdapterPrepareRequests } from './prepare-selection'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import type { Command } from 'commander'
|
|
4
|
+
import { Option } from 'commander'
|
|
5
|
+
|
|
6
|
+
import type { AdapterPrepareCommandOptions } from './adapter/prepare'
|
|
7
|
+
import { runAdapterPrepareCommand } from './adapter/prepare'
|
|
8
|
+
|
|
9
|
+
const formatErrorMessage = (error: unknown) => error instanceof Error ? error.message : String(error)
|
|
10
|
+
|
|
11
|
+
export { resolveAdapterPrepareRequests } from './adapter/prepare'
|
|
12
|
+
|
|
13
|
+
export function registerAdapterCommand(program: Command) {
|
|
14
|
+
const adapterCommand = program
|
|
15
|
+
.command('adapter')
|
|
16
|
+
.description('Manage adapter runtime resources')
|
|
17
|
+
|
|
18
|
+
adapterCommand
|
|
19
|
+
.command('prepare [targets...]')
|
|
20
|
+
.description('Preinstall managed adapter CLI resources into the global bootstrap cache')
|
|
21
|
+
.option('--all', 'Prepare all known adapter CLI resources', false)
|
|
22
|
+
.option('--json', 'Print JSON output', false)
|
|
23
|
+
.option('--quiet', 'Suppress non-error output', false)
|
|
24
|
+
.addOption(new Option('--from-postinstall', 'Mark this run as a package postinstall prewarm').hideHelp())
|
|
25
|
+
.addHelpText(
|
|
26
|
+
'after',
|
|
27
|
+
`
|
|
28
|
+
Examples:
|
|
29
|
+
oneworks adapter prepare
|
|
30
|
+
oneworks adapter prepare --all
|
|
31
|
+
oneworks adapter prepare codex claude-code gemini
|
|
32
|
+
oneworks adapter prepare claude-code.routerCli
|
|
33
|
+
`
|
|
34
|
+
)
|
|
35
|
+
.action(async (targets: string[], opts: AdapterPrepareCommandOptions) => {
|
|
36
|
+
try {
|
|
37
|
+
await runAdapterPrepareCommand(targets, opts)
|
|
38
|
+
} catch (error) {
|
|
39
|
+
const message = formatErrorMessage(error)
|
|
40
|
+
if (opts.json === true) {
|
|
41
|
+
console.error(JSON.stringify({ ok: false, error: message }, null, 2))
|
|
42
|
+
} else {
|
|
43
|
+
console.error(message)
|
|
44
|
+
}
|
|
45
|
+
process.exit(1)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import { resolveCliWorkspaceCwd } from '#~/workspace.js'
|
|
4
|
+
|
|
5
|
+
import { executeRuntimeProtocolCommand } from '../run/protocol'
|
|
6
|
+
import { readRuntimeEvents } from './runtime-store'
|
|
7
|
+
|
|
8
|
+
export interface AgentStartOptions {
|
|
9
|
+
avatar?: string
|
|
10
|
+
entity: string
|
|
11
|
+
hostSession?: string
|
|
12
|
+
json?: boolean
|
|
13
|
+
message: string
|
|
14
|
+
room?: string
|
|
15
|
+
roomTitle?: string
|
|
16
|
+
title?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AgentSessionOptions {
|
|
20
|
+
json?: boolean
|
|
21
|
+
message?: string
|
|
22
|
+
request?: string
|
|
23
|
+
session: string
|
|
24
|
+
value?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AgentEventsOptions {
|
|
28
|
+
follow?: boolean
|
|
29
|
+
jsonl?: boolean
|
|
30
|
+
session: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const printJson = (value: unknown) => {
|
|
34
|
+
console.log(JSON.stringify(value, null, 2))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const printJsonl = (value: unknown) => {
|
|
38
|
+
console.log(JSON.stringify(value))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const exitWithError = (error: unknown): never => {
|
|
42
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
43
|
+
console.error(message)
|
|
44
|
+
process.exit(1)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const unwrapProtocolResult = (result: Awaited<ReturnType<typeof executeRuntimeProtocolCommand>>) => {
|
|
48
|
+
if (!result.ok) throw new Error(result.error ?? 'Runtime protocol command failed.')
|
|
49
|
+
return result.result
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const runAgentAction = async (action: () => Promise<void>) => {
|
|
53
|
+
try {
|
|
54
|
+
await action()
|
|
55
|
+
} catch (error) {
|
|
56
|
+
exitWithError(error)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const runAgentStart = async (opts: AgentStartOptions) => {
|
|
61
|
+
const hostSessionId = opts.hostSession?.trim() ||
|
|
62
|
+
process.env.__ONEWORKS_AGENT_ROOM_HOST_SESSION_ID__?.trim() ||
|
|
63
|
+
undefined
|
|
64
|
+
const roomId = opts.room?.trim() || process.env.__ONEWORKS_AGENT_ROOM_ID__?.trim() || undefined
|
|
65
|
+
const roomTitle = opts.roomTitle?.trim() || process.env.__ONEWORKS_AGENT_ROOM_TITLE__?.trim() || undefined
|
|
66
|
+
printJson(
|
|
67
|
+
unwrapProtocolResult(
|
|
68
|
+
await executeRuntimeProtocolCommand({
|
|
69
|
+
type: 'session.start',
|
|
70
|
+
entity: opts.entity,
|
|
71
|
+
hostSessionId,
|
|
72
|
+
memberAvatar: opts.avatar,
|
|
73
|
+
memberKey: opts.entity,
|
|
74
|
+
memberLabel: opts.entity,
|
|
75
|
+
message: opts.message,
|
|
76
|
+
parentSessionId: hostSessionId,
|
|
77
|
+
roomId,
|
|
78
|
+
roomTitle,
|
|
79
|
+
runTitle: opts.title,
|
|
80
|
+
title: opts.title
|
|
81
|
+
}, {
|
|
82
|
+
cwd: resolveCliWorkspaceCwd()
|
|
83
|
+
})
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const runAgentCommand = async (
|
|
89
|
+
opts: AgentSessionOptions,
|
|
90
|
+
type: 'send_message' | 'stop' | 'kill' | 'submit_input' | 'resume'
|
|
91
|
+
) => {
|
|
92
|
+
const protocolType = (() => {
|
|
93
|
+
switch (type) {
|
|
94
|
+
case 'send_message':
|
|
95
|
+
return 'session.message'
|
|
96
|
+
case 'submit_input':
|
|
97
|
+
return 'session.submit'
|
|
98
|
+
case 'resume':
|
|
99
|
+
return 'session.resume'
|
|
100
|
+
case 'kill':
|
|
101
|
+
case 'stop':
|
|
102
|
+
return 'session.stop'
|
|
103
|
+
}
|
|
104
|
+
})()
|
|
105
|
+
printJson(
|
|
106
|
+
unwrapProtocolResult(
|
|
107
|
+
await executeRuntimeProtocolCommand({
|
|
108
|
+
type: protocolType,
|
|
109
|
+
sessionId: opts.session,
|
|
110
|
+
message: opts.message,
|
|
111
|
+
mode: type === 'kill' ? 'force' : undefined,
|
|
112
|
+
requestId: opts.request,
|
|
113
|
+
value: opts.value
|
|
114
|
+
}, {
|
|
115
|
+
cwd: resolveCliWorkspaceCwd()
|
|
116
|
+
})
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const runAgentStatus = async (opts: AgentSessionOptions) => {
|
|
122
|
+
printJson(
|
|
123
|
+
unwrapProtocolResult(
|
|
124
|
+
await executeRuntimeProtocolCommand({
|
|
125
|
+
type: 'session.status',
|
|
126
|
+
sessionId: opts.session
|
|
127
|
+
}, {
|
|
128
|
+
cwd: resolveCliWorkspaceCwd()
|
|
129
|
+
})
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const printFollowEvents = async (params: {
|
|
135
|
+
cwd: string
|
|
136
|
+
sessionId: string
|
|
137
|
+
}) => {
|
|
138
|
+
let printed = 0
|
|
139
|
+
|
|
140
|
+
for (;;) {
|
|
141
|
+
const events = await readRuntimeEvents(params.cwd, params.sessionId)
|
|
142
|
+
for (const event of events.slice(printed)) {
|
|
143
|
+
printJsonl(event)
|
|
144
|
+
}
|
|
145
|
+
printed = events.length
|
|
146
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const runAgentEvents = async (opts: AgentEventsOptions) => {
|
|
151
|
+
const cwd = resolveCliWorkspaceCwd()
|
|
152
|
+
if (opts.follow && !opts.jsonl) {
|
|
153
|
+
throw new Error('agent events --follow requires --jsonl.')
|
|
154
|
+
}
|
|
155
|
+
if (opts.follow) {
|
|
156
|
+
await printFollowEvents({ cwd, sessionId: opts.session })
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const result = unwrapProtocolResult(
|
|
161
|
+
await executeRuntimeProtocolCommand({
|
|
162
|
+
type: 'session.events',
|
|
163
|
+
sessionId: opts.session
|
|
164
|
+
}, {
|
|
165
|
+
cwd
|
|
166
|
+
})
|
|
167
|
+
)
|
|
168
|
+
const events = Array.isArray((result as { events?: unknown }).events)
|
|
169
|
+
? (result as { events: unknown[] }).events
|
|
170
|
+
: []
|
|
171
|
+
if (opts.jsonl) {
|
|
172
|
+
for (const event of events) printJsonl(event)
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
printJson(events)
|
|
176
|
+
}
|