@limrun/api 0.66.0 → 0.67.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +64 -0
  3. package/client.d.mts +7 -1
  4. package/client.d.mts.map +1 -1
  5. package/client.d.ts +7 -1
  6. package/client.d.ts.map +1 -1
  7. package/client.js +17 -3
  8. package/client.js.map +1 -1
  9. package/client.mjs +17 -3
  10. package/client.mjs.map +1 -1
  11. package/exec-client.d.mts +3 -6
  12. package/exec-client.d.mts.map +1 -1
  13. package/exec-client.d.ts +3 -6
  14. package/exec-client.d.ts.map +1 -1
  15. package/exec-client.js +4 -4
  16. package/exec-client.js.map +1 -1
  17. package/exec-client.mjs.map +1 -1
  18. package/index.d.mts +1 -0
  19. package/index.d.mts.map +1 -1
  20. package/index.d.ts +1 -0
  21. package/index.d.ts.map +1 -1
  22. package/internal/tslib.js +20 -20
  23. package/lib/asset-permissions.d.mts +45 -0
  24. package/lib/asset-permissions.d.mts.map +1 -0
  25. package/lib/asset-permissions.d.ts +45 -0
  26. package/lib/asset-permissions.d.ts.map +1 -0
  27. package/lib/asset-permissions.js +37 -0
  28. package/lib/asset-permissions.js.map +1 -0
  29. package/lib/asset-permissions.mjs +33 -0
  30. package/lib/asset-permissions.mjs.map +1 -0
  31. package/lib/workload-credentials.d.mts +14 -0
  32. package/lib/workload-credentials.d.mts.map +1 -0
  33. package/lib/workload-credentials.d.ts +14 -0
  34. package/lib/workload-credentials.d.ts.map +1 -0
  35. package/lib/workload-credentials.js +78 -0
  36. package/lib/workload-credentials.js.map +1 -0
  37. package/lib/workload-credentials.mjs +41 -0
  38. package/lib/workload-credentials.mjs.map +1 -0
  39. package/package.json +12 -2
  40. package/resources/access-tokens.d.mts +39 -0
  41. package/resources/access-tokens.d.mts.map +1 -0
  42. package/resources/access-tokens.d.ts +39 -0
  43. package/resources/access-tokens.d.ts.map +1 -0
  44. package/resources/access-tokens.js +16 -0
  45. package/resources/access-tokens.js.map +1 -0
  46. package/resources/access-tokens.mjs +12 -0
  47. package/resources/access-tokens.mjs.map +1 -0
  48. package/resources/android-instances.d.mts +5 -0
  49. package/resources/android-instances.d.mts.map +1 -1
  50. package/resources/android-instances.d.ts +5 -0
  51. package/resources/android-instances.d.ts.map +1 -1
  52. package/resources/assets.d.mts +25 -1
  53. package/resources/assets.d.mts.map +1 -1
  54. package/resources/assets.d.ts +25 -1
  55. package/resources/assets.d.ts.map +1 -1
  56. package/resources/assets.js +12 -0
  57. package/resources/assets.js.map +1 -1
  58. package/resources/assets.mjs +12 -0
  59. package/resources/assets.mjs.map +1 -1
  60. package/resources/daemon-client-shared.d.mts +2 -36
  61. package/resources/daemon-client-shared.d.mts.map +1 -1
  62. package/resources/daemon-client-shared.d.ts +2 -36
  63. package/resources/daemon-client-shared.d.ts.map +1 -1
  64. package/resources/daemon-client-shared.js +2 -21
  65. package/resources/daemon-client-shared.js.map +1 -1
  66. package/resources/daemon-client-shared.mjs +1 -20
  67. package/resources/daemon-client-shared.mjs.map +1 -1
  68. package/resources/gradle-instances-helpers.d.mts +2 -15
  69. package/resources/gradle-instances-helpers.d.mts.map +1 -1
  70. package/resources/gradle-instances-helpers.d.ts +2 -15
  71. package/resources/gradle-instances-helpers.d.ts.map +1 -1
  72. package/resources/gradle-instances-helpers.js +3 -11
  73. package/resources/gradle-instances-helpers.js.map +1 -1
  74. package/resources/gradle-instances-helpers.mjs +4 -12
  75. package/resources/gradle-instances-helpers.mjs.map +1 -1
  76. package/resources/gradle-instances.d.mts +5 -0
  77. package/resources/gradle-instances.d.mts.map +1 -1
  78. package/resources/gradle-instances.d.ts +5 -0
  79. package/resources/gradle-instances.d.ts.map +1 -1
  80. package/resources/index.d.mts +1 -0
  81. package/resources/index.d.mts.map +1 -1
  82. package/resources/index.d.ts +1 -0
  83. package/resources/index.d.ts.map +1 -1
  84. package/resources/index.js +3 -1
  85. package/resources/index.js.map +1 -1
  86. package/resources/index.mjs +1 -0
  87. package/resources/index.mjs.map +1 -1
  88. package/resources/ios-instances.d.mts +5 -0
  89. package/resources/ios-instances.d.mts.map +1 -1
  90. package/resources/ios-instances.d.ts +5 -0
  91. package/resources/ios-instances.d.ts.map +1 -1
  92. package/resources/scoped-tokens.d.mts +13 -16
  93. package/resources/scoped-tokens.d.mts.map +1 -1
  94. package/resources/scoped-tokens.d.ts +13 -16
  95. package/resources/scoped-tokens.d.ts.map +1 -1
  96. package/resources/scoped-tokens.js +4 -3
  97. package/resources/scoped-tokens.js.map +1 -1
  98. package/resources/scoped-tokens.mjs +4 -3
  99. package/resources/scoped-tokens.mjs.map +1 -1
  100. package/resources/xcode-instances-helpers.d.mts +5 -38
  101. package/resources/xcode-instances-helpers.d.mts.map +1 -1
  102. package/resources/xcode-instances-helpers.d.ts +5 -38
  103. package/resources/xcode-instances-helpers.d.ts.map +1 -1
  104. package/resources/xcode-instances-helpers.js +7 -29
  105. package/resources/xcode-instances-helpers.js.map +1 -1
  106. package/resources/xcode-instances-helpers.mjs +8 -30
  107. package/resources/xcode-instances-helpers.mjs.map +1 -1
  108. package/resources/xcode-instances.d.mts +5 -0
  109. package/resources/xcode-instances.d.mts.map +1 -1
  110. package/resources/xcode-instances.d.ts +5 -0
  111. package/resources/xcode-instances.d.ts.map +1 -1
  112. package/src/client.ts +35 -2
  113. package/src/exec-client.ts +3 -0
  114. package/src/index.ts +2 -0
  115. package/src/lib/asset-permissions.ts +67 -0
  116. package/src/lib/workload-credentials.ts +44 -0
  117. package/src/resources/access-tokens.ts +54 -0
  118. package/src/resources/android-instances.ts +6 -0
  119. package/src/resources/assets.ts +38 -0
  120. package/src/resources/daemon-client-shared.ts +3 -48
  121. package/src/resources/gradle-instances-helpers.ts +3 -30
  122. package/src/resources/gradle-instances.ts +6 -0
  123. package/src/resources/index.ts +7 -0
  124. package/src/resources/ios-instances.ts +6 -0
  125. package/src/resources/scoped-tokens.ts +15 -17
  126. package/src/resources/xcode-instances-helpers.ts +9 -75
  127. package/src/resources/xcode-instances.ts +6 -0
  128. package/src/version.ts +1 -1
  129. package/version.d.mts +1 -1
  130. package/version.d.ts +1 -1
  131. package/version.js +1 -1
  132. package/version.mjs +1 -1
