jpdcl 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.env.example +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +219 -0
  4. package/dist/catalog.d.ts +141 -0
  5. package/dist/catalog.js +261 -0
  6. package/dist/catalog.js.map +1 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +395 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +6 -0
  11. package/dist/config.js +13 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/credentials.d.ts +13 -0
  14. package/dist/credentials.js +100 -0
  15. package/dist/credentials.js.map +1 -0
  16. package/dist/crypto.d.ts +3 -0
  17. package/dist/crypto.js +21 -0
  18. package/dist/crypto.js.map +1 -0
  19. package/dist/dates.d.ts +5 -0
  20. package/dist/dates.js +20 -0
  21. package/dist/dates.js.map +1 -0
  22. package/dist/errors.d.ts +11 -0
  23. package/dist/errors.js +23 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/http-handler.d.ts +1 -0
  26. package/dist/http-handler.js +46 -0
  27. package/dist/http-handler.js.map +1 -0
  28. package/dist/http.d.ts +9 -0
  29. package/dist/http.js +43 -0
  30. package/dist/http.js.map +1 -0
  31. package/dist/index.d.ts +13 -0
  32. package/dist/index.js +14 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/launcher.d.ts +2 -0
  35. package/dist/launcher.js +11 -0
  36. package/dist/launcher.js.map +1 -0
  37. package/dist/ledger-client.d.ts +112 -0
  38. package/dist/ledger-client.js +221 -0
  39. package/dist/ledger-client.js.map +1 -0
  40. package/dist/main-client.d.ts +25 -0
  41. package/dist/main-client.js +221 -0
  42. package/dist/main-client.js.map +1 -0
  43. package/dist/mcp-stdio.d.ts +2 -0
  44. package/dist/mcp-stdio.js +6 -0
  45. package/dist/mcp-stdio.js.map +1 -0
  46. package/dist/mcp.d.ts +8 -0
  47. package/dist/mcp.js +517 -0
  48. package/dist/mcp.js.map +1 -0
  49. package/dist/runtime.d.ts +27 -0
  50. package/dist/runtime.js +316 -0
  51. package/dist/runtime.js.map +1 -0
  52. package/dist/server.d.ts +4 -0
  53. package/dist/server.js +221 -0
  54. package/dist/server.js.map +1 -0
  55. package/dist/session.d.ts +4 -0
  56. package/dist/session.js +30 -0
  57. package/dist/session.js.map +1 -0
  58. package/dist/smart-client.d.ts +50 -0
  59. package/dist/smart-client.js +238 -0
  60. package/dist/smart-client.js.map +1 -0
  61. package/dist/tariff.d.ts +121 -0
  62. package/dist/tariff.js +124 -0
  63. package/dist/tariff.js.map +1 -0
  64. package/dist/types.d.ts +51 -0
  65. package/dist/types.js +2 -0
  66. package/dist/types.js.map +1 -0
  67. package/dist/worker.d.ts +5 -0
  68. package/dist/worker.js +3 -0
  69. package/dist/worker.js.map +1 -0
  70. package/openapi.yaml +284 -0
  71. package/package.json +89 -0
  72. package/scripts/audit-http.ts +36 -0
  73. package/scripts/audit-launcher.mjs +28 -0
  74. package/scripts/audit-ledger-portal.ts +23 -0
  75. package/scripts/audit-mcp.mjs +140 -0
  76. package/scripts/audit-smart-portal.mjs +63 -0
  77. package/scripts/build.mjs +19 -0
  78. package/scripts/publish-smithery.mjs +47 -0
  79. package/src/catalog.ts +291 -0
  80. package/src/cli.ts +397 -0
  81. package/src/config.ts +15 -0
  82. package/src/credentials.ts +114 -0
  83. package/src/crypto.ts +21 -0
  84. package/src/dates.ts +19 -0
  85. package/src/errors.ts +26 -0
  86. package/src/http-handler.ts +48 -0
  87. package/src/http.ts +49 -0
  88. package/src/index.ts +13 -0
  89. package/src/launcher.ts +9 -0
  90. package/src/ledger-client.ts +258 -0
  91. package/src/main-client.ts +230 -0
  92. package/src/mcp-stdio.ts +6 -0
  93. package/src/mcp.ts +546 -0
  94. package/src/runtime.ts +329 -0
  95. package/src/server.ts +218 -0
  96. package/src/session.ts +29 -0
  97. package/src/smart-client.ts +249 -0
  98. package/src/tariff.ts +148 -0
  99. package/src/toolkit.test.ts +136 -0
  100. package/src/types.ts +51 -0
  101. package/src/worker.ts +3 -0
  102. package/tsconfig.build.json +11 -0
  103. package/tsconfig.json +16 -0
