peaks-loop 4.0.0-beta.2 → 4.0.0-beta.4
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/CHANGELOG.md +81 -0
- package/README-en.md +16 -3
- package/README.md +16 -3
- package/dist/src/cli/commands/asset-commands.d.ts +45 -0
- package/dist/src/cli/commands/asset-commands.js +404 -0
- package/dist/src/cli/commands/bee-commands.d.ts +26 -0
- package/dist/src/cli/commands/bee-commands.js +169 -0
- package/dist/src/cli/commands/evolution-commands.d.ts +27 -0
- package/dist/src/cli/commands/evolution-commands.js +393 -0
- package/dist/src/cli/commands/loop-commands.d.ts +19 -13
- package/dist/src/cli/commands/loop-commands.js +147 -13
- package/dist/src/cli/commands/sediment-commands.js +9 -0
- package/dist/src/cli/commands/skill-loop-engineering-readiness-commands.d.ts +67 -0
- package/dist/src/cli/commands/skill-loop-engineering-readiness-commands.js +173 -0
- package/dist/src/cli/program.js +17 -0
- package/dist/src/services/crystallization/crystallization-service.d.ts +239 -0
- package/dist/src/services/crystallization/crystallization-service.js +397 -0
- package/dist/src/services/crystallization/crystallization-store.d.ts +75 -0
- package/dist/src/services/crystallization/crystallization-store.js +193 -0
- package/dist/src/services/crystallization/crystallization-types.d.ts +313 -0
- package/dist/src/services/crystallization/crystallization-types.js +299 -0
- package/dist/src/services/crystallization/evidence-brief-builder.d.ts +237 -0
- package/dist/src/services/crystallization/evidence-brief-builder.js +240 -0
- package/dist/src/services/evolution/evolution-service.d.ts +137 -0
- package/dist/src/services/evolution/evolution-service.js +281 -0
- package/dist/src/services/evolution/evolution-store.d.ts +57 -0
- package/dist/src/services/evolution/evolution-store.js +232 -0
- package/dist/src/services/evolution/evolution-types.d.ts +706 -0
- package/dist/src/services/evolution/evolution-types.js +286 -0
- package/dist/src/services/evolution/independent-evaluator-runner.d.ts +90 -0
- package/dist/src/services/evolution/independent-evaluator-runner.js +106 -0
- package/dist/src/services/evolution/regression-skeptic-runner.d.ts +64 -0
- package/dist/src/services/evolution/regression-skeptic-runner.js +113 -0
- package/dist/src/services/loop/loop-bee-relation-service.d.ts +107 -0
- package/dist/src/services/loop/loop-bee-relation-service.js +211 -0
- package/dist/src/services/loop/loop-bee-relation-store.d.ts +53 -0
- package/dist/src/services/loop/loop-bee-relation-store.js +130 -0
- package/dist/src/services/loop/loop-bee-relation-types.d.ts +118 -0
- package/dist/src/services/loop/loop-bee-relation-types.js +114 -0
- package/dist/src/services/loop/loop-release-service.d.ts +46 -0
- package/dist/src/services/loop/loop-release-service.js +66 -0
- package/dist/src/services/loop/loop-release-store.d.ts +55 -0
- package/dist/src/services/loop/loop-release-store.js +134 -0
- package/dist/src/services/loop/loop-release-types.d.ts +250 -0
- package/dist/src/services/loop/loop-release-types.js +162 -0
- package/dist/src/services/share/bundle-reader.d.ts +100 -0
- package/dist/src/services/share/bundle-reader.js +434 -0
- package/dist/src/services/share/bundle-types.d.ts +392 -0
- package/dist/src/services/share/bundle-types.js +318 -0
- package/dist/src/services/share/bundle-writer.d.ts +97 -0
- package/dist/src/services/share/bundle-writer.js +429 -0
- package/dist/src/services/share/run-state-contract.d.ts +133 -0
- package/dist/src/services/share/run-state-contract.js +138 -0
- package/dist/src/services/skillhub/release-import.js +7 -2
- package/dist/src/services/skillhub/release-retain.js +6 -1
- package/dist/src/services/skillhub/sqlite-store.js +17 -1
- package/dist/src/services/skillhub/types.d.ts +10 -0
- package/dist/src/services/standards/loop-engineering-lint.d.ts +38 -0
- package/dist/src/services/standards/loop-engineering-lint.js +101 -0
- package/dist/src/services/standards/loop-engineering-readiness-lint.d.ts +111 -0
- package/dist/src/services/standards/loop-engineering-readiness-lint.js +248 -0
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/examples/video-demo/README.md +57 -0
- package/examples/video-demo/out/en-closing-960.png +0 -0
- package/examples/video-demo/out/zh-closing-960.png +0 -0
- package/examples/video-demo/preview/peaks-loop-demo-en.mp4 +0 -0
- package/examples/video-demo/preview/peaks-loop-demo.mp4 +0 -0
- package/package.json +6 -2
- package/skills/peaks-code/SKILL.md +13 -0
- package/skills/peaks-content/SKILL.md +272 -0
- package/skills/peaks-issue-fix-orchestrator/SKILL.md +307 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import type { EvolutionEvaluation, EvolutionEvaluationInput, EvolutionProposal, EvolutionProposalInput, EvolutionTargetKind, EvolutionVerdict } from "./evolution-types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Re-apply the evolution_evaluation table migration against an
|
|
5
|
+
* already-open database. Used by tests that build their own DB
|
|
6
|
+
* without the openStateDb pipeline. Idempotent.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ensureEvolutionEvaluationTable(db: Database.Database): void;
|
|
9
|
+
/**
|
|
10
|
+
* Compute the score_delta from before/after scores. Exported for
|
|
11
|
+
* the service layer to use during proposal evaluation.
|
|
12
|
+
*/
|
|
13
|
+
export declare function computeScoreDelta(before: number, after: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Build a persisted EvolutionProposal from the input. The service
|
|
16
|
+
* layer is responsible for enforcing AC-8 (single object / single
|
|
17
|
+
* dimension) BEFORE calling this; the persisted shape always has
|
|
18
|
+
* `target_count = 1` and `dimensions.length = 1`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildProposal(id: string, input: EvolutionProposalInput, createdAt?: string): EvolutionProposal;
|
|
21
|
+
/**
|
|
22
|
+
* Insert a full EvolutionEvaluation row. The row's
|
|
23
|
+
* `schema_version` is taken from the input (must equal
|
|
24
|
+
* `peaks.evolution/1`); `created_at` is stamped from the server
|
|
25
|
+
* clock here, so callers cannot backdate a row.
|
|
26
|
+
*/
|
|
27
|
+
export declare function insertEvolutionEvaluation(db: Database.Database, evalRow: EvolutionEvaluationInput): void;
|
|
28
|
+
/**
|
|
29
|
+
* Read a single EvolutionEvaluation row by id; returns undefined if
|
|
30
|
+
* absent.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getEvolutionEvaluation(db: Database.Database, id: string): EvolutionEvaluation | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* List EvolutionEvaluation rows for a specific target. Filter by
|
|
35
|
+
* `verdict` to scope the listing.
|
|
36
|
+
*/
|
|
37
|
+
export declare function listEvolutionEvaluationsByTarget(db: Database.Database, opts: {
|
|
38
|
+
target_kind: EvolutionTargetKind;
|
|
39
|
+
target_release_id: string;
|
|
40
|
+
verdict?: EvolutionVerdict;
|
|
41
|
+
}): EvolutionEvaluation[];
|
|
42
|
+
/**
|
|
43
|
+
* Update the verdict (and optional user_confirmation_pointer) of
|
|
44
|
+
* an existing EvolutionEvaluation row. Returns the updated row, or
|
|
45
|
+
* undefined if the id is absent.
|
|
46
|
+
*
|
|
47
|
+
* The service layer is responsible for enforcing that the
|
|
48
|
+
* transition `revert` is always allowed and `keep` requires
|
|
49
|
+
* `user_confirmation_pointer` set (AC-15).
|
|
50
|
+
*/
|
|
51
|
+
export declare function updateEvolutionVerdict(db: Database.Database, id: string, verdict: EvolutionVerdict, userConfirmationPointer?: string): EvolutionEvaluation | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Test seam: produce a fresh id for an EvolutionEvaluation row.
|
|
54
|
+
* Format: `eval-<hex>` (lowercase, 12 hex chars). Matches the Zod
|
|
55
|
+
* `^eval-[0-9a-f-]{8,}$` pattern in EvolutionProposalSchema.
|
|
56
|
+
*/
|
|
57
|
+
export declare function newEvaluationId(): string;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level SQLite access for the `evolution_evaluation` table. The
|
|
3
|
+
* migration is registered with the existing `openStateDb()` pipeline
|
|
4
|
+
* via `src/services/skillhub/migrations/005-evolution-evaluation.sql`
|
|
5
|
+
* and is applied in lexicographic order alongside the other
|
|
6
|
+
* skillhub migrations. The function `ensureEvolutionEvaluationTable`
|
|
7
|
+
* below is a belt-and-suspenders re-applier for callers that pass a
|
|
8
|
+
* database they built themselves (e.g. tests).
|
|
9
|
+
*
|
|
10
|
+
* Defense in depth:
|
|
11
|
+
* - All JSON columns are stored as TEXT and parsed at the boundary.
|
|
12
|
+
* No big JSON BLOB at the SQLite layer — the row stays small and
|
|
13
|
+
* queryable.
|
|
14
|
+
* - The migration is idempotent (CREATE TABLE IF NOT EXISTS, CREATE
|
|
15
|
+
* INDEX IF NOT EXISTS) so re-running it is safe.
|
|
16
|
+
* - The single-object / single-dimension / self-score / delta
|
|
17
|
+
* rules are enforced at the SERVICE layer (not the DB); the DB
|
|
18
|
+
* stores the persisted, validated row.
|
|
19
|
+
*/
|
|
20
|
+
const SCHEMA_VERSION = "peaks.evolution/1";
|
|
21
|
+
/**
|
|
22
|
+
* Re-apply the evolution_evaluation table migration against an
|
|
23
|
+
* already-open database. Used by tests that build their own DB
|
|
24
|
+
* without the openStateDb pipeline. Idempotent.
|
|
25
|
+
*/
|
|
26
|
+
export function ensureEvolutionEvaluationTable(db) {
|
|
27
|
+
db.exec(`
|
|
28
|
+
CREATE TABLE IF NOT EXISTS evolution_evaluation (
|
|
29
|
+
id TEXT PRIMARY KEY,
|
|
30
|
+
target_kind TEXT NOT NULL CHECK (target_kind IN ('loop','bee','policy','gate','evaluator')),
|
|
31
|
+
target_release_id TEXT NOT NULL,
|
|
32
|
+
optimization_dimensions_json TEXT NOT NULL,
|
|
33
|
+
target_count INTEGER NOT NULL DEFAULT 1 CHECK (target_count = 1),
|
|
34
|
+
before_snapshot_json TEXT NOT NULL DEFAULT '{}',
|
|
35
|
+
after_snapshot_json TEXT NOT NULL DEFAULT '{}',
|
|
36
|
+
diff_json TEXT NOT NULL DEFAULT '{}',
|
|
37
|
+
before_score REAL NOT NULL,
|
|
38
|
+
after_score REAL NOT NULL,
|
|
39
|
+
score_delta_min REAL NOT NULL DEFAULT 1.0 CHECK (score_delta_min >= 0),
|
|
40
|
+
score_delta REAL NOT NULL,
|
|
41
|
+
author_id TEXT NOT NULL,
|
|
42
|
+
evaluator_id TEXT NOT NULL,
|
|
43
|
+
skeptic_id TEXT NOT NULL,
|
|
44
|
+
verdict TEXT NOT NULL CHECK (verdict IN ('keep','revert','needs-user-decision')),
|
|
45
|
+
user_confirmation_pointer TEXT,
|
|
46
|
+
brief_pointer TEXT,
|
|
47
|
+
rubric_json TEXT NOT NULL DEFAULT '{}',
|
|
48
|
+
red_lines_json TEXT NOT NULL DEFAULT '[]',
|
|
49
|
+
source_traces_json TEXT NOT NULL DEFAULT '[]',
|
|
50
|
+
schema_version TEXT NOT NULL CHECK (schema_version = 'peaks.evolution/1'),
|
|
51
|
+
created_at TEXT NOT NULL,
|
|
52
|
+
CHECK (length(id) > 0)
|
|
53
|
+
);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_evolution_evaluation_target
|
|
55
|
+
ON evolution_evaluation(target_kind, target_release_id);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_evolution_evaluation_verdict
|
|
57
|
+
ON evolution_evaluation(verdict);
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_evolution_evaluation_author
|
|
59
|
+
ON evolution_evaluation(author_id);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_evolution_evaluation_evaluator
|
|
61
|
+
ON evolution_evaluation(evaluator_id);
|
|
62
|
+
`);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compute the score_delta from before/after scores. Exported for
|
|
66
|
+
* the service layer to use during proposal evaluation.
|
|
67
|
+
*/
|
|
68
|
+
export function computeScoreDelta(before, after) {
|
|
69
|
+
// Round to 6 decimals to avoid floating-point noise.
|
|
70
|
+
return Math.round((after - before) * 1_000_000) / 1_000_000;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Build a persisted EvolutionProposal from the input. The service
|
|
74
|
+
* layer is responsible for enforcing AC-8 (single object / single
|
|
75
|
+
* dimension) BEFORE calling this; the persisted shape always has
|
|
76
|
+
* `target_count = 1` and `dimensions.length = 1`.
|
|
77
|
+
*/
|
|
78
|
+
export function buildProposal(id, input, createdAt) {
|
|
79
|
+
const before = input.before_score;
|
|
80
|
+
const after = input.after_score;
|
|
81
|
+
return {
|
|
82
|
+
id,
|
|
83
|
+
target_kind: input.target_kind,
|
|
84
|
+
target_release_id: input.target_release_id,
|
|
85
|
+
optimization_dimension: input.optimization_dimension,
|
|
86
|
+
dimensions: [input.optimization_dimension],
|
|
87
|
+
target_count: 1,
|
|
88
|
+
single_object: true,
|
|
89
|
+
single_optimization_dimension: true,
|
|
90
|
+
before_snapshot: input.before_snapshot ?? {},
|
|
91
|
+
after_snapshot: input.after_snapshot ?? {},
|
|
92
|
+
diff: input.diff ?? {},
|
|
93
|
+
before_score: before,
|
|
94
|
+
after_score: after,
|
|
95
|
+
score_delta_min: input.score_delta_min ?? 1,
|
|
96
|
+
score_delta: computeScoreDelta(before, after),
|
|
97
|
+
author_id: input.author_id,
|
|
98
|
+
rubric: input.rubric ?? {},
|
|
99
|
+
red_lines: input.red_lines ?? [],
|
|
100
|
+
source_traces: input.source_traces ?? [],
|
|
101
|
+
schema_version: SCHEMA_VERSION,
|
|
102
|
+
created_at: createdAt ?? new Date().toISOString(),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function rowToEvaluation(row) {
|
|
106
|
+
return {
|
|
107
|
+
id: row.id,
|
|
108
|
+
proposal: {
|
|
109
|
+
id: row.id,
|
|
110
|
+
target_kind: row.target_kind,
|
|
111
|
+
target_release_id: row.target_release_id,
|
|
112
|
+
optimization_dimension: JSON.parse(row.optimization_dimensions_json)[0],
|
|
113
|
+
dimensions: JSON.parse(row.optimization_dimensions_json),
|
|
114
|
+
target_count: row.target_count,
|
|
115
|
+
single_object: true,
|
|
116
|
+
single_optimization_dimension: true,
|
|
117
|
+
before_snapshot: JSON.parse(row.before_snapshot_json),
|
|
118
|
+
after_snapshot: JSON.parse(row.after_snapshot_json),
|
|
119
|
+
diff: JSON.parse(row.diff_json),
|
|
120
|
+
before_score: row.before_score,
|
|
121
|
+
after_score: row.after_score,
|
|
122
|
+
score_delta_min: row.score_delta_min,
|
|
123
|
+
score_delta: row.score_delta,
|
|
124
|
+
author_id: row.author_id,
|
|
125
|
+
rubric: JSON.parse(row.rubric_json),
|
|
126
|
+
red_lines: JSON.parse(row.red_lines_json),
|
|
127
|
+
source_traces: JSON.parse(row.source_traces_json),
|
|
128
|
+
schema_version: row.schema_version,
|
|
129
|
+
created_at: row.created_at,
|
|
130
|
+
},
|
|
131
|
+
evaluator_id: row.evaluator_id,
|
|
132
|
+
skeptic_id: row.skeptic_id,
|
|
133
|
+
evaluator_result: {
|
|
134
|
+
// We persist the final post-skeptic row only; the raw
|
|
135
|
+
// evaluator_result is not part of the row. Tests can verify
|
|
136
|
+
// the verdict derivation via the service layer.
|
|
137
|
+
score: row.after_score,
|
|
138
|
+
riskTags: [],
|
|
139
|
+
refuteParagraph: "",
|
|
140
|
+
},
|
|
141
|
+
skeptic_result: {
|
|
142
|
+
driftRisks: [],
|
|
143
|
+
overfitRisks: [],
|
|
144
|
+
safetyRegressionRisks: [],
|
|
145
|
+
},
|
|
146
|
+
verdict: row.verdict,
|
|
147
|
+
user_confirmation_pointer: row.user_confirmation_pointer ?? undefined,
|
|
148
|
+
brief_pointer: row.brief_pointer ?? undefined,
|
|
149
|
+
schema_version: row.schema_version,
|
|
150
|
+
created_at: row.created_at,
|
|
151
|
+
score_delta: row.score_delta,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Insert a full EvolutionEvaluation row. The row's
|
|
156
|
+
* `schema_version` is taken from the input (must equal
|
|
157
|
+
* `peaks.evolution/1`); `created_at` is stamped from the server
|
|
158
|
+
* clock here, so callers cannot backdate a row.
|
|
159
|
+
*/
|
|
160
|
+
export function insertEvolutionEvaluation(db, evalRow) {
|
|
161
|
+
const stmt = db.prepare(`INSERT INTO evolution_evaluation (
|
|
162
|
+
id, target_kind, target_release_id, optimization_dimensions_json,
|
|
163
|
+
target_count, before_snapshot_json, after_snapshot_json, diff_json,
|
|
164
|
+
before_score, after_score, score_delta_min, score_delta,
|
|
165
|
+
author_id, evaluator_id, skeptic_id, verdict,
|
|
166
|
+
user_confirmation_pointer, brief_pointer,
|
|
167
|
+
rubric_json, red_lines_json, source_traces_json,
|
|
168
|
+
schema_version, created_at
|
|
169
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
170
|
+
stmt.run(evalRow.id, evalRow.proposal.target_kind, evalRow.proposal.target_release_id, JSON.stringify(evalRow.proposal.dimensions), evalRow.proposal.target_count, JSON.stringify(evalRow.proposal.before_snapshot), JSON.stringify(evalRow.proposal.after_snapshot), JSON.stringify(evalRow.proposal.diff), evalRow.proposal.before_score, evalRow.proposal.after_score, evalRow.proposal.score_delta_min, computeScoreDelta(evalRow.proposal.before_score, evalRow.proposal.after_score), evalRow.proposal.author_id, evalRow.evaluator_id, evalRow.skeptic_id, evalRow.verdict, evalRow.user_confirmation_pointer ?? null, evalRow.brief_pointer ?? null, JSON.stringify(evalRow.proposal.rubric), JSON.stringify(evalRow.proposal.red_lines), JSON.stringify(evalRow.proposal.source_traces), evalRow.schema_version, evalRow.created_at);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Read a single EvolutionEvaluation row by id; returns undefined if
|
|
174
|
+
* absent.
|
|
175
|
+
*/
|
|
176
|
+
export function getEvolutionEvaluation(db, id) {
|
|
177
|
+
const row = db
|
|
178
|
+
.prepare("SELECT * FROM evolution_evaluation WHERE id = ?")
|
|
179
|
+
.get(id);
|
|
180
|
+
if (!row)
|
|
181
|
+
return undefined;
|
|
182
|
+
return rowToEvaluation(row);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* List EvolutionEvaluation rows for a specific target. Filter by
|
|
186
|
+
* `verdict` to scope the listing.
|
|
187
|
+
*/
|
|
188
|
+
export function listEvolutionEvaluationsByTarget(db, opts) {
|
|
189
|
+
const params = [opts.target_kind, opts.target_release_id];
|
|
190
|
+
let sql = "SELECT * FROM evolution_evaluation WHERE target_kind = ? AND target_release_id = ?";
|
|
191
|
+
if (opts.verdict) {
|
|
192
|
+
sql += " AND verdict = ?";
|
|
193
|
+
params.push(opts.verdict);
|
|
194
|
+
}
|
|
195
|
+
sql += " ORDER BY created_at DESC, id ASC";
|
|
196
|
+
const rows = db.prepare(sql).all(...params);
|
|
197
|
+
return rows.map(rowToEvaluation);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Update the verdict (and optional user_confirmation_pointer) of
|
|
201
|
+
* an existing EvolutionEvaluation row. Returns the updated row, or
|
|
202
|
+
* undefined if the id is absent.
|
|
203
|
+
*
|
|
204
|
+
* The service layer is responsible for enforcing that the
|
|
205
|
+
* transition `revert` is always allowed and `keep` requires
|
|
206
|
+
* `user_confirmation_pointer` set (AC-15).
|
|
207
|
+
*/
|
|
208
|
+
export function updateEvolutionVerdict(db, id, verdict, userConfirmationPointer) {
|
|
209
|
+
const existing = getEvolutionEvaluation(db, id);
|
|
210
|
+
if (!existing)
|
|
211
|
+
return undefined;
|
|
212
|
+
const stmt = db.prepare(`UPDATE evolution_evaluation
|
|
213
|
+
SET verdict = ?,
|
|
214
|
+
user_confirmation_pointer = ?
|
|
215
|
+
WHERE id = ?`);
|
|
216
|
+
stmt.run(verdict, userConfirmationPointer ?? null, id);
|
|
217
|
+
return getEvolutionEvaluation(db, id);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Test seam: produce a fresh id for an EvolutionEvaluation row.
|
|
221
|
+
* Format: `eval-<hex>` (lowercase, 12 hex chars). Matches the Zod
|
|
222
|
+
* `^eval-[0-9a-f-]{8,}$` pattern in EvolutionProposalSchema.
|
|
223
|
+
*/
|
|
224
|
+
export function newEvaluationId() {
|
|
225
|
+
// 12-char hex, lowercase, no dashes — the regex allows '-' so
|
|
226
|
+
// 12-char hex is fine. We avoid UUID's dashes to keep ids
|
|
227
|
+
// grep-friendly.
|
|
228
|
+
const hex = Math.floor(Math.random() * 0x1_000_000_000_000)
|
|
229
|
+
.toString(16)
|
|
230
|
+
.padStart(12, "0");
|
|
231
|
+
return `eval-${hex}`;
|
|
232
|
+
}
|