@bibobonking/opensol 3.1.5 → 3.2.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 (53) hide show
  1. package/CHANGELOG.md +8 -2
  2. package/LICENSE +21 -0
  3. package/README.md +24 -20
  4. package/desktop/README.md +1 -1
  5. package/dist/assets/{index-C0yvs6Vl.js → index-BUVnnhRH.js} +1 -1
  6. package/dist/assets/index-CyxlAgKM.js +271 -0
  7. package/dist/assets/index-DFeF90S8.css +1 -0
  8. package/dist/index.html +2 -2
  9. package/package.json +1 -1
  10. package/server/README.md +15 -68
  11. package/server/daemon.mjs +6 -1
  12. package/server/lib/deploy.mjs +5 -10
  13. package/server/lib/shell.mjs +149 -0
  14. package/server/lib/shell.test.mjs +72 -0
  15. package/server/lib/tools.mjs +8 -44
  16. package/src/agent/adapters/anthropic.ts +2 -3
  17. package/src/agent/adapters/gemini.ts +2 -3
  18. package/src/agent/adapters/openai-responses.ts +2 -3
  19. package/src/agent/adapters/openai.ts +2 -3
  20. package/src/agent/adapters/shared.ts +3 -0
  21. package/src/components/ChatView.tsx +17 -54
  22. package/src/components/Composer.tsx +143 -135
  23. package/src/components/ContextBar.tsx +2 -2
  24. package/src/components/HomeDashboard.tsx +7 -4
  25. package/src/components/MessageItem.tsx +33 -1
  26. package/src/components/ModelDeck.tsx +5 -27
  27. package/src/components/SettingsModal.tsx +8 -0
  28. package/src/components/ToolCallCard.tsx +2 -2
  29. package/src/components/TrajectoryView.tsx +1 -1
  30. package/src/components/composer/PlusMenu.tsx +117 -0
  31. package/src/components/composer/Popover.tsx +109 -0
  32. package/src/components/composer/WorkspacePicker.tsx +102 -0
  33. package/src/core/plugins/tools/shell.ts +2 -2
  34. package/src/core/plugins/tools/subagent.ts +18 -8
  35. package/src/core/services/agent-loop.ts +45 -43
  36. package/src/core/services/compaction.ts +289 -22
  37. package/src/core/services/llm.ts +6 -2
  38. package/src/core/services/subagents.ts +191 -3
  39. package/src/core/services/system-prompt.ts +35 -7
  40. package/src/core/services/tools.ts +2 -1
  41. package/src/core/types.ts +5 -0
  42. package/src/lib/compact-plan.test.mjs +62 -0
  43. package/src/lib/compact-plan.ts +188 -0
  44. package/src/lib/subagent-types.ts +54 -0
  45. package/src/lib/tool-meta.ts +40 -0
  46. package/src/store/settings.ts +3 -0
  47. package/src/store/ui.ts +12 -0
  48. package/src/types.ts +26 -0
  49. package/shared/plugin-builder.test.mjs +0 -43
  50. package/shared/plugin-package.d.mts +0 -34
  51. package/shared/plugin-package.mjs +0 -197
  52. package/shared/plugin-package.test.mjs +0 -94
  53. package/tsconfig.tsbuildinfo +0 -1
