@nosana/kit 1.0.7 โ†’ 1.0.9

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.
@@ -1,1840 +0,0 @@
1
- <template>
2
- <div class="min-h-screen bg-gradient-to-br from-gray-50 to-green-50">
3
- <!-- Header -->
4
- <header class="bg-white shadow-lg border-b sticky top-0 z-50 backdrop-blur-sm bg-white/95">
5
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
6
- <div class="flex justify-between items-center py-4 flex-wrap">
7
- <div class="flex items-center space-x-4 flex-shrink-0">
8
- <div class="flex items-center">
9
- <div
10
- class="w-10 h-10 bg-gradient-to-br from-green-700 to-[#10e80c] rounded-lg flex items-center justify-center text-white font-bold text-lg mr-3">
11
- N
12
- </div>
13
- <div>
14
- <h1 class="text-2xl font-bold text-gray-900 leading-tight">Nosana Kit</h1>
15
- <div class="flex items-center space-x-2 text-xs text-gray-500 h-4">
16
- <span>Documentation</span>
17
- <span>โ€ข</span>
18
- <a href="https://www.npmjs.com/package/@nosana/kit" target="_blank"
19
- class="text-blue-600 hover:text-blue-800 transition-colors no-underline">
20
- ๐Ÿ“ฆ @nosana/kit
21
- </a>
22
- <span>โ€ข</span>
23
- <span class="bg-green-100 text-green-800 px-2 py-0.5 rounded-full whitespace-nowrap">v1.0.0</span>
24
- </div>
25
- </div>
26
- </div>
27
- </div>
28
- <div class="flex items-center space-x-4 flex-shrink-0">
29
- <div class="flex items-center bg-gray-50 rounded-lg px-3 py-2">
30
- <label class="text-sm font-medium text-gray-700 mr-2 whitespace-nowrap">Network:</label>
31
- <select v-model="selectedNetwork" @change="initializeClient"
32
- class="bg-transparent border-0 text-sm font-medium focus:outline-none focus:ring-0 min-w-[100px]">
33
- <option value="mainnet">๐ŸŒ Mainnet</option>
34
- <option value="devnet">๐Ÿงช Devnet</option>
35
- </select>
36
- </div>
37
- <div class="flex items-center space-x-2 min-w-[120px]">
38
- <div :class="[
39
- 'w-2 h-2 rounded-full flex-shrink-0',
40
- isConnected ? 'bg-green-500 animate-pulse' : 'bg-red-500'
41
- ]"></div>
42
- <span class="text-sm font-medium whitespace-nowrap"
43
- :class="isConnected ? 'text-green-700' : 'text-red-700'">
44
- {{ isConnected ? 'Connected' : 'Disconnected' }}
45
- </span>
46
- </div>
47
- </div>
48
- </div>
49
- </div>
50
- </header>
51
-
52
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
53
- <!-- Installation Section -->
54
- <div class="mb-8 bg-gradient-to-r from-green-700 to-[#10e80c] rounded-xl p-8 text-white">
55
- <div class="grid lg:grid-cols-2 gap-8 items-center">
56
- <div>
57
- <h1 class="text-4xl font-bold mb-4">๐Ÿš€ Nosana Kit</h1>
58
- <p class="text-green-100 text-lg mb-6">
59
- Decentralized compute network SDK for AI inference and computational tasks.
60
- Test APIs interactively and see real-time results.
61
- </p>
62
- <div class="flex flex-wrap gap-3">
63
- <span class="bg-white/20 px-3 py-1 rounded-full text-sm">TypeScript Ready</span>
64
- <span class="bg-white/20 px-3 py-1 rounded-full text-sm">Real-time Testing</span>
65
- <span class="bg-white/20 px-3 py-1 rounded-full text-sm">Interactive Docs</span>
66
- </div>
67
- </div>
68
- <div class="bg-gray-900 rounded-lg p-6">
69
- <div class="flex items-center justify-between mb-4">
70
- <h3 class="text-lg font-semibold">Installation</h3>
71
- <button @click="copyInstallCommand" class="text-gray-400 hover:text-white transition-colors"
72
- title="Copy to clipboard">
73
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
74
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
75
- d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">
76
- </path>
77
- </svg>
78
- </button>
79
- </div>
80
- <code class="text-green-400 font-mono text-sm">npm install @nosana/kit</code>
81
- </div>
82
- </div>
83
- </div>
84
-
85
- <div class="grid grid-cols-1 lg:grid-cols-5 gap-8">
86
- <!-- Sidebar Navigation -->
87
- <div class="lg:col-span-1">
88
- <div class="bg-white rounded-xl shadow-lg sticky top-24">
89
- <div class="p-6">
90
- <h3 class="font-semibold text-gray-900 mb-4">๐Ÿ“š API Reference</h3>
91
- <nav class="space-y-2">
92
- <a href="#sdk-init" @click="scrollToSection('sdk-init')"
93
- class="flex items-center space-x-2 text-sm text-gray-600 hover:text-blue-600 hover:bg-blue-50 px-3 py-2 rounded-lg transition-colors cursor-pointer">
94
- <span>๐Ÿ”ง</span>
95
- <span>SDK Initialization</span>
96
- </a>
97
- <a href="#job-retrieval" @click="scrollToSection('job-retrieval')"
98
- class="flex items-center space-x-2 text-sm text-gray-600 hover:text-green-600 hover:bg-green-50 px-3 py-2 rounded-lg transition-colors cursor-pointer">
99
- <span>๐Ÿ“‹</span>
100
- <span>Job Retrieval</span>
101
- </a>
102
- <a href="#market-discovery" @click="scrollToSection('market-discovery')"
103
- class="flex items-center space-x-2 text-sm text-gray-600 hover:text-orange-600 hover:bg-orange-50 px-3 py-2 rounded-lg transition-colors cursor-pointer">
104
- <span>๐Ÿช</span>
105
- <span>Market Discovery</span>
106
- </a>
107
- <a href="#monitoring" @click="scrollToSection('monitoring')"
108
- class="flex items-center space-x-2 text-sm text-gray-600 hover:text-purple-600 hover:bg-purple-50 px-3 py-2 rounded-lg transition-colors cursor-pointer">
109
- <span>๐Ÿ“ก</span>
110
- <span>Real-time Monitoring</span>
111
- </a>
112
- </nav>
113
- </div>
114
- </div>
115
- </div>
116
-
117
- <!-- Main Content -->
118
- <div :class="[
119
- 'transition-all duration-300',
120
- 'lg:col-span-4'
121
- ]">
122
- <div class="space-y-8">
123
- <!-- 1. SDK Initialization -->
124
- <div id="sdk-init" class="bg-white rounded-xl shadow-lg overflow-hidden border border-gray-100">
125
- <div class="bg-gradient-to-r from-blue-500 to-blue-600 px-8 py-6">
126
- <div class="flex items-center justify-between">
127
- <h2 class="text-2xl font-bold text-white">๐Ÿ”ง SDK Initialization</h2>
128
- <span class="bg-white/20 px-3 py-1 rounded-full text-sm text-blue-100">Core</span>
129
- </div>
130
- </div>
131
-
132
- <div class="p-8">
133
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
134
- <!-- Documentation -->
135
- <div class="lg:col-span-2">
136
- <div class="prose prose-blue max-w-none">
137
- <h4 class="text-xl font-semibold text-gray-900 mb-4">๐Ÿ“š Documentation</h4>
138
-
139
- <!-- Method Signature -->
140
- <div class="bg-gray-50 rounded-lg p-4 mb-6">
141
- <div class="flex items-center justify-between mb-2">
142
- <h5 class="font-semibold text-gray-700">Constructor</h5>
143
- <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">TypeScript</span>
144
- </div>
145
- <code class="text-sm font-mono text-[#10e80c]">
146
- new NosanaClient(network: NosanaNetwork, customConfig?: PartialClientConfig)
147
- </code>
148
- </div>
149
-
150
- <!-- TypeScript Types -->
151
- <div class="bg-gradient-to-r from-green-50 to-blue-50 rounded-lg p-4 mb-6">
152
- <button @click="toggleTypeInfo('init')"
153
- class="flex items-center justify-between w-full text-left">
154
- <h5 class="font-semibold text-[#10e80c]">๐Ÿ” TypeScript Types</h5>
155
- <svg :class="['w-4 h-4 transition-transform', showTypeInfo.init ? 'rotate-180' : '']"
156
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
157
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
158
- </path>
159
- </svg>
160
- </button>
161
- <div v-show="showTypeInfo.init" class="mt-4 space-y-3">
162
- <div class="bg-white rounded p-3 text-sm font-mono">
163
- <div class="text-blue-600">enum</div>
164
- <div class="text-[#10e80c]">NosanaNetwork</div>
165
- <div class="ml-4 text-gray-600">MAINNET = "mainnet"</div>
166
- <div class="ml-4 text-gray-600">DEVNET = "devnet"</div>
167
- </div>
168
- <div class="bg-white rounded p-3 text-sm font-mono">
169
- <div class="text-blue-600">interface</div>
170
- <div class="text-[#10e80c]">NosanaClient</div>
171
- <div class="ml-4 text-gray-600">config: ClientConfig</div>
172
- <div class="ml-4 text-gray-600">solana: SolanaUtils</div>
173
- <div class="ml-4 text-gray-600">jobs: JobsProgram</div>
174
- <div class="ml-4 text-gray-600">logger: Logger</div>
175
- <div class="ml-4 text-gray-600">wallet?: KeyPairSigner</div>
176
- <div class="ml-4 text-gray-600">setWallet(config: WalletConfig):
177
- Promise&lt;KeyPairSigner&gt;</div>
178
- </div>
179
- <div class="bg-white rounded p-3 text-sm font-mono">
180
- <div class="text-blue-600">interface</div>
181
- <div class="text-[#10e80c]">ClientConfig</div>
182
- <div class="ml-4 text-gray-600">solana: SolanaConfig</div>
183
- <div class="ml-4 text-gray-600">programs: ProgramsConfig</div>
184
- <div class="ml-4 text-gray-600">ipfs: IpfsConfig</div>
185
- <div class="ml-4 text-gray-600">wallet?: WalletConfig</div>
186
- </div>
187
- <div class="bg-white rounded p-3 text-sm font-mono">
188
- <div class="text-blue-600">type</div>
189
- <div class="text-[#10e80c]">PartialClientConfig</div>
190
- <div class="ml-4 text-gray-600">= Partial&lt;ClientConfig&gt;</div>
191
- <div class="ml-6 text-gray-500">// All properties optional for custom configuration</div>
192
- </div>
193
- <div class="bg-white rounded p-3 text-sm font-mono">
194
- <div class="text-blue-600">interface</div>
195
- <div class="text-[#10e80c]">SolanaConfig</div>
196
- <div class="ml-4 text-gray-600">rpcEndpoint: string</div>
197
- <div class="ml-4 text-gray-600">cluster: SolanaClusterMoniker</div>
198
- </div>
199
- <div class="bg-white rounded p-3 text-sm font-mono">
200
- <div class="text-blue-600">interface</div>
201
- <div class="text-[#10e80c]">ProgramsConfig</div>
202
- <div class="ml-4 text-gray-600">jobsAddress: Address</div>
203
- <div class="ml-4 text-gray-600">nosTokenAddress: Address</div>
204
- <div class="ml-4 text-gray-600">rewardsAddress: Address</div>
205
- </div>
206
- <div class="bg-white rounded p-3 text-sm font-mono">
207
- <div class="text-blue-600">interface</div>
208
- <div class="text-[#10e80c]">IpfsConfig</div>
209
- <div class="ml-4 text-gray-600">gatewayUrl: string</div>
210
- <div class="ml-4 text-gray-600">pinataJwt?: string</div>
211
- <div class="ml-4 text-gray-600">timeout?: number</div>
212
- </div>
213
- <div class="bg-white rounded p-3 text-sm font-mono">
214
- <div class="text-blue-600">type</div>
215
- <div class="text-[#10e80c]">WalletConfig</div>
216
- <div class="ml-4 text-gray-600">= string | number[] | KeyPairSigner</div>
217
- <div class="ml-6 text-gray-500">// JSON string, number array, or keypair instance</div>
218
- </div>
219
- </div>
220
- </div>
221
-
222
- <!-- Description -->
223
- <p class="text-gray-700 mb-6">
224
- Initialize a new Nosana client instance to interact with the decentralized compute network.
225
- The network parameter determines which Solana cluster to connect to for blockchain operations.
226
- You can optionally provide custom configuration to override default settings like RPC endpoints,
227
- program addresses, or IPFS gateways. You can also set a wallet to enable transaction signing and
228
- job posting.
229
- </p>
230
-
231
- <!-- Code Example -->
232
- <div class="bg-gray-900 rounded-lg overflow-hidden mb-6">
233
- <button @click="toggleCodeExample('init')"
234
- class="w-full flex items-center justify-between bg-gray-800 px-4 py-2 hover:bg-gray-700 transition-colors">
235
- <span class="text-gray-300 text-sm font-medium">Example Usage</span>
236
- <svg
237
- :class="['w-4 h-4 transition-transform text-gray-400', showCodeExample.init ? 'rotate-180' : '']"
238
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
239
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
240
- </path>
241
- </svg>
242
- </button>
243
- <div v-show="showCodeExample.init" class="p-4">
244
- <pre
245
- class="text-sm"><code class="text-green-400">import</code> <code class="text-blue-400">{ NosanaClient, NosanaNetwork }</code> <code class="text-green-400">from</code> <code class="text-yellow-400">'@nosana/kit'</code>
246
-
247
- <code class="text-gray-500">// Initialize for devnet (testing)</code>
248
- <code class="text-green-400">const</code> <code class="text-blue-400">client</code> <code class="text-white">=</code> <code class="text-green-400">new</code> <code class="text-yellow-400">NosanaClient</code><code class="text-white">(</code><code class="text-blue-400">NosanaNetwork</code><code class="text-white">.</code><code class="text-blue-400">DEVNET</code><code class="text-white">)</code>
249
-
250
- <code class="text-gray-500">// Initialize for mainnet with custom config</code>
251
- <code class="text-green-400">const</code> <code class="text-blue-400">customConfig</code> <code class="text-white">=</code> <code class="text-white">{</code>
252
- <code class="text-blue-400">solana</code><code class="text-white">:</code> <code class="text-white">{</code>
253
- <code class="text-blue-400">rpcEndpoint</code><code class="text-white">:</code> <code class="text-yellow-400">'https://custom-rpc.com'</code>
254
- <code class="text-white">}</code>
255
- <code class="text-white">}</code>
256
- <code class="text-green-400">const</code> <code class="text-blue-400">prodClient</code> <code class="text-white">=</code> <code class="text-green-400">new</code> <code class="text-yellow-400">NosanaClient</code><code class="text-white">(</code><code class="text-blue-400">NosanaNetwork</code><code class="text-white">.</code><code class="text-blue-400">MAINNET</code><code class="text-white">,</code> <code class="text-blue-400">customConfig</code><code class="text-white">)</code>
257
-
258
- <code class="text-gray-500">// Access configuration</code>
259
- <code class="text-blue-400">console</code><code class="text-white">.</code><code class="text-yellow-400">log</code><code class="text-white">(</code><code class="text-blue-400">client</code><code class="text-white">.</code><code class="text-blue-400">config</code><code class="text-white">.</code><code class="text-blue-400">solana</code><code class="text-white">.</code><code class="text-blue-400">rpcEndpoint</code><code class="text-white">)</code>
260
-
261
- <code class="text-gray-500">// Set wallet for transaction signing</code>
262
- <code class="text-green-400">const</code> <code class="text-blue-400">keypair</code> <code class="text-white">=</code> <code class="text-yellow-400">'[66,240,117,68,169,30...]'</code>
263
- <code class="text-green-400">const</code> <code class="text-blue-400">wallet</code> <code class="text-white">=</code> <code class="text-green-400">await</code> <code class="text-blue-400">client</code><code class="text-white">.</code><code class="text-yellow-400">setWallet</code><code class="text-white">(</code><code class="text-blue-400">keypair</code><code class="text-white">)</code>
264
- <code class="text-blue-400">console</code><code class="text-white">.</code><code class="text-yellow-400">log</code><code class="text-white">(</code><code class="text-yellow-400">'Wallet address:'</code><code class="text-white">,</code> <code class="text-blue-400">wallet</code><code class="text-white">.</code><code class="text-blue-400">address</code><code class="text-white">)</code></pre>
265
- </div>
266
- </div>
267
-
268
- <!-- Network Info -->
269
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
270
- <div class="bg-blue-50 border-l-4 border-blue-400 p-4 rounded">
271
- <h6 class="font-semibold text-blue-900 mb-2">๐ŸŒ Mainnet</h6>
272
- <p class="text-blue-800 text-sm">Production environment with real SOL tokens and live compute
273
- jobs.</p>
274
- </div>
275
- <div class="bg-green-50 border-l-4 border-green-400 p-4 rounded">
276
- <h6 class="font-semibold text-green-900 mb-2">๐Ÿงช Devnet</h6>
277
- <p class="text-green-800 text-sm">Testing environment with test tokens for development and
278
- experimentation.</p>
279
- </div>
280
- </div>
281
- </div>
282
- </div>
283
-
284
- <!-- Interactive Controls -->
285
- <div class="lg:col-span-1">
286
- <div class="bg-gradient-to-br from-blue-50 to-blue-100 rounded-xl p-6 sticky top-24">
287
- <h4 class="font-semibold text-gray-900 mb-4">๐ŸŽฎ Try It Now</h4>
288
- <div class="space-y-4">
289
- <!-- Network Status -->
290
- <div class="bg-white rounded-lg p-4 border border-blue-200">
291
- <div class="flex items-center space-x-2 mb-3">
292
- <div :class="[
293
- 'w-3 h-3 rounded-full',
294
- isConnected ? 'bg-green-500 animate-pulse' : 'bg-gray-400'
295
- ]"></div>
296
- <span class="text-sm font-medium text-gray-700">
297
- {{ selectedNetwork.toUpperCase() }} Network
298
- </span>
299
- </div>
300
- <p class="text-sm text-gray-600 mb-3">
301
- SDK initialized and ready for {{ selectedNetwork }} operations
302
- </p>
303
- <p class="text-xs text-gray-500">
304
- ๐Ÿ’ก Switch networks using the dropdown in the header
305
- </p>
306
- </div>
307
-
308
- <!-- Wallet Management -->
309
- <div class="bg-white rounded-lg p-4 border border-blue-200">
310
- <div class="flex items-center justify-between mb-3">
311
- <span class="text-sm font-semibold text-gray-700">
312
- ๐Ÿ” Wallet
313
- </span>
314
- <div class="flex items-center">
315
- <div :class="[
316
- 'w-2 h-2 rounded-full mr-2',
317
- currentWallet ? 'bg-green-500' : 'bg-gray-400'
318
- ]"></div>
319
- <span class="text-xs font-medium"
320
- :class="currentWallet ? 'text-green-700' : 'text-gray-600'">
321
- {{ currentWallet ? 'Connected' : 'None' }}
322
- </span>
323
- </div>
324
- </div>
325
-
326
- <div v-if="currentWallet" class="mb-3 p-2 bg-green-50 rounded text-xs">
327
- <div class="font-mono text-green-800 break-all">
328
- {{ currentWallet.substring(0, 8) }}...{{ currentWallet.substring(currentWallet.length - 8)
329
- }}
330
- </div>
331
- </div>
332
-
333
- <!-- Wallet Input Method Selection -->
334
- <select v-model="walletInputMethod"
335
- class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent mb-3">
336
- <option value="browser">๐ŸŒ Connect Browser Wallet</option>
337
- <option value="paste">๐Ÿ“‹ Paste Private Key</option>
338
- <option value="file">๐Ÿ“ Upload Keypair File</option>
339
- </select>
340
-
341
- <!-- Browser Wallet Connection -->
342
- <div v-if="walletInputMethod === 'browser'" class="mb-3">
343
- <div v-if="!browserWallet.connected" class="space-y-3">
344
- <div class="text-center p-4 border-2 border-dashed border-gray-300 rounded-lg">
345
- <p class="text-sm text-gray-600 mb-3">
346
- Connect your browser wallet to use with the SDK
347
- </p>
348
- <div class="space-y-2">
349
- <div class="grid grid-cols-2 gap-2">
350
- <button @click="connectWallet('phantom')"
351
- class="flex items-center justify-center px-3 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 text-xs font-medium">
352
- ๐Ÿ‘ป Phantom
353
- </button>
354
- <button @click="connectWallet('solflare')"
355
- class="flex items-center justify-center px-3 py-2 bg-orange-600 text-white rounded-lg hover:bg-orange-700 text-xs font-medium">
356
- ๐Ÿ”ฅ Solflare
357
- </button>
358
- </div>
359
- <div class="flex justify-center">
360
- <button @click="connectWallet('backpack')"
361
- class="flex items-center justify-center px-3 py-2 bg-black text-white rounded-lg hover:bg-gray-800 text-xs font-medium w-32">
362
- ๐ŸŽ’ Backpack
363
- </button>
364
- </div>
365
- </div>
366
- </div>
367
- </div>
368
- <div v-else class="p-3 bg-green-50 border border-green-200 rounded-lg">
369
- <div class="flex items-center justify-between mb-2">
370
- <div class="flex items-center space-x-2">
371
- <div class="w-2 h-2 bg-green-500 rounded-full"></div>
372
- <span class="text-sm font-medium text-green-800">
373
- {{ browserWallet.name }}
374
- </span>
375
- </div>
376
- <button @click="disconnectBrowserWallet"
377
- class="text-xs text-green-600 hover:text-green-800 underline">
378
- Disconnect
379
- </button>
380
- </div>
381
- <div class="text-xs font-mono text-green-700 break-all">
382
- {{ browserWallet.publicKey?.substring(0, 8) }}...{{
383
- browserWallet.publicKey?.substring(browserWallet.publicKey.length - 8) }}
384
- </div>
385
- </div>
386
- <p class="text-xs text-gray-500 mt-1">
387
- ๐ŸŒ Connect popular Solana wallets like Phantom, Solflare, and Backpack!
388
- </p>
389
- </div>
390
-
391
- <!-- Paste Private Key Input -->
392
- <div v-if="walletInputMethod === 'paste'" class="mb-3">
393
- <textarea v-model="walletInputs.privateKey"
394
- placeholder="Paste your private key in any format:&#10;โ€ข JSON Array: [66,240,117,68,169,30,179,62,...]&#10;โ€ข Number Array: 66,240,117,68,169,30,179,62,...&#10;โ€ข Base58: 2Ld9Q8E9TxsSPf9Zkxn55u2EuuXBZUiV..."
395
- class="w-full px-3 py-2 border border-gray-300 rounded-lg text-xs focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
396
- rows="4"></textarea>
397
- <p class="text-xs text-gray-500 mt-1">
398
- ๐Ÿ’ก Supports JSON array, number array, or Base58 format - the SDK will auto-detect!
399
- </p>
400
- </div>
401
-
402
- <!-- File Upload -->
403
- <div v-if="walletInputMethod === 'file'" class="mb-3">
404
- <input ref="fileInput" type="file" accept=".json" @change="handleFileUpload"
405
- class="w-full px-3 py-2 border border-gray-300 rounded-lg text-xs focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
406
- <p class="text-xs text-gray-500 mt-1">
407
- ๐Ÿ“ Upload a JSON keypair file (e.g., from Solana CLI)
408
- </p>
409
- </div>
410
-
411
- <!-- Set Wallet Button -->
412
- <button @click="setWallet" :disabled="loading || !canSetWallet"
413
- class="w-full px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 font-medium text-sm shadow-lg hover:shadow-xl mb-2">
414
- <span v-if="loading" class="flex items-center justify-center">
415
- <svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
416
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
417
- stroke-width="4"></circle>
418
- <path class="opacity-75" fill="currentColor"
419
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
420
- </path>
421
- </svg>
422
- Setting...
423
- </span>
424
- <span v-else>๐Ÿ” Set Wallet</span>
425
- </button>
426
-
427
- <!-- Clear Wallet Button -->
428
- <button v-if="currentWallet" @click="clearWallet"
429
- class="w-full px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-all duration-200 font-medium text-sm">
430
- ๐Ÿ—‘๏ธ Clear Wallet
431
- </button>
432
-
433
- <!-- Demo Keypair Button -->
434
- <button @click="loadDemoKeypair"
435
- class="w-full px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-all duration-200 font-medium text-sm mt-2">
436
- ๐ŸŽฏ Load Demo Keypair
437
- </button>
438
- </div>
439
-
440
- <!-- Connection Test -->
441
- <button @click="testConnection" :disabled="loading"
442
- class="w-full px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 font-medium shadow-lg hover:shadow-xl">
443
- <span v-if="loading" class="flex items-center justify-center">
444
- <svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
445
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4">
446
- </circle>
447
- <path class="opacity-75" fill="currentColor"
448
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 714 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
449
- </path>
450
- </svg>
451
- Testing...
452
- </span>
453
- <span v-else class="flex items-center justify-center">
454
- ๐Ÿ” Test Connection
455
- </span>
456
- </button>
457
- </div>
458
- </div>
459
- </div>
460
- </div>
461
- </div>
462
- </div>
463
-
464
- <!-- 2. Job Retrieval -->
465
- <div id="job-retrieval" class="bg-white rounded-xl shadow-lg overflow-hidden border border-gray-100">
466
- <div class="bg-gradient-to-r from-green-500 to-green-600 px-8 py-6">
467
- <div class="flex items-center justify-between">
468
- <h2 class="text-2xl font-bold text-white">๐Ÿ“‹ Job Retrieval</h2>
469
- <span class="bg-white/20 px-3 py-1 rounded-full text-sm text-green-100">Jobs API</span>
470
- </div>
471
- </div>
472
-
473
- <div class="p-8">
474
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
475
- <!-- Documentation -->
476
- <div class="lg:col-span-2">
477
- <div class="prose prose-green max-w-none">
478
- <h4 class="text-xl font-semibold text-gray-900 mb-4">๐Ÿ“š Documentation</h4>
479
-
480
- <!-- Method Signatures -->
481
- <div class="space-y-6">
482
- <div class="bg-gray-50 rounded-lg p-4">
483
- <div class="flex items-center justify-between mb-3">
484
- <h5 class="font-semibold text-gray-700">Get Single Job</h5>
485
- <span class="text-xs bg-green-100 text-green-800 px-2 py-1 rounded">async</span>
486
- </div>
487
- <code class="text-sm font-mono text-purple-600 block mb-3">
488
- client.jobs.get(address: PublicKey): Promise&lt;Job&gt;
489
- </code>
490
- <p class="text-gray-700 text-sm">Retrieve a specific job by its blockchain address.</p>
491
- </div>
492
-
493
- <div class="bg-gray-50 rounded-lg p-4">
494
- <div class="flex items-center justify-between mb-3">
495
- <h5 class="font-semibold text-gray-700">Get Multiple Jobs</h5>
496
- <span class="text-xs bg-green-100 text-green-800 px-2 py-1 rounded">async</span>
497
- </div>
498
- <code class="text-sm font-mono text-purple-600 block mb-3">
499
- client.jobs.all(filters?: JobFilters): Promise&lt;Job[]&gt;
500
- </code>
501
- <p class="text-gray-700 text-sm">Retrieve multiple jobs with optional filtering capabilities.
502
- The SDK also provides additional methods like <code
503
- class="text-xs bg-gray-100 px-1 rounded">runs()</code>, <code
504
- class="text-xs bg-gray-100 px-1 rounded">multiple()</code>, and <code
505
- class="text-xs bg-gray-100 px-1 rounded">post()</code> for advanced use cases.
506
- </p>
507
- </div>
508
- </div>
509
-
510
- <!-- TypeScript Types -->
511
- <div class="bg-gradient-to-r from-green-50 to-emerald-50 rounded-lg p-4 mt-6">
512
- <button @click="toggleTypeInfo('jobs')"
513
- class="flex items-center justify-between w-full text-left">
514
- <h5 class="font-semibold text-green-900">๐Ÿ” TypeScript Types</h5>
515
- <svg :class="['w-4 h-4 transition-transform', showTypeInfo.jobs ? 'rotate-180' : '']"
516
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
517
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
518
- </path>
519
- </svg>
520
- </button>
521
- <div v-show="showTypeInfo.jobs" class="mt-4 space-y-3">
522
- <div class="bg-white rounded p-3 text-sm font-mono">
523
- <div class="text-blue-600">interface</div>
524
- <div class="text-purple-600">Job</div>
525
- <div class="ml-4 text-gray-600">address: string</div>
526
- <div class="ml-4 text-gray-600">state: number</div>
527
- <div class="ml-4 text-gray-600">market: Address</div>
528
- <div class="ml-4 text-gray-600">node: Address</div>
529
- <div class="ml-4 text-gray-600">payer: Address</div>
530
- <div class="ml-4 text-gray-600">project: Address</div>
531
- <div class="ml-4 text-gray-600">price: number</div>
532
- <div class="ml-4 text-gray-600">ipfsJob: string</div>
533
- <div class="ml-4 text-gray-600">ipfsResult: string</div>
534
- <div class="ml-4 text-gray-600">timeStart: number</div>
535
- <div class="ml-4 text-gray-600">timeEnd: number</div>
536
- <div class="ml-4 text-gray-600">timeout: number</div>
537
- </div>
538
- <div class="bg-white rounded p-3 text-sm font-mono">
539
- <div class="text-blue-600">interface</div>
540
- <div class="text-purple-600">JobFilters</div>
541
- <div class="ml-4 text-gray-600">state?: number</div>
542
- <div class="ml-4 text-gray-600">market?: Address</div>
543
- <div class="ml-4 text-gray-600">node?: Address</div>
544
- <div class="ml-4 text-gray-600">project?: Address</div>
545
- </div>
546
- </div>
547
- </div>
548
-
549
- <!-- Code Example -->
550
- <div class="bg-gray-900 rounded-lg overflow-hidden mt-6">
551
- <button @click="toggleCodeExample('jobs')"
552
- class="w-full flex items-center justify-between bg-gray-800 px-4 py-2 hover:bg-gray-700 transition-colors">
553
- <span class="text-gray-300 text-sm font-medium">Example Usage</span>
554
- <svg
555
- :class="['w-4 h-4 transition-transform text-gray-400', showCodeExample.jobs ? 'rotate-180' : '']"
556
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
557
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
558
- </path>
559
- </svg>
560
- </button>
561
- <div v-show="showCodeExample.jobs" class="p-4">
562
- <pre class="text-sm"><code class="text-green-400">import</code> <code class="text-blue-400">{ address }</code> <code class="text-green-400">from</code> <code class="text-yellow-400">'@nosana/kit'</code>
563
-
564
- <code class="text-gray-500">// Get a specific job</code>
565
- <code class="text-green-400">const</code> <code class="text-blue-400">jobAddress</code> <code class="text-white">=</code> <code class="text-yellow-400">'BwBURHTRMM3Ckzo2Dzmw99hv6gV8Ve12b6iw4sm9qeyR'</code>
566
- <code class="text-green-400">const</code> <code class="text-blue-400">job</code> <code class="text-white">=</code> <code class="text-green-400">await</code> <code class="text-blue-400">client</code><code class="text-white">.</code><code class="text-blue-400">jobs</code><code class="text-white">.</code><code class="text-yellow-400">get</code><code class="text-white">(</code><code class="text-yellow-400">address</code><code class="text-white">(</code><code class="text-blue-400">jobAddress</code><code class="text-white">))</code>
567
-
568
- <code class="text-gray-500">// Get all jobs with filters</code>
569
- <code class="text-green-400">const</code> <code class="text-blue-400">runningJobs</code> <code class="text-white">=</code> <code class="text-green-400">await</code> <code class="text-blue-400">client</code><code class="text-white">.</code><code class="text-blue-400">jobs</code><code class="text-white">.</code><code class="text-yellow-400">all</code><code class="text-white">({</code>
570
- <code class="text-blue-400">state</code><code class="text-white">:</code> <code class="text-orange-400">2</code><code class="text-gray-500">, // Completed</code>
571
- <code class="text-blue-400">market</code><code class="text-white">:</code> <code class="text-yellow-400">'MarketAddress123...'</code>
572
- <code class="text-white">})</code></pre>
573
- </div>
574
- </div>
575
-
576
- <!-- Job States Reference -->
577
- <div class="mt-6">
578
- <h5 class="font-semibold text-gray-900 mb-4">Job State Reference</h5>
579
- <div class="grid grid-cols-2 gap-3">
580
- <div class="bg-yellow-50 border border-yellow-200 p-3 rounded-lg text-center">
581
- <div class="text-2xl mb-1">๐ŸŸก</div>
582
- <div class="font-semibold text-yellow-900">Queued</div>
583
- <div class="text-xs text-yellow-700">State: 0</div>
584
- </div>
585
- <div class="bg-blue-50 border border-blue-200 p-3 rounded-lg text-center">
586
- <div class="text-2xl mb-1">๐Ÿ”ต</div>
587
- <div class="font-semibold text-blue-900">Running</div>
588
- <div class="text-xs text-blue-700">State: 1</div>
589
- </div>
590
- <div class="bg-green-50 border border-green-200 p-3 rounded-lg text-center">
591
- <div class="text-2xl mb-1">๐ŸŸข</div>
592
- <div class="font-semibold text-green-900">Done</div>
593
- <div class="text-xs text-green-700">State: 2</div>
594
- </div>
595
- <div class="bg-red-50 border border-red-200 p-3 rounded-lg text-center">
596
- <div class="text-2xl mb-1">๐Ÿ”ด</div>
597
- <div class="font-semibold text-red-900">Stopped</div>
598
- <div class="text-xs text-red-700">State: 3</div>
599
- </div>
600
- </div>
601
- </div>
602
- </div>
603
- </div>
604
-
605
- <!-- Interactive Controls -->
606
- <div class="lg:col-span-1">
607
- <div class="bg-gradient-to-br from-green-50 to-emerald-100 rounded-xl p-6 sticky top-24">
608
- <h4 class="font-semibold text-gray-900 mb-4">๐ŸŽฎ Try It Now</h4>
609
-
610
- <div class="space-y-6">
611
- <!-- Get Single Job -->
612
- <div class="bg-white rounded-lg p-4 border border-green-200">
613
- <label class="block text-sm font-semibold text-gray-700 mb-2">
614
- ๐Ÿ” Get Job by Address
615
- </label>
616
- <input v-model="jobAddress" type="text" placeholder="Job address..."
617
- class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent mb-3" />
618
- <button @click="getJob" :disabled="loading || !jobAddress"
619
- class="w-full px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 font-medium text-sm shadow-lg hover:shadow-xl">
620
- <span v-if="loading" class="flex items-center justify-center">
621
- <svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
622
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
623
- stroke-width="4"></circle>
624
- <path class="opacity-75" fill="currentColor"
625
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
626
- </path>
627
- </svg>
628
- Loading...
629
- </span>
630
- <span v-else>๐Ÿ” Get Job</span>
631
- </button>
632
- </div>
633
-
634
- <!-- Get Multiple Jobs -->
635
- <div class="bg-white rounded-lg p-4 border border-green-200">
636
- <label class="block text-sm font-semibold text-gray-700 mb-2">
637
- ๐Ÿ“‹ Get Jobs with Filters
638
- </label>
639
- <select v-model="jobFilters.state"
640
- class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent mb-2">
641
- <option value="">Any State</option>
642
- <option value="0">๐ŸŸก Queued</option>
643
- <option value="1">๐Ÿ”ต Running</option>
644
- <option value="2">๐ŸŸข Done</option>
645
- <option value="3">๐Ÿ”ด Stopped</option>
646
- </select>
647
- <input v-model="jobFilters.market" type="text" placeholder="Market Address (optional)"
648
- class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent mb-2" />
649
- <input v-model="jobFilters.node" type="text" placeholder="Node Address (optional)"
650
- class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent mb-2" />
651
- <input v-model="jobFilters.project" type="text" placeholder="Project Address (optional)"
652
- class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent mb-3" />
653
- <button @click="getAllJobs" :disabled="loading"
654
- class="w-full px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 font-medium text-sm shadow-lg hover:shadow-xl">
655
- <span v-if="loading" class="flex items-center justify-center">
656
- <svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
657
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
658
- stroke-width="4"></circle>
659
- <path class="opacity-75" fill="currentColor"
660
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 714 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
661
- </path>
662
- </svg>
663
- Fetching...
664
- </span>
665
- <span v-else>๐Ÿ“‹ Get Jobs</span>
666
- </button>
667
- </div>
668
- </div>
669
- </div>
670
- </div>
671
- </div>
672
- </div>
673
- </div>
674
-
675
- <!-- 3. Market Discovery -->
676
- <div id="market-discovery" class="bg-white rounded-xl shadow-lg overflow-hidden border border-gray-100">
677
- <div class="bg-gradient-to-r from-orange-500 to-orange-600 px-8 py-6">
678
- <div class="flex items-center justify-between">
679
- <h2 class="text-2xl font-bold text-white">๐Ÿช Market Discovery</h2>
680
- <span class="bg-white/20 px-3 py-1 rounded-full text-sm text-orange-100">Markets API</span>
681
- </div>
682
- </div>
683
-
684
- <div class="p-8">
685
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
686
- <!-- Documentation -->
687
- <div class="lg:col-span-2">
688
- <div class="prose prose-orange max-w-none">
689
- <h4 class="text-xl font-semibold text-gray-900 mb-4">๐Ÿ“š Documentation</h4>
690
-
691
- <!-- Method Signature -->
692
- <div class="bg-gray-50 rounded-lg p-4 mb-6">
693
- <div class="flex items-center justify-between mb-3">
694
- <h5 class="font-semibold text-gray-700">Get All Markets</h5>
695
- <span class="text-xs bg-orange-100 text-orange-800 px-2 py-1 rounded">async</span>
696
- </div>
697
- <code class="text-sm font-mono text-purple-600 block mb-3">
698
- client.jobs.markets(): Promise&lt;Market[]&gt;
699
- </code>
700
- <p class="text-gray-700 text-sm">
701
- Retrieve all available compute markets with pricing and configuration details.
702
- </p>
703
- </div>
704
-
705
- <!-- TypeScript Types -->
706
- <div class="bg-gradient-to-r from-orange-50 to-red-50 rounded-lg p-4 mb-6">
707
- <button @click="toggleTypeInfo('markets')"
708
- class="flex items-center justify-between w-full text-left">
709
- <h5 class="font-semibold text-orange-900">๐Ÿ” TypeScript Types</h5>
710
- <svg :class="['w-4 h-4 transition-transform', showTypeInfo.markets ? 'rotate-180' : '']"
711
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
712
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
713
- </path>
714
- </svg>
715
- </button>
716
- <div v-show="showTypeInfo.markets" class="mt-4 space-y-3">
717
- <div class="bg-white rounded p-3 text-sm font-mono">
718
- <div class="text-blue-600">interface</div>
719
- <div class="text-purple-600">Market</div>
720
- <div class="ml-4 text-gray-600">address: string</div>
721
- <div class="ml-4 text-gray-600">authority: Address</div>
722
- <div class="ml-4 text-gray-600">jobExpiration: number</div>
723
- <div class="ml-4 text-gray-600">jobPrice: number</div>
724
- <div class="ml-4 text-gray-600">jobTimeout: number</div>
725
- <div class="ml-4 text-gray-600">jobType: number</div>
726
- <div class="ml-4 text-gray-600">vault: Address</div>
727
- <div class="ml-4 text-gray-600">vaultBump: number</div>
728
- <div class="ml-4 text-gray-600">nodeAccessKey: Address</div>
729
- <div class="ml-4 text-gray-600">nodeXnosMinimum: number</div>
730
- <div class="ml-4 text-gray-600">queueType: number</div>
731
- <div class="ml-4 text-gray-600">queue: Address[]</div>
732
- </div>
733
- </div>
734
- </div>
735
-
736
- <!-- Code Example -->
737
- <div class="bg-gray-900 rounded-lg overflow-hidden mb-6">
738
- <button @click="toggleCodeExample('markets')"
739
- class="w-full flex items-center justify-between bg-gray-800 px-4 py-2 hover:bg-gray-700 transition-colors">
740
- <span class="text-gray-300 text-sm font-medium">Example Usage</span>
741
- <svg
742
- :class="['w-4 h-4 transition-transform text-gray-400', showCodeExample.markets ? 'rotate-180' : '']"
743
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
744
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
745
- </path>
746
- </svg>
747
- </button>
748
- <div v-show="showCodeExample.markets" class="p-4">
749
- <pre
750
- class="text-sm"><code class="text-gray-500">// Get all available markets</code>
751
- <code class="text-green-400">const</code> <code class="text-blue-400">markets</code> <code class="text-white">=</code> <code class="text-green-400">await</code> <code class="text-blue-400">client</code><code class="text-white">.</code><code class="text-blue-400">jobs</code><code class="text-white">.</code><code class="text-yellow-400">markets</code><code class="text-white">()</code>
752
-
753
- <code class="text-gray-500">// Find cheapest market</code>
754
- <code class="text-green-400">const</code> <code class="text-blue-400">cheapest</code> <code class="text-white">=</code> <code class="text-blue-400">markets</code><code class="text-white">.</code><code class="text-yellow-400">reduce</code><code class="text-white">((</code><code class="text-blue-400">min</code><code class="text-white">,</code> <code class="text-blue-400">market</code><code class="text-white">) =></code>
755
- <code class="text-blue-400">market</code><code class="text-white">.</code><code class="text-blue-400">jobPrice</code> <code class="text-white">&lt;</code> <code class="text-blue-400">min</code><code class="text-white">.</code><code class="text-blue-400">jobPrice</code> <code class="text-white">?</code> <code class="text-blue-400">market</code> <code class="text-white">:</code> <code class="text-blue-400">min</code>
756
- <code class="text-white">)</code>
757
-
758
- <code class="text-gray-500">// Filter by timeout</code>
759
- <code class="text-green-400">const</code> <code class="text-blue-400">fastMarkets</code> <code class="text-white">=</code> <code class="text-blue-400">markets</code><code class="text-white">.</code><code class="text-yellow-400">filter</code><code class="text-white">(</code><code class="text-blue-400">m</code> <code class="text-white">=></code> <code class="text-blue-400">m</code><code class="text-white">.</code><code class="text-blue-400">jobTimeout</code> <code class="text-white">&lt;</code> <code class="text-orange-400">300</code><code class="text-white">)</code></pre>
760
- </div>
761
- </div>
762
-
763
- <!-- Market Info -->
764
- <div class="bg-orange-50 border-l-4 border-orange-400 p-4 rounded">
765
- <h6 class="font-semibold text-orange-900 mb-2">๐Ÿ’ก Market Insights</h6>
766
- <p class="text-orange-800 text-sm mb-2">
767
- Markets represent different compute environments with varying pricing, timeouts, and node
768
- requirements.
769
- </p>
770
- <ul class="text-orange-700 text-sm space-y-1">
771
- <li>โ€ข <strong>Job Price:</strong> Cost in lamports per job execution</li>
772
- <li>โ€ข <strong>Job Timeout:</strong> Maximum execution time in seconds</li>
773
- <li>โ€ข <strong>Job Expiration:</strong> How long jobs remain in queue</li>
774
- <li>โ€ข <strong>Node Access Key:</strong> Required key for node participation</li>
775
- </ul>
776
- </div>
777
- </div>
778
- </div>
779
-
780
- <!-- Interactive Controls -->
781
- <div class="lg:col-span-1">
782
- <div class="bg-gradient-to-br from-orange-50 to-red-100 rounded-xl p-6 sticky top-24">
783
- <h4 class="font-semibold text-gray-900 mb-4">๐ŸŽฎ Try It Now</h4>
784
-
785
- <div class="bg-white rounded-lg p-4 border border-orange-200">
786
- <div class="text-center mb-4">
787
- <div class="text-3xl mb-2">๐Ÿช</div>
788
- <p class="text-sm text-gray-600 mb-3">
789
- Discover all available compute markets and their pricing models.
790
- </p>
791
- </div>
792
- <button @click="getMarkets" :disabled="loading"
793
- class="w-full px-4 py-3 bg-orange-600 text-white rounded-lg hover:bg-orange-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 font-medium shadow-lg hover:shadow-xl">
794
- <span v-if="loading" class="flex items-center justify-center">
795
- <svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
796
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4">
797
- </circle>
798
- <path class="opacity-75" fill="currentColor"
799
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
800
- </path>
801
- </svg>
802
- Loading...
803
- </span>
804
- <span v-else>๐Ÿช Get Markets</span>
805
- </button>
806
- </div>
807
- </div>
808
- </div>
809
- </div>
810
- </div>
811
- </div>
812
-
813
- <!-- 4. Real-time Monitoring -->
814
- <div id="monitoring" class="bg-white rounded-xl shadow-lg overflow-hidden border border-gray-100">
815
- <div class="bg-gradient-to-r from-purple-500 to-purple-600 px-8 py-6">
816
- <div class="flex items-center justify-between">
817
- <h2 class="text-2xl font-bold text-white">๐Ÿ“ก Real-time Monitoring</h2>
818
- <span class="bg-white/20 px-3 py-1 rounded-full text-sm text-purple-100">Monitor API</span>
819
- </div>
820
- </div>
821
-
822
- <div class="p-8">
823
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
824
- <!-- Documentation -->
825
- <div class="lg:col-span-2">
826
- <div class="prose prose-purple max-w-none">
827
- <h4 class="text-xl font-semibold text-gray-900 mb-4">๐Ÿ“š Documentation</h4>
828
-
829
- <!-- Method Signature -->
830
- <div class="bg-gray-50 rounded-lg p-4 mb-6">
831
- <div class="flex items-center justify-between mb-3">
832
- <h5 class="font-semibold text-gray-700">Monitor Blockchain Events</h5>
833
- <span class="text-xs bg-purple-100 text-purple-800 px-2 py-1 rounded">async</span>
834
- </div>
835
- <code class="text-sm font-mono text-purple-600 block mb-3">
836
- client.jobs.monitor(callbacks: MonitorCallbacks): Promise&lt;() => void&gt;
837
- </code>
838
- <p class="text-gray-700 text-sm">
839
- Start real-time monitoring of blockchain events for jobs, runs, and markets.
840
- </p>
841
- </div>
842
-
843
- <!-- TypeScript Types -->
844
- <div class="bg-gradient-to-r from-purple-50 to-indigo-50 rounded-lg p-4 mb-6">
845
- <button @click="toggleTypeInfo('monitoring')"
846
- class="flex items-center justify-between w-full text-left">
847
- <h5 class="font-semibold text-purple-900">๐Ÿ” TypeScript Types</h5>
848
- <svg :class="['w-4 h-4 transition-transform', showTypeInfo.monitoring ? 'rotate-180' : '']"
849
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
850
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
851
- </path>
852
- </svg>
853
- </button>
854
- <div v-show="showTypeInfo.monitoring" class="mt-4 space-y-3">
855
- <div class="bg-white rounded p-3 text-sm font-mono">
856
- <div class="text-blue-600">interface</div>
857
- <div class="text-purple-600">MonitorCallbacks</div>
858
- <div class="ml-4 text-gray-600">onJobAccount?: (job: Job) => Promise&lt;void&gt;</div>
859
- <div class="ml-4 text-gray-600">onRunAccount?: (run: Run) => Promise&lt;void&gt;</div>
860
- <div class="ml-4 text-gray-600">onMarketAccount?: (market: Market) => Promise&lt;void&gt;
861
- </div>
862
- <div class="ml-4 text-gray-600">onError?: (error: Error, type: string) =>
863
- Promise&lt;void&gt;</div>
864
- </div>
865
- </div>
866
- </div>
867
-
868
- <!-- Code Example -->
869
- <div class="bg-gray-900 rounded-lg overflow-hidden mb-6">
870
- <button @click="toggleCodeExample('monitoring')"
871
- class="w-full flex items-center justify-between bg-gray-800 px-4 py-2 hover:bg-gray-700 transition-colors">
872
- <span class="text-gray-300 text-sm font-medium">Example Usage</span>
873
- <svg
874
- :class="['w-4 h-4 transition-transform text-gray-400', showCodeExample.monitoring ? 'rotate-180' : '']"
875
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
876
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7">
877
- </path>
878
- </svg>
879
- </button>
880
- <div v-show="showCodeExample.monitoring" class="p-4">
881
- <pre class="text-sm"><code class="text-gray-500">// Start monitoring with callbacks</code>
882
- <code class="text-green-400">const</code> <code class="text-blue-400">stopMonitoring</code> <code class="text-white">=</code> <code class="text-green-400">await</code> <code class="text-blue-400">client</code><code class="text-white">.</code><code class="text-blue-400">jobs</code><code class="text-white">.</code><code class="text-yellow-400">monitor</code><code class="text-white">({</code>
883
- <code class="text-blue-400">onJobAccount</code><code class="text-white">:</code> <code class="text-green-400">async</code> <code class="text-white">(</code><code class="text-blue-400">job</code><code class="text-white">) =></code> <code class="text-white">{</code>
884
- <code class="text-blue-400">console</code><code class="text-white">.</code><code class="text-yellow-400">log</code><code class="text-white">(</code><code class="text-yellow-400">'Job updated:'</code><code class="text-white">,</code> <code class="text-blue-400">job</code><code class="text-white">.</code><code class="text-blue-400">address</code><code class="text-white">)</code>
885
- <code class="text-white">},</code>
886
- <code class="text-blue-400">onRunAccount</code><code class="text-white">:</code> <code class="text-green-400">async</code> <code class="text-white">(</code><code class="text-blue-400">run</code><code class="text-white">) =></code> <code class="text-white">{</code>
887
- <code class="text-blue-400">console</code><code class="text-white">.</code><code class="text-yellow-400">log</code><code class="text-white">(</code><code class="text-yellow-400">'Run updated:'</code><code class="text-white">,</code> <code class="text-blue-400">run</code><code class="text-white">.</code><code class="text-blue-400">address</code><code class="text-white">)</code>
888
- <code class="text-white">},</code>
889
- <code class="text-blue-400">onError</code><code class="text-white">:</code> <code class="text-green-400">async</code> <code class="text-white">(</code><code class="text-blue-400">error</code><code class="text-white">) =></code> <code class="text-white">{</code>
890
- <code class="text-blue-400">console</code><code class="text-white">.</code><code class="text-red-400">error</code><code class="text-white">(</code><code class="text-yellow-400">'Monitor error:'</code><code class="text-white">,</code> <code class="text-blue-400">error</code><code class="text-white">)</code>
891
- <code class="text-white">}</code>
892
- <code class="text-white">})</code>
893
-
894
- <code class="text-gray-500">// Stop monitoring when done</code>
895
- <code class="text-blue-400">stopMonitoring</code><code class="text-white">()</code></pre>
896
- </div>
897
- </div>
898
-
899
- <!-- Monitor Info -->
900
- <div class="bg-purple-50 border-l-4 border-purple-400 p-4 rounded">
901
- <h6 class="font-semibold text-purple-900 mb-2">โšก Real-time Updates</h6>
902
- <p class="text-purple-800 text-sm mb-2">
903
- Monitor blockchain events in real-time to track job progress, run status, and market changes.
904
- </p>
905
- <ul class="text-purple-700 text-sm space-y-1">
906
- <li>โ€ข <strong>Job Events:</strong> State changes, node assignments, completions</li>
907
- <li>โ€ข <strong>Run Events:</strong> Execution start/stop, result updates</li>
908
- <li>โ€ข <strong>Market Events:</strong> Price changes, configuration updates</li>
909
- <li>โ€ข <strong>Error Handling:</strong> Connection issues, parsing errors</li>
910
- </ul>
911
- </div>
912
- </div>
913
- </div>
914
-
915
- <!-- Interactive Controls -->
916
- <div class="lg:col-span-1">
917
- <div class="bg-gradient-to-br from-purple-50 to-indigo-100 rounded-xl p-6 sticky top-24">
918
- <h4 class="font-semibold text-gray-900 mb-4">๐ŸŽฎ Try It Now</h4>
919
-
920
- <div class="space-y-4">
921
- <div class="bg-white rounded-lg p-4 border border-purple-200">
922
- <div class="flex items-center justify-between mb-3">
923
- <span class="text-sm font-semibold text-gray-700">
924
- Monitor Status
925
- </span>
926
- <div class="flex items-center">
927
- <div :class="[
928
- 'w-3 h-3 rounded-full mr-2',
929
- isMonitoring ? 'bg-green-500 animate-pulse' : 'bg-gray-400'
930
- ]"></div>
931
- <span class="text-sm font-medium"
932
- :class="isMonitoring ? 'text-green-700' : 'text-gray-600'">
933
- {{ isMonitoring ? 'Active' : 'Inactive' }}
934
- </span>
935
- </div>
936
- </div>
937
-
938
- <div class="text-center mb-4">
939
- <div class="text-3xl mb-2">{{ isMonitoring ? '๐Ÿ“ก' : '๐Ÿ“ด' }}</div>
940
- <p class="text-xs text-gray-500">
941
- {{ isMonitoring ? 'Watching for blockchain events' : 'Ready to start monitoring' }}
942
- </p>
943
- </div>
944
-
945
- <button @click="toggleMonitoring" :disabled="loading" :class="[
946
- 'w-full px-4 py-3 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 font-medium shadow-lg hover:shadow-xl',
947
- isMonitoring
948
- ? 'bg-red-600 text-white hover:bg-red-700'
949
- : 'bg-purple-600 text-white hover:bg-purple-700'
950
- ]">
951
- <span v-if="loading" class="flex items-center justify-center">
952
- <svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
953
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
954
- stroke-width="4"></circle>
955
- <path class="opacity-75" fill="currentColor"
956
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
957
- </path>
958
- </svg>
959
- Processing...
960
- </span>
961
- <span v-else>
962
- {{ isMonitoring ? '๐Ÿ›‘ Stop Monitoring' : '๐Ÿš€ Start Monitoring' }}
963
- </span>
964
- </button>
965
-
966
- <p class="text-xs text-gray-500 mt-3 text-center">
967
- Monitor real-time blockchain events and see updates in the terminal
968
- </p>
969
- </div>
970
- </div>
971
- </div>
972
- </div>
973
- </div>
974
- </div>
975
- </div>
976
- </div>
977
- </div>
978
-
979
- <!-- Terminal Output - Right Sidebar -->
980
- <div :class="[
981
- 'transition-all duration-300',
982
- isFullscreenTerminal ? 'fixed inset-0 z-50' : 'lg:col-span-5'
983
- ]">
984
- <div :class="[
985
- 'shadow-lg flex flex-col',
986
- isFullscreenTerminal
987
- ? 'bg-gray-900 h-full w-full'
988
- : 'bg-white rounded-xl sticky top-24 max-h-[80vh] border border-gray-100'
989
- ]">
990
- <div class="bg-gradient-to-r from-gray-800 to-gray-900 px-6 py-4 flex items-center justify-between"
991
- :class="isFullscreenTerminal ? 'rounded-none' : 'rounded-t-xl'">
992
- <div class="flex items-center space-x-3">
993
- <!-- <div class="flex space-x-1">
994
- <div class="w-3 h-3 bg-red-500 rounded-full"></div>
995
- <div class="w-3 h-3 bg-yellow-500 rounded-full"></div>
996
- <div class="w-3 h-3 bg-green-500 rounded-full"></div>
997
- </div> -->
998
- <h3 class="text-white font-semibold">๐Ÿ’ป Terminal Output</h3>
999
- </div>
1000
- <div class="flex items-center space-x-3">
1001
- <span class="text-gray-300 text-sm">{{ logs.length }} lines</span>
1002
- <button @click="autoScroll = !autoScroll" :class="[
1003
- 'px-3 py-1 text-xs rounded-full transition-all duration-200',
1004
- autoScroll ? 'bg-green-600 text-white' : 'bg-gray-600 text-gray-200'
1005
- ]">
1006
- Auto: {{ autoScroll ? 'ON' : 'OFF' }}
1007
- </button>
1008
- <button @click="toggleFullscreenTerminal"
1009
- class="p-2 text-gray-300 hover:text-white hover:bg-gray-700 rounded-lg transition-colors"
1010
- :title="isFullscreenTerminal ? 'Exit Fullscreen' : 'Enter Fullscreen'">
1011
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1012
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" :d="isFullscreenTerminal
1013
- ? 'M9 9l6 6m0-6l-6 6M21 3l-6 6m0 0V4m0 5h5M3 21l6-6m0 0v5m0-5H4'
1014
- : 'M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4'">
1015
- </path>
1016
- </svg>
1017
- </button>
1018
- </div>
1019
- </div>
1020
-
1021
- <div ref="terminal" :class="[
1022
- 'terminal flex-1 overflow-y-auto overflow-x-auto p-6',
1023
- isFullscreenTerminal ? 'min-h-[calc(100vh-8.3rem)]' : 'min-h-[400px] max-h-[400px]'
1024
- ]">
1025
- <div v-for="(log, index) in logs" :key="index" class="terminal-line whitespace-nowrap mb-2">
1026
- <span class="terminal-timestamp">{{ log.timestamp }}</span>
1027
- <span :class="getLogClass(log.type)">{{ log.message }}</span>
1028
- </div>
1029
- <div v-if="logs.length === 0" class="text-gray-400 italic text-center mt-12">
1030
- <div class="text-4xl mb-4">๐Ÿ’ป</div>
1031
- <p class="text-lg font-medium mb-2">Terminal Ready</p>
1032
- <p class="text-sm">Output will appear here when you test the API methods</p>
1033
- </div>
1034
- </div>
1035
-
1036
- <div class="p-4 border-t border-gray-200 bg-gray-50"
1037
- :class="isFullscreenTerminal ? 'rounded-none' : 'rounded-b-xl'">
1038
- <div class="flex space-x-2">
1039
- <button @click="clearLogs"
1040
- class="flex-1 px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 text-sm transition-all duration-200 font-medium shadow-lg hover:shadow-xl">
1041
- ๐Ÿ—‘๏ธ Clear Terminal
1042
- </button>
1043
- <button @click="exportLogs"
1044
- class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm transition-all duration-200 font-medium shadow-lg hover:shadow-xl">
1045
- ๐Ÿ’พ Export
1046
- </button>
1047
- </div>
1048
- </div>
1049
- </div>
1050
- </div>
1051
- </div>
1052
- </div>
1053
- </div>
1054
- </template>
1055
-
1056
- <script setup>
1057
- import { ref, computed, onMounted, nextTick } from 'vue'
1058
- const { NosanaClient, NosanaNetwork, address } = await import('@nosana/kit')
1059
- // Reactive state
1060
- const selectedNetwork = ref('mainnet')
1061
- const isConnected = ref(false)
1062
- const loading = ref(false)
1063
- const jobAddress = ref()
1064
- const jobFilters = ref({
1065
- state: '',
1066
- market: '',
1067
- node: '',
1068
- project: ''
1069
- })
1070
- const isMonitoring = ref(false)
1071
- const autoScroll = ref(true)
1072
- const logs = ref([])
1073
- const terminal = ref(null)
1074
- const isFullscreenTerminal = ref(false)
1075
-
1076
- // Wallet state
1077
- const currentWallet = ref(null)
1078
- const walletInputMethod = ref('browser')
1079
- const walletInputs = ref({
1080
- privateKey: '',
1081
- fileContent: null
1082
- })
1083
- const fileInput = ref(null)
1084
-
1085
- // Browser wallet state
1086
- const browserWallet = ref({
1087
- connected: false,
1088
- connecting: false,
1089
- name: null,
1090
- publicKey: null,
1091
- adapter: null
1092
- })
1093
-
1094
- // Documentation visibility
1095
- const showTypeInfo = ref({
1096
- init: false,
1097
- jobs: false,
1098
- markets: false,
1099
- monitoring: false
1100
- })
1101
- const showCodeExample = ref({
1102
- init: false,
1103
- jobs: false,
1104
- markets: false,
1105
- monitoring: false
1106
- })
1107
-
1108
- // SDK client
1109
- let client = null
1110
- let stopMonitoring = null
1111
-
1112
- // Computed properties
1113
- const canSetWallet = computed(() => {
1114
- switch (walletInputMethod.value) {
1115
- case 'browser':
1116
- return browserWallet.value.connected
1117
- case 'paste':
1118
- return walletInputs.value.privateKey.trim().length > 0
1119
- case 'file':
1120
- return walletInputs.value.fileContent !== null
1121
- default:
1122
- return false
1123
- }
1124
- })
1125
-
1126
- // Log types
1127
- const LogType = {
1128
- INFO: 'info',
1129
- SUCCESS: 'success',
1130
- ERROR: 'error',
1131
- WARNING: 'warning'
1132
- }
1133
-
1134
- // Utility functions
1135
- const addLog = (message, type = LogType.INFO) => {
1136
- const timestamp = new Date().toLocaleTimeString()
1137
- logs.value.push({ timestamp, message, type })
1138
-
1139
- // Always auto-scroll to bottom if autoScroll is enabled
1140
- nextTick(() => {
1141
- if (terminal.value && autoScroll.value) {
1142
- terminal.value.scrollTop = terminal.value.scrollHeight
1143
- }
1144
- })
1145
- }
1146
-
1147
- const getLogClass = (type) => {
1148
- switch (type) {
1149
- case LogType.ERROR: return 'terminal-error'
1150
- case LogType.SUCCESS: return 'terminal-success'
1151
- case LogType.WARNING: return 'terminal-warning'
1152
- default: return 'terminal-info'
1153
- }
1154
- }
1155
-
1156
- const clearLogs = () => {
1157
- logs.value = []
1158
- addLog('Logs cleared', LogType.INFO)
1159
- }
1160
-
1161
- const exportLogs = () => {
1162
- const logText = logs.value.map(log => `[${log.timestamp}] ${log.message}`).join('\n')
1163
- const blob = new Blob([logText], { type: 'text/plain' })
1164
- const url = URL.createObjectURL(blob)
1165
- const a = document.createElement('a')
1166
- a.href = url
1167
- a.download = `nosana-kit-logs-${new Date().toISOString().slice(0, 10)}.txt`
1168
- document.body.appendChild(a)
1169
- a.click()
1170
- document.body.removeChild(a)
1171
- URL.revokeObjectURL(url)
1172
- addLog('๐Ÿ“ Logs exported to file', LogType.SUCCESS)
1173
- }
1174
-
1175
- const copyInstallCommand = () => {
1176
- navigator.clipboard.writeText('npm install @nosana/kit')
1177
- addLog('๐Ÿ“‹ Installation command copied to clipboard', LogType.SUCCESS)
1178
- }
1179
-
1180
- const toggleFullscreenTerminal = () => {
1181
- isFullscreenTerminal.value = !isFullscreenTerminal.value
1182
- addLog(`๐Ÿ–ฅ๏ธ Terminal ${isFullscreenTerminal.value ? 'expanded to' : 'exited'} fullscreen`, LogType.INFO)
1183
- }
1184
-
1185
- const toggleTypeInfo = (section) => {
1186
- showTypeInfo.value[section] = !showTypeInfo.value[section]
1187
- }
1188
-
1189
- const toggleCodeExample = (section) => {
1190
- showCodeExample.value[section] = !showCodeExample.value[section]
1191
- }
1192
-
1193
- const scrollToSection = (sectionId) => {
1194
- const element = document.getElementById(sectionId)
1195
- if (element) {
1196
- element.scrollIntoView({ behavior: 'smooth', block: 'start' })
1197
- addLog(`๐Ÿ“ Navigated to ${sectionId} section`, LogType.INFO)
1198
- }
1199
- }
1200
-
1201
- const scrollToTerminal = () => {
1202
- if (terminal.value) {
1203
- terminal.value.scrollIntoView({ behavior: 'smooth', block: 'start' })
1204
- }
1205
- }
1206
-
1207
- // Wallet operations
1208
- const handleFileUpload = (event) => {
1209
- const file = event.target.files[0]
1210
- if (file) {
1211
- const reader = new FileReader()
1212
- reader.onload = (e) => {
1213
- try {
1214
- const content = e.target.result
1215
- JSON.parse(content) // Validate JSON
1216
- walletInputs.value.fileContent = content
1217
- addLog(`๐Ÿ“ File loaded: ${file.name}`, LogType.SUCCESS)
1218
- } catch (error) {
1219
- addLog(`โŒ Invalid JSON file: ${error.message}`, LogType.ERROR)
1220
- walletInputs.value.fileContent = null
1221
- if (fileInput.value) {
1222
- fileInput.value.value = ''
1223
- }
1224
- }
1225
- }
1226
- reader.readAsText(file)
1227
- }
1228
- }
1229
-
1230
- const setWallet = async () => {
1231
- if (!client) {
1232
- addLog('โŒ SDK not initialized', LogType.ERROR)
1233
- scrollToTerminal()
1234
- return
1235
- }
1236
-
1237
- try {
1238
- loading.value = true
1239
-
1240
- let walletData
1241
- let inputType = 'unknown'
1242
-
1243
- switch (walletInputMethod.value) {
1244
- case 'browser':
1245
- if (!browserWallet.value.connected || !browserWallet.value.adapter) {
1246
- throw new Error('Browser wallet not connected')
1247
- }
1248
- // For browser wallets, we'll use the adapter directly
1249
- // The SDK's setWallet method should accept a wallet adapter
1250
- walletData = browserWallet.value.adapter
1251
- inputType = `${browserWallet.value.name} browser wallet`
1252
- break
1253
- case 'paste':
1254
- walletData = walletInputs.value.privateKey.trim()
1255
- // Try to detect the format for better logging
1256
- if (walletData.startsWith('[') && walletData.endsWith(']')) {
1257
- inputType = 'JSON array'
1258
- } else if (walletData.includes(',') && !walletData.includes('[')) {
1259
- inputType = 'number array'
1260
- } else if (walletData.length > 80 && !walletData.includes(',')) {
1261
- inputType = 'Base58'
1262
- } else {
1263
- inputType = 'auto-detected format'
1264
- }
1265
- break
1266
- case 'file':
1267
- walletData = walletInputs.value.fileContent
1268
- inputType = 'keypair file'
1269
- break
1270
- default:
1271
- throw new Error('Invalid wallet input method')
1272
- }
1273
-
1274
- addLog(`๐Ÿ” Setting wallet from ${inputType}...`, LogType.INFO)
1275
- scrollToTerminal()
1276
-
1277
- const wallet = await client.setWallet(walletData)
1278
-
1279
- if (wallet && wallet.address) {
1280
- currentWallet.value = wallet.address
1281
- addLog(`โœ… Wallet set successfully!`, LogType.SUCCESS)
1282
- addLog(`๐Ÿ“ Address: ${wallet.address}`, LogType.INFO)
1283
- addLog(`๐Ÿ” Source: ${inputType}`, LogType.INFO)
1284
-
1285
- // For browser wallets, also show the wallet name
1286
- if (walletInputMethod.value === 'browser') {
1287
- addLog(`๐ŸฆŠ Connected via: ${browserWallet.value.name}`, LogType.INFO)
1288
- }
1289
- } else {
1290
- throw new Error('Wallet set but no address returned')
1291
- }
1292
-
1293
- } catch (error) {
1294
- addLog(`โŒ Failed to set wallet: ${error.message}`, LogType.ERROR)
1295
- currentWallet.value = null
1296
- } finally {
1297
- loading.value = false
1298
- }
1299
- }
1300
-
1301
- const clearWallet = async () => {
1302
- currentWallet.value = null
1303
- if (client) {
1304
- client.wallet = undefined
1305
- }
1306
-
1307
- // Also disconnect browser wallet if it's connected
1308
- if (browserWallet.value.connected) {
1309
- await disconnectBrowserWallet()
1310
- }
1311
-
1312
- addLog('๐Ÿ—‘๏ธ Wallet cleared', LogType.INFO)
1313
- scrollToTerminal()
1314
- }
1315
-
1316
- const loadDemoKeypair = () => {
1317
- const demoKeypair = '[66,240,117,68,169,30,179,62,57,123,28,249,122,218,186,173,196,222,208,58,126,168,32,91,126,64,102,33,220,51,49,97,6,197,228,206,210,117,23,184,89,48,217,110,194,137,242,129,112,23,140,120,148,249,210,18,105,192,40,197,250,132,40,149]'
1318
-
1319
- walletInputMethod.value = 'paste'
1320
- walletInputs.value.privateKey = demoKeypair
1321
- }
1322
-
1323
- // Browser wallet operations
1324
- const connectWallet = async (walletName) => {
1325
- try {
1326
- browserWallet.value.connecting = true
1327
- addLog(`๐ŸŒ Connecting to ${walletName} wallet...`, LogType.INFO)
1328
-
1329
- let adapter
1330
-
1331
- switch (walletName) {
1332
- case 'phantom':
1333
- if (process.client && window.phantom?.solana) {
1334
- adapter = window.phantom.solana
1335
- }
1336
- break
1337
- case 'solflare':
1338
- if (process.client && window.solflare) {
1339
- adapter = window.solflare
1340
- }
1341
- break
1342
- case 'backpack':
1343
- if (process.client && window.backpack) {
1344
- adapter = window.backpack
1345
- }
1346
- break
1347
- default:
1348
- throw new Error('Unsupported wallet')
1349
- }
1350
-
1351
- if (!adapter) {
1352
- throw new Error(`${walletName} wallet not found. Please install the ${walletName} browser extension and refresh the page.`)
1353
- }
1354
-
1355
- // Request connection
1356
- const connection = await adapter.connect()
1357
-
1358
- if (adapter.publicKey) {
1359
- browserWallet.value = {
1360
- connected: true,
1361
- connecting: false,
1362
- name: walletName.charAt(0).toUpperCase() + walletName.slice(1),
1363
- publicKey: adapter.publicKey.toString(),
1364
- adapter: adapter
1365
- }
1366
-
1367
- addLog(`โœ… Successfully connected to ${walletName}!`, LogType.SUCCESS)
1368
- addLog(`๐Ÿ“ Public Key: ${adapter.publicKey.toString()}`, LogType.INFO)
1369
- addLog(`๐Ÿ”— Connection: ${connection ? 'Established' : 'Ready'}`, LogType.INFO)
1370
- } else {
1371
- throw new Error('Failed to get public key from wallet')
1372
- }
1373
-
1374
- } catch (error) {
1375
- addLog(`โŒ Failed to connect to ${walletName}: ${error.message}`, LogType.ERROR)
1376
- browserWallet.value.connecting = false
1377
- browserWallet.value.connected = false
1378
- } finally {
1379
- browserWallet.value.connecting = false
1380
- }
1381
- }
1382
-
1383
- const disconnectBrowserWallet = async () => {
1384
- try {
1385
- if (browserWallet.value.adapter && browserWallet.value.adapter.disconnect) {
1386
- console.log('bro', browserWallet.value.adapter, browserWallet.value)
1387
- await browserWallet.value.adapter.disconnect()
1388
- }
1389
-
1390
- browserWallet.value = {
1391
- connected: false,
1392
- connecting: false,
1393
- name: null,
1394
- publicKey: null,
1395
- adapter: null
1396
- }
1397
-
1398
- // Clear the current wallet if it was set from browser wallet
1399
- if (currentWallet.value && currentWallet.value === browserWallet.value.publicKey) {
1400
- currentWallet.value = null
1401
- if (client) {
1402
- client.wallet = undefined
1403
- }
1404
- }
1405
-
1406
- addLog('๐Ÿ”Œ Browser wallet disconnected', LogType.INFO)
1407
- scrollToTerminal()
1408
-
1409
- } catch (error) {
1410
- addLog(`โŒ Error disconnecting wallet: ${error.message}`, LogType.ERROR)
1411
- }
1412
- }
1413
-
1414
- // SDK operations
1415
- const initializeClient = async () => {
1416
- try {
1417
- // Stop monitoring if active before switching networks
1418
- if (isMonitoring.value) {
1419
- if (stopMonitoring) {
1420
- stopMonitoring()
1421
- stopMonitoring = null
1422
- }
1423
- isMonitoring.value = false
1424
- addLog('๐Ÿ›‘ Stopped monitoring due to network switch', LogType.WARNING)
1425
- }
1426
-
1427
- // Clear wallet when switching networks
1428
- if (currentWallet.value) {
1429
- currentWallet.value = null
1430
- addLog('๐Ÿ” Wallet cleared due to network switch', LogType.WARNING)
1431
- }
1432
-
1433
- loading.value = true
1434
- addLog(`Initializing Nosana SDK for ${selectedNetwork.value}...`, LogType.INFO)
1435
-
1436
- const network = selectedNetwork.value === 'mainnet'
1437
- ? NosanaNetwork.MAINNET
1438
- : NosanaNetwork.DEVNET
1439
-
1440
- client = new NosanaClient(network)
1441
- isConnected.value = true
1442
-
1443
- addLog(`โœ… SDK initialized successfully for ${selectedNetwork.value}`, LogType.SUCCESS)
1444
- addLog(`๐Ÿ“ก RPC Endpoint: ${client.config.solana.rpcEndpoint}`, LogType.INFO)
1445
- addLog(`๐Ÿช Jobs Program: ${client.config.programs.jobsAddress}`, LogType.INFO)
1446
-
1447
- } catch (error) {
1448
- addLog(`โŒ Failed to initialize SDK: ${error.message}`, LogType.ERROR)
1449
- isConnected.value = false
1450
- } finally {
1451
- loading.value = false
1452
- }
1453
- }
1454
-
1455
- const testConnection = async () => {
1456
- if (!client) {
1457
- addLog('โŒ SDK not initialized', LogType.ERROR)
1458
- scrollToTerminal()
1459
- return
1460
- }
1461
-
1462
- try {
1463
- loading.value = true
1464
- addLog('๐Ÿ” Testing connection...', LogType.INFO)
1465
- scrollToTerminal()
1466
-
1467
- // Test by getting the latest blockhash
1468
- const latestBlockhash = await client.solana.getLatestBlockhash()
1469
- addLog(`โœ… Connection successful! Latest blockhash: ${latestBlockhash.blockhash}`, LogType.SUCCESS)
1470
-
1471
- } catch (error) {
1472
- addLog(`โŒ Connection test failed: ${error.message}`, LogType.ERROR)
1473
- } finally {
1474
- loading.value = false
1475
- }
1476
- }
1477
-
1478
- const getJob = async () => {
1479
- if (!client || !jobAddress.value) {
1480
- addLog('โŒ SDK not initialized or no job address provided', LogType.ERROR)
1481
- scrollToTerminal()
1482
- return
1483
- }
1484
-
1485
- try {
1486
- loading.value = true
1487
- addLog(`๐Ÿ” Fetching job: ${jobAddress.value}`, LogType.INFO)
1488
- scrollToTerminal()
1489
-
1490
- const job = await client.jobs.get(address(jobAddress.value))
1491
-
1492
- addLog(`โœ… Job retrieved successfully!`, LogType.SUCCESS)
1493
- addLog(`๐Ÿ“‹ Job Details:`, LogType.INFO)
1494
- addLog(` Address: ${job.address}`, LogType.INFO)
1495
- addLog(` State: ${getJobStateName(job.state)}`, LogType.INFO)
1496
- addLog(` Market: ${job.market}`, LogType.INFO)
1497
- addLog(` Price: ${job.price}`, LogType.INFO)
1498
- addLog(` IPFS Job: ${job.ipfsJob}`, LogType.INFO)
1499
- if (job.node) addLog(` Node: ${job.node}`, LogType.INFO)
1500
- if (job.timeStart) addLog(` Start Time: ${new Date(job.timeStart * 1000).toLocaleString()}`, LogType.INFO)
1501
-
1502
- } catch (error) {
1503
- addLog(`โŒ Failed to fetch job: ${error.message}`, LogType.ERROR)
1504
- } finally {
1505
- loading.value = false
1506
- }
1507
- }
1508
-
1509
- const getAllJobs = async () => {
1510
- if (!client) {
1511
- addLog('โŒ SDK not initialized', LogType.ERROR)
1512
- scrollToTerminal()
1513
- return
1514
- }
1515
-
1516
- try {
1517
- loading.value = true
1518
- addLog('๐Ÿ” Fetching jobs with filters...', LogType.INFO)
1519
- scrollToTerminal()
1520
-
1521
- const filters = {}
1522
- if (jobFilters.value.state !== '') {
1523
- filters.state = parseInt(jobFilters.value.state)
1524
- }
1525
- if (jobFilters.value.market !== '') {
1526
- filters.market = address(jobFilters.value.market)
1527
- }
1528
- if (jobFilters.value.node !== '') {
1529
- filters.node = address(jobFilters.value.node)
1530
- }
1531
- if (jobFilters.value.project !== '') {
1532
- filters.project = address(jobFilters.value.project)
1533
- }
1534
- const jobs = await client.jobs.all(filters)
1535
- // Show first 20 jobs to avoid overwhelming the terminal
1536
- const limitedJobs = jobs.slice(0, 20)
1537
-
1538
- addLog(`โœ… Retrieved ${limitedJobs.length} jobs (total: ${jobs.length})`, LogType.SUCCESS)
1539
-
1540
- limitedJobs.forEach((job, index) => {
1541
- addLog(`๐Ÿ“‹ Job ${index + 1}:`, LogType.INFO)
1542
- addLog(job, LogType.INFO)
1543
- })
1544
-
1545
- } catch (error) {
1546
- addLog(`โŒ Failed to fetch jobs: ${error.message}`, LogType.ERROR)
1547
- } finally {
1548
- loading.value = false
1549
- }
1550
- }
1551
-
1552
- const getMarkets = async () => {
1553
- if (!client) {
1554
- addLog('โŒ SDK not initialized', LogType.ERROR)
1555
- scrollToTerminal()
1556
- return
1557
- }
1558
-
1559
- try {
1560
- loading.value = true
1561
- addLog('๐Ÿ” Fetching all markets...', LogType.INFO)
1562
- scrollToTerminal()
1563
-
1564
- const markets = await client.jobs.markets()
1565
-
1566
- addLog(`โœ… Retrieved ${markets.length} markets`, LogType.SUCCESS)
1567
-
1568
- markets.forEach((market, index) => {
1569
- addLog(`๐Ÿช Market ${index + 1}:`, LogType.INFO)
1570
- addLog(market, LogType.INFO)
1571
- })
1572
-
1573
- } catch (error) {
1574
- addLog(`โŒ Failed to fetch markets: ${error.message}`, LogType.ERROR)
1575
- } finally {
1576
- loading.value = false
1577
- }
1578
- }
1579
-
1580
- const toggleMonitoring = async () => {
1581
- if (!client) {
1582
- addLog('โŒ SDK not initialized', LogType.ERROR)
1583
- scrollToTerminal()
1584
- return
1585
- }
1586
-
1587
- if (isMonitoring.value) {
1588
- // Stop monitoring
1589
- if (stopMonitoring) {
1590
- stopMonitoring()
1591
- stopMonitoring = null
1592
- }
1593
- isMonitoring.value = false
1594
- addLog('๐Ÿ›‘ Stopped monitoring account updates', LogType.WARNING)
1595
- scrollToTerminal()
1596
- } else {
1597
- // Start monitoring
1598
- try {
1599
- loading.value = true
1600
- addLog('๐Ÿš€ Starting real-time monitoring...', LogType.INFO)
1601
- scrollToTerminal()
1602
-
1603
- stopMonitoring = await client.jobs.monitor({
1604
- onJobAccount: async (jobAccount) => {
1605
- addLog(`๐Ÿ”„ Job account updated: ${jobAccount.address}`, LogType.SUCCESS)
1606
- addLog(` State: ${getJobStateName(jobAccount.state)}`, LogType.INFO)
1607
- if (jobAccount.node) addLog(` Node: ${jobAccount.node}`, LogType.INFO)
1608
- },
1609
-
1610
- onRunAccount: async (runAccount) => {
1611
- addLog(`๐Ÿƒ Run account updated: ${runAccount.address}`, LogType.SUCCESS)
1612
- addLog(` Job: ${runAccount.job}`, LogType.INFO)
1613
- addLog(` Node: ${runAccount.node}`, LogType.INFO)
1614
- },
1615
-
1616
- onMarketAccount: async (marketAccount) => {
1617
- addLog(`๐Ÿช Market account updated: ${marketAccount.address}`, LogType.SUCCESS)
1618
- addLog(` Job Price: ${marketAccount.jobPrice}`, LogType.INFO)
1619
- },
1620
-
1621
- onError: async (error, accountType) => {
1622
- addLog(`โŒ Monitor error (${accountType}): ${error.message}`, LogType.ERROR)
1623
- }
1624
- })
1625
-
1626
- isMonitoring.value = true
1627
- addLog('โœ… Real-time monitoring started successfully!', LogType.SUCCESS)
1628
- addLog('๐Ÿ‘€ Watching for job, run, and market account updates...', LogType.INFO)
1629
-
1630
- } catch (error) {
1631
- addLog(`โŒ Failed to start monitoring: ${error.message}`, LogType.ERROR)
1632
- } finally {
1633
- loading.value = false
1634
- }
1635
- }
1636
- }
1637
-
1638
- const getJobStateName = (state) => {
1639
- const states = {
1640
- 0: 'Queued',
1641
- 1: 'Running',
1642
- 2: 'Done',
1643
- 3: 'Stopped'
1644
- }
1645
- return states[state] || `Unknown (${state})`
1646
- }
1647
-
1648
- // Initialize on mount
1649
- onMounted(() => {
1650
- addLog('๐Ÿš€ Nosana SDK Playground initialized', LogType.SUCCESS)
1651
- addLog('๐Ÿ“š This is an interactive documentation and testing environment', LogType.INFO)
1652
- addLog('๐Ÿ”ง Use the controls on the left to test SDK functionality', LogType.INFO)
1653
- initializeClient()
1654
- })
1655
- </script>
1656
-
1657
- <style scoped>
1658
- .terminal {
1659
- background-color: #1a1a1a;
1660
- border-radius: 6px;
1661
- padding: 16px;
1662
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
1663
- }
1664
-
1665
- .terminal-line {
1666
- margin-bottom: 8px;
1667
- line-height: 1.4;
1668
- }
1669
-
1670
- .terminal-timestamp {
1671
- color: #6b7280;
1672
- font-size: 12px;
1673
- margin-right: 8px;
1674
- }
1675
-
1676
- .terminal-info {
1677
- color: #e5e7eb;
1678
- }
1679
-
1680
- .terminal-success {
1681
- color: #10b981;
1682
- font-weight: 500;
1683
- }
1684
-
1685
- .terminal-error {
1686
- color: #ef4444;
1687
- font-weight: 500;
1688
- }
1689
-
1690
- .terminal-warning {
1691
- color: #f59e0b;
1692
- font-weight: 500;
1693
- }
1694
-
1695
- /* Scrollbar styling */
1696
- .terminal::-webkit-scrollbar {
1697
- width: 8px;
1698
- }
1699
-
1700
- .terminal::-webkit-scrollbar-track {
1701
- background: #374151;
1702
- border-radius: 4px;
1703
- }
1704
-
1705
- .terminal::-webkit-scrollbar-thumb {
1706
- background: #6b7280;
1707
- border-radius: 4px;
1708
- }
1709
-
1710
- .terminal::-webkit-scrollbar-thumb:hover {
1711
- background: #9ca3af;
1712
- }
1713
-
1714
- /* Code blocks */
1715
- pre code {
1716
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
1717
- line-height: 1.5;
1718
- }
1719
-
1720
- /* Tab transitions */
1721
- .tab-content {
1722
- animation: fadeIn 0.3s ease-in-out;
1723
- }
1724
-
1725
- @keyframes fadeIn {
1726
- from {
1727
- opacity: 0;
1728
- transform: translateY(10px);
1729
- }
1730
-
1731
- to {
1732
- opacity: 1;
1733
- transform: translateY(0);
1734
- }
1735
- }
1736
-
1737
- /* Enhanced buttons */
1738
- button:focus {
1739
- outline: none;
1740
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
1741
- }
1742
-
1743
- /* Loading states */
1744
- .loading-pulse {
1745
- animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
1746
- }
1747
-
1748
- @keyframes pulse {
1749
-
1750
- 0%,
1751
- 100% {
1752
- opacity: 1;
1753
- }
1754
-
1755
- 50% {
1756
- opacity: 0.5;
1757
- }
1758
- }
1759
-
1760
- /* Status indicators */
1761
- .status-indicator {
1762
- animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
1763
- }
1764
-
1765
- /* Documentation section styling */
1766
- .prose {
1767
- max-width: none;
1768
- }
1769
-
1770
- .prose h3 {
1771
- margin-top: 2rem;
1772
- margin-bottom: 1rem;
1773
- }
1774
-
1775
- .prose p {
1776
- margin-bottom: 1rem;
1777
- }
1778
-
1779
- .prose ul {
1780
- margin-bottom: 1rem;
1781
- }
1782
-
1783
- .prose li {
1784
- margin-bottom: 0.5rem;
1785
- }
1786
-
1787
- .terminal {
1788
- background-color: #1a202c;
1789
- color: #68d391;
1790
- font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
1791
- font-size: 0.875rem;
1792
- padding: 1rem;
1793
- border-radius: 0.5rem;
1794
- overflow: auto;
1795
- scrollbar-width: thin;
1796
- scrollbar-color: #4a5568 #1a202c;
1797
- }
1798
-
1799
- .terminal::-webkit-scrollbar {
1800
- width: 8px;
1801
- }
1802
-
1803
- .terminal::-webkit-scrollbar-track {
1804
- background: #1a202c;
1805
- }
1806
-
1807
- .terminal::-webkit-scrollbar-thumb {
1808
- background: #4a5568;
1809
- border-radius: 4px;
1810
- }
1811
-
1812
- .terminal::-webkit-scrollbar-thumb:hover {
1813
- background: #718096;
1814
- }
1815
-
1816
- .terminal-line {
1817
- margin-bottom: 0.25rem;
1818
- }
1819
-
1820
- .terminal-timestamp {
1821
- color: #9ca3af;
1822
- margin-right: 0.5rem;
1823
- }
1824
-
1825
- .terminal-error {
1826
- color: #f87171;
1827
- }
1828
-
1829
- .terminal-success {
1830
- color: #68d391;
1831
- }
1832
-
1833
- .terminal-info {
1834
- color: #60a5fa;
1835
- }
1836
-
1837
- .terminal-warning {
1838
- color: #fbbf24;
1839
- }
1840
- </style>