solforge 0.2.4 → 0.2.6

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 (82) hide show
  1. package/README.md +471 -79
  2. package/cli.cjs +106 -78
  3. package/package.json +1 -1
  4. package/scripts/install.sh +1 -1
  5. package/scripts/postinstall.cjs +69 -61
  6. package/server/lib/base58.ts +1 -1
  7. package/server/methods/account/get-account-info.ts +3 -7
  8. package/server/methods/account/get-balance.ts +3 -7
  9. package/server/methods/account/get-multiple-accounts.ts +2 -1
  10. package/server/methods/account/get-parsed-account-info.ts +3 -7
  11. package/server/methods/account/parsers/index.ts +2 -2
  12. package/server/methods/account/parsers/loader-upgradeable.ts +14 -1
  13. package/server/methods/account/parsers/spl-token.ts +29 -10
  14. package/server/methods/account/request-airdrop.ts +44 -31
  15. package/server/methods/block/get-block.ts +3 -7
  16. package/server/methods/block/get-blocks-with-limit.ts +3 -7
  17. package/server/methods/block/is-blockhash-valid.ts +3 -7
  18. package/server/methods/get-address-lookup-table.ts +3 -7
  19. package/server/methods/program/get-program-accounts.ts +9 -9
  20. package/server/methods/program/get-token-account-balance.ts +3 -7
  21. package/server/methods/program/get-token-accounts-by-delegate.ts +4 -3
  22. package/server/methods/program/get-token-accounts-by-owner.ts +61 -35
  23. package/server/methods/program/get-token-largest-accounts.ts +3 -2
  24. package/server/methods/program/get-token-supply.ts +3 -2
  25. package/server/methods/solforge/index.ts +9 -6
  26. package/server/methods/transaction/get-parsed-transaction.ts +3 -7
  27. package/server/methods/transaction/get-signature-statuses.ts +14 -7
  28. package/server/methods/transaction/get-signatures-for-address.ts +3 -7
  29. package/server/methods/transaction/get-transaction.ts +167 -81
  30. package/server/methods/transaction/send-transaction.ts +29 -16
  31. package/server/methods/transaction/simulate-transaction.ts +3 -2
  32. package/server/rpc-server.ts +47 -34
  33. package/server/types.ts +9 -6
  34. package/server/ws-server.ts +15 -8
  35. package/src/api-server-entry.ts +91 -91
  36. package/src/cli/commands/airdrop.ts +2 -2
  37. package/src/cli/commands/config.ts +2 -2
  38. package/src/cli/commands/mint.ts +3 -3
  39. package/src/cli/commands/program-clone.ts +9 -11
  40. package/src/cli/commands/program-load.ts +3 -3
  41. package/src/cli/commands/rpc-start.ts +8 -5
  42. package/src/cli/commands/token-adopt-authority.ts +1 -1
  43. package/src/cli/commands/token-clone.ts +5 -6
  44. package/src/cli/commands/token-create.ts +5 -5
  45. package/src/cli/main.ts +38 -37
  46. package/src/cli/run-solforge.ts +20 -6
  47. package/src/cli/setup-wizard.ts +8 -6
  48. package/src/commands/add-program.ts +324 -328
  49. package/src/commands/init.ts +106 -106
  50. package/src/commands/list.ts +125 -125
  51. package/src/commands/mint.ts +247 -248
  52. package/src/commands/start.ts +837 -833
  53. package/src/commands/status.ts +80 -80
  54. package/src/commands/stop.ts +381 -382
  55. package/src/config/index.ts +33 -17
  56. package/src/config/manager.ts +150 -150
  57. package/src/db/index.ts +2 -2
  58. package/src/db/tx-store.ts +12 -8
  59. package/src/gui/public/app.css +1556 -1
  60. package/src/gui/public/build/main.css +1569 -1
  61. package/src/gui/server.ts +21 -22
  62. package/src/gui/src/api.ts +1 -1
  63. package/src/gui/src/app.tsx +96 -45
  64. package/src/gui/src/components/airdrop-mint-form.tsx +49 -19
  65. package/src/gui/src/components/clone-program-modal.tsx +31 -12
  66. package/src/gui/src/components/clone-token-modal.tsx +32 -13
  67. package/src/gui/src/components/modal.tsx +18 -11
  68. package/src/gui/src/components/programs-panel.tsx +27 -15
  69. package/src/gui/src/components/status-panel.tsx +32 -18
  70. package/src/gui/src/components/tokens-panel.tsx +25 -19
  71. package/src/gui/src/index.css +491 -463
  72. package/src/index.ts +177 -149
  73. package/src/rpc/start.ts +1 -1
  74. package/src/services/api-server.ts +494 -475
  75. package/src/services/port-manager.ts +164 -167
  76. package/src/services/process-registry.ts +144 -145
  77. package/src/services/program-cloner.ts +312 -312
  78. package/src/services/token-cloner.ts +799 -797
  79. package/src/services/validator.ts +288 -290
  80. package/src/types/config.ts +72 -72
  81. package/src/utils/shell.ts +75 -75
  82. package/src/utils/token-loader.ts +78 -78
