solforge 0.2.12 → 0.2.14

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 (175) hide show
  1. package/package.json +1 -5
  2. package/start.cjs +19 -23
  3. package/docs/API.md +0 -379
  4. package/docs/CONFIGURATION.md +0 -407
  5. package/docs/bun-single-file-executable.md +0 -585
  6. package/docs/cli-plan.md +0 -154
  7. package/docs/data-indexing-plan.md +0 -214
  8. package/docs/gui-roadmap.md +0 -202
  9. package/scripts/decode-b58.ts +0 -10
  10. package/scripts/install.sh +0 -112
  11. package/server/index.ts +0 -5
  12. package/server/lib/base58.ts +0 -33
  13. package/server/lib/faucet.ts +0 -110
  14. package/server/lib/instruction-parser.ts +0 -328
  15. package/server/lib/parsers/spl-associated-token-account.ts +0 -50
  16. package/server/lib/parsers/spl-token.ts +0 -340
  17. package/server/lib/spl-token.ts +0 -57
  18. package/server/methods/TEMPLATE.md +0 -117
  19. package/server/methods/account/get-account-info.ts +0 -86
  20. package/server/methods/account/get-balance.ts +0 -23
  21. package/server/methods/account/get-multiple-accounts.ts +0 -84
  22. package/server/methods/account/get-parsed-account-info.ts +0 -17
  23. package/server/methods/account/index.ts +0 -12
  24. package/server/methods/account/parsers/index.ts +0 -52
  25. package/server/methods/account/parsers/loader-upgradeable.ts +0 -79
  26. package/server/methods/account/parsers/spl-token.ts +0 -256
  27. package/server/methods/account/parsers/system.ts +0 -4
  28. package/server/methods/account/request-airdrop.ts +0 -271
  29. package/server/methods/admin/adopt-mint-authority.ts +0 -94
  30. package/server/methods/admin/clone-program-accounts.ts +0 -55
  31. package/server/methods/admin/clone-program.ts +0 -152
  32. package/server/methods/admin/clone-token-accounts.ts +0 -117
  33. package/server/methods/admin/clone-token-mint.ts +0 -82
  34. package/server/methods/admin/create-mint.ts +0 -114
  35. package/server/methods/admin/create-token-account.ts +0 -137
  36. package/server/methods/admin/helpers.ts +0 -70
  37. package/server/methods/admin/index.ts +0 -10
  38. package/server/methods/admin/list-mints.ts +0 -21
  39. package/server/methods/admin/load-program.ts +0 -52
  40. package/server/methods/admin/mint-to.ts +0 -266
  41. package/server/methods/block/get-block-height.ts +0 -5
  42. package/server/methods/block/get-block.ts +0 -31
  43. package/server/methods/block/get-blocks-with-limit.ts +0 -19
  44. package/server/methods/block/get-latest-blockhash.ts +0 -12
  45. package/server/methods/block/get-slot.ts +0 -5
  46. package/server/methods/block/index.ts +0 -6
  47. package/server/methods/block/is-blockhash-valid.ts +0 -19
  48. package/server/methods/epoch/get-cluster-nodes.ts +0 -17
  49. package/server/methods/epoch/get-epoch-info.ts +0 -16
  50. package/server/methods/epoch/get-epoch-schedule.ts +0 -15
  51. package/server/methods/epoch/get-highest-snapshot-slot.ts +0 -9
  52. package/server/methods/epoch/get-leader-schedule.ts +0 -8
  53. package/server/methods/epoch/get-max-retransmit-slot.ts +0 -9
  54. package/server/methods/epoch/get-max-shred-insert-slot.ts +0 -9
  55. package/server/methods/epoch/get-slot-leader.ts +0 -6
  56. package/server/methods/epoch/get-slot-leaders.ts +0 -9
  57. package/server/methods/epoch/get-stake-activation.ts +0 -9
  58. package/server/methods/epoch/get-stake-minimum-delegation.ts +0 -9
  59. package/server/methods/epoch/get-vote-accounts.ts +0 -19
  60. package/server/methods/epoch/index.ts +0 -13
  61. package/server/methods/epoch/minimum-ledger-slot.ts +0 -5
  62. package/server/methods/fee/get-fee-calculator-for-blockhash.ts +0 -12
  63. package/server/methods/fee/get-fee-for-message.ts +0 -8
  64. package/server/methods/fee/get-fee-rate-governor.ts +0 -16
  65. package/server/methods/fee/get-fees.ts +0 -14
  66. package/server/methods/fee/get-recent-prioritization-fees.ts +0 -22
  67. package/server/methods/fee/index.ts +0 -5
  68. package/server/methods/get-address-lookup-table.ts +0 -27
  69. package/server/methods/index.ts +0 -265
  70. package/server/methods/performance/get-recent-performance-samples.ts +0 -25
  71. package/server/methods/performance/get-transaction-count.ts +0 -5
  72. package/server/methods/performance/index.ts +0 -2
  73. package/server/methods/program/get-block-commitment.ts +0 -9
  74. package/server/methods/program/get-block-production.ts +0 -14
  75. package/server/methods/program/get-block-time.ts +0 -21
  76. package/server/methods/program/get-blocks.ts +0 -11
  77. package/server/methods/program/get-first-available-block.ts +0 -9
  78. package/server/methods/program/get-genesis-hash.ts +0 -6
  79. package/server/methods/program/get-identity.ts +0 -6
  80. package/server/methods/program/get-inflation-governor.ts +0 -15
  81. package/server/methods/program/get-inflation-rate.ts +0 -10
  82. package/server/methods/program/get-inflation-reward.ts +0 -12
  83. package/server/methods/program/get-largest-accounts.ts +0 -8
  84. package/server/methods/program/get-parsed-program-accounts.ts +0 -12
  85. package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +0 -12
  86. package/server/methods/program/get-parsed-token-accounts-by-owner.ts +0 -12
  87. package/server/methods/program/get-program-accounts.ts +0 -221
  88. package/server/methods/program/get-supply.ts +0 -13
  89. package/server/methods/program/get-token-account-balance.ts +0 -60
  90. package/server/methods/program/get-token-accounts-by-delegate.ts +0 -82
  91. package/server/methods/program/get-token-accounts-by-owner.ts +0 -416
  92. package/server/methods/program/get-token-largest-accounts.ts +0 -81
  93. package/server/methods/program/get-token-supply.ts +0 -39
  94. package/server/methods/program/index.ts +0 -21
  95. package/server/methods/solforge/index.ts +0 -158
  96. package/server/methods/system/get-health.ts +0 -5
  97. package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +0 -13
  98. package/server/methods/system/get-version.ts +0 -9
  99. package/server/methods/system/index.ts +0 -3
  100. package/server/methods/transaction/get-confirmed-transaction.ts +0 -11
  101. package/server/methods/transaction/get-parsed-transaction.ts +0 -17
  102. package/server/methods/transaction/get-signature-statuses.ts +0 -79
  103. package/server/methods/transaction/get-signatures-for-address.ts +0 -41
  104. package/server/methods/transaction/get-transaction.ts +0 -639
  105. package/server/methods/transaction/index.ts +0 -7
  106. package/server/methods/transaction/inner-instructions.test.ts +0 -104
  107. package/server/methods/transaction/send-transaction.ts +0 -469
  108. package/server/methods/transaction/simulate-transaction.ts +0 -57
  109. package/server/rpc-server.ts +0 -521
  110. package/server/types.ts +0 -109
  111. package/server/ws-server.ts +0 -178
  112. package/src/api-server-entry.ts +0 -109
  113. package/src/cli/bootstrap.ts +0 -67
  114. package/src/cli/commands/airdrop.ts +0 -37
  115. package/src/cli/commands/config.ts +0 -39
  116. package/src/cli/commands/mint.ts +0 -187
  117. package/src/cli/commands/program-clone.ts +0 -122
  118. package/src/cli/commands/program-load.ts +0 -64
  119. package/src/cli/commands/rpc-start.ts +0 -49
  120. package/src/cli/commands/token-adopt-authority.ts +0 -37
  121. package/src/cli/commands/token-clone.ts +0 -112
  122. package/src/cli/commands/token-create.ts +0 -81
  123. package/src/cli/main.ts +0 -158
  124. package/src/cli/run-solforge.ts +0 -112
  125. package/src/cli/setup-utils.ts +0 -54
  126. package/src/cli/setup-wizard.ts +0 -258
  127. package/src/cli/utils/args.ts +0 -15
  128. package/src/commands/add-program.ts +0 -333
  129. package/src/commands/init.ts +0 -122
  130. package/src/commands/list.ts +0 -136
  131. package/src/commands/mint.ts +0 -287
  132. package/src/commands/start.ts +0 -881
  133. package/src/commands/status.ts +0 -99
  134. package/src/commands/stop.ts +0 -405
  135. package/src/config/index.ts +0 -146
  136. package/src/config/manager.ts +0 -157
  137. package/src/db/index.ts +0 -83
  138. package/src/db/schema/accounts.ts +0 -23
  139. package/src/db/schema/address-signatures.ts +0 -31
  140. package/src/db/schema/index.ts +0 -6
  141. package/src/db/schema/meta-kv.ts +0 -9
  142. package/src/db/schema/transactions.ts +0 -36
  143. package/src/db/schema/tx-account-states.ts +0 -23
  144. package/src/db/schema/tx-accounts.ts +0 -33
  145. package/src/db/tx-store.ts +0 -264
  146. package/src/gui/public/app.css +0 -1556
  147. package/src/gui/public/build/main.css +0 -1569
  148. package/src/gui/public/build/main.js +0 -303
  149. package/src/gui/public/build/main.js.txt +0 -231
  150. package/src/gui/public/index.html +0 -19
  151. package/src/gui/server.ts +0 -296
  152. package/src/gui/src/api.ts +0 -127
  153. package/src/gui/src/app.tsx +0 -441
  154. package/src/gui/src/components/airdrop-mint-form.tsx +0 -246
  155. package/src/gui/src/components/clone-program-modal.tsx +0 -202
  156. package/src/gui/src/components/clone-token-modal.tsx +0 -230
  157. package/src/gui/src/components/modal.tsx +0 -134
  158. package/src/gui/src/components/programs-panel.tsx +0 -124
  159. package/src/gui/src/components/status-panel.tsx +0 -136
  160. package/src/gui/src/components/tokens-panel.tsx +0 -122
  161. package/src/gui/src/hooks/use-interval.ts +0 -17
  162. package/src/gui/src/index.css +0 -557
  163. package/src/gui/src/main.tsx +0 -17
  164. package/src/index.ts +0 -216
  165. package/src/migrations-bundled.ts +0 -23
  166. package/src/rpc/start.ts +0 -44
  167. package/src/services/api-server.ts +0 -504
  168. package/src/services/port-manager.ts +0 -174
  169. package/src/services/process-registry.ts +0 -153
  170. package/src/services/program-cloner.ts +0 -317
  171. package/src/services/token-cloner.ts +0 -811
  172. package/src/services/validator.ts +0 -293
  173. package/src/types/config.ts +0 -110
  174. package/src/utils/shell.ts +0 -110
  175. package/src/utils/token-loader.ts +0 -115
