@whittlelabs/sifter 0.43.3 → 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 +168 -149
  2. package/bin.js.map +4 -4
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -59095,23 +59095,41 @@ var require_http_api = __commonJS({
59095
59095
  }
59096
59096
  });
59097
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
+
59098
59113
  // ../../packages/keep/dist/client.js
59099
59114
  var require_client2 = __commonJS({
59100
59115
  "../../packages/keep/dist/client.js"(exports2) {
59101
59116
  "use strict";
59102
59117
  Object.defineProperty(exports2, "__esModule", { value: true });
59103
59118
  exports2.KeepClient = void 0;
59119
+ var keep_1 = require_keep();
59104
59120
  var KeepClient = class {
59105
- baseUrl;
59106
- headers;
59121
+ api;
59107
59122
  constructor(config) {
59108
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
59109
- this.headers = { "Content-Type": "application/json" };
59110
- if (config.serviceToken) {
59111
- this.headers["Authorization"] = `Bearer ${config.serviceToken}`;
59112
- } else if (config.accessToken) {
59113
- this.headers["Authorization"] = `Bearer ${config.accessToken}`;
59114
- }
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
+ });
59115
59133
  }
59116
59134
  // ── Actor Tokens ────────────────────────────────────────────────
59117
59135
  /**
@@ -59120,7 +59138,7 @@ var require_client2 = __commonJS({
59120
59138
  * constructed with a service token holding `keep:actor-tokens:write`.
59121
59139
  */
59122
59140
  async mintActorToken(principal) {
59123
- return this.request("POST", "/api/actor-tokens", { principal });
59141
+ return answer(await this.api.post("/api/actor-tokens", { body: { principal } }));
59124
59142
  }
59125
59143
  // ── Identity ────────────────────────────────────────────────────
59126
59144
  /**
@@ -59129,101 +59147,98 @@ var require_client2 = __commonJS({
59129
59147
  * POST /api/challenges/:token/response to mint a session.
59130
59148
  */
59131
59149
  async createAccount(email, password, name2) {
59132
- const body2 = await this.envelope("POST", "/api/accounts", { email, password, name: name2 });
59133
- if (!body2.challenge) {
59134
- throw new Error("sign-up returned no challenge; the contract expects one");
59135
- }
59136
- return body2.challenge;
59150
+ const issued = answer(await this.api.post("/api/accounts", { body: { email, password, name: name2 } }));
59151
+ return issued.challenge;
59137
59152
  }
59138
59153
  /**
59139
59154
  * Sign in. Two legitimate outcomes, neither an error: a session, or a
59140
59155
  * challenge (MFA, unverified email). Both arrive on HTTP 200.
59141
59156
  */
59142
59157
  async createSession(email, password) {
59143
- const body2 = await this.envelope("POST", "/api/sessions", { email, password });
59144
- if (body2.challenge) {
59145
- return { kind: "challenge", challenge: body2.challenge };
59146
- }
59147
- const data = body2.data;
59148
- if (!data?.session) {
59149
- throw new Error("sign-in returned neither a session nor a challenge");
59150
- }
59151
- return { kind: "session", user: data.user, session: data.session };
59158
+ return signedIn(answer(await this.api.post("/api/sessions", { body: { email, password } })));
59152
59159
  }
59153
- /** 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
+ */
59154
59164
  async respondToChallenge(challengeToken, type, response) {
59155
- 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
+ })));
59156
59169
  }
59170
+ /** End the session the refresh token belongs to. */
59157
59171
  async destroySession(refreshToken) {
59158
- await this.request("DELETE", "/api/sessions", refreshToken ? { refreshToken } : void 0);
59172
+ answer(await this.api.delete("/api/sessions", { body: { refreshToken } }));
59159
59173
  }
59160
59174
  async refreshSession(refreshToken) {
59161
- return this.request("POST", "/api/sessions/renewal", { refreshToken });
59175
+ return answer(await this.api.post("/api/sessions/renewal", { body: { refreshToken } })).session;
59162
59176
  }
59163
59177
  async getCurrentUser() {
59164
- return this.request("GET", "/api/accounts/me");
59178
+ return answer(await this.api.get("/api/accounts/me")).user;
59165
59179
  }
59166
59180
  async updateProfile(updates) {
59167
- return this.request("PATCH", "/api/accounts/me", updates);
59181
+ return answer(await this.api.patch("/api/accounts/me", { body: updates })).user;
59168
59182
  }
