@my-life-buddies/cli 0.1.2 → 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.
@@ -32,6 +32,12 @@ var PreviewController = class {
32
32
  #observedActiveRuns = /* @__PURE__ */ new Set();
33
33
  #events = [];
34
34
  #runOrdinal = 0;
35
+ #conversationRevision = 0;
36
+ #resetting = false;
37
+ #sendingMessage = false;
38
+ get resetting() {
39
+ return this.#resetting;
40
+ }
35
41
  #state = Object.freeze({ phase: "idle", revision: 0 });
36
42
  #handle;
37
43
  #simulator;
@@ -71,7 +77,7 @@ var PreviewController = class {
71
77
  return () => this.#simulatorListeners.delete(listener);
72
78
  }
73
79
  simulatorSnapshot() {
74
- return this.#simulatorState;
80
+ return Object.freeze({ ...this.#simulatorState, conversationRevision: this.#conversationRevision });
75
81
  }
76
82
  start() {
77
83
  if (this.#startPromise) return this.#startPromise;
@@ -160,49 +166,87 @@ var PreviewController = class {
160
166
  this.#simulatorListeners.clear();
161
167
  }
162
168
  async simulatorState(signal) {
163
- const state = await this.#requiredSimulator().state({ signal });
164
- return this.#observeSimulatorState(state);
169
+ if (this.#resetting) return this.simulatorSnapshot();
170
+ const simulator = this.#requiredSimulator();
171
+ const revision = this.#conversationRevision;
172
+ const state = await simulator.state({ signal });
173
+ if (!this.#resetting && revision === this.#conversationRevision && simulator === this.#simulator) {
174
+ this.#observeSimulatorState(state);
175
+ }
176
+ return this.simulatorSnapshot();
177
+ }
178
+ async resetSimulator() {
179
+ if (this.#resetting || this.#sendingMessage || this.#simulatorState.activeRun) {
180
+ throw Object.assign(new Error("\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u6216\u64CD\u4F5C\u5B8C\u6210\u540E\u518D\u6E05\u7A7A"), { status: 409, code: "SIMULATOR_BUSY" });
181
+ }
182
+ const simulator = this.#requiredSimulator();
183
+ if (!simulator.reset) {
184
+ throw Object.assign(new Error("\u5F53\u524D\u5E73\u53F0\u6682\u4E0D\u652F\u6301\u6E05\u7A7A\u5BF9\u8BDD"), { status: 501, code: "RESET_UNSUPPORTED", publicMessage: "\u5F53\u524D\u5E73\u53F0\u6682\u4E0D\u652F\u6301\u6E05\u7A7A\u5BF9\u8BDD" });
185
+ }
186
+ this.#resetting = true;
187
+ try {
188
+ await simulator.reset();
189
+ this.#conversationRevision++;
190
+ this.#stopOutputWatchers();
191
+ this.#runs.clear();
192
+ this.#observedActiveRuns.clear();
193
+ this.#events.length = 0;
194
+ this.#runOrdinal = 0;
195
+ this.#lastActiveRunId = void 0;
196
+ this.#messages = Object.freeze([]);
197
+ this.#simulatorState = Object.freeze({ messages: this.#messages });
198
+ this.#notifySimulator();
199
+ return this.simulatorSnapshot();
200
+ } finally {
201
+ this.#resetting = false;
202
+ }
165
203
  }
166
204
  async sendSimulatorMessage(input, signal) {
167
- const started = this.#now();
168
- this.#record({ channel: "client", type: "message.send", summary: "\u53D1\u9001\u4E00\u6761\u79C1\u804A\u6D88\u606F" });
169
- const accepted = await this.#requiredSimulator().send(input, { signal });
170
- if (this.#runs.has(accepted.runId)) return accepted;
171
- const acceptedAt = this.#now();
172
- const latency = Math.max(0, acceptedAt.getTime() - started.getTime());
173
- this.#upsertRun({
174
- runId: accepted.runId,
175
- messageId: accepted.messageId,
176
- status: "accepted",
177
- startedAt: started.toISOString(),
178
- updatedAt: acceptedAt.toISOString(),
179
- acceptLatencyMs: latency
180
- });
181
- this.#lastActiveRunId = accepted.runId;
182
- this.#record({
183
- channel: "app_server",
184
- type: "run.accepted",
185
- summary: "App Server \u5DF2\u63A5\u53D7\u6D88\u606F\uFF0C\u5DF2\u8BB0\u5F55\u53D1\u9001\u56DE\u6267",
186
- runId: accepted.runId,
187
- durationMs: latency
188
- });
189
- this.#simulatorState = Object.freeze({
190
- messages: Object.freeze([
191
- ...this.#simulatorState.messages.filter((message2) => message2.messageId !== accepted.messageId),
192
- Object.freeze({
193
- messageId: accepted.messageId,
194
- role: "user",
195
- text: input.text,
196
- createdAt: acceptedAt.toISOString()
197
- })
198
- ]),
199
- latestRun: Object.freeze({ runId: accepted.runId, status: "pending" }),
200
- activeRun: Object.freeze({ runId: accepted.runId, status: "pending" })
201
- });
202
- this.#messages = this.#simulatorState.messages;
203
- this.#notifySimulator();
204
- this.#startOutputWatcher(this.#requiredSimulator(), accepted.runId);
205
- return accepted;
205
+ if (this.#resetting || this.#sendingMessage) throw Object.assign(new Error("\u6A21\u62DF\u5668\u6B63\u5728\u5904\u7406\u5176\u4ED6\u64CD\u4F5C"), { status: 409, code: "SIMULATOR_BUSY" });
206
+ this.#sendingMessage = true;
207
+ try {
208
+ const started = this.#now();
209
+ this.#record({ channel: "client", type: "message.send", summary: "\u53D1\u9001\u4E00\u6761\u79C1\u804A\u6D88\u606F" });
210
+ const accepted = await this.#requiredSimulator().send(input, { signal });
211
+ if (this.#runs.has(accepted.runId)) return accepted;
212
+ const acceptedAt = this.#now();
213
+ const latency = Math.max(0, acceptedAt.getTime() - started.getTime());
214
+ this.#upsertRun({
215
+ runId: accepted.runId,
216
+ messageId: accepted.messageId,
217
+ status: "accepted",
218
+ startedAt: started.toISOString(),
219
+ updatedAt: acceptedAt.toISOString(),
220
+ acceptLatencyMs: latency
221
+ });
222
+ this.#lastActiveRunId = accepted.runId;
223
+ this.#record({
224
+ channel: "app_server",
225
+ type: "run.accepted",
226
+ summary: "App Server \u5DF2\u63A5\u53D7\u6D88\u606F\uFF0C\u5DF2\u8BB0\u5F55\u53D1\u9001\u56DE\u6267",
227
+ runId: accepted.runId,
228
+ durationMs: latency
229
+ });
230
+ this.#simulatorState = Object.freeze({
231
+ messages: Object.freeze([
232
+ ...this.#simulatorState.messages.filter((message2) => message2.messageId !== accepted.messageId),
233
+ Object.freeze({
234
+ messageId: accepted.messageId,
235
+ role: "user",
236
+ text: input.text,
237
+ createdAt: acceptedAt.toISOString()
238
+ })
239
+ ]),
240
+ latestRun: Object.freeze({ runId: accepted.runId, status: "pending" }),
241
+ activeRun: Object.freeze({ runId: accepted.runId, status: "pending" })
242
+ });
243
+ this.#messages = this.#simulatorState.messages;
244
+ this.#notifySimulator();
245
+ this.#startOutputWatcher(this.#requiredSimulator(), accepted.runId);
246
+ return accepted;
247
+ } finally {
248
+ this.#sendingMessage = false;
249
+ }
206
250
  }
