@robbeverhelst/do 1.308.0 → 1.308.2

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.
Files changed (2) hide show
  1. package/dist/do.js +595 -153
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -4630,8 +4630,16 @@ var animals = pgTable("animals", {
4630
4630
  }, (t) => [index("animals_tenant_idx").on(t.tenantId), index("animals_species_idx").on(t.speciesId)]);
4631
4631
  var animalLostCards = pgTable("animal_lost_cards", {
4632
4632
  animalId: text("animal_id").primaryKey().references(() => animals.id, { onDelete: "cascade" }),
4633
+ id: text("id").notNull().unique(),
4633
4634
  tenantId: text("tenant_id").notNull().references(() => tenants.id),
4634
- token: text("token").notNull().unique(),
4635
+ token: text("token").unique(),
4636
+ tokenHash: text("token_hash").notNull().unique(),
4637
+ tokenSealed: text("token_sealed"),
4638
+ expiresAt: timestamp("expires_at", { withTimezone: true }),
4639
+ revokedAt: timestamp("revoked_at", { withTimezone: true }),
4640
+ opens: integer("opens").notNull().default(0),
4641
+ lastOpenedAt: timestamp("last_opened_at", { withTimezone: true }),
4642
+ createdByUserId: text("created_by_user_id").references(() => users.id),
4635
4643
  photoFileId: text("photo_file_id"),
4636
4644
  phone: text("phone"),
4637
4645
  showChipped: boolean("show_chipped").notNull().default(false),
@@ -5721,6 +5729,8 @@ var invites = pgTable("invites", {
5721
5729
  type: inviteType("type").notNull(),
5722
5730
  email: text("email"),
5723
5731
  token: text("token").unique(),
5732
+ tokenHash: text("token_hash").unique(),
5733
+ tokenSealed: text("token_sealed"),
5724
5734
  role: baseRole("role").notNull().default("member"),
5725
5735
  customRoleId: text("custom_role_id").references(() => roles.id),
5726
5736
  invitedByUserId: text("invited_by_user_id").references(() => users.id),
@@ -5749,7 +5759,7 @@ var shareLinks = pgTable("share_links", {
5749
5759
  id: text("id").primaryKey(),
5750
5760
  tenantId: text("tenant_id").notNull().references(() => tenants.id),
5751
5761
  nodeId: text("node_id").notNull().references(() => nodes.id),
5752
- token: text("token").notNull().unique(),
5762
+ tokenHash: text("token_hash").notNull().unique(),
5753
5763
  capability: capability("capability").notNull().default("view"),
5754
5764
  createdByUserId: text("created_by_user_id").references(() => users.id),
5755
5765
  expiresAt: timestamp("expires_at", { withTimezone: true }),
@@ -29358,7 +29368,7 @@ var inviteSchema = createSelectSchema(invites, {
29358
29368
  acceptedAt: exports_coerce.date().nullable(),
29359
29369
  createdAt: exports_coerce.date(),
29360
29370
  updatedAt: exports_coerce.date()
29361
- });
29371
+ }).omit({ tokenHash: true, tokenSealed: true });
29362
29372
  var createInviteInputSchema = object({
29363
29373
  id: ulidSchema,
29364
29374
  type: _enum2(["email", "link", "guest"]),
@@ -29423,7 +29433,7 @@ var shareLinkSchema = createSelectSchema(shareLinks, {
29423
29433
  createdAt: exports_coerce.date(),
29424
29434
  updatedAt: exports_coerce.date()
29425
29435
  });
29426
- var shareLinkPublicSchema = shareLinkSchema.omit({ token: true });
29436
+ var shareLinkPublicSchema = shareLinkSchema.omit({ tokenHash: true });
29427
29437
  var createShareLinkInputSchema = createInsertSchema(shareLinks, {
29428
29438
  id: ulidSchema,
29429
29439
  nodeId: ulidSchema,
@@ -29431,7 +29441,7 @@ var createShareLinkInputSchema = createInsertSchema(shareLinks, {
29431
29441
  expiresAt: exports_coerce.date().nullish()
29432
29442
  }).omit({
29433
29443
  tenantId: true,
29434
- token: true,
29444
+ tokenHash: true,
29435
29445
  createdByUserId: true,
29436
29446
  revokedAt: true,
29437
29447
  createdAt: true,
@@ -31255,6 +31265,27 @@ var commentOperations = defineOperations({
31255
31265
  }
31256
31266
  });
31257
31267
 
31268
+ // ../../packages/schema/src/operations/contacts.ts
31269
+ var contactOperations = defineOperations({
31270
+ ...crudOperations({
31271
+ segment: "contacts",
31272
+ tag: "Contacts",
31273
+ noun: "contact",
31274
+ read: "contacts:read",
31275
+ write: "contacts:write",
31276
+ create: createContactInputSchema,
31277
+ update: updateContactInputSchema,
31278
+ listQuery: [
31279
+ { name: "kind", description: "Only `person` or only `organization` contacts." },
31280
+ {
31281
+ name: "role",
31282
+ description: "Only contacts with this role (`insurer`, `telecom`, `gp`, `vet`, \u2026)."
31283
+ }
31284
+ ],
31285
+ listQuerySchema: contactsQuerySchema
31286
+ })
31287
+ });
31288
+
31258
31289
  // ../../packages/schema/src/operations/days.ts
31259
31290
  var pathParams = {
31260
31291
  date: {
@@ -32275,6 +32306,24 @@ var householdOperations = defineOperations({
32275
32306
  })
32276
32307
  });
32277
32308
 
32309
+ // ../../packages/schema/src/operations/incidents.ts
32310
+ var incidentOperations = defineOperations({
32311
+ ...crudOperations({
32312
+ segment: "incidents",
32313
+ tag: "Incidents",
32314
+ noun: "incident",
32315
+ read: "documents:read",
32316
+ write: "documents:write",
32317
+ create: createIncidentInputSchema,
32318
+ update: updateIncidentInputSchema,
32319
+ listQuery: [
32320
+ { name: "status", description: "Only this status (`open`, `claimed`, `closed`)." },
32321
+ { name: "policyId", description: "Only the incidents under this policy (a document id)." }
32322
+ ],
32323
+ listQuerySchema: incidentsQuerySchema
32324
+ })
32325
+ });
32326
+
32278
32327
  // ../../packages/schema/src/operations/inventory-items.ts
32279
32328
  var inventoryItemOperations = defineOperations({
32280
32329
  ...crudOperations({
@@ -32329,6 +32378,389 @@ var inviteOperations = defineOperations({
32329
32378
  }
32330
32379
  });
32331
32380
 
32381
+ // ../../packages/schema/src/operations/journal.ts
32382
+ var journalOperations = defineOperations({
32383
+ range: {
32384
+ method: "get",
32385
+ path: "/journal/days",
32386
+ scopes: ["journal:read"],
32387
+ tag: "Journal",
32388
+ summary: "Journal days in a range",
32389
+ description: "Journal home's read: every day in `[from, to]` (at most 366 days, the key's member's civil days) that has a Journal day or a Metric value, newest first. Each carries the Journal day's title, status, highlights, tags and a plain excerpt of its summary when there is one, and that day's Metric values by key (`rating`, `mood`, `sleep`, \u2026), with how many photos and entries it holds and the first nodes it links to. The Journal is owner-only: a key reads only its own member's days.",
32390
+ afterScopes: "`*` does not cover it.",
32391
+ query: [
32392
+ { name: "from", description: "The first day, `YYYY-MM-DD`.", format: "date" },
32393
+ { name: "to", description: "The last day, `YYYY-MM-DD`.", format: "date" }
32394
+ ],
32395
+ querySchema: journalRangeQuerySchema,
32396
+ status: 200,
32397
+ response: { description: "The days.", view: "JournalDayList" },
32398
+ errors: { 400: true },
32399
+ operationId: "list_journal_days"
32400
+ },
32401
+ search: {
32402
+ method: "get",
32403
+ path: "/journal/search",
32404
+ scopes: ["journal:read"],
32405
+ tag: "Journal",
32406
+ summary: "Search Journal days",
32407
+ description: "The key's member's days that answer `q` (at least 2 characters), best match first, at most 30, each shaped like a day of `GET /journal/days`. Keyword and date, no model: the words left after small words (each with a few that mean the same, English and Dutch) scored where they're found (one-liner and tags, then highlights, then the summary and entries); a date phrase (`in March`, `last week`, `14 October`, `2026-02`, `yesterday`) narrows to its window, or alone reads it; a question leaning on a heavy or a bright day (`rough`, `tired`, `a great day`) adds the days rated 4 or less / 8 or more (within the window, else the last year). `understood` says how it was read. Owner-only: another member's days never match.",
32408
+ afterScopes: "`*` does not cover it.",
32409
+ query: [{ name: "q", description: "What to look for." }],
32410
+ querySchema: journalSearchQuerySchema,
32411
+ status: 200,
32412
+ response: { description: "The matching days.", view: "JournalSearch" },
32413
+ errors: { 400: true }
32414
+ },
32415
+ day: {
32416
+ method: "get",
32417
+ path: "/journal/days/{date}",
32418
+ scopes: ["journal:read"],
32419
+ tag: "Journal",
32420
+ summary: "A Journal day",
32421
+ description: "One civil day of the key's member: the Journal day (its summary, entries, highlights and tags; null when none was written yet) and what happened that day, read live: the Metric values, what landed (tasks and time blocks, by the member or an agent), the habits due and whether they happened, the calendar events, and the nodes the day links to.",
32422
+ afterScopes: "`*` does not cover it.",
32423
+ pathParams: {
32424
+ date: { description: "The day, `YYYY-MM-DD`.", format: "date" }
32425
+ },
32426
+ pathParamSchemas: { date: journalDateSchema },
32427
+ status: 200,
32428
+ response: { description: "The day.", view: "JournalSnapshot" },
32429
+ errors: { 400: true },
32430
+ operationId: "get_journal_day"
32431
+ },
32432
+ like: {
32433
+ method: "get",
32434
+ path: "/journal/days/{date}/like",
32435
+ scopes: ["journal:read"],
32436
+ tag: "Journal",
32437
+ summary: "Days like this one",
32438
+ description: "\"When did I feel like this before?\" (`journal_like`): the key's member's other days most like this one, by Postgres full text over each day's one-liner, highlights, tags, summary and entries, queried with this day's distinctive words (each widened by a few that mean the same, English and Dutch), then ranked by the words and tags they share and how close the rating is. Each day is shaped like the range's, with `shared` (this day's words the other has) and `sharedTags`; `words` is how this day was read. Words, not meanings: there are no embeddings. A day hidden from memories is never offered. A day with nothing written is a 404.",
32439
+ afterScopes: "`*` does not cover it.",
32440
+ pathParams: {
32441
+ date: { description: "The day, `YYYY-MM-DD`.", format: "date" }
32442
+ },
32443
+ pathParamSchemas: { date: journalDateSchema },
32444
+ query: [{ name: "limit", description: "At most this many days (1\u201310, 5 by default)." }],
32445
+ querySchema: journalLikeQuerySchema,
32446
+ status: 200,
32447
+ response: { description: "The days most like it.", view: "JournalLike" },
32448
+ errors: { 400: true, 404: true }
32449
+ },
32450
+ export: {
32451
+ method: "get",
32452
+ path: "/journal/export",
32453
+ scopes: ["journal:export"],
32454
+ tag: "Journal",
32455
+ summary: "Export the Journal as markdown",
32456
+ description: "Every Journal day of the key's member changed at or after `since` (all without it) as a markdown file: `path` (`YYYY/YYYY-MM-DD.md`), front matter (date, title, status, Metric values, tags, `do_id`), the summary, highlights, entries and links as `do://node/<id>`. Links into other sensitive domains stay out. Store `nextSince` and pass it next time. The agent's runtime pulls this nightly into its memory; do is the record and the files are read-only copies.",
32457
+ query: [
32458
+ {
32459
+ name: "since",
32460
+ description: "Only days changed at or after this instant (ISO).",
32461
+ format: "date-time"
32462
+ }
32463
+ ],
32464
+ querySchema: journalExportQuerySchema,
32465
+ status: 200,
32466
+ response: { description: "The files.", view: "JournalExport" },
32467
+ errors: { 400: true },
32468
+ operationId: "export_journal"
32469
+ },
32470
+ write: {
32471
+ method: "post",
32472
+ path: "/journal/days/{date}",
32473
+ scopes: ["journal:write"],
32474
+ tag: "Journal",
32475
+ summary: "Write a Journal day",
32476
+ description: "The agent's evening write (`journal_write_day`) or the member's: creates the day when missing and sets what's given. `summary` (markdown) replaces the summary entry, `highlights` (at most 3) replace the day's, `tags` and `links` (`{ kind, nodeId }`, e.g. `saw` a contact, `about` an event) are added, `metrics` (`{ rating: 7, mood: 4 }`) are logged on each Metric's line for that day. A future date, an unknown Metric or a value out of range is a 400. Returns the day's snapshot, the receipt; an agent's write shows in the change list with Undo.",
32477
+ pathParams: {
32478
+ date: { description: "The day, `YYYY-MM-DD`.", format: "date" }
32479
+ },
32480
+ pathParamSchemas: { date: journalDateSchema },
32481
+ body: journalWriteDayInputSchema,
32482
+ status: 200,
32483
+ response: { description: "The day.", view: "JournalSnapshot" },
32484
+ errors: { 400: true },
32485
+ operationId: "journal_write_day"
32486
+ },
32487
+ addEntry: {
32488
+ method: "post",
32489
+ path: "/journal/days/{date}/entries",
32490
+ scopes: ["journal:write"],
32491
+ tag: "Journal",
32492
+ summary: "Add a Journal entry",
32493
+ description: "Add an entry to the day (`journal_add_entry`): a markdown body and an optional title. The day is made when missing. Returns the snapshot.",
32494
+ pathParams: {
32495
+ date: { description: "The day, `YYYY-MM-DD`.", format: "date" }
32496
+ },
32497
+ pathParamSchemas: { date: journalDateSchema },
32498
+ body: journalAddEntryInputSchema,
32499
+ status: 200,
32500
+ response: { description: "The day.", view: "JournalSnapshot" },
32501
+ errors: { 400: true },
32502
+ operationId: "journal_add_entry"
32503
+ },
32504
+ correct: {
32505
+ method: "patch",
32506
+ path: "/journal/days/{date}",
32507
+ scopes: ["journal:write"],
32508
+ tag: "Journal",
32509
+ summary: "Correct a Journal day",
32510
+ description: "Fix the day (`journal_correct`): `title`, `summary` (null clears it), `highlights` and `tags` replace what's there, `removeLinks` unlinks nodes, `metrics` log a corrected value for the day, `status` and `hiddenFromMemories` set those. The member's own correction marks the day reviewed. 404 when the date has no Journal day. Returns the snapshot.",
32511
+ pathParams: {
32512
+ date: { description: "The day, `YYYY-MM-DD`.", format: "date" }
32513
+ },
32514
+ pathParamSchemas: { date: journalDateSchema },
32515
+ body: journalCorrectInputSchema,
32516
+ status: 200,
32517
+ response: { description: "The day.", view: "JournalSnapshot" },
32518
+ errors: { 400: true, 404: "No Journal day on that date." },
32519
+ operationId: "journal_correct"
32520
+ },
32521
+ stageImport: {
32522
+ method: "post",
32523
+ path: "/journal/imports",
32524
+ scopes: ["journal:write"],
32525
+ tag: "Journal",
32526
+ summary: "Stage daily notes for the backfill",
32527
+ description: "Stage parsed daily notes (`do journal import push <dir>`): one item per day (at most 400 a request) with its source file and hash, what becomes its entry (`summary`, `retro` or `log`), a one-liner, the summary's text (health lines left out), a clear `rating` or a `ratingQuestion` (\"~5/10\"), a highlight, whether it may be a milestone or had health lines, and the links counted. Nothing is written into the Journal: the member reviews and approves each month in the app, and a key can't approve. A day still waiting is replaced; one already imported is left alone (`alreadyImported`). Staged days stand behind the Journal's wall.",
32528
+ body: journalImportUploadInputSchema,
32529
+ status: 200,
32530
+ response: { description: "How the upload went.", view: "JournalImportUpload" },
32531
+ errors: { 400: true }
32532
+ },
32533
+ importOverview: {
32534
+ method: "get",
32535
+ path: "/journal/imports",
32536
+ scopes: ["journal:read"],
32537
+ tag: "Journal",
32538
+ summary: "What the backfill has staged",
32539
+ description: "The key's member's staged days: the source folder, how many files over which dates, what the parser found (summaries, ratings and the unclear ones, milestone candidates, days with health lines, links), each month's batch (days, ready, needing the member, imported), and one batch's days (`?batch=YYYY-MM`, else the newest still waiting), each with its `state`: `ready`, `needs_you`, `today` (never imported), `imported` or `skipped`.",
32540
+ afterScopes: "`*` does not cover it.",
32541
+ query: [{ name: "batch", description: "A month, `YYYY-MM`." }],
32542
+ querySchema: journalImportQuerySchema,
32543
+ status: 200,
32544
+ response: { description: "The overview.", view: "JournalImportOverview" },
32545
+ errors: { 400: true }
32546
+ },
32547
+ milestones: {
32548
+ method: "get",
32549
+ path: "/journal/milestones",
32550
+ scopes: ["journal:read"],
32551
+ tag: "Journal",
32552
+ summary: "The life timeline",
32553
+ description: "The key's member's milestones and the Space's shared ones, oldest first: title, kind, when (`happenedOn` at its `precision`: year, month or day), `planned` (still ahead), `status` (`suggested` by an agent and not kept yet, or `kept`), `visibility` (`owner` = Only me, the default; `space` = shared), the nodes it is about (each with its title, type and, for a Project, its `status`), the Journal day it happened on (rating, photos) when the date is a day, and whether it reminds yearly.",
32554
+ afterScopes: "`*` does not cover it.",
32555
+ status: 200,
32556
+ response: { description: "The timeline.", view: "JournalMilestoneList" }
32557
+ },
32558
+ createMilestone: {
32559
+ method: "post",
32560
+ path: "/journal/milestones",
32561
+ scopes: ["journal:write"],
32562
+ tag: "Journal",
32563
+ summary: "Mark a milestone",
32564
+ description: "Mark a milestone (`journal_mark_milestone`): a title, a kind, when and how exactly, planned or not, a line about it, Only me (default) or shared, the nodes it is about (`links`: a Journal day, a paper, a project, a person) and `remindYearly` (a yearly `anniversary` Key date; needs a day). An agent's mark is `suggested` unless it sends `status: kept`; the member keeps or removes it.",
32565
+ body: journalCreateMilestoneInputSchema,
32566
+ status: 201,
32567
+ response: { description: "The milestone.", view: "JournalMilestone" },
32568
+ errors: { 400: true }
32569
+ },
32570
+ updateMilestone: {
32571
+ method: "patch",
32572
+ path: "/journal/milestones/{id}",
32573
+ scopes: ["journal:write"],
32574
+ tag: "Journal",
32575
+ summary: "Change a milestone",
32576
+ description: "Change what's given; `status: kept` keeps a suggestion; `addLinks` / `removeLinks` link or unlink; `remindYearly` turns the yearly Key date on or off (it moves with the date). 404 for someone else's Only me milestone.",
32577
+ pathParams: {
32578
+ id: "The milestone's id."
32579
+ },
32580
+ pathParamSchemas: { id: ulidSchema },
32581
+ body: journalUpdateMilestoneInputSchema,
32582
+ status: 200,
32583
+ response: { description: "The milestone.", view: "JournalMilestone" },
32584
+ errors: { 400: true, 404: "No such milestone." }
32585
+ },
32586
+ removeMilestone: {
32587
+ method: "delete",
32588
+ path: "/journal/milestones/{id}",
32589
+ scopes: ["journal:write"],
32590
+ tag: "Journal",
32591
+ summary: "Remove a milestone",
32592
+ description: "Remove a milestone (to the trash) and its yearly reminder.",
32593
+ pathParams: {
32594
+ id: "The milestone's id."
32595
+ },
32596
+ pathParamSchemas: { id: ulidSchema },
32597
+ status: 204,
32598
+ response: { description: "Removed." },
32599
+ errors: { 404: "No such milestone." }
32600
+ },
32601
+ week: {
32602
+ method: "get",
32603
+ path: "/journal/weeks/{week}",
32604
+ scopes: ["journal:read"],
32605
+ tag: "Journal",
32606
+ summary: "A Journal week",
32607
+ description: "One ISO week of the key's member: the week's recap (the prose, up to 3 best moments, one pattern line; null until written), the week's days with a Journal day or a Metric value (as `GET /journal/days`), `usual` (the mean day rating over the eight weeks before) and the numbers from Progress and the Metrics: what landed (and how much of it was the member's own), each habit's done and due days, the average sleep and the nights to bed after midnight.",
32608
+ afterScopes: "`*` does not cover it.",
32609
+ pathParams: {
32610
+ week: "The ISO week, `YYYY-Www` (e.g. `2026-W42`)."
32611
+ },
32612
+ pathParamSchemas: { week: journalWeekKeySchema },
32613
+ status: 200,
32614
+ response: { description: "The week.", view: "JournalWeek" },
32615
+ errors: { 400: true }
32616
+ },
32617
+ writeRecap: {
32618
+ method: "put",
32619
+ path: "/journal/weeks/{week}/recap",
32620
+ scopes: ["journal:write"],
32621
+ tag: "Journal",
32622
+ summary: "Write a week's recap",
32623
+ description: "The agent's Sunday recap (`journal_write_recap`) or the member's: `body` (markdown) replaces the prose; `moments` (at most 3, `{ text, date?, nodeId? }`) and `pattern` replace theirs when given (`pattern: null` clears it). The recap is owner-only like the days. A week that hasn't started is a 400. Returns the week.",
32624
+ pathParams: {
32625
+ week: "The ISO week, `YYYY-Www` (e.g. `2026-W42`)."
32626
+ },
32627
+ pathParamSchemas: { week: journalWeekKeySchema },
32628
+ body: journalWriteRecapInputSchema,
32629
+ status: 200,
32630
+ response: { description: "The week.", view: "JournalWeek" },
32631
+ errors: { 400: true }
32632
+ },
32633
+ recall: {
32634
+ method: "get",
32635
+ path: "/journal/recall",
32636
+ scopes: ["journal:read"],
32637
+ tag: "Journal",
32638
+ summary: "Recall the Journal",
32639
+ description: "The agent's recall (`journal_recall`): \"what did I do / how was I around <date or topic>\" over the key's member's own days only (another member's days never answer). `q` (2+ characters) finds the days that mention it; `around` (a day) reads 3 days either side; `from`/`to` read a window (at most 366 days); words and a window combine; with neither, the last 7 days. Returns the days newest first (as `GET /journal/days`, at most `limit`, 30 by default), how many were rated and their mean, the member's kept milestones within a week of the window, and the window's last day in up to 3 earlier years (a day hidden from memories left out), so an answer can cite dates.",
32640
+ afterScopes: "`*` does not cover it.",
32641
+ query: [
32642
+ { name: "q", description: "Words to look for (2+ characters)." },
32643
+ { name: "around", description: "A day, YYYY-MM-DD: 3 days either side." },
32644
+ { name: "from", description: "The window's first day, YYYY-MM-DD." },
32645
+ { name: "to", description: "The window's last day, YYYY-MM-DD." },
32646
+ { name: "limit", description: "At most this many days (1\u201330)." }
32647
+ ],
32648
+ querySchema: journalRecallQuerySchema,
32649
+ status: 200,
32650
+ response: { description: "The days that answer.", view: "JournalRecall" },
32651
+ errors: { 400: true },
32652
+ operationId: "journal_recall"
32653
+ },
32654
+ digest: {
32655
+ method: "get",
32656
+ path: "/journal/digest",
32657
+ scopes: ["journal:read"],
32658
+ tag: "Journal",
32659
+ summary: "The Journal digest",
32660
+ description: "The last 7 days of the key's member's Journal, for the agent's private session context: each day's one line and rating (oldest first), the member's kept milestones a week either side, and `text`, the same as a few plain lines (a few hundred tokens).",
32661
+ afterScopes: "`*` does not cover it.",
32662
+ status: 200,
32663
+ response: { description: "The digest.", view: "JournalDigest" },
32664
+ operationId: "journal_digest"
32665
+ },
32666
+ month: {
32667
+ method: "get",
32668
+ path: "/journal/months/{month}",
32669
+ scopes: ["journal:read"],
32670
+ tag: "Journal",
32671
+ summary: "A Journal month",
32672
+ description: "One month of the key's member: the month's recap (the prose, up to 3 best moments, one pattern line; null until written), the month's days with a Journal day or a Metric value (as `GET /journal/days`), `usual` (the mean day rating over the three months before), the numbers from Progress and the Metrics (what landed and how much was the member's own, each habit's done and due days, the average sleep and the nights to bed after midnight) and the member's own kept milestones that happened in it.",
32673
+ afterScopes: "`*` does not cover it.",
32674
+ pathParams: {
32675
+ month: "The month, `YYYY-MM` (e.g. `2026-10`)."
32676
+ },
32677
+ pathParamSchemas: { month: journalMonthKeySchema },
32678
+ status: 200,
32679
+ response: { description: "The month.", view: "JournalMonth" },
32680
+ errors: { 400: true }
32681
+ },
32682
+ writeMonthRecap: {
32683
+ method: "put",
32684
+ path: "/journal/months/{month}/recap",
32685
+ scopes: ["journal:write"],
32686
+ tag: "Journal",
32687
+ summary: "Write a month's recap",
32688
+ description: "The agent's recap on the 1st or the member's: `body` (markdown) replaces the prose; `moments` (at most 3, `{ text, date?, nodeId? }`) and `pattern` replace theirs when given (`pattern: null` clears it). Owner-only like the days. A month that hasn't started is a 400. Returns the month.",
32689
+ pathParams: {
32690
+ month: "The month, `YYYY-MM` (e.g. `2026-10`)."
32691
+ },
32692
+ pathParamSchemas: { month: journalMonthKeySchema },
32693
+ body: journalWriteRecapInputSchema,
32694
+ status: 200,
32695
+ response: { description: "The month.", view: "JournalMonth" },
32696
+ errors: { 400: true }
32697
+ },
32698
+ year: {
32699
+ method: "get",
32700
+ path: "/journal/years/{year}",
32701
+ scopes: ["journal:read"],
32702
+ tag: "Journal",
32703
+ summary: "A Journal year",
32704
+ description: "One year of the key's member: the year's recap (null until written), the year's days (its rating pixels), each begun month's average rating, rated and kept days and best day, `usual` (the mean day rating of the year before), the numbers from Progress and the Metrics, and the member's own kept milestones of the year.",
32705
+ afterScopes: "`*` does not cover it.",
32706
+ pathParams: {
32707
+ year: "The year, `YYYY` (e.g. `2026`)."
32708
+ },
32709
+ pathParamSchemas: { year: journalYearKeySchema },
32710
+ status: 200,
32711
+ response: { description: "The year.", view: "JournalYear" },
32712
+ errors: { 400: true }
32713
+ },
32714
+ writeYearRecap: {
32715
+ method: "put",
32716
+ path: "/journal/years/{year}/recap",
32717
+ scopes: ["journal:write"],
32718
+ tag: "Journal",
32719
+ summary: "Write a year's recap",
32720
+ description: "The agent's recap on 1 January or the member's, the same input as a week's: `body` replaces the prose; `moments` and `pattern` replace theirs when given. Owner-only like the days. A year that hasn't started is a 400. Returns the year.",
32721
+ pathParams: {
32722
+ year: "The year, `YYYY` (e.g. `2026`)."
32723
+ },
32724
+ pathParamSchemas: { year: journalYearKeySchema },
32725
+ body: journalWriteRecapInputSchema,
32726
+ status: 200,
32727
+ response: { description: "The year.", view: "JournalYear" },
32728
+ errors: { 400: true }
32729
+ }
32730
+ });
32731
+
32732
+ // ../../packages/schema/src/operations/key-dates.ts
32733
+ var keyDateOperations = defineOperations({
32734
+ ...crudOperations({
32735
+ segment: "key-dates",
32736
+ tag: "Key dates",
32737
+ noun: "key date",
32738
+ read: "key-dates:read",
32739
+ write: "key-dates:write",
32740
+ create: createKeyDateInputSchema,
32741
+ update: updateKeyDateInputSchema,
32742
+ listQuery: [
32743
+ {
32744
+ name: "nodeId",
32745
+ description: "Only the key dates on this node (a contact, a document, \u2026)."
32746
+ },
32747
+ {
32748
+ name: "kind",
32749
+ description: "Only this kind (`birthday`, `renews`, `cancel_by`, `expires`, `follow_up`, `due_back`, \u2026)."
32750
+ },
32751
+ {
32752
+ name: "from",
32753
+ description: "Only dates whose next occurrence is on or after this day (`YYYY-MM-DD`)."
32754
+ },
32755
+ {
32756
+ name: "to",
32757
+ description: "Only dates whose next occurrence is on or before this day (`YYYY-MM-DD`)."
32758
+ }
32759
+ ],
32760
+ listQuerySchema: keyDatesQuerySchema
32761
+ })
32762
+ });
32763
+
32332
32764
  // ../../packages/schema/src/operations/keys.ts
32333
32765
  var keyOperations = defineOperations({
32334
32766
  list: {
@@ -32753,6 +33185,32 @@ var periodOperations = defineOperations({
32753
33185
  })
32754
33186
  });
32755
33187
 
33188
+ // ../../packages/schema/src/operations/places.ts
33189
+ var placeOperations = defineOperations({
33190
+ ...crudOperations({
33191
+ segment: "places",
33192
+ tag: "Places",
33193
+ noun: "place",
33194
+ read: "places:read",
33195
+ write: "places:write",
33196
+ create: createPlaceInputSchema,
33197
+ update: updatePlaceInputSchema
33198
+ }),
33199
+ importHouse: {
33200
+ method: "post",
33201
+ path: "/places/import",
33202
+ scopes: ["places:write"],
33203
+ tag: "Places",
33204
+ summary: "Import Places from house.json",
33205
+ description: "Import the rooms of one house from Gaard's `house.json` (`{ file, houseIndex? }`; the first house by default). Each room not imported before becomes a Place with its floor and outline; rooms already imported, even ones deleted since, are skipped, so do keeps what it owns. No sync with Gaard. Returns `{ created, skipped, places }`; fires `place.imported`.",
33206
+ body: importPlacesInputSchema,
33207
+ status: 200,
33208
+ response: { description: "`{ created, skipped, places }`." },
33209
+ errors: { 400: true },
33210
+ webhook: "place.imported"
33211
+ }
33212
+ });
33213
+
32756
33214
  // ../../packages/schema/src/operations/planning-rules.ts
32757
33215
  var planningRuleOperations = defineOperations({
32758
33216
  ...crudOperations({
@@ -34884,7 +35342,7 @@ function parseCapture2(input, now) {
34884
35342
 
34885
35343
  // src/meta.ts
34886
35344
  var CLI_NAME = "do";
34887
- var VERSION = "1.308.0";
35345
+ var VERSION = "1.308.2";
34888
35346
  var API_PREFIX = "/api/v1";
34889
35347
  var DEFAULT_API_URL = "http://localhost:3001";
34890
35348
 
@@ -35113,7 +35571,6 @@ function resolveConfig(io, flags) {
35113
35571
  }
35114
35572
 
35115
35573
  // src/contacts-commands.ts
35116
- var WRITE6 = ["contacts:write"];
35117
35574
  var FIELD_FLAGS = [
35118
35575
  { name: "name", type: "string", description: "The display name (or pass it as the argument)." },
35119
35576
  { name: "kind", type: "string", description: "person (the default) or organization." },
@@ -35156,7 +35613,7 @@ var CONTACT_COMMAND_SPECS = [
35156
35613
  summary: "Add a contact: a person or an organisation, with their details.",
35157
35614
  args: [{ name: "name", required: false, description: "The display name." }],
35158
35615
  flags: FIELD_FLAGS,
35159
- scopes: WRITE6,
35616
+ scopes: [...contactOperations.create.scopes],
35160
35617
  returns: "ContactView",
35161
35618
  notes: 'Flags build a CreateContactInput; --data adds any other field (a flag wins). The id is minted when omitted. e.g. `do contacts create "Sam Peters" --email sam@example.com --birthday 1990-04-12 --circles <id>`.'
35162
35619
  },
@@ -35165,7 +35622,7 @@ var CONTACT_COMMAND_SPECS = [
35165
35622
  summary: "Change a contact's details.",
35166
35623
  args: [{ name: "id", required: true, description: "The contact's id." }],
35167
35624
  flags: FIELD_FLAGS,
35168
- scopes: WRITE6,
35625
+ scopes: [...contactOperations.update.scopes],
35169
35626
  returns: "ContactView",
35170
35627
  notes: "Only the flags you pass change. `none` clears a field (--email none, --birthday none). --birthday keeps the contact's other dates; --roles, --circles and --tags replace the list (a role it already has keeps its details)."
35171
35628
  }
@@ -35394,22 +35851,13 @@ var ENTITIES = [
35394
35851
  createInput: "CreateAreaInput",
35395
35852
  updateInput: "UpdateAreaInput"
35396
35853
  }),
35397
- {
35854
+ entityFromOperations(placeOperations, {
35398
35855
  name: "places",
35399
- path: "/places",
35400
- readScopes: ["places:read"],
35401
35856
  returns: "PlaceView",
35402
- canGet: true,
35403
35857
  listFlags: [],
35404
- write: {
35405
- scopes: ["places:write"],
35406
- createSchema: createPlaceInputSchema,
35407
- createInput: "CreatePlaceInput",
35408
- updateSchema: updatePlaceInputSchema,
35409
- updateInput: "UpdatePlaceInput",
35410
- canDelete: true
35411
- }
35412
- },
35858
+ createInput: "CreatePlaceInput",
35859
+ updateInput: "UpdatePlaceInput"
35860
+ }),
35413
35861
  entityFromOperations(routineOperations, {
35414
35862
  name: "routines",
35415
35863
  returns: "RoutineView",
@@ -35428,12 +35876,9 @@ var ENTITIES = [
35428
35876
  createInput: "CreateNoteInput",
35429
35877
  updateInput: "UpdateNoteInput"
35430
35878
  }),
35431
- {
35879
+ entityFromOperations(contactOperations, {
35432
35880
  name: "contacts",
35433
- path: "/contacts",
35434
- readScopes: ["contacts:read"],
35435
35881
  returns: "Contact",
35436
- canGet: true,
35437
35882
  listFlags: [
35438
35883
  { name: "kind", description: "only person or only organization contacts", query: "kind" },
35439
35884
  {
@@ -35448,15 +35893,9 @@ var ENTITIES = [
35448
35893
  sends: "true"
35449
35894
  }
35450
35895
  ],
35451
- write: {
35452
- scopes: ["contacts:write"],
35453
- createSchema: createContactInputSchema,
35454
- createInput: "CreateContactInput",
35455
- updateSchema: updateContactInputSchema,
35456
- updateInput: "UpdateContactInput",
35457
- canDelete: true
35458
- }
35459
- },
35896
+ createInput: "CreateContactInput",
35897
+ updateInput: "UpdateContactInput"
35898
+ }),
35460
35899
  {
35461
35900
  name: "contact-moments",
35462
35901
  path: "/contact-moments",
@@ -35597,12 +36036,9 @@ var ENTITIES = [
35597
36036
  createInput: "CreateGuestInput",
35598
36037
  updateInput: "UpdateGuestInput"
35599
36038
  }),
35600
- {
36039
+ entityFromOperations(keyDateOperations, {
35601
36040
  name: "key-dates",
35602
- path: "/key-dates",
35603
- readScopes: ["key-dates:read"],
35604
36041
  returns: "KeyDateView",
35605
- canGet: true,
35606
36042
  listFlags: [
35607
36043
  { name: "node", description: "only the key dates on this node id", query: "nodeId" },
35608
36044
  {
@@ -35621,15 +36057,9 @@ var ENTITIES = [
35621
36057
  query: "to"
35622
36058
  }
35623
36059
  ],
35624
- write: {
35625
- scopes: ["key-dates:write"],
35626
- createSchema: createKeyDateInputSchema,
35627
- createInput: "CreateKeyDateInput",
35628
- updateSchema: updateKeyDateInputSchema,
35629
- updateInput: "UpdateKeyDateInput",
35630
- canDelete: true
35631
- }
35632
- },
36060
+ createInput: "CreateKeyDateInput",
36061
+ updateInput: "UpdateKeyDateInput"
36062
+ }),
35633
36063
  {
35634
36064
  name: "documents",
35635
36065
  path: "/documents",
@@ -35662,12 +36092,9 @@ var ENTITIES = [
35662
36092
  canDelete: true
35663
36093
  }
35664
36094
  },
35665
- {
36095
+ entityFromOperations(incidentOperations, {
35666
36096
  name: "incidents",
35667
- path: "/incidents",
35668
- readScopes: ["documents:read"],
35669
36097
  returns: "IncidentView",
35670
- canGet: true,
35671
36098
  listFlags: [
35672
36099
  { name: "status", description: "only this status (open, claimed, closed)", query: "status" },
35673
36100
  {
@@ -35676,15 +36103,9 @@ var ENTITIES = [
35676
36103
  query: "policyId"
35677
36104
  }
35678
36105
  ],
35679
- write: {
35680
- scopes: ["documents:write"],
35681
- createSchema: createIncidentInputSchema,
35682
- createInput: "CreateIncidentInput",
35683
- updateSchema: updateIncidentInputSchema,
35684
- updateInput: "UpdateIncidentInput",
35685
- canDelete: true
35686
- }
35687
- },
36106
+ createInput: "CreateIncidentInput",
36107
+ updateInput: "UpdateIncidentInput"
36108
+ }),
35688
36109
  entityFromOperations(periodOperations, {
35689
36110
  name: "periods",
35690
36111
  returns: "PeriodView",
@@ -35826,7 +36247,7 @@ function entityOperations(e) {
35826
36247
 
35827
36248
  // src/health-commands.ts
35828
36249
  var READ6 = ["health:read"];
35829
- var WRITE7 = ["health:write"];
36250
+ var WRITE6 = ["health:write"];
35830
36251
  var DATA_FLAG = {
35831
36252
  name: "data",
35832
36253
  type: "string",
@@ -35856,7 +36277,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
35856
36277
  summary: `Add a health ${noun} (--data '${createInput}').`,
35857
36278
  args: [],
35858
36279
  flags: [DATA_FLAG, ...addFlags],
35859
- scopes: WRITE7,
36280
+ scopes: WRITE6,
35860
36281
  returns,
35861
36282
  notes: `Body: ${createInput}. Always private to you; subjectNodeId names who it is about (absent = you).`
35862
36283
  },
@@ -35865,7 +36286,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
35865
36286
  summary: `Change a health ${noun} (--data '${updateInput}').`,
35866
36287
  args: [id],
35867
36288
  flags: [DATA_FLAG],
35868
- scopes: WRITE7,
36289
+ scopes: WRITE6,
35869
36290
  returns,
35870
36291
  notes: `Body: ${updateInput}. null clears a field.`
35871
36292
  },
@@ -35874,7 +36295,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
35874
36295
  summary: `Delete a health ${noun}.`,
35875
36296
  args: [id],
35876
36297
  flags: [],
35877
- scopes: WRITE7,
36298
+ scopes: WRITE6,
35878
36299
  returns: "Deleted",
35879
36300
  notes: "Destructive: needs --allow-destructive. An Agent's delete waits on your confirmation."
35880
36301
  }
@@ -35924,7 +36345,7 @@ var HEALTH_COMMAND_SPECS = [
35924
36345
  description: `Sex, in your words, for the doctor's page ("female"); "" clears it.`
35925
36346
  }
35926
36347
  ],
35927
- scopes: WRITE7,
36348
+ scopes: WRITE6,
35928
36349
  returns: "HealthProfile",
35929
36350
  notes: "Body: UpdateHealthProfileInput. An absent key keeps a field; null clears it. Lifestyle is `alcohol`, `smoking`, `exercise`, and `notes` anything else: the member's own words. Notes go on For the doctor's background; `sex` on its patient line."
35930
36351
  },
@@ -35933,7 +36354,7 @@ var HEALTH_COMMAND_SPECS = [
35933
36354
  summary: "Log one health message: find or start the Episode, add a symptom and/or a dose.",
35934
36355
  args: [],
35935
36356
  flags: [DATA_FLAG],
35936
- scopes: WRITE7,
36357
+ scopes: WRITE6,
35937
36358
  returns: "HealthReceipt",
35938
36359
  notes: 'Body: LogHealthInput, e.g. {"episode":{"title":"Sprained ankle","bodyRegion":"ankle_l"},"symptom":{"symptom":"worse on the stairs","severity":6,"severityGuessed":true},"dose":{"medication":"Ibuprofen"}}. The receipt lists each part with a line, what was guessed, and its undo. With "thread":{"slug":"<agent>"} (only an agent key that converses) the receipt card is posted there too, and postId names it.'
35939
36360
  },
@@ -35942,7 +36363,7 @@ var HEALTH_COMMAND_SPECS = [
35942
36363
  summary: `Correct a symptom entry and get a receipt (--data '{"bodyRegion":"ankle_l"}').`,
35943
36364
  args: [{ name: "entry", required: true, description: "The entry's id (its latest version)." }],
35944
36365
  flags: [DATA_FLAG],
35945
- scopes: WRITE7,
36366
+ scopes: WRITE6,
35946
36367
  returns: "HealthReceipt"
35947
36368
  },
35948
36369
  {
@@ -35953,7 +36374,7 @@ var HEALTH_COMMAND_SPECS = [
35953
36374
  { name: "trend", required: true, description: "better|same|worse, against yesterday." }
35954
36375
  ],
35955
36376
  flags: [],
35956
- scopes: WRITE7,
36377
+ scopes: WRITE6,
35957
36378
  returns: "HealthReceipt",
35958
36379
  notes: "One a day: a second check-in the same day corrects the first. The receipt carries its undo."
35959
36380
  },
@@ -35970,7 +36391,7 @@ var HEALTH_COMMAND_SPECS = [
35970
36391
  description: "Post the confirmation card in this thread (by slug); the agent's key only."
35971
36392
  }
35972
36393
  ],
35973
- scopes: WRITE7,
36394
+ scopes: WRITE6,
35974
36395
  returns: "HealthRedFlagsResult",
35975
36396
  notes: "Flags: fever, vomiting, black_stools, blood, weight_loss, night_pain, chest_pain, short_of_breath. Answers merge into those asked before; the Episode is marked checked today, and For the doctor shows it."
35976
36397
  },
@@ -35982,7 +36403,7 @@ var HEALTH_COMMAND_SPECS = [
35982
36403
  { name: "id", required: true, description: "The id from the receipt's undo." }
35983
36404
  ],
35984
36405
  flags: [],
35985
- scopes: WRITE7,
36406
+ scopes: WRITE6,
35986
36407
  returns: "HealthUndoResult",
35987
36408
  notes: "A POST, so an Agent's undo never waits on a confirmation."
35988
36409
  },
@@ -35991,7 +36412,7 @@ var HEALTH_COMMAND_SPECS = [
35991
36412
  summary: "Find the open Episode a complaint belongs to, or start one (--data UpsertHealthEpisodeInput).",
35992
36413
  args: [],
35993
36414
  flags: [DATA_FLAG],
35994
- scopes: WRITE7,
36415
+ scopes: WRITE6,
35995
36416
  returns: "HealthEpisodeUpsert"
35996
36417
  },
35997
36418
  {
@@ -36033,7 +36454,7 @@ var HEALTH_COMMAND_SPECS = [
36033
36454
  summary: "Take a For-the-doctor link back: it stops opening.",
36034
36455
  args: [{ name: "id", required: true, description: "The share's id." }],
36035
36456
  flags: [],
36036
- scopes: WRITE7,
36457
+ scopes: WRITE6,
36037
36458
  returns: "HealthShare"
36038
36459
  },
36039
36460
  ...recordSpecs("conditions", "condition", "condition", "HealthConditionView", "CreateHealthConditionInput", "UpdateHealthConditionInput"),
@@ -36051,7 +36472,7 @@ var HEALTH_COMMAND_SPECS = [
36051
36472
  description: 'Their role on it, in your words ("dermatologist").'
36052
36473
  }
36053
36474
  ],
36054
- scopes: WRITE7,
36475
+ scopes: WRITE6,
36055
36476
  returns: "HealthConditionView",
36056
36477
  notes: "A cared_by link (condition \u2192 contact). Adding one already on it sets the role. The condition's page, the Health home's Care team and For the doctor list them."
36057
36478
  },
@@ -36063,7 +36484,7 @@ var HEALTH_COMMAND_SPECS = [
36063
36484
  { name: "contact", required: true, description: "The Contact's id." }
36064
36485
  ],
36065
36486
  flags: [],
36066
- scopes: WRITE7,
36487
+ scopes: WRITE6,
36067
36488
  returns: "HealthConditionView",
36068
36489
  notes: "An Agent's removal waits on your confirmation, like a delete."
36069
36490
  },
@@ -36100,7 +36521,7 @@ var HEALTH_COMMAND_SPECS = [
36100
36521
  { name: "at", type: "string", description: "When it happened (ISO); defaults to now." },
36101
36522
  { name: "note", type: "string", description: "A free-text note." }
36102
36523
  ],
36103
- scopes: WRITE7,
36524
+ scopes: WRITE6,
36104
36525
  returns: "HealthSymptomEntry",
36105
36526
  notes: "An Agent's entry is source chat. For qualifiers, send --data with LogHealthSymptomInput."
36106
36527
  },
@@ -36116,7 +36537,7 @@ var HEALTH_COMMAND_SPECS = [
36116
36537
  }
36117
36538
  ],
36118
36539
  flags: [DATA_FLAG],
36119
- scopes: WRITE7,
36540
+ scopes: WRITE6,
36120
36541
  returns: "HealthSymptomEntry",
36121
36542
  notes: "Body: CorrectHealthSymptomInput; fields not named are copied. The history stays."
36122
36543
  },
@@ -36128,7 +36549,7 @@ var HEALTH_COMMAND_SPECS = [
36128
36549
  { name: "entry", required: true, description: "The entry's id." }
36129
36550
  ],
36130
36551
  flags: [],
36131
- scopes: WRITE7,
36552
+ scopes: WRITE6,
36132
36553
  returns: "Deleted"
36133
36554
  },
36134
36555
  ...recordSpecs("visits", "visit", "visit", "HealthEncounterView", "CreateHealthEncounterInput", "UpdateHealthEncounterInput"),
@@ -36152,7 +36573,7 @@ var HEALTH_COMMAND_SPECS = [
36152
36573
  { name: "at", type: "string", description: "When (ISO); defaults to now." },
36153
36574
  { name: "note", type: "string", description: "A free-text note." }
36154
36575
  ],
36155
- scopes: WRITE7,
36576
+ scopes: WRITE6,
36156
36577
  returns: "HealthMedicationDose"
36157
36578
  },
36158
36579
  {
@@ -36163,7 +36584,7 @@ var HEALTH_COMMAND_SPECS = [
36163
36584
  { name: "dose", required: true, description: "The dose's id." }
36164
36585
  ],
36165
36586
  flags: [DATA_FLAG],
36166
- scopes: WRITE7,
36587
+ scopes: WRITE6,
36167
36588
  returns: "HealthMedicationDose",
36168
36589
  notes: "Body: UpdateHealthDoseInput."
36169
36590
  },
@@ -36175,7 +36596,7 @@ var HEALTH_COMMAND_SPECS = [
36175
36596
  { name: "dose", required: true, description: "The dose's id." }
36176
36597
  ],
36177
36598
  flags: [],
36178
- scopes: WRITE7,
36599
+ scopes: WRITE6,
36179
36600
  returns: "Deleted"
36180
36601
  },
36181
36602
  ...recordSpecs("labs", "lab", "lab draw", "HealthLabPanelView", "CreateHealthLabPanelInput", "UpdateHealthLabPanelInput"),
@@ -36197,7 +36618,7 @@ var HEALTH_COMMAND_SPECS = [
36197
36618
  description: "The file's media type, when its name doesn't say."
36198
36619
  }
36199
36620
  ],
36200
- scopes: WRITE7,
36621
+ scopes: WRITE6,
36201
36622
  returns: "HealthLabImportResult",
36202
36623
  notes: "The draw stays a draft (status draft, source import) until the member checks it; the report is kept as its original. A report with no lab value in it is a 422."
36203
36624
  },
@@ -36282,7 +36703,7 @@ var HEALTH_COMMAND_SPECS = [
36282
36703
  summary: `Keep a target band or the supplement for an analyte (--data '{"targetLow":40,"targetHigh":60}').`,
36283
36704
  args: [{ name: "analyte", required: true, description: "The analyte key (vitamin_d)." }],
36284
36705
  flags: [DATA_FLAG],
36285
- scopes: WRITE7,
36706
+ scopes: WRITE6,
36286
36707
  returns: "HealthLabAnalyteView",
36287
36708
  notes: "Body: SetHealthLabAnalyteInput. An absent key keeps a field; null clears it."
36288
36709
  }
@@ -36626,9 +37047,9 @@ var COMMAND_SPECS = [
36626
37047
  summary: "One Journal day: the summary, entries and highlights, and what happened (Metrics, landed, habits, events, links).",
36627
37048
  args: [{ name: "date", required: true, description: "The day, YYYY-MM-DD." }],
36628
37049
  flags: [],
36629
- scopes: ["journal:read"],
37050
+ scopes: [...journalOperations.day.scopes],
36630
37051
  returns: "JournalSnapshot",
36631
- notes: "GET /api/v1/journal/days/{date} \u2014 your own days only. journal:read is a sensitive scope: a full-access (*) key doesn't hold it. `day` is null until a Journal day was written."
37052
+ notes: `${routeOf(journalOperations.day)} \u2014 your own days only. journal:read is a sensitive scope: a full-access (*) key doesn't hold it. \`day\` is null until a Journal day was written.`
36632
37053
  },
36633
37054
  {
36634
37055
  path: ["journal", "days"],
@@ -36642,18 +37063,18 @@ var COMMAND_SPECS = [
36642
37063
  description: "The last day, YYYY-MM-DD (at most 366 days on)."
36643
37064
  }
36644
37065
  ],
36645
- scopes: ["journal:read"],
37066
+ scopes: [...journalOperations.range.scopes],
36646
37067
  returns: "JournalDayList",
36647
- notes: "GET /api/v1/journal/days \u2014 lists only days with a Journal day or a Metric value. journal:read is a sensitive scope."
37068
+ notes: `${routeOf(journalOperations.range)} \u2014 lists only days with a Journal day or a Metric value. journal:read is a sensitive scope.`
36648
37069
  },
36649
37070
  {
36650
37071
  path: ["journal", "search"],
36651
37072
  summary: "Your Journal days that mention some words, newest first.",
36652
37073
  args: [{ name: "words", required: true, description: "What to look for (2+ characters)." }],
36653
37074
  flags: [],
36654
- scopes: ["journal:read"],
37075
+ scopes: [...journalOperations.search.scopes],
36655
37076
  returns: "JournalSearch",
36656
- notes: "GET /api/v1/journal/search?q= \u2014 your own days only, best match first (at most 30): the words (and a few that mean the same, English and Dutch) in the one-liner, tags, highlights, summary and entries; a date phrase (in March, last week, 14 October, 2026-02) narrows to its window; a heavy or bright question (rough, tired, a great day) adds the days rated 4 or less / 8 or more. `understood` says how it was read. journal:read is a sensitive scope."
37077
+ notes: `${routeOf(journalOperations.search)}?q= \u2014 your own days only, best match first (at most 30): the words (and a few that mean the same, English and Dutch) in the one-liner, tags, highlights, summary and entries; a date phrase (in March, last week, 14 October, 2026-02) narrows to its window; a heavy or bright question (rough, tired, a great day) adds the days rated 4 or less / 8 or more. \`understood\` says how it was read. journal:read is a sensitive scope.`
36657
37078
  },
36658
37079
  {
36659
37080
  path: ["journal", "recall"],
@@ -36669,9 +37090,9 @@ var COMMAND_SPECS = [
36669
37090
  { name: "to", type: "string", description: "The window's last day, YYYY-MM-DD." },
36670
37091
  { name: "limit", type: "string", description: "At most this many days (1\u201330)." }
36671
37092
  ],
36672
- scopes: ["journal:read"],
37093
+ scopes: [...journalOperations.recall.scopes],
36673
37094
  returns: "JournalRecall",
36674
- notes: "GET /api/v1/journal/recall (journal_recall) \u2014 your own days only, dated so an answer can cite them; another member's days never answer. --around can't be combined with --from/--to. journal:read is a sensitive scope."
37095
+ notes: `${routeOf(journalOperations.recall)} (journal_recall) \u2014 your own days only, dated so an answer can cite them; another member's days never answer. --around can't be combined with --from/--to. journal:read is a sensitive scope.`
36675
37096
  },
36676
37097
  {
36677
37098
  path: ["journal", "like"],
@@ -36684,45 +37105,45 @@ var COMMAND_SPECS = [
36684
37105
  description: "At most this many days (1\u201310, 5 by default)."
36685
37106
  }
36686
37107
  ],
36687
- scopes: ["journal:read"],
37108
+ scopes: [...journalOperations.like.scopes],
36688
37109
  returns: "JournalLike",
36689
- notes: "GET /api/v1/journal/days/{date}/like (journal_like) \u2014 your own days only, by Postgres full text over the one-liner, highlights, tags, summary and entries (the day's own words, with a few that mean the same), then shared tags and a close rating. Words, not meanings: no embeddings. A day hidden from memories is never offered. journal:read is a sensitive scope."
37110
+ notes: `${routeOf(journalOperations.like)} (journal_like) \u2014 your own days only, by Postgres full text over the one-liner, highlights, tags, summary and entries (the day's own words, with a few that mean the same), then shared tags and a close rating. Words, not meanings: no embeddings. A day hidden from memories is never offered. journal:read is a sensitive scope.`
36690
37111
  },
36691
37112
  {
36692
37113
  path: ["journal", "digest"],
36693
37114
  summary: "The last 7 days of your Journal as a short digest: each day's line and rating, and the milestones a week either side.",
36694
37115
  args: [],
36695
37116
  flags: [],
36696
- scopes: ["journal:read"],
37117
+ scopes: [...journalOperations.digest.scopes],
36697
37118
  returns: "JournalDigest",
36698
- notes: "GET /api/v1/journal/digest \u2014 `text` is the few plain lines an agent keeps in its private session. journal:read is a sensitive scope."
37119
+ notes: `${routeOf(journalOperations.digest)} \u2014 \`text\` is the few plain lines an agent keeps in its private session. journal:read is a sensitive scope.`
36699
37120
  },
36700
37121
  {
36701
37122
  path: ["journal", "week"],
36702
37123
  summary: "One week of the Journal: its recap (prose, best moments, a pattern), its days, the usual rating and the numbers from Progress.",
36703
37124
  args: [{ name: "week", required: true, description: "The ISO week, e.g. 2026-W42." }],
36704
37125
  flags: [],
36705
- scopes: ["journal:read"],
37126
+ scopes: [...journalOperations.week.scopes],
36706
37127
  returns: "JournalWeek",
36707
- notes: "GET /api/v1/journal/weeks/{week} \u2014 your own week only. `recap` is null until one was written. journal:read is a sensitive scope."
37128
+ notes: `${routeOf(journalOperations.week)} \u2014 your own week only. \`recap\` is null until one was written. journal:read is a sensitive scope.`
36708
37129
  },
36709
37130
  {
36710
37131
  path: ["journal", "month"],
36711
37132
  summary: "One month of the Journal: its recap, its days, the usual rating, the numbers from Progress and the milestones that happened in it.",
36712
37133
  args: [{ name: "month", required: true, description: "The month, e.g. 2026-10." }],
36713
37134
  flags: [],
36714
- scopes: ["journal:read"],
37135
+ scopes: [...journalOperations.month.scopes],
36715
37136
  returns: "JournalMonth",
36716
- notes: "GET /api/v1/journal/months/{month} \u2014 your own month only. `recap` is null until one was written; `usual` is the mean rating over the three months before. journal:read is a sensitive scope."
37137
+ notes: `${routeOf(journalOperations.month)} \u2014 your own month only. \`recap\` is null until one was written; \`usual\` is the mean rating over the three months before. journal:read is a sensitive scope.`
36717
37138
  },
36718
37139
  {
36719
37140
  path: ["journal", "year"],
36720
37141
  summary: "One year of the Journal: its recap, its days (the rating pixels), each month's average and best day, the usual, the numbers and the year's milestones.",
36721
37142
  args: [{ name: "year", required: true, description: "The year, e.g. 2026." }],
36722
37143
  flags: [],
36723
- scopes: ["journal:read"],
37144
+ scopes: [...journalOperations.year.scopes],
36724
37145
  returns: "JournalYear",
36725
- notes: "GET /api/v1/journal/years/{year} \u2014 your own year only. `recap` is null until one was written; `usual` is the mean rating of the year before. journal:read is a sensitive scope."
37146
+ notes: `${routeOf(journalOperations.year)} \u2014 your own year only. \`recap\` is null until one was written; \`usual\` is the mean rating of the year before. journal:read is a sensitive scope.`
36726
37147
  },
36727
37148
  {
36728
37149
  path: ["journal", "recap"],
@@ -36742,9 +37163,9 @@ var COMMAND_SPECS = [
36742
37163
  description: 'The JournalWriteRecapInput as JSON, merged under --body: {"body":"\u2026","moments":[{"text":"\u2026","date":"2026-10-14","nodeId":"\u2026"}],"pattern":"\u2026"}.'
36743
37164
  }
36744
37165
  ],
36745
- scopes: ["journal:write"],
37166
+ scopes: [...journalOperations.writeRecap.scopes],
36746
37167
  returns: "JournalWeek",
36747
- notes: "PUT /api/v1/journal/weeks/{week}/recap (journal_write_recap), /api/v1/journal/months/{month}/recap or /api/v1/journal/years/{year}/recap, by the period's shape. The body replaces the prose; moments and pattern replace theirs when given (pattern null clears it). Returns the period (a JournalWeek, JournalMonth or JournalYear)."
37168
+ notes: `${routeOf(journalOperations.writeRecap)} (journal_write_recap), /api/v1/journal/months/{month}/recap or /api/v1/journal/years/{year}/recap, by the period's shape. The body replaces the prose; moments and pattern replace theirs when given (pattern null clears it). Returns the period (a JournalWeek, JournalMonth or JournalYear).`
36748
37169
  },
36749
37170
  {
36750
37171
  path: ["journal", "write"],
@@ -36760,9 +37181,9 @@ var COMMAND_SPECS = [
36760
37181
  description: 'The JournalWriteDayInput as JSON, merged under the flags: {"summary":"\u2026","highlights":[{"text":"\u2026","nodeId":"\u2026"}],"tags":["Home"],"links":[{"kind":"saw","nodeId":"\u2026"}],"metrics":{"rating":7,"mood":4}}.'
36761
37182
  }
36762
37183
  ],
36763
- scopes: ["journal:write"],
37184
+ scopes: [...journalOperations.write.scopes],
36764
37185
  returns: "JournalSnapshot",
36765
- notes: "POST /api/v1/journal/days/{date} (journal_write_day). Makes the day when missing. Highlights replace; tags and links add. An agent's write shows in the member's change list with Undo."
37186
+ notes: `${routeOf(journalOperations.write)} (journal_write_day). Makes the day when missing. Highlights replace; tags and links add. An agent's write shows in the member's change list with Undo.`
36766
37187
  },
36767
37188
  {
36768
37189
  path: ["journal", "add"],
@@ -36772,9 +37193,9 @@ var COMMAND_SPECS = [
36772
37193
  { name: "text", required: true, description: "The entry, markdown; the rest of the line." }
36773
37194
  ],
36774
37195
  flags: [{ name: "title", type: "string", description: "The entry's title." }],
36775
- scopes: ["journal:write"],
37196
+ scopes: [...journalOperations.addEntry.scopes],
36776
37197
  returns: "JournalSnapshot",
36777
- notes: "POST /api/v1/journal/days/{date}/entries (journal_add_entry)."
37198
+ notes: `${routeOf(journalOperations.addEntry)} (journal_add_entry).`
36778
37199
  },
36779
37200
  {
36780
37201
  path: ["journal", "correct"],
@@ -36787,9 +37208,9 @@ var COMMAND_SPECS = [
36787
37208
  description: 'The JournalCorrectInput as JSON: {"title":"\u2026","summary":null,"highlights":[\u2026],"tags":[\u2026],"removeLinks":["\u2026"],"metrics":{"rating":6},"status":"reviewed","hiddenFromMemories":true}.'
36788
37209
  }
36789
37210
  ],
36790
- scopes: ["journal:write"],
37211
+ scopes: [...journalOperations.correct.scopes],
36791
37212
  returns: "JournalSnapshot",
36792
- notes: "PATCH /api/v1/journal/days/{date} (journal_correct). 404 when the date has no Journal day."
37213
+ notes: `${routeOf(journalOperations.correct)} (journal_correct). 404 when the date has no Journal day.`
36793
37214
  },
36794
37215
  {
36795
37216
  path: ["journal", "import", "push"],
@@ -36809,27 +37230,27 @@ var COMMAND_SPECS = [
36809
37230
  description: "How the days name their folder (default memory/daily)."
36810
37231
  }
36811
37232
  ],
36812
- scopes: ["journal:write"],
37233
+ scopes: [...journalOperations.stageImport.scopes],
36813
37234
  returns: "JournalImportUpload",
36814
- notes: "POST /api/v1/journal/imports, 400 days a request. The day's '## Day summary' becomes its entry; a clear 'n/10' rating is kept, '~5/10' or '3-4/10' becomes a question; health and automation lines never reach the text. Nothing is written into the Journal until the member approves the month (the app only; a key can't approve). Re-running replaces what's still waiting."
37235
+ notes: `${routeOf(journalOperations.stageImport)}, 400 days a request. The day's '## Day summary' becomes its entry; a clear 'n/10' rating is kept, '~5/10' or '3-4/10' becomes a question; health and automation lines never reach the text. Nothing is written into the Journal until the member approves the month (the app only; a key can't approve). Re-running replaces what's still waiting.`
36815
37236
  },
36816
37237
  {
36817
37238
  path: ["journal", "import", "status"],
36818
37239
  summary: "What the backfill has staged: the files, what the parser found, each month's batch.",
36819
37240
  args: [],
36820
37241
  flags: [{ name: "batch", type: "string", description: "Show this month's days (YYYY-MM)." }],
36821
- scopes: ["journal:read"],
37242
+ scopes: [...journalOperations.importOverview.scopes],
36822
37243
  returns: "JournalImportOverview",
36823
- notes: "GET /api/v1/journal/imports. journal:read is a sensitive scope."
37244
+ notes: `${routeOf(journalOperations.importOverview)}. journal:read is a sensitive scope.`
36824
37245
  },
36825
37246
  {
36826
37247
  path: ["journal", "milestones"],
36827
37248
  summary: "Your life timeline: your milestones and the Space's shared ones, oldest first, with the suggestions.",
36828
37249
  args: [],
36829
37250
  flags: [],
36830
- scopes: ["journal:read"],
37251
+ scopes: [...journalOperations.milestones.scopes],
36831
37252
  returns: "JournalMilestoneList",
36832
- notes: "GET /api/v1/journal/milestones. A milestone is Only me unless shared; `status: suggested` is an agent's mark you haven't kept yet. journal:read is a sensitive scope."
37253
+ notes: `${routeOf(journalOperations.milestones)}. A milestone is Only me unless shared; \`status: suggested\` is an agent's mark you haven't kept yet. journal:read is a sensitive scope.`
36833
37254
  },
36834
37255
  {
36835
37256
  path: ["journal", "milestone", "add"],
@@ -36856,9 +37277,9 @@ var COMMAND_SPECS = [
36856
37277
  description: 'The JournalCreateMilestoneInput as JSON, merged under the flags: {"description":"\u2026","visibility":"space","links":["<node id>"],"remindYearly":true,"status":"kept"}.'
36857
37278
  }
36858
37279
  ],
36859
- scopes: ["journal:write"],
37280
+ scopes: [...journalOperations.createMilestone.scopes],
36860
37281
  returns: "JournalMilestone",
36861
- notes: "POST /api/v1/journal/milestones (journal_mark_milestone). Only me unless visibility is space. links point it at what it's about (a Journal day, a paper, a project, a person); remindYearly adds a yearly Key date."
37282
+ notes: `${routeOf(journalOperations.createMilestone)} (journal_mark_milestone). Only me unless visibility is space. links point it at what it's about (a Journal day, a paper, a project, a person); remindYearly adds a yearly Key date.`
36862
37283
  },
36863
37284
  {
36864
37285
  path: ["journal", "milestone", "edit"],
@@ -36872,9 +37293,9 @@ var COMMAND_SPECS = [
36872
37293
  description: 'The JournalUpdateMilestoneInput as JSON: {"title":"\u2026","happenedOn":"2026-10-14","planned":false,"addLinks":["<id>"],"removeLinks":["<id>"],"remindYearly":true}.'
36873
37294
  }
36874
37295
  ],
36875
- scopes: ["journal:write"],
37296
+ scopes: [...journalOperations.updateMilestone.scopes],
36876
37297
  returns: "JournalMilestone",
36877
- notes: "PATCH /api/v1/journal/milestones/{id}."
37298
+ notes: `${routeOf(journalOperations.updateMilestone)}.`
36878
37299
  },
