@whittlelabs/sifter 0.43.2 → 0.43.4

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 (3) hide show
  1. package/bin.js +436 -274
  2. package/bin.js.map +4 -4
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -9780,12 +9780,129 @@ var require_canonicalise = __commonJS({
9780
9780
  }
9781
9781
  });
9782
9782
 
9783
+ // ../../packages/contracts/dist/client.js
9784
+ var require_client = __commonJS({
9785
+ "../../packages/contracts/dist/client.js"(exports2) {
9786
+ "use strict";
9787
+ Object.defineProperty(exports2, "__esModule", { value: true });
9788
+ exports2.createContractClient = createContractClient;
9789
+ function createContractClient({ baseUrl, transport }) {
9790
+ const root = baseUrl.replace(/\/+$/, "");
9791
+ async function request(method, template, options = {}) {
9792
+ const headers = { Accept: "application/json" };
9793
+ if (options.body !== void 0)
9794
+ headers["Content-Type"] = "application/json";
9795
+ Object.assign(headers, options.headers);
9796
+ let response;
9797
+ try {
9798
+ response = await transport(`${root}${filledPath(template, options.path)}${queryString(options.query)}`, {
9799
+ method: method.toUpperCase(),
9800
+ headers,
9801
+ body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
9802
+ signal: options.signal
9803
+ });
9804
+ } catch (err2) {
9805
+ return failure("NETWORK_ERROR", err2 instanceof Error ? err2.message : "Network request failed", null);
9806
+ }
9807
+ return answered(response);
9808
+ }
9809
+ const call = (method) => (path, options) => request(method, path, options);
9810
+ return {
9811
+ get: call("get"),
9812
+ post: call("post"),
9813
+ put: call("put"),
9814
+ patch: call("patch"),
9815
+ delete: call("delete")
9816
+ };
9817
+ }
9818
+ function filledPath(template, params) {
9819
+ const values = params ?? {};
9820
+ return template.replace(/\{([^}]+)\}/g, (_, name2) => {
9821
+ const value = values[name2];
9822
+ if (value === void 0 || value === null)
9823
+ throw new TypeError(`${template} needs the path parameter ${name2}`);
9824
+ return encodeURIComponent(String(value));
9825
+ });
9826
+ }
9827
+ function queryString(query) {
9828
+ const search = new URLSearchParams();
9829
+ for (const [key, value] of Object.entries(query ?? {})) {
9830
+ if (value === void 0 || value === null)
9831
+ continue;
9832
+ for (const item of Array.isArray(value) ? value : [value])
9833
+ search.append(key, String(item));
9834
+ }
9835
+ const text = search.toString();
9836
+ return text ? `?${text}` : "";
9837
+ }
9838
+ async function answered(response) {
9839
+ const { status } = response;
9840
+ let text;
9841
+ try {
9842
+ text = await response.text();
9843
+ } catch (err2) {
9844
+ return failure("NETWORK_ERROR", err2 instanceof Error ? err2.message : "The response could not be read", status);
9845
+ }
9846
+ let payload;
9847
+ let parsed = true;
9848
+ if (text !== "") {
9849
+ try {
9850
+ payload = JSON.parse(text);
9851
+ } catch {
9852
+ parsed = false;
9853
+ }
9854
+ }
9855
+ if (response.ok) {
9856
+ if (text === "")
9857
+ return { success: true, data: void 0, status };
9858
+ if (!parsed)
9859
+ return failure("SERVER_ERROR", `The ${status} response was not JSON`, status);
9860
+ if (isRecord(payload) && payload.success === true)
9861
+ return { success: true, data: payload.data, status };
9862
+ return { success: true, data: payload, status };
9863
+ }
9864
+ if (isRecord(payload) && payload.success === false && isRecord(payload.error)) {
9865
+ const { code, message } = payload.error;
9866
+ if (typeof code === "string" && typeof message === "string") {
9867
+ return failure(code, message, status, saysMore(payload, payload.error) ? payload : void 0);
9868
+ }
9869
+ }
9870
+ return failure("SERVER_ERROR", `Request failed with status ${status}`, status, text !== "" && parsed ? payload : void 0);
9871
+ }
9872
+ function saysMore(envelope, error) {
9873
+ return Object.keys(envelope).some((key) => key !== "success" && key !== "error") || Object.keys(error).some((key) => key !== "code" && key !== "message");
9874
+ }
9875
+ function failure(code, message, status, body2) {
9876
+ return { success: false, error: { code, message, status, ...body2 !== void 0 ? { body: body2 } : {} } };
9877
+ }
9878
+ function isRecord(value) {
9879
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9880
+ }
9881
+ }
9882
+ });
9883
+
9884
+ // ../../packages/contracts/dist/services/loom.js
9885
+ var require_loom = __commonJS({
9886
+ "../../packages/contracts/dist/services/loom.js"(exports2) {
9887
+ "use strict";
9888
+ Object.defineProperty(exports2, "__esModule", { value: true });
9889
+ exports2.service = void 0;
9890
+ exports2.createClient = createClient;
9891
+ var client_1 = require_client();
9892
+ exports2.service = "whittle-loom";
9893
+ function createClient(options) {
9894
+ return (0, client_1.createContractClient)(options);
9895
+ }
9896
+ }
9897
+ });
9898
+
9783
9899
  // ../../packages/loom/dist/seed/seed-workflows.js
