clawpowers 2.2.5 → 2.2.7

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 (138) hide show
  1. package/CHANGELOG.md +186 -160
  2. package/COMPATIBILITY.md +48 -13
  3. package/KNOWN_LIMITATIONS.md +20 -19
  4. package/LICENSE +44 -44
  5. package/LICENSING.md +10 -10
  6. package/README.md +486 -462
  7. package/SECURITY.md +52 -52
  8. package/dist/index.d.ts +17 -5
  9. package/dist/index.js +187 -92
  10. package/dist/index.js.map +1 -1
  11. package/native/Cargo.lock +4927 -4927
  12. package/native/Cargo.toml +73 -73
  13. package/native/crates/canonical/Cargo.toml +24 -24
  14. package/native/crates/canonical/src/lib.rs +677 -673
  15. package/native/crates/compression/Cargo.toml +20 -20
  16. package/native/crates/compression/benches/compression_bench.rs +42 -42
  17. package/native/crates/compression/src/lib.rs +393 -393
  18. package/native/crates/evm-eth/Cargo.toml +13 -13
  19. package/native/crates/evm-eth/src/lib.rs +105 -105
  20. package/native/crates/fee/Cargo.toml +15 -15
  21. package/native/crates/fee/src/lib.rs +281 -281
  22. package/native/crates/index/Cargo.toml +16 -16
  23. package/native/crates/index/src/lib.rs +277 -277
  24. package/native/crates/policy/Cargo.toml +17 -17
  25. package/native/crates/policy/src/lib.rs +614 -614
  26. package/native/crates/security/Cargo.toml +22 -22
  27. package/native/crates/security/src/lib.rs +478 -478
  28. package/native/crates/tokens/Cargo.toml +13 -13
  29. package/native/crates/tokens/src/lib.rs +534 -534
  30. package/native/crates/verification/Cargo.toml +23 -23
  31. package/native/crates/verification/src/lib.rs +333 -333
  32. package/native/crates/wallet/Cargo.toml +20 -20
  33. package/native/crates/wallet/src/lib.rs +261 -261
  34. package/native/crates/x402/Cargo.toml +30 -30
  35. package/native/crates/x402/src/lib.rs +423 -423
  36. package/native/ffi/Cargo.toml +34 -34
  37. package/native/ffi/build.rs +4 -4
  38. package/native/ffi/src/lib.rs +352 -352
  39. package/native/ffi/tests/integration.rs +354 -354
  40. package/native/pyo3/Cargo.toml +26 -26
  41. package/native/pyo3/pyproject.toml +16 -16
  42. package/native/pyo3/src/lib.rs +407 -407
  43. package/native/pyo3/tests/test_smoke.py +180 -180
  44. package/native/wasm/Cargo.toml +47 -44
  45. package/native/wasm/pkg/.gitignore +6 -6
  46. package/native/wasm/pkg/clawpowers_wasm.d.ts +208 -208
  47. package/native/wasm/pkg/clawpowers_wasm.js +872 -872
  48. package/native/wasm/pkg/clawpowers_wasm_bg.wasm.d.ts +40 -40
  49. package/native/wasm/pkg/package.json +16 -16
  50. package/native/wasm/pkg-node/clawpowers_wasm.d.ts +143 -143
  51. package/native/wasm/pkg-node/clawpowers_wasm.js +798 -798
  52. package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm.d.ts +40 -40
  53. package/native/wasm/pkg-node/package.json +12 -12
  54. package/native/wasm/src/lib.rs +433 -433
  55. package/package.json +13 -8
  56. package/scripts/build-wasm.mjs +59 -0
  57. package/scripts/generate_hermes_wrappers.py +211 -0
  58. package/scripts/hermes_wrapper_overrides.json +184 -0
  59. package/scripts/run-python-script.mjs +48 -0
  60. package/scripts/verify-consumer-install.mjs +109 -0
  61. package/scripts/verify-wasm-artifacts.mjs +26 -3
  62. package/scripts/verify_hermes_wrappers.py +154 -0
  63. package/skill.json +20 -0
  64. package/skills/1password/SKILL.md +34 -0
  65. package/skills/README.md +44 -0
  66. package/skills/agent-nexus-2/SKILL.md +34 -0
  67. package/skills/apple-notes/SKILL.md +34 -0
  68. package/skills/apple-reminders/SKILL.md +34 -0
  69. package/skills/autoresearch/SKILL.md +43 -0
  70. package/skills/bear-notes/SKILL.md +34 -0
  71. package/skills/blogwatcher/SKILL.md +34 -0
  72. package/skills/blucli/SKILL.md +34 -0
  73. package/skills/bluebubbles/SKILL.md +34 -0
  74. package/skills/business-strategy/SKILL.md +41 -0
  75. package/skills/camsnap/SKILL.md +34 -0
  76. package/skills/canvas/SKILL.md +34 -0
  77. package/skills/clawhub/SKILL.md +34 -0
  78. package/skills/coding-agent/SKILL.md +34 -0
  79. package/skills/coding-discipline.skill/SKILL.md +34 -0
  80. package/skills/content-writer/SKILL.md +41 -0
  81. package/skills/discord/SKILL.md +34 -0
  82. package/skills/eightctl/SKILL.md +34 -0
  83. package/skills/execution-validation.skill/SKILL.md +34 -0
  84. package/skills/gemini/SKILL.md +34 -0
  85. package/skills/gh-issues/SKILL.md +34 -0
  86. package/skills/gifgrep/SKILL.md +34 -0
  87. package/skills/github/SKILL.md +41 -0
  88. package/skills/gog/SKILL.md +34 -0
  89. package/skills/goplaces/SKILL.md +34 -0
  90. package/skills/healthcheck/SKILL.md +34 -0
  91. package/skills/himalaya/SKILL.md +34 -0
  92. package/skills/humanize/SKILL.md +41 -0
  93. package/skills/imsg/SKILL.md +34 -0
  94. package/skills/itp/SKILL.md +112 -0
  95. package/skills/mcporter/SKILL.md +34 -0
  96. package/skills/model-usage/SKILL.md +34 -0
  97. package/skills/nano-pdf/SKILL.md +34 -0
  98. package/skills/node-connect/SKILL.md +34 -0
  99. package/skills/notion/SKILL.md +34 -0
  100. package/skills/obsidian/SKILL.md +34 -0
  101. package/skills/openai-whisper/SKILL.md +34 -0
  102. package/skills/openai-whisper-api/SKILL.md +34 -0
  103. package/skills/openhue/SKILL.md +34 -0
  104. package/skills/oracle/SKILL.md +34 -0
  105. package/skills/ordercli/SKILL.md +34 -0
  106. package/skills/peekaboo/SKILL.md +34 -0
  107. package/skills/polyclaw/SKILL.md +34 -0
  108. package/skills/prospector/SKILL.md +41 -0
  109. package/skills/rsi.skill/SKILL.md +34 -0
  110. package/skills/sag/SKILL.md +34 -0
  111. package/skills/security/SKILL.md +41 -0
  112. package/skills/session-logs/SKILL.md +34 -0
  113. package/skills/sherpa-onnx-tts/SKILL.md +34 -0
  114. package/skills/skill-creator/SKILL.md +34 -0
  115. package/skills/slack/SKILL.md +34 -0
  116. package/skills/songsee/SKILL.md +34 -0
  117. package/skills/sonoscli/SKILL.md +34 -0
  118. package/skills/spotify-player/SKILL.md +34 -0
  119. package/skills/strykr-prism/SKILL.md +41 -0
  120. package/skills/summarize/SKILL.md +34 -0
  121. package/skills/taskbridge/SKILL.md +34 -0
  122. package/skills/things-mac/SKILL.md +34 -0
  123. package/skills/tmux/SKILL.md +34 -0
  124. package/skills/trello/SKILL.md +34 -0
  125. package/skills/validator-agent/SKILL.md +41 -0
  126. package/skills/video-frames/SKILL.md +34 -0
  127. package/skills/voice-call/SKILL.md +34 -0
  128. package/skills/wacli/SKILL.md +34 -0
  129. package/skills/weather/SKILL.md +34 -0
  130. package/skills/webmcp-payments/SKILL.md +41 -0
  131. package/skills/xurl/SKILL.md +34 -0
  132. package/src/skills/catalog.ts +435 -435
  133. package/src/skills/executor.ts +56 -56
  134. package/src/skills/index.ts +3 -3
  135. package/src/skills/itp/SKILL.md +112 -112
  136. package/src/skills/loader.ts +262 -193
  137. package/native/ffi/index.node +0 -0
  138. package/native/wasm/pkg-node/.gitignore +0 -6