package/src/index.ts CHANGED
@@ -2,187 +2,215 @@
2
2
 
3
3
  // Suppress bigint-buffer warning
4
4
  const originalStderrWrite = process.stderr.write.bind(process.stderr);
5
- process.stderr.write = function(chunk: any, encoding?: any, callback?: any) {
6
- if (typeof chunk === 'string' && chunk.includes('bigint: Failed to load bindings')) {
7
- return true; // Suppress this specific warning
8
- }
9
- return originalStderrWrite(chunk, encoding, callback);
10
- };
5
+ process.stderr.write = ((
6
+ chunk: unknown,
7
+ encoding?: unknown,
8
+ callback?: unknown,
9
+ ) => {
10
+ if (
11
+ typeof chunk === "string" &&
12
+ chunk.includes("bigint: Failed to load bindings")
13
+ ) {
14
+ return true; // Suppress this specific warning
15
+ }
16
+ const writer = originalStderrWrite as unknown as (
17
+ chunk: string | Uint8Array,
18
+ encoding?: BufferEncoding,
19
+ callback?: ((err?: Error) => void) | undefined,
20
+ ) => boolean;
21
+ return writer(
22
+ chunk as string | Uint8Array,
23
+ encoding as BufferEncoding | undefined,
24
+ callback as ((err?: Error) => void) | undefined,
25
+ );
26
+ }) as typeof process.stderr.write;
11
27
 
12
- import { Command } from "commander";
13
28
  import chalk from "chalk";
14
- import { existsSync } from "fs";
15
- import { resolve } from "path";
29
+ import { Command } from "commander";
30
+ import { existsSync } from "node:fs";
31
+ import { resolve } from "node:path";
32
+ import packageJson from "../package.json" with { type: "json" };
33
+ import { addProgramCommand } from "./commands/add-program.js";
16
34
  import { initCommand } from "./commands/init.js";
17
- import { statusCommand } from "./commands/status.js";
18
- import { startCommand } from "./commands/start.js";
19
- import { mintCommand } from "./commands/mint.js";
20
35
  import { listCommand } from "./commands/list.js";
21
- import { stopCommand, killCommand } from "./commands/stop.js";
22
- import { addProgramCommand } from "./commands/add-program.js";
23
- import packageJson from "../package.json" with { type: "json" };
36
+ import { mintCommand } from "./commands/mint.js";
37
+ import { startCommand } from "./commands/start.js";
38
+ import { statusCommand } from "./commands/status.js";
39
+ import { killCommand, stopCommand } from "./commands/stop.js";
24
40
 
25
41
  const program = new Command();
26
42
 
27
43
  program
28
- .name("solforge")
29
- .description("Solana localnet orchestration tool")
30
- .version(packageJson.version);
44
+ .name("solforge")
45
+ .description("Solana localnet orchestration tool")
46
+ .version(packageJson.version);
31
47
 