36879
37300
  {
36880
37301
  path: ["journal", "milestone", "remove"],
@@ -36887,9 +37308,9 @@ var COMMAND_SPECS = [
36887
37308
  description: "Required to actually remove it."
36888
37309
  }
36889
37310
  ],
36890
- scopes: ["journal:write"],
37311
+ scopes: [...journalOperations.removeMilestone.scopes],
36891
37312
  returns: "None",
36892
- notes: "DELETE /api/v1/journal/milestones/{id}. Goes to the trash like any node."
37313
+ notes: `${routeOf(journalOperations.removeMilestone)}. Goes to the trash like any node.`
36893
37314
  },
36894
37315
  {
36895
37316
  path: ["journal", "export"],
@@ -36907,9 +37328,9 @@ var COMMAND_SPECS = [
36907
37328
  description: "Write the files under this directory instead of printing them."
36908
37329
  }
36909
37330
  ],
36910
- scopes: ["journal:export"],
37331
+ scopes: [...journalOperations.export.scopes],
36911
37332
  returns: "JournalExport",
36912
- notes: "GET /api/v1/journal/export. Prints `nextSince`; pass it as --since next time. do is the record: the files are read-only copies and are overwritten."
37333
+ notes: `${routeOf(journalOperations.export)}. Prints \`nextSince\`; pass it as --since next time. do is the record: the files are read-only copies and are overwritten.`
36913
37334
  },
