@timetick/mcp 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -117,6 +117,7 @@ function registerContextTools(server) {
117
117
  server.registerTool(
118
118
  "get_context",
119
119
  {
120
+ annotations: { readOnlyHint: true },
120
121
  title: "Get active context",
121
122
  description: "Show the project and sim world the MCP server is currently operating on. If no project is selected yet and you have only one project, it is auto-selected.",
122
123
  inputSchema: {}
@@ -132,6 +133,7 @@ function registerContextTools(server) {
132
133
  server.registerTool(
133
134
  "use_project",
134
135
  {
136
+ annotations: { readOnlyHint: true },
135
137
  title: "Select project",
136
138
  description: "Switch the active project by name or id. Only needed if you have more than one project. Caches the project's sim world for subsequent tools.",
137
139
  inputSchema: {
@@ -148,6 +150,25 @@ function registerContextTools(server) {
148
150
  // src/tools/data.ts
149
151
  import { z as z3 } from "zod";
150
152
 
153
+ // src/links.ts
154
+ var SIMULATOR_PATH = /\/projects\/([^/?#&|>\s]+)\/simulators\/([^/?#&|>\s]+)/;
155
+ function looksLikeLink(text) {
156
+ return /^<?https?:\/\//i.test(text) || /(^|\.)timetick\.io\//i.test(text);
157
+ }
158
+ function parseSimulatorRef(input) {
159
+ const text = input.trim();
160
+ if (!looksLikeLink(text)) return { simulator: text };
161
+ const unescaped = text.replace(/&amp;/g, "&");
162
+ const match = unescaped.match(SIMULATOR_PATH);
163
+ if (!match) {
164
+ throw new Error(
165
+ `"${input}" looks like a link but not to a simulator. Expected https://app.timetick.io/projects/<project id>/simulators/<simulator id>.`
166
+ );
167
+ }
168
+ return { project: decodeURIComponent(match[1]), simulator: decodeURIComponent(match[2]) };
169
+ }
170
+ var SIMULATOR_ARG_DESCRIPTION = "Simulator id, base name, or portal link (https://app.timetick.io/projects/<id>/simulators/<id>)";
171
+
151
172
  // src/resolve.ts
152
173
  async function resolveSimWorld(project) {
153
174
  if (project) return lookupProject(project);
@@ -172,6 +193,20 @@ Candidates:
172
193
  ${candidates}` : " No simulators found in this sim world.")
173
194
  );
174
195
  }
196
+ async function resolveSimulatorTarget(simulator, project) {
197
+ const ref = parseSimulatorRef(simulator);
198
+ const ctx = await resolveSimWorld(ref.project ?? project);
199
+ const sim = ref.project !== void 0 ? await resolveSimulatorById(ref.simulator) : await resolveSimulator(ctx.simWorldId, ref.simulator);
200
+ return { ctx, sim };
201
+ }
202
+ async function resolveSimulatorById(id) {
203
+ const byId = await sdk().simulators.get(id).catch((err) => {
204
+ const reason = err instanceof Error ? err.message : String(err);
205
+ throw new Error(`No simulator with id ${id} from the portal link: ${reason}`);
206
+ });
207
+ if (!byId?.id) throw new Error(`No simulator with id ${id} from the portal link.`);
208
+ return { id: byId.id, baseName: byId.baseName ?? "", protocol: String(byId.protocol) };
209
+ }
175
210
  async function resolveActionId(simWorldId, protocol, actionName) {
176
211
  const page = await sdk().actions.list({
177
212
  simWorldId,
@@ -228,6 +263,7 @@ function registerDataTools(server) {
228
263
  server.registerTool(
229
264
  "list_projects",
230
265
  {
266
+ annotations: { readOnlyHint: true },
231
267
  title: "List projects",
232
268
  description: "List all projects available to your API key.",
233
269
  inputSchema: {}
@@ -240,6 +276,7 @@ function registerDataTools(server) {
240
276
  server.registerTool(
241
277
  "list_simulators",
242
278
  {
279
+ annotations: { readOnlyHint: true },
243
280
  title: "List simulators",
244
281
  description: "List simulators (virtual chargers) in the active project's sim world. Optionally filter by name search or protocol.",
245
282
  inputSchema: {
@@ -266,16 +303,16 @@ function registerDataTools(server) {
266
303
  server.registerTool(
267
304
  "get_simulator",
268
305
  {
306
+ annotations: { readOnlyHint: true },
269
307
  title: "Get simulator",
270
308
  description: "Get a simulator's details and live status (network status, launched/power status, connectors) by id or base name.",
271
309
  inputSchema: {
272
- simulator: z3.string().describe("Simulator id or base name"),
310
+ simulator: z3.string().describe(SIMULATOR_ARG_DESCRIPTION),
273
311
  project: z3.string().optional().describe("Project name or id (defaults to active project)")
274
312
  }
275
313
  },
276
314
  async ({ simulator, project }) => guard(async () => {
277
- const ctx = await resolveSimWorld(project);
278
- const resolved = await resolveSimulator(ctx.simWorldId, simulator);
315
+ const { sim: resolved } = await resolveSimulatorTarget(simulator, project);
279
316
  const details = await sdk().simulators.get(resolved.id);
280
317
  return ok(`Simulator ${resolved.baseName} (id: ${resolved.id}).`, details);
281
318
  })
@@ -283,6 +320,7 @@ function registerDataTools(server) {
283
320
  server.registerTool(
284
321
  "list_hardware",
285
322
  {
323
+ annotations: { readOnlyHint: true },
286
324
  title: "List hardware",
287
325
  description: "List physical OCPP hardware devices in the active project's sim world.",
288
326
  inputSchema: {
@@ -307,6 +345,7 @@ function registerDataTools(server) {
307
345
  server.registerTool(
308
346
  "list_actions",
309
347
  {
348
+ annotations: { readOnlyHint: true },
310
349
  title: "List actions (test steps)",
311
350
  description: "List available actions (steps) you can use to build a test \u2014 e.g. 'Power On Charger', 'Plug In Connector', 'Charge a Vehicle'. Filter by protocol. Each action lists its arguments: the name, whether it is required (allowNull false), and any default.",
312
351
  inputSchema: {
@@ -337,8 +376,7 @@ function registerDataTools(server) {
337
376
  // src/tools/simulators.ts
338
377
  import { z as z4 } from "zod";
339
378
  async function runOnSimulator(project, simulator, actionName, args = []) {
340
- const ctx = await resolveSimWorld(project);
341
- const sim = await resolveSimulator(ctx.simWorldId, simulator);
379
+ const { ctx, sim } = await resolveSimulatorTarget(simulator, project);
342
380
  const actionId = await resolveActionId(ctx.simWorldId, sim.protocol, actionName);
343
381
  const status = await sdk().testRuns.runAction({
344
382
  chargerId: sim.id,
@@ -351,10 +389,11 @@ function registerSimulatorTools(server) {
351
389
  server.registerTool(
352
390
  "power_on_simulator",
353
391
  {
392
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
354
393
  title: "Power on simulator",
355
394
  description: "Power on a simulator (boots it and connects it to the OCPP server).",
356
395
  inputSchema: {
357
- simulator: z4.string().describe("Simulator id or base name"),
396
+ simulator: z4.string().describe(SIMULATOR_ARG_DESCRIPTION),
358
397
  project: projectArg
359
398
  }
360
399
  },
@@ -366,10 +405,11 @@ function registerSimulatorTools(server) {
366
405
  server.registerTool(
367
406
  "power_off_simulator",
368
407
  {
408
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
369
409
  title: "Power off simulator",
370
410
  description: "Power off a simulator.",
371
411
  inputSchema: {
372
- simulator: z4.string().describe("Simulator id or base name"),
412
+ simulator: z4.string().describe(SIMULATOR_ARG_DESCRIPTION),
373
413
  project: projectArg
374
414
  }
375
415
  },
@@ -381,10 +421,11 @@ function registerSimulatorTools(server) {
381
421
  server.registerTool(
382
422
  "run_simulator_action",
383
423
  {
424
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
384
425
  title: "Run simulator action",
385
426
  description: "Run a single named action against a simulator \u2014 e.g. 'Reset Charger', 'Plug In Connector', 'Plug Out Connector'. Use list_actions to discover names and required args. Args are passed as key/value pairs (e.g. connectorId=1).",
386
427
  inputSchema: {
387
- simulator: z4.string().describe("Simulator id or base name"),
428
+ simulator: z4.string().describe(SIMULATOR_ARG_DESCRIPTION),
388
429
  actionName: z4.string().describe("Exact action name, e.g. 'Reset Charger'"),
389
430
  args: z4.array(z4.object({ key: z4.string(), value: z4.string() })).optional().describe("Action arguments as key/value pairs"),
390
431
  project: projectArg
@@ -405,6 +446,7 @@ function registerTestTools(server) {
405
446
  server.registerTool(
406
447
  "list_tests",
407
448
  {
449
+ annotations: { readOnlyHint: true },
408
450
  title: "List tests",
409
451
  description: "List tests (scenarios) in the active project's sim world.",
410
452
  inputSchema: {
@@ -428,6 +470,7 @@ function registerTestTools(server) {
428
470
  server.registerTool(
429
471
  "get_test",
430
472
  {
473
+ annotations: { readOnlyHint: true },
431
474
  title: "Get test",
432
475
  description: "Get a test by id, including its device requirements.",
433
476
  inputSchema: {
@@ -443,6 +486,7 @@ function registerTestTools(server) {
443
486
  server.registerTool(
444
487
  "create_test",
445
488
  {
489
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
446
490
  title: "Create test",
447
491
  description: "Create a test from an ordered list of steps. Each step references an action by name (use list_actions to discover them) plus optional key/value args. The steps run in order against a single simulator. Example steps: 'Power On Charger', 'Plug In Connector', 'Use RFID Card', 'Charge a Vehicle', 'Plug Out Connector', 'Power Off Charger'.",
448
492
  inputSchema: {
@@ -491,20 +535,20 @@ function registerTestRunTools(server) {
491
535
  server.registerTool(
492
536
  "run_test",
493
537
  {
538
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
494
539
  title: "Run test",
495
540
  description: "Run a test (scenario) on a simulator, starting immediately. Returns the test run id and status \u2014 poll with get_test_run to follow it. A simulator runs one test at a time; if it is busy this says so and starts nothing.",
496
541
  inputSchema: {
497
542
  testId: z6.string().describe("Test (scenario) id to run"),
498
- simulator: z6.string().describe("Simulator id or base name to run the test on"),
543
+ simulator: z6.string().describe(`${SIMULATOR_ARG_DESCRIPTION} to run the test on`),
499
544
  project: projectArg
500
545
  }
501
546
  },
502
547
  async ({ testId, simulator, project }) => guard(async () => {
503
- const ctx = await resolveSimWorld(project);
548
+ const { ctx, sim } = await resolveSimulatorTarget(simulator, project);
504
549
  const test = await sdk().tests.get(testId);
505
550
  const requirementId = test?.requirements?.[0]?.id;
506
551
  if (!requirementId) return fail(`Test ${testId} has no device requirement to assign.`);
507
- const sim = await resolveSimulator(ctx.simWorldId, simulator);
508
552
  const run = await sdk().testRuns.create({
509
553
  scenarioId: testId,
510
554
  simWorldId: ctx.simWorldId,
@@ -532,6 +576,7 @@ function registerTestRunTools(server) {
532
576
  server.registerTool(
533
577
  "get_test_run",
534
578
  {
579
+ annotations: { readOnlyHint: true },
535
580
  title: "Get test run",
536
581
  description: "Get a test run's status by id.",
537
582
  inputSchema: {
@@ -548,6 +593,7 @@ function registerTestRunTools(server) {
548
593
  server.registerTool(
549
594
  "list_test_runs",
550
595
  {
596
+ annotations: { readOnlyHint: true },
551
597
  title: "List test runs",
552
598
  description: "List recent test runs in the active project's sim world.",
553
599
  inputSchema: {
@@ -567,6 +613,7 @@ function registerTestRunTools(server) {
567
613
  server.registerTool(
568
614
  "stop_test_run",
569
615
  {
616
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
570
617
  title: "Stop test run",
571
618
  description: "Shut down (stop) a running or scheduled test run by id.",
572
619
  inputSchema: {
@@ -586,6 +633,265 @@ function registerTestRunTools(server) {
586
633
  );
587
634
  }
588
635
 
636
+ // src/tools/support.ts
637
+ import { z as z7 } from "zod";
638
+
639
+ // src/logs-format.ts
640
+ var DEFAULT_LOGS_LAST = "1h";
641
+ var DEFAULT_LOGS_LIMIT = 200;
642
+ var MAX_LOGS_LIMIT = 1e3;
643
+ var PAYLOAD_MAX_CHARS = 500;
644
+ var UNIT_MS = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
645
+ function parseDuration(value) {
646
+ const match = value.trim().match(/^(\d+)\s*([smhd])$/i);
647
+ if (!match) throw new Error(`Invalid duration "${value}". Use a number and a unit: 30m, 6h, 2d.`);
648
+ return Number(match[1]) * UNIT_MS[match[2].toLowerCase()];
649
+ }
650
+ function parseInstant(value, name) {
651
+ const date = new Date(value);
652
+ if (Number.isNaN(date.getTime())) throw new Error(`Invalid ${name} "${value}". Use an ISO date-time.`);
653
+ return date;
654
+ }
655
+ function resolveWindow(input, now = /* @__PURE__ */ new Date()) {
656
+ const to = input.to ? parseInstant(input.to, "to") : now;
657
+ const from = input.from ? parseInstant(input.from, "from") : new Date(to.getTime() - parseDuration(input.last ?? DEFAULT_LOGS_LAST));
658
+ if (from.getTime() > to.getTime()) throw new Error(`"from" (${from.toISOString()}) is after "to" (${to.toISOString()}).`);
659
+ return { from, to };
660
+ }
661
+ function tryJson(text) {
662
+ if (text == null) return void 0;
663
+ try {
664
+ return JSON.parse(text);
665
+ } catch {
666
+ return void 0;
667
+ }
668
+ }
669
+ function stringify(value) {
670
+ return typeof value === "string" ? value : JSON.stringify(value);
671
+ }
672
+ function truncate(text, max = PAYLOAD_MAX_CHARS) {
673
+ return text.length > max ? `${text.slice(0, max)}\u2026(+${text.length - max} chars)` : text;
674
+ }
675
+ function direction(record, envelope) {
676
+ const dir = envelope?.payload?.direction;
677
+ if (dir === "client_to_server") return "CP\u2192CSMS";
678
+ if (dir === "server_to_client") return "CSMS\u2192CP";
679
+ if (record.sender || record.receiver) return `${record.sender ?? "?"}\u2192${record.receiver ?? "?"}`;
680
+ return "-";
681
+ }
682
+ function toLogLines(records) {
683
+ const parsed = records.map((r) => ({ record: r, message: tryJson(r.content), envelope: tryJson(r.rawContent) }));
684
+ const callActions = /* @__PURE__ */ new Map();
685
+ for (const { message } of parsed) {
686
+ if (Array.isArray(message) && message[0] === 2) callActions.set(String(message[1]), String(message[2]));
687
+ }
688
+ return parsed.map(({ record, message, envelope }) => {
689
+ let action;
690
+ let payload;
691
+ if (record.protocol === "OCPP" && Array.isArray(message) && [2, 3, 4].includes(message[0])) {
692
+ const call = callActions.get(String(message[1]));
693
+ if (message[0] === 2) {
694
+ action = String(message[2]);
695
+ payload = stringify(message[3] ?? {});
696
+ } else if (message[0] === 3) {
697
+ action = `${call ?? "?"}.result`;
698
+ payload = stringify(message[2] ?? {});
699
+ } else {
700
+ action = `${call ?? "?"}.error`;
701
+ payload = [message[2], message[3], message[4] ? stringify(message[4]) : ""].filter(Boolean).join(" ");
702
+ }
703
+ } else {
704
+ action = String(envelope?.type ?? record.protocol ?? "log");
705
+ payload = record.content ?? "";
706
+ }
707
+ if (record.error) payload = `${payload} [error: ${record.error}]`;
708
+ return {
709
+ time: record.timestamp ?? "?",
710
+ direction: direction(record, envelope),
711
+ action,
712
+ payload: truncate(payload.replace(/\s+/g, " ").trim())
713
+ };
714
+ });
715
+ }
716
+ function matchesAction(line, action) {
717
+ const base = line.action.replace(/\.(result|error)$/, "").toLowerCase();
718
+ return base === action.trim().toLowerCase();
719
+ }
720
+ function formatLine(line) {
721
+ return `${line.time} ${line.direction} ${line.action} ${line.payload}`;
722
+ }
723
+
724
+ // src/tools/support.ts
725
+ var READ_ONLY = { readOnlyHint: true, openWorldHint: true };
726
+ var SECRET_KEY = /authorizationkey|password|secret|privatekey|passphrase/i;
727
+ function maskSecretItems(items) {
728
+ return (items ?? []).map(
729
+ (item) => SECRET_KEY.test(item.key ?? item.variableName ?? "") && item.value ? { ...item, value: "***" } : item
730
+ );
731
+ }
732
+ function summarizeSimulator(sim) {
733
+ const model = sim.chargerModel;
734
+ const config = sim.chargerConfig;
735
+ const connectors = (model?.connectorModels ?? sim.chargerConnectors ?? []).map((c) => `#${c.number ?? c.connectorId} ${c.type} (cable ${c.cableFixed ? "fixed" : "flexible"})`).join(", ");
736
+ const lines = [
737
+ `Simulator ${sim.baseName} (id: ${sim.id}), ${sim.protocol}; network ${sim.networkStatus}, power ${sim.launchedStatus}, OCPP status ${sim.chargerStatus}${sim.emergency ? ", EMERGENCY" : ""}.`,
738
+ `Model: ${model?.name ?? "?"} by ${model?.deviceManufacturer?.name ?? "?"}` + (connectors ? `; connectors: ${connectors}.` : "."),
739
+ `Behavior: ${config?.behavior ?? "?"}; remote templates ${config?.useRemoteTemplates ? "on" : "off"}; supported commands: ${(config?.supportedCommands ?? []).join(", ") || "\u2014"}.`,
740
+ `OCPP configuration keys: ${sim.chargerOcppConfigItems?.length ?? 0}.`
741
+ ];
742
+ if (sim.lastRun) {
743
+ lines.push(
744
+ `Last run ${sim.lastRun.id}: ${sim.lastRun.status}` + (sim.lastRun.failReason ? ` \u2014 ${sim.lastRun.failReason}` : "") + "."
745
+ );
746
+ }
747
+ return lines.join("\n");
748
+ }
749
+ var RUN_SIMULATORS_PAGE_SIZE = 20;
750
+ function simulatorName(rc) {
751
+ return rc.charger?.baseName ?? rc.device?.serialNumber ?? rc.id;
752
+ }
753
+ function findRunSimulator(records, simulator) {
754
+ const needle = simulator.trim().toLowerCase();
755
+ return records.find((rc) => rc.id === simulator.trim()) ?? records.find((rc) => rc.charger?.id === simulator.trim() || rc.device?.id === simulator.trim()) ?? records.find(
756
+ (rc) => rc.charger?.baseName?.toLowerCase() === needle || rc.device?.serialNumber?.toLowerCase() === needle
757
+ );
758
+ }
759
+ function summarizeRun(run, simulators, page, steps) {
760
+ const records = (simulators?.records ?? []).filter(Boolean);
761
+ const lines = [
762
+ `Test run ${run.id} ("${run.scenario?.name ?? "?"}"): ${run.status}` + (run.failReason ? ` \u2014 ${run.failReason}` : "") + `. Started ${run.realStartTimestamp ?? run.startTimestamp ?? "\u2014"}, stopped ${run.realStopTimestamp ?? "\u2014"}.`,
763
+ pageSummary("simulator(s)", records.length, simulators?.pageInfo ?? { page })
764
+ ];
765
+ for (const rc of records) {
766
+ lines.push(`- ${simulatorName(rc)} (simulator ${rc.charger?.id ?? rc.device?.id ?? "?"}, run entry ${rc.id}): ${rc.status}`);
767
+ }
768
+ if (steps) {
769
+ lines.push("", `Steps of ${simulatorName(steps)}:`);
770
+ for (const step of steps.chargerActions ?? []) {
771
+ lines.push(
772
+ ` step ${step.order} ${step.scenarioAction?.action?.name ?? "?"}: ${step.status}` + (step.failReason ? ` \u2014 ${step.failReason}` : "")
773
+ );
774
+ }
775
+ } else if (records.length > 1 || simulators?.pageInfo?.hasNextPage) {
776
+ lines.push("", "Pass `simulator` with one of the names or ids above to see its steps.");
777
+ }
778
+ return lines.join("\n");
779
+ }
780
+ function registerSupportTools(server) {
781
+ const projectArg = z7.string().optional().describe("Project name or id (defaults to active project; a portal link sets its own)");
782
+ server.registerTool(
783
+ "get_simulator_config",
784
+ {
785
+ title: "Get simulator configuration",
786
+ description: "Read-only. Everything that explains why a simulator behaves the way it does: its OCPP configuration keys (e.g. FreevendEnabled, HeartbeatInterval), behavior settings (vendor behavior, remote start/stop templates, supported commands, timeouts), model and connector types (fixed or flexible cable), live connectors, authorizations, charging profiles, transactions, reservations, and the last test run with its fail reason. Credential values are masked.",
787
+ inputSchema: { simulator: z7.string().describe(SIMULATOR_ARG_DESCRIPTION), project: projectArg },
788
+ annotations: READ_ONLY
789
+ },
790
+ async ({ simulator, project }) => guard(async () => {
791
+ const { sim } = await resolveSimulatorTarget(simulator, project);
792
+ const details = await sdk().simulators.getDetails(sim.id);
793
+ if (!details) return ok(`No simulator found with id ${sim.id}.`);
794
+ const items = await sdk().simulators.getOcppConfigItems(sim.id);
795
+ const safe = { ...details, chargerOcppConfigItems: maskSecretItems(items) };
796
+ return ok(summarizeSimulator(safe), safe);
797
+ })
798
+ );
799
+ server.registerTool(
800
+ "get_simulator_logs",
801
+ {
802
+ title: "Get simulator logs",
803
+ description: "Read-only. The simulator's OCPP message log for a time window, one line per message: `time direction action payload` (payload cut at ~500 chars). Newest messages are kept when the limit cuts the window; lines are printed oldest first. Use `action` to keep one OCPP action, e.g. StatusNotification (its results and errors are kept too).",
804
+ inputSchema: {
805
+ simulator: z7.string().describe(SIMULATOR_ARG_DESCRIPTION),
806
+ from: z7.string().optional().describe("Window start, ISO date-time (wins over `last`)"),
807
+ to: z7.string().optional().describe("Window end, ISO date-time (default now)"),
808
+ last: z7.string().optional().describe(`Window length back from \`to\`, e.g. 30m, 6h, 2d (default ${DEFAULT_LOGS_LAST})`),
809
+ limit: z7.number().int().min(1).max(MAX_LOGS_LIMIT).optional().describe(`Max messages to show (default ${DEFAULT_LOGS_LIMIT}, max ${MAX_LOGS_LIMIT})`),
810
+ action: z7.string().optional().describe("Keep only this OCPP action, e.g. StatusNotification"),
811
+ project: projectArg
812
+ },
813
+ annotations: READ_ONLY
814
+ },
815
+ async ({ simulator, from, to, last, limit, action, project }) => guard(async () => {
816
+ const window = resolveWindow({ from, to, last });
817
+ const { sim } = await resolveSimulatorTarget(simulator, project);
818
+ const shown = Math.min(limit ?? DEFAULT_LOGS_LIMIT, MAX_LOGS_LIMIT);
819
+ const result = await sdk().logs.list({
820
+ simulatorId: sim.id,
821
+ from: window.from,
822
+ to: window.to,
823
+ limit: action ? MAX_LOGS_LIMIT : shown
824
+ });
825
+ const all = toLogLines(result.records);
826
+ const matched = action ? all.filter((line) => matchesAction(line, action)) : all;
827
+ const lines = matched.slice(0, shown).reverse();
828
+ const truncated = matched.length > shown || Boolean(result.pageInfo?.hasNextPage);
829
+ const head = `${sim.baseName} (id: ${sim.id}) ${window.from.toISOString()} \u2026 ${window.to.toISOString()}: ${lines.length} message(s) shown` + (action ? ` matching "${action}" of ${all.length} scanned` : "") + (result.pageInfo?.totalCount != null ? `; ${result.pageInfo.totalCount} in window` : "") + `; truncated: ${truncated ? "yes \u2014 narrow the window or raise the limit" : "no"}.`;
830
+ return ok([head, "", ...lines.map(formatLine)].join("\n"));
831
+ })
832
+ );
833
+ server.registerTool(
834
+ "get_test_run_details",
835
+ {
836
+ title: "Get test run details",
837
+ description: `Read-only. A test run's status and fail reason, plus one page of its simulators or devices with their status (${RUN_SIMULATORS_PAGE_SIZE} per page). Steps \u2014 status, fail reason, expected / actual result \u2014 are shown for one simulator at a time: pass \`simulator\` with a name or id from the page. A run with a single simulator shows its steps right away.`,
838
+ inputSchema: {
839
+ runId: z7.string().describe("Test run id"),
840
+ page: z7.number().int().min(1).optional().describe("Page of the run's simulators, starting at 1 (default 1)"),
841
+ simulator: z7.string().optional().describe("Show the steps of this simulator from the page: its name, simulator id or run entry id")
842
+ },
843
+ annotations: READ_ONLY
844
+ },
845
+ async ({ runId, page, simulator }) => guard(async () => {
846
+ const run = await sdk().testRuns.getDetails(runId);
847
+ if (!run) return ok(`No test run found with id ${runId}.`);
848
+ const current = page ?? 1;
849
+ const simulators = await sdk().testRuns.listSimulators(runId, {
850
+ pagination: { page: current, pageSize: RUN_SIMULATORS_PAGE_SIZE }
851
+ });
852
+ const records = (simulators?.records ?? []).filter(Boolean);
853
+ let target;
854
+ if (simulator) {
855
+ target = findRunSimulator(records, simulator);
856
+ if (!target) {
857
+ return fail(
858
+ `No simulator "${simulator}" on page ${current} of test run ${runId}. Pass a name or id listed on this page, or another page.`
859
+ );
860
+ }
861
+ } else if (simulators?.pageInfo?.totalCount === 1 || current === 1 && records.length === 1 && !simulators?.pageInfo?.hasNextPage) {
862
+ target = records[0];
863
+ }
864
+ const ref = target?.charger?.id ? { chargerId: target.charger.id } : target?.device?.id ? { deviceId: target.device.id } : null;
865
+ const steps = ref ? await sdk().testRuns.getSimulatorSteps(runId, ref) : null;
866
+ return ok(summarizeRun(run, simulators, current, steps), { run, simulators, steps });
867
+ })
868
+ );
869
+ }
870
+
871
+ // src/tools/index.ts
872
+ var READONLY_ENV = "TIMETICK_MCP_READONLY";
873
+ function isReadOnlyMode(env = process.env) {
874
+ const value = env[READONLY_ENV]?.trim().toLowerCase();
875
+ return value === "1" || value === "true";
876
+ }
877
+ function readOnlyView(server) {
878
+ return new Proxy(server, {
879
+ get(target, prop, receiver) {
880
+ if (prop !== "registerTool") return Reflect.get(target, prop, receiver);
881
+ return (name, config, cb) => config.annotations?.readOnlyHint === true ? target.registerTool(name, config, cb) : void 0;
882
+ }
883
+ });
884
+ }
885
+ function registerTools(server, options) {
886
+ const target = options.readOnly ? readOnlyView(server) : server;
887
+ registerContextTools(target);
888
+ registerDataTools(target);
889
+ registerSimulatorTools(target);
890
+ registerTestTools(target);
891
+ registerTestRunTools(target);
892
+ registerSupportTools(target);
893
+ }
894
+
589
895
  // src/index.ts
590
896
  async function main() {
591
897
  if (!process.env.TIMETICK_API_TOKEN) {
@@ -594,15 +900,12 @@ async function main() {
594
900
  );
595
901
  process.exit(1);
596
902
  }
597
- const server = new McpServer({ name: "timetick", version: "0.1.0" });
598
- registerContextTools(server);
599
- registerDataTools(server);
600
- registerSimulatorTools(server);
601
- registerTestTools(server);
602
- registerTestRunTools(server);
903
+ const server = new McpServer({ name: "timetick", version: "0.2.1" });
904
+ const readOnly = isReadOnlyMode();
905
+ registerTools(server, { readOnly });
603
906
  const transport = new StdioServerTransport();
604
907
  await server.connect(transport);
605
- console.error("Timetick MCP server running on stdio");
908
+ console.error(`Timetick MCP server running on stdio${readOnly ? " (read-only)" : ""}`);
606
909
  }
607
910
  main().catch((error) => {
608
911
  console.error("Fatal error starting Timetick MCP server:", error);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/tools/context.ts","../src/sdk.ts","../src/context.ts","../src/result.ts","../src/tools/data.ts","../src/resolve.ts","../src/pagination.ts","../src/tools/simulators.ts","../src/tools/tests.ts","../src/tools/test-runs.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { registerContextTools } from \"./tools/context.js\";\nimport { registerDataTools } from \"./tools/data.js\";\nimport { registerSimulatorTools } from \"./tools/simulators.js\";\nimport { registerTestTools } from \"./tools/tests.js\";\nimport { registerTestRunTools } from \"./tools/test-runs.js\";\n\nasync function main() {\n if (!process.env.TIMETICK_API_TOKEN) {\n console.error(\n \"TIMETICK_API_TOKEN is not set. Generate an API key in the Timetick portal \" +\n \"(Account → API keys) and expose it to the MCP server as TIMETICK_API_TOKEN.\",\n );\n process.exit(1);\n }\n\n const server = new McpServer({ name: \"timetick\", version: \"0.1.0\" });\n\n registerContextTools(server);\n registerDataTools(server);\n registerSimulatorTools(server);\n registerTestTools(server);\n registerTestRunTools(server);\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"Timetick MCP server running on stdio\");\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error starting Timetick MCP server:\", error);\n process.exit(1);\n});\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getContext, useProject, peekContext } from \"../context.js\";\nimport { ok, guard } from \"../result.js\";\n\nexport function registerContextTools(server: McpServer) {\n server.registerTool(\n \"get_context\",\n {\n title: \"Get active context\",\n description:\n \"Show the project and sim world the MCP server is currently operating on. \" +\n \"If no project is selected yet and you have only one project, it is auto-selected.\",\n inputSchema: {},\n },\n async () =>\n guard(async () => {\n const ctx = peekContext() ?? (await getContext());\n return ok(\n `Active project: ${ctx.projectName} (id: ${ctx.projectId}); sim world: ${ctx.simWorldId} [${ctx.source}]`,\n ctx,\n );\n }),\n );\n\n server.registerTool(\n \"use_project\",\n {\n title: \"Select project\",\n description:\n \"Switch the active project by name or id. Only needed if you have more than one project. \" +\n \"Caches the project's sim world for subsequent tools.\",\n inputSchema: {\n project: z.string().describe(\"Project name or id to make active\"),\n },\n },\n async ({ project }) =>\n guard(async () => {\n const ctx = await useProject({ project });\n return ok(`Active project is now ${ctx.projectName} (id: ${ctx.projectId}).`, ctx);\n }),\n );\n}\n","import { createClient, type TimetickClient } from \"@timetick/sdk\";\n\nlet client: TimetickClient | null = null;\n\n/** Lazily-created singleton Timetick SDK client, authenticated from the environment. */\nexport function sdk(): TimetickClient {\n if (client) return client;\n\n const apiKey = process.env.TIMETICK_API_TOKEN;\n if (!apiKey) {\n throw new Error(\n \"TIMETICK_API_TOKEN is not set. Generate an API key in the Timetick portal \" +\n \"(Account → API keys) and expose it to the MCP server as TIMETICK_API_TOKEN.\",\n );\n }\n\n client = createClient({ apiKey, endpoint: process.env.TIMETICK_API_URL });\n return client;\n}\n","import { sdk } from \"./sdk.js\";\n\nexport interface Context {\n projectId: string;\n projectName: string;\n simWorldId: string;\n /** Scope id used by hardware/device queries — distinct from simWorldId. */\n deviceScopeId: string;\n /** How the project was chosen — surfaced to the user for transparency. */\n source: \"single-project\" | \"explicit\";\n}\n\n/** Thrown when the project/sim world can't be resolved without user input. */\nexport class ContextError extends Error {}\n\nlet cached: Context | null = null;\n\n/** Resolves a project's sim world (one sim world per project today). */\nasync function buildContext(\n projectId: string,\n projectName: string,\n source: Context[\"source\"],\n): Promise<Context> {\n const worlds = await sdk().simWorlds.list(projectId);\n const world = worlds?.[0];\n if (!world?.id) {\n throw new ContextError(`Project \"${projectName}\" has no sim world to operate on.`);\n }\n return {\n projectId,\n projectName,\n simWorldId: world.id,\n deviceScopeId: world.deviceScopeId ?? world.id,\n source,\n };\n}\n\nconst PROJECT_LIST_LIMIT = 15;\n\ntype ProjectSummary = { id?: string | null; name?: string | null } | null | undefined;\n\nfunction formatProjects(projects: readonly ProjectSummary[] | null | undefined): string {\n const all = projects ?? [];\n const lines = all\n .slice(0, PROJECT_LIST_LIMIT)\n .map((p) => ` - ${p?.name} (id: ${p?.id})`);\n if (all.length > PROJECT_LIST_LIMIT) {\n lines.push(` …and ${all.length - PROJECT_LIST_LIMIT} more — call list_projects to see them all.`);\n }\n return lines.join(\"\\n\");\n}\n\nasync function findProject(project: string): Promise<{ id: string; name: string }> {\n const projects = await sdk().projects.list();\n const needle = project.toLowerCase();\n const match = projects?.find(\n (p) => p?.id === project || p?.name?.toLowerCase() === needle,\n );\n if (!match?.id) {\n throw new ContextError(\n `No project matching \"${project}\". Available:\\n${formatProjects(projects)}`,\n );\n }\n return { id: match.id, name: match.name ?? \"(unnamed)\" };\n}\n\n/**\n * Returns the active context, auto-selecting the project when there is only one.\n * Throws ContextError (with the project list) when the choice is ambiguous.\n */\nexport async function getContext(): Promise<Context> {\n if (cached) return cached;\n\n const projects = await sdk().projects.list();\n if (!projects || projects.length === 0) {\n throw new ContextError(\"No projects are available for this API key.\");\n }\n if (projects.length > 1) {\n throw new ContextError(\n `You have ${projects.length} projects. Call \\`use_project\\` with a project name or id ` +\n `first:\\n${formatProjects(projects)}`,\n );\n }\n\n const only = projects[0]!;\n cached = await buildContext(only.id!, only.name ?? \"(unnamed)\", \"single-project\");\n return cached;\n}\n\n/** Explicitly switches the active project by name or id, and caches its sim world. */\nexport async function useProject(input: { project: string }): Promise<Context> {\n const match = await findProject(input.project);\n cached = await buildContext(match.id, match.name, \"explicit\");\n return cached;\n}\n\nexport async function lookupProject(project: string): Promise<Context> {\n const match = await findProject(project);\n return buildContext(match.id, match.name, \"explicit\");\n}\n\n/** Returns the cached context without resolving, if any. */\nexport function peekContext(): Context | null {\n return cached;\n}\n","import { TimetickError } from \"@timetick/sdk\";\n\ntype ToolResult = {\n content: { type: \"text\"; text: string }[];\n isError?: boolean;\n};\n\n/** Wraps a value into an MCP text result, pretty-printing objects as JSON. */\nexport function ok(text: string, data?: unknown): ToolResult {\n const body = data === undefined ? text : `${text}\\n\\n${JSON.stringify(data, null, 2)}`;\n return { content: [{ type: \"text\", text: body }] };\n}\n\nexport function fail(message: string): ToolResult {\n return { content: [{ type: \"text\", text: message }], isError: true };\n}\n\n/** Runs a tool handler, normalizing SDK/unknown errors into an isError result. */\nexport async function guard(fn: () => Promise<ToolResult>): Promise<ToolResult> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof TimetickError) return fail(`Timetick API error: ${err.message}`);\n return fail(err instanceof Error ? err.message : String(err));\n }\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimWorld, resolveSimulator } from \"../resolve.js\";\nimport { paginationArgs, pagination, pageSummary } from \"../pagination.js\";\nimport { ok, guard } from \"../result.js\";\n\nconst protocolEnum = z.enum([\"OCPP_1_6\", \"OCPP_2_0_1\"]);\n\nfunction summarizeArgs(params: unknown): Record<string, string> | undefined {\n if (!params || typeof params !== \"object\") return undefined;\n const summary: Record<string, string> = {};\n for (const [name, spec] of Object.entries(params as Record<string, unknown>)) {\n if (!spec || typeof spec !== \"object\") continue;\n const { type, allowNull, defaultValue, visible } = spec as Record<string, unknown>;\n if (visible === false) continue;\n const parts = [String(type ?? \"unknown\"), allowNull === false ? \"required\" : \"optional\"];\n if (defaultValue !== undefined) parts.push(`default ${JSON.stringify(defaultValue)}`);\n summary[name] = parts.join(\", \");\n }\n return Object.keys(summary).length > 0 ? summary : undefined;\n}\n\nexport function registerDataTools(server: McpServer) {\n server.registerTool(\n \"list_projects\",\n {\n title: \"List projects\",\n description: \"List all projects available to your API key.\",\n inputSchema: {},\n },\n async () =>\n guard(async () => {\n const projects = await sdk().projects.list();\n return ok(`Found ${projects?.length ?? 0} project(s).`, projects);\n }),\n );\n\n server.registerTool(\n \"list_simulators\",\n {\n title: \"List simulators\",\n description:\n \"List simulators (virtual chargers) in the active project's sim world. \" +\n \"Optionally filter by name search or protocol.\",\n inputSchema: {\n search: z.string().optional().describe(\"Filter by simulator base name\"),\n protocol: protocolEnum.optional(),\n ...paginationArgs,\n project: z.string().optional().describe(\"Project name or id (defaults to active project)\"),\n },\n },\n async ({ search, protocol, page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().simulators.list({\n simWorldId: ctx.simWorldId,\n searchValue: search,\n filters: protocol ? { protocol } : undefined,\n pagination: pagination(page, pageSize),\n });\n return ok(\n pageSummary(\"simulator(s)\", result?.records?.length ?? 0, result?.pageInfo),\n result,\n );\n }),\n );\n\n server.registerTool(\n \"get_simulator\",\n {\n title: \"Get simulator\",\n description:\n \"Get a simulator's details and live status (network status, launched/power status, connectors) \" +\n \"by id or base name.\",\n inputSchema: {\n simulator: z.string().describe(\"Simulator id or base name\"),\n project: z.string().optional().describe(\"Project name or id (defaults to active project)\"),\n },\n },\n async ({ simulator, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const resolved = await resolveSimulator(ctx.simWorldId, simulator);\n const details = await sdk().simulators.get(resolved.id);\n return ok(`Simulator ${resolved.baseName} (id: ${resolved.id}).`, details);\n }),\n );\n\n server.registerTool(\n \"list_hardware\",\n {\n title: \"List hardware\",\n description: \"List physical OCPP hardware devices in the active project's sim world.\",\n inputSchema: {\n search: z.string().optional().describe(\"Filter by serial number / model\"),\n ...paginationArgs,\n project: z.string().optional().describe(\"Project name or id (defaults to active project)\"),\n },\n },\n async ({ search, page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().hardware.list({\n deviceScopeId: ctx.deviceScopeId,\n searchValue: search,\n pagination: pagination(page, pageSize),\n });\n return ok(\n pageSummary(\"hardware device(s)\", result?.records?.length ?? 0, result?.pageInfo),\n result,\n );\n }),\n );\n\n server.registerTool(\n \"list_actions\",\n {\n title: \"List actions (test steps)\",\n description:\n \"List available actions (steps) you can use to build a test — e.g. 'Power On Charger', \" +\n \"'Plug In Connector', 'Charge a Vehicle'. Filter by protocol. Each action lists its \" +\n \"arguments: the name, whether it is required (allowNull false), and any default.\",\n inputSchema: {\n protocol: protocolEnum.optional(),\n search: z.string().optional().describe(\"Filter by action name\"),\n ...paginationArgs,\n project: z.string().optional().describe(\"Project name or id (defaults to active project)\"),\n },\n },\n async ({ protocol, search, page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().actions.list({\n simWorldId: ctx.simWorldId,\n searchValue: search,\n filters: { isSystem: true, deviceType: \"CHARGER\", ...(protocol ? { protocol } : {}) },\n pagination: pagination(page, pageSize, 100),\n });\n const actions = (result?.records ?? []).map((a) => ({\n name: a?.name,\n description: a?.description ?? undefined,\n args: summarizeArgs(a?.params),\n }));\n return ok(pageSummary(\"action(s)\", actions.length, result?.pageInfo), actions);\n }),\n );\n}\n","import { sdk } from \"./sdk.js\";\nimport { getContext, lookupProject, type Context } from \"./context.js\";\n\n/** Resolves the sim world to operate in: explicit project arg wins, else session context. */\nexport async function resolveSimWorld(project?: string): Promise<Context> {\n if (project) return lookupProject(project);\n return getContext();\n}\n\nexport interface ResolvedSimulator {\n id: string;\n baseName: string;\n protocol: string;\n}\n\n/** Resolves a simulator by id or by (case-insensitive) base name within a sim world. */\nexport async function resolveSimulator(\n simWorldId: string,\n simulator: string,\n): Promise<ResolvedSimulator> {\n const needle = simulator.toLowerCase();\n const page = await sdk().simulators.list({ simWorldId, searchValue: simulator });\n const records = page?.records ?? [];\n\n const exact =\n records.find((r) => r?.id === simulator) ??\n records.find((r) => r?.baseName?.toLowerCase() === needle) ??\n (records.length === 1 ? records[0] : undefined);\n\n if (exact?.id) {\n return { id: exact.id, baseName: exact.baseName ?? \"\", protocol: String(exact.protocol) };\n }\n\n // Fall back to treating the input as a raw id.\n const byId = await sdk().simulators.get(simulator).catch(() => null);\n if (byId?.id) {\n return { id: byId.id, baseName: byId.baseName ?? \"\", protocol: String(byId.protocol) };\n }\n\n const candidates = records.map((r) => ` - ${r?.baseName} (id: ${r?.id})`).join(\"\\n\");\n throw new Error(\n `No simulator matching \"${simulator}\".` +\n (candidates ? `\\nCandidates:\\n${candidates}` : \" No simulators found in this sim world.\"),\n );\n}\n\n/** Finds a system action by exact name for a given protocol; returns its id. */\nexport async function resolveActionId(\n simWorldId: string,\n protocol: string,\n actionName: string,\n): Promise<string> {\n const page = await sdk().actions.list({\n simWorldId,\n searchValue: actionName,\n filters: { isSystem: true, protocol: protocol as any, deviceType: \"CHARGER\" },\n pagination: { page: 1, pageSize: 50 },\n });\n const records = page?.records ?? [];\n const needle = actionName.toLowerCase();\n const match =\n records.find((a) => a?.name?.toLowerCase() === needle) ??\n records.find((a) => a?.name?.toLowerCase().includes(needle));\n\n if (!match?.id) {\n const available = records.map((a) => ` - ${a?.name}`).join(\"\\n\");\n throw new Error(\n `Action \"${actionName}\" not found for ${protocol}.` +\n (available ? `\\nAvailable system actions matching your search:\\n${available}` : \"\"),\n );\n }\n return match.id;\n}\n","import { z } from \"zod\";\n\nexport const DEFAULT_PAGE_SIZE = 20;\n\nexport const paginationArgs = {\n page: z.number().int().min(1).optional().describe(\"Page number, starting at 1 (default 1)\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(200)\n .optional()\n .describe(`Results per page (default ${DEFAULT_PAGE_SIZE})`),\n};\n\nexport function pagination(page?: number, pageSize?: number, defaultPageSize = DEFAULT_PAGE_SIZE) {\n return { page: page ?? 1, pageSize: pageSize ?? defaultPageSize };\n}\n\ninterface PageInfoLike {\n page?: number | null;\n pageSize?: number | null;\n hasNextPage?: boolean | null;\n totalCount?: number | null;\n}\n\nexport function pageSummary(noun: string, count: number, pageInfo?: PageInfoLike | null): string {\n const total = pageInfo?.totalCount;\n const head =\n total != null && total !== count\n ? `Showing ${count} of ${total} ${noun} (page ${pageInfo?.page ?? 1}).`\n : `Found ${count} ${noun}.`;\n return pageInfo?.hasNextPage ? `${head} More results available — request the next page.` : head;\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimWorld, resolveSimulator, resolveActionId } from \"../resolve.js\";\nimport { ok, guard } from \"../result.js\";\n\n/** Runs a single named action against a simulator via the SDK's runAction. */\nasync function runOnSimulator(\n project: string | undefined,\n simulator: string,\n actionName: string,\n args: { key: string; value: string }[] = [],\n) {\n const ctx = await resolveSimWorld(project);\n const sim = await resolveSimulator(ctx.simWorldId, simulator);\n const actionId = await resolveActionId(ctx.simWorldId, sim.protocol, actionName);\n\n const status = await sdk().testRuns.runAction({\n chargerId: sim.id,\n action: { requirementKey: \"1\", stream: 1, order: 1, actionId, args },\n });\n return { sim, actionName, status };\n}\n\nexport function registerSimulatorTools(server: McpServer) {\n const projectArg = z\n .string()\n .optional()\n .describe(\"Project name or id (defaults to active project)\");\n\n server.registerTool(\n \"power_on_simulator\",\n {\n title: \"Power on simulator\",\n description: \"Power on a simulator (boots it and connects it to the OCPP server).\",\n inputSchema: {\n simulator: z.string().describe(\"Simulator id or base name\"),\n project: projectArg,\n },\n },\n async ({ simulator, project }) =>\n guard(async () => {\n const { sim, status } = await runOnSimulator(project, simulator, \"Power On Charger\");\n return ok(`Powered on ${sim.baseName} (id: ${sim.id}).`, { status });\n }),\n );\n\n server.registerTool(\n \"power_off_simulator\",\n {\n title: \"Power off simulator\",\n description: \"Power off a simulator.\",\n inputSchema: {\n simulator: z.string().describe(\"Simulator id or base name\"),\n project: projectArg,\n },\n },\n async ({ simulator, project }) =>\n guard(async () => {\n const { sim, status } = await runOnSimulator(project, simulator, \"Power Off Charger\");\n return ok(`Powered off ${sim.baseName} (id: ${sim.id}).`, { status });\n }),\n );\n\n server.registerTool(\n \"run_simulator_action\",\n {\n title: \"Run simulator action\",\n description:\n \"Run a single named action against a simulator — e.g. 'Reset Charger', 'Plug In Connector', \" +\n \"'Plug Out Connector'. Use list_actions to discover names and required args. \" +\n \"Args are passed as key/value pairs (e.g. connectorId=1).\",\n inputSchema: {\n simulator: z.string().describe(\"Simulator id or base name\"),\n actionName: z.string().describe(\"Exact action name, e.g. 'Reset Charger'\"),\n args: z\n .array(z.object({ key: z.string(), value: z.string() }))\n .optional()\n .describe(\"Action arguments as key/value pairs\"),\n project: projectArg,\n },\n },\n async ({ simulator, actionName, args, project }) =>\n guard(async () => {\n const { sim, status } = await runOnSimulator(project, simulator, actionName, args ?? []);\n return ok(`Ran \"${actionName}\" on ${sim.baseName} (id: ${sim.id}).`, { status });\n }),\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimWorld, resolveActionId } from \"../resolve.js\";\nimport { paginationArgs, pagination, pageSummary } from \"../pagination.js\";\nimport { ok, guard } from \"../result.js\";\n\nconst protocolEnum = z.enum([\"OCPP_1_6\", \"OCPP_2_0_1\"]);\n\nexport function registerTestTools(server: McpServer) {\n const projectArg = z\n .string()\n .optional()\n .describe(\"Project name or id (defaults to active project)\");\n\n server.registerTool(\n \"list_tests\",\n {\n title: \"List tests\",\n description: \"List tests (scenarios) in the active project's sim world.\",\n inputSchema: {\n protocol: protocolEnum.optional(),\n search: z.string().optional().describe(\"Filter by test name\"),\n ...paginationArgs,\n project: projectArg,\n },\n },\n async ({ protocol, search, page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().tests.list({\n simWorldId: ctx.simWorldId,\n searchValue: search,\n filters: protocol ? { protocol } : undefined,\n pagination: pagination(page, pageSize),\n });\n return ok(pageSummary(\"test(s)\", result?.records?.length ?? 0, result?.pageInfo), result);\n }),\n );\n\n server.registerTool(\n \"get_test\",\n {\n title: \"Get test\",\n description: \"Get a test by id, including its device requirements.\",\n inputSchema: {\n testId: z.string().describe(\"Test (scenario) id\"),\n },\n },\n async ({ testId }) =>\n guard(async () => {\n const test = await sdk().tests.get(testId);\n if (!test) return ok(`No test found with id ${testId}.`);\n return ok(`Test ${test.name} (id: ${testId}).`, test);\n }),\n );\n\n server.registerTool(\n \"create_test\",\n {\n title: \"Create test\",\n description:\n \"Create a test from an ordered list of steps. Each step references an action by name \" +\n \"(use list_actions to discover them) plus optional key/value args. The steps run in order \" +\n \"against a single simulator. Example steps: 'Power On Charger', 'Plug In Connector', \" +\n \"'Use RFID Card', 'Charge a Vehicle', 'Plug Out Connector', 'Power Off Charger'.\",\n inputSchema: {\n name: z.string().describe(\"Test name\"),\n protocol: protocolEnum,\n steps: z\n .array(\n z.object({\n actionName: z.string().describe(\"Exact action name, e.g. 'Power On Charger'\"),\n args: z\n .array(z.object({ key: z.string(), value: z.string() }))\n .optional()\n .describe(\"Action arguments as key/value pairs\"),\n }),\n )\n .min(1)\n .describe(\"Ordered list of steps\"),\n project: projectArg,\n },\n },\n async ({ name, protocol, steps, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n\n const actions = [];\n for (let i = 0; i < steps.length; i++) {\n const actionId = await resolveActionId(ctx.simWorldId, protocol, steps[i].actionName);\n actions.push({\n order: i + 1,\n stream: 1,\n actionId,\n requirementKey: \"charger\",\n args: steps[i].args ?? [],\n });\n }\n\n const test = await sdk().tests.create({\n simWorldId: ctx.simWorldId,\n name,\n deviceType: \"CHARGER\",\n requirements: [\n { key: \"charger\", type: \"CHARGER\", params: [{ key: \"protocol\", value: protocol }] },\n ],\n actions,\n });\n return ok(`Created test \"${name}\" (id: ${test?.id}).`, test);\n }),\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimWorld, resolveSimulator } from \"../resolve.js\";\nimport { paginationArgs, pagination, pageSummary } from \"../pagination.js\";\nimport { ok, fail, guard } from \"../result.js\";\n\nexport function registerTestRunTools(server: McpServer) {\n const projectArg = z\n .string()\n .optional()\n .describe(\"Project name or id (defaults to active project)\");\n\n server.registerTool(\n \"run_test\",\n {\n title: \"Run test\",\n description:\n \"Run a test (scenario) on a simulator, starting immediately. \" +\n \"Returns the test run id and status — poll with get_test_run to follow it. \" +\n \"A simulator runs one test at a time; if it is busy this says so and starts nothing.\",\n inputSchema: {\n testId: z.string().describe(\"Test (scenario) id to run\"),\n simulator: z.string().describe(\"Simulator id or base name to run the test on\"),\n project: projectArg,\n },\n },\n async ({ testId, simulator, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const test = await sdk().tests.get(testId);\n const requirementId = test?.requirements?.[0]?.id;\n if (!requirementId) return fail(`Test ${testId} has no device requirement to assign.`);\n\n const sim = await resolveSimulator(ctx.simWorldId, simulator);\n const run = await sdk()\n .testRuns.create({\n scenarioId: testId,\n simWorldId: ctx.simWorldId,\n scheduleMode: \"NOW\",\n speedup: 1,\n forceDelete: false,\n requirementDevices: [\n { requirementId, deviceId: sim.id, deviceMode: \"SIMULATOR\" },\n ],\n })\n .catch(async (err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n if (!/conflict/i.test(message)) throw err;\n\n const details = await sdk().simulators.get(sim.id).catch(() => null);\n const blocking = details?.lastRun;\n throw new Error(\n `${sim.baseName} (id: ${sim.id}) is busy` +\n (blocking?.id ? ` with test run ${blocking.id} (${blocking.status})` : \"\") +\n `, so nothing was started. Stop that run with stop_test_run — the simulator is ` +\n `free once it stops — then run this test again.`,\n );\n });\n return ok(\n `Started test \"${test?.name}\" on ${sim.baseName} — run id ${run?.id}, status ${run?.status}.`,\n run,\n );\n }),\n );\n\n server.registerTool(\n \"get_test_run\",\n {\n title: \"Get test run\",\n description: \"Get a test run's status by id.\",\n inputSchema: {\n runId: z.string().describe(\"Test run id\"),\n },\n },\n async ({ runId }) =>\n guard(async () => {\n const run = await sdk().testRuns.get(runId);\n if (!run) return ok(`No test run found with id ${runId}.`);\n const reason = run.failReason ? ` — ${run.failReason}` : \"\";\n return ok(`Test run ${runId} (\"${run.scenario?.name}\"): ${run.status}${reason}.`, run);\n }),\n );\n\n server.registerTool(\n \"list_test_runs\",\n {\n title: \"List test runs\",\n description: \"List recent test runs in the active project's sim world.\",\n inputSchema: {\n ...paginationArgs,\n project: projectArg,\n },\n },\n async ({ page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().testRuns.list({\n simWorldId: ctx.simWorldId,\n pagination: pagination(page, pageSize),\n });\n return ok(pageSummary(\"test run(s)\", result?.records?.length ?? 0, result?.pageInfo), result);\n }),\n );\n\n server.registerTool(\n \"stop_test_run\",\n {\n title: \"Stop test run\",\n description: \"Shut down (stop) a running or scheduled test run by id.\",\n inputSchema: {\n runId: z.string().describe(\"Test run id\"),\n },\n },\n async ({ runId }) =>\n guard(async () => {\n const existing = await sdk().testRuns.get(runId);\n if (!existing) return fail(`No test run found with id ${runId}.`);\n\n await sdk().testRuns.shutDown(runId);\n const after = await sdk().testRuns.get(runId);\n return ok(\n `Shut down test run ${runId} (was ${existing.status}, now ${after?.status ?? \"unknown\"}).`,\n after,\n );\n }),\n );\n}\n"],"mappings":";;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;;;ACDrC,SAAS,SAAS;;;ACAlB,SAAS,oBAAyC;AAElD,IAAI,SAAgC;AAG7B,SAAS,MAAsB;AACpC,MAAI,OAAQ,QAAO;AAEnB,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,WAAS,aAAa,EAAE,QAAQ,UAAU,QAAQ,IAAI,iBAAiB,CAAC;AACxE,SAAO;AACT;;;ACLO,IAAM,eAAN,cAA2B,MAAM;AAAC;AAEzC,IAAI,SAAyB;AAG7B,eAAe,aACb,WACA,aACA,QACkB;AAClB,QAAM,SAAS,MAAM,IAAI,EAAE,UAAU,KAAK,SAAS;AACnD,QAAM,QAAQ,SAAS,CAAC;AACxB,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,IAAI,aAAa,YAAY,WAAW,mCAAmC;AAAA,EACnF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAY,MAAM;AAAA,IAClB,eAAe,MAAM,iBAAiB,MAAM;AAAA,IAC5C;AAAA,EACF;AACF;AAEA,IAAM,qBAAqB;AAI3B,SAAS,eAAe,UAAgE;AACtF,QAAM,MAAM,YAAY,CAAC;AACzB,QAAM,QAAQ,IACX,MAAM,GAAG,kBAAkB,EAC3B,IAAI,CAAC,MAAM,OAAO,GAAG,IAAI,SAAS,GAAG,EAAE,GAAG;AAC7C,MAAI,IAAI,SAAS,oBAAoB;AACnC,UAAM,KAAK,eAAU,IAAI,SAAS,kBAAkB,kDAA6C;AAAA,EACnG;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,eAAe,YAAY,SAAwD;AACjF,QAAM,WAAW,MAAM,IAAI,EAAE,SAAS,KAAK;AAC3C,QAAM,SAAS,QAAQ,YAAY;AACnC,QAAM,QAAQ,UAAU;AAAA,IACtB,CAAC,MAAM,GAAG,OAAO,WAAW,GAAG,MAAM,YAAY,MAAM;AAAA,EACzD;AACA,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,IAAI;AAAA,MACR,wBAAwB,OAAO;AAAA,EAAkB,eAAe,QAAQ,CAAC;AAAA,IAC3E;AAAA,EACF;AACA,SAAO,EAAE,IAAI,MAAM,IAAI,MAAM,MAAM,QAAQ,YAAY;AACzD;AAMA,eAAsB,aAA+B;AACnD,MAAI,OAAQ,QAAO;AAEnB,QAAM,WAAW,MAAM,IAAI,EAAE,SAAS,KAAK;AAC3C,MAAI,CAAC,YAAY,SAAS,WAAW,GAAG;AACtC,UAAM,IAAI,aAAa,6CAA6C;AAAA,EACtE;AACA,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,YAAY,SAAS,MAAM;AAAA,EACd,eAAe,QAAQ,CAAC;AAAA,IACvC;AAAA,EACF;AAEA,QAAM,OAAO,SAAS,CAAC;AACvB,WAAS,MAAM,aAAa,KAAK,IAAK,KAAK,QAAQ,aAAa,gBAAgB;AAChF,SAAO;AACT;AAGA,eAAsB,WAAW,OAA8C;AAC7E,QAAM,QAAQ,MAAM,YAAY,MAAM,OAAO;AAC7C,WAAS,MAAM,aAAa,MAAM,IAAI,MAAM,MAAM,UAAU;AAC5D,SAAO;AACT;AAEA,eAAsB,cAAc,SAAmC;AACrE,QAAM,QAAQ,MAAM,YAAY,OAAO;AACvC,SAAO,aAAa,MAAM,IAAI,MAAM,MAAM,UAAU;AACtD;AAGO,SAAS,cAA8B;AAC5C,SAAO;AACT;;;ACxGA,SAAS,qBAAqB;AAQvB,SAAS,GAAG,MAAc,MAA4B;AAC3D,QAAM,OAAO,SAAS,SAAY,OAAO,GAAG,IAAI;AAAA;AAAA,EAAO,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACpF,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,CAAC,EAAE;AACnD;AAEO,SAAS,KAAK,SAA6B;AAChD,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK;AACrE;AAGA,eAAsB,MAAM,IAAoD;AAC9E,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,SAAS,KAAK;AACZ,QAAI,eAAe,cAAe,QAAO,KAAK,uBAAuB,IAAI,OAAO,EAAE;AAClF,WAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAC9D;AACF;;;AHpBO,SAAS,qBAAqB,QAAmB;AACtD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa,CAAC;AAAA,IAChB;AAAA,IACA,YACE,MAAM,YAAY;AAChB,YAAM,MAAM,YAAY,KAAM,MAAM,WAAW;AAC/C,aAAO;AAAA,QACL,mBAAmB,IAAI,WAAW,SAAS,IAAI,SAAS,iBAAiB,IAAI,UAAU,KAAK,IAAI,MAAM;AAAA,QACtG;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,SAAS,EAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,MAClE;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAQ,MACf,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,WAAW,EAAE,QAAQ,CAAC;AACxC,aAAO,GAAG,yBAAyB,IAAI,WAAW,SAAS,IAAI,SAAS,MAAM,GAAG;AAAA,IACnF,CAAC;AAAA,EACL;AACF;;;AI1CA,SAAS,KAAAA,UAAS;;;ACIlB,eAAsB,gBAAgB,SAAoC;AACxE,MAAI,QAAS,QAAO,cAAc,OAAO;AACzC,SAAO,WAAW;AACpB;AASA,eAAsB,iBACpB,YACA,WAC4B;AAC5B,QAAM,SAAS,UAAU,YAAY;AACrC,QAAM,OAAO,MAAM,IAAI,EAAE,WAAW,KAAK,EAAE,YAAY,aAAa,UAAU,CAAC;AAC/E,QAAM,UAAU,MAAM,WAAW,CAAC;AAElC,QAAM,QACJ,QAAQ,KAAK,CAAC,MAAM,GAAG,OAAO,SAAS,KACvC,QAAQ,KAAK,CAAC,MAAM,GAAG,UAAU,YAAY,MAAM,MAAM,MACxD,QAAQ,WAAW,IAAI,QAAQ,CAAC,IAAI;AAEvC,MAAI,OAAO,IAAI;AACb,WAAO,EAAE,IAAI,MAAM,IAAI,UAAU,MAAM,YAAY,IAAI,UAAU,OAAO,MAAM,QAAQ,EAAE;AAAA,EAC1F;AAGA,QAAM,OAAO,MAAM,IAAI,EAAE,WAAW,IAAI,SAAS,EAAE,MAAM,MAAM,IAAI;AACnE,MAAI,MAAM,IAAI;AACZ,WAAO,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,YAAY,IAAI,UAAU,OAAO,KAAK,QAAQ,EAAE;AAAA,EACvF;AAEA,QAAM,aAAa,QAAQ,IAAI,CAAC,MAAM,OAAO,GAAG,QAAQ,SAAS,GAAG,EAAE,GAAG,EAAE,KAAK,IAAI;AACpF,QAAM,IAAI;AAAA,IACR,0BAA0B,SAAS,QAChC,aAAa;AAAA;AAAA,EAAkB,UAAU,KAAK;AAAA,EACnD;AACF;AAGA,eAAsB,gBACpB,YACA,UACA,YACiB;AACjB,QAAM,OAAO,MAAM,IAAI,EAAE,QAAQ,KAAK;AAAA,IACpC;AAAA,IACA,aAAa;AAAA,IACb,SAAS,EAAE,UAAU,MAAM,UAA2B,YAAY,UAAU;AAAA,IAC5E,YAAY,EAAE,MAAM,GAAG,UAAU,GAAG;AAAA,EACtC,CAAC;AACD,QAAM,UAAU,MAAM,WAAW,CAAC;AAClC,QAAM,SAAS,WAAW,YAAY;AACtC,QAAM,QACJ,QAAQ,KAAK,CAAC,MAAM,GAAG,MAAM,YAAY,MAAM,MAAM,KACrD,QAAQ,KAAK,CAAC,MAAM,GAAG,MAAM,YAAY,EAAE,SAAS,MAAM,CAAC;AAE7D,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,YAAY,QAAQ,IAAI,CAAC,MAAM,OAAO,GAAG,IAAI,EAAE,EAAE,KAAK,IAAI;AAChE,UAAM,IAAI;AAAA,MACR,WAAW,UAAU,mBAAmB,QAAQ,OAC7C,YAAY;AAAA;AAAA,EAAqD,SAAS,KAAK;AAAA,IACpF;AAAA,EACF;AACA,SAAO,MAAM;AACf;;;ACxEA,SAAS,KAAAC,UAAS;AAEX,IAAM,oBAAoB;AAE1B,IAAM,iBAAiB;AAAA,EAC5B,MAAMA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,EAC1F,UAAUA,GACP,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,GAAG,EACP,SAAS,EACT,SAAS,6BAA6B,iBAAiB,GAAG;AAC/D;AAEO,SAAS,WAAW,MAAe,UAAmB,kBAAkB,mBAAmB;AAChG,SAAO,EAAE,MAAM,QAAQ,GAAG,UAAU,YAAY,gBAAgB;AAClE;AASO,SAAS,YAAY,MAAc,OAAe,UAAwC;AAC/F,QAAM,QAAQ,UAAU;AACxB,QAAM,OACJ,SAAS,QAAQ,UAAU,QACvB,WAAW,KAAK,OAAO,KAAK,IAAI,IAAI,UAAU,UAAU,QAAQ,CAAC,OACjE,SAAS,KAAK,IAAI,IAAI;AAC5B,SAAO,UAAU,cAAc,GAAG,IAAI,0DAAqD;AAC7F;;;AF1BA,IAAM,eAAeC,GAAE,KAAK,CAAC,YAAY,YAAY,CAAC;AAEtD,SAAS,cAAc,QAAqD;AAC1E,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC5E,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAM,EAAE,MAAM,WAAW,cAAc,QAAQ,IAAI;AACnD,QAAI,YAAY,MAAO;AACvB,UAAM,QAAQ,CAAC,OAAO,QAAQ,SAAS,GAAG,cAAc,QAAQ,aAAa,UAAU;AACvF,QAAI,iBAAiB,OAAW,OAAM,KAAK,WAAW,KAAK,UAAU,YAAY,CAAC,EAAE;AACpF,YAAQ,IAAI,IAAI,MAAM,KAAK,IAAI;AAAA,EACjC;AACA,SAAO,OAAO,KAAK,OAAO,EAAE,SAAS,IAAI,UAAU;AACrD;AAEO,SAAS,kBAAkB,QAAmB;AACnD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,CAAC;AAAA,IAChB;AAAA,IACA,YACE,MAAM,YAAY;AAChB,YAAM,WAAW,MAAM,IAAI,EAAE,SAAS,KAAK;AAC3C,aAAO,GAAG,SAAS,UAAU,UAAU,CAAC,gBAAgB,QAAQ;AAAA,IAClE,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+BAA+B;AAAA,QACtE,UAAU,aAAa,SAAS;AAAA,QAChC,GAAG;AAAA,QACH,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MAC3F;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAQ,UAAU,MAAM,UAAU,QAAQ,MACjD,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,WAAW,KAAK;AAAA,QACzC,YAAY,IAAI;AAAA,QAChB,aAAa;AAAA,QACb,SAAS,WAAW,EAAE,SAAS,IAAI;AAAA,QACnC,YAAY,WAAW,MAAM,QAAQ;AAAA,MACvC,CAAC;AACD,aAAO;AAAA,QACL,YAAY,gBAAgB,QAAQ,SAAS,UAAU,GAAG,QAAQ,QAAQ;AAAA,QAC1E;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,QAC1D,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MAC3F;AAAA,IACF;AAAA,IACA,OAAO,EAAE,WAAW,QAAQ,MAC1B,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,WAAW,MAAM,iBAAiB,IAAI,YAAY,SAAS;AACjE,YAAM,UAAU,MAAM,IAAI,EAAE,WAAW,IAAI,SAAS,EAAE;AACtD,aAAO,GAAG,aAAa,SAAS,QAAQ,SAAS,SAAS,EAAE,MAAM,OAAO;AAAA,IAC3E,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iCAAiC;AAAA,QACxE,GAAG;AAAA,QACH,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MAC3F;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAQ,MAAM,UAAU,QAAQ,MACvC,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,SAAS,KAAK;AAAA,QACvC,eAAe,IAAI;AAAA,QACnB,aAAa;AAAA,QACb,YAAY,WAAW,MAAM,QAAQ;AAAA,MACvC,CAAC;AACD,aAAO;AAAA,QACL,YAAY,sBAAsB,QAAQ,SAAS,UAAU,GAAG,QAAQ,QAAQ;AAAA,QAChF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,UAAU,aAAa,SAAS;AAAA,QAChC,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uBAAuB;AAAA,QAC9D,GAAG;AAAA,QACH,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MAC3F;AAAA,IACF;AAAA,IACA,OAAO,EAAE,UAAU,QAAQ,MAAM,UAAU,QAAQ,MACjD,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,QAAQ,KAAK;AAAA,QACtC,YAAY,IAAI;AAAA,QAChB,aAAa;AAAA,QACb,SAAS,EAAE,UAAU,MAAM,YAAY,WAAW,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC,EAAG;AAAA,QACpF,YAAY,WAAW,MAAM,UAAU,GAAG;AAAA,MAC5C,CAAC;AACD,YAAM,WAAW,QAAQ,WAAW,CAAC,GAAG,IAAI,CAAC,OAAO;AAAA,QAClD,MAAM,GAAG;AAAA,QACT,aAAa,GAAG,eAAe;AAAA,QAC/B,MAAM,cAAc,GAAG,MAAM;AAAA,MAC/B,EAAE;AACF,aAAO,GAAG,YAAY,aAAa,QAAQ,QAAQ,QAAQ,QAAQ,GAAG,OAAO;AAAA,IAC/E,CAAC;AAAA,EACL;AACF;;;AGnJA,SAAS,KAAAC,UAAS;AAOlB,eAAe,eACb,SACA,WACA,YACA,OAAyC,CAAC,GAC1C;AACA,QAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,QAAM,MAAM,MAAM,iBAAiB,IAAI,YAAY,SAAS;AAC5D,QAAM,WAAW,MAAM,gBAAgB,IAAI,YAAY,IAAI,UAAU,UAAU;AAE/E,QAAM,SAAS,MAAM,IAAI,EAAE,SAAS,UAAU;AAAA,IAC5C,WAAW,IAAI;AAAA,IACf,QAAQ,EAAE,gBAAgB,KAAK,QAAQ,GAAG,OAAO,GAAG,UAAU,KAAK;AAAA,EACrE,CAAC;AACD,SAAO,EAAE,KAAK,YAAY,OAAO;AACnC;AAEO,SAAS,uBAAuB,QAAmB;AACxD,QAAM,aAAaC,GAChB,OAAO,EACP,SAAS,EACT,SAAS,iDAAiD;AAE7D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,QAC1D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,WAAW,QAAQ,MAC1B,MAAM,YAAY;AAChB,YAAM,EAAE,KAAK,OAAO,IAAI,MAAM,eAAe,SAAS,WAAW,kBAAkB;AACnF,aAAO,GAAG,cAAc,IAAI,QAAQ,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,IACrE,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,QAC1D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,WAAW,QAAQ,MAC1B,MAAM,YAAY;AAChB,YAAM,EAAE,KAAK,OAAO,IAAI,MAAM,eAAe,SAAS,WAAW,mBAAmB;AACpF,aAAO,GAAG,eAAe,IAAI,QAAQ,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,IACtE,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,QAC1D,YAAYA,GAAE,OAAO,EAAE,SAAS,yCAAyC;AAAA,QACzE,MAAMA,GACH,MAAMA,GAAE,OAAO,EAAE,KAAKA,GAAE,OAAO,GAAG,OAAOA,GAAE,OAAO,EAAE,CAAC,CAAC,EACtD,SAAS,EACT,SAAS,qCAAqC;AAAA,QACjD,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,WAAW,YAAY,MAAM,QAAQ,MAC5C,MAAM,YAAY;AAChB,YAAM,EAAE,KAAK,OAAO,IAAI,MAAM,eAAe,SAAS,WAAW,YAAY,QAAQ,CAAC,CAAC;AACvF,aAAO,GAAG,QAAQ,UAAU,QAAQ,IAAI,QAAQ,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,IACjF,CAAC;AAAA,EACL;AACF;;;ACxFA,SAAS,KAAAC,UAAS;AAOlB,IAAMC,gBAAeC,GAAE,KAAK,CAAC,YAAY,YAAY,CAAC;AAE/C,SAAS,kBAAkB,QAAmB;AACnD,QAAM,aAAaA,GAChB,OAAO,EACP,SAAS,EACT,SAAS,iDAAiD;AAE7D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,UAAUD,cAAa,SAAS;AAAA,QAChC,QAAQC,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qBAAqB;AAAA,QAC5D,GAAG;AAAA,QACH,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,UAAU,QAAQ,MAAM,UAAU,QAAQ,MACjD,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,MAAM,KAAK;AAAA,QACpC,YAAY,IAAI;AAAA,QAChB,aAAa;AAAA,QACb,SAAS,WAAW,EAAE,SAAS,IAAI;AAAA,QACnC,YAAY,WAAW,MAAM,QAAQ;AAAA,MACvC,CAAC;AACD,aAAO,GAAG,YAAY,WAAW,QAAQ,SAAS,UAAU,GAAG,QAAQ,QAAQ,GAAG,MAAM;AAAA,IAC1F,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,QAAQA,GAAE,OAAO,EAAE,SAAS,oBAAoB;AAAA,MAClD;AAAA,IACF;AAAA,IACA,OAAO,EAAE,OAAO,MACd,MAAM,YAAY;AAChB,YAAM,OAAO,MAAM,IAAI,EAAE,MAAM,IAAI,MAAM;AACzC,UAAI,CAAC,KAAM,QAAO,GAAG,yBAAyB,MAAM,GAAG;AACvD,aAAO,GAAG,QAAQ,KAAK,IAAI,SAAS,MAAM,MAAM,IAAI;AAAA,IACtD,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAIF,aAAa;AAAA,QACX,MAAMA,GAAE,OAAO,EAAE,SAAS,WAAW;AAAA,QACrC,UAAUD;AAAA,QACV,OAAOC,GACJ;AAAA,UACCA,GAAE,OAAO;AAAA,YACP,YAAYA,GAAE,OAAO,EAAE,SAAS,4CAA4C;AAAA,YAC5E,MAAMA,GACH,MAAMA,GAAE,OAAO,EAAE,KAAKA,GAAE,OAAO,GAAG,OAAOA,GAAE,OAAO,EAAE,CAAC,CAAC,EACtD,SAAS,EACT,SAAS,qCAAqC;AAAA,UACnD,CAAC;AAAA,QACH,EACC,IAAI,CAAC,EACL,SAAS,uBAAuB;AAAA,QACnC,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,UAAU,OAAO,QAAQ,MACtC,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AAEzC,YAAM,UAAU,CAAC;AACjB,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,WAAW,MAAM,gBAAgB,IAAI,YAAY,UAAU,MAAM,CAAC,EAAE,UAAU;AACpF,gBAAQ,KAAK;AAAA,UACX,OAAO,IAAI;AAAA,UACX,QAAQ;AAAA,UACR;AAAA,UACA,gBAAgB;AAAA,UAChB,MAAM,MAAM,CAAC,EAAE,QAAQ,CAAC;AAAA,QAC1B,CAAC;AAAA,MACH;AAEA,YAAM,OAAO,MAAM,IAAI,EAAE,MAAM,OAAO;AAAA,QACpC,YAAY,IAAI;AAAA,QAChB;AAAA,QACA,YAAY;AAAA,QACZ,cAAc;AAAA,UACZ,EAAE,KAAK,WAAW,MAAM,WAAW,QAAQ,CAAC,EAAE,KAAK,YAAY,OAAO,SAAS,CAAC,EAAE;AAAA,QACpF;AAAA,QACA;AAAA,MACF,CAAC;AACD,aAAO,GAAG,iBAAiB,IAAI,UAAU,MAAM,EAAE,MAAM,IAAI;AAAA,IAC7D,CAAC;AAAA,EACL;AACF;;;AChHA,SAAS,KAAAC,UAAS;AAOX,SAAS,qBAAqB,QAAmB;AACtD,QAAM,aAAaC,GAChB,OAAO,EACP,SAAS,EACT,SAAS,iDAAiD;AAE7D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,QAAQA,GAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,QACvD,WAAWA,GAAE,OAAO,EAAE,SAAS,8CAA8C;AAAA,QAC7E,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAQ,WAAW,QAAQ,MAClC,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,OAAO,MAAM,IAAI,EAAE,MAAM,IAAI,MAAM;AACzC,YAAM,gBAAgB,MAAM,eAAe,CAAC,GAAG;AAC/C,UAAI,CAAC,cAAe,QAAO,KAAK,QAAQ,MAAM,uCAAuC;AAErF,YAAM,MAAM,MAAM,iBAAiB,IAAI,YAAY,SAAS;AAC5D,YAAM,MAAM,MAAM,IAAI,EACnB,SAAS,OAAO;AAAA,QACf,YAAY;AAAA,QACZ,YAAY,IAAI;AAAA,QAChB,cAAc;AAAA,QACd,SAAS;AAAA,QACT,aAAa;AAAA,QACb,oBAAoB;AAAA,UAClB,EAAE,eAAe,UAAU,IAAI,IAAI,YAAY,YAAY;AAAA,QAC7D;AAAA,MACF,CAAC,EACA,MAAM,OAAO,QAAiB;AAC7B,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAI,CAAC,YAAY,KAAK,OAAO,EAAG,OAAM;AAEtC,cAAM,UAAU,MAAM,IAAI,EAAE,WAAW,IAAI,IAAI,EAAE,EAAE,MAAM,MAAM,IAAI;AACnE,cAAM,WAAW,SAAS;AAC1B,cAAM,IAAI;AAAA,UACR,GAAG,IAAI,QAAQ,SAAS,IAAI,EAAE,eAC3B,UAAU,KAAK,kBAAkB,SAAS,EAAE,KAAK,SAAS,MAAM,MAAM,MACvE;AAAA,QAEJ;AAAA,MACF,CAAC;AACH,aAAO;AAAA,QACL,iBAAiB,MAAM,IAAI,QAAQ,IAAI,QAAQ,kBAAa,KAAK,EAAE,YAAY,KAAK,MAAM;AAAA,QAC1F;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,OAAOA,GAAE,OAAO,EAAE,SAAS,aAAa;AAAA,MAC1C;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,MACb,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,IAAI,EAAE,SAAS,IAAI,KAAK;AAC1C,UAAI,CAAC,IAAK,QAAO,GAAG,6BAA6B,KAAK,GAAG;AACzD,YAAM,SAAS,IAAI,aAAa,WAAM,IAAI,UAAU,KAAK;AACzD,aAAO,GAAG,YAAY,KAAK,MAAM,IAAI,UAAU,IAAI,OAAO,IAAI,MAAM,GAAG,MAAM,KAAK,GAAG;AAAA,IACvF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,GAAG;AAAA,QACH,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,UAAU,QAAQ,MAC/B,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,SAAS,KAAK;AAAA,QACvC,YAAY,IAAI;AAAA,QAChB,YAAY,WAAW,MAAM,QAAQ;AAAA,MACvC,CAAC;AACD,aAAO,GAAG,YAAY,eAAe,QAAQ,SAAS,UAAU,GAAG,QAAQ,QAAQ,GAAG,MAAM;AAAA,IAC9F,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,OAAOA,GAAE,OAAO,EAAE,SAAS,aAAa;AAAA,MAC1C;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,MACb,MAAM,YAAY;AAChB,YAAM,WAAW,MAAM,IAAI,EAAE,SAAS,IAAI,KAAK;AAC/C,UAAI,CAAC,SAAU,QAAO,KAAK,6BAA6B,KAAK,GAAG;AAEhE,YAAM,IAAI,EAAE,SAAS,SAAS,KAAK;AACnC,YAAM,QAAQ,MAAM,IAAI,EAAE,SAAS,IAAI,KAAK;AAC5C,aAAO;AAAA,QACL,sBAAsB,KAAK,SAAS,SAAS,MAAM,SAAS,OAAO,UAAU,SAAS;AAAA,QACtF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AACF;;;AVtHA,eAAe,OAAO;AACpB,MAAI,CAAC,QAAQ,IAAI,oBAAoB;AACnC,YAAQ;AAAA,MACN;AAAA,IAEF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,IAAI,UAAU,EAAE,MAAM,YAAY,SAAS,QAAQ,CAAC;AAEnE,uBAAqB,MAAM;AAC3B,oBAAkB,MAAM;AACxB,yBAAuB,MAAM;AAC7B,oBAAkB,MAAM;AACxB,uBAAqB,MAAM;AAE3B,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,UAAQ,MAAM,sCAAsC;AACtD;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,6CAA6C,KAAK;AAChE,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["z","z","z","z","z","z","protocolEnum","z","z","z"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/tools/context.ts","../src/sdk.ts","../src/context.ts","../src/result.ts","../src/tools/data.ts","../src/links.ts","../src/resolve.ts","../src/pagination.ts","../src/tools/simulators.ts","../src/tools/tests.ts","../src/tools/test-runs.ts","../src/tools/support.ts","../src/logs-format.ts","../src/tools/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { isReadOnlyMode, registerTools } from \"./tools/index.js\";\n\nasync function main() {\n if (!process.env.TIMETICK_API_TOKEN) {\n console.error(\n \"TIMETICK_API_TOKEN is not set. Generate an API key in the Timetick portal \" +\n \"(Account → API keys) and expose it to the MCP server as TIMETICK_API_TOKEN.\",\n );\n process.exit(1);\n }\n\n const server = new McpServer({ name: \"timetick\", version: \"0.2.1\" });\n\n const readOnly = isReadOnlyMode();\n registerTools(server, { readOnly });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`Timetick MCP server running on stdio${readOnly ? \" (read-only)\" : \"\"}`);\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error starting Timetick MCP server:\", error);\n process.exit(1);\n});\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getContext, useProject, peekContext } from \"../context.js\";\nimport { ok, guard } from \"../result.js\";\n\nexport function registerContextTools(server: McpServer) {\n server.registerTool(\n \"get_context\",\n {\n annotations: { readOnlyHint: true },\n title: \"Get active context\",\n description:\n \"Show the project and sim world the MCP server is currently operating on. \" +\n \"If no project is selected yet and you have only one project, it is auto-selected.\",\n inputSchema: {},\n },\n async () =>\n guard(async () => {\n const ctx = peekContext() ?? (await getContext());\n return ok(\n `Active project: ${ctx.projectName} (id: ${ctx.projectId}); sim world: ${ctx.simWorldId} [${ctx.source}]`,\n ctx,\n );\n }),\n );\n\n server.registerTool(\n \"use_project\",\n {\n annotations: { readOnlyHint: true },\n title: \"Select project\",\n description:\n \"Switch the active project by name or id. Only needed if you have more than one project. \" +\n \"Caches the project's sim world for subsequent tools.\",\n inputSchema: {\n project: z.string().describe(\"Project name or id to make active\"),\n },\n },\n async ({ project }) =>\n guard(async () => {\n const ctx = await useProject({ project });\n return ok(`Active project is now ${ctx.projectName} (id: ${ctx.projectId}).`, ctx);\n }),\n );\n}\n","import { createClient, type TimetickClient } from \"@timetick/sdk\";\n\nlet client: TimetickClient | null = null;\n\n/** Lazily-created singleton Timetick SDK client, authenticated from the environment. */\nexport function sdk(): TimetickClient {\n if (client) return client;\n\n const apiKey = process.env.TIMETICK_API_TOKEN;\n if (!apiKey) {\n throw new Error(\n \"TIMETICK_API_TOKEN is not set. Generate an API key in the Timetick portal \" +\n \"(Account → API keys) and expose it to the MCP server as TIMETICK_API_TOKEN.\",\n );\n }\n\n client = createClient({ apiKey, endpoint: process.env.TIMETICK_API_URL });\n return client;\n}\n","import { sdk } from \"./sdk.js\";\n\nexport interface Context {\n projectId: string;\n projectName: string;\n simWorldId: string;\n /** Scope id used by hardware/device queries — distinct from simWorldId. */\n deviceScopeId: string;\n /** How the project was chosen — surfaced to the user for transparency. */\n source: \"single-project\" | \"explicit\";\n}\n\n/** Thrown when the project/sim world can't be resolved without user input. */\nexport class ContextError extends Error {}\n\nlet cached: Context | null = null;\n\n/** Resolves a project's sim world (one sim world per project today). */\nasync function buildContext(\n projectId: string,\n projectName: string,\n source: Context[\"source\"],\n): Promise<Context> {\n const worlds = await sdk().simWorlds.list(projectId);\n const world = worlds?.[0];\n if (!world?.id) {\n throw new ContextError(`Project \"${projectName}\" has no sim world to operate on.`);\n }\n return {\n projectId,\n projectName,\n simWorldId: world.id,\n deviceScopeId: world.deviceScopeId ?? world.id,\n source,\n };\n}\n\nconst PROJECT_LIST_LIMIT = 15;\n\ntype ProjectSummary = { id?: string | null; name?: string | null } | null | undefined;\n\nfunction formatProjects(projects: readonly ProjectSummary[] | null | undefined): string {\n const all = projects ?? [];\n const lines = all\n .slice(0, PROJECT_LIST_LIMIT)\n .map((p) => ` - ${p?.name} (id: ${p?.id})`);\n if (all.length > PROJECT_LIST_LIMIT) {\n lines.push(` …and ${all.length - PROJECT_LIST_LIMIT} more — call list_projects to see them all.`);\n }\n return lines.join(\"\\n\");\n}\n\nasync function findProject(project: string): Promise<{ id: string; name: string }> {\n const projects = await sdk().projects.list();\n const needle = project.toLowerCase();\n const match = projects?.find(\n (p) => p?.id === project || p?.name?.toLowerCase() === needle,\n );\n if (!match?.id) {\n throw new ContextError(\n `No project matching \"${project}\". Available:\\n${formatProjects(projects)}`,\n );\n }\n return { id: match.id, name: match.name ?? \"(unnamed)\" };\n}\n\n/**\n * Returns the active context, auto-selecting the project when there is only one.\n * Throws ContextError (with the project list) when the choice is ambiguous.\n */\nexport async function getContext(): Promise<Context> {\n if (cached) return cached;\n\n const projects = await sdk().projects.list();\n if (!projects || projects.length === 0) {\n throw new ContextError(\"No projects are available for this API key.\");\n }\n if (projects.length > 1) {\n throw new ContextError(\n `You have ${projects.length} projects. Call \\`use_project\\` with a project name or id ` +\n `first:\\n${formatProjects(projects)}`,\n );\n }\n\n const only = projects[0]!;\n cached = await buildContext(only.id!, only.name ?? \"(unnamed)\", \"single-project\");\n return cached;\n}\n\n/** Explicitly switches the active project by name or id, and caches its sim world. */\nexport async function useProject(input: { project: string }): Promise<Context> {\n const match = await findProject(input.project);\n cached = await buildContext(match.id, match.name, \"explicit\");\n return cached;\n}\n\nexport async function lookupProject(project: string): Promise<Context> {\n const match = await findProject(project);\n return buildContext(match.id, match.name, \"explicit\");\n}\n\n/** Returns the cached context without resolving, if any. */\nexport function peekContext(): Context | null {\n return cached;\n}\n","import { TimetickError } from \"@timetick/sdk\";\n\ntype ToolResult = {\n content: { type: \"text\"; text: string }[];\n isError?: boolean;\n};\n\n/** Wraps a value into an MCP text result, pretty-printing objects as JSON. */\nexport function ok(text: string, data?: unknown): ToolResult {\n const body = data === undefined ? text : `${text}\\n\\n${JSON.stringify(data, null, 2)}`;\n return { content: [{ type: \"text\", text: body }] };\n}\n\nexport function fail(message: string): ToolResult {\n return { content: [{ type: \"text\", text: message }], isError: true };\n}\n\n/** Runs a tool handler, normalizing SDK/unknown errors into an isError result. */\nexport async function guard(fn: () => Promise<ToolResult>): Promise<ToolResult> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof TimetickError) return fail(`Timetick API error: ${err.message}`);\n return fail(err instanceof Error ? err.message : String(err));\n }\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimWorld, resolveSimulatorTarget } from \"../resolve.js\";\nimport { SIMULATOR_ARG_DESCRIPTION } from \"../links.js\";\nimport { paginationArgs, pagination, pageSummary } from \"../pagination.js\";\nimport { ok, guard } from \"../result.js\";\n\nconst protocolEnum = z.enum([\"OCPP_1_6\", \"OCPP_2_0_1\"]);\n\nfunction summarizeArgs(params: unknown): Record<string, string> | undefined {\n if (!params || typeof params !== \"object\") return undefined;\n const summary: Record<string, string> = {};\n for (const [name, spec] of Object.entries(params as Record<string, unknown>)) {\n if (!spec || typeof spec !== \"object\") continue;\n const { type, allowNull, defaultValue, visible } = spec as Record<string, unknown>;\n if (visible === false) continue;\n const parts = [String(type ?? \"unknown\"), allowNull === false ? \"required\" : \"optional\"];\n if (defaultValue !== undefined) parts.push(`default ${JSON.stringify(defaultValue)}`);\n summary[name] = parts.join(\", \");\n }\n return Object.keys(summary).length > 0 ? summary : undefined;\n}\n\nexport function registerDataTools(server: McpServer) {\n server.registerTool(\n \"list_projects\",\n {\n annotations: { readOnlyHint: true },\n title: \"List projects\",\n description: \"List all projects available to your API key.\",\n inputSchema: {},\n },\n async () =>\n guard(async () => {\n const projects = await sdk().projects.list();\n return ok(`Found ${projects?.length ?? 0} project(s).`, projects);\n }),\n );\n\n server.registerTool(\n \"list_simulators\",\n {\n annotations: { readOnlyHint: true },\n title: \"List simulators\",\n description:\n \"List simulators (virtual chargers) in the active project's sim world. \" +\n \"Optionally filter by name search or protocol.\",\n inputSchema: {\n search: z.string().optional().describe(\"Filter by simulator base name\"),\n protocol: protocolEnum.optional(),\n ...paginationArgs,\n project: z.string().optional().describe(\"Project name or id (defaults to active project)\"),\n },\n },\n async ({ search, protocol, page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().simulators.list({\n simWorldId: ctx.simWorldId,\n searchValue: search,\n filters: protocol ? { protocol } : undefined,\n pagination: pagination(page, pageSize),\n });\n return ok(\n pageSummary(\"simulator(s)\", result?.records?.length ?? 0, result?.pageInfo),\n result,\n );\n }),\n );\n\n server.registerTool(\n \"get_simulator\",\n {\n annotations: { readOnlyHint: true },\n title: \"Get simulator\",\n description:\n \"Get a simulator's details and live status (network status, launched/power status, connectors) \" +\n \"by id or base name.\",\n inputSchema: {\n simulator: z.string().describe(SIMULATOR_ARG_DESCRIPTION),\n project: z.string().optional().describe(\"Project name or id (defaults to active project)\"),\n },\n },\n async ({ simulator, project }) =>\n guard(async () => {\n const { sim: resolved } = await resolveSimulatorTarget(simulator, project);\n const details = await sdk().simulators.get(resolved.id);\n return ok(`Simulator ${resolved.baseName} (id: ${resolved.id}).`, details);\n }),\n );\n\n server.registerTool(\n \"list_hardware\",\n {\n annotations: { readOnlyHint: true },\n title: \"List hardware\",\n description: \"List physical OCPP hardware devices in the active project's sim world.\",\n inputSchema: {\n search: z.string().optional().describe(\"Filter by serial number / model\"),\n ...paginationArgs,\n project: z.string().optional().describe(\"Project name or id (defaults to active project)\"),\n },\n },\n async ({ search, page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().hardware.list({\n deviceScopeId: ctx.deviceScopeId,\n searchValue: search,\n pagination: pagination(page, pageSize),\n });\n return ok(\n pageSummary(\"hardware device(s)\", result?.records?.length ?? 0, result?.pageInfo),\n result,\n );\n }),\n );\n\n server.registerTool(\n \"list_actions\",\n {\n annotations: { readOnlyHint: true },\n title: \"List actions (test steps)\",\n description:\n \"List available actions (steps) you can use to build a test — e.g. 'Power On Charger', \" +\n \"'Plug In Connector', 'Charge a Vehicle'. Filter by protocol. Each action lists its \" +\n \"arguments: the name, whether it is required (allowNull false), and any default.\",\n inputSchema: {\n protocol: protocolEnum.optional(),\n search: z.string().optional().describe(\"Filter by action name\"),\n ...paginationArgs,\n project: z.string().optional().describe(\"Project name or id (defaults to active project)\"),\n },\n },\n async ({ protocol, search, page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().actions.list({\n simWorldId: ctx.simWorldId,\n searchValue: search,\n filters: { isSystem: true, deviceType: \"CHARGER\", ...(protocol ? { protocol } : {}) },\n pagination: pagination(page, pageSize, 100),\n });\n const actions = (result?.records ?? []).map((a) => ({\n name: a?.name,\n description: a?.description ?? undefined,\n args: summarizeArgs(a?.params),\n }));\n return ok(pageSummary(\"action(s)\", actions.length, result?.pageInfo), actions);\n }),\n );\n}\n","export interface SimulatorRef {\n simulator: string;\n project?: string;\n}\n\nconst SIMULATOR_PATH = /\\/projects\\/([^/?#&|>\\s]+)\\/simulators\\/([^/?#&|>\\s]+)/;\n\nfunction looksLikeLink(text: string): boolean {\n return /^<?https?:\\/\\//i.test(text) || /(^|\\.)timetick\\.io\\//i.test(text);\n}\n\nexport function parseSimulatorRef(input: string): SimulatorRef {\n const text = input.trim();\n if (!looksLikeLink(text)) return { simulator: text };\n\n // Slack escapes & as &amp; in links.\n const unescaped = text.replace(/&amp;/g, \"&\");\n const match = unescaped.match(SIMULATOR_PATH);\n if (!match) {\n throw new Error(\n `\"${input}\" looks like a link but not to a simulator. Expected ` +\n \"https://app.timetick.io/projects/<project id>/simulators/<simulator id>.\",\n );\n }\n return { project: decodeURIComponent(match[1]!), simulator: decodeURIComponent(match[2]!) };\n}\n\nexport const SIMULATOR_ARG_DESCRIPTION =\n \"Simulator id, base name, or portal link (https://app.timetick.io/projects/<id>/simulators/<id>)\";\n","import { sdk } from \"./sdk.js\";\nimport { getContext, lookupProject, type Context } from \"./context.js\";\nimport { parseSimulatorRef } from \"./links.js\";\n\n/** Resolves the sim world to operate in: explicit project arg wins, else session context. */\nexport async function resolveSimWorld(project?: string): Promise<Context> {\n if (project) return lookupProject(project);\n return getContext();\n}\n\nexport interface ResolvedSimulator {\n id: string;\n baseName: string;\n protocol: string;\n}\n\n/** Resolves a simulator by id or by (case-insensitive) base name within a sim world. */\nexport async function resolveSimulator(\n simWorldId: string,\n simulator: string,\n): Promise<ResolvedSimulator> {\n const needle = simulator.toLowerCase();\n const page = await sdk().simulators.list({ simWorldId, searchValue: simulator });\n const records = page?.records ?? [];\n\n const exact =\n records.find((r) => r?.id === simulator) ??\n records.find((r) => r?.baseName?.toLowerCase() === needle) ??\n (records.length === 1 ? records[0] : undefined);\n\n if (exact?.id) {\n return { id: exact.id, baseName: exact.baseName ?? \"\", protocol: String(exact.protocol) };\n }\n\n // Fall back to treating the input as a raw id.\n const byId = await sdk().simulators.get(simulator).catch(() => null);\n if (byId?.id) {\n return { id: byId.id, baseName: byId.baseName ?? \"\", protocol: String(byId.protocol) };\n }\n\n const candidates = records.map((r) => ` - ${r?.baseName} (id: ${r?.id})`).join(\"\\n\");\n throw new Error(\n `No simulator matching \"${simulator}\".` +\n (candidates ? `\\nCandidates:\\n${candidates}` : \" No simulators found in this sim world.\"),\n );\n}\n\nexport async function resolveSimulatorTarget(\n simulator: string,\n project?: string,\n): Promise<{ ctx: Context; sim: ResolvedSimulator }> {\n const ref = parseSimulatorRef(simulator);\n const ctx = await resolveSimWorld(ref.project ?? project);\n const sim = ref.project !== undefined\n ? await resolveSimulatorById(ref.simulator)\n : await resolveSimulator(ctx.simWorldId, ref.simulator);\n return { ctx, sim };\n}\n\nasync function resolveSimulatorById(id: string): Promise<ResolvedSimulator> {\n const byId = await sdk().simulators.get(id).catch((err: unknown) => {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`No simulator with id ${id} from the portal link: ${reason}`);\n });\n if (!byId?.id) throw new Error(`No simulator with id ${id} from the portal link.`);\n return { id: byId.id, baseName: byId.baseName ?? \"\", protocol: String(byId.protocol) };\n}\n\n/** Finds a system action by exact name for a given protocol; returns its id. */\nexport async function resolveActionId(\n simWorldId: string,\n protocol: string,\n actionName: string,\n): Promise<string> {\n const page = await sdk().actions.list({\n simWorldId,\n searchValue: actionName,\n filters: { isSystem: true, protocol: protocol as any, deviceType: \"CHARGER\" },\n pagination: { page: 1, pageSize: 50 },\n });\n const records = page?.records ?? [];\n const needle = actionName.toLowerCase();\n const match =\n records.find((a) => a?.name?.toLowerCase() === needle) ??\n records.find((a) => a?.name?.toLowerCase().includes(needle));\n\n if (!match?.id) {\n const available = records.map((a) => ` - ${a?.name}`).join(\"\\n\");\n throw new Error(\n `Action \"${actionName}\" not found for ${protocol}.` +\n (available ? `\\nAvailable system actions matching your search:\\n${available}` : \"\"),\n );\n }\n return match.id;\n}\n","import { z } from \"zod\";\n\nexport const DEFAULT_PAGE_SIZE = 20;\n\nexport const paginationArgs = {\n page: z.number().int().min(1).optional().describe(\"Page number, starting at 1 (default 1)\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(200)\n .optional()\n .describe(`Results per page (default ${DEFAULT_PAGE_SIZE})`),\n};\n\nexport function pagination(page?: number, pageSize?: number, defaultPageSize = DEFAULT_PAGE_SIZE) {\n return { page: page ?? 1, pageSize: pageSize ?? defaultPageSize };\n}\n\ninterface PageInfoLike {\n page?: number | null;\n pageSize?: number | null;\n hasNextPage?: boolean | null;\n totalCount?: number | null;\n}\n\nexport function pageSummary(noun: string, count: number, pageInfo?: PageInfoLike | null): string {\n const total = pageInfo?.totalCount;\n const head =\n total != null && total !== count\n ? `Showing ${count} of ${total} ${noun} (page ${pageInfo?.page ?? 1}).`\n : `Found ${count} ${noun}.`;\n return pageInfo?.hasNextPage ? `${head} More results available — request the next page.` : head;\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimulatorTarget, resolveActionId } from \"../resolve.js\";\nimport { SIMULATOR_ARG_DESCRIPTION } from \"../links.js\";\nimport { ok, guard } from \"../result.js\";\n\n/** Runs a single named action against a simulator via the SDK's runAction. */\nasync function runOnSimulator(\n project: string | undefined,\n simulator: string,\n actionName: string,\n args: { key: string; value: string }[] = [],\n) {\n const { ctx, sim } = await resolveSimulatorTarget(simulator, project);\n const actionId = await resolveActionId(ctx.simWorldId, sim.protocol, actionName);\n\n const status = await sdk().testRuns.runAction({\n chargerId: sim.id,\n action: { requirementKey: \"1\", stream: 1, order: 1, actionId, args },\n });\n return { sim, actionName, status };\n}\n\nexport function registerSimulatorTools(server: McpServer) {\n const projectArg = z\n .string()\n .optional()\n .describe(\"Project name or id (defaults to active project)\");\n\n server.registerTool(\n \"power_on_simulator\",\n {\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },\n title: \"Power on simulator\",\n description: \"Power on a simulator (boots it and connects it to the OCPP server).\",\n inputSchema: {\n simulator: z.string().describe(SIMULATOR_ARG_DESCRIPTION),\n project: projectArg,\n },\n },\n async ({ simulator, project }) =>\n guard(async () => {\n const { sim, status } = await runOnSimulator(project, simulator, \"Power On Charger\");\n return ok(`Powered on ${sim.baseName} (id: ${sim.id}).`, { status });\n }),\n );\n\n server.registerTool(\n \"power_off_simulator\",\n {\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n title: \"Power off simulator\",\n description: \"Power off a simulator.\",\n inputSchema: {\n simulator: z.string().describe(SIMULATOR_ARG_DESCRIPTION),\n project: projectArg,\n },\n },\n async ({ simulator, project }) =>\n guard(async () => {\n const { sim, status } = await runOnSimulator(project, simulator, \"Power Off Charger\");\n return ok(`Powered off ${sim.baseName} (id: ${sim.id}).`, { status });\n }),\n );\n\n server.registerTool(\n \"run_simulator_action\",\n {\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n title: \"Run simulator action\",\n description:\n \"Run a single named action against a simulator — e.g. 'Reset Charger', 'Plug In Connector', \" +\n \"'Plug Out Connector'. Use list_actions to discover names and required args. \" +\n \"Args are passed as key/value pairs (e.g. connectorId=1).\",\n inputSchema: {\n simulator: z.string().describe(SIMULATOR_ARG_DESCRIPTION),\n actionName: z.string().describe(\"Exact action name, e.g. 'Reset Charger'\"),\n args: z\n .array(z.object({ key: z.string(), value: z.string() }))\n .optional()\n .describe(\"Action arguments as key/value pairs\"),\n project: projectArg,\n },\n },\n async ({ simulator, actionName, args, project }) =>\n guard(async () => {\n const { sim, status } = await runOnSimulator(project, simulator, actionName, args ?? []);\n return ok(`Ran \"${actionName}\" on ${sim.baseName} (id: ${sim.id}).`, { status });\n }),\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimWorld, resolveActionId } from \"../resolve.js\";\nimport { paginationArgs, pagination, pageSummary } from \"../pagination.js\";\nimport { ok, guard } from \"../result.js\";\n\nconst protocolEnum = z.enum([\"OCPP_1_6\", \"OCPP_2_0_1\"]);\n\nexport function registerTestTools(server: McpServer) {\n const projectArg = z\n .string()\n .optional()\n .describe(\"Project name or id (defaults to active project)\");\n\n server.registerTool(\n \"list_tests\",\n {\n annotations: { readOnlyHint: true },\n title: \"List tests\",\n description: \"List tests (scenarios) in the active project's sim world.\",\n inputSchema: {\n protocol: protocolEnum.optional(),\n search: z.string().optional().describe(\"Filter by test name\"),\n ...paginationArgs,\n project: projectArg,\n },\n },\n async ({ protocol, search, page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().tests.list({\n simWorldId: ctx.simWorldId,\n searchValue: search,\n filters: protocol ? { protocol } : undefined,\n pagination: pagination(page, pageSize),\n });\n return ok(pageSummary(\"test(s)\", result?.records?.length ?? 0, result?.pageInfo), result);\n }),\n );\n\n server.registerTool(\n \"get_test\",\n {\n annotations: { readOnlyHint: true },\n title: \"Get test\",\n description: \"Get a test by id, including its device requirements.\",\n inputSchema: {\n testId: z.string().describe(\"Test (scenario) id\"),\n },\n },\n async ({ testId }) =>\n guard(async () => {\n const test = await sdk().tests.get(testId);\n if (!test) return ok(`No test found with id ${testId}.`);\n return ok(`Test ${test.name} (id: ${testId}).`, test);\n }),\n );\n\n server.registerTool(\n \"create_test\",\n {\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },\n title: \"Create test\",\n description:\n \"Create a test from an ordered list of steps. Each step references an action by name \" +\n \"(use list_actions to discover them) plus optional key/value args. The steps run in order \" +\n \"against a single simulator. Example steps: 'Power On Charger', 'Plug In Connector', \" +\n \"'Use RFID Card', 'Charge a Vehicle', 'Plug Out Connector', 'Power Off Charger'.\",\n inputSchema: {\n name: z.string().describe(\"Test name\"),\n protocol: protocolEnum,\n steps: z\n .array(\n z.object({\n actionName: z.string().describe(\"Exact action name, e.g. 'Power On Charger'\"),\n args: z\n .array(z.object({ key: z.string(), value: z.string() }))\n .optional()\n .describe(\"Action arguments as key/value pairs\"),\n }),\n )\n .min(1)\n .describe(\"Ordered list of steps\"),\n project: projectArg,\n },\n },\n async ({ name, protocol, steps, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n\n const actions = [];\n for (let i = 0; i < steps.length; i++) {\n const actionId = await resolveActionId(ctx.simWorldId, protocol, steps[i].actionName);\n actions.push({\n order: i + 1,\n stream: 1,\n actionId,\n requirementKey: \"charger\",\n args: steps[i].args ?? [],\n });\n }\n\n const test = await sdk().tests.create({\n simWorldId: ctx.simWorldId,\n name,\n deviceType: \"CHARGER\",\n requirements: [\n { key: \"charger\", type: \"CHARGER\", params: [{ key: \"protocol\", value: protocol }] },\n ],\n actions,\n });\n return ok(`Created test \"${name}\" (id: ${test?.id}).`, test);\n }),\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimWorld, resolveSimulatorTarget } from \"../resolve.js\";\nimport { SIMULATOR_ARG_DESCRIPTION } from \"../links.js\";\nimport { paginationArgs, pagination, pageSummary } from \"../pagination.js\";\nimport { ok, fail, guard } from \"../result.js\";\n\nexport function registerTestRunTools(server: McpServer) {\n const projectArg = z\n .string()\n .optional()\n .describe(\"Project name or id (defaults to active project)\");\n\n server.registerTool(\n \"run_test\",\n {\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },\n title: \"Run test\",\n description:\n \"Run a test (scenario) on a simulator, starting immediately. \" +\n \"Returns the test run id and status — poll with get_test_run to follow it. \" +\n \"A simulator runs one test at a time; if it is busy this says so and starts nothing.\",\n inputSchema: {\n testId: z.string().describe(\"Test (scenario) id to run\"),\n simulator: z.string().describe(`${SIMULATOR_ARG_DESCRIPTION} to run the test on`),\n project: projectArg,\n },\n },\n async ({ testId, simulator, project }) =>\n guard(async () => {\n const { ctx, sim } = await resolveSimulatorTarget(simulator, project);\n const test = await sdk().tests.get(testId);\n const requirementId = test?.requirements?.[0]?.id;\n if (!requirementId) return fail(`Test ${testId} has no device requirement to assign.`);\n\n const run = await sdk()\n .testRuns.create({\n scenarioId: testId,\n simWorldId: ctx.simWorldId,\n scheduleMode: \"NOW\",\n speedup: 1,\n forceDelete: false,\n requirementDevices: [\n { requirementId, deviceId: sim.id, deviceMode: \"SIMULATOR\" },\n ],\n })\n .catch(async (err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n if (!/conflict/i.test(message)) throw err;\n\n const details = await sdk().simulators.get(sim.id).catch(() => null);\n const blocking = details?.lastRun;\n throw new Error(\n `${sim.baseName} (id: ${sim.id}) is busy` +\n (blocking?.id ? ` with test run ${blocking.id} (${blocking.status})` : \"\") +\n `, so nothing was started. Stop that run with stop_test_run — the simulator is ` +\n `free once it stops — then run this test again.`,\n );\n });\n return ok(\n `Started test \"${test?.name}\" on ${sim.baseName} — run id ${run?.id}, status ${run?.status}.`,\n run,\n );\n }),\n );\n\n server.registerTool(\n \"get_test_run\",\n {\n annotations: { readOnlyHint: true },\n title: \"Get test run\",\n description: \"Get a test run's status by id.\",\n inputSchema: {\n runId: z.string().describe(\"Test run id\"),\n },\n },\n async ({ runId }) =>\n guard(async () => {\n const run = await sdk().testRuns.get(runId);\n if (!run) return ok(`No test run found with id ${runId}.`);\n const reason = run.failReason ? ` — ${run.failReason}` : \"\";\n return ok(`Test run ${runId} (\"${run.scenario?.name}\"): ${run.status}${reason}.`, run);\n }),\n );\n\n server.registerTool(\n \"list_test_runs\",\n {\n annotations: { readOnlyHint: true },\n title: \"List test runs\",\n description: \"List recent test runs in the active project's sim world.\",\n inputSchema: {\n ...paginationArgs,\n project: projectArg,\n },\n },\n async ({ page, pageSize, project }) =>\n guard(async () => {\n const ctx = await resolveSimWorld(project);\n const result = await sdk().testRuns.list({\n simWorldId: ctx.simWorldId,\n pagination: pagination(page, pageSize),\n });\n return ok(pageSummary(\"test run(s)\", result?.records?.length ?? 0, result?.pageInfo), result);\n }),\n );\n\n server.registerTool(\n \"stop_test_run\",\n {\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n title: \"Stop test run\",\n description: \"Shut down (stop) a running or scheduled test run by id.\",\n inputSchema: {\n runId: z.string().describe(\"Test run id\"),\n },\n },\n async ({ runId }) =>\n guard(async () => {\n const existing = await sdk().testRuns.get(runId);\n if (!existing) return fail(`No test run found with id ${runId}.`);\n\n await sdk().testRuns.shutDown(runId);\n const after = await sdk().testRuns.get(runId);\n return ok(\n `Shut down test run ${runId} (was ${existing.status}, now ${after?.status ?? \"unknown\"}).`,\n after,\n );\n }),\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sdk } from \"../sdk.js\";\nimport { resolveSimulatorTarget } from \"../resolve.js\";\nimport { SIMULATOR_ARG_DESCRIPTION } from \"../links.js\";\nimport {\n DEFAULT_LOGS_LAST,\n DEFAULT_LOGS_LIMIT,\n MAX_LOGS_LIMIT,\n formatLine,\n matchesAction,\n resolveWindow,\n toLogLines,\n} from \"../logs-format.js\";\nimport { ok, fail, guard } from \"../result.js\";\nimport { pageSummary } from \"../pagination.js\";\n\nconst READ_ONLY = { readOnlyHint: true, openWorldHint: true } as const;\n\nconst SECRET_KEY = /authorizationkey|password|secret|privatekey|passphrase/i;\n\ntype ConfigItem = { key?: string | null; variableName?: string | null; value?: string | null };\n\nexport function maskSecretItems<T extends ConfigItem>(items: readonly T[] | null | undefined): T[] {\n return (items ?? []).map((item) =>\n SECRET_KEY.test(item.key ?? item.variableName ?? \"\") && item.value ? { ...item, value: \"***\" } : item,\n );\n}\n\nfunction summarizeSimulator(sim: any): string {\n const model = sim.chargerModel;\n const config = sim.chargerConfig;\n const connectors = (model?.connectorModels ?? sim.chargerConnectors ?? [])\n .map((c: any) => `#${c.number ?? c.connectorId} ${c.type} (cable ${c.cableFixed ? \"fixed\" : \"flexible\"})`)\n .join(\", \");\n const lines = [\n `Simulator ${sim.baseName} (id: ${sim.id}), ${sim.protocol}; network ${sim.networkStatus}, ` +\n `power ${sim.launchedStatus}, OCPP status ${sim.chargerStatus}${sim.emergency ? \", EMERGENCY\" : \"\"}.`,\n `Model: ${model?.name ?? \"?\"} by ${model?.deviceManufacturer?.name ?? \"?\"}` +\n (connectors ? `; connectors: ${connectors}.` : \".\"),\n `Behavior: ${config?.behavior ?? \"?\"}; remote templates ${config?.useRemoteTemplates ? \"on\" : \"off\"}; ` +\n `supported commands: ${(config?.supportedCommands ?? []).join(\", \") || \"—\"}.`,\n `OCPP configuration keys: ${sim.chargerOcppConfigItems?.length ?? 0}.`,\n ];\n if (sim.lastRun) {\n lines.push(\n `Last run ${sim.lastRun.id}: ${sim.lastRun.status}` +\n (sim.lastRun.failReason ? ` — ${sim.lastRun.failReason}` : \"\") + \".\",\n );\n }\n return lines.join(\"\\n\");\n}\n\nconst RUN_SIMULATORS_PAGE_SIZE = 20;\n\nfunction simulatorName(rc: any): string {\n return rc.charger?.baseName ?? rc.device?.serialNumber ?? rc.id;\n}\n\nfunction findRunSimulator(records: any[], simulator: string): any {\n const needle = simulator.trim().toLowerCase();\n return (\n records.find((rc) => rc.id === simulator.trim()) ??\n records.find((rc) => rc.charger?.id === simulator.trim() || rc.device?.id === simulator.trim()) ??\n records.find((rc) =>\n rc.charger?.baseName?.toLowerCase() === needle || rc.device?.serialNumber?.toLowerCase() === needle,\n )\n );\n}\n\nfunction summarizeRun(run: any, simulators: any, page: number, steps: any): string {\n const records = (simulators?.records ?? []).filter(Boolean);\n const lines = [\n `Test run ${run.id} (\"${run.scenario?.name ?? \"?\"}\"): ${run.status}` +\n (run.failReason ? ` — ${run.failReason}` : \"\") +\n `. Started ${run.realStartTimestamp ?? run.startTimestamp ?? \"—\"}, stopped ${run.realStopTimestamp ?? \"—\"}.`,\n pageSummary(\"simulator(s)\", records.length, simulators?.pageInfo ?? { page }),\n ];\n for (const rc of records) {\n lines.push(`- ${simulatorName(rc)} (simulator ${rc.charger?.id ?? rc.device?.id ?? \"?\"}, run entry ${rc.id}): ${rc.status}`);\n }\n if (steps) {\n lines.push(\"\", `Steps of ${simulatorName(steps)}:`);\n for (const step of steps.chargerActions ?? []) {\n lines.push(\n ` step ${step.order} ${step.scenarioAction?.action?.name ?? \"?\"}: ${step.status}` +\n (step.failReason ? ` — ${step.failReason}` : \"\"),\n );\n }\n } else if (records.length > 1 || simulators?.pageInfo?.hasNextPage) {\n lines.push(\"\", \"Pass `simulator` with one of the names or ids above to see its steps.\");\n }\n return lines.join(\"\\n\");\n}\n\nexport function registerSupportTools(server: McpServer) {\n const projectArg = z\n .string()\n .optional()\n .describe(\"Project name or id (defaults to active project; a portal link sets its own)\");\n\n server.registerTool(\n \"get_simulator_config\",\n {\n title: \"Get simulator configuration\",\n description:\n \"Read-only. Everything that explains why a simulator behaves the way it does: its OCPP \" +\n \"configuration keys (e.g. FreevendEnabled, HeartbeatInterval), behavior settings (vendor \" +\n \"behavior, remote start/stop templates, supported commands, timeouts), model and connector \" +\n \"types (fixed or flexible cable), live connectors, authorizations, charging profiles, \" +\n \"transactions, reservations, and the last test run with its fail reason. Credential values \" +\n \"are masked.\",\n inputSchema: { simulator: z.string().describe(SIMULATOR_ARG_DESCRIPTION), project: projectArg },\n annotations: READ_ONLY,\n },\n async ({ simulator, project }) =>\n guard(async () => {\n const { sim } = await resolveSimulatorTarget(simulator, project);\n const details = await sdk().simulators.getDetails(sim.id);\n if (!details) return ok(`No simulator found with id ${sim.id}.`);\n const items = await sdk().simulators.getOcppConfigItems(sim.id);\n const safe = { ...details, chargerOcppConfigItems: maskSecretItems(items as ConfigItem[] | null) };\n return ok(summarizeSimulator(safe), safe);\n }),\n );\n\n server.registerTool(\n \"get_simulator_logs\",\n {\n title: \"Get simulator logs\",\n description:\n \"Read-only. The simulator's OCPP message log for a time window, one line per message: \" +\n \"`time direction action payload` (payload cut at ~500 chars). Newest messages are kept \" +\n \"when the limit cuts the window; lines are printed oldest first. Use `action` to keep one \" +\n \"OCPP action, e.g. StatusNotification (its results and errors are kept too).\",\n inputSchema: {\n simulator: z.string().describe(SIMULATOR_ARG_DESCRIPTION),\n from: z.string().optional().describe(\"Window start, ISO date-time (wins over `last`)\"),\n to: z.string().optional().describe(\"Window end, ISO date-time (default now)\"),\n last: z.string().optional().describe(`Window length back from \\`to\\`, e.g. 30m, 6h, 2d (default ${DEFAULT_LOGS_LAST})`),\n limit: z.number().int().min(1).max(MAX_LOGS_LIMIT).optional()\n .describe(`Max messages to show (default ${DEFAULT_LOGS_LIMIT}, max ${MAX_LOGS_LIMIT})`),\n action: z.string().optional().describe(\"Keep only this OCPP action, e.g. StatusNotification\"),\n project: projectArg,\n },\n annotations: READ_ONLY,\n },\n async ({ simulator, from, to, last, limit, action, project }) =>\n guard(async () => {\n const window = resolveWindow({ from, to, last });\n const { sim } = await resolveSimulatorTarget(simulator, project);\n // With a filter, scan a full page and cut to the limit afterwards.\n const shown = Math.min(limit ?? DEFAULT_LOGS_LIMIT, MAX_LOGS_LIMIT);\n const result = await sdk().logs.list({\n simulatorId: sim.id,\n from: window.from,\n to: window.to,\n limit: action ? MAX_LOGS_LIMIT : shown,\n });\n\n const all = toLogLines(result.records);\n const matched = action ? all.filter((line) => matchesAction(line, action)) : all;\n const lines = matched.slice(0, shown).reverse();\n const truncated = matched.length > shown || Boolean(result.pageInfo?.hasNextPage);\n\n const head =\n `${sim.baseName} (id: ${sim.id}) ${window.from.toISOString()} … ${window.to.toISOString()}: ` +\n `${lines.length} message(s) shown` +\n (action ? ` matching \"${action}\" of ${all.length} scanned` : \"\") +\n (result.pageInfo?.totalCount != null ? `; ${result.pageInfo.totalCount} in window` : \"\") +\n `; truncated: ${truncated ? \"yes — narrow the window or raise the limit\" : \"no\"}.`;\n return ok([head, \"\", ...lines.map(formatLine)].join(\"\\n\"));\n }),\n );\n\n server.registerTool(\n \"get_test_run_details\",\n {\n title: \"Get test run details\",\n description:\n \"Read-only. A test run's status and fail reason, plus one page of its simulators or devices \" +\n `with their status (${RUN_SIMULATORS_PAGE_SIZE} per page). Steps — status, fail reason, ` +\n \"expected / actual result — are shown for one simulator at a time: pass `simulator` with a \" +\n \"name or id from the page. A run with a single simulator shows its steps right away.\",\n inputSchema: {\n runId: z.string().describe(\"Test run id\"),\n page: z.number().int().min(1).optional().describe(\"Page of the run's simulators, starting at 1 (default 1)\"),\n simulator: z.string().optional()\n .describe(\"Show the steps of this simulator from the page: its name, simulator id or run entry id\"),\n },\n annotations: READ_ONLY,\n },\n async ({ runId, page, simulator }) =>\n guard(async () => {\n const run = await sdk().testRuns.getDetails(runId);\n if (!run) return ok(`No test run found with id ${runId}.`);\n const current = page ?? 1;\n const simulators = await sdk().testRuns.listSimulators(runId, {\n pagination: { page: current, pageSize: RUN_SIMULATORS_PAGE_SIZE },\n });\n const records = (simulators?.records ?? []).filter(Boolean);\n\n let target: any;\n if (simulator) {\n target = findRunSimulator(records, simulator);\n if (!target) {\n return fail(\n `No simulator \"${simulator}\" on page ${current} of test run ${runId}. ` +\n \"Pass a name or id listed on this page, or another page.\",\n );\n }\n } else if (simulators?.pageInfo?.totalCount === 1 || (current === 1 && records.length === 1 && !simulators?.pageInfo?.hasNextPage)) {\n target = records[0];\n }\n\n const ref = target?.charger?.id\n ? { chargerId: target.charger.id as string }\n : target?.device?.id ? { deviceId: target.device.id as string } : null;\n const steps = ref ? await sdk().testRuns.getSimulatorSteps(runId, ref) : null;\n return ok(summarizeRun(run, simulators, current, steps), { run, simulators, steps });\n }),\n );\n}\n","\nexport const DEFAULT_LOGS_LAST = \"1h\";\nexport const DEFAULT_LOGS_LIMIT = 200;\nexport const MAX_LOGS_LIMIT = 1000;\nexport const PAYLOAD_MAX_CHARS = 500;\n\nconst UNIT_MS: Record<string, number> = { s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 };\n\nexport function parseDuration(value: string): number {\n const match = value.trim().match(/^(\\d+)\\s*([smhd])$/i);\n if (!match) throw new Error(`Invalid duration \"${value}\". Use a number and a unit: 30m, 6h, 2d.`);\n return Number(match[1]) * UNIT_MS[match[2]!.toLowerCase()]!;\n}\n\nfunction parseInstant(value: string, name: string): Date {\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) throw new Error(`Invalid ${name} \"${value}\". Use an ISO date-time.`);\n return date;\n}\n\nexport function resolveWindow(\n input: { from?: string; to?: string; last?: string },\n now: Date = new Date(),\n): { from: Date; to: Date } {\n const to = input.to ? parseInstant(input.to, \"to\") : now;\n const from = input.from\n ? parseInstant(input.from, \"from\")\n : new Date(to.getTime() - parseDuration(input.last ?? DEFAULT_LOGS_LAST));\n if (from.getTime() > to.getTime()) throw new Error(`\"from\" (${from.toISOString()}) is after \"to\" (${to.toISOString()}).`);\n return { from, to };\n}\n\nexport interface LogRecordLike {\n id: string;\n timestamp: string | null;\n sender: string | null;\n receiver: string | null;\n protocol: string | null;\n content: string | null;\n rawContent: string | null;\n error: string | null;\n}\n\nexport interface LogLine {\n time: string;\n direction: string;\n action: string;\n payload: string;\n}\n\nfunction tryJson(text: string | null): unknown {\n if (text == null) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\nfunction stringify(value: unknown): string {\n return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\nexport function truncate(text: string, max = PAYLOAD_MAX_CHARS): string {\n return text.length > max ? `${text.slice(0, max)}…(+${text.length - max} chars)` : text;\n}\n\nfunction direction(record: LogRecordLike, envelope: any): string {\n const dir = envelope?.payload?.direction;\n if (dir === \"client_to_server\") return \"CP→CSMS\";\n if (dir === \"server_to_client\") return \"CSMS→CP\";\n if (record.sender || record.receiver) return `${record.sender ?? \"?\"}→${record.receiver ?? \"?\"}`;\n return \"-\";\n}\n\nexport function toLogLines(records: LogRecordLike[]): LogLine[] {\n const parsed = records.map((r) => ({ record: r, message: tryJson(r.content), envelope: tryJson(r.rawContent) }));\n\n const callActions = new Map<string, string>();\n for (const { message } of parsed) {\n if (Array.isArray(message) && message[0] === 2) callActions.set(String(message[1]), String(message[2]));\n }\n\n return parsed.map(({ record, message, envelope }) => {\n let action: string;\n let payload: string;\n if (record.protocol === \"OCPP\" && Array.isArray(message) && [2, 3, 4].includes(message[0])) {\n const call = callActions.get(String(message[1]));\n if (message[0] === 2) {\n action = String(message[2]);\n payload = stringify(message[3] ?? {});\n } else if (message[0] === 3) {\n action = `${call ?? \"?\"}.result`;\n payload = stringify(message[2] ?? {});\n } else {\n action = `${call ?? \"?\"}.error`;\n payload = [message[2], message[3], message[4] ? stringify(message[4]) : \"\"].filter(Boolean).join(\" \");\n }\n } else {\n action = String((envelope as any)?.type ?? record.protocol ?? \"log\");\n payload = record.content ?? \"\";\n }\n if (record.error) payload = `${payload} [error: ${record.error}]`;\n return {\n time: record.timestamp ?? \"?\",\n direction: direction(record, envelope),\n action,\n payload: truncate(payload.replace(/\\s+/g, \" \").trim()),\n };\n });\n}\n\nexport function matchesAction(line: LogLine, action: string): boolean {\n const base = line.action.replace(/\\.(result|error)$/, \"\").toLowerCase();\n return base === action.trim().toLowerCase();\n}\n\nexport function formatLine(line: LogLine): string {\n return `${line.time} ${line.direction} ${line.action} ${line.payload}`;\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { registerContextTools } from \"./context.js\";\nimport { registerDataTools } from \"./data.js\";\nimport { registerSimulatorTools } from \"./simulators.js\";\nimport { registerTestTools } from \"./tests.js\";\nimport { registerTestRunTools } from \"./test-runs.js\";\nimport { registerSupportTools } from \"./support.js\";\n\nexport const READONLY_ENV = \"TIMETICK_MCP_READONLY\";\n\nexport function isReadOnlyMode(env: NodeJS.ProcessEnv = process.env): boolean {\n const value = env[READONLY_ENV]?.trim().toLowerCase();\n return value === \"1\" || value === \"true\";\n}\n\nfunction readOnlyView(server: McpServer): McpServer {\n return new Proxy(server, {\n get(target, prop, receiver) {\n if (prop !== \"registerTool\") return Reflect.get(target, prop, receiver);\n return (name: string, config: { annotations?: { readOnlyHint?: boolean } }, cb: unknown) =>\n config.annotations?.readOnlyHint === true\n ? (target.registerTool as (...args: unknown[]) => unknown)(name, config, cb)\n : undefined;\n },\n });\n}\n\nexport function registerTools(server: McpServer, options: { readOnly: boolean }) {\n const target = options.readOnly ? readOnlyView(server) : server;\n registerContextTools(target);\n registerDataTools(target);\n registerSimulatorTools(target);\n registerTestTools(target);\n registerTestRunTools(target);\n registerSupportTools(target);\n}\n"],"mappings":";;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;;;ACDrC,SAAS,SAAS;;;ACAlB,SAAS,oBAAyC;AAElD,IAAI,SAAgC;AAG7B,SAAS,MAAsB;AACpC,MAAI,OAAQ,QAAO;AAEnB,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,WAAS,aAAa,EAAE,QAAQ,UAAU,QAAQ,IAAI,iBAAiB,CAAC;AACxE,SAAO;AACT;;;ACLO,IAAM,eAAN,cAA2B,MAAM;AAAC;AAEzC,IAAI,SAAyB;AAG7B,eAAe,aACb,WACA,aACA,QACkB;AAClB,QAAM,SAAS,MAAM,IAAI,EAAE,UAAU,KAAK,SAAS;AACnD,QAAM,QAAQ,SAAS,CAAC;AACxB,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,IAAI,aAAa,YAAY,WAAW,mCAAmC;AAAA,EACnF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAY,MAAM;AAAA,IAClB,eAAe,MAAM,iBAAiB,MAAM;AAAA,IAC5C;AAAA,EACF;AACF;AAEA,IAAM,qBAAqB;AAI3B,SAAS,eAAe,UAAgE;AACtF,QAAM,MAAM,YAAY,CAAC;AACzB,QAAM,QAAQ,IACX,MAAM,GAAG,kBAAkB,EAC3B,IAAI,CAAC,MAAM,OAAO,GAAG,IAAI,SAAS,GAAG,EAAE,GAAG;AAC7C,MAAI,IAAI,SAAS,oBAAoB;AACnC,UAAM,KAAK,eAAU,IAAI,SAAS,kBAAkB,kDAA6C;AAAA,EACnG;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,eAAe,YAAY,SAAwD;AACjF,QAAM,WAAW,MAAM,IAAI,EAAE,SAAS,KAAK;AAC3C,QAAM,SAAS,QAAQ,YAAY;AACnC,QAAM,QAAQ,UAAU;AAAA,IACtB,CAAC,MAAM,GAAG,OAAO,WAAW,GAAG,MAAM,YAAY,MAAM;AAAA,EACzD;AACA,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,IAAI;AAAA,MACR,wBAAwB,OAAO;AAAA,EAAkB,eAAe,QAAQ,CAAC;AAAA,IAC3E;AAAA,EACF;AACA,SAAO,EAAE,IAAI,MAAM,IAAI,MAAM,MAAM,QAAQ,YAAY;AACzD;AAMA,eAAsB,aAA+B;AACnD,MAAI,OAAQ,QAAO;AAEnB,QAAM,WAAW,MAAM,IAAI,EAAE,SAAS,KAAK;AAC3C,MAAI,CAAC,YAAY,SAAS,WAAW,GAAG;AACtC,UAAM,IAAI,aAAa,6CAA6C;AAAA,EACtE;AACA,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,YAAY,SAAS,MAAM;AAAA,EACd,eAAe,QAAQ,CAAC;AAAA,IACvC;AAAA,EACF;AAEA,QAAM,OAAO,SAAS,CAAC;AACvB,WAAS,MAAM,aAAa,KAAK,IAAK,KAAK,QAAQ,aAAa,gBAAgB;AAChF,SAAO;AACT;AAGA,eAAsB,WAAW,OAA8C;AAC7E,QAAM,QAAQ,MAAM,YAAY,MAAM,OAAO;AAC7C,WAAS,MAAM,aAAa,MAAM,IAAI,MAAM,MAAM,UAAU;AAC5D,SAAO;AACT;AAEA,eAAsB,cAAc,SAAmC;AACrE,QAAM,QAAQ,MAAM,YAAY,OAAO;AACvC,SAAO,aAAa,MAAM,IAAI,MAAM,MAAM,UAAU;AACtD;AAGO,SAAS,cAA8B;AAC5C,SAAO;AACT;;;ACxGA,SAAS,qBAAqB;AAQvB,SAAS,GAAG,MAAc,MAA4B;AAC3D,QAAM,OAAO,SAAS,SAAY,OAAO,GAAG,IAAI;AAAA;AAAA,EAAO,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACpF,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,CAAC,EAAE;AACnD;AAEO,SAAS,KAAK,SAA6B;AAChD,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK;AACrE;AAGA,eAAsB,MAAM,IAAoD;AAC9E,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,SAAS,KAAK;AACZ,QAAI,eAAe,cAAe,QAAO,KAAK,uBAAuB,IAAI,OAAO,EAAE;AAClF,WAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAC9D;AACF;;;AHpBO,SAAS,qBAAqB,QAAmB;AACtD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa,CAAC;AAAA,IAChB;AAAA,IACA,YACE,MAAM,YAAY;AAChB,YAAM,MAAM,YAAY,KAAM,MAAM,WAAW;AAC/C,aAAO;AAAA,QACL,mBAAmB,IAAI,WAAW,SAAS,IAAI,SAAS,iBAAiB,IAAI,UAAU,KAAK,IAAI,MAAM;AAAA,QACtG;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,SAAS,EAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,MAClE;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAQ,MACf,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,WAAW,EAAE,QAAQ,CAAC;AACxC,aAAO,GAAG,yBAAyB,IAAI,WAAW,SAAS,IAAI,SAAS,MAAM,GAAG;AAAA,IACnF,CAAC;AAAA,EACL;AACF;;;AI5CA,SAAS,KAAAA,UAAS;;;ACKlB,IAAM,iBAAiB;AAEvB,SAAS,cAAc,MAAuB;AAC5C,SAAO,kBAAkB,KAAK,IAAI,KAAK,wBAAwB,KAAK,IAAI;AAC1E;AAEO,SAAS,kBAAkB,OAA6B;AAC7D,QAAM,OAAO,MAAM,KAAK;AACxB,MAAI,CAAC,cAAc,IAAI,EAAG,QAAO,EAAE,WAAW,KAAK;AAGnD,QAAM,YAAY,KAAK,QAAQ,UAAU,GAAG;AAC5C,QAAM,QAAQ,UAAU,MAAM,cAAc;AAC5C,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,IAAI,KAAK;AAAA,IAEX;AAAA,EACF;AACA,SAAO,EAAE,SAAS,mBAAmB,MAAM,CAAC,CAAE,GAAG,WAAW,mBAAmB,MAAM,CAAC,CAAE,EAAE;AAC5F;AAEO,IAAM,4BACX;;;ACvBF,eAAsB,gBAAgB,SAAoC;AACxE,MAAI,QAAS,QAAO,cAAc,OAAO;AACzC,SAAO,WAAW;AACpB;AASA,eAAsB,iBACpB,YACA,WAC4B;AAC5B,QAAM,SAAS,UAAU,YAAY;AACrC,QAAM,OAAO,MAAM,IAAI,EAAE,WAAW,KAAK,EAAE,YAAY,aAAa,UAAU,CAAC;AAC/E,QAAM,UAAU,MAAM,WAAW,CAAC;AAElC,QAAM,QACJ,QAAQ,KAAK,CAAC,MAAM,GAAG,OAAO,SAAS,KACvC,QAAQ,KAAK,CAAC,MAAM,GAAG,UAAU,YAAY,MAAM,MAAM,MACxD,QAAQ,WAAW,IAAI,QAAQ,CAAC,IAAI;AAEvC,MAAI,OAAO,IAAI;AACb,WAAO,EAAE,IAAI,MAAM,IAAI,UAAU,MAAM,YAAY,IAAI,UAAU,OAAO,MAAM,QAAQ,EAAE;AAAA,EAC1F;AAGA,QAAM,OAAO,MAAM,IAAI,EAAE,WAAW,IAAI,SAAS,EAAE,MAAM,MAAM,IAAI;AACnE,MAAI,MAAM,IAAI;AACZ,WAAO,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,YAAY,IAAI,UAAU,OAAO,KAAK,QAAQ,EAAE;AAAA,EACvF;AAEA,QAAM,aAAa,QAAQ,IAAI,CAAC,MAAM,OAAO,GAAG,QAAQ,SAAS,GAAG,EAAE,GAAG,EAAE,KAAK,IAAI;AACpF,QAAM,IAAI;AAAA,IACR,0BAA0B,SAAS,QAChC,aAAa;AAAA;AAAA,EAAkB,UAAU,KAAK;AAAA,EACnD;AACF;AAEA,eAAsB,uBACpB,WACA,SACmD;AACnD,QAAM,MAAM,kBAAkB,SAAS;AACvC,QAAM,MAAM,MAAM,gBAAgB,IAAI,WAAW,OAAO;AACxD,QAAM,MAAM,IAAI,YAAY,SACxB,MAAM,qBAAqB,IAAI,SAAS,IACxC,MAAM,iBAAiB,IAAI,YAAY,IAAI,SAAS;AACxD,SAAO,EAAE,KAAK,IAAI;AACpB;AAEA,eAAe,qBAAqB,IAAwC;AAC1E,QAAM,OAAO,MAAM,IAAI,EAAE,WAAW,IAAI,EAAE,EAAE,MAAM,CAAC,QAAiB;AAClE,UAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,UAAM,IAAI,MAAM,wBAAwB,EAAE,0BAA0B,MAAM,EAAE;AAAA,EAC9E,CAAC;AACD,MAAI,CAAC,MAAM,GAAI,OAAM,IAAI,MAAM,wBAAwB,EAAE,wBAAwB;AACjF,SAAO,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,YAAY,IAAI,UAAU,OAAO,KAAK,QAAQ,EAAE;AACvF;AAGA,eAAsB,gBACpB,YACA,UACA,YACiB;AACjB,QAAM,OAAO,MAAM,IAAI,EAAE,QAAQ,KAAK;AAAA,IACpC;AAAA,IACA,aAAa;AAAA,IACb,SAAS,EAAE,UAAU,MAAM,UAA2B,YAAY,UAAU;AAAA,IAC5E,YAAY,EAAE,MAAM,GAAG,UAAU,GAAG;AAAA,EACtC,CAAC;AACD,QAAM,UAAU,MAAM,WAAW,CAAC;AAClC,QAAM,SAAS,WAAW,YAAY;AACtC,QAAM,QACJ,QAAQ,KAAK,CAAC,MAAM,GAAG,MAAM,YAAY,MAAM,MAAM,KACrD,QAAQ,KAAK,CAAC,MAAM,GAAG,MAAM,YAAY,EAAE,SAAS,MAAM,CAAC;AAE7D,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,YAAY,QAAQ,IAAI,CAAC,MAAM,OAAO,GAAG,IAAI,EAAE,EAAE,KAAK,IAAI;AAChE,UAAM,IAAI;AAAA,MACR,WAAW,UAAU,mBAAmB,QAAQ,OAC7C,YAAY;AAAA;AAAA,EAAqD,SAAS,KAAK;AAAA,IACpF;AAAA,EACF;AACA,SAAO,MAAM;AACf;;;AC9FA,SAAS,KAAAC,UAAS;AAEX,IAAM,oBAAoB;AAE1B,IAAM,iBAAiB;AAAA,EAC5B,MAAMA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,EAC1F,UAAUA,GACP,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,GAAG,EACP,SAAS,EACT,SAAS,6BAA6B,iBAAiB,GAAG;AAC/D;AAEO,SAAS,WAAW,MAAe,UAAmB,kBAAkB,mBAAmB;AAChG,SAAO,EAAE,MAAM,QAAQ,GAAG,UAAU,YAAY,gBAAgB;AAClE;AASO,SAAS,YAAY,MAAc,OAAe,UAAwC;AAC/F,QAAM,QAAQ,UAAU;AACxB,QAAM,OACJ,SAAS,QAAQ,UAAU,QACvB,WAAW,KAAK,OAAO,KAAK,IAAI,IAAI,UAAU,UAAU,QAAQ,CAAC,OACjE,SAAS,KAAK,IAAI,IAAI;AAC5B,SAAO,UAAU,cAAc,GAAG,IAAI,0DAAqD;AAC7F;;;AHzBA,IAAM,eAAeC,GAAE,KAAK,CAAC,YAAY,YAAY,CAAC;AAEtD,SAAS,cAAc,QAAqD;AAC1E,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC5E,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAM,EAAE,MAAM,WAAW,cAAc,QAAQ,IAAI;AACnD,QAAI,YAAY,MAAO;AACvB,UAAM,QAAQ,CAAC,OAAO,QAAQ,SAAS,GAAG,cAAc,QAAQ,aAAa,UAAU;AACvF,QAAI,iBAAiB,OAAW,OAAM,KAAK,WAAW,KAAK,UAAU,YAAY,CAAC,EAAE;AACpF,YAAQ,IAAI,IAAI,MAAM,KAAK,IAAI;AAAA,EACjC;AACA,SAAO,OAAO,KAAK,OAAO,EAAE,SAAS,IAAI,UAAU;AACrD;AAEO,SAAS,kBAAkB,QAAmB;AACnD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,CAAC;AAAA,IAChB;AAAA,IACA,YACE,MAAM,YAAY;AAChB,YAAM,WAAW,MAAM,IAAI,EAAE,SAAS,KAAK;AAC3C,aAAO,GAAG,SAAS,UAAU,UAAU,CAAC,gBAAgB,QAAQ;AAAA,IAClE,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+BAA+B;AAAA,QACtE,UAAU,aAAa,SAAS;AAAA,QAChC,GAAG;AAAA,QACH,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MAC3F;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAQ,UAAU,MAAM,UAAU,QAAQ,MACjD,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,WAAW,KAAK;AAAA,QACzC,YAAY,IAAI;AAAA,QAChB,aAAa;AAAA,QACb,SAAS,WAAW,EAAE,SAAS,IAAI;AAAA,QACnC,YAAY,WAAW,MAAM,QAAQ;AAAA,MACvC,CAAC;AACD,aAAO;AAAA,QACL,YAAY,gBAAgB,QAAQ,SAAS,UAAU,GAAG,QAAQ,QAAQ;AAAA,QAC1E;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,yBAAyB;AAAA,QACxD,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MAC3F;AAAA,IACF;AAAA,IACA,OAAO,EAAE,WAAW,QAAQ,MAC1B,MAAM,YAAY;AAChB,YAAM,EAAE,KAAK,SAAS,IAAI,MAAM,uBAAuB,WAAW,OAAO;AACzE,YAAM,UAAU,MAAM,IAAI,EAAE,WAAW,IAAI,SAAS,EAAE;AACtD,aAAO,GAAG,aAAa,SAAS,QAAQ,SAAS,SAAS,EAAE,MAAM,OAAO;AAAA,IAC3E,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iCAAiC;AAAA,QACxE,GAAG;AAAA,QACH,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MAC3F;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAQ,MAAM,UAAU,QAAQ,MACvC,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,SAAS,KAAK;AAAA,QACvC,eAAe,IAAI;AAAA,QACnB,aAAa;AAAA,QACb,YAAY,WAAW,MAAM,QAAQ;AAAA,MACvC,CAAC;AACD,aAAO;AAAA,QACL,YAAY,sBAAsB,QAAQ,SAAS,UAAU,GAAG,QAAQ,QAAQ;AAAA,QAChF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,UAAU,aAAa,SAAS;AAAA,QAChC,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uBAAuB;AAAA,QAC9D,GAAG;AAAA,QACH,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MAC3F;AAAA,IACF;AAAA,IACA,OAAO,EAAE,UAAU,QAAQ,MAAM,UAAU,QAAQ,MACjD,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,QAAQ,KAAK;AAAA,QACtC,YAAY,IAAI;AAAA,QAChB,aAAa;AAAA,QACb,SAAS,EAAE,UAAU,MAAM,YAAY,WAAW,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC,EAAG;AAAA,QACpF,YAAY,WAAW,MAAM,UAAU,GAAG;AAAA,MAC5C,CAAC;AACD,YAAM,WAAW,QAAQ,WAAW,CAAC,GAAG,IAAI,CAAC,OAAO;AAAA,QAClD,MAAM,GAAG;AAAA,QACT,aAAa,GAAG,eAAe;AAAA,QAC/B,MAAM,cAAc,GAAG,MAAM;AAAA,MAC/B,EAAE;AACF,aAAO,GAAG,YAAY,aAAa,QAAQ,QAAQ,QAAQ,QAAQ,GAAG,OAAO;AAAA,IAC/E,CAAC;AAAA,EACL;AACF;;;AIxJA,SAAS,KAAAC,UAAS;AAQlB,eAAe,eACb,SACA,WACA,YACA,OAAyC,CAAC,GAC1C;AACA,QAAM,EAAE,KAAK,IAAI,IAAI,MAAM,uBAAuB,WAAW,OAAO;AACpE,QAAM,WAAW,MAAM,gBAAgB,IAAI,YAAY,IAAI,UAAU,UAAU;AAE/E,QAAM,SAAS,MAAM,IAAI,EAAE,SAAS,UAAU;AAAA,IAC5C,WAAW,IAAI;AAAA,IACf,QAAQ,EAAE,gBAAgB,KAAK,QAAQ,GAAG,OAAO,GAAG,UAAU,KAAK;AAAA,EACrE,CAAC;AACD,SAAO,EAAE,KAAK,YAAY,OAAO;AACnC;AAEO,SAAS,uBAAuB,QAAmB;AACxD,QAAM,aAAaC,GAChB,OAAO,EACP,SAAS,EACT,SAAS,iDAAiD;AAE7D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,OAAO,iBAAiB,OAAO,eAAe,KAAK;AAAA,MAChF,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,yBAAyB;AAAA,QACxD,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,WAAW,QAAQ,MAC1B,MAAM,YAAY;AAChB,YAAM,EAAE,KAAK,OAAO,IAAI,MAAM,eAAe,SAAS,WAAW,kBAAkB;AACnF,aAAO,GAAG,cAAc,IAAI,QAAQ,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,IACrE,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,eAAe,KAAK;AAAA,MAC/E,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,yBAAyB;AAAA,QACxD,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,WAAW,QAAQ,MAC1B,MAAM,YAAY;AAChB,YAAM,EAAE,KAAK,OAAO,IAAI,MAAM,eAAe,SAAS,WAAW,mBAAmB;AACpF,aAAO,GAAG,eAAe,IAAI,QAAQ,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,IACtE,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,eAAe,KAAK;AAAA,MAC/E,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,yBAAyB;AAAA,QACxD,YAAYA,GAAE,OAAO,EAAE,SAAS,yCAAyC;AAAA,QACzE,MAAMA,GACH,MAAMA,GAAE,OAAO,EAAE,KAAKA,GAAE,OAAO,GAAG,OAAOA,GAAE,OAAO,EAAE,CAAC,CAAC,EACtD,SAAS,EACT,SAAS,qCAAqC;AAAA,QACjD,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,WAAW,YAAY,MAAM,QAAQ,MAC5C,MAAM,YAAY;AAChB,YAAM,EAAE,KAAK,OAAO,IAAI,MAAM,eAAe,SAAS,WAAW,YAAY,QAAQ,CAAC,CAAC;AACvF,aAAO,GAAG,QAAQ,UAAU,QAAQ,IAAI,QAAQ,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,IACjF,CAAC;AAAA,EACL;AACF;;;AC3FA,SAAS,KAAAC,UAAS;AAOlB,IAAMC,gBAAeC,GAAE,KAAK,CAAC,YAAY,YAAY,CAAC;AAE/C,SAAS,kBAAkB,QAAmB;AACnD,QAAM,aAAaA,GAChB,OAAO,EACP,SAAS,EACT,SAAS,iDAAiD;AAE7D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,UAAUD,cAAa,SAAS;AAAA,QAChC,QAAQC,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qBAAqB;AAAA,QAC5D,GAAG;AAAA,QACH,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,UAAU,QAAQ,MAAM,UAAU,QAAQ,MACjD,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,MAAM,KAAK;AAAA,QACpC,YAAY,IAAI;AAAA,QAChB,aAAa;AAAA,QACb,SAAS,WAAW,EAAE,SAAS,IAAI;AAAA,QACnC,YAAY,WAAW,MAAM,QAAQ;AAAA,MACvC,CAAC;AACD,aAAO,GAAG,YAAY,WAAW,QAAQ,SAAS,UAAU,GAAG,QAAQ,QAAQ,GAAG,MAAM;AAAA,IAC1F,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,QAAQA,GAAE,OAAO,EAAE,SAAS,oBAAoB;AAAA,MAClD;AAAA,IACF;AAAA,IACA,OAAO,EAAE,OAAO,MACd,MAAM,YAAY;AAChB,YAAM,OAAO,MAAM,IAAI,EAAE,MAAM,IAAI,MAAM;AACzC,UAAI,CAAC,KAAM,QAAO,GAAG,yBAAyB,MAAM,GAAG;AACvD,aAAO,GAAG,QAAQ,KAAK,IAAI,SAAS,MAAM,MAAM,IAAI;AAAA,IACtD,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,OAAO,iBAAiB,OAAO,eAAe,KAAK;AAAA,MAChF,OAAO;AAAA,MACP,aACE;AAAA,MAIF,aAAa;AAAA,QACX,MAAMA,GAAE,OAAO,EAAE,SAAS,WAAW;AAAA,QACrC,UAAUD;AAAA,QACV,OAAOC,GACJ;AAAA,UACCA,GAAE,OAAO;AAAA,YACP,YAAYA,GAAE,OAAO,EAAE,SAAS,4CAA4C;AAAA,YAC5E,MAAMA,GACH,MAAMA,GAAE,OAAO,EAAE,KAAKA,GAAE,OAAO,GAAG,OAAOA,GAAE,OAAO,EAAE,CAAC,CAAC,EACtD,SAAS,EACT,SAAS,qCAAqC;AAAA,UACnD,CAAC;AAAA,QACH,EACC,IAAI,CAAC,EACL,SAAS,uBAAuB;AAAA,QACnC,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,UAAU,OAAO,QAAQ,MACtC,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AAEzC,YAAM,UAAU,CAAC;AACjB,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,WAAW,MAAM,gBAAgB,IAAI,YAAY,UAAU,MAAM,CAAC,EAAE,UAAU;AACpF,gBAAQ,KAAK;AAAA,UACX,OAAO,IAAI;AAAA,UACX,QAAQ;AAAA,UACR;AAAA,UACA,gBAAgB;AAAA,UAChB,MAAM,MAAM,CAAC,EAAE,QAAQ,CAAC;AAAA,QAC1B,CAAC;AAAA,MACH;AAEA,YAAM,OAAO,MAAM,IAAI,EAAE,MAAM,OAAO;AAAA,QACpC,YAAY,IAAI;AAAA,QAChB;AAAA,QACA,YAAY;AAAA,QACZ,cAAc;AAAA,UACZ,EAAE,KAAK,WAAW,MAAM,WAAW,QAAQ,CAAC,EAAE,KAAK,YAAY,OAAO,SAAS,CAAC,EAAE;AAAA,QACpF;AAAA,QACA;AAAA,MACF,CAAC;AACD,aAAO,GAAG,iBAAiB,IAAI,UAAU,MAAM,EAAE,MAAM,IAAI;AAAA,IAC7D,CAAC;AAAA,EACL;AACF;;;ACnHA,SAAS,KAAAC,UAAS;AAQX,SAAS,qBAAqB,QAAmB;AACtD,QAAM,aAAaC,GAChB,OAAO,EACP,SAAS,EACT,SAAS,iDAAiD;AAE7D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,OAAO,iBAAiB,OAAO,eAAe,KAAK;AAAA,MAChF,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,QAAQA,GAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,QACvD,WAAWA,GAAE,OAAO,EAAE,SAAS,GAAG,yBAAyB,qBAAqB;AAAA,QAChF,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAQ,WAAW,QAAQ,MAClC,MAAM,YAAY;AAChB,YAAM,EAAE,KAAK,IAAI,IAAI,MAAM,uBAAuB,WAAW,OAAO;AACpE,YAAM,OAAO,MAAM,IAAI,EAAE,MAAM,IAAI,MAAM;AACzC,YAAM,gBAAgB,MAAM,eAAe,CAAC,GAAG;AAC/C,UAAI,CAAC,cAAe,QAAO,KAAK,QAAQ,MAAM,uCAAuC;AAErF,YAAM,MAAM,MAAM,IAAI,EACnB,SAAS,OAAO;AAAA,QACf,YAAY;AAAA,QACZ,YAAY,IAAI;AAAA,QAChB,cAAc;AAAA,QACd,SAAS;AAAA,QACT,aAAa;AAAA,QACb,oBAAoB;AAAA,UAClB,EAAE,eAAe,UAAU,IAAI,IAAI,YAAY,YAAY;AAAA,QAC7D;AAAA,MACF,CAAC,EACA,MAAM,OAAO,QAAiB;AAC7B,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAI,CAAC,YAAY,KAAK,OAAO,EAAG,OAAM;AAEtC,cAAM,UAAU,MAAM,IAAI,EAAE,WAAW,IAAI,IAAI,EAAE,EAAE,MAAM,MAAM,IAAI;AACnE,cAAM,WAAW,SAAS;AAC1B,cAAM,IAAI;AAAA,UACR,GAAG,IAAI,QAAQ,SAAS,IAAI,EAAE,eAC3B,UAAU,KAAK,kBAAkB,SAAS,EAAE,KAAK,SAAS,MAAM,MAAM,MACvE;AAAA,QAEJ;AAAA,MACF,CAAC;AACH,aAAO;AAAA,QACL,iBAAiB,MAAM,IAAI,QAAQ,IAAI,QAAQ,kBAAa,KAAK,EAAE,YAAY,KAAK,MAAM;AAAA,QAC1F;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,OAAOA,GAAE,OAAO,EAAE,SAAS,aAAa;AAAA,MAC1C;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,MACb,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,IAAI,EAAE,SAAS,IAAI,KAAK;AAC1C,UAAI,CAAC,IAAK,QAAO,GAAG,6BAA6B,KAAK,GAAG;AACzD,YAAM,SAAS,IAAI,aAAa,WAAM,IAAI,UAAU,KAAK;AACzD,aAAO,GAAG,YAAY,KAAK,MAAM,IAAI,UAAU,IAAI,OAAO,IAAI,MAAM,GAAG,MAAM,KAAK,GAAG;AAAA,IACvF,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,KAAK;AAAA,MAClC,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,GAAG;AAAA,QACH,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,UAAU,QAAQ,MAC/B,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,gBAAgB,OAAO;AACzC,YAAM,SAAS,MAAM,IAAI,EAAE,SAAS,KAAK;AAAA,QACvC,YAAY,IAAI;AAAA,QAChB,YAAY,WAAW,MAAM,QAAQ;AAAA,MACvC,CAAC;AACD,aAAO,GAAG,YAAY,eAAe,QAAQ,SAAS,UAAU,GAAG,QAAQ,QAAQ,GAAG,MAAM;AAAA,IAC9F,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,eAAe,KAAK;AAAA,MAC/E,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,OAAOA,GAAE,OAAO,EAAE,SAAS,aAAa;AAAA,MAC1C;AAAA,IACF;AAAA,IACA,OAAO,EAAE,MAAM,MACb,MAAM,YAAY;AAChB,YAAM,WAAW,MAAM,IAAI,EAAE,SAAS,IAAI,KAAK;AAC/C,UAAI,CAAC,SAAU,QAAO,KAAK,6BAA6B,KAAK,GAAG;AAEhE,YAAM,IAAI,EAAE,SAAS,SAAS,KAAK;AACnC,YAAM,QAAQ,MAAM,IAAI,EAAE,SAAS,IAAI,KAAK;AAC5C,aAAO;AAAA,QACL,sBAAsB,KAAK,SAAS,SAAS,MAAM,SAAS,OAAO,UAAU,SAAS;AAAA,QACtF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AACF;;;ACnIA,SAAS,KAAAC,UAAS;;;ACCX,IAAM,oBAAoB;AAC1B,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB;AACvB,IAAM,oBAAoB;AAEjC,IAAM,UAAkC,EAAE,GAAG,KAAO,GAAG,KAAQ,GAAG,MAAW,GAAG,MAAW;AAEpF,SAAS,cAAc,OAAuB;AACnD,QAAM,QAAQ,MAAM,KAAK,EAAE,MAAM,qBAAqB;AACtD,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,qBAAqB,KAAK,0CAA0C;AAChG,SAAO,OAAO,MAAM,CAAC,CAAC,IAAI,QAAQ,MAAM,CAAC,EAAG,YAAY,CAAC;AAC3D;AAEA,SAAS,aAAa,OAAe,MAAoB;AACvD,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,OAAM,IAAI,MAAM,WAAW,IAAI,KAAK,KAAK,0BAA0B;AACrG,SAAO;AACT;AAEO,SAAS,cACd,OACA,MAAY,oBAAI,KAAK,GACK;AAC1B,QAAM,KAAK,MAAM,KAAK,aAAa,MAAM,IAAI,IAAI,IAAI;AACrD,QAAM,OAAO,MAAM,OACf,aAAa,MAAM,MAAM,MAAM,IAC/B,IAAI,KAAK,GAAG,QAAQ,IAAI,cAAc,MAAM,QAAQ,iBAAiB,CAAC;AAC1E,MAAI,KAAK,QAAQ,IAAI,GAAG,QAAQ,EAAG,OAAM,IAAI,MAAM,WAAW,KAAK,YAAY,CAAC,oBAAoB,GAAG,YAAY,CAAC,IAAI;AACxH,SAAO,EAAE,MAAM,GAAG;AACpB;AAoBA,SAAS,QAAQ,MAA8B;AAC7C,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,OAAwB;AACzC,SAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AACjE;AAEO,SAAS,SAAS,MAAc,MAAM,mBAA2B;AACtE,SAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC,WAAM,KAAK,SAAS,GAAG,YAAY;AACrF;AAEA,SAAS,UAAU,QAAuB,UAAuB;AAC/D,QAAM,MAAM,UAAU,SAAS;AAC/B,MAAI,QAAQ,mBAAoB,QAAO;AACvC,MAAI,QAAQ,mBAAoB,QAAO;AACvC,MAAI,OAAO,UAAU,OAAO,SAAU,QAAO,GAAG,OAAO,UAAU,GAAG,SAAI,OAAO,YAAY,GAAG;AAC9F,SAAO;AACT;AAEO,SAAS,WAAW,SAAqC;AAC9D,QAAM,SAAS,QAAQ,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,SAAS,QAAQ,EAAE,OAAO,GAAG,UAAU,QAAQ,EAAE,UAAU,EAAE,EAAE;AAE/G,QAAM,cAAc,oBAAI,IAAoB;AAC5C,aAAW,EAAE,QAAQ,KAAK,QAAQ;AAChC,QAAI,MAAM,QAAQ,OAAO,KAAK,QAAQ,CAAC,MAAM,EAAG,aAAY,IAAI,OAAO,QAAQ,CAAC,CAAC,GAAG,OAAO,QAAQ,CAAC,CAAC,CAAC;AAAA,EACxG;AAEA,SAAO,OAAO,IAAI,CAAC,EAAE,QAAQ,SAAS,SAAS,MAAM;AACnD,QAAI;AACJ,QAAI;AACJ,QAAI,OAAO,aAAa,UAAU,MAAM,QAAQ,OAAO,KAAK,CAAC,GAAG,GAAG,CAAC,EAAE,SAAS,QAAQ,CAAC,CAAC,GAAG;AAC1F,YAAM,OAAO,YAAY,IAAI,OAAO,QAAQ,CAAC,CAAC,CAAC;AAC/C,UAAI,QAAQ,CAAC,MAAM,GAAG;AACpB,iBAAS,OAAO,QAAQ,CAAC,CAAC;AAC1B,kBAAU,UAAU,QAAQ,CAAC,KAAK,CAAC,CAAC;AAAA,MACtC,WAAW,QAAQ,CAAC,MAAM,GAAG;AAC3B,iBAAS,GAAG,QAAQ,GAAG;AACvB,kBAAU,UAAU,QAAQ,CAAC,KAAK,CAAC,CAAC;AAAA,MACtC,OAAO;AACL,iBAAS,GAAG,QAAQ,GAAG;AACvB,kBAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,QAAQ,CAAC,IAAI,UAAU,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MACtG;AAAA,IACF,OAAO;AACL,eAAS,OAAQ,UAAkB,QAAQ,OAAO,YAAY,KAAK;AACnE,gBAAU,OAAO,WAAW;AAAA,IAC9B;AACA,QAAI,OAAO,MAAO,WAAU,GAAG,OAAO,YAAY,OAAO,KAAK;AAC9D,WAAO;AAAA,MACL,MAAM,OAAO,aAAa;AAAA,MAC1B,WAAW,UAAU,QAAQ,QAAQ;AAAA,MACrC;AAAA,MACA,SAAS,SAAS,QAAQ,QAAQ,QAAQ,GAAG,EAAE,KAAK,CAAC;AAAA,IACvD;AAAA,EACF,CAAC;AACH;AAEO,SAAS,cAAc,MAAe,QAAyB;AACpE,QAAM,OAAO,KAAK,OAAO,QAAQ,qBAAqB,EAAE,EAAE,YAAY;AACtE,SAAO,SAAS,OAAO,KAAK,EAAE,YAAY;AAC5C;AAEO,SAAS,WAAW,MAAuB;AAChD,SAAO,GAAG,KAAK,IAAI,KAAK,KAAK,SAAS,KAAK,KAAK,MAAM,KAAK,KAAK,OAAO;AACzE;;;ADtGA,IAAM,YAAY,EAAE,cAAc,MAAM,eAAe,KAAK;AAE5D,IAAM,aAAa;AAIZ,SAAS,gBAAsC,OAA6C;AACjG,UAAQ,SAAS,CAAC,GAAG;AAAA,IAAI,CAAC,SACxB,WAAW,KAAK,KAAK,OAAO,KAAK,gBAAgB,EAAE,KAAK,KAAK,QAAQ,EAAE,GAAG,MAAM,OAAO,MAAM,IAAI;AAAA,EACnG;AACF;AAEA,SAAS,mBAAmB,KAAkB;AAC5C,QAAM,QAAQ,IAAI;AAClB,QAAM,SAAS,IAAI;AACnB,QAAM,cAAc,OAAO,mBAAmB,IAAI,qBAAqB,CAAC,GACrE,IAAI,CAAC,MAAW,IAAI,EAAE,UAAU,EAAE,WAAW,IAAI,EAAE,IAAI,WAAW,EAAE,aAAa,UAAU,UAAU,GAAG,EACxG,KAAK,IAAI;AACZ,QAAM,QAAQ;AAAA,IACZ,aAAa,IAAI,QAAQ,SAAS,IAAI,EAAE,MAAM,IAAI,QAAQ,aAAa,IAAI,aAAa,WAC7E,IAAI,cAAc,iBAAiB,IAAI,aAAa,GAAG,IAAI,YAAY,gBAAgB,EAAE;AAAA,IACpG,UAAU,OAAO,QAAQ,GAAG,OAAO,OAAO,oBAAoB,QAAQ,GAAG,MACtE,aAAa,iBAAiB,UAAU,MAAM;AAAA,IACjD,aAAa,QAAQ,YAAY,GAAG,sBAAsB,QAAQ,qBAAqB,OAAO,KAAK,0BACzE,QAAQ,qBAAqB,CAAC,GAAG,KAAK,IAAI,KAAK,QAAG;AAAA,IAC5E,4BAA4B,IAAI,wBAAwB,UAAU,CAAC;AAAA,EACrE;AACA,MAAI,IAAI,SAAS;AACf,UAAM;AAAA,MACJ,YAAY,IAAI,QAAQ,EAAE,KAAK,IAAI,QAAQ,MAAM,MAC9C,IAAI,QAAQ,aAAa,WAAM,IAAI,QAAQ,UAAU,KAAK,MAAM;AAAA,IACrE;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,IAAM,2BAA2B;AAEjC,SAAS,cAAc,IAAiB;AACtC,SAAO,GAAG,SAAS,YAAY,GAAG,QAAQ,gBAAgB,GAAG;AAC/D;AAEA,SAAS,iBAAiB,SAAgB,WAAwB;AAChE,QAAM,SAAS,UAAU,KAAK,EAAE,YAAY;AAC5C,SACE,QAAQ,KAAK,CAAC,OAAO,GAAG,OAAO,UAAU,KAAK,CAAC,KAC/C,QAAQ,KAAK,CAAC,OAAO,GAAG,SAAS,OAAO,UAAU,KAAK,KAAK,GAAG,QAAQ,OAAO,UAAU,KAAK,CAAC,KAC9F,QAAQ;AAAA,IAAK,CAAC,OACZ,GAAG,SAAS,UAAU,YAAY,MAAM,UAAU,GAAG,QAAQ,cAAc,YAAY,MAAM;AAAA,EAC/F;AAEJ;AAEA,SAAS,aAAa,KAAU,YAAiB,MAAc,OAAoB;AACjF,QAAM,WAAW,YAAY,WAAW,CAAC,GAAG,OAAO,OAAO;AAC1D,QAAM,QAAQ;AAAA,IACZ,YAAY,IAAI,EAAE,MAAM,IAAI,UAAU,QAAQ,GAAG,OAAO,IAAI,MAAM,MAC/D,IAAI,aAAa,WAAM,IAAI,UAAU,KAAK,MAC3C,aAAa,IAAI,sBAAsB,IAAI,kBAAkB,QAAG,aAAa,IAAI,qBAAqB,QAAG;AAAA,IAC3G,YAAY,gBAAgB,QAAQ,QAAQ,YAAY,YAAY,EAAE,KAAK,CAAC;AAAA,EAC9E;AACA,aAAW,MAAM,SAAS;AACxB,UAAM,KAAK,KAAK,cAAc,EAAE,CAAC,eAAe,GAAG,SAAS,MAAM,GAAG,QAAQ,MAAM,GAAG,eAAe,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;AAAA,EAC7H;AACA,MAAI,OAAO;AACT,UAAM,KAAK,IAAI,YAAY,cAAc,KAAK,CAAC,GAAG;AAClD,eAAW,QAAQ,MAAM,kBAAkB,CAAC,GAAG;AAC7C,YAAM;AAAA,QACJ,YAAY,KAAK,KAAK,IAAI,KAAK,gBAAgB,QAAQ,QAAQ,GAAG,KAAK,KAAK,MAAM,MAC/E,KAAK,aAAa,WAAM,KAAK,UAAU,KAAK;AAAA,MACjD;AAAA,IACF;AAAA,EACF,WAAW,QAAQ,SAAS,KAAK,YAAY,UAAU,aAAa;AAClE,UAAM,KAAK,IAAI,uEAAuE;AAAA,EACxF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,qBAAqB,QAAmB;AACtD,QAAM,aAAaC,GAChB,OAAO,EACP,SAAS,EACT,SAAS,6EAA6E;AAEzF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAMF,aAAa,EAAE,WAAWA,GAAE,OAAO,EAAE,SAAS,yBAAyB,GAAG,SAAS,WAAW;AAAA,MAC9F,aAAa;AAAA,IACf;AAAA,IACA,OAAO,EAAE,WAAW,QAAQ,MAC1B,MAAM,YAAY;AAChB,YAAM,EAAE,IAAI,IAAI,MAAM,uBAAuB,WAAW,OAAO;AAC/D,YAAM,UAAU,MAAM,IAAI,EAAE,WAAW,WAAW,IAAI,EAAE;AACxD,UAAI,CAAC,QAAS,QAAO,GAAG,8BAA8B,IAAI,EAAE,GAAG;AAC/D,YAAM,QAAQ,MAAM,IAAI,EAAE,WAAW,mBAAmB,IAAI,EAAE;AAC9D,YAAM,OAAO,EAAE,GAAG,SAAS,wBAAwB,gBAAgB,KAA4B,EAAE;AACjG,aAAO,GAAG,mBAAmB,IAAI,GAAG,IAAI;AAAA,IAC1C,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAIF,aAAa;AAAA,QACX,WAAWA,GAAE,OAAO,EAAE,SAAS,yBAAyB;AAAA,QACxD,MAAMA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gDAAgD;AAAA,QACrF,IAAIA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,QAC5E,MAAMA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6DAA6D,iBAAiB,GAAG;AAAA,QACtH,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,cAAc,EAAE,SAAS,EACzD,SAAS,iCAAiC,kBAAkB,SAAS,cAAc,GAAG;AAAA,QACzF,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qDAAqD;AAAA,QAC5F,SAAS;AAAA,MACX;AAAA,MACA,aAAa;AAAA,IACf;AAAA,IACA,OAAO,EAAE,WAAW,MAAM,IAAI,MAAM,OAAO,QAAQ,QAAQ,MACzD,MAAM,YAAY;AAChB,YAAM,SAAS,cAAc,EAAE,MAAM,IAAI,KAAK,CAAC;AAC/C,YAAM,EAAE,IAAI,IAAI,MAAM,uBAAuB,WAAW,OAAO;AAE/D,YAAM,QAAQ,KAAK,IAAI,SAAS,oBAAoB,cAAc;AAClE,YAAM,SAAS,MAAM,IAAI,EAAE,KAAK,KAAK;AAAA,QACnC,aAAa,IAAI;AAAA,QACjB,MAAM,OAAO;AAAA,QACb,IAAI,OAAO;AAAA,QACX,OAAO,SAAS,iBAAiB;AAAA,MACnC,CAAC;AAED,YAAM,MAAM,WAAW,OAAO,OAAO;AACrC,YAAM,UAAU,SAAS,IAAI,OAAO,CAAC,SAAS,cAAc,MAAM,MAAM,CAAC,IAAI;AAC7E,YAAM,QAAQ,QAAQ,MAAM,GAAG,KAAK,EAAE,QAAQ;AAC9C,YAAM,YAAY,QAAQ,SAAS,SAAS,QAAQ,OAAO,UAAU,WAAW;AAEhF,YAAM,OACJ,GAAG,IAAI,QAAQ,SAAS,IAAI,EAAE,KAAK,OAAO,KAAK,YAAY,CAAC,WAAM,OAAO,GAAG,YAAY,CAAC,KACtF,MAAM,MAAM,uBACd,SAAS,cAAc,MAAM,QAAQ,IAAI,MAAM,aAAa,OAC5D,OAAO,UAAU,cAAc,OAAO,KAAK,OAAO,SAAS,UAAU,eAAe,MACrF,gBAAgB,YAAY,oDAA+C,IAAI;AACjF,aAAO,GAAG,CAAC,MAAM,IAAI,GAAG,MAAM,IAAI,UAAU,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,IAC3D,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE,iHACsB,wBAAwB;AAAA,MAGhD,aAAa;AAAA,QACX,OAAOA,GAAE,OAAO,EAAE,SAAS,aAAa;AAAA,QACxC,MAAMA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,yDAAyD;AAAA,QAC3G,WAAWA,GAAE,OAAO,EAAE,SAAS,EAC5B,SAAS,wFAAwF;AAAA,MACtG;AAAA,MACA,aAAa;AAAA,IACf;AAAA,IACA,OAAO,EAAE,OAAO,MAAM,UAAU,MAC9B,MAAM,YAAY;AAChB,YAAM,MAAM,MAAM,IAAI,EAAE,SAAS,WAAW,KAAK;AACjD,UAAI,CAAC,IAAK,QAAO,GAAG,6BAA6B,KAAK,GAAG;AACzD,YAAM,UAAU,QAAQ;AACxB,YAAM,aAAa,MAAM,IAAI,EAAE,SAAS,eAAe,OAAO;AAAA,QAC5D,YAAY,EAAE,MAAM,SAAS,UAAU,yBAAyB;AAAA,MAClE,CAAC;AACD,YAAM,WAAW,YAAY,WAAW,CAAC,GAAG,OAAO,OAAO;AAE1D,UAAI;AACJ,UAAI,WAAW;AACb,iBAAS,iBAAiB,SAAS,SAAS;AAC5C,YAAI,CAAC,QAAQ;AACX,iBAAO;AAAA,YACL,iBAAiB,SAAS,aAAa,OAAO,gBAAgB,KAAK;AAAA,UAErE;AAAA,QACF;AAAA,MACF,WAAW,YAAY,UAAU,eAAe,KAAM,YAAY,KAAK,QAAQ,WAAW,KAAK,CAAC,YAAY,UAAU,aAAc;AAClI,iBAAS,QAAQ,CAAC;AAAA,MACpB;AAEA,YAAM,MAAM,QAAQ,SAAS,KACzB,EAAE,WAAW,OAAO,QAAQ,GAAa,IACzC,QAAQ,QAAQ,KAAK,EAAE,UAAU,OAAO,OAAO,GAAa,IAAI;AACpE,YAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,SAAS,kBAAkB,OAAO,GAAG,IAAI;AACzE,aAAO,GAAG,aAAa,KAAK,YAAY,SAAS,KAAK,GAAG,EAAE,KAAK,YAAY,MAAM,CAAC;AAAA,IACrF,CAAC;AAAA,EACL;AACF;;;AEtNO,IAAM,eAAe;AAErB,SAAS,eAAe,MAAyB,QAAQ,KAAc;AAC5E,QAAM,QAAQ,IAAI,YAAY,GAAG,KAAK,EAAE,YAAY;AACpD,SAAO,UAAU,OAAO,UAAU;AACpC;AAEA,SAAS,aAAa,QAA8B;AAClD,SAAO,IAAI,MAAM,QAAQ;AAAA,IACvB,IAAI,QAAQ,MAAM,UAAU;AAC1B,UAAI,SAAS,eAAgB,QAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AACtE,aAAO,CAAC,MAAc,QAAsD,OAC1E,OAAO,aAAa,iBAAiB,OAChC,OAAO,aAAiD,MAAM,QAAQ,EAAE,IACzE;AAAA,IACR;AAAA,EACF,CAAC;AACH;AAEO,SAAS,cAAc,QAAmB,SAAgC;AAC/E,QAAM,SAAS,QAAQ,WAAW,aAAa,MAAM,IAAI;AACzD,uBAAqB,MAAM;AAC3B,oBAAkB,MAAM;AACxB,yBAAuB,MAAM;AAC7B,oBAAkB,MAAM;AACxB,uBAAqB,MAAM;AAC3B,uBAAqB,MAAM;AAC7B;;;Ad9BA,eAAe,OAAO;AACpB,MAAI,CAAC,QAAQ,IAAI,oBAAoB;AACnC,YAAQ;AAAA,MACN;AAAA,IAEF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,IAAI,UAAU,EAAE,MAAM,YAAY,SAAS,QAAQ,CAAC;AAEnE,QAAM,WAAW,eAAe;AAChC,gBAAc,QAAQ,EAAE,SAAS,CAAC;AAElC,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,UAAQ,MAAM,uCAAuC,WAAW,iBAAiB,EAAE,EAAE;AACvF;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,6CAA6C,KAAK;AAChE,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["z","z","z","z","z","z","protocolEnum","z","z","z","z","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timetick/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Model Context Protocol (MCP) server for Timetick (https://app.timetick.io) platform",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "Timetick <support@timetick.io>",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "type": "module",
21
21
  "bin": {
22
- "timetick-mcp": "./dist/index.js"
22
+ "timetick-mcp": "dist/index.js"
23
23
  },
24
24
  "main": "./dist/index.js",
25
25
  "files": [
@@ -35,6 +35,7 @@
35
35
  },
36
36
  "scripts": {
37
37
  "build": "tsup",
38
+ "prepublishOnly": "yarn build",
38
39
  "dev": "tsup --watch",
39
40
  "typecheck": "tsc --noEmit",
40
41
  "start": "node dist/index.js",