@sbugallo/opencode-vpet 0.1.1 → 0.2.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 +4 -5
- package/dist/adapters/opencode/server-plugin.d.ts +78 -0
- package/dist/adapters/opencode/server-plugin.d.ts.map +1 -0
- package/dist/adapters/opencode/session-messages.d.ts +4 -9
- package/dist/adapters/opencode/session-messages.d.ts.map +1 -1
- package/dist/adapters/opencode/usage-event-mapper.d.ts +6 -3
- package/dist/adapters/opencode/usage-event-mapper.d.ts.map +1 -1
- package/dist/adapters/opencode/vpet-toast.d.ts +4 -2
- package/dist/adapters/opencode/vpet-toast.d.ts.map +1 -1
- package/dist/cli/install.d.ts.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +4 -4
- package/dist/commands/vpet-command-result.d.ts +2 -2
- package/dist/commands/vpet-command-result.d.ts.map +1 -1
- package/dist/commands/vpet-freeze.d.ts +1 -5
- package/dist/commands/vpet-freeze.d.ts.map +1 -1
- package/dist/commands/vpet-set.d.ts +0 -2
- package/dist/commands/vpet-set.d.ts.map +1 -1
- package/dist/commands/vpet-spawn.d.ts +0 -2
- package/dist/commands/vpet-spawn.d.ts.map +1 -1
- package/dist/commands/vpet-unfreeze.d.ts +1 -5
- package/dist/commands/vpet-unfreeze.d.ts.map +1 -1
- package/dist/index.d.ts +4 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +535 -470
- package/dist/index.js.map +22 -21
- package/dist/package.json +10 -0
- package/dist/rpc.d.ts +32 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/tui/theme.d.ts +27 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/vpet-command-layer.d.ts +32 -5
- package/dist/tui/vpet-command-layer.d.ts.map +1 -1
- package/dist/tui/vpet-dex-dialog.d.ts +2 -2
- package/dist/tui/vpet-dex-dialog.d.ts.map +1 -1
- package/dist/tui/vpet-history-dialog.d.ts +2 -2
- package/dist/tui/vpet-history-dialog.d.ts.map +1 -1
- package/dist/tui.d.ts +22 -12
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +203 -66
- package/dist/tui.js.map +10 -7
- package/package.json +9 -12
- package/dist/adapters/opencode/create-server-hooks.d.ts +0 -39
- package/dist/adapters/opencode/create-server-hooks.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,24 +1,114 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// src/index.ts
|
|
3
|
+
import { Plugin } from "@opencode/plugin";
|
|
4
|
+
|
|
5
|
+
// src/domain/stage.ts
|
|
6
|
+
var DIGIMON_STAGES = [0, 1, 2, 3, 4, 5, 6, 7];
|
|
7
|
+
var isDigimonStage = (value) => {
|
|
8
|
+
return DIGIMON_STAGES.some((stage) => stage === value);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/domain/evolution.ts
|
|
12
|
+
var STAGE_GAUGE_THRESHOLDS = Object.freeze({
|
|
13
|
+
0: 500000,
|
|
14
|
+
1: 1e6,
|
|
15
|
+
2: 2000000,
|
|
16
|
+
3: 4000000,
|
|
17
|
+
4: 7500000,
|
|
18
|
+
5: 12500000,
|
|
19
|
+
6: 20000000,
|
|
20
|
+
7: 30000000
|
|
21
|
+
});
|
|
22
|
+
var isRecord = (value) => {
|
|
23
|
+
return typeof value === "object" && value !== null;
|
|
24
|
+
};
|
|
25
|
+
var isStageThresholds = (thresholds) => {
|
|
26
|
+
if (!isRecord(thresholds) || !Object.isFrozen(thresholds))
|
|
27
|
+
return false;
|
|
28
|
+
return DIGIMON_STAGES.every((stage) => {
|
|
29
|
+
const threshold = thresholds[stage];
|
|
30
|
+
return typeof threshold === "number" && Number.isFinite(threshold) && threshold > 0;
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
var applyTokenProgress = (state, tokenDelta, selector, digimonById, thresholds) => {
|
|
34
|
+
if (!isStageThresholds(thresholds)) {
|
|
35
|
+
throw new Error("Evolution thresholds must be a complete frozen policy of positive finite numbers");
|
|
36
|
+
}
|
|
37
|
+
if (state.isTerminal)
|
|
38
|
+
return state;
|
|
39
|
+
const gauge = state.gauge + tokenDelta;
|
|
40
|
+
if (gauge < thresholds[state.current.stage])
|
|
41
|
+
return { ...state, gauge };
|
|
42
|
+
if (state.current.nextEvolutions.length === 0)
|
|
43
|
+
return { ...state, gauge: 0, isTerminal: true };
|
|
44
|
+
const selection = selector();
|
|
45
|
+
if (!Number.isFinite(selection) || selection < 0 || selection >= 1) {
|
|
46
|
+
throw new Error(`Evolution selector must return a finite number in [0, 1), received ${selection}`);
|
|
47
|
+
}
|
|
48
|
+
const targetId = state.current.nextEvolutions[Math.floor(selection * state.current.nextEvolutions.length)];
|
|
49
|
+
if (targetId === undefined)
|
|
50
|
+
throw new Error("Evolution target selection failed");
|
|
51
|
+
const current = digimonById.get(targetId);
|
|
52
|
+
if (current === undefined)
|
|
53
|
+
throw new Error(`Evolution target ${targetId} is missing from the catalog`);
|
|
54
|
+
return { current, gauge: 0, isTerminal: current.nextEvolutions.length === 0 };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/application/use-cases/record-usage.ts
|
|
58
|
+
var recordUsage = ({
|
|
59
|
+
usage,
|
|
60
|
+
ledger,
|
|
61
|
+
digimonById,
|
|
62
|
+
selector,
|
|
63
|
+
thresholds
|
|
64
|
+
}) => {
|
|
65
|
+
let evolution;
|
|
66
|
+
const outcome = ledger.applyUsageReceipt(usage, (partner) => {
|
|
67
|
+
const current = digimonById.get(partner.currentNodeId);
|
|
68
|
+
if (current === undefined)
|
|
69
|
+
throw new Error(`Persisted partner node ${partner.currentNodeId} is missing from the catalog`);
|
|
70
|
+
const next = applyTokenProgress({ current, gauge: partner.gauge, isTerminal: partner.isTerminal }, usage.tokenDelta, selector, digimonById, thresholds);
|
|
71
|
+
if (next.current.id !== current.id) {
|
|
72
|
+
evolution = { fromNodeId: current.id, toNodeId: next.current.id };
|
|
73
|
+
}
|
|
74
|
+
return { currentNodeId: next.current.id, gauge: next.gauge, isTerminal: next.isTerminal };
|
|
75
|
+
});
|
|
76
|
+
switch (outcome.kind) {
|
|
77
|
+
case "applied":
|
|
78
|
+
return evolution === undefined ? { kind: "applied", receiptKey: usage.receiptKey } : { kind: "applied", receiptKey: usage.receiptKey, evolution };
|
|
79
|
+
case "duplicate":
|
|
80
|
+
return { kind: "duplicate", receiptKey: usage.receiptKey };
|
|
81
|
+
case "no_active_partner":
|
|
82
|
+
return { kind: "no_active_partner", receiptKey: usage.receiptKey };
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/application/use-cases/reconcile-usage.ts
|
|
87
|
+
var reconcileUsage = ({
|
|
88
|
+
usages,
|
|
89
|
+
ledger,
|
|
90
|
+
digimonById,
|
|
91
|
+
selector,
|
|
92
|
+
thresholds
|
|
93
|
+
}) => {
|
|
94
|
+
const outcomes = [];
|
|
95
|
+
for (const usage of usages) {
|
|
96
|
+
outcomes.push(recordUsage({ usage, ledger, digimonById, selector, thresholds }));
|
|
97
|
+
}
|
|
98
|
+
return outcomes;
|
|
99
|
+
};
|
|
100
|
+
|
|
2
101
|
// src/application/use-cases/freeze-vpet.ts
|
|
3
102
|
var freezeVpet = (control) => control.freeze();
|
|
4
103
|
|
|
5
104
|
// src/commands/vpet-freeze.ts
|
|
6
|
-
var runVpetFreezeCommand = async (control
|
|
105
|
+
var runVpetFreezeCommand = async (control) => {
|
|
7
106
|
const outcome = freezeVpet(control);
|
|
8
|
-
const parts = [
|
|
9
|
-
{
|
|
10
|
-
id: context.messageID,
|
|
11
|
-
sessionID: context.sessionID,
|
|
12
|
-
messageID: context.messageID,
|
|
13
|
-
type: "text",
|
|
14
|
-
text: "VPet frozen."
|
|
15
|
-
}
|
|
16
|
-
];
|
|
17
107
|
switch (outcome.kind) {
|
|
18
108
|
case "frozen":
|
|
19
|
-
return {
|
|
109
|
+
return { message: "VPet frozen.", variant: "info", event: { kind: "frozen" } };
|
|
20
110
|
case "already_frozen":
|
|
21
|
-
return {
|
|
111
|
+
return { message: "VPet is already frozen.", variant: "info" };
|
|
22
112
|
default: {
|
|
23
113
|
const unexpectedOutcome = outcome;
|
|
24
114
|
return unexpectedOutcome;
|
|
@@ -26,65 +116,30 @@ var runVpetFreezeCommand = async (control, context) => {
|
|
|
26
116
|
}
|
|
27
117
|
};
|
|
28
118
|
|
|
29
|
-
// src/application/use-cases/spawn-partner.ts
|
|
30
|
-
var spawnPartner = (lifecycle, createdAt) => {
|
|
31
|
-
const partner = lifecycle.spawnPartner({
|
|
32
|
-
currentNodeId: "0-001",
|
|
33
|
-
gauge: 0,
|
|
34
|
-
isTerminal: false,
|
|
35
|
-
createdAt
|
|
36
|
-
});
|
|
37
|
-
return { generation: partner.generation };
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// src/commands/vpet-spawn.ts
|
|
41
|
-
var runVpetSpawnCommand = async (lifecycle, context) => {
|
|
42
|
-
const partner = spawnPartner(lifecycle, context.createdAt);
|
|
43
|
-
return {
|
|
44
|
-
parts: [
|
|
45
|
-
{
|
|
46
|
-
id: context.messageID,
|
|
47
|
-
sessionID: context.sessionID,
|
|
48
|
-
messageID: context.messageID,
|
|
49
|
-
type: "text",
|
|
50
|
-
text: `Spawned Generation ${partner.generation}.`
|
|
51
|
-
}
|
|
52
|
-
],
|
|
53
|
-
event: { kind: "spawned", nodeId: "0-001", generation: partner.generation }
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
|
|
57
119
|
// src/application/use-cases/set-vpet-cheat-node.ts
|
|
58
120
|
var setVpetCheatNode = (control, validatedNodeId) => control.setCheatNode(validatedNodeId);
|
|
59
121
|
|
|
60
122
|
// src/commands/vpet-set.ts
|
|
61
|
-
var textPart = (context, text) => ({
|
|
62
|
-
id: context.messageID,
|
|
63
|
-
sessionID: context.sessionID,
|
|
64
|
-
messageID: context.messageID,
|
|
65
|
-
type: "text",
|
|
66
|
-
text
|
|
67
|
-
});
|
|
68
123
|
var runVpetSetCommand = async (control, context, loadCatalog) => {
|
|
69
124
|
if (context.arguments.includes(`
|
|
70
125
|
`))
|
|
71
|
-
return {
|
|
126
|
+
return { message: "Usage: /vpet-set <id>.", variant: "warning" };
|
|
72
127
|
const tokens = context.arguments.trim().split(/\s+/);
|
|
73
128
|
if (context.arguments.trim() === "" || tokens.length !== 1)
|
|
74
|
-
return {
|
|
129
|
+
return { message: "Usage: /vpet-set <id>.", variant: "warning" };
|
|
75
130
|
const requestedId = tokens[0];
|
|
76
131
|
if (requestedId === undefined)
|
|
77
|
-
return {
|
|
132
|
+
return { message: "Usage: /vpet-set <id>.", variant: "warning" };
|
|
78
133
|
const node = (await loadCatalog()).byId.get(requestedId);
|
|
79
134
|
if (node === undefined)
|
|
80
|
-
return {
|
|
135
|
+
return { message: `Unknown Digimon ID: ${requestedId}.`, variant: "warning" };
|
|
81
136
|
const outcome = setVpetCheatNode(control, node.id);
|
|
82
|
-
const
|
|
137
|
+
const message = `VPet set to ${node.nameEn} (${node.id}).`;
|
|
83
138
|
switch (outcome.kind) {
|
|
84
139
|
case "set":
|
|
85
|
-
return {
|
|
140
|
+
return { message, variant: "info", event: { kind: "set", nodeId: node.id } };
|
|
86
141
|
case "already_set":
|
|
87
|
-
return {
|
|
142
|
+
return { message, variant: "info" };
|
|
88
143
|
default: {
|
|
89
144
|
const unexpectedOutcome = outcome;
|
|
90
145
|
return unexpectedOutcome;
|
|
@@ -92,26 +147,38 @@ var runVpetSetCommand = async (control, context, loadCatalog) => {
|
|
|
92
147
|
}
|
|
93
148
|
};
|
|
94
149
|
|
|
150
|
+
// src/application/use-cases/spawn-partner.ts
|
|
151
|
+
var spawnPartner = (lifecycle, createdAt) => {
|
|
152
|
+
const partner = lifecycle.spawnPartner({
|
|
153
|
+
currentNodeId: "0-001",
|
|
154
|
+
gauge: 0,
|
|
155
|
+
isTerminal: false,
|
|
156
|
+
createdAt
|
|
157
|
+
});
|
|
158
|
+
return { generation: partner.generation };
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// src/commands/vpet-spawn.ts
|
|
162
|
+
var runVpetSpawnCommand = async (lifecycle, context) => {
|
|
163
|
+
const partner = spawnPartner(lifecycle, context.createdAt);
|
|
164
|
+
return {
|
|
165
|
+
message: `Spawned Generation ${partner.generation}.`,
|
|
166
|
+
variant: "info",
|
|
167
|
+
event: { kind: "spawned", nodeId: "0-001", generation: partner.generation }
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
|
|
95
171
|
// src/application/use-cases/unfreeze-vpet.ts
|
|
96
172
|
var unfreezeVpet = (control) => control.unfreeze();
|
|
97
173
|
|
|
98
174
|
// src/commands/vpet-unfreeze.ts
|
|
99
|
-
var runVpetUnfreezeCommand = async (control
|
|
175
|
+
var runVpetUnfreezeCommand = async (control) => {
|
|
100
176
|
const outcome = unfreezeVpet(control);
|
|
101
|
-
const parts = [
|
|
102
|
-
{
|
|
103
|
-
id: context.messageID,
|
|
104
|
-
sessionID: context.sessionID,
|
|
105
|
-
messageID: context.messageID,
|
|
106
|
-
type: "text",
|
|
107
|
-
text: "VPet unfrozen."
|
|
108
|
-
}
|
|
109
|
-
];
|
|
110
177
|
switch (outcome.kind) {
|
|
111
178
|
case "unfrozen":
|
|
112
|
-
return {
|
|
179
|
+
return { message: "VPet unfrozen.", variant: "info", event: { kind: "unfrozen" } };
|
|
113
180
|
case "already_unfrozen":
|
|
114
|
-
return {
|
|
181
|
+
return { message: "VPet is not frozen.", variant: "info" };
|
|
115
182
|
default: {
|
|
116
183
|
const unexpectedOutcome = outcome;
|
|
117
184
|
return unexpectedOutcome;
|
|
@@ -119,6 +186,121 @@ var runVpetUnfreezeCommand = async (control, context) => {
|
|
|
119
186
|
}
|
|
120
187
|
};
|
|
121
188
|
|
|
189
|
+
// src/config/global-vpet-settings.ts
|
|
190
|
+
import { readFile } from "fs/promises";
|
|
191
|
+
import { homedir } from "os";
|
|
192
|
+
import { join } from "path";
|
|
193
|
+
|
|
194
|
+
// src/config/defaults.ts
|
|
195
|
+
var EN_STAGE_LABELS = Object.freeze({
|
|
196
|
+
egg: "DigiEgg",
|
|
197
|
+
babyI: "In Training I",
|
|
198
|
+
babyII: "In Training II",
|
|
199
|
+
child: "Rookie",
|
|
200
|
+
adult: "Champion",
|
|
201
|
+
perfect: "Ultimate",
|
|
202
|
+
ultimate: "Mega",
|
|
203
|
+
superUltimate: "Ultra"
|
|
204
|
+
});
|
|
205
|
+
var JP_STAGE_LABELS = Object.freeze({
|
|
206
|
+
egg: "Digitama",
|
|
207
|
+
babyI: "Baby I",
|
|
208
|
+
babyII: "Baby II",
|
|
209
|
+
child: "Child",
|
|
210
|
+
adult: "Adult",
|
|
211
|
+
perfect: "Perfect",
|
|
212
|
+
ultimate: "Ultimate",
|
|
213
|
+
superUltimate: "SuperUltimate"
|
|
214
|
+
});
|
|
215
|
+
var VPET_STAGE_LABELS = Object.freeze({
|
|
216
|
+
en: EN_STAGE_LABELS,
|
|
217
|
+
jp: JP_STAGE_LABELS
|
|
218
|
+
});
|
|
219
|
+
var DEFAULT_STAGE_THRESHOLDS = Object.freeze({
|
|
220
|
+
egg: 500000,
|
|
221
|
+
babyI: 1e6,
|
|
222
|
+
babyII: 2000000,
|
|
223
|
+
child: 4000000,
|
|
224
|
+
adult: 7500000,
|
|
225
|
+
perfect: 12500000,
|
|
226
|
+
ultimate: 20000000,
|
|
227
|
+
superUltimate: 30000000
|
|
228
|
+
});
|
|
229
|
+
var DEFAULT_VPET_SETTINGS = Object.freeze({
|
|
230
|
+
language: "jp",
|
|
231
|
+
notifications: true,
|
|
232
|
+
stageLabels: VPET_STAGE_LABELS,
|
|
233
|
+
stageThresholds: DEFAULT_STAGE_THRESHOLDS
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// src/config/normalize.ts
|
|
237
|
+
var isRecord2 = (value) => {
|
|
238
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
239
|
+
};
|
|
240
|
+
var isLanguage = (value) => value === "en" || value === "jp";
|
|
241
|
+
var isBoolean = (value) => typeof value === "boolean";
|
|
242
|
+
var isThreshold = (value) => {
|
|
243
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
244
|
+
};
|
|
245
|
+
var read = (input, key) => {
|
|
246
|
+
try {
|
|
247
|
+
return input[key];
|
|
248
|
+
} catch {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
var normalizeThreshold = (input, key) => {
|
|
253
|
+
const value = read(input, key);
|
|
254
|
+
return isThreshold(value) ? value : DEFAULT_STAGE_THRESHOLDS[key];
|
|
255
|
+
};
|
|
256
|
+
var normalizeVpetSettings = (input) => {
|
|
257
|
+
try {
|
|
258
|
+
if (!isRecord2(input))
|
|
259
|
+
return DEFAULT_VPET_SETTINGS;
|
|
260
|
+
const thresholdInput = read(input, "stageThresholds");
|
|
261
|
+
const thresholds = isRecord2(thresholdInput) ? thresholdInput : {};
|
|
262
|
+
const stageThresholds = Object.freeze({
|
|
263
|
+
egg: normalizeThreshold(thresholds, "egg"),
|
|
264
|
+
babyI: normalizeThreshold(thresholds, "babyI"),
|
|
265
|
+
babyII: normalizeThreshold(thresholds, "babyII"),
|
|
266
|
+
child: normalizeThreshold(thresholds, "child"),
|
|
267
|
+
adult: normalizeThreshold(thresholds, "adult"),
|
|
268
|
+
perfect: normalizeThreshold(thresholds, "perfect"),
|
|
269
|
+
ultimate: normalizeThreshold(thresholds, "ultimate"),
|
|
270
|
+
superUltimate: normalizeThreshold(thresholds, "superUltimate")
|
|
271
|
+
});
|
|
272
|
+
const language = read(input, "language");
|
|
273
|
+
const notifications = read(input, "notifications");
|
|
274
|
+
return Object.freeze({
|
|
275
|
+
language: isLanguage(language) ? language : DEFAULT_VPET_SETTINGS.language,
|
|
276
|
+
notifications: isBoolean(notifications) ? notifications : DEFAULT_VPET_SETTINGS.notifications,
|
|
277
|
+
stageLabels: DEFAULT_VPET_SETTINGS.stageLabels,
|
|
278
|
+
stageThresholds
|
|
279
|
+
});
|
|
280
|
+
} catch {
|
|
281
|
+
return DEFAULT_VPET_SETTINGS;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// src/config/global-vpet-settings.ts
|
|
286
|
+
var resolveGlobalVpetConfigPath = (options = {}) => {
|
|
287
|
+
const pathApi = options.pathApi ?? { join };
|
|
288
|
+
const env = options.env ?? process.env;
|
|
289
|
+
const home = options.home ?? homedir();
|
|
290
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
291
|
+
const { APPDATA } = env;
|
|
292
|
+
return pathApi.join(APPDATA ?? pathApi.join(home, "AppData", "Roaming"), "opencode-vpet.json");
|
|
293
|
+
}
|
|
294
|
+
return pathApi.join(home, ".config", "opencode-vpet.json");
|
|
295
|
+
};
|
|
296
|
+
var loadGlobalVpetSettings = async (options = {}) => {
|
|
297
|
+
try {
|
|
298
|
+
return normalizeVpetSettings(JSON.parse(await readFile(resolveGlobalVpetConfigPath(options), "utf8")));
|
|
299
|
+
} catch {
|
|
300
|
+
return DEFAULT_VPET_SETTINGS;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
122
304
|
// src/data/digimon-data.ts
|
|
123
305
|
var DIGIMON_DATA = [
|
|
124
306
|
{
|
|
@@ -6227,12 +6409,6 @@ var DIGIMON_DATA = [
|
|
|
6227
6409
|
}
|
|
6228
6410
|
];
|
|
6229
6411
|
|
|
6230
|
-
// src/domain/stage.ts
|
|
6231
|
-
var DIGIMON_STAGES = [0, 1, 2, 3, 4, 5, 6, 7];
|
|
6232
|
-
var isDigimonStage = (value) => {
|
|
6233
|
-
return DIGIMON_STAGES.some((stage) => stage === value);
|
|
6234
|
-
};
|
|
6235
|
-
|
|
6236
6412
|
// src/data/catalog.ts
|
|
6237
6413
|
var parseNode = (input) => {
|
|
6238
6414
|
if (!isDigimonStage(input.stage))
|
|
@@ -6256,131 +6432,98 @@ var parseDigimonCatalog = (input) => {
|
|
|
6256
6432
|
byId.set(node.id, node);
|
|
6257
6433
|
}
|
|
6258
6434
|
for (const node of nodes) {
|
|
6259
|
-
for (const targetId of node.nextEvolutions) {
|
|
6260
|
-
if (!byId.has(targetId))
|
|
6261
|
-
throw new Error(`Invalid next_evolutions reference ${targetId} from ${node.id}`);
|
|
6262
|
-
}
|
|
6263
|
-
}
|
|
6264
|
-
return Object.freeze({ nodes: Object.freeze(nodes), byId });
|
|
6265
|
-
};
|
|
6266
|
-
var DIGIMON_CATALOG = parseDigimonCatalog(DIGIMON_DATA);
|
|
6267
|
-
var loadDigimonCatalog = async () => DIGIMON_CATALOG;
|
|
6268
|
-
|
|
6269
|
-
// src/domain/evolution.ts
|
|
6270
|
-
var STAGE_GAUGE_THRESHOLDS = Object.freeze({
|
|
6271
|
-
0: 500000,
|
|
6272
|
-
1: 1e6,
|
|
6273
|
-
2: 2000000,
|
|
6274
|
-
3: 4000000,
|
|
6275
|
-
4: 7500000,
|
|
6276
|
-
5: 12500000,
|
|
6277
|
-
6: 20000000,
|
|
6278
|
-
7: 30000000
|
|
6279
|
-
});
|
|
6280
|
-
var isRecord = (value) => {
|
|
6281
|
-
return typeof value === "object" && value !== null;
|
|
6282
|
-
};
|
|
6283
|
-
var isStageThresholds = (thresholds) => {
|
|
6284
|
-
if (!isRecord(thresholds) || !Object.isFrozen(thresholds))
|
|
6285
|
-
return false;
|
|
6286
|
-
return DIGIMON_STAGES.every((stage) => {
|
|
6287
|
-
const threshold = thresholds[stage];
|
|
6288
|
-
return typeof threshold === "number" && Number.isFinite(threshold) && threshold > 0;
|
|
6289
|
-
});
|
|
6290
|
-
};
|
|
6291
|
-
var applyTokenProgress = (state, tokenDelta, selector, digimonById, thresholds) => {
|
|
6292
|
-
if (!isStageThresholds(thresholds)) {
|
|
6293
|
-
throw new Error("Evolution thresholds must be a complete frozen policy of positive finite numbers");
|
|
6294
|
-
}
|
|
6295
|
-
if (state.isTerminal)
|
|
6296
|
-
return state;
|
|
6297
|
-
const gauge = state.gauge + tokenDelta;
|
|
6298
|
-
if (gauge < thresholds[state.current.stage])
|
|
6299
|
-
return { ...state, gauge };
|
|
6300
|
-
if (state.current.nextEvolutions.length === 0)
|
|
6301
|
-
return { ...state, gauge: 0, isTerminal: true };
|
|
6302
|
-
const selection = selector();
|
|
6303
|
-
if (!Number.isFinite(selection) || selection < 0 || selection >= 1) {
|
|
6304
|
-
throw new Error(`Evolution selector must return a finite number in [0, 1), received ${selection}`);
|
|
6305
|
-
}
|
|
6306
|
-
const targetId = state.current.nextEvolutions[Math.floor(selection * state.current.nextEvolutions.length)];
|
|
6307
|
-
if (targetId === undefined)
|
|
6308
|
-
throw new Error("Evolution target selection failed");
|
|
6309
|
-
const current = digimonById.get(targetId);
|
|
6310
|
-
if (current === undefined)
|
|
6311
|
-
throw new Error(`Evolution target ${targetId} is missing from the catalog`);
|
|
6312
|
-
return { current, gauge: 0, isTerminal: current.nextEvolutions.length === 0 };
|
|
6313
|
-
};
|
|
6314
|
-
|
|
6315
|
-
// src/application/use-cases/record-usage.ts
|
|
6316
|
-
var recordUsage = ({
|
|
6317
|
-
usage,
|
|
6318
|
-
ledger,
|
|
6319
|
-
digimonById,
|
|
6320
|
-
selector,
|
|
6321
|
-
thresholds
|
|
6322
|
-
}) => {
|
|
6323
|
-
let evolution;
|
|
6324
|
-
const outcome = ledger.applyUsageReceipt(usage, (partner) => {
|
|
6325
|
-
const current = digimonById.get(partner.currentNodeId);
|
|
6326
|
-
if (current === undefined)
|
|
6327
|
-
throw new Error(`Persisted partner node ${partner.currentNodeId} is missing from the catalog`);
|
|
6328
|
-
const next = applyTokenProgress({ current, gauge: partner.gauge, isTerminal: partner.isTerminal }, usage.tokenDelta, selector, digimonById, thresholds);
|
|
6329
|
-
if (next.current.id !== current.id) {
|
|
6330
|
-
evolution = { fromNodeId: current.id, toNodeId: next.current.id };
|
|
6435
|
+
for (const targetId of node.nextEvolutions) {
|
|
6436
|
+
if (!byId.has(targetId))
|
|
6437
|
+
throw new Error(`Invalid next_evolutions reference ${targetId} from ${node.id}`);
|
|
6331
6438
|
}
|
|
6332
|
-
return { currentNodeId: next.current.id, gauge: next.gauge, isTerminal: next.isTerminal };
|
|
6333
|
-
});
|
|
6334
|
-
switch (outcome.kind) {
|
|
6335
|
-
case "applied":
|
|
6336
|
-
return evolution === undefined ? { kind: "applied", receiptKey: usage.receiptKey } : { kind: "applied", receiptKey: usage.receiptKey, evolution };
|
|
6337
|
-
case "duplicate":
|
|
6338
|
-
return { kind: "duplicate", receiptKey: usage.receiptKey };
|
|
6339
|
-
case "no_active_partner":
|
|
6340
|
-
return { kind: "no_active_partner", receiptKey: usage.receiptKey };
|
|
6341
6439
|
}
|
|
6440
|
+
return Object.freeze({ nodes: Object.freeze(nodes), byId });
|
|
6342
6441
|
};
|
|
6442
|
+
var DIGIMON_CATALOG = parseDigimonCatalog(DIGIMON_DATA);
|
|
6443
|
+
var loadDigimonCatalog = async () => DIGIMON_CATALOG;
|
|
6343
6444
|
|
|
6344
|
-
// src/
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6445
|
+
// src/rpc.ts
|
|
6446
|
+
import { Rpc } from "@opencode/plugin/rpc";
|
|
6447
|
+
var VpetRpc = Rpc.define({
|
|
6448
|
+
id: "vpet",
|
|
6449
|
+
methods: {},
|
|
6450
|
+
events: {
|
|
6451
|
+
toast: {
|
|
6452
|
+
schema: {
|
|
6453
|
+
type: "object",
|
|
6454
|
+
properties: {
|
|
6455
|
+
title: { type: "string" },
|
|
6456
|
+
message: { type: "string" },
|
|
6457
|
+
variant: { enum: ["info", "success", "warning"] },
|
|
6458
|
+
duration: { type: "number" }
|
|
6459
|
+
},
|
|
6460
|
+
required: ["title", "message", "variant", "duration"],
|
|
6461
|
+
additionalProperties: false
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6355
6464
|
}
|
|
6356
|
-
|
|
6465
|
+
});
|
|
6466
|
+
|
|
6467
|
+
// src/adapters/opencode/session-messages.ts
|
|
6468
|
+
var createSessionMessagesFetcher = (client) => {
|
|
6469
|
+
return async (sessionID) => client.session.context({ sessionID });
|
|
6357
6470
|
};
|
|
6358
6471
|
|
|
6359
6472
|
// src/adapters/opencode/usage-event-mapper.ts
|
|
6360
|
-
var
|
|
6361
|
-
|
|
6473
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6474
|
+
var toFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
6475
|
+
var toTokenCount = (value) => {
|
|
6476
|
+
const count = toFiniteNumber(value);
|
|
6477
|
+
return count === undefined || count < 0 ? undefined : count;
|
|
6362
6478
|
};
|
|
6363
6479
|
var toCompletedUsageFromMessage = (message) => {
|
|
6364
|
-
if (!
|
|
6480
|
+
if (!isRecord3(message))
|
|
6481
|
+
return null;
|
|
6482
|
+
const { type, id, time, tokens, cost: rawCost } = message;
|
|
6483
|
+
if (type !== "assistant")
|
|
6484
|
+
return null;
|
|
6485
|
+
if (typeof id !== "string" || id.length === 0)
|
|
6486
|
+
return null;
|
|
6487
|
+
if (!isRecord3(time))
|
|
6365
6488
|
return null;
|
|
6366
|
-
const
|
|
6489
|
+
const { completed } = time;
|
|
6490
|
+
const completedAt = toFiniteNumber(completed);
|
|
6367
6491
|
if (completedAt === undefined)
|
|
6368
6492
|
return null;
|
|
6493
|
+
if (!isRecord3(tokens))
|
|
6494
|
+
return null;
|
|
6495
|
+
const { input: rawInput, output: rawOutput, reasoning: rawReasoning, cache } = tokens;
|
|
6496
|
+
if (!isRecord3(cache))
|
|
6497
|
+
return null;
|
|
6498
|
+
const { read: rawRead, write: rawWrite } = cache;
|
|
6499
|
+
const input = toTokenCount(rawInput);
|
|
6500
|
+
const output = toTokenCount(rawOutput);
|
|
6501
|
+
const reasoning = toTokenCount(rawReasoning);
|
|
6502
|
+
const cacheRead = toTokenCount(rawRead);
|
|
6503
|
+
const cacheWrite = toTokenCount(rawWrite);
|
|
6504
|
+
if (input === undefined || output === undefined || reasoning === undefined || cacheRead === undefined || cacheWrite === undefined) {
|
|
6505
|
+
return null;
|
|
6506
|
+
}
|
|
6507
|
+
const cost = toFiniteNumber(rawCost);
|
|
6369
6508
|
return {
|
|
6370
|
-
receiptKey: `message:${
|
|
6371
|
-
eventId: `usage:${
|
|
6372
|
-
tokenDelta:
|
|
6373
|
-
cost:
|
|
6509
|
+
receiptKey: `message:${id}`,
|
|
6510
|
+
eventId: `usage:${id}`,
|
|
6511
|
+
tokenDelta: input + output + reasoning + cacheRead + cacheWrite,
|
|
6512
|
+
cost: cost !== undefined && cost > 0 ? cost : null,
|
|
6374
6513
|
createdAt: new Date(completedAt).toISOString()
|
|
6375
6514
|
};
|
|
6376
6515
|
};
|
|
6377
|
-
var toCompletedUsageFromEvent = (event) => {
|
|
6378
|
-
if (event.type !== "message.updated")
|
|
6379
|
-
return null;
|
|
6380
|
-
return toCompletedUsageFromMessage(event.properties.info);
|
|
6381
|
-
};
|
|
6382
6516
|
|
|
6383
6517
|
// src/adapters/opencode/vpet-toast.ts
|
|
6518
|
+
var toVpetToastPayload = (data) => {
|
|
6519
|
+
if (typeof data !== "object" || data === null || Array.isArray(data))
|
|
6520
|
+
return;
|
|
6521
|
+
const { title, message, variant, duration } = data;
|
|
6522
|
+
if (typeof title !== "string" || typeof message !== "string" || variant !== "info" && variant !== "success" && variant !== "warning" || typeof duration !== "number") {
|
|
6523
|
+
return;
|
|
6524
|
+
}
|
|
6525
|
+
return { title, message, variant, duration };
|
|
6526
|
+
};
|
|
6384
6527
|
var assertNever = (value) => {
|
|
6385
6528
|
throw new Error(`Unhandled VPet toast event: ${JSON.stringify(value)}`);
|
|
6386
6529
|
};
|
|
@@ -6444,9 +6587,24 @@ var createBestEffortVpetToastNotifier = (transport) => {
|
|
|
6444
6587
|
});
|
|
6445
6588
|
};
|
|
6446
6589
|
|
|
6447
|
-
// src/adapters/opencode/
|
|
6590
|
+
// src/adapters/opencode/server-plugin.ts
|
|
6448
6591
|
var noOpNotifier = async () => {};
|
|
6449
6592
|
var EMPTY_CATALOG = Object.freeze({ nodes: Object.freeze([]), byId: new Map });
|
|
6593
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6594
|
+
var getStringOption = (options, name) => {
|
|
6595
|
+
const value = options[name];
|
|
6596
|
+
return typeof value === "string" ? value : undefined;
|
|
6597
|
+
};
|
|
6598
|
+
var toEvolutionThresholds = (settings) => Object.freeze({
|
|
6599
|
+
0: settings.stageThresholds.egg,
|
|
6600
|
+
1: settings.stageThresholds.babyI,
|
|
6601
|
+
2: settings.stageThresholds.babyII,
|
|
6602
|
+
3: settings.stageThresholds.child,
|
|
6603
|
+
4: settings.stageThresholds.adult,
|
|
6604
|
+
5: settings.stageThresholds.perfect,
|
|
6605
|
+
6: settings.stageThresholds.ultimate,
|
|
6606
|
+
7: settings.stageThresholds.superUltimate
|
|
6607
|
+
});
|
|
6450
6608
|
var toToastEvent = (result) => {
|
|
6451
6609
|
switch (result.kind) {
|
|
6452
6610
|
case "applied":
|
|
@@ -6484,172 +6642,226 @@ var requiresCatalog = (event) => {
|
|
|
6484
6642
|
}
|
|
6485
6643
|
};
|
|
6486
6644
|
var createCommandConfig = () => ({
|
|
6487
|
-
"vpet-spawn": {
|
|
6488
|
-
"vpet-freeze": {
|
|
6489
|
-
"vpet-unfreeze": {
|
|
6490
|
-
"vpet-set": {
|
|
6645
|
+
"vpet-spawn": { description: "Spawn a new virtual pet." },
|
|
6646
|
+
"vpet-freeze": { description: "Freeze virtual pet progression." },
|
|
6647
|
+
"vpet-unfreeze": { description: "Unfreeze virtual pet progression." },
|
|
6648
|
+
"vpet-set": { description: "Set the virtual pet to a Digimon ID." }
|
|
6491
6649
|
});
|
|
6492
|
-
var
|
|
6650
|
+
var toEventSessionID = (data) => {
|
|
6651
|
+
if (!isRecord4(data))
|
|
6652
|
+
return;
|
|
6653
|
+
const { sessionID } = data;
|
|
6654
|
+
return typeof sessionID === "string" && sessionID.length > 0 ? sessionID : undefined;
|
|
6655
|
+
};
|
|
6656
|
+
var toReconcileSessionID = (event) => {
|
|
6657
|
+
if (!isRecord4(event))
|
|
6658
|
+
return;
|
|
6659
|
+
const { type, data } = event;
|
|
6660
|
+
if (typeof type !== "string")
|
|
6661
|
+
return;
|
|
6662
|
+
switch (type) {
|
|
6663
|
+
case "session.execution.succeeded":
|
|
6664
|
+
case "session.idle":
|
|
6665
|
+
return toEventSessionID(data);
|
|
6666
|
+
case "session.status": {
|
|
6667
|
+
if (!isRecord4(data))
|
|
6668
|
+
return;
|
|
6669
|
+
const { status } = data;
|
|
6670
|
+
if (!isRecord4(status))
|
|
6671
|
+
return;
|
|
6672
|
+
const { type: statusType } = status;
|
|
6673
|
+
if (statusType !== "idle")
|
|
6674
|
+
return;
|
|
6675
|
+
return toEventSessionID(data);
|
|
6676
|
+
}
|
|
6677
|
+
default:
|
|
6678
|
+
return;
|
|
6679
|
+
}
|
|
6680
|
+
};
|
|
6681
|
+
var consumeVpetSessionEvents = async (input) => {
|
|
6682
|
+
for await (const event of input.event.subscribe({ signal: input.signal })) {
|
|
6683
|
+
const sessionID = toReconcileSessionID(event);
|
|
6684
|
+
if (sessionID === undefined)
|
|
6685
|
+
continue;
|
|
6686
|
+
const messages = await input.fetchMessages(sessionID);
|
|
6687
|
+
await input.reconcile(messages);
|
|
6688
|
+
}
|
|
6689
|
+
};
|
|
6690
|
+
var createVpetCommandDefinitions = ({
|
|
6493
6691
|
repository,
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
evolutionThresholds = STAGE_GAUGE_THRESHOLDS,
|
|
6497
|
-
fetchMessages,
|
|
6498
|
-
language = "jp",
|
|
6499
|
-
notificationsEnabled = true,
|
|
6500
|
-
loadCatalog = loadDigimonCatalog,
|
|
6501
|
-
notify = noOpNotifier
|
|
6692
|
+
emit,
|
|
6693
|
+
loadCatalog
|
|
6502
6694
|
}) => {
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
await notify(payload);
|
|
6512
|
-
}).then(() => {
|
|
6513
|
-
return;
|
|
6514
|
-
}, () => {
|
|
6515
|
-
return;
|
|
6516
|
-
});
|
|
6517
|
-
};
|
|
6518
|
-
return {
|
|
6519
|
-
async config(config) {
|
|
6520
|
-
config.command = {
|
|
6521
|
-
...config.command,
|
|
6522
|
-
...createCommandConfig()
|
|
6523
|
-
};
|
|
6695
|
+
const config = createCommandConfig();
|
|
6696
|
+
return [
|
|
6697
|
+
{
|
|
6698
|
+
name: "vpet-spawn",
|
|
6699
|
+
description: config["vpet-spawn"].description,
|
|
6700
|
+
execute: async () => {
|
|
6701
|
+
await emit(await runVpetSpawnCommand(repository, { createdAt: new Date().toISOString() }));
|
|
6702
|
+
}
|
|
6524
6703
|
},
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
await notifyEvent(toCommandToastEvent(result.event));
|
|
6538
|
-
}
|
|
6539
|
-
return;
|
|
6540
|
-
case "vpet-freeze":
|
|
6541
|
-
{
|
|
6542
|
-
const result = await runVpetFreezeCommand(repository, { sessionID: input.sessionID, messageID });
|
|
6543
|
-
output.parts.push(...result.parts);
|
|
6544
|
-
if (result.event !== undefined)
|
|
6545
|
-
await notifyEvent(toCommandToastEvent(result.event));
|
|
6546
|
-
}
|
|
6547
|
-
return;
|
|
6548
|
-
case "vpet-unfreeze":
|
|
6549
|
-
{
|
|
6550
|
-
const result = await runVpetUnfreezeCommand(repository, { sessionID: input.sessionID, messageID });
|
|
6551
|
-
output.parts.push(...result.parts);
|
|
6552
|
-
if (result.event !== undefined)
|
|
6553
|
-
await notifyEvent(toCommandToastEvent(result.event));
|
|
6554
|
-
}
|
|
6555
|
-
return;
|
|
6556
|
-
case "vpet-set":
|
|
6557
|
-
{
|
|
6558
|
-
let catalog;
|
|
6559
|
-
const loadCommandCatalog = async () => catalog ??= await loadCatalog();
|
|
6560
|
-
const result = await runVpetSetCommand(repository, { sessionID: input.sessionID, messageID, arguments: input.arguments }, loadCommandCatalog);
|
|
6561
|
-
output.parts.push(...result.parts);
|
|
6562
|
-
if (result.event !== undefined)
|
|
6563
|
-
await notifyEvent(toCommandToastEvent(result.event), await loadCommandCatalog());
|
|
6564
|
-
}
|
|
6565
|
-
return;
|
|
6566
|
-
default:
|
|
6567
|
-
return;
|
|
6704
|
+
{
|
|
6705
|
+
name: "vpet-freeze",
|
|
6706
|
+
description: config["vpet-freeze"].description,
|
|
6707
|
+
execute: async () => {
|
|
6708
|
+
await emit(await runVpetFreezeCommand(repository));
|
|
6709
|
+
}
|
|
6710
|
+
},
|
|
6711
|
+
{
|
|
6712
|
+
name: "vpet-unfreeze",
|
|
6713
|
+
description: config["vpet-unfreeze"].description,
|
|
6714
|
+
execute: async () => {
|
|
6715
|
+
await emit(await runVpetUnfreezeCommand(repository));
|
|
6568
6716
|
}
|
|
6569
6717
|
},
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6718
|
+
{
|
|
6719
|
+
name: "vpet-set",
|
|
6720
|
+
description: config["vpet-set"].description,
|
|
6721
|
+
execute: async ({ prompt }) => {
|
|
6722
|
+
let catalog;
|
|
6723
|
+
const loadCommandCatalog = async () => catalog ??= await loadCatalog();
|
|
6724
|
+
const result = await runVpetSetCommand(repository, { arguments: prompt.text }, loadCommandCatalog);
|
|
6725
|
+
await emit(result, result.event === undefined ? undefined : await loadCommandCatalog());
|
|
6726
|
+
}
|
|
6727
|
+
}
|
|
6728
|
+
];
|
|
6729
|
+
};
|
|
6730
|
+
var setupVpetServer = async (context, dependencies) => {
|
|
6731
|
+
const appDataRoot = getStringOption(context.options, "appDataRoot");
|
|
6732
|
+
const databasePath = getStringOption(context.options, "databasePath");
|
|
6733
|
+
const settings = await (dependencies.loadSettings ?? loadGlobalVpetSettings)();
|
|
6734
|
+
const repository = await dependencies.openRepository({
|
|
6735
|
+
...appDataRoot === undefined ? {} : { appDataRoot },
|
|
6736
|
+
...databasePath === undefined ? {} : { databasePath }
|
|
6737
|
+
});
|
|
6738
|
+
try {
|
|
6739
|
+
const language = dependencies.language ?? settings.language;
|
|
6740
|
+
const notificationsEnabled = dependencies.notificationsEnabled ?? settings.notifications;
|
|
6741
|
+
const evolutionSelector = dependencies.evolutionSelector ?? Math.random;
|
|
6742
|
+
const evolutionThresholds = dependencies.evolutionThresholds ?? toEvolutionThresholds(settings);
|
|
6743
|
+
const loadCatalog = dependencies.loadCatalog ?? loadDigimonCatalog;
|
|
6744
|
+
const fetchMessages = dependencies.fetchMessages ?? createSessionMessagesFetcher(context);
|
|
6745
|
+
let notify = dependencies.notify ?? noOpNotifier;
|
|
6746
|
+
if (dependencies.notify === undefined && notificationsEnabled) {
|
|
6747
|
+
const registration = await context.rpc.register(VpetRpc, {});
|
|
6748
|
+
notify = createBestEffortVpetToastNotifier(async (payload) => {
|
|
6749
|
+
await registration.events.emit("toast", payload);
|
|
6750
|
+
});
|
|
6751
|
+
}
|
|
6752
|
+
const notifyEvent = async (event, catalog) => {
|
|
6753
|
+
if (!notificationsEnabled)
|
|
6754
|
+
return;
|
|
6755
|
+
await Promise.resolve().then(async () => {
|
|
6756
|
+
const resolvedCatalog = catalog ?? (requiresCatalog(event) ? await loadCatalog() : EMPTY_CATALOG);
|
|
6757
|
+
const payload = formatVpetToast(event, language, resolvedCatalog);
|
|
6758
|
+
if (payload !== undefined)
|
|
6759
|
+
await notify(payload);
|
|
6760
|
+
}).then(() => {
|
|
6761
|
+
return;
|
|
6762
|
+
}, () => {
|
|
6763
|
+
return;
|
|
6764
|
+
});
|
|
6765
|
+
};
|
|
6766
|
+
const emit = async (result, catalog) => {
|
|
6767
|
+
if (result.event !== undefined) {
|
|
6768
|
+
await notifyEvent(toCommandToastEvent(result.event), catalog);
|
|
6769
|
+
return;
|
|
6770
|
+
}
|
|
6771
|
+
if (!notificationsEnabled)
|
|
6772
|
+
return;
|
|
6773
|
+
await Promise.resolve().then(() => notify({ title: "VPet", message: result.message, variant: result.variant, duration: 3000 })).then(() => {
|
|
6774
|
+
return;
|
|
6775
|
+
}, () => {
|
|
6776
|
+
return;
|
|
6777
|
+
});
|
|
6778
|
+
};
|
|
6779
|
+
const definitions = createVpetCommandDefinitions({ repository, emit, loadCatalog });
|
|
6780
|
+
await context.command.transform((editor) => {
|
|
6781
|
+
for (const definition of definitions)
|
|
6782
|
+
editor.add(definition);
|
|
6783
|
+
});
|
|
6784
|
+
const usageAbortController = new AbortController;
|
|
6785
|
+
let reconciliationFailed = false;
|
|
6786
|
+
let reconciliationFailure;
|
|
6787
|
+
const reconciliation = consumeVpetSessionEvents({
|
|
6788
|
+
event: context.event,
|
|
6789
|
+
signal: usageAbortController.signal,
|
|
6790
|
+
fetchMessages,
|
|
6791
|
+
reconcile: async (messages) => {
|
|
6792
|
+
const usages = messages.flatMap((message) => {
|
|
6793
|
+
const usage = toCompletedUsageFromMessage(message);
|
|
6794
|
+
return usage === null ? [] : [usage];
|
|
6795
|
+
});
|
|
6796
|
+
if (usages.length === 0)
|
|
6797
|
+
return;
|
|
6798
|
+
const catalog = await loadCatalog();
|
|
6799
|
+
const results = reconcileUsage({
|
|
6800
|
+
usages,
|
|
6801
|
+
ledger: repository,
|
|
6802
|
+
digimonById: catalog.byId,
|
|
6803
|
+
selector: evolutionSelector,
|
|
6804
|
+
thresholds: evolutionThresholds
|
|
6805
|
+
});
|
|
6806
|
+
for (const result of results) {
|
|
6584
6807
|
const toastEvent = toToastEvent(result);
|
|
6585
6808
|
if (toastEvent !== undefined)
|
|
6586
6809
|
await notifyEvent(toastEvent, catalog);
|
|
6587
|
-
return;
|
|
6588
|
-
}
|
|
6589
|
-
case "session.idle": {
|
|
6590
|
-
if (fetchMessages === undefined)
|
|
6591
|
-
return;
|
|
6592
|
-
const messages = await fetchMessages(event.properties.sessionID);
|
|
6593
|
-
const usages = messages.flatMap((message) => {
|
|
6594
|
-
const usage = toCompletedUsageFromMessage(message);
|
|
6595
|
-
return usage === null ? [] : [usage];
|
|
6596
|
-
});
|
|
6597
|
-
const catalog = await loadCatalog();
|
|
6598
|
-
const results = reconcileUsage({
|
|
6599
|
-
usages,
|
|
6600
|
-
ledger: repository,
|
|
6601
|
-
digimonById: catalog.byId,
|
|
6602
|
-
selector: evolutionSelector,
|
|
6603
|
-
thresholds: evolutionThresholds
|
|
6604
|
-
});
|
|
6605
|
-
for (const result of results) {
|
|
6606
|
-
const toastEvent = toToastEvent(result);
|
|
6607
|
-
if (toastEvent !== undefined)
|
|
6608
|
-
await notifyEvent(toastEvent, catalog);
|
|
6609
|
-
}
|
|
6610
|
-
return;
|
|
6611
6810
|
}
|
|
6612
|
-
default:
|
|
6613
|
-
return;
|
|
6614
6811
|
}
|
|
6615
|
-
}
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6812
|
+
}).catch((error) => {
|
|
6813
|
+
reconciliationFailed = true;
|
|
6814
|
+
reconciliationFailure = error;
|
|
6815
|
+
});
|
|
6816
|
+
let closePromise;
|
|
6817
|
+
const closeRepository = () => closePromise ??= repository.close();
|
|
6818
|
+
return async () => {
|
|
6819
|
+
usageAbortController.abort();
|
|
6820
|
+
await reconciliation;
|
|
6821
|
+
let closeFailure;
|
|
6822
|
+
try {
|
|
6823
|
+
await closeRepository();
|
|
6824
|
+
} catch (error) {
|
|
6825
|
+
closeFailure = error;
|
|
6826
|
+
}
|
|
6827
|
+
if (reconciliationFailed && closeFailure !== undefined) {
|
|
6828
|
+
throw new AggregateError([reconciliationFailure, closeFailure], "VPet usage reconciliation and repository cleanup failed.");
|
|
6829
|
+
}
|
|
6830
|
+
if (reconciliationFailed)
|
|
6831
|
+
throw reconciliationFailure;
|
|
6832
|
+
if (closeFailure !== undefined)
|
|
6833
|
+
throw closeFailure;
|
|
6834
|
+
};
|
|
6835
|
+
} catch (setupFailure) {
|
|
6836
|
+
try {
|
|
6837
|
+
await repository.close();
|
|
6838
|
+
} catch (closeFailure) {
|
|
6839
|
+
throw new AggregateError([setupFailure, closeFailure], "VPet setup and repository cleanup failed.");
|
|
6619
6840
|
}
|
|
6620
|
-
|
|
6621
|
-
}
|
|
6622
|
-
|
|
6623
|
-
// src/adapters/opencode/session-messages.ts
|
|
6624
|
-
var createSessionMessagesFetcher = (client) => {
|
|
6625
|
-
return async (sessionID) => {
|
|
6626
|
-
const result = await client.session.messages({ path: { id: sessionID } });
|
|
6627
|
-
if (result.data === undefined)
|
|
6628
|
-
throw new Error(`Unable to list messages for idle session ${sessionID}`);
|
|
6629
|
-
return result.data.map(({ info }) => info);
|
|
6630
|
-
};
|
|
6841
|
+
throw setupFailure;
|
|
6842
|
+
}
|
|
6631
6843
|
};
|
|
6632
6844
|
|
|
6633
6845
|
// src/adapters/sqlite/app-data-path.ts
|
|
6634
|
-
import { homedir } from "os";
|
|
6635
|
-
import { join } from "path";
|
|
6846
|
+
import { homedir as homedir2 } from "os";
|
|
6847
|
+
import { join as join2 } from "path";
|
|
6636
6848
|
var APP_DIRECTORY_NAME = "opencode-vpet";
|
|
6637
6849
|
var DATABASE_FILE_NAME = "pet.db";
|
|
6638
6850
|
var resolveDefaultAppDataRoot = () => {
|
|
6639
6851
|
if (process.platform === "darwin") {
|
|
6640
|
-
return
|
|
6852
|
+
return join2(homedir2(), "Library", "Application Support");
|
|
6641
6853
|
}
|
|
6642
6854
|
if (process.platform === "win32") {
|
|
6643
6855
|
const { APPDATA } = process.env;
|
|
6644
|
-
return APPDATA ??
|
|
6856
|
+
return APPDATA ?? join2(homedir2(), "AppData", "Roaming");
|
|
6645
6857
|
}
|
|
6646
|
-
return
|
|
6858
|
+
return join2(homedir2(), ".local", "share");
|
|
6647
6859
|
};
|
|
6648
6860
|
var resolveHostDataDirectory = (options = {}) => {
|
|
6649
|
-
return
|
|
6861
|
+
return join2(options.appDataRoot ?? resolveDefaultAppDataRoot(), APP_DIRECTORY_NAME);
|
|
6650
6862
|
};
|
|
6651
6863
|
var resolveHostDatabasePath = (options = {}) => {
|
|
6652
|
-
return
|
|
6864
|
+
return join2(resolveHostDataDirectory(options), DATABASE_FILE_NAME);
|
|
6653
6865
|
};
|
|
6654
6866
|
|
|
6655
6867
|
// src/adapters/sqlite/bun-sqlite-driver.ts
|
|
@@ -7101,159 +7313,12 @@ var createSqliteVpetRepository = async (options = {}) => {
|
|
|
7101
7313
|
return store;
|
|
7102
7314
|
};
|
|
7103
7315
|
|
|
7104
|
-
// src/config/global-vpet-settings.ts
|
|
7105
|
-
import { readFile } from "fs/promises";
|
|
7106
|
-
import { homedir as homedir2 } from "os";
|
|
7107
|
-
import { join as join2 } from "path";
|
|
7108
|
-
|
|
7109
|
-
// src/config/defaults.ts
|
|
7110
|
-
var EN_STAGE_LABELS = Object.freeze({
|
|
7111
|
-
egg: "DigiEgg",
|
|
7112
|
-
babyI: "In Training I",
|
|
7113
|
-
babyII: "In Training II",
|
|
7114
|
-
child: "Rookie",
|
|
7115
|
-
adult: "Champion",
|
|
7116
|
-
perfect: "Ultimate",
|
|
7117
|
-
ultimate: "Mega",
|
|
7118
|
-
superUltimate: "Ultra"
|
|
7119
|
-
});
|
|
7120
|
-
var JP_STAGE_LABELS = Object.freeze({
|
|
7121
|
-
egg: "Digitama",
|
|
7122
|
-
babyI: "Baby I",
|
|
7123
|
-
babyII: "Baby II",
|
|
7124
|
-
child: "Child",
|
|
7125
|
-
adult: "Adult",
|
|
7126
|
-
perfect: "Perfect",
|
|
7127
|
-
ultimate: "Ultimate",
|
|
7128
|
-
superUltimate: "SuperUltimate"
|
|
7129
|
-
});
|
|
7130
|
-
var VPET_STAGE_LABELS = Object.freeze({
|
|
7131
|
-
en: EN_STAGE_LABELS,
|
|
7132
|
-
jp: JP_STAGE_LABELS
|
|
7133
|
-
});
|
|
7134
|
-
var DEFAULT_STAGE_THRESHOLDS = Object.freeze({
|
|
7135
|
-
egg: 500000,
|
|
7136
|
-
babyI: 1e6,
|
|
7137
|
-
babyII: 2000000,
|
|
7138
|
-
child: 4000000,
|
|
7139
|
-
adult: 7500000,
|
|
7140
|
-
perfect: 12500000,
|
|
7141
|
-
ultimate: 20000000,
|
|
7142
|
-
superUltimate: 30000000
|
|
7143
|
-
});
|
|
7144
|
-
var DEFAULT_VPET_SETTINGS = Object.freeze({
|
|
7145
|
-
language: "jp",
|
|
7146
|
-
notifications: true,
|
|
7147
|
-
stageLabels: VPET_STAGE_LABELS,
|
|
7148
|
-
stageThresholds: DEFAULT_STAGE_THRESHOLDS
|
|
7149
|
-
});
|
|
7150
|
-
|
|
7151
|
-
// src/config/normalize.ts
|
|
7152
|
-
var isRecord2 = (value) => {
|
|
7153
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7154
|
-
};
|
|
7155
|
-
var isLanguage = (value) => value === "en" || value === "jp";
|
|
7156
|
-
var isBoolean = (value) => typeof value === "boolean";
|
|
7157
|
-
var isThreshold = (value) => {
|
|
7158
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
7159
|
-
};
|
|
7160
|
-
var read = (input, key) => {
|
|
7161
|
-
try {
|
|
7162
|
-
return input[key];
|
|
7163
|
-
} catch {
|
|
7164
|
-
return;
|
|
7165
|
-
}
|
|
7166
|
-
};
|
|
7167
|
-
var normalizeThreshold = (input, key) => {
|
|
7168
|
-
const value = read(input, key);
|
|
7169
|
-
return isThreshold(value) ? value : DEFAULT_STAGE_THRESHOLDS[key];
|
|
7170
|
-
};
|
|
7171
|
-
var normalizeVpetSettings = (input) => {
|
|
7172
|
-
try {
|
|
7173
|
-
if (!isRecord2(input))
|
|
7174
|
-
return DEFAULT_VPET_SETTINGS;
|
|
7175
|
-
const thresholdInput = read(input, "stageThresholds");
|
|
7176
|
-
const thresholds = isRecord2(thresholdInput) ? thresholdInput : {};
|
|
7177
|
-
const stageThresholds = Object.freeze({
|
|
7178
|
-
egg: normalizeThreshold(thresholds, "egg"),
|
|
7179
|
-
babyI: normalizeThreshold(thresholds, "babyI"),
|
|
7180
|
-
babyII: normalizeThreshold(thresholds, "babyII"),
|
|
7181
|
-
child: normalizeThreshold(thresholds, "child"),
|
|
7182
|
-
adult: normalizeThreshold(thresholds, "adult"),
|
|
7183
|
-
perfect: normalizeThreshold(thresholds, "perfect"),
|
|
7184
|
-
ultimate: normalizeThreshold(thresholds, "ultimate"),
|
|
7185
|
-
superUltimate: normalizeThreshold(thresholds, "superUltimate")
|
|
7186
|
-
});
|
|
7187
|
-
const language = read(input, "language");
|
|
7188
|
-
const notifications = read(input, "notifications");
|
|
7189
|
-
return Object.freeze({
|
|
7190
|
-
language: isLanguage(language) ? language : DEFAULT_VPET_SETTINGS.language,
|
|
7191
|
-
notifications: isBoolean(notifications) ? notifications : DEFAULT_VPET_SETTINGS.notifications,
|
|
7192
|
-
stageLabels: DEFAULT_VPET_SETTINGS.stageLabels,
|
|
7193
|
-
stageThresholds
|
|
7194
|
-
});
|
|
7195
|
-
} catch {
|
|
7196
|
-
return DEFAULT_VPET_SETTINGS;
|
|
7197
|
-
}
|
|
7198
|
-
};
|
|
7199
|
-
|
|
7200
|
-
// src/config/global-vpet-settings.ts
|
|
7201
|
-
var resolveGlobalVpetConfigPath = (options = {}) => {
|
|
7202
|
-
const pathApi = options.pathApi ?? { join: join2 };
|
|
7203
|
-
const env = options.env ?? process.env;
|
|
7204
|
-
const home = options.home ?? homedir2();
|
|
7205
|
-
if ((options.platform ?? process.platform) === "win32") {
|
|
7206
|
-
const { APPDATA } = env;
|
|
7207
|
-
return pathApi.join(APPDATA ?? pathApi.join(home, "AppData", "Roaming"), "opencode-vpet.json");
|
|
7208
|
-
}
|
|
7209
|
-
return pathApi.join(home, ".config", "opencode-vpet.json");
|
|
7210
|
-
};
|
|
7211
|
-
var loadGlobalVpetSettings = async (options = {}) => {
|
|
7212
|
-
try {
|
|
7213
|
-
return normalizeVpetSettings(JSON.parse(await readFile(resolveGlobalVpetConfigPath(options), "utf8")));
|
|
7214
|
-
} catch {
|
|
7215
|
-
return DEFAULT_VPET_SETTINGS;
|
|
7216
|
-
}
|
|
7217
|
-
};
|
|
7218
|
-
|
|
7219
7316
|
// src/index.ts
|
|
7220
|
-
var
|
|
7221
|
-
const value = options?.[name];
|
|
7222
|
-
return typeof value === "string" ? value : undefined;
|
|
7223
|
-
};
|
|
7224
|
-
var toEvolutionThresholds = (settings) => Object.freeze({
|
|
7225
|
-
0: settings.stageThresholds.egg,
|
|
7226
|
-
1: settings.stageThresholds.babyI,
|
|
7227
|
-
2: settings.stageThresholds.babyII,
|
|
7228
|
-
3: settings.stageThresholds.child,
|
|
7229
|
-
4: settings.stageThresholds.adult,
|
|
7230
|
-
5: settings.stageThresholds.perfect,
|
|
7231
|
-
6: settings.stageThresholds.ultimate,
|
|
7232
|
-
7: settings.stageThresholds.superUltimate
|
|
7233
|
-
});
|
|
7234
|
-
var plugin = async (input, options) => {
|
|
7235
|
-
const appDataRoot = getStringOption(options, "appDataRoot");
|
|
7236
|
-
const databasePath = getStringOption(options, "databasePath");
|
|
7237
|
-
const settings = await loadGlobalVpetSettings();
|
|
7238
|
-
const repository = await createSqliteVpetRepository({
|
|
7239
|
-
...appDataRoot === undefined ? {} : { appDataRoot },
|
|
7240
|
-
...databasePath === undefined ? {} : { databasePath }
|
|
7241
|
-
});
|
|
7242
|
-
return createServerHooks({
|
|
7243
|
-
repository,
|
|
7244
|
-
resource: repository,
|
|
7245
|
-
evolutionThresholds: toEvolutionThresholds(settings),
|
|
7246
|
-
fetchMessages: createSessionMessagesFetcher(input.client),
|
|
7247
|
-
language: settings.language,
|
|
7248
|
-
notificationsEnabled: settings.notifications,
|
|
7249
|
-
notify: createBestEffortVpetToastNotifier(async (payload) => (await input.client.tui.showToast({ body: payload })).data ?? false)
|
|
7250
|
-
});
|
|
7251
|
-
};
|
|
7252
|
-
var entryPlugin = {
|
|
7317
|
+
var plugin = Plugin.define({
|
|
7253
7318
|
id: "opencode-vpet",
|
|
7254
|
-
|
|
7255
|
-
};
|
|
7256
|
-
var src_default =
|
|
7319
|
+
setup: (context) => setupVpetServer(context, { openRepository: createSqliteVpetRepository })
|
|
7320
|
+
});
|
|
7321
|
+
var src_default = plugin;
|
|
7257
7322
|
export {
|
|
7258
7323
|
plugin,
|
|
7259
7324
|
src_default as default,
|
|
@@ -7261,5 +7326,5 @@ export {
|
|
|
7261
7326
|
DIGIMON_DATA
|
|
7262
7327
|
};
|
|
7263
7328
|
|
|
7264
|
-
//# debugId=
|
|
7329
|
+
//# debugId=73759DE8C0C8860264756E2164756E21
|
|
7265
7330
|
//# sourceMappingURL=index.js.map
|