@khorsheed/dsh-local-agent-claude-code 0.2.0-rc.1 → 0.2.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 +27 -0
- package/README.en.md +24 -106
- package/README.i18n.yaml +2 -2
- package/README.md +24 -106
- package/lib/client.js +842 -315
- package/lib/index.js +1707 -1392
- package/lib/types/claude-cli-provider.d.ts +86 -6
- package/lib/types/claude-cli-provider.js +391 -146
- package/lib/types/client/SettingsCard.d.ts +75 -24
- package/lib/types/client/SettingsCard.js +106 -128
- package/lib/types/client/icons.d.ts +1 -1
- package/lib/types/client/index.d.ts +12 -25
- package/lib/types/client/index.js +135 -50
- package/lib/types/client/locales.d.ts +36 -24
- package/lib/types/client/locales.js +40 -28
- package/lib/types/endpoint-auth.d.ts +181 -0
- package/lib/types/endpoint-auth.js +297 -0
- package/lib/types/index.d.ts +32 -16
- package/lib/types/index.js +128 -62
- package/lib/types/live-driver.d.ts +9 -6
- package/lib/types/live-driver.js +277 -119
- package/lib/types/live-switch.d.ts +4 -1
- package/lib/types/model-broker.d.ts +4 -2
- package/lib/types/model-broker.js +63 -10
- package/lib/types/model-catalog.d.ts +35 -6
- package/lib/types/model-catalog.js +58 -68
- package/lib/types/provision.d.ts +34 -27
- package/lib/types/provision.js +49 -31
- package/lib/types/records.d.ts +4 -62
- package/lib/types/records.js +5 -382
- package/locale/en.json +2 -2
- package/locale/zh.json +2 -2
- package/package.json +20 -21
- package/lib/invariant.js +0 -31
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -33
package/lib/types/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude Code harness instantiation of the local-agent family. Registers the
|
|
3
3
|
* `claude-code` harness into the core registry: scoped home under the shared
|
|
4
|
-
* homes root (`CLAUDE_CONFIG_DIR`),
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* session records. Delegation mounts through
|
|
8
|
-
* (one-shot `claude -p
|
|
9
|
-
* bundle patch's `tool-subagent-claude-code-local` row puts the tool on
|
|
10
|
-
* profile root, so every agent preset can delegate.
|
|
4
|
+
* homes root (`CLAUDE_CONFIG_DIR`), endpoint-key authentication (the scope's
|
|
5
|
+
* `endpoint-auth.json` over the plugin config's `baseUrl`/`apiKey`/
|
|
6
|
+
* `authToken` triple — no OAuth, no browser flow, nothing to authorize inside
|
|
7
|
+
* a container), and project-file session records. Delegation mounts through
|
|
8
|
+
* the `claude-local` provider (one-shot `claude -p` under the scoped home);
|
|
9
|
+
* the bundle patch's `tool-subagent-claude-code-local` row puts the tool on
|
|
10
|
+
* the profile root, so every agent preset can delegate.
|
|
11
11
|
*
|
|
12
12
|
* @module @khorsheed/dsh-local-agent-claude-code
|
|
13
13
|
*/
|
|
@@ -15,19 +15,22 @@ import z from '@deepseek-ai/schemastery';
|
|
|
15
15
|
import { settingsFace, vol } from '@khorsheed/dsh-local-agent';
|
|
16
16
|
import { endpointHost } from '@khorsheed/dsh-local-agent/types';
|
|
17
17
|
import { ClaudeCliProvider, claudeCliVersion } from './claude-cli-provider.js';
|
|
18
|
+
import { checkClaudeEndpoint, clearEndpointAuth, endpointAuthFileExists, endpointAuthStamp, hasEndpointCredential, readEndpointAuthFile, resolveEndpointAuth, writeEndpointAuth, } from './endpoint-auth.js';
|
|
18
19
|
import { DEFAULT_LIVE_IDLE_MS } from './live-driver.js';
|
|
19
20
|
import { LiveDriverSwitch } from './live-switch.js';
|
|
20
21
|
import { ClaudeModelCatalog } from './model-catalog.js';
|
|
21
22
|
import { ClaudeModelBroker, ClaudeScopedModelMemory } from './model-broker.js';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
23
|
+
import { firstModelOf, readModelList, readModelListSync, writeModelList } from '@khorsheed/dsh-local-agent';
|
|
24
|
+
import { listClaudeSessions, readClaudeTranscriptModel } from './records.js';
|
|
25
|
+
import { provisionClaudeHome, readClaudeConfiguredModel } from './provision.js';
|
|
24
26
|
/** Stable Cordis plugin name; the bundle patch row id. */
|
|
25
27
|
export const name = 'local-agent-claude-code';
|
|
26
28
|
/** Services required before the harness can register. */
|
|
27
29
|
export const inject = ['localAgent', 'subagents', 'subprocess', 'settings'];
|
|
28
30
|
/**
|
|
29
31
|
* The settings fields (`live`, `liveMirrorGranularity`, `model`,
|
|
30
|
-
* `recentModels
|
|
32
|
+
* `recentModels`, and the endpoint credential `baseUrl`/`apiKey`/`authToken`)
|
|
33
|
+
* ride the plugin Config, marked `.volatile()` where the
|
|
31
34
|
* host's schemastery has it (3.18.4+, the rc.1 line — probed, never sniffed):
|
|
32
35
|
* there the SettingsForms writes hot-track the running fiber's Volatile
|
|
33
36
|
* references without a remount, and the official one-shot import moves an old
|
|
@@ -58,6 +61,8 @@ export const Config = z.object({
|
|
|
58
61
|
]),
|
|
59
62
|
model: volatilize(SETTINGS_FIELDS.model),
|
|
60
63
|
baseUrl: z.string(),
|
|
64
|
+
apiKey: z.string(),
|
|
65
|
+
authToken: z.string(),
|
|
61
66
|
proxyUrl: z.string(),
|
|
62
67
|
live: volatilize(SETTINGS_FIELDS.live),
|
|
63
68
|
liveIdleMs: z.number().default(DEFAULT_LIVE_IDLE_MS),
|
|
@@ -85,7 +90,6 @@ const CLAUDE_SETTINGS_SCHEMA = z.object(SETTINGS_FIELDS);
|
|
|
85
90
|
export function apply(ctx, config) {
|
|
86
91
|
ctx.effect(() => {
|
|
87
92
|
const permissionMode = config.permissionMode ?? DEFAULT_PERMISSION_MODE;
|
|
88
|
-
const baseUrl = config.baseUrl;
|
|
89
93
|
const homeDir = ctx.localAgent.homeDir('claude-code');
|
|
90
94
|
// Claude creates the scoped home lazily; create it eagerly so the
|
|
91
95
|
// harness's homeDir contract is uniform with the other harnesses.
|
|
@@ -123,15 +127,28 @@ export function apply(ctx, config) {
|
|
|
123
127
|
legacyBase,
|
|
124
128
|
read: readSettings,
|
|
125
129
|
});
|
|
126
|
-
// The
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
const
|
|
130
|
+
// The endpoint credential's deployment level: the plugin config triple,
|
|
131
|
+
// merged per field UNDER the scope's own endpoint-auth.json at resolve
|
|
132
|
+
// time. Resolved PER READ against the scope's home — a card write (which
|
|
133
|
+
// lands in the file through the endpointAuth face) takes effect on the
|
|
134
|
+
// next round with no reload.
|
|
135
|
+
const endpointConfig = {
|
|
136
|
+
...config.baseUrl === undefined ? {} : { baseUrl: config.baseUrl },
|
|
137
|
+
...config.apiKey === undefined ? {} : { apiKey: config.apiKey },
|
|
138
|
+
...config.authToken === undefined ? {} : { authToken: config.authToken },
|
|
139
|
+
};
|
|
140
|
+
const resolveEndpoint = (scopeHome) => resolveEndpointAuth(scopeHome, endpointConfig);
|
|
141
|
+
// The memberless model key: the plugin-config/settings `model`, blank = unset.
|
|
142
|
+
const settingsModelKey = () => {
|
|
132
143
|
const model = face.get().model?.trim();
|
|
133
144
|
return model === undefined || model === '' ? undefined : model;
|
|
134
145
|
};
|
|
146
|
+
// The harness's default model for a scope: the curated model list's
|
|
147
|
+
// FIRST entry (models.json, edited on the card), ahead of the settings
|
|
148
|
+
// key; an empty list leaves the pre-existing chain untouched. Read sync
|
|
149
|
+
// per call — one small file, and a card write always reaches the next
|
|
150
|
+
// round.
|
|
151
|
+
const resolveModel = (scope) => firstModelOf(readModelListSync(ctx.localAgent.homeDir('claude-code', scope))) ?? settingsModelKey();
|
|
135
152
|
// The member model layers above the settings key: the session-level
|
|
136
153
|
// override map (the composer's picker writes it through the broker; both
|
|
137
154
|
// round paths and the live spawn read it) and the scoped-settings.json
|
|
@@ -152,18 +169,24 @@ export function apply(ctx, config) {
|
|
|
152
169
|
const named = delegationModel?.trim();
|
|
153
170
|
if (named !== undefined && named !== '')
|
|
154
171
|
return named;
|
|
155
|
-
|
|
172
|
+
// The member's scope decides whose list answers: a named scope's rounds
|
|
173
|
+
// read that scope's models.json, exactly like its endpoint credential.
|
|
174
|
+
const record = ctx.localAgent.memberBinding?.(childSessionId) ?? ctx.localAgent.getDelegation(childSessionId);
|
|
175
|
+
return resolveModel(record?.scope);
|
|
156
176
|
};
|
|
157
177
|
const liveSwitch = new LiveDriverSwitch(ctx, face, {
|
|
158
178
|
...config.permissionMode === undefined ? {} : { permissionMode: config.permissionMode },
|
|
159
|
-
|
|
179
|
+
endpoint: resolveEndpoint,
|
|
160
180
|
...config.liveIdleMs === undefined ? {} : { liveIdleMs: config.liveIdleMs },
|
|
161
181
|
model: effectiveModel,
|
|
162
182
|
provisionModel: (home, model) => scopedModelMemory.provision(home, model),
|
|
163
183
|
});
|
|
164
184
|
const modelCatalog = new ClaudeModelCatalog({
|
|
165
|
-
|
|
166
|
-
|
|
185
|
+
endpoint: resolveEndpoint,
|
|
186
|
+
// Re-key on the config baseUrl; the scope file's own edits (the card's
|
|
187
|
+
// credential writes land there) are covered by its fingerprint in the
|
|
188
|
+
// key's file list.
|
|
189
|
+
endpointLabel: () => config.baseUrl,
|
|
167
190
|
warn: message => ctx.logger.warn(message),
|
|
168
191
|
});
|
|
169
192
|
const modelContext = (childSessionId) => {
|
|
@@ -177,6 +200,7 @@ export function apply(ctx, config) {
|
|
|
177
200
|
return { home, directory: refresh => refresh ? modelCatalog.refresh(home, binding.cwd) : modelCatalog.read(home, binding.cwd) };
|
|
178
201
|
},
|
|
179
202
|
settingsModel: resolveModel,
|
|
203
|
+
modelList: scope => readModelListSync(ctx.localAgent.homeDir('claude-code', scope)),
|
|
180
204
|
cliDefault: childSessionId => {
|
|
181
205
|
const context = modelContext(childSessionId);
|
|
182
206
|
return context.home === homeDir ? scopedModelMemory.cliDefault(homeDir) : readClaudeConfiguredModel(context.home);
|
|
@@ -198,7 +222,7 @@ export function apply(ctx, config) {
|
|
|
198
222
|
// live-settle report.
|
|
199
223
|
transcriptModel: (cliSessionId, childSessionId) => readClaudeTranscriptModel(modelContext(childSessionId).home, cliSessionId),
|
|
200
224
|
});
|
|
201
|
-
const disposeProvider = ctx.subagents.registerProvider(new ClaudeCliProvider(ctx, permissionMode,
|
|
225
|
+
const disposeProvider = ctx.subagents.registerProvider(new ClaudeCliProvider(ctx, permissionMode, resolveEndpoint, liveSwitch.resolve, effectiveModel));
|
|
202
226
|
const disposeHarness = ctx.localAgent.register({
|
|
203
227
|
prepareMember: async ({ binding, childSession, configuration, signal }) => {
|
|
204
228
|
const driver = liveSwitch.resolve(binding.childSessionId);
|
|
@@ -211,57 +235,90 @@ export function apply(ctx, config) {
|
|
|
211
235
|
displayName: 'Claude Code',
|
|
212
236
|
homeEnvVar: 'CLAUDE_CONFIG_DIR',
|
|
213
237
|
delegationProvider: 'claude-local',
|
|
214
|
-
login:
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
// via /claude-code code <value>. The relay env is scrubbed and the
|
|
219
|
-
// scoped home pinned so the credential lands where delegations read
|
|
220
|
-
// it (the wrapper passes the harness env through).
|
|
221
|
-
// The argv pins the config directory itself (an `env NAME=VALUE`
|
|
222
|
-
// assignment outranks the spawn env), so it is built from the
|
|
223
|
-
// directory being logged in: `/claude-code login --scope <name>`
|
|
224
|
-
// authorizes THAT scope, not the default one.
|
|
225
|
-
pty: {
|
|
226
|
-
command: 'env',
|
|
227
|
-
args: (scopedHome) => ['-u', 'ANTHROPIC_API_KEY', '-u', 'ANTHROPIC_BASE_URL', `CLAUDE_CONFIG_DIR=${scopedHome}`, 'claude', 'auth', 'login'],
|
|
228
|
-
},
|
|
229
|
-
// The watch syncs the keychain credential into the runtime-readable
|
|
230
|
-
// file first (claude 2.1.236 writes keychain but reads the file),
|
|
231
|
-
// then probes.
|
|
232
|
-
watch: async (home) => {
|
|
233
|
-
await syncClaudeCredentialFile(home, message => { ctx.logger.warn(message); });
|
|
234
|
-
return claudeAuthenticated(home, message => { ctx.logger.warn(message); });
|
|
235
|
-
},
|
|
236
|
-
},
|
|
238
|
+
// No `login` declaration: the harness authenticates by endpoint key
|
|
239
|
+
// (endpoint-auth.ts), so the core reports the harness as not loginable
|
|
240
|
+
// and `/claude-code login` answers that there is no login flow — no
|
|
241
|
+
// browser, no OAuth, nothing to authorize inside a container.
|
|
237
242
|
records: { listSessions: homeDir => listClaudeSessions(homeDir) },
|
|
238
243
|
// A NAMED scope's directory is provisioned through this hook when the
|
|
239
244
|
// registry materializes it — the same eager creation and settings the
|
|
240
245
|
// default scope gets from the apply above.
|
|
241
246
|
provision: scopedHome => provisionClaudeHome(scopedHome, config.proxyUrl).then(() => { }),
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
247
|
+
// Endpoint-key presence: the scope's endpoint-auth.json merged over the
|
|
248
|
+
// plugin config must carry a non-empty key. The verdict rides the
|
|
249
|
+
// family's credential grades untouched: presence grades
|
|
250
|
+
// `present-unverified`, a completed round grades `verified`, a round's
|
|
251
|
+
// 401 grades `rejected` until the endpoint file's mtime moves past the
|
|
252
|
+
// mark (editing the file IS the re-login gesture).
|
|
253
|
+
isAuthenticated: home => resolveEndpoint(home).then(endpoint => hasEndpointCredential(endpoint)),
|
|
254
|
+
// The settings card's verify button: a cheap token-free probe of the
|
|
255
|
+
// SAVED endpoint (the reply tells the user to save first when a
|
|
256
|
+
// credential is missing). Session-less via the gateway Remote.
|
|
257
|
+
checkEndpoint: home => resolveEndpoint(home).then(endpoint => checkClaudeEndpoint(endpoint)),
|
|
258
|
+
// The settings card's credential form: read the resolved per-field
|
|
259
|
+
// state (secrets as booleans), write edits into the scope's own
|
|
260
|
+
// endpoint-auth.json — the harness's 0600 store, never the profile
|
|
261
|
+
// patch document. A cleared field falls back to the config triple.
|
|
262
|
+
endpointAuth: {
|
|
263
|
+
read: async (home) => {
|
|
264
|
+
const endpoint = await resolveEndpoint(home);
|
|
265
|
+
return {
|
|
266
|
+
...endpoint.baseUrl === undefined ? {} : { baseUrl: endpoint.baseUrl },
|
|
267
|
+
apiKeySet: endpoint.apiKey !== undefined,
|
|
268
|
+
authTokenSet: endpoint.authToken !== undefined,
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
write: async (home, fields) => {
|
|
272
|
+
const current = await readEndpointAuthFile(home) ?? {};
|
|
273
|
+
const next = { ...current };
|
|
274
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
275
|
+
if (value === undefined)
|
|
276
|
+
continue;
|
|
277
|
+
const trimmed = typeof value === 'string' ? value.trim() : '';
|
|
278
|
+
if (value === null || trimmed === '')
|
|
279
|
+
delete next[field];
|
|
280
|
+
else
|
|
281
|
+
next[field] = trimmed;
|
|
282
|
+
}
|
|
283
|
+
if (Object.keys(next).length === 0)
|
|
284
|
+
await clearEndpointAuth(home);
|
|
285
|
+
else
|
|
286
|
+
await writeEndpointAuth(home, next);
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
credentialStamp: endpointAuthStamp,
|
|
290
|
+
// Logout clears the scope's OWN endpoint file. A config-sourced
|
|
291
|
+
// credential outlives it — said out loud, or the status would flip
|
|
292
|
+
// right back and read as a failed logout.
|
|
293
|
+
logout: async (home) => {
|
|
294
|
+
if (await endpointAuthFileExists(home)) {
|
|
295
|
+
await clearEndpointAuth(home);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
throw new Error('claude-code has no scoped endpoint-auth.json to clear — the credential comes from the settings card or the profile row (baseUrl/apiKey/authToken); clear it there instead');
|
|
299
|
+
},
|
|
248
300
|
// The model surface: the settings card reads the memberless info ("what
|
|
249
301
|
// would a round run with"), the member composer reads and switches a
|
|
250
302
|
// member's session-level override through the same broker.
|
|
251
303
|
modelBroker,
|
|
304
|
+
// The settings card's curated model list (the default scope's
|
|
305
|
+
// models.json; named scopes carry their own, read per round).
|
|
306
|
+
modelList: {
|
|
307
|
+
read: home => readModelList(home),
|
|
308
|
+
write: (home, models) => writeModelList(home, models),
|
|
309
|
+
},
|
|
252
310
|
// The eval snapshot: the permission mode is the plugin config resolved
|
|
253
311
|
// at apply (it selects the spawn flags); the endpoint mirrors the
|
|
254
|
-
// provider's own resolution
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
// settings that scope's rounds would run with.
|
|
312
|
+
// provider's own resolution — the scope's file over the config triple.
|
|
313
|
+
// The model follows the family's fixed order: the plugin config key (it
|
|
314
|
+
// rides every argv) before the scoped settings.json's own `model`. The
|
|
315
|
+
// CLI's own default model is never guessed, so neither one configured
|
|
316
|
+
// means no field. The directory is a PARAMETER, not the apply-time
|
|
317
|
+
// capture: a status read (or an evaluation snapshot) of a named scope
|
|
318
|
+
// must report the settings that scope's rounds would run with.
|
|
262
319
|
effectiveSettings: async (scopedHome) => {
|
|
263
|
-
const
|
|
264
|
-
const baseUrlHost =
|
|
320
|
+
const endpoint = await resolveEndpoint(scopedHome);
|
|
321
|
+
const baseUrlHost = endpoint.baseUrl !== undefined ? endpointHost(endpoint.baseUrl) : undefined;
|
|
265
322
|
const [scopedModel, cliVersion] = await Promise.all([
|
|
266
323
|
readClaudeConfiguredModel(scopedHome).catch(() => undefined),
|
|
267
324
|
claudeCliVersion(ctx, scopedHome).catch(() => undefined),
|
|
@@ -270,7 +327,7 @@ export function apply(ctx, config) {
|
|
|
270
327
|
return {
|
|
271
328
|
drive: face.get().live ? 'live' : 'exec',
|
|
272
329
|
permissionMode,
|
|
273
|
-
baseUrlSet:
|
|
330
|
+
baseUrlSet: endpoint.baseUrl !== undefined,
|
|
274
331
|
...baseUrlHost !== undefined ? { baseUrlHost } : {},
|
|
275
332
|
...model !== undefined ? { model } : {},
|
|
276
333
|
...cliVersion !== undefined ? { cliVersion } : {},
|
|
@@ -287,6 +344,15 @@ export function apply(ctx, config) {
|
|
|
287
344
|
: records.map(r => `${r.id} ${r.workDir}`).join('\n'),
|
|
288
345
|
}));
|
|
289
346
|
}
|
|
347
|
+
// `/claude-code check`: the settings card's verify button — a cheap
|
|
348
|
+
// token-free probe of the SAVED endpoint (the reply tells the user to
|
|
349
|
+
// save first when a credential is missing).
|
|
350
|
+
if (verb === 'check') {
|
|
351
|
+
const scopeHome = invocation.agent.ctx.localAgent.homeDir('claude-code');
|
|
352
|
+
return resolveEndpoint(scopeHome)
|
|
353
|
+
.then(endpoint => checkClaudeEndpoint(endpoint))
|
|
354
|
+
.then(text => ({ kind: 'success', text }));
|
|
355
|
+
}
|
|
290
356
|
return undefined;
|
|
291
357
|
},
|
|
292
358
|
});
|
|
@@ -3,6 +3,7 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
3
3
|
import type { Session } from '@deepseek-ai/dsh-session';
|
|
4
4
|
import { type SubagentRun, type SubagentStartRequest } from '@deepseek-ai/dsh-subagent';
|
|
5
5
|
import type { Config } from './index.ts';
|
|
6
|
+
import type { ClaudeEndpointAuth } from './endpoint-auth.ts';
|
|
6
7
|
/** Default idle lifetime of an unused resident runtime before reclaim. */
|
|
7
8
|
export declare const DEFAULT_LIVE_IDLE_MS: number;
|
|
8
9
|
/** Default timeout for the turn's system/init ack (a cold boot can be slow). */
|
|
@@ -15,11 +16,6 @@ export declare const DEFAULT_LIVE_CHANNEL_RETRY_MS: number;
|
|
|
15
16
|
export declare const DEFAULT_SNAPSHOT_MIN_INTERVAL_MS = 50;
|
|
16
17
|
/** Default minimum text growth between one streaming kind's snapshot messages. */
|
|
17
18
|
export declare const DEFAULT_SNAPSHOT_MIN_CHARS = 0;
|
|
18
|
-
/**
|
|
19
|
-
* The stream-json channel could not come up (spawn failure or the first
|
|
20
|
-
* turn's init never arrived). The provider catches exactly this and falls
|
|
21
|
-
* back to the exec one-shot; any other error is a real round failure.
|
|
22
|
-
*/
|
|
23
19
|
export declare class LiveChannelUnavailableError extends Error {
|
|
24
20
|
constructor(message: string);
|
|
25
21
|
}
|
|
@@ -85,7 +81,14 @@ export declare class ClaudeLiveDriver {
|
|
|
85
81
|
*/
|
|
86
82
|
private draining;
|
|
87
83
|
private readonly disposeController;
|
|
88
|
-
constructor(ctx: Context, config: Pick<Config, 'permissionMode'
|
|
84
|
+
constructor(ctx: Context, config: Pick<Config, 'permissionMode'> & {
|
|
85
|
+
/**
|
|
86
|
+
* Resolver for the spawn's endpoint credential (endpoint-auth.ts: the
|
|
87
|
+
* member's scoped endpoint-auth.json over the plugin config triple),
|
|
88
|
+
* read per RUNTIME SPAWN — a key rotation reaches the resident process
|
|
89
|
+
* at its next respawn. Absent, no `ANTHROPIC_*` variables are set.
|
|
90
|
+
*/
|
|
91
|
+
endpoint?: (homeDir: string) => Promise<ClaudeEndpointAuth>;
|
|
89
92
|
liveIdleMs?: number;
|
|
90
93
|
liveMirrorGranularity?: ClaudeLiveMirrorGranularity;
|
|
91
94
|
/** Maximum batching wait for incremental streaming messages. */
|