@robbeverhelst/do 1.306.1 → 1.306.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/do.js +1560 -225
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -5646,6 +5646,35 @@ var foodWeekReads = pgTable("food_week_reads", {
5646
5646
  weekStart: text("week_start").notNull()
5647
5647
  }, (t) => [unique("food_week_reads_owner_week_uniq").on(t.tenantId, t.ownerUserId, t.weekStart)]);
5648
5648
 
5649
+ // ../../packages/schema/src/db/api-keys.ts
5650
+ var apiKeys = pgTable("api_keys", {
5651
+ id: text("id").primaryKey(),
5652
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
5653
+ userId: text("user_id").references(() => users.id),
5654
+ agentId: text("agent_id").references(() => agents.id),
5655
+ name: text("name").notNull(),
5656
+ prefix: text("prefix").notNull(),
5657
+ keyHash: text("key_hash").notNull().unique(),
5658
+ scopes: text("scopes").array().notNull().default([]),
5659
+ lastUsedAt: timestamp("last_used_at", { withTimezone: true }),
5660
+ revokedAt: timestamp("revoked_at", { withTimezone: true }),
5661
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5662
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5663
+ }, (t) => [index("api_keys_tenant_idx").on(t.tenantId)]);
5664
+ var apiKeyChanges = pgTable("api_key_changes", {
5665
+ id: text("id").primaryKey(),
5666
+ tenantId: text("tenant_id").notNull().references(() => tenants.id),
5667
+ apiKeyId: text("api_key_id").notNull().references(() => apiKeys.id),
5668
+ actorUserId: text("actor_user_id").references(() => users.id),
5669
+ actorApiKeyId: text("actor_api_key_id").references(() => apiKeys.id),
5670
+ actorAgentId: text("actor_agent_id").references(() => agents.id),
5671
+ beforeName: text("before_name").notNull(),
5672
+ afterName: text("after_name").notNull(),
5673
+ beforeScopes: text("before_scopes").array().notNull(),
5674
+ afterScopes: text("after_scopes").array().notNull(),
5675
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5676
+ }, (t) => [index("api_key_changes_key_idx").on(t.apiKeyId, t.createdAt)]);
5677
+
5649
5678
  // ../../packages/schema/src/db/webhooks.ts
