@sequenceholdings/artifact-studio 0.6.1 → 0.6.3
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 +43 -0
- package/VERSION-PIN.md +7 -0
- package/dist/manifest-next-ingress.d.ts +213 -0
- package/dist/manifest-next-ingress.js +187 -0
- package/dist/manifest-next-reach.d.ts +40 -0
- package/dist/manifest-next-reach.js +48 -0
- package/dist/manifest-next.d.ts +133 -8
- package/dist/manifest-next.js +7 -46
- package/dist/manifest-static.d.ts +6 -0
- package/dist/manifest-static.js +10 -0
- package/dist/manifest.d.ts +67 -0
- package/dist/manifest.js +7 -0
- package/dist/server/api-client.d.ts +1 -0
- package/dist/server/api-client.js +2 -1
- package/dist/server/broker-contract.d.ts +33 -6
- package/dist/server/broker-contract.js +20 -2
- package/dist/server/client.js +31 -16
- package/dist/server/external-context.js +1 -1
- package/dist/server/ingress-control-contract.d.ts +272 -0
- package/dist/server/ingress-control-contract.js +196 -0
- package/dist/server/ingress-routes.d.ts +76 -0
- package/dist/server/ingress-routes.js +150 -0
- package/dist/server/internal/verify-any.d.ts +3 -1
- package/dist/server/internal/verify-any.js +13 -3
- package/dist/server/processes.d.ts +56 -0
- package/dist/server/processes.js +13 -0
- package/dist/server/request-context.d.ts +8 -4
- package/dist/server/request-context.js +8 -6
- package/dist/server/runtime-config.d.ts +1 -1
- package/dist/templates/nextjs/README.md +9 -0
- package/dist/tunnel/protocol.js +2 -4
- package/dist/tunnel/reserved-headers.d.ts +7 -0
- package/dist/tunnel/reserved-headers.js +20 -0
- package/package.json +9 -1
- package/templates/nextjs/README.md +9 -0
package/dist/manifest-next.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { artifactNextAuditCapabilitySchema } from './manifest-next-reach.js';
|
|
2
3
|
/** Approved major/minor profile. Builds additionally record exact patch versions. */
|
|
3
4
|
export declare const ARTIFACT_NEXT_RUNTIME: Readonly<{
|
|
4
5
|
sdk: "sequence";
|
|
@@ -20,14 +21,8 @@ export declare const ARTIFACT_NEXT_RESOURCE_DEFAULTS: {
|
|
|
20
21
|
max_instances: number;
|
|
21
22
|
timeout_seconds: number;
|
|
22
23
|
};
|
|
23
|
-
|
|
24
|
-
*
|
|
25
|
-
* application may record under `artifact_studio`; Atlas admits a first-party service, such as
|
|
26
|
-
* `application_portal`, only for the projects it registers for it. Server-only: browsers can't audit.
|
|
27
|
-
*/
|
|
28
|
-
export declare const artifactNextAuditCapabilitySchema: z.ZodObject<{
|
|
29
|
-
service: z.ZodString;
|
|
30
|
-
}, z.core.$strict>;
|
|
24
|
+
export { artifactNextAuditCapabilitySchema };
|
|
25
|
+
export * from './manifest-next-ingress.js';
|
|
31
26
|
export declare const artifactNextServerCapabilitiesSchema: z.ZodPrefault<z.ZodObject<{
|
|
32
27
|
audit: z.ZodOptional<z.ZodObject<{
|
|
33
28
|
service: z.ZodString;
|
|
@@ -132,6 +127,71 @@ export declare const artifactNextServerSchema: z.ZodPrefault<z.ZodObject<{
|
|
|
132
127
|
lattice: z.ZodOptional<z.ZodObject<{
|
|
133
128
|
accept: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
134
129
|
}, z.core.$strict>>;
|
|
130
|
+
ingress: z.ZodOptional<z.ZodObject<{
|
|
131
|
+
external: z.ZodArray<z.ZodObject<{
|
|
132
|
+
id: z.ZodString;
|
|
133
|
+
provider: z.ZodString;
|
|
134
|
+
service_account: z.ZodString;
|
|
135
|
+
headers: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
136
|
+
capabilities: z.ZodPrefault<z.ZodObject<{
|
|
137
|
+
storage: z.ZodOptional<z.ZodRecord<z.ZodUUID, z.ZodEnum<{
|
|
138
|
+
viewer: "viewer";
|
|
139
|
+
editor: "editor";
|
|
140
|
+
admin: "admin";
|
|
141
|
+
}>>>;
|
|
142
|
+
data: z.ZodPrefault<z.ZodObject<{
|
|
143
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
144
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
145
|
+
}, z.core.$strict>>;
|
|
146
|
+
api: z.ZodPrefault<z.ZodObject<{
|
|
147
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
148
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
149
|
+
}, z.core.$strict>>;
|
|
150
|
+
functions: z.ZodPrefault<z.ZodObject<{
|
|
151
|
+
invoke: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
152
|
+
}, z.core.$strict>>;
|
|
153
|
+
audit: z.ZodOptional<z.ZodObject<{
|
|
154
|
+
service: z.ZodString;
|
|
155
|
+
}, z.core.$strict>>;
|
|
156
|
+
processes: z.ZodOptional<z.ZodNever>;
|
|
157
|
+
}, z.core.$strict>>;
|
|
158
|
+
evidence: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
159
|
+
bind: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
160
|
+
email: "email";
|
|
161
|
+
subject: "subject";
|
|
162
|
+
authTime: "authTime";
|
|
163
|
+
amr: "amr";
|
|
164
|
+
"client.ip": "client.ip";
|
|
165
|
+
"client.userAgent": "client.userAgent";
|
|
166
|
+
issuer: "issuer";
|
|
167
|
+
}>>>;
|
|
168
|
+
fresh_auth_seconds: z.ZodOptional<z.ZodNumber>;
|
|
169
|
+
}, z.core.$strict>>>;
|
|
170
|
+
routes: z.ZodArray<z.ZodObject<{
|
|
171
|
+
path: z.ZodString;
|
|
172
|
+
methods: z.ZodArray<z.ZodEnum<{
|
|
173
|
+
GET: "GET";
|
|
174
|
+
POST: "POST";
|
|
175
|
+
PUT: "PUT";
|
|
176
|
+
PATCH: "PATCH";
|
|
177
|
+
DELETE: "DELETE";
|
|
178
|
+
}>>;
|
|
179
|
+
fresh_auth_seconds: z.ZodOptional<z.ZodNumber>;
|
|
180
|
+
max_body_bytes: z.ZodDefault<z.ZodNumber>;
|
|
181
|
+
responses: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
182
|
+
"application/json": "application/json";
|
|
183
|
+
"application/problem+json": "application/problem+json";
|
|
184
|
+
"application/pdf": "application/pdf";
|
|
185
|
+
"application/zip": "application/zip";
|
|
186
|
+
"image/png": "image/png";
|
|
187
|
+
"image/jpeg": "image/jpeg";
|
|
188
|
+
"image/webp": "image/webp";
|
|
189
|
+
"text/csv": "text/csv";
|
|
190
|
+
"application/octet-stream": "application/octet-stream";
|
|
191
|
+
}>>>;
|
|
192
|
+
}, z.core.$strict>>;
|
|
193
|
+
}, z.core.$strict>>;
|
|
194
|
+
}, z.core.$strict>>;
|
|
135
195
|
}, z.core.$strict>>;
|
|
136
196
|
export declare const artifactStudioNextManifestSchema: z.ZodObject<{
|
|
137
197
|
ontology: z.ZodOptional<z.ZodObject<{
|
|
@@ -251,6 +311,71 @@ export declare const artifactStudioNextManifestSchema: z.ZodObject<{
|
|
|
251
311
|
lattice: z.ZodOptional<z.ZodObject<{
|
|
252
312
|
accept: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
253
313
|
}, z.core.$strict>>;
|
|
314
|
+
ingress: z.ZodOptional<z.ZodObject<{
|
|
315
|
+
external: z.ZodArray<z.ZodObject<{
|
|
316
|
+
id: z.ZodString;
|
|
317
|
+
provider: z.ZodString;
|
|
318
|
+
service_account: z.ZodString;
|
|
319
|
+
headers: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
320
|
+
capabilities: z.ZodPrefault<z.ZodObject<{
|
|
321
|
+
storage: z.ZodOptional<z.ZodRecord<z.ZodUUID, z.ZodEnum<{
|
|
322
|
+
viewer: "viewer";
|
|
323
|
+
editor: "editor";
|
|
324
|
+
admin: "admin";
|
|
325
|
+
}>>>;
|
|
326
|
+
data: z.ZodPrefault<z.ZodObject<{
|
|
327
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
328
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
329
|
+
}, z.core.$strict>>;
|
|
330
|
+
api: z.ZodPrefault<z.ZodObject<{
|
|
331
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
332
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
333
|
+
}, z.core.$strict>>;
|
|
334
|
+
functions: z.ZodPrefault<z.ZodObject<{
|
|
335
|
+
invoke: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
336
|
+
}, z.core.$strict>>;
|
|
337
|
+
audit: z.ZodOptional<z.ZodObject<{
|
|
338
|
+
service: z.ZodString;
|
|
339
|
+
}, z.core.$strict>>;
|
|
340
|
+
processes: z.ZodOptional<z.ZodNever>;
|
|
341
|
+
}, z.core.$strict>>;
|
|
342
|
+
evidence: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
343
|
+
bind: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
344
|
+
email: "email";
|
|
345
|
+
subject: "subject";
|
|
346
|
+
authTime: "authTime";
|
|
347
|
+
amr: "amr";
|
|
348
|
+
"client.ip": "client.ip";
|
|
349
|
+
"client.userAgent": "client.userAgent";
|
|
350
|
+
issuer: "issuer";
|
|
351
|
+
}>>>;
|
|
352
|
+
fresh_auth_seconds: z.ZodOptional<z.ZodNumber>;
|
|
353
|
+
}, z.core.$strict>>>;
|
|
354
|
+
routes: z.ZodArray<z.ZodObject<{
|
|
355
|
+
path: z.ZodString;
|
|
356
|
+
methods: z.ZodArray<z.ZodEnum<{
|
|
357
|
+
GET: "GET";
|
|
358
|
+
POST: "POST";
|
|
359
|
+
PUT: "PUT";
|
|
360
|
+
PATCH: "PATCH";
|
|
361
|
+
DELETE: "DELETE";
|
|
362
|
+
}>>;
|
|
363
|
+
fresh_auth_seconds: z.ZodOptional<z.ZodNumber>;
|
|
364
|
+
max_body_bytes: z.ZodDefault<z.ZodNumber>;
|
|
365
|
+
responses: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
366
|
+
"application/json": "application/json";
|
|
367
|
+
"application/problem+json": "application/problem+json";
|
|
368
|
+
"application/pdf": "application/pdf";
|
|
369
|
+
"application/zip": "application/zip";
|
|
370
|
+
"image/png": "image/png";
|
|
371
|
+
"image/jpeg": "image/jpeg";
|
|
372
|
+
"image/webp": "image/webp";
|
|
373
|
+
"text/csv": "text/csv";
|
|
374
|
+
"application/octet-stream": "application/octet-stream";
|
|
375
|
+
}>>>;
|
|
376
|
+
}, z.core.$strict>>;
|
|
377
|
+
}, z.core.$strict>>;
|
|
378
|
+
}, z.core.$strict>>;
|
|
254
379
|
}, z.core.$strict>>;
|
|
255
380
|
targets: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
256
381
|
url: z.ZodString;
|
package/dist/manifest-next.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { storageScopesSchema } from '@sequenceholdings/storage';
|
|
2
1
|
import { ontologyContractSchema } from '@sequenceholdings/orm/canonical';
|
|
3
2
|
import { z } from 'zod';
|
|
4
3
|
import { capabilityPinsSchema, customRolesBlockSchema, validateCapabilityPins } from './manifest-custom-roles.js';
|
|
5
4
|
import { isManagedServerInvokePath } from './server/managed-server-path.js';
|
|
5
|
+
import { artifactNextIngressSchema, validateIngressAgainstServer } from './manifest-next-ingress.js';
|
|
6
|
+
import { artifactNextAuditCapabilitySchema, reachShape, serviceAccountRef } from './manifest-next-reach.js';
|
|
6
7
|
import { apiCapabilityPathAllowed, artifactPresentationSchema, artifactStudioStaticManifestSchema, sidebarBadgeSchema, } from './manifest-static.js';
|
|
7
8
|
/** Approved major/minor profile. Builds additionally record exact patch versions. */
|
|
8
9
|
export const ARTIFACT_NEXT_RUNTIME = Object.freeze({ sdk: 'sequence', node: 24, react: 19, next: '16.3' });
|
|
@@ -11,51 +12,8 @@ export const ARTIFACT_NEXT_APPROVED_PACKAGES = Object.freeze({ next: '16.3.4', r
|
|
|
11
12
|
export const ARTIFACT_NEXT_RESOURCE_DEFAULTS = {
|
|
12
13
|
cpu: 1, memory_mb: 1024, concurrency: 20, min_instances: 0, max_instances: 3, timeout_seconds: 60,
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const apiPath = z.string().min(1).max(512).refine((value) => {
|
|
17
|
-
if (!value.startsWith('/api/') || value === '/api/' || value === '/api/*')
|
|
18
|
-
return false;
|
|
19
|
-
if (/[\\%?#\s\x00-\x1f]/.test(value))
|
|
20
|
-
return false;
|
|
21
|
-
return value.split('/').slice(1).every((part, index, parts) => part !== '.' && part !== '..' && (part !== '' || index === parts.length - 1));
|
|
22
|
-
}, 'API capability paths must be scoped below /api/ without encoded paths, traversal, queries, or fragments');
|
|
23
|
-
// ORM references are what a delegated data token covers (orm-data-plane checkDataTokenScope):
|
|
24
|
-
// a query needs namespace/table for every table it reads, and a mutation needs its
|
|
25
|
-
// namespace/ops/Operation (or table coverage). Any other shape can never match, so it is
|
|
26
|
-
// a typo to report here rather than a 403 at request time. The patterns mirror the ORM's
|
|
27
|
-
// NAMESPACE_PATTERN, IDENTIFIER_PATTERN and GraphQL operation names; they are kept local so
|
|
28
|
-
// browser code that parses manifests does not load the ORM package. platformUsers is the
|
|
29
|
-
// ORM's built-in platform-user entity (PLATFORM_USER_TABLE), a logical key outside the
|
|
30
|
-
// identifier pattern that queries traversing user relations read.
|
|
31
|
-
const ormTableRef = /^[a-z][a-z0-9_]{0,40}\/(?:[a-z][a-z0-9_]{0,62}|platformUsers)$/;
|
|
32
|
-
const ormOperationRef = /^[a-z][a-z0-9_]{0,40}\/ops\/[_A-Za-z][_0-9A-Za-z]*$/;
|
|
33
|
-
const dataReadRef = boundedRef.refine((ref) => ormTableRef.test(ref), 'ORM read references use namespace/table');
|
|
34
|
-
const dataWriteRef = boundedRef.refine((ref) => ormTableRef.test(ref) || ormOperationRef.test(ref), 'ORM write references use namespace/ops/Operation or namespace/table');
|
|
35
|
-
const latticeProcessRef = z.string().min(2).max(128).regex(/^[A-Za-z0-9._-]+$/, 'Process references are Lattice process ids');
|
|
36
|
-
const reachShape = {
|
|
37
|
-
storage: storageScopesSchema.optional(),
|
|
38
|
-
data: z.strictObject({ read: z.array(dataReadRef).max(256).default([]), write: z.array(dataWriteRef).max(256).default([]) }).prefault({}),
|
|
39
|
-
api: z.strictObject({ read: z.array(apiPath).max(256).default([]), write: z.array(apiPath).max(256).default([]) }).prefault({}),
|
|
40
|
-
functions: z.strictObject({ invoke: z.array(boundedRef).max(256).default([]) }).prefault({}),
|
|
41
|
-
// `start`: processes the server may start runs of. `read`: processes whose runs, template and
|
|
42
|
-
// the user's own steps it may read. `act`: processes whose human steps it may advance and
|
|
43
|
-
// reassign, and whose runs' viewers it may manage. All name exact process ids; the user's own
|
|
44
|
-
// Invoker / Viewer / Advancer / Owner grants decide what happens.
|
|
45
|
-
processes: z.strictObject({
|
|
46
|
-
start: z.array(latticeProcessRef).max(256).default([]),
|
|
47
|
-
read: z.array(latticeProcessRef).max(256).default([]),
|
|
48
|
-
act: z.array(latticeProcessRef).max(256).default([]),
|
|
49
|
-
}).prefault({}),
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* Where the server's audit events (`client.audit.record`) go: an Atlas audit service id. Every
|
|
53
|
-
* application may record under `artifact_studio`; Atlas admits a first-party service, such as
|
|
54
|
-
* `application_portal`, only for the projects it registers for it. Server-only: browsers can't audit.
|
|
55
|
-
*/
|
|
56
|
-
export const artifactNextAuditCapabilitySchema = z.strictObject({
|
|
57
|
-
service: z.string().regex(/^[a-z][a-z0-9_]{0,63}$/, 'Audit services are Atlas audit service ids, such as artifact_studio'),
|
|
58
|
-
});
|
|
15
|
+
export { artifactNextAuditCapabilitySchema };
|
|
16
|
+
export * from './manifest-next-ingress.js';
|
|
59
17
|
export const artifactNextServerCapabilitiesSchema = z.strictObject({
|
|
60
18
|
...reachShape,
|
|
61
19
|
audit: artifactNextAuditCapabilitySchema.optional(),
|
|
@@ -118,6 +76,8 @@ export const artifactNextServerSchema = z.strictObject({
|
|
|
118
76
|
lattice: z.strictObject({
|
|
119
77
|
accept: z.array(managedServerInvokePath).max(32).default([]),
|
|
120
78
|
}).optional(),
|
|
79
|
+
/** Front doors for callers that are not Atlas users. Omitted means none: external callers are refused. */
|
|
80
|
+
ingress: artifactNextIngressSchema.optional(),
|
|
121
81
|
}).superRefine((server, ctx) => {
|
|
122
82
|
if (new Set(server.secrets.map((secret) => secret.name)).size !== server.secrets.length) {
|
|
123
83
|
ctx.addIssue({ code: 'custom', path: ['secrets'], message: 'Each secret environment name must be unique' });
|
|
@@ -128,6 +88,7 @@ export const artifactNextServerSchema = z.strictObject({
|
|
|
128
88
|
if (server.lattice && new Set(server.lattice.accept).size !== server.lattice.accept.length) {
|
|
129
89
|
ctx.addIssue({ code: 'custom', path: ['lattice', 'accept'], message: 'Lattice accept paths must be unique' });
|
|
130
90
|
}
|
|
91
|
+
validateIngressAgainstServer({ server, ctx });
|
|
131
92
|
}).prefault({});
|
|
132
93
|
const appRoot = z.string().min(1).max(512).refine((value) => {
|
|
133
94
|
if (value === '.')
|
|
@@ -23,6 +23,12 @@ export declare const artifactOfflineCapabilitySchema: z.ZodObject<{
|
|
|
23
23
|
policy_version: z.ZodNumber;
|
|
24
24
|
}, z.core.$strip>;
|
|
25
25
|
export type ArtifactOfflineCapability = z.infer<typeof artifactOfflineCapabilitySchema>;
|
|
26
|
+
/**
|
|
27
|
+
* Whether {@link apiCapabilityPathAllowed} treats an entry as a prefix: a trailing `/`, or a
|
|
28
|
+
* trailing `/*` with no other whole-segment `*`. Such an entry admits every path below it,
|
|
29
|
+
* including routes added later. An entry with a mid-path `*` matches whole segments by count.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isPrefixApiCapabilityEntry(entry: string): boolean;
|
|
26
32
|
export declare function apiCapabilityPathAllowed(pathname: string, allowed: readonly string[]): boolean;
|
|
27
33
|
export declare const artifactStudioStaticManifestSchema: z.ZodObject<{
|
|
28
34
|
ontology: z.ZodOptional<z.ZodObject<{
|
package/dist/manifest-static.js
CHANGED
|
@@ -46,6 +46,16 @@ function hasMidPathWildcard(entry) {
|
|
|
46
46
|
const parts = entry.split('/');
|
|
47
47
|
return parts.slice(1, -1).some((part) => part === '*');
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether {@link apiCapabilityPathAllowed} treats an entry as a prefix: a trailing `/`, or a
|
|
51
|
+
* trailing `/*` with no other whole-segment `*`. Such an entry admits every path below it,
|
|
52
|
+
* including routes added later. An entry with a mid-path `*` matches whole segments by count.
|
|
53
|
+
*/
|
|
54
|
+
export function isPrefixApiCapabilityEntry(entry) {
|
|
55
|
+
if (entry.includes('*') && (!entry.endsWith('/*') || hasMidPathWildcard(entry)))
|
|
56
|
+
return false;
|
|
57
|
+
return entry.endsWith('/*') || entry.endsWith('/');
|
|
58
|
+
}
|
|
49
59
|
export function apiCapabilityPathAllowed(pathname, allowed) {
|
|
50
60
|
return allowed.some((entry) => matchesApiCapabilityEntry(pathname, entry));
|
|
51
61
|
}
|
package/dist/manifest.d.ts
CHANGED
|
@@ -204,6 +204,71 @@ export declare const artifactStudioManifestSchema: z.ZodUnion<readonly [z.ZodObj
|
|
|
204
204
|
lattice: z.ZodOptional<z.ZodObject<{
|
|
205
205
|
accept: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
206
206
|
}, z.core.$strict>>;
|
|
207
|
+
ingress: z.ZodOptional<z.ZodObject<{
|
|
208
|
+
external: z.ZodArray<z.ZodObject<{
|
|
209
|
+
id: z.ZodString;
|
|
210
|
+
provider: z.ZodString;
|
|
211
|
+
service_account: z.ZodString;
|
|
212
|
+
headers: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
213
|
+
capabilities: z.ZodPrefault<z.ZodObject<{
|
|
214
|
+
storage: z.ZodOptional<z.ZodRecord<z.ZodUUID, z.ZodEnum<{
|
|
215
|
+
viewer: "viewer";
|
|
216
|
+
editor: "editor";
|
|
217
|
+
admin: "admin";
|
|
218
|
+
}>>>;
|
|
219
|
+
data: z.ZodPrefault<z.ZodObject<{
|
|
220
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
221
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
222
|
+
}, z.core.$strict>>;
|
|
223
|
+
api: z.ZodPrefault<z.ZodObject<{
|
|
224
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
225
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
226
|
+
}, z.core.$strict>>;
|
|
227
|
+
functions: z.ZodPrefault<z.ZodObject<{
|
|
228
|
+
invoke: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
229
|
+
}, z.core.$strict>>;
|
|
230
|
+
audit: z.ZodOptional<z.ZodObject<{
|
|
231
|
+
service: z.ZodString;
|
|
232
|
+
}, z.core.$strict>>;
|
|
233
|
+
processes: z.ZodOptional<z.ZodNever>;
|
|
234
|
+
}, z.core.$strict>>;
|
|
235
|
+
evidence: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
236
|
+
bind: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
237
|
+
email: "email";
|
|
238
|
+
subject: "subject";
|
|
239
|
+
authTime: "authTime";
|
|
240
|
+
amr: "amr";
|
|
241
|
+
"client.ip": "client.ip";
|
|
242
|
+
"client.userAgent": "client.userAgent";
|
|
243
|
+
issuer: "issuer";
|
|
244
|
+
}>>>;
|
|
245
|
+
fresh_auth_seconds: z.ZodOptional<z.ZodNumber>;
|
|
246
|
+
}, z.core.$strict>>>;
|
|
247
|
+
routes: z.ZodArray<z.ZodObject<{
|
|
248
|
+
path: z.ZodString;
|
|
249
|
+
methods: z.ZodArray<z.ZodEnum<{
|
|
250
|
+
GET: "GET";
|
|
251
|
+
POST: "POST";
|
|
252
|
+
PUT: "PUT";
|
|
253
|
+
PATCH: "PATCH";
|
|
254
|
+
DELETE: "DELETE";
|
|
255
|
+
}>>;
|
|
256
|
+
fresh_auth_seconds: z.ZodOptional<z.ZodNumber>;
|
|
257
|
+
max_body_bytes: z.ZodDefault<z.ZodNumber>;
|
|
258
|
+
responses: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
259
|
+
"application/json": "application/json";
|
|
260
|
+
"application/problem+json": "application/problem+json";
|
|
261
|
+
"application/pdf": "application/pdf";
|
|
262
|
+
"application/zip": "application/zip";
|
|
263
|
+
"image/png": "image/png";
|
|
264
|
+
"image/jpeg": "image/jpeg";
|
|
265
|
+
"image/webp": "image/webp";
|
|
266
|
+
"text/csv": "text/csv";
|
|
267
|
+
"application/octet-stream": "application/octet-stream";
|
|
268
|
+
}>>>;
|
|
269
|
+
}, z.core.$strict>>;
|
|
270
|
+
}, z.core.$strict>>;
|
|
271
|
+
}, z.core.$strict>>;
|
|
207
272
|
}, z.core.$strict>>;
|
|
208
273
|
targets: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
209
274
|
url: z.ZodString;
|
|
@@ -226,3 +291,5 @@ export type ArtifactStudioManifest = ArtifactStudioStaticManifest | ArtifactStud
|
|
|
226
291
|
export type ArtifactStudioManifestInput = z.input<typeof artifactStudioManifestSchema>;
|
|
227
292
|
export declare function parseArtifactStudioManifest(value: unknown): ArtifactStudioManifest;
|
|
228
293
|
export declare function isNextArtifactManifest(manifest: ArtifactStudioManifest): manifest is ArtifactStudioNextManifest;
|
|
294
|
+
/** Parses an `artifact.bundle.yml` that must be an Artifact Next manifest (schema_version 2). */
|
|
295
|
+
export declare function parseArtifactNextManifest(value: unknown): ArtifactStudioNextManifest;
|
package/dist/manifest.js
CHANGED
|
@@ -14,3 +14,10 @@ export function parseArtifactStudioManifest(value) {
|
|
|
14
14
|
export function isNextArtifactManifest(manifest) {
|
|
15
15
|
return manifest.artifact.type === 'nextjs';
|
|
16
16
|
}
|
|
17
|
+
/** Parses an `artifact.bundle.yml` that must be an Artifact Next manifest (schema_version 2). */
|
|
18
|
+
export function parseArtifactNextManifest(value) {
|
|
19
|
+
const manifest = parseArtifactStudioManifest(value);
|
|
20
|
+
if (!isNextArtifactManifest(manifest))
|
|
21
|
+
throw new Error('Expected an Artifact Next manifest (artifact.type nextjs)');
|
|
22
|
+
return manifest;
|
|
23
|
+
}
|
|
@@ -8,6 +8,7 @@ export interface ArtifactServerFetchOptions {
|
|
|
8
8
|
/** Sends one broker operation; the client's transport (retries, the external-principal gate). */
|
|
9
9
|
export type ArtifactBrokerRequest = (operation: ArtifactServerRequest, options?: {
|
|
10
10
|
signal?: AbortSignal;
|
|
11
|
+
stream?: boolean;
|
|
11
12
|
}) => Promise<Response>;
|
|
12
13
|
/** `client.api`: Atlas routes reached through the broker as `api` operations. */
|
|
13
14
|
export declare function createApiClient({ mode, request, json }: {
|
|
@@ -22,7 +22,8 @@ export function createApiClient({ mode, request, json }) {
|
|
|
22
22
|
})) };
|
|
23
23
|
},
|
|
24
24
|
async stream(path, options = {}) {
|
|
25
|
-
|
|
25
|
+
// A stream outlives the broker's answer deadline: only its headers must arrive in time.
|
|
26
|
+
const response = await request(operation(path, options), { signal: options.signal, stream: true });
|
|
26
27
|
if (!response.ok || !response.body) {
|
|
27
28
|
throw await stepUpErrorFromResponse(response) ??
|
|
28
29
|
new ArtifactServerError(response.status, 'stream_unavailable', 'Atlas did not return a readable response');
|
|
@@ -3,10 +3,10 @@ export { ORM_JSON_SCALARS_HEADER, ORM_LOSSLESS_JSON_SCALARS, type ArtifactOrmJso
|
|
|
3
3
|
export declare const artifactServerMethodSchema: z.ZodEnum<{
|
|
4
4
|
GET: "GET";
|
|
5
5
|
POST: "POST";
|
|
6
|
-
DELETE: "DELETE";
|
|
7
|
-
HEAD: "HEAD";
|
|
8
6
|
PUT: "PUT";
|
|
9
7
|
PATCH: "PATCH";
|
|
8
|
+
DELETE: "DELETE";
|
|
9
|
+
HEAD: "HEAD";
|
|
10
10
|
}>;
|
|
11
11
|
export declare const artifactServerModeSchema: z.ZodDefault<z.ZodEnum<{
|
|
12
12
|
service: "service";
|
|
@@ -18,6 +18,8 @@ export declare function isArtifactApiPath(path: string): boolean;
|
|
|
18
18
|
export declare const ARTIFACT_PROCESS_RUN_STATUSES: readonly ["pending", "running", "completed", "failed", "cancelled", "timed_out"];
|
|
19
19
|
/** Lattice lists at most this many runs per call. */
|
|
20
20
|
export declare const ARTIFACT_PROCESS_RUNS_LIMIT = 500;
|
|
21
|
+
/** A run-events read returns at most this many events per call, as the run's stream drains them. */
|
|
22
|
+
export declare const ARTIFACT_PROCESS_RUN_EVENTS_LIMIT = 1000;
|
|
21
23
|
/** Which of the user's human steps to list: parked and waiting on them, or already done. */
|
|
22
24
|
export declare const ARTIFACT_PROCESS_TASK_VIEWS: readonly ["active", "completed"];
|
|
23
25
|
/** Lattice lists at most this many human steps per call, as its own inbox does. */
|
|
@@ -45,10 +47,10 @@ export declare const artifactServerRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
45
47
|
method: z.ZodEnum<{
|
|
46
48
|
GET: "GET";
|
|
47
49
|
POST: "POST";
|
|
48
|
-
DELETE: "DELETE";
|
|
49
|
-
HEAD: "HEAD";
|
|
50
50
|
PUT: "PUT";
|
|
51
51
|
PATCH: "PATCH";
|
|
52
|
+
DELETE: "DELETE";
|
|
53
|
+
HEAD: "HEAD";
|
|
52
54
|
}>;
|
|
53
55
|
path: z.ZodString;
|
|
54
56
|
body: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -71,10 +73,10 @@ export declare const artifactServerRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
71
73
|
method: z.ZodEnum<{
|
|
72
74
|
GET: "GET";
|
|
73
75
|
POST: "POST";
|
|
74
|
-
DELETE: "DELETE";
|
|
75
|
-
HEAD: "HEAD";
|
|
76
76
|
PUT: "PUT";
|
|
77
77
|
PATCH: "PATCH";
|
|
78
|
+
DELETE: "DELETE";
|
|
79
|
+
HEAD: "HEAD";
|
|
78
80
|
}>;
|
|
79
81
|
path: z.ZodString;
|
|
80
82
|
body: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -94,6 +96,7 @@ export declare const artifactServerRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
94
96
|
}>>;
|
|
95
97
|
process: z.ZodString;
|
|
96
98
|
runId: z.ZodOptional<z.ZodString>;
|
|
99
|
+
startNodeId: z.ZodOptional<z.ZodString>;
|
|
97
100
|
title: z.ZodOptional<z.ZodString>;
|
|
98
101
|
entity: z.ZodOptional<z.ZodObject<{
|
|
99
102
|
type: z.ZodString;
|
|
@@ -132,6 +135,15 @@ export declare const artifactServerRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
132
135
|
user: "user";
|
|
133
136
|
}>>;
|
|
134
137
|
runId: z.ZodString;
|
|
138
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
139
|
+
operation: z.ZodLiteral<"lattice_run_events">;
|
|
140
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
141
|
+
service: "service";
|
|
142
|
+
user: "user";
|
|
143
|
+
}>>;
|
|
144
|
+
runId: z.ZodString;
|
|
145
|
+
since: z.ZodOptional<z.ZodString>;
|
|
146
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
135
147
|
}, z.core.$strict>, z.ZodObject<{
|
|
136
148
|
operation: z.ZodLiteral<"lattice_process">;
|
|
137
149
|
mode: z.ZodDefault<z.ZodEnum<{
|
|
@@ -172,6 +184,21 @@ export declare const artifactServerRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
172
184
|
}>>;
|
|
173
185
|
runId: z.ZodString;
|
|
174
186
|
stepId: z.ZodString;
|
|
187
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
188
|
+
operation: z.ZodLiteral<"lattice_cancel">;
|
|
189
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
190
|
+
service: "service";
|
|
191
|
+
user: "user";
|
|
192
|
+
}>>;
|
|
193
|
+
runId: z.ZodString;
|
|
194
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
195
|
+
operation: z.ZodLiteral<"lattice_reset_step">;
|
|
196
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
197
|
+
service: "service";
|
|
198
|
+
user: "user";
|
|
199
|
+
}>>;
|
|
200
|
+
runId: z.ZodString;
|
|
201
|
+
stepId: z.ZodString;
|
|
175
202
|
}, z.core.$strict>, z.ZodObject<{
|
|
176
203
|
operation: z.ZodLiteral<"lattice_viewers">;
|
|
177
204
|
mode: z.ZodDefault<z.ZodEnum<{
|
|
@@ -31,6 +31,8 @@ const latticeVersion = z.string().min(1).max(128).regex(/^[A-Za-z0-9._-]+$/);
|
|
|
31
31
|
export const ARTIFACT_PROCESS_RUN_STATUSES = ['pending', 'running', 'completed', 'failed', 'cancelled', 'timed_out'];
|
|
32
32
|
/** Lattice lists at most this many runs per call. */
|
|
33
33
|
export const ARTIFACT_PROCESS_RUNS_LIMIT = 500;
|
|
34
|
+
/** A run-events read returns at most this many events per call, as the run's stream drains them. */
|
|
35
|
+
export const ARTIFACT_PROCESS_RUN_EVENTS_LIMIT = 1000;
|
|
34
36
|
/** Which of the user's human steps to list: parked and waiting on them, or already done. */
|
|
35
37
|
export const ARTIFACT_PROCESS_TASK_VIEWS = ['active', 'completed'];
|
|
36
38
|
/** Lattice lists at most this many human steps per call, as its own inbox does. */
|
|
@@ -106,10 +108,12 @@ export const artifactServerRequestSchema = z.discriminatedUnion('operation', [
|
|
|
106
108
|
}, 'Expected a Storage API path'), body: z.unknown().optional() }).strict(),
|
|
107
109
|
z.object({ operation: z.literal('function'), mode, id, params: z.record(z.string(), z.unknown()).optional() }).strict(),
|
|
108
110
|
// Starts a run of a declared process, as `POST /api/lattice/runs` does: the application names the
|
|
109
|
-
// run, passes its input
|
|
110
|
-
//
|
|
111
|
+
// run, passes its input, may seed who works each human node and may enter the graph at a node
|
|
112
|
+
// other than the process's start (the route's `start_node_id`). The version and engine stay with
|
|
113
|
+
// the process and its owning routes.
|
|
111
114
|
z.object({
|
|
112
115
|
operation: z.literal('lattice'), mode, process: latticeId, runId: latticeId.optional(),
|
|
116
|
+
startNodeId: z.string().min(1).max(255).regex(/^[A-Za-z0-9._-]+$/).optional(),
|
|
113
117
|
title: z.string().max(500).optional(),
|
|
114
118
|
entity: z.object({ type: entityRef, id: entityRef }).strict().optional(),
|
|
115
119
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
@@ -127,6 +131,14 @@ export const artifactServerRequestSchema = z.discriminatedUnion('operation', [
|
|
|
127
131
|
// Reads one run the user may view, as `GET /api/lattice/runs/[runId]` does: the run, its steps, the
|
|
128
132
|
// collapsed per-node states and the event cursor to resume its stream from.
|
|
129
133
|
z.object({ operation: z.literal('lattice_run'), mode, runId: latticeId }).strict(),
|
|
134
|
+
// A page of a run's events after a cursor, oldest first, as `GET /api/lattice/runs/[runId]/stream`
|
|
135
|
+
// sends them: `since` is the cursor `lattice_run` returned, or the one the previous page returned.
|
|
136
|
+
// Polling with it sees every event exactly once. The user's Viewer relation on the run decides.
|
|
137
|
+
z.object({
|
|
138
|
+
operation: z.literal('lattice_run_events'), mode, runId: latticeId,
|
|
139
|
+
since: z.string().min(1).max(512).optional(),
|
|
140
|
+
limit: z.number().int().min(1).max(ARTIFACT_PROCESS_RUN_EVENTS_LIMIT).optional(),
|
|
141
|
+
}).strict(),
|
|
130
142
|
// Describes a declared process — the applied template its runs are filled in from: nodes, edges and
|
|
131
143
|
// what a person working each human step is shown. The active version, or the one a run pinned.
|
|
132
144
|
z.object({ operation: z.literal('lattice_process'), mode, process: latticeId, version: latticeVersion.optional() }).strict(),
|
|
@@ -150,6 +162,12 @@ export const artifactServerRequestSchema = z.discriminatedUnion('operation', [
|
|
|
150
162
|
// The user's run `owner` relation decides; the node's assignee allowlist still applies.
|
|
151
163
|
z.object({ operation: z.literal('lattice_assign'), mode, runId: latticeId, stepId: latticeId, ...principalSet }).strict()
|
|
152
164
|
.refine(namesPrincipals, PRINCIPAL_SET_MESSAGE),
|
|
165
|
+
// Cancels a run of a process declared under `processes.act`, with its forked lineage, as
|
|
166
|
+
// `POST /api/lattice/runs/[runId]/cancel` does. The user's Invoker relation on the run decides.
|
|
167
|
+
z.object({ operation: z.literal('lattice_cancel'), mode, runId: latticeId }).strict(),
|
|
168
|
+
// Resets a generating agent step without cancelling its run, as `POST .../steps/[stepId]/reset`
|
|
169
|
+
// does: the step settles and the run re-parks at the node's reset edge. Invoker-gated.
|
|
170
|
+
z.object({ operation: z.literal('lattice_reset_step'), mode, runId: latticeId, stepId: latticeId }).strict(),
|
|
153
171
|
// The explicit viewer grants on a run, for its owner.
|
|
154
172
|
z.object({ operation: z.literal('lattice_viewers'), mode, runId: latticeId }).strict(),
|
|
155
173
|
// Shares a run with users and groups (grants `viewer`), or takes a share back. Owner-gated;
|
package/dist/server/client.js
CHANGED
|
@@ -170,6 +170,8 @@ async function storageApiError(response) {
|
|
|
170
170
|
const envelope = storageErrorSchema.safeParse(await response.json().catch(() => undefined));
|
|
171
171
|
return new StorageApiError(response.status, envelope.success ? envelope.data.error.code : 'request_failed', envelope.success ? envelope.data.error.message : `Storage request failed (${response.status})`);
|
|
172
172
|
}
|
|
173
|
+
/** How long Atlas has to answer a broker call; for a stream, to send its first byte of headers. */
|
|
174
|
+
const BROKER_RESPONSE_TIMEOUT_MS = 60_000;
|
|
173
175
|
/**
|
|
174
176
|
* Broker operations that change nothing in Atlas. An ORM call's caller decides from its manifest
|
|
175
177
|
* kind; function invokes and process starts can act, and audit delivery resends on its own terms.
|
|
@@ -186,7 +188,7 @@ function isReadOperation(operation) {
|
|
|
186
188
|
}
|
|
187
189
|
}
|
|
188
190
|
function createBrokerTransport({ context, principal, token, getRuntime, fetcher, log }) {
|
|
189
|
-
const send = async (input, signal) => {
|
|
191
|
+
const send = async (input, signal, stream = false) => {
|
|
190
192
|
// The one choke point for every broker operation: an external caller never acts as a user.
|
|
191
193
|
if (input.mode === 'user' && principal.kind === 'external')
|
|
192
194
|
throw new ArtifactPrincipalError('external_requires_service');
|
|
@@ -199,19 +201,32 @@ function createBrokerTransport({ context, principal, token, getRuntime, fetcher,
|
|
|
199
201
|
if (Buffer.byteLength(body, 'utf8') > maxBodyBytes)
|
|
200
202
|
throw new ArtifactServerError(413, 'request_too_large', 'Server SDK requests are limited to 1 MiB; use Storage uploads for files');
|
|
201
203
|
const runtime = await getRuntime();
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
204
|
+
// The timeout covers the whole call, body included, except for a stream: its timer is cleared
|
|
205
|
+
// once Atlas answers, so a long body isn't cut off mid-read.
|
|
206
|
+
const expiry = new AbortController();
|
|
207
|
+
const timer = setTimeout(() => expiry.abort(new DOMException('Atlas did not answer in time', 'TimeoutError')), BROKER_RESPONSE_TIMEOUT_MS);
|
|
208
|
+
if (!stream)
|
|
209
|
+
timer.unref?.();
|
|
210
|
+
const timeout = expiry.signal;
|
|
211
|
+
try {
|
|
212
|
+
return await fetcher(`${runtime.atlasUrl}${ARTIFACT_SERVER_REQUEST_PATH}`, {
|
|
213
|
+
method: 'POST',
|
|
214
|
+
headers: {
|
|
215
|
+
'Content-Type': 'application/json',
|
|
216
|
+
[ARTIFACT_RUNTIME_TOKEN_HEADER]: runtime.sessionToken,
|
|
217
|
+
[ARTIFACT_REQUEST_CONTEXT_HEADER]: token,
|
|
218
|
+
},
|
|
219
|
+
body,
|
|
220
|
+
cache: 'no-store',
|
|
221
|
+
redirect: 'error',
|
|
222
|
+
credentials: 'omit',
|
|
223
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
if (stream)
|
|
228
|
+
clearTimeout(timer);
|
|
229
|
+
}
|
|
215
230
|
};
|
|
216
231
|
/**
|
|
217
232
|
* Sends a read again when its connection to Atlas failed before Atlas answered: such failures
|
|
@@ -219,10 +234,10 @@ function createBrokerTransport({ context, principal, token, getRuntime, fetcher,
|
|
|
219
234
|
* again, because a reset can come after Atlas acted on it. A response, whatever its status, is
|
|
220
235
|
* Atlas's answer and is returned as is.
|
|
221
236
|
*/
|
|
222
|
-
return async (input, { signal, replaySafe = isReadOperation(input) } = {}) => {
|
|
237
|
+
return async (input, { signal, replaySafe = isReadOperation(input), stream = false } = {}) => {
|
|
223
238
|
for (let attempt = 1;; attempt++) {
|
|
224
239
|
try {
|
|
225
|
-
return await send(input, signal);
|
|
240
|
+
return await send(input, signal, stream);
|
|
226
241
|
}
|
|
227
242
|
catch (error) {
|
|
228
243
|
const code = replaySafe && attempt < reconnect.attempts ? transientConnectionCode(error) : null;
|
|
@@ -9,7 +9,7 @@ import { verifyAnyArtifactContext } from './internal/verify-any.js';
|
|
|
9
9
|
export function verifyArtifactExternalContext({ token, publicKeys, projectId, environment, method, path, accept, now }) {
|
|
10
10
|
if (!method || !path)
|
|
11
11
|
throw new ArtifactContextError();
|
|
12
|
-
//
|
|
12
|
+
// verifyAnyArtifactContext canonicalises `path` (canonicalizeIngressPath) before comparing it.
|
|
13
13
|
const context = verifyAnyArtifactContext({ token, publicKeys, projectId, environment, method, path, now });
|
|
14
14
|
if (context.v !== 2)
|
|
15
15
|
throw new ArtifactContextError('external_context_required');
|