@mysten-incubation/dev-wallet 0.0.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.
Files changed (213) hide show
  1. package/README.md +250 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.107.0/helpers/decorate.mjs +10 -0
  3. package/dist/adapters/base-adapter.d.mts +33 -0
  4. package/dist/adapters/base-adapter.d.mts.map +1 -0
  5. package/dist/adapters/base-adapter.mjs +77 -0
  6. package/dist/adapters/base-adapter.mjs.map +1 -0
  7. package/dist/adapters/browser.d.mts +8 -0
  8. package/dist/adapters/browser.mjs +8 -0
  9. package/dist/adapters/build-managed-account.d.mts +12 -0
  10. package/dist/adapters/build-managed-account.d.mts.map +1 -0
  11. package/dist/adapters/build-managed-account.mjs +30 -0
  12. package/dist/adapters/build-managed-account.mjs.map +1 -0
  13. package/dist/adapters/idb-store.mjs +73 -0
  14. package/dist/adapters/idb-store.mjs.map +1 -0
  15. package/dist/adapters/in-memory-adapter.d.mts +16 -0
  16. package/dist/adapters/in-memory-adapter.d.mts.map +1 -0
  17. package/dist/adapters/in-memory-adapter.mjs +44 -0
  18. package/dist/adapters/in-memory-adapter.mjs.map +1 -0
  19. package/dist/adapters/passkey-adapter.d.mts +31 -0
  20. package/dist/adapters/passkey-adapter.d.mts.map +1 -0
  21. package/dist/adapters/passkey-adapter.mjs +87 -0
  22. package/dist/adapters/passkey-adapter.mjs.map +1 -0
  23. package/dist/adapters/remote-cli-adapter.d.mts +65 -0
  24. package/dist/adapters/remote-cli-adapter.d.mts.map +1 -0
  25. package/dist/adapters/remote-cli-adapter.mjs +299 -0
  26. package/dist/adapters/remote-cli-adapter.mjs.map +1 -0
  27. package/dist/adapters/webcrypto-adapter.d.mts +20 -0
  28. package/dist/adapters/webcrypto-adapter.d.mts.map +1 -0
  29. package/dist/adapters/webcrypto-adapter.mjs +68 -0
  30. package/dist/adapters/webcrypto-adapter.mjs.map +1 -0
  31. package/dist/bin/cli.d.mts +1 -0
  32. package/dist/bin/cli.mjs +183 -0
  33. package/dist/bin/cli.mjs.map +1 -0
  34. package/dist/client/dev-wallet-client.d.mts +57 -0
  35. package/dist/client/dev-wallet-client.d.mts.map +1 -0
  36. package/dist/client/dev-wallet-client.mjs +223 -0
  37. package/dist/client/dev-wallet-client.mjs.map +1 -0
  38. package/dist/client/index.d.mts +3 -0
  39. package/dist/client/index.mjs +4 -0
  40. package/dist/client/request-handler.d.mts +42 -0
  41. package/dist/client/request-handler.d.mts.map +1 -0
  42. package/dist/client/request-handler.mjs +115 -0
  43. package/dist/client/request-handler.mjs.map +1 -0
  44. package/dist/index.d.mts +5 -0
  45. package/dist/index.mjs +4 -0
  46. package/dist/react/components.d.mts +32 -0
  47. package/dist/react/components.d.mts.map +1 -0
  48. package/dist/react/components.mjs +53 -0
  49. package/dist/react/components.mjs.map +1 -0
  50. package/dist/react/context.d.mts +22 -0
  51. package/dist/react/context.d.mts.map +1 -0
  52. package/dist/react/context.mjs +25 -0
  53. package/dist/react/context.mjs.map +1 -0
  54. package/dist/react/index.d.mts +4 -0
  55. package/dist/react/index.mjs +5 -0
  56. package/dist/react/useDevWallet.d.mts +38 -0
  57. package/dist/react/useDevWallet.d.mts.map +1 -0
  58. package/dist/react/useDevWallet.mjs +89 -0
  59. package/dist/react/useDevWallet.mjs.map +1 -0
  60. package/dist/server/cli-signing-middleware.d.mts +22 -0
  61. package/dist/server/cli-signing-middleware.d.mts.map +1 -0
  62. package/dist/server/cli-signing-middleware.mjs +117 -0
  63. package/dist/server/cli-signing-middleware.mjs.map +1 -0
  64. package/dist/server/index.d.mts +2 -0
  65. package/dist/server/index.mjs +3 -0
  66. package/dist/standalone/assets/in-memory-adapter-CBF4h2KD.js +1 -0
  67. package/dist/standalone/assets/main-VE4olb-Q.js +2841 -0
  68. package/dist/standalone/assets/webcrypto-adapter-DysAubFb.js +1 -0
  69. package/dist/standalone/bookmarklet.js +5 -0
  70. package/dist/standalone/index.html +48 -0
  71. package/dist/types.d.mts +55 -0
  72. package/dist/types.d.mts.map +1 -0
  73. package/dist/ui/copy-controller.mjs +43 -0
  74. package/dist/ui/copy-controller.mjs.map +1 -0
  75. package/dist/ui/dev-wallet-account-selector.d.mts +24 -0
  76. package/dist/ui/dev-wallet-account-selector.d.mts.map +1 -0
  77. package/dist/ui/dev-wallet-account-selector.mjs +268 -0
  78. package/dist/ui/dev-wallet-account-selector.mjs.map +1 -0
  79. package/dist/ui/dev-wallet-accounts.d.mts +29 -0
  80. package/dist/ui/dev-wallet-accounts.d.mts.map +1 -0
  81. package/dist/ui/dev-wallet-accounts.mjs +524 -0
  82. package/dist/ui/dev-wallet-accounts.mjs.map +1 -0
  83. package/dist/ui/dev-wallet-balances.d.mts +26 -0
  84. package/dist/ui/dev-wallet-balances.d.mts.map +1 -0
  85. package/dist/ui/dev-wallet-balances.mjs +130 -0
  86. package/dist/ui/dev-wallet-balances.mjs.map +1 -0
  87. package/dist/ui/dev-wallet-connect.d.mts +31 -0
  88. package/dist/ui/dev-wallet-connect.d.mts.map +1 -0
  89. package/dist/ui/dev-wallet-connect.mjs +225 -0
  90. package/dist/ui/dev-wallet-connect.mjs.map +1 -0
  91. package/dist/ui/dev-wallet-dropdown.d.mts +27 -0
  92. package/dist/ui/dev-wallet-dropdown.d.mts.map +1 -0
  93. package/dist/ui/dev-wallet-dropdown.mjs +121 -0
  94. package/dist/ui/dev-wallet-dropdown.mjs.map +1 -0
  95. package/dist/ui/dev-wallet-network-badge.d.mts +21 -0
  96. package/dist/ui/dev-wallet-network-badge.d.mts.map +1 -0
  97. package/dist/ui/dev-wallet-network-badge.mjs +121 -0
  98. package/dist/ui/dev-wallet-network-badge.mjs.map +1 -0
  99. package/dist/ui/dev-wallet-new-account.d.mts +31 -0
  100. package/dist/ui/dev-wallet-new-account.d.mts.map +1 -0
  101. package/dist/ui/dev-wallet-new-account.mjs +577 -0
  102. package/dist/ui/dev-wallet-new-account.mjs.map +1 -0
  103. package/dist/ui/dev-wallet-objects.d.mts +26 -0
  104. package/dist/ui/dev-wallet-objects.d.mts.map +1 -0
  105. package/dist/ui/dev-wallet-objects.mjs +276 -0
  106. package/dist/ui/dev-wallet-objects.mjs.map +1 -0
  107. package/dist/ui/dev-wallet-panel.d.mts +22 -0
  108. package/dist/ui/dev-wallet-panel.d.mts.map +1 -0
  109. package/dist/ui/dev-wallet-panel.mjs +192 -0
  110. package/dist/ui/dev-wallet-panel.mjs.map +1 -0
  111. package/dist/ui/dev-wallet-popup.d.mts +36 -0
  112. package/dist/ui/dev-wallet-popup.d.mts.map +1 -0
  113. package/dist/ui/dev-wallet-popup.mjs +137 -0
  114. package/dist/ui/dev-wallet-popup.mjs.map +1 -0
  115. package/dist/ui/dev-wallet-settings.d.mts +33 -0
  116. package/dist/ui/dev-wallet-settings.d.mts.map +1 -0
  117. package/dist/ui/dev-wallet-settings.mjs +635 -0
  118. package/dist/ui/dev-wallet-settings.mjs.map +1 -0
  119. package/dist/ui/dev-wallet-signing-modal.d.mts +32 -0
  120. package/dist/ui/dev-wallet-signing-modal.d.mts.map +1 -0
  121. package/dist/ui/dev-wallet-signing-modal.mjs +115 -0
  122. package/dist/ui/dev-wallet-signing-modal.mjs.map +1 -0
  123. package/dist/ui/dev-wallet-signing.d.mts +25 -0
  124. package/dist/ui/dev-wallet-signing.d.mts.map +1 -0
  125. package/dist/ui/dev-wallet-signing.mjs +544 -0
  126. package/dist/ui/dev-wallet-signing.mjs.map +1 -0
  127. package/dist/ui/dev-wallet-standalone.d.mts +23 -0
  128. package/dist/ui/dev-wallet-standalone.d.mts.map +1 -0
  129. package/dist/ui/dev-wallet-standalone.mjs +129 -0
  130. package/dist/ui/dev-wallet-standalone.mjs.map +1 -0
  131. package/dist/ui/dev-wallet-tab-bar.d.mts +19 -0
  132. package/dist/ui/dev-wallet-tab-bar.d.mts.map +1 -0
  133. package/dist/ui/dev-wallet-tab-bar.mjs +143 -0
  134. package/dist/ui/dev-wallet-tab-bar.mjs.map +1 -0
  135. package/dist/ui/index.d.mts +17 -0
  136. package/dist/ui/index.mjs +18 -0
  137. package/dist/ui/mount.d.mts +11 -0
  138. package/dist/ui/mount.d.mts.map +1 -0
  139. package/dist/ui/mount.mjs +17 -0
  140. package/dist/ui/mount.mjs.map +1 -0
  141. package/dist/ui/styles.mjs +250 -0
  142. package/dist/ui/styles.mjs.map +1 -0
  143. package/dist/ui/transaction-analyzer.mjs +58 -0
  144. package/dist/ui/transaction-analyzer.mjs.map +1 -0
  145. package/dist/ui/utils.mjs +98 -0
  146. package/dist/ui/utils.mjs.map +1 -0
  147. package/dist/ui/wallet-controller.mjs +275 -0
  148. package/dist/ui/wallet-controller.mjs.map +1 -0
  149. package/dist/wallet/constants.mjs +11 -0
  150. package/dist/wallet/constants.mjs.map +1 -0
  151. package/dist/wallet/dev-wallet.d.mts +118 -0
  152. package/dist/wallet/dev-wallet.d.mts.map +1 -0
  153. package/dist/wallet/dev-wallet.mjs +424 -0
  154. package/dist/wallet/dev-wallet.mjs.map +1 -0
  155. package/dist/wallet/initializer.d.mts +63 -0
  156. package/dist/wallet/initializer.d.mts.map +1 -0
  157. package/dist/wallet/initializer.mjs +75 -0
  158. package/dist/wallet/initializer.mjs.map +1 -0
  159. package/dist/wallet/signing.d.mts +35 -0
  160. package/dist/wallet/signing.d.mts.map +1 -0
  161. package/dist/wallet/signing.mjs +69 -0
  162. package/dist/wallet/signing.mjs.map +1 -0
  163. package/package.json +124 -0
  164. package/src/adapters/base-adapter.ts +93 -0
  165. package/src/adapters/browser.ts +15 -0
  166. package/src/adapters/build-managed-account.ts +33 -0
  167. package/src/adapters/idb-store.ts +87 -0
  168. package/src/adapters/in-memory-adapter.ts +51 -0
  169. package/src/adapters/passkey-adapter.ts +120 -0
  170. package/src/adapters/remote-cli-adapter.ts +388 -0
  171. package/src/adapters/webcrypto-adapter.ts +96 -0
  172. package/src/app/bookmarklet-entry.ts +77 -0
  173. package/src/app/index.html +48 -0
  174. package/src/app/main.ts +245 -0
  175. package/src/app/tsconfig.json +3 -0
  176. package/src/bin/cli.ts +214 -0
  177. package/src/client/dev-wallet-client.ts +280 -0
  178. package/src/client/index.ts +7 -0
  179. package/src/client/request-handler.ts +161 -0
  180. package/src/index.ts +19 -0
  181. package/src/react/components.ts +64 -0
  182. package/src/react/context.ts +38 -0
  183. package/src/react/index.ts +16 -0
  184. package/src/react/useDevWallet.ts +118 -0
  185. package/src/server/cli-signing-middleware.ts +146 -0
  186. package/src/server/index.ts +8 -0
  187. package/src/types.ts +58 -0
  188. package/src/ui/copy-controller.ts +49 -0
  189. package/src/ui/dev-wallet-account-selector.ts +283 -0
  190. package/src/ui/dev-wallet-accounts.ts +578 -0
  191. package/src/ui/dev-wallet-balances.ts +171 -0
  192. package/src/ui/dev-wallet-connect.ts +259 -0
  193. package/src/ui/dev-wallet-dropdown.ts +138 -0
  194. package/src/ui/dev-wallet-network-badge.ts +128 -0
  195. package/src/ui/dev-wallet-new-account.ts +662 -0
  196. package/src/ui/dev-wallet-objects.ts +336 -0
  197. package/src/ui/dev-wallet-panel.ts +207 -0
  198. package/src/ui/dev-wallet-popup.ts +169 -0
  199. package/src/ui/dev-wallet-settings.ts +692 -0
  200. package/src/ui/dev-wallet-signing-modal.ts +137 -0
  201. package/src/ui/dev-wallet-signing.ts +624 -0
  202. package/src/ui/dev-wallet-standalone.ts +136 -0
  203. package/src/ui/dev-wallet-tab-bar.ts +151 -0
  204. package/src/ui/index.ts +26 -0
  205. package/src/ui/mount.ts +21 -0
  206. package/src/ui/styles.ts +252 -0
  207. package/src/ui/transaction-analyzer.ts +60 -0
  208. package/src/ui/utils.ts +118 -0
  209. package/src/ui/wallet-controller.ts +340 -0
  210. package/src/wallet/constants.ts +17 -0
  211. package/src/wallet/dev-wallet.ts +597 -0
  212. package/src/wallet/initializer.ts +124 -0
  213. package/src/wallet/signing.ts +85 -0