@@ -1,197 +0,0 @@
1
- const MAX_ARCHIVE_BYTES = 5 * 1024 * 1024
2
- const MAX_PACKAGE_BYTES = 10 * 1024 * 1024
3
- const MAX_FILES = 100
4
- const MAX_MANIFEST_BYTES = 64 * 1024
5
- const CAPABILITIES = new Set([
6
- 'workspace.list',
7
- 'workspace.read',
8
- 'workspace.write',
9
- 'workspace.edit',
10
- 'ui.commands',
11
- 'ui.panels',
12
- 'ui.themes',
13
- 'network.request',
14
- ])
15
- const MANIFEST_KEYS = new Set([
16
- 'id',
17
- 'name',
18
- 'version',
19
- 'apiVersion',
20
- 'entry',
21
- 'stylesheet',
22
- 'capabilities',
23
- 'domains',
24
- ])
25
-
26
- function packageError(message) {
27
- throw new Error(`Invalid plugin package: ${message}`)
28
- }
29
-
30
- function bytesToString(bytes) {
31
- if (typeof bytes === 'string') return bytes
32
- return new TextDecoder('utf-8').decode(bytes)
33
- }
34
-
35
- function stringToBytes(str) {
36
- return new TextEncoder().encode(str)
37
- }
38
-
39
- function validateSource(path, content, apiVersion) {
40
- // API v2: skip validation — full kernel access, blob isolation is enough
41
- if (apiVersion === 2) return
42
-
43
- const source = bytesToString(content)
44
- const rule = path.endsWith('.js')
45
- ? [
46
- /\b(?:import|export)\b|\b(?:fetch|XMLHttpRequest|WebSocket|EventSource|importScripts)\s*\(|\bnavigator\s*\.\s*sendBeacon\s*\(/i,
47
- 'module imports and unmanaged network calls are forbidden',
48
- ]
49
- : path.endsWith('.css')
50
- ? [
51
- /@import\b|url\s*\(\s*(['"]?)(?!data:image\/(?:png|jpeg|gif|webp);base64,)[^)]*\)/i,
52
- 'external CSS resources and imports are forbidden',
53
- ]
54
- : null
55
- if (!rule) return
56
- const found = rule[0].exec(source)
57
- if (!found) return
58
- const line = source.slice(0, found.index).split('\n').length
59
- const hit = found[0].length > 60 ? `${found[0].slice(0, 60)}…` : found[0]
60
- packageError(`${rule[1]}: found "${hit}" at ${path}:${line}`)
61
- }
62
-
63
- function safePath(value) {
64
- if (
65
- typeof value !== 'string' ||
66
- !value ||
67
- value.includes('\0') ||
68
- value.includes('\\') ||
69
- value.startsWith('/') ||
70
- /^[a-zA-Z]:/.test(value) ||
71
- value.split('/').some((part) => !part || part === '.' || part === '..')
72
- ) {
73
- packageError(`unsafe path "${String(value)}"`)
74
- }
75
- return value
76
- }
77
-
78
- function validateDomain(value) {
79
- if (typeof value !== 'string' || value.length > 253 || value.endsWith('.')) {
80
- packageError('invalid domain')
81
- }
82
- const labels = value.split('.')
83
- if (
84
- labels.length < 2 ||
85
- labels.some((label) => !/^(?=.{1,63}$)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(label))
86
- ) {
87
- packageError(`invalid domain "${value}"`)
88
- }
89
- return value.toLowerCase()
90
- }
91
-
92
- export function validatePluginManifest(value) {
93
- if (!value || typeof value !== 'object' || Array.isArray(value)) packageError('manifest must be an object')
94
- if (Object.keys(value).some((key) => !MANIFEST_KEYS.has(key))) packageError('unknown manifest field')
95
- const { id, name, version, apiVersion, entry, stylesheet, capabilities, domains } = value
96
- if (typeof id !== 'string' || !/^[a-z0-9][a-z0-9-]{1,63}$/.test(id)) packageError('invalid plugin id')
97
- if (typeof name !== 'string' || !name.trim() || name.length > 100) packageError('invalid plugin name')
98
- if (typeof version !== 'string' || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
99
- packageError('invalid plugin version')
100
- }
101
- if (apiVersion !== 1 && apiVersion !== 2) packageError('unsupported API version')
102
- safePath(entry)
103
- safePath(stylesheet)
104
- if (!entry.endsWith('.js') || !stylesheet.endsWith('.css')) packageError('entry must be JavaScript and stylesheet must be CSS')
105
-
106
- // API v2: capabilities optional (defaults to [])
107
- const normalizedCapabilities = capabilities ?? []
108
- if (!Array.isArray(normalizedCapabilities)) packageError('capabilities must be an array')
109
- if (apiVersion === 1 && normalizedCapabilities.some((item) => !CAPABILITIES.has(item))) {
110
- packageError('invalid capability')
111
- }
112
-
113
- if (domains !== undefined && !Array.isArray(domains)) packageError('domains must be an array')
114
- const normalizedDomains = (domains ?? []).map(validateDomain)
115
- if (new Set(normalizedDomains).size !== normalizedDomains.length) packageError('duplicate domain')
116
- return {
117
- id,
118
- name: name.trim(),
119
- version,
120
- apiVersion,
121
- entry,
122
- stylesheet,
123
- capabilities: [...new Set(normalizedCapabilities)],
124
- domains: normalizedDomains,
125
- }
126
- }
127
-
128
- function normalizeFiles(files, manifest) {
129
- if (!files || typeof files !== 'object' || Array.isArray(files)) packageError('files must be a path-to-source object')
130
- const paths = Object.keys(files)
131
- if (paths.length > MAX_FILES) packageError('too many files')
132
- const normalized = {}
133
- let total = 0
134
- for (const rawPath of paths) {
135
- const filePath = safePath(rawPath)
136
- if (filePath === 'manifest.json') packageError('manifest is generated from the manifest field')
137
- if (/\.(?:mjs|cjs|jsx|ts|tsx)$/i.test(filePath) && filePath !== manifest.entry) {
138
- packageError(`undeclared executable file "${filePath}"`)
139
- }
140
- if (filePath !== manifest.entry && filePath !== manifest.stylesheet && /\.(?:js|css)$/i.test(filePath)) {
141
- packageError(`undeclared executable file "${filePath}"`)
142
- }
143
- const content = files[rawPath]
144
- if (typeof content !== 'string' && !(content instanceof Uint8Array)) {
145
- packageError(`file "${filePath}" must be text or bytes`)
146
- }
147
- const data = typeof content === 'string' ? stringToBytes(content) : content
148
- total += data.byteLength
149
- if (total > MAX_PACKAGE_BYTES) packageError('package is too large')
150
- if (filePath === manifest.entry || filePath === manifest.stylesheet) validateSource(filePath, data, manifest.apiVersion)
151
- normalized[filePath] = typeof content === 'string' ? content : bytesToString(data)
152
- }
153
- if (!(manifest.entry in normalized)) packageError('entry file is missing')
154
- if (!(manifest.stylesheet in normalized)) packageError('stylesheet file is missing')
155
- if (stringToBytes(normalized[manifest.entry]).byteLength > 2 * 1024 * 1024) packageError('entry file is too large')
156
- return normalized
157
- }
158
-
159
- export function buildPluginArchive(sources) {
160
- const manifest = validatePluginManifest(sources?.manifest)
161
- const files = normalizeFiles(sources?.files, manifest)
162
- const manifestJson = JSON.stringify(manifest)
163
- if (stringToBytes(manifestJson).byteLength > MAX_MANIFEST_BYTES) packageError('manifest is too large')
164
-
165
- const packageData = {
166
- format: 'cordis-plugin',
167
- manifest,
168
- files,
169
- }
170
-
171
- const encoded = stringToBytes(JSON.stringify(packageData))
172
- if (encoded.byteLength > MAX_ARCHIVE_BYTES) packageError('package is too large')
173
- return encoded
174
- }
175
-
176
- export function readPluginArchive(bytes) {
177
- if (!bytes || !(bytes instanceof Uint8Array) || bytes.byteLength === 0) {
178
- packageError('package archive is empty or invalid')
179
- }
180
- let parsed
181
- try {
182
- const text = new TextDecoder('utf-8').decode(bytes)
183
- parsed = JSON.parse(text)
184
- } catch {
185
- packageError('package is not a valid JSON archive')
186
- }
187
-
188
- if (!parsed || typeof parsed !== 'object') packageError('invalid package format')
189
- const manifest = validatePluginManifest(parsed.manifest)
190
- const files = parsed.files
191
- if (!files || typeof files !== 'object') packageError('missing package files')
192
-
193
- return {
194
- manifest,
195
- files,
196
- }
197
- }
@@ -1,94 +0,0 @@
1
- import test from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { buildPluginArchive, readPluginArchive } from './plugin-package.mjs'
4
-
5
- const source = {
6
- manifest: {
7
- id: 'sample-plugin',
8
- name: 'Sample Plugin',
9
- version: '1.0.0',
10
- apiVersion: 1,
11
- entry: 'plugin.js',
12
- stylesheet: 'plugin.css',
13
- capabilities: ['workspace.read'],
14
- domains: ['api.example.test'],
15
- },
16
- files: {
17
- 'plugin.js': 'api.registerCommand("sample", () => "ok")',
18
- 'plugin.css': '.sample { color: red }',
19
- 'icon.svg': '<svg xmlns="http://www.w3.org/2000/svg"></svg>',
20
- },
21
- }
22
-
23
- test('plugin archive round-trips manifest and local source files', () => {
24
- const archive = buildPluginArchive(source)
25
- const result = readPluginArchive(archive)
26
- assert.deepEqual(result.manifest, source.manifest)
27
- assert.deepEqual(result.files, source.files)
28
- })
29
-
30
- test('plugin archive rejects traversal paths before packaging', () => {
31
- assert.throws(
32
- () => buildPluginArchive({ ...source, files: { ...source.files, '../outside.js': 'bad' } }),
33
- /path/i,
34
- )
35
- })
36
-
37
- test('plugin archive rejects a manifest whose entry is missing', () => {
38
- assert.throws(() => buildPluginArchive({ ...source, files: { 'plugin.css': 'x' } }), /entry/i)
39
- })
40
-
41
- test('plugin archive rejects undeclared executable source', () => {
42
- assert.throws(
43
- () => buildPluginArchive({ ...source, files: { ...source.files, 'extra.js': 'alert(1)' } }),
44
- /executable/i,
45
- )
46
- })
47
-
48
- test('plugin archive rejects remote code declarations and invalid domains', () => {
49
- assert.throws(
50
- () => buildPluginArchive({ ...source, manifest: { ...source.manifest, entry: 'https://evil.test/p.js' } }),
51
- /entry|path/i,
52
- )
53
- assert.throws(
54
- () => buildPluginArchive({ ...source, manifest: { ...source.manifest, domains: ['https://example.test'] } }),
55
- /domain/i,
56
- )
57
- })
58
-
59
- test('plugin archive rejects unmanaged network APIs and external CSS resources', () => {
60
- assert.throws(
61
- () => buildPluginArchive({ ...source, files: { ...source.files, 'plugin.js': 'fetch("https://evil.test")' } }),
62
- /network|remote/i,
63
- )
64
- assert.throws(
65
- () => buildPluginArchive({ ...source, files: { ...source.files, 'plugin.css': '@import url("https://evil.test/style.css")' } }),
66
- /network|remote|external/i,
67
- )
68
- })
69
-
70
- test('a rejected source names the file, the line and the text that matched', () => {
71
- // The pattern is text-only, so a comment or a string trips it too — the message
72
- // has to point at the match or the author cannot find it.
73
- assert.throws(
74
- () =>
75
- buildPluginArchive({
76
- ...source,
77
- files: { ...source.files, 'plugin.js': 'maxcode.log("hi")\n// export the palette\nconst x = 1' },
78
- }),
79
- /found "export" at plugin\.js:2/,
80
- )
81
- assert.throws(() => buildPluginArchive({ ...source, files: { ...source.files, 'plugin.js': 'fetch("https://evil.test")' } }), /"fetch\("/)
82
- assert.throws(
83
- () => buildPluginArchive({ ...source, files: { ...source.files, 'plugin.css': '.a{}\n@import url("https://evil.test/x.css")' } }),
84
- /plugin\.css:2/,
85
- )
86
- })
87
-
88
- test('plugin archive rejects malformed or oversized archives', () => {
89
- assert.throws(() => readPluginArchive(new Uint8Array([0, 1, 2, 3])), /archive|zip|package/i)
90
- assert.throws(
91
- () => buildPluginArchive({ ...source, files: { ...source.files, 'plugin.js': 'x'.repeat(10_485_761) } }),
92
- /size|large/i,
93
- )
94
- })
@@ -1 +0,0 @@
1
- {"root":["./src/App.tsx","./src/main.tsx","./src/types.ts","./src/vite-env.d.ts","./src/agent/approval.ts","./src/agent/compact.ts","./src/agent/index.ts","./src/agent/model.ts","./src/agent/prompt.ts","./src/agent/queue.ts","./src/agent/subagent.ts","./src/agent/tools.ts","./src/agent/adapters/anthropic.ts","./src/agent/adapters/gemini.ts","./src/agent/adapters/openai-responses.ts","./src/agent/adapters/openai.ts","./src/agent/adapters/shared.ts","./src/components/ChatView.tsx","./src/components/CommandPalette.tsx","./src/components/Composer.tsx","./src/components/ContextBar.tsx","./src/components/DeployModal.tsx","./src/components/DiffView.tsx","./src/components/ErrorBoundary.tsx","./src/components/FileIcon.tsx","./src/components/HomeDashboard.tsx","./src/components/Logo.tsx","./src/components/Markdown.tsx","./src/components/McpPanel.tsx","./src/components/MessageItem.tsx","./src/components/ModelDeck.tsx","./src/components/PixleSol.tsx","./src/components/PluginBuilder.tsx","./src/components/PluginManager.tsx","./src/components/PreviewPane.tsx","./src/components/SessionsTab.tsx","./src/components/SettingsModal.tsx","./src/components/Sidebar.tsx","./src/components/Splash.tsx","./src/components/SystemPromptButton.tsx","./src/components/ToolCallCard.tsx","./src/components/TopBar.tsx","./src/components/TrajectoryView.tsx","./src/components/WorkspacePanel.tsx","./src/components/ui.tsx","./src/core/kernel.ts","./src/core/mcp-tools.ts","./src/core/types.ts","./src/core/plugins/tools/fs.ts","./src/core/plugins/tools/git.ts","./src/core/plugins/tools/grep.ts","./src/core/plugins/tools/shell.ts","./src/core/plugins/tools/skill.ts","./src/core/plugins/tools/subagent.ts","./src/core/plugins/tools/web.ts","./src/core/services/agent-loop.ts","./src/core/services/approval.ts","./src/core/services/compaction.ts","./src/core/services/llm.ts","./src/core/services/sessions.ts","./src/core/services/subagents.ts","./src/core/services/system-prompt.ts","./src/core/services/titles.ts","./src/core/services/tools.ts","./src/core/services/ui.ts","./src/data/providers.ts","./src/lib/boot.ts","./src/lib/context.ts","./src/lib/conversation.ts","./src/lib/credits.ts","./src/lib/daemon.ts","./src/lib/diff.ts","./src/lib/hooks.ts","./src/lib/mcp-config.ts","./src/lib/permissions.ts","./src/lib/pixlesol.ts","./src/lib/plugin-folder.ts","./src/lib/reasoning.ts","./src/lib/retry.ts","./src/lib/sse.ts","./src/lib/system-prompt.ts","./src/lib/themes.ts","./src/lib/usage.ts","./src/lib/utils.ts","./src/plugins/api.ts","./src/plugins/bridge.d.mts","./src/plugins/index.ts","./src/plugins/preview.ts","./src/plugins/runtime.ts","./src/plugins/types.ts","./src/store/chats.ts","./src/store/mcp.ts","./src/store/models.ts","./src/store/settings.ts","./src/store/skills.ts","./src/store/ui.ts","./src/store/workspace.ts"],"version":"5.9.3"}