@islamihab/kds 0.10.0 → 0.12.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 +579 -611
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -14406,7 +14406,10 @@ var printHelp = (help) => {
|
|
|
14406
14406
|
label: key,
|
|
14407
14407
|
description: `${field.description ?? ""}${isRequired(field) ? " (required)" : ""}`
|
|
14408
14408
|
}));
|
|
14409
|
-
const usagePositionals = help.positionals?.map(([key, field]) =>
|
|
14409
|
+
const usagePositionals = help.positionals?.map(([key, field]) => {
|
|
14410
|
+
const name = isRest(field) ? `${key}...` : key;
|
|
14411
|
+
return isRequired(field) ? ` <${name}>` : ` [${name}]`;
|
|
14412
|
+
}).join("") ?? "";
|
|
14410
14413
|
const options = help.options?.filter(({ field }) => !isHidden(field)).concat([
|
|
14411
14414
|
{
|
|
14412
14415
|
key: "help",
|
|
@@ -14451,6 +14454,7 @@ var baseType = (field) => {
|
|
|
14451
14454
|
return t;
|
|
14452
14455
|
};
|
|
14453
14456
|
var isRequired = (field) => !(field instanceof exports_external.ZodOptional || field instanceof exports_external.ZodDefault);
|
|
14457
|
+
var isRest = (field) => baseType(field) instanceof exports_external.ZodArray;
|
|
14454
14458
|
var isHidden = (field) => field.meta()?.hidden === true;
|
|
14455
14459
|
var isNegatable = (field) => field.meta()?.negatable === true;
|
|
14456
14460
|
var isHelpFlag = (arg) => arg === "-h" || arg === "--help";
|
|
@@ -14519,7 +14523,8 @@ var parseArgs = ({
|
|
|
14519
14523
|
options,
|
|
14520
14524
|
allowPositionals: (positionalEntries?.length ?? 0) > 0
|
|
14521
14525
|
});
|
|
14522
|
-
|
|
14526
|
+
const hasRest = positionalEntries?.some(([, field]) => isRest(field)) ?? false;
|
|
14527
|
+
if (positionalEntries && !hasRest && positionals.length > positionalEntries.length) {
|
|
14523
14528
|
help();
|
|
14524
14529
|
throw new Error(`Unexpected argument '${positionals[positionalEntries.length]}'`);
|
|
14525
14530
|
}
|
|
@@ -14552,6 +14557,10 @@ var parseArgs = ({
|
|
|
14552
14557
|
var command = (def) => {
|
|
14553
14558
|
const inputShape = exports_external.object({ positionals: exports_external.object(def.positionals), options: exports_external.object(def.options) });
|
|
14554
14559
|
const positionalEntries = Object.entries(inputShape.shape.positionals.shape);
|
|
14560
|
+
const restIndex = positionalEntries.findIndex(([, field]) => isRest(field));
|
|
14561
|
+
if (restIndex !== -1 && restIndex !== positionalEntries.length - 1) {
|
|
14562
|
+
throw new Error(`Positional '${positionalEntries[restIndex]?.[0]}' collects the rest, so it must come last`);
|
|
14563
|
+
}
|
|
14555
14564
|
const optionsEntries = optionEntries(inputShape.shape.options.shape);
|
|
14556
14565
|
const run = async (args, path) => {
|
|
14557
14566
|
const help = () => printHelp({
|
|
@@ -14567,7 +14576,10 @@ var command = (def) => {
|
|
|
14567
14576
|
await def.run({
|
|
14568
14577
|
...inputShape.parse({
|
|
14569
14578
|
options: values,
|
|
14570
|
-
positionals: Object.fromEntries(positionalEntries.map(([key], index) => [
|
|
14579
|
+
positionals: Object.fromEntries(positionalEntries.map(([key], index) => [
|
|
14580
|
+
key,
|
|
14581
|
+
index === restIndex && positionals.length > index ? positionals.slice(index) : positionals[index]
|
|
14582
|
+
]))
|
|
14571
14583
|
}),
|
|
14572
14584
|
help
|
|
14573
14585
|
});
|
|
@@ -14611,7 +14623,7 @@ import { join } from "path";
|
|
|
14611
14623
|
// package.json
|
|
14612
14624
|
var package_default = {
|
|
14613
14625
|
name: "cli",
|
|
14614
|
-
version: "0.
|
|
14626
|
+
version: "0.12.0",
|
|
14615
14627
|
private: true,
|
|
14616
14628
|
type: "module",
|
|
14617
14629
|
bin: {
|
|
@@ -15184,9 +15196,20 @@ var components = componentsGeneric();
|
|
|
15184
15196
|
|
|
15185
15197
|
// ../../packages/backend/convex/constants.ts
|
|
15186
15198
|
var KDS_DEVICE_AUTH_CLIENT_ID = "kds-cli";
|
|
15187
|
-
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
|
+
};
|
|
15188
15210
|
var PAGE_VISIBILITIES = ["public", "private"];
|
|
15189
15211
|
var MAX_PAGE_HTML_BYTES = 4000000;
|
|
15212
|
+
var MAX_PAGE_VERSIONS = 20;
|
|
15190
15213
|
var ISSUE_STATUSES = [
|
|
15191
15214
|
"backlog",
|
|
15192
15215
|
"todo",
|
|
@@ -15306,6 +15329,7 @@ var PROJECT_STATUSES = ["planned", "in_progress", "paused", "completed", "cancel
|
|
|
15306
15329
|
var PROJECT_HEALTHS = ["on_track", "at_risk", "off_track"];
|
|
15307
15330
|
var ISSUE_IDENTIFIER_PREFIX = "KAI";
|
|
15308
15331
|
var ISSUE_LIST_PAGE_SIZE = 50;
|
|
15332
|
+
var MAX_ISSUES_PER_BATCH_READ = 20;
|
|
15309
15333
|
var MAX_ISSUE_ATTACHMENT_BYTES = 1e7;
|
|
15310
15334
|
var ISSUE_DUE_DATE_MODES = ["overdue", "due_today", "due_soon", "no_due_date"];
|
|
15311
15335
|
var BUILT_IN_ISSUE_VIEWS = {
|
|
@@ -15352,13 +15376,8 @@ var BUILT_IN_ISSUE_VIEWS = {
|
|
|
15352
15376
|
}
|
|
15353
15377
|
};
|
|
15354
15378
|
var issueIsOpen = (status) => !ISSUE_TERMINAL_STATUSES.some((terminal) => terminal === status);
|
|
15355
|
-
var
|
|
15356
|
-
var
|
|
15357
|
-
var MAX_CLIENT_BILLING_ADDRESS_LENGTH = 2000;
|
|
15358
|
-
var MAX_CLIENT_BILLING_EMAIL_LENGTH = 320;
|
|
15359
|
-
var MAX_CLIENT_TAX_ID_LENGTH = 100;
|
|
15360
|
-
var MAX_CLIENT_PAYMENT_TERMS_DAYS = 365;
|
|
15361
|
-
var MAX_BILLING_DAY = 31;
|
|
15379
|
+
var CURRENCIES = ["EGP", "USD", "EUR", "GBP", "AED", "SAR"];
|
|
15380
|
+
var DEFAULT_CURRENCY = "EGP";
|
|
15362
15381
|
var RECURRING_CHARGE_CADENCES = ["monthly", "yearly"];
|
|
15363
15382
|
var COST_KINDS = ["one_off", "monthly", "yearly"];
|
|
15364
15383
|
var MAX_PROJECT_REPO_LENGTH = 200;
|
|
@@ -17338,6 +17357,7 @@ var localDateString = (timestamp) => {
|
|
|
17338
17357
|
return `${date5.getFullYear()}-${month}-${day}`;
|
|
17339
17358
|
};
|
|
17340
17359
|
var localToday = () => localDateString(Date.now());
|
|
17360
|
+
var describeChoices = (choices, separator = "; ") => Object.entries(choices).map(([key, { description }]) => `${key}: ${description}`).join(separator);
|
|
17341
17361
|
|
|
17342
17362
|
// src/lib/zod.ts
|
|
17343
17363
|
var configSchema = exports_external.object({ sessionToken: exports_external.string().optional(), convexUrl: exports_external.url(), convexSiteUrl: exports_external.url() });
|
|
@@ -24112,6 +24132,10 @@ var issueActivityLine = (detail) => {
|
|
|
24112
24132
|
case "attachment_added":
|
|
24113
24133
|
case "attachment_removed":
|
|
24114
24134
|
return `${label}: ${detail.name}`;
|
|
24135
|
+
case "page_linked":
|
|
24136
|
+
case "page_unlinked":
|
|
24137
|
+
case "page_updated":
|
|
24138
|
+
return `${label}: ${detail.title}`;
|
|
24115
24139
|
case "child_added":
|
|
24116
24140
|
case "child_removed":
|
|
24117
24141
|
return `${label}: ${detail.identifier}`;
|
|
@@ -24257,6 +24281,73 @@ var readPageId = (value) => {
|
|
|
24257
24281
|
}
|
|
24258
24282
|
};
|
|
24259
24283
|
|
|
24284
|
+
// src/commands/clients/archive.ts
|
|
24285
|
+
var archive = command({
|
|
24286
|
+
name: "archive",
|
|
24287
|
+
description: "Archive a client: it leaves pickers and default lists, and every existing reference stays intact",
|
|
24288
|
+
positionals: {
|
|
24289
|
+
id: exports_external.string().describe("Client id or URL")
|
|
24290
|
+
},
|
|
24291
|
+
run: async ({ positionals: { id } }) => {
|
|
24292
|
+
const backend = await backendClient();
|
|
24293
|
+
const { name } = await backend.mutation(api2.clients.archive, { id: readClientRef(id) });
|
|
24294
|
+
console.log(`Archived ${name}.`);
|
|
24295
|
+
}
|
|
24296
|
+
});
|
|
24297
|
+
|
|
24298
|
+
// src/lib/currency.ts
|
|
24299
|
+
var CURRENCY_CHOICES = CURRENCIES.join(", ");
|
|
24300
|
+
var currencyCode = exports_external.preprocess((value) => typeof value === "string" ? value.toUpperCase() : value, exports_external.enum(CURRENCIES));
|
|
24301
|
+
var parseLineCurrency = (raw) => {
|
|
24302
|
+
const code2 = raw.trim().toUpperCase();
|
|
24303
|
+
if (!code2)
|
|
24304
|
+
return DEFAULT_CURRENCY;
|
|
24305
|
+
const codes = CURRENCIES;
|
|
24306
|
+
if (!codes.includes(code2))
|
|
24307
|
+
throw new Error(`Line currency must be one of ${CURRENCY_CHOICES}; got "${raw.trim()}".`);
|
|
24308
|
+
return code2;
|
|
24309
|
+
};
|
|
24310
|
+
|
|
24311
|
+
// src/commands/clients/charges/add.ts
|
|
24312
|
+
var add = command({
|
|
24313
|
+
name: "add",
|
|
24314
|
+
description: "Add a recurring charge to a client and print its id",
|
|
24315
|
+
positionals: {
|
|
24316
|
+
client: exports_external.string().describe("Client id or URL"),
|
|
24317
|
+
description: exports_external.string().describe('What the charge is for, e.g. "Maintenance retainer"')
|
|
24318
|
+
},
|
|
24319
|
+
options: {
|
|
24320
|
+
amount: exports_external.coerce.number().int().describe("Amount in integer minor units (2500 = 25.00)").meta({ short: "a" }),
|
|
24321
|
+
currency: currencyCode.default(DEFAULT_CURRENCY).describe(`One of ${CURRENCY_CHOICES}`).meta({ short: "c" }),
|
|
24322
|
+
cadence: exports_external.enum(RECURRING_CHARGE_CADENCES).default("monthly").describe("monthly or yearly"),
|
|
24323
|
+
"due-month": exports_external.coerce.number().int().optional().describe("Month (1-12) a yearly charge falls due; defaults to the current month")
|
|
24324
|
+
},
|
|
24325
|
+
run: async ({ positionals: { client: ref, description }, options }) => {
|
|
24326
|
+
const backend = await backendClient();
|
|
24327
|
+
const chargeId = await backend.mutation(api2.recurringCharges.create, {
|
|
24328
|
+
clientId: readClientRef(ref),
|
|
24329
|
+
description,
|
|
24330
|
+
amount: options.amount,
|
|
24331
|
+
currency: options.currency,
|
|
24332
|
+
cadence: options.cadence,
|
|
24333
|
+
dueMonth: options["due-month"]
|
|
24334
|
+
});
|
|
24335
|
+
console.log(chargeId);
|
|
24336
|
+
}
|
|
24337
|
+
});
|
|
24338
|
+
|
|
24339
|
+
// src/lib/output.ts
|
|
24340
|
+
var formatMinorAmount = (amount, currency) => `${(amount / 100).toFixed(2)} ${currency}`;
|
|
24341
|
+
var printTable = (rows) => {
|
|
24342
|
+
if (rows.length === 0)
|
|
24343
|
+
return;
|
|
24344
|
+
const columnCount = Math.max(...rows.map((row) => row.length));
|
|
24345
|
+
const widths = Array.from({ length: columnCount }, (_, column) => Math.max(...rows.map((row) => (row[column] ?? "").length)));
|
|
24346
|
+
for (const row of rows) {
|
|
24347
|
+
console.log(row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd());
|
|
24348
|
+
}
|
|
24349
|
+
};
|
|
24350
|
+
|
|
24260
24351
|
// ../../packages/backend/convex/lib/projectRepo.ts
|
|
24261
24352
|
var DEFAULT_REPO_HOST = "github.com";
|
|
24262
24353
|
var REPO_FORMAT_ERROR = "Enter a repository like github.com/owner/name.";
|
|
@@ -24329,24 +24420,6 @@ var resolveQuotation = async (client3, ref) => {
|
|
|
24329
24420
|
throw new Error(`No quotation matches ${ref}.`);
|
|
24330
24421
|
return quotation;
|
|
24331
24422
|
};
|
|
24332
|
-
var resolvePayment = async (client3, ref) => {
|
|
24333
|
-
const payment = await client3.query(api2.invoices.getPayment, { id: ref.trim() });
|
|
24334
|
-
if (!payment)
|
|
24335
|
-
throw new Error(`No payment matches ${ref}.`);
|
|
24336
|
-
return payment;
|
|
24337
|
-
};
|
|
24338
|
-
var resolveRecurringCharge = async (client3, ref) => {
|
|
24339
|
-
const charge = await client3.query(api2.recurringCharges.get, { id: ref.trim() });
|
|
24340
|
-
if (!charge)
|
|
24341
|
-
throw new Error(`No recurring charge matches ${ref}.`);
|
|
24342
|
-
return charge;
|
|
24343
|
-
};
|
|
24344
|
-
var resolveCost = async (client3, ref) => {
|
|
24345
|
-
const cost = await client3.query(api2.costs.get, { id: ref.trim(), today: localToday() });
|
|
24346
|
-
if (!cost)
|
|
24347
|
-
throw new Error(`No cost matches ${ref}.`);
|
|
24348
|
-
return cost;
|
|
24349
|
-
};
|
|
24350
24423
|
var repoProject = async (client3) => {
|
|
24351
24424
|
const repo = await currentRepoKey();
|
|
24352
24425
|
if (!repo)
|
|
@@ -24356,88 +24429,6 @@ var repoProject = async (client3) => {
|
|
|
24356
24429
|
throw new Error(`No project is connected to ${repo}. Connect one on the project in the dashboard.`);
|
|
24357
24430
|
return project;
|
|
24358
24431
|
};
|
|
24359
|
-
var missingLabelError = async (client3, lead) => {
|
|
24360
|
-
const labels = await client3.query(api2.issueLabels.list, {});
|
|
24361
|
-
const available = labels.map((candidate) => candidate.name).join(", ");
|
|
24362
|
-
return new Error(available ? `${lead} The labels are: ${available}.` : "There are no labels yet.");
|
|
24363
|
-
};
|
|
24364
|
-
var resolveIssueLabels = async (client3, names) => await Promise.all(names.map(async (name) => {
|
|
24365
|
-
const label = await client3.query(api2.issueLabels.find, { ref: name });
|
|
24366
|
-
if (!label)
|
|
24367
|
-
throw await missingLabelError(client3, `No label named "${name}".`);
|
|
24368
|
-
return label;
|
|
24369
|
-
}));
|
|
24370
|
-
var resolveIssueLabel = async (client3, ref) => {
|
|
24371
|
-
const label = await client3.query(api2.issueLabels.find, { ref });
|
|
24372
|
-
if (!label)
|
|
24373
|
-
throw await missingLabelError(client3, `No label matches "${ref}".`);
|
|
24374
|
-
return label;
|
|
24375
|
-
};
|
|
24376
|
-
var resolveMilestone = async (client3, projectId, name) => {
|
|
24377
|
-
const milestone = await client3.query(api2.milestones.find, { projectId, name });
|
|
24378
|
-
if (!milestone) {
|
|
24379
|
-
const milestones = await client3.query(api2.milestones.listByProject, { projectId, today: localToday() });
|
|
24380
|
-
const names = milestones.map((candidate) => candidate.name).join(", ");
|
|
24381
|
-
throw new Error(names ? `No milestone named "${name}". The project has: ${names}.` : "The project has no milestones.");
|
|
24382
|
-
}
|
|
24383
|
-
return milestone;
|
|
24384
|
-
};
|
|
24385
|
-
|
|
24386
|
-
// src/commands/clients/archive.ts
|
|
24387
|
-
var archive = command({
|
|
24388
|
-
name: "archive",
|
|
24389
|
-
description: "Archive a client: it leaves pickers and default lists, and every existing reference stays intact",
|
|
24390
|
-
positionals: {
|
|
24391
|
-
id: exports_external.string().describe("Client id or URL")
|
|
24392
|
-
},
|
|
24393
|
-
run: async ({ positionals: { id } }) => {
|
|
24394
|
-
const backend = await backendClient();
|
|
24395
|
-
const client3 = await resolveClient(backend, id);
|
|
24396
|
-
await backend.mutation(api2.clients.archive, { id: client3._id });
|
|
24397
|
-
console.log(`Archived ${client3.name}.`);
|
|
24398
|
-
}
|
|
24399
|
-
});
|
|
24400
|
-
|
|
24401
|
-
// src/commands/clients/charges/add.ts
|
|
24402
|
-
var add = command({
|
|
24403
|
-
name: "add",
|
|
24404
|
-
description: "Add a recurring charge to a client and print its id",
|
|
24405
|
-
positionals: {
|
|
24406
|
-
client: exports_external.string().describe("Client id or URL"),
|
|
24407
|
-
description: exports_external.string().describe('What the charge is for, e.g. "Maintenance retainer"')
|
|
24408
|
-
},
|
|
24409
|
-
options: {
|
|
24410
|
-
amount: exports_external.coerce.number().int().describe("Amount in integer minor units (2500 = 25.00)").meta({ short: "a" }),
|
|
24411
|
-
currency: exports_external.string().describe("Three-letter code like USD or EUR").meta({ short: "c" }),
|
|
24412
|
-
cadence: exports_external.enum(RECURRING_CHARGE_CADENCES).default("monthly").describe("monthly or yearly"),
|
|
24413
|
-
"due-month": exports_external.coerce.number().int().optional().describe("Month (1-12) a yearly charge falls due; defaults to the current month")
|
|
24414
|
-
},
|
|
24415
|
-
run: async ({ positionals: { client: ref, description }, options }) => {
|
|
24416
|
-
const backend = await backendClient();
|
|
24417
|
-
const client3 = await resolveClient(backend, ref);
|
|
24418
|
-
const chargeId = await backend.mutation(api2.recurringCharges.create, {
|
|
24419
|
-
clientId: client3._id,
|
|
24420
|
-
description,
|
|
24421
|
-
amount: options.amount,
|
|
24422
|
-
currency: options.currency,
|
|
24423
|
-
cadence: options.cadence,
|
|
24424
|
-
dueMonth: options["due-month"]
|
|
24425
|
-
});
|
|
24426
|
-
console.log(chargeId);
|
|
24427
|
-
}
|
|
24428
|
-
});
|
|
24429
|
-
|
|
24430
|
-
// src/lib/output.ts
|
|
24431
|
-
var formatMinorAmount = (amount, currency) => `${(amount / 100).toFixed(2)} ${currency}`;
|
|
24432
|
-
var printTable = (rows) => {
|
|
24433
|
-
if (rows.length === 0)
|
|
24434
|
-
return;
|
|
24435
|
-
const columnCount = Math.max(...rows.map((row) => row.length));
|
|
24436
|
-
const widths = Array.from({ length: columnCount }, (_, column) => Math.max(...rows.map((row) => (row[column] ?? "").length)));
|
|
24437
|
-
for (const row of rows) {
|
|
24438
|
-
console.log(row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd());
|
|
24439
|
-
}
|
|
24440
|
-
};
|
|
24441
24432
|
|
|
24442
24433
|
// src/commands/clients/charges/list.ts
|
|
24443
24434
|
var monthName = (month) => new Date(Date.UTC(2000, month - 1)).toLocaleString("en-US", { month: "short", timeZone: "UTC" });
|
|
@@ -24480,9 +24471,8 @@ var pause = command({
|
|
|
24480
24471
|
},
|
|
24481
24472
|
run: async ({ positionals: { id } }) => {
|
|
24482
24473
|
const backend = await backendClient();
|
|
24483
|
-
const
|
|
24484
|
-
|
|
24485
|
-
console.log(`Paused ${charge.description}.`);
|
|
24474
|
+
const { description } = await backend.mutation(api2.recurringCharges.pause, { id: id.trim() });
|
|
24475
|
+
console.log(`Paused ${description}.`);
|
|
24486
24476
|
}
|
|
24487
24477
|
});
|
|
24488
24478
|
|
|
@@ -24495,9 +24485,8 @@ var remove = command({
|
|
|
24495
24485
|
},
|
|
24496
24486
|
run: async ({ positionals: { id } }) => {
|
|
24497
24487
|
const backend = await backendClient();
|
|
24498
|
-
const
|
|
24499
|
-
|
|
24500
|
-
console.log(`Removed ${charge.description}.`);
|
|
24488
|
+
const { description } = await backend.mutation(api2.recurringCharges.remove, { id: id.trim() });
|
|
24489
|
+
console.log(`Removed ${description}.`);
|
|
24501
24490
|
}
|
|
24502
24491
|
});
|
|
24503
24492
|
|
|
@@ -24510,9 +24499,8 @@ var resume = command({
|
|
|
24510
24499
|
},
|
|
24511
24500
|
run: async ({ positionals: { id } }) => {
|
|
24512
24501
|
const backend = await backendClient();
|
|
24513
|
-
const
|
|
24514
|
-
|
|
24515
|
-
console.log(`Resumed ${charge.description}.`);
|
|
24502
|
+
const { description } = await backend.mutation(api2.recurringCharges.resume, { id: id.trim() });
|
|
24503
|
+
console.log(`Resumed ${description}.`);
|
|
24516
24504
|
}
|
|
24517
24505
|
});
|
|
24518
24506
|
|
|
@@ -24526,7 +24514,7 @@ var update = command({
|
|
|
24526
24514
|
options: {
|
|
24527
24515
|
description: exports_external.string().optional().describe("New description").meta({ short: "d" }),
|
|
24528
24516
|
amount: exports_external.coerce.number().int().optional().describe("Amount in integer minor units").meta({ short: "a" }),
|
|
24529
|
-
currency:
|
|
24517
|
+
currency: currencyCode.optional().describe(`One of ${CURRENCY_CHOICES}`).meta({ short: "c" }),
|
|
24530
24518
|
cadence: exports_external.enum(RECURRING_CHARGE_CADENCES).optional().describe("monthly or yearly"),
|
|
24531
24519
|
"due-month": exports_external.coerce.number().int().optional().describe("Month (1-12) a yearly charge falls due")
|
|
24532
24520
|
},
|
|
@@ -24535,16 +24523,15 @@ var update = command({
|
|
|
24535
24523
|
throw new Error("Nothing to update. Pass --description, --amount, --currency, --cadence, or --due-month.");
|
|
24536
24524
|
}
|
|
24537
24525
|
const backend = await backendClient();
|
|
24538
|
-
const
|
|
24539
|
-
|
|
24540
|
-
id: charge._id,
|
|
24526
|
+
const { description } = await backend.mutation(api2.recurringCharges.update, {
|
|
24527
|
+
id: id.trim(),
|
|
24541
24528
|
description: options.description,
|
|
24542
24529
|
amount: options.amount,
|
|
24543
24530
|
currency: options.currency,
|
|
24544
24531
|
cadence: options.cadence,
|
|
24545
24532
|
dueMonth: options["due-month"]
|
|
24546
24533
|
});
|
|
24547
|
-
console.log(`Updated ${
|
|
24534
|
+
console.log(`Updated ${description}.`);
|
|
24548
24535
|
}
|
|
24549
24536
|
});
|
|
24550
24537
|
|
|
@@ -24658,65 +24645,6 @@ More clients exist; raise --limit past ${limit}.`);
|
|
|
24658
24645
|
}
|
|
24659
24646
|
});
|
|
24660
24647
|
|
|
24661
|
-
// ../../packages/backend/convex/lib/clientProfile.ts
|
|
24662
|
-
var normalizeClientName = (value) => {
|
|
24663
|
-
const name = value.trim();
|
|
24664
|
-
if (!name)
|
|
24665
|
-
throw new ConvexError("Client name is required.");
|
|
24666
|
-
if (name.length > MAX_CLIENT_NAME_LENGTH) {
|
|
24667
|
-
throw new ConvexError(`Client name must be ${MAX_CLIENT_NAME_LENGTH} characters or fewer.`);
|
|
24668
|
-
}
|
|
24669
|
-
return name;
|
|
24670
|
-
};
|
|
24671
|
-
var normalizeOptionalText = (value, label, max) => {
|
|
24672
|
-
const text = value?.trim();
|
|
24673
|
-
if (!text)
|
|
24674
|
-
return;
|
|
24675
|
-
if (text.length > max)
|
|
24676
|
-
throw new ConvexError(`${label} must be ${max} characters or fewer.`);
|
|
24677
|
-
return text;
|
|
24678
|
-
};
|
|
24679
|
-
var normalizeLegalName = (value) => normalizeOptionalText(value, "Legal name", MAX_CLIENT_LEGAL_NAME_LENGTH);
|
|
24680
|
-
var normalizeBillingAddress = (value) => normalizeOptionalText(value, "Billing address", MAX_CLIENT_BILLING_ADDRESS_LENGTH);
|
|
24681
|
-
var normalizeTaxId = (value) => normalizeOptionalText(value, "Tax ID", MAX_CLIENT_TAX_ID_LENGTH);
|
|
24682
|
-
var normalizeBillingEmail = (value) => {
|
|
24683
|
-
const email3 = normalizeOptionalText(value, "Billing email", MAX_CLIENT_BILLING_EMAIL_LENGTH);
|
|
24684
|
-
if (email3 === undefined)
|
|
24685
|
-
return;
|
|
24686
|
-
if (!/^[^\s@]+@[^\s@]+$/.test(email3))
|
|
24687
|
-
throw new ConvexError("Billing email must be a single valid address.");
|
|
24688
|
-
return email3;
|
|
24689
|
-
};
|
|
24690
|
-
var assertPaymentTermsDays = (value) => {
|
|
24691
|
-
if (value === undefined)
|
|
24692
|
-
return;
|
|
24693
|
-
if (!Number.isInteger(value) || value < 0 || value > MAX_CLIENT_PAYMENT_TERMS_DAYS) {
|
|
24694
|
-
throw new ConvexError(`Payment terms must be a whole number of days between 0 and ${MAX_CLIENT_PAYMENT_TERMS_DAYS}.`);
|
|
24695
|
-
}
|
|
24696
|
-
return value;
|
|
24697
|
-
};
|
|
24698
|
-
var normalizeCurrencyCode = (value) => {
|
|
24699
|
-
const code2 = value?.trim().toUpperCase();
|
|
24700
|
-
if (!code2)
|
|
24701
|
-
return;
|
|
24702
|
-
if (!/^[A-Z]{3}$/.test(code2))
|
|
24703
|
-
throw new ConvexError("Currency must be a three-letter code like USD or EUR.");
|
|
24704
|
-
return code2;
|
|
24705
|
-
};
|
|
24706
|
-
|
|
24707
|
-
// ../../node_modules/.bun/convex-helpers@0.1.120+1dd148a8b8056605/node_modules/convex-helpers/index.js
|
|
24708
|
-
var _error = Symbol();
|
|
24709
|
-
|
|
24710
|
-
// ../../packages/backend/convex/lib/billingRun.ts
|
|
24711
|
-
var assertBillingDay = (value) => {
|
|
24712
|
-
if (value === undefined)
|
|
24713
|
-
return;
|
|
24714
|
-
if (!Number.isInteger(value) || value < 1 || value > MAX_BILLING_DAY) {
|
|
24715
|
-
throw new ConvexError(`Billing day must be a whole number from 1 to ${MAX_BILLING_DAY}.`);
|
|
24716
|
-
}
|
|
24717
|
-
return value;
|
|
24718
|
-
};
|
|
24719
|
-
|
|
24720
24648
|
// src/commands/clients/update.ts
|
|
24721
24649
|
var update2 = command({
|
|
24722
24650
|
name: "update",
|
|
@@ -24740,42 +24668,19 @@ var update2 = command({
|
|
|
24740
24668
|
throw new Error("Nothing to update. Pass a profile flag, or a --no form to clear one.");
|
|
24741
24669
|
}
|
|
24742
24670
|
const text = (value) => value === null ? "" : value;
|
|
24743
|
-
const profile = {
|
|
24744
|
-
name: options.name,
|
|
24745
|
-
legalName: text(options["legal-name"]),
|
|
24746
|
-
billingAddress: text(options.address),
|
|
24747
|
-
billingEmail: text(options.email),
|
|
24748
|
-
taxId: text(options["tax-id"])
|
|
24749
|
-
};
|
|
24750
|
-
const terms = options["payment-terms"];
|
|
24751
|
-
const billingDay = options["billing-day"];
|
|
24752
|
-
if (profile.name !== undefined)
|
|
24753
|
-
normalizeClientName(profile.name);
|
|
24754
|
-
normalizeLegalName(profile.legalName);
|
|
24755
|
-
normalizeBillingAddress(profile.billingAddress);
|
|
24756
|
-
normalizeBillingEmail(profile.billingEmail);
|
|
24757
|
-
normalizeTaxId(profile.taxId);
|
|
24758
|
-
assertPaymentTermsDays(terms ?? undefined);
|
|
24759
|
-
normalizeCurrencyCode(options.currency ?? undefined);
|
|
24760
|
-
assertBillingDay(billingDay ?? undefined);
|
|
24761
24671
|
const backend = await backendClient();
|
|
24762
|
-
const
|
|
24763
|
-
|
|
24764
|
-
|
|
24765
|
-
|
|
24766
|
-
|
|
24767
|
-
|
|
24768
|
-
|
|
24769
|
-
|
|
24770
|
-
|
|
24771
|
-
|
|
24772
|
-
|
|
24773
|
-
|
|
24774
|
-
}
|
|
24775
|
-
if (billingDay !== undefined) {
|
|
24776
|
-
await backend.mutation(api2.clients.setBillingDay, { id: client3._id, billingDay: billingDay ?? undefined });
|
|
24777
|
-
}
|
|
24778
|
-
console.log(`Updated ${options.name?.trim() ?? client3.name}.`);
|
|
24672
|
+
const { name } = await backend.mutation(api2.clients.edit, {
|
|
24673
|
+
id: readClientRef(id),
|
|
24674
|
+
...options.name !== undefined ? { name: options.name } : {},
|
|
24675
|
+
...options["legal-name"] !== undefined ? { legalName: text(options["legal-name"]) } : {},
|
|
24676
|
+
...options.address !== undefined ? { billingAddress: text(options.address) } : {},
|
|
24677
|
+
...options.email !== undefined ? { billingEmail: text(options.email) } : {},
|
|
24678
|
+
...options["tax-id"] !== undefined ? { taxId: text(options["tax-id"]) } : {},
|
|
24679
|
+
...options["payment-terms"] !== undefined ? { paymentTermsDays: options["payment-terms"] } : {},
|
|
24680
|
+
...options.currency !== undefined ? { defaultCurrency: options.currency } : {},
|
|
24681
|
+
...options["billing-day"] !== undefined ? { billingDay: options["billing-day"] } : {}
|
|
24682
|
+
});
|
|
24683
|
+
console.log(`Updated ${name}.`);
|
|
24779
24684
|
}
|
|
24780
24685
|
});
|
|
24781
24686
|
|
|
@@ -24793,15 +24698,16 @@ var create2 = command({
|
|
|
24793
24698
|
name: "create",
|
|
24794
24699
|
description: "Record a cost and print its id",
|
|
24795
24700
|
longDescription: `One record per real-world bill, never one per project. A cost links to any number
|
|
24796
|
-
of projects, each carrying the full amount;
|
|
24797
|
-
|
|
24798
|
-
the
|
|
24701
|
+
of projects, each carrying the full amount; until a client's project carries it, it
|
|
24702
|
+
is overhead. Billable
|
|
24703
|
+
costs pass through onto the invoices of whichever client their projects belong to,
|
|
24704
|
+
so the links cannot span two clients; with no links, billable waits for one.`,
|
|
24799
24705
|
positionals: {
|
|
24800
24706
|
name: exports_external.string().describe('What the bill is for, e.g. "Figma seats"')
|
|
24801
24707
|
},
|
|
24802
24708
|
options: {
|
|
24803
24709
|
amount: exports_external.coerce.number().int().describe("Amount in integer minor units (2500 = 25.00)").meta({ short: "a" }),
|
|
24804
|
-
currency:
|
|
24710
|
+
currency: currencyCode.default(DEFAULT_CURRENCY).describe(`One of ${CURRENCY_CHOICES}`).meta({ short: "c" }),
|
|
24805
24711
|
kind: exports_external.enum(COST_KINDS).default("one_off").describe("one_off, monthly, or yearly").meta({ short: "k" }),
|
|
24806
24712
|
date: exports_external.string().optional().describe("The one-off's date, or a subscription's start (YYYY-MM-DD); default today"),
|
|
24807
24713
|
end: exports_external.string().optional().describe("Last date a subscription is committed to (YYYY-MM-DD)"),
|
|
@@ -24811,7 +24717,6 @@ the same client.`,
|
|
|
24811
24717
|
},
|
|
24812
24718
|
run: async ({ positionals: { name }, options }) => {
|
|
24813
24719
|
const backend = await backendClient();
|
|
24814
|
-
const projects = await Promise.all((options.project ?? []).map((ref) => resolveProject(backend, ref)));
|
|
24815
24720
|
const costId = await backend.mutation(api2.costs.create, {
|
|
24816
24721
|
name,
|
|
24817
24722
|
note: options.note,
|
|
@@ -24820,7 +24725,7 @@ the same client.`,
|
|
|
24820
24725
|
endDate: options.end,
|
|
24821
24726
|
amount: options.amount,
|
|
24822
24727
|
currency: options.currency,
|
|
24823
|
-
projectIds:
|
|
24728
|
+
projectIds: options.project?.map(readProjectRef),
|
|
24824
24729
|
billable: options.billable
|
|
24825
24730
|
});
|
|
24826
24731
|
console.log(costId);
|
|
@@ -24840,10 +24745,9 @@ var end = command({
|
|
|
24840
24745
|
},
|
|
24841
24746
|
run: async ({ positionals: { id }, options }) => {
|
|
24842
24747
|
const backend = await backendClient();
|
|
24843
|
-
const cost = await resolveCost(backend, id);
|
|
24844
24748
|
const today = localToday();
|
|
24845
|
-
await backend.mutation(api2.costs.end, { id:
|
|
24846
|
-
console.log(`Ended ${
|
|
24749
|
+
const { name } = await backend.mutation(api2.costs.end, { id: id.trim(), endDate: options.date, today });
|
|
24750
|
+
console.log(`Ended ${name} on ${options.date ?? today}.`);
|
|
24847
24751
|
}
|
|
24848
24752
|
});
|
|
24849
24753
|
|
|
@@ -24854,9 +24758,10 @@ var costKindLabel = (cost) => {
|
|
|
24854
24758
|
return cost.endDate === undefined ? cost.kind : `${cost.kind} \u2192 ${cost.endDate}`;
|
|
24855
24759
|
};
|
|
24856
24760
|
var costProjectsLabel = (cost) => {
|
|
24857
|
-
|
|
24858
|
-
|
|
24859
|
-
|
|
24761
|
+
const names = cost.projects.map((project) => project.name).join(", ");
|
|
24762
|
+
if (!cost.overhead)
|
|
24763
|
+
return names;
|
|
24764
|
+
return names === "" ? "overhead" : `overhead (${names})`;
|
|
24860
24765
|
};
|
|
24861
24766
|
|
|
24862
24767
|
// src/commands/costs/get.ts
|
|
@@ -24904,7 +24809,7 @@ after an edit. A yearly cost lands whole in its renewal month, never amortized.`
|
|
|
24904
24809
|
month: exports_external.string().optional().describe("Derive one month's costs (YYYY-MM)").meta({ short: "m" }),
|
|
24905
24810
|
kind: exports_external.enum(COST_KINDS).optional().describe("Only one_off, monthly, or yearly").meta({ short: "k" }),
|
|
24906
24811
|
project: exports_external.string().optional().describe("Only costs linked to this project (id, URL, or repo)").meta({ short: "p" }),
|
|
24907
|
-
overhead: exports_external.boolean().default(false).describe("Only costs
|
|
24812
|
+
overhead: exports_external.boolean().default(false).describe("Only costs no client's project carries"),
|
|
24908
24813
|
billable: exports_external.boolean().optional().describe("Only billable costs, or --no-billable").meta({ negatable: true }),
|
|
24909
24814
|
limit: exports_external.coerce.number().int().positive().default(50).describe("Most costs to print"),
|
|
24910
24815
|
json: exports_external.boolean().default(false).describe("Print as JSON")
|
|
@@ -24954,9 +24859,8 @@ var remove2 = command({
|
|
|
24954
24859
|
},
|
|
24955
24860
|
run: async ({ positionals: { id } }) => {
|
|
24956
24861
|
const backend = await backendClient();
|
|
24957
|
-
const
|
|
24958
|
-
|
|
24959
|
-
console.log(`Deleted ${cost.name}.`);
|
|
24862
|
+
const { name } = await backend.mutation(api2.costs.remove, { id: id.trim() });
|
|
24863
|
+
console.log(`Deleted ${name}.`);
|
|
24960
24864
|
}
|
|
24961
24865
|
});
|
|
24962
24866
|
|
|
@@ -24975,7 +24879,7 @@ is fixed at creation \u2014 delete and re-record to change it.`,
|
|
|
24975
24879
|
options: {
|
|
24976
24880
|
name: exports_external.string().optional().describe("New name"),
|
|
24977
24881
|
amount: exports_external.coerce.number().int().optional().describe("Amount in integer minor units").meta({ short: "a" }),
|
|
24978
|
-
currency:
|
|
24882
|
+
currency: currencyCode.optional().describe(`One of ${CURRENCY_CHOICES}`).meta({ short: "c" }),
|
|
24979
24883
|
date: exports_external.string().optional().describe("The one-off's date, or a subscription's start (YYYY-MM-DD)"),
|
|
24980
24884
|
end: exports_external.string().nullable().optional().describe("End date (YYYY-MM-DD), or --no-end to resume").meta({
|
|
24981
24885
|
negatable: true
|
|
@@ -24991,10 +24895,8 @@ is fixed at creation \u2014 delete and re-record to change it.`,
|
|
|
24991
24895
|
throw new Error("Nothing to update. Pass --name, --amount, --currency, --date, --end, --project, --billable, or --note.");
|
|
24992
24896
|
}
|
|
24993
24897
|
const backend = await backendClient();
|
|
24994
|
-
const
|
|
24995
|
-
|
|
24996
|
-
await backend.mutation(api2.costs.update, {
|
|
24997
|
-
id: cost._id,
|
|
24898
|
+
const { name } = await backend.mutation(api2.costs.update, {
|
|
24899
|
+
id: id.trim(),
|
|
24998
24900
|
today: localToday(),
|
|
24999
24901
|
name: options.name,
|
|
25000
24902
|
note: options.note === null ? "" : options.note,
|
|
@@ -25002,10 +24904,10 @@ is fixed at creation \u2014 delete and re-record to change it.`,
|
|
|
25002
24904
|
endDate: options.end === null ? "" : options.end,
|
|
25003
24905
|
amount: options.amount,
|
|
25004
24906
|
currency: options.currency,
|
|
25005
|
-
projectIds: options.project ===
|
|
24907
|
+
projectIds: options.project === null ? [] : options.project?.map(readProjectRef),
|
|
25006
24908
|
billable: options.billable
|
|
25007
24909
|
});
|
|
25008
|
-
console.log(`Updated ${
|
|
24910
|
+
console.log(`Updated ${name}.`);
|
|
25009
24911
|
}
|
|
25010
24912
|
});
|
|
25011
24913
|
|
|
@@ -25015,7 +24917,7 @@ var costs = group({
|
|
|
25015
24917
|
description: "Manage costs",
|
|
25016
24918
|
longDescription: `A cost is money the studio spends, recorded once per real-world bill. It links to any
|
|
25017
24919
|
number of projects \u2014 each carrying the full amount, so per-project figures never sum
|
|
25018
|
-
to the studio total
|
|
24920
|
+
to the studio total. A cost no client's project carries is overhead. Occurrences are always derived
|
|
25019
24921
|
from the record, so \`costs list --month\` answers for any month, past or future.`,
|
|
25020
24922
|
commands: [create2, list3, get2, update3, end, remove2]
|
|
25021
24923
|
});
|
|
@@ -25309,9 +25211,10 @@ var seen = command({
|
|
|
25309
25211
|
},
|
|
25310
25212
|
run: async ({ positionals: { id } }) => {
|
|
25311
25213
|
const client3 = await backendClient();
|
|
25312
|
-
const
|
|
25313
|
-
|
|
25314
|
-
|
|
25214
|
+
const { identifier } = await client3.mutation(api2.inbox.markSeen, {
|
|
25215
|
+
targetId: id === undefined ? undefined : readIssueRef(id)
|
|
25216
|
+
});
|
|
25217
|
+
console.log(identifier ? `${identifier} marked seen.` : "Inbox marked seen.");
|
|
25315
25218
|
}
|
|
25316
25219
|
});
|
|
25317
25220
|
|
|
@@ -25325,10 +25228,10 @@ var inbox = group({
|
|
|
25325
25228
|
});
|
|
25326
25229
|
|
|
25327
25230
|
// src/lib/line-items.ts
|
|
25328
|
-
var LINE_ITEM_SYNTAX = 'description|quantity[ unit]|unit price|currency, e.g. "Development|2.5 hours|10000|
|
|
25231
|
+
var LINE_ITEM_SYNTAX = 'description|quantity[ unit]|unit price[|currency], e.g. "Development|2.5 hours|10000|USD"; currency defaults to EGP';
|
|
25329
25232
|
var parseLineItem = (value) => {
|
|
25330
25233
|
const parts = value.split("|");
|
|
25331
|
-
if (parts.length !== 4)
|
|
25234
|
+
if (parts.length !== 3 && parts.length !== 4)
|
|
25332
25235
|
throw new Error(`Line items read as ${LINE_ITEM_SYNTAX}; got "${value}".`);
|
|
25333
25236
|
const [description = "", quantityPart = "", pricePart = "", currency = ""] = parts;
|
|
25334
25237
|
const [rawQuantity = "", ...unitWords] = quantityPart.trim().split(/\s+/);
|
|
@@ -25341,12 +25244,18 @@ var parseLineItem = (value) => {
|
|
|
25341
25244
|
throw new Error(`Unit prices must be integer minor units (10000 = 100.00), got "${pricePart.trim()}".`);
|
|
25342
25245
|
}
|
|
25343
25246
|
const unit = unitWords.join(" ");
|
|
25344
|
-
return {
|
|
25247
|
+
return {
|
|
25248
|
+
description: description.trim(),
|
|
25249
|
+
quantity,
|
|
25250
|
+
unit: unit || undefined,
|
|
25251
|
+
unitPrice,
|
|
25252
|
+
currency: parseLineCurrency(currency)
|
|
25253
|
+
};
|
|
25345
25254
|
};
|
|
25346
|
-
var QUOTATION_LINE_ITEM_SYNTAX = 'description|quantity[ unit]|unit price|currency[|section], e.g. "Prototype|1|100000|
|
|
25255
|
+
var QUOTATION_LINE_ITEM_SYNTAX = 'description|quantity[ unit]|unit price[|currency[|section]], e.g. "Prototype|1|100000|USD|Phase 1"; an empty currency part defaults to EGP';
|
|
25347
25256
|
var parseQuotationLineItem = (value) => {
|
|
25348
25257
|
const parts = value.split("|");
|
|
25349
|
-
if (parts.length
|
|
25258
|
+
if (parts.length < 3 || parts.length > 5) {
|
|
25350
25259
|
throw new Error(`Quotation line items read as ${QUOTATION_LINE_ITEM_SYNTAX}; got "${value}".`);
|
|
25351
25260
|
}
|
|
25352
25261
|
const section = parts.length === 5 ? parts.pop()?.trim() : undefined;
|
|
@@ -25369,11 +25278,9 @@ var create3 = command({
|
|
|
25369
25278
|
},
|
|
25370
25279
|
run: async ({ positionals: { client: ref }, options }) => {
|
|
25371
25280
|
const backend = await backendClient();
|
|
25372
|
-
const client3 = await resolveClient(backend, ref);
|
|
25373
|
-
const project = options.project === undefined ? undefined : await resolveProject(backend, options.project);
|
|
25374
25281
|
const invoiceId = await backend.mutation(api2.invoices.create, {
|
|
25375
|
-
clientId:
|
|
25376
|
-
projectId: project
|
|
25282
|
+
clientId: readClientRef(ref),
|
|
25283
|
+
projectId: options.project === undefined ? undefined : readProjectRef(options.project),
|
|
25377
25284
|
lineItems: options.line?.map(parseLineItem),
|
|
25378
25285
|
notes: options.notes,
|
|
25379
25286
|
dueDate: options.due
|
|
@@ -25391,8 +25298,21 @@ var duplicate = command({
|
|
|
25391
25298
|
},
|
|
25392
25299
|
run: async ({ positionals: { id } }) => {
|
|
25393
25300
|
const backend = await backendClient();
|
|
25394
|
-
|
|
25395
|
-
|
|
25301
|
+
console.log(await backend.mutation(api2.invoices.duplicate, { id: readInvoiceRef(id) }));
|
|
25302
|
+
}
|
|
25303
|
+
});
|
|
25304
|
+
|
|
25305
|
+
// src/commands/invoices/email.ts
|
|
25306
|
+
var email3 = command({
|
|
25307
|
+
name: "email",
|
|
25308
|
+
description: "Email the invoice's link to the client's billing address; repeatable, and never required",
|
|
25309
|
+
positionals: {
|
|
25310
|
+
id: exports_external.string().describe("Invoice id or URL")
|
|
25311
|
+
},
|
|
25312
|
+
run: async ({ positionals: { id } }) => {
|
|
25313
|
+
const backend = await backendClient();
|
|
25314
|
+
const { to, number: number4 } = await backend.mutation(api2.invoices.email, { id: readInvoiceRef(id) });
|
|
25315
|
+
console.log(`Emailed ${number4} to ${to}.`);
|
|
25396
25316
|
}
|
|
25397
25317
|
});
|
|
25398
25318
|
|
|
@@ -25421,6 +25341,9 @@ var get3 = command({
|
|
|
25421
25341
|
console.log(`Due: ${invoice.dueDate}`);
|
|
25422
25342
|
if (invoice.publicUrl)
|
|
25423
25343
|
console.log(`Link: ${invoice.publicUrl}`);
|
|
25344
|
+
if (invoice.status !== "draft") {
|
|
25345
|
+
console.log(`Emailed: ${invoice.lastEmailedAt === undefined ? "never" : localDateString(invoice.lastEmailedAt)}`);
|
|
25346
|
+
}
|
|
25424
25347
|
if (invoice.lineItems.length > 0) {
|
|
25425
25348
|
console.log("");
|
|
25426
25349
|
printTable([
|
|
@@ -25460,9 +25383,7 @@ var issue2 = command({
|
|
|
25460
25383
|
},
|
|
25461
25384
|
run: async ({ positionals: { id } }) => {
|
|
25462
25385
|
const backend = await backendClient();
|
|
25463
|
-
const
|
|
25464
|
-
await backend.mutation(api2.invoices.issue, { id: invoice._id, today: localToday() });
|
|
25465
|
-
const issued = await resolveInvoice(backend, invoice._id);
|
|
25386
|
+
const issued = await backend.mutation(api2.invoices.issue, { id: readInvoiceRef(id), today: localToday() });
|
|
25466
25387
|
console.log(`Issued ${issued.number}, due ${issued.dueDate}.`);
|
|
25467
25388
|
}
|
|
25468
25389
|
});
|
|
@@ -25477,10 +25398,11 @@ var link = command({
|
|
|
25477
25398
|
},
|
|
25478
25399
|
run: async ({ positionals: { id, project: projectRef } }) => {
|
|
25479
25400
|
const backend = await backendClient();
|
|
25480
|
-
const
|
|
25481
|
-
|
|
25482
|
-
|
|
25483
|
-
|
|
25401
|
+
const { projectName } = await backend.mutation(api2.invoices.setProject, {
|
|
25402
|
+
id: readInvoiceRef(id),
|
|
25403
|
+
projectId: projectRef === undefined ? undefined : readProjectRef(projectRef)
|
|
25404
|
+
});
|
|
25405
|
+
console.log(projectName === undefined ? "Cleared the project link." : `Linked to ${projectName}.`);
|
|
25484
25406
|
}
|
|
25485
25407
|
});
|
|
25486
25408
|
|
|
@@ -25508,7 +25430,7 @@ var list4 = command({
|
|
|
25508
25430
|
if (result.page.length === 0)
|
|
25509
25431
|
return console.log("No invoices yet.");
|
|
25510
25432
|
printTable([
|
|
25511
|
-
["NUMBER", "CLIENT", "PROJECT", "STATUS", "TOTAL", "DUE", "ID"],
|
|
25433
|
+
["NUMBER", "CLIENT", "PROJECT", "STATUS", "TOTAL", "DUE", "EMAILED", "ID"],
|
|
25512
25434
|
...result.page.map((invoice) => [
|
|
25513
25435
|
invoice.number ?? "draft",
|
|
25514
25436
|
invoice.clientName,
|
|
@@ -25516,6 +25438,7 @@ var list4 = command({
|
|
|
25516
25438
|
invoice.derivedStatus,
|
|
25517
25439
|
formatPerCurrency(invoice.totals.map(({ currency, total }) => ({ currency, amount: total }))),
|
|
25518
25440
|
invoice.dueDate ?? "-",
|
|
25441
|
+
invoice.lastEmailedAt === undefined ? "-" : localDateString(invoice.lastEmailedAt),
|
|
25519
25442
|
invoice._id
|
|
25520
25443
|
])
|
|
25521
25444
|
]);
|
|
@@ -25540,18 +25463,16 @@ var add2 = command({
|
|
|
25540
25463
|
},
|
|
25541
25464
|
run: async ({ positionals: { invoice: ref }, options }) => {
|
|
25542
25465
|
const backend = await backendClient();
|
|
25543
|
-
const
|
|
25544
|
-
|
|
25545
|
-
invoiceId: invoice._id,
|
|
25466
|
+
const payment = await backend.mutation(api2.invoices.addPayment, {
|
|
25467
|
+
invoiceId: readInvoiceRef(ref),
|
|
25546
25468
|
amount: options.amount,
|
|
25547
25469
|
currency: options.currency,
|
|
25548
25470
|
paidOn: options.date ?? localToday(),
|
|
25549
25471
|
note: options.note
|
|
25550
25472
|
});
|
|
25551
|
-
console.log(
|
|
25552
|
-
if (
|
|
25553
|
-
console.log(`${
|
|
25554
|
-
}
|
|
25473
|
+
console.log(payment.id);
|
|
25474
|
+
if (payment.paid)
|
|
25475
|
+
console.log(`${payment.number} is now fully paid.`);
|
|
25555
25476
|
}
|
|
25556
25477
|
});
|
|
25557
25478
|
|
|
@@ -25592,8 +25513,7 @@ var remove3 = command({
|
|
|
25592
25513
|
},
|
|
25593
25514
|
run: async ({ positionals: { id } }) => {
|
|
25594
25515
|
const backend = await backendClient();
|
|
25595
|
-
|
|
25596
|
-
await backend.mutation(api2.invoices.removePayment, { id: payment._id });
|
|
25516
|
+
await backend.mutation(api2.invoices.removePayment, { id: id.trim() });
|
|
25597
25517
|
console.log("Removed the payment.");
|
|
25598
25518
|
}
|
|
25599
25519
|
});
|
|
@@ -25617,9 +25537,8 @@ var rotateLink = command({
|
|
|
25617
25537
|
},
|
|
25618
25538
|
run: async ({ positionals: { id } }) => {
|
|
25619
25539
|
const backend = await backendClient();
|
|
25620
|
-
const
|
|
25621
|
-
|
|
25622
|
-
console.log(`${invoice.number} now reads at ${url2}. Send it again \u2014 the previous link is dead.`);
|
|
25540
|
+
const { url: url2, number: number4 } = await backend.mutation(api2.invoices.rotateLink, { id: readInvoiceRef(id) });
|
|
25541
|
+
console.log(`${number4} now reads at ${url2}. Send it again \u2014 the previous link is dead.`);
|
|
25623
25542
|
}
|
|
25624
25543
|
});
|
|
25625
25544
|
|
|
@@ -25641,14 +25560,13 @@ var update4 = command({
|
|
|
25641
25560
|
throw new Error("Nothing to update. Pass --line, --notes, or --due (or a --no form to clear).");
|
|
25642
25561
|
}
|
|
25643
25562
|
const backend = await backendClient();
|
|
25644
|
-
const
|
|
25645
|
-
|
|
25646
|
-
id: invoice._id,
|
|
25563
|
+
const { number: number4 } = await backend.mutation(api2.invoices.update, {
|
|
25564
|
+
id: readInvoiceRef(id),
|
|
25647
25565
|
lineItems: options.line?.map(parseLineItem),
|
|
25648
25566
|
notes: options.notes === null ? "" : options.notes,
|
|
25649
25567
|
dueDate: options.due === null ? "" : options.due
|
|
25650
25568
|
});
|
|
25651
|
-
console.log(`Updated ${
|
|
25569
|
+
console.log(`Updated ${number4 ?? "draft"}.`);
|
|
25652
25570
|
}
|
|
25653
25571
|
});
|
|
25654
25572
|
|
|
@@ -25661,9 +25579,8 @@ var voidInvoice = command({
|
|
|
25661
25579
|
},
|
|
25662
25580
|
run: async ({ positionals: { id } }) => {
|
|
25663
25581
|
const backend = await backendClient();
|
|
25664
|
-
const
|
|
25665
|
-
|
|
25666
|
-
console.log(`Voided ${invoice.number}.`);
|
|
25582
|
+
const { number: number4 } = await backend.mutation(api2.invoices.voidInvoice, { id: readInvoiceRef(id) });
|
|
25583
|
+
console.log(`Voided ${number4}.`);
|
|
25667
25584
|
}
|
|
25668
25585
|
});
|
|
25669
25586
|
|
|
@@ -25674,7 +25591,7 @@ var invoices = group({
|
|
|
25674
25591
|
longDescription: `An invoice is a draft until issued: issuing assigns its gapless number, freezes the
|
|
25675
25592
|
document, and snapshots the client's billing details. Sent invoices are immutable \u2014
|
|
25676
25593
|
the fix path is void + duplicate into a fresh draft. Drafts delete outright.`,
|
|
25677
|
-
commands: [create3, list4, get3, update4, issue2, voidInvoice, duplicate, link, rotateLink, payments]
|
|
25594
|
+
commands: [create3, list4, get3, update4, issue2, email3, voidInvoice, duplicate, link, rotateLink, payments]
|
|
25678
25595
|
});
|
|
25679
25596
|
|
|
25680
25597
|
// src/lib/attachments.ts
|
|
@@ -25743,10 +25660,15 @@ var comment = command({
|
|
|
25743
25660
|
},
|
|
25744
25661
|
run: async ({ positionals: { id, body }, options: { attach } }) => {
|
|
25745
25662
|
const client3 = await backendClient();
|
|
25746
|
-
|
|
25747
|
-
|
|
25748
|
-
|
|
25749
|
-
|
|
25663
|
+
if (attach !== undefined && attach.length > 0) {
|
|
25664
|
+
const issue3 = await resolveIssue(client3, id);
|
|
25665
|
+
printAttachments(await attachFiles(client3, issue3._id, attach));
|
|
25666
|
+
}
|
|
25667
|
+
const { identifier } = await client3.mutation(api2.issueComments.create, {
|
|
25668
|
+
issueId: readIssueRef(id),
|
|
25669
|
+
bodyMarkdown: await readTextOption(body)
|
|
25670
|
+
});
|
|
25671
|
+
console.log(`Commented on ${identifier}.`);
|
|
25750
25672
|
}
|
|
25751
25673
|
});
|
|
25752
25674
|
|
|
@@ -25766,7 +25688,6 @@ var agentConfigurationLine = (id) => {
|
|
|
25766
25688
|
var create4 = command({
|
|
25767
25689
|
name: "create",
|
|
25768
25690
|
description: "Create an issue and print its identifier",
|
|
25769
|
-
longDescription: "--disposition ready_for_agent requires --agent-config; the agent queue refuses an issue without one.",
|
|
25770
25691
|
positionals: {
|
|
25771
25692
|
title: exports_external.string().describe("Issue title")
|
|
25772
25693
|
},
|
|
@@ -25781,17 +25702,22 @@ var create4 = command({
|
|
|
25781
25702
|
milestone: exports_external.string().optional().describe("Milestone name (needs --project or --here)"),
|
|
25782
25703
|
parent: exports_external.string().optional().describe("Create as a sub-issue of this issue (identifier, number, or URL)"),
|
|
25783
25704
|
disposition: exports_external.enum(ISSUE_CREATE_DISPOSITIONS).optional().describe("Route immediately (default needs_triage)"),
|
|
25705
|
+
label: exports_external.array(exports_external.string()).optional().describe("Add a label by name (repeatable)"),
|
|
25784
25706
|
"agent-config": exports_external.enum(AGENT_CONFIGURATION_IDS).optional().describe(`Agent configuration${agentConfigurationLegacyNote()}`)
|
|
25785
25707
|
},
|
|
25786
25708
|
run: async ({ positionals: { title }, options }) => {
|
|
25787
25709
|
if (options.project && options.here)
|
|
25788
25710
|
throw new Error("Pass --project or --here, not both.");
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25711
|
+
let projectRef = options.project === undefined ? undefined : readProjectRef(options.project);
|
|
25712
|
+
if (options.here) {
|
|
25713
|
+
const repo = await currentRepoKey();
|
|
25714
|
+
if (!repo)
|
|
25715
|
+
throw new Error("No repository here: not a git checkout with an origin remote.");
|
|
25716
|
+
projectRef = repo;
|
|
25717
|
+
}
|
|
25718
|
+
if (options.milestone && projectRef === undefined)
|
|
25792
25719
|
throw new Error("A milestone needs --project or --here.");
|
|
25793
|
-
const
|
|
25794
|
-
const parent = options.parent ? await resolveIssue(client3, options.parent) : undefined;
|
|
25720
|
+
const client3 = await backendClient();
|
|
25795
25721
|
const { identifier } = await client3.mutation(api2.issues.create, {
|
|
25796
25722
|
title,
|
|
25797
25723
|
descriptionMarkdown: options.description === undefined ? undefined : await readTextOption(options.description),
|
|
@@ -25799,11 +25725,12 @@ var create4 = command({
|
|
|
25799
25725
|
priority: options.priority,
|
|
25800
25726
|
estimate: options.estimate,
|
|
25801
25727
|
dueDate: options.due,
|
|
25802
|
-
projectId:
|
|
25803
|
-
milestoneId: milestone
|
|
25804
|
-
parentId: parent
|
|
25728
|
+
projectId: projectRef,
|
|
25729
|
+
milestoneId: options.milestone,
|
|
25730
|
+
parentId: options.parent === undefined ? undefined : readIssueRef(options.parent),
|
|
25805
25731
|
disposition: options.disposition,
|
|
25806
|
-
agentConfigurationId: options["agent-config"]
|
|
25732
|
+
agentConfigurationId: options["agent-config"],
|
|
25733
|
+
labels: options.label
|
|
25807
25734
|
});
|
|
25808
25735
|
console.log(identifier);
|
|
25809
25736
|
}
|
|
@@ -25821,86 +25748,107 @@ var printTasks = (tasks) => {
|
|
|
25821
25748
|
};
|
|
25822
25749
|
|
|
25823
25750
|
// src/commands/issues/get.ts
|
|
25751
|
+
var printIssue = ({ relations, attachments, pages, tasks, feed, milestone, ...issue3 }) => {
|
|
25752
|
+
const project = issue3.project;
|
|
25753
|
+
console.log(`${issue3.identifier} ${issue3.title}`);
|
|
25754
|
+
console.log(`Status: ${issue3.status} \xB7 Priority: ${issue3.priority} \xB7 Disposition: ${issue3.disposition}`);
|
|
25755
|
+
if (issue3.estimate !== undefined)
|
|
25756
|
+
console.log(`Estimate: ${issue3.estimate}`);
|
|
25757
|
+
if (issue3.dueDate)
|
|
25758
|
+
console.log(`Due: ${issue3.dueDate}`);
|
|
25759
|
+
if (issue3.agentConfigurationId)
|
|
25760
|
+
console.log(`Agent: ${agentConfigurationLine(issue3.agentConfigurationId)}`);
|
|
25761
|
+
if (issue3.branch)
|
|
25762
|
+
console.log(`Branch: ${issue3.branch}`);
|
|
25763
|
+
if (issue3.prUrl)
|
|
25764
|
+
console.log(`PR: ${issue3.prUrl}`);
|
|
25765
|
+
if (project)
|
|
25766
|
+
console.log(`Project: ${project.name}${milestone ? ` \xB7 Milestone: ${milestone.name}` : ""}`);
|
|
25767
|
+
if (issue3.parent)
|
|
25768
|
+
console.log(`Parent: ${issue3.parent.identifier} ${issue3.parent.title}`);
|
|
25769
|
+
if (issue3.labels.length > 0)
|
|
25770
|
+
console.log(`Labels: ${issue3.labels.map((label) => label.name).join(", ")}`);
|
|
25771
|
+
if (issue3.children.total > 0)
|
|
25772
|
+
console.log(`Sub-issues: ${issue3.children.done}/${issue3.children.total} done`);
|
|
25773
|
+
const related = (entries) => entries.map((entry) => `${entry.issue.identifier} (${entry.issue.status})`).join(", ");
|
|
25774
|
+
if (relations.blockedBy.length > 0)
|
|
25775
|
+
console.log(`Blocked by: ${related(relations.blockedBy)}`);
|
|
25776
|
+
if (relations.blocks.length > 0)
|
|
25777
|
+
console.log(`Blocks: ${related(relations.blocks)}`);
|
|
25778
|
+
if (relations.duplicateOf)
|
|
25779
|
+
console.log(`Duplicate of: ${related([relations.duplicateOf])}`);
|
|
25780
|
+
if (relations.duplicates.length > 0)
|
|
25781
|
+
console.log(`Duplicated by: ${related(relations.duplicates)}`);
|
|
25782
|
+
if (relations.relatesTo.length > 0)
|
|
25783
|
+
console.log(`Related: ${related(relations.relatesTo)}`);
|
|
25784
|
+
if (attachments.length > 0) {
|
|
25785
|
+
console.log("Attachments:");
|
|
25786
|
+
for (const attachment of attachments) {
|
|
25787
|
+
console.log(` ${attachment.name} \u2014 ${attachment.url ?? "unavailable"}`);
|
|
25788
|
+
}
|
|
25789
|
+
}
|
|
25790
|
+
if (pages.length > 0) {
|
|
25791
|
+
console.log("Pages:");
|
|
25792
|
+
for (const page of pages) {
|
|
25793
|
+
console.log(` ${page.title} (${page.style}, ${page.visibility}) \u2014 ${page.url}`);
|
|
25794
|
+
}
|
|
25795
|
+
}
|
|
25796
|
+
printTasks(tasks);
|
|
25797
|
+
if (issue3.descriptionMarkdown)
|
|
25798
|
+
console.log(`
|
|
25799
|
+
${issue3.descriptionMarkdown}`);
|
|
25800
|
+
if (feed.events.length > 0)
|
|
25801
|
+
console.log(`
|
|
25802
|
+
Feed:`);
|
|
25803
|
+
if (feed.truncated)
|
|
25804
|
+
console.log("(older events truncated)");
|
|
25805
|
+
for (const event of feed.events) {
|
|
25806
|
+
const at = new Date(event.at).toISOString().slice(0, 16).replace("T", " ");
|
|
25807
|
+
if (event.type === "activity") {
|
|
25808
|
+
const via = event.via === undefined ? "" : ` \u2014 ${actorLabel(event.via, event.agent)}`;
|
|
25809
|
+
console.log(`${at} ${issueActivityLine(event.detail)}${via}`);
|
|
25810
|
+
} else {
|
|
25811
|
+
console.log(`${at} comment${event.editedAt ? " (edited)" : ""}:`);
|
|
25812
|
+
for (const line of event.bodyMarkdown.split(`
|
|
25813
|
+
`))
|
|
25814
|
+
console.log(` ${line}`);
|
|
25815
|
+
}
|
|
25816
|
+
}
|
|
25817
|
+
};
|
|
25824
25818
|
var get4 = command({
|
|
25825
25819
|
name: "get",
|
|
25826
|
-
description: "Show
|
|
25820
|
+
description: "Show one or more issues: properties, description, and feed",
|
|
25827
25821
|
positionals: {
|
|
25828
|
-
|
|
25822
|
+
ids: exports_external.array(exports_external.string()).min(1).describe(`Issue identifiers, numbers, or URLs (up to ${MAX_ISSUES_PER_BATCH_READ} per call)`)
|
|
25829
25823
|
},
|
|
25830
25824
|
options: {
|
|
25831
|
-
json: exports_external.boolean().default(false).describe("Print as JSON")
|
|
25825
|
+
json: exports_external.boolean().default(false).describe("Print as JSON (an array when given several issues)")
|
|
25832
25826
|
},
|
|
25833
|
-
run: async ({ positionals: {
|
|
25827
|
+
run: async ({ positionals: { ids }, options: { json: json2 } }) => {
|
|
25834
25828
|
const client3 = await backendClient();
|
|
25835
|
-
const
|
|
25836
|
-
const
|
|
25837
|
-
|
|
25838
|
-
|
|
25839
|
-
|
|
25840
|
-
|
|
25841
|
-
]);
|
|
25842
|
-
if (json2)
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
console.log(
|
|
25852
|
-
if (issue3.agentConfigurationId)
|
|
25853
|
-
console.log(`Agent: ${agentConfigurationLine(issue3.agentConfigurationId)}`);
|
|
25854
|
-
if (issue3.branch)
|
|
25855
|
-
console.log(`Branch: ${issue3.branch}`);
|
|
25856
|
-
if (issue3.prUrl)
|
|
25857
|
-
console.log(`PR: ${issue3.prUrl}`);
|
|
25858
|
-
if (project)
|
|
25859
|
-
console.log(`Project: ${project.name}${milestone ? ` \xB7 Milestone: ${milestone.name}` : ""}`);
|
|
25860
|
-
if (issue3.parent)
|
|
25861
|
-
console.log(`Parent: ${issue3.parent.identifier} ${issue3.parent.title}`);
|
|
25862
|
-
if (issue3.labels.length > 0)
|
|
25863
|
-
console.log(`Labels: ${issue3.labels.map((label) => label.name).join(", ")}`);
|
|
25864
|
-
if (issue3.children.total > 0)
|
|
25865
|
-
console.log(`Sub-issues: ${issue3.children.done}/${issue3.children.total} done`);
|
|
25866
|
-
const related = (entries) => entries.map((entry) => `${entry.issue.identifier} (${entry.issue.status})`).join(", ");
|
|
25867
|
-
if (relations.blockedBy.length > 0)
|
|
25868
|
-
console.log(`Blocked by: ${related(relations.blockedBy)}`);
|
|
25869
|
-
if (relations.blocks.length > 0)
|
|
25870
|
-
console.log(`Blocks: ${related(relations.blocks)}`);
|
|
25871
|
-
if (relations.duplicateOf)
|
|
25872
|
-
console.log(`Duplicate of: ${related([relations.duplicateOf])}`);
|
|
25873
|
-
if (relations.duplicates.length > 0)
|
|
25874
|
-
console.log(`Duplicated by: ${related(relations.duplicates)}`);
|
|
25875
|
-
if (relations.relatesTo.length > 0)
|
|
25876
|
-
console.log(`Related: ${related(relations.relatesTo)}`);
|
|
25877
|
-
if (attachments.length > 0) {
|
|
25878
|
-
console.log("Attachments:");
|
|
25879
|
-
for (const attachment of attachments) {
|
|
25880
|
-
console.log(` ${attachment.name} \u2014 ${attachment.url ?? "unavailable"}`);
|
|
25881
|
-
}
|
|
25882
|
-
}
|
|
25883
|
-
printTasks(tasks);
|
|
25884
|
-
if (issue3.descriptionMarkdown)
|
|
25885
|
-
console.log(`
|
|
25886
|
-
${issue3.descriptionMarkdown}`);
|
|
25887
|
-
if (feed.events.length > 0)
|
|
25888
|
-
console.log(`
|
|
25889
|
-
Feed:`);
|
|
25890
|
-
if (feed.truncated)
|
|
25891
|
-
console.log("(older events truncated)");
|
|
25892
|
-
for (const event of feed.events) {
|
|
25893
|
-
const at = new Date(event.at).toISOString().slice(0, 16).replace("T", " ");
|
|
25894
|
-
if (event.type === "activity") {
|
|
25895
|
-
const via = event.via === undefined ? "" : ` \u2014 ${actorLabel(event.via, event.agent)}`;
|
|
25896
|
-
console.log(`${at} ${issueActivityLine(event.detail)}${via}`);
|
|
25897
|
-
} else {
|
|
25898
|
-
console.log(`${at} comment${event.editedAt ? " (edited)" : ""}:`);
|
|
25899
|
-
for (const line of event.bodyMarkdown.split(`
|
|
25900
|
-
`))
|
|
25901
|
-
console.log(` ${line}`);
|
|
25902
|
-
}
|
|
25829
|
+
const found = await client3.query(api2.issues.getMany, { identifiers: ids.map(readIssueRef) });
|
|
25830
|
+
const missing = ids.filter((_, index) => !found[index]);
|
|
25831
|
+
if (missing.length === 1)
|
|
25832
|
+
throw new Error(`No issue matches ${missing[0]}.`);
|
|
25833
|
+
if (missing.length > 1)
|
|
25834
|
+
throw new Error(`No issues match ${missing.join(", ")}.`);
|
|
25835
|
+
const details = found.flatMap((detail) => detail ? [detail] : []);
|
|
25836
|
+
if (json2) {
|
|
25837
|
+
const objects = details.map(({ relations, attachments, pages, tasks, feed, milestone: _, ...issue3 }) => ({
|
|
25838
|
+
...issue3,
|
|
25839
|
+
relations,
|
|
25840
|
+
attachments,
|
|
25841
|
+
pages,
|
|
25842
|
+
tasks,
|
|
25843
|
+
feed
|
|
25844
|
+
}));
|
|
25845
|
+
return console.log(JSON.stringify(details.length === 1 ? objects[0] : objects, null, 2));
|
|
25903
25846
|
}
|
|
25847
|
+
details.forEach((detail, index) => {
|
|
25848
|
+
if (index > 0)
|
|
25849
|
+
console.log("---");
|
|
25850
|
+
printIssue(detail);
|
|
25851
|
+
});
|
|
25904
25852
|
}
|
|
25905
25853
|
});
|
|
25906
25854
|
|
|
@@ -25947,9 +25895,8 @@ var remove4 = command({
|
|
|
25947
25895
|
},
|
|
25948
25896
|
run: async ({ positionals: { label: ref } }) => {
|
|
25949
25897
|
const client3 = await backendClient();
|
|
25950
|
-
const
|
|
25951
|
-
|
|
25952
|
-
console.log(`Deleted ${label.name}.`);
|
|
25898
|
+
const removed = await client3.mutation(api2.issueLabels.remove, { id: ref });
|
|
25899
|
+
console.log(`Deleted ${removed.name}.`);
|
|
25953
25900
|
}
|
|
25954
25901
|
});
|
|
25955
25902
|
|
|
@@ -25968,9 +25915,8 @@ var update5 = command({
|
|
|
25968
25915
|
if (name === undefined && color === undefined)
|
|
25969
25916
|
throw new Error("Nothing to update. Pass --name or --color.");
|
|
25970
25917
|
const client3 = await backendClient();
|
|
25971
|
-
const
|
|
25972
|
-
|
|
25973
|
-
console.log(`Updated ${name ?? label.name}.`);
|
|
25918
|
+
const updated = await client3.mutation(api2.issueLabels.update, { id: ref, name, color });
|
|
25919
|
+
console.log(`Updated ${updated.name}.`);
|
|
25974
25920
|
}
|
|
25975
25921
|
});
|
|
25976
25922
|
|
|
@@ -25981,7 +25927,50 @@ var labels = group({
|
|
|
25981
25927
|
commands: [create5, list6, update5, remove4]
|
|
25982
25928
|
});
|
|
25983
25929
|
|
|
25930
|
+
// src/commands/issues/link-pr.ts
|
|
25931
|
+
var linkPr = command({
|
|
25932
|
+
name: "link-pr",
|
|
25933
|
+
description: "Link a pull request to an issue by hand",
|
|
25934
|
+
longDescription: `For a pull request the webhook cannot match on its own: work that started before
|
|
25935
|
+
its issue existed, so the branch never carried the identifier, or a second issue the
|
|
25936
|
+
same pull request lands. From here reviews and the merge move the issue as usual.
|
|
25937
|
+
--claim applies what opening the pull request would have: unstarted work moves to
|
|
25938
|
+
in_progress; any other status is kept.`,
|
|
25939
|
+
positionals: {
|
|
25940
|
+
id: exports_external.string().describe("Issue identifier, number, or URL"),
|
|
25941
|
+
url: exports_external.string().describe("Pull request URL")
|
|
25942
|
+
},
|
|
25943
|
+
options: {
|
|
25944
|
+
claim: exports_external.boolean().default(false).describe("Move backlog or todo work to in_progress along with the link")
|
|
25945
|
+
},
|
|
25946
|
+
run: async ({ positionals: { id, url: url2 }, options: { claim: claim2 } }) => {
|
|
25947
|
+
const client3 = await backendClient();
|
|
25948
|
+
const { identifier, linked, status: status3 } = await client3.mutation(api2.issues.linkPr, {
|
|
25949
|
+
id: readIssueRef(id),
|
|
25950
|
+
prUrl: url2,
|
|
25951
|
+
...claim2 ? { claim: claim2 } : {}
|
|
25952
|
+
});
|
|
25953
|
+
console.log(`${linked ? "Linked" : "Already linked"} ${url2} to ${identifier}: ${status3}`);
|
|
25954
|
+
}
|
|
25955
|
+
});
|
|
25956
|
+
|
|
25984
25957
|
// src/commands/issues/list.ts
|
|
25958
|
+
var resolveScope = async (client3, { project, here, milestone }) => {
|
|
25959
|
+
if (milestone) {
|
|
25960
|
+
if (!project && !here)
|
|
25961
|
+
throw new Error("A milestone filter needs --project or --here.");
|
|
25962
|
+
const ref = project ? readProjectRef(project) : await currentRepoKey();
|
|
25963
|
+
if (!ref)
|
|
25964
|
+
throw new Error("No repository here: not a git checkout with an origin remote.");
|
|
25965
|
+
const resolved = await client3.query(api2.milestones.resolve, { project: ref, name: milestone });
|
|
25966
|
+
return { projectId: resolved.projectId, milestoneId: resolved._id };
|
|
25967
|
+
}
|
|
25968
|
+
if (project)
|
|
25969
|
+
return { projectId: (await resolveProject(client3, project))._id };
|
|
25970
|
+
if (here)
|
|
25971
|
+
return { projectId: (await repoProject(client3))._id };
|
|
25972
|
+
return;
|
|
25973
|
+
};
|
|
25985
25974
|
var list7 = command({
|
|
25986
25975
|
name: "list",
|
|
25987
25976
|
description: "List open issues, most recently updated first",
|
|
@@ -26002,10 +25991,7 @@ var list7 = command({
|
|
|
26002
25991
|
if (options.project && options.here)
|
|
26003
25992
|
throw new Error("Pass --project or --here, not both.");
|
|
26004
25993
|
const client3 = await backendClient();
|
|
26005
|
-
const
|
|
26006
|
-
if (options.milestone && !project)
|
|
26007
|
-
throw new Error("A milestone filter needs --project or --here.");
|
|
26008
|
-
const milestone = project && options.milestone ? await resolveMilestone(client3, project._id, options.milestone) : undefined;
|
|
25994
|
+
const scope = await resolveScope(client3, options);
|
|
26009
25995
|
const result = await client3.query(api2.issueViews.query, {
|
|
26010
25996
|
source: {
|
|
26011
25997
|
type: "custom",
|
|
@@ -26019,8 +26005,8 @@ var list7 = command({
|
|
|
26019
26005
|
statuses: options.status ? [options.status] : options.all ? undefined : ISSUE_STATUSES.filter(issueIsOpen),
|
|
26020
26006
|
priorities: options.priority ? [options.priority] : undefined,
|
|
26021
26007
|
dispositions: options.disposition ? [options.disposition] : undefined,
|
|
26022
|
-
projectIds:
|
|
26023
|
-
milestoneIds:
|
|
26008
|
+
projectIds: scope ? [scope.projectId] : undefined,
|
|
26009
|
+
milestoneIds: scope?.milestoneId ? [scope.milestoneId] : undefined,
|
|
26024
26010
|
dueDate: options.due
|
|
26025
26011
|
}
|
|
26026
26012
|
}
|
|
@@ -26069,9 +26055,8 @@ var markAddressed = command({
|
|
|
26069
26055
|
},
|
|
26070
26056
|
run: async ({ positionals: { id }, options: { commit, finding } }) => {
|
|
26071
26057
|
const client3 = await backendClient();
|
|
26072
|
-
const issue3 = await resolveIssue(client3, id);
|
|
26073
26058
|
const result = await client3.action(api2.githubReviewer.markAddressed, {
|
|
26074
|
-
id:
|
|
26059
|
+
id: readIssueRef(id),
|
|
26075
26060
|
commitSha: commit,
|
|
26076
26061
|
findings: finding?.map(parseFindingRef)
|
|
26077
26062
|
});
|
|
@@ -26092,10 +26077,12 @@ var relate = command({
|
|
|
26092
26077
|
},
|
|
26093
26078
|
run: async ({ positionals: { id, kind, other } }) => {
|
|
26094
26079
|
const client3 = await backendClient();
|
|
26095
|
-
const
|
|
26096
|
-
|
|
26097
|
-
|
|
26098
|
-
|
|
26080
|
+
const { identifier, otherIdentifier } = await client3.mutation(api2.issues.addRelation, {
|
|
26081
|
+
id: readIssueRef(id),
|
|
26082
|
+
kind,
|
|
26083
|
+
otherIssueId: readIssueRef(other)
|
|
26084
|
+
});
|
|
26085
|
+
console.log(`${identifier} ${kind.replaceAll("_", " ")} ${otherIdentifier}.`);
|
|
26099
26086
|
}
|
|
26100
26087
|
});
|
|
26101
26088
|
|
|
@@ -26108,9 +26095,8 @@ var remove5 = command({
|
|
|
26108
26095
|
},
|
|
26109
26096
|
run: async ({ positionals: { id } }) => {
|
|
26110
26097
|
const client3 = await backendClient();
|
|
26111
|
-
const
|
|
26112
|
-
|
|
26113
|
-
console.log(`Deleted ${issue3.identifier}.`);
|
|
26098
|
+
const { identifier } = await client3.mutation(api2.issues.remove, { id: readIssueRef(id) });
|
|
26099
|
+
console.log(`Deleted ${identifier}.`);
|
|
26114
26100
|
}
|
|
26115
26101
|
});
|
|
26116
26102
|
|
|
@@ -26118,8 +26104,7 @@ var remove5 = command({
|
|
|
26118
26104
|
var route = command({
|
|
26119
26105
|
name: "route",
|
|
26120
26106
|
description: "Route an issue to a disposition (wontfix also cancels it)",
|
|
26121
|
-
longDescription:
|
|
26122
|
-
ready_for_agent requires the issue to carry an agent configuration (set --agent-config).`,
|
|
26107
|
+
longDescription: "The disposition says who acts next; needs_triage is the default.",
|
|
26123
26108
|
positionals: {
|
|
26124
26109
|
id: exports_external.string().describe("Issue identifier, number, or URL"),
|
|
26125
26110
|
disposition: exports_external.enum(ISSUE_DISPOSITIONS).describe("Where the issue goes next")
|
|
@@ -26129,13 +26114,12 @@ ready_for_agent requires the issue to carry an agent configuration (set --agent-
|
|
|
26129
26114
|
},
|
|
26130
26115
|
run: async ({ positionals: { id, disposition }, options: { comment: comment2 } }) => {
|
|
26131
26116
|
const client3 = await backendClient();
|
|
26132
|
-
const
|
|
26133
|
-
|
|
26134
|
-
id: issue3._id,
|
|
26117
|
+
const { identifier } = await client3.mutation(api2.issues.route, {
|
|
26118
|
+
id: readIssueRef(id),
|
|
26135
26119
|
disposition,
|
|
26136
26120
|
comment: comment2 === undefined ? undefined : await readTextOption(comment2)
|
|
26137
26121
|
});
|
|
26138
|
-
console.log(`Routed ${
|
|
26122
|
+
console.log(`Routed ${identifier} to ${disposition}.`);
|
|
26139
26123
|
}
|
|
26140
26124
|
});
|
|
26141
26125
|
|
|
@@ -26144,8 +26128,7 @@ var set2 = command({
|
|
|
26144
26128
|
name: "set",
|
|
26145
26129
|
description: "Set an issue's status, priority, estimate, due date, project, milestone, parent, agent configuration, or labels",
|
|
26146
26130
|
longDescription: `Status normally moves through the workflow commands and the linked pull request;
|
|
26147
|
-
--status is the correction path
|
|
26148
|
-
the issue sits in ready_for_agent.`,
|
|
26131
|
+
--status is the correction path.`,
|
|
26149
26132
|
positionals: {
|
|
26150
26133
|
id: exports_external.string().describe("Issue identifier, number, or URL")
|
|
26151
26134
|
},
|
|
@@ -26177,41 +26160,20 @@ the issue sits in ready_for_agent.`,
|
|
|
26177
26160
|
if (Object.values(options).every((value) => value === undefined))
|
|
26178
26161
|
throw new Error("Nothing to set. Pass --status, --priority, --estimate, --due, --project, --milestone, --parent, --agent-config, or --label.");
|
|
26179
26162
|
const client3 = await backendClient();
|
|
26180
|
-
const
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
26187
|
-
|
|
26188
|
-
|
|
26189
|
-
|
|
26190
|
-
|
|
26191
|
-
|
|
26192
|
-
|
|
26193
|
-
|
|
26194
|
-
await client3.mutation(api2.issues.setParent, { id: issue3._id, parentId });
|
|
26195
|
-
for (const { _id } of addLabels)
|
|
26196
|
-
await client3.mutation(api2.issues.addLabel, { issueId: issue3._id, labelId: _id });
|
|
26197
|
-
for (const { _id } of removeLabels)
|
|
26198
|
-
await client3.mutation(api2.issues.removeLabel, { issueId: issue3._id, labelId: _id });
|
|
26199
|
-
if (agentConfig !== undefined)
|
|
26200
|
-
await client3.mutation(api2.issues.setAgentConfiguration, {
|
|
26201
|
-
id: issue3._id,
|
|
26202
|
-
agentConfigurationId: agentConfig ?? undefined
|
|
26203
|
-
});
|
|
26204
|
-
if (status3 !== undefined)
|
|
26205
|
-
await client3.mutation(api2.issues.setStatus, { id: issue3._id, status: status3 });
|
|
26206
|
-
if (priority !== undefined)
|
|
26207
|
-
await client3.mutation(api2.issues.setPriority, { id: issue3._id, priority });
|
|
26208
|
-
if (estimate !== undefined)
|
|
26209
|
-
await client3.mutation(api2.issues.setEstimate, { id: issue3._id, estimate: estimate ?? undefined });
|
|
26210
|
-
if (due !== undefined)
|
|
26211
|
-
await client3.mutation(api2.issues.setDueDate, { id: issue3._id, dueDate: due ?? undefined });
|
|
26212
|
-
if (project !== undefined || milestone !== undefined)
|
|
26213
|
-
await client3.mutation(api2.issues.setMembership, { id: issue3._id, projectId, milestoneId });
|
|
26214
|
-
console.log(`Updated ${issue3.identifier}.`);
|
|
26163
|
+
const { identifier } = await client3.mutation(api2.issues.edit, {
|
|
26164
|
+
issue: readIssueRef(id),
|
|
26165
|
+
...parent !== undefined ? { parent: parent === null ? null : readIssueRef(parent) } : {},
|
|
26166
|
+
...label !== undefined ? { addLabels: label } : {},
|
|
26167
|
+
...noLabel !== undefined ? { removeLabels: noLabel } : {},
|
|
26168
|
+
...agentConfig !== undefined ? { agentConfigurationId: agentConfig } : {},
|
|
26169
|
+
...status3 !== undefined ? { status: status3 } : {},
|
|
26170
|
+
...priority !== undefined ? { priority } : {},
|
|
26171
|
+
...estimate !== undefined ? { estimate } : {},
|
|
26172
|
+
...due !== undefined ? { dueDate: due } : {},
|
|
26173
|
+
...project !== undefined ? { project: project === null ? null : readProjectRef(project) } : {},
|
|
26174
|
+
...milestone !== undefined ? { milestone } : {}
|
|
26175
|
+
});
|
|
26176
|
+
console.log(`Updated ${identifier}.`);
|
|
26215
26177
|
}
|
|
26216
26178
|
});
|
|
26217
26179
|
|
|
@@ -26229,29 +26191,18 @@ ready_for_agent is informational, and --ready belongs only to an explicit hand-o
|
|
|
26229
26191
|
ready: exports_external.boolean().default(false).describe("Also route the issue to ready_for_agent, assigning the running agent when it is identifiable")
|
|
26230
26192
|
},
|
|
26231
26193
|
run: async ({ positionals: { id }, options: { ready } }) => {
|
|
26194
|
+
const actor = await agentClaim();
|
|
26195
|
+
const configurationId = actor && agentConfigurationForActor(actor);
|
|
26232
26196
|
const client3 = await backendClient();
|
|
26233
|
-
const
|
|
26234
|
-
|
|
26235
|
-
|
|
26236
|
-
|
|
26237
|
-
|
|
26238
|
-
const configurationId = actor && agentConfigurationForActor(actor);
|
|
26239
|
-
if (configurationId && configurationId !== issue3.agentConfigurationId) {
|
|
26240
|
-
await client3.mutation(api2.issues.setAgentConfiguration, {
|
|
26241
|
-
id: issue3._id,
|
|
26242
|
-
agentConfigurationId: configurationId
|
|
26243
|
-
});
|
|
26244
|
-
assigned = configurationId;
|
|
26245
|
-
}
|
|
26246
|
-
}
|
|
26247
|
-
if (ready && issue3.disposition !== "ready_for_agent") {
|
|
26248
|
-
await client3.mutation(api2.issues.route, { id: issue3._id, disposition: "ready_for_agent" });
|
|
26249
|
-
}
|
|
26250
|
-
console.log(`Started ${issue3.identifier}: in_progress, branch ${branch}`);
|
|
26197
|
+
const { identifier, branch, disposition, assigned } = await client3.mutation(api2.issues.start, {
|
|
26198
|
+
issue: readIssueRef(id),
|
|
26199
|
+
...ready ? { ready: { agentConfigurationId: configurationId || undefined } } : {}
|
|
26200
|
+
});
|
|
26201
|
+
console.log(`Started ${identifier}: in_progress, branch ${branch}`);
|
|
26251
26202
|
if (ready) {
|
|
26252
26203
|
console.log(`Routed to ready_for_agent${assigned ? `, assigned ${agentConfiguration(assigned).label}` : ""}.`);
|
|
26253
|
-
} else if (
|
|
26254
|
-
console.log(`Note: disposition is ${
|
|
26204
|
+
} else if (disposition !== "ready_for_agent" && actor) {
|
|
26205
|
+
console.log(`Note: disposition is ${disposition}. Pass --ready if this hand-off was intended.`);
|
|
26255
26206
|
}
|
|
26256
26207
|
console.log(`
|
|
26257
26208
|
git switch -c ${shellArg(branch)}`);
|
|
@@ -26267,8 +26218,7 @@ var startReview = command({
|
|
|
26267
26218
|
},
|
|
26268
26219
|
run: async ({ positionals: { id } }) => {
|
|
26269
26220
|
const client3 = await backendClient();
|
|
26270
|
-
const
|
|
26271
|
-
const started = await client3.action(api2.githubReviewer.startReview, { id: issue3._id });
|
|
26221
|
+
const started = await client3.action(api2.githubReviewer.startReview, { id: readIssueRef(id) });
|
|
26272
26222
|
console.log(`Reviewing ${started.identifier}: in_review`);
|
|
26273
26223
|
console.log(`Announced on ${started.prUrl}; check run open.`);
|
|
26274
26224
|
}
|
|
@@ -26283,9 +26233,8 @@ var submit = command({
|
|
|
26283
26233
|
},
|
|
26284
26234
|
run: async ({ positionals: { id } }) => {
|
|
26285
26235
|
const client3 = await backendClient();
|
|
26286
|
-
const
|
|
26287
|
-
|
|
26288
|
-
console.log(`Submitted ${issue3.identifier}: ready_for_review`);
|
|
26236
|
+
const { identifier } = await client3.mutation(api2.issues.submit, { id: readIssueRef(id) });
|
|
26237
|
+
console.log(`Submitted ${identifier}: ready_for_review`);
|
|
26289
26238
|
}
|
|
26290
26239
|
});
|
|
26291
26240
|
|
|
@@ -26349,8 +26298,7 @@ Categories: ${REVIEW_FINDING_CATEGORIES.join(", ")}; severities: ${REVIEW_FINDIN
|
|
|
26349
26298
|
verdict = { kind: "request_changes", findings: await readFindings(options.findings) };
|
|
26350
26299
|
}
|
|
26351
26300
|
const client3 = await backendClient();
|
|
26352
|
-
const
|
|
26353
|
-
const submitted = await client3.action(api2.githubReviewer.submitReview, { id: issue3._id, verdict });
|
|
26301
|
+
const submitted = await client3.action(api2.githubReviewer.submitReview, { id: readIssueRef(id), verdict });
|
|
26354
26302
|
console.log(`Submitted review on ${submitted.identifier}: ${submitted.prUrl}`);
|
|
26355
26303
|
console.log(submitted.summary);
|
|
26356
26304
|
}
|
|
@@ -26377,57 +26325,38 @@ address several rows even as removals renumber it.`,
|
|
|
26377
26325
|
},
|
|
26378
26326
|
run: async ({ positionals: { id }, options: { add: add3, check: check2, uncheck, convert, remove: remove6, json: json2 } }) => {
|
|
26379
26327
|
const client3 = await backendClient();
|
|
26380
|
-
const
|
|
26381
|
-
|
|
26382
|
-
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
const promoted = at(convert);
|
|
26391
|
-
const dropped = at(remove6);
|
|
26392
|
-
for (const title of add3 ?? [])
|
|
26393
|
-
await client3.mutation(api2.issueTasks.create, { issueId: issue3._id, title });
|
|
26394
|
-
for (const task2 of ticked)
|
|
26395
|
-
await client3.mutation(api2.issueTasks.update, { id: task2._id, isDone: true });
|
|
26396
|
-
for (const task2 of unticked)
|
|
26397
|
-
await client3.mutation(api2.issueTasks.update, { id: task2._id, isDone: false });
|
|
26398
|
-
for (const task2 of promoted) {
|
|
26399
|
-
const created = await client3.mutation(api2.issueTasks.convert, { id: task2._id });
|
|
26328
|
+
const wrote = [add3, check2, uncheck, convert, remove6].some((values) => values !== undefined);
|
|
26329
|
+
if (wrote) {
|
|
26330
|
+
const { identifier, converted, tasks: tasks3 } = await client3.mutation(api2.issueTasks.edit, {
|
|
26331
|
+
issue: readIssueRef(id),
|
|
26332
|
+
...add3 !== undefined ? { add: add3 } : {},
|
|
26333
|
+
...check2 !== undefined ? { check: check2 } : {},
|
|
26334
|
+
...uncheck !== undefined ? { uncheck } : {},
|
|
26335
|
+
...convert !== undefined ? { convert } : {},
|
|
26336
|
+
...remove6 !== undefined ? { remove: remove6 } : {}
|
|
26337
|
+
});
|
|
26400
26338
|
if (!json2) {
|
|
26401
|
-
|
|
26339
|
+
for (const created of converted) {
|
|
26340
|
+
console.log(`${created.identifier} ${created.nested ? "created as a sub-issue" : "created and related"}: ${created.title}`);
|
|
26341
|
+
}
|
|
26402
26342
|
}
|
|
26343
|
+
if (json2)
|
|
26344
|
+
return console.log(JSON.stringify(tasks3, null, 2));
|
|
26345
|
+
if (tasks3.length === 0)
|
|
26346
|
+
return console.log(`${identifier} has no tasks.`);
|
|
26347
|
+
return printTasks(tasks3);
|
|
26403
26348
|
}
|
|
26404
|
-
|
|
26405
|
-
|
|
26406
|
-
const wrote = [add3, check2, uncheck, convert, remove6].some((values) => values !== undefined);
|
|
26407
|
-
const after = wrote ? await client3.query(api2.issueTasks.list, { issueId: issue3._id }) : before;
|
|
26349
|
+
const issue3 = await resolveIssue(client3, id);
|
|
26350
|
+
const tasks2 = await client3.query(api2.issueTasks.list, { issueId: issue3._id });
|
|
26408
26351
|
if (json2)
|
|
26409
|
-
return console.log(JSON.stringify(
|
|
26410
|
-
if (
|
|
26352
|
+
return console.log(JSON.stringify(tasks2, null, 2));
|
|
26353
|
+
if (tasks2.length === 0)
|
|
26411
26354
|
return console.log(`${issue3.identifier} has no tasks.`);
|
|
26412
|
-
printTasks(
|
|
26355
|
+
printTasks(tasks2);
|
|
26413
26356
|
}
|
|
26414
26357
|
});
|
|
26415
26358
|
|
|
26416
26359
|
// src/commands/issues/unrelate.ts
|
|
26417
|
-
var relationEntries = (relations, kind) => {
|
|
26418
|
-
switch (kind) {
|
|
26419
|
-
case "blocks":
|
|
26420
|
-
return relations.blocks;
|
|
26421
|
-
case "blocked_by":
|
|
26422
|
-
return relations.blockedBy;
|
|
26423
|
-
case "duplicate_of":
|
|
26424
|
-
return relations.duplicateOf ? [relations.duplicateOf] : [];
|
|
26425
|
-
case "duplicated_by":
|
|
26426
|
-
return relations.duplicates;
|
|
26427
|
-
case "relates_to":
|
|
26428
|
-
return relations.relatesTo;
|
|
26429
|
-
}
|
|
26430
|
-
};
|
|
26431
26360
|
var unrelate = command({
|
|
26432
26361
|
name: "unrelate",
|
|
26433
26362
|
description: "Remove a relation between two issues (a canceled duplicate stays canceled)",
|
|
@@ -26438,15 +26367,12 @@ var unrelate = command({
|
|
|
26438
26367
|
},
|
|
26439
26368
|
run: async ({ positionals: { id, kind, other } }) => {
|
|
26440
26369
|
const client3 = await backendClient();
|
|
26441
|
-
const
|
|
26442
|
-
|
|
26443
|
-
|
|
26444
|
-
|
|
26445
|
-
|
|
26446
|
-
|
|
26447
|
-
throw new Error(`${issue3.identifier} has no "${label}" relation to ${otherIssue.identifier}.`);
|
|
26448
|
-
await client3.mutation(api2.issues.removeRelation, { id: relation._id });
|
|
26449
|
-
console.log(`Removed: ${issue3.identifier} ${label} ${otherIssue.identifier}.`);
|
|
26370
|
+
const { identifier, otherIdentifier } = await client3.mutation(api2.issues.removeRelation, {
|
|
26371
|
+
id: readIssueRef(id),
|
|
26372
|
+
kind,
|
|
26373
|
+
otherIssueId: readIssueRef(other)
|
|
26374
|
+
});
|
|
26375
|
+
console.log(`Removed: ${identifier} ${kind.replaceAll("_", " ")} ${otherIdentifier}.`);
|
|
26450
26376
|
}
|
|
26451
26377
|
});
|
|
26452
26378
|
|
|
@@ -26466,16 +26392,18 @@ var update6 = command({
|
|
|
26466
26392
|
if (title === undefined && description === undefined && attach === undefined)
|
|
26467
26393
|
throw new Error("Nothing to update. Pass --title, --description, or --attach.");
|
|
26468
26394
|
const client3 = await backendClient();
|
|
26469
|
-
|
|
26470
|
-
|
|
26471
|
-
|
|
26472
|
-
await client3.
|
|
26473
|
-
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26478
|
-
|
|
26395
|
+
let attached;
|
|
26396
|
+
if (attach !== undefined && attach.length > 0) {
|
|
26397
|
+
const issue3 = await resolveIssue(client3, id);
|
|
26398
|
+
printAttachments(await attachFiles(client3, issue3._id, attach));
|
|
26399
|
+
attached = issue3.identifier;
|
|
26400
|
+
}
|
|
26401
|
+
const identifier = title !== undefined || description !== undefined ? (await client3.mutation(api2.issues.update, {
|
|
26402
|
+
id: readIssueRef(id),
|
|
26403
|
+
title,
|
|
26404
|
+
descriptionMarkdown: description === undefined ? undefined : await readTextOption(description)
|
|
26405
|
+
})).identifier : attached;
|
|
26406
|
+
console.log(`Updated ${identifier}.`);
|
|
26479
26407
|
}
|
|
26480
26408
|
});
|
|
26481
26409
|
|
|
@@ -26488,11 +26416,11 @@ todo, in_progress, ready_for_review, in_review, changes_requested, approved, don
|
|
|
26488
26416
|
canceled), priority, and disposition (who acts next). The workflow commands \u2014 start,
|
|
26489
26417
|
submit, start-review, submit-review \u2014 and the linked pull request move the status;
|
|
26490
26418
|
each refuses an issue outside its own status, and \`set -s\` is for corrections.
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26419
|
+
Work on a branch whose name contains the issue identifier and the branch, pull
|
|
26420
|
+
request, and status track automatically \u2014 a change request moves the issue to
|
|
26421
|
+
changes_requested, an approval to approved (one outstanding change request outweighs
|
|
26422
|
+
any number of approvals), and a merge lands it as done. link-pr covers the pull
|
|
26423
|
+
request the branch name does not point at; from there the same tracking applies.`,
|
|
26496
26424
|
commands: [
|
|
26497
26425
|
create4,
|
|
26498
26426
|
list7,
|
|
@@ -26506,6 +26434,7 @@ it as done.`,
|
|
|
26506
26434
|
startReview,
|
|
26507
26435
|
submitReview,
|
|
26508
26436
|
markAddressed,
|
|
26437
|
+
linkPr,
|
|
26509
26438
|
tasks,
|
|
26510
26439
|
relate,
|
|
26511
26440
|
unrelate,
|
|
@@ -26514,6 +26443,9 @@ it as done.`,
|
|
|
26514
26443
|
]
|
|
26515
26444
|
});
|
|
26516
26445
|
|
|
26446
|
+
// ../../packages/backend/convex/lib/object.ts
|
|
26447
|
+
var keys = (object2) => Object.keys(object2);
|
|
26448
|
+
|
|
26517
26449
|
// src/lib/group.ts
|
|
26518
26450
|
import { basename as basename2 } from "path";
|
|
26519
26451
|
var git = async (...args) => {
|
|
@@ -26541,20 +26473,21 @@ var create6 = command({
|
|
|
26541
26473
|
options: {
|
|
26542
26474
|
title: exports_external.string().describe("Page title").meta({ short: "t" }),
|
|
26543
26475
|
group: exports_external.string().nullable().optional().describe("Group to list the page under (default: the current repo)").meta({ short: "g", negatable: true }),
|
|
26544
|
-
|
|
26545
|
-
private: exports_external.boolean().default(false).describe("Require the publishing account to view the page")
|
|
26476
|
+
style: exports_external.enum(keys(PAGE_STYLES)).default("basic").describe(`Style to render in \u2014 ${describeChoices(PAGE_STYLES)}`),
|
|
26477
|
+
private: exports_external.boolean().default(false).describe("Require the publishing account to view the page"),
|
|
26478
|
+
issue: exports_external.string().optional().describe("Link the page to an issue (KAI-N, number, or URL)")
|
|
26546
26479
|
},
|
|
26547
|
-
run: async ({ positionals: { file: file2 }, options: { title, group: group2,
|
|
26480
|
+
run: async ({ positionals: { file: file2 }, options: { title, group: group2, style, private: isPrivate, issue: issue3 } }) => {
|
|
26548
26481
|
const newGroup = await groupForCreate(group2);
|
|
26549
26482
|
const html = await readBody(file2);
|
|
26550
|
-
const mode = raw ? "raw" : "themed";
|
|
26551
26483
|
const visibility = isPrivate ? "private" : "public";
|
|
26552
26484
|
const { url: url2 } = await (await backendClient()).action(api2.pages.create, {
|
|
26553
26485
|
title,
|
|
26554
26486
|
group: newGroup,
|
|
26555
26487
|
html,
|
|
26556
|
-
|
|
26557
|
-
visibility
|
|
26488
|
+
style,
|
|
26489
|
+
visibility,
|
|
26490
|
+
issue: issue3 === undefined ? undefined : readIssueRef(issue3)
|
|
26558
26491
|
});
|
|
26559
26492
|
console.log(url2);
|
|
26560
26493
|
}
|
|
@@ -26598,12 +26531,12 @@ var list8 = command({
|
|
|
26598
26531
|
if (pages.length === 0)
|
|
26599
26532
|
return console.log(group2 === undefined ? "No pages yet." : "No pages in that group.");
|
|
26600
26533
|
printTable([
|
|
26601
|
-
["TITLE", "GROUP", "ACCESS", "
|
|
26534
|
+
["TITLE", "GROUP", "ACCESS", "STYLE", "SIZE", "VERSIONS", "CREATED", "URL"],
|
|
26602
26535
|
...pages.map((page) => [
|
|
26603
26536
|
page.title,
|
|
26604
26537
|
page.group ?? "-",
|
|
26605
26538
|
page.visibility,
|
|
26606
|
-
page.
|
|
26539
|
+
page.style,
|
|
26607
26540
|
formatBytes(page.size),
|
|
26608
26541
|
page.versions === 0 ? "-" : String(page.versions),
|
|
26609
26542
|
new Date(page.createdAt).toISOString().slice(0, 10),
|
|
@@ -26626,6 +26559,14 @@ var remove6 = command({
|
|
|
26626
26559
|
}
|
|
26627
26560
|
});
|
|
26628
26561
|
|
|
26562
|
+
// src/lib/pages.ts
|
|
26563
|
+
var warnPruned = (pruned) => {
|
|
26564
|
+
if (pruned.length === 0)
|
|
26565
|
+
return;
|
|
26566
|
+
const versions2 = pruned.length === 1 ? `version ${pruned[0]}` : `versions ${pruned.join(", ")}`;
|
|
26567
|
+
console.error(`Pruned ${versions2}; the history keeps the newest ${MAX_PAGE_VERSIONS}.`);
|
|
26568
|
+
};
|
|
26569
|
+
|
|
26629
26570
|
// src/commands/pages/revert.ts
|
|
26630
26571
|
var revert = command({
|
|
26631
26572
|
name: "revert",
|
|
@@ -26635,7 +26576,8 @@ var revert = command({
|
|
|
26635
26576
|
version: exports_external.coerce.number().int().positive().describe("Version number from `kds pages versions`")
|
|
26636
26577
|
},
|
|
26637
26578
|
run: async ({ positionals: { id, version: version4 } }) => {
|
|
26638
|
-
const { url: url2 } = await (await backendClient()).mutation(api2.pages.revert, { id: readPageId(id), version: version4 });
|
|
26579
|
+
const { url: url2, pruned } = await (await backendClient()).mutation(api2.pages.revert, { id: readPageId(id), version: version4 });
|
|
26580
|
+
warnPruned(pruned);
|
|
26639
26581
|
console.log(url2);
|
|
26640
26582
|
}
|
|
26641
26583
|
});
|
|
@@ -26643,7 +26585,7 @@ var revert = command({
|
|
|
26643
26585
|
// src/commands/pages/update.ts
|
|
26644
26586
|
var update7 = command({
|
|
26645
26587
|
name: "update",
|
|
26646
|
-
description: "Replace a page's HTML, title, group,
|
|
26588
|
+
description: "Replace a page's HTML, title, group, style, or visibility",
|
|
26647
26589
|
positionals: {
|
|
26648
26590
|
id: exports_external.string().describe("Page id or URL"),
|
|
26649
26591
|
file: exports_external.string().optional().describe("HTML file, or - for stdin")
|
|
@@ -26651,21 +26593,24 @@ var update7 = command({
|
|
|
26651
26593
|
options: {
|
|
26652
26594
|
title: exports_external.string().optional().describe("New page title").meta({ short: "t" }),
|
|
26653
26595
|
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 }),
|
|
26654
|
-
|
|
26655
|
-
visibility: exports_external.enum(PAGE_VISIBILITIES).optional().describe("Who can view the page")
|
|
26596
|
+
style: exports_external.enum(keys(PAGE_STYLES)).optional().describe(`New style \u2014 ${describeChoices(PAGE_STYLES)}`),
|
|
26597
|
+
visibility: exports_external.enum(PAGE_VISIBILITIES).optional().describe("Who can view the page"),
|
|
26598
|
+
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 })
|
|
26656
26599
|
},
|
|
26657
|
-
run: async ({ positionals: { id, file: file2 }, options: { title, group: group2,
|
|
26658
|
-
if (!file2 && title === undefined && group2 === undefined &&
|
|
26659
|
-
throw new Error("Nothing to update. Pass a file, --title, --group, --no-group, --
|
|
26600
|
+
run: async ({ positionals: { id, file: file2 }, options: { title, group: group2, style, visibility, issue: issue3 } }) => {
|
|
26601
|
+
if (!file2 && title === undefined && group2 === undefined && style === undefined && visibility === undefined && issue3 === undefined)
|
|
26602
|
+
throw new Error("Nothing to update. Pass a file, --title, --group, --no-group, --style, --visibility, --issue, or --no-issue.");
|
|
26660
26603
|
const html = file2 ? await readBody(file2) : undefined;
|
|
26661
|
-
const { url: url2 } = await (await backendClient()).action(api2.pages.update, {
|
|
26604
|
+
const { url: url2, pruned } = await (await backendClient()).action(api2.pages.update, {
|
|
26662
26605
|
id: readPageId(id),
|
|
26663
26606
|
html,
|
|
26664
26607
|
title,
|
|
26665
26608
|
group: group2,
|
|
26666
|
-
|
|
26667
|
-
visibility
|
|
26609
|
+
style,
|
|
26610
|
+
visibility,
|
|
26611
|
+
issue: typeof issue3 === "string" ? readIssueRef(issue3) : issue3
|
|
26668
26612
|
});
|
|
26613
|
+
warnPruned(pruned);
|
|
26669
26614
|
console.log(url2);
|
|
26670
26615
|
}
|
|
26671
26616
|
});
|
|
@@ -26687,10 +26632,10 @@ var versions2 = command({
|
|
|
26687
26632
|
if (archived.length === 0)
|
|
26688
26633
|
return console.log("No previous versions.");
|
|
26689
26634
|
printTable([
|
|
26690
|
-
["VERSION", "
|
|
26635
|
+
["VERSION", "STYLE", "SIZE", "ARCHIVED"],
|
|
26691
26636
|
...archived.map((version4) => [
|
|
26692
26637
|
String(version4.version),
|
|
26693
|
-
version4.
|
|
26638
|
+
version4.style,
|
|
26694
26639
|
formatBytes(version4.size),
|
|
26695
26640
|
new Date(version4.archivedAt).toISOString().slice(0, 10)
|
|
26696
26641
|
])
|
|
@@ -26702,10 +26647,10 @@ var versions2 = command({
|
|
|
26702
26647
|
var pages = group({
|
|
26703
26648
|
name: "pages",
|
|
26704
26649
|
description: "Publish HTML documents to the web",
|
|
26705
|
-
longDescription: `
|
|
26706
|
-
|
|
26707
|
-
|
|
26708
|
-
|
|
26650
|
+
longDescription: `A page is an HTML fragment rendered in one of the site's styles:
|
|
26651
|
+
${describeChoices(PAGE_STYLES, `
|
|
26652
|
+
`)}
|
|
26653
|
+
Basic is the default. A page keeps its URL across updates and reverts.`,
|
|
26709
26654
|
commands: [create6, list8, get5, update7, versions2, revert, remove6]
|
|
26710
26655
|
});
|
|
26711
26656
|
|
|
@@ -26764,7 +26709,7 @@ var create7 = command({
|
|
|
26764
26709
|
health: options.health,
|
|
26765
26710
|
targetDate: options.target,
|
|
26766
26711
|
repo: options.repo,
|
|
26767
|
-
clientId: options.client === undefined ? undefined : (
|
|
26712
|
+
clientId: options.client === undefined ? undefined : readClientRef(options.client)
|
|
26768
26713
|
});
|
|
26769
26714
|
console.log(projectId);
|
|
26770
26715
|
}
|
|
@@ -26866,9 +26811,8 @@ var remove7 = command({
|
|
|
26866
26811
|
},
|
|
26867
26812
|
run: async ({ positionals: { id } }) => {
|
|
26868
26813
|
const client3 = await backendClient();
|
|
26869
|
-
const
|
|
26870
|
-
|
|
26871
|
-
console.log(`Deleted ${project2.name}.`);
|
|
26814
|
+
const removed = await client3.mutation(api2.projects.remove, { id: readProjectRef(id) });
|
|
26815
|
+
console.log(`Deleted ${removed.name}.`);
|
|
26872
26816
|
}
|
|
26873
26817
|
});
|
|
26874
26818
|
|
|
@@ -26891,21 +26835,15 @@ var set3 = command({
|
|
|
26891
26835
|
if (Object.values(options).every((value) => value === undefined))
|
|
26892
26836
|
throw new Error("Nothing to set. Pass --status, --health, --target, --repo, or --client.");
|
|
26893
26837
|
const backend = await backendClient();
|
|
26894
|
-
const
|
|
26895
|
-
|
|
26896
|
-
|
|
26897
|
-
|
|
26898
|
-
|
|
26899
|
-
|
|
26900
|
-
|
|
26901
|
-
|
|
26902
|
-
|
|
26903
|
-
await backend.mutation(api2.projects.setStatus, { id: project2._id, status: status3 });
|
|
26904
|
-
if (health !== undefined)
|
|
26905
|
-
await backend.mutation(api2.projects.setHealth, { id: project2._id, health: health ?? undefined });
|
|
26906
|
-
if (target !== undefined)
|
|
26907
|
-
await backend.mutation(api2.projects.setTargetDate, { id: project2._id, targetDate: target ?? undefined });
|
|
26908
|
-
console.log(`Updated ${project2.name}.`);
|
|
26838
|
+
const { name } = await backend.mutation(api2.projects.edit, {
|
|
26839
|
+
id: readProjectRef(id),
|
|
26840
|
+
...repo !== undefined ? { repo } : {},
|
|
26841
|
+
...client3 !== undefined ? { client: client3 === null ? null : readClientRef(client3) } : {},
|
|
26842
|
+
...status3 !== undefined ? { status: status3 } : {},
|
|
26843
|
+
...health !== undefined ? { health } : {},
|
|
26844
|
+
...target !== undefined ? { targetDate: target } : {}
|
|
26845
|
+
});
|
|
26846
|
+
console.log(`Updated ${name}.`);
|
|
26909
26847
|
}
|
|
26910
26848
|
});
|
|
26911
26849
|
|
|
@@ -26925,14 +26863,13 @@ var update8 = command({
|
|
|
26925
26863
|
if (name === undefined && summary === undefined && description === undefined)
|
|
26926
26864
|
throw new Error("Nothing to update. Pass --name, --summary, or --description.");
|
|
26927
26865
|
const client3 = await backendClient();
|
|
26928
|
-
const
|
|
26929
|
-
|
|
26930
|
-
id: project2._id,
|
|
26866
|
+
const updated = await client3.mutation(api2.projects.update, {
|
|
26867
|
+
id: readProjectRef(id),
|
|
26931
26868
|
name,
|
|
26932
26869
|
summary,
|
|
26933
26870
|
descriptionMarkdown: description === undefined ? undefined : await readTextOption(description)
|
|
26934
26871
|
});
|
|
26935
|
-
console.log(`Updated ${
|
|
26872
|
+
console.log(`Updated ${updated.name}.`);
|
|
26936
26873
|
}
|
|
26937
26874
|
});
|
|
26938
26875
|
|
|
@@ -26958,13 +26895,12 @@ var accept = command({
|
|
|
26958
26895
|
},
|
|
26959
26896
|
run: async ({ positionals: { id }, options }) => {
|
|
26960
26897
|
const backend = await backendClient();
|
|
26961
|
-
const
|
|
26962
|
-
|
|
26963
|
-
id: quotation._id,
|
|
26898
|
+
const accepted = await backend.mutation(api2.quotations.accept, {
|
|
26899
|
+
id: readQuotationRef(id),
|
|
26964
26900
|
on: options.date ?? localToday(),
|
|
26965
26901
|
note: options.note
|
|
26966
26902
|
});
|
|
26967
|
-
console.log(`Accepted ${
|
|
26903
|
+
console.log(`Accepted ${accepted.number}. Draw invoices from it with: kds quotations convert ${accepted.id}`);
|
|
26968
26904
|
}
|
|
26969
26905
|
});
|
|
26970
26906
|
|
|
@@ -26985,7 +26921,6 @@ Nothing blocks over- or under-invoicing; left to bill is advisory.`,
|
|
|
26985
26921
|
},
|
|
26986
26922
|
run: async ({ positionals: { id }, options }) => {
|
|
26987
26923
|
const backend = await backendClient();
|
|
26988
|
-
const quotation = await resolveQuotation(backend, id);
|
|
26989
26924
|
const lineIndexes = options.lines?.split(",").map((part) => {
|
|
26990
26925
|
const lineNumber = Number(part.trim());
|
|
26991
26926
|
if (!Number.isInteger(lineNumber) || lineNumber < 1) {
|
|
@@ -26994,7 +26929,7 @@ Nothing blocks over- or under-invoicing; left to bill is advisory.`,
|
|
|
26994
26929
|
return lineNumber - 1;
|
|
26995
26930
|
});
|
|
26996
26931
|
const invoiceId = await backend.mutation(api2.quotations.convert, {
|
|
26997
|
-
id:
|
|
26932
|
+
id: readQuotationRef(id),
|
|
26998
26933
|
lineIndexes,
|
|
26999
26934
|
percentage: options.percentage
|
|
27000
26935
|
});
|
|
@@ -27017,11 +26952,9 @@ var create8 = command({
|
|
|
27017
26952
|
},
|
|
27018
26953
|
run: async ({ positionals: { client: ref }, options }) => {
|
|
27019
26954
|
const backend = await backendClient();
|
|
27020
|
-
const client3 = await resolveClient(backend, ref);
|
|
27021
|
-
const project2 = options.project === undefined ? undefined : await resolveProject(backend, options.project);
|
|
27022
26955
|
const quotationId = await backend.mutation(api2.quotations.create, {
|
|
27023
|
-
clientId:
|
|
27024
|
-
projectId:
|
|
26956
|
+
clientId: readClientRef(ref),
|
|
26957
|
+
projectId: options.project === undefined ? undefined : readProjectRef(options.project),
|
|
27025
26958
|
lineItems: options.line?.map(parseQuotationLineItem),
|
|
27026
26959
|
notes: options.notes,
|
|
27027
26960
|
validUntil: options.validUntil === null ? "" : options.validUntil,
|
|
@@ -27040,8 +26973,21 @@ var duplicate2 = command({
|
|
|
27040
26973
|
},
|
|
27041
26974
|
run: async ({ positionals: { id } }) => {
|
|
27042
26975
|
const backend = await backendClient();
|
|
27043
|
-
|
|
27044
|
-
|
|
26976
|
+
console.log(await backend.mutation(api2.quotations.duplicate, { id: readQuotationRef(id), today: localToday() }));
|
|
26977
|
+
}
|
|
26978
|
+
});
|
|
26979
|
+
|
|
26980
|
+
// src/commands/quotations/email.ts
|
|
26981
|
+
var email4 = command({
|
|
26982
|
+
name: "email",
|
|
26983
|
+
description: "Email the quotation's link to the client's billing address; repeatable, and never required",
|
|
26984
|
+
positionals: {
|
|
26985
|
+
id: exports_external.string().describe("Quotation id or URL")
|
|
26986
|
+
},
|
|
26987
|
+
run: async ({ positionals: { id } }) => {
|
|
26988
|
+
const backend = await backendClient();
|
|
26989
|
+
const { to, number: number4 } = await backend.mutation(api2.quotations.email, { id: readQuotationRef(id) });
|
|
26990
|
+
console.log(`Emailed ${number4} to ${to}.`);
|
|
27045
26991
|
}
|
|
27046
26992
|
});
|
|
27047
26993
|
|
|
@@ -27070,6 +27016,9 @@ var get7 = command({
|
|
|
27070
27016
|
console.log(`Valid until: ${quotation.validUntil}`);
|
|
27071
27017
|
if (quotation.publicUrl)
|
|
27072
27018
|
console.log(`Link: ${quotation.publicUrl}`);
|
|
27019
|
+
if (quotation.status !== "draft") {
|
|
27020
|
+
console.log(`Emailed: ${quotation.lastEmailedAt === undefined ? "never" : localDateString(quotation.lastEmailedAt)}`);
|
|
27021
|
+
}
|
|
27073
27022
|
if (quotation.outcome) {
|
|
27074
27023
|
console.log(`${quotation.status === "accepted" ? "Accepted" : "Rejected"}: ${quotation.outcome.on}${quotation.outcome.note ? ` \u2014 ${quotation.outcome.note}` : ""}`);
|
|
27075
27024
|
}
|
|
@@ -27126,9 +27075,7 @@ var issue3 = command({
|
|
|
27126
27075
|
},
|
|
27127
27076
|
run: async ({ positionals: { id } }) => {
|
|
27128
27077
|
const backend = await backendClient();
|
|
27129
|
-
const
|
|
27130
|
-
await backend.mutation(api2.quotations.issue, { id: quotation._id, today: localToday() });
|
|
27131
|
-
const issued = await resolveQuotation(backend, quotation._id);
|
|
27078
|
+
const issued = await backend.mutation(api2.quotations.issue, { id: readQuotationRef(id), today: localToday() });
|
|
27132
27079
|
console.log(`Issued ${issued.number}, ${issued.validUntil ? `valid until ${issued.validUntil}` : "with no expiry"}.`);
|
|
27133
27080
|
}
|
|
27134
27081
|
});
|
|
@@ -27143,10 +27090,11 @@ var link2 = command({
|
|
|
27143
27090
|
},
|
|
27144
27091
|
run: async ({ positionals: { id, project: projectRef } }) => {
|
|
27145
27092
|
const backend = await backendClient();
|
|
27146
|
-
const
|
|
27147
|
-
|
|
27148
|
-
|
|
27149
|
-
|
|
27093
|
+
const { projectName } = await backend.mutation(api2.quotations.setProject, {
|
|
27094
|
+
id: readQuotationRef(id),
|
|
27095
|
+
projectId: projectRef === undefined ? undefined : readProjectRef(projectRef)
|
|
27096
|
+
});
|
|
27097
|
+
console.log(projectName === undefined ? "Cleared the project link." : `Linked to ${projectName}.`);
|
|
27150
27098
|
}
|
|
27151
27099
|
});
|
|
27152
27100
|
|
|
@@ -27174,7 +27122,7 @@ var list10 = command({
|
|
|
27174
27122
|
if (result.page.length === 0)
|
|
27175
27123
|
return console.log("No quotations yet.");
|
|
27176
27124
|
printTable([
|
|
27177
|
-
["NUMBER", "CLIENT", "PROJECT", "STATUS", "TOTAL", "VALID UNTIL", "ID"],
|
|
27125
|
+
["NUMBER", "CLIENT", "PROJECT", "STATUS", "TOTAL", "VALID UNTIL", "EMAILED", "ID"],
|
|
27178
27126
|
...result.page.map((quotation) => [
|
|
27179
27127
|
quotation.number ?? "draft",
|
|
27180
27128
|
quotation.clientName,
|
|
@@ -27182,6 +27130,7 @@ var list10 = command({
|
|
|
27182
27130
|
quotation.derivedStatus,
|
|
27183
27131
|
formatPerCurrency(quotation.totals.map(({ currency, total }) => ({ currency, amount: total }))),
|
|
27184
27132
|
quotation.validUntil ?? "-",
|
|
27133
|
+
quotation.lastEmailedAt === undefined ? "-" : localDateString(quotation.lastEmailedAt),
|
|
27185
27134
|
quotation._id
|
|
27186
27135
|
])
|
|
27187
27136
|
]);
|
|
@@ -27204,13 +27153,12 @@ var reject2 = command({
|
|
|
27204
27153
|
},
|
|
27205
27154
|
run: async ({ positionals: { id }, options }) => {
|
|
27206
27155
|
const backend = await backendClient();
|
|
27207
|
-
const
|
|
27208
|
-
|
|
27209
|
-
id: quotation._id,
|
|
27156
|
+
const { number: number4 } = await backend.mutation(api2.quotations.reject, {
|
|
27157
|
+
id: readQuotationRef(id),
|
|
27210
27158
|
on: options.date ?? localToday(),
|
|
27211
27159
|
note: options.note
|
|
27212
27160
|
});
|
|
27213
|
-
console.log(`Rejected ${
|
|
27161
|
+
console.log(`Rejected ${number4}.`);
|
|
27214
27162
|
}
|
|
27215
27163
|
});
|
|
27216
27164
|
|
|
@@ -27223,9 +27171,8 @@ var rotateLink2 = command({
|
|
|
27223
27171
|
},
|
|
27224
27172
|
run: async ({ positionals: { id } }) => {
|
|
27225
27173
|
const backend = await backendClient();
|
|
27226
|
-
const
|
|
27227
|
-
|
|
27228
|
-
console.log(`${quotation.number} now reads at ${url2}. Send it again \u2014 the previous link is dead.`);
|
|
27174
|
+
const { url: url2, number: number4 } = await backend.mutation(api2.quotations.rotateLink, { id: readQuotationRef(id) });
|
|
27175
|
+
console.log(`${number4} now reads at ${url2}. Send it again \u2014 the previous link is dead.`);
|
|
27229
27176
|
}
|
|
27230
27177
|
});
|
|
27231
27178
|
|
|
@@ -27247,14 +27194,13 @@ var update9 = command({
|
|
|
27247
27194
|
throw new Error("Nothing to update. Pass --line, --notes, or --valid-until (or a --no form to clear).");
|
|
27248
27195
|
}
|
|
27249
27196
|
const backend = await backendClient();
|
|
27250
|
-
const
|
|
27251
|
-
|
|
27252
|
-
id: quotation._id,
|
|
27197
|
+
const { number: number4 } = await backend.mutation(api2.quotations.update, {
|
|
27198
|
+
id: readQuotationRef(id),
|
|
27253
27199
|
lineItems: options.line?.map(parseQuotationLineItem),
|
|
27254
27200
|
notes: options.notes === null ? "" : options.notes,
|
|
27255
27201
|
validUntil: options.validUntil === null ? "" : options.validUntil
|
|
27256
27202
|
});
|
|
27257
|
-
console.log(`Updated ${
|
|
27203
|
+
console.log(`Updated ${number4 ?? "draft"}.`);
|
|
27258
27204
|
}
|
|
27259
27205
|
});
|
|
27260
27206
|
|
|
@@ -27267,9 +27213,8 @@ var voidQuotation = command({
|
|
|
27267
27213
|
},
|
|
27268
27214
|
run: async ({ positionals: { id } }) => {
|
|
27269
27215
|
const backend = await backendClient();
|
|
27270
|
-
const
|
|
27271
|
-
|
|
27272
|
-
console.log(`Voided ${quotation.number}.`);
|
|
27216
|
+
const { number: number4 } = await backend.mutation(api2.quotations.voidQuotation, { id: readQuotationRef(id) });
|
|
27217
|
+
console.log(`Voided ${number4}.`);
|
|
27273
27218
|
}
|
|
27274
27219
|
});
|
|
27275
27220
|
|
|
@@ -27281,7 +27226,21 @@ var quotations = group({
|
|
|
27281
27226
|
document awaiting the client's answer. Accept and reject are manual records;
|
|
27282
27227
|
expiry is derived from valid-until and never blocks accepting. An accepted
|
|
27283
27228
|
quotation converts into any number of draft invoices, each tracing back to it.`,
|
|
27284
|
-
commands: [
|
|
27229
|
+
commands: [
|
|
27230
|
+
create8,
|
|
27231
|
+
list10,
|
|
27232
|
+
get7,
|
|
27233
|
+
update9,
|
|
27234
|
+
issue3,
|
|
27235
|
+
email4,
|
|
27236
|
+
accept,
|
|
27237
|
+
reject2,
|
|
27238
|
+
voidQuotation,
|
|
27239
|
+
duplicate2,
|
|
27240
|
+
convert,
|
|
27241
|
+
link2,
|
|
27242
|
+
rotateLink2
|
|
27243
|
+
]
|
|
27285
27244
|
});
|
|
27286
27245
|
|
|
27287
27246
|
// src/commands/upgrade.ts
|
|
@@ -27312,6 +27271,7 @@ var rootCommand = group({
|
|
|
27312
27271
|
|
|
27313
27272
|
// src/lib/errors.ts
|
|
27314
27273
|
var errorMessage = (error51) => error51 instanceof ConvexError && typeof error51.data === "string" ? error51.data : error51.message;
|
|
27274
|
+
var isRedactedServerError = (error51) => error51 instanceof Error && !(error51 instanceof ConvexError) && /^\[Request ID: \w+\] Server Error$/.test(error51.message);
|
|
27315
27275
|
|
|
27316
27276
|
// src/lib/update-check.ts
|
|
27317
27277
|
var latestVersion = async () => {
|
|
@@ -27342,6 +27302,12 @@ var maybeNotifyUpdate = async () => {
|
|
|
27342
27302
|
if (latest && Bun.semver.order(latest, CLI_VERSION) === 1)
|
|
27343
27303
|
console.error(`kds v${latest} available (you have v${CLI_VERSION}) \u2014 run 'kds upgrade'`);
|
|
27344
27304
|
};
|
|
27305
|
+
var staleCliHint = async (current = CLI_VERSION) => {
|
|
27306
|
+
const latest = env.KDS_NO_UPDATE_CHECK ? undefined : await latestVersion().catch(() => {
|
|
27307
|
+
return;
|
|
27308
|
+
});
|
|
27309
|
+
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.";
|
|
27310
|
+
};
|
|
27345
27311
|
|
|
27346
27312
|
// src/index.ts
|
|
27347
27313
|
var args = Bun.argv.slice(2);
|
|
@@ -27351,6 +27317,8 @@ try {
|
|
|
27351
27317
|
await maybeNotifyUpdate();
|
|
27352
27318
|
} catch (error51) {
|
|
27353
27319
|
console.error(errorMessage(error51));
|
|
27320
|
+
if (isRedactedServerError(error51))
|
|
27321
|
+
console.error(await staleCliHint());
|
|
27354
27322
|
process.exitCode = 1;
|
|
27355
27323
|
} finally {
|
|
27356
27324
|
closePrompts();
|