32
48
  // Check for sf.config.json in current directory
33
49
  function findConfig(): string | null {
34
- const configPath = resolve(process.cwd(), "sf.config.json");
35
- return existsSync(configPath) ? configPath : null;
50
+ const configPath = resolve(process.cwd(), "sf.config.json");
51
+ return existsSync(configPath) ? configPath : null;
36
52
  }
37
53
 
38
54
  program
39
- .command("init")
40
- .description("Initialize a new sf.config.json in current directory")
41
- .action(async () => {
42
- console.log(chalk.blue("🚀 Initializing SolForge configuration..."));
43
- await initCommand();
44
- });
55
+ .command("init")
56
+ .description("Initialize a new sf.config.json in current directory")
57
+ .action(async () => {
58
+ console.log(chalk.blue("🚀 Initializing SolForge configuration..."));
59
+ await initCommand();
60
+ });
45
61
 
46
62
  program
47
- .command("start")
48
- .description("Start localnet with current sf.config.json")
49
- .option("--debug", "Enable debug logging to see commands and detailed output")
50
- .option("--network", "Make API server accessible over network (binds to 0.0.0.0 instead of 127.0.0.1)")
51
- .action(async (options) => {
52
- const configPath = findConfig();
53
- if (!configPath) {
54
- console.error(
55
- chalk.red("❌ No sf.config.json found in current directory")
56
- );
57
- console.log(chalk.yellow("💡 Run `solforge init` to create one"));
58
- process.exit(1);
59
- }
60
-
61
- await startCommand(options.debug || false, options.network || false);
62
- });
63
+ .command("start")
64
+ .description("Start localnet with current sf.config.json")
65
+ .option("--debug", "Enable debug logging to see commands and detailed output")
66
+ .option(
67
+ "--network",
68
+ "Make API server accessible over network (binds to 0.0.0.0 instead of 127.0.0.1)",
69
+ )
70
+ .action(async (options) => {
71
+ const configPath = findConfig();
72
+ if (!configPath) {
73
+ console.error(
74
+ chalk.red("❌ No sf.config.json found in current directory"),
75
+ );
76
+ console.log(chalk.yellow("💡 Run `solforge init` to create one"));
77
+ process.exit(1);
78
+ }
79
+
80
+ await startCommand(options.debug || false, options.network || false);
81
+ });
63
82
 
64
83
  program
65
- .command("list")
66
- .description("List all running validators")
67
- .action(async () => {
68
- await listCommand();
69
- });
84
+ .command("list")
85
+ .description("List all running validators")
86
+ .action(async () => {
87
+ await listCommand();
88
+ });
70
89
 
71
90
  program
72
- .command("stop")
73
- .description("Stop running validator(s)")
74
- .argument("[validator-id]", "ID of validator to stop")
75
- .option("--all", "Stop all running validators")
76
- .option("--kill", "Force kill the validator (SIGKILL instead of SIGTERM)")
77
- .action(async (validatorId, options) => {
78
- await stopCommand(validatorId, options);
79
- });
91
+ .command("stop")
92
+ .description("Stop running validator(s)")
93
+ .argument("[validator-id]", "ID of validator to stop")
94
+ .option("--all", "Stop all running validators")
95
+ .option("--kill", "Force kill the validator (SIGKILL instead of SIGTERM)")
96
+ .action(async (validatorId, options) => {
97
+ await stopCommand(validatorId, options);
98
+ });
80
99
 
81
100
  program
82
- .command("kill")
83
- .description("Force kill running validator(s)")
84
- .argument("[validator-id]", "ID of validator to kill")
85
- .option("--all", "Kill all running validators")
86
- .action(async (validatorId, options) => {
87
- await killCommand(validatorId, options);
88
- });
101
+ .command("kill")
102
+ .description("Force kill running validator(s)")
103
+ .argument("[validator-id]", "ID of validator to kill")
104
+ .option("--all", "Kill all running validators")
105
+ .action(async (validatorId, options) => {
106
+ await killCommand(validatorId, options);
107
+ });
89
108
 
