@robbeverhelst/do 1.300.0 → 1.302.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 +466 -230
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -4438,6 +4438,20 @@ var documents = pgTable("documents", {
4438
4438
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
4439
4439
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
4440
4440
  }, (t) => [index("documents_tenant_kind_idx").on(t.tenantId, t.kind)]);
4441
+ var paperShares = pgTable("paper_shares", {
4442
+ id: text("id").primaryKey(),
4443
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
4444
+ userId: text("user_id").notNull().references(() => users.id),
4445
+ label: text("label").notNull(),
4446
+ nodeIds: text("node_ids").array().notNull().default(sql`'{}'::text[]`),
4447
+ snapshot: jsonb("snapshot").notNull(),
4448
+ tokenHash: text("token_hash").notNull().unique(),
4449
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
4450
+ revokedAt: timestamp("revoked_at", { withTimezone: true }),
4451
+ opens: integer("opens").notNull().default(0),
4452
+ lastOpenedAt: timestamp("last_opened_at", { withTimezone: true }),
4453
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
4454
+ }, (t) => [index("paper_shares_user_idx").on(t.tenantId, t.userId, t.createdAt)]);
4441
4455
 
4442
4456
  // ../../packages/schema/src/db/incidents.ts
4443
4457
  var incidentKind = pgEnum("incident_kind", [
@@ -5222,6 +5236,7 @@ var rsvpLinks = pgTable("rsvp_links", {
5222
5236
  tokenHash: text("token_hash").notNull().unique(),
5223
5237
  mode: rsvpLinkMode("mode").notNull().default("full"),
5224
5238
  tokenSealed: text("token_sealed"),
5239
+ shortCodeHash: text("short_code_hash").unique(),
5225
5240
  expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
5226
5241
  revokedAt: timestamp("revoked_at", { withTimezone: true }),
5227
5242
  lastUsedAt: timestamp("last_used_at", { withTimezone: true }),
@@ -6038,6 +6053,12 @@ var healthLabAnalytes = pgTable("health_lab_analytes", {
6038
6053
  note: text("note"),
6039
6054
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6040
6055
  }, (t) => [primaryKey({ columns: [t.tenantId, t.userId, t.analyte] })]);
6056
+ var healthDataKeys = pgTable("health_data_keys", {
6057
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
6058
+ userId: text("user_id").notNull().references(() => users.id),
6059
+ sealedKey: text("sealed_key").notNull(),
6060
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6061
+ }, (t) => [primaryKey({ columns: [t.tenantId, t.userId] })]);
6041
6062
 
6042
6063
  // ../../packages/schema/src/db/journal.ts
6043
6064
  var journalDayStatus = pgEnum("journal_day_status", ["open", "summarised", "reviewed"]);
@@ -22455,6 +22476,28 @@ var houseUpkeepYearSchema = object({
22455
22476
  });
22456
22477
  var houseUpkeepYearQuerySchema = object({ year: exports_coerce.number().int().min(2000).max(2100).optional() }).strict();
22457
22478
 
22479
+ // ../../packages/schema/src/receipt-kinds.ts
22480
+ var MONEY_RECEIPT_NODE_TYPE = "money_transaction";
22481
+ var RECEIPT_KINDS = {
22482
+ document: { about: [] },
22483
+ health_episode: { about: ["red_flags"] },
22484
+ journal_day: { about: ["saved"] },
22485
+ time_block: { about: [] },
22486
+ meal_log: { about: [] },
22487
+ collection_item: { about: [] },
22488
+ person_note: { about: [] },
22489
+ health_encounter: { about: [] },
22490
+ animal: { about: ["vet_visit"] },
22491
+ [MONEY_RECEIPT_NODE_TYPE]: { about: ["ask", "bill"] },
22492
+ planning_rule: { about: [] }
22493
+ };
22494
+ var NODE_RECEIPT_ABOUT_VALUES = [
22495
+ ...RECEIPT_KINDS.health_episode.about,
22496
+ ...RECEIPT_KINDS.journal_day.about,
22497
+ ...RECEIPT_KINDS.animal.about
22498
+ ];
22499
+ var MONEY_RECEIPT_ABOUT_VALUES = RECEIPT_KINDS[MONEY_RECEIPT_NODE_TYPE].about;
22500
+
22458
22501
  // ../../packages/schema/src/contracts/agent-chat.ts
22459
22502
  var agentConnectionSourceSchema = _enum2(["space", "env"]);
22460
22503
  var agentGatewayStatusSchema = _enum2(["connected", "offline", "not_connected"]);
@@ -22691,7 +22734,7 @@ var postRunSchema = object({
22691
22734
  status: runStatusSchema,
22692
22735
  steps: array(runStepSchema).max(12).optional()
22693
22736
  });
22694
- var POST_RECEIPT_ABOUT_VALUES = ["red_flags", "saved", "vet_visit"];
22737
+ var POST_RECEIPT_ABOUT_VALUES = NODE_RECEIPT_ABOUT_VALUES;
22695
22738
  var POST_RECEIPT_SOURCE_MAX = 80;
22696
22739
  var postNodeReceiptInputSchema = object({
22697
22740
  nodeId: ulidSchema,
@@ -22700,7 +22743,7 @@ var postNodeReceiptInputSchema = object({
22700
22743
  source: string2().trim().min(1).max(POST_RECEIPT_SOURCE_MAX).optional(),
22701
22744
  personNotes: array(ulidSchema).min(1).max(10).optional()
22702
22745
  }).strict();
22703
- var POST_MONEY_RECEIPT_ABOUT_VALUES = ["ask", "bill"];
22746
+ var POST_MONEY_RECEIPT_ABOUT_VALUES = MONEY_RECEIPT_ABOUT_VALUES;
22704
22747
  var webUrlSchema = string2().url().max(2000).refine((url) => /^https?:\/\//i.test(url), "must be an http(s) link");
22705
22748
  var postMoneyReceiptInputSchema = object({
22706
22749
  transactionId: ulidSchema,
@@ -23178,6 +23221,14 @@ var updateHealthProfileInputSchema = object({
23178
23221
  primaryGpContactId: ulidSchema.nullish()
23179
23222
  }).strict();
23180
23223
  var grantHealthConsentInputSchema = object({ version: literal(HEALTH_CONSENT_VERSION) }).strict();
23224
+ var HEALTH_ERASE_PHRASE = "delete my health data";
23225
+ var eraseHealthDataInputSchema = object({ confirm: literal(HEALTH_ERASE_PHRASE) }).strict();
23226
+ var healthErasureSchema = object({
23227
+ records: number2().int().nonnegative(),
23228
+ files: number2().int().nonnegative(),
23229
+ keyShredded: boolean3(),
23230
+ consent: healthConsentStateSchema
23231
+ });
23181
23232
  var healthEpisodeListQuerySchema = object({
23182
23233
  status: healthEpisodeStatusSchema.optional()
23183
23234
  });
@@ -23442,7 +23493,8 @@ var FILE_SOURCES = [
23442
23493
  "doccle",
23443
23494
  "doccle_zip",
23444
23495
  "scan",
23445
- "game_save"
23496
+ "game_save",
23497
+ "google_photos"
23446
23498
  ];
23447
23499
  var fileSourceSchema = union([
23448
23500
  _enum2(FILE_SOURCES),
@@ -23615,6 +23667,20 @@ var journalSearchSchema = object({
23615
23667
  days: array(journalDayListItemSchema).max(JOURNAL_SEARCH_LIMIT),
23616
23668
  understood: journalSearchUnderstoodSchema.nullable().default(null)
23617
23669
  });
23670
+ var JOURNAL_LIKE_LIMIT = 10;
23671
+ var journalLikeQuerySchema = object({
23672
+ limit: exports_coerce.number().int().min(1).max(JOURNAL_LIKE_LIMIT).default(5)
23673
+ });
23674
+ var journalLikeDaySchema = journalDayListItemSchema.extend({
23675
+ shared: array(string2()),
23676
+ sharedTags: array(string2())
23677
+ });
23678
+ var journalLikeSchema = object({
23679
+ date: journalDateSchema,
23680
+ timezone: string2().min(1),
23681
+ words: array(string2()),
23682
+ days: array(journalLikeDaySchema).max(JOURNAL_LIKE_LIMIT)
23683
+ });
23618
23684
  var journalLinkInputSchema = object({
23619
23685
  kind: string2().trim().regex(/^[a-z][a-z_]{0,31}$/, "a link kind is a short lowercase word"),
23620
23686
  nodeId: ulidSchema
@@ -27486,6 +27552,104 @@ var familyTreeMatchDecisionResultSchema = object({
27486
27552
  matches: array(familyTreeMatchViewSchema)
27487
27553
  });
27488
27554
 
27555
+ // ../../packages/schema/src/contracts/rsvp-questions.ts
27556
+ var RSVP_QUESTION_KINDS = ["choice", "multi", "text", "number", "yes_no"];
27557
+ var rsvpQuestionKindSchema = _enum2(RSVP_QUESTION_KINDS);
27558
+ var rsvpQuestionScopeSchema = _enum2(["household", "guest"]);
27559
+ var RSVP_QUESTION_PRESETS = ["diet", "diet_other", "message"];
27560
+ var rsvpQuestionPresetSchema = _enum2(RSVP_QUESTION_PRESETS);
27561
+ var RSVP_SHOW_IF_MAX = 5;
27562
+ var rsvpTextSchema = (max) => object({
27563
+ nl: string2().trim().min(1).max(max).optional(),
27564
+ pt: string2().trim().min(1).max(max).optional()
27565
+ }).strict().refine((t) => t.nl !== undefined || t.pt !== undefined, "give it in NL or PT");
27566
+ var keySchema = string2().regex(/^[a-z][a-z0-9_]{0,31}$/, "expected a short lower-case word (vegan, civil)");
27567
+ var rsvpQuestionOptionSchema = object({ key: keySchema, label: rsvpTextSchema(120) }).strict();
27568
+ var rsvpShowIfConditionSchema = union([
27569
+ object({
27570
+ questionId: ulidSchema,
27571
+ equals: union([string2().max(200), number2(), boolean3()])
27572
+ }).strict(),
27573
+ object({ eventKey: keySchema.optional(), rsvp: literal("yes") }).strict()
27574
+ ]);
27575
+ var rsvpAnswerValueSchema = union([
27576
+ string2().max(2000),
27577
+ array(string2().max(32)).max(20),
27578
+ number2(),
27579
+ boolean3()
27580
+ ]);
27581
+ var rsvpQuestionSchema = createSelectSchema(rsvpQuestions, {
27582
+ label: object({ nl: string2().optional(), pt: string2().optional() }),
27583
+ options: array(object({
27584
+ key: string2(),
27585
+ label: object({ nl: string2().optional(), pt: string2().optional() })
27586
+ })),
27587
+ showIf: array(rsvpShowIfConditionSchema),
27588
+ preset: rsvpQuestionPresetSchema.nullable(),
27589
+ createdAt: exports_coerce.date(),
27590
+ updatedAt: exports_coerce.date()
27591
+ });
27592
+ var rsvpAnswerSchema = createSelectSchema(rsvpAnswers, {
27593
+ value: rsvpAnswerValueSchema,
27594
+ createdAt: exports_coerce.date(),
27595
+ updatedAt: exports_coerce.date()
27596
+ });
27597
+ function rsvpOptionsProblem(kind, options) {
27598
+ if (kind === "choice" || kind === "multi") {
27599
+ if (options.length < 2)
27600
+ return `a ${kind} question needs two options or more`;
27601
+ if (new Set(options.map((o) => o.key)).size !== options.length)
27602
+ return "each option needs its own key";
27603
+ return null;
27604
+ }
27605
+ return options.length > 0 ? `a ${kind} question has no options` : null;
27606
+ }
27607
+ var questionFields = {
27608
+ label: rsvpTextSchema(300),
27609
+ options: array(rsvpQuestionOptionSchema).max(30).optional(),
27610
+ scope: rsvpQuestionScopeSchema.optional(),
27611
+ eventKey: keySchema.nullish(),
27612
+ required: boolean3().optional(),
27613
+ sort: number2().int().min(0).max(1e4).optional(),
27614
+ showIf: array(rsvpShowIfConditionSchema).max(RSVP_SHOW_IF_MAX).optional()
27615
+ };
27616
+ var createRsvpQuestionInputSchema = object({
27617
+ id: ulidSchema,
27618
+ guestListId: ulidSchema,
27619
+ kind: rsvpQuestionKindSchema,
27620
+ ...questionFields
27621
+ }).strict().superRefine((q, ctx) => {
27622
+ const problem = rsvpOptionsProblem(q.kind, q.options ?? []);
27623
+ if (problem)
27624
+ ctx.addIssue({ code: "custom", message: problem, path: ["options"] });
27625
+ if (q.showIf?.some((c) => ("questionId" in c) && c.questionId === q.id))
27626
+ ctx.addIssue({
27627
+ code: "custom",
27628
+ message: "a question can't depend on itself",
27629
+ path: ["showIf"]
27630
+ });
27631
+ });
27632
+ var updateRsvpQuestionInputSchema = object(questionFields).partial().strict();
27633
+ var rsvpQuestionsQuerySchema = object({ guestListId: ulidSchema });
27634
+ var rsvpAnswerInputSchema = object({
27635
+ questionId: ulidSchema,
27636
+ guestId: ulidSchema.nullish(),
27637
+ value: rsvpAnswerValueSchema.nullable()
27638
+ }).strict();
27639
+ var setRsvpAnswersInputSchema = object({
27640
+ guestListId: ulidSchema,
27641
+ householdId: ulidSchema,
27642
+ answers: array(rsvpAnswerInputSchema).min(1).max(200)
27643
+ }).strict();
27644
+ var rsvpAnswersQuerySchema = object({
27645
+ guestListId: ulidSchema,
27646
+ householdId: ulidSchema.optional()
27647
+ });
27648
+ var addRsvpPresetsInputSchema = object({
27649
+ guestListId: ulidSchema,
27650
+ presets: array(_enum2(["diet", "message"])).min(1).max(2)
27651
+ }).strict();
27652
+
27489
27653
  // ../../packages/schema/src/contracts/guests.ts
27490
27654
  var guestAnswerSchema = _enum2(["yes", "no", "maybe"]);
27491
27655
  var guestStateSchema = _enum2(["yes", "no", "maybe", "waiting"]);
@@ -27641,6 +27805,11 @@ var guestListOverviewSchema = object({
27641
27805
  })
27642
27806
  });
27643
27807
  var RSVP_TOKEN_RE = /^rsvp_[A-Za-z0-9_-]{43}$/;
27808
+ var RSVP_CODE_ALPHABET = "23456789ABCDEFGHJKMNPQRSTWXYZ";
27809
+ var RSVP_CODE_LENGTH = 6;
27810
+ var RSVP_CODE_RE = new RegExp(`^[${RSVP_CODE_ALPHABET}]{${RSVP_CODE_LENGTH}}$`);
27811
+ var resolveRsvpCodeInputSchema = object({ code: string2().max(32) }).strict();
27812
+ var resolvedRsvpCodeSchema = object({ token: string2().regex(RSVP_TOKEN_RE) });
27644
27813
  var RSVP_LINK_MODES = ["address", "full"];
27645
27814
  var rsvpLinkModeSchema = _enum2(RSVP_LINK_MODES);
27646
27815
  var rsvpLinkSchema = object({
@@ -27658,7 +27827,8 @@ var rsvpLinkSchema = object({
27658
27827
  });
27659
27828
  var mintedRsvpLinkSchema = rsvpLinkSchema.extend({
27660
27829
  token: string2().regex(RSVP_TOKEN_RE),
27661
- path: string2()
27830
+ path: string2(),
27831
+ shortCode: string2().regex(RSVP_CODE_RE).nullable()
27662
27832
  });
27663
27833
  var createRsvpLinksInputSchema = object({
27664
27834
  guestListId: ulidSchema,
@@ -27711,10 +27881,10 @@ var submitRsvpInputSchema = object({
27711
27881
  email: string2().trim().email().max(254).nullish()
27712
27882
  }).strict()).max(30).default([]),
27713
27883
  address: householdAddressSchema.nullish(),
27714
- language: rsvpLanguageSchema.optional()
27715
- }).strict().refine((i) => i.guests.length > 0 || !!i.address || !!i.language, {
27716
- message: "nothing to send: answers, an address or a language"
27717
- });
27884
+ language: rsvpLanguageSchema.optional(),
27885
+ answers: array(rsvpAnswerInputSchema).max(200).optional(),
27886
+ dietConsent: boolean3().optional()
27887
+ }).strict().refine((i) => i.guests.length > 0 || (i.answers?.length ?? 0) > 0 || !!i.address || !!i.language, { message: "nothing to send: answers, an address or a language" });
27718
27888
  var eventTextSchema = (max) => object({
27719
27889
  nl: string2().trim().min(1).max(max).optional(),
27720
27890
  pt: string2().trim().min(1).max(max).optional()
@@ -27983,104 +28153,6 @@ var mailCampaignViewSchema = mailCampaignSchema.extend({
27983
28153
  readyToSend: boolean3()
27984
28154
  });
27985
28155
 
27986
- // ../../packages/schema/src/contracts/rsvp-questions.ts
27987
- var RSVP_QUESTION_KINDS = ["choice", "multi", "text", "number", "yes_no"];
27988
- var rsvpQuestionKindSchema = _enum2(RSVP_QUESTION_KINDS);
27989
- var rsvpQuestionScopeSchema = _enum2(["household", "guest"]);
27990
- var RSVP_QUESTION_PRESETS = ["diet", "diet_other", "message"];
27991
- var rsvpQuestionPresetSchema = _enum2(RSVP_QUESTION_PRESETS);
27992
- var RSVP_SHOW_IF_MAX = 5;
27993
- var rsvpTextSchema = (max) => object({
27994
- nl: string2().trim().min(1).max(max).optional(),
27995
- pt: string2().trim().min(1).max(max).optional()
27996
- }).strict().refine((t) => t.nl !== undefined || t.pt !== undefined, "give it in NL or PT");
27997
- var keySchema = string2().regex(/^[a-z][a-z0-9_]{0,31}$/, "expected a short lower-case word (vegan, civil)");
27998
- var rsvpQuestionOptionSchema = object({ key: keySchema, label: rsvpTextSchema(120) }).strict();
27999
- var rsvpShowIfConditionSchema = union([
28000
- object({
28001
- questionId: ulidSchema,
28002
- equals: union([string2().max(200), number2(), boolean3()])
28003
- }).strict(),
28004
- object({ eventKey: keySchema.optional(), rsvp: literal("yes") }).strict()
28005
- ]);
28006
- var rsvpAnswerValueSchema = union([
28007
- string2().max(2000),
28008
- array(string2().max(32)).max(20),
28009
- number2(),
28010
- boolean3()
28011
- ]);
28012
- var rsvpQuestionSchema = createSelectSchema(rsvpQuestions, {
28013
- label: object({ nl: string2().optional(), pt: string2().optional() }),
28014
- options: array(object({
28015
- key: string2(),
28016
- label: object({ nl: string2().optional(), pt: string2().optional() })
28017
- })),
28018
- showIf: array(rsvpShowIfConditionSchema),
28019
- preset: rsvpQuestionPresetSchema.nullable(),
28020
- createdAt: exports_coerce.date(),
28021
- updatedAt: exports_coerce.date()
28022
- });
28023
- var rsvpAnswerSchema = createSelectSchema(rsvpAnswers, {
28024
- value: rsvpAnswerValueSchema,
28025
- createdAt: exports_coerce.date(),
28026
- updatedAt: exports_coerce.date()
28027
- });
28028
- function rsvpOptionsProblem(kind, options) {
28029
- if (kind === "choice" || kind === "multi") {
28030
- if (options.length < 2)
28031
- return `a ${kind} question needs two options or more`;
28032
- if (new Set(options.map((o) => o.key)).size !== options.length)
28033
- return "each option needs its own key";
28034
- return null;
28035
- }
28036
- return options.length > 0 ? `a ${kind} question has no options` : null;
28037
- }
28038
- var questionFields = {
28039
- label: rsvpTextSchema(300),
28040
- options: array(rsvpQuestionOptionSchema).max(30).optional(),
28041
- scope: rsvpQuestionScopeSchema.optional(),
28042
- eventKey: keySchema.nullish(),
28043
- required: boolean3().optional(),
28044
- sort: number2().int().min(0).max(1e4).optional(),
28045
- showIf: array(rsvpShowIfConditionSchema).max(RSVP_SHOW_IF_MAX).optional()
28046
- };
28047
- var createRsvpQuestionInputSchema = object({
28048
- id: ulidSchema,
28049
- guestListId: ulidSchema,
28050
- kind: rsvpQuestionKindSchema,
28051
- ...questionFields
28052
- }).strict().superRefine((q, ctx) => {
28053
- const problem = rsvpOptionsProblem(q.kind, q.options ?? []);
28054
- if (problem)
28055
- ctx.addIssue({ code: "custom", message: problem, path: ["options"] });
28056
- if (q.showIf?.some((c) => ("questionId" in c) && c.questionId === q.id))
28057
- ctx.addIssue({
28058
- code: "custom",
28059
- message: "a question can't depend on itself",
28060
- path: ["showIf"]
28061
- });
28062
- });
28063
- var updateRsvpQuestionInputSchema = object(questionFields).partial().strict();
28064
- var rsvpQuestionsQuerySchema = object({ guestListId: ulidSchema });
28065
- var rsvpAnswerInputSchema = object({
28066
- questionId: ulidSchema,
28067
- guestId: ulidSchema.nullish(),
28068
- value: rsvpAnswerValueSchema.nullable()
28069
- }).strict();
28070
- var setRsvpAnswersInputSchema = object({
28071
- guestListId: ulidSchema,
28072
- householdId: ulidSchema,
28073
- answers: array(rsvpAnswerInputSchema).min(1).max(200)
28074
- }).strict();
28075
- var rsvpAnswersQuerySchema = object({
28076
- guestListId: ulidSchema,
28077
- householdId: ulidSchema.optional()
28078
- });
28079
- var addRsvpPresetsInputSchema = object({
28080
- guestListId: ulidSchema,
28081
- presets: array(_enum2(["diet", "message"])).min(1).max(2)
28082
- }).strict();
28083
-
28084
28156
  // ../../packages/schema/src/contracts/google-contacts.ts
28085
28157
  var googleContactsModeSchema = _enum2(["off", "fake", "live"]);
28086
28158
  var googleContactsStatusSchema = object({
@@ -29514,6 +29586,102 @@ function toCollectionTypeSummary(config) {
29514
29586
  }
29515
29587
  var COLLECTION_TYPE_SUMMARIES = COLLECTION_TYPE_LIST.map(toCollectionTypeSummary);
29516
29588
 
29589
+ // ../../packages/schema/src/operations/define.ts
29590
+ function defineOperations(table) {
29591
+ return table;
29592
+ }
29593
+ var PUBLIC_API_PREFIX = "/api/v1";
29594
+ function pathOf(op, params = {}) {
29595
+ return op.path.replace(/\{([A-Za-z0-9_]+)\}/g, (_, name) => {
29596
+ const value = params[name];
29597
+ if (value === undefined)
29598
+ throw new Error(`pathOf: no value for {${name}} in ${op.path}`);
29599
+ return encodeURIComponent(value);
29600
+ });
29601
+ }
29602
+ function routeOf(op) {
29603
+ return `${op.method.toUpperCase()} ${PUBLIC_API_PREFIX}${op.path}`;
29604
+ }
29605
+
29606
+ // ../../packages/schema/src/operations/tap-tags.ts
29607
+ var READ = ["habits:read", "routines:read"];
29608
+ var WRITE = ["habits:write", "routines:write"];
29609
+ var pathParams = { id: "The tag's id: the ULID the sticker's link carries (`/t/{id}`)." };
29610
+ var tapTagOperations = defineOperations({
29611
+ list: {
29612
+ method: "get",
29613
+ path: "/tap-tags",
29614
+ scopes: READ,
29615
+ tag: "Habits",
29616
+ summary: "List tap tags",
29617
+ description: "The Space's NFC stickers: where each is (`label`), what a tap does (`habit`: check it in; `routine`: mark today's run done), its target's title and its link's `path`.",
29618
+ status: 200,
29619
+ response: { description: "Every tag.", list: true },
29620
+ errors: { 403: true }
29621
+ },
29622
+ create: {
29623
+ method: "post",
29624
+ path: "/tap-tags",
29625
+ scopes: WRITE,
29626
+ tag: "Habits",
29627
+ summary: "Make a tap tag",
29628
+ description: "Bind a new sticker id (a client-minted ULID) to a live habit or routine of the Space, of the tag's kind. Write `https://<your do>/t/{id}` onto an NFC sticker.",
29629
+ body: createTapTagInputSchema,
29630
+ status: 201,
29631
+ response: { description: "The tag." },
29632
+ errors: { 400: true, 403: true }
29633
+ },
29634
+ get: {
29635
+ method: "get",
29636
+ path: "/tap-tags/{id}",
29637
+ pathParams,
29638
+ scopes: READ,
29639
+ tag: "Habits",
29640
+ summary: "Get a tap tag",
29641
+ description: "One tag.",
29642
+ status: 200,
29643
+ response: { description: "The tag." },
29644
+ errors: { 404: true }
29645
+ },
29646
+ update: {
29647
+ method: "patch",
29648
+ path: "/tap-tags/{id}",
29649
+ pathParams,
29650
+ scopes: WRITE,
29651
+ tag: "Habits",
29652
+ summary: "Rename or rebind a tap tag",
29653
+ description: "Change its `label`, or bind it to another habit or routine (`kind` and `targetId` together): the sticker stays as it is.",
29654
+ body: updateTapTagInputSchema,
29655
+ status: 200,
29656
+ response: { description: "The tag." },
29657
+ errors: { 400: true, 404: true }
29658
+ },
29659
+ remove: {
29660
+ method: "delete",
29661
+ path: "/tap-tags/{id}",
29662
+ pathParams,
29663
+ scopes: WRITE,
29664
+ tag: "Habits",
29665
+ summary: "Delete a tap tag",
29666
+ description: "The sticker's link stops doing anything.",
29667
+ status: 204,
29668
+ response: { description: "Deleted." },
29669
+ errors: { 404: true }
29670
+ },
29671
+ tap: {
29672
+ method: "post",
29673
+ path: "/tap-tags/{id}/tap",
29674
+ pathParams,
29675
+ scopes: WRITE,
29676
+ tag: "Habits",
29677
+ summary: "Tap a tap tag",
29678
+ description: "What the sticker does, as the key's member: check the habit in, or take today's run of the routine. Returns the receipt (`line`) and its `undo` (the check-in's `habitId` + `entryId`, or the run's `runId` + `stepRunIds` for `POST /routines/runs/{runId}/undo`). The same member's second tap within 30 s does nothing (`outcome: \"already\"`). The key must act for a member.",
29679
+ status: 200,
29680
+ response: { description: "The tap's receipt." },
29681
+ errors: { 403: "A scope is missing, or the key acts for no member.", 404: true }
29682
+ }
29683
+ });
29684
+
29517
29685
  // ../../node_modules/ulidx/dist/node/index.js
29518
29686
  import crypto from "crypto";
29519
29687
 
@@ -29766,7 +29934,7 @@ function parseCapture2(input, now) {
29766
29934
 
29767
29935
  // src/meta.ts
29768
29936
  var CLI_NAME = "do";
29769
- var VERSION = "1.300.0";
29937
+ var VERSION = "1.302.0";
29770
29938
  var API_PREFIX = "/api/v1";
29771
29939
  var DEFAULT_API_URL = "http://localhost:3001";
29772
29940
 
@@ -29995,7 +30163,7 @@ function resolveConfig(io, flags) {
29995
30163
  }
29996
30164
 
29997
30165
  // src/contacts-commands.ts
29998
- var WRITE = ["contacts:write"];
30166
+ var WRITE2 = ["contacts:write"];
29999
30167
  var FIELD_FLAGS = [
30000
30168
  { name: "name", type: "string", description: "The display name (or pass it as the argument)." },
30001
30169
  { name: "kind", type: "string", description: "person (the default) or organization." },
@@ -30038,7 +30206,7 @@ var CONTACT_COMMAND_SPECS = [
30038
30206
  summary: "Add a contact: a person or an organisation, with their details.",
30039
30207
  args: [{ name: "name", required: false, description: "The display name." }],
30040
30208
  flags: FIELD_FLAGS,
30041
- scopes: WRITE,
30209
+ scopes: WRITE2,
30042
30210
  returns: "ContactView",
30043
30211
  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>`.'
30044
30212
  },
@@ -30047,7 +30215,7 @@ var CONTACT_COMMAND_SPECS = [
30047
30215
  summary: "Change a contact's details.",
30048
30216
  args: [{ name: "id", required: true, description: "The contact's id." }],
30049
30217
  flags: FIELD_FLAGS,
30050
- scopes: WRITE,
30218
+ scopes: WRITE2,
30051
30219
  returns: "ContactView",
30052
30220
  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)."
30053
30221
  }
@@ -30152,11 +30320,43 @@ var DUE_BEFORE_FLAG = {
30152
30320
  description: "only rows due strictly before this instant (ISO 8601 or YYYY-MM-DD)",
30153
30321
  query: "dueBefore"
30154
30322
  };
30323
+ function entityFromOperations(ops, spec) {
30324
+ const path = ops.list.path;
30325
+ const sameScopes = (a, b) => [...a.scopes].sort().join() === [...b.scopes].sort().join();
30326
+ const reads = [ops.list, ops.get].filter((op) => op !== undefined);
30327
+ const writes = [ops.create, ops.update, ops.remove].filter((op) => op !== undefined);
30328
+ const problems = [
30329
+ ...[ops.create].filter((op) => op && (op.method !== "post" || op.path !== path)),
30330
+ ...[ops.get, ops.update, ops.remove].filter((op) => op && op.path !== `${path}/{id}`),
30331
+ ...reads.filter((op) => !sameScopes(op, ops.list)),
30332
+ ...writes.filter((op) => writes[0] && !sameScopes(op, writes[0]))
30333
+ ];
30334
+ if (problems.length > 0) {
30335
+ throw new Error(`entityFromOperations(${spec.name}): ${problems.map((op) => op?.summary).join("; ")} don't fit the entity verbs`);
30336
+ }
30337
+ const [firstWrite] = writes;
30338
+ return {
30339
+ name: spec.name,
30340
+ path,
30341
+ readScopes: [...ops.list.scopes],
30342
+ returns: spec.returns,
30343
+ canGet: ops.get !== undefined,
30344
+ listFlags: spec.listFlags,
30345
+ ...firstWrite ? {
30346
+ write: {
30347
+ scopes: [...firstWrite.scopes],
30348
+ ...ops.create ? { createSchema: ops.create.body, createInput: spec.createInput } : {},
30349
+ ...ops.update ? { updateSchema: ops.update.body, updateInput: spec.updateInput } : {},
30350
+ canDelete: ops.remove !== undefined
30351
+ }
30352
+ } : {}
30353
+ };
30354
+ }
30155
30355
  var ENTITIES = [
30156
30356
  {
30157
30357
  name: "tasks",
30158
30358
  path: "/tasks",
30159
- readScope: "tasks:read",
30359
+ readScopes: ["tasks:read"],
30160
30360
  returns: "Task",
30161
30361
  canGet: true,
30162
30362
  listFlags: [
@@ -30201,7 +30401,7 @@ var ENTITIES = [
30201
30401
  }
30202
30402
  ],
30203
30403
  write: {
30204
- scope: "tasks:write",
30404
+ scopes: ["tasks:write"],
30205
30405
  createSchema: createTaskInputSchema,
30206
30406
  createInput: "CreateTaskInput",
30207
30407
  updateSchema: updateTaskInputSchema,
@@ -30212,7 +30412,7 @@ var ENTITIES = [
30212
30412
  {
30213
30413
  name: "habits",
30214
30414
  path: "/habits",
30215
- readScope: "habits:read",
30415
+ readScopes: ["habits:read"],
30216
30416
  returns: "HabitSummary",
30217
30417
  canGet: true,
30218
30418
  listFlags: [
@@ -30224,7 +30424,7 @@ var ENTITIES = [
30224
30424
  }
30225
30425
  ],
30226
30426
  write: {
30227
- scope: "habits:write",
30427
+ scopes: ["habits:write"],
30228
30428
  createSchema: createHabitInputSchema,
30229
30429
  createInput: "CreateHabitInput",
30230
30430
  updateSchema: updateHabitInputSchema,
@@ -30235,7 +30435,7 @@ var ENTITIES = [
30235
30435
  {
30236
30436
  name: "projects",
30237
30437
  path: "/projects",
30238
- readScope: "projects:read",
30438
+ readScopes: ["projects:read"],
30239
30439
  returns: "ProjectView",
30240
30440
  canGet: true,
30241
30441
  listFlags: [
@@ -30248,7 +30448,7 @@ var ENTITIES = [
30248
30448
  DUE_BEFORE_FLAG
30249
30449
  ],
30250
30450
  write: {
30251
- scope: "projects:write",
30451
+ scopes: ["projects:write"],
30252
30452
  createSchema: createProjectInputSchema,
30253
30453
  createInput: "CreateProjectInput",
30254
30454
  updateSchema: updateProjectInputSchema,
@@ -30259,7 +30459,7 @@ var ENTITIES = [
30259
30459
  {
30260
30460
  name: "areas",
30261
30461
  path: "/areas",
30262
- readScope: "areas:read",
30462
+ readScopes: ["areas:read"],
30263
30463
  returns: "AreaView",
30264
30464
  canGet: true,
30265
30465
  listFlags: [
@@ -30272,7 +30472,7 @@ var ENTITIES = [
30272
30472
  { name: "archived", description: "only archived areas", query: "archived", sends: "true" }
30273
30473
  ],
30274
30474
  write: {
30275
- scope: "areas:write",
30475
+ scopes: ["areas:write"],
30276
30476
  createSchema: createAreaInputSchema,
30277
30477
  createInput: "CreateAreaInput",
30278
30478
  updateSchema: updateAreaInputSchema,
@@ -30283,12 +30483,12 @@ var ENTITIES = [
30283
30483
  {
30284
30484
  name: "places",
30285
30485
  path: "/places",
30286
- readScope: "places:read",
30486
+ readScopes: ["places:read"],
30287
30487
  returns: "PlaceView",
30288
30488
  canGet: true,
30289
30489
  listFlags: [],
30290
30490
  write: {
30291
- scope: "places:write",
30491
+ scopes: ["places:write"],
30292
30492
  createSchema: createPlaceInputSchema,
30293
30493
  createInput: "CreatePlaceInput",
30294
30494
  updateSchema: updatePlaceInputSchema,
@@ -30299,12 +30499,12 @@ var ENTITIES = [
30299
30499
  {
30300
30500
  name: "routines",
30301
30501
  path: "/routines",
30302
- readScope: "routines:read",
30502
+ readScopes: ["routines:read"],
30303
30503
  returns: "RoutineView",
30304
30504
  canGet: true,
30305
30505
  listFlags: [],
30306
30506
  write: {
30307
- scope: "routines:write",
30507
+ scopes: ["routines:write"],
30308
30508
  createSchema: createRoutineInputSchema,
30309
30509
  createInput: "CreateRoutineInput",
30310
30510
  updateSchema: updateRoutineInputSchema,
@@ -30315,7 +30515,7 @@ var ENTITIES = [
30315
30515
  {
30316
30516
  name: "notes",
30317
30517
  path: "/notes",
30318
- readScope: "notes:read",
30518
+ readScopes: ["notes:read"],
30319
30519
  returns: "Note",
30320
30520
  canGet: true,
30321
30521
  listFlags: [
@@ -30324,7 +30524,7 @@ var ENTITIES = [
30324
30524
  { name: "tag", description: "filter to notes carrying this tag", query: "tag" }
30325
30525
  ],
30326
30526
  write: {
30327
- scope: "notes:write",
30527
+ scopes: ["notes:write"],
30328
30528
  createSchema: createNoteInputSchema,
30329
30529
  createInput: "CreateNoteInput",
30330
30530
  updateSchema: updateNoteInputSchema,
@@ -30335,7 +30535,7 @@ var ENTITIES = [
30335
30535
  {
30336
30536
  name: "contacts",
30337
30537
  path: "/contacts",
30338
- readScope: "contacts:read",
30538
+ readScopes: ["contacts:read"],
30339
30539
  returns: "Contact",
30340
30540
  canGet: true,
30341
30541
  listFlags: [
@@ -30353,7 +30553,7 @@ var ENTITIES = [
30353
30553
  }
30354
30554
  ],
30355
30555
  write: {
30356
- scope: "contacts:write",
30556
+ scopes: ["contacts:write"],
30357
30557
  createSchema: createContactInputSchema,
30358
30558
  createInput: "CreateContactInput",
30359
30559
  updateSchema: updateContactInputSchema,
@@ -30364,7 +30564,7 @@ var ENTITIES = [
30364
30564
  {
30365
30565
  name: "contact-moments",
30366
30566
  path: "/contact-moments",
30367
- readScope: "contacts:read",
30567
+ readScopes: ["contacts:read"],
30368
30568
  returns: "ContactMomentView",
30369
30569
  canGet: true,
30370
30570
  listFlags: [
@@ -30375,7 +30575,7 @@ var ENTITIES = [
30375
30575
  { name: "limit", description: "at most this many, newest first", query: "limit" }
30376
30576
  ],
30377
30577
  write: {
30378
- scope: "contacts:write",
30578
+ scopes: ["contacts:write"],
30379
30579
  createSchema: createContactMomentInputSchema,
30380
30580
  createInput: "CreateContactMomentInput",
30381
30581
  updateSchema: updateContactMomentInputSchema,
@@ -30386,7 +30586,7 @@ var ENTITIES = [
30386
30586
  {
30387
30587
  name: "gift-ideas",
30388
30588
  path: "/gift-ideas",
30389
- readScope: "contacts:read",
30589
+ readScopes: ["contacts:read"],
30390
30590
  returns: "GiftIdeaView",
30391
30591
  canGet: true,
30392
30592
  listFlags: [
@@ -30403,7 +30603,7 @@ var ENTITIES = [
30403
30603
  }
30404
30604
  ],
30405
30605
  write: {
30406
- scope: "contacts:write",
30606
+ scopes: ["contacts:write"],
30407
30607
  createSchema: createGiftIdeaInputSchema,
30408
30608
  createInput: "CreateGiftIdeaInput",
30409
30609
  updateSchema: updateGiftIdeaInputSchema,
@@ -30414,12 +30614,12 @@ var ENTITIES = [
30414
30614
  {
30415
30615
  name: "circles",
30416
30616
  path: "/circles",
30417
- readScope: "contacts:read",
30617
+ readScopes: ["contacts:read"],
30418
30618
  returns: "CircleView",
30419
30619
  canGet: true,
30420
30620
  listFlags: [],
30421
30621
  write: {
30422
- scope: "contacts:write",
30622
+ scopes: ["contacts:write"],
30423
30623
  createSchema: createCircleInputSchema,
30424
30624
  createInput: "CreateCircleInput",
30425
30625
  updateSchema: updateCircleInputSchema,
@@ -30430,12 +30630,12 @@ var ENTITIES = [
30430
30630
  {
30431
30631
  name: "contact-relations",
30432
30632
  path: "/contact-relations",
30433
- readScope: "contacts:read",
30633
+ readScopes: ["contacts:read"],
30434
30634
  returns: "ContactRelation",
30435
30635
  canGet: true,
30436
30636
  listFlags: [{ name: "contact", description: "only this contact id", query: "contactId" }],
30437
30637
  write: {
30438
- scope: "contacts:write",
30638
+ scopes: ["contacts:write"],
30439
30639
  createSchema: createContactRelationInputSchema,
30440
30640
  createInput: "CreateContactRelationInput",
30441
30641
  updateSchema: updateContactRelationInputSchema,
@@ -30446,7 +30646,7 @@ var ENTITIES = [
30446
30646
  {
30447
30647
  name: "households",
30448
30648
  path: "/households",
30449
- readScope: "contacts:read",
30649
+ readScopes: ["contacts:read"],
30450
30650
  returns: "HouseholdView",
30451
30651
  canGet: true,
30452
30652
  listFlags: [
@@ -30457,7 +30657,7 @@ var ENTITIES = [
30457
30657
  }
30458
30658
  ],
30459
30659
  write: {
30460
- scope: "contacts:write",
30660
+ scopes: ["contacts:write"],
30461
30661
  createSchema: createHouseholdInputSchema,
30462
30662
  createInput: "CreateHouseholdInput",
30463
30663
  updateSchema: updateHouseholdInputSchema,
@@ -30468,12 +30668,12 @@ var ENTITIES = [
30468
30668
  {
30469
30669
  name: "guest-lists",
30470
30670
  path: "/guest-lists",
30471
- readScope: "contacts:read",
30671
+ readScopes: ["contacts:read"],
30472
30672
  returns: "GuestListView",
30473
30673
  canGet: true,
30474
30674
  listFlags: [{ name: "project", description: "only this project's list", query: "projectId" }],
30475
30675
  write: {
30476
- scope: "contacts:write",
30676
+ scopes: ["contacts:write"],
30477
30677
  createSchema: createGuestListInputSchema,
30478
30678
  createInput: "CreateGuestListInput",
30479
30679
  updateSchema: updateGuestListInputSchema,
@@ -30484,14 +30684,14 @@ var ENTITIES = [
30484
30684
  {
30485
30685
  name: "guest-list-events",
30486
30686
  path: "/guest-list-events",
30487
- readScope: "contacts:read",
30687
+ readScopes: ["contacts:read"],
30488
30688
  returns: "GuestListEvent",
30489
30689
  canGet: true,
30490
30690
  listFlags: [
30491
30691
  { name: "list", description: "the guest list's id (required)", query: "guestListId" }
30492
30692
  ],
30493
30693
  write: {
30494
- scope: "contacts:write",
30694
+ scopes: ["contacts:write"],
30495
30695
  createSchema: createGuestListEventInputSchema,
30496
30696
  createInput: "CreateGuestListEventInput",
30497
30697
  updateSchema: updateGuestListEventInputSchema,
@@ -30502,14 +30702,14 @@ var ENTITIES = [
30502
30702
  {
30503
30703
  name: "guest-tables",
30504
30704
  path: "/guest-tables",
30505
- readScope: "contacts:read",
30705
+ readScopes: ["contacts:read"],
30506
30706
  returns: "GuestTableView",
30507
30707
  canGet: true,
30508
30708
  listFlags: [
30509
30709
  { name: "list", description: "the guest list's id (required)", query: "guestListId" }
30510
30710
  ],
30511
30711
  write: {
30512
- scope: "contacts:write",
30712
+ scopes: ["contacts:write"],
30513
30713
  createSchema: createGuestTableInputSchema,
30514
30714
  createInput: "CreateGuestTableInput",
30515
30715
  updateSchema: updateGuestTableInputSchema,
@@ -30520,14 +30720,14 @@ var ENTITIES = [
30520
30720
  {
30521
30721
  name: "rsvp-questions",
30522
30722
  path: "/rsvp-questions",
30523
- readScope: "contacts:read",
30723
+ readScopes: ["contacts:read"],
30524
30724
  returns: "RsvpQuestion",
30525
30725
  canGet: true,
30526
30726
  listFlags: [
30527
30727
  { name: "list", description: "the guest list's id (required)", query: "guestListId" }
30528
30728
  ],
30529
30729
  write: {
30530
- scope: "contacts:write",
30730
+ scopes: ["contacts:write"],
30531
30731
  createSchema: createRsvpQuestionInputSchema,
30532
30732
  createInput: "CreateRsvpQuestionInput",
30533
30733
  updateSchema: updateRsvpQuestionInputSchema,
@@ -30538,7 +30738,7 @@ var ENTITIES = [
30538
30738
  {
30539
30739
  name: "guests",
30540
30740
  path: "/guests",
30541
- readScope: "contacts:read",
30741
+ readScopes: ["contacts:read"],
30542
30742
  returns: "GuestView",
30543
30743
  canGet: true,
30544
30744
  listFlags: [
@@ -30547,7 +30747,7 @@ var ENTITIES = [
30547
30747
  { name: "state", description: "yes, no, maybe or waiting (overall)", query: "state" }
30548
30748
  ],
30549
30749
  write: {
30550
- scope: "contacts:write",
30750
+ scopes: ["contacts:write"],
30551
30751
  createSchema: createGuestInputSchema,
30552
30752
  createInput: "CreateGuestInput",
30553
30753
  updateSchema: updateGuestInputSchema,
@@ -30558,7 +30758,7 @@ var ENTITIES = [
30558
30758
  {
30559
30759
  name: "key-dates",
30560
30760
  path: "/key-dates",
30561
- readScope: "key-dates:read",
30761
+ readScopes: ["key-dates:read"],
30562
30762
  returns: "KeyDateView",
30563
30763
  canGet: true,
30564
30764
  listFlags: [
@@ -30580,7 +30780,7 @@ var ENTITIES = [
30580
30780
  }
30581
30781
  ],
30582
30782
  write: {
30583
- scope: "key-dates:write",
30783
+ scopes: ["key-dates:write"],
30584
30784
  createSchema: createKeyDateInputSchema,
30585
30785
  createInput: "CreateKeyDateInput",
30586
30786
  updateSchema: updateKeyDateInputSchema,
@@ -30591,7 +30791,7 @@ var ENTITIES = [
30591
30791
  {
30592
30792
  name: "documents",
30593
30793
  path: "/documents",
30594
- readScope: "documents:read",
30794
+ readScopes: ["documents:read"],
30595
30795
  returns: "DocumentView",
30596
30796
  canGet: true,
30597
30797
  listFlags: [
@@ -30612,7 +30812,7 @@ var ENTITIES = [
30612
30812
  }
30613
30813
  ],
30614
30814
  write: {
30615
- scope: "documents:write",
30815
+ scopes: ["documents:write"],
30616
30816
  createSchema: createDocumentInputSchema,
30617
30817
  createInput: "CreateDocumentInput",
30618
30818
  updateSchema: updateDocumentInputSchema,
@@ -30623,7 +30823,7 @@ var ENTITIES = [
30623
30823
  {
30624
30824
  name: "incidents",
30625
30825
  path: "/incidents",
30626
- readScope: "documents:read",
30826
+ readScopes: ["documents:read"],
30627
30827
  returns: "IncidentView",
30628
30828
  canGet: true,
30629
30829
  listFlags: [
@@ -30635,7 +30835,7 @@ var ENTITIES = [
30635
30835
  }
30636
30836
  ],
30637
30837
  write: {
30638
- scope: "documents:write",
30838
+ scopes: ["documents:write"],
30639
30839
  createSchema: createIncidentInputSchema,
30640
30840
  createInput: "CreateIncidentInput",
30641
30841
  updateSchema: updateIncidentInputSchema,
@@ -30646,7 +30846,7 @@ var ENTITIES = [
30646
30846
  {
30647
30847
  name: "periods",
30648
30848
  path: "/periods",
30649
- readScope: "periods:read",
30849
+ readScopes: ["periods:read"],
30650
30850
  returns: "PeriodView",
30651
30851
  canGet: true,
30652
30852
  listFlags: [
@@ -30663,7 +30863,7 @@ var ENTITIES = [
30663
30863
  }
30664
30864
  ],
30665
30865
  write: {
30666
- scope: "periods:write",
30866
+ scopes: ["periods:write"],
30667
30867
  createSchema: createPeriodInputSchema,
30668
30868
  createInput: "CreatePeriodInput",
30669
30869
  updateSchema: updatePeriodInputSchema,
@@ -30671,26 +30871,17 @@ var ENTITIES = [
30671
30871
  canDelete: true
30672
30872
  }
30673
30873
  },
30674
- {
30874
+ entityFromOperations(tapTagOperations, {
30675
30875
  name: "tap-tags",
30676
- path: "/tap-tags",
30677
- readScope: "habits:read",
30678
30876
  returns: "TapTagView",
30679
- canGet: true,
30680
30877
  listFlags: [],
30681
- write: {
30682
- scope: "habits:write",
30683
- createSchema: createTapTagInputSchema,
30684
- createInput: "CreateTapTagInput",
30685
- updateSchema: updateTapTagInputSchema,
30686
- updateInput: "UpdateTapTagInput",
30687
- canDelete: true
30688
- }
30689
- },
30878
+ createInput: "CreateTapTagInput",
30879
+ updateInput: "UpdateTapTagInput"
30880
+ }),
30690
30881
  {
30691
30882
  name: "animals",
30692
30883
  path: "/animals",
30693
- readScope: "animals:read",
30884
+ readScopes: ["animals:read"],
30694
30885
  returns: "AnimalView",
30695
30886
  canGet: true,
30696
30887
  listFlags: [
@@ -30709,7 +30900,7 @@ var ENTITIES = [
30709
30900
  }
30710
30901
  ],
30711
30902
  write: {
30712
- scope: "animals:write",
30903
+ scopes: ["animals:write"],
30713
30904
  createSchema: createAnimalInputSchema,
30714
30905
  createInput: "CreateAnimalInput",
30715
30906
  updateSchema: updateAnimalInputSchema,
@@ -30720,7 +30911,7 @@ var ENTITIES = [
30720
30911
  {
30721
30912
  name: "runs",
30722
30913
  path: "/runs",
30723
- readScope: "collections:read",
30914
+ readScopes: ["collections:read"],
30724
30915
  returns: "RunView",
30725
30916
  canGet: true,
30726
30917
  listFlags: [
@@ -30733,7 +30924,7 @@ var ENTITIES = [
30733
30924
  { name: "theme", description: "only this theme pack (skyrim, pokemon, \u2026)", query: "theme" }
30734
30925
  ],
30735
30926
  write: {
30736
- scope: "collections:write",
30927
+ scopes: ["collections:write"],
30737
30928
  createSchema: createRunInputSchema,
30738
30929
  createInput: "CreateRunInput",
30739
30930
  updateSchema: updateRunInputSchema,
@@ -30744,12 +30935,12 @@ var ENTITIES = [
30744
30935
  {
30745
30936
  name: "shopping-lists",
30746
30937
  path: "/shopping-lists",
30747
- readScope: "shopping:read",
30938
+ readScopes: ["shopping:read"],
30748
30939
  returns: "ShoppingList",
30749
30940
  canGet: true,
30750
30941
  listFlags: [],
30751
30942
  write: {
30752
- scope: "shopping:write",
30943
+ scopes: ["shopping:write"],
30753
30944
  createSchema: createShoppingListInputSchema,
30754
30945
  createInput: "CreateShoppingListInput",
30755
30946
  updateSchema: updateShoppingListInputSchema,
@@ -30760,12 +30951,12 @@ var ENTITIES = [
30760
30951
  {
30761
30952
  name: "inventory-items",
30762
30953
  path: "/inventory-items",
30763
- readScope: "inventory:read",
30954
+ readScopes: ["inventory:read"],
30764
30955
  returns: "InventoryItem",
30765
30956
  canGet: true,
30766
30957
  listFlags: [],
30767
30958
  write: {
30768
- scope: "inventory:write",
30959
+ scopes: ["inventory:write"],
30769
30960
  createSchema: createInventoryItemInputSchema,
30770
30961
  createInput: "CreateInventoryItemInput",
30771
30962
  updateSchema: updateInventoryItemInputSchema,
@@ -30776,12 +30967,12 @@ var ENTITIES = [
30776
30967
  {
30777
30968
  name: "recipes",
30778
30969
  path: "/recipes",
30779
- readScope: "meals:read",
30970
+ readScopes: ["meals:read"],
30780
30971
  returns: "Recipe",
30781
30972
  canGet: true,
30782
30973
  listFlags: [],
30783
30974
  write: {
30784
- scope: "meals:write",
30975
+ scopes: ["meals:write"],
30785
30976
  createSchema: createRecipeInputSchema,
30786
30977
  createInput: "CreateRecipeInput",
30787
30978
  updateSchema: updateRecipeInputSchema,
@@ -30792,12 +30983,12 @@ var ENTITIES = [
30792
30983
  {
30793
30984
  name: "meal-plans",
30794
30985
  path: "/meal-plans",
30795
- readScope: "meals:read",
30986
+ readScopes: ["meals:read"],
30796
30987
  returns: "MealPlan",
30797
30988
  canGet: true,
30798
30989
  listFlags: [],
30799
30990
  write: {
30800
- scope: "meals:write",
30991
+ scopes: ["meals:write"],
30801
30992
  createSchema: createMealPlanInputSchema,
30802
30993
  createInput: "CreateMealPlanInput",
30803
30994
  updateSchema: updateMealPlanInputSchema,
@@ -30808,7 +30999,7 @@ var ENTITIES = [
30808
30999
  {
30809
31000
  name: "occurrences",
30810
31001
  path: "/occurrences",
30811
- readScope: "occurrences:read",
31002
+ readScopes: ["occurrences:read"],
30812
31003
  returns: "Occurrence",
30813
31004
  canGet: false,
30814
31005
  listFlags: [
@@ -30823,7 +31014,7 @@ var ENTITIES = [
30823
31014
  {
30824
31015
  name: "time-blocks",
30825
31016
  path: "/time-blocks",
30826
- readScope: "calendar:read",
31017
+ readScopes: ["calendar:read"],
30827
31018
  returns: "TimeBlockView",
30828
31019
  canGet: true,
30829
31020
  listFlags: [
@@ -30835,7 +31026,7 @@ var ENTITIES = [
30835
31026
  { name: "kind", description: "only blocks of this kind: task, focus or life" }
30836
31027
  ],
30837
31028
  write: {
30838
- scope: "calendar:write",
31029
+ scopes: ["calendar:write"],
30839
31030
  createSchema: createTimeBlockInputSchema,
30840
31031
  createInput: "CreateTimeBlockInput",
30841
31032
  updateSchema: updateTimeBlockInputSchema,
@@ -30846,12 +31037,12 @@ var ENTITIES = [
30846
31037
  {
30847
31038
  name: "block-templates",
30848
31039
  path: "/block-templates",
30849
- readScope: "calendar:read",
31040
+ readScopes: ["calendar:read"],
30850
31041
  returns: "BlockTemplateView",
30851
31042
  canGet: true,
30852
31043
  listFlags: [],
30853
31044
  write: {
30854
- scope: "calendar:write",
31045
+ scopes: ["calendar:write"],
30855
31046
  createSchema: createBlockTemplateInputSchema,
30856
31047
  createInput: "CreateBlockTemplateInput",
30857
31048
  updateSchema: updateBlockTemplateInputSchema,
@@ -30862,7 +31053,7 @@ var ENTITIES = [
30862
31053
  {
30863
31054
  name: "webhooks",
30864
31055
  path: "/webhooks",
30865
- readScope: "webhooks:read",
31056
+ readScopes: ["webhooks:read"],
30866
31057
  returns: "WebhookSubscription",
30867
31058
  canGet: true,
30868
31059
  listFlags: []
@@ -30883,8 +31074,8 @@ function entityOperations(e) {
30883
31074
  }
30884
31075
 
30885
31076
  // src/health-commands.ts
30886
- var READ = ["health:read"];
30887
- var WRITE2 = ["health:write"];
31077
+ var READ2 = ["health:read"];
31078
+ var WRITE3 = ["health:write"];
30888
31079
  var DATA_FLAG = {
30889
31080
  name: "data",
30890
31081
  type: "string",
@@ -30898,7 +31089,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
30898
31089
  summary: `List your health ${noun}s.`,
30899
31090
  args: [],
30900
31091
  flags: listFlags,
30901
- scopes: READ,
31092
+ scopes: READ2,
30902
31093
  returns
30903
31094
  },
30904
31095
  {
@@ -30906,7 +31097,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
30906
31097
  summary: `Show one health ${noun}.`,
30907
31098
  args: [id],
30908
31099
  flags: [],
30909
- scopes: READ,
31100
+ scopes: READ2,
30910
31101
  returns
30911
31102
  },
30912
31103
  {
@@ -30914,7 +31105,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
30914
31105
  summary: `Add a health ${noun} (--data '${createInput}').`,
30915
31106
  args: [],
30916
31107
  flags: [DATA_FLAG, ...addFlags],
30917
- scopes: WRITE2,
31108
+ scopes: WRITE3,
30918
31109
  returns,
30919
31110
  notes: `Body: ${createInput}. Always private to you; subjectNodeId names who it is about (absent = you).`
30920
31111
  },
@@ -30923,7 +31114,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
30923
31114
  summary: `Change a health ${noun} (--data '${updateInput}').`,
30924
31115
  args: [id],
30925
31116
  flags: [DATA_FLAG],
30926
- scopes: WRITE2,
31117
+ scopes: WRITE3,
30927
31118
  returns,
30928
31119
  notes: `Body: ${updateInput}. null clears a field.`
30929
31120
  },
@@ -30932,7 +31123,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
30932
31123
  summary: `Delete a health ${noun}.`,
30933
31124
  args: [id],
30934
31125
  flags: [],
30935
- scopes: WRITE2,
31126
+ scopes: WRITE3,
30936
31127
  returns: "Deleted",
30937
31128
  notes: "Destructive: needs --allow-destructive. An Agent's delete waits on your confirmation."
30938
31129
  }
@@ -30944,7 +31135,7 @@ var HEALTH_COMMAND_SPECS = [
30944
31135
  summary: "Show whether Health is on (only you turn it on or off, in do).",
30945
31136
  args: [],
30946
31137
  flags: [],
30947
- scopes: READ,
31138
+ scopes: READ2,
30948
31139
  returns: "HealthConsentState",
30949
31140
  notes: "`granted` is on at today's consent text (`currentVersion`); `renewalNeeded` means you agreed to an older text, so writes wait until you agree again in do. `aiProvider` names who processes what the agent reads (null when this deployment names none)."
30950
31141
  },
@@ -30953,7 +31144,7 @@ var HEALTH_COMMAND_SPECS = [
30953
31144
  summary: "Show your health profile: blood group, allergies, lifestyle, notes, emergency contact, GP.",
30954
31145
  args: [],
30955
31146
  flags: [],
30956
- scopes: READ,
31147
+ scopes: READ2,
30957
31148
  returns: "HealthProfile"
30958
31149
  },
30959
31150
  {
@@ -30961,7 +31152,7 @@ var HEALTH_COMMAND_SPECS = [
30961
31152
  summary: "Show your Medical ID: name, blood group, allergies, current medications, height, emergency contact.",
30962
31153
  args: [],
30963
31154
  flags: [],
30964
- scopes: READ,
31155
+ scopes: READ2,
30965
31156
  returns: "HealthMedicalId",
30966
31157
  notes: "The few facts someone helping in an emergency needs. The emergency contact comes with their name and phone; a field not set is null. Each read by a key is logged for the member."
30967
31158
  },
@@ -30982,7 +31173,7 @@ var HEALTH_COMMAND_SPECS = [
30982
31173
  description: `Sex, in your words, for the doctor's page ("female"); "" clears it.`
30983
31174
  }
30984
31175
  ],
30985
- scopes: WRITE2,
31176
+ scopes: WRITE3,
30986
31177
  returns: "HealthProfile",
30987
31178
  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."
30988
31179
  },
@@ -30991,7 +31182,7 @@ var HEALTH_COMMAND_SPECS = [
30991
31182
  summary: "Log one health message: find or start the Episode, add a symptom and/or a dose.",
30992
31183
  args: [],
30993
31184
  flags: [DATA_FLAG],
30994
- scopes: WRITE2,
31185
+ scopes: WRITE3,
30995
31186
  returns: "HealthReceipt",
30996
31187
  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.'
30997
31188
  },
@@ -31000,7 +31191,7 @@ var HEALTH_COMMAND_SPECS = [
31000
31191
  summary: `Correct a symptom entry and get a receipt (--data '{"bodyRegion":"ankle_l"}').`,
31001
31192
  args: [{ name: "entry", required: true, description: "The entry's id (its latest version)." }],
31002
31193
  flags: [DATA_FLAG],
31003
- scopes: WRITE2,
31194
+ scopes: WRITE3,
31004
31195
  returns: "HealthReceipt"
31005
31196
  },
31006
31197
  {
@@ -31011,7 +31202,7 @@ var HEALTH_COMMAND_SPECS = [
31011
31202
  { name: "trend", required: true, description: "better|same|worse, against yesterday." }
31012
31203
  ],
31013
31204
  flags: [],
31014
- scopes: WRITE2,
31205
+ scopes: WRITE3,
31015
31206
  returns: "HealthReceipt",
31016
31207
  notes: "One a day: a second check-in the same day corrects the first. The receipt carries its undo."
31017
31208
  },
@@ -31028,7 +31219,7 @@ var HEALTH_COMMAND_SPECS = [
31028
31219
  description: "Post the confirmation card in this thread (by slug); the agent's key only."
31029
31220
  }
31030
31221
  ],
31031
- scopes: WRITE2,
31222
+ scopes: WRITE3,
31032
31223
  returns: "HealthRedFlagsResult",
31033
31224
  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."
31034
31225
  },
@@ -31040,7 +31231,7 @@ var HEALTH_COMMAND_SPECS = [
31040
31231
  { name: "id", required: true, description: "The id from the receipt's undo." }
31041
31232
  ],
31042
31233
  flags: [],
31043
- scopes: WRITE2,
31234
+ scopes: WRITE3,
31044
31235
  returns: "HealthUndoResult",
31045
31236
  notes: "A POST, so an Agent's undo never waits on a confirmation."
31046
31237
  },
@@ -31049,7 +31240,7 @@ var HEALTH_COMMAND_SPECS = [
31049
31240
  summary: "Find the open Episode a complaint belongs to, or start one (--data UpsertHealthEpisodeInput).",
31050
31241
  args: [],
31051
31242
  flags: [DATA_FLAG],
31052
- scopes: WRITE2,
31243
+ scopes: WRITE3,
31053
31244
  returns: "HealthEpisodeUpsert"
31054
31245
  },
31055
31246
  {
@@ -31059,7 +31250,7 @@ var HEALTH_COMMAND_SPECS = [
31059
31250
  flags: [
31060
31251
  { name: "episode", type: "string", description: "The Episode; default the latest open one." }
31061
31252
  ],
31062
- scopes: READ,
31253
+ scopes: READ2,
31063
31254
  returns: "HealthSummary"
31064
31255
  },
31065
31256
  {
@@ -31082,7 +31273,7 @@ var HEALTH_COMMAND_SPECS = [
31082
31273
  summary: "Your For-the-doctor links: sections, expiry, opens; never the secret.",
31083
31274
  args: [],
31084
31275
  flags: [],
31085
- scopes: READ,
31276
+ scopes: READ2,
31086
31277
  returns: "HealthShare[]",
31087
31278
  notes: "Only the member makes a share, in do; a key can list and take one back."
31088
31279
  },
@@ -31091,7 +31282,7 @@ var HEALTH_COMMAND_SPECS = [
31091
31282
  summary: "Take a For-the-doctor link back: it stops opening.",
31092
31283
  args: [{ name: "id", required: true, description: "The share's id." }],
31093
31284
  flags: [],
31094
- scopes: WRITE2,
31285
+ scopes: WRITE3,
31095
31286
  returns: "HealthShare"
31096
31287
  },
31097
31288
  ...recordSpecs("conditions", "condition", "condition", "HealthConditionView", "CreateHealthConditionInput", "UpdateHealthConditionInput"),
@@ -31109,7 +31300,7 @@ var HEALTH_COMMAND_SPECS = [
31109
31300
  description: 'Their role on it, in your words ("dermatologist").'
31110
31301
  }
31111
31302
  ],
31112
- scopes: WRITE2,
31303
+ scopes: WRITE3,
31113
31304
  returns: "HealthConditionView",
31114
31305
  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."
31115
31306
  },
@@ -31121,7 +31312,7 @@ var HEALTH_COMMAND_SPECS = [
31121
31312
  { name: "contact", required: true, description: "The Contact's id." }
31122
31313
  ],
31123
31314
  flags: [],
31124
- scopes: WRITE2,
31315
+ scopes: WRITE3,
31125
31316
  returns: "HealthConditionView",
31126
31317
  notes: "An Agent's removal waits on your confirmation, like a delete."
31127
31318
  },
@@ -31158,7 +31349,7 @@ var HEALTH_COMMAND_SPECS = [
31158
31349
  { name: "at", type: "string", description: "When it happened (ISO); defaults to now." },
31159
31350
  { name: "note", type: "string", description: "A free-text note." }
31160
31351
  ],
31161
- scopes: WRITE2,
31352
+ scopes: WRITE3,
31162
31353
  returns: "HealthSymptomEntry",
31163
31354
  notes: "An Agent's entry is source chat. For qualifiers, send --data with LogHealthSymptomInput."
31164
31355
  },
@@ -31174,7 +31365,7 @@ var HEALTH_COMMAND_SPECS = [
31174
31365
  }
31175
31366
  ],
31176
31367
  flags: [DATA_FLAG],
31177
- scopes: WRITE2,
31368
+ scopes: WRITE3,
31178
31369
  returns: "HealthSymptomEntry",
31179
31370
  notes: "Body: CorrectHealthSymptomInput; fields not named are copied. The history stays."
31180
31371
  },
@@ -31186,7 +31377,7 @@ var HEALTH_COMMAND_SPECS = [
31186
31377
  { name: "entry", required: true, description: "The entry's id." }
31187
31378
  ],
31188
31379
  flags: [],
31189
- scopes: WRITE2,
31380
+ scopes: WRITE3,
31190
31381
  returns: "Deleted"
31191
31382
  },
31192
31383
  ...recordSpecs("visits", "visit", "visit", "HealthEncounterView", "CreateHealthEncounterInput", "UpdateHealthEncounterInput"),
@@ -31196,7 +31387,7 @@ var HEALTH_COMMAND_SPECS = [
31196
31387
  summary: "List the doses of a Medication, latest first.",
31197
31388
  args: [{ name: "med", required: true, description: "The Medication's id." }],
31198
31389
  flags: [],
31199
- scopes: READ,
31390
+ scopes: READ2,
31200
31391
  returns: "HealthMedicationDose"
31201
31392
  },
31202
31393
  {
@@ -31210,7 +31401,7 @@ var HEALTH_COMMAND_SPECS = [
31210
31401
  { name: "at", type: "string", description: "When (ISO); defaults to now." },
31211
31402
  { name: "note", type: "string", description: "A free-text note." }
31212
31403
  ],
31213
- scopes: WRITE2,
31404
+ scopes: WRITE3,
31214
31405
  returns: "HealthMedicationDose"
31215
31406
  },
31216
31407
  {
@@ -31221,7 +31412,7 @@ var HEALTH_COMMAND_SPECS = [
31221
31412
  { name: "dose", required: true, description: "The dose's id." }
31222
31413
  ],
31223
31414
  flags: [DATA_FLAG],
31224
- scopes: WRITE2,
31415
+ scopes: WRITE3,
31225
31416
  returns: "HealthMedicationDose",
31226
31417
  notes: "Body: UpdateHealthDoseInput."
31227
31418
  },
@@ -31233,7 +31424,7 @@ var HEALTH_COMMAND_SPECS = [
31233
31424
  { name: "dose", required: true, description: "The dose's id." }
31234
31425
  ],
31235
31426
  flags: [],
31236
- scopes: WRITE2,
31427
+ scopes: WRITE3,
31237
31428
  returns: "Deleted"
31238
31429
  },
31239
31430
  ...recordSpecs("labs", "lab", "lab draw", "HealthLabPanelView", "CreateHealthLabPanelInput", "UpdateHealthLabPanelInput"),
@@ -31255,7 +31446,7 @@ var HEALTH_COMMAND_SPECS = [
31255
31446
  description: "The file's media type, when its name doesn't say."
31256
31447
  }
31257
31448
  ],
31258
- scopes: WRITE2,
31449
+ scopes: WRITE3,
31259
31450
  returns: "HealthLabImportResult",
31260
31451
  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."
31261
31452
  },
@@ -31264,7 +31455,7 @@ var HEALTH_COMMAND_SPECS = [
31264
31455
  summary: "The canonical analytes: key, printed name, group, unit, LOINC and aliases.",
31265
31456
  args: [],
31266
31457
  flags: [],
31267
- scopes: READ,
31458
+ scopes: READ2,
31268
31459
  returns: "LabAnalyteDef[]",
31269
31460
  notes: 'A result is written under the canonical key its analyte or name stands for ("ALAT" \u2192 alt, "25-OH vitamin D" \u2192 vitamin_d); a name that matches none keeps its slug and comes back in the draw\'s `unmatched`.'
31270
31461
  },
@@ -31279,7 +31470,7 @@ var HEALTH_COMMAND_SPECS = [
31279
31470
  description: "Required: only report, change nothing."
31280
31471
  }
31281
31472
  ],
31282
- scopes: READ,
31473
+ scopes: READ2,
31283
31474
  returns: "HealthLabNormalizeReport",
31284
31475
  notes: "Nothing is rewritten: trends already read old keys as their canonical one. Lists each result's from \u2192 to, and the keys that match no canonical analyte."
31285
31476
  },
@@ -31288,7 +31479,7 @@ var HEALTH_COMMAND_SPECS = [
31288
31479
  summary: "Every analyte your draws measured, each with its trend, grouped as a lab sheet.",
31289
31480
  args: [],
31290
31481
  flags: [],
31291
- scopes: READ,
31482
+ scopes: READ2,
31292
31483
  returns: "HealthLabOverview"
31293
31484
  },
31294
31485
  {
@@ -31296,16 +31487,16 @@ var HEALTH_COMMAND_SPECS = [
31296
31487
  summary: "Whether your Google Health is connected, the last sync, and what stops it.",
31297
31488
  args: [],
31298
31489
  flags: [],
31299
- scopes: READ,
31490
+ scopes: READ2,
31300
31491
  returns: "GoogleHealthStatus",
31301
- notes: "GET /api/v1/health/google-health. Connecting needs Google's consent in a browser: Settings \u2192 Health in do. `failure` (reauth, not_linked, unavailable, other) says what stops the sync, with a title and a hint."
31492
+ notes: "GET /api/v1/health/google-health. Connecting needs Google's consent in a browser: Settings \u2192 Health in do. `failure` (reauth, not_linked, unavailable, other) says what stops the sync, with a title and a hint. `nutrition` says whether your food is written back to Google Health (turned on in do; each settled day once, one entry per meal type) and the last day written."
31302
31493
  },
31303
31494
  {
31304
31495
  path: ["health", "google", "sync"],
31305
31496
  summary: "Sync your Google Health now (sleep, resting heart rate, HRV, steps, workouts), as the morning sync does.",
31306
31497
  args: [],
31307
31498
  flags: [],
31308
- scopes: WRITE2,
31499
+ scopes: WRITE3,
31309
31500
  returns: "GoogleHealthSyncResult",
31310
31501
  notes: "POST /api/v1/health/google-health/sync. Written and skipped per resource, and the status after it; a sync that stops answers 200 with `status.failure`. 409 before Google Health is connected, 403 while the server has it switched off."
31311
31502
  },
@@ -31314,7 +31505,7 @@ var HEALTH_COMMAND_SPECS = [
31314
31505
  summary: "One analyte's trend across draws (do health analyte vitamin_d).",
31315
31506
  args: [{ name: "analyte", required: true, description: "The analyte key (vitamin_d)." }],
31316
31507
  flags: [],
31317
- scopes: READ,
31508
+ scopes: READ2,
31318
31509
  returns: "HealthLabAnalyteView"
31319
31510
  },
31320
31511
  {
@@ -31322,7 +31513,7 @@ var HEALTH_COMMAND_SPECS = [
31322
31513
  summary: `Keep a target band or the supplement for an analyte (--data '{"targetLow":40,"targetHigh":60}').`,
31323
31514
  args: [{ name: "analyte", required: true, description: "The analyte key (vitamin_d)." }],
31324
31515
  flags: [DATA_FLAG],
31325
- scopes: WRITE2,
31516
+ scopes: WRITE3,
31326
31517
  returns: "HealthLabAnalyteView",
31327
31518
  notes: "Body: SetHealthLabAnalyteInput. An absent key keeps a field; null clears it."
31328
31519
  }
@@ -31713,6 +31904,21 @@ var COMMAND_SPECS = [
31713
31904
  returns: "JournalRecall",
31714
31905
  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."
31715
31906
  },
31907
+ {
31908
+ path: ["journal", "like"],
31909
+ summary: 'Days like this one: your other days that share the most with a day (its words, tags and rating), for "when did I feel like this before?".',
31910
+ args: [{ name: "date", required: true, description: "The day, YYYY-MM-DD." }],
31911
+ flags: [
31912
+ {
31913
+ name: "limit",
31914
+ type: "string",
31915
+ description: "At most this many days (1\u201310, 5 by default)."
31916
+ }
31917
+ ],
31918
+ scopes: ["journal:read"],
31919
+ returns: "JournalLike",
31920
+ 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."
31921
+ },
31716
31922
  {
31717
31923
  path: ["journal", "digest"],
31718
31924
  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.",
@@ -32647,9 +32853,9 @@ var COMMAND_SPECS = [
32647
32853
  summary: "Tap a tag as if the phone read its sticker: check its habit in, or mark today's run of its routine done.",
32648
32854
  args: [{ name: "id", required: true, description: "The tag id (the ULID in /t/<id>)." }],
32649
32855
  flags: [],
32650
- scopes: ["habits:write", "routines:write"],
32856
+ scopes: [...tapTagOperations.tap.scopes],
32651
32857
  returns: "TapResult",
32652
- notes: "POST /api/v1/tap-tags/{id}/tap. Acts as the key's member; a second tap within 30 s does nothing (outcome already). The receipt's undo names the check-in (DELETE /habits/{habitId}/entries/{entryId}) or the run's steps (POST /routines/runs/{runId}/undo)."
32858
+ notes: `${routeOf(tapTagOperations.tap)}. Acts as the key's member; a second tap within 30 s does nothing (outcome already). The receipt's undo names the check-in (DELETE /habits/{habitId}/entries/{entryId}) or the run's steps (POST /routines/runs/{runId}/undo).`
32653
32859
  },
32654
32860
  {
32655
32861
  path: ["metrics", "list"],
@@ -35867,12 +36073,12 @@ var COMMAND_SPECS = [
35867
36073
  },
35868
36074
  {
35869
36075
  path: ["rsvp-links", "token"],
35870
- summary: "Show a live RSVP link again: the same token it was minted with.",
36076
+ summary: "Show a live RSVP link again: the same token it was minted with, and a whole-form link's short code.",
35871
36077
  args: [{ name: "id", required: true, description: "The link's id." }],
35872
36078
  flags: [],
35873
36079
  scopes: ["contacts:write"],
35874
36080
  returns: "MintedRsvpLink",
35875
- notes: "The mail and the paper QR carry this one link. A revoked or expired link is gone; one minted before tokens were kept can't be shown: replace it."
36081
+ notes: "The mail and the paper QR carry this one link; `shortCode` (six characters) is printed on the paper invitation and typed at the site's /code. A revoked or expired link is gone; one minted before tokens were kept can't be shown: replace it."
35876
36082
  },
35877
36083
  {
35878
36084
  path: ["rsvp-links", "replace"],
@@ -36249,8 +36455,8 @@ function buildManifest() {
36249
36455
  name: e.name,
36250
36456
  operations: entityOperations(e),
36251
36457
  path: `${API_PREFIX}${e.path}`,
36252
- readScope: e.readScope,
36253
- ...e.write ? { writeScope: e.write.scope } : {},
36458
+ readScopes: e.readScopes,
36459
+ ...e.write ? { writeScopes: e.write.scopes } : {},
36254
36460
  ...e.write?.createInput || e.write?.updateInput ? {
36255
36461
  inputs: {
36256
36462
  ...e.write.createInput ? { create: e.write.createInput } : {},
@@ -37388,6 +37594,24 @@ function renderJournalRecall(data, mode) {
37388
37594
  return out.join(`
37389
37595
  `);
37390
37596
  }
37597
+ function renderJournalLike(data, mode) {
37598
+ const d = data;
37599
+ const out = [paint(mode, "dim", `Like ${d.date ?? "this day"}: ${(d.words ?? []).join(" \xB7 ")}`)];
37600
+ const days = d.days ?? [];
37601
+ if (days.length === 0) {
37602
+ out.push(paint(mode, "dim", "(no other day shares enough with it)"));
37603
+ return out.join(`
37604
+ `);
37605
+ }
37606
+ for (const day of days) {
37607
+ const rating = day.metrics?.rating;
37608
+ const meta = rating === undefined ? "" : paint(mode, "dim", ` ${rating}/10`);
37609
+ const shared = [...day.shared ?? [], ...(day.sharedTags ?? []).map((t) => `#${t}`)];
37610
+ out.push(`${day.date} ${day.title ?? paint(mode, "dim", "(no entry)")}${meta}${shared.length ? paint(mode, "dim", ` \xB7 ${shared.join(", ")}`) : ""}`);
37611
+ }
37612
+ return out.join(`
37613
+ `);
37614
+ }
37391
37615
  function renderJournalDigest(data) {
37392
37616
  return data.text ?? "";
37393
37617
  }
@@ -38569,6 +38793,18 @@ var HANDLERS = {
38569
38793
  render: renderJournalRecall
38570
38794
  };
38571
38795
  },
38796
+ "journal like": async ({ args, flags, requireClient }) => {
38797
+ const [date] = args;
38798
+ if (!date)
38799
+ throw usageError("journal like needs the day, YYYY-MM-DD");
38800
+ parseInput(journalDateSchema, date);
38801
+ const limit = flagString(flags, "limit");
38802
+ parseInput(journalLikeQuerySchema, limit === undefined ? {} : { limit });
38803
+ return {
38804
+ data: await requireClient().get(`/journal/days/${date}/like`, { query: { limit } }),
38805
+ render: renderJournalLike
38806
+ };
38807
+ },
38572
38808
  "journal digest": async ({ requireClient }) => ({
38573
38809
  data: await requireClient().get("/journal/digest"),
38574
38810
  render: renderJournalDigest
@@ -39699,7 +39935,7 @@ var HANDLERS = {
39699
39935
  "tap-tags tap": async ({ args, requireClient }) => {
39700
39936
  const id = requireArg(args, 0, "tag id");
39701
39937
  return {
39702
- data: await requireClient().post(`/tap-tags/${encodeURIComponent(id)}/tap`, {}),
39938
+ data: await requireClient().post(pathOf(tapTagOperations.tap, { id }), {}),
39703
39939
  render: renderRecord
39704
39940
  };
39705
39941
  },