59169
59183
  async confirmEmail(email, code) {
59170
- await this.request("POST", "/api/email-confirmations", { email, code });
59184
+ answer(await this.api.post("/api/email-confirmations", { body: { email, code } }));
59171
59185
  }
59172
59186
  async requestVerificationCode(email) {
59173
- await this.request("POST", "/api/verification-codes", { email });
59187
+ answer(await this.api.post("/api/verification-codes", { body: { email } }));
59174
59188
  }
59175
59189
  async requestPasswordReset(email) {
59176
- await this.request("POST", "/api/password-resets", { email });
59190
+ answer(await this.api.post("/api/password-resets", { body: { email } }));
59177
59191
  }
59178
59192
  async resetPassword(email, code, newPassword) {
59179
- 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 } }));
59180
59194
  }
59181
59195
  async changePassword(currentPassword, newPassword) {
59182
- await this.request("PUT", "/api/passwords", { currentPassword, newPassword });
59196
+ answer(await this.api.put("/api/passwords", { body: { currentPassword, newPassword } }));
59183
59197
  }
59184
59198
  // ── Organizations ───────────────────────────────────────────────
59185
59199
  async listOrganizations() {
59186
- const { organizations } = await this.request("GET", "/api/orgs");
59187
- return organizations;
59200
+ return answer(await this.api.get("/api/orgs")).organizations;
59188
59201
  }
59189
59202
  async getOrganization(orgId) {
59190
- return this.request("GET", `/api/orgs/${orgId}`);
59203
+ return answer(await this.api.get("/api/orgs/{orgId}", { path: { orgId } }));
59191
59204
  }
59192
59205
  async createOrganization(name2, description) {
59193
- return this.request("POST", "/api/orgs", { name: name2, description });
59206
+ return answer(await this.api.post("/api/orgs", { body: { name: name2, description } }));
59194
59207
  }
59195
59208
  async updateOrganization(orgId, updates) {
59196
- return this.request("PATCH", `/api/orgs/${orgId}`, updates);
59209
+ return answer(await this.api.patch("/api/orgs/{orgId}", { path: { orgId }, body: updates }));
59197
59210
  }
59198
59211
  async deleteOrganization(orgId) {
59199
- await this.request("DELETE", `/api/orgs/${orgId}`);
59212
+ answer(await this.api.delete("/api/orgs/{orgId}", { path: { orgId } }));
59200
59213
  }
59201
59214
  async listMembers(orgId) {
59202
- return this.request("GET", `/api/orgs/${orgId}/members`);
59215
+ return answer(await this.api.get("/api/orgs/{orgId}/members", { path: { orgId } }));
59203
59216
  }
59204
59217
  async removeMember(orgId, userId) {
59205
- await this.request("DELETE", `/api/orgs/${orgId}/members/${userId}`);
59218
+ answer(await this.api.delete("/api/orgs/{orgId}/members/{userId}", { path: { orgId, userId } }));
59206
59219
  }
59207
59220
  async listTeams(orgId) {
59208
- return this.request("GET", `/api/orgs/${orgId}/teams`);
59221
+ return answer(await this.api.get("/api/orgs/{orgId}/teams", { path: { orgId } }));
59209
59222
  }
59210
59223
  async createTeam(orgId, name2, description) {
59211
- 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 } }));
59212
59225
  }
59213
59226
  async listTeamMembers(orgId, teamId) {
59214
- 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 } }));
59215
59228
  }
59216
59229
  async addTeamMember(orgId, teamId, userId) {
59217
- 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 } }));
59218
59231
  }
59219
- async createInvitation(orgId, email, roleGrants) {
59220
- 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 }));
59221
59235
  }
59236
+ /** The invitation behind a token, with the organization it is to. */
59222
59237
  async getInvitation(token) {
59223
- return this.request("GET", `/api/invitations/${token}`);
59238
+ return answer(await this.api.get("/api/invitations/{token}", { path: { token } }));
59224
59239
  }
59225
59240
  async acceptInvitation(token) {
59226
- await this.request("POST", `/api/invitations/${token}/acceptance`);
59241
+ answer(await this.api.post("/api/invitations/{token}/acceptance", { path: { token } }));
59227
59242
  }
59228
59243
  // ── Substrate Provisioning ──────────────────────────────────────
59229
59244
  /**
@@ -59234,7 +59249,10 @@ var require_client2 = __commonJS({
59234
59249
  * Requires `keep:service-identities:write`.
59235
59250
  */
