@domino-sdk/relay-cli 0.2.0 → 0.4.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/capabilities.json +56 -20
- package/cli/commands/discovery.mjs +1 -1
- package/cli/commands/hosting.mjs +7 -8
- package/cli/commands/project.mjs +4 -0
- package/cli/discovery.mjs +62 -0
- package/cli/doctor.mjs +9 -2
- package/cli/project.mjs +126 -29
- package/cli/skills/domino/SKILL.md +34 -4
- package/cli/skills/domino/references/authoring.md +98 -1
- package/cli/skills/domino/references/existing-app.md +6 -2
- package/cli/skills/domino/references/hosted.md +12 -0
- package/cli/skills/domino/references/participant-api.md +82 -1
- package/cli/skills/domino/references/referrals.md +57 -8
- package/cli/skills/domino/references/troubleshooting.md +2 -0
- package/dist/index.d.ts +993 -4
- package/dist/index.js +105 -3
- package/package.json +2 -2
package/cli/capabilities.json
CHANGED
|
@@ -12,7 +12,36 @@
|
|
|
12
12
|
"auth.emailVerify",
|
|
13
13
|
"auth.session"
|
|
14
14
|
],
|
|
15
|
-
"evidence": [
|
|
15
|
+
"evidence": [
|
|
16
|
+
"tests/auth.test.mjs",
|
|
17
|
+
"tests/email-auth.test.mjs"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "connected-accounts",
|
|
22
|
+
"support": "native",
|
|
23
|
+
"description": "Operators connect a Discord server and install the managed bot in Console Integrations. Participants sign in with Discord or link its identity. Declare integrations: [discord] and use ctx.discord.member() without a server ID. Captured membership and role reads survive recovery. No messages, writes, or X verification.",
|
|
24
|
+
"entrypoints": [
|
|
25
|
+
"auth.accounts",
|
|
26
|
+
"auth.start",
|
|
27
|
+
"auth.disconnect"
|
|
28
|
+
],
|
|
29
|
+
"reference": "authoring",
|
|
30
|
+
"evidence": [
|
|
31
|
+
"tests/connected-accounts.test.mjs",
|
|
32
|
+
"apps/api/src/provider-registry.ts"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "identity-exchange",
|
|
37
|
+
"support": "native",
|
|
38
|
+
"description": "Keep an existing application login using createParticipantBackend on its server. A Console administrator creates a project/environment identity integration key. The adapter verifies the application session, exchanges its stable subject for a member-only Domino session, and routes participant requests with trusted scope. Keys expire after 90 days and support rotation and revocation.",
|
|
39
|
+
"entrypoints": [],
|
|
40
|
+
"reference": "participant-api",
|
|
41
|
+
"evidence": [
|
|
42
|
+
"tests/identity-integration.test.mjs",
|
|
43
|
+
"apps/api/src/identity-integration.ts"
|
|
44
|
+
]
|
|
16
45
|
},
|
|
17
46
|
{
|
|
18
47
|
"id": "quests",
|
|
@@ -38,38 +67,45 @@
|
|
|
38
67
|
{
|
|
39
68
|
"id": "milestones",
|
|
40
69
|
"support": "composed",
|
|
41
|
-
"description": "Compose same-participant completion prerequisites with requires: [completed(quest)].
|
|
70
|
+
"description": "Compose same-participant completion prerequisites with requires: [completed(quest)]. Referral programs support milestones based on distinct qualified friends. Other aggregate thresholds need application logic.",
|
|
42
71
|
"entrypoints": ["completed", "automatic", "defineQuest"],
|
|
43
72
|
"evidence": ["tests/availability.test.mjs"]
|
|
44
73
|
},
|
|
45
74
|
{
|
|
46
75
|
"id": "referrals",
|
|
47
|
-
"support": "
|
|
48
|
-
"description": "
|
|
49
|
-
"entrypoints": [
|
|
76
|
+
"support": "native",
|
|
77
|
+
"description": "Project-scoped invitation codes, immutable inviter attribution, configurable signup, quest or external qualification, fixed rewards and distinct-friend milestones. Selective one-level inviter bonuses add points without reducing invitee awards. Code-defined rules publish through reviewed project deployments.",
|
|
78
|
+
"entrypoints": [
|
|
79
|
+
"defineReferral",
|
|
80
|
+
"referrals.list",
|
|
81
|
+
"referrals.summary",
|
|
82
|
+
"referrals.accept",
|
|
83
|
+
"referrals.history"
|
|
84
|
+
],
|
|
50
85
|
"reference": "referrals",
|
|
51
|
-
"evidence": [
|
|
52
|
-
"packages/sdk/src/authoring.ts",
|
|
53
|
-
"packages/sdk/src/schema.ts"
|
|
54
|
-
]
|
|
86
|
+
"evidence": ["tests/referrals.test.mjs", "packages/sdk/src/referrals.ts"]
|
|
55
87
|
},
|
|
56
88
|
{
|
|
57
89
|
"id": "cross-participant-rewards",
|
|
58
|
-
"support": "
|
|
59
|
-
"description": "
|
|
60
|
-
"entrypoints": [],
|
|
90
|
+
"support": "native",
|
|
91
|
+
"description": "Referral programs can reward inviter and invitee independently and award a selective percentage of eligible points to the inviter. Bonuses accumulate fractions and support limits, suspension and correction recovery. Arbitrary participant-to-participant transfers and multi-level commissions are not supported.",
|
|
92
|
+
"entrypoints": ["defineReferral", "referrals.summary"],
|
|
61
93
|
"reference": "referrals",
|
|
62
|
-
"evidence": [
|
|
63
|
-
"packages/sdk/src/authoring.ts",
|
|
64
|
-
"packages/sdk/src/schema.ts"
|
|
65
|
-
]
|
|
94
|
+
"evidence": ["tests/referrals.test.mjs", "apps/api/src/referrals.ts"]
|
|
66
95
|
},
|
|
67
96
|
{
|
|
68
97
|
"id": "rankings",
|
|
69
|
-
"support": "
|
|
70
|
-
"description": "
|
|
71
|
-
"entrypoints": [
|
|
72
|
-
|
|
98
|
+
"support": "native",
|
|
99
|
+
"description": "Publish project-scoped points leaderboards through project deployments. Read paginated standings, your rank, and nearby competitors with shared tie ranks. Code defines balance, earned, or net calculations and time windows. Public access is explicit; participant rows use board-specific aliases. Scoped management APIs supply external points, corrections, groups, and exclusions. Referral bonus points contribute to their named balance. Cross-project balances, finalized results, and rank-based prizes are separate work.",
|
|
100
|
+
"entrypoints": [
|
|
101
|
+
"defineLeaderboard",
|
|
102
|
+
"leaderboards.list",
|
|
103
|
+
"leaderboards.standings"
|
|
104
|
+
],
|
|
105
|
+
"evidence": [
|
|
106
|
+
"tests/leaderboards.test.mjs",
|
|
107
|
+
"packages/sdk/src/leaderboards.ts"
|
|
108
|
+
]
|
|
73
109
|
},
|
|
74
110
|
{
|
|
75
111
|
"id": "wallet-verification",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { action } from "../runtime.mjs";
|
|
3
3
|
|
|
4
|
-
const references = ["participant-api", "referrals", "troubleshooting"];
|
|
4
|
+
const references = ["authoring", "participant-api", "referrals", "troubleshooting"];
|
|
5
5
|
export function registerDiscoveryCommands(program) {
|
|
6
6
|
program
|
|
7
7
|
.command("capabilities")
|
package/cli/commands/hosting.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { action } from "../runtime.mjs";
|
|
|
7
7
|
import { request } from "../connection.mjs";
|
|
8
8
|
import { checkout, requireNewDirectory } from "../git.mjs";
|
|
9
9
|
import { dev } from "../dev.mjs";
|
|
10
|
-
import {
|
|
10
|
+
import { bundleProject } from "../project.mjs";
|
|
11
11
|
|
|
12
12
|
export function registerHostingCommands(program) {
|
|
13
13
|
program
|
|
@@ -29,15 +29,14 @@ export function registerHostingCommands(program) {
|
|
|
29
29
|
throw new Error(error.stdout || error.stderr || error.message);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
const
|
|
33
|
-
? await bundleProject(project)
|
|
34
|
-
: { releases: [] };
|
|
35
|
-
const catalog = await bundleCatalog(project);
|
|
32
|
+
const bundle = await bundleProject(project, { allowEmpty: true });
|
|
36
33
|
return {
|
|
37
34
|
checked: true,
|
|
38
|
-
quests:
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
quests: bundle.releases.length,
|
|
36
|
+
leaderboards: bundle.leaderboards.length,
|
|
37
|
+
referrals: bundle.referrals.length,
|
|
38
|
+
types: bundle.types.length,
|
|
39
|
+
collections: bundle.collections.length,
|
|
41
40
|
};
|
|
42
41
|
}),
|
|
43
42
|
);
|
package/cli/commands/project.mjs
CHANGED
|
@@ -26,6 +26,8 @@ function summary(value) {
|
|
|
26
26
|
})),
|
|
27
27
|
collections: value.collections,
|
|
28
28
|
supportedInteractions: value.supportedInteractions,
|
|
29
|
+
leaderboards: value.leaderboards,
|
|
30
|
+
referrals: value.referrals,
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
async function publish(connection, id) {
|
|
@@ -80,6 +82,8 @@ async function build({ project, connection, options }, deploy) {
|
|
|
80
82
|
quests: payload.releases.length,
|
|
81
83
|
types: payload.types.length,
|
|
82
84
|
collections: payload.collections.length,
|
|
85
|
+
leaderboards: payload.leaderboards.length,
|
|
86
|
+
referrals: payload.referrals.length,
|
|
83
87
|
};
|
|
84
88
|
}
|
|
85
89
|
return payload;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { glob } from "node:fs/promises";
|
|
2
|
+
import { dirname, relative, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
export const defaultDiscovery = {
|
|
5
|
+
quests: ["quests/**/*.quest.ts"],
|
|
6
|
+
questTypes: ["quests/**/*.quest-type.ts"],
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// These directories contain dependencies, build output, or local runtime data.
|
|
10
|
+
const excluded =
|
|
11
|
+
/(^|[/\\])(node_modules|dist|\.git|\.domino|\.domino-build|\.wrangler|\.artifacts)([/\\]|$)/;
|
|
12
|
+
|
|
13
|
+
export async function discoverEntries(project, kind) {
|
|
14
|
+
const root = dirname(project.path);
|
|
15
|
+
const discovery = project.config.discover;
|
|
16
|
+
const entries = new Map();
|
|
17
|
+
const key = (entry) =>
|
|
18
|
+
JSON.stringify([resolve(root, entry.entry), entry.exportName ?? "default"]);
|
|
19
|
+
|
|
20
|
+
for (const entry of project.config[kind]) {
|
|
21
|
+
const identity = key(entry);
|
|
22
|
+
if (entries.has(identity))
|
|
23
|
+
throw new Error(`Duplicate ${kind} entry: ${entry.entry}`);
|
|
24
|
+
entries.set(identity, entry);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (discovery) {
|
|
28
|
+
const paths = [];
|
|
29
|
+
for await (const file of glob(discovery[kind], {
|
|
30
|
+
cwd: root,
|
|
31
|
+
withFileTypes: true,
|
|
32
|
+
exclude: (file) =>
|
|
33
|
+
excluded.test(relative(root, resolve(file.parentPath, file.name))),
|
|
34
|
+
})) {
|
|
35
|
+
if (file.isFile()) paths.push(resolve(file.parentPath, file.name));
|
|
36
|
+
}
|
|
37
|
+
for (const path of paths.sort()) {
|
|
38
|
+
const entry = {
|
|
39
|
+
entry: path,
|
|
40
|
+
exportName: "default",
|
|
41
|
+
provider: discovery.provider,
|
|
42
|
+
...(kind === "quests" ? { settings: {} } : {}),
|
|
43
|
+
};
|
|
44
|
+
// Explicit entries customize a discovered default export without adding it twice.
|
|
45
|
+
const explicit = entries.get(key(entry));
|
|
46
|
+
entries.set(
|
|
47
|
+
key(entry),
|
|
48
|
+
explicit
|
|
49
|
+
? {
|
|
50
|
+
...explicit,
|
|
51
|
+
exportName: "default",
|
|
52
|
+
provider: explicit.provider ?? discovery.provider,
|
|
53
|
+
}
|
|
54
|
+
: entry,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return [...entries.values()].map((entry) => ({
|
|
59
|
+
...entry,
|
|
60
|
+
provider: entry.provider ?? "workers-ai",
|
|
61
|
+
}));
|
|
62
|
+
}
|
package/cli/doctor.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { access, readFile } from "node:fs/promises";
|
|
5
5
|
import { inspectAgentSkills } from "./agents.mjs";
|
|
6
6
|
import { request } from "./connection.mjs";
|
|
7
|
+
import { discoverEntries } from "./discovery.mjs";
|
|
7
8
|
|
|
8
9
|
export async function diagnose({ project, connection, options }) {
|
|
9
10
|
const checks = [];
|
|
@@ -87,7 +88,12 @@ export async function diagnose({ project, connection, options }) {
|
|
|
87
88
|
);
|
|
88
89
|
|
|
89
90
|
if (project) {
|
|
90
|
-
const entries = [
|
|
91
|
+
const entries = [
|
|
92
|
+
...(await discoverEntries(project, "quests")),
|
|
93
|
+
...(await discoverEntries(project, "questTypes")),
|
|
94
|
+
...project.config.leaderboards,
|
|
95
|
+
...project.config.referrals,
|
|
96
|
+
];
|
|
91
97
|
const directories = new Set(
|
|
92
98
|
entries.map((entry) => dirname(resolve(root, entry.entry))),
|
|
93
99
|
);
|
|
@@ -182,12 +188,13 @@ export async function diagnose({ project, connection, options }) {
|
|
|
182
188
|
id: "remote-access",
|
|
183
189
|
status: "ok",
|
|
184
190
|
message:
|
|
185
|
-
"Management API verified project access. This does not verify participant sign-in or publication permission.",
|
|
191
|
+
"Management API verified project access. This does not verify participant sign-in or publication permission. Management credentials cannot issue participant sessions. Existing-login integration requires a dedicated server key and createParticipantBackend; run domino capabilities identity-exchange.",
|
|
186
192
|
});
|
|
187
193
|
const preflight = await request(connection, "/preflight");
|
|
188
194
|
readiness = {
|
|
189
195
|
development: preflight.development,
|
|
190
196
|
staging: preflight.staging,
|
|
197
|
+
identity: preflight.identity ?? { exchange: "unknown" },
|
|
191
198
|
};
|
|
192
199
|
if (project.config.app)
|
|
193
200
|
checks.push({
|
package/cli/project.mjs
CHANGED
|
@@ -3,6 +3,20 @@ import { dirname, join, resolve } from "node:path";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { buildQuest } from "@domino-sdk/relay/build";
|
|
5
5
|
import { runGit } from "./git.mjs";
|
|
6
|
+
import { defaultDiscovery, discoverEntries } from "./discovery.mjs";
|
|
7
|
+
|
|
8
|
+
const discoveryPattern = z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.refine(
|
|
12
|
+
(pattern) =>
|
|
13
|
+
!pattern.startsWith("/") &&
|
|
14
|
+
!pattern.startsWith("!") &&
|
|
15
|
+
!pattern.includes("\\") &&
|
|
16
|
+
!pattern.includes(":") &&
|
|
17
|
+
!pattern.split("/").includes(".."),
|
|
18
|
+
"Discovery patterns must be relative to relay.json, without parent traversal or negation.",
|
|
19
|
+
);
|
|
6
20
|
|
|
7
21
|
export const projectSchema = z
|
|
8
22
|
.object({
|
|
@@ -11,6 +25,22 @@ export const projectSchema = z
|
|
|
11
25
|
organization: z.string().min(1),
|
|
12
26
|
project: z.string().min(1),
|
|
13
27
|
environment: z.enum(["test", "live"]).default("test"),
|
|
28
|
+
discover: z
|
|
29
|
+
.object({
|
|
30
|
+
quests: z.array(discoveryPattern).default([]),
|
|
31
|
+
questTypes: z.array(discoveryPattern).default([]),
|
|
32
|
+
provider: z
|
|
33
|
+
.enum([
|
|
34
|
+
"workers-ai",
|
|
35
|
+
"fixture-pass",
|
|
36
|
+
"fixture-fail",
|
|
37
|
+
"fixture-unclear",
|
|
38
|
+
"fixture-error",
|
|
39
|
+
])
|
|
40
|
+
.default("workers-ai"),
|
|
41
|
+
})
|
|
42
|
+
.strict()
|
|
43
|
+
.optional(),
|
|
14
44
|
app: z
|
|
15
45
|
.object({
|
|
16
46
|
kind: z.literal("static"),
|
|
@@ -35,11 +65,33 @@ export const projectSchema = z
|
|
|
35
65
|
"fixture-unclear",
|
|
36
66
|
"fixture-error",
|
|
37
67
|
])
|
|
38
|
-
.
|
|
68
|
+
.optional(),
|
|
39
69
|
})
|
|
40
70
|
.strict(),
|
|
41
71
|
)
|
|
42
72
|
.default([]),
|
|
73
|
+
leaderboards: z
|
|
74
|
+
.array(
|
|
75
|
+
z
|
|
76
|
+
.object({
|
|
77
|
+
entry: z.string().min(1),
|
|
78
|
+
exportName: z.string().optional(),
|
|
79
|
+
})
|
|
80
|
+
.strict(),
|
|
81
|
+
)
|
|
82
|
+
.max(50)
|
|
83
|
+
.default([]),
|
|
84
|
+
referrals: z
|
|
85
|
+
.array(
|
|
86
|
+
z
|
|
87
|
+
.object({
|
|
88
|
+
entry: z.string().min(1),
|
|
89
|
+
exportName: z.string().optional(),
|
|
90
|
+
})
|
|
91
|
+
.strict(),
|
|
92
|
+
)
|
|
93
|
+
.max(50)
|
|
94
|
+
.default([]),
|
|
43
95
|
collections: z
|
|
44
96
|
.array(
|
|
45
97
|
z
|
|
@@ -70,7 +122,7 @@ export const projectSchema = z
|
|
|
70
122
|
"fixture-error",
|
|
71
123
|
"workers-ai",
|
|
72
124
|
])
|
|
73
|
-
.
|
|
125
|
+
.optional(),
|
|
74
126
|
})
|
|
75
127
|
.strict(),
|
|
76
128
|
)
|
|
@@ -124,9 +176,34 @@ export async function findProject(path) {
|
|
|
124
176
|
}
|
|
125
177
|
}
|
|
126
178
|
|
|
127
|
-
export async function bundleProject(project) {
|
|
179
|
+
export async function bundleProject(project, { allowEmpty = false } = {}) {
|
|
180
|
+
const quests = await discoverEntries(project, "quests");
|
|
181
|
+
const questTypes = await discoverEntries(project, "questTypes");
|
|
182
|
+
if (quests.length > 50)
|
|
183
|
+
throw new Error("A project deployment supports at most 50 quests.");
|
|
184
|
+
const questEntries = new Set(
|
|
185
|
+
quests.map((q) =>
|
|
186
|
+
JSON.stringify([
|
|
187
|
+
resolve(dirname(project.path), q.entry),
|
|
188
|
+
q.exportName ?? "default",
|
|
189
|
+
]),
|
|
190
|
+
),
|
|
191
|
+
);
|
|
192
|
+
for (const type of questTypes) {
|
|
193
|
+
if (
|
|
194
|
+
questEntries.has(
|
|
195
|
+
JSON.stringify([
|
|
196
|
+
resolve(dirname(project.path), type.entry),
|
|
197
|
+
type.exportName ?? "default",
|
|
198
|
+
]),
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
throw new Error(
|
|
202
|
+
`Entry is registered as both a quest and a quest type: ${type.entry}`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
128
205
|
const releases = [];
|
|
129
|
-
for (const quest of
|
|
206
|
+
for (const quest of quests) {
|
|
130
207
|
const artifact = await buildQuest(
|
|
131
208
|
resolve(dirname(project.path), quest.entry),
|
|
132
209
|
{ exportName: quest.exportName },
|
|
@@ -138,30 +215,68 @@ export async function bundleProject(project) {
|
|
|
138
215
|
provider: quest.provider,
|
|
139
216
|
});
|
|
140
217
|
}
|
|
141
|
-
const
|
|
218
|
+
const types = [];
|
|
219
|
+
for (const type of questTypes) {
|
|
220
|
+
const artifact = await buildQuest(
|
|
221
|
+
resolve(dirname(project.path), type.entry),
|
|
222
|
+
{ exportName: type.exportName },
|
|
223
|
+
);
|
|
224
|
+
types.push({ ...artifact, provider: type.provider });
|
|
225
|
+
}
|
|
226
|
+
const catalog = {
|
|
227
|
+
types,
|
|
228
|
+
collections: project.config.collections,
|
|
229
|
+
...(project.config.supportedInteractions
|
|
230
|
+
? { supportedInteractions: project.config.supportedInteractions }
|
|
231
|
+
: {}),
|
|
232
|
+
};
|
|
233
|
+
const leaderboards = await Promise.all(
|
|
234
|
+
project.config.leaderboards.map((board) =>
|
|
235
|
+
buildQuest(resolve(dirname(project.path), board.entry), {
|
|
236
|
+
exportName: board.exportName,
|
|
237
|
+
}),
|
|
238
|
+
),
|
|
239
|
+
);
|
|
240
|
+
const referrals = await Promise.all(
|
|
241
|
+
project.config.referrals.map((entry) =>
|
|
242
|
+
buildQuest(resolve(dirname(project.path), entry.entry), {
|
|
243
|
+
exportName: entry.exportName,
|
|
244
|
+
}),
|
|
245
|
+
),
|
|
246
|
+
);
|
|
142
247
|
if (
|
|
248
|
+
!allowEmpty &&
|
|
249
|
+
!referrals.length &&
|
|
143
250
|
!releases.length &&
|
|
251
|
+
!leaderboards.length &&
|
|
144
252
|
!catalog.types.length &&
|
|
145
253
|
!catalog.collections.length &&
|
|
146
254
|
!catalog.supportedInteractions
|
|
147
255
|
)
|
|
148
256
|
throw new Error(
|
|
149
|
-
"No resources configured. Add quests, questTypes, collections, or supportedInteractions
|
|
257
|
+
"No resources configured. Add files matching discover in relay.json, or configure quests, questTypes, leaderboards, referrals, collections, or supportedInteractions.",
|
|
150
258
|
);
|
|
151
|
-
return { releases, ...catalog };
|
|
259
|
+
return { releases, leaderboards, referrals, ...catalog };
|
|
152
260
|
}
|
|
153
261
|
|
|
154
262
|
export async function initialize(directory, config) {
|
|
155
263
|
const root = resolve(directory);
|
|
156
264
|
const files = {
|
|
157
|
-
"relay.json":
|
|
265
|
+
"relay.json":
|
|
266
|
+
JSON.stringify(
|
|
267
|
+
projectSchema.parse({ discover: defaultDiscovery, ...config }),
|
|
268
|
+
null,
|
|
269
|
+
2,
|
|
270
|
+
) + "\n",
|
|
158
271
|
"RELAY.md": `# Relay project
|
|
159
272
|
|
|
160
|
-
Use Codex or Claude to author your quests with @domino-sdk/relay/authoring. Project scope and quest entries are in relay.json. Relay imposes no framework, source directory, package manager, or application build configuration. Keep your existing project setup. Add @domino-sdk/relay with your package manager
|
|
273
|
+
Use Codex or Claude to author your quests with @domino-sdk/relay/authoring. Project scope, discovery patterns, and optional quest entries are in relay.json. Relay imposes no framework, source directory, package manager, or application build configuration. Keep your existing project setup. Add @domino-sdk/relay with your package manager. The CLI is supplied by @domino-sdk/relay-cli, separately from the runtime SDK. Commit relay.json and RELAY.md; credentials live in your user config directory.
|
|
274
|
+
|
|
275
|
+
Before authoring a new quest, run domino reference authoring for the snippet discovery and installation workflow. The public catalog is https://snippets.domino.run/r/registry.json. Install project agent guidance with domino agents install so Codex and Claude can find this workflow automatically.
|
|
161
276
|
|
|
162
277
|
Run domino build or domino deploy --dry-run to bundle the complete manifest locally. Run domino deploy --preview to inspect effective remote settings without publishing, and domino deploy to atomically publish quests, questTypes, collections, and supportedInteractions to test. For live, export domino deploy --dry-run --out deployment.json and review the project bundle in Console. Run domino quests or domino releases to inspect the server. Live publication requires Console.
|
|
163
278
|
|
|
164
|
-
Author quests with @domino-sdk/relay/authoring. Each entry points to any authored module relative to relay.json and default-exports defineQuest, or specifies exportName. No dedicated quests directory is required. Example entry: {"entry":"src/community/welcome.ts","settings":{},"provider":"fixture-pass"}. Fixture providers simulate verification; use workers-ai only on an API with the AI binding configured. Settings are deployment defaults validated by the server at publication. Console custom values persist in Relay and take precedence on every deployment; no source edits are needed to retain them. Deploy output reports defaults, custom values, and effective values. Use Console to reset a setting to its deployment default. Removing or invalidating a custom setting blocks the whole batch. For a rename, add settingRenames: {"oldName":"newName"} to the quest entry to carry custom values forward. A dry run builds locally and does not preview remote custom values. Multiple quests deploy as one batch so prerequisites can refer to other quests in the batch.
|
|
279
|
+
Author quests with @domino-sdk/relay/authoring. The discover patterns in relay.json select default-exported quests and quest types. Init configures quests/**/*.quest.ts and quests/**/*.quest-type.ts. Add matching files without registering each one. Customize the patterns for other source layouts. Discovery never executes modules on the build host. Set discover.provider for local fixture verification; it defaults to workers-ai. Explicit entries override matching discovered default exports and can supply settings, provider, and settingRenames. Each entry points to any authored module relative to relay.json and default-exports defineQuest, or specifies exportName. No dedicated quests directory is required. Example entry: {"entry":"src/community/welcome.ts","settings":{},"provider":"fixture-pass"}. Fixture providers simulate verification; use workers-ai only on an API with the AI binding configured. Settings are deployment defaults validated by the server at publication. Console custom values persist in Relay and take precedence on every deployment; no source edits are needed to retain them. Deploy output reports defaults, custom values, and effective values. Use Console to reset a setting to its deployment default. Removing or invalidating a custom setting blocks the whole batch. For a rename, add settingRenames: {"oldName":"newName"} to the quest entry to carry custom values forward. A dry run builds locally and does not preview remote custom values. Multiple quests deploy as one batch so prerequisites can refer to other quests in the batch.
|
|
165
280
|
|
|
166
281
|
References: @domino-sdk/relay/authoring provides declarations; @domino-sdk/relay/build bundles without executing author code on the host; domino --help lists commands. Set RELAY_MANAGEMENT_TOKEN for CI. Use --json for machine-readable output. Never put tokens in relay.json or source files.
|
|
167
282
|
`,
|
|
@@ -183,24 +298,6 @@ References: @domino-sdk/relay/authoring provides declarations; @domino-sdk/relay
|
|
|
183
298
|
return {
|
|
184
299
|
directory: root,
|
|
185
300
|
files: Object.keys(files),
|
|
186
|
-
next: "Add @domino-sdk/relay
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export async function bundleCatalog(project) {
|
|
191
|
-
const types = [];
|
|
192
|
-
for (const type of project.config.questTypes) {
|
|
193
|
-
const artifact = await buildQuest(
|
|
194
|
-
resolve(dirname(project.path), type.entry),
|
|
195
|
-
{ exportName: type.exportName },
|
|
196
|
-
);
|
|
197
|
-
types.push({ ...artifact, provider: type.provider });
|
|
198
|
-
}
|
|
199
|
-
return {
|
|
200
|
-
types,
|
|
201
|
-
collections: project.config.collections,
|
|
202
|
-
...(project.config.supportedInteractions
|
|
203
|
-
? { supportedInteractions: project.config.supportedInteractions }
|
|
204
|
-
: {}),
|
|
301
|
+
next: "Add @domino-sdk/relay, then add default-exported quests in quests/*.quest.ts or reusable types in quests/*.quest-type.ts and run domino build. Customize discover in relay.json for other layouts.",
|
|
205
302
|
};
|
|
206
303
|
}
|
|
@@ -1,29 +1,59 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: domino
|
|
3
|
-
description: Set up a Domino project, integrate Domino into an existing app, author quests, or verify and stage a participant experience with the Domino CLI and SDK.
|
|
3
|
+
description: Set up a Domino project, integrate Domino into an existing app, find and adapt quest snippets, author quests, leaderboards, or referrals, supply external points, or verify and stage a participant experience with the Domino CLI and SDK.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Build with Domino
|
|
7
7
|
|
|
8
8
|
Deliver the requested participant experience and evidence that its main action works. Domino owns verification, completion, points, and reward execution. The app owns its presentation and integration with participant identity.
|
|
9
9
|
|
|
10
|
+
## Discover and confirm the campaign
|
|
11
|
+
|
|
12
|
+
Read existing project instructions and saved decisions first. If the owner already confirmed the relevant campaign, reuse that evidence; reopen only decisions affected by a material change. Choose the procedure for the app being built.
|
|
13
|
+
|
|
14
|
+
### Existing apps: confirm the campaign
|
|
15
|
+
|
|
16
|
+
Preserve the app's current design, components, and UI conventions. Skip brand discovery, brand-name or website questions, visual samples, and brand approval unless the owner explicitly requests a redesign.
|
|
17
|
+
|
|
18
|
+
Ask only for missing campaign basics: goal, intended participants, desired actions, and reason to participate. If these are missing, ask a few short questions, end the turn, and wait for answers before setup or implementation. Use answers already supplied. Inspect the app to identify where the participant flow belongs, then propose the campaign and smallest integration that fits its existing UI. Resolve consequential rewards, timing, and eligibility gaps with the owner. Wait for explicit campaign confirmation before implementing, and save the confirmed brief and remaining open decisions in the app's established project document. Reuse prior confirmation when it covers the work. Continue with [existing apps](references/existing-app.md) for integration steps.
|
|
19
|
+
|
|
20
|
+
### New apps: discover the brand and campaign
|
|
21
|
+
|
|
22
|
+
Before implementing a new app, establish the brand direction and campaign brief with the owner. A starter, initial prompt, or Console brief is input to discovery, not confirmation. The procedure below applies to new apps only.
|
|
23
|
+
|
|
24
|
+
1. **Ask before researching or designing.** Check the supplied brief and prior owner answers for a brand name, official website or reference, campaign goal, intended participants, desired actions, and reason to participate. Ask for missing basics in a few short questions, then end the turn and wait for the owner's answers. For an empty or vague brief, ask both which brand this is for and what the campaign should achieve. The owner may confirm there is no website or established brand. A project name, repository name, starter, or sample quest is not evidence of brand identity or campaign intent. Create a new brand only when the owner explicitly requests it. If the basics are already supplied, summarize them for confirmation instead of asking the same questions again. Complete this intake before login, setup commands, research, or a visual proposal.
|
|
25
|
+
2. **Research and prepare the proposal.** After intake, inspect the owner's website, project documents, and supplied assets, including rendered appearance and styles. Record source URLs or file paths. If a source is inaccessible or identity remains ambiguous, ask the owner for a reference and wait rather than guessing. Show a concise brand interpretation and a small visual sample using the proposed colors, type, and voice. Draft the campaign goal, success measure, intended participants, desired actions, participant motivation, and journey from the owner's answers. Include timing, rewards, eligibility, and constraints where relevant. Suggest improvements tied to the goal and check Domino capabilities before promising supported behavior.
|
|
26
|
+
3. **Resolve consequential gaps.** Separate observed facts, recommendations, and assumptions. Ask only questions whose answers materially change the experience, a few at a time, with a recommended answer where possible. Use research and existing answers instead of giving the owner a questionnaire. Keep unconfirmed rewards, budgets, inventory, dates, and eligibility explicit as open decisions; do not turn guesses into participant promises.
|
|
27
|
+
4. **Confirm and save.** Present the proposed brand direction and campaign brief together for the owner to confirm or correct. End the turn and wait for their explicit response before implementation; silence and your own confidence are not confirmation. After intake, you may inspect and connect the project, research, write the draft brief, and prepare the visual sample. Keep app implementation and campaign configuration until after confirmation. Save the agreed direction, evidence of the owner's confirmation, sources, and remaining open decisions in CAMPAIGN.md, or the existing app's established brief document. Preserve the original request and link to the brief if it lives elsewhere.
|
|
28
|
+
|
|
29
|
+
Discovery is complete when the owner has confirmed the brand direction and campaign intent, including the goal, participants, actions, and motivation. Optional details can remain open if they do not affect the first implementation; record what must wait for those decisions. On subsequent sessions, read the saved brief and continue without repeating onboarding.
|
|
30
|
+
|
|
10
31
|
## Establish the project
|
|
11
32
|
|
|
12
33
|
Read the project's agent instructions and owner brief, such as CAMPAIGN.md, when present. Inspect package scripts and relay.json before selecting commands. Use the installed CLI through the project's package manager, such as `pnpm exec domino`; the examples below abbreviate this to `domino`.
|
|
13
34
|
|
|
14
35
|
Run `domino doctor --json` to inspect setup without running application code or contacting the service. Missing setup produces exit 1 with diagnostic JSON on stdout. Invalid configuration uses the CLI's standard error JSON on stderr. Fix the reported prerequisite, then rerun the check.
|
|
15
36
|
|
|
16
|
-
Use `domino login` for browser authorization when remote work is needed.
|
|
37
|
+
Use `domino login` for browser authorization when remote work is needed. As soon as the CLI prints the login details, send the user the code first, then the clickable sign-in link, in the same message. Always preserve this order, even if the CLI prints the link first. Use the exact code and URL from the current login attempt:
|
|
38
|
+
|
|
39
|
+
> Code: `<code from CLI>`
|
|
40
|
+
>
|
|
41
|
+
> [Open Domino to sign in](<URL from CLI>) and enter this code to approve the CLI connection.
|
|
42
|
+
|
|
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
|
+
|
|
45
|
+
Run `domino doctor --remote --json` to verify access and inspect the effective organization, project, API, and environment before remote operations. Flags and RELAY_* environment variables override relay.json.
|
|
17
46
|
|
|
18
47
|
Choose the reference that matches the task:
|
|
19
48
|
|
|
20
49
|
- For a new Domino-hosted app, checkout, or hosted preview, read [hosted projects](references/hosted.md).
|
|
21
50
|
- For integration into an existing codebase, read [existing apps](references/existing-app.md).
|
|
22
|
-
-
|
|
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
|
+
- For standings, own rank, nearby participants, or pagination, read [participant API](references/participant-api.md#leaderboard-reads).
|
|
23
53
|
|
|
24
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.
|
|
25
55
|
|
|
26
|
-
For browser integration and submission recovery, read [participant API](references/participant-api.md). For referral
|
|
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).
|
|
27
57
|
|
|
28
58
|
If no CLI is installed, install `@domino-sdk/relay-cli` and `@domino-sdk/relay` through the app's package manager. Starter archives pin the packages they were verified with; retain their lockfile and `.domino/release.json` when diagnosing compatibility.
|
|
29
59
|
|