@openacp/cli 0.6.6 → 0.6.7

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 (68) hide show
  1. package/dist/{adapter-RKK7A5GI.js → adapter-7GY3N4ZH.js} +9 -9
  2. package/dist/{admin-SCP25TN2.js → admin-2HAFXQBG.js} +6 -4
  3. package/dist/{chunk-WVMSP4AF.js → chunk-2J2RBYWN.js} +2 -2
  4. package/dist/{chunk-ZKTIZME6.js → chunk-47B7GNOE.js} +2 -2
  5. package/dist/{chunk-XVL6AGMG.js → chunk-5OVPEDUB.js} +2 -2
  6. package/dist/{chunk-AHPRT3RY.js → chunk-5SXG7X5D.js} +523 -251
  7. package/dist/chunk-5SXG7X5D.js.map +1 -0
  8. package/dist/{chunk-F3AICYO4.js → chunk-JHYXKVV2.js} +19 -1
  9. package/dist/chunk-JHYXKVV2.js.map +1 -0
  10. package/dist/{chunk-FCLGYYTY.js → chunk-JUYDFUSN.js} +224 -2
  11. package/dist/chunk-JUYDFUSN.js.map +1 -0
  12. package/dist/{chunk-774Y4RAK.js → chunk-KIRH7TUJ.js} +94 -24
  13. package/dist/chunk-KIRH7TUJ.js.map +1 -0
  14. package/dist/{chunk-4GQ3I65A.js → chunk-LBIKITQT.js} +1 -2
  15. package/dist/{chunk-4GQ3I65A.js.map → chunk-LBIKITQT.js.map} +1 -1
  16. package/dist/{chunk-ZMVVW3BK.js → chunk-LO4Y5WQ7.js} +127 -18
  17. package/dist/chunk-LO4Y5WQ7.js.map +1 -0
  18. package/dist/{chunk-3IRAWHMC.js → chunk-MZXWCDBU.js} +3 -3
  19. package/dist/{chunk-YQRF3IOR.js → chunk-O7CPGUAI.js} +2 -2
  20. package/dist/{chunk-7KZI2236.js → chunk-RHE2JSYE.js} +2 -2
  21. package/dist/{chunk-3ZO3MHZN.js → chunk-SHHMBGB3.js} +4 -3
  22. package/dist/chunk-SHHMBGB3.js.map +1 -0
  23. package/dist/{chunk-JV6XQRAE.js → chunk-XANPHG7W.js} +2 -2
  24. package/dist/{chunk-PJVKOZTR.js → chunk-YEOY2PBJ.js} +2 -2
  25. package/dist/cli.js +21 -21
  26. package/dist/{config-B26J3XXN.js → config-CQAS6YHR.js} +2 -2
  27. package/dist/{config-editor-QTGUK3CD.js → config-editor-37BM56WF.js} +4 -4
  28. package/dist/{config-registry-7I6GGDOY.js → config-registry-HDXFES2D.js} +2 -2
  29. package/dist/{daemon-5DS5BQXJ.js → daemon-K33ZPSEZ.js} +3 -3
  30. package/dist/{discord-QKT3JMRW.js → discord-VOHXRTCH.js} +113 -131
  31. package/dist/discord-VOHXRTCH.js.map +1 -0
  32. package/dist/{doctor-QQ3YZEYV.js → doctor-HASEBMUD.js} +4 -4
  33. package/dist/doctor-W4VGLDVM.js +9 -0
  34. package/dist/index.d.ts +25 -9
  35. package/dist/index.js +11 -11
  36. package/dist/{main-B5L3DD3Y.js → main-DUXVFTDD.js} +19 -19
  37. package/dist/{new-session-K6UCWYOP.js → new-session-NHK7TOEW.js} +3 -3
  38. package/dist/{settings-RRF77IC4.js → settings-6TF4WIGJ.js} +3 -3
  39. package/dist/{setup-5ZKSUR26.js → setup-RJCEB6FS.js} +3 -3
  40. package/package.json +1 -1
  41. package/dist/chunk-3ZO3MHZN.js.map +0 -1
  42. package/dist/chunk-774Y4RAK.js.map +0 -1
  43. package/dist/chunk-AHPRT3RY.js.map +0 -1
  44. package/dist/chunk-F3AICYO4.js.map +0 -1
  45. package/dist/chunk-FCLGYYTY.js.map +0 -1
  46. package/dist/chunk-ZMVVW3BK.js.map +0 -1
  47. package/dist/discord-QKT3JMRW.js.map +0 -1
  48. package/dist/doctor-6SUCVUZB.js +0 -9
  49. /package/dist/{adapter-RKK7A5GI.js.map → adapter-7GY3N4ZH.js.map} +0 -0
  50. /package/dist/{admin-SCP25TN2.js.map → admin-2HAFXQBG.js.map} +0 -0
  51. /package/dist/{chunk-WVMSP4AF.js.map → chunk-2J2RBYWN.js.map} +0 -0
  52. /package/dist/{chunk-ZKTIZME6.js.map → chunk-47B7GNOE.js.map} +0 -0
  53. /package/dist/{chunk-XVL6AGMG.js.map → chunk-5OVPEDUB.js.map} +0 -0
  54. /package/dist/{chunk-3IRAWHMC.js.map → chunk-MZXWCDBU.js.map} +0 -0
  55. /package/dist/{chunk-YQRF3IOR.js.map → chunk-O7CPGUAI.js.map} +0 -0
  56. /package/dist/{chunk-7KZI2236.js.map → chunk-RHE2JSYE.js.map} +0 -0
  57. /package/dist/{chunk-JV6XQRAE.js.map → chunk-XANPHG7W.js.map} +0 -0
  58. /package/dist/{chunk-PJVKOZTR.js.map → chunk-YEOY2PBJ.js.map} +0 -0
  59. /package/dist/{config-B26J3XXN.js.map → config-CQAS6YHR.js.map} +0 -0
  60. /package/dist/{config-editor-QTGUK3CD.js.map → config-editor-37BM56WF.js.map} +0 -0
  61. /package/dist/{config-registry-7I6GGDOY.js.map → config-registry-HDXFES2D.js.map} +0 -0
  62. /package/dist/{daemon-5DS5BQXJ.js.map → daemon-K33ZPSEZ.js.map} +0 -0
  63. /package/dist/{doctor-6SUCVUZB.js.map → doctor-HASEBMUD.js.map} +0 -0
  64. /package/dist/{doctor-QQ3YZEYV.js.map → doctor-W4VGLDVM.js.map} +0 -0
  65. /package/dist/{main-B5L3DD3Y.js.map → main-DUXVFTDD.js.map} +0 -0
  66. /package/dist/{new-session-K6UCWYOP.js.map → new-session-NHK7TOEW.js.map} +0 -0
  67. /package/dist/{settings-RRF77IC4.js.map → settings-6TF4WIGJ.js.map} +0 -0
  68. /package/dist/{setup-5ZKSUR26.js.map → setup-RJCEB6FS.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/channel.ts"],"sourcesContent":["import type { OutgoingMessage, PermissionRequest, NotificationMessage, AgentCommand } from './types.js'\n\nexport interface ChannelConfig {\n enabled: boolean\n [key: string]: unknown\n}\n\nexport interface IChannelAdapter {\n start(): Promise<void>\n stop(): Promise<void>\n\n // Outgoing: core → channel\n sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>\n sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void>\n sendNotification(notification: NotificationMessage): Promise<void>\n\n // Session lifecycle on channel side\n createSessionThread(sessionId: string, name: string): Promise<string> // returns threadId\n renameSessionThread(sessionId: string, newName: string): Promise<void>\n deleteSessionThread(sessionId: string): Promise<void>\n\n // Skill commands — optional\n sendSkillCommands(sessionId: string, commands: AgentCommand[]): Promise<void>\n cleanupSkillCommands(sessionId: string): Promise<void>\n}\n\n/**\n * Base class providing default no-op implementations for optional methods.\n * Adapters can extend this or implement IChannelAdapter directly.\n */\nexport abstract class ChannelAdapter<TCore = unknown> implements IChannelAdapter {\n constructor(public readonly core: TCore, protected config: ChannelConfig) {}\n\n abstract start(): Promise<void>\n abstract stop(): Promise<void>\n\n // Outgoing: core → channel\n abstract sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>\n abstract sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void>\n abstract sendNotification(notification: NotificationMessage): Promise<void>\n\n // Session lifecycle on channel side\n abstract createSessionThread(sessionId: string, name: string): Promise<string> // returns threadId\n abstract renameSessionThread(sessionId: string, newName: string): Promise<void>\n async deleteSessionThread(_sessionId: string): Promise<void> {}\n\n // Skill commands — override in adapters that support dynamic commands\n async sendSkillCommands(_sessionId: string, _commands: AgentCommand[]): Promise<void> {}\n async cleanupSkillCommands(_sessionId: string): Promise<void> {}\n\n // Archive — override in adapters that support topic archiving\n async archiveSessionTopic(_sessionId: string): Promise<{ newThreadId: string } | null> {\n return null;\n }\n}\n"],"mappings":";AA8BO,IAAe,iBAAf,MAA0E;AAAA,EAC/E,YAA4B,MAAuB,QAAuB;AAA9C;AAAuB;AAAA,EAAwB;AAAA,EAa3E,MAAM,oBAAoB,YAAmC;AAAA,EAAC;AAAA;AAAA,EAG9D,MAAM,kBAAkB,YAAoB,WAA0C;AAAA,EAAC;AAAA,EACvF,MAAM,qBAAqB,YAAmC;AAAA,EAAC;AAAA;AAAA,EAG/D,MAAM,oBAAoB,YAA6D;AACrF,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/core/channel.ts"],"sourcesContent":["import type { OutgoingMessage, PermissionRequest, NotificationMessage, AgentCommand } from './types.js'\n\nexport interface ChannelConfig {\n enabled: boolean\n [key: string]: unknown\n}\n\nexport interface IChannelAdapter {\n start(): Promise<void>\n stop(): Promise<void>\n\n // Outgoing: core → channel\n sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>\n sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void>\n sendNotification(notification: NotificationMessage): Promise<void>\n\n // Session lifecycle on channel side\n createSessionThread(sessionId: string, name: string): Promise<string> // returns threadId\n renameSessionThread(sessionId: string, newName: string): Promise<void>\n deleteSessionThread(sessionId: string): Promise<void>\n\n // Skill commands — optional\n sendSkillCommands(sessionId: string, commands: AgentCommand[]): Promise<void>\n cleanupSkillCommands(sessionId: string): Promise<void>\n}\n\n/**\n * Base class providing default no-op implementations for optional methods.\n * Adapters can extend this or implement IChannelAdapter directly.\n */\nexport abstract class ChannelAdapter<TCore = unknown> implements IChannelAdapter {\n constructor(public readonly core: TCore, protected config: ChannelConfig) {}\n\n abstract start(): Promise<void>\n abstract stop(): Promise<void>\n\n // Outgoing: core → channel\n abstract sendMessage(sessionId: string, content: OutgoingMessage): Promise<void>\n abstract sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void>\n abstract sendNotification(notification: NotificationMessage): Promise<void>\n\n // Session lifecycle on channel side\n abstract createSessionThread(sessionId: string, name: string): Promise<string> // returns threadId\n abstract renameSessionThread(sessionId: string, newName: string): Promise<void>\n async deleteSessionThread(_sessionId: string): Promise<void> {}\n\n // Skill commands — override in adapters that support dynamic commands\n async sendSkillCommands(_sessionId: string, _commands: AgentCommand[]): Promise<void> {}\n async cleanupSkillCommands(_sessionId: string): Promise<void> {}\n\n // Archive — override in adapters that support topic archiving\n async archiveSessionTopic(_sessionId: string): Promise<void> {}\n}\n"],"mappings":";AA8BO,IAAe,iBAAf,MAA0E;AAAA,EAC/E,YAA4B,MAAuB,QAAuB;AAA9C;AAAuB;AAAA,EAAwB;AAAA,EAa3E,MAAM,oBAAoB,YAAmC;AAAA,EAAC;AAAA;AAAA,EAG9D,MAAM,kBAAkB,YAAoB,WAA0C;AAAA,EAAC;AAAA,EACvF,MAAM,qBAAqB,YAAmC;AAAA,EAAC;AAAA;AAAA,EAG/D,MAAM,oBAAoB,YAAmC;AAAA,EAAC;AAChE;","names":[]}
@@ -769,6 +769,7 @@ var Session = class extends TypedEmitter {
769
769
  voiceMode = "off";
770
770
  dangerousMode = false;
771
771
  archiving = false;
772
+ promptCount = 0;
772
773
  log;
773
774
  permissionGate = new PermissionGate();
774
775
  queue;
@@ -851,6 +852,7 @@ var Session = class extends TypedEmitter {
851
852
  await this.runWarmup();
852
853
  return;
853
854
  }
855
+ this.promptCount++;
854
856
  if (this._status === "initializing") {
855
857
  this.activate();
856
858
  }
@@ -1002,6 +1004,33 @@ ${result.text}` : result.text;
1002
1004
  this.resume();
1003
1005
  }
1004
1006
  }
1007
+ async generateSummary(timeoutMs = 15e3) {
1008
+ let summary = "";
1009
+ let timer;
1010
+ const captureHandler = (event) => {
1011
+ if (event.type === "text") summary += event.content;
1012
+ };
1013
+ this.pause((event) => event !== "agent_event");
1014
+ this.agentInstance.on("agent_event", captureHandler);
1015
+ try {
1016
+ const promptPromise = this.agentInstance.prompt(
1017
+ "Summarize what you've accomplished so far in this session in 2-3 sentences. Include: key files changed, decisions made, and current status. Reply ONLY with the summary, nothing else."
1018
+ );
1019
+ const timeoutPromise = new Promise((_, reject) => {
1020
+ timer = setTimeout(() => reject(new Error("summary timeout")), timeoutMs);
1021
+ });
1022
+ await Promise.race([promptPromise, timeoutPromise]);
1023
+ return summary.trim().slice(0, 500);
1024
+ } catch {
1025
+ this.log.warn("Failed to generate session summary");
1026
+ return "";
1027
+ } finally {
1028
+ if (timer) clearTimeout(timer);
1029
+ this.agentInstance.off("agent_event", captureHandler);
1030
+ this.clearBuffer();
1031
+ this.resume();
1032
+ }
1033
+ }
1005
1034
  /** Fire-and-forget warm-up: primes model cache while user types their first message */
1006
1035
  async warmup() {
1007
1036
  await this.queue.enqueue("\0__warmup__");
@@ -1338,7 +1367,8 @@ var SessionBridge = class {
1338
1367
  sessionId: this.session.id,
1339
1368
  sessionName: this.session.name,
1340
1369
  type: "completed",
1341
- summary: `Session "${this.session.name || this.session.id}" completed`
1370
+ summary: `Session "${this.session.name || this.session.id}" completed
1371
+ \u23F1 ${Math.round((Date.now() - this.session.createdAt.getTime()) / 6e4)} min \xB7 \u{1F4AC} ${this.session.promptCount} prompts`
1342
1372
  });
1343
1373
  break;
1344
1374
  case "error":
@@ -1589,24 +1619,34 @@ var MessageTransformer = class {
1589
1619
  case "thought":
1590
1620
  return { type: "thought", text: event.content };
1591
1621
  case "tool_call": {
1622
+ const meta = event.meta;
1592
1623
  const metadata = {
1593
1624
  id: event.id,
1594
1625
  name: event.name,
1595
1626
  kind: event.kind,
1596
1627
  status: event.status,
1597
1628
  content: event.content,
1598
- locations: event.locations
1629
+ locations: event.locations,
1630
+ rawInput: event.rawInput,
1631
+ displaySummary: meta?.displaySummary,
1632
+ displayTitle: meta?.displayTitle,
1633
+ displayKind: meta?.displayKind
1599
1634
  };
1600
1635
  this.enrichWithViewerLinks(event, metadata, sessionContext);
1601
1636
  return { type: "tool_call", text: event.name, metadata };
1602
1637
  }
1603
1638
  case "tool_update": {
1639
+ const meta = event.meta;
1604
1640
  const metadata = {
1605
1641
  id: event.id,
1606
1642
  name: event.name,
1607
1643
  kind: event.kind,
1608
1644
  status: event.status,
1609
- content: event.content
1645
+ content: event.content,
1646
+ rawInput: event.rawInput,
1647
+ displaySummary: meta?.displaySummary,
1648
+ displayTitle: meta?.displayTitle,
1649
+ displayKind: meta?.displayKind
1610
1650
  };
1611
1651
  this.enrichWithViewerLinks(event, metadata, sessionContext);
1612
1652
  return { type: "tool_update", text: "", metadata };
@@ -3195,22 +3235,82 @@ var OpenACPCore = class {
3195
3235
  this.usageStore.destroy();
3196
3236
  }
3197
3237
  }
3238
+ // --- Summary ---
3239
+ async summarizeSession(sessionId) {
3240
+ const session = this.sessionManager.getSession(sessionId);
3241
+ if (session && session.status === "active") {
3242
+ try {
3243
+ const summary = await session.generateSummary();
3244
+ if (!summary) return { ok: false, error: "Agent could not generate summary" };
3245
+ return { ok: true, summary };
3246
+ } catch (err) {
3247
+ return { ok: false, error: err.message };
3248
+ }
3249
+ }
3250
+ const record = this.sessionManager.getSessionRecord(sessionId);
3251
+ if (!record?.agentSessionId) {
3252
+ return { ok: false, error: "Session not found or has no agent history" };
3253
+ }
3254
+ const caps = getAgentCapabilities(record.agentName);
3255
+ if (!caps.supportsResume) {
3256
+ return { ok: false, error: `Agent "${record.agentName}" does not support resume \u2014 cannot summarize ended session` };
3257
+ }
3258
+ let tempSession;
3259
+ try {
3260
+ const agentInstance = await this.agentManager.resume(
3261
+ record.agentName,
3262
+ record.workingDir,
3263
+ record.agentSessionId
3264
+ );
3265
+ tempSession = new Session({
3266
+ id: `summary-${sessionId}`,
3267
+ channelId: record.channelId,
3268
+ agentName: record.agentName,
3269
+ workingDirectory: record.workingDir,
3270
+ agentInstance
3271
+ });
3272
+ tempSession.activate();
3273
+ const summary = await tempSession.generateSummary();
3274
+ if (!summary) return { ok: false, error: "Agent could not generate summary" };
3275
+ return { ok: true, summary };
3276
+ } catch (err) {
3277
+ return { ok: false, error: err.message };
3278
+ } finally {
3279
+ if (tempSession) {
3280
+ try {
3281
+ await tempSession.destroy();
3282
+ } catch {
3283
+ }
3284
+ }
3285
+ }
3286
+ }
3198
3287
  // --- Archive ---
3199
3288
  async archiveSession(sessionId) {
3200
3289
  const session = this.sessionManager.getSession(sessionId);
3201
- if (!session) return { ok: false, error: "Session not found" };
3202
- if (session.status === "initializing")
3203
- return { ok: false, error: "Session is still initializing" };
3204
- if (session.status !== "active")
3205
- return { ok: false, error: `Session is ${session.status}` };
3206
- const adapter = this.adapters.get(session.channelId);
3290
+ const record = this.sessionManager.getSessionRecord(sessionId);
3291
+ if (!session && !record) return { ok: false, error: "Session not found" };
3292
+ const channelId = session?.channelId ?? record?.channelId;
3293
+ if (!channelId) return { ok: false, error: "No channel for session" };
3294
+ const adapter = this.adapters.get(channelId);
3207
3295
  if (!adapter) return { ok: false, error: "Adapter not found for session" };
3208
3296
  try {
3209
- const result = await adapter.archiveSessionTopic(session.id);
3210
- if (!result)
3211
- return { ok: false, error: "Adapter does not support archiving" };
3212
- return { ok: true, newThreadId: result.newThreadId };
3297
+ if (session) {
3298
+ await adapter.archiveSessionTopic(session.id);
3299
+ } else {
3300
+ await adapter.deleteSessionThread(sessionId);
3301
+ }
3302
+ if (session) {
3303
+ try {
3304
+ await this.sessionManager.cancelSession(sessionId);
3305
+ } catch {
3306
+ } finally {
3307
+ session.archiving = false;
3308
+ }
3309
+ }
3310
+ await this.sessionManager.removeRecord(sessionId);
3311
+ return { ok: true };
3213
3312
  } catch (err) {
3313
+ if (session) session.archiving = false;
3214
3314
  return { ok: false, error: err.message };
3215
3315
  }
3216
3316
  }
@@ -4071,6 +4171,15 @@ function registerSessionRoutes(router, deps) {
4071
4171
  }
4072
4172
  });
4073
4173
  });
4174
+ router.post("/api/sessions/:sessionId/summary", async (_req, res, params) => {
4175
+ const sessionId = decodeURIComponent(params.sessionId);
4176
+ const result = await deps.core.summarizeSession(sessionId);
4177
+ if (result.ok) {
4178
+ deps.sendJson(res, 200, result);
4179
+ } else {
4180
+ deps.sendJson(res, 400, result);
4181
+ }
4182
+ });
4074
4183
  router.post("/api/sessions/:sessionId/archive", async (_req, res, params) => {
4075
4184
  const sessionId = decodeURIComponent(params.sessionId);
4076
4185
  const result = await deps.core.archiveSession(sessionId);
@@ -4139,7 +4248,7 @@ function redactDeep(obj) {
4139
4248
  }
4140
4249
  function registerConfigRoutes(router, deps) {
4141
4250
  router.get("/api/config/editable", async (_req, res) => {
4142
- const { getSafeFields: getSafeFields2, resolveOptions: resolveOptions2, getConfigValue: getConfigValue2 } = await import("./config-registry-7I6GGDOY.js");
4251
+ const { getSafeFields: getSafeFields2, resolveOptions: resolveOptions2, getConfigValue: getConfigValue2 } = await import("./config-registry-HDXFES2D.js");
4143
4252
  const config = deps.core.configManager.get();
4144
4253
  const safeFields = getSafeFields2();
4145
4254
  const fields = safeFields.map((def) => ({
@@ -4181,7 +4290,7 @@ function registerConfigRoutes(router, deps) {
4181
4290
  deps.sendJson(res, 400, { error: "Invalid config path" });
4182
4291
  return;
4183
4292
  }
4184
- const { getFieldDef: getFieldDef2 } = await import("./config-registry-7I6GGDOY.js");
4293
+ const { getFieldDef: getFieldDef2 } = await import("./config-registry-HDXFES2D.js");
4185
4294
  const fieldDef = getFieldDef2(configPath);
4186
4295
  if (!fieldDef || fieldDef.scope !== "safe") {
4187
4296
  deps.sendJson(res, 403, {
@@ -4206,7 +4315,7 @@ function registerConfigRoutes(router, deps) {
4206
4315
  }
4207
4316
  const lastKey = parts[parts.length - 1];
4208
4317
  target[lastKey] = value;
4209
- const { ConfigSchema } = await import("./config-B26J3XXN.js");
4318
+ const { ConfigSchema } = await import("./config-CQAS6YHR.js");
4210
4319
  const result = ConfigSchema.safeParse(cloned);
4211
4320
  if (!result.success) {
4212
4321
  deps.sendJson(res, 400, {
@@ -4226,7 +4335,7 @@ function registerConfigRoutes(router, deps) {
4226
4335
  }
4227
4336
  updateTarget[lastKey] = value;
4228
4337
  await deps.core.configManager.save(updates, configPath);
4229
- const { isHotReloadable: isHotReloadable2 } = await import("./config-registry-7I6GGDOY.js");
4338
+ const { isHotReloadable: isHotReloadable2 } = await import("./config-registry-HDXFES2D.js");
4230
4339
  const needsRestart = !isHotReloadable2(configPath);
4231
4340
  deps.sendJson(res, 200, {
4232
4341
  ok: true,
@@ -4768,4 +4877,4 @@ export {
4768
4877
  ApiServer,
4769
4878
  TopicManager
4770
4879
  };
4771
- //# sourceMappingURL=chunk-ZMVVW3BK.js.map
4880
+ //# sourceMappingURL=chunk-LO4Y5WQ7.js.map