@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,56 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
|
|
4
|
+
import { buildCommand, getStore, trimRequired } from './runtime-store-shared'
|
|
5
|
+
import type { AppendRuntimeCommandParams } from './runtime-store-shared'
|
|
6
|
+
|
|
7
|
+
export const appendRuntimeCommand = async (params: AppendRuntimeCommandParams) => {
|
|
8
|
+
const sessionId = trimRequired(params.sessionId, 'session')
|
|
9
|
+
const store = await getStore(params.cwd, params.env)
|
|
10
|
+
const session = store.session(sessionId)
|
|
11
|
+
if (!existsSync(session.sessionPath)) throw new Error(`Runtime session "${sessionId}" not found.`)
|
|
12
|
+
|
|
13
|
+
const content = params.type === 'send_message' || params.type === 'resume'
|
|
14
|
+
? trimRequired(params.message, 'message')
|
|
15
|
+
: params.message?.trim()
|
|
16
|
+
const requestId = params.type === 'submit_input'
|
|
17
|
+
? trimRequired(params.requestId, 'request')
|
|
18
|
+
: params.requestId?.trim()
|
|
19
|
+
const submitValue = params.value ?? params.data
|
|
20
|
+
if (params.type === 'submit_input' && submitValue == null) {
|
|
21
|
+
throw new Error('value is required.')
|
|
22
|
+
}
|
|
23
|
+
const value = typeof params.value === 'string' ? params.value.trim() : params.value
|
|
24
|
+
const data = params.data
|
|
25
|
+
const command = await session.appendCommand(buildCommand({
|
|
26
|
+
sessionId,
|
|
27
|
+
type: params.type,
|
|
28
|
+
ts: (params.now ?? Date.now)(),
|
|
29
|
+
content,
|
|
30
|
+
commandId: params.commandId,
|
|
31
|
+
memberKey: params.memberKey,
|
|
32
|
+
priority: params.priority,
|
|
33
|
+
requestId,
|
|
34
|
+
roomId: params.roomId,
|
|
35
|
+
runId: params.runId,
|
|
36
|
+
source: params.source,
|
|
37
|
+
value,
|
|
38
|
+
data
|
|
39
|
+
}))
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
ok: true,
|
|
43
|
+
commandId: command.commandId ?? command.id,
|
|
44
|
+
runtimeCommandId: command.id,
|
|
45
|
+
sessionId,
|
|
46
|
+
storePath: session.sessionPath,
|
|
47
|
+
type: command.type,
|
|
48
|
+
status: 'queued' as const
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const readRuntimeCommands = async (
|
|
53
|
+
cwd: string,
|
|
54
|
+
sessionId: string,
|
|
55
|
+
env: NodeJS.ProcessEnv = process.env
|
|
56
|
+
) => (await getStore(cwd, env)).session(sessionId).readCommands()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
|
|
4
|
+
import type { RuntimeEvent, RuntimeEventDraft } from '@oneworks/runtime-store'
|
|
5
|
+
|
|
6
|
+
import { getStore } from './runtime-store-shared'
|
|
7
|
+
|
|
8
|
+
export const readRuntimeEvents = async (
|
|
9
|
+
cwd: string,
|
|
10
|
+
sessionId: string,
|
|
11
|
+
env: NodeJS.ProcessEnv = process.env
|
|
12
|
+
) => {
|
|
13
|
+
const session = (await getStore(cwd, env)).session(sessionId)
|
|
14
|
+
if (!existsSync(session.sessionPath)) throw new Error(`Runtime session "${sessionId}" not found.`)
|
|
15
|
+
return session.replayEvents()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const appendRuntimeEventForTest = async (
|
|
19
|
+
cwd: string,
|
|
20
|
+
sessionId: string,
|
|
21
|
+
event: RuntimeEvent | RuntimeEventDraft,
|
|
22
|
+
env: NodeJS.ProcessEnv = process.env
|
|
23
|
+
) => (await getStore(cwd, env)).session(sessionId).appendEvent(event)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_SUPPORTED_PROTOCOL_RANGE, getCurrentProtocolVersion } from '@oneworks/runtime-protocol'
|
|
6
|
+
import { getSessionStorePath, resolveRuntimeRoot } from '@oneworks/runtime-store'
|
|
7
|
+
import type { RuntimeHeartbeat, RuntimeMeta, RuntimeState } from '@oneworks/runtime-store'
|
|
8
|
+
|
|
9
|
+
import { buildCommand, createSessionId, getStore, trimRequired } from './runtime-store-shared'
|
|
10
|
+
import type { CreateRuntimeSessionParams } from './runtime-store-shared'
|
|
11
|
+
|
|
12
|
+
export const createRuntimeRoomIdForHostSession = (hostSessionId: string) => `room_${hostSessionId}`
|
|
13
|
+
|
|
14
|
+
export const resolveRuntimeSessionStore = async (
|
|
15
|
+
cwd: string,
|
|
16
|
+
sessionId: string,
|
|
17
|
+
env: NodeJS.ProcessEnv = process.env
|
|
18
|
+
) => {
|
|
19
|
+
const root = await resolveRuntimeRoot({ cwd, env })
|
|
20
|
+
const storePath = getSessionStorePath(root, trimRequired(sessionId, 'session'))
|
|
21
|
+
return {
|
|
22
|
+
root,
|
|
23
|
+
sessionId,
|
|
24
|
+
storePath,
|
|
25
|
+
metaPath: path.resolve(storePath, 'meta.json'),
|
|
26
|
+
eventsPath: path.resolve(storePath, 'events.jsonl'),
|
|
27
|
+
commandsPath: path.resolve(storePath, 'commands.jsonl'),
|
|
28
|
+
statePath: path.resolve(storePath, 'state.json'),
|
|
29
|
+
heartbeatPath: path.resolve(storePath, 'heartbeat.json'),
|
|
30
|
+
locksPath: path.resolve(storePath, 'locks')
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const createRuntimeSession = async (params: CreateRuntimeSessionParams) => {
|
|
35
|
+
const now = params.now ?? Date.now
|
|
36
|
+
const sessionId = params.sessionId ?? createSessionId()
|
|
37
|
+
const entity = trimRequired(params.entity, 'entity')
|
|
38
|
+
const content = trimRequired(params.message, 'message')
|
|
39
|
+
const title = params.title?.trim() || `${entity} session`
|
|
40
|
+
const adapter = params.adapter?.trim() || undefined
|
|
41
|
+
const effort = params.effort
|
|
42
|
+
const model = params.model?.trim() || undefined
|
|
43
|
+
const permissionMode = params.permissionMode
|
|
44
|
+
const parentSessionId = params.parentSessionId?.trim() || undefined
|
|
45
|
+
const hostSessionId = params.hostSessionId?.trim() || parentSessionId
|
|
46
|
+
const roomId = params.roomId?.trim() ||
|
|
47
|
+
(hostSessionId == null ? undefined : createRuntimeRoomIdForHostSession(hostSessionId))
|
|
48
|
+
const roomTitle = params.roomTitle?.trim() || undefined
|
|
49
|
+
const memberKey = params.memberKey?.trim() || entity
|
|
50
|
+
const memberAvatar = params.memberAvatar?.trim() || undefined
|
|
51
|
+
const memberLabel = params.memberLabel?.trim() || entity
|
|
52
|
+
const runId = params.runId?.trim() || sessionId
|
|
53
|
+
const runTitle = params.runTitle?.trim() || title
|
|
54
|
+
const ts = now()
|
|
55
|
+
const store = await getStore(params.cwd, params.env)
|
|
56
|
+
const session = await store.createSession(
|
|
57
|
+
{
|
|
58
|
+
protocolVersion: getCurrentProtocolVersion(),
|
|
59
|
+
supportedProtocolRange: DEFAULT_SUPPORTED_PROTOCOL_RANGE,
|
|
60
|
+
sessionId,
|
|
61
|
+
title,
|
|
62
|
+
entity,
|
|
63
|
+
...(adapter != null ? { adapter } : {}),
|
|
64
|
+
...(effort != null ? { effort } : {}),
|
|
65
|
+
...(model != null ? { model } : {}),
|
|
66
|
+
...(permissionMode != null ? { permissionMode } : {}),
|
|
67
|
+
cwd: params.cwd,
|
|
68
|
+
...(parentSessionId != null ? { parentSessionId } : {}),
|
|
69
|
+
...(roomId != null ? { roomId } : {}),
|
|
70
|
+
...(roomTitle != null ? { roomTitle } : {}),
|
|
71
|
+
...(hostSessionId != null ? { hostSessionId } : {}),
|
|
72
|
+
memberKey,
|
|
73
|
+
...(memberAvatar != null ? { memberAvatar } : {}),
|
|
74
|
+
memberKind: 'entity',
|
|
75
|
+
memberLabel,
|
|
76
|
+
runId,
|
|
77
|
+
runTitle,
|
|
78
|
+
createdAt: ts,
|
|
79
|
+
needsEngineConsumer: true
|
|
80
|
+
} satisfies RuntimeMeta
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
await Promise.all([
|
|
84
|
+
session.writeState(buildInitialState({ sessionId, title, ts })),
|
|
85
|
+
session.writeHeartbeat(buildInitialHeartbeat({ sessionId, ts }))
|
|
86
|
+
])
|
|
87
|
+
const startCommand = await session.appendCommand(buildCommand({
|
|
88
|
+
sessionId,
|
|
89
|
+
type: 'start',
|
|
90
|
+
ts,
|
|
91
|
+
content,
|
|
92
|
+
commandId: params.commandId,
|
|
93
|
+
entity,
|
|
94
|
+
adapter,
|
|
95
|
+
effort,
|
|
96
|
+
model,
|
|
97
|
+
memberKey,
|
|
98
|
+
permissionMode,
|
|
99
|
+
priority: params.priority,
|
|
100
|
+
...(roomId != null ? { roomId } : {}),
|
|
101
|
+
runId,
|
|
102
|
+
source: params.source,
|
|
103
|
+
title
|
|
104
|
+
}))
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
...(params.commandId != null
|
|
108
|
+
? {
|
|
109
|
+
commandId: startCommand.commandId,
|
|
110
|
+
runtimeCommandId: startCommand.id
|
|
111
|
+
}
|
|
112
|
+
: {}),
|
|
113
|
+
sessionId,
|
|
114
|
+
storePath: session.sessionPath,
|
|
115
|
+
status: 'starting',
|
|
116
|
+
title,
|
|
117
|
+
...(hostSessionId != null ? { hostSessionId } : {}),
|
|
118
|
+
...(roomId != null ? { roomId } : {})
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const buildInitialState = (params: {
|
|
123
|
+
sessionId: string
|
|
124
|
+
title: string
|
|
125
|
+
ts: number
|
|
126
|
+
}): RuntimeState => ({
|
|
127
|
+
protocolVersion: getCurrentProtocolVersion(),
|
|
128
|
+
supportedProtocolRange: DEFAULT_SUPPORTED_PROTOCOL_RANGE,
|
|
129
|
+
sessionId: params.sessionId,
|
|
130
|
+
status: 'starting',
|
|
131
|
+
title: params.title,
|
|
132
|
+
lastSeq: 0,
|
|
133
|
+
updatedAt: params.ts,
|
|
134
|
+
needsEngineConsumer: true
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const buildInitialHeartbeat = (params: {
|
|
138
|
+
sessionId: string
|
|
139
|
+
ts: number
|
|
140
|
+
}): RuntimeHeartbeat => ({
|
|
141
|
+
protocolVersion: getCurrentProtocolVersion(),
|
|
142
|
+
supportedProtocolRange: DEFAULT_SUPPORTED_PROTOCOL_RANGE,
|
|
143
|
+
sessionId: params.sessionId,
|
|
144
|
+
runtimeId: 'pending_engine_consumer',
|
|
145
|
+
status: 'starting',
|
|
146
|
+
updatedAt: params.ts
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
export const readRuntimeStatus = async (
|
|
150
|
+
cwd: string,
|
|
151
|
+
sessionId: string,
|
|
152
|
+
env: NodeJS.ProcessEnv = process.env
|
|
153
|
+
) => {
|
|
154
|
+
const session = (await getStore(cwd, env)).session(sessionId)
|
|
155
|
+
if (!existsSync(session.sessionPath)) throw new Error(`Runtime session "${sessionId}" not found.`)
|
|
156
|
+
const [meta, state, heartbeat] = await Promise.all([
|
|
157
|
+
session.readMeta(),
|
|
158
|
+
session.readState(),
|
|
159
|
+
session.readHeartbeat()
|
|
160
|
+
])
|
|
161
|
+
return {
|
|
162
|
+
sessionId,
|
|
163
|
+
storePath: session.sessionPath,
|
|
164
|
+
status: state?.status ?? heartbeat?.status ?? 'starting',
|
|
165
|
+
title: String(state?.title ?? meta?.title ?? ''),
|
|
166
|
+
meta,
|
|
167
|
+
state,
|
|
168
|
+
heartbeat
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
|
|
4
|
+
import { DEFAULT_SUPPORTED_PROTOCOL_RANGE, getCurrentProtocolVersion } from '@oneworks/runtime-protocol'
|
|
5
|
+
import { FileRuntimeStore, resolveRuntimeRoot } from '@oneworks/runtime-store'
|
|
6
|
+
import type { RuntimeCommand } from '@oneworks/runtime-store'
|
|
7
|
+
|
|
8
|
+
export type RuntimeCommandType =
|
|
9
|
+
| 'start'
|
|
10
|
+
| 'send_message'
|
|
11
|
+
| 'stop'
|
|
12
|
+
| 'kill'
|
|
13
|
+
| 'submit_input'
|
|
14
|
+
| 'resume'
|
|
15
|
+
|
|
16
|
+
export interface CreateRuntimeSessionParams {
|
|
17
|
+
cwd: string
|
|
18
|
+
entity: string
|
|
19
|
+
title?: string
|
|
20
|
+
message: string
|
|
21
|
+
adapter?: string
|
|
22
|
+
effort?: 'low' | 'medium' | 'high' | 'max'
|
|
23
|
+
model?: string
|
|
24
|
+
commandId?: string
|
|
25
|
+
env?: NodeJS.ProcessEnv
|
|
26
|
+
hostSessionId?: string
|
|
27
|
+
memberAvatar?: string
|
|
28
|
+
memberLabel?: string
|
|
29
|
+
memberKey?: string
|
|
30
|
+
now?: () => number
|
|
31
|
+
parentSessionId?: string
|
|
32
|
+
priority?: number
|
|
33
|
+
roomId?: string
|
|
34
|
+
roomTitle?: string
|
|
35
|
+
runId?: string
|
|
36
|
+
runTitle?: string
|
|
37
|
+
sessionId?: string
|
|
38
|
+
source?: string
|
|
39
|
+
permissionMode?: 'default' | 'acceptEdits' | 'plan' | 'dontAsk' | 'bypassPermissions'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface AppendRuntimeCommandParams {
|
|
43
|
+
cwd: string
|
|
44
|
+
sessionId: string
|
|
45
|
+
type: Exclude<RuntimeCommandType, 'start'>
|
|
46
|
+
commandId?: string
|
|
47
|
+
message?: string
|
|
48
|
+
memberKey?: string
|
|
49
|
+
priority?: number
|
|
50
|
+
requestId?: string
|
|
51
|
+
roomId?: string
|
|
52
|
+
runId?: string
|
|
53
|
+
source?: string
|
|
54
|
+
value?: unknown
|
|
55
|
+
data?: string | string[]
|
|
56
|
+
env?: NodeJS.ProcessEnv
|
|
57
|
+
now?: () => number
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const trimRequired = (value: string | undefined, name: string) => {
|
|
61
|
+
const normalized = value?.trim() ?? ''
|
|
62
|
+
if (normalized === '') throw new Error(`${name} is required.`)
|
|
63
|
+
return normalized
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const getStore = async (cwd: string, env: NodeJS.ProcessEnv = process.env) =>
|
|
67
|
+
new FileRuntimeStore(await resolveRuntimeRoot({ cwd, env }))
|
|
68
|
+
|
|
69
|
+
const getCommandPriority = (type: RuntimeCommandType) => {
|
|
70
|
+
switch (type) {
|
|
71
|
+
case 'stop':
|
|
72
|
+
case 'kill':
|
|
73
|
+
return 0
|
|
74
|
+
case 'submit_input':
|
|
75
|
+
return 10
|
|
76
|
+
case 'start':
|
|
77
|
+
case 'send_message':
|
|
78
|
+
case 'resume':
|
|
79
|
+
return 20
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const commandTypeToMode = (type: RuntimeCommandType) => {
|
|
84
|
+
if (type === 'stop') return 'graceful'
|
|
85
|
+
if (type === 'kill') return 'force'
|
|
86
|
+
return undefined
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const createSessionId = () => `sess_${randomUUID()}`
|
|
90
|
+
|
|
91
|
+
export const buildCommand = (params: {
|
|
92
|
+
sessionId: string
|
|
93
|
+
type: RuntimeCommandType
|
|
94
|
+
ts: number
|
|
95
|
+
content?: string
|
|
96
|
+
commandId?: string
|
|
97
|
+
requestId?: string
|
|
98
|
+
value?: unknown
|
|
99
|
+
data?: string | string[]
|
|
100
|
+
entity?: string
|
|
101
|
+
adapter?: string
|
|
102
|
+
effort?: 'low' | 'medium' | 'high' | 'max'
|
|
103
|
+
model?: string
|
|
104
|
+
memberKey?: string
|
|
105
|
+
permissionMode?: 'default' | 'acceptEdits' | 'plan' | 'dontAsk' | 'bypassPermissions'
|
|
106
|
+
roomId?: string
|
|
107
|
+
runId?: string
|
|
108
|
+
priority?: number
|
|
109
|
+
source?: string
|
|
110
|
+
title?: string
|
|
111
|
+
}): RuntimeCommand => {
|
|
112
|
+
const command: RuntimeCommand = {
|
|
113
|
+
protocolVersion: getCurrentProtocolVersion(),
|
|
114
|
+
supportedProtocolRange: DEFAULT_SUPPORTED_PROTOCOL_RANGE,
|
|
115
|
+
id: `cmd_${params.type}_${randomUUID()}`,
|
|
116
|
+
ts: params.ts,
|
|
117
|
+
sessionId: params.sessionId,
|
|
118
|
+
type: params.type,
|
|
119
|
+
priority: params.priority ?? getCommandPriority(params.type),
|
|
120
|
+
source: params.source ?? 'cli'
|
|
121
|
+
}
|
|
122
|
+
if (params.commandId != null) command.commandId = params.commandId
|
|
123
|
+
if (params.content != null) command.content = params.content
|
|
124
|
+
if (params.requestId != null) command.requestId = params.requestId
|
|
125
|
+
if (params.value != null) command.value = params.value
|
|
126
|
+
if (params.data != null) command.data = params.data
|
|
127
|
+
if (params.entity != null) command.entity = params.entity
|
|
128
|
+
if (params.adapter != null) command.adapter = params.adapter
|
|
129
|
+
if (params.effort != null) command.effort = params.effort
|
|
130
|
+
if (params.model != null) command.model = params.model
|
|
131
|
+
if (params.permissionMode != null) command.permissionMode = params.permissionMode
|
|
132
|
+
if (params.memberKey != null) command.memberKey = params.memberKey
|
|
133
|
+
if (params.roomId != null) command.roomId = params.roomId
|
|
134
|
+
if (params.runId != null) command.runId = params.runId
|
|
135
|
+
if (params.title != null) command.title = params.title
|
|
136
|
+
const mode = commandTypeToMode(params.type)
|
|
137
|
+
if (mode != null) command.mode = mode
|
|
138
|
+
return command
|
|
139
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { runAgentAction, runAgentCommand, runAgentEvents, runAgentStart, runAgentStatus } from './agent/actions'
|
|
4
|
+
import type { AgentEventsOptions, AgentSessionOptions, AgentStartOptions } from './agent/actions'
|
|
5
|
+
|
|
6
|
+
export function registerAgentCommand(program: Command) {
|
|
7
|
+
const agentCommand = program
|
|
8
|
+
.command('agent')
|
|
9
|
+
.description('Operate agent runtime sessions through the runtime store protocol')
|
|
10
|
+
|
|
11
|
+
agentCommand
|
|
12
|
+
.command('start')
|
|
13
|
+
.description('Create a runtime session store and queue the initial start command')
|
|
14
|
+
.requiredOption('--entity <entity>', 'Entity key to start, for example dev or qa')
|
|
15
|
+
.requiredOption('--message <message>', 'Initial user message for the runtime session')
|
|
16
|
+
.option('--title <title>', 'Display title for the runtime session')
|
|
17
|
+
.option('--host-session <session>', 'Parent host session id for automatic room binding')
|
|
18
|
+
.option('--room <room>', 'Existing agent room id to attach to')
|
|
19
|
+
.option('--room-title <title>', 'Agent room title hint when a room is created')
|
|
20
|
+
.option('--avatar <avatar>', 'Avatar label shown for this room member')
|
|
21
|
+
.option('--json', 'Print JSON output', false)
|
|
22
|
+
.action((opts: AgentStartOptions) => runAgentAction(() => runAgentStart(opts)))
|
|
23
|
+
|
|
24
|
+
agentCommand
|
|
25
|
+
.command('send')
|
|
26
|
+
.description('Queue a message for an existing runtime session')
|
|
27
|
+
.requiredOption('--session <session>', 'Runtime session id')
|
|
28
|
+
.requiredOption('--message <message>', 'Message to send')
|
|
29
|
+
.option('--json', 'Print JSON output', false)
|
|
30
|
+
.action((opts: AgentSessionOptions & { session: string }) =>
|
|
31
|
+
runAgentAction(() => runAgentCommand(opts, 'send_message'))
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
agentCommand
|
|
35
|
+
.command('stop')
|
|
36
|
+
.description('Queue a graceful stop command for an existing runtime session')
|
|
37
|
+
.requiredOption('--session <session>', 'Runtime session id')
|
|
38
|
+
.option('--json', 'Print JSON output', false)
|
|
39
|
+
.action((opts: AgentSessionOptions & { session: string }) => runAgentAction(() => runAgentCommand(opts, 'stop')))
|
|
40
|
+
|
|
41
|
+
agentCommand
|
|
42
|
+
.command('kill')
|
|
43
|
+
.description('Queue a force-kill command for an existing runtime session')
|
|
44
|
+
.requiredOption('--session <session>', 'Runtime session id')
|
|
45
|
+
.option('--json', 'Print JSON output', false)
|
|
46
|
+
.action((opts: AgentSessionOptions & { session: string }) => runAgentAction(() => runAgentCommand(opts, 'kill')))
|
|
47
|
+
|
|
48
|
+
agentCommand
|
|
49
|
+
.command('submit')
|
|
50
|
+
.description('Queue a response to a pending runtime input request')
|
|
51
|
+
.requiredOption('--session <session>', 'Runtime session id')
|
|
52
|
+
.requiredOption('--request <request>', 'Pending input or approval request id')
|
|
53
|
+
.requiredOption('--value <value>', 'Submitted value')
|
|
54
|
+
.option('--json', 'Print JSON output', false)
|
|
55
|
+
.action((opts: AgentSessionOptions & { session: string }) =>
|
|
56
|
+
runAgentAction(() => runAgentCommand(opts, 'submit_input'))
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
agentCommand
|
|
60
|
+
.command('resume')
|
|
61
|
+
.description('Queue a resume command and optional follow-up message')
|
|
62
|
+
.requiredOption('--session <session>', 'Runtime session id')
|
|
63
|
+
.requiredOption('--message <message>', 'Resume message')
|
|
64
|
+
.option('--json', 'Print JSON output', false)
|
|
65
|
+
.action((opts: AgentSessionOptions & { session: string }) => runAgentAction(() => runAgentCommand(opts, 'resume')))
|
|
66
|
+
|
|
67
|
+
agentCommand
|
|
68
|
+
.command('status')
|
|
69
|
+
.description('Read runtime session status from state, heartbeat, and metadata files')
|
|
70
|
+
.requiredOption('--session <session>', 'Runtime session id')
|
|
71
|
+
.option('--json', 'Print JSON output', false)
|
|
72
|
+
.action((opts: AgentSessionOptions & { session: string }) => runAgentAction(() => runAgentStatus(opts)))
|
|
73
|
+
|
|
74
|
+
agentCommand
|
|
75
|
+
.command('events')
|
|
76
|
+
.description('Read or follow runtime session events')
|
|
77
|
+
.requiredOption('--session <session>', 'Runtime session id')
|
|
78
|
+
.option('--follow', 'Keep following the events.jsonl file', false)
|
|
79
|
+
.option('--jsonl', 'Print JSON Lines output', false)
|
|
80
|
+
.action((opts: AgentEventsOptions & { session: string }) => runAgentAction(() => runAgentEvents(opts)))
|
|
81
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getBenchmarkCase,
|
|
5
|
+
listBenchmarkCases,
|
|
6
|
+
listBenchmarkCategories,
|
|
7
|
+
readBenchmarkResult,
|
|
8
|
+
runBenchmarkCase,
|
|
9
|
+
runBenchmarkCategory
|
|
10
|
+
} from '@oneworks/app-runtime'
|
|
11
|
+
import type { BenchmarkCase, BenchmarkCategory, BenchmarkResult } from '@oneworks/types'
|
|
12
|
+
import type { Command } from 'commander'
|
|
13
|
+
|
|
14
|
+
import { createAdapterOption } from './@core/adapter-option'
|
|
15
|
+
import { resolvePermissionModeOption } from './run/options'
|
|
16
|
+
|
|
17
|
+
interface BenchmarkRunOptions {
|
|
18
|
+
category: string
|
|
19
|
+
title?: string
|
|
20
|
+
concurrency?: string
|
|
21
|
+
adapter?: string
|
|
22
|
+
model?: string
|
|
23
|
+
effort?: 'low' | 'medium' | 'high' | 'max'
|
|
24
|
+
systemPrompt?: string
|
|
25
|
+
permissionMode?: 'default' | 'acceptEdits' | 'plan' | 'dontAsk' | 'bypassPermissions'
|
|
26
|
+
yolo?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const printResult = (result: BenchmarkResult) => {
|
|
30
|
+
console.table([{
|
|
31
|
+
Category: result.category,
|
|
32
|
+
Title: result.title,
|
|
33
|
+
Status: result.status,
|
|
34
|
+
Score: result.finalScore,
|
|
35
|
+
Test: result.scores.testScore,
|
|
36
|
+
Goal: result.scores.goalScore,
|
|
37
|
+
Reference: result.scores.referenceScore,
|
|
38
|
+
DurationMs: result.durationMs,
|
|
39
|
+
Timestamp: result.timestamp
|
|
40
|
+
}])
|
|
41
|
+
|
|
42
|
+
if (result.issues.length > 0) {
|
|
43
|
+
console.log('\nIssues:')
|
|
44
|
+
for (const issue of result.issues) {
|
|
45
|
+
console.log(`- ${issue}`)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const createEventPrinter = () => {
|
|
51
|
+
return (event: {
|
|
52
|
+
category: string
|
|
53
|
+
title?: string
|
|
54
|
+
phase: string
|
|
55
|
+
message: string
|
|
56
|
+
}) => {
|
|
57
|
+
const target = event.title ? `${event.category}/${event.title}` : event.category
|
|
58
|
+
console.log(`[benchmark:${target}] [${event.phase}] ${event.message}`)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function registerBenchmarkCommand(program: Command) {
|
|
63
|
+
const benchmark = program
|
|
64
|
+
.command('benchmark')
|
|
65
|
+
.description('List and run benchmark cases')
|
|
66
|
+
|
|
67
|
+
benchmark
|
|
68
|
+
.command('list')
|
|
69
|
+
.description('List benchmark categories and cases')
|
|
70
|
+
.option('--category <category>', 'Filter by category')
|
|
71
|
+
.action(async (opts: { category?: string }) => {
|
|
72
|
+
const categories = await listBenchmarkCategories()
|
|
73
|
+
const cases = await listBenchmarkCases({
|
|
74
|
+
category: opts.category
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
if (categories.length === 0) {
|
|
78
|
+
console.log('No benchmark cases found.')
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log('Categories:')
|
|
83
|
+
console.table(categories.map((item: BenchmarkCategory) => ({
|
|
84
|
+
Category: item.category,
|
|
85
|
+
Cases: item.caseCount,
|
|
86
|
+
Pass: item.lastStatuses.pass,
|
|
87
|
+
Partial: item.lastStatuses.partial,
|
|
88
|
+
Fail: item.lastStatuses.fail
|
|
89
|
+
})))
|
|
90
|
+
|
|
91
|
+
if (cases.length === 0) {
|
|
92
|
+
console.log(`No cases found for category ${opts.category}.`)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log('\nCases:')
|
|
97
|
+
console.table(cases.map((item: BenchmarkCase) => ({
|
|
98
|
+
Category: item.category,
|
|
99
|
+
Title: item.title,
|
|
100
|
+
Summary: item.summary,
|
|
101
|
+
Status: item.latestResult?.status ?? '-',
|
|
102
|
+
Score: item.latestResult?.finalScore ?? '-'
|
|
103
|
+
})))
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
benchmark
|
|
107
|
+
.command('run')
|
|
108
|
+
.description('Run one benchmark case or an entire category')
|
|
109
|
+
.requiredOption('--category <category>', 'Benchmark category')
|
|
110
|
+
.option('--title <title>', 'Benchmark title')
|
|
111
|
+
.option('--concurrency <count>', 'Category concurrency', '2')
|
|
112
|
+
.addOption(createAdapterOption('Adapter to use'))
|
|
113
|
+
.option('--model <model>', 'Model to use')
|
|
114
|
+
.option('--effort <effort>', 'Effort to use (low, medium, high, max)')
|
|
115
|
+
.option('--system-prompt <prompt>', 'Additional system prompt')
|
|
116
|
+
.option('--permission-mode <mode>', 'Permission mode')
|
|
117
|
+
.option('--yolo', 'Shortcut for --permission-mode bypassPermissions', false)
|
|
118
|
+
.action(async (opts: BenchmarkRunOptions) => {
|
|
119
|
+
opts.permissionMode = resolvePermissionModeOption(opts.permissionMode, opts.yolo)
|
|
120
|
+
const onEvent = createEventPrinter()
|
|
121
|
+
if (opts.title != null && opts.title !== '') {
|
|
122
|
+
const output = await runBenchmarkCase({
|
|
123
|
+
category: opts.category,
|
|
124
|
+
title: opts.title,
|
|
125
|
+
adapter: opts.adapter,
|
|
126
|
+
model: opts.model,
|
|
127
|
+
effort: opts.effort,
|
|
128
|
+
systemPrompt: opts.systemPrompt,
|
|
129
|
+
permissionMode: opts.permissionMode,
|
|
130
|
+
runtime: 'cli',
|
|
131
|
+
onEvent
|
|
132
|
+
})
|
|
133
|
+
printResult(output.result)
|
|
134
|
+
process.exit(output.result.status === 'fail' ? 1 : 0)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const concurrency = Number.parseInt(opts.concurrency ?? '2', 10)
|
|
138
|
+
const output = await runBenchmarkCategory({
|
|
139
|
+
category: opts.category,
|
|
140
|
+
concurrency: Number.isNaN(concurrency) ? 2 : concurrency,
|
|
141
|
+
adapter: opts.adapter,
|
|
142
|
+
model: opts.model,
|
|
143
|
+
effort: opts.effort,
|
|
144
|
+
systemPrompt: opts.systemPrompt,
|
|
145
|
+
permissionMode: opts.permissionMode,
|
|
146
|
+
runtime: 'cli',
|
|
147
|
+
onEvent
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
console.table(output.results.map((result: BenchmarkResult) => ({
|
|
151
|
+
Title: result.title,
|
|
152
|
+
Status: result.status,
|
|
153
|
+
Score: result.finalScore,
|
|
154
|
+
Test: result.scores.testScore,
|
|
155
|
+
Goal: result.scores.goalScore,
|
|
156
|
+
Reference: result.scores.referenceScore
|
|
157
|
+
})))
|
|
158
|
+
|
|
159
|
+
const hasFailure = output.results.some((result: BenchmarkResult) => result.status === 'fail')
|
|
160
|
+
process.exit(hasFailure ? 1 : 0)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
benchmark
|
|
164
|
+
.command('show')
|
|
165
|
+
.description('Show benchmark case metadata and latest result')
|
|
166
|
+
.requiredOption('--category <category>', 'Benchmark category')
|
|
167
|
+
.requiredOption('--title <title>', 'Benchmark title')
|
|
168
|
+
.action(async (opts: { category: string; title: string }) => {
|
|
169
|
+
const caseItem = await getBenchmarkCase(opts)
|
|
170
|
+
if (caseItem == null) {
|
|
171
|
+
console.error(`Benchmark case not found: ${opts.category}/${opts.title}`)
|
|
172
|
+
process.exit(1)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log('Case:')
|
|
176
|
+
console.table([{
|
|
177
|
+
Category: caseItem.category,
|
|
178
|
+
Title: caseItem.title,
|
|
179
|
+
Summary: caseItem.summary,
|
|
180
|
+
BaseCommit: caseItem.frontmatter.baseCommit,
|
|
181
|
+
Setup: caseItem.frontmatter.setupCommand,
|
|
182
|
+
Test: caseItem.frontmatter.testCommand,
|
|
183
|
+
TimeoutSec: caseItem.frontmatter.timeoutSec
|
|
184
|
+
}])
|
|
185
|
+
|
|
186
|
+
console.log('\nRFC:')
|
|
187
|
+
console.log(caseItem.rfcBody)
|
|
188
|
+
|
|
189
|
+
const result = await readBenchmarkResult(process.cwd(), opts.category, opts.title)
|
|
190
|
+
if (result == null) {
|
|
191
|
+
console.log('\nNo result.json found.')
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log('\nLatest Result:')
|
|
196
|
+
printResult(result)
|
|
197
|
+
})
|
|
198
|
+
}
|