rubric-chat 0.2.3 → 0.4.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/README.md +29 -0
- package/dist/commands/login.js +37 -84
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/rate.js +38 -2
- package/dist/commands/rate.js.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/api.js +7 -4
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/config.js +20 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/render.js +26 -1
- package/dist/lib/render.js.map +1 -1
- package/dist/local/archetypes.js +102 -0
- package/dist/local/archetypes.js.map +1 -0
- package/dist/local/engine.js +130 -0
- package/dist/local/engine.js.map +1 -0
- package/dist/local/engine.test.js +156 -0
- package/dist/local/engine.test.js.map +1 -0
- package/dist/local/heuristics.js +127 -0
- package/dist/local/heuristics.js.map +1 -0
- package/dist/local/model.js +313 -0
- package/dist/local/model.js.map +1 -0
- package/dist/local/rubric.js +44 -0
- package/dist/local/rubric.js.map +1 -0
- package/package.json +4 -2
- package/dist/lib/local-server.js +0 -141
- package/dist/lib/local-server.js.map +0 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// Local-model runner for `rate --local`. Two engines, both optional deps
|
|
2
|
+
// loaded via dynamic import (same pattern as keytar) so a failed native
|
|
3
|
+
// install never breaks the core CLI:
|
|
4
|
+
// - "llama" → node-llama-cpp + GGUF. Has GBNF grammar enforcement,
|
|
5
|
+
// but its bundled llama.cpp predates the gemma4 arch.
|
|
6
|
+
// - "transformers" → @huggingface/transformers (ONNX Runtime). Runs the
|
|
7
|
+
// official onnx-community Gemma 4 exports today; no
|
|
8
|
+
// grammar, so output is parsed defensively instead.
|
|
9
|
+
import { totalmem } from "node:os";
|
|
10
|
+
import kleur from "kleur";
|
|
11
|
+
import prompts from "prompts";
|
|
12
|
+
import { readLocalConfig, writeLocalConfig, getModelsDir } from "../lib/config.js";
|
|
13
|
+
import { DIMENSION_NAMES } from "./rubric.js";
|
|
14
|
+
// Ungated repos so no Hugging Face account is needed. Override with
|
|
15
|
+
// RUBRIC_LOCAL_MODEL_URI if a repo moves or you want a different quant.
|
|
16
|
+
export const MODEL_REGISTRY = {
|
|
17
|
+
// Default. Gemma 4 E2B via the official onnx-community export — verified
|
|
18
|
+
// working on @huggingface/transformers 4.2.0 (the GGUF path is blocked
|
|
19
|
+
// until node-llama-cpp ships a llama.cpp with gemma4 support).
|
|
20
|
+
"gemma-4-e2b": {
|
|
21
|
+
name: "gemma-4-e2b",
|
|
22
|
+
engine: "transformers",
|
|
23
|
+
uri: "onnx-community/gemma-4-E2B-it-ONNX",
|
|
24
|
+
dtype: "q4f16",
|
|
25
|
+
approxDownload: "~3.2 GB",
|
|
26
|
+
approxRamGb: 5,
|
|
27
|
+
licenseLine: "Gemma 4 — Apache 2.0 (Google)"
|
|
28
|
+
},
|
|
29
|
+
"gemma-4-e4b": {
|
|
30
|
+
name: "gemma-4-e4b",
|
|
31
|
+
engine: "transformers",
|
|
32
|
+
uri: "onnx-community/gemma-4-E4B-it-ONNX",
|
|
33
|
+
dtype: "q4f16",
|
|
34
|
+
approxDownload: "~5 GB",
|
|
35
|
+
approxRamGb: 8,
|
|
36
|
+
licenseLine: "Gemma 4 — Apache 2.0 (Google)"
|
|
37
|
+
},
|
|
38
|
+
"gemma-3-4b": {
|
|
39
|
+
name: "gemma-3-4b",
|
|
40
|
+
engine: "llama",
|
|
41
|
+
// Google's QAT Q4_0 — markedly better than post-hoc Q4 at the same size.
|
|
42
|
+
uri: "hf:ggml-org/gemma-3-4b-it-qat-GGUF/gemma-3-4b-it-qat-Q4_0.gguf",
|
|
43
|
+
approxDownload: "~2.5 GB",
|
|
44
|
+
approxRamGb: 4,
|
|
45
|
+
licenseLine: "Gemma 3 — Gemma Terms of Use (ai.google.dev/gemma/terms)"
|
|
46
|
+
},
|
|
47
|
+
"gemma-3-1b": {
|
|
48
|
+
name: "gemma-3-1b",
|
|
49
|
+
engine: "llama",
|
|
50
|
+
uri: "hf:ggml-org/gemma-3-1b-it-GGUF:Q4_K_M",
|
|
51
|
+
approxDownload: "~0.8 GB",
|
|
52
|
+
approxRamGb: 2,
|
|
53
|
+
licenseLine: "Gemma 3 — Gemma Terms of Use (ai.google.dev/gemma/terms)"
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
export function defaultModelName() {
|
|
57
|
+
const ramGb = totalmem() / 1024 ** 3;
|
|
58
|
+
return ramGb >= 8 ? "gemma-4-e2b" : "gemma-3-1b";
|
|
59
|
+
}
|
|
60
|
+
export async function generateLocalJudgment(args) {
|
|
61
|
+
const spec = resolveSpec(args.modelName);
|
|
62
|
+
const config = await readLocalConfig();
|
|
63
|
+
const firstRun = !config.downloadedModels?.includes(spec.name);
|
|
64
|
+
if (firstRun) {
|
|
65
|
+
await confirmDownload(spec, args);
|
|
66
|
+
}
|
|
67
|
+
// Small models drift to 0-5 star-rating scales and bare-label feedback —
|
|
68
|
+
// anchor both right at the end of the prompt where attention is strongest.
|
|
69
|
+
const scaleReminder = "\n\nRemember: every score (raw_score and each dimension score) is an integer from 0 to 100. " +
|
|
70
|
+
"A typical competent session lands between 45 and 55 per dimension. Never use a 0-5 or 0-10 scale. " +
|
|
71
|
+
"Each dimension's feedback must be 1-2 full sentences describing what the user concretely did " +
|
|
72
|
+
"and how to improve — never a bare label like \"Good\".";
|
|
73
|
+
// The llama engine enforces shape with a grammar; the transformers engine
|
|
74
|
+
// has no grammar, so the shape must be spelled out in the prompt.
|
|
75
|
+
const shapeInstructions = spec.engine === "transformers"
|
|
76
|
+
? "\n\nOutput ONLY a JSON object, no markdown fences, no prose, with exactly this shape:\n" +
|
|
77
|
+
`{"raw_score": <int>, "archetype_slug": "<one of: ${args.candidates.join(", ")}>", ` +
|
|
78
|
+
`"dimensions": [one entry for EACH of ${DIMENSION_NAMES.join(", ")}: ` +
|
|
79
|
+
`{"dimension": "<name>", "score": <int>, "feedback": "<1-2 sentences>", "evidence_turn_indices": [<int>]}], ` +
|
|
80
|
+
`"summary": "<2-3 sentence verdict>"}`
|
|
81
|
+
: "";
|
|
82
|
+
const fullPrompt = args.userPrompt + scaleReminder + shapeInstructions;
|
|
83
|
+
const runner = spec.engine === "transformers"
|
|
84
|
+
? await makeTransformersRunner(spec, args, fullPrompt)
|
|
85
|
+
: await makeLlamaRunner(spec, args, fullPrompt);
|
|
86
|
+
try {
|
|
87
|
+
if (firstRun) {
|
|
88
|
+
// The runner factory finished the download — remember it so the next
|
|
89
|
+
// run skips the consent prompt.
|
|
90
|
+
await writeLocalConfig({
|
|
91
|
+
...config,
|
|
92
|
+
localModel: spec.name,
|
|
93
|
+
downloadedModels: [...(config.downloadedModels ?? []), spec.name]
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
let parsed = await runner.generate(0.1);
|
|
97
|
+
if (isDegenerate(parsed)) {
|
|
98
|
+
// One retry a bit warmer — small models occasionally emit flat,
|
|
99
|
+
// all-identical scores at near-zero temperature.
|
|
100
|
+
parsed = await runner.generate(0.6);
|
|
101
|
+
}
|
|
102
|
+
if (isDegenerate(parsed)) {
|
|
103
|
+
// Still junk after the retry: surface it as a failure so the caller
|
|
104
|
+
// falls back to the clearly-labeled heuristic estimate instead of
|
|
105
|
+
// rendering a fake model judgment.
|
|
106
|
+
throw new Error(`${spec.name} produced degenerate output twice`);
|
|
107
|
+
}
|
|
108
|
+
return { parsed, modelName: spec.name };
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
await runner.dispose();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function makeLlamaRunner(spec, args, fullPrompt) {
|
|
115
|
+
let llamaCpp;
|
|
116
|
+
try {
|
|
117
|
+
llamaCpp = await import("node-llama-cpp");
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
throw new Error("node-llama-cpp is not installed (it's an optional dependency). " +
|
|
121
|
+
"Reinstall with `npm install -g rubric-chat` and check that native builds aren't being skipped.");
|
|
122
|
+
}
|
|
123
|
+
const modelPath = await llamaCpp.resolveModelFile(process.env.RUBRIC_LOCAL_MODEL_URI ?? spec.uri, { directory: getModelsDir(), cli: !args.quiet });
|
|
124
|
+
const llama = await llamaCpp.getLlama();
|
|
125
|
+
const model = await llama.loadModel({ modelPath });
|
|
126
|
+
const grammar = await llama.createGrammarForJsonSchema(reportJsonSchema(args.candidates));
|
|
127
|
+
const context = await model.createContext();
|
|
128
|
+
const session = new llamaCpp.LlamaChatSession({
|
|
129
|
+
contextSequence: context.getSequence(),
|
|
130
|
+
systemPrompt: args.systemPrompt
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
async generate(temperature) {
|
|
134
|
+
const controller = new AbortController();
|
|
135
|
+
const timer = setTimeout(() => controller.abort(), args.timeoutMs);
|
|
136
|
+
try {
|
|
137
|
+
const raw = await session.prompt(fullPrompt, {
|
|
138
|
+
grammar,
|
|
139
|
+
temperature,
|
|
140
|
+
maxTokens: 1600,
|
|
141
|
+
signal: controller.signal
|
|
142
|
+
});
|
|
143
|
+
return JSON.parse(raw);
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
async dispose() {
|
|
150
|
+
await model.dispose();
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
async function makeTransformersRunner(spec, args, fullPrompt) {
|
|
155
|
+
let tjs;
|
|
156
|
+
try {
|
|
157
|
+
tjs = await import("@huggingface/transformers");
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
throw new Error("@huggingface/transformers is not installed (it's an optional dependency). " +
|
|
161
|
+
"Reinstall with `npm install -g rubric-chat`.");
|
|
162
|
+
}
|
|
163
|
+
tjs.env.cacheDir = getModelsDir();
|
|
164
|
+
let lastPct = -1;
|
|
165
|
+
const generator = await tjs.pipeline("text-generation", process.env.RUBRIC_LOCAL_MODEL_URI ?? spec.uri, {
|
|
166
|
+
dtype: spec.dtype ?? "q4f16",
|
|
167
|
+
device: "cpu",
|
|
168
|
+
progress_callback: args.quiet
|
|
169
|
+
? undefined
|
|
170
|
+
: (p) => {
|
|
171
|
+
if (p.status === "progress" && p.file?.includes("_data") && p.progress != null) {
|
|
172
|
+
const pct = Math.floor(p.progress);
|
|
173
|
+
if (pct !== lastPct) {
|
|
174
|
+
lastPct = pct;
|
|
175
|
+
process.stderr.write(`\r downloading ${p.file.split("/").pop()}: ${pct}% `);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
if (!args.quiet && lastPct >= 0)
|
|
181
|
+
process.stderr.write("\n");
|
|
182
|
+
const messages = [
|
|
183
|
+
{ role: "system", content: args.systemPrompt },
|
|
184
|
+
{ role: "user", content: fullPrompt }
|
|
185
|
+
];
|
|
186
|
+
return {
|
|
187
|
+
async generate(temperature) {
|
|
188
|
+
const generation = generator(messages, {
|
|
189
|
+
max_new_tokens: 1600,
|
|
190
|
+
do_sample: temperature > 0.2,
|
|
191
|
+
temperature,
|
|
192
|
+
return_full_text: false
|
|
193
|
+
});
|
|
194
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("local generation timed out")), args.timeoutMs));
|
|
195
|
+
const out = await Promise.race([generation, timeout]);
|
|
196
|
+
const generated = out[0]?.generated_text;
|
|
197
|
+
const text = typeof generated === "string"
|
|
198
|
+
? generated
|
|
199
|
+
: (generated?.at(-1)?.content ?? "");
|
|
200
|
+
return extractReportJson(text);
|
|
201
|
+
},
|
|
202
|
+
async dispose() {
|
|
203
|
+
await generator.dispose?.();
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** Defensive JSON extraction for engines without grammar enforcement. */
|
|
208
|
+
export function extractReportJson(text) {
|
|
209
|
+
const cleaned = text.replace(/```(?:json)?/gi, "");
|
|
210
|
+
const match = cleaned.match(/\{[\s\S]*\}/);
|
|
211
|
+
if (!match)
|
|
212
|
+
throw new Error("no JSON object in model output");
|
|
213
|
+
const parsed = JSON.parse(match[0]);
|
|
214
|
+
const rawDimensions = Array.isArray(parsed.dimensions) ? parsed.dimensions : [];
|
|
215
|
+
const dimensions = rawDimensions
|
|
216
|
+
.filter((d) => typeof d === "object" && d !== null)
|
|
217
|
+
.map((d) => ({
|
|
218
|
+
dimension: String(d.dimension ?? "")
|
|
219
|
+
.toLowerCase()
|
|
220
|
+
.replace(/[\s-]+/g, "_"),
|
|
221
|
+
score: Number(d.score ?? 0),
|
|
222
|
+
feedback: String(d.feedback ?? ""),
|
|
223
|
+
evidence_turn_indices: Array.isArray(d.evidence_turn_indices)
|
|
224
|
+
? d.evidence_turn_indices.map(Number).filter(Number.isFinite)
|
|
225
|
+
: [0]
|
|
226
|
+
}));
|
|
227
|
+
return {
|
|
228
|
+
raw_score: Number(parsed.raw_score ?? 0),
|
|
229
|
+
archetype_slug: String(parsed.archetype_slug ?? ""),
|
|
230
|
+
dimensions,
|
|
231
|
+
summary: String(parsed.summary ?? "")
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function resolveSpec(modelName) {
|
|
235
|
+
const name = modelName ?? defaultModelName();
|
|
236
|
+
const spec = MODEL_REGISTRY[name];
|
|
237
|
+
if (!spec) {
|
|
238
|
+
throw new Error(`Unknown local model "${name}". Available: ${Object.keys(MODEL_REGISTRY).join(", ")}`);
|
|
239
|
+
}
|
|
240
|
+
return spec;
|
|
241
|
+
}
|
|
242
|
+
async function confirmDownload(spec, args) {
|
|
243
|
+
if (!args.quiet) {
|
|
244
|
+
console.log("");
|
|
245
|
+
console.log(` Local mode needs the ${kleur.bold(spec.name)} model (${spec.approxDownload} download,`);
|
|
246
|
+
console.log(` ~${spec.approxRamGb} GB RAM while scoring). Cached in ~/.rubric/models/ after the first run.`);
|
|
247
|
+
console.log(` ${kleur.dim(spec.licenseLine)}`);
|
|
248
|
+
console.log("");
|
|
249
|
+
}
|
|
250
|
+
if (args.yes || !process.stdin.isTTY)
|
|
251
|
+
return;
|
|
252
|
+
const response = await prompts({
|
|
253
|
+
type: "confirm",
|
|
254
|
+
name: "ok",
|
|
255
|
+
message: `Download ${spec.name} now?`,
|
|
256
|
+
initial: true
|
|
257
|
+
});
|
|
258
|
+
if (!response.ok) {
|
|
259
|
+
throw new Error("model download declined — rerun with --yes to skip this prompt");
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function reportJsonSchema(candidates) {
|
|
263
|
+
// Each dimension is a fixed-order prefixItem with a `const` name so the
|
|
264
|
+
// grammar forces the model to score all six — small models otherwise emit
|
|
265
|
+
// an empty array and the whole report degrades to 50s.
|
|
266
|
+
const dimensionSlot = (name) => ({
|
|
267
|
+
type: "object",
|
|
268
|
+
properties: {
|
|
269
|
+
dimension: { const: name },
|
|
270
|
+
score: { type: "integer" },
|
|
271
|
+
feedback: { type: "string" },
|
|
272
|
+
evidence_turn_indices: {
|
|
273
|
+
type: "array",
|
|
274
|
+
items: { type: "integer" },
|
|
275
|
+
minItems: 1,
|
|
276
|
+
maxItems: 4
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
required: ["dimension", "score", "feedback", "evidence_turn_indices"]
|
|
280
|
+
});
|
|
281
|
+
return {
|
|
282
|
+
type: "object",
|
|
283
|
+
properties: {
|
|
284
|
+
raw_score: { type: "integer" },
|
|
285
|
+
archetype_slug: { enum: candidates },
|
|
286
|
+
dimensions: {
|
|
287
|
+
type: "array",
|
|
288
|
+
prefixItems: DIMENSION_NAMES.map(dimensionSlot),
|
|
289
|
+
minItems: DIMENSION_NAMES.length,
|
|
290
|
+
maxItems: DIMENSION_NAMES.length
|
|
291
|
+
},
|
|
292
|
+
summary: { type: "string" }
|
|
293
|
+
},
|
|
294
|
+
required: ["raw_score", "archetype_slug", "dimensions", "summary"]
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function isDegenerate(parsed) {
|
|
298
|
+
if (parsed.dimensions.length < 4)
|
|
299
|
+
return true;
|
|
300
|
+
if (parsed.dimensions.some((dimension) => !dimension.feedback.trim()))
|
|
301
|
+
return true;
|
|
302
|
+
const scores = parsed.dimensions.map((dimension) => dimension.score);
|
|
303
|
+
// Star-rating drift: everything ≤10 means the model ignored the 0-100 scale.
|
|
304
|
+
if (Math.max(...scores) <= 10)
|
|
305
|
+
return true;
|
|
306
|
+
const uniqueScores = new Set(scores);
|
|
307
|
+
if (uniqueScores.size === 1 && parsed.dimensions.length >= 4)
|
|
308
|
+
return true;
|
|
309
|
+
// Copy-pasted feedback across dimensions is the other tiny-model failure mode.
|
|
310
|
+
const feedbacks = new Set(parsed.dimensions.map((dimension) => dimension.feedback.trim()));
|
|
311
|
+
return feedbacks.size === 1 && parsed.dimensions.length >= 4;
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/local/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const DIMENSION_WEIGHTS = {
|
|
2
|
+
specificity: 0.2,
|
|
3
|
+
context_provision: 0.15,
|
|
4
|
+
structure: 0.1,
|
|
5
|
+
iteration_quality: 0.2,
|
|
6
|
+
scope_discipline: 0.15,
|
|
7
|
+
meta_prompting: 0.2
|
|
8
|
+
};
|
|
9
|
+
export const DIMENSION_NAMES = Object.keys(DIMENSION_WEIGHTS);
|
|
10
|
+
export function applyStrictness(raw) {
|
|
11
|
+
// Python's round() is banker's rounding; Math.round rounds half up. The
|
|
12
|
+
// curve almost never lands on an exact .5, so the drift is theoretical.
|
|
13
|
+
const adjusted = Math.pow(raw, 1.15) / Math.pow(100, 0.15);
|
|
14
|
+
return Math.max(0, Math.min(100, Math.round(adjusted)));
|
|
15
|
+
}
|
|
16
|
+
// Adapted from the server SYSTEM_PROMPT for a small on-device judge: same
|
|
17
|
+
// dimensions and calibration, plus explicit score anchors because 2–4B models
|
|
18
|
+
// skew generous. JSON shape instructions are omitted — the GBNF grammar
|
|
19
|
+
// enforces the output shape at the sampler level.
|
|
20
|
+
export const LOCAL_SYSTEM_PROMPT = `
|
|
21
|
+
You are a harsh, calibrated judge of prompting skill. Median competent users score 45-55.
|
|
22
|
+
Scores above 80 require exceptional, professional-grade prompting throughout the session.
|
|
23
|
+
|
|
24
|
+
Calibration anchors — follow them strictly:
|
|
25
|
+
- A bare one-line ask with no context, constraints, or success criteria: 20-35.
|
|
26
|
+
- A clear ask with some context but no output requirements: 40-55.
|
|
27
|
+
- Structured prompts with constraints, examples, and explicit output shape: 60-75.
|
|
28
|
+
- Only flawless professional-grade sessions throughout: 80+.
|
|
29
|
+
|
|
30
|
+
Analyze ONLY the user's turns. Do not assume access to assistant response content.
|
|
31
|
+
|
|
32
|
+
Score these dimensions from 0 to 100:
|
|
33
|
+
- specificity: concrete asks, measurable success criteria, clear desired outcome.
|
|
34
|
+
- context_provision: background, constraints, examples, prior attempts.
|
|
35
|
+
- structure: organized sections, lists, separation of ask from background.
|
|
36
|
+
- iteration_quality: diagnosis and refinement across turns instead of repeating or abandoning.
|
|
37
|
+
- scope_discipline: coherent thread instead of unrelated scattered asks.
|
|
38
|
+
- meta_prompting: instructions on role, format, length, reasoning style, and output shape.
|
|
39
|
+
|
|
40
|
+
For every dimension, cite specific user turn indices as evidence.
|
|
41
|
+
Choose exactly one archetype slug from the provided candidates.
|
|
42
|
+
Keep feedback to 1-2 short sentences per dimension and a 2-3 sentence summary.
|
|
43
|
+
`.trim();
|
|
44
|
+
//# sourceMappingURL=rubric.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rubric.js","sourceRoot":"","sources":["../../src/local/rubric.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,iBAAiB,GAAkC;IAC9D,WAAW,EAAE,GAAG;IAChB,iBAAiB,EAAE,IAAI;IACvB,SAAS,EAAE,GAAG;IACd,iBAAiB,EAAE,GAAG;IACtB,gBAAgB,EAAE,IAAI;IACtB,cAAc,EAAE,GAAG;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAoB,CAAC;AAEjF,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,0EAA0E;AAC1E,8EAA8E;AAC9E,wEAAwE;AACxE,kDAAkD;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;CAuBlC,CAAC,IAAI,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rubric-chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A strict 0–100 score for AI conversations. Auto-discovers Claude Code, Codex CLI, and Cursor sessions; also accepts ChatGPT and Claude.ai exports. Six dimensions, eight archetypes, shareable score card.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -54,7 +54,9 @@
|
|
|
54
54
|
"prompts": "^2.4.2"
|
|
55
55
|
},
|
|
56
56
|
"optionalDependencies": {
|
|
57
|
-
"
|
|
57
|
+
"@huggingface/transformers": "^4.2.0",
|
|
58
|
+
"keytar": "^7.9.0",
|
|
59
|
+
"node-llama-cpp": "^3.18.0"
|
|
58
60
|
},
|
|
59
61
|
"devDependencies": {
|
|
60
62
|
"@types/better-sqlite3": "^7.6.13",
|
package/dist/lib/local-server.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { createServer } from "node:http";
|
|
2
|
-
import { randomBytes } from "node:crypto";
|
|
3
|
-
const SUCCESS_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>Rubric — Signed in</title>
|
|
4
|
-
<style>
|
|
5
|
-
:root { color-scheme: dark; }
|
|
6
|
-
html, body { margin: 0; height: 100%; font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; background: #0C0B0A; color: #F2EEE3; }
|
|
7
|
-
main { min-height: 100%; display: grid; place-items: center; padding: 24px; }
|
|
8
|
-
.card { max-width: 420px; padding: 28px 30px; border: 1px solid #2A271F; border-radius: 16px; background: #17150F; box-shadow: 0 12px 28px -16px rgba(0,0,0,.65); }
|
|
9
|
-
.eyebrow { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: #857E73; margin: 0 0 8px; }
|
|
10
|
-
h1 { font-family: ui-serif, Georgia, serif; font-weight: 500; font-size: 26px; margin: 0 0 14px; }
|
|
11
|
-
p { color: #B7B1A1; line-height: 1.55; margin: 0 0 12px; font-size: 15px; }
|
|
12
|
-
code { background: #1F1C16; border: 1px solid #2A271F; border-radius: 6px; padding: 2px 6px; font-size: 12px; color: #F2EEE3; }
|
|
13
|
-
</style></head><body><main><div class="card">
|
|
14
|
-
<p class="eyebrow">Rubric CLI</p>
|
|
15
|
-
<h1>You can close this tab.</h1>
|
|
16
|
-
<p>Your terminal session has picked up the credentials. Head back to <code>rubric-chat</code>.</p>
|
|
17
|
-
</div></main></body></html>`;
|
|
18
|
-
const FAILURE_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>Rubric — Bad request</title></head>
|
|
19
|
-
<body style="margin:0;font-family:system-ui;background:#0C0B0A;color:#F2EEE3;display:grid;place-items:center;height:100vh">
|
|
20
|
-
<div style="max-width:420px;padding:24px;border:1px solid #9B2C2C;border-radius:12px;background:#17150F">
|
|
21
|
-
<h1 style="font-size:18px;margin:0 0 10px">Something looked off.</h1>
|
|
22
|
-
<p style="color:#B7B1A1;font-size:14px;line-height:1.55;margin:0">The CLI couldn't verify this response. Try running <code>rubric-chat login</code> again.</p>
|
|
23
|
-
</div></body></html>`;
|
|
24
|
-
function setCors(res) {
|
|
25
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
26
|
-
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
27
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
28
|
-
}
|
|
29
|
-
async function readJson(req) {
|
|
30
|
-
return new Promise((resolve, reject) => {
|
|
31
|
-
const chunks = [];
|
|
32
|
-
req.on("data", (chunk) => chunks.push(chunk));
|
|
33
|
-
req.on("end", () => {
|
|
34
|
-
const raw = Buffer.concat(chunks).toString("utf8") || "{}";
|
|
35
|
-
try {
|
|
36
|
-
resolve(JSON.parse(raw));
|
|
37
|
-
}
|
|
38
|
-
catch (err) {
|
|
39
|
-
reject(err);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
req.on("error", reject);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Spawn a one-shot HTTP server on a random localhost port. The CLI passes its
|
|
47
|
-
* URL + state to the backend with the magic-link request; the verify page in
|
|
48
|
-
* the browser POSTs `{jwt, state, user}` here after a successful sign-in.
|
|
49
|
-
*
|
|
50
|
-
* Async because server.address() returns null until the `listening` event
|
|
51
|
-
* fires — reading the port synchronously was a real bug (returned undefined
|
|
52
|
-
* → "Cannot read properties of null (reading 'port')" later).
|
|
53
|
-
*/
|
|
54
|
-
export async function startLocalCallback(timeoutMs = 5 * 60 * 1000) {
|
|
55
|
-
const state = randomBytes(16).toString("hex");
|
|
56
|
-
let resolveResult = null;
|
|
57
|
-
let rejectResult = null;
|
|
58
|
-
const resultPromise = new Promise((resolve, reject) => {
|
|
59
|
-
resolveResult = resolve;
|
|
60
|
-
rejectResult = reject;
|
|
61
|
-
});
|
|
62
|
-
const server = createServer((req, res) => {
|
|
63
|
-
if (req.method === "OPTIONS") {
|
|
64
|
-
setCors(res);
|
|
65
|
-
res.writeHead(204);
|
|
66
|
-
res.end();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (req.method !== "POST" || !req.url?.startsWith("/auth")) {
|
|
70
|
-
res.writeHead(404, { "content-type": "text/plain" });
|
|
71
|
-
res.end("not found");
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
setCors(res);
|
|
75
|
-
readJson(req)
|
|
76
|
-
.then((payload) => {
|
|
77
|
-
if (!payload || typeof payload !== "object") {
|
|
78
|
-
res.writeHead(400, { "content-type": "text/html" });
|
|
79
|
-
res.end(FAILURE_HTML);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const body = payload;
|
|
83
|
-
if (body.state !== state) {
|
|
84
|
-
res.writeHead(400, { "content-type": "text/html" });
|
|
85
|
-
res.end(FAILURE_HTML);
|
|
86
|
-
rejectResult?.(new Error("CSRF state mismatch"));
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (typeof body.jwt !== "string" || !body.jwt) {
|
|
90
|
-
res.writeHead(400, { "content-type": "text/html" });
|
|
91
|
-
res.end(FAILURE_HTML);
|
|
92
|
-
rejectResult?.(new Error("Missing jwt in callback"));
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
res.writeHead(200, { "content-type": "text/html" });
|
|
96
|
-
res.end(SUCCESS_HTML);
|
|
97
|
-
resolveResult?.({
|
|
98
|
-
jwt: body.jwt,
|
|
99
|
-
user: body.user
|
|
100
|
-
});
|
|
101
|
-
})
|
|
102
|
-
.catch((err) => {
|
|
103
|
-
res.writeHead(400, { "content-type": "text/html" });
|
|
104
|
-
res.end(FAILURE_HTML);
|
|
105
|
-
rejectResult?.(err instanceof Error ? err : new Error(String(err)));
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
await new Promise((resolve, reject) => {
|
|
109
|
-
server.once("error", reject);
|
|
110
|
-
server.listen(0, "127.0.0.1", () => {
|
|
111
|
-
server.removeListener("error", reject);
|
|
112
|
-
resolve();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
const addr = server.address();
|
|
116
|
-
if (!addr || typeof addr === "string") {
|
|
117
|
-
server.close();
|
|
118
|
-
throw new Error("Local callback server failed to bind a port");
|
|
119
|
-
}
|
|
120
|
-
const port = addr.port;
|
|
121
|
-
const callbackUrl = `http://127.0.0.1:${port}/auth`;
|
|
122
|
-
const timer = setTimeout(() => {
|
|
123
|
-
rejectResult?.(new Error("Timed out waiting for browser callback"));
|
|
124
|
-
server.close();
|
|
125
|
-
}, timeoutMs);
|
|
126
|
-
const close = () => {
|
|
127
|
-
clearTimeout(timer);
|
|
128
|
-
server.close();
|
|
129
|
-
};
|
|
130
|
-
const wait = async () => {
|
|
131
|
-
try {
|
|
132
|
-
const result = await resultPromise;
|
|
133
|
-
return result;
|
|
134
|
-
}
|
|
135
|
-
finally {
|
|
136
|
-
close();
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
return { port, callbackUrl, state, wait, close };
|
|
140
|
-
}
|
|
141
|
-
//# sourceMappingURL=local-server.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"local-server.js","sourceRoot":"","sources":["../../src/lib/local-server.ts"],"names":[],"mappings":"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"}
|