@khalilgharbaoui/opencode-claude-code-plugin 0.46.1 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +42 -0
- package/dist/index.js +342 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +32 -11
package/dist/index.js
CHANGED
|
@@ -154,7 +154,16 @@ function translateStreamForHost(stream, dialect = "v1") {
|
|
|
154
154
|
import { homedir as homedir2 } from "os";
|
|
155
155
|
|
|
156
156
|
// src/accounts.ts
|
|
157
|
-
import {
|
|
157
|
+
import {
|
|
158
|
+
chmod,
|
|
159
|
+
copyFile,
|
|
160
|
+
link,
|
|
161
|
+
lstat,
|
|
162
|
+
mkdir,
|
|
163
|
+
readlink,
|
|
164
|
+
symlink,
|
|
165
|
+
writeFile
|
|
166
|
+
} from "fs/promises";
|
|
158
167
|
import path from "path";
|
|
159
168
|
|
|
160
169
|
// src/logger.ts
|
|
@@ -401,8 +410,9 @@ function resolveAccounts(value) {
|
|
|
401
410
|
function accountProviderId(account) {
|
|
402
411
|
return `${BASE_PROVIDER_ID}-${normalizeAccountName(account)}`;
|
|
403
412
|
}
|
|
404
|
-
function accountDisplayName(account) {
|
|
405
|
-
|
|
413
|
+
function accountDisplayName(account, tier) {
|
|
414
|
+
const name = titleizeAccount(account);
|
|
415
|
+
return `Claude Code (${tier ? `${name}, ${tier}` : name})`;
|
|
406
416
|
}
|
|
407
417
|
function accountModelSuffix(account) {
|
|
408
418
|
const normalized = normalizeAccountName(account);
|
|
@@ -425,13 +435,14 @@ function expandHome(value) {
|
|
|
425
435
|
}
|
|
426
436
|
return value;
|
|
427
437
|
}
|
|
428
|
-
async function ensureAccountRuntime(account, baseCliPath) {
|
|
438
|
+
async function ensureAccountRuntime(account, baseCliPath, deps = {}) {
|
|
439
|
+
const platform2 = deps.platform ?? process.platform;
|
|
429
440
|
const configDir = accountConfigDir(account);
|
|
430
441
|
if (!configDir) return { cliPath: baseCliPath };
|
|
431
442
|
const expandedConfigDir = expandHome(configDir);
|
|
432
443
|
await mkdir(expandedConfigDir, { recursive: true });
|
|
433
444
|
try {
|
|
434
|
-
await ensureSharedCapabilities(expandedConfigDir);
|
|
445
|
+
await ensureSharedCapabilities(expandedConfigDir, platform2);
|
|
435
446
|
} catch (err) {
|
|
436
447
|
log.warn("failed to symlink shared capabilities; continuing anyway", {
|
|
437
448
|
account,
|
|
@@ -439,6 +450,13 @@ async function ensureAccountRuntime(account, baseCliPath) {
|
|
|
439
450
|
error: String(err)
|
|
440
451
|
});
|
|
441
452
|
}
|
|
453
|
+
if (platform2 === "win32") {
|
|
454
|
+
return {
|
|
455
|
+
cliPath: baseCliPath,
|
|
456
|
+
configDir: expandedConfigDir,
|
|
457
|
+
accountInProcess: true
|
|
458
|
+
};
|
|
459
|
+
}
|
|
442
460
|
const cliPath = await writeAccountWrapper(
|
|
443
461
|
normalizeAccountName(account),
|
|
444
462
|
baseCliPath,
|
|
@@ -446,13 +464,21 @@ async function ensureAccountRuntime(account, baseCliPath) {
|
|
|
446
464
|
);
|
|
447
465
|
return { cliPath, configDir: expandedConfigDir };
|
|
448
466
|
}
|
|
449
|
-
async function ensureSharedCapabilities(targetRoot) {
|
|
467
|
+
async function ensureSharedCapabilities(targetRoot, platform2) {
|
|
450
468
|
const sourceRoot = expandHome("~/.claude");
|
|
451
469
|
for (const item of SHARED_CAPABILITY_ITEMS) {
|
|
452
|
-
|
|
470
|
+
try {
|
|
471
|
+
await ensureSharedCapabilityItem(sourceRoot, targetRoot, item, platform2);
|
|
472
|
+
} catch (err) {
|
|
473
|
+
log.warn("could not share a Claude capability with the account", {
|
|
474
|
+
item,
|
|
475
|
+
target: path.join(targetRoot, item),
|
|
476
|
+
error: String(err)
|
|
477
|
+
});
|
|
478
|
+
}
|
|
453
479
|
}
|
|
454
480
|
}
|
|
455
|
-
async function ensureSharedCapabilityItem(sourceRoot, targetRoot, item) {
|
|
481
|
+
async function ensureSharedCapabilityItem(sourceRoot, targetRoot, item, platform2) {
|
|
456
482
|
const source = path.join(sourceRoot, item);
|
|
457
483
|
const target = path.join(targetRoot, item);
|
|
458
484
|
let sourceStat;
|
|
@@ -477,8 +503,29 @@ async function ensureSharedCapabilityItem(sourceRoot, targetRoot, item) {
|
|
|
477
503
|
return;
|
|
478
504
|
} catch {
|
|
479
505
|
}
|
|
480
|
-
|
|
481
|
-
|
|
506
|
+
if (sourceStat.isDirectory()) {
|
|
507
|
+
await symlink(source, target, platform2 === "win32" ? "junction" : "dir");
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (platform2 !== "win32") {
|
|
511
|
+
await symlink(source, target, "file");
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
await symlink(source, target, "file");
|
|
516
|
+
return;
|
|
517
|
+
} catch {
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
await link(source, target);
|
|
521
|
+
return;
|
|
522
|
+
} catch {
|
|
523
|
+
}
|
|
524
|
+
await copyFile(source, target);
|
|
525
|
+
log.warn(
|
|
526
|
+
"copied a shared Claude capability into the account config dir instead of linking it; later edits to the original will not be picked up",
|
|
527
|
+
{ item, target }
|
|
528
|
+
);
|
|
482
529
|
}
|
|
483
530
|
async function writeAccountWrapper(account, baseCliPath, configDir) {
|
|
484
531
|
const cacheRoot = path.join(
|
|
@@ -1490,7 +1537,7 @@ function createAccountFailoverQuestionCall(sessionKey2, input, toolCallId = `${A
|
|
|
1490
1537
|
options: [
|
|
1491
1538
|
...candidates.map((candidate) => ({
|
|
1492
1539
|
label: candidate,
|
|
1493
|
-
description: `Run on "${candidate}" until ${until}. The conversation is replayed as a fresh Claude session
|
|
1540
|
+
description: input.carryTranscript === false ? `Run on "${candidate}" until ${until}. The conversation is replayed as a fresh Claude session, and any MCP server configured only in "${source}"'s Claude profile will be missing.` : `Run on "${candidate}" until ${until}. The conversation continues where it stopped if its Claude transcript can be carried across, and is replayed as a fresh Claude session if it cannot; either way, any MCP server configured only in "${source}"'s Claude profile will be missing.`
|
|
1494
1541
|
})),
|
|
1495
1542
|
{
|
|
1496
1543
|
label: STOP_ANSWER,
|
|
@@ -1601,20 +1648,20 @@ function stripAccountFailoverParts(prompt) {
|
|
|
1601
1648
|
}
|
|
1602
1649
|
return changed ? out : prompt;
|
|
1603
1650
|
}
|
|
1604
|
-
function failoverContinuationText(target) {
|
|
1651
|
+
function failoverContinuationText(target, resumed = false) {
|
|
1605
1652
|
return [
|
|
1606
|
-
`The Claude account this conversation was running on hit its usage limit, so it has been moved to the "${target}" account and you are now in a fresh Claude session.`,
|
|
1607
|
-
"
|
|
1653
|
+
resumed ? `The Claude account this conversation was running on hit its usage limit, so it has been moved to the "${target}" account. This is the same conversation, continued: everything you already know still stands.` : `The Claude account this conversation was running on hit its usage limit, so it has been moved to the "${target}" account and you are now in a fresh Claude session. The conversation so far is above.`,
|
|
1654
|
+
"Continue the task from where it stopped: do not start over, do not re-plan, and do not repeat work that is already done.",
|
|
1608
1655
|
"Do not mention the account switch unless you are asked about it."
|
|
1609
1656
|
].join(" ");
|
|
1610
1657
|
}
|
|
1611
|
-
function buildFailoverContinuationPrompt(prompt, target) {
|
|
1658
|
+
function buildFailoverContinuationPrompt(prompt, target, resumed = false) {
|
|
1612
1659
|
const stripped = stripAccountFailoverParts(prompt);
|
|
1613
1660
|
return [
|
|
1614
1661
|
...stripped,
|
|
1615
1662
|
{
|
|
1616
1663
|
role: "user",
|
|
1617
|
-
content: [{ type: "text", text: failoverContinuationText(target) }]
|
|
1664
|
+
content: [{ type: "text", text: failoverContinuationText(target, resumed) }]
|
|
1618
1665
|
}
|
|
1619
1666
|
];
|
|
1620
1667
|
}
|
|
@@ -5067,6 +5114,9 @@ function claudeSpawnEnv(opts) {
|
|
|
5067
5114
|
if (opts?.promptCacheTtl) {
|
|
5068
5115
|
env.CLAUDE_CODE_PROMPT_CACHE_TTL = opts.promptCacheTtl;
|
|
5069
5116
|
}
|
|
5117
|
+
if (opts?.configDir) {
|
|
5118
|
+
env.CLAUDE_CONFIG_DIR = opts.configDir;
|
|
5119
|
+
}
|
|
5070
5120
|
if (opts?.ignoreAnthropicApiKey) {
|
|
5071
5121
|
delete env.ANTHROPIC_API_KEY;
|
|
5072
5122
|
delete env.ANTHROPIC_AUTH_TOKEN;
|
|
@@ -5184,6 +5234,11 @@ function getActiveProcess(key) {
|
|
|
5184
5234
|
}
|
|
5185
5235
|
return ap;
|
|
5186
5236
|
}
|
|
5237
|
+
function processBelongsToAnotherAccount(active, cliPath, configDir) {
|
|
5238
|
+
if (!active) return false;
|
|
5239
|
+
if (active.cliPath && active.cliPath !== cliPath) return true;
|
|
5240
|
+
return !!active.configDir && active.configDir !== configDir;
|
|
5241
|
+
}
|
|
5187
5242
|
function setActiveProcess(key, ap) {
|
|
5188
5243
|
cancelIdleProcessEviction(key);
|
|
5189
5244
|
activeProcesses.set(key, ap);
|
|
@@ -5399,7 +5454,7 @@ function invalidateOtherEffortSessions(baseKey, effort) {
|
|
|
5399
5454
|
clearBackgroundTasks(key);
|
|
5400
5455
|
}
|
|
5401
5456
|
}
|
|
5402
|
-
function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash, systemPromptFile, ignoreAnthropicApiKey, effort, promptCacheTtl) {
|
|
5457
|
+
function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash, systemPromptFile, ignoreAnthropicApiKey, effort, promptCacheTtl, configDir) {
|
|
5403
5458
|
evictIfNeeded();
|
|
5404
5459
|
log.info("spawning new claude process", {
|
|
5405
5460
|
cliPath,
|
|
@@ -5407,13 +5462,14 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
|
|
|
5407
5462
|
cwd,
|
|
5408
5463
|
sessionKey: sessionKey2,
|
|
5409
5464
|
effort,
|
|
5410
|
-
promptCacheTtl
|
|
5465
|
+
promptCacheTtl,
|
|
5466
|
+
configDir
|
|
5411
5467
|
});
|
|
5412
5468
|
const plan = planClaudeSpawn(cliPath, cliArgs);
|
|
5413
5469
|
const proc = spawn(plan.file, plan.args, {
|
|
5414
5470
|
cwd,
|
|
5415
5471
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5416
|
-
env: claudeSpawnEnv({ ignoreAnthropicApiKey, effort, promptCacheTtl }),
|
|
5472
|
+
env: claudeSpawnEnv({ ignoreAnthropicApiKey, effort, promptCacheTtl, configDir }),
|
|
5417
5473
|
windowsVerbatimArguments: plan.windowsVerbatimArguments
|
|
5418
5474
|
});
|
|
5419
5475
|
const lineEmitter = new EventEmitter3();
|
|
@@ -5428,6 +5484,7 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
|
|
|
5428
5484
|
startedAt: Date.now(),
|
|
5429
5485
|
cliPath,
|
|
5430
5486
|
cliArgs: [...cliArgs],
|
|
5487
|
+
configDir,
|
|
5431
5488
|
unattendedLines: [],
|
|
5432
5489
|
unattendedDropped: 0
|
|
5433
5490
|
};
|
|
@@ -5553,7 +5610,8 @@ function respawnActiveProcess(sessionKey2, cliPath, cliArgs, cwd, ignoreAnthropi
|
|
|
5553
5610
|
old.systemPromptFile,
|
|
5554
5611
|
ignoreAnthropicApiKey,
|
|
5555
5612
|
old.effort,
|
|
5556
|
-
old.promptCacheTtl
|
|
5613
|
+
old.promptCacheTtl,
|
|
5614
|
+
old.configDir
|
|
5557
5615
|
);
|
|
5558
5616
|
replacement.mcpServers = old.mcpServers;
|
|
5559
5617
|
replacement.pendingProxyCompletions = old.pendingProxyCompletions;
|
|
@@ -5984,6 +6042,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
5984
6042
|
"ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN detected; stripping it from claude spawns (ignoreAnthropicApiKey) so requests use your subscription auth, not pay-as-you-go API billing.",
|
|
5985
6043
|
"Code Mode execute not forwarded: no model-visible input schema",
|
|
5986
6044
|
"Code Mode execute not forwarded: read-only permission preset",
|
|
6045
|
+
"MCP server asked for operator input and was declined: a headless Claude Code session cannot prompt, so its elicitation can only be refused. Run that server's flow in Claude Code directly, or configure it not to elicit.",
|
|
5987
6046
|
"MCP tool not routed through the proxy: another proxy tool already holds that name, and it keeps it",
|
|
5988
6047
|
"account failover declined; ending the turn",
|
|
5989
6048
|
"agent effort override refused: unknown level",
|
|
@@ -5994,6 +6053,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
5994
6053
|
"btw: a turn is running but no claude process appeared for it",
|
|
5995
6054
|
"btw: early aside failed; the message will ask again",
|
|
5996
6055
|
"cannot interrupt this transport; waiting for the turn to end",
|
|
6056
|
+
"carried this conversation's claude transcript to the other account",
|
|
5997
6057
|
"carrying the claude conversation over to the new session key",
|
|
5998
6058
|
"claude --version output unparseable",
|
|
5999
6059
|
"claude account is out of usage; the turn ends with a note",
|
|
@@ -6019,6 +6079,8 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
6019
6079
|
"cleanup-stale: error processing cache root",
|
|
6020
6080
|
"cleanup-stale: rmSync failed",
|
|
6021
6081
|
"continuing this conversation's claude session under the new model or effort instead of replaying it",
|
|
6082
|
+
"copied a shared Claude capability into the account config dir instead of linking it; later edits to the original will not be picked up",
|
|
6083
|
+
"could not share a Claude capability with the account",
|
|
6022
6084
|
"delivering proxy results after interrupted continuation",
|
|
6023
6085
|
"draining proxy calls queued between turns",
|
|
6024
6086
|
"empty user content; sending sentinel to satisfy CLI",
|
|
@@ -6091,6 +6153,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
6091
6153
|
"remote URL images are not supported by Claude CLI, skipping",
|
|
6092
6154
|
"replaying stdout the child emitted between turns",
|
|
6093
6155
|
"replaying the conversation as text: no claude session was available to continue",
|
|
6156
|
+
"replaying this conversation as text on the other account",
|
|
6094
6157
|
"result fallback timer fired \u2014 closing stream without result event",
|
|
6095
6158
|
"resuming the claude session from before the restart instead of replaying it",
|
|
6096
6159
|
"shared Claude capability already exists; leaving untouched",
|
|
@@ -6135,6 +6198,8 @@ var BUNDLE_DATA_ALLOWLIST = {
|
|
|
6135
6198
|
emitted: "flag",
|
|
6136
6199
|
enabled: "flag",
|
|
6137
6200
|
executed: "flag",
|
|
6201
|
+
failedOver: "flag",
|
|
6202
|
+
renamed: "flag",
|
|
6138
6203
|
isUsingOverage: "flag",
|
|
6139
6204
|
hadProxyActivity: "flag",
|
|
6140
6205
|
hadReasoning: "flag",
|
|
@@ -6204,6 +6269,8 @@ var BUNDLE_DATA_ALLOWLIST = {
|
|
|
6204
6269
|
finishReason: "enum",
|
|
6205
6270
|
hash: "enum",
|
|
6206
6271
|
hostApi: "enum",
|
|
6272
|
+
// One of `SHARED_CAPABILITY_ITEMS`: a fixed list this package defines.
|
|
6273
|
+
item: "enum",
|
|
6207
6274
|
kind: "enum",
|
|
6208
6275
|
level: "enum",
|
|
6209
6276
|
mode: "enum",
|
|
@@ -6513,8 +6580,8 @@ function redactLogLine(raw, context) {
|
|
|
6513
6580
|
if (!parsed.data || Object.keys(parsed.data).length === 0) return head;
|
|
6514
6581
|
return `${head} ${JSON.stringify(sanitizeLogData(parsed.data, context))}`;
|
|
6515
6582
|
}
|
|
6516
|
-
function readLogTail(
|
|
6517
|
-
const fd = openSync(
|
|
6583
|
+
function readLogTail(path14, maxBytes = BUNDLE_TAIL_BYTES) {
|
|
6584
|
+
const fd = openSync(path14, "r");
|
|
6518
6585
|
try {
|
|
6519
6586
|
const size = fstatSync(fd).size;
|
|
6520
6587
|
const length = Math.min(size, maxBytes);
|
|
@@ -6694,7 +6761,10 @@ async function fetchPlanUsage(cliPath, options = {}) {
|
|
|
6694
6761
|
if (support && await support(cliPath) === "unsupported") {
|
|
6695
6762
|
return { status: "failed", error: "Plan usage is unavailable: this Claude CLI does not support headless output. Check usage in the Claude CLI directly." };
|
|
6696
6763
|
}
|
|
6697
|
-
const env = claudeSpawnEnv({
|
|
6764
|
+
const env = claudeSpawnEnv({
|
|
6765
|
+
ignoreAnthropicApiKey: options.ignoreAnthropicApiKey,
|
|
6766
|
+
configDir: options.configDir
|
|
6767
|
+
});
|
|
6698
6768
|
const stdout = await run(
|
|
6699
6769
|
cliPath,
|
|
6700
6770
|
["-p", "/cost", "--output-format", "json"],
|
|
@@ -7365,7 +7435,8 @@ async function gatherDoctorReport(options) {
|
|
|
7365
7435
|
});
|
|
7366
7436
|
}
|
|
7367
7437
|
const planUsage = wantsPlanUsage(options.argument ?? "") ? await (options.planUsageImpl ?? fetchPlanUsage)(cliPath, {
|
|
7368
|
-
ignoreAnthropicApiKey: options.ignoreAnthropicApiKey
|
|
7438
|
+
ignoreAnthropicApiKey: options.ignoreAnthropicApiKey,
|
|
7439
|
+
configDir: options.configDir
|
|
7369
7440
|
}) : { status: "not-requested" };
|
|
7370
7441
|
return {
|
|
7371
7442
|
plugin: base.plugin,
|
|
@@ -9127,6 +9198,14 @@ function classifyScreen(text) {
|
|
|
9127
9198
|
}
|
|
9128
9199
|
return null;
|
|
9129
9200
|
}
|
|
9201
|
+
function interactiveTranscriptPath(opts) {
|
|
9202
|
+
return path10.join(
|
|
9203
|
+
resolveConfigDir(opts.configDir),
|
|
9204
|
+
"projects",
|
|
9205
|
+
encodeCwd(path10.resolve(opts.cwd)),
|
|
9206
|
+
`${opts.sessionId}.jsonl`
|
|
9207
|
+
);
|
|
9208
|
+
}
|
|
9130
9209
|
function configDirEnv(configDir) {
|
|
9131
9210
|
return configDir ? { CLAUDE_CONFIG_DIR: configDir } : {};
|
|
9132
9211
|
}
|
|
@@ -10637,14 +10716,74 @@ function spawnInteractiveProcess(opts) {
|
|
|
10637
10716
|
return ap;
|
|
10638
10717
|
}
|
|
10639
10718
|
|
|
10719
|
+
// src/account-transcript.ts
|
|
10720
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
10721
|
+
import { constants as fsConstants } from "fs";
|
|
10722
|
+
import { chmod as chmod2, copyFile as copyFile2, lstat as lstat2, mkdir as mkdir2 } from "fs/promises";
|
|
10723
|
+
import path11 from "path";
|
|
10724
|
+
var transcriptPath = interactiveTranscriptPath;
|
|
10725
|
+
function configDirForAccount(account) {
|
|
10726
|
+
const normalized = normalizeAccountName(account || DEFAULT_ACCOUNT);
|
|
10727
|
+
return resolveConfigDir(
|
|
10728
|
+
normalized === DEFAULT_ACCOUNT ? void 0 : accountConfigDirPath(normalized)
|
|
10729
|
+
);
|
|
10730
|
+
}
|
|
10731
|
+
async function carryTranscriptToAccount(input) {
|
|
10732
|
+
const { sessionId, cwd, targetConfigDir } = input;
|
|
10733
|
+
const targetOwn = transcriptPath({ configDir: targetConfigDir, cwd, sessionId });
|
|
10734
|
+
const targetOwnStat = await statTranscript(targetOwn);
|
|
10735
|
+
if (targetOwnStat.kind === "other") {
|
|
10736
|
+
return { kind: "refused", reason: "target-not-a-file", detail: targetOwn };
|
|
10737
|
+
}
|
|
10738
|
+
let live;
|
|
10739
|
+
for (const dir of dedupe2([targetConfigDir, ...input.accountConfigDirs])) {
|
|
10740
|
+
const candidate = transcriptPath({ configDir: dir, cwd, sessionId });
|
|
10741
|
+
const stat = candidate === targetOwn ? targetOwnStat : await statTranscript(candidate);
|
|
10742
|
+
if (stat.kind !== "file") continue;
|
|
10743
|
+
if (!live || stat.size > live.size) live = { file: candidate, size: stat.size };
|
|
10744
|
+
}
|
|
10745
|
+
if (!live) return { kind: "refused", reason: "no-transcript-anywhere" };
|
|
10746
|
+
if (live.file === targetOwn) return { kind: "already-there", sessionId };
|
|
10747
|
+
const renamed = targetOwnStat.kind === "file";
|
|
10748
|
+
const carriedId = renamed ? (input.newSessionId ?? randomUUID6)() : sessionId;
|
|
10749
|
+
const target = renamed ? transcriptPath({ configDir: targetConfigDir, cwd, sessionId: carriedId }) : targetOwn;
|
|
10750
|
+
try {
|
|
10751
|
+
await mkdir2(path11.dirname(target), { recursive: true, mode: 448 });
|
|
10752
|
+
await copyFile2(live.file, target, fsConstants.COPYFILE_EXCL);
|
|
10753
|
+
await chmod2(target, 384);
|
|
10754
|
+
} catch (err) {
|
|
10755
|
+
return { kind: "refused", reason: "copy-failed", detail: String(err) };
|
|
10756
|
+
}
|
|
10757
|
+
return { kind: "carried", sessionId: carriedId, from: live.file, to: target, renamed };
|
|
10758
|
+
}
|
|
10759
|
+
async function statTranscript(file) {
|
|
10760
|
+
try {
|
|
10761
|
+
const stat = await lstat2(file);
|
|
10762
|
+
return stat.isFile() ? { kind: "file", size: stat.size } : { kind: "other", size: 0 };
|
|
10763
|
+
} catch {
|
|
10764
|
+
return { kind: "none", size: 0 };
|
|
10765
|
+
}
|
|
10766
|
+
}
|
|
10767
|
+
function dedupe2(dirs) {
|
|
10768
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10769
|
+
const out = [];
|
|
10770
|
+
for (const dir of dirs) {
|
|
10771
|
+
const resolved = path11.resolve(dir);
|
|
10772
|
+
if (seen.has(resolved)) continue;
|
|
10773
|
+
seen.add(resolved);
|
|
10774
|
+
out.push(resolved);
|
|
10775
|
+
}
|
|
10776
|
+
return out;
|
|
10777
|
+
}
|
|
10778
|
+
|
|
10640
10779
|
// src/prompts.ts
|
|
10641
10780
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
10642
10781
|
import { homedir as homedir8 } from "os";
|
|
10643
|
-
import { randomUUID as
|
|
10782
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
10644
10783
|
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join11 } from "path";
|
|
10645
|
-
function readPromptFileIfPresent(
|
|
10784
|
+
function readPromptFileIfPresent(path14) {
|
|
10646
10785
|
try {
|
|
10647
|
-
const content = readFileSync6(
|
|
10786
|
+
const content = readFileSync6(path14, "utf8").trim();
|
|
10648
10787
|
return content || void 0;
|
|
10649
10788
|
} catch {
|
|
10650
10789
|
return void 0;
|
|
@@ -10749,9 +10888,9 @@ function extractSystemMessages(prompt) {
|
|
|
10749
10888
|
}
|
|
10750
10889
|
var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md"]);
|
|
10751
10890
|
function userAuthoredInstructions(forwarded, opts = {}) {
|
|
10752
|
-
const readFile2 = opts.readFile ?? ((
|
|
10891
|
+
const readFile2 = opts.readFile ?? ((path14) => {
|
|
10753
10892
|
try {
|
|
10754
|
-
return readFileSync6(
|
|
10893
|
+
return readFileSync6(path14, "utf8");
|
|
10755
10894
|
} catch {
|
|
10756
10895
|
return null;
|
|
10757
10896
|
}
|
|
@@ -10763,16 +10902,16 @@ function userAuthoredInstructions(forwarded, opts = {}) {
|
|
|
10763
10902
|
}
|
|
10764
10903
|
const seen = /* @__PURE__ */ new Set();
|
|
10765
10904
|
for (const match of forwarded.matchAll(/^Instructions from: (.+)$/gm)) {
|
|
10766
|
-
const
|
|
10767
|
-
if (seen.has(
|
|
10768
|
-
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(
|
|
10769
|
-
seen.add(
|
|
10770
|
-
const file = readFile2(
|
|
10905
|
+
const path14 = match[1].trim();
|
|
10906
|
+
if (seen.has(path14) || !isAbsolute3(path14)) continue;
|
|
10907
|
+
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path14))) continue;
|
|
10908
|
+
seen.add(path14);
|
|
10909
|
+
const file = readFile2(path14);
|
|
10771
10910
|
if (!file?.trim()) continue;
|
|
10772
10911
|
const start = match.index + match[0].length + 1;
|
|
10773
10912
|
const content = forwarded.startsWith(file, start) ? file : forwarded.startsWith(file.trimEnd(), start) ? file.trimEnd() : null;
|
|
10774
10913
|
if (content === null) continue;
|
|
10775
|
-
out.push({ text: `Instructions from: ${
|
|
10914
|
+
out.push({ text: `Instructions from: ${path14}
|
|
10776
10915
|
${content}`, content });
|
|
10777
10916
|
}
|
|
10778
10917
|
return out;
|
|
@@ -10809,10 +10948,10 @@ ${options.compressionSummary.trim()}`
|
|
|
10809
10948
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
10810
10949
|
const content = parts.join("\n\n");
|
|
10811
10950
|
if (!content) return void 0;
|
|
10812
|
-
const
|
|
10951
|
+
const path14 = join11(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
|
|
10813
10952
|
try {
|
|
10814
|
-
writeFileSync6(
|
|
10815
|
-
return
|
|
10953
|
+
writeFileSync6(path14, content, { encoding: "utf8", mode: 384 });
|
|
10954
|
+
return path14;
|
|
10816
10955
|
} catch (err) {
|
|
10817
10956
|
log.warn("failed to write system prompt file", { error: String(err) });
|
|
10818
10957
|
return void 0;
|
|
@@ -11143,6 +11282,8 @@ _Reply with your answer to continue._
|
|
|
11143
11282
|
}
|
|
11144
11283
|
|
|
11145
11284
|
// src/control-request.ts
|
|
11285
|
+
var ELICITATION_DECLINE = { action: "decline" };
|
|
11286
|
+
var warnedElicitationServers = /* @__PURE__ */ new Set();
|
|
11146
11287
|
function controlRequestBehaviorForTool(config, toolName) {
|
|
11147
11288
|
const configured = config.controlRequestToolBehaviors;
|
|
11148
11289
|
if (configured && toolName) {
|
|
@@ -11211,6 +11352,18 @@ function handleControlRequest(config, msg, proc) {
|
|
|
11211
11352
|
}
|
|
11212
11353
|
return true;
|
|
11213
11354
|
}
|
|
11355
|
+
if (request.subtype === "elicitation") {
|
|
11356
|
+
const server2 = request.mcp_server_name || "unknown";
|
|
11357
|
+
writeControlResponse(proc, requestId, { ...ELICITATION_DECLINE });
|
|
11358
|
+
if (!warnedElicitationServers.has(server2)) {
|
|
11359
|
+
warnedElicitationServers.add(server2);
|
|
11360
|
+
log.warn(
|
|
11361
|
+
"MCP server asked for operator input and was declined: a headless Claude Code session cannot prompt, so its elicitation can only be refused. Run that server's flow in Claude Code directly, or configure it not to elicit.",
|
|
11362
|
+
{ server: server2, mode: request.mode ?? "form" }
|
|
11363
|
+
);
|
|
11364
|
+
}
|
|
11365
|
+
return true;
|
|
11366
|
+
}
|
|
11214
11367
|
writeControlResponse(proc, requestId, {});
|
|
11215
11368
|
log.debug("control request acknowledged", {
|
|
11216
11369
|
requestId,
|
|
@@ -11929,14 +12082,18 @@ var FAST_MODE_REASONS = {
|
|
|
11929
12082
|
pending: "the CLI is still checking availability"
|
|
11930
12083
|
};
|
|
11931
12084
|
var warnedFastModeReasons = /* @__PURE__ */ new Set();
|
|
11932
|
-
function reportFastModeState(msg, requested) {
|
|
12085
|
+
function reportFastModeState(msg, requested, phase = "result") {
|
|
11933
12086
|
const state = msg.fast_mode_state;
|
|
11934
12087
|
if (!state) return;
|
|
11935
12088
|
if (!requested) {
|
|
11936
|
-
log.debug("fast mode state", { state });
|
|
12089
|
+
log.debug("fast mode state", { state, phase });
|
|
11937
12090
|
return;
|
|
11938
12091
|
}
|
|
11939
12092
|
if (state === "on") {
|
|
12093
|
+
if (phase === "init") {
|
|
12094
|
+
log.debug("fast mode reported on at session start", { state, phase });
|
|
12095
|
+
return;
|
|
12096
|
+
}
|
|
11940
12097
|
log.info("fast mode active", { state });
|
|
11941
12098
|
return;
|
|
11942
12099
|
}
|
|
@@ -12431,7 +12588,7 @@ function createLineHandler(state, ctx) {
|
|
|
12431
12588
|
claudeSessionId: msg.session_id
|
|
12432
12589
|
});
|
|
12433
12590
|
}
|
|
12434
|
-
reportFastModeState(msg, ctx.fastMode);
|
|
12591
|
+
reportFastModeState(msg, ctx.fastMode, "init");
|
|
12435
12592
|
reportSystemInit(msg, {
|
|
12436
12593
|
ignoreAnthropicApiKey: ctx.config.ignoreAnthropicApiKey
|
|
12437
12594
|
});
|
|
@@ -12997,6 +13154,7 @@ ${plan}
|
|
|
12997
13154
|
}
|
|
12998
13155
|
if (msg.type === "result") {
|
|
12999
13156
|
clearFallbackTimer(state);
|
|
13157
|
+
reportFastModeState(msg, ctx.fastMode, "result");
|
|
13000
13158
|
if (msg.session_id) {
|
|
13001
13159
|
setClaudeSessionId(state.sessionKey, msg.session_id);
|
|
13002
13160
|
}
|
|
@@ -13619,6 +13777,11 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13619
13777
|
interactive: !!useInteractive,
|
|
13620
13778
|
turnStats: this.config.turnStats === true,
|
|
13621
13779
|
ignoreAnthropicApiKey: this.config.ignoreAnthropicApiKey === true,
|
|
13780
|
+
// Where the plugin applies the account itself rather than through a
|
|
13781
|
+
// wrapper (`accountInProcess`, Windows), the `/cost` spawn needs the
|
|
13782
|
+
// same config dir a turn gets, or it reports the default account's
|
|
13783
|
+
// plan usage instead of this provider's.
|
|
13784
|
+
configDir: this.config.accountInProcess ? this.config.configDir : void 0,
|
|
13622
13785
|
// `/claude-code-doctor usage` opts into the CLI's own plan-usage
|
|
13623
13786
|
// report; anything else here is ignored, as it always has been.
|
|
13624
13787
|
argument: doctor.rest
|
|
@@ -13819,21 +13982,61 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13819
13982
|
resetsAt: failoverAnswer.resetsAt
|
|
13820
13983
|
});
|
|
13821
13984
|
}
|
|
13985
|
+
const spawnConfigDir = this.config.accountInProcess ? this.skillBridgeSpawn(failover).configDir : void 0;
|
|
13822
13986
|
const processForAccount = getActiveProcess(sk);
|
|
13823
|
-
if (!compactionMode && processForAccount
|
|
13987
|
+
if (!compactionMode && processBelongsToAnotherAccount(processForAccount, cliPath, spawnConfigDir)) {
|
|
13824
13988
|
log.notice("claude process belongs to another account; starting fresh", {
|
|
13825
13989
|
sessionKey: sk,
|
|
13826
|
-
was: processForAccount
|
|
13990
|
+
was: processForAccount?.cliPath,
|
|
13827
13991
|
now: cliPath,
|
|
13828
13992
|
failedOver: failover.failedOver
|
|
13829
13993
|
});
|
|
13830
13994
|
deleteActiveProcess(sk);
|
|
13995
|
+
}
|
|
13996
|
+
let conversationOnTarget = false;
|
|
13997
|
+
const multiAccount = (this.config.failoverAccounts?.length ?? 0) > 1;
|
|
13998
|
+
const sessionBeforeAccountCheck = getClaudeSessionId(sk);
|
|
13999
|
+
if (!compactionMode && multiAccount && this.config.crossAccountResume !== false && sessionBeforeAccountCheck) {
|
|
14000
|
+
const carry = await carryTranscriptToAccount({
|
|
14001
|
+
sessionId: sessionBeforeAccountCheck,
|
|
14002
|
+
cwd,
|
|
14003
|
+
targetConfigDir: resolveConfigDir(this.skillBridgeSpawn(failover).configDir),
|
|
14004
|
+
// Every account's own directory, plus this model's configured one:
|
|
14005
|
+
// `configDir` is an option, so an account's directory is not always
|
|
14006
|
+
// the `~/.claude-<name>` the name alone would build.
|
|
14007
|
+
accountConfigDirs: [
|
|
14008
|
+
...(this.config.failoverAccounts ?? []).map(configDirForAccount),
|
|
14009
|
+
resolveConfigDir(this.config.configDir)
|
|
14010
|
+
]
|
|
14011
|
+
});
|
|
14012
|
+
if (carry.kind === "carried" || carry.kind === "already-there") {
|
|
14013
|
+
if (carry.sessionId !== sessionBeforeAccountCheck) {
|
|
14014
|
+
setClaudeSessionId(sk, carry.sessionId);
|
|
14015
|
+
}
|
|
14016
|
+
conversationOnTarget = true;
|
|
14017
|
+
if (carry.kind === "carried") {
|
|
14018
|
+
log.notice("carried this conversation's claude transcript to the other account", {
|
|
14019
|
+
sessionKey: sk,
|
|
14020
|
+
renamed: carry.renamed,
|
|
14021
|
+
sessionId: carry.sessionId,
|
|
14022
|
+
failedOver: failover.failedOver
|
|
14023
|
+
});
|
|
14024
|
+
}
|
|
14025
|
+
} else if (!getActiveProcess(sk)) {
|
|
14026
|
+
log.notice("replaying this conversation as text on the other account", {
|
|
14027
|
+
sessionKey: sk,
|
|
14028
|
+
reason: carry.reason,
|
|
14029
|
+
failedOver: failover.failedOver
|
|
14030
|
+
});
|
|
14031
|
+
deleteClaudeSessionId(sk);
|
|
14032
|
+
}
|
|
14033
|
+
} else if (!compactionMode && sessionBeforeAccountCheck && processBelongsToAnotherAccount(processForAccount, cliPath, spawnConfigDir)) {
|
|
13831
14034
|
deleteClaudeSessionId(sk);
|
|
13832
14035
|
}
|
|
13833
14036
|
const hasExistingSession = !!getClaudeSessionId(sk);
|
|
13834
14037
|
const hasActiveProcess = !!getActiveProcess(sk);
|
|
13835
14038
|
let includeHistoryContext = !hasExistingSession && !hasActiveProcess && hasPriorConversation;
|
|
13836
|
-
if (failoverAnswer?.kind === "switch" && hasPriorConversation) {
|
|
14039
|
+
if (failoverAnswer?.kind === "switch" && hasPriorConversation && !conversationOnTarget) {
|
|
13837
14040
|
includeHistoryContext = true;
|
|
13838
14041
|
}
|
|
13839
14042
|
let forkFromClaudeSessionId;
|
|
@@ -13879,12 +14082,12 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13879
14082
|
let replayReason = "no-session-for-key";
|
|
13880
14083
|
if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
|
|
13881
14084
|
const configDir = resolveConfigDir(this.config.configDir);
|
|
13882
|
-
const
|
|
14085
|
+
const transcriptPath2 = (id) => join12(configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
|
|
13883
14086
|
const resumePoint = findResumePoint({
|
|
13884
14087
|
sessionKey: sk,
|
|
13885
14088
|
prompt: options.prompt,
|
|
13886
14089
|
cliPath,
|
|
13887
|
-
transcriptPath,
|
|
14090
|
+
transcriptPath: transcriptPath2,
|
|
13888
14091
|
onRefused: (reason) => {
|
|
13889
14092
|
replayReason = reason;
|
|
13890
14093
|
}
|
|
@@ -13901,7 +14104,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13901
14104
|
sessionKey: sk,
|
|
13902
14105
|
prompt: options.prompt,
|
|
13903
14106
|
cliPath,
|
|
13904
|
-
transcriptPath,
|
|
14107
|
+
transcriptPath: transcriptPath2,
|
|
13905
14108
|
// `claudeSessionIsWriting`, not `getActiveProcess`: this walks every
|
|
13906
14109
|
// sibling key, and that one refreshes LRU order and cancels idle
|
|
13907
14110
|
// timers on everything it touches (the same reason the fork lookup
|
|
@@ -13940,13 +14143,19 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13940
14143
|
if (this.config.forkSessions === true && !compactionMode) {
|
|
13941
14144
|
recordForkFingerprint(sk, options.prompt, cliPath);
|
|
13942
14145
|
}
|
|
13943
|
-
const { model: spawnModelId, fast: fastMode } = parseModelId(
|
|
14146
|
+
const { model: spawnModelId, fast: fastMode } = parseModelId(
|
|
14147
|
+
this.config.accountInProcess ? stripAccountSuffix(failover.modelId) : failover.modelId
|
|
14148
|
+
);
|
|
13944
14149
|
const exitPlanModeQuestionResult = compactionMode ? null : consumeExitPlanModeQuestionResult(sk, options.prompt);
|
|
13945
14150
|
if (exitPlanModeQuestionResult) {
|
|
13946
14151
|
log.info("sending plan approval decision to claude", { sk });
|
|
13947
14152
|
}
|
|
13948
14153
|
const previousPendingProxyCalls = compactionMode ? [] : getPendingProxyCalls(sk);
|
|
13949
|
-
const effectivePrompt = failoverAnswer?.kind === "switch" ? buildFailoverContinuationPrompt(
|
|
14154
|
+
const effectivePrompt = failoverAnswer?.kind === "switch" ? buildFailoverContinuationPrompt(
|
|
14155
|
+
options.prompt,
|
|
14156
|
+
failoverAnswer.target,
|
|
14157
|
+
conversationOnTarget
|
|
14158
|
+
) : options.prompt;
|
|
13950
14159
|
const userMsg = exitPlanModeQuestionResult ?? getClaudeUserMessage(effectivePrompt, includeHistoryContext, {
|
|
13951
14160
|
compactionMode,
|
|
13952
14161
|
compactionInstructions: useInteractive && compactionMode ? extractSystemMessages(effectivePrompt).join("\n\n") : void 0,
|
|
@@ -14566,7 +14775,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
14566
14775
|
spawnSystemPromptFile,
|
|
14567
14776
|
self.config.ignoreAnthropicApiKey,
|
|
14568
14777
|
reasoningEffort,
|
|
14569
|
-
promptCacheTtl
|
|
14778
|
+
promptCacheTtl,
|
|
14779
|
+
spawnConfigDir
|
|
14570
14780
|
);
|
|
14571
14781
|
ap.mcpServers = spawnMcpServers;
|
|
14572
14782
|
state.proc = ap.proc;
|
|
@@ -14628,7 +14838,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
14628
14838
|
candidates: failoverAccounts,
|
|
14629
14839
|
resetsAt: state.accountLimitHit?.resetsAt,
|
|
14630
14840
|
window: state.accountLimitHit?.window,
|
|
14631
|
-
reason: state.accountLimitHit || !state.accountBlock ? void 0 : describeAccountBlock(state.accountBlock)
|
|
14841
|
+
reason: state.accountLimitHit || !state.accountBlock ? void 0 : describeAccountBlock(state.accountBlock),
|
|
14842
|
+
carryTranscript: self.config.crossAccountResume !== false
|
|
14632
14843
|
});
|
|
14633
14844
|
log.warn(
|
|
14634
14845
|
`Claude account "${sourceAccount}" ${state.accountLimitHit || !state.accountBlock ? "is out of usage" : `cannot serve (${state.accountBlock})`}; asking which account to continue on.`,
|
|
@@ -15126,11 +15337,80 @@ var ClaudeCodeLanguageModel = class {
|
|
|
15126
15337
|
}
|
|
15127
15338
|
};
|
|
15128
15339
|
|
|
15340
|
+
// src/account-tier.ts
|
|
15341
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
15342
|
+
import path12 from "path";
|
|
15343
|
+
var ORGANIZATION_TYPE_LABELS = {
|
|
15344
|
+
claude_max: "Max",
|
|
15345
|
+
claude_pro: "Pro",
|
|
15346
|
+
claude_team: "Team",
|
|
15347
|
+
claude_enterprise: "Enterprise",
|
|
15348
|
+
claude_free: "Free"
|
|
15349
|
+
};
|
|
15350
|
+
var RATE_LIMIT_TIER_LABELS = {
|
|
15351
|
+
default_claude_max_5x: "5x",
|
|
15352
|
+
default_claude_max_20x: "20x"
|
|
15353
|
+
};
|
|
15354
|
+
var tierCache = /* @__PURE__ */ new Map();
|
|
15355
|
+
function accountTierConfigDir(account) {
|
|
15356
|
+
const configured = accountConfigDirPath(account);
|
|
15357
|
+
if (configured) return configured;
|
|
15358
|
+
const fromEnv = process.env.CLAUDE_CONFIG_DIR;
|
|
15359
|
+
return fromEnv && fromEnv.trim() ? expandHome(fromEnv.trim()) : expandHome("~/.claude");
|
|
15360
|
+
}
|
|
15361
|
+
function readAccountTier(account) {
|
|
15362
|
+
const normalized = normalizeAccountName(account) || DEFAULT_ACCOUNT;
|
|
15363
|
+
const configDir = accountTierConfigDir(normalized);
|
|
15364
|
+
const cached = tierCache.get(configDir);
|
|
15365
|
+
if (cached !== void 0) return cached ?? void 0;
|
|
15366
|
+
const label = readTierFromConfigDir(configDir, normalized === DEFAULT_ACCOUNT);
|
|
15367
|
+
tierCache.set(configDir, label ?? null);
|
|
15368
|
+
return label;
|
|
15369
|
+
}
|
|
15370
|
+
function readTierFromConfigDir(configDir, allowLegacyFallback) {
|
|
15371
|
+
const candidates = [path12.join(configDir, ".claude.json")];
|
|
15372
|
+
if (allowLegacyFallback) candidates.push(expandHome("~/.claude.json"));
|
|
15373
|
+
for (const candidate of candidates) {
|
|
15374
|
+
const oauth = readOauthAccount(candidate);
|
|
15375
|
+
if (!oauth) continue;
|
|
15376
|
+
const label = tierLabel(oauth);
|
|
15377
|
+
if (label) return label;
|
|
15378
|
+
}
|
|
15379
|
+
return void 0;
|
|
15380
|
+
}
|
|
15381
|
+
function readOauthAccount(file) {
|
|
15382
|
+
let raw;
|
|
15383
|
+
try {
|
|
15384
|
+
raw = readFileSync7(file, "utf8");
|
|
15385
|
+
} catch {
|
|
15386
|
+
return void 0;
|
|
15387
|
+
}
|
|
15388
|
+
try {
|
|
15389
|
+
const parsed = JSON.parse(raw);
|
|
15390
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
15391
|
+
const oauth = parsed.oauthAccount;
|
|
15392
|
+
return oauth && typeof oauth === "object" ? oauth : void 0;
|
|
15393
|
+
} catch {
|
|
15394
|
+
log.debug("could not parse a Claude config file for its plan tier", { file });
|
|
15395
|
+
return void 0;
|
|
15396
|
+
}
|
|
15397
|
+
}
|
|
15398
|
+
function tierLabel(oauthAccount) {
|
|
15399
|
+
const organizationType = oauthAccount.organizationType;
|
|
15400
|
+
if (typeof organizationType !== "string") return void 0;
|
|
15401
|
+
const base = ORGANIZATION_TYPE_LABELS[organizationType];
|
|
15402
|
+
if (!base) return void 0;
|
|
15403
|
+
if (base !== "Max") return base;
|
|
15404
|
+
const rateLimitTier = oauthAccount.organizationRateLimitTier;
|
|
15405
|
+
const multiple = typeof rateLimitTier === "string" ? RATE_LIMIT_TIER_LABELS[rateLimitTier] : void 0;
|
|
15406
|
+
return multiple ? `${base} ${multiple}` : base;
|
|
15407
|
+
}
|
|
15408
|
+
|
|
15129
15409
|
// src/cleanup-stale.ts
|
|
15130
15410
|
import {
|
|
15131
15411
|
existsSync as existsSync5,
|
|
15132
15412
|
mkdirSync as mkdirSync5,
|
|
15133
|
-
readFileSync as
|
|
15413
|
+
readFileSync as readFileSync8,
|
|
15134
15414
|
realpathSync as realpathSync4,
|
|
15135
15415
|
rmSync as rmSync3,
|
|
15136
15416
|
writeFileSync as writeFileSync7
|
|
@@ -15157,7 +15437,7 @@ function userIntendsToUseUnscoped() {
|
|
|
15157
15437
|
const cfg = userOpencodeJsonPath();
|
|
15158
15438
|
if (!existsSync5(cfg)) return false;
|
|
15159
15439
|
try {
|
|
15160
|
-
const json = JSON.parse(
|
|
15440
|
+
const json = JSON.parse(readFileSync8(cfg, "utf8"));
|
|
15161
15441
|
const plugins = json.plugin;
|
|
15162
15442
|
if (!Array.isArray(plugins)) return false;
|
|
15163
15443
|
return plugins.some(
|
|
@@ -15178,7 +15458,7 @@ function ourLoadedDir() {
|
|
|
15178
15458
|
function ourVersion(dir) {
|
|
15179
15459
|
if (!dir) return null;
|
|
15180
15460
|
try {
|
|
15181
|
-
const pkg = JSON.parse(
|
|
15461
|
+
const pkg = JSON.parse(readFileSync8(join13(dir, "package.json"), "utf8"));
|
|
15182
15462
|
return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
|
|
15183
15463
|
} catch {
|
|
15184
15464
|
return null;
|
|
@@ -15190,7 +15470,7 @@ function markerPath() {
|
|
|
15190
15470
|
}
|
|
15191
15471
|
function markerVersion() {
|
|
15192
15472
|
try {
|
|
15193
|
-
const marker = JSON.parse(
|
|
15473
|
+
const marker = JSON.parse(readFileSync8(markerPath(), "utf8"));
|
|
15194
15474
|
return typeof marker.version === "string" ? marker.version : null;
|
|
15195
15475
|
} catch {
|
|
15196
15476
|
return null;
|
|
@@ -15247,7 +15527,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
15247
15527
|
if (!existsSync5(pkgJsonPath)) return;
|
|
15248
15528
|
let pkg = {};
|
|
15249
15529
|
try {
|
|
15250
|
-
pkg = JSON.parse(
|
|
15530
|
+
pkg = JSON.parse(readFileSync8(pkgJsonPath, "utf8"));
|
|
15251
15531
|
} catch {
|
|
15252
15532
|
return;
|
|
15253
15533
|
}
|
|
@@ -15266,7 +15546,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
15266
15546
|
const cachePkgJson = join13(cacheRoot, "package.json");
|
|
15267
15547
|
if (!existsSync5(cachePkgJson)) return;
|
|
15268
15548
|
try {
|
|
15269
|
-
const cfg = JSON.parse(
|
|
15549
|
+
const cfg = JSON.parse(readFileSync8(cachePkgJson, "utf8"));
|
|
15270
15550
|
if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
|
|
15271
15551
|
delete cfg.dependencies[STALE_PACKAGE_NAME];
|
|
15272
15552
|
writeFileSync7(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
|
|
@@ -15346,7 +15626,7 @@ function registerOpencodeLogSink(client) {
|
|
|
15346
15626
|
}
|
|
15347
15627
|
|
|
15348
15628
|
// src/v2-client.ts
|
|
15349
|
-
import
|
|
15629
|
+
import path13 from "path";
|
|
15350
15630
|
function formatAgentTypeList(agents) {
|
|
15351
15631
|
const lines = agents.flatMap((agent) => {
|
|
15352
15632
|
if (agent.hidden === true || agent.mode === "primary") return [];
|
|
@@ -15375,7 +15655,7 @@ function toV1Session(info) {
|
|
|
15375
15655
|
if (!info || typeof info !== "object") return void 0;
|
|
15376
15656
|
const session = info;
|
|
15377
15657
|
const root = session.location?.directory;
|
|
15378
|
-
const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ?
|
|
15658
|
+
const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path13.resolve(root, session.subpath) : root : void 0;
|
|
15379
15659
|
return { ...info, directory, parentID: session.parentID };
|
|
15380
15660
|
}
|
|
15381
15661
|
function toV1Messages(messages) {
|
|
@@ -15567,7 +15847,7 @@ function planV2Providers(seedSettings, defaultProxyTools) {
|
|
|
15567
15847
|
return {
|
|
15568
15848
|
info: {
|
|
15569
15849
|
id: providerID,
|
|
15570
|
-
name: accountDisplayName(account),
|
|
15850
|
+
name: accountDisplayName(account, readAccountTier(account)),
|
|
15571
15851
|
activation: "enabled",
|
|
15572
15852
|
package: V2_PLUGIN_PACKAGE,
|
|
15573
15853
|
settings: {
|
|
@@ -15934,6 +16214,7 @@ function createClaudeCode(settings = {}) {
|
|
|
15934
16214
|
forkSessions: settings.forkSessions === true,
|
|
15935
16215
|
resumeAfterRestart: settings.resumeAfterRestart !== false,
|
|
15936
16216
|
resumeAcrossModelChanges: settings.resumeAcrossModelChanges !== false,
|
|
16217
|
+
crossAccountResume: settings.crossAccountResume !== false,
|
|
15937
16218
|
transport: settings.transport,
|
|
15938
16219
|
interactive: settings.interactive,
|
|
15939
16220
|
interactiveBypass: settings.interactiveBypass,
|
|
@@ -16104,7 +16385,7 @@ async function expandAccountProviders(config) {
|
|
|
16104
16385
|
// stripped by cleanProviderOptions.
|
|
16105
16386
|
failoverAccounts: accounts
|
|
16106
16387
|
},
|
|
16107
|
-
accountDisplayName(account)
|
|
16388
|
+
accountDisplayName(account, readAccountTier(account))
|
|
16108
16389
|
),
|
|
16109
16390
|
models: configModelsForProvider(
|
|
16110
16391
|
existing?.models ?? seed?.models ?? {},
|