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.
- package/CONTRIBUTING.md +76 -0
- package/DOCS.md +370 -0
- package/LICENSE +21 -0
- package/README.md +147 -0
- package/black_logo.png +0 -0
- package/dist/assets/abap-DLDM7-KI.js +1 -0
- package/dist/assets/apex-DNDY2TF8.js +1 -0
- package/dist/assets/azcli-Y6nb8tq_.js +1 -0
- package/dist/assets/bat-BwHxbl9M.js +1 -0
- package/dist/assets/bicep-CFznDFnq.js +2 -0
- package/dist/assets/cameligo-Bf6VGUru.js +1 -0
- package/dist/assets/clojure-Dnu-v4kV.js +1 -0
- package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/assets/coffee-Bd8akH9Z.js +1 -0
- package/dist/assets/cpp-BbWJElDN.js +1 -0
- package/dist/assets/csharp-Co3qMtFm.js +1 -0
- package/dist/assets/csp-D-4FJmMZ.js +1 -0
- package/dist/assets/css-DdJfP1eB.js +3 -0
- package/dist/assets/css.worker-GxEd3MMM.js +93 -0
- package/dist/assets/cssMode-DM_ONlf-.js +1 -0
- package/dist/assets/cypher-cTPe9QuQ.js +1 -0
- package/dist/assets/dart-BOtBlQCF.js +1 -0
- package/dist/assets/dockerfile-BG73LgW2.js +1 -0
- package/dist/assets/ecl-BEgZUVRK.js +1 -0
- package/dist/assets/elixir-BkW5O-1t.js +1 -0
- package/dist/assets/flow9-BeJ5waoc.js +1 -0
- package/dist/assets/freemarker2-VbwzOQPq.js +3 -0
- package/dist/assets/fsharp-PahG7c26.js +1 -0
- package/dist/assets/go-acbASCJo.js +1 -0
- package/dist/assets/graphql-BxJiqAUM.js +1 -0
- package/dist/assets/handlebars-DLvQ802u.js +1 -0
- package/dist/assets/hcl-DtV1sZF8.js +1 -0
- package/dist/assets/html-DuEPBzmS.js +1 -0
- package/dist/assets/html.worker-lU17Tx2m.js +470 -0
- package/dist/assets/htmlMode-BfeYTJaB.js +1 -0
- package/dist/assets/index-BnBKg8GZ.js +1291 -0
- package/dist/assets/index-Dq3FlPWe.css +32 -0
- package/dist/assets/ini-Kd9XrMLS.js +1 -0
- package/dist/assets/java-CXBNlu9o.js +1 -0
- package/dist/assets/javascript-DQO1Leza.js +1 -0
- package/dist/assets/json.worker-CUJs-dtA.js +58 -0
- package/dist/assets/jsonMode--qsURhHr.js +7 -0
- package/dist/assets/julia-cl7-CwDS.js +1 -0
- package/dist/assets/kotlin-s7OhZKlX.js +1 -0
- package/dist/assets/less-9HpZscsL.js +2 -0
- package/dist/assets/lexon-OrD6JF1K.js +1 -0
- package/dist/assets/liquid-PL6MZtM8.js +1 -0
- package/dist/assets/lspLanguageFeatures-Cy5rDFeq.js +4 -0
- package/dist/assets/lua-Cyyb5UIc.js +1 -0
- package/dist/assets/m3-B8OfTtLu.js +1 -0
- package/dist/assets/markdown-BFxVWTOG.js +1 -0
- package/dist/assets/mdx-Cb3Jy14X.js +1 -0
- package/dist/assets/mips-CiqrrVzr.js +1 -0
- package/dist/assets/msdax-DmeGPVcC.js +1 -0
- package/dist/assets/mysql-C_tMU-Nz.js +1 -0
- package/dist/assets/objective-c-BDtDVThU.js +1 -0
- package/dist/assets/pascal-vHIfCaH5.js +1 -0
- package/dist/assets/pascaligo-DtZ0uQbO.js +1 -0
- package/dist/assets/perl-Ub6l9XKa.js +1 -0
- package/dist/assets/pgsql-BlNEE0v7.js +1 -0
- package/dist/assets/php-BBUBE1dy.js +1 -0
- package/dist/assets/pla-DSh2-awV.js +1 -0
- package/dist/assets/postiats-CocnycG-.js +1 -0
- package/dist/assets/powerquery-tScXyioY.js +1 -0
- package/dist/assets/powershell-COWaemsV.js +1 -0
- package/dist/assets/protobuf-Brw8urJB.js +2 -0
- package/dist/assets/pug-8SOpv6rk.js +1 -0
- package/dist/assets/python-Usm4OUwq.js +1 -0
- package/dist/assets/qsharp-Bw9ernYp.js +1 -0
- package/dist/assets/r-j7ic8hl3.js +1 -0
- package/dist/assets/razor-BIOole7a.js +1 -0
- package/dist/assets/redis-Bu5POkcn.js +1 -0
- package/dist/assets/redshift-Bs9aos_-.js +1 -0
- package/dist/assets/restructuredtext-CqXO7rUv.js +1 -0
- package/dist/assets/ruby-zBfavPgS.js +1 -0
- package/dist/assets/rust-BzKRNQWT.js +1 -0
- package/dist/assets/sb-BBc9UKZt.js +1 -0
- package/dist/assets/scala-D9hQfWCl.js +1 -0
- package/dist/assets/scheme-BPhDTwHR.js +1 -0
- package/dist/assets/scss-CBJaRo0y.js +3 -0
- package/dist/assets/shell-DiJ1NA_G.js +1 -0
- package/dist/assets/solidity-Db0IVjzk.js +1 -0
- package/dist/assets/sophia-CnS9iZB_.js +1 -0
- package/dist/assets/sparql-CJmd_6j2.js +1 -0
- package/dist/assets/sql-ClhHkBeG.js +1 -0
- package/dist/assets/st-CHwy0fLd.js +1 -0
- package/dist/assets/swift-Bqt4WxQ4.js +3 -0
- package/dist/assets/systemverilog-Bs9z6M-B.js +1 -0
- package/dist/assets/tcl-Dm6ycUr_.js +1 -0
- package/dist/assets/ts.worker-Dy9lDQQT.js +67731 -0
- package/dist/assets/tsMode-CDjF3DWK.js +11 -0
- package/dist/assets/twig-Csy3S7wG.js +1 -0
- package/dist/assets/typescript-CJR4sLnG.js +1 -0
- package/dist/assets/typespec-Btyra-wh.js +1 -0
- package/dist/assets/vb-Db0cS2oM.js +1 -0
- package/dist/assets/wgsl-DumH7NcR.js +298 -0
- package/dist/assets/xml-CJZS3uh7.js +1 -0
- package/dist/assets/yaml-DB88cW5z.js +1 -0
- package/dist/audit.d.ts +48 -0
- package/dist/container.d.ts +100 -0
- package/dist/event-emitter.d.ts +7 -0
- package/dist/favicon.png +0 -0
- package/dist/git-service.d.ts +31 -0
- package/dist/index.html +188 -0
- package/dist/logo-sm.png +0 -0
- package/dist/logo.png +0 -0
- package/dist/main.d.ts +1 -0
- package/dist/monaco-editor.d.ts +11 -0
- package/dist/monacoeditorwork/css.worker.bundle.js +54264 -0
- package/dist/monacoeditorwork/editor.worker.bundle.js +14317 -0
- package/dist/monacoeditorwork/html.worker.bundle.js +30449 -0
- package/dist/monacoeditorwork/json.worker.bundle.js +22085 -0
- package/dist/monacoeditorwork/ts.worker.bundle.js +225552 -0
- package/dist/net-intercept.d.ts +2 -0
- package/dist/network-hook.d.ts +1 -0
- package/dist/plugin.d.ts +20 -0
- package/dist/policy.d.ts +58 -0
- package/dist/sdk.d.ts +61 -0
- package/dist/tab-manager.d.ts +11 -0
- package/dist/templates.d.ts +46 -0
- package/dist/terminal.d.ts +19 -0
- package/dist/types.d.ts +109 -0
- package/dist/ui.d.ts +81 -0
- package/dist/workspace.d.ts +16 -0
- package/index.html +159 -0
- package/logo.png +0 -0
- package/package.json +31 -0
- package/public/favicon.png +0 -0
- package/public/logo-sm.png +0 -0
- package/public/logo.png +0 -0
- package/src/audit.ts +196 -0
- package/src/container.ts +723 -0
- package/src/event-emitter.ts +28 -0
- package/src/git-service.ts +202 -0
- package/src/main.ts +9 -0
- package/src/monaco-editor.ts +111 -0
- package/src/net-intercept.ts +74 -0
- package/src/network-hook.ts +248 -0
- package/src/plugin.ts +63 -0
- package/src/policy.ts +403 -0
- package/src/sdk.ts +355 -0
- package/src/style.css +432 -0
- package/src/tab-manager.ts +30 -0
- package/src/templates.ts +271 -0
- package/src/terminal.ts +78 -0
- package/src/types.ts +113 -0
- package/src/ui.ts +1266 -0
- package/src/workspace.ts +107 -0
- package/tsconfig.json +20 -0
- 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
|
+
}
|