@islamihab/kds 0.10.0 → 0.11.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 +444 -558
- 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.11.0",
|
|
14615
14627
|
private: true,
|
|
14616
14628
|
type: "module",
|
|
14617
14629
|
bin: {
|
|
@@ -15306,6 +15318,7 @@ var PROJECT_STATUSES = ["planned", "in_progress", "paused", "completed", "cancel
|
|
|
15306
15318
|
var PROJECT_HEALTHS = ["on_track", "at_risk", "off_track"];
|
|
15307
15319
|
var ISSUE_IDENTIFIER_PREFIX = "KAI";
|
|
15308
15320
|
var ISSUE_LIST_PAGE_SIZE = 50;
|
|
15321
|
+
var MAX_ISSUES_PER_BATCH_READ = 20;
|
|
15309
15322
|
var MAX_ISSUE_ATTACHMENT_BYTES = 1e7;
|
|
15310
15323
|
var ISSUE_DUE_DATE_MODES = ["overdue", "due_today", "due_soon", "no_due_date"];
|
|
15311
15324
|
var BUILT_IN_ISSUE_VIEWS = {
|
|
@@ -15352,13 +15365,8 @@ var BUILT_IN_ISSUE_VIEWS = {
|
|
|
15352
15365
|
}
|
|
15353
15366
|
};
|
|
15354
15367
|
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;
|
|
15368
|
+
var CURRENCIES = ["EGP", "USD", "EUR", "GBP", "AED", "SAR"];
|
|
15369
|
+
var DEFAULT_CURRENCY = "EGP";
|
|
15362
15370
|
var RECURRING_CHARGE_CADENCES = ["monthly", "yearly"];
|
|
15363
15371
|
var COST_KINDS = ["one_off", "monthly", "yearly"];
|
|
15364
15372
|
var MAX_PROJECT_REPO_LENGTH = 200;
|
|
@@ -24257,6 +24265,73 @@ var readPageId = (value) => {
|
|
|
24257
24265
|
}
|
|
24258
24266
|
};
|
|
24259
24267
|
|
|
24268
|
+
// src/commands/clients/archive.ts
|
|
24269
|
+
var archive = command({
|
|
24270
|
+
name: "archive",
|
|
24271
|
+
description: "Archive a client: it leaves pickers and default lists, and every existing reference stays intact",
|
|
24272
|
+
positionals: {
|
|
24273
|
+
id: exports_external.string().describe("Client id or URL")
|
|
24274
|
+
},
|
|
24275
|
+
run: async ({ positionals: { id } }) => {
|
|
24276
|
+
const backend = await backendClient();
|
|
24277
|
+
const { name } = await backend.mutation(api2.clients.archive, { id: readClientRef(id) });
|
|
24278
|
+
console.log(`Archived ${name}.`);
|
|
24279
|
+
}
|
|
24280
|
+
});
|
|
24281
|
+
|
|
24282
|
+
// src/lib/currency.ts
|
|
24283
|
+
var CURRENCY_CHOICES = CURRENCIES.join(", ");
|
|
24284
|
+
var currencyCode = exports_external.preprocess((value) => typeof value === "string" ? value.toUpperCase() : value, exports_external.enum(CURRENCIES));
|
|
24285
|
+
var parseLineCurrency = (raw) => {
|
|
24286
|
+
const code2 = raw.trim().toUpperCase();
|
|
24287
|
+
if (!code2)
|
|
24288
|
+
return DEFAULT_CURRENCY;
|
|
24289
|
+
const codes = CURRENCIES;
|
|
24290
|
+
if (!codes.includes(code2))
|
|
24291
|
+
throw new Error(`Line currency must be one of ${CURRENCY_CHOICES}; got "${raw.trim()}".`);
|
|
24292
|
+
return code2;
|
|
24293
|
+
};
|
|
24294
|
+
|
|
24295
|
+
// src/commands/clients/charges/add.ts
|
|
24296
|
+
var add = command({
|
|
24297
|
+
name: "add",
|
|
24298
|
+
description: "Add a recurring charge to a client and print its id",
|
|
24299
|
+
positionals: {
|
|
24300
|
+
client: exports_external.string().describe("Client id or URL"),
|
|
24301
|
+
description: exports_external.string().describe('What the charge is for, e.g. "Maintenance retainer"')
|
|
24302
|
+
},
|
|
24303
|
+
options: {
|
|
24304
|
+
amount: exports_external.coerce.number().int().describe("Amount in integer minor units (2500 = 25.00)").meta({ short: "a" }),
|
|
24305
|
+
currency: currencyCode.default(DEFAULT_CURRENCY).describe(`One of ${CURRENCY_CHOICES}`).meta({ short: "c" }),
|
|
24306
|
+
cadence: exports_external.enum(RECURRING_CHARGE_CADENCES).default("monthly").describe("monthly or yearly"),
|
|
24307
|
+
"due-month": exports_external.coerce.number().int().optional().describe("Month (1-12) a yearly charge falls due; defaults to the current month")
|
|
24308
|
+
},
|
|
24309
|
+
run: async ({ positionals: { client: ref, description }, options }) => {
|
|
24310
|
+
const backend = await backendClient();
|
|
24311
|
+
const chargeId = await backend.mutation(api2.recurringCharges.create, {
|
|
24312
|
+
clientId: readClientRef(ref),
|
|
24313
|
+
description,
|
|
24314
|
+
amount: options.amount,
|
|
24315
|
+
currency: options.currency,
|
|
24316
|
+
cadence: options.cadence,
|
|
24317
|
+
dueMonth: options["due-month"]
|
|
24318
|
+
});
|
|
24319
|
+
console.log(chargeId);
|
|
24320
|
+
}
|
|
24321
|
+
});
|
|
24322
|
+
|
|
24323
|
+
// src/lib/output.ts
|
|
24324
|
+
var formatMinorAmount = (amount, currency) => `${(amount / 100).toFixed(2)} ${currency}`;
|
|
24325
|
+
var printTable = (rows) => {
|
|
24326
|
+
if (rows.length === 0)
|
|
24327
|
+
return;
|
|
24328
|
+
const columnCount = Math.max(...rows.map((row) => row.length));
|
|
24329
|
+
const widths = Array.from({ length: columnCount }, (_, column) => Math.max(...rows.map((row) => (row[column] ?? "").length)));
|
|
24330
|
+
for (const row of rows) {
|
|
24331
|
+
console.log(row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd());
|
|
24332
|
+
}
|
|
24333
|
+
};
|
|
24334
|
+
|
|
24260
24335
|
// ../../packages/backend/convex/lib/projectRepo.ts
|
|
24261
24336
|
var DEFAULT_REPO_HOST = "github.com";
|
|
24262
24337
|
var REPO_FORMAT_ERROR = "Enter a repository like github.com/owner/name.";
|
|
@@ -24329,24 +24404,6 @@ var resolveQuotation = async (client3, ref) => {
|
|
|
24329
24404
|
throw new Error(`No quotation matches ${ref}.`);
|
|
24330
24405
|
return quotation;
|
|
24331
24406
|
};
|
|
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
24407
|
var repoProject = async (client3) => {
|
|
24351
24408
|
const repo = await currentRepoKey();
|
|
24352
24409
|
if (!repo)
|
|
@@ -24356,23 +24413,6 @@ var repoProject = async (client3) => {
|
|
|
24356
24413
|
throw new Error(`No project is connected to ${repo}. Connect one on the project in the dashboard.`);
|
|
24357
24414
|
return project;
|
|
24358
24415
|
};
|
|
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
24416
|
var resolveMilestone = async (client3, projectId, name) => {
|
|
24377
24417
|
const milestone = await client3.query(api2.milestones.find, { projectId, name });
|
|
24378
24418
|
if (!milestone) {
|
|
@@ -24383,62 +24423,6 @@ var resolveMilestone = async (client3, projectId, name) => {
|
|
|
24383
24423
|
return milestone;
|
|
24384
24424
|
};
|
|
24385
24425
|
|
|
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
|
-
|
|
24442
24426
|
// src/commands/clients/charges/list.ts
|
|
24443
24427
|
var monthName = (month) => new Date(Date.UTC(2000, month - 1)).toLocaleString("en-US", { month: "short", timeZone: "UTC" });
|
|
24444
24428
|
var list = command({
|
|
@@ -24480,9 +24464,8 @@ var pause = command({
|
|
|
24480
24464
|
},
|
|
24481
24465
|
run: async ({ positionals: { id } }) => {
|
|
24482
24466
|
const backend = await backendClient();
|
|
24483
|
-
const
|
|
24484
|
-
|
|
24485
|
-
console.log(`Paused ${charge.description}.`);
|
|
24467
|
+
const { description } = await backend.mutation(api2.recurringCharges.pause, { id: id.trim() });
|
|
24468
|
+
console.log(`Paused ${description}.`);
|
|
24486
24469
|
}
|
|
24487
24470
|
});
|
|
24488
24471
|
|
|
@@ -24495,9 +24478,8 @@ var remove = command({
|
|
|
24495
24478
|
},
|
|
24496
24479
|
run: async ({ positionals: { id } }) => {
|
|
24497
24480
|
const backend = await backendClient();
|
|
24498
|
-
const
|
|
24499
|
-
|
|
24500
|
-
console.log(`Removed ${charge.description}.`);
|
|
24481
|
+
const { description } = await backend.mutation(api2.recurringCharges.remove, { id: id.trim() });
|
|
24482
|
+
console.log(`Removed ${description}.`);
|
|
24501
24483
|
}
|
|
24502
24484
|
});
|
|
24503
24485
|
|
|
@@ -24510,9 +24492,8 @@ var resume = command({
|
|
|
24510
24492
|
},
|
|
24511
24493
|
run: async ({ positionals: { id } }) => {
|
|
24512
24494
|
const backend = await backendClient();
|
|
24513
|
-
const
|
|
24514
|
-
|
|
24515
|
-
console.log(`Resumed ${charge.description}.`);
|
|
24495
|
+
const { description } = await backend.mutation(api2.recurringCharges.resume, { id: id.trim() });
|
|
24496
|
+
console.log(`Resumed ${description}.`);
|
|
24516
24497
|
}
|
|
24517
24498
|
});
|
|
24518
24499
|
|
|
@@ -24526,7 +24507,7 @@ var update = command({
|
|
|
24526
24507
|
options: {
|
|
24527
24508
|
description: exports_external.string().optional().describe("New description").meta({ short: "d" }),
|
|
24528
24509
|
amount: exports_external.coerce.number().int().optional().describe("Amount in integer minor units").meta({ short: "a" }),
|
|
24529
|
-
currency:
|
|
24510
|
+
currency: currencyCode.optional().describe(`One of ${CURRENCY_CHOICES}`).meta({ short: "c" }),
|
|
24530
24511
|
cadence: exports_external.enum(RECURRING_CHARGE_CADENCES).optional().describe("monthly or yearly"),
|
|
24531
24512
|
"due-month": exports_external.coerce.number().int().optional().describe("Month (1-12) a yearly charge falls due")
|
|
24532
24513
|
},
|
|
@@ -24535,16 +24516,15 @@ var update = command({
|
|
|
24535
24516
|
throw new Error("Nothing to update. Pass --description, --amount, --currency, --cadence, or --due-month.");
|
|
24536
24517
|
}
|
|
24537
24518
|
const backend = await backendClient();
|
|
24538
|
-
const
|
|
24539
|
-
|
|
24540
|
-
id: charge._id,
|
|
24519
|
+
const { description } = await backend.mutation(api2.recurringCharges.update, {
|
|
24520
|
+
id: id.trim(),
|
|
24541
24521
|
description: options.description,
|
|
24542
24522
|
amount: options.amount,
|
|
24543
24523
|
currency: options.currency,
|
|
24544
24524
|
cadence: options.cadence,
|
|
24545
24525
|
dueMonth: options["due-month"]
|
|
24546
24526
|
});
|
|
24547
|
-
console.log(`Updated ${
|
|
24527
|
+
console.log(`Updated ${description}.`);
|
|
24548
24528
|
}
|
|
24549
24529
|
});
|
|
24550
24530
|
|
|
@@ -24658,65 +24638,6 @@ More clients exist; raise --limit past ${limit}.`);
|
|
|
24658
24638
|
}
|
|
24659
24639
|
});
|
|
24660
24640
|
|
|
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
24641
|
// src/commands/clients/update.ts
|
|
24721
24642
|
var update2 = command({
|
|
24722
24643
|
name: "update",
|
|
@@ -24740,42 +24661,19 @@ var update2 = command({
|
|
|
24740
24661
|
throw new Error("Nothing to update. Pass a profile flag, or a --no form to clear one.");
|
|
24741
24662
|
}
|
|
24742
24663
|
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
24664
|
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}.`);
|
|
24665
|
+
const { name } = await backend.mutation(api2.clients.edit, {
|
|
24666
|
+
id: readClientRef(id),
|
|
24667
|
+
...options.name !== undefined ? { name: options.name } : {},
|
|
24668
|
+
...options["legal-name"] !== undefined ? { legalName: text(options["legal-name"]) } : {},
|
|
24669
|
+
...options.address !== undefined ? { billingAddress: text(options.address) } : {},
|
|
24670
|
+
...options.email !== undefined ? { billingEmail: text(options.email) } : {},
|
|
24671
|
+
...options["tax-id"] !== undefined ? { taxId: text(options["tax-id"]) } : {},
|
|
24672
|
+
...options["payment-terms"] !== undefined ? { paymentTermsDays: options["payment-terms"] } : {},
|
|
24673
|
+
...options.currency !== undefined ? { defaultCurrency: options.currency } : {},
|
|
24674
|
+
...options["billing-day"] !== undefined ? { billingDay: options["billing-day"] } : {}
|
|
24675
|
+
});
|
|
24676
|
+
console.log(`Updated ${name}.`);
|
|
24779
24677
|
}
|
|
24780
24678
|
});
|
|
24781
24679
|
|
|
@@ -24801,7 +24699,7 @@ the same client.`,
|
|
|
24801
24699
|
},
|
|
24802
24700
|
options: {
|
|
24803
24701
|
amount: exports_external.coerce.number().int().describe("Amount in integer minor units (2500 = 25.00)").meta({ short: "a" }),
|
|
24804
|
-
currency:
|
|
24702
|
+
currency: currencyCode.default(DEFAULT_CURRENCY).describe(`One of ${CURRENCY_CHOICES}`).meta({ short: "c" }),
|
|
24805
24703
|
kind: exports_external.enum(COST_KINDS).default("one_off").describe("one_off, monthly, or yearly").meta({ short: "k" }),
|
|
24806
24704
|
date: exports_external.string().optional().describe("The one-off's date, or a subscription's start (YYYY-MM-DD); default today"),
|
|
24807
24705
|
end: exports_external.string().optional().describe("Last date a subscription is committed to (YYYY-MM-DD)"),
|
|
@@ -24811,7 +24709,6 @@ the same client.`,
|
|
|
24811
24709
|
},
|
|
24812
24710
|
run: async ({ positionals: { name }, options }) => {
|
|
24813
24711
|
const backend = await backendClient();
|
|
24814
|
-
const projects = await Promise.all((options.project ?? []).map((ref) => resolveProject(backend, ref)));
|
|
24815
24712
|
const costId = await backend.mutation(api2.costs.create, {
|
|
24816
24713
|
name,
|
|
24817
24714
|
note: options.note,
|
|
@@ -24820,7 +24717,7 @@ the same client.`,
|
|
|
24820
24717
|
endDate: options.end,
|
|
24821
24718
|
amount: options.amount,
|
|
24822
24719
|
currency: options.currency,
|
|
24823
|
-
projectIds:
|
|
24720
|
+
projectIds: options.project?.map(readProjectRef),
|
|
24824
24721
|
billable: options.billable
|
|
24825
24722
|
});
|
|
24826
24723
|
console.log(costId);
|
|
@@ -24840,10 +24737,9 @@ var end = command({
|
|
|
24840
24737
|
},
|
|
24841
24738
|
run: async ({ positionals: { id }, options }) => {
|
|
24842
24739
|
const backend = await backendClient();
|
|
24843
|
-
const cost = await resolveCost(backend, id);
|
|
24844
24740
|
const today = localToday();
|
|
24845
|
-
await backend.mutation(api2.costs.end, { id:
|
|
24846
|
-
console.log(`Ended ${
|
|
24741
|
+
const { name } = await backend.mutation(api2.costs.end, { id: id.trim(), endDate: options.date, today });
|
|
24742
|
+
console.log(`Ended ${name} on ${options.date ?? today}.`);
|
|
24847
24743
|
}
|
|
24848
24744
|
});
|
|
24849
24745
|
|
|
@@ -24954,9 +24850,8 @@ var remove2 = command({
|
|
|
24954
24850
|
},
|
|
24955
24851
|
run: async ({ positionals: { id } }) => {
|
|
24956
24852
|
const backend = await backendClient();
|
|
24957
|
-
const
|
|
24958
|
-
|
|
24959
|
-
console.log(`Deleted ${cost.name}.`);
|
|
24853
|
+
const { name } = await backend.mutation(api2.costs.remove, { id: id.trim() });
|
|
24854
|
+
console.log(`Deleted ${name}.`);
|
|
24960
24855
|
}
|
|
24961
24856
|
});
|
|
24962
24857
|
|
|
@@ -24975,7 +24870,7 @@ is fixed at creation \u2014 delete and re-record to change it.`,
|
|
|
24975
24870
|
options: {
|
|
24976
24871
|
name: exports_external.string().optional().describe("New name"),
|
|
24977
24872
|
amount: exports_external.coerce.number().int().optional().describe("Amount in integer minor units").meta({ short: "a" }),
|
|
24978
|
-
currency:
|
|
24873
|
+
currency: currencyCode.optional().describe(`One of ${CURRENCY_CHOICES}`).meta({ short: "c" }),
|
|
24979
24874
|
date: exports_external.string().optional().describe("The one-off's date, or a subscription's start (YYYY-MM-DD)"),
|
|
24980
24875
|
end: exports_external.string().nullable().optional().describe("End date (YYYY-MM-DD), or --no-end to resume").meta({
|
|
24981
24876
|
negatable: true
|
|
@@ -24991,10 +24886,8 @@ is fixed at creation \u2014 delete and re-record to change it.`,
|
|
|
24991
24886
|
throw new Error("Nothing to update. Pass --name, --amount, --currency, --date, --end, --project, --billable, or --note.");
|
|
24992
24887
|
}
|
|
24993
24888
|
const backend = await backendClient();
|
|
24994
|
-
const
|
|
24995
|
-
|
|
24996
|
-
await backend.mutation(api2.costs.update, {
|
|
24997
|
-
id: cost._id,
|
|
24889
|
+
const { name } = await backend.mutation(api2.costs.update, {
|
|
24890
|
+
id: id.trim(),
|
|
24998
24891
|
today: localToday(),
|
|
24999
24892
|
name: options.name,
|
|
25000
24893
|
note: options.note === null ? "" : options.note,
|
|
@@ -25002,10 +24895,10 @@ is fixed at creation \u2014 delete and re-record to change it.`,
|
|
|
25002
24895
|
endDate: options.end === null ? "" : options.end,
|
|
25003
24896
|
amount: options.amount,
|
|
25004
24897
|
currency: options.currency,
|
|
25005
|
-
projectIds: options.project ===
|
|
24898
|
+
projectIds: options.project === null ? [] : options.project?.map(readProjectRef),
|
|
25006
24899
|
billable: options.billable
|
|
25007
24900
|
});
|
|
25008
|
-
console.log(`Updated ${
|
|
24901
|
+
console.log(`Updated ${name}.`);
|
|
25009
24902
|
}
|
|
25010
24903
|
});
|
|
25011
24904
|
|
|
@@ -25309,9 +25202,10 @@ var seen = command({
|
|
|
25309
25202
|
},
|
|
25310
25203
|
run: async ({ positionals: { id } }) => {
|
|
25311
25204
|
const client3 = await backendClient();
|
|
25312
|
-
const
|
|
25313
|
-
|
|
25314
|
-
|
|
25205
|
+
const { identifier } = await client3.mutation(api2.inbox.markSeen, {
|
|
25206
|
+
targetId: id === undefined ? undefined : readIssueRef(id)
|
|
25207
|
+
});
|
|
25208
|
+
console.log(identifier ? `${identifier} marked seen.` : "Inbox marked seen.");
|
|
25315
25209
|
}
|
|
25316
25210
|
});
|
|
25317
25211
|
|
|
@@ -25325,10 +25219,10 @@ var inbox = group({
|
|
|
25325
25219
|
});
|
|
25326
25220
|
|
|
25327
25221
|
// src/lib/line-items.ts
|
|
25328
|
-
var LINE_ITEM_SYNTAX = 'description|quantity[ unit]|unit price|currency, e.g. "Development|2.5 hours|10000|
|
|
25222
|
+
var LINE_ITEM_SYNTAX = 'description|quantity[ unit]|unit price[|currency], e.g. "Development|2.5 hours|10000|USD"; currency defaults to EGP';
|
|
25329
25223
|
var parseLineItem = (value) => {
|
|
25330
25224
|
const parts = value.split("|");
|
|
25331
|
-
if (parts.length !== 4)
|
|
25225
|
+
if (parts.length !== 3 && parts.length !== 4)
|
|
25332
25226
|
throw new Error(`Line items read as ${LINE_ITEM_SYNTAX}; got "${value}".`);
|
|
25333
25227
|
const [description = "", quantityPart = "", pricePart = "", currency = ""] = parts;
|
|
25334
25228
|
const [rawQuantity = "", ...unitWords] = quantityPart.trim().split(/\s+/);
|
|
@@ -25341,12 +25235,18 @@ var parseLineItem = (value) => {
|
|
|
25341
25235
|
throw new Error(`Unit prices must be integer minor units (10000 = 100.00), got "${pricePart.trim()}".`);
|
|
25342
25236
|
}
|
|
25343
25237
|
const unit = unitWords.join(" ");
|
|
25344
|
-
return {
|
|
25238
|
+
return {
|
|
25239
|
+
description: description.trim(),
|
|
25240
|
+
quantity,
|
|
25241
|
+
unit: unit || undefined,
|
|
25242
|
+
unitPrice,
|
|
25243
|
+
currency: parseLineCurrency(currency)
|
|
25244
|
+
};
|
|
25345
25245
|
};
|
|
25346
|
-
var QUOTATION_LINE_ITEM_SYNTAX = 'description|quantity[ unit]|unit price|currency[|section], e.g. "Prototype|1|100000|
|
|
25246
|
+
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
25247
|
var parseQuotationLineItem = (value) => {
|
|
25348
25248
|
const parts = value.split("|");
|
|
25349
|
-
if (parts.length
|
|
25249
|
+
if (parts.length < 3 || parts.length > 5) {
|
|
25350
25250
|
throw new Error(`Quotation line items read as ${QUOTATION_LINE_ITEM_SYNTAX}; got "${value}".`);
|
|
25351
25251
|
}
|
|
25352
25252
|
const section = parts.length === 5 ? parts.pop()?.trim() : undefined;
|
|
@@ -25369,11 +25269,9 @@ var create3 = command({
|
|
|
25369
25269
|
},
|
|
25370
25270
|
run: async ({ positionals: { client: ref }, options }) => {
|
|
25371
25271
|
const backend = await backendClient();
|
|
25372
|
-
const client3 = await resolveClient(backend, ref);
|
|
25373
|
-
const project = options.project === undefined ? undefined : await resolveProject(backend, options.project);
|
|
25374
25272
|
const invoiceId = await backend.mutation(api2.invoices.create, {
|
|
25375
|
-
clientId:
|
|
25376
|
-
projectId: project
|
|
25273
|
+
clientId: readClientRef(ref),
|
|
25274
|
+
projectId: options.project === undefined ? undefined : readProjectRef(options.project),
|
|
25377
25275
|
lineItems: options.line?.map(parseLineItem),
|
|
25378
25276
|
notes: options.notes,
|
|
25379
25277
|
dueDate: options.due
|
|
@@ -25391,8 +25289,21 @@ var duplicate = command({
|
|
|
25391
25289
|
},
|
|
25392
25290
|
run: async ({ positionals: { id } }) => {
|
|
25393
25291
|
const backend = await backendClient();
|
|
25394
|
-
|
|
25395
|
-
|
|
25292
|
+
console.log(await backend.mutation(api2.invoices.duplicate, { id: readInvoiceRef(id) }));
|
|
25293
|
+
}
|
|
25294
|
+
});
|
|
25295
|
+
|
|
25296
|
+
// src/commands/invoices/email.ts
|
|
25297
|
+
var email3 = command({
|
|
25298
|
+
name: "email",
|
|
25299
|
+
description: "Email the invoice's link to the client's billing address; repeatable, and never required",
|
|
25300
|
+
positionals: {
|
|
25301
|
+
id: exports_external.string().describe("Invoice id or URL")
|
|
25302
|
+
},
|
|
25303
|
+
run: async ({ positionals: { id } }) => {
|
|
25304
|
+
const backend = await backendClient();
|
|
25305
|
+
const { to, number: number4 } = await backend.mutation(api2.invoices.email, { id: readInvoiceRef(id) });
|
|
25306
|
+
console.log(`Emailed ${number4} to ${to}.`);
|
|
25396
25307
|
}
|
|
25397
25308
|
});
|
|
25398
25309
|
|
|
@@ -25421,6 +25332,9 @@ var get3 = command({
|
|
|
25421
25332
|
console.log(`Due: ${invoice.dueDate}`);
|
|
25422
25333
|
if (invoice.publicUrl)
|
|
25423
25334
|
console.log(`Link: ${invoice.publicUrl}`);
|
|
25335
|
+
if (invoice.status !== "draft") {
|
|
25336
|
+
console.log(`Emailed: ${invoice.lastEmailedAt === undefined ? "never" : localDateString(invoice.lastEmailedAt)}`);
|
|
25337
|
+
}
|
|
25424
25338
|
if (invoice.lineItems.length > 0) {
|
|
25425
25339
|
console.log("");
|
|
25426
25340
|
printTable([
|
|
@@ -25460,9 +25374,7 @@ var issue2 = command({
|
|
|
25460
25374
|
},
|
|
25461
25375
|
run: async ({ positionals: { id } }) => {
|
|
25462
25376
|
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);
|
|
25377
|
+
const issued = await backend.mutation(api2.invoices.issue, { id: readInvoiceRef(id), today: localToday() });
|
|
25466
25378
|
console.log(`Issued ${issued.number}, due ${issued.dueDate}.`);
|
|
25467
25379
|
}
|
|
25468
25380
|
});
|
|
@@ -25477,10 +25389,11 @@ var link = command({
|
|
|
25477
25389
|
},
|
|
25478
25390
|
run: async ({ positionals: { id, project: projectRef } }) => {
|
|
25479
25391
|
const backend = await backendClient();
|
|
25480
|
-
const
|
|
25481
|
-
|
|
25482
|
-
|
|
25483
|
-
|
|
25392
|
+
const { projectName } = await backend.mutation(api2.invoices.setProject, {
|
|
25393
|
+
id: readInvoiceRef(id),
|
|
25394
|
+
projectId: projectRef === undefined ? undefined : readProjectRef(projectRef)
|
|
25395
|
+
});
|
|
25396
|
+
console.log(projectName === undefined ? "Cleared the project link." : `Linked to ${projectName}.`);
|
|
25484
25397
|
}
|
|
25485
25398
|
});
|
|
25486
25399
|
|
|
@@ -25508,7 +25421,7 @@ var list4 = command({
|
|
|
25508
25421
|
if (result.page.length === 0)
|
|
25509
25422
|
return console.log("No invoices yet.");
|
|
25510
25423
|
printTable([
|
|
25511
|
-
["NUMBER", "CLIENT", "PROJECT", "STATUS", "TOTAL", "DUE", "ID"],
|
|
25424
|
+
["NUMBER", "CLIENT", "PROJECT", "STATUS", "TOTAL", "DUE", "EMAILED", "ID"],
|
|
25512
25425
|
...result.page.map((invoice) => [
|
|
25513
25426
|
invoice.number ?? "draft",
|
|
25514
25427
|
invoice.clientName,
|
|
@@ -25516,6 +25429,7 @@ var list4 = command({
|
|
|
25516
25429
|
invoice.derivedStatus,
|
|
25517
25430
|
formatPerCurrency(invoice.totals.map(({ currency, total }) => ({ currency, amount: total }))),
|
|
25518
25431
|
invoice.dueDate ?? "-",
|
|
25432
|
+
invoice.lastEmailedAt === undefined ? "-" : localDateString(invoice.lastEmailedAt),
|
|
25519
25433
|
invoice._id
|
|
25520
25434
|
])
|
|
25521
25435
|
]);
|
|
@@ -25540,18 +25454,16 @@ var add2 = command({
|
|
|
25540
25454
|
},
|
|
25541
25455
|
run: async ({ positionals: { invoice: ref }, options }) => {
|
|
25542
25456
|
const backend = await backendClient();
|
|
25543
|
-
const
|
|
25544
|
-
|
|
25545
|
-
invoiceId: invoice._id,
|
|
25457
|
+
const payment = await backend.mutation(api2.invoices.addPayment, {
|
|
25458
|
+
invoiceId: readInvoiceRef(ref),
|
|
25546
25459
|
amount: options.amount,
|
|
25547
25460
|
currency: options.currency,
|
|
25548
25461
|
paidOn: options.date ?? localToday(),
|
|
25549
25462
|
note: options.note
|
|
25550
25463
|
});
|
|
25551
|
-
console.log(
|
|
25552
|
-
if (
|
|
25553
|
-
console.log(`${
|
|
25554
|
-
}
|
|
25464
|
+
console.log(payment.id);
|
|
25465
|
+
if (payment.paid)
|
|
25466
|
+
console.log(`${payment.number} is now fully paid.`);
|
|
25555
25467
|
}
|
|
25556
25468
|
});
|
|
25557
25469
|
|
|
@@ -25592,8 +25504,7 @@ var remove3 = command({
|
|
|
25592
25504
|
},
|
|
25593
25505
|
run: async ({ positionals: { id } }) => {
|
|
25594
25506
|
const backend = await backendClient();
|
|
25595
|
-
|
|
25596
|
-
await backend.mutation(api2.invoices.removePayment, { id: payment._id });
|
|
25507
|
+
await backend.mutation(api2.invoices.removePayment, { id: id.trim() });
|
|
25597
25508
|
console.log("Removed the payment.");
|
|
25598
25509
|
}
|
|
25599
25510
|
});
|
|
@@ -25617,9 +25528,8 @@ var rotateLink = command({
|
|
|
25617
25528
|
},
|
|
25618
25529
|
run: async ({ positionals: { id } }) => {
|
|
25619
25530
|
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.`);
|
|
25531
|
+
const { url: url2, number: number4 } = await backend.mutation(api2.invoices.rotateLink, { id: readInvoiceRef(id) });
|
|
25532
|
+
console.log(`${number4} now reads at ${url2}. Send it again \u2014 the previous link is dead.`);
|
|
25623
25533
|
}
|
|
25624
25534
|
});
|
|
25625
25535
|
|
|
@@ -25641,14 +25551,13 @@ var update4 = command({
|
|
|
25641
25551
|
throw new Error("Nothing to update. Pass --line, --notes, or --due (or a --no form to clear).");
|
|
25642
25552
|
}
|
|
25643
25553
|
const backend = await backendClient();
|
|
25644
|
-
const
|
|
25645
|
-
|
|
25646
|
-
id: invoice._id,
|
|
25554
|
+
const { number: number4 } = await backend.mutation(api2.invoices.update, {
|
|
25555
|
+
id: readInvoiceRef(id),
|
|
25647
25556
|
lineItems: options.line?.map(parseLineItem),
|
|
25648
25557
|
notes: options.notes === null ? "" : options.notes,
|
|
25649
25558
|
dueDate: options.due === null ? "" : options.due
|
|
25650
25559
|
});
|
|
25651
|
-
console.log(`Updated ${
|
|
25560
|
+
console.log(`Updated ${number4 ?? "draft"}.`);
|
|
25652
25561
|
}
|
|
25653
25562
|
});
|
|
25654
25563
|
|
|
@@ -25661,9 +25570,8 @@ var voidInvoice = command({
|
|
|
25661
25570
|
},
|
|
25662
25571
|
run: async ({ positionals: { id } }) => {
|
|
25663
25572
|
const backend = await backendClient();
|
|
25664
|
-
const
|
|
25665
|
-
|
|
25666
|
-
console.log(`Voided ${invoice.number}.`);
|
|
25573
|
+
const { number: number4 } = await backend.mutation(api2.invoices.voidInvoice, { id: readInvoiceRef(id) });
|
|
25574
|
+
console.log(`Voided ${number4}.`);
|
|
25667
25575
|
}
|
|
25668
25576
|
});
|
|
25669
25577
|
|
|
@@ -25674,7 +25582,7 @@ var invoices = group({
|
|
|
25674
25582
|
longDescription: `An invoice is a draft until issued: issuing assigns its gapless number, freezes the
|
|
25675
25583
|
document, and snapshots the client's billing details. Sent invoices are immutable \u2014
|
|
25676
25584
|
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]
|
|
25585
|
+
commands: [create3, list4, get3, update4, issue2, email3, voidInvoice, duplicate, link, rotateLink, payments]
|
|
25678
25586
|
});
|
|
25679
25587
|
|
|
25680
25588
|
// src/lib/attachments.ts
|
|
@@ -25743,10 +25651,15 @@ var comment = command({
|
|
|
25743
25651
|
},
|
|
25744
25652
|
run: async ({ positionals: { id, body }, options: { attach } }) => {
|
|
25745
25653
|
const client3 = await backendClient();
|
|
25746
|
-
|
|
25747
|
-
|
|
25748
|
-
|
|
25749
|
-
|
|
25654
|
+
if (attach !== undefined && attach.length > 0) {
|
|
25655
|
+
const issue3 = await resolveIssue(client3, id);
|
|
25656
|
+
printAttachments(await attachFiles(client3, issue3._id, attach));
|
|
25657
|
+
}
|
|
25658
|
+
const { identifier } = await client3.mutation(api2.issueComments.create, {
|
|
25659
|
+
issueId: readIssueRef(id),
|
|
25660
|
+
bodyMarkdown: await readTextOption(body)
|
|
25661
|
+
});
|
|
25662
|
+
console.log(`Commented on ${identifier}.`);
|
|
25750
25663
|
}
|
|
25751
25664
|
});
|
|
25752
25665
|
|
|
@@ -25766,7 +25679,6 @@ var agentConfigurationLine = (id) => {
|
|
|
25766
25679
|
var create4 = command({
|
|
25767
25680
|
name: "create",
|
|
25768
25681
|
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
25682
|
positionals: {
|
|
25771
25683
|
title: exports_external.string().describe("Issue title")
|
|
25772
25684
|
},
|
|
@@ -25781,17 +25693,22 @@ var create4 = command({
|
|
|
25781
25693
|
milestone: exports_external.string().optional().describe("Milestone name (needs --project or --here)"),
|
|
25782
25694
|
parent: exports_external.string().optional().describe("Create as a sub-issue of this issue (identifier, number, or URL)"),
|
|
25783
25695
|
disposition: exports_external.enum(ISSUE_CREATE_DISPOSITIONS).optional().describe("Route immediately (default needs_triage)"),
|
|
25696
|
+
label: exports_external.array(exports_external.string()).optional().describe("Add a label by name (repeatable)"),
|
|
25784
25697
|
"agent-config": exports_external.enum(AGENT_CONFIGURATION_IDS).optional().describe(`Agent configuration${agentConfigurationLegacyNote()}`)
|
|
25785
25698
|
},
|
|
25786
25699
|
run: async ({ positionals: { title }, options }) => {
|
|
25787
25700
|
if (options.project && options.here)
|
|
25788
25701
|
throw new Error("Pass --project or --here, not both.");
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25702
|
+
let projectRef = options.project === undefined ? undefined : readProjectRef(options.project);
|
|
25703
|
+
if (options.here) {
|
|
25704
|
+
const repo = await currentRepoKey();
|
|
25705
|
+
if (!repo)
|
|
25706
|
+
throw new Error("No repository here: not a git checkout with an origin remote.");
|
|
25707
|
+
projectRef = repo;
|
|
25708
|
+
}
|
|
25709
|
+
if (options.milestone && projectRef === undefined)
|
|
25792
25710
|
throw new Error("A milestone needs --project or --here.");
|
|
25793
|
-
const
|
|
25794
|
-
const parent = options.parent ? await resolveIssue(client3, options.parent) : undefined;
|
|
25711
|
+
const client3 = await backendClient();
|
|
25795
25712
|
const { identifier } = await client3.mutation(api2.issues.create, {
|
|
25796
25713
|
title,
|
|
25797
25714
|
descriptionMarkdown: options.description === undefined ? undefined : await readTextOption(options.description),
|
|
@@ -25799,11 +25716,12 @@ var create4 = command({
|
|
|
25799
25716
|
priority: options.priority,
|
|
25800
25717
|
estimate: options.estimate,
|
|
25801
25718
|
dueDate: options.due,
|
|
25802
|
-
projectId:
|
|
25803
|
-
milestoneId: milestone
|
|
25804
|
-
parentId: parent
|
|
25719
|
+
projectId: projectRef,
|
|
25720
|
+
milestoneId: options.milestone,
|
|
25721
|
+
parentId: options.parent === undefined ? undefined : readIssueRef(options.parent),
|
|
25805
25722
|
disposition: options.disposition,
|
|
25806
|
-
agentConfigurationId: options["agent-config"]
|
|
25723
|
+
agentConfigurationId: options["agent-config"],
|
|
25724
|
+
labels: options.label
|
|
25807
25725
|
});
|
|
25808
25726
|
console.log(identifier);
|
|
25809
25727
|
}
|
|
@@ -25821,86 +25739,100 @@ var printTasks = (tasks) => {
|
|
|
25821
25739
|
};
|
|
25822
25740
|
|
|
25823
25741
|
// src/commands/issues/get.ts
|
|
25742
|
+
var printIssue = ({ relations, attachments, tasks, feed, milestone, ...issue3 }) => {
|
|
25743
|
+
const project = issue3.project;
|
|
25744
|
+
console.log(`${issue3.identifier} ${issue3.title}`);
|
|
25745
|
+
console.log(`Status: ${issue3.status} \xB7 Priority: ${issue3.priority} \xB7 Disposition: ${issue3.disposition}`);
|
|
25746
|
+
if (issue3.estimate !== undefined)
|
|
25747
|
+
console.log(`Estimate: ${issue3.estimate}`);
|
|
25748
|
+
if (issue3.dueDate)
|
|
25749
|
+
console.log(`Due: ${issue3.dueDate}`);
|
|
25750
|
+
if (issue3.agentConfigurationId)
|
|
25751
|
+
console.log(`Agent: ${agentConfigurationLine(issue3.agentConfigurationId)}`);
|
|
25752
|
+
if (issue3.branch)
|
|
25753
|
+
console.log(`Branch: ${issue3.branch}`);
|
|
25754
|
+
if (issue3.prUrl)
|
|
25755
|
+
console.log(`PR: ${issue3.prUrl}`);
|
|
25756
|
+
if (project)
|
|
25757
|
+
console.log(`Project: ${project.name}${milestone ? ` \xB7 Milestone: ${milestone.name}` : ""}`);
|
|
25758
|
+
if (issue3.parent)
|
|
25759
|
+
console.log(`Parent: ${issue3.parent.identifier} ${issue3.parent.title}`);
|
|
25760
|
+
if (issue3.labels.length > 0)
|
|
25761
|
+
console.log(`Labels: ${issue3.labels.map((label) => label.name).join(", ")}`);
|
|
25762
|
+
if (issue3.children.total > 0)
|
|
25763
|
+
console.log(`Sub-issues: ${issue3.children.done}/${issue3.children.total} done`);
|
|
25764
|
+
const related = (entries) => entries.map((entry) => `${entry.issue.identifier} (${entry.issue.status})`).join(", ");
|
|
25765
|
+
if (relations.blockedBy.length > 0)
|
|
25766
|
+
console.log(`Blocked by: ${related(relations.blockedBy)}`);
|
|
25767
|
+
if (relations.blocks.length > 0)
|
|
25768
|
+
console.log(`Blocks: ${related(relations.blocks)}`);
|
|
25769
|
+
if (relations.duplicateOf)
|
|
25770
|
+
console.log(`Duplicate of: ${related([relations.duplicateOf])}`);
|
|
25771
|
+
if (relations.duplicates.length > 0)
|
|
25772
|
+
console.log(`Duplicated by: ${related(relations.duplicates)}`);
|
|
25773
|
+
if (relations.relatesTo.length > 0)
|
|
25774
|
+
console.log(`Related: ${related(relations.relatesTo)}`);
|
|
25775
|
+
if (attachments.length > 0) {
|
|
25776
|
+
console.log("Attachments:");
|
|
25777
|
+
for (const attachment of attachments) {
|
|
25778
|
+
console.log(` ${attachment.name} \u2014 ${attachment.url ?? "unavailable"}`);
|
|
25779
|
+
}
|
|
25780
|
+
}
|
|
25781
|
+
printTasks(tasks);
|
|
25782
|
+
if (issue3.descriptionMarkdown)
|
|
25783
|
+
console.log(`
|
|
25784
|
+
${issue3.descriptionMarkdown}`);
|
|
25785
|
+
if (feed.events.length > 0)
|
|
25786
|
+
console.log(`
|
|
25787
|
+
Feed:`);
|
|
25788
|
+
if (feed.truncated)
|
|
25789
|
+
console.log("(older events truncated)");
|
|
25790
|
+
for (const event of feed.events) {
|
|
25791
|
+
const at = new Date(event.at).toISOString().slice(0, 16).replace("T", " ");
|
|
25792
|
+
if (event.type === "activity") {
|
|
25793
|
+
const via = event.via === undefined ? "" : ` \u2014 ${actorLabel(event.via, event.agent)}`;
|
|
25794
|
+
console.log(`${at} ${issueActivityLine(event.detail)}${via}`);
|
|
25795
|
+
} else {
|
|
25796
|
+
console.log(`${at} comment${event.editedAt ? " (edited)" : ""}:`);
|
|
25797
|
+
for (const line of event.bodyMarkdown.split(`
|
|
25798
|
+
`))
|
|
25799
|
+
console.log(` ${line}`);
|
|
25800
|
+
}
|
|
25801
|
+
}
|
|
25802
|
+
};
|
|
25824
25803
|
var get4 = command({
|
|
25825
25804
|
name: "get",
|
|
25826
|
-
description: "Show
|
|
25805
|
+
description: "Show one or more issues: properties, description, and feed",
|
|
25827
25806
|
positionals: {
|
|
25828
|
-
|
|
25807
|
+
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
25808
|
},
|
|
25830
25809
|
options: {
|
|
25831
|
-
json: exports_external.boolean().default(false).describe("Print as JSON")
|
|
25810
|
+
json: exports_external.boolean().default(false).describe("Print as JSON (an array when given several issues)")
|
|
25832
25811
|
},
|
|
25833
|
-
run: async ({ positionals: {
|
|
25812
|
+
run: async ({ positionals: { ids }, options: { json: json2 } }) => {
|
|
25834
25813
|
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(`Due: ${issue3.dueDate}`);
|
|
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
|
-
}
|
|
25814
|
+
const found = await client3.query(api2.issues.getMany, { identifiers: ids.map(readIssueRef) });
|
|
25815
|
+
const missing = ids.filter((_, index) => !found[index]);
|
|
25816
|
+
if (missing.length === 1)
|
|
25817
|
+
throw new Error(`No issue matches ${missing[0]}.`);
|
|
25818
|
+
if (missing.length > 1)
|
|
25819
|
+
throw new Error(`No issues match ${missing.join(", ")}.`);
|
|
25820
|
+
const details = found.flatMap((detail) => detail ? [detail] : []);
|
|
25821
|
+
if (json2) {
|
|
25822
|
+
const objects = details.map(({ relations, attachments, tasks, feed, milestone: _, ...issue3 }) => ({
|
|
25823
|
+
...issue3,
|
|
25824
|
+
relations,
|
|
25825
|
+
attachments,
|
|
25826
|
+
tasks,
|
|
25827
|
+
feed
|
|
25828
|
+
}));
|
|
25829
|
+
return console.log(JSON.stringify(details.length === 1 ? objects[0] : objects, null, 2));
|
|
25903
25830
|
}
|
|
25831
|
+
details.forEach((detail, index) => {
|
|
25832
|
+
if (index > 0)
|
|
25833
|
+
console.log("---");
|
|
25834
|
+
printIssue(detail);
|
|
25835
|
+
});
|
|
25904
25836
|
}
|
|
25905
25837
|
});
|
|
25906
25838
|
|
|
@@ -25947,9 +25879,8 @@ var remove4 = command({
|
|
|
25947
25879
|
},
|
|
25948
25880
|
run: async ({ positionals: { label: ref } }) => {
|
|
25949
25881
|
const client3 = await backendClient();
|
|
25950
|
-
const
|
|
25951
|
-
|
|
25952
|
-
console.log(`Deleted ${label.name}.`);
|
|
25882
|
+
const removed = await client3.mutation(api2.issueLabels.remove, { id: ref });
|
|
25883
|
+
console.log(`Deleted ${removed.name}.`);
|
|
25953
25884
|
}
|
|
25954
25885
|
});
|
|
25955
25886
|
|
|
@@ -25968,9 +25899,8 @@ var update5 = command({
|
|
|
25968
25899
|
if (name === undefined && color === undefined)
|
|
25969
25900
|
throw new Error("Nothing to update. Pass --name or --color.");
|
|
25970
25901
|
const client3 = await backendClient();
|
|
25971
|
-
const
|
|
25972
|
-
|
|
25973
|
-
console.log(`Updated ${name ?? label.name}.`);
|
|
25902
|
+
const updated = await client3.mutation(api2.issueLabels.update, { id: ref, name, color });
|
|
25903
|
+
console.log(`Updated ${updated.name}.`);
|
|
25974
25904
|
}
|
|
25975
25905
|
});
|
|
25976
25906
|
|
|
@@ -26069,9 +25999,8 @@ var markAddressed = command({
|
|
|
26069
25999
|
},
|
|
26070
26000
|
run: async ({ positionals: { id }, options: { commit, finding } }) => {
|
|
26071
26001
|
const client3 = await backendClient();
|
|
26072
|
-
const issue3 = await resolveIssue(client3, id);
|
|
26073
26002
|
const result = await client3.action(api2.githubReviewer.markAddressed, {
|
|
26074
|
-
id:
|
|
26003
|
+
id: readIssueRef(id),
|
|
26075
26004
|
commitSha: commit,
|
|
26076
26005
|
findings: finding?.map(parseFindingRef)
|
|
26077
26006
|
});
|
|
@@ -26092,10 +26021,12 @@ var relate = command({
|
|
|
26092
26021
|
},
|
|
26093
26022
|
run: async ({ positionals: { id, kind, other } }) => {
|
|
26094
26023
|
const client3 = await backendClient();
|
|
26095
|
-
const
|
|
26096
|
-
|
|
26097
|
-
|
|
26098
|
-
|
|
26024
|
+
const { identifier, otherIdentifier } = await client3.mutation(api2.issues.addRelation, {
|
|
26025
|
+
id: readIssueRef(id),
|
|
26026
|
+
kind,
|
|
26027
|
+
otherIssueId: readIssueRef(other)
|
|
26028
|
+
});
|
|
26029
|
+
console.log(`${identifier} ${kind.replaceAll("_", " ")} ${otherIdentifier}.`);
|
|
26099
26030
|
}
|
|
26100
26031
|
});
|
|
26101
26032
|
|
|
@@ -26108,9 +26039,8 @@ var remove5 = command({
|
|
|
26108
26039
|
},
|
|
26109
26040
|
run: async ({ positionals: { id } }) => {
|
|
26110
26041
|
const client3 = await backendClient();
|
|
26111
|
-
const
|
|
26112
|
-
|
|
26113
|
-
console.log(`Deleted ${issue3.identifier}.`);
|
|
26042
|
+
const { identifier } = await client3.mutation(api2.issues.remove, { id: readIssueRef(id) });
|
|
26043
|
+
console.log(`Deleted ${identifier}.`);
|
|
26114
26044
|
}
|
|
26115
26045
|
});
|
|
26116
26046
|
|
|
@@ -26118,8 +26048,7 @@ var remove5 = command({
|
|
|
26118
26048
|
var route = command({
|
|
26119
26049
|
name: "route",
|
|
26120
26050
|
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).`,
|
|
26051
|
+
longDescription: "The disposition says who acts next; needs_triage is the default.",
|
|
26123
26052
|
positionals: {
|
|
26124
26053
|
id: exports_external.string().describe("Issue identifier, number, or URL"),
|
|
26125
26054
|
disposition: exports_external.enum(ISSUE_DISPOSITIONS).describe("Where the issue goes next")
|
|
@@ -26129,13 +26058,12 @@ ready_for_agent requires the issue to carry an agent configuration (set --agent-
|
|
|
26129
26058
|
},
|
|
26130
26059
|
run: async ({ positionals: { id, disposition }, options: { comment: comment2 } }) => {
|
|
26131
26060
|
const client3 = await backendClient();
|
|
26132
|
-
const
|
|
26133
|
-
|
|
26134
|
-
id: issue3._id,
|
|
26061
|
+
const { identifier } = await client3.mutation(api2.issues.route, {
|
|
26062
|
+
id: readIssueRef(id),
|
|
26135
26063
|
disposition,
|
|
26136
26064
|
comment: comment2 === undefined ? undefined : await readTextOption(comment2)
|
|
26137
26065
|
});
|
|
26138
|
-
console.log(`Routed ${
|
|
26066
|
+
console.log(`Routed ${identifier} to ${disposition}.`);
|
|
26139
26067
|
}
|
|
26140
26068
|
});
|
|
26141
26069
|
|
|
@@ -26144,8 +26072,7 @@ var set2 = command({
|
|
|
26144
26072
|
name: "set",
|
|
26145
26073
|
description: "Set an issue's status, priority, estimate, due date, project, milestone, parent, agent configuration, or labels",
|
|
26146
26074
|
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.`,
|
|
26075
|
+
--status is the correction path.`,
|
|
26149
26076
|
positionals: {
|
|
26150
26077
|
id: exports_external.string().describe("Issue identifier, number, or URL")
|
|
26151
26078
|
},
|
|
@@ -26177,41 +26104,20 @@ the issue sits in ready_for_agent.`,
|
|
|
26177
26104
|
if (Object.values(options).every((value) => value === undefined))
|
|
26178
26105
|
throw new Error("Nothing to set. Pass --status, --priority, --estimate, --due, --project, --milestone, --parent, --agent-config, or --label.");
|
|
26179
26106
|
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}.`);
|
|
26107
|
+
const { identifier } = await client3.mutation(api2.issues.edit, {
|
|
26108
|
+
issue: readIssueRef(id),
|
|
26109
|
+
...parent !== undefined ? { parent: parent === null ? null : readIssueRef(parent) } : {},
|
|
26110
|
+
...label !== undefined ? { addLabels: label } : {},
|
|
26111
|
+
...noLabel !== undefined ? { removeLabels: noLabel } : {},
|
|
26112
|
+
...agentConfig !== undefined ? { agentConfigurationId: agentConfig } : {},
|
|
26113
|
+
...status3 !== undefined ? { status: status3 } : {},
|
|
26114
|
+
...priority !== undefined ? { priority } : {},
|
|
26115
|
+
...estimate !== undefined ? { estimate } : {},
|
|
26116
|
+
...due !== undefined ? { dueDate: due } : {},
|
|
26117
|
+
...project !== undefined ? { project: project === null ? null : readProjectRef(project) } : {},
|
|
26118
|
+
...milestone !== undefined ? { milestone } : {}
|
|
26119
|
+
});
|
|
26120
|
+
console.log(`Updated ${identifier}.`);
|
|
26215
26121
|
}
|
|
26216
26122
|
});
|
|
26217
26123
|
|
|
@@ -26229,29 +26135,18 @@ ready_for_agent is informational, and --ready belongs only to an explicit hand-o
|
|
|
26229
26135
|
ready: exports_external.boolean().default(false).describe("Also route the issue to ready_for_agent, assigning the running agent when it is identifiable")
|
|
26230
26136
|
},
|
|
26231
26137
|
run: async ({ positionals: { id }, options: { ready } }) => {
|
|
26138
|
+
const actor = await agentClaim();
|
|
26139
|
+
const configurationId = actor && agentConfigurationForActor(actor);
|
|
26232
26140
|
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}`);
|
|
26141
|
+
const { identifier, branch, disposition, assigned } = await client3.mutation(api2.issues.start, {
|
|
26142
|
+
issue: readIssueRef(id),
|
|
26143
|
+
...ready ? { ready: { agentConfigurationId: configurationId || undefined } } : {}
|
|
26144
|
+
});
|
|
26145
|
+
console.log(`Started ${identifier}: in_progress, branch ${branch}`);
|
|
26251
26146
|
if (ready) {
|
|
26252
26147
|
console.log(`Routed to ready_for_agent${assigned ? `, assigned ${agentConfiguration(assigned).label}` : ""}.`);
|
|
26253
|
-
} else if (
|
|
26254
|
-
console.log(`Note: disposition is ${
|
|
26148
|
+
} else if (disposition !== "ready_for_agent" && actor) {
|
|
26149
|
+
console.log(`Note: disposition is ${disposition}. Pass --ready if this hand-off was intended.`);
|
|
26255
26150
|
}
|
|
26256
26151
|
console.log(`
|
|
26257
26152
|
git switch -c ${shellArg(branch)}`);
|
|
@@ -26267,8 +26162,7 @@ var startReview = command({
|
|
|
26267
26162
|
},
|
|
26268
26163
|
run: async ({ positionals: { id } }) => {
|
|
26269
26164
|
const client3 = await backendClient();
|
|
26270
|
-
const
|
|
26271
|
-
const started = await client3.action(api2.githubReviewer.startReview, { id: issue3._id });
|
|
26165
|
+
const started = await client3.action(api2.githubReviewer.startReview, { id: readIssueRef(id) });
|
|
26272
26166
|
console.log(`Reviewing ${started.identifier}: in_review`);
|
|
26273
26167
|
console.log(`Announced on ${started.prUrl}; check run open.`);
|
|
26274
26168
|
}
|
|
@@ -26283,9 +26177,8 @@ var submit = command({
|
|
|
26283
26177
|
},
|
|
26284
26178
|
run: async ({ positionals: { id } }) => {
|
|
26285
26179
|
const client3 = await backendClient();
|
|
26286
|
-
const
|
|
26287
|
-
|
|
26288
|
-
console.log(`Submitted ${issue3.identifier}: ready_for_review`);
|
|
26180
|
+
const { identifier } = await client3.mutation(api2.issues.submit, { id: readIssueRef(id) });
|
|
26181
|
+
console.log(`Submitted ${identifier}: ready_for_review`);
|
|
26289
26182
|
}
|
|
26290
26183
|
});
|
|
26291
26184
|
|
|
@@ -26349,8 +26242,7 @@ Categories: ${REVIEW_FINDING_CATEGORIES.join(", ")}; severities: ${REVIEW_FINDIN
|
|
|
26349
26242
|
verdict = { kind: "request_changes", findings: await readFindings(options.findings) };
|
|
26350
26243
|
}
|
|
26351
26244
|
const client3 = await backendClient();
|
|
26352
|
-
const
|
|
26353
|
-
const submitted = await client3.action(api2.githubReviewer.submitReview, { id: issue3._id, verdict });
|
|
26245
|
+
const submitted = await client3.action(api2.githubReviewer.submitReview, { id: readIssueRef(id), verdict });
|
|
26354
26246
|
console.log(`Submitted review on ${submitted.identifier}: ${submitted.prUrl}`);
|
|
26355
26247
|
console.log(submitted.summary);
|
|
26356
26248
|
}
|
|
@@ -26377,57 +26269,38 @@ address several rows even as removals renumber it.`,
|
|
|
26377
26269
|
},
|
|
26378
26270
|
run: async ({ positionals: { id }, options: { add: add3, check: check2, uncheck, convert, remove: remove6, json: json2 } }) => {
|
|
26379
26271
|
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 });
|
|
26272
|
+
const wrote = [add3, check2, uncheck, convert, remove6].some((values) => values !== undefined);
|
|
26273
|
+
if (wrote) {
|
|
26274
|
+
const { identifier, converted, tasks: tasks3 } = await client3.mutation(api2.issueTasks.edit, {
|
|
26275
|
+
issue: readIssueRef(id),
|
|
26276
|
+
...add3 !== undefined ? { add: add3 } : {},
|
|
26277
|
+
...check2 !== undefined ? { check: check2 } : {},
|
|
26278
|
+
...uncheck !== undefined ? { uncheck } : {},
|
|
26279
|
+
...convert !== undefined ? { convert } : {},
|
|
26280
|
+
...remove6 !== undefined ? { remove: remove6 } : {}
|
|
26281
|
+
});
|
|
26400
26282
|
if (!json2) {
|
|
26401
|
-
|
|
26283
|
+
for (const created of converted) {
|
|
26284
|
+
console.log(`${created.identifier} ${created.nested ? "created as a sub-issue" : "created and related"}: ${created.title}`);
|
|
26285
|
+
}
|
|
26402
26286
|
}
|
|
26287
|
+
if (json2)
|
|
26288
|
+
return console.log(JSON.stringify(tasks3, null, 2));
|
|
26289
|
+
if (tasks3.length === 0)
|
|
26290
|
+
return console.log(`${identifier} has no tasks.`);
|
|
26291
|
+
return printTasks(tasks3);
|
|
26403
26292
|
}
|
|
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;
|
|
26293
|
+
const issue3 = await resolveIssue(client3, id);
|
|
26294
|
+
const tasks2 = await client3.query(api2.issueTasks.list, { issueId: issue3._id });
|
|
26408
26295
|
if (json2)
|
|
26409
|
-
return console.log(JSON.stringify(
|
|
26410
|
-
if (
|
|
26296
|
+
return console.log(JSON.stringify(tasks2, null, 2));
|
|
26297
|
+
if (tasks2.length === 0)
|
|
26411
26298
|
return console.log(`${issue3.identifier} has no tasks.`);
|
|
26412
|
-
printTasks(
|
|
26299
|
+
printTasks(tasks2);
|
|
26413
26300
|
}
|
|
26414
26301
|
});
|
|
26415
26302
|
|
|
26416
26303
|
// 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
26304
|
var unrelate = command({
|
|
26432
26305
|
name: "unrelate",
|
|
26433
26306
|
description: "Remove a relation between two issues (a canceled duplicate stays canceled)",
|
|
@@ -26438,15 +26311,12 @@ var unrelate = command({
|
|
|
26438
26311
|
},
|
|
26439
26312
|
run: async ({ positionals: { id, kind, other } }) => {
|
|
26440
26313
|
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}.`);
|
|
26314
|
+
const { identifier, otherIdentifier } = await client3.mutation(api2.issues.removeRelation, {
|
|
26315
|
+
id: readIssueRef(id),
|
|
26316
|
+
kind,
|
|
26317
|
+
otherIssueId: readIssueRef(other)
|
|
26318
|
+
});
|
|
26319
|
+
console.log(`Removed: ${identifier} ${kind.replaceAll("_", " ")} ${otherIdentifier}.`);
|
|
26450
26320
|
}
|
|
26451
26321
|
});
|
|
26452
26322
|
|
|
@@ -26466,16 +26336,18 @@ var update6 = command({
|
|
|
26466
26336
|
if (title === undefined && description === undefined && attach === undefined)
|
|
26467
26337
|
throw new Error("Nothing to update. Pass --title, --description, or --attach.");
|
|
26468
26338
|
const client3 = await backendClient();
|
|
26469
|
-
|
|
26470
|
-
|
|
26471
|
-
|
|
26472
|
-
await client3.
|
|
26473
|
-
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26478
|
-
|
|
26339
|
+
let attached;
|
|
26340
|
+
if (attach !== undefined && attach.length > 0) {
|
|
26341
|
+
const issue3 = await resolveIssue(client3, id);
|
|
26342
|
+
printAttachments(await attachFiles(client3, issue3._id, attach));
|
|
26343
|
+
attached = issue3.identifier;
|
|
26344
|
+
}
|
|
26345
|
+
const identifier = title !== undefined || description !== undefined ? (await client3.mutation(api2.issues.update, {
|
|
26346
|
+
id: readIssueRef(id),
|
|
26347
|
+
title,
|
|
26348
|
+
descriptionMarkdown: description === undefined ? undefined : await readTextOption(description)
|
|
26349
|
+
})).identifier : attached;
|
|
26350
|
+
console.log(`Updated ${identifier}.`);
|
|
26479
26351
|
}
|
|
26480
26352
|
});
|
|
26481
26353
|
|
|
@@ -26764,7 +26636,7 @@ var create7 = command({
|
|
|
26764
26636
|
health: options.health,
|
|
26765
26637
|
targetDate: options.target,
|
|
26766
26638
|
repo: options.repo,
|
|
26767
|
-
clientId: options.client === undefined ? undefined : (
|
|
26639
|
+
clientId: options.client === undefined ? undefined : readClientRef(options.client)
|
|
26768
26640
|
});
|
|
26769
26641
|
console.log(projectId);
|
|
26770
26642
|
}
|
|
@@ -26866,9 +26738,8 @@ var remove7 = command({
|
|
|
26866
26738
|
},
|
|
26867
26739
|
run: async ({ positionals: { id } }) => {
|
|
26868
26740
|
const client3 = await backendClient();
|
|
26869
|
-
const
|
|
26870
|
-
|
|
26871
|
-
console.log(`Deleted ${project2.name}.`);
|
|
26741
|
+
const removed = await client3.mutation(api2.projects.remove, { id: readProjectRef(id) });
|
|
26742
|
+
console.log(`Deleted ${removed.name}.`);
|
|
26872
26743
|
}
|
|
26873
26744
|
});
|
|
26874
26745
|
|
|
@@ -26891,21 +26762,15 @@ var set3 = command({
|
|
|
26891
26762
|
if (Object.values(options).every((value) => value === undefined))
|
|
26892
26763
|
throw new Error("Nothing to set. Pass --status, --health, --target, --repo, or --client.");
|
|
26893
26764
|
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}.`);
|
|
26765
|
+
const { name } = await backend.mutation(api2.projects.edit, {
|
|
26766
|
+
id: readProjectRef(id),
|
|
26767
|
+
...repo !== undefined ? { repo } : {},
|
|
26768
|
+
...client3 !== undefined ? { client: client3 === null ? null : readClientRef(client3) } : {},
|
|
26769
|
+
...status3 !== undefined ? { status: status3 } : {},
|
|
26770
|
+
...health !== undefined ? { health } : {},
|
|
26771
|
+
...target !== undefined ? { targetDate: target } : {}
|
|
26772
|
+
});
|
|
26773
|
+
console.log(`Updated ${name}.`);
|
|
26909
26774
|
}
|
|
26910
26775
|
});
|
|
26911
26776
|
|
|
@@ -26925,14 +26790,13 @@ var update8 = command({
|
|
|
26925
26790
|
if (name === undefined && summary === undefined && description === undefined)
|
|
26926
26791
|
throw new Error("Nothing to update. Pass --name, --summary, or --description.");
|
|
26927
26792
|
const client3 = await backendClient();
|
|
26928
|
-
const
|
|
26929
|
-
|
|
26930
|
-
id: project2._id,
|
|
26793
|
+
const updated = await client3.mutation(api2.projects.update, {
|
|
26794
|
+
id: readProjectRef(id),
|
|
26931
26795
|
name,
|
|
26932
26796
|
summary,
|
|
26933
26797
|
descriptionMarkdown: description === undefined ? undefined : await readTextOption(description)
|
|
26934
26798
|
});
|
|
26935
|
-
console.log(`Updated ${
|
|
26799
|
+
console.log(`Updated ${updated.name}.`);
|
|
26936
26800
|
}
|
|
26937
26801
|
});
|
|
26938
26802
|
|
|
@@ -26958,13 +26822,12 @@ var accept = command({
|
|
|
26958
26822
|
},
|
|
26959
26823
|
run: async ({ positionals: { id }, options }) => {
|
|
26960
26824
|
const backend = await backendClient();
|
|
26961
|
-
const
|
|
26962
|
-
|
|
26963
|
-
id: quotation._id,
|
|
26825
|
+
const accepted = await backend.mutation(api2.quotations.accept, {
|
|
26826
|
+
id: readQuotationRef(id),
|
|
26964
26827
|
on: options.date ?? localToday(),
|
|
26965
26828
|
note: options.note
|
|
26966
26829
|
});
|
|
26967
|
-
console.log(`Accepted ${
|
|
26830
|
+
console.log(`Accepted ${accepted.number}. Draw invoices from it with: kds quotations convert ${accepted.id}`);
|
|
26968
26831
|
}
|
|
26969
26832
|
});
|
|
26970
26833
|
|
|
@@ -26985,7 +26848,6 @@ Nothing blocks over- or under-invoicing; left to bill is advisory.`,
|
|
|
26985
26848
|
},
|
|
26986
26849
|
run: async ({ positionals: { id }, options }) => {
|
|
26987
26850
|
const backend = await backendClient();
|
|
26988
|
-
const quotation = await resolveQuotation(backend, id);
|
|
26989
26851
|
const lineIndexes = options.lines?.split(",").map((part) => {
|
|
26990
26852
|
const lineNumber = Number(part.trim());
|
|
26991
26853
|
if (!Number.isInteger(lineNumber) || lineNumber < 1) {
|
|
@@ -26994,7 +26856,7 @@ Nothing blocks over- or under-invoicing; left to bill is advisory.`,
|
|
|
26994
26856
|
return lineNumber - 1;
|
|
26995
26857
|
});
|
|
26996
26858
|
const invoiceId = await backend.mutation(api2.quotations.convert, {
|
|
26997
|
-
id:
|
|
26859
|
+
id: readQuotationRef(id),
|
|
26998
26860
|
lineIndexes,
|
|
26999
26861
|
percentage: options.percentage
|
|
27000
26862
|
});
|
|
@@ -27017,11 +26879,9 @@ var create8 = command({
|
|
|
27017
26879
|
},
|
|
27018
26880
|
run: async ({ positionals: { client: ref }, options }) => {
|
|
27019
26881
|
const backend = await backendClient();
|
|
27020
|
-
const client3 = await resolveClient(backend, ref);
|
|
27021
|
-
const project2 = options.project === undefined ? undefined : await resolveProject(backend, options.project);
|
|
27022
26882
|
const quotationId = await backend.mutation(api2.quotations.create, {
|
|
27023
|
-
clientId:
|
|
27024
|
-
projectId:
|
|
26883
|
+
clientId: readClientRef(ref),
|
|
26884
|
+
projectId: options.project === undefined ? undefined : readProjectRef(options.project),
|
|
27025
26885
|
lineItems: options.line?.map(parseQuotationLineItem),
|
|
27026
26886
|
notes: options.notes,
|
|
27027
26887
|
validUntil: options.validUntil === null ? "" : options.validUntil,
|
|
@@ -27040,8 +26900,21 @@ var duplicate2 = command({
|
|
|
27040
26900
|
},
|
|
27041
26901
|
run: async ({ positionals: { id } }) => {
|
|
27042
26902
|
const backend = await backendClient();
|
|
27043
|
-
|
|
27044
|
-
|
|
26903
|
+
console.log(await backend.mutation(api2.quotations.duplicate, { id: readQuotationRef(id), today: localToday() }));
|
|
26904
|
+
}
|
|
26905
|
+
});
|
|
26906
|
+
|
|
26907
|
+
// src/commands/quotations/email.ts
|
|
26908
|
+
var email4 = command({
|
|
26909
|
+
name: "email",
|
|
26910
|
+
description: "Email the quotation's link to the client's billing address; repeatable, and never required",
|
|
26911
|
+
positionals: {
|
|
26912
|
+
id: exports_external.string().describe("Quotation id or URL")
|
|
26913
|
+
},
|
|
26914
|
+
run: async ({ positionals: { id } }) => {
|
|
26915
|
+
const backend = await backendClient();
|
|
26916
|
+
const { to, number: number4 } = await backend.mutation(api2.quotations.email, { id: readQuotationRef(id) });
|
|
26917
|
+
console.log(`Emailed ${number4} to ${to}.`);
|
|
27045
26918
|
}
|
|
27046
26919
|
});
|
|
27047
26920
|
|
|
@@ -27070,6 +26943,9 @@ var get7 = command({
|
|
|
27070
26943
|
console.log(`Valid until: ${quotation.validUntil}`);
|
|
27071
26944
|
if (quotation.publicUrl)
|
|
27072
26945
|
console.log(`Link: ${quotation.publicUrl}`);
|
|
26946
|
+
if (quotation.status !== "draft") {
|
|
26947
|
+
console.log(`Emailed: ${quotation.lastEmailedAt === undefined ? "never" : localDateString(quotation.lastEmailedAt)}`);
|
|
26948
|
+
}
|
|
27073
26949
|
if (quotation.outcome) {
|
|
27074
26950
|
console.log(`${quotation.status === "accepted" ? "Accepted" : "Rejected"}: ${quotation.outcome.on}${quotation.outcome.note ? ` \u2014 ${quotation.outcome.note}` : ""}`);
|
|
27075
26951
|
}
|
|
@@ -27126,9 +27002,7 @@ var issue3 = command({
|
|
|
27126
27002
|
},
|
|
27127
27003
|
run: async ({ positionals: { id } }) => {
|
|
27128
27004
|
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);
|
|
27005
|
+
const issued = await backend.mutation(api2.quotations.issue, { id: readQuotationRef(id), today: localToday() });
|
|
27132
27006
|
console.log(`Issued ${issued.number}, ${issued.validUntil ? `valid until ${issued.validUntil}` : "with no expiry"}.`);
|
|
27133
27007
|
}
|
|
27134
27008
|
});
|
|
@@ -27143,10 +27017,11 @@ var link2 = command({
|
|
|
27143
27017
|
},
|
|
27144
27018
|
run: async ({ positionals: { id, project: projectRef } }) => {
|
|
27145
27019
|
const backend = await backendClient();
|
|
27146
|
-
const
|
|
27147
|
-
|
|
27148
|
-
|
|
27149
|
-
|
|
27020
|
+
const { projectName } = await backend.mutation(api2.quotations.setProject, {
|
|
27021
|
+
id: readQuotationRef(id),
|
|
27022
|
+
projectId: projectRef === undefined ? undefined : readProjectRef(projectRef)
|
|
27023
|
+
});
|
|
27024
|
+
console.log(projectName === undefined ? "Cleared the project link." : `Linked to ${projectName}.`);
|
|
27150
27025
|
}
|
|
27151
27026
|
});
|
|
27152
27027
|
|
|
@@ -27174,7 +27049,7 @@ var list10 = command({
|
|
|
27174
27049
|
if (result.page.length === 0)
|
|
27175
27050
|
return console.log("No quotations yet.");
|
|
27176
27051
|
printTable([
|
|
27177
|
-
["NUMBER", "CLIENT", "PROJECT", "STATUS", "TOTAL", "VALID UNTIL", "ID"],
|
|
27052
|
+
["NUMBER", "CLIENT", "PROJECT", "STATUS", "TOTAL", "VALID UNTIL", "EMAILED", "ID"],
|
|
27178
27053
|
...result.page.map((quotation) => [
|
|
27179
27054
|
quotation.number ?? "draft",
|
|
27180
27055
|
quotation.clientName,
|
|
@@ -27182,6 +27057,7 @@ var list10 = command({
|
|
|
27182
27057
|
quotation.derivedStatus,
|
|
27183
27058
|
formatPerCurrency(quotation.totals.map(({ currency, total }) => ({ currency, amount: total }))),
|
|
27184
27059
|
quotation.validUntil ?? "-",
|
|
27060
|
+
quotation.lastEmailedAt === undefined ? "-" : localDateString(quotation.lastEmailedAt),
|
|
27185
27061
|
quotation._id
|
|
27186
27062
|
])
|
|
27187
27063
|
]);
|
|
@@ -27204,13 +27080,12 @@ var reject2 = command({
|
|
|
27204
27080
|
},
|
|
27205
27081
|
run: async ({ positionals: { id }, options }) => {
|
|
27206
27082
|
const backend = await backendClient();
|
|
27207
|
-
const
|
|
27208
|
-
|
|
27209
|
-
id: quotation._id,
|
|
27083
|
+
const { number: number4 } = await backend.mutation(api2.quotations.reject, {
|
|
27084
|
+
id: readQuotationRef(id),
|
|
27210
27085
|
on: options.date ?? localToday(),
|
|
27211
27086
|
note: options.note
|
|
27212
27087
|
});
|
|
27213
|
-
console.log(`Rejected ${
|
|
27088
|
+
console.log(`Rejected ${number4}.`);
|
|
27214
27089
|
}
|
|
27215
27090
|
});
|
|
27216
27091
|
|
|
@@ -27223,9 +27098,8 @@ var rotateLink2 = command({
|
|
|
27223
27098
|
},
|
|
27224
27099
|
run: async ({ positionals: { id } }) => {
|
|
27225
27100
|
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.`);
|
|
27101
|
+
const { url: url2, number: number4 } = await backend.mutation(api2.quotations.rotateLink, { id: readQuotationRef(id) });
|
|
27102
|
+
console.log(`${number4} now reads at ${url2}. Send it again \u2014 the previous link is dead.`);
|
|
27229
27103
|
}
|
|
27230
27104
|
});
|
|
27231
27105
|
|
|
@@ -27247,14 +27121,13 @@ var update9 = command({
|
|
|
27247
27121
|
throw new Error("Nothing to update. Pass --line, --notes, or --valid-until (or a --no form to clear).");
|
|
27248
27122
|
}
|
|
27249
27123
|
const backend = await backendClient();
|
|
27250
|
-
const
|
|
27251
|
-
|
|
27252
|
-
id: quotation._id,
|
|
27124
|
+
const { number: number4 } = await backend.mutation(api2.quotations.update, {
|
|
27125
|
+
id: readQuotationRef(id),
|
|
27253
27126
|
lineItems: options.line?.map(parseQuotationLineItem),
|
|
27254
27127
|
notes: options.notes === null ? "" : options.notes,
|
|
27255
27128
|
validUntil: options.validUntil === null ? "" : options.validUntil
|
|
27256
27129
|
});
|
|
27257
|
-
console.log(`Updated ${
|
|
27130
|
+
console.log(`Updated ${number4 ?? "draft"}.`);
|
|
27258
27131
|
}
|
|
27259
27132
|
});
|
|
27260
27133
|
|
|
@@ -27267,9 +27140,8 @@ var voidQuotation = command({
|
|
|
27267
27140
|
},
|
|
27268
27141
|
run: async ({ positionals: { id } }) => {
|
|
27269
27142
|
const backend = await backendClient();
|
|
27270
|
-
const
|
|
27271
|
-
|
|
27272
|
-
console.log(`Voided ${quotation.number}.`);
|
|
27143
|
+
const { number: number4 } = await backend.mutation(api2.quotations.voidQuotation, { id: readQuotationRef(id) });
|
|
27144
|
+
console.log(`Voided ${number4}.`);
|
|
27273
27145
|
}
|
|
27274
27146
|
});
|
|
27275
27147
|
|
|
@@ -27281,7 +27153,21 @@ var quotations = group({
|
|
|
27281
27153
|
document awaiting the client's answer. Accept and reject are manual records;
|
|
27282
27154
|
expiry is derived from valid-until and never blocks accepting. An accepted
|
|
27283
27155
|
quotation converts into any number of draft invoices, each tracing back to it.`,
|
|
27284
|
-
commands: [
|
|
27156
|
+
commands: [
|
|
27157
|
+
create8,
|
|
27158
|
+
list10,
|
|
27159
|
+
get7,
|
|
27160
|
+
update9,
|
|
27161
|
+
issue3,
|
|
27162
|
+
email4,
|
|
27163
|
+
accept,
|
|
27164
|
+
reject2,
|
|
27165
|
+
voidQuotation,
|
|
27166
|
+
duplicate2,
|
|
27167
|
+
convert,
|
|
27168
|
+
link2,
|
|
27169
|
+
rotateLink2
|
|
27170
|
+
]
|
|
27285
27171
|
});
|
|
27286
27172
|
|
|
27287
27173
|
// src/commands/upgrade.ts
|