@sequenceholdings/artifact-studio 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/VERSION-PIN.md +14 -3
- package/dist/api.d.ts +11 -1
- package/dist/api.js +26 -9
- package/dist/atlas-ui-css-plugin.d.ts +11 -7
- package/dist/atlas-ui-css-plugin.js +38 -10
- package/dist/build.d.ts +2 -2
- package/dist/build.js +14 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +108 -37
- package/dist/dev-link.d.ts +1 -1
- package/dist/dev-link.js +2 -1
- package/dist/gateway/admission.d.ts +55 -0
- package/dist/gateway/admission.js +132 -0
- package/dist/gateway/control.d.ts +91 -0
- package/dist/gateway/control.js +113 -0
- package/dist/gateway/hosted-proxy.d.ts +36 -0
- package/dist/gateway/hosted-proxy.js +347 -0
- package/dist/gateway/release-admission.d.ts +23 -0
- package/dist/gateway/release-admission.js +259 -0
- package/dist/gateway/server.d.ts +29 -0
- package/dist/gateway/server.js +298 -0
- package/dist/gateway/session.d.ts +71 -0
- package/dist/gateway/session.js +143 -0
- package/dist/gateway/usage.d.ts +37 -0
- package/dist/gateway/usage.js +46 -0
- package/dist/gateway-main.d.ts +1 -0
- package/dist/gateway-main.js +84 -0
- package/dist/git-clone.d.ts +23 -1
- package/dist/git-clone.js +32 -15
- package/dist/manifest-next.d.ts +217 -0
- package/dist/manifest-next.js +120 -0
- package/dist/manifest-static.d.ts +76 -0
- package/dist/manifest-static.js +124 -0
- package/dist/manifest.d.ts +124 -19
- package/dist/manifest.js +12 -118
- package/dist/next/bridge.d.ts +47 -0
- package/dist/next/bridge.js +229 -0
- package/dist/next/browser-logs.d.ts +7 -0
- package/dist/next/browser-logs.js +51 -0
- package/dist/next/browser.d.ts +6 -0
- package/dist/next/browser.js +57 -0
- package/dist/next/protocol.d.ts +73 -0
- package/dist/next/protocol.js +56 -0
- package/dist/next/release.d.ts +15 -0
- package/dist/next/release.js +46 -0
- package/dist/next/requests.d.ts +15 -0
- package/dist/next/requests.js +59 -0
- package/dist/next/routes.d.ts +4 -0
- package/dist/next/routes.js +21 -0
- package/dist/next/streams.d.ts +16 -0
- package/dist/next/streams.js +132 -0
- package/dist/next/theme.d.ts +10 -0
- package/dist/next/theme.js +45 -0
- package/dist/next/transport.d.ts +15 -0
- package/dist/next/transport.js +13 -0
- package/dist/next-build.d.ts +29 -0
- package/dist/next-build.js +129 -0
- package/dist/next-cache/adapter-runtime.d.ts +51 -0
- package/dist/next-cache/adapter-runtime.js +73 -0
- package/dist/next-cache/component-handler.d.ts +3 -0
- package/dist/next-cache/component-handler.js +10 -0
- package/dist/next-cache/components.d.ts +20 -0
- package/dist/next-cache/components.js +113 -0
- package/dist/next-cache/config.d.ts +9 -0
- package/dist/next-cache/config.js +78 -0
- package/dist/next-cache/incremental-handler.d.ts +18 -0
- package/dist/next-cache/incremental-handler.js +17 -0
- package/dist/next-cache/incremental.d.ts +27 -0
- package/dist/next-cache/incremental.js +99 -0
- package/dist/next-cache/index.d.ts +18 -0
- package/dist/next-cache/index.js +16 -0
- package/dist/next-cache/memory-store.d.ts +27 -0
- package/dist/next-cache/memory-store.js +118 -0
- package/dist/next-cache/redis-scripts.d.ts +3 -0
- package/dist/next-cache/redis-scripts.js +85 -0
- package/dist/next-cache/redis-store.d.ts +18 -0
- package/dist/next-cache/redis-store.js +56 -0
- package/dist/next-cache/runtime.d.ts +29 -0
- package/dist/next-cache/runtime.js +63 -0
- package/dist/next-cache/serialization.d.ts +39 -0
- package/dist/next-cache/serialization.js +333 -0
- package/dist/next-cache/store.d.ts +54 -0
- package/dist/next-cache/store.js +45 -0
- package/dist/next-cache/valkey-transport.d.ts +52 -0
- package/dist/next-cache/valkey-transport.js +189 -0
- package/dist/next-dev-control.d.ts +48 -0
- package/dist/next-dev-control.js +63 -0
- package/dist/next-dev-process.d.ts +17 -0
- package/dist/next-dev-process.js +110 -0
- package/dist/next-dev.d.ts +12 -0
- package/dist/next-dev.js +267 -0
- package/dist/next-image-config.d.ts +9 -0
- package/dist/next-image-config.js +101 -0
- package/dist/next-project.d.ts +22 -0
- package/dist/next-project.js +64 -0
- package/dist/next-public-fonts-local.d.ts +7 -0
- package/dist/next-public-fonts-local.js +64 -0
- package/dist/next-public-fonts.d.ts +13 -0
- package/dist/next-public-fonts.js +49 -0
- package/dist/next-source-hash.d.ts +14 -0
- package/dist/next-source-hash.js +18 -0
- package/dist/next-source.d.ts +35 -0
- package/dist/next-source.js +155 -0
- package/dist/next.d.ts +9 -0
- package/dist/next.js +5 -0
- package/dist/project.d.ts +3 -0
- package/dist/project.js +9 -1
- package/dist/runtime-contracts.d.ts +229 -0
- package/dist/runtime-contracts.js +224 -0
- package/dist/sdk.d.ts +420 -1
- package/dist/sdk.js +35 -0
- package/dist/server/broker-contract.d.ts +68 -0
- package/dist/server/broker-contract.js +33 -0
- package/dist/server/client.d.ts +67 -0
- package/dist/server/client.js +159 -0
- package/dist/server/logger.d.ts +26 -0
- package/dist/server/logger.js +24 -0
- package/dist/server/release-forms.d.ts +13 -0
- package/dist/server/release-forms.js +124 -0
- package/dist/server/request-context.d.ts +74 -0
- package/dist/server/request-context.js +103 -0
- package/dist/server/runtime-config.d.ts +53 -0
- package/dist/server/runtime-config.js +64 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +4 -0
- package/dist/templates/nextjs/.artifactignore +14 -0
- package/dist/templates/nextjs/README.md +151 -0
- package/dist/templates/nextjs/app/about/page.tsx +26 -0
- package/dist/templates/nextjs/app/actions.ts +18 -0
- package/dist/templates/nextjs/app/api/health/route.ts +6 -0
- package/dist/templates/nextjs/app/components/counter.tsx +19 -0
- package/dist/templates/nextjs/app/components/greeting-form.tsx +38 -0
- package/dist/templates/nextjs/app/globals.css +4 -0
- package/dist/templates/nextjs/app/layout.tsx +27 -0
- package/dist/templates/nextjs/app/page.tsx +51 -0
- package/dist/templates/nextjs/app/providers.tsx +107 -0
- package/dist/templates/nextjs/artifact.bundle.yml +55 -0
- package/dist/templates/nextjs/next.config.mjs +27 -0
- package/dist/templates/nextjs/package.json +32 -0
- package/dist/templates/nextjs/pnpm-workspace.yaml +8 -0
- package/dist/templates/nextjs/postcss.config.mjs +5 -0
- package/dist/templates/nextjs/tsconfig.json +20 -0
- package/dist/templates/react-vite/artifact.bundle.yml +0 -1
- package/dist/templates/react-vite/package.json +1 -1
- package/dist/tunnel/broker.d.ts +75 -0
- package/dist/tunnel/broker.js +451 -0
- package/dist/tunnel/client.d.ts +22 -0
- package/dist/tunnel/client.js +177 -0
- package/dist/tunnel/index.d.ts +6 -0
- package/dist/tunnel/index.js +4 -0
- package/dist/tunnel/io.d.ts +33 -0
- package/dist/tunnel/io.js +108 -0
- package/dist/tunnel/peer.d.ts +25 -0
- package/dist/tunnel/peer.js +147 -0
- package/dist/tunnel/protocol.d.ts +69 -0
- package/dist/tunnel/protocol.js +196 -0
- package/dist/tunnel/redis-ownership.d.ts +38 -0
- package/dist/tunnel/redis-ownership.js +100 -0
- package/dist/tunnel/redis-socket.d.ts +13 -0
- package/dist/tunnel/redis-socket.js +48 -0
- package/dist/tunnel/redis-types.d.ts +70 -0
- package/dist/tunnel/redis-types.js +9 -0
- package/dist/tunnel/redis.d.ts +53 -0
- package/dist/tunnel/redis.js +391 -0
- package/dist/tunnel/transport.d.ts +17 -0
- package/dist/tunnel/transport.js +1 -0
- package/package.json +68 -4
- package/templates/nextjs/.artifactignore +14 -0
- package/templates/nextjs/README.md +151 -0
- package/templates/nextjs/app/about/page.tsx +26 -0
- package/templates/nextjs/app/actions.ts +18 -0
- package/templates/nextjs/app/api/health/route.ts +6 -0
- package/templates/nextjs/app/components/counter.tsx +19 -0
- package/templates/nextjs/app/components/greeting-form.tsx +38 -0
- package/templates/nextjs/app/globals.css +4 -0
- package/templates/nextjs/app/layout.tsx +27 -0
- package/templates/nextjs/app/page.tsx +51 -0
- package/templates/nextjs/app/providers.tsx +107 -0
- package/templates/nextjs/artifact.bundle.yml +55 -0
- package/templates/nextjs/next.config.mjs +27 -0
- package/templates/nextjs/package.json +32 -0
- package/templates/nextjs/pnpm-workspace.yaml +8 -0
- package/templates/nextjs/postcss.config.mjs +5 -0
- package/templates/nextjs/tsconfig.json +20 -0
- package/templates/react-vite/artifact.bundle.yml +0 -1
- package/templates/react-vite/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,21 +8,22 @@ import { writeBuildArtifact } from './build.js';
|
|
|
8
8
|
import { buildArtifactStudioProjectIsolated } from './build-subprocess.js';
|
|
9
9
|
import { resolveArtifactStudioPeerVersions, } from './peer-versions.js';
|
|
10
10
|
import { prepareArtifactBuildRoot } from './prepare-build.js';
|
|
11
|
-
import { parseArtifactStudioManifest } from './manifest.js';
|
|
12
|
-
import { hasArtifactStudioManifest, readArtifactStudioSource } from './project.js';
|
|
11
|
+
import { isNextArtifactManifest, parseArtifactStudioManifest } from './manifest.js';
|
|
12
|
+
import { hasArtifactStudioManifest, readArtifactStudioManifest, readArtifactStudioSource } from './project.js';
|
|
13
13
|
import { localGitMetadata, parseSourceSpec, resolveArtifactSource, } from './source-resolver.js';
|
|
14
14
|
import { acquireDevLock } from './dev-lock.js';
|
|
15
15
|
import { runWatchLoop } from './watch-loop.js';
|
|
16
16
|
import { hashLinkedSources, hasLinkedAtlasUiToolchain, parseLinks, rebuildLinkedDep, resolveLinkedDeps, syncLinkedDist, } from './dev-link.js';
|
|
17
17
|
import { lintArtifactSandbox, reportSandboxWarnings } from './sandbox-lint.js';
|
|
18
18
|
import { assertActiveDeployProvenance } from './active-deploy-policy.js';
|
|
19
|
+
import { normalizeArtifactPath } from './paths.js';
|
|
19
20
|
import yaml from 'js-yaml';
|
|
20
21
|
const COMMANDS = [
|
|
21
22
|
'init', 'whoami', 'link', 'env', 'status', 'list', 'show',
|
|
22
23
|
'validate', 'build', 'plan', 'deploy', 'dev', 'promote', 'pull', 'rollback',
|
|
23
24
|
];
|
|
24
25
|
const USAGE = `usage:
|
|
25
|
-
seq-studio artifact init <dir>
|
|
26
|
+
seq-studio artifact init <dir> [--template react-vite|nextjs]
|
|
26
27
|
seq-studio artifact whoami --env <env>
|
|
27
28
|
seq-studio artifact link --env <env> [--project <id>]
|
|
28
29
|
seq-studio artifact env list|use <env>
|
|
@@ -33,7 +34,7 @@ const USAGE = `usage:
|
|
|
33
34
|
seq-studio artifact build [dir | --repo <ns>/<name> | --git-url <url>] [--ref <ref>] [--out dist/artifact-bundle.json]
|
|
34
35
|
seq-studio artifact plan [dir | --repo <ns>/<name> | --git-url <url>] [--ref <ref>] --env <env>
|
|
35
36
|
seq-studio artifact deploy [dir | --repo <ns>/<name> | --git-url <url>] [--ref <ref>] --env <env> [--skip-unchanged] [--no-create] [--project <id>]
|
|
36
|
-
seq-studio artifact dev [dir] --env <env> [--once] [--key default] [--link <pkgDir>[,<pkgDir>]]
|
|
37
|
+
seq-studio artifact dev [dir] --env <env> [--once] [--key default] [--link <pkgDir>[,<pkgDir>]] [--port <port>]
|
|
37
38
|
seq-studio artifact promote <deployment-id> --env <env>
|
|
38
39
|
seq-studio artifact pull <project-id> --env <env> --out <dir>
|
|
39
40
|
seq-studio artifact rollback <deployment-id> --env <env>
|
|
@@ -57,6 +58,12 @@ const USAGE = `usage:
|
|
|
57
58
|
seq-studio login are still needed to resolve the repo and upload the
|
|
58
59
|
deployment.)
|
|
59
60
|
|
|
61
|
+
Next.js: init --template nextjs, then pnpm install with Node.js 24. dev starts
|
|
62
|
+
a local Next server and an owner-only Atlas preview tunnel (no cloud credentials).
|
|
63
|
+
build --env creates an immutable hosted preview; deploy builds and publishes
|
|
64
|
+
a reviewed Git Service revision through Atlas. For local build checks, use pnpm build.
|
|
65
|
+
--once applies to static artifacts; --port applies to Next development.
|
|
66
|
+
|
|
60
67
|
Authenticate with: seq-studio login`;
|
|
61
68
|
export async function runCli(argv = process.argv.slice(2)) {
|
|
62
69
|
if (argv.some((arg) => arg === '--token' || arg.startsWith('--token='))) {
|
|
@@ -118,17 +125,30 @@ async function initCommand(args) {
|
|
|
118
125
|
const dir = resolve(args.positional[0] ?? '.');
|
|
119
126
|
const name = dir.split('/').filter(Boolean).at(-1) ?? 'my-artifact';
|
|
120
127
|
const slug = slugify(name);
|
|
128
|
+
const template = args.flags.template ?? 'react-vite';
|
|
129
|
+
if (template !== 'react-vite' && template !== 'nextjs') {
|
|
130
|
+
throw new Error('--template must be react-vite or nextjs.');
|
|
131
|
+
}
|
|
121
132
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
122
|
-
const templateRoot = existsSync(resolve(moduleDir, 'templates',
|
|
123
|
-
? resolve(moduleDir, 'templates',
|
|
124
|
-
: resolve(moduleDir, '..', 'templates',
|
|
133
|
+
const templateRoot = existsSync(resolve(moduleDir, 'templates', template))
|
|
134
|
+
? resolve(moduleDir, 'templates', template)
|
|
135
|
+
: resolve(moduleDir, '..', 'templates', template);
|
|
125
136
|
await mkdir(dir, { recursive: true });
|
|
126
137
|
await cp(templateRoot, dir, { recursive: true, force: false, errorOnExist: false });
|
|
127
138
|
await replaceInFile(join(dir, 'artifact.bundle.yml'), /\{\{slug\}\}/g, slug);
|
|
128
139
|
await replaceInFile(join(dir, 'artifact.bundle.yml'), /\{\{title\}\}/g, titleize(slug));
|
|
129
140
|
await replaceInFile(join(dir, 'package.json'), /\{\{slug\}\}/g, slug);
|
|
141
|
+
if (template === 'nextjs') {
|
|
142
|
+
const peers = resolveArtifactStudioPeerVersions();
|
|
143
|
+
await replaceInFile(join(dir, 'package.json'), /\{\{artifactStudioVersion\}\}/g, peers.cliVersion);
|
|
144
|
+
for (const file of ['app/layout.tsx', 'app/page.tsx']) {
|
|
145
|
+
await replaceInFile(join(dir, file), /\{\{title\}\}/g, titleize(slug));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
130
148
|
console.log(`[seq-studio] initialized ${dir}`);
|
|
131
|
-
console.log(
|
|
149
|
+
console.log(template === 'nextjs'
|
|
150
|
+
? 'Next: cd into the app, run pnpm install, then seq-studio login && seq-studio artifact dev --env <env>. Commit pnpm-lock.yaml before deploying. No cloud credentials are required.'
|
|
151
|
+
: 'Next: seq-studio login && seq-studio artifact dev --env <env> (see: seq-studio envs list)');
|
|
132
152
|
return 0;
|
|
133
153
|
}
|
|
134
154
|
async function whoamiCommand(args) {
|
|
@@ -184,10 +204,12 @@ async function linkCommand(args) {
|
|
|
184
204
|
title: source.manifest.artifact.title,
|
|
185
205
|
slug: source.manifest.artifact.project_id,
|
|
186
206
|
description: source.manifest.artifact.description ?? null,
|
|
207
|
+
...(isNextArtifactManifest(source.manifest) ? { projectType: 'nextjs' } : {}),
|
|
187
208
|
},
|
|
188
209
|
});
|
|
189
210
|
project = created.project;
|
|
190
211
|
}
|
|
212
|
+
assertProjectRuntime({ project, artifactType: source.manifest.artifact.type, declaredSlug: source.manifest.artifact.project_id });
|
|
191
213
|
await writeLocalConfig({
|
|
192
214
|
projectId: project.id,
|
|
193
215
|
projectSlug: project.slug,
|
|
@@ -254,8 +276,7 @@ async function listCommand(args) {
|
|
|
254
276
|
: project.activeDeploymentId
|
|
255
277
|
? 'deployed'
|
|
256
278
|
: 'not deployed';
|
|
257
|
-
|
|
258
|
-
console.log(` ${project.slug} id=${project.id} [${active}]${visibility} ${project.title}`);
|
|
279
|
+
console.log(` ${project.slug} id=${project.id} [${active}] ${project.title}`);
|
|
259
280
|
}
|
|
260
281
|
return 0;
|
|
261
282
|
}
|
|
@@ -300,8 +321,6 @@ async function showCommand(args) {
|
|
|
300
321
|
console.log(` title: ${project.title}`);
|
|
301
322
|
if (project.description)
|
|
302
323
|
console.log(` description: ${project.description}`);
|
|
303
|
-
if (project.visibility)
|
|
304
|
-
console.log(` visibility: ${project.visibility}`);
|
|
305
324
|
if (project.appEligible !== undefined)
|
|
306
325
|
console.log(` appEligible: ${project.appEligible ? 'yes' : 'no'}`);
|
|
307
326
|
if (project.createdBy)
|
|
@@ -346,6 +365,12 @@ async function validateCommand(args) {
|
|
|
346
365
|
console.log(`[seq-studio] valid ${source.manifest.bundle.name}`);
|
|
347
366
|
console.log(`[seq-studio] files: ${source.files.length}`);
|
|
348
367
|
console.log(`[seq-studio] source: ${source.sourceHash}`);
|
|
368
|
+
if (isNextArtifactManifest(source.manifest)) {
|
|
369
|
+
const { validateNextProject } = await import('./next-project.js');
|
|
370
|
+
await validateNextProject({ root: dir, manifest: source.manifest, installed: false });
|
|
371
|
+
console.log('[seq-studio] Next.js source is valid; Atlas performs the release build and hosted policy checks.');
|
|
372
|
+
return 0;
|
|
373
|
+
}
|
|
349
374
|
reportSandboxWarnings(lintArtifactSandbox(source.files));
|
|
350
375
|
return 0;
|
|
351
376
|
}
|
|
@@ -375,6 +400,9 @@ async function buildCommand(args) {
|
|
|
375
400
|
: {};
|
|
376
401
|
const source = await resolveArtifactSource(spec, remote);
|
|
377
402
|
try {
|
|
403
|
+
if (isNextArtifactManifest(await readArtifactStudioManifest(source.dir))) {
|
|
404
|
+
return await buildNextCommand({ args, source, active: false });
|
|
405
|
+
}
|
|
378
406
|
const result = await buildPreparedArtifactSource(source);
|
|
379
407
|
reportBuildDiagnostics(result);
|
|
380
408
|
// A remote source's dir is a temp dir we tear down below, so default the
|
|
@@ -405,6 +433,11 @@ async function planCommand(args) {
|
|
|
405
433
|
token,
|
|
406
434
|
});
|
|
407
435
|
try {
|
|
436
|
+
const manifest = await readArtifactStudioManifest(source.dir);
|
|
437
|
+
if (isNextArtifactManifest(manifest)) {
|
|
438
|
+
const { planNextProject } = await import('./next-build.js');
|
|
439
|
+
return await planNextProject({ root: source.dir, manifest, environment: env.name });
|
|
440
|
+
}
|
|
408
441
|
const result = await buildPreparedArtifactSource(source);
|
|
409
442
|
reportBuildDiagnostics(result);
|
|
410
443
|
console.log(`[seq-studio] plan ${result.manifest.artifact.project_id} -> ${env.name}`);
|
|
@@ -461,6 +494,9 @@ async function deployCommand(args) {
|
|
|
461
494
|
// the current branch and dirty working tree.
|
|
462
495
|
if (env.name !== 'local')
|
|
463
496
|
assertActiveDeployProvenance(source.provenance);
|
|
497
|
+
if (isNextArtifactManifest(await readArtifactStudioManifest(source.dir))) {
|
|
498
|
+
return await buildNextCommand({ args, source, active: true });
|
|
499
|
+
}
|
|
464
500
|
// --skip-unchanged: hash the raw source (no build needed) and no-op when
|
|
465
501
|
// the remote active deployment already matches *and* was built with the
|
|
466
502
|
// same CLI/atlas-ui pin. Lets CI redeploy every artifact on every release
|
|
@@ -499,7 +535,6 @@ async function deployCommand(args) {
|
|
|
499
535
|
linkConfigDir: spec.kind === 'local' ? source.dir : null,
|
|
500
536
|
allowCreate: args.flags['no-create'] !== true,
|
|
501
537
|
explicitProjectId,
|
|
502
|
-
envName: env.name,
|
|
503
538
|
});
|
|
504
539
|
const deployment = await uploadDeployment({ env: env.url, token, projectId: project.id, result, channel: 'active', provenance: source.provenance });
|
|
505
540
|
console.log(`[seq-studio] deployed ${project.title} ${deployment.version}`);
|
|
@@ -510,12 +545,56 @@ async function deployCommand(args) {
|
|
|
510
545
|
await source.cleanup();
|
|
511
546
|
}
|
|
512
547
|
}
|
|
548
|
+
async function buildNextCommand({ args, source, active }) {
|
|
549
|
+
const env = await envFromArgs(args);
|
|
550
|
+
const token = await getRequiredToken(args);
|
|
551
|
+
const manifest = await readArtifactStudioManifest(source.dir);
|
|
552
|
+
if (!isNextArtifactManifest(manifest))
|
|
553
|
+
throw new Error('Expected a Next.js application manifest.');
|
|
554
|
+
const { nextSourceRepository, resolveNextRepositoryId, submitNextBuild } = await import('./next-build.js');
|
|
555
|
+
const spec = parseSourceSpec(args);
|
|
556
|
+
const repository = nextSourceRepository({ spec, root: source.dir });
|
|
557
|
+
if (active)
|
|
558
|
+
assertActiveDeployProvenance(source.provenance);
|
|
559
|
+
const repositoryId = active || spec.kind === 'git-service'
|
|
560
|
+
? await resolveNextRepositoryId({ spec, repository, baseUrl: env.url, token })
|
|
561
|
+
: undefined;
|
|
562
|
+
const project = await ensureRemoteProject({
|
|
563
|
+
env: env.url, token, result: { manifest }, linkConfigDir: spec.kind === 'local' ? source.dir : null,
|
|
564
|
+
allowCreate: args.flags['no-create'] !== true,
|
|
565
|
+
explicitProjectId: typeof args.flags.project === 'string' ? args.flags.project : null,
|
|
566
|
+
});
|
|
567
|
+
if (args.flags['skip-unchanged']) {
|
|
568
|
+
console.log('[seq-studio] Next.js submits source to Atlas for build verification; local static-bundle skip checks do not apply.');
|
|
569
|
+
}
|
|
570
|
+
return submitNextBuild({
|
|
571
|
+
root: source.dir, manifest, projectId: project.id, baseUrl: env.url,
|
|
572
|
+
getToken: () => getRequiredToken(args), provenance: source.provenance, repository, repositoryId,
|
|
573
|
+
channel: active ? 'active' : 'preview', out: typeof args.flags.out === 'string' ? resolve(args.flags.out) : undefined,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
513
576
|
async function devCommand(args) {
|
|
514
577
|
if (args.flags.repo || args.flags['git-url']) {
|
|
515
578
|
throw new Error('dev watches a local folder; --repo / --git-url are only for build, plan, and deploy.');
|
|
516
579
|
}
|
|
517
580
|
const dir = resolve(args.positional[0] ?? '.');
|
|
518
581
|
const env = await envFromArgs(args);
|
|
582
|
+
const initialManifest = await readArtifactStudioManifest(dir);
|
|
583
|
+
if (isNextArtifactManifest(initialManifest)) {
|
|
584
|
+
if (args.flags.once)
|
|
585
|
+
throw new Error('Next.js dev is a live local session. Use artifact build --env <env> for an immutable hosted preview.');
|
|
586
|
+
const auth = await getRequiredAuth(args);
|
|
587
|
+
if (auth.authMode !== 'user')
|
|
588
|
+
throw new Error('Local Next.js development requires a user login. Run seq-studio login.');
|
|
589
|
+
const project = await ensureRemoteProject({ env: env.url, token: auth.token, result: { manifest: initialManifest }, linkConfigDir: dir });
|
|
590
|
+
const { runNextDevelopment } = await import('./next-dev.js');
|
|
591
|
+
return runNextDevelopment({
|
|
592
|
+
root: dir, manifest: initialManifest, projectId: project.id, baseUrl: env.url,
|
|
593
|
+
environment: env.name, previewKey: typeof args.flags.key === 'string' ? args.flags.key : 'default',
|
|
594
|
+
port: typeof args.flags.port === 'string' ? args.flags.port : undefined,
|
|
595
|
+
links: parseLinks(args.flags.link), getToken: () => getRequiredToken(args),
|
|
596
|
+
});
|
|
597
|
+
}
|
|
519
598
|
// Validate auth up front for a fast failure, but resolve a fresh token on
|
|
520
599
|
// every push (below) so a long-running watcher survives access-token expiry
|
|
521
600
|
// instead of dying with "Authentication failed" once the initial token's TTL
|
|
@@ -589,9 +668,7 @@ async function devCommand(args) {
|
|
|
589
668
|
// Resolve per push so the token refreshes across a long watch session.
|
|
590
669
|
const token = await getRequiredToken(args);
|
|
591
670
|
if (!projectId) {
|
|
592
|
-
|
|
593
|
-
// visibility too (and notes drift, once, when the project pre-exists).
|
|
594
|
-
const project = await ensureRemoteProject({ env: env.url, token, result, linkConfigDir: dir, envName: env.name });
|
|
671
|
+
const project = await ensureRemoteProject({ env: env.url, token, result, linkConfigDir: dir });
|
|
595
672
|
projectId = project.id;
|
|
596
673
|
}
|
|
597
674
|
const deployment = await uploadDeployment({
|
|
@@ -731,9 +808,9 @@ async function pullCommand(args) {
|
|
|
731
808
|
path: `/api/artifact-studio/projects/${projectId}/deployments/${projectData.project.activeDeploymentId}/files`,
|
|
732
809
|
});
|
|
733
810
|
for (const file of filesData.files) {
|
|
734
|
-
const target = join(out, file.path);
|
|
811
|
+
const target = join(out, normalizeArtifactPath(file.path));
|
|
735
812
|
await mkdir(dirname(target), { recursive: true });
|
|
736
|
-
await writeFile(target, file.content, '
|
|
813
|
+
await writeFile(target, file.encoding === 'base64' ? Buffer.from(file.content, 'base64') : file.content);
|
|
737
814
|
}
|
|
738
815
|
console.log(`[seq-studio] pulled ${projectId} to ${out}`);
|
|
739
816
|
return 0;
|
|
@@ -771,21 +848,7 @@ export function activeDeploymentMatchesSkipPin({ active, sourceHash, peerVersion
|
|
|
771
848
|
return (active.cliVersion === peerVersions.cliVersion &&
|
|
772
849
|
active.atlasUiVersion === peerVersions.atlasUiVersion);
|
|
773
850
|
}
|
|
774
|
-
|
|
775
|
-
* Deploys never change a live project's visibility — the Studio sharing UI /
|
|
776
|
-
* PATCH own that. The manifest's targets.<env>.visibility is honored at
|
|
777
|
-
* project CREATION only; on drift, say so instead of silently ignoring the
|
|
778
|
-
* field (which is how it read as decorative for a year).
|
|
779
|
-
*/
|
|
780
|
-
function warnOnVisibilityDrift({ project, declared, envName }) {
|
|
781
|
-
if (!declared || !envName || !project.visibility || declared === project.visibility)
|
|
782
|
-
return;
|
|
783
|
-
console.warn(`[seq-studio] note: artifact.bundle.yml targets.${envName}.visibility is "${declared}" but the project is ` +
|
|
784
|
-
`"${project.visibility}" on this environment — deploys never change visibility. Update it in Studio sharing ` +
|
|
785
|
-
`(or PATCH /api/artifact-studio/projects/${project.id}) if the manifest is what you intend.`);
|
|
786
|
-
}
|
|
787
|
-
async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCreate = true, explicitProjectId = null, envName = null }) {
|
|
788
|
-
const declaredVisibility = envName ? result.manifest.targets?.[envName]?.visibility : undefined;
|
|
851
|
+
async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCreate = true, explicitProjectId = null }) {
|
|
789
852
|
if (explicitProjectId) {
|
|
790
853
|
const data = await getJsonOr404({ baseUrl: env, token, path: `/api/artifact-studio/projects/${explicitProjectId}` });
|
|
791
854
|
if (!data)
|
|
@@ -794,23 +857,23 @@ async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCre
|
|
|
794
857
|
throw new Error(`--project ${explicitProjectId} resolves to slug "${data.project.slug}" but this source's ` +
|
|
795
858
|
`artifact.bundle.yml declares project_id "${result.manifest.artifact.project_id}" — refusing to deploy over a different project`);
|
|
796
859
|
}
|
|
797
|
-
|
|
860
|
+
assertProjectRuntime({ project: data.project, artifactType: result.manifest.artifact.type, declaredSlug: result.manifest.artifact.project_id });
|
|
798
861
|
return data.project;
|
|
799
862
|
}
|
|
800
863
|
const config = linkConfigDir ? await readLocalConfig(linkConfigDir) : {};
|
|
801
864
|
if (config.projectId) {
|
|
802
865
|
const data = await getJsonOr404({ baseUrl: env, token, path: `/api/artifact-studio/projects/${config.projectId}` });
|
|
803
866
|
if (data) {
|
|
804
|
-
|
|
867
|
+
assertProjectRuntime({ project: data.project, artifactType: result.manifest.artifact.type, declaredSlug: result.manifest.artifact.project_id });
|
|
805
868
|
return data.project;
|
|
806
869
|
}
|
|
807
870
|
console.warn(`[seq-studio] cached projectId ${config.projectId} not found on ${env} — relinking by slug`);
|
|
808
871
|
}
|
|
809
872
|
const existing = await fetchRemoteProject({ env, token, slug: result.manifest.artifact.project_id });
|
|
810
873
|
if (existing) {
|
|
874
|
+
assertProjectRuntime({ project: existing, artifactType: result.manifest.artifact.type, declaredSlug: result.manifest.artifact.project_id });
|
|
811
875
|
if (linkConfigDir)
|
|
812
876
|
await writeLocalConfig({ ...config, projectId: existing.id, projectSlug: existing.slug }, linkConfigDir);
|
|
813
|
-
warnOnVisibilityDrift({ project: existing, declared: declaredVisibility, envName });
|
|
814
877
|
return existing;
|
|
815
878
|
}
|
|
816
879
|
if (!allowCreate) {
|
|
@@ -826,13 +889,21 @@ async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCre
|
|
|
826
889
|
title: result.manifest.artifact.title,
|
|
827
890
|
slug: result.manifest.artifact.project_id,
|
|
828
891
|
description: result.manifest.artifact.description ?? null,
|
|
829
|
-
...(
|
|
892
|
+
...(result.manifest.artifact.type === 'nextjs' ? { projectType: 'nextjs' } : {}),
|
|
830
893
|
},
|
|
831
894
|
});
|
|
832
895
|
if (linkConfigDir)
|
|
833
896
|
await writeLocalConfig({ ...config, projectId: created.project.id, projectSlug: created.project.slug }, linkConfigDir);
|
|
834
897
|
return created.project;
|
|
835
898
|
}
|
|
899
|
+
function assertProjectRuntime({ project, artifactType, declaredSlug }) {
|
|
900
|
+
if (artifactType === 'nextjs' && project.slug !== declaredSlug) {
|
|
901
|
+
throw new Error(`Linked project ${project.slug} does not match artifact.bundle.yml project_id ${declaredSlug}. Relink to the intended project.`);
|
|
902
|
+
}
|
|
903
|
+
if (artifactType === 'nextjs' && project.projectType && project.projectType !== 'nextjs') {
|
|
904
|
+
throw new Error(`Project ${project.slug} uses the static runtime. Create a separate Next.js project in Atlas and update artifact.bundle.yml project_id before linking.`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
836
907
|
async function uploadDeployment({ env, token, projectId, result, channel, previewKey, linked = false, provenance, message, }) {
|
|
837
908
|
// Active deploys omit `version`: the server derives it from its own
|
|
838
909
|
// sourceHash and de-dupes against the (projectId, version) unique
|
package/dist/dev-link.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export declare function resolveLinkedDeps({ links, cwd, resolver, readPackageNam
|
|
|
53
53
|
*/
|
|
54
54
|
export declare function hashLinkedSources(deps: readonly LinkedDep[]): Promise<string>;
|
|
55
55
|
/** Run a linked package's `build` script in a child process. */
|
|
56
|
-
export declare function rebuildLinkedDep(dep: LinkedDep, signal?: AbortSignal): Promise<void>;
|
|
56
|
+
export declare function rebuildLinkedDep(dep: LinkedDep, signal?: AbortSignal, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
57
57
|
/**
|
|
58
58
|
* Copy a linked package's freshly built `dist` and root CSS entries into the
|
|
59
59
|
* location the artifact build resolves them from. A no-op when the artifact
|
package/dist/dev-link.js
CHANGED
|
@@ -135,7 +135,7 @@ async function collectFiles(root) {
|
|
|
135
135
|
return out;
|
|
136
136
|
}
|
|
137
137
|
/** Run a linked package's `build` script in a child process. */
|
|
138
|
-
export function rebuildLinkedDep(dep, signal) {
|
|
138
|
+
export function rebuildLinkedDep(dep, signal, env) {
|
|
139
139
|
return new Promise((resolveBuild, reject) => {
|
|
140
140
|
if (signal?.aborted) {
|
|
141
141
|
reject(new Error('build aborted'));
|
|
@@ -143,6 +143,7 @@ export function rebuildLinkedDep(dep, signal) {
|
|
|
143
143
|
}
|
|
144
144
|
const child = spawn('pnpm', ['run', 'build'], {
|
|
145
145
|
cwd: dep.dir,
|
|
146
|
+
...(env ? { env } : {}),
|
|
146
147
|
stdio: ['ignore', 'inherit', 'inherit'],
|
|
147
148
|
});
|
|
148
149
|
const onAbort = () => {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { EventEmitter } from 'node:events';
|
|
2
|
+
export interface AdmissionRedis {
|
|
3
|
+
readonly options: {
|
|
4
|
+
enableOfflineQueue?: boolean;
|
|
5
|
+
autoResendUnfulfilledCommands?: boolean;
|
|
6
|
+
maxRetriesPerRequest?: number | null;
|
|
7
|
+
};
|
|
8
|
+
eval(script: string, numberOfKeys: number, ...parameters: (string | number)[]): Promise<unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface AdmissionTarget {
|
|
11
|
+
environment: string;
|
|
12
|
+
projectId: string;
|
|
13
|
+
deploymentId: string;
|
|
14
|
+
resources?: {
|
|
15
|
+
concurrency?: number;
|
|
16
|
+
max_instances?: number;
|
|
17
|
+
timeout_seconds?: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
type ResponseLifecycle = Pick<EventEmitter, 'once' | 'off'> & {
|
|
21
|
+
writableFinished?: boolean;
|
|
22
|
+
destroyed?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export interface AdmissionLease {
|
|
25
|
+
requestId: string;
|
|
26
|
+
/** Redis-server deadline, for tracing; local cancellation uses elapsed time. */
|
|
27
|
+
deadline: number;
|
|
28
|
+
timeoutMs: number;
|
|
29
|
+
release(): Promise<void>;
|
|
30
|
+
/** Register immediately after admission, before starting upstream work.
|
|
31
|
+
* onTimeout must synchronously abort the upstream before the permit is freed. */
|
|
32
|
+
bind(response: ResponseLifecycle, onTimeout: () => void): () => void;
|
|
33
|
+
}
|
|
34
|
+
export declare class ArtifactAdmissionError extends Error {
|
|
35
|
+
readonly status: 429 | 503;
|
|
36
|
+
readonly code: 'capacity_exceeded' | 'admission_unavailable';
|
|
37
|
+
constructor(status: 429 | 503, code: 'capacity_exceeded' | 'admission_unavailable');
|
|
38
|
+
}
|
|
39
|
+
/** Fail-closed admission with no command retries. An uncertain acquisition is
|
|
40
|
+
* never used; its unique permit expires even if Redis accepted it before loss. */
|
|
41
|
+
export declare class ArtifactRequestAdmission {
|
|
42
|
+
private readonly options;
|
|
43
|
+
private readonly namespace;
|
|
44
|
+
private readonly ceiling;
|
|
45
|
+
constructor(options: {
|
|
46
|
+
redis: AdmissionRedis;
|
|
47
|
+
namespace: string;
|
|
48
|
+
platformCeiling?: number;
|
|
49
|
+
onError?: (event: {
|
|
50
|
+
kind: 'admission_release_failed' | 'admission_timeout_callback_failed';
|
|
51
|
+
}) => void;
|
|
52
|
+
});
|
|
53
|
+
acquire(target: AdmissionTarget): Promise<AdmissionLease>;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { performance } from 'node:perf_hooks';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { artifactNextResourceLimitsSchema } from '../manifest-next.js';
|
|
5
|
+
import { redisNamespaceSchema } from '../tunnel/redis-ownership.js';
|
|
6
|
+
export class ArtifactAdmissionError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
code;
|
|
9
|
+
constructor(status, code) {
|
|
10
|
+
super(status === 429 ? 'Application request capacity exceeded' : 'Application request admission unavailable');
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.name = 'ArtifactAdmissionError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// The tunnel's APP_ENV namespace format, so both Redis key spaces accept the same deployment identities.
|
|
17
|
+
const namespace = redisNamespaceSchema;
|
|
18
|
+
const identifier = z.string().min(1).max(128).regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/);
|
|
19
|
+
const resourceFields = artifactNextResourceLimitsSchema.unwrap().shape;
|
|
20
|
+
const resources = z.object({
|
|
21
|
+
concurrency: resourceFields.concurrency,
|
|
22
|
+
max_instances: resourceFields.max_instances,
|
|
23
|
+
timeout_seconds: resourceFields.timeout_seconds,
|
|
24
|
+
});
|
|
25
|
+
// Both keys have the same Redis Cluster hash tag. A lease can never exist in
|
|
26
|
+
// only one set, and previews/retained releases consume the same application cap.
|
|
27
|
+
const ACQUIRE = `-- artifact-request-admission-acquire-v1
|
|
28
|
+
local time = redis.call('TIME')
|
|
29
|
+
local now = tonumber(time[1]) * 1000 + math.floor(tonumber(time[2]) / 1000)
|
|
30
|
+
local maximum = tonumber(ARGV[2])
|
|
31
|
+
local deadline = now + tonumber(ARGV[3])
|
|
32
|
+
for _, key in ipairs(KEYS) do
|
|
33
|
+
redis.call('ZREMRANGEBYSCORE', key, '-inf', now)
|
|
34
|
+
if redis.call('ZCARD', key) >= maximum then return {0, now} end
|
|
35
|
+
end
|
|
36
|
+
for _, key in ipairs(KEYS) do
|
|
37
|
+
redis.call('ZADD', key, deadline, ARGV[1])
|
|
38
|
+
local latest = redis.call('ZREVRANGE', key, 0, 0, 'WITHSCORES')
|
|
39
|
+
redis.call('PEXPIRE', key, tonumber(latest[2]) - now + 1000)
|
|
40
|
+
end
|
|
41
|
+
return {1, deadline}`;
|
|
42
|
+
const RELEASE = `-- artifact-request-admission-release-v1
|
|
43
|
+
for _, key in ipairs(KEYS) do
|
|
44
|
+
redis.call('ZREM', key, ARGV[1])
|
|
45
|
+
if redis.call('ZCARD', key) == 0 then redis.call('DEL', key) end
|
|
46
|
+
end
|
|
47
|
+
return 1`;
|
|
48
|
+
/** Fail-closed admission with no command retries. An uncertain acquisition is
|
|
49
|
+
* never used; its unique permit expires even if Redis accepted it before loss. */
|
|
50
|
+
export class ArtifactRequestAdmission {
|
|
51
|
+
options;
|
|
52
|
+
namespace;
|
|
53
|
+
ceiling;
|
|
54
|
+
constructor(options) {
|
|
55
|
+
this.options = options;
|
|
56
|
+
this.namespace = namespace.parse(options.namespace);
|
|
57
|
+
this.ceiling = z.number().int().min(1).max(256).parse(options.platformCeiling ?? 256);
|
|
58
|
+
const policy = options.redis.options;
|
|
59
|
+
if (policy.enableOfflineQueue !== false || policy.autoResendUnfulfilledCommands !== false || policy.maxRetriesPerRequest !== 0) {
|
|
60
|
+
throw new Error('Admission Redis must disable offline queues, command replay and retries');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async acquire(target) {
|
|
64
|
+
if (namespace.parse(target.environment) !== this.namespace)
|
|
65
|
+
throw new Error('Admission environment mismatch');
|
|
66
|
+
const projectId = identifier.parse(target.projectId), deploymentId = identifier.parse(target.deploymentId);
|
|
67
|
+
const limits = resources.parse(target.resources ?? {});
|
|
68
|
+
const maximum = Math.min(limits.concurrency * limits.max_instances, this.ceiling);
|
|
69
|
+
const timeoutMs = limits.timeout_seconds * 1000;
|
|
70
|
+
const requestId = randomUUID();
|
|
71
|
+
const prefix = `artifact-admission:${this.namespace}:{${this.namespace}:${projectId}}`;
|
|
72
|
+
const keys = [`${prefix}:app`, `${prefix}:deployment:${deploymentId}`];
|
|
73
|
+
const began = performance.now();
|
|
74
|
+
let result;
|
|
75
|
+
try {
|
|
76
|
+
result = z.tuple([z.number().int().min(0).max(1), z.number().int().positive()]).parse(await this.options.redis.eval(ACQUIRE, 2, ...keys, requestId, maximum, timeoutMs));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new ArtifactAdmissionError(503, 'admission_unavailable');
|
|
80
|
+
}
|
|
81
|
+
if (result[0] === 0)
|
|
82
|
+
throw new ArtifactAdmissionError(429, 'capacity_exceeded');
|
|
83
|
+
// Never start work under an admission whose local request budget already
|
|
84
|
+
// elapsed while Redis was unavailable or stalled. Redis TTL reclaims it.
|
|
85
|
+
if (performance.now() - began >= timeoutMs)
|
|
86
|
+
throw new ArtifactAdmissionError(503, 'admission_unavailable');
|
|
87
|
+
let releasePromise;
|
|
88
|
+
let cleanup;
|
|
89
|
+
let bound = false;
|
|
90
|
+
const release = () => {
|
|
91
|
+
cleanup?.();
|
|
92
|
+
// Repeated finish/close/error notifications must never free another
|
|
93
|
+
// request's permit, nor cause a failed command to be retried.
|
|
94
|
+
releasePromise ??= this.options.redis.eval(RELEASE, 2, ...keys, requestId).then(() => undefined, () => { throw new ArtifactAdmissionError(503, 'admission_unavailable'); });
|
|
95
|
+
return releasePromise;
|
|
96
|
+
};
|
|
97
|
+
const releaseQuietly = () => {
|
|
98
|
+
void release().catch(() => this.options.onError?.({ kind: 'admission_release_failed' }));
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
requestId, deadline: result[1], timeoutMs, release,
|
|
102
|
+
bind: (response, onTimeout) => {
|
|
103
|
+
if (bound || releasePromise)
|
|
104
|
+
throw new Error('Admission lease already bound or released');
|
|
105
|
+
bound = true;
|
|
106
|
+
const timer = setTimeout(() => {
|
|
107
|
+
try {
|
|
108
|
+
onTimeout();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
this.options.onError?.({ kind: 'admission_timeout_callback_failed' });
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
releaseQuietly();
|
|
115
|
+
}
|
|
116
|
+
}, Math.max(0, timeoutMs - (performance.now() - began)));
|
|
117
|
+
timer.unref();
|
|
118
|
+
cleanup = () => {
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
response.off('finish', releaseQuietly);
|
|
121
|
+
response.off('close', releaseQuietly);
|
|
122
|
+
};
|
|
123
|
+
response.once('finish', releaseQuietly);
|
|
124
|
+
response.once('close', releaseQuietly);
|
|
125
|
+
if (response.writableFinished || response.destroyed)
|
|
126
|
+
releaseQuietly();
|
|
127
|
+
// Detaching also releases; callers cannot strand an admitted permit.
|
|
128
|
+
return releaseQuietly;
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { TunnelLease } from '../tunnel/index.js';
|
|
3
|
+
import type { GatewaySession } from './session.js';
|
|
4
|
+
import type { GatewayUsageEvent } from './usage.js';
|
|
5
|
+
export declare const gatewayTargetSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
6
|
+
kind: z.ZodLiteral<"development">;
|
|
7
|
+
sessionId: z.ZodString;
|
|
8
|
+
epoch: z.ZodNumber;
|
|
9
|
+
projectId: z.ZodString;
|
|
10
|
+
ownerId: z.ZodString;
|
|
11
|
+
environment: z.ZodString;
|
|
12
|
+
publicOrigin: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
13
|
+
expiresAt: z.ZodNumber;
|
|
14
|
+
maxExpiresAt: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
capabilities: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
16
|
+
serviceAccountId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
17
|
+
lattice: z.ZodOptional<z.ZodObject<{
|
|
18
|
+
runId: z.ZodNullable<z.ZodString>;
|
|
19
|
+
nodeId: z.ZodNullable<z.ZodString>;
|
|
20
|
+
stepId: z.ZodNullable<z.ZodString>;
|
|
21
|
+
processId: z.ZodNullable<z.ZodString>;
|
|
22
|
+
}, z.core.$strict>>;
|
|
23
|
+
taskRoutes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
24
|
+
manifest: z.ZodOptional<z.ZodObject<{
|
|
25
|
+
server: z.ZodObject<{
|
|
26
|
+
resources: z.ZodObject<{
|
|
27
|
+
concurrency: z.ZodNumber;
|
|
28
|
+
max_instances: z.ZodNumber;
|
|
29
|
+
timeout_seconds: z.ZodNumber;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
}, z.core.$strip>>;
|
|
33
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
34
|
+
kind: z.ZodLiteral<"hosted">;
|
|
35
|
+
deploymentId: z.ZodString;
|
|
36
|
+
frameworkBuildId: z.ZodString;
|
|
37
|
+
upstreamUrl: z.ZodString;
|
|
38
|
+
upstreamAudience: z.ZodOptional<z.ZodString>;
|
|
39
|
+
active: z.ZodBoolean;
|
|
40
|
+
published: z.ZodOptional<z.ZodBoolean>;
|
|
41
|
+
projectId: z.ZodString;
|
|
42
|
+
ownerId: z.ZodString;
|
|
43
|
+
environment: z.ZodString;
|
|
44
|
+
publicOrigin: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
45
|
+
expiresAt: z.ZodNumber;
|
|
46
|
+
maxExpiresAt: z.ZodOptional<z.ZodNumber>;
|
|
47
|
+
capabilities: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
48
|
+
serviceAccountId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
49
|
+
lattice: z.ZodOptional<z.ZodObject<{
|
|
50
|
+
runId: z.ZodNullable<z.ZodString>;
|
|
51
|
+
nodeId: z.ZodNullable<z.ZodString>;
|
|
52
|
+
stepId: z.ZodNullable<z.ZodString>;
|
|
53
|
+
processId: z.ZodNullable<z.ZodString>;
|
|
54
|
+
}, z.core.$strict>>;
|
|
55
|
+
taskRoutes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
56
|
+
manifest: z.ZodOptional<z.ZodObject<{
|
|
57
|
+
server: z.ZodObject<{
|
|
58
|
+
resources: z.ZodObject<{
|
|
59
|
+
concurrency: z.ZodNumber;
|
|
60
|
+
max_instances: z.ZodNumber;
|
|
61
|
+
timeout_seconds: z.ZodNumber;
|
|
62
|
+
}, z.core.$strip>;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
}, z.core.$strip>>;
|
|
65
|
+
}, z.core.$strip>], "kind">;
|
|
66
|
+
export type GatewayTarget = z.infer<typeof gatewayTargetSchema>;
|
|
67
|
+
export declare class GatewayControlError extends Error {
|
|
68
|
+
readonly status: number;
|
|
69
|
+
constructor(status: number);
|
|
70
|
+
}
|
|
71
|
+
export declare class ArtifactGatewayControl {
|
|
72
|
+
private readonly options;
|
|
73
|
+
private readonly origin;
|
|
74
|
+
constructor(options: {
|
|
75
|
+
url: string;
|
|
76
|
+
token: string;
|
|
77
|
+
fetch?: typeof fetch;
|
|
78
|
+
});
|
|
79
|
+
private call;
|
|
80
|
+
usage(event: GatewayUsageEvent): Promise<void>;
|
|
81
|
+
bootstrap(ticket: string): Promise<{
|
|
82
|
+
session: GatewaySession;
|
|
83
|
+
target: GatewayTarget;
|
|
84
|
+
}>;
|
|
85
|
+
authorize(session: GatewaySession): Promise<GatewayTarget>;
|
|
86
|
+
tunnel({ token, sessionId, epoch }: {
|
|
87
|
+
token: string;
|
|
88
|
+
sessionId: string;
|
|
89
|
+
epoch: number;
|
|
90
|
+
}): Promise<TunnelLease>;
|
|
91
|
+
}
|