@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,232 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import fs from 'node:fs/promises'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import process from 'node:process'
|
|
5
|
+
|
|
6
|
+
import { mergeProcessEnvWithProjectEnv, resolveProjectHomePath, resolveProjectOoBaseDirName } from '@oneworks/utils'
|
|
7
|
+
import type { Command } from 'commander'
|
|
8
|
+
|
|
9
|
+
import { collectReportTargets } from './report-targets'
|
|
10
|
+
|
|
11
|
+
export { collectReportTargets } from './report-targets'
|
|
12
|
+
|
|
13
|
+
const pad = (value: number) => String(value).padStart(2, '0')
|
|
14
|
+
|
|
15
|
+
export const formatReportTimestamp = (date: Date) => {
|
|
16
|
+
const day = [
|
|
17
|
+
date.getUTCFullYear(),
|
|
18
|
+
pad(date.getUTCMonth() + 1),
|
|
19
|
+
pad(date.getUTCDate())
|
|
20
|
+
].join('')
|
|
21
|
+
const time = [
|
|
22
|
+
pad(date.getUTCHours()),
|
|
23
|
+
pad(date.getUTCMinutes()),
|
|
24
|
+
pad(date.getUTCSeconds())
|
|
25
|
+
].join('')
|
|
26
|
+
|
|
27
|
+
return `${day}T${time}Z`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const resolveReportArchivePath = (cwd: string, filename?: string) => {
|
|
31
|
+
const baseName = filename?.trim()
|
|
32
|
+
? filename.trim()
|
|
33
|
+
: `report-${formatReportTimestamp(new Date())}`
|
|
34
|
+
|
|
35
|
+
const archiveName = baseName.endsWith('.tar.gz') || baseName.endsWith('.tgz')
|
|
36
|
+
? baseName
|
|
37
|
+
: `${baseName}.tar.gz`
|
|
38
|
+
|
|
39
|
+
return path.resolve(cwd, archiveName)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const assertArchivePath = (archivePath: string, sources: string[]) => {
|
|
43
|
+
const resolvedArchivePath = path.resolve(archivePath)
|
|
44
|
+
|
|
45
|
+
for (const source of sources) {
|
|
46
|
+
if (
|
|
47
|
+
resolvedArchivePath === source ||
|
|
48
|
+
resolvedArchivePath.startsWith(`${source}${path.sep}`)
|
|
49
|
+
) {
|
|
50
|
+
throw new Error(`Report archive must not be created inside ${source}.`)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const toTarPath = (value: string) => value.split(path.sep).join('/')
|
|
56
|
+
|
|
57
|
+
const isPathInside = (targetPath: string, sourcePath: string) => {
|
|
58
|
+
const relativePath = path.relative(path.resolve(sourcePath), path.resolve(targetPath))
|
|
59
|
+
return relativePath === '' || (
|
|
60
|
+
relativePath !== '..' &&
|
|
61
|
+
!relativePath.startsWith(`..${path.sep}`) &&
|
|
62
|
+
!path.isAbsolute(relativePath)
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const resolveCommonArchiveRoot = (sources: string[]) => {
|
|
67
|
+
const resolvedSources = sources.map(source => path.dirname(path.resolve(source)))
|
|
68
|
+
const first = resolvedSources[0]
|
|
69
|
+
if (first == null) return process.cwd()
|
|
70
|
+
|
|
71
|
+
const root = path.parse(first).root
|
|
72
|
+
let commonParts = first.slice(root.length).split(path.sep).filter(Boolean)
|
|
73
|
+
|
|
74
|
+
for (const source of resolvedSources.slice(1)) {
|
|
75
|
+
const parsed = path.parse(source)
|
|
76
|
+
if (parsed.root !== root) return parsed.root
|
|
77
|
+
|
|
78
|
+
const parts = source.slice(root.length).split(path.sep).filter(Boolean)
|
|
79
|
+
let index = 0
|
|
80
|
+
while (index < commonParts.length && commonParts[index] === parts[index]) {
|
|
81
|
+
index += 1
|
|
82
|
+
}
|
|
83
|
+
commonParts = commonParts.slice(0, index)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return path.resolve(root, ...commonParts)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const collectUnsafeSymlinkArchivePaths = async (params: {
|
|
90
|
+
archiveRoot: string
|
|
91
|
+
allowedRoot: string
|
|
92
|
+
source: string
|
|
93
|
+
}) => {
|
|
94
|
+
const unsafePaths: string[] = []
|
|
95
|
+
|
|
96
|
+
const visit = async (targetPath: string) => {
|
|
97
|
+
const info = await fs.lstat(targetPath).catch(() => undefined)
|
|
98
|
+
if (info == null) return
|
|
99
|
+
|
|
100
|
+
if (info.isSymbolicLink()) {
|
|
101
|
+
const linkTarget = await fs.readlink(targetPath).catch(() => undefined)
|
|
102
|
+
if (linkTarget == null) return
|
|
103
|
+
|
|
104
|
+
const resolvedTarget = path.isAbsolute(linkTarget)
|
|
105
|
+
? path.resolve(linkTarget)
|
|
106
|
+
: path.resolve(path.dirname(targetPath), linkTarget)
|
|
107
|
+
if (path.isAbsolute(linkTarget) || !isPathInside(resolvedTarget, params.allowedRoot)) {
|
|
108
|
+
unsafePaths.push(toTarPath(path.relative(params.archiveRoot, targetPath)))
|
|
109
|
+
}
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!info.isDirectory()) return
|
|
114
|
+
|
|
115
|
+
const entries = await fs.readdir(targetPath).catch(() => [])
|
|
116
|
+
await Promise.all(entries.map(entry => visit(path.join(targetPath, entry))))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await visit(params.source)
|
|
120
|
+
return unsafePaths
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const createTarArchive = async (cwd: string, env: NodeJS.ProcessEnv, archivePath: string, sources: string[]) => {
|
|
124
|
+
const archiveRoot = resolveCommonArchiveRoot(sources)
|
|
125
|
+
const archiveSources = sources.map(source => toTarPath(path.relative(archiveRoot, source)))
|
|
126
|
+
const mockConfigSources = archiveSources.filter(source => (
|
|
127
|
+
source === '.mock/.config' || source.endsWith('/.mock/.config') || source.includes('/.mock/.config/')
|
|
128
|
+
))
|
|
129
|
+
const tarExcludes = mockConfigSources.flatMap(source => [
|
|
130
|
+
`${source}/**/node_modules`,
|
|
131
|
+
`${source}/**/node_modules/*`
|
|
132
|
+
])
|
|
133
|
+
const projectHomeDir = resolveProjectHomePath(cwd, env)
|
|
134
|
+
tarExcludes.push(
|
|
135
|
+
...(await Promise.all(sources.map(source =>
|
|
136
|
+
collectUnsafeSymlinkArchivePaths({
|
|
137
|
+
archiveRoot,
|
|
138
|
+
allowedRoot: projectHomeDir,
|
|
139
|
+
source
|
|
140
|
+
})
|
|
141
|
+
))).flat()
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
await fs.mkdir(path.dirname(archivePath), { recursive: true })
|
|
145
|
+
|
|
146
|
+
await new Promise<void>((resolve, reject) => {
|
|
147
|
+
const child = spawn('tar', [
|
|
148
|
+
'-czf',
|
|
149
|
+
archivePath,
|
|
150
|
+
...tarExcludes.map(pattern => `--exclude=${pattern}`),
|
|
151
|
+
'-C',
|
|
152
|
+
archiveRoot,
|
|
153
|
+
...archiveSources
|
|
154
|
+
], {
|
|
155
|
+
cwd: archiveRoot,
|
|
156
|
+
stdio: ['ignore', 'ignore', 'pipe']
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
let stderr = ''
|
|
160
|
+
|
|
161
|
+
child.stderr.on('data', (chunk) => {
|
|
162
|
+
stderr += String(chunk)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
child.on('error', (error) => {
|
|
166
|
+
const err = error as NodeJS.ErrnoException
|
|
167
|
+
if (err.code === 'ENOENT') {
|
|
168
|
+
reject(new Error('Failed to create report archive: `tar` command not found.'))
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
reject(error)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
child.on('close', (code) => {
|
|
175
|
+
if (code === 0) {
|
|
176
|
+
resolve()
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const message = stderr.trim()
|
|
181
|
+
reject(
|
|
182
|
+
new Error(
|
|
183
|
+
message
|
|
184
|
+
? `Failed to create report archive: ${message}`
|
|
185
|
+
: `Failed to create report archive with exit code ${code ?? -1}.`
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface RunReportCommandOptions {
|
|
193
|
+
cwd?: string
|
|
194
|
+
filename?: string
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function runReportCommand(options: RunReportCommandOptions = {}) {
|
|
198
|
+
const cwd = options.cwd ?? process.cwd()
|
|
199
|
+
const env = mergeProcessEnvWithProjectEnv(undefined, { workspaceFolder: cwd })
|
|
200
|
+
const sources = await collectReportTargets(cwd, env)
|
|
201
|
+
|
|
202
|
+
if (sources.length === 0) {
|
|
203
|
+
console.log(
|
|
204
|
+
`No reportable files found under ${resolveProjectOoBaseDirName(env)} assets or ${
|
|
205
|
+
resolveProjectHomePath(cwd, env)
|
|
206
|
+
} runtime data.`
|
|
207
|
+
)
|
|
208
|
+
return null
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const archivePath = resolveReportArchivePath(cwd, options.filename)
|
|
212
|
+
assertArchivePath(archivePath, sources)
|
|
213
|
+
await createTarArchive(cwd, env, archivePath, sources)
|
|
214
|
+
|
|
215
|
+
console.log(`Report archive created: ${archivePath}`)
|
|
216
|
+
|
|
217
|
+
return { archivePath, sources }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function registerReportCommand(program: Command) {
|
|
221
|
+
program
|
|
222
|
+
.command('report [filename]')
|
|
223
|
+
.description('Package project-home logs, caches and selected mock data into a compressed archive')
|
|
224
|
+
.action(async (filename?: string) => {
|
|
225
|
+
try {
|
|
226
|
+
await runReportCommand({ filename })
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
229
|
+
process.exit(1)
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
buildConfigJsonVariables,
|
|
5
|
+
buildConfigSections,
|
|
6
|
+
loadConfigState,
|
|
7
|
+
resolveConfigSectionPath,
|
|
8
|
+
setConfigSectionValueAtPath,
|
|
9
|
+
updateConfigFile,
|
|
10
|
+
validateConfigSection
|
|
11
|
+
} from '@oneworks/config'
|
|
12
|
+
|
|
13
|
+
const normalizeAdapterEnvPrefix = (adapter: string) => (
|
|
14
|
+
`__ONEWORKS_PROJECT_ADAPTER_${adapter.replace(/[^a-z0-9]+/giu, '_').toUpperCase()}`
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const formatValidationIssues = (
|
|
18
|
+
issues: Array<{
|
|
19
|
+
path: Array<string | number>
|
|
20
|
+
message: string
|
|
21
|
+
}>
|
|
22
|
+
) =>
|
|
23
|
+
issues
|
|
24
|
+
.map((issue) => `${issue.path.length > 0 ? issue.path.join('.') : '<root>'}: ${issue.message}`)
|
|
25
|
+
.join('\n')
|
|
26
|
+
|
|
27
|
+
export const applyAdapterCliVersionEnv = (
|
|
28
|
+
env: NodeJS.ProcessEnv,
|
|
29
|
+
adapter: string,
|
|
30
|
+
version: string
|
|
31
|
+
) => {
|
|
32
|
+
env[`${normalizeAdapterEnvPrefix(adapter)}_INSTALL_VERSION__`] = version
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const persistAdapterCliVersionSelection = async (params: {
|
|
36
|
+
adapter: string
|
|
37
|
+
cwd: string
|
|
38
|
+
env?: Record<string, string | null | undefined>
|
|
39
|
+
version: string
|
|
40
|
+
}) => {
|
|
41
|
+
const state = await loadConfigState({
|
|
42
|
+
cwd: params.cwd,
|
|
43
|
+
jsonVariables: buildConfigJsonVariables(params.cwd, params.env ?? process.env)
|
|
44
|
+
})
|
|
45
|
+
const path = resolveConfigSectionPath(['adapters', params.adapter, 'cli', 'version'])
|
|
46
|
+
const updatedSections = setConfigSectionValueAtPath(
|
|
47
|
+
buildConfigSections(state.userSource?.rawConfig),
|
|
48
|
+
path,
|
|
49
|
+
params.version
|
|
50
|
+
)
|
|
51
|
+
const parsed = await validateConfigSection('adapters', updatedSections.adapters, {
|
|
52
|
+
cwd: params.cwd
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (!parsed.success) {
|
|
56
|
+
throw new Error(`Invalid adapter CLI version config:\n${formatValidationIssues(parsed.error.issues)}`)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return await updateConfigFile({
|
|
60
|
+
workspaceFolder: params.cwd,
|
|
61
|
+
source: 'user',
|
|
62
|
+
section: 'adapters',
|
|
63
|
+
value: parsed.data
|
|
64
|
+
})
|
|
65
|
+
}
|