@clickraft/cli 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/dist/cli.js +204 -187
- package/dist/cli.js.map +1 -1
- package/dist/index.js +204 -187
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,6 +14,8 @@ import { request as undiciRequest } from "undici";
|
|
|
14
14
|
import { z as z6 } from "zod";
|
|
15
15
|
import { createHash, randomUUID } from "crypto";
|
|
16
16
|
import { z as z5 } from "zod";
|
|
17
|
+
import { createHash as createHash2 } from "crypto";
|
|
18
|
+
import { request as undiciRequest2 } from "undici";
|
|
17
19
|
var TOKEN_FIELD_PATTERN = /"(access_token|refresh_token|device_code|user_code|code_verifier|client_secret)"\s*:\s*"[^"]*"/gi;
|
|
18
20
|
var BARE_TOKEN_PATTERN = /\bck_(?:live|dev|test)_[A-Za-z0-9_-]+/gi;
|
|
19
21
|
function redactTokenLikeFields(input) {
|
|
@@ -636,6 +638,181 @@ function errorMessage(err) {
|
|
|
636
638
|
if (err instanceof Error) return err.message;
|
|
637
639
|
return String(err);
|
|
638
640
|
}
|
|
641
|
+
async function applyWorkflowWithRetry(params) {
|
|
642
|
+
try {
|
|
643
|
+
return await patch(params);
|
|
644
|
+
} catch (err) {
|
|
645
|
+
if (!(err instanceof ApiError)) throw err;
|
|
646
|
+
const isRevConflict = err.code === "CANVAS_REV_MISMATCH" || err.meta.status === 409 && err.meta.details != null;
|
|
647
|
+
if (!isRevConflict || !params.autoRev) throw err;
|
|
648
|
+
const snapshot = await params.client.request({
|
|
649
|
+
method: "GET",
|
|
650
|
+
path: `/workflows/${encodeURIComponent(params.id)}`,
|
|
651
|
+
responseSchema: params.schemas.detail,
|
|
652
|
+
signal: params.signal
|
|
653
|
+
});
|
|
654
|
+
const retryResult = await patch({
|
|
655
|
+
...params,
|
|
656
|
+
canvasRev: snapshot.canvas_rev,
|
|
657
|
+
// Fresh key — the body changed (different canvas_rev), so reusing the
|
|
658
|
+
// caller's key would trigger IDEMPOTENCY_KEY_CONFLICT.
|
|
659
|
+
idempotencyKey: void 0
|
|
660
|
+
});
|
|
661
|
+
return { ...retryResult, retriedWithRev: snapshot.canvas_rev };
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function patch(params) {
|
|
665
|
+
const data = await params.client.request({
|
|
666
|
+
method: "PATCH",
|
|
667
|
+
path: `/workflows/${encodeURIComponent(params.id)}`,
|
|
668
|
+
body: {
|
|
669
|
+
canvas_rev: params.canvasRev,
|
|
670
|
+
operations: params.operations
|
|
671
|
+
},
|
|
672
|
+
idempotencyKey: params.idempotencyKey,
|
|
673
|
+
responseSchema: params.schemas.mutate,
|
|
674
|
+
signal: params.signal
|
|
675
|
+
});
|
|
676
|
+
return {
|
|
677
|
+
canvasRev: data.canvas_rev,
|
|
678
|
+
applied: data.applied,
|
|
679
|
+
workflow: data.workflow
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
async function uploadAsset(params) {
|
|
683
|
+
const contentType = detectContentType(params.filename, params.content);
|
|
684
|
+
const sha = sha256Hex(params.content);
|
|
685
|
+
const contentHash = `sha256:${sha}`;
|
|
686
|
+
const idempotencyKey = generateUploadIdempotencyKey(params.content, params.scope);
|
|
687
|
+
const response = await params.client.request({
|
|
688
|
+
method: "POST",
|
|
689
|
+
path: "/assets",
|
|
690
|
+
body: {
|
|
691
|
+
filename: params.filename,
|
|
692
|
+
contentType,
|
|
693
|
+
sizeBytes: params.content.length,
|
|
694
|
+
contentHash
|
|
695
|
+
},
|
|
696
|
+
idempotencyKey,
|
|
697
|
+
responseSchema: params.schema,
|
|
698
|
+
signal: params.signal
|
|
699
|
+
});
|
|
700
|
+
if (!response.deduplicated) {
|
|
701
|
+
const first = await tryPut({
|
|
702
|
+
uploadUrl: response.uploadUrl,
|
|
703
|
+
content: params.content,
|
|
704
|
+
contentType,
|
|
705
|
+
signal: params.signal,
|
|
706
|
+
dispatcher: params.putDispatcher
|
|
707
|
+
});
|
|
708
|
+
if (!first.ok) {
|
|
709
|
+
if (first.kind === "fatal") {
|
|
710
|
+
throw new ApiError(
|
|
711
|
+
"STORAGE_UPLOAD_FAILED",
|
|
712
|
+
`Upload to signed URL failed for ${params.filename} (HTTP ${first.status ?? "unknown"}).`,
|
|
713
|
+
{ retryable: false }
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
const retry = await tryPut({
|
|
717
|
+
uploadUrl: response.uploadUrl,
|
|
718
|
+
content: params.content,
|
|
719
|
+
contentType,
|
|
720
|
+
signal: params.signal,
|
|
721
|
+
dispatcher: params.putDispatcher
|
|
722
|
+
});
|
|
723
|
+
if (!retry.ok) {
|
|
724
|
+
throw new ApiError(
|
|
725
|
+
"STORAGE_UPLOAD_FAILED",
|
|
726
|
+
`Upload to signed URL failed for ${params.filename} after one retry.`,
|
|
727
|
+
{ retryable: true }
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return {
|
|
733
|
+
assetId: response.assetId,
|
|
734
|
+
publicUrl: response.publicUrl,
|
|
735
|
+
deduplicated: response.deduplicated,
|
|
736
|
+
bytes: params.content.length,
|
|
737
|
+
contentHash
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
async function tryPut(args) {
|
|
741
|
+
try {
|
|
742
|
+
const response = await undiciRequest2(args.uploadUrl, {
|
|
743
|
+
method: "PUT",
|
|
744
|
+
body: args.content,
|
|
745
|
+
headers: {
|
|
746
|
+
"content-type": args.contentType,
|
|
747
|
+
"content-length": String(args.content.length)
|
|
748
|
+
},
|
|
749
|
+
signal: args.signal,
|
|
750
|
+
dispatcher: args.dispatcher
|
|
751
|
+
});
|
|
752
|
+
await response.body.text();
|
|
753
|
+
const status = response.statusCode;
|
|
754
|
+
if (status >= 200 && status < 300) return { ok: true };
|
|
755
|
+
if (status >= 400 && status < 500) return { ok: false, kind: "fatal", status };
|
|
756
|
+
return { ok: false, kind: "retryable", status };
|
|
757
|
+
} catch {
|
|
758
|
+
return { ok: false, kind: "retryable" };
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function sha256Hex(content) {
|
|
762
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
763
|
+
}
|
|
764
|
+
var MAGIC_NUMBERS = [
|
|
765
|
+
{ contentType: "image/png", prefix: [137, 80, 78, 71, 13, 10, 26, 10] },
|
|
766
|
+
{ contentType: "image/jpeg", prefix: [255, 216, 255] },
|
|
767
|
+
{ contentType: "image/webp", prefix: [82, 73, 70, 70] }
|
|
768
|
+
// also matches WAV — disambiguated below
|
|
769
|
+
];
|
|
770
|
+
var EXTENSION_FALLBACK = {
|
|
771
|
+
png: "image/png",
|
|
772
|
+
jpg: "image/jpeg",
|
|
773
|
+
jpeg: "image/jpeg",
|
|
774
|
+
gif: "image/gif",
|
|
775
|
+
webp: "image/webp",
|
|
776
|
+
mp4: "video/mp4",
|
|
777
|
+
mov: "video/quicktime",
|
|
778
|
+
mp3: "audio/mpeg",
|
|
779
|
+
wav: "audio/wav",
|
|
780
|
+
pdf: "application/pdf",
|
|
781
|
+
txt: "text/plain",
|
|
782
|
+
json: "application/json"
|
|
783
|
+
};
|
|
784
|
+
function detectContentType(filename, content) {
|
|
785
|
+
if (content.length >= 6) {
|
|
786
|
+
const head = content.subarray(0, 6).toString("ascii");
|
|
787
|
+
if (head === "GIF87a" || head === "GIF89a") return "image/gif";
|
|
788
|
+
}
|
|
789
|
+
if (content.length >= 12 && content.subarray(4, 8).toString("ascii") === "ftyp") {
|
|
790
|
+
return "video/mp4";
|
|
791
|
+
}
|
|
792
|
+
for (const { contentType, prefix } of MAGIC_NUMBERS) {
|
|
793
|
+
if (matchesPrefix(content, prefix)) {
|
|
794
|
+
if (contentType === "image/webp") {
|
|
795
|
+
const formatId = content.subarray(8, 12).toString("ascii");
|
|
796
|
+
if (formatId === "WEBP") return "image/webp";
|
|
797
|
+
if (formatId === "WAVE") return "audio/wav";
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
return contentType;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
const ext = filename.toLowerCase().split(".").pop();
|
|
804
|
+
if (ext && EXTENSION_FALLBACK[ext]) {
|
|
805
|
+
return EXTENSION_FALLBACK[ext];
|
|
806
|
+
}
|
|
807
|
+
return "application/octet-stream";
|
|
808
|
+
}
|
|
809
|
+
function matchesPrefix(content, prefix) {
|
|
810
|
+
if (content.length < prefix.length) return false;
|
|
811
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
812
|
+
if (content[i] !== prefix[i]) return false;
|
|
813
|
+
}
|
|
814
|
+
return true;
|
|
815
|
+
}
|
|
639
816
|
|
|
640
817
|
// src/errors/codes.ts
|
|
641
818
|
var LOGIN_HINT = "Run `clickraft login` to authenticate.";
|
|
@@ -1594,10 +1771,9 @@ var GenerationResultSchema = z9.object({
|
|
|
1594
1771
|
}).strict();
|
|
1595
1772
|
|
|
1596
1773
|
// src/commands/upload.ts
|
|
1597
|
-
import { createHash as createHash2 } from "crypto";
|
|
1598
1774
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
1599
1775
|
import { basename } from "path";
|
|
1600
|
-
import
|
|
1776
|
+
import "undici";
|
|
1601
1777
|
|
|
1602
1778
|
// src/schemas/assets.ts
|
|
1603
1779
|
import { z as z10 } from "zod";
|
|
@@ -1622,60 +1798,16 @@ async function uploadFile(opts) {
|
|
|
1622
1798
|
const size = await safeStatSize(opts.filePath);
|
|
1623
1799
|
validateSize(size);
|
|
1624
1800
|
const content = await safeReadFile(opts.filePath);
|
|
1625
|
-
const contentType = detectContentType(opts.filePath, content);
|
|
1626
|
-
const sha = sha256Hex(content);
|
|
1627
|
-
const contentHash = `sha256:${sha}`;
|
|
1628
|
-
const filename = basename(opts.filePath);
|
|
1629
|
-
const idempotencyKey = generateUploadIdempotencyKey(content, scope);
|
|
1630
1801
|
const client = opts.client ?? await buildClient(opts);
|
|
1631
|
-
|
|
1802
|
+
return uploadAsset({
|
|
1632
1803
|
client,
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1804
|
+
content,
|
|
1805
|
+
filename: basename(opts.filePath),
|
|
1806
|
+
scope,
|
|
1807
|
+
signal: opts.signal,
|
|
1808
|
+
putDispatcher: opts.putDispatcher,
|
|
1809
|
+
schema: UploadResponseSchema
|
|
1639
1810
|
});
|
|
1640
|
-
if (!response.deduplicated) {
|
|
1641
|
-
const first = await tryPut({
|
|
1642
|
-
uploadUrl: response.uploadUrl,
|
|
1643
|
-
content,
|
|
1644
|
-
contentType,
|
|
1645
|
-
signal: opts.signal,
|
|
1646
|
-
dispatcher: opts.putDispatcher
|
|
1647
|
-
});
|
|
1648
|
-
if (!first.ok) {
|
|
1649
|
-
if (first.kind === "fatal") {
|
|
1650
|
-
throw new ApiError(
|
|
1651
|
-
"STORAGE_UPLOAD_FAILED",
|
|
1652
|
-
`Upload to signed URL failed for ${filename} (HTTP ${first.status ?? "unknown"}).`,
|
|
1653
|
-
{ retryable: false }
|
|
1654
|
-
);
|
|
1655
|
-
}
|
|
1656
|
-
const retry = await tryPut({
|
|
1657
|
-
uploadUrl: response.uploadUrl,
|
|
1658
|
-
content,
|
|
1659
|
-
contentType,
|
|
1660
|
-
signal: opts.signal,
|
|
1661
|
-
dispatcher: opts.putDispatcher
|
|
1662
|
-
});
|
|
1663
|
-
if (!retry.ok) {
|
|
1664
|
-
throw new ApiError(
|
|
1665
|
-
"STORAGE_UPLOAD_FAILED",
|
|
1666
|
-
`Upload to signed URL failed for ${filename} after one retry.`,
|
|
1667
|
-
{ retryable: true }
|
|
1668
|
-
);
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
return {
|
|
1673
|
-
assetId: response.assetId,
|
|
1674
|
-
publicUrl: response.publicUrl,
|
|
1675
|
-
deduplicated: response.deduplicated,
|
|
1676
|
-
bytes: content.length,
|
|
1677
|
-
contentHash
|
|
1678
|
-
};
|
|
1679
1811
|
}
|
|
1680
1812
|
async function buildClient(opts) {
|
|
1681
1813
|
const cfg = await loadConfig({
|
|
@@ -1690,42 +1822,6 @@ async function buildClient(opts) {
|
|
|
1690
1822
|
cloudflareAccess: cfg.cloudflareAccess
|
|
1691
1823
|
});
|
|
1692
1824
|
}
|
|
1693
|
-
async function postAssetMetadata(args) {
|
|
1694
|
-
return args.client.request({
|
|
1695
|
-
method: "POST",
|
|
1696
|
-
path: "/assets",
|
|
1697
|
-
body: {
|
|
1698
|
-
filename: args.filename,
|
|
1699
|
-
contentType: args.contentType,
|
|
1700
|
-
sizeBytes: args.sizeBytes,
|
|
1701
|
-
contentHash: args.contentHash
|
|
1702
|
-
},
|
|
1703
|
-
idempotencyKey: args.idempotencyKey,
|
|
1704
|
-
responseSchema: UploadResponseSchema,
|
|
1705
|
-
signal: args.signal
|
|
1706
|
-
});
|
|
1707
|
-
}
|
|
1708
|
-
async function tryPut(args) {
|
|
1709
|
-
try {
|
|
1710
|
-
const response = await undiciRequest2(args.uploadUrl, {
|
|
1711
|
-
method: "PUT",
|
|
1712
|
-
body: args.content,
|
|
1713
|
-
headers: {
|
|
1714
|
-
"content-type": args.contentType,
|
|
1715
|
-
"content-length": String(args.content.length)
|
|
1716
|
-
},
|
|
1717
|
-
signal: args.signal,
|
|
1718
|
-
dispatcher: args.dispatcher
|
|
1719
|
-
});
|
|
1720
|
-
await response.body.text();
|
|
1721
|
-
const status = response.statusCode;
|
|
1722
|
-
if (status >= 200 && status < 300) return { ok: true };
|
|
1723
|
-
if (status >= 400 && status < 500) return { ok: false, kind: "fatal", status };
|
|
1724
|
-
return { ok: false, kind: "retryable", status };
|
|
1725
|
-
} catch {
|
|
1726
|
-
return { ok: false, kind: "retryable" };
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
1825
|
async function safeStatSize(filePath) {
|
|
1730
1826
|
let stats;
|
|
1731
1827
|
try {
|
|
@@ -1787,61 +1883,6 @@ function validateSize(bytes) {
|
|
|
1787
1883
|
);
|
|
1788
1884
|
}
|
|
1789
1885
|
}
|
|
1790
|
-
function sha256Hex(content) {
|
|
1791
|
-
return createHash2("sha256").update(content).digest("hex");
|
|
1792
|
-
}
|
|
1793
|
-
var MAGIC_NUMBERS = [
|
|
1794
|
-
{ contentType: "image/png", prefix: [137, 80, 78, 71, 13, 10, 26, 10] },
|
|
1795
|
-
{ contentType: "image/jpeg", prefix: [255, 216, 255] },
|
|
1796
|
-
{ contentType: "image/webp", prefix: [82, 73, 70, 70] }
|
|
1797
|
-
// also matches WAV — disambiguated below
|
|
1798
|
-
];
|
|
1799
|
-
var EXTENSION_FALLBACK = {
|
|
1800
|
-
png: "image/png",
|
|
1801
|
-
jpg: "image/jpeg",
|
|
1802
|
-
jpeg: "image/jpeg",
|
|
1803
|
-
gif: "image/gif",
|
|
1804
|
-
webp: "image/webp",
|
|
1805
|
-
mp4: "video/mp4",
|
|
1806
|
-
mov: "video/quicktime",
|
|
1807
|
-
mp3: "audio/mpeg",
|
|
1808
|
-
wav: "audio/wav",
|
|
1809
|
-
pdf: "application/pdf",
|
|
1810
|
-
txt: "text/plain",
|
|
1811
|
-
json: "application/json"
|
|
1812
|
-
};
|
|
1813
|
-
function detectContentType(filePath, content) {
|
|
1814
|
-
if (content.length >= 6) {
|
|
1815
|
-
const head = content.subarray(0, 6).toString("ascii");
|
|
1816
|
-
if (head === "GIF87a" || head === "GIF89a") return "image/gif";
|
|
1817
|
-
}
|
|
1818
|
-
if (content.length >= 12 && content.subarray(4, 8).toString("ascii") === "ftyp") {
|
|
1819
|
-
return "video/mp4";
|
|
1820
|
-
}
|
|
1821
|
-
for (const { contentType, prefix } of MAGIC_NUMBERS) {
|
|
1822
|
-
if (matchesPrefix(content, prefix)) {
|
|
1823
|
-
if (contentType === "image/webp") {
|
|
1824
|
-
const formatId = content.subarray(8, 12).toString("ascii");
|
|
1825
|
-
if (formatId === "WEBP") return "image/webp";
|
|
1826
|
-
if (formatId === "WAVE") return "audio/wav";
|
|
1827
|
-
break;
|
|
1828
|
-
}
|
|
1829
|
-
return contentType;
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
const ext = filePath.toLowerCase().split(".").pop();
|
|
1833
|
-
if (ext && EXTENSION_FALLBACK[ext]) {
|
|
1834
|
-
return EXTENSION_FALLBACK[ext];
|
|
1835
|
-
}
|
|
1836
|
-
return "application/octet-stream";
|
|
1837
|
-
}
|
|
1838
|
-
function matchesPrefix(content, prefix) {
|
|
1839
|
-
if (content.length < prefix.length) return false;
|
|
1840
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
1841
|
-
if (content[i] !== prefix[i]) return false;
|
|
1842
|
-
}
|
|
1843
|
-
return true;
|
|
1844
|
-
}
|
|
1845
1886
|
|
|
1846
1887
|
// src/commands/generate/internal.ts
|
|
1847
1888
|
async function buildGenerateClient(opts) {
|
|
@@ -3087,54 +3128,19 @@ async function buildWorkflowClient(opts) {
|
|
|
3087
3128
|
});
|
|
3088
3129
|
}
|
|
3089
3130
|
|
|
3090
|
-
// src/commands/workflow/get.ts
|
|
3091
|
-
async function getWorkflow(opts) {
|
|
3092
|
-
const client = opts.client ?? await buildWorkflowClient(opts);
|
|
3093
|
-
return client.request({
|
|
3094
|
-
method: "GET",
|
|
3095
|
-
path: `/workflows/${encodeURIComponent(opts.id)}`,
|
|
3096
|
-
responseSchema: WorkflowDetailSchema,
|
|
3097
|
-
signal: opts.signal
|
|
3098
|
-
});
|
|
3099
|
-
}
|
|
3100
|
-
|
|
3101
3131
|
// src/commands/workflow/apply.ts
|
|
3102
3132
|
async function applyWorkflow(opts) {
|
|
3103
3133
|
const client = opts.client ?? await buildWorkflowClient(opts);
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
if (!isRevConflict || !opts.autoRev) throw err;
|
|
3110
|
-
const snapshot = await getWorkflow({ id: opts.id, client, signal: opts.signal });
|
|
3111
|
-
const retryResult = await patchWorkflow(client, {
|
|
3112
|
-
...opts,
|
|
3113
|
-
canvasRev: snapshot.canvas_rev,
|
|
3114
|
-
// Fresh key — the body changed (different canvas_rev), so reusing
|
|
3115
|
-
// the caller's key would trigger IDEMPOTENCY_KEY_CONFLICT.
|
|
3116
|
-
idempotencyKey: void 0
|
|
3117
|
-
});
|
|
3118
|
-
return { ...retryResult, retriedWithRev: snapshot.canvas_rev };
|
|
3119
|
-
}
|
|
3120
|
-
}
|
|
3121
|
-
async function patchWorkflow(client, opts) {
|
|
3122
|
-
const data = await client.request({
|
|
3123
|
-
method: "PATCH",
|
|
3124
|
-
path: `/workflows/${encodeURIComponent(opts.id)}`,
|
|
3125
|
-
body: {
|
|
3126
|
-
canvas_rev: opts.canvasRev,
|
|
3127
|
-
operations: opts.operations
|
|
3128
|
-
},
|
|
3134
|
+
return applyWorkflowWithRetry({
|
|
3135
|
+
client,
|
|
3136
|
+
id: opts.id,
|
|
3137
|
+
canvasRev: opts.canvasRev,
|
|
3138
|
+
operations: opts.operations,
|
|
3129
3139
|
idempotencyKey: opts.idempotencyKey,
|
|
3130
|
-
|
|
3131
|
-
signal: opts.signal
|
|
3140
|
+
autoRev: opts.autoRev,
|
|
3141
|
+
signal: opts.signal,
|
|
3142
|
+
schemas: { mutate: WorkflowMutateResponseSchema, detail: WorkflowDetailSchema }
|
|
3132
3143
|
});
|
|
3133
|
-
return {
|
|
3134
|
-
canvasRev: data.canvas_rev,
|
|
3135
|
-
applied: data.applied,
|
|
3136
|
-
workflow: data.workflow
|
|
3137
|
-
};
|
|
3138
3144
|
}
|
|
3139
3145
|
|
|
3140
3146
|
// src/commands/workflow/create.ts
|
|
@@ -3165,6 +3171,17 @@ async function createWorkflow(opts) {
|
|
|
3165
3171
|
});
|
|
3166
3172
|
}
|
|
3167
3173
|
|
|
3174
|
+
// src/commands/workflow/get.ts
|
|
3175
|
+
async function getWorkflow(opts) {
|
|
3176
|
+
const client = opts.client ?? await buildWorkflowClient(opts);
|
|
3177
|
+
return client.request({
|
|
3178
|
+
method: "GET",
|
|
3179
|
+
path: `/workflows/${encodeURIComponent(opts.id)}`,
|
|
3180
|
+
responseSchema: WorkflowDetailSchema,
|
|
3181
|
+
signal: opts.signal
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3168
3185
|
// src/commands/workflow/list.ts
|
|
3169
3186
|
async function listWorkflows(opts) {
|
|
3170
3187
|
const client = opts.client ?? await buildWorkflowClient(opts);
|