59236
59251
  async findOrCreateServiceIdentity(request) {
59237
- 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
+ }));
59238
59256
  }
59239
59257
  /**
59240
59258
  * Bind a role (by name) to a service identity, held at a structural
@@ -59243,7 +59261,10 @@ var require_client2 = __commonJS({
59243
59261
  * Requires `keep:role-bindings:write`.
59244
59262
  */
59245
59263
  async bindServiceIdentityRole(serviceIdentityId, request) {
59246
- 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
+ }));
59247
59268
  }
59248
59269
  /**
59249
59270
  * Mint a pre-authorised device-code (pairing code) bound to an existing
@@ -59253,11 +59274,9 @@ var require_client2 = __commonJS({
59253
59274
  * Requires `keep:service-identities:write`.
59254
59275
  */
59255
59276
  async createDeviceCodePreauth(request) {
59256
- return this.request("POST", "/api/oauth/device-code/preauth", {
59257
- client_id: request.clientId,
59258
- boundAgentId: request.boundAgentId,
59259
- ttlSeconds: request.ttlSeconds
59260
- });
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
+ }));
59261
59280
  }
59262
59281
  // ── Hosted Sifter ───────────────────────────────────────────────
59263
59282
  /**
@@ -59270,7 +59289,10 @@ var require_client2 = __commonJS({
59270
59289
  * Use the credential for the dispatch and drop it from memory afterwards.
59271
59290
  */
59272
59291
  async revealModelCredential(userId, options = {}) {
59273
- 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
+ }));
59274
59296
  }
59275
59297
  // ── GitHub OAuth token ──────────────────────────────────────────
59276
59298
  /**
@@ -59281,50 +59303,23 @@ var require_client2 = __commonJS({
59281
59303
  * NotFound) when the user has not connected GitHub.
59282
59304
  */
59283
59305
  async getUserGithubToken(userId) {
59284
- return this.request("GET", `/api/users/${encodeURIComponent(userId)}/github-token`);
59285
- }
59286
- // ── HTTP ────────────────────────────────────────────────────────
59287
- /**
59288
- * Reads the response envelope WITHOUT treating `success: false` as a
59289
- * failure. Sign-in and sign-up use it because a challenge arrives on a 2xx
59290
- * with success:false and no error object — `request` would raise a
59291
- * misleading "Request failed with status 200" and give the caller no way to
59292
- * tell MFA from a bad password (whittlelabs#530). A real error still throws.
59293
- */
59294
- async envelope(method, path, body2) {
59295
- const response = await fetch(`${this.baseUrl}${path}`, {
59296
- method,
59297
- headers: this.headers,
59298
- body: body2 ? JSON.stringify(body2) : void 0
59299
- });
59300
- const json = await response.json();
59301
- if (!response.ok || !json.success && !json.challenge) {
59302
- const error = new Error(json.error?.message ?? `Request failed with status ${response.status}`);
59303
- error.code = json.error?.code;
59304
- error.statusCode = response.status;
59305
- throw error;
59306
- }
59307
- return json;
59308
- }
59309
- async request(method, path, body2) {
59310
- const url = `${this.baseUrl}${path}`;
59311
- const response = await fetch(url, {
59312
- method,
59313
- headers: this.headers,
59314
- body: body2 ? JSON.stringify(body2) : void 0
59315
- });
59316
- const json = await response.json();
59317
- if (!response.ok || !json.success) {
59318
- const errorMessage = json.error?.message ?? `Request failed with status ${response.status}`;
59319
- const error = new Error(errorMessage);
59320
- error.code = json.error?.code;
59321
- error.statusCode = response.status;
59322
- throw error;
59323
- }
59324
- return json.data;
59306
+ return answer(await this.api.get("/api/users/{userId}/github-token", { path: { userId } }));
59325
59307
  }
59326
59308
  };
59327
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
+ }
59328
59323
  }
59329
59324
  });
59330
59325
 
