daemora 1.0.2 → 1.0.4
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 +106 -76
- package/SOUL.md +100 -28
- package/config/mcp.json +9 -9
- package/package.json +15 -8
- package/skills/apple-notes.md +0 -52
- package/skills/apple-reminders.md +1 -87
- package/skills/camsnap.md +20 -144
- package/skills/coding.md +7 -7
- package/skills/documents.md +6 -6
- package/skills/email.md +6 -6
- package/skills/gif-search.md +28 -171
- package/skills/healthcheck.md +21 -203
- package/skills/image-gen.md +24 -123
- package/skills/model-usage.md +18 -165
- package/skills/obsidian.md +28 -174
- package/skills/pdf.md +30 -181
- package/skills/research.md +6 -6
- package/skills/skill-creator.md +35 -111
- package/skills/spotify.md +2 -17
- package/skills/summarize.md +36 -193
- package/skills/things.md +23 -175
- package/skills/tmux.md +1 -91
- package/skills/trello.md +32 -157
- package/skills/video-frames.md +26 -166
- package/skills/weather.md +6 -6
- package/src/a2a/A2AClient.js +2 -2
- package/src/a2a/A2AServer.js +6 -6
- package/src/a2a/AgentCard.js +2 -2
- package/src/agents/SubAgentManager.js +61 -19
- package/src/agents/Supervisor.js +4 -4
- package/src/channels/BaseChannel.js +6 -6
- package/src/channels/BlueBubblesChannel.js +112 -0
- package/src/channels/DiscordChannel.js +8 -8
- package/src/channels/EmailChannel.js +54 -26
- package/src/channels/FeishuChannel.js +140 -0
- package/src/channels/GoogleChatChannel.js +8 -8
- package/src/channels/HttpChannel.js +2 -2
- package/src/channels/IRCChannel.js +144 -0
- package/src/channels/LineChannel.js +13 -13
- package/src/channels/MatrixChannel.js +97 -0
- package/src/channels/MattermostChannel.js +119 -0
- package/src/channels/NextcloudChannel.js +133 -0
- package/src/channels/NostrChannel.js +175 -0
- package/src/channels/SignalChannel.js +9 -9
- package/src/channels/SlackChannel.js +10 -10
- package/src/channels/TeamsChannel.js +10 -10
- package/src/channels/TelegramChannel.js +8 -8
- package/src/channels/TwitchChannel.js +128 -0
- package/src/channels/WhatsAppChannel.js +10 -10
- package/src/channels/ZaloChannel.js +119 -0
- package/src/channels/iMessageChannel.js +150 -0
- package/src/channels/index.js +241 -11
- package/src/cli.js +834 -37
- package/src/config/agentProfiles.js +19 -19
- package/src/config/channels.js +1 -1
- package/src/config/default.js +12 -7
- package/src/config/models.js +13 -3
- package/src/config/permissions.js +2 -2
- package/src/core/AgentLoop.js +13 -13
- package/src/core/Compaction.js +3 -3
- package/src/core/CostTracker.js +2 -2
- package/src/core/EventBus.js +15 -15
- package/src/core/TaskQueue.js +24 -7
- package/src/core/TaskRunner.js +19 -6
- package/src/daemon/DaemonManager.js +4 -4
- package/src/hooks/HookRunner.js +4 -4
- package/src/index.js +6 -2
- package/src/mcp/MCPAgentRunner.js +3 -3
- package/src/mcp/MCPClient.js +9 -9
- package/src/mcp/MCPManager.js +14 -14
- package/src/models/ModelRouter.js +2 -2
- package/src/safety/AuditLog.js +3 -3
- package/src/safety/CircuitBreaker.js +2 -2
- package/src/safety/CommandGuard.js +132 -0
- package/src/safety/FilesystemGuard.js +23 -3
- package/src/safety/GitRollback.js +5 -5
- package/src/safety/HumanApproval.js +9 -9
- package/src/safety/InputSanitizer.js +81 -8
- package/src/safety/PermissionGuard.js +2 -2
- package/src/safety/Sandbox.js +1 -1
- package/src/safety/SecretScanner.js +90 -28
- package/src/safety/SecretVault.js +2 -2
- package/src/scheduler/Heartbeat.js +3 -3
- package/src/scheduler/Scheduler.js +6 -6
- package/src/setup/theme.js +171 -66
- package/src/setup/wizard.js +432 -57
- package/src/skills/SkillLoader.js +145 -8
- package/src/storage/TaskStore.js +39 -15
- package/src/systemPrompt.js +45 -43
- package/src/tenants/TenantManager.js +79 -22
- package/src/tools/ToolRegistry.js +3 -3
- package/src/tools/applyPatch.js +2 -2
- package/src/tools/browserAutomation.js +4 -4
- package/src/tools/calendar.js +155 -0
- package/src/tools/clipboard.js +71 -0
- package/src/tools/contacts.js +138 -0
- package/src/tools/createDocument.js +2 -2
- package/src/tools/cronTool.js +14 -14
- package/src/tools/database.js +165 -0
- package/src/tools/editFile.js +10 -10
- package/src/tools/executeCommand.js +11 -3
- package/src/tools/generateImage.js +79 -0
- package/src/tools/gitTool.js +141 -0
- package/src/tools/glob.js +1 -1
- package/src/tools/googlePlaces.js +136 -0
- package/src/tools/grep.js +2 -2
- package/src/tools/iMessageTool.js +86 -0
- package/src/tools/imageAnalysis.js +3 -3
- package/src/tools/index.js +56 -2
- package/src/tools/makeVoiceCall.js +283 -0
- package/src/tools/manageAgents.js +2 -2
- package/src/tools/manageMCP.js +38 -20
- package/src/tools/memory.js +25 -32
- package/src/tools/messageChannel.js +1 -1
- package/src/tools/notification.js +90 -0
- package/src/tools/philipsHue.js +147 -0
- package/src/tools/projectTracker.js +8 -8
- package/src/tools/readFile.js +1 -1
- package/src/tools/readPDF.js +73 -0
- package/src/tools/screenCapture.js +6 -6
- package/src/tools/searchContent.js +2 -2
- package/src/tools/searchFiles.js +1 -1
- package/src/tools/sendEmail.js +79 -24
- package/src/tools/sendFile.js +4 -4
- package/src/tools/sonos.js +137 -0
- package/src/tools/sshTool.js +130 -0
- package/src/tools/textToSpeech.js +5 -5
- package/src/tools/transcribeAudio.js +4 -4
- package/src/tools/useMCP.js +4 -4
- package/src/tools/webFetch.js +2 -2
- package/src/tools/webSearch.js +1 -1
- package/src/utils/Embeddings.js +79 -0
- package/src/voice/VoiceSessionManager.js +170 -0
- package/src/voice/VoiceWebhook.js +188 -0
package/src/safety/Sandbox.js
CHANGED
|
@@ -2,7 +2,7 @@ import { blockedCommands } from "../config/permissions.js";
|
|
|
2
2
|
import eventBus from "../core/EventBus.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Sandbox
|
|
5
|
+
* Sandbox - command execution safety.
|
|
6
6
|
*
|
|
7
7
|
* Two modes:
|
|
8
8
|
* 1. Blocklist (default): Block known dangerous commands via regex patterns.
|
|
@@ -1,31 +1,87 @@
|
|
|
1
1
|
import eventBus from "../core/EventBus.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Secret Scanner
|
|
4
|
+
* Secret Scanner - detects and redacts sensitive data in tool I/O.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Two complementary layers:
|
|
7
|
+
*
|
|
8
|
+
* 1. Pattern-based redaction — regex patterns for known secret formats
|
|
9
|
+
* (AWS keys, GitHub tokens, OpenAI keys, JWTs, connection strings, etc.)
|
|
10
|
+
*
|
|
11
|
+
* 2. Blind env-var redaction — at startup, collect all process.env values whose
|
|
12
|
+
* names look like secrets (contain _KEY, _TOKEN, _SECRET, etc.) and redact
|
|
13
|
+
* their exact values from any tool output. This catches custom tokens like
|
|
14
|
+
* TELEGRAM_BOT_TOKEN, TWILIO_AUTH_TOKEN, DISCORD_BOT_TOKEN that don't match
|
|
15
|
+
* any known format pattern.
|
|
8
16
|
*/
|
|
9
17
|
|
|
10
18
|
const SECRET_PATTERNS = [
|
|
11
|
-
{ name: "AWS Access Key",
|
|
12
|
-
{ name: "AWS Secret Key",
|
|
13
|
-
{ name: "Generic API Key",
|
|
14
|
-
{ name: "Generic Secret",
|
|
15
|
-
{ name: "Private Key",
|
|
16
|
-
{ name: "GitHub Token",
|
|
17
|
-
{ name: "Slack Token",
|
|
18
|
-
{ name: "OpenAI Key",
|
|
19
|
-
{ name: "
|
|
20
|
-
{ name: "
|
|
21
|
-
{ name: "
|
|
19
|
+
{ name: "AWS Access Key", pattern: /AKIA[0-9A-Z]{16}/g },
|
|
20
|
+
{ name: "AWS Secret Key", pattern: /(?:aws_secret_access_key|secret_key)\s*[:=]\s*['"]?([A-Za-z0-9/+=]{40})['"]?/gi },
|
|
21
|
+
{ name: "Generic API Key", pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"]?([A-Za-z0-9_\-]{20,})['"]?/gi },
|
|
22
|
+
{ name: "Generic Secret", pattern: /(?:secret|password|passwd|pwd|token)\s*[:=]\s*['"]?([^\s'"]{8,})['"]?/gi },
|
|
23
|
+
{ name: "Private Key", pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g },
|
|
24
|
+
{ name: "GitHub Token", pattern: /gh[ps]_[A-Za-z0-9_]{36,}/g },
|
|
25
|
+
{ name: "Slack Token", pattern: /xox[bprs]-[A-Za-z0-9\-]{10,}/g },
|
|
26
|
+
{ name: "OpenAI Key", pattern: /sk-[A-Za-z0-9]{20,}/g },
|
|
27
|
+
{ name: "Anthropic Key", pattern: /sk-ant-[A-Za-z0-9\-_]{20,}/g },
|
|
28
|
+
{ name: "Google AI Key", pattern: /AIza[A-Za-z0-9\-_]{35}/g },
|
|
29
|
+
{ name: "Telegram Token", pattern: /\d{8,12}:[A-Za-z0-9_\-]{35}/g },
|
|
30
|
+
{ name: "Twilio SID", pattern: /AC[a-f0-9]{32}/g },
|
|
31
|
+
{ name: "Bearer Token", pattern: /Bearer\s+[A-Za-z0-9\-._~+\/]{20,}/g },
|
|
32
|
+
{ name: "Connection String", pattern: /(?:mongodb|postgres|mysql|redis):\/\/[^\s'"]+/gi },
|
|
33
|
+
{ name: "JWT", pattern: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g },
|
|
22
34
|
];
|
|
23
35
|
|
|
36
|
+
// ── Blind env-var redaction setup ─────────────────────────────────────────────
|
|
37
|
+
// Collect values of env vars whose NAMES suggest they hold secrets.
|
|
38
|
+
// Done once at module load — captures all secrets including ones that don't
|
|
39
|
+
// match any regex pattern above (e.g. DISCORD_BOT_TOKEN, LINE_CHANNEL_SECRET).
|
|
40
|
+
const SENSITIVE_NAME_PATTERN = /(_KEY|_TOKEN|_SECRET|_PASSWORD|_PASS|_PWD|_SID|_AUTH|_PRIVATE|_CREDENTIAL|_API|_WEBHOOK)$/i;
|
|
41
|
+
|
|
42
|
+
const _sensitiveEnvValues = new Set();
|
|
43
|
+
for (const [envKey, val] of Object.entries(process.env)) {
|
|
44
|
+
if (val && val.length >= 8 && SENSITIVE_NAME_PATTERN.test(envKey)) {
|
|
45
|
+
_sensitiveEnvValues.add(val);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
24
50
|
class SecretScanner {
|
|
25
51
|
constructor() {
|
|
26
52
|
this.detectionCount = 0;
|
|
27
53
|
}
|
|
28
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Add new secret values to the blind redaction set at runtime.
|
|
57
|
+
* Called by AgentLoop when tenant API keys are resolved.
|
|
58
|
+
* @param {string[]} values
|
|
59
|
+
*/
|
|
60
|
+
addKnownSecrets(values) {
|
|
61
|
+
for (const v of values) {
|
|
62
|
+
if (v && v.length >= 8) _sensitiveEnvValues.add(v);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Blind-redact exact env var values from text.
|
|
68
|
+
* Handles secrets that don't match any regex pattern.
|
|
69
|
+
* @param {string} text
|
|
70
|
+
* @returns {{ redacted: string, count: number }}
|
|
71
|
+
*/
|
|
72
|
+
_blindRedactEnv(text) {
|
|
73
|
+
let out = text;
|
|
74
|
+
let count = 0;
|
|
75
|
+
for (const val of _sensitiveEnvValues) {
|
|
76
|
+
if (out.includes(val)) {
|
|
77
|
+
// Use split+join instead of replaceAll for safety with special regex chars
|
|
78
|
+
out = out.split(val).join("[REDACTED:ENV_SECRET]");
|
|
79
|
+
count++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { redacted: out, count };
|
|
83
|
+
}
|
|
84
|
+
|
|
29
85
|
/**
|
|
30
86
|
* Scan text for secrets and return findings.
|
|
31
87
|
* @param {string} text - Text to scan
|
|
@@ -40,13 +96,11 @@ class SecretScanner {
|
|
|
40
96
|
let redacted = text;
|
|
41
97
|
|
|
42
98
|
for (const { name, pattern } of SECRET_PATTERNS) {
|
|
43
|
-
// Reset regex state
|
|
44
99
|
pattern.lastIndex = 0;
|
|
45
100
|
let match;
|
|
46
101
|
|
|
47
102
|
while ((match = pattern.exec(text)) !== null) {
|
|
48
103
|
const value = match[0];
|
|
49
|
-
// Skip very short matches or common false positives
|
|
50
104
|
if (value.length < 8) continue;
|
|
51
105
|
|
|
52
106
|
secrets.push({
|
|
@@ -55,8 +109,7 @@ class SecretScanner {
|
|
|
55
109
|
position: match.index,
|
|
56
110
|
});
|
|
57
111
|
|
|
58
|
-
|
|
59
|
-
redacted = redacted.replace(value, `[REDACTED:${name}]`);
|
|
112
|
+
redacted = redacted.split(value).join(`[REDACTED:${name}]`);
|
|
60
113
|
}
|
|
61
114
|
}
|
|
62
115
|
|
|
@@ -68,24 +121,33 @@ class SecretScanner {
|
|
|
68
121
|
});
|
|
69
122
|
}
|
|
70
123
|
|
|
71
|
-
return {
|
|
72
|
-
found: secrets.length > 0,
|
|
73
|
-
secrets,
|
|
74
|
-
redacted,
|
|
75
|
-
};
|
|
124
|
+
return { found: secrets.length > 0, secrets, redacted };
|
|
76
125
|
}
|
|
77
126
|
|
|
78
127
|
/**
|
|
79
128
|
* Scan and redact tool output before feeding back to agent.
|
|
129
|
+
* Applies BOTH pattern-based AND blind env-var redaction.
|
|
80
130
|
*/
|
|
81
131
|
redactOutput(text) {
|
|
132
|
+
// Layer 1: pattern-based
|
|
82
133
|
const result = this.scan(text);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
134
|
+
let out = result.redacted;
|
|
135
|
+
|
|
136
|
+
// Layer 2: blind env-var values
|
|
137
|
+
const { redacted: blindRedacted, count: blindCount } = this._blindRedactEnv(out);
|
|
138
|
+
out = blindRedacted;
|
|
139
|
+
|
|
140
|
+
const totalFound = result.secrets.length + blindCount;
|
|
141
|
+
if (totalFound > 0) {
|
|
142
|
+
const types = [
|
|
143
|
+
...result.secrets.map((s) => s.type),
|
|
144
|
+
...(blindCount > 0 ? [`ENV_SECRET(x${blindCount})`] : []),
|
|
145
|
+
];
|
|
146
|
+
console.log(` [SecretScanner] Redacted ${totalFound} secret(s): ${types.join(", ")}`);
|
|
147
|
+
this.detectionCount += blindCount;
|
|
87
148
|
}
|
|
88
|
-
|
|
149
|
+
|
|
150
|
+
return out;
|
|
89
151
|
}
|
|
90
152
|
|
|
91
153
|
/**
|
|
@@ -5,7 +5,7 @@ import { config } from "../config/default.js";
|
|
|
5
5
|
import eventBus from "../core/EventBus.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Secret Vault
|
|
8
|
+
* Secret Vault - encrypted secret storage.
|
|
9
9
|
*
|
|
10
10
|
* All API keys, tokens, and credentials are encrypted at rest using
|
|
11
11
|
* AES-256-GCM with a user-provided master passphrase.
|
|
@@ -88,7 +88,7 @@ class SecretVault {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
* Lock the vault
|
|
91
|
+
* Lock the vault - clear decrypted secrets from memory.
|
|
92
92
|
*/
|
|
93
93
|
lock() {
|
|
94
94
|
this.secrets = null;
|
|
@@ -5,7 +5,7 @@ import taskQueue from "../core/TaskQueue.js";
|
|
|
5
5
|
import eventBus from "../core/EventBus.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Heartbeat
|
|
8
|
+
* Heartbeat - periodic proactive check.
|
|
9
9
|
*
|
|
10
10
|
* Reads HEARTBEAT.md for user-defined checks.
|
|
11
11
|
* Every N minutes, creates a task: "Check status per HEARTBEAT.md"
|
|
@@ -28,12 +28,12 @@ class Heartbeat {
|
|
|
28
28
|
*/
|
|
29
29
|
start() {
|
|
30
30
|
if (!config.daemonMode) {
|
|
31
|
-
console.log(`[Heartbeat] Skipped
|
|
31
|
+
console.log(`[Heartbeat] Skipped - daemon mode not enabled`);
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (!existsSync(this.heartbeatPath)) {
|
|
36
|
-
console.log(`[Heartbeat] No HEARTBEAT.md found
|
|
36
|
+
console.log(`[Heartbeat] No HEARTBEAT.md found - heartbeat disabled`);
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -7,7 +7,7 @@ import taskQueue from "../core/TaskQueue.js";
|
|
|
7
7
|
import eventBus from "../core/EventBus.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Scheduler
|
|
10
|
+
* Scheduler - cron-based task scheduling.
|
|
11
11
|
*
|
|
12
12
|
* Schedules stored in data/schedules.json.
|
|
13
13
|
* Each schedule creates a Task when its cron expression triggers.
|
|
@@ -22,7 +22,7 @@ class Scheduler {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Start the scheduler
|
|
25
|
+
* Start the scheduler - load and activate all schedules.
|
|
26
26
|
*/
|
|
27
27
|
start() {
|
|
28
28
|
this.loadSchedules();
|
|
@@ -35,12 +35,12 @@ class Scheduler {
|
|
|
35
35
|
|
|
36
36
|
this.running = true;
|
|
37
37
|
console.log(
|
|
38
|
-
`[Scheduler] Started
|
|
38
|
+
`[Scheduler] Started - ${this.schedules.size} schedule(s) loaded`
|
|
39
39
|
);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* Stop the scheduler
|
|
43
|
+
* Stop the scheduler - cancel all cron jobs.
|
|
44
44
|
*/
|
|
45
45
|
stop() {
|
|
46
46
|
for (const [id, job] of this.cronJobs) {
|
|
@@ -92,7 +92,7 @@ class Scheduler {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* Update an existing schedule (patch
|
|
95
|
+
* Update an existing schedule (patch - only fields provided are changed).
|
|
96
96
|
* @param {string} id - Schedule ID (full or prefix)
|
|
97
97
|
* @param {object} patch - Fields to update: cronExpression, taskInput, name, enabled
|
|
98
98
|
*/
|
|
@@ -105,7 +105,7 @@ class Scheduler {
|
|
|
105
105
|
const schedule = this.schedules.get(fullId);
|
|
106
106
|
if (!schedule) throw new Error(`Schedule not found: ${id}`);
|
|
107
107
|
|
|
108
|
-
// Update cron expression
|
|
108
|
+
// Update cron expression - requires restarting the cron job
|
|
109
109
|
if (patch.cronExpression && patch.cronExpression !== schedule.cronExpression) {
|
|
110
110
|
if (!cron.validate(patch.cronExpression)) {
|
|
111
111
|
throw new Error(`Invalid cron expression: ${patch.cronExpression}`);
|
package/src/setup/theme.js
CHANGED
|
@@ -1,110 +1,215 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Daemora CLI Theme
|
|
5
|
-
*
|
|
4
|
+
* Daemora CLI Theme
|
|
5
|
+
* Color palette matches the UI exactly:
|
|
6
|
+
* Cyan #00d9ff — primary brand / headings / CTAs
|
|
7
|
+
* Teal #4ECDC4 — secondary / commands / accents
|
|
8
|
+
* Red #ff4458 — danger / features (matches logo horns)
|
|
9
|
+
* Green #00ff88 — success / security / live badges
|
|
10
|
+
* Amber #ffaa00 — warning / scheduling / [NEW] badges
|
|
11
|
+
* Muted #64748b — slate-500 body text
|
|
12
|
+
* Dim #94a3b8 — slate-400 captions
|
|
6
13
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
export const P = {
|
|
15
|
+
cyan: "#00d9ff",
|
|
16
|
+
teal: "#4ECDC4",
|
|
17
|
+
red: "#ff4458",
|
|
18
|
+
green: "#00ff88",
|
|
19
|
+
amber: "#ffaa00",
|
|
20
|
+
muted: "#64748b",
|
|
21
|
+
dim: "#94a3b8",
|
|
22
|
+
border: "#1f1f2e",
|
|
23
|
+
// semantic aliases
|
|
24
|
+
get brand() { return this.cyan; },
|
|
25
|
+
get accent() { return this.teal; },
|
|
26
|
+
get success() { return this.green; },
|
|
27
|
+
get warning() { return this.amber; },
|
|
28
|
+
get error() { return this.red; },
|
|
17
29
|
};
|
|
18
30
|
|
|
19
31
|
export const t = {
|
|
20
|
-
brand: (s) => chalk.hex(P.
|
|
21
|
-
accent: (s) => chalk.hex(P.
|
|
22
|
-
success: (s) => chalk.hex(P.
|
|
23
|
-
warning: (s) => chalk.hex(P.
|
|
24
|
-
error: (s) => chalk.hex(P.
|
|
32
|
+
brand: (s) => chalk.hex(P.cyan)(s),
|
|
33
|
+
accent: (s) => chalk.hex(P.teal)(s),
|
|
34
|
+
success: (s) => chalk.hex(P.green)(s),
|
|
35
|
+
warning: (s) => chalk.hex(P.amber)(s),
|
|
36
|
+
error: (s) => chalk.hex(P.red)(s),
|
|
25
37
|
muted: (s) => chalk.hex(P.muted)(s),
|
|
26
|
-
info: (s) => chalk.hex(P.info)(s),
|
|
27
38
|
dim: (s) => chalk.hex(P.dim)(s),
|
|
28
39
|
bold: (s) => chalk.bold(s),
|
|
29
|
-
h: (s) => chalk.bold.hex(P.
|
|
30
|
-
|
|
40
|
+
h: (s) => chalk.bold.hex(P.cyan)(s),
|
|
41
|
+
h2: (s) => chalk.bold.hex(P.teal)(s),
|
|
42
|
+
cmd: (s) => chalk.hex(P.teal)(s),
|
|
43
|
+
new: (s) => chalk.hex(P.amber)(s),
|
|
31
44
|
};
|
|
32
45
|
|
|
33
46
|
export const S = {
|
|
34
|
-
check: chalk.hex(P.
|
|
35
|
-
cross: chalk.hex(P.
|
|
36
|
-
arrow: chalk.hex(P.
|
|
37
|
-
dot: chalk.hex(P.muted)("
|
|
38
|
-
bar: chalk.hex(P.
|
|
39
|
-
dash: chalk.hex(P.
|
|
40
|
-
diamond: chalk.hex(P.
|
|
41
|
-
shield: chalk.hex(P.
|
|
42
|
-
lock: chalk.hex(P.
|
|
43
|
-
gear: chalk.hex(P.muted)("
|
|
44
|
-
bolt: chalk.hex(P.
|
|
47
|
+
check: chalk.hex(P.green)("✔"),
|
|
48
|
+
cross: chalk.hex(P.red)("✘"),
|
|
49
|
+
arrow: chalk.hex(P.cyan)("▸"),
|
|
50
|
+
dot: chalk.hex(P.muted)("·"),
|
|
51
|
+
bar: chalk.hex(P.muted)("│"),
|
|
52
|
+
dash: chalk.hex(P.border)("─"),
|
|
53
|
+
diamond: chalk.hex(P.cyan)("◆"),
|
|
54
|
+
shield: chalk.hex(P.green)("◈"),
|
|
55
|
+
lock: chalk.hex(P.amber)("▣"),
|
|
56
|
+
gear: chalk.hex(P.muted)("○"),
|
|
57
|
+
bolt: chalk.hex(P.cyan)("●"),
|
|
58
|
+
eye: chalk.hex(P.red)("◉"),
|
|
59
|
+
star: chalk.hex(P.amber)("★"),
|
|
45
60
|
};
|
|
46
61
|
|
|
62
|
+
// ─── Gradient helpers ──────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
/** Interpolate a single hex colour component */
|
|
65
|
+
function lerp(a, b, t) { return Math.round(a + (b - a) * t); }
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Render a string with a left→right cyan→teal gradient.
|
|
69
|
+
* One chalk call per visible character — fast enough for a one-shot banner.
|
|
70
|
+
*/
|
|
71
|
+
function gradientLine(line) {
|
|
72
|
+
const [cyR, cyG, cyB] = [0x00, 0xd9, 0xff]; // #00d9ff
|
|
73
|
+
const [tlR, tlG, tlB] = [0x4e, 0xcd, 0xc4]; // #4ECDC4
|
|
74
|
+
let out = "";
|
|
75
|
+
const len = line.length;
|
|
76
|
+
for (let i = 0; i < len; i++) {
|
|
77
|
+
const frac = len > 1 ? i / (len - 1) : 0;
|
|
78
|
+
out += chalk.rgb(
|
|
79
|
+
lerp(cyR, tlR, frac),
|
|
80
|
+
lerp(cyG, tlG, frac),
|
|
81
|
+
lerp(cyB, tlB, frac),
|
|
82
|
+
)(line[i]);
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── Banner ────────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* DAEMORA ASCII art — figlet "Doom" font.
|
|
91
|
+
* Width: ~65 chars — fits a standard 80-col terminal.
|
|
92
|
+
*/
|
|
93
|
+
const DAEMORA_ART = [
|
|
94
|
+
"██████╗ █████╗ ███████╗███╗ ███╗ ██████╗ ██████╗ █████╗ ",
|
|
95
|
+
"██╔══██╗██╔══██╗██╔════╝████╗ ████║██╔═══██╗██╔══██╗██╔══██╗",
|
|
96
|
+
"██║ ██║███████║█████╗ ██╔████╔██║██║ ██║██████╔╝███████║",
|
|
97
|
+
"██║ ██║██╔══██║██╔══╝ ██║╚██╔╝██║██║ ██║██╔══██╗██╔══██║",
|
|
98
|
+
"██████╔╝██║ ██║███████╗██║ ╚═╝ ██║╚██████╔╝██║ ██║██║ ██║",
|
|
99
|
+
"╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝",
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
/** Logo mark — small demon eye motif drawn with ASCII */
|
|
103
|
+
function logoMark() {
|
|
104
|
+
return [
|
|
105
|
+
chalk.hex(P.red)(" ▲ ▲"),
|
|
106
|
+
chalk.hex(P.red)(" ▲ ▲ ▲ ▲"),
|
|
107
|
+
chalk.hex(P.dim)(" ╔═══════════════════╗"),
|
|
108
|
+
chalk.hex(P.dim)(" ║") +
|
|
109
|
+
chalk.hex(P.muted)(" ") +
|
|
110
|
+
chalk.hex(P.red)("◤▬▬▬▬◥") +
|
|
111
|
+
chalk.hex(P.muted)(" ") +
|
|
112
|
+
chalk.hex(P.red)("◤▬▬▬▬◥") +
|
|
113
|
+
chalk.hex(P.muted)(" ") +
|
|
114
|
+
chalk.hex(P.dim)("║"),
|
|
115
|
+
chalk.hex(P.dim)(" ║") +
|
|
116
|
+
chalk.hex(P.muted)(" ") +
|
|
117
|
+
chalk.hex(P.red)("◉") +
|
|
118
|
+
chalk.hex(P.muted)(" ") +
|
|
119
|
+
chalk.hex(P.red)("◉") +
|
|
120
|
+
chalk.hex(P.muted)(" ") +
|
|
121
|
+
chalk.hex(P.dim)("║"),
|
|
122
|
+
chalk.hex(P.dim)(" ╚═══════════════════╝"),
|
|
123
|
+
chalk.hex(P.cyan)(" ◦ ◦"),
|
|
124
|
+
].join("\n");
|
|
125
|
+
}
|
|
126
|
+
|
|
47
127
|
export function banner() {
|
|
48
|
-
const w =
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
console.log(
|
|
128
|
+
const w = 67;
|
|
129
|
+
const topLine = chalk.hex(P.cyan)("━".repeat(w));
|
|
130
|
+
const botLine = chalk.hex(P.teal)("─".repeat(w));
|
|
131
|
+
|
|
132
|
+
console.log("\n" + topLine);
|
|
53
133
|
console.log("");
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
console.log(chalk.bold.hex(P.brand)(
|
|
61
|
-
" \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588"
|
|
62
|
-
));
|
|
63
|
-
console.log(chalk.bold.hex(P.brand)(
|
|
64
|
-
" \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588"
|
|
65
|
-
));
|
|
66
|
-
console.log(chalk.bold.hex(P.brand)(
|
|
67
|
-
" \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588"
|
|
68
|
-
));
|
|
134
|
+
|
|
135
|
+
// Gradient DAEMORA lettering
|
|
136
|
+
for (const line of DAEMORA_ART) {
|
|
137
|
+
console.log(" " + gradientLine(line));
|
|
138
|
+
}
|
|
139
|
+
|
|
69
140
|
console.log("");
|
|
70
|
-
console.log(
|
|
71
|
-
|
|
141
|
+
console.log(
|
|
142
|
+
" " +
|
|
143
|
+
chalk.hex(P.muted)(" ") +
|
|
144
|
+
chalk.hex(P.cyan)("◉") +
|
|
145
|
+
chalk.hex(P.dim)(" Your 24/7 AI Agent ") +
|
|
146
|
+
chalk.hex(P.red)("◉"),
|
|
147
|
+
);
|
|
148
|
+
console.log(botLine);
|
|
72
149
|
console.log("");
|
|
73
150
|
}
|
|
74
151
|
|
|
152
|
+
// ─── Step header (progress bar) ────────────────────────────────────────────
|
|
153
|
+
|
|
75
154
|
export function stepHeader(current, total, title) {
|
|
76
|
-
const filled = Math.round((current / total) *
|
|
77
|
-
const empty
|
|
78
|
-
const bar =
|
|
79
|
-
|
|
80
|
-
|
|
155
|
+
const filled = Math.round((current / total) * 22);
|
|
156
|
+
const empty = 22 - filled;
|
|
157
|
+
const bar =
|
|
158
|
+
chalk.hex(P.cyan)("█".repeat(filled)) +
|
|
159
|
+
chalk.hex(P.border)("░".repeat(empty));
|
|
160
|
+
const label = chalk.hex(P.muted)(`[${current}/${total}]`);
|
|
161
|
+
const w = 67;
|
|
162
|
+
const line = chalk.hex(P.border)("─".repeat(w));
|
|
163
|
+
console.log(`\n${line}`);
|
|
164
|
+
console.log(` ${bar} ${label} ${chalk.bold.hex(P.teal)(title)}`);
|
|
165
|
+
console.log(`${line}\n`);
|
|
81
166
|
}
|
|
82
167
|
|
|
168
|
+
// ─── Key/value row ─────────────────────────────────────────────────────────
|
|
169
|
+
|
|
83
170
|
export function kv(key, value) {
|
|
84
171
|
console.log(` ${S.bar} ${t.muted(key)} ${value}`);
|
|
85
172
|
}
|
|
86
173
|
|
|
174
|
+
// ─── Summary table ─────────────────────────────────────────────────────────
|
|
175
|
+
|
|
87
176
|
export function summaryTable(title, rows) {
|
|
88
177
|
const maxKey = Math.max(...rows.map(([k]) => k.length), 10);
|
|
89
|
-
const w = maxKey +
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
console.log(
|
|
178
|
+
const w = maxKey + 34;
|
|
179
|
+
const topLine = chalk.hex(P.cyan)("━".repeat(w));
|
|
180
|
+
const rowLine = chalk.hex(P.border)("─".repeat(w));
|
|
181
|
+
console.log(`\n ${chalk.bold.hex(P.cyan)(title)}`);
|
|
182
|
+
console.log(` ${topLine}`);
|
|
93
183
|
for (const [key, val] of rows) {
|
|
94
184
|
const k = t.muted(key.padEnd(maxKey));
|
|
95
185
|
console.log(` ${S.bar} ${k} ${val}`);
|
|
96
186
|
}
|
|
97
|
-
console.log(` ${
|
|
187
|
+
console.log(` ${rowLine}`);
|
|
98
188
|
}
|
|
99
189
|
|
|
190
|
+
// ─── Complete banner ────────────────────────────────────────────────────────
|
|
191
|
+
|
|
100
192
|
export function completeBanner(lines) {
|
|
101
|
-
const w =
|
|
102
|
-
const line = chalk.hex(P.
|
|
193
|
+
const w = 67;
|
|
194
|
+
const line = chalk.hex(P.green)("━".repeat(w));
|
|
103
195
|
console.log(`\n${line}`);
|
|
104
|
-
console.log(` ${S.check} ${chalk.bold.hex(P.
|
|
196
|
+
console.log(` ${S.check} ${chalk.bold.hex(P.green)("Setup Complete")}`);
|
|
197
|
+
console.log(` ${chalk.hex(P.dim)("─".repeat(w - 2))}`);
|
|
105
198
|
console.log("");
|
|
106
199
|
for (const l of lines) {
|
|
107
200
|
console.log(` ${l}`);
|
|
108
201
|
}
|
|
109
202
|
console.log(`${line}\n`);
|
|
110
203
|
}
|
|
204
|
+
|
|
205
|
+
// ─── Section header (for info commands) ────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
export function sectionHeader(title, subtitle = "") {
|
|
208
|
+
const w = 67;
|
|
209
|
+
const topLine = chalk.hex(P.cyan)("━".repeat(w));
|
|
210
|
+
const botLine = chalk.hex(P.border)("─".repeat(w));
|
|
211
|
+
console.log(`\n${topLine}`);
|
|
212
|
+
const sub = subtitle ? ` ${chalk.hex(P.muted)(subtitle)}` : "";
|
|
213
|
+
console.log(` ${chalk.bold.hex(P.cyan)(title)}${sub}`);
|
|
214
|
+
console.log(`${botLine}`);
|
|
215
|
+
}
|