@econ-v1/rpc 6.7.0 → 6.11.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/dist/client-device-pending-contract.d.ts +14 -0
- package/dist/client-device-pending-contract.d.ts.map +1 -0
- package/dist/client.d.ts +15 -13
- package/dist/client.d.ts.map +1 -1
- package/dist/device-boot-contract.d.ts +7 -0
- package/dist/device-boot-contract.d.ts.map +1 -0
- package/dist/device-cleanup-contract.d.ts +17 -0
- package/dist/device-cleanup-contract.d.ts.map +1 -0
- package/dist/dialog-contract.d.ts +26 -0
- package/dist/dialog-contract.d.ts.map +1 -0
- package/dist/errors.d.ts +33 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +144 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/message-port-client.d.ts +134 -16
- package/dist/message-port-client.d.ts.map +1 -1
- package/dist/node-app-memory-contract.d.ts +10 -0
- package/dist/node-app-memory-contract.d.ts.map +1 -0
- package/dist/onboarding/discovery-contract.d.ts +61 -34
- package/dist/onboarding/discovery-contract.d.ts.map +1 -1
- package/dist/onboarding/onboarding-contract.d.ts +119 -27
- package/dist/onboarding/onboarding-contract.d.ts.map +1 -1
- package/dist/onboarding/owner-claim-contract.d.ts +50 -0
- package/dist/onboarding/owner-claim-contract.d.ts.map +1 -0
- package/dist/pairing-status-contract.d.ts +40 -0
- package/dist/pairing-status-contract.d.ts.map +1 -0
- package/dist/protocol.d.ts +50 -12
- package/dist/protocol.d.ts.map +1 -1
- package/dist/stage-context.d.ts +31 -16
- package/dist/stage-context.d.ts.map +1 -1
- package/dist/sync-cursors-contract.d.ts +6 -0
- package/dist/sync-cursors-contract.d.ts.map +1 -0
- package/dist/system-notice-contract.d.ts +70 -0
- package/dist/system-notice-contract.d.ts.map +1 -0
- package/dist/transport-contract.d.ts +11 -0
- package/dist/transport-contract.d.ts.map +1 -0
- package/dist/transport.d.ts +2 -2
- package/dist/transport.d.ts.map +1 -1
- package/dist/trust-contract.d.ts +23 -0
- package/dist/trust-contract.d.ts.map +1 -0
- package/package.json +16 -12
- package/src/client-device-pending-contract.js +6 -0
- package/src/client.js +26 -0
- package/src/device-boot-contract.js +4 -0
- package/src/device-cleanup-contract.js +9 -0
- package/src/dialog-contract.js +6 -0
- package/src/errors.js +111 -0
- package/src/index.js +194 -0
- package/src/message-port-client.js +628 -0
- package/src/node-app-memory-contract.js +10 -0
- package/src/onboarding/discovery-contract.js +83 -0
- package/src/onboarding/onboarding-contract.js +106 -0
- package/src/onboarding/owner-claim-contract.js +59 -0
- package/src/pairing-status-contract.js +36 -0
- package/src/protocol.js +758 -0
- package/src/stage-context.js +27 -0
- package/src/sync-cursors-contract.js +3 -0
- package/src/system-notice-contract.js +64 -0
- package/src/transport-contract.js +11 -0
- package/src/transport.js +6 -0
- package/src/trust-contract.js +36 -0
- package/dist/client.js +0 -2
- package/dist/client.js.map +0 -1
- package/dist/errors.js +0 -57
- package/dist/errors.js.map +0 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +0 -1
- package/dist/message-port-client.js +0 -365
- package/dist/message-port-client.js.map +0 -1
- package/dist/onboarding/discovery-contract.js +0 -42
- package/dist/onboarding/discovery-contract.js.map +0 -1
- package/dist/onboarding/onboarding-contract.js +0 -37
- package/dist/onboarding/onboarding-contract.js.map +0 -1
- package/dist/protocol.js +0 -295
- package/dist/protocol.js.map +0 -1
- package/dist/stage-context.js +0 -2
- package/dist/stage-context.js.map +0 -1
- package/dist/transport.js +0 -2
- package/dist/transport.js.map +0 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { isJsonObject } from "../errors.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The #1815 interim "search the node on the LAN" seam. Client-only vertical slice: the
|
|
7
|
+
* discovery screen probes explicit candidate addresses (manually entered today, a signed
|
|
8
|
+
* bootstrap descriptor's `lan_candidates` in the Phase-2 follow-on — see
|
|
9
|
+
* `docs/superpowers/plans/2026-07-25-client-node-lan-discovery.md`) and the kernel reports
|
|
10
|
+
* live per-candidate progress. Mirrors `./onboarding-contract.js`'s idiom: locked wire-name
|
|
11
|
+
* constants + JSON-safe view/payload types + a runtime validator per command payload.
|
|
12
|
+
*
|
|
13
|
+
* `DISCOVERY_QUERY` is both a one-shot `query()` and a `liveQuery()` name (the kernel's
|
|
14
|
+
* `LiveQueryDefinition` row type is `DiscoveryCandidateView`, keyed by `address`) — the same
|
|
15
|
+
* dual usage every other query in `@econ-v1/kernel`'s `LiveQueryEngine` supports.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export const DISCOVERY_QUERY = "onboarding.discovery.v1";
|
|
19
|
+
export const DISCOVERY_PROBE_COMMAND = "onboarding.discovery.probe.v1";
|
|
20
|
+
export const DISCOVERY_PROMOTE_COMMAND = "onboarding.discovery.promote.v1";
|
|
21
|
+
export const DISCOVER_NODE_COMMAND = "onboarding.discovery.discover-node.v1";
|
|
22
|
+
|
|
23
|
+
/** @typedef {"probing" | "reachable" | "verified" | "unreachable"} DiscoveryCandidateStatus */
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The verified node's non-secret projection: nothing here is (or derives) a routable
|
|
27
|
+
* address, macaroon, or key — only what the found-node card renders.
|
|
28
|
+
*
|
|
29
|
+
* @typedef {{
|
|
30
|
+
* readonly displayName: string,
|
|
31
|
+
* readonly maskedNodeId: string
|
|
32
|
+
* }} DiscoveryFoundNode
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {{
|
|
37
|
+
* readonly address: string,
|
|
38
|
+
* readonly node?: DiscoveryFoundNode,
|
|
39
|
+
* readonly status: DiscoveryCandidateStatus
|
|
40
|
+
* }} DiscoveryCandidateView
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/** @typedef {{ readonly addresses: readonly string[] }} ProbeCandidatesPayload */
|
|
44
|
+
/** @typedef {{ readonly address: string }} PromoteCandidatePayload */
|
|
45
|
+
/** @typedef {{ readonly address: string }} DiscoverNodePayload */
|
|
46
|
+
/** @typedef {{ readonly address: string, readonly id: string, readonly path: "lan-direct" }} NodeDiscoveryCandidate */
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {
|
|
49
|
+
* | { readonly status: "verified", readonly candidate: NodeDiscoveryCandidate }
|
|
50
|
+
* | { readonly status: "reached-but-invalid", readonly reason: string }
|
|
51
|
+
* | { readonly status: "unreachable-or-untrusted" }
|
|
52
|
+
* } NodeDiscoveryResult
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {unknown} value
|
|
57
|
+
* @returns {value is ProbeCandidatesPayload}
|
|
58
|
+
*/
|
|
59
|
+
export function isProbeCandidatesPayload(value) {
|
|
60
|
+
if (!isJsonObject(value)) return false;
|
|
61
|
+
const { addresses } = value;
|
|
62
|
+
return Array.isArray(addresses)
|
|
63
|
+
&& addresses.length > 0
|
|
64
|
+
&& addresses.every((item) => typeof item === "string" && item.trim().length > 0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {unknown} value
|
|
69
|
+
* @returns {value is PromoteCandidatePayload}
|
|
70
|
+
*/
|
|
71
|
+
export function isPromoteCandidatePayload(value) {
|
|
72
|
+
if (!isJsonObject(value)) return false;
|
|
73
|
+
return typeof value.address === "string" && value.address.trim().length > 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {unknown} value
|
|
78
|
+
* @returns {value is DiscoverNodePayload}
|
|
79
|
+
*/
|
|
80
|
+
export function isDiscoverNodePayload(value) {
|
|
81
|
+
if (!isJsonObject(value)) return false;
|
|
82
|
+
return typeof value.address === "string" && value.address.trim().length > 0;
|
|
83
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @typedef {"new" | "pair" | "wifi" | "expired" | "lost"} OnboardingScenario */
|
|
3
|
+
/** @typedef {"scan" | "invite" | "identity" | "matchcode" | "handoff" | "done"} OnboardingScreen */
|
|
4
|
+
/** @typedef {"connect" | "install" | "secure" | "identify" | "ready"} OnboardingPhase */
|
|
5
|
+
/** @typedef {| "bootstrap-required" | "needs-onboarding" | "needs-pairing" | "ready"} OnboardingLifecycleState */
|
|
6
|
+
/** @typedef {{ readonly invitationsEnabled?: boolean; readonly scenario?: OnboardingScenario; readonly state: OnboardingLifecycleState; }} OnboardingLifecycleStatus */
|
|
7
|
+
/** @typedef {{ readonly id: string; readonly maskedNodeId: string; readonly name: string; readonly ownership: "checking" | "owned" | "unowned"; }} OnboardingDiscoveryCandidate */
|
|
8
|
+
/** @typedef {| "waiting" | "approved" | "rejected" | "expired" | "cancelled"} OnboardingApprovalState */
|
|
9
|
+
/** @typedef {{ readonly approvalExpiresAt?: string; readonly approvalState?: OnboardingApprovalState; readonly candidates?: readonly OnboardingDiscoveryCandidate[]; readonly matchcodeDigits?: readonly string[]; readonly phase: OnboardingPhase; readonly reasonCode?: string; readonly scenario: OnboardingScenario; readonly screen: OnboardingScreen; }} OnboardingView */
|
|
10
|
+
/** @typedef {OnboardingView} OnboardingEvent */
|
|
11
|
+
/** @typedef {(typeof ONBOARDING_STEP_IDS)[number]} OnboardingStepId */
|
|
12
|
+
/** @typedef {| "onboard_qr" | "onboard_open_browser" | "onboard_wifi_setup" | "wifi_setup_connecting" | "onboard_home_qr" | "onboard_wifi_joined" | "onboard_working" | "onboard_code" | "onboard_trusted" | "onboard_rejected"} OnboardingLcdScreenId */
|
|
13
|
+
/** @typedef {"open" | "wep" | "wpa2" | "wpa3"} OnboardingWifiSecurity */
|
|
14
|
+
/**
|
|
15
|
+
* Onboarding access-code rail (2026-08-10 design): a code-active node refused this device's
|
|
16
|
+
* claim outright — either the code is already bound to a different device
|
|
17
|
+
* (`code-already-bound`) or the supplied/missing code did not match (`access-code-invalid`,
|
|
18
|
+
* covering both `ACCESS_CODE_INVALID` and `ACCESS_CODE_REQUIRED`).
|
|
19
|
+
* @typedef {| "approval-expired" | "approval-rejected" | "wifi-credentials-rejected" | "wifi-scan-failed" | "wifi-unreachable" | "invitation-invalid" | "claim-failed" | "code-already-bound" | "access-code-invalid"} OnboardingReasonCode
|
|
20
|
+
*/
|
|
21
|
+
/** @typedef {{ readonly security: OnboardingWifiSecurity; readonly signal: number; readonly ssid: string; }} OnboardingNetwork */
|
|
22
|
+
/** @typedef {{ readonly lcdScreenId: OnboardingLcdScreenId; readonly phase: OnboardingPhase; readonly reasonCode?: OnboardingReasonCode; readonly stepIndex: number; readonly totalSteps: number; }} OnboardingFlowBase */
|
|
23
|
+
/** @typedef {| (OnboardingFlowBase & { readonly networks: readonly OnboardingNetwork[]; readonly scanning: boolean; readonly stepId: "choose-network"; }) | (OnboardingFlowBase & { readonly security: OnboardingWifiSecurity; readonly ssid: string; readonly stepId: "network-password"; }) | (OnboardingFlowBase & { readonly approvalState: OnboardingApprovalState; readonly digits: readonly string[]; readonly remainingSeconds: number; readonly stepId: "match-code"; }) | (OnboardingFlowBase & { readonly stepId: "invitation" }) | (OnboardingFlowBase & { readonly displayName: string; readonly displayNameLocked: boolean; readonly stepId: "identity"; readonly submitting: boolean; }) | (OnboardingFlowBase & { readonly sessionEstablished: boolean; readonly stepId: "trusted"; }) | (OnboardingFlowBase & { readonly ssid: string; readonly stepId: "handoff"; }) | (OnboardingFlowBase & { readonly stepId: Exclude< OnboardingStepId, | "choose-network" | "handoff" | "identity" | "invitation" | "match-code" | "network-password" | "trusted" >; })} OnboardingFlowView */
|
|
24
|
+
/** @typedef {{ readonly children?: readonly OnboardingLcdScreenTree[]; readonly id: string; readonly type: string; readonly [extra: string]: unknown; }} OnboardingLcdScreenTree */
|
|
25
|
+
/** @typedef {Readonly<Record<string, number | string>>} OnboardingLcdScreenState */
|
|
26
|
+
/** @typedef {"not-registered" | "unreachable"} OnboardingLcdUnavailableReason */
|
|
27
|
+
/** @typedef {{ readonly awaitingNode: boolean; readonly physicalTapOnly: boolean; readonly screenId: OnboardingLcdScreenId; readonly state: OnboardingLcdScreenState; }} OnboardingLcdScreenBase */
|
|
28
|
+
/** @typedef {| (OnboardingLcdScreenBase & { readonly kind: "authored" }) | (OnboardingLcdScreenBase & { readonly kind: "tree"; readonly tree: OnboardingLcdScreenTree }) | (OnboardingLcdScreenBase & { readonly kind: "unavailable"; readonly reason: OnboardingLcdUnavailableReason; })} OnboardingLcdScreenView */
|
|
29
|
+
/** @typedef {| { readonly kind: "advance" } | { readonly kind: "select-network"; readonly security: OnboardingWifiSecurity; readonly ssid: string; } | { readonly kind: "rescan-networks" } | { readonly kind: "reselect-network" } | { readonly kind: "submit-network-password"; readonly secret: string } | { readonly decision: "continue-in-browser" | "installed"; readonly kind: "install-decision" } | { readonly componentId: string; readonly kind: "lcd-tap" } | { readonly kind: "advance-discovery" } | { readonly code: string; readonly kind: "redeem-invitation" } | { readonly displayName: string; readonly kind: "submit-identity"; readonly username: string; } | { readonly kind: "submit-wifi"; readonly secret: string; readonly ssid: string } | { readonly kind: "retry-approval" } | { readonly kind: "reconcile-completion" } | { readonly kind: "choose-path"; readonly scenario: OnboardingScenario } | { readonly kind: "cancel" }} OnboardingCommand */
|
|
30
|
+
import { isJsonObject } from "../errors.js";
|
|
31
|
+
export const ONBOARDING_STATUS_QUERY = /** @type {const} */ ("onboarding.status.v1");
|
|
32
|
+
export const ONBOARDING_COMMAND = /** @type {const} */ ("onboarding.command.v1");
|
|
33
|
+
export const ONBOARDING_EVENTS = /** @type {const} */ ("onboarding.events.v1");
|
|
34
|
+
export const ONBOARDING_FLOW_QUERY = /** @type {const} */ ("onboarding.flow.v1");
|
|
35
|
+
export const ONBOARDING_LCD_SCREEN_QUERY = /** @type {const} */ ("onboarding.lcd-screen.v1");
|
|
36
|
+
/**
|
|
37
|
+
* The pager's declarative order. `stepIndex`/`totalSteps` are DERIVED from this list with
|
|
38
|
+
* gated rows removed (`invitation` today), so invitations-off yields ten steps and shifts
|
|
39
|
+
* every later index with no second table and no hole — the total can never be a constant.
|
|
40
|
+
*
|
|
41
|
+
* `begin-setup` ("Connect") is absent by design: its discovery scan ran only after the shell
|
|
42
|
+
* had ALREADY reached the node over HTTP, so on the shipped path it discovered nothing and was
|
|
43
|
+
* auto-advanced. Its screen still exists off-rail for the unreachable-node bootstrap case —
|
|
44
|
+
* see `OnboardingFlowService`'s `STEP_TABLE` comment.
|
|
45
|
+
*/
|
|
46
|
+
export const ONBOARDING_STEP_IDS = /** @type {const} */ ([
|
|
47
|
+
"join-qr",
|
|
48
|
+
"open-browser",
|
|
49
|
+
"choose-network",
|
|
50
|
+
"network-password",
|
|
51
|
+
"handoff",
|
|
52
|
+
"way-back",
|
|
53
|
+
"install",
|
|
54
|
+
"match-code",
|
|
55
|
+
"invitation",
|
|
56
|
+
"identity",
|
|
57
|
+
"trusted",
|
|
58
|
+
]);
|
|
59
|
+
/** @param {unknown} value @returns {value is OnboardingCommand} */
|
|
60
|
+
export function isOnboardingCommand(value) {
|
|
61
|
+
if (!isJsonObject(value) || typeof value.kind !== "string")
|
|
62
|
+
return false;
|
|
63
|
+
switch (value.kind) {
|
|
64
|
+
case "redeem-invitation":
|
|
65
|
+
return typeof value.code === "string";
|
|
66
|
+
case "advance":
|
|
67
|
+
case "advance-discovery":
|
|
68
|
+
case "rescan-networks":
|
|
69
|
+
case "reselect-network":
|
|
70
|
+
case "retry-approval":
|
|
71
|
+
case "reconcile-completion":
|
|
72
|
+
case "cancel":
|
|
73
|
+
return true;
|
|
74
|
+
case "lcd-tap":
|
|
75
|
+
return typeof value.componentId === "string" && value.componentId.length > 0;
|
|
76
|
+
case "select-network":
|
|
77
|
+
return typeof value.ssid === "string"
|
|
78
|
+
&& value.ssid.length > 0
|
|
79
|
+
&& isOnboardingWifiSecurity(value.security);
|
|
80
|
+
case "submit-network-password":
|
|
81
|
+
return typeof value.secret === "string";
|
|
82
|
+
case "install-decision":
|
|
83
|
+
return value.decision === "installed" || value.decision === "continue-in-browser";
|
|
84
|
+
case "submit-identity":
|
|
85
|
+
return typeof value.username === "string"
|
|
86
|
+
&& typeof value.displayName === "string";
|
|
87
|
+
case "submit-wifi":
|
|
88
|
+
return typeof value.ssid === "string" && typeof value.secret === "string";
|
|
89
|
+
case "choose-path":
|
|
90
|
+
return isOnboardingScenario(value.scenario);
|
|
91
|
+
default:
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** @param {unknown} value @returns {value is OnboardingWifiSecurity} */
|
|
96
|
+
function isOnboardingWifiSecurity(value) {
|
|
97
|
+
return value === "open" || value === "wep" || value === "wpa2" || value === "wpa3";
|
|
98
|
+
}
|
|
99
|
+
/** @param {unknown} value @returns {value is OnboardingScenario} */
|
|
100
|
+
function isOnboardingScenario(value) {
|
|
101
|
+
return value === "new"
|
|
102
|
+
|| value === "pair"
|
|
103
|
+
|| value === "wifi"
|
|
104
|
+
|| value === "expired"
|
|
105
|
+
|| value === "lost";
|
|
106
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { isJsonObject } from "../errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* First-owner "claim this unclaimed node" seam (#1815 DID-blocker fix,
|
|
6
|
+
* `.superpowers/sdd/did-blocker-report.md`). Deliberately separate from the generic
|
|
7
|
+
* (unwired) `ONBOARDING_COMMAND` in `./onboarding-contract.ts` — the same precedent as
|
|
8
|
+
* `DISCOVER_NODE_COMMAND` in `./discovery-contract.ts`, which is also "wired separately"
|
|
9
|
+
* rather than folded into a bigger, not-yet-built generic seam. `prepare` and `complete`
|
|
10
|
+
* are two commands (not one) because a person may spend anywhere from seconds to
|
|
11
|
+
* minutes physically walking to the node between them; `client/domain`'s
|
|
12
|
+
* `OwnerClaimService` holds the pending claim between the two calls and only unlocks it
|
|
13
|
+
* once the node itself has reported the physical confirmation.
|
|
14
|
+
* @type {"onboarding.owner-claim.prepare.v1"}
|
|
15
|
+
*/
|
|
16
|
+
export const OWNER_CLAIM_PREPARE_COMMAND = "onboarding.owner-claim.prepare.v1";
|
|
17
|
+
export const OWNER_CLAIM_COMPLETE_COMMAND = "onboarding.owner-claim.complete.v1";
|
|
18
|
+
/**
|
|
19
|
+
* @param {unknown} value
|
|
20
|
+
* @returns {value is OwnerClaimCompletePayload}
|
|
21
|
+
*/
|
|
22
|
+
export function isOwnerClaimCompletePayload(value) {
|
|
23
|
+
if (!isJsonObject(value))
|
|
24
|
+
return false;
|
|
25
|
+
return typeof value.displayName === "string" && value.displayName.trim().length > 0
|
|
26
|
+
&& typeof value.username === "string" && value.username.trim().length > 0;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `claimed-unpaired` is a SUCCESS: the owner + DID exist on the node, but the completion
|
|
30
|
+
* response carried no usable session tokens (or adopting them did not produce a session), so
|
|
31
|
+
* the shell must fall back to its ordinary device-pairing flow. Collapsing it into `claimed`
|
|
32
|
+
* would make the UI assert a trust relationship the browser does not actually hold.
|
|
33
|
+
* @typedef {| "claimed"
|
|
34
|
+
* | "claimed-unpaired"
|
|
35
|
+
* | "not-prepared"
|
|
36
|
+
* | "rejected"
|
|
37
|
+
* | "timed-out"} OwnerClaimCompleteOutcome
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* `prepare` carries NO payload (design §6.1): the node's `onboarding-prepare` route only ever
|
|
41
|
+
* received `{ public_key_multibase }`, and the identity that used to ride along was stashed
|
|
42
|
+
* purely client-side. Moving it to `complete` is what lets the match code appear at step 10
|
|
43
|
+
* while identity is not collected until step 12 — nothing is written until physical access
|
|
44
|
+
* has been proven.
|
|
45
|
+
* @typedef {Object} OwnerClaimPrepareResult
|
|
46
|
+
* @property {boolean} lcdHardware
|
|
47
|
+
* @property {string} matchCode
|
|
48
|
+
* @property {string} operationHash Empty string when the node's confirmation gate is disabled.
|
|
49
|
+
* @property {boolean} requiresConfirmation
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {Object} OwnerClaimCompletePayload
|
|
53
|
+
* @property {string} displayName
|
|
54
|
+
* @property {string} username
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {Object} OwnerClaimCompleteResult
|
|
58
|
+
* @property {OwnerClaimCompleteOutcome} outcome
|
|
59
|
+
*/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
export const CLIENT_DEVICE_PAIRING_STATUS_QUERY = "client-device.pairing-status.v1";
|
|
3
|
+
/**
|
|
4
|
+
* Device-pairing wire-error codes shared verbatim across the Rust host and this TS client
|
|
5
|
+
* boundary (review recommendation, PR #2274). Canonical contract table:
|
|
6
|
+
* `docs/development/CAPABILITY_REGISTRY.md` § "Device-pairing wire-error contract" (mirrored
|
|
7
|
+
* on the Rust side by `core/domain/src/services/client_device_pairing_service.rs`'s
|
|
8
|
+
* `CLIENT_DEVICE_WIRE_ERROR_*` constants). These are the exact strings that travel over the
|
|
9
|
+
* wire — changing any value here is a cross-repo wire-contract change, not a local rename;
|
|
10
|
+
* every producer/consumer in that table must move together.
|
|
11
|
+
*/
|
|
12
|
+
export const CLIENT_DEVICE_WIRE_ERROR_NAME_TAKEN = "name_taken";
|
|
13
|
+
/** @see CLIENT_DEVICE_WIRE_ERROR_NAME_TAKEN */
|
|
14
|
+
export const CLIENT_DEVICE_WIRE_ERROR_LAST_DEVICE = "last_device";
|
|
15
|
+
/** @see CLIENT_DEVICE_WIRE_ERROR_NAME_TAKEN */
|
|
16
|
+
export const CLIENT_DEVICE_WIRE_ERROR_LAST_DEVICE_PROTECTED = "last_device_protected";
|
|
17
|
+
/** @see CLIENT_DEVICE_WIRE_ERROR_NAME_TAKEN */
|
|
18
|
+
export const CLIENT_DEVICE_WIRE_ERROR_DEVICE_NOT_REGISTERED = "device_not_registered";
|
|
19
|
+
/**
|
|
20
|
+
* Kernel-owned view of the CURRENT pairing attempt this browser has in flight (design M4,
|
|
21
|
+
* `docs/superpowers/plans/2026-08-11-pwa-trusted-devices.md`). Single-slot — there is never
|
|
22
|
+
* more than one attempt live at a time — so the row's `id` is always the literal
|
|
23
|
+
* `"pairing-status"`, mirroring `client-device.cleanup.v1`'s single fixed-id row
|
|
24
|
+
* (`client/kernel/src/queries/device-cleanup.js`).
|
|
25
|
+
*
|
|
26
|
+
* `status` is a superset of what `<node-pairing-gate>` renders directly: `"idle"` (no attempt
|
|
27
|
+
* has begun, or the last one was reset), `"pairing"` (an attempt is in flight, matches the
|
|
28
|
+
* gate's own `"pairing"` display status), `"ready"` (the owner approved — or session
|
|
29
|
+
* establishment simply succeeded on its own), `"declined"`, `"revoked"`, `"expired"`, and
|
|
30
|
+
* `"name_conflict"` (the requested device name collided with another active/pending device —
|
|
31
|
+
* `pairing-status-service.js`'s `isNameTaken`). `"error"` is reserved for a future
|
|
32
|
+
* non-transient failure this service does not yet classify — `PairingStatusService` never
|
|
33
|
+
* emits it today; every unclassified failure keeps the row at `"pairing"` and lets the next
|
|
34
|
+
* poll attempt retry (see that file's doc comment).
|
|
35
|
+
* @typedef {{ readonly id: "pairing-status", readonly status: "idle"|"pairing"|"ready"|"declined"|"revoked"|"expired"|"name_conflict"|"error", readonly message?: string }} ClientDevicePairingStatusRow
|
|
36
|
+
*/
|