clawpowers 2.0.0 → 2.2.1

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 (63) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/COMPATIBILITY.md +13 -0
  3. package/KNOWN_LIMITATIONS.md +19 -0
  4. package/LICENSING.md +10 -0
  5. package/README.md +201 -9
  6. package/SECURITY.md +33 -53
  7. package/dist/index.d.ts +638 -5
  8. package/dist/index.js +986 -58
  9. package/dist/index.js.map +1 -1
  10. package/native/Cargo.lock +4863 -0
  11. package/native/Cargo.toml +73 -0
  12. package/native/crates/canonical/Cargo.toml +24 -0
  13. package/native/crates/canonical/src/lib.rs +673 -0
  14. package/native/crates/compression/Cargo.toml +20 -0
  15. package/native/crates/compression/benches/compression_bench.rs +42 -0
  16. package/native/crates/compression/src/lib.rs +393 -0
  17. package/native/crates/evm-eth/Cargo.toml +13 -0
  18. package/native/crates/evm-eth/src/lib.rs +105 -0
  19. package/native/crates/fee/Cargo.toml +15 -0
  20. package/native/crates/fee/src/lib.rs +281 -0
  21. package/native/crates/index/Cargo.toml +16 -0
  22. package/native/crates/index/src/lib.rs +277 -0
  23. package/native/crates/policy/Cargo.toml +17 -0
  24. package/native/crates/policy/src/lib.rs +614 -0
  25. package/native/crates/security/Cargo.toml +22 -0
  26. package/native/crates/security/src/lib.rs +478 -0
  27. package/native/crates/tokens/Cargo.toml +13 -0
  28. package/native/crates/tokens/src/lib.rs +534 -0
  29. package/native/crates/verification/Cargo.toml +23 -0
  30. package/native/crates/verification/src/lib.rs +333 -0
  31. package/native/crates/wallet/Cargo.toml +20 -0
  32. package/native/crates/wallet/src/lib.rs +261 -0
  33. package/native/crates/x402/Cargo.toml +30 -0
  34. package/native/crates/x402/src/lib.rs +423 -0
  35. package/native/ffi/Cargo.toml +34 -0
  36. package/native/ffi/build.rs +4 -0
  37. package/native/ffi/index.node +0 -0
  38. package/native/ffi/src/lib.rs +352 -0
  39. package/native/ffi/tests/integration.rs +354 -0
  40. package/native/pyo3/Cargo.toml +26 -0
  41. package/native/pyo3/pyproject.toml +16 -0
  42. package/native/pyo3/src/lib.rs +407 -0
  43. package/native/pyo3/tests/test_smoke.py +180 -0
  44. package/native/wasm/Cargo.toml +44 -0
  45. package/native/wasm/pkg/.gitignore +6 -0
  46. package/native/wasm/pkg/clawpowers_wasm.d.ts +208 -0
  47. package/native/wasm/pkg/clawpowers_wasm.js +872 -0
  48. package/native/wasm/pkg/clawpowers_wasm_bg.wasm +0 -0
  49. package/native/wasm/pkg/clawpowers_wasm_bg.wasm.d.ts +40 -0
  50. package/native/wasm/pkg/package.json +17 -0
  51. package/native/wasm/pkg-node/.gitignore +6 -0
  52. package/native/wasm/pkg-node/clawpowers_wasm.d.ts +143 -0
  53. package/native/wasm/pkg-node/clawpowers_wasm.js +798 -0
  54. package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm +0 -0
  55. package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm.d.ts +40 -0
  56. package/native/wasm/pkg-node/package.json +13 -0
  57. package/native/wasm/src/lib.rs +433 -0
  58. package/package.json +24 -3
  59. package/src/skills/catalog.ts +435 -0
  60. package/src/skills/executor.ts +56 -0
  61. package/src/skills/index.ts +3 -0
  62. package/src/skills/itp/SKILL.md +112 -0
  63. package/src/skills/loader.ts +193 -0