207
251
  async cancelSimulatorRun(runId, signal) {
208
252
  this.#record({
@@ -388,7 +432,10 @@ var PreviewController = class {
388
432
  this.#outputWatchers.set(runId, controller);
389
433
  void simulator.watchOutput(runId, {
390
434
  signal: controller.signal,
391
- onEvent: (event) => this.#observeOutputEvent(event)
435
+ onEvent: (event) => {
436
+ if (this.#resetting || simulator !== this.#simulator) return;
437
+ return this.#observeOutputEvent(event);
438
+ }
392
439
  }).catch((cause) => {
393
440
  if (controller.signal.aborted) return;
394
441
  this.#failedOutputRuns.add(runId);
@@ -487,7 +534,7 @@ var PreviewController = class {
487
534
  }
488
535
  #notifySimulator() {
489
536
  if (this.#simulatorListeners.size === 0) return;
490
- const value = Object.freeze({ state: this.#simulatorState, debug: this.debugSnapshot() });
537
+ const value = Object.freeze({ state: this.simulatorSnapshot(), debug: this.debugSnapshot() });
491
538
  for (const listener of this.#simulatorListeners) {
492
539
  try {
493
540
  listener(value);
@@ -688,8 +735,8 @@ function requestFailure(cause) {
688
735
  function publicProject(config) {
689
736
  return {
690
737
  buddyId: config.buddyId,
691
- runtime: { language: config.runtime.language },
692
- ...config.model === void 0 ? {} : { model: config.model }
738
+ ...config.model === void 0 ? {} : { model: config.model },
739
+ datasets: (config.datasets ?? []).map(({ id, purpose }) => ({ id, purpose }))
693
740
  };
694
741
  }
695
742
  async function startPreviewControlServer(options) {
@@ -810,6 +857,20 @@ data: ${JSON.stringify({
810
857
  request.once("close", () => listeners.delete(response));
811
858
  return;
812
859
  }
860
+ if (request.method === "POST" && options.controller.resetting) {
861
+ return apiError(response, 409, "SIMULATOR_BUSY", "\u6B63\u5728\u6E05\u7A7A\u5BF9\u8BDD\u5E76\u91CD\u542F DEV\uFF0C\u8BF7\u7A0D\u5019");
862
+ }
863
+ if (request.method === "POST" && url.pathname === "/api/simulator/reset") {
864
+ try {
865
+ const body = exactObject(await readJsonBody(request), ["confirm"]);
866
+ if (body?.confirm !== true) return apiError(response, 400, "RESET_CONFIRMATION_REQUIRED", "\u8BF7\u786E\u8BA4\u6E05\u7A7A\u5F53\u524D\u5F00\u53D1\u7248\u5BF9\u8BDD");
867
+ const state = await options.controller.resetSimulator();
868
+ return json(response, 200, { state, dev: options.controller.state(), debug: options.controller.debugSnapshot() });
869
+ } catch (cause) {
870
+ const failure = requestFailure(cause);
871
+ return apiError(response, failure.status, failure.code, failure.message);
872
+ }
873
+ }
813
874
  if (request.method === "POST" && url.pathname === "/api/dev/start") {
814
875
  if (Number(request.headers["content-length"] ?? 0) > 0) {
815
876
  return apiError(response, 400, "BODY_NOT_ALLOWED", "\u8BE5\u64CD\u4F5C\u4E0D\u63A5\u53D7\u8BF7\u6C42\u6B63\u6587");
@@ -1205,6 +1266,8 @@ var MlbSimulatorClient = class {
1205
1266
  #accessToken;
1206
1267
  #fetch;
1207
1268
  #sending = false;
1269
+ #resetTarget;
1270
+ #resetArchived = false;
1208
1271
  #lastSend;
1209
1272
  #privateId;
1210
1273
  #privatePromise;
@@ -1230,6 +1293,7 @@ var MlbSimulatorClient = class {
1230
1293
  return details;
1231
1294
  }
1232
1295
  async state(options = {}) {
1296
+ this.#assertResetComplete();
1233
1297
  const privateId = await this.#ensurePrivate(options.signal);
1234
1298
  const value = await this.#request("GET", `/app/privates/${encodeURIComponent(privateId)}`, {
1235
1299
  signal: options.signal
@@ -1271,6 +1335,7 @@ var MlbSimulatorClient = class {
1271
1335
  if (typeof input.text !== "string" || !input.text.trim() || Buffer.byteLength(input.text, "utf8") > 32 * 1024) {
1272
1336
  throw new TypeError("message text is invalid");
1273
1337
  }
1338
+ this.#assertResetComplete();
1274
1339
  const idempotencyKey = safeText(input.idempotencyKey, "idempotencyKey", 128);
1275
1340
  if (this.#lastSend?.key === idempotencyKey) {
1276
1341
  if (this.#lastSend.text !== input.text.trim()) throw httpError(409, { error: "\u540C\u4E00\u6761\u6D88\u606F\u4E0D\u80FD\u6539\u5199\u5185\u5BB9" });
@@ -1327,6 +1392,53 @@ var MlbSimulatorClient = class {
1327
1392
  this.#sending = false;
1328
1393
  }
1329
1394
  }
1395
+ #assertResetComplete() {
1396
+ if (this.#resetTarget) throw Object.assign(new Error("\u8BF7\u518D\u6B21\u70B9\u51FB\u6E05\u7A7A\u5BF9\u8BDD\uFF0C\u5B8C\u6210\u65B0\u4F1A\u8BDD\u521B\u5EFA"), {
1397
+ code: "RESET_INCOMPLETE",
1398
+ status: 409,
1399
+ publicMessage: "\u4F1A\u8BDD\u91CD\u7F6E\u5C1A\u672A\u5B8C\u6210\uFF0C\u8BF7\u518D\u6B21\u70B9\u51FB\u300C\u6E05\u7A7A\u5BF9\u8BDD\u300D\u91CD\u8BD5"
1400
+ });
1401
+ }
1402
+ async reset(options = {}) {
1403
+ if (this.#sending) throw httpError(409, { error: "\u6D88\u606F\u6B63\u5728\u53D1\u9001\uFF0C\u8BF7\u7A0D\u540E\u6E05\u7A7A" });
1404
+ if (!this.#resetTarget) {
1405
+ const privateId = await this.#ensurePrivate(options.signal);
1406
+ const current = object(await this.#request("GET", `/app/privates/${encodeURIComponent(privateId)}`, options));
1407
+ const conversation = object(current?.private);
1408
+ if (conversation?.id !== privateId || conversation?.buddyId !== this.#buddyId || typeof current?.replying !== "boolean") {
1409
+ throw httpError(502, { error: "\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u79C1\u804A\u72B6\u6001\uFF0C\u672A\u6E05\u7A7A\u5BF9\u8BDD" });
1410
+ }
1411
+ if (current.replying) throw httpError(409, { error: "\u8BF7\u7B49\u5F85\u5F53\u524D\u56DE\u590D\u5B8C\u6210\u540E\u518D\u6E05\u7A7A" });
1412
+ this.#resetTarget = privateId;
1413
+ }
1414
+ try {
1415
+ if (!this.#resetArchived) {
1416
+ const receipt = object(await this.#request("DELETE", `/app/privates/${encodeURIComponent(this.#resetTarget)}`, options));
1417
+ if (receipt?.ok !== true) throw httpError(502, { error: "\u65E0\u6CD5\u786E\u8BA4\u65E7\u4F1A\u8BDD\u5DF2\u7ED3\u675F" });
1418
+ this.#resetArchived = true;
1419
+ this.#privateId = void 0;
1420
+ }
1421
+ const nextId = await this.#ensurePrivate(options.signal);
1422
+ if (nextId === this.#resetTarget) {
1423
+ this.#privateId = void 0;
1424
+ throw httpError(502, { error: "MLB \u672A\u8FD4\u56DE\u65B0\u7684\u4F1A\u8BDD\u8EAB\u4EFD" });
1425
+ }
1426
+ } catch (cause) {
1427
+ const detail = object(cause)?.publicMessage;
1428
+ throw Object.assign(new Error("\u4F1A\u8BDD\u91CD\u7F6E\u5C1A\u672A\u5B8C\u6210", { cause }), {
1429
+ code: "RESET_INCOMPLETE",
1430
+ status: 502,
1431
+ publicMessage: `${this.#resetArchived ? "\u65E7\u4F1A\u8BDD\u5DF2\u7ED3\u675F\uFF0C\u65B0\u4F1A\u8BDD\u521B\u5EFA\u672A\u5B8C\u6210" : "\u5C1A\u672A\u786E\u8BA4\u65E7\u4F1A\u8BDD\u5DF2\u7ED3\u675F"}\uFF0C\u8BF7\u518D\u6B21\u70B9\u51FB\u300C\u6E05\u7A7A\u5BF9\u8BDD\u300D\u91CD\u8BD5\u3002${typeof detail === "string" ? `\uFF08${detail}\uFF09` : ""}`
1432
+ });
1433
+ }
1434
+ this.#active = void 0;
1435
+ this.#latest = void 0;
1436
+ this.#streaming = void 0;
1437
+ this.#lastSend = void 0;
1438
+ this.#messages = Object.freeze([]);
1439
+ this.#resetTarget = void 0;
1440
+ this.#resetArchived = false;
1441
+ }
1330
1442
  async cancel(_runId, _options = {}) {
1331
1443
  throw Object.assign(new Error("\u5F53\u524D MLB \u534F\u8BAE\u5C1A\u672A\u63D0\u4F9B\u7EC8\u6B62\u56DE\u590D\u63A5\u53E3"), {
1332
1444
  code: "TURN_CANCEL_UNSUPPORTED",