36914
37335
  {
36915
37336
  path: ["progress", "commits"],
@@ -37893,9 +38314,9 @@ var COMMAND_SPECS = [
37893
38314
  description: "The rest of the create body (JSON), e.g. claimNo; overrides the flags."
37894
38315
  }
37895
38316
  ],
37896
- scopes: ["documents:write"],
38317
+ scopes: [...incidentOperations.create.scopes],
37897
38318
  returns: "IncidentView",
37898
- notes: `POST /api/v1/incidents. A policy or Place that isn't a live one in the Space is a 400. Tick steps off with \`do incidents update <id> --data '{"steps":[{"id":"photos","done":true}]}'\`.`
38319
+ notes: `${routeOf(incidentOperations.create)}. A policy or Place that isn't a live one in the Space is a 400. Tick steps off with \`do incidents update <id> --data '{"steps":[{"id":"photos","done":true}]}'\`.`
37899
38320
  },
37900
38321
  {
37901
38322
  path: ["notes", "backlinks"],
@@ -40486,7 +40907,7 @@ var COMMAND_SPECS = [
40486
40907
  description: "Which house in the file (0 = the first, the default)."
40487
40908
  }
40488
40909
  ],
40489
- scopes: ["places:write"],
40910
+ scopes: [...placeOperations.importHouse.scopes],
40490
40911
  returns: "ImportPlacesResult",
