@islamihab/kds 0.12.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 +337 -93
- 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: {
|
|
@@ -15210,6 +15210,8 @@ var PAGE_STYLES = {
|
|
|
15210
15210
|
var PAGE_VISIBILITIES = ["public", "private"];
|
|
15211
15211
|
var MAX_PAGE_HTML_BYTES = 4000000;
|
|
15212
15212
|
var MAX_PAGE_VERSIONS = 20;
|
|
15213
|
+
var FILE_VISIBILITIES = ["public", "private"];
|
|
15214
|
+
var MAX_FILE_BYTES = 20000000;
|
|
15213
15215
|
var ISSUE_STATUSES = [
|
|
15214
15216
|
"backlog",
|
|
15215
15217
|
"todo",
|
|
@@ -15379,7 +15381,16 @@ var issueIsOpen = (status) => !ISSUE_TERMINAL_STATUSES.some((terminal) => termin
|
|
|
15379
15381
|
var CURRENCIES = ["EGP", "USD", "EUR", "GBP", "AED", "SAR"];
|
|
15380
15382
|
var DEFAULT_CURRENCY = "EGP";
|
|
15381
15383
|
var RECURRING_CHARGE_CADENCES = ["monthly", "yearly"];
|
|
15384
|
+
var RECURRING_CHARGE_CADENCE_LABELS = {
|
|
15385
|
+
monthly: "Monthly",
|
|
15386
|
+
yearly: "Yearly"
|
|
15387
|
+
};
|
|
15382
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
|
+
};
|
|
15383
15394
|
var MAX_PROJECT_REPO_LENGTH = 200;
|
|
15384
15395
|
var INVOICE_STATUSES = ["draft", "sent", "paid", "void"];
|
|
15385
15396
|
var invoiceLineTotal = ({ quantity, unitPrice }) => {
|
|
@@ -24280,6 +24291,15 @@ var readPageId = (value) => {
|
|
|
24280
24291
|
return trimmed;
|
|
24281
24292
|
}
|
|
24282
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
|
+
};
|
|
24283
24303
|
|
|
24284
24304
|
// src/commands/clients/archive.ts
|
|
24285
24305
|
var archive = command({
|
|
@@ -25162,6 +25182,186 @@ var daemon = group({
|
|
|
25162
25182
|
commands: [start, stop, status2, logs]
|
|
25163
25183
|
});
|
|
25164
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
|
+
|
|
25165
25365
|
// src/commands/inbox/list.ts
|
|
25166
25366
|
var inboxOptions = {
|
|
25167
25367
|
all: exports_external.boolean().default(false).describe("Include events already marked seen"),
|
|
@@ -25261,10 +25461,34 @@ var parseQuotationLineItem = (value) => {
|
|
|
25261
25461
|
const section = parts.length === 5 ? parts.pop()?.trim() : undefined;
|
|
25262
25462
|
return { ...parseLineItem(parts.join("|")), section: section || undefined };
|
|
25263
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
|
+
};
|
|
25264
25488
|
var formatPerCurrency = (entries) => entries.length === 0 ? "-" : entries.map(({ amount, currency }) => formatMinorAmount(amount, currency)).join(" + ");
|
|
25265
25489
|
|
|
25266
25490
|
// src/commands/invoices/create.ts
|
|
25267
|
-
var
|
|
25491
|
+
var create4 = command({
|
|
25268
25492
|
name: "create",
|
|
25269
25493
|
description: "Create a draft invoice for a client and print its id",
|
|
25270
25494
|
positionals: {
|
|
@@ -25317,7 +25541,7 @@ var email3 = command({
|
|
|
25317
25541
|
});
|
|
25318
25542
|
|
|
25319
25543
|
// src/commands/invoices/get.ts
|
|
25320
|
-
var
|
|
25544
|
+
var get4 = command({
|
|
25321
25545
|
name: "get",
|
|
25322
25546
|
description: "Show an invoice: document, totals, and payments",
|
|
25323
25547
|
positionals: {
|
|
@@ -25407,7 +25631,7 @@ var link = command({
|
|
|
25407
25631
|
});
|
|
25408
25632
|
|
|
25409
25633
|
// src/commands/invoices/list.ts
|
|
25410
|
-
var
|
|
25634
|
+
var list5 = command({
|
|
25411
25635
|
name: "list",
|
|
25412
25636
|
description: "List your invoices, newest first",
|
|
25413
25637
|
options: {
|
|
@@ -25477,7 +25701,7 @@ var add2 = command({
|
|
|
25477
25701
|
});
|
|
25478
25702
|
|
|
25479
25703
|
// src/commands/invoices/payments/list.ts
|
|
25480
|
-
var
|
|
25704
|
+
var list6 = command({
|
|
25481
25705
|
name: "list",
|
|
25482
25706
|
description: "List the payments recorded on an invoice, oldest first",
|
|
25483
25707
|
positionals: {
|
|
@@ -25505,7 +25729,7 @@ var list5 = command({
|
|
|
25505
25729
|
});
|
|
25506
25730
|
|
|
25507
25731
|
// src/commands/invoices/payments/remove.ts
|
|
25508
|
-
var
|
|
25732
|
+
var remove4 = command({
|
|
25509
25733
|
name: "remove",
|
|
25510
25734
|
description: "Remove a recorded payment (unflips paid when the invoice is no longer covered)",
|
|
25511
25735
|
positionals: {
|
|
@@ -25525,11 +25749,11 @@ var payments = group({
|
|
|
25525
25749
|
longDescription: `A payment is money received against a sent invoice, in one currency. Paid flips
|
|
25526
25750
|
automatically once every currency's total is covered; overpayment is rejected,
|
|
25527
25751
|
and forgiving a shortfall is a payment with a note.`,
|
|
25528
|
-
commands: [add2,
|
|
25752
|
+
commands: [add2, list6, remove4]
|
|
25529
25753
|
});
|
|
25530
25754
|
|
|
25531
25755
|
// src/commands/invoices/rotate-link.ts
|
|
25532
|
-
var
|
|
25756
|
+
var rotateLink2 = command({
|
|
25533
25757
|
name: "rotate-link",
|
|
25534
25758
|
description: "Reissue the invoice's public link, which stops every link already shared or emailed working",
|
|
25535
25759
|
positionals: {
|
|
@@ -25543,7 +25767,7 @@ var rotateLink = command({
|
|
|
25543
25767
|
});
|
|
25544
25768
|
|
|
25545
25769
|
// src/commands/invoices/update.ts
|
|
25546
|
-
var
|
|
25770
|
+
var update5 = command({
|
|
25547
25771
|
name: "update",
|
|
25548
25772
|
description: "Update a draft invoice (sent invoices are frozen)",
|
|
25549
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.",
|
|
@@ -25591,52 +25815,29 @@ var invoices = group({
|
|
|
25591
25815
|
longDescription: `An invoice is a draft until issued: issuing assigns its gapless number, freezes the
|
|
25592
25816
|
document, and snapshots the client's billing details. Sent invoices are immutable \u2014
|
|
25593
25817
|
the fix path is void + duplicate into a fresh draft. Drafts delete outright.`,
|
|
25594
|
-
commands: [
|
|
25818
|
+
commands: [create4, list5, get4, update5, issue2, email3, voidInvoice, duplicate, link, rotateLink2, payments]
|
|
25595
25819
|
});
|
|
25596
25820
|
|
|
25597
25821
|
// src/lib/attachments.ts
|
|
25598
|
-
import { basename } from "path";
|
|
25822
|
+
import { basename as basename2 } from "path";
|
|
25599
25823
|
var attachFiles = async (client3, issueId, paths) => {
|
|
25600
25824
|
if (paths.length === 0)
|
|
25601
25825
|
return [];
|
|
25602
|
-
const
|
|
25603
|
-
|
|
25604
|
-
|
|
25605
|
-
|
|
25606
|
-
|
|
25607
|
-
|
|
25608
|
-
|
|
25609
|
-
|
|
25610
|
-
|
|
25611
|
-
}
|
|
25612
|
-
const created = new Set;
|
|
25613
|
-
try {
|
|
25614
|
-
for (const { path, file: file2 } of files) {
|
|
25615
|
-
const uploadUrl = await client3.mutation(api2.issueAttachments.generateUploadUrl, { issueId });
|
|
25616
|
-
const response = await fetch(uploadUrl, {
|
|
25617
|
-
method: "POST",
|
|
25618
|
-
headers: { "Content-Type": file2.type },
|
|
25619
|
-
body: file2
|
|
25620
|
-
});
|
|
25621
|
-
if (!response.ok)
|
|
25622
|
-
throw new Error(`Uploading ${path} failed (${response.status}).`);
|
|
25623
|
-
const storageId = exports_external.custom((value) => typeof value === "string");
|
|
25624
|
-
const body = exports_external.object({ storageId }).safeParse(await response.json());
|
|
25625
|
-
if (!body.success)
|
|
25626
|
-
throw new Error("The upload returned no file reference.");
|
|
25627
|
-
created.add(await client3.action(api2.issueAttachments.create, {
|
|
25628
|
-
issueId,
|
|
25629
|
-
storageId: body.data.storageId,
|
|
25630
|
-
name: basename(path)
|
|
25631
|
-
}));
|
|
25632
|
-
}
|
|
25633
|
-
} 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) {
|
|
25634
25835
|
for (const id of created) {
|
|
25635
25836
|
await client3.mutation(api2.issueAttachments.remove, { id }).catch(() => {
|
|
25636
25837
|
console.error("An attachment from this failed batch could not be removed; check the dashboard.");
|
|
25637
25838
|
});
|
|
25638
25839
|
}
|
|
25639
|
-
throw
|
|
25840
|
+
throw failed.reason;
|
|
25640
25841
|
}
|
|
25641
25842
|
const attachments = await client3.query(api2.issueAttachments.list, { issueId });
|
|
25642
25843
|
return attachments.filter((attachment) => created.has(attachment._id));
|
|
@@ -25685,7 +25886,7 @@ var agentConfigurationLine = (id) => {
|
|
|
25685
25886
|
};
|
|
25686
25887
|
|
|
25687
25888
|
// src/commands/issues/create.ts
|
|
25688
|
-
var
|
|
25889
|
+
var create5 = command({
|
|
25689
25890
|
name: "create",
|
|
25690
25891
|
description: "Create an issue and print its identifier",
|
|
25691
25892
|
positionals: {
|
|
@@ -25815,7 +26016,7 @@ Feed:`);
|
|
|
25815
26016
|
}
|
|
25816
26017
|
}
|
|
25817
26018
|
};
|
|
25818
|
-
var
|
|
26019
|
+
var get5 = command({
|
|
25819
26020
|
name: "get",
|
|
25820
26021
|
description: "Show one or more issues: properties, description, and feed",
|
|
25821
26022
|
positionals: {
|
|
@@ -25853,7 +26054,7 @@ var get4 = command({
|
|
|
25853
26054
|
});
|
|
25854
26055
|
|
|
25855
26056
|
// src/commands/issues/labels/create.ts
|
|
25856
|
-
var
|
|
26057
|
+
var create6 = command({
|
|
25857
26058
|
name: "create",
|
|
25858
26059
|
description: "Create an issue label and print its id",
|
|
25859
26060
|
positionals: {
|
|
@@ -25870,7 +26071,7 @@ var create5 = command({
|
|
|
25870
26071
|
});
|
|
25871
26072
|
|
|
25872
26073
|
// src/commands/issues/labels/list.ts
|
|
25873
|
-
var
|
|
26074
|
+
var list7 = command({
|
|
25874
26075
|
name: "list",
|
|
25875
26076
|
description: "List issue labels alphabetically",
|
|
25876
26077
|
options: {
|
|
@@ -25887,7 +26088,7 @@ var list6 = command({
|
|
|
25887
26088
|
});
|
|
25888
26089
|
|
|
25889
26090
|
// src/commands/issues/labels/remove.ts
|
|
25890
|
-
var
|
|
26091
|
+
var remove5 = command({
|
|
25891
26092
|
name: "delete",
|
|
25892
26093
|
description: "Delete an issue label and remove it from every issue",
|
|
25893
26094
|
positionals: {
|
|
@@ -25901,7 +26102,7 @@ var remove4 = command({
|
|
|
25901
26102
|
});
|
|
25902
26103
|
|
|
25903
26104
|
// src/commands/issues/labels/update.ts
|
|
25904
|
-
var
|
|
26105
|
+
var update6 = command({
|
|
25905
26106
|
name: "update",
|
|
25906
26107
|
description: "Rename or recolor an issue label",
|
|
25907
26108
|
positionals: {
|
|
@@ -25924,7 +26125,7 @@ var update5 = command({
|
|
|
25924
26125
|
var labels = group({
|
|
25925
26126
|
name: "labels",
|
|
25926
26127
|
description: "Manage issue labels",
|
|
25927
|
-
commands: [
|
|
26128
|
+
commands: [create6, list7, update6, remove5]
|
|
25928
26129
|
});
|
|
25929
26130
|
|
|
25930
26131
|
// src/commands/issues/link-pr.ts
|
|
@@ -25971,7 +26172,7 @@ var resolveScope = async (client3, { project, here, milestone }) => {
|
|
|
25971
26172
|
return { projectId: (await repoProject(client3))._id };
|
|
25972
26173
|
return;
|
|
25973
26174
|
};
|
|
25974
|
-
var
|
|
26175
|
+
var list8 = command({
|
|
25975
26176
|
name: "list",
|
|
25976
26177
|
description: "List open issues, most recently updated first",
|
|
25977
26178
|
options: {
|
|
@@ -26087,7 +26288,7 @@ var relate = command({
|
|
|
26087
26288
|
});
|
|
26088
26289
|
|
|
26089
26290
|
// src/commands/issues/remove.ts
|
|
26090
|
-
var
|
|
26291
|
+
var remove6 = command({
|
|
26091
26292
|
name: "delete",
|
|
26092
26293
|
description: "Delete an issue permanently (sub-issues survive as top-level issues)",
|
|
26093
26294
|
positionals: {
|
|
@@ -26323,9 +26524,9 @@ address several rows even as removals renumber it.`,
|
|
|
26323
26524
|
remove: position.optional().describe("Drop a task by its number (repeatable)"),
|
|
26324
26525
|
json: exports_external.boolean().default(false).describe("Print as JSON")
|
|
26325
26526
|
},
|
|
26326
|
-
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 } }) => {
|
|
26327
26528
|
const client3 = await backendClient();
|
|
26328
|
-
const wrote = [add3, check2, uncheck, convert,
|
|
26529
|
+
const wrote = [add3, check2, uncheck, convert, remove7].some((values) => values !== undefined);
|
|
26329
26530
|
if (wrote) {
|
|
26330
26531
|
const { identifier, converted, tasks: tasks3 } = await client3.mutation(api2.issueTasks.edit, {
|
|
26331
26532
|
issue: readIssueRef(id),
|
|
@@ -26333,7 +26534,7 @@ address several rows even as removals renumber it.`,
|
|
|
26333
26534
|
...check2 !== undefined ? { check: check2 } : {},
|
|
26334
26535
|
...uncheck !== undefined ? { uncheck } : {},
|
|
26335
26536
|
...convert !== undefined ? { convert } : {},
|
|
26336
|
-
...
|
|
26537
|
+
...remove7 !== undefined ? { remove: remove7 } : {}
|
|
26337
26538
|
});
|
|
26338
26539
|
if (!json2) {
|
|
26339
26540
|
for (const created of converted) {
|
|
@@ -26377,7 +26578,7 @@ var unrelate = command({
|
|
|
26377
26578
|
});
|
|
26378
26579
|
|
|
26379
26580
|
// src/commands/issues/update.ts
|
|
26380
|
-
var
|
|
26581
|
+
var update7 = command({
|
|
26381
26582
|
name: "update",
|
|
26382
26583
|
description: "Rewrite an issue's title or description, or attach files",
|
|
26383
26584
|
positionals: {
|
|
@@ -26422,10 +26623,10 @@ changes_requested, an approval to approved (one outstanding change request outwe
|
|
|
26422
26623
|
any number of approvals), and a merge lands it as done. link-pr covers the pull
|
|
26423
26624
|
request the branch name does not point at; from there the same tracking applies.`,
|
|
26424
26625
|
commands: [
|
|
26425
|
-
|
|
26426
|
-
|
|
26427
|
-
|
|
26428
|
-
|
|
26626
|
+
create5,
|
|
26627
|
+
list8,
|
|
26628
|
+
get5,
|
|
26629
|
+
update7,
|
|
26429
26630
|
set2,
|
|
26430
26631
|
labels,
|
|
26431
26632
|
route,
|
|
@@ -26439,7 +26640,7 @@ request the branch name does not point at; from there the same tracking applies.
|
|
|
26439
26640
|
relate,
|
|
26440
26641
|
unrelate,
|
|
26441
26642
|
comment,
|
|
26442
|
-
|
|
26643
|
+
remove6
|
|
26443
26644
|
]
|
|
26444
26645
|
});
|
|
26445
26646
|
|
|
@@ -26447,7 +26648,7 @@ request the branch name does not point at; from there the same tracking applies.
|
|
|
26447
26648
|
var keys = (object2) => Object.keys(object2);
|
|
26448
26649
|
|
|
26449
26650
|
// src/lib/group.ts
|
|
26450
|
-
import { basename as
|
|
26651
|
+
import { basename as basename3 } from "path";
|
|
26451
26652
|
var git = async (...args) => {
|
|
26452
26653
|
const proc = Bun.spawn(["git", ...args], { stdout: "pipe", stderr: "ignore" });
|
|
26453
26654
|
const [output, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
@@ -26459,12 +26660,12 @@ var detectRepoGroup = async () => {
|
|
|
26459
26660
|
if (remote)
|
|
26460
26661
|
return repoNameFromRemote(remote);
|
|
26461
26662
|
const root = await git("rev-parse", "--show-toplevel");
|
|
26462
|
-
return root ?
|
|
26663
|
+
return root ? basename3(root) : undefined;
|
|
26463
26664
|
};
|
|
26464
26665
|
var groupForCreate = async (group2) => group2 === null ? undefined : group2 ?? await detectRepoGroup();
|
|
26465
26666
|
|
|
26466
26667
|
// src/commands/pages/create.ts
|
|
26467
|
-
var
|
|
26668
|
+
var create7 = command({
|
|
26468
26669
|
name: "create",
|
|
26469
26670
|
description: "Publish a page and print its URL",
|
|
26470
26671
|
positionals: {
|
|
@@ -26494,7 +26695,7 @@ var create6 = command({
|
|
|
26494
26695
|
});
|
|
26495
26696
|
|
|
26496
26697
|
// src/commands/pages/get.ts
|
|
26497
|
-
var
|
|
26698
|
+
var get6 = command({
|
|
26498
26699
|
name: "get",
|
|
26499
26700
|
description: "Print a page's HTML",
|
|
26500
26701
|
positionals: {
|
|
@@ -26513,11 +26714,8 @@ var get5 = command({
|
|
|
26513
26714
|
}
|
|
26514
26715
|
});
|
|
26515
26716
|
|
|
26516
|
-
// ../../packages/backend/convex/lib/format.ts
|
|
26517
|
-
var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${Math.round(bytes / 1024)} KB`;
|
|
26518
|
-
|
|
26519
26717
|
// src/commands/pages/list.ts
|
|
26520
|
-
var
|
|
26718
|
+
var list9 = command({
|
|
26521
26719
|
name: "list",
|
|
26522
26720
|
description: "List your published pages",
|
|
26523
26721
|
options: {
|
|
@@ -26547,7 +26745,7 @@ var list8 = command({
|
|
|
26547
26745
|
});
|
|
26548
26746
|
|
|
26549
26747
|
// src/commands/pages/remove.ts
|
|
26550
|
-
var
|
|
26748
|
+
var remove7 = command({
|
|
26551
26749
|
name: "delete",
|
|
26552
26750
|
description: "Delete a page",
|
|
26553
26751
|
positionals: {
|
|
@@ -26583,7 +26781,7 @@ var revert = command({
|
|
|
26583
26781
|
});
|
|
26584
26782
|
|
|
26585
26783
|
// src/commands/pages/update.ts
|
|
26586
|
-
var
|
|
26784
|
+
var update8 = command({
|
|
26587
26785
|
name: "update",
|
|
26588
26786
|
description: "Replace a page's HTML, title, group, style, or visibility",
|
|
26589
26787
|
positionals: {
|
|
@@ -26651,7 +26849,7 @@ var pages = group({
|
|
|
26651
26849
|
${describeChoices(PAGE_STYLES, `
|
|
26652
26850
|
`)}
|
|
26653
26851
|
Basic is the default. A page keeps its URL across updates and reverts.`,
|
|
26654
|
-
commands: [
|
|
26852
|
+
commands: [create7, list9, get6, update8, versions2, revert, remove7]
|
|
26655
26853
|
});
|
|
26656
26854
|
|
|
26657
26855
|
// src/commands/project.ts
|
|
@@ -26684,7 +26882,7 @@ var project = command({
|
|
|
26684
26882
|
});
|
|
26685
26883
|
|
|
26686
26884
|
// src/commands/projects/create.ts
|
|
26687
|
-
var
|
|
26885
|
+
var create8 = command({
|
|
26688
26886
|
name: "create",
|
|
26689
26887
|
description: "Create a project and print its id",
|
|
26690
26888
|
positionals: {
|
|
@@ -26716,7 +26914,7 @@ var create7 = command({
|
|
|
26716
26914
|
});
|
|
26717
26915
|
|
|
26718
26916
|
// src/commands/projects/get.ts
|
|
26719
|
-
var
|
|
26917
|
+
var get7 = command({
|
|
26720
26918
|
name: "get",
|
|
26721
26919
|
description: "Show a project: properties, description, and milestones",
|
|
26722
26920
|
positionals: {
|
|
@@ -26760,7 +26958,7 @@ Milestones:`);
|
|
|
26760
26958
|
});
|
|
26761
26959
|
|
|
26762
26960
|
// src/commands/projects/list.ts
|
|
26763
|
-
var
|
|
26961
|
+
var list10 = command({
|
|
26764
26962
|
name: "list",
|
|
26765
26963
|
description: "List your projects, most recently updated first",
|
|
26766
26964
|
options: {
|
|
@@ -26803,7 +27001,7 @@ More projects exist; raise --limit past ${limit}.`);
|
|
|
26803
27001
|
});
|
|
26804
27002
|
|
|
26805
27003
|
// src/commands/projects/remove.ts
|
|
26806
|
-
var
|
|
27004
|
+
var remove8 = command({
|
|
26807
27005
|
name: "delete",
|
|
26808
27006
|
description: "Delete a project and its milestones (its issues survive, projectless)",
|
|
26809
27007
|
positionals: {
|
|
@@ -26848,7 +27046,7 @@ var set3 = command({
|
|
|
26848
27046
|
});
|
|
26849
27047
|
|
|
26850
27048
|
// src/commands/projects/update.ts
|
|
26851
|
-
var
|
|
27049
|
+
var update9 = command({
|
|
26852
27050
|
name: "update",
|
|
26853
27051
|
description: "Rewrite a project's name, summary, or description",
|
|
26854
27052
|
positionals: {
|
|
@@ -26879,7 +27077,7 @@ var projects = group({
|
|
|
26879
27077
|
description: "Manage projects",
|
|
26880
27078
|
longDescription: `Connect a repository (set --repo) to make --here resolve in that checkout and to
|
|
26881
27079
|
let issue branches and pull requests link automatically.`,
|
|
26882
|
-
commands: [
|
|
27080
|
+
commands: [create8, list10, get7, update9, set3, remove8]
|
|
26883
27081
|
});
|
|
26884
27082
|
|
|
26885
27083
|
// src/commands/quotations/accept.ts
|
|
@@ -26938,7 +27136,7 @@ Nothing blocks over- or under-invoicing; left to bill is advisory.`,
|
|
|
26938
27136
|
});
|
|
26939
27137
|
|
|
26940
27138
|
// src/commands/quotations/create.ts
|
|
26941
|
-
var
|
|
27139
|
+
var create9 = command({
|
|
26942
27140
|
name: "create",
|
|
26943
27141
|
description: "Create a draft quotation for a client and print its id",
|
|
26944
27142
|
positionals: {
|
|
@@ -26946,6 +27144,8 @@ var create8 = command({
|
|
|
26946
27144
|
},
|
|
26947
27145
|
options: {
|
|
26948
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`),
|
|
26949
27149
|
project: exports_external.string().optional().describe("Project id or URL to link (must belong to the same client)"),
|
|
26950
27150
|
notes: exports_external.string().optional().describe("Free text rendered at the document foot"),
|
|
26951
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 })
|
|
@@ -26956,6 +27156,8 @@ var create8 = command({
|
|
|
26956
27156
|
clientId: readClientRef(ref),
|
|
26957
27157
|
projectId: options.project === undefined ? undefined : readProjectRef(options.project),
|
|
26958
27158
|
lineItems: options.line?.map(parseQuotationLineItem),
|
|
27159
|
+
expectedCosts: options.cost?.map(parseExpectedCost),
|
|
27160
|
+
recurringFees: options.fee?.map(parseRecurringFee),
|
|
26959
27161
|
notes: options.notes,
|
|
26960
27162
|
validUntil: options.validUntil === null ? "" : options.validUntil,
|
|
26961
27163
|
today: localToday()
|
|
@@ -26992,7 +27194,7 @@ var email4 = command({
|
|
|
26992
27194
|
});
|
|
26993
27195
|
|
|
26994
27196
|
// src/commands/quotations/get.ts
|
|
26995
|
-
var
|
|
27197
|
+
var get8 = command({
|
|
26996
27198
|
name: "get",
|
|
26997
27199
|
description: "Show a quotation: document, totals, and \u2014 once accepted \u2014 its billing progress",
|
|
26998
27200
|
positionals: {
|
|
@@ -27038,6 +27240,30 @@ var get7 = command({
|
|
|
27038
27240
|
}
|
|
27039
27241
|
console.log(`
|
|
27040
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
|
+
}
|
|
27041
27267
|
if (quotation.billing) {
|
|
27042
27268
|
console.log(`Invoiced: ${formatPerCurrency(quotation.billing.progress.map(({ currency, invoiced }) => ({ currency, amount: invoiced })))}`);
|
|
27043
27269
|
const leftToBill = quotation.billing.progress.filter(({ remaining }) => remaining > 0);
|
|
@@ -27099,7 +27325,7 @@ var link2 = command({
|
|
|
27099
27325
|
});
|
|
27100
27326
|
|
|
27101
27327
|
// src/commands/quotations/list.ts
|
|
27102
|
-
var
|
|
27328
|
+
var list11 = command({
|
|
27103
27329
|
name: "list",
|
|
27104
27330
|
description: "List your quotations, newest first (expired is derived, never stored)",
|
|
27105
27331
|
options: {
|
|
@@ -27163,7 +27389,7 @@ var reject2 = command({
|
|
|
27163
27389
|
});
|
|
27164
27390
|
|
|
27165
27391
|
// src/commands/quotations/rotate-link.ts
|
|
27166
|
-
var
|
|
27392
|
+
var rotateLink3 = command({
|
|
27167
27393
|
name: "rotate-link",
|
|
27168
27394
|
description: "Reissue the quotation's public link, which stops every link already shared or emailed working",
|
|
27169
27395
|
positionals: {
|
|
@@ -27177,26 +27403,30 @@ var rotateLink2 = command({
|
|
|
27177
27403
|
});
|
|
27178
27404
|
|
|
27179
27405
|
// src/commands/quotations/update.ts
|
|
27180
|
-
var
|
|
27406
|
+
var update10 = command({
|
|
27181
27407
|
name: "update",
|
|
27182
27408
|
description: "Update a draft quotation (sent quotations are frozen)",
|
|
27183
|
-
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.",
|
|
27184
27410
|
positionals: {
|
|
27185
27411
|
id: exports_external.string().describe("Quotation id or URL")
|
|
27186
27412
|
},
|
|
27187
27413
|
options: {
|
|
27188
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 }),
|
|
27189
27417
|
notes: exports_external.string().nullable().optional().describe("Document-foot notes, or --no-notes").meta({ negatable: true }),
|
|
27190
27418
|
validUntil: exports_external.string().nullable().optional().describe("Last valid day (YYYY-MM-DD), or --no-valid-until to never expire").meta({ negatable: true })
|
|
27191
27419
|
},
|
|
27192
27420
|
run: async ({ positionals: { id }, options }) => {
|
|
27193
|
-
if (options.line === undefined && options.notes === undefined && options.validUntil === undefined) {
|
|
27194
|
-
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).");
|
|
27195
27423
|
}
|
|
27196
27424
|
const backend = await backendClient();
|
|
27197
27425
|
const { number: number4 } = await backend.mutation(api2.quotations.update, {
|
|
27198
27426
|
id: readQuotationRef(id),
|
|
27199
27427
|
lineItems: options.line?.map(parseQuotationLineItem),
|
|
27428
|
+
expectedCosts: options.cost === null ? [] : options.cost?.map(parseExpectedCost),
|
|
27429
|
+
recurringFees: options.fee === null ? [] : options.fee?.map(parseRecurringFee),
|
|
27200
27430
|
notes: options.notes === null ? "" : options.notes,
|
|
27201
27431
|
validUntil: options.validUntil === null ? "" : options.validUntil
|
|
27202
27432
|
});
|
|
@@ -27227,10 +27457,10 @@ document awaiting the client's answer. Accept and reject are manual records;
|
|
|
27227
27457
|
expiry is derived from valid-until and never blocks accepting. An accepted
|
|
27228
27458
|
quotation converts into any number of draft invoices, each tracing back to it.`,
|
|
27229
27459
|
commands: [
|
|
27230
|
-
|
|
27231
|
-
|
|
27232
|
-
|
|
27233
|
-
|
|
27460
|
+
create9,
|
|
27461
|
+
list11,
|
|
27462
|
+
get8,
|
|
27463
|
+
update10,
|
|
27234
27464
|
issue3,
|
|
27235
27465
|
email4,
|
|
27236
27466
|
accept,
|
|
@@ -27239,7 +27469,7 @@ quotation converts into any number of draft invoices, each tracing back to it.`,
|
|
|
27239
27469
|
duplicate2,
|
|
27240
27470
|
convert,
|
|
27241
27471
|
link2,
|
|
27242
|
-
|
|
27472
|
+
rotateLink3
|
|
27243
27473
|
]
|
|
27244
27474
|
});
|
|
27245
27475
|
|
|
@@ -27258,7 +27488,21 @@ var upgrade = command({
|
|
|
27258
27488
|
var rootCommand = group({
|
|
27259
27489
|
name: "kds",
|
|
27260
27490
|
description: "KDS CLI",
|
|
27261
|
-
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
|
+
],
|
|
27262
27506
|
options: {
|
|
27263
27507
|
version: exports_external.boolean().default(false).describe("Show the version").meta({ short: "v" })
|
|
27264
27508
|
},
|