@scenario-labs/sdk 2.2.1 → 2.4.0-alpha.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.
- package/CHANGELOG.md +35 -0
- package/client.d.mts.map +1 -1
- package/client.d.ts.map +1 -1
- package/client.js +9 -1
- package/client.js.map +1 -1
- package/client.mjs +9 -1
- package/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/index.js +3 -1
- package/index.js.map +1 -1
- package/index.mjs +1 -0
- package/index.mjs.map +1 -1
- package/lib/auth-refresh.d.mts +52 -0
- package/lib/auth-refresh.d.mts.map +1 -0
- package/lib/auth-refresh.d.ts +52 -0
- package/lib/auth-refresh.d.ts.map +1 -0
- package/lib/auth-refresh.js +81 -0
- package/lib/auth-refresh.js.map +1 -0
- package/lib/auth-refresh.mjs +78 -0
- package/lib/auth-refresh.mjs.map +1 -0
- package/lib/generate.js +7 -7
- package/lib/generate.js.map +1 -1
- package/lib/generate.mjs +7 -7
- package/lib/generate.mjs.map +1 -1
- package/lib/job.d.mts +15 -3
- package/lib/job.d.mts.map +1 -1
- package/lib/job.d.ts +15 -3
- package/lib/job.d.ts.map +1 -1
- package/lib/job.js +22 -8
- package/lib/job.js.map +1 -1
- package/lib/job.mjs +22 -8
- package/lib/job.mjs.map +1 -1
- package/lib/models.d.mts +8 -2
- package/lib/models.d.mts.map +1 -1
- package/lib/models.d.ts +8 -2
- package/lib/models.d.ts.map +1 -1
- package/lib/models.js +13 -5
- package/lib/models.js.map +1 -1
- package/lib/models.mjs +13 -5
- package/lib/models.mjs.map +1 -1
- package/lib/scenario.d.mts +28 -13
- package/lib/scenario.d.mts.map +1 -1
- package/lib/scenario.d.ts +28 -13
- package/lib/scenario.d.ts.map +1 -1
- package/lib/scenario.js +31 -12
- package/lib/scenario.js.map +1 -1
- package/lib/scenario.mjs +31 -12
- package/lib/scenario.mjs.map +1 -1
- package/lib/scope.d.mts +41 -0
- package/lib/scope.d.mts.map +1 -0
- package/lib/scope.d.ts +41 -0
- package/lib/scope.d.ts.map +1 -0
- package/lib/scope.js +55 -0
- package/lib/scope.js.map +1 -0
- package/lib/scope.mjs +49 -0
- package/lib/scope.mjs.map +1 -0
- package/lib/upload.d.mts +15 -3
- package/lib/upload.d.mts.map +1 -1
- package/lib/upload.d.ts +15 -3
- package/lib/upload.d.ts.map +1 -1
- package/lib/upload.js +29 -12
- package/lib/upload.js.map +1 -1
- package/lib/upload.mjs +29 -12
- package/lib/upload.mjs.map +1 -1
- package/lib/workflow.d.mts +8 -2
- package/lib/workflow.d.mts.map +1 -1
- package/lib/workflow.d.ts +8 -2
- package/lib/workflow.d.ts.map +1 -1
- package/lib/workflow.js +14 -6
- package/lib/workflow.js.map +1 -1
- package/lib/workflow.mjs +14 -6
- package/lib/workflow.mjs.map +1 -1
- package/lib/workflow_converter.d.mts +8 -2
- package/lib/workflow_converter.d.mts.map +1 -1
- package/lib/workflow_converter.d.ts +8 -2
- package/lib/workflow_converter.d.ts.map +1 -1
- package/lib/workflow_converter.js +83 -19
- package/lib/workflow_converter.js.map +1 -1
- package/lib/workflow_converter.mjs +83 -19
- package/lib/workflow_converter.mjs.map +1 -1
- package/lib/workflow_import_validator.d.mts +1 -1
- package/lib/workflow_import_validator.d.mts.map +1 -1
- package/lib/workflow_import_validator.d.ts +1 -1
- package/lib/workflow_import_validator.d.ts.map +1 -1
- package/lib/workflow_import_validator.js +1 -0
- package/lib/workflow_import_validator.js.map +1 -1
- package/lib/workflow_import_validator.mjs +1 -0
- package/lib/workflow_import_validator.mjs.map +1 -1
- package/package.json +9 -3
- package/resources/assets/assets.d.mts +1 -1
- package/resources/assets/assets.d.mts.map +1 -1
- package/resources/assets/assets.d.ts +1 -1
- package/resources/assets/assets.d.ts.map +1 -1
- package/resources/generate.d.mts +594 -10
- package/resources/generate.d.mts.map +1 -1
- package/resources/generate.d.ts +594 -10
- package/resources/generate.d.ts.map +1 -1
- package/resources/generate.js +2 -2
- package/resources/generate.js.map +1 -1
- package/resources/generate.mjs +2 -2
- package/resources/generate.mjs.map +1 -1
- package/resources/jobs.d.mts +3 -3
- package/resources/jobs.d.mts.map +1 -1
- package/resources/jobs.d.ts +3 -3
- package/resources/jobs.d.ts.map +1 -1
- package/resources/models/examples.d.mts +2 -2
- package/resources/models/examples.d.mts.map +1 -1
- package/resources/models/examples.d.ts +2 -2
- package/resources/models/examples.d.ts.map +1 -1
- package/resources/models/models.d.mts +126 -7
- package/resources/models/models.d.mts.map +1 -1
- package/resources/models/models.d.ts +126 -7
- package/resources/models/models.d.ts.map +1 -1
- package/resources/models/models.js.map +1 -1
- package/resources/models/models.mjs.map +1 -1
- package/resources/models/train.d.mts +106 -3
- package/resources/models/train.d.mts.map +1 -1
- package/resources/models/train.d.ts +106 -3
- package/resources/models/train.d.ts.map +1 -1
- package/resources/usages.d.mts +23 -3
- package/resources/usages.d.mts.map +1 -1
- package/resources/usages.d.ts +23 -3
- package/resources/usages.d.ts.map +1 -1
- package/resources/workflows.d.mts +22 -5
- package/resources/workflows.d.mts.map +1 -1
- package/resources/workflows.d.ts +22 -5
- package/resources/workflows.d.ts.map +1 -1
- package/resources/workflows.js +4 -1
- package/resources/workflows.js.map +1 -1
- package/resources/workflows.mjs +4 -1
- package/resources/workflows.mjs.map +1 -1
- package/src/client.ts +9 -1
- package/src/index.ts +1 -0
- package/src/lib/auth-refresh.ts +98 -0
- package/src/lib/generate.ts +7 -7
- package/src/lib/job.ts +29 -7
- package/src/lib/models.ts +14 -4
- package/src/lib/scenario.ts +51 -13
- package/src/lib/scope.ts +68 -0
- package/src/lib/upload.ts +36 -11
- package/src/lib/workflow.ts +15 -5
- package/src/lib/workflow_converter.ts +128 -19
- package/src/lib/workflow_import_validator.ts +3 -1
- package/src/resources/assets/assets.ts +1 -1
- package/src/resources/generate.ts +713 -12
- package/src/resources/jobs.ts +3 -3
- package/src/resources/models/examples.ts +2 -2
- package/src/resources/models/models.ts +189 -7
- package/src/resources/models/train.ts +138 -3
- package/src/resources/usages.ts +27 -1
- package/src/resources/workflows.ts +24 -5
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.mts.map +1 -1
- package/version.d.ts +1 -1
- package/version.d.ts.map +1 -1
- package/version.js +1 -1
- package/version.js.map +1 -1
- package/version.mjs +1 -1
- package/version.mjs.map +1 -1
package/src/lib/generate.ts
CHANGED
|
@@ -29,35 +29,35 @@ export class EnhancedGenerate extends Generate {
|
|
|
29
29
|
params: GenerateCaptionParams,
|
|
30
30
|
options?: RequestOptions,
|
|
31
31
|
): APIPromise<WithJob<GenerateCaptionResponse>> {
|
|
32
|
-
return enhanceJob(this._client, super.caption(params, options));
|
|
32
|
+
return enhanceJob(this._client, super.caption(params, options), options);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
override describeStyle(
|
|
36
36
|
params: GenerateDescribeStyleParams,
|
|
37
37
|
options?: RequestOptions,
|
|
38
38
|
): APIPromise<WithJob<GenerateDescribeStyleResponse>> {
|
|
39
|
-
return enhanceJob(this._client, super.describeStyle(params, options));
|
|
39
|
+
return enhanceJob(this._client, super.describeStyle(params, options), options);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
override detect(
|
|
43
43
|
params: GenerateDetectParams,
|
|
44
44
|
options?: RequestOptions,
|
|
45
45
|
): APIPromise<WithJob<GenerateDetectResponse>> {
|
|
46
|
-
return enhanceJob(this._client, super.detect(params, options));
|
|
46
|
+
return enhanceJob(this._client, super.detect(params, options), options);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
override embed(
|
|
50
50
|
params: GenerateEmbedParams,
|
|
51
51
|
options?: RequestOptions,
|
|
52
52
|
): APIPromise<WithJob<GenerateEmbedResponse>> {
|
|
53
|
-
return enhanceJob(this._client, super.embed(params, options));
|
|
53
|
+
return enhanceJob(this._client, super.embed(params, options), options);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
override patch(
|
|
57
57
|
params: GeneratePatchParams,
|
|
58
58
|
options?: RequestOptions,
|
|
59
59
|
): APIPromise<WithJob<GeneratePatchResponse>> {
|
|
60
|
-
return enhanceJob(this._client, super.patch(params, options));
|
|
60
|
+
return enhanceJob(this._client, super.patch(params, options), options);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
override runModel(
|
|
@@ -65,13 +65,13 @@ export class EnhancedGenerate extends Generate {
|
|
|
65
65
|
params: GenerateRunModelParams,
|
|
66
66
|
options?: RequestOptions,
|
|
67
67
|
): APIPromise<WithJob<GenerateRunModelResponse>> {
|
|
68
|
-
return enhanceJob(this._client, super.runModel(modelID, params, options));
|
|
68
|
+
return enhanceJob(this._client, super.runModel(modelID, params, options), options);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
override translate(
|
|
72
72
|
params: GenerateTranslateParams,
|
|
73
73
|
options?: RequestOptions,
|
|
74
74
|
): APIPromise<WithJob<GenerateTranslateResponse>> {
|
|
75
|
-
return enhanceJob(this._client, super.translate(params, options));
|
|
75
|
+
return enhanceJob(this._client, super.translate(params, options), options);
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/lib/job.ts
CHANGED
|
@@ -7,12 +7,19 @@ import {
|
|
|
7
7
|
type JobTriggerActionParams,
|
|
8
8
|
type JobTriggerActionResponse,
|
|
9
9
|
} from '../resources/jobs';
|
|
10
|
+
import { type Scope, clientProjectId, effectiveScope, withScope } from './scope';
|
|
10
11
|
|
|
11
12
|
export interface WaitOptions {
|
|
12
13
|
/** Polling interval in milliseconds. Default: 3000 */
|
|
13
14
|
intervalMs?: number;
|
|
14
15
|
/** Maximum wait time in milliseconds. Default: 120000 */
|
|
15
16
|
timeoutMs?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Override the project scope used for the polling requests. Defaults to
|
|
19
|
+
* the scope captured on the job (per-call override on the originating
|
|
20
|
+
* call, else the client's default project).
|
|
21
|
+
*/
|
|
22
|
+
projectId?: string;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
type JobStatus = JobRetrieveResponse.Job['status'];
|
|
@@ -22,6 +29,8 @@ const TERMINAL_STATUSES: Set<JobStatus> = new Set(['success', 'failure', 'cancel
|
|
|
22
29
|
/** Job methods added on top of the original job fields. */
|
|
23
30
|
export class JobMethods {
|
|
24
31
|
/** @internal */ declare readonly _client: Scenario;
|
|
32
|
+
/** @internal scope captured from the originating call — replayed on every `.wait()` poll. */
|
|
33
|
+
declare readonly _scope?: Scope;
|
|
25
34
|
|
|
26
35
|
/**
|
|
27
36
|
* Poll until the job reaches a terminal status (success, failure, or canceled).
|
|
@@ -36,13 +45,14 @@ export class JobMethods {
|
|
|
36
45
|
async wait(this: Job, options?: WaitOptions): Promise<Job> {
|
|
37
46
|
const intervalMs = options?.intervalMs ?? 3_000;
|
|
38
47
|
const timeoutMs = options?.timeoutMs ?? 120_000;
|
|
48
|
+
const scope = waitOverrideScope(options) ?? this._scope;
|
|
39
49
|
let elapsed = 0;
|
|
40
50
|
|
|
41
51
|
while (elapsed < timeoutMs) {
|
|
42
|
-
const response = await this._client.jobs.retrieve(this.jobId);
|
|
52
|
+
const response = await this._client.jobs.retrieve(this.jobId, withScope(scope));
|
|
43
53
|
|
|
44
54
|
if (TERMINAL_STATUSES.has(response.job.status)) {
|
|
45
|
-
return Job.from(this._client, response.job);
|
|
55
|
+
return Job.from(this._client, response.job, scope);
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
@@ -52,10 +62,11 @@ export class JobMethods {
|
|
|
52
62
|
throw new Error(`Job ${this.jobId} did not complete within ${timeoutMs / 1_000}s`);
|
|
53
63
|
}
|
|
54
64
|
|
|
55
|
-
/** @internal Create a Job from raw job data. */
|
|
56
|
-
static from(client: Scenario, data: JobRetrieveResponse['job']): Job {
|
|
65
|
+
/** @internal Create a Job from raw job data, optionally remembering the originating scope. */
|
|
66
|
+
static from(client: Scenario, data: JobRetrieveResponse['job'], scope?: Scope): Job {
|
|
57
67
|
const job = Object.assign(Object.create(JobMethods.prototype), data) as Job;
|
|
58
68
|
Object.defineProperty(job, '_client', { value: client, enumerable: false });
|
|
69
|
+
if (scope) Object.defineProperty(job, '_scope', { value: scope, enumerable: false });
|
|
59
70
|
return job;
|
|
60
71
|
}
|
|
61
72
|
}
|
|
@@ -72,14 +83,19 @@ export type WithJob<T extends { job: unknown }> = T & { job: JobMethods };
|
|
|
72
83
|
|
|
73
84
|
/**
|
|
74
85
|
* @internal Wrap `_thenUnwrap` to replace `response.job` with an enhanced Job.
|
|
86
|
+
* Captures the effective scope (per-call override or client default) so
|
|
87
|
+
* `.wait()` can replay it on every poll — keeping follow-up calls in sync
|
|
88
|
+
* with the project the job was originally created in.
|
|
75
89
|
*/
|
|
76
90
|
export function enhanceJob<T extends { job: { jobId: string; status: string } }>(
|
|
77
91
|
client: Scenario,
|
|
78
92
|
promise: APIPromise<T>,
|
|
93
|
+
options?: RequestOptions,
|
|
79
94
|
): APIPromise<WithJob<T>> {
|
|
95
|
+
const scope = effectiveScope(options, clientProjectId(client));
|
|
80
96
|
return promise._thenUnwrap((data) => ({
|
|
81
97
|
...data,
|
|
82
|
-
job: Job.from(client, data.job as JobRetrieveResponse['job']),
|
|
98
|
+
job: Job.from(client, data.job as JobRetrieveResponse['job'], scope),
|
|
83
99
|
}));
|
|
84
100
|
}
|
|
85
101
|
|
|
@@ -92,7 +108,7 @@ export function enhanceJob<T extends { job: { jobId: string; status: string } }>
|
|
|
92
108
|
*/
|
|
93
109
|
export class EnhancedJobs extends Jobs {
|
|
94
110
|
override retrieve(jobID: string, options?: RequestOptions): APIPromise<WithJob<JobRetrieveResponse>> {
|
|
95
|
-
return enhanceJob(this._client, super.retrieve(jobID, options));
|
|
111
|
+
return enhanceJob(this._client, super.retrieve(jobID, options), options);
|
|
96
112
|
}
|
|
97
113
|
|
|
98
114
|
override triggerAction(
|
|
@@ -100,6 +116,12 @@ export class EnhancedJobs extends Jobs {
|
|
|
100
116
|
params: JobTriggerActionParams,
|
|
101
117
|
options?: RequestOptions,
|
|
102
118
|
): APIPromise<WithJob<JobTriggerActionResponse>> {
|
|
103
|
-
return enhanceJob(this._client, super.triggerAction(jobID, params, options));
|
|
119
|
+
return enhanceJob(this._client, super.triggerAction(jobID, params, options), options);
|
|
104
120
|
}
|
|
105
121
|
}
|
|
122
|
+
|
|
123
|
+
/** Resolve the scope override carried by a `WaitOptions`, if any. */
|
|
124
|
+
function waitOverrideScope(options?: WaitOptions): Scope | undefined {
|
|
125
|
+
if (!options?.projectId) return undefined;
|
|
126
|
+
return { projectId: options.projectId };
|
|
127
|
+
}
|
package/src/lib/models.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { GenerateRunModelParams, GenerateRunModelResponse } from '../resour
|
|
|
4
4
|
import { Models, type ModelRetrieveParams, type ModelRetrieveResponse } from '../resources/models/models';
|
|
5
5
|
import { Train, type TrainTriggerParams, type TrainTriggerResponse } from '../resources/models/train';
|
|
6
6
|
import { type WithJob, enhanceJob } from './job';
|
|
7
|
+
import { type Scope, effectiveScope, withScope } from './scope';
|
|
7
8
|
// Type-only import to break the circular `scenario.ts` ↔ `models.ts` graph at runtime.
|
|
8
9
|
import type { Scenario } from './scenario';
|
|
9
10
|
|
|
@@ -20,28 +21,35 @@ import type { Scenario } from './scenario';
|
|
|
20
21
|
*/
|
|
21
22
|
export class ModelEntity {
|
|
22
23
|
/** @internal */ declare readonly _client: Scenario;
|
|
24
|
+
/** @internal scope captured from the retrieve call — replayed when `.run()` is invoked without an explicit query override. */
|
|
25
|
+
declare readonly _scope?: Scope;
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* Generate with this model. Shortcut for
|
|
26
29
|
* `client.generate.runModel(this.id, params)`.
|
|
27
30
|
*
|
|
28
31
|
* Returns an enhanced response where `response.job` has `.wait()`.
|
|
32
|
+
* If the model was fetched with a `projectId` scope (per-call override or
|
|
33
|
+
* client default), that scope is reused unless overridden by
|
|
34
|
+
* `options.query`.
|
|
29
35
|
*/
|
|
30
36
|
run(
|
|
31
37
|
this: ModelEntity & ModelRetrieveResponse.Model,
|
|
32
38
|
params: GenerateRunModelParams,
|
|
33
39
|
options?: RequestOptions,
|
|
34
40
|
): APIPromise<WithJob<GenerateRunModelResponse>> {
|
|
35
|
-
return this._client.generate.runModel(this.id, params, options);
|
|
41
|
+
return this._client.generate.runModel(this.id, params, withScope(this._scope, options));
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
/** @internal Create a ModelEntity wrapping raw model data. */
|
|
44
|
+
/** @internal Create a ModelEntity wrapping raw model data, optionally remembering the originating scope. */
|
|
39
45
|
static from(
|
|
40
46
|
client: Scenario,
|
|
41
47
|
data: ModelRetrieveResponse.Model,
|
|
48
|
+
scope?: Scope,
|
|
42
49
|
): ModelRetrieveResponse.Model & ModelEntity {
|
|
43
50
|
const entity = Object.assign(Object.create(ModelEntity.prototype), data);
|
|
44
51
|
Object.defineProperty(entity, '_client', { value: client, enumerable: false });
|
|
52
|
+
if (scope) Object.defineProperty(entity, '_scope', { value: scope, enumerable: false });
|
|
45
53
|
return entity;
|
|
46
54
|
}
|
|
47
55
|
}
|
|
@@ -61,7 +69,7 @@ export class EnhancedTrain extends Train {
|
|
|
61
69
|
params: TrainTriggerParams,
|
|
62
70
|
options?: RequestOptions,
|
|
63
71
|
): APIPromise<WithJob<TrainTriggerResponse>> {
|
|
64
|
-
return enhanceJob(this._client, super.trigger(modelID, params, options));
|
|
72
|
+
return enhanceJob(this._client, super.trigger(modelID, params, options), options);
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
75
|
|
|
@@ -79,9 +87,11 @@ export class EnhancedModels extends Models {
|
|
|
79
87
|
query: ModelRetrieveParams | null | undefined = {},
|
|
80
88
|
options?: RequestOptions,
|
|
81
89
|
): APIPromise<EnhancedModelRetrieveResponse> {
|
|
90
|
+
const client = this._client as Scenario;
|
|
91
|
+
const scope = effectiveScope(options, client.projectId);
|
|
82
92
|
return super.retrieve(modelID, query, options)._thenUnwrap((data) => ({
|
|
83
93
|
...data,
|
|
84
|
-
model: ModelEntity.from(
|
|
94
|
+
model: ModelEntity.from(client, data.model, scope),
|
|
85
95
|
}));
|
|
86
96
|
}
|
|
87
97
|
}
|
package/src/lib/scenario.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Scenario as BaseScenario } from '../client';
|
|
1
|
+
import { Scenario as BaseScenario, type ClientOptions as BaseClientOptions } from '../client';
|
|
2
2
|
import { EnhancedAssets } from './asset';
|
|
3
3
|
import { EnhancedGenerate } from './generate';
|
|
4
4
|
import { EnhancedJobs } from './job';
|
|
@@ -6,6 +6,23 @@ import { EnhancedModels } from './models';
|
|
|
6
6
|
import { EnhancedUploads } from './upload';
|
|
7
7
|
import { EnhancedWorkflows } from './workflow';
|
|
8
8
|
|
|
9
|
+
export interface ClientOptions extends BaseClientOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Default project to scope every request to (sent as `?projectId=...`).
|
|
12
|
+
*
|
|
13
|
+
* Useful with `bearerAuth` when the user belongs to multiple projects —
|
|
14
|
+
* the server derives the team from the project, so `teamId` doesn't need
|
|
15
|
+
* to be configured at construction time. With `apiKey` auth, the project
|
|
16
|
+
* is already implied by the key and this option is unnecessary.
|
|
17
|
+
*
|
|
18
|
+
* Per-call `options.query.projectId` overrides this default for a single
|
|
19
|
+
* request. SDK helpers (`.wait()`, `model.run()`, ...) reuse the scope
|
|
20
|
+
* effective on the originating call, so an action created in project A
|
|
21
|
+
* keeps polling in project A even if the client default changes later.
|
|
22
|
+
*/
|
|
23
|
+
projectId?: string | null | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
/**
|
|
10
27
|
* Enhanced Scenario client.
|
|
11
28
|
*
|
|
@@ -17,23 +34,19 @@ import { EnhancedWorkflows } from './workflow';
|
|
|
17
34
|
* ```ts
|
|
18
35
|
* import Scenario from '@scenario-labs/sdk';
|
|
19
36
|
*
|
|
20
|
-
*
|
|
37
|
+
* // With API key — project is implied by the key.
|
|
38
|
+
* const client = new Scenario({ apiKey: '...', apiSecret: '...' });
|
|
39
|
+
*
|
|
40
|
+
* // With bearer JWT — pin a default project for all requests.
|
|
41
|
+
* const client = new Scenario({ bearerAuth: jwt, projectId: 'proj_abc' });
|
|
21
42
|
*
|
|
22
|
-
* //
|
|
23
|
-
*
|
|
43
|
+
* // Per-call override (one request only):
|
|
44
|
+
* await client.uploads.uploadFile(params, { query: { projectId: 'proj_other' } });
|
|
24
45
|
*
|
|
25
|
-
* // Enhanced: response.job.wait()
|
|
46
|
+
* // Enhanced: response.job.wait() — auto-reuses the scope of the call that produced the job
|
|
26
47
|
* const run = await client.workflows.run(workflowId, { body });
|
|
27
48
|
* const completed = await run.job.wait();
|
|
28
49
|
*
|
|
29
|
-
* // Enhanced: response.workflow.findNode()
|
|
30
|
-
* const wf = await client.workflows.retrieve(workflowId);
|
|
31
|
-
* wf.workflow.findNode('final_image');
|
|
32
|
-
*
|
|
33
|
-
* // Enhanced: generate job.wait()
|
|
34
|
-
* const gen = await client.generate.runModel(modelId, { body });
|
|
35
|
-
* const done = await gen.job.wait();
|
|
36
|
-
*
|
|
37
50
|
* // Enhanced: upload.wait()
|
|
38
51
|
* const res = await client.uploads.retrieve(uploadId);
|
|
39
52
|
* const imported = await res.upload.wait();
|
|
@@ -50,4 +63,29 @@ export class Scenario extends BaseScenario {
|
|
|
50
63
|
override uploads: EnhancedUploads = new EnhancedUploads(this);
|
|
51
64
|
override assets: EnhancedAssets = new EnhancedAssets(this);
|
|
52
65
|
override jobs: EnhancedJobs = new EnhancedJobs(this);
|
|
66
|
+
|
|
67
|
+
/** The default project this client scopes every request to, or `null` if unset. */
|
|
68
|
+
readonly projectId: string | null;
|
|
69
|
+
|
|
70
|
+
constructor(options: ClientOptions = {}) {
|
|
71
|
+
const { projectId, defaultQuery, ...rest } = options;
|
|
72
|
+
// Strip any stale projectId from defaultQuery so the typed field is the
|
|
73
|
+
// single source of truth (matters when `withOptions` reconstructs the client).
|
|
74
|
+
const cleanedDefaultQuery = stripKey(defaultQuery, 'projectId');
|
|
75
|
+
super({
|
|
76
|
+
...rest,
|
|
77
|
+
// Pass projectId through so it lands in `_options` and survives a
|
|
78
|
+
// `withOptions()` round-trip. The base ClientOptions doesn't declare
|
|
79
|
+
// this field, hence the cast.
|
|
80
|
+
projectId,
|
|
81
|
+
defaultQuery: projectId ? { ...cleanedDefaultQuery, projectId } : cleanedDefaultQuery,
|
|
82
|
+
} as BaseClientOptions);
|
|
83
|
+
this.projectId = projectId ?? null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function stripKey<T extends object>(obj: T | undefined, key: string): Partial<T> | undefined {
|
|
88
|
+
if (!obj || !(key in obj)) return obj;
|
|
89
|
+
const { [key]: _omit, ...rest } = obj as Record<string, unknown>;
|
|
90
|
+
return rest as Partial<T>;
|
|
53
91
|
}
|
package/src/lib/scope.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { RequestOptions } from '../internal/request-options';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-request project scope.
|
|
5
|
+
*
|
|
6
|
+
* With API-key auth, the server resolves the project from the key. With
|
|
7
|
+
* bearer (JWT) auth a user can belong to multiple projects, so callers must
|
|
8
|
+
* specify which one to act on by passing `projectId` as a query param
|
|
9
|
+
* (the server derives `teamId` from it — no need to send it explicitly).
|
|
10
|
+
*
|
|
11
|
+
* SDK helpers like `Job.wait()` and `Upload.wait()` make follow-up calls
|
|
12
|
+
* internally — they capture the scope effective on the originating call and
|
|
13
|
+
* forward it on each retry, so an action created in project A always polls
|
|
14
|
+
* in project A even if the client default changes between calls.
|
|
15
|
+
*/
|
|
16
|
+
export interface Scope {
|
|
17
|
+
projectId?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Extract `projectId` from a `RequestOptions.query`. Returns `undefined` if absent. */
|
|
21
|
+
export function extractScope(options?: RequestOptions): Scope | undefined {
|
|
22
|
+
const query = options?.query as Record<string, unknown> | undefined;
|
|
23
|
+
if (!query) return undefined;
|
|
24
|
+
const projectId = typeof query['projectId'] === 'string' ? query['projectId'] : undefined;
|
|
25
|
+
if (!projectId) return undefined;
|
|
26
|
+
return { projectId };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the effective scope for an outgoing call:
|
|
31
|
+
* 1. If the per-call options carry a `projectId` query — use that (the explicit override wins).
|
|
32
|
+
* 2. Otherwise, fall back to the client's default `projectId`.
|
|
33
|
+
* 3. If neither is set (e.g. API-key auth with implicit scope), return `undefined`.
|
|
34
|
+
*
|
|
35
|
+
* Returns the snapshot we want to *capture on an enhanced entity* so that
|
|
36
|
+
* follow-up helper calls (`.wait()`, `entity.run()`, ...) reuse the same
|
|
37
|
+
* scope and stay in sync with the call that produced them.
|
|
38
|
+
*/
|
|
39
|
+
export function effectiveScope(
|
|
40
|
+
options: RequestOptions | undefined,
|
|
41
|
+
fallbackProjectId: string | null | undefined,
|
|
42
|
+
): Scope | undefined {
|
|
43
|
+
const override = extractScope(options);
|
|
44
|
+
if (override) return override;
|
|
45
|
+
if (fallbackProjectId) return { projectId: fallbackProjectId };
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Read the client's default `projectId` without repeating the structural
|
|
51
|
+
* cast at every call site. The base generated `Scenario` type doesn't
|
|
52
|
+
* declare `projectId` — only the enhanced one in `./scenario` does — so
|
|
53
|
+
* `lib/` modules that import `Scenario` from `'../client'` use this helper
|
|
54
|
+
* instead of duplicating the same cast inline.
|
|
55
|
+
*/
|
|
56
|
+
export function clientProjectId(client: unknown): string | null | undefined {
|
|
57
|
+
return (client as { projectId?: string | null }).projectId;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Build a `RequestOptions` that adds the scope as a query param, merging into any existing query. */
|
|
61
|
+
export function withScope(scope: Scope | undefined, options?: RequestOptions): RequestOptions | undefined {
|
|
62
|
+
if (!scope?.projectId) return options;
|
|
63
|
+
const existingQuery = (options?.query as Record<string, unknown> | undefined) ?? {};
|
|
64
|
+
return {
|
|
65
|
+
...options,
|
|
66
|
+
query: { ...existingQuery, projectId: scope.projectId },
|
|
67
|
+
};
|
|
68
|
+
}
|
package/src/lib/upload.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '../resources/uploads';
|
|
12
12
|
import type { AssetRetrieveResponse } from '../resources/assets/assets';
|
|
13
13
|
import type { ModelRetrieveResponse } from '../resources/models/models';
|
|
14
|
+
import { type Scope, clientProjectId, effectiveScope, withScope } from './scope';
|
|
14
15
|
|
|
15
16
|
type UploadKind = NonNullable<UploadCreateParams['kind']>;
|
|
16
17
|
|
|
@@ -81,11 +82,19 @@ export interface UploadWaitOptions {
|
|
|
81
82
|
intervalMs?: number;
|
|
82
83
|
/** Maximum wait time in milliseconds. Default: 300000 */
|
|
83
84
|
timeoutMs?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Override the project scope used for the polling requests. Defaults to
|
|
87
|
+
* the scope captured on the upload (per-call override on the originating
|
|
88
|
+
* call, else the client's default project).
|
|
89
|
+
*/
|
|
90
|
+
projectId?: string;
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
/** Upload methods added on top of the original upload fields. */
|
|
87
94
|
export class UploadMethods {
|
|
88
95
|
/** @internal */ declare readonly _client: Scenario;
|
|
96
|
+
/** @internal scope captured from the originating call — replayed on every `.wait()` poll. */
|
|
97
|
+
declare readonly _scope?: Scope;
|
|
89
98
|
|
|
90
99
|
/**
|
|
91
100
|
* Poll until the upload has been processed into an entity (asset or model),
|
|
@@ -102,18 +111,20 @@ export class UploadMethods {
|
|
|
102
111
|
* ```
|
|
103
112
|
*/
|
|
104
113
|
async wait(this: Upload, options?: UploadWaitOptions): Promise<Upload> {
|
|
114
|
+
const scope = waitOverrideScope(options) ?? this._scope;
|
|
105
115
|
const final = await pollUntilImported(
|
|
106
|
-
() => this._client.uploads.retrieve(this.id),
|
|
116
|
+
() => this._client.uploads.retrieve(this.id, withScope(scope)),
|
|
107
117
|
options?.intervalMs ?? 2_000,
|
|
108
118
|
options?.timeoutMs ?? 300_000,
|
|
109
119
|
);
|
|
110
|
-
return UploadMethods.from(this._client, final.upload);
|
|
120
|
+
return UploadMethods.from(this._client, final.upload, scope);
|
|
111
121
|
}
|
|
112
122
|
|
|
113
|
-
/** @internal Create an Upload from raw upload data. */
|
|
114
|
-
static from(client: Scenario, data: UploadRetrieveResponse['upload']): Upload {
|
|
123
|
+
/** @internal Create an Upload from raw upload data, optionally remembering the originating scope. */
|
|
124
|
+
static from(client: Scenario, data: UploadRetrieveResponse['upload'], scope?: Scope): Upload {
|
|
115
125
|
const upload = Object.assign(Object.create(UploadMethods.prototype), data) as Upload;
|
|
116
126
|
Object.defineProperty(upload, '_client', { value: client, enumerable: false });
|
|
127
|
+
if (scope) Object.defineProperty(upload, '_scope', { value: scope, enumerable: false });
|
|
117
128
|
return upload;
|
|
118
129
|
}
|
|
119
130
|
}
|
|
@@ -130,14 +141,19 @@ export type WithUpload<T extends { upload: unknown }> = T & { upload: UploadMeth
|
|
|
130
141
|
|
|
131
142
|
/**
|
|
132
143
|
* @internal Wrap `_thenUnwrap` to replace `response.upload` with an enhanced Upload.
|
|
144
|
+
* Captures the effective scope (per-call override or client default) so
|
|
145
|
+
* `.wait()` can replay it on every poll — keeping follow-up calls in sync
|
|
146
|
+
* with the project the upload was originally created in.
|
|
133
147
|
*/
|
|
134
148
|
export function enhanceUpload<T extends { upload: { id: string; status: string } }>(
|
|
135
149
|
client: Scenario,
|
|
136
150
|
promise: APIPromise<T>,
|
|
151
|
+
options?: RequestOptions,
|
|
137
152
|
): APIPromise<WithUpload<T>> {
|
|
153
|
+
const scope = effectiveScope(options, clientProjectId(client));
|
|
138
154
|
return promise._thenUnwrap((data) => ({
|
|
139
155
|
...data,
|
|
140
|
-
upload: Upload.from(client, data.upload as UploadRetrieveResponse['upload']),
|
|
156
|
+
upload: Upload.from(client, data.upload as UploadRetrieveResponse['upload'], scope),
|
|
141
157
|
}));
|
|
142
158
|
}
|
|
143
159
|
|
|
@@ -153,14 +169,14 @@ export class EnhancedUploads extends Uploads {
|
|
|
153
169
|
body: UploadCreateParams,
|
|
154
170
|
options?: RequestOptions,
|
|
155
171
|
): APIPromise<WithUpload<UploadCreateResponse>> {
|
|
156
|
-
return enhanceUpload(this._client, super.create(body, options));
|
|
172
|
+
return enhanceUpload(this._client, super.create(body, options), options);
|
|
157
173
|
}
|
|
158
174
|
|
|
159
175
|
override retrieve(
|
|
160
176
|
uploadID: string,
|
|
161
177
|
options?: RequestOptions,
|
|
162
178
|
): APIPromise<WithUpload<UploadRetrieveResponse>> {
|
|
163
|
-
return enhanceUpload(this._client, super.retrieve(uploadID, options));
|
|
179
|
+
return enhanceUpload(this._client, super.retrieve(uploadID, options), options);
|
|
164
180
|
}
|
|
165
181
|
|
|
166
182
|
override triggerAction(
|
|
@@ -168,7 +184,7 @@ export class EnhancedUploads extends Uploads {
|
|
|
168
184
|
body: UploadTriggerActionParams,
|
|
169
185
|
options?: RequestOptions,
|
|
170
186
|
): APIPromise<WithUpload<UploadTriggerActionResponse>> {
|
|
171
|
-
return enhanceUpload(this._client, super.triggerAction(uploadID, body, options));
|
|
187
|
+
return enhanceUpload(this._client, super.triggerAction(uploadID, body, options), options);
|
|
172
188
|
}
|
|
173
189
|
|
|
174
190
|
/**
|
|
@@ -266,16 +282,25 @@ export class EnhancedUploads extends Uploads {
|
|
|
266
282
|
throw new Error(`Upload ${uploadId} reached status "${final.upload.status}" but has no entityId`);
|
|
267
283
|
}
|
|
268
284
|
|
|
269
|
-
// Fetch and return the resolved entity (narrowed by kind).
|
|
285
|
+
// Fetch and return the resolved entity (narrowed by kind). Forward the
|
|
286
|
+
// original options so any per-call `projectId` query is passed through
|
|
287
|
+
// to the final retrieve (necessary under bearer auth with per-call
|
|
288
|
+
// scope overrides).
|
|
270
289
|
if (params.kind === 'model') {
|
|
271
|
-
const model = await this._client.models.retrieve(entityId);
|
|
290
|
+
const model = await this._client.models.retrieve(entityId, undefined, options);
|
|
272
291
|
return model as UploadResult<K>;
|
|
273
292
|
}
|
|
274
|
-
const asset = await this._client.assets.retrieve(entityId);
|
|
293
|
+
const asset = await this._client.assets.retrieve(entityId, undefined, options);
|
|
275
294
|
return asset as UploadResult<K>;
|
|
276
295
|
}
|
|
277
296
|
}
|
|
278
297
|
|
|
298
|
+
/** Resolve the scope override carried by an `UploadWaitOptions`, if any. */
|
|
299
|
+
function waitOverrideScope(options?: UploadWaitOptions): Scope | undefined {
|
|
300
|
+
if (!options?.projectId) return undefined;
|
|
301
|
+
return { projectId: options.projectId };
|
|
302
|
+
}
|
|
303
|
+
|
|
279
304
|
// ─────────────────────── helpers ───────────────────────
|
|
280
305
|
|
|
281
306
|
async function normalizeToBytes(input: UploadFileInput): Promise<Uint8Array> {
|
package/src/lib/workflow.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type WorkflowRunResponse,
|
|
10
10
|
} from '../resources/workflows';
|
|
11
11
|
import { type WithJob, enhanceJob } from './job';
|
|
12
|
+
import { type Scope, effectiveScope, withScope } from './scope';
|
|
12
13
|
import { validateWorkflowFlow } from './workflow_validator';
|
|
13
14
|
// Type-only import to break the circular `scenario.ts` ↔ `workflow.ts` graph at runtime.
|
|
14
15
|
import type { Scenario } from './scenario';
|
|
@@ -32,6 +33,8 @@ type NodeType = DefinitionFlow['type'];
|
|
|
32
33
|
*/
|
|
33
34
|
export class WorkflowEntity {
|
|
34
35
|
/** @internal */ declare readonly _client: Scenario;
|
|
36
|
+
/** @internal scope captured from the retrieve call — replayed when `.run()` is invoked without an explicit query override. */
|
|
37
|
+
declare readonly _scope?: Scope;
|
|
35
38
|
|
|
36
39
|
/** Find a node in the workflow definition by ID. */
|
|
37
40
|
findNode(
|
|
@@ -59,22 +62,27 @@ export class WorkflowEntity {
|
|
|
59
62
|
* `client.workflows.run(this.id, params)`.
|
|
60
63
|
*
|
|
61
64
|
* Returns an enhanced response where `response.job` has `.wait()`.
|
|
65
|
+
* If the workflow was fetched with a `projectId` scope (per-call override
|
|
66
|
+
* or client default), that scope is reused unless overridden by
|
|
67
|
+
* `options.query`.
|
|
62
68
|
*/
|
|
63
69
|
run(
|
|
64
70
|
this: WorkflowEntity & WorkflowRetrieveResponse.Workflow,
|
|
65
71
|
params: WorkflowRunParams,
|
|
66
72
|
options?: RequestOptions,
|
|
67
73
|
): APIPromise<WithJob<WorkflowRunResponse>> {
|
|
68
|
-
return this._client.workflows.run(this.id, params, options);
|
|
74
|
+
return this._client.workflows.run(this.id, params, withScope(this._scope, options));
|
|
69
75
|
}
|
|
70
76
|
|
|
71
|
-
/** @internal Create a WorkflowEntity from raw workflow data. */
|
|
77
|
+
/** @internal Create a WorkflowEntity from raw workflow data, optionally remembering the originating scope. */
|
|
72
78
|
static from(
|
|
73
79
|
client: Scenario,
|
|
74
80
|
data: WorkflowRetrieveResponse.Workflow,
|
|
81
|
+
scope?: Scope,
|
|
75
82
|
): WorkflowRetrieveResponse.Workflow & WorkflowEntity {
|
|
76
83
|
const entity = Object.assign(Object.create(WorkflowEntity.prototype), data);
|
|
77
84
|
Object.defineProperty(entity, '_client', { value: client, enumerable: false });
|
|
85
|
+
if (scope) Object.defineProperty(entity, '_scope', { value: scope, enumerable: false });
|
|
78
86
|
return entity;
|
|
79
87
|
}
|
|
80
88
|
}
|
|
@@ -98,9 +106,11 @@ export class EnhancedWorkflows extends Workflows {
|
|
|
98
106
|
workflowID: string,
|
|
99
107
|
options?: RequestOptions,
|
|
100
108
|
): APIPromise<EnhancedWorkflowRetrieveResponse> {
|
|
109
|
+
const client = this._client as Scenario;
|
|
110
|
+
const scope = effectiveScope(options, client.projectId);
|
|
101
111
|
return super.retrieve(workflowID, options)._thenUnwrap((data) => ({
|
|
102
112
|
...data,
|
|
103
|
-
workflow: WorkflowEntity.from(
|
|
113
|
+
workflow: WorkflowEntity.from(client, data.workflow, scope),
|
|
104
114
|
}));
|
|
105
115
|
}
|
|
106
116
|
|
|
@@ -110,7 +120,7 @@ export class EnhancedWorkflows extends Workflows {
|
|
|
110
120
|
params: WorkflowRunParams,
|
|
111
121
|
options?: RequestOptions,
|
|
112
122
|
): APIPromise<WithJob<WorkflowRunResponse>> {
|
|
113
|
-
return enhanceJob(this._client, super.run(workflowID, params, options));
|
|
123
|
+
return enhanceJob(this._client, super.run(workflowID, params, options), options);
|
|
114
124
|
}
|
|
115
125
|
|
|
116
126
|
/** Approve a user approval node. Returns an enhanced response where `job` has `.wait()`. */
|
|
@@ -119,6 +129,6 @@ export class EnhancedWorkflows extends Workflows {
|
|
|
119
129
|
body: WorkflowApproveUserParams,
|
|
120
130
|
options?: RequestOptions,
|
|
121
131
|
): APIPromise<WithJob<WorkflowUserApprovalResponse>> {
|
|
122
|
-
return enhanceJob(this._client, super.userApproval(workflowID, body, options));
|
|
132
|
+
return enhanceJob(this._client, super.userApproval(workflowID, body, options), options);
|
|
123
133
|
}
|
|
124
134
|
}
|