@aiaiai-pt/martha-cli 0.65.0 → 0.66.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/CHANGELOG.md +7 -0
- package/dist/index.js +283 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@aiaiai-pt/martha-cli`. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project adheres to semver — `0.x` releases may include breaking changes between minor versions.
|
|
4
4
|
|
|
5
|
+
## [0.66.0] — 2026-11-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `martha definitions apply` provisions skills and agent grants (#2339). A `kind: Prompt` file creates the prompt, or a new version only when its content differs from production, promotes it and checks the production content's hash; `description`/`scope` metadata is updated when it differs. An Agent's `functions:` and `workflows:` are granted and read back; grants the file does not declare are reported as drift and never revoked. The dry-run plan lists pending grants and drift.
|
|
10
|
+
- `-f` can be given more than once (`-f agent/ -f agent/skills/`). Directories are still not recursed.
|
|
11
|
+
|
|
5
12
|
## [0.65.0] — 2026-10-24
|
|
6
13
|
|
|
7
14
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -11244,7 +11244,7 @@ init_config();
|
|
|
11244
11244
|
init_errors();
|
|
11245
11245
|
|
|
11246
11246
|
// src/version.ts
|
|
11247
|
-
var CLI_VERSION = "0.
|
|
11247
|
+
var CLI_VERSION = "0.66.0";
|
|
11248
11248
|
|
|
11249
11249
|
// src/commands/sessions.ts
|
|
11250
11250
|
function relativeTime(iso) {
|
|
@@ -16092,21 +16092,24 @@ var VALID_KINDS = new Set([
|
|
|
16092
16092
|
"Workflow",
|
|
16093
16093
|
"Agent",
|
|
16094
16094
|
"Trigger",
|
|
16095
|
-
"ClientAccess"
|
|
16095
|
+
"ClientAccess",
|
|
16096
|
+
"Prompt"
|
|
16096
16097
|
]);
|
|
16097
16098
|
var KIND_TO_API_PATH = {
|
|
16098
16099
|
Function: "/api/admin/definitions/functions",
|
|
16099
16100
|
Workflow: "/api/admin/definitions/workflows",
|
|
16100
16101
|
Agent: "/api/admin/definitions/agents",
|
|
16101
16102
|
Trigger: "/api/admin/triggers",
|
|
16102
|
-
ClientAccess: "/api/admin/definitions/client-access"
|
|
16103
|
+
ClientAccess: "/api/admin/definitions/client-access",
|
|
16104
|
+
Prompt: "/api/admin/definitions/prompts"
|
|
16103
16105
|
};
|
|
16104
16106
|
var KIND_TO_PLURAL = {
|
|
16105
16107
|
Function: "Functions",
|
|
16106
16108
|
ClientAccess: "Client access",
|
|
16107
16109
|
Workflow: "Workflows",
|
|
16108
16110
|
Agent: "Agents",
|
|
16109
|
-
Trigger: "Triggers"
|
|
16111
|
+
Trigger: "Triggers",
|
|
16112
|
+
Prompt: "Prompts"
|
|
16110
16113
|
};
|
|
16111
16114
|
var SERVER_GENERATED_FIELDS = new Set([
|
|
16112
16115
|
"id",
|
|
@@ -16274,17 +16277,36 @@ async function computeOperations(defs, api) {
|
|
|
16274
16277
|
});
|
|
16275
16278
|
continue;
|
|
16276
16279
|
}
|
|
16277
|
-
const changedFields = diffFields(def.body, remote);
|
|
16280
|
+
const changedFields = def.kind === "Prompt" ? diffPromptFields(def.body, remote) : diffFields(def.body, remote);
|
|
16278
16281
|
const needsReactivation = remote.is_active === false && def.body.is_active !== false;
|
|
16279
16282
|
if (needsReactivation && !changedFields.includes("is_active")) {
|
|
16280
16283
|
changedFields.push("is_active");
|
|
16281
16284
|
}
|
|
16285
|
+
let pendingGrants;
|
|
16286
|
+
let driftGrants;
|
|
16287
|
+
if (def.kind === "Agent" && remote) {
|
|
16288
|
+
const grants = await computeAgentGrantDiff(api, def);
|
|
16289
|
+
if (grants.pending.functions.length > 0 || grants.pending.workflows.length > 0) {
|
|
16290
|
+
pendingGrants = grants.pending;
|
|
16291
|
+
}
|
|
16292
|
+
if (grants.drift.functions.length > 0 || grants.drift.workflows.length > 0) {
|
|
16293
|
+
driftGrants = grants.drift;
|
|
16294
|
+
}
|
|
16295
|
+
} else if (def.kind === "Agent" && !remote) {
|
|
16296
|
+
const fns = def.body.functions ?? [];
|
|
16297
|
+
const wfs = def.body.workflows ?? [];
|
|
16298
|
+
if (fns.length > 0 || wfs.length > 0) {
|
|
16299
|
+
pendingGrants = { functions: fns, workflows: wfs };
|
|
16300
|
+
}
|
|
16301
|
+
}
|
|
16282
16302
|
if (changedFields.length === 0) {
|
|
16283
16303
|
ops.push({
|
|
16284
16304
|
kind: def.kind,
|
|
16285
16305
|
name: def.name,
|
|
16286
16306
|
action: "skip",
|
|
16287
|
-
source: def.source
|
|
16307
|
+
source: def.source,
|
|
16308
|
+
pendingGrants,
|
|
16309
|
+
driftGrants
|
|
16288
16310
|
});
|
|
16289
16311
|
} else {
|
|
16290
16312
|
ops.push({
|
|
@@ -16293,7 +16315,9 @@ async function computeOperations(defs, api) {
|
|
|
16293
16315
|
action: "update",
|
|
16294
16316
|
changedFields,
|
|
16295
16317
|
source: def.source,
|
|
16296
|
-
reactivate: needsReactivation || undefined
|
|
16318
|
+
reactivate: needsReactivation || undefined,
|
|
16319
|
+
pendingGrants,
|
|
16320
|
+
driftGrants
|
|
16297
16321
|
});
|
|
16298
16322
|
}
|
|
16299
16323
|
}
|
|
@@ -16307,6 +16331,8 @@ function diffFields(local, remote) {
|
|
|
16307
16331
|
continue;
|
|
16308
16332
|
if (SERVER_GENERATED_FIELDS.has(key))
|
|
16309
16333
|
continue;
|
|
16334
|
+
if (AGENT_MANAGED_FIELDS.has(key))
|
|
16335
|
+
continue;
|
|
16310
16336
|
const localVal = local[key];
|
|
16311
16337
|
const remoteVal = flatRemote[key];
|
|
16312
16338
|
if (!localSubsetOf(localVal, remoteVal)) {
|
|
@@ -16315,6 +16341,32 @@ function diffFields(local, remote) {
|
|
|
16315
16341
|
}
|
|
16316
16342
|
return changed;
|
|
16317
16343
|
}
|
|
16344
|
+
function diffPromptFields(local, remote) {
|
|
16345
|
+
const changed = [];
|
|
16346
|
+
const prod = remote.production;
|
|
16347
|
+
for (const key of Object.keys(local)) {
|
|
16348
|
+
if (key === "name")
|
|
16349
|
+
continue;
|
|
16350
|
+
if (key === "change_summary")
|
|
16351
|
+
continue;
|
|
16352
|
+
if (SERVER_GENERATED_FIELDS.has(key))
|
|
16353
|
+
continue;
|
|
16354
|
+
if (key === "content") {
|
|
16355
|
+
const localContent = local.content;
|
|
16356
|
+
const remoteContent = prod?.content;
|
|
16357
|
+
if (localContent !== remoteContent) {
|
|
16358
|
+
changed.push("content");
|
|
16359
|
+
}
|
|
16360
|
+
} else {
|
|
16361
|
+
const localVal = local[key];
|
|
16362
|
+
const remoteVal = remote[key];
|
|
16363
|
+
if (!localSubsetOf(localVal, remoteVal)) {
|
|
16364
|
+
changed.push(key);
|
|
16365
|
+
}
|
|
16366
|
+
}
|
|
16367
|
+
}
|
|
16368
|
+
return changed;
|
|
16369
|
+
}
|
|
16318
16370
|
function flattenDefinitionWrapper(remote) {
|
|
16319
16371
|
const result = {};
|
|
16320
16372
|
for (const [key, val] of Object.entries(remote)) {
|
|
@@ -16387,7 +16439,8 @@ function renderPlan(ops) {
|
|
|
16387
16439
|
"Workflow",
|
|
16388
16440
|
"Agent",
|
|
16389
16441
|
"Trigger",
|
|
16390
|
-
"ClientAccess"
|
|
16442
|
+
"ClientAccess",
|
|
16443
|
+
"Prompt"
|
|
16391
16444
|
]) {
|
|
16392
16445
|
const kindOps = grouped.get(kind);
|
|
16393
16446
|
if (!kindOps)
|
|
@@ -16406,6 +16459,16 @@ function renderPlan(ops) {
|
|
|
16406
16459
|
line += source_default.dim("(no changes)");
|
|
16407
16460
|
}
|
|
16408
16461
|
console.log(line);
|
|
16462
|
+
const pend = op.pendingGrants;
|
|
16463
|
+
if (pend && pend.functions.length + pend.workflows.length > 0) {
|
|
16464
|
+
const names = [...pend.functions, ...pend.workflows.map((w) => `workflow:${w}`)];
|
|
16465
|
+
console.log(source_default.green(` + grant ${names.join(", ")}`));
|
|
16466
|
+
}
|
|
16467
|
+
const drift = op.driftGrants;
|
|
16468
|
+
if (drift && drift.functions.length + drift.workflows.length > 0) {
|
|
16469
|
+
const names = [...drift.functions, ...drift.workflows.map((w) => `workflow:${w}`)];
|
|
16470
|
+
console.log(source_default.yellow(` ! drift (granted, not declared; not revoked): ${names.join(", ")}`));
|
|
16471
|
+
}
|
|
16409
16472
|
}
|
|
16410
16473
|
console.log();
|
|
16411
16474
|
}
|
|
@@ -16438,22 +16501,51 @@ async function executeApply(api, ops, defs, warnings) {
|
|
|
16438
16501
|
const results = [];
|
|
16439
16502
|
ops = [...ops].sort((a, b) => (a.kind === "ClientAccess" ? 1 : 0) - (b.kind === "ClientAccess" ? 1 : 0));
|
|
16440
16503
|
for (const op of ops) {
|
|
16504
|
+
const def = defsByKey.get(`${op.kind}:${op.name}`);
|
|
16441
16505
|
if (op.action === "skip") {
|
|
16442
|
-
|
|
16506
|
+
if (op.kind !== "Agent") {
|
|
16507
|
+
results.push({ name: op.name, kind: op.kind, action: "skipped" });
|
|
16508
|
+
continue;
|
|
16509
|
+
}
|
|
16510
|
+
try {
|
|
16511
|
+
const added = await applyAgentGrants(api, def, warnings);
|
|
16512
|
+
results.push({
|
|
16513
|
+
name: op.name,
|
|
16514
|
+
kind: op.kind,
|
|
16515
|
+
action: added > 0 ? "updated" : "skipped"
|
|
16516
|
+
});
|
|
16517
|
+
} catch (err) {
|
|
16518
|
+
results.push({
|
|
16519
|
+
name: op.name,
|
|
16520
|
+
kind: op.kind,
|
|
16521
|
+
action: "failed",
|
|
16522
|
+
error: err instanceof Error ? err.message : String(err),
|
|
16523
|
+
source: def.source
|
|
16524
|
+
});
|
|
16525
|
+
}
|
|
16443
16526
|
continue;
|
|
16444
16527
|
}
|
|
16445
|
-
const def = defsByKey.get(`${op.kind}:${op.name}`);
|
|
16446
16528
|
const apiPath = KIND_TO_API_PATH[op.kind];
|
|
16529
|
+
if (op.kind === "Prompt") {
|
|
16530
|
+
try {
|
|
16531
|
+
const result = await applyPrompt(api, op, def);
|
|
16532
|
+
results.push(result);
|
|
16533
|
+
} catch (err) {
|
|
16534
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
16535
|
+
results.push({
|
|
16536
|
+
name: op.name,
|
|
16537
|
+
kind: op.kind,
|
|
16538
|
+
action: "failed",
|
|
16539
|
+
error: msg,
|
|
16540
|
+
source: def.source
|
|
16541
|
+
});
|
|
16542
|
+
}
|
|
16543
|
+
continue;
|
|
16544
|
+
}
|
|
16447
16545
|
const body = op.kind === "Function" || op.kind === "Workflow" ? normalizeDefinitionWriteBody(op.kind, def.body) : { ...def.body };
|
|
16448
16546
|
if (op.kind === "Agent") {
|
|
16449
16547
|
for (const field of AGENT_MANAGED_FIELDS) {
|
|
16450
16548
|
if (field in body) {
|
|
16451
|
-
const msg = `'${field}' in ${def.name} is managed separately and will be ignored`;
|
|
16452
|
-
if (warnings) {
|
|
16453
|
-
warnings.push(msg);
|
|
16454
|
-
} else {
|
|
16455
|
-
console.error(source_default.yellow(` Warning: ${msg}.`));
|
|
16456
|
-
}
|
|
16457
16549
|
delete body[field];
|
|
16458
16550
|
}
|
|
16459
16551
|
}
|
|
@@ -16486,6 +16578,9 @@ async function executeApply(api, ops, defs, warnings) {
|
|
|
16486
16578
|
port_warnings: portWarnings.length > 0 ? portWarnings : undefined
|
|
16487
16579
|
});
|
|
16488
16580
|
}
|
|
16581
|
+
if (op.kind === "Agent") {
|
|
16582
|
+
await applyAgentGrants(api, def, warnings);
|
|
16583
|
+
}
|
|
16489
16584
|
} catch (err) {
|
|
16490
16585
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16491
16586
|
results.push({
|
|
@@ -16499,6 +16594,175 @@ async function executeApply(api, ops, defs, warnings) {
|
|
|
16499
16594
|
}
|
|
16500
16595
|
return results;
|
|
16501
16596
|
}
|
|
16597
|
+
async function applyPrompt(api, op, def) {
|
|
16598
|
+
const promptPath = KIND_TO_API_PATH.Prompt;
|
|
16599
|
+
const name = encodeURIComponent(def.name);
|
|
16600
|
+
const localContent = def.body.content;
|
|
16601
|
+
if (op.action === "create") {
|
|
16602
|
+
const createBody = {
|
|
16603
|
+
name: def.body.name,
|
|
16604
|
+
content: localContent
|
|
16605
|
+
};
|
|
16606
|
+
if (def.body.description)
|
|
16607
|
+
createBody.description = def.body.description;
|
|
16608
|
+
if (def.body.scope)
|
|
16609
|
+
createBody.scope = def.body.scope;
|
|
16610
|
+
const res = await api.post(promptPath, createBody);
|
|
16611
|
+
const readBack = await api.get(`${promptPath}/${name}`);
|
|
16612
|
+
const prodContent = readBack.production?.content;
|
|
16613
|
+
const localHash = createHash3("sha256").update(localContent).digest("hex");
|
|
16614
|
+
const remoteHash = prodContent ? createHash3("sha256").update(prodContent).digest("hex") : "";
|
|
16615
|
+
if (localHash !== remoteHash) {
|
|
16616
|
+
return {
|
|
16617
|
+
name: def.name,
|
|
16618
|
+
kind: "Prompt",
|
|
16619
|
+
action: "failed",
|
|
16620
|
+
error: `Content hash mismatch after create: local=${localHash.slice(0, 12)} remote=${remoteHash.slice(0, 12)}`,
|
|
16621
|
+
source: def.source
|
|
16622
|
+
};
|
|
16623
|
+
}
|
|
16624
|
+
const version = res.version_count ?? res.production?.version ?? 1;
|
|
16625
|
+
return {
|
|
16626
|
+
name: def.name,
|
|
16627
|
+
kind: "Prompt",
|
|
16628
|
+
action: "created",
|
|
16629
|
+
version
|
|
16630
|
+
};
|
|
16631
|
+
}
|
|
16632
|
+
const contentChanged = op.changedFields?.includes("content");
|
|
16633
|
+
const metadataFields = (op.changedFields ?? []).filter((f) => f !== "content");
|
|
16634
|
+
let version;
|
|
16635
|
+
if (contentChanged) {
|
|
16636
|
+
const versionRes = await api.post(`${promptPath}/${name}/versions`, {
|
|
16637
|
+
content: localContent,
|
|
16638
|
+
change_summary: def.body.change_summary ?? "Applied via definitions apply"
|
|
16639
|
+
});
|
|
16640
|
+
const newVersion = versionRes.version;
|
|
16641
|
+
version = newVersion;
|
|
16642
|
+
await api.post(`${promptPath}/${name}/promote`, {
|
|
16643
|
+
version: newVersion,
|
|
16644
|
+
environment: "production"
|
|
16645
|
+
});
|
|
16646
|
+
}
|
|
16647
|
+
if (metadataFields.length > 0) {
|
|
16648
|
+
const metaBody = {};
|
|
16649
|
+
if (metadataFields.includes("description")) {
|
|
16650
|
+
metaBody.description = def.body.description;
|
|
16651
|
+
}
|
|
16652
|
+
if (metadataFields.includes("scope")) {
|
|
16653
|
+
await api.put(`${promptPath}/${name}/scope`, {
|
|
16654
|
+
scope: def.body.scope
|
|
16655
|
+
});
|
|
16656
|
+
}
|
|
16657
|
+
if (Object.keys(metaBody).length > 0) {
|
|
16658
|
+
await api.put(`${promptPath}/${name}`, metaBody);
|
|
16659
|
+
}
|
|
16660
|
+
}
|
|
16661
|
+
if (contentChanged) {
|
|
16662
|
+
const readBack = await api.get(`${promptPath}/${name}`);
|
|
16663
|
+
const prodContent = readBack.production?.content;
|
|
16664
|
+
const localHash = createHash3("sha256").update(localContent).digest("hex");
|
|
16665
|
+
const remoteHash = prodContent ? createHash3("sha256").update(prodContent).digest("hex") : "";
|
|
16666
|
+
if (localHash !== remoteHash) {
|
|
16667
|
+
return {
|
|
16668
|
+
name: def.name,
|
|
16669
|
+
kind: "Prompt",
|
|
16670
|
+
action: "failed",
|
|
16671
|
+
error: `Content hash mismatch after update: local=${localHash.slice(0, 12)} remote=${remoteHash.slice(0, 12)}`,
|
|
16672
|
+
source: def.source
|
|
16673
|
+
};
|
|
16674
|
+
}
|
|
16675
|
+
}
|
|
16676
|
+
return {
|
|
16677
|
+
name: def.name,
|
|
16678
|
+
kind: "Prompt",
|
|
16679
|
+
action: "updated",
|
|
16680
|
+
version
|
|
16681
|
+
};
|
|
16682
|
+
}
|
|
16683
|
+
async function computeAgentGrantDiff(api, def) {
|
|
16684
|
+
const agentPath = KIND_TO_API_PATH.Agent;
|
|
16685
|
+
const name = encodeURIComponent(def.name);
|
|
16686
|
+
const declaredFunctions = def.body.functions ?? [];
|
|
16687
|
+
const declaredWorkflows = def.body.workflows ?? [];
|
|
16688
|
+
const pendingFns = [];
|
|
16689
|
+
const driftFns = [];
|
|
16690
|
+
const pendingWfs = [];
|
|
16691
|
+
const driftWfs = [];
|
|
16692
|
+
if (declaredFunctions.length > 0) {
|
|
16693
|
+
const remoteFns = await api.get(`${agentPath}/${name}/functions`);
|
|
16694
|
+
const remoteFnNames = new Set((Array.isArray(remoteFns) ? remoteFns : []).map((f) => f.function_name ?? f.name));
|
|
16695
|
+
for (const fn of declaredFunctions) {
|
|
16696
|
+
if (!remoteFnNames.has(fn))
|
|
16697
|
+
pendingFns.push(fn);
|
|
16698
|
+
}
|
|
16699
|
+
const declaredSet = new Set(declaredFunctions);
|
|
16700
|
+
for (const rn of remoteFnNames) {
|
|
16701
|
+
if (!declaredSet.has(rn))
|
|
16702
|
+
driftFns.push(rn);
|
|
16703
|
+
}
|
|
16704
|
+
}
|
|
16705
|
+
if (declaredWorkflows.length > 0) {
|
|
16706
|
+
const remoteWfs = await api.get(`${agentPath}/${name}/workflows`);
|
|
16707
|
+
const remoteWfNames = new Set((Array.isArray(remoteWfs) ? remoteWfs : []).map((w) => w.workflow_name ?? w.name));
|
|
16708
|
+
for (const wf of declaredWorkflows) {
|
|
16709
|
+
if (!remoteWfNames.has(wf))
|
|
16710
|
+
pendingWfs.push(wf);
|
|
16711
|
+
}
|
|
16712
|
+
const declaredSet = new Set(declaredWorkflows);
|
|
16713
|
+
for (const rn of remoteWfNames) {
|
|
16714
|
+
if (!declaredSet.has(rn))
|
|
16715
|
+
driftWfs.push(rn);
|
|
16716
|
+
}
|
|
16717
|
+
}
|
|
16718
|
+
return {
|
|
16719
|
+
pending: { functions: pendingFns, workflows: pendingWfs },
|
|
16720
|
+
drift: { functions: driftFns, workflows: driftWfs }
|
|
16721
|
+
};
|
|
16722
|
+
}
|
|
16723
|
+
async function applyAgentGrants(api, def, warnings) {
|
|
16724
|
+
const agentPath = KIND_TO_API_PATH.Agent;
|
|
16725
|
+
const name = encodeURIComponent(def.name);
|
|
16726
|
+
const diff = await computeAgentGrantDiff(api, def);
|
|
16727
|
+
for (const fn of diff.pending.functions) {
|
|
16728
|
+
await api.post(`${agentPath}/${name}/functions`, {
|
|
16729
|
+
function_name: fn
|
|
16730
|
+
});
|
|
16731
|
+
}
|
|
16732
|
+
for (const wf of diff.pending.workflows) {
|
|
16733
|
+
await api.post(`${agentPath}/${name}/workflows`, {
|
|
16734
|
+
workflow_name: wf
|
|
16735
|
+
});
|
|
16736
|
+
}
|
|
16737
|
+
const posted = diff.pending.functions.length + diff.pending.workflows.length;
|
|
16738
|
+
if (posted > 0) {
|
|
16739
|
+
const after = await computeAgentGrantDiff(api, def);
|
|
16740
|
+
const missing = [
|
|
16741
|
+
...after.pending.functions.map((f) => `function '${f}'`),
|
|
16742
|
+
...after.pending.workflows.map((w) => `workflow '${w}'`)
|
|
16743
|
+
];
|
|
16744
|
+
if (missing.length > 0) {
|
|
16745
|
+
throw new Error(`grants not on the server after apply for ${def.name}: ${missing.join(", ")}`);
|
|
16746
|
+
}
|
|
16747
|
+
}
|
|
16748
|
+
for (const fn of diff.drift.functions) {
|
|
16749
|
+
const msg = `drift: function '${fn}' granted to ${def.name} remotely but not declared in file`;
|
|
16750
|
+
if (warnings) {
|
|
16751
|
+
warnings.push(msg);
|
|
16752
|
+
} else {
|
|
16753
|
+
console.error(source_default.yellow(` Warning: ${msg}`));
|
|
16754
|
+
}
|
|
16755
|
+
}
|
|
16756
|
+
for (const wf of diff.drift.workflows) {
|
|
16757
|
+
const msg = `drift: workflow '${wf}' granted to ${def.name} remotely but not declared in file`;
|
|
16758
|
+
if (warnings) {
|
|
16759
|
+
warnings.push(msg);
|
|
16760
|
+
} else {
|
|
16761
|
+
console.error(source_default.yellow(` Warning: ${msg}`));
|
|
16762
|
+
}
|
|
16763
|
+
}
|
|
16764
|
+
return posted;
|
|
16765
|
+
}
|
|
16502
16766
|
function extractPortWarnings(response) {
|
|
16503
16767
|
const raw = response.port_warnings;
|
|
16504
16768
|
if (!Array.isArray(raw) || raw.length === 0)
|
|
@@ -16544,7 +16808,7 @@ function registerDefinitionsApply(program) {
|
|
|
16544
16808
|
if (!defCmd) {
|
|
16545
16809
|
defCmd = program.command("definitions").description("Cross-type definition operations");
|
|
16546
16810
|
}
|
|
16547
|
-
defCmd.command("apply").description("Apply definitions from YAML/JSON files (create or update, never deletes)").requiredOption("-f, --file <path>", "Path to a YAML/JSON file or directory of definitions").option("--dry-run", "Preview changes without applying").option("--yes", "Skip confirmation prompt").addHelpText("after", `
|
|
16811
|
+
defCmd.command("apply").description("Apply definitions from YAML/JSON files (create or update, never deletes)").requiredOption("-f, --file <path>", "Path to a YAML/JSON file or directory of definitions (repeatable; directories are not recursed)", (value, previous = []) => [...previous, value]).option("--dry-run", "Preview changes without applying").option("--yes", "Skip confirmation prompt").addHelpText("after", `
|
|
16548
16812
|
Examples:
|
|
16549
16813
|
$ martha definitions apply -f search.yaml Single file
|
|
16550
16814
|
$ martha definitions apply -f definitions/ Directory (flat)
|
|
@@ -16559,12 +16823,12 @@ Examples:
|
|
|
16559
16823
|
ctx.profile.api_url = program.opts().apiUrl;
|
|
16560
16824
|
}
|
|
16561
16825
|
const isJson = !!program.opts().json;
|
|
16562
|
-
const defs =
|
|
16826
|
+
const defs = opts.file.flatMap((f) => loadDefinitions(f));
|
|
16563
16827
|
if (defs.length === 0) {
|
|
16564
16828
|
if (isJson) {
|
|
16565
16829
|
console.log(JSON.stringify({ results: [], message: "No definitions found" }));
|
|
16566
16830
|
} else {
|
|
16567
|
-
console.log(`No definitions found in ${opts.file}`);
|
|
16831
|
+
console.log(`No definitions found in ${opts.file.join(", ")}`);
|
|
16568
16832
|
}
|
|
16569
16833
|
return;
|
|
16570
16834
|
}
|