@to11ai/sdk 0.0.2 → 0.1.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/LICENSE +373 -0
- package/README.md +91 -1
- package/dist/client.cjs +20 -5
- package/dist/client.d.ts +10 -1
- package/dist/client.js +26 -2
- package/dist/errors.cjs +89 -1
- package/dist/errors.d.ts +96 -0
- package/dist/errors.js +89 -1
- package/dist/gateway.cjs +8 -1
- package/dist/gateway.d.ts +19 -0
- package/dist/gateway.js +8 -1
- package/dist/projectEnvironments/resource.cjs +110 -0
- package/dist/projectEnvironments/resource.d.ts +67 -0
- package/dist/projectEnvironments/resource.js +70 -0
- package/dist/projectEnvironments/types.d.ts +121 -0
- package/dist/prompts/resource.cjs +81 -0
- package/dist/prompts/resource.d.ts +61 -2
- package/dist/prompts/resource.js +81 -0
- package/dist/prompts/types.d.ts +217 -3
- package/dist/routingRules/resource.cjs +131 -0
- package/dist/routingRules/resource.d.ts +22 -0
- package/dist/routingRules/resource.js +91 -0
- package/dist/routingRules/types.d.ts +169 -0
- package/dist/traceparent.d.ts +61 -0
- package/dist/transport.d.ts +6 -1
- package/package.json +25 -5
- package/src/client.ts +60 -1
- package/src/errors.ts +172 -0
- package/src/gateway.ts +29 -1
- package/src/projectEnvironments/resource.ts +220 -0
- package/src/projectEnvironments/types.ts +151 -0
- package/src/prompts/resource.ts +232 -0
- package/src/prompts/types.ts +270 -3
- package/src/routingRules/resource.ts +180 -0
- package/src/routingRules/types.ts +222 -0
- package/src/traceparent.ts +124 -0
- package/src/transport.ts +191 -5
package/src/errors.ts
CHANGED
|
@@ -89,3 +89,175 @@ export class To11aiNetworkError extends To11aiError {
|
|
|
89
89
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Thrown when strict-mode rejects a label move for an unregistered label.
|
|
95
|
+
*
|
|
96
|
+
* The API returns 422 with `error.code === "PROMPT_LABEL_NOT_REGISTERED"`
|
|
97
|
+
* when `project.enforce_label_registry = true` and the caller tries to
|
|
98
|
+
* move a label that has no active entry in the project label registry.
|
|
99
|
+
* The transport surfaces these as this typed subclass; existing
|
|
100
|
+
* `instanceof To11aiApiError` blocks still match.
|
|
101
|
+
*
|
|
102
|
+
* Note: this extends `To11aiApiError` directly (not `To11aiValidationError`,
|
|
103
|
+
* which hardcodes status 400). The API returns 422 for this case.
|
|
104
|
+
*/
|
|
105
|
+
export class PromptLabelNotRegisteredError extends To11aiApiError {
|
|
106
|
+
readonly projectId: string;
|
|
107
|
+
readonly label: string;
|
|
108
|
+
|
|
109
|
+
constructor(
|
|
110
|
+
message: string,
|
|
111
|
+
projectId: string,
|
|
112
|
+
label: string,
|
|
113
|
+
details?: unknown,
|
|
114
|
+
) {
|
|
115
|
+
super(
|
|
116
|
+
message,
|
|
117
|
+
422,
|
|
118
|
+
"PROMPT_LABEL_NOT_REGISTERED",
|
|
119
|
+
details !== undefined ? details : { projectId, label },
|
|
120
|
+
);
|
|
121
|
+
this.name = "PromptLabelNotRegisteredError";
|
|
122
|
+
this.projectId = projectId;
|
|
123
|
+
this.label = label;
|
|
124
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Type guard helper — checks `instanceof` (not parent + errorCode string). */
|
|
128
|
+
static is(err: unknown): err is PromptLabelNotRegisteredError {
|
|
129
|
+
return err instanceof PromptLabelNotRegisteredError;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Thrown when a rule cannot be located. Covers two variants of the same
|
|
135
|
+
* named class (single class so existing `catch (err: RuleNotFoundError)`
|
|
136
|
+
* blocks match both):
|
|
137
|
+
*
|
|
138
|
+
* * Control-plane variant (404 `RULE_NOT_FOUND`) — `ruleId` is the id
|
|
139
|
+
* the caller supplied and that did not exist in the project.
|
|
140
|
+
* * Gateway variant (404 `RULE_NOT_FOUND_IN_ENV`) — `model` is the
|
|
141
|
+
* `function::<alias>` (or raw model id) the gateway tried to resolve
|
|
142
|
+
* and `env` is the request env. `ruleId` is undefined because the
|
|
143
|
+
* alias may not resolve to any rule in the env.
|
|
144
|
+
*/
|
|
145
|
+
export class RuleNotFoundError extends To11aiApiError {
|
|
146
|
+
readonly ruleId: string | undefined;
|
|
147
|
+
readonly model: string | undefined;
|
|
148
|
+
readonly env: string | undefined;
|
|
149
|
+
|
|
150
|
+
constructor(
|
|
151
|
+
message: string,
|
|
152
|
+
errorCode: string,
|
|
153
|
+
fields: { ruleId?: string; model?: string; env?: string },
|
|
154
|
+
details?: unknown,
|
|
155
|
+
) {
|
|
156
|
+
super(message, 404, errorCode, details);
|
|
157
|
+
this.name = "RuleNotFoundError";
|
|
158
|
+
this.ruleId = fields.ruleId;
|
|
159
|
+
this.model = fields.model;
|
|
160
|
+
this.env = fields.env;
|
|
161
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
static is(err: unknown): err is RuleNotFoundError {
|
|
165
|
+
return err instanceof RuleNotFoundError;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export type NoApplicableRouteReason =
|
|
170
|
+
| "all_conditions_false"
|
|
171
|
+
| "all_variants_unreachable"
|
|
172
|
+
| "all_steps_non_routable";
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Thrown when the gateway resolved a rule + version but no variant /
|
|
176
|
+
* step / chain element was applicable to the request — every condition
|
|
177
|
+
* was false, every variant unreachable, or every fallback step
|
|
178
|
+
* non-routable. Surfaces as 422 `NO_APPLICABLE_ROUTE`.
|
|
179
|
+
*/
|
|
180
|
+
export class NoApplicableRouteError extends To11aiApiError {
|
|
181
|
+
readonly ruleId: string;
|
|
182
|
+
readonly versionId: string;
|
|
183
|
+
readonly reason: NoApplicableRouteReason | string;
|
|
184
|
+
|
|
185
|
+
constructor(
|
|
186
|
+
message: string,
|
|
187
|
+
fields: { ruleId: string; versionId: string; reason: string },
|
|
188
|
+
details?: unknown,
|
|
189
|
+
) {
|
|
190
|
+
super(message, 422, "NO_APPLICABLE_ROUTE", details);
|
|
191
|
+
this.name = "NoApplicableRouteError";
|
|
192
|
+
this.ruleId = fields.ruleId;
|
|
193
|
+
this.versionId = fields.versionId;
|
|
194
|
+
this.reason = fields.reason;
|
|
195
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
static is(err: unknown): err is NoApplicableRouteError {
|
|
199
|
+
return err instanceof NoApplicableRouteError;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Thrown when a rule references a provider that has no activation in the
|
|
205
|
+
* requested env. Surfaces as 422 `ENV_NOT_ENABLED_FOR_PROVIDER`.
|
|
206
|
+
*/
|
|
207
|
+
export class EnvNotEnabledForProviderError extends To11aiApiError {
|
|
208
|
+
readonly ruleId: string;
|
|
209
|
+
readonly envSlug: string;
|
|
210
|
+
readonly providerId: string;
|
|
211
|
+
|
|
212
|
+
constructor(
|
|
213
|
+
message: string,
|
|
214
|
+
fields: { ruleId: string; envSlug: string; providerId: string },
|
|
215
|
+
details?: unknown,
|
|
216
|
+
) {
|
|
217
|
+
super(message, 422, "ENV_NOT_ENABLED_FOR_PROVIDER", details);
|
|
218
|
+
this.name = "EnvNotEnabledForProviderError";
|
|
219
|
+
this.ruleId = fields.ruleId;
|
|
220
|
+
this.envSlug = fields.envSlug;
|
|
221
|
+
this.providerId = fields.providerId;
|
|
222
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static is(err: unknown): err is EnvNotEnabledForProviderError {
|
|
226
|
+
return err instanceof EnvNotEnabledForProviderError;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Thrown when version create rejects the snapshot for a structural
|
|
232
|
+
* reason — invalid variant params, malformed `when` clause, illegal
|
|
233
|
+
* weight set, malformed retry policy, … Surfaces with one of the
|
|
234
|
+
* `INVALID_VARIANT_PARAMS` / `INVALID_VARIANT_WEIGHTS` /
|
|
235
|
+
* `INVALID_RULE_STRATEGY` / `INVALID_STEP_COUNT` /
|
|
236
|
+
* `RULE_ENDPOINT_MISMATCH` / `INVALID_WHEN_CLAUSE` /
|
|
237
|
+
* `INVALID_STRATEGY_VARIANTS` / `INVALID_RETRY_POLICY` error codes
|
|
238
|
+
* at HTTP status 400 or 422.
|
|
239
|
+
*/
|
|
240
|
+
export class RuleVersionInvalidSnapshotError extends To11aiApiError {
|
|
241
|
+
readonly field: string | undefined;
|
|
242
|
+
readonly reason: string | undefined;
|
|
243
|
+
readonly variantName: string | undefined;
|
|
244
|
+
|
|
245
|
+
constructor(
|
|
246
|
+
message: string,
|
|
247
|
+
errorCode: string,
|
|
248
|
+
fields: { field?: string; reason?: string; variantName?: string },
|
|
249
|
+
details?: unknown,
|
|
250
|
+
statusCode: number = 400,
|
|
251
|
+
) {
|
|
252
|
+
super(message, statusCode, errorCode, details);
|
|
253
|
+
this.name = "RuleVersionInvalidSnapshotError";
|
|
254
|
+
this.field = fields.field;
|
|
255
|
+
this.reason = fields.reason;
|
|
256
|
+
this.variantName = fields.variantName;
|
|
257
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static is(err: unknown): err is RuleVersionInvalidSnapshotError {
|
|
261
|
+
return err instanceof RuleVersionInvalidSnapshotError;
|
|
262
|
+
}
|
|
263
|
+
}
|
package/src/gateway.ts
CHANGED
|
@@ -3,13 +3,32 @@ import type { ResolvedPrompt } from "./prompts/types";
|
|
|
3
3
|
export interface GatewayAuthOptions {
|
|
4
4
|
apiKey: string;
|
|
5
5
|
projectId: string;
|
|
6
|
+
/**
|
|
7
|
+
* Optional environment slug routed via the gateway's `x-to11-env`
|
|
8
|
+
* header. When omitted, the gateway falls back to the project's
|
|
9
|
+
* `is_gateway_fallback=true` environment; if no fallback exists, the
|
|
10
|
+
* request fails with `400 missing_env_no_project_default`.
|
|
11
|
+
*
|
|
12
|
+
* Slug validation matches the project-environments registry:
|
|
13
|
+
* lowercase-kebab (`a-z`, `0-9`, `-`, 1..63 chars). The SDK rejects
|
|
14
|
+
* empty / whitespace-only values eagerly so misconfiguration surfaces
|
|
15
|
+
* before the request hits the wire.
|
|
16
|
+
*/
|
|
17
|
+
env?: string;
|
|
6
18
|
}
|
|
7
19
|
|
|
8
20
|
/**
|
|
9
21
|
* Build the auth headers required for every request through the to11 gateway.
|
|
10
22
|
*
|
|
23
|
+
* Includes `x-to11-env: <slug>` when `options.env` is set — see the
|
|
24
|
+
* environments-registry spec for gateway routing semantics. Empty /
|
|
25
|
+
* whitespace-only env values throw immediately so caller misconfiguration
|
|
26
|
+
* is loud.
|
|
27
|
+
*
|
|
11
28
|
* @example
|
|
12
29
|
* const headers = gatewayAuthHeaders({ apiKey, projectId });
|
|
30
|
+
* @example
|
|
31
|
+
* const headers = gatewayAuthHeaders({ apiKey, projectId, env: "staging" });
|
|
13
32
|
*/
|
|
14
33
|
export function gatewayAuthHeaders(
|
|
15
34
|
options: GatewayAuthOptions,
|
|
@@ -20,10 +39,19 @@ export function gatewayAuthHeaders(
|
|
|
20
39
|
if (options.projectId.trim().length === 0) {
|
|
21
40
|
throw new Error("[to11ai/sdk] `projectId` must not be empty.");
|
|
22
41
|
}
|
|
23
|
-
|
|
42
|
+
const headers: Record<string, string> = {
|
|
24
43
|
"x-to11-authorization": `Bearer ${options.apiKey}`,
|
|
25
44
|
"x-to11-project-id": options.projectId,
|
|
26
45
|
};
|
|
46
|
+
if (options.env !== undefined) {
|
|
47
|
+
if (options.env.trim().length === 0) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
"[to11ai/sdk] `env` must not be empty — omit the field to use the project's gateway-fallback environment.",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
headers["x-to11-env"] = options.env;
|
|
53
|
+
}
|
|
54
|
+
return headers;
|
|
27
55
|
}
|
|
28
56
|
|
|
29
57
|
/**
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// ProjectEnvironmentResource — fluent SDK surface for project environments.
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Spec: docs/product-specs/2026-05-14-environments-registry.md
|
|
5
|
+
// Plan: docs/exec-plans/active/2026-05-14-env-registry-plan.md §S21
|
|
6
|
+
//
|
|
7
|
+
// Mirrors `PromptResource` in shape so existing call-site intuition
|
|
8
|
+
// transfers: each method takes a flat input object, unwraps the API
|
|
9
|
+
// envelope, and returns the resource shape verbatim.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
import type { ApiEnvelope, Transport } from "../transport";
|
|
13
|
+
import type {
|
|
14
|
+
ArchiveProjectEnvironmentInput,
|
|
15
|
+
ArchiveProjectEnvironmentResult,
|
|
16
|
+
CreateProjectEnvironmentInput,
|
|
17
|
+
CreateProjectEnvironmentResult,
|
|
18
|
+
GetProjectEnvironmentInput,
|
|
19
|
+
GetProjectEnvironmentResult,
|
|
20
|
+
ListProjectEnvironmentsInput,
|
|
21
|
+
ListProjectEnvironmentsResult,
|
|
22
|
+
RestoreProjectEnvironmentInput,
|
|
23
|
+
RestoreProjectEnvironmentResult,
|
|
24
|
+
UpdateProjectEnvironmentInput,
|
|
25
|
+
UpdateProjectEnvironmentResult,
|
|
26
|
+
} from "./types";
|
|
27
|
+
|
|
28
|
+
// Re-export wire types so callers can import from `@to11ai/sdk/projectEnvironments`.
|
|
29
|
+
export type {
|
|
30
|
+
ArchiveProjectEnvironmentInput,
|
|
31
|
+
ArchiveProjectEnvironmentResult,
|
|
32
|
+
CreateProjectEnvironmentInput,
|
|
33
|
+
CreateProjectEnvironmentResult,
|
|
34
|
+
GetProjectEnvironmentInput,
|
|
35
|
+
GetProjectEnvironmentResult,
|
|
36
|
+
ListProjectEnvironmentsInput,
|
|
37
|
+
ListProjectEnvironmentsResult,
|
|
38
|
+
LivePointerCounts,
|
|
39
|
+
ProjectEnvironment,
|
|
40
|
+
RestoreProjectEnvironmentInput,
|
|
41
|
+
RestoreProjectEnvironmentResult,
|
|
42
|
+
UpdateProjectEnvironmentInput,
|
|
43
|
+
UpdateProjectEnvironmentResult,
|
|
44
|
+
} from "./types";
|
|
45
|
+
|
|
46
|
+
function unwrap<T>(envelope: ApiEnvelope<T>): T {
|
|
47
|
+
if (envelope.data === null || envelope.data === undefined) {
|
|
48
|
+
throw new Error("API returned success but data is null");
|
|
49
|
+
}
|
|
50
|
+
return envelope.data;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function projectEnvironmentsPath(projectId: string): string {
|
|
54
|
+
return `/v1/projects/${encodeURIComponent(projectId)}/environments`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function projectEnvironmentPath(projectId: string, slug: string): string {
|
|
58
|
+
return `${projectEnvironmentsPath(projectId)}/${encodeURIComponent(slug)}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* SDK resource for `/v1/projects/:projectId/environments`. Constructed by
|
|
63
|
+
* `createClient` and exposed as `client.projectEnvironments`.
|
|
64
|
+
*/
|
|
65
|
+
export class ProjectEnvironmentResource {
|
|
66
|
+
private readonly transport: Transport;
|
|
67
|
+
|
|
68
|
+
constructor(transport: Transport) {
|
|
69
|
+
this.transport = transport;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── List ────────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* List the project's environments. Excludes archived rows by default —
|
|
76
|
+
* pass `includeArchived: true` to include rows with a non-null
|
|
77
|
+
* `archivedAt` (used by the admin / restore UI).
|
|
78
|
+
*/
|
|
79
|
+
async list(
|
|
80
|
+
input: ListProjectEnvironmentsInput,
|
|
81
|
+
): Promise<ListProjectEnvironmentsResult> {
|
|
82
|
+
const envelope =
|
|
83
|
+
await this.transport.request<ListProjectEnvironmentsResult>(
|
|
84
|
+
"GET",
|
|
85
|
+
projectEnvironmentsPath(input.projectId),
|
|
86
|
+
{
|
|
87
|
+
query: input.includeArchived
|
|
88
|
+
? { includeArchived: "true" }
|
|
89
|
+
: undefined,
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
return unwrap(envelope);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ─── Get ─────────────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
async get(
|
|
98
|
+
input: GetProjectEnvironmentInput,
|
|
99
|
+
): Promise<GetProjectEnvironmentResult> {
|
|
100
|
+
const envelope = await this.transport.request<GetProjectEnvironmentResult>(
|
|
101
|
+
"GET",
|
|
102
|
+
projectEnvironmentPath(input.projectId, input.slug),
|
|
103
|
+
);
|
|
104
|
+
return unwrap(envelope);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ─── Create ──────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create a new environment.
|
|
111
|
+
*
|
|
112
|
+
* `slug` must match lowercase-kebab (`a-z`, `0-9`, `-`, 1..63 chars).
|
|
113
|
+
* Reserved system slugs (`prod`, `staging`) and any already-used slug
|
|
114
|
+
* return 409 `ENVIRONMENT_SLUG_TAKEN`.
|
|
115
|
+
*
|
|
116
|
+
* Setting `isGatewayFallback: true` additionally requires the
|
|
117
|
+
* `project:manage_default_environment` capability; the create use
|
|
118
|
+
* case clears the flag on any other env in the same project.
|
|
119
|
+
*/
|
|
120
|
+
async create(
|
|
121
|
+
input: CreateProjectEnvironmentInput,
|
|
122
|
+
): Promise<CreateProjectEnvironmentResult> {
|
|
123
|
+
const { projectId, ...body } = input;
|
|
124
|
+
const envelope =
|
|
125
|
+
await this.transport.request<CreateProjectEnvironmentResult>(
|
|
126
|
+
"POST",
|
|
127
|
+
projectEnvironmentsPath(projectId),
|
|
128
|
+
{ body },
|
|
129
|
+
);
|
|
130
|
+
return unwrap(envelope);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ─── Update ──────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* PATCH an environment.
|
|
137
|
+
*
|
|
138
|
+
* - System envs (`isSystem: true`) reject `slug`/`isSystem`/immutable-
|
|
139
|
+
* field mutations with 409 `SYSTEM_ENV_IMMUTABLE`.
|
|
140
|
+
* - Renaming via `newSlug` triggers a 7-day alias row server-side so
|
|
141
|
+
* in-flight gateway requests carrying the old slug keep resolving.
|
|
142
|
+
* - Mutating `isGatewayFallback` requires the
|
|
143
|
+
* `project:manage_default_environment` capability AND the target
|
|
144
|
+
* env must have at least one live snapshot (else 409
|
|
145
|
+
* `FALLBACK_ENV_NOT_READY`).
|
|
146
|
+
*
|
|
147
|
+
* The SDK marshals `newSlug` onto the wire as `slug` (the API accepts
|
|
148
|
+
* `slug` on the PATCH body for a rename — the URL slug stays the
|
|
149
|
+
* pre-rename identifier).
|
|
150
|
+
*/
|
|
151
|
+
async update(
|
|
152
|
+
input: UpdateProjectEnvironmentInput,
|
|
153
|
+
): Promise<UpdateProjectEnvironmentResult> {
|
|
154
|
+
const { projectId, slug, newSlug, ...rest } = input;
|
|
155
|
+
const body: Record<string, unknown> = {};
|
|
156
|
+
if (newSlug !== undefined) body.slug = newSlug;
|
|
157
|
+
if (rest.name !== undefined) body.name = rest.name;
|
|
158
|
+
if (rest.color !== undefined) body.color = rest.color;
|
|
159
|
+
if (rest.description !== undefined) body.description = rest.description;
|
|
160
|
+
if (rest.isPointer !== undefined) body.isPointer = rest.isPointer;
|
|
161
|
+
if (rest.requiresApproval !== undefined)
|
|
162
|
+
body.requiresApproval = rest.requiresApproval;
|
|
163
|
+
if (rest.isDefault !== undefined) body.isDefault = rest.isDefault;
|
|
164
|
+
if (rest.isGatewayFallback !== undefined)
|
|
165
|
+
body.isGatewayFallback = rest.isGatewayFallback;
|
|
166
|
+
if (rest.sortOrder !== undefined) body.sortOrder = rest.sortOrder;
|
|
167
|
+
|
|
168
|
+
const envelope =
|
|
169
|
+
await this.transport.request<UpdateProjectEnvironmentResult>(
|
|
170
|
+
"PATCH",
|
|
171
|
+
projectEnvironmentPath(projectId, slug),
|
|
172
|
+
{ body },
|
|
173
|
+
);
|
|
174
|
+
return unwrap(envelope);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ─── Archive ─────────────────────────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Soft-archive an environment. The response carries the archived row
|
|
181
|
+
* plus a `livePointerCounts` summary (informational — pointers are
|
|
182
|
+
* cascade-cleared by the use case).
|
|
183
|
+
*
|
|
184
|
+
* System envs (`isSystem: true`) and the current gateway-fallback env
|
|
185
|
+
* cannot be archived; the API returns 409 `SYSTEM_ENV_IMMUTABLE` or
|
|
186
|
+
* `CANNOT_ARCHIVE_GATEWAY_FALLBACK` respectively.
|
|
187
|
+
*
|
|
188
|
+
* Uses `POST /:slug/archive` rather than `DELETE` so the server can
|
|
189
|
+
* return a structured 200 with the archived row + counts.
|
|
190
|
+
*/
|
|
191
|
+
async archive(
|
|
192
|
+
input: ArchiveProjectEnvironmentInput,
|
|
193
|
+
): Promise<ArchiveProjectEnvironmentResult> {
|
|
194
|
+
const envelope =
|
|
195
|
+
await this.transport.request<ArchiveProjectEnvironmentResult>(
|
|
196
|
+
"POST",
|
|
197
|
+
`${projectEnvironmentPath(input.projectId, input.slug)}/archive`,
|
|
198
|
+
);
|
|
199
|
+
return unwrap(envelope);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ─── Restore ─────────────────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Restore a previously-archived environment. The server resets
|
|
206
|
+
* `archivedAt` to `null` and returns the live row. No-op if the env
|
|
207
|
+
* is already live; returns 404 `ENVIRONMENT_NOT_FOUND` if the slug
|
|
208
|
+
* has never existed.
|
|
209
|
+
*/
|
|
210
|
+
async restore(
|
|
211
|
+
input: RestoreProjectEnvironmentInput,
|
|
212
|
+
): Promise<RestoreProjectEnvironmentResult> {
|
|
213
|
+
const envelope =
|
|
214
|
+
await this.transport.request<RestoreProjectEnvironmentResult>(
|
|
215
|
+
"POST",
|
|
216
|
+
`${projectEnvironmentPath(input.projectId, input.slug)}/restore`,
|
|
217
|
+
);
|
|
218
|
+
return unwrap(envelope);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Project Environments wire types (TO11-793 / env-registry §S21).
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Spec: docs/product-specs/2026-05-14-environments-registry.md
|
|
5
|
+
// Plan: docs/exec-plans/active/2026-05-14-env-registry-plan.md §S21
|
|
6
|
+
//
|
|
7
|
+
// Hand-rolled TS mirrors of the Effect Schema types defined in
|
|
8
|
+
// `apps/api/src/domains/tenancy/projects/model/project-environment.ts`. The
|
|
9
|
+
// published `@to11ai/sdk` package intentionally does NOT depend on the
|
|
10
|
+
// internal `@to11/api` workspace package — keep these in lockstep with the
|
|
11
|
+
// API model when adding fields.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
// ─── Wire shape ─────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Project-scoped environment row as returned by
|
|
18
|
+
* `GET /v1/projects/:projectId/environments/:slug`.
|
|
19
|
+
*
|
|
20
|
+
* Notes:
|
|
21
|
+
* - `isSystem: true` for the seeded `prod` / `staging` rows. Those rows
|
|
22
|
+
* cannot be renamed, archived, or have their `slug`/`isSystem` mutated.
|
|
23
|
+
* - `isGatewayFallback: true` marks the env the gateway routes to when no
|
|
24
|
+
* `x-to11-env` header is supplied. At most one env per project may carry
|
|
25
|
+
* this flag; clearing the only fallback is rejected with 409
|
|
26
|
+
* `CANNOT_CLEAR_ONLY_GATEWAY_FALLBACK`.
|
|
27
|
+
* - `archivedAt` is `null` for live envs and an ISO-8601 timestamp for
|
|
28
|
+
* soft-archived ones.
|
|
29
|
+
*/
|
|
30
|
+
export interface ProjectEnvironment {
|
|
31
|
+
id: string;
|
|
32
|
+
projectId: string;
|
|
33
|
+
slug: string;
|
|
34
|
+
name: string;
|
|
35
|
+
color: string | null;
|
|
36
|
+
description: string | null;
|
|
37
|
+
isPointer: boolean;
|
|
38
|
+
requiresApproval: boolean;
|
|
39
|
+
isDefault: boolean;
|
|
40
|
+
isGatewayFallback: boolean;
|
|
41
|
+
sortOrder: number;
|
|
42
|
+
isSystem: boolean;
|
|
43
|
+
archivedAt: string | null;
|
|
44
|
+
createdBy: string;
|
|
45
|
+
updatedBy: string;
|
|
46
|
+
createdAt: string;
|
|
47
|
+
updatedAt: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Pointer-count payload attached to archive responses. `prompts` is real
|
|
52
|
+
* today; `rules` / `providers` are 0 in v1 (reserved for Wave 6+ specs to
|
|
53
|
+
* keep the response shape stable when those domains land).
|
|
54
|
+
*/
|
|
55
|
+
export interface LivePointerCounts {
|
|
56
|
+
prompts: number;
|
|
57
|
+
rules: number;
|
|
58
|
+
providers: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Response body for `POST /v1/projects/:projectId/environments/:slug/archive`.
|
|
63
|
+
* Carries the soft-archived environment plus the count of live pointers
|
|
64
|
+
* cascade-cleared by the archive operation (informational, for the
|
|
65
|
+
* confirmation modal in the web UI).
|
|
66
|
+
*/
|
|
67
|
+
export interface ArchiveProjectEnvironmentResult {
|
|
68
|
+
environment: ProjectEnvironment;
|
|
69
|
+
livePointerCounts: LivePointerCounts;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── Inputs ─────────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export interface ListProjectEnvironmentsInput {
|
|
75
|
+
projectId: string;
|
|
76
|
+
/**
|
|
77
|
+
* When `true`, the response includes archived envs (those with a
|
|
78
|
+
* non-null `archivedAt`). Default behavior excludes them.
|
|
79
|
+
*/
|
|
80
|
+
includeArchived?: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type ListProjectEnvironmentsResult = ProjectEnvironment[];
|
|
84
|
+
|
|
85
|
+
export interface GetProjectEnvironmentInput {
|
|
86
|
+
projectId: string;
|
|
87
|
+
slug: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type GetProjectEnvironmentResult = ProjectEnvironment;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create input — must specify a unique `slug` matching lowercase-kebab
|
|
94
|
+
* (a-z, 0-9, hyphen, 1..63 chars). Reserved system slugs (`prod`,
|
|
95
|
+
* `staging`) are rejected by the API with `ENVIRONMENT_SLUG_TAKEN`.
|
|
96
|
+
*
|
|
97
|
+
* Setting `isGatewayFallback: true` here additionally requires the
|
|
98
|
+
* `project:manage_default_environment` capability; the create use case
|
|
99
|
+
* atomically clears the flag on any other env in the same project.
|
|
100
|
+
*/
|
|
101
|
+
export interface CreateProjectEnvironmentInput {
|
|
102
|
+
projectId: string;
|
|
103
|
+
slug: string;
|
|
104
|
+
name: string;
|
|
105
|
+
color?: string | null;
|
|
106
|
+
description?: string | null;
|
|
107
|
+
isPointer?: boolean;
|
|
108
|
+
requiresApproval?: boolean;
|
|
109
|
+
isDefault?: boolean;
|
|
110
|
+
isGatewayFallback?: boolean;
|
|
111
|
+
sortOrder?: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type CreateProjectEnvironmentResult = ProjectEnvironment;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* PATCH input — every field is optional. `slug` rename triggers a 7-day
|
|
118
|
+
* alias row server-side. System envs (`isSystem: true`) reject `slug`,
|
|
119
|
+
* `isSystem`, and other immutable-field mutations with
|
|
120
|
+
* `SYSTEM_ENV_IMMUTABLE`.
|
|
121
|
+
*
|
|
122
|
+
* As with `create`, mutating `isGatewayFallback` requires the additional
|
|
123
|
+
* `project:manage_default_environment` capability.
|
|
124
|
+
*/
|
|
125
|
+
export interface UpdateProjectEnvironmentInput {
|
|
126
|
+
projectId: string;
|
|
127
|
+
slug: string;
|
|
128
|
+
newSlug?: string;
|
|
129
|
+
name?: string;
|
|
130
|
+
color?: string | null;
|
|
131
|
+
description?: string | null;
|
|
132
|
+
isPointer?: boolean;
|
|
133
|
+
requiresApproval?: boolean;
|
|
134
|
+
isDefault?: boolean;
|
|
135
|
+
isGatewayFallback?: boolean;
|
|
136
|
+
sortOrder?: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type UpdateProjectEnvironmentResult = ProjectEnvironment;
|
|
140
|
+
|
|
141
|
+
export interface ArchiveProjectEnvironmentInput {
|
|
142
|
+
projectId: string;
|
|
143
|
+
slug: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface RestoreProjectEnvironmentInput {
|
|
147
|
+
projectId: string;
|
|
148
|
+
slug: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type RestoreProjectEnvironmentResult = ProjectEnvironment;
|