@robbeverhelst/do 1.304.0 → 1.306.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.
Files changed (2) hide show
  1. package/dist/do.js +1029 -184
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -26108,10 +26108,16 @@ var moneyTransactionViewSchema = moneyTransactionSchema.omit({ dedupHash: true }
26108
26108
  nodeId: ulidSchema,
26109
26109
  kind: moneyLinkKindSchema,
26110
26110
  title: string2(),
26111
- type: string2()
26111
+ type: string2(),
26112
+ warrantyEnds: keyDateDaySchema.nullable().default(null)
26112
26113
  })).default([]),
26113
26114
  recurring: string2().nullable().default(null),
26114
- contact: object({ id: ulidSchema, name: string2(), payments: number2().int().nonnegative() }).nullable().default(null),
26115
+ contact: object({
26116
+ id: ulidSchema,
26117
+ name: string2(),
26118
+ kind: _enum2(["person", "organization"]).default("person"),
26119
+ payments: number2().int().nonnegative()
26120
+ }).nullable().default(null),
26115
26121
  split: object({
26116
26122
  parts: number2().int().positive(),
26117
26123
  even: boolean3(),
@@ -30246,6 +30252,382 @@ var googleHealthOperations = defineOperations({
30246
30252
  }
30247
30253
  });
30248
30254
 
30255
+ // ../../packages/schema/src/operations/guest-list-events.ts
30256
+ var crud2 = crudOperations({
30257
+ segment: "guest-list-events",
30258
+ tag: "Guests",
30259
+ noun: "guest list event",
30260
+ read: "contacts:read",
30261
+ write: "contacts:write",
30262
+ create: createGuestListEventInputSchema,
30263
+ update: updateGuestListEventInputSchema,
30264
+ listQuery: [{ name: "guestListId", description: "The guest list (required)." }],
30265
+ listQuerySchema: guestListEventsQuerySchema
30266
+ });
30267
+ var guestListEventOperations = defineOperations({
30268
+ ...crud2,
30269
+ create: { ...crud2.create, webhook: "guest.event.created" },
30270
+ update: { ...crud2.update, webhook: "guest.event.updated" },
30271
+ remove: { ...crud2.remove, webhook: "guest.event.deleted" }
30272
+ });
30273
+
30274
+ // ../../packages/schema/src/operations/guest-tables.ts
30275
+ var guestTableOperations = defineOperations({
30276
+ ...crudOperations({
30277
+ segment: "guest-tables",
30278
+ tag: "Guests",
30279
+ noun: "guest table",
30280
+ read: "contacts:read",
30281
+ write: "contacts:write",
30282
+ create: createGuestTableInputSchema,
30283
+ update: updateGuestTableInputSchema,
30284
+ listQuery: [{ name: "guestListId", description: "The guest list (required)." }],
30285
+ listQuerySchema: guestTablesQuerySchema
30286
+ })
30287
+ });
30288
+
30289
+ // ../../packages/schema/src/operations/guests.ts
30290
+ var guestOperations = defineOperations({
30291
+ ...crudOperations({
30292
+ segment: "guests",
30293
+ tag: "Guests",
30294
+ noun: "guest",
30295
+ read: "contacts:read",
30296
+ write: "contacts:write",
30297
+ create: createGuestInputSchema,
30298
+ update: updateGuestInputSchema,
30299
+ listQuery: [
30300
+ { name: "guestListId", description: "Only this guest list's guests." },
30301
+ { name: "contactId", description: "Only this contact's guest rows." },
30302
+ { name: "state", description: "`yes`, `no`, `maybe` or `waiting`: their overall answer." }
30303
+ ],
30304
+ listQuerySchema: guestsQuerySchema
30305
+ }),
30306
+ sent: {
30307
+ method: "post",
30308
+ path: "/guests/sent",
30309
+ scopes: ["contacts:write"],
30310
+ tag: "Guests",
30311
+ summary: "Stamp save-the-dates or invitations as sent",
30312
+ description: "Stamps `saveTheDateAt` (`what: save_the_date`) or `inviteSentAt` (`what: invite`) on every guest in `guestIds`, at `at` or now. Returns the guests; fires `guest.sent`.",
30313
+ body: markGuestsSentInputSchema,
30314
+ status: 200,
30315
+ response: { description: "The stamped guests." },
30316
+ errors: { 400: true, 404: true },
30317
+ webhook: "guest.sent"
30318
+ }
30319
+ });
30320
+
30321
+ // ../../packages/schema/src/operations/habits.ts
30322
+ var habitListQuery = habitListQuerySchema.extend(paginationQuerySchema.shape);
30323
+ var habitOperations = defineOperations({
30324
+ list: {
30325
+ method: "get",
30326
+ path: "/habits",
30327
+ scopes: ["habits:read"],
30328
+ tag: "Habits",
30329
+ summary: "List habits",
30330
+ description: "Every growing habit (with streaks) in the Space, oldest first. `retired=true` lists the Grown shelf instead: the retired habits, history kept.",
30331
+ query: [
30332
+ { name: "areaId", description: "Only rows in this Area (ULID)." },
30333
+ {
30334
+ name: "updatedSince",
30335
+ description: "Only rows whose `updatedAt` is at or after this instant \u2014 an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight).",
30336
+ format: "date-time"
30337
+ },
30338
+ {
30339
+ name: "retired",
30340
+ description: "`true` lists the retired (Grown) habits instead of the growing ones.",
30341
+ enum: ["true", "false"]
30342
+ }
30343
+ ],
30344
+ querySchema: habitListQuery,
30345
+ paginated: true,
30346
+ status: 200,
30347
+ response: { description: "The Space's habits." }
30348
+ },
30349
+ today: {
30350
+ method: "get",
30351
+ path: "/habits/today",
30352
+ scopes: ["habits:read"],
30353
+ tag: "Habits",
30354
+ summary: "What's left today",
30355
+ description: "\"What's left?\" (#866): the habits due on the key's member's day (their saved time zone), already filtered by schedule, grouped by part of day (morning, midday, evening, anytime). Each says its value today, `done`/`open`/`skipped` (never overdue: habits don't carry over), the week for an N-per-week habit, and its last check-in (`by` names the Agent). Also today's routines with how many steps are done. Quit habits are left out.",
30356
+ status: 200,
30357
+ response: { description: "Today's habits and routines." },
30358
+ operationId: "habits_today"
30359
+ },
30360
+ getAway: {
30361
+ method: "get",
30362
+ path: "/habits/away",
30363
+ scopes: ["habits:read"],
30364
+ tag: "Habits",
30365
+ summary: "Get away state",
30366
+ description: "The Space's \"Away until \u2026\": `{ until }` while away (the day you're back, YYYY-MM-DD), else `{ until: null }`.",
30367
+ status: 200,
30368
+ response: { description: "The away state." }
30369
+ },
30370
+ setAway: {
30371
+ method: "put",
30372
+ path: "/habits/away",
30373
+ scopes: ["habits:write"],
30374
+ tag: "Habits",
30375
+ summary: "Set away",
30376
+ description: '"Away till Monday": every habit rests until `until` (the day you\'re back, after today): off Now, no push, streaks and plants wait. `until: null` comes back now. Tasks and events carry on. Fires `habit.updated`.',
30377
+ body: awayInputSchema,
30378
+ status: 200,
30379
+ response: { description: "The away state." },
30380
+ errors: { 400: true },
30381
+ webhook: "habit.updated"
30382
+ },
30383
+ get: {
30384
+ method: "get",
30385
+ path: "/habits/{id}",
30386
+ scopes: ["habits:read"],
30387
+ tag: "Habits",
30388
+ summary: "Get a habit",
30389
+ description: "One habit with its streak + heatmap.",
30390
+ status: 200,
30391
+ response: { description: "The habit." },
30392
+ errors: { 404: true }
30393
+ },
30394
+ create: {
30395
+ method: "post",
30396
+ path: "/habits",
30397
+ scopes: ["habits:write"],
30398
+ tag: "Habits",
30399
+ summary: "Create a habit",
30400
+ description: "Create a habit; fires `habit.created`.",
30401
+ body: createHabitInputSchema,
30402
+ status: 201,
30403
+ response: { description: "The created habit." },
30404
+ errors: { 400: true },
30405
+ webhook: "habit.created"
30406
+ },
30407
+ update: {
30408
+ method: "patch",
30409
+ path: "/habits/{id}",
30410
+ scopes: ["habits:write"],
30411
+ tag: "Habits",
30412
+ summary: "Update a habit",
30413
+ description: "Patch a habit; fires `habit.updated`.",
30414
+ body: updateHabitInputSchema,
30415
+ status: 200,
30416
+ response: { description: "The updated habit." },
30417
+ errors: {
30418
+ 400: true,
30419
+ 404: true
30420
+ },
30421
+ webhook: "habit.updated"
30422
+ },
30423
+ remove: {
30424
+ method: "delete",
30425
+ path: "/habits/{id}",
30426
+ scopes: ["habits:write"],
30427
+ tag: "Habits",
30428
+ summary: "Archive a habit",
30429
+ description: "Soft-delete a habit; fires `habit.deleted`.",
30430
+ status: 204,
30431
+ response: { description: "Archived." },
30432
+ errors: { 404: true },
30433
+ webhook: "habit.deleted"
30434
+ },
30435
+ checkInByTitle: {
30436
+ method: "post",
30437
+ path: "/habits/check-in",
30438
+ scopes: ["habits:write"],
30439
+ tag: "Habits",
30440
+ summary: "Check in a habit by title",
30441
+ description: 'Check in the habit whose `title` or one of whose aliases is this (case-insensitive) \u2014 "wake", "did my run", "meditated". `value` defaults to 1. `mode: "add"` (default) logs it on top of the day ("12 pages"); `mode: "set"` makes it the day\'s total ("read 40 total") by writing one entry with the difference, keeping earlier check-ins (amount habits only, else 400). `occurredAt` back-dates it: an instant, or a `YYYY-MM-DD` member\'s day (noon in their zone); `set` works on the member\'s day it lands in. Several matching habits are a 409 with `error.code: "ambiguous_habit"` and `error.details.candidates` (`id`, `title`, `aliases`) to ask which; none is a 404 naming close titles; a habit counted from an ingest (Google Fit, Strava, GitHub) is a 409 \u2014 it checks itself in. An Agent\'s key records an `agent` check-in naming the Agent. `note` puts a short note on the check-in ("drill done, felt heavy" \u2192 `note: "felt heavy"`); a `set` that changes nothing puts it on the day\'s latest check-in. The refreshed habit\'s `lastCheckIn.id` is the entry, for adding a note later. Returns the refreshed habit; fires `habit.updated`.',
30442
+ body: habitCheckInByTitleInputSchema,
30443
+ status: 201,
30444
+ response: { description: "The refreshed habit." },
30445
+ errors: {
30446
+ 400: true,
30447
+ 404: true,
30448
+ 409: "Several habits answer to the title (with the candidates), or it is counted from an ingest."
30449
+ },
30450
+ webhook: "habit.updated"
30451
+ },
30452
+ addAlias: {
30453
+ method: "post",
30454
+ path: "/habits/{id}/aliases",
30455
+ scopes: ["habits:write"],
30456
+ tag: "Habits",
30457
+ summary: "Add a habit alias",
30458
+ description: 'Add another name a check-in by title matches ("wake" for "Wake up by 07:00"); one already there (ignoring case) is a no-op, at most 12. Returns the refreshed habit; fires `habit.updated`.',
30459
+ body: habitAliasInputSchema,
30460
+ status: 201,
30461
+ response: { description: "The refreshed habit." },
30462
+ errors: {
30463
+ 400: true,
30464
+ 404: true
30465
+ },
30466
+ webhook: "habit.updated"
30467
+ },
30468
+ removeAlias: {
30469
+ method: "delete",
30470
+ path: "/habits/{id}/aliases/{alias}",
30471
+ pathParams: { alias: "The alias to drop (ignoring case)." },
30472
+ scopes: ["habits:write"],
30473
+ tag: "Habits",
30474
+ summary: "Remove a habit alias",
30475
+ description: "Drop an alias; 404 when the habit doesn't have it. Returns the refreshed habit; fires `habit.updated`.",
30476
+ status: 200,
30477
+ response: { description: "The refreshed habit." },
30478
+ errors: { 404: true },
30479
+ webhook: "habit.updated"
30480
+ },
30481
+ listCheckIns: {
30482
+ method: "get",
30483
+ path: "/habits/{id}/check-ins",
30484
+ scopes: ["habits:read"],
30485
+ tag: "Habits",
30486
+ summary: "List a habit's check-ins",
30487
+ description: "A page of the habit's check-ins, newest first; pass the oldest one's `occurredAt` as `before` for the next page.",
30488
+ query: [
30489
+ { name: "before", description: "Only check-ins before this instant (ISO 8601)." },
30490
+ { name: "limit", description: "Page size, 1\u2013200 (default 50).", type: "integer" }
30491
+ ],
30492
+ querySchema: habitEntriesQuerySchema,
30493
+ status: 200,
30494
+ response: { description: "The check-ins." },
30495
+ errors: { 404: true }
30496
+ },
30497
+ checkIn: {
30498
+ method: "post",
30499
+ path: "/habits/{id}/check-ins",
30500
+ scopes: ["habits:write"],
30501
+ tag: "Habits",
30502
+ summary: "Check in",
30503
+ description: "Record a check-in, with an optional short `note`; fires `habit.updated` with the refreshed streak. A habit counted from an ingest (`source` other than `manual`) answers 409.",
30504
+ body: createHabitEntryInputSchema,
30505
+ status: 201,
30506
+ response: { description: "The refreshed habit." },
30507
+ errors: {
30508
+ 400: true,
30509
+ 404: true
30510
+ },
30511
+ webhook: "habit.updated",
30512
+ operationId: "check_in_habit"
30513
+ },
30514
+ skip: {
30515
+ method: "post",
30516
+ path: "/habits/{id}/skip",
30517
+ scopes: ["habits:write"],
30518
+ tag: "Habits",
30519
+ summary: "Skip a habit",
30520
+ description: '"Skip gym today": a neutral rest day (a `skip` entry, value 0), never a miss. The streak treats the period as neutral and spends no grace. `date` (YYYY-MM-DD) defaults to today; an optional `note` says why ("Dentist"). Skipping a skipped day changes nothing but its note. Undo by deleting the skip entry like a check-in. Returns the refreshed habit; fires `habit.updated`.',
30521
+ body: habitSkipInputSchema,
30522
+ status: 201,
30523
+ response: { description: "The refreshed habit." },
30524
+ errors: {
30525
+ 400: true,
30526
+ 404: true
30527
+ },
30528
+ webhook: "habit.updated"
30529
+ },
30530
+ pause: {
30531
+ method: "post",
30532
+ path: "/habits/{id}/pause",
30533
+ scopes: ["habits:write"],
30534
+ tag: "Habits",
30535
+ summary: "Pause a habit",
30536
+ description: '"Pause gym till the 26th": the habit rests until `until` (the day it\'s back, after today): off Now, no push, streak and plant wait. Pausing a paused habit moves its end. Returns the refreshed habit with `pause`; fires `habit.updated`.',
30537
+ body: habitPauseInputSchema,
30538
+ status: 201,
30539
+ response: { description: "The refreshed habit." },
30540
+ errors: {
30541
+ 400: true,
30542
+ 404: true
30543
+ },
30544
+ webhook: "habit.updated"
30545
+ },
30546
+ resume: {
30547
+ method: "post",
30548
+ path: "/habits/{id}/resume",
30549
+ scopes: ["habits:write"],
30550
+ tag: "Habits",
30551
+ summary: "Resume a habit",
30552
+ description: "End a habit's pause: it's back today. The rest so far stays neutral. Returns the refreshed habit; fires `habit.updated`.",
30553
+ status: 201,
30554
+ response: { description: "The refreshed habit." },
30555
+ errors: { 404: true },
30556
+ webhook: "habit.updated"
30557
+ },
30558
+ retire: {
30559
+ method: "post",
30560
+ path: "/habits/{id}/retire",
30561
+ scopes: ["habits:write"],
30562
+ tag: "Habits",
30563
+ summary: "Retire a habit",
30564
+ description: '"I\'m done with the cold showers habit": the habit moves to the Grown shelf with its plant in bloom and keeps its history. It leaves Now and Today, sends no push, takes no check-in by title and is never due; Progress keeps its past. Returns the refreshed habit with `retiredAt`; undo with `/unretire`. Fires `habit.updated`.',
30565
+ status: 201,
30566
+ response: { description: "The refreshed habit." },
30567
+ errors: { 404: true },
30568
+ webhook: "habit.updated"
30569
+ },
30570
+ unretire: {
30571
+ method: "post",
30572
+ path: "/habits/{id}/unretire",
30573
+ scopes: ["habits:write"],
30574
+ tag: "Habits",
30575
+ summary: "Bring a habit back",
30576
+ description: "Bring a grown (retired) habit back: it's growing again from today, its history intact. Returns the refreshed habit; fires `habit.updated`.",
30577
+ status: 201,
30578
+ response: { description: "The refreshed habit." },
30579
+ errors: { 404: true },
30580
+ webhook: "habit.updated"
30581
+ },
30582
+ updateCheckIn: {
30583
+ method: "patch",
30584
+ path: "/habits/{id}/check-ins/{entryId}",
30585
+ pathParams: { entryId: "The check-in entry's ULID." },
30586
+ scopes: ["habits:write"],
30587
+ tag: "Habits",
30588
+ summary: "Note a check-in",
30589
+ description: "Edit a check-in afterwards: set, replace or clear (`null` or blank) its short `note` (at most 280 characters). The amount and time stay; undo a wrong check-in instead. Returns the check-in; fires `habit.updated`.",
30590
+ body: updateHabitEntryInputSchema,
30591
+ status: 200,
30592
+ response: { description: "The check-in, with its note." },
30593
+ errors: {
30594
+ 400: true,
30595
+ 404: true
30596
+ },
30597
+ webhook: "habit.updated",
30598
+ operationId: "note_habit_check_in"
30599
+ },
30600
+ removeCheckIn: {
30601
+ method: "delete",
30602
+ path: "/habits/{id}/check-ins/{entryId}",
30603
+ pathParams: { entryId: "The check-in entry's ULID." },
30604
+ scopes: ["habits:write"],
30605
+ tag: "Habits",
30606
+ summary: "Undo a check-in",
30607
+ description: "Delete a check-in; fires `habit.updated`.",
30608
+ status: 200,
30609
+ response: { description: "The refreshed habit." },
30610
+ errors: { 404: true },
30611
+ webhook: "habit.updated",
30612
+ operationId: "undo_habit_check_in"
30613
+ }
30614
+ });
30615
+
30616
+ // ../../packages/schema/src/operations/households.ts
30617
+ var householdOperations = defineOperations({
30618
+ ...crudOperations({
30619
+ segment: "households",
30620
+ tag: "Guests",
30621
+ noun: "household",
30622
+ read: "contacts:read",
30623
+ write: "contacts:write",
30624
+ create: createHouseholdInputSchema,
30625
+ update: updateHouseholdInputSchema,
30626
+ listQuery: [{ name: "contactId", description: "Only the households this contact is in." }],
30627
+ listQuerySchema: householdsQuerySchema
30628
+ })
30629
+ });
30630
+
30249
30631
  // ../../packages/schema/src/operations/inventory-items.ts
30250
30632
  var inventoryItemOperations = defineOperations({
30251
30633
  ...crudOperations({
@@ -30500,6 +30882,189 @@ var planOperations = defineOperations({
30500
30882
  undo: decision("/plans/{id}/undo", "undo", "Undo answered items: an accepted item's task is put back as it was before; a dismissed item simply waits again. Both read as proposed afterwards.")
30501
30883
  });
30502
30884
 
30885
+ // ../../packages/schema/src/operations/routines.ts
30886
+ var routineOperations = defineOperations({
30887
+ getRun: {
30888
+ method: "get",
30889
+ path: "/routines/runs/{runId}",
30890
+ pathParams: { runId: "The routine run's ULID." },
30891
+ scopes: ["routines:read"],
30892
+ tag: "Routines",
30893
+ summary: "Get a run",
30894
+ description: "One run with its per-step state.",
30895
+ status: 200,
30896
+ response: { description: "The run." },
30897
+ errors: { 404: true }
30898
+ },
30899
+ updateRun: {
30900
+ method: "patch",
30901
+ path: "/routines/runs/{runId}",
30902
+ pathParams: { runId: "The routine run's ULID." },
30903
+ scopes: ["routines:write"],
30904
+ tag: "Routines",
30905
+ summary: "Update a run",
30906
+ description: "Advance/finish a run; fires `routine.run.updated`.",
30907
+ body: updateRoutineRunInputSchema,
30908
+ status: 200,
30909
+ response: { description: "The updated run." },
30910
+ errors: { 400: true, 404: true },
30911
+ webhook: "routine.run.updated"
30912
+ },
30913
+ updateRunStep: {
30914
+ method: "patch",
30915
+ path: "/routines/runs/{runId}/steps/{stepRunId}",
30916
+ pathParams: { runId: "The routine run's ULID.", stepRunId: "The run step's ULID." },
30917
+ scopes: ["routines:write"],
30918
+ tag: "Routines",
30919
+ summary: "Update a run step",
30920
+ description: "Check off / edit one run step; fires `routine.run.updated`.",
30921
+ body: updateRoutineRunStepInputSchema,
30922
+ status: 200,
30923
+ response: { description: "The updated run." },
30924
+ errors: { 400: true, 404: true },
30925
+ webhook: "routine.run.updated"
30926
+ },
30927
+ undoSteps: {
30928
+ method: "post",
30929
+ path: "/routines/runs/{runId}/undo",
30930
+ pathParams: { runId: "The routine run's ULID." },
30931
+ scopes: ["routines:write"],
30932
+ tag: "Routines",
30933
+ summary: "Undo All taken",
30934
+ description: 'Take an "All taken" back in one call: each named step goes back to open and loses exactly the check-in it wrote; a finished run reopens. Steps no longer done are left alone. Fires `routine.run.updated`.',
30935
+ body: routineRunUndoInputSchema,
30936
+ status: 200,
30937
+ response: { description: "The run." },
30938
+ errors: { 400: true, 404: true },
30939
+ webhook: "routine.run.updated",
30940
+ operationId: "undo_routine_all_taken"
30941
+ },
30942
+ list: {
30943
+ method: "get",
30944
+ path: "/routines",
30945
+ scopes: ["routines:read"],
30946
+ tag: "Routines",
30947
+ summary: "List routines",
30948
+ description: "Every routine (with its steps) in the Space, newest first.",
30949
+ querySchema: paginationQuerySchema,
30950
+ paginated: true,
30951
+ status: 200,
30952
+ response: { description: "The Space's routines." }
30953
+ },
30954
+ get: {
30955
+ method: "get",
30956
+ path: "/routines/{id}",
30957
+ scopes: ["routines:read"],
30958
+ tag: "Routines",
30959
+ summary: "Get a routine",
30960
+ description: "One routine with its step templates.",
30961
+ status: 200,
30962
+ response: { description: "The routine." },
30963
+ errors: { 404: true }
30964
+ },
30965
+ create: {
30966
+ method: "post",
30967
+ path: "/routines",
30968
+ scopes: ["routines:write"],
30969
+ tag: "Routines",
30970
+ summary: "Create a routine",
30971
+ description: "Create a routine (steps ride in the payload); fires `routine.created`.",
30972
+ body: createRoutineInputSchema,
30973
+ status: 201,
30974
+ response: { description: "The created routine." },
30975
+ errors: { 400: true },
30976
+ webhook: "routine.created"
30977
+ },
30978
+ update: {
30979
+ method: "patch",
30980
+ path: "/routines/{id}",
30981
+ scopes: ["routines:write"],
30982
+ tag: "Routines",
30983
+ summary: "Update a routine",
30984
+ description: "Patch a routine; fires `routine.updated`.",
30985
+ body: updateRoutineInputSchema,
30986
+ status: 200,
30987
+ response: { description: "The updated routine." },
30988
+ errors: { 400: true, 404: true },
30989
+ webhook: "routine.updated"
30990
+ },
30991
+ remove: {
30992
+ method: "delete",
30993
+ path: "/routines/{id}",
30994
+ scopes: ["routines:write"],
30995
+ tag: "Routines",
30996
+ summary: "Delete a routine",
30997
+ description: "Soft-delete a routine; fires `routine.deleted`.",
30998
+ status: 204,
30999
+ response: { description: "Deleted." },
31000
+ errors: { 404: true },
31001
+ webhook: "routine.deleted"
31002
+ },
31003
+ attachRecurrence: {
31004
+ method: "put",
31005
+ path: "/routines/{id}/recurrence",
31006
+ scopes: ["routines:write"],
31007
+ tag: "Routines",
31008
+ summary: "Attach recurrence",
31009
+ description: "Attach/replace the routine's recurrence rule; fires `routine.updated`.",
31010
+ body: updateRecurrenceRuleInputSchema,
31011
+ status: 200,
31012
+ response: { description: "The routine." },
31013
+ errors: { 400: true, 404: true },
31014
+ webhook: "routine.updated"
31015
+ },
31016
+ detachRecurrence: {
31017
+ method: "delete",
31018
+ path: "/routines/{id}/recurrence",
31019
+ scopes: ["routines:write"],
31020
+ tag: "Routines",
31021
+ summary: "Detach recurrence",
31022
+ description: "Remove the routine's recurrence rule; returns the routine; fires `routine.updated`.",
31023
+ status: 200,
31024
+ response: { description: "The routine." },
31025
+ errors: { 404: true },
31026
+ webhook: "routine.updated"
31027
+ },
31028
+ markDone: {
31029
+ method: "post",
31030
+ path: "/routines/{id}/done",
31031
+ scopes: ["routines:write"],
31032
+ tag: "Routines",
31033
+ summary: "Check in a whole routine (All taken)",
31034
+ description: "\"Took my supplements\" (#873): one call checks in every open step of today's run (the key's member's day), starting today's run when there's none. Each habit step writes a check-in (`source: routine`); a habit already done today goes done without a second one; a step whose habit is skipped today is left alone. The run finishes once nothing is open. Returns the run and `taken`, the steps it resolved; hand their `stepRunId`s to `POST /api/v1/routines/runs/{runId}/undo` to take it all back. Calling it again changes nothing. Fires `routine.run.updated`.",
31035
+ body: routineDoneInputSchema,
31036
+ status: 200,
31037
+ response: { description: "`{ run, taken }`." },
31038
+ errors: { 400: true, 404: true },
31039
+ webhook: "routine.run.updated",
31040
+ operationId: "routine_all_taken"
31041
+ },
31042
+ startRun: {
31043
+ method: "post",
31044
+ path: "/routines/{id}/runs",
31045
+ scopes: ["routines:write"],
31046
+ tag: "Routines",
31047
+ summary: "Start a run",
31048
+ description: "Start a run of the routine; fires `routine.run.started`.",
31049
+ body: startRoutineRunInputSchema,
31050
+ status: 201,
31051
+ response: { description: "The started run." },
31052
+ errors: { 400: true, 404: true },
31053
+ webhook: "routine.run.started"
31054
+ },
31055
+ listRuns: {
31056
+ method: "get",
31057
+ path: "/routines/{id}/runs",
31058
+ scopes: ["routines:read"],
31059
+ tag: "Routines",
31060
+ summary: "List a routine's runs",
31061
+ description: "The routine's runs, newest-first.",
31062
+ status: 200,
31063
+ response: { description: "The routine's runs." },
31064
+ errors: { 404: true }
31065
+ }
31066
+ });
31067
+
30503
31068
  // ../../packages/schema/src/operations/rsvp-answers.ts
30504
31069
  var rsvpAnswerOperations = defineOperations({
30505
31070
  list: {
@@ -30556,9 +31121,115 @@ var rsvpQuestionOperations = defineOperations({
30556
31121
  description: "Adds the preset questions after the list's own (`diet`: per guest, shown to a guest who said yes to anything, with vegetarian \xB7 vegan \xB7 gluten-free \xB7 lactose-free \xB7 nut allergy \xB7 other, and a text line shown when other is picked; `message`: the household's, once). A preset the list already has stays as it is. Returns the list's questions; fires `rsvp.question.created`.",
30557
31122
  body: addRsvpPresetsInputSchema,
30558
31123
  status: 200,
30559
- response: { description: "The list's questions." },
30560
- errors: { 400: true, 404: true },
30561
- webhook: "rsvp.question.created"
31124
+ response: { description: "The list's questions." },
31125
+ errors: { 400: true, 404: true },
31126
+ webhook: "rsvp.question.created"
31127
+ }
31128
+ });
31129
+
31130
+ // ../../packages/schema/src/operations/sensors.ts
31131
+ var sensorOperations = defineOperations({
31132
+ status: {
31133
+ method: "get",
31134
+ path: "/sensors/home-assistant",
31135
+ scopes: ["places:read"],
31136
+ tag: "Sensors",
31137
+ summary: "Get the Home Assistant link",
31138
+ description: "Whether the Space has linked Home Assistant (in Settings in the app; the token is never returned), the `host` it was linked at, how many entities are mapped (`links`) and `lastReadingAt`, when the newest reading from it landed.",
31139
+ status: 200,
31140
+ response: { description: "The link's status." },
31141
+ errors: { 403: true }
31142
+ },
31143
+ entities: {
31144
+ method: "get",
31145
+ path: "/sensors/home-assistant/entities",
31146
+ scopes: ["places:read"],
31147
+ tag: "Sensors",
31148
+ summary: "List Home Assistant's entities",
31149
+ description: "Every entity Home Assistant has, by `entityId`: its name, domain, state, unit, device class, `lastUpdated`, the reading it most likely gives on a Place and on an asset (`suggested`) and the sensor link it's mapped by, or null. 409 before Home Assistant is linked; 503 when it doesn't answer.",
31150
+ status: 200,
31151
+ response: { description: "The entities, by id." },
31152
+ errors: { 403: true, 409: true, 503: "Home Assistant didn't answer, or refused the token." }
31153
+ },
31154
+ sync: {
31155
+ method: "post",
31156
+ path: "/sensors/home-assistant/sync",
31157
+ scopes: ["places:write"],
31158
+ tag: "Sensors",
31159
+ summary: "Sync Home Assistant now",
31160
+ description: "Read every mapped entity's state once and append it to the datapoints ledger: on a Place as `shed.temp`, `shed.humidity`, `shed.heater`, `shed.water_pct`, `shed.door`, `shed.feed_kg` or `shed.light` (what an animal's room card and the room alarm read), on an asset as `asset.state`, `asset.power` or `asset.energy`. A state already on the ledger, or one Home Assistant can't read, is skipped. The worker does the same every five minutes. Returns `{ syncedAt, entities, written, skipped, missing, error }` (`unauthorized`, `unavailable`, `bad_response`). 409 before Home Assistant is linked.",
31161
+ status: 200,
31162
+ response: { description: "What the sync did." },
31163
+ errors: { 403: true, 409: true }
31164
+ },
31165
+ unifiStatus: {
31166
+ method: "get",
31167
+ path: "/sensors/unifi",
31168
+ scopes: ["places:read"],
31169
+ tag: "Sensors",
31170
+ summary: "Whether UniFi is linked",
31171
+ description: "Whether the Space has linked its UniFi controller (in the app's Settings \u2192 Home), the host and site it was linked at, how many devices are mapped to an asset, and when the newest presence reading landed. Never the API key: a key can't link or unlink UniFi.",
31172
+ status: 200,
31173
+ response: { description: "The status." },
31174
+ errors: { 403: true }
31175
+ },
31176
+ unifiDevices: {
31177
+ method: "get",
31178
+ path: "/sensors/unifi/devices",
31179
+ scopes: ["places:read", "collections:read"],
31180
+ tag: "Sensors",
31181
+ summary: "The devices UniFi sees",
31182
+ description: "Every device the controller manages (gateway, switches, access points, cameras) and every client connected now: `mac`, `name`, `kind` (`device` | `client`), `model`, `online`, `firmware`, and the sensor `link` to the asset it's mapped to (behind the nodes wall). Network details. 409 before UniFi is linked; 503 when the controller can't be read.",
31183
+ status: 200,
31184
+ response: { description: "The devices." },
31185
+ errors: { 403: true, 409: true }
31186
+ },
31187
+ syncUnifi: {
31188
+ method: "post",
31189
+ path: "/sensors/unifi/sync",
31190
+ scopes: ["places:write"],
31191
+ tag: "Sensors",
31192
+ summary: "Sync UniFi now",
31193
+ description: "Map every asset whose facet names a UniFi MAC (`external.unifiMac`), ask the controller once, and put each mapped device's presence on its asset as `asset.presence` (1 online, 0 offline, with its firmware): what the asset page's Live card reads. A reading lands when the presence or firmware changes, else hourly; a device the controller doesn't list reads offline and is named in `missing`. The worker does the same every five minutes. 409 before UniFi is linked.",
31194
+ status: 200,
31195
+ response: { description: "What the sync did." },
31196
+ errors: { 403: true, 409: true }
31197
+ },
31198
+ links: {
31199
+ method: "get",
31200
+ path: "/sensors/links",
31201
+ scopes: ["places:read"],
31202
+ tag: "Sensors",
31203
+ summary: "List the mapped entities",
31204
+ description: "Every mapped entity, or only the ones on `nodeId`, with the name of what it lands on (`nodeTitle`). A mapping on an asset kept Not shared is its member's alone.",
31205
+ query: [{ name: "nodeId", description: "Only the mappings on this Place or asset." }],
31206
+ querySchema: sensorLinksQuerySchema,
31207
+ status: 200,
31208
+ response: { description: "The mappings, by entity id." },
31209
+ errors: { 400: true }
31210
+ },
31211
+ createLink: {
31212
+ method: "post",
31213
+ path: "/sensors/links",
31214
+ scopes: ["places:write"],
31215
+ tag: "Sensors",
31216
+ summary: "Map an entity",
31217
+ description: "Map a Home Assistant entity (`externalId`) to a Place, an asset, or an animal (its room: the Place it's housed in) as a `reading`: a Place takes `temp`, `humidity`, `heater`, `water`, `door`, `feeder` or `light`; an asset `state`, `power` or `energy`. Mapping an entity that's already mapped moves it. 400 when the reading doesn't fit; 404 for no such node; 409 for an animal with no room.",
31218
+ body: createSensorLinkInputSchema,
31219
+ status: 201,
31220
+ response: { description: "The mapping." },
31221
+ errors: { 400: true, 404: true, 409: true }
31222
+ },
31223
+ removeLink: {
31224
+ method: "delete",
31225
+ path: "/sensors/links/{id}",
31226
+ scopes: ["places:write"],
31227
+ tag: "Sensors",
31228
+ summary: "Unmap an entity",
31229
+ description: "Remove one mapping; the readings it gave stay on the ledger.",
31230
+ status: 204,
31231
+ response: { description: "Removed." },
31232
+ errors: { 404: true }
30562
31233
  }
30563
31234
  });
30564
31235
 
@@ -30683,6 +31354,195 @@ var tapTagOperations = defineOperations({
30683
31354
  }
30684
31355
  });
30685
31356
 
31357
+ // ../../packages/schema/src/operations/tasks.ts
31358
+ var taskListQuery = taskListQuerySchema.extend(paginationQuerySchema.shape).extend({ filter: taskListFilterSchema.optional().default("all") });
31359
+ var taskOperations = defineOperations({
31360
+ list: {
31361
+ method: "get",
31362
+ path: "/tasks",
31363
+ scopes: ["tasks:read"],
31364
+ tag: "Tasks",
31365
+ summary: "List tasks",
31366
+ description: "Every task in the key's Space, newest first.",
31367
+ query: [
31368
+ {
31369
+ name: "filter",
31370
+ description: "`all` (default), `unsorted` (the Inbox's tasks to file: open, top-level, not proposed, with no planned day, Deadline, Area or Project) or `starred`.",
31371
+ enum: ["all", "unsorted", "starred"]
31372
+ },
31373
+ {
31374
+ name: "areaId",
31375
+ description: "Only rows in this Area (ULID) \u2014 a project task by its project's Area (its effective Area)."
31376
+ },
31377
+ { name: "projectId", description: "Only tasks attached to this project (ULID)." },
31378
+ {
31379
+ name: "status",
31380
+ description: "Only rows in any of these statuses (comma list or repeated key): `todo`, `in_progress`, `done`, `cancelled`."
31381
+ },
31382
+ {
31383
+ name: "tag",
31384
+ description: "Only tasks carrying every listed tag (comma list or repeated key; case-insensitive, a leading `#` is ignored)."
31385
+ },
31386
+ {
31387
+ name: "updatedSince",
31388
+ description: "Only rows whose `updatedAt` is at or after this instant \u2014 an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight).",
31389
+ format: "date-time"
31390
+ },
31391
+ {
31392
+ name: "dueBefore",
31393
+ description: "Only rows due strictly before this instant (undated rows are excluded) \u2014 an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight).",
31394
+ format: "date-time"
31395
+ },
31396
+ {
31397
+ name: "owner",
31398
+ description: "Only tasks with this Owner: `me`, `member:<id|email>`, `agent:<id|name>` (every desk) or `agent:<id|name>/<desk>`; `any` = owned, `none` = unowned. An unknown party is a 400."
31399
+ },
31400
+ {
31401
+ name: "waitingOn",
31402
+ description: "Only tasks waiting on this party: `me`, `member:<id|email>` or `agent:<id|name>`; `any` = waiting on someone, `none` = not waiting. An unknown party is a 400."
31403
+ },
31404
+ {
31405
+ name: "completedSince",
31406
+ description: "Only tasks finished at or after this instant (`completedAt`; open and cancelled tasks have none) \u2014 an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight).",
31407
+ format: "date-time"
31408
+ },
31409
+ {
31410
+ name: "completedBefore",
31411
+ description: "Only tasks finished strictly before this instant \u2014 an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight).",
31412
+ format: "date-time"
31413
+ },
31414
+ {
31415
+ name: "proposed",
31416
+ description: "`true` = only tasks Proposed by the agent (an Agent made them for a member, who hasn't accepted yet); `false` = only tasks on the plan. Absent = both.",
31417
+ type: "boolean"
31418
+ },
31419
+ {
31420
+ name: "ready",
31421
+ description: "`true` = only open tasks whose Owner marked them Ready for you; `false` = the rest. Absent = both.",
31422
+ type: "boolean"
31423
+ },
31424
+ {
31425
+ name: "drifted",
31426
+ description: "`true` = only open tasks whose planned day moved 3+ times (the weekly plan's Drifted card); `false` = the rest. Absent = both.",
31427
+ type: "boolean"
31428
+ }
31429
+ ],
31430
+ querySchema: taskListQuery,
31431
+ paginated: true,
31432
+ status: 200,
31433
+ response: { description: "The Space's tasks.", list: true }
31434
+ },
31435
+ listSubtasks: {
31436
+ method: "get",
31437
+ path: "/tasks/{id}/subtasks",
31438
+ scopes: ["tasks:read"],
31439
+ tag: "Tasks",
31440
+ summary: "List subtasks",
31441
+ description: "The direct children of a task, ordered by `sortOrder` (ascending), then newest first.",
31442
+ status: 200,
31443
+ response: { description: "The ordered subtasks." },
31444
+ errors: { 404: true }
31445
+ },
31446
+ get: {
31447
+ method: "get",
31448
+ path: "/tasks/{id}",
31449
+ scopes: ["tasks:read"],
31450
+ tag: "Tasks",
31451
+ summary: "Get a task",
31452
+ description: "Fetch one task by id.",
31453
+ status: 200,
31454
+ response: { description: "The task." },
31455
+ errors: { 404: true }
31456
+ },
31457
+ create: {
31458
+ method: "post",
31459
+ path: "/tasks",
31460
+ scopes: ["tasks:write"],
31461
+ tag: "Tasks",
31462
+ summary: "Create a task",
31463
+ description: "Create a task; fires the `task.created` webhook.",
31464
+ body: createTaskInputSchema,
31465
+ status: 201,
31466
+ response: { description: "The created task." },
31467
+ errors: { 400: true, 409: true },
31468
+ webhook: "task.created"
31469
+ },
31470
+ update: {
31471
+ method: "patch",
31472
+ path: "/tasks/{id}",
31473
+ scopes: ["tasks:write"],
31474
+ tag: "Tasks",
31475
+ summary: "Update a task",
31476
+ description: "Patch a task; fires the `task.updated` webhook.",
31477
+ body: updateTaskInputSchema,
31478
+ status: 200,
31479
+ response: { description: "The updated task." },
31480
+ errors: { 400: true, 404: true, 409: true },
31481
+ webhook: "task.updated"
31482
+ },
31483
+ cancel: {
31484
+ method: "post",
31485
+ path: "/tasks/{id}/cancel",
31486
+ scopes: ["tasks:write"],
31487
+ tag: "Tasks",
31488
+ summary: "Cancel a task",
31489
+ description: 'Drop a task: its status becomes `cancelled` and any `completedAt` is cleared, so a drop never counts as Landed (#648). Fires `task.updated`. The same as a PATCH to `status: "cancelled"`.',
31490
+ status: 200,
31491
+ response: { description: "The cancelled task." },
31492
+ errors: { 404: true },
31493
+ webhook: "task.updated"
31494
+ },
31495
+ accept: {
31496
+ method: "post",
31497
+ path: "/tasks/{id}/accept",
31498
+ scopes: ["tasks:write"],
31499
+ tag: "Tasks",
31500
+ summary: "Accept a proposal",
31501
+ description: "Take a task Proposed by the agent onto the member's plan as it was suggested: its planned day, Deadline and Area stay; `proposedAt` clears (#889). A no-op success on a task that isn't a proposal. Fires `task.updated`. The same as a PATCH to `proposed: false`.",
31502
+ status: 200,
31503
+ response: { description: "The accepted task." },
31504
+ errors: { 404: true },
31505
+ webhook: "task.updated"
31506
+ },
31507
+ notNow: {
31508
+ method: "post",
31509
+ path: "/tasks/{id}/not-now",
31510
+ scopes: ["tasks:write"],
31511
+ tag: "Tasks",
31512
+ summary: "Not now on a proposal",
31513
+ description: "Keep a task Proposed by the agent, but out of the member's Inbox for a week (`notNowUntil`, #889). Let go is `cancel`. Fires `task.updated`.",
31514
+ status: 200,
31515
+ response: { description: "The proposal, back in a week." },
31516
+ errors: { 404: true, 409: "The task isn't a proposal." },
31517
+ webhook: "task.updated"
31518
+ },
31519
+ ready: {
31520
+ method: "post",
31521
+ path: "/tasks/{id}/ready",
31522
+ scopes: ["tasks:write"],
31523
+ tag: "Tasks",
31524
+ summary: "Mark Ready for you",
31525
+ description: "The task's Owner (the Space's agent, a Desk, a coding agent) finished something the member should look at: `readyAt` is set and the task reaches Now's Needs you with Review (#902). Looks good marks it done; Send back is a PATCH to `ready: false` (it reads Working on it again). The optional `note` is what's ready, in a line, with a link when there is one; absent keeps the note already there. Fires `task.updated`.",
31526
+ body: markTaskReadyInputSchema,
31527
+ status: 200,
31528
+ response: { description: "The task, Ready for you." },
31529
+ errors: { 404: true, 409: "The task is done or let go; only an open task can be ready." },
31530
+ webhook: "task.updated"
31531
+ },
31532
+ remove: {
31533
+ method: "delete",
31534
+ path: "/tasks/{id}",
31535
+ scopes: ["tasks:write"],
31536
+ tag: "Tasks",
31537
+ summary: "Delete a task",
31538
+ description: "Delete a task; fires the `task.deleted` webhook.",
31539
+ status: 204,
31540
+ response: { description: "Deleted." },
31541
+ errors: { 404: true },
31542
+ webhook: "task.deleted"
31543
+ }
31544
+ });
31545
+
30686
31546
  // ../../packages/schema/src/operations/today.ts
30687
31547
  var todayOperations = defineOperations({
30688
31548
  fromEarlier: {
@@ -31032,7 +31892,7 @@ function parseCapture2(input, now) {
31032
31892
 
31033
31893
  // src/meta.ts
31034
31894
  var CLI_NAME = "do";
31035
- var VERSION = "1.304.0";
31895
+ var VERSION = "1.306.0";
31036
31896
  var API_PREFIX = "/api/v1";
31037
31897
  var DEFAULT_API_URL = "http://localhost:3001";
31038
31898
 
@@ -31451,12 +32311,9 @@ function entityFromOperations(ops, spec) {
31451
32311
  };
31452
32312
  }
31453
32313
  var ENTITIES = [
31454
- {
32314
+ entityFromOperations(taskOperations, {
31455
32315
  name: "tasks",
31456
- path: "/tasks",
31457
- readScopes: ["tasks:read"],
31458
32316
  returns: "Task",
31459
- canGet: true,
31460
32317
  listFlags: [
31461
32318
  {
31462
32319
  name: "filter",
@@ -31498,21 +32355,12 @@ var ENTITIES = [
31498
32355
  boolean: true
31499
32356
  }
31500
32357
  ],
31501
- write: {
31502
- scopes: ["tasks:write"],
31503
- createSchema: createTaskInputSchema,
31504
- createInput: "CreateTaskInput",
31505
- updateSchema: updateTaskInputSchema,
31506
- updateInput: "UpdateTaskInput",
31507
- canDelete: true
31508
- }
31509
- },
31510
- {
32358
+ createInput: "CreateTaskInput",
32359
+ updateInput: "UpdateTaskInput"
32360
+ }),
32361
+ entityFromOperations(habitOperations, {
31511
32362
  name: "habits",
31512
- path: "/habits",
31513
- readScopes: ["habits:read"],
31514
32363
  returns: "HabitSummary",
31515
- canGet: true,
31516
32364
  listFlags: [
31517
32365
  AREA_FLAG,
31518
32366
  UPDATED_SINCE_FLAG,
@@ -31521,15 +32369,9 @@ var ENTITIES = [
31521
32369
  description: "list the Grown shelf (retired habits) instead of the growing ones"
31522
32370
  }
31523
32371
  ],
31524
- write: {
31525
- scopes: ["habits:write"],
31526
- createSchema: createHabitInputSchema,
31527
- createInput: "CreateHabitInput",
31528
- updateSchema: updateHabitInputSchema,
31529
- updateInput: "UpdateHabitInput",
31530
- canDelete: true
31531
- }
31532
- },
32372
+ createInput: "CreateHabitInput",
32373
+ updateInput: "UpdateHabitInput"
32374
+ }),
31533
32375
  {
31534
32376
  name: "projects",
31535
32377
  path: "/projects",
@@ -31594,22 +32436,13 @@ var ENTITIES = [
31594
32436
  canDelete: true
31595
32437
  }
31596
32438
  },
31597
- {
32439
+ entityFromOperations(routineOperations, {
31598
32440
  name: "routines",
31599
- path: "/routines",
31600
- readScopes: ["routines:read"],
31601
32441
  returns: "RoutineView",
31602
- canGet: true,
31603
32442
  listFlags: [],
31604
- write: {
31605
- scopes: ["routines:write"],
31606
- createSchema: createRoutineInputSchema,
31607
- createInput: "CreateRoutineInput",
31608
- updateSchema: updateRoutineInputSchema,
31609
- updateInput: "UpdateRoutineInput",
31610
- canDelete: true
31611
- }
31612
- },
32443
+ createInput: "CreateRoutineInput",
32444
+ updateInput: "UpdateRoutineInput"
32445
+ }),
31613
32446
  {
31614
32447
  name: "notes",
31615
32448
  path: "/notes",
@@ -31741,12 +32574,9 @@ var ENTITIES = [
31741
32574
  canDelete: true
31742
32575
  }
31743
32576
  },
31744
- {
32577
+ entityFromOperations(householdOperations, {
31745
32578
  name: "households",
31746
- path: "/households",
31747
- readScopes: ["contacts:read"],
31748
32579
  returns: "HouseholdView",
31749
- canGet: true,
31750
32580
  listFlags: [
31751
32581
  {
31752
32582
  name: "contact",
@@ -31754,15 +32584,9 @@ var ENTITIES = [
31754
32584
  query: "contactId"
31755
32585
  }
31756
32586
  ],
31757
- write: {
31758
- scopes: ["contacts:write"],
31759
- createSchema: createHouseholdInputSchema,
31760
- createInput: "CreateHouseholdInput",
31761
- updateSchema: updateHouseholdInputSchema,
31762
- updateInput: "UpdateHouseholdInput",
31763
- canDelete: true
31764
- }
31765
- },
32587
+ createInput: "CreateHouseholdInput",
32588
+ updateInput: "UpdateHouseholdInput"
32589
+ }),
31766
32590
  {
31767
32591
  name: "guest-lists",
31768
32592
  path: "/guest-lists",
@@ -31779,42 +32603,24 @@ var ENTITIES = [
31779
32603
  canDelete: true
31780
32604
  }
31781
32605
  },
31782
- {
32606
+ entityFromOperations(guestListEventOperations, {
31783
32607
  name: "guest-list-events",
31784
- path: "/guest-list-events",
31785
- readScopes: ["contacts:read"],
31786
32608
  returns: "GuestListEvent",
31787
- canGet: true,
31788
32609
  listFlags: [
31789
32610
  { name: "list", description: "the guest list's id (required)", query: "guestListId" }
31790
32611
  ],
31791
- write: {
31792
- scopes: ["contacts:write"],
31793
- createSchema: createGuestListEventInputSchema,
31794
- createInput: "CreateGuestListEventInput",
31795
- updateSchema: updateGuestListEventInputSchema,
31796
- updateInput: "UpdateGuestListEventInput",
31797
- canDelete: true
31798
- }
31799
- },
31800
- {
32612
+ createInput: "CreateGuestListEventInput",
32613
+ updateInput: "UpdateGuestListEventInput"
32614
+ }),
32615
+ entityFromOperations(guestTableOperations, {
31801
32616
  name: "guest-tables",
31802
- path: "/guest-tables",
31803
- readScopes: ["contacts:read"],
31804
32617
  returns: "GuestTableView",
31805
- canGet: true,
31806
32618
  listFlags: [
31807
32619
  { name: "list", description: "the guest list's id (required)", query: "guestListId" }
31808
32620
  ],
31809
- write: {
31810
- scopes: ["contacts:write"],
31811
- createSchema: createGuestTableInputSchema,
31812
- createInput: "CreateGuestTableInput",
31813
- updateSchema: updateGuestTableInputSchema,
31814
- updateInput: "UpdateGuestTableInput",
31815
- canDelete: true
31816
- }
31817
- },
32621
+ createInput: "CreateGuestTableInput",
32622
+ updateInput: "UpdateGuestTableInput"
32623
+ }),
31818
32624
  entityFromOperations(rsvpQuestionOperations, {
31819
32625
  name: "rsvp-questions",
31820
32626
  returns: "RsvpQuestion",
@@ -31824,26 +32630,17 @@ var ENTITIES = [
31824
32630
  createInput: "CreateRsvpQuestionInput",
31825
32631
  updateInput: "UpdateRsvpQuestionInput"
31826
32632
  }),
31827
- {
32633
+ entityFromOperations(guestOperations, {
31828
32634
  name: "guests",
31829
- path: "/guests",
31830
- readScopes: ["contacts:read"],
31831
32635
  returns: "GuestView",
31832
- canGet: true,
31833
32636
  listFlags: [
31834
32637
  { name: "list", description: "only this guest list's guests", query: "guestListId" },
31835
32638
  { name: "contact", description: "only this contact's guest rows", query: "contactId" },
31836
32639
  { name: "state", description: "yes, no, maybe or waiting (overall)", query: "state" }
31837
32640
  ],
31838
- write: {
31839
- scopes: ["contacts:write"],
31840
- createSchema: createGuestInputSchema,
31841
- createInput: "CreateGuestInput",
31842
- updateSchema: updateGuestInputSchema,
31843
- updateInput: "UpdateGuestInput",
31844
- canDelete: true
31845
- }
31846
- },
32641
+ createInput: "CreateGuestInput",
32642
+ updateInput: "UpdateGuestInput"
32643
+ }),
31847
32644
  {
31848
32645
  name: "key-dates",
31849
32646
  path: "/key-dates",
@@ -33299,27 +34096,27 @@ var COMMAND_SPECS = [
33299
34096
  summary: "Drop a task: status cancelled, never counted as Landed.",
33300
34097
  args: [{ name: "id", required: true, description: "Task ULID." }],
33301
34098
  flags: [],
33302
- scopes: ["tasks:write"],
34099
+ scopes: [...taskOperations.cancel.scopes],
33303
34100
  returns: "Task",
33304
- notes: "POST /api/v1/tasks/{id}/cancel. Use this, not `task done`, for a dropped task."
34101
+ notes: `${routeOf(taskOperations.cancel)}. Use this, not \`task done\`, for a dropped task.`
33305
34102
  },
33306
34103
  {
33307
34104
  path: ["tasks", "accept"],
33308
34105
  summary: "Accept a task Proposed by the agent: it joins the plan as suggested (day, Deadline, Area kept).",
33309
34106
  args: [{ name: "id", required: true, description: "Task ULID." }],
33310
34107
  flags: [],
33311
- scopes: ["tasks:write"],
34108
+ scopes: [...taskOperations.accept.scopes],
33312
34109
  returns: "Task",
33313
- notes: "POST /api/v1/tasks/{id}/accept (#889). A no-op on a task that isn't a proposal. List proposals with `do tasks list --proposed`."
34110
+ notes: `${routeOf(taskOperations.accept)} (#889). A no-op on a task that isn't a proposal. List proposals with \`do tasks list --proposed\`.`
33314
34111
  },
33315
34112
  {
33316
34113
  path: ["tasks", "dismiss"],
33317
34114
  summary: "Not now on a task Proposed by the agent: it leaves the Inbox for a week, still proposed.",
33318
34115
  args: [{ name: "id", required: true, description: "Task ULID." }],
33319
34116
  flags: [],
33320
- scopes: ["tasks:write"],
34117
+ scopes: [...taskOperations.notNow.scopes],
33321
34118
  returns: "Task",
33322
- notes: "POST /api/v1/tasks/{id}/not-now (#889); 409 when the task isn't a proposal. Let go is `do task cancel`."
34119
+ notes: `${routeOf(taskOperations.notNow)} (#889); 409 when the task isn't a proposal. Let go is \`do task cancel\`.`
33323
34120
  },
33324
34121
  {
33325
34122
  path: ["block-templates", "week"],
@@ -33613,16 +34410,16 @@ var COMMAND_SPECS = [
33613
34410
  description: `What's ready, in a line, with a link when there is one ("Draft ready: 38 hours, 3,420 euro \u2014 https://\u2026"); at most 500 characters. Absent keeps the note already there.`
33614
34411
  }
33615
34412
  ],
33616
- scopes: ["tasks:write"],
34413
+ scopes: [...taskOperations.ready.scopes],
33617
34414
  returns: "Task",
33618
- notes: "POST /api/v1/tasks/{id}/ready (#902); 409 when the task is done or let go. It reaches Now's Needs you with Review (Looks good = done, Send back = `ready: false`). List them with `do tasks list --ready`."
34415
+ notes: `${routeOf(taskOperations.ready)} (#902); 409 when the task is done or let go. It reaches Now's Needs you with Review (Looks good = done, Send back = \`ready: false\`). List them with \`do tasks list --ready\`.`
33619
34416
  },
33620
34417
  {
33621
34418
  path: ["task", "reopen"],
33622
34419
  summary: "Reopen a done or cancelled task (status todo; clears completedAt).",
33623
34420
  args: [{ name: "id", required: true, description: "Task ULID." }],
33624
34421
  flags: [],
33625
- scopes: ["tasks:write"],
34422
+ scopes: [...taskOperations.update.scopes],
33626
34423
  returns: "Task"
33627
34424
  },
33628
34425
  {
@@ -33642,7 +34439,7 @@ var COMMAND_SPECS = [
33642
34439
  description: "How long it takes, in minutes (sets durationMinutes)."
33643
34440
  }
33644
34441
  ],
33645
- scopes: ["tasks:write"],
34442
+ scopes: [...taskOperations.update.scopes],
33646
34443
  returns: "Task"
33647
34444
  },
33648
34445
  {
@@ -33661,7 +34458,7 @@ var COMMAND_SPECS = [
33661
34458
  description: "How long it takes, in minutes (sets durationMinutes)."
33662
34459
  }
33663
34460
  ],
33664
- scopes: ["tasks:write"],
34461
+ scopes: [...taskOperations.update.scopes],
33665
34462
  returns: "Task"
33666
34463
  },
33667
34464
  {
@@ -33682,7 +34479,7 @@ var COMMAND_SPECS = [
33682
34479
  description: 'A short note on the check-in ("felt heavy"), at most 280 characters.'
33683
34480
  }
33684
34481
  ],
33685
- scopes: ["habits:write"],
34482
+ scopes: [...habitOperations.checkIn.scopes],
33686
34483
  returns: "HabitSummary"
33687
34484
  },
33688
34485
  {
@@ -33709,7 +34506,7 @@ var COMMAND_SPECS = [
33709
34506
  description: 'A short note on the check-in ("felt heavy"), at most 280 characters.'
33710
34507
  }
33711
34508
  ],
33712
- scopes: ["habits:write"],
34509
+ scopes: [...habitOperations.checkInByTitle.scopes],
33713
34510
  returns: "HabitSummary",
33714
34511
  notes: `POST /api/v1/habits/check-in. Title or alias, case-insensitive; several matches \u2192 409 with error.details.candidates (id, title, aliases), none \u2192 404 naming close titles; a habit counted from Google Fit, Strava or GitHub \u2192 409. --set writes the difference to the day's total as its own check-in. An Agent's key records the check-in as that Agent's ("<agent>, 07:12"). --note puts a short note on it ("drill done, felt heavy" \u2192 --note "felt heavy"); the reply's lastCheckIn.id is the check-in, for \`do habit note\` later.`
33715
34512
  },
@@ -33721,7 +34518,7 @@ var COMMAND_SPECS = [
33721
34518
  { name: "alias", required: true, description: "The other name." }
33722
34519
  ],
33723
34520
  flags: [],
33724
- scopes: ["habits:write"],
34521
+ scopes: [...habitOperations.addAlias.scopes],
33725
34522
  returns: "HabitSummary",
33726
34523
  notes: "POST /api/v1/habits/:id/aliases. One already there (ignoring case) is a no-op; at most 12."
33727
34524
  },
@@ -33733,7 +34530,7 @@ var COMMAND_SPECS = [
33733
34530
  { name: "alias", required: true, description: "The alias to drop (ignoring case)." }
33734
34531
  ],
33735
34532
  flags: [],
33736
- scopes: ["habits:write"],
34533
+ scopes: [...habitOperations.removeAlias.scopes],
33737
34534
  returns: "HabitSummary",
33738
34535
  notes: "DELETE /api/v1/habits/:id/aliases/:alias. 404 when the habit doesn't have it."
33739
34536
  },
@@ -33742,9 +34539,9 @@ var COMMAND_SPECS = [
33742
34539
  summary: "What's left today: the habits due by part of day, and today's routines.",
33743
34540
  args: [],
33744
34541
  flags: [],
33745
- scopes: ["habits:read"],
34542
+ scopes: [...habitOperations.today.scopes],
33746
34543
  returns: "HabitsTodayResponse",
33747
- notes: "GET /api/v1/habits/today. Your saved time zone's day; already filtered by schedule. Each habit says done/open/skipped (never overdue), its value today, the week for an N-per-week habit and its last check-in (`by` names the Agent). Quit habits are left out."
34544
+ notes: `${routeOf(habitOperations.today)}. Your saved time zone's day; already filtered by schedule. Each habit says done/open/skipped (never overdue), its value today, the week for an N-per-week habit and its last check-in (\`by\` names the Agent). Quit habits are left out.`
33748
34545
  },
33749
34546
  {
33750
34547
  path: ["habit", "check-ins"],
@@ -33754,7 +34551,7 @@ var COMMAND_SPECS = [
33754
34551
  { name: "before", type: "string", description: "Only check-ins before this instant (ISO)." },
33755
34552
  { name: "limit", type: "string", description: "Page size 1\u2013200 (default 50)." }
33756
34553
  ],
33757
- scopes: ["habits:read"],
34554
+ scopes: [...habitOperations.listCheckIns.scopes],
33758
34555
  returns: "HabitEntry"
33759
34556
  },
33760
34557
  {
@@ -33770,9 +34567,9 @@ var COMMAND_SPECS = [
33770
34567
  { name: "note", required: false, description: "The note, at most 280 characters." }
33771
34568
  ],
33772
34569
  flags: [{ name: "clear", type: "boolean", description: "Drop the check-in's note." }],
33773
- scopes: ["habits:write"],
34570
+ scopes: [...habitOperations.updateCheckIn.scopes],
33774
34571
  returns: "HabitEntry",
33775
- notes: "PATCH /api/v1/habits/{id}/check-ins/{entryId}. Replaces the note; the amount and time stay (undo a wrong check-in instead). A skip's note is its why."
34572
+ notes: `${routeOf(habitOperations.updateCheckIn)}. Replaces the note; the amount and time stay (undo a wrong check-in instead). A skip's note is its why.`
33776
34573
  },
33777
34574
  {
33778
34575
  path: ["habit", "skip"],
@@ -33786,9 +34583,9 @@ var COMMAND_SPECS = [
33786
34583
  description: 'Why, if you like ("Dentist"); shows in the log.'
33787
34584
  }
33788
34585
  ],
33789
- scopes: ["habits:write"],
34586
+ scopes: [...habitOperations.skip.scopes],
33790
34587
  returns: "HabitSummary",
33791
- notes: "POST /api/v1/habits/{id}/skip. The streak treats the period as neutral and spends no grace; the habit leaves Now for the day. Skipping a skipped day changes nothing. Undo: delete the skip entry (`do habit check-ins <id>` lists it with kind `skip`)."
34588
+ notes: `${routeOf(habitOperations.skip)}. The streak treats the period as neutral and spends no grace; the habit leaves Now for the day. Skipping a skipped day changes nothing. Undo: delete the skip entry (\`do habit check-ins <id>\` lists it with kind \`skip\`).`
33792
34589
  },
33793
34590
  {
33794
34591
  path: ["routine", "done"],
@@ -33801,9 +34598,9 @@ var COMMAND_SPECS = [
33801
34598
  }
33802
34599
  ],
33803
34600
  flags: [],
33804
- scopes: ["routines:write"],
34601
+ scopes: [...routineOperations.markDone.scopes],
33805
34602
  returns: "RoutineDoneResult",
33806
- notes: "POST /api/v1/routines/{id}/done. Today's run (your saved time zone's day), started if there's none. Each habit step checks in its habit; a habit already done today isn't counted twice; a step whose habit is skipped today is left alone. Calling it again changes nothing. Returns `run` and `taken` (the steps it resolved). Undo all of it with `do routine undo <run.id> <taken stepRunIds\u2026>`."
34603
+ notes: `${routeOf(routineOperations.markDone)}. Today's run (your saved time zone's day), started if there's none. Each habit step checks in its habit; a habit already done today isn't counted twice; a step whose habit is skipped today is left alone. Calling it again changes nothing. Returns \`run\` and \`taken\` (the steps it resolved). Undo all of it with \`do routine undo <run.id> <taken stepRunIds\u2026>\`.`
33807
34604
  },
33808
34605
  {
33809
34606
  path: ["routine", "undo"],
@@ -33821,9 +34618,9 @@ var COMMAND_SPECS = [
33821
34618
  }
33822
34619
  ],
33823
34620
  flags: [],
33824
- scopes: ["routines:write"],
34621
+ scopes: [...routineOperations.undoSteps.scopes],
33825
34622
  returns: "RoutineRunView",
33826
- notes: "POST /api/v1/routines/runs/{runId}/undo. Exactly the check-ins those steps wrote are removed; a finished run reopens. Steps no longer done are left alone."
34623
+ notes: `${routeOf(routineOperations.undoSteps)}. Exactly the check-ins those steps wrote are removed; a finished run reopens. Steps no longer done are left alone.`
33827
34624
  },
33828
34625
  {
33829
34626
  path: ["habit", "pause"],
@@ -33836,9 +34633,9 @@ var COMMAND_SPECS = [
33836
34633
  description: "The day it's back (YYYY-MM-DD), after today. Required."
33837
34634
  }
33838
34635
  ],
33839
- scopes: ["habits:write"],
34636
+ scopes: [...habitOperations.pause.scopes],
33840
34637
  returns: "HabitSummary",
33841
- notes: "POST /api/v1/habits/{id}/pause. Pausing a paused habit moves the day it's back. Paused days are neutral: the streak is frozen, never broken."
34638
+ notes: `${routeOf(habitOperations.pause)}. Pausing a paused habit moves the day it's back. Paused days are neutral: the streak is frozen, never broken.`
33842
34639
  },
33843
34640
  {
33844
34641
  path: ["habit", "remind"],
@@ -33848,36 +34645,36 @@ var COMMAND_SPECS = [
33848
34645
  { name: "state", required: true, description: "`on` or `off`." }
33849
34646
  ],
33850
34647
  flags: [],
33851
- scopes: ["habits:write"],
34648
+ scopes: [...habitOperations.update.scopes],
33852
34649
  returns: "HabitSummary",
33853
- notes: "PATCH /api/v1/habits/{id} {remind}. Off by default. On, the habit sends one Web Push a day at its time of day (a part of day: morning 08:00, midday 12:00, evening 18:00; none: 09:00), only in waking hours (07:00\u201322:00), with Done / +1 / Skip today buttons; not when it's done, skipped, paused or off today."
34650
+ notes: `${routeOf(habitOperations.update)} {remind}. Off by default. On, the habit sends one Web Push a day at its time of day (a part of day: morning 08:00, midday 12:00, evening 18:00; none: 09:00), only in waking hours (07:00\u201322:00), with Done / +1 / Skip today buttons; not when it's done, skipped, paused or off today.`
33854
34651
  },
33855
34652
  {
33856
34653
  path: ["habit", "resume"],
33857
34654
  summary: "Resume a paused habit: it's back today.",
33858
34655
  args: [{ name: "id", required: true, description: "Habit ULID." }],
33859
34656
  flags: [],
33860
- scopes: ["habits:write"],
34657
+ scopes: [...habitOperations.resume.scopes],
33861
34658
  returns: "HabitSummary",
33862
- notes: "POST /api/v1/habits/{id}/resume. The rest so far stays neutral."
34659
+ notes: `${routeOf(habitOperations.resume)}. The rest so far stays neutral.`
33863
34660
  },
33864
34661
  {
33865
34662
  path: ["habit", "retire"],
33866
34663
  summary: `Retire a habit to the Grown shelf ("I'm done with the cold showers habit"): history kept.`,
33867
34664
  args: [{ name: "id", required: true, description: "Habit ULID." }],
33868
34665
  flags: [],
33869
- scopes: ["habits:write"],
34666
+ scopes: [...habitOperations.retire.scopes],
33870
34667
  returns: "HabitSummary",
33871
- notes: "POST /api/v1/habits/{id}/retire. Not a delete: it keeps every check-in and its plant blooms on the Grown shelf. It leaves Now and Today, sends no push, takes no check-in by title and is never due; Progress keeps its past. `do habits list --retired` lists the shelf. Undo: `do habit unretire <id>`."
34668
+ notes: `${routeOf(habitOperations.retire)}. Not a delete: it keeps every check-in and its plant blooms on the Grown shelf. It leaves Now and Today, sends no push, takes no check-in by title and is never due; Progress keeps its past. \`do habits list --retired\` lists the shelf. Undo: \`do habit unretire <id>\`.`
33872
34669
  },
33873
34670
  {
33874
34671
  path: ["habit", "unretire"],
33875
34672
  summary: "Bring a grown (retired) habit back: growing again from today.",
33876
34673
  args: [{ name: "id", required: true, description: "Habit ULID." }],
33877
34674
  flags: [],
33878
- scopes: ["habits:write"],
34675
+ scopes: [...habitOperations.unretire.scopes],
33879
34676
  returns: "HabitSummary",
33880
- notes: "POST /api/v1/habits/{id}/unretire. Its history is intact."
34677
+ notes: `${routeOf(habitOperations.unretire)}. Its history is intact.`
33881
34678
  },
33882
34679
  {
33883
34680
  path: ["away"],
@@ -36561,63 +37358,63 @@ var COMMAND_SPECS = [
36561
37358
  summary: "Whether the Space has linked Home Assistant, how many entities are mapped, the newest reading.",
36562
37359
  args: [],
36563
37360
  flags: [],
36564
- scopes: ["places:read"],
37361
+ scopes: [...sensorOperations.status.scopes],
36565
37362
  returns: "HomeAssistantStatus",
36566
- notes: "GET /api/v1/sensors/home-assistant. Home Assistant is linked in the app's Settings \u2192 Home; the token is never returned."
37363
+ notes: `${routeOf(sensorOperations.status)}. Home Assistant is linked in the app's Settings \u2192 Home; the token is never returned.`
36567
37364
  },
36568
37365
  {
36569
37366
  path: ["sensors", "entities"],
36570
37367
  summary: "Every entity Home Assistant has, with its state, the reading it likely gives and what it's mapped to.",
36571
37368
  args: [],
36572
37369
  flags: [],
36573
- scopes: ["places:read"],
37370
+ scopes: [...sensorOperations.entities.scopes],
36574
37371
  returns: "HomeAssistantEntity[]",
36575
- notes: "GET /api/v1/sensors/home-assistant/entities. 409 before Home Assistant is linked; 503 when it doesn't answer."
37372
+ notes: `${routeOf(sensorOperations.entities)}. 409 before Home Assistant is linked; 503 when it doesn't answer.`
36576
37373
  },
36577
37374
  {
36578
37375
  path: ["sensors", "sync"],
36579
37376
  summary: "Read every mapped entity now and put its state on the ledger, as the five-minute sync does.",
36580
37377
  args: [],
36581
37378
  flags: [],
36582
- scopes: ["places:write"],
37379
+ scopes: [...sensorOperations.sync.scopes],
36583
37380
  returns: "SensorSyncResult",
36584
- notes: "POST /api/v1/sensors/home-assistant/sync. A room's readings feed its card and the room alarm; an asset's show on its page. Returns entities, written, skipped, missing and the error (unauthorized, unavailable, bad_response). 409 before Home Assistant is linked."
37381
+ notes: `${routeOf(sensorOperations.sync)}. A room's readings feed its card and the room alarm; an asset's show on its page. Returns entities, written, skipped, missing and the error (unauthorized, unavailable, bad_response). 409 before Home Assistant is linked.`
36585
37382
  },
36586
37383
  {
36587
37384
  path: ["sensors", "unifi"],
36588
37385
  summary: "Whether the Space has linked UniFi, how many devices are mapped, the newest reading.",
36589
37386
  args: [],
36590
37387
  flags: [],
36591
- scopes: ["places:read"],
37388
+ scopes: [...sensorOperations.unifiStatus.scopes],
36592
37389
  returns: "UnifiStatus",
36593
- notes: "GET /api/v1/sensors/unifi. Linking UniFi (address + API key) is a member's, in Settings \u2192 Home; a key never sees the API key."
37390
+ notes: `${routeOf(sensorOperations.unifiStatus)}. Linking UniFi (address + API key) is a member's, in Settings \u2192 Home; a key never sees the API key.`
36594
37391
  },
36595
37392
  {
36596
37393
  path: ["sensors", "unifi-devices"],
36597
37394
  summary: "Every device and connected client UniFi sees, with the asset each is mapped to.",
36598
37395
  args: [],
36599
37396
  flags: [],
36600
- scopes: ["places:read", "collections:read"],
37397
+ scopes: [...sensorOperations.unifiDevices.scopes],
36601
37398
  returns: "UnifiDevice[]",
36602
- notes: "GET /api/v1/sensors/unifi/devices. Map one with `do sensors map <mac> <asset> --reading presence --provider unifi`, or give the asset `external.unifiMac` on its facet. 409 before UniFi is linked."
37399
+ notes: `${routeOf(sensorOperations.unifiDevices)}. Map one with \`do sensors map <mac> <asset> --reading presence --provider unifi\`, or give the asset \`external.unifiMac\` on its facet. 409 before UniFi is linked.`
36603
37400
  },
36604
37401
  {
36605
37402
  path: ["sensors", "unifi-sync"],
36606
37403
  summary: "Put every mapped device's presence (online/offline, firmware) on its asset now.",
36607
37404
  args: [],
36608
37405
  flags: [],
36609
- scopes: ["places:write"],
37406
+ scopes: [...sensorOperations.syncUnifi.scopes],
36610
37407
  returns: "SensorSyncResult",
36611
- notes: "POST /api/v1/sensors/unifi/sync. Assets whose facet names a UniFi MAC are mapped first; readings land as asset.presence on change, else hourly. The worker does the same every five minutes. 409 before UniFi is linked."
37408
+ notes: `${routeOf(sensorOperations.syncUnifi)}. Assets whose facet names a UniFi MAC are mapped first; readings land as asset.presence on change, else hourly. The worker does the same every five minutes. 409 before UniFi is linked.`
36612
37409
  },
36613
37410
  {
36614
37411
  path: ["sensors", "links"],
36615
37412
  summary: "The mapped entities, or the ones on one Place or asset.",
36616
37413
  args: [],
36617
37414
  flags: [{ name: "node", type: "string", description: "Only the mappings on this node ULID." }],
36618
- scopes: ["places:read"],
37415
+ scopes: [...sensorOperations.links.scopes],
36619
37416
  returns: "SensorLink[]",
36620
- notes: "GET /api/v1/sensors/links."
37417
+ notes: `${routeOf(sensorOperations.links)}.`
36621
37418
  },
36622
37419
  {
36623
37420
  path: ["sensors", "map"],
@@ -36643,18 +37440,18 @@ var COMMAND_SPECS = [
36643
37440
  },
36644
37441
  { name: "label", type: "string", description: "A name for it (Home Assistant's own)." }
36645
37442
  ],
36646
- scopes: ["places:write"],
37443
+ scopes: [...sensorOperations.createLink.scopes],
36647
37444
  returns: "SensorLink",
36648
- notes: "POST /api/v1/sensors/links. An animal maps to the Place it's housed in (409 when it has none). Mapping an entity again moves it."
37445
+ notes: `${routeOf(sensorOperations.createLink)}. An animal maps to the Place it's housed in (409 when it has none). Mapping an entity again moves it.`
36649
37446
  },
36650
37447
  {
36651
37448
  path: ["sensors", "unmap"],
36652
37449
  summary: "Unmap an entity; the readings it gave stay.",
36653
37450
  args: [{ name: "link", required: true, description: "The sensor link ULID." }],
36654
37451
  flags: [],
36655
- scopes: ["places:write"],
37452
+ scopes: [...sensorOperations.removeLink.scopes],
36656
37453
  returns: "None",
36657
- notes: "DELETE /api/v1/sensors/links/{id}."
37454
+ notes: `${routeOf(sensorOperations.removeLink)}.`
36658
37455
  },
36659
37456
  {
36660
37457
  path: ["runs", "sessions", "list"],
@@ -36964,7 +37761,7 @@ var COMMAND_SPECS = [
36964
37761
  description: 'JSON MarkGuestsSentInput, e.g. {"guestIds":["01\u2026"],"what":"save_the_date"} (or "invite"; "at" defaults to now).'
36965
37762
  }
36966
37763
  ],
36967
- scopes: ["contacts:write"],
37764
+ scopes: [...guestOperations.sent.scopes],
36968
37765
  returns: "GuestView"
36969
37766
  },
36970
37767
  {
@@ -40614,15 +41411,24 @@ var HANDLERS = {
40614
41411
  },
40615
41412
  "task cancel": async ({ args, requireClient }) => {
40616
41413
  const id = requireArg(args, 0, "task id");
40617
- return { data: await requireClient().post(`/tasks/${id}/cancel`, {}), render: renderTask };
41414
+ return {
41415
+ data: await requireClient().post(pathOf(taskOperations.cancel, { id }), {}),
41416
+ render: renderTask
41417
+ };
40618
41418
  },
40619
41419
  "tasks accept": async ({ args, requireClient }) => {
40620
41420
  const id = requireArg(args, 0, "task id");
40621
- return { data: await requireClient().post(`/tasks/${id}/accept`, {}), render: renderTask };
41421
+ return {
41422
+ data: await requireClient().post(pathOf(taskOperations.accept, { id }), {}),
41423
+ render: renderTask
41424
+ };
40622
41425
  },
40623
41426
  "tasks dismiss": async ({ args, requireClient }) => {
40624
41427
  const id = requireArg(args, 0, "task id");
40625
- return { data: await requireClient().post(`/tasks/${id}/not-now`, {}), render: renderTask };
41428
+ return {
41429
+ data: await requireClient().post(pathOf(taskOperations.notNow, { id }), {}),
41430
+ render: renderTask
41431
+ };
40626
41432
  },
40627
41433
  "block-templates week": async ({ flags, requireClient }) => {
40628
41434
  const start = flagString(flags, "start");
@@ -40792,12 +41598,18 @@ var HANDLERS = {
40792
41598
  const id = requireArg(args, 0, "task id");
40793
41599
  const note = flagString(flags, "note");
40794
41600
  const body = parseInput(markTaskReadyInputSchema, note !== undefined ? { note } : {});
40795
- return { data: await requireClient().post(`/tasks/${id}/ready`, body), render: renderTask };
41601
+ return {
41602
+ data: await requireClient().post(pathOf(taskOperations.ready, { id }), body),
41603
+ render: renderTask
41604
+ };
40796
41605
  },
40797
41606
  "task reopen": async ({ args, requireClient }) => {
40798
41607
  const id = requireArg(args, 0, "task id");
40799
41608
  const body = parseInput(updateTaskInputSchema, { status: "todo" });
40800
- return { data: await requireClient().patch(`/tasks/${id}`, body), render: renderTask };
41609
+ return {
41610
+ data: await requireClient().patch(pathOf(taskOperations.update, { id }), body),
41611
+ render: renderTask
41612
+ };
40801
41613
  },
40802
41614
  "task snooze": async ({ args, flags, io, requireClient }) => {
40803
41615
  const id = requireArg(args, 0, "task id");
@@ -40809,7 +41621,10 @@ var HANDLERS = {
40809
41621
  ...until ? { allDay: until.allDay } : {},
40810
41622
  ...duration ? { durationMinutes: duration } : {}
40811
41623
  });
40812
- return { data: await requireClient().patch(`/tasks/${id}`, body), render: renderTask };
41624
+ return {
41625
+ data: await requireClient().patch(pathOf(taskOperations.update, { id }), body),
41626
+ render: renderTask
41627
+ };
40813
41628
  },
40814
41629
  "task schedule": async ({ args, flags, requireClient }) => {
40815
41630
  const id = requireArg(args, 0, "task id");
@@ -40822,7 +41637,10 @@ var HANDLERS = {
40822
41637
  allDay: at.allDay,
40823
41638
  ...duration ? { durationMinutes: duration } : {}
40824
41639
  });
40825
- return { data: await requireClient().patch(`/tasks/${id}`, body), render: renderTask };
41640
+ return {
41641
+ data: await requireClient().patch(pathOf(taskOperations.update, { id }), body),
41642
+ render: renderTask
41643
+ };
40826
41644
  },
40827
41645
  "habit log": async ({ args, flags, requireClient }) => {
40828
41646
  const id = requireArg(args, 0, "habit id");
@@ -40839,7 +41657,7 @@ var HANDLERS = {
40839
41657
  ...note !== undefined ? { note } : {}
40840
41658
  });
40841
41659
  return {
40842
- data: await requireClient().post(`/habits/${id}/check-ins`, input),
41660
+ data: await requireClient().post(pathOf(habitOperations.checkIn, { id }), input),
40843
41661
  render: renderHabit
40844
41662
  };
40845
41663
  },
@@ -40863,7 +41681,10 @@ var HANDLERS = {
40863
41681
  ...at ? { occurredAt: at } : {},
40864
41682
  ...note !== undefined ? { note } : {}
40865
41683
  });
