@robbeverhelst/do 1.334.0 → 1.336.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/dist/do.js +801 -50
- package/package.json +1 -1
package/dist/do.js
CHANGED
|
@@ -14418,7 +14418,7 @@ var configError = (message) => new CliError("config", message, EXIT.CONFIG);
|
|
|
14418
14418
|
|
|
14419
14419
|
// src/meta.ts
|
|
14420
14420
|
var CLI_NAME = "do";
|
|
14421
|
-
var VERSION = "1.
|
|
14421
|
+
var VERSION = "1.336.0";
|
|
14422
14422
|
var API_PREFIX = "/api/v1";
|
|
14423
14423
|
var DEFAULT_API_URL = "http://localhost:3001";
|
|
14424
14424
|
|
|
@@ -22915,6 +22915,12 @@ var moneyAccountKind = pgEnum("money_account_kind", [
|
|
|
22915
22915
|
"pension",
|
|
22916
22916
|
"asset_manual"
|
|
22917
22917
|
]);
|
|
22918
|
+
var moneyPensionSubtype = pgEnum("money_pension_subtype", [
|
|
22919
|
+
"group_insurance",
|
|
22920
|
+
"pension_savings_fund",
|
|
22921
|
+
"pension_savings_insurance",
|
|
22922
|
+
"vapz"
|
|
22923
|
+
]);
|
|
22918
22924
|
var moneyAccountHolder = pgEnum("money_account_holder", ["me", "joint", "other"]);
|
|
22919
22925
|
var moneyAccountEntity = pgEnum("money_account_entity", ["personal", "business"]);
|
|
22920
22926
|
var moneyImportSource = pgEnum("money_import_source", [
|
|
@@ -22966,6 +22972,8 @@ var moneyTaxTag = pgEnum("money_tax_tag", [
|
|
|
22966
22972
|
var moneyAccounts = pgTable("money_accounts", {
|
|
22967
22973
|
...leafColumns(),
|
|
22968
22974
|
kind: moneyAccountKind("kind").notNull().default("current"),
|
|
22975
|
+
pensionSubtype: moneyPensionSubtype("pension_subtype"),
|
|
22976
|
+
lockedUntil: date5("locked_until"),
|
|
22969
22977
|
holder: moneyAccountHolder("holder").notNull().default("me"),
|
|
22970
22978
|
entity: moneyAccountEntity("entity").notNull().default("personal"),
|
|
22971
22979
|
institutionContactId: text("institution_contact_id").references(() => contacts.id, {
|
|
@@ -25202,6 +25210,7 @@ var fileViewLinkSchema = object({
|
|
|
25202
25210
|
|
|
25203
25211
|
// ../../packages/schema/src/contracts/money.ts
|
|
25204
25212
|
var moneyAccountKindSchema = _enum2(moneyAccountKind.enumValues);
|
|
25213
|
+
var moneyPensionSubtypeSchema = _enum2(moneyPensionSubtype.enumValues);
|
|
25205
25214
|
var moneyAccountHolderSchema = _enum2(moneyAccountHolder.enumValues);
|
|
25206
25215
|
var moneyAccountEntitySchema = _enum2(moneyAccountEntity.enumValues);
|
|
25207
25216
|
var moneyImportSourceSchema = _enum2(moneyImportSource.enumValues);
|
|
@@ -25297,6 +25306,8 @@ var createMoneyAccountInputSchema = object({
|
|
|
25297
25306
|
id: ulidSchema,
|
|
25298
25307
|
title: string2().trim().min(1).max(120),
|
|
25299
25308
|
kind: moneyAccountKindSchema.optional(),
|
|
25309
|
+
pensionSubtype: moneyPensionSubtypeSchema.nullish(),
|
|
25310
|
+
lockedUntil: keyDateDaySchema.nullish(),
|
|
25300
25311
|
holder: moneyAccountHolderSchema.optional(),
|
|
25301
25312
|
institutionContactId: ulidSchema.nullish(),
|
|
25302
25313
|
iban: string2().trim().transform(compactIban).pipe(string2().regex(/^[A-Z]{2}\d{2}[A-Z0-9]{8,30}$/, "expected an IBAN")).nullish(),
|
|
@@ -25310,7 +25321,9 @@ var moneyBalanceAtSchema = union([keyDateDaySchema, exports_iso.datetime({ offse
|
|
|
25310
25321
|
var updateMoneyAccountInputSchema = createMoneyAccountInputSchema.omit({ id: true, currency: true }).partial().extend({
|
|
25311
25322
|
balance: moneyAmountSchema.optional(),
|
|
25312
25323
|
balanceAt: moneyBalanceAtSchema.optional()
|
|
25313
|
-
}).strict().refine((u) => u.
|
|
25324
|
+
}).strict().refine((u) => Object.values(u).some((v) => v !== undefined), {
|
|
25325
|
+
message: "nothing to change: give a field to set (or a balance with its balanceAt)"
|
|
25326
|
+
}).refine((u) => u.balance === undefined === (u.balanceAt === undefined), {
|
|
25314
25327
|
message: "a balance and the day it's from go together",
|
|
25315
25328
|
path: ["balanceAt"]
|
|
25316
25329
|
});
|
|
@@ -26558,6 +26571,16 @@ var familyTreeUnionSchema = object({
|
|
|
26558
26571
|
partners: array(familyTreeRefSchema),
|
|
26559
26572
|
children: array(familyTreeRefSchema)
|
|
26560
26573
|
});
|
|
26574
|
+
var FAMILY_TREE_MAX_GENERATIONS = 50;
|
|
26575
|
+
var familyTreeAncestorSchema = object({
|
|
26576
|
+
n: number2().int().min(1),
|
|
26577
|
+
generation: number2().int().min(0),
|
|
26578
|
+
ref: familyTreeRefSchema,
|
|
26579
|
+
repeatOf: number2().int().nullable()
|
|
26580
|
+
});
|
|
26581
|
+
function ahnentafelGeneration(n) {
|
|
26582
|
+
return Math.floor(Math.log2(n));
|
|
26583
|
+
}
|
|
26561
26584
|
var familyTreePersonViewSchema = object({
|
|
26562
26585
|
ref: familyTreeRefSchema,
|
|
26563
26586
|
name: string2(),
|
|
@@ -26615,11 +26638,24 @@ var familyTreeViewSchema = object({
|
|
|
26615
26638
|
birthdaysOffered: array(familyTreeRefSchema),
|
|
26616
26639
|
branch: familyTreeBranchViewSchema.nullable(),
|
|
26617
26640
|
matches: array(familyTreeMatchSchema),
|
|
26641
|
+
edits: object({ count: number2().int(), skipped: number2().int() }),
|
|
26618
26642
|
updatedAt: exports_coerce.date()
|
|
26619
26643
|
});
|
|
26620
26644
|
var familyTreeResponseSchema = object({ tree: familyTreeViewSchema.nullable() });
|
|
26621
26645
|
var sourceNameSchema = string2().trim().min(1).max(80);
|
|
26622
26646
|
var sourceEmailSchema = string2().trim().email().max(200);
|
|
26647
|
+
var familyTreeAncestorsQuerySchema = object({
|
|
26648
|
+
ref: familyTreeRefSchema.optional(),
|
|
26649
|
+
depth: exports_coerce.number().int().min(1).max(FAMILY_TREE_MAX_GENERATIONS).optional()
|
|
26650
|
+
});
|
|
26651
|
+
var familyTreeAncestorsSchema = object({
|
|
26652
|
+
rootRef: familyTreeRefSchema,
|
|
26653
|
+
depth: number2().int().nullable(),
|
|
26654
|
+
generations: number2().int(),
|
|
26655
|
+
deeper: boolean2(),
|
|
26656
|
+
ancestors: array(familyTreeAncestorSchema),
|
|
26657
|
+
people: array(familyTreePersonViewSchema)
|
|
26658
|
+
});
|
|
26623
26659
|
var familyTreeQuerySchema = object({ side: familyTreeSideSchema.optional() });
|
|
26624
26660
|
var familyTreeUploadFieldsSchema = object({
|
|
26625
26661
|
sourceName: preprocess((v) => v === "" ? undefined : v, sourceNameSchema.optional()),
|
|
@@ -26656,7 +26692,118 @@ var familyTreeMatchDecisionResultSchema = object({
|
|
|
26656
26692
|
matches: array(familyTreeMatchViewSchema)
|
|
26657
26693
|
});
|
|
26658
26694
|
|
|
26695
|
+
// ../../packages/schema/src/contracts/family-tree-edits.ts
|
|
26696
|
+
var familyTreeDateSchema = string2().trim().regex(/^\d{4}(-\d{2}-\d{2})?$/, "expected YYYY or YYYY-MM-DD").refine((v) => {
|
|
26697
|
+
if (v.length === 4)
|
|
26698
|
+
return true;
|
|
26699
|
+
const d = new Date(`${v}T00:00:00Z`);
|
|
26700
|
+
return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === v;
|
|
26701
|
+
}, "not a real day");
|
|
26702
|
+
var nameSchema = string2().trim().min(1).max(120);
|
|
26703
|
+
var partSchema = string2().trim().min(1).max(80).nullish();
|
|
26704
|
+
var placeSchema = string2().trim().min(1).max(200).nullish();
|
|
26705
|
+
var sexSchema = _enum2(["M", "F"]).nullish();
|
|
26706
|
+
var personFieldsShape = {
|
|
26707
|
+
given: partSchema,
|
|
26708
|
+
surname: partSchema,
|
|
26709
|
+
sex: sexSchema,
|
|
26710
|
+
birthDate: familyTreeDateSchema.nullish(),
|
|
26711
|
+
birthPlace: placeSchema,
|
|
26712
|
+
deathDate: familyTreeDateSchema.nullish(),
|
|
26713
|
+
deathPlace: placeSchema
|
|
26714
|
+
};
|
|
26715
|
+
var addFamilyTreePersonInputSchema = object({ name: nameSchema, ...personFieldsShape }).strict();
|
|
26716
|
+
var editFamilyTreePersonInputSchema = object({ name: nameSchema.optional(), ...personFieldsShape }).strict().refine((v) => Object.values(v).some((x) => x !== undefined), "nothing to change");
|
|
26717
|
+
var factTypeSchema = string2().trim().min(1).max(40).transform((v) => v.toLowerCase());
|
|
26718
|
+
var factFieldsShape = {
|
|
26719
|
+
date: string2().trim().min(1).max(60).nullish(),
|
|
26720
|
+
place: placeSchema,
|
|
26721
|
+
value: string2().trim().min(1).max(2000).nullish()
|
|
26722
|
+
};
|
|
26723
|
+
var addFamilyTreeFactInputSchema = object({ type: factTypeSchema, ...factFieldsShape }).strict();
|
|
26724
|
+
var editFamilyTreeFactInputSchema = object({ type: factTypeSchema.optional(), ...factFieldsShape }).strict().refine((v) => Object.values(v).some((x) => x !== undefined), "nothing to change");
|
|
26725
|
+
var familyTreeFactIdSchema = string2().regex(/^[A-Za-z0-9_-]{1,40}$/, "expected a fact id");
|
|
26726
|
+
var familyTreeLinkInputSchema = discriminatedUnion("kind", [
|
|
26727
|
+
object({ kind: literal("parent"), parent: familyTreeRefSchema, child: familyTreeRefSchema }).strict(),
|
|
26728
|
+
object({ kind: literal("partner"), a: familyTreeRefSchema, b: familyTreeRefSchema }).strict()
|
|
26729
|
+
]);
|
|
26730
|
+
var familyTreeFactSchema = object({
|
|
26731
|
+
id: familyTreeFactIdSchema,
|
|
26732
|
+
type: string2(),
|
|
26733
|
+
date: string2().nullable(),
|
|
26734
|
+
place: string2().nullable(),
|
|
26735
|
+
value: string2().nullable()
|
|
26736
|
+
});
|
|
26737
|
+
var relativeSchema = object({ ref: familyTreeRefSchema, name: string2() });
|
|
26738
|
+
var familyTreePersonDetailSchema = object({
|
|
26739
|
+
ref: familyTreeRefSchema,
|
|
26740
|
+
name: string2(),
|
|
26741
|
+
given: string2().nullable(),
|
|
26742
|
+
surname: string2().nullable(),
|
|
26743
|
+
sex: _enum2(["M", "F"]).nullable(),
|
|
26744
|
+
birthDate: string2().nullable(),
|
|
26745
|
+
birthYear: number2().int().nullable(),
|
|
26746
|
+
birthPlace: string2().nullable(),
|
|
26747
|
+
deathDate: string2().nullable(),
|
|
26748
|
+
deathYear: number2().int().nullable(),
|
|
26749
|
+
deathPlace: string2().nullable(),
|
|
26750
|
+
living: boolean2(),
|
|
26751
|
+
facts: array(familyTreeFactSchema),
|
|
26752
|
+
parents: array(relativeSchema),
|
|
26753
|
+
partners: array(relativeSchema),
|
|
26754
|
+
children: array(relativeSchema),
|
|
26755
|
+
added: boolean2(),
|
|
26756
|
+
contact: object({ id: ulidSchema, title: string2() }).nullable()
|
|
26757
|
+
});
|
|
26758
|
+
var familyTreeEditKindSchema = _enum2([
|
|
26759
|
+
"person_add",
|
|
26760
|
+
"person_edit",
|
|
26761
|
+
"person_remove",
|
|
26762
|
+
"fact_add",
|
|
26763
|
+
"fact_edit",
|
|
26764
|
+
"fact_remove",
|
|
26765
|
+
"link_parent",
|
|
26766
|
+
"link_partner",
|
|
26767
|
+
"unlink_parent",
|
|
26768
|
+
"unlink_partner"
|
|
26769
|
+
]);
|
|
26770
|
+
var familyTreeEditViewSchema = object({
|
|
26771
|
+
id: ulidSchema,
|
|
26772
|
+
kind: familyTreeEditKindSchema,
|
|
26773
|
+
line: string2(),
|
|
26774
|
+
at: exports_coerce.date(),
|
|
26775
|
+
applied: boolean2(),
|
|
26776
|
+
reason: string2().nullable()
|
|
26777
|
+
});
|
|
26778
|
+
var familyTreeEditsSchema = object({ edits: array(familyTreeEditViewSchema) });
|
|
26779
|
+
var familyTreeChangeSchema = object({
|
|
26780
|
+
ref: familyTreeRefSchema,
|
|
26781
|
+
op: _enum2(["created", "updated", "deleted"]),
|
|
26782
|
+
name: string2(),
|
|
26783
|
+
fields: array(string2())
|
|
26784
|
+
});
|
|
26785
|
+
var familyTreeEditReceiptSchema = object({
|
|
26786
|
+
edit: familyTreeEditViewSchema,
|
|
26787
|
+
changes: array(familyTreeChangeSchema),
|
|
26788
|
+
person: familyTreePersonDetailSchema.nullable()
|
|
26789
|
+
});
|
|
26790
|
+
var familyTreeUndoResultSchema = object({
|
|
26791
|
+
undone: ulidSchema,
|
|
26792
|
+
changes: array(familyTreeChangeSchema),
|
|
26793
|
+
skipped: array(familyTreeEditViewSchema)
|
|
26794
|
+
});
|
|
26795
|
+
|
|
26659
26796
|
// ../../packages/schema/src/operations/family-tree.ts
|
|
26797
|
+
var REF = {
|
|
26798
|
+
ref: { description: "The person's ref in the tree (`I12`, or `P\u2026` for one added in do)." }
|
|
26799
|
+
};
|
|
26800
|
+
var REF_SCHEMA = { ref: familyTreeRefSchema };
|
|
26801
|
+
var FACT = {
|
|
26802
|
+
...REF,
|
|
26803
|
+
factId: { description: "The fact's id (`OCCU-1` from the GEDCOM, `f-\u2026` for one added in do)." }
|
|
26804
|
+
};
|
|
26805
|
+
var FACT_SCHEMA = { ref: familyTreeRefSchema, factId: familyTreeFactIdSchema };
|
|
26806
|
+
var RECEIPT = "Returns the receipt `{ edit, changes, person }`: `edit.id` is what `POST /family-tree/edits/{id}/undo` takes, `changes` lists each person the edit changed (created \xB7 updated \xB7 deleted, with the fields). The edit is kept as an overlay over the import, so a new GEDCOM keeps it.";
|
|
26660
26807
|
var NOT_IN_STAR = "A sensitive scope (ADR 0028): never part of `*`, and only the tree of the member who minted the key.";
|
|
26661
26808
|
var familyTreeOperations = defineOperations({
|
|
26662
26809
|
import: {
|
|
@@ -26693,6 +26840,31 @@ var familyTreeOperations = defineOperations({
|
|
|
26693
26840
|
response: { description: "`{ tree }`." },
|
|
26694
26841
|
errors: { 400: true, 403: "The key doesn't hold `people:family-tree`, or is no member's." }
|
|
26695
26842
|
},
|
|
26843
|
+
ancestors: {
|
|
26844
|
+
method: "get",
|
|
26845
|
+
path: "/family-tree/ancestors",
|
|
26846
|
+
scopes: ["people:family-tree"],
|
|
26847
|
+
tag: "People",
|
|
26848
|
+
summary: "The direct line back",
|
|
26849
|
+
description: "A pedigree: every ancestor of `ref` (the member's own place in the tree when left out) as far as `depth` generations (1\u201350; all of them when left out). Each slot has its Ahnentafel number `n` (the person is 1, a father 2n, a mother 2n+1) and `generation`; someone who is an ancestor along two lines appears at each number, `repeatOf` naming the first. `people` holds each person once; `deeper` says the tree goes further back than `depth`. A 400 before the member said who they are and with no `ref`.",
|
|
26850
|
+
query: [
|
|
26851
|
+
{
|
|
26852
|
+
name: "ref",
|
|
26853
|
+
description: "Whose ancestors (default: the member's own place in the tree)."
|
|
26854
|
+
},
|
|
26855
|
+
{
|
|
26856
|
+
name: "depth",
|
|
26857
|
+
type: "integer",
|
|
26858
|
+
minimum: 1,
|
|
26859
|
+
maximum: 50,
|
|
26860
|
+
description: "How many generations back (default: all)."
|
|
26861
|
+
}
|
|
26862
|
+
],
|
|
26863
|
+
querySchema: familyTreeAncestorsQuerySchema,
|
|
26864
|
+
status: 200,
|
|
26865
|
+
response: { description: "`{ rootRef, depth, generations, deeper, ancestors, people }`." },
|
|
26866
|
+
errors: { 400: true, 404: true }
|
|
26867
|
+
},
|
|
26696
26868
|
people: {
|
|
26697
26869
|
method: "get",
|
|
26698
26870
|
path: "/family-tree/people",
|
|
@@ -26744,6 +26916,150 @@ var familyTreeOperations = defineOperations({
|
|
|
26744
26916
|
status: 200,
|
|
26745
26917
|
response: { description: "`{ id, did, matches }`." },
|
|
26746
26918
|
errors: { 400: true, 404: true }
|
|
26919
|
+
},
|
|
26920
|
+
person: {
|
|
26921
|
+
method: "get",
|
|
26922
|
+
path: "/family-tree/people/{ref}",
|
|
26923
|
+
scopes: ["people:family-tree"],
|
|
26924
|
+
tag: "People",
|
|
26925
|
+
summary: "One person in the tree",
|
|
26926
|
+
description: "Their name, sex, birth and death (date and place), facts (each with the `id` an edit takes), parents, partners and children, whether the member added them, and the Contact they're linked to.",
|
|
26927
|
+
pathParams: REF,
|
|
26928
|
+
pathParamSchemas: REF_SCHEMA,
|
|
26929
|
+
status: 200,
|
|
26930
|
+
response: { description: "The person." },
|
|
26931
|
+
errors: { 400: true, 404: true }
|
|
26932
|
+
},
|
|
26933
|
+
addPerson: {
|
|
26934
|
+
method: "post",
|
|
26935
|
+
path: "/family-tree/people",
|
|
26936
|
+
scopes: ["people:family-tree"],
|
|
26937
|
+
tag: "People",
|
|
26938
|
+
summary: "Add a person to the tree",
|
|
26939
|
+
description: `\`{ name, given?, surname?, sex? (M \xB7 F), birthDate?, birthPlace?, deathDate?, deathPlace? }\`; dates are \`YYYY\` or \`YYYY-MM-DD\`, and a death date or place means not living. They get a new ref (\`P\u2026\`); link them to parents or a partner next. ${RECEIPT}`,
|
|
26940
|
+
body: addFamilyTreePersonInputSchema,
|
|
26941
|
+
status: 201,
|
|
26942
|
+
response: { description: "The receipt." },
|
|
26943
|
+
errors: { 400: true, 404: true }
|
|
26944
|
+
},
|
|
26945
|
+
editPerson: {
|
|
26946
|
+
method: "patch",
|
|
26947
|
+
path: "/family-tree/people/{ref}",
|
|
26948
|
+
scopes: ["people:family-tree"],
|
|
26949
|
+
tag: "People",
|
|
26950
|
+
summary: "Change a person in the tree",
|
|
26951
|
+
description: `Any of the add's fields; \`null\` clears one. A change to the death decides again whether they're living. ${RECEIPT}`,
|
|
26952
|
+
pathParams: REF,
|
|
26953
|
+
pathParamSchemas: REF_SCHEMA,
|
|
26954
|
+
body: editFamilyTreePersonInputSchema,
|
|
26955
|
+
status: 200,
|
|
26956
|
+
response: { description: "The receipt." },
|
|
26957
|
+
errors: { 400: true, 404: true }
|
|
26958
|
+
},
|
|
26959
|
+
removePerson: {
|
|
26960
|
+
method: "delete",
|
|
26961
|
+
path: "/family-tree/people/{ref}",
|
|
26962
|
+
scopes: ["people:family-tree"],
|
|
26963
|
+
tag: "People",
|
|
26964
|
+
summary: "Take a person out of the tree",
|
|
26965
|
+
description: `Out of every family they're in; their link to a Contact comes back with an Undo. ${RECEIPT}`,
|
|
26966
|
+
pathParams: REF,
|
|
26967
|
+
pathParamSchemas: REF_SCHEMA,
|
|
26968
|
+
status: 200,
|
|
26969
|
+
response: { description: "The receipt (`person`: null)." },
|
|
26970
|
+
errors: { 400: true, 404: true }
|
|
26971
|
+
},
|
|
26972
|
+
addFact: {
|
|
26973
|
+
method: "post",
|
|
26974
|
+
path: "/family-tree/people/{ref}/facts",
|
|
26975
|
+
scopes: ["people:family-tree"],
|
|
26976
|
+
tag: "People",
|
|
26977
|
+
summary: "Add a fact to a person",
|
|
26978
|
+
description: `\`{ type, date?, place?, value? }\`: an occupation, a residence, a baptism, a note (the type is a word of the member's own). The date is as written (\`ABT 1720\`). ${RECEIPT}`,
|
|
26979
|
+
pathParams: REF,
|
|
26980
|
+
pathParamSchemas: REF_SCHEMA,
|
|
26981
|
+
body: addFamilyTreeFactInputSchema,
|
|
26982
|
+
status: 201,
|
|
26983
|
+
response: { description: "The receipt." },
|
|
26984
|
+
errors: { 400: true, 404: true }
|
|
26985
|
+
},
|
|
26986
|
+
editFact: {
|
|
26987
|
+
method: "patch",
|
|
26988
|
+
path: "/family-tree/people/{ref}/facts/{factId}",
|
|
26989
|
+
scopes: ["people:family-tree"],
|
|
26990
|
+
tag: "People",
|
|
26991
|
+
summary: "Change a fact",
|
|
26992
|
+
description: `Any of \`type\`, \`date\`, \`place\`, \`value\`; \`null\` clears one. ${RECEIPT}`,
|
|
26993
|
+
pathParams: FACT,
|
|
26994
|
+
pathParamSchemas: FACT_SCHEMA,
|
|
26995
|
+
body: editFamilyTreeFactInputSchema,
|
|
26996
|
+
status: 200,
|
|
26997
|
+
response: { description: "The receipt." },
|
|
26998
|
+
errors: { 400: true, 404: true }
|
|
26999
|
+
},
|
|
27000
|
+
removeFact: {
|
|
27001
|
+
method: "delete",
|
|
27002
|
+
path: "/family-tree/people/{ref}/facts/{factId}",
|
|
27003
|
+
scopes: ["people:family-tree"],
|
|
27004
|
+
tag: "People",
|
|
27005
|
+
summary: "Remove a fact",
|
|
27006
|
+
description: RECEIPT,
|
|
27007
|
+
pathParams: FACT,
|
|
27008
|
+
pathParamSchemas: FACT_SCHEMA,
|
|
27009
|
+
status: 200,
|
|
27010
|
+
response: { description: "The receipt." },
|
|
27011
|
+
errors: { 400: true, 404: true }
|
|
27012
|
+
},
|
|
27013
|
+
link: {
|
|
27014
|
+
method: "post",
|
|
27015
|
+
path: "/family-tree/links",
|
|
27016
|
+
scopes: ["people:family-tree"],
|
|
27017
|
+
tag: "People",
|
|
27018
|
+
summary: "Link parent and child, or partners",
|
|
27019
|
+
description: `\`{ kind: "parent", parent, child }\` (a child has at most two parents: a third is a 409) or \`{ kind: "partner", a, b }\`. ${RECEIPT}`,
|
|
27020
|
+
body: familyTreeLinkInputSchema,
|
|
27021
|
+
status: 200,
|
|
27022
|
+
response: { description: "The receipt." },
|
|
27023
|
+
errors: { 400: true, 404: true, 409: true },
|
|
27024
|
+
operationId: "family_tree_link"
|
|
27025
|
+
},
|
|
27026
|
+
unlink: {
|
|
27027
|
+
method: "post",
|
|
27028
|
+
path: "/family-tree/unlink",
|
|
27029
|
+
scopes: ["people:family-tree"],
|
|
27030
|
+
tag: "People",
|
|
27031
|
+
summary: "Unlink parent and child, or partners",
|
|
27032
|
+
description: `The link's body. A child with siblings keeps them with the other parent; partners with children together are a 409 (unlink a parent first). ${RECEIPT}`,
|
|
27033
|
+
body: familyTreeLinkInputSchema,
|
|
27034
|
+
status: 200,
|
|
27035
|
+
response: { description: "The receipt." },
|
|
27036
|
+
errors: { 400: true, 404: true, 409: true },
|
|
27037
|
+
operationId: "family_tree_unlink"
|
|
27038
|
+
},
|
|
27039
|
+
edits: {
|
|
27040
|
+
method: "get",
|
|
27041
|
+
path: "/family-tree/edits",
|
|
27042
|
+
scopes: ["people:family-tree"],
|
|
27043
|
+
tag: "People",
|
|
27044
|
+
summary: "The member's edits to the tree",
|
|
27045
|
+
description: "Oldest first, each `{ id, kind, line, at, applied, reason }`. The edits are an overlay replayed over every import: one the current file has no room for (its person or fact isn't in it, or it no longer fits) is `applied: false` with the `reason`, kept, and applies again when a later file brings its person back.",
|
|
27046
|
+
status: 200,
|
|
27047
|
+
response: { description: "`{ edits }`." },
|
|
27048
|
+
errors: { 404: true }
|
|
27049
|
+
},
|
|
27050
|
+
undo: {
|
|
27051
|
+
method: "post",
|
|
27052
|
+
path: "/family-tree/edits/{id}/undo",
|
|
27053
|
+
scopes: ["people:family-tree"],
|
|
27054
|
+
tag: "People",
|
|
27055
|
+
summary: "Undo an edit to the tree",
|
|
27056
|
+
description: "The receipt's Undo: the edit leaves the overlay and the tree is replayed without it. Returns `{ undone, changes, skipped }`; `skipped` lists the edits that leaned on it (a fact on a person it added) and no longer apply. A POST, so it doesn't wait on a confirmation the way a delete does.",
|
|
27057
|
+
pathParams: { id: "The edit's id (a receipt's `edit.id`)." },
|
|
27058
|
+
pathParamSchemas: { id: ulidSchema },
|
|
27059
|
+
status: 200,
|
|
27060
|
+
response: { description: "`{ undone, changes, skipped }`." },
|
|
27061
|
+
errors: { 400: true, 404: true },
|
|
27062
|
+
operationId: "family_tree_undo"
|
|
26747
27063
|
}
|
|
26748
27064
|
});
|
|
26749
27065
|
|
|
@@ -27940,7 +28256,7 @@ var foodPatternsSchema = object({
|
|
|
27940
28256
|
// ../../packages/schema/src/operations/food-log.ts
|
|
27941
28257
|
var NO_MEMBER = "The key holds no health scope by name, or has no member behind it.";
|
|
27942
28258
|
var HIDDEN2 = "No such meal, or it isn't the key's member's.";
|
|
27943
|
-
var
|
|
28259
|
+
var RECEIPT2 = "The meal with its `receipt`: a headline with the kcal range, a confidence word (close \xB7 estimate \xB7 rough), a line per item with its source, and the Undo / Edit / Save as my meal calls.";
|
|
27944
28260
|
var ITEM = "Each item: `label` and one of `foodId` (from `GET /api/v1/foods/search`), `productId` or `recipeId`, with `grams`, `quantity` + `unit` (`2 boterham`, `1 el`) or `servings`; optional `gramsLow`/`gramsHigh` and `matchConfidence`. With no reference, an `estimate` `{kcal, proteinG, carbsG, fatG}` (a labelled guess). The numbers are computed: table \xD7 grams.";
|
|
27945
28261
|
var meal = { id: "The meal's ULID." };
|
|
27946
28262
|
var mealId = { id: ulidSchema };
|
|
@@ -27970,7 +28286,7 @@ var foodLogOperations = defineOperations({
|
|
|
27970
28286
|
description: `Log what the member ate in one call: \`id\`, \`title\`, \`eatenAt\`, \`mealType\` (breakfast \xB7 lunch \xB7 dinner \xB7 snack \xB7 drink), optional \`portionNote\`, \`place\`, \`flags\`, \`capture\`, \`photoFileId\`, and \`items\`. ${ITEM} Always private to the member.`,
|
|
27971
28287
|
body: createMealLogInputSchema,
|
|
27972
28288
|
status: 201,
|
|
27973
|
-
response: { description:
|
|
28289
|
+
response: { description: RECEIPT2 },
|
|
27974
28290
|
errors: { 400: true, 403: NO_MEMBER },
|
|
27975
28291
|
operationId: "food_log"
|
|
27976
28292
|
},
|
|
@@ -27998,7 +28314,7 @@ var foodLogOperations = defineOperations({
|
|
|
27998
28314
|
description: `Correct a meal: \`replace\` (\`[{itemId, item}]\`), \`remove\` (item ids) and \`add\` (items) add rows, never edit one; the meal's own fields update. ${ITEM}`,
|
|
27999
28315
|
body: correctMealLogInputSchema,
|
|
28000
28316
|
status: 200,
|
|
28001
|
-
response: { description:
|
|
28317
|
+
response: { description: RECEIPT2 },
|
|
28002
28318
|
errors: { 400: true, 404: HIDDEN2 },
|
|
28003
28319
|
operationId: "food_correct"
|
|
28004
28320
|
},
|
|
@@ -28025,7 +28341,7 @@ var foodLogOperations = defineOperations({
|
|
|
28025
28341
|
summary: "Undo a meal's last correction",
|
|
28026
28342
|
description: "Take back the latest correction: the items it replaced or removed are current again.",
|
|
28027
28343
|
status: 200,
|
|
28028
|
-
response: { description:
|
|
28344
|
+
response: { description: RECEIPT2 },
|
|
28029
28345
|
errors: { 400: true, 404: HIDDEN2 }
|
|
28030
28346
|
},
|
|
28031
28347
|
saveMeal: {
|
|
@@ -33038,7 +33354,10 @@ var moneyNetWorthAccountSchema = object({
|
|
|
33038
33354
|
currency: string2(),
|
|
33039
33355
|
value: amount5,
|
|
33040
33356
|
source: moneySnapshotSourceSchema,
|
|
33041
|
-
month: moneyMonthSchema
|
|
33357
|
+
month: moneyMonthSchema,
|
|
33358
|
+
pensionSubtype: moneyPensionSubtypeSchema.nullable(),
|
|
33359
|
+
lockedUntil: string2().nullable(),
|
|
33360
|
+
locked: boolean2()
|
|
33042
33361
|
});
|
|
33043
33362
|
var moneyNetWorthUnknownSchema = moneyNetWorthAccountSchema.pick({
|
|
33044
33363
|
accountId: true,
|
|
@@ -33053,6 +33372,8 @@ var moneyNetWorthSchema = object({
|
|
|
33053
33372
|
total: amount5,
|
|
33054
33373
|
have: amount5,
|
|
33055
33374
|
owe: amount5,
|
|
33375
|
+
liquid: amount5,
|
|
33376
|
+
locked: amount5,
|
|
33056
33377
|
points: array(moneyNetWorthPointSchema),
|
|
33057
33378
|
accounts: array(moneyNetWorthAccountSchema),
|
|
33058
33379
|
complete: boolean2(),
|
|
@@ -33263,7 +33584,7 @@ var moneyOperations = defineOperations({
|
|
|
33263
33584
|
scopes: ["finance:write"],
|
|
33264
33585
|
tag: "Money",
|
|
33265
33586
|
summary: "Open a money account",
|
|
33266
|
-
description: 'Open an account: `id`, `title`, optional `kind` (current \xB7 savings \xB7 credit_card \xB7 cash \xB7 voucher \xB7 loan \xB7 investment \xB7 pension \xB7 asset_manual), `holder`, `iban` (stored only masked and hashed), `currency`, `openingBalance` (a decimal string), `includeInBudget
|
|
33587
|
+
description: 'Open an account: `id`, `title`, optional `kind` (current \xB7 savings \xB7 credit_card \xB7 cash \xB7 voucher \xB7 loan \xB7 investment \xB7 pension \xB7 asset_manual), the `pensionSubtype` of a pension (group_insurance \xB7 pension_savings_fund \xB7 pension_savings_insurance \xB7 vapz; on another kind a 400), `lockedUntil` (YYYY-MM-DD, any kind), `holder`, `iban` (stored only masked and hashed), `currency`, `openingBalance` (a decimal string), `includeInBudget` (always off for a pension, which never counts toward the budget), `includeInNetWorth` and `visibility`. An account is the household\'s (`visibility: space`) or "Only me" (`owner`, the default for a new one); another member never sees an Only me account or anything on it.',
|
|
33267
33588
|
body: createMoneyAccountInputSchema,
|
|
33268
33589
|
status: 201,
|
|
33269
33590
|
response: { description: "The account." },
|
|
@@ -33338,7 +33659,7 @@ var moneyOperations = defineOperations({
|
|
|
33338
33659
|
scopes: ["finance:write"],
|
|
33339
33660
|
tag: "Money",
|
|
33340
33661
|
summary: "Update a money account",
|
|
33341
|
-
description: "Rename it, change its kind, IBAN, opening balance, what it counts towards, or who sees it. `balance` with `balanceAt` (a day, read as its end, or a moment) states what it held then: the anchor net worth counts from, back and forward through the ledger, for a bank whose export has no running balance. The two go together; a day ahead, or an account a bank link keeps, is a 400.",
|
|
33662
|
+
description: "Rename it, change its kind (an investment account moved to `pension` keeps its balance and ledger; moved off `pension`, its `pensionSubtype` is cleared), its `pensionSubtype` or `lockedUntil` (null clears), IBAN, opening balance, what it counts towards, or who sees it. `balance` with `balanceAt` (a day, read as its end, or a moment) states what it held then: the anchor net worth counts from, back and forward through the ledger, for a bank whose export has no running balance. The two go together; a day ahead, or an account a bank link keeps, is a 400, and so is a body that sets nothing.",
|
|
33342
33663
|
pathParams: { id: "The account's ULID." },
|
|
33343
33664
|
pathParamSchemas: { id: ulidSchema },
|
|
33344
33665
|
body: updateMoneyAccountInputSchema,
|
|
@@ -33858,7 +34179,7 @@ var moneyOperations = defineOperations({
|
|
|
33858
34179
|
scopes: ["finance:read"],
|
|
33859
34180
|
tag: "Money",
|
|
33860
34181
|
summary: "Get net worth and its monthly line",
|
|
33861
|
-
description: "Net worth for the accounts that count in it (`includeInNetWorth`): `total`, `have` and `owe` this month, `points` (one per month, oldest first, the current month last; `total` null before any account had a value) and `accounts` (biggest first, each with its `value`, `source` \u2014 balance, ledger or manual \u2014 and the `month` that value is from). A loan or a credit card always counts as owed. This month follows each account's balance, carried forward by what was booked after `balanceAt`; the months before an account was first read are worked back from its ledger; a value set by hand carries on until the next one. An account whose ledger came from a bank but has no balance yet (an export with no running balance) is never counted as 0: it is listed under `unknown` and left out of the total, and `complete` is false. `scope`: `all` (default), `space` or `owner`; `months` 2\u201360 (12 by default).",
|
|
34182
|
+
description: "Net worth for the accounts that count in it (`includeInNetWorth`): `total`, `have` and `owe` this month, `liquid` and `locked` (they add up to `total`; locked is a pension, or an account whose `lockedUntil` day is ahead, never a debt), `points` (one per month, oldest first, the current month last; `total` null before any account had a value) and `accounts` (biggest first, each with its `value`, `source` \u2014 balance, ledger or manual \u2014 and the `month` that value is from). A loan or a credit card always counts as owed. This month follows each account's balance, carried forward by what was booked after `balanceAt`; the months before an account was first read are worked back from its ledger; a value set by hand carries on until the next one. An account whose ledger came from a bank but has no balance yet (an export with no running balance) is never counted as 0: it is listed under `unknown` and left out of the total, and `complete` is false. `scope`: `all` (default), `space` or `owner`; `months` 2\u201360 (12 by default).",
|
|
33862
34183
|
query: [
|
|
33863
34184
|
{ name: "scope", description: "all, space or owner." },
|
|
33864
34185
|
{ name: "months", description: "How many months the line covers (2\u201360, default 12)." }
|
|
@@ -34566,12 +34887,12 @@ var periodOperations = defineOperations({
|
|
|
34566
34887
|
var placePointSchema = tuple([number2().finite(), number2().finite()]);
|
|
34567
34888
|
var placeOutlineSchema = array(placePointSchema).min(3).max(200);
|
|
34568
34889
|
var placeKindSchema = _enum2(placeKindValues);
|
|
34569
|
-
var
|
|
34890
|
+
var placeSchema2 = createSelectSchema(places, {
|
|
34570
34891
|
outline: placeOutlineSchema.nullable(),
|
|
34571
34892
|
kind: string2().default("room"),
|
|
34572
34893
|
gardenFeatureId: string2().nullable().default(null)
|
|
34573
34894
|
});
|
|
34574
|
-
var placeViewSchema =
|
|
34895
|
+
var placeViewSchema = placeSchema2.extend({
|
|
34575
34896
|
title: string2(),
|
|
34576
34897
|
parentId: string2().nullable().default(null),
|
|
34577
34898
|
createdAt: exports_coerce.date(),
|
|
@@ -36009,7 +36330,8 @@ var projectOperations = defineOperations({
|
|
|
36009
36330
|
var occurrenceViewSchema = occurrenceSchema.extend({
|
|
36010
36331
|
title: string2(),
|
|
36011
36332
|
templateKind: _enum2(["task", "habit", "routine"]),
|
|
36012
|
-
measurementType: habitMeasurementTypeSchema.nullable()
|
|
36333
|
+
measurementType: habitMeasurementTypeSchema.nullable(),
|
|
36334
|
+
allDay: boolean2().optional()
|
|
36013
36335
|
});
|
|
36014
36336
|
var todayResponseSchema = object({
|
|
36015
36337
|
from: exports_coerce.date(),
|
|
@@ -48608,7 +48930,78 @@ function goalSavedSource(goal) {
|
|
|
48608
48930
|
return "counted toward it";
|
|
48609
48931
|
}
|
|
48610
48932
|
|
|
48933
|
+
// src/global-flags.ts
|
|
48934
|
+
var GLOBAL_FLAGS = [
|
|
48935
|
+
{ name: "json", type: "boolean", description: "Emit JSON (automatic when stdout is piped)." },
|
|
48936
|
+
{ name: "quiet", type: "boolean", description: "Suppress human output (errors still report)." },
|
|
48937
|
+
{ name: "verbose", type: "boolean", description: "Extra diagnostics on stderr." },
|
|
48938
|
+
{
|
|
48939
|
+
name: "api-key",
|
|
48940
|
+
type: "string",
|
|
48941
|
+
description: "API key; overrides DO_API_KEY and the config file."
|
|
48942
|
+
},
|
|
48943
|
+
{
|
|
48944
|
+
name: "url",
|
|
48945
|
+
type: "string",
|
|
48946
|
+
description: "API origin; overrides DO_API_URL and the config file."
|
|
48947
|
+
},
|
|
48948
|
+
{ name: "help", type: "boolean", description: "Show help for the command." },
|
|
48949
|
+
{ name: "version", type: "boolean", description: "Print the CLI version." },
|
|
48950
|
+
{
|
|
48951
|
+
name: "data",
|
|
48952
|
+
type: "string",
|
|
48953
|
+
description: `JSON object body for create/update (e.g. --data '{"title":"Buy milk"}').`
|
|
48954
|
+
},
|
|
48955
|
+
{
|
|
48956
|
+
name: "allow-destructive",
|
|
48957
|
+
type: "boolean",
|
|
48958
|
+
description: "Permit a destructive verb (delete/revoke/remove-member). Off by default \u2014 a guardrail for autonomous agents."
|
|
48959
|
+
},
|
|
48960
|
+
{
|
|
48961
|
+
name: "dry-run",
|
|
48962
|
+
type: "boolean",
|
|
48963
|
+
description: "Preview a create/update/delete/archive (prints before/after) without writing \u2014 for agent-driven cleanup."
|
|
48964
|
+
}
|
|
48965
|
+
];
|
|
48966
|
+
|
|
48611
48967
|
// src/commands/money.ts
|
|
48968
|
+
function refuseUnknownFlags(flags, own) {
|
|
48969
|
+
const known = new Set([...own, ...GLOBAL_FLAGS.map((f) => f.name)]);
|
|
48970
|
+
const unknown = Object.keys(flags).filter((name) => !known.has(name));
|
|
48971
|
+
if (unknown.length > 0) {
|
|
48972
|
+
throw usageError(`unknown flag ${unknown.map((n) => `--${n}`).join(", ")} (takes ${own.map((n) => `--${n}`).join(", ")}; see \`do help\`)`);
|
|
48973
|
+
}
|
|
48974
|
+
}
|
|
48975
|
+
var ACCOUNT_KIND_FLAGS = [
|
|
48976
|
+
{
|
|
48977
|
+
name: "kind",
|
|
48978
|
+
type: "string",
|
|
48979
|
+
description: "current \xB7 savings \xB7 credit_card \xB7 cash \xB7 voucher \xB7 loan \xB7 investment \xB7 pension \xB7 asset_manual."
|
|
48980
|
+
},
|
|
48981
|
+
{
|
|
48982
|
+
name: "subtype",
|
|
48983
|
+
type: "string",
|
|
48984
|
+
description: "A pension's kind: group_insurance \xB7 pension_savings_fund \xB7 pension_savings_insurance \xB7 vapz (`none` clears it)."
|
|
48985
|
+
},
|
|
48986
|
+
{
|
|
48987
|
+
name: "locked-until",
|
|
48988
|
+
type: "string",
|
|
48989
|
+
description: "YYYY-MM-DD until which it can't be taken out (`none` clears it)."
|
|
48990
|
+
}
|
|
48991
|
+
];
|
|
48992
|
+
function accountKindFields(flags) {
|
|
48993
|
+
const out = {};
|
|
48994
|
+
const kind = flagString(flags, "kind");
|
|
48995
|
+
const subtype = flagString(flags, "subtype");
|
|
48996
|
+
const lockedUntil = flagString(flags, "locked-until");
|
|
48997
|
+
if (kind !== undefined)
|
|
48998
|
+
out.kind = kind;
|
|
48999
|
+
if (subtype !== undefined)
|
|
49000
|
+
out.pensionSubtype = subtype === "none" ? null : subtype;
|
|
49001
|
+
if (lockedUntil !== undefined)
|
|
49002
|
+
out.lockedUntil = lockedUntil === "none" ? null : lockedUntil;
|
|
49003
|
+
return out;
|
|
49004
|
+
}
|
|
48612
49005
|
function renderDedupe(data) {
|
|
48613
49006
|
const r = data;
|
|
48614
49007
|
const lines = r.pairs.map((p) => `${p.bookedOn} ${p.amount.padStart(10)} ${p.description ?? p.counterpartyName ?? ""} keep ${p.keepId} twin ${p.twinId} (${p.twinBookedOn})${p.ambiguous ? " ambiguous: sort by hand" : ""}`);
|
|
@@ -48692,13 +49085,17 @@ var commands31 = [
|
|
|
48692
49085
|
path: ["money", "account-add"],
|
|
48693
49086
|
summary: "Open a money account (Only me unless --data says visibility: space).",
|
|
48694
49087
|
args: [],
|
|
48695
|
-
flags: [
|
|
49088
|
+
flags: [
|
|
49089
|
+
{ name: "data", type: "string", description: "CreateMoneyAccountInput JSON." },
|
|
49090
|
+
...ACCOUNT_KIND_FLAGS
|
|
49091
|
+
],
|
|
48696
49092
|
returns: "MoneyAccountView",
|
|
48697
|
-
notes: `. \`do money account-add --data '{"title":"Joint","kind":"current","holder":"joint","visibility":"space"}'\`. An IBAN is stored only masked and hashed.`,
|
|
49093
|
+
notes: `. \`do money account-add --data '{"title":"Joint","kind":"current","holder":"joint","visibility":"space"}'\`. An IBAN is stored only masked and hashed. A pension: \`--kind pension --subtype group_insurance --locked-until 2055-01-01\` (a subtype on another kind is a 400).`,
|
|
48698
49094
|
run: async ({ flags, requireClient }) => {
|
|
48699
49095
|
const body = parseInput(createMoneyAccountInputSchema, {
|
|
48700
49096
|
id: ulid3(),
|
|
48701
|
-
...readDataFlag(flags)
|
|
49097
|
+
...readDataFlag(flags),
|
|
49098
|
+
...accountKindFields(flags)
|
|
48702
49099
|
});
|
|
48703
49100
|
return {
|
|
48704
49101
|
data: await requireClient().post(pathOf(moneyOperations.createAccount), body),
|
|
@@ -48713,6 +49110,7 @@ var commands31 = [
|
|
|
48713
49110
|
args: [{ name: "id", required: true, description: "Account ULID." }],
|
|
48714
49111
|
flags: [
|
|
48715
49112
|
{ name: "data", type: "string", description: "UpdateMoneyAccountInput JSON." },
|
|
49113
|
+
...ACCOUNT_KIND_FLAGS,
|
|
48716
49114
|
{
|
|
48717
49115
|
name: "balance",
|
|
48718
49116
|
type: "string",
|
|
@@ -48725,8 +49123,14 @@ var commands31 = [
|
|
|
48725
49123
|
}
|
|
48726
49124
|
],
|
|
48727
49125
|
returns: "MoneyAccountView",
|
|
48728
|
-
notes: ". `--balance 1234.56 --balance-at 2026-10-27` anchors an account whose bank export has no running balance (ING's): net worth counts from it, back and forward through the ledger. A negative one rides as `--balance=-20`. A day ahead is refused, and so is an account a bank link keeps. `--data` takes the rest (title,
|
|
49126
|
+
notes: ". `--balance 1234.56 --balance-at 2026-10-27` anchors an account whose bank export has no running balance (ING's): net worth counts from it, back and forward through the ledger. A negative one rides as `--balance=-20`. A day ahead is refused, and so is an account a bank link keeps. `--kind pension --subtype vapz --locked-until 2055-01-01` refiles it (an investment account moved to pension keeps its balance and ledger; moved off pension, the subtype is cleared). `--data` takes the rest (title, iban, includeInNetWorth, \u2026). A flag it doesn't take, or nothing to change, is refused before anything is sent.",
|
|
48729
49127
|
run: async ({ args, flags, io, requireClient }) => {
|
|
49128
|
+
refuseUnknownFlags(flags, [
|
|
49129
|
+
"data",
|
|
49130
|
+
...ACCOUNT_KIND_FLAGS.map((f) => f.name),
|
|
49131
|
+
"balance",
|
|
49132
|
+
"balance-at"
|
|
49133
|
+
]);
|
|
48730
49134
|
const id = requireArg(args, 0, "account id");
|
|
48731
49135
|
const balance = flagString(flags, "balance");
|
|
48732
49136
|
const balanceAt = flagString(flags, "balance-at");
|
|
@@ -48735,6 +49139,7 @@ var commands31 = [
|
|
|
48735
49139
|
}
|
|
48736
49140
|
const body = parseInput(updateMoneyAccountInputSchema, {
|
|
48737
49141
|
...readDataFlag(flags),
|
|
49142
|
+
...accountKindFields(flags),
|
|
48738
49143
|
...balance !== undefined ? { balance, balanceAt: balanceAt ?? isoDay(io.now()) } : {}
|
|
48739
49144
|
});
|
|
48740
49145
|
return {
|
|
@@ -49531,7 +49936,7 @@ var commands31 = [
|
|
|
49531
49936
|
}
|
|
49532
49937
|
],
|
|
49533
49938
|
returns: "MoneyNetWorth",
|
|
49534
|
-
notes: ". total, have and owe this month; points per month (oldest first, total null before any value); accounts biggest first with value, source (balance \xB7 ledger \xB7 manual) and the month it's from. A loan or a card always counts as owed. `complete: false` when an account counts but its balance isn't known (a bank export with no running balance): it's listed under `unknown`, left out of the total, never counted as 0; `do money account-update <id> --balance \u2026 --balance-at \u2026` states it.",
|
|
49939
|
+
notes: ". total, have and owe this month, and liquid beside locked (a pension, or an account locked until a day ahead; each account says `locked`, `lockedUntil` and a pension's `pensionSubtype`); points per month (oldest first, total null before any value); accounts biggest first with value, source (balance \xB7 ledger \xB7 manual) and the month it's from. A loan or a card always counts as owed. `complete: false` when an account counts but its balance isn't known (a bank export with no running balance): it's listed under `unknown`, left out of the total, never counted as 0; `do money account-update <id> --balance \u2026 --balance-at \u2026` states it.",
|
|
49535
49940
|
run: async ({ flags, requireClient }) => ({
|
|
49536
49941
|
data: await requireClient().get(pathOf(moneyOperations.netWorthRead), {
|
|
49537
49942
|
query: { scope: flagString(flags, "scope"), months: flagString(flags, "months") }
|
|
@@ -50116,6 +50521,139 @@ var commands33 = [
|
|
|
50116
50521
|
// src/commands/people.ts
|
|
50117
50522
|
import { readFileSync as readFileSync9 } from "fs";
|
|
50118
50523
|
import { basename as basename6 } from "path";
|
|
50524
|
+
|
|
50525
|
+
// ../../packages/domain/src/family-tree.ts
|
|
50526
|
+
function lifeYears(p) {
|
|
50527
|
+
if (p.birthYear && p.deathYear)
|
|
50528
|
+
return `${p.birthYear}\u2013${p.deathYear}`;
|
|
50529
|
+
if (p.deathYear)
|
|
50530
|
+
return `\u2013${p.deathYear}`;
|
|
50531
|
+
return p.birthYear ? String(p.birthYear) : "";
|
|
50532
|
+
}
|
|
50533
|
+
function greats(generation) {
|
|
50534
|
+
if (generation < 3)
|
|
50535
|
+
return "";
|
|
50536
|
+
const n = generation - 2;
|
|
50537
|
+
return n === 1 ? "great-" : `${n}\xD7 great-`;
|
|
50538
|
+
}
|
|
50539
|
+
function generationLabel(generation) {
|
|
50540
|
+
if (generation === 0)
|
|
50541
|
+
return "You";
|
|
50542
|
+
if (generation === 1)
|
|
50543
|
+
return "Parents";
|
|
50544
|
+
const word = `${greats(generation)}grandparents`;
|
|
50545
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
50546
|
+
}
|
|
50547
|
+
function ancestorTitle(n) {
|
|
50548
|
+
const generation = ahnentafelGeneration(n);
|
|
50549
|
+
if (generation === 0)
|
|
50550
|
+
return "You";
|
|
50551
|
+
const male = n % 2 === 0;
|
|
50552
|
+
if (generation === 1)
|
|
50553
|
+
return male ? "Father" : "Mother";
|
|
50554
|
+
const word = `${greats(generation)}grand${male ? "father" : "mother"}`;
|
|
50555
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
50556
|
+
}
|
|
50557
|
+
function pedigreeOutline(view) {
|
|
50558
|
+
const people = new Map(view.people.map((p) => [p.ref, p]));
|
|
50559
|
+
const slots = new Map(view.ancestors.map((a) => [a.n, a]));
|
|
50560
|
+
const rows = [];
|
|
50561
|
+
const visit = (n) => {
|
|
50562
|
+
const slot = slots.get(n);
|
|
50563
|
+
const person = slot ? people.get(slot.ref) : undefined;
|
|
50564
|
+
if (!slot || !person)
|
|
50565
|
+
return;
|
|
50566
|
+
const parents = [2 * n, 2 * n + 1].filter((m) => slots.has(m));
|
|
50567
|
+
rows.push({
|
|
50568
|
+
n,
|
|
50569
|
+
generation: slot.generation,
|
|
50570
|
+
title: ancestorTitle(n),
|
|
50571
|
+
person,
|
|
50572
|
+
repeatOf: slot.repeatOf,
|
|
50573
|
+
parents
|
|
50574
|
+
});
|
|
50575
|
+
for (const m of parents)
|
|
50576
|
+
visit(m);
|
|
50577
|
+
};
|
|
50578
|
+
visit(1);
|
|
50579
|
+
return rows;
|
|
50580
|
+
}
|
|
50581
|
+
function pedigreeText(view) {
|
|
50582
|
+
const lines = pedigreeOutline(view).map((r) => {
|
|
50583
|
+
const years = lifeYears(r.person);
|
|
50584
|
+
const name = years ? `${r.person.name} (${years})` : r.person.name;
|
|
50585
|
+
const again = r.repeatOf ? ` \xB7 also #${r.repeatOf}, line shown there` : "";
|
|
50586
|
+
return `${" ".repeat(r.generation)}${r.title} \xB7 ${name} \xB7 ${r.person.ref}${again}`;
|
|
50587
|
+
});
|
|
50588
|
+
const deepest = generationLabel(view.generations).toLowerCase();
|
|
50589
|
+
const more = view.deeper ? " The tree goes further back: ask for a bigger depth." : "";
|
|
50590
|
+
return [...lines, "", `${view.generations} generations back (to the ${deepest}).${more}`].join(`
|
|
50591
|
+
`);
|
|
50592
|
+
}
|
|
50593
|
+
|
|
50594
|
+
// src/commands/people.ts
|
|
50595
|
+
var EDIT_NOTE = "Answers with a receipt: edit.id (for `do people tree undo`), the changes person by person, and the person after. Edits are kept over a new import.";
|
|
50596
|
+
var PERSON_FLAGS = [
|
|
50597
|
+
{ name: "given", type: "string", description: "First name(s)." },
|
|
50598
|
+
{ name: "surname", type: "string", description: "Surname." },
|
|
50599
|
+
{ name: "sex", type: "string", description: "M or F." },
|
|
50600
|
+
{ name: "born", type: "string", description: "Birth date: YYYY or YYYY-MM-DD." },
|
|
50601
|
+
{ name: "born-place", type: "string", description: "Where they were born." },
|
|
50602
|
+
{ name: "died", type: "string", description: "Death date: YYYY or YYYY-MM-DD." },
|
|
50603
|
+
{ name: "died-place", type: "string", description: "Where they died." }
|
|
50604
|
+
];
|
|
50605
|
+
var PERSON_FLAG_FIELDS = {
|
|
50606
|
+
given: "given",
|
|
50607
|
+
surname: "surname",
|
|
50608
|
+
sex: "sex",
|
|
50609
|
+
born: "birthDate",
|
|
50610
|
+
"born-place": "birthPlace",
|
|
50611
|
+
died: "deathDate",
|
|
50612
|
+
"died-place": "deathPlace"
|
|
50613
|
+
};
|
|
50614
|
+
var FACT_FLAGS = [
|
|
50615
|
+
{ name: "date", type: "string", description: "The date as written (ABT 1720, 1801-03-02)." },
|
|
50616
|
+
{ name: "place", type: "string", description: "Where." },
|
|
50617
|
+
{ name: "value", type: "string", description: "The value or note (farmer, a line of text)." }
|
|
50618
|
+
];
|
|
50619
|
+
var CLEAR_FLAG = {
|
|
50620
|
+
name: "clear",
|
|
50621
|
+
type: "string",
|
|
50622
|
+
description: "Fields to clear, comma-separated."
|
|
50623
|
+
};
|
|
50624
|
+
var FACT_ARGS = [
|
|
50625
|
+
{ name: "ref", required: true, description: "Their ref in the tree." },
|
|
50626
|
+
{ name: "factId", required: true, description: "The fact's id (OCCU-1, f-\u2026)." }
|
|
50627
|
+
];
|
|
50628
|
+
var LINK_ARGS = [
|
|
50629
|
+
{ name: "kind", required: true, description: "parent, child or partner." },
|
|
50630
|
+
{ name: "ref", required: true, description: "The person's ref." },
|
|
50631
|
+
{ name: "other", required: true, description: "Their parent's, child's or partner's ref." }
|
|
50632
|
+
];
|
|
50633
|
+
var pick2 = (flags, fields) => Object.fromEntries(Object.entries(fields).flatMap(([flag, field]) => {
|
|
50634
|
+
const v = flagString(flags, flag);
|
|
50635
|
+
return v === undefined ? [] : [[field, v]];
|
|
50636
|
+
}));
|
|
50637
|
+
var personFields = (flags) => pick2(flags, { name: "name", ...PERSON_FLAG_FIELDS });
|
|
50638
|
+
var factFields = (flags) => pick2(flags, { date: "date", place: "place", value: "value" });
|
|
50639
|
+
var clears = (flags) => Object.fromEntries((flagString(flags, "clear") ?? "").split(",").map((f) => f.trim()).filter(Boolean).map((f) => [f, null]));
|
|
50640
|
+
var factParams = (args) => ({
|
|
50641
|
+
ref: parseInput(familyTreeRefSchema, requireArg(args, 0, "ref")),
|
|
50642
|
+
factId: parseInput(familyTreeFactIdSchema, requireArg(args, 1, "fact id"))
|
|
50643
|
+
});
|
|
50644
|
+
function linkBody(args) {
|
|
50645
|
+
const kind = requireArg(args, 0, "kind");
|
|
50646
|
+
const ref = requireArg(args, 1, "ref");
|
|
50647
|
+
const other = requireArg(args, 2, "other");
|
|
50648
|
+
if (kind === "parent")
|
|
50649
|
+
return parseInput(familyTreeLinkInputSchema, { kind, parent: other, child: ref });
|
|
50650
|
+
if (kind === "child") {
|
|
50651
|
+
return parseInput(familyTreeLinkInputSchema, { kind: "parent", parent: ref, child: other });
|
|
50652
|
+
}
|
|
50653
|
+
if (kind === "partner")
|
|
50654
|
+
return parseInput(familyTreeLinkInputSchema, { kind, a: ref, b: other });
|
|
50655
|
+
throw usageError("the kind is parent, child or partner");
|
|
50656
|
+
}
|
|
50119
50657
|
var commands34 = [
|
|
50120
50658
|
{
|
|
50121
50659
|
spec: {
|
|
@@ -50471,6 +51009,34 @@ var commands34 = [
|
|
|
50471
51009
|
};
|
|
50472
51010
|
}
|
|
50473
51011
|
},
|
|
51012
|
+
{
|
|
51013
|
+
spec: {
|
|
51014
|
+
path: ["people", "tree", "ancestors"],
|
|
51015
|
+
summary: "Your direct line back, as a text pedigree: as far as --depth, or all of it.",
|
|
51016
|
+
args: [],
|
|
51017
|
+
flags: [
|
|
51018
|
+
{
|
|
51019
|
+
name: "depth",
|
|
51020
|
+
type: "string",
|
|
51021
|
+
description: "How many generations back (1\u201350; default all)."
|
|
51022
|
+
},
|
|
51023
|
+
{ name: "ref", type: "string", description: "Whose ancestors (default: you in the tree)." }
|
|
51024
|
+
],
|
|
51025
|
+
scopes: [...familyTreeOperations.ancestors.scopes],
|
|
51026
|
+
returns: "FamilyTreeAncestors",
|
|
51027
|
+
notes: "Each line is who they are to you, the name, the years and their ref, indented by generation; father's line before mother's. --json gives the slots by Ahnentafel number (you 1, a father 2n, a mother 2n+1)."
|
|
51028
|
+
},
|
|
51029
|
+
run: async ({ flags, requireClient }) => {
|
|
51030
|
+
const depth = flagString(flags, "depth");
|
|
51031
|
+
const ref = flagString(flags, "ref");
|
|
51032
|
+
const query = { ...depth ? { depth } : {}, ...ref ? { ref } : {} };
|
|
51033
|
+
parseInput(familyTreeAncestorsQuerySchema, query);
|
|
51034
|
+
return {
|
|
51035
|
+
data: await requireClient().get(pathOf(familyTreeOperations.ancestors), { query }),
|
|
51036
|
+
render: (data) => pedigreeText(data)
|
|
51037
|
+
};
|
|
51038
|
+
}
|
|
51039
|
+
},
|
|
50474
51040
|
{
|
|
50475
51041
|
spec: {
|
|
50476
51042
|
path: ["people", "tree", "list"],
|
|
@@ -50555,6 +51121,223 @@ var commands34 = [
|
|
|
50555
51121
|
render: renderRecord
|
|
50556
51122
|
};
|
|
50557
51123
|
}
|
|
51124
|
+
},
|
|
51125
|
+
{
|
|
51126
|
+
spec: {
|
|
51127
|
+
path: ["people", "tree", "person"],
|
|
51128
|
+
summary: "One person in your tree: facts (with their ids), parents, partners, children.",
|
|
51129
|
+
args: [{ name: "ref", required: true, description: "Their ref in the tree (I12)." }],
|
|
51130
|
+
flags: [],
|
|
51131
|
+
scopes: [...familyTreeOperations.person.scopes],
|
|
51132
|
+
returns: "FamilyTreePersonDetail"
|
|
51133
|
+
},
|
|
51134
|
+
run: async ({ args, requireClient }) => {
|
|
51135
|
+
const ref = parseInput(familyTreeRefSchema, requireArg(args, 0, "ref"));
|
|
51136
|
+
return {
|
|
51137
|
+
data: await requireClient().get(pathOf(familyTreeOperations.person, { ref })),
|
|
51138
|
+
render: renderRecord
|
|
51139
|
+
};
|
|
51140
|
+
}
|
|
51141
|
+
},
|
|
51142
|
+
{
|
|
51143
|
+
spec: {
|
|
51144
|
+
path: ["people", "tree", "person", "add"],
|
|
51145
|
+
summary: "Add someone your tree is missing.",
|
|
51146
|
+
args: [{ name: "name", required: true, description: "Their full name." }],
|
|
51147
|
+
flags: PERSON_FLAGS,
|
|
51148
|
+
scopes: [...familyTreeOperations.addPerson.scopes],
|
|
51149
|
+
returns: "FamilyTreeEditReceipt",
|
|
51150
|
+
notes: `${EDIT_NOTE} They get a new ref (P\u2026): link them next (\`do people tree link parent <child> <parent>\`).`
|
|
51151
|
+
},
|
|
51152
|
+
run: async ({ args, flags, requireClient }) => {
|
|
51153
|
+
const body = parseInput(addFamilyTreePersonInputSchema, {
|
|
51154
|
+
...personFields(flags),
|
|
51155
|
+
name: args.join(" ").trim() || requireArg(args, 0, "name")
|
|
51156
|
+
});
|
|
51157
|
+
return {
|
|
51158
|
+
data: await requireClient().post(pathOf(familyTreeOperations.addPerson), body),
|
|
51159
|
+
render: renderRecord
|
|
51160
|
+
};
|
|
51161
|
+
}
|
|
51162
|
+
},
|
|
51163
|
+
{
|
|
51164
|
+
spec: {
|
|
51165
|
+
path: ["people", "tree", "person", "edit"],
|
|
51166
|
+
summary: "Change a person in your tree: name, sex, birth, death.",
|
|
51167
|
+
args: [{ name: "ref", required: true, description: "Their ref in the tree." }],
|
|
51168
|
+
flags: [
|
|
51169
|
+
{ name: "name", type: "string", description: "Their full name." },
|
|
51170
|
+
...PERSON_FLAGS,
|
|
51171
|
+
CLEAR_FLAG
|
|
51172
|
+
],
|
|
51173
|
+
scopes: [...familyTreeOperations.editPerson.scopes],
|
|
51174
|
+
returns: "FamilyTreeEditReceipt",
|
|
51175
|
+
notes: `${EDIT_NOTE} --clear takes field names (birthPlace,deathDate,\u2026).`
|
|
51176
|
+
},
|
|
51177
|
+
run: async ({ args, flags, requireClient }) => {
|
|
51178
|
+
const ref = parseInput(familyTreeRefSchema, requireArg(args, 0, "ref"));
|
|
51179
|
+
const body = parseInput(editFamilyTreePersonInputSchema, {
|
|
51180
|
+
...personFields(flags),
|
|
51181
|
+
...clears(flags),
|
|
51182
|
+
...readDataFlag(flags)
|
|
51183
|
+
});
|
|
51184
|
+
return {
|
|
51185
|
+
data: await requireClient().patch(pathOf(familyTreeOperations.editPerson, { ref }), body),
|
|
51186
|
+
render: renderRecord
|
|
51187
|
+
};
|
|
51188
|
+
}
|
|
51189
|
+
},
|
|
51190
|
+
{
|
|
51191
|
+
spec: {
|
|
51192
|
+
path: ["people", "tree", "person", "remove"],
|
|
51193
|
+
summary: "Take a person out of your tree (and out of their families).",
|
|
51194
|
+
args: [{ name: "ref", required: true, description: "Their ref in the tree." }],
|
|
51195
|
+
flags: [],
|
|
51196
|
+
scopes: [...familyTreeOperations.removePerson.scopes],
|
|
51197
|
+
returns: "FamilyTreeEditReceipt",
|
|
51198
|
+
notes: `${EDIT_NOTE} An agent's key waits for the member's yes, as every delete does.`
|
|
51199
|
+
},
|
|
51200
|
+
run: async ({ args, requireClient }) => {
|
|
51201
|
+
const ref = parseInput(familyTreeRefSchema, requireArg(args, 0, "ref"));
|
|
51202
|
+
return {
|
|
51203
|
+
data: await requireClient().delete(pathOf(familyTreeOperations.removePerson, { ref })),
|
|
51204
|
+
render: renderRecord
|
|
51205
|
+
};
|
|
51206
|
+
}
|
|
51207
|
+
},
|
|
51208
|
+
{
|
|
51209
|
+
spec: {
|
|
51210
|
+
path: ["people", "tree", "fact", "add"],
|
|
51211
|
+
summary: "Add a fact to a person: an occupation, a residence, a note.",
|
|
51212
|
+
args: [
|
|
51213
|
+
{ name: "ref", required: true, description: "Their ref in the tree." },
|
|
51214
|
+
{
|
|
51215
|
+
name: "type",
|
|
51216
|
+
required: true,
|
|
51217
|
+
description: "What kind (occupation, residence, note, \u2026)."
|
|
51218
|
+
}
|
|
51219
|
+
],
|
|
51220
|
+
flags: FACT_FLAGS,
|
|
51221
|
+
scopes: [...familyTreeOperations.addFact.scopes],
|
|
51222
|
+
returns: "FamilyTreeEditReceipt",
|
|
51223
|
+
notes: EDIT_NOTE
|
|
51224
|
+
},
|
|
51225
|
+
run: async ({ args, flags, requireClient }) => {
|
|
51226
|
+
const ref = parseInput(familyTreeRefSchema, requireArg(args, 0, "ref"));
|
|
51227
|
+
const body = parseInput(addFamilyTreeFactInputSchema, {
|
|
51228
|
+
type: requireArg(args, 1, "type"),
|
|
51229
|
+
...factFields(flags)
|
|
51230
|
+
});
|
|
51231
|
+
return {
|
|
51232
|
+
data: await requireClient().post(pathOf(familyTreeOperations.addFact, { ref }), body),
|
|
51233
|
+
render: renderRecord
|
|
51234
|
+
};
|
|
51235
|
+
}
|
|
51236
|
+
},
|
|
51237
|
+
{
|
|
51238
|
+
spec: {
|
|
51239
|
+
path: ["people", "tree", "fact", "edit"],
|
|
51240
|
+
summary: "Change one of a person's facts.",
|
|
51241
|
+
args: FACT_ARGS,
|
|
51242
|
+
flags: [
|
|
51243
|
+
{ name: "type", type: "string", description: "What kind of fact." },
|
|
51244
|
+
...FACT_FLAGS,
|
|
51245
|
+
CLEAR_FLAG
|
|
51246
|
+
],
|
|
51247
|
+
scopes: [...familyTreeOperations.editFact.scopes],
|
|
51248
|
+
returns: "FamilyTreeEditReceipt",
|
|
51249
|
+
notes: `${EDIT_NOTE} --clear takes field names (date,place,value).`
|
|
51250
|
+
},
|
|
51251
|
+
run: async ({ args, flags, requireClient }) => {
|
|
51252
|
+
const params = factParams(args);
|
|
51253
|
+
const type = flagString(flags, "type");
|
|
51254
|
+
const body = parseInput(editFamilyTreeFactInputSchema, {
|
|
51255
|
+
...type ? { type } : {},
|
|
51256
|
+
...factFields(flags),
|
|
51257
|
+
...clears(flags)
|
|
51258
|
+
});
|
|
51259
|
+
return {
|
|
51260
|
+
data: await requireClient().patch(pathOf(familyTreeOperations.editFact, params), body),
|
|
51261
|
+
render: renderRecord
|
|
51262
|
+
};
|
|
51263
|
+
}
|
|
51264
|
+
},
|
|
51265
|
+
{
|
|
51266
|
+
spec: {
|
|
51267
|
+
path: ["people", "tree", "fact", "remove"],
|
|
51268
|
+
summary: "Remove one of a person's facts.",
|
|
51269
|
+
args: FACT_ARGS,
|
|
51270
|
+
flags: [],
|
|
51271
|
+
scopes: [...familyTreeOperations.removeFact.scopes],
|
|
51272
|
+
returns: "FamilyTreeEditReceipt",
|
|
51273
|
+
notes: `${EDIT_NOTE} An agent's key waits for the member's yes, as every delete does.`
|
|
51274
|
+
},
|
|
51275
|
+
run: async ({ args, requireClient }) => {
|
|
51276
|
+
return {
|
|
51277
|
+
data: await requireClient().delete(pathOf(familyTreeOperations.removeFact, factParams(args))),
|
|
51278
|
+
render: renderRecord
|
|
51279
|
+
};
|
|
51280
|
+
}
|
|
51281
|
+
},
|
|
51282
|
+
{
|
|
51283
|
+
spec: {
|
|
51284
|
+
path: ["people", "tree", "link"],
|
|
51285
|
+
summary: "Link two people: give someone a parent or a child, or a partner.",
|
|
51286
|
+
args: LINK_ARGS,
|
|
51287
|
+
flags: [],
|
|
51288
|
+
scopes: [...familyTreeOperations.link.scopes],
|
|
51289
|
+
returns: "FamilyTreeEditReceipt",
|
|
51290
|
+
notes: `${EDIT_NOTE} A child has at most two parents.`
|
|
51291
|
+
},
|
|
51292
|
+
run: async ({ args, requireClient }) => ({
|
|
51293
|
+
data: await requireClient().post(pathOf(familyTreeOperations.link), linkBody(args)),
|
|
51294
|
+
render: renderRecord
|
|
51295
|
+
})
|
|
51296
|
+
},
|
|
51297
|
+
{
|
|
51298
|
+
spec: {
|
|
51299
|
+
path: ["people", "tree", "unlink"],
|
|
51300
|
+
summary: "Undo a parent, child or partner link.",
|
|
51301
|
+
args: LINK_ARGS,
|
|
51302
|
+
flags: [],
|
|
51303
|
+
scopes: [...familyTreeOperations.unlink.scopes],
|
|
51304
|
+
returns: "FamilyTreeEditReceipt",
|
|
51305
|
+
notes: `${EDIT_NOTE} Partners with children together: unlink a parent first.`
|
|
51306
|
+
},
|
|
51307
|
+
run: async ({ args, requireClient }) => ({
|
|
51308
|
+
data: await requireClient().post(pathOf(familyTreeOperations.unlink), linkBody(args)),
|
|
51309
|
+
render: renderRecord
|
|
51310
|
+
})
|
|
51311
|
+
},
|
|
51312
|
+
{
|
|
51313
|
+
spec: {
|
|
51314
|
+
path: ["people", "tree", "edits"],
|
|
51315
|
+
summary: "Your edits to the tree, oldest first; the ones the current file has no room for are flagged.",
|
|
51316
|
+
args: [],
|
|
51317
|
+
flags: [],
|
|
51318
|
+
scopes: [...familyTreeOperations.edits.scopes],
|
|
51319
|
+
returns: "FamilyTreeEdits",
|
|
51320
|
+
notes: "Edits are kept over every new import: one whose person isn't in the new file is applied: false with its reason, and applies again when a later file has them."
|
|
51321
|
+
},
|
|
51322
|
+
run: async ({ requireClient }) => ({
|
|
51323
|
+
data: await requireClient().get(pathOf(familyTreeOperations.edits)),
|
|
51324
|
+
render: renderRecord
|
|
51325
|
+
})
|
|
51326
|
+
},
|
|
51327
|
+
{
|
|
51328
|
+
spec: {
|
|
51329
|
+
path: ["people", "tree", "undo"],
|
|
51330
|
+
summary: "Undo one edit to the tree, by the id its receipt gave.",
|
|
51331
|
+
args: [{ name: "editId", required: true, description: "The receipt's edit.id." }],
|
|
51332
|
+
flags: [],
|
|
51333
|
+
scopes: [...familyTreeOperations.undo.scopes],
|
|
51334
|
+
returns: "FamilyTreeUndoResult",
|
|
51335
|
+
notes: "skipped lists the edits that leaned on it (a fact on a person it added)."
|
|
51336
|
+
},
|
|
51337
|
+
run: async ({ args, requireClient }) => ({
|
|
51338
|
+
data: await requireClient().post(pathOf(familyTreeOperations.undo, { id: requireArg(args, 0, "edit id") }), {}),
|
|
51339
|
+
render: renderRecord
|
|
51340
|
+
})
|
|
50558
51341
|
}
|
|
50559
51342
|
];
|
|
50560
51343
|
|
|
@@ -53398,38 +54181,6 @@ var FEATURE_COMMANDS = [
|
|
|
53398
54181
|
// src/registry.ts
|
|
53399
54182
|
var COMMANDS = FEATURE_COMMANDS;
|
|
53400
54183
|
var COMMAND_SPECS = COMMANDS.map((c) => c.spec);
|
|
53401
|
-
var GLOBAL_FLAGS = [
|
|
53402
|
-
{ name: "json", type: "boolean", description: "Emit JSON (automatic when stdout is piped)." },
|
|
53403
|
-
{ name: "quiet", type: "boolean", description: "Suppress human output (errors still report)." },
|
|
53404
|
-
{ name: "verbose", type: "boolean", description: "Extra diagnostics on stderr." },
|
|
53405
|
-
{
|
|
53406
|
-
name: "api-key",
|
|
53407
|
-
type: "string",
|
|
53408
|
-
description: "API key; overrides DO_API_KEY and the config file."
|
|
53409
|
-
},
|
|
53410
|
-
{
|
|
53411
|
-
name: "url",
|
|
53412
|
-
type: "string",
|
|
53413
|
-
description: "API origin; overrides DO_API_URL and the config file."
|
|
53414
|
-
},
|
|
53415
|
-
{ name: "help", type: "boolean", description: "Show help for the command." },
|
|
53416
|
-
{ name: "version", type: "boolean", description: "Print the CLI version." },
|
|
53417
|
-
{
|
|
53418
|
-
name: "data",
|
|
53419
|
-
type: "string",
|
|
53420
|
-
description: `JSON object body for create/update (e.g. --data '{"title":"Buy milk"}').`
|
|
53421
|
-
},
|
|
53422
|
-
{
|
|
53423
|
-
name: "allow-destructive",
|
|
53424
|
-
type: "boolean",
|
|
53425
|
-
description: "Permit a destructive verb (delete/revoke/remove-member). Off by default \u2014 a guardrail for autonomous agents."
|
|
53426
|
-
},
|
|
53427
|
-
{
|
|
53428
|
-
name: "dry-run",
|
|
53429
|
-
type: "boolean",
|
|
53430
|
-
description: "Preview a create/update/delete/archive (prints before/after) without writing \u2014 for agent-driven cleanup."
|
|
53431
|
-
}
|
|
53432
|
-
];
|
|
53433
54184
|
function matchCommand(positionals) {
|
|
53434
54185
|
for (const len of [4, 3, 2, 1]) {
|
|
53435
54186
|
const candidate = positionals.slice(0, len);
|
package/package.json
CHANGED