40491
40912
  notes: "Each room becomes a Place; rooms already imported (even ones deleted since) are skipped, so do keeps what it owns. No sync with Gaard. Returns { created, skipped, places }."
40492
40913
  },
@@ -43348,6 +43769,11 @@ async function dispatchEntity(entity, rest, flags, requireClient) {
43348
43769
  }
43349
43770
  throw usageError(`unknown operation for ${entity.name}: \`${op ?? ""}\` (use ${entityOperations(entity).join(", ")})`);
43350
43771
  }
43772
+ var RECAP_OPERATION = {
43773
+ week: journalOperations.writeRecap,
43774
+ month: journalOperations.writeMonthRecap,
43775
+ year: journalOperations.writeYearRecap
43776
+ };
43351
43777
  function runFixed(spec, ctx) {
43352
43778
  const key = spec.path.join(" ");
43353
43779
  const handler = HANDLERS[key];
@@ -43566,7 +43992,7 @@ var HANDLERS = {
43566
43992
  throw usageError("journal day needs a date as YYYY-MM-DD, e.g. `do journal day 2026-10-15`");
43567
43993
  }
43568
43994
  return {
43569
- data: await requireClient().get(`/journal/days/${date}`),
43995
+ data: await requireClient().get(pathOf(journalOperations.day, { date })),
43570
43996
  render: renderJournalDay
43571
43997
  };
43572
43998
  },
