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,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.repairPlanResponse = repairPlanResponse;
|
|
8
|
+
/**
|
|
9
|
+
* Salvage a non-JSON planner response:
|
|
10
|
+
* 1. Direct JSON parse (already valid)
|
|
11
|
+
* 2. Extract from ```json ... ``` fences
|
|
12
|
+
* 3. Extract first {...} block
|
|
13
|
+
* 4. Treat plain-text as a direct-answer plan
|
|
14
|
+
*/
|
|
15
|
+
function repairPlanResponse(raw) {
|
|
16
|
+
if (!raw || raw.trim().length === 0) {
|
|
17
|
+
return { plan: null, repaired: false };
|
|
18
|
+
}
|
|
19
|
+
// Try 1: direct JSON parse
|
|
20
|
+
try {
|
|
21
|
+
const plan = JSON.parse(raw.trim());
|
|
22
|
+
return { plan, repaired: false };
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
// Try 2: extract from ```json ... ``` fences (already done by caller's regex,
|
|
26
|
+
// but replicated here so this function is self-contained)
|
|
27
|
+
const fenceMatch = raw.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
28
|
+
if (fenceMatch) {
|
|
29
|
+
try {
|
|
30
|
+
const plan = JSON.parse(fenceMatch[1].trim());
|
|
31
|
+
return { plan, repaired: true };
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
}
|
|
35
|
+
// Try 3: extract first {...} block
|
|
36
|
+
const firstBrace = raw.indexOf('{');
|
|
37
|
+
const lastBrace = raw.lastIndexOf('}');
|
|
38
|
+
if (firstBrace >= 0 && lastBrace > firstBrace) {
|
|
39
|
+
try {
|
|
40
|
+
const plan = JSON.parse(raw.slice(firstBrace, lastBrace + 1));
|
|
41
|
+
return { plan, repaired: true };
|
|
42
|
+
}
|
|
43
|
+
catch { }
|
|
44
|
+
}
|
|
45
|
+
// Try 4: if the model answered directly (no JSON at all),
|
|
46
|
+
// wrap as a direct-answer plan so the executor can return it immediately.
|
|
47
|
+
// Heuristic: >10 chars, not an error message, not too long.
|
|
48
|
+
const trimmed = raw.trim();
|
|
49
|
+
const looksLikeAnswer = trimmed.length > 10 &&
|
|
50
|
+
trimmed.length < 4000 &&
|
|
51
|
+
!/^(error|failed|cannot|unable)/i.test(trimmed);
|
|
52
|
+
if (looksLikeAnswer) {
|
|
53
|
+
const directPlan = {
|
|
54
|
+
goal: trimmed.slice(0, 80),
|
|
55
|
+
requires_execution: false,
|
|
56
|
+
plan: [],
|
|
57
|
+
phases: [],
|
|
58
|
+
direct_response: trimmed,
|
|
59
|
+
reasoning: 'Model provided direct answer without structured plan',
|
|
60
|
+
};
|
|
61
|
+
return { plan: directPlan, repaired: true, directAnswer: trimmed };
|
|
62
|
+
}
|
|
63
|
+
return { plan: null, repaired: false };
|
|
64
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
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.planTool = void 0;
|
|
11
|
+
// core/planTool.ts — Manus-style phased task planner.
|
|
12
|
+
// Tracks multi-phase task execution with workspace persistence.
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
// ── PlanTool singleton ────────────────────────────────────────
|
|
16
|
+
class PlanTool {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.activePlans = new Map();
|
|
19
|
+
}
|
|
20
|
+
create(goal, phases) {
|
|
21
|
+
const id = `task_${Date.now()}`;
|
|
22
|
+
const workspaceDir = path_1.default.join(process.cwd(), 'workspace', 'tasks', id);
|
|
23
|
+
fs_1.default.mkdirSync(workspaceDir, { recursive: true });
|
|
24
|
+
const plan = {
|
|
25
|
+
id,
|
|
26
|
+
goal,
|
|
27
|
+
phases: phases.map((p, i) => ({
|
|
28
|
+
...p,
|
|
29
|
+
status: i === 0 ? 'running' : 'pending',
|
|
30
|
+
startedAt: i === 0 ? Date.now() : undefined,
|
|
31
|
+
})),
|
|
32
|
+
currentPhaseIndex: 0,
|
|
33
|
+
status: 'running',
|
|
34
|
+
createdAt: Date.now(),
|
|
35
|
+
workspaceDir,
|
|
36
|
+
};
|
|
37
|
+
this.activePlans.set(id, plan);
|
|
38
|
+
this.savePlan(plan);
|
|
39
|
+
return plan;
|
|
40
|
+
}
|
|
41
|
+
advancePhase(planId, result) {
|
|
42
|
+
const plan = this.activePlans.get(planId);
|
|
43
|
+
if (!plan)
|
|
44
|
+
return null;
|
|
45
|
+
const current = plan.phases[plan.currentPhaseIndex];
|
|
46
|
+
if (current) {
|
|
47
|
+
current.status = 'done';
|
|
48
|
+
current.result = result;
|
|
49
|
+
current.completedAt = Date.now();
|
|
50
|
+
}
|
|
51
|
+
plan.currentPhaseIndex++;
|
|
52
|
+
if (plan.currentPhaseIndex >= plan.phases.length) {
|
|
53
|
+
plan.status = 'done';
|
|
54
|
+
plan.completedAt = Date.now();
|
|
55
|
+
this.savePlan(plan);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const next = plan.phases[plan.currentPhaseIndex];
|
|
59
|
+
next.status = 'running';
|
|
60
|
+
next.startedAt = Date.now();
|
|
61
|
+
this.savePlan(plan);
|
|
62
|
+
return next;
|
|
63
|
+
}
|
|
64
|
+
failPhase(planId, error) {
|
|
65
|
+
const plan = this.activePlans.get(planId);
|
|
66
|
+
if (!plan)
|
|
67
|
+
return;
|
|
68
|
+
const current = plan.phases[plan.currentPhaseIndex];
|
|
69
|
+
if (current) {
|
|
70
|
+
current.status = 'failed';
|
|
71
|
+
current.result = error;
|
|
72
|
+
}
|
|
73
|
+
plan.status = 'failed';
|
|
74
|
+
this.savePlan(plan);
|
|
75
|
+
}
|
|
76
|
+
getCurrentPhase(planId) {
|
|
77
|
+
const plan = this.activePlans.get(planId);
|
|
78
|
+
if (!plan)
|
|
79
|
+
return null;
|
|
80
|
+
return plan.phases[plan.currentPhaseIndex] || null;
|
|
81
|
+
}
|
|
82
|
+
getPlan(planId) {
|
|
83
|
+
return this.activePlans.get(planId) || this.loadPlan(planId);
|
|
84
|
+
}
|
|
85
|
+
formatSummary(plan) {
|
|
86
|
+
return plan.phases.map((p, i) => {
|
|
87
|
+
const icon = p.status === 'done' ? '✓' : p.status === 'running' ? '▶' : p.status === 'failed' ? '✗' : '○';
|
|
88
|
+
return `${icon} Phase ${i + 1}: ${p.title}`;
|
|
89
|
+
}).join(' → ');
|
|
90
|
+
}
|
|
91
|
+
savePlan(plan) {
|
|
92
|
+
try {
|
|
93
|
+
fs_1.default.writeFileSync(path_1.default.join(plan.workspaceDir, 'plan.json'), JSON.stringify(plan, null, 2));
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
}
|
|
97
|
+
loadPlan(planId) {
|
|
98
|
+
try {
|
|
99
|
+
const planPath = path_1.default.join(process.cwd(), 'workspace', 'tasks', planId, 'plan.json');
|
|
100
|
+
if (!fs_1.default.existsSync(planPath))
|
|
101
|
+
return null;
|
|
102
|
+
const loaded = JSON.parse(fs_1.default.readFileSync(planPath, 'utf-8'));
|
|
103
|
+
this.activePlans.set(planId, loaded);
|
|
104
|
+
return loaded;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.planTool = new PlanTool();
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// core/playwrightBridge.ts — Centralised Playwright session
|
|
4
|
+
// ============================================================
|
|
5
|
+
// Single persistent browser context shared across all tool calls
|
|
6
|
+
// within a server session. All browser tools route through here
|
|
7
|
+
// instead of duplicating context/page management in toolRegistry.
|
|
8
|
+
//
|
|
9
|
+
// Environment variables:
|
|
10
|
+
// AIDEN_BROWSER_HEADLESS=true run headless (default: false / headed)
|
|
11
|
+
// AIDEN_BROWSER_TIMEOUT=15000 default navigation timeout in ms
|
|
12
|
+
// ============================================================
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.pwNavigate = pwNavigate;
|
|
51
|
+
exports.pwScreenshot = pwScreenshot;
|
|
52
|
+
exports.pwClick = pwClick;
|
|
53
|
+
exports.pwClickFirstResult = pwClickFirstResult;
|
|
54
|
+
exports.pwType = pwType;
|
|
55
|
+
exports.pwScroll = pwScroll;
|
|
56
|
+
exports.pwSnapshot = pwSnapshot;
|
|
57
|
+
exports.pwGetUrl = pwGetUrl;
|
|
58
|
+
exports.pwClose = pwClose;
|
|
59
|
+
exports.getActiveBrowserPage = getActiveBrowserPage;
|
|
60
|
+
const path_1 = __importDefault(require("path"));
|
|
61
|
+
const fs_1 = __importDefault(require("fs"));
|
|
62
|
+
const paths_1 = require("./paths");
|
|
63
|
+
// ── Lazy-import Playwright so the server boots even if playwright
|
|
64
|
+
// is not installed (tools will return a clear error message).
|
|
65
|
+
let _chromium = null;
|
|
66
|
+
async function getChromium() {
|
|
67
|
+
if (!_chromium) {
|
|
68
|
+
const pw = await Promise.resolve().then(() => __importStar(require('playwright')));
|
|
69
|
+
_chromium = pw.chromium;
|
|
70
|
+
}
|
|
71
|
+
return _chromium;
|
|
72
|
+
}
|
|
73
|
+
// ── Singleton state ──────────────────────────────────────────
|
|
74
|
+
let _browserContext = null;
|
|
75
|
+
let _activePage = null;
|
|
76
|
+
let _idleTimer = null;
|
|
77
|
+
const IDLE_MS = 5 * 60 * 1000; // 5 min
|
|
78
|
+
const NAV_TIMEOUT = parseInt(process.env.AIDEN_BROWSER_TIMEOUT ?? '15000', 10);
|
|
79
|
+
const HEADLESS = process.env.AIDEN_BROWSER_HEADLESS === 'true';
|
|
80
|
+
function getBrowserProfileDir() {
|
|
81
|
+
const base = (0, paths_1.getUserDataDir)();
|
|
82
|
+
const dir = path_1.default.join(base, 'browser-profile');
|
|
83
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
84
|
+
return dir;
|
|
85
|
+
}
|
|
86
|
+
function resetIdleTimer() {
|
|
87
|
+
if (_idleTimer)
|
|
88
|
+
clearTimeout(_idleTimer);
|
|
89
|
+
_idleTimer = setTimeout(async () => {
|
|
90
|
+
if (_browserContext) {
|
|
91
|
+
console.log('[Browser] Closing idle browser after 5 min inactivity');
|
|
92
|
+
try {
|
|
93
|
+
await _browserContext.close();
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
_browserContext = null;
|
|
97
|
+
_activePage = null;
|
|
98
|
+
}
|
|
99
|
+
}, IDLE_MS);
|
|
100
|
+
}
|
|
101
|
+
async function ensureContext() {
|
|
102
|
+
if (!_browserContext) {
|
|
103
|
+
const chromium = await getChromium();
|
|
104
|
+
const profile = getBrowserProfileDir();
|
|
105
|
+
console.log(`[Browser] Launching — profile: ${profile} headless: ${HEADLESS}`);
|
|
106
|
+
_browserContext = await chromium.launchPersistentContext(profile, {
|
|
107
|
+
headless: HEADLESS,
|
|
108
|
+
viewport: { width: 1280, height: 720 },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
resetIdleTimer();
|
|
112
|
+
return _browserContext;
|
|
113
|
+
}
|
|
114
|
+
async function ensurePage() {
|
|
115
|
+
const ctx = await ensureContext();
|
|
116
|
+
const pages = ctx.pages();
|
|
117
|
+
if (!_activePage || _activePage.isClosed()) {
|
|
118
|
+
const blank = pages.find((p) => p.url() === 'about:blank');
|
|
119
|
+
_activePage = blank ?? await ctx.newPage();
|
|
120
|
+
}
|
|
121
|
+
return _activePage;
|
|
122
|
+
}
|
|
123
|
+
// ── Exported helpers ─────────────────────────────────────────
|
|
124
|
+
// ── Playwright availability check ────────────────────────────────────────────
|
|
125
|
+
let _pwAvailable = null;
|
|
126
|
+
async function checkPwAvailable() {
|
|
127
|
+
if (_pwAvailable !== null)
|
|
128
|
+
return _pwAvailable;
|
|
129
|
+
try {
|
|
130
|
+
await Promise.resolve().then(() => __importStar(require('playwright')));
|
|
131
|
+
_pwAvailable = true;
|
|
132
|
+
console.log('[Browser] playwright available');
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
_pwAvailable = false;
|
|
136
|
+
console.warn('[Browser] playwright not installed — browser tools unavailable. Run: npm install playwright');
|
|
137
|
+
}
|
|
138
|
+
return _pwAvailable;
|
|
139
|
+
}
|
|
140
|
+
/** Navigate to a URL, reusing the active page (opens blank tab if needed). */
|
|
141
|
+
async function pwNavigate(url) {
|
|
142
|
+
const available = await checkPwAvailable();
|
|
143
|
+
if (!available) {
|
|
144
|
+
return { ok: false, url, error: 'playwright not installed. Run: npm install playwright && npx playwright install chromium' };
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const ctx = await ensureContext();
|
|
148
|
+
const pages = ctx.pages();
|
|
149
|
+
const blank = pages.find((p) => p.url() === 'about:blank');
|
|
150
|
+
_activePage = blank ?? await ctx.newPage();
|
|
151
|
+
await _activePage.goto(url, { waitUntil: 'domcontentloaded', timeout: NAV_TIMEOUT });
|
|
152
|
+
return { ok: true, url: _activePage.url() };
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
return { ok: false, url, error: e.message };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** Take a full-page screenshot, saved to workspace/screenshots/. Returns the file path. */
|
|
159
|
+
async function pwScreenshot() {
|
|
160
|
+
try {
|
|
161
|
+
const page = await ensurePage();
|
|
162
|
+
const dir = path_1.default.join(process.cwd(), 'workspace', 'screenshots');
|
|
163
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
164
|
+
const file = path_1.default.join(dir, `screenshot_${Date.now()}.png`);
|
|
165
|
+
await page.screenshot({ path: file, fullPage: false });
|
|
166
|
+
return { ok: true, path: file };
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
return { ok: false, error: e.message };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/** Click an element by CSS selector or text. Pass 'first_result' for search-result shortcuts. */
|
|
173
|
+
async function pwClick(target) {
|
|
174
|
+
try {
|
|
175
|
+
const page = await ensurePage();
|
|
176
|
+
const tryClick = async (sel) => {
|
|
177
|
+
try {
|
|
178
|
+
await page.waitForSelector(sel, { state: 'visible', timeout: 5000 });
|
|
179
|
+
await page.locator(sel).first().click({ timeout: 5000 });
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const clicked = (await tryClick(target)) || (await tryClick(`text=${target}`));
|
|
187
|
+
if (!clicked)
|
|
188
|
+
return { ok: false, error: `Element not found or not visible: "${target}"` };
|
|
189
|
+
await page.waitForLoadState('domcontentloaded', { timeout: 5000 }).catch(() => { });
|
|
190
|
+
return { ok: true };
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
return { ok: false, error: e.message };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/** Click the first organic search result on Google / YouTube / DuckDuckGo / Bing. */
|
|
197
|
+
async function pwClickFirstResult() {
|
|
198
|
+
try {
|
|
199
|
+
const page = await ensurePage();
|
|
200
|
+
const currentUrl = page.url();
|
|
201
|
+
const SITES = [
|
|
202
|
+
{
|
|
203
|
+
pattern: /youtube\.com\/results/,
|
|
204
|
+
cfg: { selectors: ['a#video-title', 'ytd-video-renderer a[href*="/watch"]', 'ytd-rich-item-renderer a#thumbnail'], navPattern: /youtube\.com\/watch/ },
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
pattern: /google\.com\/search/,
|
|
208
|
+
cfg: { selectors: ['div.g h3 a', 'div#search a[href]:not([href*="google.com/search"])', 'h3.LC20lb'] },
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
pattern: /duckduckgo\.com/,
|
|
212
|
+
cfg: { selectors: ['article[data-testid="result"] h2 a', 'a.result__a', 'ol.react-results--main li a[data-testid="result-title-a"]'] },
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
pattern: /bing\.com\/search/,
|
|
216
|
+
cfg: { selectors: ['li.b_algo h2 a', '#b_results .b_algo a'] },
|
|
217
|
+
},
|
|
218
|
+
];
|
|
219
|
+
const match = SITES.find(s => s.pattern.test(currentUrl));
|
|
220
|
+
if (!match)
|
|
221
|
+
return { ok: false, error: `first_result not supported for ${currentUrl}` };
|
|
222
|
+
let locator = null;
|
|
223
|
+
for (const sel of match.cfg.selectors) {
|
|
224
|
+
try {
|
|
225
|
+
await page.waitForSelector(sel, { state: 'visible', timeout: 8000 });
|
|
226
|
+
locator = page.locator(sel).first();
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
catch { /* try next */ }
|
|
230
|
+
}
|
|
231
|
+
if (!locator)
|
|
232
|
+
return { ok: false, error: `No result selector appeared on ${currentUrl}` };
|
|
233
|
+
if (match.cfg.navPattern) {
|
|
234
|
+
await Promise.all([
|
|
235
|
+
page.waitForURL(match.cfg.navPattern, { timeout: 12000 }),
|
|
236
|
+
locator.click({ timeout: 5000 }),
|
|
237
|
+
]);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
await locator.click({ timeout: 5000 });
|
|
241
|
+
await page.waitForLoadState('domcontentloaded', { timeout: 8000 }).catch(() => { });
|
|
242
|
+
}
|
|
243
|
+
return { ok: true, url: page.url() };
|
|
244
|
+
}
|
|
245
|
+
catch (e) {
|
|
246
|
+
return { ok: false, error: e.message };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Type text into the specified selector (defaults to first input). */
|
|
250
|
+
async function pwType(selector, text) {
|
|
251
|
+
try {
|
|
252
|
+
const page = await ensurePage();
|
|
253
|
+
await page.waitForSelector(selector, { state: 'visible', timeout: 5000 }).catch(() => { });
|
|
254
|
+
await page.fill(selector, text);
|
|
255
|
+
return { ok: true };
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
return { ok: false, error: e.message };
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/** Scroll the page or a specific element. */
|
|
262
|
+
async function pwScroll(direction, amount, selector) {
|
|
263
|
+
try {
|
|
264
|
+
const page = await ensurePage();
|
|
265
|
+
if (selector) {
|
|
266
|
+
await page.waitForSelector(selector, { state: 'visible', timeout: 5000 }).catch(() => { });
|
|
267
|
+
if (direction === 'top') {
|
|
268
|
+
await page.evaluate((sel) => {
|
|
269
|
+
// eslint-disable-next-line no-undef
|
|
270
|
+
const el = globalThis.document.querySelector(sel);
|
|
271
|
+
if (el)
|
|
272
|
+
el.scrollTop = 0;
|
|
273
|
+
}, selector);
|
|
274
|
+
}
|
|
275
|
+
else if (direction === 'bottom') {
|
|
276
|
+
await page.evaluate((sel) => {
|
|
277
|
+
// eslint-disable-next-line no-undef
|
|
278
|
+
const el = globalThis.document.querySelector(sel);
|
|
279
|
+
if (el)
|
|
280
|
+
el.scrollTop = el.scrollHeight;
|
|
281
|
+
}, selector);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
const delta = direction === 'up' ? -amount : amount;
|
|
285
|
+
await page.evaluate(({ sel, dy }) => {
|
|
286
|
+
// eslint-disable-next-line no-undef
|
|
287
|
+
const el = globalThis.document.querySelector(sel);
|
|
288
|
+
if (el)
|
|
289
|
+
el.scrollBy(0, dy);
|
|
290
|
+
}, { sel: selector, dy: delta });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
if (direction === 'top') {
|
|
295
|
+
await page.evaluate(() => globalThis.window.scrollTo(0, 0));
|
|
296
|
+
}
|
|
297
|
+
else if (direction === 'bottom') {
|
|
298
|
+
await page.evaluate(() => globalThis.window.scrollTo(0, globalThis.document.body.scrollHeight));
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
const delta = direction === 'up' ? -amount : amount;
|
|
302
|
+
await page.evaluate((dy) => globalThis.window.scrollBy(0, dy), delta);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return { ok: true };
|
|
306
|
+
}
|
|
307
|
+
catch (e) {
|
|
308
|
+
return { ok: false, error: e.message };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/** Extract visible text from the current page body (first 3 000 chars). */
|
|
312
|
+
async function pwSnapshot() {
|
|
313
|
+
try {
|
|
314
|
+
const page = await ensurePage();
|
|
315
|
+
// eslint-disable-next-line no-undef
|
|
316
|
+
const text = await page.evaluate(() => globalThis.document.body.innerText);
|
|
317
|
+
return { ok: true, text: text.slice(0, 3000) };
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
return { ok: false, error: e.message };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/** Return the URL currently loaded in the active browser page. */
|
|
324
|
+
async function pwGetUrl() {
|
|
325
|
+
try {
|
|
326
|
+
if (!_activePage || _activePage.isClosed()) {
|
|
327
|
+
const ctx = await ensureContext();
|
|
328
|
+
const pages = ctx.pages();
|
|
329
|
+
if (pages.length === 0)
|
|
330
|
+
return { ok: false, error: 'No browser page open. Use open_browser first.' };
|
|
331
|
+
_activePage = pages[pages.length - 1];
|
|
332
|
+
}
|
|
333
|
+
return { ok: true, url: _activePage.url() };
|
|
334
|
+
}
|
|
335
|
+
catch (e) {
|
|
336
|
+
return { ok: false, error: e.message };
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/** Close the browser context and release all resources (call on server shutdown). */
|
|
340
|
+
async function pwClose() {
|
|
341
|
+
if (_idleTimer) {
|
|
342
|
+
clearTimeout(_idleTimer);
|
|
343
|
+
_idleTimer = null;
|
|
344
|
+
}
|
|
345
|
+
if (_browserContext) {
|
|
346
|
+
try {
|
|
347
|
+
await _browserContext.close();
|
|
348
|
+
}
|
|
349
|
+
catch { }
|
|
350
|
+
_browserContext = null;
|
|
351
|
+
_activePage = null;
|
|
352
|
+
console.log('[Browser] Closed on shutdown');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/** Expose active page for legacy callers that still need it. */
|
|
356
|
+
function getActiveBrowserPage() { return _activePage; }
|
|
@@ -0,0 +1,121 @@
|
|
|
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.pluginManager = void 0;
|
|
11
|
+
// core/pluginSystem.ts — Plugin system foundation.
|
|
12
|
+
// Allows community extensions without modifying core code.
|
|
13
|
+
// Scans workspace/plugins/*/plugin.json, loads entry files,
|
|
14
|
+
// and calls onLoad(ctx) with a sandboxed PluginContext.
|
|
15
|
+
const fs_1 = __importDefault(require("fs"));
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const toolRegistry_1 = require("./toolRegistry");
|
|
18
|
+
const hooks_1 = require("./hooks");
|
|
19
|
+
const PLUGINS_DIR = path_1.default.join(process.cwd(), 'workspace', 'plugins');
|
|
20
|
+
// ── PluginManager ──────────────────────────────────────────────
|
|
21
|
+
class PluginManager {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.loaded = [];
|
|
24
|
+
}
|
|
25
|
+
// ── Load all plugins from workspace/plugins/ ───────────────
|
|
26
|
+
async loadAll() {
|
|
27
|
+
if (!fs_1.default.existsSync(PLUGINS_DIR)) {
|
|
28
|
+
console.log('[Plugins] No plugins directory found — skipping');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const dirs = fs_1.default.readdirSync(PLUGINS_DIR, { withFileTypes: true })
|
|
32
|
+
.filter(d => d.isDirectory())
|
|
33
|
+
.map(d => path_1.default.join(PLUGINS_DIR, d.name));
|
|
34
|
+
for (const dir of dirs) {
|
|
35
|
+
try {
|
|
36
|
+
await this.loadPlugin(dir);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
console.error(`[Plugins] Failed to load plugin at ${path_1.default.basename(dir)}:`, e.message);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
console.log(`[Plugins] ${this.loaded.length} plugin(s) active`);
|
|
43
|
+
}
|
|
44
|
+
// ── Load a single plugin directory ────────────────────────
|
|
45
|
+
async loadPlugin(dir) {
|
|
46
|
+
const manifestPath = path_1.default.join(dir, 'plugin.json');
|
|
47
|
+
if (!fs_1.default.existsSync(manifestPath)) {
|
|
48
|
+
console.warn(`[Plugins] No plugin.json in ${path_1.default.basename(dir)} — skipping`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
let manifest;
|
|
52
|
+
try {
|
|
53
|
+
manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf-8'));
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
throw new Error(`Invalid plugin.json: ${e.message}`);
|
|
57
|
+
}
|
|
58
|
+
if (!manifest.name || !manifest.version || !manifest.entry) {
|
|
59
|
+
throw new Error('plugin.json missing required fields: name, version, entry');
|
|
60
|
+
}
|
|
61
|
+
// ── Security: entry must not escape plugin directory ───────
|
|
62
|
+
const entryPath = path_1.default.resolve(dir, manifest.entry);
|
|
63
|
+
const resolvedBase = path_1.default.resolve(dir);
|
|
64
|
+
if (!entryPath.startsWith(resolvedBase + path_1.default.sep) && entryPath !== resolvedBase) {
|
|
65
|
+
throw new Error(`Security: entry path escapes plugin directory: ${manifest.entry}`);
|
|
66
|
+
}
|
|
67
|
+
if (!fs_1.default.existsSync(entryPath)) {
|
|
68
|
+
throw new Error(`Entry file not found: ${manifest.entry}`);
|
|
69
|
+
}
|
|
70
|
+
// ── Load the plugin module ─────────────────────────────────
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
72
|
+
const mod = require(entryPath);
|
|
73
|
+
const plugin = mod.default ?? mod;
|
|
74
|
+
if (typeof plugin?.onLoad !== 'function') {
|
|
75
|
+
throw new Error(`Plugin "${manifest.name}" must export an object with an onLoad() function`);
|
|
76
|
+
}
|
|
77
|
+
// ── Build sandboxed context ────────────────────────────────
|
|
78
|
+
const ctx = {
|
|
79
|
+
registerTool: (def) => {
|
|
80
|
+
(0, toolRegistry_1.registerExternalTool)(def.name, def.execute, manifest.name);
|
|
81
|
+
},
|
|
82
|
+
registerHook: (event, handler) => {
|
|
83
|
+
(0, hooks_1.registerExternalHook)(event, handler, manifest.name);
|
|
84
|
+
},
|
|
85
|
+
log: (msg) => {
|
|
86
|
+
console.log(`[Plugin:${manifest.name}] ${msg}`);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
await plugin.onLoad(ctx);
|
|
90
|
+
this.loaded.push({ manifest, dir, active: true, plugin });
|
|
91
|
+
console.log(`[Plugins] Loaded "${manifest.name}" v${manifest.version}${manifest.author ? ` by ${manifest.author}` : ''}`);
|
|
92
|
+
}
|
|
93
|
+
// ── List loaded plugins ────────────────────────────────────
|
|
94
|
+
list() {
|
|
95
|
+
return this.loaded.map(r => ({
|
|
96
|
+
name: r.manifest.name,
|
|
97
|
+
version: r.manifest.version,
|
|
98
|
+
description: r.manifest.description,
|
|
99
|
+
author: r.manifest.author,
|
|
100
|
+
active: r.active,
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
// ── Unload all plugins ─────────────────────────────────────
|
|
104
|
+
async unloadAll() {
|
|
105
|
+
for (const record of this.loaded) {
|
|
106
|
+
try {
|
|
107
|
+
if (record.plugin?.onUnload) {
|
|
108
|
+
await record.plugin.onUnload();
|
|
109
|
+
}
|
|
110
|
+
record.active = false;
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
console.error(`[Plugins] onUnload error for "${record.manifest.name}":`, e.message);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
console.log(`[Plugins] Unloaded ${this.loaded.length} plugin(s)`);
|
|
117
|
+
this.loaded = [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// ── Singleton ──────────────────────────────────────────────────
|
|
121
|
+
exports.pluginManager = new PluginManager();
|