@bixbd/utils-mcp 0.1.0-beta.5 → 0.1.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +337 -117
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -4504,44 +4504,10 @@ var init_src = __esm(() => {
4504
4504
  init_registry();
4505
4505
  });
4506
4506
 
4507
- // ../../connectors/desco/src/schemas.ts
4508
- var RawAccounts, RawBalance, RawRealtime, AccountsOut, BalanceOut, RealtimeOut;
4509
- var init_schemas3 = __esm(() => {
4507
+ // ../../connectors/desco/src/schemas/out.ts
4508
+ var AccountsOut, BalanceOut, RealtimeOut, DataOut;
4509
+ var init_out = __esm(() => {
4510
4510
  init_src();
4511
- RawAccounts = object({
4512
- accounts: array(object({
4513
- accountId: number2(),
4514
- accountNo: string2().nullish(),
4515
- meterNo: string2().nullish(),
4516
- status: string2().nullish(),
4517
- linkStatus: string2().nullish()
4518
- }))
4519
- });
4520
- RawBalance = object({
4521
- balance: number2().nullish(),
4522
- currency: string2().nullish(),
4523
- balanceSnapshotAt: string2().nullish(),
4524
- timestampAvailable: boolean2().optional(),
4525
- stale: boolean2().optional(),
4526
- currentMonthConsumedTaka: number2().nullish(),
4527
- readingTime: string2().nullish(),
4528
- consumerMessages: array(unknown()).optional()
4529
- });
4530
- RawRealtime = object({
4531
- enabled: boolean2().optional(),
4532
- meters: array(object({
4533
- accountId: number2().nullish(),
4534
- meterNo: string2().nullish(),
4535
- meterType: string2().nullish(),
4536
- status: string2().nullish(),
4537
- message: string2().nullish(),
4538
- lastRealtimeBalance: number2().nullish(),
4539
- lastRealtimeReceivedAt: string2().nullish(),
4540
- nextEligibleAt: string2().nullish(),
4541
- attemptsToday: number2().nullish(),
4542
- remainingAttempts: number2().nullish()
4543
- }))
4544
- });
4545
4511
  AccountsOut = object({
4546
4512
  accounts: array(object({
4547
4513
  accountId: number2(),
@@ -4577,14 +4543,66 @@ var init_schemas3 = __esm(() => {
4577
4543
  remainingAttempts: number2().nullable()
4578
4544
  }))
4579
4545
  });
4546
+ DataOut = object({ data: unknown() });
4580
4547
  });
4581
4548
 
4582
- // ../../connectors/desco/src/index.ts
4583
- var exports_src = {};
4584
- __export(exports_src, {
4585
- default: () => src_default,
4586
- desco: () => desco
4549
+ // ../../connectors/desco/src/schemas/raw.ts
4550
+ var RawAccounts, RawBalance, RawRealtime;
4551
+ var init_raw = __esm(() => {
4552
+ init_src();
4553
+ RawAccounts = object({
4554
+ accounts: array(object({
4555
+ accountId: number2(),
4556
+ accountNo: string2().nullish(),
4557
+ meterNo: string2().nullish(),
4558
+ status: string2().nullish(),
4559
+ linkStatus: string2().nullish()
4560
+ }))
4561
+ });
4562
+ RawBalance = object({
4563
+ balance: number2().nullish(),
4564
+ currency: string2().nullish(),
4565
+ balanceSnapshotAt: string2().nullish(),
4566
+ timestampAvailable: boolean2().optional(),
4567
+ stale: boolean2().optional(),
4568
+ currentMonthConsumedTaka: number2().nullish(),
4569
+ readingTime: string2().nullish(),
4570
+ consumerMessages: array(unknown()).optional()
4571
+ });
4572
+ RawRealtime = object({
4573
+ enabled: boolean2().optional(),
4574
+ meters: array(object({
4575
+ accountId: number2().nullish(),
4576
+ meterNo: string2().nullish(),
4577
+ meterType: string2().nullish(),
4578
+ status: string2().nullish(),
4579
+ message: string2().nullish(),
4580
+ lastRealtimeBalance: number2().nullish(),
4581
+ lastRealtimeReceivedAt: string2().nullish(),
4582
+ nextEligibleAt: string2().nullish(),
4583
+ attemptsToday: number2().nullish(),
4584
+ remainingAttempts: number2().nullish()
4585
+ }))
4586
+ });
4587
+ });
4588
+
4589
+ // ../../connectors/desco/src/schemas/index.ts
4590
+ var init_schemas3 = __esm(() => {
4591
+ init_out();
4592
+ init_raw();
4587
4593
  });
4594
+
4595
+ // ../../connectors/desco/src/client/accounts.ts
4596
+ async function listAccounts(ctx) {
4597
+ const raw = await ctx.http.json("/accounts", RawAccounts);
4598
+ return raw.accounts.map((account) => ({
4599
+ accountId: account.accountId,
4600
+ accountNoLast4: maskLast4(account.accountNo),
4601
+ meterNoLast4: maskLast4(account.meterNo),
4602
+ status: account.status ?? null,
4603
+ linkStatus: account.linkStatus ?? null
4604
+ }));
4605
+ }
4588
4606
  async function resolveAccountId(ctx) {
4589
4607
  const accounts = await listAccounts(ctx);
4590
4608
  const only = accounts[0];
@@ -4597,92 +4615,294 @@ async function resolveAccountId(ctx) {
4597
4615
  }
4598
4616
  return only.accountId;
4599
4617
  }
4600
- var listAccounts = async (ctx) => {
4601
- const raw = await ctx.http.json("/accounts", RawAccounts);
4602
- return raw.accounts.map((account) => ({
4603
- accountId: account.accountId,
4604
- accountNoLast4: maskLast4(account.accountNo),
4605
- meterNoLast4: maskLast4(account.meterNo),
4606
- status: account.status ?? null,
4607
- linkStatus: account.linkStatus ?? null
4608
- }));
4609
- }, desco, src_default;
4610
- var init_src2 = __esm(() => {
4618
+ var init_accounts = __esm(() => {
4619
+ init_src();
4620
+ init_schemas3();
4621
+ });
4622
+
4623
+ // ../../connectors/desco/src/client/query.ts
4624
+ function withQuery(path, query) {
4625
+ const params = new URLSearchParams;
4626
+ for (const [key, value] of Object.entries(query)) {
4627
+ if (value !== undefined && value !== "") {
4628
+ params.set(key, String(value));
4629
+ }
4630
+ }
4631
+ const text = params.toString();
4632
+ return text ? `${path}?${text}` : path;
4633
+ }
4634
+
4635
+ // ../../connectors/desco/src/client/redact.ts
4636
+ function redact(value) {
4637
+ if (Array.isArray(value)) {
4638
+ return value.map(redact);
4639
+ }
4640
+ if (value !== null && typeof value === "object") {
4641
+ return Object.fromEntries(Object.entries(value).map(([key, v]) => [
4642
+ key,
4643
+ MASKED_KEYS.has(key) && typeof v === "string" ? maskLast4(v) : redact(v)
4644
+ ]));
4645
+ }
4646
+ return value;
4647
+ }
4648
+ var MASKED_KEYS;
4649
+ var init_redact = __esm(() => {
4650
+ init_src();
4651
+ MASKED_KEYS = new Set(["accountNo", "meterNo", "consumerNo"]);
4652
+ });
4653
+
4654
+ // ../../connectors/desco/src/client/index.ts
4655
+ var init_client = __esm(() => {
4656
+ init_accounts();
4657
+ init_redact();
4658
+ });
4659
+
4660
+ // ../../connectors/desco/src/tools/factories.ts
4661
+ var Loose, accountIdInput, dateInput, monthInput, publicGet = (name, description, path) => defineTool({
4662
+ name,
4663
+ description,
4664
+ access: "read",
4665
+ input: object({}),
4666
+ output: DataOut,
4667
+ handler: async (_input, ctx) => ({ data: await ctx.http.json(path, Loose) })
4668
+ }), publicSearch = (name, description, path, shape) => defineTool({
4669
+ name,
4670
+ description,
4671
+ access: "read",
4672
+ input: object({
4673
+ ...shape,
4674
+ page: number2().int().min(0).optional().describe("Zero-based page number."),
4675
+ size: number2().int().min(1).max(50).optional().describe("Results per page.")
4676
+ }),
4677
+ output: DataOut,
4678
+ handler: async (input, ctx) => ({
4679
+ data: await ctx.http.json(withQuery(path, input), Loose)
4680
+ })
4681
+ }), accountGet = (name, description, suffix, shape = {}) => defineTool({
4682
+ name,
4683
+ description,
4684
+ access: "read",
4685
+ input: object({ accountId: accountIdInput, ...shape }),
4686
+ output: DataOut,
4687
+ handler: async (input, ctx) => {
4688
+ const { accountId: given, ...query } = input;
4689
+ const accountId = given ?? await resolveAccountId(ctx);
4690
+ const path = withQuery(`/accounts/${accountId}${suffix}`, query);
4691
+ return { data: redact(await ctx.http.json(path, Loose)) };
4692
+ }
4693
+ });
4694
+ var init_factories = __esm(() => {
4611
4695
  init_src();
4696
+ init_client();
4612
4697
  init_schemas3();
4698
+ Loose = unknown();
4699
+ accountIdInput = number2().int().positive().optional().describe("Internal accountId from desco_list_accounts. Omit if only one account is linked.");
4700
+ dateInput = string2().regex(/^\d{4}-\d{2}-\d{2}$/, "Use YYYY-MM-DD").optional();
4701
+ monthInput = string2().regex(/^\d{4}-\d{2}$/, "Use YYYY-MM").optional();
4702
+ });
4703
+
4704
+ // ../../connectors/desco/src/tools/accounts.ts
4705
+ var listAccountsTool, getBalance, getRealtimeStatus, accountTools;
4706
+ var init_accounts2 = __esm(() => {
4707
+ init_src();
4708
+ init_client();
4709
+ init_schemas3();
4710
+ init_factories();
4711
+ listAccountsTool = defineTool({
4712
+ name: "list_accounts",
4713
+ description: "List the DESCO prepaid accounts linked to the signed-in user, with account IDs. Use first when the user has not given an accountId.",
4714
+ access: "read",
4715
+ input: object({}),
4716
+ output: AccountsOut,
4717
+ handler: async (_input, ctx) => ({ accounts: await listAccounts(ctx) })
4718
+ });
4719
+ getBalance = defineTool({
4720
+ name: "get_balance",
4721
+ description: "Get the daily prepaid electricity balance in BDT for a DESCO account. Use when asked about meter balance or remaining credit. Omit accountId if only one account is linked.",
4722
+ access: "read",
4723
+ input: object({ accountId: accountIdInput }),
4724
+ output: BalanceOut,
4725
+ handler: async (input, ctx) => {
4726
+ const accountId = input.accountId ?? await resolveAccountId(ctx);
4727
+ const raw = await ctx.http.json(`/accounts/${accountId}/balance`, RawBalance);
4728
+ const balance = raw.balance ?? null;
4729
+ const timestampAvailable = raw.timestampAvailable !== false;
4730
+ const unavailable = balance === null || !timestampAvailable;
4731
+ return {
4732
+ balance,
4733
+ currency: raw.currency ?? null,
4734
+ balanceSnapshotAt: raw.balanceSnapshotAt ?? null,
4735
+ timestampAvailable,
4736
+ stale: raw.stale === true,
4737
+ currentMonthConsumedTaka: raw.currentMonthConsumedTaka ?? null,
4738
+ readingTime: raw.readingTime ?? null,
4739
+ consumerMessages: raw.consumerMessages ?? [],
4740
+ note: unavailable ? "The balance is unavailable right now. Do not treat it as zero." : "This is a daily snapshot, not a live meter reading. A recent recharge may not appear until the next update."
4741
+ };
4742
+ }
4743
+ });
4744
+ getRealtimeStatus = defineTool({
4745
+ name: "get_realtime_status",
4746
+ description: "Read the last realtime-reading result and whether a new reading is allowed, per meter. Use to explain realtime limits. It does not request a new reading.",
4747
+ access: "read",
4748
+ input: object({}),
4749
+ output: RealtimeOut,
4750
+ handler: async (_input, ctx) => {
4751
+ const raw = await ctx.http.json("/accounts/realtime-balance", RawRealtime);
4752
+ return {
4753
+ enabled: raw.enabled === true,
4754
+ meters: raw.meters.map((meter) => ({
4755
+ accountId: meter.accountId ?? null,
4756
+ meterNoLast4: maskLast4(meter.meterNo),
4757
+ meterType: meter.meterType ?? null,
4758
+ status: meter.status ?? null,
4759
+ message: meter.message ?? null,
4760
+ lastRealtimeBalance: meter.lastRealtimeBalance ?? null,
4761
+ lastRealtimeReceivedAt: meter.lastRealtimeReceivedAt ?? null,
4762
+ nextEligibleAt: meter.nextEligibleAt ?? null,
4763
+ attemptsToday: meter.attemptsToday ?? null,
4764
+ remainingAttempts: meter.remainingAttempts ?? null
4765
+ }))
4766
+ };
4767
+ }
4768
+ });
4769
+ accountTools = [listAccountsTool, getBalance, getRealtimeStatus];
4770
+ });
4771
+
4772
+ // ../../connectors/desco/src/tools/billing.ts
4773
+ var billingTools;
4774
+ var init_billing = __esm(() => {
4775
+ init_factories();
4776
+ billingTools = [
4777
+ accountGet("get_recharge_history", "Recharge transactions for a date range (YYYY-MM-DD).", "/transactions", {
4778
+ dateFrom: dateInput,
4779
+ dateTo: dateInput
4780
+ }),
4781
+ accountGet("get_token_history", "Recharge tokens issued for a date range (YYYY-MM-DD). Tokens are credentials for the meter, so share them only with the account owner.", "/tokens", { dateFrom: dateInput, dateTo: dateInput }),
4782
+ accountGet("get_token_resend_eligibility", "Whether a recharge token can be re-sent for this account. Does not send anything.", "/token-resend/eligibility")
4783
+ ];
4784
+ });
4785
+
4786
+ // ../../connectors/desco/src/tools/directory.ts
4787
+ var directoryTools;
4788
+ var init_directory = __esm(() => {
4789
+ init_src();
4790
+ init_factories();
4791
+ directoryTools = [
4792
+ publicSearch("find_recharge_points", "Search DESCO prepaid recharge points (POS shops) by text, bank or area. Bank and area values must match the portal exactly, for example bank 'MYCash', area 'Mirpur'.", "/public/pos-locations", {
4793
+ q: string2().optional().describe("Free-text search on shop name or address."),
4794
+ bank: string2().optional(),
4795
+ area: string2().optional()
4796
+ }),
4797
+ publicGet("list_recharge_point_filters", "Valid bank and area values for find_recharge_points.", "/public/pos-locations/filters"),
4798
+ publicSearch("find_meter_manuals", "Search prepaid meter user manuals (PDF) by manufacturer, phase (SINGLE or THREE) and language.", "/public/meter-manuals", {
4799
+ q: string2().optional(),
4800
+ manufacturer: string2().optional(),
4801
+ phase: _enum(["SINGLE", "THREE"]).optional(),
4802
+ locale: string2().optional()
4803
+ }),
4804
+ publicSearch("find_tutorials", "Search DESCO prepaid video tutorials by text, category and language.", "/public/tutorials", {
4805
+ q: string2().optional(),
4806
+ category: string2().optional(),
4807
+ locale: string2().optional()
4808
+ })
4809
+ ];
4810
+ });
4811
+
4812
+ // ../../connectors/desco/src/tools/info.ts
4813
+ var listNotices, infoTools;
4814
+ var init_info = __esm(() => {
4815
+ init_src();
4816
+ init_schemas3();
4817
+ init_factories();
4818
+ listNotices = defineTool({
4819
+ name: "list_notices",
4820
+ description: "Active DESCO portal notices, or the important homepage notices (monthly charges, recharge deductions).",
4821
+ access: "read",
4822
+ input: object({ kind: _enum(["active", "homepage"]).default("active") }),
4823
+ output: DataOut,
4824
+ handler: async (input, ctx) => ({ data: await ctx.http.json(`/public/notices/${input.kind}`, unknown()) })
4825
+ });
4826
+ infoTools = [
4827
+ publicGet("get_tariff", "Current DESCO retail electricity tariff: unit slabs, rates per kWh and demand charges by customer class. Use for price-per-unit questions.", "/public/tariff-schedules/current"),
4828
+ listNotices,
4829
+ publicGet("list_faqs", "DESCO prepaid FAQ in Bangla and English.", "/public/faqs"),
4830
+ publicGet("list_short_codes", "Prepaid meter keypad short codes (e.g. 801 shows balance), grouped by purpose, with meter and phase applicability.", "/public/prepaid-short-codes"),
4831
+ publicGet("list_tariff_documents", "Current tariff orders and circulars published by DESCO, with titles and summaries.", "/public/documents/tariffs/current"),
4832
+ publicGet("list_documents", "All published DESCO tariff documents, forms, circulars and policies.", "/public/documents")
4833
+ ];
4834
+ });
4835
+
4836
+ // ../../connectors/desco/src/tools/profile.ts
4837
+ var profileTools;
4838
+ var init_profile = __esm(() => {
4839
+ init_factories();
4840
+ profileTools = [
4841
+ accountGet("get_account_summary", "Summary of a DESCO prepaid account: customer, meter and connection details.", "/summary"),
4842
+ accountGet("get_contact_office", "The DESCO office that serves this account: name, address and phone.", "/contact-office")
4843
+ ];
4844
+ });
4845
+
4846
+ // ../../connectors/desco/src/tools/usage.ts
4847
+ var usageTools;
4848
+ var init_usage = __esm(() => {
4849
+ init_src();
4850
+ init_factories();
4851
+ usageTools = [
4852
+ accountGet("get_daily_usage", "Daily electricity usage for a date range. Dates are YYYY-MM-DD.", "/usage/daily", {
4853
+ dateFrom: dateInput,
4854
+ dateTo: dateInput
4855
+ }),
4856
+ accountGet("get_monthly_usage", "Monthly electricity usage for a month range. Months are YYYY-MM.", "/usage/monthly", {
4857
+ monthFrom: monthInput,
4858
+ monthTo: monthInput
4859
+ }),
4860
+ accountGet("get_consumption_comparison", "Compare monthly consumption this year against last year, up to an end month.", "/consumption-comparison", { year: number2().int().min(2000).max(2100).optional(), endMonth: number2().int().min(1).max(12).optional() }),
4861
+ accountGet("get_energy_vs_consumed_taka", "Energy (kWh) versus money consumed (BDT) per month. Months are YYYY-MM.", "/energy-vs-consumed-taka", { monthFrom: monthInput, monthTo: monthInput })
4862
+ ];
4863
+ });
4864
+
4865
+ // ../../connectors/desco/src/tools/index.ts
4866
+ var tools;
4867
+ var init_tools = __esm(() => {
4868
+ init_accounts2();
4869
+ init_billing();
4870
+ init_directory();
4871
+ init_info();
4872
+ init_profile();
4873
+ init_usage();
4874
+ tools = [
4875
+ ...accountTools,
4876
+ ...profileTools,
4877
+ ...usageTools,
4878
+ ...billingTools,
4879
+ ...infoTools,
4880
+ ...directoryTools
4881
+ ];
4882
+ });
4883
+
4884
+ // ../../connectors/desco/src/index.ts
4885
+ var exports_src = {};
4886
+ __export(exports_src, {
4887
+ default: () => src_default,
4888
+ desco: () => desco
4889
+ });
4890
+ var desco, src_default;
4891
+ var init_src2 = __esm(() => {
4892
+ init_src();
4893
+ init_tools();
4613
4894
  desco = defineConnector({
4614
4895
  id: "desco",
4615
- version: "0.2.0",
4896
+ version: "0.3.0",
4616
4897
  status: "beta",
4617
- description: "DESCO prepaid electricity: linked accounts and balance (unofficial)",
4898
+ description: "DESCO prepaid electricity: accounts, balance, usage, recharges, tariffs, notices and recharge points (unofficial)",
4618
4899
  baseUrl: "https://prepaid.desco.org.bd/api",
4619
4900
  auth: formLogin({
4620
4901
  csrfPath: "/auth/csrf",
4621
4902
  loginPath: "/auth/login",
4622
4903
  secrets: { username: "DESCO_USERNAME", password: "DESCO_PASSWORD" }
4623
4904
  }),
4624
- tools: [
4625
- defineTool({
4626
- name: "list_accounts",
4627
- description: "List the DESCO prepaid accounts linked to the signed-in user, with account IDs. Use first when the user has not given an accountId.",
4628
- access: "read",
4629
- input: object({}),
4630
- output: AccountsOut,
4631
- handler: async (_input, ctx) => ({ accounts: await listAccounts(ctx) })
4632
- }),
4633
- defineTool({
4634
- name: "get_balance",
4635
- description: "Get the daily prepaid electricity balance in BDT for a DESCO account. Use when asked about meter balance or remaining credit. Omit accountId if only one account is linked.",
4636
- access: "read",
4637
- input: object({
4638
- accountId: number2().int().positive().optional().describe("Internal accountId from list_accounts, not the account or meter number.")
4639
- }),
4640
- output: BalanceOut,
4641
- handler: async (input, ctx) => {
4642
- const accountId = input.accountId ?? await resolveAccountId(ctx);
4643
- const raw = await ctx.http.json(`/accounts/${accountId}/balance`, RawBalance);
4644
- const balance = raw.balance ?? null;
4645
- const timestampAvailable = raw.timestampAvailable !== false;
4646
- const unavailable = balance === null || !timestampAvailable;
4647
- return {
4648
- balance,
4649
- currency: raw.currency ?? null,
4650
- balanceSnapshotAt: raw.balanceSnapshotAt ?? null,
4651
- timestampAvailable,
4652
- stale: raw.stale === true,
4653
- currentMonthConsumedTaka: raw.currentMonthConsumedTaka ?? null,
4654
- readingTime: raw.readingTime ?? null,
4655
- consumerMessages: raw.consumerMessages ?? [],
4656
- note: unavailable ? "The balance is unavailable right now. Do not treat it as zero." : "This is a daily snapshot, not a live meter reading. A recent recharge may not appear until the next update."
4657
- };
4658
- }
4659
- }),
4660
- defineTool({
4661
- name: "get_realtime_status",
4662
- description: "Read the last realtime-reading result and whether a new reading is allowed, per meter. Use to explain realtime limits. It does not request a new reading.",
4663
- access: "read",
4664
- input: object({}),
4665
- output: RealtimeOut,
4666
- handler: async (_input, ctx) => {
4667
- const raw = await ctx.http.json("/accounts/realtime-balance", RawRealtime);
4668
- return {
4669
- enabled: raw.enabled === true,
4670
- meters: raw.meters.map((meter) => ({
4671
- accountId: meter.accountId ?? null,
4672
- meterNoLast4: maskLast4(meter.meterNo),
4673
- meterType: meter.meterType ?? null,
4674
- status: meter.status ?? null,
4675
- message: meter.message ?? null,
4676
- lastRealtimeBalance: meter.lastRealtimeBalance ?? null,
4677
- lastRealtimeReceivedAt: meter.lastRealtimeReceivedAt ?? null,
4678
- nextEligibleAt: meter.nextEligibleAt ?? null,
4679
- attemptsToday: meter.attemptsToday ?? null,
4680
- remainingAttempts: meter.remainingAttempts ?? null
4681
- }))
4682
- };
4683
- }
4684
- })
4685
- ]
4905
+ tools
4686
4906
  });
4687
4907
  src_default = desco;
4688
4908
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bixbd/utils-mcp",
3
- "version": "0.1.0-beta.5",
3
+ "version": "0.1.0-beta.6",
4
4
  "description": "Connect utility portals to AI agents through MCP. Works with any MCP app via npx; credentials stay on your machine.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -47,8 +47,8 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@bixbd/utils-mcp-connector-example": "0.0.0",
50
- "@bixbd/utils-mcp-connector-desco": "0.1.0-beta.5",
51
- "@bixbd/utils-mcp-core": "0.1.0-beta.5",
50
+ "@bixbd/utils-mcp-connector-desco": "0.1.0-beta.6",
51
+ "@bixbd/utils-mcp-core": "0.1.0-beta.6",
52
52
  "@modelcontextprotocol/sdk": "^1.0.0"
53
53
  }
54
54
  }