society-protocol 1.0.0
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/LICENSE +21 -0
- package/README.md +111 -0
- package/dist/adapters.d.ts +101 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +764 -0
- package/dist/adapters.js.map +1 -0
- package/dist/agents-md.d.ts +59 -0
- package/dist/agents-md.d.ts.map +1 -0
- package/dist/agents-md.js +204 -0
- package/dist/agents-md.js.map +1 -0
- package/dist/autoconfig.d.ts +137 -0
- package/dist/autoconfig.d.ts.map +1 -0
- package/dist/autoconfig.js +452 -0
- package/dist/autoconfig.js.map +1 -0
- package/dist/bootstrap.d.ts +68 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +304 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/bridges/a2a-bridge.d.ts +156 -0
- package/dist/bridges/a2a-bridge.d.ts.map +1 -0
- package/dist/bridges/a2a-bridge.js +337 -0
- package/dist/bridges/a2a-bridge.js.map +1 -0
- package/dist/bridges/mcp-bridge.d.ts +87 -0
- package/dist/bridges/mcp-bridge.d.ts.map +1 -0
- package/dist/bridges/mcp-bridge.js +332 -0
- package/dist/bridges/mcp-bridge.js.map +1 -0
- package/dist/cache.d.ts +130 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +257 -0
- package/dist/cache.js.map +1 -0
- package/dist/capsules.d.ts +23 -0
- package/dist/capsules.d.ts.map +1 -0
- package/dist/capsules.js +75 -0
- package/dist/capsules.js.map +1 -0
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +263 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/coc.d.ts +121 -0
- package/dist/coc.d.ts.map +1 -0
- package/dist/coc.js +629 -0
- package/dist/coc.js.map +1 -0
- package/dist/coc.test.d.ts +2 -0
- package/dist/coc.test.d.ts.map +1 -0
- package/dist/coc.test.js +80 -0
- package/dist/coc.test.js.map +1 -0
- package/dist/compression.d.ts +125 -0
- package/dist/compression.d.ts.map +1 -0
- package/dist/compression.js +573 -0
- package/dist/compression.js.map +1 -0
- package/dist/cot-stream.d.ts +220 -0
- package/dist/cot-stream.d.ts.map +1 -0
- package/dist/cot-stream.js +673 -0
- package/dist/cot-stream.js.map +1 -0
- package/dist/crypto-wasm.d.ts +100 -0
- package/dist/crypto-wasm.d.ts.map +1 -0
- package/dist/crypto-wasm.js +229 -0
- package/dist/crypto-wasm.js.map +1 -0
- package/dist/federation.d.ts +200 -0
- package/dist/federation.d.ts.map +1 -0
- package/dist/federation.js +691 -0
- package/dist/federation.js.map +1 -0
- package/dist/federation.test.d.ts +2 -0
- package/dist/federation.test.d.ts.map +1 -0
- package/dist/federation.test.js +71 -0
- package/dist/federation.test.js.map +1 -0
- package/dist/gateway/capability-router.d.ts +77 -0
- package/dist/gateway/capability-router.d.ts.map +1 -0
- package/dist/gateway/capability-router.js +222 -0
- package/dist/gateway/capability-router.js.map +1 -0
- package/dist/gateway/demand-spawner.d.ts +155 -0
- package/dist/gateway/demand-spawner.d.ts.map +1 -0
- package/dist/gateway/demand-spawner.js +426 -0
- package/dist/gateway/demand-spawner.js.map +1 -0
- package/dist/identity.d.ts +46 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +102 -0
- package/dist/identity.js.map +1 -0
- package/dist/identity.test.d.ts +2 -0
- package/dist/identity.test.d.ts.map +1 -0
- package/dist/identity.test.js +45 -0
- package/dist/identity.test.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1572 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.d.ts +210 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +1105 -0
- package/dist/integration.js.map +1 -0
- package/dist/integration.test.d.ts +2 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +155 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/knowledge.d.ts +219 -0
- package/dist/knowledge.d.ts.map +1 -0
- package/dist/knowledge.js +543 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/knowledge.test.d.ts +2 -0
- package/dist/knowledge.test.d.ts.map +1 -0
- package/dist/knowledge.test.js +72 -0
- package/dist/knowledge.test.js.map +1 -0
- package/dist/latent-space.d.ts +178 -0
- package/dist/latent-space.d.ts.map +1 -0
- package/dist/latent-space.js +385 -0
- package/dist/latent-space.js.map +1 -0
- package/dist/lib.d.ts +30 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +30 -0
- package/dist/lib.js.map +1 -0
- package/dist/mcp/server.d.ts +74 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +1392 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/metrics.d.ts +98 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +222 -0
- package/dist/metrics.js.map +1 -0
- package/dist/p2p.d.ts +87 -0
- package/dist/p2p.d.ts.map +1 -0
- package/dist/p2p.js +606 -0
- package/dist/p2p.js.map +1 -0
- package/dist/persona/capabilities.d.ts +17 -0
- package/dist/persona/capabilities.d.ts.map +1 -0
- package/dist/persona/capabilities.js +224 -0
- package/dist/persona/capabilities.js.map +1 -0
- package/dist/persona/domains.d.ts +22 -0
- package/dist/persona/domains.d.ts.map +1 -0
- package/dist/persona/domains.js +176 -0
- package/dist/persona/domains.js.map +1 -0
- package/dist/persona/embeddings.d.ts +40 -0
- package/dist/persona/embeddings.d.ts.map +1 -0
- package/dist/persona/embeddings.js +265 -0
- package/dist/persona/embeddings.js.map +1 -0
- package/dist/persona/engine.d.ts +79 -0
- package/dist/persona/engine.d.ts.map +1 -0
- package/dist/persona/engine.js +1087 -0
- package/dist/persona/engine.js.map +1 -0
- package/dist/persona/index.d.ts +11 -0
- package/dist/persona/index.d.ts.map +1 -0
- package/dist/persona/index.js +11 -0
- package/dist/persona/index.js.map +1 -0
- package/dist/persona/lifecycle.d.ts +17 -0
- package/dist/persona/lifecycle.d.ts.map +1 -0
- package/dist/persona/lifecycle.js +36 -0
- package/dist/persona/lifecycle.js.map +1 -0
- package/dist/persona/retrieval.d.ts +6 -0
- package/dist/persona/retrieval.d.ts.map +1 -0
- package/dist/persona/retrieval.js +122 -0
- package/dist/persona/retrieval.js.map +1 -0
- package/dist/persona/sync.d.ts +15 -0
- package/dist/persona/sync.d.ts.map +1 -0
- package/dist/persona/sync.js +92 -0
- package/dist/persona/sync.js.map +1 -0
- package/dist/persona/types.d.ts +283 -0
- package/dist/persona/types.d.ts.map +1 -0
- package/dist/persona/types.js +2 -0
- package/dist/persona/types.js.map +1 -0
- package/dist/persona/zkp/engine.d.ts +26 -0
- package/dist/persona/zkp/engine.d.ts.map +1 -0
- package/dist/persona/zkp/engine.js +370 -0
- package/dist/persona/zkp/engine.js.map +1 -0
- package/dist/persona/zkp/types.d.ts +39 -0
- package/dist/persona/zkp/types.d.ts.map +1 -0
- package/dist/persona/zkp/types.js +2 -0
- package/dist/persona/zkp/types.js.map +1 -0
- package/dist/planner.d.ts +114 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +522 -0
- package/dist/planner.js.map +1 -0
- package/dist/proactive/checkpoints.d.ts +9 -0
- package/dist/proactive/checkpoints.d.ts.map +1 -0
- package/dist/proactive/checkpoints.js +20 -0
- package/dist/proactive/checkpoints.js.map +1 -0
- package/dist/proactive/engine.d.ts +59 -0
- package/dist/proactive/engine.d.ts.map +1 -0
- package/dist/proactive/engine.js +406 -0
- package/dist/proactive/engine.js.map +1 -0
- package/dist/proactive/scheduler.d.ts +11 -0
- package/dist/proactive/scheduler.d.ts.map +1 -0
- package/dist/proactive/scheduler.js +45 -0
- package/dist/proactive/scheduler.js.map +1 -0
- package/dist/proactive/swarm-controller.d.ts +189 -0
- package/dist/proactive/swarm-controller.d.ts.map +1 -0
- package/dist/proactive/swarm-controller.js +477 -0
- package/dist/proactive/swarm-controller.js.map +1 -0
- package/dist/proactive/swarm-registry.d.ts +13 -0
- package/dist/proactive/swarm-registry.d.ts.map +1 -0
- package/dist/proactive/swarm-registry.js +122 -0
- package/dist/proactive/swarm-registry.js.map +1 -0
- package/dist/proactive/types.d.ts +145 -0
- package/dist/proactive/types.d.ts.map +1 -0
- package/dist/proactive/types.js +25 -0
- package/dist/proactive/types.js.map +1 -0
- package/dist/registry.d.ts +35 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +88 -0
- package/dist/registry.js.map +1 -0
- package/dist/reputation.d.ts +123 -0
- package/dist/reputation.d.ts.map +1 -0
- package/dist/reputation.js +366 -0
- package/dist/reputation.js.map +1 -0
- package/dist/reputation.test.d.ts +5 -0
- package/dist/reputation.test.d.ts.map +1 -0
- package/dist/reputation.test.js +265 -0
- package/dist/reputation.test.js.map +1 -0
- package/dist/rooms.d.ts +96 -0
- package/dist/rooms.d.ts.map +1 -0
- package/dist/rooms.js +410 -0
- package/dist/rooms.js.map +1 -0
- package/dist/sdk/client.d.ts +290 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +1287 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/index.d.ts +32 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +70 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/security.d.ts +230 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +652 -0
- package/dist/security.js.map +1 -0
- package/dist/skills/engine.d.ts +262 -0
- package/dist/skills/engine.d.ts.map +1 -0
- package/dist/skills/engine.js +788 -0
- package/dist/skills/engine.js.map +1 -0
- package/dist/skills/engine.test.d.ts +2 -0
- package/dist/skills/engine.test.d.ts.map +1 -0
- package/dist/skills/engine.test.js +134 -0
- package/dist/skills/engine.test.js.map +1 -0
- package/dist/skills/parser.d.ts +129 -0
- package/dist/skills/parser.d.ts.map +1 -0
- package/dist/skills/parser.js +318 -0
- package/dist/skills/parser.js.map +1 -0
- package/dist/social.d.ts +149 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +401 -0
- package/dist/social.js.map +1 -0
- package/dist/storage-optimized.d.ts +116 -0
- package/dist/storage-optimized.d.ts.map +1 -0
- package/dist/storage-optimized.js +264 -0
- package/dist/storage-optimized.js.map +1 -0
- package/dist/storage.d.ts +584 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +2703 -0
- package/dist/storage.js.map +1 -0
- package/dist/storage.test.d.ts +2 -0
- package/dist/storage.test.d.ts.map +1 -0
- package/dist/storage.test.js +78 -0
- package/dist/storage.test.js.map +1 -0
- package/dist/swp.d.ts +443 -0
- package/dist/swp.d.ts.map +1 -0
- package/dist/swp.js +223 -0
- package/dist/swp.js.map +1 -0
- package/dist/swp.test.d.ts +5 -0
- package/dist/swp.test.d.ts.map +1 -0
- package/dist/swp.test.js +127 -0
- package/dist/swp.test.js.map +1 -0
- package/dist/templates.d.ts +25 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +1048 -0
- package/dist/templates.js.map +1 -0
- package/dist/test-e2e.d.ts +14 -0
- package/dist/test-e2e.d.ts.map +1 -0
- package/dist/test-e2e.js +266 -0
- package/dist/test-e2e.js.map +1 -0
- package/dist/workers/research-worker.d.ts +19 -0
- package/dist/workers/research-worker.d.ts.map +1 -0
- package/dist/workers/research-worker.js +141 -0
- package/dist/workers/research-worker.js.map +1 -0
- package/package.json +110 -0
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Society Protocol - Skills Engine v1.0
|
|
3
|
+
*
|
|
4
|
+
* Sistema de skills/plugins avançado:
|
|
5
|
+
* - skill.md: Skills Society Protocol (YAML + Markdown)
|
|
6
|
+
* - claw.md: Skills OpenClaw específicas
|
|
7
|
+
* - Integração multi-runtime (OpenClaw, Claude, Ollama, etc)
|
|
8
|
+
* - Auto-discovery e hot-reload de skills
|
|
9
|
+
* - Composição de skills (skills chamando skills)
|
|
10
|
+
*/
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
12
|
+
import { ulid } from 'ulid';
|
|
13
|
+
import { readFileSync, existsSync, readdirSync, watch } from 'fs';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
import YAML from 'yaml';
|
|
16
|
+
import { spawn } from 'child_process';
|
|
17
|
+
import { SkillParser } from './parser.js';
|
|
18
|
+
export class SkillExecutionError extends Error {
|
|
19
|
+
code;
|
|
20
|
+
runtimeType;
|
|
21
|
+
details;
|
|
22
|
+
constructor(code, message, runtimeType, details) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.code = code;
|
|
25
|
+
this.runtimeType = runtimeType;
|
|
26
|
+
this.details = details;
|
|
27
|
+
this.name = 'SkillExecutionError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// ─── Skills Engine ───────────────────────────────────────────────
|
|
31
|
+
export class SkillsEngine extends EventEmitter {
|
|
32
|
+
storage;
|
|
33
|
+
identity;
|
|
34
|
+
skills = new Map();
|
|
35
|
+
clawSkills = new Map();
|
|
36
|
+
runtimes = new Map();
|
|
37
|
+
skillDir;
|
|
38
|
+
watchers = [];
|
|
39
|
+
skillParser = new SkillParser();
|
|
40
|
+
constructor(storage, identity, skillDir) {
|
|
41
|
+
super();
|
|
42
|
+
this.storage = storage;
|
|
43
|
+
this.identity = identity;
|
|
44
|
+
this.skillDir = skillDir || join(process.env.HOME || '', '.society', 'skills');
|
|
45
|
+
this.loadSkills();
|
|
46
|
+
this.setupWatchers();
|
|
47
|
+
}
|
|
48
|
+
// ─── Skill Loading ───────────────────────────────────────────
|
|
49
|
+
loadSkills() {
|
|
50
|
+
if (!existsSync(this.skillDir)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const entries = readdirSync(this.skillDir, { withFileTypes: true });
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
// Pasta de skill
|
|
57
|
+
const skillPath = join(this.skillDir, entry.name);
|
|
58
|
+
this.loadSkillFromDir(skillPath);
|
|
59
|
+
}
|
|
60
|
+
else if (entry.name.endsWith('.md')) {
|
|
61
|
+
// Arquivo único
|
|
62
|
+
this.loadSkillFromFile(join(this.skillDir, entry.name));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
loadSkillFromDir(dir) {
|
|
67
|
+
const skillMd = join(dir, 'skill.md');
|
|
68
|
+
const clawMd = join(dir, 'claw.md');
|
|
69
|
+
if (existsSync(skillMd)) {
|
|
70
|
+
this.loadSkillFromFile(skillMd);
|
|
71
|
+
}
|
|
72
|
+
if (existsSync(clawMd)) {
|
|
73
|
+
this.loadClawSkillFromFile(clawMd);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
loadSkillFromFile(path) {
|
|
77
|
+
try {
|
|
78
|
+
const content = readFileSync(path, 'utf-8');
|
|
79
|
+
const manifest = this.parseSkillMd(content);
|
|
80
|
+
if (manifest) {
|
|
81
|
+
manifest.skill.id = manifest.skill.id || this.generateSkillId(path);
|
|
82
|
+
this.skills.set(manifest.skill.id, manifest);
|
|
83
|
+
this.emit('skill:loaded', manifest.skill.id, manifest);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.error(`Failed to load skill from ${path}:`, err);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
loadClawSkillFromFile(path) {
|
|
91
|
+
try {
|
|
92
|
+
const content = readFileSync(path, 'utf-8');
|
|
93
|
+
const clawSkill = this.parseClawMd(content);
|
|
94
|
+
if (clawSkill) {
|
|
95
|
+
const id = this.generateSkillId(path);
|
|
96
|
+
this.clawSkills.set(id, clawSkill);
|
|
97
|
+
this.emit('claw-skill:loaded', id, clawSkill);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
console.error(`Failed to load claw skill from ${path}:`, err);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// ─── Parsers ─────────────────────────────────────────────────
|
|
105
|
+
parseSkillMd(content) {
|
|
106
|
+
const parsed = this.skillParser.parse(content);
|
|
107
|
+
const skillInfo = parsed?.skill;
|
|
108
|
+
if (!skillInfo?.name) {
|
|
109
|
+
throw new Error('Invalid skill manifest: skill.name is required');
|
|
110
|
+
}
|
|
111
|
+
const id = skillInfo.id || `skill_${ulid().slice(0, 8)}`;
|
|
112
|
+
const nowIso = new Date().toISOString();
|
|
113
|
+
const runtime = parsed.runtime && typeof parsed.runtime === 'object'
|
|
114
|
+
? parsed.runtime
|
|
115
|
+
: { type: 'local' };
|
|
116
|
+
const triggers = Array.isArray(parsed.triggers)
|
|
117
|
+
? parsed.triggers.map((trigger) => this.normalizeTrigger(trigger))
|
|
118
|
+
: [{ type: 'manual', config: {} }];
|
|
119
|
+
const capabilities = parsed.capabilities && typeof parsed.capabilities === 'object'
|
|
120
|
+
? parsed.capabilities
|
|
121
|
+
: {
|
|
122
|
+
inputs: Array.isArray(parsed.inputs)
|
|
123
|
+
? parsed.inputs.map((input) => this.normalizeInputCapability(input))
|
|
124
|
+
: [],
|
|
125
|
+
outputs: Array.isArray(parsed.outputs)
|
|
126
|
+
? parsed.outputs.map((output) => this.normalizeOutputCapability(output))
|
|
127
|
+
: []
|
|
128
|
+
};
|
|
129
|
+
return {
|
|
130
|
+
skill: {
|
|
131
|
+
id,
|
|
132
|
+
name: skillInfo.name,
|
|
133
|
+
version: skillInfo.version || '1.0.0',
|
|
134
|
+
description: skillInfo.description || parsed.documentation || 'Skill',
|
|
135
|
+
author: skillInfo.author,
|
|
136
|
+
license: skillInfo.license,
|
|
137
|
+
homepage: skillInfo.homepage,
|
|
138
|
+
repository: skillInfo.repository,
|
|
139
|
+
tags: skillInfo.tags || [],
|
|
140
|
+
icon: skillInfo.icon
|
|
141
|
+
},
|
|
142
|
+
runtime,
|
|
143
|
+
triggers,
|
|
144
|
+
capabilities,
|
|
145
|
+
actions: parsed.actions || [],
|
|
146
|
+
compose: parsed.compose,
|
|
147
|
+
society: parsed.society || {},
|
|
148
|
+
knowledge: parsed.knowledge,
|
|
149
|
+
security: parsed.security || {
|
|
150
|
+
sandbox: 'light',
|
|
151
|
+
permissions: []
|
|
152
|
+
},
|
|
153
|
+
meta: parsed.meta || {
|
|
154
|
+
created: nowIso,
|
|
155
|
+
updated: nowIso,
|
|
156
|
+
version: 1
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
parseClawMd(content) {
|
|
161
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
162
|
+
if (!match)
|
|
163
|
+
return null;
|
|
164
|
+
const [_, yamlContent, instructionsBody] = match;
|
|
165
|
+
const claw = YAML.parse(yamlContent);
|
|
166
|
+
if (claw && instructionsBody.trim()) {
|
|
167
|
+
claw.instructions.system = instructionsBody.trim();
|
|
168
|
+
}
|
|
169
|
+
return claw;
|
|
170
|
+
}
|
|
171
|
+
// ─── Skill Execution ─────────────────────────────────────────
|
|
172
|
+
async executeSkill(skillId, inputs, context) {
|
|
173
|
+
const skill = this.skills.get(skillId);
|
|
174
|
+
if (!skill) {
|
|
175
|
+
throw new SkillExecutionError('not_found', `Skill ${skillId} not found`);
|
|
176
|
+
}
|
|
177
|
+
// Validar inputs
|
|
178
|
+
this.validateInputs(skill, inputs);
|
|
179
|
+
const runtime = {
|
|
180
|
+
id: `run_${ulid()}`,
|
|
181
|
+
skillId,
|
|
182
|
+
status: 'running',
|
|
183
|
+
startedAt: Date.now(),
|
|
184
|
+
inputs,
|
|
185
|
+
outputs: {},
|
|
186
|
+
logs: [],
|
|
187
|
+
artifacts: []
|
|
188
|
+
};
|
|
189
|
+
this.runtimes.set(runtime.id, runtime);
|
|
190
|
+
this.emit('runtime:started', runtime);
|
|
191
|
+
try {
|
|
192
|
+
await this.runRuntimeWithRetries(skill, runtime, context);
|
|
193
|
+
// Executar ações pós-execução
|
|
194
|
+
await this.executeActions(skill, runtime, context);
|
|
195
|
+
runtime.status = 'completed';
|
|
196
|
+
runtime.finishedAt = Date.now();
|
|
197
|
+
this.emit('runtime:completed', runtime);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
runtime.status = 'failed';
|
|
201
|
+
const normalized = this.normalizeExecutionError(skill.runtime.type, error);
|
|
202
|
+
runtime.error = normalized.message;
|
|
203
|
+
runtime.finishedAt = Date.now();
|
|
204
|
+
this.emit('runtime:failed', runtime);
|
|
205
|
+
throw normalized;
|
|
206
|
+
}
|
|
207
|
+
return runtime;
|
|
208
|
+
}
|
|
209
|
+
async runRuntimeWithRetries(skill, runtime, context) {
|
|
210
|
+
const maxRetries = this.getMaxRetries(skill);
|
|
211
|
+
let attempt = 0;
|
|
212
|
+
while (true) {
|
|
213
|
+
try {
|
|
214
|
+
await this.executeRuntime(skill, runtime, context);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (attempt >= maxRetries) {
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
attempt++;
|
|
222
|
+
runtime.logs.push(`Runtime ${skill.runtime.type} failed on attempt ${attempt}/${maxRetries + 1}. Retrying...`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async executeRuntime(skill, runtime, context) {
|
|
227
|
+
switch (skill.runtime.type) {
|
|
228
|
+
case 'openclaw':
|
|
229
|
+
await this.executeOpenClaw(skill, runtime, context);
|
|
230
|
+
break;
|
|
231
|
+
case 'claude':
|
|
232
|
+
await this.executeClaude(skill, runtime, context);
|
|
233
|
+
break;
|
|
234
|
+
case 'ollama':
|
|
235
|
+
await this.executeOllama(skill, runtime, context);
|
|
236
|
+
break;
|
|
237
|
+
case 'http':
|
|
238
|
+
await this.executeHttp(skill, runtime);
|
|
239
|
+
break;
|
|
240
|
+
case 'docker':
|
|
241
|
+
await this.executeDocker(skill, runtime);
|
|
242
|
+
break;
|
|
243
|
+
case 'local':
|
|
244
|
+
await this.executeLocal(skill, runtime);
|
|
245
|
+
break;
|
|
246
|
+
default:
|
|
247
|
+
throw new SkillExecutionError('runtime', `Unknown runtime type: ${skill.runtime.type}`, skill.runtime.type);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
getMaxRetries(skill) {
|
|
251
|
+
const retries = Number(skill.runtime?.retries ?? skill.security?.retries ?? 0);
|
|
252
|
+
if (!Number.isFinite(retries) || retries <= 0) {
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
255
|
+
return Math.min(5, Math.floor(retries));
|
|
256
|
+
}
|
|
257
|
+
getRuntimeTimeoutMs(skill, fallbackMs = 30_000) {
|
|
258
|
+
const timeout = Number(skill.runtime?.timeoutMs ?? skill.security?.maxExecutionTime ?? fallbackMs);
|
|
259
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
260
|
+
return fallbackMs;
|
|
261
|
+
}
|
|
262
|
+
return timeout;
|
|
263
|
+
}
|
|
264
|
+
normalizeExecutionError(runtimeType, error) {
|
|
265
|
+
if (error instanceof SkillExecutionError) {
|
|
266
|
+
return error;
|
|
267
|
+
}
|
|
268
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
269
|
+
if (/timed? out/i.test(message)) {
|
|
270
|
+
return new SkillExecutionError('timeout', message, runtimeType);
|
|
271
|
+
}
|
|
272
|
+
if (/fetch|network|ENOTFOUND|ECONNREFUSED/i.test(message)) {
|
|
273
|
+
return new SkillExecutionError('network', message, runtimeType);
|
|
274
|
+
}
|
|
275
|
+
return new SkillExecutionError('runtime', message, runtimeType);
|
|
276
|
+
}
|
|
277
|
+
async executeOpenClaw(skill, runtime, context) {
|
|
278
|
+
const config = skill.runtime.openclaw;
|
|
279
|
+
runtime.logs.push('Initializing OpenClaw runtime...');
|
|
280
|
+
const instructions = this.buildOpenClawInstructions(skill, runtime.inputs);
|
|
281
|
+
const endpoint = process.env.OPENCLAW_API_URL || 'http://localhost:8080/v1/skills/execute';
|
|
282
|
+
runtime.logs.push(`Calling OpenClaw endpoint ${endpoint}`);
|
|
283
|
+
const response = await fetch(endpoint, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers: {
|
|
286
|
+
'Content-Type': 'application/json'
|
|
287
|
+
},
|
|
288
|
+
body: JSON.stringify({
|
|
289
|
+
model: config?.model,
|
|
290
|
+
mcp: !!config?.mcp,
|
|
291
|
+
tools: config?.tools || [],
|
|
292
|
+
instructions,
|
|
293
|
+
inputs: runtime.inputs,
|
|
294
|
+
context
|
|
295
|
+
}),
|
|
296
|
+
signal: AbortSignal.timeout(this.getRuntimeTimeoutMs(skill, 30_000))
|
|
297
|
+
});
|
|
298
|
+
if (!response.ok) {
|
|
299
|
+
throw new Error(`OpenClaw execution failed: ${response.status}`);
|
|
300
|
+
}
|
|
301
|
+
const payload = await response.json();
|
|
302
|
+
const responseData = payload && typeof payload === 'object'
|
|
303
|
+
? payload
|
|
304
|
+
: { result: payload };
|
|
305
|
+
runtime.outputs = {
|
|
306
|
+
...responseData,
|
|
307
|
+
instructions
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
async executeClaude(skill, runtime, context) {
|
|
311
|
+
const config = skill.runtime.claude;
|
|
312
|
+
runtime.logs.push('Initializing Claude runtime...');
|
|
313
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
314
|
+
if (!apiKey) {
|
|
315
|
+
throw new Error('ANTHROPIC_API_KEY is required for Claude runtime');
|
|
316
|
+
}
|
|
317
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
headers: {
|
|
320
|
+
'content-type': 'application/json',
|
|
321
|
+
'x-api-key': apiKey,
|
|
322
|
+
'anthropic-version': '2023-06-01'
|
|
323
|
+
},
|
|
324
|
+
body: JSON.stringify({
|
|
325
|
+
model: config.model || 'claude-3-5-sonnet-latest',
|
|
326
|
+
max_tokens: config.maxTokens || 1024,
|
|
327
|
+
system: config.systemPrompt || 'You are a helpful assistant.',
|
|
328
|
+
messages: [
|
|
329
|
+
{
|
|
330
|
+
role: 'user',
|
|
331
|
+
content: JSON.stringify({
|
|
332
|
+
inputs: runtime.inputs,
|
|
333
|
+
context
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
]
|
|
337
|
+
}),
|
|
338
|
+
signal: AbortSignal.timeout(this.getRuntimeTimeoutMs(skill, 30_000))
|
|
339
|
+
});
|
|
340
|
+
if (!response.ok) {
|
|
341
|
+
throw new Error(`Claude execution failed: ${response.status}`);
|
|
342
|
+
}
|
|
343
|
+
const data = await response.json();
|
|
344
|
+
const resultText = data?.content?.[0]?.text ?? JSON.stringify(data);
|
|
345
|
+
runtime.outputs = {
|
|
346
|
+
result: resultText,
|
|
347
|
+
model: config.model || 'claude-3-5-sonnet'
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
async executeOllama(skill, runtime, context) {
|
|
351
|
+
const config = skill.runtime.ollama;
|
|
352
|
+
runtime.logs.push(`Initializing Ollama with model ${config.model}...`);
|
|
353
|
+
const ollamaUrl = process.env.OLLAMA_URL || 'http://localhost:11434';
|
|
354
|
+
const response = await fetch(`${ollamaUrl}/api/generate`, {
|
|
355
|
+
method: 'POST',
|
|
356
|
+
headers: { 'Content-Type': 'application/json' },
|
|
357
|
+
body: JSON.stringify({
|
|
358
|
+
model: config.model,
|
|
359
|
+
prompt: JSON.stringify({
|
|
360
|
+
inputs: runtime.inputs,
|
|
361
|
+
context
|
|
362
|
+
}),
|
|
363
|
+
stream: false,
|
|
364
|
+
...(config.parameters || {})
|
|
365
|
+
}),
|
|
366
|
+
signal: AbortSignal.timeout(this.getRuntimeTimeoutMs(skill, 30_000))
|
|
367
|
+
});
|
|
368
|
+
if (!response.ok) {
|
|
369
|
+
throw new Error(`Ollama execution failed: ${response.status}`);
|
|
370
|
+
}
|
|
371
|
+
const data = await response.json();
|
|
372
|
+
runtime.outputs = {
|
|
373
|
+
result: data?.response || '',
|
|
374
|
+
model: config.model
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
async executeHttp(skill, runtime) {
|
|
378
|
+
const config = skill.runtime.http;
|
|
379
|
+
runtime.logs.push(`Making HTTP ${config.method || 'POST'} to ${config.endpoint}...`);
|
|
380
|
+
const response = await fetch(config.endpoint, {
|
|
381
|
+
method: config.method || 'POST',
|
|
382
|
+
headers: {
|
|
383
|
+
'Content-Type': 'application/json',
|
|
384
|
+
...config.headers
|
|
385
|
+
},
|
|
386
|
+
body: JSON.stringify(runtime.inputs),
|
|
387
|
+
signal: AbortSignal.timeout(config.timeout ? config.timeout * 1000 : this.getRuntimeTimeoutMs(skill, 30_000))
|
|
388
|
+
});
|
|
389
|
+
if (!response.ok) {
|
|
390
|
+
throw new Error(`HTTP error: ${response.status}`);
|
|
391
|
+
}
|
|
392
|
+
runtime.outputs = await response.json();
|
|
393
|
+
}
|
|
394
|
+
async executeDocker(skill, runtime) {
|
|
395
|
+
const config = skill.runtime.docker;
|
|
396
|
+
runtime.logs.push(`Running Docker container ${config.image}...`);
|
|
397
|
+
const args = ['run', '--rm'];
|
|
398
|
+
for (const volume of config.volumes || []) {
|
|
399
|
+
args.push('-v', volume);
|
|
400
|
+
}
|
|
401
|
+
for (const [key, value] of Object.entries(config.env || {})) {
|
|
402
|
+
args.push('-e', `${key}=${value}`);
|
|
403
|
+
}
|
|
404
|
+
args.push(config.image);
|
|
405
|
+
if (config.command) {
|
|
406
|
+
args.push('sh', '-lc', config.command);
|
|
407
|
+
}
|
|
408
|
+
const execution = await this.runCommand('docker', args, skill.security.maxExecutionTime || 60_000);
|
|
409
|
+
runtime.outputs = {
|
|
410
|
+
result: execution.stdout.trim(),
|
|
411
|
+
stderr: execution.stderr.trim(),
|
|
412
|
+
code: execution.code,
|
|
413
|
+
image: config.image
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
async executeLocal(skill, runtime) {
|
|
417
|
+
runtime.logs.push('Executing local skill...');
|
|
418
|
+
// Executar ações definidas na skill
|
|
419
|
+
for (const action of skill.actions || []) {
|
|
420
|
+
runtime.logs.push(`Executing action: ${action.name}`);
|
|
421
|
+
if (action.type !== 'execute')
|
|
422
|
+
continue;
|
|
423
|
+
const command = action.config.command;
|
|
424
|
+
if (!command)
|
|
425
|
+
continue;
|
|
426
|
+
const execution = await this.runCommand('sh', ['-lc', command], skill.security.maxExecutionTime || 60_000);
|
|
427
|
+
runtime.outputs[action.name] = {
|
|
428
|
+
stdout: execution.stdout.trim(),
|
|
429
|
+
stderr: execution.stderr.trim(),
|
|
430
|
+
code: execution.code
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
runtime.outputs = {
|
|
434
|
+
...runtime.outputs,
|
|
435
|
+
result: 'Local execution completed',
|
|
436
|
+
inputs: runtime.inputs
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
async executeActions(skill, runtime, context) {
|
|
440
|
+
for (const action of skill.actions || []) {
|
|
441
|
+
// Verificar condição
|
|
442
|
+
if (action.condition && !this.evaluateCondition(action.condition, runtime)) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
switch (action.type) {
|
|
446
|
+
case 'summon':
|
|
447
|
+
await this.executeSummonAction(skill, action, runtime, context);
|
|
448
|
+
break;
|
|
449
|
+
case 'message':
|
|
450
|
+
await this.executeMessageAction(skill, action, runtime, context);
|
|
451
|
+
break;
|
|
452
|
+
case 'notify':
|
|
453
|
+
runtime.logs.push(`Notification: ${action.config.message}`);
|
|
454
|
+
break;
|
|
455
|
+
case 'export':
|
|
456
|
+
runtime.logs.push(`Exporting to ${action.config.format}`);
|
|
457
|
+
break;
|
|
458
|
+
case 'compose':
|
|
459
|
+
await this.executeComposedSkills(skill, runtime, context);
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async executeComposedSkills(parentSkill, runtime, context) {
|
|
465
|
+
if (!parentSkill.compose)
|
|
466
|
+
return;
|
|
467
|
+
const { skills, sequence } = parentSkill.compose;
|
|
468
|
+
if (sequence === 'parallel') {
|
|
469
|
+
// Executar todas em paralelo
|
|
470
|
+
await Promise.all(skills.map(id => this.executeSkill(id, runtime.inputs, context)));
|
|
471
|
+
}
|
|
472
|
+
else if (sequence === 'sequential') {
|
|
473
|
+
// Executar em sequência
|
|
474
|
+
let composedInputs = { ...runtime.inputs };
|
|
475
|
+
for (const id of skills) {
|
|
476
|
+
const subRuntime = await this.executeSkill(id, composedInputs, context);
|
|
477
|
+
runtime.outputs[`compose:${id}`] = subRuntime.outputs;
|
|
478
|
+
// Mapear outputs para inputs da próxima
|
|
479
|
+
if (parentSkill.compose?.mapping) {
|
|
480
|
+
composedInputs = {
|
|
481
|
+
...composedInputs,
|
|
482
|
+
...this.applyComposeMapping(parentSkill.compose.mapping, id, subRuntime.outputs)
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// ─── Skill Composition ───────────────────────────────────────
|
|
489
|
+
composeSkills(name, skillIds, options) {
|
|
490
|
+
const composed = {
|
|
491
|
+
skill: {
|
|
492
|
+
id: `composed_${ulid()}`,
|
|
493
|
+
name,
|
|
494
|
+
version: '1.0.0',
|
|
495
|
+
description: `Composed skill: ${skillIds.join(' + ')}`
|
|
496
|
+
},
|
|
497
|
+
runtime: {
|
|
498
|
+
type: 'local'
|
|
499
|
+
},
|
|
500
|
+
triggers: [],
|
|
501
|
+
capabilities: {
|
|
502
|
+
inputs: [],
|
|
503
|
+
outputs: []
|
|
504
|
+
},
|
|
505
|
+
actions: [],
|
|
506
|
+
compose: {
|
|
507
|
+
skills: skillIds,
|
|
508
|
+
sequence: options.sequence || 'sequential',
|
|
509
|
+
mapping: options.mapping
|
|
510
|
+
},
|
|
511
|
+
society: {},
|
|
512
|
+
security: {
|
|
513
|
+
sandbox: 'light',
|
|
514
|
+
permissions: ['compose:execute']
|
|
515
|
+
},
|
|
516
|
+
meta: {
|
|
517
|
+
created: new Date().toISOString(),
|
|
518
|
+
updated: new Date().toISOString(),
|
|
519
|
+
version: 1
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
// Agregar capabilities das skills componentes
|
|
523
|
+
for (const id of skillIds) {
|
|
524
|
+
const skill = this.skills.get(id);
|
|
525
|
+
if (skill) {
|
|
526
|
+
composed.capabilities.inputs.push(...skill.capabilities.inputs);
|
|
527
|
+
composed.capabilities.outputs.push(...skill.capabilities.outputs);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
this.skills.set(composed.skill.id, composed);
|
|
531
|
+
return composed;
|
|
532
|
+
}
|
|
533
|
+
// ─── Helpers ─────────────────────────────────────────────────
|
|
534
|
+
normalizeTrigger(trigger) {
|
|
535
|
+
const rawType = String(trigger?.type || 'manual');
|
|
536
|
+
const type = rawType === 'schedule'
|
|
537
|
+
? 'cron'
|
|
538
|
+
: (['webhook', 'cron', 'event', 'file', 'manual', 'api', 'mention'].includes(rawType)
|
|
539
|
+
? rawType
|
|
540
|
+
: 'manual');
|
|
541
|
+
const config = trigger?.config && typeof trigger.config === 'object'
|
|
542
|
+
? trigger.config
|
|
543
|
+
: {
|
|
544
|
+
source: trigger?.source,
|
|
545
|
+
event: trigger?.event,
|
|
546
|
+
cron: trigger?.cron,
|
|
547
|
+
condition: trigger?.condition
|
|
548
|
+
};
|
|
549
|
+
return { type: type, config };
|
|
550
|
+
}
|
|
551
|
+
normalizeInputCapability(input) {
|
|
552
|
+
const mappedType = this.mapExternalTypeToInputType(input?.type);
|
|
553
|
+
return {
|
|
554
|
+
name: String(input?.name || 'input'),
|
|
555
|
+
type: mappedType,
|
|
556
|
+
description: input?.description,
|
|
557
|
+
required: input?.required ?? false,
|
|
558
|
+
default: input?.default,
|
|
559
|
+
validation: input?.validation
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
normalizeOutputCapability(output) {
|
|
563
|
+
const mappedType = this.mapExternalTypeToOutputType(output?.type);
|
|
564
|
+
return {
|
|
565
|
+
name: String(output?.name || 'output'),
|
|
566
|
+
type: mappedType,
|
|
567
|
+
description: output?.description
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
mapExternalTypeToInputType(type) {
|
|
571
|
+
switch (type) {
|
|
572
|
+
case 'number':
|
|
573
|
+
case 'boolean':
|
|
574
|
+
case 'array':
|
|
575
|
+
case 'object':
|
|
576
|
+
case 'file':
|
|
577
|
+
case 'string':
|
|
578
|
+
return type;
|
|
579
|
+
case 'json':
|
|
580
|
+
return 'object';
|
|
581
|
+
case 'url':
|
|
582
|
+
case 'markdown':
|
|
583
|
+
default:
|
|
584
|
+
return 'string';
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
mapExternalTypeToOutputType(type) {
|
|
588
|
+
switch (type) {
|
|
589
|
+
case 'number':
|
|
590
|
+
case 'boolean':
|
|
591
|
+
case 'array':
|
|
592
|
+
case 'object':
|
|
593
|
+
case 'file':
|
|
594
|
+
case 'string':
|
|
595
|
+
return type;
|
|
596
|
+
case 'json':
|
|
597
|
+
return 'object';
|
|
598
|
+
case 'artifact':
|
|
599
|
+
return 'file';
|
|
600
|
+
case 'html':
|
|
601
|
+
case 'markdown':
|
|
602
|
+
default:
|
|
603
|
+
return 'string';
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
validateInputs(skill, inputs) {
|
|
607
|
+
for (const input of skill.capabilities.inputs) {
|
|
608
|
+
if (input.required && !(input.name in inputs)) {
|
|
609
|
+
throw new SkillExecutionError('validation', `Required input missing: ${input.name}`, skill.runtime.type);
|
|
610
|
+
}
|
|
611
|
+
if (input.name in inputs && input.validation) {
|
|
612
|
+
const value = inputs[input.name];
|
|
613
|
+
if (input.validation.pattern) {
|
|
614
|
+
const regex = new RegExp(input.validation.pattern);
|
|
615
|
+
if (!regex.test(String(value))) {
|
|
616
|
+
throw new SkillExecutionError('validation', `Input ${input.name} does not match pattern`, skill.runtime.type);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (input.validation.min !== undefined && value < input.validation.min) {
|
|
620
|
+
throw new SkillExecutionError('validation', `Input ${input.name} below minimum`, skill.runtime.type);
|
|
621
|
+
}
|
|
622
|
+
if (input.validation.max !== undefined && value > input.validation.max) {
|
|
623
|
+
throw new SkillExecutionError('validation', `Input ${input.name} above maximum`, skill.runtime.type);
|
|
624
|
+
}
|
|
625
|
+
if (input.validation.enum && !input.validation.enum.includes(value)) {
|
|
626
|
+
throw new SkillExecutionError('validation', `Input ${input.name} not in allowed values`, skill.runtime.type);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
evaluateCondition(condition, runtime) {
|
|
632
|
+
// Simplificado - usar engine de expressões real em produção
|
|
633
|
+
try {
|
|
634
|
+
// Substituir variáveis
|
|
635
|
+
const expr = condition
|
|
636
|
+
.replace(/inputs\.(\w+)/g, (_, key) => JSON.stringify(runtime.inputs[key]))
|
|
637
|
+
.replace(/outputs\.(\w+)/g, (_, key) => JSON.stringify(runtime.outputs[key]));
|
|
638
|
+
// Avaliar (cuidado com segurança em produção!)
|
|
639
|
+
return eval(expr);
|
|
640
|
+
}
|
|
641
|
+
catch {
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
buildOpenClawInstructions(skill, inputs) {
|
|
646
|
+
return `
|
|
647
|
+
# Task: ${skill.skill.name}
|
|
648
|
+
|
|
649
|
+
## Inputs
|
|
650
|
+
${Object.entries(inputs).map(([k, v]) => `- ${k}: ${JSON.stringify(v)}`).join('\n')}
|
|
651
|
+
|
|
652
|
+
## Instructions
|
|
653
|
+
${skill.actions.map(a => `- ${a.name}: ${a.description}`).join('\n')}
|
|
654
|
+
`.trim();
|
|
655
|
+
}
|
|
656
|
+
applyComposeMapping(mapping, skillId, outputs) {
|
|
657
|
+
const mapped = {};
|
|
658
|
+
for (const [target, source] of Object.entries(mapping)) {
|
|
659
|
+
const [sourceSkill, sourceKey] = source.split('.');
|
|
660
|
+
if (sourceSkill && sourceKey) {
|
|
661
|
+
if (sourceSkill === skillId && sourceKey in outputs) {
|
|
662
|
+
mapped[target] = outputs[sourceKey];
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
else if (source in outputs) {
|
|
666
|
+
mapped[target] = outputs[source];
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return mapped;
|
|
670
|
+
}
|
|
671
|
+
async executeSummonAction(skill, action, runtime, context) {
|
|
672
|
+
const roomId = action.config.room || context?.room || skill.society.room;
|
|
673
|
+
if (!roomId) {
|
|
674
|
+
throw new Error('Summon action requires a room');
|
|
675
|
+
}
|
|
676
|
+
this.storage.createRoom(roomId, roomId, this.identity.did);
|
|
677
|
+
const chainId = `skill_coc_${ulid()}`;
|
|
678
|
+
const goal = action.config.goal || runtime.inputs.goal || skill.skill.description;
|
|
679
|
+
this.storage.createChain(chainId, roomId, String(goal), skill.society.template || null, this.identity.did, 'normal');
|
|
680
|
+
runtime.logs.push(`Summoned chain ${chainId}`);
|
|
681
|
+
runtime.outputs.summonedChainId = chainId;
|
|
682
|
+
}
|
|
683
|
+
async executeMessageAction(skill, action, runtime, context) {
|
|
684
|
+
const roomId = action.config.room || context?.room || skill.society.room;
|
|
685
|
+
if (!roomId) {
|
|
686
|
+
throw new Error('Message action requires a room');
|
|
687
|
+
}
|
|
688
|
+
this.storage.createRoom(roomId, roomId, this.identity.did);
|
|
689
|
+
const text = String(action.config.message || runtime.outputs.result || runtime.inputs.message || '');
|
|
690
|
+
const messageId = `msg_${ulid()}`;
|
|
691
|
+
this.storage.saveMessage(messageId, roomId, this.identity.did, this.identity.displayName, text, null, Date.now());
|
|
692
|
+
runtime.logs.push(`Stored message ${messageId} in room ${roomId}`);
|
|
693
|
+
runtime.outputs.messageId = messageId;
|
|
694
|
+
}
|
|
695
|
+
runCommand(command, args, timeoutMs) {
|
|
696
|
+
return new Promise((resolve, reject) => {
|
|
697
|
+
const child = spawn(command, args, {
|
|
698
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
699
|
+
});
|
|
700
|
+
let stdout = '';
|
|
701
|
+
let stderr = '';
|
|
702
|
+
let timedOut = false;
|
|
703
|
+
const timer = setTimeout(() => {
|
|
704
|
+
timedOut = true;
|
|
705
|
+
child.kill('SIGTERM');
|
|
706
|
+
}, timeoutMs);
|
|
707
|
+
child.stdout.on('data', (chunk) => {
|
|
708
|
+
stdout += chunk.toString();
|
|
709
|
+
});
|
|
710
|
+
child.stderr.on('data', (chunk) => {
|
|
711
|
+
stderr += chunk.toString();
|
|
712
|
+
});
|
|
713
|
+
child.on('error', (error) => {
|
|
714
|
+
clearTimeout(timer);
|
|
715
|
+
reject(error);
|
|
716
|
+
});
|
|
717
|
+
child.on('close', (code) => {
|
|
718
|
+
clearTimeout(timer);
|
|
719
|
+
if (timedOut) {
|
|
720
|
+
reject(new Error(`Command timed out after ${timeoutMs}ms: ${command}`));
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (code !== 0) {
|
|
724
|
+
reject(new Error(`Command failed (${command}): ${stderr.trim() || `exit ${code}`}`));
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
resolve({ stdout, stderr, code: code || 0 });
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
setupWatchers() {
|
|
732
|
+
if (!existsSync(this.skillDir))
|
|
733
|
+
return;
|
|
734
|
+
const watcher = watch(this.skillDir, { recursive: true }, (event, filename) => {
|
|
735
|
+
if (filename?.endsWith('.md')) {
|
|
736
|
+
console.log(`Skill file changed: ${filename}`);
|
|
737
|
+
// Recarregar skill
|
|
738
|
+
const fullPath = join(this.skillDir, filename);
|
|
739
|
+
if (existsSync(fullPath)) {
|
|
740
|
+
if (filename.includes('claw')) {
|
|
741
|
+
this.loadClawSkillFromFile(fullPath);
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
this.loadSkillFromFile(fullPath);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
this.watchers.push(watcher);
|
|
750
|
+
}
|
|
751
|
+
generateSkillId(path) {
|
|
752
|
+
const basename = path.split('/').pop()?.replace(/\.md$/, '') || 'skill';
|
|
753
|
+
return `skill_${basename}_${ulid().slice(0, 8)}`;
|
|
754
|
+
}
|
|
755
|
+
// ─── Public API ──────────────────────────────────────────────
|
|
756
|
+
getSkill(id) {
|
|
757
|
+
return this.skills.get(id);
|
|
758
|
+
}
|
|
759
|
+
getClawSkill(id) {
|
|
760
|
+
return this.clawSkills.get(id);
|
|
761
|
+
}
|
|
762
|
+
listSkills() {
|
|
763
|
+
return Array.from(this.skills.values());
|
|
764
|
+
}
|
|
765
|
+
listClawSkills() {
|
|
766
|
+
return Array.from(this.clawSkills.entries()).map(([id, skill]) => ({ id, skill }));
|
|
767
|
+
}
|
|
768
|
+
searchSkills(query) {
|
|
769
|
+
const q = query.toLowerCase();
|
|
770
|
+
return this.listSkills().filter(s => s.skill.name.toLowerCase().includes(q) ||
|
|
771
|
+
s.skill.description.toLowerCase().includes(q) ||
|
|
772
|
+
s.skill.tags?.some(t => t.toLowerCase().includes(q)));
|
|
773
|
+
}
|
|
774
|
+
getRuntime(id) {
|
|
775
|
+
return this.runtimes.get(id);
|
|
776
|
+
}
|
|
777
|
+
listRuntimes() {
|
|
778
|
+
return Array.from(this.runtimes.values());
|
|
779
|
+
}
|
|
780
|
+
stop() {
|
|
781
|
+
for (const watcher of this.watchers) {
|
|
782
|
+
watcher.close();
|
|
783
|
+
}
|
|
784
|
+
this.watchers = [];
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
// Classes already exported via 'export class'
|
|
788
|
+
//# sourceMappingURL=engine.js.map
|