@domino-sdk/relay-cli 0.7.0 → 0.8.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/hosting.mjs +19 -2
- package/cli/commands/project.mjs +25 -3
- package/cli/dev.mjs +57 -35
- package/cli/migrate.mjs +399 -0
- package/cli/output.mjs +11 -0
- package/cli/project.mjs +39 -15
- package/cli/skills/domino/references/authoring.md +47 -7
- package/cli/skills/domino/references/existing-app.md +3 -1
- package/cli/skills/domino/references/participant-api.md +4 -0
- package/cli/skills/domino/references/referrals.md +1 -1
- package/dist/index.d.ts +178 -12
- package/dist/index.js +55 -6
- package/package.json +4 -2
package/cli/project.mjs
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pointLedgerDeclarationSchema,
|
|
3
|
+
collectionSlotDeclarationSchema,
|
|
4
|
+
} from "@domino-sdk/relay";
|
|
1
5
|
import { hostedAppSchema } from "@domino-sdk/relay-cli";
|
|
2
6
|
import { readFile, writeFile, mkdir, access } from "node:fs/promises";
|
|
3
7
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -84,16 +88,8 @@ export const projectSchema = z
|
|
|
84
88
|
)
|
|
85
89
|
.max(50)
|
|
86
90
|
.default([]),
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
z
|
|
90
|
-
.object({
|
|
91
|
-
id: z.string().regex(/^[\w-]+$/),
|
|
92
|
-
title: z.string().min(1).max(120),
|
|
93
|
-
})
|
|
94
|
-
.strict(),
|
|
95
|
-
)
|
|
96
|
-
.default([]),
|
|
91
|
+
pointLedgers: z.array(pointLedgerDeclarationSchema).max(50).default([]),
|
|
92
|
+
collections: z.array(collectionSlotDeclarationSchema).default([]),
|
|
97
93
|
supportedInteractions: z
|
|
98
94
|
.array(z.enum(["photo", "quiz", "claim", "staff", "automatic"]))
|
|
99
95
|
.min(1)
|
|
@@ -129,7 +125,28 @@ export async function readJson(path) {
|
|
|
129
125
|
|
|
130
126
|
async function loadProject(path) {
|
|
131
127
|
const raw = await readJson(path);
|
|
132
|
-
const
|
|
128
|
+
const parsed = projectSchema.safeParse(raw);
|
|
129
|
+
if (!parsed.success) {
|
|
130
|
+
const details = parsed.error.issues.map((issue) => ({
|
|
131
|
+
code:
|
|
132
|
+
issue.path[0] === "collections"
|
|
133
|
+
? "COLLECTION_DECLARATION_INVALID"
|
|
134
|
+
: "MANIFEST_INVALID",
|
|
135
|
+
severity: "error",
|
|
136
|
+
file: path,
|
|
137
|
+
path: issue.path,
|
|
138
|
+
message: issue.message,
|
|
139
|
+
suggestion:
|
|
140
|
+
issue.path[0] === "collections"
|
|
141
|
+
? "Run domino migrate to inspect collection keys. Keep the original id value as key."
|
|
142
|
+
: "Correct this field in relay.json, then rerun domino check --json.",
|
|
143
|
+
}));
|
|
144
|
+
throw Object.assign(new Error(`Invalid project manifest: ${path}`), {
|
|
145
|
+
code: "MANIFEST_INVALID",
|
|
146
|
+
details,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const config = parsed.data;
|
|
133
150
|
const scopeSources = Object.fromEntries(
|
|
134
151
|
["apiUrl", "organization", "project", "environment"].map((key) => [
|
|
135
152
|
key,
|
|
@@ -165,13 +182,14 @@ async function loadProject(path) {
|
|
|
165
182
|
return { path, config, scopeSources };
|
|
166
183
|
}
|
|
167
184
|
|
|
168
|
-
export async function
|
|
169
|
-
if (path) return
|
|
185
|
+
export async function findProjectFile(path) {
|
|
186
|
+
if (path) return resolve(path);
|
|
170
187
|
let dir = process.cwd();
|
|
171
188
|
while (true) {
|
|
172
189
|
const path = join(dir, "relay.json");
|
|
173
190
|
try {
|
|
174
|
-
|
|
191
|
+
await access(path);
|
|
192
|
+
return path;
|
|
175
193
|
} catch (error) {
|
|
176
194
|
if (error.code !== "ENOENT") throw error;
|
|
177
195
|
}
|
|
@@ -179,6 +197,10 @@ export async function findProject(path) {
|
|
|
179
197
|
dir = dirname(dir);
|
|
180
198
|
}
|
|
181
199
|
}
|
|
200
|
+
export async function findProject(path) {
|
|
201
|
+
const file = await findProjectFile(path);
|
|
202
|
+
return file ? loadProject(file) : null;
|
|
203
|
+
}
|
|
182
204
|
|
|
183
205
|
export async function bundleProject(project, { allowEmpty = false } = {}) {
|
|
184
206
|
const quests = await discoverEntries(project, "quests");
|
|
@@ -228,6 +250,7 @@ export async function bundleProject(project, { allowEmpty = false } = {}) {
|
|
|
228
250
|
types.push({ ...artifact, provider: type.provider });
|
|
229
251
|
}
|
|
230
252
|
const catalog = {
|
|
253
|
+
pointLedgers: project.config.pointLedgers,
|
|
231
254
|
types,
|
|
232
255
|
collections: project.config.collections,
|
|
233
256
|
...(project.config.supportedInteractions
|
|
@@ -250,6 +273,7 @@ export async function bundleProject(project, { allowEmpty = false } = {}) {
|
|
|
250
273
|
);
|
|
251
274
|
if (
|
|
252
275
|
!allowEmpty &&
|
|
276
|
+
!catalog.pointLedgers.length &&
|
|
253
277
|
!referrals.length &&
|
|
254
278
|
!releases.length &&
|
|
255
279
|
!leaderboards.length &&
|
|
@@ -258,7 +282,7 @@ export async function bundleProject(project, { allowEmpty = false } = {}) {
|
|
|
258
282
|
!catalog.supportedInteractions
|
|
259
283
|
)
|
|
260
284
|
throw new Error(
|
|
261
|
-
"No resources configured. Add files matching discover in relay.json, or configure quests, questTypes, leaderboards, referrals, collections, or supportedInteractions.",
|
|
285
|
+
"No resources configured. Add files matching discover in relay.json, or configure quests, questTypes, pointLedgers, leaderboards, referrals, collections, or supportedInteractions.",
|
|
262
286
|
);
|
|
263
287
|
return { releases, leaderboards, referrals, ...catalog };
|
|
264
288
|
}
|
|
@@ -20,16 +20,54 @@ curl -fsS https://snippets.domino.run/r/registry.json
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Preserve existing customized files; adapt those in place when the recipe is already installed. Use the project's package manager equivalent when it is not pnpm.
|
|
23
|
-
3. Read the installed `docs/snippets/<name>.md`. Adapt the source to the requested behavior, choose unique
|
|
23
|
+
3. Read the installed `docs/snippets/<name>.md`. Adapt the source to the requested behavior, choose unique authoring keys for new quests, and configure settings, rewards, and verification prerequisites. Keep existing authoring keys stable and verification source out of the browser bundle.
|
|
24
24
|
4. Confirm the source matches `discover` in `relay.json`. Move it to a matching directory for custom layouts, or add an explicit entry if discovery is disabled or unsupported by the installed CLI. Run the project's checks and `domino build --json`, then exercise the requested participant behavior using the verification workflow in the Domino skill. Installation only copies source; publication is a separate step below.
|
|
25
25
|
|
|
26
26
|
If no recipe fits or the catalog is unavailable, author with the installed SDK declarations and existing project examples. Report an unavailable catalog as unchecked, rather than claiming no matching snippet exists. Snippets are editable starting points; check compatibility with the project's installed SDK before changing package versions.
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## Upgrade existing authoring code
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Existing `id` properties on authoring declarations still work as aliases for `key`. They name source declarations; they do not choose server resource IDs. Keep the original value when migrating. Providing different `id` and `key` values fails. New code should use `key`.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Run these commands from the project directory after upgrading the SDK and CLI:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
domino migrate
|
|
36
|
+
domino migrate --write
|
|
37
|
+
domino check --json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`migrate` previews a diff without writing files or contacting Domino. `--write` applies the changes. It renames authoring identity properties and collection declarations, and proposes `pointLedgers` entries for statically known ledger keys. Existing server IDs are not renamed. Review proposed ledger names before deploying. You may keep referencing a ledger that already exists remotely without adding a local declaration.
|
|
41
|
+
|
|
42
|
+
The migration follows configured and discovered entry modules and their local imports. It recognizes SDK import aliases and namespace imports, preserves nested tier and rule IDs, and does not execute source. Indirect objects, spreads, and dynamic ledger references receive manual instructions. It does not rewrite dependencies, unconfigured source, or files outside the project directory. Resolve conflicts before applying; a conflicting identity prevents all writes. Repeating the command after a completed migration produces no further diff.
|
|
43
|
+
|
|
44
|
+
`domino check --json` includes a `diagnostics` array with `code`, `severity`, `file`, `line`, `column`, `message`, and `suggestion` for source findings. `AUTHORING_ID_DEPRECATED` is a non-blocking migration warning. `AUTHORING_IDENTITY_CONFLICT` and `AUTHORING_KEY_MISSING` fail the check. Dynamic references and missing local ledger declarations are warnings because the selected ledger may already exist remotely. Manifest validation errors appear on stderr with `code: "MANIFEST_INVALID"` and field paths in `details`. Agents should follow the reported suggestion and rerun the check; never invent a replacement key for an existing resource.
|
|
45
|
+
|
|
46
|
+
## Resource IDs and authoring keys
|
|
47
|
+
|
|
48
|
+
Use the IDs returned by Domino when referencing projects, quests, point ledgers, rewards, and inventory items. Creation requests do not accept a new resource's ID. The server assigns an opaque, prefixed ID. Existing stored IDs remain valid after upgrade.
|
|
49
|
+
|
|
50
|
+
In source, give `defineQuest`, `defineQuestType`, `defineTieredReward`, `defineLeaderboard`, and `defineReferral` a stable `key`. Collection declarations in `relay.json` also use `key`. Domino maps each key to a resource ID within the project and environment. Redeploying the same key updates the same resource; changing the key creates another resource. Display titles are independent of identity. Quest prerequisites can reference authoring keys, and participant quest and collection lookups accept keys or returned IDs.
|
|
51
|
+
|
|
52
|
+
Create point ledgers in Console **Rewards → Point ledgers → New ledger**, with `management.points.createLedger({ name, description })`, or declare them in `relay.json`:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"pointLedgers": [
|
|
57
|
+
{ "key": "community", "name": "XP", "description": "Lifetime progress" }
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Merge this property into the existing manifest. Then use `points("community", 100)` or the returned ledger ID. A deployment previews ledger declarations with the other resources and publishes them together. An unknown ledger reference fails validation instead of creating a balance. `management.points.ledgers()` lists the available ledgers.
|
|
63
|
+
|
|
64
|
+
For a selectable ledger, use `settings.resource({ label: "Point ledger", source: pointLedgers() })`. Import `pointLedgers` from `@domino-sdk/relay/authoring`. An empty selection keeps a quest inactive until an operator chooses a ledger.
|
|
65
|
+
|
|
66
|
+
The management SDK's `createProject`, `points.createLedger`, `inventory.createItem`, and `catalog.allocateQuest` helpers generate an `actionId` when omitted. They retry one uncertain network or server failure with that same key. If creation still fails, `CreationError.actionId` lets you retry the same input explicitly. For retries across process restarts, generate and persist an action ID before calling the helper. Direct HTTP creation requests still require `actionId`.
|
|
67
|
+
|
|
68
|
+
Use `actionId` to make a creation request retryable. Reuse the same action ID and payload after an uncertain response; a different payload with the same action ID fails. The action ID is not the resource ID. Provider subjects, observation delivery IDs, completion keys, and nested quiz, tier, budget, and referral-rule keys keep their own scopes.
|
|
69
|
+
|
|
70
|
+
For pickup inventory, supply a label for each new variant and omit `item`; saving returns its assigned item ID. Reuse that ID to share existing stock. For standalone stock, type a name in the Console Stock item picker and choose **Create item “NAME”**, or use `management.inventory.createItem({ name })`. Both return a server-assigned item ID. Stock adjustments accept existing item IDs only.
|
|
33
71
|
|
|
34
72
|
## Resource and date/time settings
|
|
35
73
|
|
|
@@ -59,13 +97,13 @@ Fixture providers simulate verification. `workers-ai` requires a configured serv
|
|
|
59
97
|
|
|
60
98
|
## Leaderboard definitions
|
|
61
99
|
|
|
62
|
-
Default-export `defineLeaderboard({
|
|
100
|
+
Default-export `defineLeaderboard({ key, title, balance, score, access, group })` from `@domino-sdk/relay/authoring`. Add `{ "entry": "src/leaderboard.ts" }` to `leaderboards` in `relay.json`; optional `exportName` selects a named export. Deploy through the existing project bundle workflow. Definitions and standings previews appear in Console Releases before live publication.
|
|
63
101
|
|
|
64
102
|
```ts
|
|
65
103
|
import { defineLeaderboard } from "@domino-sdk/relay/authoring";
|
|
66
104
|
|
|
67
105
|
export default defineLeaderboard({
|
|
68
|
-
|
|
106
|
+
key: "weekly",
|
|
69
107
|
title: "Weekly contributors",
|
|
70
108
|
balance: "community",
|
|
71
109
|
access: "participants",
|
|
@@ -90,7 +128,7 @@ import { createManagementClient } from "@domino-sdk/relay-cli";
|
|
|
90
128
|
const management = createManagementClient({
|
|
91
129
|
baseUrl: "https://relay.domino.run",
|
|
92
130
|
organization: "my-organization",
|
|
93
|
-
project:
|
|
131
|
+
project: process.env.DOMINO_PROJECT_ID!,
|
|
94
132
|
environment: "test",
|
|
95
133
|
getToken: async () => process.env.DOMINO_MANAGEMENT_KEY!,
|
|
96
134
|
});
|
|
@@ -173,3 +211,5 @@ Author the entry quest with `manual({ actor: "staff" })` and no evidence input.
|
|
|
173
211
|
## Hosted server applications
|
|
174
212
|
|
|
175
213
|
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.
|
|
214
|
+
|
|
215
|
+
Ledger names can repeat. Edit names and descriptions in Console, or call `management.pointLedgers.save({ id, name, description })` to update an existing ledger. `settings.pointLedger` also renders a searchable picker; existing text settings need a new type version and instance upgrade.
|
|
@@ -6,6 +6,8 @@ Inspect the app's framework, package manager, workspace boundaries, authenticati
|
|
|
6
6
|
|
|
7
7
|
Use the Domino project selected by the user. If no remote project exists, create it in Console. `domino create` provisions a hosted repository and is not the existing-app integration path.
|
|
8
8
|
|
|
9
|
+
Use the project ID returned by Console. Domino assigns it when the project is created; the project name is a separate display label. Do not invent a project ID or derive it from the name.
|
|
10
|
+
|
|
9
11
|
From the intended package or project root, run `domino init --project <id>` to create relay.json and RELAY.md. Init verifies access and does not install packages or generate sample quests. If these files already exist, inspect the existing connection and continue from it. Init deliberately refuses to overwrite them.
|
|
10
12
|
|
|
11
13
|
Run `domino agents install` to install this guidance for both Codex and Claude, or select one with `--agent codex` or `--agent claude`. The installer leaves existing AGENTS.md and CLAUDE.md files intact. It can resume missing files, but stops before replacing customized Domino guidance.
|
|
@@ -22,4 +24,4 @@ Mount the adapter at `/relay/*` and use `createBrowserRelayClient({ baseUrl: "/r
|
|
|
22
24
|
|
|
23
25
|
Read [authoring](authoring.md) when adding quests or collection slots. Run the app's normal checks and development server. Use its existing preview deployment process when available. `domino deploy` publishes Domino behavior; it does not deploy the app. `domino stage` requires a supported Domino-hosted static repository, so it is not a general deployment command for existing apps.
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
When upgrading an existing integration, run `domino migrate` to inspect authoring changes, apply them with `domino migrate --write`, and run `domino check --json`. Keep existing authoring names. Legacy `id` remains accepted as an alias for `key`; do not recreate projects or replace stored IDs to perform this upgrade. See [authoring upgrades](authoring.md#upgrade-existing-authoring-code) for diagnostics and manual cases.
|
|
@@ -126,3 +126,7 @@ Call `relay.me.rewardDeliveries()` to read role deliveries for the signed-in mem
|
|
|
126
126
|
## Removed demo shortcuts
|
|
127
127
|
|
|
128
128
|
`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.
|
|
129
|
+
|
|
130
|
+
## Resource references
|
|
131
|
+
|
|
132
|
+
Store returned resource IDs as opaque strings. A quest's `quest` field and a collection's `id` are server-assigned. Authored resources also expose their stable `key`; `relay.quests.get(key)` and `relay.quests.collection(key)` resolve it. Console-created quests have no authoring key, so use their returned IDs. Continue supplying stable action IDs for retries; those values do not become resource IDs.
|
|
@@ -8,7 +8,7 @@ Run `domino capabilities referrals --json` against the installed release. Native
|
|
|
8
8
|
import { defineReferral, points } from "@domino-sdk/relay/authoring";
|
|
9
9
|
|
|
10
10
|
export default defineReferral({
|
|
11
|
-
|
|
11
|
+
key: "friends",
|
|
12
12
|
title: "Invite friends",
|
|
13
13
|
qualification: { kind: "quest", quest: "welcome" },
|
|
14
14
|
rewards: { inviter: [points("community", 25)] },
|