@grindxp/cli 0.1.7 → 0.1.9
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/index.js +766 -134
- package/dist/web/client/assets/Copy.es-Bs4NgJu-.js +1 -0
- package/dist/web/client/assets/Sword.es-2Xm7T3t2.js +1 -0
- package/dist/web/client/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/dist/web/client/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/dist/web/client/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/dist/web/client/assets/index-6XDcqRbL.js +42 -0
- package/dist/web/client/assets/index-BXM1N6tm.js +1 -0
- package/dist/web/client/assets/index-B_KMiE38.js +1 -0
- package/dist/web/client/assets/index-CGj2rOLm.js +1 -0
- package/dist/web/client/assets/index-CS5BuFbt.js +1 -0
- package/dist/web/client/assets/index-CYsASiu-.js +1 -0
- package/dist/web/client/assets/index-DAvwM0SX.js +1 -0
- package/dist/web/client/assets/index-DCBFp5DJ.js +1 -0
- package/dist/web/client/assets/index-DjKt1qNz.js +1 -0
- package/dist/web/client/assets/index-PIcFs1vr.js +1 -0
- package/dist/web/client/assets/instrument-serif-latin-400-italic-DKMiL14s.woff2 +0 -0
- package/dist/web/client/assets/instrument-serif-latin-400-italic-u__WvvIK.woff +0 -0
- package/dist/web/client/assets/instrument-serif-latin-400-normal-BVbkICAY.woff +0 -0
- package/dist/web/client/assets/instrument-serif-latin-400-normal-DnYpCC2O.woff2 +0 -0
- package/dist/web/client/assets/instrument-serif-latin-ext-400-italic-C9HzH3YL.woff2 +0 -0
- package/dist/web/client/assets/instrument-serif-latin-ext-400-italic-D7-lnxEk.woff +0 -0
- package/dist/web/client/assets/instrument-serif-latin-ext-400-normal-C2je3j2s.woff2 +0 -0
- package/dist/web/client/assets/instrument-serif-latin-ext-400-normal-CFCUzsTy.woff +0 -0
- package/dist/web/client/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/web/client/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/web/client/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/web/client/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/web/client/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/web/client/assets/main-BI1EOhmt.js +18 -0
- package/dist/web/client/assets/styles-7TpWqjrh.css +1 -0
- package/dist/web/client/favicon.ico +0 -0
- package/dist/web/server/assets/_tanstack-start-manifest_v-B_rvI8DG.js +4 -0
- package/dist/web/server/assets/agent.functions-BL3upUNr.js +19541 -0
- package/dist/web/server/assets/data.functions-DZmdFOMQ.js +285 -0
- package/dist/web/server/assets/index-4SxmUYH6.js +14 -0
- package/dist/web/server/assets/index-B2ULpkv2.js +4587 -0
- package/dist/web/server/assets/index-BGBMycx-.js +2275 -0
- package/dist/web/server/assets/index-BL8u2X7w.js +14 -0
- package/dist/web/server/assets/index-BQUCDamI.js +5924 -0
- package/dist/web/server/assets/index-BRRsXrOi.js +14 -0
- package/dist/web/server/assets/index-BiD7uOOh.js +14 -0
- package/dist/web/server/assets/index-CB8UtTN8.js +66 -0
- package/dist/web/server/assets/index-D2yaimYL.js +14 -0
- package/dist/web/server/assets/index-D3RUqTdb.js +14 -0
- package/dist/web/server/assets/index-DTB2dYCz.js +1426 -0
- package/dist/web/server/assets/index-DfU25rnD.js +477 -0
- package/dist/web/server/assets/index-SHH7zSKt.js +66 -0
- package/dist/web/server/assets/router-CXyGzWDS.js +589 -0
- package/dist/web/server/assets/sessions-UCWtijHE.js +438 -0
- package/dist/web/server/assets/start-HYkvq4Ni.js +4 -0
- package/dist/web/server/assets/token-DGoahKjI.js +86 -0
- package/dist/web/server/assets/token-util-BopJPy-I.js +451 -0
- package/dist/web/server/assets/token-util-Bw35afYM.js +30 -0
- package/dist/web/server/assets/vault.server-CscY5Z8e.js +19357 -0
- package/dist/web/server/server.js +4889 -0
- package/package.json +53 -51
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { T as TSS_SERVER_FUNCTION } from "../server.js";
|
|
2
|
+
import { e as eq, S as companionSettings, T as asc, U as companionInsights, l as sql, d as desc, b as and, N as skills, V as skillSchema, W as skillCategorySchema, X as skillLevelFromXp, Y as inArray, M as quests, _ as questSchema, P as gte, Q as questLogs, $ as completeQuestInputSchema, a0 as calculateQuestXp, B as questDifficultySchema, a1 as proofs, L as users, a2 as levelFromXp, a3 as createQuestInputSchema, a4 as conversations, a5 as promptHistory, a6 as messages } from "./vault.server-CscY5Z8e.js";
|
|
3
|
+
const createServerRpc = (serverFnMeta, splitImportFn) => {
|
|
4
|
+
const url = "/_serverFn/" + serverFnMeta.id;
|
|
5
|
+
return Object.assign(splitImportFn, {
|
|
6
|
+
url,
|
|
7
|
+
serverFnMeta,
|
|
8
|
+
[TSS_SERVER_FUNCTION]: true
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
async function getCompanionByUserId(db, userId) {
|
|
12
|
+
const row = await db.query.companionSettings.findFirst({
|
|
13
|
+
where: eq(companionSettings.userId, userId)
|
|
14
|
+
});
|
|
15
|
+
return row ?? null;
|
|
16
|
+
}
|
|
17
|
+
async function updateCompanionUserContext(db, userId, userContext) {
|
|
18
|
+
const [updated] = await db.update(companionSettings).set({ userContext, updatedAt: Date.now() }).where(eq(companionSettings.userId, userId)).returning();
|
|
19
|
+
if (!updated) throw new Error("Companion not found. Run `grindxp init` first.");
|
|
20
|
+
return updated;
|
|
21
|
+
}
|
|
22
|
+
async function listCompanionInsights(db, userId, limit = 20) {
|
|
23
|
+
return db.query.companionInsights.findMany({
|
|
24
|
+
where: eq(companionInsights.userId, userId),
|
|
25
|
+
orderBy: [
|
|
26
|
+
asc(sql`case when ${companionInsights.source} = 'user-stated' then 0 else 1 end`),
|
|
27
|
+
desc(companionInsights.confidence),
|
|
28
|
+
desc(companionInsights.updatedAt)
|
|
29
|
+
],
|
|
30
|
+
limit
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async function createCompanionInsight(db, values) {
|
|
34
|
+
const [row] = await db.insert(companionInsights).values(values).returning();
|
|
35
|
+
if (!row) throw new Error("Failed to insert companion insight");
|
|
36
|
+
return row;
|
|
37
|
+
}
|
|
38
|
+
async function updateCompanionInsight(db, insightId, userId, values) {
|
|
39
|
+
const [row] = await db.update(companionInsights).set({
|
|
40
|
+
...values.category !== void 0 ? { category: values.category } : {},
|
|
41
|
+
...values.content !== void 0 ? { content: values.content } : {},
|
|
42
|
+
...values.confidence !== void 0 ? { confidence: values.confidence } : {},
|
|
43
|
+
...values.source !== void 0 ? { source: values.source } : {},
|
|
44
|
+
updatedAt: Date.now()
|
|
45
|
+
}).where(and(eq(companionInsights.id, insightId), eq(companionInsights.userId, userId))).returning();
|
|
46
|
+
if (!row) throw new Error("Companion insight not found");
|
|
47
|
+
return row;
|
|
48
|
+
}
|
|
49
|
+
async function findCompanionInsightByContent(db, userId, category, content) {
|
|
50
|
+
const row = await db.query.companionInsights.findFirst({
|
|
51
|
+
where: and(
|
|
52
|
+
eq(companionInsights.userId, userId),
|
|
53
|
+
eq(companionInsights.category, category),
|
|
54
|
+
sql`lower(trim(${companionInsights.content})) = lower(trim(${content}))`
|
|
55
|
+
)
|
|
56
|
+
});
|
|
57
|
+
return row ?? null;
|
|
58
|
+
}
|
|
59
|
+
async function updateCompanionMode(db, userId, mode) {
|
|
60
|
+
const [updated] = await db.update(companionSettings).set({ mode, updatedAt: Date.now() }).where(eq(companionSettings.userId, userId)).returning();
|
|
61
|
+
if (!updated) throw new Error("Companion not found. Run `grindxp init` first.");
|
|
62
|
+
return updated;
|
|
63
|
+
}
|
|
64
|
+
async function deleteCompanionInsight(db, insightId, userId) {
|
|
65
|
+
const row = await db.delete(companionInsights).where(and(eq(companionInsights.id, insightId), eq(companionInsights.userId, userId))).returning({ id: companionInsights.id });
|
|
66
|
+
return row.length > 0;
|
|
67
|
+
}
|
|
68
|
+
function rowToSkill(row) {
|
|
69
|
+
return skillSchema.parse({
|
|
70
|
+
id: row.id,
|
|
71
|
+
userId: row.userId,
|
|
72
|
+
name: row.name,
|
|
73
|
+
category: row.category,
|
|
74
|
+
parentId: row.parentId ?? void 0,
|
|
75
|
+
xp: row.xp,
|
|
76
|
+
level: row.level,
|
|
77
|
+
metadata: row.metadata,
|
|
78
|
+
createdAt: row.createdAt,
|
|
79
|
+
updatedAt: row.updatedAt
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async function createSkill(db, input) {
|
|
83
|
+
const [row] = await db.insert(skills).values({
|
|
84
|
+
userId: input.userId,
|
|
85
|
+
name: input.name,
|
|
86
|
+
category: input.category,
|
|
87
|
+
metadata: {}
|
|
88
|
+
}).returning();
|
|
89
|
+
if (!row) throw new Error("Failed to insert skill");
|
|
90
|
+
return rowToSkill(row);
|
|
91
|
+
}
|
|
92
|
+
async function getSkillByName(db, userId, name) {
|
|
93
|
+
const row = await db.query.skills.findFirst({
|
|
94
|
+
where: and(eq(skills.userId, userId), eq(skills.name, name))
|
|
95
|
+
});
|
|
96
|
+
if (!row) return null;
|
|
97
|
+
return rowToSkill(row);
|
|
98
|
+
}
|
|
99
|
+
async function listSkillsByUser(db, userId) {
|
|
100
|
+
const rows = await db.query.skills.findMany({
|
|
101
|
+
where: eq(skills.userId, userId),
|
|
102
|
+
orderBy: [desc(skills.xp)]
|
|
103
|
+
});
|
|
104
|
+
return rows.map(rowToSkill);
|
|
105
|
+
}
|
|
106
|
+
async function addXpToSkill(db, skillId, deltaXp) {
|
|
107
|
+
const row = await db.query.skills.findFirst({ where: eq(skills.id, skillId) });
|
|
108
|
+
if (!row) throw new Error("Skill not found");
|
|
109
|
+
const newXp = Math.max(0, row.xp + deltaXp);
|
|
110
|
+
const newLevel = skillLevelFromXp(newXp);
|
|
111
|
+
const [updated] = await db.update(skills).set({ xp: newXp, level: newLevel }).where(eq(skills.id, skillId)).returning();
|
|
112
|
+
if (!updated) throw new Error("Failed to update skill XP");
|
|
113
|
+
return rowToSkill(updated);
|
|
114
|
+
}
|
|
115
|
+
function parseSkillTag(tag) {
|
|
116
|
+
const defaultCategory = "life";
|
|
117
|
+
if (tag.includes(":")) {
|
|
118
|
+
const [rawCategory, ...rest] = tag.split(":");
|
|
119
|
+
const name = rest.join(":");
|
|
120
|
+
const parsed = skillCategorySchema.safeParse(rawCategory);
|
|
121
|
+
return {
|
|
122
|
+
name: name || tag,
|
|
123
|
+
category: parsed.success ? parsed.data : defaultCategory
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { name: tag, category: defaultCategory };
|
|
127
|
+
}
|
|
128
|
+
async function upsertSkillByTag(db, userId, tag) {
|
|
129
|
+
const { name, category } = parseSkillTag(tag);
|
|
130
|
+
const existing = await getSkillByName(db, userId, name);
|
|
131
|
+
if (existing) return existing;
|
|
132
|
+
return createSkill(db, { userId, name, category });
|
|
133
|
+
}
|
|
134
|
+
async function distributeSkillXp(db, userId, skillTags, totalXp) {
|
|
135
|
+
if (skillTags.length === 0 || totalXp <= 0) return [];
|
|
136
|
+
const xpPerTag = computeXpDistribution(skillTags.length, totalXp);
|
|
137
|
+
const gains = [];
|
|
138
|
+
for (let i = 0; i < skillTags.length; i++) {
|
|
139
|
+
const tag = skillTags[i];
|
|
140
|
+
const xp = xpPerTag[i];
|
|
141
|
+
if (tag === void 0 || xp === void 0) continue;
|
|
142
|
+
const skill = await upsertSkillByTag(db, userId, tag);
|
|
143
|
+
const xpBefore = skill.xp;
|
|
144
|
+
const levelBefore = skill.level;
|
|
145
|
+
const updated = await addXpToSkill(db, skill.id, xp);
|
|
146
|
+
gains.push({
|
|
147
|
+
skillId: updated.id,
|
|
148
|
+
name: updated.name,
|
|
149
|
+
category: updated.category,
|
|
150
|
+
xpBefore,
|
|
151
|
+
xpAfter: updated.xp,
|
|
152
|
+
levelBefore,
|
|
153
|
+
levelAfter: updated.level,
|
|
154
|
+
xpGained: xp,
|
|
155
|
+
leveledUp: updated.level > levelBefore
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return gains;
|
|
159
|
+
}
|
|
160
|
+
function computeXpDistribution(tagCount, totalXp) {
|
|
161
|
+
if (tagCount === 1) return [totalXp];
|
|
162
|
+
const primaryXp = Math.ceil(totalXp * 0.5);
|
|
163
|
+
const remaining = totalXp - primaryXp;
|
|
164
|
+
const secondaryCount = tagCount - 1;
|
|
165
|
+
const perSecondary = Math.floor(remaining / secondaryCount);
|
|
166
|
+
const distribution = [primaryXp];
|
|
167
|
+
let distributed = primaryXp;
|
|
168
|
+
for (let i = 1; i < tagCount; i++) {
|
|
169
|
+
const xp = i === tagCount - 1 ? totalXp - distributed : perSecondary;
|
|
170
|
+
distribution.push(xp);
|
|
171
|
+
distributed += xp;
|
|
172
|
+
}
|
|
173
|
+
return distribution;
|
|
174
|
+
}
|
|
175
|
+
function rowToQuest(row) {
|
|
176
|
+
return questSchema.parse({
|
|
177
|
+
id: row.id,
|
|
178
|
+
userId: row.userId,
|
|
179
|
+
title: row.title,
|
|
180
|
+
description: row.description ?? void 0,
|
|
181
|
+
type: row.type,
|
|
182
|
+
difficulty: row.difficulty,
|
|
183
|
+
status: row.status,
|
|
184
|
+
objectives: row.objectives,
|
|
185
|
+
skillTags: row.skillTags,
|
|
186
|
+
schedule: row.scheduleCron ? { cron: row.scheduleCron, timezone: "UTC" } : void 0,
|
|
187
|
+
parentId: row.parentId ?? void 0,
|
|
188
|
+
streakCount: row.streakCount,
|
|
189
|
+
baseXp: row.baseXp,
|
|
190
|
+
metadata: row.metadata,
|
|
191
|
+
createdAt: row.createdAt,
|
|
192
|
+
updatedAt: row.updatedAt,
|
|
193
|
+
completedAt: row.completedAt ?? void 0,
|
|
194
|
+
deadlineAt: row.deadlineAt ?? void 0
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async function createQuest(db, input) {
|
|
198
|
+
const valid = createQuestInputSchema.parse(input);
|
|
199
|
+
const [row] = await db.insert(quests).values({
|
|
200
|
+
userId: valid.userId,
|
|
201
|
+
title: valid.title,
|
|
202
|
+
description: valid.description,
|
|
203
|
+
type: valid.type,
|
|
204
|
+
difficulty: valid.difficulty,
|
|
205
|
+
status: "active",
|
|
206
|
+
objectives: valid.objectives,
|
|
207
|
+
skillTags: valid.skillTags,
|
|
208
|
+
scheduleCron: valid.schedule?.cron,
|
|
209
|
+
parentId: valid.parentId,
|
|
210
|
+
streakCount: valid.streakCount ?? 0,
|
|
211
|
+
baseXp: valid.baseXp,
|
|
212
|
+
metadata: valid.metadata,
|
|
213
|
+
deadlineAt: valid.deadlineAt
|
|
214
|
+
}).returning();
|
|
215
|
+
if (!row) throw new Error("Failed to insert quest");
|
|
216
|
+
return rowToQuest(row);
|
|
217
|
+
}
|
|
218
|
+
async function getQuestById(db, questId) {
|
|
219
|
+
const row = await db.query.quests.findFirst({ where: eq(quests.id, questId) });
|
|
220
|
+
if (!row) return null;
|
|
221
|
+
return rowToQuest(row);
|
|
222
|
+
}
|
|
223
|
+
async function findQuestByPrefix(db, userId, prefix) {
|
|
224
|
+
const rows = await db.query.quests.findMany({
|
|
225
|
+
where: eq(quests.userId, userId),
|
|
226
|
+
orderBy: [desc(quests.updatedAt)]
|
|
227
|
+
});
|
|
228
|
+
const lowerPrefix = prefix.toLowerCase();
|
|
229
|
+
const match = rows.find(
|
|
230
|
+
(r) => r.id.startsWith(prefix) || r.title.toLowerCase().includes(lowerPrefix)
|
|
231
|
+
);
|
|
232
|
+
return match ? rowToQuest(match) : null;
|
|
233
|
+
}
|
|
234
|
+
async function listQuestsByUser(db, userId, statusFilter) {
|
|
235
|
+
const where = statusFilter?.length ? and(eq(quests.userId, userId), inArray(quests.status, statusFilter)) : eq(quests.userId, userId);
|
|
236
|
+
const rows = await db.query.quests.findMany({
|
|
237
|
+
where,
|
|
238
|
+
orderBy: [desc(quests.updatedAt)]
|
|
239
|
+
});
|
|
240
|
+
return rows.map(rowToQuest);
|
|
241
|
+
}
|
|
242
|
+
async function updateQuestStatus(db, questId, userId, status) {
|
|
243
|
+
const now = Date.now();
|
|
244
|
+
await db.update(quests).set({
|
|
245
|
+
status,
|
|
246
|
+
completedAt: status === "completed" ? now : null,
|
|
247
|
+
streakCount: status === "abandoned" ? 0 : void 0
|
|
248
|
+
}).where(and(eq(quests.id, questId), eq(quests.userId, userId)));
|
|
249
|
+
}
|
|
250
|
+
async function completeQuest(db, input) {
|
|
251
|
+
const { userId, ...rest } = input;
|
|
252
|
+
const validInput = completeQuestInputSchema.parse(rest);
|
|
253
|
+
const completedAt = validInput.completedAt ?? Date.now();
|
|
254
|
+
const quest = await db.query.quests.findFirst({
|
|
255
|
+
where: and(eq(quests.id, validInput.questId), eq(quests.userId, userId))
|
|
256
|
+
});
|
|
257
|
+
if (!quest) throw new Error("Quest not found");
|
|
258
|
+
if (quest.status === "completed") throw new Error("Quest already completed");
|
|
259
|
+
if (quest.status === "abandoned") throw new Error("Cannot complete an abandoned quest");
|
|
260
|
+
const xpResult = calculateQuestXp({
|
|
261
|
+
baseXp: quest.baseXp,
|
|
262
|
+
difficulty: questDifficultySchema.parse(quest.difficulty),
|
|
263
|
+
streakDays: quest.streakCount,
|
|
264
|
+
proofType: validInput.proofType
|
|
265
|
+
});
|
|
266
|
+
const skillTags = quest.skillTags;
|
|
267
|
+
let skillGains = [];
|
|
268
|
+
const proofConfidence = validInput.proofConfidence ?? (typeof validInput.proofData.confidence === "number" && validInput.proofData.confidence >= 0 && validInput.proofData.confidence <= 1 ? validInput.proofData.confidence : void 0);
|
|
269
|
+
await db.transaction(async (tx) => {
|
|
270
|
+
await tx.update(quests).set({
|
|
271
|
+
status: "completed",
|
|
272
|
+
completedAt,
|
|
273
|
+
streakCount: quest.streakCount + 1
|
|
274
|
+
}).where(eq(quests.id, validInput.questId));
|
|
275
|
+
const [questLog] = await tx.insert(questLogs).values({
|
|
276
|
+
questId: quest.id,
|
|
277
|
+
userId: quest.userId,
|
|
278
|
+
completedAt,
|
|
279
|
+
durationMinutes: validInput.durationMinutes,
|
|
280
|
+
xpEarned: xpResult.totalXp,
|
|
281
|
+
proofType: validInput.proofType,
|
|
282
|
+
proofData: validInput.proofData,
|
|
283
|
+
streakDay: quest.streakCount + 1
|
|
284
|
+
}).returning();
|
|
285
|
+
if (!questLog) {
|
|
286
|
+
throw new Error("Failed to insert quest log");
|
|
287
|
+
}
|
|
288
|
+
await tx.insert(proofs).values({
|
|
289
|
+
questLogId: questLog.id,
|
|
290
|
+
type: validInput.proofType,
|
|
291
|
+
...proofConfidence !== void 0 ? { confidence: proofConfidence } : {},
|
|
292
|
+
data: validInput.proofData
|
|
293
|
+
});
|
|
294
|
+
const user = await tx.query.users.findFirst({ where: eq(users.id, quest.userId) });
|
|
295
|
+
if (user) {
|
|
296
|
+
const newXp = user.totalXp + xpResult.totalXp;
|
|
297
|
+
await tx.update(users).set({ totalXp: newXp, level: levelFromXp(newXp) }).where(eq(users.id, quest.userId));
|
|
298
|
+
}
|
|
299
|
+
skillGains = await distributeSkillXp(tx, quest.userId, skillTags, xpResult.totalXp);
|
|
300
|
+
});
|
|
301
|
+
return { xpEarned: xpResult.totalXp, skillGains };
|
|
302
|
+
}
|
|
303
|
+
async function updateQuest(db, questId, userId, patch) {
|
|
304
|
+
const set = { updatedAt: Date.now() };
|
|
305
|
+
if (patch.title !== void 0) set.title = patch.title;
|
|
306
|
+
if (patch.description !== void 0) set.description = patch.description;
|
|
307
|
+
if (patch.type !== void 0) set.type = patch.type;
|
|
308
|
+
if (patch.difficulty !== void 0) set.difficulty = patch.difficulty;
|
|
309
|
+
if (patch.skillTags !== void 0) set.skillTags = patch.skillTags;
|
|
310
|
+
if (patch.baseXp !== void 0) set.baseXp = patch.baseXp;
|
|
311
|
+
if (patch.scheduleCron !== void 0) set.scheduleCron = patch.scheduleCron;
|
|
312
|
+
if (patch.deadlineAt !== void 0) set.deadlineAt = patch.deadlineAt;
|
|
313
|
+
const [row] = await db.update(quests).set(set).where(and(eq(quests.id, questId), eq(quests.userId, userId))).returning();
|
|
314
|
+
return row ? rowToQuest(row) : null;
|
|
315
|
+
}
|
|
316
|
+
async function listQuestLogs(db, userId, options = {}) {
|
|
317
|
+
const { limit = 20, since } = options;
|
|
318
|
+
const where = since ? and(eq(questLogs.userId, userId), gte(questLogs.completedAt, since)) : eq(questLogs.userId, userId);
|
|
319
|
+
return db.query.questLogs.findMany({
|
|
320
|
+
where,
|
|
321
|
+
orderBy: [desc(questLogs.completedAt)],
|
|
322
|
+
limit
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
async function createConversation(db, userId, title) {
|
|
326
|
+
const [row] = await db.insert(conversations).values({ userId, title: null }).returning();
|
|
327
|
+
if (!row) throw new Error("Failed to create conversation");
|
|
328
|
+
return row;
|
|
329
|
+
}
|
|
330
|
+
async function getConversationById(db, id) {
|
|
331
|
+
const row = await db.query.conversations.findFirst({
|
|
332
|
+
where: eq(conversations.id, id)
|
|
333
|
+
});
|
|
334
|
+
return row ?? null;
|
|
335
|
+
}
|
|
336
|
+
async function listConversations(db, userId, limit = 20) {
|
|
337
|
+
return db.query.conversations.findMany({
|
|
338
|
+
where: eq(conversations.userId, userId),
|
|
339
|
+
orderBy: [desc(conversations.updatedAt)],
|
|
340
|
+
limit
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
async function appendMessage(db, conversationId, msg) {
|
|
344
|
+
const [row] = await db.insert(messages).values({
|
|
345
|
+
conversationId,
|
|
346
|
+
role: msg.role,
|
|
347
|
+
content: msg.content,
|
|
348
|
+
toolCalls: msg.toolCalls,
|
|
349
|
+
toolResults: msg.toolResults,
|
|
350
|
+
...msg.attachments ? { attachments: msg.attachments } : {}
|
|
351
|
+
}).returning();
|
|
352
|
+
if (!row) throw new Error("Failed to insert message");
|
|
353
|
+
await db.update(conversations).set({ updatedAt: Date.now() }).where(eq(conversations.id, conversationId));
|
|
354
|
+
return row;
|
|
355
|
+
}
|
|
356
|
+
async function getConversationMessages(db, conversationId, limit = 50) {
|
|
357
|
+
return db.query.messages.findMany({
|
|
358
|
+
where: eq(messages.conversationId, conversationId),
|
|
359
|
+
orderBy: [desc(messages.createdAt)],
|
|
360
|
+
limit
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
function storedToModelMessages(stored) {
|
|
364
|
+
const sorted = [...stored].sort((a, b) => a.createdAt - b.createdAt);
|
|
365
|
+
const result = [];
|
|
366
|
+
for (const msg of sorted) {
|
|
367
|
+
if (msg.role === "user") {
|
|
368
|
+
if (msg.attachments?.length) {
|
|
369
|
+
result.push({
|
|
370
|
+
role: "user",
|
|
371
|
+
content: [
|
|
372
|
+
...msg.attachments.map((a) => ({
|
|
373
|
+
type: "image",
|
|
374
|
+
image: a.base64,
|
|
375
|
+
mediaType: a.mime
|
|
376
|
+
})),
|
|
377
|
+
{ type: "text", text: msg.content }
|
|
378
|
+
]
|
|
379
|
+
});
|
|
380
|
+
} else {
|
|
381
|
+
result.push({ role: "user", content: msg.content });
|
|
382
|
+
}
|
|
383
|
+
} else if (msg.role === "assistant") {
|
|
384
|
+
result.push({ role: "assistant", content: msg.content });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
const PROMPT_HISTORY_MAX = 100;
|
|
390
|
+
async function appendPromptHistory(db, userId, content) {
|
|
391
|
+
await db.insert(promptHistory).values({ userId, content });
|
|
392
|
+
const rows = await db.query.promptHistory.findMany({
|
|
393
|
+
where: eq(promptHistory.userId, userId),
|
|
394
|
+
orderBy: [desc(promptHistory.createdAt)],
|
|
395
|
+
columns: { id: true }
|
|
396
|
+
});
|
|
397
|
+
if (rows.length > PROMPT_HISTORY_MAX) {
|
|
398
|
+
const excess = rows.slice(PROMPT_HISTORY_MAX).map((r) => r.id);
|
|
399
|
+
await db.delete(promptHistory).where(inArray(promptHistory.id, excess));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
async function getPromptHistory(db, userId) {
|
|
403
|
+
const rows = await db.query.promptHistory.findMany({
|
|
404
|
+
where: eq(promptHistory.userId, userId),
|
|
405
|
+
orderBy: [asc(promptHistory.createdAt)],
|
|
406
|
+
limit: PROMPT_HISTORY_MAX,
|
|
407
|
+
columns: { content: true }
|
|
408
|
+
});
|
|
409
|
+
return rows.map((r) => r.content);
|
|
410
|
+
}
|
|
411
|
+
export {
|
|
412
|
+
listSkillsByUser as a,
|
|
413
|
+
listConversations as b,
|
|
414
|
+
createServerRpc as c,
|
|
415
|
+
getPromptHistory as d,
|
|
416
|
+
appendPromptHistory as e,
|
|
417
|
+
createQuest as f,
|
|
418
|
+
getCompanionByUserId as g,
|
|
419
|
+
completeQuest as h,
|
|
420
|
+
getQuestById as i,
|
|
421
|
+
updateCompanionMode as j,
|
|
422
|
+
listCompanionInsights as k,
|
|
423
|
+
listQuestsByUser as l,
|
|
424
|
+
deleteCompanionInsight as m,
|
|
425
|
+
findQuestByPrefix as n,
|
|
426
|
+
updateQuest as o,
|
|
427
|
+
listQuestLogs as p,
|
|
428
|
+
updateCompanionUserContext as q,
|
|
429
|
+
updateCompanionInsight as r,
|
|
430
|
+
findCompanionInsightByContent as s,
|
|
431
|
+
createCompanionInsight as t,
|
|
432
|
+
updateQuestStatus as u,
|
|
433
|
+
getConversationById as v,
|
|
434
|
+
createConversation as w,
|
|
435
|
+
appendMessage as x,
|
|
436
|
+
getConversationMessages as y,
|
|
437
|
+
storedToModelMessages as z
|
|
438
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { a7 as getDefaultExportFromCjs } from "./vault.server-CscY5Z8e.js";
|
|
2
|
+
import { a2 as requireTokenError } from "./agent.functions-BL3upUNr.js";
|
|
3
|
+
import { r as requireTokenUtil } from "./token-util-BopJPy-I.js";
|
|
4
|
+
function _mergeNamespaces(n, m) {
|
|
5
|
+
for (var i = 0; i < m.length; i++) {
|
|
6
|
+
const e = m[i];
|
|
7
|
+
if (typeof e !== "string" && !Array.isArray(e)) {
|
|
8
|
+
for (const k in e) {
|
|
9
|
+
if (k !== "default" && !(k in n)) {
|
|
10
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
11
|
+
if (d) {
|
|
12
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: () => e[k]
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: "Module" }));
|
|
22
|
+
}
|
|
23
|
+
var token$2;
|
|
24
|
+
var hasRequiredToken;
|
|
25
|
+
function requireToken() {
|
|
26
|
+
if (hasRequiredToken) return token$2;
|
|
27
|
+
hasRequiredToken = 1;
|
|
28
|
+
var __defProp = Object.defineProperty;
|
|
29
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
30
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
31
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
32
|
+
var __export = (target, all) => {
|
|
33
|
+
for (var name in all)
|
|
34
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
35
|
+
};
|
|
36
|
+
var __copyProps = (to, from, except, desc) => {
|
|
37
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
38
|
+
for (let key of __getOwnPropNames(from))
|
|
39
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
40
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
41
|
+
}
|
|
42
|
+
return to;
|
|
43
|
+
};
|
|
44
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
45
|
+
var token_exports = {};
|
|
46
|
+
__export(token_exports, {
|
|
47
|
+
refreshToken: () => refreshToken
|
|
48
|
+
});
|
|
49
|
+
token$2 = __toCommonJS(token_exports);
|
|
50
|
+
var import_token_error = requireTokenError();
|
|
51
|
+
var import_token_util = requireTokenUtil();
|
|
52
|
+
async function refreshToken() {
|
|
53
|
+
const { projectId, teamId } = (0, import_token_util.findProjectInfo)();
|
|
54
|
+
let maybeToken = (0, import_token_util.loadToken)(projectId);
|
|
55
|
+
if (!maybeToken || (0, import_token_util.isExpired)((0, import_token_util.getTokenPayload)(maybeToken.token))) {
|
|
56
|
+
const authToken = await (0, import_token_util.getVercelCliToken)();
|
|
57
|
+
if (!authToken) {
|
|
58
|
+
throw new import_token_error.VercelOidcTokenError(
|
|
59
|
+
"Failed to refresh OIDC token: Log in to Vercel CLI and link your project with `vc link`"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (!projectId) {
|
|
63
|
+
throw new import_token_error.VercelOidcTokenError(
|
|
64
|
+
"Failed to refresh OIDC token: Try re-linking your project with `vc link`"
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
maybeToken = await (0, import_token_util.getVercelOidcToken)(authToken, projectId, teamId);
|
|
68
|
+
if (!maybeToken) {
|
|
69
|
+
throw new import_token_error.VercelOidcTokenError("Failed to refresh OIDC token");
|
|
70
|
+
}
|
|
71
|
+
(0, import_token_util.saveToken)(maybeToken, projectId);
|
|
72
|
+
}
|
|
73
|
+
process.env.VERCEL_OIDC_TOKEN = maybeToken.token;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
return token$2;
|
|
77
|
+
}
|
|
78
|
+
var tokenExports = requireToken();
|
|
79
|
+
const token = /* @__PURE__ */ getDefaultExportFromCjs(tokenExports);
|
|
80
|
+
const token$1 = /* @__PURE__ */ _mergeNamespaces({
|
|
81
|
+
__proto__: null,
|
|
82
|
+
default: token
|
|
83
|
+
}, [tokenExports]);
|
|
84
|
+
export {
|
|
85
|
+
token$1 as t
|
|
86
|
+
};
|