@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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/channel.js +7 -0
  3. package/cli.js +5 -0
  4. package/mem.js +7 -0
  5. package/package.json +59 -0
  6. package/postinstall.js +75 -0
  7. package/src/AGENTS.md +169 -0
  8. package/src/channel-cli.ts +19 -0
  9. package/src/cli-argv.ts +27 -0
  10. package/src/cli.ts +63 -0
  11. package/src/commands/@core/adapter-option.ts +85 -0
  12. package/src/commands/@core/extra-options.ts +12 -0
  13. package/src/commands/@core/plugin-install.ts +1 -0
  14. package/src/commands/@core/plugin-source.ts +1 -0
  15. package/src/commands/accounts.ts +204 -0
  16. package/src/commands/adapter/prepare-selection.ts +181 -0
  17. package/src/commands/adapter/prepare.ts +104 -0
  18. package/src/commands/adapter.ts +48 -0
  19. package/src/commands/agent/actions.ts +176 -0
  20. package/src/commands/agent/runtime-store-commands.ts +56 -0
  21. package/src/commands/agent/runtime-store-events.ts +23 -0
  22. package/src/commands/agent/runtime-store-session.ts +170 -0
  23. package/src/commands/agent/runtime-store-shared.ts +139 -0
  24. package/src/commands/agent/runtime-store.ts +4 -0
  25. package/src/commands/agent.ts +81 -0
  26. package/src/commands/benchmark.ts +198 -0
  27. package/src/commands/channel.ts +594 -0
  28. package/src/commands/clear.ts +140 -0
  29. package/src/commands/config/actions.ts +196 -0
  30. package/src/commands/config/display-state.ts +108 -0
  31. package/src/commands/config/index.ts +135 -0
  32. package/src/commands/config/interactive.ts +121 -0
  33. package/src/commands/config/read-state.ts +56 -0
  34. package/src/commands/config/section-state.ts +109 -0
  35. package/src/commands/config/shared.ts +195 -0
  36. package/src/commands/kill.ts +41 -0
  37. package/src/commands/list.ts +224 -0
  38. package/src/commands/memory/context.ts +76 -0
  39. package/src/commands/memory/entries.ts +131 -0
  40. package/src/commands/memory/shared.ts +89 -0
  41. package/src/commands/memory/store.ts +69 -0
  42. package/src/commands/memory/target.ts +54 -0
  43. package/src/commands/memory.ts +97 -0
  44. package/src/commands/plugin.ts +62 -0
  45. package/src/commands/report-targets.ts +149 -0
  46. package/src/commands/report.ts +232 -0
  47. package/src/commands/run/adapter-cli-version.ts +65 -0
  48. package/src/commands/run/command.ts +982 -0
  49. package/src/commands/run/input-bridge.ts +108 -0
  50. package/src/commands/run/input-control.ts +112 -0
  51. package/src/commands/run/input-decision.ts +88 -0
  52. package/src/commands/run/options.ts +104 -0
  53. package/src/commands/run/output.ts +179 -0
  54. package/src/commands/run/permission-decision.ts +19 -0
  55. package/src/commands/run/permission-recovery.ts +194 -0
  56. package/src/commands/run/permission-state.ts +177 -0
  57. package/src/commands/run/print-idle-timeout.ts +47 -0
  58. package/src/commands/run/protocol-envelope.ts +111 -0
  59. package/src/commands/run/protocol-stdio.ts +71 -0
  60. package/src/commands/run/protocol.ts +391 -0
  61. package/src/commands/run/runtime-command-bridge.ts +190 -0
  62. package/src/commands/run/runtime-event-sink.ts +560 -0
  63. package/src/commands/run/session-exit-controller.ts +45 -0
  64. package/src/commands/run/types.ts +65 -0
  65. package/src/commands/run.ts +62 -0
  66. package/src/commands/session-control.ts +133 -0
  67. package/src/commands/skills/add-command.ts +88 -0
  68. package/src/commands/skills/install-command.ts +105 -0
  69. package/src/commands/skills/install.ts +216 -0
  70. package/src/commands/skills/progress.ts +126 -0
  71. package/src/commands/skills/publish-command.ts +85 -0
  72. package/src/commands/skills/register.ts +17 -0
  73. package/src/commands/skills/remove-command.ts +102 -0
  74. package/src/commands/skills/shared.ts +117 -0
  75. package/src/commands/skills/sync.ts +571 -0
  76. package/src/commands/skills/types.ts +33 -0
  77. package/src/commands/skills.ts +1 -0
  78. package/src/commands/stop.ts +41 -0
  79. package/src/config.ts +1 -0
  80. package/src/default-skill-plugin.ts +29 -0
  81. package/src/env.ts +1 -0
  82. package/src/hooks/plugins/index.ts +66 -0
  83. package/src/mem-cli.ts +19 -0
  84. package/src/session-cache.ts +250 -0
  85. package/src/session-permission-cache.ts +40 -0
  86. package/src/utils.ts +25 -0
  87. 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
+ }