@domino-sdk/relay-cli 0.3.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/capabilities.json +45 -20
- package/cli/commands/discovery.mjs +23 -4
- package/cli/commands/hosting.mjs +7 -8
- package/cli/commands/project.mjs +7 -1
- package/cli/commands/staging.mjs +2 -2
- package/cli/connected-dev.mjs +4 -2
- package/cli/discovery.mjs +62 -0
- package/cli/doctor.mjs +24 -3
- package/cli/git.mjs +38 -7
- package/cli/package-manager.mjs +31 -0
- package/cli/project.mjs +138 -37
- package/cli/runtime.mjs +8 -1
- package/cli/skills/domino/SKILL.md +36 -6
- package/cli/skills/domino/references/authoring.md +109 -2
- package/cli/skills/domino/references/existing-app.md +2 -0
- package/cli/skills/domino/references/hosted.md +14 -2
- package/cli/skills/domino/references/participant-api.md +55 -2
- package/cli/skills/domino/references/referrals.md +57 -8
- package/cli/skills/domino/references/troubleshooting.md +18 -0
- package/cli.mjs +4 -1
- package/dist/index.d.ts +1070 -4
- package/dist/index.js +191 -7
- package/package.json +2 -2
|
@@ -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
|
|
|
@@ -62,3 +62,56 @@ Submission results are `submitted` with an attempt, or `quest-updated` with `lat
|
|
|
62
62
|
`controller.status()` returns `empty`, `needs-review`, `pending` with upload/claim stage, or `submitted` with attempt ID. `needs-review` means the saved draft needs the participant's review, not that a moderator is reviewing it. Use `resume()` to recover a saved operation after reload or a lost response. Use `relay.quests.retry(view)` only for a failed verification attempt. Do not invent a new submission to recover an uncertain response.
|
|
63
63
|
|
|
64
64
|
Use `relay.me.observeProgress(listener)` for balances, completion and rewards. Dispose observers on unmount. Sign-in changes clear old participant observations. Render 401 as a sign-in request, 403 as an access issue, and transient failures with a retry that preserves the saved draft. IndexedDB and Web Locks must be available for durable browser submissions.
|
|
65
|
+
|
|
66
|
+
## Leaderboard reads
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { createBrowserRelayClient } from "@domino-sdk/relay/browser";
|
|
70
|
+
|
|
71
|
+
const relay = createBrowserRelayClient({ baseUrl: "/relay" });
|
|
72
|
+
const boards = await relay.leaderboards.list();
|
|
73
|
+
const page = await relay.leaderboards.standings("weekly", { limit: 25 });
|
|
74
|
+
for (const row of page.items) {
|
|
75
|
+
console.log(row.participant, row.rank, row.name, row.score);
|
|
76
|
+
}
|
|
77
|
+
console.log("My rank:", page.me?.rank);
|
|
78
|
+
console.log("Nearby:", page.nearby);
|
|
79
|
+
if (page.nextCursor) {
|
|
80
|
+
const nextPage = await relay.leaderboards.standings("weekly", {
|
|
81
|
+
limit: 25,
|
|
82
|
+
cursor: page.nextCursor,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Use `client.leaderboards.list()` and `client.leaderboards.standings(id, { limit, cursor })` for project-scoped standings. Results include `items`, `me`, `nearby`, resolved `period`, `calculatedAt`, `lastEntryAt`, and `nextCursor`. Equal scores share ranks. Restart pagination on HTTP 409; scores or eligibility may have changed. Private boards require a participant session. Public boards are explicitly published with public access and expose generated board-specific display aliases, not account identities. Both the participant proxy and existing-app bridge support these GET routes.
|
|
88
|
+
|
|
89
|
+
Never expose management credentials to implement a leaderboard. Points writes and moderation use the scoped management API from trusted servers or Console. Cross-project balances, prize delivery, and finalized competition results are not supported by the leaderboard API.
|
|
90
|
+
|
|
91
|
+
Render `row.rank` as returned, rather than using the array index; ties share ranks such as `1, 2, 2, 4`. Use `row.participant` as the board-specific row key. `me` is null for anonymous or unranked participants, and `nearby` contains the member plus up to two rows on either side. No qualifying points means no ranked row. Display an empty state when `items` is empty.
|
|
92
|
+
|
|
93
|
+
Cursors expire after 15 minutes. On HTTP 409, discard accumulated pages and fetch the first page again. `calculatedAt` is the calculation time; `lastEntryAt` is the latest received balance entry, not evidence that an external provider has synchronized. Public rows contain generated names, not account names or avatars.
|
|
94
|
+
|
|
95
|
+
## Referral invitations and progress
|
|
96
|
+
|
|
97
|
+
Use `relay.referrals.summary()` for the signed-in member's invitation code, attributed and qualified counts, milestone progress, and earned/settled/outstanding accounts. Use `accept({ code, actionId })` after sign-in and before the first quest completion. Preserve the code through sign-in and reuse input on uncertain retries. `history(before?)` reads paginated payout activity. Read [referrals](referrals.md) for authoring, qualification, corrections, and verification. The participant proxy and existing-app bridge route these operations with session-derived identity.
|
|
98
|
+
|
|
99
|
+
## Connected accounts
|
|
100
|
+
|
|
101
|
+
Project integrations and participant identities are separate. An organization administrator connects Discord in Console **Integrations**, selects a server, and installs the managed bot. Each project/environment has one server binding. See [project integrations](https://console.domino.run/docs/build/integrations).
|
|
102
|
+
|
|
103
|
+
Participants who sign in with Discord already have the identity needed for verification. If they use another login, keep their current member signed in, call `relay.auth.start("discord", "connect")`, and navigate to the returned `url`. This requests only `identify`, not membership-reading access. `relay.auth.accounts()` lists the provider's configuration, link status, subject, and identity revision. It never returns credentials. Quest views expose required participant identities in `connections`, derived from their declared integrations.
|
|
104
|
+
|
|
105
|
+
To choose a different identity, first call `relay.auth.disconnect("discord")`. This unlinks the active Discord identity across the member's organization and environment. Ownership, sign-in eligibility, and earned rewards stay intact; another member cannot claim the identity. It does not disconnect the project's Discord server. Both operations retain historical verification facts.
|
|
106
|
+
|
|
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
|
+
|
|
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,15 +1,64 @@
|
|
|
1
|
-
# Build a referral
|
|
1
|
+
# Build a referral program
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run `domino capabilities referrals --json` against the installed release. Native referrals own invitation codes, project-scoped attribution, qualification, fixed rewards, milestones, and one-level inviter bonuses. The participant app owns invitation presentation and retaining the code through sign-in. Use the existing project ledger and publication flow.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Author and publish
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```ts
|
|
8
|
+
import { defineReferral, points } from "@domino-sdk/relay/authoring";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
export default defineReferral({
|
|
11
|
+
id: "friends",
|
|
12
|
+
title: "Invite friends",
|
|
13
|
+
qualification: { kind: "quest", quest: "welcome" },
|
|
14
|
+
rewards: { inviter: [points("community", 25)] },
|
|
15
|
+
milestones: [
|
|
16
|
+
{ id: "five", count: 5, rewards: [points("community", 250)] },
|
|
17
|
+
],
|
|
18
|
+
bonuses: [
|
|
19
|
+
{ id: "share", balance: "community", basisPoints: 1000, quests: ["welcome"] },
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
```
|
|
10
23
|
|
|
11
|
-
|
|
24
|
+
Register `{ "entry": "src/referrals.ts" }` in `relay.json` under `referrals`; `exportName` selects a named export. `domino check` bundles definitions without executing author code locally. Preview and publish with the existing project deployment commands. Live publication uses the reviewed Console bundle. Omitted programs remain published; `enabled: false` stops future qualification and bonuses. Keep program and rule IDs stable.
|
|
12
25
|
|
|
13
|
-
|
|
26
|
+
Choose qualification explicitly: `signup` qualifies when a signed-in participant accepts an invitation; `quest` requires the named quest; `external` requires the trusted management qualification command. Each program counts distinct qualified friends. Existing points and tier rewards are accepted for inviter, invitee, and milestones. Each referral tier reward uses its own retained staff-handover entitlement. Different friends can earn separate items; requalification reuses the original entitlement.
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
`basisPoints: 1000` means an additional 10%; the invitee retains their full award. Omitted quest filters include all quests in that balance. Optional `inviterGroup` restricts eligibility using the same named groups as leaderboards. Optional `durationDays` and `maxPoints` limit each invited friend's bonus. Fractions accumulate per friend, rule, and balance. Percentage rules must not overlap for the same source; group names cannot prove disjointness. Fixed rewards and milestones may coexist with a bonus.
|
|
29
|
+
|
|
30
|
+
## Integrate the participant flow
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { createBrowserRelayClient } from "@domino-sdk/relay/browser";
|
|
34
|
+
|
|
35
|
+
const relay = createBrowserRelayClient({ baseUrl: "/relay" });
|
|
36
|
+
const summary = await relay.referrals.summary();
|
|
37
|
+
const invitation = new URL("https://app.example.com/join");
|
|
38
|
+
invitation.searchParams.set("ref", summary.code);
|
|
39
|
+
|
|
40
|
+
// After the invited friend signs in, using the code retained by the app:
|
|
41
|
+
const code = "ref_code_from_invitation";
|
|
42
|
+
await relay.referrals.accept({ code, actionId: "accept-invitation" });
|
|
43
|
+
const activity = await relay.referrals.history();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Use the configured participant proxy or identity bridge. Domino derives the invited member from their authenticated session. Accept before their first completed quest. The first accepted inviter is permanent; self-referrals, cycles, and cross-project codes fail. Link visits alone do not establish attribution. Retry uncertain requests with the same action ID and payload. Do not discard the retained invitation until acceptance succeeds or the user dismisses a terminal error.
|
|
47
|
+
|
|
48
|
+
Render attributed and per-program qualified counts, milestone progress, and earned/settled/outstanding point accounts. Settled points can include amounts offset against correction debt. Fetch older history with `referrals.history(activity.next)` when `next` is non-null. These reads reveal no invitee identities or detailed activity.
|
|
49
|
+
|
|
50
|
+
## External activity and moderation
|
|
51
|
+
|
|
52
|
+
On a trusted backend, construct `createManagementClient` from `@domino-sdk/relay-cli` with the project/environment scope and server key. Use `management.referrals.control({ kind: "qualify", actionId, member, program, qualified: true, reason })` after verifying an external condition. Use the same command with `qualified: false` for invalidation. These commands require `publish`; reads require `read`.
|
|
53
|
+
|
|
54
|
+
External `management.points.update` awards generate a bonus only with both `referralEligible: true` and a matching bonus rule with `external: true`. They require `points` permission. Preserve the source event's action ID. Source points and referral bonuses are atomic. Adjustments, absolute totals, spending, and referral payouts do not generate bonuses.
|
|
55
|
+
|
|
56
|
+
Use `control({ kind: "suspend", actionId, member, suspended, reason })` to stop or resume future payouts while preserving attribution. Console Referrals exposes these controls, qualifications, payment history, and outstanding corrections. No missed payouts accumulate during suspension or ineligibility.
|
|
57
|
+
|
|
58
|
+
Reversals use the original rate. A fully reversed qualifying quest award revokes qualification; non-point invalidation requires staff action. Correct available points first and offset outstanding corrections against future referral payouts in that balance. Ordinary future earnings are not seized. Restore the original fixed/milestone entitlement when eligibility returns, cancelling correction debt before restoring recovered points. Do not issue duplicate rewards. Invalidated tier entitlements require staff review before handover; `resolve-reward` clears that review after the original reward has been reconciled.
|
|
59
|
+
|
|
60
|
+
New rates and eligibility apply when an award is accepted, even if its effective date is backdated. Qualifying quest points count; earlier earnings do not. Requalification does not replay past percentage earnings. Fixed rewards already issued retain their payload and milestone threshold.
|
|
61
|
+
|
|
62
|
+
## Prove the flow
|
|
63
|
+
|
|
64
|
+
Use two signed-in test participants. Share and accept a code, complete the qualifying quest, and verify both balances and qualified counts after reload. Repeat the acceptance and source event to prove deduplication. Check self-referral rejection, a second inviter, an ineligible source, suspension, and a reversal after the inviter spends their bonus. Verify desktop and mobile UI states, including unqualified and outstanding-correction states. Report the project, environment, program, observed balances, and any unverified external condition. Simulated quest providers prove the flow, not the external activity.
|
|
@@ -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
|
);
|