@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 +1113 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
904
|
-
const uploadRequest = {
|
|
913
|
+
const initRequest = {
|
|
905
914
|
method: "POST",
|
|
906
915
|
headers: {
|
|
907
916
|
Authorization: `Bearer ${accessToken}`,
|
|
908
|
-
"Content-Type":
|
|
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
|
|
921
|
-
|
|
922
|
-
|
|
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
|
-
|
|
982
|
+
finalizeResult?.error ?? `File finalize failed (${finalizeResponse.status})`
|
|
925
983
|
);
|
|
926
984
|
}
|
|
927
|
-
return
|
|
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/
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
await
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
|
|
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
|
-
|
|
1405
|
-
|
|
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(
|