clawcontainer 1.0.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 (150) hide show
  1. package/CONTRIBUTING.md +76 -0
  2. package/DOCS.md +370 -0
  3. package/LICENSE +21 -0
  4. package/README.md +147 -0
  5. package/black_logo.png +0 -0
  6. package/dist/assets/abap-DLDM7-KI.js +1 -0
  7. package/dist/assets/apex-DNDY2TF8.js +1 -0
  8. package/dist/assets/azcli-Y6nb8tq_.js +1 -0
  9. package/dist/assets/bat-BwHxbl9M.js +1 -0
  10. package/dist/assets/bicep-CFznDFnq.js +2 -0
  11. package/dist/assets/cameligo-Bf6VGUru.js +1 -0
  12. package/dist/assets/clojure-Dnu-v4kV.js +1 -0
  13. package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  14. package/dist/assets/coffee-Bd8akH9Z.js +1 -0
  15. package/dist/assets/cpp-BbWJElDN.js +1 -0
  16. package/dist/assets/csharp-Co3qMtFm.js +1 -0
  17. package/dist/assets/csp-D-4FJmMZ.js +1 -0
  18. package/dist/assets/css-DdJfP1eB.js +3 -0
  19. package/dist/assets/css.worker-GxEd3MMM.js +93 -0
  20. package/dist/assets/cssMode-DM_ONlf-.js +1 -0
  21. package/dist/assets/cypher-cTPe9QuQ.js +1 -0
  22. package/dist/assets/dart-BOtBlQCF.js +1 -0
  23. package/dist/assets/dockerfile-BG73LgW2.js +1 -0
  24. package/dist/assets/ecl-BEgZUVRK.js +1 -0
  25. package/dist/assets/elixir-BkW5O-1t.js +1 -0
  26. package/dist/assets/flow9-BeJ5waoc.js +1 -0
  27. package/dist/assets/freemarker2-VbwzOQPq.js +3 -0
  28. package/dist/assets/fsharp-PahG7c26.js +1 -0
  29. package/dist/assets/go-acbASCJo.js +1 -0
  30. package/dist/assets/graphql-BxJiqAUM.js +1 -0
  31. package/dist/assets/handlebars-DLvQ802u.js +1 -0
  32. package/dist/assets/hcl-DtV1sZF8.js +1 -0
  33. package/dist/assets/html-DuEPBzmS.js +1 -0
  34. package/dist/assets/html.worker-lU17Tx2m.js +470 -0
  35. package/dist/assets/htmlMode-BfeYTJaB.js +1 -0
  36. package/dist/assets/index-BnBKg8GZ.js +1291 -0
  37. package/dist/assets/index-Dq3FlPWe.css +32 -0
  38. package/dist/assets/ini-Kd9XrMLS.js +1 -0
  39. package/dist/assets/java-CXBNlu9o.js +1 -0
  40. package/dist/assets/javascript-DQO1Leza.js +1 -0
  41. package/dist/assets/json.worker-CUJs-dtA.js +58 -0
  42. package/dist/assets/jsonMode--qsURhHr.js +7 -0
  43. package/dist/assets/julia-cl7-CwDS.js +1 -0
  44. package/dist/assets/kotlin-s7OhZKlX.js +1 -0
  45. package/dist/assets/less-9HpZscsL.js +2 -0
  46. package/dist/assets/lexon-OrD6JF1K.js +1 -0
  47. package/dist/assets/liquid-PL6MZtM8.js +1 -0
  48. package/dist/assets/lspLanguageFeatures-Cy5rDFeq.js +4 -0
  49. package/dist/assets/lua-Cyyb5UIc.js +1 -0
  50. package/dist/assets/m3-B8OfTtLu.js +1 -0
  51. package/dist/assets/markdown-BFxVWTOG.js +1 -0
  52. package/dist/assets/mdx-Cb3Jy14X.js +1 -0
  53. package/dist/assets/mips-CiqrrVzr.js +1 -0
  54. package/dist/assets/msdax-DmeGPVcC.js +1 -0
  55. package/dist/assets/mysql-C_tMU-Nz.js +1 -0
  56. package/dist/assets/objective-c-BDtDVThU.js +1 -0
  57. package/dist/assets/pascal-vHIfCaH5.js +1 -0
  58. package/dist/assets/pascaligo-DtZ0uQbO.js +1 -0
  59. package/dist/assets/perl-Ub6l9XKa.js +1 -0
  60. package/dist/assets/pgsql-BlNEE0v7.js +1 -0
  61. package/dist/assets/php-BBUBE1dy.js +1 -0
  62. package/dist/assets/pla-DSh2-awV.js +1 -0
  63. package/dist/assets/postiats-CocnycG-.js +1 -0
  64. package/dist/assets/powerquery-tScXyioY.js +1 -0
  65. package/dist/assets/powershell-COWaemsV.js +1 -0
  66. package/dist/assets/protobuf-Brw8urJB.js +2 -0
  67. package/dist/assets/pug-8SOpv6rk.js +1 -0
  68. package/dist/assets/python-Usm4OUwq.js +1 -0
  69. package/dist/assets/qsharp-Bw9ernYp.js +1 -0
  70. package/dist/assets/r-j7ic8hl3.js +1 -0
  71. package/dist/assets/razor-BIOole7a.js +1 -0
  72. package/dist/assets/redis-Bu5POkcn.js +1 -0
  73. package/dist/assets/redshift-Bs9aos_-.js +1 -0
  74. package/dist/assets/restructuredtext-CqXO7rUv.js +1 -0
  75. package/dist/assets/ruby-zBfavPgS.js +1 -0
  76. package/dist/assets/rust-BzKRNQWT.js +1 -0
  77. package/dist/assets/sb-BBc9UKZt.js +1 -0
  78. package/dist/assets/scala-D9hQfWCl.js +1 -0
  79. package/dist/assets/scheme-BPhDTwHR.js +1 -0
  80. package/dist/assets/scss-CBJaRo0y.js +3 -0
  81. package/dist/assets/shell-DiJ1NA_G.js +1 -0
  82. package/dist/assets/solidity-Db0IVjzk.js +1 -0
  83. package/dist/assets/sophia-CnS9iZB_.js +1 -0
  84. package/dist/assets/sparql-CJmd_6j2.js +1 -0
  85. package/dist/assets/sql-ClhHkBeG.js +1 -0
  86. package/dist/assets/st-CHwy0fLd.js +1 -0
  87. package/dist/assets/swift-Bqt4WxQ4.js +3 -0
  88. package/dist/assets/systemverilog-Bs9z6M-B.js +1 -0
  89. package/dist/assets/tcl-Dm6ycUr_.js +1 -0
  90. package/dist/assets/ts.worker-Dy9lDQQT.js +67731 -0
  91. package/dist/assets/tsMode-CDjF3DWK.js +11 -0
  92. package/dist/assets/twig-Csy3S7wG.js +1 -0
  93. package/dist/assets/typescript-CJR4sLnG.js +1 -0
  94. package/dist/assets/typespec-Btyra-wh.js +1 -0
  95. package/dist/assets/vb-Db0cS2oM.js +1 -0
  96. package/dist/assets/wgsl-DumH7NcR.js +298 -0
  97. package/dist/assets/xml-CJZS3uh7.js +1 -0
  98. package/dist/assets/yaml-DB88cW5z.js +1 -0
  99. package/dist/audit.d.ts +48 -0
  100. package/dist/container.d.ts +100 -0
  101. package/dist/event-emitter.d.ts +7 -0
  102. package/dist/favicon.png +0 -0
  103. package/dist/git-service.d.ts +31 -0
  104. package/dist/index.html +188 -0
  105. package/dist/logo-sm.png +0 -0
  106. package/dist/logo.png +0 -0
  107. package/dist/main.d.ts +1 -0
  108. package/dist/monaco-editor.d.ts +11 -0
  109. package/dist/monacoeditorwork/css.worker.bundle.js +54264 -0
  110. package/dist/monacoeditorwork/editor.worker.bundle.js +14317 -0
  111. package/dist/monacoeditorwork/html.worker.bundle.js +30449 -0
  112. package/dist/monacoeditorwork/json.worker.bundle.js +22085 -0
  113. package/dist/monacoeditorwork/ts.worker.bundle.js +225552 -0
  114. package/dist/net-intercept.d.ts +2 -0
  115. package/dist/network-hook.d.ts +1 -0
  116. package/dist/plugin.d.ts +20 -0
  117. package/dist/policy.d.ts +58 -0
  118. package/dist/sdk.d.ts +61 -0
  119. package/dist/tab-manager.d.ts +11 -0
  120. package/dist/templates.d.ts +46 -0
  121. package/dist/terminal.d.ts +19 -0
  122. package/dist/types.d.ts +109 -0
  123. package/dist/ui.d.ts +81 -0
  124. package/dist/workspace.d.ts +16 -0
  125. package/index.html +159 -0
  126. package/logo.png +0 -0
  127. package/package.json +31 -0
  128. package/public/favicon.png +0 -0
  129. package/public/logo-sm.png +0 -0
  130. package/public/logo.png +0 -0
  131. package/src/audit.ts +196 -0
  132. package/src/container.ts +723 -0
  133. package/src/event-emitter.ts +28 -0
  134. package/src/git-service.ts +202 -0
  135. package/src/main.ts +9 -0
  136. package/src/monaco-editor.ts +111 -0
  137. package/src/net-intercept.ts +74 -0
  138. package/src/network-hook.ts +248 -0
  139. package/src/plugin.ts +63 -0
  140. package/src/policy.ts +403 -0
  141. package/src/sdk.ts +355 -0
  142. package/src/style.css +432 -0
  143. package/src/tab-manager.ts +30 -0
  144. package/src/templates.ts +271 -0
  145. package/src/terminal.ts +78 -0
  146. package/src/types.ts +113 -0
  147. package/src/ui.ts +1266 -0
  148. package/src/workspace.ts +107 -0
  149. package/tsconfig.json +20 -0
  150. package/vite.config.ts +52 -0
