🚀. Socket Launch Week Day 3:Socket Firewall Now Blocks Malicious VS Code and Open VSX Extensions.Learn more
Sign In

@daomar/agentfleet

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@daomar/agentfleet - npm Package Compare versions

Comparing version
3.0.0
to
3.1.0
+57
-0
dist/backends/index.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });

@@ -6,4 +39,7 @@ exports.LocalFolderBackend = exports.validateRelativePath = exports.TransientIOError = exports.PermissionError = exports.AlreadyExistsError = exports.NotFoundError = exports.BackendError = void 0;

exports.listBackends = listBackends;
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const local_folder_js_1 = require("./local-folder.js");
const i18n_js_1 = require("../services/i18n.js");
const onedrive_detector_js_1 = require("../services/onedrive-detector.js");
var errors_js_1 = require("./errors.js");

@@ -19,2 +55,21 @@ Object.defineProperty(exports, "BackendError", { enumerable: true, get: function () { return errors_js_1.BackendError; } });

Object.defineProperty(exports, "LocalFolderBackend", { enumerable: true, get: function () { return local_folder_js_2.LocalFolderBackend; } });
function createOnedriveBackend(business) {
return (config) => {
// If path is already resolved (from config), use it directly
if (config.path && typeof config.path === 'string') {
return new local_folder_js_1.LocalFolderBackend({ path: config.path });
}
// Auto-detect OneDrive path
const detector = new onedrive_detector_js_1.OneDriveDetector();
const accounts = detector.detectAccounts();
const account = accounts.find((a) => a.isBusiness === business);
if (!account) {
const key = business ? 'init.onedrive_business_not_found' : 'init.onedrive_not_found';
throw new Error((0, i18n_js_1.t)(key));
}
const fleetPath = path.join(account.path, 'AgentFleet');
fs.mkdirSync(fleetPath, { recursive: true });
return new local_folder_js_1.LocalFolderBackend({ path: fleetPath });
};
}
const registry = new Map([

@@ -30,2 +85,4 @@ [

],
['onedrive', createOnedriveBackend(false)],
['onedrive-business', createOnedriveBackend(true)],
]);

