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.
Files changed (96) hide show
  1. package/README.md +94 -11
  2. package/bin/natureco.js +470 -10
  3. package/package.json +10 -6
  4. package/src/commands/admin-rpc.js +219 -0
  5. package/src/commands/agent.js +89 -0
  6. package/src/commands/approvals.js +53 -0
  7. package/src/commands/backup.js +124 -0
  8. package/src/commands/bonjour.js +167 -0
  9. package/src/commands/capability.js +64 -0
  10. package/src/commands/channels.js +94 -4
  11. package/src/commands/chat.js +11 -25
  12. package/src/commands/clickclack.js +130 -0
  13. package/src/commands/commitments.js +32 -0
  14. package/src/commands/completion.js +76 -0
  15. package/src/commands/config.js +111 -68
  16. package/src/commands/configure.js +93 -0
  17. package/src/commands/crestodian.js +92 -0
  18. package/src/commands/daemon.js +60 -0
  19. package/src/commands/device-pair.js +248 -0
  20. package/src/commands/devices.js +110 -0
  21. package/src/commands/directory.js +47 -0
  22. package/src/commands/dns.js +58 -0
  23. package/src/commands/docs.js +43 -0
  24. package/src/commands/doctor.js +121 -16
  25. package/src/commands/exec-policy.js +71 -0
  26. package/src/commands/gateway-server.js +1175 -30
  27. package/src/commands/gateway.js +11 -20
  28. package/src/commands/health.js +18 -0
  29. package/src/commands/help.js +6 -0
  30. package/src/commands/imessage.js +169 -0
  31. package/src/commands/infer.js +73 -0
  32. package/src/commands/irc.js +119 -0
  33. package/src/commands/mattermost.js +164 -0
  34. package/src/commands/memory-cmd.js +134 -1
  35. package/src/commands/message.js +30 -4
  36. package/src/commands/migrate.js +213 -2
  37. package/src/commands/models.js +584 -216
  38. package/src/commands/node.js +98 -0
  39. package/src/commands/nodes.js +106 -0
  40. package/src/commands/oc-path.js +200 -0
  41. package/src/commands/onboard.js +70 -0
  42. package/src/commands/open-prose.js +67 -0
  43. package/src/commands/plugins.js +415 -172
  44. package/src/commands/policy.js +176 -0
  45. package/src/commands/proxy.js +155 -0
  46. package/src/commands/qr.js +28 -0
  47. package/src/commands/sandbox.js +125 -0
  48. package/src/commands/secrets.js +118 -0
  49. package/src/commands/security.js +149 -1
  50. package/src/commands/setup.js +114 -10
  51. package/src/commands/signal.js +495 -0
  52. package/src/commands/skills.js +20 -29
  53. package/src/commands/sms.js +168 -0
  54. package/src/commands/system.js +53 -0
  55. package/src/commands/tasks.js +328 -79
  56. package/src/commands/terminal.js +21 -0
  57. package/src/commands/thread-ownership.js +157 -0
  58. package/src/commands/transcripts.js +72 -0
  59. package/src/commands/voice.js +82 -0
  60. package/src/commands/vydra.js +98 -0
  61. package/src/commands/webhooks.js +79 -0
  62. package/src/commands/whatsapp.js +7 -21
  63. package/src/commands/workboard.js +207 -0
  64. package/src/tools/audio_understanding.js +154 -0
  65. package/src/tools/bash.js +63 -29
  66. package/src/tools/browser.js +112 -0
  67. package/src/tools/canvas.js +104 -0
  68. package/src/tools/document_extract.js +84 -0
  69. package/src/tools/duckduckgo.js +54 -0
  70. package/src/tools/exa_search.js +66 -0
  71. package/src/tools/firecrawl.js +104 -0
  72. package/src/tools/image_generation.js +99 -0
  73. package/src/tools/llm_task.js +118 -0
  74. package/src/tools/media_understanding.js +128 -0
  75. package/src/tools/music_generation.js +113 -0
  76. package/src/tools/parallel_search.js +77 -0
  77. package/src/tools/phone_control.js +80 -0
  78. package/src/tools/phone_control_enhanced.js +184 -0
  79. package/src/tools/searxng.js +61 -0
  80. package/src/tools/speech_to_text.js +135 -0
  81. package/src/tools/text_to_speech.js +105 -0
  82. package/src/tools/thread_ownership.js +88 -0
  83. package/src/tools/video_generation.js +72 -0
  84. package/src/tools/web_readability.js +104 -0
  85. package/src/utils/api.js +3 -20
  86. package/src/utils/approvals.js +297 -0
  87. package/src/utils/background.js +223 -66
  88. package/src/utils/baileys.js +21 -0
  89. package/src/utils/config.js +141 -10
  90. package/src/utils/errors.js +148 -0
  91. package/src/utils/inquirer-wrapper.js +1 -2
  92. package/src/utils/memory.js +200 -0
  93. package/src/utils/path-utils.js +13 -13
  94. package/src/utils/plugin-registry.js +238 -0
  95. package/src/utils/secrets.js +177 -0
  96. package/src/utils/skills.js +10 -23
