@wiimdy/openfunderse 1.1.3 → 1.1.5

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.
@@ -0,0 +1,17 @@
1
+ # OpenFunderse participant env scaffold
2
+ # Copy values from your PARTICIPANT_PK, ADDRESS
3
+
4
+ RELAYER_URL=https://openfunderse-relayer-liard.vercel.app/
5
+ BOT_ID=bot-participant-1
6
+ BOT_API_KEY=replace_me
7
+
8
+ CHAIN_ID=10143
9
+ CLAIM_ATTESTATION_VERIFIER_ADDRESS=0x76AB8571cED9F4A81b733969216F53C1e13BC835
10
+ # Temporary bootstrap key (public and unsafe). Replace via bot-init before real usage.
11
+ PARTICIPANT_PRIVATE_KEY=__TEMP_PRIVATE_KEY__
12
+ PARTICIPANT_ADDRESS=0x00
13
+
14
+ PARTICIPANT_REQUIRE_EXPLICIT_SUBMIT=true
15
+ PARTICIPANT_AUTO_SUBMIT=false
16
+ # PARTICIPANT_TRUSTED_RELAYER_HOSTS=openfunderse-relayer.example.com
17
+ # PARTICIPANT_ALLOW_HTTP_RELAYER=true
@@ -0,0 +1,31 @@
1
+ # OpenFunderse strategy env scaffold
2
+ # Copy values from your STRATEGY_PK, ADDRESS
3
+
4
+ RELAYER_URL=https://openfunderse-relayer-liard.vercel.app/
5
+ BOT_ID=bot-strategy-1
6
+ BOT_API_KEY=replace_me
7
+ CHAIN_ID=10143
8
+ RPC_URL=https://testnet-rpc.monad.xyz
9
+
10
+ # NadFun / protocol addresses
11
+ INTENT_BOOK_ADDRESS=0x76AB8571cED9F4A81b733969216F53C1e13BC835
12
+ NADFUN_EXECUTION_ADAPTER_ADDRESS=0x7949DBd0779Cd829D1B2491A33C8EcCc88669Aa4
13
+ ADAPTER_ADDRESS=0x7949DBd0779Cd829D1B2491A33C8EcCc88669Aa4
14
+ VAULT_ADDRESS=0x34FB76f115f4C4E461eB90eA9f92faEF8c4A5E0A
15
+ NADFUN_LENS_ADDRESS=0xB056d79CA5257589692699a46623F901a3BB76f1
16
+ NADFUN_BONDING_CURVE_ROUTER=0x865054F0F6A288adaAc30261731361EA7E908003
17
+ NADFUN_DEX_ROUTER=0x5D4a4f430cA3B1b2dB86B9cFE48a5316800F5fb2
18
+ NADFUN_WMON_ADDRESS=0xFb8bE43D65FBC1290D6178C6DbA6E58c6D18fA60
19
+
20
+ # Strategy signer (EOA)
21
+ # Temporary bootstrap key (public and unsafe). Replace via bot-init before real usage.
22
+ STRATEGY_PRIVATE_KEY=__TEMP_PRIVATE_KEY__
23
+ STRATEGY_ADDRESS=0x00
24
+
25
+ # STRATEGY_CREATE_MIN_SIGNER_BALANCE_WEI=10000000000000000
26
+
27
+ # Safety defaults
28
+ STRATEGY_REQUIRE_EXPLICIT_SUBMIT=true
29
+ STRATEGY_AUTO_SUBMIT=false
30
+ # STRATEGY_TRUSTED_RELAYER_HOSTS=openfunderse-relayer.example.com
31
+ # STRATEGY_ALLOW_HTTP_RELAYER=true
package/README.md CHANGED
@@ -15,10 +15,16 @@ npx @wiimdy/openfunderse@latest bot-init --skill-name strategy --yes
15
15
  # or
