aiden-runtime 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +465 -0
- package/config/devos.config.json +186 -0
- package/config/hardware.json +9 -0
- package/config/model-selection.json +7 -0
- package/config/setup-complete.json +20 -0
- package/dist/api/routes/computerUse.js +112 -0
- package/dist/api/server.js +6870 -0
- package/dist/bin/npx-init.js +71 -0
- package/dist/coordination/commandGate.js +115 -0
- package/dist/coordination/livePulse.js +127 -0
- package/dist/core/agentLoop.js +2718 -0
- package/dist/core/agentShield.js +231 -0
- package/dist/core/aidenIdentity.js +215 -0
- package/dist/core/aidenPersonality.js +166 -0
- package/dist/core/aidenSdk.js +374 -0
- package/dist/core/asyncTasks.js +82 -0
- package/dist/core/auditTrail.js +61 -0
- package/dist/core/auxiliaryClient.js +114 -0
- package/dist/core/bgLLM.js +108 -0
- package/dist/core/bm25.js +68 -0
- package/dist/core/callbackSystem.js +64 -0
- package/dist/core/channels/adapter.js +6 -0
- package/dist/core/channels/discord.js +173 -0
- package/dist/core/channels/email.js +253 -0
- package/dist/core/channels/imessage.js +164 -0
- package/dist/core/channels/manager.js +96 -0
- package/dist/core/channels/signal.js +140 -0
- package/dist/core/channels/slack.js +139 -0
- package/dist/core/channels/twilio.js +144 -0
- package/dist/core/channels/webhook.js +186 -0
- package/dist/core/channels/whatsapp.js +185 -0
- package/dist/core/clarifyBus.js +75 -0
- package/dist/core/codeInterpreter.js +82 -0
- package/dist/core/computerControl.js +439 -0
- package/dist/core/conversationMemory.js +334 -0
- package/dist/core/costTracker.js +221 -0
- package/dist/core/cronManager.js +217 -0
- package/dist/core/deepKB.js +77 -0
- package/dist/core/doctor.js +279 -0
- package/dist/core/dreamEngine.js +334 -0
- package/dist/core/entityGraph.js +169 -0
- package/dist/core/eventBus.js +16 -0
- package/dist/core/evolutionAnalyzer.js +153 -0
- package/dist/core/executionLoop.js +309 -0
- package/dist/core/executor.js +224 -0
- package/dist/core/failureAnalyzer.js +166 -0
- package/dist/core/fastPathExpansion.js +82 -0
- package/dist/core/faultEngine.js +106 -0
- package/dist/core/featureGates.js +70 -0
- package/dist/core/fileIngestion.js +113 -0
- package/dist/core/gateway.js +97 -0
- package/dist/core/goalTracker.js +75 -0
- package/dist/core/growthEngine.js +168 -0
- package/dist/core/hardwareDetector.js +98 -0
- package/dist/core/hooks.js +45 -0
- package/dist/core/httpKeepalive.js +46 -0
- package/dist/core/hybridSearch.js +101 -0
- package/dist/core/importers.js +164 -0
- package/dist/core/instinctSystem.js +223 -0
- package/dist/core/knowledgeBase.js +351 -0
- package/dist/core/learningMemory.js +121 -0
- package/dist/core/lessonsBrowser.js +125 -0
- package/dist/core/licenseManager.js +399 -0
- package/dist/core/logBuffer.js +85 -0
- package/dist/core/machineId.js +87 -0
- package/dist/core/mcpClient.js +442 -0
- package/dist/core/memoryDistiller.js +165 -0
- package/dist/core/memoryExtractor.js +212 -0
- package/dist/core/memoryIds.js +213 -0
- package/dist/core/memoryPreamble.js +113 -0
- package/dist/core/memoryQuery.js +136 -0
- package/dist/core/memoryRecall.js +140 -0
- package/dist/core/memoryStrategy.js +201 -0
- package/dist/core/messageValidator.js +85 -0
- package/dist/core/modelDiscovery.js +108 -0
- package/dist/core/modelRouter.js +118 -0
- package/dist/core/morningBriefing.js +203 -0
- package/dist/core/multiGoalValidator.js +51 -0
- package/dist/core/parallelExecutor.js +43 -0
- package/dist/core/passiveSkillObserver.js +204 -0
- package/dist/core/paths.js +57 -0
- package/dist/core/patternDetector.js +83 -0
- package/dist/core/planResponseRepair.js +64 -0
- package/dist/core/planTool.js +111 -0
- package/dist/core/playwrightBridge.js +356 -0
- package/dist/core/pluginSystem.js +121 -0
- package/dist/core/privateMode.js +85 -0
- package/dist/core/reactLoop.js +156 -0
- package/dist/core/recipeEngine.js +166 -0
- package/dist/core/responseCache.js +128 -0
- package/dist/core/runSandbox.js +132 -0
- package/dist/core/sandboxRunner.js +200 -0
- package/dist/core/scheduler.js +543 -0
- package/dist/core/secretScanner.js +49 -0
- package/dist/core/semanticMemory.js +223 -0
- package/dist/core/sessionMemory.js +259 -0
- package/dist/core/sessionRouter.js +91 -0
- package/dist/core/sessionSearch.js +163 -0
- package/dist/core/setupWizard.js +225 -0
- package/dist/core/skillImporter.js +303 -0
- package/dist/core/skillLibrary.js +144 -0
- package/dist/core/skillLoader.js +471 -0
- package/dist/core/skillTeacher.js +352 -0
- package/dist/core/skillValidator.js +210 -0
- package/dist/core/skillWriter.js +384 -0
- package/dist/core/slashAsTool.js +226 -0
- package/dist/core/spawnManager.js +197 -0
- package/dist/core/statusVerbs.js +43 -0
- package/dist/core/swarmManager.js +109 -0
- package/dist/core/taskQueue.js +119 -0
- package/dist/core/taskRecovery.js +128 -0
- package/dist/core/taskState.js +168 -0
- package/dist/core/telegramBot.js +152 -0
- package/dist/core/todoManager.js +70 -0
- package/dist/core/toolNameRepair.js +71 -0
- package/dist/core/toolRegistry.js +2730 -0
- package/dist/core/tools/calendarTool.js +98 -0
- package/dist/core/tools/companyFilingsTool.js +98 -0
- package/dist/core/tools/gmailTool.js +87 -0
- package/dist/core/tools/marketDataTool.js +135 -0
- package/dist/core/tools/socialResearchTool.js +121 -0
- package/dist/core/truthCheck.js +57 -0
- package/dist/core/updateChecker.js +74 -0
- package/dist/core/userCognitionProfile.js +238 -0
- package/dist/core/userProfile.js +341 -0
- package/dist/core/version.js +5 -0
- package/dist/core/visionAnalyze.js +161 -0
- package/dist/core/voice/audio.js +187 -0
- package/dist/core/voice/stt.js +226 -0
- package/dist/core/voice/tts.js +310 -0
- package/dist/core/voiceInput.js +118 -0
- package/dist/core/voiceOutput.js +130 -0
- package/dist/core/webSearch.js +326 -0
- package/dist/core/workflowTracker.js +72 -0
- package/dist/core/workspaceMemory.js +54 -0
- package/dist/core/youtubeTranscript.js +224 -0
- package/dist/integrations/computerUse/apiRegistry.js +113 -0
- package/dist/integrations/computerUse/screenAgent.js +203 -0
- package/dist/integrations/computerUse/visionLoop.js +296 -0
- package/dist/memory/memoryLayers.js +143 -0
- package/dist/providers/boa.js +93 -0
- package/dist/providers/cerebras.js +70 -0
- package/dist/providers/custom.js +89 -0
- package/dist/providers/gemini.js +82 -0
- package/dist/providers/groq.js +92 -0
- package/dist/providers/index.js +149 -0
- package/dist/providers/nvidia.js +70 -0
- package/dist/providers/ollama.js +99 -0
- package/dist/providers/openrouter.js +74 -0
- package/dist/providers/router.js +497 -0
- package/dist/providers/types.js +6 -0
- package/dist/security/browserVault.js +129 -0
- package/dist/security/dataGuard.js +89 -0
- package/dist/tools/eonetTool.js +72 -0
- package/dist/types/computerUse.js +2 -0
- package/dist/types/executor.js +2 -0
- package/dist-bundle/cli.js +357859 -0
- package/package.json +256 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.rebuildIndex = rebuildIndex;
|
|
11
|
+
exports.getIndex = getIndex;
|
|
12
|
+
exports.searchSessions = searchSessions;
|
|
13
|
+
exports.getIndexSize = getIndexSize;
|
|
14
|
+
// core/sessionSearch.ts — BM25 full-text index over workspace session files.
|
|
15
|
+
//
|
|
16
|
+
// Indexes workspace/sessions/*.md and workspace/memory/*.md on first call,
|
|
17
|
+
// then incrementally re-indexes any files newer than the last rebuild.
|
|
18
|
+
// Exposes searchSessions(query, topK) for synchronous ranked results.
|
|
19
|
+
//
|
|
20
|
+
// BM25 parameters: k1=1.5 b=0.75 (standard defaults)
|
|
21
|
+
const fs_1 = __importDefault(require("fs"));
|
|
22
|
+
const path_1 = __importDefault(require("path"));
|
|
23
|
+
const K1 = 1.5;
|
|
24
|
+
const B = 0.75;
|
|
25
|
+
let _index = null;
|
|
26
|
+
// ── Tokenizer ─────────────────────────────────────────────────
|
|
27
|
+
function tokenize(text) {
|
|
28
|
+
return text
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
31
|
+
.split(/\s+/)
|
|
32
|
+
.filter(t => t.length > 1);
|
|
33
|
+
}
|
|
34
|
+
// ── Index builder ─────────────────────────────────────────────
|
|
35
|
+
function getSessionDirs() {
|
|
36
|
+
const base = process.cwd();
|
|
37
|
+
return [
|
|
38
|
+
path_1.default.join(base, 'workspace', 'sessions'),
|
|
39
|
+
path_1.default.join(base, 'workspace', 'memory'),
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
function loadDocuments() {
|
|
43
|
+
const docs = [];
|
|
44
|
+
for (const dir of getSessionDirs()) {
|
|
45
|
+
if (!fs_1.default.existsSync(dir))
|
|
46
|
+
continue;
|
|
47
|
+
let entries;
|
|
48
|
+
try {
|
|
49
|
+
entries = fs_1.default.readdirSync(dir);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
for (const fname of entries) {
|
|
55
|
+
if (!fname.endsWith('.md') && !fname.endsWith('.json'))
|
|
56
|
+
continue;
|
|
57
|
+
const fpath = path_1.default.join(dir, fname);
|
|
58
|
+
let raw = '';
|
|
59
|
+
try {
|
|
60
|
+
raw = fs_1.default.readFileSync(fpath, 'utf-8');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
let content = raw;
|
|
66
|
+
// For JSON, stringify to searchable text
|
|
67
|
+
if (fname.endsWith('.json')) {
|
|
68
|
+
try {
|
|
69
|
+
content = JSON.stringify(JSON.parse(raw), null, 1);
|
|
70
|
+
}
|
|
71
|
+
catch { /* keep raw */ }
|
|
72
|
+
}
|
|
73
|
+
const stat = fs_1.default.statSync(fpath);
|
|
74
|
+
const firstH1 = raw.match(/^#\s+(.+)$/m)?.[1] ?? fname.replace(/\.\w+$/, '');
|
|
75
|
+
docs.push({
|
|
76
|
+
id: fname.replace(/\.\w+$/, ''),
|
|
77
|
+
path: fpath,
|
|
78
|
+
title: firstH1.trim(),
|
|
79
|
+
content,
|
|
80
|
+
mtime: stat.mtimeMs,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return docs;
|
|
85
|
+
}
|
|
86
|
+
function buildIndex(docs) {
|
|
87
|
+
const tf = [];
|
|
88
|
+
const df = new Map();
|
|
89
|
+
let totalLen = 0;
|
|
90
|
+
for (const doc of docs) {
|
|
91
|
+
const tokens = tokenize(doc.content);
|
|
92
|
+
totalLen += tokens.length;
|
|
93
|
+
const counts = new Map();
|
|
94
|
+
for (const t of tokens)
|
|
95
|
+
counts.set(t, (counts.get(t) ?? 0) + 1);
|
|
96
|
+
tf.push(counts);
|
|
97
|
+
for (const term of counts.keys())
|
|
98
|
+
df.set(term, (df.get(term) ?? 0) + 1);
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
docs,
|
|
102
|
+
tf,
|
|
103
|
+
df,
|
|
104
|
+
avgdl: docs.length ? totalLen / docs.length : 1,
|
|
105
|
+
built: Date.now(),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** Rebuild index from disk. */
|
|
109
|
+
function rebuildIndex() {
|
|
110
|
+
const docs = loadDocuments();
|
|
111
|
+
_index = buildIndex(docs);
|
|
112
|
+
return _index;
|
|
113
|
+
}
|
|
114
|
+
/** Get or lazily build the index. Rebuilds if > 5 minutes old. */
|
|
115
|
+
function getIndex() {
|
|
116
|
+
if (!_index || Date.now() - _index.built > 5 * 60 * 1000) {
|
|
117
|
+
_index = rebuildIndex();
|
|
118
|
+
}
|
|
119
|
+
return _index;
|
|
120
|
+
}
|
|
121
|
+
// ── BM25 scoring ─────────────────────────────────────────────
|
|
122
|
+
function bm25Score(idx, docI, query) {
|
|
123
|
+
const N = idx.docs.length;
|
|
124
|
+
const dl = Array.from(idx.tf[docI].values()).reduce((a, b) => a + b, 0);
|
|
125
|
+
let score = 0;
|
|
126
|
+
for (const term of query) {
|
|
127
|
+
const freq = idx.tf[docI].get(term) ?? 0;
|
|
128
|
+
if (freq === 0)
|
|
129
|
+
continue;
|
|
130
|
+
const df = idx.df.get(term) ?? 0;
|
|
131
|
+
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
132
|
+
const tf = (freq * (K1 + 1)) / (freq + K1 * (1 - B + B * (dl / idx.avgdl)));
|
|
133
|
+
score += idf * tf;
|
|
134
|
+
}
|
|
135
|
+
return score;
|
|
136
|
+
}
|
|
137
|
+
// ── Public search API ─────────────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* Full-text BM25 search over indexed session/memory files.
|
|
140
|
+
* @param query Natural-language query string
|
|
141
|
+
* @param topK Maximum results to return (default 5)
|
|
142
|
+
* @returns Hits sorted by descending BM25 score
|
|
143
|
+
*/
|
|
144
|
+
function searchSessions(query, topK = 5) {
|
|
145
|
+
const idx = getIndex();
|
|
146
|
+
if (!idx.docs.length)
|
|
147
|
+
return [];
|
|
148
|
+
const tokens = tokenize(query);
|
|
149
|
+
if (!tokens.length)
|
|
150
|
+
return [];
|
|
151
|
+
const scored = idx.docs.map((doc, i) => ({
|
|
152
|
+
doc,
|
|
153
|
+
score: bm25Score(idx, i, tokens),
|
|
154
|
+
}));
|
|
155
|
+
return scored
|
|
156
|
+
.filter(h => h.score > 0)
|
|
157
|
+
.sort((a, b) => b.score - a.score)
|
|
158
|
+
.slice(0, topK);
|
|
159
|
+
}
|
|
160
|
+
/** Returns the number of indexed documents. */
|
|
161
|
+
function getIndexSize() {
|
|
162
|
+
return getIndex().docs.length;
|
|
163
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.runSetupWizard = runSetupWizard;
|
|
11
|
+
exports.isSetupComplete = isSetupComplete;
|
|
12
|
+
// core/setupWizard.ts — First-boot setup wizard.
|
|
13
|
+
// Sprint 25: 3-case smart UX:
|
|
14
|
+
// Case 1 — All good models already installed → confirm + use
|
|
15
|
+
// Case 2 — Gaps or user wants upgrades → show plan + pull
|
|
16
|
+
// Case 3 — Skip/offline → save what exists with fallbacks
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const readline_1 = __importDefault(require("readline"));
|
|
20
|
+
const child_process_1 = require("child_process");
|
|
21
|
+
const hardwareDetector_1 = require("./hardwareDetector");
|
|
22
|
+
const modelRouter_1 = require("./modelRouter");
|
|
23
|
+
const livePulse_1 = require("../coordination/livePulse");
|
|
24
|
+
const CONFIG_DIR = path_1.default.join(process.cwd(), 'config');
|
|
25
|
+
const MODEL_CONFIG = path_1.default.join(CONFIG_DIR, 'model-selection.json');
|
|
26
|
+
const SETUP_FLAG = path_1.default.join(CONFIG_DIR, 'setup-complete.json');
|
|
27
|
+
const TASK_TYPES = ['chat', 'code', 'vision', 'reasoning', 'embedding'];
|
|
28
|
+
function ask(q, nonTtyDefault = 'skip') {
|
|
29
|
+
// When running as an Electron child process or in any non-interactive context
|
|
30
|
+
// (no TTY attached to stdin), auto-answer with the default to avoid hanging.
|
|
31
|
+
if (!process.stdin.isTTY) {
|
|
32
|
+
console.log(`${q}[non-interactive: auto-answering "${nonTtyDefault}"]`);
|
|
33
|
+
return Promise.resolve(nonTtyDefault);
|
|
34
|
+
}
|
|
35
|
+
const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
36
|
+
return new Promise(res => rl.question(q, ans => { rl.close(); res(ans.trim().toLowerCase()); }));
|
|
37
|
+
}
|
|
38
|
+
function pullModel(model) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
livePulse_1.livePulse.act('CEO', `Pulling model: ${model}`);
|
|
41
|
+
const proc = (0, child_process_1.spawn)('ollama', ['pull', model], { stdio: 'inherit' });
|
|
42
|
+
proc.on('close', code => {
|
|
43
|
+
if (code === 0) {
|
|
44
|
+
livePulse_1.livePulse.done('CEO', `Model ready: ${model}`);
|
|
45
|
+
resolve();
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
livePulse_1.livePulse.error('CEO', `Failed: ${model}`);
|
|
49
|
+
reject(new Error(`exit ${code}`));
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function saveSelection(selection) {
|
|
55
|
+
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
56
|
+
fs_1.default.writeFileSync(MODEL_CONFIG, JSON.stringify(selection, null, 2));
|
|
57
|
+
}
|
|
58
|
+
function markComplete(hw, selection) {
|
|
59
|
+
fs_1.default.writeFileSync(SETUP_FLAG, JSON.stringify({
|
|
60
|
+
complete: true,
|
|
61
|
+
setupAt: new Date().toISOString(),
|
|
62
|
+
hardware: hw,
|
|
63
|
+
models: selection,
|
|
64
|
+
}, null, 2));
|
|
65
|
+
}
|
|
66
|
+
async function runSetupWizard() {
|
|
67
|
+
if (isSetupComplete())
|
|
68
|
+
return;
|
|
69
|
+
const hw = (0, hardwareDetector_1.detectHardware)();
|
|
70
|
+
// ── Ollama reachability check ─────────────────────────────────
|
|
71
|
+
let ollamaReachable = false;
|
|
72
|
+
try {
|
|
73
|
+
const r = await fetch('http://127.0.0.1:11434/api/tags', {
|
|
74
|
+
signal: AbortSignal.timeout(3000),
|
|
75
|
+
});
|
|
76
|
+
ollamaReachable = r.ok;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Ollama not running or not installed
|
|
80
|
+
}
|
|
81
|
+
if (!ollamaReachable) {
|
|
82
|
+
markComplete(hw, {});
|
|
83
|
+
console.log(`
|
|
84
|
+
┌─────────────────────────────────────────┐
|
|
85
|
+
│ DevOS — First Boot │
|
|
86
|
+
└─────────────────────────────────────────┘
|
|
87
|
+
|
|
88
|
+
Ollama is not running on this machine.
|
|
89
|
+
Aiden will start in cloud-only mode — your configured API keys will be used.
|
|
90
|
+
|
|
91
|
+
To enable local models later:
|
|
92
|
+
1. Install Ollama: https://ollama.com/download
|
|
93
|
+
2. Run: ollama serve
|
|
94
|
+
3. Delete config/setup-complete.json and restart with: npm start
|
|
95
|
+
|
|
96
|
+
`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
modelRouter_1.modelRouter.syncWithOllama();
|
|
100
|
+
const assessment = modelRouter_1.modelRouter.assessInstalledModels();
|
|
101
|
+
const gpuLine = hw.appleSilicon
|
|
102
|
+
? `Apple Silicon (${hw.gpu})`
|
|
103
|
+
: `${hw.gpu} · ${hw.vramGB}GB VRAM`;
|
|
104
|
+
console.log(`
|
|
105
|
+
┌─────────────────────────────────────────┐
|
|
106
|
+
│ DevOS — First Boot │
|
|
107
|
+
└─────────────────────────────────────────┘
|
|
108
|
+
|
|
109
|
+
Hey! I just scanned your machine.
|
|
110
|
+
|
|
111
|
+
GPU → ${gpuLine}
|
|
112
|
+
RAM → ${hw.ramGB}GB
|
|
113
|
+
OS → ${hw.platform}${hw.cudaAvailable ? ' · CUDA ✓' : ''}
|
|
114
|
+
`);
|
|
115
|
+
// ── CASE 1: All good models already installed ────────────────
|
|
116
|
+
if (assessment.allGood) {
|
|
117
|
+
console.log(` Good news — I found models already installed on your machine:\n`);
|
|
118
|
+
const selection = {};
|
|
119
|
+
for (const task of TASK_TYPES) {
|
|
120
|
+
const m = assessment.goodModels[task];
|
|
121
|
+
const upgrade = assessment.upgradesAvailable[task];
|
|
122
|
+
const note = upgrade ? ` (better option: ${upgrade.name})` : '';
|
|
123
|
+
console.log(` ${task.padEnd(10)} → ${m.name.padEnd(25)} ✓ installed${note}`);
|
|
124
|
+
selection[task] = m.name;
|
|
125
|
+
}
|
|
126
|
+
console.log('');
|
|
127
|
+
const answer = await ask(' Use these models? (yes / no) ', 'yes');
|
|
128
|
+
console.log('');
|
|
129
|
+
if (answer === 'yes' || answer === 'y') {
|
|
130
|
+
saveSelection(selection);
|
|
131
|
+
markComplete(hw, selection);
|
|
132
|
+
console.log(' ✓ All set. DevOS is ready.\n Run: npm start\n');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// If no, fall through to show upgrade options
|
|
136
|
+
console.log(' Ok — showing better options available for your hardware:\n');
|
|
137
|
+
}
|
|
138
|
+
// ── CASE 2: Some gaps or user wants upgrades ─────────────────
|
|
139
|
+
const selection = {};
|
|
140
|
+
const toPull = [];
|
|
141
|
+
console.log(` Here's what I recommend for your ${hw.gpu}:\n`);
|
|
142
|
+
for (const task of TASK_TYPES) {
|
|
143
|
+
const good = assessment.goodModels[task];
|
|
144
|
+
const upgrade = assessment.upgradesAvailable[task];
|
|
145
|
+
const missing = assessment.missingModels[task];
|
|
146
|
+
if (good && !upgrade) {
|
|
147
|
+
// Already has the best option
|
|
148
|
+
console.log(` ${task.padEnd(10)} → ${good.name.padEnd(25)} ✓ installed, best fit`);
|
|
149
|
+
selection[task] = good.name;
|
|
150
|
+
}
|
|
151
|
+
else if (good && upgrade) {
|
|
152
|
+
// Has something but better available
|
|
153
|
+
console.log(` ${task.padEnd(10)} → ${upgrade.name.padEnd(25)} ↑ upgrade from ${good.name}`);
|
|
154
|
+
selection[task] = upgrade.name;
|
|
155
|
+
toPull.push(upgrade.name);
|
|
156
|
+
}
|
|
157
|
+
else if (missing) {
|
|
158
|
+
// Nothing installed for this task
|
|
159
|
+
console.log(` ${task.padEnd(10)} → ${missing.name.padEnd(25)} ✗ not installed`);
|
|
160
|
+
selection[task] = missing.name;
|
|
161
|
+
toPull.push(missing.name);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// Absolute fallback
|
|
165
|
+
console.log(` ${task.padEnd(10)} → ${'phi3:mini'.padEnd(25)} ⚠ fallback`);
|
|
166
|
+
selection[task] = 'phi3:mini';
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
console.log('');
|
|
170
|
+
// Nothing needs pulling — just save config and finish
|
|
171
|
+
if (!toPull.length) {
|
|
172
|
+
saveSelection(selection);
|
|
173
|
+
markComplete(hw, selection);
|
|
174
|
+
console.log(' ✓ Configuration saved. Run: npm start\n');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const modelWord = toPull.length === 1 ? 'model' : 'models';
|
|
178
|
+
const answer = await ask(` Download ${toPull.length} ${modelWord}? (yes / no / skip) `);
|
|
179
|
+
console.log('');
|
|
180
|
+
// ── CASE 3: Skip downloads ───────────────────────────────────
|
|
181
|
+
if (answer === 'skip' || answer === 's') {
|
|
182
|
+
// Keep installed models; fall back for tasks with nothing
|
|
183
|
+
for (const task of TASK_TYPES) {
|
|
184
|
+
if (!selection[task] || toPull.includes(selection[task])) {
|
|
185
|
+
const installed = assessment.goodModels[task];
|
|
186
|
+
selection[task] = installed ? installed.name : 'phi3:mini';
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
saveSelection(selection);
|
|
190
|
+
markComplete(hw, selection);
|
|
191
|
+
console.log(' ✓ Saved with available models. Pull later: ollama pull <model>\n');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (answer !== 'yes' && answer !== 'y') {
|
|
195
|
+
console.log(' No problem. Run: devos config models to configure manually.\n');
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// Pull missing / upgrade models
|
|
199
|
+
const unique = [...new Set(toPull)];
|
|
200
|
+
console.log(` Pulling ${unique.length} ${unique.length === 1 ? 'model' : 'models'}...\n`);
|
|
201
|
+
for (const model of unique) {
|
|
202
|
+
try {
|
|
203
|
+
await pullModel(model);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
console.log(` ✗ Could not pull ${model}. Try manually: ollama pull ${model}`);
|
|
207
|
+
// Fall back to installed equivalent for this task
|
|
208
|
+
for (const task of TASK_TYPES) {
|
|
209
|
+
if (selection[task] === model) {
|
|
210
|
+
const fallback = assessment.goodModels[task];
|
|
211
|
+
selection[task] = fallback ? fallback.name : 'phi3:mini';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
saveSelection(selection);
|
|
217
|
+
markComplete(hw, selection);
|
|
218
|
+
console.log(`
|
|
219
|
+
✓ DevOS is configured for your machine.
|
|
220
|
+
Run: npm start
|
|
221
|
+
`);
|
|
222
|
+
}
|
|
223
|
+
function isSetupComplete() {
|
|
224
|
+
return fs_1.default.existsSync(SETUP_FLAG);
|
|
225
|
+
}
|