@codeledger/engine 0.7.0
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/ccs/index.d.ts +11 -0
- package/dist/ccs/index.d.ts.map +1 -0
- package/dist/ccs/index.js +10 -0
- package/dist/ccs/index.js.map +1 -0
- package/dist/ccs/score.d.ts +61 -0
- package/dist/ccs/score.d.ts.map +1 -0
- package/dist/ccs/score.js +250 -0
- package/dist/ccs/score.js.map +1 -0
- package/dist/ecl/index.d.ts +9 -0
- package/dist/ecl/index.d.ts.map +1 -0
- package/dist/ecl/index.js +9 -0
- package/dist/ecl/index.js.map +1 -0
- package/dist/ecl/ledger.d.ts +84 -0
- package/dist/ecl/ledger.d.ts.map +1 -0
- package/dist/ecl/ledger.js +235 -0
- package/dist/ecl/ledger.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/iole/expansion-ladder.d.ts +51 -0
- package/dist/iole/expansion-ladder.d.ts.map +1 -0
- package/dist/iole/expansion-ladder.js +153 -0
- package/dist/iole/expansion-ladder.js.map +1 -0
- package/dist/iole/failure-vector.d.ts +21 -0
- package/dist/iole/failure-vector.d.ts.map +1 -0
- package/dist/iole/failure-vector.js +156 -0
- package/dist/iole/failure-vector.js.map +1 -0
- package/dist/iole/index.d.ts +19 -0
- package/dist/iole/index.d.ts.map +1 -0
- package/dist/iole/index.js +17 -0
- package/dist/iole/index.js.map +1 -0
- package/dist/iole/intent-decomposition.d.ts +33 -0
- package/dist/iole/intent-decomposition.d.ts.map +1 -0
- package/dist/iole/intent-decomposition.js +252 -0
- package/dist/iole/intent-decomposition.js.map +1 -0
- package/dist/iole/intent-signature.d.ts +37 -0
- package/dist/iole/intent-signature.d.ts.map +1 -0
- package/dist/iole/intent-signature.js +112 -0
- package/dist/iole/intent-signature.js.map +1 -0
- package/dist/iole/outcome-score.d.ts +25 -0
- package/dist/iole/outcome-score.d.ts.map +1 -0
- package/dist/iole/outcome-score.js +128 -0
- package/dist/iole/outcome-score.js.map +1 -0
- package/dist/isc/index.d.ts +8 -0
- package/dist/isc/index.d.ts.map +1 -0
- package/dist/isc/index.js +8 -0
- package/dist/isc/index.js.map +1 -0
- package/dist/isc/score.d.ts +27 -0
- package/dist/isc/score.d.ts.map +1 -0
- package/dist/isc/score.js +347 -0
- package/dist/isc/score.js.map +1 -0
- package/dist/license/index.d.ts +14 -0
- package/dist/license/index.d.ts.map +1 -0
- package/dist/license/index.js +11 -0
- package/dist/license/index.js.map +1 -0
- package/dist/license/parse.d.ts +42 -0
- package/dist/license/parse.d.ts.map +1 -0
- package/dist/license/parse.js +106 -0
- package/dist/license/parse.js.map +1 -0
- package/dist/license/publicKey.d.ts +37 -0
- package/dist/license/publicKey.d.ts.map +1 -0
- package/dist/license/publicKey.js +48 -0
- package/dist/license/publicKey.js.map +1 -0
- package/dist/license/verify.d.ts +33 -0
- package/dist/license/verify.d.ts.map +1 -0
- package/dist/license/verify.js +82 -0
- package/dist/license/verify.js.map +1 -0
- package/dist/orchestrator/index.d.ts +10 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +10 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +44 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.js +182 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/policy-sim/index.d.ts +8 -0
- package/dist/policy-sim/index.d.ts.map +1 -0
- package/dist/policy-sim/index.js +8 -0
- package/dist/policy-sim/index.js.map +1 -0
- package/dist/policy-sim/simulate.d.ts +18 -0
- package/dist/policy-sim/simulate.d.ts.map +1 -0
- package/dist/policy-sim/simulate.js +61 -0
- package/dist/policy-sim/simulate.js.map +1 -0
- package/dist/provenance/graph.d.ts +42 -0
- package/dist/provenance/graph.d.ts.map +1 -0
- package/dist/provenance/graph.js +139 -0
- package/dist/provenance/graph.js.map +1 -0
- package/dist/provenance/index.d.ts +8 -0
- package/dist/provenance/index.d.ts.map +1 -0
- package/dist/provenance/index.js +8 -0
- package/dist/provenance/index.js.map +1 -0
- package/dist/sce/index.d.ts +15 -0
- package/dist/sce/index.d.ts.map +1 -0
- package/dist/sce/index.js +14 -0
- package/dist/sce/index.js.map +1 -0
- package/dist/sce/slice-builder.d.ts +35 -0
- package/dist/sce/slice-builder.d.ts.map +1 -0
- package/dist/sce/slice-builder.js +198 -0
- package/dist/sce/slice-builder.js.map +1 -0
- package/dist/sce/symbol-graph.d.ts +21 -0
- package/dist/sce/symbol-graph.d.ts.map +1 -0
- package/dist/sce/symbol-graph.js +187 -0
- package/dist/sce/symbol-graph.js.map +1 -0
- package/dist/team-ledger/index.d.ts +8 -0
- package/dist/team-ledger/index.d.ts.map +1 -0
- package/dist/team-ledger/index.js +8 -0
- package/dist/team-ledger/index.js.map +1 -0
- package/dist/team-ledger/ledger.d.ts +48 -0
- package/dist/team-ledger/ledger.d.ts.map +1 -0
- package/dist/team-ledger/ledger.js +208 -0
- package/dist/team-ledger/ledger.js.map +1 -0
- package/dist/team-metrics/index.d.ts +8 -0
- package/dist/team-metrics/index.d.ts.map +1 -0
- package/dist/team-metrics/index.js +8 -0
- package/dist/team-metrics/index.js.map +1 -0
- package/dist/team-metrics/metrics.d.ts +42 -0
- package/dist/team-metrics/metrics.d.ts.map +1 -0
- package/dist/team-metrics/metrics.js +156 -0
- package/dist/team-metrics/metrics.js.map +1 -0
- package/dist/team-policy/index.d.ts +8 -0
- package/dist/team-policy/index.d.ts.map +1 -0
- package/dist/team-policy/index.js +8 -0
- package/dist/team-policy/index.js.map +1 -0
- package/dist/team-policy/policy.d.ts +35 -0
- package/dist/team-policy/policy.d.ts.map +1 -0
- package/dist/team-policy/policy.js +100 -0
- package/dist/team-policy/policy.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ISC — Intent Sufficiency Check
|
|
3
|
+
*
|
|
4
|
+
* PRE-CONTEXT certification layer. Evaluates whether a task prompt carries
|
|
5
|
+
* enough structured intent to justify context construction.
|
|
6
|
+
*
|
|
7
|
+
* Design principles:
|
|
8
|
+
* - Deterministic: same input → same output (no ML, no external calls)
|
|
9
|
+
* - Local-first: pure string analysis
|
|
10
|
+
* - Additive scoring: five independent factors summed with fixed weights
|
|
11
|
+
*
|
|
12
|
+
* Decision thresholds:
|
|
13
|
+
* SUFFICIENT ≥ 0.75 → proceed normally
|
|
14
|
+
* WEAK 0.50–0.74 → proceed with -0.05 CCS penalty
|
|
15
|
+
* INSUFFICIENT < 0.50 → block context construction; surface recommendations
|
|
16
|
+
*/
|
|
17
|
+
// ─── Weights ─────────────────────────────────────────────────────────────────
|
|
18
|
+
const W_TOKEN_SIGNAL = 0.20;
|
|
19
|
+
const W_OPERATION_CLARITY = 0.30;
|
|
20
|
+
const W_DOMAIN_CLARITY = 0.25;
|
|
21
|
+
const W_TARGET_SPECIFICITY = 0.15;
|
|
22
|
+
const W_CONSTRAINT_PRESENCE = 0.10;
|
|
23
|
+
// ─── Thresholds ───────────────────────────────────────────────────────────────
|
|
24
|
+
export const ISC_SUFFICIENT_THRESHOLD = 0.75;
|
|
25
|
+
export const ISC_WEAK_THRESHOLD = 0.50;
|
|
26
|
+
// ─── Keyword Tables ───────────────────────────────────────────────────────────
|
|
27
|
+
/**
|
|
28
|
+
* Operation clarity keywords — clear action verbs that signal a well-formed
|
|
29
|
+
* task. Grouped by verb family for readability.
|
|
30
|
+
*/
|
|
31
|
+
const OPERATION_KEYWORDS = [
|
|
32
|
+
// Bug / fix
|
|
33
|
+
'fix', 'fixes', 'fixing', 'fixed',
|
|
34
|
+
'patch', 'patches', 'patching',
|
|
35
|
+
'debug', 'debugs', 'debugging',
|
|
36
|
+
'repair', 'repairs', 'repairing',
|
|
37
|
+
'resolve', 'resolves', 'resolving',
|
|
38
|
+
// Feature / add
|
|
39
|
+
'add', 'adds', 'adding',
|
|
40
|
+
'implement', 'implements', 'implementing',
|
|
41
|
+
'create', 'creates', 'creating',
|
|
42
|
+
'build', 'builds', 'building',
|
|
43
|
+
'introduce', 'introduces', 'introducing',
|
|
44
|
+
// Refactor / clean
|
|
45
|
+
'refactor', 'refactors', 'refactoring',
|
|
46
|
+
'rename', 'renames', 'renaming',
|
|
47
|
+
'move', 'moves', 'moving',
|
|
48
|
+
'extract', 'extracts', 'extracting',
|
|
49
|
+
'clean', 'cleans', 'cleaning',
|
|
50
|
+
'reorganize', 'reorganizes', 'reorganizing',
|
|
51
|
+
// Update / change
|
|
52
|
+
'update', 'updates', 'updating',
|
|
53
|
+
'upgrade', 'upgrades', 'upgrading',
|
|
54
|
+
'change', 'changes', 'changing',
|
|
55
|
+
'modify', 'modifies', 'modifying',
|
|
56
|
+
'replace', 'replaces', 'replacing',
|
|
57
|
+
// Remove / delete
|
|
58
|
+
'remove', 'removes', 'removing',
|
|
59
|
+
'delete', 'deletes', 'deleting',
|
|
60
|
+
'drop', 'drops', 'dropping',
|
|
61
|
+
// Test
|
|
62
|
+
'test', 'tests', 'testing',
|
|
63
|
+
'write test', 'add test', 'cover',
|
|
64
|
+
// Document / explain
|
|
65
|
+
'document', 'documents', 'documenting',
|
|
66
|
+
'explain', 'explains', 'explaining',
|
|
67
|
+
// Improve / optimize
|
|
68
|
+
'improve', 'improves', 'improving',
|
|
69
|
+
'optimize', 'optimizes', 'optimizing',
|
|
70
|
+
'enhance', 'enhances', 'enhancing',
|
|
71
|
+
// Migrate / convert
|
|
72
|
+
'migrate', 'migrates', 'migrating',
|
|
73
|
+
'convert', 'converts', 'converting',
|
|
74
|
+
'port', 'ports', 'porting',
|
|
75
|
+
];
|
|
76
|
+
/**
|
|
77
|
+
* Domain clarity keywords — recognisable technical domains. A prompt
|
|
78
|
+
* containing any of these is scored as domain-clear.
|
|
79
|
+
*/
|
|
80
|
+
const DOMAIN_KEYWORDS = [
|
|
81
|
+
// Auth / security
|
|
82
|
+
'auth', 'authentication', 'authorization', 'login', 'logout', 'session',
|
|
83
|
+
'oauth', 'jwt', 'token', 'password', 'credential', 'permission', 'role',
|
|
84
|
+
// API / routes
|
|
85
|
+
'api', 'endpoint', 'route', 'routes', 'rest', 'graphql', 'grpc',
|
|
86
|
+
'http', 'https', 'request', 'response', 'middleware',
|
|
87
|
+
// Database / storage
|
|
88
|
+
'database', 'db', 'sql', 'query', 'migration', 'schema', 'table', 'index',
|
|
89
|
+
'sqlite', 'postgres', 'mysql', 'mongo', 'redis', 'cache',
|
|
90
|
+
// UI / frontend
|
|
91
|
+
'ui', 'frontend', 'component', 'react', 'vue', 'angular', 'css', 'style',
|
|
92
|
+
'modal', 'button', 'form', 'input', 'render', 'view',
|
|
93
|
+
// Build / CI
|
|
94
|
+
'build', 'ci', 'workflow', 'deploy', 'release', 'publish', 'bundle',
|
|
95
|
+
'webpack', 'vite', 'rollup', 'tsconfig', 'eslint',
|
|
96
|
+
// Config / infra
|
|
97
|
+
'config', 'configuration', 'settings', 'env', 'environment',
|
|
98
|
+
'docker', 'kubernetes', 'infrastructure',
|
|
99
|
+
// Types / models
|
|
100
|
+
'type', 'types', 'interface', 'model', 'schema', 'dto', 'entity',
|
|
101
|
+
// Tests
|
|
102
|
+
'test', 'tests', 'spec', 'jest', 'vitest', 'mocha', 'coverage',
|
|
103
|
+
// Logging / observability
|
|
104
|
+
'log', 'logs', 'logging', 'telemetry', 'metric', 'trace',
|
|
105
|
+
// CLI / commands
|
|
106
|
+
'cli', 'command', 'flag', 'argument', 'subcommand',
|
|
107
|
+
// Core concepts in this repo
|
|
108
|
+
'ledger', 'bundle', 'context', 'ccs', 'ecl', 'iole', 'sce', 'isc',
|
|
109
|
+
'slice', 'selector', 'scanner', 'repo', 'graph', 'hotspot',
|
|
110
|
+
];
|
|
111
|
+
/**
|
|
112
|
+
* Constraint keywords — words that introduce scope or quality limitations.
|
|
113
|
+
*/
|
|
114
|
+
const CONSTRAINT_KEYWORDS = [
|
|
115
|
+
'without', 'without breaking', 'must', 'should', 'ensure', 'only',
|
|
116
|
+
'keep', 'maintain', 'preserve', 'backward compat', 'backwards compat',
|
|
117
|
+
'no breaking', 'don\'t break', 'do not break', 'safe', 'safely',
|
|
118
|
+
'not affect', 'except', 'but not', 'limited to', 'within',
|
|
119
|
+
'in packages/', 'in src/', 'in tests/', 'only in',
|
|
120
|
+
];
|
|
121
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* Run the Intent Sufficiency Check on a task prompt.
|
|
124
|
+
*
|
|
125
|
+
* Returns a full IntentSufficiencyCheck with score, decision, per-factor
|
|
126
|
+
* breakdown, issues list, and actionable recommendations.
|
|
127
|
+
*/
|
|
128
|
+
export function checkIntentSufficiency(task) {
|
|
129
|
+
const taskLower = task.toLowerCase().trim();
|
|
130
|
+
const tokens = tokenize(task);
|
|
131
|
+
// ── Factor 1: Token Signal (weight 0.20) ─────────────────────────────────
|
|
132
|
+
const tokenSignalScore = scoreTokenSignal(tokens);
|
|
133
|
+
// ── Factor 2: Operation Clarity (weight 0.30) ────────────────────────────
|
|
134
|
+
const { score: operationClarityScore, detected: detectedOperation } = scoreOperationClarity(taskLower, tokens);
|
|
135
|
+
// ── Factor 3: Domain Clarity (weight 0.25) ───────────────────────────────
|
|
136
|
+
const { score: domainClarityScore, detected: detectedDomain } = scoreDomainClarity(taskLower, tokens);
|
|
137
|
+
// ── Factor 4: Target Specificity (weight 0.15) ───────────────────────────
|
|
138
|
+
const { score: targetSpecificityScore, detected: detectedTargets } = scoreTargetSpecificity(task, taskLower, tokens);
|
|
139
|
+
// ── Factor 5: Constraint Presence (weight 0.10) ──────────────────────────
|
|
140
|
+
const constraintPresenceScore = scoreConstraintPresence(taskLower);
|
|
141
|
+
// ── Weighted composite ───────────────────────────────────────────────────
|
|
142
|
+
const factors = {
|
|
143
|
+
tokenSignalScore,
|
|
144
|
+
operationClarityScore,
|
|
145
|
+
domainClarityScore,
|
|
146
|
+
targetSpecificityScore,
|
|
147
|
+
constraintPresenceScore,
|
|
148
|
+
};
|
|
149
|
+
const score = tokenSignalScore * W_TOKEN_SIGNAL +
|
|
150
|
+
operationClarityScore * W_OPERATION_CLARITY +
|
|
151
|
+
domainClarityScore * W_DOMAIN_CLARITY +
|
|
152
|
+
targetSpecificityScore * W_TARGET_SPECIFICITY +
|
|
153
|
+
constraintPresenceScore * W_CONSTRAINT_PRESENCE;
|
|
154
|
+
const roundedScore = Math.round(score * 1000) / 1000;
|
|
155
|
+
// ── Decision ─────────────────────────────────────────────────────────────
|
|
156
|
+
const decision = roundedScore >= ISC_SUFFICIENT_THRESHOLD
|
|
157
|
+
? 'SUFFICIENT'
|
|
158
|
+
: roundedScore >= ISC_WEAK_THRESHOLD
|
|
159
|
+
? 'WEAK'
|
|
160
|
+
: 'INSUFFICIENT';
|
|
161
|
+
// ── Issues + Recommendations ─────────────────────────────────────────────
|
|
162
|
+
const { issues, recommendations } = buildDiagnostics(factors, tokens);
|
|
163
|
+
return {
|
|
164
|
+
score: roundedScore,
|
|
165
|
+
decision,
|
|
166
|
+
factors,
|
|
167
|
+
issues,
|
|
168
|
+
recommendations,
|
|
169
|
+
...(detectedOperation ? { detectedOperation } : {}),
|
|
170
|
+
...(detectedDomain ? { detectedDomain } : {}),
|
|
171
|
+
...(detectedTargets.length > 0 ? { detectedTargets } : {}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// ─── Factor Scorers ───────────────────────────────────────────────────────────
|
|
175
|
+
/**
|
|
176
|
+
* Factor 1: Token Signal
|
|
177
|
+
*
|
|
178
|
+
* Scoring table:
|
|
179
|
+
* < 2 tokens → 0.0
|
|
180
|
+
* 2 tokens → 0.3
|
|
181
|
+
* 3–4 tokens → 0.6
|
|
182
|
+
* 5–7 tokens → 0.8
|
|
183
|
+
* ≥ 8 tokens → 1.0
|
|
184
|
+
*/
|
|
185
|
+
function scoreTokenSignal(tokens) {
|
|
186
|
+
const n = tokens.length;
|
|
187
|
+
if (n < 2)
|
|
188
|
+
return 0.0;
|
|
189
|
+
if (n === 2)
|
|
190
|
+
return 0.3;
|
|
191
|
+
if (n <= 4)
|
|
192
|
+
return 0.6;
|
|
193
|
+
if (n <= 7)
|
|
194
|
+
return 0.8;
|
|
195
|
+
return 1.0;
|
|
196
|
+
}
|
|
197
|
+
function scoreOperationClarity(taskLower, _tokens) {
|
|
198
|
+
for (const kw of OPERATION_KEYWORDS) {
|
|
199
|
+
// Match as a whole word or at start of a phrase
|
|
200
|
+
const escaped = kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
201
|
+
const regex = new RegExp(`\\b${escaped}\\b`);
|
|
202
|
+
if (regex.test(taskLower)) {
|
|
203
|
+
return { score: 1.0, detected: kw };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return { score: 0.0, detected: undefined };
|
|
207
|
+
}
|
|
208
|
+
function scoreDomainClarity(taskLower, _tokens) {
|
|
209
|
+
for (const kw of DOMAIN_KEYWORDS) {
|
|
210
|
+
const escaped = kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
211
|
+
const regex = new RegExp(`\\b${escaped}\\b`);
|
|
212
|
+
if (regex.test(taskLower)) {
|
|
213
|
+
return { score: 1.0, detected: kw };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return { score: 0.0, detected: undefined };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Factor 4: Target Specificity
|
|
220
|
+
*
|
|
221
|
+
* Rewards concrete targets:
|
|
222
|
+
* - File/directory path tokens (contain '/' or end in known extension)
|
|
223
|
+
* - camelCase or snake_case identifiers (internal caps or underscores)
|
|
224
|
+
* - Quoted identifiers: "something" or 'something'
|
|
225
|
+
* - PascalCase identifiers (likely type/class names)
|
|
226
|
+
* - Known extensions mentioned without full path (.ts, .json, etc.)
|
|
227
|
+
*
|
|
228
|
+
* Score:
|
|
229
|
+
* 0 targets → 0.0
|
|
230
|
+
* 1 target → 0.6
|
|
231
|
+
* 2 targets → 0.8
|
|
232
|
+
* ≥ 3 → 1.0
|
|
233
|
+
*/
|
|
234
|
+
function scoreTargetSpecificity(taskOriginal, taskLower, tokens) {
|
|
235
|
+
const targets = new Set();
|
|
236
|
+
// File/directory paths (contain '/' or '\')
|
|
237
|
+
for (const token of tokens) {
|
|
238
|
+
if (/[/\\]/.test(token) && token.length > 1) {
|
|
239
|
+
targets.add(token);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Tokens with known source extensions
|
|
243
|
+
const EXT_RE = /\.(ts|tsx|js|mjs|cjs|json|yaml|yml|md|sh|py|go|rs|java)$/i;
|
|
244
|
+
for (const token of tokens) {
|
|
245
|
+
if (EXT_RE.test(token)) {
|
|
246
|
+
targets.add(token);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// camelCase identifiers (at least one lowercase followed by uppercase)
|
|
250
|
+
const CAMEL_RE = /^[a-z][a-z0-9]*[A-Z][a-zA-Z0-9]*$/;
|
|
251
|
+
for (const token of tokens) {
|
|
252
|
+
if (CAMEL_RE.test(token)) {
|
|
253
|
+
targets.add(token);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// snake_case identifiers (at least one underscore between word chars)
|
|
257
|
+
const SNAKE_RE = /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/;
|
|
258
|
+
for (const token of tokens) {
|
|
259
|
+
if (SNAKE_RE.test(token)) {
|
|
260
|
+
targets.add(token);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// PascalCase (starts with uppercase, has mixed case, length > 3)
|
|
264
|
+
const PASCAL_RE = /^[A-Z][a-z][a-zA-Z0-9]{2,}$/;
|
|
265
|
+
for (const token of tokens) {
|
|
266
|
+
if (PASCAL_RE.test(token)) {
|
|
267
|
+
targets.add(token);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// ALL_CAPS identifiers like README, API, CLI, UI, etc. (3+ chars)
|
|
271
|
+
const ALLCAPS_RE = /^[A-Z][A-Z0-9_]{2,}$/;
|
|
272
|
+
for (const token of tokens) {
|
|
273
|
+
const cleaned = token.replace(/[^A-Z0-9_]/g, '');
|
|
274
|
+
if (ALLCAPS_RE.test(cleaned)) {
|
|
275
|
+
targets.add(token);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Quoted identifiers
|
|
279
|
+
const QUOTED_RE = /["'`]([^"'`]{2,40})["'`]/g;
|
|
280
|
+
let m;
|
|
281
|
+
while ((m = QUOTED_RE.exec(taskOriginal)) !== null) {
|
|
282
|
+
if (m[1] !== undefined)
|
|
283
|
+
targets.add(m[1]);
|
|
284
|
+
}
|
|
285
|
+
// CLI flags like --force, --json
|
|
286
|
+
const FLAG_RE = /--[a-z][-a-z0-9]+/g;
|
|
287
|
+
while ((m = FLAG_RE.exec(taskLower)) !== null) {
|
|
288
|
+
if (m[0] !== undefined)
|
|
289
|
+
targets.add(m[0]);
|
|
290
|
+
}
|
|
291
|
+
// Package scope tokens (e.g. @codeledger/engine, packages/engine)
|
|
292
|
+
const SCOPE_RE = /@[a-z][-a-z0-9]*\/[a-z][-a-z0-9]*/g;
|
|
293
|
+
while ((m = SCOPE_RE.exec(taskLower)) !== null) {
|
|
294
|
+
if (m[0] !== undefined)
|
|
295
|
+
targets.add(m[0]);
|
|
296
|
+
}
|
|
297
|
+
const detected = Array.from(targets).slice(0, 10);
|
|
298
|
+
const count = detected.length;
|
|
299
|
+
const score = count === 0 ? 0.0
|
|
300
|
+
: count === 1 ? 0.6
|
|
301
|
+
: count === 2 ? 0.8
|
|
302
|
+
: 1.0;
|
|
303
|
+
return { score, detected };
|
|
304
|
+
}
|
|
305
|
+
function scoreConstraintPresence(taskLower) {
|
|
306
|
+
for (const kw of CONSTRAINT_KEYWORDS) {
|
|
307
|
+
if (taskLower.includes(kw)) {
|
|
308
|
+
return 1.0;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return 0.0;
|
|
312
|
+
}
|
|
313
|
+
// ─── Diagnostics ─────────────────────────────────────────────────────────────
|
|
314
|
+
function buildDiagnostics(factors, tokens) {
|
|
315
|
+
const issues = [];
|
|
316
|
+
const recommendations = [];
|
|
317
|
+
if (factors.tokenSignalScore < 0.6) {
|
|
318
|
+
issues.push(`Prompt is too short (${tokens.length} token${tokens.length === 1 ? '' : 's'}); too little context for reliable file selection.`);
|
|
319
|
+
recommendations.push('Expand your prompt with more detail about what you want to change and where.');
|
|
320
|
+
}
|
|
321
|
+
if (factors.operationClarityScore === 0) {
|
|
322
|
+
issues.push('No clear action verb detected (e.g., fix, add, refactor, update, remove).');
|
|
323
|
+
recommendations.push('Start the prompt with a concrete action: "Fix …", "Add …", "Refactor …".');
|
|
324
|
+
}
|
|
325
|
+
if (factors.domainClarityScore === 0) {
|
|
326
|
+
issues.push('No recognisable technical domain detected (e.g., auth, api, database, cli, types).');
|
|
327
|
+
recommendations.push('Include the technical area this task belongs to (e.g., "in the auth middleware", "for the database layer").');
|
|
328
|
+
}
|
|
329
|
+
if (factors.targetSpecificityScore < 0.6) {
|
|
330
|
+
issues.push('No specific target identified (file path, function name, module name, or flag).');
|
|
331
|
+
recommendations.push('Name the specific file, function, or module being changed (e.g., "in packages/cli/src/commands/context.ts", "the buildSlice function").');
|
|
332
|
+
}
|
|
333
|
+
return { issues, recommendations };
|
|
334
|
+
}
|
|
335
|
+
// ─── Utilities ────────────────────────────────────────────────────────────────
|
|
336
|
+
/**
|
|
337
|
+
* Tokenize a task string into meaningful tokens.
|
|
338
|
+
* Splits on whitespace and common punctuation, preserving paths and identifiers.
|
|
339
|
+
*/
|
|
340
|
+
function tokenize(task) {
|
|
341
|
+
// Split on whitespace, keeping compound tokens intact
|
|
342
|
+
return task
|
|
343
|
+
.split(/\s+/)
|
|
344
|
+
.map((t) => t.replace(/^[,;:()"'`]+|[,;:()"'`]+$/g, ''))
|
|
345
|
+
.filter((t) => t.length > 0);
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/isc/score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @codeledger/engine/license — Signed license verification.
|
|
3
|
+
*
|
|
4
|
+
* Local-first, offline license verification using Ed25519 digital signatures.
|
|
5
|
+
* The private key lives in the license issuer service (separate private repo).
|
|
6
|
+
* Only the public key is embedded here.
|
|
7
|
+
*/
|
|
8
|
+
export { parseLicenseKey, isSignedKey, base64urlToBuffer } from './parse.js';
|
|
9
|
+
export type { LicensePayload, ParsedSignedLicense, ParsedLegacyLicense, ParsedLicense, ParseResult, ParseError, } from './parse.js';
|
|
10
|
+
export { verifySignature, validateClaims, verifySignedLicense } from './verify.js';
|
|
11
|
+
export type { VerifyResult, VerifySuccess, VerifyFailure } from './verify.js';
|
|
12
|
+
export { KNOWN_PUBLIC_KEYS, getActivePublicKey, getPublicKeyById, } from './publicKey.js';
|
|
13
|
+
export type { PublicKeyEntry } from './publicKey.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7E,YAAY,EACV,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACnF,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9E,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @codeledger/engine/license — Signed license verification.
|
|
3
|
+
*
|
|
4
|
+
* Local-first, offline license verification using Ed25519 digital signatures.
|
|
5
|
+
* The private key lives in the license issuer service (separate private repo).
|
|
6
|
+
* Only the public key is embedded here.
|
|
7
|
+
*/
|
|
8
|
+
export { parseLicenseKey, isSignedKey, base64urlToBuffer } from './parse.js';
|
|
9
|
+
export { verifySignature, validateClaims, verifySignedLicense } from './verify.js';
|
|
10
|
+
export { KNOWN_PUBLIC_KEYS, getActivePublicKey, getPublicKeyById, } from './publicKey.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAU7E,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGnF,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License key parsing — split signed keys into components.
|
|
3
|
+
*
|
|
4
|
+
* Signed format: CL-PRO.<base64url-payload>.<base64url-signature>
|
|
5
|
+
* Legacy format: CL-PRO-XXXX-XXXX-XXXX
|
|
6
|
+
*/
|
|
7
|
+
export interface LicensePayload {
|
|
8
|
+
plan: string;
|
|
9
|
+
features: string[];
|
|
10
|
+
issuedAt: string;
|
|
11
|
+
expiresAt: string | null;
|
|
12
|
+
issuedTo?: string;
|
|
13
|
+
licenseId: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ParsedSignedLicense {
|
|
16
|
+
kind: 'signed';
|
|
17
|
+
prefix: string;
|
|
18
|
+
payloadRaw: string;
|
|
19
|
+
signatureRaw: string;
|
|
20
|
+
payload: LicensePayload;
|
|
21
|
+
}
|
|
22
|
+
export interface ParsedLegacyLicense {
|
|
23
|
+
kind: 'legacy';
|
|
24
|
+
rawKey: string;
|
|
25
|
+
}
|
|
26
|
+
export type ParsedLicense = ParsedSignedLicense | ParsedLegacyLicense;
|
|
27
|
+
export interface ParseError {
|
|
28
|
+
kind: 'error';
|
|
29
|
+
reason: string;
|
|
30
|
+
}
|
|
31
|
+
export type ParseResult = ParsedLicense | ParseError;
|
|
32
|
+
/** Convert base64url string to Buffer. Exported for use by verify.ts. */
|
|
33
|
+
export declare function base64urlToBuffer(input: string): Buffer;
|
|
34
|
+
/**
|
|
35
|
+
* Determine whether a key is signed (contains dots) or legacy format.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isSignedKey(key: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Parse a license key string into its components.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseLicenseKey(rawKey: string): ParseResult;
|
|
42
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/license/parse.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,aAAa,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAEtE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC;AAIrD,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvD;AAOD;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAyB3D"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License key parsing — split signed keys into components.
|
|
3
|
+
*
|
|
4
|
+
* Signed format: CL-PRO.<base64url-payload>.<base64url-signature>
|
|
5
|
+
* Legacy format: CL-PRO-XXXX-XXXX-XXXX
|
|
6
|
+
*/
|
|
7
|
+
// ── Base64url helpers ──────────────────────────────────────────────────────
|
|
8
|
+
/** Convert base64url string to Buffer. Exported for use by verify.ts. */
|
|
9
|
+
export function base64urlToBuffer(input) {
|
|
10
|
+
let base64 = input.replace(/-/g, '+').replace(/_/g, '/');
|
|
11
|
+
const pad = base64.length % 4;
|
|
12
|
+
if (pad === 2)
|
|
13
|
+
base64 += '==';
|
|
14
|
+
else if (pad === 3)
|
|
15
|
+
base64 += '=';
|
|
16
|
+
return Buffer.from(base64, 'base64');
|
|
17
|
+
}
|
|
18
|
+
// ── Parser ─────────────────────────────────────────────────────────────────
|
|
19
|
+
const SIGNED_PREFIX = 'CL-PRO';
|
|
20
|
+
const LEGACY_PATTERN = /^CL-PRO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
|
|
21
|
+
/**
|
|
22
|
+
* Determine whether a key is signed (contains dots) or legacy format.
|
|
23
|
+
*/
|
|
24
|
+
export function isSignedKey(key) {
|
|
25
|
+
return key.startsWith(SIGNED_PREFIX + '.') && key.split('.').length === 3;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse a license key string into its components.
|
|
29
|
+
*/
|
|
30
|
+
export function parseLicenseKey(rawKey) {
|
|
31
|
+
if (!rawKey || typeof rawKey !== 'string') {
|
|
32
|
+
return { kind: 'error', reason: 'License key is empty.' };
|
|
33
|
+
}
|
|
34
|
+
const trimmed = rawKey.trim();
|
|
35
|
+
// Signed format: CL-PRO.<payload>.<signature>
|
|
36
|
+
if (isSignedKey(trimmed)) {
|
|
37
|
+
return parseSignedKey(trimmed);
|
|
38
|
+
}
|
|
39
|
+
// Legacy format: CL-PRO-XXXX-XXXX-XXXX
|
|
40
|
+
if (LEGACY_PATTERN.test(trimmed)) {
|
|
41
|
+
return { kind: 'legacy', rawKey: trimmed };
|
|
42
|
+
}
|
|
43
|
+
// Neither format matches
|
|
44
|
+
if (trimmed.startsWith('CL-PRO.')) {
|
|
45
|
+
return { kind: 'error', reason: 'Invalid signed license format. Expected: CL-PRO.<payload>.<signature>' };
|
|
46
|
+
}
|
|
47
|
+
if (trimmed.startsWith('CL-PRO-')) {
|
|
48
|
+
return { kind: 'error', reason: 'Invalid key format. Expected: CL-PRO-XXXX-XXXX-XXXX (uppercase alphanumeric).' };
|
|
49
|
+
}
|
|
50
|
+
return { kind: 'error', reason: 'Invalid license format.' };
|
|
51
|
+
}
|
|
52
|
+
function parseSignedKey(key) {
|
|
53
|
+
const parts = key.split('.');
|
|
54
|
+
if (parts.length !== 3) {
|
|
55
|
+
return { kind: 'error', reason: 'Invalid signed license format. Expected three dot-separated parts.' };
|
|
56
|
+
}
|
|
57
|
+
const [prefix, payloadRaw, signatureRaw] = parts;
|
|
58
|
+
if (prefix !== SIGNED_PREFIX) {
|
|
59
|
+
return { kind: 'error', reason: `Invalid license prefix. Expected "${SIGNED_PREFIX}".` };
|
|
60
|
+
}
|
|
61
|
+
if (!payloadRaw || !signatureRaw) {
|
|
62
|
+
return { kind: 'error', reason: 'License payload or signature is empty.' };
|
|
63
|
+
}
|
|
64
|
+
// Decode and parse payload
|
|
65
|
+
let decoded;
|
|
66
|
+
try {
|
|
67
|
+
const json = base64urlToBuffer(payloadRaw).toString('utf-8');
|
|
68
|
+
decoded = JSON.parse(json);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return { kind: 'error', reason: 'Failed to decode license payload.' };
|
|
72
|
+
}
|
|
73
|
+
if (typeof decoded !== 'object' || decoded === null) {
|
|
74
|
+
return { kind: 'error', reason: 'License payload is not a valid object.' };
|
|
75
|
+
}
|
|
76
|
+
const obj = decoded;
|
|
77
|
+
// Validate required fields
|
|
78
|
+
if (typeof obj['plan'] !== 'string') {
|
|
79
|
+
return { kind: 'error', reason: 'Missing required field: plan.' };
|
|
80
|
+
}
|
|
81
|
+
if (!Array.isArray(obj['features'])) {
|
|
82
|
+
return { kind: 'error', reason: 'Missing required field: features.' };
|
|
83
|
+
}
|
|
84
|
+
if (typeof obj['issuedAt'] !== 'string') {
|
|
85
|
+
return { kind: 'error', reason: 'Missing required field: issuedAt.' };
|
|
86
|
+
}
|
|
87
|
+
if (typeof obj['licenseId'] !== 'string') {
|
|
88
|
+
return { kind: 'error', reason: 'Missing required field: licenseId.' };
|
|
89
|
+
}
|
|
90
|
+
const payload = {
|
|
91
|
+
plan: obj['plan'],
|
|
92
|
+
features: obj['features'],
|
|
93
|
+
issuedAt: obj['issuedAt'],
|
|
94
|
+
expiresAt: typeof obj['expiresAt'] === 'string' ? obj['expiresAt'] : null,
|
|
95
|
+
issuedTo: typeof obj['issuedTo'] === 'string' ? obj['issuedTo'] : undefined,
|
|
96
|
+
licenseId: obj['licenseId'],
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
kind: 'signed',
|
|
100
|
+
prefix,
|
|
101
|
+
payloadRaw,
|
|
102
|
+
signatureRaw,
|
|
103
|
+
payload,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/license/parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded public key for license verification.
|
|
3
|
+
*
|
|
4
|
+
* This is the Ed25519 public key corresponding to the private key held by
|
|
5
|
+
* the license issuer service. Only the public key lives in this repo.
|
|
6
|
+
*
|
|
7
|
+
* Key rotation: add new keys to KNOWN_PUBLIC_KEYS with a unique keyId.
|
|
8
|
+
* The active key is used for new verifications; older keys remain for
|
|
9
|
+
* backward compatibility with previously issued licenses.
|
|
10
|
+
*/
|
|
11
|
+
export interface PublicKeyEntry {
|
|
12
|
+
keyId: string;
|
|
13
|
+
algorithm: 'Ed25519';
|
|
14
|
+
/** PEM-encoded public key */
|
|
15
|
+
pem: string;
|
|
16
|
+
/** When this key became active */
|
|
17
|
+
activeFrom: string;
|
|
18
|
+
/** If set, this key is no longer used for new issuance */
|
|
19
|
+
retiredAt?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Known public keys for license verification.
|
|
23
|
+
* The issuer service signs with the corresponding private key.
|
|
24
|
+
*
|
|
25
|
+
* To rotate keys:
|
|
26
|
+
* 1. Generate a new Ed25519 keypair in the issuer service
|
|
27
|
+
* 2. Add the public key here with a new keyId
|
|
28
|
+
* 3. Set retiredAt on the old key
|
|
29
|
+
* 4. The issuer starts signing with the new key
|
|
30
|
+
* 5. Old licenses continue to verify against the old key
|
|
31
|
+
*/
|
|
32
|
+
export declare const KNOWN_PUBLIC_KEYS: PublicKeyEntry[];
|
|
33
|
+
/** Get the currently active public key (most recent non-retired). */
|
|
34
|
+
export declare function getActivePublicKey(): PublicKeyEntry;
|
|
35
|
+
/** Find a public key by keyId (for verifying older licenses). */
|
|
36
|
+
export declare function getPublicKeyById(keyId: string): PublicKeyEntry | undefined;
|
|
37
|
+
//# sourceMappingURL=publicKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicKey.d.ts","sourceRoot":"","sources":["../../src/license/publicKey.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,CAAC;IACrB,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,EAW7C,CAAC;AAEF,qEAAqE;AACrE,wBAAgB,kBAAkB,IAAI,cAAc,CAQnD;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE1E"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded public key for license verification.
|
|
3
|
+
*
|
|
4
|
+
* This is the Ed25519 public key corresponding to the private key held by
|
|
5
|
+
* the license issuer service. Only the public key lives in this repo.
|
|
6
|
+
*
|
|
7
|
+
* Key rotation: add new keys to KNOWN_PUBLIC_KEYS with a unique keyId.
|
|
8
|
+
* The active key is used for new verifications; older keys remain for
|
|
9
|
+
* backward compatibility with previously issued licenses.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Known public keys for license verification.
|
|
13
|
+
* The issuer service signs with the corresponding private key.
|
|
14
|
+
*
|
|
15
|
+
* To rotate keys:
|
|
16
|
+
* 1. Generate a new Ed25519 keypair in the issuer service
|
|
17
|
+
* 2. Add the public key here with a new keyId
|
|
18
|
+
* 3. Set retiredAt on the old key
|
|
19
|
+
* 4. The issuer starts signing with the new key
|
|
20
|
+
* 5. Old licenses continue to verify against the old key
|
|
21
|
+
*/
|
|
22
|
+
export const KNOWN_PUBLIC_KEYS = [
|
|
23
|
+
{
|
|
24
|
+
keyId: 'cl-v1',
|
|
25
|
+
algorithm: 'Ed25519',
|
|
26
|
+
pem: [
|
|
27
|
+
'-----BEGIN PUBLIC KEY-----',
|
|
28
|
+
'MCowBQYDK2VwAyEADQoOMv3lKd7sfINa9Fr9cfRQn9QiE/pEO5XcOaFc0Ys=',
|
|
29
|
+
'-----END PUBLIC KEY-----',
|
|
30
|
+
].join('\n'),
|
|
31
|
+
activeFrom: '2026-03-22T00:00:00Z',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
/** Get the currently active public key (most recent non-retired). */
|
|
35
|
+
export function getActivePublicKey() {
|
|
36
|
+
const active = KNOWN_PUBLIC_KEYS
|
|
37
|
+
.filter((k) => !k.retiredAt)
|
|
38
|
+
.sort((a, b) => b.activeFrom.localeCompare(a.activeFrom));
|
|
39
|
+
if (active.length === 0) {
|
|
40
|
+
throw new Error('No active public key configured.');
|
|
41
|
+
}
|
|
42
|
+
return active[0];
|
|
43
|
+
}
|
|
44
|
+
/** Find a public key by keyId (for verifying older licenses). */
|
|
45
|
+
export function getPublicKeyById(keyId) {
|
|
46
|
+
return KNOWN_PUBLIC_KEYS.find((k) => k.keyId === keyId);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=publicKey.js.map
|