package/SECURITY.md CHANGED
@@ -1,52 +1,52 @@
1
- # Security Policy
2
-
3
- ## Supported Versions
4
-
5
- | Version | Supported |
6
- | ------- | --------- |
7
- | 2.2.x | ✅ |
8
- | 2.1.x | ✅ |
9
- | < 2.1.0 | ❌ |
10
-
11
- ## Reporting a Vulnerability
12
-
13
- Please report security issues privately to **bill@ai-agent-economy.com**.
14
-
15
- Include:
16
- - affected version
17
- - reproduction steps
18
- - impact assessment
19
- - whether the issue affects native, WASM, or TypeScript fallback paths
20
-
21
- Please do not open public GitHub issues for unpatched vulnerabilities.
22
-
23
- ## Response Expectations
24
-
25
- - initial acknowledgement: within 3 business days
26
- - severity triage: within 5 business days
27
- - fix target: depends on impact and exploitability
28
-
29
- ## Scope Notes
30
-
31
- ClawPowers ships wallet, payment, memory, and RSI primitives. Security-sensitive areas include:
32
- - private key generation and storage
33
- - payment policy enforcement
34
- - x402 header creation and parsing
35
- - native and WASM crypto fallbacks
36
- - local file-backed memory stores
37
-
38
- ## Data Handling
39
-
40
- ClawPowers is designed for local-first operation.
41
-
42
- - no telemetry is sent by the library itself
43
- - secrets are not intentionally exfiltrated by package code
44
- - payment, wallet, and memory data storage behavior depends on the consuming application configuration
45
-
46
- ## Disclosure Policy
47
-
48
- We prefer coordinated disclosure. After a fix is available, we may publish:
49
- - affected versions
50
- - impact summary
51
- - remediation guidance
52
- - CVE or advisory references when applicable
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported |
6
+ | ------- | --------- |
7
+ | 2.2.x | ✅ |
8
+ | 2.1.x | ✅ |
9
+ | < 2.1.0 | ❌ |
10
+
11
+ ## Reporting a Vulnerability
12
+
13
+ Please report security issues privately to **bill@ai-agent-economy.com**.
14
+
15
+ Include:
16
+ - affected version
17
+ - reproduction steps
18
+ - impact assessment
19
+ - whether the issue affects native, WASM, or TypeScript fallback paths
20
+
21
+ Please do not open public GitHub issues for unpatched vulnerabilities.
22
+
23
+ ## Response Expectations
24
+
25
+ - initial acknowledgement: within 3 business days
26
+ - severity triage: within 5 business days
27
+ - fix target: depends on impact and exploitability
28
+
29
+ ## Scope Notes
30
+
31
+ ClawPowers ships wallet, payment, memory, and RSI primitives. Security-sensitive areas include:
32
+ - private key generation and storage
33
+ - payment policy enforcement
34
+ - x402 header creation and parsing
35
+ - native and WASM crypto fallbacks
36
+ - local file-backed memory stores
37
+
38
+ ## Data Handling
39
+
40
+ ClawPowers is designed for local-first operation.
41
+
42
+ - no telemetry is sent by the library itself
43
+ - secrets are not intentionally exfiltrated by package code
44
+ - payment, wallet, and memory data storage behavior depends on the consuming application configuration
45
+
46
+ ## Disclosure Policy
47
+
48
+ We prefer coordinated disclosure. After a fix is available, we may publish:
49
+ - affected versions
50
+ - impact summary
51
+ - remediation guidance
52
+ - CVE or advisory references when applicable
package/dist/index.d.ts CHANGED
@@ -347,7 +347,7 @@ declare function setConfigValue(config: ConfigFile, key: string, value: string):
347
347
  * Default paths, version, config values, RSI tier boundaries.
