@carrierllc/mcp 0.13.0 → 0.13.4
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/README.md +4 -3
- package/dist/{chunk-ZUUC3J4J.js → chunk-DUAXD7GD.js} +1529 -1130
- package/dist/chunk-DUAXD7GD.js.map +1 -0
- package/dist/cli.js +61 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.js +95 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugin/carrier/README.md +2 -1
- package/plugin/carrier/skills/issue-esim/SKILL.md +92 -0
- package/plugin/carrier/skills/provision/SKILL.md +2 -0
- package/templates/storefront/src/app/activate/[orderId]/ActivateClient.tsx +2 -2
- package/templates/storefront/src/components/faq/FaqSection.tsx +2 -2
- package/dist/chunk-ZUUC3J4J.js.map +0 -1
|
@@ -1815,7 +1815,7 @@ function storefrontScreen(input) {
|
|
|
1815
1815
|
// package.json
|
|
1816
1816
|
var package_default = {
|
|
1817
1817
|
name: "@carrierllc/mcp",
|
|
1818
|
-
version: "0.13.
|
|
1818
|
+
version: "0.13.4",
|
|
1819
1819
|
description: "Carrier MCP \u2014 natural-language control of MVNO/eSIM fleets via eSIMVault OCS. Stdio mode for direct integration with Claude Desktop, Cursor, Windsurf, and MCP-compatible clients. Ships the `carrier` CLI (plugin install + white-label eSIM storefront scaffold).",
|
|
1820
1820
|
license: "MIT",
|
|
1821
1821
|
author: "Carrier (Lifecycle Innovations Limited)",
|
|
@@ -1899,1218 +1899,1610 @@ var package_default = {
|
|
|
1899
1899
|
// src/version.ts
|
|
1900
1900
|
var CARRIER_VERSION = package_default.version;
|
|
1901
1901
|
|
|
1902
|
-
// ../../packages/ocs-client/dist/chunk-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
"listSponsor",
|
|
1935
|
-
"listDestinationListPrefix",
|
|
1936
|
-
"listDetailedLocationZone",
|
|
1937
|
-
"listLocationZoneElement",
|
|
1938
|
-
"listSubscriberVoipTariff",
|
|
1939
|
-
"listVoipTariffRule",
|
|
1940
|
-
"affectSubscriberFakePhoneNumber",
|
|
1941
|
-
"affectSubscriberRealPhoneNumber",
|
|
1942
|
-
"getCustomerTariff",
|
|
1943
|
-
"resetSubsGzCounter",
|
|
1944
|
-
"sendMtSms"
|
|
1945
|
-
]);
|
|
1946
|
-
var OcsResponseEnvelope = z.object({
|
|
1947
|
-
status: z.object({
|
|
1948
|
-
code: z.number(),
|
|
1949
|
-
// Required to match the `OcsStatus` interface in client.ts ({ code, msg }).
|
|
1950
|
-
msg: z.string()
|
|
1951
|
-
}).passthrough()
|
|
1952
|
-
}).passthrough();
|
|
1953
|
-
var GetResellerInfoResponse = z.object({
|
|
1954
|
-
id: z.number(),
|
|
1955
|
-
name: z.string(),
|
|
1956
|
-
parentId: z.number(),
|
|
1957
|
-
parentName: z.string(),
|
|
1958
|
-
// Production eSIMVault returns decimal money as a JSON string ("495.52").
|
|
1959
|
-
// Staging fixtures used a number, so accept both exact provider variants.
|
|
1960
|
-
balance: z.union([z.number(), z.string().regex(/^-?\d+(?:\.\d+)?$/)]),
|
|
1961
|
-
cdrFolder: z.string().optional(),
|
|
1962
|
-
trafficInfo: z.object({}).passthrough().optional(),
|
|
1963
|
-
chargingInfo: z.object({}).passthrough().optional(),
|
|
1964
|
-
contactInfo: z.object({}).passthrough().optional()
|
|
1965
|
-
}).passthrough();
|
|
1966
|
-
var ListResellerAccountResponse = z.object({
|
|
1967
|
-
reseller: z.array(
|
|
1968
|
-
z.object({
|
|
1969
|
-
id: z.number(),
|
|
1970
|
-
name: z.string(),
|
|
1971
|
-
account: z.array(z.object({}).passthrough())
|
|
1972
|
-
}).passthrough()
|
|
1973
|
-
)
|
|
1974
|
-
}).passthrough();
|
|
1975
|
-
var ListPrepaidPackageTemplateResponse = z.object({
|
|
1976
|
-
template: z.array(
|
|
1977
|
-
z.object({
|
|
1978
|
-
prepaidpackagetemplateid: z.number(),
|
|
1979
|
-
prepaidpackagetemplatename: z.string(),
|
|
1980
|
-
resellerid: z.number(),
|
|
1981
|
-
locationzoneid: z.number()
|
|
1982
|
-
}).passthrough()
|
|
1983
|
-
)
|
|
1984
|
-
}).passthrough();
|
|
1985
|
-
var SubscriberStatusName = z.string().regex(/^[A-Za-z]/, "subscriber status must be a non-empty alpha-leading name");
|
|
1986
|
-
var GetSingleSubscriberResponse = z.object({
|
|
1987
|
-
status: z.array(
|
|
1988
|
-
z.object({
|
|
1989
|
-
status: SubscriberStatusName
|
|
1990
|
-
}).passthrough()
|
|
1991
|
-
).min(1, "getSingleSubscriber.status must be a non-empty array")
|
|
1992
|
-
}).passthrough();
|
|
1993
|
-
var RESPONSE_SCHEMAS = {
|
|
1994
|
-
getResellerInfo: GetResellerInfoResponse,
|
|
1995
|
-
listResellerAccount: ListResellerAccountResponse,
|
|
1996
|
-
listPrepaidPackageTemplate: ListPrepaidPackageTemplateResponse,
|
|
1997
|
-
getSingleSubscriber: GetSingleSubscriberResponse
|
|
1998
|
-
};
|
|
1999
|
-
var IccidParam = z.object({ iccid: z.string().min(1) });
|
|
2000
|
-
var AccountIdParam = z.object({ accountId: z.coerce.number().int().positive() });
|
|
2001
|
-
var PackageIdParam = z.object({ packageId: z.coerce.number().int().positive() });
|
|
2002
|
-
var TemplateIdParam = z.object({ templateId: z.coerce.number().int().positive() });
|
|
2003
|
-
var ResellerIdParam = z.object({ resellerId: z.coerce.number().int().positive() });
|
|
2004
|
-
var DateRangeBody = z.object({
|
|
2005
|
-
start: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD"),
|
|
2006
|
-
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD")
|
|
2007
|
-
});
|
|
2008
|
-
var ErrorResponse = z.object({
|
|
2009
|
-
error: z.string(),
|
|
2010
|
-
code: z.number().optional(),
|
|
2011
|
-
retryAfter: z.number().optional()
|
|
2012
|
-
});
|
|
2013
|
-
var OkResponse = z.object({
|
|
2014
|
-
ok: z.boolean(),
|
|
2015
|
-
data: z.unknown().optional()
|
|
2016
|
-
});
|
|
2017
|
-
var ModifySubscriberStatusBody = z.object({
|
|
2018
|
-
newStatus: z.enum(["active", "suspended", "deactivated"])
|
|
2019
|
-
});
|
|
2020
|
-
var ModifySubscriberBalanceBody = z.object({
|
|
2021
|
-
amount: z.number().optional(),
|
|
2022
|
-
setBalance: z.number().optional()
|
|
2023
|
-
}).refine((v) => v.amount !== void 0 || v.setBalance !== void 0, {
|
|
2024
|
-
message: "Provide either amount (delta) or setBalance (absolute)"
|
|
2025
|
-
});
|
|
2026
|
-
var ModifySubscriberContactInfoBody = z.object({
|
|
2027
|
-
name: z.string().optional(),
|
|
2028
|
-
mail: z.string().email().optional(),
|
|
2029
|
-
phone: z.string().optional()
|
|
2030
|
-
});
|
|
2031
|
-
var SetTrafficRestrictionsBody = z.object({
|
|
2032
|
-
blockData: z.boolean().optional(),
|
|
2033
|
-
blockVoice: z.boolean().optional(),
|
|
2034
|
-
blockSms: z.boolean().optional(),
|
|
2035
|
-
blockRoaming: z.boolean().optional()
|
|
2036
|
-
});
|
|
2037
|
-
var ModifySteeringListBody = z.object({
|
|
2038
|
-
steeringListId: z.number().int().positive()
|
|
2039
|
-
});
|
|
2040
|
-
var HlrSetBitrateBody = z.object({
|
|
2041
|
-
limit: z.number().int().positive()
|
|
2042
|
-
});
|
|
2043
|
-
var AssignPackageBody = z.object({
|
|
2044
|
-
packageTemplateId: z.number().int().positive(),
|
|
2045
|
-
startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
2046
|
-
});
|
|
2047
|
-
var AssignRecurringPackageBody = z.object({
|
|
2048
|
-
packageTemplateId: z.number().int().positive()
|
|
2049
|
-
});
|
|
2050
|
-
var ModifyPackageLimitsBody = z.object({
|
|
2051
|
-
dataLimitMb: z.number().positive().optional(),
|
|
2052
|
-
voiceLimitMin: z.number().nonnegative().optional(),
|
|
2053
|
-
smsLimit: z.number().nonnegative().optional()
|
|
2054
|
-
});
|
|
2055
|
-
var ModifyPackageExpiryBody = z.object({
|
|
2056
|
-
expiryDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
2057
|
-
});
|
|
2058
|
-
var ModifyPackageStatusBody = z.object({
|
|
2059
|
-
status: z.enum(["active", "paused"])
|
|
2060
|
-
});
|
|
2061
|
-
var StopResumeRecurringBody = z.object({
|
|
2062
|
-
action: z.enum(["stop", "resume"])
|
|
2063
|
-
});
|
|
2064
|
-
var CreatePackageTemplateBody = z.object({
|
|
2065
|
-
name: z.string().min(1),
|
|
2066
|
-
dataLimitMb: z.number().positive(),
|
|
2067
|
-
validityDays: z.number().int().positive(),
|
|
2068
|
-
price: z.number().nonnegative().optional(),
|
|
2069
|
-
recurring: z.boolean().optional()
|
|
2070
|
-
});
|
|
2071
|
-
var ModifyTemplateCoreBody = z.object({
|
|
2072
|
-
name: z.string().optional(),
|
|
2073
|
-
dataLimitMb: z.number().positive().optional(),
|
|
2074
|
-
validityDays: z.number().int().positive().optional()
|
|
2075
|
-
});
|
|
2076
|
-
var ModifyTemplateRecurringBody = z.object({
|
|
2077
|
-
billingCycleDays: z.number().int().positive().optional(),
|
|
2078
|
-
autoRenew: z.boolean().optional()
|
|
2079
|
-
});
|
|
2080
|
-
var ModifyTemplateThrottlingBody = z.object({
|
|
2081
|
-
throttleSpeedKbps: z.number().int().nonnegative()
|
|
2082
|
-
});
|
|
2083
|
-
var ModifyAccountBalanceBody = z.object({
|
|
2084
|
-
amount: z.number(),
|
|
2085
|
-
mode: z.enum(["add", "subtract", "set"]).default("add")
|
|
2086
|
-
});
|
|
2087
|
-
var MoveSubscribersBody = z.object({
|
|
2088
|
-
fromAccountId: z.number().int().positive(),
|
|
2089
|
-
iccidStart: z.string().min(1),
|
|
2090
|
-
iccidEnd: z.string().min(1)
|
|
2091
|
-
});
|
|
2092
|
-
var CreateLocationZoneBody = z.object({
|
|
2093
|
-
name: z.string().min(1),
|
|
2094
|
-
countries: z.array(z.string().length(2)).min(1)
|
|
2095
|
-
});
|
|
2096
|
-
var SendSmsBody = z.object({
|
|
2097
|
-
text: z.string().min(1).max(160),
|
|
2098
|
-
senderId: z.string().optional()
|
|
2099
|
-
});
|
|
2100
|
-
var ChangeSimStatusBody = z.object({
|
|
2101
|
-
newStatus: z.enum(["Enable", "Disable"])
|
|
2102
|
-
});
|
|
2103
|
-
var DiagnoseSubscriberBody = z.object({
|
|
2104
|
-
iccid: z.string().min(1)
|
|
2105
|
-
});
|
|
2106
|
-
var FleetHealthQuery = z.object({
|
|
2107
|
-
accountId: z.coerce.number().int().positive().optional()
|
|
2108
|
-
});
|
|
2109
|
-
var ChurnRiskQuery = z.object({
|
|
2110
|
-
daysSinceUsage: z.coerce.number().int().positive().default(14),
|
|
2111
|
-
limit: z.coerce.number().int().positive().max(500).default(50)
|
|
2112
|
-
});
|
|
2113
|
-
var DetectUsageAnomaliesBody = z.object({
|
|
2114
|
-
start: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
2115
|
-
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
2116
|
-
thresholdMultiplier: z.number().positive().default(2.5)
|
|
2117
|
-
});
|
|
2118
|
-
var OptimizePackageBody = z.object({
|
|
2119
|
-
iccid: z.string().min(1)
|
|
2120
|
-
});
|
|
2121
|
-
var AuditNetworkCoverageBody = z.object({
|
|
2122
|
-
locationZoneId: z.number().int().positive().optional()
|
|
2123
|
-
});
|
|
2124
|
-
var MarketingIntelligenceQuery = z.object({
|
|
2125
|
-
segment: z.enum(["low_usage", "high_usage", "expiring_soon", "churned"]).optional()
|
|
2126
|
-
});
|
|
2127
|
-
var HighCostSubscribersQuery = z.object({
|
|
2128
|
-
limit: z.coerce.number().int().positive().max(200).default(20),
|
|
2129
|
-
start: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
2130
|
-
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
2131
|
-
});
|
|
2132
|
-
var WebhookEventBody = z.record(z.string(), z.unknown());
|
|
2133
|
-
var HealthResponse = z.object({
|
|
2134
|
-
ok: z.literal(true),
|
|
2135
|
-
version: z.string(),
|
|
2136
|
-
build_sha: z.string()
|
|
2137
|
-
});
|
|
2138
|
-
|
|
2139
|
-
// ../../packages/ocs-client/dist/chunk-CGMK4FTD.js
|
|
2140
|
-
var DEFAULT_OCS_BUDGET = {
|
|
2141
|
-
attemptMs: 3e4,
|
|
2142
|
-
maxAttempts: 3,
|
|
2143
|
-
totalMs: 75e3
|
|
2144
|
-
};
|
|
2145
|
-
var OCS_METHOD_BUDGETS = {
|
|
2146
|
-
// 3.4 MB, 88-128 s measured. 180 s is 1.4x the slowest observation. One
|
|
2147
|
-
// attempt: payload size does not vary, so a retry only doubles the wait.
|
|
2148
|
-
getCustomerTariff: { attemptMs: 18e4, maxAttempts: 1, totalMs: 18e4 }
|
|
2149
|
-
};
|
|
2150
|
-
var MIN_ATTEMPT_BUDGET_MS = 1e3;
|
|
2151
|
-
var BACKOFF_BASE_MS = 500;
|
|
2152
|
-
var OcsTimeoutError = class extends Error {
|
|
2153
|
-
constructor(method, timeoutMs, budgetMs = timeoutMs, attempts = 1) {
|
|
2154
|
-
super(
|
|
2155
|
-
`[${method}] OCS request timed out after ${timeoutMs}ms` + (attempts > 1 ? ` on each of ${attempts} attempts` : "") + (budgetMs > timeoutMs ? ` (budget ${budgetMs}ms)` : "") + `. The upstream did not deliver a complete response in time \u2014 usually an oversized payload or a wedged upstream. Narrow the request, or raise OCS_TIMEOUT_MS / OCS_TOTAL_TIMEOUT_MS if this method is legitimately slower here.`
|
|
2156
|
-
);
|
|
2157
|
-
this.method = method;
|
|
2158
|
-
this.timeoutMs = timeoutMs;
|
|
2159
|
-
this.budgetMs = budgetMs;
|
|
2160
|
-
this.attempts = attempts;
|
|
2161
|
-
this.name = "OcsTimeoutError";
|
|
1902
|
+
// ../../packages/ocs-client/dist/chunk-LLQXWRFM.js
|
|
1903
|
+
var OCS_MAX_USAGE_WINDOW_DAYS = 7;
|
|
1904
|
+
function clampUsagePeriod(start, end, maxDays = OCS_MAX_USAGE_WINDOW_DAYS) {
|
|
1905
|
+
const endMs = Date.parse(`${end}T00:00:00Z`);
|
|
1906
|
+
const startMs = Date.parse(`${start}T00:00:00Z`);
|
|
1907
|
+
if (Number.isNaN(endMs) || Number.isNaN(startMs) || startMs > endMs) {
|
|
1908
|
+
return { start, end };
|
|
1909
|
+
}
|
|
1910
|
+
const minStartMs = endMs - (maxDays - 1) * 864e5;
|
|
1911
|
+
if (startMs < minStartMs) {
|
|
1912
|
+
return { start: new Date(minStartMs).toISOString().slice(0, 10), end };
|
|
1913
|
+
}
|
|
1914
|
+
return { start, end };
|
|
1915
|
+
}
|
|
1916
|
+
function lastNDaysPeriod(days = OCS_MAX_USAGE_WINDOW_DAYS, now = /* @__PURE__ */ new Date()) {
|
|
1917
|
+
const end = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
1918
|
+
const start = new Date(end);
|
|
1919
|
+
start.setUTCDate(end.getUTCDate() - (days - 1));
|
|
1920
|
+
return {
|
|
1921
|
+
start: start.toISOString().slice(0, 10),
|
|
1922
|
+
end: end.toISOString().slice(0, 10)
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
function locationParams(iccid) {
|
|
1926
|
+
return { iccid };
|
|
1927
|
+
}
|
|
1928
|
+
function subscriberIdParams(iccid) {
|
|
1929
|
+
return { iccid };
|
|
1930
|
+
}
|
|
1931
|
+
function recurringPackageParams(iccid, packageTemplateId, options = {}) {
|
|
1932
|
+
if (options.activationAtFirstUse !== void 0 && options.startTimeUTC !== void 0) {
|
|
1933
|
+
throw new Error("activationAtFirstUse cannot be combined with startTimeUTC");
|
|
2162
1934
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
};
|
|
2168
|
-
function envInt(name) {
|
|
2169
|
-
const proc = globalThis["process"];
|
|
2170
|
-
const parsed = Number(proc?.env?.[name]);
|
|
2171
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
1935
|
+
return {
|
|
1936
|
+
subscriber: { iccid },
|
|
1937
|
+
packageTemplateId,
|
|
1938
|
+
...options.startTimeUTC !== void 0 ? { startTimeUTC: options.startTimeUTC } : { activationAtFirstUse: options.activationAtFirstUse ?? true }
|
|
1939
|
+
};
|
|
2172
1940
|
}
|
|
2173
|
-
function
|
|
2174
|
-
|
|
2175
|
-
const attemptMs = envInt("OCS_TIMEOUT_MS") ?? base.attemptMs;
|
|
2176
|
-
const totalMs = Math.max(envInt("OCS_TOTAL_TIMEOUT_MS") ?? base.totalMs, attemptMs);
|
|
2177
|
-
return { attemptMs, maxAttempts: base.maxAttempts, totalMs };
|
|
1941
|
+
function networkEventsOverPeriodParams(iccid, start, end) {
|
|
1942
|
+
return usageOverPeriodParams(iccid, start, end);
|
|
2178
1943
|
}
|
|
2179
|
-
function
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
function finish() {
|
|
2183
|
-
clearTimeout(timer);
|
|
2184
|
-
signal.removeEventListener("abort", finish);
|
|
2185
|
-
resolve();
|
|
2186
|
-
}
|
|
2187
|
-
signal.addEventListener("abort", finish, { once: true });
|
|
2188
|
-
});
|
|
1944
|
+
function usageOverPeriodParams(iccid, start, end) {
|
|
1945
|
+
const period = clampUsagePeriod(start, end);
|
|
1946
|
+
return { subscriber: { iccid }, period };
|
|
2189
1947
|
}
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
1948
|
+
function imsiFromSubscriberRecord(record) {
|
|
1949
|
+
if (!record || typeof record !== "object") return void 0;
|
|
1950
|
+
const rec = record;
|
|
1951
|
+
const inner = rec.getSingleSubscriber && typeof rec.getSingleSubscriber === "object" ? rec.getSingleSubscriber : rec;
|
|
1952
|
+
const direct = inner.imsi;
|
|
1953
|
+
if (typeof direct === "string" && direct.length > 0) return direct;
|
|
1954
|
+
for (const key of ["imsiList", "multiImsi"]) {
|
|
1955
|
+
const list = inner[key];
|
|
1956
|
+
if (!Array.isArray(list)) continue;
|
|
1957
|
+
for (const entry of list) {
|
|
1958
|
+
if (!entry || typeof entry !== "object") continue;
|
|
1959
|
+
const value = entry.imsi;
|
|
1960
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
1961
|
+
}
|
|
2201
1962
|
}
|
|
1963
|
+
return void 0;
|
|
2202
1964
|
}
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
if (Date.now() + backoff + MIN_ATTEMPT_BUDGET_MS > deadlineAt) break;
|
|
2237
|
-
await sleep2(backoff, budgetController.signal);
|
|
1965
|
+
var OCS_PREPAID_PACKAGE_LIMIT_FIELDS = [
|
|
1966
|
+
"dataByte",
|
|
1967
|
+
"mocSecond",
|
|
1968
|
+
"mtcSecond",
|
|
1969
|
+
"moSms",
|
|
1970
|
+
"mtSms"
|
|
1971
|
+
];
|
|
1972
|
+
var LIMIT_ALIASES = {
|
|
1973
|
+
dataByte: "dataByte",
|
|
1974
|
+
dataBytes: "dataByte",
|
|
1975
|
+
dataLimit: "dataByte",
|
|
1976
|
+
databyte: "dataByte",
|
|
1977
|
+
mocSecond: "mocSecond",
|
|
1978
|
+
mocSeconds: "mocSecond",
|
|
1979
|
+
voiceOutLimit: "mocSecond",
|
|
1980
|
+
mtcSecond: "mtcSecond",
|
|
1981
|
+
mtcSeconds: "mtcSecond",
|
|
1982
|
+
voiceInLimit: "mtcSecond",
|
|
1983
|
+
moSms: "moSms",
|
|
1984
|
+
moSmsNumber: "moSms",
|
|
1985
|
+
smsOutLimit: "moSms",
|
|
1986
|
+
mtSms: "mtSms",
|
|
1987
|
+
mtSmsNumber: "mtSms",
|
|
1988
|
+
smsInLimit: "mtSms"
|
|
1989
|
+
};
|
|
1990
|
+
function prepaidPackageLimits(limits) {
|
|
1991
|
+
const out = {};
|
|
1992
|
+
const unknown = [];
|
|
1993
|
+
for (const [key, value] of Object.entries(limits)) {
|
|
1994
|
+
const target = LIMIT_ALIASES[key];
|
|
1995
|
+
if (!target) {
|
|
1996
|
+
unknown.push(key);
|
|
1997
|
+
continue;
|
|
2238
1998
|
}
|
|
2239
|
-
|
|
2240
|
-
clearTimeout(budgetTimer);
|
|
1999
|
+
out[target] = value;
|
|
2241
2000
|
}
|
|
2242
|
-
|
|
2001
|
+
if (unknown.length > 0) {
|
|
2002
|
+
throw new Error(
|
|
2003
|
+
`Unknown package limit field(s): ${unknown.join(", ")}. OCS accepts only ${OCS_PREPAID_PACKAGE_LIMIT_FIELDS.join(", ")} inside newLimits (aliases: ${Object.keys(LIMIT_ALIASES).join(", ")}).`
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
if (Object.keys(out).length === 0) {
|
|
2007
|
+
throw new Error(
|
|
2008
|
+
`No package limits to change. Provide at least one of ${OCS_PREPAID_PACKAGE_LIMIT_FIELDS.join(", ")}.`
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
2011
|
+
return out;
|
|
2243
2012
|
}
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
var windows = /* @__PURE__ */ new Map();
|
|
2249
|
-
function getWindowState(key) {
|
|
2250
|
-
let s = windows.get(key);
|
|
2251
|
-
if (!s) {
|
|
2252
|
-
s = { log: [], gate: Promise.resolve() };
|
|
2253
|
-
windows.set(key, s);
|
|
2013
|
+
function ocsLocalDateTime(value, bareDate = "end") {
|
|
2014
|
+
const trimmed = value.trim();
|
|
2015
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
|
|
2016
|
+
return bareDate === "start" ? `${trimmed}T00:00:00` : `${trimmed}T23:59:59`;
|
|
2254
2017
|
}
|
|
2255
|
-
return
|
|
2018
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(trimmed)) return `${trimmed}:00`;
|
|
2019
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(trimmed)) {
|
|
2020
|
+
return trimmed.replace(/(?:Z|[+-]\d{2}:?\d{2})$/, "").slice(0, 19);
|
|
2021
|
+
}
|
|
2022
|
+
throw new Error(
|
|
2023
|
+
`"${value}" is not a date OCS can read. Use YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss \u2014 this field is a java.time.LocalDateTime and a zone offset is rejected.`
|
|
2024
|
+
);
|
|
2256
2025
|
}
|
|
2257
|
-
|
|
2258
|
-
const
|
|
2259
|
-
|
|
2026
|
+
function activePeriodFromPackages(listResponse, packageId) {
|
|
2027
|
+
const root = listResponse ?? {};
|
|
2028
|
+
const payload = root.listSubscriberPrepaidPackages ?? root.data ?? root;
|
|
2029
|
+
const packages = Array.isArray(payload?.packages) ? payload.packages : [];
|
|
2030
|
+
for (const entry of packages) {
|
|
2031
|
+
if (!entry || typeof entry !== "object") continue;
|
|
2032
|
+
const rec = entry;
|
|
2033
|
+
const id = rec.subscriberprepaidpackageid ?? rec.subscriberPrepaidPackageId;
|
|
2034
|
+
if (Number(id) !== packageId) continue;
|
|
2035
|
+
const activation = rec.tsactivationutc ?? rec.tsActivationUtc;
|
|
2036
|
+
const expiration = rec.tsexpirationutc ?? rec.tsExpirationUtc;
|
|
2037
|
+
const window = {};
|
|
2038
|
+
if (typeof activation === "string" && activation) window.activation = activation;
|
|
2039
|
+
if (typeof expiration === "string" && expiration) window.expiration = expiration;
|
|
2040
|
+
return window;
|
|
2041
|
+
}
|
|
2042
|
+
return void 0;
|
|
2043
|
+
}
|
|
2044
|
+
var OCS_ACTIVE_PERIOD_DEFAULT_COMMENT = "Active period changed via Carrier";
|
|
2045
|
+
function packageActivePeriodParams(packageId, change) {
|
|
2046
|
+
if (change.startDate === void 0 && change.endDate === void 0) {
|
|
2047
|
+
throw new Error("Provide startDate and/or endDate to change a package's active period.");
|
|
2048
|
+
}
|
|
2049
|
+
const activation = change.startDate !== void 0 ? ocsLocalDateTime(change.startDate, "start") : change.current?.activation;
|
|
2050
|
+
const expiration = change.endDate !== void 0 ? ocsLocalDateTime(change.endDate, "end") : change.current?.expiration;
|
|
2051
|
+
for (const [field, value, omitted] of [
|
|
2052
|
+
["newActivationDateUtc", activation, "startDate"],
|
|
2053
|
+
["newExpirationDateUtc", expiration, "endDate"]
|
|
2054
|
+
]) {
|
|
2055
|
+
if (value !== void 0) continue;
|
|
2056
|
+
throw new Error(
|
|
2057
|
+
`OCS requires both bounds on an active-period change and rejects the whole request with "Missing '${field}'" when one is absent. Supply ${omitted}, or read the package's current window first (activePeriodFromPackages) and pass it as \`current\`.`
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
return {
|
|
2061
|
+
packageId,
|
|
2062
|
+
// Already LocalDateTime-shaped when it came from `current`; normalising
|
|
2063
|
+
// again is a no-op and keeps a hand-passed value honest.
|
|
2064
|
+
newActivationDateUtc: ocsLocalDateTime(activation, "start"),
|
|
2065
|
+
newExpirationDateUtc: ocsLocalDateTime(expiration, "end"),
|
|
2066
|
+
comment: change.comment?.trim() || OCS_ACTIVE_PERIOD_DEFAULT_COMMENT
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
function recurringIdForPackage(listResponse, packageId) {
|
|
2070
|
+
const root = listResponse ?? {};
|
|
2071
|
+
const payload = root.listSubscriberPrepaidPackages ?? root.data ?? root;
|
|
2072
|
+
const packages = Array.isArray(payload?.packages) ? payload.packages : [];
|
|
2073
|
+
for (const entry of packages) {
|
|
2074
|
+
if (!entry || typeof entry !== "object") continue;
|
|
2075
|
+
const rec = entry;
|
|
2076
|
+
const id = rec.subscriberprepaidpackageid ?? rec.subscriberPrepaidPackageId;
|
|
2077
|
+
if (Number(id) !== packageId) continue;
|
|
2078
|
+
const recurring = rec.recurringPackage ?? rec.recurringId;
|
|
2079
|
+
const value = Number(recurring);
|
|
2080
|
+
return Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2081
|
+
}
|
|
2082
|
+
return void 0;
|
|
2083
|
+
}
|
|
2084
|
+
function emptyEsimStatusCounts() {
|
|
2085
|
+
return { active: 0, suspended: 0, inventory: 0, other: 0, total: 0 };
|
|
2086
|
+
}
|
|
2087
|
+
function addEsimStatusRow(acc, rec) {
|
|
2088
|
+
const count = Number(rec.count ?? 0);
|
|
2089
|
+
if (!Number.isFinite(count) || count === 0) return;
|
|
2090
|
+
const str2 = String(rec.statusStr ?? rec.name ?? rec.status ?? "").toUpperCase();
|
|
2091
|
+
if (str2 === "ACTIVATED" || str2 === "ACTIVE" || str2 === "AFFECTED") {
|
|
2092
|
+
acc.active += count;
|
|
2093
|
+
return;
|
|
2094
|
+
}
|
|
2095
|
+
if (str2 === "SUSPENDED") {
|
|
2096
|
+
acc.suspended += count;
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
if (str2 === "FREE" || str2 === "INVENTORY" || str2 === "NOT_ACTIVATED" || str2 === "AVAILABLE") {
|
|
2100
|
+
acc.inventory += count;
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
const num = rec.statusNum;
|
|
2104
|
+
if (typeof num === "number") {
|
|
2105
|
+
if (num === 1) acc.active += count;
|
|
2106
|
+
else if (num === 0) acc.inventory += count;
|
|
2107
|
+
else if (num === 2) acc.suspended += count;
|
|
2108
|
+
else acc.other += count;
|
|
2260
2109
|
return;
|
|
2261
2110
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2111
|
+
acc.other += count;
|
|
2112
|
+
}
|
|
2113
|
+
function extractEsimStatusCounts(json) {
|
|
2114
|
+
const acc = emptyEsimStatusCounts();
|
|
2115
|
+
const root = json ?? {};
|
|
2116
|
+
const payload = root.data ?? root.esimStatusPerAccount ?? root;
|
|
2117
|
+
const accounts = Array.isArray(payload) ? payload : Array.isArray(payload.account) ? payload.account : [];
|
|
2118
|
+
for (const account of accounts) {
|
|
2119
|
+
const rec = account ?? {};
|
|
2120
|
+
const sponsors = Array.isArray(rec.sponsor) ? rec.sponsor : [];
|
|
2121
|
+
if (sponsors.length > 0) {
|
|
2122
|
+
for (const sp of sponsors) {
|
|
2123
|
+
const esim = sp?.esim ?? {};
|
|
2124
|
+
const statuses = Array.isArray(esim.status) ? esim.status : [];
|
|
2125
|
+
for (const row of statuses) addEsimStatusRow(acc, row ?? {});
|
|
2276
2126
|
}
|
|
2277
|
-
|
|
2278
|
-
await new Promise((r) => setTimeout(r, waitMs));
|
|
2127
|
+
continue;
|
|
2279
2128
|
}
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2129
|
+
acc.active += Number(rec.active ?? 0);
|
|
2130
|
+
acc.suspended += Number(rec.suspended ?? 0);
|
|
2131
|
+
acc.inventory += Number(rec.inventory ?? rec.notActivated ?? rec.free ?? 0);
|
|
2132
|
+
acc.other += Number(rec.other ?? rec.terminated ?? 0);
|
|
2133
|
+
}
|
|
2134
|
+
acc.total = acc.active + acc.suspended + acc.inventory + acc.other;
|
|
2135
|
+
return acc;
|
|
2283
2136
|
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
affectSubscriberFakePhoneNumber: 300,
|
|
2298
|
-
affectSubscriberRealPhoneNumber: 300,
|
|
2299
|
-
esimStatusPerAccount: 300,
|
|
2300
|
-
getSubscriberActivePeriod: 300,
|
|
2301
|
-
hlrGetBitrate: 300,
|
|
2302
|
-
hlrSetBitrate: 300,
|
|
2303
|
-
listSubscriber: 300,
|
|
2304
|
-
listSubscriberPrepaidPackages: 300,
|
|
2305
|
-
moveSubscriberRangeToAccount: 300,
|
|
2306
|
-
pushSteeringToSubs: 300,
|
|
2307
|
-
setSubscriberTrafficRestrictions: 300,
|
|
2308
|
-
getSingleSubscriber: 300,
|
|
2309
|
-
getSubscriberLocationByCellId: 300
|
|
2310
|
-
};
|
|
2311
|
-
var DEFAULT_LIMIT_PER_MIN = 300;
|
|
2312
|
-
var WINDOW_MS2 = 6e4;
|
|
2313
|
-
var METRIC_EMIT_MIN_INTERVAL_MS = 1e4;
|
|
2314
|
-
var BATCH_FRACTION = 0.2;
|
|
2315
|
-
function resellerKeyHash(resellerKey) {
|
|
2316
|
-
if (!resellerKey) return "00000000";
|
|
2317
|
-
let h = 5381;
|
|
2318
|
-
for (let i = 0; i < resellerKey.length; i++) {
|
|
2319
|
-
h = ((h << 5) + h ^ resellerKey.charCodeAt(i)) >>> 0;
|
|
2137
|
+
function esimStatusPerAccountParams(args) {
|
|
2138
|
+
if (typeof args === "number") return { accountId: args };
|
|
2139
|
+
if (args.accountId !== void 0) return { accountId: args.accountId };
|
|
2140
|
+
if (args.resellerId !== void 0) return { resellerId: args.resellerId };
|
|
2141
|
+
throw new Error("esimStatusPerAccountParams needs an accountId or a resellerId");
|
|
2142
|
+
}
|
|
2143
|
+
var UNLIMITED_DATA_BYTES = 100 * 1024 * 1024 * 1024;
|
|
2144
|
+
var BYTES_PER_MB = 1048576;
|
|
2145
|
+
var TEMPLATE_DATA_KEYS = ["databyte", "dataBytes", "dataLimit"];
|
|
2146
|
+
function readTemplateDataBytes(t) {
|
|
2147
|
+
for (const key of TEMPLATE_DATA_KEYS) {
|
|
2148
|
+
const v = t[key];
|
|
2149
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
2320
2150
|
}
|
|
2321
|
-
return
|
|
2151
|
+
return void 0;
|
|
2322
2152
|
}
|
|
2323
|
-
function
|
|
2324
|
-
|
|
2325
|
-
const
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2153
|
+
function throttlePercentFromMb(mb, totalBytes = UNLIMITED_DATA_BYTES) {
|
|
2154
|
+
if (!Number.isFinite(mb) || mb <= 0 || totalBytes <= 0) return 100;
|
|
2155
|
+
const pct2 = Math.round(mb * BYTES_PER_MB / totalBytes * 100);
|
|
2156
|
+
return Math.min(100, Math.max(1, pct2));
|
|
2157
|
+
}
|
|
2158
|
+
var OCS_THROTTLE_KBPS = [
|
|
2159
|
+
128,
|
|
2160
|
+
256,
|
|
2161
|
+
384,
|
|
2162
|
+
512,
|
|
2163
|
+
1024,
|
|
2164
|
+
3072,
|
|
2165
|
+
5120,
|
|
2166
|
+
7680,
|
|
2167
|
+
10240,
|
|
2168
|
+
20480,
|
|
2169
|
+
51200,
|
|
2170
|
+
102400
|
|
2171
|
+
];
|
|
2172
|
+
var OCS_HLR_BITRATE_LIMITS = [
|
|
2173
|
+
"KB_32",
|
|
2174
|
+
"KB_64",
|
|
2175
|
+
"KB_128",
|
|
2176
|
+
"KB_256",
|
|
2177
|
+
"KB_384",
|
|
2178
|
+
"KB_512",
|
|
2179
|
+
"KB_1024",
|
|
2180
|
+
"KB_2048",
|
|
2181
|
+
"KB_3072",
|
|
2182
|
+
"KB_5120",
|
|
2183
|
+
"KB_7680",
|
|
2184
|
+
"KB_10240",
|
|
2185
|
+
"KB_20480",
|
|
2186
|
+
"KB_51200",
|
|
2187
|
+
"KB_102400",
|
|
2188
|
+
"UNLIMITED"
|
|
2189
|
+
];
|
|
2190
|
+
var OCS_HLR_BITRATE_SET = new Set(OCS_HLR_BITRATE_LIMITS);
|
|
2191
|
+
function ocsHlrBitrate(raw) {
|
|
2192
|
+
const token = hlrBitrateToken(raw);
|
|
2193
|
+
if (token !== void 0 && OCS_HLR_BITRATE_SET.has(token)) return token;
|
|
2194
|
+
return {
|
|
2195
|
+
error: "limit must be an HLR bitrate in Kbps (32, 64, 128, 256, 384, 512, 1024, 2048, 3072, 5120, 7680, 10240, 20480, 51200, 102400) or UNLIMITED"
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function ocsHlrBitrateFromBps(raw) {
|
|
2199
|
+
if (raw === 0) return "UNLIMITED";
|
|
2200
|
+
if (!Number.isInteger(raw) || raw < 0 || raw % 1e3 !== 0) {
|
|
2201
|
+
return {
|
|
2202
|
+
error: "bitrate must be an HLR speed in bits per second that matches a KB_ level (for example 256000), or 0 for UNLIMITED"
|
|
2203
|
+
};
|
|
2342
2204
|
}
|
|
2205
|
+
const mapped = ocsHlrBitrate(raw / 1e3);
|
|
2206
|
+
if (typeof mapped === "string") return mapped;
|
|
2207
|
+
return {
|
|
2208
|
+
error: "bitrate must be an HLR speed in bits per second that matches a KB_ level (for example 256000), or 0 for UNLIMITED"
|
|
2209
|
+
};
|
|
2343
2210
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2211
|
+
function hlrBitrateToken(raw) {
|
|
2212
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw > 0) return `KB_${raw}`;
|
|
2213
|
+
if (typeof raw !== "string") return void 0;
|
|
2214
|
+
const key = raw.trim().toUpperCase().replace(/\s+/g, "_");
|
|
2215
|
+
if (key === "UNLIMITED") return "UNLIMITED";
|
|
2216
|
+
if (key.startsWith("KB_")) return key;
|
|
2217
|
+
if (/^\d+$/.test(key)) return `KB_${key}`;
|
|
2218
|
+
return void 0;
|
|
2219
|
+
}
|
|
2220
|
+
function ocsSimStatus(raw) {
|
|
2221
|
+
const key = raw.trim().toLowerCase();
|
|
2222
|
+
if (key === "enable" || key === "enabled" || key === "affected") return "Affected";
|
|
2223
|
+
if (key === "disable" || key === "disabled" || key === "free") return "Free";
|
|
2224
|
+
if (key === "deleted") {
|
|
2225
|
+
return {
|
|
2226
|
+
error: "DELETED is not a changeSimStatus value. newStatus must be Enable or Disable"
|
|
2354
2227
|
};
|
|
2355
|
-
_endpointWindows.set(key, s);
|
|
2356
2228
|
}
|
|
2357
|
-
return
|
|
2229
|
+
return { error: "newStatus must be Enable or Disable" };
|
|
2358
2230
|
}
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2231
|
+
var OCS_PERIODICITY = { DAILY: 0, WEEKLY: 1, MONTHLY: 2 };
|
|
2232
|
+
var OCS_PERIODICITY_VALUES = [0, 1, 2];
|
|
2233
|
+
var CEILING_MULTIPLIERS = [20, 25, 50, 100];
|
|
2234
|
+
var SECONDS_PER_DAY = 86400;
|
|
2235
|
+
function throttledBytesPerPeriod(kbps, periodDays = 1) {
|
|
2236
|
+
if (!Number.isFinite(kbps) || kbps <= 0) return 0;
|
|
2237
|
+
return kbps * 1e3 * SECONDS_PER_DAY * Math.max(1, periodDays) / 8;
|
|
2363
2238
|
}
|
|
2364
|
-
|
|
2365
|
-
const
|
|
2366
|
-
|
|
2367
|
-
|
|
2239
|
+
function unlimitedCeilingFor(windowMb, throttleKbps, periodDays = 1) {
|
|
2240
|
+
const windowBytes = windowMb * BYTES_PER_MB;
|
|
2241
|
+
const unreachableAbove = windowBytes + throttledBytesPerPeriod(throttleKbps ?? 0, periodDays);
|
|
2242
|
+
for (const multiplier of CEILING_MULTIPLIERS) {
|
|
2243
|
+
const ceilingBytes = windowBytes * multiplier;
|
|
2244
|
+
if (ceilingBytes > unreachableAbove) {
|
|
2245
|
+
return { ceilingBytes, percent: Math.round(100 / multiplier) };
|
|
2246
|
+
}
|
|
2368
2247
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2248
|
+
const last = CEILING_MULTIPLIERS[CEILING_MULTIPLIERS.length - 1];
|
|
2249
|
+
return { ceilingBytes: windowBytes * last, percent: Math.round(100 / last) };
|
|
2371
2250
|
}
|
|
2372
|
-
|
|
2373
|
-
const
|
|
2374
|
-
const
|
|
2375
|
-
const
|
|
2376
|
-
const
|
|
2377
|
-
const
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
const
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
if (priority === "batch") {
|
|
2405
|
-
state.batchLog.push(now);
|
|
2406
|
-
}
|
|
2407
|
-
return;
|
|
2408
|
-
}
|
|
2409
|
-
let waitMs;
|
|
2410
|
-
if (globalFull) {
|
|
2411
|
-
const oldest = state.log[0];
|
|
2412
|
-
waitMs = WINDOW_MS2 - (now - oldest) + 1;
|
|
2413
|
-
} else {
|
|
2414
|
-
const oldestBatch = state.batchLog[0];
|
|
2415
|
-
waitMs = WINDOW_MS2 - (now - oldestBatch) + 1;
|
|
2416
|
-
}
|
|
2417
|
-
await new Promise((r) => setTimeout(r, waitMs));
|
|
2251
|
+
function normalizePackageTemplate(template) {
|
|
2252
|
+
const out = { ...template };
|
|
2253
|
+
const unlimitedFlag = out.unlimited === true;
|
|
2254
|
+
const current = readTemplateDataBytes(out);
|
|
2255
|
+
const isUnlimited = unlimitedFlag || current === void 0 || current === 0;
|
|
2256
|
+
const throttleAfterMb = typeof out.throttleAfterMb === "number" ? out.throttleAfterMb : void 0;
|
|
2257
|
+
const throttleKbps = typeof out.throttleKbps === "number" ? out.throttleKbps : void 0;
|
|
2258
|
+
const periodDays = typeof out.perioddays === "number" ? out.perioddays : 1;
|
|
2259
|
+
if (throttleKbps !== void 0 && !OCS_THROTTLE_KBPS.includes(throttleKbps)) {
|
|
2260
|
+
throw new Error(
|
|
2261
|
+
`throttleKbps ${throttleKbps} is not an OCS bitrate. Allowed: ${OCS_THROTTLE_KBPS.join(", ")}. This field is a SPEED in Kbps, not a data allowance.`
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
const periodicity = out.recurringPeriodicityType;
|
|
2265
|
+
if (periodicity !== void 0 && !OCS_PERIODICITY_VALUES.includes(periodicity)) {
|
|
2266
|
+
throw new Error(
|
|
2267
|
+
`recurringPeriodicityType ${periodicity} is out of range. Allowed: 0=Daily, 1=Weekly, 2=Monthly. (The live v2 Mango templates use 3, which is not a valid value.)`
|
|
2268
|
+
);
|
|
2269
|
+
}
|
|
2270
|
+
if (out.recurring === true && (periodicity === OCS_PERIODICITY.DAILY || periodicity === OCS_PERIODICITY.WEEKLY) && out.nbOccurrence === void 0) {
|
|
2271
|
+
throw new Error(
|
|
2272
|
+
"nbOccurrence is required for a daily or weekly recurring template; OCS only treats it as optional for monthly templates."
|
|
2273
|
+
);
|
|
2274
|
+
}
|
|
2275
|
+
if (throttleKbps !== void 0) out.throttlingThreshold1Limit = throttleKbps;
|
|
2276
|
+
delete out.throttleKbps;
|
|
2277
|
+
if (isUnlimited) {
|
|
2278
|
+
let ceiling = UNLIMITED_DATA_BYTES;
|
|
2279
|
+
if (throttleAfterMb !== void 0 && throttleAfterMb > 0) {
|
|
2280
|
+
const sized = unlimitedCeilingFor(throttleAfterMb, throttleKbps, periodDays);
|
|
2281
|
+
ceiling = sized.ceilingBytes;
|
|
2282
|
+
out.throttlingThreshold1Perc = sized.percent;
|
|
2418
2283
|
}
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2284
|
+
for (const key of TEMPLATE_DATA_KEYS) {
|
|
2285
|
+
if (key in out) out[key] = ceiling;
|
|
2286
|
+
}
|
|
2287
|
+
if (!TEMPLATE_DATA_KEYS.some((k) => k in out)) out.databyte = ceiling;
|
|
2288
|
+
}
|
|
2289
|
+
delete out.unlimited;
|
|
2290
|
+
if (throttleAfterMb !== void 0) {
|
|
2291
|
+
if (!isUnlimited) {
|
|
2292
|
+
const total = readTemplateDataBytes(out) ?? UNLIMITED_DATA_BYTES;
|
|
2293
|
+
out.throttlingThreshold1Perc = throttlePercentFromMb(throttleAfterMb, total);
|
|
2294
|
+
}
|
|
2295
|
+
out.throttlingActive = out.throttlingActive ?? true;
|
|
2296
|
+
}
|
|
2297
|
+
delete out.throttleAfterMb;
|
|
2298
|
+
return out;
|
|
2425
2299
|
}
|
|
2426
|
-
function
|
|
2427
|
-
const
|
|
2428
|
-
const
|
|
2429
|
-
|
|
2430
|
-
|
|
2300
|
+
function normalizePackageTemplateChanges(changes) {
|
|
2301
|
+
const out = { ...changes };
|
|
2302
|
+
const explicitlyUnlimited = out.unlimited === true;
|
|
2303
|
+
for (const key of TEMPLATE_DATA_KEYS) {
|
|
2304
|
+
if (!(key in out)) continue;
|
|
2305
|
+
if (out[key] === 0 || explicitlyUnlimited) out[key] = UNLIMITED_DATA_BYTES;
|
|
2431
2306
|
}
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
for (const ts of state.log) {
|
|
2435
|
-
if (ts > windowStart) calls_in_window++;
|
|
2307
|
+
if (explicitlyUnlimited && !TEMPLATE_DATA_KEYS.some((k) => k in out)) {
|
|
2308
|
+
out.databyte = UNLIMITED_DATA_BYTES;
|
|
2436
2309
|
}
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2310
|
+
delete out.unlimited;
|
|
2311
|
+
const throttleAfterMb = out.throttleAfterMb;
|
|
2312
|
+
if (typeof throttleAfterMb === "number") {
|
|
2313
|
+
const total = readTemplateDataBytes(out) ?? UNLIMITED_DATA_BYTES;
|
|
2314
|
+
out.throttlingThreshold1Perc = throttlePercentFromMb(throttleAfterMb, total);
|
|
2440
2315
|
}
|
|
2441
|
-
|
|
2316
|
+
delete out.throttleAfterMb;
|
|
2317
|
+
return out;
|
|
2442
2318
|
}
|
|
2443
|
-
var
|
|
2444
|
-
|
|
2445
|
-
"
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2319
|
+
var LOCATION_ZONE_KEYS = /* @__PURE__ */ new Set(["locationZoneName", "name", "tadigList", "tadigs", "networkProfileId"]);
|
|
2320
|
+
function zoneRecord(input) {
|
|
2321
|
+
if (typeof input !== "string") {
|
|
2322
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2323
|
+
return { ok: false, error: "zone must be a JSON object" };
|
|
2324
|
+
}
|
|
2325
|
+
return { ok: true, body: input };
|
|
2326
|
+
}
|
|
2327
|
+
let parsed;
|
|
2328
|
+
try {
|
|
2329
|
+
parsed = JSON.parse(input);
|
|
2330
|
+
} catch {
|
|
2331
|
+
return { ok: false, error: "zone must be a JSON object" };
|
|
2332
|
+
}
|
|
2333
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2334
|
+
return { ok: false, error: "zone must be a JSON object" };
|
|
2335
|
+
}
|
|
2336
|
+
return { ok: true, body: parsed };
|
|
2337
|
+
}
|
|
2338
|
+
function zoneName(raw) {
|
|
2339
|
+
if (raw === void 0 || raw === null || raw === "") return { error: "locationZoneName is required" };
|
|
2340
|
+
if (typeof raw !== "string") return { error: "locationZoneName must be a string" };
|
|
2341
|
+
const name = raw.trim();
|
|
2342
|
+
if (!name) return { error: "locationZoneName is required" };
|
|
2343
|
+
return name;
|
|
2344
|
+
}
|
|
2345
|
+
function zoneTadigs(raw) {
|
|
2346
|
+
if (raw === void 0 || raw === null || raw === "") return { error: "tadigList is required" };
|
|
2347
|
+
const parts = typeof raw === "string" ? raw.split(/[\s,]+/) : Array.isArray(raw) ? raw : null;
|
|
2348
|
+
if (!parts) return { error: "tadigList must be a list of TADIG codes" };
|
|
2349
|
+
const list = [];
|
|
2350
|
+
for (const part of parts) {
|
|
2351
|
+
if (typeof part !== "string") return { error: "tadigList must be a list of TADIG codes" };
|
|
2352
|
+
const code = part.trim();
|
|
2353
|
+
if (code) list.push(code);
|
|
2354
|
+
}
|
|
2355
|
+
if (list.length === 0) return { error: "tadigList is required" };
|
|
2356
|
+
return list;
|
|
2357
|
+
}
|
|
2358
|
+
function zoneProfile(raw) {
|
|
2359
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw > 0) return raw;
|
|
2360
|
+
if (typeof raw === "string" && /^\d+$/.test(raw.trim())) {
|
|
2361
|
+
const n = Number(raw.trim());
|
|
2362
|
+
return n > 0 ? n : void 0;
|
|
2363
|
+
}
|
|
2364
|
+
return void 0;
|
|
2365
|
+
}
|
|
2366
|
+
function locationZoneCreateParams(input) {
|
|
2367
|
+
const parsed = zoneRecord(input);
|
|
2368
|
+
if (!parsed.ok) return { error: parsed.error };
|
|
2369
|
+
const body = parsed.body;
|
|
2370
|
+
if ("countries" in body || "operators" in body) {
|
|
2371
|
+
return {
|
|
2372
|
+
error: "createLocationZone has no countries or operators field. Send tadigList (TADIG codes) and networkProfileId."
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
for (const key of Object.keys(body)) {
|
|
2376
|
+
if (!LOCATION_ZONE_KEYS.has(key)) {
|
|
2377
|
+
return { error: "createLocationZone accepts locationZoneName, tadigList, and networkProfileId" };
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
const name = zoneName("locationZoneName" in body ? body.locationZoneName : body.name);
|
|
2381
|
+
if (typeof name !== "string") return name;
|
|
2382
|
+
const tadigList = zoneTadigs("tadigList" in body ? body.tadigList : body.tadigs);
|
|
2383
|
+
if (!Array.isArray(tadigList)) return tadigList;
|
|
2384
|
+
const networkProfileId = zoneProfile(body.networkProfileId);
|
|
2385
|
+
if (networkProfileId === void 0) {
|
|
2386
|
+
return { error: "networkProfileId must be a positive integer" };
|
|
2387
|
+
}
|
|
2388
|
+
return { locationZoneName: name, tadigList, networkProfileId };
|
|
2389
|
+
}
|
|
2390
|
+
var PACKAGE_TEMPLATE_CREATE_FIELDS = [
|
|
2391
|
+
"cost",
|
|
2392
|
+
"databyte",
|
|
2393
|
+
"deleted",
|
|
2394
|
+
"destinationzoneid",
|
|
2395
|
+
"esimSponsor",
|
|
2396
|
+
"hotspotBlocked",
|
|
2397
|
+
"locationzoneid",
|
|
2398
|
+
"mocsecond",
|
|
2399
|
+
"mosmsnumber",
|
|
2400
|
+
"mtcsecond",
|
|
2401
|
+
"mtsmsnumber",
|
|
2402
|
+
"nbOccurrence",
|
|
2403
|
+
"pckSubPrePaidPackagesDataSlicesTmpl",
|
|
2404
|
+
"perioddays",
|
|
2405
|
+
"picture",
|
|
2406
|
+
"prepaidpackagetemplateid",
|
|
2407
|
+
"prepaidpackagetemplatename",
|
|
2408
|
+
"priority",
|
|
2409
|
+
"rdbDestinationZones",
|
|
2410
|
+
"rdbLocationZones",
|
|
2411
|
+
"recurring",
|
|
2412
|
+
"recurringPeriodicityFrequency",
|
|
2413
|
+
"recurringPeriodicityType",
|
|
2414
|
+
"reportUnitsPreviousPackage",
|
|
2415
|
+
"reseller",
|
|
2416
|
+
"resellerid",
|
|
2417
|
+
"sponsors",
|
|
2418
|
+
"throttleTillEnd",
|
|
2419
|
+
"throttlingActive",
|
|
2420
|
+
"throttlingErrorAction",
|
|
2421
|
+
"throttlingThreshold1Limit",
|
|
2422
|
+
"throttlingThreshold1Perc",
|
|
2423
|
+
"throttlingThreshold2Limit",
|
|
2424
|
+
"throttlingThreshold2Perc",
|
|
2425
|
+
"throttlingThreshold3Limit",
|
|
2426
|
+
"throttlingThreshold3Perc",
|
|
2427
|
+
"throttlingThreshold4Limit",
|
|
2428
|
+
"throttlingThreshold4Perc",
|
|
2429
|
+
"uiEndAvailibilityPeriod",
|
|
2430
|
+
"uiStartAvailablePeriod",
|
|
2431
|
+
"uiVisible",
|
|
2432
|
+
"userUiName"
|
|
2449
2433
|
];
|
|
2450
|
-
var
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
name: "prepaidpackagetemplatename",
|
|
2462
|
-
volume: "databyte",
|
|
2463
|
-
validityDays: "perioddays"
|
|
2464
|
-
},
|
|
2465
|
-
modifyPPTRecurring: { templateId: "prepaidpackagetemplateid" },
|
|
2466
|
-
modifyPPTThrottling: { templateId: "prepaidpackagetemplateid" },
|
|
2467
|
-
// Console NL-create and the MCP tool both advertise `name` / `volume` /
|
|
2468
|
-
// `validityDays`. OCS createPrepaidPackageTemplate rejects unknown keys
|
|
2469
|
-
// (Jackson code 2) — live failure was Unrecognized field "name".
|
|
2470
|
-
createPrepaidPackageTemplate: {
|
|
2471
|
-
name: "prepaidpackagetemplatename",
|
|
2472
|
-
volume: "databyte",
|
|
2473
|
-
validityDays: "perioddays",
|
|
2474
|
-
dataLimit: "databyte"
|
|
2475
|
-
},
|
|
2476
|
-
createLocationZone: { name: "locationZoneName" }
|
|
2434
|
+
var PACKAGE_TEMPLATE_CREATE_FIELD_SET = new Set(PACKAGE_TEMPLATE_CREATE_FIELDS);
|
|
2435
|
+
var PACKAGE_TEMPLATE_CREATE_ALIASES = {
|
|
2436
|
+
name: "prepaidpackagetemplatename",
|
|
2437
|
+
volume: "databyte",
|
|
2438
|
+
dataLimit: "databyte",
|
|
2439
|
+
dataBytes: "databyte",
|
|
2440
|
+
validityDays: "perioddays",
|
|
2441
|
+
periodDays: "perioddays",
|
|
2442
|
+
locationZoneId: "locationzoneid",
|
|
2443
|
+
destinationZoneId: "destinationzoneid",
|
|
2444
|
+
resellerId: "resellerid"
|
|
2477
2445
|
};
|
|
2478
|
-
function
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2446
|
+
function templateRecord(input) {
|
|
2447
|
+
if (typeof input !== "string") {
|
|
2448
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2449
|
+
return { ok: false, error: "template must be a JSON object" };
|
|
2450
|
+
}
|
|
2451
|
+
return { ok: true, body: input };
|
|
2482
2452
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
if (!Object.prototype.hasOwnProperty.call(record, to)) out[to] = record[from];
|
|
2489
|
-
delete out[from];
|
|
2453
|
+
let parsed;
|
|
2454
|
+
try {
|
|
2455
|
+
parsed = JSON.parse(input);
|
|
2456
|
+
} catch {
|
|
2457
|
+
return { ok: false, error: "template must be a JSON object" };
|
|
2490
2458
|
}
|
|
2491
|
-
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
|
-
return
|
|
2495
|
-
}
|
|
2496
|
-
function isBareIntMethod(method) {
|
|
2497
|
-
return isBareIntResellerMethod(method) || Object.prototype.hasOwnProperty.call(OCS_BARE_INT_OTHER_METHODS, method);
|
|
2459
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2460
|
+
return { ok: false, error: "template must be a JSON object" };
|
|
2461
|
+
}
|
|
2462
|
+
return { ok: true, body: parsed };
|
|
2498
2463
|
}
|
|
2499
|
-
function
|
|
2500
|
-
if (
|
|
2501
|
-
|
|
2464
|
+
function templateName(raw) {
|
|
2465
|
+
if (typeof raw !== "string") return { error: "prepaidpackagetemplatename must be a string" };
|
|
2466
|
+
const name = raw.trim();
|
|
2467
|
+
if (!name) return { error: "prepaidpackagetemplatename is required" };
|
|
2468
|
+
return name;
|
|
2502
2469
|
}
|
|
2503
|
-
function
|
|
2504
|
-
|
|
2505
|
-
if (
|
|
2506
|
-
const
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2470
|
+
function packageTemplateCreateParams(input) {
|
|
2471
|
+
const parsed = templateRecord(input);
|
|
2472
|
+
if (!parsed.ok) return { error: parsed.error };
|
|
2473
|
+
const body = parsed.body;
|
|
2474
|
+
if ("price" in body) return { error: "createPrepaidPackageTemplate has no price field" };
|
|
2475
|
+
if ("description" in body) return { error: "createPrepaidPackageTemplate has no description field" };
|
|
2476
|
+
const out = {};
|
|
2477
|
+
for (const [key, value] of Object.entries(body)) {
|
|
2478
|
+
if (PACKAGE_TEMPLATE_CREATE_FIELD_SET.has(key)) out[key] = value;
|
|
2479
|
+
}
|
|
2480
|
+
for (const [key, value] of Object.entries(body)) {
|
|
2481
|
+
const target = PACKAGE_TEMPLATE_CREATE_ALIASES[key];
|
|
2482
|
+
if (!target) {
|
|
2483
|
+
if (!PACKAGE_TEMPLATE_CREATE_FIELD_SET.has(key)) {
|
|
2484
|
+
return { error: `createPrepaidPackageTemplate does not accept ${key}` };
|
|
2485
|
+
}
|
|
2486
|
+
continue;
|
|
2487
|
+
}
|
|
2488
|
+
if (target in out) continue;
|
|
2489
|
+
if (target === "prepaidpackagetemplatename") {
|
|
2490
|
+
const name = templateName(value);
|
|
2491
|
+
if (typeof name !== "string") return name;
|
|
2492
|
+
out[target] = name;
|
|
2493
|
+
continue;
|
|
2494
|
+
}
|
|
2495
|
+
out[target] = value;
|
|
2496
|
+
}
|
|
2497
|
+
if ("prepaidpackagetemplatename" in out) {
|
|
2498
|
+
const name = templateName(out["prepaidpackagetemplatename"]);
|
|
2499
|
+
if (typeof name !== "string") return name;
|
|
2500
|
+
out["prepaidpackagetemplatename"] = name;
|
|
2501
|
+
}
|
|
2502
|
+
return out;
|
|
2510
2503
|
}
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
this.details = details;
|
|
2504
|
+
function packageTemplateCreateFromToolJson(template) {
|
|
2505
|
+
let parsed;
|
|
2506
|
+
try {
|
|
2507
|
+
parsed = JSON.parse(template);
|
|
2508
|
+
} catch {
|
|
2509
|
+
return { error: "template must be a JSON object" };
|
|
2518
2510
|
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
details;
|
|
2522
|
-
};
|
|
2523
|
-
var OcsUnknownMethodError = class extends Error {
|
|
2524
|
-
constructor(method) {
|
|
2525
|
-
super(
|
|
2526
|
-
`[${method}] is not a known OCS v1 method. It is not in the verified set of ${OCS_V1_METHODS.size} methods (createAccount/createSubscriber do not exist on the OCS). If this is a genuinely new method, add it to OCS_V1_METHODS or construct the client without { strictMethods: true }.`
|
|
2527
|
-
);
|
|
2528
|
-
this.method = method;
|
|
2529
|
-
this.name = "OcsUnknownMethodError";
|
|
2511
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2512
|
+
return { error: "template must be a JSON object" };
|
|
2530
2513
|
}
|
|
2531
|
-
|
|
2514
|
+
let normalized;
|
|
2515
|
+
try {
|
|
2516
|
+
normalized = normalizePackageTemplate(parsed);
|
|
2517
|
+
} catch (err) {
|
|
2518
|
+
const message = err instanceof Error ? err.message : "template could not be normalized";
|
|
2519
|
+
return { error: message };
|
|
2520
|
+
}
|
|
2521
|
+
return packageTemplateCreateParams(normalized);
|
|
2522
|
+
}
|
|
2523
|
+
var PACKAGE_TEMPLATE_MODIFY_EXTRA = {
|
|
2524
|
+
modifyPPTCore: ["changeSponsor"],
|
|
2525
|
+
modifyPPTRecurring: [],
|
|
2526
|
+
modifyPPTThrottling: []
|
|
2532
2527
|
};
|
|
2533
|
-
var
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2528
|
+
var PACKAGE_TEMPLATE_MODIFY_IDENTITY = /* @__PURE__ */ new Set(["templateId", "prepaidpackagetemplateid"]);
|
|
2529
|
+
var PACKAGE_TEMPLATE_MODIFY_ALIASES = {
|
|
2530
|
+
...PACKAGE_TEMPLATE_CREATE_ALIASES,
|
|
2531
|
+
periodicity: "recurringPeriodicityType",
|
|
2532
|
+
occurrences: "nbOccurrence",
|
|
2533
|
+
firstThresholdPercent: "throttlingThreshold1Perc",
|
|
2534
|
+
firstThresholdLimitKbps: "throttlingThreshold1Limit",
|
|
2535
|
+
errorAction: "throttlingErrorAction"
|
|
2536
|
+
};
|
|
2537
|
+
function changesRecord(input) {
|
|
2538
|
+
if (typeof input !== "string") {
|
|
2539
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2540
|
+
return { ok: false, error: "changes must be a JSON object" };
|
|
2541
|
+
}
|
|
2542
|
+
return { ok: true, body: input };
|
|
2542
2543
|
}
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
return _OcsContractError.summarize(this.issues);
|
|
2544
|
+
let parsed;
|
|
2545
|
+
try {
|
|
2546
|
+
parsed = JSON.parse(input);
|
|
2547
|
+
} catch {
|
|
2548
|
+
return { ok: false, error: "changes must be a JSON object" };
|
|
2549
2549
|
}
|
|
2550
|
-
|
|
2551
|
-
return
|
|
2550
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2551
|
+
return { ok: false, error: "changes must be a JSON object" };
|
|
2552
2552
|
}
|
|
2553
|
-
};
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
return {
|
|
2563
|
-
message: "Subscriber is end-of-life (code 17). It can no longer be mutated. Use modify_subscriber_status to revive before further changes.",
|
|
2564
|
-
details: {}
|
|
2565
|
-
};
|
|
2566
|
-
default:
|
|
2567
|
-
if (code >= 10001 && code <= 10004) {
|
|
2568
|
-
const details = {};
|
|
2569
|
-
if (rawBody["invalidTadigs"] !== void 0) {
|
|
2570
|
-
details.invalidTadigs = rawBody["invalidTadigs"];
|
|
2571
|
-
}
|
|
2572
|
-
if (rawBody["existingLZ"] !== void 0) {
|
|
2573
|
-
details.existingLZ = rawBody["existingLZ"];
|
|
2574
|
-
}
|
|
2575
|
-
return { message: rawMsg || "Location zone validation failed", details };
|
|
2576
|
-
}
|
|
2577
|
-
return { message: rawMsg || "Unknown error", details: {} };
|
|
2553
|
+
return { ok: true, body: parsed };
|
|
2554
|
+
}
|
|
2555
|
+
function periodicityValue(raw) {
|
|
2556
|
+
if (typeof raw === "number" && OCS_PERIODICITY_VALUES.includes(raw)) return raw;
|
|
2557
|
+
if (typeof raw === "string") {
|
|
2558
|
+
const key = raw.trim().toLowerCase();
|
|
2559
|
+
if (key === "daily" || key === "0") return OCS_PERIODICITY.DAILY;
|
|
2560
|
+
if (key === "weekly" || key === "1") return OCS_PERIODICITY.WEEKLY;
|
|
2561
|
+
if (key === "monthly" || key === "2") return OCS_PERIODICITY.MONTHLY;
|
|
2578
2562
|
}
|
|
2563
|
+
return { error: "recurringPeriodicityType must be 0 (daily), 1 (weekly), or 2 (monthly)" };
|
|
2579
2564
|
}
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
if (
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
const delay = Math.max(0, Math.round(base + jitter));
|
|
2597
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
2598
|
-
}
|
|
2599
|
-
try {
|
|
2600
|
-
return await fn();
|
|
2601
|
-
} catch (err) {
|
|
2602
|
-
lastError = err;
|
|
2603
|
-
if (err instanceof OcsApiError) {
|
|
2604
|
-
if (err.code !== 100) throw err;
|
|
2605
|
-
maxRetries = TRAFFIC_CONTROL_MAX_RETRIES;
|
|
2606
|
-
backoffBaseMs = TRAFFIC_CONTROL_BACKOFF_BASE_MS;
|
|
2607
|
-
backoffMaxMs = TRAFFIC_CONTROL_BACKOFF_MAX_MS;
|
|
2608
|
-
}
|
|
2609
|
-
if (err instanceof OcsContractError) {
|
|
2610
|
-
throw err;
|
|
2611
|
-
}
|
|
2612
|
-
if (err instanceof OcsTimeoutError) {
|
|
2613
|
-
throw err;
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2565
|
+
function throttlingErrorActionValue(raw) {
|
|
2566
|
+
if (typeof raw === "number" && Number.isInteger(raw)) return raw;
|
|
2567
|
+
if (typeof raw === "string" && /^-?\d+$/.test(raw.trim())) return Number(raw.trim());
|
|
2568
|
+
if (typeof raw === "string" && raw.trim().toLowerCase() === "continue_unthrottled") {
|
|
2569
|
+
return {
|
|
2570
|
+
error: "throttlingErrorAction is an integer. continue_unthrottled is not a modifyPPT value"
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2573
|
+
return { error: "throttlingErrorAction is an integer" };
|
|
2574
|
+
}
|
|
2575
|
+
function applyTemplateField(out, target, value) {
|
|
2576
|
+
if (target === "prepaidpackagetemplatename") {
|
|
2577
|
+
const name = templateName(value);
|
|
2578
|
+
if (typeof name !== "string") return name;
|
|
2579
|
+
out[target] = name;
|
|
2580
|
+
return void 0;
|
|
2616
2581
|
}
|
|
2617
|
-
|
|
2582
|
+
if (target === "recurringPeriodicityType") {
|
|
2583
|
+
const period = periodicityValue(value);
|
|
2584
|
+
if (typeof period !== "number") return period;
|
|
2585
|
+
out[target] = period;
|
|
2586
|
+
return void 0;
|
|
2587
|
+
}
|
|
2588
|
+
if (target === "throttlingErrorAction") {
|
|
2589
|
+
const action = throttlingErrorActionValue(value);
|
|
2590
|
+
if (typeof action !== "number") return action;
|
|
2591
|
+
out[target] = action;
|
|
2592
|
+
return void 0;
|
|
2593
|
+
}
|
|
2594
|
+
out[target] = value;
|
|
2595
|
+
return void 0;
|
|
2618
2596
|
}
|
|
2619
|
-
function
|
|
2620
|
-
|
|
2597
|
+
function packageTemplateModifyParams(input, method) {
|
|
2598
|
+
const parsed = changesRecord(input);
|
|
2599
|
+
if (!parsed.ok) return { error: parsed.error };
|
|
2600
|
+
const body = parsed.body;
|
|
2601
|
+
if ("price" in body) return { error: `${method} has no price field` };
|
|
2602
|
+
if ("description" in body) return { error: `${method} has no description field` };
|
|
2603
|
+
const accepted = /* @__PURE__ */ new Set([
|
|
2604
|
+
...PACKAGE_TEMPLATE_CREATE_FIELDS,
|
|
2605
|
+
...PACKAGE_TEMPLATE_MODIFY_EXTRA[method]
|
|
2606
|
+
]);
|
|
2607
|
+
const out = {};
|
|
2608
|
+
for (const [key, value] of Object.entries(body)) {
|
|
2609
|
+
if (PACKAGE_TEMPLATE_MODIFY_IDENTITY.has(key) || !accepted.has(key)) continue;
|
|
2610
|
+
const applied = applyTemplateField(out, key, value);
|
|
2611
|
+
if (applied) return applied;
|
|
2612
|
+
}
|
|
2613
|
+
for (const [key, value] of Object.entries(body)) {
|
|
2614
|
+
if (PACKAGE_TEMPLATE_MODIFY_IDENTITY.has(key)) continue;
|
|
2615
|
+
const target = PACKAGE_TEMPLATE_MODIFY_ALIASES[key];
|
|
2616
|
+
if (!target) {
|
|
2617
|
+
if (!accepted.has(key)) return { error: `${method} does not accept ${key}` };
|
|
2618
|
+
continue;
|
|
2619
|
+
}
|
|
2620
|
+
if (target in out) continue;
|
|
2621
|
+
const applied = applyTemplateField(out, target, value);
|
|
2622
|
+
if (applied) return applied;
|
|
2623
|
+
}
|
|
2624
|
+
if (Object.keys(out).length === 0) {
|
|
2625
|
+
return { error: "at least one template field required" };
|
|
2626
|
+
}
|
|
2627
|
+
return out;
|
|
2621
2628
|
}
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
* @param options Contract-enforcement opt-outs (see `OcsClientOptions`).
|
|
2629
|
-
*/
|
|
2630
|
-
constructor(baseUrl, _defaultToken, _priority = "interactive", options = {}) {
|
|
2631
|
-
this._defaultToken = _defaultToken;
|
|
2632
|
-
this._priority = _priority;
|
|
2633
|
-
let end = baseUrl.length;
|
|
2634
|
-
while (end > 0 && baseUrl.charCodeAt(end - 1) === 47) end--;
|
|
2635
|
-
this.baseUrl = baseUrl.slice(0, end);
|
|
2636
|
-
this._strictMethods = options.strictMethods ?? false;
|
|
2637
|
-
this._validateResponses = options.validateResponses ?? true;
|
|
2638
|
-
this._strictValidation = options.strictValidation ?? false;
|
|
2629
|
+
function packageTemplateModifyFromToolJson(changes, method) {
|
|
2630
|
+
let parsed;
|
|
2631
|
+
try {
|
|
2632
|
+
parsed = JSON.parse(changes);
|
|
2633
|
+
} catch {
|
|
2634
|
+
return { error: "changes must be a JSON object" };
|
|
2639
2635
|
}
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
async call(method, params = {}, token) {
|
|
2651
|
-
if (!OCS_V1_METHODS.has(method)) {
|
|
2652
|
-
if (this._strictMethods) throw new OcsUnknownMethodError(method);
|
|
2653
|
-
if (!this._warnedMethods.has(method)) {
|
|
2654
|
-
this._warnedMethods.add(method);
|
|
2655
|
-
console.warn(
|
|
2656
|
-
`[ocs-client] method "${method}" is not in the verified OCS v1 set \u2014 it may not exist on this OCS instance (see OCS_V1_METHODS).`
|
|
2657
|
-
);
|
|
2658
|
-
}
|
|
2659
|
-
}
|
|
2660
|
-
const tok = token ?? this._defaultToken;
|
|
2661
|
-
if (!tok) {
|
|
2662
|
-
throw new OcsApiError(-1, "No OCS API token provided", method);
|
|
2636
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2637
|
+
return { error: "changes must be a JSON object" };
|
|
2638
|
+
}
|
|
2639
|
+
let body = parsed;
|
|
2640
|
+
if (method === "modifyPPTCore") {
|
|
2641
|
+
try {
|
|
2642
|
+
body = normalizePackageTemplateChanges(body);
|
|
2643
|
+
} catch (err) {
|
|
2644
|
+
const message = err instanceof Error ? err.message : "changes could not be normalized";
|
|
2645
|
+
return { error: message };
|
|
2663
2646
|
}
|
|
2664
|
-
const url = `${this.baseUrl}/v1?token=${tok}`;
|
|
2665
|
-
assertBareIntParam(method, params);
|
|
2666
|
-
const wireParams = applyParamRenames(method, params);
|
|
2667
|
-
const body = JSON.stringify({ [method]: wireParams });
|
|
2668
|
-
return retryWithBackoff(async () => {
|
|
2669
|
-
await acquireToken(tok);
|
|
2670
|
-
await acquireEndpointSlot(tok, method, this._priority);
|
|
2671
|
-
return runWithBudget(method, async (signal, attemptMs) => {
|
|
2672
|
-
let res;
|
|
2673
|
-
try {
|
|
2674
|
-
res = await fetch(url, {
|
|
2675
|
-
method: "POST",
|
|
2676
|
-
headers: { "Content-Type": "application/json" },
|
|
2677
|
-
body,
|
|
2678
|
-
signal
|
|
2679
|
-
});
|
|
2680
|
-
} catch (err) {
|
|
2681
|
-
if (isAbortError(err)) {
|
|
2682
|
-
throw new OcsTimeoutError(method, attemptMs);
|
|
2683
|
-
}
|
|
2684
|
-
throw err;
|
|
2685
|
-
}
|
|
2686
|
-
if (res.status >= 500) {
|
|
2687
|
-
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
2688
|
-
}
|
|
2689
|
-
if (!res.ok) {
|
|
2690
|
-
throw new OcsApiError(
|
|
2691
|
-
res.status,
|
|
2692
|
-
`HTTP ${res.status} ${res.statusText}`,
|
|
2693
|
-
method
|
|
2694
|
-
);
|
|
2695
|
-
}
|
|
2696
|
-
let json;
|
|
2697
|
-
try {
|
|
2698
|
-
json = await res.json();
|
|
2699
|
-
} catch (err) {
|
|
2700
|
-
if (isAbortError(err)) {
|
|
2701
|
-
throw new OcsTimeoutError(method, attemptMs);
|
|
2702
|
-
}
|
|
2703
|
-
throw err;
|
|
2704
|
-
}
|
|
2705
|
-
if (json.status?.code !== 0) {
|
|
2706
|
-
const ocsCode = json.status?.code ?? -1;
|
|
2707
|
-
const rawMsg = json.status?.msg ?? "Unknown error";
|
|
2708
|
-
const rawBody = json;
|
|
2709
|
-
const { message, details } = ocsErrorMessage(ocsCode, rawMsg, rawBody);
|
|
2710
|
-
throw new OcsApiError(ocsCode, message, method, details);
|
|
2711
|
-
}
|
|
2712
|
-
let payload;
|
|
2713
|
-
if (method === "getCustomerTariff" && json["listTariffRule"] !== void 0) {
|
|
2714
|
-
payload = json["listTariffRule"];
|
|
2715
|
-
} else if (method === "getSubscriberLocationByCellId") {
|
|
2716
|
-
const byMethod = json[method];
|
|
2717
|
-
if (byMethod !== void 0) {
|
|
2718
|
-
payload = byMethod;
|
|
2719
|
-
} else if (json["subscriberLocation"] !== void 0) {
|
|
2720
|
-
payload = json["subscriberLocation"];
|
|
2721
|
-
} else {
|
|
2722
|
-
payload = json[method] ?? json;
|
|
2723
|
-
}
|
|
2724
|
-
} else if (json[method] !== void 0) {
|
|
2725
|
-
payload = json[method];
|
|
2726
|
-
} else if (Object.keys(json).some((k) => k !== "status")) {
|
|
2727
|
-
payload = json;
|
|
2728
|
-
} else {
|
|
2729
|
-
payload = null;
|
|
2730
|
-
}
|
|
2731
|
-
if (this._validateResponses) {
|
|
2732
|
-
const schema = RESPONSE_SCHEMAS[method];
|
|
2733
|
-
if (schema) {
|
|
2734
|
-
const parsed = schema.safeParse(payload);
|
|
2735
|
-
if (!parsed.success) {
|
|
2736
|
-
const issues = parsed.error.issues.map((i) => ({
|
|
2737
|
-
path: i.path.join(".") || "<root>",
|
|
2738
|
-
message: i.message
|
|
2739
|
-
}));
|
|
2740
|
-
if (this._strictValidation) {
|
|
2741
|
-
throw new OcsContractError(method, issues, payload);
|
|
2742
|
-
}
|
|
2743
|
-
if (!this._warnedValidation.has(method)) {
|
|
2744
|
-
this._warnedValidation.add(method);
|
|
2745
|
-
console.warn(`[ocs-client] ${new OcsContractError(method, issues, payload).message}`);
|
|
2746
|
-
}
|
|
2747
|
-
}
|
|
2748
|
-
}
|
|
2749
|
-
}
|
|
2750
|
-
return payload;
|
|
2751
|
-
});
|
|
2752
|
-
});
|
|
2753
2647
|
}
|
|
2648
|
+
return packageTemplateModifyParams(body, method);
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
// ../../packages/ocs-client/dist/chunk-SFPWPYGO.js
|
|
2652
|
+
import { z } from "zod";
|
|
2653
|
+
var OCS_V1_METHODS = /* @__PURE__ */ new Set([
|
|
2654
|
+
"getResellerInfo",
|
|
2655
|
+
"listResellerAccount",
|
|
2656
|
+
"listPrepaidPackageTemplate",
|
|
2657
|
+
"listSubscriberPrepaidPackages",
|
|
2658
|
+
"listSubscriber",
|
|
2659
|
+
"getSingleSubscriber",
|
|
2660
|
+
"getSimProviderStatus",
|
|
2661
|
+
"getSubscriberLocation",
|
|
2662
|
+
"getSubscriberLocationByCellId",
|
|
2663
|
+
"esimStatusPerAccount",
|
|
2664
|
+
"subscriberUsageOverPeriod",
|
|
2665
|
+
"affectRecurringPackageToSubscriber",
|
|
2666
|
+
"deleteSubscriberPackage",
|
|
2667
|
+
"modifyAccountBalance",
|
|
2668
|
+
"modifySubscriberBalance",
|
|
2669
|
+
"modifySubscriberContactInfo",
|
|
2670
|
+
"modifySubscriberMobilePlan",
|
|
2671
|
+
"modifySubscriberSteeringList",
|
|
2672
|
+
"modifySubscriberVoipPlan",
|
|
2673
|
+
"modifySubscriberPrepaidPackageActivePeriod",
|
|
2674
|
+
"modifySubscriberPrepaidPackageLimits",
|
|
2675
|
+
"modifySubscriberPrepaidPackageStatus",
|
|
2676
|
+
"modifyPPTCore",
|
|
2677
|
+
"modifyPPTRecurring",
|
|
2678
|
+
"createPrepaidPackageTemplate",
|
|
2679
|
+
"createLocationZone",
|
|
2680
|
+
"changeNetworkProfileOfLocationZone",
|
|
2681
|
+
"hlrGetBitrate",
|
|
2682
|
+
"hlrSetBitrate",
|
|
2683
|
+
"listSponsor",
|
|
2684
|
+
"listDestinationListPrefix",
|
|
2685
|
+
"listDetailedLocationZone",
|
|
2686
|
+
"listLocationZoneElement",
|
|
2687
|
+
"listSubscriberVoipTariff",
|
|
2688
|
+
"listVoipTariffRule",
|
|
2689
|
+
"affectSubscriberFakePhoneNumber",
|
|
2690
|
+
"affectSubscriberRealPhoneNumber",
|
|
2691
|
+
"getCustomerTariff",
|
|
2692
|
+
"resetSubsGzCounter",
|
|
2693
|
+
"sendMtSms"
|
|
2694
|
+
]);
|
|
2695
|
+
var OcsResponseEnvelope = z.object({
|
|
2696
|
+
status: z.object({
|
|
2697
|
+
code: z.number(),
|
|
2698
|
+
// Required to match the `OcsStatus` interface in client.ts ({ code, msg }).
|
|
2699
|
+
msg: z.string()
|
|
2700
|
+
}).passthrough()
|
|
2701
|
+
}).passthrough();
|
|
2702
|
+
var GetResellerInfoResponse = z.object({
|
|
2703
|
+
id: z.number(),
|
|
2704
|
+
name: z.string(),
|
|
2705
|
+
parentId: z.number(),
|
|
2706
|
+
parentName: z.string(),
|
|
2707
|
+
// Production eSIMVault returns decimal money as a JSON string ("495.52").
|
|
2708
|
+
// Staging fixtures used a number, so accept both exact provider variants.
|
|
2709
|
+
balance: z.union([z.number(), z.string().regex(/^-?\d+(?:\.\d+)?$/)]),
|
|
2710
|
+
cdrFolder: z.string().optional(),
|
|
2711
|
+
trafficInfo: z.object({}).passthrough().optional(),
|
|
2712
|
+
chargingInfo: z.object({}).passthrough().optional(),
|
|
2713
|
+
contactInfo: z.object({}).passthrough().optional()
|
|
2714
|
+
}).passthrough();
|
|
2715
|
+
var ListResellerAccountResponse = z.object({
|
|
2716
|
+
reseller: z.array(
|
|
2717
|
+
z.object({
|
|
2718
|
+
id: z.number(),
|
|
2719
|
+
name: z.string(),
|
|
2720
|
+
account: z.array(z.object({}).passthrough())
|
|
2721
|
+
}).passthrough()
|
|
2722
|
+
)
|
|
2723
|
+
}).passthrough();
|
|
2724
|
+
var ListPrepaidPackageTemplateResponse = z.object({
|
|
2725
|
+
template: z.array(
|
|
2726
|
+
z.object({
|
|
2727
|
+
prepaidpackagetemplateid: z.number(),
|
|
2728
|
+
prepaidpackagetemplatename: z.string(),
|
|
2729
|
+
resellerid: z.number(),
|
|
2730
|
+
locationzoneid: z.number()
|
|
2731
|
+
}).passthrough()
|
|
2732
|
+
)
|
|
2733
|
+
}).passthrough();
|
|
2734
|
+
var SubscriberStatusName = z.string().regex(/^[A-Za-z]/, "subscriber status must be a non-empty alpha-leading name");
|
|
2735
|
+
var GetSingleSubscriberResponse = z.object({
|
|
2736
|
+
status: z.array(
|
|
2737
|
+
z.object({
|
|
2738
|
+
status: SubscriberStatusName
|
|
2739
|
+
}).passthrough()
|
|
2740
|
+
).min(1, "getSingleSubscriber.status must be a non-empty array")
|
|
2741
|
+
}).passthrough();
|
|
2742
|
+
var RESPONSE_SCHEMAS = {
|
|
2743
|
+
getResellerInfo: GetResellerInfoResponse,
|
|
2744
|
+
listResellerAccount: ListResellerAccountResponse,
|
|
2745
|
+
listPrepaidPackageTemplate: ListPrepaidPackageTemplateResponse,
|
|
2746
|
+
getSingleSubscriber: GetSingleSubscriberResponse
|
|
2754
2747
|
};
|
|
2748
|
+
var IccidParam = z.object({ iccid: z.string().min(1) });
|
|
2749
|
+
var AccountIdParam = z.object({ accountId: z.coerce.number().int().positive() });
|
|
2750
|
+
var PackageIdParam = z.object({ packageId: z.coerce.number().int().positive() });
|
|
2751
|
+
var TemplateIdParam = z.object({ templateId: z.coerce.number().int().positive() });
|
|
2752
|
+
var ResellerIdParam = z.object({ resellerId: z.coerce.number().int().positive() });
|
|
2753
|
+
var DateRangeBody = z.object({
|
|
2754
|
+
start: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD"),
|
|
2755
|
+
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD")
|
|
2756
|
+
});
|
|
2757
|
+
var ErrorResponse = z.object({
|
|
2758
|
+
error: z.string(),
|
|
2759
|
+
code: z.number().optional(),
|
|
2760
|
+
retryAfter: z.number().optional()
|
|
2761
|
+
});
|
|
2762
|
+
var OkResponse = z.object({
|
|
2763
|
+
ok: z.boolean(),
|
|
2764
|
+
data: z.unknown().optional()
|
|
2765
|
+
});
|
|
2766
|
+
var ModifySubscriberStatusBody = z.object({
|
|
2767
|
+
newStatus: z.enum(["active", "suspended", "deactivated"])
|
|
2768
|
+
});
|
|
2769
|
+
var ModifySubscriberBalanceBody = z.object({
|
|
2770
|
+
amount: z.number().optional(),
|
|
2771
|
+
setBalance: z.number().optional()
|
|
2772
|
+
}).refine((v) => v.amount !== void 0 || v.setBalance !== void 0, {
|
|
2773
|
+
message: "Provide either amount (delta) or setBalance (absolute)"
|
|
2774
|
+
});
|
|
2775
|
+
var ModifySubscriberContactInfoBody = z.object({
|
|
2776
|
+
name: z.string().optional(),
|
|
2777
|
+
mail: z.string().email().optional(),
|
|
2778
|
+
phone: z.string().optional()
|
|
2779
|
+
});
|
|
2780
|
+
var SetTrafficRestrictionsBody = z.object({
|
|
2781
|
+
blockData: z.boolean().optional(),
|
|
2782
|
+
blockVoice: z.boolean().optional(),
|
|
2783
|
+
blockSms: z.boolean().optional(),
|
|
2784
|
+
blockRoaming: z.boolean().optional()
|
|
2785
|
+
});
|
|
2786
|
+
var ModifySteeringListBody = z.object({
|
|
2787
|
+
steeringListId: z.number().int().positive()
|
|
2788
|
+
});
|
|
2789
|
+
var HlrSetBitrateBody = z.object({
|
|
2790
|
+
limit: z.enum(OCS_HLR_BITRATE_LIMITS)
|
|
2791
|
+
});
|
|
2792
|
+
var AssignPackageBody = z.object({
|
|
2793
|
+
packageTemplateId: z.number().int().positive(),
|
|
2794
|
+
startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
2795
|
+
});
|
|
2796
|
+
var AssignRecurringPackageBody = z.object({
|
|
2797
|
+
packageTemplateId: z.number().int().positive()
|
|
2798
|
+
});
|
|
2799
|
+
var ModifyPackageLimitsBody = z.object({
|
|
2800
|
+
dataLimitMb: z.number().positive().optional(),
|
|
2801
|
+
voiceLimitMin: z.number().nonnegative().optional(),
|
|
2802
|
+
smsLimit: z.number().nonnegative().optional()
|
|
2803
|
+
});
|
|
2804
|
+
var ModifyPackageExpiryBody = z.object({
|
|
2805
|
+
expiryDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
2806
|
+
});
|
|
2807
|
+
var ModifyPackageStatusBody = z.object({
|
|
2808
|
+
status: z.enum(["active", "paused"])
|
|
2809
|
+
});
|
|
2810
|
+
var StopResumeRecurringBody = z.object({
|
|
2811
|
+
action: z.enum(["stop", "resume"])
|
|
2812
|
+
});
|
|
2813
|
+
var CreatePackageTemplateBody = z.object({
|
|
2814
|
+
name: z.string().min(1),
|
|
2815
|
+
dataLimitMb: z.number().positive(),
|
|
2816
|
+
validityDays: z.number().int().positive(),
|
|
2817
|
+
price: z.number().nonnegative().optional(),
|
|
2818
|
+
recurring: z.boolean().optional()
|
|
2819
|
+
});
|
|
2820
|
+
var ModifyTemplateCoreBody = z.object({
|
|
2821
|
+
name: z.string().optional(),
|
|
2822
|
+
dataLimitMb: z.number().positive().optional(),
|
|
2823
|
+
validityDays: z.number().int().positive().optional()
|
|
2824
|
+
});
|
|
2825
|
+
var ModifyTemplateRecurringBody = z.object({
|
|
2826
|
+
billingCycleDays: z.number().int().positive().optional(),
|
|
2827
|
+
autoRenew: z.boolean().optional()
|
|
2828
|
+
});
|
|
2829
|
+
var ModifyTemplateThrottlingBody = z.object({
|
|
2830
|
+
throttleSpeedKbps: z.number().int().nonnegative()
|
|
2831
|
+
});
|
|
2832
|
+
var ModifyAccountBalanceBody = z.object({
|
|
2833
|
+
amount: z.number(),
|
|
2834
|
+
// adapt and add are a signed delta. set replaces the balance. subtract
|
|
2835
|
+
// negates a positive amount. The API drops mode before the OCS call.
|
|
2836
|
+
mode: z.enum(["adapt", "set", "add", "subtract"]).default("adapt")
|
|
2837
|
+
});
|
|
2838
|
+
var MoveSubscribersBody = z.object({
|
|
2839
|
+
fromAccountId: z.number().int().positive(),
|
|
2840
|
+
iccidStart: z.string().min(1),
|
|
2841
|
+
iccidEnd: z.string().min(1)
|
|
2842
|
+
});
|
|
2843
|
+
var CreateLocationZoneBody = z.object({
|
|
2844
|
+
name: z.string().min(1),
|
|
2845
|
+
countries: z.array(z.string().length(2)).min(1)
|
|
2846
|
+
});
|
|
2847
|
+
var SendSmsBody = z.object({
|
|
2848
|
+
text: z.string().min(1).max(160),
|
|
2849
|
+
senderId: z.string().optional()
|
|
2850
|
+
});
|
|
2851
|
+
var ChangeSimStatusBody = z.object({
|
|
2852
|
+
newStatus: z.enum(["Enable", "Disable"])
|
|
2853
|
+
});
|
|
2854
|
+
var DiagnoseSubscriberBody = z.object({
|
|
2855
|
+
iccid: z.string().min(1)
|
|
2856
|
+
});
|
|
2857
|
+
var FleetHealthQuery = z.object({
|
|
2858
|
+
accountId: z.coerce.number().int().positive().optional()
|
|
2859
|
+
});
|
|
2860
|
+
var ChurnRiskQuery = z.object({
|
|
2861
|
+
daysSinceUsage: z.coerce.number().int().positive().default(14),
|
|
2862
|
+
limit: z.coerce.number().int().positive().max(500).default(50)
|
|
2863
|
+
});
|
|
2864
|
+
var DetectUsageAnomaliesBody = z.object({
|
|
2865
|
+
start: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
2866
|
+
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
2867
|
+
thresholdMultiplier: z.number().positive().default(2.5)
|
|
2868
|
+
});
|
|
2869
|
+
var OptimizePackageBody = z.object({
|
|
2870
|
+
iccid: z.string().min(1)
|
|
2871
|
+
});
|
|
2872
|
+
var AuditNetworkCoverageBody = z.object({
|
|
2873
|
+
locationZoneId: z.number().int().positive().optional()
|
|
2874
|
+
});
|
|
2875
|
+
var MarketingIntelligenceQuery = z.object({
|
|
2876
|
+
segment: z.enum(["low_usage", "high_usage", "expiring_soon", "churned"]).optional()
|
|
2877
|
+
});
|
|
2878
|
+
var HighCostSubscribersQuery = z.object({
|
|
2879
|
+
limit: z.coerce.number().int().positive().max(200).default(20),
|
|
2880
|
+
start: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
2881
|
+
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
2882
|
+
});
|
|
2883
|
+
var WebhookEventBody = z.record(z.string(), z.unknown());
|
|
2884
|
+
var HealthResponse = z.object({
|
|
2885
|
+
ok: z.literal(true),
|
|
2886
|
+
version: z.string(),
|
|
2887
|
+
build_sha: z.string()
|
|
2888
|
+
});
|
|
2755
2889
|
|
|
2756
|
-
// ../../packages/ocs-client/dist/chunk-
|
|
2757
|
-
var
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2890
|
+
// ../../packages/ocs-client/dist/chunk-CGMK4FTD.js
|
|
2891
|
+
var DEFAULT_OCS_BUDGET = {
|
|
2892
|
+
attemptMs: 3e4,
|
|
2893
|
+
maxAttempts: 3,
|
|
2894
|
+
totalMs: 75e3
|
|
2895
|
+
};
|
|
2896
|
+
var OCS_METHOD_BUDGETS = {
|
|
2897
|
+
// 3.4 MB, 88-128 s measured. 180 s is 1.4x the slowest observation. One
|
|
2898
|
+
// attempt: payload size does not vary, so a retry only doubles the wait.
|
|
2899
|
+
getCustomerTariff: { attemptMs: 18e4, maxAttempts: 1, totalMs: 18e4 }
|
|
2900
|
+
};
|
|
2901
|
+
var MIN_ATTEMPT_BUDGET_MS = 1e3;
|
|
2902
|
+
var BACKOFF_BASE_MS = 500;
|
|
2903
|
+
var OcsTimeoutError = class extends Error {
|
|
2904
|
+
constructor(method, timeoutMs, budgetMs = timeoutMs, attempts = 1) {
|
|
2905
|
+
super(
|
|
2906
|
+
`[${method}] OCS request timed out after ${timeoutMs}ms` + (attempts > 1 ? ` on each of ${attempts} attempts` : "") + (budgetMs > timeoutMs ? ` (budget ${budgetMs}ms)` : "") + `. The upstream did not deliver a complete response in time \u2014 usually an oversized payload or a wedged upstream. Narrow the request, or raise OCS_TIMEOUT_MS / OCS_TOTAL_TIMEOUT_MS if this method is legitimately slower here.`
|
|
2907
|
+
);
|
|
2908
|
+
this.method = method;
|
|
2909
|
+
this.timeoutMs = timeoutMs;
|
|
2910
|
+
this.budgetMs = budgetMs;
|
|
2911
|
+
this.attempts = attempts;
|
|
2912
|
+
this.name = "OcsTimeoutError";
|
|
2767
2913
|
}
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
};
|
|
2914
|
+
method;
|
|
2915
|
+
timeoutMs;
|
|
2916
|
+
budgetMs;
|
|
2917
|
+
attempts;
|
|
2918
|
+
};
|
|
2919
|
+
function envInt(name) {
|
|
2920
|
+
const proc = globalThis["process"];
|
|
2921
|
+
const parsed = Number(proc?.env?.[name]);
|
|
2922
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
2778
2923
|
}
|
|
2779
|
-
function
|
|
2780
|
-
|
|
2924
|
+
function ocsBudgetFor(method) {
|
|
2925
|
+
const base = OCS_METHOD_BUDGETS[method] ?? DEFAULT_OCS_BUDGET;
|
|
2926
|
+
const attemptMs = envInt("OCS_TIMEOUT_MS") ?? base.attemptMs;
|
|
2927
|
+
const totalMs = Math.max(envInt("OCS_TOTAL_TIMEOUT_MS") ?? base.totalMs, attemptMs);
|
|
2928
|
+
return { attemptMs, maxAttempts: base.maxAttempts, totalMs };
|
|
2781
2929
|
}
|
|
2782
|
-
function
|
|
2783
|
-
return
|
|
2930
|
+
function sleep2(ms, signal) {
|
|
2931
|
+
return new Promise((resolve) => {
|
|
2932
|
+
const timer = setTimeout(finish, ms);
|
|
2933
|
+
function finish() {
|
|
2934
|
+
clearTimeout(timer);
|
|
2935
|
+
signal.removeEventListener("abort", finish);
|
|
2936
|
+
resolve();
|
|
2937
|
+
}
|
|
2938
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
2939
|
+
});
|
|
2784
2940
|
}
|
|
2785
|
-
function
|
|
2786
|
-
|
|
2787
|
-
|
|
2941
|
+
async function withDeadline(work, deadline, ms) {
|
|
2942
|
+
let timer;
|
|
2943
|
+
try {
|
|
2944
|
+
return await Promise.race([
|
|
2945
|
+
work,
|
|
2946
|
+
new Promise((_resolve, reject) => {
|
|
2947
|
+
timer = setTimeout(() => reject(deadline), ms);
|
|
2948
|
+
})
|
|
2949
|
+
]);
|
|
2950
|
+
} finally {
|
|
2951
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2788
2952
|
}
|
|
2789
|
-
return {
|
|
2790
|
-
subscriber: { iccid },
|
|
2791
|
-
packageTemplateId,
|
|
2792
|
-
...options.startTimeUTC !== void 0 ? { startTimeUTC: options.startTimeUTC } : { activationAtFirstUse: options.activationAtFirstUse ?? true }
|
|
2793
|
-
};
|
|
2794
2953
|
}
|
|
2795
|
-
function
|
|
2796
|
-
|
|
2954
|
+
async function runWithBudget(method, attempt, budget = ocsBudgetFor(method)) {
|
|
2955
|
+
const deadlineAt = Date.now() + budget.totalMs;
|
|
2956
|
+
const budgetController = new AbortController();
|
|
2957
|
+
const budgetTimer = setTimeout(() => budgetController.abort(), budget.totalMs);
|
|
2958
|
+
let attempts = 0;
|
|
2959
|
+
let lastTimeout;
|
|
2960
|
+
try {
|
|
2961
|
+
for (let i = 0; i < budget.maxAttempts; i++) {
|
|
2962
|
+
const remaining = deadlineAt - Date.now();
|
|
2963
|
+
if (remaining < MIN_ATTEMPT_BUDGET_MS) break;
|
|
2964
|
+
const attemptMs = Math.min(budget.attemptMs, remaining);
|
|
2965
|
+
attempts++;
|
|
2966
|
+
const attemptController = new AbortController();
|
|
2967
|
+
const onBudgetAbort = () => attemptController.abort();
|
|
2968
|
+
budgetController.signal.addEventListener("abort", onBudgetAbort, { once: true });
|
|
2969
|
+
const attemptTimer = setTimeout(() => attemptController.abort(), attemptMs);
|
|
2970
|
+
const breach = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);
|
|
2971
|
+
try {
|
|
2972
|
+
return await withDeadline(
|
|
2973
|
+
attempt(attemptController.signal, attemptMs),
|
|
2974
|
+
breach,
|
|
2975
|
+
attemptMs
|
|
2976
|
+
);
|
|
2977
|
+
} catch (err) {
|
|
2978
|
+
if (!(err instanceof OcsTimeoutError)) throw err;
|
|
2979
|
+
lastTimeout = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);
|
|
2980
|
+
} finally {
|
|
2981
|
+
clearTimeout(attemptTimer);
|
|
2982
|
+
budgetController.signal.removeEventListener("abort", onBudgetAbort);
|
|
2983
|
+
attemptController.abort();
|
|
2984
|
+
}
|
|
2985
|
+
if (i === budget.maxAttempts - 1) break;
|
|
2986
|
+
const backoff = Math.random() * BACKOFF_BASE_MS * 2 ** i;
|
|
2987
|
+
if (Date.now() + backoff + MIN_ATTEMPT_BUDGET_MS > deadlineAt) break;
|
|
2988
|
+
await sleep2(backoff, budgetController.signal);
|
|
2989
|
+
}
|
|
2990
|
+
} finally {
|
|
2991
|
+
clearTimeout(budgetTimer);
|
|
2992
|
+
}
|
|
2993
|
+
throw lastTimeout ?? new OcsTimeoutError(method, budget.attemptMs, budget.totalMs, Math.max(attempts, 1));
|
|
2797
2994
|
}
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2995
|
+
|
|
2996
|
+
// ../../packages/ocs-client/dist/chunk-WYFBYNKX.js
|
|
2997
|
+
var RATE_LIMIT = 10;
|
|
2998
|
+
var WINDOW_MS = 1e3;
|
|
2999
|
+
var windows = /* @__PURE__ */ new Map();
|
|
3000
|
+
function getWindowState(key) {
|
|
3001
|
+
let s = windows.get(key);
|
|
3002
|
+
if (!s) {
|
|
3003
|
+
s = { log: [], gate: Promise.resolve() };
|
|
3004
|
+
windows.set(key, s);
|
|
3005
|
+
}
|
|
3006
|
+
return s;
|
|
2801
3007
|
}
|
|
2802
|
-
function
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
const direct = inner.imsi;
|
|
2807
|
-
if (typeof direct === "string" && direct.length > 0) return direct;
|
|
2808
|
-
for (const key of ["imsiList", "multiImsi"]) {
|
|
2809
|
-
const list = inner[key];
|
|
2810
|
-
if (!Array.isArray(list)) continue;
|
|
2811
|
-
for (const entry of list) {
|
|
2812
|
-
if (!entry || typeof entry !== "object") continue;
|
|
2813
|
-
const value = entry.imsi;
|
|
2814
|
-
if (typeof value === "string" && value.length > 0) return value;
|
|
2815
|
-
}
|
|
3008
|
+
async function acquireToken(resellerId) {
|
|
3009
|
+
const proc = globalThis["process"];
|
|
3010
|
+
if (proc?.env?.["RATE_FLOOR_DISABLED"] === "true" || globalThis["RATE_FLOOR_DISABLED"] === "true") {
|
|
3011
|
+
return;
|
|
2816
3012
|
}
|
|
2817
|
-
|
|
3013
|
+
const state = getWindowState(resellerId);
|
|
3014
|
+
const ticket = state.gate.then(async () => {
|
|
3015
|
+
while (true) {
|
|
3016
|
+
const now = Date.now();
|
|
3017
|
+
if (state.log.length < RATE_LIMIT) {
|
|
3018
|
+
state.log.push(now);
|
|
3019
|
+
return;
|
|
3020
|
+
}
|
|
3021
|
+
const oldest = state.log[0];
|
|
3022
|
+
const age = now - oldest;
|
|
3023
|
+
if (age >= WINDOW_MS) {
|
|
3024
|
+
state.log.shift();
|
|
3025
|
+
state.log.push(now);
|
|
3026
|
+
return;
|
|
3027
|
+
}
|
|
3028
|
+
const waitMs = WINDOW_MS - age + 1;
|
|
3029
|
+
await new Promise((r) => setTimeout(r, waitMs));
|
|
3030
|
+
}
|
|
3031
|
+
});
|
|
3032
|
+
state.gate = ticket;
|
|
3033
|
+
return ticket;
|
|
2818
3034
|
}
|
|
2819
|
-
var
|
|
2820
|
-
"
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
3035
|
+
var ENDPOINT_LIMITS_PER_MIN = {
|
|
3036
|
+
// Global sentinel (keyed as "__global__")
|
|
3037
|
+
__global__: 600,
|
|
3038
|
+
// 30/min — tightest endpoint
|
|
3039
|
+
subscriberNetworkEventsOverPeriod: 30,
|
|
3040
|
+
// 100/min
|
|
3041
|
+
subscriberUsageOverPeriod: 100,
|
|
3042
|
+
// 150/min
|
|
3043
|
+
listLocationZoneElement: 150,
|
|
3044
|
+
sendMtSms: 150,
|
|
3045
|
+
// 300/min (explicit; also the fallback default)
|
|
3046
|
+
modifySubscriberMobilePlan: 300,
|
|
3047
|
+
affectRecurringPackageToSubscriber: 300,
|
|
3048
|
+
affectSubscriberFakePhoneNumber: 300,
|
|
3049
|
+
affectSubscriberRealPhoneNumber: 300,
|
|
3050
|
+
esimStatusPerAccount: 300,
|
|
3051
|
+
getSubscriberActivePeriod: 300,
|
|
3052
|
+
hlrGetBitrate: 300,
|
|
3053
|
+
hlrSetBitrate: 300,
|
|
3054
|
+
listSubscriber: 300,
|
|
3055
|
+
listSubscriberPrepaidPackages: 300,
|
|
3056
|
+
moveSubscriberRangeToAccount: 300,
|
|
3057
|
+
pushSteeringToSubs: 300,
|
|
3058
|
+
setSubscriberTrafficRestrictions: 300,
|
|
3059
|
+
getSingleSubscriber: 300,
|
|
3060
|
+
getSubscriberLocationByCellId: 300
|
|
2843
3061
|
};
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
out[target] = value;
|
|
2854
|
-
}
|
|
2855
|
-
if (unknown.length > 0) {
|
|
2856
|
-
throw new Error(
|
|
2857
|
-
`Unknown package limit field(s): ${unknown.join(", ")}. OCS accepts only ${OCS_PREPAID_PACKAGE_LIMIT_FIELDS.join(", ")} inside newLimits (aliases: ${Object.keys(LIMIT_ALIASES).join(", ")}).`
|
|
2858
|
-
);
|
|
3062
|
+
var DEFAULT_LIMIT_PER_MIN = 300;
|
|
3063
|
+
var WINDOW_MS2 = 6e4;
|
|
3064
|
+
var METRIC_EMIT_MIN_INTERVAL_MS = 1e4;
|
|
3065
|
+
var BATCH_FRACTION = 0.2;
|
|
3066
|
+
function resellerKeyHash(resellerKey) {
|
|
3067
|
+
if (!resellerKey) return "00000000";
|
|
3068
|
+
let h = 5381;
|
|
3069
|
+
for (let i = 0; i < resellerKey.length; i++) {
|
|
3070
|
+
h = ((h << 5) + h ^ resellerKey.charCodeAt(i)) >>> 0;
|
|
2859
3071
|
}
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
3072
|
+
return h.toString(16).padStart(8, "0");
|
|
3073
|
+
}
|
|
3074
|
+
function emitMetric(endpoint, resellerKey, callsInWindow, limitPerMin) {
|
|
3075
|
+
const pct2 = Math.round(callsInWindow / limitPerMin * 100);
|
|
3076
|
+
const reseller_hash = resellerKeyHash(resellerKey);
|
|
3077
|
+
console.error(
|
|
3078
|
+
JSON.stringify({
|
|
3079
|
+
metric: "carrier_ocs_calls_per_min",
|
|
3080
|
+
endpoint,
|
|
3081
|
+
reseller_hash,
|
|
3082
|
+
calls_in_window: callsInWindow,
|
|
3083
|
+
limit_per_min: limitPerMin,
|
|
3084
|
+
utilisation_pct: pct2,
|
|
3085
|
+
alert: pct2 >= 80,
|
|
3086
|
+
ts: Date.now()
|
|
3087
|
+
})
|
|
3088
|
+
);
|
|
3089
|
+
if (pct2 >= 80) {
|
|
3090
|
+
console.warn(
|
|
3091
|
+
`[rate-governor] ALERT: ${endpoint} at ${pct2}% capacity (${callsInWindow}/${limitPerMin} per min) reseller=${reseller_hash}`
|
|
2863
3092
|
);
|
|
2864
3093
|
}
|
|
2865
|
-
return out;
|
|
2866
3094
|
}
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
3095
|
+
var _endpointWindows = /* @__PURE__ */ new Map();
|
|
3096
|
+
function _getEndpointState(key) {
|
|
3097
|
+
let s = _endpointWindows.get(key);
|
|
3098
|
+
if (!s) {
|
|
3099
|
+
s = {
|
|
3100
|
+
log: [],
|
|
3101
|
+
batchLog: [],
|
|
3102
|
+
gate: Promise.resolve(),
|
|
3103
|
+
lastMetricEmitAt: 0,
|
|
3104
|
+
metricBelowHighUtil: true
|
|
3105
|
+
};
|
|
3106
|
+
_endpointWindows.set(key, s);
|
|
2875
3107
|
}
|
|
2876
|
-
|
|
2877
|
-
`"${value}" is not a date OCS can read. Use YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss \u2014 this field is a java.time.LocalDateTime and a zone offset is rejected.`
|
|
2878
|
-
);
|
|
3108
|
+
return s;
|
|
2879
3109
|
}
|
|
2880
|
-
function
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
for (const entry of packages) {
|
|
2885
|
-
if (!entry || typeof entry !== "object") continue;
|
|
2886
|
-
const rec = entry;
|
|
2887
|
-
const id = rec.subscriberprepaidpackageid ?? rec.subscriberPrepaidPackageId;
|
|
2888
|
-
if (Number(id) !== packageId) continue;
|
|
2889
|
-
const activation = rec.tsactivationutc ?? rec.tsActivationUtc;
|
|
2890
|
-
const expiration = rec.tsexpirationutc ?? rec.tsExpirationUtc;
|
|
2891
|
-
const window = {};
|
|
2892
|
-
if (typeof activation === "string" && activation) window.activation = activation;
|
|
2893
|
-
if (typeof expiration === "string" && expiration) window.expiration = expiration;
|
|
2894
|
-
return window;
|
|
2895
|
-
}
|
|
2896
|
-
return void 0;
|
|
3110
|
+
function _prune(log, windowStart) {
|
|
3111
|
+
let i = 0;
|
|
3112
|
+
while (i < log.length && log[i] <= windowStart) i++;
|
|
3113
|
+
return i > 0 ? log.slice(i) : log;
|
|
2897
3114
|
}
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
if (
|
|
2901
|
-
|
|
2902
|
-
}
|
|
2903
|
-
const activation = change.startDate !== void 0 ? ocsLocalDateTime(change.startDate, "start") : change.current?.activation;
|
|
2904
|
-
const expiration = change.endDate !== void 0 ? ocsLocalDateTime(change.endDate, "end") : change.current?.expiration;
|
|
2905
|
-
for (const [field, value, omitted] of [
|
|
2906
|
-
["newActivationDateUtc", activation, "startDate"],
|
|
2907
|
-
["newExpirationDateUtc", expiration, "endDate"]
|
|
2908
|
-
]) {
|
|
2909
|
-
if (value !== void 0) continue;
|
|
2910
|
-
throw new Error(
|
|
2911
|
-
`OCS requires both bounds on an active-period change and rejects the whole request with "Missing '${field}'" when one is absent. Supply ${omitted}, or read the package's current window first (activePeriodFromPackages) and pass it as \`current\`.`
|
|
2912
|
-
);
|
|
3115
|
+
async function acquireEndpointSlot(resellerId, endpoint, priority = "interactive") {
|
|
3116
|
+
const proc = globalThis["process"];
|
|
3117
|
+
if (proc?.env?.["RATE_FLOOR_DISABLED"] === "true" || globalThis["RATE_FLOOR_DISABLED"] === "true") {
|
|
3118
|
+
return;
|
|
2913
3119
|
}
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
// Already LocalDateTime-shaped when it came from `current`; normalising
|
|
2917
|
-
// again is a no-op and keeps a hand-passed value honest.
|
|
2918
|
-
newActivationDateUtc: ocsLocalDateTime(activation, "start"),
|
|
2919
|
-
newExpirationDateUtc: ocsLocalDateTime(expiration, "end"),
|
|
2920
|
-
comment: change.comment?.trim() || OCS_ACTIVE_PERIOD_DEFAULT_COMMENT
|
|
2921
|
-
};
|
|
3120
|
+
await _acquireOneSlot(resellerId, "__global__", priority);
|
|
3121
|
+
await _acquireOneSlot(resellerId, endpoint, priority);
|
|
2922
3122
|
}
|
|
2923
|
-
function
|
|
2924
|
-
const
|
|
2925
|
-
const
|
|
2926
|
-
const
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
3123
|
+
async function _acquireOneSlot(resellerId, endpoint, priority) {
|
|
3124
|
+
const bucketKey = `${resellerId}:${endpoint}`;
|
|
3125
|
+
const limitPerMin = ENDPOINT_LIMITS_PER_MIN[endpoint] ?? DEFAULT_LIMIT_PER_MIN;
|
|
3126
|
+
const batchCap = Math.floor(limitPerMin * BATCH_FRACTION);
|
|
3127
|
+
const state = _getEndpointState(bucketKey);
|
|
3128
|
+
const ticket = state.gate.then(async () => {
|
|
3129
|
+
while (true) {
|
|
3130
|
+
const now = Date.now();
|
|
3131
|
+
const windowStart = now - WINDOW_MS2;
|
|
3132
|
+
state.log = _prune(state.log, windowStart);
|
|
3133
|
+
state.batchLog = _prune(state.batchLog, windowStart);
|
|
3134
|
+
const totalInWindow = state.log.length;
|
|
3135
|
+
const batchInWindow = state.batchLog.length;
|
|
3136
|
+
if (totalInWindow > 0 && endpoint !== "__global__") {
|
|
3137
|
+
const utilPct = Math.round(totalInWindow / limitPerMin * 100);
|
|
3138
|
+
const intervalOk = now - state.lastMetricEmitAt >= METRIC_EMIT_MIN_INTERVAL_MS;
|
|
3139
|
+
const enteredHighUtil = utilPct >= 80 && state.metricBelowHighUtil;
|
|
3140
|
+
if (intervalOk || enteredHighUtil) {
|
|
3141
|
+
emitMetric(endpoint, resellerId, totalInWindow, limitPerMin);
|
|
3142
|
+
state.lastMetricEmitAt = now;
|
|
3143
|
+
if (utilPct >= 80) {
|
|
3144
|
+
state.metricBelowHighUtil = false;
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
if (utilPct < 80) {
|
|
3148
|
+
state.metricBelowHighUtil = true;
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
const globalFull = totalInWindow >= limitPerMin;
|
|
3152
|
+
const batchFull = priority === "batch" && batchInWindow >= batchCap;
|
|
3153
|
+
if (!globalFull && !batchFull) {
|
|
3154
|
+
state.log.push(now);
|
|
3155
|
+
if (priority === "batch") {
|
|
3156
|
+
state.batchLog.push(now);
|
|
3157
|
+
}
|
|
3158
|
+
return;
|
|
3159
|
+
}
|
|
3160
|
+
let waitMs;
|
|
3161
|
+
if (globalFull) {
|
|
3162
|
+
const oldest = state.log[0];
|
|
3163
|
+
waitMs = WINDOW_MS2 - (now - oldest) + 1;
|
|
3164
|
+
} else {
|
|
3165
|
+
const oldestBatch = state.batchLog[0];
|
|
3166
|
+
waitMs = WINDOW_MS2 - (now - oldestBatch) + 1;
|
|
3167
|
+
}
|
|
3168
|
+
await new Promise((r) => setTimeout(r, waitMs));
|
|
3169
|
+
}
|
|
3170
|
+
});
|
|
3171
|
+
state.gate = ticket;
|
|
3172
|
+
return ticket;
|
|
2937
3173
|
}
|
|
2938
|
-
function
|
|
2939
|
-
return
|
|
3174
|
+
function getLimitForEndpoint(endpoint) {
|
|
3175
|
+
return ENDPOINT_LIMITS_PER_MIN[endpoint] ?? DEFAULT_LIMIT_PER_MIN;
|
|
2940
3176
|
}
|
|
2941
|
-
function
|
|
2942
|
-
const
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
acc.active += count;
|
|
2947
|
-
return;
|
|
2948
|
-
}
|
|
2949
|
-
if (str2 === "SUSPENDED") {
|
|
2950
|
-
acc.suspended += count;
|
|
2951
|
-
return;
|
|
3177
|
+
function getRateLimitWindowCounts(resellerKey, endpoint) {
|
|
3178
|
+
const bucketKey = `${resellerKey}:${endpoint}`;
|
|
3179
|
+
const state = _endpointWindows.get(bucketKey);
|
|
3180
|
+
if (!state) {
|
|
3181
|
+
return { calls_in_window: 0, batch_calls_in_window: 0 };
|
|
2952
3182
|
}
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
3183
|
+
const windowStart = Date.now() - WINDOW_MS2;
|
|
3184
|
+
let calls_in_window = 0;
|
|
3185
|
+
for (const ts of state.log) {
|
|
3186
|
+
if (ts > windowStart) calls_in_window++;
|
|
2956
3187
|
}
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
if (
|
|
2960
|
-
else if (num === 0) acc.inventory += count;
|
|
2961
|
-
else if (num === 2) acc.suspended += count;
|
|
2962
|
-
else acc.other += count;
|
|
2963
|
-
return;
|
|
3188
|
+
let batch_calls_in_window = 0;
|
|
3189
|
+
for (const ts of state.batchLog) {
|
|
3190
|
+
if (ts > windowStart) batch_calls_in_window++;
|
|
2964
3191
|
}
|
|
2965
|
-
|
|
3192
|
+
return { calls_in_window, batch_calls_in_window };
|
|
2966
3193
|
}
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3194
|
+
var OCS_BARE_INT_RESELLER_METHODS = [
|
|
3195
|
+
"listSponsor",
|
|
3196
|
+
"listSteeringList",
|
|
3197
|
+
"listDetailedDestinationList",
|
|
3198
|
+
"listDetailedLocationZone",
|
|
3199
|
+
"getCustomerTariff"
|
|
3200
|
+
];
|
|
3201
|
+
var OCS_BARE_INT_OTHER_METHODS = {
|
|
3202
|
+
listLocationZoneElement: "locationZoneId",
|
|
3203
|
+
listDestinationListPrefix: "destinationListId",
|
|
3204
|
+
listVoipTariffRule: "voipPlanId",
|
|
3205
|
+
getSimProviderStatus: "simId",
|
|
3206
|
+
deleteSubscriberPackage: "packageId"
|
|
3207
|
+
};
|
|
3208
|
+
var OCS_PARAM_RENAMES = {
|
|
3209
|
+
getResellerInfo: { resellerId: "id" },
|
|
3210
|
+
modifyPPTCore: {
|
|
3211
|
+
templateId: "prepaidpackagetemplateid",
|
|
3212
|
+
name: "prepaidpackagetemplatename",
|
|
3213
|
+
volume: "databyte",
|
|
3214
|
+
validityDays: "perioddays"
|
|
3215
|
+
},
|
|
3216
|
+
modifyPPTRecurring: { templateId: "prepaidpackagetemplateid" },
|
|
3217
|
+
modifyPPTThrottling: { templateId: "prepaidpackagetemplateid" },
|
|
3218
|
+
// Console NL-create and the MCP tool both advertise `name` / `volume` /
|
|
3219
|
+
// `validityDays`. OCS createPrepaidPackageTemplate rejects unknown keys
|
|
3220
|
+
// (Jackson code 2) — live failure was Unrecognized field "name".
|
|
3221
|
+
createPrepaidPackageTemplate: {
|
|
3222
|
+
name: "prepaidpackagetemplatename",
|
|
3223
|
+
volume: "databyte",
|
|
3224
|
+
validityDays: "perioddays",
|
|
3225
|
+
dataLimit: "databyte"
|
|
3226
|
+
},
|
|
3227
|
+
createLocationZone: { name: "locationZoneName" }
|
|
3228
|
+
};
|
|
3229
|
+
function applyParamRenames(method, params) {
|
|
3230
|
+
const renames = OCS_PARAM_RENAMES[method];
|
|
3231
|
+
if (!renames || !params || typeof params !== "object" || Array.isArray(params)) {
|
|
3232
|
+
return params;
|
|
2987
3233
|
}
|
|
2988
|
-
|
|
2989
|
-
|
|
3234
|
+
const record = params;
|
|
3235
|
+
let out;
|
|
3236
|
+
for (const [from, to] of Object.entries(renames)) {
|
|
3237
|
+
if (!Object.prototype.hasOwnProperty.call(record, from)) continue;
|
|
3238
|
+
out ??= { ...record };
|
|
3239
|
+
if (!Object.prototype.hasOwnProperty.call(record, to)) out[to] = record[from];
|
|
3240
|
+
delete out[from];
|
|
3241
|
+
}
|
|
3242
|
+
return out ?? params;
|
|
2990
3243
|
}
|
|
2991
|
-
function
|
|
2992
|
-
|
|
2993
|
-
if (args.accountId !== void 0) return { accountId: args.accountId };
|
|
2994
|
-
if (args.resellerId !== void 0) return { resellerId: args.resellerId };
|
|
2995
|
-
throw new Error("esimStatusPerAccountParams needs an accountId or a resellerId");
|
|
3244
|
+
function isBareIntResellerMethod(method) {
|
|
3245
|
+
return OCS_BARE_INT_RESELLER_METHODS.includes(method);
|
|
2996
3246
|
}
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
var TEMPLATE_DATA_KEYS = ["databyte", "dataBytes", "dataLimit"];
|
|
3000
|
-
function readTemplateDataBytes(t) {
|
|
3001
|
-
for (const key of TEMPLATE_DATA_KEYS) {
|
|
3002
|
-
const v = t[key];
|
|
3003
|
-
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
3004
|
-
}
|
|
3005
|
-
return void 0;
|
|
3247
|
+
function isBareIntMethod(method) {
|
|
3248
|
+
return isBareIntResellerMethod(method) || Object.prototype.hasOwnProperty.call(OCS_BARE_INT_OTHER_METHODS, method);
|
|
3006
3249
|
}
|
|
3007
|
-
function
|
|
3008
|
-
if (
|
|
3009
|
-
|
|
3010
|
-
return Math.min(100, Math.max(1, pct2));
|
|
3250
|
+
function bareIntIdName(method) {
|
|
3251
|
+
if (isBareIntResellerMethod(method)) return "resellerId";
|
|
3252
|
+
return OCS_BARE_INT_OTHER_METHODS[method] ?? "id";
|
|
3011
3253
|
}
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
5120,
|
|
3020
|
-
7680,
|
|
3021
|
-
10240,
|
|
3022
|
-
20480,
|
|
3023
|
-
51200,
|
|
3024
|
-
102400
|
|
3025
|
-
];
|
|
3026
|
-
var OCS_PERIODICITY = { DAILY: 0, WEEKLY: 1, MONTHLY: 2 };
|
|
3027
|
-
var OCS_PERIODICITY_VALUES = [0, 1, 2];
|
|
3028
|
-
var CEILING_MULTIPLIERS = [20, 25, 50, 100];
|
|
3029
|
-
var SECONDS_PER_DAY = 86400;
|
|
3030
|
-
function throttledBytesPerPeriod(kbps, periodDays = 1) {
|
|
3031
|
-
if (!Number.isFinite(kbps) || kbps <= 0) return 0;
|
|
3032
|
-
return kbps * 1e3 * SECONDS_PER_DAY * Math.max(1, periodDays) / 8;
|
|
3254
|
+
function assertBareIntParam(method, params) {
|
|
3255
|
+
if (!isBareIntMethod(method)) return;
|
|
3256
|
+
if (typeof params === "number") return;
|
|
3257
|
+
const shape = params === null ? "null" : Array.isArray(params) ? "array" : typeof params === "object" ? "object" : typeof params;
|
|
3258
|
+
throw new TypeError(
|
|
3259
|
+
`OCS method "${method}" takes a BARE INTEGER ${bareIntIdName(method)}, but received ${shape}. Sending an object makes OCS reject the whole request with "Cannot deserialize value of type java.lang.Integer from Object value", which surfaces in the UI as an empty panel. Pass the id itself, e.g. call("${method}", 1170).`
|
|
3260
|
+
);
|
|
3033
3261
|
}
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
}
|
|
3262
|
+
var OcsApiError = class extends Error {
|
|
3263
|
+
constructor(code, message, method, details = {}) {
|
|
3264
|
+
super(`[${method}] OCS error ${code}: ${message}`);
|
|
3265
|
+
this.code = code;
|
|
3266
|
+
this.method = method;
|
|
3267
|
+
this.name = "OcsApiError";
|
|
3268
|
+
this.details = details;
|
|
3042
3269
|
}
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
const throttleAfterMb = typeof out.throttleAfterMb === "number" ? out.throttleAfterMb : void 0;
|
|
3052
|
-
const throttleKbps = typeof out.throttleKbps === "number" ? out.throttleKbps : void 0;
|
|
3053
|
-
const periodDays = typeof out.perioddays === "number" ? out.perioddays : 1;
|
|
3054
|
-
if (throttleKbps !== void 0 && !OCS_THROTTLE_KBPS.includes(throttleKbps)) {
|
|
3055
|
-
throw new Error(
|
|
3056
|
-
`throttleKbps ${throttleKbps} is not an OCS bitrate. Allowed: ${OCS_THROTTLE_KBPS.join(", ")}. This field is a SPEED in Kbps, not a data allowance.`
|
|
3270
|
+
code;
|
|
3271
|
+
method;
|
|
3272
|
+
details;
|
|
3273
|
+
};
|
|
3274
|
+
var OcsUnknownMethodError = class extends Error {
|
|
3275
|
+
constructor(method) {
|
|
3276
|
+
super(
|
|
3277
|
+
`[${method}] is not a known OCS v1 method. It is not in the verified set of ${OCS_V1_METHODS.size} methods (createAccount/createSubscriber do not exist on the OCS). If this is a genuinely new method, add it to OCS_V1_METHODS or construct the client without { strictMethods: true }.`
|
|
3057
3278
|
);
|
|
3279
|
+
this.method = method;
|
|
3280
|
+
this.name = "OcsUnknownMethodError";
|
|
3058
3281
|
}
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3282
|
+
method;
|
|
3283
|
+
};
|
|
3284
|
+
var OcsContractError = class _OcsContractError extends Error {
|
|
3285
|
+
constructor(method, issues, payload) {
|
|
3286
|
+
super(
|
|
3287
|
+
`[${method}] OCS response failed contract validation: ${_OcsContractError.summarize(issues)}`
|
|
3063
3288
|
);
|
|
3289
|
+
this.method = method;
|
|
3290
|
+
this.issues = issues;
|
|
3291
|
+
this.payload = payload;
|
|
3292
|
+
this.name = "OcsContractError";
|
|
3293
|
+
}
|
|
3294
|
+
method;
|
|
3295
|
+
issues;
|
|
3296
|
+
payload;
|
|
3297
|
+
/** One-line, human-readable join of the issues — for logs and the error message. */
|
|
3298
|
+
get summary() {
|
|
3299
|
+
return _OcsContractError.summarize(this.issues);
|
|
3064
3300
|
}
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
"nbOccurrence is required for a daily or weekly recurring template; OCS only treats it as optional for monthly templates."
|
|
3068
|
-
);
|
|
3301
|
+
static summarize(issues) {
|
|
3302
|
+
return issues.map((i) => `${i.path}: ${i.message}`).join("; ");
|
|
3069
3303
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3304
|
+
};
|
|
3305
|
+
function ocsErrorMessage(code, rawMsg, rawBody) {
|
|
3306
|
+
switch (code) {
|
|
3307
|
+
case 12:
|
|
3308
|
+
return {
|
|
3309
|
+
message: "OCS resource is read-only (code 12). The package/subscriber/account state does not allow this mutation. Often returned for end-of-life subscribers or finalized invoices.",
|
|
3310
|
+
details: {}
|
|
3311
|
+
};
|
|
3312
|
+
case 17:
|
|
3313
|
+
return {
|
|
3314
|
+
message: "Subscriber is end-of-life (code 17). It can no longer be mutated. Use modify_subscriber_status to revive before further changes.",
|
|
3315
|
+
details: {}
|
|
3316
|
+
};
|
|
3317
|
+
default:
|
|
3318
|
+
if (code >= 10001 && code <= 10004) {
|
|
3319
|
+
const details = {};
|
|
3320
|
+
if (rawBody["invalidTadigs"] !== void 0) {
|
|
3321
|
+
details.invalidTadigs = rawBody["invalidTadigs"];
|
|
3322
|
+
}
|
|
3323
|
+
if (rawBody["existingLZ"] !== void 0) {
|
|
3324
|
+
details.existingLZ = rawBody["existingLZ"];
|
|
3325
|
+
}
|
|
3326
|
+
return { message: rawMsg || "Location zone validation failed", details };
|
|
3327
|
+
}
|
|
3328
|
+
return { message: rawMsg || "Unknown error", details: {} };
|
|
3083
3329
|
}
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3330
|
+
}
|
|
3331
|
+
var BACKOFF_BASE_MS2 = 500;
|
|
3332
|
+
var BACKOFF_MAX_MS = 4e3;
|
|
3333
|
+
var BACKOFF_MAX_RETRIES = 3;
|
|
3334
|
+
var TRAFFIC_CONTROL_BACKOFF_BASE_MS = 1e3;
|
|
3335
|
+
var TRAFFIC_CONTROL_BACKOFF_MAX_MS = 16e3;
|
|
3336
|
+
var TRAFFIC_CONTROL_MAX_RETRIES = 6;
|
|
3337
|
+
var BACKOFF_JITTER = 0.25;
|
|
3338
|
+
async function retryWithBackoff(fn) {
|
|
3339
|
+
let lastError;
|
|
3340
|
+
let maxRetries = BACKOFF_MAX_RETRIES;
|
|
3341
|
+
let backoffBaseMs = BACKOFF_BASE_MS2;
|
|
3342
|
+
let backoffMaxMs = BACKOFF_MAX_MS;
|
|
3343
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
3344
|
+
if (attempt > 0) {
|
|
3345
|
+
const base = Math.min(backoffBaseMs * Math.pow(2, attempt - 1), backoffMaxMs);
|
|
3346
|
+
const jitter = base * BACKOFF_JITTER * (2 * Math.random() - 1);
|
|
3347
|
+
const delay = Math.max(0, Math.round(base + jitter));
|
|
3348
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
3349
|
+
}
|
|
3350
|
+
try {
|
|
3351
|
+
return await fn();
|
|
3352
|
+
} catch (err) {
|
|
3353
|
+
lastError = err;
|
|
3354
|
+
if (err instanceof OcsApiError) {
|
|
3355
|
+
if (err.code !== 100) throw err;
|
|
3356
|
+
maxRetries = TRAFFIC_CONTROL_MAX_RETRIES;
|
|
3357
|
+
backoffBaseMs = TRAFFIC_CONTROL_BACKOFF_BASE_MS;
|
|
3358
|
+
backoffMaxMs = TRAFFIC_CONTROL_BACKOFF_MAX_MS;
|
|
3359
|
+
}
|
|
3360
|
+
if (err instanceof OcsContractError) {
|
|
3361
|
+
throw err;
|
|
3362
|
+
}
|
|
3363
|
+
if (err instanceof OcsTimeoutError) {
|
|
3364
|
+
throw err;
|
|
3365
|
+
}
|
|
3089
3366
|
}
|
|
3090
|
-
out.throttlingActive = out.throttlingActive ?? true;
|
|
3091
3367
|
}
|
|
3092
|
-
|
|
3093
|
-
return out;
|
|
3368
|
+
throw lastError;
|
|
3094
3369
|
}
|
|
3095
|
-
function
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3370
|
+
function isAbortError(err) {
|
|
3371
|
+
return err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError" || /aborted|timed? ?out/i.test(err.message));
|
|
3372
|
+
}
|
|
3373
|
+
var OcsClient = class {
|
|
3374
|
+
/**
|
|
3375
|
+
* @param baseUrl OCS base URL without trailing slash (e.g. https://ocs.esimvault.cloud).
|
|
3376
|
+
* @param _defaultToken Default API token; identifies the reseller for rate-floor keying.
|
|
3377
|
+
* @param _priority Call priority: "interactive" (MCP tool calls, 80% capacity) or
|
|
3378
|
+
* "batch" (cron/poll callers, 20% capacity). Default: "interactive".
|
|
3379
|
+
* @param options Contract-enforcement opt-outs (see `OcsClientOptions`).
|
|
3380
|
+
*/
|
|
3381
|
+
constructor(baseUrl, _defaultToken, _priority = "interactive", options = {}) {
|
|
3382
|
+
this._defaultToken = _defaultToken;
|
|
3383
|
+
this._priority = _priority;
|
|
3384
|
+
let end = baseUrl.length;
|
|
3385
|
+
while (end > 0 && baseUrl.charCodeAt(end - 1) === 47) end--;
|
|
3386
|
+
this.baseUrl = baseUrl.slice(0, end);
|
|
3387
|
+
this._strictMethods = options.strictMethods ?? false;
|
|
3388
|
+
this._validateResponses = options.validateResponses ?? true;
|
|
3389
|
+
this._strictValidation = options.strictValidation ?? false;
|
|
3104
3390
|
}
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3391
|
+
_defaultToken;
|
|
3392
|
+
_priority;
|
|
3393
|
+
baseUrl;
|
|
3394
|
+
_strictMethods;
|
|
3395
|
+
_validateResponses;
|
|
3396
|
+
_strictValidation;
|
|
3397
|
+
// Per-instance (NOT static): a static set would dedup across requests in a
|
|
3398
|
+
// reused Worker isolate and silently swallow recurring contract-drift warnings.
|
|
3399
|
+
_warnedMethods = /* @__PURE__ */ new Set();
|
|
3400
|
+
_warnedValidation = /* @__PURE__ */ new Set();
|
|
3401
|
+
async call(method, params = {}, token) {
|
|
3402
|
+
if (!OCS_V1_METHODS.has(method)) {
|
|
3403
|
+
if (this._strictMethods) throw new OcsUnknownMethodError(method);
|
|
3404
|
+
if (!this._warnedMethods.has(method)) {
|
|
3405
|
+
this._warnedMethods.add(method);
|
|
3406
|
+
console.warn(
|
|
3407
|
+
`[ocs-client] method "${method}" is not in the verified OCS v1 set \u2014 it may not exist on this OCS instance (see OCS_V1_METHODS).`
|
|
3408
|
+
);
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
const tok = token ?? this._defaultToken;
|
|
3412
|
+
if (!tok) {
|
|
3413
|
+
throw new OcsApiError(-1, "No OCS API token provided", method);
|
|
3414
|
+
}
|
|
3415
|
+
const url = `${this.baseUrl}/v1?token=${tok}`;
|
|
3416
|
+
assertBareIntParam(method, params);
|
|
3417
|
+
const wireParams = applyParamRenames(method, params);
|
|
3418
|
+
const body = JSON.stringify({ [method]: wireParams });
|
|
3419
|
+
return retryWithBackoff(async () => {
|
|
3420
|
+
await acquireToken(tok);
|
|
3421
|
+
await acquireEndpointSlot(tok, method, this._priority);
|
|
3422
|
+
return runWithBudget(method, async (signal, attemptMs) => {
|
|
3423
|
+
let res;
|
|
3424
|
+
try {
|
|
3425
|
+
res = await fetch(url, {
|
|
3426
|
+
method: "POST",
|
|
3427
|
+
headers: { "Content-Type": "application/json" },
|
|
3428
|
+
body,
|
|
3429
|
+
signal
|
|
3430
|
+
});
|
|
3431
|
+
} catch (err) {
|
|
3432
|
+
if (isAbortError(err)) {
|
|
3433
|
+
throw new OcsTimeoutError(method, attemptMs);
|
|
3434
|
+
}
|
|
3435
|
+
throw err;
|
|
3436
|
+
}
|
|
3437
|
+
if (res.status >= 500) {
|
|
3438
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
3439
|
+
}
|
|
3440
|
+
if (!res.ok) {
|
|
3441
|
+
throw new OcsApiError(
|
|
3442
|
+
res.status,
|
|
3443
|
+
`HTTP ${res.status} ${res.statusText}`,
|
|
3444
|
+
method
|
|
3445
|
+
);
|
|
3446
|
+
}
|
|
3447
|
+
let json;
|
|
3448
|
+
try {
|
|
3449
|
+
json = await res.json();
|
|
3450
|
+
} catch (err) {
|
|
3451
|
+
if (isAbortError(err)) {
|
|
3452
|
+
throw new OcsTimeoutError(method, attemptMs);
|
|
3453
|
+
}
|
|
3454
|
+
throw err;
|
|
3455
|
+
}
|
|
3456
|
+
if (json.status?.code !== 0) {
|
|
3457
|
+
const ocsCode = json.status?.code ?? -1;
|
|
3458
|
+
const rawMsg = json.status?.msg ?? "Unknown error";
|
|
3459
|
+
const rawBody = json;
|
|
3460
|
+
const { message, details } = ocsErrorMessage(ocsCode, rawMsg, rawBody);
|
|
3461
|
+
throw new OcsApiError(ocsCode, message, method, details);
|
|
3462
|
+
}
|
|
3463
|
+
let payload;
|
|
3464
|
+
if (method === "getCustomerTariff" && json["listTariffRule"] !== void 0) {
|
|
3465
|
+
payload = json["listTariffRule"];
|
|
3466
|
+
} else if (method === "getSubscriberLocationByCellId") {
|
|
3467
|
+
const byMethod = json[method];
|
|
3468
|
+
if (byMethod !== void 0) {
|
|
3469
|
+
payload = byMethod;
|
|
3470
|
+
} else if (json["subscriberLocation"] !== void 0) {
|
|
3471
|
+
payload = json["subscriberLocation"];
|
|
3472
|
+
} else {
|
|
3473
|
+
payload = json[method] ?? json;
|
|
3474
|
+
}
|
|
3475
|
+
} else if (json[method] !== void 0) {
|
|
3476
|
+
payload = json[method];
|
|
3477
|
+
} else if (Object.keys(json).some((k) => k !== "status")) {
|
|
3478
|
+
payload = json;
|
|
3479
|
+
} else {
|
|
3480
|
+
payload = null;
|
|
3481
|
+
}
|
|
3482
|
+
if (this._validateResponses) {
|
|
3483
|
+
const schema = RESPONSE_SCHEMAS[method];
|
|
3484
|
+
if (schema) {
|
|
3485
|
+
const parsed = schema.safeParse(payload);
|
|
3486
|
+
if (!parsed.success) {
|
|
3487
|
+
const issues = parsed.error.issues.map((i) => ({
|
|
3488
|
+
path: i.path.join(".") || "<root>",
|
|
3489
|
+
message: i.message
|
|
3490
|
+
}));
|
|
3491
|
+
if (this._strictValidation) {
|
|
3492
|
+
throw new OcsContractError(method, issues, payload);
|
|
3493
|
+
}
|
|
3494
|
+
if (!this._warnedValidation.has(method)) {
|
|
3495
|
+
this._warnedValidation.add(method);
|
|
3496
|
+
console.warn(`[ocs-client] ${new OcsContractError(method, issues, payload).message}`);
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
return payload;
|
|
3502
|
+
});
|
|
3503
|
+
});
|
|
3110
3504
|
}
|
|
3111
|
-
|
|
3112
|
-
return out;
|
|
3113
|
-
}
|
|
3505
|
+
};
|
|
3114
3506
|
|
|
3115
3507
|
// ../../packages/ocs-spec/src/mcc-iso.ts
|
|
3116
3508
|
var MCC_TO_ISO = {
|
|
@@ -3929,20 +4321,23 @@ var NETWORK_EVENT_KEYS = [
|
|
|
3929
4321
|
"mocAnswer",
|
|
3930
4322
|
"mtcAnswer"
|
|
3931
4323
|
];
|
|
4324
|
+
function positiveSimId(raw) {
|
|
4325
|
+
const n = typeof raw === "number" ? raw : typeof raw === "string" && raw.trim() !== "" ? Number(raw) : NaN;
|
|
4326
|
+
return Number.isSafeInteger(n) && n > 0 ? n : void 0;
|
|
4327
|
+
}
|
|
3932
4328
|
function subscriberSimId(record) {
|
|
3933
4329
|
if (!record || typeof record !== "object") return void 0;
|
|
3934
4330
|
const rec = record;
|
|
3935
4331
|
const sim = rec.sim;
|
|
3936
4332
|
if (sim && typeof sim === "object") {
|
|
3937
|
-
const
|
|
3938
|
-
|
|
3939
|
-
if (
|
|
3940
|
-
}
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
}
|
|
4333
|
+
const simRec = sim;
|
|
4334
|
+
const fromSim = positiveSimId(simRec.id) ?? positiveSimId(simRec.simId) ?? positiveSimId(simRec.sim_id);
|
|
4335
|
+
if (fromSim !== void 0) return fromSim;
|
|
4336
|
+
}
|
|
4337
|
+
const flat = positiveSimId(rec.simId) ?? positiveSimId(rec.sim_id);
|
|
4338
|
+
if (flat !== void 0) return flat;
|
|
4339
|
+
const nested = rec.subscriber;
|
|
4340
|
+
if (nested && typeof nested === "object" && nested !== record) return subscriberSimId(nested);
|
|
3946
4341
|
return void 0;
|
|
3947
4342
|
}
|
|
3948
4343
|
function prepaidPackageRows(data) {
|
|
@@ -4274,10 +4669,6 @@ async function generateStorefrontLogo(input) {
|
|
|
4274
4669
|
}
|
|
4275
4670
|
|
|
4276
4671
|
export {
|
|
4277
|
-
getLimitForEndpoint,
|
|
4278
|
-
getRateLimitWindowCounts,
|
|
4279
|
-
OcsApiError,
|
|
4280
|
-
OcsClient,
|
|
4281
4672
|
OCS_MAX_USAGE_WINDOW_DAYS,
|
|
4282
4673
|
clampUsagePeriod,
|
|
4283
4674
|
lastNDaysPeriod,
|
|
@@ -4294,8 +4685,16 @@ export {
|
|
|
4294
4685
|
recurringIdForPackage,
|
|
4295
4686
|
extractEsimStatusCounts,
|
|
4296
4687
|
esimStatusPerAccountParams,
|
|
4297
|
-
|
|
4298
|
-
|
|
4688
|
+
ocsHlrBitrate,
|
|
4689
|
+
ocsHlrBitrateFromBps,
|
|
4690
|
+
ocsSimStatus,
|
|
4691
|
+
locationZoneCreateParams,
|
|
4692
|
+
packageTemplateCreateFromToolJson,
|
|
4693
|
+
packageTemplateModifyFromToolJson,
|
|
4694
|
+
getLimitForEndpoint,
|
|
4695
|
+
getRateLimitWindowCounts,
|
|
4696
|
+
OcsApiError,
|
|
4697
|
+
OcsClient,
|
|
4299
4698
|
mccToIso,
|
|
4300
4699
|
buildListSubscriberParams,
|
|
4301
4700
|
extractSubscriberStatus,
|
|
@@ -4359,4 +4758,4 @@ export {
|
|
|
4359
4758
|
storefrontScreen,
|
|
4360
4759
|
CARRIER_VERSION
|
|
4361
4760
|
};
|
|
4362
|
-
//# sourceMappingURL=chunk-
|
|
4761
|
+
//# sourceMappingURL=chunk-DUAXD7GD.js.map
|