@shardflux/cli 0.22.0 → 0.23.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/CHANGELOG.md +98 -0
- package/README.md +37 -10
- package/dist/acp-journal.js +58 -6
- package/dist/acp-login.d.ts +10 -3
- package/dist/acp-login.js +31 -4
- package/dist/acp-models.d.ts +6 -1
- package/dist/acp-models.js +21 -16
- package/dist/acp.js +60 -7
- package/dist/agent-commands.js +29 -15
- package/dist/agent-target-commands.d.ts +20 -1
- package/dist/agent-target-commands.js +49 -18
- package/dist/agent-targets.js +6 -1
- package/dist/args.d.ts +1 -1
- package/dist/args.js +8 -7
- package/dist/cli-proxy-forward.d.ts +0 -2
- package/dist/cli-proxy-forward.js +2 -9
- package/dist/cli-proxy-hub.d.ts +22 -30
- package/dist/cli-proxy-hub.js +65 -94
- package/dist/cli-proxy.d.ts +12 -38
- package/dist/cli-proxy.js +51 -168
- package/dist/codex-cloud.d.ts +9 -4
- package/dist/codex-cloud.js +47 -14
- package/dist/codex-linux.d.ts +14 -0
- package/dist/codex-linux.js +69 -0
- package/dist/command-center-actions.js +13 -4
- package/dist/command-center-model.d.ts +2 -0
- package/dist/command-center-model.js +6 -1
- package/dist/command-center.js +3 -3
- package/dist/commands.js +2 -2
- package/dist/conversation-terminal.js +3 -2
- package/dist/conversations.d.ts +14 -1
- package/dist/conversations.js +55 -6
- package/dist/desktop-linux.js +16 -3
- package/dist/errors.d.ts +2 -1
- package/dist/errors.js +5 -2
- package/dist/filesystem-commands.js +25 -3
- package/dist/menubar.d.ts +6 -5
- package/dist/menubar.js +38 -28
- package/dist/move-worktrees.d.ts +13 -7
- package/dist/move-worktrees.js +68 -17
- package/dist/move.js +20 -25
- package/dist/onboarding.js +4 -2
- package/dist/prepared-repository.d.ts +2 -0
- package/dist/prepared-repository.js +1 -1
- package/dist/prepared-repository.py +3 -2
- package/dist/project-preparation.d.ts +14 -0
- package/dist/project-preparation.js +16 -4
- package/dist/project-sync.d.ts +3 -0
- package/dist/project-sync.js +4 -1
- package/dist/t3-launch.d.ts +2 -1
- package/dist/t3-launch.js +11 -2
- package/dist/t3-proxy.d.ts +81 -0
- package/dist/t3-proxy.js +325 -0
- package/dist/terminal-apps.d.ts +11 -0
- package/dist/terminal-apps.js +18 -5
- package/dist/update.d.ts +2 -0
- package/dist/update.js +32 -6
- package/package.json +5 -5
package/dist/cli-proxy-hub.d.ts
CHANGED
|
@@ -40,12 +40,6 @@ export declare const HUB_INFO_FILE = "cli-proxy.json";
|
|
|
40
40
|
/** Paths in the hub workspace. */
|
|
41
41
|
export declare const HUB_DIR = "/home/user/.cli-proxy-hub";
|
|
42
42
|
export declare const HUB_AUTH_DIR = "/home/user/.cli-proxy-api";
|
|
43
|
-
/**
|
|
44
|
-
* Each provider's active login: `{ "<provider type>": "<auth file, relative to the auth directory>" }` (0600). The one
|
|
45
|
-
* login of each provider that stays enabled in the hub; every other login of that provider is kept there with
|
|
46
|
-
* `"disabled": true`. Written by the CLI and by the hub's sf-agent, both under the hub's `logins.lock`.
|
|
47
|
-
*/
|
|
48
|
-
export declare const HUB_ACTIVE_FILE = "/home/user/.cli-proxy-hub/active.json";
|
|
49
43
|
export interface HubInfo {
|
|
50
44
|
version: 1;
|
|
51
45
|
api_url: string;
|
|
@@ -238,7 +232,7 @@ export interface EnsureResult {
|
|
|
238
232
|
callbackReplaced: boolean;
|
|
239
233
|
/** The config written now; null when the hub kept its own. */
|
|
240
234
|
config: HubConfig | null;
|
|
241
|
-
/** The hub's logins,
|
|
235
|
+
/** The hub's logins, each enabled or disabled as the user left it. */
|
|
242
236
|
logins: HubLogin[];
|
|
243
237
|
}
|
|
244
238
|
/** The hub could not be made ready (exit 1, code `cli_proxy_hub`). */
|
|
@@ -254,10 +248,10 @@ export declare function ensureCliProxyHub(cloud: Shardflux | HubCloud, opts: Ens
|
|
|
254
248
|
/**
|
|
255
249
|
* A login in the hub: one auth file. Only its provider, email and state are ever shown, never its tokens.
|
|
256
250
|
*
|
|
257
|
-
* CLIProxyAPI
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
251
|
+
* The hub is the user's own CLIProxyAPI: it routes requests across the enabled logins exactly as its config says
|
|
252
|
+
* (round-robin, fill-first, session affinity, retries), the user's routing and their choice. A login with
|
|
253
|
+
* `"disabled": true` stays in the hub and is never used (CLIProxyAPI 8.0.23); `shard cli-proxy use` switches logins on
|
|
254
|
+
* and off.
|
|
261
255
|
*/
|
|
262
256
|
export interface HubLogin {
|
|
263
257
|
/** Path relative to the auth directory. */
|
|
@@ -267,15 +261,10 @@ export interface HubLogin {
|
|
|
267
261
|
email: string | null;
|
|
268
262
|
/** The file carries `"disabled": true`. */
|
|
269
263
|
disabled: boolean;
|
|
270
|
-
/**
|
|
264
|
+
/** It serves requests: enabled, with a provider, and the user's (not a file the retired account pool router left). */
|
|
271
265
|
active: boolean;
|
|
272
266
|
}
|
|
273
|
-
/**
|
|
274
|
-
export declare function parseActive(text: string | null): Record<string, string>;
|
|
275
|
-
/**
|
|
276
|
-
* The hub's logins, oldest first, as they are (nothing changes): `active` marks the one active.json names while it is
|
|
277
|
-
* enabled. Files the retired account pool router left are not listed (they stay disabled).
|
|
278
|
-
*/
|
|
267
|
+
/** The hub's logins, oldest first, as they are (nothing changes). Files the retired account pool router left are not listed. */
|
|
279
268
|
export declare function listHubLogins(ws: HubWorkspace): Promise<HubLogin[]>;
|
|
280
269
|
/** The auth file's text with `disabled` set (true) or removed (false), every other field kept; null when unchanged. */
|
|
281
270
|
export declare function withDisabled(text: string, disabled: boolean): string | null;
|
|
@@ -287,22 +276,25 @@ export declare function withDisabled(text: string, disabled: boolean): string |
|
|
|
287
276
|
* or `gone`.
|
|
288
277
|
*/
|
|
289
278
|
export declare const SET_AUTH_SCRIPT = "set -euo pipefail\na=/home/user/.cli-proxy-api; rel=$1; want=$2\ncase \"$rel\" in ''|/*|*..*) echo \"not a path inside the auth folder\" >&2; exit 2 ;; esac\nf=\"$a/$rel\"\n[ -f \"$f\" ] || { echo gone; exit 0; }\ntmp=$(mktemp \"$(dirname \"$f\")/.writing.XXXXXX\"); trap 'rm -f \"$tmp\"' EXIT\nchmod 0600 \"$tmp\"; cat > \"$tmp\"; touch -r \"$f\" \"$tmp\"\nif [ \"$(sha256sum < \"$f\" | cut -d' ' -f1)\" != \"$want\" ]; then echo changed; exit 0; fi\nmv -f \"$tmp\" \"$f\"; trap - EXIT\necho written\n";
|
|
290
|
-
/** Writes active.json atomically (0600) from standard input. */
|
|
291
|
-
export declare const ACTIVE_SCRIPT = "set -euo pipefail\ndir=/home/user/.cli-proxy-hub; mkdir -p \"$dir\"; umask 077\ntmp=$(mktemp \"$dir/.active.XXXXXX\"); trap 'rm -f \"$tmp\"' EXIT\ncat > \"$tmp\"; mv -f \"$tmp\" \"$dir/active.json\"; trap - EXIT\necho written\n";
|
|
292
279
|
/**
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
* active.json records the choice first, then every other login of that provider is disabled (all other fields kept), then
|
|
296
|
-
* the chosen one is enabled. Files without a `type` are left alone; the retired pool router's files are
|
|
297
|
-
* disabled and never chosen. Returns the logins as they are now, oldest first (without the pool router's).
|
|
280
|
+
* Under the hub's `logins` lock: the retired account pool router's files stay disabled (they were never the user's);
|
|
281
|
+
* every other login keeps the state its owner gave it. Returns the logins as they are now, oldest first.
|
|
298
282
|
*/
|
|
299
|
-
export declare function
|
|
283
|
+
export declare function settleHubLogins(ws: HubWorkspace): Promise<HubLogin[]>;
|
|
300
284
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
285
|
+
* The user's choice of which logins serve requests (`shard cli-proxy use`; call it under the hub's `logins` lock).
|
|
286
|
+
* `only[provider]` names the one login of that provider that stays enabled (it is enabled first, then the provider's
|
|
287
|
+
* other logins are disabled, so the provider is never without one); `all` enables every login of the hub, which then
|
|
288
|
+
* routes across them as its config says. Providers the choice does not name are left as they are. Returns the logins
|
|
289
|
+
* as they are now, oldest first.
|
|
304
290
|
*/
|
|
305
|
-
export declare function
|
|
291
|
+
export declare function useHubLogins(ws: HubWorkspace, choice: {
|
|
292
|
+
only?: Record<string, string>;
|
|
293
|
+
all?: boolean;
|
|
294
|
+
}): Promise<HubLogin[]>;
|
|
295
|
+
/** Where CLI 0.22.0 reads each provider's one login ({"<provider>": "<auth file>"}); written atomically (0600). */
|
|
296
|
+
export declare const LEGACY_ACTIVE_FILE = "/home/user/.cli-proxy-hub/active.json";
|
|
297
|
+
export declare const LEGACY_ACTIVE_SCRIPT = "set -euo pipefail\ndir=/home/user/.cli-proxy-hub; mkdir -p \"$dir\"; umask 077\ntmp=$(mktemp \"$dir/.active.XXXXXX\"); trap 'rm -f \"$tmp\"' EXIT\ncat > \"$tmp\"; mv -f \"$tmp\" \"$dir/active.json\"; trap - EXIT\necho written\n";
|
|
306
298
|
/** Removes the router's files; prints `retired` when its hub token was there (its sf-agent then starts again without it). */
|
|
307
299
|
export declare const RETIRE_POOL_SCRIPT: string;
|
|
308
300
|
/** The config without the router's own client key (`api-keys`, or v8 `access.api-keys`); the text itself when absent. */
|
package/dist/cli-proxy-hub.js
CHANGED
|
@@ -50,12 +50,6 @@ export const HUB_INFO_FILE = 'cli-proxy.json';
|
|
|
50
50
|
/** Paths in the hub workspace. */
|
|
51
51
|
export const HUB_DIR = '/home/user/.cli-proxy-hub';
|
|
52
52
|
export const HUB_AUTH_DIR = '/home/user/.cli-proxy-api';
|
|
53
|
-
/**
|
|
54
|
-
* Each provider's active login: `{ "<provider type>": "<auth file, relative to the auth directory>" }` (0600). The one
|
|
55
|
-
* login of each provider that stays enabled in the hub; every other login of that provider is kept there with
|
|
56
|
-
* `"disabled": true`. Written by the CLI and by the hub's sf-agent, both under the hub's `logins.lock`.
|
|
57
|
-
*/
|
|
58
|
-
export const HUB_ACTIVE_FILE = `${HUB_DIR}/active.json`;
|
|
59
53
|
const CALLBACK_PATH = /^\/__shardflux\/callback\/sfcb_[A-Za-z0-9_-]+\/$/;
|
|
60
54
|
function callbackUrlOk(cb) {
|
|
61
55
|
try {
|
|
@@ -661,8 +655,8 @@ export async function ensureCliProxyHub(cloud, opts) {
|
|
|
661
655
|
// A hub that once ran the retired account pool router: its files go, and its sf-agent starts again without it.
|
|
662
656
|
if (await retirePool(ws))
|
|
663
657
|
restart = true;
|
|
664
|
-
//
|
|
665
|
-
const logins = await ws.withLock('logins', () =>
|
|
658
|
+
// The user's logins stay as they are; only the retired account pool router's files are kept disabled.
|
|
659
|
+
const logins = await ws.withLock('logins', () => settleHubLogins(ws));
|
|
666
660
|
const started = await ws.sh(START_SCRIPT, [restart ? '1' : '0'], { timeoutMs: 180_000 });
|
|
667
661
|
const startLines = started.stdout.trim().split('\n');
|
|
668
662
|
const state = startLines.find((l) => ['running', 'started', 'restarted'].includes(l));
|
|
@@ -684,23 +678,6 @@ export async function ensureCliProxyHub(cloud, opts) {
|
|
|
684
678
|
writeHubInfo(opts.dir, info);
|
|
685
679
|
return { info, created, process: state, callbackReplaced, config, logins };
|
|
686
680
|
}
|
|
687
|
-
/** Parses active.json: provider type → auth file. Anything else (missing, not JSON, other values) counts as empty. */
|
|
688
|
-
export function parseActive(text) {
|
|
689
|
-
let v;
|
|
690
|
-
try {
|
|
691
|
-
v = JSON.parse(text ?? '');
|
|
692
|
-
}
|
|
693
|
-
catch {
|
|
694
|
-
return {};
|
|
695
|
-
}
|
|
696
|
-
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
697
|
-
return {};
|
|
698
|
-
const out = {};
|
|
699
|
-
for (const [k, f] of Object.entries(v))
|
|
700
|
-
if (typeof f === 'string' && f)
|
|
701
|
-
out[k.toLowerCase()] = f;
|
|
702
|
-
return out;
|
|
703
|
-
}
|
|
704
681
|
const docOf = (text) => {
|
|
705
682
|
try {
|
|
706
683
|
const v = JSON.parse(text ?? '');
|
|
@@ -710,16 +687,13 @@ const docOf = (text) => {
|
|
|
710
687
|
return null;
|
|
711
688
|
}
|
|
712
689
|
};
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
* enabled. Files the retired account pool router left are not listed (they stay disabled).
|
|
716
|
-
*/
|
|
690
|
+
const publicLogins = (logins) => logins.filter((l) => !l.pool).map(({ file, provider, email, disabled, active }) => ({ file, provider, email, disabled, active }));
|
|
691
|
+
/** The hub's logins, oldest first, as they are (nothing changes). Files the retired account pool router left are not listed. */
|
|
717
692
|
export async function listHubLogins(ws) {
|
|
718
|
-
|
|
719
|
-
return logins.filter((l) => !l.pool).map(({ file, provider, email, disabled, active }) => ({ file, provider, email, disabled, active }));
|
|
693
|
+
return publicLogins(await readHubLogins(ws));
|
|
720
694
|
}
|
|
721
695
|
/** The hub's logins, oldest first (the order authFiles lists them in). */
|
|
722
|
-
async function readHubLogins(ws
|
|
696
|
+
async function readHubLogins(ws) {
|
|
723
697
|
const files = await ws.authFiles();
|
|
724
698
|
const texts = await Promise.all(files.map((f) => ws.read(`${HUB_AUTH_DIR}/${f}`)));
|
|
725
699
|
return files.map((file, i) => {
|
|
@@ -727,10 +701,10 @@ async function readHubLogins(ws, stored) {
|
|
|
727
701
|
const doc = docOf(text);
|
|
728
702
|
const type = typeof doc?.type === 'string' && doc.type.trim() ? doc.type.trim().toLowerCase() : null;
|
|
729
703
|
const email = typeof doc?.email === 'string' && doc.email ? doc.email : null;
|
|
730
|
-
// The retired pool router's files (`sfa-<account id>.json`, `"prefix": "sfa-…"`)
|
|
704
|
+
// The retired pool router's files (`sfa-<account id>.json`, `"prefix": "sfa-…"`) were never the user's logins.
|
|
731
705
|
const pool = /^sfa-[^/]+\.json$/.test(file) || (typeof doc?.prefix === 'string' && doc.prefix.startsWith('sfa-'));
|
|
732
706
|
const disabled = doc?.disabled === true;
|
|
733
|
-
return { file, provider: type, email, disabled, active: !pool && !disabled && type !== null
|
|
707
|
+
return { file, provider: type, email, disabled, active: !pool && !disabled && type !== null, text, doc, pool };
|
|
734
708
|
});
|
|
735
709
|
}
|
|
736
710
|
/** The auth file's text with `disabled` set (true) or removed (false), every other field kept; null when unchanged. */
|
|
@@ -762,13 +736,6 @@ if [ "$(sha256sum < "$f" | cut -d' ' -f1)" != "$want" ]; then echo changed; exit
|
|
|
762
736
|
mv -f "$tmp" "$f"; trap - EXIT
|
|
763
737
|
echo written
|
|
764
738
|
`;
|
|
765
|
-
/** Writes active.json atomically (0600) from standard input. */
|
|
766
|
-
export const ACTIVE_SCRIPT = `set -euo pipefail
|
|
767
|
-
dir=${HUB_DIR}; mkdir -p "$dir"; umask 077
|
|
768
|
-
tmp=$(mktemp "$dir/.active.XXXXXX"); trap 'rm -f "$tmp"' EXIT
|
|
769
|
-
cat > "$tmp"; mv -f "$tmp" "$dir/active.json"; trap - EXIT
|
|
770
|
-
echo written
|
|
771
|
-
`;
|
|
772
739
|
const sha256 = (text) => createHash('sha256').update(text, 'utf8').digest('hex');
|
|
773
740
|
/** Sets one login's `disabled` (true) or clears it (false), reading the file again when CLIProxyAPI changed it meanwhile. */
|
|
774
741
|
async function setDisabled(ws, login, disabled) {
|
|
@@ -790,66 +757,70 @@ async function setDisabled(ws, login, disabled) {
|
|
|
790
757
|
throw new HubError(`${login.file} kept changing in the hub while it was being ${disabled ? 'disabled' : 'enabled'}.`, 'run: shard cli-proxy import');
|
|
791
758
|
}
|
|
792
759
|
/**
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
* active.json records the choice first, then every other login of that provider is disabled (all other fields kept), then
|
|
796
|
-
* the chosen one is enabled. Files without a `type` are left alone; the retired pool router's files are
|
|
797
|
-
* disabled and never chosen. Returns the logins as they are now, oldest first (without the pool router's).
|
|
760
|
+
* Under the hub's `logins` lock: the retired account pool router's files stay disabled (they were never the user's);
|
|
761
|
+
* every other login keeps the state its owner gave it. Returns the logins as they are now, oldest first.
|
|
798
762
|
*/
|
|
799
|
-
export async function
|
|
800
|
-
const
|
|
801
|
-
const stored = parseActive(storedText);
|
|
802
|
-
const logins = await readHubLogins(ws, stored);
|
|
803
|
-
const chosen = {};
|
|
804
|
-
for (const login of logins) {
|
|
805
|
-
const p = login.provider;
|
|
806
|
-
if (p === null || login.pool || chosen[p] !== undefined)
|
|
807
|
-
continue;
|
|
808
|
-
const mine = logins.filter((l) => l.provider === p && !l.pool);
|
|
809
|
-
const wanted = prefer[p];
|
|
810
|
-
if (wanted !== undefined && !mine.some((l) => l.file === wanted))
|
|
811
|
-
throw new HubError(`The hub has no ${p} login ${wanted} any more.`, 'see the hub’s logins: shard cli-proxy');
|
|
812
|
-
const current = mine.find((l) => l.file === stored[p] && !l.disabled);
|
|
813
|
-
chosen[p] = wanted ?? current?.file ?? login.file;
|
|
814
|
-
}
|
|
815
|
-
// Record the choice first, then disable the rest, then enable the chosen login: two logins of one provider are never
|
|
816
|
-
// enabled at the same moment (CLIProxyAPI would fail over between them), at the cost of an instant with none during a
|
|
817
|
-
// switch. The hub's sf-agent keeps one enabled login per provider on every change (the one active.json names, else
|
|
818
|
-
// the oldest enabled, never enabling one), so in this order it never disables the chosen login.
|
|
819
|
-
const text = `${JSON.stringify(Object.fromEntries(Object.entries(chosen).sort(([a], [b]) => a.localeCompare(b))), null, 2)}\n`;
|
|
820
|
-
if (text !== storedText && (storedText !== null || Object.keys(chosen).length > 0)) {
|
|
821
|
-
const r = await ws.sh(ACTIVE_SCRIPT, [], { stdin: text });
|
|
822
|
-
if (r.code !== 0)
|
|
823
|
-
throw new HubError(`The hub did not record its active logins: ${r.stderr.trim().split('\n').at(-1) || `exit ${r.code}`}`, 'run: shard cli-proxy import');
|
|
824
|
-
}
|
|
763
|
+
export async function settleHubLogins(ws) {
|
|
764
|
+
const logins = await readHubLogins(ws);
|
|
825
765
|
for (const l of logins)
|
|
826
|
-
if (l.
|
|
766
|
+
if (l.pool && !l.disabled)
|
|
827
767
|
await setDisabled(ws, l, true);
|
|
828
|
-
|
|
829
|
-
if (l.provider !== null && chosen[l.provider] === l.file && l.disabled)
|
|
830
|
-
await setDisabled(ws, l, false);
|
|
831
|
-
return logins.filter((l) => !l.pool).map(({ file, provider, email, disabled }) => {
|
|
832
|
-
const active = provider !== null && chosen[provider] === file;
|
|
833
|
-
return { file, provider, email, disabled: provider === null ? disabled : !active, active };
|
|
834
|
-
});
|
|
768
|
+
return publicLogins(logins);
|
|
835
769
|
}
|
|
836
770
|
/**
|
|
837
|
-
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
771
|
+
* The user's choice of which logins serve requests (`shard cli-proxy use`; call it under the hub's `logins` lock).
|
|
772
|
+
* `only[provider]` names the one login of that provider that stays enabled (it is enabled first, then the provider's
|
|
773
|
+
* other logins are disabled, so the provider is never without one); `all` enables every login of the hub, which then
|
|
774
|
+
* routes across them as its config says. Providers the choice does not name are left as they are. Returns the logins
|
|
775
|
+
* as they are now, oldest first.
|
|
840
776
|
*/
|
|
841
|
-
export async function
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
777
|
+
export async function useHubLogins(ws, choice) {
|
|
778
|
+
const logins = await readHubLogins(ws);
|
|
779
|
+
const only = choice.only ?? {};
|
|
780
|
+
for (const [p, file] of Object.entries(only)) {
|
|
781
|
+
if (!logins.some((l) => !l.pool && l.provider === p && l.file === file))
|
|
782
|
+
throw new HubError(`The hub has no ${p} login ${file} any more.`, 'see the hub’s logins: shard cli-proxy');
|
|
783
|
+
}
|
|
784
|
+
const wanted = (l) => {
|
|
785
|
+
if (l.pool || l.provider === null)
|
|
786
|
+
return null;
|
|
787
|
+
if (only[l.provider] !== undefined)
|
|
788
|
+
return only[l.provider] === l.file;
|
|
789
|
+
return choice.all ? true : null;
|
|
790
|
+
};
|
|
791
|
+
for (const l of logins)
|
|
792
|
+
if (wanted(l) === true && l.disabled)
|
|
793
|
+
await setDisabled(ws, l, false);
|
|
794
|
+
for (const l of logins)
|
|
795
|
+
if (wanted(l) === false && !l.disabled)
|
|
796
|
+
await setDisabled(ws, l, true);
|
|
797
|
+
// Computers still on CLI 0.22.0 keep the login active.json names for each provider: it names this choice too.
|
|
798
|
+
if (Object.keys(only).length) {
|
|
799
|
+
const text = await ws.read(LEGACY_ACTIVE_FILE);
|
|
800
|
+
let stored = {};
|
|
801
|
+
try {
|
|
802
|
+
const v = JSON.parse(text ?? '');
|
|
803
|
+
if (v && typeof v === 'object' && !Array.isArray(v))
|
|
804
|
+
stored = v;
|
|
805
|
+
}
|
|
806
|
+
catch { /* none yet */ }
|
|
807
|
+
const next = `${JSON.stringify({ ...stored, ...only }, null, 2)}\n`;
|
|
808
|
+
if (next !== text) {
|
|
809
|
+
const r = await ws.sh(LEGACY_ACTIVE_SCRIPT, [], { stdin: next });
|
|
810
|
+
if (r.code !== 0)
|
|
811
|
+
throw new HubError(`The hub did not record the choice: ${r.stderr.trim().split('\n').at(-1) || `exit ${r.code}`}`, 'run: shard cli-proxy use <email>');
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return listHubLogins(ws);
|
|
852
815
|
}
|
|
816
|
+
/** Where CLI 0.22.0 reads each provider's one login ({"<provider>": "<auth file>"}); written atomically (0600). */
|
|
817
|
+
export const LEGACY_ACTIVE_FILE = `${HUB_DIR}/active.json`;
|
|
818
|
+
export const LEGACY_ACTIVE_SCRIPT = `set -euo pipefail
|
|
819
|
+
dir=${HUB_DIR}; mkdir -p "$dir"; umask 077
|
|
820
|
+
tmp=$(mktemp "$dir/.active.XXXXXX"); trap 'rm -f "$tmp"' EXIT
|
|
821
|
+
cat > "$tmp"; mv -f "$tmp" "$dir/active.json"; trap - EXIT
|
|
822
|
+
echo written
|
|
823
|
+
`;
|
|
853
824
|
/** What the retired account pool router kept in the hub (codex-connector 5 with pooled accounts). */
|
|
854
825
|
const POOL_LEFTOVERS = ['api-token', 'api-url', 'pool.json', 'pool-creds.json', 'pool-status.json', 'pool-client.key', 'pool.lock'];
|
|
855
826
|
/** Removes the router's files; prints `retired` when its hub token was there (its sf-agent then starts again without it). */
|
package/dist/cli-proxy.d.ts
CHANGED
|
@@ -213,14 +213,9 @@ export interface HubAccount {
|
|
|
213
213
|
disabled: boolean;
|
|
214
214
|
/** The auth file, relative to the hub's auth directory (null when CLIProxyAPI does not name it). */
|
|
215
215
|
file: string | null;
|
|
216
|
-
/**
|
|
216
|
+
/** It serves requests (enabled); a disabled login stays in the hub unused. */
|
|
217
217
|
active: boolean;
|
|
218
218
|
}
|
|
219
|
-
/**
|
|
220
|
-
* Marks each provider's active login: an enabled login whose file is one of `activeFiles` (the hub's active.json).
|
|
221
|
-
* Without them (unreadable), a provider's only enabled login is its active one.
|
|
222
|
-
*/
|
|
223
|
-
export declare function markActive(accounts: HubAccount[], activeFiles: ReadonlySet<string> | null): HubAccount[];
|
|
224
219
|
/** The hub's logins from its management API (wakes the hub). */
|
|
225
220
|
export declare function hubAccounts(info: HubInfo, timeoutMs?: number): Promise<HubAccount[]>;
|
|
226
221
|
/** Whether the hub answers /healthz through its callback URL, and the edge's reason when it does not. */
|
|
@@ -253,8 +248,8 @@ export interface ImportResult {
|
|
|
253
248
|
};
|
|
254
249
|
/**
|
|
255
250
|
* added: accounts this import gave the hub; already: accounts the hub had (its tokens stay); moved_aside: where this
|
|
256
|
-
* computer's auth files went once the hub had them (`<dir>.moved-to-shardflux`). active:
|
|
257
|
-
*
|
|
251
|
+
* computer's auth files went once the hub had them (`<dir>.moved-to-shardflux`). active: the logins that serve
|
|
252
|
+
* requests; disabled: logins kept in the hub unused (as they were on the computer, or by --use).
|
|
258
253
|
*/
|
|
259
254
|
logins: {
|
|
260
255
|
uploaded: string[];
|
|
@@ -301,9 +296,9 @@ export declare function matchLogins<T extends {
|
|
|
301
296
|
* one: the hub's copy may hold a newer refresh token), start the forwarder on 127.0.0.1:8317 and put the hub info into
|
|
302
297
|
* the machine template.
|
|
303
298
|
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
299
|
+
* Each login arrives as it is on this computer (enabled or disabled), and the hub routes across the enabled ones as
|
|
300
|
+
* the user's config says. `--use <email|file>` (repeatable, one per provider) makes that login the only one serving
|
|
301
|
+
* its provider (the provider's others are kept in the hub, disabled), as `shard cli-proxy use` does.
|
|
307
302
|
*/
|
|
308
303
|
export declare function importCliProxy(ctx: Ctx, opts?: ImportOptions): Promise<ImportResult>;
|
|
309
304
|
export interface ExportOptions {
|
|
@@ -335,46 +330,25 @@ export interface ExportResult {
|
|
|
335
330
|
*/
|
|
336
331
|
export declare function exportCliProxy(ctx: Ctx, opts?: ExportOptions): Promise<ExportResult>;
|
|
337
332
|
export interface UseResult {
|
|
338
|
-
/** Each provider this command set, with the login it
|
|
333
|
+
/** Each provider this command set to one login, with the login that served it alone before (null if not one) and the one now. */
|
|
339
334
|
switched: Array<{
|
|
340
335
|
provider: string;
|
|
341
336
|
from: LoginRef | null;
|
|
342
337
|
to: LoginRef;
|
|
343
338
|
}>;
|
|
344
|
-
/**
|
|
339
|
+
/** The logins that serve requests now, and the ones kept in the hub, disabled. */
|
|
345
340
|
active: LoginRef[];
|
|
346
341
|
disabled: LoginRef[];
|
|
347
342
|
}
|
|
348
343
|
/**
|
|
349
|
-
* `shard cli-proxy use <email|file>`:
|
|
350
|
-
*
|
|
344
|
+
* `shard cli-proxy use <email|file>`: that login alone serves its provider (an email with a Claude and a Codex login
|
|
345
|
+
* sets both); the provider's other logins stay in the hub, disabled. `--all`: every login serves again, and the hub
|
|
346
|
+
* routes across them as its config says. Under the hub's logins lock.
|
|
351
347
|
*/
|
|
352
348
|
export declare function useCliProxyLogin(ctx: Ctx, spec: string, opts?: {
|
|
353
349
|
hub?: HubCloud;
|
|
350
|
+
all?: boolean;
|
|
354
351
|
}): Promise<UseResult>;
|
|
355
|
-
/** How often the forwarder checks that the hub serves one login per provider while it runs. */
|
|
356
|
-
export declare const HUB_LOGIN_CHECK_MS: number;
|
|
357
|
-
/** How soon it looks again when the hub was asleep (a VM's request may wake it any moment; looking never wakes it). */
|
|
358
|
-
export declare const HUB_LOGIN_ASLEEP_MS = 60000;
|
|
359
|
-
/** The least time between two checks a request asks for (requests come in bursts). */
|
|
360
|
-
export declare const HUB_LOGIN_NUDGE_MS = 10000;
|
|
361
|
-
export interface HubLoginChecks {
|
|
362
|
-
/** A request arrived: the hub is awake or waking. Checks now unless a check found it running and fixed recently. */
|
|
363
|
-
nudge(): void;
|
|
364
|
-
stop(): void;
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* The forwarder's check that the hub serves one login per provider (enforceHubLoginsIfRunning), never waking the hub:
|
|
368
|
-
* at start; then every HUB_LOGIN_CHECK_MS while it runs and every HUB_LOGIN_ASLEEP_MS while it sleeps; and at the first
|
|
369
|
-
* request after it slept (nudge), so a hub set up before one login per provider cannot fail over between logins for
|
|
370
|
-
* long after it wakes. One check at a time.
|
|
371
|
-
*/
|
|
372
|
-
export declare function hubLoginChecks(ctx: Ctx, source: () => HubInfo | null, log: (line: string) => void, opts?: {
|
|
373
|
-
hub?: HubCloud;
|
|
374
|
-
everyMs?: number;
|
|
375
|
-
asleepMs?: number;
|
|
376
|
-
nudgeMs?: number;
|
|
377
|
-
}): HubLoginChecks;
|
|
378
352
|
/**
|
|
379
353
|
* After the edge refused the hub's callback URL (`callback_invalid`: the API key that created it was revoked):
|
|
380
354
|
* replaces the URLs the edge refuses (under the hub's lock), saves them locally and gives VMs the new VM address.
|