@@ -0,0 +1,245 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { fromBase64, toBase64 } from '@mysten/sui/utils';
5
+
6
+ import { RemoteCliAdapter } from '../adapters/remote-cli-adapter.js';
7
+ import type { SignerAdapter } from '../types.js';
8
+ import { parseWalletRequest } from '../client/request-handler.js';
9
+ import { getNetworkFromChain } from '../wallet/constants.js';
10
+ import { DevWallet } from '../wallet/dev-wallet.js';
11
+
12
+ // Import UI components to register custom elements
13
+ import '../ui/dev-wallet-popup.js';
14
+ import type { DevWalletStandalone } from '../ui/dev-wallet-standalone.js';
15
+ import '../ui/dev-wallet-standalone.js';
16
+ import type { DevWalletConnect } from '../ui/dev-wallet-connect.js';
17
+
18
+ declare global {
19
+ interface Window {
20
+ __DEV_WALLET_CLI__?: boolean;
21
+ }
22
+ }
23
+
24
+ // NOTE: JWT secret is stored in localStorage (readable by any JS on the same origin).
25
+ // This is acceptable for a dev-only tool but should not be used in production wallets.
26
+ function getJwtSecretKey(): Uint8Array {
27
+ const stored = localStorage.getItem('dev-wallet:jwt-secret');
28
+ if (stored) return fromBase64(stored);
29
+ const key = crypto.getRandomValues(new Uint8Array(32));
30
+ localStorage.setItem('dev-wallet:jwt-secret', toBase64(key));
31
+ return key;
32
+ }
33
+
34
+ async function createWallet(): Promise<DevWallet> {
35
+ const initTasks: Array<Promise<SignerAdapter | null>> = [];
36
+
37
+ // WebCrypto adapter (persistent via IndexedDB — preferred default)
38
+ if (typeof indexedDB !== 'undefined') {
39
+ initTasks.push(
40
+ import('../adapters/webcrypto-adapter.js')
41
+ .then(async ({ WebCryptoSignerAdapter }) => {
42
+ const adapter = new WebCryptoSignerAdapter();
43
+ await adapter.initialize();
44
+ return adapter;
45
+ })
46
+ .catch(() => null),
47
+ );
48
+ }
49
+
50
+ // In-memory adapter (lightweight fallback, always available)
51
+ initTasks.push(
52
+ import('../adapters/in-memory-adapter.js').then(async ({ InMemorySignerAdapter }) => {
53
+ const adapter = new InMemorySignerAdapter();
54
+ await adapter.initialize();
55
+ return adapter;
56
+ }),
57
+ );
58
+
59
+ // CLI adapter (when running with CLI middleware)
60
+ if (window.__DEV_WALLET_CLI__) {
61
+ initTasks.push(
62
+ import('../adapters/remote-cli-adapter.js').then(async ({ RemoteCliAdapter }) => {
63
+ const cliAdapter = new RemoteCliAdapter();
64
+ await cliAdapter.initialize();
65
+ return cliAdapter;
66
+ }),
67
+ );
68
+ }
69
+
70
+ const results = await Promise.allSettled(initTasks);
71
+
72
+ for (const result of results) {
73
+ if (result.status === 'rejected') {
74
+ console.warn('[dev-wallet] Adapter initialization failed:', result.reason);
75
+ }
76
+ }
77
+
78
+ const adapters = results
79
+ .filter(
80
+ (r): r is PromiseFulfilledResult<SignerAdapter> =>
81
+ r.status === 'fulfilled' && r.value !== null,
82
+ )
83
+ .map((r) => r.value);
84
+
85
+ if (adapters.length === 0) {
86
+ throw new Error('No adapters could be initialized');
87
+ }
88
+
89
+ const hasAccounts = adapters.some((a) => a.getAccounts().length > 0);
90
+ if (!hasAccounts) {
91
+ const creatableAdapter = adapters.find((a) => a.createAccount);
92
+ if (creatableAdapter?.createAccount) {
93
+ await creatableAdapter.createAccount({ label: 'Dev Account' });
94
+ }
95
+ }
96
+
97
+ return new DevWallet({
98
+ adapters,
99
+ activeNetwork: 'devnet',
100
+ persistNetworks: true,
101
+ });
102
+ }
103
+
104
+ async function handlePopupRequest(hash: string) {
105
+ const app = document.getElementById('app');
106
+ if (!app) throw new Error('Missing #app element in document');
107
+
108
+ try {
109
+ const wallet = await createWallet();
110
+ const jwtSecretKey = getJwtSecretKey();
111
+
112
+ const request = parseWalletRequest({
113
+ adapters: [...wallet.adapters],
114
+ jwtSecretKey,
115
+ getClient: (network) => {
116
+ try {
117
+ return wallet.getClient(network);
118
+ } catch {
119
+ return undefined;
120
+ }
121
+ },
122
+ hash,
123
+ });
124
+
125
+ app.innerHTML = '';
126
+
127
+ const network = request.chain ? getNetworkFromChain(request.chain) : undefined;
128
+ const client = network
129
+ ? (wallet.getClient(network) ?? wallet.activeClient)
130
+ : wallet.activeClient;
131
+
132
+ const popup = document.createElement('dev-wallet-popup');
133
+ popup.walletName = 'Dev Wallet (Web)';
134
+ popup.requestType = request.type;
135
+ popup.appName = request.appName;
136
+ popup.appUrl = request.appUrl;
137
+ popup.address = request.address ?? '';
138
+ if (request.address) {
139
+ const account = wallet.getAdapterForAccount(request.address)?.getAccount(request.address);
140
+ if (account) {
141
+ popup.accountLabel = account.label;
142
+ }
143
+ }
144
+ popup.chain = request.chain ?? 'sui:unknown';
145
+ popup.data = request.data ?? null;
146
+ popup.client = client ?? null;
147
+
148
+ if (request.type === 'connect') {
149
+ popup.connectAccounts = wallet.adapters.flatMap((a) =>
150
+ a.getAccounts().map((acc) => ({
151
+ address: acc.address,
152
+ label: acc.label,
153
+ adapterName: a.name,
154
+ })),
155
+ );
156
+ }
157
+
158
+ let handling = false;
159
+
160
+ popup.addEventListener('approve', async (e) => {
161
+ if (handling) return;
162
+ handling = true;
163
+ try {
164
+ const detail = (e as CustomEvent).detail;
165
+ await request.approve(
166
+ detail?.selectedAddresses ? { selectedAddresses: detail.selectedAddresses } : undefined,
167
+ );
168
+ window.close();
169
+ } catch (error) {
170
+ handling = false;
171
+ const connectEl = popup.shadowRoot?.querySelector(
172
+ 'dev-wallet-connect',
173
+ ) as DevWalletConnect | null;
174
+ if (connectEl) {
175
+ connectEl.showError(error instanceof Error ? error.message : String(error));
176
+ }
177
+ }
178
+ });
179
+
180
+ popup.addEventListener('reject', () => {
181
+ if (handling) return;
182
+ handling = true;
183
+ request.reject('User rejected');
184
+ window.close();
185
+ });
186
+
187
+ app.appendChild(popup);
188
+ } catch (error) {
189
+ showErrorMessage(app, 'Error', error);
190
+ }
191
+ }
192
+
193
+ async function showStandaloneUI() {
194
+ const app = document.getElementById('app');
195
+ if (!app) throw new Error('Missing #app element in document');
196
+
197
+ try {
198
+ const wallet = await createWallet();
199
+
200
+ app.innerHTML = '';
201
+
202
+ const el = document.createElement('dev-wallet-standalone') as DevWalletStandalone;
203
+ el.wallet = wallet;
204
+ el.bookmarkletOrigin = window.location.origin;
205
+ app.appendChild(el);
206
+ } catch (error) {
207
+ showErrorMessage(app, 'Failed to initialize wallet', error);
208
+ }
209
+ }
210
+
211
+ function showErrorMessage(container: HTMLElement, title: string, error: unknown) {
212
+ container.innerHTML = '';
213
+ const wrapper = document.createElement('div');
214
+ wrapper.style.cssText = 'color: #ef4444; text-align: center;';
215
+
216
+ const heading = document.createElement('h3');
217
+ heading.textContent = title;
218
+ wrapper.appendChild(heading);
219
+
220
+ const message = document.createElement('p');
221
+ message.textContent = error instanceof Error ? error.message : String(error);
222
+ message.style.cssText = 'font-size: 13px; margin-top: 8px;';
223
+ wrapper.appendChild(message);
224
+
225
+ container.appendChild(wrapper);
226
+ }
227
+
228
+ // Token is stored in localStorage so popup windows (signing requests) can also read it.
229
+ function extractAndStoreCliToken(): void {
230
+ const url = new URL(window.location.href);
231
+ const urlToken = url.searchParams.get('token');
232
+ if (urlToken) {
233
+ localStorage.setItem(RemoteCliAdapter.TOKEN_KEY, urlToken);
234
+ url.searchParams.delete('token');
235
+ history.replaceState(null, '', url.pathname + url.hash);
236
+ }
237
+ }
238
+
239
+ extractAndStoreCliToken();
240
+ const hash = window.location.hash.slice(1);
241
+ if (hash) {
242
+ handlePopupRequest(hash);
243
+ } else {
244
+ showStandaloneUI();
245
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../tsconfig.json"
3
+ }
package/src/bin/cli.ts ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ // Copyright (c) Mysten Labs, Inc.
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ import { execFile } from 'node:child_process';
6
+ import { existsSync } from 'node:fs';
7
+ import { readFile } from 'node:fs/promises';
8
+ import { resolve, dirname } from 'node:path';
9
+ import { parseArgs, promisify } from 'node:util';
10
+
11
+ import { serve } from '@hono/node-server';
12
+ import { Hono } from 'hono';
13
+ import { cors } from 'hono/cors';
14
+
15
+ const execFileAsync = promisify(execFile);
16
+
17
+ const MIME_TYPES: Record<string, string> = {
18
+ '.html': 'text/html; charset=utf-8',
19
+ '.js': 'application/javascript',
20
+ '.mjs': 'application/javascript',
21
+ '.css': 'text/css',
22
+ '.json': 'application/json',
23
+ '.svg': 'image/svg+xml',
24
+ '.png': 'image/png',
25
+ '.ico': 'image/x-icon',
26
+ '.woff': 'font/woff',
27
+ '.woff2': 'font/woff2',
28
+ };
29
+
30
+ function findStandaloneDir(startDir: string): string | null {
31
+ let dir = startDir;
32
+ for (let i = 0; i < 5; i++) {
33
+ const candidate = resolve(dir, 'standalone');
34
+ if (existsSync(resolve(candidate, 'index.html'))) return candidate;
35
+ const parent = dirname(dir);
36
+ if (parent === dir) break;
37
+ dir = parent;
38
+ }
39
+ return null;
40
+ }
41
+
42
+ function formatStartupMessage(options: {
43
+ walletUrl: string;
44
+ baseUrl: string;
45
+ adapters: string[];
46
+ hasSuiCli: boolean;
47
+ bookmarkletCode: string;
48
+ }): string {
49
+ const { walletUrl, baseUrl, adapters, hasSuiCli, bookmarkletCode } = options;
50
+ return `
51
+ Dev Wallet running at ${walletUrl}
52
+ Adapters: ${adapters.join(', ')}${!hasSuiCli ? '\n (Install sui CLI to enable CLI signing)' : ''}
53
+
54
+ To connect from your dApp:
55
+
56
+ import { DevWalletClient } from '@mysten-incubation/dev-wallet/client';
57
+ DevWalletClient.register({ origin: '${baseUrl}' });
58
+
59
+ Or use the bookmarklet — drag from the Settings tab, or copy:
60
+
61
+ ${bookmarkletCode}
62
+
63
+ Or paste in the browser console:
64
+
65
+ var s=document.createElement('script');s.src='${baseUrl}/bookmarklet.js';document.head.appendChild(s);
66
+
67
+ Press Ctrl+C to stop.
68
+ `;
69
+ }
70
+
71
+ async function main() {
72
+ const { values } = parseArgs({
73
+ options: {
74
+ port: { type: 'string', default: '5174' },
75
+ help: { type: 'boolean', short: 'h' },
76
+ },
77
+ allowPositionals: true,
78
+ });
79
+
80
+ if (values.help) {
81
+ console.log(`
82
+ Usage: dev-wallet serve [options]
83
+
84
+ Start a dev wallet web app for dApp signing.
85
+
86
+ Options:
87
+ --port <number> Port to serve on (default: 5174)
88
+ -h, --help Show this help message
89
+
90
+ The wallet automatically detects available adapters:
91
+ - In-Memory (Ed25519) — always available
92
+ - WebCrypto (Passkey) — available in browsers with IndexedDB
93
+ - CLI Signer — available when the sui CLI is on your PATH
94
+ `);
95
+ process.exit(0);
96
+ }
97
+
98
+ const port = Number(values.port ?? '5174');
99
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
100
+ console.error('Error: --port must be an integer between 1 and 65535');
101
+ process.exit(1);
102
+ }
103
+
104
+ let hasSuiCli = false;
105
+ try {
106
+ await execFileAsync('sui', ['--version']);
107
+ hasSuiCli = true;
108
+ } catch {
109
+ // sui CLI not available — CLI adapter will not be enabled
110
+ }
111
+
112
+ const scriptDir = dirname(new URL(import.meta.url).pathname);
113
+ const standaloneDir = findStandaloneDir(scriptDir);
114
+
115
+ if (!standaloneDir) {
116
+ console.error(
117
+ 'Standalone wallet app not found.\nRun `pnpm turbo build --filter=@mysten-incubation/dev-wallet` first.',
118
+ );
119
+ process.exit(1);
120
+ }
121
+
122
+ let indexHtml = await readFile(resolve(standaloneDir, 'index.html'), 'utf-8');
123
+ if (hasSuiCli) {
124
+ indexHtml = indexHtml.replace(
125
+ '</head>',
126
+ '<script>window.__DEV_WALLET_CLI__=true;</script></head>',
127
+ );
128
+ if (!indexHtml.includes('__DEV_WALLET_CLI__')) {
129
+ console.warn('Warning: Failed to inject CLI flag into index.html');
130
+ }
131
+ }
132
+
133
+ let bookmarkletJs: string | null = null;
134
+ try {
135
+ bookmarkletJs = await readFile(resolve(standaloneDir, 'bookmarklet.js'), 'utf-8');
136
+ } catch {
137
+ // bookmarklet not available
138
+ }
139
+
140
+ const app = new Hono();
141
+ let cliToken: string | null = null;
142
+
143
+ if (hasSuiCli) {
144
+ const { createCliSigningMiddleware } = await import('../server/cli-signing-middleware.js');
145
+ const result = createCliSigningMiddleware({ port });
146
+ cliToken = result.token;
147
+ app.route('/', result.app);
148
+ }
149
+
150
+ app.get('/bookmarklet.js', cors({ origin: '*' }), (c) => {
151
+ if (!bookmarkletJs) {
152
+ return c.text('Bookmarklet not found', 404);
153
+ }
154
+ c.header('Cache-Control', 'no-cache');
155
+ return c.body(bookmarkletJs, { headers: { 'Content-Type': 'application/javascript' } });
156
+ });
157
+
158
+ app.get('*', async (c) => {
159
+ const urlPath = decodeURIComponent(new URL(c.req.url).pathname);
160
+
161
+ if (urlPath === '/' || urlPath === '/index.html') {
162
+ return c.html(indexHtml);
163
+ }
164
+
165
+ const filePath = resolve(standaloneDir, urlPath.slice(1));
166
+ if (!filePath.startsWith(standaloneDir)) {
167
+ return c.html(indexHtml);
168
+ }
169
+
170
+ try {
171
+ const content = await readFile(filePath);
172
+ const ext = filePath.slice(filePath.lastIndexOf('.'));
173
+ const contentType = MIME_TYPES[ext] || 'application/octet-stream';
174
+ return c.body(content, { headers: { 'Content-Type': contentType } });
175
+ } catch {
176
+ return c.html(indexHtml);
177
+ }
178
+ });
179
+
180
+ const baseUrl = `http://localhost:${port}`;
181
+
182
+ const server = serve({ fetch: app.fetch, port }, () => {
183
+ const enabledAdapters = ['In-Memory (Ed25519)', 'WebCrypto (Passkey)'];
184
+ if (hasSuiCli) {
185
+ enabledAdapters.push('CLI Signer');
186
+ }
187
+
188
+ const walletUrl = cliToken ? `${baseUrl}/?token=${cliToken}` : baseUrl;
189
+ const bookmarkletCode = `javascript:void(document.head.appendChild(Object.assign(document.createElement('script'),{src:'${baseUrl}/bookmarklet.js'})))`;
190
+
191
+ console.log(
192
+ formatStartupMessage({
193
+ walletUrl,
194
+ baseUrl,
195
+ adapters: enabledAdapters,
196
+ hasSuiCli,
197
+ bookmarkletCode,
198
+ }),
199
+ );
200
+ });
201
+
202
+ function shutdown() {
203
+ console.log('\nShutting down...');
204
+ server.close(() => process.exit(0));
205
+ setTimeout(() => process.exit(1), 3000).unref();
206
+ }
207
+ process.on('SIGINT', shutdown);
208
+ process.on('SIGTERM', shutdown);
209
+ }
210
+
211
+ main().catch((error) => {
212
+ console.error('Failed to start dev wallet:', error);
213
+ process.exit(1);
214
+ });