@domino-sdk/relay-cli 0.4.0 → 0.5.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/cli/commands/discovery.mjs +23 -4
- package/cli/commands/project.mjs +3 -1
- package/cli/commands/staging.mjs +2 -2
- package/cli/connected-dev.mjs +4 -2
- package/cli/doctor.mjs +17 -2
- package/cli/git.mjs +38 -7
- package/cli/package-manager.mjs +31 -0
- package/cli/project.mjs +16 -12
- package/cli/runtime.mjs +8 -1
- package/cli/skills/domino/SKILL.md +3 -3
- package/cli/skills/domino/references/authoring.md +11 -1
- package/cli/skills/domino/references/hosted.md +3 -3
- package/cli/skills/domino/references/participant-api.md +10 -2
- package/cli/skills/domino/references/troubleshooting.md +18 -0
- package/cli.mjs +4 -1
- package/dist/index.d.ts +82 -1
- package/dist/index.js +90 -4
- package/package.json +2 -2
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { action } from "../runtime.mjs";
|
|
3
3
|
|
|
4
|
-
const references = [
|
|
4
|
+
const references = [
|
|
5
|
+
"authoring",
|
|
6
|
+
"participant-api",
|
|
7
|
+
"referrals",
|
|
8
|
+
"troubleshooting",
|
|
9
|
+
];
|
|
5
10
|
export function registerDiscoveryCommands(program) {
|
|
6
11
|
program
|
|
7
12
|
.command("capabilities")
|
|
8
|
-
.description(
|
|
13
|
+
.description(
|
|
14
|
+
"Read this CLI release's bundled capability catalog (not a server probe)",
|
|
15
|
+
)
|
|
9
16
|
.argument("[id]", "Capability identifier")
|
|
10
17
|
.action(
|
|
11
18
|
action(async (_, id) => {
|
|
@@ -15,13 +22,25 @@ export function registerDiscoveryCommands(program) {
|
|
|
15
22
|
"utf8",
|
|
16
23
|
),
|
|
17
24
|
);
|
|
18
|
-
|
|
25
|
+
const { version } = JSON.parse(
|
|
26
|
+
await readFile(
|
|
27
|
+
new URL("../../package.json", import.meta.url),
|
|
28
|
+
"utf8",
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
const provenance = {
|
|
32
|
+
kind: "bundled-catalog",
|
|
33
|
+
cliVersion: version,
|
|
34
|
+
serverVerified: false,
|
|
35
|
+
note: "Describes this CLI release. Scope flags do not make it project-specific. Verify the deployed contract before treating missing or conflicting entries as unsupported.",
|
|
36
|
+
};
|
|
37
|
+
if (!id) return { ...catalog, provenance };
|
|
19
38
|
const capability = catalog.capabilities.find((item) => item.id === id);
|
|
20
39
|
if (!capability)
|
|
21
40
|
throw new Error(
|
|
22
41
|
`Unknown capability. Choose: ${catalog.capabilities.map((item) => item.id).join(", ")}`,
|
|
23
42
|
);
|
|
24
|
-
return capability;
|
|
43
|
+
return { ...capability, provenance };
|
|
25
44
|
}),
|
|
26
45
|
);
|
|
27
46
|
program
|
package/cli/commands/project.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
2
3
|
import { action } from "../runtime.mjs";
|
|
3
4
|
import { request } from "../connection.mjs";
|
|
4
5
|
import { initialize, bundleProject } from "../project.mjs";
|
|
@@ -76,6 +77,7 @@ async function build({ project, connection, options }, deploy) {
|
|
|
76
77
|
return publish(connection, preview.id);
|
|
77
78
|
}
|
|
78
79
|
if (options.out) {
|
|
80
|
+
await mkdir(dirname(options.out), { recursive: true });
|
|
79
81
|
await writeFile(options.out, JSON.stringify(payload, null, 2) + "\n");
|
|
80
82
|
return {
|
|
81
83
|
file: options.out,
|
package/cli/commands/staging.mjs
CHANGED
|
@@ -20,9 +20,9 @@ export function registerStagingCommands(program) {
|
|
|
20
20
|
action(async ({ connection, project, options }) => {
|
|
21
21
|
if (!project)
|
|
22
22
|
throw new Error("Run stage inside a hosted campaign checkout.");
|
|
23
|
-
if (project.config.app
|
|
23
|
+
if (!project.config.app)
|
|
24
24
|
throw new Error(
|
|
25
|
-
"
|
|
25
|
+
"Configure app.kind as static or worker in relay.json before staging.",
|
|
26
26
|
);
|
|
27
27
|
const scoped = { ...connection, environment: "test" };
|
|
28
28
|
const preflight = await request(scoped, "/preflight");
|
package/cli/connected-dev.mjs
CHANGED
|
@@ -22,7 +22,7 @@ export async function connectedDev(project, connection, options) {
|
|
|
22
22
|
const preflight = await request(connection, "/preflight");
|
|
23
23
|
if (preflight.development?.status !== "configured")
|
|
24
24
|
throw new Error(
|
|
25
|
-
"Hosted development is not configured. Run domino doctor --remote --json; a Domino operator must enable
|
|
25
|
+
"Hosted development is not configured. Run domino doctor --remote --json; a Domino operator must enable preview hosting.",
|
|
26
26
|
);
|
|
27
27
|
const root = dirname(project.path);
|
|
28
28
|
const connector = await previewConnector(options.cloudflared);
|
|
@@ -126,6 +126,7 @@ export async function connectedDev(project, connection, options) {
|
|
|
126
126
|
session = await request(connection, "/development/session", "POST", {
|
|
127
127
|
...lease,
|
|
128
128
|
port,
|
|
129
|
+
routing: project.config.app.routing,
|
|
129
130
|
});
|
|
130
131
|
const sync = developmentSync(connection, project.path);
|
|
131
132
|
await sync();
|
|
@@ -143,6 +144,7 @@ export async function connectedDev(project, connection, options) {
|
|
|
143
144
|
start("pnpm", ["run", project.config.app.devScript], {
|
|
144
145
|
DOMINO_APP_PORT: String(port),
|
|
145
146
|
DOMINO_PREVIEW_ORIGIN: session.url,
|
|
147
|
+
DOMINO_PARTICIPANT_API: connection.apiUrl,
|
|
146
148
|
});
|
|
147
149
|
let appReady = false;
|
|
148
150
|
for (let attempt = 0; attempt < 150 && !stopping; attempt++) {
|
|
@@ -190,7 +192,7 @@ export async function connectedDev(project, connection, options) {
|
|
|
190
192
|
)
|
|
191
193
|
);
|
|
192
194
|
process.stderr.write(
|
|
193
|
-
`Preview: ${session.url}\
|
|
195
|
+
`Preview: ${session.url}\nKeep this terminal running. Use domino stage for a preview that stays online.\n`,
|
|
194
196
|
);
|
|
195
197
|
let renewed = Date.now();
|
|
196
198
|
while (!stopping) {
|
package/cli/doctor.mjs
CHANGED
|
@@ -5,10 +5,12 @@ import { access, readFile } from "node:fs/promises";
|
|
|
5
5
|
import { inspectAgentSkills } from "./agents.mjs";
|
|
6
6
|
import { request } from "./connection.mjs";
|
|
7
7
|
import { discoverEntries } from "./discovery.mjs";
|
|
8
|
+
import { packageManager } from "./package-manager.mjs";
|
|
8
9
|
|
|
9
|
-
export async function diagnose({ project, connection, options }) {
|
|
10
|
+
export async function diagnose({ project, connection, options, scopeSources }) {
|
|
10
11
|
const checks = [];
|
|
11
12
|
const root = project ? dirname(project.path) : process.cwd();
|
|
13
|
+
const manager = await packageManager(root);
|
|
12
14
|
checks.push(
|
|
13
15
|
project
|
|
14
16
|
? { id: "manifest", status: "ok", message: "Project manifest is valid." }
|
|
@@ -170,6 +172,13 @@ export async function diagnose({ project, connection, options }) {
|
|
|
170
172
|
development: { status: "not-checked" },
|
|
171
173
|
staging: "not-checked",
|
|
172
174
|
};
|
|
175
|
+
if (options.remote && !project)
|
|
176
|
+
checks.push({
|
|
177
|
+
id: "remote-access",
|
|
178
|
+
status: "warning",
|
|
179
|
+
message:
|
|
180
|
+
"Remote access was not checked because no relay.json was found. This does not diagnose a failed checkout. Use its checkout error details; do not install skills in the parent directory to repair remote access.",
|
|
181
|
+
});
|
|
173
182
|
if (options.remote && project) {
|
|
174
183
|
try {
|
|
175
184
|
const value = await request(connection, "/access");
|
|
@@ -238,6 +247,8 @@ export async function diagnose({ project, connection, options }) {
|
|
|
238
247
|
project: connection.project,
|
|
239
248
|
environment: connection.environment,
|
|
240
249
|
},
|
|
250
|
+
scopeSources,
|
|
251
|
+
packageManager: manager,
|
|
241
252
|
agents,
|
|
242
253
|
readiness,
|
|
243
254
|
remoteAccess,
|
|
@@ -245,6 +256,10 @@ export async function diagnose({ project, connection, options }) {
|
|
|
245
256
|
? "static-app-configured"
|
|
246
257
|
: "use-existing-app-workflow",
|
|
247
258
|
checks,
|
|
248
|
-
next:
|
|
259
|
+
next:
|
|
260
|
+
checks.some((check) => check.id === "sdk" && check.status === "error") &&
|
|
261
|
+
manager.installCommand
|
|
262
|
+
? `Run ${manager.installCommand} in ${root} and wait for it to finish before rerunning doctor. Then verify a participant action.`
|
|
263
|
+
: "After setup, run the project's checks and verify a real participant action. Setup diagnostics do not prove preview or live readiness.",
|
|
249
264
|
};
|
|
250
265
|
}
|
package/cli/git.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { access, mkdir, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { apiUrl, request, savedToken } from "./connection.mjs";
|
|
6
|
+
import { packageManager } from "./package-manager.mjs";
|
|
6
7
|
|
|
7
8
|
export function runGit(args, { cwd, input, env = {} } = {}) {
|
|
8
9
|
return new Promise((resolve, reject) => {
|
|
@@ -20,11 +21,13 @@ export function runGit(args, { cwd, input, env = {} } = {}) {
|
|
|
20
21
|
stderr += data;
|
|
21
22
|
});
|
|
22
23
|
child.on("error", reject);
|
|
23
|
-
child.on("close", (code) =>
|
|
24
|
-
code === 0
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
child.on("close", (code) => {
|
|
25
|
+
if (code === 0) return resolve(stdout.trim());
|
|
26
|
+
const error = new Error(`Git failed (${code}): ${stderr.trim()}`);
|
|
27
|
+
error.exitCode = code;
|
|
28
|
+
error.gitCommand = args[0];
|
|
29
|
+
reject(error);
|
|
30
|
+
});
|
|
28
31
|
child.stdin.end(input);
|
|
29
32
|
});
|
|
30
33
|
}
|
|
@@ -86,7 +89,7 @@ export async function requireNewDirectory(directory) {
|
|
|
86
89
|
export async function checkout(connection, repository, directory) {
|
|
87
90
|
if (repository.status !== "ready")
|
|
88
91
|
throw new Error(
|
|
89
|
-
|
|
92
|
+
`Repository provisioning is incomplete (status: ${repository.status}). Inspect the existing project in Console, then retry domino checkout. Do not create a replacement project.`,
|
|
90
93
|
);
|
|
91
94
|
const remote = new URL(repository.remote);
|
|
92
95
|
if (
|
|
@@ -152,14 +155,42 @@ export async function checkout(connection, repository, directory) {
|
|
|
152
155
|
environment: "test",
|
|
153
156
|
}) + "\n",
|
|
154
157
|
);
|
|
158
|
+
const manager = await packageManager(root);
|
|
155
159
|
return {
|
|
156
160
|
directory: root,
|
|
157
161
|
remote: repository.remote,
|
|
158
|
-
|
|
162
|
+
packageManager: manager,
|
|
163
|
+
scopeFile: resolve(root, ".git/domino.json"),
|
|
164
|
+
next: manager.installCommand
|
|
165
|
+
? `In ${root}, run ${manager.installCommand} and wait for success. Then run the project's installed domino doctor --remote --json before domino dev.`
|
|
166
|
+
: "Read package.json and install with the project's declared package manager and lockfile. Wait for success before running the installed domino doctor --remote --json.",
|
|
159
167
|
};
|
|
160
168
|
} catch (error) {
|
|
161
169
|
// This directory was created exclusively by this operation and has no user edits.
|
|
162
170
|
await rm(root, { recursive: true, force: true });
|
|
171
|
+
if (error.gitCommand) {
|
|
172
|
+
error.code = "CHECKOUT_GIT_FAILED";
|
|
173
|
+
error.details = {
|
|
174
|
+
phase: error.gitCommand,
|
|
175
|
+
exitCode: error.exitCode,
|
|
176
|
+
classification: /repository not found/i.test(error.message)
|
|
177
|
+
? "repository-unavailable"
|
|
178
|
+
: /authentication failed|401|403/i.test(error.message)
|
|
179
|
+
? "access-denied"
|
|
180
|
+
: "git-failure",
|
|
181
|
+
repositoryStatus: repository.status,
|
|
182
|
+
organization: repository.organization,
|
|
183
|
+
project: repository.project,
|
|
184
|
+
remote: repository.remote,
|
|
185
|
+
};
|
|
186
|
+
error.message += ` Repository API reported ${repository.status}; Git ${error.gitCommand} failed.`;
|
|
187
|
+
error.message +=
|
|
188
|
+
error.gitCommand === "fetch"
|
|
189
|
+
? " This does not establish whether provisioning, access, or transport caused the failure. Retry checkout once with the same scope; if it repeats, share these diagnostics with the operator."
|
|
190
|
+
: " Resolve the Git error above before retrying checkout.";
|
|
191
|
+
error.message +=
|
|
192
|
+
" Do not recreate the project or install dependencies in its parent directory.";
|
|
193
|
+
}
|
|
163
194
|
throw error;
|
|
164
195
|
}
|
|
165
196
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
// Only generate a runnable command for a validated, exact package-manager pin.
|
|
5
|
+
// Never interpolate arbitrary package.json content into a suggested shell command.
|
|
6
|
+
export async function packageManager(directory) {
|
|
7
|
+
let pkg;
|
|
8
|
+
try {
|
|
9
|
+
pkg = JSON.parse(await readFile(join(directory, "package.json"), "utf8"));
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (error.code === "ENOENT")
|
|
12
|
+
return { declared: null, installCommand: null };
|
|
13
|
+
return {
|
|
14
|
+
declared: null,
|
|
15
|
+
installCommand: null,
|
|
16
|
+
error:
|
|
17
|
+
"Cannot read package.json. Restore valid JSON before installing dependencies.",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const declared =
|
|
21
|
+
typeof pkg?.packageManager === "string" ? pkg.packageManager : null;
|
|
22
|
+
const pin =
|
|
23
|
+
/^(pnpm|npm)@(\d+\.\d+\.\d+(?:-[\w.-]+)?)(?:\+sha\d+\.[a-fA-F0-9]+)?$/.exec(
|
|
24
|
+
declared ?? "",
|
|
25
|
+
);
|
|
26
|
+
if (!pin) return { declared, installCommand: null };
|
|
27
|
+
const [, name, version] = pin;
|
|
28
|
+
const install = name === "pnpm" ? "install --frozen-lockfile" : "ci";
|
|
29
|
+
const runner = `npx --yes ${name}@${version}`;
|
|
30
|
+
return { declared, installCommand: `${runner} ${install}` };
|
|
31
|
+
}
|
package/cli/project.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hostedAppSchema } from "@domino-sdk/relay-cli";
|
|
1
2
|
import { readFile, writeFile, mkdir, access } from "node:fs/promises";
|
|
2
3
|
import { dirname, join, resolve } from "node:path";
|
|
3
4
|
import { z } from "zod";
|
|
@@ -41,16 +42,7 @@ export const projectSchema = z
|
|
|
41
42
|
})
|
|
42
43
|
.strict()
|
|
43
44
|
.optional(),
|
|
44
|
-
app:
|
|
45
|
-
.object({
|
|
46
|
-
kind: z.literal("static"),
|
|
47
|
-
directory: z.string().min(1),
|
|
48
|
-
devScript: z.string().min(1),
|
|
49
|
-
checkScript: z.string().min(1),
|
|
50
|
-
buildScript: z.string().min(1),
|
|
51
|
-
})
|
|
52
|
-
.strict()
|
|
53
|
-
.optional(),
|
|
45
|
+
app: hostedAppSchema.optional(),
|
|
54
46
|
questTypes: z
|
|
55
47
|
.array(
|
|
56
48
|
z
|
|
@@ -136,7 +128,14 @@ export async function readJson(path) {
|
|
|
136
128
|
}
|
|
137
129
|
|
|
138
130
|
async function loadProject(path) {
|
|
139
|
-
const
|
|
131
|
+
const raw = await readJson(path);
|
|
132
|
+
const config = projectSchema.parse(raw);
|
|
133
|
+
const scopeSources = Object.fromEntries(
|
|
134
|
+
["apiUrl", "organization", "project", "environment"].map((key) => [
|
|
135
|
+
key,
|
|
136
|
+
raw[key] === undefined ? { kind: "default" } : { kind: "manifest", path },
|
|
137
|
+
]),
|
|
138
|
+
);
|
|
140
139
|
const dir = dirname(path);
|
|
141
140
|
// Worktrees keep shared connection metadata in the common Git directory.
|
|
142
141
|
const gitDir = await runGit(["rev-parse", "--git-common-dir"], {
|
|
@@ -154,11 +153,16 @@ async function loadProject(path) {
|
|
|
154
153
|
.strict()
|
|
155
154
|
.parse(await readJson(resolve(dir, gitDir, "domino.json")));
|
|
156
155
|
Object.assign(config, local);
|
|
156
|
+
for (const key of Object.keys(local))
|
|
157
|
+
scopeSources[key] = {
|
|
158
|
+
kind: "checkout",
|
|
159
|
+
path: resolve(dir, gitDir, "domino.json"),
|
|
160
|
+
};
|
|
157
161
|
} catch (error) {
|
|
158
162
|
if (error.code !== "ENOENT") throw error;
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
|
-
return { path, config };
|
|
165
|
+
return { path, config, scopeSources };
|
|
162
166
|
}
|
|
163
167
|
|
|
164
168
|
export async function findProject(path) {
|
package/cli/runtime.mjs
CHANGED
|
@@ -8,11 +8,18 @@ export function action(handler) {
|
|
|
8
8
|
const options = command.optsWithGlobals();
|
|
9
9
|
const project = await findProject(options.config);
|
|
10
10
|
const connection = {};
|
|
11
|
+
const scopeSources = {};
|
|
11
12
|
for (const key of ["apiUrl", "organization", "project", "environment"]) {
|
|
12
13
|
const env =
|
|
13
14
|
key === "apiUrl" ? "RELAY_API_URL" : `RELAY_${key.toUpperCase()}`;
|
|
14
15
|
connection[key] =
|
|
15
16
|
options[key] ?? process.env[env] ?? project?.config[key];
|
|
17
|
+
scopeSources[key] =
|
|
18
|
+
options[key] !== undefined
|
|
19
|
+
? { kind: "flag", name: key === "apiUrl" ? "--api-url" : `--${key}` }
|
|
20
|
+
: process.env[env] !== undefined
|
|
21
|
+
? { kind: "environment", name: env }
|
|
22
|
+
: (project?.scopeSources[key] ?? { kind: "default" });
|
|
16
23
|
}
|
|
17
24
|
connection.apiUrl = apiUrl(connection.apiUrl ?? "https://relay.domino.run");
|
|
18
25
|
if (
|
|
@@ -21,7 +28,7 @@ export function action(handler) {
|
|
|
21
28
|
)
|
|
22
29
|
throw new Error("Environment must be test or live.");
|
|
23
30
|
const result = await handler(
|
|
24
|
-
{ options, project, connection },
|
|
31
|
+
{ options, project, connection, scopeSources },
|
|
25
32
|
...command.processedArgs,
|
|
26
33
|
);
|
|
27
34
|
console.log(JSON.stringify(result, null, options.json ? undefined : 2));
|
|
@@ -42,7 +42,7 @@ Use `domino login` for browser authorization when remote work is needed. As soon
|
|
|
42
42
|
|
|
43
43
|
Keep the login process running while the user approves in their browser. The CLI polls for approval and saves the credential itself; never ask the user to paste credentials into chat. Wait for the CLI to confirm success before continuing. If the attempt expires, run `domino login` again and send the new code first, then the new link.
|
|
44
44
|
|
|
45
|
-
Run `domino doctor --remote --json` to verify access and
|
|
45
|
+
Run `domino doctor --remote --json` after dependency installation finishes to verify access before remote operations. Inspect `scope` and `scopeSources`: flags override `RELAY_*` environment variables, then the Git common directory's `domino.json`, then `relay.json`. Checkout writes the untracked local connection file; starter values such as `local` and `campaign` in the manifest may therefore be valid defaults. Credentials live separately in the user config directory. For scope or setup failures, read [troubleshooting](references/troubleshooting.md).
|
|
46
46
|
|
|
47
47
|
Choose the reference that matches the task:
|
|
48
48
|
|
|
@@ -51,7 +51,7 @@ Choose the reference that matches the task:
|
|
|
51
51
|
- Before authoring a quest, read [authoring](references/authoring.md#find-a-snippet) and check the live snippet catalog for a suitable starting point. The same reference covers reusable types, collections, leaderboards, external points, and publication.
|
|
52
52
|
- For standings, own rank, nearby participants, or pagination, read [participant API](references/participant-api.md#leaderboard-reads).
|
|
53
53
|
|
|
54
|
-
Before designing referrals, rankings, milestones, or rewards for another participant, run `domino capabilities --json`. Read `domino capabilities <id> --json` for the support boundary. Treat an unlisted capability as unknown and inspect its contract before promising it.
|
|
54
|
+
Before designing referrals, rankings, milestones, or rewards for another participant, run `domino capabilities --json`. Read `domino capabilities <id> --json` for the support boundary. The output's `provenance` identifies a bundled CLI catalog, not a project-specific server probe. Treat an unlisted capability or a disagreement with the deployed API as unknown and inspect its contract before promising it. Check recurrence, daily limits, social verification, and prize fulfillment separately; points or leaderboards alone do not establish those mechanics.
|
|
55
55
|
|
|
56
56
|
For browser integration and submission recovery, read [participant API](references/participant-api.md). For invitation links, qualification, inviter bonuses, or referral milestones, read [referrals](references/referrals.md). For startup, staging, or version failures, read [troubleshooting](references/troubleshooting.md).
|
|
57
57
|
|
|
@@ -61,4 +61,4 @@ If no CLI is installed, install `@domino-sdk/relay-cli` and `@domino-sdk/relay`
|
|
|
61
61
|
|
|
62
62
|
Run the app's checks and exercise the requested participant action through the running app. Verify the resulting progress or completion state. Keep fixture-provider results distinct from real external verification.
|
|
63
63
|
|
|
64
|
-
Return the working preview or local URL, what participant action you verified, and any remaining launch dependency. A successful build, installed skill, or healthy doctor result is not proof of participant behavior. Live publication requires human review in Console; hosted staging uses test data and real sign-in.
|
|
64
|
+
Return the working preview or local URL, what participant action you verified, and any remaining launch dependency. Report failed or blocked browser checks explicitly, including a missing browser executable. A successful build, installed skill, or healthy doctor result is not proof of participant behavior. Live publication requires human review in Console; hosted staging uses test data and real sign-in.
|
|
@@ -31,7 +31,7 @@ Source owns reusable quest definitions, collection slots, and deployment default
|
|
|
31
31
|
|
|
32
32
|
Entry `settings` supplies deployment defaults. Remote custom values take precedence on subsequent deployments. A removed or incompatible setting can block the batch. Use `settingRenames` for a deliberate rename, or have the operator reset the custom value in Console under **Quests**. Changing source defaults does not clear overrides.
|
|
33
33
|
|
|
34
|
-
Use `domino build --json` or `domino deploy --dry-run --json` for offline bundling. These do not validate effective remote settings. Use `domino deploy --preview --json` to resolve and validate the remote batch without publishing. Check the selected scope before publication.
|
|
34
|
+
Use `domino build --json` or `domino deploy --dry-run --json` for offline bundling. To save the bundle, use `domino build --out .domino-build/deployment.json --json`; the CLI creates missing parent directories. These checks do not validate effective remote settings. Use `domino deploy --preview --json` to resolve and validate the remote batch without publishing. Check the selected scope before publication.
|
|
35
35
|
|
|
36
36
|
Use `domino deploy --environment test --json` when test publication is part of the task. It publishes configured quests, types, collections, and supported interactions together. After a lost publication response, use the returned `domino deploy --resume <id>` recovery command. Starting another deployment may create another immutable release.
|
|
37
37
|
|
|
@@ -110,3 +110,13 @@ The platform supplies a read-only client bound to the project's configured serve
|
|
|
110
110
|
Successful reads, including absent membership, are stored with the account revision, integration revision, server, and observation time. Recovery reuses captured facts. Changing either connection invalidates unfinished reads. After a failed attempt, start a new submission if a connection changed. Membership screening (`pending`) and role requirements remain author decisions.
|
|
111
111
|
|
|
112
112
|
The campaign starter includes participant identity controls. Existing apps use the same APIs through `createParticipantBackend`; see [participant API](participant-api.md#connected-accounts). The managed bot is configured by the platform, never by a quest author. Do not invent message, reaction, or write capabilities: `member` is the only Discord operation currently available. X remains unavailable.
|
|
113
|
+
|
|
114
|
+
The release's `provider` setting selects the photo-check backend used by `ctx.review.photo`; it does not select the evaluator for Discord, quizzes, or other authored checks. `humanReview` allows quest code to return a review decision. It does not automatically route Discord failures to staff. The Discord membership snippet returns accept or reject decisions and leaves provider outages as errors.
|
|
115
|
+
|
|
116
|
+
## Entry after app-owned pass verification
|
|
117
|
+
|
|
118
|
+
Author the entry quest with `manual({ actor: "staff" })` and no evidence input. The participant app verifies its event pass on its server, resolves the signed-in member, and calls `POST /management/v1/passes/verify` with `{ provider, event, subject, member, quest }`. Use a server-only management key with `confirm` permission in the intended project and environment. Relay records unique pass ownership and submits the entry attempt; it does not decide whether the external ticket is valid. Repeating the same pass for the same member reuses the durable attempt. A different member cannot claim that pass, and one member cannot link a second pass for the same provider and event.
|
|
119
|
+
|
|
120
|
+
## Hosted server applications
|
|
121
|
+
|
|
122
|
+
Quest discovery and private resource artifacts work the same way for static and Worker apps. Keep quest definitions outside the public asset directory. For server-rendered applications, configure the Worker module output and browser asset output separately in `relay.json`; see [hosted apps](hosted.md). With `app.routing="transparent"`, hosting reserves no paths. Omitting the setting preserves legacy routing. The starter owns its `/relay/*` proxy as ordinary application code; custom handlers can take precedence.
|
|
@@ -6,9 +6,9 @@ If Console already created the project and repository, run `domino checkout <pro
|
|
|
6
6
|
|
|
7
7
|
For a failure after remote project creation, inspect the existing project's state before repeating creation. Keep partially created local directories intact while recovering. Checkout accepts `--directory` for a fresh destination.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Enter the checked-out directory before installing guidance or dependencies. Use checkout's `packageManager.installCommand`, which selects the exact version declared in package.json and preserves the lockfile. For `pnpm@10.34.5`, this is `npx --yes pnpm@10.34.5 install --frozen-lockfile`. Wait for installation to succeed before running doctor; a missing SDK during installation is not a new fault. If guidance is already installed, do not reinstall it as a remedy for checkout or authentication failures. The starter includes pinned SDK, CLI, and local-runtime archives in `.domino`; upgrade them through a platform package release rather than modifying their contents. Read CAMPAIGN.md for the owner's brief.
|
|
10
10
|
|
|
11
|
-
The starter
|
|
11
|
+
The starter uses Vite, React, TanStack Router file-based routes, and Tailwind CSS v4, with an app-owned Worker in `server/index.ts` for participant requests and browser assets. Its look is neutral on purpose (greyscale color tokens in `src/styles.css`, no logo or accent) and it ships sample quests and copy that introduce Domino; replace them with the owner's campaign. Its AGENTS.md maps the source and describes the `src/relay` hooks for session, quest state, submission, and recovery. Build the interface on those hooks and keep their recovery behavior.
|
|
12
12
|
|
|
13
13
|
The starter's relay.json declares discovery patterns for `quests/**/*.quest.ts` and `quests/**/*.quest-type.ts`. Add a matching default-exported file to include it in builds and development sync. Installed snippets use these paths. Use explicit entries only for named exports or per-resource overrides.
|
|
14
14
|
|
|
@@ -22,7 +22,7 @@ When the task includes sharing a preview, commit the intended changes and push u
|
|
|
22
22
|
|
|
23
23
|
If stage submission loses its response, repeat the printed commit and request ID. Use `domino builds --json` and `domino logs <build-id> --json` to recover progress after disconnecting. A failed build leaves the previous preview active. Report the URL returned for the successful build rather than constructing one.
|
|
24
24
|
|
|
25
|
-
Cloud staging supports the starter's
|
|
25
|
+
Cloud staging supports static apps and compiled Cloudflare Worker apps, including SSR and server routes. Configure `app.kind="worker"`, `directory` for private modules, `main` relative to that directory, `compatibilityDate`, optional `compatibilityFlags`, and optional `assetsDirectory` for browser assets. Keep `devScript`, `checkScript`, and `buildScript`. TanStack Start with Cloudflare Vite typically uses `dist/server`, `index.js`, `dist/client`, and `nodejs_compat`. The dev server must honor `DOMINO_APP_PORT` and `DOMINO_PREVIEW_ORIGIN`. The build must also emit the starter's private `.domino-build` quest/catalog artifacts. Set `app.routing="transparent"` to preserve methods, cookies, and app authorization. Hosting reserves no URL paths and forwards requests and responses unchanged. The starter explicitly proxies `/relay/*` in its own server, used in both development and staging; apps can override or remove that handler. Use `proxyParticipant(request, { baseUrl, fetch, hosted: true })` to integrate Domino-managed sign-in explicitly. The API validates the registered app origin, session, and CSRF headers. `DOMINO_PARTICIPANT_API` supplies the selected upstream during connected development. `runWorkerFirst: true` lets your Worker handle even requests matching browser assets through its ASSETS binding. Non-secret string `vars` are supported; customer secrets and resource bindings are not provisioned by this contract. New staging builds use the same project test data as development and real participant sign-in. Omitted routing or `app.routing="legacy"` preserves existing gateway behavior, including implicit `/relay/` routes. To migrate, add the app-owned proxy, explicitly select transparent routing, restart and test the preview with the updated CLI, then commit and stage. Routing is recorded per build and preview session; old records and old CLI requests remain legacy. Test data does not imply that external provider operations are simulated. Hosted app live activation remains a separate platform milestone.
|
|
26
26
|
|
|
27
27
|
For referral programs, register authored modules in `referrals`. `domino check` and `domino dev` include them even without quests. Preserve invitation codes across the app's sign-in redirect and verify acceptance through the configured participant proxy before any quest completes. See [referrals](referrals.md).
|
|
28
28
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Participant API
|
|
2
2
|
|
|
3
|
-
Create `createBrowserRelayClient({ baseUrl: "/relay" })` from `@domino-sdk/relay/browser`. The browser client uses cookies and durable submission recovery.
|
|
3
|
+
Create `createBrowserRelayClient({ baseUrl: "/relay" })` from `@domino-sdk/relay/browser`. The browser client uses cookies and durable submission recovery. With `app.routing="transparent"`, the app explicitly mounts its participant integration at `/relay/*`; hosting does not intercept or reserve that prefix. Existing apps without that setting retain the gateway participant proxy. Management tokens are not participant credentials.
|
|
4
4
|
|
|
5
5
|
## Sign in
|
|
6
6
|
|
|
@@ -40,7 +40,7 @@ The browser still uses `createBrowserRelayClient({ baseUrl: "/relay" })`. Reques
|
|
|
40
40
|
|
|
41
41
|
The adapter uses `POST /v1/auth/exchange` with `{ subject, previousToken? }` and the integration key as a server bearer credential. Scope and the member role come from the registered integration, never from request input. It adds a server-only integration header when proxying participant requests. A plain exchange result is not a substitute for configuring that routing; use the adapter.
|
|
42
42
|
|
|
43
|
-
`https://console.domino.run/relay` proxies management requests only. Use the participant API origin for the adapter. The lower-level `proxyParticipant`
|
|
43
|
+
`https://console.domino.run/relay` proxies management requests only. Use the participant API origin for the adapter. For Domino-managed sign-in in a hosted preview or staging app, explicitly call `proxyParticipant(request, { baseUrl: "https://relay.domino.run", fetch, hosted: true })` from an app-owned route. The API resolves the app origin against its registration and still validates sessions and CSRF. The lower-level `proxyParticipant` also supports deployments with preconfigured participant routing; it deliberately excludes browser Authorization and organization/project/environment selectors.
|
|
44
44
|
|
|
45
45
|
## Display and complete quests
|
|
46
46
|
|
|
@@ -107,3 +107,11 @@ To choose a different identity, first call `relay.auth.disconnect("discord")`. T
|
|
|
107
107
|
The campaign starter includes these account controls. Apps using `createParticipantBackend` retain their own login and use the same APIs. The adapter preserves OAuth binding cookies and its session during return navigation; the application's own login cookie must also support top-level OAuth returns. If the app session changes during consent, start connection again.
|
|
108
108
|
|
|
109
109
|
After changing an account or integration during a failed attempt, use the quest controller's `discard()` to clear the saved failed submission, then submit again. The starter labels this **Start a new submission**. Discard is allowed only after a confirmed failed or rejected attempt. It keeps server history and cannot discard an active or accepted attempt. Use `resume()` when the connection has not changed to preserve captured facts.
|
|
110
|
+
|
|
111
|
+
## Resource IDs
|
|
112
|
+
|
|
113
|
+
Treat resource IDs as opaque strings and store the complete value. New generated IDs keep their resource prefix, such as `member_`, `session_`, or `attempt_`, followed by 22 random alphanumeric characters. Existing IDs remain valid. Do not parse the suffix as a UUID.
|
|
114
|
+
|
|
115
|
+
## Removed demo shortcuts
|
|
116
|
+
|
|
117
|
+
`auth.demoSignIn` and the campaign template endpoint are no longer supported. Use managed sign-in or a registered identity integration. The platform participant `me.linkPass` route remains unconfigured. An app can handle that route itself and verify a pass on its server, then call `POST /management/v1/passes/verify` with a server-only management key carrying `confirm` permission. Send `{ provider, event, subject, member, quest }` and the usual organization, project, and environment headers. Resolve the member from the authenticated participant session, never from browser input. The quest must be staff-confirmed with no evidence. Relay binds ownership uniquely per provider/event in that project, preserves the authenticated management actor, and retries the same entry attempt. There is no built-in event-code verifier.
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Development and staging diagnostics
|
|
2
2
|
|
|
3
|
+
## Scope and installation
|
|
4
|
+
|
|
5
|
+
Read `scopeSources` next to `scope` in `domino doctor --json`. Each value comes from a command flag, a named `RELAY_*` environment variable, the checkout's local connection file, the manifest, or a default. Precedence is flags > environment > checkout connection > manifest. `domino checkout` writes `.git/domino.json`; linked Git worktrees read the file in their shared Git common directory. This untracked file contains API, organization, project, and environment identifiers, not credentials. Do not rewrite starter manifest values merely because they differ from the effective scope.
|
|
6
|
+
|
|
7
|
+
Use the `packageManager.installCommand` printed by checkout or doctor. It selects the declared version with a frozen lockfile. Wait for the install process to finish before diagnosing missing dependencies. Run checks from the project directory. Without a manifest, `doctor --remote` does not probe the remote and cannot explain why checkout failed.
|
|
8
|
+
|
|
9
|
+
For `CHECKOUT_GIT_FAILED`, retain the original Git message and the structured `details`: failed phase, exit code, error classification, repository status reported by the API, project, and remote. A `ready` API status combined with a Git "Repository not found" error does not establish whether provisioning, permissions, or transport caused the failure. Retry the same checkout once. If it fails again, send the diagnostics to the operator and keep the existing Console project. Do not create a replacement, reinstall skills in a parent directory, or repeatedly run unrelated diagnostics.
|
|
10
|
+
|
|
11
|
+
## Capability disagreements
|
|
12
|
+
|
|
13
|
+
`domino capabilities` reads the catalog bundled with the CLI version in `provenance.cliVersion`. It does not request server capabilities; `serverVerified` is false even when project flags are supplied. Compare the installed version, the applicable public contract, and the actual deployed endpoint before changing the architecture. A successful management route does not by itself prove participant access or payout support. A missing route or catalog entry does not prove platform-wide non-support.
|
|
14
|
+
|
|
15
|
+
## Verification failures
|
|
16
|
+
|
|
17
|
+
Keep build results, browser rendering, and participant behavior separate. If a browser executable is missing or a check fails, report the exact blocked check and leave its behavior unverified. Do not turn a generated visual sample or a successful build into a claim that the app works in a browser.
|
|
18
|
+
|
|
19
|
+
## Preview diagnostics
|
|
20
|
+
|
|
3
21
|
Start with `domino doctor --json`. Add `--remote` to verify project access, publication permission, hosting configuration and repository readiness. Configuration checks do not prove a successful build or real participant sign-in. If cloud hosting is missing, a Domino operator must configure it; changing quest code will not fix it.
|
|
4
22
|
|
|
5
23
|
`domino check` runs the app's checks and bundles authored entries. `domino deploy --preview --environment test --json` validates the effective deployment against the server without publishing. Test and live data are separate; test does not imply external provider requests are fake.
|
package/cli.mjs
CHANGED
|
@@ -82,7 +82,8 @@ const program = new Command()
|
|
|
82
82
|
.addHelpText(
|
|
83
83
|
"after",
|
|
84
84
|
`
|
|
85
|
-
Scope: flags > RELAY_* environment variables > nearest relay.json.
|
|
85
|
+
Scope: flags > RELAY_* environment variables > Git-common-directory domino.json > nearest relay.json.
|
|
86
|
+
Run domino doctor --json to see effective scope values and their sources.
|
|
86
87
|
API default: https://relay.domino.run. Init defaults to test.
|
|
87
88
|
Credentials: RELAY_MANAGEMENT_TOKEN > saved token for the API origin.
|
|
88
89
|
Run domino login for browser sign-in. Live publication requires Console.
|
|
@@ -108,6 +109,8 @@ try {
|
|
|
108
109
|
? JSON.stringify({
|
|
109
110
|
error: error.message,
|
|
110
111
|
...(error.status ? { status: error.status } : {}),
|
|
112
|
+
...(error.code ? { code: error.code } : {}),
|
|
113
|
+
...(error.details ? { details: error.details } : {}),
|
|
111
114
|
})
|
|
112
115
|
: `Error: ${error.message}`,
|
|
113
116
|
);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
import { pointsMutationSchema, referralControlSchema, leaderboardQuerySchema, leaderboardExclusionSchema, leaderboardGroupSchema, projectDeploymentSchema, publishDeploymentSchema, catalogDeploymentSchema, saveQuestDraftSchema, publishQuestDraftSchema, collectionBeginSchema, configurePickupSchema } from '@domino-sdk/relay';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
+
declare const appRoutingSchema: z.ZodDefault<z.ZodEnum<{
|
|
5
|
+
legacy: "legacy";
|
|
6
|
+
transparent: "transparent";
|
|
7
|
+
}>>;
|
|
8
|
+
declare const hostedAppSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
9
|
+
routing: z.ZodDefault<z.ZodEnum<{
|
|
10
|
+
legacy: "legacy";
|
|
11
|
+
transparent: "transparent";
|
|
12
|
+
}>>;
|
|
13
|
+
devScript: z.ZodString;
|
|
14
|
+
checkScript: z.ZodString;
|
|
15
|
+
buildScript: z.ZodString;
|
|
16
|
+
kind: z.ZodLiteral<"static">;
|
|
17
|
+
directory: z.ZodString;
|
|
18
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
19
|
+
runWorkerFirst: z.ZodDefault<z.ZodBoolean>;
|
|
20
|
+
main: z.ZodString;
|
|
21
|
+
compatibilityDate: z.ZodString;
|
|
22
|
+
compatibilityFlags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
23
|
+
vars: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
24
|
+
routing: z.ZodDefault<z.ZodEnum<{
|
|
25
|
+
legacy: "legacy";
|
|
26
|
+
transparent: "transparent";
|
|
27
|
+
}>>;
|
|
28
|
+
devScript: z.ZodString;
|
|
29
|
+
checkScript: z.ZodString;
|
|
30
|
+
buildScript: z.ZodString;
|
|
31
|
+
kind: z.ZodLiteral<"worker">;
|
|
32
|
+
directory: z.ZodString;
|
|
33
|
+
assetsDirectory: z.ZodOptional<z.ZodString>;
|
|
34
|
+
}, z.core.$strict>], "kind">;
|
|
4
35
|
declare const repositorySchema: z.ZodObject<{
|
|
5
36
|
id: z.ZodString;
|
|
6
37
|
organization: z.ZodString;
|
|
@@ -32,6 +63,11 @@ declare const stageRequestSchema: z.ZodObject<{
|
|
|
32
63
|
requestId: z.ZodString;
|
|
33
64
|
}, z.core.$strict>;
|
|
34
65
|
declare const buildIdentitySchema: z.ZodObject<{
|
|
66
|
+
routing: z.ZodDefault<z.ZodEnum<{
|
|
67
|
+
legacy: "legacy";
|
|
68
|
+
transparent: "transparent";
|
|
69
|
+
}>>;
|
|
70
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
35
71
|
runtime: z.ZodDefault<z.ZodEnum<{
|
|
36
72
|
simulation: "simulation";
|
|
37
73
|
"hosted-test": "hosted-test";
|
|
@@ -82,6 +118,11 @@ declare const buildStatusSchema: z.ZodObject<{
|
|
|
82
118
|
}, z.core.$strip>;
|
|
83
119
|
type BuildStatus = z.infer<typeof buildStatusSchema>;
|
|
84
120
|
declare const hostedBuildSchema: z.ZodObject<{
|
|
121
|
+
routing: z.ZodDefault<z.ZodEnum<{
|
|
122
|
+
legacy: "legacy";
|
|
123
|
+
transparent: "transparent";
|
|
124
|
+
}>>;
|
|
125
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
85
126
|
runtime: z.ZodDefault<z.ZodEnum<{
|
|
86
127
|
simulation: "simulation";
|
|
87
128
|
"hosted-test": "hosted-test";
|
|
@@ -123,6 +164,11 @@ declare const hostedBuildSchema: z.ZodObject<{
|
|
|
123
164
|
type HostedBuild = z.infer<typeof hostedBuildSchema>;
|
|
124
165
|
declare const buildSnapshotSchema: z.ZodObject<{
|
|
125
166
|
builds: z.ZodArray<z.ZodObject<{
|
|
167
|
+
routing: z.ZodDefault<z.ZodEnum<{
|
|
168
|
+
legacy: "legacy";
|
|
169
|
+
transparent: "transparent";
|
|
170
|
+
}>>;
|
|
171
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
126
172
|
runtime: z.ZodDefault<z.ZodEnum<{
|
|
127
173
|
simulation: "simulation";
|
|
128
174
|
"hosted-test": "hosted-test";
|
|
@@ -162,6 +208,11 @@ declare const buildSnapshotSchema: z.ZodObject<{
|
|
|
162
208
|
message: z.ZodString;
|
|
163
209
|
}, z.core.$strip>>;
|
|
164
210
|
current: z.ZodNullable<z.ZodObject<{
|
|
211
|
+
routing: z.ZodDefault<z.ZodEnum<{
|
|
212
|
+
legacy: "legacy";
|
|
213
|
+
transparent: "transparent";
|
|
214
|
+
}>>;
|
|
215
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
165
216
|
runtime: z.ZodDefault<z.ZodEnum<{
|
|
166
217
|
simulation: "simulation";
|
|
167
218
|
"hosted-test": "hosted-test";
|
|
@@ -201,6 +252,11 @@ declare const buildSnapshotSchema: z.ZodObject<{
|
|
|
201
252
|
message: z.ZodString;
|
|
202
253
|
}, z.core.$strip>>;
|
|
203
254
|
selected: z.ZodNullable<z.ZodObject<{
|
|
255
|
+
routing: z.ZodDefault<z.ZodEnum<{
|
|
256
|
+
legacy: "legacy";
|
|
257
|
+
transparent: "transparent";
|
|
258
|
+
}>>;
|
|
259
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
204
260
|
runtime: z.ZodDefault<z.ZodEnum<{
|
|
205
261
|
simulation: "simulation";
|
|
206
262
|
"hosted-test": "hosted-test";
|
|
@@ -245,6 +301,27 @@ type BuildSnapshot = z.infer<typeof buildSnapshotSchema>;
|
|
|
245
301
|
declare const assetPathSchema: z.ZodString;
|
|
246
302
|
declare const buildArtifactsSchema: z.ZodObject<{
|
|
247
303
|
format: z.ZodLiteral<1>;
|
|
304
|
+
routing: z.ZodDefault<z.ZodEnum<{
|
|
305
|
+
legacy: "legacy";
|
|
306
|
+
transparent: "transparent";
|
|
307
|
+
}>>;
|
|
308
|
+
worker: z.ZodOptional<z.ZodObject<{
|
|
309
|
+
modules: z.ZodArray<z.ZodObject<{
|
|
310
|
+
path: z.ZodString;
|
|
311
|
+
type: z.ZodEnum<{
|
|
312
|
+
"application/javascript+module": "application/javascript+module";
|
|
313
|
+
"application/wasm": "application/wasm";
|
|
314
|
+
"text/plain": "text/plain";
|
|
315
|
+
"application/octet-stream": "application/octet-stream";
|
|
316
|
+
}>;
|
|
317
|
+
content: z.ZodString;
|
|
318
|
+
}, z.core.$strict>>;
|
|
319
|
+
runWorkerFirst: z.ZodDefault<z.ZodBoolean>;
|
|
320
|
+
main: z.ZodString;
|
|
321
|
+
compatibilityDate: z.ZodString;
|
|
322
|
+
compatibilityFlags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
323
|
+
vars: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
324
|
+
}, z.core.$strict>>;
|
|
248
325
|
assets: z.ZodArray<z.ZodObject<{
|
|
249
326
|
path: z.ZodString;
|
|
250
327
|
content: z.ZodString;
|
|
@@ -319,6 +396,7 @@ declare const projectSetupSchema: z.ZodObject<{
|
|
|
319
396
|
brief: z.ZodString;
|
|
320
397
|
}, z.core.$strict>;
|
|
321
398
|
declare const projectSchema: z.ZodObject<{
|
|
399
|
+
slug: z.ZodString;
|
|
322
400
|
setup: z.ZodOptional<z.ZodObject<{
|
|
323
401
|
mode: z.ZodEnum<{
|
|
324
402
|
new: "new";
|
|
@@ -378,6 +456,7 @@ declare const accessSchema: z.ZodObject<{
|
|
|
378
456
|
admin: z.ZodBoolean;
|
|
379
457
|
local: z.ZodBoolean;
|
|
380
458
|
projects: z.ZodArray<z.ZodObject<{
|
|
459
|
+
slug: z.ZodString;
|
|
381
460
|
setup: z.ZodOptional<z.ZodObject<{
|
|
382
461
|
mode: z.ZodEnum<{
|
|
383
462
|
new: "new";
|
|
@@ -5615,6 +5694,7 @@ declare function createManagementClient(options: {
|
|
|
5615
5694
|
admin: boolean;
|
|
5616
5695
|
local: boolean;
|
|
5617
5696
|
projects: {
|
|
5697
|
+
slug: string;
|
|
5618
5698
|
id: string;
|
|
5619
5699
|
organization: string;
|
|
5620
5700
|
project: string;
|
|
@@ -5631,6 +5711,7 @@ declare function createManagementClient(options: {
|
|
|
5631
5711
|
}[];
|
|
5632
5712
|
}>;
|
|
5633
5713
|
createProject: (input: z.infer<typeof projectInputSchema>) => Promise<{
|
|
5714
|
+
slug: string;
|
|
5634
5715
|
id: string;
|
|
5635
5716
|
organization: string;
|
|
5636
5717
|
project: string;
|
|
@@ -6781,4 +6862,4 @@ declare function createManagementClient(options: {
|
|
|
6781
6862
|
}>;
|
|
6782
6863
|
};
|
|
6783
6864
|
|
|
6784
|
-
export { type Access, type AuditRecord, type BuildArtifacts, type BuildIdentity, type BuildSnapshot, type BuildStatus, type HostedBuild, type ListQuery, type ManagementPage, type Permission, type Project, accessSchema, accountProfileSchema, assetPathSchema, auditRecordSchema, buildArtifactsSchema, buildIdentitySchema, buildPhaseSchema, buildSnapshotSchema, buildStatusSchema, commitSchema, createManagementClient, createRepositorySchema, developmentPreflightSchema, deviceApproveSchema, deviceRedeemSchema, deviceStartSchema, environmentSchema, grantSchema, hostedBuildSchema, inventorySchema, memberAccountSchema, memberDetailSchema, memberSchema, metricsSchema, operationInputSchema, operationSchema, pageSchema, permissionSchema, projectInputSchema, projectSchema, projectSetupSchema, querySchema, readinessSchema, repositorySchema, rolePermissions, roleSchema, stageRequestSchema, stockInputSchema, tokenInputSchema, tokenSchema };
|
|
6865
|
+
export { type Access, type AuditRecord, type BuildArtifacts, type BuildIdentity, type BuildSnapshot, type BuildStatus, type HostedBuild, type ListQuery, type ManagementPage, type Permission, type Project, accessSchema, accountProfileSchema, appRoutingSchema, assetPathSchema, auditRecordSchema, buildArtifactsSchema, buildIdentitySchema, buildPhaseSchema, buildSnapshotSchema, buildStatusSchema, commitSchema, createManagementClient, createRepositorySchema, developmentPreflightSchema, deviceApproveSchema, deviceRedeemSchema, deviceStartSchema, environmentSchema, grantSchema, hostedAppSchema, hostedBuildSchema, inventorySchema, memberAccountSchema, memberDetailSchema, memberSchema, metricsSchema, operationInputSchema, operationSchema, pageSchema, permissionSchema, projectInputSchema, projectSchema, projectSetupSchema, querySchema, readinessSchema, repositorySchema, rolePermissions, roleSchema, stageRequestSchema, stockInputSchema, tokenInputSchema, tokenSchema };
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,46 @@ import { z as z3 } from "zod";
|
|
|
32
32
|
// src/hosting.ts
|
|
33
33
|
import { z } from "zod";
|
|
34
34
|
import { identifier } from "@domino-sdk/relay";
|
|
35
|
+
var outputPathSchema = z.string().min(1).max(512).refine(
|
|
36
|
+
(path) => path.split("/").every(
|
|
37
|
+
(part) => /^[\w-][\w.-]*$/.test(part) && part !== "node_modules"
|
|
38
|
+
),
|
|
39
|
+
"Expected a relative build output path without hidden segments"
|
|
40
|
+
);
|
|
41
|
+
var appRoutingSchema = z.enum(["legacy", "transparent"]).default("legacy");
|
|
42
|
+
var appScripts = {
|
|
43
|
+
routing: appRoutingSchema,
|
|
44
|
+
devScript: z.string().regex(/^[\w:-]+$/),
|
|
45
|
+
checkScript: z.string().regex(/^[\w:-]+$/),
|
|
46
|
+
buildScript: z.string().regex(/^[\w:-]+$/)
|
|
47
|
+
};
|
|
48
|
+
var workerSettings = {
|
|
49
|
+
runWorkerFirst: z.boolean().default(false),
|
|
50
|
+
main: outputPathSchema.refine(
|
|
51
|
+
(path) => /\.m?js$/.test(path),
|
|
52
|
+
"Expected a compiled JavaScript entry"
|
|
53
|
+
),
|
|
54
|
+
compatibilityDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
55
|
+
compatibilityFlags: z.array(z.string().min(1)).max(50).default([]),
|
|
56
|
+
vars: z.record(
|
|
57
|
+
z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).refine((name) => name !== "ASSETS"),
|
|
58
|
+
z.string()
|
|
59
|
+
).default({})
|
|
60
|
+
};
|
|
61
|
+
var hostedAppSchema = z.discriminatedUnion("kind", [
|
|
62
|
+
z.object({
|
|
63
|
+
kind: z.literal("static"),
|
|
64
|
+
directory: outputPathSchema,
|
|
65
|
+
...appScripts
|
|
66
|
+
}).strict(),
|
|
67
|
+
z.object({
|
|
68
|
+
kind: z.literal("worker"),
|
|
69
|
+
directory: outputPathSchema,
|
|
70
|
+
assetsDirectory: outputPathSchema.optional(),
|
|
71
|
+
...appScripts,
|
|
72
|
+
...workerSettings
|
|
73
|
+
}).strict()
|
|
74
|
+
]);
|
|
35
75
|
var repositorySchema = z.object({
|
|
36
76
|
id: z.string().uuid(),
|
|
37
77
|
organization: identifier,
|
|
@@ -60,6 +100,8 @@ var stageRequestSchema = z.object({
|
|
|
60
100
|
requestId: z.string().uuid()
|
|
61
101
|
}).strict();
|
|
62
102
|
var buildIdentitySchema = z.object({
|
|
103
|
+
routing: appRoutingSchema,
|
|
104
|
+
slug: z.string().min(1).max(35).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).optional(),
|
|
63
105
|
runtime: z.enum(["simulation", "hosted-test"]).default("simulation"),
|
|
64
106
|
id: z.string().uuid(),
|
|
65
107
|
repository: z.string().uuid(),
|
|
@@ -99,11 +141,29 @@ var buildSnapshotSchema = z.object({
|
|
|
99
141
|
logs: z.string()
|
|
100
142
|
});
|
|
101
143
|
var assetPathSchema = z.string().max(512).refine(
|
|
102
|
-
(path) => path.startsWith("/") && !/[\\\u0000-\u001f?#%]/.test(path) && path.slice(1).split("/").every((part) => part.length > 0 && !part.startsWith("."))
|
|
103
|
-
"Expected a public asset path without hidden
|
|
144
|
+
(path) => path.startsWith("/") && !/[\\\u0000-\u001f?#%]/.test(path) && path.slice(1).split("/").every((part) => part.length > 0 && !part.startsWith(".")),
|
|
145
|
+
"Expected a public asset path without hidden segments"
|
|
104
146
|
);
|
|
105
147
|
var buildArtifactsSchema = z.object({
|
|
106
148
|
format: z.literal(1),
|
|
149
|
+
routing: appRoutingSchema,
|
|
150
|
+
worker: z.object({
|
|
151
|
+
...workerSettings,
|
|
152
|
+
modules: z.array(
|
|
153
|
+
z.object({
|
|
154
|
+
path: outputPathSchema.refine((path) => path !== "metadata"),
|
|
155
|
+
type: z.enum([
|
|
156
|
+
"application/javascript+module",
|
|
157
|
+
"application/wasm",
|
|
158
|
+
"text/plain",
|
|
159
|
+
"application/octet-stream"
|
|
160
|
+
]),
|
|
161
|
+
content: z.string().max(14e6).regex(
|
|
162
|
+
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
|
|
163
|
+
)
|
|
164
|
+
}).strict()
|
|
165
|
+
).min(1).max(500)
|
|
166
|
+
}).strict().optional(),
|
|
107
167
|
assets: z.array(
|
|
108
168
|
z.object({
|
|
109
169
|
path: assetPathSchema,
|
|
@@ -112,13 +172,36 @@ var buildArtifactsSchema = z.object({
|
|
|
112
172
|
),
|
|
113
173
|
size: z.number().int().min(0).max(1e7)
|
|
114
174
|
}).strict()
|
|
115
|
-
).
|
|
175
|
+
).max(500),
|
|
116
176
|
quests: z.unknown(),
|
|
117
177
|
catalog: z.unknown()
|
|
118
178
|
}).strict().superRefine((value, ctx) => {
|
|
179
|
+
if (value.worker) {
|
|
180
|
+
const modules = value.worker.modules;
|
|
181
|
+
if (new Set(modules.map((m) => m.path)).size !== modules.length)
|
|
182
|
+
ctx.addIssue({
|
|
183
|
+
code: "custom",
|
|
184
|
+
message: "Duplicate Worker module paths"
|
|
185
|
+
});
|
|
186
|
+
if (!modules.some(
|
|
187
|
+
(m) => m.path === value.worker?.main && m.type === "application/javascript+module"
|
|
188
|
+
))
|
|
189
|
+
ctx.addIssue({
|
|
190
|
+
code: "custom",
|
|
191
|
+
message: "Worker entry module is missing"
|
|
192
|
+
});
|
|
193
|
+
if (modules.reduce(
|
|
194
|
+
(sum, m) => sum + m.content.length * 3 / 4 - (m.content.endsWith("==") ? 2 : m.content.endsWith("=") ? 1 : 0),
|
|
195
|
+
0
|
|
196
|
+
) > 1e7)
|
|
197
|
+
ctx.addIssue({
|
|
198
|
+
code: "custom",
|
|
199
|
+
message: "Worker modules exceed 10 MB"
|
|
200
|
+
});
|
|
201
|
+
}
|
|
119
202
|
if (new Set(value.assets.map((a) => a.path)).size !== value.assets.length)
|
|
120
203
|
ctx.addIssue({ code: "custom", message: "Duplicate asset paths" });
|
|
121
|
-
if (!value.assets.some((a) => a.path === "/index.html"))
|
|
204
|
+
if (!value.worker && !value.assets.some((a) => a.path === "/index.html"))
|
|
122
205
|
ctx.addIssue({
|
|
123
206
|
code: "custom",
|
|
124
207
|
message: "Build must include index.html"
|
|
@@ -193,6 +276,7 @@ var projectSetupSchema = z3.object({
|
|
|
193
276
|
brief: z3.string().trim().min(1).max(4e3)
|
|
194
277
|
}).strict();
|
|
195
278
|
var projectSchema = z3.object({
|
|
279
|
+
slug: z3.string().min(1).max(35).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
|
|
196
280
|
setup: projectSetupSchema.optional(),
|
|
197
281
|
id: z3.string().min(1),
|
|
198
282
|
organization: identifier2,
|
|
@@ -526,6 +610,7 @@ function createManagementClient(options) {
|
|
|
526
610
|
export {
|
|
527
611
|
accessSchema,
|
|
528
612
|
accountProfileSchema,
|
|
613
|
+
appRoutingSchema,
|
|
529
614
|
assetPathSchema,
|
|
530
615
|
auditRecordSchema,
|
|
531
616
|
buildArtifactsSchema,
|
|
@@ -542,6 +627,7 @@ export {
|
|
|
542
627
|
deviceStartSchema,
|
|
543
628
|
environmentSchema,
|
|
544
629
|
grantSchema,
|
|
630
|
+
hostedAppSchema,
|
|
545
631
|
hostedBuildSchema,
|
|
546
632
|
inventorySchema,
|
|
547
633
|
memberAccountSchema,
|
package/package.json
CHANGED