@whittlelabs/trivet 0.1.8 → 0.1.10

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 +125 -75
  2. package/bin.js.map +3 -3
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -59266,18 +59266,6 @@ var require_client2 = __commonJS({
59266
59266
  body: request
59267
59267
  }));
59268
59268
  }
59269
- /**
59270
- * Mint a pre-authorised device-code (pairing code) bound to an existing
59271
- * service identity. The Sift "Pair your Sifter" button calls this and
59272
- * shows `userCode` to the user.
59273
- *
59274
- * Requires `keep:service-identities:write`.
59275
- */
59276
- async createDeviceCodePreauth(request) {
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
- }));
59280
- }
59281
59269
  // ── Hosted Sifter ───────────────────────────────────────────────
59282
59270
  /**
59283
59271
  * Reveal a person's model-provider key for one hosted dispatch, with the
@@ -67953,6 +67941,10 @@ var require_store = __commonJS({
67953
67941
  await fsp.rename(tmp, this.path);
67954
67942
  await fsp.chmod(this.path, 384);
67955
67943
  }
67944
+ /** Forget the pairing: `revoke` of this machine, whose credential is dead. */
67945
+ async remove() {
67946
+ await fsp.rm(this.path, { force: true });
67947
+ }
67956
67948
  };
67957
67949
  exports2.PairingConfigStore = PairingConfigStore;
67958
67950
  }