16
16
  npx @wiimdy/openfunderse@latest bot-init --skill-name participant --yes
17
17
 
18
- # 3) load env in current shell
19
- set -a; source .env; set +a
18
+ # 3) (optional) load env in current shell
19
+ # @wiimdy/openfunderse-agents auto-loads .env.strategy / .env.participant by command role.
20
+ set -a; source .env.strategy; set +a
21
+ # or
22
+ set -a; source .env.participant; set +a
20
23
  ```
21
24
 
25
+ By default, `install` and `bot-init` also sync env keys into OpenClaw config (`~/.openclaw/openclaw.json > env.vars`).
26
+ Disable this with `--no-sync-openclaw-env`.
27
+
22
28
  ## Where Files Are Stored
23
29
 
24
30
  - In OpenClaw, skills are installed under `~/.openclaw/workspace/skills`.
@@ -30,5 +36,7 @@ set -a; source .env; set +a
30
36
  - Use only:
31
37
  - `openfunderse-strategy`
32
38
  - `openfunderse-participant`
33
- - Default env scaffold path is `.env`.
34
- - If you run both bots in the same directory, use `--env-path` to split files (for example `.env.strategy`, `.env.participant`).
39
+ - Default env scaffold path is role-based:
40
+ - strategy: `.env.strategy`
41
+ - participant: `.env.participant`
42
+ - Use `--env-path` only when you want a custom filename/location.
@@ -12,6 +12,8 @@ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
12
12
  const THIS_FILE = fileURLToPath(import.meta.url);
13
13
  const PACKAGE_ROOT = path.resolve(path.dirname(THIS_FILE), "..");
14
14
  const PACKS_ROOT = path.join(PACKAGE_ROOT, "packs");
15
+ const STRATEGY_ENV_TEMPLATE_PATH = path.join(PACKAGE_ROOT, ".env.strategy.example");
16
+ const PARTICIPANT_ENV_TEMPLATE_PATH = path.join(PACKAGE_ROOT, ".env.participant.example");
15
17
  const DEFAULT_RUNTIME_PACKAGE = "@wiimdy/openfunderse-agents";
16
18
  const SUPPORTED_ENV_PROFILES = new Set(["strategy", "participant", "all"]);
17
19
  const SUPPORTED_BOT_INIT_ROLES = new Set(["strategy", "participant"]);
@@ -69,8 +71,10 @@ function printUsage() {
69
71
  Usage:
70
72
  openfunderse list
71
73
  openfunderse bot-init [--role <strategy|participant>] [--skill-name <name>] [--env-path <path>] [--wallet-dir <dir>] [--wallet-name <name>] [--force] [--yes]
74
+ [--no-sync-openclaw-env]
72
75
  openfunderse install <pack-name> [--dest <skills-dir>] [--codex-home <dir>] [--force] [--with-runtime]
73
76
  [--no-init-env] [--env-path <path>] [--env-profile <strategy|participant|all>]
77
+ [--no-sync-openclaw-env]
74
78
  [--runtime-package <name>] [--runtime-dir <dir>] [--runtime-manager <npm|pnpm|yarn|bun>]
75
79
 
76
80
  Examples:
@@ -103,6 +107,7 @@ function parseArgs(argv) {
103
107
  skillName: "",
104
108
  walletDir: "",
105
109
  walletName: "",
110
+ syncOpenclawEnv: true,
106
111
  yes: false
107
112
  };
108
113
  const positionals = [];
@@ -135,6 +140,14 @@ function parseArgs(argv) {
135
140
  options.withRuntime = true;
136
141
  continue;
137
142
  }
143
+ if (token === "--sync-openclaw-env") {
144
+ options.syncOpenclawEnv = true;
145
+ continue;
146
+ }
147
+ if (token === "--no-sync-openclaw-env") {
148
+ options.syncOpenclawEnv = false;
149
+ continue;
150
+ }
138
151
  if (token === "--init-env") {
139
152
  options.initEnv = true;
140
153
  options.initEnvExplicit = true;
@@ -315,12 +328,42 @@ function runtimeEnvExamplePath(runtimeDir, runtimePackage) {
315
328
  return path.join(runtimeDir, "node_modules", ...runtimePackage.split("/"), ".env.example");
316
329
  }
317
330
 
331
+ function openclawConfigPath(codexHome) {
332
+ const resolvedCodexHome = path.resolve(codexHome);
333
+ return path.join(path.dirname(resolvedCodexHome), "openclaw.json");
334
+ }
335
+
336
+ function defaultEnvFileNameForProfile(profile) {
337
+ if (profile === "strategy") {
338
+ return ".env.strategy";
339
+ }
340
+ if (profile === "participant") {
341
+ return ".env.participant";
342
+ }
343
+ return ".env";
344
+ }
345
+
346
+ function applyTemplateTokens(template) {
347
+ return template.replaceAll("__TEMP_PRIVATE_KEY__", TEMP_PRIVATE_KEY);
348
+ }
349
+
350
+ async function readProfileTemplate(profile) {
351
+ const templatePath =
352
+ profile === "strategy" ? STRATEGY_ENV_TEMPLATE_PATH : PARTICIPANT_ENV_TEMPLATE_PATH;
353
+ const fallback = profile === "strategy" ? STRATEGY_ENV_TEMPLATE : PARTICIPANT_ENV_TEMPLATE;
354
+ if (!existsSync(templatePath)) {
355
+ return fallback;
356
+ }
357
+ const template = await readFile(templatePath, "utf8");
358
+ return applyTemplateTokens(template);
359
+ }
360
+
318
361
  async function buildEnvScaffold(profile, runtimeDir, runtimePackage) {
319
362
  if (profile === "strategy") {
320
- return STRATEGY_ENV_TEMPLATE;
363
+ return readProfileTemplate("strategy");
321
364
  }
322
365
  if (profile === "participant") {
323
- return PARTICIPANT_ENV_TEMPLATE;
366
+ return readProfileTemplate("participant");
324
367
  }
325
368
 
326
369
  const runtimeTemplate = runtimeEnvExamplePath(runtimeDir, runtimePackage);
@@ -328,7 +371,9 @@ async function buildEnvScaffold(profile, runtimeDir, runtimePackage) {
328
371
  return readFile(runtimeTemplate, "utf8");
329
372
  }
330
373
 
331
- return `${STRATEGY_ENV_TEMPLATE}\n\n${PARTICIPANT_ENV_TEMPLATE}`;
374
+ const strategy = await readProfileTemplate("strategy");
375
+ const participant = await readProfileTemplate("participant");
376
+ return `${strategy}\n\n${participant}`;
332
377
  }
333
378
 
334
379
  async function writeEnvScaffold(options) {
@@ -341,7 +386,7 @@ async function writeEnvScaffold(options) {
341
386
  const profile = normalizeEnvProfile(rawProfile);
342
387
  const envTarget = options.envFile
343
388
  ? path.resolve(options.envFile)
344
- : path.join(runtimeDir, ".env");
389
+ : path.join(runtimeDir, defaultEnvFileNameForProfile(profile));
345
390
 
346
391
  const alreadyExists = existsSync(envTarget);
347
392
  if (alreadyExists && !options.force) {
@@ -368,7 +413,7 @@ async function writeEnvScaffold(options) {
368
413
  }
369
414
 
370
415
  function defaultEnvPathForRole(role) {
371
- return path.join(process.cwd(), ".env");
416
+ return path.join(process.cwd(), `.env.${role}`);
372
417
  }
373
418
 
374
419
  function readAssignedEnvValue(content, key) {
@@ -382,6 +427,86 @@ function readAssignedEnvValue(content, key) {
382
427
  return "";
383
428
  }
384
429
 
430
+ function parseEnvAssignments(content) {
431
+ const result = {};
432
+ const lines = String(content || "").replace(/^\uFEFF/, "").split(/\r?\n/);
433
+ for (const rawLine of lines) {
434
+ const line = rawLine.trim();
435
+ if (!line || line.startsWith("#")) continue;
436
+ const match = line.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
437
+ if (!match) continue;
438
+ const key = match[1];
439
+ let value = (match[2] || "").trim();
440
+ if (
441
+ ((value.startsWith("\"") && value.endsWith("\"")) ||
442
+ (value.startsWith("'") && value.endsWith("'"))) &&
443
+ value.length >= 2
444
+ ) {
445
+ value = value.slice(1, -1);
446
+ }
447
+ result[key] = value;
448
+ }
449
+ return result;
450
+ }
451
+
452
+ async function syncOpenclawEnvVarsFromFile(envFile, codexHome) {
453
+ const configPath = openclawConfigPath(codexHome);
454
+ if (!existsSync(configPath)) {
455
+ return {
456
+ synced: false,
457
+ reason: "openclaw-config-not-found",
458
+ configPath,
459
+ envFile,
460
+ writtenKeys: []
461
+ };
462
+ }
463
+
464
+ const envContent = await readFile(envFile, "utf8");
465
+ const assignments = parseEnvAssignments(envContent);
466
+ const keys = Object.keys(assignments);
467
+ if (keys.length === 0) {
468
+ return {
469
+ synced: false,
470
+ reason: "empty-env-file",
471
+ configPath,
472
+ envFile,
473
+ writtenKeys: []
474
+ };
475
+ }
476
+
477
+ const rawConfig = await readFile(configPath, "utf8");
478
+ const parsedConfig = JSON.parse(rawConfig);
479
+ if (!parsedConfig || typeof parsedConfig !== "object" || Array.isArray(parsedConfig)) {
480
+ throw new Error(`invalid openclaw config json object: ${configPath}`);
481
+ }
482
+
483
+ const nextConfig = { ...parsedConfig };
484
+ const envSection =
485
+ nextConfig.env && typeof nextConfig.env === "object" && !Array.isArray(nextConfig.env)
486
+ ? { ...nextConfig.env }
487
+ : {};
488
+ const varsSection =
489
+ envSection.vars && typeof envSection.vars === "object" && !Array.isArray(envSection.vars)
490
+ ? { ...envSection.vars }
491
+ : {};
492
+
493
+ for (const [key, value] of Object.entries(assignments)) {
494
+ varsSection[key] = value;
495
+ }
496
+
497
+ envSection.vars = varsSection;
498
+ nextConfig.env = envSection;
499
+ await writeFile(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`);
500
+
501
+ return {
502
+ synced: true,
503
+ reason: "ok",
504
+ configPath,
505
+ envFile,
506
+ writtenKeys: keys
507
+ };
508
+ }
509
+
385
510
  function isPlaceholderEnvValue(value) {
386
511
  const normalized = (value || "").trim();
387
512
  if (!normalized) return true;
@@ -567,11 +692,27 @@ async function runBotInit(options) {
567
692
  await mkdir(path.dirname(envFile), { recursive: true });
568
693
  await writeFile(envFile, nextEnvContent);
569
694
  await chmod(envFile, 0o600);
695
+ let syncMeta = null;
696
+ if (options.syncOpenclawEnv) {
697
+ const codexHome = options.codexHome ? path.resolve(options.codexHome) : defaultCodexHome();
698
+ syncMeta = await syncOpenclawEnvVarsFromFile(envFile, codexHome);
699
+ }
570
700
  const sourceCommand = `set -a; source ${shellQuote(envFile)}; set +a`;
571
701
 
572
702
  console.log(`Initialized ${role} bot wallet for Monad testnet (${DEFAULT_MONAD_CHAIN_ID}).`);
573
703
  console.log(`Address: ${wallet.address}`);
574
704
  console.log(`Env file updated: ${envFile}`);
705
+ if (syncMeta) {
706
+ if (syncMeta.synced) {
707
+ console.log(
708
+ `Synced env vars to OpenClaw config: ${syncMeta.configPath} (${syncMeta.writtenKeys.length} keys)`
709
+ );
710
+ } else {
711
+ console.log(
712
+ `Skipped OpenClaw env sync (${syncMeta.reason}): ${syncMeta.configPath}`
713
+ );
714
+ }
715
+ }
575
716
  console.log(`Wallet backup (keep secret): ${walletFiles.walletPath}`);
576
717
  console.log(`Private key backup (keep secret): ${walletFiles.privateKeyPath}`);
577
718
  console.log(`Load env now: ${sourceCommand}`);
@@ -747,6 +888,10 @@ async function installPack(packName, options) {
747
888
  };
748
889
  envScaffoldMeta = await writeEnvScaffold(envOptions);
749
890
  }
891
+ let openclawSyncMeta = null;
892
+ if (envScaffoldMeta && options.syncOpenclawEnv) {
893
+ openclawSyncMeta = await syncOpenclawEnvVarsFromFile(envScaffoldMeta.envFile, codexHome);
894
+ }
750
895
 
751
896
  console.log(`Installed pack: ${packName}`);
752
897
  console.log(`Skills root: ${skillsRoot}`);
@@ -767,6 +912,17 @@ async function installPack(packName, options) {
767
912
  );
768
913
  }
