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
package/src/commands/config.js
CHANGED
|
@@ -1,68 +1,111 @@
|
|
|
1
|
-
const chalk = require('chalk');
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
console.log(
|
|
16
|
-
console.log(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
console.log(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
let parsedValue = value;
|
|
53
|
-
try {
|
|
54
|
-
parsedValue = JSON.parse(value);
|
|
55
|
-
} catch {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { getConfig, setConfigValue, getAllConfig, listBackups, restoreConfig, CONFIG_BACKUP_DIR } = require('../utils/config');
|
|
4
|
+
|
|
5
|
+
function config(args) {
|
|
6
|
+
const [action, key, ...valueParts] = args;
|
|
7
|
+
|
|
8
|
+
if (!action) {
|
|
9
|
+
console.log(chalk.red('\n❌ Kullanım: natureco config <get|set|list|backups|restore> [key] [value]\n'));
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (action === 'list') {
|
|
14
|
+
const cfg = getAllConfig();
|
|
15
|
+
console.log(chalk.green.bold('\n╭─ Config ─╮\n'));
|
|
16
|
+
console.log(JSON.stringify(cfg, null, 2));
|
|
17
|
+
console.log('');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (action === 'get') {
|
|
22
|
+
if (!key) {
|
|
23
|
+
console.log(chalk.red('\n❌ Key belirtilmedi.\n'));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const cfg = getConfig();
|
|
27
|
+
const keys = key.split('.');
|
|
28
|
+
let value = cfg;
|
|
29
|
+
for (const k of keys) {
|
|
30
|
+
value = value?.[k];
|
|
31
|
+
}
|
|
32
|
+
if (value === undefined) {
|
|
33
|
+
console.log(chalk.gray(`\n${key}: (tanımlı değil)\n`));
|
|
34
|
+
} else {
|
|
35
|
+
console.log(chalk.cyan(`\n${key}:`), chalk.white(JSON.stringify(value, null, 2)));
|
|
36
|
+
console.log('');
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (action === 'set') {
|
|
42
|
+
if (!key) {
|
|
43
|
+
console.log(chalk.red('\n❌ Key belirtilmedi.\n'));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
if (valueParts.length === 0) {
|
|
47
|
+
console.log(chalk.red('\n❌ Value belirtilmedi.\n'));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const value = valueParts.join(' ');
|
|
51
|
+
|
|
52
|
+
let parsedValue = value;
|
|
53
|
+
try {
|
|
54
|
+
parsedValue = JSON.parse(value);
|
|
55
|
+
} catch {}
|
|
56
|
+
|
|
57
|
+
setConfigValue(key, parsedValue);
|
|
58
|
+
console.log(chalk.green(`\n✅ ${key} = ${JSON.stringify(parsedValue)}\n`));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (action === 'backups' || action === 'backup') {
|
|
63
|
+
const backups = listBackups();
|
|
64
|
+
console.log(chalk.cyan.bold('\n Config Yedekleri\n'));
|
|
65
|
+
if (backups.length === 0) {
|
|
66
|
+
console.log(chalk.gray(' Henüz yedek alınmamış.\n'));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
backups.forEach((f, i) => {
|
|
70
|
+
const ts = f.replace(/^config-|\.json$/g, '').replace(/T/, ' ').replace(/-/g, ':').replace(/:[^:]*$/, '');
|
|
71
|
+
console.log(chalk.white(` ${i + 1}. ${ts}`));
|
|
72
|
+
console.log(chalk.gray(` ${path.join(CONFIG_BACKUP_DIR, f)}`));
|
|
73
|
+
});
|
|
74
|
+
console.log(chalk.gray(`\n Geri yüklemek için: natureco config restore <dosya-adı>`));
|
|
75
|
+
console.log(chalk.gray(` Örnek: natureco config restore ${backups[0] || 'config-....json'}\n`));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (action === 'restore') {
|
|
80
|
+
const backupId = key;
|
|
81
|
+
if (!backupId) {
|
|
82
|
+
const backups = listBackups();
|
|
83
|
+
if (backups.length === 0) {
|
|
84
|
+
console.log(chalk.red('\n❌ Geri yüklenecek yedek bulunamadı.\n'));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
console.log(chalk.cyan.bold('\n Geri Yükleme\n'));
|
|
88
|
+
console.log(chalk.gray(' En son yedek: ') + chalk.white(backups[0]));
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log(chalk.gray(' Kullanım: ') + chalk.cyan(`natureco config restore ${backups[0]}`));
|
|
91
|
+
console.log(chalk.gray(' Yedekleri listelemek için: ') + chalk.cyan('natureco config backups\n'));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const result = restoreConfig(backupId);
|
|
97
|
+
console.log(chalk.green(`\n✅ Config geri yüklendi: ${result.timestamp}\n`));
|
|
98
|
+
console.log(chalk.gray(` Kaynak: ${result.path}\n`));
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.log(chalk.red(`\n❌ Geri yükleme başarısız: ${err.message}\n`));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log(chalk.red(`\n❌ Geçersiz action: ${action}\n`));
|
|
107
|
+
console.log(chalk.gray(' Kullanım: natureco config <get|set|list|backups|restore> [key] [value]\n'));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = config;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const readline = require('readline');
|
|
3
|
+
|
|
4
|
+
function rlQuestion(query) {
|
|
5
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
6
|
+
return new Promise(resolve => {
|
|
7
|
+
rl.question(query, answer => { rl.close(); resolve(answer.trim()); });
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function configure(args) {
|
|
12
|
+
console.log(chalk.cyan('\n ⚙️ Interactive Configuration\n'));
|
|
13
|
+
console.log(chalk.gray(' Press Ctrl+C anytime to cancel.\n'));
|
|
14
|
+
|
|
15
|
+
const { getConfig, saveConfig } = require('../utils/config');
|
|
16
|
+
const config = getConfig();
|
|
17
|
+
|
|
18
|
+
console.log(' 1) AI Provider & Model');
|
|
19
|
+
console.log(' 2) Gateway Settings');
|
|
20
|
+
console.log(' 3) Channels');
|
|
21
|
+
console.log(' 4) Voice / TTS');
|
|
22
|
+
console.log(' 5) API Keys');
|
|
23
|
+
console.log(' 6) View Current Config');
|
|
24
|
+
console.log(' 7) Cancel');
|
|
25
|
+
console.log();
|
|
26
|
+
|
|
27
|
+
const choice = await rlQuestion(' Select [1-7]: ');
|
|
28
|
+
|
|
29
|
+
if (choice === '7' || choice === '') {
|
|
30
|
+
console.log(chalk.gray('\n Cancelled.\n'));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (choice === '6') {
|
|
35
|
+
console.log(chalk.cyan('\n' + JSON.stringify(config, null, 2) + '\n'));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (choice === '1') {
|
|
40
|
+
const provider = await rlQuestion(` Provider (${config.provider || 'openai'}): `);
|
|
41
|
+
const model = await rlQuestion(` Model (${config.model || 'gpt-4o'}): `);
|
|
42
|
+
config.provider = provider || config.provider || 'openai';
|
|
43
|
+
config.model = model || config.model || 'gpt-4o';
|
|
44
|
+
saveConfig(config);
|
|
45
|
+
console.log(chalk.green(`\n ✅ Provider: ${config.provider}, Model: ${config.model}\n`));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (choice === '2') {
|
|
50
|
+
const url = await rlQuestion(` Gateway URL (${config.gatewayUrl || 'ws://localhost:3848'}): `);
|
|
51
|
+
const port = await rlQuestion(` Gateway port (${config.gatewayPort || 3848}): `);
|
|
52
|
+
config.gatewayUrl = url || config.gatewayUrl || 'ws://localhost:3848';
|
|
53
|
+
config.gatewayPort = parseInt(port) || config.gatewayPort || 3848;
|
|
54
|
+
saveConfig(config);
|
|
55
|
+
console.log(chalk.green(`\n ✅ Gateway: ${config.gatewayUrl}\n`));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (choice === '3') {
|
|
60
|
+
console.log(chalk.gray('\n Configure channels using:'));
|
|
61
|
+
console.log(chalk.cyan(' natureco channels add'));
|
|
62
|
+
console.log(chalk.cyan(' natureco telegram connect'));
|
|
63
|
+
console.log(chalk.cyan(' natureco whatsapp connect\n'));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (choice === '4') {
|
|
68
|
+
const tts = await rlQuestion(` TTS Provider (${config.tts?.provider || 'openai'}): `);
|
|
69
|
+
const voiceId = await rlQuestion(` Voice ID (${config.tts?.voiceId || '(none)'}): `);
|
|
70
|
+
if (!config.tts) config.tts = {};
|
|
71
|
+
config.tts.provider = tts || config.tts.provider || 'openai';
|
|
72
|
+
if (voiceId) config.tts.voiceId = voiceId;
|
|
73
|
+
saveConfig(config);
|
|
74
|
+
console.log(chalk.green(`\n ✅ Voice: ${config.tts.provider}\n`));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (choice === '5') {
|
|
79
|
+
const openai = await rlQuestion(` OpenAI API Key (${config.openaiApiKey ? '(set)' : '(none)'}): `);
|
|
80
|
+
const anthropic = await rlQuestion(` Anthropic API Key (${config.anthropicApiKey ? '(set)' : '(none)'}): `);
|
|
81
|
+
const tavily = await rlQuestion(` Tavily Search Key (${config.tavilyApiKey ? '(set)' : '(none)'}): `);
|
|
82
|
+
if (openai) config.openaiApiKey = openai;
|
|
83
|
+
if (anthropic) config.anthropicApiKey = anthropic;
|
|
84
|
+
if (tavily) config.tavilyApiKey = tavily;
|
|
85
|
+
saveConfig(config);
|
|
86
|
+
console.log(chalk.green('\n ✅ API Keys saved\n'));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(chalk.red('\n ❌ Invalid choice\n'));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = configure;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { execSync } = require('child_process');
|
|
3
|
+
|
|
4
|
+
async function crestodian(args) {
|
|
5
|
+
const fixMode = args.includes('--fix') || args.includes('-f');
|
|
6
|
+
|
|
7
|
+
console.log(chalk.cyan('\n 🛠️ Crestodian — Setup & Repair Assistant\n'));
|
|
8
|
+
console.log(chalk.gray(' Diagnosing your NatureCo installation...\n'));
|
|
9
|
+
|
|
10
|
+
const checks = [];
|
|
11
|
+
|
|
12
|
+
// Check Node version
|
|
13
|
+
const nodeMajor = parseInt(process.version.slice(1).split('.')[0]);
|
|
14
|
+
checks.push({
|
|
15
|
+
name: 'Node.js version',
|
|
16
|
+
ok: nodeMajor >= 18,
|
|
17
|
+
message: `Node ${process.version} (18+ required)`,
|
|
18
|
+
fix: 'Install Node.js 18+: https://nodejs.org'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Check npm
|
|
22
|
+
try {
|
|
23
|
+
const npmVer = execSync('npm --version', { encoding: 'utf8' }).trim();
|
|
24
|
+
checks.push({ name: 'npm', ok: true, message: `npm ${npmVer}` });
|
|
25
|
+
} catch {
|
|
26
|
+
checks.push({ name: 'npm', ok: false, message: 'npm not found', fix: 'Install Node.js (includes npm)' });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check config
|
|
30
|
+
try {
|
|
31
|
+
const { getConfig } = require('../utils/config');
|
|
32
|
+
const config = getConfig();
|
|
33
|
+
checks.push({
|
|
34
|
+
name: 'Configuration',
|
|
35
|
+
ok: !!(config.openaiApiKey || config.anthropicApiKey || config.provider),
|
|
36
|
+
message: config.provider ? `Provider: ${config.provider}` : 'Not configured',
|
|
37
|
+
fix: 'Run: natureco configure'
|
|
38
|
+
});
|
|
39
|
+
} catch {
|
|
40
|
+
checks.push({ name: 'Configuration', ok: false, message: 'Config error', fix: 'Run: natureco setup' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check git
|
|
44
|
+
try {
|
|
45
|
+
execSync('git --version', { stdio: 'pipe', encoding: 'utf8' });
|
|
46
|
+
checks.push({ name: 'Git', ok: true, message: 'Available' });
|
|
47
|
+
} catch {
|
|
48
|
+
checks.push({ name: 'Git', ok: false, message: 'Not found', fix: 'Install git: https://git-scm.com' });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check Playwright
|
|
52
|
+
try {
|
|
53
|
+
require.resolve('playwright');
|
|
54
|
+
checks.push({ name: 'Playwright', ok: true, message: 'Installed' });
|
|
55
|
+
} catch {
|
|
56
|
+
checks.push({ name: 'Playwright', ok: true, message: 'Optional (not installed)', optional: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Print results
|
|
60
|
+
const failed = checks.filter(c => !c.ok);
|
|
61
|
+
for (const check of checks) {
|
|
62
|
+
const icon = check.ok ? chalk.green('✅') : check.optional ? chalk.gray('⬜') : chalk.red('❌');
|
|
63
|
+
console.log(` ${icon} ${chalk.white(check.name)} ${chalk.gray('- ' + check.message)}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(chalk.gray('\n ' + '─'.repeat(48)));
|
|
67
|
+
|
|
68
|
+
if (failed.length === 0) {
|
|
69
|
+
console.log(chalk.green('\n ✅ All checks passed!\n'));
|
|
70
|
+
} else {
|
|
71
|
+
console.log(chalk.yellow(`\n ⚠️ ${failed.length} issue(s) found:\n`));
|
|
72
|
+
for (const f of failed) {
|
|
73
|
+
console.log(` ${chalk.yellow('●')} ${f.name}: ${f.message}`);
|
|
74
|
+
if (fixMode && f.fix) {
|
|
75
|
+
console.log(` ${chalk.gray('Fix:')} ${f.fix}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (fixMode) {
|
|
80
|
+
console.log(chalk.gray('\n Running fixes...\n'));
|
|
81
|
+
for (const f of failed) {
|
|
82
|
+
if (f.name === 'Configuration') {
|
|
83
|
+
console.log(chalk.gray(' Run: natureco onboard'));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(chalk.gray('\n To auto-fix: ') + chalk.cyan('natureco crestodian --fix\n'));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = crestodian;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { execSync, spawn } = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const GATEWAY_SCRIPT = path.join(__dirname, 'gateway-server.js');
|
|
6
|
+
|
|
7
|
+
function daemon(args) {
|
|
8
|
+
const [action, ...params] = args || [];
|
|
9
|
+
|
|
10
|
+
if (!action || action === 'status') return statusDaemon();
|
|
11
|
+
if (action === 'start') return startDaemon();
|
|
12
|
+
if (action === 'stop') return stopDaemon();
|
|
13
|
+
if (action === 'restart') return restartDaemon();
|
|
14
|
+
|
|
15
|
+
console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
|
|
16
|
+
console.log(chalk.gray(' Kullanım: natureco daemon [status|start|stop|restart]\n'));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function statusDaemon() {
|
|
21
|
+
console.log(chalk.cyan('\n ⚙️ Gateway Daemon\n'));
|
|
22
|
+
console.log(chalk.gray(' ' + '─'.repeat(48)));
|
|
23
|
+
console.log(` ${chalk.white('Status:')} ${chalk.green('running')}`);
|
|
24
|
+
console.log(` ${chalk.white('PID:')} ${process.pid}`);
|
|
25
|
+
console.log(` ${chalk.white('Uptime:')} ${Math.floor(process.uptime())}s`);
|
|
26
|
+
console.log(chalk.gray('\n Manage with:') + chalk.cyan(' natureco gateway start|stop|status'));
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function startDaemon() {
|
|
31
|
+
console.log(chalk.cyan('\n Starting Gateway daemon...\n'));
|
|
32
|
+
try {
|
|
33
|
+
const child = spawn(process.execPath, [GATEWAY_SCRIPT, 'start'], {
|
|
34
|
+
detached: true,
|
|
35
|
+
stdio: 'ignore',
|
|
36
|
+
cwd: path.join(__dirname, '..', '..')
|
|
37
|
+
});
|
|
38
|
+
child.unref();
|
|
39
|
+
console.log(chalk.green(' ✅ Gateway daemon started\n'));
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.log(chalk.red(` ❌ ${err.message}\n`));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function stopDaemon() {
|
|
46
|
+
console.log(chalk.cyan('\n Stopping Gateway daemon...\n'));
|
|
47
|
+
try {
|
|
48
|
+
execSync('taskkill /F /IM node.exe /FI "WINDOWTITLE eq natureco-gateway" 2>nul', { stdio: 'pipe' });
|
|
49
|
+
console.log(chalk.gray(' 🛑 Gateway daemon stopped\n'));
|
|
50
|
+
} catch {
|
|
51
|
+
console.log(chalk.yellow(' ⚠️ Could not stop daemon (may not be running)\n'));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function restartDaemon() {
|
|
56
|
+
stopDaemon();
|
|
57
|
+
setTimeout(() => startDaemon(), 1000);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = daemon;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const { getConfig, saveConfig } = require('../utils/config');
|
|
7
|
+
|
|
8
|
+
const PAIRING_FILE = path.join(os.homedir(), '.natureco', 'data', 'pairings.json');
|
|
9
|
+
|
|
10
|
+
function loadPairings() {
|
|
11
|
+
try {
|
|
12
|
+
if (fs.existsSync(PAIRING_FILE)) return JSON.parse(fs.readFileSync(PAIRING_FILE, 'utf8'));
|
|
13
|
+
} catch {}
|
|
14
|
+
return { pairedDevices: [], pendingRequests: [] };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function savePairings(data) {
|
|
18
|
+
const dir = path.dirname(PAIRING_FILE);
|
|
19
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
fs.writeFileSync(PAIRING_FILE, JSON.stringify(data, null, 2));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function generatePairingCode() {
|
|
24
|
+
return crypto.randomBytes(4).toString('hex').toUpperCase();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function devicePair(args) {
|
|
28
|
+
const [action, ...params] = args || [];
|
|
29
|
+
|
|
30
|
+
if (!action || action === 'list') return listDevices();
|
|
31
|
+
if (action === 'request') return requestPairing(params[0], params[1]);
|
|
32
|
+
if (action === 'approve') return approvePairing(params[0]);
|
|
33
|
+
if (action === 'reject') return rejectPairing(params[0]);
|
|
34
|
+
if (action === 'remove') return removeDevice(params[0]);
|
|
35
|
+
if (action === 'pairing-code') return showPairingCode();
|
|
36
|
+
if (action === 'verify') return verifyPairing(params[0], params[1]);
|
|
37
|
+
|
|
38
|
+
console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
|
|
39
|
+
console.log(chalk.gray(' Kullanım: natureco device-pair [list|request|approve|reject|remove|pairing-code|verify]\n'));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function listDevices() {
|
|
44
|
+
const data = loadPairings();
|
|
45
|
+
|
|
46
|
+
console.log(chalk.cyan('\n 📱 Paired Devices\n'));
|
|
47
|
+
console.log(chalk.gray(' ' + '─'.repeat(48)));
|
|
48
|
+
|
|
49
|
+
const devices = data.pairedDevices || [];
|
|
50
|
+
if (devices.length === 0) {
|
|
51
|
+
console.log(chalk.gray(' Eşleştirilmiş cihaz yok.\n'));
|
|
52
|
+
} else {
|
|
53
|
+
for (const d of devices) {
|
|
54
|
+
console.log(` ${chalk.green('●')} ${chalk.white(d.name || d.id)} ${chalk.gray(`(${d.type || 'unknown'})`)}`);
|
|
55
|
+
console.log(` ${chalk.gray('ID:')} ${d.id}`);
|
|
56
|
+
console.log(` ${chalk.gray('Since:')} ${d.pairedAt ? new Date(d.pairedAt).toLocaleString() : '-'}`);
|
|
57
|
+
console.log(` ${chalk.gray('Token:')} ${d.token ? d.token.substring(0, 8) + '…' : '-'}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const pending = data.pendingRequests || [];
|
|
62
|
+
if (pending.length > 0) {
|
|
63
|
+
console.log(chalk.yellow(`\n ⏳ Bekleyen İstekler (${pending.length})\n`));
|
|
64
|
+
for (const p of pending) {
|
|
65
|
+
console.log(` ${chalk.yellow('◐')} ${chalk.white(p.name || p.id)} ${chalk.gray(`(${p.type || 'unknown'})`)}`);
|
|
66
|
+
console.log(` ${chalk.gray('Code:')} ${chalk.cyan(p.code)}`);
|
|
67
|
+
console.log(` ${chalk.gray('Since:')} ${p.requestedAt ? new Date(p.requestedAt).toLocaleString() : '-'}`);
|
|
68
|
+
}
|
|
69
|
+
console.log(chalk.gray('\n Onaylamak için:'));
|
|
70
|
+
console.log(chalk.cyan(' natureco device-pair approve <code>'));
|
|
71
|
+
console.log(chalk.cyan(' natureco device-pair reject <code>'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function requestPairing(deviceName, deviceType) {
|
|
78
|
+
const name = deviceName || `Device-${crypto.randomBytes(3).toString('hex')}`;
|
|
79
|
+
const type = deviceType || 'cli';
|
|
80
|
+
const code = generatePairingCode();
|
|
81
|
+
const id = `dev_${crypto.randomBytes(8).toString('hex')}`;
|
|
82
|
+
|
|
83
|
+
const data = loadPairings();
|
|
84
|
+
if (!data.pendingRequests) data.pendingRequests = [];
|
|
85
|
+
|
|
86
|
+
data.pendingRequests.push({
|
|
87
|
+
id,
|
|
88
|
+
name,
|
|
89
|
+
type,
|
|
90
|
+
code,
|
|
91
|
+
requestedAt: new Date().toISOString(),
|
|
92
|
+
expiresAt: new Date(Date.now() + 15 * 60 * 1000).toISOString()
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
savePairings(data);
|
|
96
|
+
|
|
97
|
+
console.log(chalk.cyan('\n 📱 Pairing Request\n'));
|
|
98
|
+
console.log(chalk.gray(' ' + '─'.repeat(48)));
|
|
99
|
+
console.log(` ${chalk.white('Name:')} ${chalk.cyan(name)}`);
|
|
100
|
+
console.log(` ${chalk.white('Type:')} ${chalk.cyan(type)}`);
|
|
101
|
+
console.log(` ${chalk.white('Code:')} ${chalk.yellow(code)}`);
|
|
102
|
+
console.log(` ${chalk.white('ID:')} ${chalk.gray(id)}`);
|
|
103
|
+
console.log(chalk.gray(`\n Pairing code expires: ${new Date(Date.now() + 15 * 60 * 1000).toLocaleString()}`));
|
|
104
|
+
console.log(chalk.gray('\n On another terminal:'));
|
|
105
|
+
console.log(chalk.cyan(` natureco device-pair approve ${code}`));
|
|
106
|
+
console.log(chalk.cyan(` natureco device-pair reject ${code}`));
|
|
107
|
+
console.log();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function approvePairing(code) {
|
|
111
|
+
if (!code) {
|
|
112
|
+
console.log(chalk.red('\n ❌ Pairing code gerekli\n'));
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const data = loadPairings();
|
|
117
|
+
const idx = (data.pendingRequests || []).findIndex(p => p.code === code);
|
|
118
|
+
|
|
119
|
+
if (idx === -1) {
|
|
120
|
+
console.log(chalk.red(`\n ❌ Geçersiz pairing code: ${code}\n`));
|
|
121
|
+
console.log(chalk.gray(' Bekleyen istekleri görmek için: natureco device-pair list\n'));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const request = data.pendingRequests[idx];
|
|
126
|
+
const token = `nc_${crypto.randomBytes(16).toString('hex')}`;
|
|
127
|
+
|
|
128
|
+
if (!data.pairedDevices) data.pairedDevices = [];
|
|
129
|
+
data.pairedDevices.push({
|
|
130
|
+
id: request.id,
|
|
131
|
+
name: request.name,
|
|
132
|
+
type: request.type,
|
|
133
|
+
token,
|
|
134
|
+
pairedAt: new Date().toISOString()
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
data.pendingRequests.splice(idx, 1);
|
|
138
|
+
savePairings(data);
|
|
139
|
+
|
|
140
|
+
console.log(chalk.green(`\n ✅ Paired: ${request.name}\n`));
|
|
141
|
+
console.log(chalk.gray(` Device ID: ${request.id}`));
|
|
142
|
+
console.log(chalk.gray(` Token: ${token}\n`));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function rejectPairing(code) {
|
|
146
|
+
if (!code) {
|
|
147
|
+
console.log(chalk.red('\n ❌ Pairing code gerekli\n'));
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const data = loadPairings();
|
|
152
|
+
const idx = (data.pendingRequests || []).findIndex(p => p.code === code);
|
|
153
|
+
|
|
154
|
+
if (idx === -1) {
|
|
155
|
+
console.log(chalk.red(`\n ❌ Geçersiz pairing code: ${code}\n`));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const request = data.pendingRequests[idx];
|
|
160
|
+
data.pendingRequests.splice(idx, 1);
|
|
161
|
+
savePairings(data);
|
|
162
|
+
|
|
163
|
+
console.log(chalk.gray(`\n 🔴 Rejected: ${request.name}\n`));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function removeDevice(deviceId) {
|
|
167
|
+
if (!deviceId) {
|
|
168
|
+
console.log(chalk.red('\n ❌ Device ID gerekli\n'));
|
|
169
|
+
console.log(chalk.cyan(' natureco device-pair remove dev_abc123\n'));
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const data = loadPairings();
|
|
174
|
+
const idx = (data.pairedDevices || []).findIndex(d => d.id === deviceId);
|
|
175
|
+
|
|
176
|
+
if (idx === -1) {
|
|
177
|
+
console.log(chalk.red(`\n ❌ Cihaz bulunamadı: ${deviceId}\n`));
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const device = data.pairedDevices[idx];
|
|
182
|
+
data.pairedDevices.splice(idx, 1);
|
|
183
|
+
savePairings(data);
|
|
184
|
+
|
|
185
|
+
console.log(chalk.gray(`\n 🗑️ Removed: ${device.name} (${deviceId})\n`));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function showPairingCode() {
|
|
189
|
+
const code = generatePairingCode();
|
|
190
|
+
const expiresAt = new Date(Date.now() + 15 * 60 * 1000);
|
|
191
|
+
|
|
192
|
+
console.log(chalk.cyan('\n 🔑 Pairing Code\n'));
|
|
193
|
+
console.log(chalk.gray(' ' + '─'.repeat(48)));
|
|
194
|
+
console.log(` Code: ${chalk.bold.yellow(code)}`);
|
|
195
|
+
console.log(` Expires: ${chalk.gray(expiresAt.toLocaleString())}`);
|
|
196
|
+
console.log(chalk.gray('\n Share this code with the device to pair.\n'));
|
|
197
|
+
console.log(chalk.gray(' On the other device:'));
|
|
198
|
+
console.log(chalk.cyan(` natureco device-pair verify ${code} <device-name>\n`));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function verifyPairing(code, deviceName) {
|
|
202
|
+
if (!code) {
|
|
203
|
+
console.log(chalk.red('\n ❌ Pairing code gerekli\n'));
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const data = loadPairings();
|
|
208
|
+
const pending = data.pendingRequests || [];
|
|
209
|
+
|
|
210
|
+
if (pending.some(p => p.code === code)) {
|
|
211
|
+
console.log(chalk.green('\n ✅ Pairing code valid — awaiting approval\n'));
|
|
212
|
+
console.log(chalk.gray(' Ask the admin to run:'));
|
|
213
|
+
console.log(chalk.cyan(` natureco device-pair approve ${code}\n`));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const paired = data.pairedDevices || [];
|
|
218
|
+
const matched = paired.find(d => {
|
|
219
|
+
const cfg = getConfig();
|
|
220
|
+
return cfg.pairingToken && d.token === cfg.pairingToken;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (matched) {
|
|
224
|
+
console.log(chalk.green(`\n ✅ Already paired as: ${matched.name}\n`));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const name = deviceName || `Device-${crypto.randomBytes(3).toString('hex')}`;
|
|
229
|
+
const newId = `dev_${crypto.randomBytes(8).toString('hex')}`;
|
|
230
|
+
|
|
231
|
+
if (!data.pendingRequests) data.pendingRequests = [];
|
|
232
|
+
data.pendingRequests.push({
|
|
233
|
+
id: newId,
|
|
234
|
+
name,
|
|
235
|
+
type: 'cli',
|
|
236
|
+
code,
|
|
237
|
+
requestedAt: new Date().toISOString(),
|
|
238
|
+
expiresAt: new Date(Date.now() + 15 * 60 * 1000).toISOString()
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
savePairings(data);
|
|
242
|
+
|
|
243
|
+
console.log(chalk.yellow(`\n ⏳ Pairing requested for "${name}" with code ${code}\n`));
|
|
244
|
+
console.log(chalk.gray(' Admin should approve with:'));
|
|
245
|
+
console.log(chalk.cyan(` natureco device-pair approve ${code}\n`));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
module.exports = devicePair;
|