package/src/sdk.ts ADDED
@@ -0,0 +1,355 @@
1
+ // ─── ClawContainer SDK ──────────────────────────────────────────────────────
2
+
3
+ import type { WebContainer } from '@webcontainer/api';
4
+ import { TerminalManager } from './terminal.js';
5
+ import { ContainerManager } from './container.js';
6
+ import { UIManager } from './ui.js';
7
+ import { AuditLog, type AuditEntry, type AuditSource, type AuditLevel, type AuditEvent } from './audit.js';
8
+ import { PolicyEngine } from './policy.js';
9
+ import { installBrowserFetchInterceptor } from './net-intercept.js';
10
+ import { TypedEventEmitter } from './event-emitter.js';
11
+ import { PluginManager } from './plugin.js';
12
+ import { TabManager } from './tab-manager.js';
13
+ import type {
14
+ AgentConfig,
15
+ ClawContainerOptions,
16
+ ClawContainerPlugin,
17
+ ClawContainerEvents,
18
+ ClawContainerSDK,
19
+ TabDefinition,
20
+ } from './types.js';
21
+ import {
22
+ type ContainerTemplate,
23
+ TemplateRegistry,
24
+ resolveTemplate,
25
+ mergeTemplateWithOptions,
26
+ parseTemplateYaml,
27
+ } from './templates.js';
28
+
29
+ export class ClawContainer extends TypedEventEmitter<ClawContainerEvents> implements ClawContainerSDK {
30
+ // ─── Static template API ─────────────────────────────────────────────────
31
+ private static _templateRegistry = new TemplateRegistry();
32
+
33
+ /** Registered templates (read-only). */
34
+ static get templates(): Map<string, ContainerTemplate> {
35
+ return ClawContainer._templateRegistry.all;
36
+ }
37
+
38
+ /** Register a named template for reuse. */
39
+ static registerTemplate(template: ContainerTemplate): void {
40
+ ClawContainer._templateRegistry.register(template);
41
+ }
42
+
43
+ /** Parse a YAML string into a ContainerTemplate. */
44
+ static parseTemplate(yaml: string): ContainerTemplate {
45
+ return parseTemplateYaml(yaml);
46
+ }
47
+
48
+ // ─── Instance ────────────────────────────────────────────────────────────
49
+ private _container: ContainerManager;
50
+ private _terminal: TerminalManager;
51
+ private _ui: UIManager;
52
+ private _audit: AuditLog;
53
+ private _policy: PolicyEngine;
54
+ private _plugins: PluginManager;
55
+ private _tabs: TabManager;
56
+ private _options: ClawContainerOptions;
57
+ private _selector: string;
58
+ private _started = false;
59
+
60
+ constructor(selector: string, options?: ClawContainerOptions) {
61
+ super();
62
+ this._selector = selector;
63
+ this._options = options ?? {};
64
+
65
+ this._terminal = new TerminalManager();
66
+ this._container = new ContainerManager();
67
+ this._audit = new AuditLog();
68
+ this._policy = new PolicyEngine();
69
+ this._plugins = new PluginManager();
70
+
71
+ installBrowserFetchInterceptor(this._audit);
72
+
73
+ // Restore saved policy
74
+ const savedPolicyYaml = localStorage.getItem('clawchef_policy');
75
+ if (savedPolicyYaml) {
76
+ try {
77
+ this._policy.loadPolicy(PolicyEngine.fromYaml(savedPolicyYaml));
78
+ this._audit.log('policy.load', 'Restored policy from localStorage', undefined, { source: 'boot' });
79
+ } catch { /* ignore invalid */ }
80
+ }
81
+
82
+ this._container.setAuditLog(this._audit);
83
+ this._container.setPolicy(this._policy);
84
+
85
+ this._ui = new UIManager(this._container, this._audit, this._policy);
86
+ this._tabs = new TabManager(this._ui);
87
+
88
+ // Register plugins from options
89
+ if (this._options.plugins) {
90
+ for (const p of this._options.plugins) this._plugins.register(p);
91
+ }
92
+
93
+ // Forward audit entries as 'log' events
94
+ this._audit.onEntry((entry) => this.emit('log', entry));
95
+
96
+ // Forward file change events
97
+ this._container.onFileChange((path) => this.emit('file.change', path));
98
+ }
99
+
100
+ // ─── Public getters ───────────────────────────────────────────────────────
101
+
102
+ /** Raw WebContainer instance for direct access. */
103
+ get container(): WebContainer | null {
104
+ return this._container.getWebContainer();
105
+ }
106
+
107
+ /** Terminal manager instance. */
108
+ get terminal(): TerminalManager {
109
+ return this._terminal;
110
+ }
111
+
112
+ // ─── Lifecycle ────────────────────────────────────────────────────────────
113
+
114
+ async start(): Promise<void> {
115
+ if (this._started) return;
116
+ this._started = true;
117
+
118
+ // ─── Resolve template and merge with options ───────────────────────────
119
+ const template = resolveTemplate(this._options.template, ClawContainer._templateRegistry);
120
+ const opts = mergeTemplateWithOptions(template, this._options);
121
+
122
+ // Dispatch plugin onInit
123
+ this._plugins.dispatchInit(this);
124
+
125
+ // Init UI
126
+ this._ui.init();
127
+ this._container.setStatusListener((s) => {
128
+ this._ui.setStatus(s);
129
+ this.emit('status', s);
130
+ });
131
+
132
+ // Mount terminal
133
+ const terminalContainer = document.getElementById('terminal-container')
134
+ ?? document.querySelector(this._selector)?.querySelector('#terminal-container');
135
+ if (terminalContainer) {
136
+ this._terminal.mount(terminalContainer as HTMLElement);
137
+ }
138
+
139
+ // Remove loading overlay
140
+ const overlay = document.getElementById('loading-overlay');
141
+ if (overlay) {
142
+ overlay.classList.add('fade-out');
143
+ setTimeout(() => overlay.remove(), 400);
144
+ }
145
+
146
+ // Open preview
147
+ this._ui.openPreviewOnLoad();
148
+
149
+ // Merge plugin contributions with merged options
150
+ const extraServices = { ...this._plugins.mergedServices, ...opts.services };
151
+ const extraWorkspace = { ...this._plugins.mergedWorkspace, ...opts.workspace };
152
+ const extraEnv = { ...this._plugins.mergedEnv, ...opts.env };
153
+
154
+ // Step 1: Boot
155
+ this._terminal.write('\x1b[90m[ClawLess] Booting WebContainer…\x1b[0m\r\n');
156
+ this._audit.log('status.change', 'boot sequence started', undefined, { source: 'boot' });
157
+
158
+ try {
159
+ await this._container.boot({
160
+ workspace: Object.keys(extraWorkspace).length > 0 ? extraWorkspace : undefined,
161
+ services: Object.keys(extraServices).length > 0 ? extraServices : undefined,
162
+ });
163
+ this._audit.log('status.change', 'webcontainer booted', undefined, { source: 'boot' });
164
+ } catch (e) {
165
+ this._terminal.write(`\r\n\x1b[31m[ClawLess] Boot failed: ${(e as Error).message}\x1b[0m\r\n`);
166
+ this.emit('error', e as Error);
167
+ return;
168
+ }
169
+
170
+ // Step 2: npm install
171
+ this._terminal.write('\x1b[90m[ClawLess] Installing dependencies (npm install)…\x1b[0m\r\n\r\n');
172
+ try {
173
+ await this._container.runNpmInstall(this._terminal);
174
+ } catch (e) {
175
+ this._terminal.write(`\r\n\x1b[31m[ClawLess] npm install failed:\x1b[0m\r\n${(e as Error).message}\r\n`);
176
+ this.emit('error', e as Error);
177
+ return;
178
+ }
179
+
180
+ this._audit.log('status.change', 'npm install complete', undefined, { source: 'boot' });
181
+ this._terminal.write('\r\n\x1b[32m[ClawLess] Installation complete.\x1b[0m\r\n\r\n');
182
+
183
+ // Step 3: Run startup script if provided
184
+ if (opts.startupScript) {
185
+ this._terminal.write('\x1b[90m[ClawLess] Running startup script…\x1b[0m\r\n');
186
+ try {
187
+ await this._container.runStartupScript(opts.startupScript, this._terminal);
188
+ } catch (e) {
189
+ this._terminal.write(`\r\n\x1b[31m[ClawLess] Startup script failed:\x1b[0m\r\n${(e as Error).message}\r\n`);
190
+ this.emit('error', e as Error);
191
+ return;
192
+ }
193
+ }
194
+
195
+ // Step 4: Inject env vars if provided
196
+ if (Object.keys(extraEnv).length > 0) {
197
+ await this._container.configureEnv({
198
+ provider: 'custom',
199
+ model: '',
200
+ envVars: extraEnv,
201
+ });
202
+ }
203
+
204
+ // Step 5: Launch agent (unless agent: false)
205
+ const agentConfig = opts.agent;
206
+
207
+ if (agentConfig === false) {
208
+ // No agent — container is ready for user to do whatever they want
209
+ this._terminal.write('\x1b[32m[ClawLess] Ready (no agent).\x1b[0m\r\n\r\n');
210
+ this.emit('ready');
211
+ } else {
212
+ const config = agentConfig as AgentConfig;
213
+ const isGitclaw = config.package === 'gitclaw';
214
+
215
+ if (isGitclaw) {
216
+ // Gitclaw flow — check API keys
217
+ const savedConfig = this._ui.getSavedConfig();
218
+ if (!savedConfig) {
219
+ this._ui.showConfigPanel();
220
+ this._terminal.write('\x1b[33m[ClawLess] Configure your API key in the sidebar to continue.\x1b[0m\r\n\r\n');
221
+ await this.waitForConfig();
222
+ }
223
+
224
+ const envConfig = this._ui.getSavedConfig()!;
225
+ this._terminal.write('\x1b[90m[ClawLess] Launching gitclaw…\x1b[0m\r\n\r\n');
226
+ this._audit.log('status.change', 'launching gitclaw', undefined, { source: 'boot' });
227
+
228
+ try {
229
+ await this._container.configureEnv(envConfig);
230
+ await this._container.startGitclaw(this._terminal);
231
+ } catch (e) {
232
+ this._terminal.write(`\r\n\x1b[31m[ClawLess] Launch failed: ${(e as Error).message}\x1b[0m\r\n`);
233
+ this.emit('error', e as Error);
234
+ return;
235
+ }
236
+ } else {
237
+ // Custom agent config
238
+ this._terminal.write(`\x1b[90m[ClawLess] Launching agent (${config.package})…\x1b[0m\r\n\r\n`);
239
+ this._audit.log('status.change', `launching ${config.package}`, undefined, { source: 'boot' });
240
+
241
+ try {
242
+ await this._container.startAgent(config, this._terminal);
243
+ } catch (e) {
244
+ this._terminal.write(`\r\n\x1b[31m[ClawLess] Agent launch failed: ${(e as Error).message}\x1b[0m\r\n`);
245
+ this.emit('error', e as Error);
246
+ return;
247
+ }
248
+ }
249
+
250
+ this.emit('ready');
251
+ }
252
+
253
+ // Register custom tabs from plugins and merged options
254
+ const allTabs = [...this._plugins.mergedTabs, ...(opts.tabs ?? [])];
255
+ for (const tab of allTabs) {
256
+ this._tabs.addTab(tab);
257
+ }
258
+
259
+ // Dispatch plugin onReady
260
+ this._plugins.dispatchReady(this);
261
+ }
262
+
263
+ async stop(): Promise<void> {
264
+ this._plugins.dispatchDestroy(this);
265
+ this._started = false;
266
+ }
267
+
268
+ async restart(): Promise<void> {
269
+ await this.stop();
270
+ await this.start();
271
+ }
272
+
273
+ // ─── Command execution ────────────────────────────────────────────────────
274
+
275
+ /** Run any shell command and return stdout. */
276
+ async exec(cmd: string): Promise<string> {
277
+ return this._container.exec(cmd);
278
+ }
279
+
280
+ /** Open a raw interactive shell. */
281
+ async shell(): Promise<void> {
282
+ await this._container.startShell(this._terminal);
283
+ }
284
+
285
+ /** Write data to the active process stdin. */
286
+ async sendInput(data: string): Promise<void> {
287
+ await this._container.sendToShell(data);
288
+ }
289
+
290
+ // ─── File system convenience ──────────────────────────────────────────────
291
+
292
+ fs = {
293
+ read: (path: string): Promise<string> => {
294
+ return this._container.readFile(path);
295
+ },
296
+ write: (path: string, content: string): Promise<void> => {
297
+ return this._container.writeFile(path, content);
298
+ },
299
+ list: (dir?: string): Promise<string[]> => {
300
+ return this._container.listWorkspaceFiles(dir);
301
+ },
302
+ mkdir: (path: string): Promise<void> => {
303
+ return this._container.mkdir(path);
304
+ },
305
+ remove: (path: string): Promise<void> => {
306
+ return this._container.remove(path);
307
+ },
308
+ };
309
+
310
+ // ─── Git ──────────────────────────────────────────────────────────────────
311
+
312
+ git = {
313
+ clone: (url: string, token: string): Promise<void> => {
314
+ return this._container.cloneRepo(url, token);
315
+ },
316
+ push: (message?: string): Promise<string> => {
317
+ return this._container.syncToRepo(message);
318
+ },
319
+ };
320
+
321
+ // ─── Audit / Logs ─────────────────────────────────────────────────────────
322
+
323
+ /** Get audit log entries, optionally filtered. */
324
+ logs(filter?: { source?: AuditSource; level?: AuditLevel; event?: AuditEvent }): AuditEntry[] {
325
+ if (filter) return this._audit.filter(filter);
326
+ return this._audit.getEntries();
327
+ }
328
+
329
+ // ─── Plugin API ───────────────────────────────────────────────────────────
330
+
331
+ /** Register a plugin. Must be called before start(). */
332
+ use(plugin: ClawContainerPlugin): void {
333
+ this._plugins.register(plugin);
334
+ }
335
+
336
+ // ─── Tab API ──────────────────────────────────────────────────────────────
337
+
338
+ addTab(def: TabDefinition): void {
339
+ this._tabs.addTab(def);
340
+ }
341
+
342
+ removeTab(id: string): void {
343
+ this._tabs.removeTab(id);
344
+ }
345
+
346
+ // ─── Helpers ──────────────────────────────────────────────────────────────
347
+
348
+ private waitForConfig(): Promise<void> {
349
+ return new Promise(resolve => {
350
+ const interval = setInterval(() => {
351
+ if (this._ui.getSavedConfig()) { clearInterval(interval); resolve(); }
352
+ }, 500);
353
+ });
354
+ }
355
+ }