@aiaiai-pt/martha-cli 0.51.2 → 0.51.7
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/CHANGELOG.md +37 -0
- package/dist/index.js +298 -44
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,43 @@ All notable changes to `@aiaiai-pt/martha-cli`. Format: [Keep a Changelog](https
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.51.5] — 2026-07-20
|
|
8
|
+
|
|
9
|
+
### Fixed — host tools ran in a doubled working directory
|
|
10
|
+
|
|
11
|
+
- The runner concatenated its scoped `--cwd` with a per-call `cwd`
|
|
12
|
+
(`` `${state.cwd}/${req.cwd}` ``), so an ABSOLUTE request path — what an agent
|
|
13
|
+
sends naturally when its prompt names the repo — became
|
|
14
|
+
`/Users/me/repo//Users/me/repo` and every `host_exec` failed with ENOENT /
|
|
15
|
+
MODULE_NOT_FOUND. Now resolved properly (absolute used as-is, relative joined)
|
|
16
|
+
and still confined inside the scoped workspace.
|
|
17
|
+
|
|
18
|
+
## [0.51.4] — 2026-07-20
|
|
19
|
+
|
|
20
|
+
### Added — `MARTHA_BROWSER_HEADFUL` for WAF-protected pages
|
|
21
|
+
|
|
22
|
+
- `host_browser` launched Chromium headless unconditionally, so any site that
|
|
23
|
+
fingerprints headless Chrome (Cloudflare & co.) served it 403 — exactly the
|
|
24
|
+
pages the tool exists to reach. Set `MARTHA_BROWSER_HEADFUL=1` on the runner
|
|
25
|
+
to launch headful with a real Chrome UA and a 1440x900 viewport. Default is
|
|
26
|
+
unchanged (headless), so CI and display-less servers are unaffected.
|
|
27
|
+
- Verified against `piscapisca.pt/carros`: 403 headless, 200 headful with live
|
|
28
|
+
listing data, same machine and same tool.
|
|
29
|
+
|
|
30
|
+
## [0.51.3] — 2026-07-20
|
|
31
|
+
|
|
32
|
+
### Fixed — `host_browser` could never find playwright
|
|
33
|
+
|
|
34
|
+
- `host_browser` resolved `playwright` with a bare specifier, i.e. relative to
|
|
35
|
+
`martha-cli/dist/` — never the runner's `--cwd`. Its own error message told
|
|
36
|
+
users to `npm i -D playwright` (naturally read as "in my project"), which was
|
|
37
|
+
exactly where the CLI would not look, so the tool reported "not installed"
|
|
38
|
+
forever on fully-provisioned machines. Now resolved from `process.cwd()`
|
|
39
|
+
first, falling back to the CLI-local install; the failure message names both
|
|
40
|
+
locations searched.
|
|
41
|
+
- Accept playwright's CJS export shape (`.default`) when loaded by file URL —
|
|
42
|
+
otherwise resolution succeeded but `chromium` was `undefined`.
|
|
43
|
+
|
|
7
44
|
## [0.51.2] — 2026-07-19
|
|
8
45
|
|
|
9
46
|
### Fixed — selected-client local execution (#968)
|
package/dist/index.js
CHANGED
|
@@ -11042,6 +11042,7 @@ ${items.length} ${config.typeNamePlural}`));
|
|
|
11042
11042
|
if (config.normalizeBody)
|
|
11043
11043
|
body = config.normalizeBody(body);
|
|
11044
11044
|
const result = await ctx.api.put(`${config.apiPath}/${encodeURIComponent(name)}`, body);
|
|
11045
|
+
config.verifyUpdateResult?.(body, result);
|
|
11045
11046
|
if (isJson()) {
|
|
11046
11047
|
console.log(JSON.stringify(result, null, 2));
|
|
11047
11048
|
return;
|
|
@@ -11119,7 +11120,7 @@ init_config();
|
|
|
11119
11120
|
init_errors();
|
|
11120
11121
|
|
|
11121
11122
|
// src/version.ts
|
|
11122
|
-
var CLI_VERSION = "0.51.
|
|
11123
|
+
var CLI_VERSION = "0.51.7";
|
|
11123
11124
|
|
|
11124
11125
|
// src/commands/sessions.ts
|
|
11125
11126
|
function relativeTime(iso) {
|
|
@@ -12262,6 +12263,7 @@ function parseLocalAgentRequests(data) {
|
|
|
12262
12263
|
}
|
|
12263
12264
|
|
|
12264
12265
|
// src/lib/local-tool-runner.ts
|
|
12266
|
+
import path7 from "node:path";
|
|
12265
12267
|
import { createHash as createHash2 } from "node:crypto";
|
|
12266
12268
|
init_errors();
|
|
12267
12269
|
|
|
@@ -12686,23 +12688,54 @@ ${lines.join(`
|
|
|
12686
12688
|
return { error: true, message: `${req.action} failed: ${String(e)}` };
|
|
12687
12689
|
}
|
|
12688
12690
|
}
|
|
12691
|
+
var REAL_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36";
|
|
12689
12692
|
|
|
12690
12693
|
class BrowserSession {
|
|
12691
12694
|
browser = null;
|
|
12692
12695
|
page = null;
|
|
12693
12696
|
consoleBuffer = [];
|
|
12697
|
+
headful() {
|
|
12698
|
+
const v = (process.env.MARTHA_BROWSER_HEADFUL || "").toLowerCase();
|
|
12699
|
+
return v === "1" || v === "true" || v === "yes";
|
|
12700
|
+
}
|
|
12694
12701
|
async ensurePage() {
|
|
12695
12702
|
if (this.page)
|
|
12696
12703
|
return this.page;
|
|
12697
12704
|
const moduleName = "playwright";
|
|
12698
12705
|
let chromium;
|
|
12706
|
+
const candidates = [];
|
|
12699
12707
|
try {
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
12708
|
+
const { createRequire: createRequire2 } = await import("node:module");
|
|
12709
|
+
const { pathToFileURL } = await import("node:url");
|
|
12710
|
+
const path5 = await import("node:path");
|
|
12711
|
+
const req = createRequire2(pathToFileURL(path5.join(process.cwd(), "noop.js")));
|
|
12712
|
+
candidates.push(pathToFileURL(req.resolve(moduleName)).href);
|
|
12713
|
+
} catch {}
|
|
12714
|
+
candidates.push(moduleName);
|
|
12715
|
+
let lastErr;
|
|
12716
|
+
let loaded = null;
|
|
12717
|
+
for (const spec of candidates) {
|
|
12718
|
+
try {
|
|
12719
|
+
loaded = await import(spec);
|
|
12720
|
+
break;
|
|
12721
|
+
} catch (e) {
|
|
12722
|
+
lastErr = e;
|
|
12723
|
+
}
|
|
12724
|
+
}
|
|
12725
|
+
if (!loaded) {
|
|
12726
|
+
throw new Error(`playwright is not installed where the runner can load it (looked in ${process.cwd()} and alongside the martha CLI). Install it in the runner's --cwd: \`npm i -D playwright && npx playwright install chromium\`` + (lastErr instanceof Error ? ` — ${lastErr.message}` : ""));
|
|
12727
|
+
}
|
|
12728
|
+
const pw = loaded.chromium ? loaded : loaded.default;
|
|
12729
|
+
if (!pw?.chromium) {
|
|
12730
|
+
throw new Error(`playwright loaded but exposes no \`chromium\` export (from ${process.cwd()}); check the installed playwright version.`);
|
|
12703
12731
|
}
|
|
12704
|
-
|
|
12705
|
-
|
|
12732
|
+
chromium = pw.chromium;
|
|
12733
|
+
const headful = this.headful();
|
|
12734
|
+
this.browser = await chromium.launch({ headless: !headful });
|
|
12735
|
+
this.page = headful ? await (await this.browser.newContext({
|
|
12736
|
+
userAgent: REAL_UA,
|
|
12737
|
+
viewport: { width: 1440, height: 900 }
|
|
12738
|
+
})).newPage() : await this.browser.newPage();
|
|
12706
12739
|
this.page.on("console", (msg) => this.consoleBuffer.push({
|
|
12707
12740
|
kind: "console",
|
|
12708
12741
|
level: msg.type(),
|
|
@@ -13078,6 +13111,15 @@ function parseJobToolRequests(data, toolName) {
|
|
|
13078
13111
|
}
|
|
13079
13112
|
|
|
13080
13113
|
// src/lib/local-tool-runner.ts
|
|
13114
|
+
function resolveRequestCwd(stateCwd, reqCwd) {
|
|
13115
|
+
if (!reqCwd)
|
|
13116
|
+
return stateCwd;
|
|
13117
|
+
const base = path7.resolve(stateCwd);
|
|
13118
|
+
const target = path7.isAbsolute(reqCwd) ? path7.resolve(reqCwd) : path7.resolve(base, reqCwd);
|
|
13119
|
+
const rel = path7.relative(base, target);
|
|
13120
|
+
const escapes = rel.startsWith("..") || path7.isAbsolute(rel);
|
|
13121
|
+
return escapes ? base : target;
|
|
13122
|
+
}
|
|
13081
13123
|
function selectedClientOptions(clientId) {
|
|
13082
13124
|
return clientId ? { params: { selected_client: clientId } } : undefined;
|
|
13083
13125
|
}
|
|
@@ -13118,7 +13160,7 @@ async function resolveHostExec(sessionId, toolCallId, req, state) {
|
|
|
13118
13160
|
message: "Host execution declined; re-run with --yes to allow."
|
|
13119
13161
|
};
|
|
13120
13162
|
}
|
|
13121
|
-
const cwd =
|
|
13163
|
+
const cwd = resolveRequestCwd(state.cwd, req.cwd);
|
|
13122
13164
|
if (req.background) {
|
|
13123
13165
|
const jobId = mintJobId();
|
|
13124
13166
|
process.stderr.write(source_default.dim(`
|
|
@@ -13281,7 +13323,7 @@ async function resolveLocalAgent(sessionId, toolCallId, req, state) {
|
|
|
13281
13323
|
message: `A previous local_agent run (${decision.command}) did not complete; ` + `not re-running. Its log survives under .martha-runner/ — read it with host_read.`
|
|
13282
13324
|
};
|
|
13283
13325
|
}
|
|
13284
|
-
const cwd =
|
|
13326
|
+
const cwd = resolveRequestCwd(state.cwd, req.cwd);
|
|
13285
13327
|
const { bin, args, stdin } = buildSpawn(invocation, req.task);
|
|
13286
13328
|
if (req.background) {
|
|
13287
13329
|
const jobId = mintJobId();
|
|
@@ -14512,9 +14554,195 @@ function registerConfigCommand(program2) {
|
|
|
14512
14554
|
|
|
14513
14555
|
// src/commands/definitions-apply.ts
|
|
14514
14556
|
import fs8 from "node:fs";
|
|
14515
|
-
import
|
|
14557
|
+
import path8 from "node:path";
|
|
14516
14558
|
init_dist();
|
|
14517
14559
|
init_errors();
|
|
14560
|
+
|
|
14561
|
+
// src/commands/definition-payload.ts
|
|
14562
|
+
init_errors();
|
|
14563
|
+
var MUTABLE_FIELDS = {
|
|
14564
|
+
Function: new Set([
|
|
14565
|
+
"tool_name",
|
|
14566
|
+
"description",
|
|
14567
|
+
"endpoint",
|
|
14568
|
+
"http_method",
|
|
14569
|
+
"handler",
|
|
14570
|
+
"category",
|
|
14571
|
+
"parameters",
|
|
14572
|
+
"request_body",
|
|
14573
|
+
"auth",
|
|
14574
|
+
"extra_headers",
|
|
14575
|
+
"tags",
|
|
14576
|
+
"is_active",
|
|
14577
|
+
"collection_id_param",
|
|
14578
|
+
"risk_tags",
|
|
14579
|
+
"risk_level"
|
|
14580
|
+
]),
|
|
14581
|
+
Workflow: new Set([
|
|
14582
|
+
"description",
|
|
14583
|
+
"format_version",
|
|
14584
|
+
"nodes",
|
|
14585
|
+
"edges",
|
|
14586
|
+
"variables",
|
|
14587
|
+
"input_schema",
|
|
14588
|
+
"on_error",
|
|
14589
|
+
"timeout_seconds",
|
|
14590
|
+
"ui_metadata",
|
|
14591
|
+
"is_active"
|
|
14592
|
+
])
|
|
14593
|
+
};
|
|
14594
|
+
function isPlainObject(value) {
|
|
14595
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
14596
|
+
}
|
|
14597
|
+
function valuesEqual(left, right) {
|
|
14598
|
+
if (left === right)
|
|
14599
|
+
return true;
|
|
14600
|
+
if (left === null || right === null)
|
|
14601
|
+
return false;
|
|
14602
|
+
if (left === undefined || right === undefined)
|
|
14603
|
+
return false;
|
|
14604
|
+
if (typeof left !== typeof right)
|
|
14605
|
+
return false;
|
|
14606
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
14607
|
+
if (!Array.isArray(left) || !Array.isArray(right))
|
|
14608
|
+
return false;
|
|
14609
|
+
return left.length === right.length && left.every((value, index) => valuesEqual(value, right[index]));
|
|
14610
|
+
}
|
|
14611
|
+
if (isPlainObject(left) && isPlainObject(right)) {
|
|
14612
|
+
const leftKeys = Object.keys(left);
|
|
14613
|
+
const rightKeys = Object.keys(right);
|
|
14614
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key) => (key in right) && valuesEqual(left[key], right[key]));
|
|
14615
|
+
}
|
|
14616
|
+
return false;
|
|
14617
|
+
}
|
|
14618
|
+
function requestedValueMatches(requested, observed) {
|
|
14619
|
+
if (requested === observed)
|
|
14620
|
+
return true;
|
|
14621
|
+
if (requested === null || requested === undefined) {
|
|
14622
|
+
return observed === null || observed === undefined;
|
|
14623
|
+
}
|
|
14624
|
+
if (observed === null || observed === undefined)
|
|
14625
|
+
return false;
|
|
14626
|
+
if (typeof requested !== typeof observed)
|
|
14627
|
+
return false;
|
|
14628
|
+
if (Array.isArray(requested)) {
|
|
14629
|
+
if (!Array.isArray(observed) || requested.length !== observed.length) {
|
|
14630
|
+
return false;
|
|
14631
|
+
}
|
|
14632
|
+
return requested.every((value, index) => requestedValueMatches(value, observed[index]));
|
|
14633
|
+
}
|
|
14634
|
+
if (isPlainObject(requested)) {
|
|
14635
|
+
if (!isPlainObject(observed))
|
|
14636
|
+
return false;
|
|
14637
|
+
const requestedKeys = Object.keys(requested);
|
|
14638
|
+
if (requestedKeys.length === 0) {
|
|
14639
|
+
return Object.keys(observed).length === 0;
|
|
14640
|
+
}
|
|
14641
|
+
return requestedKeys.every((key) => requestedValueMatches(requested[key], observed[key]));
|
|
14642
|
+
}
|
|
14643
|
+
return false;
|
|
14644
|
+
}
|
|
14645
|
+
function normalizeDefinitionWriteBody(kind, body) {
|
|
14646
|
+
const normalized = { ...body };
|
|
14647
|
+
if (!("definition" in normalized))
|
|
14648
|
+
return normalized;
|
|
14649
|
+
const nested = normalized.definition;
|
|
14650
|
+
if (!isPlainObject(nested)) {
|
|
14651
|
+
throw new CLIError("'definition' must be an object", 4 /* Validation */);
|
|
14652
|
+
}
|
|
14653
|
+
if ("definition" in nested) {
|
|
14654
|
+
throw new CLIError("Only one 'definition' wrapper is supported", 4 /* Validation */);
|
|
14655
|
+
}
|
|
14656
|
+
delete normalized.definition;
|
|
14657
|
+
for (const [key, nestedValue] of Object.entries(nested)) {
|
|
14658
|
+
if (key in normalized && !valuesEqual(normalized[key], nestedValue)) {
|
|
14659
|
+
throw new CLIError(`Conflicting values for '${key}' in flat and nested ${kind} fields`, 4 /* Validation */);
|
|
14660
|
+
}
|
|
14661
|
+
if (!(key in normalized))
|
|
14662
|
+
normalized[key] = nestedValue;
|
|
14663
|
+
}
|
|
14664
|
+
return normalized;
|
|
14665
|
+
}
|
|
14666
|
+
function canonicalizeWireValue(kind, field, value) {
|
|
14667
|
+
if (kind === "Function" && field === "http_method") {
|
|
14668
|
+
return typeof value === "string" ? value.toUpperCase() : value;
|
|
14669
|
+
}
|
|
14670
|
+
if (kind === "Function" && field === "request_body" && isPlainObject(value)) {
|
|
14671
|
+
const canonical = {
|
|
14672
|
+
content_type: "application/json",
|
|
14673
|
+
required: false,
|
|
14674
|
+
schema_def: null,
|
|
14675
|
+
...value
|
|
14676
|
+
};
|
|
14677
|
+
if ("schema" in canonical) {
|
|
14678
|
+
canonical.schema_def = canonical.schema;
|
|
14679
|
+
delete canonical.schema;
|
|
14680
|
+
}
|
|
14681
|
+
return canonical;
|
|
14682
|
+
}
|
|
14683
|
+
if (kind === "Function" && field === "auth" && isPlainObject(value)) {
|
|
14684
|
+
return {
|
|
14685
|
+
scheme: null,
|
|
14686
|
+
header: null,
|
|
14687
|
+
query_param: null,
|
|
14688
|
+
value: null,
|
|
14689
|
+
credential_source: null,
|
|
14690
|
+
...value
|
|
14691
|
+
};
|
|
14692
|
+
}
|
|
14693
|
+
if (kind === "Workflow" && field === "nodes" && Array.isArray(value)) {
|
|
14694
|
+
return value.map((node) => isPlainObject(node) ? { label: null, config: {}, position: null, ...node } : node);
|
|
14695
|
+
}
|
|
14696
|
+
if (kind === "Workflow" && field === "edges" && Array.isArray(value)) {
|
|
14697
|
+
return value.map((edge) => isPlainObject(edge) ? { source_handle: null, condition: null, label: null, ...edge } : edge);
|
|
14698
|
+
}
|
|
14699
|
+
return value;
|
|
14700
|
+
}
|
|
14701
|
+
function canonicalizeParameter(value) {
|
|
14702
|
+
if (!isPlainObject(value))
|
|
14703
|
+
return value;
|
|
14704
|
+
return {
|
|
14705
|
+
description: null,
|
|
14706
|
+
required: false,
|
|
14707
|
+
location: "query",
|
|
14708
|
+
enum: null,
|
|
14709
|
+
default: null,
|
|
14710
|
+
...value
|
|
14711
|
+
};
|
|
14712
|
+
}
|
|
14713
|
+
function parameterMapMatches(requested, observed) {
|
|
14714
|
+
if (!isPlainObject(requested) || !isPlainObject(observed)) {
|
|
14715
|
+
return requestedValueMatches(requested, observed);
|
|
14716
|
+
}
|
|
14717
|
+
const requestedNames = Object.keys(requested);
|
|
14718
|
+
const observedNames = Object.keys(observed);
|
|
14719
|
+
return requestedNames.length === observedNames.length && requestedNames.every((name) => (name in observed) && valuesEqual(canonicalizeParameter(requested[name]), canonicalizeParameter(observed[name])));
|
|
14720
|
+
}
|
|
14721
|
+
function fieldValueMatches(kind, field, requested, observed) {
|
|
14722
|
+
if (kind === "Function" && field === "parameters") {
|
|
14723
|
+
return parameterMapMatches(requested, observed);
|
|
14724
|
+
}
|
|
14725
|
+
const isExactTypedValue = kind === "Function" && (field === "request_body" || field === "auth") || kind === "Workflow" && (field === "nodes" || field === "edges");
|
|
14726
|
+
if (isExactTypedValue) {
|
|
14727
|
+
return valuesEqual(requested, observed);
|
|
14728
|
+
}
|
|
14729
|
+
const isExactReplacementMap = kind === "Function" && field === "extra_headers" || kind === "Workflow" && (field === "variables" || field === "input_schema" || field === "ui_metadata");
|
|
14730
|
+
if (isExactReplacementMap) {
|
|
14731
|
+
return valuesEqual(requested, observed);
|
|
14732
|
+
}
|
|
14733
|
+
return requestedValueMatches(requested, observed);
|
|
14734
|
+
}
|
|
14735
|
+
function verifyDefinitionUpdateResponse(kind, requestedBody, response) {
|
|
14736
|
+
const requested = normalizeDefinitionWriteBody(kind, requestedBody);
|
|
14737
|
+
const observed = normalizeDefinitionWriteBody(kind, response);
|
|
14738
|
+
const mutableFields = MUTABLE_FIELDS[kind];
|
|
14739
|
+
const mismatches = Object.keys(requested).filter((field) => mutableFields.has(field) && !fieldValueMatches(kind, field, canonicalizeWireValue(kind, field, requested[field]), canonicalizeWireValue(kind, field, observed[field])));
|
|
14740
|
+
if (mismatches.length > 0) {
|
|
14741
|
+
throw new CLIError(`Server response did not persist requested field(s): ${mismatches.join(", ")}`, 1 /* Error */);
|
|
14742
|
+
}
|
|
14743
|
+
}
|
|
14744
|
+
|
|
14745
|
+
// src/commands/definitions-apply.ts
|
|
14518
14746
|
var VALID_KINDS = new Set([
|
|
14519
14747
|
"Function",
|
|
14520
14748
|
"Workflow",
|
|
@@ -14549,12 +14777,11 @@ var SERVER_GENERATED_FIELDS = new Set([
|
|
|
14549
14777
|
"has_parallel",
|
|
14550
14778
|
"has_conditions",
|
|
14551
14779
|
"function_refs",
|
|
14552
|
-
"format_version",
|
|
14553
14780
|
"tenant_id"
|
|
14554
14781
|
]);
|
|
14555
14782
|
var AGENT_MANAGED_FIELDS = new Set(["functions", "workflows"]);
|
|
14556
14783
|
function loadDefinitions(inputPath) {
|
|
14557
|
-
const resolved =
|
|
14784
|
+
const resolved = path8.resolve(inputPath);
|
|
14558
14785
|
if (!fs8.existsSync(resolved)) {
|
|
14559
14786
|
throw new CLIError(`Path not found: ${inputPath}`, 1 /* Error */);
|
|
14560
14787
|
}
|
|
@@ -14567,7 +14794,7 @@ function loadDefinitions(inputPath) {
|
|
|
14567
14794
|
function loadDirectory(dirPath) {
|
|
14568
14795
|
const entries = fs8.readdirSync(dirPath).sort();
|
|
14569
14796
|
const validExts = new Set([".yaml", ".yml", ".json"]);
|
|
14570
|
-
const files = entries.filter((e) => validExts.has(
|
|
14797
|
+
const files = entries.filter((e) => validExts.has(path8.extname(e).toLowerCase())).map((e) => path8.join(dirPath, e));
|
|
14571
14798
|
if (files.length === 0) {
|
|
14572
14799
|
throw new CLIError(`No YAML or JSON files found in ${dirPath}`, 4 /* Validation */);
|
|
14573
14800
|
}
|
|
@@ -14579,7 +14806,7 @@ function loadDirectory(dirPath) {
|
|
|
14579
14806
|
}
|
|
14580
14807
|
function loadFile(filePath) {
|
|
14581
14808
|
const content = fs8.readFileSync(filePath, "utf-8");
|
|
14582
|
-
const ext =
|
|
14809
|
+
const ext = path8.extname(filePath).toLowerCase();
|
|
14583
14810
|
if (ext === ".json") {
|
|
14584
14811
|
const parsed = parseJsonFile(content, filePath);
|
|
14585
14812
|
return [toLocalDefinition(parsed, filePath)];
|
|
@@ -14639,7 +14866,9 @@ function toLocalDefinition(raw, source) {
|
|
|
14639
14866
|
continue;
|
|
14640
14867
|
body[k] = v;
|
|
14641
14868
|
}
|
|
14642
|
-
|
|
14869
|
+
const typedKind = kind;
|
|
14870
|
+
const normalizedBody = typedKind === "Function" || typedKind === "Workflow" ? normalizeDefinitionWriteBody(typedKind, body) : body;
|
|
14871
|
+
return { kind: typedKind, name, body: normalizedBody, source };
|
|
14643
14872
|
}
|
|
14644
14873
|
async function computeOperations(defs, api) {
|
|
14645
14874
|
const ops = [];
|
|
@@ -14814,7 +15043,7 @@ async function executeApply(api, ops, defs, warnings) {
|
|
|
14814
15043
|
}
|
|
14815
15044
|
const def = defsByKey.get(`${op.kind}:${op.name}`);
|
|
14816
15045
|
const apiPath = KIND_TO_API_PATH[op.kind];
|
|
14817
|
-
const body = { ...def.body };
|
|
15046
|
+
const body = op.kind === "Function" || op.kind === "Workflow" ? normalizeDefinitionWriteBody(op.kind, def.body) : { ...def.body };
|
|
14818
15047
|
if (op.kind === "Agent") {
|
|
14819
15048
|
for (const field of AGENT_MANAGED_FIELDS) {
|
|
14820
15049
|
if (field in body) {
|
|
@@ -14841,6 +15070,9 @@ async function executeApply(api, ops, defs, warnings) {
|
|
|
14841
15070
|
});
|
|
14842
15071
|
} else {
|
|
14843
15072
|
const res = await api.put(`${apiPath}/${encodeURIComponent(op.name)}`, body);
|
|
15073
|
+
if (op.kind === "Function" || op.kind === "Workflow") {
|
|
15074
|
+
verifyDefinitionUpdateResponse(op.kind, body, res);
|
|
15075
|
+
}
|
|
14844
15076
|
const portWarnings = extractPortWarnings(res);
|
|
14845
15077
|
results.push({
|
|
14846
15078
|
name: op.name,
|
|
@@ -15123,6 +15355,8 @@ var functionsConfig = {
|
|
|
15123
15355
|
typeName: "function",
|
|
15124
15356
|
typeNamePlural: "functions",
|
|
15125
15357
|
apiPath: "/api/admin/definitions/functions",
|
|
15358
|
+
normalizeBody: (body) => normalizeDefinitionWriteBody("Function", body),
|
|
15359
|
+
verifyUpdateResult: (body, result) => verifyDefinitionUpdateResponse("Function", body, result),
|
|
15126
15360
|
extractList: (data) => {
|
|
15127
15361
|
if (!Array.isArray(data)) {
|
|
15128
15362
|
throw new Error("Unexpected API response shape for functions list");
|
|
@@ -15754,6 +15988,8 @@ var workflowsConfig = {
|
|
|
15754
15988
|
typeName: "workflow",
|
|
15755
15989
|
typeNamePlural: "workflows",
|
|
15756
15990
|
apiPath: "/api/admin/definitions/workflows",
|
|
15991
|
+
normalizeBody: (body) => normalizeDefinitionWriteBody("Workflow", body),
|
|
15992
|
+
verifyUpdateResult: (body, result) => verifyDefinitionUpdateResponse("Workflow", body, result),
|
|
15757
15993
|
extractList: (data) => {
|
|
15758
15994
|
if (!Array.isArray(data)) {
|
|
15759
15995
|
throw new Error("Unexpected API response shape for workflows list");
|
|
@@ -16141,6 +16377,14 @@ function colorStatus2(status) {
|
|
|
16141
16377
|
const fn = STATUS_COLORS2[status] ?? source_default.white;
|
|
16142
16378
|
return fn(status);
|
|
16143
16379
|
}
|
|
16380
|
+
function unwrapList(res) {
|
|
16381
|
+
if (Array.isArray(res))
|
|
16382
|
+
return res;
|
|
16383
|
+
return res?.items ?? [];
|
|
16384
|
+
}
|
|
16385
|
+
function ingestionLabel(doc) {
|
|
16386
|
+
return doc.ingestion_status ?? "not ingested";
|
|
16387
|
+
}
|
|
16144
16388
|
async function pollIngestionUntilDone(ctx, documentId, intervalMs = 2000, timeoutMs = 300000) {
|
|
16145
16389
|
const deadline = Date.now() + timeoutMs;
|
|
16146
16390
|
while (Date.now() < deadline) {
|
|
@@ -16380,7 +16624,9 @@ ${items.length} collections`));
|
|
|
16380
16624
|
const ctx = getCtx();
|
|
16381
16625
|
const col = await resolveCollection(ctx, id);
|
|
16382
16626
|
const extraPromptIds = opts.clear ? [] : promptIds;
|
|
16383
|
-
const result = await ctx.api.put(`/api/admin/collections/${encodeURIComponent(col.id)}`, {
|
|
16627
|
+
const result = await ctx.api.put(`/api/admin/collections/${encodeURIComponent(col.id)}`, {
|
|
16628
|
+
extra_prompt_ids: extraPromptIds
|
|
16629
|
+
});
|
|
16384
16630
|
if (isJson()) {
|
|
16385
16631
|
console.log(JSON.stringify({ extra_prompt_ids: result.extra_prompt_ids ?? [] }, null, 2));
|
|
16386
16632
|
return;
|
|
@@ -16486,9 +16732,10 @@ ${items.length} collections`));
|
|
|
16486
16732
|
const params = {};
|
|
16487
16733
|
if (!opts.inactive)
|
|
16488
16734
|
params.active_only = "true";
|
|
16489
|
-
const
|
|
16735
|
+
const res = await ctx.api.get(`/api/admin/collections/${encodeURIComponent(collectionId)}/documents`, { params });
|
|
16736
|
+
const items = unwrapList(res);
|
|
16490
16737
|
if (isJson()) {
|
|
16491
|
-
console.log(JSON.stringify(
|
|
16738
|
+
console.log(JSON.stringify(res, null, 2));
|
|
16492
16739
|
return;
|
|
16493
16740
|
}
|
|
16494
16741
|
const columns = [
|
|
@@ -16502,8 +16749,8 @@ ${items.length} collections`));
|
|
|
16502
16749
|
accessor: (i) => i.size_bytes != null ? formatBytes(i.size_bytes) : "-"
|
|
16503
16750
|
},
|
|
16504
16751
|
{
|
|
16505
|
-
header: "
|
|
16506
|
-
accessor: (i) => i
|
|
16752
|
+
header: "INGESTION",
|
|
16753
|
+
accessor: (i) => ingestionLabel(i)
|
|
16507
16754
|
}
|
|
16508
16755
|
];
|
|
16509
16756
|
const widths = columns.map((col) => items.length === 0 ? col.header.length : Math.max(col.header.length, ...items.map((item) => col.accessor(item).length)));
|
|
@@ -16532,9 +16779,11 @@ ${items.length} documents`));
|
|
|
16532
16779
|
if (doc.size_bytes != null) {
|
|
16533
16780
|
console.log(` Size: ${formatBytes(doc.size_bytes)}`);
|
|
16534
16781
|
}
|
|
16535
|
-
console.log(`
|
|
16782
|
+
console.log(` Upload: ${colorStatus2(doc.status ?? "-")}`);
|
|
16783
|
+
console.log(` Ingestion: ${colorStatus2(ingestionLabel(doc))}`);
|
|
16536
16784
|
if (doc.chunk_count != null) {
|
|
16537
|
-
|
|
16785
|
+
const warn = doc.ingestion_status === "ready" && doc.chunk_count === 0 ? source_default.yellow(" ← ready but nothing retrievable") : "";
|
|
16786
|
+
console.log(` Chunks: ${doc.chunk_count}${warn}`);
|
|
16538
16787
|
}
|
|
16539
16788
|
console.log(` Active: ${doc.is_active !== false ? source_default.green("yes") : source_default.dim("no")}`);
|
|
16540
16789
|
if (doc.created_at)
|
|
@@ -18204,8 +18453,11 @@ ${data.length} spec(s)`));
|
|
|
18204
18453
|
if (opts.force)
|
|
18205
18454
|
params.force = "true";
|
|
18206
18455
|
const result = await ctx.api.post(`/api/admin/definitions/openapi-specs/${encodeURIComponent(id)}/sync`, undefined, { params });
|
|
18456
|
+
const syncFailed = result.status === "error" || result.status === "partial";
|
|
18207
18457
|
if (isJson()) {
|
|
18208
18458
|
console.log(JSON.stringify(result, null, 2));
|
|
18459
|
+
if (syncFailed)
|
|
18460
|
+
process.exitCode = 1 /* Error */;
|
|
18209
18461
|
return;
|
|
18210
18462
|
}
|
|
18211
18463
|
console.log(result.message ?? "Sync completed.");
|
|
@@ -18221,6 +18473,8 @@ ${data.length} spec(s)`));
|
|
|
18221
18473
|
console.log(source_default.red(` - ${err}`));
|
|
18222
18474
|
}
|
|
18223
18475
|
}
|
|
18476
|
+
if (syncFailed)
|
|
18477
|
+
process.exitCode = 1 /* Error */;
|
|
18224
18478
|
});
|
|
18225
18479
|
cmd.command("import-openapi").description("Import an OpenAPI spec").requiredOption("--source <url>", "OpenAPI spec URL").requiredOption("--name <name>", "Integration name").option("--prefix <prefix>", "Function name prefix").option("--include-tags <tags>", "Comma-separated tags to include").option("--exclude-tags <tags>", "Comma-separated tags to exclude").option("--dry-run", "Preview without creating (default)", true).option("--no-dry-run", "Actually create the functions").option("--force", "Force import even with conflicts").option("--auth-scheme <scheme>", "Auth scheme: apiKey, bearer, basic, oauth2, none").option("--auth-header <header>", "Header name for auth (default: Authorization)").option("--auth-value <value>", "Auth value or env var reference (${VAR})").option("--auth-query-param <param>", "Query parameter name for apiKey auth").option("--auth-credential-source <key>", "Vault credential lookup key").action(async (opts) => {
|
|
18226
18480
|
const ctx = getCtx();
|
|
@@ -18370,8 +18624,8 @@ ${data.length} spec(s)`));
|
|
|
18370
18624
|
Resources:
|
|
18371
18625
|
`));
|
|
18372
18626
|
for (const r of resources) {
|
|
18373
|
-
const
|
|
18374
|
-
console.log(` ${source_default.cyan(r.label || r.name)}` + source_default.dim(` → ${
|
|
18627
|
+
const path9 = r.path || `/${r.name}`;
|
|
18628
|
+
console.log(` ${source_default.cyan(r.label || r.name)}` + source_default.dim(` → ${path9}`));
|
|
18375
18629
|
}
|
|
18376
18630
|
}
|
|
18377
18631
|
try {
|
|
@@ -18391,14 +18645,14 @@ Functions:
|
|
|
18391
18645
|
console.log(source_default.dim(`
|
|
18392
18646
|
Proxy: martha integrations proxy ${name} GET /<path>`));
|
|
18393
18647
|
});
|
|
18394
|
-
cmd.command("proxy <name> <method> <path>").description("Send a request through the plugin proxy").option("--data <json>", "JSON request body").option("--query <params>", "Query params as key=val&key=val").action(async (name, method,
|
|
18648
|
+
cmd.command("proxy <name> <method> <path>").description("Send a request through the plugin proxy").option("--data <json>", "JSON request body").option("--query <params>", "Query params as key=val&key=val").action(async (name, method, path9, opts) => {
|
|
18395
18649
|
const ctx = getCtx();
|
|
18396
18650
|
const upperMethod = method.toUpperCase();
|
|
18397
18651
|
const allowed = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
18398
18652
|
if (!allowed.has(upperMethod)) {
|
|
18399
18653
|
throw new CLIError(`Invalid method: ${method}`, 4 /* Validation */, "Allowed: GET, POST, PUT, PATCH, DELETE");
|
|
18400
18654
|
}
|
|
18401
|
-
const cleanPath =
|
|
18655
|
+
const cleanPath = path9.startsWith("/") ? path9.slice(1) : path9;
|
|
18402
18656
|
const proxyUrl = `/api/admin/plugins/${encodeURIComponent(name)}/${cleanPath}`;
|
|
18403
18657
|
const params = {};
|
|
18404
18658
|
if (opts.query) {
|
|
@@ -18776,11 +19030,11 @@ async function readStdin2() {
|
|
|
18776
19030
|
|
|
18777
19031
|
// src/commands/messaging.ts
|
|
18778
19032
|
init_errors();
|
|
18779
|
-
async function messagingFetch(baseUrl,
|
|
18780
|
-
if (/^(https?:)?\/\//i.test(
|
|
19033
|
+
async function messagingFetch(baseUrl, path9, opts) {
|
|
19034
|
+
if (/^(https?:)?\/\//i.test(path9)) {
|
|
18781
19035
|
throw new CLIError("Absolute URL paths are not allowed", 1 /* Error */);
|
|
18782
19036
|
}
|
|
18783
|
-
const url = new URL(
|
|
19037
|
+
const url = new URL(path9, baseUrl);
|
|
18784
19038
|
const headers = {
|
|
18785
19039
|
"Content-Type": "application/json"
|
|
18786
19040
|
};
|
|
@@ -20218,16 +20472,16 @@ ${wikis.length} wiki${wikis.length === 1 ? "" : "s"}`));
|
|
|
20218
20472
|
console.log(source_default.dim(`
|
|
20219
20473
|
${pages.length} pages`));
|
|
20220
20474
|
});
|
|
20221
|
-
cmd.command("get <path>").description("Fetch the raw markdown body of a wiki page").option("--wiki <slug>", "Wiki slug (default: the tenant default wiki)").option("--out <file>", "Write body to file instead of stdout").action(async (
|
|
20475
|
+
cmd.command("get <path>").description("Fetch the raw markdown body of a wiki page").option("--wiki <slug>", "Wiki slug (default: the tenant default wiki)").option("--out <file>", "Write body to file instead of stdout").action(async (path9, opts) => {
|
|
20222
20476
|
const ctx = getCtx();
|
|
20223
|
-
const safe =
|
|
20477
|
+
const safe = path9.split("/").map(encodeURIComponent).join("/");
|
|
20224
20478
|
const slug = encodeURIComponent(opts.wiki ?? DEFAULT_WIKI_SLUG);
|
|
20225
20479
|
const endpoint = `/api/wiki/${slug}/pages/${safe}`;
|
|
20226
20480
|
const resp = await ctx.api.getRaw(endpoint, {
|
|
20227
20481
|
headers: { Accept: "text/markdown,*/*" }
|
|
20228
20482
|
});
|
|
20229
20483
|
if (resp.status === 404) {
|
|
20230
|
-
throw new CLIError(`page not found: ${
|
|
20484
|
+
throw new CLIError(`page not found: ${path9}`, 3 /* NotFound */);
|
|
20231
20485
|
}
|
|
20232
20486
|
if (!resp.ok) {
|
|
20233
20487
|
const detail = await resp.text();
|
|
@@ -20239,12 +20493,12 @@ ${pages.length} pages`));
|
|
|
20239
20493
|
if (!isJson()) {
|
|
20240
20494
|
console.log(source_default.dim(`wrote ${body.length} bytes to ${opts.out}`));
|
|
20241
20495
|
} else {
|
|
20242
|
-
console.log(JSON.stringify({ path:
|
|
20496
|
+
console.log(JSON.stringify({ path: path9, bytes: body.length, file: opts.out }));
|
|
20243
20497
|
}
|
|
20244
20498
|
return;
|
|
20245
20499
|
}
|
|
20246
20500
|
if (isJson()) {
|
|
20247
|
-
console.log(JSON.stringify({ path:
|
|
20501
|
+
console.log(JSON.stringify({ path: path9, body, etag: resp.headers.get("ETag") }));
|
|
20248
20502
|
} else {
|
|
20249
20503
|
process.stdout.write(body);
|
|
20250
20504
|
}
|
|
@@ -21936,11 +22190,11 @@ function registerInitCommand(program2) {
|
|
|
21936
22190
|
|
|
21937
22191
|
// src/commands/doctor.ts
|
|
21938
22192
|
var FETCH_TIMEOUT_MS = 5000;
|
|
21939
|
-
function resolveApiUrl(base,
|
|
21940
|
-
return new URL(
|
|
22193
|
+
function resolveApiUrl(base, path9) {
|
|
22194
|
+
return new URL(path9, base).toString();
|
|
21941
22195
|
}
|
|
21942
|
-
function apiUrl(ctx,
|
|
21943
|
-
return resolveApiUrl(ctx.profile.api_url,
|
|
22196
|
+
function apiUrl(ctx, path9) {
|
|
22197
|
+
return resolveApiUrl(ctx.profile.api_url, path9);
|
|
21944
22198
|
}
|
|
21945
22199
|
async function fetchJSON(url, init) {
|
|
21946
22200
|
const res = await fetch(url, {
|
|
@@ -22238,15 +22492,15 @@ function registerDoctorCommand(program2) {
|
|
|
22238
22492
|
// src/commands/skill.ts
|
|
22239
22493
|
init_errors();
|
|
22240
22494
|
import fs12 from "node:fs";
|
|
22241
|
-
import
|
|
22495
|
+
import path9 from "node:path";
|
|
22242
22496
|
import { fileURLToPath } from "node:url";
|
|
22243
22497
|
function locateSkill() {
|
|
22244
|
-
const here =
|
|
22498
|
+
const here = path9.dirname(fileURLToPath(import.meta.url));
|
|
22245
22499
|
const candidates = [
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
|
|
22500
|
+
path9.join(here, "skills", "martha-cli", "SKILL.md"),
|
|
22501
|
+
path9.join(here, "..", "skills", "martha-cli", "SKILL.md"),
|
|
22502
|
+
path9.join(here, "..", "..", "..", "skills", "martha-cli", "SKILL.md"),
|
|
22503
|
+
path9.join(here, "..", "..", "skills", "martha-cli", "SKILL.md")
|
|
22250
22504
|
];
|
|
22251
22505
|
for (const p of candidates) {
|
|
22252
22506
|
if (fs12.existsSync(p))
|