@crewx/sdk 0.8.0-rc.78 → 0.8.0-rc.80
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/dist/plugin/plugin-provider.js +1 -1
- package/dist/provider/bridge.js +1 -0
- package/package.json +1 -1
- package/dist/esm/agent/resolver.js +0 -41
- package/dist/esm/boxing/box-storage.interface.js +0 -5
- package/dist/esm/boxing/box.service.js +0 -69
- package/dist/esm/boxing/box.types.js +0 -5
- package/dist/esm/boxing/context-builder.js +0 -76
- package/dist/esm/client/CrewxClient.js +0 -82
- package/dist/esm/client/index.js +0 -2
- package/dist/esm/config/loader.browser.js +0 -54
- package/dist/esm/config/loader.js +0 -77
- package/dist/esm/events/TypedEventEmitter.js +0 -61
- package/dist/esm/events/types.js +0 -8
- package/dist/esm/facade/Crewx.browser.js +0 -310
- package/dist/esm/facade/Crewx.js +0 -941
- package/dist/esm/hooks/define.js +0 -10
- package/dist/esm/hooks/dispatch.js +0 -76
- package/dist/esm/hooks/index.js +0 -6
- package/dist/esm/hooks/observer.js +0 -56
- package/dist/esm/hooks/plugin.js +0 -12
- package/dist/esm/hooks/types.js +0 -9
- package/dist/esm/index.browser.js +0 -15
- package/dist/esm/index.js +0 -60
- package/dist/esm/layout/loader.js +0 -268
- package/dist/esm/layout/props-validator.js +0 -297
- package/dist/esm/layout/renderer.js +0 -180
- package/dist/esm/layout/types.js +0 -31
- package/dist/esm/parallel/agent-runtime.js +0 -21
- package/dist/esm/parallel/helpers.js +0 -214
- package/dist/esm/parallel/index.js +0 -5
- package/dist/esm/parallel/parallel-runner.js +0 -221
- package/dist/esm/parallel/types.js +0 -5
- package/dist/esm/parsers/agent-call.util.js +0 -15
- package/dist/esm/parsers/claude.parser.js +0 -64
- package/dist/esm/parsers/codex.parser.js +0 -97
- package/dist/esm/parsers/copilot.parser.js +0 -63
- package/dist/esm/parsers/gemini.parser.js +0 -43
- package/dist/esm/parsers/opencode.parser.js +0 -73
- package/dist/esm/parsers/router.js +0 -53
- package/dist/esm/platform/BrowserFsAdapter.js +0 -80
- package/dist/esm/platform/IFsAdapter.js +0 -2
- package/dist/esm/platform/NodeFsAdapter.js +0 -34
- package/dist/esm/plugin/plugin-provider.js +0 -202
- package/dist/esm/plugin/types.js +0 -8
- package/dist/esm/plugin.js +0 -25
- package/dist/esm/provider/bridge.browser.js +0 -43
- package/dist/esm/provider/bridge.js +0 -373
- package/dist/esm/provider/parse-usage.js +0 -80
- package/dist/esm/provider/register-api.js +0 -21
- package/dist/esm/provider/vercel-runtime.js +0 -310
- package/dist/esm/remote/index.js +0 -10
- package/dist/esm/remote/remote-agent-manager.js +0 -194
- package/dist/esm/remote/remote-provider.js +0 -98
- package/dist/esm/remote/remote-transport.js +0 -79
- package/dist/esm/remote/types.js +0 -8
- package/dist/esm/server/auth.js +0 -31
- package/dist/esm/server/handler.js +0 -72
- package/dist/esm/server/index.js +0 -5
- package/dist/esm/server/tool-adapter.js +0 -92
- package/dist/esm/template/engine.js +0 -100
- package/dist/esm/template/helpers/exec.browser.js +0 -31
- package/dist/esm/template/helpers/exec.js +0 -220
- package/dist/esm/template/helpers/fenced_code.js +0 -17
- package/dist/esm/template/helpers/include.js +0 -20
- package/dist/esm/template/helpers/p1p2.js +0 -83
- package/dist/esm/template/loader/DocumentLoader.js +0 -124
- package/dist/esm/template/types.js +0 -5
- package/dist/esm/tools/delegate.js +0 -57
- package/dist/esm/tools/index.js +0 -5
- package/dist/esm/tools/node/builtin.js +0 -541
- package/dist/esm/tools/node/index.js +0 -54
- package/dist/esm/types/index.js +0 -27
- package/dist/esm/types/task-log.types.js +0 -5
- package/dist/esm/utils/env-defaults.js +0 -23
- package/dist/esm/utils/glob-match.js +0 -38
- package/dist/esm/utils/id.js +0 -46
- package/dist/esm/utils/workspace.js +0 -21
- package/dist/parsers/api.parser.d.ts +0 -10
- package/dist/parsers/api.parser.js +0 -26
- package/dist/provider/mastra-runtime.d.ts +0 -45
- package/dist/provider/mastra-runtime.js +0 -208
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { isAgentCallCommand, parseAgentCall } from './agent-call.util';
|
|
2
|
-
/**
|
|
3
|
-
* Parse Gemini stream-json events into TaskLogEntry[].
|
|
4
|
-
*
|
|
5
|
-
* Gemini events:
|
|
6
|
-
* - { type: "message", role: "assistant", delta: true, content: "..." }
|
|
7
|
-
* - { type: "init", ... } (ignored)
|
|
8
|
-
* - { type: "tool_use", tool_name, tool_id, parameters }
|
|
9
|
-
* - { type: "tool_result", tool_id, status, output }
|
|
10
|
-
*/
|
|
11
|
-
export function parseGeminiEvent(timestamp, parsed) {
|
|
12
|
-
const type = parsed.type;
|
|
13
|
-
if (type === 'message') {
|
|
14
|
-
const role = parsed.role;
|
|
15
|
-
if (role !== 'assistant')
|
|
16
|
-
return [];
|
|
17
|
-
const content = parsed.content;
|
|
18
|
-
if (!content)
|
|
19
|
-
return [];
|
|
20
|
-
return [{ timestamp, type: 'text', content }];
|
|
21
|
-
}
|
|
22
|
-
if (type === 'tool_use') {
|
|
23
|
-
const toolName = String(parsed.tool_name || '');
|
|
24
|
-
const toolUseId = parsed.tool_id;
|
|
25
|
-
const parameters = parsed.parameters;
|
|
26
|
-
const inputStr = parameters?.command
|
|
27
|
-
? String(parameters.command)
|
|
28
|
-
: JSON.stringify(parameters || {});
|
|
29
|
-
if (/^bash$/i.test(toolName) && isAgentCallCommand(inputStr)) {
|
|
30
|
-
return [parseAgentCall(timestamp, inputStr)];
|
|
31
|
-
}
|
|
32
|
-
return [{ timestamp, type: 'tool_use', toolName, toolUseId, toolInput: inputStr }];
|
|
33
|
-
}
|
|
34
|
-
if (type === 'tool_result') {
|
|
35
|
-
const toolUseId = parsed.tool_id;
|
|
36
|
-
const output = String(parsed.output || '');
|
|
37
|
-
const isError = parsed.status !== 'success';
|
|
38
|
-
return [{ timestamp, type: 'tool_result', toolUseId, resultPreview: output, isError }];
|
|
39
|
-
}
|
|
40
|
-
// init and other events: skip
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=gemini.parser.js.map
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse OpenCode stream-json events (--format json) into TaskLogEntry[].
|
|
3
|
-
*
|
|
4
|
-
* OpenCode events:
|
|
5
|
-
* - { type: "step_start", sessionID: "ses_...", part: { type: "step-start" } }
|
|
6
|
-
* - { type: "text", part: { type: "text", text: "..." } }
|
|
7
|
-
* - { type: "step_finish", part: { reason: "stop", tokens: { total, input, output, reasoning, cache: { write, read } }, cost: 0 } }
|
|
8
|
-
* - { type: "tool_use", part: { type: "tool", tool: "bash"|"write"|..., callID: "...", state: { status, input, output } } }
|
|
9
|
-
*/
|
|
10
|
-
export function parseOpencodeEvent(timestamp, parsed) {
|
|
11
|
-
const type = parsed.type;
|
|
12
|
-
if (type === 'text') {
|
|
13
|
-
const part = parsed.part;
|
|
14
|
-
const text = part?.text;
|
|
15
|
-
if (!text)
|
|
16
|
-
return [];
|
|
17
|
-
return [{ timestamp, type: 'text', content: text }];
|
|
18
|
-
}
|
|
19
|
-
if (type === 'tool_use') {
|
|
20
|
-
const part = parsed.part;
|
|
21
|
-
if (!part)
|
|
22
|
-
return [];
|
|
23
|
-
const tool = String(part.tool || '');
|
|
24
|
-
const toolUseId = String(part.callID || '');
|
|
25
|
-
const state = part.state;
|
|
26
|
-
const status = String(state?.status || '');
|
|
27
|
-
const input = state?.input;
|
|
28
|
-
const inputStr = extractToolInput(input);
|
|
29
|
-
const entries = [
|
|
30
|
-
{ timestamp, type: 'tool_use', toolName: tool, toolUseId, toolInput: inputStr },
|
|
31
|
-
];
|
|
32
|
-
if (status === 'completed' && state?.output !== undefined) {
|
|
33
|
-
const output = state.output;
|
|
34
|
-
const outputStr = typeof output === 'string' ? output : JSON.stringify(output);
|
|
35
|
-
entries.push({ timestamp, type: 'tool_result', toolUseId, resultPreview: outputStr, isError: false });
|
|
36
|
-
}
|
|
37
|
-
else if (status === 'error') {
|
|
38
|
-
entries.push({
|
|
39
|
-
timestamp,
|
|
40
|
-
type: 'tool_result',
|
|
41
|
-
toolUseId,
|
|
42
|
-
resultPreview: String(state?.error || 'tool error'),
|
|
43
|
-
isError: true,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
return entries;
|
|
47
|
-
}
|
|
48
|
-
// step_start, step_finish: skip (usage is extracted by parse-usage.ts)
|
|
49
|
-
return [];
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Extract a human-friendly string from OpenCode tool input.
|
|
53
|
-
*
|
|
54
|
-
* Matches Claude/Gemini parser behaviour:
|
|
55
|
-
* - bash-like → input.command ("mkdir -p /tmp/foo")
|
|
56
|
-
* - file tools → input.filePath ("/tmp/foo/bar.ts")
|
|
57
|
-
* - otherwise → JSON.stringify fallback
|
|
58
|
-
*/
|
|
59
|
-
function extractToolInput(input) {
|
|
60
|
-
if (typeof input === 'string')
|
|
61
|
-
return input;
|
|
62
|
-
if (!input || typeof input !== 'object')
|
|
63
|
-
return JSON.stringify(input ?? {});
|
|
64
|
-
const obj = input;
|
|
65
|
-
if (obj.command)
|
|
66
|
-
return String(obj.command);
|
|
67
|
-
if (obj.filePath)
|
|
68
|
-
return String(obj.filePath);
|
|
69
|
-
if (obj.pattern)
|
|
70
|
-
return String(obj.pattern);
|
|
71
|
-
return JSON.stringify(obj);
|
|
72
|
-
}
|
|
73
|
-
//# sourceMappingURL=opencode.parser.js.map
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* parseStdoutEvent — routes a single stdout JSONL event to the appropriate provider parser.
|
|
3
|
-
* Returns TaskLogEntry[] (may be empty if event is not recognized or irrelevant).
|
|
4
|
-
*/
|
|
5
|
-
import { parseClaudeEvent } from './claude.parser';
|
|
6
|
-
import { parseCopilotEvent } from './copilot.parser';
|
|
7
|
-
import { parseCodexEvent } from './codex.parser';
|
|
8
|
-
import { parseGeminiEvent } from './gemini.parser';
|
|
9
|
-
import { parseOpencodeEvent } from './opencode.parser';
|
|
10
|
-
export function parseStdoutEvent(timestamp, message) {
|
|
11
|
-
let parsed;
|
|
12
|
-
try {
|
|
13
|
-
parsed = JSON.parse(message);
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return [];
|
|
17
|
-
}
|
|
18
|
-
const type = parsed.type;
|
|
19
|
-
if (!type)
|
|
20
|
-
return [];
|
|
21
|
-
// Claude stream-json (system event excluded — meta event)
|
|
22
|
-
if (type === 'assistant' || type === 'user') {
|
|
23
|
-
return parseClaudeEvent(timestamp, parsed);
|
|
24
|
-
}
|
|
25
|
-
// Copilot json — assistant.* check must precede Gemini "message" check
|
|
26
|
-
if (type.startsWith('tool.execution') || type.startsWith('assistant.') || type === 'user.message') {
|
|
27
|
-
return parseCopilotEvent(timestamp, parsed);
|
|
28
|
-
}
|
|
29
|
-
// Codex experimental-json
|
|
30
|
-
if (type.startsWith('item.') || type.startsWith('turn.') || type.startsWith('thread.')) {
|
|
31
|
-
return parseCodexEvent(timestamp, parsed);
|
|
32
|
-
}
|
|
33
|
-
// Gemini stream-json — tool_use/tool_result routed by structure below
|
|
34
|
-
if (type === 'message' || type === 'init') {
|
|
35
|
-
return parseGeminiEvent(timestamp, parsed);
|
|
36
|
-
}
|
|
37
|
-
// tool_use / tool_result: disambiguate by schema structure.
|
|
38
|
-
// OpenCode has a `part` object; Gemini is flat (tool_name, parameters).
|
|
39
|
-
if (type === 'tool_use' || type === 'tool_result') {
|
|
40
|
-
if (parsed.part && typeof parsed.part === 'object') {
|
|
41
|
-
return parseOpencodeEvent(timestamp, parsed);
|
|
42
|
-
}
|
|
43
|
-
return parseGeminiEvent(timestamp, parsed);
|
|
44
|
-
}
|
|
45
|
-
// OpenCode stream-json (--format json)
|
|
46
|
-
// Note: 'text' is a generic name but not used by any other provider currently.
|
|
47
|
-
// 'step_start' and 'step_finish' are OpenCode-specific.
|
|
48
|
-
if (type === 'text' || type === 'step_start' || type === 'step_finish') {
|
|
49
|
-
return parseOpencodeEvent(timestamp, parsed);
|
|
50
|
-
}
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
//# sourceMappingURL=router.js.map
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BrowserFsAdapter — IFsAdapter implementation for browser environments.
|
|
3
|
-
*
|
|
4
|
-
* Uses localStorage as the backing store with a key-prefix namespace.
|
|
5
|
-
* Paths are normalized to forward-slash POSIX style.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* const fs = new BrowserFsAdapter();
|
|
9
|
-
* // Pre-load content before using with DocumentLoader:
|
|
10
|
-
* fs.setItem('/docs/guide.md', '# Guide\n...');
|
|
11
|
-
*/
|
|
12
|
-
const DEFAULT_PREFIX = 'crewx:fs:';
|
|
13
|
-
export class BrowserFsAdapter {
|
|
14
|
-
prefix;
|
|
15
|
-
store;
|
|
16
|
-
/**
|
|
17
|
-
* @param options.prefix - localStorage key prefix (default: 'crewx:fs:')
|
|
18
|
-
* @param options.storage - optional external storage map (defaults to an in-memory Map;
|
|
19
|
-
* pass a localStorage-backed wrapper for persistence)
|
|
20
|
-
*/
|
|
21
|
-
constructor(options) {
|
|
22
|
-
this.prefix = options?.prefix ?? DEFAULT_PREFIX;
|
|
23
|
-
this.store = options?.storage ?? new Map();
|
|
24
|
-
}
|
|
25
|
-
async readFile(path) {
|
|
26
|
-
const key = this.toKey(path);
|
|
27
|
-
const content = this.store.get(key);
|
|
28
|
-
if (content === undefined) {
|
|
29
|
-
throw new Error(`BrowserFsAdapter: file not found: ${path}`);
|
|
30
|
-
}
|
|
31
|
-
return content;
|
|
32
|
-
}
|
|
33
|
-
async exists(path) {
|
|
34
|
-
return this.store.has(this.toKey(path));
|
|
35
|
-
}
|
|
36
|
-
resolvePath(...segments) {
|
|
37
|
-
// Simple POSIX-style path joining (no true "resolve" since there's no cwd)
|
|
38
|
-
const joined = segments
|
|
39
|
-
.map(s => s.replace(/\\/g, '/'))
|
|
40
|
-
.join('/')
|
|
41
|
-
.replace(/\/+/g, '/');
|
|
42
|
-
// Normalize . and .. segments
|
|
43
|
-
const parts = joined.split('/');
|
|
44
|
-
const resolved = [];
|
|
45
|
-
for (const part of parts) {
|
|
46
|
-
if (part === '.' || part === '')
|
|
47
|
-
continue;
|
|
48
|
-
if (part === '..') {
|
|
49
|
-
resolved.pop();
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
resolved.push(part);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
const result = resolved.join('/');
|
|
56
|
-
return joined.startsWith('/') ? `/${result}` : result;
|
|
57
|
-
}
|
|
58
|
-
isAbsolute(path) {
|
|
59
|
-
return path.startsWith('/');
|
|
60
|
-
}
|
|
61
|
-
// ── Browser-specific helpers ───────────────────────────────────────────────
|
|
62
|
-
/** Store a virtual file for later reading. */
|
|
63
|
-
setItem(path, content) {
|
|
64
|
-
this.store.set(this.toKey(path), content);
|
|
65
|
-
}
|
|
66
|
-
/** Remove a virtual file. */
|
|
67
|
-
removeItem(path) {
|
|
68
|
-
this.store.delete(this.toKey(path));
|
|
69
|
-
}
|
|
70
|
-
/** List all stored virtual file paths. */
|
|
71
|
-
keys() {
|
|
72
|
-
return Array.from(this.store.keys())
|
|
73
|
-
.filter(k => k.startsWith(this.prefix))
|
|
74
|
-
.map(k => k.slice(this.prefix.length));
|
|
75
|
-
}
|
|
76
|
-
toKey(path) {
|
|
77
|
-
return `${this.prefix}${path.replace(/\\/g, '/')}`;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
//# sourceMappingURL=BrowserFsAdapter.js.map
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NodeFsAdapter — default IFsAdapter implementation using Node.js fs/path.
|
|
3
|
-
*
|
|
4
|
-
* This is the concrete adapter used in CLI and server contexts.
|
|
5
|
-
* Full platform abstraction (WEB, browser) is deferred to a future round.
|
|
6
|
-
*/
|
|
7
|
-
import { readFile, access } from 'fs/promises';
|
|
8
|
-
import { resolve, join, isAbsolute } from 'path';
|
|
9
|
-
export class NodeFsAdapter {
|
|
10
|
-
async readFile(path) {
|
|
11
|
-
return readFile(path, 'utf-8');
|
|
12
|
-
}
|
|
13
|
-
async exists(path) {
|
|
14
|
-
try {
|
|
15
|
-
await access(path);
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
resolvePath(...segments) {
|
|
23
|
-
if (segments.length === 1) {
|
|
24
|
-
return resolve(segments[0]);
|
|
25
|
-
}
|
|
26
|
-
return resolve(join(...segments));
|
|
27
|
-
}
|
|
28
|
-
isAbsolute(path) {
|
|
29
|
-
return isAbsolute(path);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/** Singleton default adapter for convenience in CLI usage. */
|
|
33
|
-
export const defaultFsAdapter = new NodeFsAdapter();
|
|
34
|
-
//# sourceMappingURL=NodeFsAdapter.js.map
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin Provider Runtime
|
|
3
|
-
*
|
|
4
|
-
* Implements ProviderRuntime for `type: plugin` providers in crewx.yaml.
|
|
5
|
-
* Wraps an arbitrary CLI tool as a CrewX agent via child_process.spawn (shell: false).
|
|
6
|
-
*
|
|
7
|
-
* Symmetric to remote/remote-provider.ts — registered as the `plugin` namespace factory.
|
|
8
|
-
*/
|
|
9
|
-
import { spawn } from 'child_process';
|
|
10
|
-
import { ProviderError } from '../provider/bridge.js';
|
|
11
|
-
/**
|
|
12
|
-
* ProviderRuntime implementation for plugin (CLI-wrapped) providers.
|
|
13
|
-
*
|
|
14
|
-
* Created by the `plugin` namespace factory when `createProvider('plugin/mock')` is called.
|
|
15
|
-
* Spawns the configured CLI with args derived from query_args/execute_args,
|
|
16
|
-
* applies `{model}` substitution, and routes the prompt via args or stdin.
|
|
17
|
-
*/
|
|
18
|
-
export class PluginProviderRuntime {
|
|
19
|
-
config;
|
|
20
|
-
providerStr;
|
|
21
|
-
constructor(id, config) {
|
|
22
|
-
this.config = config;
|
|
23
|
-
this.providerStr = `plugin/${id}`;
|
|
24
|
-
// Validate at construction time (fail early, not at call time)
|
|
25
|
-
validateCliCommand(config.cli_command);
|
|
26
|
-
validateCliArgs(config.query_args);
|
|
27
|
-
validateCliArgs(config.execute_args);
|
|
28
|
-
if (config.error_patterns)
|
|
29
|
-
validateErrorPatterns(config.error_patterns);
|
|
30
|
-
if (config.env)
|
|
31
|
-
validateEnv(config.env);
|
|
32
|
-
}
|
|
33
|
-
async query(message, options) {
|
|
34
|
-
const model = options?.model ?? this.config.default_model ?? 'default';
|
|
35
|
-
const args = resolveArgs(this.config.query_args, model, message, this.config.prompt_in_args);
|
|
36
|
-
const stdinMessage = this.config.prompt_in_args ? undefined : message;
|
|
37
|
-
options?.onCommand?.(`${this.config.cli_command} ${args.join(' ')}`);
|
|
38
|
-
const timeoutMs = this.config.timeout?.query ?? 600000;
|
|
39
|
-
return this.runProcess(args, options, stdinMessage, timeoutMs);
|
|
40
|
-
}
|
|
41
|
-
async execute(message, options) {
|
|
42
|
-
const model = options?.model ?? this.config.default_model ?? 'default';
|
|
43
|
-
const args = resolveArgs(this.config.execute_args, model, message, this.config.prompt_in_args);
|
|
44
|
-
const stdinMessage = this.config.prompt_in_args ? undefined : message;
|
|
45
|
-
options?.onCommand?.(`${this.config.cli_command} ${args.join(' ')}`);
|
|
46
|
-
const timeoutMs = this.config.timeout?.execute ?? 600000;
|
|
47
|
-
return this.runProcess(args, options, stdinMessage, timeoutMs);
|
|
48
|
-
}
|
|
49
|
-
runProcess(args, options, stdinMessage, timeoutMs) {
|
|
50
|
-
return new Promise((resolve, reject) => {
|
|
51
|
-
// Merge env: process.env is the base; config.env values are added on top.
|
|
52
|
-
// Never replaces process.env — only extends it.
|
|
53
|
-
const mergedEnv = {
|
|
54
|
-
...process.env,
|
|
55
|
-
...(this.config.env ?? {}),
|
|
56
|
-
};
|
|
57
|
-
const proc = spawn(this.config.cli_command, args, {
|
|
58
|
-
env: mergedEnv,
|
|
59
|
-
shell: false, // Security: no shell interpolation
|
|
60
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
61
|
-
});
|
|
62
|
-
// Write prompt to stdin when prompt_in_args is false
|
|
63
|
-
if (stdinMessage !== undefined) {
|
|
64
|
-
proc.stdin.write(stdinMessage);
|
|
65
|
-
proc.stdin.end();
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
proc.stdin.end();
|
|
69
|
-
}
|
|
70
|
-
if (proc.pid !== undefined) {
|
|
71
|
-
options?.onPid?.(proc.pid);
|
|
72
|
-
}
|
|
73
|
-
let stdout = '';
|
|
74
|
-
let stderr = '';
|
|
75
|
-
let stdoutBuf = '';
|
|
76
|
-
let stderrBuf = '';
|
|
77
|
-
proc.stdout.on('data', (chunk) => {
|
|
78
|
-
const str = chunk.toString();
|
|
79
|
-
stdout += str;
|
|
80
|
-
stdoutBuf += str;
|
|
81
|
-
const lines = stdoutBuf.split('\n');
|
|
82
|
-
stdoutBuf = lines.pop() ?? '';
|
|
83
|
-
for (const line of lines) {
|
|
84
|
-
if (line.trim())
|
|
85
|
-
options?.onOutput?.(line, 'stdout');
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
proc.stderr.on('data', (chunk) => {
|
|
89
|
-
const str = chunk.toString();
|
|
90
|
-
stderr += str;
|
|
91
|
-
stderrBuf += str;
|
|
92
|
-
const lines = stderrBuf.split('\n');
|
|
93
|
-
stderrBuf = lines.pop() ?? '';
|
|
94
|
-
for (const line of lines) {
|
|
95
|
-
if (line.trim())
|
|
96
|
-
options?.onOutput?.(line, 'stderr');
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
// Hard timeout — terminate and reject if exceeded
|
|
100
|
-
const timeoutHandle = setTimeout(() => {
|
|
101
|
-
proc.kill('SIGTERM');
|
|
102
|
-
reject(new ProviderError(`Plugin provider "${this.providerStr}" timed out after ${timeoutMs}ms`, this.providerStr));
|
|
103
|
-
}, timeoutMs);
|
|
104
|
-
proc.on('error', (err) => {
|
|
105
|
-
clearTimeout(timeoutHandle);
|
|
106
|
-
if (err.code === 'ENOENT') {
|
|
107
|
-
const hint = this.config.not_installed_message
|
|
108
|
-
?? `CLI command "${this.config.cli_command}" not found. Is ${this.config.id} installed?`;
|
|
109
|
-
reject(new ProviderError(hint, this.providerStr));
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
reject(new ProviderError(`Spawn error: ${err.message}`, this.providerStr));
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
proc.on('close', (code) => {
|
|
116
|
-
clearTimeout(timeoutHandle);
|
|
117
|
-
// Flush residual buffered content (no trailing newline)
|
|
118
|
-
if (stdoutBuf.trim())
|
|
119
|
-
options?.onOutput?.(stdoutBuf, 'stdout');
|
|
120
|
-
if (stderrBuf.trim())
|
|
121
|
-
options?.onOutput?.(stderrBuf, 'stderr');
|
|
122
|
-
const exitCode = code ?? 0;
|
|
123
|
-
options?.onExitCode?.(exitCode);
|
|
124
|
-
// Check configured error patterns before exit code
|
|
125
|
-
if (this.config.error_patterns) {
|
|
126
|
-
const combined = stderr || stdout;
|
|
127
|
-
for (const ep of this.config.error_patterns) {
|
|
128
|
-
if (combined.includes(ep.pattern)) {
|
|
129
|
-
reject(new ProviderError(ep.message, this.providerStr));
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (code !== 0) {
|
|
135
|
-
reject(new ProviderError(`Process exited with code ${code}: ${stderr.slice(0, 500)}`, this.providerStr));
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
resolve(stdout.trim());
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// ── Factory ─────────────────────────────────────────────────────────────────
|
|
144
|
-
/**
|
|
145
|
-
* Create a provider factory function for the `plugin` namespace.
|
|
146
|
-
*
|
|
147
|
-
* @param pluginConfigs - Map of plugin provider ID → config (from crewx.yaml `providers:` section)
|
|
148
|
-
* @returns ProviderFactory to register via registerProviderFactory('plugin', factory)
|
|
149
|
-
*/
|
|
150
|
-
export function createPluginProviderFactory(pluginConfigs) {
|
|
151
|
-
return (id, _providerStr) => {
|
|
152
|
-
const config = pluginConfigs.get(id);
|
|
153
|
-
if (!config) {
|
|
154
|
-
throw new ProviderError(`Plugin provider "${id}" not found. Available: ${Array.from(pluginConfigs.keys()).join(', ') || '(none)'}`, `plugin/${id}`);
|
|
155
|
-
}
|
|
156
|
-
return new PluginProviderRuntime(id, config);
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
160
|
-
/**
|
|
161
|
-
* Build the final args array for a CLI invocation:
|
|
162
|
-
* 1. Replace `{model}` placeholders in each arg.
|
|
163
|
-
* 2. If promptInArgs is true, append the prompt as a positional argument.
|
|
164
|
-
*/
|
|
165
|
-
function resolveArgs(templateArgs, model, prompt, promptInArgs) {
|
|
166
|
-
const args = templateArgs.map(arg => arg.replace(/\{model\}/g, model));
|
|
167
|
-
if (promptInArgs) {
|
|
168
|
-
args.push(prompt);
|
|
169
|
-
}
|
|
170
|
-
return args;
|
|
171
|
-
}
|
|
172
|
-
// ── Validation ───────────────────────────────────────────────────────────────
|
|
173
|
-
function validateCliCommand(cliCommand) {
|
|
174
|
-
if (!cliCommand || typeof cliCommand !== 'string') {
|
|
175
|
-
throw new Error('Plugin provider requires a cli_command');
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
function validateCliArgs(args) {
|
|
179
|
-
if (!Array.isArray(args)) {
|
|
180
|
-
throw new Error('CLI arguments must be an array');
|
|
181
|
-
}
|
|
182
|
-
for (const arg of args) {
|
|
183
|
-
if (typeof arg !== 'string') {
|
|
184
|
-
throw new Error('Each CLI argument must be a string');
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
function validateErrorPatterns(patterns) {
|
|
189
|
-
for (const { pattern } of patterns) {
|
|
190
|
-
if (typeof pattern !== 'string') {
|
|
191
|
-
throw new Error('Error pattern must be a string');
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
function validateEnv(env) {
|
|
196
|
-
for (const [key, value] of Object.entries(env)) {
|
|
197
|
-
if (typeof key !== 'string' || typeof value !== 'string') {
|
|
198
|
-
throw new Error('env entries must be string key/value pairs');
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
//# sourceMappingURL=plugin-provider.js.map
|
package/dist/esm/plugin/types.js
DELETED
package/dist/esm/plugin.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CrewxPlugin — base class for SDK plugins.
|
|
3
|
-
*
|
|
4
|
-
* Plugins subscribe to Crewx events in attach() and clean up in detach().
|
|
5
|
-
* Register via Crewx.use(plugin).
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* class MyPlugin extends CrewxPlugin {
|
|
9
|
-
* readonly name = 'my-plugin';
|
|
10
|
-
* attach(crewx: Crewx) {
|
|
11
|
-
* crewx.on('task:start', (e) => console.log(e.traceId));
|
|
12
|
-
* }
|
|
13
|
-
* }
|
|
14
|
-
* await crewx.use(new MyPlugin());
|
|
15
|
-
*/
|
|
16
|
-
export class CrewxPlugin {
|
|
17
|
-
/**
|
|
18
|
-
* Called on Crewx.close() in LIFO order.
|
|
19
|
-
* Remove event listeners and release resources here.
|
|
20
|
-
* Default: no-op.
|
|
21
|
-
*/
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
23
|
-
detach(_crewx) { }
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=plugin.js.map
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser-safe provider bridge.
|
|
3
|
-
* Registry pattern only — no child_process / CLI spawn.
|
|
4
|
-
*/
|
|
5
|
-
export class ProviderError extends Error {
|
|
6
|
-
providerStr;
|
|
7
|
-
constructor(message, providerStr) {
|
|
8
|
-
super(message);
|
|
9
|
-
this.providerStr = providerStr;
|
|
10
|
-
this.name = 'ProviderError';
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
// ── Provider factory registry ───────────────────────────────────────────────
|
|
14
|
-
const _providerFactories = new Map();
|
|
15
|
-
/**
|
|
16
|
-
* Register a provider factory for a given namespace.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* registerProviderFactory('api', (id, providerStr) => ({
|
|
20
|
-
* async query(message, options) { ... },
|
|
21
|
-
* async execute(message, options) { ... },
|
|
22
|
-
* }));
|
|
23
|
-
*/
|
|
24
|
-
export function registerProviderFactory(namespace, factory) {
|
|
25
|
-
_providerFactories.set(namespace, factory);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Create a provider runtime for the given provider string.
|
|
29
|
-
* Browser version — only supports registered factories (no CLI fallback).
|
|
30
|
-
*/
|
|
31
|
-
export function createProvider(providerStr) {
|
|
32
|
-
const parts = providerStr.split('/');
|
|
33
|
-
if (parts.length !== 2) {
|
|
34
|
-
throw new ProviderError(`Invalid provider format: "${providerStr}". Expected namespace/id (e.g., api/webllm)`, providerStr);
|
|
35
|
-
}
|
|
36
|
-
const [namespace, id] = parts;
|
|
37
|
-
const factory = _providerFactories.get(namespace);
|
|
38
|
-
if (factory) {
|
|
39
|
-
return factory(id, providerStr);
|
|
40
|
-
}
|
|
41
|
-
throw new ProviderError(`Unsupported provider namespace: "${namespace}". Register a factory with registerProviderFactory('${namespace}', factory).`, providerStr);
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=bridge.browser.js.map
|