769
914
  }
915
+ if (openclawSyncMeta) {
916
+ if (openclawSyncMeta.synced) {
917
+ console.log(
918
+ `Synced env vars to OpenClaw config: ${openclawSyncMeta.configPath} (${openclawSyncMeta.writtenKeys.length} keys)`
919
+ );
920
+ } else {
921
+ console.log(
922
+ `Skipped OpenClaw env sync (${openclawSyncMeta.reason}): ${openclawSyncMeta.configPath}`
923
+ );
924
+ }
925
+ }
770
926
  console.log("Restart Codex to pick up new skills.");
771
927
  }
772
928
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wiimdy/openfunderse",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Install OpenFunderse skill packs into Codex",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,6 +9,8 @@
9
9
  "files": [
10
10
  "bin",
11
11
  "packs",
12
+ ".env.strategy.example",
13
+ ".env.participant.example",
12
14
  "README.md"
13
15
  ],
14
16
  "dependencies": {
@@ -48,9 +48,13 @@ npx @wiimdy/openfunderse@latest bot-init \
48
48
  4) Load env for the current shell:
49
49
 
50
50
  ```bash
51
- set -a; source .env; set +a
51
+ set -a; source .env.participant; set +a
52
52
  ```
53
53
 
54
+ OpenClaw note:
55
+ - `install` / `bot-init` sync env keys into `~/.openclaw/openclaw.json` (`env.vars`) by default.
56
+ - Use `--no-sync-openclaw-env` if you want file-only behavior.
57
+
54
58
  Note:
55
59
  - The scaffold includes a temporary public key placeholder by default.
56
60
  - Always run `bot-init` before funding or running production actions.
@@ -70,9 +70,13 @@ npx @wiimdy/openfunderse@latest bot-init \
70
70
  4) Load env for the current shell:
71
71
 
72
72
  ```bash
73
- set -a; source .env; set +a
73
+ set -a; source .env.strategy; set +a
74
74
  ```
75
75
 
76
+ OpenClaw note:
77
+ - `install` / `bot-init` sync env keys into `~/.openclaw/openclaw.json` (`env.vars`) by default.
78
+ - Use `--no-sync-openclaw-env` if you want file-only behavior.
79
+
76
80
  Note:
77
81
  - The scaffold includes a temporary public key placeholder by default.
78
82
  - Always run `bot-init` before funding or running production actions.