@runcofactor/cli 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -0
- package/dist/api-catalog-GFGCLVS4.js +401 -0
- package/dist/api-catalog-GFGCLVS4.js.map +1 -0
- package/dist/attachment-SJC7D7FY.js +276 -0
- package/dist/attachment-SJC7D7FY.js.map +1 -0
- package/dist/auth-EN6NQCJY.js +31 -0
- package/dist/auth-EN6NQCJY.js.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-3CZU7XCS.js +272 -0
- package/dist/chunk-3CZU7XCS.js.map +1 -0
- package/dist/chunk-4NRDONSC.js +1048 -0
- package/dist/chunk-4NRDONSC.js.map +1 -0
- package/dist/chunk-4PNOV7OV.js +51980 -0
- package/dist/chunk-4PNOV7OV.js.map +1 -0
- package/dist/chunk-52IO7ACJ.js +2941 -0
- package/dist/chunk-52IO7ACJ.js.map +1 -0
- package/dist/chunk-6LEZCKKV.js +663 -0
- package/dist/chunk-6LEZCKKV.js.map +1 -0
- package/dist/chunk-A4OFTUVV.js +170 -0
- package/dist/chunk-A4OFTUVV.js.map +1 -0
- package/dist/chunk-B2VISN2V.js +2459 -0
- package/dist/chunk-B2VISN2V.js.map +1 -0
- package/dist/chunk-CLAWT7BJ.js +271 -0
- package/dist/chunk-CLAWT7BJ.js.map +1 -0
- package/dist/chunk-GKY2KXO5.js +281 -0
- package/dist/chunk-GKY2KXO5.js.map +1 -0
- package/dist/chunk-I4QLI3JO.js +413 -0
- package/dist/chunk-I4QLI3JO.js.map +1 -0
- package/dist/chunk-IMPZAX6L.js +180 -0
- package/dist/chunk-IMPZAX6L.js.map +1 -0
- package/dist/chunk-J6SH7GQT.js +241 -0
- package/dist/chunk-J6SH7GQT.js.map +1 -0
- package/dist/chunk-JDBCZ3Z2.js +298 -0
- package/dist/chunk-JDBCZ3Z2.js.map +1 -0
- package/dist/chunk-JICSYFRJ.js +46 -0
- package/dist/chunk-JICSYFRJ.js.map +1 -0
- package/dist/chunk-JWHYXEA2.js +32 -0
- package/dist/chunk-JWHYXEA2.js.map +1 -0
- package/dist/chunk-N7HFALX3.js +88 -0
- package/dist/chunk-N7HFALX3.js.map +1 -0
- package/dist/chunk-Q4BO4Z5V.js +178 -0
- package/dist/chunk-Q4BO4Z5V.js.map +1 -0
- package/dist/chunk-QFDOWABX.js +195 -0
- package/dist/chunk-QFDOWABX.js.map +1 -0
- package/dist/chunk-SJJW77WN.js +605 -0
- package/dist/chunk-SJJW77WN.js.map +1 -0
- package/dist/chunk-VKVL7WBN.js +50 -0
- package/dist/chunk-VKVL7WBN.js.map +1 -0
- package/dist/chunk-WOHL4FTB.js +353 -0
- package/dist/chunk-WOHL4FTB.js.map +1 -0
- package/dist/chunk-XQXVG7RA.js +426 -0
- package/dist/chunk-XQXVG7RA.js.map +1 -0
- package/dist/chunk-ZD3KHESI.js +94 -0
- package/dist/chunk-ZD3KHESI.js.map +1 -0
- package/dist/credential-COLTBV6D.js +450 -0
- package/dist/credential-COLTBV6D.js.map +1 -0
- package/dist/credential-template-EEPYFG7R.js +291 -0
- package/dist/credential-template-EEPYFG7R.js.map +1 -0
- package/dist/debug-workflow-KOVH5GOP.js +259 -0
- package/dist/debug-workflow-KOVH5GOP.js.map +1 -0
- package/dist/dist-44PZR7BQ.js +423 -0
- package/dist/dist-44PZR7BQ.js.map +1 -0
- package/dist/docs-3SSORHB5.js +366 -0
- package/dist/docs-3SSORHB5.js.map +1 -0
- package/dist/doctor-OTSIWV6G.js +132 -0
- package/dist/doctor-OTSIWV6G.js.map +1 -0
- package/dist/feature-flag-T2LRZME3.js +283 -0
- package/dist/feature-flag-T2LRZME3.js.map +1 -0
- package/dist/file-type-S3UUBVCT.js +4205 -0
- package/dist/file-type-S3UUBVCT.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +982 -0
- package/dist/index.js.map +1 -0
- package/dist/init-TGWRECT2.js +208 -0
- package/dist/init-TGWRECT2.js.map +1 -0
- package/dist/install-DXSNWZKO.js +295 -0
- package/dist/install-DXSNWZKO.js.map +1 -0
- package/dist/issues-XIZOPX6T.js +564 -0
- package/dist/issues-XIZOPX6T.js.map +1 -0
- package/dist/knobs-NBINZIUB.js +295 -0
- package/dist/knobs-NBINZIUB.js.map +1 -0
- package/dist/list-3TDHK637.js +59 -0
- package/dist/list-3TDHK637.js.map +1 -0
- package/dist/local-HICVD6PU.js +100 -0
- package/dist/local-HICVD6PU.js.map +1 -0
- package/dist/login-UOIUXVUK.js +315 -0
- package/dist/login-UOIUXVUK.js.map +1 -0
- package/dist/mcp-54IJEOUA.js +1956 -0
- package/dist/mcp-54IJEOUA.js.map +1 -0
- package/dist/pdf-extract-ABWV3L5X.js +13 -0
- package/dist/pdf-extract-ABWV3L5X.js.map +1 -0
- package/dist/pdfjs-DQ2YA5S3.js +41561 -0
- package/dist/pdfjs-DQ2YA5S3.js.map +1 -0
- package/dist/property-UXLOLPTD.js +701 -0
- package/dist/property-UXLOLPTD.js.map +1 -0
- package/dist/property-skill-guide.md +189 -0
- package/dist/psql-BYUUNLGG.js +115 -0
- package/dist/psql-BYUUNLGG.js.map +1 -0
- package/dist/record-4TR2UIIS.js +233 -0
- package/dist/record-4TR2UIIS.js.map +1 -0
- package/dist/redis-35VBUYW7.js +116 -0
- package/dist/redis-35VBUYW7.js.map +1 -0
- package/dist/release-OOLTUBUT.js +354 -0
- package/dist/release-OOLTUBUT.js.map +1 -0
- package/dist/sandbox-XGSUU6G7.js +2046 -0
- package/dist/sandbox-XGSUU6G7.js.map +1 -0
- package/dist/scenarios-GWLJEGHA.js +280 -0
- package/dist/scenarios-GWLJEGHA.js.map +1 -0
- package/dist/session-MYFNZPRP.js +2414 -0
- package/dist/session-MYFNZPRP.js.map +1 -0
- package/dist/skill-KM34LDMO.js +1839 -0
- package/dist/skill-KM34LDMO.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +262 -0
- package/dist/skills/debug-run/history/1.0.0.md +244 -0
- package/dist/skills/debug-run/history/1.0.1.md +245 -0
- package/dist/skills/debug-run/history/1.0.2.md +254 -0
- package/dist/skills/debug-run/history/1.1.0.md +262 -0
- package/dist/skills/debug-run/version.json +3 -0
- package/dist/spec-RA3CFEKG.js +951 -0
- package/dist/spec-RA3CFEKG.js.map +1 -0
- package/dist/src-LB4HBTJQ.js +1516 -0
- package/dist/src-LB4HBTJQ.js.map +1 -0
- package/dist/src-N3TPKGTY.js +244 -0
- package/dist/src-N3TPKGTY.js.map +1 -0
- package/dist/src-UVDX5UP6.js +17 -0
- package/dist/src-UVDX5UP6.js.map +1 -0
- package/dist/start-PSEQSQA3.js +151 -0
- package/dist/start-PSEQSQA3.js.map +1 -0
- package/dist/tag-YY6YAABQ.js +22 -0
- package/dist/tag-YY6YAABQ.js.map +1 -0
- package/dist/task-IQC3YR5K.js +922 -0
- package/dist/task-IQC3YR5K.js.map +1 -0
- package/dist/test-suite-DZNJFXDW.js +394 -0
- package/dist/test-suite-DZNJFXDW.js.map +1 -0
- package/dist/trace-HXIW7U2D.js +14 -0
- package/dist/trace-HXIW7U2D.js.map +1 -0
- package/dist/triage-ORG45R5W.js +16 -0
- package/dist/triage-ORG45R5W.js.map +1 -0
- package/dist/upgrade-L2VYMP2V.js +61 -0
- package/dist/upgrade-L2VYMP2V.js.map +1 -0
- package/dist/workflow-XYFH5LTP.js +3708 -0
- package/dist/workflow-XYFH5LTP.js.map +1 -0
- package/dist/workflow-cache-NQJQFWMV.js +253 -0
- package/dist/workflow-cache-NQJQFWMV.js.map +1 -0
- package/dist/workflow-debug-BGD4UIXU.js +927 -0
- package/dist/workflow-debug-BGD4UIXU.js.map +1 -0
- package/dist/workflow-proposals-MTUDX46O.js +382 -0
- package/dist/workflow-proposals-MTUDX46O.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
apiRequest
|
|
4
|
+
} from "./chunk-WOHL4FTB.js";
|
|
5
|
+
import "./chunk-SJJW77WN.js";
|
|
6
|
+
import "./chunk-3CZU7XCS.js";
|
|
7
|
+
import {
|
|
8
|
+
getArgValue,
|
|
9
|
+
hasFlag,
|
|
10
|
+
resolveConfig
|
|
11
|
+
} from "./chunk-A4OFTUVV.js";
|
|
12
|
+
import "./chunk-JWHYXEA2.js";
|
|
13
|
+
import "./chunk-VKVL7WBN.js";
|
|
14
|
+
|
|
15
|
+
// src/attachment.ts
|
|
16
|
+
import fs from "fs/promises";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import process from "process";
|
|
19
|
+
var VALID_KINDS = ["all", "image", "pdf", "spreadsheet", "data"];
|
|
20
|
+
function printAttachmentHelp() {
|
|
21
|
+
console.log(
|
|
22
|
+
[
|
|
23
|
+
"Usage: canary attachment <sub-command> [options]",
|
|
24
|
+
"",
|
|
25
|
+
"Sub-commands:",
|
|
26
|
+
" list [options] List uploaded attachments",
|
|
27
|
+
" upload <file> [options] Upload a local file; prints the new ID",
|
|
28
|
+
" get <id> [--url] Print attachment metadata (or signed URL)",
|
|
29
|
+
" delete <id> Soft-delete an attachment",
|
|
30
|
+
"",
|
|
31
|
+
"List options:",
|
|
32
|
+
" --kind <kind> Filter: all, image, pdf, spreadsheet, data (default: all)",
|
|
33
|
+
" --search <query> Search by filename",
|
|
34
|
+
" --page <n> Page number (default: 1)",
|
|
35
|
+
" --page-size <n> Page size (default: 25)",
|
|
36
|
+
" --json Output raw JSON",
|
|
37
|
+
"",
|
|
38
|
+
"Upload options:",
|
|
39
|
+
" --filename <name> Override filename (default: basename of file)",
|
|
40
|
+
" --json Output raw JSON",
|
|
41
|
+
"",
|
|
42
|
+
"Get options:",
|
|
43
|
+
" --url Print short-lived signed download URL",
|
|
44
|
+
" --json Output raw JSON",
|
|
45
|
+
"",
|
|
46
|
+
"Tip: use the returned ID in a node's `configJson.attachmentIds` array",
|
|
47
|
+
"for action or notification nodes when creating/patching workflows.",
|
|
48
|
+
"",
|
|
49
|
+
"Common options:",
|
|
50
|
+
" --env <env> Target environment (prod, dev, local)",
|
|
51
|
+
" --api-url <url> API URL override",
|
|
52
|
+
" --token <key> API token override"
|
|
53
|
+
].join("\n")
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
function formatSize(bytes) {
|
|
57
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
58
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
59
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
60
|
+
}
|
|
61
|
+
function truncate(s, max) {
|
|
62
|
+
return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
|
|
63
|
+
}
|
|
64
|
+
async function handleList(argv, apiUrl, token) {
|
|
65
|
+
const kind = getArgValue(argv, "--kind");
|
|
66
|
+
const search = getArgValue(argv, "--search");
|
|
67
|
+
const page = getArgValue(argv, "--page");
|
|
68
|
+
const pageSize = getArgValue(argv, "--page-size");
|
|
69
|
+
const jsonOutput = hasFlag(argv, "--json");
|
|
70
|
+
if (kind && !VALID_KINDS.includes(kind)) {
|
|
71
|
+
console.error(`Error: invalid --kind "${kind}". Must be one of: ${VALID_KINDS.join(", ")}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
const params = new URLSearchParams();
|
|
75
|
+
if (kind) params.set("kind", kind);
|
|
76
|
+
if (search) params.set("search", search);
|
|
77
|
+
if (page) params.set("page", page);
|
|
78
|
+
if (pageSize) params.set("pageSize", pageSize);
|
|
79
|
+
const qs = params.toString();
|
|
80
|
+
const route = `/org/attachments${qs ? `?${qs}` : ""}`;
|
|
81
|
+
const result = await apiRequest(apiUrl, token, "GET", route);
|
|
82
|
+
if (!result.ok) {
|
|
83
|
+
console.error(`Error: ${result.error}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
if (jsonOutput) {
|
|
87
|
+
console.log(JSON.stringify({ data: result.data, pagination: result.pagination }, null, 2));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const { data, pagination } = result;
|
|
91
|
+
console.log(
|
|
92
|
+
`Attachments: ${pagination.totalItems} total (page ${pagination.page}/${pagination.totalPages})
|
|
93
|
+
`
|
|
94
|
+
);
|
|
95
|
+
if (data.length === 0) {
|
|
96
|
+
console.log("No attachments found.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const idW = 36;
|
|
100
|
+
const nameW = 40;
|
|
101
|
+
const typeW = 24;
|
|
102
|
+
const sizeW = 10;
|
|
103
|
+
console.log(
|
|
104
|
+
["ID".padEnd(idW), "Filename".padEnd(nameW), "Type".padEnd(typeW), "Size".padEnd(sizeW)].join(
|
|
105
|
+
" "
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
for (const a of data) {
|
|
109
|
+
console.log(
|
|
110
|
+
[
|
|
111
|
+
a.id.padEnd(idW),
|
|
112
|
+
truncate(a.filename, nameW).padEnd(nameW),
|
|
113
|
+
truncate(a.contentType, typeW).padEnd(typeW),
|
|
114
|
+
formatSize(a.sizeBytes).padEnd(sizeW)
|
|
115
|
+
].join(" ")
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function handleUpload(argv, apiUrl, token) {
|
|
120
|
+
const filePath = argv.find((a) => !a.startsWith("-"));
|
|
121
|
+
if (!filePath) {
|
|
122
|
+
console.error("Error: missing file path.");
|
|
123
|
+
console.error("Usage: canary attachment upload <file> [--filename <name>]");
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const overrideName = getArgValue(argv, "--filename");
|
|
127
|
+
const jsonOutput = hasFlag(argv, "--json");
|
|
128
|
+
let buf;
|
|
129
|
+
try {
|
|
130
|
+
buf = await fs.readFile(filePath);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
console.error(`Error: failed to read ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
const filename = overrideName ?? path.basename(filePath);
|
|
136
|
+
const form = new FormData();
|
|
137
|
+
form.append("files", new Blob([new Uint8Array(buf)]), filename);
|
|
138
|
+
const res = await fetch(`${apiUrl}/org/attachments`, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
141
|
+
body: form
|
|
142
|
+
});
|
|
143
|
+
if (res.status === 401) {
|
|
144
|
+
console.error("Error: Unauthorized. Run: canary login");
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
let body;
|
|
148
|
+
try {
|
|
149
|
+
body = await res.json();
|
|
150
|
+
} catch {
|
|
151
|
+
console.error(`Error: invalid JSON response (${res.status})`);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
if (!body.ok || !body.data || body.data.length === 0) {
|
|
155
|
+
console.error(`Error: ${body.error ?? `upload failed (${res.status})`}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const [created] = body.data;
|
|
159
|
+
if (jsonOutput) {
|
|
160
|
+
console.log(JSON.stringify(created, null, 2));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
console.log(`\u2713 Uploaded ${created.filename}`);
|
|
164
|
+
console.log(` ID: ${created.id}`);
|
|
165
|
+
console.log(` Type: ${created.contentType}`);
|
|
166
|
+
console.log(` Size: ${formatSize(created.sizeBytes)}`);
|
|
167
|
+
console.log("");
|
|
168
|
+
console.log("Reference in a workflow node:");
|
|
169
|
+
console.log(` configJson.attachmentIds = ["${created.id}"]`);
|
|
170
|
+
}
|
|
171
|
+
async function handleGet(argv, apiUrl, token) {
|
|
172
|
+
const id = argv.find((a) => !a.startsWith("-"));
|
|
173
|
+
if (!id) {
|
|
174
|
+
console.error("Error: missing attachment ID.");
|
|
175
|
+
console.error("Usage: canary attachment get <id> [--url]");
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
const wantUrl = hasFlag(argv, "--url");
|
|
179
|
+
const jsonOutput = hasFlag(argv, "--json");
|
|
180
|
+
if (wantUrl) {
|
|
181
|
+
const result = await apiRequest(
|
|
182
|
+
apiUrl,
|
|
183
|
+
token,
|
|
184
|
+
"GET",
|
|
185
|
+
`/org/attachments/${id}/url`
|
|
186
|
+
);
|
|
187
|
+
if (!result.ok || !result.url) {
|
|
188
|
+
console.error(`Error: ${result.error ?? "failed to fetch signed URL"}`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
if (jsonOutput) {
|
|
192
|
+
console.log(JSON.stringify({ id, url: result.url }, null, 2));
|
|
193
|
+
} else {
|
|
194
|
+
console.log(result.url);
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const res = await fetch(`${apiUrl}/org/attachments/resolve`, {
|
|
199
|
+
method: "POST",
|
|
200
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
201
|
+
body: JSON.stringify({ ids: [id] })
|
|
202
|
+
});
|
|
203
|
+
if (res.status === 401) {
|
|
204
|
+
console.error("Error: Unauthorized. Run: canary login");
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
const body = await res.json();
|
|
208
|
+
if (!body.ok || !body.data || body.data.length === 0) {
|
|
209
|
+
console.error(`Error: ${body.error ?? "attachment not found"}`);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
const [att] = body.data;
|
|
213
|
+
if (jsonOutput) {
|
|
214
|
+
console.log(JSON.stringify(att, null, 2));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
console.log(`${att.filename}`);
|
|
218
|
+
console.log(` ID: ${att.id}`);
|
|
219
|
+
console.log(` Type: ${att.contentType}`);
|
|
220
|
+
console.log(` Size: ${formatSize(att.sizeBytes)}`);
|
|
221
|
+
console.log(` Created: ${att.createdAt}`);
|
|
222
|
+
}
|
|
223
|
+
async function handleDelete(argv, apiUrl, token) {
|
|
224
|
+
const id = argv.find((a) => !a.startsWith("-"));
|
|
225
|
+
if (!id) {
|
|
226
|
+
console.error("Error: missing attachment ID.");
|
|
227
|
+
console.error("Usage: canary attachment delete <id>");
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
const jsonOutput = hasFlag(argv, "--json");
|
|
231
|
+
const result = await apiRequest(
|
|
232
|
+
apiUrl,
|
|
233
|
+
token,
|
|
234
|
+
"DELETE",
|
|
235
|
+
`/org/attachments/${id}`
|
|
236
|
+
);
|
|
237
|
+
if (!result.ok) {
|
|
238
|
+
console.error(`Error: ${result.error ?? "delete failed"}`);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
if (jsonOutput) {
|
|
242
|
+
console.log(JSON.stringify({ ok: true, id }, null, 2));
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
console.log(`\u2713 Deleted attachment ${id}`);
|
|
246
|
+
}
|
|
247
|
+
async function runAttachment(argv) {
|
|
248
|
+
const [subCommand, ...rest] = argv;
|
|
249
|
+
if (!subCommand || subCommand === "help" || hasFlag(argv, "--help", "-h")) {
|
|
250
|
+
printAttachmentHelp();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const { apiUrl, token } = await resolveConfig(rest);
|
|
254
|
+
switch (subCommand) {
|
|
255
|
+
case "list":
|
|
256
|
+
await handleList(rest, apiUrl, token);
|
|
257
|
+
return;
|
|
258
|
+
case "upload":
|
|
259
|
+
await handleUpload(rest, apiUrl, token);
|
|
260
|
+
return;
|
|
261
|
+
case "get":
|
|
262
|
+
await handleGet(rest, apiUrl, token);
|
|
263
|
+
return;
|
|
264
|
+
case "delete":
|
|
265
|
+
await handleDelete(rest, apiUrl, token);
|
|
266
|
+
return;
|
|
267
|
+
default:
|
|
268
|
+
console.error(`Unknown attachment sub-command: ${subCommand}`);
|
|
269
|
+
printAttachmentHelp();
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
export {
|
|
274
|
+
runAttachment
|
|
275
|
+
};
|
|
276
|
+
//# sourceMappingURL=attachment-SJC7D7FY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/attachment.ts"],"sourcesContent":["/**\n * CLI Attachment Commands\n *\n * List, upload, fetch, and delete org-scoped attachments used by action\n * and notification nodes (config.attachmentIds).\n *\n * @module attachment\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { resolveConfig, getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\n\ntype AttachmentListItem = {\n id: string;\n filename: string;\n contentType: string;\n sizeBytes: number;\n createdAt: string;\n createdBy: string | null;\n};\n\ntype ListResponse = {\n ok: boolean;\n error?: string;\n data: AttachmentListItem[];\n pagination: { page: number; pageSize: number; totalItems: number; totalPages: number };\n};\n\ntype UploadResponse = {\n ok: boolean;\n error?: string;\n data?: AttachmentListItem[];\n};\n\ntype UrlResponse = { ok: boolean; error?: string; url?: string };\n\nconst VALID_KINDS = ['all', 'image', 'pdf', 'spreadsheet', 'data'] as const;\n\nfunction printAttachmentHelp(): void {\n console.log(\n [\n 'Usage: canary attachment <sub-command> [options]',\n '',\n 'Sub-commands:',\n ' list [options] List uploaded attachments',\n ' upload <file> [options] Upload a local file; prints the new ID',\n ' get <id> [--url] Print attachment metadata (or signed URL)',\n ' delete <id> Soft-delete an attachment',\n '',\n 'List options:',\n ' --kind <kind> Filter: all, image, pdf, spreadsheet, data (default: all)',\n ' --search <query> Search by filename',\n ' --page <n> Page number (default: 1)',\n ' --page-size <n> Page size (default: 25)',\n ' --json Output raw JSON',\n '',\n 'Upload options:',\n ' --filename <name> Override filename (default: basename of file)',\n ' --json Output raw JSON',\n '',\n 'Get options:',\n ' --url Print short-lived signed download URL',\n ' --json Output raw JSON',\n '',\n 'Tip: use the returned ID in a node\\'s `configJson.attachmentIds` array',\n 'for action or notification nodes when creating/patching workflows.',\n '',\n 'Common options:',\n ' --env <env> Target environment (prod, dev, local)',\n ' --api-url <url> API URL override',\n ' --token <key> API token override',\n ].join('\\n')\n );\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction truncate(s: string, max: number): string {\n return s.length <= max ? s : s.slice(0, max - 1) + '…';\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const kind = getArgValue(argv, '--kind');\n const search = getArgValue(argv, '--search');\n const page = getArgValue(argv, '--page');\n const pageSize = getArgValue(argv, '--page-size');\n const jsonOutput = hasFlag(argv, '--json');\n\n if (kind && !VALID_KINDS.includes(kind as (typeof VALID_KINDS)[number])) {\n console.error(`Error: invalid --kind \"${kind}\". Must be one of: ${VALID_KINDS.join(', ')}`);\n process.exit(1);\n }\n\n const params = new URLSearchParams();\n if (kind) params.set('kind', kind);\n if (search) params.set('search', search);\n if (page) params.set('page', page);\n if (pageSize) params.set('pageSize', pageSize);\n\n const qs = params.toString();\n const route = `/org/attachments${qs ? `?${qs}` : ''}`;\n const result = await apiRequest<ListResponse>(apiUrl, token, 'GET', route);\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify({ data: result.data, pagination: result.pagination }, null, 2));\n return;\n }\n\n const { data, pagination } = result;\n console.log(\n `Attachments: ${pagination.totalItems} total (page ${pagination.page}/${pagination.totalPages})\\n`\n );\n\n if (data.length === 0) {\n console.log('No attachments found.');\n return;\n }\n\n const idW = 36;\n const nameW = 40;\n const typeW = 24;\n const sizeW = 10;\n\n console.log(\n ['ID'.padEnd(idW), 'Filename'.padEnd(nameW), 'Type'.padEnd(typeW), 'Size'.padEnd(sizeW)].join(\n ' '\n )\n );\n for (const a of data) {\n console.log(\n [\n a.id.padEnd(idW),\n truncate(a.filename, nameW).padEnd(nameW),\n truncate(a.contentType, typeW).padEnd(typeW),\n formatSize(a.sizeBytes).padEnd(sizeW),\n ].join(' ')\n );\n }\n}\n\nasync function handleUpload(argv: string[], apiUrl: string, token: string): Promise<void> {\n const filePath = argv.find((a) => !a.startsWith('-'));\n if (!filePath) {\n console.error('Error: missing file path.');\n console.error('Usage: canary attachment upload <file> [--filename <name>]');\n process.exit(1);\n }\n\n const overrideName = getArgValue(argv, '--filename');\n const jsonOutput = hasFlag(argv, '--json');\n\n let buf: Buffer;\n try {\n buf = await fs.readFile(filePath);\n } catch (err) {\n console.error(`Error: failed to read ${filePath}: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n const filename = overrideName ?? path.basename(filePath);\n const form = new FormData();\n form.append('files', new Blob([new Uint8Array(buf)]), filename);\n\n const res = await fetch(`${apiUrl}/org/attachments`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: form,\n });\n\n if (res.status === 401) {\n console.error('Error: Unauthorized. Run: canary login');\n process.exit(1);\n }\n\n let body: UploadResponse;\n try {\n body = (await res.json()) as UploadResponse;\n } catch {\n console.error(`Error: invalid JSON response (${res.status})`);\n process.exit(1);\n }\n\n if (!body.ok || !body.data || body.data.length === 0) {\n console.error(`Error: ${body.error ?? `upload failed (${res.status})`}`);\n process.exit(1);\n }\n\n const [created] = body.data;\n\n if (jsonOutput) {\n console.log(JSON.stringify(created, null, 2));\n return;\n }\n\n console.log(`\\u2713 Uploaded ${created.filename}`);\n console.log(` ID: ${created.id}`);\n console.log(` Type: ${created.contentType}`);\n console.log(` Size: ${formatSize(created.sizeBytes)}`);\n console.log('');\n console.log('Reference in a workflow node:');\n console.log(` configJson.attachmentIds = [\"${created.id}\"]`);\n}\n\nasync function handleGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const id = argv.find((a) => !a.startsWith('-'));\n if (!id) {\n console.error('Error: missing attachment ID.');\n console.error('Usage: canary attachment get <id> [--url]');\n process.exit(1);\n }\n\n const wantUrl = hasFlag(argv, '--url');\n const jsonOutput = hasFlag(argv, '--json');\n\n if (wantUrl) {\n const result = await apiRequest<UrlResponse>(\n apiUrl,\n token,\n 'GET',\n `/org/attachments/${id}/url`\n );\n if (!result.ok || !result.url) {\n console.error(`Error: ${result.error ?? 'failed to fetch signed URL'}`);\n process.exit(1);\n }\n if (jsonOutput) {\n console.log(JSON.stringify({ id, url: result.url }, null, 2));\n } else {\n console.log(result.url);\n }\n return;\n }\n\n // No dedicated GET by id metadata endpoint; use bulk resolve as the read path.\n const res = await fetch(`${apiUrl}/org/attachments/resolve`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({ ids: [id] }),\n });\n if (res.status === 401) {\n console.error('Error: Unauthorized. Run: canary login');\n process.exit(1);\n }\n const body = (await res.json()) as { ok: boolean; error?: string; data?: AttachmentListItem[] };\n if (!body.ok || !body.data || body.data.length === 0) {\n console.error(`Error: ${body.error ?? 'attachment not found'}`);\n process.exit(1);\n }\n const [att] = body.data;\n if (jsonOutput) {\n console.log(JSON.stringify(att, null, 2));\n return;\n }\n console.log(`${att.filename}`);\n console.log(` ID: ${att.id}`);\n console.log(` Type: ${att.contentType}`);\n console.log(` Size: ${formatSize(att.sizeBytes)}`);\n console.log(` Created: ${att.createdAt}`);\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const id = argv.find((a) => !a.startsWith('-'));\n if (!id) {\n console.error('Error: missing attachment ID.');\n console.error('Usage: canary attachment delete <id>');\n process.exit(1);\n }\n const jsonOutput = hasFlag(argv, '--json');\n\n const result = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'DELETE',\n `/org/attachments/${id}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error ?? 'delete failed'}`);\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify({ ok: true, id }, null, 2));\n return;\n }\n console.log(`\\u2713 Deleted attachment ${id}`);\n}\n\nexport async function runAttachment(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === 'help' || hasFlag(argv, '--help', '-h')) {\n printAttachmentHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(rest);\n\n switch (subCommand) {\n case 'list':\n await handleList(rest, apiUrl, token);\n return;\n case 'upload':\n await handleUpload(rest, apiUrl, token);\n return;\n case 'get':\n await handleGet(rest, apiUrl, token);\n return;\n case 'delete':\n await handleDelete(rest, apiUrl, token);\n return;\n default:\n console.error(`Unknown attachment sub-command: ${subCommand}`);\n printAttachmentHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
ENV_URLS,
|
|
4
|
+
envToProfile,
|
|
5
|
+
getArgValue,
|
|
6
|
+
hasFlag,
|
|
7
|
+
readAllStoredTokens,
|
|
8
|
+
readStoredApiUrl,
|
|
9
|
+
readStoredAuth,
|
|
10
|
+
readStoredToken,
|
|
11
|
+
resolveAppUrl,
|
|
12
|
+
resolveConfig,
|
|
13
|
+
resolveOptionalConfig,
|
|
14
|
+
saveAuth
|
|
15
|
+
} from "./chunk-A4OFTUVV.js";
|
|
16
|
+
import "./chunk-VKVL7WBN.js";
|
|
17
|
+
export {
|
|
18
|
+
ENV_URLS,
|
|
19
|
+
envToProfile,
|
|
20
|
+
getArgValue,
|
|
21
|
+
hasFlag,
|
|
22
|
+
readAllStoredTokens,
|
|
23
|
+
readStoredApiUrl,
|
|
24
|
+
readStoredAuth,
|
|
25
|
+
readStoredToken,
|
|
26
|
+
resolveAppUrl,
|
|
27
|
+
resolveConfig,
|
|
28
|
+
resolveOptionalConfig,
|
|
29
|
+
saveAuth
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=auth-EN6NQCJY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/bin.js
ADDED
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bin.ts"],"sourcesContent":["import { main } from \"./index.js\";\n\nvoid main(process.argv.slice(2));\n"],"mappings":";;;;AAAA,SAAS,YAAY;AAErB,KAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC;","names":[]}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/jwt.ts
|
|
4
|
+
function decodeJwtPayload(token) {
|
|
5
|
+
try {
|
|
6
|
+
const parts = token.split(".");
|
|
7
|
+
if (parts.length !== 3) return null;
|
|
8
|
+
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
9
|
+
const json = Buffer.from(base64, "base64").toString("utf8");
|
|
10
|
+
const payload = JSON.parse(json);
|
|
11
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return payload;
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function isSuperadminToken(token) {
|
|
20
|
+
if (!token) return false;
|
|
21
|
+
const payload = decodeJwtPayload(token);
|
|
22
|
+
return payload?.is_superadmin === true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/api-fetch.ts
|
|
26
|
+
import process from "process";
|
|
27
|
+
var CANARY_REQUEST_ID_HEADER = "x-canary-request-id";
|
|
28
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
29
|
+
var JWT_EXP_SKEW_SEC = 60;
|
|
30
|
+
function redactSecrets(text) {
|
|
31
|
+
return text.replace(/Bearer\s+[A-Za-z0-9._-]+/gi, "Bearer [redacted]").replace(/\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\b/g, "[redacted-jwt]");
|
|
32
|
+
}
|
|
33
|
+
function redactUrl(url) {
|
|
34
|
+
const q = url.indexOf("?");
|
|
35
|
+
return q === -1 ? url : `${url.slice(0, q)}?[redacted]`;
|
|
36
|
+
}
|
|
37
|
+
function isDebug() {
|
|
38
|
+
return process.env.CANARY_DEBUG === "1" || process.env.CANARY_DEBUG === "true" || process.argv.includes("--debug");
|
|
39
|
+
}
|
|
40
|
+
function debug(line) {
|
|
41
|
+
if (isDebug()) {
|
|
42
|
+
process.stderr.write(`[canary-debug] ${line}
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
var TLS_CODES = /* @__PURE__ */ new Set([
|
|
47
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
48
|
+
"UNABLE_TO_GET_ISSUER_CERT",
|
|
49
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
50
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
51
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
52
|
+
"CERT_HAS_EXPIRED",
|
|
53
|
+
"CERT_UNTRUSTED",
|
|
54
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
55
|
+
"CERT_SIGNATURE_FAILURE"
|
|
56
|
+
]);
|
|
57
|
+
function extractErrorCode(err) {
|
|
58
|
+
if (!err || typeof err !== "object") return null;
|
|
59
|
+
const withCode = err;
|
|
60
|
+
if (typeof withCode.code === "string") return withCode.code;
|
|
61
|
+
if (withCode.cause) return extractErrorCode(withCode.cause);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
function looksLikeHtml(body, contentType) {
|
|
65
|
+
if (contentType && contentType.toLowerCase().includes("text/html")) return true;
|
|
66
|
+
const trimmed = body.trimStart();
|
|
67
|
+
return trimmed.startsWith("<!") || trimmed.startsWith("<html") || trimmed.startsWith("<HTML");
|
|
68
|
+
}
|
|
69
|
+
function jwtExpStatus(token) {
|
|
70
|
+
if (!token) return "unknown";
|
|
71
|
+
const payload = decodeJwtPayload(token);
|
|
72
|
+
const exp = payload && typeof payload.exp === "number" ? payload.exp : null;
|
|
73
|
+
if (exp === null) return "unknown";
|
|
74
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
75
|
+
return exp < nowSec - JWT_EXP_SKEW_SEC ? "expired" : "valid";
|
|
76
|
+
}
|
|
77
|
+
var warnedAboutPlaintext = false;
|
|
78
|
+
function maybeWarnPlaintextAuth(url, hasAuthHeader2) {
|
|
79
|
+
if (!hasAuthHeader2 || warnedAboutPlaintext) return;
|
|
80
|
+
if (!url.startsWith("http://")) return;
|
|
81
|
+
try {
|
|
82
|
+
const host = new URL(url).hostname;
|
|
83
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1") return;
|
|
84
|
+
} catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
warnedAboutPlaintext = true;
|
|
88
|
+
process.stderr.write(
|
|
89
|
+
`[canary] WARNING: sending Authorization over plaintext http:// (${url}). Your API token may be exposed to the network. Use https:// in CANARY_API_URL.
|
|
90
|
+
`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
function hasAuthHeader(init) {
|
|
94
|
+
const h = init.headers;
|
|
95
|
+
if (!h) return false;
|
|
96
|
+
if (h instanceof Headers) return h.has("authorization");
|
|
97
|
+
if (Array.isArray(h)) return h.some(([k]) => k.toLowerCase() === "authorization");
|
|
98
|
+
return Object.keys(h).some((k) => k.toLowerCase() === "authorization");
|
|
99
|
+
}
|
|
100
|
+
async function apiFetch(url, init = {}) {
|
|
101
|
+
const { token, timeoutMs, ...fetchInit } = init;
|
|
102
|
+
maybeWarnPlaintextAuth(url, hasAuthHeader(fetchInit));
|
|
103
|
+
debug(`\u2192 ${fetchInit.method ?? "GET"} ${redactUrl(url)}`);
|
|
104
|
+
const timeout = timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
105
|
+
const timeoutSignal = timeout > 0 ? AbortSignal.timeout(timeout) : null;
|
|
106
|
+
const effectiveSignal = fetchInit.signal && timeoutSignal ? AbortSignal.any([fetchInit.signal, timeoutSignal]) : fetchInit.signal ?? timeoutSignal ?? void 0;
|
|
107
|
+
let response;
|
|
108
|
+
try {
|
|
109
|
+
response = await fetch(url, { ...fetchInit, signal: effectiveSignal });
|
|
110
|
+
} catch (err) {
|
|
111
|
+
const isTimeout = err?.name === "TimeoutError" || Boolean(timeoutSignal?.aborted);
|
|
112
|
+
if (isTimeout) {
|
|
113
|
+
const msg2 = `Request to ${redactUrl(url)} timed out after ${timeout}ms.`;
|
|
114
|
+
debug(`\u2716 network timeout`);
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
failure: { kind: "network", status: null, url, message: msg2, cause: err }
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const code = extractErrorCode(err);
|
|
121
|
+
if (code && TLS_CODES.has(code)) {
|
|
122
|
+
const msg2 = [
|
|
123
|
+
`TLS verification failed talking to ${redactUrl(url)} (${code}).`,
|
|
124
|
+
"A corporate proxy (Zscaler, Netskope, ZPA, Cloudflare Access) is likely intercepting HTTPS.",
|
|
125
|
+
"Fixes: point Node at the proxy's root CA with",
|
|
126
|
+
" NODE_EXTRA_CA_CERTS=/path/to/corporate-root.pem canary \u2026",
|
|
127
|
+
"or on Node 20+ enable the system trust store with",
|
|
128
|
+
" NODE_OPTIONS=--use-system-ca canary \u2026"
|
|
129
|
+
].join("\n");
|
|
130
|
+
debug(`\u2716 tls ${code}`);
|
|
131
|
+
return { ok: false, failure: { kind: "tls", status: null, url, message: msg2, cause: err } };
|
|
132
|
+
}
|
|
133
|
+
const msg = `Network error reaching ${redactUrl(url)}: ${String(err.message ?? err)}`;
|
|
134
|
+
debug(`\u2716 network ${msg}`);
|
|
135
|
+
return { ok: false, failure: { kind: "network", status: null, url, message: msg, cause: err } };
|
|
136
|
+
}
|
|
137
|
+
const requestId = response.headers.get(CANARY_REQUEST_ID_HEADER);
|
|
138
|
+
const contentType = response.headers.get("content-type");
|
|
139
|
+
debug(`\u2190 ${response.status} ${redactUrl(url)} (ct=${contentType ?? "n/a"} rid=${requestId ?? "none"})`);
|
|
140
|
+
if (response.ok) {
|
|
141
|
+
return { ok: true, response, requestId };
|
|
142
|
+
}
|
|
143
|
+
const bodyText = await response.clone().text().catch(() => "");
|
|
144
|
+
const snippet = redactSecrets(bodyText.slice(0, 200));
|
|
145
|
+
if (looksLikeHtml(bodyText, contentType)) {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
failure: {
|
|
149
|
+
kind: "html-body",
|
|
150
|
+
status: response.status,
|
|
151
|
+
url,
|
|
152
|
+
contentType: contentType ?? void 0,
|
|
153
|
+
requestId,
|
|
154
|
+
body: snippet,
|
|
155
|
+
message: [
|
|
156
|
+
`Got an HTML response from ${url} (status ${response.status}).`,
|
|
157
|
+
"A corporate proxy block page or captive portal is intercepting this request \u2014 the",
|
|
158
|
+
"response did not come from the Canary API.",
|
|
159
|
+
"Check that your proxy (e.g. Zscaler) allowlists the API hostname, and that any",
|
|
160
|
+
"required sign-in to the proxy itself is current.",
|
|
161
|
+
snippet ? `First bytes: ${snippet.replace(/\s+/g, " ")}` : ""
|
|
162
|
+
].filter(Boolean).join("\n")
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if ((response.status === 401 || response.status === 403) && !requestId) {
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
failure: {
|
|
170
|
+
kind: "proxy-injected-4xx",
|
|
171
|
+
status: response.status,
|
|
172
|
+
url,
|
|
173
|
+
contentType: contentType ?? void 0,
|
|
174
|
+
requestId,
|
|
175
|
+
body: snippet,
|
|
176
|
+
message: [
|
|
177
|
+
`Got ${response.status} from ${url} but the response is missing the \`${CANARY_REQUEST_ID_HEADER}\` header.`,
|
|
178
|
+
"This response was almost certainly injected by a corporate proxy (Zscaler, Netskope,",
|
|
179
|
+
'Cloudflare Access) rather than returned by our API \u2014 retrying "canary login" will not fix it.',
|
|
180
|
+
"Ask your IT team to allowlist the API host, or re-auth to the proxy and try again."
|
|
181
|
+
].join("\n")
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (response.status === 401) {
|
|
186
|
+
const jwtStatus = jwtExpStatus(token);
|
|
187
|
+
if (jwtStatus === "valid") {
|
|
188
|
+
return {
|
|
189
|
+
ok: false,
|
|
190
|
+
failure: {
|
|
191
|
+
kind: "token-revoked",
|
|
192
|
+
status: 401,
|
|
193
|
+
url,
|
|
194
|
+
requestId,
|
|
195
|
+
body: snippet,
|
|
196
|
+
message: [
|
|
197
|
+
"Server rejected a token that is still valid locally (unexpired JWT).",
|
|
198
|
+
"Likely causes: the token was revoked, the server's signing key rotated, or the",
|
|
199
|
+
"user/org was deactivated. `canary login` will issue a fresh token."
|
|
200
|
+
].join("\n")
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (jwtStatus === "expired") {
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
failure: {
|
|
208
|
+
kind: "token-expired",
|
|
209
|
+
status: 401,
|
|
210
|
+
url,
|
|
211
|
+
requestId,
|
|
212
|
+
body: snippet,
|
|
213
|
+
message: "Your stored token has expired. Run: canary login"
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
ok: false,
|
|
219
|
+
failure: {
|
|
220
|
+
kind: "unauthorized",
|
|
221
|
+
status: 401,
|
|
222
|
+
url,
|
|
223
|
+
requestId,
|
|
224
|
+
body: snippet,
|
|
225
|
+
message: "Unauthorized. Run: canary login"
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
ok: false,
|
|
231
|
+
failure: {
|
|
232
|
+
kind: "http-error",
|
|
233
|
+
status: response.status,
|
|
234
|
+
url,
|
|
235
|
+
contentType: contentType ?? void 0,
|
|
236
|
+
requestId,
|
|
237
|
+
body: bodyText,
|
|
238
|
+
message: `${response.status} ${response.statusText}${snippet ? `: ${snippet}` : ""}`
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function isTerminalFailure(failure) {
|
|
243
|
+
return failure.kind !== "http-error";
|
|
244
|
+
}
|
|
245
|
+
function exitOnFailure(failure) {
|
|
246
|
+
process.stderr.write(`Error: ${failure.message}
|
|
247
|
+
`);
|
|
248
|
+
if (isDebug() && failure.body && failure.kind !== "html-body") {
|
|
249
|
+
process.stderr.write(`[canary-debug] body: ${failure.body}
|
|
250
|
+
`);
|
|
251
|
+
}
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
async function fetchOrExit(url, init = {}) {
|
|
255
|
+
const result = await apiFetch(url, init);
|
|
256
|
+
if (result.ok) return result.response;
|
|
257
|
+
if (isTerminalFailure(result.failure)) exitOnFailure(result.failure);
|
|
258
|
+
return new Response(result.failure.body ?? "", {
|
|
259
|
+
status: result.failure.status ?? 500,
|
|
260
|
+
headers: result.failure.contentType ? { "content-type": result.failure.contentType } : void 0
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export {
|
|
265
|
+
decodeJwtPayload,
|
|
266
|
+
isSuperadminToken,
|
|
267
|
+
apiFetch,
|
|
268
|
+
isTerminalFailure,
|
|
269
|
+
exitOnFailure,
|
|
270
|
+
fetchOrExit
|
|
271
|
+
};
|
|
272
|
+
//# sourceMappingURL=chunk-3CZU7XCS.js.map
|