348
348
  */
349
349
 
350
- declare const VERSION = "2.2.0";
350
+ declare const VERSION = "2.2.7";
351
351
  declare const PACKAGE_NAME = "clawpowers";
352
352
  declare const CLAWPOWERS_HOME: string;
353
353
  declare const DEFAULT_CONFIG: ConfigFile;
@@ -1085,14 +1085,26 @@ declare class SkillExecutor {
1085
1085
  }
1086
1086
 
1087
1087
  interface WalletConfig {
1088
- readonly chain: 'base' | 'ethereum' | 'polygon';
1088
+ readonly chain: 'base' | 'ethereum' | 'polygon' | 'local';
1089
1089
  readonly dataDir: string;
1090
+ /**
1091
+ * Passphrase used to encrypt newly generated/imported wallet key files.
1092
+ * If omitted, ClawPowers generates a high-entropy passphrase and returns it
1093
+ * on the WalletInfo object from generateWallet()/importWallet().
1094
+ */
1095
+ readonly passphrase?: string;
1090
1096
  }
1091
1097
  interface WalletInfo {
1092
1098
  readonly address: string;
1093
1099
  readonly chain: string;
1094
1100
  readonly createdAt: string;
1095
1101
  readonly keyFile: string;
1102
+ /**
1103
+ * Encryption passphrase for the key file. Present on WalletInfo returned by
1104
+ * generateWallet()/importWallet(); intentionally absent from listWallets()
1105
+ * because the passphrase is never persisted in plaintext.
1106
+ */
1107
+ readonly passphrase?: string;
1096
1108
  }
