@hilbras/omninode 2.0.0-alpha.11 → 2.0.0-alpha.13
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/README.md +16 -2
- package/dist/cli/index.js +292 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +78 -11
- package/dist/index.js +212 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1995,6 +1995,26 @@ type PlannerConfigYaml = z.infer<typeof plannerConfigSchema>;
|
|
|
1995
1995
|
type ProjectConfigYaml = z.infer<typeof projectConfigSchema>;
|
|
1996
1996
|
type AppConfigYaml = z.infer<typeof appConfigSchema>;
|
|
1997
1997
|
|
|
1998
|
+
/**
|
|
1999
|
+
* Inline secret detection (roadmap §17, Secrets).
|
|
2000
|
+
*
|
|
2001
|
+
* OmniNode configuration must never contain literal credentials — only
|
|
2002
|
+
* environment-variable *references*. This scanner runs before the config is
|
|
2003
|
+
* parsed and reports anything that looks like an inline secret, so the file
|
|
2004
|
+
* is rejected before it can be committed or persisted.
|
|
2005
|
+
*/
|
|
2006
|
+
interface SecretFinding {
|
|
2007
|
+
/** 1-based line number in the source. */
|
|
2008
|
+
line: number;
|
|
2009
|
+
/** The offending key, e.g. `api_key`. */
|
|
2010
|
+
key: string;
|
|
2011
|
+
/** Redacted excerpt — never echoes the value. */
|
|
2012
|
+
excerpt: string;
|
|
2013
|
+
}
|
|
2014
|
+
declare function scanForInlineSecrets(source: string): SecretFinding[];
|
|
2015
|
+
/** Formats findings as an actionable configuration error message. */
|
|
2016
|
+
declare function describeSecretFindings(file: string, findings: SecretFinding[]): string;
|
|
2017
|
+
|
|
1998
2018
|
interface AppConfig {
|
|
1999
2019
|
project: {
|
|
2000
2020
|
name: string;
|
|
@@ -2391,27 +2411,55 @@ declare class RoleRegistry {
|
|
|
2391
2411
|
/** Builds a role registry from the project configuration. */
|
|
2392
2412
|
declare function buildRoleRegistry(config: AppConfig): RoleRegistry;
|
|
2393
2413
|
|
|
2414
|
+
/** Current on-disk schema version. */
|
|
2415
|
+
declare const SCHEMA_VERSION = 2;
|
|
2394
2416
|
interface JsonDocument<T> {
|
|
2395
|
-
/** Bumped when the persisted shape changes in a breaking way (v2 Phase 12). */
|
|
2396
2417
|
schemaVersion?: number;
|
|
2397
2418
|
items: T[];
|
|
2398
2419
|
}
|
|
2420
|
+
type DocumentState = {
|
|
2421
|
+
status: "missing";
|
|
2422
|
+
} | {
|
|
2423
|
+
status: "empty";
|
|
2424
|
+
} | {
|
|
2425
|
+
status: "v1-legacy";
|
|
2426
|
+
} | {
|
|
2427
|
+
status: "current";
|
|
2428
|
+
schemaVersion: number;
|
|
2429
|
+
count: number;
|
|
2430
|
+
} | {
|
|
2431
|
+
status: "future";
|
|
2432
|
+
schemaVersion: number;
|
|
2433
|
+
} | {
|
|
2434
|
+
status: "corrupt";
|
|
2435
|
+
detail: string;
|
|
2436
|
+
};
|
|
2399
2437
|
interface JsonFileStoreOptions {
|
|
2400
2438
|
directory?: string;
|
|
2401
2439
|
fileName: string;
|
|
2440
|
+
/** Schema version to write (defaults to the current version). */
|
|
2402
2441
|
schemaVersion?: number;
|
|
2403
|
-
/** Called when a persisted file
|
|
2442
|
+
/** Called when a persisted file is corrupt (after quarantine). */
|
|
2404
2443
|
onCorrupt?: (path: string, error: unknown) => void;
|
|
2405
|
-
|
|
2444
|
+
/** Disable file moves (used by dry runs / tests that inspect in place). */
|
|
2445
|
+
quarantine?: boolean;
|
|
2446
|
+
}
|
|
2447
|
+
/** Durably writes a file: temp → fsync → rename → directory fsync. */
|
|
2448
|
+
declare function writeFileAtomic(target: string, contents: string): Promise<void>;
|
|
2449
|
+
/** Reads and classifies a persisted document without modifying it. */
|
|
2450
|
+
declare function inspectDocument(path: string): Promise<DocumentState>;
|
|
2451
|
+
/** Reads a document, migrating v1 shapes and quarantining corruption. */
|
|
2452
|
+
declare function readDocument<T>(path: string, onCorrupt?: (path: string, error: unknown) => void, quarantine?: boolean): Promise<T[]>;
|
|
2406
2453
|
declare class JsonFileStore<T> {
|
|
2407
2454
|
private readonly filePath;
|
|
2408
2455
|
private readonly schemaVersion;
|
|
2409
2456
|
private readonly onCorrupt?;
|
|
2457
|
+
private readonly quarantine;
|
|
2410
2458
|
private readonly log;
|
|
2411
2459
|
private queue;
|
|
2412
2460
|
constructor(options: JsonFileStoreOptions);
|
|
2413
2461
|
get path(): string;
|
|
2414
|
-
/** All items, or [] when missing/corrupt. */
|
|
2462
|
+
/** All items, or [] when missing/migrated-from-v1/corrupt. */
|
|
2415
2463
|
protected readAll(): Promise<T[]>;
|
|
2416
2464
|
protected writeAll(items: T[]): Promise<void>;
|
|
2417
2465
|
/** Serializes an async operation against this file. */
|
|
@@ -2676,6 +2724,8 @@ interface CombinedMeta {
|
|
|
2676
2724
|
pipelineRunId?: string;
|
|
2677
2725
|
taskIds?: string[];
|
|
2678
2726
|
}
|
|
2727
|
+
/** Serialized size budget for a single report (roadmap §17 — output limits). */
|
|
2728
|
+
declare const MAX_REPORT_BYTES: number;
|
|
2679
2729
|
interface CollectionDiagnostics {
|
|
2680
2730
|
collected: number;
|
|
2681
2731
|
accepted: number;
|
|
@@ -3017,11 +3067,9 @@ declare function createDefaultChatFn(providers: AppConfig["project"]["providers"
|
|
|
3017
3067
|
declare function buildPipelineEngine(config: AppConfig, log?: Logger): PipelineEngine;
|
|
3018
3068
|
|
|
3019
3069
|
/**
|
|
3020
|
-
* Persistence layer (v2 Phase
|
|
3021
|
-
*
|
|
3022
|
-
*
|
|
3023
|
-
* (TaskStore, PipelineRunStore, ReportStore, PlanStore, IMemoryProvider,
|
|
3024
|
-
* AuditSink) — see docs/API.md for the inventory and stability status.
|
|
3070
|
+
* Persistence layer (v2 Phase 12): one entry point to every local store,
|
|
3071
|
+
* the canonical storage interface names, and the migration path that upgrades
|
|
3072
|
+
* v1 data in place.
|
|
3025
3073
|
*/
|
|
3026
3074
|
|
|
3027
3075
|
/** Bundles every store for a single project directory. */
|
|
@@ -3034,7 +3082,26 @@ declare class ProjectStores {
|
|
|
3034
3082
|
readonly audit: FileAuditLog;
|
|
3035
3083
|
constructor(directory?: string);
|
|
3036
3084
|
}
|
|
3085
|
+
interface MigrationReport {
|
|
3086
|
+
file: string;
|
|
3087
|
+
state: DocumentState["status"];
|
|
3088
|
+
before?: number;
|
|
3089
|
+
after?: number;
|
|
3090
|
+
migrated: boolean;
|
|
3091
|
+
note?: string;
|
|
3092
|
+
}
|
|
3093
|
+
/** Every persisted store file OmniNode manages in a project. */
|
|
3094
|
+
declare const STORE_FILES: readonly ["tasks.json", "pipelines.json", "reports.json", "plans.json", "memory.json"];
|
|
3095
|
+
/**
|
|
3096
|
+
* Migration (roadmap §16): inspects every store file, reports its schema
|
|
3097
|
+
* state, and — unless `checkOnly` — rewrites v1 documents at the current
|
|
3098
|
+
* schema version. Idempotent and safe: unknown/future versions are reported,
|
|
3099
|
+
* never rewritten.
|
|
3100
|
+
*/
|
|
3101
|
+
declare function migrateProjectStores(directory?: string, options?: {
|
|
3102
|
+
checkOnly?: boolean;
|
|
3103
|
+
}): Promise<MigrationReport[]>;
|
|
3037
3104
|
|
|
3038
|
-
declare const OMNINODE_VERSION = "2.0.0-alpha.
|
|
3105
|
+
declare const OMNINODE_VERSION = "2.0.0-alpha.13";
|
|
3039
3106
|
|
|
3040
|
-
export { AgentAdapterRegistry, type AgentConfig, type AgentConfigYaml, type AgentDescriptor, AgentError, type AgentInfo, type AgentInputMode, type AgentIntegrationType, AgentRegistry, type AgentStatus, type AgentTaskInput, type AgentTaskOutput, type AgentToNodeMessage, type AggregateOptions, type AggregatedFinding, type AggregatedRecommendation, type AppConfig, type AppConfigYaml, type ArtifactKind, type ArtifactPayload, type AskedQuestion, type AuditAction, type AuditEvent, type AuditSink, type ChatFn, type ChatMessage, type ChatRequest, type ChatResponse, type ChatRole, type ChatStreamChunk, type ChatUsage, type CollectionDiagnostics, type CombinedMeta, type CombinedReport, type CombinedReportFilter, type Confidence, ConfigError, type ConflictPosition, ConsoleLogSink, type CreateTaskInput, type CwdValidation, DEFAULT_MAX_OUTPUT_BYTES, type DecodeResult, type DiscoveryResult, type EnvPolicy, type EnvelopeMeta, type Evidence, type EvidenceInput, type EvidenceKind, type ExecutionRecord, type ExtractionMeta, FileAuditLog, FilePipelineRunStore, FilePlanStore, FileReportStore, FileTaskStore, type Finding, type FindingOrigin, type FindingSeverity, type FindingSource, type HelloResult, HeuristicPlanner, type HttpRequestOptions, type HttpResponse, type IAgent, type IAgentAdapter, type IChatProvider, type IMemoryProvider, type IPipelineExecutor, type IPlanner, type IProvider, type JsonDocument, JsonFileStore, type JsonFileStoreOptions, type LoadConfigOptions, LocalMemoryProvider, type LogLevel, type LogSink, Logger, type LoggerOptions, MAX_CONTEXT_CHARS, MAX_MESSAGE_BYTES, type MemoryCategory, type MemoryContextQuery, type MemoryEntry, MemoryError, type MemoryProviderConfig, type MemoryProviderMetadata, type MemoryQuery, type MemoryScope, MemoryService, type MemoryServiceOptions, type MemoryTaskContext, type MessageEnvelope, type Modality, type ModelCapabilities, type ModelFilter, type ModelInfo, ModelPlanner, ModelRegistry, type ModelStatus, type NodeToAgentMessage, OMNINODE_VERSION, OPENAI_COMPATIBLE_TYPES, type OmniHilbrasConnectionResult, OmniHilbrasProvider, OmniNodeError, type OmniNodeErrorCode, type OmniNodeErrorOptions, OpenAICompatibleProvider, PROTOCOL_V2, type PipelineConfigYaml, type PipelineDefinition, PipelineEngine, type PipelineEngineOptions, PipelineError, type PipelineRun, type PipelineRunAttempt, type PipelineRunFilter, type PipelineRunOptions, type PipelineRunStatus, type PipelineRunStore, type PipelineStep, type PipelineStepCondition, type PipelineStepConfigYaml, type PipelineStepKind, type PipelineStepRun, type Plan, type PlanFilter, type PlanJsonResult, type PlanRequest, type PlanStep, type PlanStore, type PlanValidationIssue, type PlanValidationResult, type PlannerConfigYaml, PlannerError, ProcessAgent, ProcessAgentAdapter, type Project, type ProjectConfigYaml, ProjectStores, type ProtocolAgentToNodeType, ProtocolDecoder, type ProtocolEnvelope, ProtocolError, type ProtocolMessageType, type ProtocolNodeToAgentType, ProtocolSession, type ProtocolViolation, type ProtocolViolationCode, type ProviderAuthentication, type ProviderCapability, type ProviderConfig, type ProviderConfigYaml, ProviderError, type ProviderErrorKind, type ProviderHealth, type ProviderStatus, type ProviderType, type QuestionPayload, type QuestionResponder, type RememberaConfig, RememberaMemoryProvider, type Report, type ReportArtifact, type ReportConflict, type ReportFilter, type ReportPayload, ReportService, type ReportStore, type ReportValidationIssue, type ReportValidationResult, type RoleConfigYaml, type RoleDefinition, RoleRegistry, type RunPipelineOptions, SUPPORTED_PROTOCOL_VERSIONS, type SessionIds, TERMINAL_TASK_STATUSES, type Task, type TaskContext, TaskEngine, type TaskEngineOptions, TaskError, type TaskPayload, type TaskResult, type TaskStatus, type TaskStore, type TaskStoreFilter, type WritableLike, agentConfigSchema, aggregateReports, appConfigSchema, authHeaders, buildAgentRegistry, buildChildEnv, buildHello, buildHelloAck, buildPipelineEngine, buildPlanner, buildPlannerContext, buildRoleRegistry, composeTaskText, createAgent, createDefaultChatFn, createMemoryProvider, createProvider, createTaskEngine, decodeMessage, decodeStream, defaultMetadata, defaultProjectConfigYaml, defaultSearch, describeExit, detectConflicts, encodeMessage, envelope, expandEnvRefs, extractMessage, extractReportFromText, findConfigFile, isNetworkError, isOmniNodeError, isRetryable, isSupportedProtocol, isTimeoutError, keywords, killTree, kindFromStatus, loadConfig, logger, newMessageId, normalizeEvidence, normalizeFinding, parseHello, parsePlanJson, parseRetryAfter, pipelineConfigSchema, pipelineStepConfigSchema, planJsonSchema, plannerConfigSchema, projectConfigSchema, providerConfigSchema, providerHttpError, providerTransportError, registryKey, requestJson, requireCompletion, resolveApiKey, resolveEnvPolicy, roleConfigSchema, sanitizeChunk, similar, validateEnvelope, validatePlan, validateReport, validateWorkingDirectory };
|
|
3107
|
+
export { AgentAdapterRegistry, type AgentConfig, type AgentConfigYaml, type AgentDescriptor, AgentError, type AgentInfo, type AgentInputMode, type AgentIntegrationType, AgentRegistry, type AgentStatus, type AgentTaskInput, type AgentTaskOutput, type AgentToNodeMessage, type AggregateOptions, type AggregatedFinding, type AggregatedRecommendation, type AppConfig, type AppConfigYaml, type ArtifactKind, type ArtifactPayload, type AskedQuestion, type AuditAction, type AuditEvent, type AuditSink, type AuditSink as AuditStore, type ChatFn, type ChatMessage, type ChatRequest, type ChatResponse, type ChatRole, type ChatStreamChunk, type ChatUsage, type CollectionDiagnostics, type CombinedMeta, type CombinedReport, type CombinedReportFilter, type Confidence, ConfigError, type ConflictPosition, ConsoleLogSink, type CreateTaskInput, type CwdValidation, DEFAULT_MAX_OUTPUT_BYTES, type DecodeResult, type DiscoveryResult, type DocumentState, type EnvPolicy, type EnvelopeMeta, type Evidence, type EvidenceInput, type EvidenceKind, type ExecutionRecord, type ExtractionMeta, FileAuditLog, FilePipelineRunStore, FilePlanStore, FileReportStore, FileTaskStore, type Finding, type FindingOrigin, type FindingSeverity, type FindingSource, type HelloResult, HeuristicPlanner, type HttpRequestOptions, type HttpResponse, type IAgent, type IAgentAdapter, type IChatProvider, type IMemoryProvider, type IPipelineExecutor, type IPlanner, type IProvider, type JsonDocument, JsonFileStore, type JsonFileStoreOptions, type LoadConfigOptions, LocalMemoryProvider, type LogLevel, type LogSink, Logger, type LoggerOptions, MAX_CONTEXT_CHARS, MAX_MESSAGE_BYTES, MAX_REPORT_BYTES, type MemoryCategory, type MemoryContextQuery, type MemoryEntry, MemoryError, type MemoryProviderConfig, type MemoryProviderMetadata, type MemoryQuery, type MemoryScope, MemoryService, type MemoryServiceOptions, LocalMemoryProvider as MemoryStore, type MemoryTaskContext, type MessageEnvelope, type MigrationReport, type Modality, type ModelCapabilities, type ModelFilter, type ModelInfo, ModelPlanner, ModelRegistry, type ModelStatus, type NodeToAgentMessage, OMNINODE_VERSION, OPENAI_COMPATIBLE_TYPES, type OmniHilbrasConnectionResult, OmniHilbrasProvider, OmniNodeError, type OmniNodeErrorCode, type OmniNodeErrorOptions, OpenAICompatibleProvider, PROTOCOL_V2, type PipelineConfigYaml, type PipelineDefinition, PipelineEngine, type PipelineEngineOptions, PipelineError, type PipelineRun, type PipelineRunAttempt, type PipelineRunFilter, type PipelineRunOptions, type PipelineRunStatus, type PipelineRunStore, type PipelineStep, type PipelineStepCondition, type PipelineStepConfigYaml, type PipelineStepKind, type PipelineStepRun, type PipelineRunStore as PipelineStore, type Plan, type PlanFilter, type PlanJsonResult, type PlanRequest, type PlanStep, type PlanStore, type PlanValidationIssue, type PlanValidationResult, type PlannerConfigYaml, PlannerError, ProcessAgent, ProcessAgentAdapter, type Project, type ProjectConfigYaml, ProjectStores, type ProtocolAgentToNodeType, ProtocolDecoder, type ProtocolEnvelope, ProtocolError, type ProtocolMessageType, type ProtocolNodeToAgentType, ProtocolSession, type ProtocolViolation, type ProtocolViolationCode, type ProviderAuthentication, type ProviderCapability, type ProviderConfig, type ProviderConfigYaml, ProviderError, type ProviderErrorKind, type ProviderHealth, type ProviderStatus, type ProviderType, type QuestionPayload, type QuestionResponder, type RememberaConfig, RememberaMemoryProvider, type Report, type ReportArtifact, type ReportConflict, type ReportFilter, type ReportPayload, ReportService, type ReportStore, type ReportValidationIssue, type ReportValidationResult, type RoleConfigYaml, type RoleDefinition, RoleRegistry, type RunPipelineOptions, SCHEMA_VERSION, STORE_FILES, SUPPORTED_PROTOCOL_VERSIONS, type SecretFinding, type SessionIds, TERMINAL_TASK_STATUSES, type Task, type TaskContext, TaskEngine, type TaskEngineOptions, TaskError, type TaskPayload, type TaskResult, type TaskStatus, type TaskStore, type TaskStoreFilter, type WritableLike, agentConfigSchema, aggregateReports, appConfigSchema, authHeaders, buildAgentRegistry, buildChildEnv, buildHello, buildHelloAck, buildPipelineEngine, buildPlanner, buildPlannerContext, buildRoleRegistry, composeTaskText, createAgent, createDefaultChatFn, createMemoryProvider, createProvider, createTaskEngine, decodeMessage, decodeStream, defaultMetadata, defaultProjectConfigYaml, defaultSearch, describeExit, describeSecretFindings, detectConflicts, encodeMessage, envelope, expandEnvRefs, extractMessage, extractReportFromText, findConfigFile, inspectDocument, isNetworkError, isOmniNodeError, isRetryable, isSupportedProtocol, isTimeoutError, keywords, killTree, kindFromStatus, loadConfig, logger, migrateProjectStores, newMessageId, normalizeEvidence, normalizeFinding, parseHello, parsePlanJson, parseRetryAfter, pipelineConfigSchema, pipelineStepConfigSchema, planJsonSchema, plannerConfigSchema, projectConfigSchema, providerConfigSchema, providerHttpError, providerTransportError, readDocument, registryKey, requestJson, requireCompletion, resolveApiKey, resolveEnvPolicy, roleConfigSchema, sanitizeChunk, scanForInlineSecrets, similar, validateEnvelope, validatePlan, validateReport, validateWorkingDirectory, writeFileAtomic };
|
package/dist/index.js
CHANGED
|
@@ -202,6 +202,39 @@ var appConfigSchema = z.object({
|
|
|
202
202
|
logging: z.object({ level: z.enum(["debug", "info", "warn", "error"]) }).strict().optional()
|
|
203
203
|
}).strict();
|
|
204
204
|
|
|
205
|
+
// src/config/secrets.ts
|
|
206
|
+
var SAFE_KEY_SUFFIXES = ["_env", "_env_var", "_file", "_path", "_ref", "_id", "_url", "_command"];
|
|
207
|
+
var SECRET_KEY = /^\s*([A-Za-z0-9_-]*(?:api[_-]?key|token|password|passwd|secret|credential)[A-Za-z0-9_-]*)\s*:\s*(.*)$/i;
|
|
208
|
+
var SAFE_VALUE = /^\s*(?:\$\{[^}]*\}|\$[A-Z_][A-Z0-9_]*|<[^>]*>|""|''|null|~)\s*$/i;
|
|
209
|
+
function scanForInlineSecrets(source) {
|
|
210
|
+
const findings = [];
|
|
211
|
+
source.split("\n").forEach((line, index) => {
|
|
212
|
+
const trimmed = line.trim();
|
|
213
|
+
if (trimmed.startsWith("#")) return;
|
|
214
|
+
const match = SECRET_KEY.exec(line);
|
|
215
|
+
if (!match) return;
|
|
216
|
+
const key = match[1] ?? "";
|
|
217
|
+
const rawValue = (match[2] ?? "").trim().replace(/,\s*$/, "");
|
|
218
|
+
const keyIsReference = SAFE_KEY_SUFFIXES.some((suffix) => key.toLowerCase().endsWith(suffix));
|
|
219
|
+
if (keyIsReference) return;
|
|
220
|
+
const value = rawValue.replace(/^["']|["']$/g, "");
|
|
221
|
+
if (value.length === 0 || SAFE_VALUE.test(rawValue)) return;
|
|
222
|
+
if (value.length < 8) return;
|
|
223
|
+
findings.push({
|
|
224
|
+
line: index + 1,
|
|
225
|
+
key,
|
|
226
|
+
excerpt: `${key}: ${value.slice(0, 3)}\u2026 (${value.length} chars, redacted)`
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
return findings;
|
|
230
|
+
}
|
|
231
|
+
function describeSecretFindings(file, findings) {
|
|
232
|
+
const lines = findings.map((f) => ` line ${f.line}: ${f.excerpt}`).join("\n");
|
|
233
|
+
return `${file} appears to contain ${findings.length} inline secret(s):
|
|
234
|
+
${lines}
|
|
235
|
+
OmniNode never stores credentials in configuration. Replace each value with an environment-variable reference, e.g. api_key_env_var: MY_API_KEY.`;
|
|
236
|
+
}
|
|
237
|
+
|
|
205
238
|
// src/config/loader.ts
|
|
206
239
|
import { existsSync, readFileSync } from "fs";
|
|
207
240
|
import { parse as parseYaml } from "yaml";
|
|
@@ -238,6 +271,10 @@ function loadConfig(options = {}) {
|
|
|
238
271
|
);
|
|
239
272
|
}
|
|
240
273
|
const raw = readFileSync(path, "utf8");
|
|
274
|
+
const secretFindings = scanForInlineSecrets(raw);
|
|
275
|
+
if (secretFindings.length > 0) {
|
|
276
|
+
throw new ConfigError("CONFIG_INVALID", describeSecretFindings(path, secretFindings));
|
|
277
|
+
}
|
|
241
278
|
const expanded = expandEnvRefs(raw, env, path);
|
|
242
279
|
let data;
|
|
243
280
|
try {
|
|
@@ -1820,51 +1857,119 @@ function buildRoleRegistry(config) {
|
|
|
1820
1857
|
}
|
|
1821
1858
|
|
|
1822
1859
|
// src/persistence/json-file-store.ts
|
|
1823
|
-
import { mkdir, readFile, rename,
|
|
1860
|
+
import { open, mkdir, readFile, rename, stat } from "fs/promises";
|
|
1824
1861
|
import { dirname } from "path";
|
|
1862
|
+
var SCHEMA_VERSION = 2;
|
|
1863
|
+
async function writeFileAtomic(target, contents) {
|
|
1864
|
+
const tmp = `${target}.tmp`;
|
|
1865
|
+
await mkdir(dirname(target), { recursive: true });
|
|
1866
|
+
const handle = await open(tmp, "w");
|
|
1867
|
+
try {
|
|
1868
|
+
await handle.writeFile(contents, "utf8");
|
|
1869
|
+
await handle.sync();
|
|
1870
|
+
} finally {
|
|
1871
|
+
await handle.close();
|
|
1872
|
+
}
|
|
1873
|
+
await rename(tmp, target);
|
|
1874
|
+
try {
|
|
1875
|
+
const dir = await open(dirname(target), "r");
|
|
1876
|
+
try {
|
|
1877
|
+
await dir.sync();
|
|
1878
|
+
} finally {
|
|
1879
|
+
await dir.close();
|
|
1880
|
+
}
|
|
1881
|
+
} catch {
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
async function inspectDocument(path) {
|
|
1885
|
+
let raw;
|
|
1886
|
+
try {
|
|
1887
|
+
await stat(path);
|
|
1888
|
+
} catch {
|
|
1889
|
+
return { status: "missing" };
|
|
1890
|
+
}
|
|
1891
|
+
try {
|
|
1892
|
+
raw = await readFile(path, "utf8");
|
|
1893
|
+
} catch (error) {
|
|
1894
|
+
return { status: "corrupt", detail: `unreadable: ${error instanceof Error ? error.message : String(error)}` };
|
|
1895
|
+
}
|
|
1896
|
+
if (raw.trim().length === 0) return { status: "empty" };
|
|
1897
|
+
let parsed;
|
|
1898
|
+
try {
|
|
1899
|
+
parsed = JSON.parse(raw);
|
|
1900
|
+
} catch (error) {
|
|
1901
|
+
return { status: "corrupt", detail: error instanceof Error ? error.message : "invalid JSON" };
|
|
1902
|
+
}
|
|
1903
|
+
if (Array.isArray(parsed)) return { status: "v1-legacy" };
|
|
1904
|
+
if (parsed === null || typeof parsed !== "object") return { status: "corrupt", detail: "not an object" };
|
|
1905
|
+
const doc = parsed;
|
|
1906
|
+
if (!Array.isArray(doc.items)) return { status: "corrupt", detail: "missing items array" };
|
|
1907
|
+
const version = typeof doc.schemaVersion === "number" ? doc.schemaVersion : 1;
|
|
1908
|
+
if (version > SCHEMA_VERSION) return { status: "future", schemaVersion: version };
|
|
1909
|
+
if (version < SCHEMA_VERSION) return { status: "v1-legacy" };
|
|
1910
|
+
return { status: "current", schemaVersion: version, count: doc.items.length };
|
|
1911
|
+
}
|
|
1912
|
+
async function readDocument(path, onCorrupt, quarantine = true) {
|
|
1913
|
+
const state = await inspectDocument(path);
|
|
1914
|
+
if (state.status === "missing" || state.status === "empty") return [];
|
|
1915
|
+
if (state.status === "future") {
|
|
1916
|
+
const error = new Error(
|
|
1917
|
+
`${path} was written by a newer OmniNode (schema v${state.schemaVersion} > v${SCHEMA_VERSION})`
|
|
1918
|
+
);
|
|
1919
|
+
onCorrupt?.(path, error);
|
|
1920
|
+
return [];
|
|
1921
|
+
}
|
|
1922
|
+
let raw;
|
|
1923
|
+
try {
|
|
1924
|
+
raw = await readFile(path, "utf8");
|
|
1925
|
+
} catch (error) {
|
|
1926
|
+
onCorrupt?.(path, error);
|
|
1927
|
+
return [];
|
|
1928
|
+
}
|
|
1929
|
+
try {
|
|
1930
|
+
const parsed = JSON.parse(raw);
|
|
1931
|
+
if (Array.isArray(parsed)) return parsed;
|
|
1932
|
+
return parsed.items ?? [];
|
|
1933
|
+
} catch (error) {
|
|
1934
|
+
if (quarantine) {
|
|
1935
|
+
const quarantinePath = `${path}.corrupt-${Date.now()}`;
|
|
1936
|
+
try {
|
|
1937
|
+
await rename(path, quarantinePath);
|
|
1938
|
+
logger.child({ component: "persistence" }).warn(
|
|
1939
|
+
`Quarantined unreadable store file ${path} \u2192 ${quarantinePath}`
|
|
1940
|
+
);
|
|
1941
|
+
} catch {
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
onCorrupt?.(path, error);
|
|
1945
|
+
return [];
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1825
1948
|
var JsonFileStore = class {
|
|
1826
1949
|
filePath;
|
|
1827
1950
|
schemaVersion;
|
|
1828
1951
|
onCorrupt;
|
|
1952
|
+
quarantine;
|
|
1829
1953
|
log;
|
|
1830
1954
|
queue = Promise.resolve();
|
|
1831
1955
|
constructor(options) {
|
|
1832
1956
|
this.filePath = `${options.directory ?? `${process.cwd()}/.omninode`}/${options.fileName}`;
|
|
1833
|
-
this.schemaVersion = options.schemaVersion ??
|
|
1957
|
+
this.schemaVersion = options.schemaVersion ?? SCHEMA_VERSION;
|
|
1834
1958
|
this.onCorrupt = options.onCorrupt;
|
|
1959
|
+
this.quarantine = options.quarantine ?? true;
|
|
1835
1960
|
this.log = logger.child({ component: "persistence", file: options.fileName });
|
|
1836
1961
|
}
|
|
1837
1962
|
get path() {
|
|
1838
1963
|
return this.filePath;
|
|
1839
1964
|
}
|
|
1840
|
-
/** All items, or [] when missing/corrupt. */
|
|
1965
|
+
/** All items, or [] when missing/migrated-from-v1/corrupt. */
|
|
1841
1966
|
async readAll() {
|
|
1842
|
-
|
|
1843
|
-
try {
|
|
1844
|
-
raw = await readFile(this.filePath, "utf8");
|
|
1845
|
-
} catch {
|
|
1846
|
-
return [];
|
|
1847
|
-
}
|
|
1848
|
-
try {
|
|
1849
|
-
const parsed = JSON.parse(raw);
|
|
1850
|
-
if (Array.isArray(parsed)) return parsed;
|
|
1851
|
-
if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.items)) {
|
|
1852
|
-
return parsed.items;
|
|
1853
|
-
}
|
|
1854
|
-
throw new Error("persisted file has an unrecognized shape");
|
|
1855
|
-
} catch (error) {
|
|
1856
|
-
this.log.warn(`Treating ${this.filePath} as empty \u2014 it could not be parsed.`);
|
|
1857
|
-
this.onCorrupt?.(this.filePath, error);
|
|
1858
|
-
return [];
|
|
1859
|
-
}
|
|
1967
|
+
return readDocument(this.filePath, this.onCorrupt, this.quarantine);
|
|
1860
1968
|
}
|
|
1861
1969
|
async writeAll(items) {
|
|
1862
1970
|
const document = { schemaVersion: this.schemaVersion, items };
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
await writeFile(tmp, `${JSON.stringify(document, null, 2)}
|
|
1866
|
-
`, "utf8");
|
|
1867
|
-
await rename(tmp, this.filePath);
|
|
1971
|
+
await writeFileAtomic(this.filePath, `${JSON.stringify(document, null, 2)}
|
|
1972
|
+
`);
|
|
1868
1973
|
}
|
|
1869
1974
|
/** Serializes an async operation against this file. */
|
|
1870
1975
|
synchronized(operation) {
|
|
@@ -2167,7 +2272,7 @@ function sleep(ms) {
|
|
|
2167
2272
|
}
|
|
2168
2273
|
|
|
2169
2274
|
// src/audit/index.ts
|
|
2170
|
-
import {
|
|
2275
|
+
import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
|
|
2171
2276
|
import { dirname as dirname2 } from "path";
|
|
2172
2277
|
var FileAuditLog = class {
|
|
2173
2278
|
filePath;
|
|
@@ -2179,8 +2284,15 @@ var FileAuditLog = class {
|
|
|
2179
2284
|
async record(event) {
|
|
2180
2285
|
try {
|
|
2181
2286
|
await mkdir2(dirname2(this.filePath), { recursive: true });
|
|
2182
|
-
await
|
|
2287
|
+
const { open: open2 } = await import("fs/promises");
|
|
2288
|
+
const handle = await open2(this.filePath, "a");
|
|
2289
|
+
try {
|
|
2290
|
+
await handle.appendFile(`${JSON.stringify(event)}
|
|
2183
2291
|
`, "utf8");
|
|
2292
|
+
await handle.sync();
|
|
2293
|
+
} finally {
|
|
2294
|
+
await handle.close();
|
|
2295
|
+
}
|
|
2184
2296
|
} catch (error) {
|
|
2185
2297
|
this.log.warn(
|
|
2186
2298
|
`Audit write failed: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -2189,7 +2301,6 @@ var FileAuditLog = class {
|
|
|
2189
2301
|
}
|
|
2190
2302
|
/** Recent events, oldest first; used by the CLI. */
|
|
2191
2303
|
async recent(limit = 20) {
|
|
2192
|
-
const { readFile: readFile2 } = await import("fs/promises");
|
|
2193
2304
|
let raw;
|
|
2194
2305
|
try {
|
|
2195
2306
|
raw = await readFile2(this.filePath, "utf8");
|
|
@@ -3822,6 +3933,7 @@ function truncate3(text, max) {
|
|
|
3822
3933
|
}
|
|
3823
3934
|
|
|
3824
3935
|
// src/reports/service.ts
|
|
3936
|
+
var MAX_REPORT_BYTES = 256 * 1024;
|
|
3825
3937
|
var ReportService = class {
|
|
3826
3938
|
constructor(store, log = logger) {
|
|
3827
3939
|
this.store = store;
|
|
@@ -3856,6 +3968,16 @@ var ReportService = class {
|
|
|
3856
3968
|
const reports = [];
|
|
3857
3969
|
const rejected = [];
|
|
3858
3970
|
for (const candidate of candidates) {
|
|
3971
|
+
const size = Buffer.byteLength(JSON.stringify(candidate), "utf8");
|
|
3972
|
+
if (size > MAX_REPORT_BYTES) {
|
|
3973
|
+
rejected.push({
|
|
3974
|
+
key: candidate?.id ?? "(unknown)",
|
|
3975
|
+
reason: `report exceeds the ${MAX_REPORT_BYTES} byte limit (${size} bytes)`,
|
|
3976
|
+
issues: []
|
|
3977
|
+
});
|
|
3978
|
+
this.log.warn(`Rejected oversized report (${size} bytes).`);
|
|
3979
|
+
continue;
|
|
3980
|
+
}
|
|
3859
3981
|
const result = validateReport(candidate);
|
|
3860
3982
|
if (result.valid) {
|
|
3861
3983
|
reports.push(result.report);
|
|
@@ -3976,9 +4098,59 @@ var ProjectStores = class {
|
|
|
3976
4098
|
this.audit = new FileAuditLog(directory);
|
|
3977
4099
|
}
|
|
3978
4100
|
};
|
|
4101
|
+
var STORE_FILES = [
|
|
4102
|
+
"tasks.json",
|
|
4103
|
+
"pipelines.json",
|
|
4104
|
+
"reports.json",
|
|
4105
|
+
"plans.json",
|
|
4106
|
+
"memory.json"
|
|
4107
|
+
];
|
|
4108
|
+
async function migrateProjectStores(directory = `${process.cwd()}/.omninode`, options = {}) {
|
|
4109
|
+
const reports = [];
|
|
4110
|
+
for (const file of STORE_FILES) {
|
|
4111
|
+
const path = `${directory}/${file}`;
|
|
4112
|
+
const state = await inspectDocument(path);
|
|
4113
|
+
if (state.status === "missing") {
|
|
4114
|
+
reports.push({ file, state: state.status, migrated: false });
|
|
4115
|
+
continue;
|
|
4116
|
+
}
|
|
4117
|
+
if (state.status === "current") {
|
|
4118
|
+
reports.push({ file, state: state.status, before: state.count, after: state.count, migrated: false });
|
|
4119
|
+
continue;
|
|
4120
|
+
}
|
|
4121
|
+
if (state.status === "future") {
|
|
4122
|
+
reports.push({
|
|
4123
|
+
file,
|
|
4124
|
+
state: state.status,
|
|
4125
|
+
migrated: false,
|
|
4126
|
+
note: `written by a newer OmniNode (schema v${state.schemaVersion}); not touched`
|
|
4127
|
+
});
|
|
4128
|
+
continue;
|
|
4129
|
+
}
|
|
4130
|
+
if (state.status === "corrupt") {
|
|
4131
|
+
reports.push({ file, state: state.status, migrated: false, note: state.detail });
|
|
4132
|
+
continue;
|
|
4133
|
+
}
|
|
4134
|
+
const items = await readDocument(path, void 0, false);
|
|
4135
|
+
if (!options.checkOnly) {
|
|
4136
|
+
await writeFileAtomic(path, `${JSON.stringify({ schemaVersion: SCHEMA_VERSION, items }, null, 2)}
|
|
4137
|
+
`);
|
|
4138
|
+
}
|
|
4139
|
+
const beforeCount = items.length;
|
|
4140
|
+
reports.push({
|
|
4141
|
+
file,
|
|
4142
|
+
state: state.status,
|
|
4143
|
+
before: beforeCount,
|
|
4144
|
+
after: items.length,
|
|
4145
|
+
migrated: !options.checkOnly,
|
|
4146
|
+
...options.checkOnly ? { note: "would migrate" } : {}
|
|
4147
|
+
});
|
|
4148
|
+
}
|
|
4149
|
+
return reports;
|
|
4150
|
+
}
|
|
3979
4151
|
|
|
3980
4152
|
// src/version.ts
|
|
3981
|
-
var OMNINODE_VERSION = "2.0.0-alpha.
|
|
4153
|
+
var OMNINODE_VERSION = "2.0.0-alpha.13";
|
|
3982
4154
|
export {
|
|
3983
4155
|
AgentAdapterRegistry,
|
|
3984
4156
|
AgentError,
|
|
@@ -3997,6 +4169,7 @@ export {
|
|
|
3997
4169
|
Logger,
|
|
3998
4170
|
MAX_CONTEXT_CHARS,
|
|
3999
4171
|
MAX_MESSAGE_BYTES,
|
|
4172
|
+
MAX_REPORT_BYTES,
|
|
4000
4173
|
MemoryError,
|
|
4001
4174
|
MemoryService,
|
|
4002
4175
|
ModelPlanner,
|
|
@@ -4020,6 +4193,8 @@ export {
|
|
|
4020
4193
|
RememberaMemoryProvider,
|
|
4021
4194
|
ReportService,
|
|
4022
4195
|
RoleRegistry,
|
|
4196
|
+
SCHEMA_VERSION,
|
|
4197
|
+
STORE_FILES,
|
|
4023
4198
|
SUPPORTED_PROTOCOL_VERSIONS,
|
|
4024
4199
|
TERMINAL_TASK_STATUSES,
|
|
4025
4200
|
TaskEngine,
|
|
@@ -4048,6 +4223,7 @@ export {
|
|
|
4048
4223
|
defaultProjectConfigYaml,
|
|
4049
4224
|
defaultSearch,
|
|
4050
4225
|
describeExit,
|
|
4226
|
+
describeSecretFindings,
|
|
4051
4227
|
detectConflicts,
|
|
4052
4228
|
encodeMessage,
|
|
4053
4229
|
envelope,
|
|
@@ -4055,6 +4231,7 @@ export {
|
|
|
4055
4231
|
extractMessage,
|
|
4056
4232
|
extractReportFromText,
|
|
4057
4233
|
findConfigFile,
|
|
4234
|
+
inspectDocument,
|
|
4058
4235
|
isNetworkError,
|
|
4059
4236
|
isOmniNodeError,
|
|
4060
4237
|
isRetryable,
|
|
@@ -4065,6 +4242,7 @@ export {
|
|
|
4065
4242
|
kindFromStatus,
|
|
4066
4243
|
loadConfig,
|
|
4067
4244
|
logger,
|
|
4245
|
+
migrateProjectStores,
|
|
4068
4246
|
newMessageId,
|
|
4069
4247
|
normalizeEvidence,
|
|
4070
4248
|
normalizeFinding,
|
|
@@ -4079,6 +4257,7 @@ export {
|
|
|
4079
4257
|
providerConfigSchema,
|
|
4080
4258
|
providerHttpError,
|
|
4081
4259
|
providerTransportError,
|
|
4260
|
+
readDocument,
|
|
4082
4261
|
registryKey,
|
|
4083
4262
|
requestJson,
|
|
4084
4263
|
requireCompletion,
|
|
@@ -4086,10 +4265,12 @@ export {
|
|
|
4086
4265
|
resolveEnvPolicy,
|
|
4087
4266
|
roleConfigSchema,
|
|
4088
4267
|
sanitizeChunk,
|
|
4268
|
+
scanForInlineSecrets,
|
|
4089
4269
|
similar,
|
|
4090
4270
|
validateEnvelope,
|
|
4091
4271
|
validatePlan,
|
|
4092
4272
|
validateReport,
|
|
4093
|
-
validateWorkingDirectory
|
|
4273
|
+
validateWorkingDirectory,
|
|
4274
|
+
writeFileAtomic
|
|
4094
4275
|
};
|
|
4095
4276
|
//# sourceMappingURL=index.js.map
|