90
109
  program
91
- .command("api-server")
92
- .description("Start API server standalone")
93
- .option("-p, --port <port>", "Port for API server", "3000")
94
- .option("--host <host>", "Host to bind to (default: 127.0.0.1, use 0.0.0.0 for network access)")
95
- .option("--rpc-url <url>", "Validator RPC URL", "http://127.0.0.1:8899")
96
- .option("--faucet-url <url>", "Validator faucet URL", "http://127.0.0.1:9900")
97
- .option("--work-dir <dir>", "Work directory", "./.solforge")
98
- .action(async (options) => {
99
- const configPath = findConfig();
100
- if (!configPath) {
101
- console.error(
102
- chalk.red("❌ No sf.config.json found in current directory")
103
- );
104
- console.log(chalk.yellow("💡 Run `solforge init` to create one"));
105
- process.exit(1);
106
- }
107
-
108
- // Import API server components
109
- const { APIServer } = await import("./services/api-server.js");
110
- const { configManager } = await import("./config/manager.js");
111
-
112
- try {
113
- await configManager.load(configPath);
114
- const config = configManager.getConfig();
115
-
116
- const apiServer = new APIServer({
117
- port: parseInt(options.port),
118
- host: options.host,
119
- validatorRpcUrl: options.rpcUrl,
120
- validatorFaucetUrl: options.faucetUrl,
121
- config,
122
- workDir: options.workDir,
123
- });
124
-
125
- const result = await apiServer.start();
126
- if (result.success) {
127
- console.log(chalk.green("✅ API Server started successfully!"));
128
-
129
- // Keep the process alive
130
- process.on("SIGTERM", async () => {
131
- console.log(chalk.yellow("📡 API Server received SIGTERM, shutting down..."));
132
- await apiServer.stop();
133
- process.exit(0);
134
- });
135
-
136
- process.on("SIGINT", async () => {
137
- console.log(chalk.yellow("📡 API Server received SIGINT, shutting down..."));
138
- await apiServer.stop();
139
- process.exit(0);
140
- });
141
-
142
- // Keep process alive
143
- setInterval(() => {}, 1000);
144
- } else {
145
- console.error(chalk.red(`❌ Failed to start API server: ${result.error}`));
146
- process.exit(1);
147
- }
148
- } catch (error) {
149
- console.error(
150
- chalk.red(
151
- `❌ API Server error: ${
152
- error instanceof Error ? error.message : String(error)
153
- }`
154
- )
155
- );
156
- process.exit(1);
157
- }
158
- });
110
+ .command("api-server")
111
+ .description("Start API server standalone")
112
+ .option("-p, --port <port>", "Port for API server", "3000")
113
+ .option(
114
+ "--host <host>",
115
+ "Host to bind to (default: 127.0.0.1, use 0.0.0.0 for network access)",
116
+ )
117
+ .option("--rpc-url <url>", "Validator RPC URL", "http://127.0.0.1:8899")
118
+ .option("--faucet-url <url>", "Validator faucet URL", "http://127.0.0.1:9900")
119
+ .option("--work-dir <dir>", "Work directory", "./.solforge")
120
+ .action(async (options) => {
121
+ const configPath = findConfig();
122
+ if (!configPath) {
123
+ console.error(
124
+ chalk.red("❌ No sf.config.json found in current directory"),
125
+ );
126
+ console.log(chalk.yellow("💡 Run `solforge init` to create one"));
127
+ process.exit(1);
128
+ }
129
+
130
+ // Import API server components
131
+ const { APIServer } = await import("./services/api-server.js");
132
+ const { configManager } = await import("./config/manager.js");
133
+
134
+ try {
135
+ await configManager.load(configPath);
136
+ const config = configManager.getConfig();
137
+
138
+ const apiServer = new APIServer({
139
+ port: parseInt(options.port, 10),
140
+ host: options.host,
141
+ validatorRpcUrl: options.rpcUrl,
142
+ validatorFaucetUrl: options.faucetUrl,
143
+ config,
144
+ workDir: options.workDir,
145
+ });
146
+
147
+ const result = await apiServer.start();
148
+ if (result.success) {
149
+ console.log(chalk.green(" API Server started successfully!"));
150
+
151
+ // Keep the process alive
152
+ process.on("SIGTERM", async () => {
153
+ console.log(
154
+ chalk.yellow("📡 API Server received SIGTERM, shutting down..."),
155
+ );
156
+ await apiServer.stop();
157
+ process.exit(0);
158
+ });
159
+
160
+ process.on("SIGINT", async () => {
161
+ console.log(
162
+ chalk.yellow("📡 API Server received SIGINT, shutting down..."),
163
+ );
164
+ await apiServer.stop();
165
+ process.exit(0);
166
+ });
167
+
168
+ // Keep process alive
169
+ setInterval(() => {}, 1000);
170
+ } else {
171
+ console.error(
172
+ chalk.red(`❌ Failed to start API server: ${result.error}`),
173
+ );
174
+ process.exit(1);
175
+ }
176
+ } catch (error) {
177
+ console.error(
178
+ chalk.red(
179
+ `❌ API Server error: ${
180
+ error instanceof Error ? error.message : String(error)
181
+ }`,
182
+ ),
183
+ );
184
+ process.exit(1);
185
+ }
186
+ });
159
187
 