@@ -43577,7 +44003,7 @@ var HANDLERS = {
43577
44003
  throw usageError("journal days needs --from and --to as YYYY-MM-DD");
43578
44004
  }
43579
44005
  return {
43580
- data: await requireClient().get("/journal/days", { query: { from, to } }),
44006
+ data: await requireClient().get(pathOf(journalOperations.range), { query: { from, to } }),
43581
44007
  render: renderJournalDays
43582
44008
  };
43583
44009
  },
@@ -43586,7 +44012,7 @@ var HANDLERS = {
43586
44012
  if (q.length < 2)
43587
44013
  throw usageError("journal search needs the words to look for");
43588
44014
  return {
43589
- data: await requireClient().get("/journal/search", { query: { q } }),
44015
+ data: await requireClient().get(pathOf(journalOperations.search), { query: { q } }),
43590
44016
  render: renderJournalSearch
43591
44017
  };
43592
44018
  },
@@ -43603,7 +44029,7 @@ var HANDLERS = {
43603
44029
  };
43604
44030
  parseInput(journalRecallQuerySchema, Object.fromEntries(Object.entries(query).filter(([, v]) => v !== undefined)));
43605
44031
  return {
43606
- data: await requireClient().get("/journal/recall", { query }),
44032
+ data: await requireClient().get(pathOf(journalOperations.recall), { query }),
43607
44033
  render: renderJournalRecall
43608
44034
  };
