@carrierllc/mcp 0.13.29 → 0.13.30
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 +137 -1
- package/dist/{chunk-LJPYVILK.js → chunk-ZJY7H2QW.js} +12 -3
- package/dist/chunk-ZJY7H2QW.js.map +1 -0
- package/dist/cli.js +937 -330
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/native/claude-keychain.js +57 -0
- package/package.json +10 -2
- package/plugin/.claude-plugin/marketplace.json +2 -2
- package/plugin/carrier/.claude-plugin/plugin.json +1 -1
- package/shell/carrier.bash +56 -0
- package/shell/carrier.zsh +35 -0
- package/templates/storefront/open-next.config.ts +8 -1
- package/templates/storefront/wrangler.jsonc +1 -1
- package/dist/chunk-LJPYVILK.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// macOS built-in JXA Objective-C bridge. No compiler, downloads or owner actions.
|
|
2
|
+
// Apple SecItem.h: AuthenticationUIFail means errSecInteractionNotAllowed, NOT skip.
|
|
3
|
+
ObjC.import('Foundation');
|
|
4
|
+
ObjC.import('Security');
|
|
5
|
+
ObjC.bindFunction('SecKeychainSetUserInteractionAllowed', ['int', ['bool']]);
|
|
6
|
+
ObjC.bindFunction('SecItemCopyMatching', ['int', ['id', 'id *']]);
|
|
7
|
+
function run() {
|
|
8
|
+
var output = $.NSFileHandle.alloc.initWithFileDescriptorCloseOnDealloc(3, false);
|
|
9
|
+
function send(result) {
|
|
10
|
+
var data = $(JSON.stringify(result)).dataUsingEncoding($.NSUTF8StringEncoding);
|
|
11
|
+
output.writeData(data);
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
var input = $.NSFileHandle.fileHandleWithStandardInput.readDataToEndOfFile;
|
|
15
|
+
if (Number(input.length) > 16384) { send({status:'malformed'}); return; }
|
|
16
|
+
var request = JSON.parse(ObjC.unwrap($.NSString.alloc.initWithDataEncoding(input, $.NSUTF8StringEncoding)));
|
|
17
|
+
if (typeof request.service !== 'string' || !/^Claude Code(?:-custom-oauth)?-credentials(?:-[a-f0-9]{8})?$/.test(request.service) ||
|
|
18
|
+
typeof request.account !== 'string' || !/^[a-zA-Z0-9._-]+$/.test(request.account) ||
|
|
19
|
+
typeof request.key !== 'string' || !/^[a-zA-Z0-9_-]+\|[a-f0-9]{16}$/.test(request.key)) {
|
|
20
|
+
send({status:'malformed'}); return;
|
|
21
|
+
}
|
|
22
|
+
// Global legacy-Keychain UI inhibition is process-local. This child never restores
|
|
23
|
+
// it to true; SecItem's modern auth path independently uses AuthenticationUIFail.
|
|
24
|
+
if ($.SecKeychainSetUserInteractionAllowed(false) !== 0) { send({status:'unavailable'}); return; }
|
|
25
|
+
var query = $.NSMutableDictionary.alloc.init;
|
|
26
|
+
query.setObjectForKey(ObjC.castRefToObject($.kSecClassGenericPassword), ObjC.castRefToObject($.kSecClass));
|
|
27
|
+
query.setObjectForKey($(request.service), ObjC.castRefToObject($.kSecAttrService));
|
|
28
|
+
query.setObjectForKey($(request.account), ObjC.castRefToObject($.kSecAttrAccount));
|
|
29
|
+
query.setObjectForKey(ObjC.castRefToObject($.kSecMatchLimitOne), ObjC.castRefToObject($.kSecMatchLimit));
|
|
30
|
+
query.setObjectForKey($.NSNumber.numberWithBool(true), ObjC.castRefToObject($.kSecReturnData));
|
|
31
|
+
query.setObjectForKey(ObjC.castRefToObject($.kSecUseAuthenticationUIFail), ObjC.castRefToObject($.kSecUseAuthenticationUI));
|
|
32
|
+
if (Number(query.count) !== 6) { send({status:'malformed'}); return; }
|
|
33
|
+
var result = Ref();
|
|
34
|
+
var code = $.SecItemCopyMatching(query, result);
|
|
35
|
+
if (code !== 0) {
|
|
36
|
+
send({status: code === -25300 ? 'absent' : code === -25308 ? 'locked' :
|
|
37
|
+
code === -25293 || code === -128 ? 'denied' : 'unavailable'}); return;
|
|
38
|
+
}
|
|
39
|
+
var data = ObjC.castRefToObject(result[0]);
|
|
40
|
+
if (!data || Number(data.length) > 1048576) { send({status:'malformed'}); return; }
|
|
41
|
+
var text = ObjC.unwrap($.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding));
|
|
42
|
+
var root = JSON.parse(text), entry = root && root.mcpOAuth && root.mcpOAuth[request.key];
|
|
43
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) { send({status:'absent-entry'}); return; }
|
|
44
|
+
// Never transport unrelated credentials or the owner's rotating/registration secrets.
|
|
45
|
+
var selected = {};
|
|
46
|
+
['serverName','serverUrl','issuer','resource','accessToken','expiresAt','scope','clientId',
|
|
47
|
+
'organizationId','organization_id','orgId','accountId'].forEach(function(k) {
|
|
48
|
+
if (Object.prototype.hasOwnProperty.call(entry,k)) selected[k] = entry[k];
|
|
49
|
+
});
|
|
50
|
+
if (entry.discoveryState) {
|
|
51
|
+
selected.discoveryState = {authorizationServerUrl:entry.discoveryState.authorizationServerUrl,
|
|
52
|
+
resourceMetadataUrl:entry.discoveryState.resourceMetadataUrl};
|
|
53
|
+
}
|
|
54
|
+
send({status:'ok',entry:selected});
|
|
55
|
+
} catch (_) { send({status:'malformed'}); }
|
|
56
|
+
// Nothing on stdout/stderr. Only the inherited private pipe receives the result.
|
|
57
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carrierllc/mcp",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.30",
|
|
4
4
|
"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).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Carrier (Lifecycle Innovations Limited)",
|
|
7
7
|
"homepage": "https://mcp.carrier.llc",
|
|
8
8
|
"type": "module",
|
|
9
|
+
"carrierCompatibility": {
|
|
10
|
+
"claudeOAuthOwnerVersion": "2.1.277",
|
|
11
|
+
"claudeOAuthOwnerVersions": ["2.1.277", "2.1.296"]
|
|
12
|
+
},
|
|
9
13
|
"main": "./dist/index.js",
|
|
10
14
|
"files": [
|
|
11
15
|
"dist",
|
|
12
16
|
"plugin",
|
|
17
|
+
"shell",
|
|
18
|
+
"native",
|
|
13
19
|
"README.md",
|
|
14
20
|
"templates",
|
|
15
21
|
"!templates/**/node_modules",
|
|
@@ -27,7 +33,9 @@
|
|
|
27
33
|
"lint": "eslint src",
|
|
28
34
|
"prepublishOnly": "pnpm run build",
|
|
29
35
|
"build:test": "tsup --config tsup.test.config.ts",
|
|
30
|
-
"test": "pnpm run build && pnpm run build:test && node --test test/*.test.js",
|
|
36
|
+
"test": "pnpm run build && pnpm run build:test && node --test-concurrency=1 --test test/*.test.js && pnpm run test:oauth-repair",
|
|
37
|
+
"test:oauth-repair": "node --experimental-transform-types --test-concurrency=1 --test test/oauth-repair.test.mjs test/claude-owner.test.mjs test/current-owner.test.mjs",
|
|
38
|
+
"test:oauth-shell": "node scripts/check-oauth-shell.mjs",
|
|
31
39
|
"check:pack": "node scripts/check-pack-size.mjs",
|
|
32
40
|
"generate:domains": "node scripts/generate-domains.mjs"
|
|
33
41
|
},
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Carrier — natural-language MVNO/eSIM fleet control + white-label storefront for Claude Code.",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.5"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "carrier",
|
|
14
14
|
"source": "./carrier",
|
|
15
15
|
"description": "Provision eSIMs, manage subscribers/packages/billing/usage/SMS/churn/fleet, and roll out a white-labeled storefront — all in natural language via the Carrier MCP.",
|
|
16
|
-
"version": "0.1.
|
|
16
|
+
"version": "0.1.5",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Lifecycle Innovations Limited",
|
|
19
19
|
"url": "https://carrier.llc"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carrier",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Natural-language control of your MVNO/eSIM fleet — the Stripe of telecom. Provision eSIMs, manage subscribers, packages, billing, usage, SMS, churn, and fleet health, and roll out a white-labeled storefront. Wraps the Carrier MCP (130 tools over OAuth 2.1).",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Lifecycle Innovations Limited",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Static, explicitly sourced bash integration. No startup edit or secret eval.
|
|
2
|
+
# Needed to see caller ERR traps; opt-in loading enables Bash errtrace.
|
|
3
|
+
set -E
|
|
4
|
+
# Refuse command traps rather than executing them with newly loaded secrets.
|
|
5
|
+
carrier_auth_export() {
|
|
6
|
+
set +x +v
|
|
7
|
+
local _carrier_fd _carrier_token _carrier_origin _carrier_expiry _carrier_handoff _carrier_binding _carrier_rc=0
|
|
8
|
+
if [[ -n $(trap -p) ]]; then
|
|
9
|
+
printf '%s\n' 'Carrier export refused: remove shell traps first.' >&2
|
|
10
|
+
# Do not restore tracing into an unsafe trap context.
|
|
11
|
+
return 1
|
|
12
|
+
fi
|
|
13
|
+
# A replaced API key remains explicit; the resolver ignores a stale fingerprint.
|
|
14
|
+
# Bash 3.2 (macOS /bin/bash) has no {fd} allocation. A fifo keeps the handoff
|
|
15
|
+
# off the command line and off a world-readable temp file.
|
|
16
|
+
local _carrier_dir _carrier_pid
|
|
17
|
+
_carrier_dir=$(mktemp -d "${TMPDIR:-/tmp}/carrier-handoff.XXXXXX") || return 1
|
|
18
|
+
mkfifo "$_carrier_dir/pipe" || { rm -rf "$_carrier_dir"; return 1; }
|
|
19
|
+
command carrier credential-handoff >"$_carrier_dir/pipe" 3>&1 1>/dev/null &
|
|
20
|
+
_carrier_pid=$!
|
|
21
|
+
# One open reader. Separate redirections close the FIFO after the first
|
|
22
|
+
# line, so a fast writer drops the rest and the next read blocks forever.
|
|
23
|
+
# Bash 3.2 has no {fd} allocation; a compound redirect stays open for all five.
|
|
24
|
+
{
|
|
25
|
+
IFS= read -r _carrier_token || _carrier_rc=1
|
|
26
|
+
IFS= read -r _carrier_origin || _carrier_rc=1
|
|
27
|
+
IFS= read -r _carrier_expiry || _carrier_rc=1
|
|
28
|
+
IFS= read -r _carrier_handoff || _carrier_rc=1
|
|
29
|
+
IFS= read -r _carrier_binding || _carrier_rc=1
|
|
30
|
+
} <"$_carrier_dir/pipe"
|
|
31
|
+
if [[ $_carrier_rc -ne 0 ]]; then
|
|
32
|
+
:
|
|
33
|
+
elif [[ ! $_carrier_expiry =~ ^[0-9]+$ || -z $_carrier_token ]]; then
|
|
34
|
+
_carrier_rc=1
|
|
35
|
+
else
|
|
36
|
+
export CARRIER_API_KEY="$_carrier_token"
|
|
37
|
+
export CARRIER_OAUTH_ORIGIN="$_carrier_origin" CARRIER_OAUTH_EXPIRES_AT="$_carrier_expiry"
|
|
38
|
+
if [[ $_carrier_handoff == - ]]; then unset CARRIER_OAUTH_HANDOFF CARRIER_OAUTH_BINDING
|
|
39
|
+
else export CARRIER_OAUTH_HANDOFF="$_carrier_handoff" CARRIER_OAUTH_BINDING="$_carrier_binding"; fi
|
|
40
|
+
fi
|
|
41
|
+
wait "$_carrier_pid" || _carrier_rc=1
|
|
42
|
+
rm -rf "$_carrier_dir"
|
|
43
|
+
unset _carrier_token
|
|
44
|
+
# Xtrace remains disabled after successful export: the next caller command
|
|
45
|
+
# could expand the environment. Re-enable only knowingly, outside this helper.
|
|
46
|
+
[[ $_carrier_rc == 0 ]] || printf '%s\n' 'Carrier export failed; no environment was changed.' >&2
|
|
47
|
+
return "$_carrier_rc"
|
|
48
|
+
}
|
|
49
|
+
# Trace attribute makes caller DEBUG/RETURN traps visible for fail-closed refusal.
|
|
50
|
+
declare -ft carrier_auth_export
|
|
51
|
+
carrier_auth_clear() {
|
|
52
|
+
set +x +v
|
|
53
|
+
if [[ -n $(trap -p) ]]; then return 1; fi
|
|
54
|
+
unset CARRIER_API_KEY CARRIER_OAUTH_ORIGIN CARRIER_OAUTH_EXPIRES_AT CARRIER_OAUTH_HANDOFF CARRIER_OAUTH_BINDING
|
|
55
|
+
}
|
|
56
|
+
declare -ft carrier_auth_clear
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Static, explicitly sourced zsh integration. No startup edit or secret eval.
|
|
2
|
+
carrier_auth_export() {
|
|
3
|
+
# localoptions restores options on return, so deliberately do not use it:
|
|
4
|
+
# tracing stays off after export to protect the next caller expansion.
|
|
5
|
+
unsetopt xtrace verbose
|
|
6
|
+
if (( $+functions[TRAPDEBUG] || $+functions[TRAPZERR] )) || [[ -n $(trap) ]]; then
|
|
7
|
+
print -u2 -- 'Carrier export refused: remove command traps first.'
|
|
8
|
+
return 1
|
|
9
|
+
fi
|
|
10
|
+
local _carrier_fd _carrier_token _carrier_origin _carrier_expiry _carrier_handoff _carrier_binding _carrier_rc=0
|
|
11
|
+
exec {_carrier_fd}< <(command carrier credential-handoff 3>&1 1>/dev/null)
|
|
12
|
+
if ! IFS= read -r _carrier_token <&$_carrier_fd ||
|
|
13
|
+
! IFS= read -r _carrier_origin <&$_carrier_fd ||
|
|
14
|
+
! IFS= read -r _carrier_expiry <&$_carrier_fd ||
|
|
15
|
+
! IFS= read -r _carrier_handoff <&$_carrier_fd ||
|
|
16
|
+
! IFS= read -r _carrier_binding <&$_carrier_fd; then
|
|
17
|
+
_carrier_rc=1
|
|
18
|
+
elif [[ $_carrier_expiry != <-> || -z $_carrier_token ]]; then
|
|
19
|
+
_carrier_rc=1
|
|
20
|
+
else
|
|
21
|
+
export CARRIER_API_KEY="$_carrier_token"
|
|
22
|
+
export CARRIER_OAUTH_ORIGIN="$_carrier_origin" CARRIER_OAUTH_EXPIRES_AT="$_carrier_expiry"
|
|
23
|
+
if [[ $_carrier_handoff == - ]]; then unset CARRIER_OAUTH_HANDOFF CARRIER_OAUTH_BINDING
|
|
24
|
+
else export CARRIER_OAUTH_HANDOFF="$_carrier_handoff" CARRIER_OAUTH_BINDING="$_carrier_binding"; fi
|
|
25
|
+
fi
|
|
26
|
+
exec {_carrier_fd}<&-
|
|
27
|
+
unset _carrier_token
|
|
28
|
+
(( _carrier_rc == 0 )) || print -u2 -- 'Carrier export failed; no environment was changed.'
|
|
29
|
+
return $_carrier_rc
|
|
30
|
+
}
|
|
31
|
+
carrier_auth_clear() {
|
|
32
|
+
unsetopt xtrace verbose
|
|
33
|
+
if (( $+functions[TRAPDEBUG] || $+functions[TRAPZERR] )) || [[ -n $(trap) ]]; then return 1; fi
|
|
34
|
+
unset CARRIER_API_KEY CARRIER_OAUTH_ORIGIN CARRIER_OAUTH_EXPIRES_AT CARRIER_OAUTH_HANDOFF CARRIER_OAUTH_BINDING
|
|
35
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const config = {
|
|
4
|
+
...defineCloudflareConfig({}),
|
|
5
|
+
// Run the installed CLI directly: nested npm reads pnpm-only environment
|
|
6
|
+
// settings and warns. This is the same Next build, not a skipped build.
|
|
7
|
+
buildCommand: "node node_modules/next/dist/bin/next build",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default config;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "node_modules/wrangler/config-schema.json",
|
|
3
3
|
"main": ".open-next/worker.js",
|
|
4
4
|
"name": "storefront",
|
|
5
|
-
"compatibility_date": "
|
|
5
|
+
"compatibility_date": "2026-09-26",
|
|
6
6
|
"compatibility_flags": ["nodejs_compat"],
|
|
7
7
|
"assets": {
|
|
8
8
|
"directory": ".open-next/assets",
|