9784
9900
  var require_seed_workflows = __commonJS({
9785
9901
  "../../packages/loom/dist/seed/seed-workflows.js"(exports2) {
9786
9902
  "use strict";
9787
9903
  Object.defineProperty(exports2, "__esModule", { value: true });
9788
9904
  exports2.seedWorkflows = seedWorkflows;
9905
+ var loom_1 = require_loom();
9789
9906
  var canonicalise_1 = require_canonicalise();
9790
9907
  var errors_1 = require_errors2();
9791
9908
  var NULL_LOGGER = {
@@ -9799,13 +9916,16 @@ var require_seed_workflows = __commonJS({
9799
9916
  async function seedWorkflows(opts) {
9800
9917
  const logger = opts.logger ?? NULL_LOGGER;
9801
9918
  const fetchFn = opts.fetchFn ?? fetch;
9802
- const baseUrl = opts.loomApiUrl.replace(/\/$/, "");
9803
- const headers = { "Content-Type": "application/json" };
9804
- if ("accessToken" in opts.auth) {
9805
- headers["Authorization"] = `Bearer ${opts.auth.accessToken}`;
9806
- } else {
9807
- headers["X-API-Key"] = opts.auth.apiKey;
9808
- }
9919
+ const credential = "accessToken" in opts.auth ? { Authorization: `Bearer ${opts.auth.accessToken}` } : { "X-API-Key": opts.auth.apiKey };
9920
+ const loom = (0, loom_1.createClient)({
9921
+ baseUrl: opts.loomApiUrl,
9922
+ transport: (url, init2) => {
9923
+ const headers = new Headers(init2.headers);
9924
+ for (const [name2, value] of Object.entries(credential))
9925
+ headers.set(name2, value);
9926
+ return fetchFn(url, { ...init2, headers });
9927
+ }
9928
+ });
9809
9929
  const results = [];
9810
9930
  for (const workflow of opts.workflows) {
9811
9931
  let ownerClass;
@@ -9841,17 +9961,12 @@ var require_seed_workflows = __commonJS({
9841
9961
  nodes: body2.nodes.length,
9842
9962
  transitions: body2.transitions.length
9843
9963
  });
9844
- const response = await fetchFn(`${baseUrl}/api/admin/seed-workflow`, {
9845
- method: "POST",
9846
- headers,
9847
- body: JSON.stringify(body2)
9848
- });
9849
- const json = await response.json();
9850
- if (!response.ok || !json.success || !json.data) {
9851
- const message = json.error?.message ?? `seedWorkflows: ${workflow.name} failed with HTTP ${response.status}`;
9964
+ const result = await loom.post("/api/admin/seed-workflow", { body: body2 });
9965
+ if (!result.success) {
9966
+ const message = result.error.code === "SERVER_ERROR" ? `seedWorkflows: ${workflow.name} failed with HTTP ${result.error.status}` : result.error.message;
9852
9967
  throw new errors_1.SeedWorkflowsError(workflow.name, message);
9853
9968
  }
9854
- results.push(json.data);
9969
+ results.push(result.data);
9855
9970
  }
9856
9971
  return { workflows: results };
9857
9972
  }
@@ -58980,23 +59095,41 @@ var require_http_api = __commonJS({
58980
59095
  }
58981
59096
  });
58982
59097
 
59098
+ // ../../packages/contracts/dist/services/keep.js
59099
+ var require_keep = __commonJS({
59100
+ "../../packages/contracts/dist/services/keep.js"(exports2) {
59101
+ "use strict";
59102
+ Object.defineProperty(exports2, "__esModule", { value: true });
59103
+ exports2.service = void 0;
59104
+ exports2.createClient = createClient;
59105
+ var client_1 = require_client();
59106
+ exports2.service = "whittle-keep";
59107
+ function createClient(options) {
59108
+ return (0, client_1.createContractClient)(options);
59109
+ }
59110
+ }
59111
+ });
59112
+
58983
59113
  // ../../packages/keep/dist/client.js
58984
- var require_client = __commonJS({
59114
+ var require_client2 = __commonJS({
58985
59115
  "../../packages/keep/dist/client.js"(exports2) {
58986
59116
  "use strict";
58987
59117
  Object.defineProperty(exports2, "__esModule", { value: true });
58988
59118
  exports2.KeepClient = void 0;
59119
+ var keep_1 = require_keep();
58989
59120
  var KeepClient = class {
58990
- baseUrl;
58991
- headers;
59121
+ api;
58992
59122
  constructor(config) {
58993
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
58994
- this.headers = { "Content-Type": "application/json" };
58995
- if (config.serviceToken) {
58996
- this.headers["Authorization"] = `Bearer ${config.serviceToken}`;
58997
- } else if (config.accessToken) {
58998
- this.headers["Authorization"] = `Bearer ${config.accessToken}`;
58999
- }
59123
+ const token = config.serviceToken ?? config.accessToken;
59124
+ this.api = (0, keep_1.createClient)({
59125
+ baseUrl: config.baseUrl,
59126
+ transport: (url, init2) => {
59127
+ const headers = new Headers(init2.headers);
59128
+ if (token)
59129
+ headers.set("Authorization", `Bearer ${token}`);
59130
+ return fetch(url, { ...init2, headers });
59131
+ }
59132
+ });
59000
59133
  }
59001
59134
  // ── Actor Tokens ────────────────────────────────────────────────
59002
59135
  /**
@@ -59005,7 +59138,7 @@ var require_client = __commonJS({
59005
59138
  * constructed with a service token holding `keep:actor-tokens:write`.
59006
59139
  */
59007
59140
  async mintActorToken(principal) {
59008
- return this.request("POST", "/api/actor-tokens", { principal });
59141
+ return answer(await this.api.post("/api/actor-tokens", { body: { principal } }));
59009
59142
  }
59010
59143
  // ── Identity ────────────────────────────────────────────────────
59011
59144
  /**
@@ -59014,101 +59147,98 @@ var require_client = __commonJS({
59014
59147
  * POST /api/challenges/:token/response to mint a session.
59015
59148
  */
59016
59149
  async createAccount(email, password, name2) {
59017
- const body2 = await this.envelope("POST", "/api/accounts", { email, password, name: name2 });
59018
- if (!body2.challenge) {
59019
- throw new Error("sign-up returned no challenge; the contract expects one");
59020
- }
59021
- return body2.challenge;
59150
+ const issued = answer(await this.api.post("/api/accounts", { body: { email, password, name: name2 } }));
59151
+ return issued.challenge;
59022
59152
  }
59023
59153
  /**
59024
59154
  * Sign in. Two legitimate outcomes, neither an error: a session, or a
59025
59155
  * challenge (MFA, unverified email). Both arrive on HTTP 200.
59026
59156
  */
59027
59157
  async createSession(email, password) {
59028
- const body2 = await this.envelope("POST", "/api/sessions", { email, password });
59029
- if (body2.challenge) {
59030
- return { kind: "challenge", challenge: body2.challenge };
59031
- }
59032
- const data = body2.data;
59033
- if (!data?.session) {
59034
- throw new Error("sign-in returned neither a session nor a challenge");
59035
- }
59036
- return { kind: "session", user: data.user, session: data.session };
59158
+ return signedIn(answer(await this.api.post("/api/sessions", { body: { email, password } })));
59037
59159
  }
59038
- /** Answer a challenge and obtain the session it was standing in for. */
59160
+ /**
59161
+ * Answer a challenge. Its answer is the session it was standing in for, or
59162
+ * the next challenge (an MFA step after the email was verified).
59163
+ */
59039
59164
  async respondToChallenge(challengeToken, type, response) {
59040
- return this.request("POST", `/api/challenges/${encodeURIComponent(challengeToken)}/response`, { type, response });
59165
+ return signedIn(answer(await this.api.post("/api/challenges/{token}/response", {
59166
+ path: { token: challengeToken },
59167
+ body: { type, response }
59168
+ })));
59041
59169
  }
59170
+ /** End the session the refresh token belongs to. */
59042
59171
  async destroySession(refreshToken) {
59043
- await this.request("DELETE", "/api/sessions", refreshToken ? { refreshToken } : void 0);
59172
+ answer(await this.api.delete("/api/sessions", { body: { refreshToken } }));
59044
59173
  }
59045
59174
  async refreshSession(refreshToken) {
59046
- return this.request("POST", "/api/sessions/renewal", { refreshToken });
59175
+ return answer(await this.api.post("/api/sessions/renewal", { body: { refreshToken } })).session;
59047
59176
  }
59048
59177
  async getCurrentUser() {
59049
- return this.request("GET", "/api/accounts/me");
59178
+ return answer(await this.api.get("/api/accounts/me")).user;
59050
59179
  }
59051
59180
  async updateProfile(updates) {
59052
- return this.request("PATCH", "/api/accounts/me", updates);
59181
+ return answer(await this.api.patch("/api/accounts/me", { body: updates })).user;
59053
59182
  }
59054
59183
  async confirmEmail(email, code) {
59055
- await this.request("POST", "/api/email-confirmations", { email, code });
59184
+ answer(await this.api.post("/api/email-confirmations", { body: { email, code } }));
59056
59185
  }
59057
59186
  async requestVerificationCode(email) {
59058
- await this.request("POST", "/api/verification-codes", { email });
59187
+ answer(await this.api.post("/api/verification-codes", { body: { email } }));
59059
59188
  }
59060
59189
  async requestPasswordReset(email) {
59061
- await this.request("POST", "/api/password-resets", { email });
59190
+ answer(await this.api.post("/api/password-resets", { body: { email } }));
59062
59191
  }
59063
59192
  async resetPassword(email, code, newPassword) {
59064
- await this.request("PUT", `/api/password-resets/${code}`, { email, newPassword });
59193
+ answer(await this.api.put("/api/password-resets/{code}", { path: { code }, body: { email, newPassword } }));
59065
59194
  }
59066
59195
  async changePassword(currentPassword, newPassword) {
59067
- await this.request("PUT", "/api/passwords", { currentPassword, newPassword });
59196
+ answer(await this.api.put("/api/passwords", { body: { currentPassword, newPassword } }));
59068
59197
  }
59069
59198
  // ── Organizations ───────────────────────────────────────────────
59070
59199
  async listOrganizations() {
59071
- const { organizations } = await this.request("GET", "/api/orgs");
59072
- return organizations;
59200
+ return answer(await this.api.get("/api/orgs")).organizations;
59073
59201
  }
59074
59202
  async getOrganization(orgId) {
59075
- return this.request("GET", `/api/orgs/${orgId}`);
59203
+ return answer(await this.api.get("/api/orgs/{orgId}", { path: { orgId } }));
59076
59204
  }
59077
59205
  async createOrganization(name2, description) {
59078
- return this.request("POST", "/api/orgs", { name: name2, description });
59206
+ return answer(await this.api.post("/api/orgs", { body: { name: name2, description } }));
59079
59207
  }
59080
59208
  async updateOrganization(orgId, updates) {
59081
- return this.request("PATCH", `/api/orgs/${orgId}`, updates);
59209
+ return answer(await this.api.patch("/api/orgs/{orgId}", { path: { orgId }, body: updates }));
59082
59210
  }
59083
59211
  async deleteOrganization(orgId) {
59084
- await this.request("DELETE", `/api/orgs/${orgId}`);
59212
+ answer(await this.api.delete("/api/orgs/{orgId}", { path: { orgId } }));
59085
59213
  }
59086
59214
  async listMembers(orgId) {
59087
- return this.request("GET", `/api/orgs/${orgId}/members`);
59215
+ return answer(await this.api.get("/api/orgs/{orgId}/members", { path: { orgId } }));
59088
59216
  }
59089
59217
  async removeMember(orgId, userId) {
59090
- await this.request("DELETE", `/api/orgs/${orgId}/members/${userId}`);
59218
+ answer(await this.api.delete("/api/orgs/{orgId}/members/{userId}", { path: { orgId, userId } }));
59091
59219
  }
59092
59220
  async listTeams(orgId) {
59093
- return this.request("GET", `/api/orgs/${orgId}/teams`);
59221
+ return answer(await this.api.get("/api/orgs/{orgId}/teams", { path: { orgId } }));
59094
59222
  }
59095
59223
  async createTeam(orgId, name2, description) {
59096
- return this.request("POST", `/api/orgs/${orgId}/teams`, { name: name2, description });
59224
+ return answer(await this.api.post("/api/orgs/{orgId}/teams", { path: { orgId }, body: { name: name2, description } }));
59097
59225
  }
59098
59226
  async listTeamMembers(orgId, teamId) {
59099
- return this.request("GET", `/api/orgs/${orgId}/teams/${teamId}/members`);
59227
+ return answer(await this.api.get("/api/orgs/{orgId}/teams/{teamId}/members", { path: { orgId, teamId } }));
59100
59228
  }
59101
59229
  async addTeamMember(orgId, teamId, userId) {
59102
- return this.request("POST", `/api/orgs/${orgId}/teams/${teamId}/members`, { userId });
59230
+ return answer(await this.api.post("/api/orgs/{orgId}/teams/{teamId}/members", { path: { orgId, teamId }, body: { userId } }));
59103
59231
  }
59104
- async createInvitation(orgId, email, roleGrants) {
59105
- return this.request("POST", `/api/orgs/${orgId}/invitations`, { email, roleGrants });
59232
+ /** Invite someone to the organization, seated in at least one of its seat pools (ADR-0147). */
59233
+ async createInvitation(orgId, invitation) {
59234
+ return answer(await this.api.post("/api/orgs/{orgId}/invitations", { path: { orgId }, body: invitation }));
59106
59235
  }
59236
+ /** The invitation behind a token, with the organization it is to. */
59107
59237
  async getInvitation(token) {
59108
- return this.request("GET", `/api/invitations/${token}`);
59238
+ return answer(await this.api.get("/api/invitations/{token}", { path: { token } }));
59109
59239
  }
59110
59240
  async acceptInvitation(token) {
59111
- await this.request("POST", `/api/invitations/${token}/acceptance`);
59241
+ answer(await this.api.post("/api/invitations/{token}/acceptance", { path: { token } }));
59112
59242
  }
59113
59243
  // ── Substrate Provisioning ──────────────────────────────────────
59114
59244
  /**
@@ -59119,7 +59249,10 @@ var require_client = __commonJS({
59119
59249
  * Requires `keep:service-identities:write`.
59120
59250
  */
59121
59251
  async findOrCreateServiceIdentity(request) {
59122
- return this.request("POST", `/api/users/${request.owner.principalId}/service-identities`, request);
59252
+ return answer(await this.api.post("/api/users/{userId}/service-identities", {
59253
+ path: { userId: request.owner.principalId },
59254
+ body: request
59255
+ }));
59123
59256
  }
59124
59257
  /**
59125
59258
  * Bind a role (by name) to a service identity, held at a structural
@@ -59128,7 +59261,10 @@ var require_client = __commonJS({
59128
59261
  * Requires `keep:role-bindings:write`.
59129
59262
  */
59130
59263
  async bindServiceIdentityRole(serviceIdentityId, request) {
59131
- return this.request("POST", `/api/service-identities/${serviceIdentityId}/role-bindings`, request);
59264
+ return answer(await this.api.post("/api/service-identities/{id}/role-bindings", {
59265
+ path: { id: serviceIdentityId },
59266
+ body: request
59267
+ }));
59132
59268
  }
59133
59269
  /**
59134
59270
  * Mint a pre-authorised device-code (pairing code) bound to an existing
@@ -59138,11 +59274,9 @@ var require_client = __commonJS({
59138
59274
  * Requires `keep:service-identities:write`.
59139
59275
  */
59140
59276
  async createDeviceCodePreauth(request) {
59141
- return this.request("POST", "/api/oauth/device-code/preauth", {
59142
- client_id: request.clientId,
59143
- boundAgentId: request.boundAgentId,
59144
- ttlSeconds: request.ttlSeconds
59145
- });
59277
+ return answer(await this.api.post("/api/oauth/device-code/preauth", {
59278
+ body: { client_id: request.clientId, boundAgentId: request.boundAgentId, ttlSeconds: request.ttlSeconds }
59279
+ }));
59146
59280
  }
59147
59281
  // ── Hosted Sifter ───────────────────────────────────────────────
59148
59282
  /**
@@ -59155,7 +59289,10 @@ var require_client = __commonJS({
59155
59289
  * Use the credential for the dispatch and drop it from memory afterwards.
59156
59290
  */
59157
59291
  async revealModelCredential(userId, options = {}) {
59158
- return this.request("POST", `/api/users/${encodeURIComponent(userId)}/model-credential`, options.provider ? { provider: options.provider } : {});
59292
+ return answer(await this.api.post("/api/users/{userId}/model-credential", {
59293
+ path: { userId },
59294
+ body: options.provider ? { provider: options.provider } : {}
59295
+ }));
59159
59296
  }
59160
59297
  // ── GitHub OAuth token ──────────────────────────────────────────
59161
59298
  /**
@@ -59166,50 +59303,23 @@ var require_client = __commonJS({
59166
59303
  * NotFound) when the user has not connected GitHub.
59167
59304
  */
59168
59305
  async getUserGithubToken(userId) {
59169
- return this.request("GET", `/api/users/${encodeURIComponent(userId)}/github-token`);
59170
- }
59171
- // ── HTTP ────────────────────────────────────────────────────────
59172
- /**
59173
- * Reads the response envelope WITHOUT treating `success: false` as a
59174
- * failure. Sign-in and sign-up use it because a challenge arrives on a 2xx
59175
- * with success:false and no error object — `request` would raise a
59176
- * misleading "Request failed with status 200" and give the caller no way to
59177
- * tell MFA from a bad password (whittlelabs#530). A real error still throws.
59178
- */
59179
- async envelope(method, path, body2) {
59180
- const response = await fetch(`${this.baseUrl}${path}`, {
59181
- method,
59182
- headers: this.headers,
59183
- body: body2 ? JSON.stringify(body2) : void 0
59184
- });
59185
- const json = await response.json();
59186
- if (!response.ok || !json.success && !json.challenge) {
59187
- const error = new Error(json.error?.message ?? `Request failed with status ${response.status}`);
59188
- error.code = json.error?.code;
59189
- error.statusCode = response.status;
59190
- throw error;
59191
- }
59192
- return json;
59193
- }
59194
- async request(method, path, body2) {
59195
- const url = `${this.baseUrl}${path}`;
59196
- const response = await fetch(url, {
59197
- method,
59198
- headers: this.headers,
59199
- body: body2 ? JSON.stringify(body2) : void 0
59200
- });
59201
- const json = await response.json();
59202
- if (!response.ok || !json.success) {
59203
- const errorMessage = json.error?.message ?? `Request failed with status ${response.status}`;
59204
- const error = new Error(errorMessage);
59205
- error.code = json.error?.code;
59206
- error.statusCode = response.status;
59207
- throw error;
59208
- }
59209
- return json.data;
59306
+ return answer(await this.api.get("/api/users/{userId}/github-token", { path: { userId } }));
59210
59307
  }
59211
59308
  };
59212
59309
  exports2.KeepClient = KeepClient;
59310
+ function signedIn(answered) {
59311
+ if ("challenge" in answered)
59312
+ return { kind: "challenge", challenge: answered.challenge };
59313
+ return { kind: "session", user: answered.user, session: answered.session };
59314
+ }
59315
+ function answer(result) {
59316
+ if (result.success)
59317
+ return result.data;
59318
+ const error = new Error(result.error.message);
59319
+ error.code = result.error.code;
59320
+ error.statusCode = result.error.status ?? void 0;
59321
+ throw error;
59322
+ }
59213
59323
  }
59214
59324
  });
59215
59325
 
@@ -59299,6 +59409,7 @@ var require_exchange = __commonJS({
59299
59409
  exports2.KeepUnavailableError = void 0;
59300
59410
  exports2.exchangeCredentials = exchangeCredentials;
59301
59411
  exports2.hostOf = hostOf;
59412
+ var keep_1 = require_keep();
59302
59413
  var rate_limited_1 = require_rate_limited();
59303
59414
  var KeepUnavailableError = class extends Error {
59304
59415
  code = "SERVICE_UNAVAILABLE";
@@ -59315,52 +59426,77 @@ var require_exchange = __commonJS({
59315
59426
  };
59316
59427
  exports2.KeepUnavailableError = KeepUnavailableError;
59317
59428
  async function exchangeCredentials(request) {
59318
- const { manager, url } = request;
59319
- const host = hostOf(url);
59320
- let response;
59321
- try {
59322
- response = await fetch(url, {
59323
- method: "POST",
59324
- headers: { "Content-Type": "application/json" },
59325
- body: JSON.stringify({
59326
- clientId: request.clientId,
59327
- clientSecret: request.clientSecret
59328
- })
59329
- });
59330
- } catch (err2) {
59331
- throw new KeepUnavailableError(`[${manager}] could not reach Keep at ${host}: ${describeTransportError(err2)}`, { host, statusCode: null, cause: err2 });
59429
+ const { manager } = request;
59430
+ const host = hostOf(request.keepApiUrl);
59431
+ let raised;
59432
+ const raise = (error) => {
59433
+ raised = { error };
59434
+ throw error;
59435
+ };
59436
+ let refusalMessage;
59437
+ const api = (0, keep_1.createClient)({
59438
+ baseUrl: request.keepApiUrl,
59439
+ transport: async (url, init2) => {
59440
+ let response;
59441
+ try {
59442
+ response = await fetch(url, init2);
59443
+ } catch (err2) {
59444
+ return raise(new KeepUnavailableError(`[${manager}] could not reach Keep at ${host}: ${describeTransportError(err2)}`, {
59445
+ host,
59446
+ statusCode: null,
59447
+ cause: err2
59448
+ }));
59449
+ }
59450
+ try {
59451
+ request.onResponse?.(response);
59452
+ } catch (err2) {
59453
+ return raise(err2);
59454
+ }
59455
+ const text = await response.text().catch(() => "");
59456
+ const json = parsedEnvelope(text);
59457
+ if (response.status >= 500) {
59458
+ const reason = json?.error?.message ?? `${request.exchangeName} failed with status ${response.status}`;
59459
+ return raise(new KeepUnavailableError(`[${manager}] Keep at ${host} is unavailable (${response.status}): ${reason}`, {
59460
+ host,
59461
+ statusCode: response.status
59462
+ }));
59463
+ }
59464
+ if (response.status === 429) {
59465
+ return raise(request.rateLimit.close({
59466
+ manager,
59467
+ host,
59468
+ reason: json?.error?.message ?? "Too Many Requests",
59469
+ retryAfterMs: (0, rate_limited_1.retryAfterMsOf)(json, response.headers)
59470
+ }));
59471
+ }
59472
+ if (json === null) {
59473
+ return raise(new Error(`[${manager}] Keep at ${host} answered ${response.status} with a body that is not JSON`));
59474
+ }
59475
+ refusalMessage = json.error?.message;
59476
+ if (response.ok && json.success === false) {
59477
+ return raise(new Error(`[${manager}] ${refusalMessage ?? `${request.exchangeName} failed with status ${response.status}`}`));
59478
+ }
59479
+ return new Response(text, { status: response.status, statusText: response.statusText, headers: response.headers });
59480
+ }
59481
+ });
59482
+ const result = await request.send(api);
59483
+ if (raised)
59484
+ throw raised.error;
59485
+ if (result.success)
59486
+ return result.data;
59487
+ const msg = refusalMessage ?? `${request.exchangeName} failed with status ${result.error.status}`;
59488
+ if (result.error.status === 401 && request.refused) {
59489
+ throw request.refused(msg);
59332
59490
  }
59333
- request.onResponse?.(response);
59334
- const text = await response.text().catch(() => "");
59335
- let json;
59491
+ throw new Error(`[${manager}] ${msg}`);
59492
+ }
59493
+ function parsedEnvelope(text) {
59336
59494
  try {
59337
- json = JSON.parse(text);
59495
+ const parsed = JSON.parse(text);
59496
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
59338
59497
  } catch {
59339
- json = null;
59340
- }
59341
- if (response.status >= 500) {
59342
- const reason = json?.error?.message ?? `${request.exchangeName} failed with status ${response.status}`;
59343
- throw new KeepUnavailableError(`[${manager}] Keep at ${host} is unavailable (${response.status}): ${reason}`, { host, statusCode: response.status });
59344
- }
59345
- if (response.status === 429) {
59346
- throw request.rateLimit.close({
59347
- manager,
59348
- host,
59349
- reason: json?.error?.message ?? "Too Many Requests",
59350
- retryAfterMs: (0, rate_limited_1.retryAfterMsOf)(json, response.headers)
59351
- });
59352
- }
59353
- if (json === null) {
59354
- throw new Error(`[${manager}] Keep at ${host} answered ${response.status} with a body that is not JSON`);
59355
- }
59356
- if (!response.ok || !json.success) {
59357
- const msg = json.error?.message ?? `${request.exchangeName} failed with status ${response.status}`;
59358
- if (response.status === 401 && request.refused) {
59359
- throw request.refused(msg);
59360
- }
59361
- throw new Error(`[${manager}] ${msg}`);
59498
+ return null;
59362
59499
  }
59363
- return json.data;
59364
59500
  }
59365
59501
  function hostOf(url) {
59366
59502
  try {
@@ -59604,9 +59740,8 @@ var require_service_token = __commonJS({
59604
59740
  async exchange() {
59605
59741
  const data = await (0, exchange_1.exchangeCredentials)({
59606
59742
  manager: "ServiceTokenManager",
59607
- url: `${this.keepApiUrl}/api/service-tokens`,
59608
- clientId: this.clientId,
59609
- clientSecret: this.clientSecret,
59743
+ keepApiUrl: this.keepApiUrl,
59744
+ send: (api) => api.post("/api/service-tokens", { body: { clientId: this.clientId, clientSecret: this.clientSecret } }),
59610
59745
  rateLimit: this.rateLimit,
59611
59746
  onResponse: this.onResponse,
59612
59747
  exchangeName: "Token exchange",
@@ -59677,9 +59812,8 @@ var require_agent_token = __commonJS({
59677
59812
  async exchange() {
59678
59813
  const data = await (0, exchange_1.exchangeCredentials)({
59679
59814
  manager: "AgentTokenManager",
59680
- url: `${this.keepApiUrl}/api/agent-tokens`,
59681
- clientId: this.clientId,
59682
- clientSecret: this.clientSecret,
59815
+ keepApiUrl: this.keepApiUrl,
59816
+ send: (api) => api.post("/api/agent-tokens", { body: { clientId: this.clientId, clientSecret: this.clientSecret } }),
59683
59817
  rateLimit: this.rateLimit,
59684
59818
  onResponse: this.onResponse,
59685
59819
  exchangeName: "Agent token exchange",
@@ -59700,7 +59834,7 @@ var require_dist4 = __commonJS({
59700
59834
  "use strict";
59701
59835
  Object.defineProperty(exports2, "__esModule", { value: true });
59702
59836
  exports2.waitForKeepToken = exports2.KeepCredentialsRefusedError = exports2.KeepRateLimitedError = exports2.KeepUnavailableError = exports2.AgentCredentialRevokedError = exports2.AgentTokenManager = exports2.ServiceTokenManager = exports2.KeepClient = void 0;
59703
- var client_1 = require_client();
59837
+ var client_1 = require_client2();
59704
59838
  Object.defineProperty(exports2, "KeepClient", { enumerable: true, get: function() {
59705
59839
  return client_1.KeepClient;
59706
59840
  } });
@@ -68558,13 +68692,28 @@ var require_typed_exit = __commonJS({
68558
68692
  }
68559
68693
  });
68560
68694
 
68695
+ // ../../packages/contracts/dist/services/jobs.js
68696
+ var require_jobs = __commonJS({
68697
+ "../../packages/contracts/dist/services/jobs.js"(exports2) {
68698
+ "use strict";
68699
+ Object.defineProperty(exports2, "__esModule", { value: true });
68700
+ exports2.service = void 0;
68701
+ exports2.createClient = createClient;
68702
+ var client_1 = require_client();
68703
+ exports2.service = "whittle-jobs";
68704
+ function createClient(options) {
68705
+ return (0, client_1.createContractClient)(options);
68706
+ }
68707
+ }
68708
+ });
68709
+
68561
68710
  // ../../packages/jobs/dist/response.js
68562
68711
  var require_response = __commonJS({
68563
68712
  "../../packages/jobs/dist/response.js"(exports2) {
68564
68713
  "use strict";
68565
68714
  Object.defineProperty(exports2, "__esModule", { value: true });
68566
68715
  exports2.NonJsonResponseError = exports2.ServiceUnavailableError = void 0;
68567
- exports2.readJsonResponse = readJsonResponse;
68716
+ exports2.refuseUnreadable = refuseUnreadable;
68568
68717
  var EXCERPT_LIMIT = 120;
68569
68718
  var UNAVAILABLE_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
68570
68719
  var UnreadableResponseError = class extends Error {
@@ -68618,31 +68767,31 @@ var require_response = __commonJS({
68618
68767
  return url;
68619
68768
  }
68620
68769
  }
68621
- async function readJsonResponse(response, url) {
68770
+ async function refuseUnreadable(response, url) {
68622
68771
  const contentType = response.headers.get("content-type");
68623
68772
  const unavailable = UNAVAILABLE_STATUSES.has(response.status);
68624
- if (unavailable || !isJson(contentType)) {
68625
- const body2 = await response.text().catch(() => "");
68626
- const detail = {
68627
- host: hostOf(url),
68628
- statusCode: response.status,
68629
- statusText: response.statusText ?? "",
68630
- contentType,
68631
- bodyExcerpt: excerpt(body2)
68632
- };
68633
- throw unavailable ? new ServiceUnavailableError(detail) : new NonJsonResponseError(detail);
68634
- }
68635
- return await response.json();
68773
+ if (!unavailable && isJson(contentType))
68774
+ return;
68775
+ const body2 = await response.text().catch(() => "");
68776
+ const detail = {
68777
+ host: hostOf(url),
68778
+ statusCode: response.status,
68779
+ statusText: response.statusText ?? "",
68780
+ contentType,
68781
+ bodyExcerpt: excerpt(body2)
68782
+ };
68783
+ throw unavailable ? new ServiceUnavailableError(detail) : new NonJsonResponseError(detail);
68636
68784
  }
68637
68785
  }
68638
68786
  });
68639
68787
 
68640
68788
  // ../../packages/jobs/dist/client.js
68641
- var require_client2 = __commonJS({
68789
+ var require_client3 = __commonJS({
68642
68790
  "../../packages/jobs/dist/client.js"(exports2) {
68643
68791
  "use strict";
68644
68792
  Object.defineProperty(exports2, "__esModule", { value: true });
68645
68793
  exports2.JobsClient = void 0;
68794
+ var jobs_1 = require_jobs();
68646
68795
  var response_1 = require_response();
68647
68796
  var JobsClient = class {
68648
68797
  baseUrl;
@@ -68682,19 +68831,26 @@ var require_client2 = __commonJS({
68682
68831
  * revoke-then-repair, clock skew) while the local expiry timer still thinks
68683
68832
  * it's valid. Force a re-mint and retry once before surfacing the failure.
68684
68833
  * `onResponse` (min-version contract) fires on the response we actually act
68685
- * on. `body` is a string, so it is safe to resend.
68834
+ * on. The contract client hands over its body as a string, so it is safe to resend.
68686
68835
  */
68687
- async fetchWithAuthRetry(method, url, body2) {
68688
- let response = await fetch(url, { method, headers: await this.buildHeaders(), body: body2 });
68836
+ async fetchWithAuthRetry(url, init2) {
68837
+ const send = async (forceRefresh) => {
68838
+ const headers = new Headers(init2.headers);
68839
+ for (const [name2, value] of Object.entries(await this.buildHeaders(forceRefresh)))
68840
+ headers.set(name2, value);
68841
+ return fetch(url, { ...init2, headers });
68842
+ };
68843
+ let response = await send(false);
68689
68844
  if (response.status === 401 && this.getAccessToken) {
68690
- response = await fetch(url, { method, headers: await this.buildHeaders(true), body: body2 });
68845
+ await response.body?.cancel().catch(() => void 0);
68846
+ response = await send(true);
68691
68847
  }
68692
68848
  this.onResponse?.(response);
68693
68849
  return response;
68694
68850
  }
68695
68851
  // ── Job Pools ──────────────────────────────────────────────
68696
68852
  async createJobPool(options) {
68697
- return this.request("POST", "/api/job-pools", options);
68853
+ return this.call((api) => api.post("/api/job-pools", { body: options }));
68698
68854
  }
68699
68855
  /**
68700
68856
  * Idempotent upsert by owner and name. When the
@@ -68702,7 +68858,7 @@ var require_client2 = __commonJS({
68702
68858
  * constraints. The Sift provisioning hook relies on this semantics.
68703
68859
  */
68704
68860
  async findOrCreateJobPool(options) {
68705
- return this.request("POST", "/api/job-pools?idempotent=true", options);
68861
+ return this.call((api) => api.post("/api/job-pools", { query: { idempotent: "true" }, body: options }));
68706
68862
  }
68707
68863
  /**
68708
68864
  * Every tenant's pools, which needs jobs:job-pools:read at operator context
@@ -68710,17 +68866,12 @@ var require_client2 = __commonJS({
68710
68866
  * one exact name; neither confers standing (BE-035).
68711
68867
  */
68712
68868
  async listJobPools(filters) {
68713
- const params = new URLSearchParams();
68714
- if (filters?.owner)
68715
- params.set("owner", `${filters.owner.principalClass}:${filters.owner.principalId}`);
68716
- if (filters?.name)
68717
- params.set("name", filters.name);
68718
- const qs = params.toString();
68719
- const { pools } = await this.request("GET", `/api/job-pools${qs ? `?${qs}` : ""}`);
68869
+ const owner = filters?.owner ? `${filters.owner.principalClass}:${filters.owner.principalId}` : void 0;
68870
+ const { pools } = await this.call((api) => api.get("/api/job-pools", { query: { owner, name: filters?.name } }));
68720
68871
  return pools;
68721
68872
  }
68722
68873
  async getJobPool(poolId) {
68723
- return this.request("GET", `/api/job-pools/${poolId}`);
68874
+ return this.call((api) => api.get("/api/job-pools/{id}", { path: { id: poolId } }));
68724
68875
  }
68725
68876
  /**
68726
68877
  * Resolve a pool by exact name. Names are unique per OWNER, not globally
@@ -68756,46 +68907,51 @@ var require_client2 = __commonJS({
68756
68907
  // A subscriber joins a pool by registering as a member of it. These
68757
68908
  // map to the server's `/api/job-pools/:poolId/members` routes.
68758
68909
  async listMembers(poolId) {
68759
- const { members } = await this.request("GET", `/api/job-pools/${poolId}/members`);
68910
+ const { members } = await this.call((api) => api.get("/api/job-pools/{poolId}/members", { path: { poolId } }));
68760
68911
  return members;
68761
68912
  }
68762
68913
  async registerMember(poolId, options) {
68763
- return this.request("POST", `/api/job-pools/${poolId}/members`, options);
68914
+ return this.call((api) => api.post("/api/job-pools/{poolId}/members", { path: { poolId }, body: options }));
68764
68915
  }
68765
68916
  async updateMember(poolId, memberId, options) {
68766
- return this.request("PUT", `/api/job-pools/${poolId}/members/${memberId}`, options);
68917
+ return this.call((api) => api.put("/api/job-pools/{poolId}/members/{mid}", { path: { poolId, mid: memberId }, body: options }));
68767
68918
  }
68768
68919
  // ── Jobs (producer) ──────────────────────────────────────────────
68769
68920
  /**
68770
68921
  * Enqueue a job into a job pool. The `inputs` are opaque to Jobs; a Shuttle
68771
68922
  * reads the strategy header inside them to decide how to run it (ADR 0005).
68923
+ * The body names its fields one by one, so nothing a caller spreads into
68924
+ * the options reaches the wire.
68772
68925
  */
68773
68926
  async enqueueJob(options) {
68774
68927
  const jobPoolId = await this.resolvePoolRef(options.pool);
68775
- const body2 = {
68776
- jobPoolId,
68777
- title: options.title,
68778
- progressChannel: options.progressChannel,
68779
- instructions: options.instructions,
68780
- maxAttempts: options.maxAttempts,
68781
- deadlineDuration: options.deadlineDuration,
68782
- claimDeadlineDuration: options.claimDeadlineDuration,
68783
- priority: options.priority,
68784
- tags: options.tags,
68785
- clientRef: options.clientRef,
68786
- clientContext: options.clientContext,
68787
- inputs: options.inputs
68788
- };
68789
- return this.request("POST", "/api/jobs", body2);
68928
+ return this.call((api) => api.post("/api/jobs", {
68929
+ body: {
68930
+ jobPoolId,
68931
+ title: options.title,
68932
+ progressChannel: options.progressChannel,
68933
+ instructions: options.instructions,
68934
+ maxAttempts: options.maxAttempts,
68935
+ deadlineDuration: options.deadlineDuration,
68936
+ claimDeadlineDuration: options.claimDeadlineDuration,
68937
+ priority: options.priority,
68938
+ tags: options.tags,
68939
+ clientRef: options.clientRef,
68940
+ clientContext: options.clientContext,
68941
+ inputs: options.inputs
68942
+ }
68943
+ }));
68790
68944
  }
68791
68945
  async getJob(jobId) {
68792
- return this.request("GET", `/api/jobs/${jobId}`);
68946
+ return this.call((api) => api.get("/api/jobs/{id}", { path: { id: jobId } }));
68793
68947
  }
68948
+ /** Answers the job without its inputs, as the route does. */
68794
68949
  async updateJob(jobId, options) {
68795
- return this.request("PATCH", `/api/jobs/${jobId}`, options);
68950
+ return this.call((api) => api.patch("/api/jobs/{id}", { path: { id: jobId }, body: options }));
68796
68951
  }
68952
+ /** Answers the job without its inputs, as the route does. */
68797
68953
  async cancelJob(jobId) {
68798
- return this.request("PUT", `/api/jobs/${jobId}`);
68954
+ return this.call((api) => api.put("/api/jobs/{id}", { path: { id: jobId } }));
68799
68955
  }
68800
68956
  /**
68801
68957
  * Permanently delete every job filed under `clientRefPrefix` (which must end
@@ -68803,14 +68959,14 @@ var require_client2 = __commonJS({
68803
68959
  * the caller needs `jobs:job-purges:write` at '*'. Idempotent.
68804
68960
  */
68805
68961
  async purgeJobs(clientRefPrefix) {
68806
- return this.request("POST", "/api/job-purges", { clientRefPrefix });
68962
+ return this.call((api) => api.post("/api/job-purges", { body: { clientRefPrefix } }));
68807
68963
  }
68808
68964
  // ── Outputs ──────────────────────────────────────────────────────
68809
68965
  async listOutputs(jobId) {
68810
68966
  const all = [];
68811
- const pageSize = 100;
68812
- for (let offset = 0; ; offset += pageSize) {
68813
- const { outputs, total } = await this.request("GET", `/api/jobs/${jobId}/output?limit=${pageSize}&offset=${offset}`);
68967
+ const limit = 100;
68968
+ for (let offset = 0; ; offset += limit) {
68969
+ const { outputs, total } = await this.call((api) => api.get("/api/jobs/{jobId}/output", { path: { jobId }, query: { limit, offset } }));
68814
68970
  all.push(...outputs);
68815
68971
  if (outputs.length === 0 || all.length >= total)
68816
68972
  break;
@@ -68818,7 +68974,7 @@ var require_client2 = __commonJS({
68818
68974
  return all;
68819
68975
  }
68820
68976
  async createOutput(jobId, options) {
68821
- return this.request("POST", `/api/jobs/${jobId}/output`, options);
68977
+ return this.call((api) => api.post("/api/jobs/{jobId}/output", { path: { jobId }, body: options }));
68822
68978
  }
68823
68979
  /**
68824
68980
  * Post a batch of live progress frames for a running job. Best-effort
@@ -68827,7 +68983,7 @@ var require_client2 = __commonJS({
68827
68983
  * let a dropped frame affect the job.
68828
68984
  */
68829
68985
  async postProgress(jobId, options) {
68830
- await this.request("POST", `/api/jobs/${jobId}/progress`, options);
68986
+ await this.call((api) => api.post("/api/jobs/{jobId}/progress", { path: { jobId }, body: options }));
68831
68987
  }
68832
68988
  // ── Attendance (subscriber dispatch lifecycle) ───────────────────
68833
68989
  /**
@@ -68836,7 +68992,10 @@ var require_client2 = __commonJS({
68836
68992
  * subscriber needs to execute and submit.
68837
68993
  */
68838
68994
  async acceptDispatch(jobId, options) {
68839
- return this.request("POST", `/api/jobs/${jobId}/attendance`, options?.shiftId ? { shiftId: options.shiftId } : void 0);
68995
+ return this.call((api) => api.post("/api/jobs/{jobId}/attendance", {
68996
+ path: { jobId },
68997
+ body: options?.shiftId ? { shiftId: options.shiftId } : void 0
68998
+ }));
68840
68999
  }
68841
69000
  // ── Shifts (whittlelabs ADR 0129) ────────────────────────────────
68842
69001
  /**
@@ -68844,39 +69003,33 @@ var require_client2 = __commonJS({
68844
69003
  * answers 404; the subscribe loop then claims without one.
68845
69004
  */
68846
69005
  async startShift() {
68847
- return this.request("POST", "/api/my/shifts");
69006
+ return this.call((api) => api.post("/api/my/shifts"));
68848
69007
  }
68849
69008
  /** End a shift, releasing every claim still taken on it back to the queue. */
68850
69009
  async endShift(shiftId) {
68851
- await this.request("DELETE", `/api/my/shifts/${shiftId}`);
69010
+ await this.call((api) => api.delete("/api/my/shifts/{shiftId}", { path: { shiftId } }));
68852
69011
  }
68853
69012
  async submit(jobId, options) {
68854
- return this.request("PUT", `/api/jobs/${jobId}/attendance`, options);
69013
+ return this.call((api) => api.put("/api/jobs/{jobId}/attendance", { path: { jobId }, body: options }));
68855
69014
  }
68856
69015
  // ── Discovery (rare) ─────────────────────────────────────────────
68857
69016
  async listDispatchableJobs(options) {
68858
- const params = new URLSearchParams();
68859
- if (options?.pools?.length)
68860
- params.set("pools", options.pools.join(","));
68861
- if (options?.tags?.length)
68862
- params.set("tags", options.tags.join(","));
68863
- if (options?.maxAttempts !== void 0)
68864
- params.set("maxAttempts", String(options.maxAttempts));
68865
- if (options?.minPriority !== void 0)
68866
- params.set("minPriority", String(options.minPriority));
68867
- if (options?.limit !== void 0)
68868
- params.set("limit", String(options.limit));
68869
- if (options?.offset !== void 0)
68870
- params.set("offset", String(options.offset));
68871
- if (options?.sort)
68872
- params.set("sort", options.sort);
68873
- if (options?.shift)
68874
- params.set("shift", options.shift);
68875
- const qs = params.toString();
68876
- const { jobs, total, directives, shift } = await this.request("GET", `/api/my/jobs${qs ? `?${qs}` : ""}`);
69017
+ const { jobs, total, directives, shift } = await this.call((api) => api.get("/api/my/jobs", {
69018
+ query: {
69019
+ pools: options?.pools?.length ? options.pools.join(",") : void 0,
69020
+ tags: options?.tags?.length ? options.tags.join(",") : void 0,
69021
+ maxAttempts: options?.maxAttempts === void 0 ? void 0 : String(options.maxAttempts),
69022
+ minPriority: options?.minPriority === void 0 ? void 0 : String(options.minPriority),
69023
+ limit: options?.limit,
69024
+ offset: options?.offset,
69025
+ sort: options?.sort || void 0,
69026
+ shift: options?.shift || void 0
69027
+ }
69028
+ }));
68877
69029
  return {
68878
69030
  data: jobs,
68879
69031
  pagination: { total, limit: options?.limit ?? 25, offset: options?.offset ?? 0 },
69032
+ // Absent from a Jobs that predates directives or shifts.
68880
69033
  directives: directives ?? null,
68881
69034
  shift: shift ?? null
68882
69035
  };
@@ -68890,38 +69043,47 @@ var require_client2 = __commonJS({
68890
69043
  async openHintStream(options = {}) {
68891
69044
  const qs = options.shift ? `?shift=${encodeURIComponent(options.shift)}` : "";
68892
69045
  const url = `${this.baseUrl}/api/my/jobs/stream${qs}`;
68893
- const headers = async (forceRefresh) => ({
68894
- ...await this.buildHeaders(forceRefresh),
68895
- Accept: "text/event-stream",
68896
- ...options.lastEventId ? { "Last-Event-ID": options.lastEventId } : {}
69046
+ return this.fetchWithAuthRetry(url, {
69047
+ headers: { Accept: "text/event-stream", ...options.lastEventId ? { "Last-Event-ID": options.lastEventId } : {} },
69048
+ signal: options.signal
68897
69049
  });
68898
- let response = await fetch(url, { headers: await headers(false), signal: options.signal });
68899
- if (response.status === 401 && this.getAccessToken) {
68900
- await response.body?.cancel().catch(() => void 0);
68901
- response = await fetch(url, { headers: await headers(true), signal: options.signal });
68902
- }
68903
- this.onResponse?.(response);
68904
- return response;
68905
69050
  }
68906
69051
  // ── HTTP plumbing ────────────────────────────────────────────────
68907
69052
  /**
68908
- * Every call's one reader. `readJsonResponse` refuses to parse a reply that
68909
- * is not the API's JSON — an ingress 502/503/504 while a pod rolls, or any
68910
- * non-JSON body — and throws a named failure instead of a parse error. What
68911
- * comes back here is the API's own envelope, success or not.
69053
+ * Every call's one path. The contract client builds the request and reads
69054
+ * the API's envelope; the transport under it authenticates (one retry on a
69055
+ * 401 for a token provider), fires `onResponse`, and refuses a reply that is
69056
+ * not the API's JSON with a named failure (`refuseUnreadable`: an ingress
69057
+ * 502/503/504 while a pod rolls, or any non-JSON body). A failure the
69058
+ * transport raises is rethrown as raised, so `ServiceUnavailableError` and
69059
+ * fetch's own `TypeError` reach the subscribe loop's outage handling rather
69060
+ * than arriving as a NETWORK_ERROR result. The API's own refusal throws an
69061
+ * `Error` carrying `code` and `statusCode`, as it always has.
68912
69062
  */
68913
- async request(method, path, body2) {
68914
- const url = `${this.baseUrl}${path}`;
68915
- const response = await this.fetchWithAuthRetry(method, url, body2 ? JSON.stringify(body2) : void 0);
68916
- const json = await (0, response_1.readJsonResponse)(response, url);
68917
- if (!response.ok || !json.success) {
68918
- const errorMessage = json.error?.message ?? `Request failed with status ${response.status}`;
68919
- const error = new Error(errorMessage);
68920
- error.code = json.error?.code;
68921
- error.statusCode = response.status;
68922
- throw error;
68923
- }
68924
- return json.data;
69063
+ async call(send) {
69064
+ let raised;
69065
+ const api = (0, jobs_1.createClient)({
69066
+ baseUrl: this.baseUrl,
69067
+ transport: async (url, init2) => {
69068
+ try {
69069
+ const response = await this.fetchWithAuthRetry(url, init2);
69070
+ await (0, response_1.refuseUnreadable)(response, url);
69071
+ return response;
69072
+ } catch (error2) {
69073
+ raised = { error: error2 };
69074
+ throw error2;
69075
+ }
69076
+ }
69077
+ });
69078
+ const result = await send(api);
69079
+ if (raised)
69080
+ throw raised.error;
69081
+ if (result.success)
69082
+ return result.data;
69083
+ const error = new Error(result.error.message);
69084
+ error.code = result.error.code;
69085
+ error.statusCode = result.error.status ?? void 0;
69086
+ throw error;
68925
69087
  }
68926
69088
  };
68927
69089
  exports2.JobsClient = JobsClient;
@@ -69804,7 +69966,7 @@ var require_dist7 = __commonJS({
69804
69966
  "use strict";
69805
69967
  Object.defineProperty(exports2, "__esModule", { value: true });
69806
69968
  exports2.subscribe = exports2.ServiceUnavailableError = exports2.NonJsonResponseError = exports2.JobsClient = void 0;
69807
- var client_1 = require_client2();
69969
+ var client_1 = require_client3();
69808
69970
  Object.defineProperty(exports2, "JobsClient", { enumerable: true, get: function() {
69809
69971
  return client_1.JobsClient;
69810
69972
  } });
@@ -76245,7 +76407,7 @@ var import_shuttle2 = __toESM(require_dist8());
76245
76407
 
76246
76408
  // src/brand.ts
76247
76409
  var import_shuttle = __toESM(require_dist8());
76248
- var buildVersion = true ? "0.43.2" : pkg.version;
76410
+ var buildVersion = true ? "0.43.4" : pkg.version;
76249
76411
  var AGENT_EXECUTORS = ["claude-code", "codex", "copilot"];
76250
76412
  var sifterBrand = {
76251
76413
  product: {