@tagma/sdk 0.4.2 → 0.4.3

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 (105) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/stdin-approval.d.ts +6 -0
  3. package/dist/adapters/stdin-approval.d.ts.map +1 -0
  4. package/dist/adapters/stdin-approval.js +90 -0
  5. package/dist/adapters/stdin-approval.js.map +1 -0
  6. package/dist/adapters/websocket-approval.d.ts +28 -0
  7. package/dist/adapters/websocket-approval.d.ts.map +1 -0
  8. package/dist/adapters/websocket-approval.js +145 -0
  9. package/dist/adapters/websocket-approval.js.map +1 -0
  10. package/dist/approval.d.ts +13 -0
  11. package/dist/approval.d.ts.map +1 -0
  12. package/dist/approval.js +91 -0
  13. package/dist/approval.js.map +1 -0
  14. package/dist/bootstrap.d.ts +2 -0
  15. package/dist/bootstrap.d.ts.map +1 -0
  16. package/dist/bootstrap.js +30 -0
  17. package/dist/bootstrap.js.map +1 -0
  18. package/dist/completions/exit-code.d.ts +3 -0
  19. package/dist/completions/exit-code.d.ts.map +1 -0
  20. package/dist/completions/exit-code.js +25 -0
  21. package/dist/completions/exit-code.js.map +1 -0
  22. package/dist/completions/file-exists.d.ts +3 -0
  23. package/dist/completions/file-exists.d.ts.map +1 -0
  24. package/dist/completions/file-exists.js +58 -0
  25. package/dist/completions/file-exists.js.map +1 -0
  26. package/dist/completions/output-check.d.ts +3 -0
  27. package/dist/completions/output-check.d.ts.map +1 -0
  28. package/dist/completions/output-check.js +81 -0
  29. package/dist/completions/output-check.js.map +1 -0
  30. package/dist/config-ops.d.ts +55 -0
  31. package/dist/config-ops.d.ts.map +1 -0
  32. package/dist/config-ops.js +258 -0
  33. package/dist/config-ops.js.map +1 -0
  34. package/dist/dag.d.ts +46 -0
  35. package/dist/dag.d.ts.map +1 -0
  36. package/dist/dag.js +193 -0
  37. package/dist/dag.js.map +1 -0
  38. package/dist/drivers/claude-code.d.ts +3 -0
  39. package/dist/drivers/claude-code.d.ts.map +1 -0
  40. package/dist/drivers/claude-code.js +210 -0
  41. package/dist/drivers/claude-code.js.map +1 -0
  42. package/dist/engine.d.ts +89 -0
  43. package/dist/engine.d.ts.map +1 -0
  44. package/dist/engine.js +815 -0
  45. package/dist/engine.js.map +1 -0
  46. package/dist/hooks.d.ts +73 -0
  47. package/dist/hooks.d.ts.map +1 -0
  48. package/dist/hooks.js +103 -0
  49. package/dist/hooks.js.map +1 -0
  50. package/dist/logger.d.ts +61 -0
  51. package/dist/logger.d.ts.map +1 -0
  52. package/dist/logger.js +151 -0
  53. package/dist/logger.js.map +1 -0
  54. package/dist/middlewares/static-context.d.ts +3 -0
  55. package/dist/middlewares/static-context.d.ts.map +1 -0
  56. package/dist/middlewares/static-context.js +36 -0
  57. package/dist/middlewares/static-context.js.map +1 -0
  58. package/dist/pipeline-runner.d.ts +50 -0
  59. package/dist/pipeline-runner.d.ts.map +1 -0
  60. package/dist/pipeline-runner.js +139 -0
  61. package/dist/pipeline-runner.js.map +1 -0
  62. package/dist/registry.d.ts +43 -0
  63. package/dist/registry.d.ts.map +1 -0
  64. package/dist/registry.js +182 -0
  65. package/dist/registry.js.map +1 -0
  66. package/dist/runner.d.ts +19 -0
  67. package/dist/runner.d.ts.map +1 -0
  68. package/dist/runner.js +364 -0
  69. package/dist/runner.js.map +1 -0
  70. package/dist/schema.d.ts +27 -0
  71. package/dist/schema.d.ts.map +1 -0
  72. package/dist/schema.js +373 -0
  73. package/dist/schema.js.map +1 -0
  74. package/dist/sdk.d.ts +27 -0
  75. package/dist/sdk.d.ts.map +1 -0
  76. package/dist/sdk.js +33 -0
  77. package/dist/sdk.js.map +1 -0
  78. package/dist/templates.d.ts +20 -0
  79. package/dist/templates.d.ts.map +1 -0
  80. package/dist/templates.js +93 -0
  81. package/dist/templates.js.map +1 -0
  82. package/dist/triggers/file.d.ts +3 -0
  83. package/dist/triggers/file.d.ts.map +1 -0
  84. package/dist/triggers/file.js +123 -0
  85. package/dist/triggers/file.js.map +1 -0
  86. package/dist/triggers/manual.d.ts +3 -0
  87. package/dist/triggers/manual.d.ts.map +1 -0
  88. package/dist/triggers/manual.js +73 -0
  89. package/dist/triggers/manual.js.map +1 -0
  90. package/dist/types.d.ts +4 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +14 -0
  93. package/dist/types.js.map +1 -0
  94. package/dist/utils.d.ts +14 -0
  95. package/dist/utils.d.ts.map +1 -0
  96. package/dist/utils.js +138 -0
  97. package/dist/utils.js.map +1 -0
  98. package/dist/validate-raw.d.ts +26 -0
  99. package/dist/validate-raw.d.ts.map +1 -0
  100. package/dist/validate-raw.js +260 -0
  101. package/dist/validate-raw.js.map +1 -0
  102. package/package.json +26 -17
  103. package/src/approval.ts +5 -3
  104. package/src/registry.ts +214 -214
  105. package/scripts/preinstall.js +0 -38
