agent-shim 8.21.0 → 8.22.0

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/dist/index.cjs CHANGED
@@ -10577,6 +10577,17 @@ function bearerOf(headers) {
10577
10577
  }
10578
10578
  return presented.slice(0, "bearer ".length).toLowerCase() === "bearer " ? presented.slice("bearer ".length) : void 0;
10579
10579
  }
10580
+ function presentationRefusal(headers) {
10581
+ const presented = singleHeader2(headers, "authorization");
10582
+ if (presented === void 0) {
10583
+ return "absent";
10584
+ }
10585
+ if (presented.slice(0, "bearer ".length).toLowerCase() !== "bearer ") {
10586
+ return "not-a-bearer";
10587
+ }
10588
+ const bearer = presented.slice("bearer ".length);
10589
+ return bearer.slice(0, "bearer ".length).toLowerCase() === "bearer " ? "doubled-scheme-word" : "unrecognised";
10590
+ }
10580
10591
  async function readBody2(request4, capBytes) {
10581
10592
  return await new Promise((resolve, reject) => {
10582
10593
  const chunks = [];
@@ -10665,6 +10676,9 @@ function createRcSelfHostSurface(deps) {
10665
10676
  }
10666
10677
  return false;
10667
10678
  };
10679
+ const credentialRefusal = (headers) => presentsCredential(headers) ? void 0 : presentationRefusal(headers);
10680
+ const workerRefusal = (headers, session) => presentsWorkerJwt(headers, session) ? void 0 : presentationRefusal(headers);
10681
+ const workerOrCredentialRefusal = (headers, session) => presentsWorkerJwt(headers, session) || presentsCredential(headers) ? void 0 : presentationRefusal(headers);
10668
10682
  const frameOf = (event) => {
10669
10683
  const envelope = {
10670
10684
  event_id: event.eventId,
@@ -10753,8 +10767,14 @@ data: ${JSON.stringify(envelope)}
10753
10767
  const epochStale = async (response) => {
10754
10768
  await answerJson2(response, HTTP_CONFLICT, { type: "error", error: { type: "api_error", message: "worker_epoch does not match the session's live epoch" } }, { [CONFLICT_REASON_HEADER2]: CONFLICT_REASON_EPOCH_STALE });
10755
10769
  };
10756
- const unauthorized = async (response) => {
10757
- await answerJson2(response, HTTP_STATUS.unauthorized, { type: "error", error: { type: "authentication_error", message: "the self-hosted Remote Control surface did not recognise the presented credential" } });
10770
+ const refusalMessages = {
10771
+ absent: "the self-hosted Remote Control surface authenticates by the Authorization header, and this call carried none",
10772
+ "not-a-bearer": "the self-hosted Remote Control surface reads a Bearer authorization, and this call's Authorization header did not carry the Bearer scheme word",
10773
+ "doubled-scheme-word": "the self-hosted Remote Control surface reads one Bearer scheme word, and this call's Authorization header carried it twice: a persisted session record holds the whole header value with the scheme word included, so prefixing Bearer onto it presents a different credential",
10774
+ unrecognised: "the self-hosted Remote Control surface did not recognise the presented credential"
10775
+ };
10776
+ const unauthorized = async (response, refusal) => {
10777
+ await answerJson2(response, HTTP_STATUS.unauthorized, { type: "error", error: { type: "authentication_error", message: refusalMessages[refusal] } });
10758
10778
  };
10759
10779
  const serveSessionPath = async (session, tail, request4, response) => {
10760
10780
  const method = request4.method.toUpperCase();
@@ -10772,8 +10792,9 @@ data: ${JSON.stringify(envelope)}
10772
10792
  return;
10773
10793
  }
10774
10794
  if (tail === "bridge" && method === "POST") {
10775
- if (!presentsCredential(request4.headers)) {
10776
- await unauthorized(response);
10795
+ const refusal = credentialRefusal(request4.headers);
10796
+ if (refusal !== void 0) {
10797
+ await unauthorized(response, refusal);
10777
10798
  return;
10778
10799
  }
10779
10800
  const workerJwt = mintWorkerJwt(deps.randomToken, deps.now());
@@ -10783,8 +10804,9 @@ data: ${JSON.stringify(envelope)}
10783
10804
  return;
10784
10805
  }
10785
10806
  if (tail === "archive" && method === "POST") {
10786
- if (!presentsCredential(request4.headers)) {
10787
- await unauthorized(response);
10807
+ const refusal = credentialRefusal(request4.headers);
10808
+ if (refusal !== void 0) {
10809
+ await unauthorized(response, refusal);
10788
10810
  return;
10789
10811
  }
10790
10812
  for (const sink of [...session.workerStreams, ...session.clientStreams]) {
@@ -10798,8 +10820,9 @@ data: ${JSON.stringify(envelope)}
10798
10820
  if (tail === "worker" || tail.startsWith("worker/")) {
10799
10821
  const workerTail = tail === "worker" ? "" : tail.slice("worker/".length);
10800
10822
  const webProxy = workerTail === "web-fetch" || workerTail === "web-search";
10801
- if (!(webProxy ? presentsCredential(request4.headers) || presentsWorkerJwt(request4.headers, session) : presentsWorkerJwt(request4.headers, session))) {
10802
- await unauthorized(response);
10823
+ const refusal = webProxy ? workerOrCredentialRefusal(request4.headers, session) : workerRefusal(request4.headers, session);
10824
+ if (refusal !== void 0) {
10825
+ await unauthorized(response, refusal);
10803
10826
  return;
10804
10827
  }
10805
10828
  if (workerTail === "" && method === "GET") {
@@ -10955,8 +10978,9 @@ data: ${JSON.stringify(envelope)}
10955
10978
  await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no worker path "${workerTail}"` } });
10956
10979
  return;
10957
10980
  }
10958
- if (!presentsCredential(request4.headers)) {
10959
- await unauthorized(response);
10981
+ const clientRefusal = credentialRefusal(request4.headers);
10982
+ if (clientRefusal !== void 0) {
10983
+ await unauthorized(response, clientRefusal);
10960
10984
  return;
10961
10985
  }
10962
10986
  if (tail === "events" && method === "POST") {
@@ -11021,8 +11045,9 @@ data: ${JSON.stringify(envelope)}
11021
11045
  await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
11022
11046
  return;
11023
11047
  }
11024
- if (!presentsCredential(request4.headers)) {
11025
- await unauthorized(response);
11048
+ const listRefusal = credentialRefusal(request4.headers);
11049
+ if (listRefusal !== void 0) {
11050
+ await unauthorized(response, listRefusal);
11026
11051
  return;
11027
11052
  }
11028
11053
  await answerJson2(response, HTTP_STATUS.ok, { data: [] });
@@ -11034,8 +11059,9 @@ data: ${JSON.stringify(envelope)}
11034
11059
  }
11035
11060
  if (pathname === RC_SESSIONS_PATH_PREFIX2) {
11036
11061
  if (method === "POST") {
11037
- if (!presentsCredential(request4.headers)) {
11038
- await unauthorized(response);
11062
+ const createRefusal = credentialRefusal(request4.headers);
11063
+ if (createRefusal !== void 0) {
11064
+ await unauthorized(response, createRefusal);
11039
11065
  return;
11040
11066
  }
11041
11067
  const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
@@ -11060,8 +11086,9 @@ data: ${JSON.stringify(envelope)}
11060
11086
  return;
11061
11087
  }
11062
11088
  if (method === "GET") {
11063
- if (!presentsCredential(request4.headers)) {
11064
- await unauthorized(response);
11089
+ const readRefusal = credentialRefusal(request4.headers);
11090
+ if (readRefusal !== void 0) {
11091
+ await unauthorized(response, readRefusal);
11065
11092
  return;
11066
11093
  }
11067
11094
  await answerJson2(response, HTTP_STATUS.ok, { data: [...sessions.values()].map((session) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), updated_at: new Date(session.lastTrafficAt).toISOString(), session_status: "active" })) });
package/dist/index.mjs CHANGED
@@ -10305,6 +10305,17 @@ function bearerOf(headers) {
10305
10305
  }
10306
10306
  return presented.slice(0, "bearer ".length).toLowerCase() === "bearer " ? presented.slice("bearer ".length) : void 0;
10307
10307
  }
10308
+ function presentationRefusal(headers) {
10309
+ const presented = singleHeader2(headers, "authorization");
10310
+ if (presented === void 0) {
10311
+ return "absent";
10312
+ }
10313
+ if (presented.slice(0, "bearer ".length).toLowerCase() !== "bearer ") {
10314
+ return "not-a-bearer";
10315
+ }
10316
+ const bearer = presented.slice("bearer ".length);
10317
+ return bearer.slice(0, "bearer ".length).toLowerCase() === "bearer " ? "doubled-scheme-word" : "unrecognised";
10318
+ }
10308
10319
  async function readBody2(request4, capBytes) {
10309
10320
  return await new Promise((resolve, reject) => {
10310
10321
  const chunks = [];
@@ -10393,6 +10404,9 @@ function createRcSelfHostSurface(deps) {
10393
10404
  }
10394
10405
  return false;
10395
10406
  };
10407
+ const credentialRefusal = (headers) => presentsCredential(headers) ? void 0 : presentationRefusal(headers);
10408
+ const workerRefusal = (headers, session) => presentsWorkerJwt(headers, session) ? void 0 : presentationRefusal(headers);
10409
+ const workerOrCredentialRefusal = (headers, session) => presentsWorkerJwt(headers, session) || presentsCredential(headers) ? void 0 : presentationRefusal(headers);
10396
10410
  const frameOf = (event) => {
10397
10411
  const envelope = {
10398
10412
  event_id: event.eventId,
@@ -10481,8 +10495,14 @@ data: ${JSON.stringify(envelope)}
10481
10495
  const epochStale = async (response) => {
10482
10496
  await answerJson2(response, HTTP_CONFLICT, { type: "error", error: { type: "api_error", message: "worker_epoch does not match the session's live epoch" } }, { [CONFLICT_REASON_HEADER2]: CONFLICT_REASON_EPOCH_STALE });
10483
10497
  };
10484
- const unauthorized = async (response) => {
10485
- await answerJson2(response, HTTP_STATUS.unauthorized, { type: "error", error: { type: "authentication_error", message: "the self-hosted Remote Control surface did not recognise the presented credential" } });
10498
+ const refusalMessages = {
10499
+ absent: "the self-hosted Remote Control surface authenticates by the Authorization header, and this call carried none",
10500
+ "not-a-bearer": "the self-hosted Remote Control surface reads a Bearer authorization, and this call's Authorization header did not carry the Bearer scheme word",
10501
+ "doubled-scheme-word": "the self-hosted Remote Control surface reads one Bearer scheme word, and this call's Authorization header carried it twice: a persisted session record holds the whole header value with the scheme word included, so prefixing Bearer onto it presents a different credential",
10502
+ unrecognised: "the self-hosted Remote Control surface did not recognise the presented credential"
10503
+ };
10504
+ const unauthorized = async (response, refusal) => {
10505
+ await answerJson2(response, HTTP_STATUS.unauthorized, { type: "error", error: { type: "authentication_error", message: refusalMessages[refusal] } });
10486
10506
  };
10487
10507
  const serveSessionPath = async (session, tail, request4, response) => {
10488
10508
  const method = request4.method.toUpperCase();
@@ -10500,8 +10520,9 @@ data: ${JSON.stringify(envelope)}
10500
10520
  return;
10501
10521
  }
10502
10522
  if (tail === "bridge" && method === "POST") {
10503
- if (!presentsCredential(request4.headers)) {
10504
- await unauthorized(response);
10523
+ const refusal = credentialRefusal(request4.headers);
10524
+ if (refusal !== void 0) {
10525
+ await unauthorized(response, refusal);
10505
10526
  return;
10506
10527
  }
10507
10528
  const workerJwt = mintWorkerJwt(deps.randomToken, deps.now());
@@ -10511,8 +10532,9 @@ data: ${JSON.stringify(envelope)}
10511
10532
  return;
10512
10533
  }
10513
10534
  if (tail === "archive" && method === "POST") {
10514
- if (!presentsCredential(request4.headers)) {
10515
- await unauthorized(response);
10535
+ const refusal = credentialRefusal(request4.headers);
10536
+ if (refusal !== void 0) {
10537
+ await unauthorized(response, refusal);
10516
10538
  return;
10517
10539
  }
10518
10540
  for (const sink of [...session.workerStreams, ...session.clientStreams]) {
@@ -10526,8 +10548,9 @@ data: ${JSON.stringify(envelope)}
10526
10548
  if (tail === "worker" || tail.startsWith("worker/")) {
10527
10549
  const workerTail = tail === "worker" ? "" : tail.slice("worker/".length);
10528
10550
  const webProxy = workerTail === "web-fetch" || workerTail === "web-search";
10529
- if (!(webProxy ? presentsCredential(request4.headers) || presentsWorkerJwt(request4.headers, session) : presentsWorkerJwt(request4.headers, session))) {
10530
- await unauthorized(response);
10551
+ const refusal = webProxy ? workerOrCredentialRefusal(request4.headers, session) : workerRefusal(request4.headers, session);
10552
+ if (refusal !== void 0) {
10553
+ await unauthorized(response, refusal);
10531
10554
  return;
10532
10555
  }
10533
10556
  if (workerTail === "" && method === "GET") {
@@ -10683,8 +10706,9 @@ data: ${JSON.stringify(envelope)}
10683
10706
  await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no worker path "${workerTail}"` } });
10684
10707
  return;
10685
10708
  }
10686
- if (!presentsCredential(request4.headers)) {
10687
- await unauthorized(response);
10709
+ const clientRefusal = credentialRefusal(request4.headers);
10710
+ if (clientRefusal !== void 0) {
10711
+ await unauthorized(response, clientRefusal);
10688
10712
  return;
10689
10713
  }
10690
10714
  if (tail === "events" && method === "POST") {
@@ -10749,8 +10773,9 @@ data: ${JSON.stringify(envelope)}
10749
10773
  await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
10750
10774
  return;
10751
10775
  }
10752
- if (!presentsCredential(request4.headers)) {
10753
- await unauthorized(response);
10776
+ const listRefusal = credentialRefusal(request4.headers);
10777
+ if (listRefusal !== void 0) {
10778
+ await unauthorized(response, listRefusal);
10754
10779
  return;
10755
10780
  }
10756
10781
  await answerJson2(response, HTTP_STATUS.ok, { data: [] });
@@ -10762,8 +10787,9 @@ data: ${JSON.stringify(envelope)}
10762
10787
  }
10763
10788
  if (pathname === RC_SESSIONS_PATH_PREFIX2) {
10764
10789
  if (method === "POST") {
10765
- if (!presentsCredential(request4.headers)) {
10766
- await unauthorized(response);
10790
+ const createRefusal = credentialRefusal(request4.headers);
10791
+ if (createRefusal !== void 0) {
10792
+ await unauthorized(response, createRefusal);
10767
10793
  return;
10768
10794
  }
10769
10795
  const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
@@ -10788,8 +10814,9 @@ data: ${JSON.stringify(envelope)}
10788
10814
  return;
10789
10815
  }
10790
10816
  if (method === "GET") {
10791
- if (!presentsCredential(request4.headers)) {
10792
- await unauthorized(response);
10817
+ const readRefusal = credentialRefusal(request4.headers);
10818
+ if (readRefusal !== void 0) {
10819
+ await unauthorized(response, readRefusal);
10793
10820
  return;
10794
10821
  }
10795
10822
  await answerJson2(response, HTTP_STATUS.ok, { data: [...sessions.values()].map((session) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), updated_at: new Date(session.lastTrafficAt).toISOString(), session_status: "active" })) });
@@ -3998,13 +3998,13 @@ declare function createControlApiRouter(deps: ControlApiDeps): {
3998
3998
  "config-profile": "config-profile";
3999
3999
  identity: "identity";
4000
4000
  provider: "provider";
4001
- pool: "pool";
4002
- keychain: "keychain";
4003
4001
  "ambient-credential": "ambient-credential";
4004
4002
  "binary-discovery": "binary-discovery";
4005
4003
  "claude-shim": "claude-shim";
4006
4004
  "path-resolution": "path-resolution";
4007
4005
  "legacy-name": "legacy-name";
4006
+ pool: "pool";
4007
+ keychain: "keychain";
4008
4008
  "directory-rules": "directory-rules";
4009
4009
  "categories-local": "categories-local";
4010
4010
  "active-identity": "active-identity";
@@ -4475,13 +4475,13 @@ declare function createDoorApiRouter(deps: DoorApiDeps): {
4475
4475
  "config-profile": "config-profile";
4476
4476
  identity: "identity";
4477
4477
  provider: "provider";
4478
- pool: "pool";
4479
- keychain: "keychain";
4480
4478
  "ambient-credential": "ambient-credential";
4481
4479
  "binary-discovery": "binary-discovery";
4482
4480
  "claude-shim": "claude-shim";
4483
4481
  "path-resolution": "path-resolution";
4484
4482
  "legacy-name": "legacy-name";
4483
+ pool: "pool";
4484
+ keychain: "keychain";
4485
4485
  "directory-rules": "directory-rules";
4486
4486
  "categories-local": "categories-local";
4487
4487
  "active-identity": "active-identity";
@@ -4769,13 +4769,13 @@ declare const DoctorFindingSchema: z.ZodObject<{
4769
4769
  "config-profile": "config-profile";
4770
4770
  identity: "identity";
4771
4771
  provider: "provider";
4772
- pool: "pool";
4773
- keychain: "keychain";
4774
4772
  "ambient-credential": "ambient-credential";
4775
4773
  "binary-discovery": "binary-discovery";
4776
4774
  "claude-shim": "claude-shim";
4777
4775
  "path-resolution": "path-resolution";
4778
4776
  "legacy-name": "legacy-name";
4777
+ pool: "pool";
4778
+ keychain: "keychain";
4779
4779
  "directory-rules": "directory-rules";
4780
4780
  "categories-local": "categories-local";
4781
4781
  "active-identity": "active-identity";
@@ -4798,13 +4798,13 @@ declare const DoctorRunOutputSchema: z.ZodObject<{
4798
4798
  "config-profile": "config-profile";
4799
4799
  identity: "identity";
4800
4800
  provider: "provider";
4801
- pool: "pool";
4802
- keychain: "keychain";
4803
4801
  "ambient-credential": "ambient-credential";
4804
4802
  "binary-discovery": "binary-discovery";
4805
4803
  "claude-shim": "claude-shim";
4806
4804
  "path-resolution": "path-resolution";
4807
4805
  "legacy-name": "legacy-name";
4806
+ pool: "pool";
4807
+ keychain: "keychain";
4808
4808
  "directory-rules": "directory-rules";
4809
4809
  "categories-local": "categories-local";
4810
4810
  "active-identity": "active-identity";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-shim",
3
- "version": "8.21.0",
3
+ "version": "8.22.0",
4
4
  "description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
5
5
  "license": "MIT",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",