@@ -59414,6 +59409,7 @@ var require_exchange = __commonJS({
59414
59409
  exports2.KeepUnavailableError = void 0;
59415
59410
  exports2.exchangeCredentials = exchangeCredentials;
59416
59411
  exports2.hostOf = hostOf;
59412
+ var keep_1 = require_keep();
59417
59413
  var rate_limited_1 = require_rate_limited();
59418
59414
  var KeepUnavailableError = class extends Error {
59419
59415
  code = "SERVICE_UNAVAILABLE";
@@ -59430,52 +59426,77 @@ var require_exchange = __commonJS({
59430
59426
  };
59431
59427
  exports2.KeepUnavailableError = KeepUnavailableError;
59432
59428
  async function exchangeCredentials(request) {
59433
- const { manager, url } = request;
59434
- const host = hostOf(url);
59435
- let response;
59436
- try {
59437
- response = await fetch(url, {
59438
- method: "POST",
59439
- headers: { "Content-Type": "application/json" },
59440
- body: JSON.stringify({
59441
- clientId: request.clientId,
59442
- clientSecret: request.clientSecret
59443
- })
59444
- });
59445
- } catch (err2) {
59446
- 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);
59447
59490
  }
59448
- request.onResponse?.(response);
59449
- const text = await response.text().catch(() => "");
59450
- let json;
59491
+ throw new Error(`[${manager}] ${msg}`);
59492
+ }
59493
+ function parsedEnvelope(text) {
59451
59494
  try {
59452
- json = JSON.parse(text);
59495
+ const parsed = JSON.parse(text);
59496
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
59453
59497
  } catch {
59454
- json = null;
59455
- }
59456
- if (response.status >= 500) {
59457
- const reason = json?.error?.message ?? `${request.exchangeName} failed with status ${response.status}`;
59458
- throw new KeepUnavailableError(`[${manager}] Keep at ${host} is unavailable (${response.status}): ${reason}`, { host, statusCode: response.status });
59459
- }
59460
- if (response.status === 429) {
59461
- throw request.rateLimit.close({
59462
- manager,
59463
- host,
59464
- reason: json?.error?.message ?? "Too Many Requests",
59465
- retryAfterMs: (0, rate_limited_1.retryAfterMsOf)(json, response.headers)
59466
- });
59467
- }
59468
- if (json === null) {
59469
- throw new Error(`[${manager}] Keep at ${host} answered ${response.status} with a body that is not JSON`);
59470
- }
59471
- if (!response.ok || !json.success) {
59472
- const msg = json.error?.message ?? `${request.exchangeName} failed with status ${response.status}`;
59473
- if (response.status === 401 && request.refused) {
59474
- throw request.refused(msg);
59475
- }
59476
- throw new Error(`[${manager}] ${msg}`);
59498
+ return null;
59477
59499
  }
59478
- return json.data;
59479
59500
  }
59480
59501
  function hostOf(url) {
59481
59502
  try {
@@ -59719,9 +59740,8 @@ var require_service_token = __commonJS({
59719
59740
  async exchange() {
59720
59741
  const data = await (0, exchange_1.exchangeCredentials)({
59721
59742
  manager: "ServiceTokenManager",
59722
- url: `${this.keepApiUrl}/api/service-tokens`,
59723
- clientId: this.clientId,
59724
- clientSecret: this.clientSecret,
59743
+ keepApiUrl: this.keepApiUrl,
59744
+ send: (api) => api.post("/api/service-tokens", { body: { clientId: this.clientId, clientSecret: this.clientSecret } }),
59725
59745
  rateLimit: this.rateLimit,
59726
59746
  onResponse: this.onResponse,
59727
59747
  exchangeName: "Token exchange",
@@ -59792,9 +59812,8 @@ var require_agent_token = __commonJS({
59792
59812
  async exchange() {
59793
59813
  const data = await (0, exchange_1.exchangeCredentials)({
59794
59814
  manager: "AgentTokenManager",
59795
- url: `${this.keepApiUrl}/api/agent-tokens`,
59796
- clientId: this.clientId,
59797
- clientSecret: this.clientSecret,
59815
+ keepApiUrl: this.keepApiUrl,
59816
+ send: (api) => api.post("/api/agent-tokens", { body: { clientId: this.clientId, clientSecret: this.clientSecret } }),
59798
59817
  rateLimit: this.rateLimit,
59799
59818
  onResponse: this.onResponse,
59800
59819
  exchangeName: "Agent token exchange",
@@ -76388,7 +76407,7 @@ var import_shuttle2 = __toESM(require_dist8());
76388
76407
 
76389
76408
  // src/brand.ts
76390
76409
  var import_shuttle = __toESM(require_dist8());
76391
- var buildVersion = true ? "0.43.3" : pkg.version;
76410
+ var buildVersion = true ? "0.43.4" : pkg.version;
76392
76411
  var AGENT_EXECUTORS = ["claude-code", "codex", "copilot"];
76393
76412
  var sifterBrand = {
76394
76413
  product: {