@@ -75773,38 +75765,55 @@ var require_pairings = __commonJS({
75773
75765
  Object.defineProperty(exports2, "__esModule", { value: true });
75774
75766
  exports2.listPairings = listPairings;
75775
75767
  exports2.revokePairing = revokePairing;
75768
+ var keep_1 = require_keep();
75769
+ var keep_2 = require_dist4();
75770
+ var version_check_1 = require_version_check();
75771
+ var PAGE_LIMIT = 100;
75776
75772
  async function listPairings(opts) {
75777
- const fetcher = opts.fetcher ?? globalThis.fetch;
75778
- const url = `${opts.brand.keepRegistration.keepApiUrl.replace(/\/$/, "")}/api/me/service-identities?client_id=${encodeURIComponent(opts.brand.keepRegistration.oauthClientId)}`;
75779
- const response = await fetcher(url, {
75780
- method: "GET",
75781
- headers: { Authorization: `Bearer ${opts.accessToken}` }
75782
- });
75783
- return parseJson(response);
75773
+ const machines = [];
75774
+ for (let offset = 0; ; offset += PAGE_LIMIT) {
75775
+ const { pairings, total } = await ask(opts, (keep) => keep.get("/api/pairings", {
75776
+ query: { limit: PAGE_LIMIT, offset },
75777
+ headers: { Authorization: `Bearer ${opts.accessToken}` }
75778
+ }));
75779
+ machines.push(...pairings);
75780
+ if (pairings.length < PAGE_LIMIT || offset + pairings.length >= total)
75781
+ return machines;
75782
+ }
75784
75783
  }
75785
75784
  async function revokePairing(opts) {
75786
- const fetcher = opts.fetcher ?? globalThis.fetch;
75787
- const url = `${opts.brand.keepRegistration.keepApiUrl.replace(/\/$/, "")}/api/service-identities/${encodeURIComponent(opts.identityId)}`;
75788
- const response = await fetcher(url, {
75789
- method: "DELETE",
75785
+ await ask(opts, (keep) => keep.delete("/api/pairings/{pairingId}", {
75786
+ path: { pairingId: opts.pairingId },
75790
75787
  headers: { Authorization: `Bearer ${opts.accessToken}` }
75791
- });
75792
- if (!response.ok) {
75793
- throw new Error(`Revoke failed with status ${response.status}`);
75794
- }
75788
+ }));
75795
75789
  }
75796
- async function parseJson(response) {
75797
- let body2;
75798
- try {
75799
- body2 = await response.json();
75800
- } catch {
75801
- throw new Error(`Keep returned non-JSON (status ${response.status}).`);
75802
- }
75803
- if (!response.ok) {
75804
- const err2 = body2;
75805
- throw new Error(err2.error?.message ?? `Keep returned status ${response.status}.`);
75790
+ async function ask(opts, send) {
75791
+ const fetcher = opts.fetcher ?? globalThis.fetch;
75792
+ let belowFloor;
75793
+ const transport = async (url, init2) => {
75794
+ const response = await fetcher(url, init2);
75795
+ try {
75796
+ (0, version_check_1.checkResponseMinVersion)({
75797
+ response,
75798
+ productId: opts.brand.product.id,
75799
+ currentVersion: opts.brand.product.version,
75800
+ packageName: opts.brand.product.packageName
75801
+ });
75802
+ } catch (err2) {
75803
+ belowFloor = err2;
75804
+ throw err2;
75805
+ }
75806
+ return response;
75807
+ };
75808
+ const result = await send((0, keep_1.createClient)({ baseUrl: opts.keepApiUrl.replace(/\/$/, ""), transport }));
75809
+ if (belowFloor !== void 0)
75810
+ throw belowFloor;
75811
+ if (result.success)
75812
+ return result.data;
75813
+ if (result.error.status === 401) {
75814
+ throw new keep_2.AgentCredentialRevokedError(`Keep refused this machine's token (401): ${result.error.message}`);
75806
75815
  }
75807
- return body2.data;
75816
+ throw new Error(result.error.message || `Keep answered ${result.error.status ?? "nothing"}.`);
75808
75817
  }
75809
75818
  }
75810
75819
  });
@@ -75815,42 +75824,75 @@ var require_list = __commonJS({
75815
75824
  "use strict";
75816
75825
  Object.defineProperty(exports2, "__esModule", { value: true });
75817
75826
  exports2.buildListCommand = buildListCommand;
75827
+ exports2.mintAgentToken = mintAgentToken;
75828
+ exports2.renderPairings = renderPairings;
75818
75829
  var commander_1 = require_commander();
75819
75830
  var keep_1 = require_dist4();
75820
75831
  var apply_1 = require_apply();
75821
75832
  var store_1 = require_store();
75822
75833
  var pairings_1 = require_pairings();
75834
+ var version_check_1 = require_version_check();
75835
+ var typed_exit_1 = require_typed_exit();
75823
75836
  function buildListCommand(brand) {
75824
- return new commander_1.Command("list").description(`List paired ${brand.product.title} machines`).option("-p, --profile <name>", 'Config profile whose pairing credentials to use (default "default")').action(async (options) => {
75837
+ return new commander_1.Command("list").description(`List the machines paired to your account as a ${brand.product.title}`).option("-p, --profile <name>", 'Config profile whose pairing credentials to use (default "default")').option("--json", "Print the pairings as JSON and nothing else").action(async (options) => {
75825
75838
  try {
75826
75839
  const paths = (0, apply_1.resolvePaths)(brand, (0, apply_1.resolveProfile)(brand, options.profile));
75827
- const store = new store_1.PairingConfigStore(paths.configFile);
75828
- const pairing = store.read();
75829
- const tokens = new keep_1.ServiceTokenManager({
75840
+ const pairing = new store_1.PairingConfigStore(paths.configFile).read();
75841
+ const machines = await (0, pairings_1.listPairings)({
75842
+ brand,
75830
75843
  keepApiUrl: pairing.keepApiUrl,
75831
- clientId: pairing.credentials.clientId,
75832
- clientSecret: pairing.credentials.clientSecret
75844
+ accessToken: await mintAgentToken(pairing, brand)
75833
75845
  });
75834
- const accessToken = await tokens.getToken();
75835
- if (!accessToken) {
75836
- throw new Error("Failed to mint service token from stored credentials.");
75837
- }
75838
- const machines = await (0, pairings_1.listPairings)({ brand, accessToken });
75839
- if (machines.length === 0) {
75840
- console.log("No paired machines.");
75846
+ if (options.json) {
75847
+ console.log(JSON.stringify(machines, null, 2));
75841
75848
  return;
75842
75849
  }
75843
- console.log(["id", "name", "hostname", "pairedAt", "lastSeenAt", "active"].join(" "));
75844
- for (const m of machines) {
75845
- console.log([m.id, m.name, m.hostname ?? "-", m.pairedAt ?? "-", m.lastSeenAt ?? "-", m.active].join(" "));
75846
- }
75850
+ for (const line of renderPairings(machines, pairing.serviceIdentity.id))
75851
+ console.log(line);
75847
75852
  } catch (err2) {
75853
+ if ((0, typed_exit_1.isTypedExit)(err2))
75854
+ throw err2;
75848
75855
  const message = err2 instanceof Error ? err2.message : String(err2);
75849
75856
  console.error(`list failed: ${message}`);
75850
75857
  process.exit(1);
75851
75858
  }
75852
75859
  });
75853
75860
  }
75861
+ async function mintAgentToken(pairing, brand) {
75862
+ const token = await new keep_1.AgentTokenManager({
75863
+ keepApiUrl: pairing.keepApiUrl,
75864
+ clientId: pairing.credentials.clientId,
75865
+ clientSecret: pairing.credentials.clientSecret,
75866
+ onResponse: (response) => (0, version_check_1.checkResponseMinVersion)({
75867
+ response,
75868
+ productId: brand.product.id,
75869
+ currentVersion: brand.product.version,
75870
+ packageName: brand.product.packageName
75871
+ })
75872
+ }).getToken();
75873
+ if (!token)
75874
+ throw new Error("This pairing holds no credentials to mint a token with. Run `init` again.");
75875
+ return token;
75876
+ }
75877
+ function renderPairings(machines, self2) {
75878
+ if (machines.length === 0)
75879
+ return ["No paired machines."];
75880
+ const header = ["", "ID", "NAME", "HOST", "PAIRED", "LAST USED"];
75881
+ const rows = machines.map((m) => [
75882
+ m.id === self2 ? "*" : "",
75883
+ m.id,
75884
+ m.displayName,
75885
+ m.hostname ?? "-",
75886
+ m.pairedAt.slice(0, 10),
75887
+ m.lastUsedAt ? m.lastUsedAt.slice(0, 16).replace("T", " ") : "never"
75888
+ ]);
75889
+ const widths = header.map((h, i2) => Math.max(h.length, ...rows.map((r) => r[i2].length)));
75890
+ const line = (cells) => cells.map((cell, i2) => cell.padEnd(widths[i2])).join(" ").trimEnd();
75891
+ const lines = [line(header), ...rows.map(line)];
75892
+ if (machines.some((m) => m.id === self2))
75893
+ lines.push("", "* this machine");
75894
+ return lines;
75895
+ }
75854
75896
  }
75855
75897
  });
75856
75898
 
@@ -75860,43 +75902,51 @@ var require_revoke = __commonJS({
75860
75902
  "use strict";
75861
75903
  Object.defineProperty(exports2, "__esModule", { value: true });
75862
75904
  exports2.buildRevokeCommand = buildRevokeCommand;
75905
+ exports2.pickMachine = pickMachine;
75863
75906
  var commander_1 = require_commander();
75864
- var keep_1 = require_dist4();
75865
75907
  var apply_1 = require_apply();
75866
75908
  var store_1 = require_store();
75867
75909
  var pairings_1 = require_pairings();
75910
+ var list_1 = require_list();
75911
+ var typed_exit_1 = require_typed_exit();
75868
75912
  function buildRevokeCommand(brand) {
75869
- return new commander_1.Command("revoke").description(`Revoke a paired ${brand.product.title}`).argument("<nameOrId>", "Machine name or service-identity id to revoke").option("-p, --profile <name>", 'Config profile whose pairing credentials to use (default "default")').action(async (nameOrId, options) => {
75913
+ return new commander_1.Command("revoke").description(`Revoke a machine paired to your account as a ${brand.product.title} (this one included)`).argument("<machine>", "The machine to revoke: its id, the start of its id, its name, or its host, as `list` shows them").option("-p, --profile <name>", 'Config profile whose pairing credentials to use (default "default")').action(async (machine, options) => {
75870
75914
  try {
75871
75915
  const paths = (0, apply_1.resolvePaths)(brand, (0, apply_1.resolveProfile)(brand, options.profile));
75872
75916
  const store = new store_1.PairingConfigStore(paths.configFile);
75873
75917
  const pairing = store.read();
75874
- const tokens = new keep_1.ServiceTokenManager({
75875
- keepApiUrl: pairing.keepApiUrl,
75876
- clientId: pairing.credentials.clientId,
75877
- clientSecret: pairing.credentials.clientSecret
75878
- });
75879
- const accessToken = await tokens.getToken();
75880
- if (!accessToken) {
75881
- throw new Error("Failed to mint service token from stored credentials.");
75882
- }
75883
- const machines = await (0, pairings_1.listPairings)({ brand, accessToken });
75884
- const target = machines.find((m) => m.id === nameOrId || m.name === nameOrId || m.displayName === nameOrId);
75885
- if (!target) {
75886
- throw new Error(`No paired machine matches "${nameOrId}".`);
75887
- }
75918
+ const api = { brand, keepApiUrl: pairing.keepApiUrl, accessToken: await (0, list_1.mintAgentToken)(pairing, brand) };
75919
+ const target = pickMachine(await (0, pairings_1.listPairings)(api), machine);
75920
+ await (0, pairings_1.revokePairing)({ ...api, pairingId: target.id });
75888
75921
  if (target.id === pairing.serviceIdentity.id) {
75889
- throw new Error("Refusing to revoke the currently-paired identity. Run `init` on a different machine and revoke from there.");
75922
+ await store.remove();
75923
+ console.log(`Revoked this machine, "${target.displayName}" (${target.id}).`);
75924
+ console.log(`Its pairing is removed from ${paths.configFile}; run \`${brand.product.cliBinary} init\` to pair it again.`);
75925
+ return;
75890
75926
  }
75891
- await (0, pairings_1.revokePairing)({ brand, accessToken, identityId: target.id });
75892
- console.log(`Revoked ${target.name} (${target.id}).`);
75927
+ console.log(`Revoked "${target.displayName}" (${target.id}). It can take no more work.`);
75893
75928
  } catch (err2) {
75929
+ if ((0, typed_exit_1.isTypedExit)(err2))
75930
+ throw err2;
75894
75931
  const message = err2 instanceof Error ? err2.message : String(err2);
75895
75932
  console.error(`revoke failed: ${message}`);
75896
75933
  process.exit(1);
75897
75934
  }
75898
75935
  });
75899
75936
  }
75937
+ function pickMachine(machines, query) {
75938
+ const exact = machines.find((m) => m.id === query);
75939
+ if (exact)
75940
+ return exact;
75941
+ const candidates = machines.filter((m) => m.id.startsWith(query) || m.name === query || m.displayName === query || m.hostname === query);
75942
+ if (candidates.length === 1)
75943
+ return candidates[0];
75944
+ if (candidates.length === 0) {
75945
+ throw new Error(`No paired machine matches "${query}". Run \`list\` to see them.`);
75946
+ }
75947
+ const named = candidates.map((m) => ` ${m.id} ${m.displayName}${m.hostname ? ` (${m.hostname})` : ""}`);
75948
+ throw new Error([`"${query}" matches ${candidates.length} machines; name one by its id:`, ...named].join("\n"));
75949
+ }
75900
75950
  }
75901
75951
  });
75902
75952
 
@@ -76402,7 +76452,7 @@ var import_shuttle2 = __toESM(require_dist8());
76402
76452
 
76403
76453
  // src/brand.ts
76404
76454
  var import_shuttle = __toESM(require_dist8());
76405
- var buildVersion = true ? "0.1.8" : pkg.version;
76455
+ var buildVersion = true ? "0.1.10" : pkg.version;
76406
76456
  var EXECUTORS = ["claude-code"];
76407
76457
  var trivetBrand = {
76408
76458
  product: {