@@ -0,0 +1,297 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const net = require('net');
5
+ const chalk = require('chalk');
6
+ const inquirer = require('./inquirer-wrapper');
7
+ const { NatureCoError } = require('./errors');
8
+
9
+ const APPROVALS_FILE = path.join(os.homedir(), '.natureco', 'exec-approvals.json');
10
+ const APPROVALS_SOCKET_PATH = path.join(os.homedir(), '.natureco', 'exec-approvals.sock');
11
+ const DEFAULT_TIMEOUT_MS = 1800000; // 30 min
12
+
13
+ class ExecApprovalError extends NatureCoError {
14
+ constructor(message, options = {}) {
15
+ super(message, options);
16
+ this.command = options.command || null;
17
+ }
18
+ }
19
+
20
+ // -- Data types --
21
+
22
+ /**
23
+ * @typedef {'deny'|'allowlist'|'full'} ExecSecurity
24
+ * @typedef {'off'|'on-miss'|'always'} ExecAsk
25
+ * @typedef {'deny'|'allowlist'|'ask'|'auto'|'full'} ExecMode
26
+ * @typedef {{ id?: string, pattern: string, argPattern?: string, source?: string, lastUsedAt?: string, lastUsedCommand?: string }} AllowlistEntry
27
+ * @typedef {{ version: 1, defaults?: { security?: ExecSecurity, ask?: ExecAsk }, agents?: Record<string, { security?: ExecSecurity, ask?: ExecAsk, allowlist?: AllowlistEntry[] }> }} ApprovalsFile
28
+ * @typedef {'allow-once'|'allow-always'|'deny'} ApprovalDecision
29
+ */
30
+
31
+ function getApprovalsPath() {
32
+ return APPROVALS_FILE;
33
+ }
34
+
35
+ function loadApprovals() {
36
+ if (!fs.existsSync(APPROVALS_FILE)) {
37
+ return { version: 1, defaults: { security: 'full', ask: 'off' }, agents: {} };
38
+ }
39
+ try {
40
+ return JSON.parse(fs.readFileSync(APPROVALS_FILE, 'utf8'));
41
+ } catch {
42
+ return { version: 1, defaults: { security: 'full', ask: 'off' }, agents: {} };
43
+ }
44
+ }
45
+
46
+ function saveApprovals(data) {
47
+ const dir = path.dirname(APPROVALS_FILE);
48
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
49
+ fs.writeFileSync(APPROVALS_FILE, JSON.stringify(data, null, 2), 'utf8');
50
+ }
51
+
52
+ function resolveEffectivePolicy(agentId) {
53
+ const file = loadApprovals();
54
+ const defaults = file.defaults || { security: 'full', ask: 'off' };
55
+ if (!agentId || !file.agents?.[agentId]) {
56
+ return { security: defaults.security || 'full', ask: defaults.ask || 'off', allowlist: [] };
57
+ }
58
+ const agent = file.agents[agentId];
59
+ return {
60
+ security: agent.security || defaults.security || 'full',
61
+ ask: agent.ask || defaults.ask || 'off',
62
+ allowlist: agent.allowlist || [],
63
+ };
64
+ }
65
+
66
+ function resolveMode(security, ask) {
67
+ if (security === 'deny') return 'deny';
68
+ if (security === 'allowlist' && ask === 'always') return 'ask';
69
+ if (security === 'allowlist') return 'allowlist';
70
+ if (security === 'full') return 'full';
71
+ return 'full';
72
+ }
73
+
74
+ function matchAllowlist(entries, command) {
75
+ if (!entries || !command) return null;
76
+ for (const entry of entries) {
77
+ try {
78
+ const pattern = new RegExp(entry.pattern, 'i');
79
+ if (pattern.test(command)) {
80
+ if (entry.argPattern) {
81
+ const argRe = new RegExp(entry.argPattern, 'i');
82
+ const args = command.split(/\s+/).slice(1).join(' ');
83
+ if (!argRe.test(args)) continue;
84
+ }
85
+ return entry;
86
+ }
87
+ } catch {}
88
+ }
89
+ return null;
90
+ }
91
+
92
+ function requiresApproval({ command, agentId, security, ask }) {
93
+ const policy = resolveEffectivePolicy(agentId);
94
+ const mode = resolveMode(security || policy.security, ask || policy.ask);
95
+
96
+ if (mode === 'deny') return { required: true, reason: 'deny' };
97
+ if (mode === 'full') return { required: false, reason: 'full' };
98
+
99
+ // Check allowlist
100
+ const match = matchAllowlist(policy.allowlist, command);
101
+ if (match) return { required: false, reason: 'allowlist', entry: match };
102
+
103
+ if (mode === 'allowlist') return { required: true, reason: 'not-in-allowlist' };
104
+ if (mode === 'ask') return { required: true, reason: 'ask' };
105
+
106
+ return { required: true, reason: 'unknown' };
107
+ }
108
+
109
+ // Built-in safe commands that never need approval
110
+ const SAFE_COMMANDS = new Set([
111
+ 'ls', 'cat', 'head', 'tail', 'echo', 'pwd', 'date', 'whoami',
112
+ 'node -e', 'node -v', 'npm -v', 'git status', 'git diff', 'git log',
113
+ ]);
114
+
115
+ function isSafeCommand(command) {
116
+ if (SAFE_COMMANDS.has(command.trim())) return true;
117
+ for (const safe of SAFE_COMMANDS) {
118
+ if (command.trim().startsWith(safe)) return true;
119
+ }
120
+ return false;
121
+ }
122
+
123
+ // Known dangerous patterns that should always warn
124
+ const DANGEROUS_PATTERNS = [
125
+ /^rm\s+-rf\s+\/\s*$/,
126
+ /^mkfs/,
127
+ /^dd\s+if=.*\s+of=\/dev/,
128
+ /^:\(\)\s*\{.*:\(\)\s*;\s*\};/,
129
+ /^chmod\s+-R\s+777\s+\//,
130
+ /^chown\s+-R/,
131
+ /^>\/dev\/sda/,
132
+ /^\|.*sh$/,
133
+ /^curl.*\|.*sh$/,
134
+ /^wget.*\|.*sh$/,
135
+ ];
136
+
137
+ function isDangerousCommand(command) {
138
+ for (const pattern of DANGEROUS_PATTERNS) {
139
+ if (pattern.test(command.trim())) return true;
140
+ }
141
+ return false;
142
+ }
143
+
144
+ async function requestUserApproval(command, options = {}) {
145
+ const { timeoutMs = DEFAULT_TIMEOUT_MS, agentId } = options;
146
+
147
+ console.log('');
148
+ console.log(chalk.yellow(' ⚠️ Command requires approval'));
149
+ console.log(chalk.gray(' ─'.repeat(30)));
150
+ console.log(chalk.white(' ') + command);
151
+ console.log(chalk.gray(' ─'.repeat(30)));
152
+
153
+ const choices = [
154
+ { value: 'allow-once', name: 'Allow once' },
155
+ { value: 'allow-always', name: 'Always allow this command' },
156
+ { value: 'deny', name: 'Deny' },
157
+ ];
158
+
159
+ // Add edit option if command is dangerous
160
+ if (options.isDangerous) {
161
+ choices.push({ value: 'edit', name: 'Edit command' });
162
+ }
163
+
164
+ process.stdin.resume();
165
+ const { decision } = await inquirer.prompt([{
166
+ type: 'list',
167
+ name: 'decision',
168
+ message: 'What would you like to do?',
169
+ choices,
170
+ }]);
171
+
172
+ if (decision === 'edit') {
173
+ const { edited } = await inquirer.prompt([{
174
+ type: 'input',
175
+ name: 'edited',
176
+ message: 'Edit command:',
177
+ default: command,
178
+ }]);
179
+ return { decision: 'allow-once', command: edited };
180
+ }
181
+
182
+ if (decision === 'allow-always') {
183
+ addAllowlistEntry(agentId, command);
184
+ }
185
+
186
+ return { decision, command };
187
+ }
188
+
189
+ function addAllowlistEntry(agentId, command) {
190
+ const file = loadApprovals();
191
+ if (!file.agents) file.agents = {};
192
+ if (!file.agents[agentId]) file.agents[agentId] = { allowlist: [] };
193
+
194
+ // Escape special regex chars in the command for pattern matching
195
+ const escaped = command.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
196
+ const entry = {
197
+ id: `auto-${Date.now()}`,
198
+ pattern: `^${escaped}$`,
199
+ source: 'allow-always',
200
+ lastUsedAt: new Date().toISOString(),
201
+ lastUsedCommand: command,
202
+ };
203
+
204
+ file.agents[agentId].allowlist.push(entry);
205
+ saveApprovals(file);
206
+ }
207
+
208
+ function setSecurityPolicy(agentId, options = {}) {
209
+ const file = loadApprovals();
210
+ if (!file.agents) file.agents = {};
211
+ if (!file.agents[agentId]) file.agents[agentId] = {};
212
+
213
+ if (options.security) file.agents[agentId].security = options.security;
214
+ if (options.ask !== undefined) file.agents[agentId].ask = options.ask;
215
+
216
+ saveApprovals(file);
217
+ }
218
+
219
+ async function checkCommand(command, options = {}) {
220
+ const { agentId = 'default' } = options;
221
+
222
+ // Empty command
223
+ if (!command || !command.trim()) {
224
+ return { allowed: false, reason: 'empty' };
225
+ }
226
+
227
+ // Check if safe
228
+ if (isSafeCommand(command)) {
229
+ return { allowed: true, reason: 'safe-command' };
230
+ }
231
+
232
+ // Check if dangerous
233
+ const dangerous = isDangerousCommand(command);
234
+ const policy = resolveEffectivePolicy(agentId);
235
+ const mode = resolveMode(policy.security, policy.ask);
236
+
237
+ if (mode === 'deny') {
238
+ return { allowed: false, reason: 'denied-by-policy', policy };
239
+ }
240
+
241
+ // Check allowlist
242
+ const match = matchAllowlist(policy.allowlist, command);
243
+ if (match) {
244
+ return { allowed: true, reason: 'allowlist', entry: match };
245
+ }
246
+
247
+ if (mode === 'allowlist') {
248
+ return { allowed: false, reason: 'not-in-allowlist', policy };
249
+ }
250
+
251
+ if (mode === 'ask') {
252
+ const result = await requestUserApproval(command, { ...options, isDangerous: dangerous });
253
+ return {
254
+ allowed: result.decision === 'allow-once' || result.decision === 'allow-always',
255
+ reason: result.decision,
256
+ editedCommand: result.command,
257
+ };
258
+ }
259
+
260
+ // Full mode - always allow
261
+ return { allowed: true, reason: 'full-mode' };
262
+ }
263
+
264
+ function listAllowlist(agentId) {
265
+ const policy = resolveEffectivePolicy(agentId);
266
+ return policy.allowlist || [];
267
+ }
268
+
269
+ function removeAllowlistEntry(agentId, entryId) {
270
+ const file = loadApprovals();
271
+ if (!file.agents?.[agentId]?.allowlist) return false;
272
+ const before = file.agents[agentId].allowlist.length;
273
+ file.agents[agentId].allowlist = file.agents[agentId].allowlist.filter(e => e.id !== entryId);
274
+ saveApprovals(file);
275
+ return file.agents[agentId].allowlist.length < before;
276
+ }
277
+
278
+ module.exports = {
279
+ ExecApprovalError,
280
+ loadApprovals,
281
+ saveApprovals,
282
+ resolveEffectivePolicy,
283
+ resolveMode,
284
+ matchAllowlist,
285
+ requiresApproval,
286
+ isSafeCommand,
287
+ isDangerousCommand,
288
+ requestUserApproval,
289
+ addAllowlistEntry,
290
+ setSecurityPolicy,
291
+ checkCommand,
292
+ listAllowlist,
293
+ removeAllowlistEntry,
294
+ getApprovalsPath,
295
+ DANGEROUS_PATTERNS,
296
+ SAFE_COMMANDS,
297
+ };
@@ -1,66 +1,223 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
-
5
- const BACKGROUND_TASKS_FILE = path.join(os.homedir(), '.natureco', 'background-tasks.json');
6
-
7
- function ensureBackgroundDir() {
8
- const dir = path.dirname(BACKGROUND_TASKS_FILE);
9
- if (!fs.existsSync(dir)) {
10
- fs.mkdirSync(dir, { recursive: true });
11
- }
12
- }
13
-
14
- function loadBackgroundTasks() {
15
- ensureBackgroundDir();
16
- if (!fs.existsSync(BACKGROUND_TASKS_FILE)) {
17
- return [];
18
- }
19
- try {
20
- const data = fs.readFileSync(BACKGROUND_TASKS_FILE, 'utf-8');
21
- return JSON.parse(data);
22
- } catch {
23
- return [];
24
- }
25
- }
26
-
27
- function saveBackgroundTasks(tasks) {
28
- ensureBackgroundDir();
29
- fs.writeFileSync(BACKGROUND_TASKS_FILE, JSON.stringify(tasks, null, 2), 'utf-8');
30
- }
31
-
32
- function addBackgroundTask(task) {
33
- const tasks = loadBackgroundTasks();
34
- const id = Date.now().toString(36) + Math.random().toString(36).slice(2);
35
- const newTask = {
36
- id,
37
- ...task,
38
- status: 'pending',
39
- createdAt: new Date().toISOString(),
40
- };
41
- tasks.push(newTask);
42
- saveBackgroundTasks(tasks);
43
- return newTask;
44
- }
45
-
46
- function updateBackgroundTask(id, updates) {
47
- const tasks = loadBackgroundTasks();
48
- const index = tasks.findIndex(t => t.id === id);
49
- if (index === -1) return false;
50
- tasks[index] = { ...tasks[index], ...updates };
51
- saveBackgroundTasks(tasks);
52
- return true;
53
- }
54
-
55
- function getBackgroundTask(id) {
56
- const tasks = loadBackgroundTasks();
57
- return tasks.find(t => t.id === id);
58
- }
59
-
60
- module.exports = {
61
- loadBackgroundTasks,
62
- saveBackgroundTasks,
63
- addBackgroundTask,
64
- updateBackgroundTask,
65
- getBackgroundTask,
66
- };
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ const TASKS_FILE = path.join(os.homedir(), '.natureco', 'tasks.json');
6
+ const TASK_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
7
+ const STALE_QUEUED_MS = 10 * 60 * 1000;
8
+ const STALE_RUNNING_MS = 30 * 60 * 1000;
9
+ const LOST_GRACE_MS = 5 * 60 * 1000;
10
+
11
+ const TASK_STATUSES = ['queued', 'running', 'succeeded', 'failed', 'timed_out', 'cancelled', 'lost'];
12
+ const TERMINAL_STATUSES = ['succeeded', 'failed', 'timed_out', 'cancelled', 'lost'];
13
+ const RUNTIME_TYPES = ['cli', 'cron', 'subagent', 'acp'];
14
+ const NOTIFY_POLICIES = ['done_only', 'state_changes', 'silent'];
15
+
16
+ function ensureDir() {
17
+ const dir = path.dirname(TASKS_FILE);
18
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
19
+ }
20
+
21
+ function loadTasks() {
22
+ ensureDir();
23
+ if (!fs.existsSync(TASKS_FILE)) return [];
24
+ try {
25
+ return JSON.parse(fs.readFileSync(TASKS_FILE, 'utf-8'));
26
+ } catch {
27
+ return [];
28
+ }
29
+ }
30
+
31
+ function saveTasks(tasks) {
32
+ ensureDir();
33
+ fs.writeFileSync(TASKS_FILE, JSON.stringify(tasks, null, 2), 'utf-8');
34
+ }
35
+
36
+ function generateId() {
37
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
38
+ }
39
+
40
+ function nowISO() {
41
+ return new Date().toISOString();
42
+ }
43
+
44
+ function createTask(data) {
45
+ const tasks = loadTasks();
46
+ const task = {
47
+ id: generateId(),
48
+ status: 'queued',
49
+ runtime: data.runtime || 'cli',
50
+ message: data.message || '',
51
+ botName: data.botName || 'default',
52
+ childSessionKey: data.childSessionKey || null,
53
+ requesterSessionKey: data.requesterSessionKey || null,
54
+ runId: data.runId || null,
55
+ notifyPolicy: data.notifyPolicy || 'done_only',
56
+ createdAt: nowISO(),
57
+ startedAt: null,
58
+ endedAt: null,
59
+ error: null,
60
+ result: null,
61
+ cleanupAfter: null,
62
+ metadata: data.metadata || {},
63
+ };
64
+ tasks.push(task);
65
+ saveTasks(tasks);
66
+ return task;
67
+ }
68
+
69
+ function updateTask(id, updates) {
70
+ const tasks = loadTasks();
71
+ const idx = tasks.findIndex(t => t.id === id);
72
+ if (idx === -1) return null;
73
+
74
+ const now = nowISO();
75
+ if (updates.status === 'running' && !tasks[idx].startedAt) {
76
+ updates.startedAt = now;
77
+ }
78
+ if (updates.status && TERMINAL_STATUSES.includes(updates.status) && !tasks[idx].endedAt) {
79
+ updates.endedAt = now;
80
+ updates.cleanupAfter = new Date(Date.now() + TASK_RETENTION_MS).toISOString();
81
+ }
82
+
83
+ tasks[idx] = { ...tasks[idx], ...updates };
84
+ saveTasks(tasks);
85
+ return tasks[idx];
86
+ }
87
+
88
+ function getTask(id) {
89
+ const tasks = loadTasks();
90
+ return tasks.find(t => t.id === id) || null;
91
+ }
92
+
93
+ function getTasksBySession(sessionKey) {
94
+ const tasks = loadTasks();
95
+ return tasks.filter(t => t.requesterSessionKey === sessionKey || t.childSessionKey === sessionKey);
96
+ }
97
+
98
+ function cancelTask(id) {
99
+ const task = getTask(id);
100
+ if (!task) return null;
101
+ if (TERMINAL_STATUSES.includes(task.status)) return task;
102
+ return updateTask(id, { status: 'cancelled' });
103
+ }
104
+
105
+ function listTasks(options = {}) {
106
+ let tasks = loadTasks();
107
+
108
+ if (options.runtime) {
109
+ tasks = tasks.filter(t => t.runtime === options.runtime);
110
+ }
111
+ if (options.status) {
112
+ tasks = tasks.filter(t => t.status === options.status);
113
+ }
114
+ if (options.limit) {
115
+ tasks = tasks.slice(0, options.limit);
116
+ }
117
+
118
+ return tasks.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
119
+ }
120
+
121
+ function auditTasks() {
122
+ const tasks = loadTasks();
123
+ const now = Date.now();
124
+ const findings = [];
125
+
126
+ tasks.forEach(t => {
127
+ const createdAt = new Date(t.createdAt).getTime();
128
+
129
+ if (t.status === 'queued' && (now - createdAt) > STALE_QUEUED_MS) {
130
+ findings.push({ id: t.id, severity: 'warn', code: 'stale_queued', message: `${Math.round((now - createdAt) / 1000)}s boyunca kuyrukta bekliyor` });
131
+ }
132
+
133
+ if (t.status === 'running' && t.startedAt) {
134
+ const startedAt = new Date(t.startedAt).getTime();
135
+ if ((now - startedAt) > STALE_RUNNING_MS) {
136
+ findings.push({ id: t.id, severity: 'error', code: 'stale_running', message: `${Math.round((now - startedAt) / 1000)}s boyunca çalışıyor` });
137
+ }
138
+ }
139
+
140
+ if (t.status === 'lost') {
141
+ const sev = t.cleanupAfter && new Date(t.cleanupAfter).getTime() > now ? 'warn' : 'error';
142
+ findings.push({ id: t.id, severity: sev, code: 'lost', message: `Runtime backing kayboldu` });
143
+ }
144
+
145
+ if (TERMINAL_STATUSES.includes(t.status) && !t.cleanupAfter) {
146
+ findings.push({ id: t.id, severity: 'warn', code: 'missing_cleanup', message: 'Terminal task has no cleanup timestamp' });
147
+ }
148
+
149
+ if (t.endedAt && t.startedAt && new Date(t.endedAt) < new Date(t.startedAt)) {
150
+ findings.push({ id: t.id, severity: 'warn', code: 'inconsistent_timestamps', message: 'Bitiş zamanı başlangıçtan önce' });
151
+ }
152
+ });
153
+
154
+ return findings;
155
+ }
156
+
157
+ function maintenanceTasks(dryRun = true) {
158
+ const tasks = loadTasks();
159
+ const now = Date.now();
160
+ const stats = { pruned: 0, reconciled: 0, cleaned: 0 };
161
+
162
+ const remaining = tasks.filter(t => {
163
+ if (t.cleanupAfter && new Date(t.cleanupAfter).getTime() < now) {
164
+ if (!dryRun) stats.pruned++;
165
+ return dryRun;
166
+ }
167
+
168
+ if (t.status === 'queued' && (now - new Date(t.createdAt).getTime()) > LOST_GRACE_MS) {
169
+ if (!dryRun) {
170
+ t.status = 'lost';
171
+ t.endedAt = nowISO();
172
+ t.cleanupAfter = new Date(now + TASK_RETENTION_MS).toISOString();
173
+ }
174
+ stats.reconciled++;
175
+ }
176
+
177
+ if (t.status === 'running' && t.startedAt && (now - new Date(t.startedAt).getTime()) > (STALE_RUNNING_MS + LOST_GRACE_MS)) {
178
+ if (!dryRun) {
179
+ t.status = 'lost';
180
+ t.endedAt = nowISO();
181
+ t.cleanupAfter = new Date(now + TASK_RETENTION_MS).toISOString();
182
+ }
183
+ stats.reconciled++;
184
+ }
185
+
186
+ if (TERMINAL_STATUSES.includes(t.status) && !t.cleanupAfter) {
187
+ if (!dryRun) {
188
+ t.cleanupAfter = new Date(now + TASK_RETENTION_MS).toISOString();
189
+ }
190
+ stats.cleaned++;
191
+ }
192
+
193
+ return true;
194
+ });
195
+
196
+ if (!dryRun) saveTasks(remaining);
197
+ return { ...stats, remaining: dryRun ? remaining.length : remaining.length };
198
+ }
199
+
200
+ function getTaskSummary() {
201
+ const tasks = loadTasks();
202
+ const active = tasks.filter(t => t.status === 'queued' || t.status === 'running').length;
203
+ const failures = tasks.filter(t => ['failed', 'timed_out', 'lost'].includes(t.status)).length;
204
+ const byRuntime = {};
205
+ RUNTIME_TYPES.forEach(r => { byRuntime[r] = tasks.filter(t => t.runtime === r).length; });
206
+ return { active, failures, byRuntime, total: tasks.length };
207
+ }
208
+
209
+ module.exports = {
210
+ createTask,
211
+ updateTask,
212
+ getTask,
213
+ getTasksBySession,
214
+ cancelTask,
215
+ listTasks,
216
+ auditTasks,
217
+ maintenanceTasks,
218
+ getTaskSummary,
219
+ TASK_STATUSES,
220
+ TERMINAL_STATUSES,
221
+ RUNTIME_TYPES,
222
+ NOTIFY_POLICIES,
223
+ };
@@ -0,0 +1,21 @@
1
+ let _makeWASocket, _useMultiFileAuthState, _DisconnectReason, _fetchLatestBaileysVersion, _Browsers;
2
+
3
+ function loadBaileys() {
4
+ if (!_makeWASocket) {
5
+ const baileys = require('@whiskeysockets/baileys');
6
+ _makeWASocket = baileys.default;
7
+ _useMultiFileAuthState = baileys.useMultiFileAuthState;
8
+ _DisconnectReason = baileys.DisconnectReason;
9
+ _fetchLatestBaileysVersion = baileys.fetchLatestBaileysVersion;
10
+ _Browsers = baileys.Browsers;
11
+ }
12
+ return {
13
+ makeWASocket: _makeWASocket,
14
+ useMultiFileAuthState: _useMultiFileAuthState,
15
+ DisconnectReason: _DisconnectReason,
16
+ fetchLatestBaileysVersion: _fetchLatestBaileysVersion,
17
+ Browsers: _Browsers,
18
+ };
19
+ }
20
+
21
+ module.exports = { loadBaileys };