@cleocode/core 2026.4.11 → 2026.4.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/dist/codebase-map/analyzers/architecture.d.ts.map +1 -1
- package/dist/codebase-map/analyzers/architecture.js +0 -1
- package/dist/codebase-map/analyzers/architecture.js.map +1 -1
- package/dist/conduit/local-transport.d.ts +18 -8
- package/dist/conduit/local-transport.d.ts.map +1 -1
- package/dist/conduit/local-transport.js +23 -13
- package/dist/conduit/local-transport.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -1
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +19 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +6 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.js +175 -68950
- package/dist/index.js.map +1 -7
- package/dist/init.d.ts +1 -2
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +1 -2
- package/dist/init.js.map +1 -1
- package/dist/internal.d.ts +8 -3
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +13 -6
- package/dist/internal.js.map +1 -1
- package/dist/memory/learnings.d.ts +2 -2
- package/dist/memory/patterns.d.ts +6 -6
- package/dist/output.d.ts +32 -11
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +67 -67
- package/dist/output.js.map +1 -1
- package/dist/paths.js +80 -14
- package/dist/paths.js.map +1 -1
- package/dist/skills/dynamic-skill-generator.d.ts +0 -2
- package/dist/skills/dynamic-skill-generator.d.ts.map +1 -1
- package/dist/skills/dynamic-skill-generator.js.map +1 -1
- package/dist/store/agent-registry-accessor.d.ts +203 -12
- package/dist/store/agent-registry-accessor.d.ts.map +1 -1
- package/dist/store/agent-registry-accessor.js +618 -100
- package/dist/store/agent-registry-accessor.js.map +1 -1
- package/dist/store/api-key-kdf.d.ts +73 -0
- package/dist/store/api-key-kdf.d.ts.map +1 -0
- package/dist/store/api-key-kdf.js +84 -0
- package/dist/store/api-key-kdf.js.map +1 -0
- package/dist/store/cleanup-legacy.js +171 -0
- package/dist/store/cleanup-legacy.js.map +1 -0
- package/dist/store/conduit-sqlite.d.ts +184 -0
- package/dist/store/conduit-sqlite.d.ts.map +1 -0
- package/dist/store/conduit-sqlite.js +570 -0
- package/dist/store/conduit-sqlite.js.map +1 -0
- package/dist/store/global-salt.d.ts +78 -0
- package/dist/store/global-salt.d.ts.map +1 -0
- package/dist/store/global-salt.js +147 -0
- package/dist/store/global-salt.js.map +1 -0
- package/dist/store/migrate-signaldock-to-conduit.d.ts +81 -0
- package/dist/store/migrate-signaldock-to-conduit.d.ts.map +1 -0
- package/dist/store/migrate-signaldock-to-conduit.js +555 -0
- package/dist/store/migrate-signaldock-to-conduit.js.map +1 -0
- package/dist/store/nexus-sqlite.js +28 -3
- package/dist/store/nexus-sqlite.js.map +1 -1
- package/dist/store/signaldock-sqlite.d.ts +122 -19
- package/dist/store/signaldock-sqlite.d.ts.map +1 -1
- package/dist/store/signaldock-sqlite.js +401 -251
- package/dist/store/signaldock-sqlite.js.map +1 -1
- package/dist/store/sqlite-backup.js +122 -4
- package/dist/store/sqlite-backup.js.map +1 -1
- package/dist/system/backup.d.ts +0 -26
- package/dist/system/backup.d.ts.map +1 -1
- package/dist/system/runtime.d.ts +0 -2
- package/dist/system/runtime.d.ts.map +1 -1
- package/dist/system/runtime.js +3 -3
- package/dist/system/runtime.js.map +1 -1
- package/dist/tasks/add.d.ts +1 -1
- package/dist/tasks/add.d.ts.map +1 -1
- package/dist/tasks/add.js +98 -23
- package/dist/tasks/add.js.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +4 -1
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/find.d.ts.map +1 -1
- package/dist/tasks/find.js +4 -1
- package/dist/tasks/find.js.map +1 -1
- package/dist/tasks/labels.d.ts.map +1 -1
- package/dist/tasks/labels.js +4 -1
- package/dist/tasks/labels.js.map +1 -1
- package/dist/tasks/relates.d.ts.map +1 -1
- package/dist/tasks/relates.js +16 -4
- package/dist/tasks/relates.js.map +1 -1
- package/dist/tasks/show.d.ts.map +1 -1
- package/dist/tasks/show.js +4 -1
- package/dist/tasks/show.js.map +1 -1
- package/dist/tasks/update.d.ts.map +1 -1
- package/dist/tasks/update.js +32 -6
- package/dist/tasks/update.js.map +1 -1
- package/dist/validation/engine.d.ts.map +1 -1
- package/dist/validation/engine.js +16 -4
- package/dist/validation/engine.js.map +1 -1
- package/dist/validation/param-utils.d.ts +5 -3
- package/dist/validation/param-utils.d.ts.map +1 -1
- package/dist/validation/param-utils.js +8 -6
- package/dist/validation/param-utils.js.map +1 -1
- package/dist/validation/protocols/_shared.d.ts.map +1 -1
- package/dist/validation/protocols/_shared.js +13 -6
- package/dist/validation/protocols/_shared.js.map +1 -1
- package/package.json +9 -7
- package/src/adapters/__tests__/manager.test.ts +0 -1
- package/src/codebase-map/analyzers/architecture.ts +0 -1
- package/src/conduit/__tests__/local-credential-flow.test.ts +20 -18
- package/src/conduit/__tests__/local-transport.test.ts +14 -12
- package/src/conduit/local-transport.ts +23 -13
- package/src/config.ts +0 -1
- package/src/errors.ts +24 -0
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +2 -5
- package/src/init.ts +1 -2
- package/src/internal.ts +96 -2
- package/src/lifecycle/cant/lifecycle-rcasd.cant +133 -0
- package/src/memory/__tests__/engine-compat.test.ts +2 -2
- package/src/memory/__tests__/pipeline-manifest-sqlite.test.ts +4 -4
- package/src/observability/__tests__/index.test.ts +4 -4
- package/src/observability/__tests__/log-filter.test.ts +4 -4
- package/src/output.ts +73 -75
- package/src/sessions/__tests__/session-grade.integration.test.ts +1 -1
- package/src/sessions/__tests__/session-grade.test.ts +2 -2
- package/src/skills/__tests__/dynamic-skill-generator.test.ts +0 -2
- package/src/skills/dynamic-skill-generator.ts +0 -2
- package/src/store/__tests__/agent-registry-accessor.test.ts +807 -0
- package/src/store/__tests__/api-key-kdf.test.ts +113 -0
- package/src/store/__tests__/backup-crypto.test.ts +101 -0
- package/src/store/__tests__/backup-pack.test.ts +491 -0
- package/src/store/__tests__/backup-unpack.test.ts +298 -0
- package/src/store/__tests__/conduit-sqlite.test.ts +413 -0
- package/src/store/__tests__/global-salt.test.ts +195 -0
- package/src/store/__tests__/migrate-signaldock-to-conduit.test.ts +715 -0
- package/src/store/__tests__/regenerators.test.ts +234 -0
- package/src/store/__tests__/restore-conflict-report.test.ts +274 -0
- package/src/store/__tests__/restore-json-merge.test.ts +521 -0
- package/src/store/__tests__/signaldock-sqlite.test.ts +652 -0
- package/src/store/__tests__/sqlite-backup-global.test.ts +307 -3
- package/src/store/__tests__/sqlite-backup.test.ts +5 -1
- package/src/store/__tests__/t310-integration.test.ts +1150 -0
- package/src/store/__tests__/t310-readiness.test.ts +111 -0
- package/src/store/__tests__/t311-integration.test.ts +661 -0
- package/src/store/agent-registry-accessor.ts +847 -140
- package/src/store/api-key-kdf.ts +104 -0
- package/src/store/backup-crypto.ts +209 -0
- package/src/store/backup-pack.ts +739 -0
- package/src/store/backup-unpack.ts +583 -0
- package/src/store/conduit-sqlite.ts +655 -0
- package/src/store/global-salt.ts +175 -0
- package/src/store/migrate-signaldock-to-conduit.ts +669 -0
- package/src/store/regenerators.ts +243 -0
- package/src/store/restore-conflict-report.ts +317 -0
- package/src/store/restore-json-merge.ts +653 -0
- package/src/store/signaldock-sqlite.ts +431 -254
- package/src/store/sqlite-backup.ts +185 -10
- package/src/store/t310-readiness.ts +119 -0
- package/src/system/backup.ts +2 -62
- package/src/system/runtime.ts +4 -6
- package/src/tasks/__tests__/error-hints.test.ts +256 -0
- package/src/tasks/add.ts +99 -9
- package/src/tasks/complete.ts +4 -1
- package/src/tasks/find.ts +4 -1
- package/src/tasks/labels.ts +4 -1
- package/src/tasks/relates.ts +16 -4
- package/src/tasks/show.ts +4 -1
- package/src/tasks/update.ts +32 -3
- package/src/validation/__tests__/error-hints.test.ts +97 -0
- package/src/validation/engine.ts +16 -1
- package/src/validation/param-utils.ts +10 -7
- package/src/validation/protocols/_shared.ts +14 -6
- package/src/validation/protocols/cant/architecture-decision.cant +80 -0
- package/src/validation/protocols/cant/artifact-publish.cant +95 -0
- package/src/validation/protocols/cant/consensus.cant +74 -0
- package/src/validation/protocols/cant/contribution.cant +82 -0
- package/src/validation/protocols/cant/decomposition.cant +92 -0
- package/src/validation/protocols/cant/implementation.cant +67 -0
- package/src/validation/protocols/cant/provenance.cant +88 -0
- package/src/validation/protocols/cant/release.cant +96 -0
- package/src/validation/protocols/cant/research.cant +66 -0
- package/src/validation/protocols/cant/specification.cant +67 -0
- package/src/validation/protocols/cant/testing.cant +88 -0
- package/src/validation/protocols/cant/validation.cant +65 -0
- package/src/validation/protocols/protocols-markdown/decomposition.md +0 -4
- package/templates/config.template.json +0 -1
- package/templates/global-config.template.json +0 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for api-key-kdf.ts (T349).
|
|
3
|
+
*
|
|
4
|
+
* @task T349
|
|
5
|
+
* @epic T310
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createHmac, randomBytes } from 'node:crypto';
|
|
9
|
+
import { describe, expect, it } from 'vitest';
|
|
10
|
+
import { deriveApiKey, deriveLegacyProjectKey } from '../api-key-kdf.js';
|
|
11
|
+
|
|
12
|
+
describe('api-key-kdf', () => {
|
|
13
|
+
const machineKey = Buffer.from(
|
|
14
|
+
'0000000000000000000000000000000000000000000000000000000000000001',
|
|
15
|
+
'hex',
|
|
16
|
+
);
|
|
17
|
+
const globalSalt = Buffer.from(
|
|
18
|
+
'1111111111111111111111111111111111111111111111111111111111111111',
|
|
19
|
+
'hex',
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
describe('deriveApiKey', () => {
|
|
23
|
+
it('returns a 32-byte Buffer', () => {
|
|
24
|
+
const key = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-1' });
|
|
25
|
+
expect(key).toBeInstanceOf(Buffer);
|
|
26
|
+
expect(key.length).toBe(32);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('is deterministic for identical inputs', () => {
|
|
30
|
+
const k1 = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-1' });
|
|
31
|
+
const k2 = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-1' });
|
|
32
|
+
expect(k2.equals(k1)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('produces different keys for different agentIds', () => {
|
|
36
|
+
const k1 = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-1' });
|
|
37
|
+
const k2 = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-2' });
|
|
38
|
+
expect(k2.equals(k1)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('produces different keys for different machineKeys', () => {
|
|
42
|
+
const otherMachine = randomBytes(32);
|
|
43
|
+
const k1 = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-1' });
|
|
44
|
+
const k2 = deriveApiKey({ machineKey: otherMachine, globalSalt, agentId: 'agent-1' });
|
|
45
|
+
expect(k2.equals(k1)).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('produces different keys for different globalSalts', () => {
|
|
49
|
+
const otherSalt = randomBytes(32);
|
|
50
|
+
const k1 = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-1' });
|
|
51
|
+
const k2 = deriveApiKey({ machineKey, globalSalt: otherSalt, agentId: 'agent-1' });
|
|
52
|
+
expect(k2.equals(k1)).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('throws on empty machineKey', () => {
|
|
56
|
+
expect(() =>
|
|
57
|
+
deriveApiKey({ machineKey: Buffer.alloc(0), globalSalt, agentId: 'agent-1' }),
|
|
58
|
+
).toThrow(/machineKey/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('throws on globalSalt of wrong size', () => {
|
|
62
|
+
expect(() =>
|
|
63
|
+
deriveApiKey({ machineKey, globalSalt: Buffer.alloc(16), agentId: 'agent-1' }),
|
|
64
|
+
).toThrow(/globalSalt.*32/);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('throws on empty agentId', () => {
|
|
68
|
+
expect(() => deriveApiKey({ machineKey, globalSalt, agentId: '' })).toThrow(/agentId/);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('uses HMAC-SHA256 primitive (compatibility with external crypto libs)', () => {
|
|
72
|
+
const key = Buffer.concat([machineKey, globalSalt]);
|
|
73
|
+
const expected = createHmac('sha256', key).update('agent-1', 'utf8').digest();
|
|
74
|
+
const actual = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-1' });
|
|
75
|
+
expect(actual.equals(expected)).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('deriveLegacyProjectKey', () => {
|
|
80
|
+
it('returns a 32-byte Buffer', () => {
|
|
81
|
+
const key = deriveLegacyProjectKey(machineKey, '/home/user/project');
|
|
82
|
+
expect(key).toBeInstanceOf(Buffer);
|
|
83
|
+
expect(key.length).toBe(32);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('is deterministic', () => {
|
|
87
|
+
const k1 = deriveLegacyProjectKey(machineKey, '/p');
|
|
88
|
+
const k2 = deriveLegacyProjectKey(machineKey, '/p');
|
|
89
|
+
expect(k2.equals(k1)).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('produces different keys for different project paths', () => {
|
|
93
|
+
const k1 = deriveLegacyProjectKey(machineKey, '/project-a');
|
|
94
|
+
const k2 = deriveLegacyProjectKey(machineKey, '/project-b');
|
|
95
|
+
expect(k2.equals(k1)).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('differs from new KDF for same machineKey + agentId', () => {
|
|
99
|
+
const legacy = deriveLegacyProjectKey(machineKey, 'agent-1');
|
|
100
|
+
const modern = deriveApiKey({ machineKey, globalSalt, agentId: 'agent-1' });
|
|
101
|
+
// Would be a bug if identical — legacy has no salt
|
|
102
|
+
expect(modern.equals(legacy)).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('throws on empty machineKey', () => {
|
|
106
|
+
expect(() => deriveLegacyProjectKey(Buffer.alloc(0), '/p')).toThrow(/machineKey/);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('throws on empty projectPath', () => {
|
|
110
|
+
expect(() => deriveLegacyProjectKey(machineKey, '')).toThrow(/projectPath/);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for backup-crypto.ts (T345).
|
|
3
|
+
*
|
|
4
|
+
* Covers: round-trip correctness, magic/version header, ciphertext
|
|
5
|
+
* non-determinism (random salt + nonce), error paths (wrong passphrase,
|
|
6
|
+
* truncated payload, bit-flip tamper, magic mismatch, too-short), and
|
|
7
|
+
* isEncryptedBundle detection.
|
|
8
|
+
*
|
|
9
|
+
* @task T345
|
|
10
|
+
* @epic T311
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import crypto from 'node:crypto';
|
|
14
|
+
import { describe, expect, it } from 'vitest';
|
|
15
|
+
import { decryptBundle, encryptBundle, isEncryptedBundle } from '../backup-crypto.js';
|
|
16
|
+
|
|
17
|
+
describe('backup-crypto', () => {
|
|
18
|
+
const plaintext = Buffer.from('Hello, CLEO backup world! 🌍 Non-ASCII: ñoño café');
|
|
19
|
+
const passphrase = 'correct horse battery staple';
|
|
20
|
+
|
|
21
|
+
it('round-trip: decrypt(encrypt(x)) == x', () => {
|
|
22
|
+
const encrypted = encryptBundle(plaintext, passphrase);
|
|
23
|
+
const decrypted = decryptBundle(encrypted, passphrase);
|
|
24
|
+
expect(decrypted.equals(plaintext)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('encryptBundle output starts with CLEOENC1 magic', () => {
|
|
28
|
+
const enc = encryptBundle(plaintext, passphrase);
|
|
29
|
+
expect(enc.subarray(0, 8).toString('utf8')).toBe('CLEOENC1');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('encryptBundle output has version byte 0x01 at offset 8', () => {
|
|
33
|
+
const enc = encryptBundle(plaintext, passphrase);
|
|
34
|
+
expect(enc[8]).toBe(0x01);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('two encryptions of same input differ (random salt + nonce)', () => {
|
|
38
|
+
const e1 = encryptBundle(plaintext, passphrase);
|
|
39
|
+
const e2 = encryptBundle(plaintext, passphrase);
|
|
40
|
+
expect(e1.equals(e2)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('decryptBundle throws on wrong passphrase', () => {
|
|
44
|
+
const enc = encryptBundle(plaintext, passphrase);
|
|
45
|
+
expect(() => decryptBundle(enc, 'wrong passphrase')).toThrow(/authentication failed/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('decryptBundle throws on truncated ciphertext (auth tag missing)', () => {
|
|
49
|
+
const enc = encryptBundle(plaintext, passphrase);
|
|
50
|
+
const truncated = enc.subarray(0, enc.length - 1);
|
|
51
|
+
expect(() => decryptBundle(truncated, passphrase)).toThrow();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('decryptBundle throws on flipped bit (auth tag fails)', () => {
|
|
55
|
+
const enc = encryptBundle(plaintext, passphrase);
|
|
56
|
+
// Flip a byte in the middle of the ciphertext region
|
|
57
|
+
const tampered = Buffer.from(enc);
|
|
58
|
+
const idx = 16 + 32 + 12 + 5; // well inside ciphertext region
|
|
59
|
+
tampered[idx] ^= 0x01;
|
|
60
|
+
expect(() => decryptBundle(tampered, passphrase)).toThrow(/authentication failed/);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('decryptBundle throws on magic mismatch', () => {
|
|
64
|
+
const bogus = Buffer.alloc(128);
|
|
65
|
+
expect(() => decryptBundle(bogus, passphrase)).toThrow(/magic mismatch/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('decryptBundle throws on payload too short', () => {
|
|
69
|
+
const tiny = Buffer.alloc(32);
|
|
70
|
+
expect(() => decryptBundle(tiny, passphrase)).toThrow(/too short/);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('encryptBundle throws on empty passphrase', () => {
|
|
74
|
+
expect(() => encryptBundle(plaintext, '')).toThrow(/passphrase/);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('isEncryptedBundle returns true for magic header', () => {
|
|
78
|
+
const enc = encryptBundle(plaintext, passphrase);
|
|
79
|
+
expect(isEncryptedBundle(enc.subarray(0, 8))).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('isEncryptedBundle returns false for random data', () => {
|
|
83
|
+
const random = crypto.randomBytes(32);
|
|
84
|
+
expect(isEncryptedBundle(random)).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('handles large payloads (1 MB)', () => {
|
|
88
|
+
const large = crypto.randomBytes(1024 * 1024);
|
|
89
|
+
const enc = encryptBundle(large, passphrase);
|
|
90
|
+
const dec = decryptBundle(enc, passphrase);
|
|
91
|
+
expect(dec.equals(large)).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('scrypt derivation is deterministic for same passphrase + salt', () => {
|
|
95
|
+
// Indirectly verified: two independent round-trips both recover the plaintext.
|
|
96
|
+
const e1 = encryptBundle(plaintext, passphrase);
|
|
97
|
+
const e2 = encryptBundle(plaintext, passphrase);
|
|
98
|
+
expect(decryptBundle(e1, passphrase).equals(plaintext)).toBe(true);
|
|
99
|
+
expect(decryptBundle(e2, passphrase).equals(plaintext)).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|