5650
5679
  var webhookSubscriptions = pgTable("webhook_subscriptions", {
5651
5680
  id: text("id").primaryKey(),
@@ -24797,6 +24826,56 @@ var reviewDayStatSchema = object({
24797
24826
  });
24798
24827
  var reviewStatsSchema = array(reviewDayStatSchema);
24799
24828
 
24829
+ // ../../packages/schema/src/contracts/reads.ts
24830
+ var readWindowQuerySchema = object({
24831
+ from: instantQuerySchema.optional(),
24832
+ to: instantQuerySchema.optional()
24833
+ });
24834
+ var nowQuerySchema = readWindowQuerySchema.extend({
24835
+ for: taskWaitingOnInputSchema.optional()
24836
+ });
24837
+ var nowResponseSchema = object({
24838
+ from: exports_coerce.date(),
24839
+ to: exports_coerce.date(),
24840
+ for: actorRefSchema,
24841
+ waitingOnYou: array(taskViewSchema),
24842
+ readyForYou: array(taskViewSchema).default([]),
24843
+ timeline: todayResponseSchema
24844
+ });
24845
+ var landedByActorSchema = object({
24846
+ actor: actorRefSchema.nullable(),
24847
+ count: number2().int().nonnegative()
24848
+ });
24849
+ var landedResponseSchema = object({
24850
+ from: exports_coerce.date(),
24851
+ to: exports_coerce.date(),
24852
+ total: number2().int().nonnegative(),
24853
+ byActor: array(landedByActorSchema),
24854
+ tasks: array(taskViewSchema)
24855
+ });
24856
+ var searchNodeTypeSchema = _enum2([
24857
+ "task",
24858
+ "project",
24859
+ "area",
24860
+ "note",
24861
+ "habit",
24862
+ "routine",
24863
+ "contact",
24864
+ "collection_item"
24865
+ ]);
24866
+ var searchQuerySchema = object({
24867
+ q: string2().trim().min(1).max(200),
24868
+ type: preprocess((raw) => (Array.isArray(raw) ? raw : [raw]).flatMap((v) => typeof v === "string" ? v.split(",") : [v]).map((v) => typeof v === "string" ? v.trim() : v).filter((v) => v !== ""), array(searchNodeTypeSchema).min(1)).optional(),
24869
+ limit: exports_coerce.number().int().min(1).max(50).default(20)
24870
+ });
24871
+ var searchHitSchema = object({
24872
+ id: string2(),
24873
+ type: searchNodeTypeSchema,
24874
+ title: string2(),
24875
+ snippet: string2().nullable(),
24876
+ updatedAt: exports_coerce.date()
24877
+ });
24878
+
24800
24879
  // ../../packages/schema/src/contracts/progress.ts
24801
24880
  var progressDateSchema = string2().regex(/^\d{4}-\d{2}-\d{2}$/, "expected YYYY-MM-DD");
24802
24881
  var progressRangeDays = [7, 30, 90];
@@ -28865,6 +28944,83 @@ var planCreateResultSchema = planViewSchema.extend({
28865
28944
  });
28866
28945
 
28867
28946
  // ../../packages/schema/src/contracts/api-key-scopes.ts
28947
+ var API_KEY_SCOPE_VALUES = [
28948
+ "webhooks:read",
28949
+ "webhooks:write",
28950
+ "tasks:read",
28951
+ "tasks:write",
28952
+ "habits:read",
28953
+ "habits:write",
28954
+ "occurrences:read",
28955
+ "occurrences:write",
28956
+ "shopping:read",
28957
+ "shopping:write",
28958
+ "inventory:read",
28959
+ "inventory:write",
28960
+ "meals:read",
28961
+ "meals:write",
28962
+ "notes:read",
28963
+ "notes:write",
28964
+ "contacts:read",
28965
+ "contacts:write",
28966
+ "projects:read",
28967
+ "projects:write",
28968
+ "areas:read",
28969
+ "areas:write",
28970
+ "routines:read",
28971
+ "routines:write",
28972
+ "settings:read",
28973
+ "settings:write",
28974
+ "calendar:read",
28975
+ "calendar:write",
28976
+ "members:read",
28977
+ "members:write",
28978
+ "sharing:read",
28979
+ "sharing:write",
28980
+ "comments:read",
28981
+ "comments:write",
28982
+ "roles:read",
28983
+ "links:read",
28984
+ "links:write",
28985
+ "collections:read",
28986
+ "collections:write",
28987
+ "presets:read",
28988
+ "presets:install",
28989
+ "export:read",
28990
+ "orca:read",
28991
+ "orca:write",
28992
+ "places:read",
28993
+ "places:write",
28994
+ "agents:read",
28995
+ "agents:write",
28996
+ "agents:converse",
28997
+ "files:read",
28998
+ "files:write",
28999
+ "key-dates:read",
29000
+ "key-dates:write",
29001
+ "documents:read",
29002
+ "documents:write",
29003
+ "periods:read",
29004
+ "periods:write",
29005
+ "planning:read",
29006
+ "planning:write",
29007
+ "animals:read",
29008
+ "animals:write",
29009
+ "keys:read",
29010
+ "keys:write",
29011
+ "health:read",
29012
+ "health:write",
29013
+ "health:export",
29014
+ "journal:read",
29015
+ "journal:write",
29016
+ "journal:export",
29017
+ "documents:private",
29018
+ "finance:read",
29019
+ "finance:write",
29020
+ "people:family-tree",
29021
+ "private:*",
29022
+ "*"
29023
+ ];
28868
29024
  var FULL_ACCESS_SCOPE = "*";
28869
29025
  function scopeGranted(granted, required) {
28870
29026
  if (granted.includes(required))
@@ -28876,6 +29032,67 @@ function scopeGranted(granted, required) {
28876
29032
  return granted.includes(FULL_ACCESS_SCOPE);
28877
29033
  }
28878
29034
 
29035
+ // ../../packages/schema/src/contracts/api-keys.ts
29036
+ var apiKeyScopeSchema = _enum2(API_KEY_SCOPE_VALUES);
29037
+ var apiKeyScopesInputSchema = array(apiKeyScopeSchema).refine((scopes) => !scopes.includes(PRIVATE_ACCESS_SCOPE) || scopes.includes(FULL_ACCESS_SCOPE), {
29038
+ message: `"${PRIVATE_ACCESS_SCOPE}" is only granted together with "${FULL_ACCESS_SCOPE}"`
29039
+ });
29040
+ var apiKeySchema = createSelectSchema(apiKeys, {
29041
+ scopes: array(apiKeyScopeSchema),
29042
+ agentId: string2().nullable().default(null),
29043
+ lastUsedAt: exports_coerce.date().nullable(),
29044
+ revokedAt: exports_coerce.date().nullable(),
29045
+ createdAt: exports_coerce.date(),
29046
+ updatedAt: exports_coerce.date()
29047
+ });
29048
+ var apiKeyPublicSchema = apiKeySchema.omit({ keyHash: true });
29049
+ var createApiKeyInputSchema = createInsertSchema(apiKeys, {
29050
+ name: string2().min(1).max(120),
29051
+ scopes: apiKeyScopesInputSchema.optional(),
29052
+ agentId: ulidSchema.nullish()
29053
+ }).omit({
29054
+ id: true,
29055
+ tenantId: true,
29056
+ userId: true,
29057
+ prefix: true,
29058
+ keyHash: true,
29059
+ lastUsedAt: true,
29060
+ revokedAt: true,
29061
+ createdAt: true,
29062
+ updatedAt: true
29063
+ }).strict();
29064
+ var createApiKeyResultSchema = apiKeyPublicSchema.extend({ key: string2() });
29065
+ var updateApiKeyInputSchema = object({
29066
+ name: string2().min(1).max(120).optional(),
29067
+ scopes: apiKeyScopesInputSchema.optional()
29068
+ }).strict().refine((input) => input.name !== undefined || input.scopes !== undefined, {
29069
+ message: "change the name, the scopes, or both"
29070
+ });
29071
+ var apiKeyChangeSchema = object({
29072
+ id: string2(),
29073
+ at: exports_coerce.date(),
29074
+ by: object({
29075
+ userId: string2().nullable(),
29076
+ apiKeyId: string2().nullable(),
29077
+ apiKeyName: string2().nullable(),
29078
+ agentId: string2().nullable(),
29079
+ agentName: string2().nullable()
29080
+ }),
29081
+ before: object({ name: string2(), scopes: array(apiKeyScopeSchema) }),
29082
+ after: object({ name: string2(), scopes: array(apiKeyScopeSchema) })
29083
+ });
29084
+ var apiKeyListItemSchema = apiKeyPublicSchema.extend({
29085
+ mine: boolean3().default(false),
29086
+ changes: array(apiKeyChangeSchema).default([])
29087
+ });
29088
+ var apiKeyContextSchema = object({
29089
+ apiKeyId: string2(),
29090
+ tenantId: string2(),
29091
+ userId: string2().nullable(),
29092
+ agentId: string2().nullable(),
29093
+ scopes: array(apiKeyScopeSchema)
29094
+ });
29095
+
28879
29096
  // ../../packages/schema/src/net/ssrf.ts
28880
29097
  var BLOCKED_HOST_SUFFIXES = [".internal", ".local", ".localhost"];
28881
29098
  var BLOCKED_HOSTNAMES = new Set(["localhost"]);
@@ -30144,6 +30361,45 @@ function routeOf(op) {
30144
30361
  return `${op.method.toUpperCase()} ${PUBLIC_API_PREFIX}${op.path}`;
30145
30362
  }
30146
30363
 
30364
+ // ../../packages/schema/src/operations/agent-connection.ts
30365
+ var notManager = "The key's member doesn't run the Space (`canManageSpace`: owner or admin).";
30366
+ var agentConnectionOperations = defineOperations({
30367
+ get: {
30368
+ method: "get",
30369
+ path: "/agent/connection",
30370
+ scopes: ["agents:read"],
30371
+ tag: "Agents",
30372
+ summary: "Get the agent connection",
30373
+ description: "The agent runtime the agent panel's chat goes to (`connected: false` when none). Never returns the key: `hasKey` says whether one is set.",
30374
+ status: 200,
30375
+ response: { description: "The connection, without its key." }
30376
+ },
30377
+ save: {
30378
+ method: "put",
30379
+ path: "/agent/connection",
30380
+ scopes: ["agents:write"],
30381
+ tag: "Agents",
30382
+ summary: "Set the agent connection",
30383
+ description: "Create or replace the Space's connection: `endpoint`, optional `model`, `agentId` and `autoFile`. An absent `apiKey` keeps the stored key, `null` clears it; the key is write-only. Owner/admin only (`canManageSpace`); 404 for an `agentId` that isn't a live Agent of the Space.",
30384
+ body: saveAgentConnectionInputSchema,
30385
+ status: 200,
30386
+ response: { description: "The saved connection, without its key." },
30387
+ errors: { 400: true, 403: notManager, 404: true },
30388
+ mcp: false
30389
+ },
30390
+ remove: {
30391
+ method: "delete",
30392
+ path: "/agent/connection",
30393
+ scopes: ["agents:write"],
30394
+ tag: "Agents",
30395
+ summary: "Remove the agent connection",
30396
+ description: "Forget the Space's connection (the deployment's env connection, if any, applies again). Owner/admin only (`canManageSpace`). An Agent's key waits for the member's confirmation (428 `confirmation_required`).",
30397
+ status: 204,
30398
+ response: { description: "Removed." },
30399
+ errors: { 403: notManager }
30400
+ }
30401
+ });
30402
+
30147
30403
  // ../../packages/schema/src/operations/agents.ts
30148
30404
  var agentOperations = defineOperations({
30149
30405
  list: {
@@ -30737,6 +30993,182 @@ var calendarOperations = defineOperations({
30737
30993
  }
30738
30994
  });
30739
30995
 
30996
+ // ../../packages/schema/src/operations/changes.ts
30997
+ var changesOperations = defineOperations({
30998
+ list: {
30999
+ method: "get",
31000
+ path: "/changes",
31001
+ scopes: [],
31002
+ tag: "Changes",
31003
+ summary: "Read the change feed",
31004
+ description: "What was created, updated or deleted in the Space after `since`, oldest first \u2014 the main sync mechanism. Store `nextCursor` and pass it back as `since`; ask again right away while `hasMore`. Each entry names the entity kind, id and op (a soft delete is `deleted`); refetch the entity from its own resource. `since=now` returns no changes and a fresh cursor (take it, then do the initial full read); omit `since` to read the whole log. The feed never skips a change that commits later. No scope of its own: a key sees the kinds its `*:read` scopes cover (`tasks:read` \u2192 `task`, `collections:read` \u2192 `collection_item`, a full-access key sees all), and gets a 403 when it covers none or asks for a kind it can't read.",
31005
+ query: [
31006
+ { name: "since", description: "A `nextCursor` from a prior page, or `now`." },
31007
+ {
31008
+ name: "limit",
31009
+ description: "Page size, 1\u2013500 (default 100; larger values are capped).",
31010
+ type: "integer",
31011
+ minimum: 1
31012
+ },
31013
+ {
31014
+ name: "entity",
31015
+ description: "Comma-separated kinds to keep: `task`, `project`, `area`, `routine`, `habit`, `occurrence`, `note`, `contact`, `collection_item`, `inventory_item`, `shopping_list`, `recipe`, `meal_plan`, `event`."
31016
+ }
31017
+ ],
31018
+ querySchema: changesQuerySchema,
31019
+ status: 200,
31020
+ response: { description: "One page of changes.", view: "ChangesPage" },
31021
+ errors: { 400: true, 403: "The key can't read any feed kind, or one it asked for." },
31022
+ operationId: "read_changes"
31023
+ }
31024
+ });
31025
+
31026
+ // ../../packages/schema/src/operations/collection.ts
31027
+ var READ2 = ["collections:read"];
31028
+ var WRITE2 = ["collections:write"];
31029
+ var entryParams = { entryId: "The entry's ULID." };
31030
+ var collectionOperations = defineOperations({
31031
+ ...crudOperations({
31032
+ segment: "collection",
31033
+ tag: "Collection",
31034
+ noun: "collection item",
31035
+ read: "collections:read",
31036
+ write: "collections:write",
31037
+ eventBase: "collection.item",
31038
+ create: createCollectionItemInputSchema,
31039
+ update: updateCollectionItemInputSchema,
31040
+ paginated: true,
31041
+ listQuery: [
31042
+ {
31043
+ name: "type",
31044
+ description: "Only items of this Type (`book`, `game`, \u2026; see `/collection/types`)."
31045
+ },
31046
+ { name: "status", description: "Only items in this status of their Type." },
31047
+ { name: "tag", description: "Only items carrying this tag." },
31048
+ {
31049
+ name: "q",
31050
+ description: "Only items whose title contains this text (case-insensitive): find an item by name."
31051
+ },
31052
+ { name: "title", description: "Only items with exactly this title (case-insensitive)." }
31053
+ ],
31054
+ listQuerySchema: paginationQuerySchema.extend({
31055
+ type: string2().optional(),
31056
+ status: string2().optional(),
31057
+ tag: string2().optional(),
31058
+ q: string2().optional(),
31059
+ title: string2().optional()
31060
+ })
31061
+ }),
31062
+ log: {
31063
+ method: "post",
31064
+ path: "/collection/log",
31065
+ scopes: WRITE2,
31066
+ tag: "Collection",
31067
+ summary: "Log a collection entry by title",
31068
+ description: 'Log an Entry ("finished Hades, 4 stars") against the item with exactly this `title` (case-insensitive), narrowed by `type` when given. With no such item and a `type`, the item is added first; without a `type`, a 404 names the closest titles. Several items with the title are a 409 that names them (ask which, or log against one by id). `status` also moves the item to that status of its Type (`want`, `have`, `doing`, `done` or `dropped`; e.g. `done` for "finished"). `rating` is 0.5\u20135 in half steps; the day defaults to today in the member\'s timezone. Returns `{ created, entryId, item }`; fires `collection.entry.created`.',
31069
+ body: collectionLogInputSchema,
31070
+ status: 201,
31071
+ response: { description: "`{ created, entryId, item }`." },
31072
+ errors: { 400: true, 404: true, 409: "Several items have this title; the message names them." },
31073
+ webhook: "collection.entry.created"
31074
+ },
31075
+ types: {
31076
+ method: "get",
31077
+ path: "/collection/types",
31078
+ scopes: READ2,
31079
+ tag: "Collection",
31080
+ summary: "List Collection Types",
31081
+ description: "The code-defined Type registry (Books, Films, \u2026) as summaries.",
31082
+ status: 200,
31083
+ response: { description: "The Type registry." }
31084
+ },
31085
+ backfillCovers: {
31086
+ method: "post",
31087
+ path: "/collection/covers",
31088
+ scopes: WRITE2,
31089
+ tag: "Collection",
31090
+ summary: "Backfill covers",
31091
+ description: "Find covers for books and games that have none (Open Library; IGDB, then Steam), fetched into do's own media store. Works through up to 25 items per call; call again for more. Returns how many were tried and how many now have a cover.",
31092
+ status: 200,
31093
+ response: { description: "`{ checked, found }`." }
31094
+ },
31095
+ cover: {
31096
+ method: "post",
31097
+ path: "/collection/{id}/cover",
31098
+ scopes: WRITE2,
31099
+ tag: "Collection",
31100
+ summary: "Find or replace an item's cover",
31101
+ description: "With no `url`, find the next cover from the providers (books: Open Library; games: IGDB, then Steam) and replace the current one: the one showing and the ones passed over before are skipped, so asking again moves on; `found: false` when no other is left. With a `url`, fetch that image into do's media store and use it (422 when it can't be fetched). Covers are never hotlinked. Returns `{ found, item }`; fires `collection.item.updated`.",
31102
+ body: collectionCoverInputSchema,
31103
+ status: 200,
31104
+ response: { description: "`{ found, item }`." },
31105
+ errors: { 400: true, 404: true, 422: "The given image couldn't be fetched." },
31106
+ webhook: "collection.item.updated"
31107
+ },
31108
+ coverCandidates: {
31109
+ method: "get",
31110
+ path: "/collection/{id}/cover/candidates",
31111
+ scopes: READ2,
31112
+ tag: "Collection",
31113
+ summary: "List other covers for an item",
31114
+ description: "Other covers to pick from, best first (at most 6): the providers' image URLs (books: Open Library, then Google Books; games: IGDB, then Steam) without the one showing and the ones an earlier replace passed over. Nothing is fetched yet; post one as `url` to `/api/v1/collection/{id}/cover` to use it (it's stored by do, and the cover it replaces is passed over from then on). Empty for a Type without automatic covers. Returns `{ candidates: [{ url }] }`.",
31115
+ status: 200,
31116
+ response: { description: "`{ candidates: [{ url }] }`." },
31117
+ errors: { 404: true }
31118
+ },
31119
+ listEntries: {
31120
+ method: "get",
31121
+ path: "/collection/{id}/entries",
31122
+ scopes: READ2,
31123
+ tag: "Collection",
31124
+ summary: "List an item's entries",
31125
+ description: "The item's dated Entry log (ratings/notes).",
31126
+ status: 200,
31127
+ response: { description: "The item's entries." },
31128
+ errors: { 404: true }
31129
+ },
31130
+ addEntry: {
31131
+ method: "post",
31132
+ path: "/collection/{id}/entries",
31133
+ scopes: WRITE2,
31134
+ tag: "Collection",
31135
+ summary: "Append an entry",
31136
+ description: "Append an Entry (a rating and/or note) to the item with this id; `id` and `occurredOn` are optional (the server mints one and uses today in the member's timezone). To log by title instead, use `POST /api/v1/collection/log`. Returns the item with its recomputed headline rating; fires `collection.entry.created`.",
31137
+ body: createCollectionEntryInputSchema,
31138
+ status: 201,
31139
+ response: { description: "The updated item detail." },
31140
+ errors: { 400: true, 404: true },
31141
+ webhook: "collection.entry.created"
31142
+ },
31143
+ editEntry: {
31144
+ method: "patch",
31145
+ path: "/collection/{id}/entries/{entryId}",
31146
+ pathParams: entryParams,
31147
+ scopes: WRITE2,
31148
+ tag: "Collection",
31149
+ summary: "Edit a collection entry",
31150
+ description: "Fix an Entry's day, rating or note (`null` clears the rating or note); returns the item with its recomputed headline rating; fires `collection.entry.updated`.",
31151
+ body: updateCollectionEntryInputSchema,
31152
+ status: 200,
31153
+ response: { description: "The updated item detail." },
31154
+ errors: { 400: true, 404: true },
31155
+ webhook: "collection.entry.updated"
31156
+ },
31157
+ removeEntry: {
31158
+ method: "delete",
31159
+ path: "/collection/{id}/entries/{entryId}",
31160
+ pathParams: entryParams,
31161
+ scopes: WRITE2,
31162
+ tag: "Collection",
31163
+ summary: "Delete an entry",
31164
+ description: "Remove one Entry; fires `collection.entry.deleted`.",
31165
+ status: 204,
31166
+ response: { description: "Deleted." },
31167
+ errors: { 404: true },
31168
+ webhook: "collection.entry.deleted"
31169
+ }
31170
+ });
31171
+
30740
31172
  // ../../packages/schema/src/operations/collection-upkeep.ts
30741
31173
  var collectionUpkeepOperations = defineOperations({
30742
31174
  list: {
@@ -30776,6 +31208,34 @@ var collectionUpkeepOperations = defineOperations({
30776
31208
  }
30777
31209
  });
30778
31210
 
31211
+ // ../../packages/schema/src/operations/comments.ts
31212
+ var commentOperations = defineOperations({
31213
+ list: {
31214
+ method: "get",
31215
+ path: "/comments",
31216
+ scopes: ["comments:read"],
31217
+ tag: "Collaboration",
31218
+ summary: "List a node's comments",
31219
+ description: "A node's comment thread (`?nodeId=`).",
31220
+ query: [{ name: "nodeId", description: "The node's ULID.", required: true }],
31221
+ status: 200,
31222
+ response: { description: "The node's comments." }
31223
+ },
31224
+ create: {
31225
+ method: "post",
31226
+ path: "/comments",
31227
+ scopes: ["comments:write"],
31228
+ tag: "Collaboration",
31229
+ summary: "Post a comment",
31230
+ description: "Add a comment to a node's thread; fires `comment.created`.",
31231
+ body: createCommentInputSchema,
31232
+ status: 201,
31233
+ response: { description: "The created comment." },
31234
+ errors: { 400: true },
31235
+ webhook: "comment.created"
31236
+ }
31237
+ });
31238
+
30779
31239
  // ../../packages/schema/src/operations/days.ts
30780
31240
  var pathParams = {
30781
31241
  date: {
@@ -31629,15 +32089,15 @@ var habitOperations = defineOperations({
31629
32089
  });
31630
32090
 
31631
32091
  // ../../packages/schema/src/operations/house.ts
31632
- var READ2 = ["collections:read"];
31633
- var WRITE2 = ["collections:write"];
32092
+ var READ3 = ["collections:read"];
32093
+ var WRITE3 = ["collections:write"];
31634
32094
  var WRITE_WITH_TASKS = ["collections:write", "tasks:write"];
31635
32095
  var receipt = "Returns `{ item, receipt }`: the asset now, and a receipt naming everything made or changed (`itemId`, `created`, `before`, `taskIds`, `entryIds`, `files`). Post the receipt to `/api/v1/house/undo` to take it all back.";
31636
32096
  var houseOperations = defineOperations({
31637
32097
  overview: {
31638
32098
  method: "get",
31639
32099
  path: "/house",
31640
- scopes: READ2,
32100
+ scopes: READ3,
31641
32101
  tag: "House",
31642
32102
  summary: "The House home",
31643
32103
  description: "What's broken (assets in condition `broken` or with an open repair Task), upkeep coming up (open upkeep Tasks with the day each is next due, soonest first), the top-level Places with how many things live in each, and `thingCount` / `upkeepCount` / `toolCount` / `animalCount` (the animals living here, groups counted once).",
@@ -31647,7 +32107,7 @@ var houseOperations = defineOperations({
31647
32107
  value: {
31648
32108
  method: "get",
31649
32109
  path: "/house/value",
31650
- scopes: READ2,
32110
+ scopes: READ3,
31651
32111
  tag: "House",
31652
32112
  summary: "The value inventory",
31653
32113
  description: "What's in the house, for the contents insurance and a claim: every owned asset with its Place, `bought` (purchase, else install day), `paid`, `now` (+ `nowSource`), `proof` (`receipt_photo` \xB7 `receipt` \xB7 `photo` \xB7 `none`: a receipt/warranty Document that `covers` it or a non-image File on it; an image File on it or its cover), `excluded` (`insure: false`, say a borrowed thing: listed, not summed) and `visibility`. `totals` sums now and paid; `policy` is the active home insurance Document with an `insuredValue`, or null. Another member's Only me things never show.",
@@ -31657,7 +32117,7 @@ var houseOperations = defineOperations({
31657
32117
  year: {
31658
32118
  method: "get",
31659
32119
  path: "/house/upkeep",
31660
- scopes: READ2,
32120
+ scopes: READ3,
31661
32121
  tag: "House",
31662
32122
  summary: "The upkeep year",
31663
32123
  description: "Every upkeep routine (a Task tied to an asset as `maintains`) across one year: `routines[].months` holds twelve marks, January first \u2014 `done` (an occurrence done, or a `service` Entry for the Task), `late` (skipped or slipped past), `due` (this month, not done), `planned` (coming, projected from the rule past what's materialized) or null. `needs` lists the Supply parts that fit an asset with a routine in `needsMonth` (next month), with `low` at or below the restock level; `spent` sums this year's costs on asset Entries (repairs + faults, services).",
@@ -31672,7 +32132,7 @@ var houseOperations = defineOperations({
31672
32132
  asset: {
31673
32133
  method: "get",
31674
32134
  path: "/house/assets/{id}",
31675
- scopes: READ2,
32135
+ scopes: READ3,
31676
32136
  tag: "House",
31677
32137
  summary: "One asset's House page",
31678
32138
  description: "The asset as a House page: `item` (the Item with its Entries), `trail` (the Places it sits in, outermost first), `parts` (the Supply items that fit it, kind `fits`), and `linked` (every other node tied to it either way \u2014 a Document that `covers` it, a Task, an Event \u2014 plus the Contacts on its Entries as `serviced_by`), `warrantyUntil` (the latest warranty end among the warranty papers and receipts that cover it, or null; upkeep and repair Tasks are read from `/api/v1/collection/{id}/upkeep`). Only me rows of other members never show.",
@@ -31683,7 +32143,7 @@ var houseOperations = defineOperations({
31683
32143
  linkPart: {
31684
32144
  method: "post",
31685
32145
  path: "/house/assets/{id}/parts",
31686
- scopes: WRITE2,
32146
+ scopes: WRITE3,
31687
32147
  tag: "House",
31688
32148
  summary: "Tie a part to an asset",
31689
32149
  description: "Tie a Supply item to the asset as a part that fits it (a filter, a door rope, a spare battery). Idempotent. Returns the asset page.",
@@ -31696,7 +32156,7 @@ var houseOperations = defineOperations({
31696
32156
  method: "delete",
31697
32157
  path: "/house/assets/{id}/parts/{inventoryItemId}",
31698
32158
  pathParams: { inventoryItemId: "The Supply item's ULID." },
31699
- scopes: WRITE2,
32159
+ scopes: WRITE3,
31700
32160
  tag: "House",
31701
32161
  summary: "Untie a part from an asset",
31702
32162
  description: "The Supply item stays; only the link goes. Returns the asset page.",
@@ -31707,7 +32167,7 @@ var houseOperations = defineOperations({
31707
32167
  lend: {
31708
32168
  method: "post",
31709
32169
  path: "/house/assets/{id}/lend",
31710
- scopes: WRITE2,
32170
+ scopes: WRITE3,
31711
32171
  tag: "House",
31712
32172
  summary: "Lend a thing, or note one borrowed (asset_lend)",
31713
32173
  description: "Sets the Item's `loan` \u2014 `direction` `lent` (to someone) or `borrowed` (from them), `who`, an optional `contactId`, `since` (default today), `dueBack`, `note` \u2014 and logs a `lent` Entry. Works on any Collection Item. 409 while it's already out.",
@@ -31719,7 +32179,7 @@ var houseOperations = defineOperations({
31719
32179
  giveBack: {
31720
32180
  method: "post",
31721
32181
  path: "/house/assets/{id}/return",
31722
- scopes: WRITE2,
32182
+ scopes: WRITE3,
31723
32183
  tag: "House",
31724
32184
  summary: "It's back (asset_return)",
31725
32185
  description: "Clears the Item's `loan` and logs a `returned` Entry (`on` default today, optional `note`). 409 when it isn't out.",
@@ -31796,6 +32256,73 @@ var inventoryItemOperations = defineOperations({
31796
32256
  })
31797
32257
  });
31798
32258
 
32259
+ // ../../packages/schema/src/operations/invites.ts
32260
+ var inviteOperations = defineOperations({
32261
+ list: {
32262
+ method: "get",
32263
+ path: "/invites",
32264
+ scopes: ["members:read"],
32265
+ tag: "Collaboration",
32266
+ summary: "List invites",
32267
+ description: "The Space's pending invitations.",
32268
+ status: 200,
32269
+ response: { description: "The invites." }
32270
+ },
32271
+ create: {
32272
+ method: "post",
32273
+ path: "/invites",
32274
+ scopes: ["members:write"],
32275
+ tag: "Collaboration",
32276
+ summary: "Create an invite",
32277
+ description: "Invite someone to the Space; fires `invite.created`.",
32278
+ body: createInviteInputSchema,
32279
+ status: 201,
32280
+ response: { description: "The created invite." },
32281
+ errors: { 400: true },
32282
+ webhook: "invite.created"
32283
+ },
32284
+ revoke: {
32285
+ method: "delete",
32286
+ path: "/invites/{id}",
32287
+ scopes: ["members:write"],
32288
+ tag: "Collaboration",
32289
+ summary: "Revoke an invite",
32290
+ description: "Revoke a pending invitation; fires `invite.revoked`.",
32291
+ status: 204,
32292
+ response: { description: "Revoked." },
32293
+ errors: { 404: true },
32294
+ webhook: "invite.revoked"
32295
+ }
32296
+ });
32297
+
32298
+ // ../../packages/schema/src/operations/keys.ts
32299
+ var keyOperations = defineOperations({
32300
+ list: {
32301
+ method: "get",
32302
+ path: "/keys",
32303
+ scopes: ["keys:read"],
32304
+ tag: "Identity",
32305
+ summary: "List your API keys",
32306
+ description: "The keys the member behind this key minted (never a secret), newest first, each with its recorded changes.",
32307
+ status: 200,
32308
+ response: { description: "The keys." },
32309
+ errors: { 403: true }
32310
+ },
32311
+ update: {
32312
+ method: "patch",
32313
+ path: "/keys/{id}",
32314
+ scopes: ["keys:write"],
32315
+ tag: "Identity",
32316
+ summary: "Change a key's name or scopes",
32317
+ description: "In place: the key keeps its id and secret. Only the member's own keys, and never more than the calling key holds: it can't widen its own scopes or change a key that reaches further (403). `private:*` only rides on `*`. Recorded on the key, before \u2192 after.",
32318
+ body: updateApiKeyInputSchema,
32319
+ status: 200,
32320
+ response: { description: "The key, changed." },
32321
+ errors: { 400: true, 403: true, 404: true },
32322
+ throttle: "strict"
32323
+ }
32324
+ });
32325
+
31799
32326
  // ../../packages/schema/src/operations/mail-campaigns.ts
31800
32327
  var pathParams2 = { id: "The campaign's ULID." };
31801
32328
  var mailCampaignOperations = defineOperations({
@@ -31878,6 +32405,45 @@ var mealPlanOperations = defineOperations({
31878
32405
  })
31879
32406
  });
31880
32407
 
32408
+ // ../../packages/schema/src/operations/members.ts
32409
+ var memberOperations = defineOperations({
32410
+ list: {
32411
+ method: "get",
32412
+ path: "/members",
32413
+ scopes: ["members:read"],
32414
+ tag: "Collaboration",
32415
+ summary: "List members",
32416
+ description: "The Space roster.",
32417
+ status: 200,
32418
+ response: { description: "The members." }
32419
+ },
32420
+ setRole: {
32421
+ method: "patch",
32422
+ path: "/members/{id}/role",
32423
+ scopes: ["members:write"],
32424
+ tag: "Collaboration",
32425
+ summary: "Set a member's role",
32426
+ description: "Change a member's role; fires `member.role.updated`.",
32427
+ body: setMemberRoleInputSchema,
32428
+ status: 200,
32429
+ response: { description: "The updated member." },
32430
+ errors: { 400: true, 404: true },
32431
+ webhook: "member.role.updated"
32432
+ },
32433
+ remove: {
32434
+ method: "delete",
32435
+ path: "/members/{id}",
32436
+ scopes: ["members:write"],
32437
+ tag: "Collaboration",
32438
+ summary: "Remove a member",
32439
+ description: "Remove a member from the Space; fires `member.removed`.",
32440
+ status: 204,
32441
+ response: { description: "Removed." },
32442
+ errors: { 404: true },
32443
+ webhook: "member.removed"
32444
+ }
32445
+ });
32446
+
31881
32447
  // ../../packages/schema/src/operations/metrics.ts
31882
32448
  var NO_MEMBER2 = "The key acts for no member.";
31883
32449
  var metricOperations = defineOperations({
@@ -31944,6 +32510,51 @@ var metricOperations = defineOperations({
31944
32510
  }
31945
32511
  });
31946
32512
 
32513
+ // ../../packages/schema/src/operations/nodes.ts
32514
+ var pathParams3 = { id: "The owner node's ULID (any node type)." };
32515
+ var ineligible = "The node is unknown, deleted, hidden from this Space, or can't carry a note (a `note` or `event` node).";
32516
+ var nodeOperations = defineOperations({
32517
+ getNotes: {
32518
+ method: "get",
32519
+ path: "/nodes/{id}/notes",
32520
+ pathParams: pathParams3,
32521
+ scopes: ["notes:read"],
32522
+ tag: "Notes",
32523
+ summary: "Get a node's attached note",
32524
+ description: "The attached note of any node as `{ nodeId, noteId, body, updatedAt }`; a node with no note yet returns nulls.",
32525
+ status: 200,
32526
+ response: { description: "The attached-note view." },
32527
+ errors: { 404: ineligible }
32528
+ },
32529
+ setNotes: {
32530
+ method: "put",
32531
+ path: "/nodes/{id}/notes",
32532
+ pathParams: pathParams3,
32533
+ scopes: ["notes:write"],
32534
+ tag: "Notes",
32535
+ summary: "Set a node's attached note",
32536
+ description: "Upsert the node's attached note and return the attached-note view; fires `note.created` when this creates the note, else `note.updated`, with the Note's standard view. A blank body clears the note (fires `note.deleted` if one existed).",
32537
+ body: setAttachedNoteInputSchema,
32538
+ status: 200,
32539
+ response: { description: "The attached-note view." },
32540
+ errors: { 400: true, 404: ineligible },
32541
+ webhook: "note.updated"
32542
+ },
32543
+ clearNotes: {
32544
+ method: "delete",
32545
+ path: "/nodes/{id}/notes",
32546
+ pathParams: pathParams3,
32547
+ scopes: ["notes:write"],
32548
+ tag: "Notes",
32549
+ summary: "Clear a node's attached note",
32550
+ description: "Remove the node's attached note; fires `note.deleted` with the cleared note's id. Idempotent \u2014 a node with no note is a no-op.",
32551
+ status: 204,
32552
+ response: { description: "Cleared." },
32553
+ errors: { 404: ineligible },
32554
+ webhook: "note.deleted"
32555
+ }
32556
+ });
32557
+
31947
32558
  // ../../packages/schema/src/operations/occurrences.ts
31948
32559
  var occurrenceOperations = defineOperations({
31949
32560
  list: {
@@ -31986,6 +32597,78 @@ var occurrenceOperations = defineOperations({
31986
32597
  }
31987
32598
  });
31988
32599
 
32600
+ // ../../packages/schema/src/operations/orca.ts
32601
+ var host = "The Orca host (e.g. `theseus`).";
32602
+ var handle = "The session's handle (`term_\u2026`).";
32603
+ var notConnected = "Orca isn't connected to this Space, or the bridge doesn't know that host or session.";
32604
+ var unavailable = "Orca unreachable: the orca-bridge didn't answer.";
32605
+ var orcaOperations = defineOperations({
32606
+ overview: {
32607
+ method: "get",
32608
+ path: "/orca",
32609
+ scopes: ["orca:read"],
32610
+ tag: "Orca",
32611
+ summary: "List Orca worktrees",
32612
+ description: "Every host's worktrees with status (`running` / `idle` / `waiting`), the last line, sessions and the linked Task or Project, plus linked worktrees that have ended; `state` is `ok`, `unreachable` or `not_configured`.",
32613
+ status: 200,
32614
+ response: { description: "The Orca overview." }
32615
+ },
32616
+ output: {
32617
+ method: "get",
32618
+ path: "/orca/hosts/{host}/terminals/{handle}/output",
32619
+ pathParams: { host, handle },
32620
+ scopes: ["orca:read"],
32621
+ tag: "Orca",
32622
+ summary: "Read an Orca session",
32623
+ description: "A session's live output; pass `nextCursor` back as `cursor` for only what's new, or `screen=true` for the rendered frame.",
32624
+ query: [
32625
+ { name: "cursor", description: "Read from this cursor (a prior `nextCursor`)." },
32626
+ { name: "limit", description: "At most this many lines (1\u20132000).", type: "integer" },
32627
+ { name: "screen", description: "`true` reads the rendered screen instead of the stream." }
32628
+ ],
32629
+ querySchema: orcaReadOutputQuerySchema,
32630
+ status: 200,
32631
+ response: { description: "The output." },
32632
+ errors: { 400: true, 404: notConnected, 503: unavailable }
32633
+ },
32634
+ send: {
32635
+ method: "post",
32636
+ path: "/orca/hosts/{host}/terminals/{handle}/messages",
32637
+ pathParams: { host, handle },
32638
+ scopes: ["orca:write"],
32639
+ tag: "Orca",
32640
+ summary: "Send to an Orca session",
32641
+ description: "Type a message into a session (`enter` submits it, default true). An Agent's send waits for the member: a 428 `confirmation_required` until they confirm it in the agent panel, which replays it.",
32642
+ body: orcaSendMessageInputSchema,
32643
+ status: 200,
32644
+ response: { description: "Orca accepted the input." },
32645
+ errors: {
32646
+ 400: true,
32647
+ 404: notConnected,
32648
+ 428: "`confirmation_required`: the member must confirm an Agent's send.",
32649
+ 503: unavailable
32650
+ }
32651
+ },
32652
+ restart: {
32653
+ method: "post",
32654
+ path: "/orca/hosts/{host}/sessions",
32655
+ pathParams: { host },
32656
+ scopes: ["orca:write"],
32657
+ tag: "Orca",
32658
+ summary: "Restart an Orca session",
32659
+ description: "Start the agent afresh in a worktree: a new session running `claude` or `codex` (an allowlist, never a free command); the old session is left as it was. Gated like a send: an Agent's restart waits for the member's confirmation (428 `confirmation_required`).",
32660
+ body: orcaRestartSessionInputSchema,
32661
+ status: 200,
32662
+ response: { description: "Orca started the session; its new `handle`." },
32663
+ errors: {
32664
+ 400: true,
32665
+ 404: notConnected,
32666
+ 428: "`confirmation_required`: the member must confirm an Agent's restart.",
32667
+ 503: unavailable
32668
+ }
32669
+ }
32670
+ });
32671
+
31989
32672
  // ../../packages/schema/src/operations/periods.ts
31990
32673
  var periodOperations = defineOperations({
31991
32674
  ...crudOperations({
@@ -32249,6 +32932,149 @@ var progressOperations = defineOperations({
32249
32932
  }
32250
32933
  });
32251
32934
 
32935
+ // ../../packages/schema/src/operations/projects.ts
32936
+ var INSTANT = "an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight)";
32937
+ var projectOperations = defineOperations({
32938
+ ...crudOperations({
32939
+ segment: "projects",
32940
+ tag: "Projects",
32941
+ noun: "project",
32942
+ read: "projects:read",
32943
+ write: "projects:write",
32944
+ create: createProjectInputSchema,
32945
+ update: updateProjectInputSchema,
32946
+ paginated: true,
32947
+ listQuery: [
32948
+ { name: "areaId", description: "Only rows in this Area (ULID)." },
32949
+ {
32950
+ name: "status",
32951
+ description: `Only rows in any of these statuses (comma list or repeated key): ${projectStatusSchema.options.map((v) => `\`${v}\``).join(", ")}.`
32952
+ },
32953
+ {
32954
+ name: "updatedSince",
32955
+ description: `Only rows whose \`updatedAt\` is at or after this instant \u2014 ${INSTANT}.`,
32956
+ format: "date-time"
32957
+ },
32958
+ {
32959
+ name: "dueBefore",
32960
+ description: `Only rows due strictly before this instant (undated rows are excluded) \u2014 ${INSTANT}.`,
32961
+ format: "date-time"
32962
+ }
32963
+ ],
32964
+ listQuerySchema: projectListQuerySchema.extend(paginationQuerySchema.shape)
32965
+ }),
32966
+ attachTask: {
32967
+ method: "post",
32968
+ path: "/projects/{id}/tasks",
32969
+ scopes: ["projects:write"],
32970
+ tag: "Projects",
32971
+ summary: "Attach a task",
32972
+ description: "Attach an existing task to the project; fires `project.task.attached`.",
32973
+ body: attachTaskInputSchema,
32974
+ status: 204,
32975
+ response: { description: "Attached." },
32976
+ errors: { 400: true, 404: true },
32977
+ webhook: "project.task.attached"
32978
+ },
32979
+ detachTask: {
32980
+ method: "delete",
32981
+ path: "/projects/{id}/tasks/{taskId}",
32982
+ pathParams: { taskId: "The task's ULID." },
32983
+ scopes: ["projects:write"],
32984
+ tag: "Projects",
32985
+ summary: "Detach a task",
32986
+ description: "Detach a task from the project; fires `project.task.detached`.",
32987
+ status: 204,
32988
+ response: { description: "Detached." },
32989
+ errors: { 404: true },
32990
+ webhook: "project.task.detached"
32991
+ }
32992
+ });
32993
+
32994
+ // ../../packages/schema/src/operations/reads.ts
32995
+ var INSTANT2 = "an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight)";
32996
+ var windowParams = [
32997
+ {
32998
+ name: "from",
32999
+ description: `Window start \u2014 ${INSTANT2}. Default: today's start, UTC.`,
33000
+ format: "date-time"
33001
+ },
33002
+ {
33003
+ name: "to",
33004
+ description: `Window end (exclusive) \u2014 ${INSTANT2}. Default: a day after \`from\`.`,
33005
+ format: "date-time"
33006
+ }
33007
+ ];
33008
+ var readOperations = defineOperations({
33009
+ now: {
33010
+ method: "get",
33011
+ path: "/now",
33012
+ scopes: ["tasks:read"],
33013
+ tag: "Reads",
33014
+ summary: "What needs me now",
33015
+ description: "The open tasks waiting on `for` (default `me`, the key's own member or Agent), longest-waiting first and leaving out ones snoozed past now, plus the window's timeline (scheduled, due and overdue \u2014 the app's Today).",
33016
+ query: [
33017
+ {
33018
+ name: "for",
33019
+ description: "Whose waiting list: `me` (default), `member:<id|email>` or `agent:<id|name>`."
33020
+ },
33021
+ ...windowParams
33022
+ ],
33023
+ querySchema: nowQuerySchema,
33024
+ status: 200,
33025
+ response: { description: "The Now read.", view: "NowResponse" },
33026
+ errors: { 400: true },
33027
+ operationId: "get_now"
33028
+ },
33029
+ landed: {
33030
+ method: "get",
33031
+ path: "/landed",
33032
+ scopes: ["tasks:read"],
33033
+ tag: "Reads",
33034
+ summary: "What landed",
33035
+ description: "What got done in the window by `completedAt` (cancelled tasks never count): the total, a per-Actor split (who finished each \u2014 a member or an Agent), and the tasks, most recently finished first.",
33036
+ query: windowParams,
33037
+ querySchema: readWindowQuerySchema,
33038
+ status: 200,
33039
+ response: { description: "The Landed read.", view: "LandedResponse" },
33040
+ operationId: "get_landed"
33041
+ },
33042
+ review: {
33043
+ method: "get",
33044
+ path: "/review",
33045
+ scopes: ["tasks:read"],
33046
+ tag: "Reads",
33047
+ summary: "Review tallies",
33048
+ description: "Per-day completion tallies over the window: tasks (by `completedAt`) and habit/routine occurrences.",
33049
+ query: windowParams,
33050
+ querySchema: readWindowQuerySchema,
33051
+ status: 200,
33052
+ response: { description: "One entry per day of the window." },
33053
+ operationId: "get_review"
33054
+ },
33055
+ search: {
33056
+ method: "get",
33057
+ path: "/search",
33058
+ scopes: [],
33059
+ tag: "Reads",
33060
+ summary: "Search the Space",
33061
+ description: "Case-insensitive search over live node titles and note bodies; title hits first, then newest. No scope of its own: a key sees the node types its `*:read` scopes cover (`tasks:read` \u2192 `task`, `notes:read` \u2192 `note`, \u2026 a full-access key sees all), and gets a 403 when it covers none of the requested types.",
33062
+ query: [
33063
+ { name: "q", description: "The text to find (1\u2013200 chars)." },
33064
+ {
33065
+ name: "type",
33066
+ description: "Only these node types (comma list): `task`, `project`, `area`, `note`, `habit`, `routine`, `contact`, `collection_item`."
33067
+ },
33068
+ { name: "limit", description: "Max hits, 1\u201350 (default 20).", type: "integer", minimum: 1 }
33069
+ ],
33070
+ querySchema: searchQuerySchema,
33071
+ status: 200,
33072
+ response: { description: "The hits.", list: true, view: "SearchHit" },
33073
+ errors: { 400: true, 403: "The key can read none of the requested node types." },
33074
+ operationId: "search"
33075
+ }
33076
+ });
33077
+
32252
33078
  // ../../packages/schema/src/operations/recipes.ts
32253
33079
  var recipe = { id: "The recipe's ULID." };
32254
33080
  var recipeOperations = defineOperations({
@@ -32303,6 +33129,40 @@ var recipeOperations = defineOperations({
32303
33129
  }
32304
33130
  });
32305
33131
 
33132
+ // ../../packages/schema/src/operations/roles.ts
33133
+ var roleOperations = defineOperations({
33134
+ listPermissionSets: {
33135
+ method: "get",
33136
+ path: "/roles/permission-sets",
33137
+ scopes: ["roles:read"],
33138
+ tag: "Collaboration",
33139
+ summary: "List permission sets",
33140
+ description: "The Space's permission sets.",
33141
+ status: 200,
33142
+ response: { description: "The permission sets." }
33143
+ },
33144
+ listMembers: {
33145
+ method: "get",
33146
+ path: "/roles/members",
33147
+ scopes: ["roles:read"],
33148
+ tag: "Collaboration",
33149
+ summary: "List role assignments",
33150
+ description: "Each member's role assignment.",
33151
+ status: 200,
33152
+ response: { description: "The role assignments." }
33153
+ },
33154
+ list: {
33155
+ method: "get",
33156
+ path: "/roles",
33157
+ scopes: ["roles:read"],
33158
+ tag: "Collaboration",
33159
+ summary: "List roles",
33160
+ description: "The Space's roles.",
33161
+ status: 200,
33162
+ response: { description: "The roles." }
33163
+ }
33164
+ });
33165
+
32306
33166
  // ../../packages/schema/src/operations/routines.ts
32307
33167
  var routineOperations = defineOperations({
32308
33168
  getRun: {
@@ -32621,6 +33481,210 @@ var rsvpQuestionOperations = defineOperations({
32621
33481
  }
32622
33482
  });
32623
33483
 
33484
+ // ../../packages/schema/src/operations/runs.ts
33485
+ var READ4 = ["collections:read"];
33486
+ var WRITE4 = ["collections:write"];
33487
+ var runOperations = defineOperations({
33488
+ list: {
33489
+ method: "get",
33490
+ path: "/runs",
33491
+ scopes: READ4,
33492
+ tag: "Runs",
33493
+ summary: "List game runs",
33494
+ description: "The Space's runs, last touched first, each with its game, its facets, `lastPlayedOn` and `sessionCount`.",
33495
+ query: [
33496
+ { name: "gameId", description: "Only the runs of this game (a Collection Item id)." },
33497
+ {
33498
+ name: "status",
33499
+ description: "`planned`, `playing`, `paused`, `blocked`, `finished` or `dropped`."
33500
+ },
33501
+ {
33502
+ name: "theme",
33503
+ description: "Only runs skinned by this theme pack (`skyrim`, `pokemon`, \u2026)."
33504
+ }
33505
+ ],
33506
+ querySchema: runsQuerySchema,
33507
+ status: 200,
33508
+ response: { description: "The runs." }
33509
+ },
33510
+ create: {
33511
+ method: "post",
33512
+ path: "/runs",
33513
+ scopes: WRITE4,
33514
+ tag: "Runs",
33515
+ summary: "Start a game run",
33516
+ description: "A playthrough under a game Item (`gameId`, 400 when it isn't a game). The client mints `id`; `status` defaults to `playing`; `facet` holds the sections (character, arc, orderOfPlay, quests, nextSteps, choices, homes, people, mounts, team, checklists, uniques, achievements, install). Fires `run.created`.",
33517
+ body: createRunInputSchema,
33518
+ status: 201,
33519
+ response: { description: "The run." },
33520
+ errors: { 400: true, 404: true },
33521
+ webhook: "run.created"
33522
+ },
33523
+ steamStatus: {
33524
+ method: "get",
33525
+ path: "/runs/steam",
33526
+ scopes: READ4,
33527
+ tag: "Runs",
33528
+ summary: "Get the Steam achievements link",
33529
+ description: "Whether the key's member has linked Steam (from Settings in the app; the Steam ID and Web API key are never returned), how many games carry a `steamAppId` and `lastSyncedAt`, the newest sync on a run. 403 for a key no member minted.",
33530
+ status: 200,
33531
+ response: { description: "The link's status." },
33532
+ errors: { 403: true }
33533
+ },
33534
+ steamSync: {
33535
+ method: "post",
33536
+ path: "/runs/steam/sync",
33537
+ scopes: WRITE4,
33538
+ tag: "Runs",
33539
+ summary: "Sync Steam achievements now",
33540
+ description: "As the key's member, read Steam's achievements for every game with a `steamAppId` (or only `gameId`) and write them into each run of that game whose achievements come from Steam or aren't kept yet (`source: steam`, `total`, `unlocked` with the day, `locked`, `syncedAt`); runs kept by hand are left alone. The daily sync does the same. Per game: outcome (ok \xB7 failed), runs written, unlocked, total and the error (`unauthorized`, `private`, `no_stats`, `unavailable`, `bad_response`). 409 before the member has linked Steam.",
33541
+ body: steamSyncInputSchema,
33542
+ status: 200,
33543
+ response: { description: "What the sync did, game by game." },
33544
+ errors: { 400: true, 403: true, 409: true }
33545
+ },
33546
+ get: {
33547
+ method: "get",
33548
+ path: "/runs/{id}",
33549
+ scopes: READ4,
33550
+ tag: "Runs",
33551
+ summary: "Get a game run",
33552
+ description: "The run with its sessions, newest first; each session has its recap (`note`), `minutes` and the agent's change `batches`.",
33553
+ status: 200,
33554
+ response: { description: "The run." },
33555
+ errors: { 404: true }
33556
+ },
33557
+ update: {
33558
+ method: "patch",
33559
+ path: "/runs/{id}",
33560
+ scopes: WRITE4,
33561
+ tag: "Runs",
33562
+ summary: "Edit a game run",
33563
+ description: "Rename it, move its status, or replace `facet` whole. For one change at a time use `/changes`. Fires `run.updated`.",
33564
+ body: updateRunInputSchema,
33565
+ status: 200,
33566
+ response: { description: "The run." },
33567
+ errors: { 400: true, 404: true },
33568
+ webhook: "run.updated"
33569
+ },
33570
+ remove: {
33571
+ method: "delete",
33572
+ path: "/runs/{id}",
33573
+ scopes: WRITE4,
33574
+ tag: "Runs",
33575
+ summary: "Delete a game run",
33576
+ description: "Soft-deletes the run. Fires `run.deleted`.",
33577
+ status: 204,
33578
+ response: { description: "Deleted." },
33579
+ errors: { 404: true },
33580
+ webhook: "run.deleted"
33581
+ },
33582
+ applyChanges: {
33583
+ method: "post",
33584
+ path: "/runs/{id}/changes",
33585
+ scopes: WRITE4,
33586
+ tag: "Runs",
33587
+ summary: "Apply a run chat update",
33588
+ description: 'What the agent sends for "<game> update: \u2026": `changes`, each `{ op: "set"|"merge", section, value }` for a one-value section, `{ op: "upsert", section, value }` / `{ op: "remove", section, match }` for a list section (matched by its name field, case ignored), or `{ op: "tick", section: "checklists", key, ids, done? }` to tick many checklist ids at once; `message` is quoted on the receipt. The batch goes on `sessionId`, else the last session if it was active in the last 6 hours, else a new one (`newSession` forces one); `recap` writes the session\'s recap. A change that leaves the run invalid is a 400 and nothing is written. Returns `{ run, sessionId, batch }`, the batch\'s `changes` carrying one receipt `label` each. Fires `run.updated`.',
33589
+ body: applyRunChangesInputSchema,
33590
+ status: 201,
33591
+ response: { description: "`{ run, sessionId, batch }`." },
33592
+ errors: { 400: true, 404: true },
33593
+ webhook: "run.updated"
33594
+ },
33595
+ undo: {
33596
+ method: "post",
33597
+ path: "/runs/{id}/undo",
33598
+ scopes: WRITE4,
33599
+ tag: "Runs",
33600
+ summary: "Undo a run chat update",
33601
+ description: "Puts the sections a batch changed back as they were and marks it undone: `batchId`, else the latest batch not yet undone (404 when there is none; 400 when it was already undone). Returns the run; fires `run.updated`.",
33602
+ body: undoRunChangesInputSchema,
33603
+ status: 200,
33604
+ response: { description: "The run." },
33605
+ errors: { 400: true, 404: true },
33606
+ webhook: "run.updated"
33607
+ },
33608
+ uploadSaveHeader: {
33609
+ method: "post",
33610
+ path: "/runs/{id}/save-header",
33611
+ scopes: WRITE4,
33612
+ tag: "Runs",
33613
+ summary: "Send a game save's header to a run",
33614
+ description: "The save sync (run tracker phase 4): the header of the newest save as `@do/skyrim-save` reads it on the gaming PC (name, level, location, race, sex, play time, saved at, the in-game date, plugins, General Stats) and its screenshot as a base64 PNG. It becomes the run's `save` section and its character's level, as one update with a receipt and Undo; the screenshot is a file on the run (`source: game_save`), replacing the last save's. The same save again (number, file and time) changes nothing: `unchanged: true`. Needs a member's key. Returns `{ run, unchanged, sessionId, batch }`; fires `run.updated`.",
33615
+ body: uploadRunSaveHeaderInputSchema,
33616
+ status: 200,
33617
+ response: { description: "`{ run, unchanged, sessionId, batch }`." },
33618
+ errors: { 400: true, 403: "A read-only key, or a key with no member.", 404: true },
33619
+ webhook: "run.updated"
33620
+ },
33621
+ tickChecklist: {
33622
+ method: "post",
33623
+ path: "/runs/{id}/checklists/{key}/tick",
33624
+ pathParams: { key: "The checklist's key (`dex`, `masks`)." },
33625
+ scopes: WRITE4,
33626
+ tag: "Runs",
33627
+ summary: "Tick a run checklist in bulk",
33628
+ description: "Tick many ids on one checklist in one call, e.g. backfilling a Pok\xE9dex (`ids` are national numbers): `done: false` unticks. A checklist the run doesn't have yet is made from its theme pack's catalog of that key (404 when there's neither). Recorded like a chat update, with a receipt and Undo. Returns `{ run, sessionId, batch }`; fires `run.updated`.",
33629
+ body: tickRunChecklistInputSchema,
33630
+ status: 201,
33631
+ response: { description: "`{ run, sessionId, batch }`." },
33632
+ errors: { 400: true, 404: true },
33633
+ webhook: "run.updated"
33634
+ },
33635
+ listSessions: {
33636
+ method: "get",
33637
+ path: "/runs/{id}/sessions",
33638
+ scopes: READ4,
33639
+ tag: "Runs",
33640
+ summary: "List a run's sessions",
33641
+ description: "Newest first, each with its recap, minutes and change batches.",
33642
+ status: 200,
33643
+ response: { description: "The sessions." },
33644
+ errors: { 404: true }
33645
+ },
33646
+ addSession: {
33647
+ method: "post",
33648
+ path: "/runs/{id}/sessions",
33649
+ scopes: WRITE4,
33650
+ tag: "Runs",
33651
+ summary: "Log a run session",
33652
+ description: "A sitting with the game: `occurredOn` (default today, UTC), `recap` (second person, a few sentences) and `minutes`. Returns the run; fires `run.session.created`.",
33653
+ body: createRunSessionInputSchema,
33654
+ status: 201,
33655
+ response: { description: "The run." },
33656
+ errors: { 400: true, 404: true },
33657
+ webhook: "run.session.created"
33658
+ },
33659
+ updateSession: {
33660
+ method: "patch",
33661
+ path: "/runs/{id}/sessions/{sessionId}",
33662
+ pathParams: { sessionId: "The session's ULID." },
33663
+ scopes: WRITE4,
33664
+ tag: "Runs",
33665
+ summary: "Edit a run session",
33666
+ description: "Fix its day, recap or minutes (`null` clears the recap or minutes). Returns the run; fires `run.session.updated`.",
33667
+ body: updateRunSessionInputSchema,
33668
+ status: 200,
33669
+ response: { description: "The run." },
33670
+ errors: { 400: true, 404: true },
33671
+ webhook: "run.session.updated"
33672
+ },
33673
+ removeSession: {
33674
+ method: "delete",
33675
+ path: "/runs/{id}/sessions/{sessionId}",
33676
+ pathParams: { sessionId: "The session's ULID." },
33677
+ scopes: WRITE4,
33678
+ tag: "Runs",
33679
+ summary: "Delete a run session",
33680
+ description: "Its changes stay applied. Fires `run.session.deleted`.",
33681
+ status: 204,
33682
+ response: { description: "Deleted." },
33683
+ errors: { 404: true },
33684
+ webhook: "run.session.deleted"
33685
+ }
33686
+ });
33687
+
32624
33688
  // ../../packages/schema/src/operations/seating-proposals.ts
32625
33689
  var proposal = { id: "The proposal's ULID." };
32626
33690
  var seatingProposalOperations = defineOperations({
@@ -32823,6 +33887,141 @@ var settingsOperations = defineOperations({
32823
33887
  }
32824
33888
  });
32825
33889
 
33890
+ // ../../packages/schema/src/operations/share-links.ts
33891
+ var shareLinkOperations = defineOperations({
33892
+ listForNode: {
33893
+ method: "get",
33894
+ path: "/share-links/node/{nodeId}",
33895
+ pathParams: { nodeId: "The node's ULID." },
33896
+ scopes: ["links:read"],
33897
+ tag: "Collaboration",
33898
+ summary: "List a node's share-links",
33899
+ description: "Public, scoped links minted for a node.",
33900
+ status: 200,
33901
+ response: { description: "The node's share-links." },
33902
+ errors: { 404: true }
33903
+ },
33904
+ get: {
33905
+ method: "get",
33906
+ path: "/share-links/{id}",
33907
+ scopes: ["links:read"],
33908
+ tag: "Collaboration",
33909
+ summary: "Get a share-link",
33910
+ description: "One share-link by id.",
33911
+ status: 200,
33912
+ response: { description: "The share-link." },
33913
+ errors: { 404: true }
33914
+ },
33915
+ create: {
33916
+ method: "post",
33917
+ path: "/share-links",
33918
+ scopes: ["links:write"],
33919
+ tag: "Collaboration",
33920
+ summary: "Mint a share-link",
33921
+ description: "Mint a public, scoped link to a node; fires `share-link.created`.",
33922
+ body: createShareLinkInputSchema,
33923
+ status: 201,
33924
+ response: { description: "The created share-link." },
33925
+ errors: { 400: true },
33926
+ webhook: "share-link.created"
33927
+ },
33928
+ revoke: {
33929
+ method: "delete",
33930
+ path: "/share-links/{id}",
33931
+ scopes: ["links:write"],
33932
+ tag: "Collaboration",
33933
+ summary: "Revoke a share-link",
33934
+ description: "Revoke a public share-link; fires `share-link.revoked`.",
33935
+ status: 204,
33936
+ response: { description: "Revoked." },
33937
+ errors: { 404: true },
33938
+ webhook: "share-link.revoked"
33939
+ }
33940
+ });
33941
+
33942
+ // ../../packages/schema/src/operations/sharing.ts
33943
+ var nodeId = "The node's ULID.";
33944
+ var grantId = "The grant's ULID.";
33945
+ var sharingOperations = defineOperations({
33946
+ listSubjects: {
33947
+ method: "get",
33948
+ path: "/sharing/subjects",
33949
+ scopes: ["sharing:read"],
33950
+ tag: "Collaboration",
33951
+ summary: "List grantable subjects",
33952
+ description: "The members/roles a node can be shared with.",
33953
+ status: 200,
33954
+ response: { description: "The subjects." }
33955
+ },
33956
+ listGrants: {
33957
+ method: "get",
33958
+ path: "/sharing/nodes/{nodeId}/grants",
33959
+ pathParams: { nodeId },
33960
+ scopes: ["sharing:read"],
33961
+ tag: "Collaboration",
33962
+ summary: "List a node's grants",
33963
+ description: "Who a node is shared with, and at what level.",
33964
+ status: 200,
33965
+ response: { description: "The node's grants." },
33966
+ errors: { 404: true }
33967
+ },
33968
+ createGrant: {
33969
+ method: "post",
33970
+ path: "/sharing/nodes/{nodeId}/grants",
33971
+ pathParams: { nodeId },
33972
+ scopes: ["sharing:write"],
33973
+ tag: "Collaboration",
33974
+ summary: "Grant access to a node",
33975
+ description: "Share a node with a member/role; fires `node.grant.created`.",
33976
+ body: createNodeGrantInputSchema,
33977
+ status: 201,
33978
+ response: { description: "The created grant." },
33979
+ errors: { 400: true, 404: true },
33980
+ webhook: "node.grant.created"
33981
+ },
33982
+ updateGrant: {
33983
+ method: "patch",
33984
+ path: "/sharing/nodes/{nodeId}/grants/{grantId}",
33985
+ pathParams: { nodeId, grantId },
33986
+ scopes: ["sharing:write"],
33987
+ tag: "Collaboration",
33988
+ summary: "Update a grant",
33989
+ description: "Change a grant's access level; fires `node.grant.updated`.",
33990
+ body: updateNodeGrantInputSchema,
33991
+ status: 200,
33992
+ response: { description: "The updated grant." },
33993
+ errors: { 400: true, 404: true },
33994
+ webhook: "node.grant.updated"
33995
+ },
33996
+ revokeGrant: {
33997
+ method: "delete",
33998
+ path: "/sharing/nodes/{nodeId}/grants/{grantId}",
33999
+ pathParams: { nodeId, grantId },
34000
+ scopes: ["sharing:write"],
34001
+ tag: "Collaboration",
34002
+ summary: "Revoke a grant",
34003
+ description: "Revoke a node grant; fires `node.grant.revoked`.",
34004
+ status: 204,
34005
+ response: { description: "Revoked." },
34006
+ errors: { 404: true },
34007
+ webhook: "node.grant.revoked"
34008
+ },
34009
+ assign: {
34010
+ method: "put",
34011
+ path: "/sharing/nodes/{nodeId}/assignee",
34012
+ pathParams: { nodeId },
34013
+ scopes: ["sharing:write"],
34014
+ tag: "Collaboration",
34015
+ summary: "Assign a node",
34016
+ description: "Set a node's assignee; fires `node.assigned`.",
34017
+ body: assignNodeInputSchema,
34018
+ status: 200,
34019
+ response: { description: "The assignment grant." },
34020
+ errors: { 400: true, 404: true },
34021
+ webhook: "node.assigned"
34022
+ }
34023
+ });
34024
+
32826
34025
  // ../../packages/schema/src/operations/shopping-lists.ts
32827
34026
  var shoppingListOperations = defineOperations({
32828
34027
  ...crudOperations({
@@ -32839,14 +34038,14 @@ var shoppingListOperations = defineOperations({
32839
34038
  });
32840
34039
 
32841
34040
  // ../../packages/schema/src/operations/tap-tags.ts
32842
- var READ3 = ["habits:read", "routines:read"];
32843
- var WRITE3 = ["habits:write", "routines:write"];
32844
- var pathParams3 = { id: "The tag's id: the ULID the sticker's link carries (`/t/{id}`)." };
34041
+ var READ5 = ["habits:read", "routines:read"];
34042
+ var WRITE5 = ["habits:write", "routines:write"];
34043
+ var pathParams4 = { id: "The tag's id: the ULID the sticker's link carries (`/t/{id}`)." };
32845
34044
  var tapTagOperations = defineOperations({
32846
34045
  list: {
32847
34046
  method: "get",
32848
34047
  path: "/tap-tags",
32849
- scopes: READ3,
34048
+ scopes: READ5,
32850
34049
  tag: "Habits",
32851
34050
  summary: "List tap tags",
32852
34051
  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`.",
@@ -32857,7 +34056,7 @@ var tapTagOperations = defineOperations({
32857
34056
  create: {
32858
34057
  method: "post",
32859
34058
  path: "/tap-tags",
32860
- scopes: WRITE3,
34059
+ scopes: WRITE5,
32861
34060
  tag: "Habits",
32862
34061
  summary: "Make a tap tag",
32863
34062
  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.",
@@ -32869,8 +34068,8 @@ var tapTagOperations = defineOperations({
32869
34068
  get: {
32870
34069
  method: "get",
32871
34070
  path: "/tap-tags/{id}",
32872
- pathParams: pathParams3,
32873
- scopes: READ3,
34071
+ pathParams: pathParams4,
34072
+ scopes: READ5,
32874
34073
  tag: "Habits",
32875
34074
  summary: "Get a tap tag",
32876
34075
  description: "One tag.",
@@ -32881,8 +34080,8 @@ var tapTagOperations = defineOperations({
32881
34080
  update: {
32882
34081
  method: "patch",
32883
34082
  path: "/tap-tags/{id}",
32884
- pathParams: pathParams3,
32885
- scopes: WRITE3,
34083
+ pathParams: pathParams4,
34084
+ scopes: WRITE5,
32886
34085
  tag: "Habits",
32887
34086
  summary: "Rename or rebind a tap tag",
32888
34087
  description: "Change its `label`, or bind it to another habit or routine (`kind` and `targetId` together): the sticker stays as it is.",
@@ -32894,8 +34093,8 @@ var tapTagOperations = defineOperations({
32894
34093
  remove: {
32895
34094
  method: "delete",
32896
34095
  path: "/tap-tags/{id}",
32897
- pathParams: pathParams3,
32898
- scopes: WRITE3,
34096
+ pathParams: pathParams4,
34097
+ scopes: WRITE5,
32899
34098
  tag: "Habits",
32900
34099
  summary: "Delete a tap tag",
32901
34100
  description: "The sticker's link stops doing anything.",
@@ -32906,8 +34105,8 @@ var tapTagOperations = defineOperations({
32906
34105
  tap: {
32907
34106
  method: "post",
32908
34107
  path: "/tap-tags/{id}/tap",
32909
- pathParams: pathParams3,
32910
- scopes: WRITE3,
34108
+ pathParams: pathParams4,
34109
+ scopes: WRITE5,
32911
34110
  tag: "Habits",
32912
34111
  summary: "Tap a tap tag",
32913
34112
  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.",
@@ -33107,7 +34306,7 @@ var taskOperations = defineOperations({
33107
34306
  });
33108
34307
 
33109
34308
  // ../../packages/schema/src/operations/time-blocks.ts
33110
- var INSTANT = "an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight)";
34309
+ var INSTANT3 = "an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight)";
33111
34310
  var timeBlockOperations = defineOperations({
33112
34311
  ...crudOperations({
33113
34312
  segment: "time-blocks",
@@ -33120,12 +34319,12 @@ var timeBlockOperations = defineOperations({
33120
34319
  listQuery: [
33121
34320
  {
33122
34321
  name: "from",
33123
- description: `Start of the window (inclusive) \u2014 ${INSTANT}. With \`to\`; both default to the next day.`,
34322
+ description: `Start of the window (inclusive) \u2014 ${INSTANT3}. With \`to\`; both default to the next day.`,
33124
34323
  format: "date-time"
33125
34324
  },
33126
34325
  {
33127
34326
  name: "to",
33128
- description: `End of the window (exclusive, at most 62 days after \`from\`) \u2014 ${INSTANT}.`,
34327
+ description: `End of the window (exclusive, at most 62 days after \`from\`) \u2014 ${INSTANT3}.`,
33129
34328
  format: "date-time"
33130
34329
  },
33131
34330
  { name: "kind", description: "Only blocks of this kind: `task`, `focus` or `life`." }
@@ -33268,6 +34467,112 @@ var webhookOperations = defineOperations({
33268
34467
  }
33269
34468
  });
33270
34469
 
34470
+ // ../../packages/schema/src/operations/whoami.ts
34471
+ var whoamiOperations = defineOperations({
34472
+ whoami: {
34473
+ method: "get",
34474
+ path: "/whoami",
34475
+ scopes: [],
34476
+ tag: "Identity",
34477
+ summary: "Identity ping",
34478
+ description: "What the presented key resolved to (tenant + scopes), and the Agent it acts as when it is an Agent's key (`agentId`, else null).",
34479
+ status: 200,
34480
+ response: { description: "The resolved API-key identity." },
34481
+ operationId: "whoami"
34482
+ }
34483
+ });
34484
+
34485
+ // ../../packages/schema/src/operations/withings.ts
34486
+ var withingsOperations = defineOperations({
34487
+ status: {
34488
+ method: "get",
34489
+ path: "/health/withings",
34490
+ scopes: ["health:read"],
34491
+ tag: "Health",
34492
+ summary: "Get the Withings link",
34493
+ description: "The key's member's Withings link (connected in the app, Settings \u2192 Health): `available` and `mode` (off \xB7 fake \xB7 live, the server's switch), `connected`, `lastSyncAt`, and `failure` (`reauth` \xB7 `unavailable` \xB7 `other`, with a title and a hint) when the sync stopped. Never another member's.",
34494
+ status: 200,
34495
+ response: { description: "The link's status." },
34496
+ errors: { 403: true }
34497
+ },
34498
+ sync: {
34499
+ method: "post",
34500
+ path: "/health/withings/sync",
34501
+ scopes: ["health:write"],
34502
+ tag: "Health",
34503
+ summary: "Sync Withings now",
34504
+ description: "As the key's member, one sync of their Withings scale, as the 07:30 morning sync and Withings' own notify do: each weigh-in (kg) onto their own ledger, joining the Weight Metric, nothing twice. Returns written and skipped and the status after it; a sync that stops answers 200 with `status.failure`. 409 before the member connected, 403 while the server has Withings off.",
34505
+ status: 200,
34506
+ response: { description: "What came in, and the status after it." },
34507
+ errors: { 403: true, 409: true }
34508
+ }
34509
+ });
34510
+
34511
+ // ../../packages/schema/src/operations/worktree-links.ts
34512
+ var pathParams5 = { nodeId: "The linked Task's or Project's ULID." };
34513
+ var notLinkable = "No live Task or Project with that id in this Space (or, for GET, no link on it).";
34514
+ var worktreeLinkOperations = defineOperations({
34515
+ list: {
34516
+ method: "get",
34517
+ path: "/worktree-links",
34518
+ scopes: ["orca:read"],
34519
+ tag: "Orca",
34520
+ summary: "List worktree links",
34521
+ description: "Every Worktree link on a live Task or Project in the Space, with the node's `title`.",
34522
+ query: [
34523
+ { name: "host", description: "Only links to worktrees on this Orca host." },
34524
+ {
34525
+ name: "worktreeId",
34526
+ description: "Only links to this Orca worktree id (`<repoId>::<path>`)."
34527
+ },
34528
+ { name: "subject", description: "Only links on this kind of node: `task` or `project`." }
34529
+ ],
34530
+ querySchema: worktreeLinkListQuerySchema,
34531
+ status: 200,
34532
+ response: { description: "The Space's worktree links." },
34533
+ errors: { 400: true }
34534
+ },
34535
+ get: {
34536
+ method: "get",
34537
+ path: "/worktree-links/{nodeId}",
34538
+ pathParams: pathParams5,
34539
+ scopes: ["orca:read"],
34540
+ tag: "Orca",
34541
+ summary: "Get a worktree link",
34542
+ description: "The Worktree link on a Task or Project.",
34543
+ status: 200,
34544
+ response: { description: "The link." },
34545
+ errors: { 404: notLinkable }
34546
+ },
34547
+ set: {
34548
+ method: "put",
34549
+ path: "/worktree-links/{nodeId}",
34550
+ pathParams: pathParams5,
34551
+ scopes: ["orca:write"],
34552
+ tag: "Orca",
34553
+ summary: "Set a worktree link",
34554
+ description: "Link a Task or Project to an Orca worktree (`host` + `worktreeId`, optionally `path` and `branch`), replacing any earlier link; fires `worktree.link.set`.",
34555
+ body: setWorktreeLinkInputSchema,
34556
+ status: 200,
34557
+ response: { description: "The stored link." },
34558
+ errors: { 400: true, 404: notLinkable },
34559
+ webhook: "worktree.link.set"
34560
+ },
34561
+ clear: {
34562
+ method: "delete",
34563
+ path: "/worktree-links/{nodeId}",
34564
+ pathParams: pathParams5,
34565
+ scopes: ["orca:write"],
34566
+ tag: "Orca",
34567
+ summary: "Clear a worktree link",
34568
+ description: "Unlink a Task or Project; fires `worktree.link.cleared` with the removed link. Idempotent \u2014 a node without a link is a no-op.",
34569
+ status: 204,
34570
+ response: { description: "Cleared." },
34571
+ errors: { 404: notLinkable },
34572
+ webhook: "worktree.link.cleared"
34573
+ }
34574
+ });
34575
+
33271
34576
  // ../../node_modules/ulidx/dist/node/index.js
33272
34577
  import crypto from "crypto";
33273
34578
 
@@ -33520,7 +34825,7 @@ function parseCapture2(input, now) {
33520
34825
 
33521
34826
  // src/meta.ts
33522
34827
  var CLI_NAME = "do";
33523
- var VERSION = "1.306.1";
34828
+ var VERSION = "1.306.2";
33524
34829
  var API_PREFIX = "/api/v1";
33525
34830
  var DEFAULT_API_URL = "http://localhost:3001";
33526
34831
 
@@ -33749,7 +35054,7 @@ function resolveConfig(io, flags) {
33749
35054
  }
33750
35055
 
33751
35056
  // src/contacts-commands.ts
33752
- var WRITE4 = ["contacts:write"];
35057
+ var WRITE6 = ["contacts:write"];
33753
35058
  var FIELD_FLAGS = [
33754
35059
  { name: "name", type: "string", description: "The display name (or pass it as the argument)." },
33755
35060
  { name: "kind", type: "string", description: "person (the default) or organization." },
@@ -33792,7 +35097,7 @@ var CONTACT_COMMAND_SPECS = [
33792
35097
  summary: "Add a contact: a person or an organisation, with their details.",
33793
35098
  args: [{ name: "name", required: false, description: "The display name." }],
33794
35099
  flags: FIELD_FLAGS,
33795
- scopes: WRITE4,
35100
+ scopes: WRITE6,
33796
35101
  returns: "ContactView",
33797
35102
  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>`.'
33798
35103
  },
@@ -33801,7 +35106,7 @@ var CONTACT_COMMAND_SPECS = [
33801
35106
  summary: "Change a contact's details.",
33802
35107
  args: [{ name: "id", required: true, description: "The contact's id." }],
33803
35108
  flags: FIELD_FLAGS,
33804
- scopes: WRITE4,
35109
+ scopes: WRITE6,
33805
35110
  returns: "ContactView",
33806
35111
  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)."
33807
35112
  }
@@ -34000,12 +35305,9 @@ var ENTITIES = [
34000
35305
  createInput: "CreateHabitInput",
34001
35306
  updateInput: "UpdateHabitInput"
34002
35307
  }),
34003
- {
35308
+ entityFromOperations(projectOperations, {
34004
35309
  name: "projects",
34005
- path: "/projects",
34006
- readScopes: ["projects:read"],
34007
35310
  returns: "ProjectView",
34008
- canGet: true,
34009
35311
  listFlags: [
34010
35312
  AREA_FLAG,
34011
35313
  {
@@ -34015,15 +35317,9 @@ var ENTITIES = [
34015
35317
  UPDATED_SINCE_FLAG,
34016
35318
  DUE_BEFORE_FLAG
34017
35319
  ],
34018
- write: {
34019
- scopes: ["projects:write"],
34020
- createSchema: createProjectInputSchema,
34021
- createInput: "CreateProjectInput",
34022
- updateSchema: updateProjectInputSchema,
34023
- updateInput: "UpdateProjectInput",
34024
- canDelete: true
34025
- }
34026
- },
35320
+ createInput: "CreateProjectInput",
35321
+ updateInput: "UpdateProjectInput"
35322
+ }),
34027
35323
  entityFromOperations(areaOperations, {
34028
35324
  name: "areas",
34029
35325
  returns: "AreaView",
@@ -34386,12 +35682,9 @@ var ENTITIES = [
34386
35682
  createInput: "CreateAnimalInput",
34387
35683
  updateInput: "UpdateAnimalInput"
34388
35684
  }),
34389
- {
35685
+ entityFromOperations(runOperations, {
34390
35686
  name: "runs",
34391
- path: "/runs",
34392
- readScopes: ["collections:read"],
34393
35687
  returns: "RunView",
34394
- canGet: true,
34395
35688
  listFlags: [
34396
35689
  { name: "game", description: "only the runs of this game item id", query: "gameId" },
34397
35690
  {
@@ -34401,15 +35694,9 @@ var ENTITIES = [
34401
35694
  },
34402
35695
  { name: "theme", description: "only this theme pack (skyrim, pokemon, \u2026)", query: "theme" }
34403
35696
  ],
34404
- write: {
34405
- scopes: ["collections:write"],
34406
- createSchema: createRunInputSchema,
34407
- createInput: "CreateRunInput",
34408
- updateSchema: updateRunInputSchema,
34409
- updateInput: "UpdateRunInput",
34410
- canDelete: true
34411
- }
34412
- },
35697
+ createInput: "CreateRunInput",
35698
+ updateInput: "UpdateRunInput"
35699
+ }),
34413
35700
  entityFromOperations(shoppingListOperations, {
34414
35701
  name: "shopping-lists",
34415
35702
  returns: "ShoppingList",
@@ -34488,8 +35775,8 @@ function entityOperations(e) {
34488
35775
  }
34489
35776
 
34490
35777
  // src/health-commands.ts
34491
- var READ4 = ["health:read"];
34492
- var WRITE5 = ["health:write"];
35778
+ var READ6 = ["health:read"];
35779
+ var WRITE7 = ["health:write"];
34493
35780
  var DATA_FLAG = {
34494
35781
  name: "data",
34495
35782
  type: "string",
@@ -34503,7 +35790,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34503
35790
  summary: `List your health ${noun}s.`,
34504
35791
  args: [],
34505
35792
  flags: listFlags,
34506
- scopes: READ4,
35793
+ scopes: READ6,
34507
35794
  returns
34508
35795
  },
34509
35796
  {
@@ -34511,7 +35798,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34511
35798
  summary: `Show one health ${noun}.`,
34512
35799
  args: [id],
34513
35800
  flags: [],
34514
- scopes: READ4,
35801
+ scopes: READ6,
34515
35802
  returns
34516
35803
  },
34517
35804
  {
@@ -34519,7 +35806,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34519
35806
  summary: `Add a health ${noun} (--data '${createInput}').`,
34520
35807
  args: [],
34521
35808
  flags: [DATA_FLAG, ...addFlags],
34522
- scopes: WRITE5,
35809
+ scopes: WRITE7,
34523
35810
  returns,
34524
35811
  notes: `Body: ${createInput}. Always private to you; subjectNodeId names who it is about (absent = you).`
34525
35812
  },
@@ -34528,7 +35815,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34528
35815
  summary: `Change a health ${noun} (--data '${updateInput}').`,
34529
35816
  args: [id],
34530
35817
  flags: [DATA_FLAG],
34531
- scopes: WRITE5,
35818
+ scopes: WRITE7,
34532
35819
  returns,
34533
35820
  notes: `Body: ${updateInput}. null clears a field.`
34534
35821
  },
@@ -34537,7 +35824,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34537
35824
  summary: `Delete a health ${noun}.`,
34538
35825
  args: [id],
34539
35826
  flags: [],
34540
- scopes: WRITE5,
35827
+ scopes: WRITE7,
34541
35828
  returns: "Deleted",
34542
35829
  notes: "Destructive: needs --allow-destructive. An Agent's delete waits on your confirmation."
34543
35830
  }
@@ -34549,7 +35836,7 @@ var HEALTH_COMMAND_SPECS = [
34549
35836
  summary: "Show whether Health is on (only you turn it on or off, in do).",
34550
35837
  args: [],
34551
35838
  flags: [],
34552
- scopes: READ4,
35839
+ scopes: READ6,
34553
35840
  returns: "HealthConsentState",
34554
35841
  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)."
34555
35842
  },
@@ -34558,7 +35845,7 @@ var HEALTH_COMMAND_SPECS = [
34558
35845
  summary: "Show your health profile: blood group, allergies, lifestyle, notes, emergency contact, GP.",
34559
35846
  args: [],
34560
35847
  flags: [],
34561
- scopes: READ4,
35848
+ scopes: READ6,
34562
35849
  returns: "HealthProfile"
34563
35850
  },
34564
35851
  {
@@ -34566,7 +35853,7 @@ var HEALTH_COMMAND_SPECS = [
34566
35853
  summary: "Show your Medical ID: name, blood group, allergies, current medications, height, emergency contact.",
34567
35854
  args: [],
34568
35855
  flags: [],
34569
- scopes: READ4,
35856
+ scopes: READ6,
34570
35857
  returns: "HealthMedicalId",
34571
35858
  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."
34572
35859
  },
@@ -34587,7 +35874,7 @@ var HEALTH_COMMAND_SPECS = [
34587
35874
  description: `Sex, in your words, for the doctor's page ("female"); "" clears it.`
34588
35875
  }
34589
35876
  ],
34590
- scopes: WRITE5,
35877
+ scopes: WRITE7,
34591
35878
  returns: "HealthProfile",
34592
35879
  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."
34593
35880
  },
@@ -34596,7 +35883,7 @@ var HEALTH_COMMAND_SPECS = [
34596
35883
  summary: "Log one health message: find or start the Episode, add a symptom and/or a dose.",
34597
35884
  args: [],
34598
35885
  flags: [DATA_FLAG],
34599
- scopes: WRITE5,
35886
+ scopes: WRITE7,
34600
35887
  returns: "HealthReceipt",
34601
35888
  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.'
34602
35889
  },
@@ -34605,7 +35892,7 @@ var HEALTH_COMMAND_SPECS = [
34605
35892
  summary: `Correct a symptom entry and get a receipt (--data '{"bodyRegion":"ankle_l"}').`,
34606
35893
  args: [{ name: "entry", required: true, description: "The entry's id (its latest version)." }],
34607
35894
  flags: [DATA_FLAG],
34608
- scopes: WRITE5,
35895
+ scopes: WRITE7,
34609
35896
  returns: "HealthReceipt"
34610
35897
  },
34611
35898
  {
@@ -34616,7 +35903,7 @@ var HEALTH_COMMAND_SPECS = [
34616
35903
  { name: "trend", required: true, description: "better|same|worse, against yesterday." }
34617
35904
  ],
34618
35905
  flags: [],
34619
- scopes: WRITE5,
35906
+ scopes: WRITE7,
34620
35907
  returns: "HealthReceipt",
34621
35908
  notes: "One a day: a second check-in the same day corrects the first. The receipt carries its undo."
34622
35909
  },
@@ -34633,7 +35920,7 @@ var HEALTH_COMMAND_SPECS = [
34633
35920
  description: "Post the confirmation card in this thread (by slug); the agent's key only."
34634
35921
  }
34635
35922
  ],
34636
- scopes: WRITE5,
35923
+ scopes: WRITE7,
34637
35924
  returns: "HealthRedFlagsResult",
34638
35925
  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."
34639
35926
  },
@@ -34645,7 +35932,7 @@ var HEALTH_COMMAND_SPECS = [
34645
35932
  { name: "id", required: true, description: "The id from the receipt's undo." }
34646
35933
  ],
34647
35934
  flags: [],
34648
- scopes: WRITE5,
35935
+ scopes: WRITE7,
34649
35936
  returns: "HealthUndoResult",
34650
35937
  notes: "A POST, so an Agent's undo never waits on a confirmation."
34651
35938
  },
@@ -34654,7 +35941,7 @@ var HEALTH_COMMAND_SPECS = [
34654
35941
  summary: "Find the open Episode a complaint belongs to, or start one (--data UpsertHealthEpisodeInput).",
34655
35942
  args: [],
34656
35943
  flags: [DATA_FLAG],
34657
- scopes: WRITE5,
35944
+ scopes: WRITE7,
34658
35945
  returns: "HealthEpisodeUpsert"
34659
35946
  },
34660
35947
  {
@@ -34664,7 +35951,7 @@ var HEALTH_COMMAND_SPECS = [
34664
35951
  flags: [
34665
35952
  { name: "episode", type: "string", description: "The Episode; default the latest open one." }
34666
35953
  ],
34667
- scopes: READ4,
35954
+ scopes: READ6,
34668
35955
  returns: "HealthSummary"
34669
35956
  },
34670
35957
  {
@@ -34687,7 +35974,7 @@ var HEALTH_COMMAND_SPECS = [
34687
35974
  summary: "Your For-the-doctor links: sections, expiry, opens; never the secret.",
34688
35975
  args: [],
34689
35976
  flags: [],
34690
- scopes: READ4,
35977
+ scopes: READ6,
34691
35978
  returns: "HealthShare[]",
34692
35979
  notes: "Only the member makes a share, in do; a key can list and take one back."
34693
35980
  },
@@ -34696,7 +35983,7 @@ var HEALTH_COMMAND_SPECS = [
34696
35983
  summary: "Take a For-the-doctor link back: it stops opening.",
34697
35984
  args: [{ name: "id", required: true, description: "The share's id." }],
34698
35985
  flags: [],
34699
- scopes: WRITE5,
35986
+ scopes: WRITE7,
34700
35987
  returns: "HealthShare"
34701
35988
  },
34702
35989
  ...recordSpecs("conditions", "condition", "condition", "HealthConditionView", "CreateHealthConditionInput", "UpdateHealthConditionInput"),
@@ -34714,7 +36001,7 @@ var HEALTH_COMMAND_SPECS = [
34714
36001
  description: 'Their role on it, in your words ("dermatologist").'
34715
36002
  }
34716
36003
  ],
34717
- scopes: WRITE5,
36004
+ scopes: WRITE7,
34718
36005
  returns: "HealthConditionView",
34719
36006
  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."
34720
36007
  },
@@ -34726,7 +36013,7 @@ var HEALTH_COMMAND_SPECS = [
34726
36013
  { name: "contact", required: true, description: "The Contact's id." }
34727
36014
  ],
34728
36015
  flags: [],
34729
- scopes: WRITE5,
36016
+ scopes: WRITE7,
34730
36017
  returns: "HealthConditionView",
34731
36018
  notes: "An Agent's removal waits on your confirmation, like a delete."
34732
36019
  },
@@ -34763,7 +36050,7 @@ var HEALTH_COMMAND_SPECS = [
34763
36050
  { name: "at", type: "string", description: "When it happened (ISO); defaults to now." },
34764
36051
  { name: "note", type: "string", description: "A free-text note." }
34765
36052
  ],
34766
- scopes: WRITE5,
36053
+ scopes: WRITE7,
34767
36054
  returns: "HealthSymptomEntry",
34768
36055
  notes: "An Agent's entry is source chat. For qualifiers, send --data with LogHealthSymptomInput."
34769
36056
  },
@@ -34779,7 +36066,7 @@ var HEALTH_COMMAND_SPECS = [
34779
36066
  }
34780
36067
  ],
34781
36068
  flags: [DATA_FLAG],
34782
- scopes: WRITE5,
36069
+ scopes: WRITE7,
34783
36070
  returns: "HealthSymptomEntry",
34784
36071
  notes: "Body: CorrectHealthSymptomInput; fields not named are copied. The history stays."
34785
36072
  },
@@ -34791,7 +36078,7 @@ var HEALTH_COMMAND_SPECS = [
34791
36078
  { name: "entry", required: true, description: "The entry's id." }
34792
36079
  ],
34793
36080
  flags: [],
34794
- scopes: WRITE5,
36081
+ scopes: WRITE7,
34795
36082
  returns: "Deleted"
34796
36083
  },
34797
36084
  ...recordSpecs("visits", "visit", "visit", "HealthEncounterView", "CreateHealthEncounterInput", "UpdateHealthEncounterInput"),
@@ -34801,7 +36088,7 @@ var HEALTH_COMMAND_SPECS = [
34801
36088
  summary: "List the doses of a Medication, latest first.",
34802
36089
  args: [{ name: "med", required: true, description: "The Medication's id." }],
34803
36090
  flags: [],
34804
- scopes: READ4,
36091
+ scopes: READ6,
34805
36092
  returns: "HealthMedicationDose"
34806
36093
  },
34807
36094
  {
@@ -34815,7 +36102,7 @@ var HEALTH_COMMAND_SPECS = [
34815
36102
  { name: "at", type: "string", description: "When (ISO); defaults to now." },
34816
36103
  { name: "note", type: "string", description: "A free-text note." }
34817
36104
  ],
34818
- scopes: WRITE5,
36105
+ scopes: WRITE7,
34819
36106
  returns: "HealthMedicationDose"
34820
36107
  },
34821
36108
  {
@@ -34826,7 +36113,7 @@ var HEALTH_COMMAND_SPECS = [
34826
36113
  { name: "dose", required: true, description: "The dose's id." }
34827
36114
  ],
34828
36115
  flags: [DATA_FLAG],
34829
- scopes: WRITE5,
36116
+ scopes: WRITE7,
34830
36117
  returns: "HealthMedicationDose",
34831
36118
  notes: "Body: UpdateHealthDoseInput."
34832
36119
  },
@@ -34838,7 +36125,7 @@ var HEALTH_COMMAND_SPECS = [
34838
36125
  { name: "dose", required: true, description: "The dose's id." }
34839
36126
  ],
34840
36127
  flags: [],
34841
- scopes: WRITE5,
36128
+ scopes: WRITE7,
34842
36129
  returns: "Deleted"
34843
36130
  },
34844
36131
  ...recordSpecs("labs", "lab", "lab draw", "HealthLabPanelView", "CreateHealthLabPanelInput", "UpdateHealthLabPanelInput"),
@@ -34860,7 +36147,7 @@ var HEALTH_COMMAND_SPECS = [
34860
36147
  description: "The file's media type, when its name doesn't say."
34861
36148
  }
34862
36149
  ],
34863
- scopes: WRITE5,
36150
+ scopes: WRITE7,
34864
36151
  returns: "HealthLabImportResult",
34865
36152
  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."
34866
36153
  },
@@ -34869,7 +36156,7 @@ var HEALTH_COMMAND_SPECS = [
34869
36156
  summary: "The canonical analytes: key, printed name, group, unit, LOINC and aliases.",
34870
36157
  args: [],
34871
36158
  flags: [],
34872
- scopes: READ4,
36159
+ scopes: READ6,
34873
36160
  returns: "LabAnalyteDef[]",
34874
36161
  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`.'
34875
36162
  },
@@ -34884,7 +36171,7 @@ var HEALTH_COMMAND_SPECS = [
34884
36171
  description: "Required: only report, change nothing."
34885
36172
  }
34886
36173
  ],
34887
- scopes: READ4,
36174
+ scopes: READ6,
34888
36175
  returns: "HealthLabNormalizeReport",
34889
36176
  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."
34890
36177
  },
@@ -34893,7 +36180,7 @@ var HEALTH_COMMAND_SPECS = [
34893
36180
  summary: "Every analyte your draws measured, each with its trend, grouped as a lab sheet.",
34894
36181
  args: [],
34895
36182
  flags: [],
34896
- scopes: READ4,
36183
+ scopes: READ6,
34897
36184
  returns: "HealthLabOverview"
34898
36185
  },
34899
36186
  {
@@ -34919,25 +36206,25 @@ var HEALTH_COMMAND_SPECS = [
34919
36206
  summary: "Whether your Withings scale is connected, the last sync, and what stops it.",
34920
36207
  args: [],
34921
36208
  flags: [],
34922
- scopes: READ4,
36209
+ scopes: [...withingsOperations.status.scopes],
34923
36210
  returns: "WithingsStatus",
34924
- notes: "GET /api/v1/health/withings. Connecting needs Withings' consent in a browser: Settings \u2192 Health in do. `failure` (reauth, unavailable, other) says what stops the sync, with a title and a hint."
36211
+ notes: `${routeOf(withingsOperations.status)}. Connecting needs Withings' consent in a browser: Settings \u2192 Health in do. \`failure\` (reauth, unavailable, other) says what stops the sync, with a title and a hint.`
34925
36212
  },
34926
36213
  {
34927
36214
  path: ["health", "withings", "sync"],
34928
36215
  summary: "Sync your Withings weigh-ins now, as the morning sync does.",
34929
36216
  args: [],
34930
36217
  flags: [],
34931
- scopes: WRITE5,
36218
+ scopes: [...withingsOperations.sync.scopes],
34932
36219
  returns: "WithingsSyncResult",
34933
- notes: "POST /api/v1/health/withings/sync. Written and skipped, and the status after it; a sync that stops answers 200 with `status.failure`. 409 before Withings is connected, 403 while the server has it switched off."
36220
+ notes: `${routeOf(withingsOperations.sync)}. Written and skipped, and the status after it; a sync that stops answers 200 with \`status.failure\`. 409 before Withings is connected, 403 while the server has it switched off.`
34934
36221
  },
34935
36222
  {
34936
36223
  path: ["health", "analyte"],
34937
36224
  summary: "One analyte's trend across draws (do health analyte vitamin_d).",
34938
36225
  args: [{ name: "analyte", required: true, description: "The analyte key (vitamin_d)." }],
34939
36226
  flags: [],
34940
- scopes: READ4,
36227
+ scopes: READ6,
34941
36228
  returns: "HealthLabAnalyteView"
34942
36229
  },
34943
36230
  {
@@ -34945,7 +36232,7 @@ var HEALTH_COMMAND_SPECS = [
34945
36232
  summary: `Keep a target band or the supplement for an analyte (--data '{"targetLow":40,"targetHigh":60}').`,
34946
36233
  args: [{ name: "analyte", required: true, description: "The analyte key (vitamin_d)." }],
34947
36234
  flags: [DATA_FLAG],
34948
- scopes: WRITE5,
36235
+ scopes: WRITE7,
34949
36236
  returns: "HealthLabAnalyteView",
34950
36237
  notes: "Body: SetHealthLabAnalyteInput. An absent key keeps a field; null clears it."
34951
36238
  }
@@ -35229,9 +36516,9 @@ var COMMAND_SPECS = [
35229
36516
  { name: "from", type: "string", description: "Window start (ISO); default today, UTC." },
35230
36517
  { name: "to", type: "string", description: "Window end (ISO); default a day after from." }
35231
36518
  ],
35232
- scopes: ["tasks:read"],
36519
+ scopes: [...readOperations.now.scopes],
35233
36520
  returns: "NowResponse",
35234
- notes: "GET /api/v1/now \u2014 server-side; pass your local day bounds with --from/--to."
36521
+ notes: `${routeOf(readOperations.now)} \u2014 server-side; pass your local day bounds with --from/--to.`
35235
36522
  },
35236
36523
  {
35237
36524
  path: ["landed"],
@@ -35241,7 +36528,7 @@ var COMMAND_SPECS = [
35241
36528
  { name: "from", type: "string", description: "Window start (ISO); default today, UTC." },
35242
36529
  { name: "to", type: "string", description: "Window end (ISO); default a day after from." }
35243
36530
  ],
35244
- scopes: ["tasks:read"],
36531
+ scopes: [...readOperations.landed.scopes],
35245
36532
  returns: "LandedResponse",
35246
36533
  notes: "Cancelled tasks never count. For a week: --from <monday> --to <next monday>."
35247
36534
  },
@@ -35253,7 +36540,7 @@ var COMMAND_SPECS = [
35253
36540
  { name: "from", type: "string", description: "Window start (ISO); default today, UTC." },
35254
36541
  { name: "to", type: "string", description: "Window end (ISO); default a day after from." }
35255
36542
  ],
35256
- scopes: ["tasks:read"],
36543
+ scopes: [...readOperations.review.scopes],
35257
36544
  returns: "ReviewDayStat"
35258
36545
  },
35259
36546
  {
@@ -35638,7 +36925,7 @@ var COMMAND_SPECS = [
35638
36925
  },
35639
36926
  { name: "limit", type: "string", description: "Max hits (1\u201350, default 20)." }
35640
36927
  ],
35641
- scopes: [],
36928
+ scopes: [...readOperations.search.scopes],
35642
36929
  returns: "SearchHit",
35643
36930
  notes: "Returns only the node types your key holds a read scope for."
35644
36931
  },
@@ -36574,7 +37861,7 @@ var COMMAND_SPECS = [
36574
37861
  summary: "Show the note attached to any node (Habit, Area, Contact, \u2026).",
36575
37862
  args: [{ name: "node", required: true, description: "Owner node ULID." }],
36576
37863
  flags: [],
36577
- scopes: ["notes:read"],
37864
+ scopes: [...nodeOperations.getNotes.scopes],
36578
37865
  returns: "AttachedNoteView",
36579
37866
  notes: "No note yet is `noteId: null` (human output says so). 404 for a `note`/`event` node."
36580
37867
  },
@@ -36583,7 +37870,7 @@ var COMMAND_SPECS = [
36583
37870
  summary: "Set the note attached to any node (upsert).",
36584
37871
  args: [{ name: "node", required: true, description: "Owner node ULID." }],
36585
37872
  flags: [{ name: "body", type: "string", description: "The note body (markdown)." }],
36586
- scopes: ["notes:write"],
37873
+ scopes: [...nodeOperations.setNotes.scopes],
36587
37874
  returns: "AttachedNoteView",
36588
37875
  notes: "Body from --body (required). A blank body is refused \u2014 clear with `notes detach`."
36589
37876
  },
@@ -36616,7 +37903,7 @@ var COMMAND_SPECS = [
36616
37903
  flags: [
36617
37904
  { name: "allow-destructive", type: "boolean", description: "Required to actually clear." }
36618
37905
  ],
36619
- scopes: ["notes:write"],
37906
+ scopes: [...nodeOperations.clearNotes.scopes],
36620
37907
  returns: "None",
36621
37908
  notes: "Destructive \u2014 gated behind --allow-destructive + a notes:write key. Idempotent."
36622
37909
  },
@@ -36625,7 +37912,7 @@ var COMMAND_SPECS = [
36625
37912
  summary: "List every Orca host's worktrees: status, last line, sessions and linked Task.",
36626
37913
  args: [],
36627
37914
  flags: [],
36628
- scopes: ["orca:read"],
37915
+ scopes: [...orcaOperations.overview.scopes],
36629
37916
  returns: "OrcaOverview",
36630
37917
  notes: "state is ok | unreachable | not_configured. waitingCount counts worktrees waiting on you. Read live; nothing is stored."
36631
37918
  },
@@ -36641,7 +37928,7 @@ var COMMAND_SPECS = [
36641
37928
  { name: "limit", type: "string", description: "At most this many lines (1\u20132000)." },
36642
37929
  { name: "screen", type: "boolean", description: "Read the rendered screen instead." }
36643
37930
  ],
36644
- scopes: ["orca:read"],
37931
+ scopes: [...orcaOperations.output.scopes],
36645
37932
  returns: "OrcaTerminalOutput"
36646
37933
  },
36647
37934
  {
@@ -36655,7 +37942,7 @@ var COMMAND_SPECS = [
36655
37942
  { name: "text", type: "string", description: "The message to send." },
36656
37943
  { name: "no-enter", type: "boolean", description: "Type it without pressing Enter." }
36657
37944
  ],
36658
- scopes: ["orca:write"],
37945
+ scopes: [...orcaOperations.send.scopes],
36659
37946
  returns: "OrcaSendMessageResult",
36660
37947
  notes: "An Agent's key gets a 428 confirmation_required; it runs once the member confirms in do's agent panel (#678)."
36661
37948
  },
@@ -36671,7 +37958,7 @@ var COMMAND_SPECS = [
36671
37958
  },
36672
37959
  { name: "agent", type: "string", description: "The agent to start: claude or codex." }
36673
37960
  ],
36674
- scopes: ["orca:write"],
37961
+ scopes: [...orcaOperations.restart.scopes],
36675
37962
  returns: "OrcaRestartSessionResult",
36676
37963
  notes: "Starts a new session; the old one is left as it was. Gated like orca send: an Agent's key gets a 428 confirmation_required until the member confirms."
36677
37964
  },
@@ -36688,7 +37975,7 @@ var COMMAND_SPECS = [
36688
37975
  },
36689
37976
  { name: "subject", type: "string", description: "Only `task` or `project` links." }
36690
37977
  ],
36691
- scopes: ["orca:read"],
37978
+ scopes: [...worktreeLinkOperations.list.scopes],
36692
37979
  returns: "WorktreeLinkView",
36693
37980
  notes: "Each row carries the linked node's title. Progress is never stored \u2014 read it in Orca."
36694
37981
  },
@@ -36697,7 +37984,7 @@ var COMMAND_SPECS = [
36697
37984
  summary: "Show the Worktree link on a Task or Project.",
36698
37985
  args: [{ name: "node", required: true, description: "Task or Project ULID." }],
36699
37986
  flags: [],
36700
- scopes: ["orca:read"],
37987
+ scopes: [...worktreeLinkOperations.get.scopes],
36701
37988
  returns: "WorktreeLink",
36702
37989
  notes: "Not found (exit 4) when the node has no link. Task/Project reads carry it as worktreeLink."
36703
37990
  },
@@ -36719,7 +38006,7 @@ var COMMAND_SPECS = [
36719
38006
  },
36720
38007
  { name: "branch", type: "string", description: "The branch, when known." }
36721
38008
  ],
36722
- scopes: ["orca:write"],
38009
+ scopes: [...worktreeLinkOperations.set.scopes],
36723
38010
  returns: "WorktreeLink",
36724
38011
  notes: "PUT /api/v1/worktree-links/{node}. --host and --worktree are required."
36725
38012
  },
@@ -36730,7 +38017,7 @@ var COMMAND_SPECS = [
36730
38017
  flags: [
36731
38018
  { name: "allow-destructive", type: "boolean", description: "Required to actually unlink." }
36732
38019
  ],
36733
- scopes: ["orca:write"],
38020
+ scopes: [...worktreeLinkOperations.clear.scopes],
36734
38021
  returns: "None",
36735
38022
  notes: "Gated behind --allow-destructive + an orca:write key. Idempotent."
36736
38023
  },
@@ -37110,7 +38397,7 @@ var COMMAND_SPECS = [
37110
38397
  summary: "Show the agent runtime the agent panel's chat goes to (never its key).",
37111
38398
  args: [],
37112
38399
  flags: [],
37113
- scopes: ["agents:read"],
38400
+ scopes: [...agentConnectionOperations.get.scopes],
37114
38401
  returns: "AgentConnection",
37115
38402
  notes: "hasKey says whether a key is set; the key itself is write-only."
37116
38403
  },
@@ -37143,7 +38430,7 @@ var COMMAND_SPECS = [
37143
38430
  },
37144
38431
  { name: "clear-key", type: "boolean", description: "Remove the stored key." }
37145
38432
  ],
37146
- scopes: ["agents:write"],
38433
+ scopes: [...agentConnectionOperations.save.scopes],
37147
38434
  returns: "AgentConnection",
37148
38435
  notes: "The runtime's key is read from DO_AGENT_API_KEY or, with --key-stdin, from stdin \u2014 never a flag. With neither, the stored key is kept. --model and --agent are replaced as sent (left out clears them). Owner/admin only (canManageSpace)."
37149
38436
  },
@@ -37154,7 +38441,7 @@ var COMMAND_SPECS = [
37154
38441
  flags: [
37155
38442
  { name: "allow-destructive", type: "boolean", description: "Required to actually remove." }
37156
38443
  ],
37157
- scopes: ["agents:write"],
38444
+ scopes: [...agentConnectionOperations.remove.scopes],
37158
38445
  returns: "None",
37159
38446
  notes: "Owner/admin only. An Agent's key gets a 428 confirmation_required until the member confirms in do's agent panel."
37160
38447
  },
@@ -37492,7 +38779,7 @@ var COMMAND_SPECS = [
37492
38779
  summary: "List the catalog Types (key \xB7 label \xB7 status subset).",
37493
38780
  args: [],
37494
38781
  flags: [],
37495
- scopes: ["collections:read"],
38782
+ scopes: [...collectionOperations.types.scopes],
37496
38783
  returns: "CollectionTypeSummary",
37497
38784
  notes: "The Type registry is code-defined (ADR 0017); served from GET /api/v1/collection/types."
37498
38785
  },
@@ -37520,7 +38807,7 @@ var COMMAND_SPECS = [
37520
38807
  },
37521
38808
  ...PAGINATION_FLAGS
37522
38809
  ],
37523
- scopes: ["collections:read"],
38810
+ scopes: [...collectionOperations.list.scopes],
37524
38811
  returns: "CollectionItemView"
37525
38812
  },
37526
38813
  {
@@ -37535,7 +38822,7 @@ var COMMAND_SPECS = [
37535
38822
  description: "Initial status; defaults to the Type's first."
37536
38823
  }
37537
38824
  ],
37538
- scopes: ["collections:write"],
38825
+ scopes: [...collectionOperations.create.scopes],
37539
38826
  returns: "CollectionItemView",
37540
38827
  grammar: {
37541
38828
  "#tag": "Adds a tag. Repeatable: '#scifi #2024'."
@@ -37554,7 +38841,7 @@ var COMMAND_SPECS = [
37554
38841
  }
37555
38842
  ],
37556
38843
  flags: [],
37557
- scopes: ["collections:write"],
38844
+ scopes: [...collectionOperations.update.scopes],
37558
38845
  returns: "CollectionItemView"
37559
38846
  },
37560
38847
  {
@@ -37577,7 +38864,7 @@ var COMMAND_SPECS = [
37577
38864
  { name: "cost", type: "string", description: "What it cost (a sweep, a repair)." },
37578
38865
  { name: "contact", type: "string", description: "Contact ULID of who did it." }
37579
38866
  ],
37580
- scopes: ["collections:write"],
38867
+ scopes: [...collectionOperations.addEntry.scopes],
37581
38868
  returns: "CollectionItemDetail"
37582
38869
  },
37583
38870
  {
@@ -37635,9 +38922,9 @@ var COMMAND_SPECS = [
37635
38922
  description: "Day it occurred (YYYY-MM-DD); defaults to today."
37636
38923
  }
37637
38924
  ],
37638
- scopes: ["collections:write"],
38925
+ scopes: [...collectionOperations.log.scopes],
37639
38926
  returns: "CollectionLogResult",
37640
- notes: "POST /api/v1/collection/log. Matches the exact title (case-insensitive); several matches \u2192 409 naming them, none without --type \u2192 404 naming close titles."
38927
+ notes: `${routeOf(collectionOperations.log)}. Matches the exact title (case-insensitive); several matches \u2192 409 naming them, none without --type \u2192 404 naming close titles.`
37641
38928
  },
37642
38929
  {
37643
38930
  path: ["collection", "edit-entry"],
@@ -37651,7 +38938,7 @@ var COMMAND_SPECS = [
37651
38938
  { name: "note", type: "string", description: "New note." },
37652
38939
  { name: "on", type: "string", description: "New day (YYYY-MM-DD)." }
37653
38940
  ],
37654
- scopes: ["collections:write"],
38941
+ scopes: [...collectionOperations.editEntry.scopes],
37655
38942
  returns: "CollectionItemDetail"
37656
38943
  },
37657
38944
  {
@@ -37665,7 +38952,7 @@ var COMMAND_SPECS = [
37665
38952
  description: "Use this image instead of searching (fixes a wrong cover)."
37666
38953
  }
37667
38954
  ],
37668
- scopes: ["collections:write"],
38955
+ scopes: [...collectionOperations.cover.scopes],
37669
38956
  returns: "CollectionCoverResult",
37670
38957
  notes: "Without --url: books from Open Library, games from IGDB then Steam; replaces the current cover with the next one (the one showing and ones passed over before are skipped). found: false when no other is left. Returns { found, item }."
37671
38958
  },
@@ -37674,7 +38961,7 @@ var COMMAND_SPECS = [
37674
38961
  summary: "List other covers for an Item, best first (up to 6).",
37675
38962
  args: [{ name: "id", required: true, description: "Item ULID." }],
37676
38963
  flags: [],
37677
- scopes: ["collections:read"],
38964
+ scopes: [...collectionOperations.coverCandidates.scopes],
37678
38965
  returns: "CollectionCoverCandidates",
37679
38966
  notes: "The providers' image URLs without the one showing and the ones passed over. Nothing is fetched; use one with `do collection cover <id> --url <url>`. Returns { candidates: [{ url }] }."
37680
38967
  },
@@ -37683,7 +38970,7 @@ var COMMAND_SPECS = [
37683
38970
  summary: "Backfill covers for books and games that have none (25 per call).",
37684
38971
  args: [],
37685
38972
  flags: [],
37686
- scopes: ["collections:write"],
38973
+ scopes: [...collectionOperations.backfillCovers.scopes],
37687
38974
  returns: "CollectionCoverBackfillResult",
37688
38975
  notes: "Returns { checked, found }; run again while checked is 25 to work through more."
37689
38976
  },
@@ -38856,9 +40143,9 @@ var COMMAND_SPECS = [
38856
40143
  { name: "session", type: "string", description: "Put the changes on this session id." },
38857
40144
  { name: "new-session", type: "boolean", description: "Start a new session for them." }
38858
40145
  ],
38859
- scopes: ["collections:write"],
40146
+ scopes: [...runOperations.applyChanges.scopes],
38860
40147
  returns: "RunChangesResult",
38861
- notes: "POST /api/v1/runs/{id}/changes. Ops: set|merge on a one-value section (character, arc, nextSteps, achievements, install, \u2026), upsert|remove on a list section matched by name (quests, choices, homes, people, mounts, uniques, orderOfPlay, team, checklists, links), tick on a checklist. The batch joins the last session if it was active in the last 6 hours, else opens a new one. A change that leaves the run invalid writes nothing."
40148
+ notes: `${routeOf(runOperations.applyChanges)}. Ops: set|merge on a one-value section (character, arc, nextSteps, achievements, install, \u2026), upsert|remove on a list section matched by name (quests, choices, homes, people, mounts, uniques, orderOfPlay, team, checklists, links), tick on a checklist. The batch joins the last session if it was active in the last 6 hours, else opens a new one. A change that leaves the run invalid writes nothing.`
38862
40149
  },
38863
40150
  {
38864
40151
  path: ["runs", "save-header"],
@@ -38884,18 +40171,18 @@ var COMMAND_SPECS = [
38884
40171
  },
38885
40172
  { name: "dry-run", type: "boolean", description: "Print what the save says; send nothing." }
38886
40173
  ],
38887
- scopes: ["collections:write"],
40174
+ scopes: [...runOperations.uploadSaveHeader.scopes],
38888
40175
  returns: "RunSaveHeaderResult",
38889
- notes: "POST /api/v1/runs/{id}/save-header. The save sync (run tracker phase 4): name, level, location, race, play time, the in-game date, plugins and General Stats become the run's `save` section and its character's level, as one update with a receipt and Undo; the screenshot is a file on the run (the last one is dropped). The same save twice changes nothing. Needs a member's key (the screenshot is stored as theirs). The save itself is only read. See docs/agents/skyrim-save-sync.md."
40176
+ notes: `${routeOf(runOperations.uploadSaveHeader)}. The save sync (run tracker phase 4): name, level, location, race, play time, the in-game date, plugins and General Stats become the run's \`save\` section and its character's level, as one update with a receipt and Undo; the screenshot is a file on the run (the last one is dropped). The same save twice changes nothing. Needs a member's key (the screenshot is stored as theirs). The save itself is only read. See docs/agents/skyrim-save-sync.md.`
38890
40177
  },
38891
40178
  {
38892
40179
  path: ["runs", "undo"],
38893
40180
  summary: "Undo a run's latest chat update (or the one given).",
38894
40181
  args: [{ name: "id", required: true, description: "Run ULID." }],
38895
40182
  flags: [{ name: "batch", type: "string", description: "The batch id to undo." }],
38896
- scopes: ["collections:write"],
40183
+ scopes: [...runOperations.undo.scopes],
38897
40184
  returns: "RunDetail",
38898
- notes: "POST /api/v1/runs/{id}/undo. Puts the changed sections back as they were."
40185
+ notes: `${routeOf(runOperations.undo)}. Puts the changed sections back as they were.`
38899
40186
  },
38900
40187
  {
38901
40188
  path: ["runs", "dex", "tick"],
@@ -38914,27 +40201,27 @@ var COMMAND_SPECS = [
38914
40201
  { name: "untick", type: "boolean", description: "Untick them instead." },
38915
40202
  { name: "message", type: "string", description: "The chat message, quoted on the receipt." }
38916
40203
  ],
38917
- scopes: ["collections:write"],
40204
+ scopes: [...runOperations.tickChecklist.scopes],
38918
40205
  returns: "RunChangesResult",
38919
- notes: "POST /api/v1/runs/{id}/checklists/{key}/tick. `do runs dex tick <run> 495 496 497`, `do runs dex tick <run> --bulk caught.txt` (up to 5000 ids). A dex the run doesn't have yet is made from the Pok\xE9mon pack's national dex."
40206
+ notes: `${routeOf(runOperations.tickChecklist)}. \`do runs dex tick <run> 495 496 497\`, \`do runs dex tick <run> --bulk caught.txt\` (up to 5000 ids). A dex the run doesn't have yet is made from the Pok\xE9mon pack's national dex.`
38920
40207
  },
38921
40208
  {
38922
40209
  path: ["runs", "steam", "status"],
38923
40210
  summary: "Whether you've linked Steam, how many games have a Steam app id, and the last sync.",
38924
40211
  args: [],
38925
40212
  flags: [],
38926
- scopes: ["collections:read"],
40213
+ scopes: [...runOperations.steamStatus.scopes],
38927
40214
  returns: "SteamAccountStatus",
38928
- notes: "GET /api/v1/runs/steam. Steam is linked in the app's Settings; the Steam ID and Web API key are never returned. A game takes part once its `steamAppId` is set."
40215
+ notes: `${routeOf(runOperations.steamStatus)}. Steam is linked in the app's Settings; the Steam ID and Web API key are never returned. A game takes part once its \`steamAppId\` is set.`
38929
40216
  },
38930
40217
  {
38931
40218
  path: ["runs", "steam", "sync"],
38932
40219
  summary: "Sync your Steam achievements into your runs now, as the daily sync does.",
38933
40220
  args: [],
38934
40221
  flags: [{ name: "game", type: "string", description: "Only this game item ULID." }],
38935
- scopes: ["collections:write"],
40222
+ scopes: [...runOperations.steamSync.scopes],
38936
40223
  returns: "SteamSyncResult",
38937
- notes: "POST /api/v1/runs/steam/sync. Writes into each run whose achievements come from Steam or aren't kept yet; runs kept by hand are left alone. Per game: outcome, runs, unlocked, total and the error (unauthorized, private, no_stats, unavailable, bad_response). 409 before Steam is linked."
40224
+ notes: `${routeOf(runOperations.steamSync)}. Writes into each run whose achievements come from Steam or aren't kept yet; runs kept by hand are left alone. Per game: outcome, runs, unlocked, total and the error (unauthorized, private, no_stats, unavailable, bad_response). 409 before Steam is linked.`
38938
40225
  },
38939
40226
  {
38940
40227
  path: ["sensors", "status"],
@@ -39041,9 +40328,9 @@ var COMMAND_SPECS = [
39041
40328
  summary: "A run's sessions, newest first, with recaps and change batches.",
39042
40329
  args: [{ name: "run", required: true, description: "Run ULID." }],
39043
40330
  flags: [],
39044
- scopes: ["collections:read"],
40331
+ scopes: [...runOperations.listSessions.scopes],
39045
40332
  returns: "RunSession[]",
39046
- notes: "GET /api/v1/runs/{id}/sessions."
40333
+ notes: `${routeOf(runOperations.listSessions)}.`
39047
40334
  },
39048
40335
  {
39049
40336
  path: ["runs", "sessions", "add"],
@@ -39054,9 +40341,9 @@ var COMMAND_SPECS = [
39054
40341
  { name: "minutes", type: "string", description: "How long it lasted." },
39055
40342
  { name: "on", type: "string", description: "The day, YYYY-MM-DD (default today)." }
39056
40343
  ],
39057
- scopes: ["collections:write"],
40344
+ scopes: [...runOperations.addSession.scopes],
39058
40345
  returns: "RunDetail",
39059
- notes: "POST /api/v1/runs/{id}/sessions."
40346
+ notes: `${routeOf(runOperations.addSession)}.`
39060
40347
  },
39061
40348
  {
39062
40349
  path: ["runs", "sessions", "update"],
@@ -39070,9 +40357,9 @@ var COMMAND_SPECS = [
39070
40357
  { name: "minutes", type: "string", description: "How long it lasted." },
39071
40358
  { name: "on", type: "string", description: "The day, YYYY-MM-DD." }
39072
40359
  ],
39073
- scopes: ["collections:write"],
40360
+ scopes: [...runOperations.updateSession.scopes],
39074
40361
  returns: "RunDetail",
39075
- notes: "PATCH /api/v1/runs/{id}/sessions/{sessionId}."
40362
+ notes: `${routeOf(runOperations.updateSession)}.`
39076
40363
  },
39077
40364
  {
39078
40365
  path: ["people", "propose"],
@@ -39134,9 +40421,9 @@ var COMMAND_SPECS = [
39134
40421
  description: "Required to actually delete it."
39135
40422
  }
39136
40423
  ],
39137
- scopes: ["collections:write"],
40424
+ scopes: [...runOperations.removeSession.scopes],
39138
40425
  returns: "None",
39139
- notes: "DELETE /api/v1/runs/{id}/sessions/{sessionId}."
40426
+ notes: `${routeOf(runOperations.removeSession)}.`
39140
40427
  },
39141
40428
  {
39142
40429
  path: ["places", "import"],
@@ -39568,7 +40855,7 @@ var COMMAND_SPECS = [
39568
40855
  description: 'JSON CreateInviteInput, e.g. {"type":"link"} or {"type":"email","email":"a@b.c"}.'
39569
40856
  }
39570
40857
  ],
39571
- scopes: ["members:write"],
40858
+ scopes: [...inviteOperations.create.scopes],
39572
40859
  returns: "Invite"
39573
40860
  },
39574
40861
  {
@@ -39576,7 +40863,7 @@ var COMMAND_SPECS = [
39576
40863
  summary: "List the Space's invites.",
39577
40864
  args: [],
39578
40865
  flags: [],
39579
- scopes: ["members:read"],
40866
+ scopes: [...inviteOperations.list.scopes],
39580
40867
  returns: "Invite"
39581
40868
  },
39582
40869
  {
@@ -39586,7 +40873,7 @@ var COMMAND_SPECS = [
39586
40873
  flags: [
39587
40874
  { name: "allow-destructive", type: "boolean", description: "Required to actually revoke." }
39588
40875
  ],
39589
- scopes: ["members:write"],
40876
+ scopes: [...inviteOperations.revoke.scopes],
39590
40877
  returns: "None",
39591
40878
  notes: "Destructive \u2014 gated behind --allow-destructive + a members:write key."
39592
40879
  },
@@ -39595,7 +40882,7 @@ var COMMAND_SPECS = [
39595
40882
  summary: "List the Space's members.",
39596
40883
  args: [],
39597
40884
  flags: [],
39598
- scopes: ["members:read"],
40885
+ scopes: [...memberOperations.list.scopes],
39599
40886
  returns: "MemberView"
39600
40887
  },
39601
40888
  {
@@ -39606,7 +40893,7 @@ var COMMAND_SPECS = [
39606
40893
  { name: "role", type: "string", description: "Base role: owner|admin|member|guest." },
39607
40894
  { name: "data", type: "string", description: "JSON SetMemberRoleInput (overrides --role)." }
39608
40895
  ],
39609
- scopes: ["members:write"],
40896
+ scopes: [...memberOperations.setRole.scopes],
39610
40897
  returns: "MemberView"
39611
40898
  },
39612
40899
  {
@@ -39616,7 +40903,7 @@ var COMMAND_SPECS = [
39616
40903
  flags: [
39617
40904
  { name: "allow-destructive", type: "boolean", description: "Required to actually remove." }
39618
40905
  ],
39619
- scopes: ["members:write"],
40906
+ scopes: [...memberOperations.remove.scopes],
39620
40907
  returns: "None",
39621
40908
  notes: "Destructive \u2014 gated behind --allow-destructive + a members:write key."
39622
40909
  },
@@ -39639,7 +40926,7 @@ var COMMAND_SPECS = [
39639
40926
  default: "member"
39640
40927
  }
39641
40928
  ],
39642
- scopes: ["sharing:write"],
40929
+ scopes: [...sharingOperations.createGrant.scopes],
39643
40930
  returns: "NodeGrantView"
39644
40931
  },
39645
40932
  {
@@ -39647,7 +40934,7 @@ var COMMAND_SPECS = [
39647
40934
  summary: "List the grants on a node.",
39648
40935
  args: [{ name: "node", required: true, description: "Node ULID." }],
39649
40936
  flags: [],
39650
- scopes: ["sharing:read"],
40937
+ scopes: [...sharingOperations.listGrants.scopes],
39651
40938
  returns: "NodeGrantView"
39652
40939
  },
39653
40940
  {
@@ -39660,7 +40947,7 @@ var COMMAND_SPECS = [
39660
40947
  flags: [
39661
40948
  { name: "allow-destructive", type: "boolean", description: "Required to actually revoke." }
39662
40949
  ],
39663
- scopes: ["sharing:write"],
40950
+ scopes: [...sharingOperations.revokeGrant.scopes],
39664
40951
  returns: "None",
39665
40952
  notes: "Destructive \u2014 gated behind --allow-destructive + a sharing:write key."
39666
40953
  },
@@ -39672,7 +40959,7 @@ var COMMAND_SPECS = [
39672
40959
  { name: "to", type: "string", description: "Member ULID to assign to." },
39673
40960
  { name: "cap", type: "string", description: "Capability (default edit).", default: "edit" }
39674
40961
  ],
39675
- scopes: ["sharing:write"],
40962
+ scopes: [...sharingOperations.assign.scopes],
39676
40963
  returns: "NodeGrantView"
39677
40964
  },
39678
40965
  {
@@ -39683,7 +40970,7 @@ var COMMAND_SPECS = [
39683
40970
  { name: "body", required: true, description: "Comment text." }
39684
40971
  ],
39685
40972
  flags: [],
39686
- scopes: ["comments:write"],
40973
+ scopes: [...commentOperations.create.scopes],
39687
40974
  returns: "CommentView"
39688
40975
  },
39689
40976
  {
@@ -39691,7 +40978,7 @@ var COMMAND_SPECS = [
39691
40978
  summary: "List a node's comment thread.",
39692
40979
  args: [{ name: "node", required: true, description: "Node ULID." }],
39693
40980
  flags: [],
39694
- scopes: ["comments:read"],
40981
+ scopes: [...commentOperations.list.scopes],
39695
40982
  returns: "CommentView"
39696
40983
  },
39697
40984
  {
@@ -39699,7 +40986,7 @@ var COMMAND_SPECS = [
39699
40986
  summary: "List the Space's custom roles.",
39700
40987
  args: [],
39701
40988
  flags: [],
39702
- scopes: ["roles:read"],
40989
+ scopes: [...roleOperations.list.scopes],
39703
40990
  returns: "RoleView"
39704
40991
  },
39705
40992
  {
@@ -39710,7 +40997,7 @@ var COMMAND_SPECS = [
39710
40997
  { name: "cap", type: "string", description: "Capability (default view).", default: "view" },
39711
40998
  { name: "expires", type: "string", description: "Expiry (ISO date/time)." }
39712
40999
  ],
39713
- scopes: ["links:write"],
41000
+ scopes: [...shareLinkOperations.create.scopes],
39714
41001
  returns: "CreateShareLinkResult",
39715
41002
  notes: "The signed token is returned once, in the create response."
39716
41003
  },
@@ -39719,7 +41006,7 @@ var COMMAND_SPECS = [
39719
41006
  summary: "List the share-links for a node.",
39720
41007
  args: [{ name: "node", required: true, description: "Node ULID." }],
39721
41008
  flags: [],
39722
- scopes: ["links:read"],
41009
+ scopes: [...shareLinkOperations.listForNode.scopes],
39723
41010
  returns: "ShareLinkPublic"
39724
41011
  },
39725
41012
  {
@@ -39729,7 +41016,7 @@ var COMMAND_SPECS = [
39729
41016
  flags: [
39730
41017
  { name: "allow-destructive", type: "boolean", description: "Required to actually revoke." }
39731
41018
  ],
39732
- scopes: ["links:write"],
41019
+ scopes: [...shareLinkOperations.revoke.scopes],
39733
41020
  returns: "ShareLinkPublic",
39734
41021
  notes: "Destructive \u2014 gated behind --allow-destructive + a links:write key."
39735
41022
  },
@@ -39766,16 +41053,16 @@ var COMMAND_SPECS = [
39766
41053
  description: "Comma-separated kinds to keep, e.g. task,project."
39767
41054
  }
39768
41055
  ],
39769
- scopes: [],
41056
+ scopes: [...changesOperations.list.scopes],
39770
41057
  returns: "ChangesPage",
39771
- notes: "GET /api/v1/changes. Store nextCursor and pass it back as --since; rerun at once while hasMore. A key sees the kinds its *:read scopes cover (tasks:read \u2192 task, \u2026)."
41058
+ notes: `${routeOf(changesOperations.list)}. Store nextCursor and pass it back as --since; rerun at once while hasMore. A key sees the kinds its *:read scopes cover (tasks:read \u2192 task, \u2026).`
39772
41059
  },
39773
41060
  {
39774
41061
  path: ["keys", "list"],
39775
41062
  summary: "List your API keys: name, id, reach, and when each last changed.",
39776
41063
  args: [],
39777
41064
  flags: [],
39778
- scopes: ["keys:read"],
41065
+ scopes: [...keyOperations.list.scopes],
39779
41066
  returns: "ApiKeyListItem",
39780
41067
  notes: "Only the keys of the member behind this key; never a secret."
39781
41068
  },
@@ -39796,7 +41083,7 @@ var COMMAND_SPECS = [
39796
41083
  },
39797
41084
  { name: "name", type: "string", description: "A new name for the key." }
39798
41085
  ],
39799
- scopes: ["keys:write"],
41086
+ scopes: [...keyOperations.update.scopes],
39800
41087
  returns: "ApiKey",
39801
41088
  notes: "A key never grants more than it holds: it can't widen its own scopes or change a key that reaches further (exit 3). Every change is recorded on the key. Minting and revoking stay in the app."
39802
41089
  },
@@ -39805,7 +41092,7 @@ var COMMAND_SPECS = [
39805
41092
  summary: "Show the tenant and scopes the configured key resolves to.",
39806
41093
  args: [],
39807
41094
  flags: [],
39808
- scopes: [],
41095
+ scopes: [...whoamiOperations.whoami.scopes],
39809
41096
  returns: "ApiKeyContext"
39810
41097
  },
39811
41098
  {
@@ -42131,7 +43418,7 @@ var HANDLERS = {
42131
43418
  };
42132
43419
  },
42133
43420
  now: async ({ flags, requireClient }) => ({
42134
- data: await requireClient().get("/now", {
43421
+ data: await requireClient().get(pathOf(readOperations.now), {
42135
43422
  query: {
42136
43423
  for: flagString(flags, "for"),
42137
43424
  from: flagString(flags, "from"),
@@ -42141,13 +43428,13 @@ var HANDLERS = {
42141
43428
  render: renderNow
42142
43429
  }),
42143
43430
  landed: async ({ flags, requireClient }) => ({
42144
- data: await requireClient().get("/landed", {
43431
+ data: await requireClient().get(pathOf(readOperations.landed), {
42145
43432
  query: { from: flagString(flags, "from"), to: flagString(flags, "to") }
42146
43433
  }),
42147
43434
  render: renderLanded
42148
43435
  }),
42149
43436
  review: async ({ flags, requireClient }) => ({
42150
- data: await requireClient().get("/review", {
43437
+ data: await requireClient().get(pathOf(readOperations.review), {
42151
43438
  query: { from: flagString(flags, "from"), to: flagString(flags, "to") }
42152
43439
  }),
42153
43440
  render: renderReview
@@ -42461,14 +43748,14 @@ var HANDLERS = {
42461
43748
  if (q.length === 0)
42462
43749
  throw usageError('search needs text, e.g. `do search "passport"`');
42463
43750
  return {
42464
- data: await requireClient().get("/search", {
43751
+ data: await requireClient().get(pathOf(readOperations.search), {
42465
43752
  query: { q, type: flagString(flags, "type"), limit: flagString(flags, "limit") }
42466
43753
  }),
42467
43754
  render: renderSearch
42468
43755
  };
42469
43756
  },
42470
43757
  changes: async ({ flags, requireClient }) => {
42471
- const text = await requireClient().getText("/changes", {
43758
+ const text = await requireClient().getText(pathOf(changesOperations.list), {
42472
43759
  query: {
42473
43760
  since: flagString(flags, "since"),
42474
43761
  limit: flagString(flags, "limit"),
@@ -42480,7 +43767,7 @@ var HANDLERS = {
42480
43767
  "notes attached": async ({ args, requireClient }) => {
42481
43768
  const node = requireArg(args, 0, "node id");
42482
43769
  return {
42483
- data: await requireClient().get(`/nodes/${node}/notes`),
43770
+ data: await requireClient().get(pathOf(nodeOperations.getNotes, { id: node })),
42484
43771
  render: renderAttachedNote
42485
43772
  };
42486
43773
  },
@@ -42494,7 +43781,7 @@ var HANDLERS = {
42494
43781
  }
42495
43782
  const body = parseInput(setAttachedNoteInputSchema, { body: raw });
42496
43783
  return {
42497
- data: await requireClient().put(`/nodes/${node}/notes`, body),
43784
+ data: await requireClient().put(pathOf(nodeOperations.setNotes, { id: node }), body),
42498
43785
  render: renderAttachedNote
42499
43786
  };
42500
43787
  },
@@ -42524,18 +43811,18 @@ var HANDLERS = {
42524
43811
  "notes detach": async ({ args, flags, requireClient }) => {
42525
43812
  const node = requireArg(args, 0, "node id");
42526
43813
  assertDestructiveAllowed(flags, `detach the note from ${node}`);
42527
- await requireClient().delete(`/nodes/${node}/notes`);
43814
+ await requireClient().delete(pathOf(nodeOperations.clearNotes, { id: node }));
42528
43815
  return { data: { ok: true, detached: node }, render: renderRecord };
42529
43816
  },
42530
43817
  "orca list": async ({ requireClient }) => ({
42531
- data: await requireClient().get("/orca"),
43818
+ data: await requireClient().get(pathOf(orcaOperations.overview)),
42532
43819
  render: renderOrca
42533
43820
  }),
42534
43821
  "orca output": async ({ args, flags, requireClient }) => {
42535
43822
  const host = requireArg(args, 0, "host");
42536
43823
  const handle = requireArg(args, 1, "session handle");
42537
43824
  return {
42538
- data: await requireClient().get(`/orca/hosts/${encodeURIComponent(host)}/terminals/${encodeURIComponent(handle)}/output`, {
43825
+ data: await requireClient().get(pathOf(orcaOperations.output, { host, handle }), {
42539
43826
  query: {
42540
43827
  cursor: flagString(flags, "cursor"),
42541
43828
  limit: flagString(flags, "limit"),
@@ -42556,7 +43843,7 @@ var HANDLERS = {
42556
43843
  enter: !flagBool(flags, "no-enter")
42557
43844
  });
42558
43845
  return {
42559
- data: await requireClient().post(`/orca/hosts/${encodeURIComponent(host)}/terminals/${encodeURIComponent(handle)}/messages`, body),
43846
+ data: await requireClient().post(pathOf(orcaOperations.send, { host, handle }), body),
42560
43847
  render: renderRecord
42561
43848
  };
42562
43849
  },
@@ -42571,12 +43858,12 @@ var HANDLERS = {
42571
43858
  agent: flagString(flags, "agent")
42572
43859
  });
42573
43860
  return {
42574
- data: await requireClient().post(`/orca/hosts/${encodeURIComponent(host)}/sessions`, body),
43861
+ data: await requireClient().post(pathOf(orcaOperations.restart, { host }), body),
42575
43862
  render: renderRecord
42576
43863
  };
42577
43864
  },
42578
43865
  "worktree-links list": async ({ flags, requireClient }) => ({
42579
- data: await requireClient().get("/worktree-links", {
43866
+ data: await requireClient().get(pathOf(worktreeLinkOperations.list), {
42580
43867
  query: {
42581
43868
  host: flagString(flags, "host"),
42582
43869
  worktreeId: flagString(flags, "worktree"),
@@ -42588,7 +43875,7 @@ var HANDLERS = {
42588
43875
  "worktree-links get": async ({ args, requireClient }) => {
42589
43876
  const node = requireArg(args, 0, "node id");
42590
43877
  return {
42591
- data: await requireClient().get(`/worktree-links/${node}`),
43878
+ data: await requireClient().get(pathOf(worktreeLinkOperations.get, { nodeId: node })),
42592
43879
  render: renderWorktreeLink
42593
43880
  };
42594
43881
  },
@@ -42606,14 +43893,14 @@ var HANDLERS = {
42606
43893
  branch: flagString(flags, "branch")
42607
43894
  });
42608
43895
  return {
42609
- data: await requireClient().put(`/worktree-links/${node}`, body),
43896
+ data: await requireClient().put(pathOf(worktreeLinkOperations.set, { nodeId: node }), body),
42610
43897
  render: renderWorktreeLink
42611
43898
  };
42612
43899
  },
42613
43900
  "worktree-links clear": async ({ args, flags, requireClient }) => {
42614
43901
  const node = requireArg(args, 0, "node id");
42615
43902
  assertDestructiveAllowed(flags, `unlink ${node} from its worktree`);
42616
- await requireClient().delete(`/worktree-links/${node}`);
43903
+ await requireClient().delete(pathOf(worktreeLinkOperations.clear, { nodeId: node }));
42617
43904
  return { data: { ok: true, unlinked: node }, render: renderRecord };
42618
43905
  },
42619
43906
  "files list": async ({ flags, requireClient }) => ({
@@ -43511,7 +44798,7 @@ var HANDLERS = {
43511
44798
  render: renderList
43512
44799
  }),
43513
44800
  "agent connection get": async ({ requireClient }) => ({
43514
- data: await requireClient().get("/agent/connection"),
44801
+ data: await requireClient().get(pathOf(agentConnectionOperations.get)),
43515
44802
  render: renderRecord
43516
44803
  }),
43517
44804
  "agent connection set": async ({ io, flags, requireClient }) => {
@@ -43528,11 +44815,14 @@ var HANDLERS = {
43528
44815
  autoFile,
43529
44816
  apiKey: await readAgentKey(io, flags)
43530
44817
  });
43531
- return { data: await client.put("/agent/connection", body), render: renderRecord };
44818
+ return {
44819
+ data: await client.put(pathOf(agentConnectionOperations.save), body),
44820
+ render: renderRecord
44821
+ };
43532
44822
  },
43533
44823
  "agent connection remove": async ({ flags, requireClient }) => {
43534
44824
  assertDestructiveAllowed(flags, "remove the agent connection");
43535
- await requireClient().delete("/agent/connection");
44825
+ await requireClient().delete(pathOf(agentConnectionOperations.remove));
43536
44826
  return { data: { ok: true, removed: "agent connection" }, render: renderRecord };
43537
44827
  },
43538
44828
  "agent post": agentPost,
@@ -43665,7 +44955,7 @@ var HANDLERS = {
43665
44955
  return { data: result, render: renderPresetInstall };
43666
44956
  },
43667
44957
  "collection types": async ({ requireClient }) => {
43668
- const types = collectionTypeSummarySchema.array().parse(await requireClient().get("/collection/types"));
44958
+ const types = collectionTypeSummarySchema.array().parse(await requireClient().get(pathOf(collectionOperations.types)));
43669
44959
  return { data: types, render: renderList };
43670
44960
  },
43671
44961
  "collection list": async ({ flags, requireClient }) => {
@@ -43677,7 +44967,10 @@ var HANDLERS = {
43677
44967
  title: flagString(flags, "title")
43678
44968
  };
43679
44969
  applyPageFlags(query, flags);
43680
- return { data: await requireClient().getPage("/collection", { query }), render: renderList };
44970
+ return {
44971
+ data: await requireClient().getPage(pathOf(collectionOperations.list), { query }),
44972
+ render: renderList
44973
+ };
43681
44974
  },
43682
44975
  "collection add": async ({ args, flags, requireClient }) => {
43683
44976
  const { title, tags } = parseTitleTags(args);
@@ -43691,13 +44984,19 @@ var HANDLERS = {
43691
44984
  ...tags.length > 0 ? { tags } : {},
43692
44985
  ...flagString(flags, "status") ? { status: flagString(flags, "status") } : {}
43693
44986
  });
43694
- return { data: await requireClient().post("/collection", body), render: renderRecord };
44987
+ return {
44988
+ data: await requireClient().post(pathOf(collectionOperations.create), body),
44989
+ render: renderRecord
44990
+ };
43695
44991
  },
43696
44992
  "collection set-status": async ({ args, requireClient }) => {
43697
44993
  const id = requireArg(args, 0, "item id");
43698
44994
  const status = requireArg(args, 1, "status");
43699
44995
  const body = parseInput(updateCollectionItemInputSchema, { status });
43700
- return { data: await requireClient().patch(`/collection/${id}`, body), render: renderRecord };
44996
+ return {
44997
+ data: await requireClient().patch(pathOf(collectionOperations.update, { id }), body),
44998
+ render: renderRecord
44999
+ };
43701
45000
  },
43702
45001
  "collection add-entry": async ({ args, flags, io, requireClient }) => {
43703
45002
  const id = requireArg(args, 0, "item id");
@@ -43719,7 +45018,7 @@ var HANDLERS = {
43719
45018
  ...flagString(flags, "contact") ? { contactId: flagString(flags, "contact") } : {}
43720
45019
  });
43721
45020
  return {
43722
- data: await requireClient().post(`/collection/${id}/entries`, body),
45021
+ data: await requireClient().post(pathOf(collectionOperations.addEntry, { id }), body),
43723
45022
  render: renderRecord
43724
45023
  };
43725
45024
  },
@@ -43767,7 +45066,10 @@ var HANDLERS = {
43767
45066
  ...flagString(flags, "note") ? { note: flagString(flags, "note") } : {},
43768
45067
  ...flagString(flags, "on") ? { occurredOn: flagString(flags, "on") } : {}
43769
45068
  });
43770
- return { data: await requireClient().post("/collection/log", body), render: renderRecord };
45069
+ return {
45070
+ data: await requireClient().post(pathOf(collectionOperations.log), body),
45071
+ render: renderRecord
45072
+ };
43771
45073
  },
43772
45074
  "collection edit-entry": async ({ args, flags, requireClient }) => {
43773
45075
  const id = requireArg(args, 0, "item id");
@@ -43782,7 +45084,7 @@ var HANDLERS = {
43782
45084
  ...flagString(flags, "on") ? { occurredOn: flagString(flags, "on") } : {}
43783
45085
  });
43784
45086
  return {
43785
- data: await requireClient().patch(`/collection/${id}/entries/${entryId}`, body),
45087
+ data: await requireClient().patch(pathOf(collectionOperations.editEntry, { id, entryId }), body),
43786
45088
  render: renderRecord
43787
45089
  };
43788
45090
  },
@@ -43791,17 +45093,17 @@ var HANDLERS = {
43791
45093
  const url = flagString(flags, "url");
43792
45094
  const body = parseInput(collectionCoverInputSchema, url ? { url } : {});
43793
45095
  return {
43794
- data: await requireClient().post(`/collection/${id}/cover`, body),
45096
+ data: await requireClient().post(pathOf(collectionOperations.cover, { id }), body),
43795
45097
  render: renderRecord
43796
45098
  };
43797
45099
  },
43798
45100
  "collection cover-candidates": async ({ args, requireClient }) => {
43799
45101
  const id = requireArg(args, 0, "item id");
43800
- const result = collectionCoverCandidatesSchema.parse(await requireClient().get(`/collection/${id}/cover/candidates`));
45102
+ const result = collectionCoverCandidatesSchema.parse(await requireClient().get(pathOf(collectionOperations.coverCandidates, { id })));
43801
45103
  return { data: result, render: renderRecord };
43802
45104
  },
43803
45105
  "collection covers": async ({ requireClient }) => {
43804
- const result = collectionCoverBackfillResultSchema.parse(await requireClient().post("/collection/covers", undefined));
45106
+ const result = collectionCoverBackfillResultSchema.parse(await requireClient().post(pathOf(collectionOperations.backfillCovers), undefined));
43805
45107
  return { data: result, render: renderRecord };
43806
45108
  },
43807
45109
  "areas purpose": async ({ args, flags, requireClient }) => {
@@ -44582,7 +45884,10 @@ var HANDLERS = {
44582
45884
  if (flagBool(flags, "new-session"))
44583
45885
  body.newSession = true;
44584
45886
  const input = parseInput(applyRunChangesInputSchema, body);
44585
- return { data: await requireClient().post(`/runs/${id}/changes`, input), render: renderRecord };
45887
+ return {
45888
+ data: await requireClient().post(pathOf(runOperations.applyChanges, { id }), input),
45889
+ render: renderRecord
45890
+ };
44586
45891
  },
44587
45892
  "runs save-header": async ({ args, flags, io, requireClient }) => {
44588
45893
  const id = requireArg(args, 0, "run id");
@@ -44619,7 +45924,10 @@ var HANDLERS = {
44619
45924
  const id = requireArg(args, 0, "run id");
44620
45925
  const batch = flagString(flags, "batch");
44621
45926
  const input = parseInput(undoRunChangesInputSchema, batch ? { batchId: batch } : {});
44622
- return { data: await requireClient().post(`/runs/${id}/undo`, input), render: renderRecord };
45927
+ return {
45928
+ data: await requireClient().post(pathOf(runOperations.undo, { id }), input),
45929
+ render: renderRecord
45930
+ };
44623
45931
  },
44624
45932
  "runs dex tick": async ({ args, flags, requireClient, io }) => {
44625
45933
  const id = requireArg(args, 0, "run id");
@@ -44643,19 +45951,22 @@ var HANDLERS = {
44643
45951
  ...message ? { message } : {}
44644
45952
  });
44645
45953
  return {
44646
- data: await requireClient().post(`/runs/${id}/checklists/${encodeURIComponent(key)}/tick`, input),
45954
+ data: await requireClient().post(pathOf(runOperations.tickChecklist, { id, key }), input),
44647
45955
  render: renderRecord
44648
45956
  };
44649
45957
  },
44650
45958
  "runs steam status": async ({ requireClient }) => ({
44651
- data: await requireClient().get("/runs/steam"),
45959
+ data: await requireClient().get(pathOf(runOperations.steamStatus)),
44652
45960
  render: renderRecord
44653
45961
  }),
44654
45962
  "runs steam sync": async ({ flags, requireClient }) => {
44655
45963
  const body = parseInput(steamSyncInputSchema, {
44656
45964
  ...typeof flags.game === "string" ? { gameId: flags.game } : {}
44657
45965
  });
44658
- return { data: await requireClient().post("/runs/steam/sync", body), render: renderRecord };
45966
+ return {
45967
+ data: await requireClient().post(pathOf(runOperations.steamSync), body),
45968
+ render: renderRecord
45969
+ };
44659
45970
  },
44660
45971
  "sensors status": async ({ requireClient }) => ({
44661
45972
  data: await requireClient().get(pathOf(sensorOperations.status)),
@@ -44707,13 +46018,16 @@ var HANDLERS = {
44707
46018
  },
44708
46019
  "runs sessions list": async ({ args, requireClient }) => {
44709
46020
  const id = requireArg(args, 0, "run id");
44710
- return { data: await requireClient().get(`/runs/${id}/sessions`), render: renderList };
46021
+ return {
46022
+ data: await requireClient().get(pathOf(runOperations.listSessions, { id })),
46023
+ render: renderList
46024
+ };
44711
46025
  },
44712
46026
  "runs sessions add": async ({ args, flags, requireClient }) => {
44713
46027
  const id = requireArg(args, 0, "run id");
44714
46028
  const input = parseInput(createRunSessionInputSchema, sessionFlags(flags));
44715
46029
  return {
44716
- data: await requireClient().post(`/runs/${id}/sessions`, input),
46030
+ data: await requireClient().post(pathOf(runOperations.addSession, { id }), input),
44717
46031
  render: renderRecord
44718
46032
  };
44719
46033
  },
@@ -44722,7 +46036,7 @@ var HANDLERS = {
44722
46036
  const session = requireArg(args, 1, "session id");
44723
46037
  const input = parseInput(updateRunSessionInputSchema, sessionFlags(flags));
44724
46038
  return {
44725
- data: await requireClient().patch(`/runs/${id}/sessions/${session}`, input),
46039
+ data: await requireClient().patch(pathOf(runOperations.updateSession, { id, sessionId: session }), input),
44726
46040
  render: renderRecord
44727
46041
  };
44728
46042
  },
@@ -44730,7 +46044,7 @@ var HANDLERS = {
44730
46044
  const id = requireArg(args, 0, "run id");
44731
46045
  const session = requireArg(args, 1, "session id");
44732
46046
  assertDestructiveAllowed(flags, `delete session ${session}`);
44733
- await requireClient().delete(`/runs/${id}/sessions/${session}`);
46047
+ await requireClient().delete(pathOf(runOperations.removeSession, { id, sessionId: session }));
44734
46048
  return { data: { ok: true, removed: session }, render: renderRecord };
44735
46049
  },
44736
46050
  ...healthRecordHandlers(),
@@ -44807,11 +46121,11 @@ var HANDLERS = {
44807
46121
  render: renderRecord
44808
46122
  }),
44809
46123
  "health withings status": async ({ requireClient }) => ({
44810
- data: await requireClient().get("/health/withings"),
46124
+ data: await requireClient().get(pathOf(withingsOperations.status)),
44811
46125
  render: renderRecord
44812
46126
  }),
44813
46127
  "health withings sync": async ({ requireClient }) => ({
44814
- data: await requireClient().post("/health/withings/sync", {}),
46128
+ data: await requireClient().post(pathOf(withingsOperations.sync), {}),
44815
46129
  render: renderRecord
44816
46130
  }),
44817
46131
  "health google status": async ({ requireClient }) => ({
@@ -45383,20 +46697,23 @@ var HANDLERS = {
45383
46697
  },
45384
46698
  "invite create": async ({ flags, requireClient }) => {
45385
46699
  const body = parseInput(createInviteInputSchema, { id: ulid3(), ...readDataFlag(flags) });
45386
- return { data: await requireClient().post("/invites", body), render: renderRecord };
46700
+ return {
46701
+ data: await requireClient().post(pathOf(inviteOperations.create), body),
46702
+ render: renderRecord
46703
+ };
45387
46704
  },
45388
46705
  "invite list": async ({ requireClient }) => ({
45389
- data: await requireClient().get("/invites"),
46706
+ data: await requireClient().get(pathOf(inviteOperations.list)),
45390
46707
  render: renderList
45391
46708
  }),
45392
46709
  "invite revoke": async ({ args, flags, requireClient }) => {
45393
46710
  const id = requireArg(args, 0, "invite id");
45394
46711
  assertDestructiveAllowed(flags, `revoke invite ${id}`);
45395
- await requireClient().delete(`/invites/${id}`);
46712
+ await requireClient().delete(pathOf(inviteOperations.revoke, { id }));
45396
46713
  return { data: { ok: true, revoked: id }, render: renderRecord };
45397
46714
  },
45398
46715
  "member list": async ({ requireClient }) => ({
45399
- data: await requireClient().get("/members"),
46716
+ data: await requireClient().get(pathOf(memberOperations.list)),
45400
46717
  render: renderList
45401
46718
  }),
45402
46719
  "member role": async ({ args, flags, requireClient }) => {
@@ -45404,12 +46721,15 @@ var HANDLERS = {
45404
46721
  const role = flagString(flags, "role");
45405
46722
  const raw = flagString(flags, "data") ? readDataFlag(flags) : role ? { role } : {};
45406
46723
  const body = parseInput(setMemberRoleInputSchema, raw);
45407
- return { data: await requireClient().patch(`/members/${id}/role`, body), render: renderRecord };
46724
+ return {
46725
+ data: await requireClient().patch(pathOf(memberOperations.setRole, { id }), body),
46726
+ render: renderRecord
46727
+ };
45408
46728
  },
45409
46729
  "member remove": async ({ args, flags, requireClient }) => {
45410
46730
  const id = requireArg(args, 0, "member id");
45411
46731
  assertDestructiveAllowed(flags, `remove member ${id}`);
45412
- await requireClient().delete(`/members/${id}`);
46732
+ await requireClient().delete(pathOf(memberOperations.remove, { id }));
45413
46733
  return { data: { ok: true, removed: id }, render: renderRecord };
45414
46734
  },
45415
46735
  share: async ({ args, flags, requireClient }) => {
@@ -45422,19 +46742,22 @@ var HANDLERS = {
45422
46742
  capability: flagString(flags, "cap") ?? "view"
45423
46743
  });
45424
46744
  return {
45425
- data: await requireClient().post(`/sharing/nodes/${node}/grants`, body),
46745
+ data: await requireClient().post(pathOf(sharingOperations.createGrant, { nodeId: node }), body),
45426
46746
  render: renderRecord
45427
46747
  };
45428
46748
  },
45429
46749
  "share list": async ({ args, requireClient }) => {
45430
46750
  const node = requireArg(args, 0, "node id");
45431
- return { data: await requireClient().get(`/sharing/nodes/${node}/grants`), render: renderList };
46751
+ return {
46752
+ data: await requireClient().get(pathOf(sharingOperations.listGrants, { nodeId: node })),
46753
+ render: renderList
46754
+ };
45432
46755
  },
45433
46756
  "share revoke": async ({ args, flags, requireClient }) => {
45434
46757
  const node = requireArg(args, 0, "node id");
45435
46758
  const grant = requireArg(args, 1, "grant id");
45436
46759
  assertDestructiveAllowed(flags, `revoke grant ${grant}`);
45437
- await requireClient().delete(`/sharing/nodes/${node}/grants/${grant}`);
46760
+ await requireClient().delete(pathOf(sharingOperations.revokeGrant, { nodeId: node, grantId: grant }));
45438
46761
  return { data: { ok: true, revoked: grant }, render: renderRecord };
45439
46762
  },
45440
46763
  assign: async ({ args, flags, requireClient }) => {
@@ -45446,7 +46769,7 @@ var HANDLERS = {
45446
46769
  ...flagString(flags, "cap") ? { capability: flagString(flags, "cap") } : {}
45447
46770
  });
45448
46771
  return {
45449
- data: await requireClient().put(`/sharing/nodes/${node}/assignee`, body),
46772
+ data: await requireClient().put(pathOf(sharingOperations.assign, { nodeId: node }), body),
45450
46773
  render: renderRecord
45451
46774
  };
45452
46775
  },
@@ -45456,17 +46779,20 @@ var HANDLERS = {
45456
46779
  if (body.length === 0)
45457
46780
  throw usageError('comment needs text, e.g. `do comment <node> "Nice!"`');
45458
46781
  const input = parseInput(createCommentInputSchema, { id: ulid3(), nodeId: node, body });
45459
- return { data: await requireClient().post("/comments", input), render: renderRecord };
46782
+ return {
46783
+ data: await requireClient().post(pathOf(commentOperations.create), input),
46784
+ render: renderRecord
46785
+ };
45460
46786
  },
45461
46787
  "comment list": async ({ args, requireClient }) => {
45462
46788
  const node = requireArg(args, 0, "node id");
45463
46789
  return {
45464
- data: await requireClient().get("/comments", { query: { nodeId: node } }),
46790
+ data: await requireClient().get(pathOf(commentOperations.list), { query: { nodeId: node } }),
45465
46791
  render: renderList
45466
46792
  };
45467
46793
  },
45468
46794
  "role list": async ({ requireClient }) => ({
45469
- data: await requireClient().get("/roles"),
46795
+ data: await requireClient().get(pathOf(roleOperations.list)),
45470
46796
  render: renderList
45471
46797
  }),
45472
46798
  "link create": async ({ args, flags, requireClient }) => {
@@ -45477,20 +46803,26 @@ var HANDLERS = {
45477
46803
  ...flagString(flags, "cap") ? { capability: flagString(flags, "cap") } : {},
45478
46804
  ...flagString(flags, "expires") ? { expiresAt: flagString(flags, "expires") } : {}
45479
46805
  });
45480
- return { data: await requireClient().post("/share-links", body), render: renderRecord };
46806
+ return {
46807
+ data: await requireClient().post(pathOf(shareLinkOperations.create), body),
46808
+ render: renderRecord
46809
+ };
45481
46810
  },
45482
46811
  "link list": async ({ args, requireClient }) => {
45483
46812
  const node = requireArg(args, 0, "node id");
45484
- return { data: await requireClient().get(`/share-links/node/${node}`), render: renderList };
46813
+ return {
46814
+ data: await requireClient().get(pathOf(shareLinkOperations.listForNode, { nodeId: node })),
46815
+ render: renderList
46816
+ };
45485
46817
  },
45486
46818
  "link revoke": async ({ args, flags, requireClient }) => {
45487
46819
  const id = requireArg(args, 0, "share-link id");
45488
46820
  assertDestructiveAllowed(flags, `revoke share-link ${id}`);
45489
- await requireClient().delete(`/share-links/${id}`);
46821
+ await requireClient().delete(pathOf(shareLinkOperations.revoke, { id }));
45490
46822
  return { data: { ok: true, revoked: id }, render: renderRecord };
45491
46823
  },
45492
46824
  "keys list": async ({ requireClient }) => ({
45493
- data: await requireClient().getPage("/keys"),
46825
+ data: await requireClient().getPage(pathOf(keyOperations.list)),
45494
46826
  render: renderKeys
45495
46827
  }),
45496
46828
  "keys update": async ({ args, flags, requireClient }) => {
@@ -45509,10 +46841,13 @@ var HANDLERS = {
45509
46841
  ...name === undefined ? {} : { name },
45510
46842
  ...scopes === undefined ? {} : { scopes: includePrivate ? [...scopes, "private:*"] : scopes }
45511
46843
  };
45512
- return { data: await requireClient().patch(`/keys/${id}`, body), render: renderRecord };
46844
+ return {
46845
+ data: await requireClient().patch(pathOf(keyOperations.update, { id }), body),
46846
+ render: renderRecord
46847
+ };
45513
46848
  },
45514
46849
  whoami: async ({ requireClient }) => ({
45515
- data: await requireClient().get("/whoami"),
46850
+ data: await requireClient().get(pathOf(whoamiOperations.whoami)),
45516
46851
  render: renderWhoami
45517
46852
  }),
45518
46853
  login: async ({ io, config, requireClient }) => {