atabey 0.1.1-beta → 0.1.3
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 +145 -79
- package/dist/atabey/src/cli/commands/coverage.js +17 -12
- package/dist/atabey/src/cli/commands/coverage.js.map +1 -1
- package/dist/atabey/src/cli/commands/lint.d.ts +2 -1
- package/dist/atabey/src/cli/commands/lint.js +20 -1
- package/dist/atabey/src/cli/commands/lint.js.map +1 -1
- package/dist/atabey/src/cli/commands/mcp.js +56 -23
- package/dist/atabey/src/cli/commands/mcp.js.map +1 -1
- package/dist/atabey/src/cli/commands/orchestrate.d.ts +6 -6
- package/dist/atabey/src/cli/index.js +1 -1
- package/dist/shared/src/agent-identity.d.ts +1 -34
- package/dist/shared/src/agent-identity.js +1 -76
- package/dist/shared/src/agent-identity.js.map +1 -1
- package/dist/shared/src/api-catalog.d.ts +1 -57
- package/dist/shared/src/api-catalog.js +1 -253
- package/dist/shared/src/api-catalog.js.map +1 -1
- package/dist/shared/src/audit-chain.d.ts +1 -80
- package/dist/shared/src/audit-chain.js +1 -261
- package/dist/shared/src/audit-chain.js.map +1 -1
- package/dist/shared/src/audit.d.ts +1 -121
- package/dist/shared/src/audit.js +1 -321
- package/dist/shared/src/audit.js.map +1 -1
- package/dist/shared/src/constants.d.ts +1 -150
- package/dist/shared/src/constants.js +1 -211
- package/dist/shared/src/constants.js.map +1 -1
- package/dist/shared/src/database.d.ts +1 -6
- package/dist/shared/src/database.js +1 -15
- package/dist/shared/src/database.js.map +1 -1
- package/dist/shared/src/errors.d.ts +1 -46
- package/dist/shared/src/errors.js +1 -68
- package/dist/shared/src/errors.js.map +1 -1
- package/dist/shared/src/file-lock.d.ts +1 -93
- package/dist/shared/src/file-lock.js +1 -239
- package/dist/shared/src/file-lock.js.map +1 -1
- package/dist/shared/src/fs.d.ts +1 -9
- package/dist/shared/src/fs.js +1 -53
- package/dist/shared/src/fs.js.map +1 -1
- package/dist/shared/src/index.d.ts +2 -31
- package/dist/shared/src/index.js +2 -31
- package/dist/shared/src/index.js.map +1 -1
- package/dist/shared/src/job-queue.d.ts +1 -76
- package/dist/shared/src/job-queue.js +1 -199
- package/dist/shared/src/job-queue.js.map +1 -1
- package/dist/shared/src/lock.d.ts +1 -82
- package/dist/shared/src/lock.js +1 -158
- package/dist/shared/src/lock.js.map +1 -1
- package/dist/shared/src/logger.d.ts +1 -31
- package/dist/shared/src/logger.js +1 -152
- package/dist/shared/src/logger.js.map +1 -1
- package/dist/shared/src/memory-prune.d.ts +1 -18
- package/dist/shared/src/memory-prune.js +1 -47
- package/dist/shared/src/memory-prune.js.map +1 -1
- package/dist/shared/src/migrations.d.ts +1 -69
- package/dist/shared/src/migrations.js +1 -393
- package/dist/shared/src/migrations.js.map +1 -1
- package/dist/shared/src/modules/agent/agent-identity.d.ts +34 -0
- package/dist/shared/src/modules/agent/agent-identity.js +77 -0
- package/dist/shared/src/modules/agent/agent-identity.js.map +1 -0
- package/dist/shared/src/modules/agent/api-catalog.d.ts +57 -0
- package/dist/shared/src/modules/agent/api-catalog.js +254 -0
- package/dist/shared/src/modules/agent/api-catalog.js.map +1 -0
- package/dist/shared/src/modules/agent/index.d.ts +3 -0
- package/dist/shared/src/modules/agent/index.js +4 -0
- package/dist/shared/src/modules/agent/index.js.map +1 -0
- package/dist/shared/src/modules/agent/tokenizer.d.ts +54 -0
- package/dist/shared/src/modules/agent/tokenizer.js +272 -0
- package/dist/shared/src/modules/agent/tokenizer.js.map +1 -0
- package/dist/shared/src/modules/concurrency/file-lock.d.ts +93 -0
- package/dist/shared/src/modules/concurrency/file-lock.js +240 -0
- package/dist/shared/src/modules/concurrency/file-lock.js.map +1 -0
- package/dist/shared/src/modules/concurrency/index.d.ts +6 -0
- package/dist/shared/src/modules/concurrency/index.js +7 -0
- package/dist/shared/src/modules/concurrency/index.js.map +1 -0
- package/dist/shared/src/modules/concurrency/job-queue.d.ts +76 -0
- package/dist/shared/src/modules/concurrency/job-queue.js +200 -0
- package/dist/shared/src/modules/concurrency/job-queue.js.map +1 -0
- package/dist/shared/src/modules/concurrency/lock.d.ts +82 -0
- package/dist/shared/src/modules/concurrency/lock.js +159 -0
- package/dist/shared/src/modules/concurrency/lock.js.map +1 -0
- package/dist/shared/src/modules/concurrency/resilience.d.ts +84 -0
- package/dist/shared/src/modules/concurrency/resilience.js +217 -0
- package/dist/shared/src/modules/concurrency/resilience.js.map +1 -0
- package/dist/shared/src/modules/concurrency/worker-pool.d.ts +52 -0
- package/dist/shared/src/modules/concurrency/worker-pool.js +229 -0
- package/dist/shared/src/modules/concurrency/worker-pool.js.map +1 -0
- package/dist/shared/src/modules/concurrency/worker-thread.d.ts +21 -0
- package/dist/shared/src/modules/concurrency/worker-thread.js +67 -0
- package/dist/shared/src/modules/concurrency/worker-thread.js.map +1 -0
- package/dist/shared/src/modules/index.d.ts +14 -0
- package/dist/shared/src/modules/index.js +15 -0
- package/dist/shared/src/modules/index.js.map +1 -0
- package/dist/shared/src/modules/observability/audit-chain.d.ts +80 -0
- package/dist/shared/src/modules/observability/audit-chain.js +262 -0
- package/dist/shared/src/modules/observability/audit-chain.js.map +1 -0
- package/dist/shared/src/modules/observability/audit.d.ts +121 -0
- package/dist/shared/src/modules/observability/audit.js +322 -0
- package/dist/shared/src/modules/observability/audit.js.map +1 -0
- package/dist/shared/src/modules/observability/index.d.ts +4 -0
- package/dist/shared/src/modules/observability/index.js +5 -0
- package/dist/shared/src/modules/observability/index.js.map +1 -0
- package/dist/shared/src/modules/observability/logger.d.ts +31 -0
- package/dist/shared/src/modules/observability/logger.js +153 -0
- package/dist/shared/src/modules/observability/logger.js.map +1 -0
- package/dist/shared/src/modules/observability/otel.d.ts +39 -0
- package/dist/shared/src/modules/observability/otel.js +93 -0
- package/dist/shared/src/modules/observability/otel.js.map +1 -0
- package/dist/shared/src/modules/primitives/constants.d.ts +150 -0
- package/dist/shared/src/modules/primitives/constants.js +212 -0
- package/dist/shared/src/modules/primitives/constants.js.map +1 -0
- package/dist/shared/src/modules/primitives/errors.d.ts +46 -0
- package/dist/shared/src/modules/primitives/errors.js +69 -0
- package/dist/shared/src/modules/primitives/errors.js.map +1 -0
- package/dist/shared/src/modules/primitives/fs.d.ts +9 -0
- package/dist/shared/src/modules/primitives/fs.js +54 -0
- package/dist/shared/src/modules/primitives/fs.js.map +1 -0
- package/dist/shared/src/modules/primitives/index.d.ts +7 -0
- package/dist/shared/src/modules/primitives/index.js +8 -0
- package/dist/shared/src/modules/primitives/index.js.map +1 -0
- package/dist/shared/src/modules/primitives/outbound-http.d.ts +32 -0
- package/dist/shared/src/modules/primitives/outbound-http.js +88 -0
- package/dist/shared/src/modules/primitives/outbound-http.js.map +1 -0
- package/dist/shared/src/modules/primitives/string.d.ts +1 -0
- package/dist/shared/src/modules/primitives/string.js +30 -0
- package/dist/shared/src/modules/primitives/string.js.map +1 -0
- package/dist/shared/src/modules/primitives/time.d.ts +5 -0
- package/dist/shared/src/modules/primitives/time.js +35 -0
- package/dist/shared/src/modules/primitives/time.js.map +1 -0
- package/dist/shared/src/modules/primitives/types.d.ts +27 -0
- package/dist/shared/src/modules/primitives/types.js +17 -0
- package/dist/shared/src/modules/primitives/types.js.map +1 -0
- package/dist/shared/src/modules/sandbox/index.d.ts +3 -0
- package/dist/shared/src/modules/sandbox/index.js +4 -0
- package/dist/shared/src/modules/sandbox/index.js.map +1 -0
- package/dist/shared/src/modules/sandbox/sandbox-fs.d.ts +28 -0
- package/dist/shared/src/modules/sandbox/sandbox-fs.js +143 -0
- package/dist/shared/src/modules/sandbox/sandbox-fs.js.map +1 -0
- package/dist/shared/src/modules/sandbox/sandbox-runtime.d.ts +78 -0
- package/dist/shared/src/modules/sandbox/sandbox-runtime.js +282 -0
- package/dist/shared/src/modules/sandbox/sandbox-runtime.js.map +1 -0
- package/dist/shared/src/modules/sandbox/sandbox.d.ts +54 -0
- package/dist/shared/src/modules/sandbox/sandbox.js +179 -0
- package/dist/shared/src/modules/sandbox/sandbox.js.map +1 -0
- package/dist/shared/src/modules/security/index.d.ts +5 -0
- package/dist/shared/src/modules/security/index.js +6 -0
- package/dist/shared/src/modules/security/index.js.map +1 -0
- package/dist/shared/src/modules/security/pii.d.ts +70 -0
- package/dist/shared/src/modules/security/pii.js +276 -0
- package/dist/shared/src/modules/security/pii.js.map +1 -0
- package/dist/shared/src/modules/security/policy-gate.d.ts +42 -0
- package/dist/shared/src/modules/security/policy-gate.js +172 -0
- package/dist/shared/src/modules/security/policy-gate.js.map +1 -0
- package/dist/shared/src/modules/security/retention.d.ts +68 -0
- package/dist/shared/src/modules/security/retention.js +218 -0
- package/dist/shared/src/modules/security/retention.js.map +1 -0
- package/dist/shared/src/modules/security/secrets.d.ts +99 -0
- package/dist/shared/src/modules/security/secrets.js +371 -0
- package/dist/shared/src/modules/security/secrets.js.map +1 -0
- package/dist/shared/src/modules/security/tls.d.ts +87 -0
- package/dist/shared/src/modules/security/tls.js +154 -0
- package/dist/shared/src/modules/security/tls.js.map +1 -0
- package/dist/shared/src/modules/storage/database.d.ts +6 -0
- package/dist/shared/src/modules/storage/database.js +16 -0
- package/dist/shared/src/modules/storage/database.js.map +1 -0
- package/dist/shared/src/modules/storage/index.d.ts +4 -0
- package/dist/shared/src/modules/storage/index.js +5 -0
- package/dist/shared/src/modules/storage/index.js.map +1 -0
- package/dist/shared/src/modules/storage/memory-prune.d.ts +18 -0
- package/dist/shared/src/modules/storage/memory-prune.js +48 -0
- package/dist/shared/src/modules/storage/memory-prune.js.map +1 -0
- package/dist/shared/src/modules/storage/migrations.d.ts +69 -0
- package/dist/shared/src/modules/storage/migrations.js +394 -0
- package/dist/shared/src/modules/storage/migrations.js.map +1 -0
- package/dist/shared/src/modules/storage/storage.d.ts +341 -0
- package/dist/shared/src/modules/storage/storage.js +1004 -0
- package/dist/shared/src/modules/storage/storage.js.map +1 -0
- package/dist/shared/src/otel.d.ts +1 -39
- package/dist/shared/src/otel.js +1 -92
- package/dist/shared/src/otel.js.map +1 -1
- package/dist/shared/src/outbound-http.d.ts +1 -32
- package/dist/shared/src/outbound-http.js +1 -87
- package/dist/shared/src/outbound-http.js.map +1 -1
- package/dist/shared/src/pii.d.ts +1 -70
- package/dist/shared/src/pii.js +1 -275
- package/dist/shared/src/pii.js.map +1 -1
- package/dist/shared/src/policy-gate.d.ts +1 -42
- package/dist/shared/src/policy-gate.js +1 -171
- package/dist/shared/src/policy-gate.js.map +1 -1
- package/dist/shared/src/resilience.d.ts +1 -84
- package/dist/shared/src/resilience.js +1 -216
- package/dist/shared/src/resilience.js.map +1 -1
- package/dist/shared/src/retention.d.ts +1 -68
- package/dist/shared/src/retention.js +1 -217
- package/dist/shared/src/retention.js.map +1 -1
- package/dist/shared/src/sandbox-fs.d.ts +1 -28
- package/dist/shared/src/sandbox-fs.js +1 -142
- package/dist/shared/src/sandbox-fs.js.map +1 -1
- package/dist/shared/src/sandbox-runtime.d.ts +1 -78
- package/dist/shared/src/sandbox-runtime.js +1 -281
- package/dist/shared/src/sandbox-runtime.js.map +1 -1
- package/dist/shared/src/sandbox.d.ts +1 -54
- package/dist/shared/src/sandbox.js +1 -178
- package/dist/shared/src/sandbox.js.map +1 -1
- package/dist/shared/src/secrets.d.ts +1 -99
- package/dist/shared/src/secrets.js +1 -370
- package/dist/shared/src/secrets.js.map +1 -1
- package/dist/shared/src/storage.d.ts +1 -341
- package/dist/shared/src/storage.js +1 -1003
- package/dist/shared/src/storage.js.map +1 -1
- package/dist/shared/src/string.d.ts +1 -1
- package/dist/shared/src/string.js +1 -29
- package/dist/shared/src/string.js.map +1 -1
- package/dist/shared/src/time.d.ts +1 -5
- package/dist/shared/src/time.js +1 -34
- package/dist/shared/src/time.js.map +1 -1
- package/dist/shared/src/tls.d.ts +1 -87
- package/dist/shared/src/tls.js +1 -153
- package/dist/shared/src/tls.js.map +1 -1
- package/dist/shared/src/tokenizer.d.ts +1 -54
- package/dist/shared/src/tokenizer.js +1 -271
- package/dist/shared/src/tokenizer.js.map +1 -1
- package/dist/shared/src/types.d.ts +1 -27
- package/dist/shared/src/types.js +1 -16
- package/dist/shared/src/types.js.map +1 -1
- package/dist/shared/src/worker-pool.d.ts +1 -52
- package/dist/shared/src/worker-pool.js +1 -228
- package/dist/shared/src/worker-pool.js.map +1 -1
- package/dist/shared/src/worker-thread.d.ts +1 -21
- package/dist/shared/src/worker-thread.js +1 -66
- package/dist/shared/src/worker-thread.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Specialty memory pruning — deduplication + cap + summary.
|
|
3
|
-
*
|
|
4
|
-
* `updateSpecialtyMemory` appends raw markdown lessons forever, which bloats
|
|
5
|
-
* prompts and accumulates contradictions. This pure function deduplicates
|
|
6
|
-
* identical lessons (ignoring the timestamp) and caps the retained count,
|
|
7
|
-
* leaving a summary marker for pruned entries.
|
|
8
|
-
*/
|
|
9
|
-
export interface PruneResult {
|
|
10
|
-
content: string;
|
|
11
|
-
removed: number;
|
|
12
|
-
kept: number;
|
|
13
|
-
}
|
|
14
|
-
export interface PruneOptions {
|
|
15
|
-
/** Maximum number of lessons retained (newest kept). Default: 100. */
|
|
16
|
-
maxLessons?: number;
|
|
17
|
-
}
|
|
18
|
-
export declare function pruneSpecialtyMarkdown(content: string, options?: PruneOptions): PruneResult;
|
|
1
|
+
export * from "./modules/storage/memory-prune.js";
|
|
@@ -1,48 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
* Specialty memory pruning — deduplication + cap + summary.
|
|
3
|
-
*
|
|
4
|
-
* `updateSpecialtyMemory` appends raw markdown lessons forever, which bloats
|
|
5
|
-
* prompts and accumulates contradictions. This pure function deduplicates
|
|
6
|
-
* identical lessons (ignoring the timestamp) and caps the retained count,
|
|
7
|
-
* leaving a summary marker for pruned entries.
|
|
8
|
-
*/
|
|
9
|
-
const LESSON_PREFIX = /^\s*-\s+\*\*\[/;
|
|
10
|
-
const TIMESTAMP = /\[[^\]]*\]/g;
|
|
11
|
-
export function pruneSpecialtyMarkdown(content, options = {}) {
|
|
12
|
-
const maxLessons = options.maxLessons ?? 100;
|
|
13
|
-
if (!content)
|
|
14
|
-
return { content: content ?? "", removed: 0, kept: 0 };
|
|
15
|
-
const lines = content.split(/\r?\n/);
|
|
16
|
-
const header = [];
|
|
17
|
-
const lessons = [];
|
|
18
|
-
const seen = new Set();
|
|
19
|
-
let removed = 0;
|
|
20
|
-
for (const line of lines) {
|
|
21
|
-
if (LESSON_PREFIX.test(line)) {
|
|
22
|
-
const key = line.replace(TIMESTAMP, "").trim().toLowerCase();
|
|
23
|
-
if (seen.has(key)) {
|
|
24
|
-
removed++;
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
seen.add(key);
|
|
28
|
-
lessons.push(line);
|
|
29
|
-
}
|
|
30
|
-
else if (line.trim() === "" && lessons.length > 0) {
|
|
31
|
-
// collapse blank lines between lessons
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
header.push(line);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
let kept = lessons;
|
|
39
|
-
if (lessons.length > maxLessons) {
|
|
40
|
-
const dropped = lessons.length - maxLessons;
|
|
41
|
-
removed += dropped;
|
|
42
|
-
kept = lessons.slice(-maxLessons);
|
|
43
|
-
header.push(`- _[memory-prune] ${dropped} older lesson(s) pruned to control prompt size._`);
|
|
44
|
-
}
|
|
45
|
-
const body = [...header, ...kept].join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
46
|
-
return { content: body ? `${body}\n` : "", removed, kept: kept.length };
|
|
47
|
-
}
|
|
1
|
+
export * from "./modules/storage/memory-prune.js";
|
|
48
2
|
//# sourceMappingURL=memory-prune.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-prune.js","sourceRoot":"","sources":["../../../../shared/src/memory-prune.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"memory-prune.js","sourceRoot":"","sources":["../../../../shared/src/memory-prune.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC"}
|
|
@@ -1,69 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* SQLite Schema Migration Runner, Startup Integrity Check & Online Backup/Restore (Phase 1.5)
|
|
3
|
-
*
|
|
4
|
-
* Provides:
|
|
5
|
-
* 1. Versioned, checksummed schema migrations (`schema_migrations` table).
|
|
6
|
-
* 2. Startup integrity verification (`PRAGMA quick_check` / `PRAGMA integrity_check`).
|
|
7
|
-
* 3. Atomic online backup (`VACUUM INTO` + `.sha256` manifest) and verified restore.
|
|
8
|
-
*/
|
|
9
|
-
import Database from "better-sqlite3";
|
|
10
|
-
export interface SchemaMigration {
|
|
11
|
-
version: number;
|
|
12
|
-
name: string;
|
|
13
|
-
sql: string;
|
|
14
|
-
}
|
|
15
|
-
export interface AppliedMigration {
|
|
16
|
-
version: number;
|
|
17
|
-
name: string;
|
|
18
|
-
checksum: string;
|
|
19
|
-
applied_at: string;
|
|
20
|
-
}
|
|
21
|
-
export interface IntegrityCheckResult {
|
|
22
|
-
ok: boolean;
|
|
23
|
-
messages: string[];
|
|
24
|
-
foreignKeyViolations: number;
|
|
25
|
-
}
|
|
26
|
-
export interface BackupResult {
|
|
27
|
-
backupPath: string;
|
|
28
|
-
sha256: string;
|
|
29
|
-
sizeBytes: number;
|
|
30
|
-
createdAt: string;
|
|
31
|
-
schemaVersion: number;
|
|
32
|
-
}
|
|
33
|
-
export declare const BUILTIN_MIGRATIONS: SchemaMigration[];
|
|
34
|
-
/**
|
|
35
|
-
* Run all pending schema migrations transactionally and return applied version numbers.
|
|
36
|
-
*/
|
|
37
|
-
export declare function runPendingMigrations(db: Database.Database, migrations?: SchemaMigration[]): {
|
|
38
|
-
applied: number[];
|
|
39
|
-
currentVersion: number;
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* Return applied and pending migrations for diagnostics/CLI.
|
|
43
|
-
*/
|
|
44
|
-
export declare function getMigrationStatus(db: Database.Database, migrations?: SchemaMigration[]): {
|
|
45
|
-
currentVersion: number;
|
|
46
|
-
applied: AppliedMigration[];
|
|
47
|
-
pending: Array<{
|
|
48
|
-
version: number;
|
|
49
|
-
name: string;
|
|
50
|
-
}>;
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* Run SQLite integrity_check / quick_check and foreign_key_check.
|
|
54
|
-
*/
|
|
55
|
-
export declare function verifyDatabaseIntegrity(db: Database.Database, mode?: "quick" | "full"): IntegrityCheckResult;
|
|
56
|
-
/**
|
|
57
|
-
* Perform a consistent online backup using SQLite `VACUUM INTO` and write a `.sha256` sidecar.
|
|
58
|
-
*/
|
|
59
|
-
export declare function backupDatabase(db: Database.Database, destinationPath: string): BackupResult;
|
|
60
|
-
/**
|
|
61
|
-
* Verify a backup file (checksum sidecar + SQLite integrity_check) and atomically restore it.
|
|
62
|
-
*/
|
|
63
|
-
export declare function restoreDatabase(backupPath: string, targetDbPath: string, options?: {
|
|
64
|
-
verifyChecksum?: boolean;
|
|
65
|
-
}): {
|
|
66
|
-
restoredPath: string;
|
|
67
|
-
sha256: string;
|
|
68
|
-
schemaVersion: number;
|
|
69
|
-
};
|
|
1
|
+
export * from "./modules/storage/migrations.js";
|
|
@@ -1,394 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
* SQLite Schema Migration Runner, Startup Integrity Check & Online Backup/Restore (Phase 1.5)
|
|
3
|
-
*
|
|
4
|
-
* Provides:
|
|
5
|
-
* 1. Versioned, checksummed schema migrations (`schema_migrations` table).
|
|
6
|
-
* 2. Startup integrity verification (`PRAGMA quick_check` / `PRAGMA integrity_check`).
|
|
7
|
-
* 3. Atomic online backup (`VACUUM INTO` + `.sha256` manifest) and verified restore.
|
|
8
|
-
*/
|
|
9
|
-
import Database from "better-sqlite3";
|
|
10
|
-
import crypto from "crypto";
|
|
11
|
-
import fs from "fs";
|
|
12
|
-
import path from "path";
|
|
13
|
-
export const BUILTIN_MIGRATIONS = [
|
|
14
|
-
{
|
|
15
|
-
version: 1,
|
|
16
|
-
name: "001_initial_core_tables",
|
|
17
|
-
sql: `
|
|
18
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
19
|
-
name TEXT PRIMARY KEY,
|
|
20
|
-
state TEXT DEFAULT 'READY',
|
|
21
|
-
task TEXT DEFAULT 'Idle',
|
|
22
|
-
description TEXT DEFAULT '',
|
|
23
|
-
skills TEXT DEFAULT '[]',
|
|
24
|
-
last_updated DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
25
|
-
);
|
|
26
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
27
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
28
|
-
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
29
|
-
sender TEXT,
|
|
30
|
-
receiver TEXT,
|
|
31
|
-
category TEXT,
|
|
32
|
-
content TEXT,
|
|
33
|
-
trace_id TEXT,
|
|
34
|
-
parent_id TEXT,
|
|
35
|
-
status TEXT DEFAULT 'PENDING',
|
|
36
|
-
priority TEXT DEFAULT 'NORMAL',
|
|
37
|
-
requires_approval BOOLEAN DEFAULT 0
|
|
38
|
-
);
|
|
39
|
-
CREATE TABLE IF NOT EXISTS tasks (
|
|
40
|
-
id TEXT PRIMARY KEY,
|
|
41
|
-
trace_id TEXT,
|
|
42
|
-
description TEXT,
|
|
43
|
-
agent TEXT,
|
|
44
|
-
status TEXT DEFAULT 'PENDING',
|
|
45
|
-
priority TEXT DEFAULT 'NORMAL',
|
|
46
|
-
dependencies TEXT,
|
|
47
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
48
|
-
);
|
|
49
|
-
CREATE TABLE IF NOT EXISTS logs (
|
|
50
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
51
|
-
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
52
|
-
agent TEXT,
|
|
53
|
-
action TEXT,
|
|
54
|
-
trace_id TEXT,
|
|
55
|
-
status TEXT,
|
|
56
|
-
summary TEXT,
|
|
57
|
-
findings TEXT,
|
|
58
|
-
prev_hash TEXT,
|
|
59
|
-
hash TEXT
|
|
60
|
-
);
|
|
61
|
-
CREATE TABLE IF NOT EXISTS metadata (
|
|
62
|
-
key TEXT PRIMARY KEY,
|
|
63
|
-
value TEXT
|
|
64
|
-
);
|
|
65
|
-
CREATE TABLE IF NOT EXISTS costs (
|
|
66
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
-
provider TEXT,
|
|
68
|
-
model TEXT,
|
|
69
|
-
input_tokens INTEGER,
|
|
70
|
-
output_tokens INTEGER,
|
|
71
|
-
cost REAL,
|
|
72
|
-
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
73
|
-
trace_id TEXT
|
|
74
|
-
);
|
|
75
|
-
CREATE TABLE IF NOT EXISTS knowledge (
|
|
76
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
77
|
-
filename TEXT UNIQUE,
|
|
78
|
-
content TEXT,
|
|
79
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
80
|
-
);
|
|
81
|
-
CREATE TABLE IF NOT EXISTS users (
|
|
82
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
83
|
-
name TEXT UNIQUE,
|
|
84
|
-
token TEXT UNIQUE,
|
|
85
|
-
role TEXT DEFAULT 'user',
|
|
86
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
87
|
-
);
|
|
88
|
-
`,
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
version: 2,
|
|
92
|
-
name: "002_governance_approvals_discipline_cooldowns",
|
|
93
|
-
sql: `
|
|
94
|
-
CREATE TABLE IF NOT EXISTS loop_cooldowns (
|
|
95
|
-
agent TEXT PRIMARY KEY,
|
|
96
|
-
cooldown_until INTEGER NOT NULL,
|
|
97
|
-
detail TEXT,
|
|
98
|
-
cooldown_count INTEGER DEFAULT 1,
|
|
99
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
100
|
-
);
|
|
101
|
-
CREATE TABLE IF NOT EXISTS approvals (
|
|
102
|
-
trace_id TEXT PRIMARY KEY,
|
|
103
|
-
tool_name TEXT NOT NULL,
|
|
104
|
-
agent TEXT NOT NULL,
|
|
105
|
-
risk_score INTEGER NOT NULL,
|
|
106
|
-
risk_level TEXT NOT NULL,
|
|
107
|
-
reason TEXT NOT NULL,
|
|
108
|
-
args TEXT NOT NULL,
|
|
109
|
-
created_at INTEGER NOT NULL,
|
|
110
|
-
expires_at INTEGER NOT NULL,
|
|
111
|
-
status TEXT DEFAULT 'PENDING',
|
|
112
|
-
passcode TEXT
|
|
113
|
-
);
|
|
114
|
-
CREATE TABLE IF NOT EXISTS discipline (
|
|
115
|
-
agent TEXT PRIMARY KEY,
|
|
116
|
-
total_calls INTEGER DEFAULT 0,
|
|
117
|
-
violations INTEGER DEFAULT 0,
|
|
118
|
-
last_violation TEXT,
|
|
119
|
-
cooldown_until INTEGER DEFAULT 0,
|
|
120
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
121
|
-
);
|
|
122
|
-
`,
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
version: 3,
|
|
126
|
-
name: "003_performance_indexes",
|
|
127
|
-
sql: `
|
|
128
|
-
CREATE INDEX IF NOT EXISTS idx_messages_receiver_status ON messages(receiver, status);
|
|
129
|
-
CREATE INDEX IF NOT EXISTS idx_messages_trace ON messages(trace_id);
|
|
130
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_trace ON tasks(trace_id);
|
|
131
|
-
CREATE INDEX IF NOT EXISTS idx_logs_trace ON logs(trace_id);
|
|
132
|
-
CREATE INDEX IF NOT EXISTS idx_costs_timestamp ON costs(timestamp);
|
|
133
|
-
CREATE INDEX IF NOT EXISTS idx_costs_trace ON costs(trace_id);
|
|
134
|
-
`,
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
version: 4,
|
|
138
|
-
name: "004_api_catalog",
|
|
139
|
-
sql: `
|
|
140
|
-
CREATE TABLE IF NOT EXISTS api_endpoints (
|
|
141
|
-
method TEXT NOT NULL,
|
|
142
|
-
path TEXT NOT NULL,
|
|
143
|
-
tag TEXT,
|
|
144
|
-
summary TEXT,
|
|
145
|
-
auth_required INTEGER DEFAULT 0,
|
|
146
|
-
request_schema TEXT,
|
|
147
|
-
response_schema TEXT,
|
|
148
|
-
source TEXT,
|
|
149
|
-
updated_at TEXT NOT NULL,
|
|
150
|
-
PRIMARY KEY (method, path)
|
|
151
|
-
);
|
|
152
|
-
CREATE INDEX IF NOT EXISTS idx_api_endpoints_tag ON api_endpoints(tag);
|
|
153
|
-
`,
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
version: 5,
|
|
157
|
-
name: "005_discipline_call_history",
|
|
158
|
-
sql: `
|
|
159
|
-
CREATE TABLE IF NOT EXISTS discipline_calls (
|
|
160
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
161
|
-
agent TEXT NOT NULL,
|
|
162
|
-
tool TEXT NOT NULL,
|
|
163
|
-
ts INTEGER NOT NULL,
|
|
164
|
-
args_size INTEGER DEFAULT 0
|
|
165
|
-
);
|
|
166
|
-
CREATE INDEX IF NOT EXISTS idx_discipline_calls_agent_ts ON discipline_calls(agent, ts);
|
|
167
|
-
`,
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
version: 6,
|
|
171
|
-
name: "006_code_graph",
|
|
172
|
-
sql: `
|
|
173
|
-
CREATE TABLE IF NOT EXISTS code_files (
|
|
174
|
-
path TEXT PRIMARY KEY,
|
|
175
|
-
hash TEXT NOT NULL,
|
|
176
|
-
updated_at TEXT NOT NULL
|
|
177
|
-
);
|
|
178
|
-
CREATE TABLE IF NOT EXISTS code_symbols (
|
|
179
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
180
|
-
file TEXT NOT NULL,
|
|
181
|
-
name TEXT NOT NULL,
|
|
182
|
-
kind TEXT NOT NULL,
|
|
183
|
-
start_line INTEGER,
|
|
184
|
-
end_line INTEGER,
|
|
185
|
-
signature TEXT,
|
|
186
|
-
exported INTEGER DEFAULT 0
|
|
187
|
-
);
|
|
188
|
-
CREATE INDEX IF NOT EXISTS idx_code_symbols_name ON code_symbols(name);
|
|
189
|
-
CREATE INDEX IF NOT EXISTS idx_code_symbols_file ON code_symbols(file);
|
|
190
|
-
CREATE TABLE IF NOT EXISTS code_imports (
|
|
191
|
-
file TEXT NOT NULL,
|
|
192
|
-
specifier TEXT NOT NULL
|
|
193
|
-
);
|
|
194
|
-
CREATE INDEX IF NOT EXISTS idx_code_imports_file ON code_imports(file);
|
|
195
|
-
`,
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
version: 7,
|
|
199
|
-
name: "007_job_queue",
|
|
200
|
-
sql: `
|
|
201
|
-
CREATE TABLE IF NOT EXISTS job_queue (
|
|
202
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
203
|
-
queue_name TEXT NOT NULL,
|
|
204
|
-
payload TEXT NOT NULL,
|
|
205
|
-
status TEXT DEFAULT 'pending',
|
|
206
|
-
retries INTEGER DEFAULT 0,
|
|
207
|
-
max_retries INTEGER DEFAULT 3,
|
|
208
|
-
error_message TEXT,
|
|
209
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
210
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
211
|
-
);
|
|
212
|
-
CREATE INDEX IF NOT EXISTS idx_job_queue_status ON job_queue(status);
|
|
213
|
-
CREATE INDEX IF NOT EXISTS idx_job_queue_queue_name ON job_queue(queue_name);
|
|
214
|
-
`,
|
|
215
|
-
},
|
|
216
|
-
];
|
|
217
|
-
function migrationChecksum(sql) {
|
|
218
|
-
return crypto.createHash("sha256").update(sql.trim(), "utf8").digest("hex");
|
|
219
|
-
}
|
|
220
|
-
function ensureMigrationsTable(db) {
|
|
221
|
-
db.exec(`
|
|
222
|
-
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
223
|
-
version INTEGER PRIMARY KEY,
|
|
224
|
-
name TEXT NOT NULL,
|
|
225
|
-
checksum TEXT NOT NULL,
|
|
226
|
-
applied_at TEXT NOT NULL
|
|
227
|
-
)
|
|
228
|
-
`);
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Run all pending schema migrations transactionally and return applied version numbers.
|
|
232
|
-
*/
|
|
233
|
-
export function runPendingMigrations(db, migrations = BUILTIN_MIGRATIONS) {
|
|
234
|
-
ensureMigrationsTable(db);
|
|
235
|
-
const existingRows = db
|
|
236
|
-
.prepare("SELECT version, name, checksum, applied_at FROM schema_migrations ORDER BY version ASC")
|
|
237
|
-
.all();
|
|
238
|
-
const existingMap = new Map(existingRows.map((r) => [r.version, r]));
|
|
239
|
-
const sorted = [...migrations].sort((a, b) => a.version - b.version);
|
|
240
|
-
const applied = [];
|
|
241
|
-
for (const m of sorted) {
|
|
242
|
-
const prev = existingMap.get(m.version);
|
|
243
|
-
const checksum = migrationChecksum(m.sql);
|
|
244
|
-
if (prev) {
|
|
245
|
-
if (prev.checksum !== checksum) {
|
|
246
|
-
throw new Error(`[MIGRATION] Checksum mismatch for migration v${m.version} (${m.name}): ` +
|
|
247
|
-
`expected ${prev.checksum}, got ${checksum}.`);
|
|
248
|
-
}
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
const applyOne = db.transaction(() => {
|
|
252
|
-
db.exec(m.sql);
|
|
253
|
-
db.prepare("INSERT INTO schema_migrations (version, name, checksum, applied_at) VALUES (?, ?, ?, ?)").run(m.version, m.name, checksum, new Date().toISOString());
|
|
254
|
-
});
|
|
255
|
-
applyOne();
|
|
256
|
-
applied.push(m.version);
|
|
257
|
-
}
|
|
258
|
-
// Legacy column backfill for older databases that predate v1 hash columns
|
|
259
|
-
try {
|
|
260
|
-
const tableInfo = db.prepare("PRAGMA table_info(logs)").all();
|
|
261
|
-
const colNames = tableInfo.map((c) => c.name);
|
|
262
|
-
if (colNames.length > 0) {
|
|
263
|
-
if (!colNames.includes("prev_hash"))
|
|
264
|
-
db.exec("ALTER TABLE logs ADD COLUMN prev_hash TEXT");
|
|
265
|
-
if (!colNames.includes("hash"))
|
|
266
|
-
db.exec("ALTER TABLE logs ADD COLUMN hash TEXT");
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
catch {
|
|
270
|
-
/* ignore if logs table does not exist in custom migration test */
|
|
271
|
-
}
|
|
272
|
-
const latest = db.prepare("SELECT MAX(version) as v FROM schema_migrations").get();
|
|
273
|
-
return {
|
|
274
|
-
applied,
|
|
275
|
-
currentVersion: latest?.v ?? 0,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Return applied and pending migrations for diagnostics/CLI.
|
|
280
|
-
*/
|
|
281
|
-
export function getMigrationStatus(db, migrations = BUILTIN_MIGRATIONS) {
|
|
282
|
-
ensureMigrationsTable(db);
|
|
283
|
-
const applied = db
|
|
284
|
-
.prepare("SELECT version, name, checksum, applied_at FROM schema_migrations ORDER BY version ASC")
|
|
285
|
-
.all();
|
|
286
|
-
const appliedSet = new Set(applied.map((a) => a.version));
|
|
287
|
-
const pending = migrations
|
|
288
|
-
.filter((m) => !appliedSet.has(m.version))
|
|
289
|
-
.map((m) => ({ version: m.version, name: m.name }));
|
|
290
|
-
const currentVersion = applied.length > 0 ? applied[applied.length - 1].version : 0;
|
|
291
|
-
return { currentVersion, applied, pending };
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Run SQLite integrity_check / quick_check and foreign_key_check.
|
|
295
|
-
*/
|
|
296
|
-
export function verifyDatabaseIntegrity(db, mode = "quick") {
|
|
297
|
-
const pragma = mode === "full" ? "integrity_check" : "quick_check";
|
|
298
|
-
const rows = db.prepare(`PRAGMA ${pragma}`).all();
|
|
299
|
-
const messages = rows.map((r) => Object.values(r)[0] || "");
|
|
300
|
-
const fkRows = db.prepare("PRAGMA foreign_key_check").all();
|
|
301
|
-
const ok = messages.length === 1 && messages[0].toLowerCase() === "ok" && fkRows.length === 0;
|
|
302
|
-
return {
|
|
303
|
-
ok,
|
|
304
|
-
messages,
|
|
305
|
-
foreignKeyViolations: fkRows.length,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Perform a consistent online backup using SQLite `VACUUM INTO` and write a `.sha256` sidecar.
|
|
310
|
-
*/
|
|
311
|
-
export function backupDatabase(db, destinationPath) {
|
|
312
|
-
const absDest = path.resolve(destinationPath);
|
|
313
|
-
const parentDir = path.dirname(absDest);
|
|
314
|
-
fs.mkdirSync(parentDir, { recursive: true });
|
|
315
|
-
if (fs.existsSync(absDest)) {
|
|
316
|
-
fs.unlinkSync(absDest);
|
|
317
|
-
}
|
|
318
|
-
// Escape single quotes in path for SQLite VACUUM INTO literal
|
|
319
|
-
const escapedPath = absDest.replace(/'/g, "''");
|
|
320
|
-
db.exec(`VACUUM INTO '${escapedPath}'`);
|
|
321
|
-
const bytes = fs.readFileSync(absDest);
|
|
322
|
-
const sha256 = crypto.createHash("sha256").update(bytes).digest("hex");
|
|
323
|
-
fs.writeFileSync(`${absDest}.sha256`, `${sha256} ${path.basename(absDest)}\n`, { mode: 0o600 });
|
|
324
|
-
const status = getMigrationStatus(db);
|
|
325
|
-
return {
|
|
326
|
-
backupPath: absDest,
|
|
327
|
-
sha256,
|
|
328
|
-
sizeBytes: bytes.length,
|
|
329
|
-
createdAt: new Date().toISOString(),
|
|
330
|
-
schemaVersion: status.currentVersion,
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Verify a backup file (checksum sidecar + SQLite integrity_check) and atomically restore it.
|
|
335
|
-
*/
|
|
336
|
-
export function restoreDatabase(backupPath, targetDbPath, options = {}) {
|
|
337
|
-
const absBackup = path.resolve(backupPath);
|
|
338
|
-
const absTarget = path.resolve(targetDbPath);
|
|
339
|
-
if (!fs.existsSync(absBackup)) {
|
|
340
|
-
throw new Error(`Backup file does not exist: ${absBackup}`);
|
|
341
|
-
}
|
|
342
|
-
const bytes = fs.readFileSync(absBackup);
|
|
343
|
-
const actualSha256 = crypto.createHash("sha256").update(bytes).digest("hex");
|
|
344
|
-
const sidecarPath = `${absBackup}.sha256`;
|
|
345
|
-
if (options.verifyChecksum !== false && fs.existsSync(sidecarPath)) {
|
|
346
|
-
const expected = fs.readFileSync(sidecarPath, "utf8").trim().split(/\s+/)[0];
|
|
347
|
-
if (expected && expected !== actualSha256) {
|
|
348
|
-
throw new Error(`Backup checksum mismatch for ${absBackup}: expected ${expected}, got ${actualSha256}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
// Open backup in read-only mode to verify SQLite integrity before overwriting target
|
|
352
|
-
const tempCheckDb = new Database(absBackup, { readonly: true });
|
|
353
|
-
let schemaVersion = 0;
|
|
354
|
-
try {
|
|
355
|
-
const integrity = verifyDatabaseIntegrity(tempCheckDb, "full");
|
|
356
|
-
if (!integrity.ok) {
|
|
357
|
-
throw new Error(`Backup file failed SQLite integrity check: ${integrity.messages.join("; ")}`);
|
|
358
|
-
}
|
|
359
|
-
try {
|
|
360
|
-
const row = tempCheckDb
|
|
361
|
-
.prepare("SELECT MAX(version) as v FROM schema_migrations")
|
|
362
|
-
.get();
|
|
363
|
-
schemaVersion = row?.v ?? 0;
|
|
364
|
-
}
|
|
365
|
-
catch {
|
|
366
|
-
/* schema_migrations table may not exist in older backups */
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
finally {
|
|
370
|
-
tempCheckDb.close();
|
|
371
|
-
}
|
|
372
|
-
fs.mkdirSync(path.dirname(absTarget), { recursive: true });
|
|
373
|
-
const stagingPath = `${absTarget}.restore.${process.pid}`;
|
|
374
|
-
fs.copyFileSync(absBackup, stagingPath);
|
|
375
|
-
// Remove stale WAL/SHM files before atomic swap
|
|
376
|
-
for (const suffix of ["-wal", "-shm"]) {
|
|
377
|
-
const walFile = `${absTarget}${suffix}`;
|
|
378
|
-
if (fs.existsSync(walFile)) {
|
|
379
|
-
try {
|
|
380
|
-
fs.unlinkSync(walFile);
|
|
381
|
-
}
|
|
382
|
-
catch {
|
|
383
|
-
/* ignore */
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
fs.renameSync(stagingPath, absTarget);
|
|
388
|
-
return {
|
|
389
|
-
restoredPath: absTarget,
|
|
390
|
-
sha256: actualSha256,
|
|
391
|
-
schemaVersion,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
1
|
+
export * from "./modules/storage/migrations.js";
|
|
394
2
|
//# sourceMappingURL=migrations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../../shared/src/migrations.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../../shared/src/migrations.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Identity — cryptographically signed agent tokens.
|
|
3
|
+
*
|
|
4
|
+
* Problem: `ATABEY_ACTIVE_AGENT` is a plain env var, so any tool/subprocess can
|
|
5
|
+
* impersonate `@manager` or `@security`. When `ATABEY_AGENT_SECRET` is set,
|
|
6
|
+
* Atabey accepts an HMAC-signed `ATABEY_AGENT_TOKEN` and verifies it
|
|
7
|
+
* (fail-closed on tampering/expiry). Set `ATABEY_AGENT_IDENTITY_REQUIRED=true`
|
|
8
|
+
* to refuse plain-env identity entirely.
|
|
9
|
+
*
|
|
10
|
+
* Token format: `${agent}.${issuedAt}.${expiresAt}.${hmacSha256Base64url}`
|
|
11
|
+
*/
|
|
12
|
+
export interface AgentIdentity {
|
|
13
|
+
agent: string;
|
|
14
|
+
issuedAt: number;
|
|
15
|
+
expiresAt: number;
|
|
16
|
+
}
|
|
17
|
+
export interface AgentTokenResult {
|
|
18
|
+
valid: boolean;
|
|
19
|
+
agent?: string;
|
|
20
|
+
reason?: string;
|
|
21
|
+
}
|
|
22
|
+
/** True when agent-token verification is mandatory (plain env rejected). */
|
|
23
|
+
export declare function isAgentIdentityEnforced(): boolean;
|
|
24
|
+
/** Sign an agent identity into a compact token. */
|
|
25
|
+
export declare function signAgentToken(agent: string, options?: {
|
|
26
|
+
ttlMs?: number;
|
|
27
|
+
key?: string | Buffer;
|
|
28
|
+
}): string;
|
|
29
|
+
/** Verify an agent token (signature + expiry). */
|
|
30
|
+
export declare function verifyAgentToken(token: string, options?: {
|
|
31
|
+
key?: string | Buffer;
|
|
32
|
+
}): AgentTokenResult;
|
|
33
|
+
/** Decode a token's identity without verification (for diagnostics only). */
|
|
34
|
+
export declare function decodeAgentToken(token: string): AgentIdentity | null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Identity — cryptographically signed agent tokens.
|
|
3
|
+
*
|
|
4
|
+
* Problem: `ATABEY_ACTIVE_AGENT` is a plain env var, so any tool/subprocess can
|
|
5
|
+
* impersonate `@manager` or `@security`. When `ATABEY_AGENT_SECRET` is set,
|
|
6
|
+
* Atabey accepts an HMAC-signed `ATABEY_AGENT_TOKEN` and verifies it
|
|
7
|
+
* (fail-closed on tampering/expiry). Set `ATABEY_AGENT_IDENTITY_REQUIRED=true`
|
|
8
|
+
* to refuse plain-env identity entirely.
|
|
9
|
+
*
|
|
10
|
+
* Token format: `${agent}.${issuedAt}.${expiresAt}.${hmacSha256Base64url}`
|
|
11
|
+
*/
|
|
12
|
+
import crypto from "crypto";
|
|
13
|
+
import { resolveAuditChainKey } from "../observability/audit-chain.js";
|
|
14
|
+
function resolveKey(explicit) {
|
|
15
|
+
if (explicit) {
|
|
16
|
+
return Buffer.isBuffer(explicit) ? explicit : crypto.createHash("sha256").update(explicit, "utf8").digest();
|
|
17
|
+
}
|
|
18
|
+
const envSecret = process.env.ATABEY_AGENT_SECRET;
|
|
19
|
+
if (envSecret && envSecret.trim()) {
|
|
20
|
+
return crypto.createHash("sha256").update(envSecret.trim(), "utf8").digest();
|
|
21
|
+
}
|
|
22
|
+
return resolveAuditChainKey();
|
|
23
|
+
}
|
|
24
|
+
function timingSafeEqual(a, b) {
|
|
25
|
+
const ba = Buffer.from(a);
|
|
26
|
+
const bb = Buffer.from(b);
|
|
27
|
+
if (ba.length !== bb.length)
|
|
28
|
+
return false;
|
|
29
|
+
return crypto.timingSafeEqual(ba, bb);
|
|
30
|
+
}
|
|
31
|
+
/** True when agent-token verification is mandatory (plain env rejected). */
|
|
32
|
+
export function isAgentIdentityEnforced() {
|
|
33
|
+
return process.env.ATABEY_AGENT_IDENTITY_REQUIRED === "true";
|
|
34
|
+
}
|
|
35
|
+
/** Sign an agent identity into a compact token. */
|
|
36
|
+
export function signAgentToken(agent, options = {}) {
|
|
37
|
+
const key = resolveKey(options.key);
|
|
38
|
+
if (!key) {
|
|
39
|
+
throw new Error("[AGENT_IDENTITY] No signing key. Set ATABEY_AGENT_SECRET.");
|
|
40
|
+
}
|
|
41
|
+
const name = agent.replace(/^@/, "");
|
|
42
|
+
const issuedAt = Date.now();
|
|
43
|
+
const expiresAt = issuedAt + (options.ttlMs ?? 60 * 60 * 1000);
|
|
44
|
+
const payload = `${name}.${issuedAt}.${expiresAt}`;
|
|
45
|
+
const sig = crypto.createHmac("sha256", key).update(payload).digest("base64url");
|
|
46
|
+
return `${payload}.${sig}`;
|
|
47
|
+
}
|
|
48
|
+
/** Verify an agent token (signature + expiry). */
|
|
49
|
+
export function verifyAgentToken(token, options = {}) {
|
|
50
|
+
const key = resolveKey(options.key);
|
|
51
|
+
if (!key)
|
|
52
|
+
return { valid: false, reason: "No signing key configured." };
|
|
53
|
+
if (!token || typeof token !== "string")
|
|
54
|
+
return { valid: false, reason: "Empty token." };
|
|
55
|
+
const parts = token.split(".");
|
|
56
|
+
if (parts.length !== 4)
|
|
57
|
+
return { valid: false, reason: "Malformed token." };
|
|
58
|
+
const [name, issuedAt, expiresAt, sig] = parts;
|
|
59
|
+
const payload = `${name}.${issuedAt}.${expiresAt}`;
|
|
60
|
+
const expected = crypto.createHmac("sha256", key).update(payload).digest("base64url");
|
|
61
|
+
if (!timingSafeEqual(sig, expected))
|
|
62
|
+
return { valid: false, reason: "Signature mismatch." };
|
|
63
|
+
const exp = Number(expiresAt);
|
|
64
|
+
if (!Number.isFinite(exp))
|
|
65
|
+
return { valid: false, reason: "Invalid expiry." };
|
|
66
|
+
if (Date.now() > exp)
|
|
67
|
+
return { valid: false, reason: "Token expired." };
|
|
68
|
+
return { valid: true, agent: `@${name}` };
|
|
69
|
+
}
|
|
70
|
+
/** Decode a token's identity without verification (for diagnostics only). */
|
|
71
|
+
export function decodeAgentToken(token) {
|
|
72
|
+
const parts = token.split(".");
|
|
73
|
+
if (parts.length !== 4)
|
|
74
|
+
return null;
|
|
75
|
+
return { agent: `@${parts[0]}`, issuedAt: Number(parts[1]), expiresAt: Number(parts[2]) };
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=agent-identity.js.map
|