1097
1109
  interface SignedMessage {
1098
1110
  readonly message: string;
@@ -1123,7 +1135,8 @@ declare class WalletManager {
1123
1135
 
1124
1136
  /**
1125
1137
  * Generate a new Ethereum-compatible wallet.
1126
- * Private key is encrypted with a random passphrase and stored to disk.
1138
+ * Private key is encrypted with config.passphrase or a generated high-entropy passphrase.
1139
+ * The returned WalletInfo includes the passphrase needed for later key-file signing.
1127
1140
  */
1128
1141
  declare function generateWallet(config: WalletConfig): Promise<WalletInfo>;
1129
1142
  /**
@@ -1132,8 +1145,7 @@ declare function generateWallet(config: WalletConfig): Promise<WalletInfo>;
1132
1145
  declare function importWallet(privateKeyHex: string, config: WalletConfig): Promise<WalletInfo>;
1133
1146
  /**
1134
1147
  * Sign a message using an encrypted key file and passphrase (returns structured result).
1135
- * Uses secp256k1 ECDSA over Keccak-256(UTF-8 message) when native/WASM tiers provide it;
1136
- * otherwise falls back to HMAC-SHA256 for backward compatibility.
1148
+ * Uses secp256k1 ECDSA over Keccak-256(UTF-8 message) when native/WASM tiers provide it.
1137
1149
  */
1138
1150
  declare function signMessage(message: string, keyFile: string, passphrase: string): Promise<SignedMessage>;
1139
1151
  /**
package/dist/index.js CHANGED
@@ -448,7 +448,7 @@ import { z } from "zod";
448
448
  // src/constants.ts
449
449
  import { join } from "path";
450
450
  import { homedir } from "os";
451
- var VERSION = "2.2.0";
451
+ var VERSION = "2.2.7";
452
452
  var PACKAGE_NAME = "clawpowers";
453
453
  var CLAWPOWERS_HOME = join(homedir(), ".clawpowers");
454
454
  var CONFIG_PATH = join(CLAWPOWERS_HOME, "config.json");
@@ -2287,12 +2287,60 @@ var RSIAuditLog = class {
2287
2287
 
2288
2288
  // src/rsi/auto-research.ts
2289
2289
  import { randomUUID as randomUUID4 } from "crypto";
2290
- import { execSync } from "child_process";
2291
- import { mkdirSync as mkdirSync2, existsSync as existsSync8, writeFileSync as writeFileSync2 } from "fs";
2290
+ import { execFileSync } from "child_process";
2291
+ import { mkdirSync as mkdirSync2, existsSync as existsSync8, rmSync, writeFileSync as writeFileSync2 } from "fs";
2292
2292
  import { join as join4 } from "path";
2293
2293
  import { tmpdir } from "os";
2294
2294
  var MIN_CONFIDENCE = 0.3;
2295
2295
  var REQUIRED_PASSING_RUNS = 3;
2296
+ function tokenizeWords(value) {
2297
+ const tokens = [];
2298
+ let current = "";
2299
+ for (const char of value.toLowerCase()) {
2300
+ const isAlphaNum = char >= "a" && char <= "z" || char >= "0" && char <= "9";
2301
+ if (isAlphaNum) {
2302
+ current += char;
2303
+ continue;
2304
+ }
2305
+ if (current.length > 3) {
2306
+ tokens.push(current);
2307
+ }
2308
+ current = "";
2309
+ }
2310
+ if (current.length > 3) {
2311
+ tokens.push(current);
2312
+ }
2313
+ return tokens;
2314
+ }
2315
+ function splitSearchTerms(value) {
2316
+ return value.split(" ").map((part) => part.trim()).filter((part) => part.length > 3);
2317
+ }
2318
+ function slugify(value) {
2319
+ let slug = "";
2320
+ let lastWasDash = false;
2321
+ for (const char of value.toLowerCase()) {
2322
+ const isAlphaNum = char >= "a" && char <= "z" || char >= "0" && char <= "9";
2323
+ if (isAlphaNum) {
2324
+ slug += char;
2325
+ lastWasDash = false;
2326
+ continue;
2327
+ }
2328
+ if (!lastWasDash && slug.length > 0) {
2329
+ slug += "-";
2330
+ lastWasDash = true;
2331
+ }
2332
+ }
2333
+ if (slug.endsWith("-")) {
2334
+ slug = slug.slice(0, -1);
2335
+ }
2336
+ return slug.slice(0, 40);
2337
+ }
2338
+ function sanitizeShellComment(value, maxLength) {
2339
+ return value.replace(/[\r\n]+/g, " ").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "").slice(0, maxLength);
2340
+ }
2341
+ function shellSingleQuote(value) {
2342
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
2343
+ }
2296
2344
  function buildSearchQuery(failure) {
2297
2345
  const clean = failure.error.replace(/\x1b\[[0-9;]*m/g, "");
2298
2346
  const firstLine = clean.split("\n")[0]?.trim() ?? clean.trim();
@@ -2318,9 +2366,9 @@ function scoreConfidence(candidate, failure) {
2318
2366
  break;
2319
2367
  }
2320
2368
  const failureWords = new Set(
2321
- failure.taskDescription.toLowerCase().split(/\W+/).filter((w) => w.length > 3)
2369
+ tokenizeWords(failure.taskDescription)
2322
2370
  );
2323
- const approachWords = candidate.approach.toLowerCase().split(/\W+/);
2371
+ const approachWords = tokenizeWords(candidate.approach);
2324
2372
  const matches = approachWords.filter((w) => failureWords.has(w)).length;
2325
2373
  const overlap = Math.min(matches / Math.max(failureWords.size, 1), 0.25);
2326
2374
  base += overlap;
@@ -2367,7 +2415,7 @@ var AutoResearcher = class {
2367
2415
  const testScript = this.buildTestScript(candidate, task, sandboxDir);
2368
2416
  const scriptPath = join4(sandboxDir, "test.sh");
2369
2417
  writeFileSync2(scriptPath, testScript, { mode: 493 });
2370
- const output = execSync(`bash "${scriptPath}"`, {
2418
+ const output = execFileSync("bash", ["./test.sh"], {
2371
2419
  cwd: sandboxDir,
2372
2420
  timeout: 3e4,
2373
2421
  encoding: "utf-8",
@@ -2389,7 +2437,7 @@ var AutoResearcher = class {
2389
2437
  };
2390
2438
  } finally {
2391
2439
  try {
2392
- execSync(`rm -rf "${sandboxDir}"`, { timeout: 5e3, stdio: "ignore" });
2440
+ rmSync(sandboxDir, { recursive: true, force: true });
2393
2441
  } catch {
2394
2442
  }
2395
2443
  }
@@ -2445,8 +2493,8 @@ var AutoResearcher = class {
2445
2493
  searchSkillCatalog(failure) {
2446
2494
  try {
2447
2495
  const { SKILLS_CATALOG: SKILLS_CATALOG2 } = (init_catalog(), __toCommonJS(catalog_exports));
2448
- const errorTokens = failure.error.toLowerCase().split(/\W+/).filter((t) => t.length > 3);
2449
- const taskTokens = failure.taskDescription.toLowerCase().split(/\W+/).filter((t) => t.length > 3);
2496
+ const errorTokens = tokenizeWords(failure.error);
2497
+ const taskTokens = tokenizeWords(failure.taskDescription);
2450
2498
  const relevantTokens = /* @__PURE__ */ new Set([...errorTokens, ...taskTokens]);
2451
2499
  return SKILLS_CATALOG2.filter((skill) => {
2452
2500
  const haystack = `${skill.name} ${skill.description}`.toLowerCase();
@@ -2468,16 +2516,16 @@ var AutoResearcher = class {
2468
2516
  }
2469
2517
  async searchNpmRegistry(failure) {
2470
2518
  const query = buildSearchQuery(failure);
2471
- const keywords = query.split(/\s+/).filter((w) => w.length > 3).slice(0, 5).join("+");
2519
+ const keywords = splitSearchTerms(query).slice(0, 5);
2520
+ if (keywords.length === 0) {
2521
+ return [];
2522
+ }
2472
2523
  try {
2473
- const raw = execSync(
2474
- `npm search ${keywords} --json --no-description 2>/dev/null`,
2475
- {
2476
- timeout: 15e3,
2477
- encoding: "utf-8",
2478
- stdio: ["ignore", "pipe", "ignore"]
2479
- }
2480
- );
2524
+ const raw = execFileSync("npm", ["search", ...keywords, "--json", "--no-description"], {
2525
+ timeout: 15e3,
2526
+ encoding: "utf-8",
2527
+ stdio: ["ignore", "pipe", "ignore"]
2528
+ });
2481
2529
  const results = JSON.parse(raw);
2482
2530
  return results.slice(0, 3).map((pkg) => {
2483
2531
  const base = {
@@ -2519,9 +2567,9 @@ var AutoResearcher = class {
2519
2567
  "set -euo pipefail",
2520
2568
  "",
2521
2569
  "# AutoResearch sandbox test",
2522
- `# Task: ${task.description.replace(/'/g, "'\\''").slice(0, 200)}`,
2570
+ `# Task: ${sanitizeShellComment(task.description, 200)}`,
2523
2571
  `# Candidate source: ${candidate.source}`,
2524
- `# Candidate: ${candidate.description.replace(/'/g, "'\\''").slice(0, 200)}`,
2572
+ `# Candidate: ${sanitizeShellComment(candidate.description, 200)}`,
2525
2573
  ""
2526
2574
  ];
2527
2575
  if (candidate.source === "npm-registry") {
@@ -2548,7 +2596,7 @@ var AutoResearcher = class {
2548
2596
  } else {
2549
2597
  const q = buildSearchQuery({ error: candidate.description, taskDescription: task.description, executionSteps: [], skillsUsed: [], attemptCount: 1 });
2550
2598
  if (q.trim().length > 0) {
2551
- lines.push(`echo "web-search candidate: query is '${q.replace(/'/g, "").slice(0, 100)}'"`);
2599
+ lines.push(`echo ${shellSingleQuote(`web-search candidate: query is ${q.slice(0, 100)}`)}`);
2552
2600
  lines.push("exit 0");
2553
2601
  } else {
2554
2602
  lines.push('echo "web-search candidate: empty query" >&2');
@@ -2558,13 +2606,13 @@ var AutoResearcher = class {
2558
2606
  lines.push("");
2559
2607
  lines.push("# Success criteria check");
2560
2608
  for (const criterion of task.successCriteria.slice(0, 2)) {
2561
- lines.push(`echo "Criterion: ${criterion.replace(/'/g, "").slice(0, 100)}"`);
2609
+ lines.push(`echo ${shellSingleQuote(`Criterion: ${criterion.slice(0, 100)}`)}`);
2562
2610
  }
2563
2611
  lines.push('echo "Test passed"');
2564
2612
  return lines.join("\n") + "\n";
2565
2613
  }
2566
2614
  deriveSkillName(candidate) {
2567
- const slug = candidate.description.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
2615
+ const slug = slugify(candidate.description);
2568
2616
  return `auto-${slug}`;
2569
2617
  }
2570
2618
  renderSkillMd(def) {
@@ -2616,12 +2664,66 @@ async function runAutoResearch(failure, task, skillsDir) {
2616
2664
  // src/skills/loader.ts
2617
2665
  import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync9, statSync } from "fs";
2618
2666
  import { join as join5 } from "path";
2667
+ function stripQuotes(value) {
2668
+ if (value.length >= 2) {
2669
+ const first = value[0];
2670
+ const last = value[value.length - 1];
2671
+ if (first === '"' && last === '"' || first === "'" && last === "'") {
2672
+ return value.slice(1, -1);
2673
+ }
2674
+ }
2675
+ return value;
2676
+ }
2677
+ function splitYamlKeyValue(line) {
2678
+ const colonIndex = line.indexOf(":");
2679
+ if (colonIndex <= 0) {
2680
+ return null;
2681
+ }
2682
+ const key = line.slice(0, colonIndex).trim();
2683
+ if (!key) {
2684
+ return null;
2685
+ }
2686
+ for (const char of key) {
2687
+ const isAlphaNum = char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char >= "0" && char <= "9" || char === "_";
2688
+ if (!isAlphaNum) {
2689
+ return null;
2690
+ }
2691
+ }
2692
+ return {
2693
+ key,
2694
+ value: line.slice(colonIndex + 1).trim()
2695
+ };
2696
+ }
2697
+ function parseInlineArray(value) {
2698
+ if (!value.startsWith("[") || !value.endsWith("]")) {
2699
+ return null;
2700
+ }
2701
+ const inner = value.slice(1, -1).trim();
2702
+ if (!inner) {
2703
+ return [];
2704
+ }
2705
+ return inner.split(",").map((item) => stripQuotes(item.trim())).filter(Boolean);
2706
+ }
2707
+ function parseListItem(line) {
2708
+ if (!line.startsWith("-")) {
2709
+ return null;
2710
+ }
2711
+ const value = line.slice(1).trim();
2712
+ if (!value) {
2713
+ return null;
2714
+ }
2715
+ return stripQuotes(value);
2716
+ }
2619
2717
  function parseFrontmatter(content) {
2620
- const match = content.match(/^---\n([\s\S]*?)\n---/);
2621
- if (!match?.[1]) {
2718
+ const normalized = content.replace(/\r\n/g, "\n");
2719
+ if (!normalized.startsWith("---\n")) {
2720
+ return {};
2721
+ }
2722
+ const endIndex = normalized.indexOf("\n---", 4);
2723
+ if (endIndex === -1) {
2622
2724
  return {};
2623
2725
  }
2624
- const yamlText = match[1];
2726
+ const yamlText = normalized.slice(4, endIndex);
2625
2727
  const result = {};
2626
2728
  const lines = yamlText.split("\n");
2627
2729
  let currentKey = "";
@@ -2631,10 +2733,10 @@ function parseFrontmatter(content) {
2631
2733
  for (const line of lines) {
2632
2734
  const trimmed = line.trim();
2633
2735
  if (trimmed === "" || trimmed.startsWith("#")) continue;
2634
- const topLevel = line.match(/^(\w+):\s*(.*)$/);
2635
- if (topLevel?.[1]) {
2636
- currentKey = topLevel[1];
2637
- const val = topLevel[2]?.trim().replace(/^["']|["']$/g, "") ?? "";
2736
+ const topLevel = line === trimmed ? splitYamlKeyValue(trimmed) : null;
2737
+ if (topLevel) {
2738
+ currentKey = topLevel.key;
2739
+ const val = stripQuotes(topLevel.value);
2638
2740
  if (currentKey === "name" && val) result.name = val;
2639
2741
  if (currentKey === "description" && val) result.description = val;
2640
2742
  inRequires = false;
@@ -2646,31 +2748,30 @@ function parseFrontmatter(content) {
2646
2748
  continue;
2647
2749
  }
2648
2750
  if (inRequires) {
2649
- const reqKey = trimmed.match(/^(\w+):\s*(.*)$/);
2650
- if (reqKey?.[1]) {
2651
- requiresKey = reqKey[1];
2652
- const inlineArr = reqKey[2]?.match(/\[(.*)\]/);
2653
- if (inlineArr?.[1]) {
2654
- const items = inlineArr[1].split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
2751
+ const reqKey = splitYamlKeyValue(trimmed);
2752
+ if (reqKey) {
2753
+ requiresKey = reqKey.key;
2754
+ const items = parseInlineArray(reqKey.value);
2755
+ if (items) {
2655
2756
  if (requiresKey === "bins") requires.bins = items;
2656
2757
  if (requiresKey === "env") requires.env = items;
2657
2758
  if (requiresKey === "config") requires.config = items;
2658
2759
  }
2659
2760
  continue;
2660
2761
  }
2661
- const listItem = trimmed.match(/^-\s*["']?(.+?)["']?$/);
2662
- if (listItem?.[1] && requiresKey) {
2762
+ const listItem = parseListItem(trimmed);
2763
+ if (listItem && requiresKey) {
2663
2764
  if (requiresKey === "bins") {
2664
2765
  requires.bins = requires.bins ?? [];
2665
- requires.bins.push(listItem[1]);
2766
+ requires.bins.push(listItem);
2666
2767
  }
2667
2768
  if (requiresKey === "env") {
2668
2769
  requires.env = requires.env ?? [];
2669
- requires.env.push(listItem[1]);
2770
+ requires.env.push(listItem);
2670
2771
  }
2671
2772
  if (requiresKey === "config") {
2672
2773
  requires.config = requires.config ?? [];
2673
- requires.config.push(listItem[1]);
2774
+ requires.config.push(listItem);
2674
2775
  }
2675
2776
  }
2676
2777
  }
@@ -2791,6 +2892,7 @@ var SCRYPT_P = 1;
2791
2892
  var KEY_LENGTH = 32;
2792
2893
  var IV_LENGTH = 12;
2793
2894
  var AUTH_TAG_LENGTH = 16;
2895
+ var GENERATED_PASSPHRASE_BYTES = 32;
2794
2896
  function addressFromKeyMaterial(keyMaterial) {
2795
2897
  const digestHex = digestForWalletAddress(keyMaterial);
2796
2898
  const hash = Buffer.from(digestHex.replace(/^0x/, ""), "hex");
@@ -2803,6 +2905,13 @@ function deriveKey(passphrase, salt) {
2803
2905
  p: SCRYPT_P
2804
2906
  });
2805
2907
  }
2908
+ function resolvePassphrase(config) {
2909
+ const passphrase = config.passphrase ?? randomBytes2(GENERATED_PASSPHRASE_BYTES).toString("hex");
2910
+ if (passphrase.length === 0) {
2911
+ throw new Error("Wallet passphrase must not be empty");
2912
+ }
2913
+ return passphrase;
2914
+ }
2806
2915
  function encryptPrivateKey(privateKey, passphrase) {
2807
2916
  const salt = randomBytes2(32);
2808
2917
  const key = deriveKey(passphrase, salt);
@@ -2845,7 +2954,32 @@ async function ensureDir(dir) {
2845
2954
  await mkdir7(dir, { recursive: true });
2846
2955
  }
2847
2956
  }
2957
+ async function writeEncryptedWallet(privateKey, address, config, createdAt, passphrase) {
2958
+ const encrypted = encryptPrivateKey(privateKey, passphrase);
2959
+ const keyFileData = {
2960
+ version: 1,
2961
+ address,
2962
+ chain: config.chain,
2963
+ createdAt,
2964
+ crypto: {
2965
+ cipher: "aes-256-gcm",
2966
+ ciphertext: encrypted.ciphertext,
2967
+ iv: encrypted.iv,
2968
+ authTag: encrypted.authTag,
2969
+ salt: encrypted.salt,
2970
+ scryptParams: { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }
2971
+ }
2972
+ };
2973
+ await ensureDir(config.dataDir);
2974
+ const keyFileName = `${address.slice(2, 10)}-${Date.now()}.json`;
2975
+ const keyFilePath = join6(config.dataDir, keyFileName);
2976
+ await writeFile4(keyFilePath, JSON.stringify(keyFileData, null, 2) + "\n", "utf-8");
2977
+ return keyFilePath;
2978
+ }
2848
2979
  async function signMessageFromKeyFile(message, keyFile, passphrase) {
2980
+ if (passphrase.length === 0) {
2981
+ throw new Error("Wallet passphrase must not be empty");
2982
+ }
2849
2983
  const content = await readFile7(keyFile, "utf-8");
2850
2984
  const keyFileData = JSON.parse(content);
2851
2985
  const privateKey = decryptPrivateKey(
@@ -2866,14 +3000,9 @@ async function signMessageFromKeyFile(message, keyFile, passphrase) {
2866
3000
  address: keyFileData.address
2867
3001
  };
2868
3002
  }
2869
- const { createHmac } = await import("crypto");
2870
- dlog("signMessage (keyfile): HMAC-SHA256 legacy (no secp256k1/keccak tier)");
2871
- const signature = createHmac("sha256", privateKey).update(message).digest("hex");
2872
- return {
2873
- message,
2874
- signature: "0x" + signature,
2875
- address: keyFileData.address
2876
- };
3003
+ throw new Error(
3004
+ `Ethereum signing requires Keccak-256 + secp256k1 (Tier 1 native or Tier 2 WASM). Current active tier is "${getActiveTier()}" which cannot produce a valid EVM signature. Install the native addon or enable the WASM tier before calling signMessage().`
3005
+ );
2877
3006
  }
2878
3007
  async function signMessageFromPrivateKey(privateKeyHex, message) {
2879
3008
  const cleaned = privateKeyHex.replace(/^0x/i, "");
@@ -2898,31 +3027,14 @@ async function generateWallet(config) {
2898
3027
  const privateKeyHex = privateKey.toString("hex");
2899
3028
  const address = generateAddress(privateKeyHex);
2900
3029
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
2901
- const passphrase = randomBytes2(16).toString("hex");
2902
- const encrypted = encryptPrivateKey(privateKey, passphrase);
2903
- const keyFileData = {
2904
- version: 1,
2905
- address,
2906
- chain: config.chain,
2907
- createdAt,
2908
- crypto: {
2909
- cipher: "aes-256-gcm",
2910
- ciphertext: encrypted.ciphertext,
2911
- iv: encrypted.iv,
2912
- authTag: encrypted.authTag,
2913
- salt: encrypted.salt,
2914
- scryptParams: { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }
2915
- }
2916
- };
2917
- await ensureDir(config.dataDir);
2918
- const keyFileName = `${address.slice(2, 10)}-${Date.now()}.json`;
2919
- const keyFilePath = join6(config.dataDir, keyFileName);
2920
- await writeFile4(keyFilePath, JSON.stringify(keyFileData, null, 2) + "\n", "utf-8");
3030
+ const passphrase = resolvePassphrase(config);
3031
+ const keyFilePath = await writeEncryptedWallet(privateKey, address, config, createdAt, passphrase);
2921
3032
  return {
2922
3033
  address,
2923
3034
  chain: config.chain,
2924
3035
  createdAt,
2925
- keyFile: keyFilePath
3036
+ keyFile: keyFilePath,
3037
+ passphrase
2926
3038
  };
2927
3039
  }
2928
3040
  async function importWallet(privateKeyHex, config) {
@@ -2933,31 +3045,14 @@ async function importWallet(privateKeyHex, config) {
2933
3045
  const privateKey = Buffer.from(cleaned, "hex");
2934
3046
  const address = generateAddress(cleaned);
2935
3047
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
2936
- const passphrase = randomBytes2(16).toString("hex");
2937
- const encrypted = encryptPrivateKey(privateKey, passphrase);
2938
- const keyFileData = {
2939
- version: 1,
2940
- address,
2941
- chain: config.chain,
2942
- createdAt,
2943
- crypto: {
2944
- cipher: "aes-256-gcm",
2945
- ciphertext: encrypted.ciphertext,
2946
- iv: encrypted.iv,
2947
- authTag: encrypted.authTag,
2948
- salt: encrypted.salt,
2949
- scryptParams: { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }
2950
- }
2951
- };
2952
- await ensureDir(config.dataDir);
2953
- const keyFileName = `${address.slice(2, 10)}-${Date.now()}.json`;
2954
- const keyFilePath = join6(config.dataDir, keyFileName);
2955
- await writeFile4(keyFilePath, JSON.stringify(keyFileData, null, 2) + "\n", "utf-8");
3048
+ const passphrase = resolvePassphrase(config);
3049
+ const keyFilePath = await writeEncryptedWallet(privateKey, address, config, createdAt, passphrase);
2956
3050
  return {
2957
3051
  address,
2958
3052
  chain: config.chain,
2959
3053
  createdAt,
2960
- keyFile: keyFilePath
3054
+ keyFile: keyFilePath,
3055
+ passphrase
2961
3056
  };
2962
3057
  }
2963
3058
  async function signMessage(a, b, c) {
@@ -3457,7 +3552,7 @@ export {
3457
3552
  * Drop-in capability layer: payments, memory, RSI, wallet.
3458
3553
  * No agent control loop — bring your own agent.
3459
3554
  *
3460
- * @version 2.2.0
3555
+ * @version 2.2.6
3461
3556
  * @license BSL-1.1
3462
3557
  * @patent-pending
3463
3558
  */