@aigne/afs-tesla 1.11.0-beta.12

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/LICENSE.md ADDED
@@ -0,0 +1,26 @@
1
+ # Proprietary License
2
+
3
+ Copyright (c) 2024-2025 ArcBlock, Inc. All Rights Reserved.
4
+
5
+ This software and associated documentation files (the "Software") are proprietary
6
+ and confidential. Unauthorized copying, modification, distribution, or use of
7
+ this Software, via any medium, is strictly prohibited.
8
+
9
+ The Software is provided for internal use only within ArcBlock, Inc. and its
10
+ authorized affiliates.
11
+
12
+ ## No License Granted
13
+
14
+ No license, express or implied, is granted to any party for any purpose.
15
+ All rights are reserved by ArcBlock, Inc.
16
+
17
+ ## Public Artifact Distribution
18
+
19
+ Portions of this Software may be released publicly under separate open-source
20
+ licenses (such as MIT License) through designated public repositories. Such
21
+ public releases are governed by their respective licenses and do not affect
22
+ the proprietary nature of this repository.
23
+
24
+ ## Contact
25
+
26
+ For licensing inquiries, contact: legal@arcblock.io
@@ -0,0 +1,11 @@
1
+
2
+ //#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
3
+ function __decorate(decorators, target, key, desc) {
4
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
7
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
8
+ }
9
+
10
+ //#endregion
11
+ exports.__decorate = __decorate;
@@ -0,0 +1,10 @@
1
+ //#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
2
+ function __decorate(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ }
8
+
9
+ //#endregion
10
+ export { __decorate };
package/dist/auth.cjs ADDED
@@ -0,0 +1,146 @@
1
+ let _aigne_afs = require("@aigne/afs");
2
+
3
+ //#region src/auth.ts
4
+ /**
5
+ * Tesla OAuth2 Authorization Code Flow
6
+ *
7
+ * Implements Tesla Fleet API authentication using standard OAuth2
8
+ * Authorization Code flow (NOT PKCE).
9
+ *
10
+ * Auth domain: https://fleet-auth.prd.vn.cloud.tesla.com
11
+ * Scopes: openid offline_access vehicle_device_data vehicle_cmds
12
+ * vehicle_charging_cmds energy_device_data energy_cmds vehicle_location
13
+ */
14
+ const TESLA_AUTH_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize";
15
+ const TESLA_TOKEN_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token";
16
+ const TESLA_SCOPES = [
17
+ "openid",
18
+ "offline_access",
19
+ "vehicle_device_data",
20
+ "vehicle_cmds",
21
+ "vehicle_charging_cmds",
22
+ "energy_device_data",
23
+ "energy_cmds",
24
+ "vehicle_location"
25
+ ];
26
+ const REGION_BASE_URLS = {
27
+ na: "https://fleet-api.prd.na.vn.cloud.tesla.com",
28
+ eu: "https://fleet-api.prd.eu.vn.cloud.tesla.com",
29
+ cn: "https://fleet-api.prd.cn.vn.cloud.tesla.com"
30
+ };
31
+ const DEFAULT_REDIRECT_URI = "https://localhost:8443/callback";
32
+ /**
33
+ * Generate the OAuth2 authorization URL for user consent.
34
+ *
35
+ * The user should be redirected to this URL to grant access.
36
+ * After granting, Tesla redirects to the redirectUri with an authorization code.
37
+ */
38
+ function getAuthorizationUrl(options) {
39
+ const { clientId, redirectUri = DEFAULT_REDIRECT_URI, scopes = [...TESLA_SCOPES] } = options;
40
+ const state = options.state ?? generateState();
41
+ return {
42
+ url: `${TESLA_AUTH_URL}?${new URLSearchParams({
43
+ response_type: "code",
44
+ client_id: clientId,
45
+ redirect_uri: redirectUri,
46
+ scope: scopes.join(" "),
47
+ state
48
+ }).toString()}`,
49
+ state
50
+ };
51
+ }
52
+ /**
53
+ * Exchange an authorization code for access and refresh tokens.
54
+ *
55
+ * This should be called after the user has been redirected back
56
+ * from Tesla's authorization page with an authorization code.
57
+ */
58
+ async function exchangeCode(options) {
59
+ const { code, clientId, clientSecret, redirectUri = DEFAULT_REDIRECT_URI, _testAuthBaseUrl } = options;
60
+ const tokenUrl = _testAuthBaseUrl ? `${_testAuthBaseUrl}/oauth2/v3/token` : TESLA_TOKEN_URL;
61
+ const body = new URLSearchParams({
62
+ grant_type: "authorization_code",
63
+ code,
64
+ client_id: clientId,
65
+ client_secret: clientSecret,
66
+ redirect_uri: redirectUri
67
+ });
68
+ let response;
69
+ try {
70
+ response = await fetch(tokenUrl, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
73
+ body: body.toString()
74
+ });
75
+ } catch {
76
+ throw new _aigne_afs.AFSError("Failed to connect to Tesla auth server", "AFS_TIMEOUT");
77
+ }
78
+ if (!response.ok) throw new _aigne_afs.AFSError(`Tesla auth error: ${(await response.json().catch(() => ({}))).error_description || "Token exchange failed"}`, "AFS_AUTH_ERROR");
79
+ const data = await response.json();
80
+ return {
81
+ accessToken: data.access_token,
82
+ refreshToken: data.refresh_token,
83
+ expiresIn: data.expires_in,
84
+ tokenType: data.token_type || "Bearer"
85
+ };
86
+ }
87
+ /**
88
+ * Refresh an expired access token using a refresh token.
89
+ */
90
+ async function refreshToken(options) {
91
+ const { refreshToken: refreshTokenValue, clientId, clientSecret, _testAuthBaseUrl } = options;
92
+ const tokenUrl = _testAuthBaseUrl ? `${_testAuthBaseUrl}/oauth2/v3/token` : TESLA_TOKEN_URL;
93
+ const body = new URLSearchParams({
94
+ grant_type: "refresh_token",
95
+ refresh_token: refreshTokenValue,
96
+ client_id: clientId,
97
+ client_secret: clientSecret
98
+ });
99
+ let response;
100
+ try {
101
+ response = await fetch(tokenUrl, {
102
+ method: "POST",
103
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
104
+ body: body.toString()
105
+ });
106
+ } catch {
107
+ throw new _aigne_afs.AFSError("Failed to connect to Tesla auth server", "AFS_TIMEOUT");
108
+ }
109
+ if (!response.ok) throw new _aigne_afs.AFSError(`Tesla auth error: ${(await response.json().catch(() => ({}))).error_description || "Token refresh failed"}`, "AFS_AUTH_ERROR");
110
+ const data = await response.json();
111
+ return {
112
+ accessToken: data.access_token,
113
+ refreshToken: data.refresh_token,
114
+ expiresIn: data.expires_in,
115
+ tokenType: data.token_type || "Bearer"
116
+ };
117
+ }
118
+ /**
119
+ * Get the Fleet API base URL for a given region.
120
+ */
121
+ function getBaseUrl(region) {
122
+ return REGION_BASE_URLS[region] || REGION_BASE_URLS.na;
123
+ }
124
+ /**
125
+ * Tesla OAuth2 utilities — authorization URL, token exchange, token refresh, region URLs.
126
+ */
127
+ const TeslaAuth = {
128
+ getAuthorizationUrl,
129
+ exchangeCode,
130
+ refreshToken,
131
+ getBaseUrl
132
+ };
133
+ /**
134
+ * Generate a random state parameter for CSRF protection.
135
+ */
136
+ function generateState() {
137
+ const array = new Uint8Array(32);
138
+ crypto.getRandomValues(array);
139
+ return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
140
+ }
141
+
142
+ //#endregion
143
+ exports.TESLA_AUTH_URL = TESLA_AUTH_URL;
144
+ exports.TESLA_SCOPES = TESLA_SCOPES;
145
+ exports.TESLA_TOKEN_URL = TESLA_TOKEN_URL;
146
+ exports.TeslaAuth = TeslaAuth;
@@ -0,0 +1,79 @@
1
+ //#region src/auth.d.ts
2
+ /**
3
+ * Tesla OAuth2 Authorization Code Flow
4
+ *
5
+ * Implements Tesla Fleet API authentication using standard OAuth2
6
+ * Authorization Code flow (NOT PKCE).
7
+ *
8
+ * Auth domain: https://fleet-auth.prd.vn.cloud.tesla.com
9
+ * Scopes: openid offline_access vehicle_device_data vehicle_cmds
10
+ * vehicle_charging_cmds energy_device_data energy_cmds vehicle_location
11
+ */
12
+ declare const TESLA_AUTH_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize";
13
+ declare const TESLA_TOKEN_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token";
14
+ declare const TESLA_SCOPES: readonly ["openid", "offline_access", "vehicle_device_data", "vehicle_cmds", "vehicle_charging_cmds", "energy_device_data", "energy_cmds", "vehicle_location"];
15
+ interface AuthorizationUrlOptions {
16
+ clientId: string;
17
+ redirectUri?: string;
18
+ state?: string;
19
+ scopes?: string[];
20
+ }
21
+ interface AuthorizationUrlResult {
22
+ url: string;
23
+ state: string;
24
+ }
25
+ interface ExchangeCodeOptions {
26
+ code: string;
27
+ clientId: string;
28
+ clientSecret: string;
29
+ redirectUri?: string;
30
+ /** Test-only: override auth base URL */
31
+ _testAuthBaseUrl?: string;
32
+ }
33
+ interface RefreshTokenOptions {
34
+ refreshToken: string;
35
+ clientId: string;
36
+ clientSecret: string;
37
+ /** Test-only: override auth base URL */
38
+ _testAuthBaseUrl?: string;
39
+ }
40
+ interface TokenResult {
41
+ accessToken: string;
42
+ refreshToken?: string;
43
+ expiresIn: number;
44
+ tokenType: string;
45
+ }
46
+ /**
47
+ * Generate the OAuth2 authorization URL for user consent.
48
+ *
49
+ * The user should be redirected to this URL to grant access.
50
+ * After granting, Tesla redirects to the redirectUri with an authorization code.
51
+ */
52
+ declare function getAuthorizationUrl(options: AuthorizationUrlOptions): AuthorizationUrlResult;
53
+ /**
54
+ * Exchange an authorization code for access and refresh tokens.
55
+ *
56
+ * This should be called after the user has been redirected back
57
+ * from Tesla's authorization page with an authorization code.
58
+ */
59
+ declare function exchangeCode(options: ExchangeCodeOptions): Promise<TokenResult>;
60
+ /**
61
+ * Refresh an expired access token using a refresh token.
62
+ */
63
+ declare function refreshToken(options: RefreshTokenOptions): Promise<TokenResult>;
64
+ /**
65
+ * Get the Fleet API base URL for a given region.
66
+ */
67
+ declare function getBaseUrl(region: string): string;
68
+ /**
69
+ * Tesla OAuth2 utilities — authorization URL, token exchange, token refresh, region URLs.
70
+ */
71
+ declare const TeslaAuth: {
72
+ getAuthorizationUrl: typeof getAuthorizationUrl;
73
+ exchangeCode: typeof exchangeCode;
74
+ refreshToken: typeof refreshToken;
75
+ getBaseUrl: typeof getBaseUrl;
76
+ };
77
+ //#endregion
78
+ export { AuthorizationUrlOptions, AuthorizationUrlResult, ExchangeCodeOptions, RefreshTokenOptions, TESLA_AUTH_URL, TESLA_SCOPES, TESLA_TOKEN_URL, TeslaAuth, TokenResult };
79
+ //# sourceMappingURL=auth.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.cts","names":[],"sources":["../src/auth.ts"],"mappings":";;AAiBA;;;;;AACA;;;;cADa,cAAA;AAAA,cACA,eAAA;AAAA,cAEA,YAAA;AAAA,UAuBI,uBAAA;EACf,QAAA;EACA,WAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,sBAAA;EACf,GAAA;EACA,KAAA;AAAA;AAAA,UAGe,mBAAA;EACf,IAAA;EACA,QAAA;EACA,YAAA;EACA,WAAA;;EAEA,gBAAA;AAAA;AAAA,UAGe,mBAAA;EACf,YAAA;EACA,QAAA;EACA,YAAA;EAXA;EAaA,gBAAA;AAAA;AAAA,UAGe,WAAA;EACf,WAAA;EACA,YAAA;EACA,SAAA;EACA,SAAA;AAAA;;;;;;;iBAaO,mBAAA,CAAoB,OAAA,EAAS,uBAAA,GAA0B,sBAAA;;AAjBhE;;;;;iBA2Ce,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,OAAA,CAAQ,WAAA;;;;iBAiDpD,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,OAAA,CAAQ,WAAA;AAvFlE;;;AAAA,iBAiIQ,UAAA,CAAW,MAAA;;;;cAOP,SAAA"}
@@ -0,0 +1,79 @@
1
+ //#region src/auth.d.ts
2
+ /**
3
+ * Tesla OAuth2 Authorization Code Flow
4
+ *
5
+ * Implements Tesla Fleet API authentication using standard OAuth2
6
+ * Authorization Code flow (NOT PKCE).
7
+ *
8
+ * Auth domain: https://fleet-auth.prd.vn.cloud.tesla.com
9
+ * Scopes: openid offline_access vehicle_device_data vehicle_cmds
10
+ * vehicle_charging_cmds energy_device_data energy_cmds vehicle_location
11
+ */
12
+ declare const TESLA_AUTH_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize";
13
+ declare const TESLA_TOKEN_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token";
14
+ declare const TESLA_SCOPES: readonly ["openid", "offline_access", "vehicle_device_data", "vehicle_cmds", "vehicle_charging_cmds", "energy_device_data", "energy_cmds", "vehicle_location"];
15
+ interface AuthorizationUrlOptions {
16
+ clientId: string;
17
+ redirectUri?: string;
18
+ state?: string;
19
+ scopes?: string[];
20
+ }
21
+ interface AuthorizationUrlResult {
22
+ url: string;
23
+ state: string;
24
+ }
25
+ interface ExchangeCodeOptions {
26
+ code: string;
27
+ clientId: string;
28
+ clientSecret: string;
29
+ redirectUri?: string;
30
+ /** Test-only: override auth base URL */
31
+ _testAuthBaseUrl?: string;
32
+ }
33
+ interface RefreshTokenOptions {
34
+ refreshToken: string;
35
+ clientId: string;
36
+ clientSecret: string;
37
+ /** Test-only: override auth base URL */
38
+ _testAuthBaseUrl?: string;
39
+ }
40
+ interface TokenResult {
41
+ accessToken: string;
42
+ refreshToken?: string;
43
+ expiresIn: number;
44
+ tokenType: string;
45
+ }
46
+ /**
47
+ * Generate the OAuth2 authorization URL for user consent.
48
+ *
49
+ * The user should be redirected to this URL to grant access.
50
+ * After granting, Tesla redirects to the redirectUri with an authorization code.
51
+ */
52
+ declare function getAuthorizationUrl(options: AuthorizationUrlOptions): AuthorizationUrlResult;
53
+ /**
54
+ * Exchange an authorization code for access and refresh tokens.
55
+ *
56
+ * This should be called after the user has been redirected back
57
+ * from Tesla's authorization page with an authorization code.
58
+ */
59
+ declare function exchangeCode(options: ExchangeCodeOptions): Promise<TokenResult>;
60
+ /**
61
+ * Refresh an expired access token using a refresh token.
62
+ */
63
+ declare function refreshToken(options: RefreshTokenOptions): Promise<TokenResult>;
64
+ /**
65
+ * Get the Fleet API base URL for a given region.
66
+ */
67
+ declare function getBaseUrl(region: string): string;
68
+ /**
69
+ * Tesla OAuth2 utilities — authorization URL, token exchange, token refresh, region URLs.
70
+ */
71
+ declare const TeslaAuth: {
72
+ getAuthorizationUrl: typeof getAuthorizationUrl;
73
+ exchangeCode: typeof exchangeCode;
74
+ refreshToken: typeof refreshToken;
75
+ getBaseUrl: typeof getBaseUrl;
76
+ };
77
+ //#endregion
78
+ export { AuthorizationUrlOptions, AuthorizationUrlResult, ExchangeCodeOptions, RefreshTokenOptions, TESLA_AUTH_URL, TESLA_SCOPES, TESLA_TOKEN_URL, TeslaAuth, TokenResult };
79
+ //# sourceMappingURL=auth.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.mts","names":[],"sources":["../src/auth.ts"],"mappings":";;AAiBA;;;;;AACA;;;;cADa,cAAA;AAAA,cACA,eAAA;AAAA,cAEA,YAAA;AAAA,UAuBI,uBAAA;EACf,QAAA;EACA,WAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,sBAAA;EACf,GAAA;EACA,KAAA;AAAA;AAAA,UAGe,mBAAA;EACf,IAAA;EACA,QAAA;EACA,YAAA;EACA,WAAA;;EAEA,gBAAA;AAAA;AAAA,UAGe,mBAAA;EACf,YAAA;EACA,QAAA;EACA,YAAA;EAXA;EAaA,gBAAA;AAAA;AAAA,UAGe,WAAA;EACf,WAAA;EACA,YAAA;EACA,SAAA;EACA,SAAA;AAAA;;;;;;;iBAaO,mBAAA,CAAoB,OAAA,EAAS,uBAAA,GAA0B,sBAAA;;AAjBhE;;;;;iBA2Ce,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,OAAA,CAAQ,WAAA;;;;iBAiDpD,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,OAAA,CAAQ,WAAA;AAvFlE;;;AAAA,iBAiIQ,UAAA,CAAW,MAAA;;;;cAOP,SAAA"}
package/dist/auth.mjs ADDED
@@ -0,0 +1,144 @@
1
+ import { AFSError } from "@aigne/afs";
2
+
3
+ //#region src/auth.ts
4
+ /**
5
+ * Tesla OAuth2 Authorization Code Flow
6
+ *
7
+ * Implements Tesla Fleet API authentication using standard OAuth2
8
+ * Authorization Code flow (NOT PKCE).
9
+ *
10
+ * Auth domain: https://fleet-auth.prd.vn.cloud.tesla.com
11
+ * Scopes: openid offline_access vehicle_device_data vehicle_cmds
12
+ * vehicle_charging_cmds energy_device_data energy_cmds vehicle_location
13
+ */
14
+ const TESLA_AUTH_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize";
15
+ const TESLA_TOKEN_URL = "https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token";
16
+ const TESLA_SCOPES = [
17
+ "openid",
18
+ "offline_access",
19
+ "vehicle_device_data",
20
+ "vehicle_cmds",
21
+ "vehicle_charging_cmds",
22
+ "energy_device_data",
23
+ "energy_cmds",
24
+ "vehicle_location"
25
+ ];
26
+ const REGION_BASE_URLS = {
27
+ na: "https://fleet-api.prd.na.vn.cloud.tesla.com",
28
+ eu: "https://fleet-api.prd.eu.vn.cloud.tesla.com",
29
+ cn: "https://fleet-api.prd.cn.vn.cloud.tesla.com"
30
+ };
31
+ const DEFAULT_REDIRECT_URI = "https://localhost:8443/callback";
32
+ /**
33
+ * Generate the OAuth2 authorization URL for user consent.
34
+ *
35
+ * The user should be redirected to this URL to grant access.
36
+ * After granting, Tesla redirects to the redirectUri with an authorization code.
37
+ */
38
+ function getAuthorizationUrl(options) {
39
+ const { clientId, redirectUri = DEFAULT_REDIRECT_URI, scopes = [...TESLA_SCOPES] } = options;
40
+ const state = options.state ?? generateState();
41
+ return {
42
+ url: `${TESLA_AUTH_URL}?${new URLSearchParams({
43
+ response_type: "code",
44
+ client_id: clientId,
45
+ redirect_uri: redirectUri,
46
+ scope: scopes.join(" "),
47
+ state
48
+ }).toString()}`,
49
+ state
50
+ };
51
+ }
52
+ /**
53
+ * Exchange an authorization code for access and refresh tokens.
54
+ *
55
+ * This should be called after the user has been redirected back
56
+ * from Tesla's authorization page with an authorization code.
57
+ */
58
+ async function exchangeCode(options) {
59
+ const { code, clientId, clientSecret, redirectUri = DEFAULT_REDIRECT_URI, _testAuthBaseUrl } = options;
60
+ const tokenUrl = _testAuthBaseUrl ? `${_testAuthBaseUrl}/oauth2/v3/token` : TESLA_TOKEN_URL;
61
+ const body = new URLSearchParams({
62
+ grant_type: "authorization_code",
63
+ code,
64
+ client_id: clientId,
65
+ client_secret: clientSecret,
66
+ redirect_uri: redirectUri
67
+ });
68
+ let response;
69
+ try {
70
+ response = await fetch(tokenUrl, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
73
+ body: body.toString()
74
+ });
75
+ } catch {
76
+ throw new AFSError("Failed to connect to Tesla auth server", "AFS_TIMEOUT");
77
+ }
78
+ if (!response.ok) throw new AFSError(`Tesla auth error: ${(await response.json().catch(() => ({}))).error_description || "Token exchange failed"}`, "AFS_AUTH_ERROR");
79
+ const data = await response.json();
80
+ return {
81
+ accessToken: data.access_token,
82
+ refreshToken: data.refresh_token,
83
+ expiresIn: data.expires_in,
84
+ tokenType: data.token_type || "Bearer"
85
+ };
86
+ }
87
+ /**
88
+ * Refresh an expired access token using a refresh token.
89
+ */
90
+ async function refreshToken(options) {
91
+ const { refreshToken: refreshTokenValue, clientId, clientSecret, _testAuthBaseUrl } = options;
92
+ const tokenUrl = _testAuthBaseUrl ? `${_testAuthBaseUrl}/oauth2/v3/token` : TESLA_TOKEN_URL;
93
+ const body = new URLSearchParams({
94
+ grant_type: "refresh_token",
95
+ refresh_token: refreshTokenValue,
96
+ client_id: clientId,
97
+ client_secret: clientSecret
98
+ });
99
+ let response;
100
+ try {
101
+ response = await fetch(tokenUrl, {
102
+ method: "POST",
103
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
104
+ body: body.toString()
105
+ });
106
+ } catch {
107
+ throw new AFSError("Failed to connect to Tesla auth server", "AFS_TIMEOUT");
108
+ }
109
+ if (!response.ok) throw new AFSError(`Tesla auth error: ${(await response.json().catch(() => ({}))).error_description || "Token refresh failed"}`, "AFS_AUTH_ERROR");
110
+ const data = await response.json();
111
+ return {
112
+ accessToken: data.access_token,
113
+ refreshToken: data.refresh_token,
114
+ expiresIn: data.expires_in,
115
+ tokenType: data.token_type || "Bearer"
116
+ };
117
+ }
118
+ /**
119
+ * Get the Fleet API base URL for a given region.
120
+ */
121
+ function getBaseUrl(region) {
122
+ return REGION_BASE_URLS[region] || REGION_BASE_URLS.na;
123
+ }
124
+ /**
125
+ * Tesla OAuth2 utilities — authorization URL, token exchange, token refresh, region URLs.
126
+ */
127
+ const TeslaAuth = {
128
+ getAuthorizationUrl,
129
+ exchangeCode,
130
+ refreshToken,
131
+ getBaseUrl
132
+ };
133
+ /**
134
+ * Generate a random state parameter for CSRF protection.
135
+ */
136
+ function generateState() {
137
+ const array = new Uint8Array(32);
138
+ crypto.getRandomValues(array);
139
+ return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
140
+ }
141
+
142
+ //#endregion
143
+ export { TESLA_AUTH_URL, TESLA_SCOPES, TESLA_TOKEN_URL, TeslaAuth };
144
+ //# sourceMappingURL=auth.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.mjs","names":[],"sources":["../src/auth.ts"],"sourcesContent":["/**\n * Tesla OAuth2 Authorization Code Flow\n *\n * Implements Tesla Fleet API authentication using standard OAuth2\n * Authorization Code flow (NOT PKCE).\n *\n * Auth domain: https://fleet-auth.prd.vn.cloud.tesla.com\n * Scopes: openid offline_access vehicle_device_data vehicle_cmds\n * vehicle_charging_cmds energy_device_data energy_cmds vehicle_location\n */\n\nimport { AFSError } from \"@aigne/afs\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nexport const TESLA_AUTH_URL = \"https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize\";\nexport const TESLA_TOKEN_URL = \"https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token\";\n\nexport const TESLA_SCOPES = [\n \"openid\",\n \"offline_access\",\n \"vehicle_device_data\",\n \"vehicle_cmds\",\n \"vehicle_charging_cmds\",\n \"energy_device_data\",\n \"energy_cmds\",\n \"vehicle_location\",\n] as const;\n\nconst REGION_BASE_URLS: Record<string, string> = {\n na: \"https://fleet-api.prd.na.vn.cloud.tesla.com\",\n eu: \"https://fleet-api.prd.eu.vn.cloud.tesla.com\",\n cn: \"https://fleet-api.prd.cn.vn.cloud.tesla.com\",\n};\n\nconst DEFAULT_REDIRECT_URI = \"https://localhost:8443/callback\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface AuthorizationUrlOptions {\n clientId: string;\n redirectUri?: string;\n state?: string;\n scopes?: string[];\n}\n\nexport interface AuthorizationUrlResult {\n url: string;\n state: string;\n}\n\nexport interface ExchangeCodeOptions {\n code: string;\n clientId: string;\n clientSecret: string;\n redirectUri?: string;\n /** Test-only: override auth base URL */\n _testAuthBaseUrl?: string;\n}\n\nexport interface RefreshTokenOptions {\n refreshToken: string;\n clientId: string;\n clientSecret: string;\n /** Test-only: override auth base URL */\n _testAuthBaseUrl?: string;\n}\n\nexport interface TokenResult {\n accessToken: string;\n refreshToken?: string;\n expiresIn: number;\n tokenType: string;\n}\n\n// ============================================================================\n// Auth Functions\n// ============================================================================\n\n/**\n * Generate the OAuth2 authorization URL for user consent.\n *\n * The user should be redirected to this URL to grant access.\n * After granting, Tesla redirects to the redirectUri with an authorization code.\n */\nfunction getAuthorizationUrl(options: AuthorizationUrlOptions): AuthorizationUrlResult {\n const { clientId, redirectUri = DEFAULT_REDIRECT_URI, scopes = [...TESLA_SCOPES] } = options;\n\n // Auto-generate state for CSRF protection if not provided\n const state = options.state ?? generateState();\n\n const params = new URLSearchParams({\n response_type: \"code\",\n client_id: clientId,\n redirect_uri: redirectUri,\n scope: scopes.join(\" \"),\n state,\n });\n\n return {\n url: `${TESLA_AUTH_URL}?${params.toString()}`,\n state,\n };\n}\n\n/**\n * Exchange an authorization code for access and refresh tokens.\n *\n * This should be called after the user has been redirected back\n * from Tesla's authorization page with an authorization code.\n */\nasync function exchangeCode(options: ExchangeCodeOptions): Promise<TokenResult> {\n const {\n code,\n clientId,\n clientSecret,\n redirectUri = DEFAULT_REDIRECT_URI,\n _testAuthBaseUrl,\n } = options;\n\n const tokenUrl = _testAuthBaseUrl ? `${_testAuthBaseUrl}/oauth2/v3/token` : TESLA_TOKEN_URL;\n\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n client_id: clientId,\n client_secret: clientSecret,\n redirect_uri: redirectUri,\n });\n\n let response: Response;\n try {\n response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n } catch {\n throw new AFSError(\"Failed to connect to Tesla auth server\", \"AFS_TIMEOUT\");\n }\n\n if (!response.ok) {\n const errorBody = (await response.json().catch(() => ({}))) as Record<string, unknown>;\n const description = (errorBody.error_description as string) || \"Token exchange failed\";\n throw new AFSError(`Tesla auth error: ${description}`, \"AFS_AUTH_ERROR\");\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n\n return {\n accessToken: data.access_token as string,\n refreshToken: data.refresh_token as string | undefined,\n expiresIn: data.expires_in as number,\n tokenType: (data.token_type as string) || \"Bearer\",\n };\n}\n\n/**\n * Refresh an expired access token using a refresh token.\n */\nasync function refreshToken(options: RefreshTokenOptions): Promise<TokenResult> {\n const { refreshToken: refreshTokenValue, clientId, clientSecret, _testAuthBaseUrl } = options;\n\n const tokenUrl = _testAuthBaseUrl ? `${_testAuthBaseUrl}/oauth2/v3/token` : TESLA_TOKEN_URL;\n\n const body = new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: refreshTokenValue,\n client_id: clientId,\n client_secret: clientSecret,\n });\n\n let response: Response;\n try {\n response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n } catch {\n throw new AFSError(\"Failed to connect to Tesla auth server\", \"AFS_TIMEOUT\");\n }\n\n if (!response.ok) {\n const errorBody = (await response.json().catch(() => ({}))) as Record<string, unknown>;\n const description = (errorBody.error_description as string) || \"Token refresh failed\";\n throw new AFSError(`Tesla auth error: ${description}`, \"AFS_AUTH_ERROR\");\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n\n return {\n accessToken: data.access_token as string,\n refreshToken: data.refresh_token as string | undefined,\n expiresIn: data.expires_in as number,\n tokenType: (data.token_type as string) || \"Bearer\",\n };\n}\n\n/**\n * Get the Fleet API base URL for a given region.\n */\nfunction getBaseUrl(region: string): string {\n return REGION_BASE_URLS[region] || REGION_BASE_URLS.na!;\n}\n\n/**\n * Tesla OAuth2 utilities — authorization URL, token exchange, token refresh, region URLs.\n */\nexport const TeslaAuth = {\n getAuthorizationUrl,\n exchangeCode,\n refreshToken,\n getBaseUrl,\n};\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Generate a random state parameter for CSRF protection.\n */\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,MAAa,iBAAiB;AAC9B,MAAa,kBAAkB;AAE/B,MAAa,eAAe;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,mBAA2C;CAC/C,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;AAED,MAAM,uBAAuB;;;;;;;AAoD7B,SAAS,oBAAoB,SAA0D;CACrF,MAAM,EAAE,UAAU,cAAc,sBAAsB,SAAS,CAAC,GAAG,aAAa,KAAK;CAGrF,MAAM,QAAQ,QAAQ,SAAS,eAAe;AAU9C,QAAO;EACL,KAAK,GAAG,eAAe,GATV,IAAI,gBAAgB;GACjC,eAAe;GACf,WAAW;GACX,cAAc;GACd,OAAO,OAAO,KAAK,IAAI;GACvB;GACD,CAAC,CAGiC,UAAU;EAC3C;EACD;;;;;;;;AASH,eAAe,aAAa,SAAoD;CAC9E,MAAM,EACJ,MACA,UACA,cACA,cAAc,sBACd,qBACE;CAEJ,MAAM,WAAW,mBAAmB,GAAG,iBAAiB,oBAAoB;CAE5E,MAAM,OAAO,IAAI,gBAAgB;EAC/B,YAAY;EACZ;EACA,WAAW;EACX,eAAe;EACf,cAAc;EACf,CAAC;CAEF,IAAI;AACJ,KAAI;AACF,aAAW,MAAM,MAAM,UAAU;GAC/B,QAAQ;GACR,SAAS,EAAE,gBAAgB,qCAAqC;GAChE,MAAM,KAAK,UAAU;GACtB,CAAC;SACI;AACN,QAAM,IAAI,SAAS,0CAA0C,cAAc;;AAG7E,KAAI,CAAC,SAAS,GAGZ,OAAM,IAAI,SAAS,sBAFA,MAAM,SAAS,MAAM,CAAC,aAAa,EAAE,EAAE,EAC3B,qBAAgC,2BACR,iBAAiB;CAG1E,MAAM,OAAQ,MAAM,SAAS,MAAM;AAEnC,QAAO;EACL,aAAa,KAAK;EAClB,cAAc,KAAK;EACnB,WAAW,KAAK;EAChB,WAAY,KAAK,cAAyB;EAC3C;;;;;AAMH,eAAe,aAAa,SAAoD;CAC9E,MAAM,EAAE,cAAc,mBAAmB,UAAU,cAAc,qBAAqB;CAEtF,MAAM,WAAW,mBAAmB,GAAG,iBAAiB,oBAAoB;CAE5E,MAAM,OAAO,IAAI,gBAAgB;EAC/B,YAAY;EACZ,eAAe;EACf,WAAW;EACX,eAAe;EAChB,CAAC;CAEF,IAAI;AACJ,KAAI;AACF,aAAW,MAAM,MAAM,UAAU;GAC/B,QAAQ;GACR,SAAS,EAAE,gBAAgB,qCAAqC;GAChE,MAAM,KAAK,UAAU;GACtB,CAAC;SACI;AACN,QAAM,IAAI,SAAS,0CAA0C,cAAc;;AAG7E,KAAI,CAAC,SAAS,GAGZ,OAAM,IAAI,SAAS,sBAFA,MAAM,SAAS,MAAM,CAAC,aAAa,EAAE,EAAE,EAC3B,qBAAgC,0BACR,iBAAiB;CAG1E,MAAM,OAAQ,MAAM,SAAS,MAAM;AAEnC,QAAO;EACL,aAAa,KAAK;EAClB,cAAc,KAAK;EACnB,WAAW,KAAK;EAChB,WAAY,KAAK,cAAyB;EAC3C;;;;;AAMH,SAAS,WAAW,QAAwB;AAC1C,QAAO,iBAAiB,WAAW,iBAAiB;;;;;AAMtD,MAAa,YAAY;CACvB;CACA;CACA;CACA;CACD;;;;AASD,SAAS,gBAAwB;CAC/B,MAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,QAAO,gBAAgB,MAAM;AAC7B,QAAO,MAAM,KAAK,QAAQ,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG"}