43609
44035
  },
@@ -43615,18 +44041,20 @@ var HANDLERS = {
43615
44041
  const limit = flagString(flags, "limit");
43616
44042
  parseInput(journalLikeQuerySchema, limit === undefined ? {} : { limit });
43617
44043
  return {
43618
- data: await requireClient().get(`/journal/days/${date}/like`, { query: { limit } }),
44044
+ data: await requireClient().get(pathOf(journalOperations.like, { date }), {
44045
+ query: { limit }
44046
+ }),
43619
44047
  render: renderJournalLike
43620
44048
  };
43621
44049
  },
43622
44050
  "journal digest": async ({ requireClient }) => ({
43623
- data: await requireClient().get("/journal/digest"),
44051
+ data: await requireClient().get(pathOf(journalOperations.digest)),
43624
44052
  render: renderJournalDigest
43625
44053
  }),
43626
44054
  "journal week": async ({ args, requireClient }) => {
43627
44055
  const week = journalWeekArg(args, "week");
43628
44056
  return {
43629
- data: await requireClient().get(`/journal/weeks/${week}`),
44057
+ data: await requireClient().get(pathOf(journalOperations.week, { week })),
43630
44058
  render: renderJournalPeriod
43631
44059
  };
43632
44060
  },
@@ -43636,7 +44064,7 @@ var HANDLERS = {
43636
44064
  throw usageError("journal month needs a month as YYYY-MM, e.g. `do journal month 2026-10`");
43637
44065
  }