@@ -0,0 +1,193 @@
1
+ /**
2
+ * ClawPowers Agent — Skill Loader
3
+ * Discovers skills from skill directories, validates SKILL.md frontmatter.
4
+ */
5
+
6
+ import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import type { SkillManifest, SkillRequirements, Profile } from '../types.js';
9
+
10
+ // ─── YAML Frontmatter Parser ──────────────────────────────────────────────────
11
+
12
+ interface ParsedFrontmatter {
13
+ name?: string;
14
+ description?: string;
15
+ metadata?: {
16
+ openclaw?: {
17
+ requires?: {
18
+ bins?: string[];
19
+ env?: string[];
20
+ config?: string[];
21
+ };
22
+ };
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Parse YAML frontmatter from a SKILL.md file content.
28
+ * Expects format: ---\n<yaml>\n---\n<content>
29
+ */
30
+ export function parseFrontmatter(content: string): ParsedFrontmatter {
31
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
32
+ if (!match?.[1]) {
33
+ return {};
34
+ }
35
+
36
+ const yamlText = match[1];
37
+ const result: ParsedFrontmatter = {};
38
+
39
+ // Simple YAML parser for our known structure
40
+ const lines = yamlText.split('\n');
41
+ let currentKey = '';
42
+ let inRequires = false;
43
+ let requiresKey = '';
44
+ const requires: { bins?: string[]; env?: string[]; config?: string[] } = {};
45
+
46
+ for (const line of lines) {
47
+ const trimmed = line.trim();
48
+ if (trimmed === '' || trimmed.startsWith('#')) continue;
49
+
50
+ // Top-level keys
51
+ const topLevel = line.match(/^(\w+):\s*(.*)$/);
52
+ if (topLevel?.[1]) {
53
+ currentKey = topLevel[1];
54
+ const val = topLevel[2]?.trim().replace(/^["']|["']$/g, '') ?? '';
55
+ if (currentKey === 'name' && val) result.name = val;
56
+ if (currentKey === 'description' && val) result.description = val;
57
+ inRequires = false;
58
+ continue;
59
+ }
60
+
61
+ // metadata.openclaw.requires detection
62
+ if (trimmed === 'openclaw:') continue;
63
+ if (trimmed === 'requires:') {
64
+ inRequires = true;
65
+ continue;
66
+ }
67
+
68
+ if (inRequires) {
69
+ const reqKey = trimmed.match(/^(\w+):\s*(.*)$/);
70
+ if (reqKey?.[1]) {
71
+ requiresKey = reqKey[1];
72
+ // Inline array: bins: ["node", "git"]
73
+ const inlineArr = reqKey[2]?.match(/\[(.*)\]/);
74
+ if (inlineArr?.[1]) {
75
+ const items = inlineArr[1].split(',').map(s => s.trim().replace(/^["']|["']$/g, ''));
76
+ if (requiresKey === 'bins') requires.bins = items;
77
+ if (requiresKey === 'env') requires.env = items;
78
+ if (requiresKey === 'config') requires.config = items;
79
+ }
80
+ continue;
81
+ }
82
+ // List item under requires key
83
+ const listItem = trimmed.match(/^-\s*["']?(.+?)["']?$/);
84
+ if (listItem?.[1] && requiresKey) {
85
+ if (requiresKey === 'bins') {
86
+ requires.bins = requires.bins ?? [];
87
+ requires.bins.push(listItem[1]);
88
+ }
89
+ if (requiresKey === 'env') {
90
+ requires.env = requires.env ?? [];
91
+ requires.env.push(listItem[1]);
92
+ }
93
+ if (requiresKey === 'config') {
94
+ requires.config = requires.config ?? [];
95
+ requires.config.push(listItem[1]);
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ if (Object.keys(requires).length > 0) {
102
+ result.metadata = { openclaw: { requires } };
103
+ }
104
+
105
+ return result;
106
+ }
107
+
108
+ // ─── Skill Discovery ──────────────────────────────────────────────────────────
109
+
110
+ /**
111
+ * Load a single skill manifest from a directory containing SKILL.md
112
+ */
113
+ export function loadSkillManifest(skillDir: string): SkillManifest | null {
114
+ const skillMdPath = join(skillDir, 'SKILL.md');
115
+ if (!existsSync(skillMdPath)) {
116
+ return null;
117
+ }
118
+
119
+ const content = readFileSync(skillMdPath, 'utf-8');
120
+ const frontmatter = parseFrontmatter(content);
121
+
122
+ if (!frontmatter.name || !frontmatter.description) {
123
+ return null;
124
+ }
125
+
126
+ let requirements: SkillRequirements | null = null;
127
+ const req = frontmatter.metadata?.openclaw?.requires;
128
+ if (req) {
129
+ requirements = {
130
+ bins: req.bins ?? [],
131
+ env: req.env ?? [],
132
+ config: req.config ?? [],
133
+ };
134
+ }
135
+
136
+ return {
137
+ name: frontmatter.name,
138
+ description: frontmatter.description,
139
+ path: skillDir,
140
+ requirements,
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Discover all skills in a directory. Each subdirectory with a valid SKILL.md
146
+ * becomes a skill manifest.
147
+ */
148
+ export function discoverSkills(skillsDir: string): SkillManifest[] {
149
+ if (!existsSync(skillsDir)) {
150
+ return [];
151
+ }
152
+
153
+ const entries = readdirSync(skillsDir);
154
+ const manifests: SkillManifest[] = [];
155
+
156
+ for (const entry of entries) {
157
+ const fullPath = join(skillsDir, entry);
158
+ if (!statSync(fullPath).isDirectory()) continue;
159
+
160
+ const manifest = loadSkillManifest(fullPath);
161
+ if (manifest) {
162
+ manifests.push(manifest);
163
+ }
164
+ }
165
+
166
+ return manifests.sort((a, b) => a.name.localeCompare(b.name));
167
+ }
168
+
169
+ /**
170
+ * Filter skills based on active profile.
171
+ * Returns only skills that are listed in the profile's skill list.
172
+ */
173
+ export function getActiveSkills(
174
+ allSkills: SkillManifest[],
175
+ profile: Profile
176
+ ): SkillManifest[] {
177
+ const profileSkillSet = new Set(profile.skills);
178
+ return allSkills.filter(skill => profileSkillSet.has(skill.name));
179
+ }
180
+
181
+ /**
182
+ * List skills with their active/inactive status for a given profile.
183
+ */
184
+ export function listSkillsWithStatus(
185
+ allSkills: SkillManifest[],
186
+ profile: Profile
187
+ ): Array<{ skill: SkillManifest; active: boolean }> {
188
+ const profileSkillSet = new Set(profile.skills);
189
+ return allSkills.map(skill => ({
190
+ skill,
191
+ active: profileSkillSet.has(skill.name),
192
+ }));
193
+ }