@@ -0,0 +1,139 @@
1
+ // ═══ PipelineRunner ═══
2
+ //
3
+ // Wraps runPipeline in a lifecycle object suited for multi-pipeline management
4
+ // in sidecar / Tauri IPC scenarios. Each instance controls one pipeline run.
5
+ //
6
+ // Typical sidecar usage:
7
+ //
8
+ // const runners = new Map<string, PipelineRunner>();
9
+ //
10
+ // const runner = new PipelineRunner(config, workDir);
11
+ // runner.subscribe(event => ipcEmit('pipeline_event', event));
12
+ // runner.start();
13
+ // runners.set(runner.instanceId, runner);
14
+ //
15
+ // // Later, from IPC:
16
+ // runners.get(id)?.abort();
17
+ import { runPipeline } from './engine';
18
+ import { generateRunId } from './utils';
19
+ export class PipelineRunner {
20
+ config;
21
+ workDir;
22
+ opts;
23
+ /**
24
+ * Stable ID assigned before start() — safe to use as a Map key in the sidecar
25
+ * before the engine-assigned runId becomes available.
26
+ */
27
+ instanceId;
28
+ /**
29
+ * The runId generated by the engine. Available after the first 'pipeline_start'
30
+ * event fires (i.e. effectively immediately after start() is called).
31
+ * null until then.
32
+ */
33
+ _runId = null;
34
+ _status = 'idle';
35
+ _result = null;
36
+ _abortController = new AbortController();
37
+ _handlers = new Set();
38
+ _states = null;
39
+ _statesMirror = new Map();
40
+ constructor(config, workDir, opts = {}) {
41
+ this.config = config;
42
+ this.workDir = workDir;
43
+ this.opts = opts;
44
+ this.instanceId = generateRunId();
45
+ }
46
+ get runId() { return this._runId; }
47
+ get status() { return this._status; }
48
+ /**
49
+ * Start the pipeline. Calling start() more than once returns the same Promise.
50
+ */
51
+ start() {
52
+ if (this._result)
53
+ return this._result;
54
+ // Guard: if abort() was called before start(), the signal is already
55
+ // aborted. Create a fresh controller so the pipeline doesn't terminate
56
+ // immediately. If users truly want pre-abort semantics, they call
57
+ // abort() after start().
58
+ if (this._abortController.signal.aborted) {
59
+ this._abortController = new AbortController();
60
+ this._status = 'idle';
61
+ }
62
+ this._status = 'running';
63
+ this._result = runPipeline(this.config, this.workDir, {
64
+ ...this.opts,
65
+ signal: this._abortController.signal,
66
+ onEvent: (event) => {
67
+ if (event.type === 'pipeline_start') {
68
+ this._runId = event.runId;
69
+ // Initialize the live mirror with the full initial state snapshot
70
+ for (const [id, state] of event.states) {
71
+ this._statesMirror.set(id, { ...state });
72
+ }
73
+ }
74
+ if (event.type === 'task_status_change') {
75
+ // Keep the mirror up to date so getStates() works during the run
76
+ this._statesMirror.set(event.taskId, event.state);
77
+ }
78
+ if (event.type === 'pipeline_end') {
79
+ this._status = this._abortController.signal.aborted ? 'aborted' : 'done';
80
+ }
81
+ for (const h of this._handlers)
82
+ h(event);
83
+ },
84
+ }).then(result => {
85
+ this._states = result.states;
86
+ if (this._status === 'running')
87
+ this._status = 'done';
88
+ return result;
89
+ }).catch(err => {
90
+ this._status = 'aborted';
91
+ throw err;
92
+ });
93
+ return this._result;
94
+ }
95
+ /**
96
+ * Cancel the running pipeline. Safe to call multiple times or before start().
97
+ */
98
+ abort(reason) {
99
+ this._status = 'aborted';
100
+ this._abortController.abort(reason);
101
+ }
102
+ /**
103
+ * Live snapshot of task states. Available from the first pipeline_start event onward
104
+ * (i.e. as soon as start() is called) and remains accessible after the run completes.
105
+ * Returns null only if the pipeline has never started.
106
+ */
107
+ getStates() {
108
+ if (this._states)
109
+ return snapshotStates(this._states);
110
+ if (this._statesMirror.size > 0)
111
+ return snapshotStates(this._statesMirror);
112
+ return null;
113
+ }
114
+ /**
115
+ * Subscribe to pipeline/task events. Returns an unsubscribe function.
116
+ * Events are emitted synchronously in the engine's event loop, so keep
117
+ * handlers non-blocking (e.g. queue to IPC, do not await inside).
118
+ */
119
+ subscribe(handler) {
120
+ this._handlers.add(handler);
121
+ return () => this._handlers.delete(handler);
122
+ }
123
+ }
124
+ /** Deep-copy a states map so callers cannot mutate SDK internals. */
125
+ function snapshotStates(src) {
126
+ const copy = new Map();
127
+ for (const [id, s] of src) {
128
+ copy.set(id, {
129
+ config: { ...s.config },
130
+ trackConfig: { ...s.trackConfig },
131
+ status: s.status,
132
+ result: s.result ? { ...s.result } : null,
133
+ startedAt: s.startedAt,
134
+ finishedAt: s.finishedAt,
135
+ });
136
+ }
137
+ return copy;
138
+ }
139
+ //# sourceMappingURL=pipeline-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-runner.js","sourceRoot":"","sources":["../src/pipeline-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { PluginCategory, DriverPlugin, TriggerPlugin, CompletionPlugin, MiddlewarePlugin, PluginManifest } from './types';
2
+ type PluginType = DriverPlugin | TriggerPlugin | CompletionPlugin | MiddlewarePlugin;
3
+ export type RegisterResult = 'registered' | 'replaced' | 'unchanged';
4
+ /**
5
+ * Register a plugin under (category, type). Returns:
6
+ * - 'registered' on first registration
7
+ * - 'replaced' when an existing entry was overwritten with a different handler
8
+ * - 'unchanged' when the same handler instance was already present
9
+ *
10
+ * Throws if `category` is unknown, `type` is empty, or `handler` violates the
11
+ * minimum interface contract for the category.
12
+ */
13
+ export declare function registerPlugin<T extends PluginType>(category: PluginCategory, type: string, handler: T): RegisterResult;
14
+ /**
15
+ * Remove a plugin from the in-process registry. Returns true if a plugin
16
+ * was actually removed. Note: ESM module caching is not affected, so
17
+ * re-importing the same file after unregister will yield the cached module —
18
+ * callers wanting a fresh load must restart the host process.
19
+ */
20
+ export declare function unregisterPlugin(category: PluginCategory, type: string): boolean;
21
+ export declare function getHandler<T extends PluginType>(category: PluginCategory, type: string): T;
22
+ export declare function hasHandler(category: PluginCategory, type: string): boolean;
23
+ export declare const PLUGIN_NAME_RE: RegExp;
24
+ export declare function isValidPluginName(name: unknown): name is string;
25
+ /**
26
+ * Parse and validate the `tagmaPlugin` field of a `package.json` blob.
27
+ *
28
+ * Returns the strongly-typed manifest if the field is present and
29
+ * well-formed (`category` is one of the four known categories and `type`
30
+ * is a non-empty string). Returns `null` if the field is absent — that
31
+ * is the host's signal that the package is a library, not a plugin.
32
+ *
33
+ * Throws if the field is present but malformed: that's a packaging bug
34
+ * the plugin author should hear about loudly, not a silent skip.
35
+ *
36
+ * Hosts use this during auto-discovery to decide whether to load a
37
+ * package as a plugin without having to dynamically `import()` it.
38
+ */
39
+ export declare function readPluginManifest(pkgJson: unknown): PluginManifest | null;
40
+ export declare function loadPlugins(pluginNames: readonly string[]): Promise<void>;
41
+ export declare function listRegistered(category: PluginCategory): string[];
42
+ export {};
43
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAAE,YAAY,EAAE,aAAa,EAC3C,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EACnD,MAAM,SAAS,CAAC;AAEjB,KAAK,UAAU,GAAG,YAAY,GAAG,aAAa,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AAsFrF,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,UAAU,GAAG,WAAW,CAAC;AAErE;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,UAAU,EACjD,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GACjD,cAAc,CAchB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGhF;AAED,wBAAgB,UAAU,CAAC,CAAC,SAAS,UAAU,EAC7C,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GACrC,CAAC,CASH;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1E;AAKD,eAAO,MAAM,cAAc,QAA4D,CAAC;AAExF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM,CAE/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAmB1E;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB/E;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,EAAE,CAEjE"}
@@ -0,0 +1,182 @@
1
+ const VALID_CATEGORIES = new Set([
2
+ 'drivers', 'triggers', 'completions', 'middlewares',
3
+ ]);
4
+ const registries = {
5
+ drivers: new Map(),
6
+ triggers: new Map(),
7
+ completions: new Map(),
8
+ middlewares: new Map(),
9
+ };
10
+ /**
11
+ * Minimal contract enforcement so a malformed plugin fails fast at
12
+ * registration time rather than crashing the engine mid-run.
13
+ *
14
+ * For drivers we materialize `capabilities` and assert each field is a
15
+ * boolean — otherwise a plugin author can write
16
+ * get capabilities() { throw new Error('boom') }
17
+ * and pass the basic typeof check, then crash preflight when the engine
18
+ * touches `driver.capabilities.sessionResume`. (R8)
19
+ */
20
+ function validateContract(category, handler) {
21
+ if (!handler || typeof handler !== 'object') {
22
+ throw new Error(`Plugin handler for category "${category}" must be an object`);
23
+ }
24
+ const h = handler;
25
+ if (typeof h.name !== 'string' || h.name.length === 0) {
26
+ throw new Error(`Plugin handler for category "${category}" must declare a non-empty "name"`);
27
+ }
28
+ switch (category) {
29
+ case 'drivers': {
30
+ if (typeof h.buildCommand !== 'function') {
31
+ throw new Error(`drivers plugin "${h.name}" must export buildCommand()`);
32
+ }
33
+ // Materialize capabilities — this triggers any throwing getter NOW
34
+ // instead of during preflight.
35
+ let caps;
36
+ try {
37
+ caps = h.capabilities;
38
+ }
39
+ catch (err) {
40
+ throw new Error(`drivers plugin "${h.name}" capabilities accessor threw: ` +
41
+ (err instanceof Error ? err.message : String(err)));
42
+ }
43
+ if (!caps || typeof caps !== 'object') {
44
+ throw new Error(`drivers plugin "${h.name}" must declare capabilities object`);
45
+ }
46
+ const c = caps;
47
+ for (const field of ['sessionResume', 'systemPrompt', 'outputFormat']) {
48
+ if (typeof c[field] !== 'boolean') {
49
+ throw new Error(`drivers plugin "${h.name}".capabilities.${field} must be a boolean (got ${typeof c[field]})`);
50
+ }
51
+ }
52
+ // Optional methods, but if present must be functions.
53
+ for (const opt of ['parseResult', 'resolveModel', 'resolveTools']) {
54
+ if (h[opt] !== undefined && typeof h[opt] !== 'function') {
55
+ throw new Error(`drivers plugin "${h.name}".${opt} must be a function or undefined`);
56
+ }
57
+ }
58
+ break;
59
+ }
60
+ case 'triggers':
61
+ if (typeof h.watch !== 'function') {
62
+ throw new Error(`triggers plugin "${h.name}" must export watch()`);
63
+ }
64
+ break;
65
+ case 'completions':
66
+ if (typeof h.check !== 'function') {
67
+ throw new Error(`completions plugin "${h.name}" must export check()`);
68
+ }
69
+ break;
70
+ case 'middlewares':
71
+ if (typeof h.enhance !== 'function') {
72
+ throw new Error(`middlewares plugin "${h.name}" must export enhance()`);
73
+ }
74
+ break;
75
+ }
76
+ }
77
+ /**
78
+ * Register a plugin under (category, type). Returns:
79
+ * - 'registered' on first registration
80
+ * - 'replaced' when an existing entry was overwritten with a different handler
81
+ * - 'unchanged' when the same handler instance was already present
82
+ *
83
+ * Throws if `category` is unknown, `type` is empty, or `handler` violates the
84
+ * minimum interface contract for the category.
85
+ */
86
+ export function registerPlugin(category, type, handler) {
87
+ if (!VALID_CATEGORIES.has(category)) {
88
+ throw new Error(`Unknown plugin category "${category}"`);
89
+ }
90
+ if (typeof type !== 'string' || type.length === 0) {
91
+ throw new Error(`Plugin type must be a non-empty string (category="${category}")`);
92
+ }
93
+ validateContract(category, handler);
94
+ const registry = registries[category];
95
+ const existing = registry.get(type);
96
+ if (existing === handler)
97
+ return 'unchanged';
98
+ const wasReplaced = existing !== undefined;
99
+ registry.set(type, handler);
100
+ return wasReplaced ? 'replaced' : 'registered';
101
+ }
102
+ /**
103
+ * Remove a plugin from the in-process registry. Returns true if a plugin
104
+ * was actually removed. Note: ESM module caching is not affected, so
105
+ * re-importing the same file after unregister will yield the cached module —
106
+ * callers wanting a fresh load must restart the host process.
107
+ */
108
+ export function unregisterPlugin(category, type) {
109
+ if (!VALID_CATEGORIES.has(category))
110
+ return false;
111
+ return registries[category].delete(type);
112
+ }
113
+ export function getHandler(category, type) {
114
+ const handler = registries[category].get(type);
115
+ if (!handler) {
116
+ throw new Error(`${category} type "${type}" not registered.\n` +
117
+ `Install the plugin: bun add @tagma/${category.replace(/s$/, '')}-${type}`);
118
+ }
119
+ return handler;
120
+ }
121
+ export function hasHandler(category, type) {
122
+ return registries[category].has(type);
123
+ }
124
+ // Plugin name must be a scoped npm package or a tagma-prefixed package.
125
+ // Reject absolute/relative paths and suspicious patterns to prevent
126
+ // arbitrary code execution via crafted YAML configs.
127
+ export const PLUGIN_NAME_RE = /^(@[a-z0-9-]+\/[a-z0-9._-]+|tagma-plugin-[a-z0-9._-]+)$/;
128
+ export function isValidPluginName(name) {
129
+ return typeof name === 'string' && PLUGIN_NAME_RE.test(name);
130
+ }
131
+ /**
132
+ * Parse and validate the `tagmaPlugin` field of a `package.json` blob.
133
+ *
134
+ * Returns the strongly-typed manifest if the field is present and
135
+ * well-formed (`category` is one of the four known categories and `type`
136
+ * is a non-empty string). Returns `null` if the field is absent — that
137
+ * is the host's signal that the package is a library, not a plugin.
138
+ *
139
+ * Throws if the field is present but malformed: that's a packaging bug
140
+ * the plugin author should hear about loudly, not a silent skip.
141
+ *
142
+ * Hosts use this during auto-discovery to decide whether to load a
143
+ * package as a plugin without having to dynamically `import()` it.
144
+ */
145
+ export function readPluginManifest(pkgJson) {
146
+ if (!pkgJson || typeof pkgJson !== 'object')
147
+ return null;
148
+ const raw = pkgJson.tagmaPlugin;
149
+ if (raw === undefined)
150
+ return null;
151
+ if (!raw || typeof raw !== 'object') {
152
+ throw new Error('tagmaPlugin field must be an object with { category, type }');
153
+ }
154
+ const m = raw;
155
+ const category = m.category;
156
+ const type = m.type;
157
+ if (typeof category !== 'string' || !VALID_CATEGORIES.has(category)) {
158
+ throw new Error(`tagmaPlugin.category must be one of ${[...VALID_CATEGORIES].join(', ')}, got ${JSON.stringify(category)}`);
159
+ }
160
+ if (typeof type !== 'string' || type.length === 0) {
161
+ throw new Error(`tagmaPlugin.type must be a non-empty string, got ${JSON.stringify(type)}`);
162
+ }
163
+ return { category: category, type };
164
+ }
165
+ export async function loadPlugins(pluginNames) {
166
+ for (const name of pluginNames) {
167
+ if (!isValidPluginName(name)) {
168
+ throw new Error(`Plugin "${name}" rejected: plugin names must be scoped npm packages ` +
169
+ `(e.g. @tagma/trigger-xyz) or tagma-plugin-* packages. ` +
170
+ `Relative/absolute paths are not allowed.`);
171
+ }
172
+ const mod = await import(name);
173
+ if (!mod.pluginCategory || !mod.pluginType || !mod.default) {
174
+ throw new Error(`Plugin "${name}" must export pluginCategory, pluginType, and default`);
175
+ }
176
+ registerPlugin(mod.pluginCategory, mod.pluginType, mod.default);
177
+ }
178
+ }
179
+ export function listRegistered(category) {
180
+ return [...registries[category].keys()];
181
+ }
182
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { SpawnSpec, DriverPlugin, TaskResult } from './types';
2
+ export interface RunOptions {
3
+ readonly timeoutMs?: number;
4
+ readonly signal?: AbortSignal;
5
+ }
6
+ /**
7
+ * R2: Validate a SpawnSpec returned by a third-party driver. Returns null on
8
+ * success or a human-readable error message describing the first violation.
9
+ *
10
+ * Catching this here is critical: an undetected bad spec ends up calling
11
+ * Bun.spawn with garbage and the resulting TypeError leaks into engine
12
+ * processTask's catch block as "Cannot read properties of undefined". By
13
+ * validating here we surface a clear "Driver X returned invalid args" message
14
+ * instead, and short-circuit before holding any process resources.
15
+ */
16
+ export declare function validateSpawnSpec(spec: unknown, driverName: string): string | null;
17
+ export declare function runSpawn(spec: SpawnSpec, driver: DriverPlugin | null, opts?: RunOptions): Promise<TaskResult>;
18
+ export declare function runCommand(command: string, cwd: string, opts?: RunOptions): Promise<TaskResult>;
19
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAc,MAAM,SAAS,CAAC;AA+B/E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAsFD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoClF;AAED,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,YAAY,GAAG,IAAI,EAC3B,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,UAAU,CAAC,CAmNrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,UAAU,CAAC,CAMrB"}