johns-harness 2026.9.8 → 2026.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -10
- package/dist/auth-choice-Ck7FJ_PD.js +1 -1
- package/dist/auth-choice-CtvmIf50.js +2 -2
- package/dist/auth-choice-DYEtgk1X.js +2 -2
- package/dist/auth-choice-DtQ1-QBl.js +1 -1
- package/dist/auth-profiles-5CHn7vq1.js +101 -171
- package/dist/auth-store-transactions.js +135 -0
- package/dist/auth-token-DXCF3dFL.js +28 -28
- package/dist/auth-token-f950j5qR.js +28 -28
- package/dist/codex-oauth-owner.js +227 -0
- package/dist/codex-oauth-token.js +53 -0
- package/dist/command-registry-ZarkU2_Y.js +5 -1
- package/dist/compact-B247y5Qt.js +35 -4
- package/dist/config-koj5M_EO.js +10 -9
- package/dist/coord-swarm-board.js +30 -4
- package/dist/daemon-cli.js +12 -48
- package/dist/doctor-config-flow-B1i686kY.js +10 -201
- package/dist/doctor-config-flow-BjBa3YNF.js +10 -201
- package/dist/entry.js +4 -0
- package/dist/exec-approvals-1t-rSzbk.js +4 -5
- package/dist/exec-approvals-oKbYU_E8.js +4 -5
- package/dist/globals-BM8hKFm0.js +1 -0
- package/dist/globals-Bv4ZcVWM.js +1 -0
- package/dist/index.js +5 -0
- package/dist/installs-DGqxcU3J.js +2 -9
- package/dist/manifest-registry-BMEqbkWA.js +8 -7
- package/dist/manifest-registry-Ba187z7Z.js +8 -7
- package/dist/memory-cli-DkhQZF9L.js +29 -2
- package/dist/migrate-inspect.js +215 -0
- package/dist/migrate-plan.js +30 -0
- package/dist/migrate-plugin-adapters.js +31 -0
- package/dist/migrate-service.js +42 -0
- package/dist/migrate-transaction.js +89 -0
- package/dist/migrate.js +28 -0
- package/dist/model-selection-BGlGpPgM.js +102 -143
- package/dist/model-selection-BU6wl1le.js +102 -143
- package/dist/model-selection-L7RMwsG-.js +101 -171
- package/dist/models-CxHdes0w.js +3 -3
- package/dist/models-cli-DZnoM8Ln.js +3 -3
- package/dist/mr-memory-setup.js +64 -0
- package/dist/onboard-C_T1ajyP.js +2 -0
- package/dist/onboard-CjFEsMQX.js +2 -0
- package/dist/onboarding-DYOenUs-.js +2 -0
- package/dist/onboarding-VbQraljl.js +2 -0
- package/dist/paths-BJV7vkaX.js +3 -39
- package/dist/paths-BfR2LXbA.js +3 -39
- package/dist/paths-GBpjI3o0.js +3 -39
- package/dist/paths-hfkBoC7i.js +3 -39
- package/dist/pi-embedded-C6ITuRXf.js +38 -8
- package/dist/pi-embedded-DoQsYfIY.js +38 -8
- package/dist/plugin-sdk/agents/auth-profiles/profiles.d.ts +1 -1
- package/dist/plugin-sdk/agents/auth-profiles/store.d.ts +2 -2
- package/dist/plugin-sdk/bluebubbles.js +4 -40
- package/dist/plugin-sdk/commands/onboard-auth.credentials.d.ts +2 -2
- package/dist/plugin-sdk/config/agent-limits.d.ts +1 -1
- package/dist/plugin-sdk/config/types.agent-defaults.d.ts +2 -2
- package/dist/plugin-sdk/config-4Fgm-yEH.js +102 -143
- package/dist/plugin-sdk/config-Di-lyCdh.js +102 -143
- package/dist/plugin-sdk/core.js +3 -26
- package/dist/plugin-sdk/device-pair.js +3 -26
- package/dist/plugin-sdk/diagnostics-otel.js +3 -26
- package/dist/plugin-sdk/discord.js +12 -48
- package/dist/plugin-sdk/dispatch-BP0viZiL.js +38 -8
- package/dist/plugin-sdk/dispatch-CQsjmw7g.js +38 -8
- package/dist/plugin-sdk/dispatch-Cerq29sy.js +38 -8
- package/dist/plugin-sdk/dispatch-Cndjtt0g.js +38 -8
- package/dist/plugin-sdk/dispatch-UogiJYul.js +38 -8
- package/dist/plugin-sdk/feishu.js +4 -40
- package/dist/plugin-sdk/google-gemini-cli-auth.js +3 -39
- package/dist/plugin-sdk/googlechat.js +4 -40
- package/dist/plugin-sdk/imessage.js +4 -40
- package/dist/plugin-sdk/model-auth-CX9cPHdC.js +102 -143
- package/dist/plugin-sdk/model-auth-Ciehz0x5.js +102 -143
- package/dist/plugin-sdk/model-auth-DVyo6JSX.js +102 -143
- package/dist/plugin-sdk/model-auth-kiHYHGrs.js +102 -143
- package/dist/plugin-sdk/model-selection-Dbj4jDYM.js +40 -96
- package/dist/plugin-sdk/nostr.js +3 -26
- package/dist/plugin-sdk/paths-B9fwHuf0.js +3 -39
- package/dist/plugin-sdk/paths-D6tDENa_.js +3 -39
- package/dist/plugin-sdk/paths-Dmn791zP.js +3 -39
- package/dist/plugin-sdk/paths-akVZbnot.js +3 -39
- package/dist/plugin-sdk/paths-eFexkPEh.js +3 -39
- package/dist/plugin-sdk/reply-DbZnH8-h.js +38 -8
- package/dist/plugin-sdk/signal.js +4 -40
- package/dist/plugin-sdk/slack.js +4 -40
- package/dist/plugin-sdk/synology-chat.js +3 -39
- package/dist/plugin-sdk/telegram.js +4 -40
- package/dist/plugin-sdk/tlon.js +4 -40
- package/dist/plugin-sdk/twitch.js +4 -40
- package/dist/plugin-sdk/voice-call.js +3 -39
- package/dist/plugin-sdk/zalo.js +4 -40
- package/dist/plugin-sdk/zalouser.js +4 -40
- package/dist/program-Csx0T2gv.js +1 -0
- package/dist/program-context-CKpVgs9O.js +4 -0
- package/dist/prompt-select-styled-C_vP6os-.js +2 -4
- package/dist/prompt-select-styled-ClBOfLjF.js +2 -4
- package/dist/provider-auth-helpers-BKS7ln76.js +1 -1
- package/dist/provider-auth-helpers-D5_z82-N.js +1 -1
- package/dist/register.migrate.js +12 -0
- package/dist/register.onboard-3v4vug5B.js +3 -1
- package/dist/register.onboard-Cwc9htf5.js +3 -1
- package/dist/reply-C5LKjXcC.js +35 -4
- package/dist/status-Cz7cjn88.js +1 -1
- package/dist/status-DH0rKO8q.js +1 -1
- package/dist/swarm-primitive.js +1563 -0
- package/dist/utils-CUUJ4XQX.js +3 -39
- package/docs/PATCHES.md +165 -113
- package/docs/SWARM.md +174 -0
- package/docs/images/swarm-spawn-alt.png +0 -0
- package/docs/images/swarm-spawn.png +0 -0
- package/docs/tools/subagents.md +5 -5
- package/johnness.mjs +5 -0
- package/package.json +1 -1
- package/scripts/postinstall.mjs +3 -6
package/README.md
CHANGED
|
@@ -8,7 +8,17 @@
|
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
John's Harness is a production agent harness for engineers who run more than one coding agent at a time. Agents work the same repositories at once, coordinate through a durable board keyed by git origin, message each other while they run, and ship only through an exact-head push gate with per-commit production authority. Memory persists across sessions and compaction and is shared by every agent in the swarm.
|
|
11
|
+
John's Harness is a production agent harness for engineers who run more than one coding agent at a time. Agents work the same repositories at once, coordinate through a durable board keyed by git origin, message each other while they run, and ship only through an exact-head push gate with per-commit production authority. Memory persists across sessions and compaction and is shared by every agent in the swarm. Launching a swarm takes one command and a number; the members divide the work among themselves.
|
|
12
|
+
|
|
13
|
+
## Migrating from OpenClaw
|
|
14
|
+
Bring your agents, memory, credentials, and scheduled jobs into John's Harness.
|
|
15
|
+
```sh
|
|
16
|
+
npm i -g johns-harness && johnness migrate
|
|
17
|
+
```
|
|
18
|
+
The command backs up your state, creates `~/.johnness`, rewrites settings, and checks the result.
|
|
19
|
+
If your gateway is running, it shows the exact stop command before making changes.
|
|
20
|
+
Review any script updates, then follow the printed commands to start the new gateway.
|
|
21
|
+
Your original installation stays intact. Preview with `johnness migrate --dry-run`; undo with `johnness migrate --rollback`.
|
|
12
22
|
|
|
13
23
|
## Quick start
|
|
14
24
|
|
|
@@ -17,12 +27,12 @@ Requires Node 22.12 or newer.
|
|
|
17
27
|
```bash
|
|
18
28
|
npm install -g johns-harness
|
|
19
29
|
johnness onboard
|
|
20
|
-
johnness
|
|
30
|
+
johnness memory mk_your_key
|
|
21
31
|
```
|
|
22
32
|
|
|
23
33
|
The package is `johns-harness`; the command it installs is `johnness`.
|
|
24
34
|
|
|
25
|
-
The wizard sets up credentials, workspace, gateway, and channels.
|
|
35
|
+
The wizard sets up credentials, workspace, gateway, and channels. It asks for your MemoryRouter API key and links to [signup](https://app.memoryrouter.ai/signup), free to start. If you skip memory in the wizard, the third line stores the key later.
|
|
26
36
|
|
|
27
37
|
### First run
|
|
28
38
|
|
|
@@ -30,7 +40,7 @@ The wizard sets up credentials, workspace, gateway, and channels. The third line
|
|
|
30
40
|
| --- | --- | --- |
|
|
31
41
|
| Install | `npm install -g johns-harness` | Installs the CLI, the runtime, and the default memory plugin. A postinstall step applies the patched dependency files. |
|
|
32
42
|
| Configure | `johnness onboard` | Interactive wizard for gateway, workspace, and skills. `johnness configure` reopens it later. |
|
|
33
|
-
| Memory | `johnness
|
|
43
|
+
| Memory | `johnness memory mk_your_key` | Turns on persistent memory. The plugin is already installed and enabled. |
|
|
34
44
|
| Start | `johnness gateway start` | Starts the gateway as a background service. `johnness gateway run` keeps it in the foreground. |
|
|
35
45
|
| Connect | `johnness channels add --channel telegram --token "<bot token>"` | Connects a chat channel. Discord, Slack, WhatsApp, Signal, iMessage, Matrix, and more are supported. |
|
|
36
46
|
| Check | `johnness doctor` | Health checks and quick fixes for the gateway and channels. |
|
|
@@ -46,21 +56,54 @@ node johnness.mjs --help
|
|
|
46
56
|
### Verify
|
|
47
57
|
|
|
48
58
|
```bash
|
|
49
|
-
npm test #
|
|
50
|
-
bash scripts/verify-patches.sh #
|
|
59
|
+
npm test # 701 tests against the installed dependency tree
|
|
60
|
+
bash scripts/verify-patches.sh # 258 checks across every patch family
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Launch a swarm with one command
|
|
64
|
+
|
|
65
|
+
One call, one number. The orchestrator writes a single directive and chooses how many agents should work it. The members decide everything else.
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
swarm_spawn({
|
|
69
|
+
directive: "<the whole briefing: goal, scope, forbidden files, push rules, author identity>",
|
|
70
|
+
count: 12,
|
|
71
|
+
coord: { root: "/abs/path/to/repo", branch: "main", prodAuthorized: false },
|
|
72
|
+
model: "anthropic/claude-fable-5-1" // optional, applies to every member
|
|
73
|
+
})
|
|
51
74
|
```
|
|
52
75
|
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+
Every member receives the same directive plus a short runtime preamble that carries the protocol. Nobody hands out work.
|
|
79
|
+
|
|
80
|
+
The lowest-numbered live member decomposes the directive into work items on the shared board. The post is a compare-and-swap, so exactly one plan exists no matter how many members arrive at the same moment; the rest wait for it rather than poll.
|
|
81
|
+
|
|
82
|
+
Each member claims one open item atomically, works it, pushes, marks it done or failed, and claims the next. A claim is exactly one winner, even under concurrent attempts.
|
|
83
|
+
|
|
84
|
+
Claims carry a 15 minute lease that heartbeats extend. A lease that lapses reopens the item the moment anyone looks at the board, so a dead member never strands its work.
|
|
85
|
+
|
|
86
|
+
When work reveals more items, any member appends them to the plan and the addition wakes the others through their inboxes. Members message each other directly only for a real dependency, and each records how the swarm coordinated so the next one starts smarter.
|
|
87
|
+
|
|
88
|
+
The preamble also carries the operating protocol as defaults with named exceptions: the planner declares the shape of the work, one integrator merges parallel branches and builds and pushes once so parallel work flows into a single sequential step, one gateway or a shared budget fronts each rate-limited host, and done means done. Members ask a service holder with a durable request and wait on the answer without polling.
|
|
89
|
+
|
|
90
|
+
Per-member announces are suppressed. When the last member finishes, one `swarm finished` rollup arrives with every item, its owner, its pushed head, and its summary.
|
|
91
|
+
|
|
92
|
+
`count` runs from 2 to 100. Members beyond the concurrency limit are queued and start as slots free, and `swarm_status` reads a swarm's progress without interrupting anyone. Members are ordinary agents: same depth rules, same push preflight, same per-commit production authority, no force pushes.
|
|
93
|
+
|
|
94
|
+
Full guide, including the preamble members receive and the work item state machine: [`docs/SWARM.md`](docs/SWARM.md).
|
|
95
|
+
|
|
53
96
|
## The problem
|
|
54
97
|
|
|
55
98
|
Coding agents today run as isolated processes. Two agents on the same repository have no shared state, no way to talk to each other mid-task, and no gate between a dirty local branch and production. Once a context window compacts, whatever the agent knew is gone.
|
|
56
99
|
|
|
57
100
|
## What makes it different
|
|
58
101
|
|
|
59
|
-
- **A swarm, not a pile of agents.** One coordination board per repository, direct agent-to-agent messages delivered inside a busy agent's run, exclusive claims on shared areas,
|
|
102
|
+
- **A swarm, not a pile of agents.** One coordination board per repository, direct agent-to-agent messages delivered inside a busy agent's run, exclusive claims on shared areas, a push preflight that blocks stale, forced, broad, and unauthorized pushes, and `swarm_spawn`, which launches up to 100 agents from one directive in a single call.
|
|
60
103
|
- **Memory built for production.** Tiered recall that traces from consolidated reflections down to the verbatim record, surviving compaction and shared across the swarm. Installed and enabled by default.
|
|
61
104
|
- **Production authority per commit.** Promotion is granted to an exact SHA, never to a branch name, so one worker's authorization never covers another worker's commits.
|
|
62
105
|
- **An append-only action log.** Agents record every consequential action to `ACTION_LOG.md` and read it before acting.
|
|
63
|
-
- **A fixed runtime surface.** The core does not grow on a schedule. New mechanisms enter only to solve a demonstrated production failure, one narrow, tested, reversible patch at a time:
|
|
106
|
+
- **A fixed runtime surface.** The core does not grow on a schedule. New mechanisms enter only to solve a demonstrated production failure, one narrow, tested, reversible patch at a time: 59 patch families, each with a regression test and a rollback path.
|
|
64
107
|
- **Reliability with a definition.** Bounded retries, explicit failure states, exact-once continuation, context-pressure recovery, durable delivery, and supervised gateway recovery.
|
|
65
108
|
- **Coding first, phone optional.** Direct the work at a high level from a chat channel while agents plan, implement, test, coordinate, and stage releases unattended.
|
|
66
109
|
- **Local voice.** Optional native Whisper transcription of inbound audio, entirely on your machine. Off by default. [Setup](docs/NATIVE-WHISPER-MUSE.md).
|
|
@@ -92,8 +135,12 @@ Agents sharing a filesystem are not a swarm. A swarm knows who owns which work,
|
|
|
92
135
|
|
|
93
136
|
**Production authority belongs to a commit.** Never to a branch name or a timestamp.
|
|
94
137
|
|
|
138
|
+
**One directive, one number.** `swarm_spawn` launches up to 100 members from the same briefing and they divide the work themselves. See the launch section near the top of this page and the [swarm guide](docs/SWARM.md).
|
|
139
|
+
|
|
95
140
|
## Memory
|
|
96
141
|
|
|
142
|
+
Run `johnness memory mk_your_key` to set your MemoryRouter API key and start persistent memory, or enter it during onboarding.
|
|
143
|
+
|
|
97
144
|
The future of AI is not smarter models. It is models with the right context: what is happening, what happened before, and what matters now.
|
|
98
145
|
|
|
99
146
|
An agent in this harness remembers what it was told yesterday, the decision it made last Tuesday, the fix it tried that did not work, and why. That context persists across sessions and survives compaction. It does not live and die inside one context window.
|
|
@@ -123,7 +170,7 @@ The log is a [`RULES.md`](RULES.md) workspace convention enforced through the ac
|
|
|
123
170
|
|
|
124
171
|
## Patch families
|
|
125
172
|
|
|
126
|
-
The runtime carries
|
|
173
|
+
The runtime carries 59 patch families. Each is a production fix applied at the source with a regression test, a durable patch marker, and a rollback path. A verifier runs 258 checks against the package tree and installed dependencies, and 701 tests run against the installed dependency tree.
|
|
127
174
|
|
|
128
175
|
Every family follows the same six steps: reproduce the failure, trace the exact runtime path, make the smallest source-level change that restores the invariant, add a regression test and a patch marker, run the verifier, and retain rollback artifacts. The full index: [`docs/PATCHES.md`](docs/PATCHES.md).
|
|
129
176
|
|
|
@@ -140,7 +187,7 @@ Every family follows the same six steps: reproduce the failure, trace the exact
|
|
|
140
187
|
## Documentation
|
|
141
188
|
|
|
142
189
|
- [`docs/index.md`](docs/index.md): runtime docs, from gateway and channels to plugins, nodes, and CLI reference.
|
|
143
|
-
- [`docs/PATCHES.md`](docs/PATCHES.md): the
|
|
190
|
+
- [`docs/PATCHES.md`](docs/PATCHES.md): the 59 patch families, one record each.
|
|
144
191
|
- [`docs/NATIVE-WHISPER-MUSE.md`](docs/NATIVE-WHISPER-MUSE.md): local Whisper transcription setup.
|
|
145
192
|
- [`RULES.md`](RULES.md): the default operating rules, including Rule Zero on production authorization.
|
|
146
193
|
|
|
@@ -62,7 +62,7 @@ async function applyAuthChoiceAnthropic(params) {
|
|
|
62
62
|
provider,
|
|
63
63
|
name: String(profileNameRaw ?? "")
|
|
64
64
|
});
|
|
65
|
-
upsertAuthProfile({
|
|
65
|
+
await upsertAuthProfile({
|
|
66
66
|
profileId: namedProfileId,
|
|
67
67
|
agentDir: params.agentDir,
|
|
68
68
|
credential: {
|
|
@@ -813,7 +813,7 @@ async function applyAuthChoicePluginProvider(params, options) {
|
|
|
813
813
|
});
|
|
814
814
|
if (result.configPatch) nextConfig = mergeConfigPatch(nextConfig, result.configPatch);
|
|
815
815
|
for (const profile of result.profiles) {
|
|
816
|
-
upsertAuthProfile({
|
|
816
|
+
await upsertAuthProfile({
|
|
817
817
|
profileId: profile.profileId,
|
|
818
818
|
credential: profile.credential,
|
|
819
819
|
agentDir
|
|
@@ -62,7 +62,7 @@ async function applyAuthChoiceAnthropic(params) {
|
|
|
62
62
|
provider,
|
|
63
63
|
name: String(profileNameRaw ?? "")
|
|
64
64
|
});
|
|
65
|
-
upsertAuthProfile({
|
|
65
|
+
await upsertAuthProfile({
|
|
66
66
|
profileId: namedProfileId,
|
|
67
67
|
agentDir: params.agentDir,
|
|
68
68
|
credential: {
|
|
@@ -813,7 +813,7 @@ async function applyAuthChoicePluginProvider(params, options) {
|
|
|
813
813
|
});
|
|
814
814
|
if (result.configPatch) nextConfig = mergeConfigPatch(nextConfig, result.configPatch);
|
|
815
815
|
for (const profile of result.profiles) {
|
|
816
|
-
upsertAuthProfile({
|
|
816
|
+
await upsertAuthProfile({
|
|
817
817
|
profileId: profile.profileId,
|
|
818
818
|
credential: profile.credential,
|
|
819
819
|
agentDir
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { refreshManagedCodexProfile, readManagedCodexStore, mutateAuthProfile, updateAuthStoreMetadata, saveAuthStoreSnapshot } from "./codex-oauth-owner.js";
|
|
1
2
|
import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
|
|
2
3
|
import { _ as expandHomePrefix, a as resolveCanonicalConfigPath, c as resolveDefaultConfigCandidates, d as resolveIsNixMode, f as resolveLegacyStateDirs, g as resolveStateDir, h as resolveOAuthPath, i as isNixMode, l as resolveGatewayLockDir, m as resolveOAuthDir, n as DEFAULT_GATEWAY_PORT, o as resolveConfigPath, p as resolveNewStateDir, r as STATE_DIR, s as resolveConfigPathCandidate, t as CONFIG_PATH, u as resolveGatewayPort, y as resolveRequiredHomeDir } from "./paths-BfR2LXbA.js";
|
|
3
4
|
import { c as sanitizeForLog, l as stripAnsi, t as createSubsystemLogger } from "./subsystem-Cf9yS0UI.js";
|
|
@@ -4199,13 +4200,7 @@ function resolveAuthStorePathForDisplay(agentDir) {
|
|
|
4199
4200
|
const pathname = resolveAuthStorePath(agentDir);
|
|
4200
4201
|
return pathname.startsWith("~") ? pathname : resolveUserPath(pathname);
|
|
4201
4202
|
}
|
|
4202
|
-
|
|
4203
|
-
if (fs.existsSync(pathname)) return;
|
|
4204
|
-
saveJsonFile(pathname, {
|
|
4205
|
-
version: 1,
|
|
4206
|
-
profiles: {}
|
|
4207
|
-
});
|
|
4208
|
-
}
|
|
4203
|
+
|
|
4209
4204
|
//#endregion
|
|
4210
4205
|
//#region src/agents/auth-profiles/store.ts
|
|
4211
4206
|
const AUTH_PROFILE_TYPES = new Set([
|
|
@@ -4213,6 +4208,24 @@ const AUTH_PROFILE_TYPES = new Set([
|
|
|
4213
4208
|
"oauth",
|
|
4214
4209
|
"token"
|
|
4215
4210
|
]);
|
|
4211
|
+
// JOHNNESS_PATCH_CODEX_OAUTH_OWNERSHIP_58: all compiled adapters share one owner.
|
|
4212
|
+
function codexOwnershipContext(agentDir) {
|
|
4213
|
+
return {
|
|
4214
|
+
stateDir: resolveStateDir(),
|
|
4215
|
+
mainAgentDir: resolveJohnnessAgentDir(),
|
|
4216
|
+
agentDir: agentDir ? resolveUserPath(agentDir) : void 0,
|
|
4217
|
+
log: (event) => log$8.info("Codex OAuth ownership", event)
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4220
|
+
function readCodexOwnedAuthStore(agentDir, store) {
|
|
4221
|
+
return readManagedCodexStore({ ...codexOwnershipContext(agentDir), store });
|
|
4222
|
+
}
|
|
4223
|
+
function sanitizeAuthProfileCredential(credential) {
|
|
4224
|
+
const sanitized = { ...credential };
|
|
4225
|
+
if (sanitized.type === "api_key" && sanitized.keyRef) delete sanitized.key;
|
|
4226
|
+
if (sanitized.type === "token" && sanitized.tokenRef) delete sanitized.token;
|
|
4227
|
+
return sanitized;
|
|
4228
|
+
}
|
|
4216
4229
|
const runtimeAuthStoreSnapshots = /* @__PURE__ */ new Map();
|
|
4217
4230
|
function resolveRuntimeStoreKey(agentDir) {
|
|
4218
4231
|
return resolveAuthStorePath(agentDir);
|
|
@@ -4226,34 +4239,34 @@ function resolveRuntimeAuthProfileStore(agentDir) {
|
|
|
4226
4239
|
const requestedKey = resolveRuntimeStoreKey(agentDir);
|
|
4227
4240
|
const mainStore = runtimeAuthStoreSnapshots.get(mainKey);
|
|
4228
4241
|
const requestedStore = runtimeAuthStoreSnapshots.get(requestedKey);
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
if (requestedStore) return cloneAuthProfileStore(requestedStore);
|
|
4235
|
-
if (mainStore) return cloneAuthProfileStore(mainStore);
|
|
4236
|
-
return null;
|
|
4242
|
+
let store;
|
|
4243
|
+
if (!agentDir || requestedKey === mainKey) store = mainStore;
|
|
4244
|
+
else if (mainStore && requestedStore) store = mergeAuthProfileStores(cloneAuthProfileStore(mainStore), cloneAuthProfileStore(requestedStore));
|
|
4245
|
+
else store = requestedStore ?? mainStore;
|
|
4246
|
+
return store ? readCodexOwnedAuthStore(agentDir, cloneAuthProfileStore(store)) : null;
|
|
4237
4247
|
}
|
|
4238
4248
|
function replaceRuntimeAuthProfileStoreSnapshots(entries) {
|
|
4239
4249
|
runtimeAuthStoreSnapshots.clear();
|
|
4240
|
-
for (const entry of entries) runtimeAuthStoreSnapshots.set(resolveRuntimeStoreKey(entry.agentDir), cloneAuthProfileStore(entry.store));
|
|
4250
|
+
for (const entry of entries) runtimeAuthStoreSnapshots.set(resolveRuntimeStoreKey(entry.agentDir), readCodexOwnedAuthStore(entry.agentDir, cloneAuthProfileStore(entry.store)));
|
|
4241
4251
|
}
|
|
4242
4252
|
function clearRuntimeAuthProfileStoreSnapshots() {
|
|
4243
4253
|
runtimeAuthStoreSnapshots.clear();
|
|
4244
4254
|
}
|
|
4245
4255
|
async function updateAuthProfileStoreWithLock(params) {
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
const
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4256
|
+
return await updateAuthStoreMetadata({
|
|
4257
|
+
...codexOwnershipContext(params.agentDir),
|
|
4258
|
+
update: (metadata) => {
|
|
4259
|
+
const effective = readCodexOwnedAuthStore(params.agentDir);
|
|
4260
|
+
const profiles = Object.freeze(Object.fromEntries(Object.entries(effective.profiles).map(([id, credential]) => [id, Object.freeze({ ...credential })])));
|
|
4261
|
+
const view = { ...metadata };
|
|
4262
|
+
Object.defineProperty(view, "profiles", {
|
|
4263
|
+
enumerable: true,
|
|
4264
|
+
get: () => profiles,
|
|
4265
|
+
set: () => { throw new Error("Credential mutations require upsertAuthProfileWithLock"); }
|
|
4266
|
+
});
|
|
4267
|
+
if (params.updater(view)) for (const key of ["usageStats", "lastGood", "order"]) metadata[key] = view[key];
|
|
4268
|
+
}
|
|
4269
|
+
});
|
|
4257
4270
|
}
|
|
4258
4271
|
/**
|
|
4259
4272
|
* Normalise a raw auth-profiles.json credential entry.
|
|
@@ -4439,68 +4452,18 @@ function loadCoercedStore(authPath) {
|
|
|
4439
4452
|
return coerceAuthStore(loadJsonFile(authPath));
|
|
4440
4453
|
}
|
|
4441
4454
|
function loadAuthProfileStore() {
|
|
4442
|
-
|
|
4443
|
-
const asStore = loadCoercedStore(authPath);
|
|
4444
|
-
if (asStore) {
|
|
4445
|
-
if (syncExternalCliCredentials(asStore)) saveJsonFile(authPath, asStore);
|
|
4446
|
-
return asStore;
|
|
4447
|
-
}
|
|
4448
|
-
const legacy = coerceLegacyStore(loadJsonFile(resolveLegacyAuthStorePath()));
|
|
4449
|
-
if (legacy) {
|
|
4450
|
-
const store = {
|
|
4451
|
-
version: 1,
|
|
4452
|
-
profiles: {}
|
|
4453
|
-
};
|
|
4454
|
-
applyLegacyStore(store, legacy);
|
|
4455
|
-
syncExternalCliCredentials(store);
|
|
4456
|
-
return store;
|
|
4457
|
-
}
|
|
4458
|
-
const store = {
|
|
4459
|
-
version: 1,
|
|
4460
|
-
profiles: {}
|
|
4461
|
-
};
|
|
4462
|
-
syncExternalCliCredentials(store);
|
|
4463
|
-
return store;
|
|
4455
|
+
return loadAuthProfileStoreForAgent();
|
|
4464
4456
|
}
|
|
4465
4457
|
function loadAuthProfileStoreForAgent(agentDir, options) {
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
if (agentDir && !readOnly) {
|
|
4474
|
-
const mainStore = coerceAuthStore(loadJsonFile(resolveAuthStorePath()));
|
|
4475
|
-
if (mainStore && Object.keys(mainStore.profiles).length > 0) {
|
|
4476
|
-
saveJsonFile(authPath, mainStore);
|
|
4477
|
-
log$8.info("inherited auth-profiles from main agent", { agentDir });
|
|
4478
|
-
return mainStore;
|
|
4479
|
-
}
|
|
4458
|
+
// Loading never copies rotating credentials, migrates files, or imports external CLI state.
|
|
4459
|
+
let store = loadCoercedStore(resolveAuthStorePath(agentDir));
|
|
4460
|
+
if (!store) {
|
|
4461
|
+
store = { version: 1, profiles: {} };
|
|
4462
|
+
const legacy = coerceLegacyStore(loadJsonFile(resolveLegacyAuthStorePath(agentDir)));
|
|
4463
|
+
if (legacy) applyLegacyStore(store, legacy);
|
|
4464
|
+
mergeOAuthFileIntoStore(store);
|
|
4480
4465
|
}
|
|
4481
|
-
|
|
4482
|
-
const store = {
|
|
4483
|
-
version: 1,
|
|
4484
|
-
profiles: {}
|
|
4485
|
-
};
|
|
4486
|
-
if (legacy) applyLegacyStore(store, legacy);
|
|
4487
|
-
const mergedOAuth = mergeOAuthFileIntoStore(store);
|
|
4488
|
-
const syncedCli = syncExternalCliCredentials(store);
|
|
4489
|
-
const forceReadOnly = process.env.JOHNNESS_AUTH_STORE_READONLY === "1";
|
|
4490
|
-
const shouldWrite = !readOnly && !forceReadOnly && (legacy !== null || mergedOAuth || syncedCli);
|
|
4491
|
-
if (shouldWrite) saveJsonFile(authPath, store);
|
|
4492
|
-
if (shouldWrite && legacy !== null) {
|
|
4493
|
-
const legacyPath = resolveLegacyAuthStorePath(agentDir);
|
|
4494
|
-
try {
|
|
4495
|
-
fs.unlinkSync(legacyPath);
|
|
4496
|
-
} catch (err) {
|
|
4497
|
-
if (err?.code !== "ENOENT") log$8.warn("failed to delete legacy auth.json after migration", {
|
|
4498
|
-
err,
|
|
4499
|
-
legacyPath
|
|
4500
|
-
});
|
|
4501
|
-
}
|
|
4502
|
-
}
|
|
4503
|
-
return store;
|
|
4466
|
+
return readCodexOwnedAuthStore(agentDir, store);
|
|
4504
4467
|
}
|
|
4505
4468
|
function loadAuthProfileStoreForRuntime(agentDir, options) {
|
|
4506
4469
|
const store = loadAuthProfileStoreForAgent(agentDir, options);
|
|
@@ -4519,13 +4482,12 @@ function ensureAuthProfileStore(agentDir, options) {
|
|
|
4519
4482
|
const runtimeStore = resolveRuntimeAuthProfileStore(agentDir);
|
|
4520
4483
|
if (runtimeStore) return runtimeStore;
|
|
4521
4484
|
const store = loadAuthProfileStoreForAgent(agentDir, options);
|
|
4522
|
-
const authPath = resolveAuthStorePath(agentDir);
|
|
4523
4485
|
const mainAuthPath = resolveAuthStorePath();
|
|
4524
|
-
|
|
4525
|
-
return
|
|
4486
|
+
const effective = !agentDir || resolveAuthStorePath(agentDir) === mainAuthPath ? store : mergeAuthProfileStores(loadAuthProfileStoreForAgent(void 0, options), store);
|
|
4487
|
+
return readCodexOwnedAuthStore(agentDir, effective);
|
|
4526
4488
|
}
|
|
4527
|
-
function saveAuthProfileStore(store, agentDir) {
|
|
4528
|
-
|
|
4489
|
+
async function saveAuthProfileStore(store, agentDir) {
|
|
4490
|
+
await saveAuthStoreSnapshot({ ...codexOwnershipContext(agentDir), store: {
|
|
4529
4491
|
version: 1,
|
|
4530
4492
|
profiles: Object.fromEntries(Object.entries(store.profiles).map(([profileId, credential]) => {
|
|
4531
4493
|
if (credential.type === "api_key" && credential.keyRef && credential.key !== void 0) {
|
|
@@ -4543,7 +4505,7 @@ function saveAuthProfileStore(store, agentDir) {
|
|
|
4543
4505
|
order: store.order ?? void 0,
|
|
4544
4506
|
lastGood: store.lastGood ?? void 0,
|
|
4545
4507
|
usageStats: store.usageStats ?? void 0
|
|
4546
|
-
});
|
|
4508
|
+
} });
|
|
4547
4509
|
}
|
|
4548
4510
|
//#endregion
|
|
4549
4511
|
//#region src/agents/auth-profiles/profiles.ts
|
|
@@ -4568,7 +4530,7 @@ async function setAuthProfileOrder(params) {
|
|
|
4568
4530
|
}
|
|
4569
4531
|
});
|
|
4570
4532
|
}
|
|
4571
|
-
function upsertAuthProfile(params) {
|
|
4533
|
+
async function upsertAuthProfile(params) {
|
|
4572
4534
|
const credential = params.credential.type === "api_key" ? {
|
|
4573
4535
|
...params.credential,
|
|
4574
4536
|
...typeof params.credential.key === "string" ? { key: normalizeSecretInput(params.credential.key) } : {}
|
|
@@ -4576,18 +4538,11 @@ function upsertAuthProfile(params) {
|
|
|
4576
4538
|
...params.credential,
|
|
4577
4539
|
token: normalizeSecretInput(params.credential.token)
|
|
4578
4540
|
} : params.credential;
|
|
4579
|
-
|
|
4580
|
-
store.profiles[params.profileId] = credential;
|
|
4581
|
-
saveAuthProfileStore(store, params.agentDir);
|
|
4541
|
+
await mutateAuthProfile({ ...codexOwnershipContext(params.agentDir), profileId: params.profileId, credential: sanitizeAuthProfileCredential(credential) });
|
|
4582
4542
|
}
|
|
4583
4543
|
async function upsertAuthProfileWithLock(params) {
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
updater: (store) => {
|
|
4587
|
-
store.profiles[params.profileId] = params.credential;
|
|
4588
|
-
return true;
|
|
4589
|
-
}
|
|
4590
|
-
});
|
|
4544
|
+
await mutateAuthProfile({ ...codexOwnershipContext(params.agentDir), profileId: params.profileId, credential: sanitizeAuthProfileCredential(params.credential) });
|
|
4545
|
+
return readCodexOwnedAuthStore(params.agentDir);
|
|
4591
4546
|
}
|
|
4592
4547
|
function listProfilesForProvider(store, provider) {
|
|
4593
4548
|
const providerKey = normalizeProviderIdForAuth(provider);
|
|
@@ -4607,17 +4562,7 @@ async function markAuthProfileGood(params) {
|
|
|
4607
4562
|
return true;
|
|
4608
4563
|
}
|
|
4609
4564
|
});
|
|
4610
|
-
if (updated)
|
|
4611
|
-
store.lastGood = updated.lastGood;
|
|
4612
|
-
return;
|
|
4613
|
-
}
|
|
4614
|
-
const profile = store.profiles[profileId];
|
|
4615
|
-
if (!profile || profile.provider !== provider) return;
|
|
4616
|
-
store.lastGood = {
|
|
4617
|
-
...store.lastGood,
|
|
4618
|
-
[provider]: profileId
|
|
4619
|
-
};
|
|
4620
|
-
saveAuthProfileStore(store, agentDir);
|
|
4565
|
+
if (updated) store.lastGood = updated.lastGood;
|
|
4621
4566
|
}
|
|
4622
4567
|
//#endregion
|
|
4623
4568
|
//#region src/agents/auth-profiles/repair.ts
|
|
@@ -4939,7 +4884,7 @@ function resolveAgentMaxConcurrent(cfg) {
|
|
|
4939
4884
|
function resolveSubagentMaxConcurrent(cfg) {
|
|
4940
4885
|
const raw = cfg?.agents?.defaults?.subagents?.maxConcurrent;
|
|
4941
4886
|
if (typeof raw === "number" && Number.isFinite(raw)) return Math.max(1, Math.floor(raw));
|
|
4942
|
-
return
|
|
4887
|
+
return 100;
|
|
4943
4888
|
}
|
|
4944
4889
|
//#endregion
|
|
4945
4890
|
//#region src/config/talk.ts
|
|
@@ -5357,7 +5302,7 @@ function applyAgentDefaults(cfg) {
|
|
|
5357
5302
|
}
|
|
5358
5303
|
const nextSubagents = defaults?.subagents ? { ...defaults.subagents } : {};
|
|
5359
5304
|
if (!hasSubMax) {
|
|
5360
|
-
nextSubagents.maxConcurrent =
|
|
5305
|
+
nextSubagents.maxConcurrent = 100;
|
|
5361
5306
|
mutated = true;
|
|
5362
5307
|
}
|
|
5363
5308
|
if (!mutated) return cfg;
|
|
@@ -11026,7 +10971,7 @@ const AgentDefaultsSchema = z.object({
|
|
|
11026
10971
|
subagents: z.object({
|
|
11027
10972
|
maxConcurrent: z.number().int().positive().optional(),
|
|
11028
10973
|
maxSpawnDepth: z.number().int().min(1).max(5).optional().describe("Maximum nesting depth for sub-agent spawning. 1 = no nesting (default), 2 = sub-agents can spawn sub-sub-agents."),
|
|
11029
|
-
maxChildrenPerAgent: z.number().int().min(1).max(
|
|
10974
|
+
maxChildrenPerAgent: z.number().int().min(1).max(100).optional().describe("Maximum number of active children a single agent session can spawn (default: 100)."),
|
|
11030
10975
|
archiveAfterMinutes: z.number().int().positive().optional(),
|
|
11031
10976
|
model: AgentModelSchema.optional(),
|
|
11032
10977
|
thinking: z.string().optional(),
|
|
@@ -16298,19 +16243,15 @@ function buildOAuthProfileResult(params) {
|
|
|
16298
16243
|
function extractErrorMessage(error) {
|
|
16299
16244
|
return error instanceof Error ? error.message : String(error);
|
|
16300
16245
|
}
|
|
16301
|
-
|
|
16302
|
-
|
|
16303
|
-
|
|
16304
|
-
if (!/extract\s+accountid\s+from\s+token/i.test(message)) return false;
|
|
16305
|
-
return typeof params.credentials.access === "string" && params.credentials.access.trim().length > 0;
|
|
16306
|
-
}
|
|
16307
|
-
function adoptNewerMainOAuthCredential(params) {
|
|
16246
|
+
|
|
16247
|
+
async function adoptNewerMainOAuthCredential(params) {
|
|
16248
|
+
if (params.cred.provider === "openai-codex") return readCodexOwnedAuthStore(params.agentDir, params.store).profiles[params.profileId] ?? null;
|
|
16308
16249
|
if (!params.agentDir) return null;
|
|
16309
16250
|
try {
|
|
16310
16251
|
const mainCred = ensureAuthProfileStore(void 0).profiles[params.profileId];
|
|
16311
16252
|
if (mainCred?.type === "oauth" && mainCred.provider === params.cred.provider && Number.isFinite(mainCred.expires) && (!Number.isFinite(params.cred.expires) || mainCred.expires > params.cred.expires)) {
|
|
16312
16253
|
params.store.profiles[params.profileId] = { ...mainCred };
|
|
16313
|
-
saveAuthProfileStore(params.store, params.agentDir);
|
|
16254
|
+
await saveAuthProfileStore(params.store, params.agentDir);
|
|
16314
16255
|
log$8.info("adopted newer OAuth credentials from main agent", {
|
|
16315
16256
|
profileId: params.profileId,
|
|
16316
16257
|
agentDir: params.agentDir,
|
|
@@ -16327,11 +16268,22 @@ function adoptNewerMainOAuthCredential(params) {
|
|
|
16327
16268
|
return null;
|
|
16328
16269
|
}
|
|
16329
16270
|
async function refreshOAuthTokenWithLock(params) {
|
|
16271
|
+
const store = readCodexOwnedAuthStore(params.agentDir, params.store);
|
|
16272
|
+
const cred = store.profiles[params.profileId];
|
|
16273
|
+
if (cred?.provider === "openai-codex" || params.store?.profiles[params.profileId]?.provider === "openai-codex") return await refreshManagedCodexProfile({
|
|
16274
|
+
...codexOwnershipContext(params.agentDir),
|
|
16275
|
+
profileId: params.profileId,
|
|
16276
|
+
store: params.store
|
|
16277
|
+
});
|
|
16278
|
+
return await refreshUnmanagedOAuthTokenWithLock(params);
|
|
16279
|
+
}
|
|
16280
|
+
async function refreshUnmanagedOAuthTokenWithLock(params) {
|
|
16330
16281
|
const authPath = resolveAuthStorePath(params.agentDir);
|
|
16331
|
-
|
|
16282
|
+
fs.mkdirSync(path.dirname(authPath), { recursive: true, mode: 448 });
|
|
16332
16283
|
return await withFileLock(authPath, AUTH_STORE_LOCK_OPTIONS, async () => {
|
|
16333
16284
|
const store = ensureAuthProfileStore(params.agentDir);
|
|
16334
16285
|
const cred = store.profiles[params.profileId];
|
|
16286
|
+
if (cred?.provider === "openai-codex") throw new Error("Auth profile changed to Codex; resolve again through its owner");
|
|
16335
16287
|
if (!cred || cred.type !== "oauth") return null;
|
|
16336
16288
|
if (Date.now() < cred.expires) return {
|
|
16337
16289
|
apiKey: buildOAuthApiKey(cred.provider, cred),
|
|
@@ -16361,13 +16313,15 @@ async function refreshOAuthTokenWithLock(params) {
|
|
|
16361
16313
|
...result.newCredentials,
|
|
16362
16314
|
type: "oauth"
|
|
16363
16315
|
};
|
|
16364
|
-
saveAuthProfileStore(store, params.agentDir);
|
|
16316
|
+
await saveAuthProfileStore(store, params.agentDir);
|
|
16365
16317
|
return result;
|
|
16366
16318
|
});
|
|
16367
16319
|
}
|
|
16368
16320
|
async function tryResolveOAuthProfile(params) {
|
|
16369
16321
|
const { cfg, store, profileId } = params;
|
|
16370
|
-
const
|
|
16322
|
+
const effective = readCodexOwnedAuthStore(params.agentDir, store);
|
|
16323
|
+
store.profiles = effective.profiles;
|
|
16324
|
+
const cred = effective.profiles[profileId];
|
|
16371
16325
|
if (!cred || cred.type !== "oauth") return null;
|
|
16372
16326
|
if (!isProfileConfigCompatible({
|
|
16373
16327
|
cfg,
|
|
@@ -16375,6 +16329,11 @@ async function tryResolveOAuthProfile(params) {
|
|
|
16375
16329
|
provider: cred.provider,
|
|
16376
16330
|
mode: cred.type
|
|
16377
16331
|
})) return null;
|
|
16332
|
+
if (cred.type === "oauth" && cred.provider === "openai-codex") {
|
|
16333
|
+
const result = await refreshManagedCodexProfile({ ...codexOwnershipContext(params.agentDir), profileId, store });
|
|
16334
|
+
if (!result) return null;
|
|
16335
|
+
return buildApiKeyProfileResult({ apiKey: result.apiKey, provider: "openai-codex", email: result.newCredentials?.email ?? cred.email });
|
|
16336
|
+
}
|
|
16378
16337
|
if (Date.now() < cred.expires) return buildOAuthProfileResult({
|
|
16379
16338
|
provider: cred.provider,
|
|
16380
16339
|
credentials: cred,
|
|
@@ -16427,7 +16386,9 @@ async function resolveProfileSecretString(params) {
|
|
|
16427
16386
|
}
|
|
16428
16387
|
async function resolveApiKeyForProfile(params) {
|
|
16429
16388
|
const { cfg, store, profileId } = params;
|
|
16430
|
-
const
|
|
16389
|
+
const effective = readCodexOwnedAuthStore(params.agentDir, store);
|
|
16390
|
+
store.profiles = effective.profiles;
|
|
16391
|
+
const cred = effective.profiles[profileId];
|
|
16431
16392
|
if (!cred) return null;
|
|
16432
16393
|
if (!isProfileConfigCompatible({
|
|
16433
16394
|
cfg,
|
|
@@ -16436,6 +16397,11 @@ async function resolveApiKeyForProfile(params) {
|
|
|
16436
16397
|
mode: cred.type,
|
|
16437
16398
|
allowOAuthTokenCompatibility: true
|
|
16438
16399
|
})) return null;
|
|
16400
|
+
if (cred.type === "oauth" && cred.provider === "openai-codex") {
|
|
16401
|
+
const result = await refreshManagedCodexProfile({ ...codexOwnershipContext(params.agentDir), profileId, store });
|
|
16402
|
+
if (!result) return null;
|
|
16403
|
+
return buildApiKeyProfileResult({ apiKey: result.apiKey, provider: "openai-codex", email: result.newCredentials?.email ?? cred.email });
|
|
16404
|
+
}
|
|
16439
16405
|
const refResolveCache = {};
|
|
16440
16406
|
const configForRefResolution = cfg ?? loadConfig();
|
|
16441
16407
|
const refDefaults = configForRefResolution.secrets?.defaults;
|
|
@@ -16479,7 +16445,7 @@ async function resolveApiKeyForProfile(params) {
|
|
|
16479
16445
|
email: cred.email
|
|
16480
16446
|
});
|
|
16481
16447
|
}
|
|
16482
|
-
const oauthCred = adoptNewerMainOAuthCredential({
|
|
16448
|
+
const oauthCred = await adoptNewerMainOAuthCredential({
|
|
16483
16449
|
store,
|
|
16484
16450
|
profileId,
|
|
16485
16451
|
agentDir: params.agentDir,
|
|
@@ -16504,6 +16470,7 @@ async function resolveApiKeyForProfile(params) {
|
|
|
16504
16470
|
} catch (error) {
|
|
16505
16471
|
const refreshedStore = ensureAuthProfileStore(params.agentDir);
|
|
16506
16472
|
const refreshed = refreshedStore.profiles[profileId];
|
|
16473
|
+
if (refreshed?.type === "oauth" && refreshed.provider === "openai-codex") return await resolveApiKeyForProfile({ ...params, store: refreshedStore });
|
|
16507
16474
|
if (refreshed?.type === "oauth" && Date.now() < refreshed.expires) return buildOAuthProfileResult({
|
|
16508
16475
|
provider: refreshed.provider,
|
|
16509
16476
|
credentials: refreshed,
|
|
@@ -16526,9 +16493,9 @@ async function resolveApiKeyForProfile(params) {
|
|
|
16526
16493
|
} catch {}
|
|
16527
16494
|
if (params.agentDir) try {
|
|
16528
16495
|
const mainCred = ensureAuthProfileStore(void 0).profiles[profileId];
|
|
16529
|
-
if (mainCred?.type === "oauth" && Date.now() < mainCred.expires) {
|
|
16496
|
+
if (mainCred?.type === "oauth" && mainCred.provider === cred.provider && Date.now() < mainCred.expires) {
|
|
16530
16497
|
refreshedStore.profiles[profileId] = { ...mainCred };
|
|
16531
|
-
saveAuthProfileStore(refreshedStore, params.agentDir);
|
|
16498
|
+
await saveAuthProfileStore(refreshedStore, params.agentDir);
|
|
16532
16499
|
log$8.info("inherited fresh OAuth credentials from main agent", {
|
|
16533
16500
|
profileId,
|
|
16534
16501
|
agentDir: params.agentDir,
|
|
@@ -16541,21 +16508,6 @@ async function resolveApiKeyForProfile(params) {
|
|
|
16541
16508
|
});
|
|
16542
16509
|
}
|
|
16543
16510
|
} catch {}
|
|
16544
|
-
if (shouldUseOpenaiCodexRefreshFallback({
|
|
16545
|
-
provider: cred.provider,
|
|
16546
|
-
credentials: cred,
|
|
16547
|
-
error
|
|
16548
|
-
})) {
|
|
16549
|
-
log$8.warn("openai-codex oauth refresh failed; using cached access token fallback", {
|
|
16550
|
-
profileId,
|
|
16551
|
-
provider: cred.provider
|
|
16552
|
-
});
|
|
16553
|
-
return buildApiKeyProfileResult({
|
|
16554
|
-
apiKey: cred.access,
|
|
16555
|
-
provider: cred.provider,
|
|
16556
|
-
email: cred.email
|
|
16557
|
-
});
|
|
16558
|
-
}
|
|
16559
16511
|
const message = extractErrorMessage(error);
|
|
16560
16512
|
const hint = formatAuthDoctorHint({
|
|
16561
16513
|
cfg,
|
|
@@ -16728,13 +16680,7 @@ async function markAuthProfileUsed(params) {
|
|
|
16728
16680
|
return true;
|
|
16729
16681
|
}
|
|
16730
16682
|
});
|
|
16731
|
-
if (updated)
|
|
16732
|
-
store.usageStats = updated.usageStats;
|
|
16733
|
-
return;
|
|
16734
|
-
}
|
|
16735
|
-
if (!store.profiles[profileId]) return;
|
|
16736
|
-
updateUsageStatsEntry(store, profileId, (existing) => resetUsageStats(existing, { lastUsed: Date.now() }));
|
|
16737
|
-
saveAuthProfileStore(store, agentDir);
|
|
16683
|
+
if (updated) store.usageStats = updated.usageStats;
|
|
16738
16684
|
}
|
|
16739
16685
|
function calculateAuthProfileCooldownMs(errorCount) {
|
|
16740
16686
|
const normalized = Math.max(1, errorCount);
|
|
@@ -16855,23 +16801,7 @@ async function markAuthProfileFailure(params) {
|
|
|
16855
16801
|
return true;
|
|
16856
16802
|
}
|
|
16857
16803
|
});
|
|
16858
|
-
if (updated)
|
|
16859
|
-
store.usageStats = updated.usageStats;
|
|
16860
|
-
return;
|
|
16861
|
-
}
|
|
16862
|
-
if (!store.profiles[profileId]) return;
|
|
16863
|
-
const now = Date.now();
|
|
16864
|
-
const cfgResolved = resolveAuthCooldownConfig({
|
|
16865
|
-
cfg,
|
|
16866
|
-
providerId: normalizeProviderId(store.profiles[profileId]?.provider ?? "")
|
|
16867
|
-
});
|
|
16868
|
-
updateUsageStatsEntry(store, profileId, (existing) => computeNextProfileUsageStats({
|
|
16869
|
-
existing: existing ?? {},
|
|
16870
|
-
now,
|
|
16871
|
-
reason,
|
|
16872
|
-
cfgResolved
|
|
16873
|
-
}));
|
|
16874
|
-
saveAuthProfileStore(store, agentDir);
|
|
16804
|
+
if (updated) store.usageStats = updated.usageStats;
|
|
16875
16805
|
}
|
|
16876
16806
|
//#endregion
|
|
16877
16807
|
//#region src/agents/auth-profiles/order.ts
|