@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.
- package/.env.participant.example +17 -0
- package/.env.strategy.example +31 -0
- package/README.md +12 -4
- package/bin/openfunderse.mjs +161 -5
- package/package.json +3 -1
- package/packs/openfunderse-participant/openfunderse-participant/SKILL.md +5 -1
- package/packs/openfunderse-strategy/openfunderse-strategy/SKILL.md +5 -1
|
@@ -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
|
-
|
|
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
|
|
34
|
-
-
|
|
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.
|
package/bin/openfunderse.mjs
CHANGED
|
@@ -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
|
|
363
|
+
return readProfileTemplate("strategy");
|
|
321
364
|
}
|
|
322
365
|
if (profile === "participant") {
|
|
323
|
-
return
|
|
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
|
-
|
|
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,
|
|
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(),
|
|
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
|
+
"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.
|