@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.
Files changed (44) hide show
  1. package/README.md +4 -5
  2. package/dist/adapters/opencode/server-plugin.d.ts +78 -0
  3. package/dist/adapters/opencode/server-plugin.d.ts.map +1 -0
  4. package/dist/adapters/opencode/session-messages.d.ts +4 -9
  5. package/dist/adapters/opencode/session-messages.d.ts.map +1 -1
  6. package/dist/adapters/opencode/usage-event-mapper.d.ts +6 -3
  7. package/dist/adapters/opencode/usage-event-mapper.d.ts.map +1 -1
  8. package/dist/adapters/opencode/vpet-toast.d.ts +4 -2
  9. package/dist/adapters/opencode/vpet-toast.d.ts.map +1 -1
  10. package/dist/cli/install.d.ts.map +1 -1
  11. package/dist/cli.js +3 -3
  12. package/dist/cli.js.map +4 -4
  13. package/dist/commands/vpet-command-result.d.ts +2 -2
  14. package/dist/commands/vpet-command-result.d.ts.map +1 -1
  15. package/dist/commands/vpet-freeze.d.ts +1 -5
  16. package/dist/commands/vpet-freeze.d.ts.map +1 -1
  17. package/dist/commands/vpet-set.d.ts +0 -2
  18. package/dist/commands/vpet-set.d.ts.map +1 -1
  19. package/dist/commands/vpet-spawn.d.ts +0 -2
  20. package/dist/commands/vpet-spawn.d.ts.map +1 -1
  21. package/dist/commands/vpet-unfreeze.d.ts +1 -5
  22. package/dist/commands/vpet-unfreeze.d.ts.map +1 -1
  23. package/dist/index.d.ts +4 -8
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +535 -470
  26. package/dist/index.js.map +22 -21
  27. package/dist/package.json +10 -0
  28. package/dist/rpc.d.ts +32 -0
  29. package/dist/rpc.d.ts.map +1 -0
  30. package/dist/tui/theme.d.ts +27 -0
  31. package/dist/tui/theme.d.ts.map +1 -0
  32. package/dist/tui/vpet-command-layer.d.ts +32 -5
  33. package/dist/tui/vpet-command-layer.d.ts.map +1 -1
  34. package/dist/tui/vpet-dex-dialog.d.ts +2 -2
  35. package/dist/tui/vpet-dex-dialog.d.ts.map +1 -1
  36. package/dist/tui/vpet-history-dialog.d.ts +2 -2
  37. package/dist/tui/vpet-history-dialog.d.ts.map +1 -1
  38. package/dist/tui.d.ts +22 -12
  39. package/dist/tui.d.ts.map +1 -1
  40. package/dist/tui.js +203 -66
  41. package/dist/tui.js.map +10 -7
  42. package/package.json +9 -12
  43. package/dist/adapters/opencode/create-server-hooks.d.ts +0 -39
  44. 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, context) => {
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 { parts, event: { kind: "frozen" } };
109
+ return { message: "VPet frozen.", variant: "info", event: { kind: "frozen" } };
20
110
  case "already_frozen":
21
- return { parts };
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 { parts: [textPart(context, "Usage: /vpet-set <id>.")] };
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 { parts: [textPart(context, "Usage: /vpet-set <id>.")] };
129
+ return { message: "Usage: /vpet-set <id>.", variant: "warning" };
75
130
  const requestedId = tokens[0];
76
131
  if (requestedId === undefined)
77
- return { parts: [textPart(context, "Usage: /vpet-set <id>.")] };
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 { parts: [textPart(context, `Unknown Digimon ID: ${requestedId}.`)] };
135
+ return { message: `Unknown Digimon ID: ${requestedId}.`, variant: "warning" };
81
136
  const outcome = setVpetCheatNode(control, node.id);
82
- const parts = [textPart(context, `VPet set to ${node.nameEn} (${node.id}).`)];
137
+ const message = `VPet set to ${node.nameEn} (${node.id}).`;
83
138
  switch (outcome.kind) {
84
139
  case "set":
85
- return { parts, event: { kind: "set", nodeId: node.id } };
140
+ return { message, variant: "info", event: { kind: "set", nodeId: node.id } };
86
141
  case "already_set":
87
- return { parts };
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, context) => {
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 { parts, event: { kind: "unfrozen" } };
179
+ return { message: "VPet unfrozen.", variant: "info", event: { kind: "unfrozen" } };
113
180
  case "already_unfrozen":
114
- return { parts };
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/application/use-cases/reconcile-usage.ts
6345
- var reconcileUsage = ({
6346
- usages,
6347
- ledger,
6348
- digimonById,
6349
- selector,
6350
- thresholds
6351
- }) => {
6352
- const outcomes = [];
6353
- for (const usage of usages) {
6354
- outcomes.push(recordUsage({ usage, ledger, digimonById, selector, thresholds }));
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
- return outcomes;
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 isCompletedAssistantMessage = (message) => {
6361
- return message.role === "assistant" && "time" in message && message.time.completed !== undefined;
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 (!isCompletedAssistantMessage(message))
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 completedAt = message.time.completed;
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:${message.id}`,
6371
- eventId: `usage:${message.id}`,
6372
- tokenDelta: message.tokens.input + message.tokens.output + message.tokens.reasoning + message.tokens.cache.read + message.tokens.cache.write,
6373
- cost: Number.isFinite(message.cost) && message.cost > 0 ? message.cost : null,
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/create-server-hooks.ts
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": { template: "Spawn a new virtual pet." },
6488
- "vpet-freeze": { template: "Freeze virtual pet progression." },
6489
- "vpet-unfreeze": { template: "Unfreeze virtual pet progression." },
6490
- "vpet-set": { template: "Set the virtual pet to a Digimon ID: $ARGUMENTS" }
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 createServerHooks = ({
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
- resource,
6495
- evolutionSelector = Math.random,
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
- let closePromise;
6504
- const notifyEvent = async (event, catalog) => {
6505
- if (!notificationsEnabled)
6506
- return;
6507
- await Promise.resolve().then(async () => {
6508
- const resolvedCatalog = catalog ?? (requiresCatalog(event) ? await loadCatalog() : EMPTY_CATALOG);
6509
- const payload = formatVpetToast(event, language, resolvedCatalog);
6510
- if (payload !== undefined)
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
- async "command.execute.before"(input, output) {
6526
- const messageID = `vpet-${input.command}-${input.sessionID}-${crypto.randomUUID()}`;
6527
- switch (input.command) {
6528
- case "vpet-spawn":
6529
- {
6530
- const result = await runVpetSpawnCommand(repository, {
6531
- sessionID: input.sessionID,
6532
- messageID,
6533
- createdAt: new Date().toISOString()
6534
- });
6535
- output.parts.push(...result.parts);
6536
- if (result.event !== undefined)
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
- async event({ event }) {
6571
- switch (event.type) {
6572
- case "message.updated": {
6573
- const usage = toCompletedUsageFromEvent(event);
6574
- if (usage === null)
6575
- return;
6576
- const catalog = await loadCatalog();
6577
- const result = recordUsage({
6578
- usage,
6579
- ledger: repository,
6580
- digimonById: catalog.byId,
6581
- selector: evolutionSelector,
6582
- thresholds: evolutionThresholds
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
- async dispose() {
6617
- closePromise ??= resource.close();
6618
- await closePromise;
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 join(homedir(), "Library", "Application Support");
6852
+ return join2(homedir2(), "Library", "Application Support");
6641
6853
  }
6642
6854
  if (process.platform === "win32") {
6643
6855
  const { APPDATA } = process.env;
6644
- return APPDATA ?? join(homedir(), "AppData", "Roaming");
6856
+ return APPDATA ?? join2(homedir2(), "AppData", "Roaming");
6645
6857
  }
6646
- return join(homedir(), ".local", "share");
6858
+ return join2(homedir2(), ".local", "share");
6647
6859
  };
6648
6860
  var resolveHostDataDirectory = (options = {}) => {
6649
- return join(options.appDataRoot ?? resolveDefaultAppDataRoot(), APP_DIRECTORY_NAME);
6861
+ return join2(options.appDataRoot ?? resolveDefaultAppDataRoot(), APP_DIRECTORY_NAME);
6650
6862
  };
6651
6863
  var resolveHostDatabasePath = (options = {}) => {
6652
- return join(resolveHostDataDirectory(options), DATABASE_FILE_NAME);
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 getStringOption = (options, name) => {
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
- server: plugin
7255
- };
7256
- var src_default = entryPlugin;
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=831F7233213F01D764756E2164756E21
7329
+ //# debugId=73759DE8C0C8860264756E2164756E21
7265
7330
  //# sourceMappingURL=index.js.map