@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,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
+ }