@islamihab/kds 0.11.0 → 0.13.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/index.js +474 -148
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -14623,7 +14623,7 @@ import { join } from "path";
|
|
|
14623
14623
|
// package.json
|
|
14624
14624
|
var package_default = {
|
|
14625
14625
|
name: "cli",
|
|
14626
|
-
version: "0.
|
|
14626
|
+
version: "0.13.0",
|
|
14627
14627
|
private: true,
|
|
14628
14628
|
type: "module",
|
|
14629
14629
|
bin: {
|
|
@@ -15196,9 +15196,22 @@ var components = componentsGeneric();
|
|
|
15196
15196
|
|
|
15197
15197
|
// ../../packages/backend/convex/constants.ts
|
|
15198
15198
|
var KDS_DEVICE_AUTH_CLIENT_ID = "kds-cli";
|
|
15199
|
-
var
|
|
15199
|
+
var PAGE_STYLES = {
|
|
15200
|
+
basic: {
|
|
15201
|
+
description: "a report in the house theme with a light/dark toggle and the credit line, where you write only what goes inside the page"
|
|
15202
|
+
},
|
|
15203
|
+
mockup: {
|
|
15204
|
+
description: "an interactive prototype where the fragment is the screen of a phone, its own <style> and <script> tags do the rest, it fills the display on a real phone, and its manifest and apple-touch-icon install it under its own name and icon"
|
|
15205
|
+
},
|
|
15206
|
+
unthemed: {
|
|
15207
|
+
description: "a blank document where the fragment is served as written with nothing added, so its own <style> and <script> tags decide everything"
|
|
15208
|
+
}
|
|
15209
|
+
};
|
|
15200
15210
|
var PAGE_VISIBILITIES = ["public", "private"];
|
|
15201
15211
|
var MAX_PAGE_HTML_BYTES = 4000000;
|
|
15212
|
+
var MAX_PAGE_VERSIONS = 20;
|
|
15213
|
+
var FILE_VISIBILITIES = ["public", "private"];
|
|
15214
|
+
var MAX_FILE_BYTES = 20000000;
|
|
15202
15215
|
var ISSUE_STATUSES = [
|
|
15203
15216
|
"backlog",
|
|
15204
15217
|
"todo",
|
|
@@ -15368,7 +15381,16 @@ var issueIsOpen = (status) => !ISSUE_TERMINAL_STATUSES.some((terminal) => termin
|
|
|
15368
15381
|
var CURRENCIES = ["EGP", "USD", "EUR", "GBP", "AED", "SAR"];
|
|
15369
15382
|
var DEFAULT_CURRENCY = "EGP";
|
|
15370
15383
|
var RECURRING_CHARGE_CADENCES = ["monthly", "yearly"];
|
|
15384
|
+
var RECURRING_CHARGE_CADENCE_LABELS = {
|
|
15385
|
+
monthly: "Monthly",
|
|
15386
|
+
yearly: "Yearly"
|
|
15387
|
+
};
|
|
15371
15388
|
var COST_KINDS = ["one_off", "monthly", "yearly"];
|
|
15389
|
+
var COST_KIND_LABELS = {
|
|
15390
|
+
one_off: "One-off",
|
|
15391
|
+
monthly: "Monthly",
|
|
15392
|
+
yearly: "Yearly"
|
|
15393
|
+
};
|
|
15372
15394
|
var MAX_PROJECT_REPO_LENGTH = 200;
|
|
15373
15395
|
var INVOICE_STATUSES = ["draft", "sent", "paid", "void"];
|
|
15374
15396
|
var invoiceLineTotal = ({ quantity, unitPrice }) => {
|
|
@@ -17346,6 +17368,7 @@ var localDateString = (timestamp) => {
|
|
|
17346
17368
|
return `${date5.getFullYear()}-${month}-${day}`;
|
|
17347
17369
|
};
|
|
17348
17370
|
var localToday = () => localDateString(Date.now());
|
|
17371
|
+
var describeChoices = (choices, separator = "; ") => Object.entries(choices).map(([key, { description }]) => `${key}: ${description}`).join(separator);
|
|
17349
17372
|
|
|
17350
17373
|
// src/lib/zod.ts
|
|
17351
17374
|
var configSchema = exports_external.object({ sessionToken: exports_external.string().optional(), convexUrl: exports_external.url(), convexSiteUrl: exports_external.url() });
|
|
@@ -24120,6 +24143,10 @@ var issueActivityLine = (detail) => {
|
|
|
24120
24143
|
case "attachment_added":
|
|
24121
24144
|
case "attachment_removed":
|
|
24122
24145
|
return `${label}: ${detail.name}`;
|
|
24146
|
+
case "page_linked":
|
|
24147
|
+
case "page_unlinked":
|
|
24148
|
+
case "page_updated":
|
|
24149
|
+
return `${label}: ${detail.title}`;
|
|
24123
24150
|
case "child_added":
|
|
24124
24151
|
case "child_removed":
|
|
24125
24152
|
return `${label}: ${detail.identifier}`;
|
|
@@ -24264,6 +24291,15 @@ var readPageId = (value) => {
|
|
|
24264
24291
|
return trimmed;
|
|
24265
24292
|
}
|
|
24266
24293
|
};
|
|
24294
|
+
var readFileRef = (value) => {
|
|
24295
|
+
const trimmed = value.trim();
|
|
24296
|
+
try {
|
|
24297
|
+
const url2 = new URL(trimmed);
|
|
24298
|
+
return url2.pathname.match(/\/f\/([^/]+)\/?$/)?.[1] ?? trimmed;
|
|
24299
|
+
} catch {
|
|
24300
|
+
return trimmed;
|
|
24301
|
+
}
|
|
24302
|
+
};
|
|
24267
24303
|
|
|
24268
24304
|
// src/commands/clients/archive.ts
|
|
24269
24305
|
var archive = command({
|
|
@@ -24413,15 +24449,6 @@ var repoProject = async (client3) => {
|
|
|
24413
24449
|
throw new Error(`No project is connected to ${repo}. Connect one on the project in the dashboard.`);
|
|
24414
24450
|
return project;
|
|
24415
24451
|
};
|
|
24416
|
-
var resolveMilestone = async (client3, projectId, name) => {
|
|
24417
|
-
const milestone = await client3.query(api2.milestones.find, { projectId, name });
|
|
24418
|
-
if (!milestone) {
|
|
24419
|
-
const milestones = await client3.query(api2.milestones.listByProject, { projectId, today: localToday() });
|
|
24420
|
-
const names = milestones.map((candidate) => candidate.name).join(", ");
|
|
24421
|
-
throw new Error(names ? `No milestone named "${name}". The project has: ${names}.` : "The project has no milestones.");
|
|
24422
|
-
}
|
|
24423
|
-
return milestone;
|
|
24424
|
-
};
|
|
24425
24452
|
|
|
24426
24453
|
// src/commands/clients/charges/list.ts
|
|
24427
24454
|
var monthName = (month) => new Date(Date.UTC(2000, month - 1)).toLocaleString("en-US", { month: "short", timeZone: "UTC" });
|
|
@@ -24691,9 +24718,10 @@ var create2 = command({
|
|
|
24691
24718
|
name: "create",
|
|
24692
24719
|
description: "Record a cost and print its id",
|
|
24693
24720
|
longDescription: `One record per real-world bill, never one per project. A cost links to any number
|
|
24694
|
-
of projects, each carrying the full amount;
|
|
24695
|
-
|
|
24696
|
-
the
|
|
24721
|
+
of projects, each carrying the full amount; until a client's project carries it, it
|
|
24722
|
+
is overhead. Billable
|
|
24723
|
+
costs pass through onto the invoices of whichever client their projects belong to,
|
|
24724
|
+
so the links cannot span two clients; with no links, billable waits for one.`,
|
|
24697
24725
|
positionals: {
|
|
24698
24726
|
name: exports_external.string().describe('What the bill is for, e.g. "Figma seats"')
|
|
24699
24727
|
},
|
|
@@ -24750,9 +24778,10 @@ var costKindLabel = (cost) => {
|
|
|
24750
24778
|
return cost.endDate === undefined ? cost.kind : `${cost.kind} \u2192 ${cost.endDate}`;
|
|
24751
24779
|
};
|
|
24752
24780
|
var costProjectsLabel = (cost) => {
|
|
24753
|
-
|
|
24754
|
-
|
|
24755
|
-
|
|
24781
|
+
const names = cost.projects.map((project) => project.name).join(", ");
|
|
24782
|
+
if (!cost.overhead)
|
|
24783
|
+
return names;
|
|
24784
|
+
return names === "" ? "overhead" : `overhead (${names})`;
|
|
24756
24785
|
};
|
|
24757
24786
|
|
|
24758
24787
|
// src/commands/costs/get.ts
|
|
@@ -24800,7 +24829,7 @@ after an edit. A yearly cost lands whole in its renewal month, never amortized.`
|
|
|
24800
24829
|
month: exports_external.string().optional().describe("Derive one month's costs (YYYY-MM)").meta({ short: "m" }),
|
|
24801
24830
|
kind: exports_external.enum(COST_KINDS).optional().describe("Only one_off, monthly, or yearly").meta({ short: "k" }),
|
|
24802
24831
|
project: exports_external.string().optional().describe("Only costs linked to this project (id, URL, or repo)").meta({ short: "p" }),
|
|
24803
|
-
overhead: exports_external.boolean().default(false).describe("Only costs
|
|
24832
|
+
overhead: exports_external.boolean().default(false).describe("Only costs no client's project carries"),
|
|
24804
24833
|
billable: exports_external.boolean().optional().describe("Only billable costs, or --no-billable").meta({ negatable: true }),
|
|
24805
24834
|
limit: exports_external.coerce.number().int().positive().default(50).describe("Most costs to print"),
|
|
24806
24835
|
json: exports_external.boolean().default(false).describe("Print as JSON")
|
|
@@ -24908,7 +24937,7 @@ var costs = group({
|
|
|
24908
24937
|
description: "Manage costs",
|
|
24909
24938
|
longDescription: `A cost is money the studio spends, recorded once per real-world bill. It links to any
|
|
24910
24939
|
number of projects \u2014 each carrying the full amount, so per-project figures never sum
|
|
24911
|
-
to the studio total
|
|
24940
|
+
to the studio total. A cost no client's project carries is overhead. Occurrences are always derived
|
|
24912
24941
|
from the record, so \`costs list --month\` answers for any month, past or future.`,
|
|
24913
24942
|
commands: [create2, list3, get2, update3, end, remove2]
|
|
24914
24943
|
});
|
|
@@ -25153,6 +25182,186 @@ var daemon = group({
|
|
|
25153
25182
|
commands: [start, stop, status2, logs]
|
|
25154
25183
|
});
|
|
25155
25184
|
|
|
25185
|
+
// src/commands/files/create.ts
|
|
25186
|
+
import { basename } from "path";
|
|
25187
|
+
|
|
25188
|
+
// src/lib/upload.ts
|
|
25189
|
+
var localFile = async (path, maxBytes) => {
|
|
25190
|
+
const file2 = Bun.file(path);
|
|
25191
|
+
if (!await file2.exists())
|
|
25192
|
+
throw new Error(`No file at ${path}.`);
|
|
25193
|
+
if (file2.size <= 0)
|
|
25194
|
+
throw new Error(`${path} is empty.`);
|
|
25195
|
+
if (file2.size > maxBytes)
|
|
25196
|
+
throw new Error(`${path} is too large (${file2.size} bytes; max ${maxBytes}).`);
|
|
25197
|
+
return file2;
|
|
25198
|
+
};
|
|
25199
|
+
var uploadFile = async (path, file2, uploadUrl) => {
|
|
25200
|
+
const response = await fetch(uploadUrl, { method: "POST", headers: { "Content-Type": file2.type }, body: file2 });
|
|
25201
|
+
if (!response.ok)
|
|
25202
|
+
throw new Error(`Uploading ${path} failed (${response.status}).`);
|
|
25203
|
+
const storageId = exports_external.custom((value) => typeof value === "string");
|
|
25204
|
+
const body = exports_external.object({ storageId }).safeParse(await response.json());
|
|
25205
|
+
if (!body.success)
|
|
25206
|
+
throw new Error("The upload returned no file reference.");
|
|
25207
|
+
return body.data.storageId;
|
|
25208
|
+
};
|
|
25209
|
+
|
|
25210
|
+
// src/commands/files/create.ts
|
|
25211
|
+
var create3 = command({
|
|
25212
|
+
name: "create",
|
|
25213
|
+
description: "Upload one or more files and print their URLs",
|
|
25214
|
+
positionals: {
|
|
25215
|
+
files: exports_external.array(exports_external.string()).min(1).describe("Files to upload")
|
|
25216
|
+
},
|
|
25217
|
+
options: {
|
|
25218
|
+
folder: exports_external.string().optional().describe("Folder to file them under, such as screenshots/2026-09").meta({ short: "f" }),
|
|
25219
|
+
private: exports_external.boolean().default(false).describe("Keep the file to this account: no share URL is issued")
|
|
25220
|
+
},
|
|
25221
|
+
run: async ({ positionals: { files: paths }, options: { folder, private: isPrivate } }) => {
|
|
25222
|
+
const files = await Promise.all(paths.map(async (path) => ({ path, file: await localFile(path, MAX_FILE_BYTES) })));
|
|
25223
|
+
const client3 = await backendClient();
|
|
25224
|
+
const results = await Promise.allSettled(files.map(async ({ path, file: file2 }) => {
|
|
25225
|
+
const uploadUrl = await client3.mutation(api2.files.generateUploadUrl, {});
|
|
25226
|
+
const storageId = await uploadFile(path, file2, uploadUrl);
|
|
25227
|
+
return await client3.action(api2.files.create, {
|
|
25228
|
+
storageId,
|
|
25229
|
+
name: basename(path),
|
|
25230
|
+
folder,
|
|
25231
|
+
visibility: isPrivate ? "private" : "public"
|
|
25232
|
+
});
|
|
25233
|
+
}));
|
|
25234
|
+
for (const result of results) {
|
|
25235
|
+
if (result.status === "fulfilled")
|
|
25236
|
+
console.log(result.value.url ?? result.value.downloadUrl ?? result.value.id);
|
|
25237
|
+
}
|
|
25238
|
+
const failed = results.find((result) => result.status === "rejected");
|
|
25239
|
+
if (failed)
|
|
25240
|
+
throw failed.reason;
|
|
25241
|
+
}
|
|
25242
|
+
});
|
|
25243
|
+
|
|
25244
|
+
// ../../packages/backend/convex/lib/format.ts
|
|
25245
|
+
var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${Math.round(bytes / 1024)} KB`;
|
|
25246
|
+
|
|
25247
|
+
// src/commands/files/get.ts
|
|
25248
|
+
var get3 = command({
|
|
25249
|
+
name: "get",
|
|
25250
|
+
description: "Show a file's details and links",
|
|
25251
|
+
positionals: {
|
|
25252
|
+
id: exports_external.string().describe("File id or URL")
|
|
25253
|
+
},
|
|
25254
|
+
options: {
|
|
25255
|
+
json: exports_external.boolean().default(false).describe("Print as JSON")
|
|
25256
|
+
},
|
|
25257
|
+
run: async ({ positionals: { id }, options: { json: json2 } }) => {
|
|
25258
|
+
const file2 = await (await backendClient()).query(api2.files.get, { ref: readFileRef(id) });
|
|
25259
|
+
if (json2)
|
|
25260
|
+
return console.log(JSON.stringify(file2, null, 2));
|
|
25261
|
+
console.log(`Name: ${file2.name}`);
|
|
25262
|
+
console.log(`Folder: ${file2.folder ?? "-"}`);
|
|
25263
|
+
console.log(`Access: ${file2.visibility}`);
|
|
25264
|
+
console.log(`Type: ${file2.mimeType}`);
|
|
25265
|
+
console.log(`Size: ${formatBytes(file2.size)}`);
|
|
25266
|
+
console.log(`Created: ${new Date(file2.createdAt).toISOString()}`);
|
|
25267
|
+
console.log(`Id: ${file2.id}`);
|
|
25268
|
+
console.log(`URL: ${file2.url ?? "- (private)"}`);
|
|
25269
|
+
console.log(`Download: ${file2.downloadUrl ?? "-"}`);
|
|
25270
|
+
}
|
|
25271
|
+
});
|
|
25272
|
+
|
|
25273
|
+
// src/commands/files/list.ts
|
|
25274
|
+
var list4 = command({
|
|
25275
|
+
name: "list",
|
|
25276
|
+
description: "List your files, or one folder and everything beneath it",
|
|
25277
|
+
positionals: {
|
|
25278
|
+
folder: exports_external.string().optional().describe("Folder path to list")
|
|
25279
|
+
},
|
|
25280
|
+
options: {
|
|
25281
|
+
json: exports_external.boolean().default(false).describe("Print as JSON")
|
|
25282
|
+
},
|
|
25283
|
+
run: async ({ positionals: { folder }, options: { json: json2 } }) => {
|
|
25284
|
+
const files = await (await backendClient()).query(api2.files.list, { folder });
|
|
25285
|
+
if (json2)
|
|
25286
|
+
return console.log(JSON.stringify(files, null, 2));
|
|
25287
|
+
if (files.length === 0)
|
|
25288
|
+
return console.log(folder === undefined ? "No files yet." : "No files in that folder.");
|
|
25289
|
+
printTable([
|
|
25290
|
+
["NAME", "FOLDER", "ACCESS", "TYPE", "SIZE", "CREATED", "URL"],
|
|
25291
|
+
...files.map((file2) => [
|
|
25292
|
+
file2.name,
|
|
25293
|
+
file2.folder ?? "-",
|
|
25294
|
+
file2.visibility,
|
|
25295
|
+
file2.mimeType,
|
|
25296
|
+
formatBytes(file2.size),
|
|
25297
|
+
new Date(file2.createdAt).toISOString().slice(0, 10),
|
|
25298
|
+
file2.url ?? file2.id
|
|
25299
|
+
])
|
|
25300
|
+
]);
|
|
25301
|
+
}
|
|
25302
|
+
});
|
|
25303
|
+
|
|
25304
|
+
// src/commands/files/remove.ts
|
|
25305
|
+
var remove3 = command({
|
|
25306
|
+
name: "delete",
|
|
25307
|
+
description: "Delete a file",
|
|
25308
|
+
positionals: {
|
|
25309
|
+
id: exports_external.string().describe("File id or URL")
|
|
25310
|
+
},
|
|
25311
|
+
run: async ({ positionals: { id } }) => {
|
|
25312
|
+
await (await backendClient()).mutation(api2.files.remove, { ref: readFileRef(id) });
|
|
25313
|
+
console.log("Deleted.");
|
|
25314
|
+
}
|
|
25315
|
+
});
|
|
25316
|
+
|
|
25317
|
+
// src/commands/files/rotate-link.ts
|
|
25318
|
+
var rotateLink = command({
|
|
25319
|
+
name: "rotate-link",
|
|
25320
|
+
description: "Reissue a file's share URL; every link handed out so far stops working",
|
|
25321
|
+
positionals: {
|
|
25322
|
+
id: exports_external.string().describe("File id or URL")
|
|
25323
|
+
},
|
|
25324
|
+
run: async ({ positionals: { id } }) => {
|
|
25325
|
+
const file2 = await (await backendClient()).mutation(api2.files.rotateLink, { ref: readFileRef(id) });
|
|
25326
|
+
console.log(file2.url ?? "Rotated; the file is private, so no share URL is issued.");
|
|
25327
|
+
}
|
|
25328
|
+
});
|
|
25329
|
+
|
|
25330
|
+
// src/commands/files/update.ts
|
|
25331
|
+
var update4 = command({
|
|
25332
|
+
name: "update",
|
|
25333
|
+
description: "Rename a file, move it to a folder, or change who can reach it",
|
|
25334
|
+
positionals: {
|
|
25335
|
+
id: exports_external.string().describe("File id or URL")
|
|
25336
|
+
},
|
|
25337
|
+
options: {
|
|
25338
|
+
name: exports_external.string().optional().describe("New file name").meta({ short: "n" }),
|
|
25339
|
+
folder: exports_external.string().nullable().optional().describe("Move the file to this folder, or --no-folder to move it to the root").meta({ short: "f", negatable: true }),
|
|
25340
|
+
visibility: exports_external.enum(FILE_VISIBILITIES).optional().describe("Who can reach the file")
|
|
25341
|
+
},
|
|
25342
|
+
run: async ({ positionals: { id }, options: { name, folder, visibility } }) => {
|
|
25343
|
+
if (name === undefined && folder === undefined && visibility === undefined)
|
|
25344
|
+
throw new Error("Nothing to update. Pass --name, --folder, --no-folder, or --visibility.");
|
|
25345
|
+
const file2 = await (await backendClient()).mutation(api2.files.update, {
|
|
25346
|
+
ref: readFileRef(id),
|
|
25347
|
+
name,
|
|
25348
|
+
folder,
|
|
25349
|
+
visibility
|
|
25350
|
+
});
|
|
25351
|
+
console.log(file2.url ?? "Updated.");
|
|
25352
|
+
}
|
|
25353
|
+
});
|
|
25354
|
+
|
|
25355
|
+
// src/commands/files/index.ts
|
|
25356
|
+
var files = group({
|
|
25357
|
+
name: "files",
|
|
25358
|
+
description: "Share files at a URL",
|
|
25359
|
+
longDescription: `A file is uploaded as it is and reached at a share URL that renders images, video, audio,
|
|
25360
|
+
PDF and plain text in the browser and downloads everything else. Folders are paths such as
|
|
25361
|
+
screenshots/2026-09; a private file has no share URL and is reached from this account only.`,
|
|
25362
|
+
commands: [create3, list4, get3, update4, rotateLink, remove3]
|
|
25363
|
+
});
|
|
25364
|
+
|
|
25156
25365
|
// src/commands/inbox/list.ts
|
|
25157
25366
|
var inboxOptions = {
|
|
25158
25367
|
all: exports_external.boolean().default(false).describe("Include events already marked seen"),
|
|
@@ -25252,10 +25461,34 @@ var parseQuotationLineItem = (value) => {
|
|
|
25252
25461
|
const section = parts.length === 5 ? parts.pop()?.trim() : undefined;
|
|
25253
25462
|
return { ...parseLineItem(parts.join("|")), section: section || undefined };
|
|
25254
25463
|
};
|
|
25464
|
+
var EXPECTED_COST_SYNTAX = 'name|amount|kind[|currency], kind one of one_off, monthly, yearly, e.g. "Hosting|2500|monthly|USD"; currency defaults to EGP';
|
|
25465
|
+
var RECURRING_FEE_SYNTAX = 'description|amount|cadence[|currency], cadence monthly or yearly, e.g. "Maintenance|50000|monthly|USD"; currency defaults to EGP';
|
|
25466
|
+
var parseCommitment = (value, syntax, kinds) => {
|
|
25467
|
+
const parts = value.split("|");
|
|
25468
|
+
if (parts.length !== 3 && parts.length !== 4)
|
|
25469
|
+
throw new Error(`Entries read as ${syntax}; got "${value}".`);
|
|
25470
|
+
const [label = "", amountPart = "", kindPart = "", currency = ""] = parts;
|
|
25471
|
+
const amount = Number(amountPart.trim());
|
|
25472
|
+
if (!amountPart.trim() || !Number.isSafeInteger(amount)) {
|
|
25473
|
+
throw new Error(`Amounts must be integer minor units (10000 = 100.00), got "${amountPart.trim()}".`);
|
|
25474
|
+
}
|
|
25475
|
+
const kind = kinds.find((candidate) => candidate === kindPart.trim());
|
|
25476
|
+
if (kind === undefined)
|
|
25477
|
+
throw new Error(`Expected one of ${kinds.join(", ")}, got "${kindPart.trim()}".`);
|
|
25478
|
+
return { label: label.trim(), amount, kind, currency: parseLineCurrency(currency) };
|
|
25479
|
+
};
|
|
25480
|
+
var parseExpectedCost = (value) => {
|
|
25481
|
+
const { label, kind, ...rest } = parseCommitment(value, EXPECTED_COST_SYNTAX, COST_KINDS);
|
|
25482
|
+
return { name: label, kind, ...rest };
|
|
25483
|
+
};
|
|
25484
|
+
var parseRecurringFee = (value) => {
|
|
25485
|
+
const { label, kind, ...rest } = parseCommitment(value, RECURRING_FEE_SYNTAX, RECURRING_CHARGE_CADENCES);
|
|
25486
|
+
return { description: label, cadence: kind, ...rest };
|
|
25487
|
+
};
|
|
25255
25488
|
var formatPerCurrency = (entries) => entries.length === 0 ? "-" : entries.map(({ amount, currency }) => formatMinorAmount(amount, currency)).join(" + ");
|
|
25256
25489
|
|
|
25257
25490
|
// src/commands/invoices/create.ts
|
|
25258
|
-
var
|
|
25491
|
+
var create4 = command({
|
|
25259
25492
|
name: "create",
|
|
25260
25493
|
description: "Create a draft invoice for a client and print its id",
|
|
25261
25494
|
positionals: {
|
|
@@ -25308,7 +25541,7 @@ var email3 = command({
|
|
|
25308
25541
|
});
|
|
25309
25542
|
|
|
25310
25543
|
// src/commands/invoices/get.ts
|
|
25311
|
-
var
|
|
25544
|
+
var get4 = command({
|
|
25312
25545
|
name: "get",
|
|
25313
25546
|
description: "Show an invoice: document, totals, and payments",
|
|
25314
25547
|
positionals: {
|
|
@@ -25398,7 +25631,7 @@ var link = command({
|
|
|
25398
25631
|
});
|
|
25399
25632
|
|
|
25400
25633
|
// src/commands/invoices/list.ts
|
|
25401
|
-
var
|
|
25634
|
+
var list5 = command({
|
|
25402
25635
|
name: "list",
|
|
25403
25636
|
description: "List your invoices, newest first",
|
|
25404
25637
|
options: {
|
|
@@ -25468,7 +25701,7 @@ var add2 = command({
|
|
|
25468
25701
|
});
|
|
25469
25702
|
|
|
25470
25703
|
// src/commands/invoices/payments/list.ts
|
|
25471
|
-
var
|
|
25704
|
+
var list6 = command({
|
|
25472
25705
|
name: "list",
|
|
25473
25706
|
description: "List the payments recorded on an invoice, oldest first",
|
|
25474
25707
|
positionals: {
|
|
@@ -25496,7 +25729,7 @@ var list5 = command({
|
|
|
25496
25729
|
});
|
|
25497
25730
|
|
|
25498
25731
|
// src/commands/invoices/payments/remove.ts
|
|
25499
|
-
var
|
|
25732
|
+
var remove4 = command({
|
|
25500
25733
|
name: "remove",
|
|
25501
25734
|
description: "Remove a recorded payment (unflips paid when the invoice is no longer covered)",
|
|
25502
25735
|
positionals: {
|
|
@@ -25516,11 +25749,11 @@ var payments = group({
|
|
|
25516
25749
|
longDescription: `A payment is money received against a sent invoice, in one currency. Paid flips
|
|
25517
25750
|
automatically once every currency's total is covered; overpayment is rejected,
|
|
25518
25751
|
and forgiving a shortfall is a payment with a note.`,
|
|
25519
|
-
commands: [add2,
|
|
25752
|
+
commands: [add2, list6, remove4]
|
|
25520
25753
|
});
|
|
25521
25754
|
|
|
25522
25755
|
// src/commands/invoices/rotate-link.ts
|
|
25523
|
-
var
|
|
25756
|
+
var rotateLink2 = command({
|
|
25524
25757
|
name: "rotate-link",
|
|
25525
25758
|
description: "Reissue the invoice's public link, which stops every link already shared or emailed working",
|
|
25526
25759
|
positionals: {
|
|
@@ -25534,7 +25767,7 @@ var rotateLink = command({
|
|
|
25534
25767
|
});
|
|
25535
25768
|
|
|
25536
25769
|
// src/commands/invoices/update.ts
|
|
25537
|
-
var
|
|
25770
|
+
var update5 = command({
|
|
25538
25771
|
name: "update",
|
|
25539
25772
|
description: "Update a draft invoice (sent invoices are frozen)",
|
|
25540
25773
|
longDescription: "An omitted flag leaves its field unchanged. Passed --line flags replace the whole line list; --no-notes and --no-due clear their fields.",
|
|
@@ -25582,52 +25815,29 @@ var invoices = group({
|
|
|
25582
25815
|
longDescription: `An invoice is a draft until issued: issuing assigns its gapless number, freezes the
|
|
25583
25816
|
document, and snapshots the client's billing details. Sent invoices are immutable \u2014
|
|
25584
25817
|
the fix path is void + duplicate into a fresh draft. Drafts delete outright.`,
|
|
25585
|
-
commands: [
|
|
25818
|
+
commands: [create4, list5, get4, update5, issue2, email3, voidInvoice, duplicate, link, rotateLink2, payments]
|
|
25586
25819
|
});
|
|
25587
25820
|
|
|
25588
25821
|
// src/lib/attachments.ts
|
|
25589
|
-
import { basename } from "path";
|
|
25822
|
+
import { basename as basename2 } from "path";
|
|
25590
25823
|
var attachFiles = async (client3, issueId, paths) => {
|
|
25591
25824
|
if (paths.length === 0)
|
|
25592
25825
|
return [];
|
|
25593
|
-
const
|
|
25594
|
-
|
|
25595
|
-
|
|
25596
|
-
|
|
25597
|
-
|
|
25598
|
-
|
|
25599
|
-
|
|
25600
|
-
|
|
25601
|
-
|
|
25602
|
-
}
|
|
25603
|
-
const created = new Set;
|
|
25604
|
-
try {
|
|
25605
|
-
for (const { path, file: file2 } of files) {
|
|
25606
|
-
const uploadUrl = await client3.mutation(api2.issueAttachments.generateUploadUrl, { issueId });
|
|
25607
|
-
const response = await fetch(uploadUrl, {
|
|
25608
|
-
method: "POST",
|
|
25609
|
-
headers: { "Content-Type": file2.type },
|
|
25610
|
-
body: file2
|
|
25611
|
-
});
|
|
25612
|
-
if (!response.ok)
|
|
25613
|
-
throw new Error(`Uploading ${path} failed (${response.status}).`);
|
|
25614
|
-
const storageId = exports_external.custom((value) => typeof value === "string");
|
|
25615
|
-
const body = exports_external.object({ storageId }).safeParse(await response.json());
|
|
25616
|
-
if (!body.success)
|
|
25617
|
-
throw new Error("The upload returned no file reference.");
|
|
25618
|
-
created.add(await client3.action(api2.issueAttachments.create, {
|
|
25619
|
-
issueId,
|
|
25620
|
-
storageId: body.data.storageId,
|
|
25621
|
-
name: basename(path)
|
|
25622
|
-
}));
|
|
25623
|
-
}
|
|
25624
|
-
} catch (error51) {
|
|
25826
|
+
const files2 = await Promise.all(paths.map(async (path) => ({ path, file: await localFile(path, MAX_ISSUE_ATTACHMENT_BYTES) })));
|
|
25827
|
+
const results = await Promise.allSettled(files2.map(async ({ path, file: file2 }) => {
|
|
25828
|
+
const uploadUrl = await client3.mutation(api2.issueAttachments.generateUploadUrl, { issueId });
|
|
25829
|
+
const storageId = await uploadFile(path, file2, uploadUrl);
|
|
25830
|
+
return await client3.action(api2.issueAttachments.create, { issueId, storageId, name: basename2(path) });
|
|
25831
|
+
}));
|
|
25832
|
+
const created = new Set(results.flatMap((result) => result.status === "fulfilled" ? [result.value] : []));
|
|
25833
|
+
const failed = results.find((result) => result.status === "rejected");
|
|
25834
|
+
if (failed) {
|
|
25625
25835
|
for (const id of created) {
|
|
25626
25836
|
await client3.mutation(api2.issueAttachments.remove, { id }).catch(() => {
|
|
25627
25837
|
console.error("An attachment from this failed batch could not be removed; check the dashboard.");
|
|
25628
25838
|
});
|
|
25629
25839
|
}
|
|
25630
|
-
throw
|
|
25840
|
+
throw failed.reason;
|
|
25631
25841
|
}
|
|
25632
25842
|
const attachments = await client3.query(api2.issueAttachments.list, { issueId });
|
|
25633
25843
|
return attachments.filter((attachment) => created.has(attachment._id));
|
|
@@ -25676,7 +25886,7 @@ var agentConfigurationLine = (id) => {
|
|
|
25676
25886
|
};
|
|
25677
25887
|
|
|
25678
25888
|
// src/commands/issues/create.ts
|
|
25679
|
-
var
|
|
25889
|
+
var create5 = command({
|
|
25680
25890
|
name: "create",
|
|
25681
25891
|
description: "Create an issue and print its identifier",
|
|
25682
25892
|
positionals: {
|
|
@@ -25739,7 +25949,7 @@ var printTasks = (tasks) => {
|
|
|
25739
25949
|
};
|
|
25740
25950
|
|
|
25741
25951
|
// src/commands/issues/get.ts
|
|
25742
|
-
var printIssue = ({ relations, attachments, tasks, feed, milestone, ...issue3 }) => {
|
|
25952
|
+
var printIssue = ({ relations, attachments, pages, tasks, feed, milestone, ...issue3 }) => {
|
|
25743
25953
|
const project = issue3.project;
|
|
25744
25954
|
console.log(`${issue3.identifier} ${issue3.title}`);
|
|
25745
25955
|
console.log(`Status: ${issue3.status} \xB7 Priority: ${issue3.priority} \xB7 Disposition: ${issue3.disposition}`);
|
|
@@ -25778,6 +25988,12 @@ var printIssue = ({ relations, attachments, tasks, feed, milestone, ...issue3 })
|
|
|
25778
25988
|
console.log(` ${attachment.name} \u2014 ${attachment.url ?? "unavailable"}`);
|
|
25779
25989
|
}
|
|
25780
25990
|
}
|
|
25991
|
+
if (pages.length > 0) {
|
|
25992
|
+
console.log("Pages:");
|
|
25993
|
+
for (const page of pages) {
|
|
25994
|
+
console.log(` ${page.title} (${page.style}, ${page.visibility}) \u2014 ${page.url}`);
|
|
25995
|
+
}
|
|
25996
|
+
}
|
|
25781
25997
|
printTasks(tasks);
|
|
25782
25998
|
if (issue3.descriptionMarkdown)
|
|
25783
25999
|
console.log(`
|
|
@@ -25800,7 +26016,7 @@ Feed:`);
|
|
|
25800
26016
|
}
|
|
25801
26017
|
}
|
|
25802
26018
|
};
|
|
25803
|
-
var
|
|
26019
|
+
var get5 = command({
|
|
25804
26020
|
name: "get",
|
|
25805
26021
|
description: "Show one or more issues: properties, description, and feed",
|
|
25806
26022
|
positionals: {
|
|
@@ -25819,10 +26035,11 @@ var get4 = command({
|
|
|
25819
26035
|
throw new Error(`No issues match ${missing.join(", ")}.`);
|
|
25820
26036
|
const details = found.flatMap((detail) => detail ? [detail] : []);
|
|
25821
26037
|
if (json2) {
|
|
25822
|
-
const objects = details.map(({ relations, attachments, tasks, feed, milestone: _, ...issue3 }) => ({
|
|
26038
|
+
const objects = details.map(({ relations, attachments, pages, tasks, feed, milestone: _, ...issue3 }) => ({
|
|
25823
26039
|
...issue3,
|
|
25824
26040
|
relations,
|
|
25825
26041
|
attachments,
|
|
26042
|
+
pages,
|
|
25826
26043
|
tasks,
|
|
25827
26044
|
feed
|
|
25828
26045
|
}));
|
|
@@ -25837,7 +26054,7 @@ var get4 = command({
|
|
|
25837
26054
|
});
|
|
25838
26055
|
|
|
25839
26056
|
// src/commands/issues/labels/create.ts
|
|
25840
|
-
var
|
|
26057
|
+
var create6 = command({
|
|
25841
26058
|
name: "create",
|
|
25842
26059
|
description: "Create an issue label and print its id",
|
|
25843
26060
|
positionals: {
|
|
@@ -25854,7 +26071,7 @@ var create5 = command({
|
|
|
25854
26071
|
});
|
|
25855
26072
|
|
|
25856
26073
|
// src/commands/issues/labels/list.ts
|
|
25857
|
-
var
|
|
26074
|
+
var list7 = command({
|
|
25858
26075
|
name: "list",
|
|
25859
26076
|
description: "List issue labels alphabetically",
|
|
25860
26077
|
options: {
|
|
@@ -25871,7 +26088,7 @@ var list6 = command({
|
|
|
25871
26088
|
});
|
|
25872
26089
|
|
|
25873
26090
|
// src/commands/issues/labels/remove.ts
|
|
25874
|
-
var
|
|
26091
|
+
var remove5 = command({
|
|
25875
26092
|
name: "delete",
|
|
25876
26093
|
description: "Delete an issue label and remove it from every issue",
|
|
25877
26094
|
positionals: {
|
|
@@ -25885,7 +26102,7 @@ var remove4 = command({
|
|
|
25885
26102
|
});
|
|
25886
26103
|
|
|
25887
26104
|
// src/commands/issues/labels/update.ts
|
|
25888
|
-
var
|
|
26105
|
+
var update6 = command({
|
|
25889
26106
|
name: "update",
|
|
25890
26107
|
description: "Rename or recolor an issue label",
|
|
25891
26108
|
positionals: {
|
|
@@ -25908,11 +26125,54 @@ var update5 = command({
|
|
|
25908
26125
|
var labels = group({
|
|
25909
26126
|
name: "labels",
|
|
25910
26127
|
description: "Manage issue labels",
|
|
25911
|
-
commands: [
|
|
26128
|
+
commands: [create6, list7, update6, remove5]
|
|
26129
|
+
});
|
|
26130
|
+
|
|
26131
|
+
// src/commands/issues/link-pr.ts
|
|
26132
|
+
var linkPr = command({
|
|
26133
|
+
name: "link-pr",
|
|
26134
|
+
description: "Link a pull request to an issue by hand",
|
|
26135
|
+
longDescription: `For a pull request the webhook cannot match on its own: work that started before
|
|
26136
|
+
its issue existed, so the branch never carried the identifier, or a second issue the
|
|
26137
|
+
same pull request lands. From here reviews and the merge move the issue as usual.
|
|
26138
|
+
--claim applies what opening the pull request would have: unstarted work moves to
|
|
26139
|
+
in_progress; any other status is kept.`,
|
|
26140
|
+
positionals: {
|
|
26141
|
+
id: exports_external.string().describe("Issue identifier, number, or URL"),
|
|
26142
|
+
url: exports_external.string().describe("Pull request URL")
|
|
26143
|
+
},
|
|
26144
|
+
options: {
|
|
26145
|
+
claim: exports_external.boolean().default(false).describe("Move backlog or todo work to in_progress along with the link")
|
|
26146
|
+
},
|
|
26147
|
+
run: async ({ positionals: { id, url: url2 }, options: { claim: claim2 } }) => {
|
|
26148
|
+
const client3 = await backendClient();
|
|
26149
|
+
const { identifier, linked, status: status3 } = await client3.mutation(api2.issues.linkPr, {
|
|
26150
|
+
id: readIssueRef(id),
|
|
26151
|
+
prUrl: url2,
|
|
26152
|
+
...claim2 ? { claim: claim2 } : {}
|
|
26153
|
+
});
|
|
26154
|
+
console.log(`${linked ? "Linked" : "Already linked"} ${url2} to ${identifier}: ${status3}`);
|
|
26155
|
+
}
|
|
25912
26156
|
});
|
|
25913
26157
|
|
|
25914
26158
|
// src/commands/issues/list.ts
|
|
25915
|
-
var
|
|
26159
|
+
var resolveScope = async (client3, { project, here, milestone }) => {
|
|
26160
|
+
if (milestone) {
|
|
26161
|
+
if (!project && !here)
|
|
26162
|
+
throw new Error("A milestone filter needs --project or --here.");
|
|
26163
|
+
const ref = project ? readProjectRef(project) : await currentRepoKey();
|
|
26164
|
+
if (!ref)
|
|
26165
|
+
throw new Error("No repository here: not a git checkout with an origin remote.");
|
|
26166
|
+
const resolved = await client3.query(api2.milestones.resolve, { project: ref, name: milestone });
|
|
26167
|
+
return { projectId: resolved.projectId, milestoneId: resolved._id };
|
|
26168
|
+
}
|
|
26169
|
+
if (project)
|
|
26170
|
+
return { projectId: (await resolveProject(client3, project))._id };
|
|
26171
|
+
if (here)
|
|
26172
|
+
return { projectId: (await repoProject(client3))._id };
|
|
26173
|
+
return;
|
|
26174
|
+
};
|
|
26175
|
+
var list8 = command({
|
|
25916
26176
|
name: "list",
|
|
25917
26177
|
description: "List open issues, most recently updated first",
|
|
25918
26178
|
options: {
|
|
@@ -25932,10 +26192,7 @@ var list7 = command({
|
|
|
25932
26192
|
if (options.project && options.here)
|
|
25933
26193
|
throw new Error("Pass --project or --here, not both.");
|
|
25934
26194
|
const client3 = await backendClient();
|
|
25935
|
-
const
|
|
25936
|
-
if (options.milestone && !project)
|
|
25937
|
-
throw new Error("A milestone filter needs --project or --here.");
|
|
25938
|
-
const milestone = project && options.milestone ? await resolveMilestone(client3, project._id, options.milestone) : undefined;
|
|
26195
|
+
const scope = await resolveScope(client3, options);
|
|
25939
26196
|
const result = await client3.query(api2.issueViews.query, {
|
|
25940
26197
|
source: {
|
|
25941
26198
|
type: "custom",
|
|
@@ -25949,8 +26206,8 @@ var list7 = command({
|
|
|
25949
26206
|
statuses: options.status ? [options.status] : options.all ? undefined : ISSUE_STATUSES.filter(issueIsOpen),
|
|
25950
26207
|
priorities: options.priority ? [options.priority] : undefined,
|
|
25951
26208
|
dispositions: options.disposition ? [options.disposition] : undefined,
|
|
25952
|
-
projectIds:
|
|
25953
|
-
milestoneIds:
|
|
26209
|
+
projectIds: scope ? [scope.projectId] : undefined,
|
|
26210
|
+
milestoneIds: scope?.milestoneId ? [scope.milestoneId] : undefined,
|
|
25954
26211
|
dueDate: options.due
|
|
25955
26212
|
}
|
|
25956
26213
|
}
|
|
@@ -26031,7 +26288,7 @@ var relate = command({
|
|
|
26031
26288
|
});
|
|
26032
26289
|
|
|
26033
26290
|
// src/commands/issues/remove.ts
|
|
26034
|
-
var
|
|
26291
|
+
var remove6 = command({
|
|
26035
26292
|
name: "delete",
|
|
26036
26293
|
description: "Delete an issue permanently (sub-issues survive as top-level issues)",
|
|
26037
26294
|
positionals: {
|
|
@@ -26267,9 +26524,9 @@ address several rows even as removals renumber it.`,
|
|
|
26267
26524
|
remove: position.optional().describe("Drop a task by its number (repeatable)"),
|
|
26268
26525
|
json: exports_external.boolean().default(false).describe("Print as JSON")
|
|
26269
26526
|
},
|
|
26270
|
-
run: async ({ positionals: { id }, options: { add: add3, check: check2, uncheck, convert, remove:
|
|
26527
|
+
run: async ({ positionals: { id }, options: { add: add3, check: check2, uncheck, convert, remove: remove7, json: json2 } }) => {
|
|
26271
26528
|
const client3 = await backendClient();
|
|
26272
|
-
const wrote = [add3, check2, uncheck, convert,
|
|
26529
|
+
const wrote = [add3, check2, uncheck, convert, remove7].some((values) => values !== undefined);
|
|
26273
26530
|
if (wrote) {
|
|
26274
26531
|
const { identifier, converted, tasks: tasks3 } = await client3.mutation(api2.issueTasks.edit, {
|
|
26275
26532
|
issue: readIssueRef(id),
|
|
@@ -26277,7 +26534,7 @@ address several rows even as removals renumber it.`,
|
|
|
26277
26534
|
...check2 !== undefined ? { check: check2 } : {},
|
|
26278
26535
|
...uncheck !== undefined ? { uncheck } : {},
|
|
26279
26536
|
...convert !== undefined ? { convert } : {},
|
|
26280
|
-
...
|
|
26537
|
+
...remove7 !== undefined ? { remove: remove7 } : {}
|
|
26281
26538
|
});
|
|
26282
26539
|
if (!json2) {
|
|
26283
26540
|
for (const created of converted) {
|
|
@@ -26321,7 +26578,7 @@ var unrelate = command({
|
|
|
26321
26578
|
});
|
|
26322
26579
|
|
|
26323
26580
|
// src/commands/issues/update.ts
|
|
26324
|
-
var
|
|
26581
|
+
var update7 = command({
|
|
26325
26582
|
name: "update",
|
|
26326
26583
|
description: "Rewrite an issue's title or description, or attach files",
|
|
26327
26584
|
positionals: {
|
|
@@ -26360,16 +26617,16 @@ todo, in_progress, ready_for_review, in_review, changes_requested, approved, don
|
|
|
26360
26617
|
canceled), priority, and disposition (who acts next). The workflow commands \u2014 start,
|
|
26361
26618
|
submit, start-review, submit-review \u2014 and the linked pull request move the status;
|
|
26362
26619
|
each refuses an issue outside its own status, and \`set -s\` is for corrections.
|
|
26363
|
-
|
|
26364
|
-
|
|
26365
|
-
|
|
26366
|
-
|
|
26367
|
-
|
|
26620
|
+
Work on a branch whose name contains the issue identifier and the branch, pull
|
|
26621
|
+
request, and status track automatically \u2014 a change request moves the issue to
|
|
26622
|
+
changes_requested, an approval to approved (one outstanding change request outweighs
|
|
26623
|
+
any number of approvals), and a merge lands it as done. link-pr covers the pull
|
|
26624
|
+
request the branch name does not point at; from there the same tracking applies.`,
|
|
26368
26625
|
commands: [
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
|
|
26372
|
-
|
|
26626
|
+
create5,
|
|
26627
|
+
list8,
|
|
26628
|
+
get5,
|
|
26629
|
+
update7,
|
|
26373
26630
|
set2,
|
|
26374
26631
|
labels,
|
|
26375
26632
|
route,
|
|
@@ -26378,16 +26635,20 @@ it as done.`,
|
|
|
26378
26635
|
startReview,
|
|
26379
26636
|
submitReview,
|
|
26380
26637
|
markAddressed,
|
|
26638
|
+
linkPr,
|
|
26381
26639
|
tasks,
|
|
26382
26640
|
relate,
|
|
26383
26641
|
unrelate,
|
|
26384
26642
|
comment,
|
|
26385
|
-
|
|
26643
|
+
remove6
|
|
26386
26644
|
]
|
|
26387
26645
|
});
|
|
26388
26646
|
|
|
26647
|
+
// ../../packages/backend/convex/lib/object.ts
|
|
26648
|
+
var keys = (object2) => Object.keys(object2);
|
|
26649
|
+
|
|
26389
26650
|
// src/lib/group.ts
|
|
26390
|
-
import { basename as
|
|
26651
|
+
import { basename as basename3 } from "path";
|
|
26391
26652
|
var git = async (...args) => {
|
|
26392
26653
|
const proc = Bun.spawn(["git", ...args], { stdout: "pipe", stderr: "ignore" });
|
|
26393
26654
|
const [output, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
@@ -26399,12 +26660,12 @@ var detectRepoGroup = async () => {
|
|
|
26399
26660
|
if (remote)
|
|
26400
26661
|
return repoNameFromRemote(remote);
|
|
26401
26662
|
const root = await git("rev-parse", "--show-toplevel");
|
|
26402
|
-
return root ?
|
|
26663
|
+
return root ? basename3(root) : undefined;
|
|
26403
26664
|
};
|
|
26404
26665
|
var groupForCreate = async (group2) => group2 === null ? undefined : group2 ?? await detectRepoGroup();
|
|
26405
26666
|
|
|
26406
26667
|
// src/commands/pages/create.ts
|
|
26407
|
-
var
|
|
26668
|
+
var create7 = command({
|
|
26408
26669
|
name: "create",
|
|
26409
26670
|
description: "Publish a page and print its URL",
|
|
26410
26671
|
positionals: {
|
|
@@ -26413,27 +26674,28 @@ var create6 = command({
|
|
|
26413
26674
|
options: {
|
|
26414
26675
|
title: exports_external.string().describe("Page title").meta({ short: "t" }),
|
|
26415
26676
|
group: exports_external.string().nullable().optional().describe("Group to list the page under (default: the current repo)").meta({ short: "g", negatable: true }),
|
|
26416
|
-
|
|
26417
|
-
private: exports_external.boolean().default(false).describe("Require the publishing account to view the page")
|
|
26677
|
+
style: exports_external.enum(keys(PAGE_STYLES)).default("basic").describe(`Style to render in \u2014 ${describeChoices(PAGE_STYLES)}`),
|
|
26678
|
+
private: exports_external.boolean().default(false).describe("Require the publishing account to view the page"),
|
|
26679
|
+
issue: exports_external.string().optional().describe("Link the page to an issue (KAI-N, number, or URL)")
|
|
26418
26680
|
},
|
|
26419
|
-
run: async ({ positionals: { file: file2 }, options: { title, group: group2,
|
|
26681
|
+
run: async ({ positionals: { file: file2 }, options: { title, group: group2, style, private: isPrivate, issue: issue3 } }) => {
|
|
26420
26682
|
const newGroup = await groupForCreate(group2);
|
|
26421
26683
|
const html = await readBody(file2);
|
|
26422
|
-
const mode = raw ? "raw" : "themed";
|
|
26423
26684
|
const visibility = isPrivate ? "private" : "public";
|
|
26424
26685
|
const { url: url2 } = await (await backendClient()).action(api2.pages.create, {
|
|
26425
26686
|
title,
|
|
26426
26687
|
group: newGroup,
|
|
26427
26688
|
html,
|
|
26428
|
-
|
|
26429
|
-
visibility
|
|
26689
|
+
style,
|
|
26690
|
+
visibility,
|
|
26691
|
+
issue: issue3 === undefined ? undefined : readIssueRef(issue3)
|
|
26430
26692
|
});
|
|
26431
26693
|
console.log(url2);
|
|
26432
26694
|
}
|
|
26433
26695
|
});
|
|
26434
26696
|
|
|
26435
26697
|
// src/commands/pages/get.ts
|
|
26436
|
-
var
|
|
26698
|
+
var get6 = command({
|
|
26437
26699
|
name: "get",
|
|
26438
26700
|
description: "Print a page's HTML",
|
|
26439
26701
|
positionals: {
|
|
@@ -26452,11 +26714,8 @@ var get5 = command({
|
|
|
26452
26714
|
}
|
|
26453
26715
|
});
|
|
26454
26716
|
|
|
26455
|
-
// ../../packages/backend/convex/lib/format.ts
|
|
26456
|
-
var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${Math.round(bytes / 1024)} KB`;
|
|
26457
|
-
|
|
26458
26717
|
// src/commands/pages/list.ts
|
|
26459
|
-
var
|
|
26718
|
+
var list9 = command({
|
|
26460
26719
|
name: "list",
|
|
26461
26720
|
description: "List your published pages",
|
|
26462
26721
|
options: {
|
|
@@ -26470,12 +26729,12 @@ var list8 = command({
|
|
|
26470
26729
|
if (pages.length === 0)
|
|
26471
26730
|
return console.log(group2 === undefined ? "No pages yet." : "No pages in that group.");
|
|
26472
26731
|
printTable([
|
|
26473
|
-
["TITLE", "GROUP", "ACCESS", "
|
|
26732
|
+
["TITLE", "GROUP", "ACCESS", "STYLE", "SIZE", "VERSIONS", "CREATED", "URL"],
|
|
26474
26733
|
...pages.map((page) => [
|
|
26475
26734
|
page.title,
|
|
26476
26735
|
page.group ?? "-",
|
|
26477
26736
|
page.visibility,
|
|
26478
|
-
page.
|
|
26737
|
+
page.style,
|
|
26479
26738
|
formatBytes(page.size),
|
|
26480
26739
|
page.versions === 0 ? "-" : String(page.versions),
|
|
26481
26740
|
new Date(page.createdAt).toISOString().slice(0, 10),
|
|
@@ -26486,7 +26745,7 @@ var list8 = command({
|
|
|
26486
26745
|
});
|
|
26487
26746
|
|
|
26488
26747
|
// src/commands/pages/remove.ts
|
|
26489
|
-
var
|
|
26748
|
+
var remove7 = command({
|
|
26490
26749
|
name: "delete",
|
|
26491
26750
|
description: "Delete a page",
|
|
26492
26751
|
positionals: {
|
|
@@ -26498,6 +26757,14 @@ var remove6 = command({
|
|
|
26498
26757
|
}
|
|
26499
26758
|
});
|
|
26500
26759
|
|
|
26760
|
+
// src/lib/pages.ts
|
|
26761
|
+
var warnPruned = (pruned) => {
|
|
26762
|
+
if (pruned.length === 0)
|
|
26763
|
+
return;
|
|
26764
|
+
const versions2 = pruned.length === 1 ? `version ${pruned[0]}` : `versions ${pruned.join(", ")}`;
|
|
26765
|
+
console.error(`Pruned ${versions2}; the history keeps the newest ${MAX_PAGE_VERSIONS}.`);
|
|
26766
|
+
};
|
|
26767
|
+
|
|
26501
26768
|
// src/commands/pages/revert.ts
|
|
26502
26769
|
var revert = command({
|
|
26503
26770
|
name: "revert",
|
|
@@ -26507,15 +26774,16 @@ var revert = command({
|
|
|
26507
26774
|
version: exports_external.coerce.number().int().positive().describe("Version number from `kds pages versions`")
|
|
26508
26775
|
},
|
|
26509
26776
|
run: async ({ positionals: { id, version: version4 } }) => {
|
|
26510
|
-
const { url: url2 } = await (await backendClient()).mutation(api2.pages.revert, { id: readPageId(id), version: version4 });
|
|
26777
|
+
const { url: url2, pruned } = await (await backendClient()).mutation(api2.pages.revert, { id: readPageId(id), version: version4 });
|
|
26778
|
+
warnPruned(pruned);
|
|
26511
26779
|
console.log(url2);
|
|
26512
26780
|
}
|
|
26513
26781
|
});
|
|
26514
26782
|
|
|
26515
26783
|
// src/commands/pages/update.ts
|
|
26516
|
-
var
|
|
26784
|
+
var update8 = command({
|
|
26517
26785
|
name: "update",
|
|
26518
|
-
description: "Replace a page's HTML, title, group,
|
|
26786
|
+
description: "Replace a page's HTML, title, group, style, or visibility",
|
|
26519
26787
|
positionals: {
|
|
26520
26788
|
id: exports_external.string().describe("Page id or URL"),
|
|
26521
26789
|
file: exports_external.string().optional().describe("HTML file, or - for stdin")
|
|
@@ -26523,21 +26791,24 @@ var update7 = command({
|
|
|
26523
26791
|
options: {
|
|
26524
26792
|
title: exports_external.string().optional().describe("New page title").meta({ short: "t" }),
|
|
26525
26793
|
group: exports_external.string().nullable().optional().describe("Move the page to this group, or --no-group to remove it from one").meta({ short: "g", negatable: true }),
|
|
26526
|
-
|
|
26527
|
-
visibility: exports_external.enum(PAGE_VISIBILITIES).optional().describe("Who can view the page")
|
|
26794
|
+
style: exports_external.enum(keys(PAGE_STYLES)).optional().describe(`New style \u2014 ${describeChoices(PAGE_STYLES)}`),
|
|
26795
|
+
visibility: exports_external.enum(PAGE_VISIBILITIES).optional().describe("Who can view the page"),
|
|
26796
|
+
issue: exports_external.string().nullable().optional().describe("Link the page to an issue (KAI-N, number, or URL), or --no-issue to unlink it").meta({ negatable: true })
|
|
26528
26797
|
},
|
|
26529
|
-
run: async ({ positionals: { id, file: file2 }, options: { title, group: group2,
|
|
26530
|
-
if (!file2 && title === undefined && group2 === undefined &&
|
|
26531
|
-
throw new Error("Nothing to update. Pass a file, --title, --group, --no-group, --
|
|
26798
|
+
run: async ({ positionals: { id, file: file2 }, options: { title, group: group2, style, visibility, issue: issue3 } }) => {
|
|
26799
|
+
if (!file2 && title === undefined && group2 === undefined && style === undefined && visibility === undefined && issue3 === undefined)
|
|
26800
|
+
throw new Error("Nothing to update. Pass a file, --title, --group, --no-group, --style, --visibility, --issue, or --no-issue.");
|
|
26532
26801
|
const html = file2 ? await readBody(file2) : undefined;
|
|
26533
|
-
const { url: url2 } = await (await backendClient()).action(api2.pages.update, {
|
|
26802
|
+
const { url: url2, pruned } = await (await backendClient()).action(api2.pages.update, {
|
|
26534
26803
|
id: readPageId(id),
|
|
26535
26804
|
html,
|
|
26536
26805
|
title,
|
|
26537
26806
|
group: group2,
|
|
26538
|
-
|
|
26539
|
-
visibility
|
|
26807
|
+
style,
|
|
26808
|
+
visibility,
|
|
26809
|
+
issue: typeof issue3 === "string" ? readIssueRef(issue3) : issue3
|
|
26540
26810
|
});
|
|
26811
|
+
warnPruned(pruned);
|
|
26541
26812
|
console.log(url2);
|
|
26542
26813
|
}
|
|
26543
26814
|
});
|
|
@@ -26559,10 +26830,10 @@ var versions2 = command({
|
|
|
26559
26830
|
if (archived.length === 0)
|
|
26560
26831
|
return console.log("No previous versions.");
|
|
26561
26832
|
printTable([
|
|
26562
|
-
["VERSION", "
|
|
26833
|
+
["VERSION", "STYLE", "SIZE", "ARCHIVED"],
|
|
26563
26834
|
...archived.map((version4) => [
|
|
26564
26835
|
String(version4.version),
|
|
26565
|
-
version4.
|
|
26836
|
+
version4.style,
|
|
26566
26837
|
formatBytes(version4.size),
|
|
26567
26838
|
new Date(version4.archivedAt).toISOString().slice(0, 10)
|
|
26568
26839
|
])
|
|
@@ -26574,11 +26845,11 @@ var versions2 = command({
|
|
|
26574
26845
|
var pages = group({
|
|
26575
26846
|
name: "pages",
|
|
26576
26847
|
description: "Publish HTML documents to the web",
|
|
26577
|
-
longDescription: `
|
|
26578
|
-
|
|
26579
|
-
|
|
26580
|
-
|
|
26581
|
-
commands: [
|
|
26848
|
+
longDescription: `A page is an HTML fragment rendered in one of the site's styles:
|
|
26849
|
+
${describeChoices(PAGE_STYLES, `
|
|
26850
|
+
`)}
|
|
26851
|
+
Basic is the default. A page keeps its URL across updates and reverts.`,
|
|
26852
|
+
commands: [create7, list9, get6, update8, versions2, revert, remove7]
|
|
26582
26853
|
});
|
|
26583
26854
|
|
|
26584
26855
|
// src/commands/project.ts
|
|
@@ -26611,7 +26882,7 @@ var project = command({
|
|
|
26611
26882
|
});
|
|
26612
26883
|
|
|
26613
26884
|
// src/commands/projects/create.ts
|
|
26614
|
-
var
|
|
26885
|
+
var create8 = command({
|
|
26615
26886
|
name: "create",
|
|
26616
26887
|
description: "Create a project and print its id",
|
|
26617
26888
|
positionals: {
|
|
@@ -26643,7 +26914,7 @@ var create7 = command({
|
|
|
26643
26914
|
});
|
|
26644
26915
|
|
|
26645
26916
|
// src/commands/projects/get.ts
|
|
26646
|
-
var
|
|
26917
|
+
var get7 = command({
|
|
26647
26918
|
name: "get",
|
|
26648
26919
|
description: "Show a project: properties, description, and milestones",
|
|
26649
26920
|
positionals: {
|
|
@@ -26687,7 +26958,7 @@ Milestones:`);
|
|
|
26687
26958
|
});
|
|
26688
26959
|
|
|
26689
26960
|
// src/commands/projects/list.ts
|
|
26690
|
-
var
|
|
26961
|
+
var list10 = command({
|
|
26691
26962
|
name: "list",
|
|
26692
26963
|
description: "List your projects, most recently updated first",
|
|
26693
26964
|
options: {
|
|
@@ -26730,7 +27001,7 @@ More projects exist; raise --limit past ${limit}.`);
|
|
|
26730
27001
|
});
|
|
26731
27002
|
|
|
26732
27003
|
// src/commands/projects/remove.ts
|
|
26733
|
-
var
|
|
27004
|
+
var remove8 = command({
|
|
26734
27005
|
name: "delete",
|
|
26735
27006
|
description: "Delete a project and its milestones (its issues survive, projectless)",
|
|
26736
27007
|
positionals: {
|
|
@@ -26775,7 +27046,7 @@ var set3 = command({
|
|
|
26775
27046
|
});
|
|
26776
27047
|
|
|
26777
27048
|
// src/commands/projects/update.ts
|
|
26778
|
-
var
|
|
27049
|
+
var update9 = command({
|
|
26779
27050
|
name: "update",
|
|
26780
27051
|
description: "Rewrite a project's name, summary, or description",
|
|
26781
27052
|
positionals: {
|
|
@@ -26806,7 +27077,7 @@ var projects = group({
|
|
|
26806
27077
|
description: "Manage projects",
|
|
26807
27078
|
longDescription: `Connect a repository (set --repo) to make --here resolve in that checkout and to
|
|
26808
27079
|
let issue branches and pull requests link automatically.`,
|
|
26809
|
-
commands: [
|
|
27080
|
+
commands: [create8, list10, get7, update9, set3, remove8]
|
|
26810
27081
|
});
|
|
26811
27082
|
|
|
26812
27083
|
// src/commands/quotations/accept.ts
|
|
@@ -26865,7 +27136,7 @@ Nothing blocks over- or under-invoicing; left to bill is advisory.`,
|
|
|
26865
27136
|
});
|
|
26866
27137
|
|
|
26867
27138
|
// src/commands/quotations/create.ts
|
|
26868
|
-
var
|
|
27139
|
+
var create9 = command({
|
|
26869
27140
|
name: "create",
|
|
26870
27141
|
description: "Create a draft quotation for a client and print its id",
|
|
26871
27142
|
positionals: {
|
|
@@ -26873,6 +27144,8 @@ var create8 = command({
|
|
|
26873
27144
|
},
|
|
26874
27145
|
options: {
|
|
26875
27146
|
line: exports_external.array(exports_external.string()).optional().describe(`Line item as ${QUOTATION_LINE_ITEM_SYNTAX}; repeatable`).meta({ short: "l" }),
|
|
27147
|
+
cost: exports_external.array(exports_external.string()).optional().describe(`Expected cost as ${EXPECTED_COST_SYNTAX}; repeatable`),
|
|
27148
|
+
fee: exports_external.array(exports_external.string()).optional().describe(`Recurring fee as ${RECURRING_FEE_SYNTAX}; repeatable`),
|
|
26876
27149
|
project: exports_external.string().optional().describe("Project id or URL to link (must belong to the same client)"),
|
|
26877
27150
|
notes: exports_external.string().optional().describe("Free text rendered at the document foot"),
|
|
26878
27151
|
validUntil: exports_external.string().nullable().optional().describe("Last valid day (YYYY-MM-DD); defaults from the profile's validity days, --no-valid-until never expires").meta({ negatable: true })
|
|
@@ -26883,6 +27156,8 @@ var create8 = command({
|
|
|
26883
27156
|
clientId: readClientRef(ref),
|
|
26884
27157
|
projectId: options.project === undefined ? undefined : readProjectRef(options.project),
|
|
26885
27158
|
lineItems: options.line?.map(parseQuotationLineItem),
|
|
27159
|
+
expectedCosts: options.cost?.map(parseExpectedCost),
|
|
27160
|
+
recurringFees: options.fee?.map(parseRecurringFee),
|
|
26886
27161
|
notes: options.notes,
|
|
26887
27162
|
validUntil: options.validUntil === null ? "" : options.validUntil,
|
|
26888
27163
|
today: localToday()
|
|
@@ -26919,7 +27194,7 @@ var email4 = command({
|
|
|
26919
27194
|
});
|
|
26920
27195
|
|
|
26921
27196
|
// src/commands/quotations/get.ts
|
|
26922
|
-
var
|
|
27197
|
+
var get8 = command({
|
|
26923
27198
|
name: "get",
|
|
26924
27199
|
description: "Show a quotation: document, totals, and \u2014 once accepted \u2014 its billing progress",
|
|
26925
27200
|
positionals: {
|
|
@@ -26965,6 +27240,30 @@ var get7 = command({
|
|
|
26965
27240
|
}
|
|
26966
27241
|
console.log(`
|
|
26967
27242
|
Total: ${formatPerCurrency(quotation.totals.map(({ currency, total }) => ({ currency, amount: total })))}`);
|
|
27243
|
+
if (quotation.expectedCosts.length > 0) {
|
|
27244
|
+
console.log(`
|
|
27245
|
+
Expected costs (estimates, billed at cost):`);
|
|
27246
|
+
printTable([
|
|
27247
|
+
["NAME", "KIND", "AMOUNT"],
|
|
27248
|
+
...quotation.expectedCosts.map((cost) => [
|
|
27249
|
+
cost.name,
|
|
27250
|
+
COST_KIND_LABELS[cost.kind],
|
|
27251
|
+
formatMinorAmount(cost.amount, cost.currency)
|
|
27252
|
+
])
|
|
27253
|
+
]);
|
|
27254
|
+
}
|
|
27255
|
+
if (quotation.recurringFees.length > 0) {
|
|
27256
|
+
console.log(`
|
|
27257
|
+
Recurring fees:`);
|
|
27258
|
+
printTable([
|
|
27259
|
+
["DESCRIPTION", "CADENCE", "AMOUNT"],
|
|
27260
|
+
...quotation.recurringFees.map((fee) => [
|
|
27261
|
+
fee.description,
|
|
27262
|
+
RECURRING_CHARGE_CADENCE_LABELS[fee.cadence],
|
|
27263
|
+
formatMinorAmount(fee.amount, fee.currency)
|
|
27264
|
+
])
|
|
27265
|
+
]);
|
|
27266
|
+
}
|
|
26968
27267
|
if (quotation.billing) {
|
|
26969
27268
|
console.log(`Invoiced: ${formatPerCurrency(quotation.billing.progress.map(({ currency, invoiced }) => ({ currency, amount: invoiced })))}`);
|
|
26970
27269
|
const leftToBill = quotation.billing.progress.filter(({ remaining }) => remaining > 0);
|
|
@@ -27026,7 +27325,7 @@ var link2 = command({
|
|
|
27026
27325
|
});
|
|
27027
27326
|
|
|
27028
27327
|
// src/commands/quotations/list.ts
|
|
27029
|
-
var
|
|
27328
|
+
var list11 = command({
|
|
27030
27329
|
name: "list",
|
|
27031
27330
|
description: "List your quotations, newest first (expired is derived, never stored)",
|
|
27032
27331
|
options: {
|
|
@@ -27090,7 +27389,7 @@ var reject2 = command({
|
|
|
27090
27389
|
});
|
|
27091
27390
|
|
|
27092
27391
|
// src/commands/quotations/rotate-link.ts
|
|
27093
|
-
var
|
|
27392
|
+
var rotateLink3 = command({
|
|
27094
27393
|
name: "rotate-link",
|
|
27095
27394
|
description: "Reissue the quotation's public link, which stops every link already shared or emailed working",
|
|
27096
27395
|
positionals: {
|
|
@@ -27104,26 +27403,30 @@ var rotateLink2 = command({
|
|
|
27104
27403
|
});
|
|
27105
27404
|
|
|
27106
27405
|
// src/commands/quotations/update.ts
|
|
27107
|
-
var
|
|
27406
|
+
var update10 = command({
|
|
27108
27407
|
name: "update",
|
|
27109
27408
|
description: "Update a draft quotation (sent quotations are frozen)",
|
|
27110
|
-
longDescription: "An omitted flag leaves its field unchanged. Passed --line flags replace
|
|
27409
|
+
longDescription: "An omitted flag leaves its field unchanged. Passed --line, --cost, or --fee flags replace that whole list (--no-cost or --no-fee empties it); --no-notes clears the notes and --no-valid-until makes the quotation never expire.",
|
|
27111
27410
|
positionals: {
|
|
27112
27411
|
id: exports_external.string().describe("Quotation id or URL")
|
|
27113
27412
|
},
|
|
27114
27413
|
options: {
|
|
27115
27414
|
line: exports_external.array(exports_external.string()).optional().describe(`Line item as ${QUOTATION_LINE_ITEM_SYNTAX}; repeatable`).meta({ short: "l" }),
|
|
27415
|
+
cost: exports_external.array(exports_external.string()).nullable().optional().describe(`Expected cost as ${EXPECTED_COST_SYNTAX}; repeatable, or --no-cost to clear`).meta({ negatable: true }),
|
|
27416
|
+
fee: exports_external.array(exports_external.string()).nullable().optional().describe(`Recurring fee as ${RECURRING_FEE_SYNTAX}; repeatable, or --no-fee to clear`).meta({ negatable: true }),
|
|
27116
27417
|
notes: exports_external.string().nullable().optional().describe("Document-foot notes, or --no-notes").meta({ negatable: true }),
|
|
27117
27418
|
validUntil: exports_external.string().nullable().optional().describe("Last valid day (YYYY-MM-DD), or --no-valid-until to never expire").meta({ negatable: true })
|
|
27118
27419
|
},
|
|
27119
27420
|
run: async ({ positionals: { id }, options }) => {
|
|
27120
|
-
if (options.line === undefined && options.notes === undefined && options.validUntil === undefined) {
|
|
27121
|
-
throw new Error("Nothing to update. Pass --line, --notes, or --valid-until (or a --no form to clear).");
|
|
27421
|
+
if (options.line === undefined && options.cost === undefined && options.fee === undefined && options.notes === undefined && options.validUntil === undefined) {
|
|
27422
|
+
throw new Error("Nothing to update. Pass --line, --cost, --fee, --notes, or --valid-until (or a --no form to clear).");
|
|
27122
27423
|
}
|
|
27123
27424
|
const backend = await backendClient();
|
|
27124
27425
|
const { number: number4 } = await backend.mutation(api2.quotations.update, {
|
|
27125
27426
|
id: readQuotationRef(id),
|
|
27126
27427
|
lineItems: options.line?.map(parseQuotationLineItem),
|
|
27428
|
+
expectedCosts: options.cost === null ? [] : options.cost?.map(parseExpectedCost),
|
|
27429
|
+
recurringFees: options.fee === null ? [] : options.fee?.map(parseRecurringFee),
|
|
27127
27430
|
notes: options.notes === null ? "" : options.notes,
|
|
27128
27431
|
validUntil: options.validUntil === null ? "" : options.validUntil
|
|
27129
27432
|
});
|
|
@@ -27154,10 +27457,10 @@ document awaiting the client's answer. Accept and reject are manual records;
|
|
|
27154
27457
|
expiry is derived from valid-until and never blocks accepting. An accepted
|
|
27155
27458
|
quotation converts into any number of draft invoices, each tracing back to it.`,
|
|
27156
27459
|
commands: [
|
|
27157
|
-
|
|
27158
|
-
|
|
27159
|
-
|
|
27160
|
-
|
|
27460
|
+
create9,
|
|
27461
|
+
list11,
|
|
27462
|
+
get8,
|
|
27463
|
+
update10,
|
|
27161
27464
|
issue3,
|
|
27162
27465
|
email4,
|
|
27163
27466
|
accept,
|
|
@@ -27166,7 +27469,7 @@ quotation converts into any number of draft invoices, each tracing back to it.`,
|
|
|
27166
27469
|
duplicate2,
|
|
27167
27470
|
convert,
|
|
27168
27471
|
link2,
|
|
27169
|
-
|
|
27472
|
+
rotateLink3
|
|
27170
27473
|
]
|
|
27171
27474
|
});
|
|
27172
27475
|
|
|
@@ -27185,7 +27488,21 @@ var upgrade = command({
|
|
|
27185
27488
|
var rootCommand = group({
|
|
27186
27489
|
name: "kds",
|
|
27187
27490
|
description: "KDS CLI",
|
|
27188
|
-
commands: [
|
|
27491
|
+
commands: [
|
|
27492
|
+
auth,
|
|
27493
|
+
clients,
|
|
27494
|
+
costs,
|
|
27495
|
+
daemon,
|
|
27496
|
+
files,
|
|
27497
|
+
inbox,
|
|
27498
|
+
invoices,
|
|
27499
|
+
issues,
|
|
27500
|
+
pages,
|
|
27501
|
+
project,
|
|
27502
|
+
projects,
|
|
27503
|
+
quotations,
|
|
27504
|
+
upgrade
|
|
27505
|
+
],
|
|
27189
27506
|
options: {
|
|
27190
27507
|
version: exports_external.boolean().default(false).describe("Show the version").meta({ short: "v" })
|
|
27191
27508
|
},
|
|
@@ -27198,6 +27515,7 @@ var rootCommand = group({
|
|
|
27198
27515
|
|
|
27199
27516
|
// src/lib/errors.ts
|
|
27200
27517
|
var errorMessage = (error51) => error51 instanceof ConvexError && typeof error51.data === "string" ? error51.data : error51.message;
|
|
27518
|
+
var isRedactedServerError = (error51) => error51 instanceof Error && !(error51 instanceof ConvexError) && /^\[Request ID: \w+\] Server Error$/.test(error51.message);
|
|
27201
27519
|
|
|
27202
27520
|
// src/lib/update-check.ts
|
|
27203
27521
|
var latestVersion = async () => {
|
|
@@ -27228,6 +27546,12 @@ var maybeNotifyUpdate = async () => {
|
|
|
27228
27546
|
if (latest && Bun.semver.order(latest, CLI_VERSION) === 1)
|
|
27229
27547
|
console.error(`kds v${latest} available (you have v${CLI_VERSION}) \u2014 run 'kds upgrade'`);
|
|
27230
27548
|
};
|
|
27549
|
+
var staleCliHint = async (current = CLI_VERSION) => {
|
|
27550
|
+
const latest = env.KDS_NO_UPDATE_CHECK ? undefined : await latestVersion().catch(() => {
|
|
27551
|
+
return;
|
|
27552
|
+
});
|
|
27553
|
+
return latest && Bun.semver.order(latest, current) === 1 ? `kds v${current} is behind v${latest}, so the server likely refused a request this version still sends \u2014 run 'kds upgrade' and retry.` : "The server gave no details. If kds was installed a while ago, try 'kds upgrade'; otherwise report the request id.";
|
|
27554
|
+
};
|
|
27231
27555
|
|
|
27232
27556
|
// src/index.ts
|
|
27233
27557
|
var args = Bun.argv.slice(2);
|
|
@@ -27237,6 +27561,8 @@ try {
|
|
|
27237
27561
|
await maybeNotifyUpdate();
|
|
27238
27562
|
} catch (error51) {
|
|
27239
27563
|
console.error(errorMessage(error51));
|
|
27564
|
+
if (isRedactedServerError(error51))
|
|
27565
|
+
console.error(await staleCliHint());
|
|
27240
27566
|
process.exitCode = 1;
|
|
27241
27567
|
} finally {
|
|
27242
27568
|
closePrompts();
|