openbot 0.4.7 → 0.5.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/.claude/agents/runtime-security-reviewer.md +32 -0
- package/.claude/hooks/format.sh +18 -0
- package/.claude/hooks/protect-secrets.sh +23 -0
- package/.claude/settings.json +26 -0
- package/.claude/skills/deploy/SKILL.md +26 -0
- package/.claude/skills/new-plugin/SKILL.md +46 -0
- package/.dockerignore +9 -0
- package/.mcp.json +8 -0
- package/AGENTS.md +2 -2
- package/CLAUDE.md +50 -0
- package/Dockerfile +41 -0
- package/deploy/README.md +61 -0
- package/deploy/entrypoint.sh +6 -0
- package/deploy/fly.toml +36 -0
- package/dist/app/bootstrap.js +29 -0
- package/dist/app/cli.js +3 -1
- package/dist/app/cloud-mode.js +26 -0
- package/dist/app/config.js +61 -16
- package/dist/app/server.js +38 -6
- package/dist/plugins/approval/index.js +1 -1
- package/dist/plugins/bash/index.js +386 -164
- package/dist/plugins/memory/service.js +2 -5
- package/dist/plugins/openbot/context.js +17 -1
- package/dist/plugins/openbot/index.js +23 -8
- package/dist/plugins/openbot/model.js +59 -0
- package/dist/plugins/openbot/runtime.js +39 -56
- package/dist/plugins/openbot/system-prompt.js +43 -8
- package/dist/plugins/preview/index.js +267 -0
- package/dist/plugins/storage/index.js +3 -3
- package/dist/plugins/storage/service.js +58 -12
- package/dist/plugins/todo/index.js +131 -38
- package/dist/plugins/todo/service.js +93 -0
- package/dist/plugins/ui/index.js +8 -1
- package/dist/services/plugins/model-registry.js +101 -0
- package/dist/services/plugins/registry.js +6 -4
- package/dist/services/plugins/service.js +2 -6
- package/docs/agents.md +20 -2
- package/docs/architecture.md +1 -1
- package/docs/plugins.md +13 -3
- package/docs/templates/AGENT.example.md +1 -1
- package/package.json +5 -2
- package/pnpm-workspace.yaml +2 -0
- package/src/app/bootstrap.ts +43 -0
- package/src/app/cli.ts +3 -1
- package/src/app/cloud-mode.ts +41 -0
- package/src/app/config.ts +71 -19
- package/src/app/server.ts +45 -6
- package/src/app/types.ts +162 -31
- package/src/plugins/approval/index.ts +1 -1
- package/src/plugins/bash/index.ts +498 -185
- package/src/plugins/memory/service.ts +2 -5
- package/src/plugins/openbot/context.ts +17 -1
- package/src/plugins/openbot/index.ts +25 -10
- package/src/plugins/openbot/model.ts +76 -0
- package/src/plugins/openbot/runtime.ts +47 -69
- package/src/plugins/openbot/system-prompt.ts +43 -8
- package/src/plugins/preview/index.ts +323 -0
- package/src/plugins/storage/index.ts +5 -8
- package/src/plugins/storage/service.ts +79 -15
- package/src/plugins/todo/index.ts +166 -0
- package/src/plugins/todo/service.ts +123 -0
- package/src/plugins/ui/index.ts +8 -1
- package/src/services/plugins/domain.ts +2 -0
- package/src/services/plugins/model-registry.ts +146 -0
- package/src/services/plugins/registry.ts +6 -4
- package/src/services/plugins/service.ts +2 -6
- package/dist/agents/openbot/index.js +0 -76
- package/dist/agents/openbot/middleware/approval.js +0 -132
- package/dist/agents/openbot/runtime.js +0 -289
- package/dist/agents/openbot/system-prompt.js +0 -32
- package/dist/agents/openbot/tools/delegation.js +0 -78
- package/dist/agents/openbot/tools/mcp.js +0 -99
- package/dist/agents/openbot/tools/shell.js +0 -91
- package/dist/agents/openbot/tools/storage.js +0 -75
- package/dist/agents/openbot/tools/ui.js +0 -176
- package/dist/agents/system.js +0 -33
- package/dist/bus/agent-package.js +0 -1
- package/dist/bus/plugin.js +0 -1
- package/dist/bus/services.js +0 -754
- package/dist/bus/types.js +0 -1
- package/dist/harness/agent-harness.js +0 -45
- package/dist/harness/agent-invoke-run.js +0 -44
- package/dist/harness/agent-turn.js +0 -99
- package/dist/harness/channel-participants.js +0 -40
- package/dist/harness/constants.js +0 -2
- package/dist/harness/context-meter.js +0 -97
- package/dist/harness/context.js +0 -363
- package/dist/harness/dispatch.js +0 -144
- package/dist/harness/dispatcher.js +0 -156
- package/dist/harness/event-normalizer.js +0 -59
- package/dist/harness/history.js +0 -177
- package/dist/harness/mcp.js +0 -61
- package/dist/harness/orchestration.js +0 -88
- package/dist/harness/orchestrator.js +0 -149
- package/dist/harness/participants.js +0 -22
- package/dist/harness/process.js +0 -29
- package/dist/harness/queue-processor.js +0 -187
- package/dist/harness/run-harness.js +0 -154
- package/dist/harness/run.js +0 -98
- package/dist/harness/runtime-factory.js +0 -73
- package/dist/harness/runtime.js +0 -57
- package/dist/harness/todo-advance.js +0 -93
- package/dist/harness/todo-dispatch.js +0 -51
- package/dist/harness/todos.js +0 -5
- package/dist/harness/turn.js +0 -79
- package/dist/harness/types.js +0 -1
- package/dist/plugins/ai-sdk/index.js +0 -34
- package/dist/plugins/ai-sdk/runtime.js +0 -395
- package/dist/plugins/ai-sdk/system-prompt.js +0 -18
- package/dist/plugins/ai-sdk.js +0 -331
- package/dist/plugins/approval.js +0 -163
- package/dist/plugins/delegation.js +0 -108
- package/dist/plugins/mcp/index.js +0 -108
- package/dist/plugins/mcp.js +0 -140
- package/dist/plugins/shell/index.js +0 -100
- package/dist/plugins/shell.js +0 -123
- package/dist/plugins/storage-tools/index.js +0 -76
- package/dist/plugins/storage.js +0 -737
- package/dist/plugins/thread-namer/index.js +0 -72
- package/dist/plugins/threads/index.js +0 -114
- package/dist/plugins/ui.js +0 -211
- package/dist/plugins/workflow/index.js +0 -65
- package/dist/registry/agents.js +0 -138
- package/dist/registry/plugins.js +0 -110
- package/dist/services/agent-packages.js +0 -103
- package/dist/services/memory.js +0 -152
- package/dist/services/plugins.js +0 -98
- package/dist/services/storage.js +0 -1028
- package/dist/workflow/service.js +0 -106
- package/dist/workflow/types.js +0 -3
package/dist/registry/plugins.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { pathToFileURL } from 'node:url';
|
|
4
|
-
import { openbotPlugin } from '../plugins/openbot/index.js';
|
|
5
|
-
import { shellPlugin } from '../plugins/shell/index.js';
|
|
6
|
-
import { storageToolsPlugin } from '../plugins/storage-tools/index.js';
|
|
7
|
-
import { uiPlugin } from '../plugins/ui/index.js';
|
|
8
|
-
import { approvalPlugin } from '../plugins/approval/index.js';
|
|
9
|
-
import { memoryPlugin } from '../plugins/memory/index.js';
|
|
10
|
-
import { DEFAULT_PLUGINS_DIR, DEFAULT_BASE_DIR, loadConfig, resolvePath } from '../app/config.js';
|
|
11
|
-
let pluginsDir = null;
|
|
12
|
-
const loadedPlugins = new Set();
|
|
13
|
-
const cache = new Map();
|
|
14
|
-
const BUILT_IN = {
|
|
15
|
-
[openbotPlugin.id]: openbotPlugin,
|
|
16
|
-
[shellPlugin.id]: shellPlugin,
|
|
17
|
-
[storageToolsPlugin.id]: storageToolsPlugin,
|
|
18
|
-
[uiPlugin.id]: uiPlugin,
|
|
19
|
-
[approvalPlugin.id]: approvalPlugin,
|
|
20
|
-
[memoryPlugin.id]: memoryPlugin,
|
|
21
|
-
};
|
|
22
|
-
/** Normalize a dynamically imported plugin module. Supports `plugin`, `default`. */
|
|
23
|
-
export function parsePluginModule(module) {
|
|
24
|
-
const raw = module.plugin ??
|
|
25
|
-
module.default;
|
|
26
|
-
if (!raw || typeof raw !== 'object')
|
|
27
|
-
return null;
|
|
28
|
-
const factory = raw.factory;
|
|
29
|
-
if (typeof factory !== 'function')
|
|
30
|
-
return null;
|
|
31
|
-
const name = typeof raw.name === 'string' ? raw.name : '';
|
|
32
|
-
const description = typeof raw.description === 'string' ? raw.description : '';
|
|
33
|
-
const image = typeof raw.image === 'string' ? raw.image : undefined;
|
|
34
|
-
const configSchema = raw.configSchema;
|
|
35
|
-
const toolDefinitions = raw.toolDefinitions;
|
|
36
|
-
return {
|
|
37
|
-
name,
|
|
38
|
-
description,
|
|
39
|
-
image,
|
|
40
|
-
configSchema,
|
|
41
|
-
toolDefinitions,
|
|
42
|
-
factory: factory,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
/** Initialize the on-disk plugins directory (defaults to ~/.openbot/plugins). */
|
|
46
|
-
export function initPlugins(dir) {
|
|
47
|
-
if (dir) {
|
|
48
|
-
pluginsDir = dir;
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
const config = loadConfig();
|
|
52
|
-
const baseDir = config.baseDir || DEFAULT_BASE_DIR;
|
|
53
|
-
pluginsDir = path.join(resolvePath(baseDir), DEFAULT_PLUGINS_DIR);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Resolve a Plugin by id. The id is either:
|
|
58
|
-
* - a built-in id (e.g. "openbot", "shell"), or
|
|
59
|
-
* - an npm package name (e.g. "openbot-plugin-foo" or "@scope/foo"),
|
|
60
|
-
* in which case the folder layout is `plugins/<id>/dist/index.js`.
|
|
61
|
-
*/
|
|
62
|
-
export async function resolvePlugin(id) {
|
|
63
|
-
if (cache.has(id))
|
|
64
|
-
return cache.get(id);
|
|
65
|
-
if (BUILT_IN[id]) {
|
|
66
|
-
cache.set(id, BUILT_IN[id]);
|
|
67
|
-
return BUILT_IN[id];
|
|
68
|
-
}
|
|
69
|
-
if (!pluginsDir) {
|
|
70
|
-
initPlugins();
|
|
71
|
-
}
|
|
72
|
-
if (!pluginsDir)
|
|
73
|
-
return null;
|
|
74
|
-
const distPath = path.join(pluginsDir, id, 'dist', 'index.js');
|
|
75
|
-
if (!fs.existsSync(distPath)) {
|
|
76
|
-
console.warn(`[plugins] Plugin "${id}" not found at ${distPath}.`);
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
try {
|
|
80
|
-
const module = await import(pathToFileURL(distPath).href);
|
|
81
|
-
const parsed = parsePluginModule(module);
|
|
82
|
-
if (!parsed) {
|
|
83
|
-
console.warn(`[plugins] Plugin "${id}" at ${distPath} has no recognizable export.`);
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
const plugin = { id, ...parsed, name: parsed.name || id };
|
|
87
|
-
cache.set(id, plugin);
|
|
88
|
-
if (!loadedPlugins.has(id)) {
|
|
89
|
-
console.log(`[plugins] Loaded community plugin "${id}" from ${distPath}`);
|
|
90
|
-
loadedPlugins.add(id);
|
|
91
|
-
}
|
|
92
|
-
return plugin;
|
|
93
|
-
}
|
|
94
|
-
catch (e) {
|
|
95
|
-
console.warn(`[plugins] Failed to load plugin "${id}" from ${distPath}:`, e);
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/** Drop a single id from the in-memory cache (e.g. after fresh install). */
|
|
100
|
-
export function invalidatePlugin(id) {
|
|
101
|
-
cache.delete(id);
|
|
102
|
-
loadedPlugins.delete(id);
|
|
103
|
-
}
|
|
104
|
-
/** List built-in plugins (for marketplace/registry views). */
|
|
105
|
-
export function listBuiltInPlugins() {
|
|
106
|
-
return Object.values(BUILT_IN);
|
|
107
|
-
}
|
|
108
|
-
export function getPluginsDir() {
|
|
109
|
-
return pluginsDir;
|
|
110
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { exec } from 'node:child_process';
|
|
5
|
-
import { promisify } from 'node:util';
|
|
6
|
-
import { pathToFileURL } from 'node:url';
|
|
7
|
-
import { DEFAULT_AGENT_PACKAGES_DIR, DEFAULT_BASE_DIR, loadConfig, resolvePath, } from '../app/config.js';
|
|
8
|
-
import { parseAgentPackageModule } from '../registry/agents.js';
|
|
9
|
-
const execAsync = promisify(exec);
|
|
10
|
-
/**
|
|
11
|
-
* Lifecycle for community-built agent packages distributed via npm.
|
|
12
|
-
* The package format mirrors a normal npm package whose `dist/index.js`
|
|
13
|
-
* exports `agentPackage` (or `default`) that satisfies the `AgentPackage`
|
|
14
|
-
* interface.
|
|
15
|
-
*/
|
|
16
|
-
export const agentPackageService = {
|
|
17
|
-
install: async ({ packageName, version }) => {
|
|
18
|
-
const config = loadConfig();
|
|
19
|
-
const baseDir = resolvePath(config.baseDir || DEFAULT_BASE_DIR);
|
|
20
|
-
const packagesDir = path.join(baseDir, DEFAULT_AGENT_PACKAGES_DIR);
|
|
21
|
-
await fs.mkdir(packagesDir, { recursive: true });
|
|
22
|
-
const target = version ? `${packageName}@${version}` : packageName;
|
|
23
|
-
const entries = await fs.readdir(packagesDir, { withFileTypes: true });
|
|
24
|
-
for (const entry of entries) {
|
|
25
|
-
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
26
|
-
const pkgPath = path.join(packagesDir, entry.name, 'package.json');
|
|
27
|
-
if (existsSync(pkgPath)) {
|
|
28
|
-
try {
|
|
29
|
-
const pkgJson = JSON.parse(await fs.readFile(pkgPath, 'utf-8'));
|
|
30
|
-
if (pkgJson.name === packageName && (!version || pkgJson.version === version)) {
|
|
31
|
-
console.log(`[agent-packages] ${packageName}${version ? `@${version}` : ''} is already installed at ${entry.name}.`);
|
|
32
|
-
return {
|
|
33
|
-
name: pkgJson.name,
|
|
34
|
-
id: entry.name,
|
|
35
|
-
version: pkgJson.version,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
// ignore corrupted package.json
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
console.log(`[agent-packages] Installing ${target} to ${packagesDir}...`);
|
|
46
|
-
try {
|
|
47
|
-
const tempDir = path.join(packagesDir, '.tmp_' + Date.now());
|
|
48
|
-
await fs.mkdir(tempDir, { recursive: true });
|
|
49
|
-
await execAsync(`npm install ${target} --no-save --prefix "${tempDir}"`);
|
|
50
|
-
const pkgNameOnly = packageName.includes('/') ? packageName.split('/').pop() : packageName;
|
|
51
|
-
const installedPath = path.join(tempDir, 'node_modules', packageName);
|
|
52
|
-
let finalId = pkgNameOnly;
|
|
53
|
-
try {
|
|
54
|
-
const distPath = path.join(installedPath, 'dist', 'index.js');
|
|
55
|
-
if (existsSync(distPath)) {
|
|
56
|
-
const module = await import(pathToFileURL(distPath).href);
|
|
57
|
-
const exported = parseAgentPackageModule(module);
|
|
58
|
-
if (exported?.id)
|
|
59
|
-
finalId = exported.id;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
console.warn(`[agent-packages] Could not read package metadata for ${packageName}; using folder name as id.`, e);
|
|
64
|
-
}
|
|
65
|
-
const finalPath = path.join(packagesDir, finalId);
|
|
66
|
-
await fs.rm(finalPath, { recursive: true, force: true });
|
|
67
|
-
await fs.rename(installedPath, finalPath);
|
|
68
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
69
|
-
console.log(`[agent-packages] Running npm install in ${finalPath}...`);
|
|
70
|
-
try {
|
|
71
|
-
await execAsync(`npm install`, { cwd: finalPath });
|
|
72
|
-
console.log(`[agent-packages] npm install completed in ${finalPath}`);
|
|
73
|
-
}
|
|
74
|
-
catch (e) {
|
|
75
|
-
console.warn(`[agent-packages] Failed to run npm install in ${finalPath}:`, e);
|
|
76
|
-
}
|
|
77
|
-
const pkgJson = JSON.parse(await fs.readFile(path.join(finalPath, 'package.json'), 'utf-8'));
|
|
78
|
-
return {
|
|
79
|
-
name: pkgJson.name,
|
|
80
|
-
id: finalId,
|
|
81
|
-
version: pkgJson.version,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
console.error(`[agent-packages] Failed to install ${packageName}:`, error);
|
|
86
|
-
throw new Error(`Failed to install agent package ${packageName}: ${error.message}`);
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
uninstall: async (id) => {
|
|
90
|
-
const config = loadConfig();
|
|
91
|
-
const baseDir = resolvePath(config.baseDir || DEFAULT_BASE_DIR);
|
|
92
|
-
const packagesDir = path.join(baseDir, DEFAULT_AGENT_PACKAGES_DIR);
|
|
93
|
-
const packagePath = path.join(packagesDir, id);
|
|
94
|
-
try {
|
|
95
|
-
await fs.rm(packagePath, { recursive: true, force: true });
|
|
96
|
-
console.log(`[agent-packages] Uninstalled agent package ${id}`);
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
console.error(`[agent-packages] Failed to uninstall ${id}:`, error);
|
|
100
|
-
throw new Error(`Failed to uninstall agent package ${id}: ${error.message}`);
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
};
|
package/dist/services/memory.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import crypto from 'node:crypto';
|
|
4
|
-
import { DEFAULT_BASE_DIR, loadConfig, resolvePath } from '../app/config.js';
|
|
5
|
-
const DEFAULT_LIMIT = 50;
|
|
6
|
-
const MAX_LIMIT = 500;
|
|
7
|
-
const getMemoryDir = () => {
|
|
8
|
-
const config = loadConfig();
|
|
9
|
-
return path.join(resolvePath(config.baseDir || DEFAULT_BASE_DIR), 'memory');
|
|
10
|
-
};
|
|
11
|
-
const getLogPath = () => path.join(getMemoryDir(), 'log.jsonl');
|
|
12
|
-
const ensureDir = async () => {
|
|
13
|
-
await fs.mkdir(getMemoryDir(), { recursive: true });
|
|
14
|
-
};
|
|
15
|
-
const readLog = async () => {
|
|
16
|
-
try {
|
|
17
|
-
const raw = await fs.readFile(getLogPath(), 'utf-8');
|
|
18
|
-
return raw
|
|
19
|
-
.split(/\r?\n/)
|
|
20
|
-
.map((line) => line.trim())
|
|
21
|
-
.filter(Boolean)
|
|
22
|
-
.map((line) => {
|
|
23
|
-
try {
|
|
24
|
-
return JSON.parse(line);
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
.filter((e) => !!e);
|
|
31
|
-
}
|
|
32
|
-
catch (e) {
|
|
33
|
-
if (e?.code === 'ENOENT')
|
|
34
|
-
return [];
|
|
35
|
-
throw e;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
const replay = (entries) => {
|
|
39
|
-
const out = new Map();
|
|
40
|
-
for (const entry of entries) {
|
|
41
|
-
if (entry.op === 'add') {
|
|
42
|
-
out.set(entry.record.id, entry.record);
|
|
43
|
-
}
|
|
44
|
-
else if (entry.op === 'delete') {
|
|
45
|
-
out.delete(entry.id);
|
|
46
|
-
}
|
|
47
|
-
else if (entry.op === 'update') {
|
|
48
|
-
const existing = out.get(entry.id);
|
|
49
|
-
if (!existing)
|
|
50
|
-
continue;
|
|
51
|
-
out.set(entry.id, {
|
|
52
|
-
...existing,
|
|
53
|
-
...entry.patch,
|
|
54
|
-
id: existing.id,
|
|
55
|
-
updatedAt: entry.at,
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return out;
|
|
60
|
-
};
|
|
61
|
-
const appendEntry = async (entry) => {
|
|
62
|
-
await ensureDir();
|
|
63
|
-
await fs.appendFile(getLogPath(), `${JSON.stringify(entry)}\n`, 'utf-8');
|
|
64
|
-
};
|
|
65
|
-
const matchesQuery = (record, query, tag) => {
|
|
66
|
-
if (tag) {
|
|
67
|
-
if (!record.tags || !record.tags.includes(tag))
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
if (query) {
|
|
71
|
-
const q = query.toLowerCase();
|
|
72
|
-
if (!record.content.toLowerCase().includes(q))
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
return true;
|
|
76
|
-
};
|
|
77
|
-
export const memoryService = {
|
|
78
|
-
appendMemory: async (args) => {
|
|
79
|
-
const now = new Date().toISOString();
|
|
80
|
-
const record = {
|
|
81
|
-
id: crypto.randomUUID(),
|
|
82
|
-
scope: args.scope,
|
|
83
|
-
content: args.content,
|
|
84
|
-
tags: args.tags?.length ? args.tags : undefined,
|
|
85
|
-
createdAt: now,
|
|
86
|
-
updatedAt: now,
|
|
87
|
-
};
|
|
88
|
-
await appendEntry({ op: 'add', record });
|
|
89
|
-
return record;
|
|
90
|
-
},
|
|
91
|
-
updateMemory: async (args) => {
|
|
92
|
-
const entries = await readLog();
|
|
93
|
-
const map = replay(entries);
|
|
94
|
-
if (!map.has(args.id))
|
|
95
|
-
return false;
|
|
96
|
-
const at = new Date().toISOString();
|
|
97
|
-
const patch = {};
|
|
98
|
-
if (args.content !== undefined)
|
|
99
|
-
patch.content = args.content;
|
|
100
|
-
if (args.tags !== undefined)
|
|
101
|
-
patch.tags = args.tags.length ? args.tags : undefined;
|
|
102
|
-
if (Object.keys(patch).length === 0)
|
|
103
|
-
return true;
|
|
104
|
-
await appendEntry({ op: 'update', id: args.id, patch, at });
|
|
105
|
-
return true;
|
|
106
|
-
},
|
|
107
|
-
deleteMemory: async (args) => {
|
|
108
|
-
const entries = await readLog();
|
|
109
|
-
const map = replay(entries);
|
|
110
|
-
if (!map.has(args.id))
|
|
111
|
-
return false;
|
|
112
|
-
await appendEntry({ op: 'delete', id: args.id, at: new Date().toISOString() });
|
|
113
|
-
return true;
|
|
114
|
-
},
|
|
115
|
-
listMemories: async (args = {}) => {
|
|
116
|
-
const entries = await readLog();
|
|
117
|
-
const map = replay(entries);
|
|
118
|
-
const limit = Math.min(Math.max(args.limit ?? DEFAULT_LIMIT, 1), MAX_LIMIT);
|
|
119
|
-
const scopeSet = (() => {
|
|
120
|
-
if (args.scope)
|
|
121
|
-
return new Set([args.scope]);
|
|
122
|
-
if (args.scopes && args.scopes.length > 0)
|
|
123
|
-
return new Set(args.scopes);
|
|
124
|
-
return null;
|
|
125
|
-
})();
|
|
126
|
-
const filtered = [];
|
|
127
|
-
for (const record of map.values()) {
|
|
128
|
-
if (scopeSet && !scopeSet.has(record.scope))
|
|
129
|
-
continue;
|
|
130
|
-
if (!matchesQuery(record, args.query, args.tag))
|
|
131
|
-
continue;
|
|
132
|
-
filtered.push(record);
|
|
133
|
-
}
|
|
134
|
-
filtered.sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));
|
|
135
|
-
return filtered.slice(0, limit);
|
|
136
|
-
},
|
|
137
|
-
/**
|
|
138
|
-
* Compact the log into a single `add` per surviving record. Cheap to call
|
|
139
|
-
* occasionally; not required for correctness.
|
|
140
|
-
*/
|
|
141
|
-
compact: async () => {
|
|
142
|
-
const entries = await readLog();
|
|
143
|
-
const map = replay(entries);
|
|
144
|
-
const surviving = Array.from(map.values());
|
|
145
|
-
await ensureDir();
|
|
146
|
-
const tmp = `${getLogPath()}.tmp`;
|
|
147
|
-
const body = surviving.map((record) => JSON.stringify({ op: 'add', record })).join('\n');
|
|
148
|
-
await fs.writeFile(tmp, body ? `${body}\n` : '', 'utf-8');
|
|
149
|
-
await fs.rename(tmp, getLogPath());
|
|
150
|
-
return surviving.length;
|
|
151
|
-
},
|
|
152
|
-
};
|
package/dist/services/plugins.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { exec } from 'node:child_process';
|
|
5
|
-
import { promisify } from 'node:util';
|
|
6
|
-
import { DEFAULT_PLUGINS_DIR, DEFAULT_BASE_DIR, loadConfig, resolvePath, } from '../app/config.js';
|
|
7
|
-
import { invalidatePlugin } from '../registry/plugins.js';
|
|
8
|
-
const execAsync = promisify(exec);
|
|
9
|
-
const getPluginsDir = () => {
|
|
10
|
-
const config = loadConfig();
|
|
11
|
-
const baseDir = resolvePath(config.baseDir || DEFAULT_BASE_DIR);
|
|
12
|
-
return path.join(baseDir, DEFAULT_PLUGINS_DIR);
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Lifecycle for community-built plugins distributed via npm.
|
|
16
|
-
* Each plugin is installed to `<plugins>/<npm-name>/` and is identified
|
|
17
|
-
* everywhere (AGENT.md `plugins[].id`, registry, runtime resolution) by its
|
|
18
|
-
* npm name. Scoped packages (`@scope/foo`) live under `<plugins>/@scope/foo/`.
|
|
19
|
-
*/
|
|
20
|
-
export const pluginService = {
|
|
21
|
-
isInstalled: async (packageName) => {
|
|
22
|
-
const finalPath = path.join(getPluginsDir(), packageName);
|
|
23
|
-
return existsSync(path.join(finalPath, 'dist', 'index.js'));
|
|
24
|
-
},
|
|
25
|
-
install: async ({ packageName, version }) => {
|
|
26
|
-
const pluginsDir = getPluginsDir();
|
|
27
|
-
await fs.mkdir(pluginsDir, { recursive: true });
|
|
28
|
-
const finalPath = path.join(pluginsDir, packageName);
|
|
29
|
-
if (existsSync(path.join(finalPath, 'package.json'))) {
|
|
30
|
-
try {
|
|
31
|
-
const pkgJson = JSON.parse(await fs.readFile(path.join(finalPath, 'package.json'), 'utf-8'));
|
|
32
|
-
if (!version || pkgJson.version === version) {
|
|
33
|
-
console.log(`[plugins] ${packageName}${version ? `@${version}` : ''} is already installed.`);
|
|
34
|
-
return { name: pkgJson.name, version: pkgJson.version };
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
// corrupted; reinstall below
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
const target = version ? `${packageName}@${version}` : packageName;
|
|
42
|
-
console.log(`[plugins] Installing ${target} to ${pluginsDir}...`);
|
|
43
|
-
const tempDir = path.join(pluginsDir, '.tmp_' + Date.now());
|
|
44
|
-
try {
|
|
45
|
-
await fs.mkdir(tempDir, { recursive: true });
|
|
46
|
-
await execAsync(`npm install ${target} --no-save --prefix "${tempDir}"`);
|
|
47
|
-
const installedPath = path.join(tempDir, 'node_modules', packageName);
|
|
48
|
-
if (!existsSync(installedPath)) {
|
|
49
|
-
throw new Error(`npm did not produce ${installedPath}`);
|
|
50
|
-
}
|
|
51
|
-
await fs.mkdir(path.dirname(finalPath), { recursive: true });
|
|
52
|
-
await fs.rm(finalPath, { recursive: true, force: true });
|
|
53
|
-
await fs.rename(installedPath, finalPath);
|
|
54
|
-
console.log(`[plugins] Running npm install in ${finalPath}...`);
|
|
55
|
-
try {
|
|
56
|
-
await execAsync(`npm install`, { cwd: finalPath });
|
|
57
|
-
console.log(`[plugins] npm install completed in ${finalPath}`);
|
|
58
|
-
}
|
|
59
|
-
catch (e) {
|
|
60
|
-
console.warn(`[plugins] Failed to run npm install in ${finalPath}:`, e);
|
|
61
|
-
}
|
|
62
|
-
const pkgJson = JSON.parse(await fs.readFile(path.join(finalPath, 'package.json'), 'utf-8'));
|
|
63
|
-
invalidatePlugin(packageName);
|
|
64
|
-
return { name: pkgJson.name, version: pkgJson.version };
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
console.error(`[plugins] Failed to install ${packageName}:`, error);
|
|
68
|
-
throw new Error(`Failed to install plugin ${packageName}: ${error.message}`);
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
uninstall: async (packageName) => {
|
|
75
|
-
const pluginsDir = getPluginsDir();
|
|
76
|
-
const pluginPath = path.join(pluginsDir, packageName);
|
|
77
|
-
try {
|
|
78
|
-
await fs.rm(pluginPath, { recursive: true, force: true });
|
|
79
|
-
invalidatePlugin(packageName);
|
|
80
|
-
console.log(`[plugins] Uninstalled plugin ${packageName}`);
|
|
81
|
-
if (packageName.startsWith('@')) {
|
|
82
|
-
const scopeDir = path.dirname(pluginPath);
|
|
83
|
-
try {
|
|
84
|
-
const remaining = await fs.readdir(scopeDir);
|
|
85
|
-
if (remaining.length === 0)
|
|
86
|
-
await fs.rmdir(scopeDir);
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
// ignore
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
console.error(`[plugins] Failed to uninstall ${packageName}:`, error);
|
|
95
|
-
throw new Error(`Failed to uninstall plugin ${packageName}: ${error.message}`);
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
};
|