@cleocode/core 2026.4.11 → 2026.4.12
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 +7 -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 +49 -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__/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__/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/agent-registry-accessor.ts +847 -140
- package/src/store/api-key-kdf.ts +104 -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/signaldock-sqlite.ts +431 -254
- package/src/store/sqlite-backup.ts +185 -10
- 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,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for global-salt subsystem.
|
|
3
|
+
*
|
|
4
|
+
* @task T348
|
|
5
|
+
* @epic T310
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import crypto from 'node:crypto';
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
13
|
+
import {
|
|
14
|
+
__clearGlobalSaltCache,
|
|
15
|
+
GLOBAL_SALT_FILENAME,
|
|
16
|
+
GLOBAL_SALT_SIZE,
|
|
17
|
+
getGlobalSalt,
|
|
18
|
+
getGlobalSaltPath,
|
|
19
|
+
validateGlobalSalt,
|
|
20
|
+
} from '../global-salt.js';
|
|
21
|
+
|
|
22
|
+
// Mock getCleoHome to use a per-test tmp directory
|
|
23
|
+
let tmpHome: string;
|
|
24
|
+
vi.mock('../../paths.js', () => ({
|
|
25
|
+
getCleoHome: () => tmpHome,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe('global-salt', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'cleo-t348-'));
|
|
31
|
+
__clearGlobalSaltCache();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// -------------------------------------------------------------------------
|
|
39
|
+
// getGlobalSaltPath
|
|
40
|
+
// -------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
describe('getGlobalSaltPath', () => {
|
|
43
|
+
it('returns <cleoHome>/global-salt', () => {
|
|
44
|
+
expect(getGlobalSaltPath()).toBe(path.join(tmpHome, GLOBAL_SALT_FILENAME));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// -------------------------------------------------------------------------
|
|
49
|
+
// getGlobalSalt — first-run generation
|
|
50
|
+
// -------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
describe('getGlobalSalt — first-run generation', () => {
|
|
53
|
+
it('generates a Buffer of exactly 32 bytes on first call', () => {
|
|
54
|
+
const salt = getGlobalSalt();
|
|
55
|
+
expect(salt).toBeInstanceOf(Buffer);
|
|
56
|
+
expect(salt.length).toBe(GLOBAL_SALT_SIZE);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('persists the salt file to disk', () => {
|
|
60
|
+
getGlobalSalt();
|
|
61
|
+
expect(fs.existsSync(getGlobalSaltPath())).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('writes the salt file with mode 0o600 on POSIX', () => {
|
|
65
|
+
if (process.platform === 'win32') return;
|
|
66
|
+
getGlobalSalt();
|
|
67
|
+
const mode = fs.statSync(getGlobalSaltPath()).mode & 0o777;
|
|
68
|
+
expect(mode).toBe(0o600);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('creates the cleoHome directory if it does not exist', () => {
|
|
72
|
+
// Remove the tmp dir to simulate a missing cleoHome
|
|
73
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
74
|
+
expect(() => getGlobalSalt()).not.toThrow();
|
|
75
|
+
expect(fs.existsSync(tmpHome)).toBe(true);
|
|
76
|
+
expect(fs.existsSync(getGlobalSaltPath())).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('leaves no lingering .tmp- files after a successful write', () => {
|
|
80
|
+
getGlobalSalt();
|
|
81
|
+
const files = fs.readdirSync(tmpHome);
|
|
82
|
+
const lingering = files.filter((f) => f.includes('.tmp-'));
|
|
83
|
+
expect(lingering).toEqual([]);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// -------------------------------------------------------------------------
|
|
88
|
+
// getGlobalSalt — memoization
|
|
89
|
+
// -------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
describe('getGlobalSalt — memoization', () => {
|
|
92
|
+
it('returns the same Buffer instance on repeated calls (memoized)', () => {
|
|
93
|
+
const first = getGlobalSalt();
|
|
94
|
+
const second = getGlobalSalt();
|
|
95
|
+
expect(second.equals(first)).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('returns the same bytes after a cache clear (simulated process restart)', () => {
|
|
99
|
+
const first = getGlobalSalt();
|
|
100
|
+
__clearGlobalSaltCache();
|
|
101
|
+
const second = getGlobalSalt();
|
|
102
|
+
expect(second.equals(first)).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('NEVER overwrites an existing file on subsequent calls', () => {
|
|
106
|
+
getGlobalSalt();
|
|
107
|
+
const mtime1 = fs.statSync(getGlobalSaltPath()).mtimeMs;
|
|
108
|
+
|
|
109
|
+
// Spin-wait 2 ms so mtime could differ if the file were rewritten
|
|
110
|
+
const until = Date.now() + 2;
|
|
111
|
+
while (Date.now() < until) {
|
|
112
|
+
// busy wait
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
__clearGlobalSaltCache();
|
|
116
|
+
getGlobalSalt();
|
|
117
|
+
const mtime2 = fs.statSync(getGlobalSaltPath()).mtimeMs;
|
|
118
|
+
expect(mtime2).toBe(mtime1);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// -------------------------------------------------------------------------
|
|
123
|
+
// getGlobalSalt — validation on existing file
|
|
124
|
+
// -------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
describe('getGlobalSalt — validation on existing file', () => {
|
|
127
|
+
it('throws with "wrong size" when the file has incorrect byte count', () => {
|
|
128
|
+
fs.mkdirSync(tmpHome, { recursive: true });
|
|
129
|
+
fs.writeFileSync(getGlobalSaltPath(), Buffer.alloc(16), { mode: 0o600 });
|
|
130
|
+
__clearGlobalSaltCache();
|
|
131
|
+
expect(() => getGlobalSalt()).toThrow(/wrong size/);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('throws with "wrong permissions" on POSIX when mode is too permissive', () => {
|
|
135
|
+
if (process.platform === 'win32') return;
|
|
136
|
+
fs.mkdirSync(tmpHome, { recursive: true });
|
|
137
|
+
const randomBytes = crypto.randomBytes(GLOBAL_SALT_SIZE);
|
|
138
|
+
fs.writeFileSync(getGlobalSaltPath(), randomBytes, { mode: 0o644 });
|
|
139
|
+
__clearGlobalSaltCache();
|
|
140
|
+
expect(() => getGlobalSalt()).toThrow(/wrong permissions/);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
// validateGlobalSalt
|
|
146
|
+
// -------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
describe('validateGlobalSalt', () => {
|
|
149
|
+
it('is a no-op when the salt file does not exist', () => {
|
|
150
|
+
expect(() => validateGlobalSalt()).not.toThrow();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('does not throw when the salt file is valid', () => {
|
|
154
|
+
getGlobalSalt(); // generates a valid file
|
|
155
|
+
expect(() => validateGlobalSalt()).not.toThrow();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('throws with "validation failed" and "size" when size is wrong', () => {
|
|
159
|
+
fs.mkdirSync(tmpHome, { recursive: true });
|
|
160
|
+
fs.writeFileSync(getGlobalSaltPath(), Buffer.alloc(10), { mode: 0o600 });
|
|
161
|
+
expect(() => validateGlobalSalt()).toThrow(/validation failed.*size/i);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('throws with "validation failed" and "permissions" on POSIX when mode is wrong', () => {
|
|
165
|
+
if (process.platform === 'win32') return;
|
|
166
|
+
fs.mkdirSync(tmpHome, { recursive: true });
|
|
167
|
+
const randomBytes = crypto.randomBytes(GLOBAL_SALT_SIZE);
|
|
168
|
+
fs.writeFileSync(getGlobalSaltPath(), randomBytes, { mode: 0o644 });
|
|
169
|
+
expect(() => validateGlobalSalt()).toThrow(/validation failed.*permissions/i);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// -------------------------------------------------------------------------
|
|
174
|
+
// __clearGlobalSaltCache — test utility
|
|
175
|
+
// -------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
describe('__clearGlobalSaltCache', () => {
|
|
178
|
+
it('allows re-generation in a fresh tmp dir after cache is cleared', () => {
|
|
179
|
+
const first = getGlobalSalt();
|
|
180
|
+
|
|
181
|
+
// Simulate switching to a different machine / user by using a new tmpHome
|
|
182
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
183
|
+
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'cleo-t348-fresh-'));
|
|
184
|
+
__clearGlobalSaltCache();
|
|
185
|
+
|
|
186
|
+
const second = getGlobalSalt();
|
|
187
|
+
// Different directory => different (freshly generated) salt
|
|
188
|
+
expect(fs.existsSync(getGlobalSaltPath())).toBe(true);
|
|
189
|
+
// The new salt is a valid 32-byte buffer (may or may not equal first by chance)
|
|
190
|
+
expect(second.length).toBe(GLOBAL_SALT_SIZE);
|
|
191
|
+
// Suppress unused variable warning
|
|
192
|
+
expect(first.length).toBe(GLOBAL_SALT_SIZE);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|