160
188
  program
161
- .command("add-program")
162
- .description("Add a program to sf.config.json")
189
+ .command("add-program")
190
+ .description("Add a program to sf.config.json")
163
191
 
164
- .option("--program-id <address>", "Mainnet program ID to clone and deploy")
165
- .option("--name <name>", "Friendly name for the program")
166
- .option("--no-interactive", "Run in non-interactive mode")
167
- .action(async (options) => {
168
- await addProgramCommand(options);
169
- });
192
+ .option("--program-id <address>", "Mainnet program ID to clone and deploy")
193
+ .option("--name <name>", "Friendly name for the program")
194
+ .option("--no-interactive", "Run in non-interactive mode")
195
+ .action(async (options) => {
196
+ await addProgramCommand(options);
197
+ });
170
198
 
171
199
  program
172
- .command("status")
173
- .description("Show localnet status")
174
- .action(async () => {
175
- await statusCommand();
176
- });
200
+ .command("status")
201
+ .description("Show localnet status")
202
+ .action(async () => {
203
+ await statusCommand();
204
+ });
177
205
 
178
206
  program.addCommand(mintCommand);
179
207
 
180
208
  program
181
- .command("reset")
182
- .description("Reset localnet ledger")
183
- .action(async () => {
184
- console.log(chalk.blue("🔄 Resetting localnet..."));
185
- // TODO: Implement reset
186
- });
209
+ .command("reset")
210
+ .description("Reset localnet ledger")
211
+ .action(async () => {
212
+ console.log(chalk.blue("🔄 Resetting localnet..."));
213
+ // TODO: Implement reset
214
+ });
187
215
 
188
216
  program.parse();
package/src/rpc/start.ts CHANGED
@@ -27,7 +27,7 @@ export function startRpcServers(opts: RpcStartOptions = {}) {
27
27
  if (opts.dbPath) process.env.SOLFORGE_DB_PATH = opts.dbPath;
28
28
 
29
29
  const { httpServer, rpcServer } = createLiteSVMRpcServer(rpcPort, host);
30
- const { wsServer } = createLiteSVMWebSocketServer(rpcServer, wsPort);
30
+ const { wsServer } = createLiteSVMWebSocketServer(rpcServer, wsPort, host);
31
31
  const guiServer = guiEnabled
32
32
  ? startGuiServer({ port: guiPort, host, rpcPort, rpcServer })
33
33
  : null;