@program-video/cli 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -872,6 +872,16 @@ function inferMimeType(fileName) {
872
872
  const extension = path2.extname(fileName).toLowerCase();
873
873
  return MIME_TYPES_BY_EXTENSION[extension] ?? null;
874
874
  }
875
+ function normalizeToolParams(toolName, params) {
876
+ if (toolName !== "addScene") {
877
+ return params;
878
+ }
879
+ const next = { ...params };
880
+ if (typeof next.name === "string" && next.name.length > 0 && (typeof next.title !== "string" || next.title.length === 0)) {
881
+ next.title = next.name;
882
+ }
883
+ return next;
884
+ }
875
885
  async function uploadFileFromPath(params, projectId, accessToken, source) {
876
886
  const filePath = params.path;
877
887
  if (typeof filePath !== "string" || filePath.length === 0) {
@@ -900,31 +910,79 @@ async function uploadFileFromPath(params, projectId, accessToken, source) {
900
910
  throw new Error("category must match /^[a-z0-9_-]{1,32}$/i");
901
911
  }
902
912
  const metadata = params.metadata && typeof params.metadata === "object" ? params.metadata : void 0;
903
- const fileStream = createReadStream(filePath);
904
- const uploadRequest = {
913
+ const initRequest = {
905
914
  method: "POST",
906
915
  headers: {
907
916
  Authorization: `Bearer ${accessToken}`,
908
- "Content-Type": mimeType,
909
- "Content-Length": String(fileStat.size),
917
+ "Content-Type": "application/json",
910
918
  "x-project-id": projectId,
911
- "x-file-name": encodeURIComponent(fileName),
912
- "x-file-size": String(fileStat.size),
913
- ...category ? { "x-category": category } : {},
914
- ...metadata ? { "x-metadata": JSON.stringify(metadata) } : {},
915
919
  ...source ? { "x-source": source } : {}
916
920
  },
921
+ body: JSON.stringify({
922
+ step: "init",
923
+ fileName,
924
+ mimeType,
925
+ fileSize: fileStat.size,
926
+ category,
927
+ metadata
928
+ })
929
+ };
930
+ const initResponse = await fetch(`${BASE_URL}/api/cli/upload`, initRequest);
931
+ const initResult = await initResponse.json().catch(() => null);
932
+ if (!initResponse.ok || !initResult?.success || !initResult.data) {
933
+ throw new Error(
934
+ initResult?.error ?? `Upload init failed (${initResponse.status})`
935
+ );
936
+ }
937
+ const fileStream = createReadStream(filePath);
938
+ const uploadRequest = {
939
+ method: "POST",
940
+ headers: {
941
+ "Content-Type": mimeType,
942
+ "Content-Length": String(fileStat.size)
943
+ },
917
944
  body: fileStream,
918
945
  duplex: "half"
919
946
  };
920
- const response = await fetch(`${BASE_URL}/api/cli/upload`, uploadRequest);
921
- const result = await response.json().catch(() => null);
922
- if (!response.ok || !result?.success) {
947
+ const storageUploadResponse = await fetch(
948
+ initResult.data.uploadUrl,
949
+ uploadRequest
950
+ );
951
+ const storageUploadJson = await storageUploadResponse.json().catch(() => null);
952
+ if (!storageUploadResponse.ok || !storageUploadJson || typeof storageUploadJson.storageId !== "string") {
953
+ throw new Error(
954
+ `Failed to upload bytes to storage (${storageUploadResponse.status})`
955
+ );
956
+ }
957
+ const finalizeRequest = {
958
+ method: "POST",
959
+ headers: {
960
+ Authorization: `Bearer ${accessToken}`,
961
+ "Content-Type": "application/json",
962
+ "x-project-id": projectId,
963
+ ...source ? { "x-source": source } : {}
964
+ },
965
+ body: JSON.stringify({
966
+ step: "finalize",
967
+ storageId: storageUploadJson.storageId,
968
+ fileName,
969
+ mimeType,
970
+ fileSize: fileStat.size,
971
+ category,
972
+ metadata
973
+ })
974
+ };
975
+ const finalizeResponse = await fetch(
976
+ `${BASE_URL}/api/cli/upload`,
977
+ finalizeRequest
978
+ );
979
+ const finalizeResult = await finalizeResponse.json().catch(() => null);
980
+ if (!finalizeResponse.ok || !finalizeResult?.success) {
923
981
  throw new Error(
924
- result?.error ?? `File upload failed (${response.status})`
982
+ finalizeResult?.error ?? `File finalize failed (${finalizeResponse.status})`
925
983
  );
926
984
  }
927
- return result;
985
+ return finalizeResult;
928
986
  }
929
987
  async function executeCommand(toolName, options) {
930
988
  const showSpinner = !options.json && process.stdout.isTTY;
@@ -940,6 +998,7 @@ async function executeCommand(toolName, options) {
940
998
  if (options.params) {
941
999
  try {
942
1000
  params = JSON.parse(options.params);
1001
+ params = normalizeToolParams(toolName, params);
943
1002
  } catch {
944
1003
  spinner?.stop();
945
1004
  spinner?.clear();
@@ -1369,40 +1428,1048 @@ For agent integration, always use the --json flag.
1369
1428
  `);
1370
1429
  }
1371
1430
 
1372
- // src/index.ts
1373
- var program = new Command();
1374
- var require2 = createRequire(import.meta.url);
1375
- var packageJson = require2("../package.json");
1376
- program.name("program").description("CLI for the Program video creation platform").version(packageJson.version);
1377
- var auth = program.command("auth").description("Authentication commands");
1378
- auth.command("login").description("Authenticate with the Program platform").option("--json", "Output as JSON").action(async (options) => {
1379
- await loginCommand(options);
1380
- });
1381
- auth.command("logout").description("Log out from the Program platform").option("--json", "Output as JSON").action((options) => {
1382
- logoutCommand(options);
1383
- });
1384
- auth.command("status").description("Check authentication status").option("--json", "Output as JSON").action((options) => {
1385
- statusCommand(options);
1386
- });
1387
- var project = program.command("project").description("Project management commands");
1388
- project.command("create").description("Create a new project").option("--title <title>", "Project title").option("--description <description>", "Project description").option("--json", "Output as JSON").action(async (options) => {
1389
- await createCommand(options);
1390
- });
1391
- project.command("list").description("List projects").option("--limit <limit>", "Maximum number of projects to return", parseInt).option("--json", "Output as JSON").action(async (options) => {
1392
- await listCommand(options);
1393
- });
1394
- project.command("get <id>").description("Get project details including composition").option("--json", "Output as JSON").action(async (id, options) => {
1395
- await getCommand(id, options);
1396
- });
1397
- var workflow = program.command("workflow").description("Workflow template commands");
1398
- workflow.command("list").description("List available workflow templates").option("--limit <limit>", "Maximum number of templates to return", parseInt).option("--public-only", "Only show public templates").option("--json", "Output as JSON").action(async (options) => {
1399
- await listCommand2(options);
1400
- });
1401
- workflow.command("run <templateId>").description("Execute a workflow template").option("--project <projectId>", "Project ID to associate with execution").option("--input <json>", "JSON input for the workflow").option("--json", "Output as JSON").action(async (templateId, options) => {
1402
- await runCommand(templateId, options);
1431
+ // src/commands/template/list.ts
1432
+ import chalk14 from "chalk";
1433
+ import ora12 from "ora";
1434
+ async function listCommand5(options) {
1435
+ const showSpinner = !options.json && process.stdout.isTTY;
1436
+ const spinner = showSpinner ? ora12("Fetching templates...").start() : null;
1437
+ const client = createConvexClient(convexConfig);
1438
+ const result = await client.query("templates.list", {
1439
+ category: options.publicOnly ? "public" : void 0
1440
+ });
1441
+ spinner?.stop();
1442
+ spinner?.clear();
1443
+ if (!result.success || !result.data) {
1444
+ outputError(result.error ?? "Failed to list templates", options);
1445
+ process.exit(1);
1446
+ }
1447
+ const templates = result.data;
1448
+ if (options.json) {
1449
+ outputSuccess(
1450
+ templates.map((t) => ({
1451
+ id: t._id,
1452
+ name: t.name,
1453
+ description: t.description,
1454
+ public: t.public,
1455
+ stepCount: Array.isArray(t.steps) ? t.steps.length : 0
1456
+ })),
1457
+ options
1458
+ );
1459
+ } else {
1460
+ if (templates.length === 0) {
1461
+ console.log(chalk14.dim("No templates found."));
1462
+ return;
1463
+ }
1464
+ console.log(
1465
+ `Found ${chalk14.cyan(templates.length)} template${templates.length === 1 ? "" : "s"}:
1466
+ `
1467
+ );
1468
+ const rows = templates.map((t) => [
1469
+ t._id,
1470
+ t.name,
1471
+ t.description?.substring(0, 40) ?? chalk14.dim("No description"),
1472
+ String(Array.isArray(t.steps) ? t.steps.length : 0),
1473
+ t.public ? chalk14.green("public") : chalk14.dim("private")
1474
+ ]);
1475
+ console.log(
1476
+ formatTable(["ID", "Name", "Description", "Steps", "Visibility"], rows)
1477
+ );
1478
+ }
1479
+ }
1480
+
1481
+ // src/commands/template/get.ts
1482
+ import chalk15 from "chalk";
1483
+ import ora13 from "ora";
1484
+
1485
+ // src/lib/context.ts
1486
+ import Conf2 from "conf";
1487
+ import * as fs3 from "fs";
1488
+ import * as path3 from "path";
1489
+ import * as os2 from "os";
1490
+ var CONTEXT_DIR = path3.join(os2.homedir(), ".program");
1491
+ function ensureContextDir() {
1492
+ if (!fs3.existsSync(CONTEXT_DIR)) {
1493
+ fs3.mkdirSync(CONTEXT_DIR, { mode: 448, recursive: true });
1494
+ }
1495
+ }
1496
+ var store2 = new Conf2({
1497
+ projectName: "program-cli",
1498
+ cwd: CONTEXT_DIR,
1499
+ configName: "context",
1500
+ fileExtension: "json",
1501
+ defaults: {
1502
+ activeTemplateId: null
1503
+ }
1403
1504
  });
1404
- workflow.command("status <executionId>").description("Check workflow execution status").option("--watch", "Continuously poll for status updates").option("--interval <ms>", "Poll interval in milliseconds", parseInt).option("--json", "Output as JSON").action(async (executionId, options) => {
1405
- await statusCommand2(executionId, options);
1505
+ function setActiveTemplate(id) {
1506
+ ensureContextDir();
1507
+ store2.set("activeTemplateId", id);
1508
+ }
1509
+ function getActiveTemplate() {
1510
+ return store2.get("activeTemplateId");
1511
+ }
1512
+ function clearActiveTemplate() {
1513
+ store2.set("activeTemplateId", null);
1514
+ }
1515
+
1516
+ // src/commands/template/get.ts
1517
+ async function getCommand2(id, options) {
1518
+ const templateId = id ?? getActiveTemplate();
1519
+ if (!templateId) {
1520
+ outputError(
1521
+ "No template ID provided and no active template set. Use 'template use <id>' to set one.",
1522
+ options
1523
+ );
1524
+ process.exit(1);
1525
+ }
1526
+ const showSpinner = !options.json && process.stdout.isTTY;
1527
+ const spinner = showSpinner ? ora13("Fetching template...").start() : null;
1528
+ const client = createConvexClient(convexConfig);
1529
+ const result = await client.query("templates.byId", {
1530
+ id: templateId
1531
+ });
1532
+ spinner?.stop();
1533
+ spinner?.clear();
1534
+ if (!result.success || !result.data) {
1535
+ outputError(result.error ?? "Template not found", options);
1536
+ process.exit(1);
1537
+ }
1538
+ const template2 = result.data;
1539
+ if (options.json) {
1540
+ outputSuccess(template2, options);
1541
+ } else {
1542
+ console.log(
1543
+ formatKeyValue([
1544
+ { key: "ID", value: template2._id },
1545
+ { key: "Name", value: template2.name },
1546
+ {
1547
+ key: "Description",
1548
+ value: template2.description ?? chalk15.dim("None")
1549
+ },
1550
+ {
1551
+ key: "Visibility",
1552
+ value: template2.public ? chalk15.green("public") : chalk15.dim("private")
1553
+ },
1554
+ {
1555
+ key: "Created",
1556
+ value: new Date(template2.createdAt).toLocaleString()
1557
+ },
1558
+ {
1559
+ key: "Updated",
1560
+ value: new Date(template2.updatedAt).toLocaleString()
1561
+ }
1562
+ ])
1563
+ );
1564
+ const steps = template2.steps ?? [];
1565
+ if (steps.length === 0) {
1566
+ console.log(chalk15.dim("\nNo steps configured."));
1567
+ } else {
1568
+ console.log(`
1569
+ ${chalk15.bold("Steps")} (${steps.length}):
1570
+ `);
1571
+ const rows = steps.map((s, i) => [
1572
+ String(i),
1573
+ s.id ?? chalk15.dim("\u2014"),
1574
+ s.stepId,
1575
+ summarizeConfig(s.config)
1576
+ ]);
1577
+ console.log(formatTable(["#", "ID", "Type", "Config"], rows));
1578
+ }
1579
+ }
1580
+ }
1581
+ function summarizeConfig(config) {
1582
+ const keys = Object.keys(config);
1583
+ if (keys.length === 0) return chalk15.dim("{}");
1584
+ const summary = keys.slice(0, 3).map((k) => {
1585
+ const v = config[k];
1586
+ const vs = typeof v === "string" ? v.length > 20 ? v.substring(0, 20) + "..." : v : JSON.stringify(v);
1587
+ return `${k}=${vs}`;
1588
+ }).join(", ");
1589
+ return keys.length > 3 ? summary + `, +${keys.length - 3} more` : summary;
1590
+ }
1591
+
1592
+ // src/commands/template/create.ts
1593
+ import chalk16 from "chalk";
1594
+ import ora14 from "ora";
1595
+ async function createCommand2(options) {
1596
+ if (!options.name) {
1597
+ outputError("--name is required", options);
1598
+ process.exit(1);
1599
+ }
1600
+ const showSpinner = !options.json && process.stdout.isTTY;
1601
+ const spinner = showSpinner ? ora14("Creating template...").start() : null;
1602
+ const client = createConvexClient(convexConfig);
1603
+ const result = await client.mutation("templates.create", {
1604
+ name: options.name,
1605
+ description: options.description,
1606
+ public: options.public ?? false
1607
+ });
1608
+ spinner?.stop();
1609
+ spinner?.clear();
1610
+ if (!result.success || !result.data) {
1611
+ outputError(result.error ?? "Failed to create template", options);
1612
+ process.exit(1);
1613
+ }
1614
+ const templateId = result.data;
1615
+ setActiveTemplate(templateId);
1616
+ if (options.json) {
1617
+ outputSuccess({ id: templateId, active: true }, options);
1618
+ } else {
1619
+ console.log(formatSuccess("Template created (now active)"));
1620
+ console.log(chalk16.dim(` ID: ${templateId}`));
1621
+ }
1622
+ }
1623
+
1624
+ // src/commands/template/update.ts
1625
+ import ora15 from "ora";
1626
+ async function updateCommand(id, options) {
1627
+ const templateId = id ?? getActiveTemplate();
1628
+ if (!templateId) {
1629
+ outputError(
1630
+ "No template ID provided and no active template set. Use 'template use <id>' to set one.",
1631
+ options
1632
+ );
1633
+ process.exit(1);
1634
+ }
1635
+ const showSpinner = !options.json && process.stdout.isTTY;
1636
+ const spinner = showSpinner ? ora15("Updating template...").start() : null;
1637
+ const client = createConvexClient(convexConfig);
1638
+ const params = { id: templateId };
1639
+ if (options.name !== void 0) params.name = options.name;
1640
+ if (options.description !== void 0) params.description = options.description;
1641
+ if (options.public !== void 0) params.public = options.public;
1642
+ const result = await client.mutation("templates.update", params);
1643
+ spinner?.stop();
1644
+ spinner?.clear();
1645
+ if (!result.success) {
1646
+ outputError(result.error ?? "Failed to update template", options);
1647
+ process.exit(1);
1648
+ }
1649
+ if (options.json) {
1650
+ outputSuccess({ id: templateId, updated: true }, options);
1651
+ } else {
1652
+ console.log(formatSuccess("Template updated"));
1653
+ }
1654
+ }
1655
+
1656
+ // src/commands/template/remove.ts
1657
+ import chalk17 from "chalk";
1658
+ import ora16 from "ora";
1659
+ async function removeCommand(id, options) {
1660
+ const templateId = id ?? getActiveTemplate();
1661
+ if (!templateId) {
1662
+ outputError(
1663
+ "No template ID provided and no active template set. Use 'template use <id>' to set one.",
1664
+ options
1665
+ );
1666
+ process.exit(1);
1667
+ }
1668
+ if (!options.force && !options.json) {
1669
+ const readline = await import("readline");
1670
+ const rl = readline.createInterface({
1671
+ input: process.stdin,
1672
+ output: process.stdout
1673
+ });
1674
+ const answer = await new Promise((resolve) => {
1675
+ rl.question(
1676
+ chalk17.yellow(`Are you sure you want to delete template ${templateId}? (y/N) `),
1677
+ resolve
1678
+ );
1679
+ });
1680
+ rl.close();
1681
+ if (answer.toLowerCase() !== "y") {
1682
+ console.log(chalk17.dim("Cancelled."));
1683
+ return;
1684
+ }
1685
+ }
1686
+ const showSpinner = !options.json && process.stdout.isTTY;
1687
+ const spinner = showSpinner ? ora16("Deleting template...").start() : null;
1688
+ const client = createConvexClient(convexConfig);
1689
+ const result = await client.mutation("templates.remove", {
1690
+ id: templateId
1691
+ });
1692
+ spinner?.stop();
1693
+ spinner?.clear();
1694
+ if (!result.success) {
1695
+ outputError(result.error ?? "Failed to delete template", options);
1696
+ process.exit(1);
1697
+ }
1698
+ if (getActiveTemplate() === templateId) {
1699
+ clearActiveTemplate();
1700
+ }
1701
+ if (options.json) {
1702
+ outputSuccess({ id: templateId, deleted: true }, options);
1703
+ } else {
1704
+ console.log(formatSuccess("Template deleted"));
1705
+ }
1706
+ }
1707
+
1708
+ // src/commands/template/run.ts
1709
+ import chalk18 from "chalk";
1710
+ import ora17 from "ora";
1711
+ async function runCommand2(id, options) {
1712
+ const templateId = id ?? getActiveTemplate();
1713
+ if (!templateId) {
1714
+ outputError(
1715
+ "No template ID provided and no active template set. Use 'template use <id>' to set one.",
1716
+ options
1717
+ );
1718
+ process.exit(1);
1719
+ }
1720
+ const showSpinner = !options.json && process.stdout.isTTY;
1721
+ const spinner = showSpinner ? ora17("Starting workflow execution...").start() : null;
1722
+ let input;
1723
+ if (options.input) {
1724
+ try {
1725
+ input = JSON.parse(options.input);
1726
+ } catch {
1727
+ spinner?.stop();
1728
+ spinner?.clear();
1729
+ outputError("Invalid JSON in --input parameter", options);
1730
+ process.exit(1);
1731
+ }
1732
+ }
1733
+ const client = createConvexClient(convexConfig);
1734
+ const result = await client.mutation("workflows.run", {
1735
+ templateId,
1736
+ projectId: options.project,
1737
+ input
1738
+ });
1739
+ spinner?.stop();
1740
+ spinner?.clear();
1741
+ if (!result.success || !result.data) {
1742
+ outputError(result.error ?? "Failed to start workflow", options);
1743
+ process.exit(1);
1744
+ }
1745
+ const executionId = result.data;
1746
+ if (options.json) {
1747
+ outputSuccess({ executionId }, options);
1748
+ } else {
1749
+ console.log(formatSuccess("Workflow started"));
1750
+ console.log(chalk18.dim(` Execution ID: ${executionId}`));
1751
+ console.log(
1752
+ chalk18.dim(` Check status with: program template status ${executionId}`)
1753
+ );
1754
+ }
1755
+ }
1756
+
1757
+ // src/commands/template/status.ts
1758
+ import chalk19 from "chalk";
1759
+ import ora18 from "ora";
1760
+ async function statusCommand3(executionId, options) {
1761
+ const client = createConvexClient(convexConfig);
1762
+ const pollInterval = options.interval ?? 2e3;
1763
+ if (options.watch) {
1764
+ await watchStatus2(client, executionId, pollInterval, options);
1765
+ } else {
1766
+ await getStatusOnce2(client, executionId, options);
1767
+ }
1768
+ }
1769
+ async function getStatusOnce2(client, executionId, options) {
1770
+ const showSpinner = !options.json && process.stdout.isTTY;
1771
+ const spinner = showSpinner ? ora18("Fetching execution status...").start() : null;
1772
+ const result = await client.query("workflows.status", {
1773
+ executionId
1774
+ });
1775
+ spinner?.stop();
1776
+ spinner?.clear();
1777
+ if (!result.success) {
1778
+ outputError(result.error ?? "Failed to get status", options);
1779
+ process.exit(1);
1780
+ }
1781
+ const status = result.data;
1782
+ if (!status) {
1783
+ outputError("Execution not found", options);
1784
+ process.exit(1);
1785
+ }
1786
+ if (options.json) {
1787
+ outputSuccess(status, options);
1788
+ } else {
1789
+ printStatus2(status);
1790
+ }
1791
+ }
1792
+ async function watchStatus2(client, executionId, pollInterval, options) {
1793
+ let lastStatus = null;
1794
+ const poll = async () => {
1795
+ const result = await client.query("workflows.status", {
1796
+ executionId
1797
+ });
1798
+ if (!result.success) {
1799
+ outputError(result.error ?? "Failed to get status", options);
1800
+ return true;
1801
+ }
1802
+ const status = result.data;
1803
+ if (!status) {
1804
+ outputError("Execution not found", options);
1805
+ return true;
1806
+ }
1807
+ if (options.json) {
1808
+ if (status.status !== lastStatus) {
1809
+ console.log(JSON.stringify(status));
1810
+ lastStatus = status.status;
1811
+ }
1812
+ } else {
1813
+ if (status.status !== lastStatus) {
1814
+ console.clear();
1815
+ console.log(chalk19.dim(`Watching execution ${executionId}...
1816
+ `));
1817
+ printStatus2(status);
1818
+ lastStatus = status.status;
1819
+ }
1820
+ }
1821
+ if (["success", "error", "cancelled"].includes(status.status)) {
1822
+ return true;
1823
+ }
1824
+ return false;
1825
+ };
1826
+ let done = await poll();
1827
+ while (!done) {
1828
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1829
+ done = await poll();
1830
+ }
1831
+ }
1832
+ function printStatus2(status) {
1833
+ const statusColor = getStatusColor2(status.status);
1834
+ console.log(
1835
+ formatKeyValue([
1836
+ { key: "Status", value: statusColor(status.status) },
1837
+ ...status.completedAt ? [
1838
+ {
1839
+ key: "Completed",
1840
+ value: new Date(status.completedAt).toLocaleString()
1841
+ }
1842
+ ] : []
1843
+ ])
1844
+ );
1845
+ if (status.nodeStatuses && status.nodeStatuses.length > 0) {
1846
+ console.log(chalk19.dim("\nStep progress:"));
1847
+ for (const node of status.nodeStatuses) {
1848
+ const nodeColor = getStatusColor2(node.status);
1849
+ console.log(` ${nodeColor(getStatusIcon2(node.status))} ${node.nodeId}`);
1850
+ }
1851
+ }
1852
+ if (status.error) {
1853
+ console.log(formatWarning(`
1854
+ Error: ${status.error}`));
1855
+ }
1856
+ if (status.status === "success" && status.output) {
1857
+ console.log(formatSuccess("\nOutput:"));
1858
+ console.log(JSON.stringify(status.output, null, 2));
1859
+ }
1860
+ }
1861
+ function getStatusColor2(status) {
1862
+ switch (status) {
1863
+ case "success":
1864
+ return chalk19.green;
1865
+ case "error":
1866
+ return chalk19.red;
1867
+ case "cancelled":
1868
+ return chalk19.yellow;
1869
+ case "running":
1870
+ return chalk19.blue;
1871
+ case "pending":
1872
+ default:
1873
+ return chalk19.dim;
1874
+ }
1875
+ }
1876
+ function getStatusIcon2(status) {
1877
+ switch (status) {
1878
+ case "success":
1879
+ return "\u2713";
1880
+ case "error":
1881
+ return "\u2715";
1882
+ case "running":
1883
+ return "\u25CF";
1884
+ case "pending":
1885
+ default:
1886
+ return "\u25CB";
1887
+ }
1888
+ }
1889
+
1890
+ // src/commands/template/use.ts
1891
+ import chalk20 from "chalk";
1892
+ import ora19 from "ora";
1893
+ async function useCommand(id, options) {
1894
+ const showSpinner = !options.json && process.stdout.isTTY;
1895
+ const spinner = showSpinner ? ora19("Verifying template...").start() : null;
1896
+ const client = createConvexClient(convexConfig);
1897
+ const result = await client.query("templates.byId", { id });
1898
+ spinner?.stop();
1899
+ spinner?.clear();
1900
+ if (!result.success || !result.data) {
1901
+ outputError(result.error ?? "Template not found or not accessible", options);
1902
+ process.exit(1);
1903
+ }
1904
+ setActiveTemplate(id);
1905
+ if (options.json) {
1906
+ outputSuccess({ id, name: result.data.name, active: true }, options);
1907
+ } else {
1908
+ console.log(formatSuccess(`Active template set to "${result.data.name}"`));
1909
+ console.log(chalk20.dim(` ID: ${id}`));
1910
+ }
1911
+ }
1912
+
1913
+ // src/commands/template/step/list.ts
1914
+ import chalk21 from "chalk";
1915
+ import ora20 from "ora";
1916
+ async function stepListCommand(options) {
1917
+ const showSpinner = !options.json && process.stdout.isTTY;
1918
+ const spinner = showSpinner ? ora20("Fetching step types...").start() : null;
1919
+ const client = createConvexClient(convexConfig);
1920
+ const result = await client.query("templates.steps", {
1921
+ category: options.category
1922
+ });
1923
+ spinner?.stop();
1924
+ spinner?.clear();
1925
+ if (!result.success || !result.data) {
1926
+ outputError(result.error ?? "Failed to list step types", options);
1927
+ process.exit(1);
1928
+ }
1929
+ const steps = result.data;
1930
+ if (options.json) {
1931
+ outputSuccess(steps, options);
1932
+ } else {
1933
+ if (steps.length === 0) {
1934
+ console.log(chalk21.dim("No step types found."));
1935
+ return;
1936
+ }
1937
+ console.log(
1938
+ `Found ${chalk21.cyan(steps.length)} step type${steps.length === 1 ? "" : "s"}:
1939
+ `
1940
+ );
1941
+ const rows = steps.map((s) => [
1942
+ s.id,
1943
+ s.name,
1944
+ s.category,
1945
+ s.isPrimitive ? chalk21.yellow("primitive") : ""
1946
+ ]);
1947
+ console.log(formatTable(["Type", "Name", "Category", ""], rows));
1948
+ }
1949
+ }
1950
+
1951
+ // src/commands/template/step/info.ts
1952
+ import chalk22 from "chalk";
1953
+ import ora21 from "ora";
1954
+ async function stepInfoCommand(stepType, options) {
1955
+ const showSpinner = !options.json && process.stdout.isTTY;
1956
+ const spinner = showSpinner ? ora21("Fetching step info...").start() : null;
1957
+ const client = createConvexClient(convexConfig);
1958
+ const result = await client.query("templates.stepInfo", {
1959
+ stepId: stepType
1960
+ });
1961
+ spinner?.stop();
1962
+ spinner?.clear();
1963
+ if (!result.success) {
1964
+ outputError(result.error ?? "Failed to get step info", options);
1965
+ process.exit(1);
1966
+ }
1967
+ if (!result.data) {
1968
+ outputError(`Step type "${stepType}" not found`, options);
1969
+ process.exit(1);
1970
+ }
1971
+ const step2 = result.data;
1972
+ if (options.json) {
1973
+ outputSuccess(step2, options);
1974
+ } else {
1975
+ console.log(
1976
+ formatKeyValue([
1977
+ { key: "Type", value: step2.id },
1978
+ { key: "Name", value: step2.name },
1979
+ { key: "Category", value: step2.category },
1980
+ { key: "Description", value: step2.description },
1981
+ {
1982
+ key: "Primitive",
1983
+ value: step2.isPrimitive ? chalk22.yellow("yes") : "no"
1984
+ }
1985
+ ])
1986
+ );
1987
+ const visibleInputs = step2.inputs.filter((i) => !i.hidden);
1988
+ if (visibleInputs.length > 0) {
1989
+ console.log(`
1990
+ ${chalk22.bold("Inputs")}:
1991
+ `);
1992
+ const inputRows = visibleInputs.map((i) => [
1993
+ i.key,
1994
+ i.type,
1995
+ i.required ? chalk22.green("yes") : chalk22.dim("no"),
1996
+ i.description
1997
+ ]);
1998
+ console.log(
1999
+ formatTable(["Key", "Type", "Required", "Description"], inputRows)
2000
+ );
2001
+ }
2002
+ if (step2.outputs.length > 0) {
2003
+ console.log(`
2004
+ ${chalk22.bold("Outputs")}:
2005
+ `);
2006
+ const outputRows = step2.outputs.map((o) => [
2007
+ o.key,
2008
+ o.type,
2009
+ o.description
2010
+ ]);
2011
+ console.log(formatTable(["Key", "Type", "Description"], outputRows));
2012
+ }
2013
+ }
2014
+ }
2015
+
2016
+ // src/commands/template/step/add.ts
2017
+ import chalk23 from "chalk";
2018
+ import ora22 from "ora";
2019
+ import crypto from "crypto";
2020
+ async function stepAddCommand(stepType, options) {
2021
+ const templateId = options.template ?? getActiveTemplate();
2022
+ if (!templateId) {
2023
+ outputError(
2024
+ "No template ID. Use --template <id> or set active template with 'template use <id>'.",
2025
+ options
2026
+ );
2027
+ process.exit(1);
2028
+ }
2029
+ let config = {};
2030
+ if (options.config) {
2031
+ try {
2032
+ config = JSON.parse(options.config);
2033
+ } catch {
2034
+ outputError("Invalid JSON in --config parameter", options);
2035
+ process.exit(1);
2036
+ }
2037
+ }
2038
+ const showSpinner = !options.json && process.stdout.isTTY;
2039
+ const spinner = showSpinner ? ora22("Adding step...").start() : null;
2040
+ const client = createConvexClient(convexConfig);
2041
+ const getResult = await client.query("templates.byId", {
2042
+ id: templateId
2043
+ });
2044
+ if (!getResult.success || !getResult.data) {
2045
+ spinner?.stop();
2046
+ spinner?.clear();
2047
+ outputError(getResult.error ?? "Template not found", options);
2048
+ process.exit(1);
2049
+ }
2050
+ const steps = (getResult.data.steps ?? []).map((s) => ({ ...s }));
2051
+ const newId = crypto.randomUUID().slice(0, 8);
2052
+ let insertAt;
2053
+ if (options.at !== void 0) {
2054
+ insertAt = Math.max(0, Math.min(options.at, steps.length));
2055
+ } else if (options.after) {
2056
+ const afterIndex = steps.findIndex((s) => s.id === options.after);
2057
+ if (afterIndex === -1) {
2058
+ spinner?.stop();
2059
+ spinner?.clear();
2060
+ outputError(`Step with id "${options.after}" not found`, options);
2061
+ process.exit(1);
2062
+ }
2063
+ insertAt = afterIndex + 1;
2064
+ } else {
2065
+ insertAt = steps.length;
2066
+ }
2067
+ const newStep = {
2068
+ id: newId,
2069
+ stepId: stepType,
2070
+ config,
2071
+ order: insertAt
2072
+ };
2073
+ steps.splice(insertAt, 0, newStep);
2074
+ for (let i = 0; i < steps.length; i++) {
2075
+ const s = steps[i];
2076
+ if (s) s.order = i;
2077
+ }
2078
+ const updateResult = await client.mutation("templates.update", {
2079
+ id: templateId,
2080
+ steps
2081
+ });
2082
+ spinner?.stop();
2083
+ spinner?.clear();
2084
+ if (!updateResult.success) {
2085
+ outputError(updateResult.error ?? "Failed to update template", options);
2086
+ process.exit(1);
2087
+ }
2088
+ if (options.json) {
2089
+ outputSuccess(
2090
+ { id: newId, stepId: stepType, order: insertAt, templateId },
2091
+ options
2092
+ );
2093
+ } else {
2094
+ console.log(formatSuccess(`Step added: ${stepType}`));
2095
+ console.log(chalk23.dim(` Step ID: ${newId}`));
2096
+ console.log(chalk23.dim(` Position: ${insertAt}`));
2097
+ }
2098
+ }
2099
+
2100
+ // src/commands/template/step/remove.ts
2101
+ import ora23 from "ora";
2102
+ async function stepRemoveCommand(stepInstanceId, options) {
2103
+ const templateId = options.template ?? getActiveTemplate();
2104
+ if (!templateId) {
2105
+ outputError(
2106
+ "No template ID. Use --template <id> or set active template with 'template use <id>'.",
2107
+ options
2108
+ );
2109
+ process.exit(1);
2110
+ }
2111
+ const showSpinner = !options.json && process.stdout.isTTY;
2112
+ const spinner = showSpinner ? ora23("Removing step...").start() : null;
2113
+ const client = createConvexClient(convexConfig);
2114
+ const getResult = await client.query("templates.byId", {
2115
+ id: templateId
2116
+ });
2117
+ if (!getResult.success || !getResult.data) {
2118
+ spinner?.stop();
2119
+ spinner?.clear();
2120
+ outputError(getResult.error ?? "Template not found", options);
2121
+ process.exit(1);
2122
+ }
2123
+ const steps = getResult.data.steps ?? [];
2124
+ const removeIndex = steps.findIndex((s) => s.id === stepInstanceId);
2125
+ if (removeIndex === -1) {
2126
+ spinner?.stop();
2127
+ spinner?.clear();
2128
+ outputError(`Step with id "${stepInstanceId}" not found in template`, options);
2129
+ process.exit(1);
2130
+ }
2131
+ const removed = steps[removeIndex];
2132
+ if (!removed) {
2133
+ spinner?.stop();
2134
+ spinner?.clear();
2135
+ outputError(`Step at index ${removeIndex} not found`, options);
2136
+ process.exit(1);
2137
+ }
2138
+ const newSteps = steps.filter((_, i) => i !== removeIndex);
2139
+ for (let i = 0; i < newSteps.length; i++) {
2140
+ const s = newSteps[i];
2141
+ if (s) s.order = i;
2142
+ }
2143
+ const updateResult = await client.mutation("templates.update", {
2144
+ id: templateId,
2145
+ steps: newSteps
2146
+ });
2147
+ spinner?.stop();
2148
+ spinner?.clear();
2149
+ if (!updateResult.success) {
2150
+ outputError(updateResult.error ?? "Failed to update template", options);
2151
+ process.exit(1);
2152
+ }
2153
+ if (options.json) {
2154
+ outputSuccess(
2155
+ { removed: stepInstanceId, stepId: removed.stepId, templateId },
2156
+ options
2157
+ );
2158
+ } else {
2159
+ console.log(formatSuccess(`Removed step: ${removed.stepId} (${stepInstanceId})`));
2160
+ }
2161
+ }
2162
+
2163
+ // src/commands/template/step/update.ts
2164
+ import ora24 from "ora";
2165
+ async function stepUpdateCommand(stepInstanceId, options) {
2166
+ const templateId = options.template ?? getActiveTemplate();
2167
+ if (!templateId) {
2168
+ outputError(
2169
+ "No template ID. Use --template <id> or set active template with 'template use <id>'.",
2170
+ options
2171
+ );
2172
+ process.exit(1);
2173
+ }
2174
+ if (!options.config) {
2175
+ outputError("--config is required", options);
2176
+ process.exit(1);
2177
+ }
2178
+ let configPatch;
2179
+ try {
2180
+ configPatch = JSON.parse(options.config);
2181
+ } catch {
2182
+ outputError("Invalid JSON in --config parameter", options);
2183
+ process.exit(1);
2184
+ }
2185
+ const showSpinner = !options.json && process.stdout.isTTY;
2186
+ const spinner = showSpinner ? ora24("Updating step...").start() : null;
2187
+ const client = createConvexClient(convexConfig);
2188
+ const getResult = await client.query("templates.byId", {
2189
+ id: templateId
2190
+ });
2191
+ if (!getResult.success || !getResult.data) {
2192
+ spinner?.stop();
2193
+ spinner?.clear();
2194
+ outputError(getResult.error ?? "Template not found", options);
2195
+ process.exit(1);
2196
+ }
2197
+ const steps = (getResult.data.steps ?? []).map((s) => ({ ...s }));
2198
+ const stepIndex = steps.findIndex((s) => s.id === stepInstanceId);
2199
+ if (stepIndex === -1) {
2200
+ spinner?.stop();
2201
+ spinner?.clear();
2202
+ outputError(`Step with id "${stepInstanceId}" not found in template`, options);
2203
+ process.exit(1);
2204
+ }
2205
+ const targetStep = steps[stepIndex];
2206
+ if (!targetStep) {
2207
+ spinner?.stop();
2208
+ spinner?.clear();
2209
+ outputError(`Step at index ${stepIndex} not found`, options);
2210
+ process.exit(1);
2211
+ }
2212
+ targetStep.config = {
2213
+ ...targetStep.config,
2214
+ ...configPatch
2215
+ };
2216
+ const updateResult = await client.mutation("templates.update", {
2217
+ id: templateId,
2218
+ steps
2219
+ });
2220
+ spinner?.stop();
2221
+ spinner?.clear();
2222
+ if (!updateResult.success) {
2223
+ outputError(updateResult.error ?? "Failed to update template", options);
2224
+ process.exit(1);
2225
+ }
2226
+ if (options.json) {
2227
+ outputSuccess(
2228
+ {
2229
+ id: stepInstanceId,
2230
+ stepId: targetStep.stepId,
2231
+ config: targetStep.config,
2232
+ templateId
2233
+ },
2234
+ options
2235
+ );
2236
+ } else {
2237
+ console.log(
2238
+ formatSuccess(
2239
+ `Updated step: ${targetStep.stepId} (${stepInstanceId})`
2240
+ )
2241
+ );
2242
+ }
2243
+ }
2244
+
2245
+ // src/commands/template/model/list.ts
2246
+ import chalk24 from "chalk";
2247
+ import ora25 from "ora";
2248
+ async function modelListCommand(category, options) {
2249
+ const showSpinner = !options.json && process.stdout.isTTY;
2250
+ const spinner = showSpinner ? ora25("Fetching models...").start() : null;
2251
+ const client = createConvexClient(convexConfig);
2252
+ const result = await client.query("models.list", { category });
2253
+ spinner?.stop();
2254
+ spinner?.clear();
2255
+ if (!result.success || !result.data) {
2256
+ outputError(result.error ?? "Failed to list models", options);
2257
+ process.exit(1);
2258
+ }
2259
+ const models = result.data;
2260
+ if (options.json) {
2261
+ outputSuccess(
2262
+ models.map((m) => ({
2263
+ modelId: m.modelId,
2264
+ name: m.displayName ?? m.modelName,
2265
+ provider: m.provider,
2266
+ description: m.description
2267
+ })),
2268
+ options
2269
+ );
2270
+ } else {
2271
+ if (models.length === 0) {
2272
+ console.log(chalk24.dim(`No ${category} models found.`));
2273
+ return;
2274
+ }
2275
+ console.log(
2276
+ `Found ${chalk24.cyan(models.length)} ${category} model${models.length === 1 ? "" : "s"}:
2277
+ `
2278
+ );
2279
+ const rows = models.map((m) => [
2280
+ m.modelId,
2281
+ m.displayName ?? m.modelName,
2282
+ m.provider
2283
+ ]);
2284
+ console.log(formatTable(["Model ID", "Name", "Provider"], rows));
2285
+ }
2286
+ }
2287
+
2288
+ // src/commands/template/model/schema.ts
2289
+ import chalk25 from "chalk";
2290
+ import ora26 from "ora";
2291
+ async function modelSchemaCommand(modelId, options) {
2292
+ const showSpinner = !options.json && process.stdout.isTTY;
2293
+ const spinner = showSpinner ? ora26("Fetching model schema...").start() : null;
2294
+ const client = createConvexClient(convexConfig);
2295
+ const result = await client.query("models.byModelId", {
2296
+ modelId
2297
+ });
2298
+ spinner?.stop();
2299
+ spinner?.clear();
2300
+ if (!result.success) {
2301
+ outputError(result.error ?? "Failed to get model", options);
2302
+ process.exit(1);
2303
+ }
2304
+ if (!result.data) {
2305
+ outputError(`Model "${modelId}" not found`, options);
2306
+ process.exit(1);
2307
+ }
2308
+ const model2 = result.data;
2309
+ if (options.json) {
2310
+ outputSuccess(
2311
+ {
2312
+ modelId: model2.modelId,
2313
+ name: model2.displayName ?? model2.modelName,
2314
+ provider: model2.provider,
2315
+ category: model2.category,
2316
+ description: model2.description,
2317
+ inputSchema: model2.inputSchema
2318
+ },
2319
+ options
2320
+ );
2321
+ } else {
2322
+ console.log(
2323
+ formatKeyValue([
2324
+ { key: "Model ID", value: model2.modelId },
2325
+ { key: "Name", value: model2.displayName ?? model2.modelName },
2326
+ { key: "Provider", value: model2.provider },
2327
+ { key: "Category", value: model2.category },
2328
+ {
2329
+ key: "Description",
2330
+ value: model2.description ?? chalk25.dim("None")
2331
+ }
2332
+ ])
2333
+ );
2334
+ const schema = model2.inputSchema;
2335
+ if (!schema?.properties) {
2336
+ console.log(chalk25.dim("\nNo input schema available."));
2337
+ return;
2338
+ }
2339
+ const required = new Set(schema.required ?? []);
2340
+ const props = schema.properties;
2341
+ console.log(`
2342
+ ${chalk25.bold("Input Parameters")}:
2343
+ `);
2344
+ const rows = Object.entries(props).map(([key, prop]) => [
2345
+ key,
2346
+ prop.type ?? "unknown",
2347
+ required.has(key) ? chalk25.green("yes") : chalk25.dim("no"),
2348
+ formatDefault(prop.default),
2349
+ formatConstraints(prop)
2350
+ ]);
2351
+ console.log(
2352
+ formatTable(
2353
+ ["Parameter", "Type", "Required", "Default", "Constraints"],
2354
+ rows
2355
+ )
2356
+ );
2357
+ const withDescriptions = Object.entries(props).filter(
2358
+ ([, p]) => p.description
2359
+ );
2360
+ if (withDescriptions.length > 0) {
2361
+ console.log(`
2362
+ ${chalk25.bold("Parameter Details")}:
2363
+ `);
2364
+ for (const [key, prop] of withDescriptions) {
2365
+ console.log(` ${chalk25.cyan(key)}: ${prop.description}`);
2366
+ if (prop.enum) {
2367
+ console.log(
2368
+ ` Values: ${prop.enum.map((v) => chalk25.green(String(v))).join(", ")}`
2369
+ );
2370
+ }
2371
+ }
2372
+ }
2373
+ }
2374
+ }
2375
+ function formatDefault(value) {
2376
+ if (value === void 0 || value === null) return chalk25.dim("\u2014");
2377
+ if (typeof value === "object") return JSON.stringify(value);
2378
+ return String(value);
2379
+ }
2380
+ function formatConstraints(prop) {
2381
+ const parts = [];
2382
+ if (prop.enum) {
2383
+ const enumStr = prop.enum.map(String).join("|");
2384
+ parts.push(enumStr.length > 40 ? enumStr.substring(0, 37) + "..." : enumStr);
2385
+ }
2386
+ if (prop.minimum !== void 0) parts.push(`min:${prop.minimum}`);
2387
+ if (prop.maximum !== void 0) parts.push(`max:${prop.maximum}`);
2388
+ return parts.length > 0 ? parts.join(", ") : chalk25.dim("\u2014");
2389
+ }
2390
+
2391
+ // src/index.ts
2392
+ var program = new Command();
2393
+ var require2 = createRequire(import.meta.url);
2394
+ var packageJson = require2("../package.json");
2395
+ program.name("program").description("CLI for the Program video creation platform").version(packageJson.version);
2396
+ var auth = program.command("auth").description("Authentication commands");
2397
+ auth.command("login").description("Authenticate with the Program platform").option("--json", "Output as JSON").action(async (options) => {
2398
+ await loginCommand(options);
2399
+ });
2400
+ auth.command("logout").description("Log out from the Program platform").option("--json", "Output as JSON").action((options) => {
2401
+ logoutCommand(options);
2402
+ });
2403
+ auth.command("status").description("Check authentication status").option("--json", "Output as JSON").action((options) => {
2404
+ statusCommand(options);
2405
+ });
2406
+ var project = program.command("project").description("Project management commands");
2407
+ project.command("create").description("Create a new project").option("--title <title>", "Project title").option("--description <description>", "Project description").option("--json", "Output as JSON").action(async (options) => {
2408
+ await createCommand(options);
2409
+ });
2410
+ project.command("list").description("List projects").option("--limit <limit>", "Maximum number of projects to return", parseInt).option("--json", "Output as JSON").action(async (options) => {
2411
+ await listCommand(options);
2412
+ });
2413
+ project.command("get <id>").description("Get project details including composition").option("--json", "Output as JSON").action(async (id, options) => {
2414
+ await getCommand(id, options);
2415
+ });
2416
+ var workflow = program.command("workflow").description("Workflow template commands (use 'template' for full management)");
2417
+ workflow.command("list").description("List available workflow templates").option("--limit <limit>", "Maximum number of templates to return", parseInt).option("--public-only", "Only show public templates").option("--json", "Output as JSON").action(async (options) => {
2418
+ await listCommand2(options);
2419
+ });
2420
+ workflow.command("run <templateId>").description("Execute a workflow template").option("--project <projectId>", "Project ID to associate with execution").option("--input <json>", "JSON input for the workflow").option("--json", "Output as JSON").action(async (templateId, options) => {
2421
+ await runCommand(templateId, options);
2422
+ });
2423
+ workflow.command("status <executionId>").description("Check workflow execution status").option("--watch", "Continuously poll for status updates").option("--interval <ms>", "Poll interval in milliseconds", parseInt).option("--json", "Output as JSON").action(async (executionId, options) => {
2424
+ await statusCommand2(executionId, options);
2425
+ });
2426
+ var template = program.command("template").description("Template management commands");
2427
+ template.command("list").description("List templates").option("--public-only", "Only show public templates").option("--json", "Output as JSON").action(async (options) => {
2428
+ await listCommand5(options);
2429
+ });
2430
+ template.command("get [id]").description("Get template details (uses active template if no ID)").option("--json", "Output as JSON").action(async (id, options) => {
2431
+ await getCommand2(id, options);
2432
+ });
2433
+ template.command("create").description("Create a new template (auto-sets as active)").requiredOption("--name <name>", "Template name").option("--description <description>", "Template description").option("--public", "Make template public").option("--json", "Output as JSON").action(async (options) => {
2434
+ await createCommand2(options);
2435
+ });
2436
+ template.command("update [id]").description("Update template metadata (uses active template if no ID)").option("--name <name>", "New template name").option("--description <description>", "New template description").option("--public", "Make template public").option("--json", "Output as JSON").action(async (id, options) => {
2437
+ await updateCommand(id, options);
2438
+ });
2439
+ template.command("remove [id]").description("Delete a template (uses active template if no ID)").option("--force", "Skip confirmation prompt").option("--json", "Output as JSON").action(async (id, options) => {
2440
+ await removeCommand(id, options);
2441
+ });
2442
+ template.command("run [id]").description("Execute a template (uses active template if no ID)").option("--project <projectId>", "Project ID to associate with execution").option("--input <json>", "JSON input for the workflow").option("--json", "Output as JSON").action(async (id, options) => {
2443
+ await runCommand2(id, options);
2444
+ });
2445
+ template.command("status <executionId>").description("Check execution status").option("--watch", "Continuously poll for status updates").option("--interval <ms>", "Poll interval in milliseconds", parseInt).option("--json", "Output as JSON").action(async (executionId, options) => {
2446
+ await statusCommand3(executionId, options);
2447
+ });
2448
+ template.command("use <id>").description("Set the active template context").option("--json", "Output as JSON").action(async (id, options) => {
2449
+ await useCommand(id, options);
2450
+ });
2451
+ var step = template.command("step").description("Manage template steps and discover step types");
2452
+ step.command("list").description("List available step types").option("--category <category>", "Filter by category").option("--json", "Output as JSON").action(async (options) => {
2453
+ await stepListCommand(options);
2454
+ });
2455
+ step.command("info <stepType>").description("Show step type details (inputs, outputs)").option("--json", "Output as JSON").action(async (stepType, options) => {
2456
+ await stepInfoCommand(stepType, options);
2457
+ });
2458
+ step.command("add <stepType>").description("Add a step to the active template").option("--config <json>", "Step configuration as JSON").option("--at <position>", "Insert at position (0-indexed)", parseInt).option("--after <id>", "Insert after step with this ID").option("--template <id>", "Template ID (overrides active template)").option("--json", "Output as JSON").action(async (stepType, options) => {
2459
+ await stepAddCommand(stepType, options);
2460
+ });
2461
+ step.command("remove <id>").description("Remove a step by its instance ID").option("--template <id>", "Template ID (overrides active template)").option("--json", "Output as JSON").action(async (id, options) => {
2462
+ await stepRemoveCommand(id, options);
2463
+ });
2464
+ step.command("update <id>").description("Update a step's config by its instance ID").option("--config <json>", "Config to merge as JSON").option("--template <id>", "Template ID (overrides active template)").option("--json", "Output as JSON").action(async (id, options) => {
2465
+ await stepUpdateCommand(id, options);
2466
+ });
2467
+ var model = template.command("model").description("Browse models and schemas");
2468
+ model.command("list <category>").description("List models for a category (image, video, speech, text)").option("--json", "Output as JSON").action(async (category, options) => {
2469
+ await modelListCommand(category, options);
2470
+ });
2471
+ model.command("schema <modelId>").description("Show full input schema for a model").option("--json", "Output as JSON").action(async (modelId, options) => {
2472
+ await modelSchemaCommand(modelId, options);
1406
2473
  });
1407
2474
  var tool = program.command("tool").description("Execute agent tools directly");
1408
2475
  tool.addHelpText(