@daomar/agentfleet
Advanced tools
| "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 }); | ||
| exports.extractFileReferences = extractFileReferences; | ||
| const fs = __importStar(require("fs")); | ||
| const path = __importStar(require("path")); | ||
| /** | ||
| * Scan stdout for local file references, verify they exist, | ||
| * and rewrite paths to relative references. | ||
| * | ||
| * Detected patterns: | ||
| * - file:///absolute/path/to/file.ext | ||
| * - Standalone absolute paths with known extensions (must exist on disk) | ||
| */ | ||
| function extractFileReferences(stdout) { | ||
| const detectedFiles = []; | ||
| const seenPaths = new Set(); | ||
| let rewrittenStdout = stdout; | ||
| // Pattern 1: file:///path/to/file | ||
| const fileUriRegex = /file:\/\/(\/[^\s"'<>]+\.[a-zA-Z0-9]+)/g; | ||
| let match; | ||
| while ((match = fileUriRegex.exec(stdout)) !== null) { | ||
| const filePath = match[1]; | ||
| if (seenPaths.has(filePath)) | ||
| continue; | ||
| if (existsAndIsFile(filePath)) { | ||
| const targetName = uniqueTargetName(filePath, detectedFiles); | ||
| detectedFiles.push({ sourcePath: filePath, targetName }); | ||
| seenPaths.add(filePath); | ||
| rewrittenStdout = rewrittenStdout.split(`file://${filePath}`).join(`./${targetName}`); | ||
| } | ||
| } | ||
| // Pattern 2: absolute paths with extensions, appearing as standalone tokens | ||
| // Match paths like /home/user/.claude/usage-data/report.html | ||
| // but not paths that are clearly part of code or commands | ||
| const absPathRegex = /(?:^|[\s:])(\/((?:home|Users|tmp|var|opt|etc|usr)[^\s"'<>]*\.[a-zA-Z0-9]+))/gm; | ||
| while ((match = absPathRegex.exec(stdout)) !== null) { | ||
| const filePath = match[1].trim(); | ||
| if (seenPaths.has(filePath)) | ||
| continue; | ||
| if (existsAndIsFile(filePath)) { | ||
| const targetName = uniqueTargetName(filePath, detectedFiles); | ||
| detectedFiles.push({ sourcePath: filePath, targetName }); | ||
| seenPaths.add(filePath); | ||
| rewrittenStdout = rewrittenStdout.split(filePath).join(`./${targetName}`); | ||
| } | ||
| } | ||
| return { rewrittenStdout, detectedFiles }; | ||
| } | ||
| function existsAndIsFile(filePath) { | ||
| try { | ||
| const stat = fs.statSync(filePath); | ||
| return stat.isFile(); | ||
| } | ||
| catch { | ||
| return false; | ||
| } | ||
| } | ||
| /** | ||
| * Generate a unique target filename, handling collisions. | ||
| * e.g. if "report.html" is already taken, use "report-2.html" | ||
| */ | ||
| function uniqueTargetName(filePath, existing) { | ||
| const base = path.basename(filePath); | ||
| const existingNames = new Set(existing.map((f) => f.targetName)); | ||
| if (!existingNames.has(base)) | ||
| return base; | ||
| const ext = path.extname(base); | ||
| const name = path.basename(base, ext); | ||
| let counter = 2; | ||
| while (existingNames.has(`${name}-${counter}${ext}`)) { | ||
| counter++; | ||
| } | ||
| return `${name}-${counter}${ext}`; | ||
| } | ||
| //# sourceMappingURL=artifact-extractor.js.map |
@@ -197,4 +197,7 @@ "use strict"; | ||
| } | ||
| getRootPath() { | ||
| return this.rootPath; | ||
| } | ||
| } | ||
| exports.LocalFolderBackend = LocalFolderBackend; | ||
| //# sourceMappingURL=local-folder.js.map |
@@ -45,2 +45,3 @@ "use strict"; | ||
| const protocol_engine_1 = require("../services/protocol-engine"); | ||
| const artifact_extractor_1 = require("../services/artifact-extractor"); | ||
| const init_1 = require("./init"); | ||
@@ -230,3 +231,5 @@ const fs = __importStar(require("fs")); | ||
| }); | ||
| // Write per-agent result | ||
| // Extract file references from stdout and rewrite paths | ||
| const { rewrittenStdout, detectedFiles } = (0, artifact_extractor_1.extractFileReferences)(execResult.stdout ?? ''); | ||
| // Write per-agent result as directory | ||
| const result = { | ||
@@ -240,6 +243,6 @@ taskId: task.id, | ||
| durationMs: Date.now() - startTime, | ||
| stdout: execResult.stdout?.substring(0, 64 * 1024), | ||
| artifacts: detectedFiles.map((f) => f.targetName), | ||
| error: execResult.error, | ||
| }; | ||
| await engine.writeResult(task.id, result); | ||
| await engine.writeResultDir(task.id, result, rewrittenStdout, detectedFiles); | ||
| console.log(`✅ ${(0, i18n_1.t)('run.task_completed', { taskId: task.id, duration: ((Date.now() - startTime) / 1000).toFixed(1) })}`); | ||
@@ -260,3 +263,3 @@ } | ||
| }; | ||
| await engine.writeResult(task.id, failResult); | ||
| await engine.writeResultDir(task.id, failResult, '', []); | ||
| } | ||
@@ -263,0 +266,0 @@ catch { /* best effort */ } |
@@ -166,3 +166,3 @@ "use strict"; | ||
| } | ||
| printTaskDetail(detail); | ||
| printTaskDetail(detail, engine); | ||
| } | ||
@@ -176,2 +176,4 @@ async function readTaskDetail(taskId, engine) { | ||
| const results = []; | ||
| const stdoutMap = new Map(); | ||
| const artifactsMap = new Map(); | ||
| const agents = await engine.listResults(taskId); | ||
@@ -183,2 +185,16 @@ for (const agentId of agents) { | ||
| results.push({ ...result, agentId: result.agentId ?? agentId }); | ||
| // Try to read stdout.txt (new format) | ||
| const stdout = await engine.readResultStdout(taskId, agentId); | ||
| if (stdout !== null) { | ||
| stdoutMap.set(agentId, stdout); | ||
| } | ||
| else if (result.stdout) { | ||
| // Fallback to inline stdout (old format) | ||
| stdoutMap.set(agentId, result.stdout); | ||
| } | ||
| // List artifact files (new format) | ||
| const artifacts = await engine.listResultArtifacts(taskId, agentId); | ||
| if (artifacts.length > 0) { | ||
| artifactsMap.set(agentId, artifacts); | ||
| } | ||
| } | ||
@@ -190,6 +206,6 @@ } | ||
| } | ||
| return { task, results, errors }; | ||
| return { task, results, stdoutMap, artifactsMap, errors }; | ||
| } | ||
| function printTaskDetail(detail) { | ||
| const { task, results, errors } = detail; | ||
| function printTaskDetail(detail, engine) { | ||
| const { task, results, stdoutMap, artifactsMap, errors } = detail; | ||
| console.log(`\n📋 ${(0, i18n_1.t)('status.task_header', { taskId: task.id })}`); | ||
@@ -209,2 +225,3 @@ console.log(` ${(0, i18n_1.t)('status.task_title', { title: task.title || (0, i18n_1.t)('status.task_title_none') })}`); | ||
| const icon = result.status === 'completed' ? '✅' : '❌'; | ||
| const resultDirPath = engine.resolveAbsolutePath(`results/${task.id}/${result.agentId}`); | ||
| console.log(` ${icon} ${result.agentId}`); | ||
@@ -224,9 +241,23 @@ console.log(` ${(0, i18n_1.t)('status.result_status', { status: result.status, exitCode: result.exitCode ?? 'N/A' })}`); | ||
| } | ||
| if (result.artifacts && result.artifacts.length > 0) { | ||
| console.log(` ${(0, i18n_1.t)('status.result_artifacts', { artifacts: result.artifacts.join(', ') })}`); | ||
| // Show stdout file path | ||
| const stdoutContent = stdoutMap.get(result.agentId); | ||
| if (stdoutContent !== undefined) { | ||
| const stdoutPath = `${resultDirPath}/stdout.txt`; | ||
| console.log(` ${(0, i18n_1.t)('status.result_stdout_file', { path: stdoutPath })}`); | ||
| } | ||
| // Show artifacts with full paths | ||
| const artifacts = artifactsMap.get(result.agentId) ?? result.artifacts; | ||
| if (artifacts && artifacts.length > 0) { | ||
| const fullPaths = artifacts.map((a) => `${resultDirPath}/${a}`); | ||
| console.log(` ${(0, i18n_1.t)('status.result_artifacts', { artifacts: fullPaths.join(', ') })}`); | ||
| } | ||
| if (result.error) { | ||
| console.log(` ${(0, i18n_1.t)('status.result_error', { error: result.error })}`); | ||
| } | ||
| if (result.stdout) { | ||
| // Show stdout excerpt | ||
| if (stdoutContent) { | ||
| const excerpt = stdoutContent.substring(0, 200); | ||
| console.log(` ${(0, i18n_1.t)('status.result_output', { output: `${excerpt}${stdoutContent.length > 200 ? '...' : ''}` })}`); | ||
| } | ||
| else if (result.stdout) { | ||
| const excerpt = result.stdout.substring(0, 200); | ||
@@ -233,0 +264,0 @@ console.log(` ${(0, i18n_1.t)('status.result_output', { output: `${excerpt}${result.stdout.length > 200 ? '...' : ''}` })}`); |
@@ -101,4 +101,4 @@ "use strict"; | ||
| console.log(` ${(0, i18n_1.t)('submit.working_dir', { path: task.workingDirectory || '' })}`); | ||
| console.log(` ${(0, i18n_1.t)('submit.created', { taskId })}`); | ||
| console.log(` ${(0, i18n_1.t)('submit.status_hint', { taskId })}`); | ||
| } | ||
| //# sourceMappingURL=submit.js.map |
@@ -92,2 +92,3 @@ { | ||
| "status.result_artifacts": "Artifacts: {artifacts}", | ||
| "status.result_stdout_file": "Output file: {path}", | ||
| "status.result_error": "Error: {error}", | ||
@@ -208,2 +209,3 @@ "status.result_output": "Output: {output}", | ||
| "submit.created": "Task {taskId} created via v3 protocol", | ||
| "submit.status_hint": "Run: agentfleet status {taskId}", | ||
| "submit.error": "Failed to submit task: {message}", | ||
@@ -210,0 +212,0 @@ "submit.auto_init": "No configuration found. Initializing AgentFleet with OneDrive...", |
@@ -92,2 +92,3 @@ { | ||
| "status.result_artifacts": "产物: {artifacts}", | ||
| "status.result_stdout_file": "输出文件: {path}", | ||
| "status.result_error": "错误: {error}", | ||
@@ -208,2 +209,3 @@ "status.result_output": "输出: {output}", | ||
| "submit.created": "任务 {taskId} 已通过 v3 协议创建", | ||
| "submit.status_hint": "运行: agentfleet status {taskId}", | ||
| "submit.error": "提交任务失败: {message}", | ||
@@ -210,0 +212,0 @@ "submit.auto_init": "未找到配置。正在使用 OneDrive 初始化 AgentFleet...", |
| "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 }); | ||
| exports.ProtocolEngine = void 0; | ||
| const fs = __importStar(require("fs")); | ||
| const errors_1 = require("../backends/errors"); | ||
@@ -22,2 +56,9 @@ /** | ||
| /** | ||
| * Resolve a relative path to an absolute path using the backend root. | ||
| */ | ||
| resolveAbsolutePath(relativePath) { | ||
| const root = this.backend.getRootPath(); | ||
| return `${root}/${relativePath}`; | ||
| } | ||
| /** | ||
| * Scan tasks/ for all tasks, return pending ones sorted by priority desc then createdAt asc. | ||
@@ -130,2 +171,3 @@ * v2 compat: tasks without a status field are treated as pending. | ||
| * Write a per-agent result to results/{taskId}/{agentId}.json | ||
| * @deprecated Use writeResultDir for new results | ||
| */ | ||
@@ -137,15 +179,54 @@ async writeResult(taskId, result) { | ||
| /** | ||
| * Write a per-agent result as a directory: | ||
| * results/{taskId}/{agentId}/result.json — metadata | ||
| * results/{taskId}/{agentId}/stdout.txt — raw stdout | ||
| * results/{taskId}/{agentId}/<filename> — artifact files | ||
| */ | ||
| async writeResultDir(taskId, result, stdout, files) { | ||
| const base = `results/${taskId}/${this.agentId}`; | ||
| // Write metadata | ||
| await this.backend.writeFile(`${base}/result.json`, JSON.stringify(result, null, 2)); | ||
| // Write stdout as standalone file | ||
| await this.backend.writeFile(`${base}/stdout.txt`, stdout); | ||
| // Copy detected artifact files | ||
| for (const file of files) { | ||
| try { | ||
| const content = fs.readFileSync(file.sourcePath, 'utf-8'); | ||
| await this.backend.writeFile(`${base}/${file.targetName}`, content); | ||
| } | ||
| catch { | ||
| // Best effort — file may have been removed after detection | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * List all agent results for a task. | ||
| * Returns array of agentIds that have results. | ||
| * Supports both old format ({agentId}.json) and new format ({agentId}/result.json). | ||
| */ | ||
| async listResults(taskId) { | ||
| try { | ||
| 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', ''); | ||
| }); | ||
| const entries = await this.backend.listFiles(`results/${taskId}`); | ||
| const agentIds = new Set(); | ||
| for (const entry of entries) { | ||
| const basename = entry.split('/').pop() ?? ''; | ||
| if (basename.endsWith('.json')) { | ||
| // Old format: agent-id.json (but not result.json inside a subdir) | ||
| agentIds.add(basename.replace('.json', '')); | ||
| } | ||
| else { | ||
| // New format: directory name is the agentId | ||
| // Verify it has a result.json inside | ||
| try { | ||
| const exists = await this.backend.fileExists(`${entry}/result.json`); | ||
| if (exists) { | ||
| agentIds.add(basename); | ||
| } | ||
| } | ||
| catch { | ||
| // Not a valid result dir, skip | ||
| } | ||
| } | ||
| } | ||
| return [...agentIds]; | ||
| } | ||
@@ -160,5 +241,16 @@ catch (err) { | ||
| * Read a specific agent's result for a task. | ||
| * Tries new format (directory) first, falls back to old format (.json file). | ||
| */ | ||
| async readResult(taskId, agentId) { | ||
| // Try new format first: results/{taskId}/{agentId}/result.json | ||
| try { | ||
| const content = await this.backend.readFile(`results/${taskId}/${agentId}/result.json`); | ||
| return JSON.parse(content); | ||
| } | ||
| catch (err) { | ||
| if (!(err instanceof errors_1.NotFoundError)) | ||
| throw err; | ||
| } | ||
| // Fallback to old format: results/{taskId}/{agentId}.json | ||
| try { | ||
| const content = await this.backend.readFile(`results/${taskId}/${agentId}.json`); | ||
@@ -174,5 +266,42 @@ return JSON.parse(content); | ||
| /** | ||
| * Read the stdout.txt for a result (new format only). | ||
| * Returns null if not found or if using old format. | ||
| */ | ||
| async readResultStdout(taskId, agentId) { | ||
| try { | ||
| return await this.backend.readFile(`results/${taskId}/${agentId}/stdout.txt`); | ||
| } | ||
| catch (err) { | ||
| if (err instanceof errors_1.NotFoundError) | ||
| return null; | ||
| throw err; | ||
| } | ||
| } | ||
| /** | ||
| * List artifact files for a result (new format only). | ||
| * Returns filenames excluding result.json and stdout.txt. | ||
| */ | ||
| async listResultArtifacts(taskId, agentId) { | ||
| try { | ||
| const entries = await this.backend.listFiles(`results/${taskId}/${agentId}`); | ||
| return entries | ||
| .map((e) => e.split('/').pop() ?? '') | ||
| .filter((name) => name !== 'result.json' && name !== 'stdout.txt' && name.length > 0); | ||
| } | ||
| catch (err) { | ||
| if (err instanceof errors_1.NotFoundError) | ||
| return []; | ||
| throw err; | ||
| } | ||
| } | ||
| /** | ||
| * Check if this agent already has a result for a task. | ||
| * Checks both new format (directory) and old format (.json file). | ||
| */ | ||
| async hasResult(taskId) { | ||
| // Check new format first | ||
| const newExists = await this.backend.fileExists(`results/${taskId}/${this.agentId}/result.json`); | ||
| if (newExists) | ||
| return true; | ||
| // Fallback to old format | ||
| return this.backend.fileExists(`results/${taskId}/${this.agentId}.json`); | ||
@@ -179,0 +308,0 @@ } |
+1
-1
| { | ||
| "name": "@daomar/agentfleet", | ||
| "version": "3.1.4", | ||
| "version": "3.1.5", | ||
| "description": "Distributed agent orchestration, without a control plane.", | ||
@@ -5,0 +5,0 @@ "main": "dist/cli.js", |
AI-detected potential malware
Supply chain riskAI has identified this package as malware. This is a strong signal that the package may be malicious.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 3 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
AI-detected potential malware
Supply chain riskAI has identified this package as malware. This is a strong signal that the package may be malicious.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
414949
2.95%43
2.38%4384
6.74%25
13.64%