43638
44066
  return {
43639
- data: await requireClient().get(`/journal/months/${month}`),
44067
+ data: await requireClient().get(pathOf(journalOperations.month, { month })),
43640
44068
  render: renderJournalPeriod
43641
44069
  };
43642
44070
  },
@@ -43646,7 +44074,7 @@ var HANDLERS = {
43646
44074
  throw usageError("journal year needs a year as YYYY, e.g. `do journal year 2026`");
43647
44075
  }
43648
44076
  return {
43649
- data: await requireClient().get(`/journal/years/${year}`),
44077
+ data: await requireClient().get(pathOf(journalOperations.year, { year })),
43650
44078
  render: renderJournalPeriod
43651
44079
  };
43652
44080
  },
@@ -43662,7 +44090,7 @@ var HANDLERS = {
43662
44090
  body.body = text;
43663
44091
  const input = parseInput(journalWriteRecapInputSchema, body);
43664
44092
  return {
43665
- data: await requireClient().put(`/journal/${kind}s/${period}/recap`, input),
44093
+ data: await requireClient().put(pathOf(RECAP_OPERATION[kind], { [kind]: period }), input),
43666
44094
  render: renderJournalPeriod
43667
44095
  };
43668
44096
  },
@@ -43687,7 +44115,7 @@ var HANDLERS = {
43687
44115
  }
