@robbeverhelst/do 1.306.1 → 1.307.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/do.js +1563 -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(),
@@ -19899,6 +19928,9 @@ var loginResultSchema = object({
19899
19928
  tenantId: ulidSchema,
19900
19929
  onboardingCompleted: boolean3()
19901
19930
  });
19931
+ var googleIdTokenInputSchema = object({
19932
+ idToken: string2().min(1)
19933
+ });
19902
19934
  var registerInputSchema = object({
19903
19935
  email: string2().email(),
19904
19936
  password: string2().min(8)
@@ -24797,6 +24829,56 @@ var reviewDayStatSchema = object({
24797
24829
  });
24798
24830
  var reviewStatsSchema = array(reviewDayStatSchema);
24799
24831
 
24832
+ // ../../packages/schema/src/contracts/reads.ts
24833
+ var readWindowQuerySchema = object({
24834
+ from: instantQuerySchema.optional(),
24835
+ to: instantQuerySchema.optional()
24836
+ });
24837
+ var nowQuerySchema = readWindowQuerySchema.extend({
24838
+ for: taskWaitingOnInputSchema.optional()
24839
+ });
24840
+ var nowResponseSchema = object({
24841
+ from: exports_coerce.date(),
24842
+ to: exports_coerce.date(),
24843
+ for: actorRefSchema,
24844
+ waitingOnYou: array(taskViewSchema),
24845
+ readyForYou: array(taskViewSchema).default([]),
24846
+ timeline: todayResponseSchema
24847
+ });
24848
+ var landedByActorSchema = object({
24849
+ actor: actorRefSchema.nullable(),
24850
+ count: number2().int().nonnegative()
24851
+ });
24852
+ var landedResponseSchema = object({
24853
+ from: exports_coerce.date(),
24854
+ to: exports_coerce.date(),
24855
+ total: number2().int().nonnegative(),
24856
+ byActor: array(landedByActorSchema),
24857
+ tasks: array(taskViewSchema)
24858
+ });
24859
+ var searchNodeTypeSchema = _enum2([
24860
+ "task",
24861
+ "project",
24862
+ "area",
24863
+ "note",
24864
+ "habit",
24865
+ "routine",
24866
+ "contact",
24867
+ "collection_item"
24868
+ ]);
24869
+ var searchQuerySchema = object({
24870
+ q: string2().trim().min(1).max(200),
24871
+ 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(),
24872
+ limit: exports_coerce.number().int().min(1).max(50).default(20)
24873
+ });
24874
+ var searchHitSchema = object({
24875
+ id: string2(),
24876
+ type: searchNodeTypeSchema,
24877
+ title: string2(),
24878
+ snippet: string2().nullable(),
24879
+ updatedAt: exports_coerce.date()
24880
+ });
24881
+
24800
24882
  // ../../packages/schema/src/contracts/progress.ts
24801
24883
  var progressDateSchema = string2().regex(/^\d{4}-\d{2}-\d{2}$/, "expected YYYY-MM-DD");
24802
24884
  var progressRangeDays = [7, 30, 90];
@@ -28865,6 +28947,83 @@ var planCreateResultSchema = planViewSchema.extend({
28865
28947
  });
28866
28948
 
28867
28949
  // ../../packages/schema/src/contracts/api-key-scopes.ts
28950
+ var API_KEY_SCOPE_VALUES = [
28951
+ "webhooks:read",
28952
+ "webhooks:write",
28953
+ "tasks:read",
28954
+ "tasks:write",
28955
+ "habits:read",
28956
+ "habits:write",
28957
+ "occurrences:read",
28958
+ "occurrences:write",
28959
+ "shopping:read",
28960
+ "shopping:write",
28961
+ "inventory:read",
28962
+ "inventory:write",
28963
+ "meals:read",
28964
+ "meals:write",
28965
+ "notes:read",
28966
+ "notes:write",
28967
+ "contacts:read",
28968
+ "contacts:write",
28969
+ "projects:read",
28970
+ "projects:write",
28971
+ "areas:read",
28972
+ "areas:write",
28973
+ "routines:read",
28974
+ "routines:write",
28975
+ "settings:read",
28976
+ "settings:write",
28977
+ "calendar:read",
28978
+ "calendar:write",
28979
+ "members:read",
28980
+ "members:write",
28981
+ "sharing:read",
28982
+ "sharing:write",
28983
+ "comments:read",
28984
+ "comments:write",
28985
+ "roles:read",
28986
+ "links:read",
28987
+ "links:write",
28988
+ "collections:read",
28989
+ "collections:write",
28990
+ "presets:read",
28991
+ "presets:install",
28992
+ "export:read",
28993
+ "orca:read",
28994
+ "orca:write",
28995
+ "places:read",
28996
+ "places:write",
28997
+ "agents:read",
28998
+ "agents:write",
28999
+ "agents:converse",
29000
+ "files:read",
29001
+ "files:write",
29002
+ "key-dates:read",
29003
+ "key-dates:write",
29004
+ "documents:read",
29005
+ "documents:write",
29006
+ "periods:read",
29007
+ "periods:write",
29008
+ "planning:read",
29009
+ "planning:write",
29010
+ "animals:read",
29011
+ "animals:write",
29012
+ "keys:read",
29013
+ "keys:write",
29014
+ "health:read",
29015
+ "health:write",
29016
+ "health:export",
29017
+ "journal:read",
29018
+ "journal:write",
29019
+ "journal:export",
29020
+ "documents:private",
29021
+ "finance:read",
29022
+ "finance:write",
29023
+ "people:family-tree",
29024
+ "private:*",
29025
+ "*"
29026
+ ];
28868
29027
  var FULL_ACCESS_SCOPE = "*";
28869
29028
  function scopeGranted(granted, required) {
28870
29029
  if (granted.includes(required))
@@ -28876,6 +29035,67 @@ function scopeGranted(granted, required) {
28876
29035
  return granted.includes(FULL_ACCESS_SCOPE);
28877
29036
  }
28878
29037
 
29038
+ // ../../packages/schema/src/contracts/api-keys.ts
29039
+ var apiKeyScopeSchema = _enum2(API_KEY_SCOPE_VALUES);
29040
+ var apiKeyScopesInputSchema = array(apiKeyScopeSchema).refine((scopes) => !scopes.includes(PRIVATE_ACCESS_SCOPE) || scopes.includes(FULL_ACCESS_SCOPE), {
29041
+ message: `"${PRIVATE_ACCESS_SCOPE}" is only granted together with "${FULL_ACCESS_SCOPE}"`
29042
+ });
29043
+ var apiKeySchema = createSelectSchema(apiKeys, {
29044
+ scopes: array(apiKeyScopeSchema),
29045
+ agentId: string2().nullable().default(null),
29046
+ lastUsedAt: exports_coerce.date().nullable(),
29047
+ revokedAt: exports_coerce.date().nullable(),
29048
+ createdAt: exports_coerce.date(),
29049
+ updatedAt: exports_coerce.date()
29050
+ });
29051
+ var apiKeyPublicSchema = apiKeySchema.omit({ keyHash: true });
29052
+ var createApiKeyInputSchema = createInsertSchema(apiKeys, {
29053
+ name: string2().min(1).max(120),
29054
+ scopes: apiKeyScopesInputSchema.optional(),
29055
+ agentId: ulidSchema.nullish()
29056
+ }).omit({
29057
+ id: true,
29058
+ tenantId: true,
29059
+ userId: true,
29060
+ prefix: true,
29061
+ keyHash: true,
29062
+ lastUsedAt: true,
29063
+ revokedAt: true,
29064
+ createdAt: true,
29065
+ updatedAt: true
29066
+ }).strict();
29067
+ var createApiKeyResultSchema = apiKeyPublicSchema.extend({ key: string2() });
29068
+ var updateApiKeyInputSchema = object({
29069
+ name: string2().min(1).max(120).optional(),
29070
+ scopes: apiKeyScopesInputSchema.optional()
29071
+ }).strict().refine((input) => input.name !== undefined || input.scopes !== undefined, {
29072
+ message: "change the name, the scopes, or both"
29073
+ });
29074
+ var apiKeyChangeSchema = object({
29075
+ id: string2(),
29076
+ at: exports_coerce.date(),
29077
+ by: object({
29078
+ userId: string2().nullable(),
29079
+ apiKeyId: string2().nullable(),
29080
+ apiKeyName: string2().nullable(),
29081
+ agentId: string2().nullable(),
29082
+ agentName: string2().nullable()
29083
+ }),
29084
+ before: object({ name: string2(), scopes: array(apiKeyScopeSchema) }),
29085
+ after: object({ name: string2(), scopes: array(apiKeyScopeSchema) })
29086
+ });
29087
+ var apiKeyListItemSchema = apiKeyPublicSchema.extend({
29088
+ mine: boolean3().default(false),
29089
+ changes: array(apiKeyChangeSchema).default([])
29090
+ });
29091
+ var apiKeyContextSchema = object({
29092
+ apiKeyId: string2(),
29093
+ tenantId: string2(),
29094
+ userId: string2().nullable(),
29095
+ agentId: string2().nullable(),
29096
+ scopes: array(apiKeyScopeSchema)
29097
+ });
29098
+
28879
29099
  // ../../packages/schema/src/net/ssrf.ts
28880
29100
  var BLOCKED_HOST_SUFFIXES = [".internal", ".local", ".localhost"];
28881
29101
  var BLOCKED_HOSTNAMES = new Set(["localhost"]);
@@ -30144,6 +30364,45 @@ function routeOf(op) {
30144
30364
  return `${op.method.toUpperCase()} ${PUBLIC_API_PREFIX}${op.path}`;
30145
30365
  }
30146
30366
 
30367
+ // ../../packages/schema/src/operations/agent-connection.ts
30368
+ var notManager = "The key's member doesn't run the Space (`canManageSpace`: owner or admin).";
30369
+ var agentConnectionOperations = defineOperations({
30370
+ get: {
30371
+ method: "get",
30372
+ path: "/agent/connection",
30373
+ scopes: ["agents:read"],
30374
+ tag: "Agents",
30375
+ summary: "Get the agent connection",
30376
+ 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.",
30377
+ status: 200,
30378
+ response: { description: "The connection, without its key." }
30379
+ },
30380
+ save: {
30381
+ method: "put",
30382
+ path: "/agent/connection",
30383
+ scopes: ["agents:write"],
30384
+ tag: "Agents",
30385
+ summary: "Set the agent connection",
30386
+ 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.",
30387
+ body: saveAgentConnectionInputSchema,
30388
+ status: 200,
30389
+ response: { description: "The saved connection, without its key." },
30390
+ errors: { 400: true, 403: notManager, 404: true },
30391
+ mcp: false
30392
+ },
30393
+ remove: {
30394
+ method: "delete",
30395
+ path: "/agent/connection",
30396
+ scopes: ["agents:write"],
30397
+ tag: "Agents",
30398
+ summary: "Remove the agent connection",
30399
+ 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`).",
30400
+ status: 204,
30401
+ response: { description: "Removed." },
30402
+ errors: { 403: notManager }
30403
+ }
30404
+ });
30405
+
30147
30406
  // ../../packages/schema/src/operations/agents.ts
30148
30407
  var agentOperations = defineOperations({
30149
30408
  list: {
@@ -30737,6 +30996,182 @@ var calendarOperations = defineOperations({
30737
30996
  }
30738
30997
  });
30739
30998
 
30999
+ // ../../packages/schema/src/operations/changes.ts
31000
+ var changesOperations = defineOperations({
31001
+ list: {
31002
+ method: "get",
31003
+ path: "/changes",
31004
+ scopes: [],
31005
+ tag: "Changes",
31006
+ summary: "Read the change feed",
31007
+ 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.",
31008
+ query: [
31009
+ { name: "since", description: "A `nextCursor` from a prior page, or `now`." },
31010
+ {
31011
+ name: "limit",
31012
+ description: "Page size, 1\u2013500 (default 100; larger values are capped).",
31013
+ type: "integer",
31014
+ minimum: 1
31015
+ },
31016
+ {
31017
+ name: "entity",
31018
+ description: "Comma-separated kinds to keep: `task`, `project`, `area`, `routine`, `habit`, `occurrence`, `note`, `contact`, `collection_item`, `inventory_item`, `shopping_list`, `recipe`, `meal_plan`, `event`."
31019
+ }
31020
+ ],
31021
+ querySchema: changesQuerySchema,
31022
+ status: 200,
31023
+ response: { description: "One page of changes.", view: "ChangesPage" },
31024
+ errors: { 400: true, 403: "The key can't read any feed kind, or one it asked for." },
31025
+ operationId: "read_changes"
31026
+ }
31027
+ });
31028
+
31029
+ // ../../packages/schema/src/operations/collection.ts
31030
+ var READ2 = ["collections:read"];
31031
+ var WRITE2 = ["collections:write"];
31032
+ var entryParams = { entryId: "The entry's ULID." };
31033
+ var collectionOperations = defineOperations({
31034
+ ...crudOperations({
31035
+ segment: "collection",
31036
+ tag: "Collection",
31037
+ noun: "collection item",
31038
+ read: "collections:read",
31039
+ write: "collections:write",
31040
+ eventBase: "collection.item",
31041
+ create: createCollectionItemInputSchema,
31042
+ update: updateCollectionItemInputSchema,
31043
+ paginated: true,
31044
+ listQuery: [
31045
+ {
31046
+ name: "type",
31047
+ description: "Only items of this Type (`book`, `game`, \u2026; see `/collection/types`)."
31048
+ },
31049
+ { name: "status", description: "Only items in this status of their Type." },
31050
+ { name: "tag", description: "Only items carrying this tag." },
31051
+ {
31052
+ name: "q",
31053
+ description: "Only items whose title contains this text (case-insensitive): find an item by name."
31054
+ },
31055
+ { name: "title", description: "Only items with exactly this title (case-insensitive)." }
31056
+ ],
31057
+ listQuerySchema: paginationQuerySchema.extend({
31058
+ type: string2().optional(),
31059
+ status: string2().optional(),
31060
+ tag: string2().optional(),
31061
+ q: string2().optional(),
31062
+ title: string2().optional()
31063
+ })
31064
+ }),
31065
+ log: {
31066
+ method: "post",
31067
+ path: "/collection/log",
31068
+ scopes: WRITE2,
31069
+ tag: "Collection",
31070
+ summary: "Log a collection entry by title",
31071
+ 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`.',
31072
+ body: collectionLogInputSchema,
31073
+ status: 201,
31074
+ response: { description: "`{ created, entryId, item }`." },
31075
+ errors: { 400: true, 404: true, 409: "Several items have this title; the message names them." },
31076
+ webhook: "collection.entry.created"
31077
+ },
31078
+ types: {
31079
+ method: "get",
31080
+ path: "/collection/types",
31081
+ scopes: READ2,
31082
+ tag: "Collection",
31083
+ summary: "List Collection Types",
31084
+ description: "The code-defined Type registry (Books, Films, \u2026) as summaries.",
31085
+ status: 200,
31086
+ response: { description: "The Type registry." }
31087
+ },
31088
+ backfillCovers: {
31089
+ method: "post",
31090
+ path: "/collection/covers",
31091
+ scopes: WRITE2,
31092
+ tag: "Collection",
31093
+ summary: "Backfill covers",
31094
+ 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.",
31095
+ status: 200,
31096
+ response: { description: "`{ checked, found }`." }
31097
+ },
31098
+ cover: {
31099
+ method: "post",
31100
+ path: "/collection/{id}/cover",
31101
+ scopes: WRITE2,
31102
+ tag: "Collection",
31103
+ summary: "Find or replace an item's cover",
31104
+ 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`.",
31105
+ body: collectionCoverInputSchema,
31106
+ status: 200,
31107
+ response: { description: "`{ found, item }`." },
31108
+ errors: { 400: true, 404: true, 422: "The given image couldn't be fetched." },
31109
+ webhook: "collection.item.updated"
31110
+ },
31111
+ coverCandidates: {
31112
+ method: "get",
31113
+ path: "/collection/{id}/cover/candidates",
31114
+ scopes: READ2,
31115
+ tag: "Collection",
31116
+ summary: "List other covers for an item",
31117
+ 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 }] }`.",
31118
+ status: 200,
31119
+ response: { description: "`{ candidates: [{ url }] }`." },
31120
+ errors: { 404: true }
31121
+ },
31122
+ listEntries: {
31123
+ method: "get",
31124
+ path: "/collection/{id}/entries",
31125
+ scopes: READ2,
31126
+ tag: "Collection",
31127
+ summary: "List an item's entries",
31128
+ description: "The item's dated Entry log (ratings/notes).",
31129
+ status: 200,
31130
+ response: { description: "The item's entries." },
31131
+ errors: { 404: true }
31132
+ },
31133
+ addEntry: {
31134
+ method: "post",
31135
+ path: "/collection/{id}/entries",
31136
+ scopes: WRITE2,
31137
+ tag: "Collection",
31138
+ summary: "Append an entry",
31139
+ 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`.",
31140
+ body: createCollectionEntryInputSchema,
31141
+ status: 201,
31142
+ response: { description: "The updated item detail." },
31143
+ errors: { 400: true, 404: true },
31144
+ webhook: "collection.entry.created"
31145
+ },
31146
+ editEntry: {
31147
+ method: "patch",
31148
+ path: "/collection/{id}/entries/{entryId}",
31149
+ pathParams: entryParams,
31150
+ scopes: WRITE2,
31151
+ tag: "Collection",
31152
+ summary: "Edit a collection entry",
31153
+ 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`.",
31154
+ body: updateCollectionEntryInputSchema,
31155
+ status: 200,
31156
+ response: { description: "The updated item detail." },
31157
+ errors: { 400: true, 404: true },
31158
+ webhook: "collection.entry.updated"
31159
+ },
31160
+ removeEntry: {
31161
+ method: "delete",
31162
+ path: "/collection/{id}/entries/{entryId}",
31163
+ pathParams: entryParams,
31164
+ scopes: WRITE2,
31165
+ tag: "Collection",
31166
+ summary: "Delete an entry",
31167
+ description: "Remove one Entry; fires `collection.entry.deleted`.",
31168
+ status: 204,
31169
+ response: { description: "Deleted." },
31170
+ errors: { 404: true },
31171
+ webhook: "collection.entry.deleted"
31172
+ }
31173
+ });
31174
+
30740
31175
  // ../../packages/schema/src/operations/collection-upkeep.ts
30741
31176
  var collectionUpkeepOperations = defineOperations({
30742
31177
  list: {
@@ -30776,6 +31211,34 @@ var collectionUpkeepOperations = defineOperations({
30776
31211
  }
30777
31212
  });
30778
31213
 
31214
+ // ../../packages/schema/src/operations/comments.ts
31215
+ var commentOperations = defineOperations({
31216
+ list: {
31217
+ method: "get",
31218
+ path: "/comments",
31219
+ scopes: ["comments:read"],
31220
+ tag: "Collaboration",
31221
+ summary: "List a node's comments",
31222
+ description: "A node's comment thread (`?nodeId=`).",
31223
+ query: [{ name: "nodeId", description: "The node's ULID.", required: true }],
31224
+ status: 200,
31225
+ response: { description: "The node's comments." }
31226
+ },
31227
+ create: {
31228
+ method: "post",
31229
+ path: "/comments",
31230
+ scopes: ["comments:write"],
31231
+ tag: "Collaboration",
31232
+ summary: "Post a comment",
31233
+ description: "Add a comment to a node's thread; fires `comment.created`.",
31234
+ body: createCommentInputSchema,
31235
+ status: 201,
31236
+ response: { description: "The created comment." },
31237
+ errors: { 400: true },
31238
+ webhook: "comment.created"
31239
+ }
31240
+ });
31241
+
30779
31242
  // ../../packages/schema/src/operations/days.ts
30780
31243
  var pathParams = {
30781
31244
  date: {
@@ -31629,15 +32092,15 @@ var habitOperations = defineOperations({
31629
32092
  });
31630
32093
 
31631
32094
  // ../../packages/schema/src/operations/house.ts
31632
- var READ2 = ["collections:read"];
31633
- var WRITE2 = ["collections:write"];
32095
+ var READ3 = ["collections:read"];
32096
+ var WRITE3 = ["collections:write"];
31634
32097
  var WRITE_WITH_TASKS = ["collections:write", "tasks:write"];
31635
32098
  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
32099
  var houseOperations = defineOperations({
31637
32100
  overview: {
31638
32101
  method: "get",
31639
32102
  path: "/house",
31640
- scopes: READ2,
32103
+ scopes: READ3,
31641
32104
  tag: "House",
31642
32105
  summary: "The House home",
31643
32106
  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 +32110,7 @@ var houseOperations = defineOperations({
31647
32110
  value: {
31648
32111
  method: "get",
31649
32112
  path: "/house/value",
31650
- scopes: READ2,
32113
+ scopes: READ3,
31651
32114
  tag: "House",
31652
32115
  summary: "The value inventory",
31653
32116
  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 +32120,7 @@ var houseOperations = defineOperations({
31657
32120
  year: {
31658
32121
  method: "get",
31659
32122
  path: "/house/upkeep",
31660
- scopes: READ2,
32123
+ scopes: READ3,
31661
32124
  tag: "House",
31662
32125
  summary: "The upkeep year",
31663
32126
  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 +32135,7 @@ var houseOperations = defineOperations({
31672
32135
  asset: {
31673
32136
  method: "get",
31674
32137
  path: "/house/assets/{id}",
31675
- scopes: READ2,
32138
+ scopes: READ3,
31676
32139
  tag: "House",
31677
32140
  summary: "One asset's House page",
31678
32141
  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 +32146,7 @@ var houseOperations = defineOperations({
31683
32146
  linkPart: {
31684
32147
  method: "post",
31685
32148
  path: "/house/assets/{id}/parts",
31686
- scopes: WRITE2,
32149
+ scopes: WRITE3,
31687
32150
  tag: "House",
31688
32151
  summary: "Tie a part to an asset",
31689
32152
  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 +32159,7 @@ var houseOperations = defineOperations({
31696
32159
  method: "delete",
31697
32160
  path: "/house/assets/{id}/parts/{inventoryItemId}",
31698
32161
  pathParams: { inventoryItemId: "The Supply item's ULID." },
31699
- scopes: WRITE2,
32162
+ scopes: WRITE3,
31700
32163
  tag: "House",
31701
32164
  summary: "Untie a part from an asset",
31702
32165
  description: "The Supply item stays; only the link goes. Returns the asset page.",
@@ -31707,7 +32170,7 @@ var houseOperations = defineOperations({
31707
32170
  lend: {
31708
32171
  method: "post",
31709
32172
  path: "/house/assets/{id}/lend",
31710
- scopes: WRITE2,
32173
+ scopes: WRITE3,
31711
32174
  tag: "House",
31712
32175
  summary: "Lend a thing, or note one borrowed (asset_lend)",
31713
32176
  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 +32182,7 @@ var houseOperations = defineOperations({
31719
32182
  giveBack: {
31720
32183
  method: "post",
31721
32184
  path: "/house/assets/{id}/return",
31722
- scopes: WRITE2,
32185
+ scopes: WRITE3,
31723
32186
  tag: "House",
31724
32187
  summary: "It's back (asset_return)",
31725
32188
  description: "Clears the Item's `loan` and logs a `returned` Entry (`on` default today, optional `note`). 409 when it isn't out.",
@@ -31796,6 +32259,73 @@ var inventoryItemOperations = defineOperations({
31796
32259
  })
31797
32260
  });
31798
32261
 
32262
+ // ../../packages/schema/src/operations/invites.ts
32263
+ var inviteOperations = defineOperations({
32264
+ list: {
32265
+ method: "get",
32266
+ path: "/invites",
32267
+ scopes: ["members:read"],
32268
+ tag: "Collaboration",
32269
+ summary: "List invites",
32270
+ description: "The Space's pending invitations.",
32271
+ status: 200,
32272
+ response: { description: "The invites." }
32273
+ },
32274
+ create: {
32275
+ method: "post",
32276
+ path: "/invites",
32277
+ scopes: ["members:write"],
32278
+ tag: "Collaboration",
32279
+ summary: "Create an invite",
32280
+ description: "Invite someone to the Space; fires `invite.created`.",
32281
+ body: createInviteInputSchema,
32282
+ status: 201,
32283
+ response: { description: "The created invite." },
32284
+ errors: { 400: true },
32285
+ webhook: "invite.created"
32286
+ },
32287
+ revoke: {
32288
+ method: "delete",
32289
+ path: "/invites/{id}",
32290
+ scopes: ["members:write"],
32291
+ tag: "Collaboration",
32292
+ summary: "Revoke an invite",
32293
+ description: "Revoke a pending invitation; fires `invite.revoked`.",
32294
+ status: 204,
32295
+ response: { description: "Revoked." },
32296
+ errors: { 404: true },
32297
+ webhook: "invite.revoked"
32298
+ }
32299
+ });
32300
+
32301
+ // ../../packages/schema/src/operations/keys.ts
32302
+ var keyOperations = defineOperations({
32303
+ list: {
32304
+ method: "get",
32305
+ path: "/keys",
32306
+ scopes: ["keys:read"],
32307
+ tag: "Identity",
32308
+ summary: "List your API keys",
32309
+ description: "The keys the member behind this key minted (never a secret), newest first, each with its recorded changes.",
32310
+ status: 200,
32311
+ response: { description: "The keys." },
32312
+ errors: { 403: true }
32313
+ },
32314
+ update: {
32315
+ method: "patch",
32316
+ path: "/keys/{id}",
32317
+ scopes: ["keys:write"],
32318
+ tag: "Identity",
32319
+ summary: "Change a key's name or scopes",
32320
+ 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.",
32321
+ body: updateApiKeyInputSchema,
32322
+ status: 200,
32323
+ response: { description: "The key, changed." },
32324
+ errors: { 400: true, 403: true, 404: true },
32325
+ throttle: "strict"
32326
+ }
32327
+ });
32328
+
31799
32329
  // ../../packages/schema/src/operations/mail-campaigns.ts
31800
32330
  var pathParams2 = { id: "The campaign's ULID." };
31801
32331
  var mailCampaignOperations = defineOperations({
@@ -31878,6 +32408,45 @@ var mealPlanOperations = defineOperations({
31878
32408
  })
31879
32409
  });
31880
32410
 
32411
+ // ../../packages/schema/src/operations/members.ts
32412
+ var memberOperations = defineOperations({
32413
+ list: {
32414
+ method: "get",
32415
+ path: "/members",
32416
+ scopes: ["members:read"],
32417
+ tag: "Collaboration",
32418
+ summary: "List members",
32419
+ description: "The Space roster.",
32420
+ status: 200,
32421
+ response: { description: "The members." }
32422
+ },
32423
+ setRole: {
32424
+ method: "patch",
32425
+ path: "/members/{id}/role",
32426
+ scopes: ["members:write"],
32427
+ tag: "Collaboration",
32428
+ summary: "Set a member's role",
32429
+ description: "Change a member's role; fires `member.role.updated`.",
32430
+ body: setMemberRoleInputSchema,
32431
+ status: 200,
32432
+ response: { description: "The updated member." },
32433
+ errors: { 400: true, 404: true },
32434
+ webhook: "member.role.updated"
32435
+ },
32436
+ remove: {
32437
+ method: "delete",
32438
+ path: "/members/{id}",
32439
+ scopes: ["members:write"],
32440
+ tag: "Collaboration",
32441
+ summary: "Remove a member",
32442
+ description: "Remove a member from the Space; fires `member.removed`.",
32443
+ status: 204,
32444
+ response: { description: "Removed." },
32445
+ errors: { 404: true },
32446
+ webhook: "member.removed"
32447
+ }
32448
+ });
32449
+
31881
32450
  // ../../packages/schema/src/operations/metrics.ts
31882
32451
  var NO_MEMBER2 = "The key acts for no member.";
31883
32452
  var metricOperations = defineOperations({
@@ -31944,6 +32513,51 @@ var metricOperations = defineOperations({
31944
32513
  }
31945
32514
  });
31946
32515
 
32516
+ // ../../packages/schema/src/operations/nodes.ts
32517
+ var pathParams3 = { id: "The owner node's ULID (any node type)." };
32518
+ var ineligible = "The node is unknown, deleted, hidden from this Space, or can't carry a note (a `note` or `event` node).";
32519
+ var nodeOperations = defineOperations({
32520
+ getNotes: {
32521
+ method: "get",
32522
+ path: "/nodes/{id}/notes",
32523
+ pathParams: pathParams3,
32524
+ scopes: ["notes:read"],
32525
+ tag: "Notes",
32526
+ summary: "Get a node's attached note",
32527
+ description: "The attached note of any node as `{ nodeId, noteId, body, updatedAt }`; a node with no note yet returns nulls.",
32528
+ status: 200,
32529
+ response: { description: "The attached-note view." },
32530
+ errors: { 404: ineligible }
32531
+ },
32532
+ setNotes: {
32533
+ method: "put",
32534
+ path: "/nodes/{id}/notes",
32535
+ pathParams: pathParams3,
32536
+ scopes: ["notes:write"],
32537
+ tag: "Notes",
32538
+ summary: "Set a node's attached note",
32539
+ 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).",
32540
+ body: setAttachedNoteInputSchema,
32541
+ status: 200,
32542
+ response: { description: "The attached-note view." },
32543
+ errors: { 400: true, 404: ineligible },
32544
+ webhook: "note.updated"
32545
+ },
32546
+ clearNotes: {
32547
+ method: "delete",
32548
+ path: "/nodes/{id}/notes",
32549
+ pathParams: pathParams3,
32550
+ scopes: ["notes:write"],
32551
+ tag: "Notes",
32552
+ summary: "Clear a node's attached note",
32553
+ 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.",
32554
+ status: 204,
32555
+ response: { description: "Cleared." },
32556
+ errors: { 404: ineligible },
32557
+ webhook: "note.deleted"
32558
+ }
32559
+ });
32560
+
31947
32561
  // ../../packages/schema/src/operations/occurrences.ts
31948
32562
  var occurrenceOperations = defineOperations({
31949
32563
  list: {
@@ -31986,6 +32600,78 @@ var occurrenceOperations = defineOperations({
31986
32600
  }
31987
32601
  });
31988
32602
 
32603
+ // ../../packages/schema/src/operations/orca.ts
32604
+ var host = "The Orca host (e.g. `theseus`).";
32605
+ var handle = "The session's handle (`term_\u2026`).";
32606
+ var notConnected = "Orca isn't connected to this Space, or the bridge doesn't know that host or session.";
32607
+ var unavailable = "Orca unreachable: the orca-bridge didn't answer.";
32608
+ var orcaOperations = defineOperations({
32609
+ overview: {
32610
+ method: "get",
32611
+ path: "/orca",
32612
+ scopes: ["orca:read"],
32613
+ tag: "Orca",
32614
+ summary: "List Orca worktrees",
32615
+ 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`.",
32616
+ status: 200,
32617
+ response: { description: "The Orca overview." }
32618
+ },
32619
+ output: {
32620
+ method: "get",
32621
+ path: "/orca/hosts/{host}/terminals/{handle}/output",
32622
+ pathParams: { host, handle },
32623
+ scopes: ["orca:read"],
32624
+ tag: "Orca",
32625
+ summary: "Read an Orca session",
32626
+ description: "A session's live output; pass `nextCursor` back as `cursor` for only what's new, or `screen=true` for the rendered frame.",
32627
+ query: [
32628
+ { name: "cursor", description: "Read from this cursor (a prior `nextCursor`)." },
32629
+ { name: "limit", description: "At most this many lines (1\u20132000).", type: "integer" },
32630
+ { name: "screen", description: "`true` reads the rendered screen instead of the stream." }
32631
+ ],
32632
+ querySchema: orcaReadOutputQuerySchema,
32633
+ status: 200,
32634
+ response: { description: "The output." },
32635
+ errors: { 400: true, 404: notConnected, 503: unavailable }
32636
+ },
32637
+ send: {
32638
+ method: "post",
32639
+ path: "/orca/hosts/{host}/terminals/{handle}/messages",
32640
+ pathParams: { host, handle },
32641
+ scopes: ["orca:write"],
32642
+ tag: "Orca",
32643
+ summary: "Send to an Orca session",
32644
+ 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.",
32645
+ body: orcaSendMessageInputSchema,
32646
+ status: 200,
32647
+ response: { description: "Orca accepted the input." },
32648
+ errors: {
32649
+ 400: true,
32650
+ 404: notConnected,
32651
+ 428: "`confirmation_required`: the member must confirm an Agent's send.",
32652
+ 503: unavailable
32653
+ }
32654
+ },
32655
+ restart: {
32656
+ method: "post",
32657
+ path: "/orca/hosts/{host}/sessions",
32658
+ pathParams: { host },
32659
+ scopes: ["orca:write"],
32660
+ tag: "Orca",
32661
+ summary: "Restart an Orca session",
32662
+ 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`).",
32663
+ body: orcaRestartSessionInputSchema,
32664
+ status: 200,
32665
+ response: { description: "Orca started the session; its new `handle`." },
32666
+ errors: {
32667
+ 400: true,
32668
+ 404: notConnected,
32669
+ 428: "`confirmation_required`: the member must confirm an Agent's restart.",
32670
+ 503: unavailable
32671
+ }
32672
+ }
32673
+ });
32674
+
31989
32675
  // ../../packages/schema/src/operations/periods.ts
31990
32676
  var periodOperations = defineOperations({
31991
32677
  ...crudOperations({
@@ -32249,6 +32935,149 @@ var progressOperations = defineOperations({
32249
32935
  }
32250
32936
  });
32251
32937
 
32938
+ // ../../packages/schema/src/operations/projects.ts
32939
+ var INSTANT = "an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight)";
32940
+ var projectOperations = defineOperations({
32941
+ ...crudOperations({
32942
+ segment: "projects",
32943
+ tag: "Projects",
32944
+ noun: "project",
32945
+ read: "projects:read",
32946
+ write: "projects:write",
32947
+ create: createProjectInputSchema,
32948
+ update: updateProjectInputSchema,
32949
+ paginated: true,
32950
+ listQuery: [
32951
+ { name: "areaId", description: "Only rows in this Area (ULID)." },
32952
+ {
32953
+ name: "status",
32954
+ description: `Only rows in any of these statuses (comma list or repeated key): ${projectStatusSchema.options.map((v) => `\`${v}\``).join(", ")}.`
32955
+ },
32956
+ {
32957
+ name: "updatedSince",
32958
+ description: `Only rows whose \`updatedAt\` is at or after this instant \u2014 ${INSTANT}.`,
32959
+ format: "date-time"
32960
+ },
32961
+ {
32962
+ name: "dueBefore",
32963
+ description: `Only rows due strictly before this instant (undated rows are excluded) \u2014 ${INSTANT}.`,
32964
+ format: "date-time"
32965
+ }
32966
+ ],
32967
+ listQuerySchema: projectListQuerySchema.extend(paginationQuerySchema.shape)
32968
+ }),
32969
+ attachTask: {
32970
+ method: "post",
32971
+ path: "/projects/{id}/tasks",
32972
+ scopes: ["projects:write"],
32973
+ tag: "Projects",
32974
+ summary: "Attach a task",
32975
+ description: "Attach an existing task to the project; fires `project.task.attached`.",
32976
+ body: attachTaskInputSchema,
32977
+ status: 204,
32978
+ response: { description: "Attached." },
32979
+ errors: { 400: true, 404: true },
32980
+ webhook: "project.task.attached"
32981
+ },
32982
+ detachTask: {
32983
+ method: "delete",
32984
+ path: "/projects/{id}/tasks/{taskId}",
32985
+ pathParams: { taskId: "The task's ULID." },
32986
+ scopes: ["projects:write"],
32987
+ tag: "Projects",
32988
+ summary: "Detach a task",
32989
+ description: "Detach a task from the project; fires `project.task.detached`.",
32990
+ status: 204,
32991
+ response: { description: "Detached." },
32992
+ errors: { 404: true },
32993
+ webhook: "project.task.detached"
32994
+ }
32995
+ });
32996
+
32997
+ // ../../packages/schema/src/operations/reads.ts
32998
+ var INSTANT2 = "an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight)";
32999
+ var windowParams = [
33000
+ {
33001
+ name: "from",
33002
+ description: `Window start \u2014 ${INSTANT2}. Default: today's start, UTC.`,
33003
+ format: "date-time"
33004
+ },
33005
+ {
33006
+ name: "to",
33007
+ description: `Window end (exclusive) \u2014 ${INSTANT2}. Default: a day after \`from\`.`,
33008
+ format: "date-time"
33009
+ }
33010
+ ];
33011
+ var readOperations = defineOperations({
33012
+ now: {
33013
+ method: "get",
33014
+ path: "/now",
33015
+ scopes: ["tasks:read"],
33016
+ tag: "Reads",
33017
+ summary: "What needs me now",
33018
+ 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).",
33019
+ query: [
33020
+ {
33021
+ name: "for",
33022
+ description: "Whose waiting list: `me` (default), `member:<id|email>` or `agent:<id|name>`."
33023
+ },
33024
+ ...windowParams
33025
+ ],
33026
+ querySchema: nowQuerySchema,
33027
+ status: 200,
33028
+ response: { description: "The Now read.", view: "NowResponse" },
33029
+ errors: { 400: true },
33030
+ operationId: "get_now"
33031
+ },
33032
+ landed: {
33033
+ method: "get",
33034
+ path: "/landed",
33035
+ scopes: ["tasks:read"],
33036
+ tag: "Reads",
33037
+ summary: "What landed",
33038
+ 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.",
33039
+ query: windowParams,
33040
+ querySchema: readWindowQuerySchema,
33041
+ status: 200,
33042
+ response: { description: "The Landed read.", view: "LandedResponse" },
33043
+ operationId: "get_landed"
33044
+ },
33045
+ review: {
33046
+ method: "get",
33047
+ path: "/review",
33048
+ scopes: ["tasks:read"],
33049
+ tag: "Reads",
33050
+ summary: "Review tallies",
33051
+ description: "Per-day completion tallies over the window: tasks (by `completedAt`) and habit/routine occurrences.",
33052
+ query: windowParams,
33053
+ querySchema: readWindowQuerySchema,
33054
+ status: 200,
33055
+ response: { description: "One entry per day of the window." },
33056
+ operationId: "get_review"
33057
+ },
33058
+ search: {
33059
+ method: "get",
33060
+ path: "/search",
33061
+ scopes: [],
33062
+ tag: "Reads",
33063
+ summary: "Search the Space",
33064
+ 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.",
33065
+ query: [
33066
+ { name: "q", description: "The text to find (1\u2013200 chars)." },
33067
+ {
33068
+ name: "type",
33069
+ description: "Only these node types (comma list): `task`, `project`, `area`, `note`, `habit`, `routine`, `contact`, `collection_item`."
33070
+ },
33071
+ { name: "limit", description: "Max hits, 1\u201350 (default 20).", type: "integer", minimum: 1 }
33072
+ ],
33073
+ querySchema: searchQuerySchema,
33074
+ status: 200,
33075
+ response: { description: "The hits.", list: true, view: "SearchHit" },
33076
+ errors: { 400: true, 403: "The key can read none of the requested node types." },
33077
+ operationId: "search"
33078
+ }
33079
+ });
33080
+
32252
33081
  // ../../packages/schema/src/operations/recipes.ts
32253
33082
  var recipe = { id: "The recipe's ULID." };
32254
33083
  var recipeOperations = defineOperations({
@@ -32303,6 +33132,40 @@ var recipeOperations = defineOperations({
32303
33132
  }
32304
33133
  });
32305
33134
 
33135
+ // ../../packages/schema/src/operations/roles.ts
33136
+ var roleOperations = defineOperations({
33137
+ listPermissionSets: {
33138
+ method: "get",
33139
+ path: "/roles/permission-sets",
33140
+ scopes: ["roles:read"],
33141
+ tag: "Collaboration",
33142
+ summary: "List permission sets",
33143
+ description: "The Space's permission sets.",
33144
+ status: 200,
33145
+ response: { description: "The permission sets." }
33146
+ },
33147
+ listMembers: {
33148
+ method: "get",
33149
+ path: "/roles/members",
33150
+ scopes: ["roles:read"],
33151
+ tag: "Collaboration",
33152
+ summary: "List role assignments",
33153
+ description: "Each member's role assignment.",
33154
+ status: 200,
33155
+ response: { description: "The role assignments." }
33156
+ },
33157
+ list: {
33158
+ method: "get",
33159
+ path: "/roles",
33160
+ scopes: ["roles:read"],
33161
+ tag: "Collaboration",
33162
+ summary: "List roles",
33163
+ description: "The Space's roles.",
33164
+ status: 200,
33165
+ response: { description: "The roles." }
33166
+ }
33167
+ });
33168
+
32306
33169
  // ../../packages/schema/src/operations/routines.ts
32307
33170
  var routineOperations = defineOperations({
32308
33171
  getRun: {
@@ -32621,6 +33484,210 @@ var rsvpQuestionOperations = defineOperations({
32621
33484
  }
32622
33485
  });
32623
33486
 
33487
+ // ../../packages/schema/src/operations/runs.ts
33488
+ var READ4 = ["collections:read"];
33489
+ var WRITE4 = ["collections:write"];
33490
+ var runOperations = defineOperations({
33491
+ list: {
33492
+ method: "get",
33493
+ path: "/runs",
33494
+ scopes: READ4,
33495
+ tag: "Runs",
33496
+ summary: "List game runs",
33497
+ description: "The Space's runs, last touched first, each with its game, its facets, `lastPlayedOn` and `sessionCount`.",
33498
+ query: [
33499
+ { name: "gameId", description: "Only the runs of this game (a Collection Item id)." },
33500
+ {
33501
+ name: "status",
33502
+ description: "`planned`, `playing`, `paused`, `blocked`, `finished` or `dropped`."
33503
+ },
33504
+ {
33505
+ name: "theme",
33506
+ description: "Only runs skinned by this theme pack (`skyrim`, `pokemon`, \u2026)."
33507
+ }
33508
+ ],
33509
+ querySchema: runsQuerySchema,
33510
+ status: 200,
33511
+ response: { description: "The runs." }
33512
+ },
33513
+ create: {
33514
+ method: "post",
33515
+ path: "/runs",
33516
+ scopes: WRITE4,
33517
+ tag: "Runs",
33518
+ summary: "Start a game run",
33519
+ 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`.",
33520
+ body: createRunInputSchema,
33521
+ status: 201,
33522
+ response: { description: "The run." },
33523
+ errors: { 400: true, 404: true },
33524
+ webhook: "run.created"
33525
+ },
33526
+ steamStatus: {
33527
+ method: "get",
33528
+ path: "/runs/steam",
33529
+ scopes: READ4,
33530
+ tag: "Runs",
33531
+ summary: "Get the Steam achievements link",
33532
+ 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.",
33533
+ status: 200,
33534
+ response: { description: "The link's status." },
33535
+ errors: { 403: true }
33536
+ },
33537
+ steamSync: {
33538
+ method: "post",
33539
+ path: "/runs/steam/sync",
33540
+ scopes: WRITE4,
33541
+ tag: "Runs",
33542
+ summary: "Sync Steam achievements now",
33543
+ 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.",
33544
+ body: steamSyncInputSchema,
33545
+ status: 200,
33546
+ response: { description: "What the sync did, game by game." },
33547
+ errors: { 400: true, 403: true, 409: true }
33548
+ },
33549
+ get: {
33550
+ method: "get",
33551
+ path: "/runs/{id}",
33552
+ scopes: READ4,
33553
+ tag: "Runs",
33554
+ summary: "Get a game run",
33555
+ description: "The run with its sessions, newest first; each session has its recap (`note`), `minutes` and the agent's change `batches`.",
33556
+ status: 200,
33557
+ response: { description: "The run." },
33558
+ errors: { 404: true }
33559
+ },
33560
+ update: {
33561
+ method: "patch",
33562
+ path: "/runs/{id}",
33563
+ scopes: WRITE4,
33564
+ tag: "Runs",
33565
+ summary: "Edit a game run",
33566
+ description: "Rename it, move its status, or replace `facet` whole. For one change at a time use `/changes`. Fires `run.updated`.",
33567
+ body: updateRunInputSchema,
33568
+ status: 200,
33569
+ response: { description: "The run." },
33570
+ errors: { 400: true, 404: true },
33571
+ webhook: "run.updated"
33572
+ },
33573
+ remove: {
33574
+ method: "delete",
33575
+ path: "/runs/{id}",
33576
+ scopes: WRITE4,
33577
+ tag: "Runs",
33578
+ summary: "Delete a game run",
33579
+ description: "Soft-deletes the run. Fires `run.deleted`.",
33580
+ status: 204,
33581
+ response: { description: "Deleted." },
33582
+ errors: { 404: true },
33583
+ webhook: "run.deleted"
33584
+ },
33585
+ applyChanges: {
33586
+ method: "post",
33587
+ path: "/runs/{id}/changes",
33588
+ scopes: WRITE4,
33589
+ tag: "Runs",
33590
+ summary: "Apply a run chat update",
33591
+ 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`.',
33592
+ body: applyRunChangesInputSchema,
33593
+ status: 201,
33594
+ response: { description: "`{ run, sessionId, batch }`." },
33595
+ errors: { 400: true, 404: true },
33596
+ webhook: "run.updated"
33597
+ },
33598
+ undo: {
33599
+ method: "post",
33600
+ path: "/runs/{id}/undo",
33601
+ scopes: WRITE4,
33602
+ tag: "Runs",
33603
+ summary: "Undo a run chat update",
33604
+ 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`.",
33605
+ body: undoRunChangesInputSchema,
33606
+ status: 200,
33607
+ response: { description: "The run." },
33608
+ errors: { 400: true, 404: true },
33609
+ webhook: "run.updated"
33610
+ },
33611
+ uploadSaveHeader: {
33612
+ method: "post",
33613
+ path: "/runs/{id}/save-header",
33614
+ scopes: WRITE4,
33615
+ tag: "Runs",
33616
+ summary: "Send a game save's header to a run",
33617
+ 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`.",
33618
+ body: uploadRunSaveHeaderInputSchema,
33619
+ status: 200,
33620
+ response: { description: "`{ run, unchanged, sessionId, batch }`." },
33621
+ errors: { 400: true, 403: "A read-only key, or a key with no member.", 404: true },
33622
+ webhook: "run.updated"
33623
+ },
33624
+ tickChecklist: {
33625
+ method: "post",
33626
+ path: "/runs/{id}/checklists/{key}/tick",
33627
+ pathParams: { key: "The checklist's key (`dex`, `masks`)." },
33628
+ scopes: WRITE4,
33629
+ tag: "Runs",
33630
+ summary: "Tick a run checklist in bulk",
33631
+ 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`.",
33632
+ body: tickRunChecklistInputSchema,
33633
+ status: 201,
33634
+ response: { description: "`{ run, sessionId, batch }`." },
33635
+ errors: { 400: true, 404: true },
33636
+ webhook: "run.updated"
33637
+ },
33638
+ listSessions: {
33639
+ method: "get",
33640
+ path: "/runs/{id}/sessions",
33641
+ scopes: READ4,
33642
+ tag: "Runs",
33643
+ summary: "List a run's sessions",
33644
+ description: "Newest first, each with its recap, minutes and change batches.",
33645
+ status: 200,
33646
+ response: { description: "The sessions." },
33647
+ errors: { 404: true }
33648
+ },
33649
+ addSession: {
33650
+ method: "post",
33651
+ path: "/runs/{id}/sessions",
33652
+ scopes: WRITE4,
33653
+ tag: "Runs",
33654
+ summary: "Log a run session",
33655
+ 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`.",
33656
+ body: createRunSessionInputSchema,
33657
+ status: 201,
33658
+ response: { description: "The run." },
33659
+ errors: { 400: true, 404: true },
33660
+ webhook: "run.session.created"
33661
+ },
33662
+ updateSession: {
33663
+ method: "patch",
33664
+ path: "/runs/{id}/sessions/{sessionId}",
33665
+ pathParams: { sessionId: "The session's ULID." },
33666
+ scopes: WRITE4,
33667
+ tag: "Runs",
33668
+ summary: "Edit a run session",
33669
+ description: "Fix its day, recap or minutes (`null` clears the recap or minutes). Returns the run; fires `run.session.updated`.",
33670
+ body: updateRunSessionInputSchema,
33671
+ status: 200,
33672
+ response: { description: "The run." },
33673
+ errors: { 400: true, 404: true },
33674
+ webhook: "run.session.updated"
33675
+ },
33676
+ removeSession: {
33677
+ method: "delete",
33678
+ path: "/runs/{id}/sessions/{sessionId}",
33679
+ pathParams: { sessionId: "The session's ULID." },
33680
+ scopes: WRITE4,
33681
+ tag: "Runs",
33682
+ summary: "Delete a run session",
33683
+ description: "Its changes stay applied. Fires `run.session.deleted`.",
33684
+ status: 204,
33685
+ response: { description: "Deleted." },
33686
+ errors: { 404: true },
33687
+ webhook: "run.session.deleted"
33688
+ }
33689
+ });
33690
+
32624
33691
  // ../../packages/schema/src/operations/seating-proposals.ts
32625
33692
  var proposal = { id: "The proposal's ULID." };
32626
33693
  var seatingProposalOperations = defineOperations({
@@ -32823,6 +33890,141 @@ var settingsOperations = defineOperations({
32823
33890
  }
32824
33891
  });
32825
33892
 
33893
+ // ../../packages/schema/src/operations/share-links.ts
33894
+ var shareLinkOperations = defineOperations({
33895
+ listForNode: {
33896
+ method: "get",
33897
+ path: "/share-links/node/{nodeId}",
33898
+ pathParams: { nodeId: "The node's ULID." },
33899
+ scopes: ["links:read"],
33900
+ tag: "Collaboration",
33901
+ summary: "List a node's share-links",
33902
+ description: "Public, scoped links minted for a node.",
33903
+ status: 200,
33904
+ response: { description: "The node's share-links." },
33905
+ errors: { 404: true }
33906
+ },
33907
+ get: {
33908
+ method: "get",
33909
+ path: "/share-links/{id}",
33910
+ scopes: ["links:read"],
33911
+ tag: "Collaboration",
33912
+ summary: "Get a share-link",
33913
+ description: "One share-link by id.",
33914
+ status: 200,
33915
+ response: { description: "The share-link." },
33916
+ errors: { 404: true }
33917
+ },
33918
+ create: {
33919
+ method: "post",
33920
+ path: "/share-links",
33921
+ scopes: ["links:write"],
33922
+ tag: "Collaboration",
33923
+ summary: "Mint a share-link",
33924
+ description: "Mint a public, scoped link to a node; fires `share-link.created`.",
33925
+ body: createShareLinkInputSchema,
33926
+ status: 201,
33927
+ response: { description: "The created share-link." },
33928
+ errors: { 400: true },
33929
+ webhook: "share-link.created"
33930
+ },
33931
+ revoke: {
33932
+ method: "delete",
33933
+ path: "/share-links/{id}",
33934
+ scopes: ["links:write"],
33935
+ tag: "Collaboration",
33936
+ summary: "Revoke a share-link",
33937
+ description: "Revoke a public share-link; fires `share-link.revoked`.",
33938
+ status: 204,
33939
+ response: { description: "Revoked." },
33940
+ errors: { 404: true },
33941
+ webhook: "share-link.revoked"
33942
+ }
33943
+ });
33944
+
33945
+ // ../../packages/schema/src/operations/sharing.ts
33946
+ var nodeId = "The node's ULID.";
33947
+ var grantId = "The grant's ULID.";
33948
+ var sharingOperations = defineOperations({
33949
+ listSubjects: {
33950
+ method: "get",
33951
+ path: "/sharing/subjects",
33952
+ scopes: ["sharing:read"],
33953
+ tag: "Collaboration",
33954
+ summary: "List grantable subjects",
33955
+ description: "The members/roles a node can be shared with.",
33956
+ status: 200,
33957
+ response: { description: "The subjects." }
33958
+ },
33959
+ listGrants: {
33960
+ method: "get",
33961
+ path: "/sharing/nodes/{nodeId}/grants",
33962
+ pathParams: { nodeId },
33963
+ scopes: ["sharing:read"],
33964
+ tag: "Collaboration",
33965
+ summary: "List a node's grants",
33966
+ description: "Who a node is shared with, and at what level.",
33967
+ status: 200,
33968
+ response: { description: "The node's grants." },
33969
+ errors: { 404: true }
33970
+ },
33971
+ createGrant: {
33972
+ method: "post",
33973
+ path: "/sharing/nodes/{nodeId}/grants",
33974
+ pathParams: { nodeId },
33975
+ scopes: ["sharing:write"],
33976
+ tag: "Collaboration",
33977
+ summary: "Grant access to a node",
33978
+ description: "Share a node with a member/role; fires `node.grant.created`.",
33979
+ body: createNodeGrantInputSchema,
33980
+ status: 201,
33981
+ response: { description: "The created grant." },
33982
+ errors: { 400: true, 404: true },
33983
+ webhook: "node.grant.created"
33984
+ },
33985
+ updateGrant: {
33986
+ method: "patch",
33987
+ path: "/sharing/nodes/{nodeId}/grants/{grantId}",
33988
+ pathParams: { nodeId, grantId },
33989
+ scopes: ["sharing:write"],
33990
+ tag: "Collaboration",
33991
+ summary: "Update a grant",
33992
+ description: "Change a grant's access level; fires `node.grant.updated`.",
33993
+ body: updateNodeGrantInputSchema,
33994
+ status: 200,
33995
+ response: { description: "The updated grant." },
33996
+ errors: { 400: true, 404: true },
33997
+ webhook: "node.grant.updated"
33998
+ },
33999
+ revokeGrant: {
34000
+ method: "delete",
34001
+ path: "/sharing/nodes/{nodeId}/grants/{grantId}",
34002
+ pathParams: { nodeId, grantId },
34003
+ scopes: ["sharing:write"],
34004
+ tag: "Collaboration",
34005
+ summary: "Revoke a grant",
34006
+ description: "Revoke a node grant; fires `node.grant.revoked`.",
34007
+ status: 204,
34008
+ response: { description: "Revoked." },
34009
+ errors: { 404: true },
34010
+ webhook: "node.grant.revoked"
34011
+ },
34012
+ assign: {
34013
+ method: "put",
34014
+ path: "/sharing/nodes/{nodeId}/assignee",
34015
+ pathParams: { nodeId },
34016
+ scopes: ["sharing:write"],
34017
+ tag: "Collaboration",
34018
+ summary: "Assign a node",
34019
+ description: "Set a node's assignee; fires `node.assigned`.",
34020
+ body: assignNodeInputSchema,
34021
+ status: 200,
34022
+ response: { description: "The assignment grant." },
34023
+ errors: { 400: true, 404: true },
34024
+ webhook: "node.assigned"
34025
+ }
34026
+ });
34027
+
32826
34028
  // ../../packages/schema/src/operations/shopping-lists.ts
32827
34029
  var shoppingListOperations = defineOperations({
32828
34030
  ...crudOperations({
@@ -32839,14 +34041,14 @@ var shoppingListOperations = defineOperations({
32839
34041
  });
32840
34042
 
32841
34043
  // ../../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}`)." };
34044
+ var READ5 = ["habits:read", "routines:read"];
34045
+ var WRITE5 = ["habits:write", "routines:write"];
34046
+ var pathParams4 = { id: "The tag's id: the ULID the sticker's link carries (`/t/{id}`)." };
32845
34047
  var tapTagOperations = defineOperations({
32846
34048
  list: {
32847
34049
  method: "get",
32848
34050
  path: "/tap-tags",
32849
- scopes: READ3,
34051
+ scopes: READ5,
32850
34052
  tag: "Habits",
32851
34053
  summary: "List tap tags",
32852
34054
  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 +34059,7 @@ var tapTagOperations = defineOperations({
32857
34059
  create: {
32858
34060
  method: "post",
32859
34061
  path: "/tap-tags",
32860
- scopes: WRITE3,
34062
+ scopes: WRITE5,
32861
34063
  tag: "Habits",
32862
34064
  summary: "Make a tap tag",
32863
34065
  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 +34071,8 @@ var tapTagOperations = defineOperations({
32869
34071
  get: {
32870
34072
  method: "get",
32871
34073
  path: "/tap-tags/{id}",
32872
- pathParams: pathParams3,
32873
- scopes: READ3,
34074
+ pathParams: pathParams4,
34075
+ scopes: READ5,
32874
34076
  tag: "Habits",
32875
34077
  summary: "Get a tap tag",
32876
34078
  description: "One tag.",
@@ -32881,8 +34083,8 @@ var tapTagOperations = defineOperations({
32881
34083
  update: {
32882
34084
  method: "patch",
32883
34085
  path: "/tap-tags/{id}",
32884
- pathParams: pathParams3,
32885
- scopes: WRITE3,
34086
+ pathParams: pathParams4,
34087
+ scopes: WRITE5,
32886
34088
  tag: "Habits",
32887
34089
  summary: "Rename or rebind a tap tag",
32888
34090
  description: "Change its `label`, or bind it to another habit or routine (`kind` and `targetId` together): the sticker stays as it is.",
@@ -32894,8 +34096,8 @@ var tapTagOperations = defineOperations({
32894
34096
  remove: {
32895
34097
  method: "delete",
32896
34098
  path: "/tap-tags/{id}",
32897
- pathParams: pathParams3,
32898
- scopes: WRITE3,
34099
+ pathParams: pathParams4,
34100
+ scopes: WRITE5,
32899
34101
  tag: "Habits",
32900
34102
  summary: "Delete a tap tag",
32901
34103
  description: "The sticker's link stops doing anything.",
@@ -32906,8 +34108,8 @@ var tapTagOperations = defineOperations({
32906
34108
  tap: {
32907
34109
  method: "post",
32908
34110
  path: "/tap-tags/{id}/tap",
32909
- pathParams: pathParams3,
32910
- scopes: WRITE3,
34111
+ pathParams: pathParams4,
34112
+ scopes: WRITE5,
32911
34113
  tag: "Habits",
32912
34114
  summary: "Tap a tap tag",
32913
34115
  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 +34309,7 @@ var taskOperations = defineOperations({
33107
34309
  });
33108
34310
 
33109
34311
  // ../../packages/schema/src/operations/time-blocks.ts
33110
- var INSTANT = "an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight)";
34312
+ var INSTANT3 = "an ISO 8601 datetime with offset, or a bare `YYYY-MM-DD` (UTC midnight)";
33111
34313
  var timeBlockOperations = defineOperations({
33112
34314
  ...crudOperations({
33113
34315
  segment: "time-blocks",
@@ -33120,12 +34322,12 @@ var timeBlockOperations = defineOperations({
33120
34322
  listQuery: [
33121
34323
  {
33122
34324
  name: "from",
33123
- description: `Start of the window (inclusive) \u2014 ${INSTANT}. With \`to\`; both default to the next day.`,
34325
+ description: `Start of the window (inclusive) \u2014 ${INSTANT3}. With \`to\`; both default to the next day.`,
33124
34326
  format: "date-time"
33125
34327
  },
33126
34328
  {
33127
34329
  name: "to",
33128
- description: `End of the window (exclusive, at most 62 days after \`from\`) \u2014 ${INSTANT}.`,
34330
+ description: `End of the window (exclusive, at most 62 days after \`from\`) \u2014 ${INSTANT3}.`,
33129
34331
  format: "date-time"
33130
34332
  },
33131
34333
  { name: "kind", description: "Only blocks of this kind: `task`, `focus` or `life`." }
@@ -33268,6 +34470,112 @@ var webhookOperations = defineOperations({
33268
34470
  }
33269
34471
  });
33270
34472
 
34473
+ // ../../packages/schema/src/operations/whoami.ts
34474
+ var whoamiOperations = defineOperations({
34475
+ whoami: {
34476
+ method: "get",
34477
+ path: "/whoami",
34478
+ scopes: [],
34479
+ tag: "Identity",
34480
+ summary: "Identity ping",
34481
+ 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).",
34482
+ status: 200,
34483
+ response: { description: "The resolved API-key identity." },
34484
+ operationId: "whoami"
34485
+ }
34486
+ });
34487
+
34488
+ // ../../packages/schema/src/operations/withings.ts
34489
+ var withingsOperations = defineOperations({
34490
+ status: {
34491
+ method: "get",
34492
+ path: "/health/withings",
34493
+ scopes: ["health:read"],
34494
+ tag: "Health",
34495
+ summary: "Get the Withings link",
34496
+ 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.",
34497
+ status: 200,
34498
+ response: { description: "The link's status." },
34499
+ errors: { 403: true }
34500
+ },
34501
+ sync: {
34502
+ method: "post",
34503
+ path: "/health/withings/sync",
34504
+ scopes: ["health:write"],
34505
+ tag: "Health",
34506
+ summary: "Sync Withings now",
34507
+ 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.",
34508
+ status: 200,
34509
+ response: { description: "What came in, and the status after it." },
34510
+ errors: { 403: true, 409: true }
34511
+ }
34512
+ });
34513
+
34514
+ // ../../packages/schema/src/operations/worktree-links.ts
34515
+ var pathParams5 = { nodeId: "The linked Task's or Project's ULID." };
34516
+ var notLinkable = "No live Task or Project with that id in this Space (or, for GET, no link on it).";
34517
+ var worktreeLinkOperations = defineOperations({
34518
+ list: {
34519
+ method: "get",
34520
+ path: "/worktree-links",
34521
+ scopes: ["orca:read"],
34522
+ tag: "Orca",
34523
+ summary: "List worktree links",
34524
+ description: "Every Worktree link on a live Task or Project in the Space, with the node's `title`.",
34525
+ query: [
34526
+ { name: "host", description: "Only links to worktrees on this Orca host." },
34527
+ {
34528
+ name: "worktreeId",
34529
+ description: "Only links to this Orca worktree id (`<repoId>::<path>`)."
34530
+ },
34531
+ { name: "subject", description: "Only links on this kind of node: `task` or `project`." }
34532
+ ],
34533
+ querySchema: worktreeLinkListQuerySchema,
34534
+ status: 200,
34535
+ response: { description: "The Space's worktree links." },
34536
+ errors: { 400: true }
34537
+ },
34538
+ get: {
34539
+ method: "get",
34540
+ path: "/worktree-links/{nodeId}",
34541
+ pathParams: pathParams5,
34542
+ scopes: ["orca:read"],
34543
+ tag: "Orca",
34544
+ summary: "Get a worktree link",
34545
+ description: "The Worktree link on a Task or Project.",
34546
+ status: 200,
34547
+ response: { description: "The link." },
34548
+ errors: { 404: notLinkable }
34549
+ },
34550
+ set: {
34551
+ method: "put",
34552
+ path: "/worktree-links/{nodeId}",
34553
+ pathParams: pathParams5,
34554
+ scopes: ["orca:write"],
34555
+ tag: "Orca",
34556
+ summary: "Set a worktree link",
34557
+ description: "Link a Task or Project to an Orca worktree (`host` + `worktreeId`, optionally `path` and `branch`), replacing any earlier link; fires `worktree.link.set`.",
34558
+ body: setWorktreeLinkInputSchema,
34559
+ status: 200,
34560
+ response: { description: "The stored link." },
34561
+ errors: { 400: true, 404: notLinkable },
34562
+ webhook: "worktree.link.set"
34563
+ },
34564
+ clear: {
34565
+ method: "delete",
34566
+ path: "/worktree-links/{nodeId}",
34567
+ pathParams: pathParams5,
34568
+ scopes: ["orca:write"],
34569
+ tag: "Orca",
34570
+ summary: "Clear a worktree link",
34571
+ 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.",
34572
+ status: 204,
34573
+ response: { description: "Cleared." },
34574
+ errors: { 404: notLinkable },
34575
+ webhook: "worktree.link.cleared"
34576
+ }
34577
+ });
34578
+
33271
34579
  // ../../node_modules/ulidx/dist/node/index.js
33272
34580
  import crypto from "crypto";
33273
34581
 
@@ -33520,7 +34828,7 @@ function parseCapture2(input, now) {
33520
34828
 
33521
34829
  // src/meta.ts
33522
34830
  var CLI_NAME = "do";
33523
- var VERSION = "1.306.1";
34831
+ var VERSION = "1.307.0";
33524
34832
  var API_PREFIX = "/api/v1";
33525
34833
  var DEFAULT_API_URL = "http://localhost:3001";
33526
34834
 
@@ -33749,7 +35057,7 @@ function resolveConfig(io, flags) {
33749
35057
  }
33750
35058
 
33751
35059
  // src/contacts-commands.ts
33752
- var WRITE4 = ["contacts:write"];
35060
+ var WRITE6 = ["contacts:write"];
33753
35061
  var FIELD_FLAGS = [
33754
35062
  { name: "name", type: "string", description: "The display name (or pass it as the argument)." },
33755
35063
  { name: "kind", type: "string", description: "person (the default) or organization." },
@@ -33792,7 +35100,7 @@ var CONTACT_COMMAND_SPECS = [
33792
35100
  summary: "Add a contact: a person or an organisation, with their details.",
33793
35101
  args: [{ name: "name", required: false, description: "The display name." }],
33794
35102
  flags: FIELD_FLAGS,
33795
- scopes: WRITE4,
35103
+ scopes: WRITE6,
33796
35104
  returns: "ContactView",
33797
35105
  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
35106
  },
@@ -33801,7 +35109,7 @@ var CONTACT_COMMAND_SPECS = [
33801
35109
  summary: "Change a contact's details.",
33802
35110
  args: [{ name: "id", required: true, description: "The contact's id." }],
33803
35111
  flags: FIELD_FLAGS,
33804
- scopes: WRITE4,
35112
+ scopes: WRITE6,
33805
35113
  returns: "ContactView",
33806
35114
  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
35115
  }
@@ -34000,12 +35308,9 @@ var ENTITIES = [
34000
35308
  createInput: "CreateHabitInput",
34001
35309
  updateInput: "UpdateHabitInput"
34002
35310
  }),
34003
- {
35311
+ entityFromOperations(projectOperations, {
34004
35312
  name: "projects",
34005
- path: "/projects",
34006
- readScopes: ["projects:read"],
34007
35313
  returns: "ProjectView",
34008
- canGet: true,
34009
35314
  listFlags: [
34010
35315
  AREA_FLAG,
34011
35316
  {
@@ -34015,15 +35320,9 @@ var ENTITIES = [
34015
35320
  UPDATED_SINCE_FLAG,
34016
35321
  DUE_BEFORE_FLAG
34017
35322
  ],
34018
- write: {
34019
- scopes: ["projects:write"],
34020
- createSchema: createProjectInputSchema,
34021
- createInput: "CreateProjectInput",
34022
- updateSchema: updateProjectInputSchema,
34023
- updateInput: "UpdateProjectInput",
34024
- canDelete: true
34025
- }
34026
- },
35323
+ createInput: "CreateProjectInput",
35324
+ updateInput: "UpdateProjectInput"
35325
+ }),
34027
35326
  entityFromOperations(areaOperations, {
34028
35327
  name: "areas",
34029
35328
  returns: "AreaView",
@@ -34386,12 +35685,9 @@ var ENTITIES = [
34386
35685
  createInput: "CreateAnimalInput",
34387
35686
  updateInput: "UpdateAnimalInput"
34388
35687
  }),
34389
- {
35688
+ entityFromOperations(runOperations, {
34390
35689
  name: "runs",
34391
- path: "/runs",
34392
- readScopes: ["collections:read"],
34393
35690
  returns: "RunView",
34394
- canGet: true,
34395
35691
  listFlags: [
34396
35692
  { name: "game", description: "only the runs of this game item id", query: "gameId" },
34397
35693
  {
@@ -34401,15 +35697,9 @@ var ENTITIES = [
34401
35697
  },
34402
35698
  { name: "theme", description: "only this theme pack (skyrim, pokemon, \u2026)", query: "theme" }
34403
35699
  ],
34404
- write: {
34405
- scopes: ["collections:write"],
34406
- createSchema: createRunInputSchema,
34407
- createInput: "CreateRunInput",
34408
- updateSchema: updateRunInputSchema,
34409
- updateInput: "UpdateRunInput",
34410
- canDelete: true
34411
- }
34412
- },
35700
+ createInput: "CreateRunInput",
35701
+ updateInput: "UpdateRunInput"
35702
+ }),
34413
35703
  entityFromOperations(shoppingListOperations, {
34414
35704
  name: "shopping-lists",
34415
35705
  returns: "ShoppingList",
@@ -34488,8 +35778,8 @@ function entityOperations(e) {
34488
35778
  }
34489
35779
 
34490
35780
  // src/health-commands.ts
34491
- var READ4 = ["health:read"];
34492
- var WRITE5 = ["health:write"];
35781
+ var READ6 = ["health:read"];
35782
+ var WRITE7 = ["health:write"];
34493
35783
  var DATA_FLAG = {
34494
35784
  name: "data",
34495
35785
  type: "string",
@@ -34503,7 +35793,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34503
35793
  summary: `List your health ${noun}s.`,
34504
35794
  args: [],
34505
35795
  flags: listFlags,
34506
- scopes: READ4,
35796
+ scopes: READ6,
34507
35797
  returns
34508
35798
  },
34509
35799
  {
@@ -34511,7 +35801,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34511
35801
  summary: `Show one health ${noun}.`,
34512
35802
  args: [id],
34513
35803
  flags: [],
34514
- scopes: READ4,
35804
+ scopes: READ6,
34515
35805
  returns
34516
35806
  },
34517
35807
  {
@@ -34519,7 +35809,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34519
35809
  summary: `Add a health ${noun} (--data '${createInput}').`,
34520
35810
  args: [],
34521
35811
  flags: [DATA_FLAG, ...addFlags],
34522
- scopes: WRITE5,
35812
+ scopes: WRITE7,
34523
35813
  returns,
34524
35814
  notes: `Body: ${createInput}. Always private to you; subjectNodeId names who it is about (absent = you).`
34525
35815
  },
@@ -34528,7 +35818,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34528
35818
  summary: `Change a health ${noun} (--data '${updateInput}').`,
34529
35819
  args: [id],
34530
35820
  flags: [DATA_FLAG],
34531
- scopes: WRITE5,
35821
+ scopes: WRITE7,
34532
35822
  returns,
34533
35823
  notes: `Body: ${updateInput}. null clears a field.`
34534
35824
  },
@@ -34537,7 +35827,7 @@ function recordSpecs(plural, singular, noun, returns, createInput, updateInput,
34537
35827
  summary: `Delete a health ${noun}.`,
34538
35828
  args: [id],
34539
35829
  flags: [],
34540
- scopes: WRITE5,
35830
+ scopes: WRITE7,
34541
35831
  returns: "Deleted",
34542
35832
  notes: "Destructive: needs --allow-destructive. An Agent's delete waits on your confirmation."
34543
35833
  }
@@ -34549,7 +35839,7 @@ var HEALTH_COMMAND_SPECS = [
34549
35839
  summary: "Show whether Health is on (only you turn it on or off, in do).",
34550
35840
  args: [],
34551
35841
  flags: [],
34552
- scopes: READ4,
35842
+ scopes: READ6,
34553
35843
  returns: "HealthConsentState",
34554
35844
  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
35845
  },
@@ -34558,7 +35848,7 @@ var HEALTH_COMMAND_SPECS = [
34558
35848
  summary: "Show your health profile: blood group, allergies, lifestyle, notes, emergency contact, GP.",
34559
35849
  args: [],
34560
35850
  flags: [],
34561
- scopes: READ4,
35851
+ scopes: READ6,
34562
35852
  returns: "HealthProfile"
34563
35853
  },
34564
35854
  {
@@ -34566,7 +35856,7 @@ var HEALTH_COMMAND_SPECS = [
34566
35856
  summary: "Show your Medical ID: name, blood group, allergies, current medications, height, emergency contact.",
34567
35857
  args: [],
34568
35858
  flags: [],
34569
- scopes: READ4,
35859
+ scopes: READ6,
34570
35860
  returns: "HealthMedicalId",
34571
35861
  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
35862
  },
@@ -34587,7 +35877,7 @@ var HEALTH_COMMAND_SPECS = [
34587
35877
  description: `Sex, in your words, for the doctor's page ("female"); "" clears it.`
34588
35878
  }
34589
35879
  ],
34590
- scopes: WRITE5,
35880
+ scopes: WRITE7,
34591
35881
  returns: "HealthProfile",
34592
35882
  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
35883
  },
@@ -34596,7 +35886,7 @@ var HEALTH_COMMAND_SPECS = [
34596
35886
  summary: "Log one health message: find or start the Episode, add a symptom and/or a dose.",
34597
35887
  args: [],
34598
35888
  flags: [DATA_FLAG],
34599
- scopes: WRITE5,
35889
+ scopes: WRITE7,
34600
35890
  returns: "HealthReceipt",
34601
35891
  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
35892
  },
@@ -34605,7 +35895,7 @@ var HEALTH_COMMAND_SPECS = [
34605
35895
  summary: `Correct a symptom entry and get a receipt (--data '{"bodyRegion":"ankle_l"}').`,
34606
35896
  args: [{ name: "entry", required: true, description: "The entry's id (its latest version)." }],
34607
35897
  flags: [DATA_FLAG],
34608
- scopes: WRITE5,
35898
+ scopes: WRITE7,
34609
35899
  returns: "HealthReceipt"
34610
35900
  },
34611
35901
  {
@@ -34616,7 +35906,7 @@ var HEALTH_COMMAND_SPECS = [
34616
35906
  { name: "trend", required: true, description: "better|same|worse, against yesterday." }
34617
35907
  ],
34618
35908
  flags: [],
34619
- scopes: WRITE5,
35909
+ scopes: WRITE7,
34620
35910
  returns: "HealthReceipt",
34621
35911
  notes: "One a day: a second check-in the same day corrects the first. The receipt carries its undo."
34622
35912
  },
@@ -34633,7 +35923,7 @@ var HEALTH_COMMAND_SPECS = [
34633
35923
  description: "Post the confirmation card in this thread (by slug); the agent's key only."
34634
35924
  }
34635
35925
  ],
34636
- scopes: WRITE5,
35926
+ scopes: WRITE7,
34637
35927
  returns: "HealthRedFlagsResult",
34638
35928
  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
35929
  },
@@ -34645,7 +35935,7 @@ var HEALTH_COMMAND_SPECS = [
34645
35935
  { name: "id", required: true, description: "The id from the receipt's undo." }
34646
35936
  ],
34647
35937
  flags: [],
34648
- scopes: WRITE5,
35938
+ scopes: WRITE7,
34649
35939
  returns: "HealthUndoResult",
34650
35940
  notes: "A POST, so an Agent's undo never waits on a confirmation."
34651
35941
  },
@@ -34654,7 +35944,7 @@ var HEALTH_COMMAND_SPECS = [
34654
35944
  summary: "Find the open Episode a complaint belongs to, or start one (--data UpsertHealthEpisodeInput).",
34655
35945
  args: [],
34656
35946
  flags: [DATA_FLAG],
34657
- scopes: WRITE5,
35947
+ scopes: WRITE7,
34658
35948
  returns: "HealthEpisodeUpsert"
34659
35949
  },
34660
35950
  {
@@ -34664,7 +35954,7 @@ var HEALTH_COMMAND_SPECS = [
34664
35954
  flags: [
34665
35955
  { name: "episode", type: "string", description: "The Episode; default the latest open one." }
34666
35956
  ],
34667
- scopes: READ4,
35957
+ scopes: READ6,
34668
35958
  returns: "HealthSummary"
34669
35959
  },
34670
35960
  {
@@ -34687,7 +35977,7 @@ var HEALTH_COMMAND_SPECS = [
34687
35977
  summary: "Your For-the-doctor links: sections, expiry, opens; never the secret.",
34688
35978
  args: [],
34689
35979
  flags: [],
34690
- scopes: READ4,
35980
+ scopes: READ6,
34691
35981
  returns: "HealthShare[]",
34692
35982
  notes: "Only the member makes a share, in do; a key can list and take one back."
34693
35983
  },
@@ -34696,7 +35986,7 @@ var HEALTH_COMMAND_SPECS = [
34696
35986
  summary: "Take a For-the-doctor link back: it stops opening.",
34697
35987
  args: [{ name: "id", required: true, description: "The share's id." }],
34698
35988
  flags: [],
34699
- scopes: WRITE5,
35989
+ scopes: WRITE7,
34700
35990
  returns: "HealthShare"
34701
35991
  },
34702
35992
  ...recordSpecs("conditions", "condition", "condition", "HealthConditionView", "CreateHealthConditionInput", "UpdateHealthConditionInput"),
@@ -34714,7 +36004,7 @@ var HEALTH_COMMAND_SPECS = [
34714
36004
  description: 'Their role on it, in your words ("dermatologist").'
34715
36005
  }
34716
36006
  ],
34717
- scopes: WRITE5,
36007
+ scopes: WRITE7,
34718
36008
  returns: "HealthConditionView",
34719
36009
  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
36010
  },
@@ -34726,7 +36016,7 @@ var HEALTH_COMMAND_SPECS = [
34726
36016
  { name: "contact", required: true, description: "The Contact's id." }
34727
36017
  ],
34728
36018
  flags: [],
34729
- scopes: WRITE5,
36019
+ scopes: WRITE7,
34730
36020
  returns: "HealthConditionView",
34731
36021
  notes: "An Agent's removal waits on your confirmation, like a delete."
34732
36022
  },
@@ -34763,7 +36053,7 @@ var HEALTH_COMMAND_SPECS = [
34763
36053
  { name: "at", type: "string", description: "When it happened (ISO); defaults to now." },
34764
36054
  { name: "note", type: "string", description: "A free-text note." }
34765
36055
  ],
34766
- scopes: WRITE5,
36056
+ scopes: WRITE7,
34767
36057
  returns: "HealthSymptomEntry",
34768
36058
  notes: "An Agent's entry is source chat. For qualifiers, send --data with LogHealthSymptomInput."
34769
36059
  },
@@ -34779,7 +36069,7 @@ var HEALTH_COMMAND_SPECS = [
34779
36069
  }
34780
36070
  ],
34781
36071
  flags: [DATA_FLAG],
34782
- scopes: WRITE5,
36072
+ scopes: WRITE7,
34783
36073
  returns: "HealthSymptomEntry",
34784
36074
  notes: "Body: CorrectHealthSymptomInput; fields not named are copied. The history stays."
34785
36075
  },
@@ -34791,7 +36081,7 @@ var HEALTH_COMMAND_SPECS = [
34791
36081
  { name: "entry", required: true, description: "The entry's id." }
34792
36082
  ],
34793
36083
  flags: [],
34794
- scopes: WRITE5,
36084
+ scopes: WRITE7,
34795
36085
  returns: "Deleted"
34796
36086
  },
34797
36087
  ...recordSpecs("visits", "visit", "visit", "HealthEncounterView", "CreateHealthEncounterInput", "UpdateHealthEncounterInput"),
@@ -34801,7 +36091,7 @@ var HEALTH_COMMAND_SPECS = [
34801
36091
  summary: "List the doses of a Medication, latest first.",
34802
36092
  args: [{ name: "med", required: true, description: "The Medication's id." }],
34803
36093
  flags: [],
34804
- scopes: READ4,
36094
+ scopes: READ6,
34805
36095
  returns: "HealthMedicationDose"
34806
36096
  },
34807
36097
  {
@@ -34815,7 +36105,7 @@ var HEALTH_COMMAND_SPECS = [
34815
36105
  { name: "at", type: "string", description: "When (ISO); defaults to now." },
34816
36106
  { name: "note", type: "string", description: "A free-text note." }
34817
36107
  ],
34818
- scopes: WRITE5,
36108
+ scopes: WRITE7,
34819
36109
  returns: "HealthMedicationDose"
34820
36110
  },
34821
36111
  {
@@ -34826,7 +36116,7 @@ var HEALTH_COMMAND_SPECS = [
34826
36116
  { name: "dose", required: true, description: "The dose's id." }
34827
36117
  ],
34828
36118
  flags: [DATA_FLAG],
34829
- scopes: WRITE5,
36119
+ scopes: WRITE7,
34830
36120
  returns: "HealthMedicationDose",
34831
36121
  notes: "Body: UpdateHealthDoseInput."
34832
36122
  },
@@ -34838,7 +36128,7 @@ var HEALTH_COMMAND_SPECS = [
34838
36128
  { name: "dose", required: true, description: "The dose's id." }
34839
36129
  ],
34840
36130
  flags: [],
34841
- scopes: WRITE5,
36131
+ scopes: WRITE7,
34842
36132
  returns: "Deleted"
34843
36133
  },
34844
36134
  ...recordSpecs("labs", "lab", "lab draw", "HealthLabPanelView", "CreateHealthLabPanelInput", "UpdateHealthLabPanelInput"),
@@ -34860,7 +36150,7 @@ var HEALTH_COMMAND_SPECS = [
34860
36150
  description: "The file's media type, when its name doesn't say."
34861
36151
  }
34862
36152
  ],
34863
- scopes: WRITE5,
36153
+ scopes: WRITE7,
34864
36154
  returns: "HealthLabImportResult",
34865
36155
  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
36156
  },
@@ -34869,7 +36159,7 @@ var HEALTH_COMMAND_SPECS = [
34869
36159
  summary: "The canonical analytes: key, printed name, group, unit, LOINC and aliases.",
34870
36160
  args: [],
34871
36161
  flags: [],
34872
- scopes: READ4,
36162
+ scopes: READ6,
34873
36163
  returns: "LabAnalyteDef[]",
34874
36164
  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
36165
  },
@@ -34884,7 +36174,7 @@ var HEALTH_COMMAND_SPECS = [
34884
36174
  description: "Required: only report, change nothing."
34885
36175
  }
34886
36176
  ],
34887
- scopes: READ4,
36177
+ scopes: READ6,
34888
36178
  returns: "HealthLabNormalizeReport",
34889
36179
  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
36180
  },
@@ -34893,7 +36183,7 @@ var HEALTH_COMMAND_SPECS = [
34893
36183
  summary: "Every analyte your draws measured, each with its trend, grouped as a lab sheet.",
34894
36184
  args: [],
34895
36185
  flags: [],
34896
- scopes: READ4,
36186
+ scopes: READ6,
34897
36187
  returns: "HealthLabOverview"
34898
36188
  },
34899
36189
  {
@@ -34919,25 +36209,25 @@ var HEALTH_COMMAND_SPECS = [
34919
36209
  summary: "Whether your Withings scale is connected, the last sync, and what stops it.",
34920
36210
  args: [],
34921
36211
  flags: [],
34922
- scopes: READ4,
36212
+ scopes: [...withingsOperations.status.scopes],
34923
36213
  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."
36214
+ 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
36215
  },
34926
36216
  {
34927
36217
  path: ["health", "withings", "sync"],
34928
36218
  summary: "Sync your Withings weigh-ins now, as the morning sync does.",
34929
36219
  args: [],
34930
36220
  flags: [],
34931
- scopes: WRITE5,
36221
+ scopes: [...withingsOperations.sync.scopes],
34932
36222
  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."
36223
+ 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
36224
  },
34935
36225
  {
34936
36226
  path: ["health", "analyte"],
34937
36227
  summary: "One analyte's trend across draws (do health analyte vitamin_d).",
34938
36228
  args: [{ name: "analyte", required: true, description: "The analyte key (vitamin_d)." }],
34939
36229
  flags: [],
34940
- scopes: READ4,
36230
+ scopes: READ6,
34941
36231
  returns: "HealthLabAnalyteView"
34942
36232
  },
34943
36233
  {
@@ -34945,7 +36235,7 @@ var HEALTH_COMMAND_SPECS = [
34945
36235
  summary: `Keep a target band or the supplement for an analyte (--data '{"targetLow":40,"targetHigh":60}').`,
34946
36236
  args: [{ name: "analyte", required: true, description: "The analyte key (vitamin_d)." }],
34947
36237
  flags: [DATA_FLAG],
34948
- scopes: WRITE5,
36238
+ scopes: WRITE7,
34949
36239
  returns: "HealthLabAnalyteView",
34950
36240
  notes: "Body: SetHealthLabAnalyteInput. An absent key keeps a field; null clears it."
34951
36241
  }
@@ -35229,9 +36519,9 @@ var COMMAND_SPECS = [
35229
36519
  { name: "from", type: "string", description: "Window start (ISO); default today, UTC." },
35230
36520
  { name: "to", type: "string", description: "Window end (ISO); default a day after from." }
35231
36521
  ],
35232
- scopes: ["tasks:read"],
36522
+ scopes: [...readOperations.now.scopes],
35233
36523
  returns: "NowResponse",
35234
- notes: "GET /api/v1/now \u2014 server-side; pass your local day bounds with --from/--to."
36524
+ notes: `${routeOf(readOperations.now)} \u2014 server-side; pass your local day bounds with --from/--to.`
35235
36525
  },
35236
36526
  {
35237
36527
  path: ["landed"],
@@ -35241,7 +36531,7 @@ var COMMAND_SPECS = [
35241
36531
  { name: "from", type: "string", description: "Window start (ISO); default today, UTC." },
35242
36532
  { name: "to", type: "string", description: "Window end (ISO); default a day after from." }
35243
36533
  ],
35244
- scopes: ["tasks:read"],
36534
+ scopes: [...readOperations.landed.scopes],
35245
36535
  returns: "LandedResponse",
35246
36536
  notes: "Cancelled tasks never count. For a week: --from <monday> --to <next monday>."
35247
36537
  },
@@ -35253,7 +36543,7 @@ var COMMAND_SPECS = [
35253
36543
  { name: "from", type: "string", description: "Window start (ISO); default today, UTC." },
35254
36544
  { name: "to", type: "string", description: "Window end (ISO); default a day after from." }
35255
36545
  ],
35256
- scopes: ["tasks:read"],
36546
+ scopes: [...readOperations.review.scopes],
35257
36547
  returns: "ReviewDayStat"
35258
36548
  },
35259
36549
  {
@@ -35638,7 +36928,7 @@ var COMMAND_SPECS = [
35638
36928
  },
35639
36929
  { name: "limit", type: "string", description: "Max hits (1\u201350, default 20)." }
35640
36930
  ],
35641
- scopes: [],
36931
+ scopes: [...readOperations.search.scopes],
35642
36932
  returns: "SearchHit",
35643
36933
  notes: "Returns only the node types your key holds a read scope for."
35644
36934
  },
@@ -36574,7 +37864,7 @@ var COMMAND_SPECS = [
36574
37864
  summary: "Show the note attached to any node (Habit, Area, Contact, \u2026).",
36575
37865
  args: [{ name: "node", required: true, description: "Owner node ULID." }],
36576
37866
  flags: [],
36577
- scopes: ["notes:read"],
37867
+ scopes: [...nodeOperations.getNotes.scopes],
36578
37868
  returns: "AttachedNoteView",
36579
37869
  notes: "No note yet is `noteId: null` (human output says so). 404 for a `note`/`event` node."
36580
37870
  },
@@ -36583,7 +37873,7 @@ var COMMAND_SPECS = [
36583
37873
  summary: "Set the note attached to any node (upsert).",
36584
37874
  args: [{ name: "node", required: true, description: "Owner node ULID." }],
36585
37875
  flags: [{ name: "body", type: "string", description: "The note body (markdown)." }],
36586
- scopes: ["notes:write"],
37876
+ scopes: [...nodeOperations.setNotes.scopes],
36587
37877
  returns: "AttachedNoteView",
36588
37878
  notes: "Body from --body (required). A blank body is refused \u2014 clear with `notes detach`."
36589
37879
  },
@@ -36616,7 +37906,7 @@ var COMMAND_SPECS = [
36616
37906
  flags: [
36617
37907
  { name: "allow-destructive", type: "boolean", description: "Required to actually clear." }
36618
37908
  ],
36619
- scopes: ["notes:write"],
37909
+ scopes: [...nodeOperations.clearNotes.scopes],
36620
37910
  returns: "None",
36621
37911
  notes: "Destructive \u2014 gated behind --allow-destructive + a notes:write key. Idempotent."
36622
37912
  },
@@ -36625,7 +37915,7 @@ var COMMAND_SPECS = [
36625
37915
  summary: "List every Orca host's worktrees: status, last line, sessions and linked Task.",
36626
37916
  args: [],
36627
37917
  flags: [],
36628
- scopes: ["orca:read"],
37918
+ scopes: [...orcaOperations.overview.scopes],
36629
37919
  returns: "OrcaOverview",
36630
37920
  notes: "state is ok | unreachable | not_configured. waitingCount counts worktrees waiting on you. Read live; nothing is stored."
36631
37921
  },
@@ -36641,7 +37931,7 @@ var COMMAND_SPECS = [
36641
37931
  { name: "limit", type: "string", description: "At most this many lines (1\u20132000)." },
36642
37932
  { name: "screen", type: "boolean", description: "Read the rendered screen instead." }
36643
37933
  ],
36644
- scopes: ["orca:read"],
37934
+ scopes: [...orcaOperations.output.scopes],
36645
37935
  returns: "OrcaTerminalOutput"
36646
37936
  },
36647
37937
  {
@@ -36655,7 +37945,7 @@ var COMMAND_SPECS = [
36655
37945
  { name: "text", type: "string", description: "The message to send." },
36656
37946
  { name: "no-enter", type: "boolean", description: "Type it without pressing Enter." }
36657
37947
  ],
36658
- scopes: ["orca:write"],
37948
+ scopes: [...orcaOperations.send.scopes],
36659
37949
  returns: "OrcaSendMessageResult",
36660
37950
  notes: "An Agent's key gets a 428 confirmation_required; it runs once the member confirms in do's agent panel (#678)."
36661
37951
  },
@@ -36671,7 +37961,7 @@ var COMMAND_SPECS = [
36671
37961
  },
36672
37962
  { name: "agent", type: "string", description: "The agent to start: claude or codex." }
36673
37963
  ],
36674
- scopes: ["orca:write"],
37964
+ scopes: [...orcaOperations.restart.scopes],
36675
37965
  returns: "OrcaRestartSessionResult",
36676
37966
  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
37967
  },
@@ -36688,7 +37978,7 @@ var COMMAND_SPECS = [
36688
37978
  },
36689
37979
  { name: "subject", type: "string", description: "Only `task` or `project` links." }
36690
37980
  ],
36691
- scopes: ["orca:read"],
37981
+ scopes: [...worktreeLinkOperations.list.scopes],
36692
37982
  returns: "WorktreeLinkView",
36693
37983
  notes: "Each row carries the linked node's title. Progress is never stored \u2014 read it in Orca."
36694
37984
  },
@@ -36697,7 +37987,7 @@ var COMMAND_SPECS = [
36697
37987
  summary: "Show the Worktree link on a Task or Project.",
36698
37988
  args: [{ name: "node", required: true, description: "Task or Project ULID." }],
36699
37989
  flags: [],
36700
- scopes: ["orca:read"],
37990
+ scopes: [...worktreeLinkOperations.get.scopes],
36701
37991
  returns: "WorktreeLink",
36702
37992
  notes: "Not found (exit 4) when the node has no link. Task/Project reads carry it as worktreeLink."
36703
37993
  },
@@ -36719,7 +38009,7 @@ var COMMAND_SPECS = [
36719
38009
  },
36720
38010
  { name: "branch", type: "string", description: "The branch, when known." }
36721
38011
  ],
36722
- scopes: ["orca:write"],
38012
+ scopes: [...worktreeLinkOperations.set.scopes],
36723
38013
  returns: "WorktreeLink",
36724
38014
  notes: "PUT /api/v1/worktree-links/{node}. --host and --worktree are required."
36725
38015
  },
@@ -36730,7 +38020,7 @@ var COMMAND_SPECS = [
36730
38020
  flags: [
36731
38021
  { name: "allow-destructive", type: "boolean", description: "Required to actually unlink." }
36732
38022
  ],
36733
- scopes: ["orca:write"],
38023
+ scopes: [...worktreeLinkOperations.clear.scopes],
36734
38024
  returns: "None",
36735
38025
  notes: "Gated behind --allow-destructive + an orca:write key. Idempotent."
36736
38026
  },
@@ -37110,7 +38400,7 @@ var COMMAND_SPECS = [
37110
38400
  summary: "Show the agent runtime the agent panel's chat goes to (never its key).",
37111
38401
  args: [],
37112
38402
  flags: [],
37113
- scopes: ["agents:read"],
38403
+ scopes: [...agentConnectionOperations.get.scopes],
37114
38404
  returns: "AgentConnection",
37115
38405
  notes: "hasKey says whether a key is set; the key itself is write-only."
37116
38406
  },
@@ -37143,7 +38433,7 @@ var COMMAND_SPECS = [
37143
38433
  },
37144
38434
  { name: "clear-key", type: "boolean", description: "Remove the stored key." }
37145
38435
  ],
37146
- scopes: ["agents:write"],
38436
+ scopes: [...agentConnectionOperations.save.scopes],
37147
38437
  returns: "AgentConnection",
37148
38438
  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
38439
  },
@@ -37154,7 +38444,7 @@ var COMMAND_SPECS = [
37154
38444
  flags: [
37155
38445
  { name: "allow-destructive", type: "boolean", description: "Required to actually remove." }
37156
38446
  ],
37157
- scopes: ["agents:write"],
38447
+ scopes: [...agentConnectionOperations.remove.scopes],
37158
38448
  returns: "None",
37159
38449
  notes: "Owner/admin only. An Agent's key gets a 428 confirmation_required until the member confirms in do's agent panel."
37160
38450
  },
@@ -37492,7 +38782,7 @@ var COMMAND_SPECS = [
37492
38782
  summary: "List the catalog Types (key \xB7 label \xB7 status subset).",
37493
38783
  args: [],
37494
38784
  flags: [],
37495
- scopes: ["collections:read"],
38785
+ scopes: [...collectionOperations.types.scopes],
37496
38786
  returns: "CollectionTypeSummary",
37497
38787
  notes: "The Type registry is code-defined (ADR 0017); served from GET /api/v1/collection/types."
37498
38788
  },
@@ -37520,7 +38810,7 @@ var COMMAND_SPECS = [
37520
38810
  },
37521
38811
  ...PAGINATION_FLAGS
37522
38812
  ],
37523
- scopes: ["collections:read"],
38813
+ scopes: [...collectionOperations.list.scopes],
37524
38814
  returns: "CollectionItemView"
37525
38815
  },
37526
38816
  {
@@ -37535,7 +38825,7 @@ var COMMAND_SPECS = [
37535
38825
  description: "Initial status; defaults to the Type's first."
37536
38826
  }
37537
38827
  ],
37538
- scopes: ["collections:write"],
38828
+ scopes: [...collectionOperations.create.scopes],
37539
38829
  returns: "CollectionItemView",
37540
38830
  grammar: {
37541
38831
  "#tag": "Adds a tag. Repeatable: '#scifi #2024'."
@@ -37554,7 +38844,7 @@ var COMMAND_SPECS = [
37554
38844
  }
37555
38845
  ],
37556
38846
  flags: [],
37557
- scopes: ["collections:write"],
38847
+ scopes: [...collectionOperations.update.scopes],
37558
38848
  returns: "CollectionItemView"
37559
38849
  },
37560
38850
  {
@@ -37577,7 +38867,7 @@ var COMMAND_SPECS = [
37577
38867
  { name: "cost", type: "string", description: "What it cost (a sweep, a repair)." },
37578
38868
  { name: "contact", type: "string", description: "Contact ULID of who did it." }
37579
38869
  ],
37580
- scopes: ["collections:write"],
38870
+ scopes: [...collectionOperations.addEntry.scopes],
37581
38871
  returns: "CollectionItemDetail"
37582
38872
  },
37583
38873
  {
@@ -37635,9 +38925,9 @@ var COMMAND_SPECS = [
37635
38925
  description: "Day it occurred (YYYY-MM-DD); defaults to today."
37636
38926
  }
37637
38927
  ],
37638
- scopes: ["collections:write"],
38928
+ scopes: [...collectionOperations.log.scopes],
37639
38929
  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."
38930
+ 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
38931
  },
37642
38932
  {
37643
38933
  path: ["collection", "edit-entry"],
@@ -37651,7 +38941,7 @@ var COMMAND_SPECS = [
37651
38941
  { name: "note", type: "string", description: "New note." },
37652
38942
  { name: "on", type: "string", description: "New day (YYYY-MM-DD)." }
37653
38943
  ],
37654
- scopes: ["collections:write"],
38944
+ scopes: [...collectionOperations.editEntry.scopes],
37655
38945
  returns: "CollectionItemDetail"
37656
38946
  },
37657
38947
  {
@@ -37665,7 +38955,7 @@ var COMMAND_SPECS = [
37665
38955
  description: "Use this image instead of searching (fixes a wrong cover)."
37666
38956
  }
37667
38957
  ],
37668
- scopes: ["collections:write"],
38958
+ scopes: [...collectionOperations.cover.scopes],
37669
38959
  returns: "CollectionCoverResult",
37670
38960
  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
38961
  },
@@ -37674,7 +38964,7 @@ var COMMAND_SPECS = [
37674
38964
  summary: "List other covers for an Item, best first (up to 6).",
37675
38965
  args: [{ name: "id", required: true, description: "Item ULID." }],
37676
38966
  flags: [],
37677
- scopes: ["collections:read"],
38967
+ scopes: [...collectionOperations.coverCandidates.scopes],
37678
38968
  returns: "CollectionCoverCandidates",
37679
38969
  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
38970
  },
@@ -37683,7 +38973,7 @@ var COMMAND_SPECS = [
37683
38973
  summary: "Backfill covers for books and games that have none (25 per call).",
37684
38974
  args: [],
37685
38975
  flags: [],
37686
- scopes: ["collections:write"],
38976
+ scopes: [...collectionOperations.backfillCovers.scopes],
37687
38977
  returns: "CollectionCoverBackfillResult",
37688
38978
  notes: "Returns { checked, found }; run again while checked is 25 to work through more."
37689
38979
  },
@@ -38856,9 +40146,9 @@ var COMMAND_SPECS = [
38856
40146
  { name: "session", type: "string", description: "Put the changes on this session id." },
38857
40147
  { name: "new-session", type: "boolean", description: "Start a new session for them." }
38858
40148
  ],
38859
- scopes: ["collections:write"],
40149
+ scopes: [...runOperations.applyChanges.scopes],
38860
40150
  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."
40151
+ 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
40152
  },
38863
40153
  {
38864
40154
  path: ["runs", "save-header"],
@@ -38884,18 +40174,18 @@ var COMMAND_SPECS = [
38884
40174
  },
38885
40175
  { name: "dry-run", type: "boolean", description: "Print what the save says; send nothing." }
38886
40176
  ],
38887
- scopes: ["collections:write"],
40177
+ scopes: [...runOperations.uploadSaveHeader.scopes],
38888
40178
  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."
40179
+ 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
40180
  },
38891
40181
  {
38892
40182
  path: ["runs", "undo"],
38893
40183
  summary: "Undo a run's latest chat update (or the one given).",
38894
40184
  args: [{ name: "id", required: true, description: "Run ULID." }],
38895
40185
  flags: [{ name: "batch", type: "string", description: "The batch id to undo." }],
38896
- scopes: ["collections:write"],
40186
+ scopes: [...runOperations.undo.scopes],
38897
40187
  returns: "RunDetail",
38898
- notes: "POST /api/v1/runs/{id}/undo. Puts the changed sections back as they were."
40188
+ notes: `${routeOf(runOperations.undo)}. Puts the changed sections back as they were.`
38899
40189
  },
38900
40190
  {
38901
40191
  path: ["runs", "dex", "tick"],
@@ -38914,27 +40204,27 @@ var COMMAND_SPECS = [
38914
40204
  { name: "untick", type: "boolean", description: "Untick them instead." },
38915
40205
  { name: "message", type: "string", description: "The chat message, quoted on the receipt." }
38916
40206
  ],
38917
- scopes: ["collections:write"],
40207
+ scopes: [...runOperations.tickChecklist.scopes],
38918
40208
  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."
40209
+ 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
40210
  },
38921
40211
  {
38922
40212
  path: ["runs", "steam", "status"],
38923
40213
  summary: "Whether you've linked Steam, how many games have a Steam app id, and the last sync.",
38924
40214
  args: [],
38925
40215
  flags: [],
38926
- scopes: ["collections:read"],
40216
+ scopes: [...runOperations.steamStatus.scopes],
38927
40217
  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."
40218
+ 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
40219
  },
38930
40220
  {
38931
40221
  path: ["runs", "steam", "sync"],
38932
40222
  summary: "Sync your Steam achievements into your runs now, as the daily sync does.",
38933
40223
  args: [],
38934
40224
  flags: [{ name: "game", type: "string", description: "Only this game item ULID." }],
38935
- scopes: ["collections:write"],
40225
+ scopes: [...runOperations.steamSync.scopes],
38936
40226
  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."
40227
+ 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
40228
  },
38939
40229
  {
38940
40230
  path: ["sensors", "status"],
@@ -39041,9 +40331,9 @@ var COMMAND_SPECS = [
39041
40331
  summary: "A run's sessions, newest first, with recaps and change batches.",
39042
40332
  args: [{ name: "run", required: true, description: "Run ULID." }],
39043
40333
  flags: [],
39044
- scopes: ["collections:read"],
40334
+ scopes: [...runOperations.listSessions.scopes],
39045
40335
  returns: "RunSession[]",
39046
- notes: "GET /api/v1/runs/{id}/sessions."
40336
+ notes: `${routeOf(runOperations.listSessions)}.`
39047
40337
  },
39048
40338
  {
39049
40339
  path: ["runs", "sessions", "add"],
@@ -39054,9 +40344,9 @@ var COMMAND_SPECS = [
39054
40344
  { name: "minutes", type: "string", description: "How long it lasted." },
39055
40345
  { name: "on", type: "string", description: "The day, YYYY-MM-DD (default today)." }
39056
40346
  ],
39057
- scopes: ["collections:write"],
40347
+ scopes: [...runOperations.addSession.scopes],
39058
40348
  returns: "RunDetail",
39059
- notes: "POST /api/v1/runs/{id}/sessions."
40349
+ notes: `${routeOf(runOperations.addSession)}.`
39060
40350
  },
39061
40351
  {
39062
40352
  path: ["runs", "sessions", "update"],
@@ -39070,9 +40360,9 @@ var COMMAND_SPECS = [
39070
40360
  { name: "minutes", type: "string", description: "How long it lasted." },
39071
40361
  { name: "on", type: "string", description: "The day, YYYY-MM-DD." }
39072
40362
  ],
39073
- scopes: ["collections:write"],
40363
+ scopes: [...runOperations.updateSession.scopes],
39074
40364
  returns: "RunDetail",
39075
- notes: "PATCH /api/v1/runs/{id}/sessions/{sessionId}."
40365
+ notes: `${routeOf(runOperations.updateSession)}.`
39076
40366
  },
39077
40367
  {
39078
40368
  path: ["people", "propose"],
@@ -39134,9 +40424,9 @@ var COMMAND_SPECS = [
39134
40424
  description: "Required to actually delete it."
39135
40425
  }
39136
40426
  ],
39137
- scopes: ["collections:write"],
40427
+ scopes: [...runOperations.removeSession.scopes],
39138
40428
  returns: "None",
39139
- notes: "DELETE /api/v1/runs/{id}/sessions/{sessionId}."
40429
+ notes: `${routeOf(runOperations.removeSession)}.`
39140
40430
  },
39141
40431
  {
39142
40432
  path: ["places", "import"],
@@ -39568,7 +40858,7 @@ var COMMAND_SPECS = [
39568
40858
  description: 'JSON CreateInviteInput, e.g. {"type":"link"} or {"type":"email","email":"a@b.c"}.'
39569
40859
  }
39570
40860
  ],
39571
- scopes: ["members:write"],
40861
+ scopes: [...inviteOperations.create.scopes],
39572
40862
  returns: "Invite"
39573
40863
  },
39574
40864
  {
@@ -39576,7 +40866,7 @@ var COMMAND_SPECS = [
39576
40866
  summary: "List the Space's invites.",
39577
40867
  args: [],
39578
40868
  flags: [],
39579
- scopes: ["members:read"],
40869
+ scopes: [...inviteOperations.list.scopes],
39580
40870
  returns: "Invite"
39581
40871
  },
39582
40872
  {
@@ -39586,7 +40876,7 @@ var COMMAND_SPECS = [
39586
40876
  flags: [
39587
40877
  { name: "allow-destructive", type: "boolean", description: "Required to actually revoke." }
39588
40878
  ],
39589
- scopes: ["members:write"],
40879
+ scopes: [...inviteOperations.revoke.scopes],
39590
40880
  returns: "None",
39591
40881
  notes: "Destructive \u2014 gated behind --allow-destructive + a members:write key."
39592
40882
  },
@@ -39595,7 +40885,7 @@ var COMMAND_SPECS = [
39595
40885
  summary: "List the Space's members.",
39596
40886
  args: [],
39597
40887
  flags: [],
39598
- scopes: ["members:read"],
40888
+ scopes: [...memberOperations.list.scopes],
39599
40889
  returns: "MemberView"
39600
40890
  },
39601
40891
  {
@@ -39606,7 +40896,7 @@ var COMMAND_SPECS = [
39606
40896
  { name: "role", type: "string", description: "Base role: owner|admin|member|guest." },
39607
40897
  { name: "data", type: "string", description: "JSON SetMemberRoleInput (overrides --role)." }
39608
40898
  ],
39609
- scopes: ["members:write"],
40899
+ scopes: [...memberOperations.setRole.scopes],
39610
40900
  returns: "MemberView"
39611
40901
  },
39612
40902
  {
@@ -39616,7 +40906,7 @@ var COMMAND_SPECS = [
39616
40906
  flags: [
39617
40907
  { name: "allow-destructive", type: "boolean", description: "Required to actually remove." }
39618
40908
  ],
39619
- scopes: ["members:write"],
40909
+ scopes: [...memberOperations.remove.scopes],
39620
40910
  returns: "None",
39621
40911
  notes: "Destructive \u2014 gated behind --allow-destructive + a members:write key."
39622
40912
  },
@@ -39639,7 +40929,7 @@ var COMMAND_SPECS = [
39639
40929
  default: "member"
39640
40930
  }
39641
40931
  ],
39642
- scopes: ["sharing:write"],
40932
+ scopes: [...sharingOperations.createGrant.scopes],
39643
40933
  returns: "NodeGrantView"
39644
40934
  },
39645
40935
  {
@@ -39647,7 +40937,7 @@ var COMMAND_SPECS = [
39647
40937
  summary: "List the grants on a node.",
39648
40938
  args: [{ name: "node", required: true, description: "Node ULID." }],
39649
40939
  flags: [],
39650
- scopes: ["sharing:read"],
40940
+ scopes: [...sharingOperations.listGrants.scopes],
39651
40941
  returns: "NodeGrantView"
39652
40942
  },
39653
40943
  {
@@ -39660,7 +40950,7 @@ var COMMAND_SPECS = [
39660
40950
  flags: [
39661
40951
  { name: "allow-destructive", type: "boolean", description: "Required to actually revoke." }
39662
40952
  ],
39663
- scopes: ["sharing:write"],
40953
+ scopes: [...sharingOperations.revokeGrant.scopes],
39664
40954
  returns: "None",
39665
40955
  notes: "Destructive \u2014 gated behind --allow-destructive + a sharing:write key."
39666
40956
  },
@@ -39672,7 +40962,7 @@ var COMMAND_SPECS = [
39672
40962
  { name: "to", type: "string", description: "Member ULID to assign to." },
39673
40963
  { name: "cap", type: "string", description: "Capability (default edit).", default: "edit" }
39674
40964
  ],
39675
- scopes: ["sharing:write"],
40965
+ scopes: [...sharingOperations.assign.scopes],
39676
40966
  returns: "NodeGrantView"
39677
40967
  },
39678
40968
  {
@@ -39683,7 +40973,7 @@ var COMMAND_SPECS = [
39683
40973
  { name: "body", required: true, description: "Comment text." }
39684
40974
  ],
39685
40975
  flags: [],
39686
- scopes: ["comments:write"],
40976
+ scopes: [...commentOperations.create.scopes],
39687
40977
  returns: "CommentView"
39688
40978
  },
39689
40979
  {
@@ -39691,7 +40981,7 @@ var COMMAND_SPECS = [
39691
40981
  summary: "List a node's comment thread.",
39692
40982
  args: [{ name: "node", required: true, description: "Node ULID." }],
39693
40983
  flags: [],
39694
- scopes: ["comments:read"],
40984
+ scopes: [...commentOperations.list.scopes],
39695
40985
  returns: "CommentView"
39696
40986
  },
39697
40987
  {
@@ -39699,7 +40989,7 @@ var COMMAND_SPECS = [
39699
40989
  summary: "List the Space's custom roles.",
39700
40990
  args: [],
39701
40991
  flags: [],
39702
- scopes: ["roles:read"],
40992
+ scopes: [...roleOperations.list.scopes],
39703
40993
  returns: "RoleView"
39704
40994
  },
39705
40995
  {
@@ -39710,7 +41000,7 @@ var COMMAND_SPECS = [
39710
41000
  { name: "cap", type: "string", description: "Capability (default view).", default: "view" },
39711
41001
  { name: "expires", type: "string", description: "Expiry (ISO date/time)." }
39712
41002
  ],
39713
- scopes: ["links:write"],
41003
+ scopes: [...shareLinkOperations.create.scopes],
39714
41004
  returns: "CreateShareLinkResult",
39715
41005
  notes: "The signed token is returned once, in the create response."
39716
41006
  },
@@ -39719,7 +41009,7 @@ var COMMAND_SPECS = [
39719
41009
  summary: "List the share-links for a node.",
39720
41010
  args: [{ name: "node", required: true, description: "Node ULID." }],
39721
41011
  flags: [],
39722
- scopes: ["links:read"],
41012
+ scopes: [...shareLinkOperations.listForNode.scopes],
39723
41013
  returns: "ShareLinkPublic"
39724
41014
  },
39725
41015
  {
@@ -39729,7 +41019,7 @@ var COMMAND_SPECS = [
39729
41019
  flags: [
39730
41020
  { name: "allow-destructive", type: "boolean", description: "Required to actually revoke." }
39731
41021
  ],
39732
- scopes: ["links:write"],
41022
+ scopes: [...shareLinkOperations.revoke.scopes],
39733
41023
  returns: "ShareLinkPublic",
39734
41024
  notes: "Destructive \u2014 gated behind --allow-destructive + a links:write key."
39735
41025
  },
@@ -39766,16 +41056,16 @@ var COMMAND_SPECS = [
39766
41056
  description: "Comma-separated kinds to keep, e.g. task,project."
39767
41057
  }
39768
41058
  ],
39769
- scopes: [],
41059
+ scopes: [...changesOperations.list.scopes],
39770
41060
  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)."
41061
+ 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
41062
  },
39773
41063
  {
39774
41064
  path: ["keys", "list"],
39775
41065
  summary: "List your API keys: name, id, reach, and when each last changed.",
39776
41066
  args: [],
39777
41067
  flags: [],
39778
- scopes: ["keys:read"],
41068
+ scopes: [...keyOperations.list.scopes],
39779
41069
  returns: "ApiKeyListItem",
39780
41070
  notes: "Only the keys of the member behind this key; never a secret."
39781
41071
  },
@@ -39796,7 +41086,7 @@ var COMMAND_SPECS = [
39796
41086
  },
39797
41087
  { name: "name", type: "string", description: "A new name for the key." }
39798
41088
  ],
39799
- scopes: ["keys:write"],
41089
+ scopes: [...keyOperations.update.scopes],
39800
41090
  returns: "ApiKey",
39801
41091
  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
41092
  },
@@ -39805,7 +41095,7 @@ var COMMAND_SPECS = [
39805
41095
  summary: "Show the tenant and scopes the configured key resolves to.",
39806
41096
  args: [],
39807
41097
  flags: [],
39808
- scopes: [],
41098
+ scopes: [...whoamiOperations.whoami.scopes],
39809
41099
  returns: "ApiKeyContext"
39810
41100
  },
39811
41101
  {
@@ -42131,7 +43421,7 @@ var HANDLERS = {
42131
43421
  };
42132
43422
  },
42133
43423
  now: async ({ flags, requireClient }) => ({
42134
- data: await requireClient().get("/now", {
43424
+ data: await requireClient().get(pathOf(readOperations.now), {
42135
43425
  query: {
42136
43426
  for: flagString(flags, "for"),
42137
43427
  from: flagString(flags, "from"),
@@ -42141,13 +43431,13 @@ var HANDLERS = {
42141
43431
  render: renderNow
42142
43432
  }),
42143
43433
  landed: async ({ flags, requireClient }) => ({
42144
- data: await requireClient().get("/landed", {
43434
+ data: await requireClient().get(pathOf(readOperations.landed), {
42145
43435
  query: { from: flagString(flags, "from"), to: flagString(flags, "to") }
42146
43436
  }),
42147
43437
  render: renderLanded
42148
43438
  }),
42149
43439
  review: async ({ flags, requireClient }) => ({
42150
- data: await requireClient().get("/review", {
43440
+ data: await requireClient().get(pathOf(readOperations.review), {
42151
43441
  query: { from: flagString(flags, "from"), to: flagString(flags, "to") }
42152
43442
  }),
42153
43443
  render: renderReview
@@ -42461,14 +43751,14 @@ var HANDLERS = {
42461
43751
  if (q.length === 0)
42462
43752
  throw usageError('search needs text, e.g. `do search "passport"`');
42463
43753
  return {
42464
- data: await requireClient().get("/search", {
43754
+ data: await requireClient().get(pathOf(readOperations.search), {
42465
43755
  query: { q, type: flagString(flags, "type"), limit: flagString(flags, "limit") }
42466
43756
  }),
42467
43757
  render: renderSearch
42468
43758
  };
42469
43759
  },
42470
43760
  changes: async ({ flags, requireClient }) => {
42471
- const text = await requireClient().getText("/changes", {
43761
+ const text = await requireClient().getText(pathOf(changesOperations.list), {
42472
43762
  query: {
42473
43763
  since: flagString(flags, "since"),
42474
43764
  limit: flagString(flags, "limit"),
@@ -42480,7 +43770,7 @@ var HANDLERS = {
42480
43770
  "notes attached": async ({ args, requireClient }) => {
42481
43771
  const node = requireArg(args, 0, "node id");
42482
43772
  return {
42483
- data: await requireClient().get(`/nodes/${node}/notes`),
43773
+ data: await requireClient().get(pathOf(nodeOperations.getNotes, { id: node })),
42484
43774
  render: renderAttachedNote
42485
43775
  };
42486
43776
  },
@@ -42494,7 +43784,7 @@ var HANDLERS = {
42494
43784
  }
42495
43785
  const body = parseInput(setAttachedNoteInputSchema, { body: raw });
42496
43786
  return {
42497
- data: await requireClient().put(`/nodes/${node}/notes`, body),
43787
+ data: await requireClient().put(pathOf(nodeOperations.setNotes, { id: node }), body),
42498
43788
  render: renderAttachedNote
42499
43789
  };
42500
43790
  },
@@ -42524,18 +43814,18 @@ var HANDLERS = {
42524
43814
  "notes detach": async ({ args, flags, requireClient }) => {
42525
43815
  const node = requireArg(args, 0, "node id");
42526
43816
  assertDestructiveAllowed(flags, `detach the note from ${node}`);
42527
- await requireClient().delete(`/nodes/${node}/notes`);
43817
+ await requireClient().delete(pathOf(nodeOperations.clearNotes, { id: node }));
42528
43818
  return { data: { ok: true, detached: node }, render: renderRecord };
42529
43819
  },
42530
43820
  "orca list": async ({ requireClient }) => ({
42531
- data: await requireClient().get("/orca"),
43821
+ data: await requireClient().get(pathOf(orcaOperations.overview)),
42532
43822
  render: renderOrca
42533
43823
  }),
42534
43824
  "orca output": async ({ args, flags, requireClient }) => {
42535
43825
  const host = requireArg(args, 0, "host");
42536
43826
  const handle = requireArg(args, 1, "session handle");
42537
43827
  return {
42538
- data: await requireClient().get(`/orca/hosts/${encodeURIComponent(host)}/terminals/${encodeURIComponent(handle)}/output`, {
43828
+ data: await requireClient().get(pathOf(orcaOperations.output, { host, handle }), {
42539
43829
  query: {
42540
43830
  cursor: flagString(flags, "cursor"),
42541
43831
  limit: flagString(flags, "limit"),
@@ -42556,7 +43846,7 @@ var HANDLERS = {
42556
43846
  enter: !flagBool(flags, "no-enter")
42557
43847
  });
42558
43848
  return {
42559
- data: await requireClient().post(`/orca/hosts/${encodeURIComponent(host)}/terminals/${encodeURIComponent(handle)}/messages`, body),
43849
+ data: await requireClient().post(pathOf(orcaOperations.send, { host, handle }), body),
42560
43850
  render: renderRecord
42561
43851
  };
42562
43852
  },
@@ -42571,12 +43861,12 @@ var HANDLERS = {
42571
43861
  agent: flagString(flags, "agent")
42572
43862
  });
42573
43863
  return {
42574
- data: await requireClient().post(`/orca/hosts/${encodeURIComponent(host)}/sessions`, body),
43864
+ data: await requireClient().post(pathOf(orcaOperations.restart, { host }), body),
42575
43865
  render: renderRecord
42576
43866
  };
42577
43867
  },
42578
43868
  "worktree-links list": async ({ flags, requireClient }) => ({
42579
- data: await requireClient().get("/worktree-links", {
43869
+ data: await requireClient().get(pathOf(worktreeLinkOperations.list), {
42580
43870
  query: {
42581
43871
  host: flagString(flags, "host"),
42582
43872
  worktreeId: flagString(flags, "worktree"),
@@ -42588,7 +43878,7 @@ var HANDLERS = {
42588
43878
  "worktree-links get": async ({ args, requireClient }) => {
42589
43879
  const node = requireArg(args, 0, "node id");
42590
43880
  return {
42591
- data: await requireClient().get(`/worktree-links/${node}`),
43881
+ data: await requireClient().get(pathOf(worktreeLinkOperations.get, { nodeId: node })),
42592
43882
  render: renderWorktreeLink
42593
43883
  };
42594
43884
  },
@@ -42606,14 +43896,14 @@ var HANDLERS = {
42606
43896
  branch: flagString(flags, "branch")
42607
43897
  });
42608
43898
  return {
42609
- data: await requireClient().put(`/worktree-links/${node}`, body),
43899
+ data: await requireClient().put(pathOf(worktreeLinkOperations.set, { nodeId: node }), body),
42610
43900
  render: renderWorktreeLink
42611
43901
  };
42612
43902
  },
42613
43903
  "worktree-links clear": async ({ args, flags, requireClient }) => {
42614
43904
  const node = requireArg(args, 0, "node id");
42615
43905
  assertDestructiveAllowed(flags, `unlink ${node} from its worktree`);
42616
- await requireClient().delete(`/worktree-links/${node}`);
43906
+ await requireClient().delete(pathOf(worktreeLinkOperations.clear, { nodeId: node }));
42617
43907
  return { data: { ok: true, unlinked: node }, render: renderRecord };
42618
43908
  },
42619
43909
  "files list": async ({ flags, requireClient }) => ({
@@ -43511,7 +44801,7 @@ var HANDLERS = {
43511
44801
  render: renderList
43512
44802
  }),
43513
44803
  "agent connection get": async ({ requireClient }) => ({
43514
- data: await requireClient().get("/agent/connection"),
44804
+ data: await requireClient().get(pathOf(agentConnectionOperations.get)),
43515
44805
  render: renderRecord
43516
44806
  }),
43517
44807
  "agent connection set": async ({ io, flags, requireClient }) => {
@@ -43528,11 +44818,14 @@ var HANDLERS = {
43528
44818
  autoFile,
43529
44819
  apiKey: await readAgentKey(io, flags)
43530
44820
  });
43531
- return { data: await client.put("/agent/connection", body), render: renderRecord };
44821
+ return {
44822
+ data: await client.put(pathOf(agentConnectionOperations.save), body),
44823
+ render: renderRecord
44824
+ };
43532
44825
  },
43533
44826
  "agent connection remove": async ({ flags, requireClient }) => {
43534
44827
  assertDestructiveAllowed(flags, "remove the agent connection");
43535
- await requireClient().delete("/agent/connection");
44828
+ await requireClient().delete(pathOf(agentConnectionOperations.remove));
43536
44829
  return { data: { ok: true, removed: "agent connection" }, render: renderRecord };
43537
44830
  },
43538
44831
  "agent post": agentPost,
@@ -43665,7 +44958,7 @@ var HANDLERS = {
43665
44958
  return { data: result, render: renderPresetInstall };
43666
44959
  },
43667
44960
  "collection types": async ({ requireClient }) => {
43668
- const types = collectionTypeSummarySchema.array().parse(await requireClient().get("/collection/types"));
44961
+ const types = collectionTypeSummarySchema.array().parse(await requireClient().get(pathOf(collectionOperations.types)));
43669
44962
  return { data: types, render: renderList };
43670
44963
  },
43671
44964
  "collection list": async ({ flags, requireClient }) => {
@@ -43677,7 +44970,10 @@ var HANDLERS = {
43677
44970
  title: flagString(flags, "title")
43678
44971
  };
43679
44972
  applyPageFlags(query, flags);
43680
- return { data: await requireClient().getPage("/collection", { query }), render: renderList };
44973
+ return {
44974
+ data: await requireClient().getPage(pathOf(collectionOperations.list), { query }),
44975
+ render: renderList
44976
+ };
43681
44977
  },
43682
44978
  "collection add": async ({ args, flags, requireClient }) => {
43683
44979
  const { title, tags } = parseTitleTags(args);
@@ -43691,13 +44987,19 @@ var HANDLERS = {
43691
44987
  ...tags.length > 0 ? { tags } : {},
43692
44988
  ...flagString(flags, "status") ? { status: flagString(flags, "status") } : {}
43693
44989
  });
43694
- return { data: await requireClient().post("/collection", body), render: renderRecord };
44990
+ return {
44991
+ data: await requireClient().post(pathOf(collectionOperations.create), body),
44992
+ render: renderRecord
44993
+ };
43695
44994
  },
43696
44995
  "collection set-status": async ({ args, requireClient }) => {
43697
44996
  const id = requireArg(args, 0, "item id");
43698
44997
  const status = requireArg(args, 1, "status");
43699
44998
  const body = parseInput(updateCollectionItemInputSchema, { status });
43700
- return { data: await requireClient().patch(`/collection/${id}`, body), render: renderRecord };
44999
+ return {
45000
+ data: await requireClient().patch(pathOf(collectionOperations.update, { id }), body),
45001
+ render: renderRecord
45002
+ };
43701
45003
  },
43702
45004
  "collection add-entry": async ({ args, flags, io, requireClient }) => {
43703
45005
  const id = requireArg(args, 0, "item id");
@@ -43719,7 +45021,7 @@ var HANDLERS = {
43719
45021
  ...flagString(flags, "contact") ? { contactId: flagString(flags, "contact") } : {}
43720
45022
  });
43721
45023
  return {
43722
- data: await requireClient().post(`/collection/${id}/entries`, body),
45024
+ data: await requireClient().post(pathOf(collectionOperations.addEntry, { id }), body),
43723
45025
  render: renderRecord
43724
45026
  };
43725
45027
  },
@@ -43767,7 +45069,10 @@ var HANDLERS = {
43767
45069
  ...flagString(flags, "note") ? { note: flagString(flags, "note") } : {},
43768
45070
  ...flagString(flags, "on") ? { occurredOn: flagString(flags, "on") } : {}
43769
45071
  });
43770
- return { data: await requireClient().post("/collection/log", body), render: renderRecord };
45072
+ return {
45073
+ data: await requireClient().post(pathOf(collectionOperations.log), body),
45074
+ render: renderRecord
45075
+ };
43771
45076
  },
43772
45077
  "collection edit-entry": async ({ args, flags, requireClient }) => {
43773
45078
  const id = requireArg(args, 0, "item id");
@@ -43782,7 +45087,7 @@ var HANDLERS = {
43782
45087
  ...flagString(flags, "on") ? { occurredOn: flagString(flags, "on") } : {}
43783
45088
  });
43784
45089
  return {
43785
- data: await requireClient().patch(`/collection/${id}/entries/${entryId}`, body),
45090
+ data: await requireClient().patch(pathOf(collectionOperations.editEntry, { id, entryId }), body),
43786
45091
  render: renderRecord
43787
45092
  };
43788
45093
  },
@@ -43791,17 +45096,17 @@ var HANDLERS = {
43791
45096
  const url = flagString(flags, "url");
43792
45097
  const body = parseInput(collectionCoverInputSchema, url ? { url } : {});
43793
45098
  return {
43794
- data: await requireClient().post(`/collection/${id}/cover`, body),
45099
+ data: await requireClient().post(pathOf(collectionOperations.cover, { id }), body),
43795
45100
  render: renderRecord
43796
45101
  };
43797
45102
  },
43798
45103
  "collection cover-candidates": async ({ args, requireClient }) => {
43799
45104
  const id = requireArg(args, 0, "item id");
43800
- const result = collectionCoverCandidatesSchema.parse(await requireClient().get(`/collection/${id}/cover/candidates`));
45105
+ const result = collectionCoverCandidatesSchema.parse(await requireClient().get(pathOf(collectionOperations.coverCandidates, { id })));
43801
45106
  return { data: result, render: renderRecord };
43802
45107
  },
43803
45108
  "collection covers": async ({ requireClient }) => {
43804
- const result = collectionCoverBackfillResultSchema.parse(await requireClient().post("/collection/covers", undefined));
45109
+ const result = collectionCoverBackfillResultSchema.parse(await requireClient().post(pathOf(collectionOperations.backfillCovers), undefined));
43805
45110
  return { data: result, render: renderRecord };
43806
45111
  },
43807
45112
  "areas purpose": async ({ args, flags, requireClient }) => {
@@ -44582,7 +45887,10 @@ var HANDLERS = {
44582
45887
  if (flagBool(flags, "new-session"))
44583
45888
  body.newSession = true;
44584
45889
  const input = parseInput(applyRunChangesInputSchema, body);
44585
- return { data: await requireClient().post(`/runs/${id}/changes`, input), render: renderRecord };
45890
+ return {
45891
+ data: await requireClient().post(pathOf(runOperations.applyChanges, { id }), input),
45892
+ render: renderRecord
45893
+ };
44586
45894
  },
44587
45895
  "runs save-header": async ({ args, flags, io, requireClient }) => {
44588
45896
  const id = requireArg(args, 0, "run id");
@@ -44619,7 +45927,10 @@ var HANDLERS = {
44619
45927
  const id = requireArg(args, 0, "run id");
44620
45928
  const batch = flagString(flags, "batch");
44621
45929
  const input = parseInput(undoRunChangesInputSchema, batch ? { batchId: batch } : {});
44622
- return { data: await requireClient().post(`/runs/${id}/undo`, input), render: renderRecord };
45930
+ return {
45931
+ data: await requireClient().post(pathOf(runOperations.undo, { id }), input),
45932
+ render: renderRecord
45933
+ };
44623
45934
  },
44624
45935
  "runs dex tick": async ({ args, flags, requireClient, io }) => {
44625
45936
  const id = requireArg(args, 0, "run id");
@@ -44643,19 +45954,22 @@ var HANDLERS = {
44643
45954
  ...message ? { message } : {}
44644
45955
  });
44645
45956
  return {
44646
- data: await requireClient().post(`/runs/${id}/checklists/${encodeURIComponent(key)}/tick`, input),
45957
+ data: await requireClient().post(pathOf(runOperations.tickChecklist, { id, key }), input),
44647
45958
  render: renderRecord
44648
45959
  };
44649
45960
  },
44650
45961
  "runs steam status": async ({ requireClient }) => ({
44651
- data: await requireClient().get("/runs/steam"),
45962
+ data: await requireClient().get(pathOf(runOperations.steamStatus)),
44652
45963
  render: renderRecord
44653
45964
  }),
44654
45965
  "runs steam sync": async ({ flags, requireClient }) => {
44655
45966
  const body = parseInput(steamSyncInputSchema, {
44656
45967
  ...typeof flags.game === "string" ? { gameId: flags.game } : {}
44657
45968
  });
44658
- return { data: await requireClient().post("/runs/steam/sync", body), render: renderRecord };
45969
+ return {
45970
+ data: await requireClient().post(pathOf(runOperations.steamSync), body),
45971
+ render: renderRecord
45972
+ };
44659
45973
  },
44660
45974
  "sensors status": async ({ requireClient }) => ({
44661
45975
  data: await requireClient().get(pathOf(sensorOperations.status)),
@@ -44707,13 +46021,16 @@ var HANDLERS = {
44707
46021
  },
44708
46022
  "runs sessions list": async ({ args, requireClient }) => {
44709
46023
  const id = requireArg(args, 0, "run id");
44710
- return { data: await requireClient().get(`/runs/${id}/sessions`), render: renderList };
46024
+ return {
46025
+ data: await requireClient().get(pathOf(runOperations.listSessions, { id })),
46026
+ render: renderList
46027
+ };
44711
46028
  },
44712
46029
  "runs sessions add": async ({ args, flags, requireClient }) => {
44713
46030
  const id = requireArg(args, 0, "run id");
44714
46031
  const input = parseInput(createRunSessionInputSchema, sessionFlags(flags));
44715
46032
  return {
44716
- data: await requireClient().post(`/runs/${id}/sessions`, input),
46033
+ data: await requireClient().post(pathOf(runOperations.addSession, { id }), input),
44717
46034
  render: renderRecord
44718
46035
  };
44719
46036
  },
@@ -44722,7 +46039,7 @@ var HANDLERS = {
44722
46039
  const session = requireArg(args, 1, "session id");
44723
46040
  const input = parseInput(updateRunSessionInputSchema, sessionFlags(flags));
44724
46041
  return {
44725
- data: await requireClient().patch(`/runs/${id}/sessions/${session}`, input),
46042
+ data: await requireClient().patch(pathOf(runOperations.updateSession, { id, sessionId: session }), input),
44726
46043
  render: renderRecord
44727
46044
  };
44728
46045
  },
@@ -44730,7 +46047,7 @@ var HANDLERS = {
44730
46047
  const id = requireArg(args, 0, "run id");
44731
46048
  const session = requireArg(args, 1, "session id");
44732
46049
  assertDestructiveAllowed(flags, `delete session ${session}`);
44733
- await requireClient().delete(`/runs/${id}/sessions/${session}`);
46050
+ await requireClient().delete(pathOf(runOperations.removeSession, { id, sessionId: session }));
44734
46051
  return { data: { ok: true, removed: session }, render: renderRecord };
44735
46052
  },
44736
46053
  ...healthRecordHandlers(),
@@ -44807,11 +46124,11 @@ var HANDLERS = {
44807
46124
  render: renderRecord
44808
46125
  }),
44809
46126
  "health withings status": async ({ requireClient }) => ({
44810
- data: await requireClient().get("/health/withings"),
46127
+ data: await requireClient().get(pathOf(withingsOperations.status)),
44811
46128
  render: renderRecord
44812
46129
  }),
44813
46130
  "health withings sync": async ({ requireClient }) => ({
44814
- data: await requireClient().post("/health/withings/sync", {}),
46131
+ data: await requireClient().post(pathOf(withingsOperations.sync), {}),
44815
46132
  render: renderRecord
44816
46133
  }),
44817
46134
  "health google status": async ({ requireClient }) => ({
@@ -45383,20 +46700,23 @@ var HANDLERS = {
45383
46700
  },
45384
46701
  "invite create": async ({ flags, requireClient }) => {
45385
46702
  const body = parseInput(createInviteInputSchema, { id: ulid3(), ...readDataFlag(flags) });
45386
- return { data: await requireClient().post("/invites", body), render: renderRecord };
46703
+ return {
46704
+ data: await requireClient().post(pathOf(inviteOperations.create), body),
46705
+ render: renderRecord
46706
+ };
45387
46707
  },
45388
46708
  "invite list": async ({ requireClient }) => ({
45389
- data: await requireClient().get("/invites"),
46709
+ data: await requireClient().get(pathOf(inviteOperations.list)),
45390
46710
  render: renderList
45391
46711
  }),
45392
46712
  "invite revoke": async ({ args, flags, requireClient }) => {
45393
46713
  const id = requireArg(args, 0, "invite id");
45394
46714
  assertDestructiveAllowed(flags, `revoke invite ${id}`);
45395
- await requireClient().delete(`/invites/${id}`);
46715
+ await requireClient().delete(pathOf(inviteOperations.revoke, { id }));
45396
46716
  return { data: { ok: true, revoked: id }, render: renderRecord };
45397
46717
  },
45398
46718
  "member list": async ({ requireClient }) => ({
45399
- data: await requireClient().get("/members"),
46719
+ data: await requireClient().get(pathOf(memberOperations.list)),
45400
46720
  render: renderList
45401
46721
  }),
45402
46722
  "member role": async ({ args, flags, requireClient }) => {
@@ -45404,12 +46724,15 @@ var HANDLERS = {
45404
46724
  const role = flagString(flags, "role");
45405
46725
  const raw = flagString(flags, "data") ? readDataFlag(flags) : role ? { role } : {};
45406
46726
  const body = parseInput(setMemberRoleInputSchema, raw);
45407
- return { data: await requireClient().patch(`/members/${id}/role`, body), render: renderRecord };
46727
+ return {
46728
+ data: await requireClient().patch(pathOf(memberOperations.setRole, { id }), body),
46729
+ render: renderRecord
46730
+ };
45408
46731
  },
45409
46732
  "member remove": async ({ args, flags, requireClient }) => {
45410
46733
  const id = requireArg(args, 0, "member id");
45411
46734
  assertDestructiveAllowed(flags, `remove member ${id}`);
45412
- await requireClient().delete(`/members/${id}`);
46735
+ await requireClient().delete(pathOf(memberOperations.remove, { id }));
45413
46736
  return { data: { ok: true, removed: id }, render: renderRecord };
45414
46737
  },
45415
46738
  share: async ({ args, flags, requireClient }) => {
@@ -45422,19 +46745,22 @@ var HANDLERS = {
45422
46745
  capability: flagString(flags, "cap") ?? "view"
45423
46746
  });
45424
46747
  return {
45425
- data: await requireClient().post(`/sharing/nodes/${node}/grants`, body),
46748
+ data: await requireClient().post(pathOf(sharingOperations.createGrant, { nodeId: node }), body),
45426
46749
  render: renderRecord
45427
46750
  };
45428
46751
  },
45429
46752
  "share list": async ({ args, requireClient }) => {
45430
46753
  const node = requireArg(args, 0, "node id");
45431
- return { data: await requireClient().get(`/sharing/nodes/${node}/grants`), render: renderList };
46754
+ return {
46755
+ data: await requireClient().get(pathOf(sharingOperations.listGrants, { nodeId: node })),
46756
+ render: renderList
46757
+ };
45432
46758
  },
45433
46759
  "share revoke": async ({ args, flags, requireClient }) => {
45434
46760
  const node = requireArg(args, 0, "node id");
45435
46761
  const grant = requireArg(args, 1, "grant id");
45436
46762
  assertDestructiveAllowed(flags, `revoke grant ${grant}`);
45437
- await requireClient().delete(`/sharing/nodes/${node}/grants/${grant}`);
46763
+ await requireClient().delete(pathOf(sharingOperations.revokeGrant, { nodeId: node, grantId: grant }));
45438
46764
  return { data: { ok: true, revoked: grant }, render: renderRecord };
45439
46765
  },
45440
46766
  assign: async ({ args, flags, requireClient }) => {
@@ -45446,7 +46772,7 @@ var HANDLERS = {
45446
46772
  ...flagString(flags, "cap") ? { capability: flagString(flags, "cap") } : {}
45447
46773
  });
45448
46774
  return {
45449
- data: await requireClient().put(`/sharing/nodes/${node}/assignee`, body),
46775
+ data: await requireClient().put(pathOf(sharingOperations.assign, { nodeId: node }), body),
45450
46776
  render: renderRecord
45451
46777
  };
45452
46778
  },
@@ -45456,17 +46782,20 @@ var HANDLERS = {
45456
46782
  if (body.length === 0)
45457
46783
  throw usageError('comment needs text, e.g. `do comment <node> "Nice!"`');
45458
46784
  const input = parseInput(createCommentInputSchema, { id: ulid3(), nodeId: node, body });
45459
- return { data: await requireClient().post("/comments", input), render: renderRecord };
46785
+ return {
46786
+ data: await requireClient().post(pathOf(commentOperations.create), input),
46787
+ render: renderRecord
46788
+ };
45460
46789
  },
45461
46790
  "comment list": async ({ args, requireClient }) => {
45462
46791
  const node = requireArg(args, 0, "node id");
45463
46792
  return {
45464
- data: await requireClient().get("/comments", { query: { nodeId: node } }),
46793
+ data: await requireClient().get(pathOf(commentOperations.list), { query: { nodeId: node } }),
45465
46794
  render: renderList
45466
46795
  };
45467
46796
  },
45468
46797
  "role list": async ({ requireClient }) => ({
45469
- data: await requireClient().get("/roles"),
46798
+ data: await requireClient().get(pathOf(roleOperations.list)),
45470
46799
  render: renderList
45471
46800
  }),
45472
46801
  "link create": async ({ args, flags, requireClient }) => {
@@ -45477,20 +46806,26 @@ var HANDLERS = {
45477
46806
  ...flagString(flags, "cap") ? { capability: flagString(flags, "cap") } : {},
45478
46807
  ...flagString(flags, "expires") ? { expiresAt: flagString(flags, "expires") } : {}
45479
46808
  });
45480
- return { data: await requireClient().post("/share-links", body), render: renderRecord };
46809
+ return {
46810
+ data: await requireClient().post(pathOf(shareLinkOperations.create), body),
46811
+ render: renderRecord
46812
+ };
45481
46813
  },
45482
46814
  "link list": async ({ args, requireClient }) => {
45483
46815
  const node = requireArg(args, 0, "node id");
45484
- return { data: await requireClient().get(`/share-links/node/${node}`), render: renderList };
46816
+ return {
46817
+ data: await requireClient().get(pathOf(shareLinkOperations.listForNode, { nodeId: node })),
46818
+ render: renderList
46819
+ };
45485
46820
  },
45486
46821
  "link revoke": async ({ args, flags, requireClient }) => {
45487
46822
  const id = requireArg(args, 0, "share-link id");
45488
46823
  assertDestructiveAllowed(flags, `revoke share-link ${id}`);
45489
- await requireClient().delete(`/share-links/${id}`);
46824
+ await requireClient().delete(pathOf(shareLinkOperations.revoke, { id }));
45490
46825
  return { data: { ok: true, revoked: id }, render: renderRecord };
45491
46826
  },
45492
46827
  "keys list": async ({ requireClient }) => ({
45493
- data: await requireClient().getPage("/keys"),
46828
+ data: await requireClient().getPage(pathOf(keyOperations.list)),
45494
46829
  render: renderKeys
45495
46830
  }),
45496
46831
  "keys update": async ({ args, flags, requireClient }) => {
@@ -45509,10 +46844,13 @@ var HANDLERS = {
45509
46844
  ...name === undefined ? {} : { name },
45510
46845
  ...scopes === undefined ? {} : { scopes: includePrivate ? [...scopes, "private:*"] : scopes }
45511
46846
  };
45512
- return { data: await requireClient().patch(`/keys/${id}`, body), render: renderRecord };
46847
+ return {
46848
+ data: await requireClient().patch(pathOf(keyOperations.update, { id }), body),
46849
+ render: renderRecord
46850
+ };
45513
46851
  },
45514
46852
  whoami: async ({ requireClient }) => ({
45515
- data: await requireClient().get("/whoami"),
46853
+ data: await requireClient().get(pathOf(whoamiOperations.whoami)),
45516
46854
  render: renderWhoami
45517
46855
  }),
45518
46856
  login: async ({ io, config, requireClient }) => {