primitive-admin 1.2.0-alpha.7 → 1.2.0-alpha.9
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.
- package/README.md +8 -2
- package/assets/skill/skills/primitive-platform/SKILL.md +22 -8
- package/dist/src/commands/apps-children.d.ts +1 -1
- package/dist/src/commands/apps-children.js +55 -145
- package/dist/src/commands/apps-children.js.map +1 -1
- package/dist/src/commands/auth.js +107 -33
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/database-types.js +13 -313
- package/dist/src/commands/database-types.js.map +1 -1
- package/dist/src/commands/databases.js +13 -1
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.js +70 -11
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/llm.js +9 -6
- package/dist/src/commands/llm.js.map +1 -1
- package/dist/src/commands/settings.d.ts +12 -9
- package/dist/src/commands/settings.js +31 -79
- package/dist/src/commands/settings.js.map +1 -1
- package/dist/src/commands/sync-app-settings.d.ts +12 -2
- package/dist/src/commands/sync-app-settings.js +35 -10
- package/dist/src/commands/sync-app-settings.js.map +1 -1
- package/dist/src/commands/sync.d.ts +1 -8
- package/dist/src/commands/sync.js +47 -45
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +46 -6
- package/dist/src/lib/api-client.js +156 -64
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +38 -0
- package/dist/src/lib/app-settings-descriptor.js +96 -4
- package/dist/src/lib/app-settings-descriptor.js.map +1 -1
- package/dist/src/lib/child-apps-local.d.ts +0 -46
- package/dist/src/lib/child-apps-local.js +2 -72
- package/dist/src/lib/child-apps-local.js.map +1 -1
- package/dist/src/lib/credentials-home.d.ts +51 -0
- package/dist/src/lib/credentials-home.js +169 -0
- package/dist/src/lib/credentials-home.js.map +1 -0
- package/dist/src/lib/credentials-lock.d.ts +80 -0
- package/dist/src/lib/credentials-lock.js +222 -0
- package/dist/src/lib/credentials-lock.js.map +1 -0
- package/dist/src/lib/credentials-store.d.ts +56 -4
- package/dist/src/lib/credentials-store.js +236 -78
- package/dist/src/lib/credentials-store.js.map +1 -1
- package/dist/src/lib/env-resolver-core.d.ts +1 -1
- package/dist/src/lib/env-resolver-core.js.map +1 -1
- package/dist/src/lib/function-bundle.d.ts +38 -18
- package/dist/src/lib/function-bundle.js +60 -35
- package/dist/src/lib/function-bundle.js.map +1 -1
- package/dist/src/lib/function-document-types.d.ts +5 -3
- package/dist/src/lib/function-document-types.js +10 -14
- package/dist/src/lib/function-document-types.js.map +1 -1
- package/dist/src/lib/function-source-roots.d.ts +67 -0
- package/dist/src/lib/function-source-roots.js +148 -0
- package/dist/src/lib/function-source-roots.js.map +1 -0
- package/dist/src/lib/function-sync.d.ts +27 -0
- package/dist/src/lib/function-sync.js +16 -3
- package/dist/src/lib/function-sync.js.map +1 -1
- package/dist/src/lib/function-triggers.d.ts +15 -3
- package/dist/src/lib/function-triggers.js +72 -10
- package/dist/src/lib/function-triggers.js.map +1 -1
- package/dist/src/lib/function-typecheck.d.ts +37 -2
- package/dist/src/lib/function-typecheck.js +210 -28
- package/dist/src/lib/function-typecheck.js.map +1 -1
- package/dist/src/lib/generated-config-surfaces.d.ts +48 -11
- package/dist/src/lib/generated-config-surfaces.js +178 -49
- package/dist/src/lib/generated-config-surfaces.js.map +1 -1
- package/dist/src/lib/generated-sdk-types.d.ts +1 -1
- package/dist/src/lib/generated-sdk-types.js +1 -1
- package/dist/src/lib/generated-sdk-types.js.map +1 -1
- package/dist/src/lib/generated-workflow-model-fields.d.ts +20 -0
- package/dist/src/lib/generated-workflow-model-fields.js +53 -0
- package/dist/src/lib/generated-workflow-model-fields.js.map +1 -0
- package/dist/src/lib/login-scope-lines.d.ts +1 -1
- package/dist/src/lib/login-scope-lines.js +1 -1
- package/dist/src/lib/login-scope-lines.js.map +1 -1
- package/dist/src/lib/project-config.d.ts +7 -0
- package/dist/src/lib/project-config.js +22 -1
- package/dist/src/lib/project-config.js.map +1 -1
- package/dist/src/lib/prompt-schema-codegen.d.ts +10 -3
- package/dist/src/lib/prompt-schema-codegen.js +52 -14
- package/dist/src/lib/prompt-schema-codegen.js.map +1 -1
- package/dist/src/lib/root-import-decision.d.ts +9 -4
- package/dist/src/lib/root-import-decision.js +24 -3
- package/dist/src/lib/root-import-decision.js.map +1 -1
- package/dist/src/lib/scope-request.d.ts +8 -5
- package/dist/src/lib/scope-request.js +17 -8
- package/dist/src/lib/scope-request.js.map +1 -1
- package/dist/src/lib/swift-codegen/agentGenerator.d.ts +6 -2
- package/dist/src/lib/swift-codegen/agentGenerator.js +56 -4
- package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -1
- package/dist/src/lib/workflow-field-descriptor.d.ts +91 -0
- package/dist/src/lib/workflow-field-descriptor.js +153 -0
- package/dist/src/lib/workflow-field-descriptor.js.map +1 -0
- package/package.json +2 -2
|
@@ -51,6 +51,8 @@ export interface AppChildSummary {
|
|
|
51
51
|
expiresAt: string | null;
|
|
52
52
|
/** Exact origins the child's CORS and email-redirect checks accept beside its authored lists. */
|
|
53
53
|
previewOrigins?: string[];
|
|
54
|
+
/** The child's protection level: always `internal` (#4564); absent from an older server. */
|
|
55
|
+
protection?: string;
|
|
54
56
|
}
|
|
55
57
|
/** A child's preview origins after a write to them. */
|
|
56
58
|
export interface ChildPreviewOrigins {
|
|
@@ -499,9 +501,18 @@ export declare class ApiClient {
|
|
|
499
501
|
request<T = any>(path: string, options?: RequestInit): Promise<T>;
|
|
500
502
|
/**
|
|
501
503
|
* Whether a 403 should be retried with the stored step-up: it names
|
|
502
|
-
* `admin` among the missing scopes,
|
|
504
|
+
* `admin` among the missing scopes, or it is a protected app's refusal of
|
|
505
|
+
* a token that is no step-up, and an unexpired step-up is stored.
|
|
503
506
|
*/
|
|
504
507
|
private stepUpApplies;
|
|
508
|
+
/**
|
|
509
|
+
* #4564 — a request outside `request` (an upload, a function invocation)
|
|
510
|
+
* sent with the CLI's own credentials, retried once with the stored step-up
|
|
511
|
+
* exactly as `request` retries. `init` builds the request for a bearer. The
|
|
512
|
+
* answer is the retry's, unless the retry was refused 401: the step-up is
|
|
513
|
+
* dropped and the original refusal comes back, still readable.
|
|
514
|
+
*/
|
|
515
|
+
private fetchWithStepUp;
|
|
505
516
|
/** Forget a step-up the server no longer accepts. */
|
|
506
517
|
private dropStepUp;
|
|
507
518
|
/** The typed error a failed response throws. */
|
|
@@ -2104,10 +2115,14 @@ export declare class ApiClient {
|
|
|
2104
2115
|
* through the caller's document permission (reader+) or the app-admin arm.
|
|
2105
2116
|
* Returns the unified `{ items, hasMore, nextCursor? }` envelope. The cursor
|
|
2106
2117
|
* is an opaque token — feed `nextCursor` back as `cursor` for the next page.
|
|
2118
|
+
* `limit` is 100 records when omitted and at most 10000; it is forwarded
|
|
2119
|
+
* unparsed (a string from the command line) so the server validates it.
|
|
2107
2120
|
*/
|
|
2108
2121
|
queryDocumentRecords(appId: string, documentId: string, modelName: string, queryOptions?: {
|
|
2109
2122
|
filter?: Record<string, any>;
|
|
2110
|
-
|
|
2123
|
+
/** `{ field: 1 | -1 }`, key order as precedence; the route validates it. */
|
|
2124
|
+
sort?: unknown;
|
|
2125
|
+
limit?: number | string;
|
|
2111
2126
|
cursor?: string;
|
|
2112
2127
|
/** #3764 — the format the caller believes this document has. */
|
|
2113
2128
|
documentFormat?: 1 | 3;
|
|
@@ -2127,16 +2142,20 @@ export declare class ApiClient {
|
|
|
2127
2142
|
* with `{ options: { operations, groupBy?, filter? } }`, answering `{ result }`
|
|
2128
2143
|
* in the same shape as the database endpoint.
|
|
2129
2144
|
*
|
|
2130
|
-
* `groupBy` takes plain field names
|
|
2131
|
-
*
|
|
2132
|
-
*
|
|
2145
|
+
* `groupBy` takes plain field names and period levels `{ field, period }`
|
|
2146
|
+
* (keyed `<field>_<period>` in a row's `group`): StringSet facet grouping
|
|
2147
|
+
* and `{ field, contains }` membership grouping are database-only, which is
|
|
2148
|
+
* why this signature is narrower than the databases one.
|
|
2133
2149
|
*/
|
|
2134
2150
|
aggregateDocumentRecords(appId: string, documentId: string, modelName: string, options: {
|
|
2135
2151
|
operations: {
|
|
2136
2152
|
type: string;
|
|
2137
2153
|
field?: string;
|
|
2138
2154
|
}[];
|
|
2139
|
-
groupBy?: string
|
|
2155
|
+
groupBy?: Array<string | {
|
|
2156
|
+
field: string;
|
|
2157
|
+
period: "month" | "quarter" | "year";
|
|
2158
|
+
}>;
|
|
2140
2159
|
filter?: Record<string, any>;
|
|
2141
2160
|
}, extra?: {
|
|
2142
2161
|
documentFormat?: 1 | 3;
|
|
@@ -2354,6 +2373,27 @@ export declare class ApiClient {
|
|
|
2354
2373
|
contentType: string;
|
|
2355
2374
|
sha256: string;
|
|
2356
2375
|
}): Promise<any>;
|
|
2376
|
+
/**
|
|
2377
|
+
* Upload a file to an agent session by `sessionId` (#4585): the document
|
|
2378
|
+
* blob upload's headers and body, on the session's attachment route.
|
|
2379
|
+
*/
|
|
2380
|
+
uploadAgentSessionAttachment(appId: string, sessionId: string, blobId: string, data: Buffer, meta: {
|
|
2381
|
+
filename: string;
|
|
2382
|
+
contentType: string;
|
|
2383
|
+
sha256: string;
|
|
2384
|
+
}): Promise<any>;
|
|
2385
|
+
/** Download an agent session's file by `sessionId` (#4585). */
|
|
2386
|
+
downloadAgentSessionAttachment(appId: string, sessionId: string, blobId: string): Promise<Buffer>;
|
|
2387
|
+
/**
|
|
2388
|
+
* An agent session file's download headers (lower-cased names) by
|
|
2389
|
+
* `sessionId` (#4585). A refusal has no body, so it fails with the status
|
|
2390
|
+
* alone and no code.
|
|
2391
|
+
*/
|
|
2392
|
+
headAgentSessionAttachment(appId: string, sessionId: string, blobId: string): Promise<{
|
|
2393
|
+
status: number;
|
|
2394
|
+
headers: Record<string, string>;
|
|
2395
|
+
}>;
|
|
2396
|
+
private agentSessionAttachmentRead;
|
|
2357
2397
|
listDocumentAliases(appId: string, documentId: string): Promise<any[]>;
|
|
2358
2398
|
/**
|
|
2359
2399
|
* The document a user's alias points at, or null when the user holds no
|
|
@@ -243,9 +243,10 @@ export class ApiClient {
|
|
|
243
243
|
};
|
|
244
244
|
let { response, text } = await send(credentials.accessToken);
|
|
245
245
|
// #3889 — a 403 naming `admin` is retried once with a stored, unexpired
|
|
246
|
-
// step-up
|
|
247
|
-
//
|
|
248
|
-
//
|
|
246
|
+
// step-up, and (#4564) so is a write a protected app refused for want of
|
|
247
|
+
// one. A retry refused 401 means the step-up is gone (revoked or expired
|
|
248
|
+
// early): drop it and show the original refusal. The retry's own refusal
|
|
249
|
+
// (say, a step-up without the scope this command needs) is shown as it is.
|
|
249
250
|
if (response.status === 403 && this.stepUpApplies(response, text, path)) {
|
|
250
251
|
const retried = await send(credentials.stepUp.accessToken);
|
|
251
252
|
if (retried.response.ok) {
|
|
@@ -265,7 +266,8 @@ export class ApiClient {
|
|
|
265
266
|
}
|
|
266
267
|
/**
|
|
267
268
|
* Whether a 403 should be retried with the stored step-up: it names
|
|
268
|
-
* `admin` among the missing scopes,
|
|
269
|
+
* `admin` among the missing scopes, or it is a protected app's refusal of
|
|
270
|
+
* a token that is no step-up, and an unexpired step-up is stored.
|
|
269
271
|
*/
|
|
270
272
|
stepUpApplies(response, text, path) {
|
|
271
273
|
const stepUp = this.credentials?.stepUp;
|
|
@@ -273,10 +275,41 @@ export class ApiClient {
|
|
|
273
275
|
return false;
|
|
274
276
|
if (new Date(stepUp.expiresAt).getTime() <= Date.now())
|
|
275
277
|
return false;
|
|
276
|
-
const
|
|
278
|
+
const parsed = parseErrorResponse(response, text, path);
|
|
279
|
+
if (parsed.code === "PROTECTED_APP_STEP_UP_REQUIRED")
|
|
280
|
+
return true;
|
|
281
|
+
const details = parsed.details;
|
|
277
282
|
const missing = details && !Array.isArray(details) ? details.missing : undefined;
|
|
278
283
|
return Array.isArray(missing) && missing.includes("admin");
|
|
279
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* #4564 — a request outside `request` (an upload, a function invocation)
|
|
287
|
+
* sent with the CLI's own credentials, retried once with the stored step-up
|
|
288
|
+
* exactly as `request` retries. `init` builds the request for a bearer. The
|
|
289
|
+
* answer is the retry's, unless the retry was refused 401: the step-up is
|
|
290
|
+
* dropped and the original refusal comes back, still readable.
|
|
291
|
+
*/
|
|
292
|
+
async fetchWithStepUp(url, path, accessToken, init) {
|
|
293
|
+
const response = await fetchWithTLS(url, init(accessToken));
|
|
294
|
+
if (response.status !== 403)
|
|
295
|
+
return response;
|
|
296
|
+
// Read once to decide; hand the caller a response it can still read.
|
|
297
|
+
const text = await response.text();
|
|
298
|
+
const original = new Response(text, {
|
|
299
|
+
status: response.status,
|
|
300
|
+
statusText: response.statusText,
|
|
301
|
+
headers: response.headers,
|
|
302
|
+
});
|
|
303
|
+
if (!this.stepUpApplies(response, text, path))
|
|
304
|
+
return original;
|
|
305
|
+
const retried = await fetchWithTLS(url, init(this.credentials.stepUp.accessToken));
|
|
306
|
+
if (retried.status === 401) {
|
|
307
|
+
await retried.text().catch(() => "");
|
|
308
|
+
this.dropStepUp();
|
|
309
|
+
return original;
|
|
310
|
+
}
|
|
311
|
+
return retried;
|
|
312
|
+
}
|
|
280
313
|
/** Forget a step-up the server no longer accepts. */
|
|
281
314
|
dropStepUp() {
|
|
282
315
|
if (!this.credentials?.stepUp)
|
|
@@ -944,21 +977,25 @@ export class ApiClient {
|
|
|
944
977
|
: options?.runtime === "task"
|
|
945
978
|
? `${base}/start`
|
|
946
979
|
: base;
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
980
|
+
const init = (bearer) => {
|
|
981
|
+
const headers = {
|
|
982
|
+
Authorization: `Bearer ${bearer}`,
|
|
983
|
+
"Content-Type": "application/json",
|
|
984
|
+
};
|
|
985
|
+
// The CLI's own credentials reach an app-scoped route through the
|
|
986
|
+
// global-admin header; a minted user token does not need it and must
|
|
987
|
+
// not carry it.
|
|
988
|
+
if (!options?.bearer && credentials.globalAdminAppId) {
|
|
989
|
+
headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
|
|
990
|
+
}
|
|
991
|
+
return { method: "POST", headers, body: JSON.stringify(body) };
|
|
950
992
|
};
|
|
951
|
-
|
|
952
|
-
//
|
|
953
|
-
//
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const response = await fetchWithTLS(`${credentials.serverUrl}${path}`, {
|
|
958
|
-
method: "POST",
|
|
959
|
-
headers,
|
|
960
|
-
body: JSON.stringify(body),
|
|
961
|
-
});
|
|
993
|
+
const url = `${credentials.serverUrl}${path}`;
|
|
994
|
+
// A minted user token is sent as it is; the CLI's own credentials get the
|
|
995
|
+
// step-up retry a protected app's refusal asks for (#4564).
|
|
996
|
+
const response = options?.bearer
|
|
997
|
+
? await fetchWithTLS(url, init(options.bearer))
|
|
998
|
+
: await this.fetchWithStepUp(url, path, credentials.accessToken, init);
|
|
962
999
|
const text = await response.text();
|
|
963
1000
|
let parsed = null;
|
|
964
1001
|
try {
|
|
@@ -1495,17 +1532,15 @@ export class ApiClient {
|
|
|
1495
1532
|
const credentials = await this.ensureAuthenticated();
|
|
1496
1533
|
const encodedFilename = encodeURIComponent(filename);
|
|
1497
1534
|
const url = `${credentials.serverUrl}/admin/api/apps/${appId}/blocks/${blockType}/${blockId}/test-cases/${testCaseId}/attachments/${encodedFilename}`;
|
|
1498
|
-
const
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
method: "PUT",
|
|
1507
|
-
headers,
|
|
1508
|
-
body: data,
|
|
1535
|
+
const response = await this.fetchWithStepUp(url, url, credentials.accessToken, (bearer) => {
|
|
1536
|
+
const headers = {
|
|
1537
|
+
Authorization: `Bearer ${bearer}`,
|
|
1538
|
+
"Content-Type": contentType,
|
|
1539
|
+
};
|
|
1540
|
+
if (credentials.globalAdminAppId) {
|
|
1541
|
+
headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
|
|
1542
|
+
}
|
|
1543
|
+
return { method: "PUT", headers, body: data };
|
|
1509
1544
|
});
|
|
1510
1545
|
const text = await response.text();
|
|
1511
1546
|
if (!response.ok) {
|
|
@@ -2460,11 +2495,15 @@ export class ApiClient {
|
|
|
2460
2495
|
* through the caller's document permission (reader+) or the app-admin arm.
|
|
2461
2496
|
* Returns the unified `{ items, hasMore, nextCursor? }` envelope. The cursor
|
|
2462
2497
|
* is an opaque token — feed `nextCursor` back as `cursor` for the next page.
|
|
2498
|
+
* `limit` is 100 records when omitted and at most 10000; it is forwarded
|
|
2499
|
+
* unparsed (a string from the command line) so the server validates it.
|
|
2463
2500
|
*/
|
|
2464
2501
|
async queryDocumentRecords(appId, documentId, modelName, queryOptions) {
|
|
2465
2502
|
const query = {};
|
|
2466
2503
|
if (queryOptions?.filter)
|
|
2467
2504
|
query.filter = JSON.stringify(queryOptions.filter);
|
|
2505
|
+
if (queryOptions?.sort !== undefined)
|
|
2506
|
+
query.sort = JSON.stringify(queryOptions.sort);
|
|
2468
2507
|
if (queryOptions?.documentFormat !== undefined) {
|
|
2469
2508
|
query.documentFormat = queryOptions.documentFormat;
|
|
2470
2509
|
}
|
|
@@ -2496,9 +2535,10 @@ export class ApiClient {
|
|
|
2496
2535
|
* with `{ options: { operations, groupBy?, filter? } }`, answering `{ result }`
|
|
2497
2536
|
* in the same shape as the database endpoint.
|
|
2498
2537
|
*
|
|
2499
|
-
* `groupBy` takes plain field names
|
|
2500
|
-
*
|
|
2501
|
-
*
|
|
2538
|
+
* `groupBy` takes plain field names and period levels `{ field, period }`
|
|
2539
|
+
* (keyed `<field>_<period>` in a row's `group`): StringSet facet grouping
|
|
2540
|
+
* and `{ field, contains }` membership grouping are database-only, which is
|
|
2541
|
+
* why this signature is narrower than the databases one.
|
|
2502
2542
|
*/
|
|
2503
2543
|
async aggregateDocumentRecords(appId, documentId, modelName, options, extra) {
|
|
2504
2544
|
return this.post(`/app/${appId}/api/documents/${documentId}/records/${encodeURIComponent(modelName)}/aggregate` +
|
|
@@ -2589,18 +2629,16 @@ export class ApiClient {
|
|
|
2589
2629
|
form.set("firstId", chunk.firstId);
|
|
2590
2630
|
form.set("lastId", chunk.lastId);
|
|
2591
2631
|
form.set("chunk", new Blob([chunk.body]), `${chunk.index}.ndjson.gz`);
|
|
2592
|
-
const headers = {
|
|
2593
|
-
Authorization: `Bearer ${credentials.accessToken}`,
|
|
2594
|
-
};
|
|
2595
|
-
if (credentials.globalAdminAppId) {
|
|
2596
|
-
headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
|
|
2597
|
-
}
|
|
2598
2632
|
const url = `${credentials.serverUrl}/app/${appId}/api/documents/${documentId}` +
|
|
2599
2633
|
`/ingest/${encodeURIComponent(sessionId)}/chunks`;
|
|
2600
|
-
const response = await
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2634
|
+
const response = await this.fetchWithStepUp(url, url, credentials.accessToken, (bearer) => {
|
|
2635
|
+
const headers = {
|
|
2636
|
+
Authorization: `Bearer ${bearer}`,
|
|
2637
|
+
};
|
|
2638
|
+
if (credentials.globalAdminAppId) {
|
|
2639
|
+
headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
|
|
2640
|
+
}
|
|
2641
|
+
return { method: "POST", headers, body: form };
|
|
2604
2642
|
});
|
|
2605
2643
|
const text = await response.text();
|
|
2606
2644
|
let result = {};
|
|
@@ -2758,6 +2796,31 @@ export class ApiClient {
|
|
|
2758
2796
|
async uploadBlob(appId, documentId, blobId, data, meta) {
|
|
2759
2797
|
const credentials = await this.ensureAuthenticated();
|
|
2760
2798
|
const url = `${credentials.serverUrl}/app/${appId}/api/documents/${documentId}/blobs/${blobId}`;
|
|
2799
|
+
const response = await this.fetchWithStepUp(url, url, credentials.accessToken, (bearer) => {
|
|
2800
|
+
const headers = {
|
|
2801
|
+
Authorization: `Bearer ${bearer}`,
|
|
2802
|
+
"Content-Type": meta.contentType,
|
|
2803
|
+
"X-Blob-Filename": encodeURIComponent(meta.filename),
|
|
2804
|
+
"X-Blob-Size": String(data.length),
|
|
2805
|
+
"X-Blob-Sha256": meta.sha256,
|
|
2806
|
+
};
|
|
2807
|
+
if (credentials.globalAdminAppId) {
|
|
2808
|
+
headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
|
|
2809
|
+
}
|
|
2810
|
+
return { method: "PUT", headers, body: data };
|
|
2811
|
+
});
|
|
2812
|
+
if (!response.ok) {
|
|
2813
|
+
throw await blobFailure(response, "Failed to upload blob: ");
|
|
2814
|
+
}
|
|
2815
|
+
return response.json();
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Upload a file to an agent session by `sessionId` (#4585): the document
|
|
2819
|
+
* blob upload's headers and body, on the session's attachment route.
|
|
2820
|
+
*/
|
|
2821
|
+
async uploadAgentSessionAttachment(appId, sessionId, blobId, data, meta) {
|
|
2822
|
+
const credentials = await this.ensureAuthenticated();
|
|
2823
|
+
const url = `${credentials.serverUrl}/app/${appId}/api/agent-sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(blobId)}`;
|
|
2761
2824
|
const headers = {
|
|
2762
2825
|
Authorization: `Bearer ${credentials.accessToken}`,
|
|
2763
2826
|
"Content-Type": meta.contentType,
|
|
@@ -2768,16 +2831,47 @@ export class ApiClient {
|
|
|
2768
2831
|
if (credentials.globalAdminAppId) {
|
|
2769
2832
|
headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
|
|
2770
2833
|
}
|
|
2771
|
-
const response = await fetchWithTLS(url, {
|
|
2772
|
-
method: "PUT",
|
|
2773
|
-
headers,
|
|
2774
|
-
body: data,
|
|
2775
|
-
});
|
|
2834
|
+
const response = await fetchWithTLS(url, { method: "PUT", headers, body: data });
|
|
2776
2835
|
if (!response.ok) {
|
|
2777
|
-
throw await blobFailure(response, "Failed to upload
|
|
2836
|
+
throw await blobFailure(response, "Failed to upload attachment: ");
|
|
2778
2837
|
}
|
|
2779
2838
|
return response.json();
|
|
2780
2839
|
}
|
|
2840
|
+
/** Download an agent session's file by `sessionId` (#4585). */
|
|
2841
|
+
async downloadAgentSessionAttachment(appId, sessionId, blobId) {
|
|
2842
|
+
const response = await this.agentSessionAttachmentRead("GET", appId, sessionId, blobId);
|
|
2843
|
+
if (!response.ok) {
|
|
2844
|
+
throw await blobFailure(response, "Failed to download attachment: ");
|
|
2845
|
+
}
|
|
2846
|
+
return Buffer.from(await response.arrayBuffer());
|
|
2847
|
+
}
|
|
2848
|
+
/**
|
|
2849
|
+
* An agent session file's download headers (lower-cased names) by
|
|
2850
|
+
* `sessionId` (#4585). A refusal has no body, so it fails with the status
|
|
2851
|
+
* alone and no code.
|
|
2852
|
+
*/
|
|
2853
|
+
async headAgentSessionAttachment(appId, sessionId, blobId) {
|
|
2854
|
+
const response = await this.agentSessionAttachmentRead("HEAD", appId, sessionId, blobId);
|
|
2855
|
+
if (!response.ok && response.status !== 304) {
|
|
2856
|
+
throw new ApiError(`Failed to read attachment headers: HTTP ${response.status}`, response.status);
|
|
2857
|
+
}
|
|
2858
|
+
const headers = {};
|
|
2859
|
+
response.headers.forEach((value, name) => {
|
|
2860
|
+
headers[name.toLowerCase()] = value;
|
|
2861
|
+
});
|
|
2862
|
+
return { status: response.status, headers };
|
|
2863
|
+
}
|
|
2864
|
+
async agentSessionAttachmentRead(method, appId, sessionId, blobId) {
|
|
2865
|
+
const credentials = await this.ensureAuthenticated();
|
|
2866
|
+
const url = `${credentials.serverUrl}/app/${appId}/api/agent-sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(blobId)}/download`;
|
|
2867
|
+
const headers = {
|
|
2868
|
+
Authorization: `Bearer ${credentials.accessToken}`,
|
|
2869
|
+
};
|
|
2870
|
+
if (credentials.globalAdminAppId) {
|
|
2871
|
+
headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
|
|
2872
|
+
}
|
|
2873
|
+
return fetchWithTLS(url, { method, headers });
|
|
2874
|
+
}
|
|
2781
2875
|
async listDocumentAliases(appId, documentId) {
|
|
2782
2876
|
return this.get(`/app/${appId}/api/documents/${documentId}/aliases`);
|
|
2783
2877
|
}
|
|
@@ -2939,21 +3033,19 @@ export class ApiClient {
|
|
|
2939
3033
|
async uploadBucketBlob(appId, bucketIdOrKey, data, meta) {
|
|
2940
3034
|
const credentials = await this.ensureAuthenticated();
|
|
2941
3035
|
const url = `${credentials.serverUrl}/app/${appId}/api/blob-buckets/${encodeURIComponent(bucketIdOrKey)}/blobs`;
|
|
2942
|
-
const
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
method: "POST",
|
|
2955
|
-
headers,
|
|
2956
|
-
body: data,
|
|
3036
|
+
const response = await this.fetchWithStepUp(url, url, credentials.accessToken, (bearer) => {
|
|
3037
|
+
const headers = {
|
|
3038
|
+
Authorization: `Bearer ${bearer}`,
|
|
3039
|
+
"Content-Type": meta.contentType,
|
|
3040
|
+
"X-Blob-Filename": encodeURIComponent(meta.filename),
|
|
3041
|
+
};
|
|
3042
|
+
if (meta.tags && meta.tags.length > 0) {
|
|
3043
|
+
headers["X-Blob-Tags"] = JSON.stringify(meta.tags);
|
|
3044
|
+
}
|
|
3045
|
+
if (credentials.globalAdminAppId) {
|
|
3046
|
+
headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
|
|
3047
|
+
}
|
|
3048
|
+
return { method: "POST", headers, body: data };
|
|
2957
3049
|
});
|
|
2958
3050
|
if (!response.ok) {
|
|
2959
3051
|
throw await blobFailure(response, "Failed to upload blob: ");
|