43688
44116
  const input = parseInput(journalWriteDayInputSchema, body);
43689
44117
  return {
43690
- data: await requireClient().post(`/journal/days/${date}`, input),
44118
+ data: await requireClient().post(pathOf(journalOperations.write, { date }), input),
43691
44119
  render: renderJournalDay
43692
44120
  };
43693
44121
  },
@@ -43702,7 +44130,7 @@ var HANDLERS = {
43702
44130
  ...title !== undefined ? { title } : {}
43703
44131
  });
43704
44132
  return {
43705
- data: await requireClient().post(`/journal/days/${date}/entries`, input),
44133
+ data: await requireClient().post(pathOf(journalOperations.addEntry, { date }), input),
43706
44134
  render: renderJournalDay
43707
44135
  };
43708
44136
  },
@@ -43712,7 +44140,7 @@ var HANDLERS = {
43712
44140
  throw usageError("journal correct needs --data");
43713
44141
  const input = parseInput(journalCorrectInputSchema, readDataFlag(flags));
43714
44142
  return {
43715
- data: await requireClient().patch(`/journal/days/${date}`, input),
44143
+ data: await requireClient().patch(pathOf(journalOperations.correct, { date }), input),
43716
44144
  render: renderJournalDay
43717
44145
  };
43718
44146
  },
@@ -43730,7 +44158,7 @@ var HANDLERS = {
43730
44158
  const input = parseInput(journalImportUploadInputSchema, {
43731
44159
  items: items.slice(i, i + JOURNAL_IMPORT_MAX_ITEMS)
43732
44160
  });
43733
- const res = await requireClient().post("/journal/imports", input);
44161
+ const res = await requireClient().post(pathOf(journalOperations.stageImport), input);
43734
44162
  staged += res.staged;
43735
44163
  alreadyImported += res.alreadyImported;
43736
44164
  }
@@ -43742,12 +44170,14 @@ var HANDLERS = {
43742
44170
  "journal import status": async ({ flags, requireClient }) => {
43743
44171
  const batch = flagString(flags, "batch");
43744
44172
  return {
43745
- data: await requireClient().get("/journal/imports", { query: batch ? { batch } : {} }),
44173
+ data: await requireClient().get(pathOf(journalOperations.importOverview), {
44174
+ query: batch ? { batch } : {}
44175
+ }),
43746
44176
  render: renderJournalImportStatus
43747
44177
  };
43748
44178
  },
43749
44179
  "journal milestones": async ({ requireClient }) => ({
43750
- data: await requireClient().get("/journal/milestones"),
44180
+ data: await requireClient().get(pathOf(journalOperations.milestones)),
43751
44181
  render: renderJournalMilestones
43752
44182
  }),
43753
44183
  "journal milestone add": async ({ args, flags, requireClient }) => {
@@ -43768,7 +44198,7 @@ var HANDLERS = {
43768
44198
  body.planned = true;
43769
44199
  const input = parseInput(journalCreateMilestoneInputSchema, body);
43770
44200
  return {
43771
- data: await requireClient().post("/journal/milestones", input),
44201
+ data: await requireClient().post(pathOf(journalOperations.createMilestone), input),
43772
44202
  render: renderJournalMilestone
43773
44203
  };
43774
44204
  },
@@ -43779,20 +44209,20 @@ var HANDLERS = {
43779
44209
  body.status = "kept";
43780
44210
  const input = parseInput(journalUpdateMilestoneInputSchema, body);
43781
44211
  return {
43782
- data: await requireClient().patch(`/journal/milestones/${id}`, input),
44212
+ data: await requireClient().patch(pathOf(journalOperations.updateMilestone, { id }), input),
43783
44213
  render: renderJournalMilestone
43784
44214
  };
43785
44215
  },
43786
44216
  "journal milestone remove": async ({ args, flags, requireClient }) => {
43787
44217
  const id = requireArg(args, 0, "milestone id");
43788
44218
  assertDestructiveAllowed(flags, `remove milestone ${id}`);
43789
- await requireClient().delete(`/journal/milestones/${id}`);
44219
+ await requireClient().delete(pathOf(journalOperations.removeMilestone, { id }));
43790
44220
  return { data: { ok: true, removed: id }, render: renderRecord };
43791
44221
  },
43792
44222
  "journal export": async ({ flags, requireClient }) => {
43793
44223
  const since = flagString(flags, "since");
43794
44224
  const out = flagString(flags, "out");
43795
- const data = await requireClient().get("/journal/export", {
44225
+ const data = await requireClient().get(pathOf(journalOperations.export), {
43796
44226
  query: { since }
43797
44227
  });
43798
44228
  if (out !== undefined) {
@@ -43861,13 +44291,16 @@ var HANDLERS = {
43861
44291
  }
43862
44292
  const client = requireClient();
43863
44293
  if (flagBool(flags, "dry-run")) {
43864
- const before = await client.get(`/notes/${id}`);
44294
+ const before = await client.get(pathOf(noteOperations.get, { id }));
43865
44295
  return {
43866
44296
  data: { dryRun: true, op: "update", entity: "notes", id, before, changes: body },
43867
44297
  render: renderRecord
43868
44298
  };
43869
44299
  }
43870
- return { data: await client.patch(`/notes/${id}`, body), render: renderRecord };
44300
+ return {
44301
+ data: await client.patch(pathOf(noteOperations.update, { id }), body),
44302
+ render: renderRecord
44303
+ };
43871
44304
  },
43872
44305
  "notes detach": async ({ args, flags, requireClient }) => {
43873
44306
  const node = requireArg(args, 0, "node id");
@@ -44198,7 +44631,10 @@ var HANDLERS = {
44198
44631
  ...fromFlags,
44199
44632
  ...readDataFlag(flags)
44200
44633
  });
44201
- return { data: await requireClient().post("/incidents", body), render: renderRecord };
44634
+ return {
44635
+ data: await requireClient().post(pathOf(incidentOperations.create), body),
44636
+ render: renderRecord
44637
+ };
44202
44638
  },
44203
44639
  "food log": async ({ flags, requireClient }) => {
44204
44640
  if (flagString(flags, "data") === undefined)
@@ -46387,7 +46823,7 @@ var HANDLERS = {
46387
46823
  file,
46388
46824
  ...house !== undefined ? { houseIndex: Number(house) } : {}
46389
46825
  });
46390
- const result = importPlacesResultSchema.parse(await requireClient().post("/places/import", body));
46826
+ const result = importPlacesResultSchema.parse(await requireClient().post(pathOf(placeOperations.importHouse), body));
46391
46827
  return { data: result, render: renderRecord };
46392
46828
  },
46393
46829
  "people note": async ({ flags, requireClient }) => {
@@ -46494,13 +46930,16 @@ var HANDLERS = {
46494
46930
  render: renderRecord
46495
46931
  };
46496
46932
  }
46497
- return { data: await requireClient().post("/contacts", body), render: renderRecord };
46933
+ return {
46934
+ data: await requireClient().post(pathOf(contactOperations.create), body),
46935
+ render: renderRecord
46936
+ };
46498
46937
  },
46499
46938
  "contacts update": async ({ args, flags, requireClient }) => {
46500
46939
  const id = requireArg(args, 0, "contact id");
46501
46940
  const client = requireClient();
46502
46941
  const dryRun = flagBool(flags, "dry-run");
46503
- const before = dryRun || MERGING_FLAGS.some((f) => flags[f] !== undefined) ? await client.get(`/contacts/${id}`) : undefined;
46942
+ const before = dryRun || MERGING_FLAGS.some((f) => flags[f] !== undefined) ? await client.get(pathOf(contactOperations.get, { id })) : undefined;
46504
46943
  const body = parseInput(updateContactInputSchema, contactBodyFromFlags(flags, readDataFlag(flags), "update", before));
46505
46944
  if (Object.keys(body).length === 0)
46506
46945
  throw usageError("nothing to change \u2014 pass a flag such as --email, or --data");
@@ -46518,7 +46957,10 @@ var HANDLERS = {
46518
46957
  render: renderRecord
46519
46958
  };
46520
46959
  }
46521
- return { data: await client.patch(`/contacts/${id}`, body), render: renderRecord };
46960
+ return {
46961
+ data: await client.patch(pathOf(contactOperations.update, { id }), body),
46962
+ render: renderRecord
46963
+ };
46522
46964
  },
46523
46965
  "people tree": async ({ flags, requireClient }) => {
46524
46966
  const side = flagString(flags, "side");