vibe-roast 0.98.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 +418 -0
- package/assests/README.md +43 -0
- package/assests/badges/.gitkeep +0 -0
- package/assests/badges/01-builder/builder-badge.png +0 -0
- package/assests/badges/01-builder/builder-badge.svg +4 -0
- package/assests/badges/02-debugger/debugger-badge.png +0 -0
- package/assests/badges/02-debugger/debugger-badge.svg +4 -0
- package/assests/badges/03-architect/architect-badge.png +0 -0
- package/assests/badges/03-architect/architect-badge.svg +4 -0
- package/assests/badges/04-codex-believer/codex-believer-badge.png +0 -0
- package/assests/badges/04-codex-believer/codex-believer-badge.svg +4 -0
- package/assests/badges/05-prompt-priest/prompt-priest-badge.png +0 -0
- package/assests/badges/05-prompt-priest/prompt-priest-badge.svg +4 -0
- package/assests/badges/06-tab-hoarder/tab-hoarder-badge.png +0 -0
- package/assests/badges/06-tab-hoarder/tab-hoarder-badge.svg +4 -0
- package/assests/badges/07-context-maxxer/context-maxxer-badge.png +0 -0
- package/assests/badges/07-context-maxxer/context-maxxer-badge.svg +4 -0
- package/assests/badges/08-yolo-shipper/yolo-shipper-badge.png +0 -0
- package/assests/badges/08-yolo-shipper/yolo-shipper-badge.svg +4 -0
- package/assests/banners/.gitkeep +0 -0
- package/assests/banners/home-hero.png +0 -0
- package/assests/banners/home-hero.svg +3 -0
- package/assests/banners/result-header.png +0 -0
- package/assests/banners/result-header.svg +3 -0
- package/assests/banners/share-strip.png +0 -0
- package/assests/banners/share-strip.svg +3 -0
- package/assests/characters/01-builder/builder-card.sbti-archive.svg +5 -0
- package/assests/characters/01-builder/builder-card.svg +3 -0
- package/assests/characters/01-builder/builder-character.sbti-archive.svg +3 -0
- package/assests/characters/01-builder/builder-character.svg +3 -0
- package/assests/characters/01-builder/builder-figure.png +0 -0
- package/assests/characters/01-builder/builder-mascot.png +0 -0
- package/assests/characters/02-debugger/debugger-card.sbti-archive.svg +5 -0
- package/assests/characters/02-debugger/debugger-card.svg +3 -0
- package/assests/characters/02-debugger/debugger-character.sbti-archive.svg +3 -0
- package/assests/characters/02-debugger/debugger-character.svg +3 -0
- package/assests/characters/02-debugger/debugger-figure.png +0 -0
- package/assests/characters/02-debugger/debugger-mascot.png +0 -0
- package/assests/characters/03-architect/architect-card.sbti-archive.svg +5 -0
- package/assests/characters/03-architect/architect-card.svg +3 -0
- package/assests/characters/03-architect/architect-character.sbti-archive.svg +3 -0
- package/assests/characters/03-architect/architect-character.svg +3 -0
- package/assests/characters/03-architect/architect-figure.png +0 -0
- package/assests/characters/03-architect/architect-mascot.png +0 -0
- package/assests/characters/04-codex-believer/codex-believer-card.sbti-archive.svg +5 -0
- package/assests/characters/04-codex-believer/codex-believer-card.svg +3 -0
- package/assests/characters/04-codex-believer/codex-believer-character.sbti-archive.svg +3 -0
- package/assests/characters/04-codex-believer/codex-believer-character.svg +3 -0
- package/assests/characters/04-codex-believer/codex-believer-figure.png +0 -0
- package/assests/characters/04-codex-believer/codex-believer-mascot.png +0 -0
- package/assests/characters/05-prompt-priest/prompt-priest-card.sbti-archive.svg +5 -0
- package/assests/characters/05-prompt-priest/prompt-priest-card.svg +3 -0
- package/assests/characters/05-prompt-priest/prompt-priest-character.sbti-archive.svg +3 -0
- package/assests/characters/05-prompt-priest/prompt-priest-character.svg +3 -0
- package/assests/characters/05-prompt-priest/prompt-priest-figure.png +0 -0
- package/assests/characters/05-prompt-priest/prompt-priest-mascot.png +0 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-card.sbti-archive.svg +5 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-card.svg +3 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-character.sbti-archive.svg +3 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-character.svg +3 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-figure.png +0 -0
- package/assests/characters/06-tab-hoarder/tab-hoarder-mascot.png +0 -0
- package/assests/characters/07-context-maxxer/context-maxxer-card.sbti-archive.svg +5 -0
- package/assests/characters/07-context-maxxer/context-maxxer-card.svg +3 -0
- package/assests/characters/07-context-maxxer/context-maxxer-character.sbti-archive.svg +3 -0
- package/assests/characters/07-context-maxxer/context-maxxer-character.svg +3 -0
- package/assests/characters/07-context-maxxer/context-maxxer-figure.png +0 -0
- package/assests/characters/07-context-maxxer/context-maxxer-mascot.png +0 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-card.sbti-archive.svg +5 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-card.svg +3 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-character.sbti-archive.svg +3 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-character.svg +3 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-figure.png +0 -0
- package/assests/characters/08-yolo-shipper/yolo-shipper-mascot.png +0 -0
- package/assests/characters-vibe-types/aosf-strategy-shipper/strategy-shipper-figure.png +0 -0
- package/assests/characters-vibe-types/aosx-agent-believer/agent-believer-figure.png +0 -0
- package/assests/characters-vibe-types/aovf-systems-architect/systems-architect-figure.png +0 -0
- package/assests/characters-vibe-types/aovx-context-cartographer/context-cartographer-figure.png +0 -0
- package/assests/characters-vibe-types/apsf-diagram-sprinter/diagram-sprinter-figure.png +0 -0
- package/assests/characters-vibe-types/apsx-infinite-planner/infinite-planner-figure.png +0 -0
- package/assests/characters-vibe-types/apvf-architect/architect-figure.png +0 -0
- package/assests/characters-vibe-types/apvx-prompt-priest/prompt-priest-figure.png +0 -0
- package/assests/characters-vibe-types/mosf-yolo-shipper/yolo-shipper-figure.png +0 -0
- package/assests/characters-vibe-types/mosx-agent-commander/agent-commander-figure.png +0 -0
- package/assests/characters-vibe-types/movf-agent-engineer/agent-engineer-figure.png +0 -0
- package/assests/characters-vibe-types/movx-systems-wrangler/systems-wrangler-figure.png +0 -0
- package/assests/characters-vibe-types/mpsf-builder/builder-figure.png +0 -0
- package/assests/characters-vibe-types/mpsx-tab-hoarder/tab-hoarder-figure.png +0 -0
- package/assests/characters-vibe-types/mpvf-debugger/debugger-figure.png +0 -0
- package/assests/characters-vibe-types/mpvx-context-maxxer/context-maxxer-figure.png +0 -0
- package/assests/live-report.html +1403 -0
- package/assests/notes/live-inspect-summary.json +3487 -0
- package/assests/scripts/compose-banners.py +148 -0
- package/assests/scripts/live-report-boot.js +2 -0
- package/assests/scripts/profiles.json +104 -0
- package/assests/scripts/qa-faces.mjs +72 -0
- package/assests/scripts/roast-extras.js +619 -0
- package/assests/scripts/score-engine.js +62 -0
- package/assests/scripts/split-badges.py +49 -0
- package/assests/scripts/split-reference.py +38 -0
- package/assests/scripts/sync-banners.mjs +45 -0
- package/assests/scripts/sync-cards.mjs +50 -0
- package/assests/scripts/sync-raster.mjs +118 -0
- package/assests/scripts/vibe-types.json +22 -0
- package/assests/source/design-system.md +50 -0
- package/assests/source/prompts/01-builder.md +19 -0
- package/assests/source/prompts/02-debugger.md +17 -0
- package/assests/source/prompts/03-architect.md +16 -0
- package/assests/source/prompts/04-codex-believer.md +13 -0
- package/assests/source/prompts/05-prompt-priest.md +13 -0
- package/assests/source/prompts/06-tab-hoarder.md +13 -0
- package/assests/source/prompts/07-context-maxxer.md +13 -0
- package/assests/source/prompts/08-yolo-shipper.md +13 -0
- package/assests/source/references/sbti-style-reference-board.png +0 -0
- package/assests/source/references/vibe-badges-v6-board.png +0 -0
- package/assests/source/references/vibe-mascots-v4-reference.png +0 -0
- package/assests/source/references/vibe-profiles-v5-board-a.png +0 -0
- package/assests/source/references/vibe-profiles-v5-board-b.png +0 -0
- package/assests/source/vibe-types-visual-brief.md +38 -0
- package/bin/hook.js +121 -0
- package/bin/vibe-roast.js +98 -0
- package/bin/vibe-wrapper.js +4 -0
- package/dashboard/dist/assets/index-DlBycTcO.css +1 -0
- package/dashboard/dist/assets/index-t8m64Hm9.js +168 -0
- package/dashboard/dist/index.html +32 -0
- package/package.json +58 -0
- package/src/extract/environment.js +160 -0
- package/src/extract/phrase-stats.js +684 -0
- package/src/extract/prompt-analysis.js +368 -0
- package/src/extract/text.js +30 -0
- package/src/hooks/install.js +184 -0
- package/src/inspect.js +261 -0
- package/src/lib/activity-metrics.js +238 -0
- package/src/lib/agent-score.js +187 -0
- package/src/lib/ai-roast.js +446 -0
- package/src/lib/dates.js +43 -0
- package/src/lib/hashtags.js +150 -0
- package/src/lib/jsonl.js +48 -0
- package/src/lib/load-env.js +59 -0
- package/src/lib/roast-evidence.js +186 -0
- package/src/lib/roast-snapshot.js +58 -0
- package/src/server.js +644 -0
- package/src/sources/aider.js +150 -0
- package/src/sources/amazonq.js +113 -0
- package/src/sources/antigravity.js +147 -0
- package/src/sources/claude.js +262 -0
- package/src/sources/cline.js +11 -0
- package/src/sources/codex.js +222 -0
- package/src/sources/common.js +133 -0
- package/src/sources/continue.js +67 -0
- package/src/sources/copilot.js +170 -0
- package/src/sources/cursor.js +238 -0
- package/src/sources/gemini.js +75 -0
- package/src/sources/index.js +61 -0
- package/src/sources/roo.js +11 -0
- package/src/sources/token-tracker.js +171 -0
- package/src/sources/vibe-tracker.js +79 -0
- package/src/sources/vscode-tasks.js +142 -0
- package/src/sources/windsurf.js +109 -0
- package/worker/README.md +96 -0
- package/worker/package-lock.json +1547 -0
- package/worker/package.json +20 -0
- package/worker/src/index.mjs +694 -0
- package/worker/wrangler.jsonc +40 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const path = require("node:path");
|
|
2
|
+
|
|
3
|
+
const { walkFiles } = require("../lib/jsonl");
|
|
4
|
+
const { isInRange } = require("../lib/dates");
|
|
5
|
+
const {
|
|
6
|
+
emptyTotals,
|
|
7
|
+
emptyReport,
|
|
8
|
+
addTotals,
|
|
9
|
+
usageFromObject,
|
|
10
|
+
cleanText,
|
|
11
|
+
toIsoTimestamp,
|
|
12
|
+
readJsonFile,
|
|
13
|
+
resolveVscodeGlobalStorage,
|
|
14
|
+
} = require("./common");
|
|
15
|
+
|
|
16
|
+
async function inspectVscodeTaskStore({
|
|
17
|
+
source,
|
|
18
|
+
extensionId,
|
|
19
|
+
root,
|
|
20
|
+
range,
|
|
21
|
+
home,
|
|
22
|
+
platform,
|
|
23
|
+
env,
|
|
24
|
+
appNames = ["Code", "Code - Insiders", "Cursor", "Windsurf"],
|
|
25
|
+
} = {}) {
|
|
26
|
+
const roots = root
|
|
27
|
+
? [root]
|
|
28
|
+
: appNames.map((appName) =>
|
|
29
|
+
resolveVscodeGlobalStorage({ home, platform, env, appName, parts: [extensionId, "tasks"] }),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const prompts = [];
|
|
33
|
+
const tokenTotals = emptyTotals();
|
|
34
|
+
let filesScanned = 0;
|
|
35
|
+
const notes = [];
|
|
36
|
+
let usedRoot = roots[0] || null;
|
|
37
|
+
|
|
38
|
+
for (const tasksRoot of roots) {
|
|
39
|
+
const files = await walkFiles(
|
|
40
|
+
tasksRoot,
|
|
41
|
+
(_filePath, name) =>
|
|
42
|
+
name === "ui_messages.json" ||
|
|
43
|
+
name === "api_conversation_history.json" ||
|
|
44
|
+
name.endsWith(".json"),
|
|
45
|
+
);
|
|
46
|
+
if (!files.length) continue;
|
|
47
|
+
usedRoot = tasksRoot;
|
|
48
|
+
for (const file of files) {
|
|
49
|
+
const base = path.basename(file);
|
|
50
|
+
if (
|
|
51
|
+
base !== "ui_messages.json" &&
|
|
52
|
+
base !== "api_conversation_history.json" &&
|
|
53
|
+
!/messages?/i.test(base)
|
|
54
|
+
) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
filesScanned += 1;
|
|
58
|
+
const data = await readJsonFile(file);
|
|
59
|
+
const rows = normalizeMessageRows(data);
|
|
60
|
+
for (const row of rows) {
|
|
61
|
+
const text = extractTaskUserText(row);
|
|
62
|
+
if (!text) continue;
|
|
63
|
+
const timestamp = toIsoTimestamp(row.ts || row.timestamp || row.time || row.createdAt || row.date);
|
|
64
|
+
if (!isInRange(timestamp, range)) continue;
|
|
65
|
+
prompts.push({
|
|
66
|
+
source,
|
|
67
|
+
timestamp,
|
|
68
|
+
session_file: file,
|
|
69
|
+
text,
|
|
70
|
+
});
|
|
71
|
+
addTotals(tokenTotals, usageFromObject(row));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (filesScanned > 0) break;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!filesScanned && !root) {
|
|
78
|
+
notes.push(
|
|
79
|
+
`No ${source} task transcripts found under VS Code/Cursor globalStorage (${extensionId}/tasks).`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
...(emptyReport(source, usedRoot, notes)),
|
|
85
|
+
files_scanned: filesScanned,
|
|
86
|
+
prompt_count: prompts.length,
|
|
87
|
+
token_totals: tokenTotals,
|
|
88
|
+
prompts,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeMessageRows(data) {
|
|
93
|
+
if (Array.isArray(data)) return data;
|
|
94
|
+
if (!data || typeof data !== "object") return [];
|
|
95
|
+
if (Array.isArray(data.messages)) return data.messages;
|
|
96
|
+
if (Array.isArray(data.clineMessages)) return data.clineMessages;
|
|
97
|
+
if (Array.isArray(data.history)) return data.history;
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function extractTaskUserText(row) {
|
|
102
|
+
if (!row || typeof row !== "object") return "";
|
|
103
|
+
const type = String(row.type || row.role || row.say || row.ask || "").toLowerCase();
|
|
104
|
+
const say = String(row.say || "").toLowerCase();
|
|
105
|
+
const ask = String(row.ask || "").toLowerCase();
|
|
106
|
+
const role = String(row.role || "").toLowerCase();
|
|
107
|
+
|
|
108
|
+
const looksUser =
|
|
109
|
+
role === "user" ||
|
|
110
|
+
type === "user" ||
|
|
111
|
+
type === "human" ||
|
|
112
|
+
say === "user_feedback" ||
|
|
113
|
+
say === "text" && role !== "assistant" ||
|
|
114
|
+
ask === "followup" ||
|
|
115
|
+
ask === "command" ||
|
|
116
|
+
(type === "ask" && !/tool|error|completion/i.test(ask));
|
|
117
|
+
|
|
118
|
+
if (!looksUser) {
|
|
119
|
+
// api_conversation_history style
|
|
120
|
+
if (role !== "user" && type !== "user") return "";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const candidates = [
|
|
124
|
+
row.text,
|
|
125
|
+
row.content,
|
|
126
|
+
row.message,
|
|
127
|
+
row.userContent,
|
|
128
|
+
row.ask === "followup" ? row.text : null,
|
|
129
|
+
Array.isArray(row.content) ? row.content : null,
|
|
130
|
+
];
|
|
131
|
+
for (const candidate of candidates) {
|
|
132
|
+
const text = cleanText(candidate);
|
|
133
|
+
if (text) return text;
|
|
134
|
+
}
|
|
135
|
+
return "";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = {
|
|
139
|
+
inspectVscodeTaskStore,
|
|
140
|
+
extractTaskUserText,
|
|
141
|
+
normalizeMessageRows,
|
|
142
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const os = require("node:os");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
const { walkFiles, readJsonl } = require("../lib/jsonl");
|
|
5
|
+
const { isInRange } = require("../lib/dates");
|
|
6
|
+
const {
|
|
7
|
+
emptyTotals,
|
|
8
|
+
emptyReport,
|
|
9
|
+
addTotals,
|
|
10
|
+
usageFromObject,
|
|
11
|
+
cleanText,
|
|
12
|
+
toIsoTimestamp,
|
|
13
|
+
readJsonFile,
|
|
14
|
+
} = require("./common");
|
|
15
|
+
|
|
16
|
+
async function inspectWindsurf({ root, range, home = os.homedir() } = {}) {
|
|
17
|
+
const windsurfRoot = root || path.join(home, ".codeium", "windsurf");
|
|
18
|
+
const files = await walkFiles(
|
|
19
|
+
windsurfRoot,
|
|
20
|
+
(_filePath, name) =>
|
|
21
|
+
name.endsWith(".json") ||
|
|
22
|
+
name.endsWith(".jsonl") ||
|
|
23
|
+
name.endsWith(".md") ||
|
|
24
|
+
name.endsWith(".pb"),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const prompts = [];
|
|
28
|
+
const tokenTotals = emptyTotals();
|
|
29
|
+
let readableFiles = 0;
|
|
30
|
+
let encryptedPb = 0;
|
|
31
|
+
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
if (file.endsWith(".pb")) {
|
|
34
|
+
encryptedPb += 1;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
readableFiles += 1;
|
|
38
|
+
if (file.endsWith(".jsonl")) {
|
|
39
|
+
await readJsonl(file, (obj) => {
|
|
40
|
+
const text = extractWindsurfUserText(obj);
|
|
41
|
+
if (!text) return;
|
|
42
|
+
const timestamp = toIsoTimestamp(obj.timestamp || obj.time || obj.createdAt);
|
|
43
|
+
if (!isInRange(timestamp, range)) return;
|
|
44
|
+
prompts.push({
|
|
45
|
+
source: "windsurf",
|
|
46
|
+
timestamp,
|
|
47
|
+
session_file: file,
|
|
48
|
+
text,
|
|
49
|
+
});
|
|
50
|
+
addTotals(tokenTotals, usageFromObject(obj));
|
|
51
|
+
});
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (file.endsWith(".md")) {
|
|
56
|
+
// Exported Cascade markdown — treat lines after "## User" / "### User" as prompts.
|
|
57
|
+
// Keep lightweight: only explicit user headings.
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data = await readJsonFile(file);
|
|
62
|
+
const rows = Array.isArray(data)
|
|
63
|
+
? data
|
|
64
|
+
: Array.isArray(data?.messages)
|
|
65
|
+
? data.messages
|
|
66
|
+
: Array.isArray(data?.trajectory)
|
|
67
|
+
? data.trajectory
|
|
68
|
+
: Array.isArray(data?.steps)
|
|
69
|
+
? data.steps
|
|
70
|
+
: [];
|
|
71
|
+
for (const row of rows) {
|
|
72
|
+
const text = extractWindsurfUserText(row);
|
|
73
|
+
if (!text) continue;
|
|
74
|
+
const timestamp = toIsoTimestamp(row.timestamp || row.time || row.createdAt || data?.createdAt);
|
|
75
|
+
if (!isInRange(timestamp, range)) continue;
|
|
76
|
+
prompts.push({
|
|
77
|
+
source: "windsurf",
|
|
78
|
+
timestamp,
|
|
79
|
+
session_file: file,
|
|
80
|
+
text,
|
|
81
|
+
});
|
|
82
|
+
addTotals(tokenTotals, usageFromObject(row));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const notes = [];
|
|
87
|
+
if (encryptedPb > 0 && prompts.length === 0) {
|
|
88
|
+
notes.push(
|
|
89
|
+
`Found ${encryptedPb} encrypted Cascade .pb trajectory file(s); plaintext JSON/JSONL exports are required for prompt extraction.`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
...(emptyReport("windsurf", windsurfRoot, notes)),
|
|
95
|
+
files_scanned: readableFiles + encryptedPb,
|
|
96
|
+
prompt_count: prompts.length,
|
|
97
|
+
token_totals: tokenTotals,
|
|
98
|
+
prompts,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function extractWindsurfUserText(row) {
|
|
103
|
+
if (!row || typeof row !== "object") return "";
|
|
104
|
+
const role = String(row.role || row.type || row.author || row.kind || "").toLowerCase();
|
|
105
|
+
if (role && !/(user|human|prompt|input)/.test(role)) return "";
|
|
106
|
+
return cleanText(row.text ?? row.content ?? row.message ?? row.prompt ?? "");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = { inspectWindsurf, extractWindsurfUserText };
|
package/worker/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Vibe Roast Auth · Cloudflare Worker
|
|
2
|
+
|
|
3
|
+
This Worker is the production OAuth broker and hosted free-roast gateway for
|
|
4
|
+
the public npm client. It keeps GitHub secrets off user machines, stores OAuth
|
|
5
|
+
flows and one-time tickets in a SQLite-backed Durable Object, and invokes
|
|
6
|
+
Cloudflare Workers AI through a server-only binding.
|
|
7
|
+
|
|
8
|
+
## Configure
|
|
9
|
+
|
|
10
|
+
Authenticate Wrangler:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm run worker:login
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Set the two server-only secrets:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm run worker:secret:github
|
|
20
|
+
npm run worker:secret:session
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `GITHUB_CLIENT_SECRET`: the GitHub App client secret.
|
|
24
|
+
- `AUTH_BROKER_SESSION_SECRET`: at least 32 random bytes; for example,
|
|
25
|
+
`openssl rand -base64 48`.
|
|
26
|
+
|
|
27
|
+
The public GitHub App client ID lives in `wrangler.jsonc`. Override it there if
|
|
28
|
+
the app registration changes.
|
|
29
|
+
|
|
30
|
+
## Deploy
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm run worker:deploy
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Wrangler binds the Worker to the configured Cloudflare Custom Domain:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
https://auth.pinktalk.online
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Configure the GitHub App callback with this exact URL:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
https://auth.pinktalk.online/oauth/github/callback
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The public npm app uses `https://auth.pinktalk.online` automatically. Users do
|
|
49
|
+
not need to configure `VIBE_WRAPPER_AUTH_BROKER_URL`; that variable is only an
|
|
50
|
+
override for local development or a self-hosted broker.
|
|
51
|
+
|
|
52
|
+
`AUTH_BROKER_PUBLIC_URL` pins OAuth redirects to the Custom Domain even when
|
|
53
|
+
the `workers.dev` preview URL is also enabled. It is not confidential.
|
|
54
|
+
|
|
55
|
+
## Hosted free AI
|
|
56
|
+
|
|
57
|
+
`POST /v1/chat/completions` is an OpenAI-compatible internal endpoint used by
|
|
58
|
+
the localhost npm server. It requires the signed broker session returned after
|
|
59
|
+
GitHub OAuth; it is not an anonymous public inference endpoint.
|
|
60
|
+
|
|
61
|
+
- Model: `@cf/qwen/qwen3-30b-a3b-fp8`
|
|
62
|
+
- Default quota: 9 model calls per GitHub account per UTC day
|
|
63
|
+
- Configuration: `FREE_AI_DAILY_CALLS` in `wrangler.jsonc`
|
|
64
|
+
- Limits: at most 8 messages, 50,000 characters of prompt text, and 1,800
|
|
65
|
+
output tokens per call
|
|
66
|
+
|
|
67
|
+
The local roast writer spends one model call per roast. It validates the
|
|
68
|
+
bilingual JSON/schema but does not run a post-generation grounding/style audit,
|
|
69
|
+
editor pass, or repair call. Raw prompts and local paths are never sent; only
|
|
70
|
+
the aggregate `roast_evidence` packet is used.
|
|
71
|
+
|
|
72
|
+
The localhost client also sends a compact behavioral snapshot header. The
|
|
73
|
+
Durable Object binds the generated roast to the authenticated GitHub identity
|
|
74
|
+
and reuses it without consuming quota while the profile remains materially
|
|
75
|
+
equivalent. Token growth and minor score movement do not invalidate the cache;
|
|
76
|
+
type changes, large axis/radar/category shifts, a substantially different
|
|
77
|
+
concept set, or a changed top Agent do. The cache contains aggregate snapshot
|
|
78
|
+
fields and generated copy, never raw prompts.
|
|
79
|
+
|
|
80
|
+
## Local development
|
|
81
|
+
|
|
82
|
+
Create an ignored `worker/.dev.vars`:
|
|
83
|
+
|
|
84
|
+
```dotenv
|
|
85
|
+
GITHUB_CLIENT_SECRET=...
|
|
86
|
+
AUTH_BROKER_SESSION_SECRET=...
|
|
87
|
+
AUTH_BROKER_PUBLIC_URL=http://localhost:8787
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then run:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm run worker:dev
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The GitHub App must include the matching local callback URL while testing.
|