@limrun/api 0.65.1 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/README.md +64 -0
- package/client.d.mts +7 -1
- package/client.d.mts.map +1 -1
- package/client.d.ts +7 -1
- package/client.d.ts.map +1 -1
- package/client.js +17 -3
- package/client.js.map +1 -1
- package/client.mjs +17 -3
- package/client.mjs.map +1 -1
- package/exec-client.d.mts +3 -6
- package/exec-client.d.mts.map +1 -1
- package/exec-client.d.ts +3 -6
- package/exec-client.d.ts.map +1 -1
- package/exec-client.js +4 -4
- package/exec-client.js.map +1 -1
- package/exec-client.mjs.map +1 -1
- package/index.d.mts +1 -0
- package/index.d.mts.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/internal/tslib.js +20 -20
- package/ios-client.d.mts +76 -2
- package/ios-client.d.mts.map +1 -1
- package/ios-client.d.ts +76 -2
- package/ios-client.d.ts.map +1 -1
- package/ios-client.js +88 -16
- package/ios-client.js.map +1 -1
- package/ios-client.mjs +88 -16
- package/ios-client.mjs.map +1 -1
- package/lib/asset-permissions.d.mts +45 -0
- package/lib/asset-permissions.d.mts.map +1 -0
- package/lib/asset-permissions.d.ts +45 -0
- package/lib/asset-permissions.d.ts.map +1 -0
- package/lib/asset-permissions.js +37 -0
- package/lib/asset-permissions.js.map +1 -0
- package/lib/asset-permissions.mjs +33 -0
- package/lib/asset-permissions.mjs.map +1 -0
- package/lib/workload-credentials.d.mts +14 -0
- package/lib/workload-credentials.d.mts.map +1 -0
- package/lib/workload-credentials.d.ts +14 -0
- package/lib/workload-credentials.d.ts.map +1 -0
- package/lib/workload-credentials.js +78 -0
- package/lib/workload-credentials.js.map +1 -0
- package/lib/workload-credentials.mjs +41 -0
- package/lib/workload-credentials.mjs.map +1 -0
- package/package.json +12 -2
- package/resources/access-tokens.d.mts +38 -0
- package/resources/access-tokens.d.mts.map +1 -0
- package/resources/access-tokens.d.ts +38 -0
- package/resources/access-tokens.d.ts.map +1 -0
- package/resources/access-tokens.js +16 -0
- package/resources/access-tokens.js.map +1 -0
- package/resources/access-tokens.mjs +12 -0
- package/resources/access-tokens.mjs.map +1 -0
- package/resources/android-instances.d.mts +5 -0
- package/resources/android-instances.d.mts.map +1 -1
- package/resources/android-instances.d.ts +5 -0
- package/resources/android-instances.d.ts.map +1 -1
- package/resources/assets.d.mts +25 -1
- package/resources/assets.d.mts.map +1 -1
- package/resources/assets.d.ts +25 -1
- package/resources/assets.d.ts.map +1 -1
- package/resources/assets.js +12 -0
- package/resources/assets.js.map +1 -1
- package/resources/assets.mjs +12 -0
- package/resources/assets.mjs.map +1 -1
- package/resources/daemon-client-shared.d.mts +2 -36
- package/resources/daemon-client-shared.d.mts.map +1 -1
- package/resources/daemon-client-shared.d.ts +2 -36
- package/resources/daemon-client-shared.d.ts.map +1 -1
- package/resources/daemon-client-shared.js +2 -21
- package/resources/daemon-client-shared.js.map +1 -1
- package/resources/daemon-client-shared.mjs +1 -20
- package/resources/daemon-client-shared.mjs.map +1 -1
- package/resources/gradle-instances-helpers.d.mts +2 -15
- package/resources/gradle-instances-helpers.d.mts.map +1 -1
- package/resources/gradle-instances-helpers.d.ts +2 -15
- package/resources/gradle-instances-helpers.d.ts.map +1 -1
- package/resources/gradle-instances-helpers.js +3 -11
- package/resources/gradle-instances-helpers.js.map +1 -1
- package/resources/gradle-instances-helpers.mjs +4 -12
- package/resources/gradle-instances-helpers.mjs.map +1 -1
- package/resources/gradle-instances.d.mts +5 -0
- package/resources/gradle-instances.d.mts.map +1 -1
- package/resources/gradle-instances.d.ts +5 -0
- package/resources/gradle-instances.d.ts.map +1 -1
- package/resources/index.d.mts +1 -0
- package/resources/index.d.mts.map +1 -1
- package/resources/index.d.ts +1 -0
- package/resources/index.d.ts.map +1 -1
- package/resources/index.js +3 -1
- package/resources/index.js.map +1 -1
- package/resources/index.mjs +1 -0
- package/resources/index.mjs.map +1 -1
- package/resources/ios-instances.d.mts +5 -0
- package/resources/ios-instances.d.mts.map +1 -1
- package/resources/ios-instances.d.ts +5 -0
- package/resources/ios-instances.d.ts.map +1 -1
- package/resources/scoped-tokens.d.mts +9 -16
- package/resources/scoped-tokens.d.mts.map +1 -1
- package/resources/scoped-tokens.d.ts +9 -16
- package/resources/scoped-tokens.d.ts.map +1 -1
- package/resources/scoped-tokens.js +3 -3
- package/resources/scoped-tokens.js.map +1 -1
- package/resources/scoped-tokens.mjs +3 -3
- package/resources/scoped-tokens.mjs.map +1 -1
- package/resources/xcode-instances-helpers.d.mts +5 -38
- package/resources/xcode-instances-helpers.d.mts.map +1 -1
- package/resources/xcode-instances-helpers.d.ts +5 -38
- package/resources/xcode-instances-helpers.d.ts.map +1 -1
- package/resources/xcode-instances-helpers.js +7 -29
- package/resources/xcode-instances-helpers.js.map +1 -1
- package/resources/xcode-instances-helpers.mjs +8 -30
- package/resources/xcode-instances-helpers.mjs.map +1 -1
- package/resources/xcode-instances.d.mts +5 -0
- package/resources/xcode-instances.d.mts.map +1 -1
- package/resources/xcode-instances.d.ts +5 -0
- package/resources/xcode-instances.d.ts.map +1 -1
- package/src/client.ts +29 -2
- package/src/exec-client.ts +3 -0
- package/src/index.ts +2 -0
- package/src/ios-client.ts +189 -21
- package/src/lib/asset-permissions.ts +67 -0
- package/src/lib/workload-credentials.ts +44 -0
- package/src/resources/access-tokens.ts +46 -0
- package/src/resources/android-instances.ts +6 -0
- package/src/resources/assets.ts +38 -0
- package/src/resources/daemon-client-shared.ts +3 -48
- package/src/resources/gradle-instances-helpers.ts +3 -30
- package/src/resources/gradle-instances.ts +6 -0
- package/src/resources/index.ts +2 -0
- package/src/resources/ios-instances.ts +6 -0
- package/src/resources/scoped-tokens.ts +10 -17
- package/src/resources/xcode-instances-helpers.ts +9 -75
- package/src/resources/xcode-instances.ts +6 -0
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -94,6 +94,11 @@ export declare namespace XcodeInstanceCreateParams {
|
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
interface Spec {
|
|
97
|
+
/** Inline instance role. Omit for the organization's compatibility default. */
|
|
98
|
+
role?: {
|
|
99
|
+
/** Asset permissions delegated by the creator. An empty array grants nothing. */
|
|
100
|
+
permissions: Array<string>;
|
|
101
|
+
};
|
|
97
102
|
/**
|
|
98
103
|
* ClientLocation takes precedence over ClientIP regardless of clue order. Without
|
|
99
104
|
* either clue, routing uses Cloudflare visitor coordinates, then its client IP.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xcode-instances.d.ts","sourceRoot":"","sources":["../src/resources/xcode-instances.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,4BAAyB;AAC/C,OAAO,EAAE,UAAU,EAAE,+BAA4B;AACjD,OAAO,EAAE,KAAK,EAAE,KAAK,WAAW,EAAE,WAAW,EAAE,8BAA2B;AAE1E,OAAO,EAAE,cAAc,EAAE,uCAAoC;AAG7D,qBAAa,cAAe,SAAQ,WAAW;IAC7C;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,yBAAyB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,CAAC,aAAa,CAAC;IAK9F;;OAEG;IACH,IAAI,CACF,KAAK,GAAE,uBAAuB,GAAG,IAAI,GAAG,SAAc,EACtD,OAAO,CAAC,EAAE,cAAc,GACvB,WAAW,CAAC,mBAAmB,EAAE,aAAa,CAAC;IAIlD;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC;IAO9D;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,CAAC,aAAa,CAAC;CAGrE;AAED,MAAM,MAAM,mBAAmB,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IAEjC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC;IAEzB,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC;CAC9B;AAED,yBAAiB,aAAa,CAAC;IAC7B,UAAiB,QAAQ;QACvB,EAAE,EAAE,MAAM,CAAC;QAEX,SAAS,EAAE,MAAM,CAAC;QAElB,cAAc,EAAE,MAAM,CAAC;QAEvB,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB,MAAM,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QAEnC,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;IAED,UAAiB,IAAI;QACnB,iBAAiB,EAAE,MAAM,CAAC;QAE1B,MAAM,EAAE,MAAM,CAAC;QAEf,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB;IAED,UAAiB,MAAM;QACrB;;;;WAIG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QAEf,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,YAAY,CAAC;QAEpE,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB,YAAY,CAAC,EAAE,MAAM,CAAC;QAEtB;;;;;;;;;;;WAWG;QACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B;CACF;AAED,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;OAEG;IACH,QAAQ,CAAC,EAAE,yBAAyB,CAAC,QAAQ,CAAC;IAE9C;;OAEG;IACH,IAAI,CAAC,EAAE,yBAAyB,CAAC,IAAI,CAAC;CACvC;AAED,yBAAiB,yBAAyB,CAAC;IACzC,UAAiB,QAAQ;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB,MAAM,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;KACpC;IAED,UAAiB,IAAI;QACnB;;;WAGG;QACH,KAAK,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEzB;;;WAGG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB;;;;WAIG;QACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAE3B;;;;;;;WAOG;QACH,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;QAElC;;;;;;;;;;;;;;;;;;WAkBG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAED,UAAiB,IAAI,CAAC;QACpB,UAAiB,IAAI;YACnB,IAAI,EAAE,UAAU,GAAG,gBAAgB,CAAC;YAEpC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAElB;;;eAGG;YACH,cAAc,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC;SACtC;QAED,UAAiB,IAAI,CAAC;YACpB,UAAiB,cAAc;gBAC7B;;mBAEG;gBACH,QAAQ,EAAE,MAAM,CAAC;gBAEjB;;mBAEG;gBACH,SAAS,EAAE,MAAM,CAAC;aACnB;SACF;KACF;CACF;AAED,MAAM,WAAW,uBAAwB,SAAQ,WAAW;IAC1D;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,CAAC,OAAO,WAAW,cAAc,CAAC;IACtC,OAAO,EACL,KAAK,aAAa,IAAI,aAAa,EACnC,KAAK,mBAAmB,IAAI,mBAAmB,EAC/C,KAAK,yBAAyB,IAAI,yBAAyB,EAC3D,KAAK,uBAAuB,IAAI,uBAAuB,GACxD,CAAC;CACH"}
|
|
1
|
+
{"version":3,"file":"xcode-instances.d.ts","sourceRoot":"","sources":["../src/resources/xcode-instances.ts"],"names":[],"mappings":"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"}
|
package/src/client.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WorkloadCredentials } from './lib/workload-credentials';
|
|
1
2
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
3
|
|
|
3
4
|
import type { RequestInit, RequestInfo, BodyInit } from './internal/builtin-types';
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
} from './resources/ios-instances';
|
|
58
59
|
import { IosInstances } from './resources/ios-instances-helpers';
|
|
59
60
|
import { SessionArtifact } from './resources/session-artifacts';
|
|
61
|
+
import { AccessToken, AccessTokenCreateParams, AccessTokens } from './resources/access-tokens';
|
|
60
62
|
import { ScopedToken, ScopedTokenCreateParams, ScopedTokens } from './resources/scoped-tokens';
|
|
61
63
|
import {
|
|
62
64
|
XcodeInstance,
|
|
@@ -99,6 +101,8 @@ import {
|
|
|
99
101
|
import { isEmptyObj } from './internal/utils/values';
|
|
100
102
|
|
|
101
103
|
export interface ClientOptions {
|
|
104
|
+
/** Projected workload JWT file. Defaults to LIMRUN_WORKLOAD_IDENTITY_TOKEN_FILE. Null disables discovery. */
|
|
105
|
+
workloadTokenFile?: string | null;
|
|
102
106
|
/**
|
|
103
107
|
* Defaults to process.env['LIM_API_KEY'].
|
|
104
108
|
*/
|
|
@@ -193,6 +197,7 @@ export class Limrun {
|
|
|
193
197
|
fetchOptions: MergedRequestInit | undefined;
|
|
194
198
|
|
|
195
199
|
private fetch: Fetch;
|
|
200
|
+
private workloadCredentials?: WorkloadCredentials;
|
|
196
201
|
#encoder: Opts.RequestEncoder;
|
|
197
202
|
protected idempotencyHeader?: string;
|
|
198
203
|
private _options: ClientOptions;
|
|
@@ -215,10 +220,15 @@ export class Limrun {
|
|
|
215
220
|
apiKey = readEnv('LIM_API_KEY') ?? null,
|
|
216
221
|
...opts
|
|
217
222
|
}: ClientOptions = {}) {
|
|
223
|
+
const tokenFile =
|
|
224
|
+
opts.workloadTokenFile === null ?
|
|
225
|
+
undefined
|
|
226
|
+
: opts.workloadTokenFile ?? readEnv('LIMRUN_WORKLOAD_IDENTITY_TOKEN_FILE');
|
|
218
227
|
const options: ClientOptions = {
|
|
219
228
|
apiKey,
|
|
220
229
|
...opts,
|
|
221
|
-
baseURL:
|
|
230
|
+
baseURL:
|
|
231
|
+
baseURL || (!apiKey && tokenFile ? readEnv('DIRECTOR_URL') : undefined) || `https://api.limrun.com`,
|
|
222
232
|
};
|
|
223
233
|
|
|
224
234
|
this.baseURL = options.baseURL!;
|
|
@@ -251,6 +261,13 @@ export class Limrun {
|
|
|
251
261
|
this._options = options;
|
|
252
262
|
|
|
253
263
|
this.apiKey = apiKey;
|
|
264
|
+
if (!apiKey && tokenFile)
|
|
265
|
+
this.workloadCredentials = new WorkloadCredentials(
|
|
266
|
+
this.baseURL,
|
|
267
|
+
tokenFile,
|
|
268
|
+
this.fetch,
|
|
269
|
+
this.fetchOptions,
|
|
270
|
+
);
|
|
254
271
|
}
|
|
255
272
|
|
|
256
273
|
/**
|
|
@@ -284,7 +301,7 @@ export class Limrun {
|
|
|
284
301
|
}
|
|
285
302
|
|
|
286
303
|
protected validateHeaders({ values, nulls }: NullableHeaders) {
|
|
287
|
-
if (this.apiKey && values.get('authorization')) {
|
|
304
|
+
if ((this.apiKey || this.workloadCredentials) && values.get('authorization')) {
|
|
288
305
|
return;
|
|
289
306
|
}
|
|
290
307
|
if (nulls.has('authorization')) {
|
|
@@ -298,6 +315,10 @@ export class Limrun {
|
|
|
298
315
|
|
|
299
316
|
protected async authHeaders(opts: FinalRequestOptions): Promise<NullableHeaders | undefined> {
|
|
300
317
|
if (this.apiKey == null) {
|
|
318
|
+
const explicit = buildHeaders([this._options.defaultHeaders, opts.headers]);
|
|
319
|
+
if (explicit.values.has('authorization') || explicit.nulls.has('authorization')) return undefined;
|
|
320
|
+
if (this.workloadCredentials)
|
|
321
|
+
return buildHeaders([{ Authorization: `Bearer ${await this.workloadCredentials.get()}` }]);
|
|
301
322
|
return undefined;
|
|
302
323
|
}
|
|
303
324
|
return buildHeaders([{ Authorization: `Bearer ${this.apiKey}` }]);
|
|
@@ -850,6 +871,8 @@ export class Limrun {
|
|
|
850
871
|
xcodeInstances: API.XcodeInstances = new API.XcodeInstances(this);
|
|
851
872
|
gradleInstances: API.GradleInstances = new API.GradleInstances(this);
|
|
852
873
|
analytics: API.Analytics = new API.Analytics(this);
|
|
874
|
+
accessTokens: API.AccessTokens = new API.AccessTokens(this);
|
|
875
|
+
/** @deprecated Use accessTokens. */
|
|
853
876
|
scopedTokens: API.ScopedTokens = new API.ScopedTokens(this);
|
|
854
877
|
}
|
|
855
878
|
|
|
@@ -859,6 +882,7 @@ Limrun.IosInstances = IosInstances;
|
|
|
859
882
|
Limrun.XcodeInstances = XcodeInstances;
|
|
860
883
|
Limrun.GradleInstances = GradleInstances;
|
|
861
884
|
Limrun.Analytics = Analytics;
|
|
885
|
+
Limrun.AccessTokens = AccessTokens;
|
|
862
886
|
Limrun.ScopedTokens = ScopedTokens;
|
|
863
887
|
|
|
864
888
|
export declare namespace Limrun {
|
|
@@ -939,6 +963,9 @@ export declare namespace Limrun {
|
|
|
939
963
|
};
|
|
940
964
|
|
|
941
965
|
export {
|
|
966
|
+
AccessTokens as AccessTokens,
|
|
967
|
+
type AccessToken as AccessToken,
|
|
968
|
+
type AccessTokenCreateParams as AccessTokenCreateParams,
|
|
942
969
|
ScopedTokens as ScopedTokens,
|
|
943
970
|
type ScopedToken as ScopedToken,
|
|
944
971
|
type ScopedTokenCreateParams as ScopedTokenCreateParams,
|
package/src/exec-client.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AssetUpload } from './lib/asset-permissions';
|
|
1
2
|
/**
|
|
2
3
|
* Client for executing commands on limbuild server with streaming output.
|
|
3
4
|
*
|
|
@@ -111,6 +112,7 @@ export type XcodeBuildExecRequest = {
|
|
|
111
112
|
gitInit?: boolean;
|
|
112
113
|
/** How xcodebuild stdout is processed before streaming. Defaults to 'xcbeautify'. */
|
|
113
114
|
logProcessor?: 'xcbeautify' | 'none';
|
|
115
|
+
assetUpload?: AssetUpload;
|
|
114
116
|
signedUploadUrl?: string;
|
|
115
117
|
/**
|
|
116
118
|
* ID of the Limrun asset signedUploadUrl targets, when it was minted from
|
|
@@ -208,6 +210,7 @@ export type GradleBuildExecRequest = {
|
|
|
208
210
|
reactNative?: GradleReactNativeConfig;
|
|
209
211
|
signing?: GradleSigningConfig;
|
|
210
212
|
playstore?: GradlePlaystoreConfig;
|
|
213
|
+
assetUpload?: AssetUpload;
|
|
211
214
|
signedUploadUrl?: string;
|
|
212
215
|
webhook?: WebhookConfig;
|
|
213
216
|
additionalMetadata?: {
|
package/src/index.ts
CHANGED
|
@@ -226,3 +226,5 @@ export {
|
|
|
226
226
|
UnprocessableEntityError,
|
|
227
227
|
} from './core/error';
|
|
228
228
|
export { isDirectInstanceHttpError, type DirectInstanceHttpError } from './internal/direct-instance-errors';
|
|
229
|
+
|
|
230
|
+
export { type AssetUpload, type BuildUpload } from './lib/asset-permissions';
|
package/src/ios-client.ts
CHANGED
|
@@ -476,19 +476,77 @@ export type AppExitInfo = {
|
|
|
476
476
|
*/
|
|
477
477
|
export type LaunchAppExitCallback = (logs: string[], info: AppExitInfo) => Promise<void> | void;
|
|
478
478
|
|
|
479
|
-
/** Handle for
|
|
479
|
+
/** Handle for a watch registered via {@link InstanceClient.monitorApp} or {@link InstanceClient.watchApp}. */
|
|
480
480
|
export type AppWatch = {
|
|
481
481
|
/** Identifier of this watch on the server. */
|
|
482
482
|
execId: string;
|
|
483
|
-
/** Cancels the watch;
|
|
483
|
+
/** Cancels the watch; its callbacks will not be invoked afterwards. */
|
|
484
484
|
stop: () => Promise<void>;
|
|
485
485
|
};
|
|
486
486
|
|
|
487
|
+
/** What {@link InstanceClient.monitorApp} reports. Pass either callback or both. */
|
|
488
|
+
export type AppMonitorCallbacks = {
|
|
489
|
+
/** Called at every exit of any process of the app. Monitored apps report no logs. */
|
|
490
|
+
onExit?: LaunchAppExitCallback;
|
|
491
|
+
/** Called for each sample of a main-thread hang and once at its end. */
|
|
492
|
+
onHang?: AppHangCallback;
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* A sample of an app's main-thread hang, or the hang's end. The main thread is hung once it has
|
|
497
|
+
* not answered for 1 second. The simulator samples a hang 1, 3, 6 and 10 seconds in, then reports
|
|
498
|
+
* once more when the main thread answers or the app exits.
|
|
499
|
+
*/
|
|
500
|
+
export type AppHangInfo = {
|
|
501
|
+
bundleId: string;
|
|
502
|
+
pid: number;
|
|
503
|
+
/** Numbers the process's hangs from 1. A hang's samples and its end share it. */
|
|
504
|
+
hangId: number;
|
|
505
|
+
/** `false` for a sample while the main thread is still hung, `true` once it answered or the app exited. */
|
|
506
|
+
ended: boolean;
|
|
507
|
+
/** The hang's age at this sample, or its whole length at the end. */
|
|
508
|
+
durationMs: number;
|
|
509
|
+
/**
|
|
510
|
+
* The main thread's run state at the sample: `running`, `waiting`, `uninterruptible`, `stopped`
|
|
511
|
+
* or `halted`. This and the fields below come with samples only, each left out when the app
|
|
512
|
+
* could not read it.
|
|
513
|
+
*/
|
|
514
|
+
mainThreadState?: string;
|
|
515
|
+
/**
|
|
516
|
+
* CPU time the main thread used during the hang. Close to `durationMs` while `running` means the
|
|
517
|
+
* app kept its main thread busy; near zero while `waiting` means it waited, and `stackTrace`
|
|
518
|
+
* shows on what.
|
|
519
|
+
*/
|
|
520
|
+
mainThreadCpuMs?: number;
|
|
521
|
+
/** The part of `mainThreadCpuMs` spent in the kernel, for example reading compressed memory back. */
|
|
522
|
+
mainThreadKernelMs?: number;
|
|
523
|
+
/** CPU time all of the app's threads used during the hang. */
|
|
524
|
+
appCpuMs?: number;
|
|
525
|
+
/** Pages the app read in from disk during the hang. */
|
|
526
|
+
pageIns?: number;
|
|
527
|
+
/** The app's memory footprint at the sample. */
|
|
528
|
+
footprintBytes?: number;
|
|
529
|
+
/**
|
|
530
|
+
* The main thread's frames, with symbol names for the app's own code, followed by the binary
|
|
531
|
+
* images they belong to, laid out like {@link AppCrashInfo.stackTrace}.
|
|
532
|
+
*/
|
|
533
|
+
stackTrace?: string;
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
/** Called for each sample of a hang and once at its end. */
|
|
537
|
+
export type AppHangCallback = (info: AppHangInfo) => Promise<void> | void;
|
|
538
|
+
|
|
487
539
|
type LaunchAppCommonOptions = {
|
|
488
540
|
/** App environment variables. Requires relaunch; reserved keys and existing launch variables are ignored. */
|
|
489
541
|
env?: Record<string, string>;
|
|
490
542
|
/** Called once when the launched app exits, crashes, or is terminated. */
|
|
491
543
|
onExit?: LaunchAppExitCallback;
|
|
544
|
+
/**
|
|
545
|
+
* Called for each sample of the launched process's main-thread hangs and once at each hang's
|
|
546
|
+
* end, until that process exits. Simulators that predate hang reports launch the app without
|
|
547
|
+
* them. See {@link InstanceClient.monitorApp}.
|
|
548
|
+
*/
|
|
549
|
+
onHang?: AppHangCallback;
|
|
492
550
|
};
|
|
493
551
|
|
|
494
552
|
type StandardLaunchAppOptions = LaunchAppCommonOptions & {
|
|
@@ -804,6 +862,9 @@ export type InstanceClient = {
|
|
|
804
862
|
* exit reason and crash details, the next time any process of the app exits. Watched apps
|
|
805
863
|
* report no logs.
|
|
806
864
|
*
|
|
865
|
+
* @deprecated Use {@link monitorApp}, which reports every exit and hang of the app until you
|
|
866
|
+
* stop it, including after relaunches. `watchApp` reports only the next exit and will be removed.
|
|
867
|
+
*
|
|
807
868
|
* The app does not need to be running yet: if it starts later, it is picked up automatically.
|
|
808
869
|
*
|
|
809
870
|
* @param bundleId Bundle identifier of the app to watch
|
|
@@ -812,6 +873,23 @@ export type InstanceClient = {
|
|
|
812
873
|
*/
|
|
813
874
|
watchApp: (bundleId: string, onExit: LaunchAppExitCallback) => Promise<AppWatch>;
|
|
814
875
|
|
|
876
|
+
/**
|
|
877
|
+
* Monitor an app without launching it through {@link launchApp}, for example an app a test
|
|
878
|
+
* runner relaunches or one opened with {@link openUrl}. `onExit` fires at every exit of any
|
|
879
|
+
* process of the app, with the reason and crash details. `onHang` fires for each sample of a
|
|
880
|
+
* main-thread hang (no answer for 1 second or more) and once at its end. The monitor outlives
|
|
881
|
+
* exits and relaunches until you stop it, and the app does not need to be running yet.
|
|
882
|
+
*
|
|
883
|
+
* Each app process reports at most 100 hangs. A simulator keeps its 8 newest monitors, so
|
|
884
|
+
* starting a ninth silently ends the oldest. Simulators that predate monitors reject the call
|
|
885
|
+
* after 10 seconds.
|
|
886
|
+
*
|
|
887
|
+
* @param bundleId Bundle identifier of the app to monitor
|
|
888
|
+
* @param callbacks `onExit`, `onHang`, or both
|
|
889
|
+
* @returns A handle whose `stop()` ends the monitor
|
|
890
|
+
*/
|
|
891
|
+
monitorApp: (bundleId: string, callbacks: AppMonitorCallbacks) => Promise<AppWatch>;
|
|
892
|
+
|
|
815
893
|
/**
|
|
816
894
|
* Fetch the last N lines of app logs (combined stdout/stderr)
|
|
817
895
|
* @param bundleId Bundle identifier of the app
|
|
@@ -1447,6 +1525,14 @@ type AppExitMessage = {
|
|
|
1447
1525
|
logLineCount?: number;
|
|
1448
1526
|
};
|
|
1449
1527
|
|
|
1528
|
+
/** appHang as the server sends it. */
|
|
1529
|
+
type AppHangMessage = {
|
|
1530
|
+
type: 'appHang';
|
|
1531
|
+
execId: string;
|
|
1532
|
+
bundleId: string;
|
|
1533
|
+
hang: Omit<AppHangInfo, 'bundleId'>;
|
|
1534
|
+
};
|
|
1535
|
+
|
|
1450
1536
|
type ServerResponse = {
|
|
1451
1537
|
recordingId?: string;
|
|
1452
1538
|
state?: FoldState;
|
|
@@ -1693,8 +1779,8 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
1693
1779
|
const simctlExecutions: Map<string, SimctlExecution> = new Map();
|
|
1694
1780
|
|
|
1695
1781
|
// Server notifications are keyed by notification type and that type's
|
|
1696
|
-
// identifier field. They intentionally survive transient WebSocket reconnects
|
|
1697
|
-
//
|
|
1782
|
+
// identifier field. They intentionally survive transient WebSocket reconnects.
|
|
1783
|
+
// appExit handlers are one-shot; appHang handlers stay until their watch stops.
|
|
1698
1784
|
type ServerNotificationHandler = (message: ServerResponse) => Promise<void>;
|
|
1699
1785
|
const serverNotifications: Map<string, Map<string, ServerNotificationHandler>> = new Map();
|
|
1700
1786
|
|
|
@@ -1811,6 +1897,11 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
1811
1897
|
return handler;
|
|
1812
1898
|
};
|
|
1813
1899
|
|
|
1900
|
+
const getServerNotification = (
|
|
1901
|
+
type: string,
|
|
1902
|
+
notificationId: string,
|
|
1903
|
+
): ServerNotificationHandler | undefined => serverNotifications.get(type)?.get(notificationId);
|
|
1904
|
+
|
|
1814
1905
|
const deleteServerNotification = (type: string, notificationId: string): void => {
|
|
1815
1906
|
const handlers = serverNotifications.get(type);
|
|
1816
1907
|
if (!handlers) {
|
|
@@ -1943,7 +2034,9 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
1943
2034
|
return true;
|
|
1944
2035
|
}
|
|
1945
2036
|
|
|
1946
|
-
|
|
2037
|
+
// Launches and watchApp end at the exit; monitors keep their handler until stopped.
|
|
2038
|
+
const notification =
|
|
2039
|
+
takeServerNotification('appExit', execId) ?? getServerNotification('monitorExit', execId);
|
|
1947
2040
|
if (!notification) {
|
|
1948
2041
|
logger.debug(`Received appExit for unknown or already handled execId: ${execId}`);
|
|
1949
2042
|
return true;
|
|
@@ -1956,6 +2049,26 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
1956
2049
|
return true;
|
|
1957
2050
|
};
|
|
1958
2051
|
|
|
2052
|
+
const handleAppHang = (message: ServerResponse): boolean => {
|
|
2053
|
+
if (message.type !== 'appHang') {
|
|
2054
|
+
return false;
|
|
2055
|
+
}
|
|
2056
|
+
const { execId, bundleId, hang } = message as unknown as AppHangMessage;
|
|
2057
|
+
if (typeof execId !== 'string' || typeof bundleId !== 'string' || typeof hang !== 'object' || !hang) {
|
|
2058
|
+
logger.warn('Received malformed appHang message:', message);
|
|
2059
|
+
return true;
|
|
2060
|
+
}
|
|
2061
|
+
const notification = getServerNotification('appHang', execId);
|
|
2062
|
+
if (!notification) {
|
|
2063
|
+
logger.debug(`Received appHang for unknown or stopped execId: ${execId}`);
|
|
2064
|
+
return true;
|
|
2065
|
+
}
|
|
2066
|
+
void notification(message).catch((error) => {
|
|
2067
|
+
logger.error(`Error processing appHang notification for execId ${execId}:`, error);
|
|
2068
|
+
});
|
|
2069
|
+
return true;
|
|
2070
|
+
};
|
|
2071
|
+
|
|
1959
2072
|
// Response handlers - transform raw responses to typed results
|
|
1960
2073
|
const responseHandlers: Record<string, (msg: ServerResponse) => unknown> = {
|
|
1961
2074
|
screenshotResult: (msg) => ({
|
|
@@ -1979,6 +2092,7 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
1979
2092
|
launchAppResult: () => undefined,
|
|
1980
2093
|
terminateAppResult: () => undefined,
|
|
1981
2094
|
watchAppResult: () => undefined,
|
|
2095
|
+
monitorAppResult: () => undefined,
|
|
1982
2096
|
unwatchAppResult: () => undefined,
|
|
1983
2097
|
appLogTailResult: (msg) => msg.logs ?? '',
|
|
1984
2098
|
listAppsResult: (msg) => JSON.parse(msg.apps || '[]') as InstalledApp[],
|
|
@@ -2048,7 +2162,7 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2048
2162
|
return;
|
|
2049
2163
|
}
|
|
2050
2164
|
|
|
2051
|
-
if (handleAppExit(message)) {
|
|
2165
|
+
if (handleAppExit(message) || handleAppHang(message)) {
|
|
2052
2166
|
return;
|
|
2053
2167
|
}
|
|
2054
2168
|
|
|
@@ -2190,6 +2304,7 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2190
2304
|
launchApp,
|
|
2191
2305
|
terminateApp,
|
|
2192
2306
|
watchApp,
|
|
2307
|
+
monitorApp,
|
|
2193
2308
|
appLogTail,
|
|
2194
2309
|
streamAppLog,
|
|
2195
2310
|
streamSyslog,
|
|
@@ -2416,7 +2531,10 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2416
2531
|
return sendRequest<void>('toggleKeyboard');
|
|
2417
2532
|
};
|
|
2418
2533
|
|
|
2419
|
-
const launchApp = (
|
|
2534
|
+
const launchApp = async (
|
|
2535
|
+
bundleId: string,
|
|
2536
|
+
modeOrOptions?: LaunchAppMode | LaunchAppOptions,
|
|
2537
|
+
): Promise<void> => {
|
|
2420
2538
|
const launchOptions: LaunchAppOptions =
|
|
2421
2539
|
typeof modeOrOptions === 'string' ? { mode: modeOrOptions } : modeOrOptions ?? {};
|
|
2422
2540
|
const requestedMode =
|
|
@@ -2424,8 +2542,8 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2424
2542
|
(modeOrOptions as { mode?: LaunchAppMode }).mode
|
|
2425
2543
|
: launchOptions.mode;
|
|
2426
2544
|
if (launchOptions.runtime && requestedMode === 'ForegroundIfRunning') {
|
|
2427
|
-
|
|
2428
|
-
|
|
2545
|
+
throw new Error(
|
|
2546
|
+
'launchApp runtime launches require RelaunchIfRunning so runtime injection is applied.',
|
|
2429
2547
|
);
|
|
2430
2548
|
}
|
|
2431
2549
|
const hasEnvironment = Object.keys(launchOptions.env ?? {}).length > 0;
|
|
@@ -2433,23 +2551,34 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2433
2551
|
launchOptions.runtime ? 'RelaunchIfRunning' : (
|
|
2434
2552
|
launchOptions.mode ?? (hasEnvironment ? 'RelaunchIfRunning' : undefined)
|
|
2435
2553
|
);
|
|
2436
|
-
const onExit = launchOptions
|
|
2437
|
-
const execId = onExit ? generateId() : undefined;
|
|
2438
|
-
if (execId
|
|
2439
|
-
|
|
2554
|
+
const { onExit, onHang } = launchOptions;
|
|
2555
|
+
const execId = onExit || onHang ? generateId() : undefined;
|
|
2556
|
+
if (execId) {
|
|
2557
|
+
const exitHandler = onExit ? appExitHandler(execId, onExit) : undefined;
|
|
2558
|
+
if (onHang) registerServerNotification('appHang', execId, appHangHandler(execId, onHang));
|
|
2559
|
+
// The launch's hang reports end with its exit.
|
|
2560
|
+
registerServerNotification('appExit', execId, async (message) => {
|
|
2561
|
+
deleteServerNotification('appHang', execId);
|
|
2562
|
+
await exitHandler?.(message);
|
|
2563
|
+
});
|
|
2440
2564
|
}
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2565
|
+
try {
|
|
2566
|
+
await sendRequest<void>('launchApp', {
|
|
2567
|
+
bundleId,
|
|
2568
|
+
mode,
|
|
2569
|
+
runtime: launchOptions.runtime,
|
|
2570
|
+
env: launchOptions.env,
|
|
2571
|
+
execId,
|
|
2572
|
+
// Sent only with onHang so other launches stay byte-identical.
|
|
2573
|
+
hangs: onHang ? true : undefined,
|
|
2574
|
+
});
|
|
2575
|
+
} catch (error) {
|
|
2448
2576
|
if (execId) {
|
|
2449
2577
|
deleteServerNotification('appExit', execId);
|
|
2578
|
+
deleteServerNotification('appHang', execId);
|
|
2450
2579
|
}
|
|
2451
2580
|
throw error;
|
|
2452
|
-
}
|
|
2581
|
+
}
|
|
2453
2582
|
};
|
|
2454
2583
|
|
|
2455
2584
|
/** Delivers one appExit to the callback, fetching the logs from servers that predate inline logs. */
|
|
@@ -2503,6 +2632,45 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2503
2632
|
};
|
|
2504
2633
|
};
|
|
2505
2634
|
|
|
2635
|
+
/** Delivers each appHang of a launch or hang watch to the callback. */
|
|
2636
|
+
const appHangHandler =
|
|
2637
|
+
(execId: string, onHang: AppHangCallback): ServerNotificationHandler =>
|
|
2638
|
+
async (response) => {
|
|
2639
|
+
const { hang, bundleId } = response as unknown as AppHangMessage;
|
|
2640
|
+
try {
|
|
2641
|
+
await onHang({ ...hang, bundleId });
|
|
2642
|
+
} catch (error) {
|
|
2643
|
+
logger.error(`Error in onHang callback for execId ${execId}:`, error);
|
|
2644
|
+
}
|
|
2645
|
+
};
|
|
2646
|
+
|
|
2647
|
+
const monitorApp = async (
|
|
2648
|
+
bundleId: string,
|
|
2649
|
+
{ onExit, onHang }: AppMonitorCallbacks,
|
|
2650
|
+
): Promise<AppWatch> => {
|
|
2651
|
+
const execId = generateId();
|
|
2652
|
+
const forget = () => {
|
|
2653
|
+
deleteServerNotification('monitorExit', execId);
|
|
2654
|
+
deleteServerNotification('appHang', execId);
|
|
2655
|
+
};
|
|
2656
|
+
if (onExit) registerServerNotification('monitorExit', execId, appExitHandler(execId, onExit));
|
|
2657
|
+
if (onHang) registerServerNotification('appHang', execId, appHangHandler(execId, onHang));
|
|
2658
|
+
try {
|
|
2659
|
+
// Simulators that predate monitors never answer, so the wait stays short.
|
|
2660
|
+
await sendRequest<void>('monitorApp', { bundleId, execId }, undefined, 10_000);
|
|
2661
|
+
} catch (error) {
|
|
2662
|
+
forget();
|
|
2663
|
+
throw error;
|
|
2664
|
+
}
|
|
2665
|
+
return {
|
|
2666
|
+
execId,
|
|
2667
|
+
stop: async () => {
|
|
2668
|
+
forget();
|
|
2669
|
+
await sendRequest<void>('unwatchApp', { execId });
|
|
2670
|
+
},
|
|
2671
|
+
};
|
|
2672
|
+
};
|
|
2673
|
+
|
|
2506
2674
|
const terminateApp = (bundleId: string): Promise<void> => {
|
|
2507
2675
|
return sendRequest<void>('terminateApp', { bundleId });
|
|
2508
2676
|
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Uploaded by the daemon after the build, using fresh workload credentials unless token is supplied. */
|
|
2
|
+
export interface AssetUpload {
|
|
3
|
+
target: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
/** Asset expiry as a Go duration. Build uploads default to 336h. */
|
|
6
|
+
ttl?: string;
|
|
7
|
+
/** Metadata applied before the upload. Requires update permission. */
|
|
8
|
+
uploadOptions?: AssetUploadMetadata;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AssetUploadMetadata {
|
|
12
|
+
displayName?: string;
|
|
13
|
+
bundleIdentifier?: string;
|
|
14
|
+
shortVersion?: string;
|
|
15
|
+
buildVersion?: string;
|
|
16
|
+
deeplink?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type BuildUpload =
|
|
20
|
+
| (AssetUpload & { assetName?: never; signedUploadUrl?: never })
|
|
21
|
+
| {
|
|
22
|
+
signedUploadUrl: string;
|
|
23
|
+
target?: never;
|
|
24
|
+
assetName?: never;
|
|
25
|
+
ttl?: never;
|
|
26
|
+
token?: never;
|
|
27
|
+
uploadOptions?: never;
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
/** @deprecated Use a literal target such as "name/result.apk". Authorization now happens in the workload. */
|
|
31
|
+
assetName: string;
|
|
32
|
+
ttl?: string;
|
|
33
|
+
uploadOptions?: AssetUploadMetadata;
|
|
34
|
+
target?: never;
|
|
35
|
+
signedUploadUrl?: never;
|
|
36
|
+
token?: never;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const DEFAULT_BUILD_ASSET_TTL = '336h';
|
|
40
|
+
|
|
41
|
+
/** Normalize compatibility names without requesting an asset URL on the client. */
|
|
42
|
+
export function normalizeAssetUpload(
|
|
43
|
+
upload: BuildUpload,
|
|
44
|
+
): { assetUpload: AssetUpload; signedUploadUrl?: never } | { signedUploadUrl: string; assetUpload?: never } {
|
|
45
|
+
const modes = [upload.target, upload.assetName, upload.signedUploadUrl].filter((v) => v !== undefined);
|
|
46
|
+
if (modes.length !== 1) throw new Error('Provide exactly one upload target or signedUploadUrl');
|
|
47
|
+
if (upload.signedUploadUrl !== undefined) {
|
|
48
|
+
if (!upload.signedUploadUrl) throw new Error('signedUploadUrl must not be empty');
|
|
49
|
+
return { signedUploadUrl: upload.signedUploadUrl };
|
|
50
|
+
}
|
|
51
|
+
if (upload.assetName === '') throw new Error('assetName must not be empty');
|
|
52
|
+
const target = upload.target ?? `name/${upload.assetName!}`;
|
|
53
|
+
if (target.startsWith('name/')) {
|
|
54
|
+
if (target === 'name/') throw new Error('Name targets must include an exact name after name/');
|
|
55
|
+
} else if (!/^asset_[0-7][0-9a-hjkmnp-tv-z]{25}$/.test(target)) {
|
|
56
|
+
throw new Error('Upload target must be an asset ID or an exact name target');
|
|
57
|
+
}
|
|
58
|
+
if (upload.token !== undefined && !upload.token.trim()) throw new Error('Upload token must not be empty');
|
|
59
|
+
return {
|
|
60
|
+
assetUpload: {
|
|
61
|
+
target,
|
|
62
|
+
ttl: upload.ttl || DEFAULT_BUILD_ASSET_TTL,
|
|
63
|
+
...(upload.token !== undefined && { token: upload.token }),
|
|
64
|
+
...(upload.uploadOptions !== undefined && { uploadOptions: upload.uploadOptions }),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { MergedRequestInit } from '../internal/types';
|
|
2
|
+
|
|
3
|
+
/** Refreshes workload credentials without retaining the projected JWT. */
|
|
4
|
+
export class WorkloadCredentials {
|
|
5
|
+
private cached?: { token: string; expires: number };
|
|
6
|
+
private pending: Promise<string> | undefined;
|
|
7
|
+
|
|
8
|
+
constructor(
|
|
9
|
+
private endpoint: string,
|
|
10
|
+
private tokenFile: string,
|
|
11
|
+
private fetcher: typeof fetch,
|
|
12
|
+
private fetchOptions?: MergedRequestInit,
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
async get(): Promise<string> {
|
|
16
|
+
if (this.cached && this.cached.expires - Date.now() > 60_000) return this.cached.token;
|
|
17
|
+
if (!this.pending)
|
|
18
|
+
this.pending = this.exchange().finally(() => {
|
|
19
|
+
this.pending = undefined;
|
|
20
|
+
});
|
|
21
|
+
return this.pending;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private async exchange(): Promise<string> {
|
|
25
|
+
const { readFile } = await import('node:fs/promises');
|
|
26
|
+
const jwt = (await readFile(this.tokenFile, 'utf8')).trim();
|
|
27
|
+
const response = await this.fetcher(`${this.endpoint.replace(/\/$/, '')}/v1/access_tokens`, {
|
|
28
|
+
...(this.fetchOptions as RequestInit),
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: { Authorization: `Bearer ${jwt}`, 'Content-Type': 'application/json' },
|
|
31
|
+
body: JSON.stringify({}),
|
|
32
|
+
signal: AbortSignal.timeout(30_000),
|
|
33
|
+
redirect: 'error',
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) throw new Error(`Workload token exchange returned HTTP ${response.status}`);
|
|
36
|
+
const body = (await response.json()) as { token?: string; expiresAt: string };
|
|
37
|
+
if (!body.token) throw new Error('This instance has no delegated asset permissions');
|
|
38
|
+
const expires = Date.parse(body.expiresAt);
|
|
39
|
+
if (!Number.isFinite(expires) || expires <= Date.now())
|
|
40
|
+
throw new Error('Workload token exchange returned an invalid expiration');
|
|
41
|
+
this.cached = { token: body.token, expires };
|
|
42
|
+
return body.token;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { APIResource } from '../core/resource';
|
|
2
|
+
import { APIPromise } from '../core/api-promise';
|
|
3
|
+
import { RequestOptions } from '../internal/request-options';
|
|
4
|
+
|
|
5
|
+
export class AccessTokens extends APIResource {
|
|
6
|
+
/** Mint a temporary credential whose permissions are covered by the caller. */
|
|
7
|
+
create(body: AccessTokenCreateParams, options?: RequestOptions): APIPromise<AccessToken> {
|
|
8
|
+
return this._client.post('/v1/access_tokens', { body, ...options });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Revoke a credential by the ID returned when it was minted. */
|
|
12
|
+
revoke(id: string, options?: RequestOptions): APIPromise<{ status: string }> {
|
|
13
|
+
return this._client.delete(`/v1/access_tokens/${encodeURIComponent(id)}`, options);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface AccessToken {
|
|
18
|
+
id: string;
|
|
19
|
+
token: string;
|
|
20
|
+
permissions: Array<string>;
|
|
21
|
+
/** @deprecated Use permissions. */
|
|
22
|
+
scopes: Array<string>;
|
|
23
|
+
expiresAt: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AccessTokenCreateParams {
|
|
27
|
+
/**
|
|
28
|
+
* Permissions in the form `<resource>:<id|*>:<action>`, e.g. `device:*:install`,
|
|
29
|
+
* `asset:asset_01h455vb4pex5vsknk084sn02q:read` or `applerelay:*:connect`.
|
|
30
|
+
* Resource IDs are the customer-visible IDs returned by the API.
|
|
31
|
+
*/
|
|
32
|
+
permissions?: Array<string>;
|
|
33
|
+
|
|
34
|
+
/** @deprecated Use permissions. If both are supplied, their permission sets must match. */
|
|
35
|
+
scopes?: Array<string>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* How long the token stays valid. Defaults to 3600 (1 hour), maximum is 14400 (4
|
|
39
|
+
* hours).
|
|
40
|
+
*/
|
|
41
|
+
ttlSeconds?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export declare namespace AccessTokens {
|
|
45
|
+
export { type AccessToken as AccessToken, type AccessTokenCreateParams as AccessTokenCreateParams };
|
|
46
|
+
}
|
|
@@ -190,6 +190,12 @@ export namespace AndroidInstanceCreateParams {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
export interface Spec {
|
|
193
|
+
/** Inline instance role. Omit for the organization's compatibility default. */
|
|
194
|
+
role?: {
|
|
195
|
+
/** Asset permissions delegated by the creator. An empty array grants nothing. */
|
|
196
|
+
permissions: Array<string>;
|
|
197
|
+
};
|
|
198
|
+
|
|
193
199
|
/**
|
|
194
200
|
* ClientLocation takes precedence over ClientIP regardless of clue order. Without
|
|
195
201
|
* either clue, routing uses Cloudflare visitor coordinates, then its client IP.
|