natureco-cli 2.23.28 → 2.23.30
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/README.md +94 -11
- package/bin/natureco.js +470 -10
- package/package.json +10 -6
- package/src/commands/admin-rpc.js +219 -0
- package/src/commands/agent.js +89 -0
- package/src/commands/approvals.js +53 -0
- package/src/commands/backup.js +124 -0
- package/src/commands/bonjour.js +167 -0
- package/src/commands/capability.js +64 -0
- package/src/commands/channels.js +94 -4
- package/src/commands/chat.js +11 -25
- package/src/commands/clickclack.js +130 -0
- package/src/commands/commitments.js +32 -0
- package/src/commands/completion.js +76 -0
- package/src/commands/config.js +111 -68
- package/src/commands/configure.js +93 -0
- package/src/commands/crestodian.js +92 -0
- package/src/commands/daemon.js +60 -0
- package/src/commands/device-pair.js +248 -0
- package/src/commands/devices.js +110 -0
- package/src/commands/directory.js +47 -0
- package/src/commands/dns.js +58 -0
- package/src/commands/docs.js +43 -0
- package/src/commands/doctor.js +121 -16
- package/src/commands/exec-policy.js +71 -0
- package/src/commands/gateway-server.js +1175 -30
- package/src/commands/gateway.js +11 -20
- package/src/commands/health.js +18 -0
- package/src/commands/help.js +6 -0
- package/src/commands/imessage.js +169 -0
- package/src/commands/infer.js +73 -0
- package/src/commands/irc.js +119 -0
- package/src/commands/mattermost.js +164 -0
- package/src/commands/memory-cmd.js +134 -1
- package/src/commands/message.js +30 -4
- package/src/commands/migrate.js +213 -2
- package/src/commands/models.js +584 -216
- package/src/commands/node.js +98 -0
- package/src/commands/nodes.js +106 -0
- package/src/commands/oc-path.js +200 -0
- package/src/commands/onboard.js +70 -0
- package/src/commands/open-prose.js +67 -0
- package/src/commands/plugins.js +415 -172
- package/src/commands/policy.js +176 -0
- package/src/commands/proxy.js +155 -0
- package/src/commands/qr.js +28 -0
- package/src/commands/sandbox.js +125 -0
- package/src/commands/secrets.js +118 -0
- package/src/commands/security.js +149 -1
- package/src/commands/setup.js +114 -10
- package/src/commands/signal.js +495 -0
- package/src/commands/skills.js +20 -29
- package/src/commands/sms.js +168 -0
- package/src/commands/system.js +53 -0
- package/src/commands/tasks.js +328 -79
- package/src/commands/terminal.js +21 -0
- package/src/commands/thread-ownership.js +157 -0
- package/src/commands/transcripts.js +72 -0
- package/src/commands/voice.js +82 -0
- package/src/commands/vydra.js +98 -0
- package/src/commands/webhooks.js +79 -0
- package/src/commands/whatsapp.js +7 -21
- package/src/commands/workboard.js +207 -0
- package/src/tools/audio_understanding.js +154 -0
- package/src/tools/bash.js +63 -29
- package/src/tools/browser.js +112 -0
- package/src/tools/canvas.js +104 -0
- package/src/tools/document_extract.js +84 -0
- package/src/tools/duckduckgo.js +54 -0
- package/src/tools/exa_search.js +66 -0
- package/src/tools/firecrawl.js +104 -0
- package/src/tools/image_generation.js +99 -0
- package/src/tools/llm_task.js +118 -0
- package/src/tools/media_understanding.js +128 -0
- package/src/tools/music_generation.js +113 -0
- package/src/tools/parallel_search.js +77 -0
- package/src/tools/phone_control.js +80 -0
- package/src/tools/phone_control_enhanced.js +184 -0
- package/src/tools/searxng.js +61 -0
- package/src/tools/speech_to_text.js +135 -0
- package/src/tools/text_to_speech.js +105 -0
- package/src/tools/thread_ownership.js +88 -0
- package/src/tools/video_generation.js +72 -0
- package/src/tools/web_readability.js +104 -0
- package/src/utils/api.js +3 -20
- package/src/utils/approvals.js +297 -0
- package/src/utils/background.js +223 -66
- package/src/utils/baileys.js +21 -0
- package/src/utils/config.js +141 -10
- package/src/utils/errors.js +148 -0
- package/src/utils/inquirer-wrapper.js +1 -2
- package/src/utils/memory.js +200 -0
- package/src/utils/path-utils.js +13 -13
- package/src/utils/plugin-registry.js +238 -0
- package/src/utils/secrets.js +177 -0
- package/src/utils/skills.js +10 -23
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
const { PluginError, handleError } = require('./errors');
|
|
6
|
+
|
|
7
|
+
const PLUGINS_DIR = path.join(os.homedir(), '.natureco', 'plugins');
|
|
8
|
+
const REGISTRY_FILE = path.join(os.homedir(), '.natureco', 'plugin-registry.json');
|
|
9
|
+
|
|
10
|
+
function ensureDir(dir) {
|
|
11
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function loadRegistry() {
|
|
15
|
+
if (!fs.existsSync(REGISTRY_FILE)) return { version: 1, plugins: [], updatedAt: null };
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(fs.readFileSync(REGISTRY_FILE, 'utf-8'));
|
|
18
|
+
} catch {
|
|
19
|
+
return { version: 1, plugins: [], updatedAt: null };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function saveRegistry(registry) {
|
|
24
|
+
ensureDir(path.dirname(REGISTRY_FILE));
|
|
25
|
+
registry.updatedAt = new Date().toISOString();
|
|
26
|
+
fs.writeFileSync(REGISTRY_FILE, JSON.stringify(registry, null, 2), 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function scanInstalled() {
|
|
30
|
+
ensureDir(PLUGINS_DIR);
|
|
31
|
+
return fs.readdirSync(PLUGINS_DIR, { withFileTypes: true })
|
|
32
|
+
.filter(d => d.isDirectory())
|
|
33
|
+
.map(d => {
|
|
34
|
+
const manifestFile = path.join(PLUGINS_DIR, d.name, 'plugin.json');
|
|
35
|
+
const pkgFile = path.join(PLUGINS_DIR, d.name, 'package.json');
|
|
36
|
+
const manifest = readManifest(manifestFile, pkgFile, d.name);
|
|
37
|
+
const disabledFile = path.join(PLUGINS_DIR, d.name, '.disabled');
|
|
38
|
+
return { ...manifest, slug: d.name, enabled: !fs.existsSync(disabledFile), installPath: path.join(PLUGINS_DIR, d.name) };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readManifest(manifestFile, pkgFile, fallbackName) {
|
|
43
|
+
let meta = { name: fallbackName, description: '', version: '1.0.0', author: '', license: 'MIT', keywords: [], entry: 'index.js' };
|
|
44
|
+
if (fs.existsSync(pkgFile)) {
|
|
45
|
+
try {
|
|
46
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
47
|
+
meta = { ...meta, name: pkg.name || meta.name, description: pkg.description || meta.description, version: pkg.version || meta.version, author: pkg.author || meta.author, license: pkg.license || meta.license, keywords: pkg.keywords || meta.keywords, entry: pkg.main || meta.entry, dependencies: pkg.dependencies, openclaw: pkg.openclaw };
|
|
48
|
+
} catch {}
|
|
49
|
+
}
|
|
50
|
+
if (fs.existsSync(manifestFile)) {
|
|
51
|
+
try {
|
|
52
|
+
const m = JSON.parse(fs.readFileSync(manifestFile, 'utf-8'));
|
|
53
|
+
meta = { ...meta, ...m };
|
|
54
|
+
} catch {}
|
|
55
|
+
}
|
|
56
|
+
return meta;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function validateManifest(manifest) {
|
|
60
|
+
const errors = [];
|
|
61
|
+
if (!manifest.name) errors.push('Plugin adı (name) gerekli');
|
|
62
|
+
if (!manifest.entry && !manifest.openclaw?.tool) errors.push('Entry noktası (entry/index.js) gerekli');
|
|
63
|
+
if (manifest.version && !/^\d+\.\d+\.\d+/.test(manifest.version)) errors.push('Version semver formatında olmalı (x.y.z)');
|
|
64
|
+
return errors;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getInstalledIds() {
|
|
68
|
+
const registry = loadRegistry();
|
|
69
|
+
const installed = new Set(registry.plugins.map(p => p.id));
|
|
70
|
+
scanInstalled().forEach(p => installed.add(p.slug));
|
|
71
|
+
return [...installed];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getPlugin(slug) {
|
|
75
|
+
return scanInstalled().find(p => p.slug === slug) || null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function installPlugin(spec) {
|
|
79
|
+
ensureDir(PLUGINS_DIR);
|
|
80
|
+
|
|
81
|
+
if (spec.startsWith('./') || spec.startsWith('/') || spec.startsWith('.\\') || spec.includes(':\\') || spec.startsWith('\\\\')) {
|
|
82
|
+
return installLocal(spec);
|
|
83
|
+
}
|
|
84
|
+
if (spec.startsWith('clawhub:') || spec.startsWith('naturehub:')) {
|
|
85
|
+
return installFromHub(spec);
|
|
86
|
+
}
|
|
87
|
+
if (spec.startsWith('npm:')) {
|
|
88
|
+
return installFromNpm(spec.slice(4));
|
|
89
|
+
}
|
|
90
|
+
if (spec.startsWith('git:')) {
|
|
91
|
+
return installFromGit(spec.slice(4));
|
|
92
|
+
}
|
|
93
|
+
if (spec.includes('/') && !spec.startsWith('@')) {
|
|
94
|
+
return installFromNpm(spec);
|
|
95
|
+
}
|
|
96
|
+
return installFromNpm(spec);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function installLocal(spec) {
|
|
100
|
+
const src = path.resolve(spec);
|
|
101
|
+
if (!fs.existsSync(src)) throw new PluginError(`Yol bulunamadı: ${src}`, 'install', spec);
|
|
102
|
+
const slug = path.basename(src);
|
|
103
|
+
const dest = path.join(PLUGINS_DIR, slug);
|
|
104
|
+
if (fs.existsSync(dest)) {
|
|
105
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
108
|
+
const manifest = readManifest(path.join(dest, 'plugin.json'), path.join(dest, 'package.json'), slug);
|
|
109
|
+
const errors = validateManifest(manifest);
|
|
110
|
+
if (errors.length > 0) {
|
|
111
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
112
|
+
throw new PluginError(`Geçersiz manifest: ${errors.join(', ')}`, 'install', spec);
|
|
113
|
+
}
|
|
114
|
+
const registry = loadRegistry();
|
|
115
|
+
registry.plugins = registry.plugins.filter(p => p.id !== slug);
|
|
116
|
+
registry.plugins.push({ id: slug, name: manifest.name, version: manifest.version, source: 'local', spec, installedAt: new Date().toISOString() });
|
|
117
|
+
saveRegistry(registry);
|
|
118
|
+
return { slug, name: manifest.name, version: manifest.version, source: 'local' };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function installFromNpm(pkg) {
|
|
122
|
+
const tmpDir = path.join(os.tmpdir(), `nc-plugin-${Date.now()}`);
|
|
123
|
+
ensureDir(tmpDir);
|
|
124
|
+
try {
|
|
125
|
+
execSync(`npm install ${pkg} --prefix "${tmpDir}" --no-save --ignore-scripts --no-audit --no-fund`, { stdio: 'pipe', timeout: 120000 });
|
|
126
|
+
const pkgDir = path.join(tmpDir, 'node_modules', pkg.split('/').pop());
|
|
127
|
+
const scopedPkgDir = pkg.startsWith('@') ? path.join(tmpDir, 'node_modules', pkg) : null;
|
|
128
|
+
const srcDir = scopedPkgDir && fs.existsSync(scopedPkgDir) ? scopedPkgDir : (fs.existsSync(pkgDir) ? pkgDir : null);
|
|
129
|
+
if (!srcDir) throw new PluginError(`Paket bulunamadı: ${pkg}`, 'install', pkg);
|
|
130
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.join(srcDir, 'package.json'), 'utf-8'));
|
|
131
|
+
const slug = pkgJson.name?.replace(/@/g, '').replace(/\//g, '-') || pkg.replace(/[@\/]/g, '-').replace(/^-/, '');
|
|
132
|
+
const dest = path.join(PLUGINS_DIR, slug);
|
|
133
|
+
if (fs.existsSync(dest)) fs.rmSync(dest, { recursive: true, force: true });
|
|
134
|
+
fs.cpSync(srcDir, dest, { recursive: true });
|
|
135
|
+
const manifest = readManifest(path.join(dest, 'plugin.json'), path.join(dest, 'package.json'), slug);
|
|
136
|
+
const errors = validateManifest(manifest);
|
|
137
|
+
if (errors.length > 0) {
|
|
138
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
139
|
+
throw new PluginError(`Geçersiz manifest: ${errors.join(', ')}`, 'install', pkg);
|
|
140
|
+
}
|
|
141
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
142
|
+
const registry = loadRegistry();
|
|
143
|
+
registry.plugins = registry.plugins.filter(p => p.id !== slug);
|
|
144
|
+
registry.plugins.push({ id: slug, name: manifest.name, version: manifest.version, source: 'npm', spec: pkg, installedAt: new Date().toISOString() });
|
|
145
|
+
saveRegistry(registry);
|
|
146
|
+
return { slug, name: manifest.name, version: manifest.version, source: 'npm' };
|
|
147
|
+
} catch (err) {
|
|
148
|
+
if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function installFromGit(spec) {
|
|
154
|
+
const repoUrl = spec.startsWith('github.com/') ? `https://${spec}` : spec.startsWith('http') ? spec : spec.includes('/') ? `https://github.com/${spec}` : spec;
|
|
155
|
+
const tmpDir = path.join(os.tmpdir(), `nc-plugin-git-${Date.now()}`);
|
|
156
|
+
ensureDir(tmpDir);
|
|
157
|
+
try {
|
|
158
|
+
execSync(`git clone --depth 1 "${repoUrl}" "${tmpDir}"`, { stdio: 'pipe', timeout: 60000 });
|
|
159
|
+
const slug = spec.split('/').pop().replace(/\.git$/, '');
|
|
160
|
+
const dest = path.join(PLUGINS_DIR, slug);
|
|
161
|
+
if (fs.existsSync(dest)) fs.rmSync(dest, { recursive: true, force: true });
|
|
162
|
+
const items = fs.readdirSync(tmpDir);
|
|
163
|
+
const subDir = items.length === 1 && fs.statSync(path.join(tmpDir, items[0])).isDirectory() ? path.join(tmpDir, items[0]) : tmpDir;
|
|
164
|
+
fs.cpSync(subDir, dest, { recursive: true });
|
|
165
|
+
const manifest = readManifest(path.join(dest, 'plugin.json'), path.join(dest, 'package.json'), slug);
|
|
166
|
+
const errors = validateManifest(manifest);
|
|
167
|
+
if (errors.length > 0) {
|
|
168
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
169
|
+
throw new PluginError(`Geçersiz manifest: ${errors.join(', ')}`, 'install', spec);
|
|
170
|
+
}
|
|
171
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
172
|
+
const registry = loadRegistry();
|
|
173
|
+
registry.plugins = registry.plugins.filter(p => p.id !== slug);
|
|
174
|
+
registry.plugins.push({ id: slug, name: manifest.name, version: manifest.version, source: 'git', spec: repoUrl, installedAt: new Date().toISOString() });
|
|
175
|
+
saveRegistry(registry);
|
|
176
|
+
return { slug, name: manifest.name, version: manifest.version, source: 'git' };
|
|
177
|
+
} catch (err) {
|
|
178
|
+
if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function installFromHub(spec) {
|
|
184
|
+
const pkg = spec.replace(/^(clawhub|naturehub):\/?/, '');
|
|
185
|
+
return installFromNpm(pkg);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function uninstallPlugin(slug, options = {}) {
|
|
189
|
+
const plugin = getPlugin(slug);
|
|
190
|
+
if (!plugin) throw new PluginError(`Plugin bulunamadı: ${slug}`, 'uninstall', slug);
|
|
191
|
+
if (!options.keepFiles) {
|
|
192
|
+
fs.rmSync(plugin.installPath, { recursive: true, force: true });
|
|
193
|
+
}
|
|
194
|
+
const registry = loadRegistry();
|
|
195
|
+
registry.plugins = registry.plugins.filter(p => p.id !== slug);
|
|
196
|
+
saveRegistry(registry);
|
|
197
|
+
return { slug, name: plugin.name };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function updatePlugin(slug) {
|
|
201
|
+
const plugin = getPlugin(slug);
|
|
202
|
+
if (!plugin) throw new PluginError(`Plugin bulunamadı: ${slug}`, 'update', slug);
|
|
203
|
+
const registry = loadRegistry();
|
|
204
|
+
const record = registry.plugins.find(p => p.id === slug);
|
|
205
|
+
if (!record || !record.spec) throw new PluginError(`Plugin kaydı bulunamadı veya spec eksik: ${slug}`, 'update', slug);
|
|
206
|
+
if (record.source === 'npm') {
|
|
207
|
+
fs.rmSync(plugin.installPath, { recursive: true, force: true });
|
|
208
|
+
return installFromNpm(record.spec);
|
|
209
|
+
}
|
|
210
|
+
if (record.source === 'git' && record.spec) {
|
|
211
|
+
fs.rmSync(plugin.installPath, { recursive: true, force: true });
|
|
212
|
+
return installFromGit(record.spec);
|
|
213
|
+
}
|
|
214
|
+
throw new PluginError(`${record.source} kaynağından güncelleme desteklenmiyor`, 'update', slug);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function searchRegistry(query) {
|
|
218
|
+
const registry = loadRegistry();
|
|
219
|
+
const q = query.toLowerCase();
|
|
220
|
+
return registry.plugins.filter(p =>
|
|
221
|
+
p.id.toLowerCase().includes(q) ||
|
|
222
|
+
(p.name || '').toLowerCase().includes(q)
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
module.exports = {
|
|
227
|
+
PLUGINS_DIR,
|
|
228
|
+
loadRegistry,
|
|
229
|
+
saveRegistry,
|
|
230
|
+
scanInstalled,
|
|
231
|
+
getPlugin,
|
|
232
|
+
getInstalledIds,
|
|
233
|
+
installPlugin,
|
|
234
|
+
uninstallPlugin,
|
|
235
|
+
updatePlugin,
|
|
236
|
+
searchRegistry,
|
|
237
|
+
validateManifest,
|
|
238
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { NatureCoError } = require('./errors');
|
|
5
|
+
|
|
6
|
+
const ENV_SHORTHAND_RE = /^\$\{([A-Z][A-Z0-9_]{0,127})\}$/;
|
|
7
|
+
const ENV_SHORT_RE = /^\$([A-Z][A-Z0-9_]{0,127})$/;
|
|
8
|
+
const SECRET_REF_ENV_PREFIX = 'secretref-env:';
|
|
9
|
+
const SECRET_REF_ENV_LEGACY = '__env__:';
|
|
10
|
+
|
|
11
|
+
class UnresolvedSecretError extends NatureCoError {
|
|
12
|
+
constructor(message, options = {}) {
|
|
13
|
+
super(message, options);
|
|
14
|
+
this.ref = options.ref || null;
|
|
15
|
+
this.path = options.path || null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Cache for resolved secrets
|
|
20
|
+
const _secretCache = new Map();
|
|
21
|
+
|
|
22
|
+
function resolveEnvVar(name) {
|
|
23
|
+
return process.env[name] ?? null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function coerceSecretRef(value) {
|
|
27
|
+
if (!value || typeof value !== 'string') return null;
|
|
28
|
+
|
|
29
|
+
// Structured ref: {"source":"env","provider":"default","id":"MY_KEY"}
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(value);
|
|
32
|
+
if (parsed && typeof parsed === 'object' && parsed.source && parsed.provider && parsed.id) {
|
|
33
|
+
return { source: parsed.source, provider: parsed.provider, id: parsed.id };
|
|
34
|
+
}
|
|
35
|
+
} catch {}
|
|
36
|
+
|
|
37
|
+
// Env shorthand: ${MY_KEY} or $MY_KEY
|
|
38
|
+
let m = value.match(ENV_SHORTHAND_RE);
|
|
39
|
+
if (m) return { source: 'env', provider: 'default', id: m[1] };
|
|
40
|
+
m = value.match(ENV_SHORT_RE);
|
|
41
|
+
if (m) return { source: 'env', provider: 'default', id: m[1] };
|
|
42
|
+
|
|
43
|
+
// Legacy: secretref-env:MY_KEY or __env__:MY_KEY
|
|
44
|
+
if (value.startsWith(SECRET_REF_ENV_PREFIX)) {
|
|
45
|
+
return { source: 'env', provider: 'default', id: value.slice(SECRET_REF_ENV_PREFIX.length) };
|
|
46
|
+
}
|
|
47
|
+
if (value.startsWith(SECRET_REF_ENV_LEGACY)) {
|
|
48
|
+
return { source: 'env', provider: 'default', id: value.slice(SECRET_REF_ENV_LEGACY.length) };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveSecretRef(ref, options = {}) {
|
|
55
|
+
const { cache = true, throwOnMissing = false } = options;
|
|
56
|
+
const cacheKey = `${ref.source}:${ref.provider}:${ref.id}`;
|
|
57
|
+
|
|
58
|
+
if (cache && _secretCache.has(cacheKey)) {
|
|
59
|
+
return _secretCache.get(cacheKey);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let resolved = null;
|
|
63
|
+
|
|
64
|
+
if (ref.source === 'env') {
|
|
65
|
+
resolved = resolveEnvVar(ref.id);
|
|
66
|
+
} else if (ref.source === 'file') {
|
|
67
|
+
try {
|
|
68
|
+
const content = fs.readFileSync(ref.id, 'utf8').trim();
|
|
69
|
+
resolved = content;
|
|
70
|
+
} catch {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (resolved === null && throwOnMissing) {
|
|
74
|
+
throw new UnresolvedSecretError(
|
|
75
|
+
`Secret not found: ${cacheKey}`,
|
|
76
|
+
{ ref, path: cacheKey }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (cache && resolved !== null) {
|
|
81
|
+
_secretCache.set(cacheKey, resolved);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return resolved;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resolveSecretValue(value, options = {}) {
|
|
88
|
+
if (!value || typeof value !== 'string') return value;
|
|
89
|
+
|
|
90
|
+
const ref = coerceSecretRef(value);
|
|
91
|
+
if (ref) {
|
|
92
|
+
const resolved = resolveSecretRef(ref, options);
|
|
93
|
+
return resolved ?? value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function resolveConfigSecrets(config, options = {}) {
|
|
100
|
+
if (!config || typeof config !== 'object') return config;
|
|
101
|
+
|
|
102
|
+
const resolved = Array.isArray(config) ? [] : {};
|
|
103
|
+
for (const [key, value] of Object.entries(config)) {
|
|
104
|
+
if (value && typeof value === 'object') {
|
|
105
|
+
resolved[key] = resolveConfigSecrets(value, options);
|
|
106
|
+
} else if (typeof value === 'string') {
|
|
107
|
+
resolved[key] = resolveSecretValue(value, options);
|
|
108
|
+
} else {
|
|
109
|
+
resolved[key] = value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return resolved;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function loadEnvFile(filePath) {
|
|
116
|
+
const envPath = filePath || path.join(process.cwd(), '.env');
|
|
117
|
+
if (!fs.existsSync(envPath)) return {};
|
|
118
|
+
|
|
119
|
+
const env = {};
|
|
120
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
121
|
+
for (const line of content.split('\n')) {
|
|
122
|
+
const trimmed = line.trim();
|
|
123
|
+
if (!trimmed || trimmed.startsWith('#') || !trimmed.includes('=')) continue;
|
|
124
|
+
const eqIdx = trimmed.indexOf('=');
|
|
125
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
126
|
+
let val = trimmed.slice(eqIdx + 1).trim();
|
|
127
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
128
|
+
val = val.slice(1, -1);
|
|
129
|
+
}
|
|
130
|
+
if (key) env[key] = val;
|
|
131
|
+
}
|
|
132
|
+
return env;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function injectEnvFile(envPath) {
|
|
136
|
+
const env = loadEnvFile(envPath);
|
|
137
|
+
for (const [key, val] of Object.entries(env)) {
|
|
138
|
+
if (!(key in process.env)) {
|
|
139
|
+
process.env[key] = val;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return env;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function clearSecretCache() {
|
|
146
|
+
_secretCache.clear();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function listSecretRefs(config, prefix = '') {
|
|
150
|
+
const refs = [];
|
|
151
|
+
if (!config || typeof config !== 'object') return refs;
|
|
152
|
+
|
|
153
|
+
for (const [key, value] of Object.entries(config)) {
|
|
154
|
+
const fullPath = prefix ? `${prefix}.${key}` : key;
|
|
155
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
156
|
+
refs.push(...listSecretRefs(value, fullPath));
|
|
157
|
+
} else if (typeof value === 'string') {
|
|
158
|
+
const ref = coerceSecretRef(value);
|
|
159
|
+
if (ref) {
|
|
160
|
+
refs.push({ path: fullPath, ref, value });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return refs;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = {
|
|
168
|
+
UnresolvedSecretError,
|
|
169
|
+
coerceSecretRef,
|
|
170
|
+
resolveSecretRef,
|
|
171
|
+
resolveSecretValue,
|
|
172
|
+
resolveConfigSecrets,
|
|
173
|
+
loadEnvFile,
|
|
174
|
+
injectEnvFile,
|
|
175
|
+
clearSecretCache,
|
|
176
|
+
listSecretRefs,
|
|
177
|
+
};
|
package/src/utils/skills.js
CHANGED
|
@@ -299,30 +299,17 @@ Bu skill ${name} işlemlerini yapar.
|
|
|
299
299
|
return path.join(skillDir, 'SKILL.md');
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
//
|
|
303
|
-
const POPULAR_SKILLS = [
|
|
304
|
-
{ slug: 'github', name: 'GitHub', description: 'GitHub repo işlemleri', source: 'clawhub' },
|
|
305
|
-
{ slug: 'filesystem', name: 'Filesystem', description: 'Dosya sistemi işlemleri', source: 'clawhub' },
|
|
306
|
-
{ slug: 'web-search', name: 'Web Search', description: 'Web arama', source: 'clawhub' },
|
|
307
|
-
{ slug: 'summarize', name: 'Summarize', description: 'Metin özetleme', source: 'clawhub' },
|
|
308
|
-
{ slug: 'code-review', name: 'Code Review', description: 'Kod inceleme', source: 'clawhub' },
|
|
309
|
-
{ slug: 'translate', name: 'Translate', description: 'Çeviri', source: 'clawhub' },
|
|
310
|
-
{ slug: 'weather', name: 'Weather', description: 'Hava durumu', source: 'clawhub' },
|
|
311
|
-
{ slug: 'calendar', name: 'Calendar', description: 'Takvim yönetimi', source: 'clawhub' },
|
|
312
|
-
];
|
|
313
|
-
|
|
314
|
-
// Get popular skills from ClawHub API (fallback to hardcoded list)
|
|
302
|
+
// Get popular skills from ClawHub API (no fallback — transparent error)
|
|
315
303
|
async function getPopularSkills() {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
return POPULAR_SKILLS;
|
|
304
|
+
const response = await fetch('https://clawhub.ai/api/skills?q=&limit=20');
|
|
305
|
+
if (!response.ok) {
|
|
306
|
+
throw new Error(`ClawHub API returned HTTP ${response.status}`);
|
|
307
|
+
}
|
|
308
|
+
const data = await response.json();
|
|
309
|
+
if (!data.skills || data.skills.length === 0) {
|
|
310
|
+
throw new Error('ClawHub API returned empty skill list');
|
|
311
|
+
}
|
|
312
|
+
return data.skills.map(s => ({ ...s, source: 'clawhub' }));
|
|
326
313
|
}
|
|
327
314
|
|
|
328
315
|
// Get skill prompt injection content (full SKILL.md content)
|