40866
- return { data: await requireClient().post("/habits/check-in", input), render: renderHabit };
41684
+ return {
41685
+ data: await requireClient().post(pathOf(habitOperations.checkInByTitle), input),
41686
+ render: renderHabit
41687
+ };
40867
41688
  },
40868
41689
  "habit alias add": async ({ args, requireClient }) => {
40869
41690
  const id = requireArg(args, 0, "habit id");
@@ -40872,7 +41693,7 @@ var HANDLERS = {
40872
41693
  throw usageError("habit alias add needs an alias, e.g. `do habit alias add <id> wake`");
40873
41694
  const input = parseInput(habitAliasInputSchema, { alias });
40874
41695
  return {
40875
- data: await requireClient().post(`/habits/${id}/aliases`, input),
41696
+ data: await requireClient().post(pathOf(habitOperations.addAlias, { id }), input),
40876
41697
  render: renderHabit
40877
41698
  };
40878
41699
  },
@@ -40882,12 +41703,12 @@ var HANDLERS = {
40882
41703
  if (!alias)
40883
41704
  throw usageError("habit alias rm needs the alias to drop");
40884
41705
  return {
40885
- data: await requireClient().delete(`/habits/${id}/aliases/${encodeURIComponent(alias)}`),
41706
+ data: await requireClient().delete(pathOf(habitOperations.removeAlias, { id, alias })),
40886
41707
  render: renderHabit
40887
41708
  };
40888
41709
  },
40889
41710
  "habits today": async ({ requireClient }) => ({
40890
- data: await requireClient().get("/habits/today"),
41711
+ data: await requireClient().get(pathOf(habitOperations.today)),
40891
41712
  render: renderHabitsToday
40892
41713
  }),
40893
41714
  "habit check-ins": async ({ args, flags, requireClient }) => {
@@ -40897,7 +41718,7 @@ var HANDLERS = {
40897
41718
  limit: flagString(flags, "limit")
40898
41719
  };
40899
41720
  return {
40900
- data: await requireClient().get(`/habits/${id}/check-ins`, { query }),
41721
+ data: await requireClient().get(pathOf(habitOperations.listCheckIns, { id }), { query }),
40901
41722
  render: renderList
40902
41723
  };
40903
41724
  },
@@ -40912,7 +41733,7 @@ var HANDLERS = {
40912
41733
  throw usageError('habit note needs the note, e.g. `do habit note <id> <check-in id> "felt heavy"`');
40913
41734
  const input = parseInput(updateHabitEntryInputSchema, { note: clear ? null : text });
40914
41735
  return {
40915
- data: await requireClient().patch(`/habits/${id}/check-ins/${entryId}`, input),
41736
+ data: await requireClient().patch(pathOf(habitOperations.updateCheckIn, { id, entryId }), input),
40916
41737
  render: renderHabitEntry
40917
41738
  };
40918
41739
  },
@@ -40924,7 +41745,10 @@ var HANDLERS = {
40924
41745
  ...date ? { date } : {},
40925
41746
  ...note !== undefined ? { note } : {}
40926
41747
  });
