@ibgib/web-gib 0.0.58 → 0.0.60
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/AUTO-GENERATED-version.d.mts +1 -1
- package/dist/AUTO-GENERATED-version.mjs +1 -1
- package/dist/identity/custodian-delegate-helper.d.mts.map +1 -1
- package/dist/identity/custodian-delegate-helper.mjs +20 -3
- package/dist/identity/custodian-delegate-helper.mjs.map +1 -1
- package/dist/identity/identity-constants.mjs +2 -2
- package/dist/identity/identity-constants.mjs.map +1 -1
- package/dist/identity/identity-types.d.mts +4 -4
- package/dist/identity/identity-types.d.mts.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.css +93 -17
- package/dist/ui/component/identity/add-device/add-device.d.mts +24 -7
- package/dist/ui/component/identity/add-device/add-device.d.mts.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.html +97 -23
- package/dist/ui/component/identity/add-device/add-device.mjs +492 -184
- package/dist/ui/component/identity/add-device/add-device.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs +126 -74
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.css +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts +8 -8
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.html +29 -20
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs +51 -51
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs.map +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.d.mts +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.d.mts.map +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.html +3 -3
- package/dist/ui/component/identity/remove-device/remove-device.mjs +7 -7
- package/dist/ui/component/identity/remove-device/remove-device.mjs.map +1 -1
- package/dist/util/device-helper.d.mts +30 -0
- package/dist/util/device-helper.d.mts.map +1 -0
- package/dist/util/device-helper.mjs +90 -0
- package/dist/util/device-helper.mjs.map +1 -0
- package/dist/util/device-helper.respec.d.mts +2 -0
- package/dist/util/device-helper.respec.d.mts.map +1 -0
- package/dist/util/device-helper.respec.mjs +83 -0
- package/dist/util/device-helper.respec.mjs.map +1 -0
- package/package.json +3 -3
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/identity/custodian-delegate-helper.mts +28 -3
- package/src/identity/identity-constants.mts +2 -2
- package/src/identity/identity-types.mts +4 -4
- package/src/ui/component/identity/add-device/add-device.css +93 -17
- package/src/ui/component/identity/add-device/add-device.html +97 -23
- package/src/ui/component/identity/add-device/add-device.mts +579 -192
- package/src/ui/component/identity/keystone-creator/keystone-creator.mts +135 -76
- package/src/ui/component/identity/keystone-details/keystone-details.css +1 -1
- package/src/ui/component/identity/keystone-details/keystone-details.html +29 -20
- package/src/ui/component/identity/keystone-details/keystone-details.mts +57 -57
- package/src/ui/component/identity/remove-device/remove-device.html +3 -3
- package/src/ui/component/identity/remove-device/remove-device.mts +8 -8
- package/src/util/device-helper.mts +104 -0
- package/src/util/device-helper.respec.mts +90 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-implementation-plan/SKILL.md +3 -0
|
@@ -7,13 +7,17 @@ import { KeystoneService_V1 } from "@ibgib/core-gib/dist/keystone/keystone-servi
|
|
|
7
7
|
import { KeystoneProfileBuilder } from "@ibgib/core-gib/dist/keystone/policy/keystone-profile-builder.mjs";
|
|
8
8
|
import { KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT, KEYSTONE_USERNAME_REGEXP } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
|
|
9
9
|
import { deriveDelegateSecret } from "@ibgib/core-gib/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs";
|
|
10
|
-
import { getTimelinesGroupedByTjp,
|
|
10
|
+
import { getTimelinesGroupedByTjp, toDto } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
|
|
11
11
|
import { updateSpecialIndex } from "@ibgib/core-gib/dist/timeline/timeline-api.mjs";
|
|
12
|
+
import { validateEmail } from "@ibgib/core-gib/dist/keystone/keystone-helpers.mjs";
|
|
12
13
|
import { getApiBridge } from "../../../../api/ibgib-api-bridge.mjs";
|
|
13
14
|
import { getGlobalMetaspace_waitIfNeeded } from "../../../../helpers.mjs";
|
|
14
15
|
import { IbGibDynamicComponentMetaBase, IbGibFormInstanceBase, } from "../../ibgib-dynamic-component-bases.mjs";
|
|
15
16
|
import { getComponentCtorArg } from "../../../../app-bootstrap/init-orchestration.mjs";
|
|
16
17
|
import { EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, EVENT_IBGIB_UI_MESSAGE } from "../../../ui-constants.mjs";
|
|
18
|
+
import { generateDefaultDeviceTag } from "../../../../util/device-helper.mjs";
|
|
19
|
+
import { executeSsoOAuthPopup } from "../../../../identity/sso-popup-helper.mjs";
|
|
20
|
+
import { deriveLocalCustodianDelegateSecret } from "../../../../identity/custodian-delegate-helper.mjs";
|
|
17
21
|
export const ADD_DEVICE_COMPONENT_NAME = 'ibgib-add-device';
|
|
18
22
|
/**
|
|
19
23
|
* Metadata for the Add Device / Pairing component.
|
|
@@ -44,6 +48,11 @@ export class AddDeviceComponentMeta extends IbGibDynamicComponentMetaBase {
|
|
|
44
48
|
*/
|
|
45
49
|
export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
46
50
|
lc = `[AddDeviceComponentInstance]`;
|
|
51
|
+
// Staged state across progressive surfaces
|
|
52
|
+
_stagedEmail;
|
|
53
|
+
_stagedPrimaryTjpAddr;
|
|
54
|
+
_stagedPrimaryKeystone;
|
|
55
|
+
_stagedPrimaryGraph;
|
|
47
56
|
constructor() {
|
|
48
57
|
super();
|
|
49
58
|
}
|
|
@@ -51,19 +60,34 @@ export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
|
51
60
|
await super.initialize(opts);
|
|
52
61
|
}
|
|
53
62
|
async created() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
const shadow = this.shadowRoot;
|
|
64
|
+
const elements = {};
|
|
65
|
+
elements.containerEl = shadow.getElementById('container');
|
|
66
|
+
elements.titleEl = shadow.getElementById('title');
|
|
67
|
+
elements.subtitleEl = shadow.getElementById('subtitle');
|
|
68
|
+
elements.surfaceEntryEl = shadow.getElementById('surface-entry');
|
|
69
|
+
elements.btnGoogleSso = shadow.getElementById('btn-google-sso');
|
|
70
|
+
elements.btnGithubSso = shadow.getElementById('btn-github-sso');
|
|
71
|
+
elements.inputIdentifier = shadow.getElementById('input-identifier');
|
|
72
|
+
elements.btnContinueEmail = shadow.getElementById('btn-continue-email');
|
|
73
|
+
elements.sectionCodeVerification = shadow.getElementById('section-code-verification');
|
|
74
|
+
elements.inputCode = shadow.getElementById('input-code');
|
|
75
|
+
elements.btnVerifyCode = shadow.getElementById('btn-verify-code');
|
|
76
|
+
elements.sectionSovereignPassword = shadow.getElementById('section-sovereign-password');
|
|
77
|
+
elements.inputPassword = shadow.getElementById('input-password');
|
|
78
|
+
elements.btnAuthorizePassword = shadow.getElementById('btn-authorize-password');
|
|
79
|
+
elements.inputDeviceTag = shadow.getElementById('input-device-tag');
|
|
80
|
+
elements.checkboxMakeActive = shadow.getElementById('checkbox-make-active');
|
|
81
|
+
elements.statusArea = shadow.getElementById('status-area');
|
|
82
|
+
elements.statusText = shadow.getElementById('status-text');
|
|
83
|
+
elements.resultAreaEl = shadow.getElementById('result-area');
|
|
84
|
+
elements.delegateAddrEl = shadow.getElementById('delegate-addr');
|
|
85
|
+
elements.contentEl = elements.containerEl;
|
|
86
|
+
this.elements = elements;
|
|
87
|
+
// Auto-generate clean, friendly default device tag
|
|
88
|
+
if (elements.inputDeviceTag && !elements.inputDeviceTag.value) {
|
|
89
|
+
elements.inputDeviceTag.value = generateDefaultDeviceTag();
|
|
90
|
+
}
|
|
67
91
|
this.initHandlers();
|
|
68
92
|
await this.renderUI();
|
|
69
93
|
}
|
|
@@ -71,7 +95,12 @@ export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
|
71
95
|
// cleanup if needed
|
|
72
96
|
}
|
|
73
97
|
initHandlers() {
|
|
74
|
-
|
|
98
|
+
const elements = this.elements;
|
|
99
|
+
elements.btnGoogleSso?.addEventListener('click', () => this.handleSsoClick('google'));
|
|
100
|
+
elements.btnGithubSso?.addEventListener('click', () => this.handleSsoClick('github'));
|
|
101
|
+
elements.btnContinueEmail?.addEventListener('click', () => this.handleContinueEmail());
|
|
102
|
+
elements.btnVerifyCode?.addEventListener('click', () => this.handleVerifyCode());
|
|
103
|
+
elements.btnAuthorizePassword?.addEventListener('click', () => this.handleAuthorizePassword());
|
|
75
104
|
this.shadowRoot.getElementById('btn-switch-create-account')?.addEventListener('click', (e) => {
|
|
76
105
|
e.preventDefault();
|
|
77
106
|
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_UI_MESSAGE, {
|
|
@@ -80,142 +109,370 @@ export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
|
80
109
|
composed: true
|
|
81
110
|
}));
|
|
82
111
|
});
|
|
83
|
-
|
|
84
|
-
|
|
112
|
+
elements.inputIdentifier?.addEventListener('input', () => {
|
|
113
|
+
elements.inputIdentifier.classList.remove('invalid');
|
|
114
|
+
});
|
|
115
|
+
elements.inputCode?.addEventListener('input', () => {
|
|
116
|
+
elements.inputCode.classList.remove('invalid');
|
|
85
117
|
});
|
|
86
|
-
|
|
87
|
-
|
|
118
|
+
elements.inputPassword?.addEventListener('input', () => {
|
|
119
|
+
elements.inputPassword.classList.remove('invalid');
|
|
88
120
|
});
|
|
89
|
-
|
|
90
|
-
|
|
121
|
+
elements.inputDeviceTag?.addEventListener('input', () => {
|
|
122
|
+
elements.inputDeviceTag.classList.remove('invalid');
|
|
91
123
|
});
|
|
92
124
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
125
|
+
getDeviceTag() {
|
|
126
|
+
const elements = this.elements;
|
|
127
|
+
const raw = elements.inputDeviceTag?.value?.trim();
|
|
128
|
+
if (raw && KEYSTONE_USERNAME_REGEXP.test(raw)) {
|
|
129
|
+
return raw;
|
|
97
130
|
}
|
|
131
|
+
const generated = generateDefaultDeviceTag();
|
|
132
|
+
if (elements.inputDeviceTag) {
|
|
133
|
+
elements.inputDeviceTag.value = generated;
|
|
134
|
+
}
|
|
135
|
+
return generated;
|
|
98
136
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
async
|
|
103
|
-
const lc = `${this.lc}[${this.
|
|
137
|
+
// =========================================================================
|
|
138
|
+
// Flow 1: OAuth2 SSO Sign In (Google / GitHub)
|
|
139
|
+
// =========================================================================
|
|
140
|
+
async handleSsoClick(providerId) {
|
|
141
|
+
const lc = `${this.lc}[${this.handleSsoClick.name}]`;
|
|
142
|
+
const providerName = providerId === 'google' ? 'Google' : 'GitHub';
|
|
143
|
+
this.setStatus(`Opening ${providerName} sign-in window...`, 'info');
|
|
144
|
+
this.showBusy({
|
|
145
|
+
isBusy: true,
|
|
146
|
+
title: 'Sign In via SSO',
|
|
147
|
+
msg: `Opening ${providerName} authorization window...\nPlease complete sign-in in the popup window.`,
|
|
148
|
+
animationEmoji: '🌐'
|
|
149
|
+
});
|
|
104
150
|
try {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.elements.inputIdentifier.value = identifier;
|
|
109
|
-
if (!identifier) {
|
|
110
|
-
this.elements.inputIdentifier.classList.add('invalid');
|
|
111
|
-
this.setStatus("Please enter a registered email.", "error");
|
|
151
|
+
const resPopup = await executeSsoOAuthPopup({ providerId });
|
|
152
|
+
if (!resPopup.success || !resPopup.code) {
|
|
153
|
+
this.setStatus(resPopup.message || `${providerName} sign-in cancelled.`, 'error');
|
|
112
154
|
return;
|
|
113
155
|
}
|
|
114
|
-
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
156
|
+
this.setStatus(`Validating ${providerName} authorization...`, 'info');
|
|
157
|
+
this.showBusy({
|
|
158
|
+
isBusy: true,
|
|
159
|
+
title: 'Exchanging SSO Code...',
|
|
160
|
+
msg: `Validating ${providerName} authorization with server...`,
|
|
161
|
+
animationEmoji: '🔐'
|
|
162
|
+
});
|
|
163
|
+
const apiBridge = getApiBridge();
|
|
164
|
+
const resLogin = await apiBridge.postSsoLogin({
|
|
165
|
+
code: resPopup.code,
|
|
166
|
+
providerId,
|
|
167
|
+
redirectUri: resPopup.redirectUri
|
|
168
|
+
});
|
|
169
|
+
if (!resLogin.success || !resLogin.primaryTjpAddr || !resLogin.custodianReqs) {
|
|
170
|
+
this.setStatus(resLogin.message || `${providerName} login failed.`, 'error');
|
|
119
171
|
return;
|
|
120
172
|
}
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
173
|
+
const { primaryTjpAddr, custodianReqs, primaryKeystone } = resLogin;
|
|
174
|
+
const email = resLogin.userInfo?.email || '';
|
|
175
|
+
const username = email ? email.split('@')[0] : 'user';
|
|
176
|
+
await this.completeCustodialDelegateOnboarding({
|
|
177
|
+
primaryTjpAddr,
|
|
178
|
+
custodianReqs,
|
|
179
|
+
fallbackPrimaryKeystone: primaryKeystone,
|
|
180
|
+
fallbackPrimaryGraph: resLogin.primaryGraph,
|
|
181
|
+
email,
|
|
182
|
+
username,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
const emsg = extractErrorMsg(error);
|
|
187
|
+
console.error(`${lc} SSO login error: ${emsg}`, error);
|
|
188
|
+
this.setStatus(`Error: ${emsg}`, 'error');
|
|
189
|
+
}
|
|
190
|
+
finally {
|
|
191
|
+
this.showBusy({ isBusy: false });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// =========================================================================
|
|
195
|
+
// Flow 2: Email Sign In (2FA OTP Gate)
|
|
196
|
+
// =========================================================================
|
|
197
|
+
async handleContinueEmail() {
|
|
198
|
+
const lc = `${this.lc}[${this.handleContinueEmail.name}]`;
|
|
199
|
+
const elements = this.elements;
|
|
200
|
+
const email = elements.inputIdentifier?.value?.trim();
|
|
201
|
+
if (!email || !validateEmail(email)) {
|
|
202
|
+
elements.inputIdentifier.classList.add('invalid');
|
|
203
|
+
this.setStatus('Please enter a valid email address.', 'error');
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
elements.btnContinueEmail.disabled = true;
|
|
207
|
+
elements.btnContinueEmail.textContent = 'Checking...';
|
|
208
|
+
this.setStatus('Checking identity on server...', 'info');
|
|
209
|
+
this.showBusy({
|
|
210
|
+
isBusy: true,
|
|
211
|
+
title: 'Locating Identity...',
|
|
212
|
+
msg: 'Locating account on server...',
|
|
213
|
+
animationEmoji: '🔍'
|
|
214
|
+
});
|
|
215
|
+
try {
|
|
216
|
+
// 1. Look up primary identity keystone for this email
|
|
217
|
+
const resLookup = await getApiBridge().lookupPrimaryKeystone({ identifier: email });
|
|
218
|
+
if (!resLookup.success || !resLookup.tjpAddr) {
|
|
219
|
+
this.setStatus('No account found for this email address. Please click "Create Account" below.', 'error');
|
|
126
220
|
return;
|
|
127
221
|
}
|
|
128
|
-
this.
|
|
222
|
+
this._stagedEmail = email;
|
|
223
|
+
this._stagedPrimaryTjpAddr = resLookup.tjpAddr;
|
|
224
|
+
// 2. Dispatch 6-digit verification code to email
|
|
225
|
+
this.setStatus('Dispatching 6-digit verification code...', 'info');
|
|
129
226
|
this.showBusy({
|
|
130
227
|
isBusy: true,
|
|
131
|
-
title: '
|
|
132
|
-
msg: '
|
|
133
|
-
animationEmoji: '
|
|
228
|
+
title: 'Sending Code...',
|
|
229
|
+
msg: 'Dispatching 6-digit verification code to your email...',
|
|
230
|
+
animationEmoji: '✉️'
|
|
134
231
|
});
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
232
|
+
const resCode = await getApiBridge().postVerificationCode({
|
|
233
|
+
email,
|
|
234
|
+
primaryTjpAddr: this._stagedPrimaryTjpAddr
|
|
235
|
+
});
|
|
236
|
+
if (!resCode.success) {
|
|
237
|
+
this.setStatus(resCode.message || 'Failed to dispatch verification code.', 'error');
|
|
238
|
+
return;
|
|
139
239
|
}
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
240
|
+
// 3. Transition UI to 6-digit PIN input prompt
|
|
241
|
+
elements.sectionCodeVerification.classList.remove('hidden');
|
|
242
|
+
elements.sectionCodeVerification.scrollIntoView({ behavior: 'smooth' });
|
|
243
|
+
this.setStatus(`Verification code sent to ${email}! Enter the 6-digit code above to continue.`, 'info');
|
|
244
|
+
elements.inputCode?.focus();
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
const emsg = extractErrorMsg(error);
|
|
248
|
+
console.error(`${lc} ${emsg}`, error);
|
|
249
|
+
this.setStatus(`Error: ${emsg}`, 'error');
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
elements.btnContinueEmail.disabled = false;
|
|
253
|
+
elements.btnContinueEmail.textContent = 'Continue with Email';
|
|
254
|
+
this.showBusy({ isBusy: false });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async handleVerifyCode() {
|
|
258
|
+
const lc = `${this.lc}[${this.handleVerifyCode.name}]`;
|
|
259
|
+
const elements = this.elements;
|
|
260
|
+
const code = elements.inputCode?.value?.trim();
|
|
261
|
+
const email = this._stagedEmail;
|
|
262
|
+
const primaryTjpAddr = this._stagedPrimaryTjpAddr;
|
|
263
|
+
if (!email || !primaryTjpAddr) {
|
|
264
|
+
this.setStatus('Please enter your email and request a code first.', 'error');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (!code || code.length !== 6) {
|
|
268
|
+
elements.inputCode.classList.add('invalid');
|
|
269
|
+
this.setStatus('Please enter a 6-digit verification code.', 'error');
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
elements.btnVerifyCode.disabled = true;
|
|
273
|
+
elements.btnVerifyCode.textContent = 'Verifying...';
|
|
274
|
+
this.setStatus('Verifying 6-digit code...', 'info');
|
|
275
|
+
this.showBusy({
|
|
276
|
+
isBusy: true,
|
|
277
|
+
title: 'Verifying Code...',
|
|
278
|
+
msg: 'Validating verification code with server...',
|
|
279
|
+
animationEmoji: '🔐'
|
|
280
|
+
});
|
|
281
|
+
try {
|
|
282
|
+
// First, attempt Custodian Genesis / Sign-In resolution
|
|
283
|
+
const resGenesis = await getApiBridge().postCustodianGenesis({ email, code });
|
|
284
|
+
if (resGenesis.success && resGenesis.primaryTjpAddr && resGenesis.custodianReqs) {
|
|
285
|
+
// -------------------------------------------------------------
|
|
286
|
+
// Path A: Custodial Account (100% Passwordless)
|
|
287
|
+
// -------------------------------------------------------------
|
|
288
|
+
const { primaryTjpAddr, custodianReqs, primaryKeystone, primaryGraph } = resGenesis;
|
|
289
|
+
const username = email ? email.split('@')[0] : 'user';
|
|
290
|
+
await this.completeCustodialDelegateOnboarding({
|
|
291
|
+
primaryTjpAddr,
|
|
292
|
+
custodianReqs,
|
|
293
|
+
fallbackPrimaryKeystone: primaryKeystone,
|
|
294
|
+
fallbackPrimaryGraph: primaryGraph,
|
|
295
|
+
email,
|
|
296
|
+
username,
|
|
297
|
+
});
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
// -----------------------------------------------------------------
|
|
301
|
+
// Path B: Sovereign Account (Fetch Verified Graph, Reveal Password Prompt)
|
|
302
|
+
// -----------------------------------------------------------------
|
|
303
|
+
const resVerify = await getApiBridge().postVerifyCode({ email, primaryTjpAddr, code });
|
|
304
|
+
if (!resVerify.success) {
|
|
305
|
+
this.setStatus(resVerify.message || 'Verification failed. Please check the code.', 'error');
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
this.setStatus('Retrieving identity graph from server...', 'info');
|
|
309
|
+
this.showBusy({
|
|
310
|
+
isBusy: true,
|
|
311
|
+
title: 'Loading Identity Graph...',
|
|
312
|
+
msg: 'Fetching cryptographic identity timeline from server...',
|
|
313
|
+
animationEmoji: '📥'
|
|
145
314
|
});
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const found = res?.ibGibs?.[0];
|
|
151
|
-
if (found && found.data) {
|
|
152
|
-
const username = found.data.frameDetails?.username ?? '';
|
|
153
|
-
const emails = found.data.emails ?? found.data.frameDetails?.emails ?? [];
|
|
154
|
-
if (username.toLowerCase() === identifier.toLowerCase() || emails.some((e) => e.toLowerCase() === identifier.toLowerCase())) {
|
|
155
|
-
primaryKeystone = found;
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
315
|
+
const resGraph = await getApiBridge().getKeystoneGraph(primaryTjpAddr);
|
|
316
|
+
if (!resGraph.success || !resGraph.graph) {
|
|
317
|
+
this.setStatus('Failed to retrieve identity graph from server.', 'error');
|
|
318
|
+
return;
|
|
159
319
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
const graphIbGibs = Object.values(resGraph.graph);
|
|
171
|
-
await metaspace.put({ ibGibs: graphIbGibs, space });
|
|
172
|
-
// Order timelines by TJP and register each frame in ascending order
|
|
173
|
-
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: graphIbGibs });
|
|
174
|
-
for (const tjpKey of Object.keys(timelinesMap)) {
|
|
175
|
-
const timeline = timelinesMap[tjpKey];
|
|
176
|
-
for (const frame of timeline) {
|
|
177
|
-
await metaspace.registerNewIbGib({ ibGib: frame, space });
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
// Set primaryKeystone to the latest frame in the domain timeline
|
|
181
|
-
const domainTimeline = timelinesMap[domainAddr] || timelinesMap[Object.keys(timelinesMap)[0]];
|
|
182
|
-
if (domainTimeline && domainTimeline.length > 0) {
|
|
183
|
-
primaryKeystone = domainTimeline[domainTimeline.length - 1];
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
primaryKeystone = fetchedKeystone;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
await metaspace.put({ ibGibs: [fetchedKeystone], space });
|
|
191
|
-
await metaspace.registerNewIbGib({ ibGib: fetchedKeystone, space });
|
|
192
|
-
primaryKeystone = fetchedKeystone;
|
|
320
|
+
this._stagedPrimaryGraph = resGraph.graph;
|
|
321
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
322
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
323
|
+
if (space) {
|
|
324
|
+
const graphIbGibs = Object.values(resGraph.graph);
|
|
325
|
+
await metaspace.put({ ibGibs: graphIbGibs, space });
|
|
326
|
+
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: graphIbGibs });
|
|
327
|
+
for (const tjpKey of Object.keys(timelinesMap)) {
|
|
328
|
+
for (const frame of timelinesMap[tjpKey]) {
|
|
329
|
+
await metaspace.registerNewIbGib({ ibGib: frame, space });
|
|
193
330
|
}
|
|
194
331
|
}
|
|
332
|
+
const domainTimeline = timelinesMap[primaryTjpAddr] || timelinesMap[Object.keys(timelinesMap)[0]];
|
|
333
|
+
if (domainTimeline && domainTimeline.length > 0) {
|
|
334
|
+
this._stagedPrimaryKeystone = domainTimeline[domainTimeline.length - 1];
|
|
335
|
+
}
|
|
195
336
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
337
|
+
// Hide OTP section, reveal Sovereign Master Password prompt
|
|
338
|
+
elements.sectionCodeVerification.classList.add('hidden');
|
|
339
|
+
elements.sectionSovereignPassword.classList.remove('hidden');
|
|
340
|
+
elements.sectionSovereignPassword.scrollIntoView({ behavior: 'smooth' });
|
|
341
|
+
this.setStatus('2FA verified! Please enter your master password to authorize this device.', 'info');
|
|
342
|
+
elements.inputPassword?.focus();
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
const emsg = extractErrorMsg(error);
|
|
346
|
+
console.error(`${lc} ${emsg}`, error);
|
|
347
|
+
this.setStatus(`Error: ${emsg}`, 'error');
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
elements.btnVerifyCode.disabled = false;
|
|
351
|
+
elements.btnVerifyCode.textContent = 'Verify Code';
|
|
352
|
+
this.showBusy({ isBusy: false });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// =========================================================================
|
|
356
|
+
// DRY Custodial Delegation Helper (Shared by SSO & Email OTP)
|
|
357
|
+
// =========================================================================
|
|
358
|
+
async completeCustodialDelegateOnboarding({ primaryTjpAddr, custodianReqs, fallbackPrimaryKeystone, fallbackPrimaryGraph, email, username, }) {
|
|
359
|
+
const lc = `${this.lc}[${this.completeCustodialDelegateOnboarding.name}]`;
|
|
360
|
+
const deviceTag = this.getDeviceTag();
|
|
361
|
+
this.setStatus('Deriving local device delegate keys...', 'info');
|
|
362
|
+
this.showBusy({
|
|
363
|
+
isBusy: true,
|
|
364
|
+
title: 'Creating Sync Delegate...',
|
|
365
|
+
msg: `Generating cryptographic sync delegate keys for ${deviceTag}...`,
|
|
366
|
+
animationEmoji: '🔑'
|
|
367
|
+
});
|
|
368
|
+
const delegateSecret = await deriveLocalCustodianDelegateSecret({ primaryTjpAddr });
|
|
369
|
+
const userAlias = username || (email ? email.split('@')[0] : 'user');
|
|
370
|
+
const custodianSyncProfileName = process.env.KEYSTONE_PROFILE_CUSTODIAN_SYNC;
|
|
371
|
+
if (!custodianSyncProfileName || !custodianSyncProfileName.trim()) {
|
|
372
|
+
throw new Error('KEYSTONE_PROFILE_CUSTODIAN_SYNC environment variable is missing. Expected valid profile name like "sync-profile.dev" or "sync-profile.prod". (E: 4d5e6f7890123456789abcdef4)');
|
|
373
|
+
}
|
|
374
|
+
const delegateBuilder = KeystoneProfileBuilder.buildKeystone(custodianSyncProfileName.trim())
|
|
375
|
+
.withUsername(`${userAlias}-sync-${deviceTag}`)
|
|
376
|
+
.withDescription(`Sync Delegate for ${userAlias} on ${deviceTag}`)
|
|
377
|
+
.withDetails({
|
|
378
|
+
client: 'space-gib-web',
|
|
379
|
+
role: 'sync-delegate',
|
|
380
|
+
deviceTag,
|
|
381
|
+
primaryTjpAddr,
|
|
382
|
+
custodianReqs,
|
|
383
|
+
});
|
|
384
|
+
const configs = await delegateBuilder.compileConfigs();
|
|
385
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
386
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
387
|
+
if (!space) {
|
|
388
|
+
this.setStatus('Could not access local user space.', 'error');
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const keystoneService = new KeystoneService_V1();
|
|
392
|
+
const delegateKeystone = await keystoneService.genesis({
|
|
393
|
+
masterSecret: delegateSecret,
|
|
394
|
+
configs,
|
|
395
|
+
metaspace,
|
|
396
|
+
space,
|
|
397
|
+
frameDetails: delegateBuilder.getFrameDetails(),
|
|
398
|
+
isPrimary: false
|
|
399
|
+
});
|
|
400
|
+
this.setStatus('Registering device delegate with custodian server...', 'info');
|
|
401
|
+
this.showBusy({
|
|
402
|
+
isBusy: true,
|
|
403
|
+
title: 'Registering Delegate...',
|
|
404
|
+
msg: 'Registering local delegate with custodian server...',
|
|
405
|
+
animationEmoji: '🔄'
|
|
406
|
+
});
|
|
407
|
+
const resRegister = await getApiBridge().postCustodianRegisterDelegate({ delegateKeystone });
|
|
408
|
+
if (!resRegister.success) {
|
|
409
|
+
this.setStatus(resRegister.message || 'Custodian delegate registration failed.', 'error');
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
// Finalize onboarding into local space & special index
|
|
413
|
+
await this.finalizeDeviceOnboarding({
|
|
414
|
+
primaryKeystone: resRegister.evolvedPrimaryKeystone || fallbackPrimaryKeystone,
|
|
415
|
+
primaryGraph: resRegister.primaryGraph || fallbackPrimaryGraph,
|
|
416
|
+
delegateKeystone,
|
|
417
|
+
primaryTjpAddr,
|
|
418
|
+
delegateSecret,
|
|
419
|
+
makeActive: this.elements?.checkboxMakeActive?.checked ?? true
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
// =========================================================================
|
|
423
|
+
// Flow 3: Sovereign Master Password Unlock & Local Evolution
|
|
424
|
+
// =========================================================================
|
|
425
|
+
async handleAuthorizePassword() {
|
|
426
|
+
const lc = `${this.lc}[${this.handleAuthorizePassword.name}]`;
|
|
427
|
+
const elements = this.elements;
|
|
428
|
+
const masterSecret = elements.inputPassword?.value?.trim();
|
|
429
|
+
const primaryKeystone = this._stagedPrimaryKeystone;
|
|
430
|
+
const primaryTjpAddr = this._stagedPrimaryTjpAddr;
|
|
431
|
+
if (!masterSecret) {
|
|
432
|
+
elements.inputPassword.classList.add('invalid');
|
|
433
|
+
this.setStatus('Please enter your master password.', 'error');
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (!primaryKeystone || !primaryTjpAddr) {
|
|
437
|
+
this.setStatus('Missing staged primary identity context. Please restart sign-in.', 'error');
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
elements.btnAuthorizePassword.disabled = true;
|
|
441
|
+
elements.btnAuthorizePassword.textContent = 'Authorizing...';
|
|
442
|
+
this.setStatus('Verifying master password locally...', 'info');
|
|
443
|
+
this.showBusy({
|
|
444
|
+
isBusy: true,
|
|
445
|
+
title: 'Verifying Password...',
|
|
446
|
+
msg: 'Verifying cryptographic password locally in browser...',
|
|
447
|
+
animationEmoji: '🔐'
|
|
448
|
+
});
|
|
449
|
+
try {
|
|
450
|
+
const keystoneService = new KeystoneService_V1();
|
|
451
|
+
const isValid = await keystoneService.verifySigningSecret({
|
|
452
|
+
keystoneIbGib: primaryKeystone,
|
|
453
|
+
signingSecret: masterSecret,
|
|
454
|
+
poolId: 'manage'
|
|
455
|
+
});
|
|
456
|
+
if (!isValid) {
|
|
457
|
+
elements.inputPassword.classList.add('invalid');
|
|
458
|
+
this.setStatus('Incorrect master password. Please try again.', 'error');
|
|
202
459
|
return;
|
|
203
460
|
}
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
this.setStatus(
|
|
461
|
+
const deviceTag = this.getDeviceTag();
|
|
462
|
+
const primaryUsername = primaryKeystone.data?.frameDetails?.username || 'user';
|
|
463
|
+
this.setStatus(`Generating local sync delegate for ${deviceTag}...`, 'info');
|
|
207
464
|
this.showBusy({
|
|
208
465
|
isBusy: true,
|
|
209
466
|
title: 'Generating Keys...',
|
|
210
|
-
msg:
|
|
467
|
+
msg: `Generating cryptographic delegate keys for ${deviceTag}...`,
|
|
211
468
|
animationEmoji: '🔑'
|
|
212
469
|
});
|
|
213
|
-
const primaryTjpAddr = getTjpAddr({ ibGib: primaryKeystone, defaultIfNone: 'incomingAddr' });
|
|
214
|
-
if (!primaryTjpAddr) {
|
|
215
|
-
throw new Error(`(UNEXPECTED) primaryTjpAddr falsy? couldn't get tjpAddr from primaryKeystone? (E: bc8cc8678e1ce4194ab02646160fde26)`);
|
|
216
|
-
}
|
|
217
470
|
const delegateSecret = await deriveDelegateSecret({ masterSecret });
|
|
218
|
-
const
|
|
471
|
+
const sovereignSyncProfileName = process.env.KEYSTONE_PROFILE_SOVEREIGN_SYNC;
|
|
472
|
+
if (!sovereignSyncProfileName || !sovereignSyncProfileName.trim()) {
|
|
473
|
+
throw new Error('KEYSTONE_PROFILE_SOVEREIGN_SYNC environment variable is missing. Expected valid profile name like "sync-profile.dev" or "sync-profile.prod". (E: 8f9e0a1b2c3d4e5f67890123456789ab)');
|
|
474
|
+
}
|
|
475
|
+
const delegateBuilder = KeystoneProfileBuilder.buildKeystone(sovereignSyncProfileName.trim())
|
|
219
476
|
.withUsername(`${primaryUsername}-sync-${deviceTag}`)
|
|
220
477
|
.withDescription(`Sync Delegate for ${primaryUsername} on ${deviceTag}`)
|
|
221
478
|
.withDetails({
|
|
@@ -225,100 +482,151 @@ export class AddDeviceComponentInstance extends IbGibFormInstanceBase {
|
|
|
225
482
|
primaryTjpAddr,
|
|
226
483
|
});
|
|
227
484
|
const delegateConfigs = await delegateBuilder.compileConfigs();
|
|
228
|
-
const
|
|
485
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
486
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
487
|
+
if (!space) {
|
|
488
|
+
throw new Error('No default space found in metaspace.');
|
|
489
|
+
}
|
|
229
490
|
const delegateKeystone = await keystoneService.genesis({
|
|
230
491
|
masterSecret: delegateSecret,
|
|
231
492
|
configs: delegateConfigs,
|
|
232
493
|
metaspace,
|
|
233
494
|
space,
|
|
234
|
-
frameDetails: delegateBuilder.getFrameDetails()
|
|
495
|
+
frameDetails: delegateBuilder.getFrameDetails(),
|
|
496
|
+
isPrimary: false
|
|
235
497
|
});
|
|
236
|
-
|
|
237
|
-
console.log(`${lc} Local device delegate generated: ${delegateAddr}`);
|
|
238
|
-
await delay(humanEyeDelay);
|
|
239
|
-
// 3. Evolve parent primary keystone to register delegate
|
|
240
|
-
this.setStatus("Registering device on primary identity...", "info");
|
|
498
|
+
this.setStatus('Evolving primary identity to register delegate...', 'info');
|
|
241
499
|
this.showBusy({
|
|
242
500
|
isBusy: true,
|
|
243
501
|
title: 'Registering Device...',
|
|
244
|
-
msg: '
|
|
245
|
-
animationEmoji: '
|
|
502
|
+
msg: 'Evolving primary identity to register new device delegate...',
|
|
503
|
+
animationEmoji: '⚙️'
|
|
246
504
|
});
|
|
247
|
-
const
|
|
505
|
+
const evolvedParentKeystone = await keystoneService.registerDelegate({
|
|
248
506
|
parentKeystone: primaryKeystone,
|
|
249
507
|
delegateKeystone,
|
|
250
508
|
masterSecret,
|
|
509
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
251
510
|
metaspace,
|
|
252
511
|
space,
|
|
253
|
-
|
|
254
|
-
deviceName: deviceTag,
|
|
512
|
+
deviceName: deviceTag
|
|
255
513
|
});
|
|
256
|
-
|
|
257
|
-
console.log(`${lc} Primary identity evolved with delegate: ${evolvedPrimaryAddr}`);
|
|
258
|
-
await delay(humanEyeDelay);
|
|
259
|
-
// 4. Post evolved primary keystone + delegate to server
|
|
260
|
-
this.setStatus("Syncing device pairing to server...", "info");
|
|
514
|
+
this.setStatus('Publishing evolved identity to server...', 'info');
|
|
261
515
|
this.showBusy({
|
|
262
516
|
isBusy: true,
|
|
263
517
|
title: 'Syncing to Server...',
|
|
264
|
-
msg: '
|
|
265
|
-
animationEmoji: '
|
|
518
|
+
msg: 'Publishing evolved identity and delegate to server...',
|
|
519
|
+
animationEmoji: '⚡'
|
|
266
520
|
});
|
|
267
|
-
// const domainAddr = primaryTjpAddr ? getIbGibAddr({ ibGib: primaryKeystone }) : getIbGibAddr({ ibGib: primaryKeystone });
|
|
268
521
|
const resEvolve = await getApiBridge().putEvolveKeystone({
|
|
269
522
|
domainAddr: primaryTjpAddr,
|
|
270
|
-
keystoneIbGib:
|
|
523
|
+
keystoneIbGib: evolvedParentKeystone,
|
|
271
524
|
relatedKeystoneIbGibs: [delegateKeystone]
|
|
272
525
|
});
|
|
273
526
|
if (!resEvolve.success) {
|
|
274
|
-
|
|
275
|
-
console.error(`${lc} ${msg}`);
|
|
276
|
-
this.setStatus(msg, "error");
|
|
277
|
-
this.showBusy({ isBusy: false });
|
|
278
|
-
this.setButtonLoading(false);
|
|
279
|
-
return;
|
|
527
|
+
console.warn(`${lc} Warning syncing delegate evolution to server: ${resEvolve.message}`);
|
|
280
528
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
type: "keystones",
|
|
290
|
-
rel8nInfos: [
|
|
291
|
-
{
|
|
292
|
-
rel8nName: "keystone",
|
|
293
|
-
ibGibs: [
|
|
294
|
-
toDto({ ibGib: evolvedPrimaryKeystone }),
|
|
295
|
-
toDto({ ibGib: delegateKeystone })
|
|
296
|
-
],
|
|
297
|
-
},
|
|
298
|
-
],
|
|
299
|
-
metaspace,
|
|
300
|
-
space,
|
|
529
|
+
await this.finalizeDeviceOnboarding({
|
|
530
|
+
primaryKeystone: evolvedParentKeystone,
|
|
531
|
+
primaryGraph: this._stagedPrimaryGraph,
|
|
532
|
+
delegateKeystone,
|
|
533
|
+
primaryTjpAddr,
|
|
534
|
+
masterSecret,
|
|
535
|
+
delegateSecret,
|
|
536
|
+
makeActive: elements.checkboxMakeActive?.checked ?? true
|
|
301
537
|
});
|
|
302
|
-
// 6. Set as active identity if checked
|
|
303
|
-
if (this.elements.checkboxMakeActive.checked) {
|
|
304
|
-
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, {
|
|
305
|
-
detail: { activeIdentityAddr: evolvedPrimaryAddr },
|
|
306
|
-
bubbles: true,
|
|
307
|
-
composed: true
|
|
308
|
-
}));
|
|
309
|
-
}
|
|
310
|
-
this.showBusy({ isBusy: false });
|
|
311
|
-
this.setButtonLoading(false);
|
|
312
|
-
this.elements.delegateAddrEl.innerText = delegateAddr;
|
|
313
|
-
this.elements.resultAreaEl.classList.remove('collapsed');
|
|
314
|
-
this.setStatus("Device successfully paired!", "success");
|
|
315
538
|
}
|
|
316
539
|
catch (error) {
|
|
317
|
-
|
|
318
|
-
|
|
540
|
+
const emsg = extractErrorMsg(error);
|
|
541
|
+
console.error(`${lc} ${emsg}`, error);
|
|
542
|
+
this.setStatus(`Error: ${emsg}`, 'error');
|
|
543
|
+
}
|
|
544
|
+
finally {
|
|
545
|
+
elements.btnAuthorizePassword.disabled = false;
|
|
546
|
+
elements.btnAuthorizePassword.textContent = 'Authorize & Pair Device';
|
|
319
547
|
this.showBusy({ isBusy: false });
|
|
320
|
-
this.setButtonLoading(false);
|
|
321
548
|
}
|
|
322
549
|
}
|
|
550
|
+
// =========================================================================
|
|
551
|
+
// Finalize Device Onboarding & Navigation
|
|
552
|
+
// =========================================================================
|
|
553
|
+
async finalizeDeviceOnboarding({ primaryKeystone, primaryGraph, delegateKeystone, primaryTjpAddr, masterSecret, delegateSecret, makeActive = true, }) {
|
|
554
|
+
const lc = `${this.lc}[${this.finalizeDeviceOnboarding.name}]`;
|
|
555
|
+
const humanEyeDelay = 500;
|
|
556
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
557
|
+
if (!metaspace) {
|
|
558
|
+
throw new Error('Metaspace not found');
|
|
559
|
+
}
|
|
560
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
561
|
+
if (!space) {
|
|
562
|
+
throw new Error('Local user space not found');
|
|
563
|
+
}
|
|
564
|
+
// 1. Put primary frames and delegate into local space
|
|
565
|
+
if (primaryGraph) {
|
|
566
|
+
const graphIbGibs = Object.values(primaryGraph);
|
|
567
|
+
await metaspace.put({ ibGibs: graphIbGibs, space });
|
|
568
|
+
const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: graphIbGibs });
|
|
569
|
+
for (const tjpKey of Object.keys(timelinesMap)) {
|
|
570
|
+
for (const frame of timelinesMap[tjpKey]) {
|
|
571
|
+
await metaspace.registerNewIbGib({ ibGib: frame, space });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
else if (primaryKeystone) {
|
|
576
|
+
await metaspace.put({ ibGibs: [primaryKeystone], space });
|
|
577
|
+
await metaspace.registerNewIbGib({ ibGib: primaryKeystone, space });
|
|
578
|
+
}
|
|
579
|
+
await metaspace.put({ ibGibs: [delegateKeystone], space });
|
|
580
|
+
await metaspace.registerNewIbGib({ ibGib: delegateKeystone, space });
|
|
581
|
+
// 2. Register in keystones special index
|
|
582
|
+
const ibGibsToRegister = [toDto({ ibGib: delegateKeystone })];
|
|
583
|
+
if (primaryKeystone) {
|
|
584
|
+
ibGibsToRegister.unshift(toDto({ ibGib: primaryKeystone }));
|
|
585
|
+
}
|
|
586
|
+
await updateSpecialIndex({
|
|
587
|
+
type: 'keystones',
|
|
588
|
+
rel8nInfos: [{ rel8nName: 'keystone', ibGibs: ibGibsToRegister }],
|
|
589
|
+
metaspace,
|
|
590
|
+
space
|
|
591
|
+
});
|
|
592
|
+
// 3. Make active if checked
|
|
593
|
+
if (makeActive) {
|
|
594
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, {
|
|
595
|
+
detail: { activeIdentityAddr: primaryTjpAddr },
|
|
596
|
+
bubbles: true,
|
|
597
|
+
composed: true
|
|
598
|
+
}));
|
|
599
|
+
}
|
|
600
|
+
// 4. Update UI results
|
|
601
|
+
const delegateAddr = getIbGibAddr({ ibGib: delegateKeystone });
|
|
602
|
+
if (this.elements?.delegateAddrEl) {
|
|
603
|
+
this.elements.delegateAddrEl.textContent = delegateAddr;
|
|
604
|
+
}
|
|
605
|
+
if (this.elements?.resultAreaEl) {
|
|
606
|
+
this.elements.resultAreaEl.classList.remove('collapsed');
|
|
607
|
+
}
|
|
608
|
+
const successMsg = 'Device paired and identity activated successfully!';
|
|
609
|
+
this.setStatus(successMsg, 'success');
|
|
610
|
+
this.showBusy({
|
|
611
|
+
isBusy: true,
|
|
612
|
+
title: 'Success!',
|
|
613
|
+
msg: successMsg,
|
|
614
|
+
animationEmoji: '✅'
|
|
615
|
+
});
|
|
616
|
+
await delay(humanEyeDelay);
|
|
617
|
+
// 5. Navigate to identity manager
|
|
618
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_UI_MESSAGE, {
|
|
619
|
+
detail: { action: 'show-identity-manager', addr: primaryTjpAddr },
|
|
620
|
+
bubbles: true,
|
|
621
|
+
composed: true
|
|
622
|
+
}));
|
|
623
|
+
this.showBusy({ isBusy: false });
|
|
624
|
+
}
|
|
625
|
+
async renderUI() {
|
|
626
|
+
// No complex rendering needed
|
|
627
|
+
}
|
|
628
|
+
async handleContextUpdated() {
|
|
629
|
+
await this.renderUI();
|
|
630
|
+
}
|
|
323
631
|
}
|
|
324
632
|
//# sourceMappingURL=add-device.mjs.map
|