@@ -1,99 +0,0 @@
1
- import chalk from "chalk";
2
- import { configManager } from "../config/manager.js";
3
- import { processRegistry } from "../services/process-registry.js";
4
- import { checkSolanaTools, runCommand } from "../utils/shell.js";
5
-
6
- export async function statusCommand(): Promise<void> {
7
- console.log(chalk.blue("📊 Checking system status...\n"));
8
-
9
- // Check Solana CLI tools
10
- console.log(chalk.cyan("🔧 Solana CLI Tools:"));
11
- const tools = await checkSolanaTools();
12
-
13
- console.log(
14
- ` ${tools.solana ? "✅" : "❌"} solana CLI: ${
15
- tools.solana ? "Available" : "Not found"
16
- }`,
17
- );
18
- console.log(
19
- ` ${tools.splToken ? "✅" : "❌"} spl-token CLI: ${
20
- tools.splToken ? "Available" : "Not found"
21
- }`,
22
- );
23
-
24
- if (!tools.solana || !tools.splToken) {
25
- console.log(chalk.yellow("\n💡 Install Solana CLI tools:"));
26
- console.log(
27
- chalk.gray(
28
- ' sh -c "$(curl -sSfL https://release.solana.com/v1.18.4/install)"',
29
- ),
30
- );
31
- console.log();
32
- }
33
-
34
- // Check running validators
35
- console.log(chalk.cyan("\n🏗️ Validator Status:"));
36
-
37
- // Clean up dead processes first
38
- await processRegistry.cleanup();
39
-
40
- const validators = processRegistry.getRunning();
41
-
42
- if (validators.length === 0) {
43
- console.log(` ❌ No validators running`);
44
- console.log(` 💡 Run 'solforge start' to launch a validator`);
45
- } else {
46
- console.log(
47
- ` ✅ ${validators.length} validator${
48
- validators.length > 1 ? "s" : ""
49
- } running`,
50
- );
51
-
52
- for (const validator of validators) {
53
- const isRunning = await processRegistry.isProcessRunning(validator.pid);
54
- if (isRunning) {
55
- console.log(` 🔹 ${validator.name} (${validator.id}):`);
56
- console.log(` 🌐 RPC: ${validator.rpcUrl}`);
57
- console.log(` 💰 Faucet: ${validator.faucetUrl}`);
58
- console.log(` 🆔 PID: ${validator.pid}`);
59
-
60
- // Get current slot for this validator
61
- try {
62
- const slotResult = await runCommand(
63
- "solana",
64
- ["slot", "--url", validator.rpcUrl, "--output", "json"],
65
- { silent: true, jsonOutput: true },
66
- );
67
-
68
- if (slotResult.success && typeof slotResult.stdout === "object") {
69
- console.log(` 📊 Current slot: ${slotResult.stdout}`);
70
- }
71
- } catch {
72
- // Ignore slot check errors
73
- }
74
- } else {
75
- processRegistry.updateStatus(validator.id, "stopped");
76
- console.log(
77
- ` ⚠️ ${validator.name} (${validator.id}): Process stopped`,
78
- );
79
- }
80
- }
81
-
82
- console.log(` 💡 Run 'solforge list' for detailed validator information`);
83
- }
84
-
85
- // Check config file
86
- console.log(chalk.cyan("\n📄 Configuration:"));
87
- try {
88
- const config = configManager.getConfig();
89
- console.log(` ✅ Config loaded: ${configManager.getConfigPath()}`);
90
- console.log(` 📝 Project: ${config.name}`);
91
- console.log(` 🪙 Tokens: ${config.tokens.length}`);
92
- console.log(` 📦 Programs: ${config.programs.length}`);
93
- } catch (_error) {
94
- console.log(` ❌ No valid configuration found`);
95
- console.log(` 💡 Run 'solforge init' to create one`);
96
- }
97
-
98
- console.log();
99
- }
@@ -1,405 +0,0 @@
1
- import { select } from "@inquirer/prompts";
2
- import chalk from "chalk";
3
- import type { RunningValidator } from "../services/process-registry.js";
4
- import { processRegistry } from "../services/process-registry.js";
5
-
6
- export async function stopCommand(
7
- validatorId?: string,
8
- options: { all?: boolean; kill?: boolean } = {},
9
- ): Promise<void> {
10
- console.log(chalk.blue("🛑 Stopping validator(s)...\n"));
11
-
12
- // Clean up dead processes first
13
- await processRegistry.cleanup();
14
-
15
- const validators = processRegistry.getRunning();
16
-
17
- if (validators.length === 0) {
18
- console.log(chalk.yellow("⚠️ No running validators found"));
19
- return;
20
- }
21
-
22
- let validatorsToStop: RunningValidator[] = [];
23
-
24
- if (options.all) {
25
- // Stop all validators
26
- validatorsToStop = validators;
27
- console.log(
28
- chalk.cyan(`🔄 Stopping all ${validators.length} validator(s)...`),
29
- );
30
- } else if (validatorId) {
31
- // Stop specific validator
32
- const validator = processRegistry.getById(validatorId);
33
- if (!validator) {
34
- console.error(
35
- chalk.red(`❌ Validator with ID '${validatorId}' not found`),
36
- );
37
- console.log(
38
- chalk.gray("💡 Use `solforge list` to see running validators"),
39
- );
40
- return;
41
- }
42
- validatorsToStop = [validator];
43
- console.log(
44
- chalk.cyan(
45
- `🔄 Stopping validator '${validator.name}' (${validatorId})...`,
46
- ),
47
- );
48
- } else {
49
- // No specific validator specified, show available options
50
- console.log(chalk.yellow("⚠️ Please specify which validator to stop:"));
51
- console.log(
52
- chalk.gray("💡 Use `solforge stop <id>` to stop a specific validator"),
53
- );
54
- console.log(
55
- chalk.gray("💡 Use `solforge stop --all` to stop all validators"),
56
- );
57
- console.log(chalk.gray("💡 Use `solforge list` to see running validators"));
58
- return;
59
- }
60
-
61
- // Stop each validator
62
- let stoppedCount = 0;
63
- let errorCount = 0;
64
-
65
- for (const validator of validatorsToStop) {
66
- try {
67
- const result = await stopValidator(validator, options.kill);
68
- if (result.success) {
69
- console.log(
70
- chalk.green(`✅ Stopped ${validator.name} (${validator.id})`),
71
- );
72
- stoppedCount++;
73
- } else {
74
- console.error(
75
- chalk.red(
76
- `❌ Failed to stop ${validator.name} (${validator.id}): ${result.error}`,
77
- ),
78
- );
79
- errorCount++;
80
- }
81
- } catch (error) {
82
- console.error(
83
- chalk.red(
84
- `❌ Error stopping ${validator.name} (${validator.id}): ${
85
- error instanceof Error ? error.message : String(error)
86
- }`,
87
- ),
88
- );
89
- errorCount++;
90
- }
91
- }
92
-
93
- // Summary
94
- console.log();
95
- if (stoppedCount > 0) {
96
- console.log(
97
- chalk.green(`✅ Successfully stopped ${stoppedCount} validator(s)`),
98
- );
99
- }
100
- if (errorCount > 0) {
101
- console.log(chalk.red(`❌ Failed to stop ${errorCount} validator(s)`));
102
- }
103
- }
104
-
105
- async function stopValidator(
106
- validator: RunningValidator,
107
- forceKill: boolean = false,
108
- ): Promise<{ success: boolean; error?: string }> {
109
- try {
110
- // Stop the API server first if it has a PID
111
- if (validator.apiServerPid) {
112
- try {
113
- process.kill(validator.apiServerPid, "SIGTERM");
114
- console.log(
115
- chalk.gray(`📡 Stopped API server (PID: ${validator.apiServerPid})`),
116
- );
117
- } catch (error) {
118
- console.log(
119
- chalk.yellow(
120
- `⚠️ Warning: Failed to stop API server: ${
121
- error instanceof Error ? error.message : String(error)
122
- }`,
123
- ),
124
- );
125
- }
126
- }
127
-
128
- // Check if process is still running
129
- const isRunning = await processRegistry.isProcessRunning(validator.pid);
130
- if (!isRunning) {
131
- // Process already stopped, just clean up registry
132
- processRegistry.unregister(validator.id);
133
- return { success: true };
134
- }
135
-
136
- const signal = forceKill ? "SIGKILL" : "SIGTERM";
137
-
138
- // Send termination signal
139
- process.kill(validator.pid, signal);
140
-
141
- if (forceKill) {
142
- // For SIGKILL, process should stop immediately
143
- processRegistry.unregister(validator.id);
144
- return { success: true };
145
- } else {
146
- // For SIGTERM, wait for graceful shutdown
147
- const shutdownResult = await waitForProcessShutdown(validator.pid, 10000);
148
-
149
- if (shutdownResult.success) {
150
- processRegistry.unregister(validator.id);
151
- return { success: true };
152
- } else {
153
- // Graceful shutdown failed, try force kill
154
- console.log(
155
- chalk.yellow(
156
- `⚠️ Graceful shutdown failed for ${validator.name}, force killing...`,
157
- ),
158
- );
159
- process.kill(validator.pid, "SIGKILL");
160
- processRegistry.unregister(validator.id);
161
- return { success: true };
162
- }
163
- }
164
- } catch (error) {
165
- const errorMessage = error instanceof Error ? error.message : String(error);
166
-
167
- // If error is "ESRCH" (No such process), the process is already gone
168
- if (errorMessage.includes("ESRCH")) {
169
- processRegistry.unregister(validator.id);
170
- return { success: true };
171
- }
172
-
173
- return { success: false, error: errorMessage };
174
- }
175
- }
176
-
177
- async function waitForProcessShutdown(
178
- pid: number,
179
- timeoutMs: number = 10000,
180
- ): Promise<{ success: boolean; error?: string }> {
181
- const startTime = Date.now();
182
-
183
- while (Date.now() - startTime < timeoutMs) {
184
- try {
185
- // Send signal 0 to check if process exists
186
- process.kill(pid, 0);
187
- // If no error thrown, process is still running
188
- await new Promise((resolve) => setTimeout(resolve, 500));
189
- } catch (_error) {
190
- // Process is gone
191
- return { success: true };
192
- }
193
- }
194
-
195
- return { success: false, error: "Process shutdown timeout" };
196
- }
197
-
198
- export async function killCommand(
199
- validatorId?: string,
200
- options: { all?: boolean } = {},
201
- ): Promise<void> {
202
- console.log(chalk.red("💀 Force killing validator(s)...\n"));
203
-
204
- // Clean up dead processes first
205
- await processRegistry.cleanup();
206
-
207
- const validators = processRegistry.getRunning();
208
-
209
- if (validators.length === 0) {
210
- console.log(chalk.yellow("⚠️ No running validators found"));
211
- return;
212
- }
213
-
214
- let validatorsToKill: RunningValidator[] = [];
215
-
216
- if (options.all) {
217
- // Kill all validators
218
- validatorsToKill = validators;
219
- console.log(
220
- chalk.cyan(`🔄 Force killing all ${validators.length} validator(s)...`),
221
- );
222
- } else if (validatorId) {
223
- // Kill specific validator
224
- const validator = processRegistry.getById(validatorId);
225
- if (!validator) {
226
- console.error(
227
- chalk.red(`❌ Validator with ID '${validatorId}' not found`),
228
- );
229
- console.log(
230
- chalk.gray("💡 Use `solforge list` to see running validators"),
231
- );
232
- return;
233
- }
234
- validatorsToKill = [validator];
235
- console.log(
236
- chalk.cyan(
237
- `🔄 Force killing validator '${validator.name}' (${validatorId})...`,
238
- ),
239
- );
240
- } else {
241
- // No specific validator specified, show interactive selection
242
- console.log(chalk.cyan("📋 Select validator(s) to force kill:\n"));
243
-
244
- // Display current validators
245
- displayValidatorsTable(validators);
246
-
247
- const choices = [
248
- ...validators.map((v) => ({
249
- name: `${v.name} (${v.id}) - PID: ${v.pid}`,
250
- value: v.id,
251
- })),
252
- {
253
- name: chalk.red("Kill ALL validators"),
254
- value: "all",
255
- },
256
- {
257
- name: chalk.gray("Cancel"),
258
- value: "cancel",
259
- },
260
- ];
261
-
262
- const selectedValidator = await select({
263
- message: "Which validator would you like to force kill?",
264
- choices,
265
- });
266
-
267
- if (selectedValidator === "cancel") {
268
- console.log(chalk.gray("Operation cancelled"));
269
- return;
270
- }
271
-
272
- if (selectedValidator === "all") {
273
- validatorsToKill = validators;
274
- console.log(
275
- chalk.cyan(`🔄 Force killing all ${validators.length} validator(s)...`),
276
- );
277
- } else {
278
- const validator = processRegistry.getById(selectedValidator);
279
- if (!validator) {
280
- console.error(chalk.red("❌ Selected validator not found"));
281
- return;
282
- }
283
- validatorsToKill = [validator];
284
- console.log(
285
- chalk.cyan(
286
- `🔄 Force killing validator '${validator.name}' (${selectedValidator})...`,
287
- ),
288
- );
289
- }
290
- }
291
-
292
- // Kill each validator
293
- let killedCount = 0;
294
- let errorCount = 0;
295
-
296
- for (const validator of validatorsToKill) {
297
- try {
298
- const result = await stopValidator(validator, true); // Force kill
299
- if (result.success) {
300
- console.log(
301
- chalk.green(`✅ Killed ${validator.name} (${validator.id})`),
302
- );
303
- killedCount++;
304
- } else {
305
- console.error(
306
- chalk.red(
307
- `❌ Failed to kill ${validator.name} (${validator.id}): ${result.error}`,
308
- ),
309
- );
310
- errorCount++;
311
- }
312
- } catch (error) {
313
- console.error(
314
- chalk.red(
315
- `❌ Error killing ${validator.name} (${validator.id}): ${
316
- error instanceof Error ? error.message : String(error)
317
- }`,
318
- ),
319
- );
320
- errorCount++;
321
- }
322
- }
323
-
324
- // Summary
325
- console.log();
326
- if (killedCount > 0) {
327
- console.log(
328
- chalk.green(`✅ Successfully killed ${killedCount} validator(s)`),
329
- );
330
- }
331
- if (errorCount > 0) {
332
- console.log(chalk.red(`❌ Failed to kill ${errorCount} validator(s)`));
333
- }
334
- }
335
-
336
- function displayValidatorsTable(validators: RunningValidator[]): void {
337
- // Calculate column widths
338
- const maxIdWidth = Math.max(2, ...validators.map((v) => v.id.length));
339
- const maxNameWidth = Math.max(4, ...validators.map((v) => v.name.length));
340
- const maxPidWidth = Math.max(
341
- 3,
342
- ...validators.map((v) => v.pid.toString().length),
343
- );
344
- const maxPortWidth = 9; // "8899/9900" format
345
- const maxUptimeWidth = 7;
346
-
347
- // Header
348
- const header =
349
- chalk.bold("ID".padEnd(maxIdWidth)) +
350
- " | " +
351
- chalk.bold("Name".padEnd(maxNameWidth)) +
352
- " | " +
353
- chalk.bold("PID".padEnd(maxPidWidth)) +
354
- " | " +
355
- chalk.bold("RPC/Faucet".padEnd(maxPortWidth)) +
356
- " | " +
357
- chalk.bold("Uptime".padEnd(maxUptimeWidth)) +
358
- " | " +
359
- chalk.bold("Status");
360
-
361
- console.log(header);
362
- console.log("-".repeat(header.length - 20)); // Subtract ANSI codes length
363
-
364
- // Rows
365
- validators.forEach((validator) => {
366
- const uptime = formatUptime(validator.startTime);
367
- const ports = `${validator.rpcPort}/${validator.faucetPort}`;
368
- const status =
369
- validator.status === "running" ? chalk.green("●") : chalk.red("●");
370
-
371
- const row =
372
- validator.id.padEnd(maxIdWidth) +
373
- " | " +
374
- validator.name.padEnd(maxNameWidth) +
375
- " | " +
376
- validator.pid.toString().padEnd(maxPidWidth) +
377
- " | " +
378
- ports.padEnd(maxPortWidth) +
379
- " | " +
380
- uptime.padEnd(maxUptimeWidth) +
381
- " | " +
382
- status;
383
-
384
- console.log(row);
385
- });
386
-
387
- console.log(); // Empty line
388
- }
389
-
390
- function formatUptime(startTime: Date): string {
391
- const now = new Date();
392
- const uptimeMs = now.getTime() - startTime.getTime();
393
- const uptimeSeconds = Math.floor(uptimeMs / 1000);
394
-
395
- if (uptimeSeconds < 60) {
396
- return `${uptimeSeconds}s`;
397
- } else if (uptimeSeconds < 3600) {
398
- const minutes = Math.floor(uptimeSeconds / 60);
399
- return `${minutes}m`;
400
- } else {
401
- const hours = Math.floor(uptimeSeconds / 3600);
402
- const minutes = Math.floor((uptimeSeconds % 3600) / 60);
403
- return `${hours}h${minutes}m`;
404
- }
405
- }
@@ -1,146 +0,0 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
2
- import { dirname } from "node:path";
3
-
4
- export interface SolforgeConfig {
5
- server: {
6
- rpcPort: number;
7
- wsPort: number;
8
- db: { mode: "ephemeral" | "persistent"; path: string };
9
- };
10
- svm: { initialLamports: string; faucetSOL: number };
11
- clone: {
12
- endpoint: string;
13
- programs: string[];
14
- tokens: string[];
15
- programAccounts: Array<{
16
- programId: string;
17
- limit?: number;
18
- filters?: unknown[];
19
- }>;
20
- };
21
- gui: { enabled: boolean; port: number };
22
- bootstrap: {
23
- airdrops: Array<{ address: string; amountSol: number }>;
24
- };
25
- }
26
-
27
- export const defaultConfig: SolforgeConfig = {
28
- server: {
29
- rpcPort: 8899,
30
- wsPort: 8900,
31
- db: { mode: "ephemeral", path: ".solforge/db.db" },
32
- },
33
- svm: { initialLamports: "1000000000000000", faucetSOL: 1000 },
34
- clone: {
35
- endpoint: "https://api.mainnet-beta.solana.com",
36
- programs: [],
37
- tokens: [],
38
- programAccounts: [],
39
- },
40
- gui: { enabled: true, port: 42069 },
41
- bootstrap: { airdrops: [] },
42
- };
43
-
44
- export async function readConfig(path?: string): Promise<SolforgeConfig> {
45
- const p = path || "sf.config.json";
46
- try {
47
- const t = await Bun.file(p).text();
48
- const json = JSON.parse(t);
49
- return deepMerge(defaultConfig, json);
50
- } catch {
51
- return defaultConfig;
52
- }
53
- }
54
-
55
- export async function writeDefaultConfig(opts: { force?: boolean } = {}) {
56
- const p = "sf.config.json";
57
- try {
58
- if (!opts.force)
59
- await Bun.file(p)
60
- .text()
61
- .then(() => {
62
- throw new Error("exists");
63
- });
64
- } catch {}
65
- const dir = dirname(p);
66
- if (dir && dir !== ".") {
67
- try {
68
- mkdirSync(dir, { recursive: true });
69
- } catch {}
70
- }
71
- writeFileSync(p, `${JSON.stringify(defaultConfig, null, 2)}\n`);
72
- }
73
-
74
- export async function writeConfig(
75
- config: SolforgeConfig,
76
- path = "sf.config.json",
77
- ) {
78
- const dir = dirname(path);
79
- if (dir && dir !== ".") {
80
- try {
81
- mkdirSync(dir, { recursive: true });
82
- } catch {}
83
- }
84
- await Bun.write(path, `${JSON.stringify(config, null, 2)}\n`);
85
- }
86
-
87
- export function getConfigValue(
88
- cfg: Record<string, unknown>,
89
- path?: string,
90
- ): unknown {
91
- if (!path) return cfg;
92
- let cur: unknown = cfg;
93
- for (const k of path.split(".")) {
94
- if (
95
- cur &&
96
- typeof cur === "object" &&
97
- k in (cur as Record<string, unknown>)
98
- ) {
99
- cur = (cur as Record<string, unknown>)[k];
100
- } else {
101
- return undefined;
102
- }
103
- }
104
- return cur;
105
- }
106
-
107
- export function setConfigValue<T extends Record<string, unknown>>(
108
- cfg: T,
109
- path: string,
110
- value: unknown,
111
- ): T {
112
- const parts = path.split(".");
113
- let node: Record<string, unknown> = cfg;
114
- for (let i = 0; i < parts.length - 1; i++) {
115
- const k = parts[i];
116
- if (!node[k] || typeof node[k] !== "object") node[k] = {};
117
- node = node[k] as Record<string, unknown>;
118
- }
119
- node[parts[parts.length - 1]] = coerceValue(value);
120
- return cfg;
121
- }
122
-
123
- function coerceValue(v: unknown): unknown {
124
- if (v === "true") return true;
125
- if (v === "false") return false;
126
- if (typeof v === "string" && v !== "" && !Number.isNaN(Number(v)))
127
- return Number(v);
128
- try {
129
- return typeof v === "string" ? JSON.parse(v) : v;
130
- } catch {
131
- return v;
132
- }
133
- }
134
-
135
- function deepMerge<T>(a: T, b: Partial<T>): T {
136
- if (Array.isArray(a) || Array.isArray(b)) return (b ?? a) as unknown as T;
137
- if (typeof a === "object" && typeof b === "object" && a && b) {
138
- const out: Record<string, unknown> = { ...(a as Record<string, unknown>) };
139
- for (const [k, v] of Object.entries(b)) {
140
- const ak = (a as Record<string, unknown>)[k];
141
- out[k] = deepMerge(ak as unknown, v as unknown) as unknown;
142
- }
143
- return out as unknown as T;
144
- }
145
- return (b ?? a) as unknown as T;
146
- }