primitive-admin 1.2.0-alpha.3 → 1.2.0-alpha.5

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 (143) hide show
  1. package/assets/skill/skills/primitive-platform/SKILL.md +17 -0
  2. package/dist/src/commands/agent-sessions.js +4 -0
  3. package/dist/src/commands/agent-sessions.js.map +1 -1
  4. package/dist/src/commands/auth.js +7 -4
  5. package/dist/src/commands/auth.js.map +1 -1
  6. package/dist/src/commands/connections.js +11 -12
  7. package/dist/src/commands/connections.js.map +1 -1
  8. package/dist/src/commands/database-type-configs.js +2 -76
  9. package/dist/src/commands/database-type-configs.js.map +1 -1
  10. package/dist/src/commands/database-types.js +313 -13
  11. package/dist/src/commands/database-types.js.map +1 -1
  12. package/dist/src/commands/databases.d.ts +9 -0
  13. package/dist/src/commands/databases.js +78 -403
  14. package/dist/src/commands/databases.js.map +1 -1
  15. package/dist/src/commands/documents.d.ts +32 -2
  16. package/dist/src/commands/documents.js +344 -711
  17. package/dist/src/commands/documents.js.map +1 -1
  18. package/dist/src/commands/functions.js +2 -2
  19. package/dist/src/commands/functions.js.map +1 -1
  20. package/dist/src/commands/llm.js +6 -9
  21. package/dist/src/commands/llm.js.map +1 -1
  22. package/dist/src/commands/prompts.d.ts +7 -0
  23. package/dist/src/commands/prompts.js +44 -0
  24. package/dist/src/commands/prompts.js.map +1 -1
  25. package/dist/src/commands/settings.d.ts +9 -12
  26. package/dist/src/commands/settings.js +79 -31
  27. package/dist/src/commands/settings.js.map +1 -1
  28. package/dist/src/commands/sync.d.ts +43 -110
  29. package/dist/src/commands/sync.js +356 -1251
  30. package/dist/src/commands/sync.js.map +1 -1
  31. package/dist/src/lib/aggregate-output.d.ts +25 -0
  32. package/dist/src/lib/aggregate-output.js +53 -0
  33. package/dist/src/lib/aggregate-output.js.map +1 -0
  34. package/dist/src/lib/api-client.d.ts +48 -204
  35. package/dist/src/lib/api-client.js +57 -321
  36. package/dist/src/lib/api-client.js.map +1 -1
  37. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +6 -7
  38. package/dist/src/lib/codegen-shared/generatedFiles.js +2 -2
  39. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
  40. package/dist/src/lib/codegen-shared/prettierStable.d.ts +1 -1
  41. package/dist/src/lib/codegen-shared/prettierStable.js +1 -1
  42. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +2 -2
  43. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +2 -2
  44. package/dist/src/lib/config-object-descriptor.js +15 -10
  45. package/dist/src/lib/config-object-descriptor.js.map +1 -1
  46. package/dist/src/lib/config-surface.d.ts +2 -1
  47. package/dist/src/lib/config-surface.js +4 -1
  48. package/dist/src/lib/config-surface.js.map +1 -1
  49. package/dist/src/lib/connections-list.d.ts +10 -0
  50. package/dist/src/lib/connections-list.js +18 -0
  51. package/dist/src/lib/connections-list.js.map +1 -0
  52. package/dist/src/lib/csv.d.ts +8 -9
  53. package/dist/src/lib/csv.js +3 -4
  54. package/dist/src/lib/csv.js.map +1 -1
  55. package/dist/src/lib/db-codegen/dbGenerator.js +5 -16
  56. package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -1
  57. package/dist/src/lib/db-codegen/dbNaming.d.ts +5 -13
  58. package/dist/src/lib/db-codegen/dbNaming.js +5 -17
  59. package/dist/src/lib/db-codegen/dbNaming.js.map +1 -1
  60. package/dist/src/lib/db-codegen/dbTemplates.d.ts +7 -56
  61. package/dist/src/lib/db-codegen/dbTemplates.js +25 -125
  62. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
  63. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +2 -2
  64. package/dist/src/lib/db-codegen/dbTsTypes.js +2 -2
  65. package/dist/src/lib/db-codegen/dbTypeIR.d.ts +60 -2
  66. package/dist/src/lib/db-codegen/dbTypeIR.js +12 -1
  67. package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -1
  68. package/dist/src/lib/document-export-record-log.d.ts +57 -0
  69. package/dist/src/lib/document-export-record-log.js +215 -0
  70. package/dist/src/lib/document-export-record-log.js.map +1 -0
  71. package/dist/src/lib/document-ingest-artifact.d.ts +13 -2
  72. package/dist/src/lib/document-ingest-artifact.js +7 -3
  73. package/dist/src/lib/document-ingest-artifact.js.map +1 -1
  74. package/dist/src/lib/document-ingest-rows.d.ts +16 -0
  75. package/dist/src/lib/document-ingest-rows.js +17 -0
  76. package/dist/src/lib/document-ingest-rows.js.map +1 -1
  77. package/dist/src/lib/document-ingest.d.ts +22 -0
  78. package/dist/src/lib/document-ingest.js +33 -0
  79. package/dist/src/lib/document-ingest.js.map +1 -1
  80. package/dist/src/lib/function-sync.d.ts +3 -16
  81. package/dist/src/lib/function-sync.js +14 -18
  82. package/dist/src/lib/function-sync.js.map +1 -1
  83. package/dist/src/lib/function-triggers.d.ts +3 -15
  84. package/dist/src/lib/function-triggers.js +10 -72
  85. package/dist/src/lib/function-triggers.js.map +1 -1
  86. package/dist/src/lib/generated-allowlist.js +0 -20
  87. package/dist/src/lib/generated-allowlist.js.map +1 -1
  88. package/dist/src/lib/generated-config-surfaces.d.ts +212 -49
  89. package/dist/src/lib/generated-config-surfaces.js +759 -184
  90. package/dist/src/lib/generated-config-surfaces.js.map +1 -1
  91. package/dist/src/lib/generated-sdk-types.d.ts +1 -1
  92. package/dist/src/lib/generated-sdk-types.js +1 -1
  93. package/dist/src/lib/generated-sdk-types.js.map +1 -1
  94. package/dist/src/lib/login-scope-lines.d.ts +15 -0
  95. package/dist/src/lib/login-scope-lines.js +22 -0
  96. package/dist/src/lib/login-scope-lines.js.map +1 -0
  97. package/dist/src/lib/paginate.d.ts +0 -5
  98. package/dist/src/lib/paginate.js +0 -5
  99. package/dist/src/lib/paginate.js.map +1 -1
  100. package/dist/src/lib/prompt-schema-codegen.js +5 -2
  101. package/dist/src/lib/prompt-schema-codegen.js.map +1 -1
  102. package/dist/src/lib/refresh-admin-credentials.d.ts +7 -0
  103. package/dist/src/lib/refresh-admin-credentials.js +16 -4
  104. package/dist/src/lib/refresh-admin-credentials.js.map +1 -1
  105. package/dist/src/lib/root-import-decision.d.ts +15 -14
  106. package/dist/src/lib/root-import-decision.js +44 -51
  107. package/dist/src/lib/root-import-decision.js.map +1 -1
  108. package/dist/src/lib/snapshot-build-rows.d.ts +21 -0
  109. package/dist/src/lib/snapshot-build-rows.js +22 -0
  110. package/dist/src/lib/snapshot-build-rows.js.map +1 -1
  111. package/dist/src/lib/snapshot-build.js +8 -3
  112. package/dist/src/lib/snapshot-build.js.map +1 -1
  113. package/dist/src/lib/swift-codegen/banners.d.ts +34 -7
  114. package/dist/src/lib/swift-codegen/banners.js +37 -7
  115. package/dist/src/lib/swift-codegen/banners.js.map +1 -1
  116. package/dist/src/lib/swift-codegen/dbGenerator.d.ts +4 -3
  117. package/dist/src/lib/swift-codegen/dbGenerator.js +14 -117
  118. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
  119. package/dist/src/lib/swift-codegen/functionGenerator.d.ts +3 -3
  120. package/dist/src/lib/swift-codegen/functionGenerator.js +22 -17
  121. package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -1
  122. package/dist/src/lib/swift-codegen/generator.d.ts +5 -5
  123. package/dist/src/lib/swift-codegen/generator.js +35 -40
  124. package/dist/src/lib/swift-codegen/generator.js.map +1 -1
  125. package/dist/src/lib/toml-database-config.d.ts +14 -75
  126. package/dist/src/lib/toml-database-config.js +14 -305
  127. package/dist/src/lib/toml-database-config.js.map +1 -1
  128. package/dist/src/lib/workflow-codegen/generator.d.ts +7 -7
  129. package/dist/src/lib/workflow-codegen/generator.js +16 -23
  130. package/dist/src/lib/workflow-codegen/generator.js.map +1 -1
  131. package/dist/src/lib/workflow-codegen/invokerIR.d.ts +6 -12
  132. package/dist/src/lib/workflow-codegen/invokerIR.js +4 -5
  133. package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -1
  134. package/dist/src/lib/workflow-toml-validator.d.ts +2 -0
  135. package/dist/src/lib/workflow-toml-validator.js +28 -1
  136. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  137. package/package.json +3 -5
  138. package/dist/src/lib/generated-workflow-model-fields.d.ts +0 -20
  139. package/dist/src/lib/generated-workflow-model-fields.js +0 -53
  140. package/dist/src/lib/generated-workflow-model-fields.js.map +0 -1
  141. package/dist/src/lib/workflow-field-descriptor.d.ts +0 -91
  142. package/dist/src/lib/workflow-field-descriptor.js +0 -153
  143. package/dist/src/lib/workflow-field-descriptor.js.map +0 -1