40927
- return { data: await requireClient().post(`/habits/${id}/skip`, input), render: renderHabit };
41748
+ return {
41749
+ data: await requireClient().post(pathOf(habitOperations.skip, { id }), input),
41750
+ render: renderHabit
41751
+ };
40928
41752
  },
40929
41753
  "habit pause": async ({ args, flags, requireClient }) => {
40930
41754
  const id = requireArg(args, 0, "habit id");
@@ -40932,7 +41756,10 @@ var HANDLERS = {
40932
41756
  if (!until)
40933
41757
  throw usageError("habit pause needs --until <YYYY-MM-DD>, the day it's back");
40934
41758
  const input = parseInput(habitPauseInputSchema, { until });
40935
- return { data: await requireClient().post(`/habits/${id}/pause`, input), render: renderHabit };
41759
+ return {
41760
+ data: await requireClient().post(pathOf(habitOperations.pause, { id }), input),
41761
+ render: renderHabit
41762
+ };
40936
41763
  },
40937
41764
  "habit remind": async ({ args, requireClient }) => {
40938
41765
  const id = requireArg(args, 0, "habit id");
@@ -40940,19 +41767,31 @@ var HANDLERS = {
40940
41767
  if (state !== "on" && state !== "off")
40941
41768
  throw usageError("habit remind takes `on` or `off`");
40942
41769
  const input = parseInput(updateHabitInputSchema, { remind: state === "on" });
40943
- return { data: await requireClient().patch(`/habits/${id}`, input), render: renderHabit };
41770
+ return {
41771
+ data: await requireClient().patch(pathOf(habitOperations.update, { id }), input),
41772
+ render: renderHabit
41773
+ };
40944
41774
  },
40945
41775
  "habit resume": async ({ args, requireClient }) => {
40946
41776
  const id = requireArg(args, 0, "habit id");
40947
- return { data: await requireClient().post(`/habits/${id}/resume`, {}), render: renderHabit };
41777
+ return {
41778
+ data: await requireClient().post(pathOf(habitOperations.resume, { id }), {}),
41779
+ render: renderHabit
41780
+ };
40948
41781
  },
40949
41782
  "habit retire": async ({ args, requireClient }) => {
40950
41783
  const id = requireArg(args, 0, "habit id");
40951
- return { data: await requireClient().post(`/habits/${id}/retire`, {}), render: renderHabit };
41784
+ return {
41785
+ data: await requireClient().post(pathOf(habitOperations.retire, { id }), {}),
41786
+ render: renderHabit
41787
+ };
40952
41788
  },
40953
41789
  "habit unretire": async ({ args, requireClient }) => {
40954
41790
  const id = requireArg(args, 0, "habit id");
40955
- return { data: await requireClient().post(`/habits/${id}/unretire`, {}), render: renderHabit };
41791
+ return {
41792
+ data: await requireClient().post(pathOf(habitOperations.unretire, { id }), {}),
41793
+ render: renderHabit
41794
+ };
40956
41795
  },
40957
41796
  away: async ({ args, flags, requireClient }) => {
40958
41797
  const until = flagString(flags, "until");
@@ -40971,7 +41810,7 @@ var HANDLERS = {
40971
41810
  const id = requireArg(args, 0, "routine id");
40972
41811
  const input = parseInput(routineDoneInputSchema, {});
40973
41812
  return {
40974
- data: await requireClient().post(`/routines/${id}/done`, input),
41813
+ data: await requireClient().post(pathOf(routineOperations.markDone, { id }), input),
40975
41814
  render: renderRoutineDone
40976
41815
  };
40977
41816
  },
@@ -40983,7 +41822,7 @@ var HANDLERS = {
40983
41822
  }
40984
41823
  const input = parseInput(routineRunUndoInputSchema, { stepRunIds });
40985
41824
  return {
40986
- data: await requireClient().post(`/routines/runs/${runId}/undo`, input),
41825
+ data: await requireClient().post(pathOf(routineOperations.undoSteps, { runId }), input),
40987
41826
  render: renderRoutineUndo
40988
41827
  };
40989
41828
  },
@@ -42157,31 +42996,31 @@ var HANDLERS = {
42157
42996
  return { data: await requireClient().post("/runs/steam/sync", body), render: renderRecord };
42158
42997
  },
42159
42998
  "sensors status": async ({ requireClient }) => ({
42160
- data: await requireClient().get("/sensors/home-assistant"),
42999
+ data: await requireClient().get(pathOf(sensorOperations.status)),
42161
43000
  render: renderRecord
42162
43001
  }),
42163
43002
  "sensors entities": async ({ requireClient }) => ({
42164
- data: await requireClient().get("/sensors/home-assistant/entities"),
43003
+ data: await requireClient().get(pathOf(sensorOperations.entities)),
42165
43004
  render: renderList
42166
43005
  }),
42167
43006
  "sensors sync": async ({ requireClient }) => ({
42168
- data: await requireClient().post("/sensors/home-assistant/sync", {}),
43007
+ data: await requireClient().post(pathOf(sensorOperations.sync), {}),
42169
43008
  render: renderRecord
42170
43009
  }),
42171
43010
  "sensors unifi": async ({ requireClient }) => ({
42172
- data: await requireClient().get("/sensors/unifi"),
43011
+ data: await requireClient().get(pathOf(sensorOperations.unifiStatus)),
42173
43012
  render: renderRecord
42174
43013
  }),
42175
43014
  "sensors unifi-devices": async ({ requireClient }) => ({
42176
- data: await requireClient().get("/sensors/unifi/devices"),
43015
+ data: await requireClient().get(pathOf(sensorOperations.unifiDevices)),
42177
43016
  render: renderList
42178
43017
  }),
42179
43018
  "sensors unifi-sync": async ({ requireClient }) => ({
42180
- data: await requireClient().post("/sensors/unifi/sync", {}),
43019
+ data: await requireClient().post(pathOf(sensorOperations.syncUnifi), {}),
42181
43020
  render: renderRecord
42182
43021
  }),
42183
43022
  "sensors links": async ({ flags, requireClient }) => ({
42184
- data: await requireClient().get("/sensors/links", {
43023
+ data: await requireClient().get(pathOf(sensorOperations.links), {
42185
43024
  query: { nodeId: flagString(flags, "node") }
42186
43025
  }),
42187
43026
  render: renderList
@@ -42194,11 +43033,14 @@ var HANDLERS = {
42194
43033
  ...flagString(flags, "provider") ? { provider: flagString(flags, "provider") } : {},
42195
43034
  ...flagString(flags, "label") ? { label: flagString(flags, "label") } : {}
42196
43035
  });
42197
- return { data: await requireClient().post("/sensors/links", body), render: renderRecord };
43036
+ return {
43037
+ data: await requireClient().post(pathOf(sensorOperations.createLink), body),
43038
+ render: renderRecord
43039
+ };
42198
43040
  },
42199
43041
  "sensors unmap": async ({ args, requireClient }) => {
42200
43042
  const id = requireArg(args, 0, "sensor link id");
42201
- await requireClient().delete(`/sensors/links/${id}`);
43043
+ await requireClient().delete(pathOf(sensorOperations.removeLink, { id }));
42202
43044
  return { data: { ok: true, unmapped: id }, render: renderRecord };
42203
43045
  },
42204
43046
  "runs sessions list": async ({ args, requireClient }) => {
@@ -42701,7 +43543,10 @@ var HANDLERS = {
42701
43543
  },
42702
43544
  "guests sent": async ({ flags, requireClient }) => {
42703
43545
  const body = parseInput(markGuestsSentInputSchema, readDataFlag(flags));
42704
- return { data: await requireClient().post("/guests/sent", body), render: renderRecord };
43546
+ return {
43547
+ data: await requireClient().post(pathOf(guestOperations.sent), body),
43548
+ render: renderRecord
43549
+ };
42705
43550
  },
42706
43551
  "rsvp-questions presets": async ({ flags, requireClient }) => {
42707
43552
  const body = parseInput(addRsvpPresetsInputSchema, readDataFlag(flags));