@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
|
@@ -1,23 +1,49 @@
|
|
|
1
1
|
import { bindSettingsScope } from '@khorsheed/dsh-local-agent/src/client/settings-scope.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Local-agent-claude-code plugin, browser half: the claude-code settings card
|
|
4
|
+
* — a section of the family core's「本机 Agent 委派」family card on the
|
|
5
|
+
* Plugins page (registered through the core's
|
|
6
|
+
* `localAgentUi.registerHarnessCard` while the Host serves the
|
|
7
|
+
* `local-agent-claude-code` namespace), or this package's own `plugins.item`
|
|
8
|
+
* entry when the core predates the family card (the declare-and-degrade
|
|
9
|
+
* fallback). Every install shape serves the row's
|
|
10
|
+
* volatile Config under the row id — standalone install, family bundle, or
|
|
11
|
+
* mode pack — so the card follows the served namespace rather than any
|
|
12
|
+
* carrier bundle, and a composition without the row shows no trace of it. The
|
|
13
|
+
* card carries the family core's shared ProviderAuthBlock
|
|
14
|
+
* (login/logout/manual-handoff, backed by the core's localAgentGateway Remote
|
|
15
|
+
* and the commands channel), the default-model block (free-text write plus
|
|
16
|
+
* the gateway's model surface: the effective-model line and the suggestion
|
|
17
|
+
* vocabulary), and the resident-mode switch, whose writes ride the bound
|
|
18
|
+
* settingsScope — the host watcher hot-applies them, no reload. Every read
|
|
19
|
+
* degrades: an absent gateway renders the auth block's 'unavailable' state
|
|
20
|
+
* and drops the model surface, an unregistered namespace disables the live
|
|
21
|
+
* controls.
|
|
22
|
+
* @module @khorsheed/dsh-local-agent-claude-code/client
|
|
23
|
+
*/
|
|
24
|
+
import { createElement, useSyncExternalStore } from 'react';
|
|
2
25
|
import { en, NS, zh } from './locales.js';
|
|
3
|
-
import { ClaudeCodeBundleConfig,
|
|
4
|
-
export { ClaudeCodeBundleConfig, ClaudeCodeSettingsCard } from './SettingsCard.js';
|
|
26
|
+
import { ClaudeCodeBundleConfig, ClaudeCodeCardSection, } from './SettingsCard.js';
|
|
27
|
+
export { ClaudeCodeBundleConfig, ClaudeCodeCardSection, ClaudeCodeSettingsCard } from './SettingsCard.js';
|
|
5
28
|
export { en, NS, zh };
|
|
6
29
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
30
|
+
* Required services: slot registry, command Remote, and locale registry. The
|
|
31
|
+
* settings scope is NOT a static inject — its service name differs per host
|
|
32
|
+
* line (`settingsScope` on 0.1.5, `configForms` on rc.1), so a static entry
|
|
33
|
+
* would pend the whole client plugin on the other line; bindSettingsScope
|
|
34
|
+
* probes the serving one instead (the settings client package is a declared
|
|
35
|
+
* client inject, so one of them is always already up). The card track probes
|
|
36
|
+
* `configForms` through the same kind of deferred inject (see apply).
|
|
10
37
|
*/
|
|
11
|
-
const PACKAGE_NAME = '@khorsheed/dsh-local-agent-claude-code';
|
|
12
|
-
/** Required services: slot registry, settings scope, command Remote, and locale registry. */
|
|
13
38
|
export const inject = ['slots', 'remote', 'remote.commands', 'locale'];
|
|
14
39
|
/**
|
|
15
40
|
* Client plugin body: register the dictionaries and the claude-code settings
|
|
16
|
-
*
|
|
41
|
+
* card bound to the local-agent-claude-code namespace.
|
|
17
42
|
* @param ctx - client root context.
|
|
18
43
|
*/
|
|
19
44
|
export function apply(ctx) {
|
|
20
45
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'local-agent-claude-code: dictionaries');
|
|
46
|
+
const t = ctx.locale.bind(NS);
|
|
21
47
|
const scope = bindSettingsScope(ctx, 'local-agent-claude-code');
|
|
22
48
|
// The auth block's copy lives in the family core's dictionary; binding is
|
|
23
49
|
// stable per namespace and late dictionary registration still resolves.
|
|
@@ -27,21 +53,58 @@ export function apply(ctx) {
|
|
|
27
53
|
// never matters, and degrade to the auth block's 'unavailable' state when
|
|
28
54
|
// the core client is absent.
|
|
29
55
|
const gateway = () => ctx.get('remote.localAgentGateway');
|
|
30
|
-
//
|
|
31
|
-
|
|
56
|
+
// The card's inject face minus the hooks compartment — the family-card
|
|
57
|
+
// section consumes this verbatim; the standalone card's slot registration
|
|
58
|
+
// adds the hooks compartment back below.
|
|
59
|
+
const cardFace = () => ({
|
|
32
60
|
scope,
|
|
33
|
-
hooks: { settings: scope },
|
|
34
61
|
authT,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
62
|
+
// The curated model list's read/write faces, over the same Remotes.
|
|
63
|
+
modelList: {
|
|
64
|
+
read: () => {
|
|
65
|
+
const gw = gateway();
|
|
66
|
+
if (gw === undefined || typeof gw.modelList !== 'function')
|
|
67
|
+
return Promise.resolve(undefined);
|
|
68
|
+
return gw.modelList('claude-code').then(result => (result.ok ? result.value : undefined));
|
|
69
|
+
},
|
|
70
|
+
write: (models) => {
|
|
71
|
+
const gw = gateway();
|
|
72
|
+
if (gw === undefined || typeof gw.configureModelList !== 'function')
|
|
73
|
+
return Promise.resolve(undefined);
|
|
74
|
+
return gw.configureModelList('claude-code', models).then(result => (result.ok ? result.value : undefined));
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
// The endpoint's served catalog for the add dialog. A core predating
|
|
78
|
+
// modelDirectory leaves the dialog with its reason.
|
|
79
|
+
modelDirectory: (refresh) => {
|
|
80
|
+
const gw = gateway();
|
|
81
|
+
if (gw === undefined || typeof gw.modelDirectory !== 'function')
|
|
82
|
+
return Promise.resolve(undefined);
|
|
83
|
+
return gw.modelDirectory('claude-code', undefined, refresh ?? false).then(result => (result.ok ? result.value : undefined));
|
|
84
|
+
},
|
|
85
|
+
// The verify button's probe, over the session-less gateway Remote. A core
|
|
86
|
+
// predating `checkEndpoint` hides the button (declare-and-degrade).
|
|
87
|
+
checkEndpoint: () => {
|
|
41
88
|
const gw = gateway();
|
|
42
|
-
if (gw === undefined || typeof gw.
|
|
89
|
+
if (gw === undefined || typeof gw.checkEndpoint !== 'function')
|
|
43
90
|
return Promise.resolve(undefined);
|
|
44
|
-
return gw.
|
|
91
|
+
return gw.checkEndpoint('claude-code').then(result => (result.ok ? result.value : undefined));
|
|
92
|
+
},
|
|
93
|
+
// The credential form's read/write face, over the same session-less
|
|
94
|
+
// Remotes. Absent on a core that predates them — the card hides the form.
|
|
95
|
+
endpointAuth: {
|
|
96
|
+
read: () => {
|
|
97
|
+
const gw = gateway();
|
|
98
|
+
if (gw === undefined || typeof gw.endpointAuth !== 'function')
|
|
99
|
+
return Promise.resolve(undefined);
|
|
100
|
+
return gw.endpointAuth('claude-code').then(result => (result.ok ? result.value : undefined));
|
|
101
|
+
},
|
|
102
|
+
write: (fields) => {
|
|
103
|
+
const gw = gateway();
|
|
104
|
+
if (gw === undefined || typeof gw.configureEndpoint !== 'function')
|
|
105
|
+
return Promise.resolve(undefined);
|
|
106
|
+
return gw.configureEndpoint('claude-code', fields).then(result => (result.ok ? result.value : undefined));
|
|
107
|
+
},
|
|
45
108
|
},
|
|
46
109
|
auth: {
|
|
47
110
|
status: name =>
|
|
@@ -53,35 +116,57 @@ export function apply(ctx) {
|
|
|
53
116
|
.then(result => (result.ok ? result.value?.result.text : undefined)),
|
|
54
117
|
},
|
|
55
118
|
});
|
|
56
|
-
// The settings
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// the
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
119
|
+
// The settings card injects this face.
|
|
120
|
+
const cardInject = () => ({ ...cardFace(), hooks: { settings: scope } });
|
|
121
|
+
// The family-card section render bypasses the slot renderer's inject.hooks
|
|
122
|
+
// materialization, so bind the scope's selector hook here with the same
|
|
123
|
+
// semantics (a uSES subscription over the scope's own subscribe/getSnapshot).
|
|
124
|
+
const subscribeScope = (listener) => scope.subscribe(listener);
|
|
125
|
+
const readScope = () => scope.getSnapshot();
|
|
126
|
+
const useSettings = (select) => select(useSyncExternalStore(subscribeScope, readScope));
|
|
127
|
+
// One Plugins-page seat gated on the SERVED NAMESPACE, never on the install
|
|
128
|
+
// shape: whichever composition carries the `local-agent-claude-code` row —
|
|
129
|
+
// standalone install, family bundle, or mode pack — serves the row's
|
|
130
|
+
// volatile Config under the row id, and `whileServed` keeps the
|
|
131
|
+
// registration alive for exactly that span (the official companion
|
|
132
|
+
// convention), so a composition without the row shows no trace of the card.
|
|
133
|
+
// This replaces the retired per-shape seats: `plugins.row.config` hardcoded
|
|
134
|
+
// one carrier bundle into its key, and the host deleted
|
|
135
|
+
// `settings.plugin.item`. The probe rides a deferred inject because
|
|
136
|
+
// configForms exists only on the alpha.2+ host line; a 0.1.5 host never
|
|
137
|
+
// fires it, and a configForms predating whileServed (0.1.6-alpha.2) skips
|
|
138
|
+
// the card.
|
|
139
|
+
ctx.inject(['configForms'], (formsCtx) => {
|
|
140
|
+
const forms = formsCtx.configForms;
|
|
141
|
+
if (typeof forms.whileServed !== 'function')
|
|
142
|
+
return;
|
|
143
|
+
return forms.whileServed(['local-agent-claude-code'], () => {
|
|
144
|
+
// The family card owns the Plugins-page seat when the core's client
|
|
145
|
+
// provides it: a core new enough answers registerHarnessCard, and this
|
|
146
|
+
// package renders as a section of the「本机 Agent 委派」card instead of
|
|
147
|
+
// its own entry. The probe runs at serve time — a host round-trip past
|
|
148
|
+
// client plugin boot — so a composed core has already provided the
|
|
149
|
+
// service; an old or absent core keeps the standalone card below.
|
|
150
|
+
const ui = ctx.get('localAgentUi');
|
|
151
|
+
if (typeof ui?.registerHarnessCard === 'function') {
|
|
152
|
+
return ui.registerHarnessCard({
|
|
153
|
+
name: 'claude-code',
|
|
154
|
+
// The retired standalone card's order, kept as the section order.
|
|
155
|
+
order: 130,
|
|
156
|
+
locale: NS,
|
|
157
|
+
label: () => t('card.title'),
|
|
158
|
+
render: ({ active, useSessions }) => createElement(ClaudeCodeCardSection, { active, useSessions, ...cardFace(), useSettings, t }),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return ctx.slots.inject('plugins.item', () => ctx.slots.register({
|
|
162
|
+
name: 'plugins.item',
|
|
163
|
+
id: 'local-agent-claude-code',
|
|
164
|
+
// Community cards sort after the official settings pages (orders 10-40).
|
|
165
|
+
order: 130,
|
|
166
|
+
label: () => t('card.title'),
|
|
167
|
+
locale: NS,
|
|
168
|
+
inject: cardInject,
|
|
169
|
+
}, ClaudeCodeBundleConfig));
|
|
170
|
+
});
|
|
171
|
+
});
|
|
87
172
|
}
|
|
@@ -8,18 +8,24 @@ export declare const zh: {
|
|
|
8
8
|
'card.expand': string;
|
|
9
9
|
'card.collapse': string;
|
|
10
10
|
'auth.title': string;
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
11
|
+
'endpoint.info.aria': string;
|
|
12
|
+
'endpoint.info': string;
|
|
13
|
+
'endpoint.baseUrl': string;
|
|
14
|
+
'endpoint.baseUrl.placeholder': string;
|
|
15
|
+
'endpoint.apiKey': string;
|
|
16
|
+
'endpoint.authToken': string;
|
|
17
|
+
'endpoint.secret.configured': string;
|
|
18
|
+
'endpoint.secret.placeholder': string;
|
|
19
|
+
'endpoint.configured': string;
|
|
20
|
+
'endpoint.save': string;
|
|
21
|
+
'endpoint.applied': string;
|
|
22
|
+
'endpoint.error': string;
|
|
23
|
+
'endpoint.check': string;
|
|
24
|
+
'endpoint.checking': string;
|
|
25
|
+
'endpoint.checkDirtyHint': string;
|
|
26
|
+
'endpoint.checkUnavailable': string;
|
|
27
|
+
'endpoint.unavailable': string;
|
|
28
|
+
'endpoint.checkFailed': string;
|
|
23
29
|
'live.title': string;
|
|
24
30
|
'live.info.aria': string;
|
|
25
31
|
'live.info': string;
|
|
@@ -42,18 +48,24 @@ export declare const en: {
|
|
|
42
48
|
'card.expand': string;
|
|
43
49
|
'card.collapse': string;
|
|
44
50
|
'auth.title': string;
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
51
|
+
'endpoint.info.aria': string;
|
|
52
|
+
'endpoint.info': string;
|
|
53
|
+
'endpoint.baseUrl': string;
|
|
54
|
+
'endpoint.baseUrl.placeholder': string;
|
|
55
|
+
'endpoint.apiKey': string;
|
|
56
|
+
'endpoint.authToken': string;
|
|
57
|
+
'endpoint.secret.configured': string;
|
|
58
|
+
'endpoint.secret.placeholder': string;
|
|
59
|
+
'endpoint.configured': string;
|
|
60
|
+
'endpoint.save': string;
|
|
61
|
+
'endpoint.applied': string;
|
|
62
|
+
'endpoint.error': string;
|
|
63
|
+
'endpoint.check': string;
|
|
64
|
+
'endpoint.checking': string;
|
|
65
|
+
'endpoint.checkDirtyHint': string;
|
|
66
|
+
'endpoint.checkUnavailable': string;
|
|
67
|
+
'endpoint.unavailable': string;
|
|
68
|
+
'endpoint.checkFailed': string;
|
|
57
69
|
'live.title': string;
|
|
58
70
|
'live.info.aria': string;
|
|
59
71
|
'live.info': string;
|
|
@@ -3,23 +3,29 @@
|
|
|
3
3
|
export const NS = 'local-agent-claude-code';
|
|
4
4
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
5
5
|
export const zh = {
|
|
6
|
-
'card.title': '
|
|
7
|
-
'card.description': 'Claude Code
|
|
6
|
+
'card.title': '本机 Claude Code 委派',
|
|
7
|
+
'card.description': '使用本机安装的 Claude Code CLI 委派:端点凭据与常驻模式(live)',
|
|
8
8
|
'card.expand': '展开',
|
|
9
9
|
'card.collapse': '收起',
|
|
10
10
|
'auth.title': '认证',
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
11
|
+
'endpoint.info.aria': '端点凭据说明',
|
|
12
|
+
'endpoint.info': '这里保存的就是默认 scope 的 endpoint-auth.json(0600,只存本机),它优先于 profile 的 YAML 配置行;宿主 shell 的环境变量从不被读取。端点留空 = Anthropic 官方端点;API Key 与 Auth Token 至少填一个(分别对应 x-api-key 与 Bearer 头,看你的中转认哪种)。密钥绝不写进 profile 文件,也不进入评测条件指纹。保存对下一轮委派生效,进行中的轮次不受影响。',
|
|
13
|
+
'endpoint.baseUrl': '端点地址(baseUrl)',
|
|
14
|
+
'endpoint.baseUrl.placeholder': '留空 = Anthropic 官方端点',
|
|
15
|
+
'endpoint.apiKey': 'API Key',
|
|
16
|
+
'endpoint.authToken': 'Auth Token',
|
|
17
|
+
'endpoint.secret.configured': '已配置 · 输入新值更换,留空保存 = 清除',
|
|
18
|
+
'endpoint.secret.placeholder': '未配置',
|
|
19
|
+
'endpoint.configured': '已配置',
|
|
20
|
+
'endpoint.save': '保存凭据',
|
|
21
|
+
'endpoint.applied': '已保存,下一轮委派生效;进行中的轮次不受影响',
|
|
22
|
+
'endpoint.error': '保存失败,请重试',
|
|
23
|
+
'endpoint.check': '验证端点并获取模型',
|
|
24
|
+
'endpoint.checking': '验证中…',
|
|
25
|
+
'endpoint.checkDirtyHint': '有未保存的修改,先保存凭据再验证',
|
|
26
|
+
'endpoint.checkUnavailable': '当前核心不支持端点验证(请升级 local-agent 核心包)',
|
|
27
|
+
'endpoint.unavailable': '当前核心不支持凭据配置(请升级 local-agent 核心包)',
|
|
28
|
+
'endpoint.checkFailed': '验证失败,请重试',
|
|
23
29
|
'live.title': '常驻模式(live)',
|
|
24
30
|
'live.info.aria': '常驻模式说明',
|
|
25
31
|
'live.info': '常驻模式 = 成员进程常驻:输出实时流入成员会话、取消不杀进程、崩溃后自动续上原会话;关闭则每轮独立进程,跑完一次性出结果。设置为全局偏好,对所有后续委派生效。',
|
|
@@ -29,23 +35,29 @@ export const zh = {
|
|
|
29
35
|
};
|
|
30
36
|
/** English dictionary, checked complete against the zh key set. */
|
|
31
37
|
export const en = {
|
|
32
|
-
'card.title': 'Local
|
|
33
|
-
'card.description': 'Claude Code
|
|
38
|
+
'card.title': 'Local Claude Code Delegation',
|
|
39
|
+
'card.description': "Delegate through this machine's Claude Code CLI: endpoint credential and resident (live) mode",
|
|
34
40
|
'card.expand': 'Expand',
|
|
35
41
|
'card.collapse': 'Collapse',
|
|
36
42
|
'auth.title': 'Authentication',
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
43
|
+
'endpoint.info.aria': 'About the endpoint credential',
|
|
44
|
+
'endpoint.info': "What you save here is the default scope's endpoint-auth.json (mode 0600, local only), which outranks the profile's YAML row; the host shell's environment is never read. A blank endpoint means Anthropic's own; fill at least one of API Key and Auth Token (x-api-key and Bearer respectively — whichever your relay accepts). Keys never enter the profile document or an evaluation's condition fingerprint. Saving applies to the next delegation round; rounds in flight are unaffected.",
|
|
45
|
+
'endpoint.baseUrl': 'Endpoint (baseUrl)',
|
|
46
|
+
'endpoint.baseUrl.placeholder': "Blank = Anthropic's own endpoint",
|
|
47
|
+
'endpoint.apiKey': 'API Key',
|
|
48
|
+
'endpoint.authToken': 'Auth Token',
|
|
49
|
+
'endpoint.secret.configured': 'Configured · type to replace, save blank to clear',
|
|
50
|
+
'endpoint.secret.placeholder': 'Not configured',
|
|
51
|
+
'endpoint.configured': 'Configured',
|
|
52
|
+
'endpoint.save': 'Save credential',
|
|
53
|
+
'endpoint.applied': 'Saved — takes effect on the next delegation round; rounds in flight are unaffected',
|
|
54
|
+
'endpoint.error': 'Save failed, please retry',
|
|
55
|
+
'endpoint.check': 'Verify endpoint & fetch models',
|
|
56
|
+
'endpoint.checking': 'Checking…',
|
|
57
|
+
'endpoint.checkDirtyHint': 'Unsaved changes — save the credential first',
|
|
58
|
+
'endpoint.checkUnavailable': 'Endpoint verification is not supported by this core (upgrade the local-agent core)',
|
|
59
|
+
'endpoint.unavailable': 'Credential configuration is not supported by this core (upgrade the local-agent core)',
|
|
60
|
+
'endpoint.checkFailed': 'Check failed, please retry',
|
|
49
61
|
'live.title': 'Resident mode (live)',
|
|
50
62
|
'live.info.aria': 'About resident mode',
|
|
51
63
|
'live.info': 'Resident mode keeps the member process alive: output streams into the member session in real time, cancelling does not kill the process, and a crash resumes the same session. Off means every round runs as an independent one-shot process. The setting is global and applies to every later delegation.',
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Endpoint-key authentication — the claude-code harness's ONLY auth mode.
|
|
3
|
+
* There is no OAuth anywhere in this package: a delegation round reaches its
|
|
4
|
+
* endpoint with `ANTHROPIC_BASE_URL` plus `ANTHROPIC_API_KEY` (x-api-key) or
|
|
5
|
+
* `ANTHROPIC_AUTH_TOKEN` (Authorization: Bearer), exactly the environment a
|
|
6
|
+
* shell would export for a proxy relay, and `claude -p` needs nothing else
|
|
7
|
+
* (verified against claude 2.1.296: a fresh scoped home with only these
|
|
8
|
+
* variables runs a round and scaffolds `.claude.json` itself — no onboarding
|
|
9
|
+
* or key-approval gate in headless mode).
|
|
10
|
+
*
|
|
11
|
+
* Two layers, merged per field, file over config:
|
|
12
|
+
*
|
|
13
|
+
* - `<scopedHome>/endpoint-auth.json` (mode 0600) — the per-scope credential.
|
|
14
|
+
* Its name deliberately matches the eval contract hash's credential denylist
|
|
15
|
+
* (`auth`), so the key material never enters an evaluation's condition
|
|
16
|
+
* fingerprint; the file's mtime serves as the credential stamp, which makes
|
|
17
|
+
* "edit the file" the recovery gesture for a `rejected` grade.
|
|
18
|
+
* - The plugin config's `baseUrl` / `apiKey` / `authToken` — the deployment
|
|
19
|
+
* default every scope without a file shares. Read PER ROUND from the
|
|
20
|
+
* apply-time snapshot, so a profile edit applies to the next round (and a
|
|
21
|
+
* host restart clears the in-memory `rejected` mark).
|
|
22
|
+
*
|
|
23
|
+
* The env layer this module emits rides the family's existing plumbing:
|
|
24
|
+
* `delegationEnv` on the host, and `containerExecSpawn`'s name-only `-e`
|
|
25
|
+
* forwarding into a container (the value travels in the docker client's
|
|
26
|
+
* environment, never on an argv). A containerized round therefore
|
|
27
|
+
* authenticates with the same credential as the host — nothing is authorized
|
|
28
|
+
* inside the unit, which is the point.
|
|
29
|
+
* @module @khorsheed/dsh-local-agent-claude-code/endpoint-auth
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* The scoped-home credential file's name. Chosen to match the eval contract
|
|
33
|
+
* hash's credential-shaped denylist (`/token|key|credential|secret|password|auth|\.env/i`),
|
|
34
|
+
* so the file is excluded from condition fingerprints by name.
|
|
35
|
+
*/
|
|
36
|
+
export declare const ENDPOINT_AUTH_FILE = "endpoint-auth.json";
|
|
37
|
+
/**
|
|
38
|
+
* One scope's endpoint credential. `baseUrl` is optional (absent means the
|
|
39
|
+
* CLI's own default endpoint — an official console key needs no relay); at
|
|
40
|
+
* least one of `apiKey` / `authToken` must be non-empty for the scope to
|
|
41
|
+
* count as authenticated. When both are set the CLI's own precedence rules
|
|
42
|
+
* apply — the package passes both through untouched.
|
|
43
|
+
*/
|
|
44
|
+
export interface ClaudeEndpointAuth {
|
|
45
|
+
readonly baseUrl?: string;
|
|
46
|
+
/** Sent as `ANTHROPIC_API_KEY` (the `x-api-key` header). */
|
|
47
|
+
readonly apiKey?: string;
|
|
48
|
+
/** Sent as `ANTHROPIC_AUTH_TOKEN` (the `Authorization: Bearer` header). */
|
|
49
|
+
readonly authToken?: string;
|
|
50
|
+
}
|
|
51
|
+
/** Whether a resolved endpoint carries a usable credential. */
|
|
52
|
+
export declare function hasEndpointCredential(auth: ClaudeEndpointAuth | undefined): auth is (ClaudeEndpointAuth & {
|
|
53
|
+
apiKey: string;
|
|
54
|
+
}) | (ClaudeEndpointAuth & {
|
|
55
|
+
authToken: string;
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Read the scope's endpoint-auth.json. Absent or unreadable yields undefined
|
|
59
|
+
* (the caller falls back to the plugin config); malformed JSON or a non-object
|
|
60
|
+
* payload also yields undefined — a torn file must not fail a status read.
|
|
61
|
+
* Non-string fields are dropped, never carried through.
|
|
62
|
+
* @param homeDir - the `claude-code` harness's scoped home.
|
|
63
|
+
* @returns the file's endpoint credential, or undefined.
|
|
64
|
+
*/
|
|
65
|
+
export declare function readEndpointAuthFile(homeDir: string): Promise<ClaudeEndpointAuth | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Resolve one scope's effective endpoint credential: the scope's
|
|
68
|
+
* endpoint-auth.json merged FIELD BY FIELD over the plugin config's
|
|
69
|
+
* `baseUrl` / `apiKey` / `authToken` (a file that names only a key rotates
|
|
70
|
+
* the key while keeping the deployment's relay URL). The result may carry no
|
|
71
|
+
* credential at all — {@link hasEndpointCredential} is the judgment, kept
|
|
72
|
+
* separate so the caller can still route/log the resolved baseUrl.
|
|
73
|
+
* @param homeDir - the scope's scoped home.
|
|
74
|
+
* @param config - the plugin config's deployment-level endpoint triple.
|
|
75
|
+
* @returns the merged endpoint auth.
|
|
76
|
+
*/
|
|
77
|
+
export declare function resolveEndpointAuth(homeDir: string, config: ClaudeEndpointAuth): Promise<ClaudeEndpointAuth>;
|
|
78
|
+
/**
|
|
79
|
+
* The spawn-env layer for a resolved endpoint: one entry per defined field,
|
|
80
|
+
* nothing else. Undefined yields an empty layer — the spread then adds no
|
|
81
|
+
* keys at all, byte-identical to the pre-endpoint env.
|
|
82
|
+
* @param auth - the resolved endpoint auth, or undefined.
|
|
83
|
+
* @returns the explicit env entries for the delegation spawn.
|
|
84
|
+
*/
|
|
85
|
+
export declare function endpointAuthEnv(auth: ClaudeEndpointAuth | undefined): NodeJS.ProcessEnv;
|
|
86
|
+
/**
|
|
87
|
+
* The credential marker's modification stamp (epoch ms): the endpoint file's
|
|
88
|
+
* mtime, or undefined when the scope has no file (a config-sourced credential
|
|
89
|
+
* has no marker — rotating it pairs with the profile edit and instance
|
|
90
|
+
* restart that clear the in-memory grade marks). A fresh file write is a
|
|
91
|
+
* fresh stamp, which is how an edited-in key recovers a `rejected` grade.
|
|
92
|
+
* @param homeDir - the scope's scoped home.
|
|
93
|
+
* @returns the stamp, or undefined.
|
|
94
|
+
*/
|
|
95
|
+
export declare function endpointAuthStamp(homeDir: string): Promise<number | undefined>;
|
|
96
|
+
/** Whether the scope carries its own endpoint file at all. */
|
|
97
|
+
export declare function endpointAuthFileExists(homeDir: string): Promise<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* Write the scope's endpoint-auth.json (mode 0600, mkdir -p first), the
|
|
100
|
+
* per-scope provisioning path an evaluation's `conditions provision` (or a
|
|
101
|
+
* person) uses to give one scope its own credential. Whole-file write: the
|
|
102
|
+
* payload is exactly what {@link readEndpointAuthFile} parses back.
|
|
103
|
+
* @param homeDir - the scope's scoped home.
|
|
104
|
+
* @param auth - the endpoint credential to persist.
|
|
105
|
+
*/
|
|
106
|
+
export declare function writeEndpointAuth(homeDir: string, auth: ClaudeEndpointAuth): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Clear the scope's OWN endpoint credential (the endpoint-auth.json file).
|
|
109
|
+
* A deployment-level credential from the plugin config is untouched — the
|
|
110
|
+
* core's logout reply then names the profile row as the place to edit.
|
|
111
|
+
* @param homeDir - the scope's scoped home.
|
|
112
|
+
*/
|
|
113
|
+
export declare function clearEndpointAuth(homeDir: string): Promise<void>;
|
|
114
|
+
/** The official endpoint used when no baseUrl is configured anywhere. */
|
|
115
|
+
export declare const OFFICIAL_BASE_URL = "https://api.anthropic.com";
|
|
116
|
+
/** The subcommand's fetch shape (injected in tests). */
|
|
117
|
+
export type EndpointCheckFetch = (url: string, init: {
|
|
118
|
+
headers: Record<string, string>;
|
|
119
|
+
signal: AbortSignal;
|
|
120
|
+
}) => Promise<{
|
|
121
|
+
ok: boolean;
|
|
122
|
+
status: number;
|
|
123
|
+
text(): Promise<string>;
|
|
124
|
+
}>;
|
|
125
|
+
/** One model the endpoint serves, from `GET <baseUrl>/v1/models`. */
|
|
126
|
+
export interface EndpointServedModel {
|
|
127
|
+
readonly id: string;
|
|
128
|
+
readonly displayName?: string;
|
|
129
|
+
/** Reasoning effort levels the endpoint advertises for this model, in ascending order. */
|
|
130
|
+
readonly efforts?: readonly string[];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The outcome of one `GET <baseUrl>/v1/models`: the parsed model list, the
|
|
134
|
+
* HTTP status when the endpoint answered, or the transport error's message.
|
|
135
|
+
* Callers fold it their own way (the check's verdict text, the catalog's
|
|
136
|
+
* directory); no case throws.
|
|
137
|
+
*/
|
|
138
|
+
export type EndpointModelsResult = {
|
|
139
|
+
readonly kind: 'models';
|
|
140
|
+
readonly models: readonly EndpointServedModel[];
|
|
141
|
+
} | {
|
|
142
|
+
readonly kind: 'http';
|
|
143
|
+
readonly status: number;
|
|
144
|
+
readonly body: string;
|
|
145
|
+
} | {
|
|
146
|
+
readonly kind: 'unreachable';
|
|
147
|
+
readonly message: string;
|
|
148
|
+
} | {
|
|
149
|
+
readonly kind: 'malformed';
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* `GET <baseUrl>/v1/models` with the resolved credential's auth header(s) —
|
|
153
|
+
* the token-free endpoint probe shared by the `/claude-code check` verdict
|
|
154
|
+
* and the model catalog. Anthropic's own API serves this route with
|
|
155
|
+
* `x-api-key`; relays (openproxy verified) proxy it and answer the same
|
|
156
|
+
* shape. Never throws: every failure mode folds into the result.
|
|
157
|
+
* @param endpoint - the resolved endpoint auth (caller checks the credential).
|
|
158
|
+
* @param fetchImpl - the HTTP fetch; default the host's global fetch.
|
|
159
|
+
* @param signal - abort for the whole probe (timeout bundled by the caller).
|
|
160
|
+
* @returns the folded result.
|
|
161
|
+
*/
|
|
162
|
+
export declare function fetchEndpointModels(endpoint: ClaudeEndpointAuth & {
|
|
163
|
+
readonly baseUrl: string;
|
|
164
|
+
}, fetchImpl?: EndpointCheckFetch, signal?: AbortSignal): Promise<EndpointModelsResult>;
|
|
165
|
+
/**
|
|
166
|
+
* Probe the resolved endpoint with a cheap, token-free round-trip: `GET
|
|
167
|
+
* <baseUrl>/v1/models` with the resolved credential's auth header(s). A 200
|
|
168
|
+
* proves both reachability and credential validity — the exact evidence a
|
|
169
|
+
* "verify" button needs — and the served model list rides the reply so the
|
|
170
|
+
* user sees what the endpoint offers (any id can be typed into the model
|
|
171
|
+
* field by hand). One transport-level retry rides along: a local proxy
|
|
172
|
+
* restart (a Surge/clash reload) stalls the first attempt into the timeout
|
|
173
|
+
* and answers the retry immediately. Never throws: every failure mode folds
|
|
174
|
+
* into the reply text, since the reply IS the diagnostic.
|
|
175
|
+
* @param endpoint - the resolved endpoint auth (scope file over settings).
|
|
176
|
+
* @param fetchImpl - the HTTP fetch; default the host's global fetch.
|
|
177
|
+
* @param timeoutMs - one attempt's deadline (two attempts at most).
|
|
178
|
+
* @returns the human-readable verdict for the `/claude-code check` reply.
|
|
179
|
+
*/
|
|
180
|
+
export declare function checkClaudeEndpoint(endpoint: ClaudeEndpointAuth, fetchImpl?: EndpointCheckFetch, timeoutMs?: number): Promise<string>;
|
|
181
|
+
//# sourceMappingURL=endpoint-auth.d.ts.map
|