@@ -11,6 +11,15 @@ import { drainAppsUntilConverged, } from "./workflow-usage.js";
11
11
  * fewest round trips the contract allows; a larger value is clamped there.
12
12
  */
13
13
  const ADMIN_INVENTORY_PAGE_SIZE = 100;
14
+ /** The agent block's wire keys, in the surface's order. */
15
+ const AGENT_TEST_CASE_WIRE_KEYS = [
16
+ "agentHistory",
17
+ "agentContext",
18
+ "agentTimezone",
19
+ "agentLocale",
20
+ "agentExpectedToolCall",
21
+ "agentExpectedNoToolCall",
22
+ ];
14
23
  export class ApiError extends Error {
15
24
  statusCode;
16
25
  code;
@@ -125,55 +134,15 @@ export function parseErrorResponse(response, text, path) {
125
134
  return { message, code, details, raw: errorData };
126
135
  }
127
136
  /**
128
- * Typed exception classes for the database-schema feature (issue #666).
129
- * Each maps 1:1 to a server `code` value emitted from the op-edit or
130
- * schema-edit gate. They all extend ApiError so existing catch-all paths
131
- * continue to work; specialized catch blocks can branch on `instanceof`.
132
- *
133
- * Per round-2 addendum A1, `details` is always preserved so callers can
134
- * extract structured offender lists (refs[], operations[], etc.).
137
+ * Typed exception for the database-schema feature (issue #666): a stored
138
+ * schema that does not parse. `details` carries the line and column.
139
+ * (#3189 — the operation gates' exceptions retired with operations.)
135
140
  */
136
- export class SchemaRequiredError extends ApiError {
137
- constructor(message, details) {
138
- super(message, 422, "SCHEMA_REQUIRED", details);
139
- this.name = "SchemaRequiredError";
140
- }
141
- }
142
141
  function detailsRecord(details) {
143
142
  return details && !Array.isArray(details) && typeof details === "object"
144
143
  ? details
145
144
  : undefined;
146
145
  }
147
- export class OperationRefError extends ApiError {
148
- constructor(message, details) {
149
- super(message, 422, "OPERATION_REFERENCES_UNDEFINED", details);
150
- this.name = "OperationRefError";
151
- }
152
- get refs() {
153
- const d = detailsRecord(this.details);
154
- return Array.isArray(d?.refs) ? d.refs : [];
155
- }
156
- }
157
- export class SchemaBreaksOpsError extends ApiError {
158
- constructor(message, details) {
159
- super(message, 422, "SCHEMA_BREAKS_OPERATIONS", details);
160
- this.name = "SchemaBreaksOpsError";
161
- }
162
- get operations() {
163
- const d = detailsRecord(this.details);
164
- return Array.isArray(d?.operations) ? d.operations : [];
165
- }
166
- }
167
- export class SchemaHasUncheckableOpsError extends ApiError {
168
- constructor(message, details) {
169
- super(message, 422, "SCHEMA_HAS_UNCHECKABLE_OPS", details);
170
- this.name = "SchemaHasUncheckableOpsError";
171
- }
172
- get operations() {
173
- const d = detailsRecord(this.details);
174
- return Array.isArray(d?.operations) ? d.operations : [];
175
- }
176
- }
177
146
  export class TomlParseError extends ApiError {
178
147
  constructor(message, details) {
179
148
  super(message, 400, "TOML_PARSE_ERROR", details);
@@ -188,55 +157,6 @@ export class TomlParseError extends ApiError {
188
157
  return typeof d?.column === "number" ? d.column : undefined;
189
158
  }
190
159
  }
191
- export class OpsExistError extends ApiError {
192
- constructor(message, details) {
193
- super(message, 409, "OPS_EXIST", details);
194
- this.name = "OpsExistError";
195
- }
196
- get opCount() {
197
- const d = detailsRecord(this.details);
198
- return typeof d?.opCount === "number" ? d.opCount : 0;
199
- }
200
- }
201
- /**
202
- * Normalize a `databases operations execute` result to the CLI's one list
203
- * envelope — but only when the result is a Durable Object query page
204
- * (issue #2440).
205
- *
206
- * A registered operation is a list only sometimes. Of the shapes the server's
207
- * operation dispatch can return, exactly two carry a top-level `data` array: a
208
- * bare `query` and a `pipeline` whose `returnField` names a query step. A
209
- * `count` (`{ count }`), an `aggregate` (`{ result }`), a mutation
210
- * (`{ results }`), an `applyToQuery` (`{ matched, affected, failed, … }`) and a
211
- * `returnField: "all"` pipeline (`{ steps: { … } }`) do not, so blanket
212
- * normalization would corrupt them. This helper therefore tests the shape
213
- * first and passes everything else through byte-for-byte — including the
214
- * nested steps of a `returnField: "all"` pipeline, which are operation-defined
215
- * objects the CLI must not reach into.
216
- *
217
- * Deliberately narrow, not a general-purpose predicate: the contract is
218
- * specifically "an operations-execute result may be a DO query page".
219
- * `executeDatabaseOperation()` is the only caller.
220
- *
221
- * The conversion itself is delegated to `normalizeCliListEnvelope()` so cursor
222
- * handling, `prevCursor` dropping and `hasMore` derivation have exactly one
223
- * implementation. That helper throws on an unrecognized shape — correct when
224
- * the payload is supposed to be a list, wrong here, where the payload is the
225
- * caller's own result — which is why the shape test has to run first. `_timing`
226
- * (`--timing`) is re-attached afterwards, since it rides alongside the envelope
227
- * rather than inside it.
228
- */
229
- export function normalizeDatabaseOperationResult(raw) {
230
- if (raw === null || typeof raw !== "object" || Array.isArray(raw))
231
- return raw;
232
- const page = raw;
233
- if (!Array.isArray(page.data))
234
- return raw;
235
- const envelope = normalizeCliListEnvelope(page);
236
- if (page._timing !== undefined)
237
- envelope._timing = page._timing;
238
- return envelope;
239
- }
240
160
  export class ApiClient {
241
161
  /**
242
162
  * Loaded on first use (`ensureAuthenticated`), never in a constructor.
@@ -293,8 +213,9 @@ export class ApiClient {
293
213
  // error or a server-side rejection. Callers that need a finer
294
214
  // distinction (e.g. `primitive token`) consume RefreshError directly
295
215
  // from refresh-admin-credentials.ts instead of going through here.
296
- // A revoked session (#3885) says so.
297
- throw new ApiError(err.code === "SESSION_REVOKED"
216
+ // A refusal naming the session says why: revoked (#3885), or a login
217
+ // made before scoped sessions (#3891).
218
+ throw new ApiError(err.code === "SESSION_REVOKED" || err.code === "SESSION_UNSCOPED"
298
219
  ? err.message
299
220
  : "Token refresh failed. Please login again.", 401, err.code);
300
221
  }
@@ -383,24 +304,9 @@ export class ApiClient {
383
304
  if (response.status === 409 && parsed.code === "CONFLICT") {
384
305
  return new ConflictError(parsed.message, serverModifiedAt, expectedModifiedAt, detailsRecord);
385
306
  }
386
- if (response.status === 409 && parsed.code === "OPS_EXIST") {
387
- return new OpsExistError(parsed.message, detailsRecord);
388
- }
389
307
  if (response.status === 400 && parsed.code === "TOML_PARSE_ERROR") {
390
308
  return new TomlParseError(parsed.message, detailsRecord);
391
309
  }
392
- if (response.status === 422 && parsed.code === "SCHEMA_REQUIRED") {
393
- return new SchemaRequiredError(parsed.message, detailsRecord);
394
- }
395
- if (response.status === 422 && parsed.code === "OPERATION_REFERENCES_UNDEFINED") {
396
- return new OperationRefError(parsed.message, detailsRecord);
397
- }
398
- if (response.status === 422 && parsed.code === "SCHEMA_BREAKS_OPERATIONS") {
399
- return new SchemaBreaksOpsError(parsed.message, detailsRecord);
400
- }
401
- if (response.status === 422 && parsed.code === "SCHEMA_HAS_UNCHECKABLE_OPS") {
402
- return new SchemaHasUncheckableOpsError(parsed.message, detailsRecord);
403
- }
404
310
  return new ApiError(parsed.message, response.status, parsed.code, parsed.details);
405
311
  }
406
312
  /**
@@ -1140,10 +1046,9 @@ export class ApiClient {
1140
1046
  // ============================================
1141
1047
  /**
1142
1048
  * List the live connections the server believes exist for exactly one of a
1143
- * document, user, or database. Read-only inspection over admin-api. Rows are
1049
+ * document or user. Read-only inspection over admin-api. Rows are
1144
1050
  * per-connection-×-subscription bindings, so `connectionId` is not unique
1145
- * across rows. `params` on a database-subscription row is caller-defined
1146
- * bound CEL input — typed `unknown`.
1051
+ * across rows.
1147
1052
  */
1148
1053
  async listConnections(appId, selector, params) {
1149
1054
  const result = await this.get(`/admin/api/apps/${appId}/connections`, {
@@ -1529,6 +1434,13 @@ export class ApiClient {
1529
1434
  if (payload.expectedJsonSubset) {
1530
1435
  serverPayload.expectedJsonSubset = payload.expectedJsonSubset;
1531
1436
  }
1437
+ // #4262 — an agent case's `[test.agent]` keys, each when the caller set it:
1438
+ // presence, not truthiness, so `agentExpectedNoToolCall: false` travels. A
1439
+ // chat case sets none, and its body is unchanged.
1440
+ for (const key of AGENT_TEST_CASE_WIRE_KEYS) {
1441
+ if (payload[key] !== undefined)
1442
+ serverPayload[key] = payload[key];
1443
+ }
1532
1444
  return this.post(`/admin/api/apps/${appId}/blocks/${blockType}/${blockId}/test-cases`, serverPayload);
1533
1445
  }
1534
1446
  async updateTestCase(appId, blockType, blockId, testCaseId, payload) {
@@ -1563,6 +1475,11 @@ export class ApiClient {
1563
1475
  }
1564
1476
  if (payload.key !== undefined)
1565
1477
  serverPayload.key = payload.key;
1478
+ // #4262 — as on create; an explicit `null` clears the field.
1479
+ for (const key of AGENT_TEST_CASE_WIRE_KEYS) {
1480
+ if (payload[key] !== undefined)
1481
+ serverPayload[key] = payload[key];
1482
+ }
1566
1483
  return this.patch(`/admin/api/apps/${appId}/blocks/${blockType}/${blockId}/test-cases/${testCaseId}`, serverPayload);
1567
1484
  }
1568
1485
  async deleteTestCase(appId, blockType, blockId, testCaseId) {
@@ -2126,72 +2043,6 @@ export class ApiClient {
2126
2043
  });
2127
2044
  }
2128
2045
  // ============================================
2129
- // DATABASE OPERATIONS (registered operations)
2130
- // ============================================
2131
- async listDatabaseOperations(appId, databaseId) {
2132
- return this.get(`/app/${appId}/api/databases/${databaseId}/operations`);
2133
- }
2134
- /**
2135
- * Execute a registered operation and, when the result is a query page,
2136
- * normalize it to the CLI's one list envelope (issue #2440).
2137
- *
2138
- * The registered operation decides the response shape, so the normalization
2139
- * is conditional — see {@link normalizeDatabaseOperationResult}. Everything
2140
- * else (a `count`, an `aggregate`, a mutation, a `returnField: "all"`
2141
- * pipeline) is returned exactly as the server sent it.
2142
- */
2143
- async executeDatabaseOperation(appId, databaseId, operationName, data, token, options) {
2144
- const path = `/app/${appId}/api/databases/${databaseId}/operations/${encodeURIComponent(operationName)}/execute`;
2145
- const extraHeaders = {};
2146
- if (options?.timing) {
2147
- extraHeaders["X-Timing"] = "true";
2148
- }
2149
- if (token) {
2150
- return normalizeDatabaseOperationResult(await this.requestWithToken(path, token, {
2151
- method: "POST",
2152
- body: JSON.stringify(data || {}),
2153
- headers: extraHeaders,
2154
- }));
2155
- }
2156
- return normalizeDatabaseOperationResult(await this.request(path, {
2157
- method: "POST",
2158
- body: JSON.stringify(data || {}),
2159
- headers: extraHeaders,
2160
- }));
2161
- }
2162
- /**
2163
- * Execute a registered batch (bulk) database operation. Posts a chunk of
2164
- * items to the canonical `operations/:name/batch` endpoint (the same one the
2165
- * client library's `executeBatch` uses — NOT the deprecated `import-bulk`
2166
- * alias). Used by `databases import-csv`.
2167
- *
2168
- * @returns `{ imported, failed }` — DO-level write outcome counts. Per-item
2169
- * validation/access failures abort the whole chunk with a 4xx (thrown).
2170
- */
2171
- async executeBatch(appId, databaseId, operationName, batch) {
2172
- return this.post(`/app/${appId}/api/databases/${databaseId}/operations/${encodeURIComponent(operationName)}/batch`, { batch });
2173
- }
2174
- /**
2175
- * Make a request using a specific JWT token instead of the CLI's credentials.
2176
- * Used for executing operations as a different user.
2177
- */
2178
- async requestWithToken(path, token, options = {}) {
2179
- const credentials = await this.ensureAuthenticated();
2180
- const url = `${credentials.serverUrl}${path}`;
2181
- const headers = {
2182
- Authorization: `Bearer ${token}`,
2183
- "Content-Type": "application/json",
2184
- ...(options.headers || {}),
2185
- };
2186
- const response = await fetchWithTLS(url, { ...options, headers });
2187
- const text = await response.text();
2188
- if (!response.ok) {
2189
- const parsed = parseErrorResponse(response, text, path);
2190
- throw new ApiError(parsed.message, response.status, parsed.code, parsed.details);
2191
- }
2192
- return text ? JSON.parse(text) : null;
2193
- }
2194
- // ============================================
2195
2046
  // DATABASE INDEXES
2196
2047
  // ============================================
2197
2048
  async listDatabaseIndexes(appId, databaseId, modelName) {
@@ -2224,7 +2075,7 @@ export class ApiClient {
2224
2075
  // Issue #1336 (codex round-1 P2): `dryRun` runs every create-time
2225
2076
  // validation (inline CEL rule lint, manifest shape, schema TOML parse)
2226
2077
  // WITHOUT persisting, so a fresh-type `config push --dry-run` surfaces a
2227
- // malformed `defaultAccess`/manifest the same way the real POST would.
2078
+ // malformed rule or manifest the same way the real POST would.
2228
2079
  options) {
2229
2080
  const query = options?.dryRun ? "?dryRun=true" : "";
2230
2081
  return this.post(`/app/${appId}/api/databases/types${query}`, data);
@@ -2245,105 +2096,14 @@ export class ApiClient {
2245
2096
  return this.delete(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}${query}`);
2246
2097
  }
2247
2098
  /**
2248
- * Issue #666 Phase 3: ask the server to scaffold a TOML schema from
2249
- * existing ops + DO field introspection. Read-only — does NOT persist.
2099
+ * Issue #666 Phase 3: ask the server to scaffold a TOML schema from the
2100
+ * type's databases (the models each holds, and their fields). Read-only —
2101
+ * does NOT persist.
2250
2102
  */
2251
2103
  async scaffoldDatabaseTypeSchema(appId, databaseType) {
2252
2104
  return this.post(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/schema:scaffold`, {});
2253
2105
  }
2254
2106
  // ============================================
2255
- // DATABASE TYPE OPERATIONS
2256
- // ============================================
2257
- async listDatabaseTypeOperations(appId, databaseType) {
2258
- return this.get(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations`);
2259
- }
2260
- async getDatabaseTypeOperation(appId, databaseType, name) {
2261
- return this.get(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations/${encodeURIComponent(name)}`);
2262
- }
2263
- async createDatabaseTypeOperation(appId, databaseType, data,
2264
- // Issue #813 (1A): when `dryRun` is set the server runs the op-edit gate
2265
- // without persisting — used by `config push --dry-run` to surface gate
2266
- // failures the real push would hit. `schemaOverride` lets the gate run
2267
- // against the schema the SAME push is about to land (so an op that depends
2268
- // on a new field isn't falsely rejected against the stale stored schema).
2269
- //
2270
- // Issue #915 (defect a, follow-up): for a FRESH db-type the parent config
2271
- // does not exist on the server yet, so the server's "access required"
2272
- // check has no `defaultAccess` to fall back on. Thread the TOML-declared
2273
- // type-level `defaultAccess` so an op that omits `access` (intending to
2274
- // inherit it after the type is created) isn't falsely rejected during the
2275
- // dry-run. Server only trusts this on the fresh-type dry-run path.
2276
- options) {
2277
- const query = options?.dryRun ? "?dryRun=true" : "";
2278
- let body = data;
2279
- if (options?.dryRun && options.schemaOverride !== undefined) {
2280
- body = { ...body, schemaOverride: options.schemaOverride };
2281
- }
2282
- if (options?.dryRun && options.defaultAccess !== undefined) {
2283
- body = { ...body, defaultAccess: options.defaultAccess };
2284
- }
2285
- if (options?.dryRun && options.metadataManifestOverride !== undefined) {
2286
- body = { ...body, metadataManifestOverride: options.metadataManifestOverride };
2287
- }
2288
- return this.post(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations${query}`, body);
2289
- }
2290
- async updateDatabaseTypeOperation(appId, databaseType, name, data, expectedModifiedAt,
2291
- // Issue #813 (1A): dry-run runs the op-edit gate without persisting.
2292
- // `schemaOverride` gates against the schema the same push is landing.
2293
- // Issue #1336: `metadataManifestOverride` gates the op-access lint against
2294
- // the manifest the same push is landing (see `createDatabaseTypeOperation`).
2295
- options) {
2296
- let body = expectedModifiedAt ? { ...data, expectedModifiedAt } : { ...data };
2297
- if (options?.dryRun && options.schemaOverride !== undefined) {
2298
- body = { ...body, schemaOverride: options.schemaOverride };
2299
- }
2300
- if (options?.dryRun && options.metadataManifestOverride !== undefined) {
2301
- body = { ...body, metadataManifestOverride: options.metadataManifestOverride };
2302
- }
2303
- const query = options?.dryRun ? "?dryRun=true" : "";
2304
- return this.patch(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations/${encodeURIComponent(name)}${query}`, body);
2305
- }
2306
- async deleteDatabaseTypeOperation(appId, databaseType, name) {
2307
- return this.delete(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations/${encodeURIComponent(name)}`);
2308
- }
2309
- // ============================================
2310
- // DATABASE TYPE SUBSCRIPTIONS (#740 / #803)
2311
- // ============================================
2312
- //
2313
- // Transparent passthroughs against the server's type-scoped subscription
2314
- // routes (GET/POST/PUT/DELETE `/databases/types/:type/subscriptions[/:key]`).
2315
- // The wire format is authoritative in the controller
2316
- // (`database-type-subscriptions-controller.ts`): create requires
2317
- // `subscriptionKey`, `displayName`, `modelName`, `filter` (CEL), `access`
2318
- // (CEL); optional `description`, `select` (string[]), `emit`
2319
- // (enter/update/leave subset), `params` (object, ≤5 entries), `status`.
2320
- //
2321
- // The list endpoint wraps results in `{ items: [...] }` (unlike operations,
2322
- // which returns a bare array). We unwrap here so callers get an array,
2323
- // matching `listDatabaseTypeOperations`.
2324
- async listDatabaseTypeSubscriptions(appId, databaseType) {
2325
- const result = await this.get(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions`);
2326
- if (Array.isArray(result))
2327
- return result;
2328
- return Array.isArray(result?.items) ? result.items : [];
2329
- }
2330
- async getDatabaseTypeSubscription(appId, databaseType, subscriptionKey) {
2331
- return this.get(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions/${encodeURIComponent(subscriptionKey)}`);
2332
- }
2333
- async createDatabaseTypeSubscription(appId, databaseType, data) {
2334
- return this.post(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions`, data);
2335
- }
2336
- async updateDatabaseTypeSubscription(appId, databaseType, subscriptionKey, data,
2337
- // #2731 B10 — optimistic concurrency, as the sibling operation update has
2338
- // had. Omitted (by `--force`, and by every caller predating it) means the
2339
- // write stays unconditional.
2340
- expectedModifiedAt) {
2341
- return this.put(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions/${encodeURIComponent(subscriptionKey)}`, expectedModifiedAt ? { ...data, expectedModifiedAt } : data);
2342
- }
2343
- async deleteDatabaseTypeSubscription(appId, databaseType, subscriptionKey) {
2344
- return this.delete(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions/${encodeURIComponent(subscriptionKey)}`);
2345
- }
2346
- // ============================================
2347
2107
  // GROUPS
2348
2108
  // ============================================
2349
2109
  async listGroups(appId, params) {
@@ -2661,51 +2421,6 @@ export class ApiClient {
2661
2421
  async importDocumentState(appId, documentId, stateBase64) {
2662
2422
  return this.post(`/app/${appId}/api/documents/${documentId}/import/state`, { state: stateBase64 });
2663
2423
  }
2664
- /**
2665
- * Upload one artifact of a large document's import chain (#2816,
2666
- * behavior 20).
2667
- *
2668
- * The bytes travel raw and unmodified: the manifest's digest is over the
2669
- * STORED bytes, so re-encoding a chunk here would fail its own verification
2670
- * on install. The query says which artifact it is — the server derives the
2671
- * key from the document it belongs to, so nothing here composes one.
2672
- */
2673
- async uploadDocumentChainArtifact(appId, documentId, query, body) {
2674
- const credentials = await this.ensureAuthenticated();
2675
- const search = new URLSearchParams();
2676
- for (const [key, value] of Object.entries(query)) {
2677
- search.set(key, String(value));
2678
- }
2679
- const url = `${credentials.serverUrl}/app/${appId}/api/documents/${documentId}` +
2680
- `/import/chain/artifact?${search.toString()}`;
2681
- const headers = {
2682
- Authorization: `Bearer ${credentials.accessToken}`,
2683
- "Content-Type": "application/octet-stream",
2684
- };
2685
- if (credentials.globalAdminAppId) {
2686
- headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
2687
- }
2688
- const response = await fetchWithTLS(url, {
2689
- method: "POST",
2690
- headers,
2691
- body: body,
2692
- });
2693
- if (!response.ok) {
2694
- // #3403 — through the shared parser: reading `error` alone dropped the
2695
- // envelope's `code` and `details`, so the refusal reached the operator
2696
- // (and `primitive documents import`) with nothing to branch on.
2697
- throw await blobFailure(response, "Failed to upload import artifact: ");
2698
- }
2699
- return await response.json().catch(() => ({}));
2700
- }
2701
- /** Start installing an uploaded chain (#2816, behavior 20). */
2702
- async installDocumentChain(appId, documentId, plan) {
2703
- return this.post(`/app/${appId}/api/documents/${documentId}/import/chain`, plan);
2704
- }
2705
- /** How far a document's chain install has got (#2816, behavior 20). */
2706
- async getDocumentChainInstall(appId, documentId) {
2707
- return this.get(`/app/${appId}/api/documents/${documentId}/import/chain`);
2708
- }
2709
2424
  async getDocument(appId, documentId) {
2710
2425
  return this.get(`/app/${appId}/api/documents/${documentId}`);
2711
2426
  }
@@ -2796,6 +2511,23 @@ export class ApiClient {
2796
2511
  async getDocumentStats(appId, documentId) {
2797
2512
  return this.get(`/app/${appId}/api/documents/${documentId}/stats`);
2798
2513
  }
2514
+ /**
2515
+ * A record-log document's room (#4306): `GET documents/:id/room` →
2516
+ * `{ ok, appId, documentId, documentFormat, room }`, `room` null for a
2517
+ * format that has no record room. With `refused` (#4310), the answer also
2518
+ * carries a page of that bulk load's refused rows. App admins only.
2519
+ */
2520
+ async getDocumentRoom(appId, documentId, query = {}) {
2521
+ const params = new URLSearchParams();
2522
+ if (query.refused !== undefined)
2523
+ params.set("refused", query.refused);
2524
+ if (query.limit !== undefined)
2525
+ params.set("limit", String(query.limit));
2526
+ if (query.cursor !== undefined)
2527
+ params.set("cursor", query.cursor);
2528
+ const search = params.toString();
2529
+ return this.get(`/app/${appId}/api/documents/${documentId}/room${search ? `?${search}` : ""}`);
2530
+ }
2799
2531
  /**
2800
2532
  * A large document's snapshot builds, newest first, one page (#3432).
2801
2533
  * `GET documents/:id/snapshots` → `{ items, hasMore, nextCursor? }`.
@@ -2829,9 +2561,13 @@ export class ApiClient {
2829
2561
  async getDocumentIngest(appId, documentId, sessionId) {
2830
2562
  return this.get(`/app/${appId}/api/documents/${documentId}/ingest/${encodeURIComponent(sessionId)}`);
2831
2563
  }
2832
- /** Open a bulk-load session on a large document (#3435, criterion 9). */
2833
- async createDocumentIngest(appId, documentId) {
2834
- return this.post(`/app/${appId}/api/documents/${documentId}/ingest`, {});
2564
+ /**
2565
+ * Open a bulk-load session on a large document (#3435, criterion 9). A
2566
+ * record-log (format 3) document's load takes `reserveEpoch`, `mode` and a
2567
+ * refresh's `models` (#4310); `{}` keeps every format's defaults.
2568
+ */
2569
+ async createDocumentIngest(appId, documentId, options = {}) {
2570
+ return this.post(`/app/${appId}/api/documents/${documentId}/ingest`, options);
2835
2571
  }
2836
2572
  /**
2837
2573
  * Upload one chunk of a bulk-load session's artifact (#3435).