@@ -32,0 +89,0 @@ /**

+1
-1

@@ -72,3 +72,3 @@ #!/usr/bin/env node

.requiredOption('--backend <name>', (0, i18n_1.t)('init.option_backend'), 'local-folder')
.requiredOption('--path <dir>', (0, i18n_1.t)('init.option_path'))
.option('--path <dir>', (0, i18n_1.t)('init.option_path'))
.option('--force', 'Overwrite existing configuration')

@@ -75,0 +75,0 @@ .action(init_1.initCommand);

@@ -43,7 +43,8 @@ "use strict";

const i18n_js_1 = require("../services/i18n.js");
const onedrive_detector_js_1 = require("../services/onedrive-detector.js");
const CONFIG_DIR = path.join(os.homedir(), '.agentfleet');
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
const LOGS_DIR = path.join(CONFIG_DIR, 'logs');
const FLEET_DIRS = ['tasks', 'claims', 'heartbeats', 'results', 'archive', 'fleet'];
const VERSION = '3.0.0';
const FLEET_DIRS = ['tasks', 'results'];
const VERSION = '3.1.0';
function generateAgentId() {

@@ -67,3 +68,2 @@ const hostname = os.hostname().replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 32);

const backendName = options.backend;
const fleetPath = path.resolve(options.path);
// Validate backend name

@@ -76,2 +76,36 @@ const available = deps.listBackends();

}
// Resolve fleet path
let fleetPath;
const isOneDrive = backendName === 'onedrive' || backendName === 'onedrive-business';
if (options.path) {
fleetPath = path.resolve(options.path);
}
else if (isOneDrive) {
// Auto-detect OneDrive path
const isBusiness = backendName === 'onedrive-business';
try {
const detectFn = deps.detectOneDrive ?? (() => new onedrive_detector_js_1.OneDriveDetector().detectAccounts());
const accounts = detectFn();
const account = accounts.find((a) => a.isBusiness === isBusiness);
if (!account) {
const key = isBusiness ? 'init.onedrive_business_not_found' : 'init.onedrive_not_found';
console.error(`❌ ${(0, i18n_js_1.t)(key)}`);
process.exitCode = 1;
return;
}
fleetPath = path.join(account.path, 'AgentFleet');
console.log(`📂 ${(0, i18n_js_1.t)('init.onedrive_detected', { path: fleetPath })}`);
}
catch (err) {
const key = isBusiness ? 'init.onedrive_business_not_found' : 'init.onedrive_not_found';
console.error(`❌ ${(0, i18n_js_1.t)(key)}`);
process.exitCode = 1;
return;
}
}
else {
console.error(`❌ ${(0, i18n_js_1.t)('init.path_required_for_backend', { backend: backendName })}`);
process.exitCode = 1;
return;
}
// Check existing config (unless --force)

@@ -78,0 +112,0 @@ if (!options.force) {

@@ -6,7 +6,17 @@ "use strict";

const daemon_1 = require("../services/daemon");
const config_1 = require("../services/config");
const i18n_1 = require("../services/i18n");
function installCommand(_options, _cmdObj, dependencies = {}) {
async function installCommand(_options, _cmdObj, dependencies = {}) {
const autoStartManager = dependencies.autoStartManager ?? (0, auto_start_1.createAutoStartManager)();
const daemonService = dependencies.daemonService ?? new daemon_1.DaemonService();
const exit = dependencies.exit ?? ((code) => process.exit(code));
// Check v3 config exists
const loadConfigFn = dependencies.loadConfigFn ?? config_1.loadConfig;
try {
await loadConfigFn();
}
catch {
console.error(`❌ ${(0, i18n_1.t)('run.no_config')}`);
return exit(1);
}
if (!autoStartManager.isSupported()) {

@@ -13,0 +23,0 @@ console.error(`❌ ${(0, i18n_1.t)('autostart.unsupported', { platform: process.platform })}`);

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });

@@ -12,2 +45,20 @@ exports.runCommand = runCommand;

const protocol_engine_1 = require("../services/protocol-engine");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const AGENTFLEET_DIR = path.join(os.homedir(), '.agentfleet');
function loadProcessedIds(processedPath) {
try {
if (fs.existsSync(processedPath)) {
const data = JSON.parse(fs.readFileSync(processedPath, 'utf-8'));
return new Set(data.processedIds ?? []);
}
}
catch { /* ignore corrupt file */ }
return new Set();
}
function saveProcessedIds(processedPath, ids) {
fs.mkdirSync(path.dirname(processedPath), { recursive: true });
fs.writeFileSync(processedPath, JSON.stringify({ processedIds: Array.from(ids) }, null, 2));
}
async function runCommand(options, dependencies = {}) {

@@ -90,4 +141,3 @@ const exit = dependencies.exit ?? ((code) => process.exit(code));

: new protocol_engine_1.ProtocolEngine(backend, config.agentId, {
convergenceWindowMs: config.convergenceWindowMs,
heartbeatIntervalMs: config.heartbeatIntervalMs,
pollIntervalMs: pollIntervalSeconds * 1000,
});

@@ -101,3 +151,3 @@ // Create executor with v2-compatible config shape

onedriveAccountType: 'personal',
hostname: require('os').hostname(),
hostname: os.hostname(),
defaultAgent: config.defaultAgent ?? 'claude-code',

@@ -113,6 +163,9 @@ defaultAgentCommand: config.defaultAgentCommand ?? 'claude -p {prompt}',

: new agent_executor_1.AgentExecutor(executorConfig);
// Load local processed IDs
const processedPath = dependencies.processedPath ?? path.join(AGENTFLEET_DIR, 'processed.json');
const processedIds = loadProcessedIds(processedPath);
let running = 0;
let shuttingDown = false;
let polling = false;
// Poll-claim loop
// Broadcast poll loop
async function pollCycle() {

@@ -123,108 +176,74 @@ if (shuttingDown || polling)

try {
// 1. Check stale claims and heartbeats
const staleClaims = await engine.checkStaleClaims();
for (const taskId of staleClaims) {
console.log(`🔄 ${(0, i18n_1.t)('protocol.stale_claim_recovered', { taskId })}`);
await engine.resetTaskToPending(taskId);
}
const staleHeartbeats = await engine.checkStaleHeartbeats();
for (const taskId of staleHeartbeats) {
console.log(`🔄 ${(0, i18n_1.t)('protocol.stale_heartbeat_recovered', { taskId })}`);
await engine.resetTaskToPending(taskId);
}
// 2. Skip scan if at max concurrency
// Skip if at max concurrency
if (running >= maxConcurrency)
return;
// 3. Scan for pending tasks
// Scan for pending tasks
const { tasks, errors } = await engine.scan();
// 4. Reject malformed tasks
for (const err of errors) {
const match = err.match(/^tasks\/(.+)\.json:/);
if (match) {
console.log(`⛔ ${(0, i18n_1.t)('run.task_rejected', { taskId: match[1], reason: err })}`);
await engine.rejectTask(match[1], err);
}
console.warn(`⚠️ ${err}`);
}
if (tasks.length === 0)
return;
// 5. Pick highest-priority task
const task = tasks[0];
// 5a. Attempt claim
console.log(`🏁 ${(0, i18n_1.t)('protocol.claim_attempt', { taskId: task.id })}`);
const claimed = await engine.attemptClaim(task.id);
if (!claimed)
return;
// 5b. Wait for convergence
const convergenceMs = engine.getConvergenceWindowMs();
console.log(`⏳ ${(0, i18n_1.t)('run.convergence_waiting', { seconds: (convergenceMs / 1000).toFixed(1), taskId: task.id })}`);
await sleepFn(convergenceMs);
// 5c. Resolve claim winner
const { won, winnerId } = await engine.resolveClaimWinner(task.id);
if (!won) {
console.log(`❌ ${(0, i18n_1.t)('protocol.claim_lost', { taskId: task.id, winnerId })}`);
return;
}
console.log(`✅ ${(0, i18n_1.t)('protocol.claim_won', { taskId: task.id })}`);
// 5d. Update status
await engine.updateTaskStatus(task.id, 'claimed');
await engine.updateTaskStatus(task.id, 'running');
// 5e. Start heartbeat timer
const heartbeatInterval = setInterval(async () => {
try {
await engine.writeHeartbeat(task.id);
// Process unprocessed tasks
for (const task of tasks) {
if (shuttingDown)
break;
if (running >= maxConcurrency)
break;
if (processedIds.has(task.id))
continue;
// Also check if we already have a result on the backend
const alreadyDone = await engine.hasResult(task.id);
if (alreadyDone) {
processedIds.add(task.id);
saveProcessedIds(processedPath, processedIds);
continue;
}
catch (err) {
console.warn(`⚠️ ${(0, i18n_1.t)('run.heartbeat_failed', { taskId: task.id, message: err.message })}`);
}
}, engine.getHeartbeatIntervalMs());
running++;
// 5f. Execute via AgentExecutor
const startTime = Date.now();
try {
await engine.writeHeartbeat(task.id);
const execResult = await executor.execute({
id: task.id,
prompt: task.prompt,
command: task.command || executorConfig.defaultAgentCommand,
workingDirectory: task.workingDirectory,
});
// 5g. Write protocol result
const result = {
taskId: task.id,
agentId: config.agentId,
status: execResult.status === 'completed' ? 'completed' : 'failed',
exitCode: execResult.exitCode,
stdout: execResult.stdout?.substring(0, 64 * 1024),
completedAt: new Date().toISOString(),
durationMs: Date.now() - startTime,
error: execResult.error,
};
// 5h. Archive task
await engine.archiveTask(task.id, result);
}
catch (err) {
console.error(`❌ ${(0, i18n_1.t)('run.execution_error', { taskId: task.id, message: err.message })}`);
// Try to mark as failed
console.log(`\n📋 ${(0, i18n_1.t)('run.new_task', { taskId: task.id, title: task.title ? ` - ${task.title}` : '' })}`);
running++;
const startTime = Date.now();
try {
const failResult = {
const execResult = await executor.execute({
id: task.id,
prompt: task.prompt,
command: task.command || executorConfig.defaultAgentCommand,
workingDirectory: task.workingDirectory,
});
// Write per-agent result
const result = {
taskId: task.id,
agentId: config.agentId,
status: 'failed',
exitCode: null,
status: execResult.status === 'completed' ? 'completed' : 'failed',
exitCode: execResult.exitCode,
stdout: execResult.stdout?.substring(0, 64 * 1024),
completedAt: new Date().toISOString(),
durationMs: Date.now() - startTime,
error: err.message,
error: execResult.error,
};
await engine.archiveTask(task.id, failResult);
await engine.writeResult(task.id, result);
console.log(`✅ ${(0, i18n_1.t)('run.task_completed', { taskId: task.id, duration: ((Date.now() - startTime) / 1000).toFixed(1) })}`);
}
catch {
// Last resort: reset to pending
await engine.resetTaskToPending(task.id);
catch (err) {
console.error(`❌ ${(0, i18n_1.t)('run.execution_error', { taskId: task.id, message: err.message })}`);
// Write failure result
try {
const failResult = {
taskId: task.id,
agentId: config.agentId,
status: 'failed',
exitCode: null,
completedAt: new Date().toISOString(),
durationMs: Date.now() - startTime,
error: err.message,
};
await engine.writeResult(task.id, failResult);
}
catch { /* best effort */ }
}
finally {
running--;
processedIds.add(task.id);
saveProcessedIds(processedPath, processedIds);
}
}
finally {
// 5i. Clear heartbeat timer
clearInterval(heartbeatInterval);
running--;
}
}

@@ -238,3 +257,4 @@ catch (err) {

}
console.log(`\n🟢 ${(0, i18n_1.t)('run.running_on', { hostname: require('os').hostname() })}`);
console.log(`\n🟢 ${(0, i18n_1.t)('run.running_on', { hostname: os.hostname() })}`);
console.log(` ${(0, i18n_1.t)('run.agent_id', { agentId: config.agentId })}`);
console.log(` ${(0, i18n_1.t)('run.concurrency', { value: maxConcurrency })}`);

@@ -241,0 +261,0 @@ console.log(` ${(0, i18n_1.t)('run.poll_interval', { value: pollIntervalSeconds })}`);

@@ -37,18 +37,15 @@ "use strict";

exports.statusCommand = statusCommand;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const setup_1 = require("../services/setup");
const daemon_1 = require("../services/daemon");
const auto_start_1 = require("../services/auto-start");
const version_checker_1 = require("../services/version-checker");
const bootstrap_1 = require("../services/bootstrap");
const config_1 = require("../services/config");
const index_1 = require("../backends/index");
const protocol_engine_1 = require("../services/protocol-engine");
const i18n_1 = require("../services/i18n");
const fs = __importStar(require("fs"));
async function statusCommand(taskId, _cmdObj, dependencies = {}) {
const setup = dependencies.setup ?? new setup_1.SetupService();
const daemonService = dependencies.daemonService ?? new daemon_1.DaemonService();
const autoStartManager = dependencies.autoStartManager ?? (0, auto_start_1.createAutoStartManager)();
const versionChecker = dependencies.versionChecker ?? new version_checker_1.VersionChecker();
await (dependencies.bootstrapFn ?? bootstrap_1.bootstrap)({ setup });
const tasksDir = setup.getTasksDir();
const outputDir = setup.getOutputDir();
const exit = dependencies.exit ?? ((code) => { process.exitCode = code; });
// Show version info

@@ -58,8 +55,27 @@ await showVersionInfo(versionChecker);

showProcessInfo(daemonService, autoStartManager);
// Load v3 config
let config;
try {
const loadConfigFn = dependencies.loadConfigFn ?? config_1.loadConfig;
config = await loadConfigFn();
}
catch (err) {
console.error(`❌ ${(0, i18n_1.t)('run.no_config')}`);
exit(1);
return;
}
// Create backend + engine
const createBackendFn = dependencies.createBackend ?? index_1.getBackend;
const backend = createBackendFn(config.backend, config.backendConfig);
await backend.initialize();
const engine = dependencies.createEngine
? dependencies.createEngine(backend, config.agentId)
: new protocol_engine_1.ProtocolEngine(backend, config.agentId);
if (taskId) {
showTaskDetail(taskId, tasksDir, outputDir);
await showTaskDetail(taskId, engine);
}
else {
showAllTasks(tasksDir, outputDir);
await showAllTasks(engine);
}
await backend.shutdown();
}

@@ -110,33 +126,26 @@ function showProcessInfo(daemonService, autoStartManager) {

}
function showAllTasks(tasksDir, outputDir) {
const taskFiles = fs.readdirSync(tasksDir).filter(f => f.endsWith('.json'));
if (taskFiles.length === 0) {
async function showAllTasks(engine) {
const { tasks, errors } = await engine.listAllTasks();
for (const err of errors) {
console.warn(`⚠️ ${err}`);
}
if (tasks.length === 0) {
console.log((0, i18n_1.t)('status.no_tasks'));
return;
}
console.log(`\n📋 ${(0, i18n_1.t)('status.tasks_header', { count: taskFiles.length })}\n`);
console.log(`\n📋 ${(0, i18n_1.t)('status.tasks_header', { count: tasks.length })}\n`);
console.log(padRight((0, i18n_1.t)('status.col_id'), 36) + padRight((0, i18n_1.t)('status.col_title'), 30) + padRight((0, i18n_1.t)('status.col_status'), 12) + (0, i18n_1.t)('status.col_results'));
console.log('─'.repeat(90));
for (const file of taskFiles) {
for (const task of tasks) {
try {
const content = fs.readFileSync(path.join(tasksDir, file), 'utf-8');
const task = JSON.parse(content);
// Check for results
const taskOutputDir = path.join(outputDir, task.id);
let resultCount = 0;
let machines = [];
if (fs.existsSync(taskOutputDir)) {
const resultFiles = fs.readdirSync(taskOutputDir).filter(f => f.endsWith('-result.json'));
resultCount = resultFiles.length;
machines = resultFiles.map(f => f.replace('-result.json', ''));
}
const status = resultCount > 0 ? (0, i18n_1.t)('status.status_done', { count: resultCount }) : (0, i18n_1.t)('status.status_pending');
const machineStr = machines.length > 0 ? machines.join(', ') : '-';
const agents = await engine.listResults(task.id);
const status = agents.length > 0 ? (0, i18n_1.t)('status.status_done', { count: agents.length }) : (0, i18n_1.t)('status.status_pending');
const agentStr = agents.length > 0 ? agents.join(', ') : '-';
console.log(padRight(task.id, 36) +
padRight(task.title || (0, i18n_1.t)('status.untitled'), 30) +
padRight(status, 12) +
machineStr);
agentStr);
}
catch {
console.log(padRight(file, 36) + (0, i18n_1.t)('status.error_reading'));
console.log(padRight(task.id, 36) + (0, i18n_1.t)('status.error_reading'));
}

@@ -146,20 +155,8 @@ }

}
function showTaskDetail(taskId, tasksDir, outputDir) {
// Find task file
const taskFiles = fs.readdirSync(tasksDir).filter(f => f.endsWith('.json'));
let task = null;
for (const file of taskFiles) {
try {
const content = fs.readFileSync(path.join(tasksDir, file), 'utf-8');
const parsed = JSON.parse(content);
if (parsed.id === taskId) {
task = parsed;
break;
}
}
catch { /* skip */ }
}
async function showTaskDetail(taskId, engine) {
const task = await engine.readTask(taskId);
if (!task) {
console.error(`❌ ${(0, i18n_1.t)('status.task_not_found', { taskId })}`);
process.exit(1);
process.exitCode = 1;
return;
}

@@ -169,36 +166,37 @@ console.log(`\n📋 ${(0, i18n_1.t)('status.task_header', { taskId: task.id })}`);

console.log(` ${(0, i18n_1.t)('status.task_prompt', { prompt: task.prompt })}`);
console.log(` ${(0, i18n_1.t)('status.task_working_dir', { path: task.workingDirectory || (0, i18n_1.t)('status.task_default') })}`);
console.log(` ${(0, i18n_1.t)('status.task_agent', { agent: task.command || (0, i18n_1.t)('status.task_default') })}`);
if (task.workingDirectory) {
console.log(` ${(0, i18n_1.t)('status.task_working_dir', { path: task.workingDirectory })}`);
}
if (task.command) {
console.log(` ${(0, i18n_1.t)('status.task_agent', { agent: task.command })}`);
}
console.log(` ${(0, i18n_1.t)('status.task_created', { date: task.createdAt ? (0, i18n_1.formatDate)(task.createdAt) : (0, i18n_1.t)('status.task_unknown') })}`);
console.log(` ${(0, i18n_1.t)('status.task_created_by', { hostname: task.createdBy || (0, i18n_1.t)('status.task_unknown') })}`);
// Show results
const taskOutputDir = path.join(outputDir, task.id);
if (fs.existsSync(taskOutputDir)) {
const resultFiles = fs.readdirSync(taskOutputDir).filter(f => f.endsWith('-result.json'));
if (resultFiles.length > 0) {
console.log(`\n📊 ${(0, i18n_1.t)('status.results_header', { count: resultFiles.length })}\n`);
for (const resultFile of resultFiles) {
try {
const content = fs.readFileSync(path.join(taskOutputDir, resultFile), 'utf-8');
const result = JSON.parse(content);
const icon = result.status === 'completed' ? '✅' : result.status === 'timeout' ? '⏰' : '❌';
console.log(` ${icon} ${result.hostname}`);
console.log(` ${(0, i18n_1.t)('status.result_status', { status: result.status, exitCode: result.exitCode })}`);
console.log(` ${(0, i18n_1.t)('status.result_started', { date: (0, i18n_1.formatDate)(result.startedAt) })}`);
const agents = await engine.listResults(taskId);
if (agents.length > 0) {
console.log(`\n📊 ${(0, i18n_1.t)('status.results_header', { count: agents.length })}\n`);
for (const agentId of agents) {
try {
const result = await engine.readResult(taskId, agentId);
if (!result)
continue;
const icon = result.status === 'completed' ? '✅' : '❌';
console.log(` ${icon} ${agentId}`);
console.log(` ${(0, i18n_1.t)('status.result_status', { status: result.status, exitCode: result.exitCode ?? 'N/A' })}`);
if (result.completedAt) {
console.log(` ${(0, i18n_1.t)('status.result_completed', { date: (0, i18n_1.formatDate)(result.completedAt) })}`);
if (result.error)
console.log(` ${(0, i18n_1.t)('status.result_error', { error: result.error })}`);
}
catch { /* skip */ }
if (result.durationMs) {
console.log(` Duration: ${(result.durationMs / 1000).toFixed(1)}s`);
}
if (result.error) {
console.log(` ${(0, i18n_1.t)('status.result_error', { error: result.error })}`);
}
if (result.stdout) {
const excerpt = result.stdout.substring(0, 200);
console.log(` Output: ${excerpt}${result.stdout.length > 200 ? '...' : ''}`);
}
}
catch { /* skip */ }
}
// List all output files
const allFiles = fs.readdirSync(taskOutputDir);
if (allFiles.length > 0) {
console.log(`\n📁 ${(0, i18n_1.t)('status.output_files')}`);
for (const f of allFiles) {
const stats = fs.statSync(path.join(taskOutputDir, f));
console.log(` ${f} (${formatBytes(stats.size)})`);
}
}
}

@@ -213,9 +211,2 @@ else {

}
function formatBytes(bytes) {
if (bytes < 1024)
return `${bytes}B`;
if (bytes < 1024 * 1024)
return `${(bytes / 1024).toFixed(1)}KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
}
//# sourceMappingURL=status.js.map
{
"cli.description": "Distributed agent orchestration, without a control plane.",
"cli.run_description": "Start AgentFleet: auto-initialize if needed, then watch for tasks",
"cli.run_description": "Start watching for tasks and execute them",
"cli.run_option_poll": "Polling interval in seconds",

@@ -172,2 +172,6 @@ "cli.run_option_concurrency": "Maximum concurrent agent processes",

"init.option_path": "Path to the shared fleet directory",
"init.onedrive_detected": "OneDrive detected: {path}",
"init.onedrive_not_found": "No personal OneDrive account found. Please install OneDrive and sign in.",
"init.onedrive_business_not_found": "No OneDrive for Business account found. Please install OneDrive and sign in with your work account.",
"init.path_required_for_backend": "The --path option is required for backend '{backend}'.",

@@ -184,2 +188,4 @@ "protocol.claim_attempt": "Attempting claim on task {taskId}",

"run.new_task": "New task: {taskId}{title}",
"run.task_completed": "Task {taskId} completed in {duration}s",
"run.task_rejected": "Task {taskId} rejected: {reason}",

@@ -191,2 +197,3 @@ "run.heartbeat_failed": "Heartbeat failed for task {taskId}: {message}",

"run.convergence_waiting": "Waiting {seconds}s for convergence on task {taskId}...",
"run.agent_id": "Agent ID: {agentId}",

@@ -193,0 +200,0 @@ "submit.created": "Task {taskId} created via v3 protocol",

{
"cli.description": "分布式智能体编排,无需控制平面。",
"cli.run_description": "启动 AgentFleet:如需初始化则自动完成,然后监听任务",
"cli.run_description": "开始监听任务并执行",
"cli.run_option_poll": "轮询间隔(秒)",

@@ -172,2 +172,6 @@ "cli.run_option_concurrency": "最大并发代理进程数",

"init.option_path": "共享 Fleet 目录的路径",
"init.onedrive_detected": "已检测到 OneDrive: {path}",
"init.onedrive_not_found": "未找到个人 OneDrive 帐户。请安装 OneDrive 并登录。",
"init.onedrive_business_not_found": "未找到 OneDrive 企业版帐户。请安装 OneDrive 并使用工作帐户登录。",
"init.path_required_for_backend": "后端 '{backend}' 需要 --path 参数。",

@@ -184,2 +188,4 @@ "protocol.claim_attempt": "正在尝试认领任务 {taskId}",

"run.new_task": "新任务: {taskId}{title}",
"run.task_completed": "任务 {taskId} 已完成,耗时 {duration}秒",
"run.task_rejected": "任务 {taskId} 已拒绝: {reason}",

@@ -191,2 +197,3 @@ "run.heartbeat_failed": "任务 {taskId} 心跳失败: {message}",

"run.convergence_waiting": "正在等待任务 {taskId} 的 {seconds}秒 收敛窗口...",
"run.agent_id": "Agent ID: {agentId}",

@@ -193,0 +200,0 @@ "submit.created": "任务 {taskId} 已通过 v3 协议创建",

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtocolEngine = void 0;
const errors_js_1 = require("../backends/errors.js");
const DEFAULTS = {
convergenceWindowMs: 5000,
heartbeatIntervalMs: 30000,
claimAgeTimeoutFactor: 2, // claimAgeTimeout = convergenceWindow * factor
heartbeatStaleFactor: 3, // heartbeatStale = heartbeatInterval * factor
};
const errors_1 = require("../backends/errors");
/**
* Protocol engine for broadcast execution model.
* Every agent executes every task. No claims, no tiebreaks.
*/
class ProtocolEngine {

@@ -15,20 +13,13 @@ constructor(backend, agentId, options = {}) {

this.agentId = agentId;
const convergence = options.convergenceWindowMs
?? backend.getRecommendedConvergenceWindow()
?? DEFAULTS.convergenceWindowMs;
this.convergenceWindowMs = convergence;
this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULTS.heartbeatIntervalMs;
this.claimAgeTimeoutMs = options.claimAgeTimeoutMs ?? convergence * DEFAULTS.claimAgeTimeoutFactor;
this.heartbeatStaleMs = options.heartbeatStaleMs ?? this.heartbeatIntervalMs * DEFAULTS.heartbeatStaleFactor;
this.pollIntervalMs = options.pollIntervalMs ?? 10000;
}
getConvergenceWindowMs() {
return this.convergenceWindowMs;
getAgentId() {
return this.agentId;
}
getHeartbeatIntervalMs() {
return this.heartbeatIntervalMs;
getPollIntervalMs() {
return this.pollIntervalMs;
}
/**
* Scan tasks/ directory, parse JSON files, return pending tasks sorted by
* priority (desc) then createdAt (asc).
* v2 compat: tasks without status are treated as 'pending'.
* Scan tasks/ for all tasks, return pending ones sorted by priority desc then createdAt asc.
* v2 compat: tasks without a status field are treated as pending.
*/

@@ -42,3 +33,3 @@ async scan() {

catch (err) {
if (err instanceof errors_js_1.NotFoundError) {
if (err instanceof errors_1.NotFoundError) {
return { tasks: [], errors: [] };

@@ -89,19 +80,12 @@ }

/**
* Attempt to claim a task by writing claims/{taskId}/{agentId}.
* Returns true if the claim file was created, false if it already exists.
* Read a single task by ID.
*/
async attemptClaim(taskId) {
const claimPath = `claims/${taskId}/${this.agentId}`;
const claimData = {
agentId: this.agentId,
claimedAt: new Date().toISOString(),
};
async readTask(taskId) {
try {
await this.backend.createExclusive(claimPath, JSON.stringify(claimData));
return true;
const content = await this.backend.readFile(`tasks/${taskId}.json`);
return JSON.parse(content);
}
catch (err) {
if (err instanceof errors_js_1.AlreadyExistsError) {
return false;
}
if (err instanceof errors_1.NotFoundError)
return null;
throw err;

@@ -111,222 +95,87 @@ }

/**
* Resolve claim winner for a task.
* Reads all claim files under claims/{taskId}/, picks lowest agentId (ASCII sort).
* If this agent is not the winner, deletes our own claim.
* Returns { won: boolean, winnerId: string }.
* List all tasks (any status).
*/
async resolveClaimWinner(taskId) {
const claimDir = `claims/${taskId}`;
async listAllTasks() {
const errors = [];
let files;
try {
files = await this.backend.listFiles(claimDir);
files = await this.backend.listFiles('tasks');
}
catch (err) {
if (err instanceof errors_js_1.NotFoundError) {
return { won: false, winnerId: '' };
if (err instanceof errors_1.NotFoundError) {
return { tasks: [], errors: [] };
}
throw err;
}
// Extract agent IDs from file paths
const agentIds = files.map((f) => {
const parts = f.split('/');
return parts[parts.length - 1];
}).filter((id) => id.length > 0);
if (agentIds.length === 0) {
return { won: false, winnerId: '' };
const tasks = [];
for (const file of files) {
if (!file.endsWith('.json'))
continue;
try {
const content = await this.backend.readFile(file);
const parsed = JSON.parse(content);
if (!parsed.id || !parsed.prompt) {
errors.push(`${file}: missing required fields`);
continue;
}
if (!parsed.status)
parsed.status = 'pending';
tasks.push(parsed);
}
catch (err) {
errors.push(`${file}: ${err.message}`);
}
}
// Sort lexicographically, lowest wins
agentIds.sort();
const winnerId = agentIds[0];
const won = winnerId === this.agentId;
// If we lost, delete our claim
if (!won) {
const ourClaim = `claims/${taskId}/${this.agentId}`;
await this.backend.deleteFile(ourClaim);
}
return { won, winnerId };
return { tasks, errors };
}
/**
* Update the status field of a task file. Read-modify-write.
* Write a per-agent result to results/{taskId}/{agentId}.json
*/
async updateTaskStatus(taskId, status) {
const taskPath = `tasks/${taskId}.json`;
const content = await this.backend.readFile(taskPath);
const task = JSON.parse(content);
task.status = status;
task.updatedAt = new Date().toISOString();
await this.backend.writeFile(taskPath, JSON.stringify(task, null, 2));
async writeResult(taskId, result) {
const resultPath = `results/${taskId}/${this.agentId}.json`;
await this.backend.writeFile(resultPath, JSON.stringify(result, null, 2));
}
/**
* Write a heartbeat file for the given task.
* List all agent results for a task.
* Returns array of agentIds that have results.
*/
async writeHeartbeat(taskId) {
const heartbeatPath = `heartbeats/${taskId}`;
const heartbeat = {
agentId: this.agentId,
taskId,
timestamp: new Date().toISOString(),
pid: process.pid,
};
await this.backend.writeFile(heartbeatPath, JSON.stringify(heartbeat));
}
/**
* Check for stale claims: claims older than claimAgeTimeout with no heartbeat.
* Returns task IDs that should be reset to pending.
*/
async checkStaleClaims() {
const staleTaskIds = [];
let claimDirs;
async listResults(taskId) {
try {
claimDirs = await this.backend.listFiles('claims');
const files = await this.backend.listFiles(`results/${taskId}`);
return files
.filter((f) => f.endsWith('.json'))
.map((f) => {
// f is like "results/{taskId}/agent-1.json"
const basename = f.split('/').pop() ?? '';
return basename.replace('.json', '');
});
}
catch (err) {
if (err instanceof errors_js_1.NotFoundError)
if (err instanceof errors_1.NotFoundError)
return [];
throw err;
}
const now = Date.now();
for (const claimDir of claimDirs) {
const taskId = claimDir.split('/').pop();
// Check if there's a heartbeat for this task
const hasHeartbeat = await this.backend.fileExists(`heartbeats/${taskId}`);
if (hasHeartbeat)
continue;
// Check claim age
let claimFiles;
try {
claimFiles = await this.backend.listFiles(claimDir);
}
catch {
continue;
}
let isStale = false;
for (const claimFile of claimFiles) {
const mtime = await this.backend.getFileModifiedTime(claimFile);
if (mtime && (now - mtime.getTime()) > this.claimAgeTimeoutMs) {
isStale = true;
// Delete the stale claim
await this.backend.deleteFile(claimFile);
}
}
if (isStale) {
staleTaskIds.push(taskId);
}
}
return staleTaskIds;
}
/**
* Check for stale heartbeats: heartbeats older than heartbeatStaleMs.
* Returns task IDs that should be reset to pending.
* Read a specific agent's result for a task.
*/
async checkStaleHeartbeats() {
const staleTaskIds = [];
let heartbeatFiles;
async readResult(taskId, agentId) {
try {
heartbeatFiles = await this.backend.listFiles('heartbeats');
const content = await this.backend.readFile(`results/${taskId}/${agentId}.json`);
return JSON.parse(content);
}
catch (err) {
if (err instanceof errors_js_1.NotFoundError)
return [];
if (err instanceof errors_1.NotFoundError)
return null;
throw err;
}
const now = Date.now();
for (const hbFile of heartbeatFiles) {
const mtime = await this.backend.getFileModifiedTime(hbFile);
if (mtime && (now - mtime.getTime()) > this.heartbeatStaleMs) {
const taskId = hbFile.split('/').pop();
staleTaskIds.push(taskId);
// Clean up stale heartbeat
await this.backend.deleteFile(hbFile);
}
}
return staleTaskIds;
}
/**
* Archive a completed task. Write archive + result, then clean up.
* Cleanup: delete claims dir files, heartbeat, task file.
* Check if this agent already has a result for a task.
*/
async archiveTask(taskId, result) {
// Read the task file
const taskPath = `tasks/${taskId}.json`;
const taskContent = await this.backend.readFile(taskPath);
const task = JSON.parse(taskContent);
// Write result
await this.backend.writeFile(`results/${taskId}.json`, JSON.stringify(result, null, 2));
// Write archive
const archived = {
task,
result,
archivedAt: new Date().toISOString(),
};
await this.backend.writeFile(`archive/${taskId}.json`, JSON.stringify(archived, null, 2));
// Clean up claims
try {
const claimFiles = await this.backend.listFiles(`claims/${taskId}`);
for (const f of claimFiles) {
await this.backend.deleteFile(f);
}
}
catch {
// claims dir may not exist
}
// Clean up heartbeat
await this.backend.deleteFile(`heartbeats/${taskId}`);
// Delete task file
await this.backend.deleteFile(taskPath);
async hasResult(taskId) {
return this.backend.fileExists(`results/${taskId}/${this.agentId}.json`);
}
/**
* Reject a malformed task. Write archive with rejected status, then delete task.
*/
async rejectTask(taskId, reason) {
const taskPath = `tasks/${taskId}.json`;
let task;
try {
const content = await this.backend.readFile(taskPath);
task = JSON.parse(content);
}
catch {
// If we can't read the task, create a minimal record
task = {
id: taskId,
prompt: '',
status: 'rejected',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
}
task.status = 'rejected';
task.updatedAt = new Date().toISOString();
const result = {
taskId,
agentId: this.agentId,
status: 'failed',
exitCode: null,
completedAt: new Date().toISOString(),
durationMs: 0,
error: reason,
};
const archived = {
task,
result,
archivedAt: new Date().toISOString(),
};
await this.backend.writeFile(`archive/${taskId}.json`, JSON.stringify(archived, null, 2));
// Delete task file
await this.backend.deleteFile(taskPath);
}
/**
* Reset a task to pending. Used after recovering stale claims/heartbeats.
*/
async resetTaskToPending(taskId) {
try {
await this.updateTaskStatus(taskId, 'pending');
}
catch (err) {
if (err instanceof errors_js_1.NotFoundError)
return; // Task was already cleaned up
throw err;
}
}
}
exports.ProtocolEngine = ProtocolEngine;
//# sourceMappingURL=protocol-engine.js.map
{
"name": "@daomar/agentfleet",
"version": "3.0.0",
"version": "3.1.0",
"description": "Distributed agent orchestration, without a control plane.",

@@ -5,0 +5,0 @@ "main": "dist/cli.js",