mthds 0.8.1 → 0.10.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/README.md +43 -21
- package/dist/agent/commands/api-commands.js +290 -93
- package/dist/agent/commands/api-commands.js.map +1 -1
- package/dist/agent/commands/config.js +2 -2
- package/dist/agent/commands/config.js.map +1 -1
- package/dist/agent/commands/update-check.d.ts +14 -1
- package/dist/agent/commands/update-check.js +238 -16
- package/dist/agent/commands/update-check.js.map +1 -1
- package/dist/agent/commands/validate.js +5 -13
- package/dist/agent/commands/validate.js.map +1 -1
- package/dist/agent/plugin-version.d.ts +1 -1
- package/dist/agent/plugin-version.js +1 -1
- package/dist/agent/remote-version.d.ts +31 -0
- package/dist/agent/remote-version.js +74 -0
- package/dist/agent/remote-version.js.map +1 -0
- package/dist/agent/update-cache.d.ts +41 -0
- package/dist/agent/update-cache.js +129 -0
- package/dist/agent/update-cache.js.map +1 -1
- package/dist/agent-cli.js +5 -5
- package/dist/agent-cli.js.map +1 -1
- package/dist/cli/commands/config.js +2 -2
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/install.js +19 -39
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/run.js +82 -69
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/setup.js +22 -23
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/utils.d.ts +1 -1
- package/dist/cli/commands/validate.js +10 -14
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/config/config.d.ts +14 -1
- package/dist/config/config.js +31 -6
- package/dist/config/config.js.map +1 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +22 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol/concept.d.ts +14 -0
- package/dist/protocol/concept.js +10 -0
- package/dist/protocol/concept.js.map +1 -0
- package/dist/protocol/exceptions.d.ts +10 -0
- package/dist/protocol/exceptions.js +12 -0
- package/dist/protocol/exceptions.js.map +1 -0
- package/dist/protocol/models.d.ts +95 -0
- package/dist/protocol/models.js +24 -0
- package/dist/protocol/models.js.map +1 -0
- package/dist/protocol/options.d.ts +60 -0
- package/dist/protocol/options.js +11 -0
- package/dist/protocol/options.js.map +1 -0
- package/dist/protocol/pipe_output.d.ts +11 -0
- package/dist/protocol/pipe_output.js +7 -0
- package/dist/protocol/pipe_output.js.map +1 -0
- package/dist/protocol/pipeline_inputs.d.ts +8 -0
- package/dist/protocol/pipeline_inputs.js +7 -0
- package/dist/protocol/pipeline_inputs.js.map +1 -0
- package/dist/protocol/protocol.d.ts +47 -0
- package/dist/{client → protocol}/protocol.js.map +1 -1
- package/dist/protocol/stuff.d.ts +16 -0
- package/dist/protocol/stuff.js +8 -0
- package/dist/{client/models → protocol}/stuff.js.map +1 -1
- package/dist/protocol/working_memory.d.ts +10 -0
- package/dist/protocol/working_memory.js +7 -0
- package/dist/protocol/working_memory.js.map +1 -0
- package/dist/runners/api/client.d.ts +170 -0
- package/dist/runners/api/client.js +653 -0
- package/dist/runners/api/client.js.map +1 -0
- package/dist/runners/api/exceptions.d.ts +106 -0
- package/dist/runners/api/exceptions.js +141 -0
- package/dist/runners/api/exceptions.js.map +1 -0
- package/dist/runners/api/models.d.ts +38 -0
- package/dist/runners/api/models.js +13 -0
- package/dist/runners/api/models.js.map +1 -0
- package/dist/runners/api/runs.d.ts +130 -0
- package/dist/runners/api/runs.js +93 -0
- package/dist/runners/api/runs.js.map +1 -0
- package/dist/runners/base-runner.d.ts +27 -0
- package/dist/runners/base-runner.js +25 -0
- package/dist/runners/base-runner.js.map +1 -0
- package/dist/runners/pipelex/runner.d.ts +38 -0
- package/dist/runners/{pipelex-runner.js → pipelex/runner.js} +168 -83
- package/dist/runners/pipelex/runner.js.map +1 -0
- package/dist/runners/registry.js +10 -4
- package/dist/runners/registry.js.map +1 -1
- package/dist/runners/types.d.ts +13 -71
- package/dist/runners/types.js.map +1 -1
- package/package.json +6 -3
- package/dist/client/client.d.ts +0 -15
- package/dist/client/client.js +0 -127
- package/dist/client/client.js.map +0 -1
- package/dist/client/exceptions.d.ts +0 -46
- package/dist/client/exceptions.js +0 -61
- package/dist/client/exceptions.js.map +0 -1
- package/dist/client/index.d.ts +0 -5
- package/dist/client/index.js +0 -3
- package/dist/client/index.js.map +0 -1
- package/dist/client/models/index.d.ts +0 -4
- package/dist/client/models/index.js +0 -2
- package/dist/client/models/index.js.map +0 -1
- package/dist/client/models/pipe_output.d.ts +0 -2
- package/dist/client/models/pipe_output.js +0 -2
- package/dist/client/models/pipe_output.js.map +0 -1
- package/dist/client/models/pipeline_inputs.d.ts +0 -3
- package/dist/client/models/pipeline_inputs.js +0 -2
- package/dist/client/models/pipeline_inputs.js.map +0 -1
- package/dist/client/models/stuff.d.ts +0 -1
- package/dist/client/models/stuff.js +0 -2
- package/dist/client/models/working_memory.d.ts +0 -1
- package/dist/client/models/working_memory.js +0 -2
- package/dist/client/models/working_memory.js.map +0 -1
- package/dist/client/pipeline.d.ts +0 -36
- package/dist/client/pipeline.js +0 -2
- package/dist/client/pipeline.js.map +0 -1
- package/dist/client/protocol.d.ts +0 -5
- package/dist/runners/api-runner.d.ts +0 -24
- package/dist/runners/api-runner.js +0 -91
- package/dist/runners/api-runner.js.map +0 -1
- package/dist/runners/pipelex-runner.d.ts +0 -30
- package/dist/runners/pipelex-runner.js.map +0 -1
- /package/dist/{client → protocol}/protocol.js +0 -0
package/README.md
CHANGED
|
@@ -136,24 +136,31 @@ npx mthds setup runner pipelex
|
|
|
136
136
|
|
|
137
137
|
### Configure the API runner
|
|
138
138
|
|
|
139
|
-
The
|
|
139
|
+
The local pipelex runner is the default; to use the hosted (or self-hosted) API instead, set up the API runner interactively:
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
142
|
mthds setup runner api
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
This prompts for the API URL and API key (masked input) and saves them to `~/.mthds/config`.
|
|
145
|
+
This prompts for the API base URL and an API key (masked input), and saves them to `~/.mthds/config`.
|
|
146
|
+
|
|
147
|
+
There is ONE base URL — the host only, with no version prefix. The SDK composes every endpoint as `{base}/v1/{endpoint}`:
|
|
148
|
+
|
|
149
|
+
- **`base-url`** — hosted: `https://api.pipelex.com` (the default). Self-hosted: `http://localhost:8081` (a bare [pipelex-api](https://github.com/Pipelex/pipelex-api) runner).
|
|
146
150
|
|
|
147
151
|
You can also set values directly:
|
|
148
152
|
|
|
149
153
|
```bash
|
|
150
154
|
mthds config set api-key YOUR_KEY
|
|
151
|
-
|
|
155
|
+
# Hosted (default):
|
|
156
|
+
mthds config set base-url https://api.pipelex.com
|
|
157
|
+
# Self-hosted bare runner:
|
|
158
|
+
mthds config set base-url http://localhost:8081
|
|
152
159
|
```
|
|
153
160
|
|
|
154
161
|
Configuration is stored in `~/.mthds/config` and shared between mthds-js and mthds-python.
|
|
155
162
|
|
|
156
|
-
You can also use environment variables (`
|
|
163
|
+
You can also use environment variables (`MTHDS_API_KEY`, `MTHDS_API_URL`) which take precedence over the config file.
|
|
157
164
|
|
|
158
165
|
See the [SDK Usage](#sdk-usage) section below to connect to a Pipelex API instance programmatically.
|
|
159
166
|
|
|
@@ -171,11 +178,11 @@ npm install mthds
|
|
|
171
178
|
import { MthdsApiClient } from "mthds";
|
|
172
179
|
|
|
173
180
|
const client = new MthdsApiClient({
|
|
174
|
-
|
|
181
|
+
baseUrl: "https://api.pipelex.com",
|
|
175
182
|
apiToken: "your-api-key",
|
|
176
183
|
});
|
|
177
184
|
|
|
178
|
-
const result = await client.
|
|
185
|
+
const result = await client.execute({
|
|
179
186
|
pipe_code: "my-pipeline",
|
|
180
187
|
inputs: {
|
|
181
188
|
topic: "quantum computing",
|
|
@@ -185,50 +192,65 @@ const result = await client.executePipeline({
|
|
|
185
192
|
console.log(result.pipe_output);
|
|
186
193
|
```
|
|
187
194
|
|
|
195
|
+
The base URL is the host only — every endpoint composes as `{baseUrl}/v1/{endpoint}` (e.g. `https://api.pipelex.com/v1/execute`); `/health` resolves to the origin root.
|
|
196
|
+
|
|
188
197
|
### Self-hosted API
|
|
189
198
|
|
|
190
|
-
Point the client
|
|
199
|
+
Point the client at your own [pipelex-api](https://github.com/Pipelex/pipelex-api) instance — the same `MTHDSProtocol` surface, same paths:
|
|
191
200
|
|
|
192
201
|
```typescript
|
|
193
202
|
const client = new MthdsApiClient({
|
|
194
|
-
|
|
203
|
+
baseUrl: "http://localhost:8081",
|
|
195
204
|
apiToken: "your-api-key",
|
|
196
205
|
});
|
|
197
206
|
```
|
|
198
207
|
|
|
208
|
+
The bare open-source runner has no run store, so the durable run-lifecycle methods (`getRunStatus`/`getRunResult`/`waitForResult`) throw a clear `RunLifecycleUnavailableError` against it — use `execute` (blocking) or `start` instead (completion delivery is implementation-defined — see your runner's API documentation). The `GET /v1/version` handshake tells the SDK which deployment it is talking to.
|
|
209
|
+
|
|
210
|
+
> Note: the bare-runner blocking path returns the runner's native `pipe_output`, whereas the hosted durable path returns `main_stuff` + `graph_spec`. Cross-shape normalization is a v1 TODO.
|
|
211
|
+
|
|
199
212
|
### Environment variables
|
|
200
213
|
|
|
201
214
|
Instead of passing options to the constructor, you can set environment variables:
|
|
202
215
|
|
|
203
216
|
| Variable | Description |
|
|
204
217
|
|----------|-------------|
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
218
|
+
| `MTHDS_API_URL` | API base URL — host only, no version prefix (default `https://api.pipelex.com`) |
|
|
219
|
+
| `MTHDS_API_KEY` | API authentication token |
|
|
207
220
|
|
|
208
221
|
```typescript
|
|
209
|
-
// Reads
|
|
222
|
+
// Reads MTHDS_API_URL and MTHDS_API_KEY from the environment
|
|
210
223
|
const client = new MthdsApiClient();
|
|
211
224
|
```
|
|
212
225
|
|
|
213
226
|
### Methods
|
|
214
227
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
|
218
|
-
|
|
228
|
+
The client implements the MTHDS Protocol plus the hosted run-lifecycle extension:
|
|
229
|
+
|
|
230
|
+
| Method | Route | Description |
|
|
231
|
+
|--------|-------|-------------|
|
|
232
|
+
| `execute(options)` | `POST /v1/execute` | Execute a method and wait for the result — returns a `RunResultExecute` (throws `RunStillRunningError` on the protocol's optional 202 degrade) |
|
|
233
|
+
| `start(options)` | `POST /v1/start` | Start a method asynchronously — returns a `RunResultStart` with the authoritative `pipeline_run_id` |
|
|
234
|
+
| `validate(contents, allowSignatures?)` | `POST /v1/validate` | Parse, validate, and dry-run a bundle |
|
|
235
|
+
| `models(category?)` | `GET /v1/models` | The model deck the runner routes to |
|
|
236
|
+
| `version()` | `GET /v1/version` | Protocol + implementation versions (the feature-detection handshake) |
|
|
237
|
+
| `getRunStatus(runId)` | `GET /v1/runs/{id}/status` | Hosted extension — self-healing status read |
|
|
238
|
+
| `getRunResult(runId)` | `GET /v1/runs/{id}/results` | Hosted extension — single-shot result lookup |
|
|
239
|
+
| `waitForResult(runId, options?)` | — | Hosted extension — poll to a terminal state |
|
|
219
240
|
|
|
220
|
-
###
|
|
241
|
+
### Run options
|
|
221
242
|
|
|
222
243
|
| Option | Type | Description |
|
|
223
244
|
|--------|------|-------------|
|
|
224
|
-
| `pipe_code` | `string` |
|
|
225
|
-
| `
|
|
226
|
-
| `inputs` | `Record<string, string \| string[] \| object>` |
|
|
245
|
+
| `pipe_code` | `string` | Pipe code to execute |
|
|
246
|
+
| `mthds_contents` | `string[]` | Raw bundle contents (alternative to `pipe_code`) |
|
|
247
|
+
| `inputs` | `Record<string, string \| string[] \| object>` | Method input variables |
|
|
227
248
|
| `output_name` | `string` | Name of the output to return |
|
|
228
249
|
| `output_multiplicity` | `boolean \| number` | Expected output multiplicity |
|
|
229
|
-
| `
|
|
250
|
+
| `dynamic_output_concept_ref` | `string` | Dynamic output concept reference |
|
|
251
|
+
| `extra` | `Record<string, unknown>` | Server-specific extension args, forwarded verbatim into the request body (e.g. a stored-method id) |
|
|
230
252
|
|
|
231
|
-
Either `pipe_code` or `
|
|
253
|
+
Either `pipe_code` or `mthds_contents` must be provided (or a server-specific extension arg via `extra`). Anything beyond the protocol's basic args is server-specific and rides the generic `extra` option, merged into the request body — the server you call defines and handles its own extension args (a client-supplied run id, where a server supports one, is such an extension; the request side never names it). `startAndWaitForResult()` runs the whole start + poll lifecycle in one call.
|
|
232
254
|
|
|
233
255
|
## Telemetry
|
|
234
256
|
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { agentError, agentSuccess, AGENT_ERROR_DOMAINS } from "../output.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
10
|
+
import { MODEL_CATEGORIES } from "../../protocol/models.js";
|
|
11
|
+
import { ApiResponseError, RunFailedError, RunTimeoutError } from "../../runners/api/exceptions.js";
|
|
13
12
|
/**
|
|
14
13
|
* Register all API-runner commands on the program.
|
|
15
14
|
* Only called when --runner=api.
|
|
@@ -114,7 +113,7 @@ export function registerApiRunnerCommands(program, makeRunner) {
|
|
|
114
113
|
validateGroup
|
|
115
114
|
.command("bundle")
|
|
116
115
|
.argument("[target]", "Bundle file (.mthds) or directory")
|
|
117
|
-
.option("--
|
|
116
|
+
.option("--allow-signatures", "Tolerate unimplemented pipe signatures")
|
|
118
117
|
.option("--content <mthds>", "Bundle content as a string")
|
|
119
118
|
.description("Validate a bundle file or content")
|
|
120
119
|
.allowUnknownOption()
|
|
@@ -123,62 +122,24 @@ export function registerApiRunnerCommands(program, makeRunner) {
|
|
|
123
122
|
.action(async (target, options) => {
|
|
124
123
|
const runner = safeCreateRunner(makeRunner);
|
|
125
124
|
const mthdsContent = resolveContent(target, options.content);
|
|
126
|
-
|
|
127
|
-
const result = await runner.validate({
|
|
128
|
-
mthds_contents: [mthdsContent],
|
|
129
|
-
pipe_code: options.pipe,
|
|
130
|
-
});
|
|
131
|
-
if (result.success) {
|
|
132
|
-
agentSuccess({ ...result });
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
agentError(result.message, "ValidationError", {
|
|
136
|
-
error_domain: AGENT_ERROR_DOMAINS.VALIDATION,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
catch (err) {
|
|
141
|
-
agentError(err.message, "RunnerError", {
|
|
142
|
-
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
125
|
+
await runProtocolValidate(runner, [mthdsContent], options.allowSignatures ?? false);
|
|
145
126
|
});
|
|
146
127
|
validateGroup
|
|
147
128
|
.command("pipe")
|
|
148
|
-
.argument("<target>", "
|
|
149
|
-
.option("--
|
|
150
|
-
.description("Validate a
|
|
129
|
+
.argument("<target>", ".mthds bundle file")
|
|
130
|
+
.option("--allow-signatures", "Tolerate unimplemented pipe signatures")
|
|
131
|
+
.description("Validate a bundle file (protocol validate covers every pipe in it)")
|
|
151
132
|
.allowUnknownOption()
|
|
152
133
|
.allowExcessArguments(true)
|
|
153
134
|
.exitOverride()
|
|
154
135
|
.action(async (target, options) => {
|
|
155
136
|
const runner = safeCreateRunner(makeRunner);
|
|
156
|
-
if (target.endsWith(".mthds")) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const result = await runner.validate({
|
|
160
|
-
mthds_contents: [mthdsContent],
|
|
161
|
-
pipe_code: options.pipe,
|
|
162
|
-
});
|
|
163
|
-
handleValidateResult(result);
|
|
164
|
-
}
|
|
165
|
-
catch (err) {
|
|
166
|
-
agentError(err.message, "RunnerError", {
|
|
167
|
-
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
try {
|
|
173
|
-
const result = await runner.validate({ pipe_code: target });
|
|
174
|
-
handleValidateResult(result);
|
|
175
|
-
}
|
|
176
|
-
catch (err) {
|
|
177
|
-
agentError(err.message, "RunnerError", {
|
|
178
|
-
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
179
|
-
});
|
|
180
|
-
}
|
|
137
|
+
if (!target.endsWith(".mthds")) {
|
|
138
|
+
agentError("Validating a bare pipe code is not supported via the API runner — the protocol validate takes bundle contents. Pass a .mthds file.", "ArgumentError", { error_domain: AGENT_ERROR_DOMAINS.ARGUMENT });
|
|
139
|
+
return;
|
|
181
140
|
}
|
|
141
|
+
const mthdsContent = readFileOrError(target);
|
|
142
|
+
await runProtocolValidate(runner, [mthdsContent], options.allowSignatures ?? false);
|
|
182
143
|
});
|
|
183
144
|
validateGroup
|
|
184
145
|
.command("method")
|
|
@@ -188,20 +149,8 @@ export function registerApiRunnerCommands(program, makeRunner) {
|
|
|
188
149
|
.allowUnknownOption()
|
|
189
150
|
.allowExcessArguments(true)
|
|
190
151
|
.exitOverride()
|
|
191
|
-
.action(async (
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
const result = await runner.validate({
|
|
195
|
-
method_url: target,
|
|
196
|
-
pipe_code: options.pipe,
|
|
197
|
-
});
|
|
198
|
-
handleValidateResult(result);
|
|
199
|
-
}
|
|
200
|
-
catch (err) {
|
|
201
|
-
agentError(err.message, "RunnerError", {
|
|
202
|
-
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
152
|
+
.action(async () => {
|
|
153
|
+
agentError("'validate method' is not supported via the API runner — the protocol validate takes bundle contents, not a method URL. Pass a .mthds file to 'validate bundle', or use --runner pipelex.", "UnsupportedError", { error_domain: AGENT_ERROR_DOMAINS.RUNNER });
|
|
205
154
|
});
|
|
206
155
|
// ── inputs ──
|
|
207
156
|
const inputsGroup = program
|
|
@@ -310,12 +259,17 @@ export function registerApiRunnerCommands(program, makeRunner) {
|
|
|
310
259
|
inputs = parseJsonOrError(raw, "inputs file");
|
|
311
260
|
}
|
|
312
261
|
try {
|
|
313
|
-
const result = await runner.
|
|
262
|
+
const result = await runner.startAndWaitForResult({
|
|
314
263
|
mthds_contents: [mthdsContent],
|
|
315
264
|
pipe_code: pipeCode,
|
|
316
265
|
inputs,
|
|
317
266
|
});
|
|
318
|
-
agentSuccess({
|
|
267
|
+
agentSuccess({
|
|
268
|
+
state: "completed",
|
|
269
|
+
pipeline_run_id: result.pipeline_run_id,
|
|
270
|
+
main_stuff: result.main_stuff ?? result.pipe_output ?? null,
|
|
271
|
+
graph_spec: result.graph_spec ?? null,
|
|
272
|
+
});
|
|
319
273
|
}
|
|
320
274
|
catch (err) {
|
|
321
275
|
agentError(err.message, "RunnerError", {
|
|
@@ -349,20 +303,31 @@ export function registerApiRunnerCommands(program, makeRunner) {
|
|
|
349
303
|
.allowExcessArguments(true)
|
|
350
304
|
.exitOverride()
|
|
351
305
|
.action(runAction);
|
|
352
|
-
// ──
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
306
|
+
// ── run start ──
|
|
307
|
+
// Submit a run and return its id immediately. All run state lives behind the
|
|
308
|
+
// returned `pipeline_run_id` (DB + Temporal), so an agent can submit here,
|
|
309
|
+
// disconnect, and later resume with `run status` / `run result` / `run poll`.
|
|
310
|
+
runGroup
|
|
311
|
+
.command("start")
|
|
312
|
+
.argument("[target]", "Bundle file (.mthds) or directory")
|
|
313
|
+
.option("--pipe <code>", "Pipe code to run")
|
|
314
|
+
.option("-i, --inputs <file>", "Path to JSON inputs file")
|
|
315
|
+
.option("--content <mthds>", "Bundle content as a string")
|
|
316
|
+
.option("--inputs-json <json>", "Inputs as a JSON string")
|
|
317
|
+
.option("--extra <json>", "Server-specific extension args as a JSON object (e.g. a stored-method run) — forwarded to the runner verbatim")
|
|
318
|
+
.option("--output-name <name>", "Name of the output slot to write to")
|
|
319
|
+
.option("--output-multiplicity <value>", "Output multiplicity: 'false', 'true', or an exact count")
|
|
320
|
+
.option("--dynamic-output <concept_ref>", "Override for the dynamic output concept ref")
|
|
321
|
+
.description("Start a run and return its id without waiting")
|
|
357
322
|
.allowUnknownOption()
|
|
358
323
|
.allowExcessArguments(true)
|
|
359
324
|
.exitOverride()
|
|
360
|
-
.action(async (options) => {
|
|
325
|
+
.action(async (target, options) => {
|
|
361
326
|
const runner = safeCreateRunner(makeRunner);
|
|
327
|
+
const startOptions = resolveStartOptions(target, options);
|
|
362
328
|
try {
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
agentSuccess({ ...result });
|
|
329
|
+
const ack = await runner.start(startOptions);
|
|
330
|
+
agentSuccess({ ...ack });
|
|
366
331
|
}
|
|
367
332
|
catch (err) {
|
|
368
333
|
agentError(err.message, "RunnerError", {
|
|
@@ -370,24 +335,151 @@ export function registerApiRunnerCommands(program, makeRunner) {
|
|
|
370
335
|
});
|
|
371
336
|
}
|
|
372
337
|
});
|
|
373
|
-
// ──
|
|
374
|
-
|
|
375
|
-
.command("
|
|
376
|
-
.
|
|
377
|
-
.
|
|
378
|
-
.
|
|
379
|
-
.
|
|
338
|
+
// ── run status ──
|
|
339
|
+
runGroup
|
|
340
|
+
.command("status")
|
|
341
|
+
.argument("<pipeline_run_id>", "Run id")
|
|
342
|
+
.description("Fetch a run's current status by id (self-healing)")
|
|
343
|
+
.allowUnknownOption()
|
|
344
|
+
.allowExcessArguments(true)
|
|
345
|
+
.exitOverride()
|
|
346
|
+
.action(async (runId) => {
|
|
347
|
+
const runner = safeCreateRunner(makeRunner);
|
|
348
|
+
try {
|
|
349
|
+
const run = await runner.getRunStatus(runId);
|
|
350
|
+
agentSuccess({ ...run });
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
agentError(err.message, "RunnerError", {
|
|
354
|
+
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
// ── run result ──
|
|
359
|
+
// Single-shot result lookup: 202 → still running, 200 → result, 409 → failed.
|
|
360
|
+
runGroup
|
|
361
|
+
.command("result")
|
|
362
|
+
.argument("<pipeline_run_id>", "Run id")
|
|
363
|
+
.description("Fetch a run's result by id, once (does not wait)")
|
|
380
364
|
.allowUnknownOption()
|
|
381
365
|
.allowExcessArguments(true)
|
|
382
366
|
.exitOverride()
|
|
383
|
-
.action(async (
|
|
384
|
-
|
|
385
|
-
|
|
367
|
+
.action(async (runId) => {
|
|
368
|
+
const runner = safeCreateRunner(makeRunner);
|
|
369
|
+
let state;
|
|
370
|
+
try {
|
|
371
|
+
state = await runner.getRunResult(runId);
|
|
372
|
+
}
|
|
373
|
+
catch (err) {
|
|
374
|
+
agentError(err.message, "RunnerError", {
|
|
375
|
+
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
376
|
+
});
|
|
386
377
|
return;
|
|
387
378
|
}
|
|
379
|
+
switch (state.state) {
|
|
380
|
+
case "running":
|
|
381
|
+
agentSuccess({
|
|
382
|
+
state: "running",
|
|
383
|
+
pipeline_run_id: state.pipeline_run_id,
|
|
384
|
+
retry_after_seconds: state.retry_after_seconds,
|
|
385
|
+
hint: `Run is still in progress. Poll with: mthds-agent run poll ${runId}`,
|
|
386
|
+
});
|
|
387
|
+
break;
|
|
388
|
+
case "completed":
|
|
389
|
+
agentSuccess({
|
|
390
|
+
state: "completed",
|
|
391
|
+
pipeline_run_id: state.pipeline_run_id,
|
|
392
|
+
main_stuff: state.result.main_stuff ?? null,
|
|
393
|
+
graph_spec: state.result.graph_spec ?? null,
|
|
394
|
+
});
|
|
395
|
+
break;
|
|
396
|
+
case "failed":
|
|
397
|
+
agentError(state.message, "RunFailedError", {
|
|
398
|
+
error_domain: AGENT_ERROR_DOMAINS.PIPELINE,
|
|
399
|
+
retryable: false,
|
|
400
|
+
});
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
// ── run poll ──
|
|
405
|
+
// Block until the run reaches a terminal state. Ctrl-C (or any SIGINT) stops
|
|
406
|
+
// waiting WITHOUT cancelling the run — the run keeps executing server-side
|
|
407
|
+
// and can be resumed by id.
|
|
408
|
+
runGroup
|
|
409
|
+
.command("poll")
|
|
410
|
+
.argument("<pipeline_run_id>", "Run id")
|
|
411
|
+
.option("--interval <seconds>", "Base poll interval in seconds (default 2)")
|
|
412
|
+
.option("--timeout <seconds>", "Max seconds to wait before giving up (default 1200)")
|
|
413
|
+
.description("Poll a run to completion, then return its result")
|
|
414
|
+
.allowUnknownOption()
|
|
415
|
+
.allowExcessArguments(true)
|
|
416
|
+
.exitOverride()
|
|
417
|
+
.action(async (runId, options) => {
|
|
418
|
+
const runner = safeCreateRunner(makeRunner);
|
|
419
|
+
const intervalMs = parsePositiveSeconds(options.interval, "--interval");
|
|
420
|
+
const timeoutMs = parsePositiveSeconds(options.timeout, "--timeout");
|
|
421
|
+
const controller = new AbortController();
|
|
422
|
+
const onSigint = () => controller.abort();
|
|
423
|
+
process.once("SIGINT", onSigint);
|
|
424
|
+
try {
|
|
425
|
+
const result = await runner.waitForResult(runId, {
|
|
426
|
+
intervalMs,
|
|
427
|
+
timeoutMs,
|
|
428
|
+
signal: controller.signal,
|
|
429
|
+
});
|
|
430
|
+
agentSuccess({
|
|
431
|
+
state: "completed",
|
|
432
|
+
pipeline_run_id: runId,
|
|
433
|
+
main_stuff: result.main_stuff ?? null,
|
|
434
|
+
graph_spec: result.graph_spec ?? null,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
if (controller.signal.aborted) {
|
|
439
|
+
// Walk-away: not an error. Report the run as still resumable by id.
|
|
440
|
+
agentSuccess({
|
|
441
|
+
state: "running",
|
|
442
|
+
pipeline_run_id: runId,
|
|
443
|
+
resumable: true,
|
|
444
|
+
hint: `Stopped waiting; the run continues. Resume with: mthds-agent run poll ${runId}`,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
else if (err instanceof RunFailedError) {
|
|
448
|
+
agentError(err.message, "RunFailedError", {
|
|
449
|
+
error_domain: AGENT_ERROR_DOMAINS.PIPELINE,
|
|
450
|
+
retryable: false,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
else if (err instanceof RunTimeoutError) {
|
|
454
|
+
agentError(err.message, "RunTimeoutError", {
|
|
455
|
+
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
456
|
+
retryable: true,
|
|
457
|
+
hint: `The run is still executing. Resume with: mthds-agent run poll ${runId}`,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
agentError(err.message, "RunnerError", {
|
|
462
|
+
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
finally {
|
|
467
|
+
process.removeListener("SIGINT", onSigint);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
// ── models ──
|
|
471
|
+
program
|
|
472
|
+
.command("models")
|
|
473
|
+
.description("List the model deck (models, aliases, waterfalls)")
|
|
474
|
+
.option("--type <type>", "Filter by model category (llm, extract, img_gen, search)")
|
|
475
|
+
.allowUnknownOption()
|
|
476
|
+
.allowExcessArguments(true)
|
|
477
|
+
.exitOverride()
|
|
478
|
+
.action(async (options) => {
|
|
388
479
|
const runner = safeCreateRunner(makeRunner);
|
|
480
|
+
const category = parseModelCategory(options.type);
|
|
389
481
|
try {
|
|
390
|
-
const result = await runner.
|
|
482
|
+
const result = await runner.models(category);
|
|
391
483
|
agentSuccess({ ...result });
|
|
392
484
|
}
|
|
393
485
|
catch (err) {
|
|
@@ -396,6 +488,21 @@ export function registerApiRunnerCommands(program, makeRunner) {
|
|
|
396
488
|
});
|
|
397
489
|
}
|
|
398
490
|
});
|
|
491
|
+
// ── check-model ──
|
|
492
|
+
// check-model is a LOCAL CLI capability (pipelex runner) only — the MTHDS
|
|
493
|
+
// API has no check-model route. Registered here so the API runner errors
|
|
494
|
+
// cleanly instead of failing with an opaque 404.
|
|
495
|
+
program
|
|
496
|
+
.command("check-model")
|
|
497
|
+
.description("Validate a model reference with fuzzy suggestions (pipelex runner only)")
|
|
498
|
+
.argument("<reference>", "Model reference to check")
|
|
499
|
+
.option("--type <type>", "Model category (llm, extract, img_gen, search)")
|
|
500
|
+
.allowUnknownOption()
|
|
501
|
+
.allowExcessArguments(true)
|
|
502
|
+
.exitOverride()
|
|
503
|
+
.action(async () => {
|
|
504
|
+
agentError("check-model is not available on the API runner — the MTHDS API has no check-model route. It is a local capability of the pipelex runner: re-run with --runner pipelex. To list what the API can route to, use: mthds-agent models", "UnsupportedError", { error_domain: AGENT_ERROR_DOMAINS.RUNNER });
|
|
505
|
+
});
|
|
399
506
|
}
|
|
400
507
|
// ── Helpers ──
|
|
401
508
|
function safeCreateRunner(makeRunner) {
|
|
@@ -485,13 +592,103 @@ function resolvePipeCode(mthdsContent, pipeCodeOption) {
|
|
|
485
592
|
});
|
|
486
593
|
throw new Error("unreachable");
|
|
487
594
|
}
|
|
488
|
-
function
|
|
489
|
-
if (
|
|
490
|
-
|
|
595
|
+
function resolveRunInputs(options) {
|
|
596
|
+
if (options.inputsJson)
|
|
597
|
+
return parseJsonOrError(options.inputsJson, "--inputs-json");
|
|
598
|
+
if (options.inputs) {
|
|
599
|
+
const raw = readFileOrError(options.inputs);
|
|
600
|
+
return parseJsonOrError(raw, "inputs file");
|
|
601
|
+
}
|
|
602
|
+
return undefined;
|
|
603
|
+
}
|
|
604
|
+
function resolveStartOptions(target, options) {
|
|
605
|
+
const outputs = {
|
|
606
|
+
output_name: options.outputName,
|
|
607
|
+
output_multiplicity: parseMultiplicity(options.outputMultiplicity),
|
|
608
|
+
dynamic_output_concept_ref: options.dynamicOutput,
|
|
609
|
+
};
|
|
610
|
+
const extra = parseExtraOption(options.extra);
|
|
611
|
+
// No inline bundle → an extension-only start: the run is identified entirely by
|
|
612
|
+
// server-specific args passed through `--extra` (e.g. a stored-method run). The
|
|
613
|
+
// runner is the source of truth for what `extra` it accepts — the SDK and CLI
|
|
614
|
+
// never name those args.
|
|
615
|
+
if (!target && !options.content) {
|
|
616
|
+
return { pipe_code: options.pipe, inputs: resolveRunInputs(options), ...outputs, extra };
|
|
617
|
+
}
|
|
618
|
+
// resolveContentForRun may set options.inputs (directory auto-discovery), so
|
|
619
|
+
// resolve the bundle before reading inputs.
|
|
620
|
+
const mthdsContent = resolveContentForRun(target, options);
|
|
621
|
+
const pipeCode = resolvePipeCode(mthdsContent, options.pipe);
|
|
622
|
+
return { pipe_code: pipeCode, mthds_contents: [mthdsContent], inputs: resolveRunInputs(options), ...outputs, extra };
|
|
623
|
+
}
|
|
624
|
+
/** Parse `--extra <json>` into the generic extension passthrough — a JSON object of server-defined args. */
|
|
625
|
+
function parseExtraOption(raw) {
|
|
626
|
+
if (!raw)
|
|
627
|
+
return undefined;
|
|
628
|
+
const parsed = parseJsonOrError(raw, "--extra");
|
|
629
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
630
|
+
agentError("--extra must be a JSON object of server-specific args.", "ArgumentError", {
|
|
631
|
+
error_domain: AGENT_ERROR_DOMAINS.ARGUMENT,
|
|
632
|
+
});
|
|
633
|
+
throw new Error("unreachable");
|
|
634
|
+
}
|
|
635
|
+
return parsed;
|
|
636
|
+
}
|
|
637
|
+
/** Parse `--output-multiplicity`: "false"/"true" → boolean, a positive integer → count. */
|
|
638
|
+
function parseMultiplicity(raw) {
|
|
639
|
+
if (raw === undefined)
|
|
640
|
+
return undefined;
|
|
641
|
+
if (raw === "true")
|
|
642
|
+
return true;
|
|
643
|
+
if (raw === "false")
|
|
644
|
+
return false;
|
|
645
|
+
const count = Number(raw);
|
|
646
|
+
if (Number.isInteger(count) && count > 0)
|
|
647
|
+
return count;
|
|
648
|
+
agentError("--output-multiplicity must be 'true', 'false', or a positive integer.", "ArgumentError", { error_domain: AGENT_ERROR_DOMAINS.ARGUMENT });
|
|
649
|
+
throw new Error("unreachable");
|
|
650
|
+
}
|
|
651
|
+
function parsePositiveSeconds(raw, label) {
|
|
652
|
+
if (raw === undefined)
|
|
653
|
+
return undefined;
|
|
654
|
+
const seconds = Number(raw);
|
|
655
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
656
|
+
agentError(`${label} must be a positive number of seconds.`, "ArgumentError", {
|
|
657
|
+
error_domain: AGENT_ERROR_DOMAINS.ARGUMENT,
|
|
658
|
+
});
|
|
659
|
+
throw new Error("unreachable");
|
|
660
|
+
}
|
|
661
|
+
return seconds * 1000;
|
|
662
|
+
}
|
|
663
|
+
/** Parse the `--type` model-category filter, erroring on unknown values. */
|
|
664
|
+
function parseModelCategory(raw) {
|
|
665
|
+
if (raw === undefined)
|
|
666
|
+
return undefined;
|
|
667
|
+
if (MODEL_CATEGORIES.includes(raw)) {
|
|
668
|
+
return raw;
|
|
669
|
+
}
|
|
670
|
+
agentError(`--type must be one of: ${MODEL_CATEGORIES.join(", ")}.`, "ArgumentError", { error_domain: AGENT_ERROR_DOMAINS.ARGUMENT });
|
|
671
|
+
throw new Error("unreachable");
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Run the protocol validate (`POST /v1/validate`) and emit the agent envelope.
|
|
675
|
+
* A valid bundle returns the structural artifacts; an invalid bundle is an
|
|
676
|
+
* HTTP 422 problem, surfaced as a ValidationError.
|
|
677
|
+
*/
|
|
678
|
+
async function runProtocolValidate(runner, mthdsContents, allowSignatures) {
|
|
679
|
+
try {
|
|
680
|
+
const report = await runner.validate(mthdsContents, allowSignatures);
|
|
681
|
+
agentSuccess({ success: true, ...report });
|
|
491
682
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
683
|
+
catch (err) {
|
|
684
|
+
if (err instanceof ApiResponseError && err.status === 422) {
|
|
685
|
+
agentError(err.serverMessage ?? err.message, "ValidationError", {
|
|
686
|
+
error_domain: AGENT_ERROR_DOMAINS.VALIDATION,
|
|
687
|
+
});
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
agentError(err.message, "RunnerError", {
|
|
691
|
+
error_domain: AGENT_ERROR_DOMAINS.RUNNER,
|
|
495
692
|
});
|
|
496
693
|
}
|
|
497
694
|
}
|