@@ -0,0 +1,19 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+
6
+ const root = process.cwd();
7
+ const output = path.join(root, "dist");
8
+ fs.rmSync(output, { recursive: true, force: true });
9
+
10
+ const compiler = path.join(root, "node_modules", "typescript", "bin", "tsc");
11
+ const result = spawnSync(process.execPath, [compiler, "-p", "tsconfig.build.json"], {
12
+ cwd: root,
13
+ stdio: "inherit",
14
+ });
15
+ if (result.status !== 0) process.exit(result.status ?? 1);
16
+
17
+ for (const name of ["launcher.js", "cli.js", "mcp-stdio.js", "server.js"]) {
18
+ try { fs.chmodSync(path.join(output, name), 0o755); } catch { /* Windows/npm shims do not need chmod. */ }
19
+ }
@@ -0,0 +1,47 @@
1
+ import { spawnSync } from "node:child_process";
2
+
3
+ const mcpUrl = process.env.SMITHERY_MCP_URL;
4
+ const serverName = process.env.SMITHERY_SERVER_NAME;
5
+ if (!mcpUrl) throw new Error("SMITHERY_MCP_URL is required");
6
+ if (!serverName) throw new Error("SMITHERY_SERVER_NAME must be namespace/server");
7
+
8
+ const configSchema = {
9
+ type: "object",
10
+ properties: {
11
+ loginId: {
12
+ type: "string",
13
+ title: "JPDCL login ID",
14
+ description: "Jammu Power Distribution Corporation Limited portal mobile number or email",
15
+ "x-from": { header: "x-jpdcl-login-id" },
16
+ },
17
+ password: {
18
+ type: "string",
19
+ title: "JPDCL password",
20
+ description: "Jammu Power Distribution Corporation Limited portal password",
21
+ format: "password",
22
+ "x-from": { header: "x-jpdcl-password" },
23
+ },
24
+ allowMutations: {
25
+ type: "boolean",
26
+ title: "Allow account changes",
27
+ description: "Keep disabled unless account-changing tools are intentionally needed",
28
+ default: false,
29
+ "x-from": { query: "allowMutations" },
30
+ },
31
+ },
32
+ required: ["loginId", "password"],
33
+ additionalProperties: false,
34
+ };
35
+
36
+ const result = spawnSync("npx", [
37
+ "-y",
38
+ "@smithery/cli@latest",
39
+ "mcp",
40
+ "publish",
41
+ mcpUrl,
42
+ "--name",
43
+ serverName,
44
+ "--config-schema",
45
+ JSON.stringify(configSchema),
46
+ ], { stdio: "inherit" });
47
+ if (result.status !== 0) process.exit(result.status ?? 1);
package/src/catalog.ts ADDED
@@ -0,0 +1,291 @@
1
+ import type { EndpointDefinition } from "./types.js";
2
+
3
+ const main = (
4
+ path: string,
5
+ description: string,
6
+ options: Partial<EndpointDefinition> = {},
7
+ ): EndpointDefinition => ({
8
+ portal: "main",
9
+ method: "POST",
10
+ path,
11
+ encrypted: true,
12
+ auth: "main-session",
13
+ dataClass: "observed",
14
+ description,
15
+ ...options,
16
+ });
17
+
18
+ const smart = (
19
+ method: EndpointDefinition["method"],
20
+ path: string,
21
+ description: string,
22
+ options: Partial<EndpointDefinition> = {},
23
+ ): EndpointDefinition => ({
24
+ portal: "smart",
25
+ method,
26
+ path,
27
+ auth: "smart-bearer",
28
+ dataClass: "observed",
29
+ description,
30
+ ...options,
31
+ });
32
+
33
+ const ledger = (
34
+ path: string,
35
+ description: string,
36
+ options: Partial<EndpointDefinition> = {},
37
+ ): EndpointDefinition => ({
38
+ portal: "ledger",
39
+ method: "POST",
40
+ path,
41
+ encrypted: false,
42
+ auth: "public",
43
+ dataClass: "observed",
44
+ description,
45
+ ...options,
46
+ });
47
+
48
+ export const endpointCatalog = {
49
+ // JPDCL WSS authentication and account management
50
+ main_login: main("/userLoginWebNew", "Consumer login; credentials are supplied in a dedicated header", {
51
+ method: "GET",
52
+ auth: "public-basic",
53
+ encrypted: false,
54
+ }),
55
+ main_department_login: main("/departmentLogin", "Corporate/department login", {
56
+ auth: "public-basic",
57
+ encrypted: false,
58
+ bodyExample: { userid: "USER", password: "PASSWORD" },
59
+ }),
60
+ main_validate_password: main("/validatePassword", "Validate an existing password", {
61
+ mutation: false,
62
+ }),
63
+ main_reset_password: main("/resetPasswordNew", "Reset an authenticated user's password", {
64
+ mutation: true,
65
+ }),
66
+ main_forgot_password: main("/forgetPasswordNew", "Forgot-password OTP and reset workflow", {
67
+ auth: "public-basic",
68
+ mutation: true,
69
+ }),
70
+ main_register: main("/userRegistrationNew", "Consumer registration and OTP workflow", {
71
+ auth: "public-basic",
72
+ mutation: true,
73
+ }),
74
+ main_visitor_count: main("/visitorCount", "Read/update portal visitor count", {
75
+ auth: "public-basic",
76
+ bodyExample: { visitor_type: "web" },
77
+ }),
78
+ main_customer_info: main("/GetCustomerInfo", "Complete consumer, account, bill, and meter summary", {
79
+ auth: "public-basic",
80
+ bodyExample: { accountid: "0000000000" },
81
+ }),
82
+ main_accounts_for_identity: main("/getAccountsByEmailMobile", "Accounts registered to an email or mobile number"),
83
+ main_add_primary_account: main("/addPrimaryAccount", "Attach a primary account to a login", {
84
+ auth: "public-basic",
85
+ mutation: true,
86
+ bodyExample: { loginid: "...", accountid: "...", consumercode: "..." },
87
+ }),
88
+ main_linked_accounts: main("/getLinkedAccountsNew", "List linked consumer accounts", {
89
+ bodyExample: { loginid: "..." },
90
+ }),
91
+ main_link_account: main("/linkAccountNew", "Link a child account", {
92
+ mutation: true,
93
+ bodyExample: {
94
+ loginid: "...",
95
+ parent_acc_id: "...",
96
+ child_acc_id: "...",
97
+ child_consumer_code: "...",
98
+ },
99
+ }),
100
+ main_swap_linked_account: main("/swapLinkedAccount", "Make a linked account primary", {
101
+ mutation: true,
102
+ }),
103
+ main_delink_account: main("/delinkAccountNew", "Remove a linked account", { mutation: true }),
104
+ main_update_contact: main("/updateContectDetails", "Update mobile number or email", {
105
+ mutation: true,
106
+ }),
107
+ main_alert_settings: main("/alertSettings", "Read or update alert settings", { mutation: true }),
108
+
109
+ // Bills, payments, consumption, and meters
110
+ main_bill_history: main("/BillPaymentHist", "Bill or payment history for a date range", {
111
+ bodyExample: { acct_id: "...", type: "BILL", st_dt: "2026-01-01", en_dt: "2026-07-31" },
112
+ }),
113
+ main_bill_pdf: main("/BillPDF", "Download an electricity bill as encoded PDF data", {
114
+ binary: true,
115
+ bodyExample: { bill_id: "..." },
116
+ }),
117
+ main_consumption: main("/GetBillConsumption", "Meter/register consumption history", {
118
+ bodyExample: { accountid: "...", fromdate: "2026-01-01", todate: "2026-07-31" },
119
+ }),
120
+ main_consumption_legacy: main("/ConsumptionHist", "Legacy consumption history endpoint"),
121
+ main_meter_changes: main("/GetMeterChangedHistory", "Meter replacement/change history"),
122
+ main_meter_status: main("/meterStatus", "Live prepaid meter connection status", {
123
+ bodyExample: { account_id: "..." },
124
+ }),
125
+ main_prepaid_transactions: main("/prepaidChargePayment", "Prepaid charges, payments, and recharges", {
126
+ bodyExample: {
127
+ account_id: "...",
128
+ transaction_type: "CHARGE",
129
+ fromdate: "01-07-2026",
130
+ todate: "24-07-2026",
131
+ },
132
+ }),
133
+ main_prepaid_bill_history: main("/getPrepaidBillHistory", "Prepaid billing-segment history"),
134
+ main_prepaid_bill_distribution: main("/getBillDistribution", "Detailed prepaid bill breakup", {
135
+ bodyExample: { bseg_id: "..." },
136
+ }),
137
+ main_prepaid_statement_date: main("/PrepaidStatementPDFByDate", "Prepaid PDF statement by date range", {
138
+ binary: true,
139
+ }),
140
+ main_prepaid_statement_month: main("/PrepaidStatementPDFByMonth", "Prepaid PDF statement by month", {
141
+ binary: true,
142
+ }),
143
+ main_same_day_transaction: main("/CheckSameDayTransaction", "Check for an existing same-day payment", {
144
+ auth: "public-basic",
145
+ }),
146
+ main_initiate_payment: main("/InitiateBillPayRequest", "Create a BillDesk payment intent", {
147
+ auth: "public-basic",
148
+ mutation: true,
149
+ }),
150
+ main_cancel_payment_remark: main("/updateRemarkOnPaymentCancel", "Record a cancelled payment attempt", {
151
+ auth: "public-basic",
152
+ mutation: true,
153
+ }),
154
+ main_amnesty_info: main("/getAMNESTYConsumerInfo", "Amnesty-scheme consumer information", {
155
+ auth: "public-basic",
156
+ }),
157
+
158
+ // Complaints, contact, corporate/group billing, and diagnostics
159
+ main_complaint_types: main("/getListOfComplaint", "Available technical and non-technical complaint types"),
160
+ main_complaints: main("/complaintStatus", "Complaint/service-request history and status"),
161
+ main_register_complaint: main("/registerComplaint", "Register a complaint or service request", {
162
+ mutation: true,
163
+ }),
164
+ main_contact: main("/contactus", "Submit a contact-us message", {
165
+ auth: "public-basic",
166
+ mutation: true,
167
+ }),
168
+ main_group_info: main("/getGroupConsumerInfo", "Department/corporate consumer group information"),
169
+ main_group_hierarchy: main("/getHierarchy", "Department/corporate circle/division/subdivision hierarchy"),
170
+ main_group_payment: main("/InitiateGroupPayRequest", "Create a corporate group payment intent", {
171
+ mutation: true,
172
+ }),
173
+ main_group_payment_report: main("/GroupPaymentReport", "Corporate group payment report"),
174
+ main_smart_sso: main("/getGenusSSOToken", "Issue the Genus smart-meter portal SSO JWT", {
175
+ auth: "public-basic",
176
+ bodyExample: { accountId: "...", mtrno: "..." },
177
+ }),
178
+ main_generate_log: main("/generateLog", "Portal client diagnostic logging", {
179
+ auth: "public-basic",
180
+ mutation: true,
181
+ }),
182
+ // Current Genus consumer portal (cp.rdssjpdcl.com). The JPDCL SSO JWT is
183
+ // accepted directly as the bearer token; no browser or token exchange is needed.
184
+ smart_otp_send: smart("POST", "/Authentication/otp-login/send", "Send consumer-login OTP", { auth: "public", mutation: true }),
185
+ smart_otp_verify: smart("POST", "/Authentication/otp-login/verify", "Verify consumer-login OTP", { auth: "public", mutation: true }),
186
+ smart_guest_otp_send: smart("POST", "/Authentication/guest-login/send-otp", "Send guest-login OTP", { auth: "public", mutation: true }),
187
+ smart_guest_otp_verify: smart("POST", "/Authentication/guest-login/verify-otp", "Verify guest-login OTP", { auth: "public", mutation: true }),
188
+ smart_switch_account: smart("POST", "/Authentication/switch-account", "Switch the current smart-meter account and issue a refreshed token", { mutation: true }),
189
+ smart_logout: smart("POST", "/Authentication/logout", "Invalidate a smart-meter session", { mutation: true }),
190
+ smart_admin_login: smart("POST", "/AdminAuth/login", "Authenticate to the separately protected smart-portal administration area", { auth: "public" }),
191
+ smart_admin_login_counts: smart("GET", "/LoginHistory/login-counts", "Administrative consumer login-count report", { auth: "smart-admin" }),
192
+ smart_admin_bypass_link: smart("POST", "/consumerportal/api/ConsumerPortal/CreateToken", "Generate a consumer bypass URL from the administration API", { auth: "smart-admin", mutation: true, bodyExample: { region: "JPDCL", kno: "...", meterNumber: "..." } }),
193
+
194
+ // Consumption, readings, forecasts, balance, and alerts
195
+ smart_today_monthly: smart("GET", "/EnergyConsumption/todayormonthlyconsumption/{meterNumber}", "Today's and current-month consumption", { parameters: ["meterNumber"] }),
196
+ smart_current_month: smart("GET", "/EnergyConsumption/current-month/{accountId}", "Current-month energy consumption", { parameters: ["accountId"] }),
197
+ smart_last_month_bill: smart("GET", "/energyconsumption/last-month-bill/{accountId}", "Last-month bill through the energy service", { parameters: ["accountId"] }),
198
+ smart_meter_reading: smart("GET", "/energyconsumption/meter-reading/{meterNumber}", "Meter reading history and on-demand status", { parameters: ["meterNumber"] }),
199
+ smart_current_meter_reading: smart("GET", "/EnergyConsumption/current-meter-reading/{accountId}", "Latest smart-meter reading", { parameters: ["accountId"] }),
200
+ smart_prepaid_recharge_balance: smart("GET", "/energyconsumption/prepaid-recharge-balance/{accountId}", "Prepaid recharge and balance summary", { parameters: ["accountId"] }),
201
+ smart_prepaid_balance: smart("GET", "/EnergyConsumption/prepaid-balance-status/{meterNumber}", "Current prepaid balance and status", { parameters: ["meterNumber"] }),
202
+ smart_bill_summary: smart("GET", "/energyconsumption/bill-summary/{accountId}?fromDate={fromDate}&toDate={toDate}", "Bill summary for a date range", { parameters: ["accountId", "fromDate", "toDate"] }),
203
+ smart_insights: smart("GET", "/energyconsumption/insights/{accountId}", "Derived energy insights, comparisons, savings estimates, and smart tips; not raw meter evidence", { parameters: ["accountId"], dataClass: "derived" }),
204
+ smart_forecast_today: smart("GET", "/EnergyConsumption/forecast/today/{meterNumber}", "Predicted consumption for today; not a measured reading", { parameters: ["meterNumber"], dataClass: "derived" }),
205
+ smart_forecast_weekly: smart("GET", "/EnergyConsumption/forecast/weekly/{meterNumber}", "Predicted weekly consumption; not measured readings", { parameters: ["meterNumber"], dataClass: "derived" }),
206
+ smart_forecast_monthly: smart("GET", "/EnergyConsumption/forecast/monthly/{meterNumber}", "Predicted monthly consumption; not measured readings", { parameters: ["meterNumber"], dataClass: "derived" }),
207
+ smart_consumption_comparison: smart("GET", "/EnergyConsumption/consumption/{meterNumber}?type={type}&value={value}", "Daily, weekly, or monthly consumption comparison", { parameters: ["meterNumber", "type", "value"] }),
208
+ smart_consumption_30min: smart("GET", "/EnergyConsumption/30min-log/{meterNumber}?fromDate={fromDate}&toDate={toDate}&sortOrder={sortOrder}", "Half-hour interval import/export log", { parameters: ["meterNumber", "fromDate", "toDate", "sortOrder"] }),
209
+ smart_consumption_30min_csv: smart("GET", "/EnergyConsumption/30min-log/{meterNumber}/download/csv?fromDate={fromDate}&toDate={toDate}&sortOrder={sortOrder}", "Download half-hour interval log as CSV", { parameters: ["meterNumber", "fromDate", "toDate", "sortOrder"], binary: true }),
210
+ smart_consumption_30min_pdf: smart("GET", "/EnergyConsumption/30min-log/{meterNumber}/download/pdf?fromDate={fromDate}&toDate={toDate}&sortOrder={sortOrder}", "Download half-hour interval log as PDF", { parameters: ["meterNumber", "fromDate", "toDate", "sortOrder"], binary: true }),
211
+ smart_on_demand_request: smart("POST", "/EnergyConsumption/OnDemandRequest", "Request an immediate meter read", { mutation: true, bodyExample: { meterNumber: "..." } }),
212
+ smart_on_demand_logs: smart("GET", "/EnergyConsumption/instant-request-logs/{meterNumber}", "On-demand meter-read request history", { parameters: ["meterNumber"] }),
213
+ smart_my_alerts: smart("GET", "/preferences/my-alerts?kno={accountId}&meterNo={meterNumber}", "Live, daily, and monthly consumption alert snapshot", { parameters: ["accountId", "meterNumber"] }),
214
+
215
+ // Postpaid and prepaid histories
216
+ smart_postpaid_payment_history: smart("GET", "/postpaidbilling/payment-history/{accountId}", "Postpaid payment history", { parameters: ["accountId"] }),
217
+ smart_postpaid_bill_history: smart("GET", "/postpaidbilling/bill-history/{accountId}", "Postpaid bill history", { parameters: ["accountId"] }),
218
+ smart_postpaid_last_bill: smart("GET", "/postpaidbilling/last-month-bill/{accountId}", "Latest postpaid bill", { parameters: ["accountId"] }),
219
+ smart_prepaid_recharge_history: smart("GET", "/billing/{meterNumber}/RechargeHistory", "Prepaid recharge history with optional duration/status filters", { parameters: ["meterNumber"] }),
220
+ smart_prepaid_bill_history: smart("GET", "/billing/{meterNumber}/billingHistory", "Prepaid billing summary with optional duration/status filters", { parameters: ["meterNumber"] }),
221
+ smart_prepaid_recharge_pdf: smart("GET", "/EnergyConsumption/recharge-history/{accountId}/download/pdf?month={month}&year={year}", "Download monthly prepaid recharge history PDF", { parameters: ["accountId", "month", "year"], binary: true }),
222
+
223
+ // Complaints and guided support
224
+ smart_faqs: smart("GET", "/ConsumerProfile/faqs", "Frequently asked questions", { dataClass: "advisory" }),
225
+ smart_contact_support: smart("GET", "/ConsumerProfile/contact-support", "Support telephone and email", { dataClass: "configuration" }),
226
+ smart_complaint_categories: smart("GET", "/complaint-categories", "Complaint categories"),
227
+ smart_complaint_priorities: smart("GET", "/complaint-categories/priorities", "Complaint priority values"),
228
+ smart_complaints: smart("GET", "/Complaints/my-complaints", "Paginated complaints; accepts userId, statusCodes, pageNumber, and pageSize"),
229
+ smart_create_complaint: smart("POST", "/Complaints", "Create a complaint", { mutation: true }),
230
+ smart_cancel_complaint: smart("POST", "/Complaints/{complaintId}/cancel", "Cancel a complaint with a reason", { parameters: ["complaintId"], mutation: true }),
231
+ smart_track_complaint: smart("GET", "/Complaints/{complaintId}/track", "Complaint timeline and current status", { parameters: ["complaintId"] }),
232
+ smart_chatbot_questions: smart("GET", "/chatbot/questions", "Guided support chatbot questions"),
233
+ smart_chatbot_submit: smart("POST", "/chatbot/submit", "Submit guided-support answers", { mutation: true, bodyExample: { userId: "...", answers: [] } }),
234
+ smart_msedcl_chatbot_questions: smart("GET", "/msedclchatbot/questions", "Alternate DISCOM chatbot questions"),
235
+ smart_msedcl_chatbot_options: smart("GET", "/msedclchatbot/options?typeId={typeId}&majorTypeId={majorTypeId}", "Alternate chatbot dependent options", { parameters: ["typeId", "majorTypeId"] }),
236
+ smart_msedcl_chatbot_precheck: smart("POST", "/msedclchatbot/precheck", "Alternate chatbot request precheck"),
237
+ smart_msedcl_chatbot_submit: smart("POST", "/msedclchatbot/submit", "Submit alternate chatbot request", { mutation: true }),
238
+ smart_msedcl_chatbot_tickets: smart("GET", "/msedclchatbot/tickets", "Alternate chatbot tickets"),
239
+ smart_msedcl_chatbot_ticket: smart("GET", "/msedclchatbot/tickets/{ticketId}", "Alternate chatbot ticket detail", { parameters: ["ticketId"] }),
240
+
241
+ // Profile, preferences, notifications, localization, and offices
242
+ smart_meter_details: smart("GET", "/ConsumerProfile/meter-details/{meterNumber}", "Meter installation and technical details", { parameters: ["meterNumber"] }),
243
+ smart_tariff_details: smart("GET", "/ConsumerProfile/tariff-details", "Current tariff details"),
244
+ smart_slab_rates: smart("GET", "/ConsumerProfile/slab-rates", "Tariff slab rates"),
245
+ smart_page_content: smart("GET", "/ConsumerProfile/page-content/{contentKey}?tenantId={tenantId}", "Tenant-managed explanatory, legal, or energy-saving content; not meter evidence", { parameters: ["contentKey", "tenantId"], dataClass: "advisory" }),
246
+ smart_update_account_label: smart("PUT", "/ConsumerProfile/me/account-label", "Update account label/type", { mutation: true }),
247
+ smart_update_language: smart("POST", "/Localization/user/language", "Update preferred language", { mutation: true }),
248
+ smart_preferences: smart("GET", "/preferences?isPrepaid={isPrepaid}", "Notification preferences", { parameters: ["isPrepaid"] }),
249
+ smart_update_preferences: smart("PUT", "/preferences", "Update one notification preference", { mutation: true, bodyExample: { key: "...", value: "true" } }),
250
+ smart_enable_all_preferences: smart("POST", "/preferences/enable-all", "Enable all notifications", { mutation: true }),
251
+ smart_disable_all_preferences: smart("POST", "/preferences/disable-all", "Disable all notifications", { mutation: true }),
252
+ smart_update_alerts: smart("POST", "/preferences/my-alerts", "Update daily and monthly consumption alert thresholds", { mutation: true, bodyExample: { Kno: "...", MeterNo: "...", Daily: { IsEnabled: true, RateKwh: 10, Description: null }, Monthly: { IsEnabled: true, RateKwh: 250, Description: null } } }),
253
+ smart_notifications: smart("GET", "/Notifications/{userId}", "All notifications for a user", { parameters: ["userId"] }),
254
+ smart_notification_unread_count: smart("GET", "/Notifications/unread-count/{userId}", "Unread notification count", { parameters: ["userId"] }),
255
+ smart_notification_mark_read: smart("POST", "/Notifications/read/{notificationId}?userId={userId}", "Mark one notification read", { parameters: ["notificationId", "userId"], mutation: true }),
256
+ smart_notification_mark_all_read: smart("POST", "/Notifications/read-all/{userId}", "Mark every notification read", { parameters: ["userId"], mutation: true }),
257
+ smart_notification_delete: smart("DELETE", "/Notifications/{notificationId}", "Delete a notification", { parameters: ["notificationId"], mutation: true }),
258
+ smart_notification_filter: smart("GET", "/Notifications/{userId}/filter", "Filter notifications by type, unread state, duration, or dates", { parameters: ["userId"] }),
259
+ smart_nearby_offices: smart("GET", "/ConsumerProfile/nearby-offices?lat={lat}&lng={lng}", "Locate nearby offices; optional query can be supplied", { parameters: ["lat", "lng"] }),
260
+ smart_appliances: smart("GET", "/Appliance", "Reference appliance catalog and default wattages used for local estimates", { dataClass: "configuration" }),
261
+
262
+ // Analytical reports. filter is the portal's query-string filter; format may be csv/pdf.
263
+ smart_report: smart("GET", "/Report/{meterNumber}/{reportType}", "Power events, TOD, peak-slot, voltage, and maximum-demand reports", { parameters: ["meterNumber", "reportType"] }),
264
+ smart_report_power_events: smart("GET", "/Report/{meterNumber}/PowerOnOff", "Power on/off event report", { parameters: ["meterNumber"] }),
265
+ smart_report_daily_tod: smart("GET", "/Report/{meterNumber}/DayWiseTOD", "Daily time-of-day report", { parameters: ["meterNumber"] }),
266
+ smart_report_monthly_tod: smart("GET", "/Report/{meterNumber}/MonthlyTOD", "Monthly time-of-day report", { parameters: ["meterNumber"] }),
267
+ smart_report_peak_slots: smart("GET", "/Report/{meterNumber}/PeakSlotConsumption", "Current peak-slot consumption", { parameters: ["meterNumber"] }),
268
+ smart_report_peak_slots_monthly: smart("GET", "/Report/{meterNumber}/PeakSlotConsumptionMonthly", "Monthly peak-slot consumption", { parameters: ["meterNumber"] }),
269
+ smart_report_voltage: smart("GET", "/Report/{meterNumber}/ConsumerVoltageDataProfile", "Voltage profile report", { parameters: ["meterNumber"] }),
270
+ smart_report_demand: smart("GET", "/Report/{meterNumber}/SanctionLoadVSMaxDemand", "Sanctioned load versus maximum demand", { parameters: ["meterNumber"] }),
271
+
272
+ // Payment bridge exposed by this portal variant.
273
+ smart_payment_initiate: smart("POST", "/msedcl-payment/initiate", "Create a payment intent", { mutation: true }),
274
+ smart_payment_verify: smart("GET", "/msedcl-payment/verify?request_id={requestId}&statusCode={statusCode}&checksum={checksum}", "Verify a payment callback", { parameters: ["requestId", "statusCode", "checksum"] }),
275
+
276
+ // Public daily smart-meter register ledger (smartmeter1.jpdcl.co.in).
277
+ ledger_consumer_readings: ledger("/smartmeter/assets/php/_getConsumerDetails.php", "Daily cumulative import, export and net-import kWh/kVAh registers plus net-meter identity", { parameters: ["consumerId"] }),
278
+ ledger_meter_alarms: ledger("/smartmeter/assets/php/_getAlarmDetails.php", "Meter alarm records exposed by the daily ledger service", { parameters: ["meterNumber"] }),
279
+ } satisfies Record<string, EndpointDefinition>;
280
+
281
+ export type EndpointName = keyof typeof endpointCatalog;
282
+
283
+ export function isEndpointName(name: string): name is EndpointName {
284
+ return Object.prototype.hasOwnProperty.call(endpointCatalog, name);
285
+ }
286
+
287
+ export function listEndpoints(portal?: EndpointDefinition["portal"]) {
288
+ return Object.entries(endpointCatalog)
289
+ .filter(([, definition]) => !portal || definition.portal === portal)
290
+ .map(([name, definition]) => ({ name, ...definition }));
291
+ }