agent-shim 11.78.0 → 11.80.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/README.md +2 -1
- package/dist/cli.cjs +406 -88
- package/dist/index.cjs +133 -68
- package/dist/index.mjs +133 -68
- package/dist/types/index.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ That's it — with no further configuration, everything in `~/.claude` that isn'
|
|
|
57
57
|
- **An identity** is a login: the thing that owns credentials and daemon state, selected with `agent-shim run @<name>` or, once `agent-shim shim enable` has run, `claude @<name>`.
|
|
58
58
|
- **A configuration profile** is a reusable, named bundle of sharing rules, independent of any identity, resolved per working directory via directory rules or a committed `.agent-shim.json`.
|
|
59
59
|
|
|
60
|
-
A third, optional thing sits on top of identities: **a pool** is a named list of them. Select `pool:<name>` where an identity name goes (`claude @pool:subs`) and agent-shim picks the member whose unused quota is closest to expiring, skipping any that are refused right now, using the usage it already records. A pool can instead declare `--preference listed`, which tries the members in the order the pool lists them, skipping only one that is refused right now. A member entry may itself name another pool (`pool:<name>`), which is ranked with its own policy and contributes its pick at that position, so "this identity first, then the fleet" nests the fleet rather than copying its member list; cycles are refused. See [docs/configuration-model.md](docs/configuration-model.md#pools-picking-an-account-at-launch).
|
|
60
|
+
A third, optional thing sits on top of identities: **a pool** is a named list of them. Select `pool:<name>` where an identity name goes (`claude @pool:subs`) and agent-shim picks the member whose unused quota is closest to expiring, skipping any that are refused right now, using the usage it already records. A pool can instead declare `--preference listed`, which tries the members in the order the pool lists them, skipping only one that is refused right now. A member entry may itself name another pool (`pool:<name>`), which is ranked with its own policy and contributes its pick at that position, so "this identity first, then the fleet" nests the fleet rather than copying its member list; cycles are refused. Members that hold the same login (the same account in the same organisation) share one quota window, so a pool ranks them as one candidate and never fails over from one to the other; `pool add`, `pool set` and `doctor` warn when a pool lists two of them. See [docs/configuration-model.md](docs/configuration-model.md#pools-picking-an-account-at-launch). On a terminal, `agent-shim run` asks which identity to run as when none resolves, when a pool pick would change account, or when the identity is refused right now (`--pick` forces the question, `--no-pick` never asks); see [docs/configuration-model.md](docs/configuration-model.md#choosing-the-identity-at-launch-the-picker).
|
|
61
61
|
|
|
62
62
|
Every top-level entry in `~/.claude` is classified into one of five categories, shipped as a default map (`config/categories.default.json`):
|
|
63
63
|
|
|
@@ -200,6 +200,7 @@ agent-shim run [@<identity>] [launch flags] [claude arguments]
|
|
|
200
200
|
# --claude-version <version> run exactly this installed Claude Code version
|
|
201
201
|
# --delay <duration> wait this long after the launcher's own output before starting claude; --[no-]pause waits for a key press instead
|
|
202
202
|
# --[no-]explain with @pool:<name>, list every member the pick ranked and why, not only the one picked
|
|
203
|
+
# --[no-]pick on a terminal, ask which identity to run as, even when one resolved (--no-pick never asks); a launch also asks by itself when no identity resolves, a pool pick would change account or organisation, or the identity is refused right now
|
|
203
204
|
# --native run the real claude with nothing from agent-shim applied; cannot be combined with the flags above
|
|
204
205
|
claude @<identity> ... # the same, once `agent-shim shim enable` has run
|
|
205
206
|
```
|
package/dist/cli.cjs
CHANGED
|
@@ -38192,13 +38192,95 @@ var init_account = __esm({
|
|
|
38192
38192
|
}
|
|
38193
38193
|
});
|
|
38194
38194
|
|
|
38195
|
+
// src/usage/accountDescription.ts
|
|
38196
|
+
function describeAccountOrganisation(account) {
|
|
38197
|
+
const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
|
|
38198
|
+
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
38199
|
+
}
|
|
38200
|
+
function describeAccountPlan(account) {
|
|
38201
|
+
const parts = [account.billingType === void 0 ? void 0 : `billing ${account.billingType}`, account.seatTier === void 0 ? void 0 : `seat tier ${account.seatTier}`].filter((part) => part !== void 0);
|
|
38202
|
+
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
38203
|
+
}
|
|
38204
|
+
var init_accountDescription = __esm({
|
|
38205
|
+
"src/usage/accountDescription.ts"() {
|
|
38206
|
+
"use strict";
|
|
38207
|
+
}
|
|
38208
|
+
});
|
|
38209
|
+
|
|
38210
|
+
// src/identityAccounts.ts
|
|
38211
|
+
function readIdentityAccount(fs31, identitiesDir, identity) {
|
|
38212
|
+
try {
|
|
38213
|
+
const account = readAccountMetadata(fs31, identitiesDir, identity);
|
|
38214
|
+
return account === void 0 ? void 0 : { account };
|
|
38215
|
+
} catch (error63) {
|
|
38216
|
+
if (error63 instanceof AccountMetadataError) {
|
|
38217
|
+
return { problem: error63.message };
|
|
38218
|
+
}
|
|
38219
|
+
throw error63;
|
|
38220
|
+
}
|
|
38221
|
+
}
|
|
38222
|
+
function summariseIdentityAccount(account) {
|
|
38223
|
+
const summary = {
|
|
38224
|
+
...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
|
|
38225
|
+
...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
|
|
38226
|
+
...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
|
|
38227
|
+
...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
|
|
38228
|
+
...account.billingType === void 0 ? {} : { billingType: account.billingType },
|
|
38229
|
+
...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
|
|
38230
|
+
};
|
|
38231
|
+
return Object.keys(summary).length === 0 ? void 0 : summary;
|
|
38232
|
+
}
|
|
38233
|
+
function loginKeyOf(account) {
|
|
38234
|
+
return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
|
|
38235
|
+
}
|
|
38236
|
+
function collectByLogin(items, loginOf) {
|
|
38237
|
+
const groups = /* @__PURE__ */ new Map();
|
|
38238
|
+
for (const item of items) {
|
|
38239
|
+
const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
|
|
38240
|
+
const group = groups.get(key);
|
|
38241
|
+
if (group === void 0) {
|
|
38242
|
+
groups.set(key, [item]);
|
|
38243
|
+
} else {
|
|
38244
|
+
group.push(item);
|
|
38245
|
+
}
|
|
38246
|
+
}
|
|
38247
|
+
return [...groups.values()];
|
|
38248
|
+
}
|
|
38249
|
+
function groupByLogin(items, loginOf) {
|
|
38250
|
+
return collectByLogin(items, loginOf).flat();
|
|
38251
|
+
}
|
|
38252
|
+
function groupSharedLogins(items, loginOf) {
|
|
38253
|
+
return collectByLogin(items, loginOf).filter((group) => group.length > 1);
|
|
38254
|
+
}
|
|
38255
|
+
function joinNames(names) {
|
|
38256
|
+
const last = names.at(-1);
|
|
38257
|
+
return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
|
|
38258
|
+
}
|
|
38259
|
+
function describeAccountHolder(summary) {
|
|
38260
|
+
const organisation = describeAccountOrganisation(summary);
|
|
38261
|
+
if (summary.emailAddress === void 0) {
|
|
38262
|
+
return organisation;
|
|
38263
|
+
}
|
|
38264
|
+
return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
|
|
38265
|
+
}
|
|
38266
|
+
function describeIdentityAccount(summary) {
|
|
38267
|
+
return [describeAccountHolder(summary), summary.organizationRole === void 0 ? void 0 : `role ${summary.organizationRole}`, describeAccountPlan(summary)].filter((part) => part !== void 0).join("; ");
|
|
38268
|
+
}
|
|
38269
|
+
var init_identityAccounts = __esm({
|
|
38270
|
+
"src/identityAccounts.ts"() {
|
|
38271
|
+
"use strict";
|
|
38272
|
+
init_account();
|
|
38273
|
+
init_accountDescription();
|
|
38274
|
+
}
|
|
38275
|
+
});
|
|
38276
|
+
|
|
38195
38277
|
// package.json
|
|
38196
38278
|
var package_default;
|
|
38197
38279
|
var init_package = __esm({
|
|
38198
38280
|
"package.json"() {
|
|
38199
38281
|
package_default = {
|
|
38200
38282
|
name: "agent-shim",
|
|
38201
|
-
version: "11.
|
|
38283
|
+
version: "11.80.0",
|
|
38202
38284
|
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.",
|
|
38203
38285
|
license: "MIT",
|
|
38204
38286
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -38918,6 +39000,9 @@ function assessRefusedNested(member, nested, nowMs) {
|
|
|
38918
39000
|
};
|
|
38919
39001
|
}
|
|
38920
39002
|
function assess(member, nowMs) {
|
|
39003
|
+
if (member.sameLoginAs !== void 0) {
|
|
39004
|
+
return { candidate: { identity: member.identity, class: "ineligible", plan: planOf(member.account), reasons: [`same login as ${member.sameLoginAs}`] } };
|
|
39005
|
+
}
|
|
38921
39006
|
const nested = member.nested;
|
|
38922
39007
|
if (nested === void 0) {
|
|
38923
39008
|
return assessIdentity(member, nowMs);
|
|
@@ -39461,6 +39546,29 @@ function loadPoolMembers(fs31, paths, identities, nowMs) {
|
|
|
39461
39546
|
}
|
|
39462
39547
|
});
|
|
39463
39548
|
}
|
|
39549
|
+
function snapshotAgeKey(member) {
|
|
39550
|
+
return member.snapshot === void 0 ? Number.NEGATIVE_INFINITY : Date.parse(member.snapshot.updatedAt);
|
|
39551
|
+
}
|
|
39552
|
+
function collapseSharedLogins(members2) {
|
|
39553
|
+
const groups = groupSharedLogins(members2, (member) => member.nested === void 0 && member.account !== void 0 ? loginKeyOf(member.account) : void 0);
|
|
39554
|
+
const representativeOf = /* @__PURE__ */ new Map();
|
|
39555
|
+
for (const group of groups) {
|
|
39556
|
+
const representative = group.reduce((freshest, member) => snapshotAgeKey(member) > snapshotAgeKey(freshest) ? member : freshest);
|
|
39557
|
+
for (const member of group) {
|
|
39558
|
+
if (member !== representative) {
|
|
39559
|
+
representativeOf.set(member, representative.identity);
|
|
39560
|
+
}
|
|
39561
|
+
}
|
|
39562
|
+
}
|
|
39563
|
+
return members2.map((member) => {
|
|
39564
|
+
const representative = representativeOf.get(member);
|
|
39565
|
+
return representative === void 0 ? member : { ...member, sameLoginAs: representative };
|
|
39566
|
+
});
|
|
39567
|
+
}
|
|
39568
|
+
function stickyOnRepresentative(sticky, members2) {
|
|
39569
|
+
const twin = sticky === void 0 ? void 0 : members2.find((member) => member.identity === sticky.identity && member.sameLoginAs !== void 0);
|
|
39570
|
+
return sticky === void 0 || twin?.sameLoginAs === void 0 ? sticky : { ...sticky, identity: twin.sameLoginAs };
|
|
39571
|
+
}
|
|
39464
39572
|
function rankPoolGraph(input2) {
|
|
39465
39573
|
return rankNamedPool(input2, input2.poolName, [], input2.sticky);
|
|
39466
39574
|
}
|
|
@@ -39488,13 +39596,15 @@ function rankNamedPool(input2, poolName, stack, sticky) {
|
|
|
39488
39596
|
members2.push(typeof entry === "string" ? nested.member : { ...nested.member, policy: entry.when });
|
|
39489
39597
|
missing.push(...nested.missing);
|
|
39490
39598
|
}
|
|
39599
|
+
const collapsed = collapseSharedLogins(members2);
|
|
39600
|
+
const stickyPick = stickyOnRepresentative(sticky, collapsed);
|
|
39491
39601
|
const ranking = rankPool({
|
|
39492
|
-
members:
|
|
39602
|
+
members: collapsed,
|
|
39493
39603
|
nowMs: input2.nowMs,
|
|
39494
39604
|
resuming: input2.resuming,
|
|
39495
39605
|
...input2.resumeContextTokens === void 0 ? {} : { resumeContextTokens: input2.resumeContextTokens },
|
|
39496
39606
|
...input2.committedContextTokens === void 0 ? {} : { committedContextTokens: input2.committedContextTokens },
|
|
39497
|
-
...
|
|
39607
|
+
...stickyPick === void 0 ? {} : { sticky: stickyPick },
|
|
39498
39608
|
...pool.preference === void 0 ? {} : { preference: pool.preference }
|
|
39499
39609
|
});
|
|
39500
39610
|
return { ranking, missing };
|
|
@@ -39561,6 +39671,7 @@ var init_poolPick = __esm({
|
|
|
39561
39671
|
init_cliError();
|
|
39562
39672
|
init_identity();
|
|
39563
39673
|
init_schema();
|
|
39674
|
+
init_identityAccounts();
|
|
39564
39675
|
init_account();
|
|
39565
39676
|
init_pick();
|
|
39566
39677
|
init_read();
|
|
@@ -67175,7 +67286,8 @@ var init_argv = __esm({
|
|
|
67175
67286
|
{ flag: "--remote-control", key: "remoteControl" },
|
|
67176
67287
|
{ flag: "--wait", key: "wait" },
|
|
67177
67288
|
{ flag: "--pause", key: "pause" },
|
|
67178
|
-
{ flag: "--explain", key: "explain" }
|
|
67289
|
+
{ flag: "--explain", key: "explain" },
|
|
67290
|
+
{ flag: "--pick", key: "pick" }
|
|
67179
67291
|
];
|
|
67180
67292
|
LAUNCHER_FLAG_NAMES = [
|
|
67181
67293
|
...VALUED_FLAGS,
|
|
@@ -69956,7 +70068,7 @@ function decideLaunch(params, readOnly) {
|
|
|
69956
70068
|
const configDirEscapeHatchApplies = env.CLAUDE_CONFIG_DIR !== void 0 && env.CLAUDE_CONFIG_DIR !== "";
|
|
69957
70069
|
const decidedIdentity = decideIdentity({
|
|
69958
70070
|
env,
|
|
69959
|
-
argv0Identity: parsedArgv.identity,
|
|
70071
|
+
argv0Identity: params.pickedIdentity ?? parsedArgv.identity,
|
|
69960
70072
|
directoryPinnedIdentity: params.directoryPinnedIdentity,
|
|
69961
70073
|
readActiveIdentityFile: () => {
|
|
69962
70074
|
const raw = fs31.readFileUtf8(paths.activeIdentityFile);
|
|
@@ -117703,77 +117815,8 @@ function registerProfileCommand(program2, deps) {
|
|
|
117703
117815
|
init_providersStore();
|
|
117704
117816
|
init_identity();
|
|
117705
117817
|
init_realPorts();
|
|
117706
|
-
|
|
117707
|
-
|
|
117708
|
-
init_account();
|
|
117709
|
-
|
|
117710
|
-
// src/usage/accountDescription.ts
|
|
117711
|
-
function describeAccountOrganisation(account) {
|
|
117712
|
-
const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
|
|
117713
|
-
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
117714
|
-
}
|
|
117715
|
-
function describeAccountPlan(account) {
|
|
117716
|
-
const parts = [account.billingType === void 0 ? void 0 : `billing ${account.billingType}`, account.seatTier === void 0 ? void 0 : `seat tier ${account.seatTier}`].filter((part) => part !== void 0);
|
|
117717
|
-
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
117718
|
-
}
|
|
117719
|
-
|
|
117720
|
-
// src/identityAccounts.ts
|
|
117721
|
-
function readIdentityAccount(fs31, identitiesDir, identity) {
|
|
117722
|
-
try {
|
|
117723
|
-
const account = readAccountMetadata(fs31, identitiesDir, identity);
|
|
117724
|
-
return account === void 0 ? void 0 : { account };
|
|
117725
|
-
} catch (error63) {
|
|
117726
|
-
if (error63 instanceof AccountMetadataError) {
|
|
117727
|
-
return { problem: error63.message };
|
|
117728
|
-
}
|
|
117729
|
-
throw error63;
|
|
117730
|
-
}
|
|
117731
|
-
}
|
|
117732
|
-
function summariseIdentityAccount(account) {
|
|
117733
|
-
const summary = {
|
|
117734
|
-
...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
|
|
117735
|
-
...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
|
|
117736
|
-
...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
|
|
117737
|
-
...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
|
|
117738
|
-
...account.billingType === void 0 ? {} : { billingType: account.billingType },
|
|
117739
|
-
...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
|
|
117740
|
-
};
|
|
117741
|
-
return Object.keys(summary).length === 0 ? void 0 : summary;
|
|
117742
|
-
}
|
|
117743
|
-
function loginKeyOf(account) {
|
|
117744
|
-
return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
|
|
117745
|
-
}
|
|
117746
|
-
function collectByLogin(items, loginOf) {
|
|
117747
|
-
const groups = /* @__PURE__ */ new Map();
|
|
117748
|
-
for (const item of items) {
|
|
117749
|
-
const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
|
|
117750
|
-
const group = groups.get(key);
|
|
117751
|
-
if (group === void 0) {
|
|
117752
|
-
groups.set(key, [item]);
|
|
117753
|
-
} else {
|
|
117754
|
-
group.push(item);
|
|
117755
|
-
}
|
|
117756
|
-
}
|
|
117757
|
-
return [...groups.values()];
|
|
117758
|
-
}
|
|
117759
|
-
function groupByLogin(items, loginOf) {
|
|
117760
|
-
return collectByLogin(items, loginOf).flat();
|
|
117761
|
-
}
|
|
117762
|
-
function groupSharedLogins(items, loginOf) {
|
|
117763
|
-
return collectByLogin(items, loginOf).filter((group) => group.length > 1);
|
|
117764
|
-
}
|
|
117765
|
-
function describeAccountHolder(summary) {
|
|
117766
|
-
const organisation = describeAccountOrganisation(summary);
|
|
117767
|
-
if (summary.emailAddress === void 0) {
|
|
117768
|
-
return organisation;
|
|
117769
|
-
}
|
|
117770
|
-
return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
|
|
117771
|
-
}
|
|
117772
|
-
function describeIdentityAccount(summary) {
|
|
117773
|
-
return [describeAccountHolder(summary), summary.organizationRole === void 0 ? void 0 : `role ${summary.organizationRole}`, describeAccountPlan(summary)].filter((part) => part !== void 0).join("; ");
|
|
117774
|
-
}
|
|
117775
|
-
|
|
117776
|
-
// src/identityManager.ts
|
|
117818
|
+
init_identityAccounts();
|
|
117819
|
+
init_accountDescription();
|
|
117777
117820
|
async function runIdentityWizard(prompts, paths, name, options = {}) {
|
|
117778
117821
|
if (!IdentitySchema.safeParse({ name, allowAmbientCredential: false }).success) {
|
|
117779
117822
|
throw new InvalidIdentityNameError(name);
|
|
@@ -123233,6 +123276,7 @@ init_realPorts();
|
|
|
123233
123276
|
var import_node_fs20 = __toESM(require("node:fs"), 1);
|
|
123234
123277
|
var import_node_path47 = __toESM(require("node:path"), 1);
|
|
123235
123278
|
init_signIn();
|
|
123279
|
+
init_identityAccounts();
|
|
123236
123280
|
init_load();
|
|
123237
123281
|
init_unrecognisedKeys();
|
|
123238
123282
|
init_schema();
|
|
@@ -123507,6 +123551,27 @@ init_providerAliases();
|
|
|
123507
123551
|
init_providersStore();
|
|
123508
123552
|
init_guard();
|
|
123509
123553
|
init_identity();
|
|
123554
|
+
|
|
123555
|
+
// src/poolSharedLogins.ts
|
|
123556
|
+
init_identityAccounts();
|
|
123557
|
+
init_identity();
|
|
123558
|
+
function sharedLoginMembers(members2, accountOf) {
|
|
123559
|
+
const known = members2.flatMap((identity) => {
|
|
123560
|
+
const account = poolNameOf(identity) === void 0 ? accountOf(identity) : void 0;
|
|
123561
|
+
return account === void 0 ? [] : [{ identity, account }];
|
|
123562
|
+
});
|
|
123563
|
+
return groupSharedLogins(known, (entry) => loginKeyOf(entry.account)).flatMap((group) => {
|
|
123564
|
+
const first = group[0];
|
|
123565
|
+
return first === void 0 ? [] : [{ members: group.map((entry) => entry.identity), account: first.account }];
|
|
123566
|
+
});
|
|
123567
|
+
}
|
|
123568
|
+
function describeSharedLoginMembers(poolName, shared) {
|
|
123569
|
+
const summary = summariseIdentityAccount(shared.account);
|
|
123570
|
+
const holder = summary === void 0 ? void 0 : describeAccountHolder(summary);
|
|
123571
|
+
return `Pool "${poolName}" lists ${joinNames(shared.members)}, which hold the same login${holder === void 0 ? "" : ` (${holder})`}, so they share one quota window. A launch ranks them as one candidate and never fails over from one to another; keep one of them in the pool.`;
|
|
123572
|
+
}
|
|
123573
|
+
|
|
123574
|
+
// src/doctorReport.ts
|
|
123510
123575
|
init_extends();
|
|
123511
123576
|
var DOCTOR_SEVERITIES = ["pass", "warn", "fail"];
|
|
123512
123577
|
var DOCTOR_SECTIONS = [
|
|
@@ -123529,10 +123594,6 @@ var DOCTOR_SECTIONS = [
|
|
|
123529
123594
|
"frontdoor",
|
|
123530
123595
|
"models"
|
|
123531
123596
|
];
|
|
123532
|
-
function joinNames(names) {
|
|
123533
|
-
const last = names.at(-1);
|
|
123534
|
-
return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
|
|
123535
|
-
}
|
|
123536
123597
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
123537
123598
|
if (status.status !== "shadowed") {
|
|
123538
123599
|
return status;
|
|
@@ -123845,6 +123906,7 @@ function runDoctor(params) {
|
|
|
123845
123906
|
}
|
|
123846
123907
|
const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
|
|
123847
123908
|
const pools = validatedGlobalConfig?.ok === true ? validatedGlobalConfig.data.pools ?? {} : {};
|
|
123909
|
+
const accountByIdentity = new Map(params.identities.flatMap((entry) => validIdentityNames.has(entry.name) && entry.account !== void 0 ? [[entry.name, entry.account]] : []));
|
|
123848
123910
|
for (const [poolName, pool] of Object.entries(pools)) {
|
|
123849
123911
|
const missing = pool.identities.filter((member) => poolNameOf(poolMemberIdentity(member)) === void 0 && !validIdentityNames.has(poolMemberIdentity(member)));
|
|
123850
123912
|
const undefinedPools = pool.identities.flatMap((member) => {
|
|
@@ -123860,6 +123922,9 @@ function runDoctor(params) {
|
|
|
123860
123922
|
];
|
|
123861
123923
|
push("pool", "fail", `Pool "${poolName}" names ${problems.join(" and ")}.`, poolName);
|
|
123862
123924
|
}
|
|
123925
|
+
for (const shared of sharedLoginMembers(pool.identities.map(poolMemberIdentity), (identityName) => accountByIdentity.get(identityName))) {
|
|
123926
|
+
push("pool", "warn", describeSharedLoginMembers(poolName, shared), poolName);
|
|
123927
|
+
}
|
|
123863
123928
|
}
|
|
123864
123929
|
const reportedCycles = /* @__PURE__ */ new Set();
|
|
123865
123930
|
for (const poolName of Object.keys(pools).sort()) {
|
|
@@ -156375,6 +156440,7 @@ init_schema();
|
|
|
156375
156440
|
init_realPorts();
|
|
156376
156441
|
init_candidateText();
|
|
156377
156442
|
init_preflight();
|
|
156443
|
+
init_identityAccounts();
|
|
156378
156444
|
function formatPoolPick(report, nowMs) {
|
|
156379
156445
|
const head = report.pick === void 0 ? `Pool ${report.pool}: every member is refused right now${report.earliestReturn === void 0 ? "" : `; ${report.earliestReturn.identity} returns at ${report.earliestReturn.at} (in ${formatAge(Date.parse(report.earliestReturn.at) - nowMs)})`}` : `Pool ${report.pool}: a launch from ${report.directory} would run as ${report.pick}`;
|
|
156380
156446
|
const rows = report.candidates.map((candidate, index) => {
|
|
@@ -156383,6 +156449,11 @@ function formatPoolPick(report, nowMs) {
|
|
|
156383
156449
|
});
|
|
156384
156450
|
return [head, ...rows, ...report.missing.map((name) => ` (skipped: ${name} is not an identity)`), ...report.stickyProblem === void 0 ? [] : [`Note: ${report.stickyProblem}; the last-pick record was ignored.`]];
|
|
156385
156451
|
}
|
|
156452
|
+
function warnSharedLogins(paths, poolName, members2) {
|
|
156453
|
+
for (const shared of sharedLoginMembers(members2, (identity) => readIdentityAccount(realFsPort, paths.identitiesDir, identity)?.account)) {
|
|
156454
|
+
console.error(`agent-shim: warning: ${describeSharedLoginMembers(poolName, shared)}`);
|
|
156455
|
+
}
|
|
156456
|
+
}
|
|
156386
156457
|
function policyNotes(entry) {
|
|
156387
156458
|
const notes = [
|
|
156388
156459
|
...entry?.preference === void 0 ? [] : [`preference: ${entry.preference}`],
|
|
@@ -156415,6 +156486,7 @@ function registerPoolCommand(program2, deps) {
|
|
|
156415
156486
|
reportMutation(options.json, { action: "created", kind: "pool", name, value: created }, () => {
|
|
156416
156487
|
console.log(`Created pool "${name}" with ${members2.join(", ")}.`);
|
|
156417
156488
|
});
|
|
156489
|
+
warnSharedLogins(paths, name, created.identities.map(poolMemberIdentity));
|
|
156418
156490
|
}),
|
|
156419
156491
|
["agent-shim pool add subs --identity work --identity personal", "agent-shim pool add client --identity client-main --identity spare --preference listed"]
|
|
156420
156492
|
);
|
|
@@ -156432,6 +156504,7 @@ function registerPoolCommand(program2, deps) {
|
|
|
156432
156504
|
reportMutation(options.json, { action: "updated", kind: "pool", name, value: updated }, () => {
|
|
156433
156505
|
console.log(`Pool "${name}" now has ${updated.identities.map(poolMemberIdentity).join(", ")}.`);
|
|
156434
156506
|
});
|
|
156507
|
+
warnSharedLogins(paths, name, updated.identities.map(poolMemberIdentity));
|
|
156435
156508
|
}),
|
|
156436
156509
|
["agent-shim pool set subs --identity work --identity personal --identity spare", "agent-shim pool set subs --preference listed", "agent-shim pool set subs --resume-on-limit --compact-before-limit --compact-at-utilisation 85"]
|
|
156437
156510
|
);
|
|
@@ -157480,6 +157553,7 @@ var import_node_path70 = __toESM(require("node:path"), 1);
|
|
|
157480
157553
|
init_commandDeps();
|
|
157481
157554
|
init_realPorts();
|
|
157482
157555
|
init_account();
|
|
157556
|
+
init_accountDescription();
|
|
157483
157557
|
init_signIn();
|
|
157484
157558
|
init_resets();
|
|
157485
157559
|
init_read();
|
|
@@ -157820,6 +157894,242 @@ function buildProgram(deps) {
|
|
|
157820
157894
|
// src/runClaude.ts
|
|
157821
157895
|
init_cliError();
|
|
157822
157896
|
init_configProfilesStore();
|
|
157897
|
+
|
|
157898
|
+
// src/launcher/pickerPrompt.ts
|
|
157899
|
+
function promptsIdentityPicker(prompts) {
|
|
157900
|
+
return {
|
|
157901
|
+
pick: async (request7) => {
|
|
157902
|
+
const answer = await prompts.select({
|
|
157903
|
+
message: request7.message,
|
|
157904
|
+
options: request7.entries.map((entry) => ({ value: entry.value, label: entry.label })),
|
|
157905
|
+
...request7.initial === void 0 ? {} : { initialValue: request7.initial }
|
|
157906
|
+
});
|
|
157907
|
+
return prompts.isCancel(answer) ? { kind: "cancelled" } : { kind: "picked", value: answer };
|
|
157908
|
+
}
|
|
157909
|
+
};
|
|
157910
|
+
}
|
|
157911
|
+
|
|
157912
|
+
// src/launcher/runPicker.ts
|
|
157913
|
+
init_cliError();
|
|
157914
|
+
init_schema();
|
|
157915
|
+
init_identityAccounts();
|
|
157916
|
+
init_account();
|
|
157917
|
+
init_candidateText();
|
|
157918
|
+
init_pick();
|
|
157919
|
+
init_poolPick();
|
|
157920
|
+
init_identity();
|
|
157921
|
+
init_launchReport();
|
|
157922
|
+
init_hold();
|
|
157923
|
+
|
|
157924
|
+
// src/launcher/pickerEntries.ts
|
|
157925
|
+
var PART_SEPARATOR = " | ";
|
|
157926
|
+
function pickerLabel(facts) {
|
|
157927
|
+
const name = [facts.rank === void 0 ? void 0 : `${String(facts.rank)}.`, facts.identity, facts.heading, facts.picked === true ? "(picked)" : void 0].filter((part) => part !== void 0).join(" ");
|
|
157928
|
+
return [name, facts.detail, facts.account, facts.quota].filter((part) => part !== void 0).join(PART_SEPARATOR);
|
|
157929
|
+
}
|
|
157930
|
+
|
|
157931
|
+
// src/launcher/pickerDecision.ts
|
|
157932
|
+
init_claudeSubcommand();
|
|
157933
|
+
function decidePicker(facts) {
|
|
157934
|
+
if (!facts.interactive || facts.configDirEscapeHatch || facts.pick === false) {
|
|
157935
|
+
return void 0;
|
|
157936
|
+
}
|
|
157937
|
+
if (facts.pick === true) {
|
|
157938
|
+
return "requested";
|
|
157939
|
+
}
|
|
157940
|
+
if (facts.unattended) {
|
|
157941
|
+
return void 0;
|
|
157942
|
+
}
|
|
157943
|
+
const { selection } = facts;
|
|
157944
|
+
switch (selection.kind) {
|
|
157945
|
+
case "none":
|
|
157946
|
+
return "no-identity";
|
|
157947
|
+
case "identity":
|
|
157948
|
+
return selection.limited ? "no-eligible-identity" : void 0;
|
|
157949
|
+
case "pool":
|
|
157950
|
+
if (selection.outcome === "none-eligible") {
|
|
157951
|
+
return facts.waiting ? void 0 : "no-eligible-identity";
|
|
157952
|
+
}
|
|
157953
|
+
return selection.outcome === "changes-account" ? "account-change" : void 0;
|
|
157954
|
+
default:
|
|
157955
|
+
return selection;
|
|
157956
|
+
}
|
|
157957
|
+
}
|
|
157958
|
+
var TERMINATOR4 = "--";
|
|
157959
|
+
var UNATTENDED_FLAGS = /* @__PURE__ */ new Set(["-p", "--print", "--bg"]);
|
|
157960
|
+
function isUnattendedLaunch(passthrough) {
|
|
157961
|
+
const end = passthrough.indexOf(TERMINATOR4);
|
|
157962
|
+
const head = end === -1 ? passthrough : passthrough.slice(0, end);
|
|
157963
|
+
return head.some((token) => UNATTENDED_FLAGS.has(token)) || isClaudeSubcommand(passthrough);
|
|
157964
|
+
}
|
|
157965
|
+
|
|
157966
|
+
// src/launcher/runPicker.ts
|
|
157967
|
+
init_pool();
|
|
157968
|
+
var IdentityPickerCancelledError = class extends CliError {
|
|
157969
|
+
exitCode = LAUNCH_CANCELLED_EXIT;
|
|
157970
|
+
constructor() {
|
|
157971
|
+
super("launch cancelled at the identity picker: no identity was chosen.");
|
|
157972
|
+
this.name = "IdentityPickerCancelledError";
|
|
157973
|
+
}
|
|
157974
|
+
};
|
|
157975
|
+
function memberTexts(member, nowMs) {
|
|
157976
|
+
if (member === void 0) {
|
|
157977
|
+
return {};
|
|
157978
|
+
}
|
|
157979
|
+
const billing = { login: member.account, snapshot: member.snapshot, provider: void 0, nowMs };
|
|
157980
|
+
const account = accountRows(billing)[0]?.value;
|
|
157981
|
+
const quotaRow2 = quotaRows(billing)[0];
|
|
157982
|
+
return {
|
|
157983
|
+
...account === void 0 ? {} : { account },
|
|
157984
|
+
...quotaRow2 === void 0 ? {} : { quota: quotaRow2.detail === void 0 ? quotaRow2.value : `${quotaRow2.value} (${quotaRow2.detail})` }
|
|
157985
|
+
};
|
|
157986
|
+
}
|
|
157987
|
+
function refusalOf(candidate) {
|
|
157988
|
+
return candidate.class === "ineligible" ? candidate.reasons[0] : void 0;
|
|
157989
|
+
}
|
|
157990
|
+
function accountsDiffer(members2, left, right) {
|
|
157991
|
+
const keyOf3 = (identity) => {
|
|
157992
|
+
const account = members2.find((member) => member.identity === identity)?.account;
|
|
157993
|
+
return account === void 0 ? void 0 : loginKeyOf(account);
|
|
157994
|
+
};
|
|
157995
|
+
const leftKey = keyOf3(left);
|
|
157996
|
+
const rightKey = keyOf3(right);
|
|
157997
|
+
return leftKey !== void 0 && rightKey !== void 0 && leftKey !== rightKey;
|
|
157998
|
+
}
|
|
157999
|
+
function poolContext(params, poolName, resuming, nowMs) {
|
|
158000
|
+
const { paths, usageFs } = params;
|
|
158001
|
+
const { sticky } = readStickyPick(usageFs, paths.usagePicksFile, params.cwd);
|
|
158002
|
+
const { ranking } = rankPoolGraph({
|
|
158003
|
+
fs: usageFs,
|
|
158004
|
+
paths,
|
|
158005
|
+
pools: params.pools ?? {},
|
|
158006
|
+
poolName,
|
|
158007
|
+
nowMs,
|
|
158008
|
+
resuming,
|
|
158009
|
+
identityExists: (name) => loadIdentity(paths.identitiesDir, name, params.fs) !== void 0,
|
|
158010
|
+
...sticky === void 0 ? {} : { sticky }
|
|
158011
|
+
});
|
|
158012
|
+
const pick2 = ranking.pick?.identity;
|
|
158013
|
+
const named = [.../* @__PURE__ */ new Set([...ranking.candidates.map((candidate) => candidate.identity), ...sticky === void 0 ? [] : [sticky.identity]])].filter(isIdentityName);
|
|
158014
|
+
const members2 = loadPoolMembers(usageFs, paths, named, nowMs);
|
|
158015
|
+
const changes = pick2 !== void 0 && sticky !== void 0 && sticky.identity !== pick2 && accountsDiffer(members2, sticky.identity, pick2);
|
|
158016
|
+
return {
|
|
158017
|
+
selection: { kind: "pool", outcome: pick2 === void 0 ? "none-eligible" : changes ? "changes-account" : "keeps-account" },
|
|
158018
|
+
entries: ranking.candidates.map((candidate, index) => ({
|
|
158019
|
+
value: candidate.identity,
|
|
158020
|
+
label: pickerLabel({
|
|
158021
|
+
identity: candidate.identity,
|
|
158022
|
+
rank: index + 1,
|
|
158023
|
+
heading: candidateHeading(candidate),
|
|
158024
|
+
picked: candidate.identity === pick2,
|
|
158025
|
+
detail: refusalOf(candidate),
|
|
158026
|
+
...memberTexts(members2.find((member) => member.identity === candidate.identity), nowMs)
|
|
158027
|
+
})
|
|
158028
|
+
})),
|
|
158029
|
+
initial: pick2,
|
|
158030
|
+
poolPick: pick2,
|
|
158031
|
+
message: (reason) => poolMessage(reason, poolName, pick2, sticky)
|
|
158032
|
+
};
|
|
158033
|
+
}
|
|
158034
|
+
function poolMessage(reason, poolName, pick2, sticky) {
|
|
158035
|
+
switch (reason) {
|
|
158036
|
+
case "account-change":
|
|
158037
|
+
return `Pool "${poolName}" would run this launch as ${pick2 ?? "(none)"}, a different account from ${sticky?.identity ?? "(none)"}, which this directory last ran as. Which identity should it run as?`;
|
|
158038
|
+
case "no-eligible-identity":
|
|
158039
|
+
return `Every member of pool "${poolName}" is refused right now. Which identity should this launch run as?`;
|
|
158040
|
+
case "requested":
|
|
158041
|
+
case "no-identity":
|
|
158042
|
+
return `Pool "${poolName}" is selected for this launch. Which identity should it run as?`;
|
|
158043
|
+
default:
|
|
158044
|
+
return reason;
|
|
158045
|
+
}
|
|
158046
|
+
}
|
|
158047
|
+
function identityContext(params, resuming, nowMs) {
|
|
158048
|
+
const { paths, usageFs } = params;
|
|
158049
|
+
const members2 = loadPoolMembers(usageFs, paths, params.identityNames(), nowMs);
|
|
158050
|
+
const { candidates } = rankPool({ members: members2, nowMs, resuming, preference: "listed" });
|
|
158051
|
+
const named = params.decision.name;
|
|
158052
|
+
const limited = named !== void 0 && candidates.some((candidate) => candidate.identity === named && candidate.class === "ineligible");
|
|
158053
|
+
const identityEntries = candidates.map((candidate) => ({
|
|
158054
|
+
value: candidate.identity,
|
|
158055
|
+
label: pickerLabel({ identity: candidate.identity, detail: refusalOf(candidate), ...memberTexts(members2.find((member) => member.identity === candidate.identity), nowMs) })
|
|
158056
|
+
}));
|
|
158057
|
+
const poolEntries = Object.entries(params.pools ?? {}).map(([name, pool]) => ({
|
|
158058
|
+
value: `pool:${name}`,
|
|
158059
|
+
label: pickerLabel({ identity: `pool:${name}`, detail: `members ${pool.identities.map(poolMemberIdentity).join(", ")}` })
|
|
158060
|
+
}));
|
|
158061
|
+
return {
|
|
158062
|
+
selection: named === void 0 ? { kind: "none" } : { kind: "identity", limited },
|
|
158063
|
+
entries: [...identityEntries, ...poolEntries],
|
|
158064
|
+
initial: named,
|
|
158065
|
+
poolPick: void 0,
|
|
158066
|
+
message: (reason) => {
|
|
158067
|
+
switch (reason) {
|
|
158068
|
+
case "no-identity":
|
|
158069
|
+
return "No identity is selected for this launch. Which identity should it run as?";
|
|
158070
|
+
case "no-eligible-identity":
|
|
158071
|
+
return `${named ?? "The selected identity"} is refused right now. Which identity should this launch run as?`;
|
|
158072
|
+
case "requested":
|
|
158073
|
+
case "account-change":
|
|
158074
|
+
return "Which identity should this launch run as?";
|
|
158075
|
+
default:
|
|
158076
|
+
return reason;
|
|
158077
|
+
}
|
|
158078
|
+
}
|
|
158079
|
+
};
|
|
158080
|
+
}
|
|
158081
|
+
async function runIdentityPicker(params) {
|
|
158082
|
+
if (!params.interactive) {
|
|
158083
|
+
return { kind: "unchanged" };
|
|
158084
|
+
}
|
|
158085
|
+
const nowMs = params.now();
|
|
158086
|
+
const resuming = isResuming(params.parsed.rest);
|
|
158087
|
+
const context = params.decision.pool === void 0 ? identityContext(params, resuming, nowMs) : poolContext(params, params.decision.pool, resuming, nowMs);
|
|
158088
|
+
const reason = decidePicker({
|
|
158089
|
+
interactive: params.interactive,
|
|
158090
|
+
unattended: isUnattendedLaunch(params.parsed.rest),
|
|
158091
|
+
pick: params.parsed.pick,
|
|
158092
|
+
configDirEscapeHatch: params.decision.configDirEscapeHatch,
|
|
158093
|
+
waiting: params.parsed.wait === true,
|
|
158094
|
+
selection: context.selection
|
|
158095
|
+
});
|
|
158096
|
+
if (reason === void 0) {
|
|
158097
|
+
return { kind: "unchanged" };
|
|
158098
|
+
}
|
|
158099
|
+
const request7 = { message: context.message(reason), entries: context.entries, ...context.initial === void 0 ? {} : { initial: context.initial } };
|
|
158100
|
+
const answer = await params.picker.pick(request7);
|
|
158101
|
+
if (answer.kind === "cancelled") {
|
|
158102
|
+
return { kind: "cancelled" };
|
|
158103
|
+
}
|
|
158104
|
+
const chosen = answer.value;
|
|
158105
|
+
if (chosen === (params.decision.pool === void 0 ? params.decision.name : context.poolPick)) {
|
|
158106
|
+
return { kind: "unchanged" };
|
|
158107
|
+
}
|
|
158108
|
+
if (params.decision.pool !== void 0 && poolNameOf(chosen) === void 0) {
|
|
158109
|
+
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, chosen, nowMs);
|
|
158110
|
+
}
|
|
158111
|
+
return { kind: "selected", selector: chosen };
|
|
158112
|
+
}
|
|
158113
|
+
|
|
158114
|
+
// src/launchPicker.ts
|
|
158115
|
+
init_realPorts();
|
|
158116
|
+
async function pickLaunchIdentity(params) {
|
|
158117
|
+
return await runIdentityPicker({
|
|
158118
|
+
parsed: params.parsed,
|
|
158119
|
+
decision: params.decision,
|
|
158120
|
+
interactive: process.stdin.isTTY && process.stdout.isTTY,
|
|
158121
|
+
paths: params.paths,
|
|
158122
|
+
fs: realFsPort,
|
|
158123
|
+
usageFs: params.farm.runtime.fs,
|
|
158124
|
+
pools: params.farm.pools,
|
|
158125
|
+
cwd: params.farm.runtime.cwd,
|
|
158126
|
+
now: params.farm.runtime.now,
|
|
158127
|
+
identityNames: () => listIdentities(params.paths).flatMap((listing) => "problem" in listing ? [] : [listing.name]),
|
|
158128
|
+
picker: promptsIdentityPicker(realPromptsPort)
|
|
158129
|
+
});
|
|
158130
|
+
}
|
|
158131
|
+
|
|
158132
|
+
// src/runClaude.ts
|
|
157823
158133
|
init_launchWiring();
|
|
157824
158134
|
init_paths();
|
|
157825
158135
|
|
|
@@ -158589,6 +158899,7 @@ function otherLaunchFlags(parsed) {
|
|
|
158589
158899
|
...parsed.skipPermissions === void 0 ? [] : ["--[no-]skip-permissions"],
|
|
158590
158900
|
...parsed.remoteControl === void 0 ? [] : ["--[no-]remote-control"],
|
|
158591
158901
|
...parsed.wait === void 0 ? [] : ["--[no-]wait"],
|
|
158902
|
+
...parsed.pick === void 0 ? [] : ["--[no-]pick"],
|
|
158592
158903
|
...parsed.categoryFlags.length === 0 ? [] : ["--category"],
|
|
158593
158904
|
...parsed.shareFlags.length === 0 ? [] : ["--share"],
|
|
158594
158905
|
...parsed.hideFlags.length === 0 ? [] : ["--hide"]
|
|
@@ -158654,13 +158965,19 @@ async function runClaude(argvOverride) {
|
|
|
158654
158965
|
}
|
|
158655
158966
|
}
|
|
158656
158967
|
}
|
|
158657
|
-
|
|
158658
|
-
paths,
|
|
158659
|
-
|
|
158660
|
-
|
|
158661
|
-
|
|
158662
|
-
|
|
158663
|
-
|
|
158968
|
+
const refreshUsage = async (poolName2) => {
|
|
158969
|
+
await refreshPools({ paths, cwd: process.cwd(), poolNames: poolName2 === void 0 ? [] : [poolName2], spawnDaemon: spawnDetachedSupervisor, log: realLogPort.warn });
|
|
158970
|
+
};
|
|
158971
|
+
await refreshUsage(identityDecision.pool);
|
|
158972
|
+
const picked = await pickLaunchIdentity({ paths, farm, parsed: nativeArgv, decision: identityDecision });
|
|
158973
|
+
if (picked.kind === "cancelled") {
|
|
158974
|
+
throw new IdentityPickerCancelledError();
|
|
158975
|
+
}
|
|
158976
|
+
const pickedIdentity = picked.kind === "selected" ? picked.selector : void 0;
|
|
158977
|
+
const launchDecision = pickedIdentity === void 0 ? identityDecision : decideIdentity({ env: procForDecision.env, argv0Identity: pickedIdentity, readActiveIdentityFile: () => void 0 });
|
|
158978
|
+
if (launchDecision.pool !== identityDecision.pool) {
|
|
158979
|
+
await refreshUsage(launchDecision.pool);
|
|
158980
|
+
}
|
|
158664
158981
|
const prepareParams = {
|
|
158665
158982
|
...realPrepareLaunchParams(paths, {
|
|
158666
158983
|
proc: argvOverride === void 0 ? realProcPort : { ...realProcPort, argv: argvOverride },
|
|
@@ -158669,10 +158986,11 @@ async function runClaude(argvOverride) {
|
|
|
158669
158986
|
farm,
|
|
158670
158987
|
allowMissingConfigProfile
|
|
158671
158988
|
}),
|
|
158989
|
+
...pickedIdentity === void 0 ? {} : { pickedIdentity },
|
|
158672
158990
|
// Wired here rather than in realPrepareLaunchParams because the port re-invokes this very binary: correct for the agent-shim command line, wrong for a library host whose own executable is not agent-shim.
|
|
158673
158991
|
update: realLaunchUpdatePort(paths)
|
|
158674
158992
|
};
|
|
158675
|
-
const poolName =
|
|
158993
|
+
const poolName = launchDecision.pool;
|
|
158676
158994
|
if (poolName !== void 0 && farm.pools !== void 0 && supervisesLaunch(farm.pools[poolName], nativeArgv.rest)) {
|
|
158677
158995
|
const code = await runSupervised({
|
|
158678
158996
|
paths,
|
package/dist/index.cjs
CHANGED
|
@@ -4764,7 +4764,7 @@ var init_package = __esm({
|
|
|
4764
4764
|
"package.json"() {
|
|
4765
4765
|
package_default = {
|
|
4766
4766
|
name: "agent-shim",
|
|
4767
|
-
version: "11.
|
|
4767
|
+
version: "11.80.0",
|
|
4768
4768
|
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.",
|
|
4769
4769
|
license: "MIT",
|
|
4770
4770
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -7037,6 +7037,78 @@ var init_account = __esm({
|
|
|
7037
7037
|
}
|
|
7038
7038
|
});
|
|
7039
7039
|
|
|
7040
|
+
// src/usage/accountDescription.ts
|
|
7041
|
+
function describeAccountOrganisation(account) {
|
|
7042
|
+
const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
|
|
7043
|
+
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
7044
|
+
}
|
|
7045
|
+
var init_accountDescription = __esm({
|
|
7046
|
+
"src/usage/accountDescription.ts"() {
|
|
7047
|
+
"use strict";
|
|
7048
|
+
}
|
|
7049
|
+
});
|
|
7050
|
+
|
|
7051
|
+
// src/identityAccounts.ts
|
|
7052
|
+
function readIdentityAccount(fs14, identitiesDir, identity) {
|
|
7053
|
+
try {
|
|
7054
|
+
const account = readAccountMetadata(fs14, identitiesDir, identity);
|
|
7055
|
+
return account === void 0 ? void 0 : { account };
|
|
7056
|
+
} catch (error) {
|
|
7057
|
+
if (error instanceof AccountMetadataError) {
|
|
7058
|
+
return { problem: error.message };
|
|
7059
|
+
}
|
|
7060
|
+
throw error;
|
|
7061
|
+
}
|
|
7062
|
+
}
|
|
7063
|
+
function summariseIdentityAccount(account) {
|
|
7064
|
+
const summary = {
|
|
7065
|
+
...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
|
|
7066
|
+
...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
|
|
7067
|
+
...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
|
|
7068
|
+
...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
|
|
7069
|
+
...account.billingType === void 0 ? {} : { billingType: account.billingType },
|
|
7070
|
+
...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
|
|
7071
|
+
};
|
|
7072
|
+
return Object.keys(summary).length === 0 ? void 0 : summary;
|
|
7073
|
+
}
|
|
7074
|
+
function loginKeyOf(account) {
|
|
7075
|
+
return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
|
|
7076
|
+
}
|
|
7077
|
+
function collectByLogin(items, loginOf) {
|
|
7078
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7079
|
+
for (const item of items) {
|
|
7080
|
+
const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
|
|
7081
|
+
const group = groups.get(key);
|
|
7082
|
+
if (group === void 0) {
|
|
7083
|
+
groups.set(key, [item]);
|
|
7084
|
+
} else {
|
|
7085
|
+
group.push(item);
|
|
7086
|
+
}
|
|
7087
|
+
}
|
|
7088
|
+
return [...groups.values()];
|
|
7089
|
+
}
|
|
7090
|
+
function groupSharedLogins(items, loginOf) {
|
|
7091
|
+
return collectByLogin(items, loginOf).filter((group) => group.length > 1);
|
|
7092
|
+
}
|
|
7093
|
+
function joinNames(names) {
|
|
7094
|
+
const last = names.at(-1);
|
|
7095
|
+
return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
|
|
7096
|
+
}
|
|
7097
|
+
function describeAccountHolder(summary) {
|
|
7098
|
+
const organisation = describeAccountOrganisation(summary);
|
|
7099
|
+
if (summary.emailAddress === void 0) {
|
|
7100
|
+
return organisation;
|
|
7101
|
+
}
|
|
7102
|
+
return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
|
|
7103
|
+
}
|
|
7104
|
+
var init_identityAccounts = __esm({
|
|
7105
|
+
"src/identityAccounts.ts"() {
|
|
7106
|
+
"use strict";
|
|
7107
|
+
init_account();
|
|
7108
|
+
init_accountDescription();
|
|
7109
|
+
}
|
|
7110
|
+
});
|
|
7111
|
+
|
|
7040
7112
|
// src/usage/rateLimit.ts
|
|
7041
7113
|
function epochSecondsToIso(value) {
|
|
7042
7114
|
if (value === void 0) {
|
|
@@ -7404,6 +7476,9 @@ function assessRefusedNested(member, nested, nowMs) {
|
|
|
7404
7476
|
};
|
|
7405
7477
|
}
|
|
7406
7478
|
function assess(member, nowMs) {
|
|
7479
|
+
if (member.sameLoginAs !== void 0) {
|
|
7480
|
+
return { candidate: { identity: member.identity, class: "ineligible", plan: planOf(member.account), reasons: [`same login as ${member.sameLoginAs}`] } };
|
|
7481
|
+
}
|
|
7407
7482
|
const nested = member.nested;
|
|
7408
7483
|
if (nested === void 0) {
|
|
7409
7484
|
return assessIdentity(member, nowMs);
|
|
@@ -7876,6 +7951,29 @@ function loadPoolMembers(fs14, paths, identities, nowMs) {
|
|
|
7876
7951
|
}
|
|
7877
7952
|
});
|
|
7878
7953
|
}
|
|
7954
|
+
function snapshotAgeKey(member) {
|
|
7955
|
+
return member.snapshot === void 0 ? Number.NEGATIVE_INFINITY : Date.parse(member.snapshot.updatedAt);
|
|
7956
|
+
}
|
|
7957
|
+
function collapseSharedLogins(members) {
|
|
7958
|
+
const groups = groupSharedLogins(members, (member) => member.nested === void 0 && member.account !== void 0 ? loginKeyOf(member.account) : void 0);
|
|
7959
|
+
const representativeOf = /* @__PURE__ */ new Map();
|
|
7960
|
+
for (const group of groups) {
|
|
7961
|
+
const representative = group.reduce((freshest, member) => snapshotAgeKey(member) > snapshotAgeKey(freshest) ? member : freshest);
|
|
7962
|
+
for (const member of group) {
|
|
7963
|
+
if (member !== representative) {
|
|
7964
|
+
representativeOf.set(member, representative.identity);
|
|
7965
|
+
}
|
|
7966
|
+
}
|
|
7967
|
+
}
|
|
7968
|
+
return members.map((member) => {
|
|
7969
|
+
const representative = representativeOf.get(member);
|
|
7970
|
+
return representative === void 0 ? member : { ...member, sameLoginAs: representative };
|
|
7971
|
+
});
|
|
7972
|
+
}
|
|
7973
|
+
function stickyOnRepresentative(sticky, members) {
|
|
7974
|
+
const twin = sticky === void 0 ? void 0 : members.find((member) => member.identity === sticky.identity && member.sameLoginAs !== void 0);
|
|
7975
|
+
return sticky === void 0 || twin?.sameLoginAs === void 0 ? sticky : { ...sticky, identity: twin.sameLoginAs };
|
|
7976
|
+
}
|
|
7879
7977
|
function rankPoolGraph(input) {
|
|
7880
7978
|
return rankNamedPool(input, input.poolName, [], input.sticky);
|
|
7881
7979
|
}
|
|
@@ -7903,13 +8001,15 @@ function rankNamedPool(input, poolName, stack, sticky) {
|
|
|
7903
8001
|
members.push(typeof entry === "string" ? nested.member : { ...nested.member, policy: entry.when });
|
|
7904
8002
|
missing.push(...nested.missing);
|
|
7905
8003
|
}
|
|
8004
|
+
const collapsed = collapseSharedLogins(members);
|
|
8005
|
+
const stickyPick = stickyOnRepresentative(sticky, collapsed);
|
|
7906
8006
|
const ranking = rankPool({
|
|
7907
|
-
members,
|
|
8007
|
+
members: collapsed,
|
|
7908
8008
|
nowMs: input.nowMs,
|
|
7909
8009
|
resuming: input.resuming,
|
|
7910
8010
|
...input.resumeContextTokens === void 0 ? {} : { resumeContextTokens: input.resumeContextTokens },
|
|
7911
8011
|
...input.committedContextTokens === void 0 ? {} : { committedContextTokens: input.committedContextTokens },
|
|
7912
|
-
...
|
|
8012
|
+
...stickyPick === void 0 ? {} : { sticky: stickyPick },
|
|
7913
8013
|
...pool.preference === void 0 ? {} : { preference: pool.preference }
|
|
7914
8014
|
});
|
|
7915
8015
|
return { ranking, missing };
|
|
@@ -7997,6 +8097,7 @@ var init_poolPick = __esm({
|
|
|
7997
8097
|
init_cliError();
|
|
7998
8098
|
init_identity();
|
|
7999
8099
|
init_schema();
|
|
8100
|
+
init_identityAccounts();
|
|
8000
8101
|
init_account();
|
|
8001
8102
|
init_pick();
|
|
8002
8103
|
init_read();
|
|
@@ -10726,7 +10827,8 @@ var init_argv = __esm({
|
|
|
10726
10827
|
{ flag: "--remote-control", key: "remoteControl" },
|
|
10727
10828
|
{ flag: "--wait", key: "wait" },
|
|
10728
10829
|
{ flag: "--pause", key: "pause" },
|
|
10729
|
-
{ flag: "--explain", key: "explain" }
|
|
10830
|
+
{ flag: "--explain", key: "explain" },
|
|
10831
|
+
{ flag: "--pick", key: "pick" }
|
|
10730
10832
|
];
|
|
10731
10833
|
LAUNCHER_FLAG_NAMES = [
|
|
10732
10834
|
...VALUED_FLAGS,
|
|
@@ -11812,7 +11914,7 @@ function decideLaunch(params, readOnly) {
|
|
|
11812
11914
|
const configDirEscapeHatchApplies = env.CLAUDE_CONFIG_DIR !== void 0 && env.CLAUDE_CONFIG_DIR !== "";
|
|
11813
11915
|
const decidedIdentity = decideIdentity({
|
|
11814
11916
|
env,
|
|
11815
|
-
argv0Identity: parsedArgv.identity,
|
|
11917
|
+
argv0Identity: params.pickedIdentity ?? parsedArgv.identity,
|
|
11816
11918
|
directoryPinnedIdentity: params.directoryPinnedIdentity,
|
|
11817
11919
|
readActiveIdentityFile: () => {
|
|
11818
11920
|
const raw = fs14.readFileUtf8(paths.activeIdentityFile);
|
|
@@ -17083,65 +17185,7 @@ init_realPorts();
|
|
|
17083
17185
|
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
17084
17186
|
var import_node_path29 = __toESM(require("node:path"), 1);
|
|
17085
17187
|
init_signIn();
|
|
17086
|
-
|
|
17087
|
-
// src/identityAccounts.ts
|
|
17088
|
-
init_account();
|
|
17089
|
-
|
|
17090
|
-
// src/usage/accountDescription.ts
|
|
17091
|
-
function describeAccountOrganisation(account) {
|
|
17092
|
-
const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
|
|
17093
|
-
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
17094
|
-
}
|
|
17095
|
-
|
|
17096
|
-
// src/identityAccounts.ts
|
|
17097
|
-
function readIdentityAccount(fs14, identitiesDir, identity) {
|
|
17098
|
-
try {
|
|
17099
|
-
const account = readAccountMetadata(fs14, identitiesDir, identity);
|
|
17100
|
-
return account === void 0 ? void 0 : { account };
|
|
17101
|
-
} catch (error) {
|
|
17102
|
-
if (error instanceof AccountMetadataError) {
|
|
17103
|
-
return { problem: error.message };
|
|
17104
|
-
}
|
|
17105
|
-
throw error;
|
|
17106
|
-
}
|
|
17107
|
-
}
|
|
17108
|
-
function summariseIdentityAccount(account) {
|
|
17109
|
-
const summary = {
|
|
17110
|
-
...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
|
|
17111
|
-
...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
|
|
17112
|
-
...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
|
|
17113
|
-
...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
|
|
17114
|
-
...account.billingType === void 0 ? {} : { billingType: account.billingType },
|
|
17115
|
-
...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
|
|
17116
|
-
};
|
|
17117
|
-
return Object.keys(summary).length === 0 ? void 0 : summary;
|
|
17118
|
-
}
|
|
17119
|
-
function loginKeyOf(account) {
|
|
17120
|
-
return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
|
|
17121
|
-
}
|
|
17122
|
-
function collectByLogin(items, loginOf) {
|
|
17123
|
-
const groups = /* @__PURE__ */ new Map();
|
|
17124
|
-
for (const item of items) {
|
|
17125
|
-
const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
|
|
17126
|
-
const group = groups.get(key);
|
|
17127
|
-
if (group === void 0) {
|
|
17128
|
-
groups.set(key, [item]);
|
|
17129
|
-
} else {
|
|
17130
|
-
group.push(item);
|
|
17131
|
-
}
|
|
17132
|
-
}
|
|
17133
|
-
return [...groups.values()];
|
|
17134
|
-
}
|
|
17135
|
-
function groupSharedLogins(items, loginOf) {
|
|
17136
|
-
return collectByLogin(items, loginOf).filter((group) => group.length > 1);
|
|
17137
|
-
}
|
|
17138
|
-
function describeAccountHolder(summary) {
|
|
17139
|
-
const organisation = describeAccountOrganisation(summary);
|
|
17140
|
-
if (summary.emailAddress === void 0) {
|
|
17141
|
-
return organisation;
|
|
17142
|
-
}
|
|
17143
|
-
return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
|
|
17144
|
-
}
|
|
17188
|
+
init_identityAccounts();
|
|
17145
17189
|
|
|
17146
17190
|
// src/codex/siwcStore.ts
|
|
17147
17191
|
var import_zod26 = require("zod");
|
|
@@ -17495,6 +17539,27 @@ init_providerAliases();
|
|
|
17495
17539
|
init_providersStore();
|
|
17496
17540
|
init_guard();
|
|
17497
17541
|
init_identity();
|
|
17542
|
+
|
|
17543
|
+
// src/poolSharedLogins.ts
|
|
17544
|
+
init_identityAccounts();
|
|
17545
|
+
init_identity();
|
|
17546
|
+
function sharedLoginMembers(members, accountOf) {
|
|
17547
|
+
const known = members.flatMap((identity) => {
|
|
17548
|
+
const account = poolNameOf(identity) === void 0 ? accountOf(identity) : void 0;
|
|
17549
|
+
return account === void 0 ? [] : [{ identity, account }];
|
|
17550
|
+
});
|
|
17551
|
+
return groupSharedLogins(known, (entry) => loginKeyOf(entry.account)).flatMap((group) => {
|
|
17552
|
+
const first = group[0];
|
|
17553
|
+
return first === void 0 ? [] : [{ members: group.map((entry) => entry.identity), account: first.account }];
|
|
17554
|
+
});
|
|
17555
|
+
}
|
|
17556
|
+
function describeSharedLoginMembers(poolName, shared) {
|
|
17557
|
+
const summary = summariseIdentityAccount(shared.account);
|
|
17558
|
+
const holder = summary === void 0 ? void 0 : describeAccountHolder(summary);
|
|
17559
|
+
return `Pool "${poolName}" lists ${joinNames(shared.members)}, which hold the same login${holder === void 0 ? "" : ` (${holder})`}, so they share one quota window. A launch ranks them as one candidate and never fails over from one to another; keep one of them in the pool.`;
|
|
17560
|
+
}
|
|
17561
|
+
|
|
17562
|
+
// src/doctorReport.ts
|
|
17498
17563
|
init_extends();
|
|
17499
17564
|
var DOCTOR_SEVERITIES = ["pass", "warn", "fail"];
|
|
17500
17565
|
var DOCTOR_SECTIONS = [
|
|
@@ -17517,10 +17582,6 @@ var DOCTOR_SECTIONS = [
|
|
|
17517
17582
|
"frontdoor",
|
|
17518
17583
|
"models"
|
|
17519
17584
|
];
|
|
17520
|
-
function joinNames(names) {
|
|
17521
|
-
const last = names.at(-1);
|
|
17522
|
-
return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
|
|
17523
|
-
}
|
|
17524
17585
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
17525
17586
|
if (status.status !== "shadowed") {
|
|
17526
17587
|
return status;
|
|
@@ -17833,6 +17894,7 @@ function runDoctor(params) {
|
|
|
17833
17894
|
}
|
|
17834
17895
|
const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
|
|
17835
17896
|
const pools = validatedGlobalConfig?.ok === true ? validatedGlobalConfig.data.pools ?? {} : {};
|
|
17897
|
+
const accountByIdentity = new Map(params.identities.flatMap((entry) => validIdentityNames.has(entry.name) && entry.account !== void 0 ? [[entry.name, entry.account]] : []));
|
|
17836
17898
|
for (const [poolName, pool] of Object.entries(pools)) {
|
|
17837
17899
|
const missing = pool.identities.filter((member) => poolNameOf(poolMemberIdentity(member)) === void 0 && !validIdentityNames.has(poolMemberIdentity(member)));
|
|
17838
17900
|
const undefinedPools = pool.identities.flatMap((member) => {
|
|
@@ -17848,6 +17910,9 @@ function runDoctor(params) {
|
|
|
17848
17910
|
];
|
|
17849
17911
|
push("pool", "fail", `Pool "${poolName}" names ${problems.join(" and ")}.`, poolName);
|
|
17850
17912
|
}
|
|
17913
|
+
for (const shared of sharedLoginMembers(pool.identities.map(poolMemberIdentity), (identityName) => accountByIdentity.get(identityName))) {
|
|
17914
|
+
push("pool", "warn", describeSharedLoginMembers(poolName, shared), poolName);
|
|
17915
|
+
}
|
|
17851
17916
|
}
|
|
17852
17917
|
const reportedCycles = /* @__PURE__ */ new Set();
|
|
17853
17918
|
for (const poolName of Object.keys(pools).sort()) {
|
package/dist/index.mjs
CHANGED
|
@@ -4740,7 +4740,7 @@ var init_package = __esm({
|
|
|
4740
4740
|
"package.json"() {
|
|
4741
4741
|
package_default = {
|
|
4742
4742
|
name: "agent-shim",
|
|
4743
|
-
version: "11.
|
|
4743
|
+
version: "11.80.0",
|
|
4744
4744
|
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.",
|
|
4745
4745
|
license: "MIT",
|
|
4746
4746
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -7012,6 +7012,78 @@ var init_account = __esm({
|
|
|
7012
7012
|
}
|
|
7013
7013
|
});
|
|
7014
7014
|
|
|
7015
|
+
// src/usage/accountDescription.ts
|
|
7016
|
+
function describeAccountOrganisation(account) {
|
|
7017
|
+
const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
|
|
7018
|
+
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
7019
|
+
}
|
|
7020
|
+
var init_accountDescription = __esm({
|
|
7021
|
+
"src/usage/accountDescription.ts"() {
|
|
7022
|
+
"use strict";
|
|
7023
|
+
}
|
|
7024
|
+
});
|
|
7025
|
+
|
|
7026
|
+
// src/identityAccounts.ts
|
|
7027
|
+
function readIdentityAccount(fs14, identitiesDir, identity) {
|
|
7028
|
+
try {
|
|
7029
|
+
const account = readAccountMetadata(fs14, identitiesDir, identity);
|
|
7030
|
+
return account === void 0 ? void 0 : { account };
|
|
7031
|
+
} catch (error) {
|
|
7032
|
+
if (error instanceof AccountMetadataError) {
|
|
7033
|
+
return { problem: error.message };
|
|
7034
|
+
}
|
|
7035
|
+
throw error;
|
|
7036
|
+
}
|
|
7037
|
+
}
|
|
7038
|
+
function summariseIdentityAccount(account) {
|
|
7039
|
+
const summary = {
|
|
7040
|
+
...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
|
|
7041
|
+
...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
|
|
7042
|
+
...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
|
|
7043
|
+
...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
|
|
7044
|
+
...account.billingType === void 0 ? {} : { billingType: account.billingType },
|
|
7045
|
+
...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
|
|
7046
|
+
};
|
|
7047
|
+
return Object.keys(summary).length === 0 ? void 0 : summary;
|
|
7048
|
+
}
|
|
7049
|
+
function loginKeyOf(account) {
|
|
7050
|
+
return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
|
|
7051
|
+
}
|
|
7052
|
+
function collectByLogin(items, loginOf) {
|
|
7053
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7054
|
+
for (const item of items) {
|
|
7055
|
+
const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
|
|
7056
|
+
const group = groups.get(key);
|
|
7057
|
+
if (group === void 0) {
|
|
7058
|
+
groups.set(key, [item]);
|
|
7059
|
+
} else {
|
|
7060
|
+
group.push(item);
|
|
7061
|
+
}
|
|
7062
|
+
}
|
|
7063
|
+
return [...groups.values()];
|
|
7064
|
+
}
|
|
7065
|
+
function groupSharedLogins(items, loginOf) {
|
|
7066
|
+
return collectByLogin(items, loginOf).filter((group) => group.length > 1);
|
|
7067
|
+
}
|
|
7068
|
+
function joinNames(names) {
|
|
7069
|
+
const last = names.at(-1);
|
|
7070
|
+
return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
|
|
7071
|
+
}
|
|
7072
|
+
function describeAccountHolder(summary) {
|
|
7073
|
+
const organisation = describeAccountOrganisation(summary);
|
|
7074
|
+
if (summary.emailAddress === void 0) {
|
|
7075
|
+
return organisation;
|
|
7076
|
+
}
|
|
7077
|
+
return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
|
|
7078
|
+
}
|
|
7079
|
+
var init_identityAccounts = __esm({
|
|
7080
|
+
"src/identityAccounts.ts"() {
|
|
7081
|
+
"use strict";
|
|
7082
|
+
init_account();
|
|
7083
|
+
init_accountDescription();
|
|
7084
|
+
}
|
|
7085
|
+
});
|
|
7086
|
+
|
|
7015
7087
|
// src/usage/rateLimit.ts
|
|
7016
7088
|
function epochSecondsToIso(value) {
|
|
7017
7089
|
if (value === void 0) {
|
|
@@ -7379,6 +7451,9 @@ function assessRefusedNested(member, nested, nowMs) {
|
|
|
7379
7451
|
};
|
|
7380
7452
|
}
|
|
7381
7453
|
function assess(member, nowMs) {
|
|
7454
|
+
if (member.sameLoginAs !== void 0) {
|
|
7455
|
+
return { candidate: { identity: member.identity, class: "ineligible", plan: planOf(member.account), reasons: [`same login as ${member.sameLoginAs}`] } };
|
|
7456
|
+
}
|
|
7382
7457
|
const nested = member.nested;
|
|
7383
7458
|
if (nested === void 0) {
|
|
7384
7459
|
return assessIdentity(member, nowMs);
|
|
@@ -7853,6 +7928,29 @@ function loadPoolMembers(fs14, paths, identities, nowMs) {
|
|
|
7853
7928
|
}
|
|
7854
7929
|
});
|
|
7855
7930
|
}
|
|
7931
|
+
function snapshotAgeKey(member) {
|
|
7932
|
+
return member.snapshot === void 0 ? Number.NEGATIVE_INFINITY : Date.parse(member.snapshot.updatedAt);
|
|
7933
|
+
}
|
|
7934
|
+
function collapseSharedLogins(members) {
|
|
7935
|
+
const groups = groupSharedLogins(members, (member) => member.nested === void 0 && member.account !== void 0 ? loginKeyOf(member.account) : void 0);
|
|
7936
|
+
const representativeOf = /* @__PURE__ */ new Map();
|
|
7937
|
+
for (const group of groups) {
|
|
7938
|
+
const representative = group.reduce((freshest, member) => snapshotAgeKey(member) > snapshotAgeKey(freshest) ? member : freshest);
|
|
7939
|
+
for (const member of group) {
|
|
7940
|
+
if (member !== representative) {
|
|
7941
|
+
representativeOf.set(member, representative.identity);
|
|
7942
|
+
}
|
|
7943
|
+
}
|
|
7944
|
+
}
|
|
7945
|
+
return members.map((member) => {
|
|
7946
|
+
const representative = representativeOf.get(member);
|
|
7947
|
+
return representative === void 0 ? member : { ...member, sameLoginAs: representative };
|
|
7948
|
+
});
|
|
7949
|
+
}
|
|
7950
|
+
function stickyOnRepresentative(sticky, members) {
|
|
7951
|
+
const twin = sticky === void 0 ? void 0 : members.find((member) => member.identity === sticky.identity && member.sameLoginAs !== void 0);
|
|
7952
|
+
return sticky === void 0 || twin?.sameLoginAs === void 0 ? sticky : { ...sticky, identity: twin.sameLoginAs };
|
|
7953
|
+
}
|
|
7856
7954
|
function rankPoolGraph(input) {
|
|
7857
7955
|
return rankNamedPool(input, input.poolName, [], input.sticky);
|
|
7858
7956
|
}
|
|
@@ -7880,13 +7978,15 @@ function rankNamedPool(input, poolName, stack, sticky) {
|
|
|
7880
7978
|
members.push(typeof entry === "string" ? nested.member : { ...nested.member, policy: entry.when });
|
|
7881
7979
|
missing.push(...nested.missing);
|
|
7882
7980
|
}
|
|
7981
|
+
const collapsed = collapseSharedLogins(members);
|
|
7982
|
+
const stickyPick = stickyOnRepresentative(sticky, collapsed);
|
|
7883
7983
|
const ranking = rankPool({
|
|
7884
|
-
members,
|
|
7984
|
+
members: collapsed,
|
|
7885
7985
|
nowMs: input.nowMs,
|
|
7886
7986
|
resuming: input.resuming,
|
|
7887
7987
|
...input.resumeContextTokens === void 0 ? {} : { resumeContextTokens: input.resumeContextTokens },
|
|
7888
7988
|
...input.committedContextTokens === void 0 ? {} : { committedContextTokens: input.committedContextTokens },
|
|
7889
|
-
...
|
|
7989
|
+
...stickyPick === void 0 ? {} : { sticky: stickyPick },
|
|
7890
7990
|
...pool.preference === void 0 ? {} : { preference: pool.preference }
|
|
7891
7991
|
});
|
|
7892
7992
|
return { ranking, missing };
|
|
@@ -7972,6 +8072,7 @@ var init_poolPick = __esm({
|
|
|
7972
8072
|
init_cliError();
|
|
7973
8073
|
init_identity();
|
|
7974
8074
|
init_schema();
|
|
8075
|
+
init_identityAccounts();
|
|
7975
8076
|
init_account();
|
|
7976
8077
|
init_pick();
|
|
7977
8078
|
init_read();
|
|
@@ -10699,7 +10800,8 @@ var init_argv = __esm({
|
|
|
10699
10800
|
{ flag: "--remote-control", key: "remoteControl" },
|
|
10700
10801
|
{ flag: "--wait", key: "wait" },
|
|
10701
10802
|
{ flag: "--pause", key: "pause" },
|
|
10702
|
-
{ flag: "--explain", key: "explain" }
|
|
10803
|
+
{ flag: "--explain", key: "explain" },
|
|
10804
|
+
{ flag: "--pick", key: "pick" }
|
|
10703
10805
|
];
|
|
10704
10806
|
LAUNCHER_FLAG_NAMES = [
|
|
10705
10807
|
...VALUED_FLAGS,
|
|
@@ -11787,7 +11889,7 @@ function decideLaunch(params, readOnly) {
|
|
|
11787
11889
|
const configDirEscapeHatchApplies = env.CLAUDE_CONFIG_DIR !== void 0 && env.CLAUDE_CONFIG_DIR !== "";
|
|
11788
11890
|
const decidedIdentity = decideIdentity({
|
|
11789
11891
|
env,
|
|
11790
|
-
argv0Identity: parsedArgv.identity,
|
|
11892
|
+
argv0Identity: params.pickedIdentity ?? parsedArgv.identity,
|
|
11791
11893
|
directoryPinnedIdentity: params.directoryPinnedIdentity,
|
|
11792
11894
|
readActiveIdentityFile: () => {
|
|
11793
11895
|
const raw = fs14.readFileUtf8(paths.activeIdentityFile);
|
|
@@ -16668,65 +16770,7 @@ init_realPorts();
|
|
|
16668
16770
|
import fs11 from "node:fs";
|
|
16669
16771
|
import path29 from "node:path";
|
|
16670
16772
|
init_signIn();
|
|
16671
|
-
|
|
16672
|
-
// src/identityAccounts.ts
|
|
16673
|
-
init_account();
|
|
16674
|
-
|
|
16675
|
-
// src/usage/accountDescription.ts
|
|
16676
|
-
function describeAccountOrganisation(account) {
|
|
16677
|
-
const parts = [account.organizationName, account.organizationType].filter((part) => part !== void 0);
|
|
16678
|
-
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
16679
|
-
}
|
|
16680
|
-
|
|
16681
|
-
// src/identityAccounts.ts
|
|
16682
|
-
function readIdentityAccount(fs14, identitiesDir, identity) {
|
|
16683
|
-
try {
|
|
16684
|
-
const account = readAccountMetadata(fs14, identitiesDir, identity);
|
|
16685
|
-
return account === void 0 ? void 0 : { account };
|
|
16686
|
-
} catch (error) {
|
|
16687
|
-
if (error instanceof AccountMetadataError) {
|
|
16688
|
-
return { problem: error.message };
|
|
16689
|
-
}
|
|
16690
|
-
throw error;
|
|
16691
|
-
}
|
|
16692
|
-
}
|
|
16693
|
-
function summariseIdentityAccount(account) {
|
|
16694
|
-
const summary = {
|
|
16695
|
-
...account.emailAddress === void 0 ? {} : { emailAddress: account.emailAddress },
|
|
16696
|
-
...account.organizationName === void 0 ? {} : { organizationName: account.organizationName },
|
|
16697
|
-
...account.organizationType === void 0 ? {} : { organizationType: account.organizationType },
|
|
16698
|
-
...account.organizationRole === void 0 ? {} : { organizationRole: account.organizationRole },
|
|
16699
|
-
...account.billingType === void 0 ? {} : { billingType: account.billingType },
|
|
16700
|
-
...account.seatTier === void 0 ? {} : { seatTier: account.seatTier }
|
|
16701
|
-
};
|
|
16702
|
-
return Object.keys(summary).length === 0 ? void 0 : summary;
|
|
16703
|
-
}
|
|
16704
|
-
function loginKeyOf(account) {
|
|
16705
|
-
return account.accountUuid === void 0 ? void 0 : JSON.stringify([account.accountUuid, account.organizationUuid ?? null]);
|
|
16706
|
-
}
|
|
16707
|
-
function collectByLogin(items, loginOf) {
|
|
16708
|
-
const groups = /* @__PURE__ */ new Map();
|
|
16709
|
-
for (const item of items) {
|
|
16710
|
-
const key = loginOf(item) ?? /* @__PURE__ */ Symbol("no login");
|
|
16711
|
-
const group = groups.get(key);
|
|
16712
|
-
if (group === void 0) {
|
|
16713
|
-
groups.set(key, [item]);
|
|
16714
|
-
} else {
|
|
16715
|
-
group.push(item);
|
|
16716
|
-
}
|
|
16717
|
-
}
|
|
16718
|
-
return [...groups.values()];
|
|
16719
|
-
}
|
|
16720
|
-
function groupSharedLogins(items, loginOf) {
|
|
16721
|
-
return collectByLogin(items, loginOf).filter((group) => group.length > 1);
|
|
16722
|
-
}
|
|
16723
|
-
function describeAccountHolder(summary) {
|
|
16724
|
-
const organisation = describeAccountOrganisation(summary);
|
|
16725
|
-
if (summary.emailAddress === void 0) {
|
|
16726
|
-
return organisation;
|
|
16727
|
-
}
|
|
16728
|
-
return organisation === void 0 ? summary.emailAddress : `${summary.emailAddress} (${organisation})`;
|
|
16729
|
-
}
|
|
16773
|
+
init_identityAccounts();
|
|
16730
16774
|
|
|
16731
16775
|
// src/codex/siwcStore.ts
|
|
16732
16776
|
import { z as z26 } from "zod";
|
|
@@ -17080,6 +17124,27 @@ init_providerAliases();
|
|
|
17080
17124
|
init_providersStore();
|
|
17081
17125
|
init_guard();
|
|
17082
17126
|
init_identity();
|
|
17127
|
+
|
|
17128
|
+
// src/poolSharedLogins.ts
|
|
17129
|
+
init_identityAccounts();
|
|
17130
|
+
init_identity();
|
|
17131
|
+
function sharedLoginMembers(members, accountOf) {
|
|
17132
|
+
const known = members.flatMap((identity) => {
|
|
17133
|
+
const account = poolNameOf(identity) === void 0 ? accountOf(identity) : void 0;
|
|
17134
|
+
return account === void 0 ? [] : [{ identity, account }];
|
|
17135
|
+
});
|
|
17136
|
+
return groupSharedLogins(known, (entry) => loginKeyOf(entry.account)).flatMap((group) => {
|
|
17137
|
+
const first = group[0];
|
|
17138
|
+
return first === void 0 ? [] : [{ members: group.map((entry) => entry.identity), account: first.account }];
|
|
17139
|
+
});
|
|
17140
|
+
}
|
|
17141
|
+
function describeSharedLoginMembers(poolName, shared) {
|
|
17142
|
+
const summary = summariseIdentityAccount(shared.account);
|
|
17143
|
+
const holder = summary === void 0 ? void 0 : describeAccountHolder(summary);
|
|
17144
|
+
return `Pool "${poolName}" lists ${joinNames(shared.members)}, which hold the same login${holder === void 0 ? "" : ` (${holder})`}, so they share one quota window. A launch ranks them as one candidate and never fails over from one to another; keep one of them in the pool.`;
|
|
17145
|
+
}
|
|
17146
|
+
|
|
17147
|
+
// src/doctorReport.ts
|
|
17083
17148
|
init_extends();
|
|
17084
17149
|
var DOCTOR_SEVERITIES = ["pass", "warn", "fail"];
|
|
17085
17150
|
var DOCTOR_SECTIONS = [
|
|
@@ -17102,10 +17167,6 @@ var DOCTOR_SECTIONS = [
|
|
|
17102
17167
|
"frontdoor",
|
|
17103
17168
|
"models"
|
|
17104
17169
|
];
|
|
17105
|
-
function joinNames(names) {
|
|
17106
|
-
const last = names.at(-1);
|
|
17107
|
-
return names.length < 2 || last === void 0 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${last}`;
|
|
17108
|
-
}
|
|
17109
17170
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
17110
17171
|
if (status.status !== "shadowed") {
|
|
17111
17172
|
return status;
|
|
@@ -17418,6 +17479,7 @@ function runDoctor(params) {
|
|
|
17418
17479
|
}
|
|
17419
17480
|
const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
|
|
17420
17481
|
const pools = validatedGlobalConfig?.ok === true ? validatedGlobalConfig.data.pools ?? {} : {};
|
|
17482
|
+
const accountByIdentity = new Map(params.identities.flatMap((entry) => validIdentityNames.has(entry.name) && entry.account !== void 0 ? [[entry.name, entry.account]] : []));
|
|
17421
17483
|
for (const [poolName, pool] of Object.entries(pools)) {
|
|
17422
17484
|
const missing = pool.identities.filter((member) => poolNameOf(poolMemberIdentity(member)) === void 0 && !validIdentityNames.has(poolMemberIdentity(member)));
|
|
17423
17485
|
const undefinedPools = pool.identities.flatMap((member) => {
|
|
@@ -17433,6 +17495,9 @@ function runDoctor(params) {
|
|
|
17433
17495
|
];
|
|
17434
17496
|
push("pool", "fail", `Pool "${poolName}" names ${problems.join(" and ")}.`, poolName);
|
|
17435
17497
|
}
|
|
17498
|
+
for (const shared of sharedLoginMembers(pool.identities.map(poolMemberIdentity), (identityName) => accountByIdentity.get(identityName))) {
|
|
17499
|
+
push("pool", "warn", describeSharedLoginMembers(poolName, shared), poolName);
|
|
17500
|
+
}
|
|
17436
17501
|
}
|
|
17437
17502
|
const reportedCycles = /* @__PURE__ */ new Set();
|
|
17438
17503
|
for (const poolName of Object.keys(pools).sort()) {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -14032,6 +14032,8 @@ interface PrepareLaunchParams {
|
|
|
14032
14032
|
readonly log: LogPort;
|
|
14033
14033
|
/** Discovers the real `claude` binary to spawn. Injected so `runLauncher` never depends on `src/versionDiscovery.ts`'s own filesystem/PATH inputs directly — the caller (`src/cli.ts`) wires the real discovery, tests wire a fake that returns a fixed path. */
|
|
14034
14034
|
readonly resolveClaudeBinary: ClaudeBinaryResolver;
|
|
14035
|
+
/** The identity (or `pool:<name>`) the person chose at the identity picker, which takes the place of whatever the arguments named: the question was asked because that choice was not settled. */
|
|
14036
|
+
readonly pickedIdentity?: string;
|
|
14035
14037
|
/** An identity pinned to `$PWD` by a directory rule. Accepted as an already-resolved value — the rules-loading code that produces it lands in Phase 4/5. */
|
|
14036
14038
|
readonly directoryPinnedIdentity?: string;
|
|
14037
14039
|
/** A directory rule's `configProfile` selection for `$PWD`. Accepted as an already-resolved value for the same reason. */
|
|
@@ -26334,6 +26336,8 @@ interface PoolMember {
|
|
|
26334
26336
|
readonly records: readonly UsageRecord[];
|
|
26335
26337
|
/** Present when this member stands for a nested `pool:<name>` entry rather than a direct identity. */
|
|
26336
26338
|
readonly nested?: NestedContribution;
|
|
26339
|
+
/** The member that represents this member's login in the ranking, when both hold the same login and so draw on one quota window. This member is then ineligible as a candidate of its own, since ranking it beside its representative would count one window twice and could fail over to a login that is limited too. */
|
|
26340
|
+
readonly sameLoginAs?: string;
|
|
26337
26341
|
/** The member entry's policy condition, from the object form of a pool member: definite false skips the member as ineligible by policy, indeterminate demotes it to last within its class, naming the missing fact either way. */
|
|
26338
26342
|
readonly policy?: WhenCondition;
|
|
26339
26343
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-shim",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.80.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>",
|