@@ -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,54 @@
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<AccessTokenRevokeResponse> {
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 interface AccessTokenRevokeResponse {
45
+ status: string;
46
+ }
47
+
48
+ export declare namespace AccessTokens {
49
+ export {
50
+ type AccessToken as AccessToken,
51
+ type AccessTokenCreateParams as AccessTokenCreateParams,
52
+ type AccessTokenRevokeResponse,
53
+ };
54
+ }
@@ -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.
@@ -7,6 +7,20 @@ import { RequestOptions } from '../internal/request-options';
7
7
  import { path } from '../internal/utils/path';
8
8
 
9
9
  export class Assets extends APIResource {
10
+ /** Obtain upload URLs and the existing file MD5. Downloads require read permission. */
11
+ createUploadURL(assetID: string, options?: RequestOptions): APIPromise<AssetUploadURLs> {
12
+ return this._client.get(path`/v1/assets/${assetID}/upload_urls`, options);
13
+ }
14
+
15
+ /** Update editable metadata. The lookup name cannot be changed. */
16
+ update(assetID: string, body: AssetUpdateParams, options?: RequestOptions): APIPromise<void> {
17
+ return this._client.patch(path`/v1/assets/${assetID}`, {
18
+ body,
19
+ ...options,
20
+ headers: buildHeaders([{ Accept: '*/*' }, options?.headers]),
21
+ });
22
+ }
23
+
10
24
  /**
11
25
  * List organization's all assets with given filters. If none given, return all
12
26
  * assets.
@@ -99,8 +113,10 @@ export interface AssetGetOrCreateResponse {
99
113
 
100
114
  name: string;
101
115
 
116
+ /** Empty unless the credential grants read. */
102
117
  signedDownloadUrl: string;
103
118
 
119
+ /** Empty unless the credential grants upload. */
104
120
  signedUploadUrl: string;
105
121
 
106
122
  /**
@@ -235,5 +251,27 @@ export declare namespace Assets {
235
251
  type AssetListParams as AssetListParams,
236
252
  type AssetGetParams as AssetGetParams,
237
253
  type AssetGetOrCreateParams as AssetGetOrCreateParams,
254
+ type AssetUpdateParams as AssetUpdateParams,
255
+ type AssetUploadURLs as AssetUploadURLs,
238
256
  };
239
257
  }
258
+
259
+ export interface AssetUpdateParams {
260
+ displayName?: string;
261
+ bundleIdentifier?: string;
262
+ shortVersion?: string;
263
+ buildVersion?: string;
264
+ deeplink?: string;
265
+ targetSdk?: string;
266
+ ttl?: string;
267
+ }
268
+
269
+ export interface AssetUploadURLs {
270
+ /** Existing file MD5. Omitted when no file exists; storage-check failures return an error. */
271
+ md5?: string;
272
+ signedUploadUrl: string;
273
+ /** Upload the icon PNG with PUT and Content-Type: image/png. No other headers are required. */
274
+ signedIconUploadUrl: string;
275
+ /** Included only with read permission. */
276
+ iconUrl?: string;
277
+ }
@@ -71,54 +71,9 @@ export type SyncResult = {
71
71
 
72
72
  export type AssetUploadUrls = { id: string; signedUploadUrl: string; signedDownloadUrl: string };
73
73
 
74
- /**
75
- * App metadata recorded on the asset at upload time, for bundles that don't
76
- * go through limbuild (which extracts and records it automatically). The
77
- * registry's OTA install flow reads the manifest identity from these fields.
78
- */
79
- export type AssetUploadOptions = {
80
- /** Human-readable app title users see on iOS. */
81
- displayName?: string;
82
- /** CFBundleIdentifier of the uploaded app bundle. */
83
- bundleIdentifier?: string;
84
- /** CFBundleShortVersionString, e.g. 1.2.3. */
85
- shortVersion?: string;
86
- /** CFBundleVersion, e.g. 42. */
87
- buildVersion?: string;
88
- /** The app's primary URL scheme, e.g. "myapp" for myapp:// deep links. */
89
- deeplink?: string;
90
- };
91
-
92
- /**
93
- * Default TTL for build-product uploads. Every build upload without an
94
- * explicit ttl pushes the asset's expiry to 14 days from that upload, so
95
- * abandoned build artifacts don't accumulate in Asset Storage forever.
96
- * Assets uploaded through the general asset API keep no expiry by default.
97
- */
98
- export const DEFAULT_BUILD_ASSET_TTL = '336h';
99
-
100
- /**
101
- * Mints presigned upload/download URLs for a named asset via assets.getOrCreate,
102
- * wrapping failures with the asset name (and the original error as cause).
103
- * All build-product upload paths (xcodebuild, gradlebuild, RBE) go through
104
- * here, which is what scopes DEFAULT_BUILD_ASSET_TTL to build uploads.
105
- */
106
- export function mintAssetUploadUrls(
107
- assets: {
108
- getOrCreate: (body: { name: string; ttl?: string } & AssetUploadOptions) => Promise<AssetUploadUrls>;
109
- },
110
- name: string,
111
- ttl?: string,
112
- uploadOptions?: AssetUploadOptions,
113
- ): Promise<AssetUploadUrls> {
114
- return assets.getOrCreate({ name, ttl: ttl || DEFAULT_BUILD_ASSET_TTL, ...uploadOptions }).catch((err) => {
115
- const message = `Failed to create upload URL for asset '${name}': ${
116
- err instanceof Error ? err.message : err
117
- }`;
118
- // @ts-ignore - not all envs have native support for cause yet
119
- throw new Error(message, { cause: err });
120
- });
121
- }
74
+ // Compatibility exports for callers that imported build-upload metadata here.
75
+ export type { AssetUploadMetadata as AssetUploadOptions } from '../lib/asset-permissions';
76
+ export { DEFAULT_BUILD_ASSET_TTL } from '../lib/asset-permissions';
122
77
 
123
78
  const logLevels: LogLevel[] = ['none', 'error', 'warn', 'info', 'debug'];
124
79
 
@@ -1,4 +1,5 @@
1
1
  import { pullBuildFile } from '../internal/build-pull-file';
2
+ import { normalizeAssetUpload, type BuildUpload } from '../lib/asset-permissions';
2
3
  import { GradleInstances, type GradleInstance } from './gradle-instances';
3
4
  import { APIPromise } from '../core/api-promise';
4
5
  import { RequestOptions } from '../internal/request-options';
@@ -24,7 +25,6 @@ import {
24
25
  type BuildRunOptions,
25
26
  createDaemonLogger,
26
27
  deriveBasisCache,
27
- mintAssetUploadUrls,
28
28
  type BuildLog,
29
29
  type LogLevel,
30
30
  type SyncResult,
@@ -72,19 +72,7 @@ export type GradleBuildOptions = {
72
72
  /** Relative path to the Gradle root when auto-discovery is ambiguous. */
73
73
  projectPath?: string;
74
74
  /** Upload the built artifact as a named org asset, or to a presigned URL. */
75
- upload?:
76
- | {
77
- assetName: string;
78
- /**
79
- * Asset TTL as a Go duration (e.g. "24h", "30m"; "1d" is invalid),
80
- * forwarded to assets.getOrCreate. Defaults to 336h (14 days): each
81
- * build upload without an explicit ttl pushes the asset's expiry to
82
- * 14 days from that upload.
83
- */
84
- ttl?: string;
85
- signedUploadUrl?: never;
86
- }
87
- | { signedUploadUrl: string; assetName?: never; ttl?: never };
75
+ upload?: BuildUpload;
88
76
  /** React Native / Expo tuning. */
89
77
  reactNative?: GradleReactNativeConfig;
90
78
  /** Release signing config; presence makes bundleRelease the default task. */
@@ -227,22 +215,7 @@ class GradleInstancesHelpers extends GradleInstances {
227
215
  ...(options?.playstore && { playstore: options.playstore }),
228
216
  };
229
217
 
230
- if (options?.upload && 'assetName' in options.upload) {
231
- const requestPromise = mintAssetUploadUrls(
232
- client.assets,
233
- options.upload.assetName,
234
- options.upload.ttl,
235
- ).then((asset) => {
236
- request.signedUploadUrl = asset.signedUploadUrl;
237
- request.additionalMetadata = { signedDownloadUrl: asset.signedDownloadUrl };
238
- return request;
239
- });
240
- return exec(requestPromise, { apiUrl, token, log });
241
- }
242
-
243
- if (options?.upload && 'signedUploadUrl' in options.upload) {
244
- request.signedUploadUrl = options.upload.signedUploadUrl;
245
- }
218
+ if (options?.upload) Object.assign(request, normalizeAssetUpload(options.upload));
246
219
 
247
220
  return exec(request, { apiUrl, token, log });
248
221
  },
@@ -139,6 +139,12 @@ export namespace GradleInstanceCreateParams {
139
139
  }
140
140
 
141
141
  export interface Spec {
142
+ /** Inline instance role. Omit for the organization's compatibility default. */
143
+ role?: {
144
+ /** Asset permissions delegated by the creator. An empty array grants nothing. */
145
+ permissions: Array<string>;
146
+ };
147
+
142
148
  /**
143
149
  * ClientLocation takes precedence over ClientIP regardless of clue order. Without
144
150
  * either clue, routing uses Cloudflare visitor coordinates, then its client IP.
@@ -97,3 +97,10 @@ export {
97
97
  type GradleBuildOptions,
98
98
  type GradleBuildLog,
99
99
  } from './gradle-instances-helpers';
100
+
101
+ export {
102
+ AccessTokens,
103
+ type AccessToken,
104
+ type AccessTokenCreateParams,
105
+ type AccessTokenRevokeResponse,
106
+ } from './access-tokens';
@@ -174,6 +174,12 @@ export namespace IosInstanceCreateParams {
174
174
  }
175
175
 
176
176
  export interface Spec {
177
+ /** Inline instance role. Omit for the organization's compatibility default. */
178
+ role?: {
179
+ /** Asset permissions delegated by the creator. An empty array grants nothing. */
180
+ permissions: Array<string>;
181
+ };
182
+
177
183
  /**
178
184
  * ClientLocation takes precedence over ClientIP regardless of clue order. Without
179
185
  * either clue, routing uses Cloudflare visitor coordinates, then its client IP.
@@ -1,22 +1,28 @@
1
1
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
+ import type { AccessTokenCreateParams } from './access-tokens';
3
4
  import { APIResource } from '../core/resource';
4
5
  import { APIPromise } from '../core/api-promise';
5
6
  import { RequestOptions } from '../internal/request-options';
6
7
 
8
+ /** @deprecated Use AccessTokens. */
7
9
  export class ScopedTokens extends APIResource {
8
10
  /**
9
- * Mint a short-lived scoped token whose scopes limit what the holder can do, e.g.
11
+ * Mint a short-lived scoped token whose permissions limit what the holder can do, e.g.
10
12
  * install a specific asset on a device through the registry. The token is verified
11
- * offline by services holding the token signing public key and cannot be revoked,
12
- * so keep TTLs short. It is bound to the authenticated caller's organization.
13
+ * by services holding the signing public key. It is bound to the caller's organization.
14
+ * @deprecated Use client.accessTokens.create(). This method keeps the legacy URL.
13
15
  */
14
16
  create(body: ScopedTokenCreateParams, options?: RequestOptions): APIPromise<ScopedToken> {
15
17
  return this._client.post('/v1/scoped_tokens', { body, ...options });
16
18
  }
17
19
  }
18
20
 
21
+ /** @deprecated Use AccessToken. */
19
22
  export interface ScopedToken {
23
+ /** Credential ID, when returned by the server. */
24
+ id?: string;
25
+
20
26
  /**
21
27
  * The scoped token, to be sent as a Bearer token or the token query parameter.
22
28
  */
@@ -24,24 +30,16 @@ export interface ScopedToken {
24
30
 
25
31
  expiresAt: string;
26
32
 
27
- scopes: Array<string>;
28
- }
33
+ /** Permissions granted to this credential. */
34
+ permissions?: Array<string>;
29
35
 
30
- export interface ScopedTokenCreateParams {
31
- /**
32
- * Scopes in the form `<resource>:<id|*>:<action>`, e.g. `device:*:install`,
33
- * `asset:asset_01h455vb4pex5vsknk084sn02q:read` or `applerelay:*:connect`.
34
- * Resource IDs are the customer-visible IDs returned by the API.
35
- */
36
+ /** @deprecated Use permissions. */
36
37
  scopes: Array<string>;
37
-
38
- /**
39
- * How long the token stays valid. Defaults to 3600 (1 hour), maximum is 14400 (4
40
- * hours).
41
- */
42
- ttlSeconds?: number;
43
38
  }
44
39
 
40
+ /** @deprecated Use AccessTokenCreateParams. */
41
+ export type ScopedTokenCreateParams = AccessTokenCreateParams;
42
+
45
43
  export declare namespace ScopedTokens {
46
44
  export { type ScopedToken as ScopedToken, type ScopedTokenCreateParams as ScopedTokenCreateParams };
47
45
  }
@@ -1,4 +1,5 @@
1
1
  import { pullBuildFile } from '../internal/build-pull-file';
2
+ import { normalizeAssetUpload, type BuildUpload } from '../lib/asset-permissions';
2
3
  import {
3
4
  XcodeInstances as GeneratedXcodeInstances,
4
5
  type XcodeInstance,
@@ -48,8 +49,6 @@ import {
48
49
  type BuildRunOptions,
49
50
  createDaemonLogger,
50
51
  deriveBasisCache,
51
- mintAssetUploadUrls,
52
- type AssetUploadOptions,
53
52
  type BuildLog,
54
53
  type LogLevel,
55
54
  type SyncResult,
@@ -238,25 +237,7 @@ export type ReactNativeBuildConfig = {
238
237
  };
239
238
 
240
239
  export type XcodeBuildOptions = {
241
- upload?:
242
- | {
243
- assetName: string;
244
- /**
245
- * Asset TTL as a Go duration (e.g. "24h", "30m"; "1d" is invalid),
246
- * forwarded to assets.getOrCreate. Defaults to 336h (14 days): each
247
- * build upload without an explicit ttl pushes the asset's expiry to
248
- * 14 days from that upload.
249
- */
250
- ttl?: string;
251
- /**
252
- * App metadata to record on the asset up front. limbuild extracts
253
- * the same metadata from the built bundle after the build and
254
- * overwrites whatever it found; values set here survive only for
255
- * fields the bundle doesn't declare (e.g. a deep link scheme).
256
- */
257
- uploadOptions?: AssetUploadOptions;
258
- }
259
- | { signedUploadUrl: string };
240
+ upload?: BuildUpload;
260
241
  signing?: XcodeSigningConfig;
261
242
  cloudSigning?: XcodeCloudSigningConfig;
262
243
  /**
@@ -457,32 +438,14 @@ export type RbeInstallResult = {
457
438
  installDurationMs?: number;
458
439
  };
459
440
 
460
- export type RbeUploadOptions =
461
- | {
462
- /** Upload as a named asset: the SDK mints the presigned upload URL via assets.getOrCreate. */
463
- assetName: string;
464
- /**
465
- * Optional asset TTL as a Go duration (e.g. "24h", "30m"; "1d" is
466
- * invalid), forwarded to assets.getOrCreate. Defaults to 336h (14
467
- * days): each upload without an explicit ttl pushes the asset's
468
- * expiry to 14 days from that upload.
469
- */
470
- ttl?: string;
471
- signedUploadUrl?: never;
472
- }
473
- | {
474
- /** Upload to a caller-minted presigned Limrun asset storage URL. */
475
- signedUploadUrl: string;
476
- assetName?: never;
477
- ttl?: never;
478
- };
441
+ export type RbeUploadOptions = BuildUpload;
479
442
 
480
443
  export type RbeUploadResult = {
481
444
  /** The .app bundle name of the uploaded build; it is the root directory of the uploaded tar.gz. */
482
445
  appName: string;
483
446
  /** CFBundleIdentifier of the uploaded app, when known. */
484
447
  bundleId?: string;
485
- /** Presigned download URL of the asset; present when uploading by assetName. */
448
+ /** @deprecated Target uploads do not return a download URL. Request one separately with read permission. */
486
449
  signedDownloadUrl?: string;
487
450
  };
488
451
 
@@ -958,7 +921,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
958
921
  iosInstanceId: string;
959
922
  simulator: IosInstance;
960
923
  }> => {
961
- const simulator = await client.iosInstances.create({ wait: true });
924
+ const simulator = await client.iosInstances.create({ wait: true, spec: { role: { permissions: [] } } });
962
925
  // The sim exists from here; if attach fails, delete it so we never leak an
963
926
  // orphan the caller can't reap.
964
927
  try {
@@ -1064,25 +1027,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
1064
1027
  ...(options?.onXctestEvent && { onXctestEvent: options.onXctestEvent }),
1065
1028
  };
1066
1029
 
1067
- if (options?.upload && 'assetName' in options.upload) {
1068
- const requestPromise = mintAssetUploadUrls(
1069
- client.assets,
1070
- options.upload.assetName,
1071
- options.upload.ttl,
1072
- options.upload.uploadOptions,
1073
- ).then((asset) => {
1074
- request.signedUploadUrl = asset.signedUploadUrl;
1075
- // Lets limbuild record the built app's metadata on the asset.
1076
- request.assetId = asset.id;
1077
- request.additionalMetadata = { signedDownloadUrl: asset.signedDownloadUrl };
1078
- return request;
1079
- });
1080
- return exec(requestPromise, execOptions);
1081
- }
1082
-
1083
- if (options?.upload && 'signedUploadUrl' in options.upload) {
1084
- request.signedUploadUrl = options.upload.signedUploadUrl;
1085
- }
1030
+ if (options?.upload) Object.assign(request, normalizeAssetUpload(options.upload));
1086
1031
 
1087
1032
  return exec(request, execOptions);
1088
1033
  },
@@ -1176,18 +1121,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
1176
1121
  },
1177
1122
 
1178
1123
  async uploadLatestRbeBuild(opts: RbeUploadOptions): Promise<RbeUploadResult> {
1179
- let signedUploadUrl: string;
1180
- let signedDownloadUrl: string | undefined;
1181
- if (opts.assetName !== undefined) {
1182
- if (!opts.assetName) {
1183
- throw new Error('assetName must not be empty');
1184
- }
1185
- const asset = await mintAssetUploadUrls(client.assets, opts.assetName, opts.ttl);
1186
- signedUploadUrl = asset.signedUploadUrl;
1187
- signedDownloadUrl = asset.signedDownloadUrl;
1188
- } else {
1189
- signedUploadUrl = opts.signedUploadUrl;
1190
- }
1124
+ const body = normalizeAssetUpload(opts);
1191
1125
 
1192
1126
  const post = async (): Promise<{ appName: string; bundleId?: string }> => {
1193
1127
  // The daemon uploads to asset storage before responding, so this
@@ -1198,7 +1132,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
1198
1132
  'Content-Type': 'application/json',
1199
1133
  Authorization: `Bearer ${token}`,
1200
1134
  },
1201
- body: JSON.stringify({ signedUploadUrl }),
1135
+ body: JSON.stringify(body),
1202
1136
  });
1203
1137
  if (res.status === 404) {
1204
1138
  // A vanished instance and a limbuild that predates the endpoint
@@ -1221,7 +1155,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
1221
1155
  isTransientError(err) ||
1222
1156
  (isDirectInstanceHttpError(err, 400) && /no successful RBE app build/i.test(err.body));
1223
1157
  const result = await retryTransient(post, { retryOn });
1224
- return { ...result, ...(signedDownloadUrl && { signedDownloadUrl }) };
1158
+ return result;
1225
1159
  },
1226
1160
 
1227
1161
  async getActiveRbeBuilds(): Promise<RbeActiveBuild[]> {
@@ -138,6 +138,12 @@ export namespace XcodeInstanceCreateParams {
138
138
  }
139
139
 
140
140
  export interface Spec {
141
+ /** Inline instance role. Omit for the organization's compatibility default. */
142
+ role?: {
143
+ /** Asset permissions delegated by the creator. An empty array grants nothing. */
144
+ permissions: Array<string>;
145
+ };
146
+
141
147
  /**
142
148
  * ClientLocation takes precedence over ClientIP regardless of clue order. Without
143
149
  * either clue, routing uses Cloudflare visitor coordinates, then its client IP.
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.66.0'; // x-release-please-version
1
+ export const VERSION = '0.67.1'; // x-release-please-version
package/version.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.66.0";
1
+ export declare const VERSION = "0.67.1";
2
2
  //# sourceMappingURL=version.d.mts.map
package/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.66.0";
1
+ export declare const VERSION = "0.67.1";
2
2
  //# sourceMappingURL=version.d.ts.map
package/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '0.66.0'; // x-release-please-version
4
+ exports.VERSION = '0.67.1'; // x-release-please-version
5
5
  //# sourceMappingURL=version.js.map
package/version.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.66.0'; // x-release-please-version
1
+ export const VERSION = '0.67.1'; // x-release-please-version
2
2
  //# sourceMappingURL=version.mjs.map