@modesto_ma/dsh-channels 0.5.0-beta.7 → 0.6.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/README.md +10 -10
- package/lib/client.js +53 -37
- package/lib/client.js.map +1 -1
- package/lib/index.js +445 -665
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/analyze/contract.ts +7 -3
- package/src/analyze/launcher.ts +28 -5
- package/src/analyze/server.ts +60 -54
- package/src/client/api.ts +28 -30
- package/src/client/catalog.ts +1 -1
- package/src/client/panel/advanced-tab.tsx +1 -1
- package/src/config.ts +4 -13
- package/src/health.ts +0 -12
- package/src/host/agent.ts +24 -1
- package/src/index.ts +6 -4
- package/src/service.ts +18 -38
- package/src/session/manager.ts +4 -4
- package/src/session/request-policy.ts +64 -5
- package/src/settings/contract.ts +49 -34
- package/src/settings/routes.ts +159 -443
- package/src/settings/surface.ts +5 -6
- package/src/streamer.ts +3 -2
package/src/service.ts
CHANGED
|
@@ -57,8 +57,7 @@ import type {
|
|
|
57
57
|
} from './domain.ts'
|
|
58
58
|
import { RouteStore } from './session/routes.ts'
|
|
59
59
|
import { AnalyzeLauncher } from './analyze/launcher.ts'
|
|
60
|
-
import {
|
|
61
|
-
import type { AnalyzeServer } from './analyze/server.ts'
|
|
60
|
+
import type { AnalyzeHandler } from './analyze/server.ts'
|
|
62
61
|
import { InflightRepair } from './session/repair.ts'
|
|
63
62
|
import { SessionManager } from './session/manager.ts'
|
|
64
63
|
import type { LiveAgent, RenewCommit } from './session/manager.ts'
|
|
@@ -313,10 +312,6 @@ export class FeishuService {
|
|
|
313
312
|
private readonly inbound: InboundRouter
|
|
314
313
|
/** Log-platform analyze API business side (requests → shared analysis topics). */
|
|
315
314
|
private readonly analyzeLauncher: AnalyzeLauncher
|
|
316
|
-
/** Bound analyze listener while connected with `analyzeListen` set. */
|
|
317
|
-
private analyzeServer: AnalyzeServer | undefined
|
|
318
|
-
/** Why the analyze listener could not bind at the last connect. */
|
|
319
|
-
private analyzeListenError: string | undefined
|
|
320
315
|
/** Agent/session lifecycle: live registry, resume, presets, renew. */
|
|
321
316
|
private readonly sessions: SessionManager
|
|
322
317
|
/**
|
|
@@ -980,6 +975,8 @@ export class FeishuService {
|
|
|
980
975
|
}
|
|
981
976
|
}
|
|
982
977
|
|
|
978
|
+
/** The analyze API's business side, for the Web route. */
|
|
979
|
+
analyzeHandler(): AnalyzeHandler { return this.analyzeLauncher }
|
|
983
980
|
isRunning(): boolean { return this.started }
|
|
984
981
|
isRestarting(): boolean { return this.restarting }
|
|
985
982
|
/** True while the panel's explicit connect action is in flight. */
|
|
@@ -1660,9 +1657,9 @@ export class FeishuService {
|
|
|
1660
1657
|
// Background: the results only feed the panel and the log.
|
|
1661
1658
|
void this.runScopeCheck(channel, appId, ownerLookup)
|
|
1662
1659
|
if (this.config.slashCommandPanel) void this.runSlashCommandSync(channel)
|
|
1663
|
-
// Before
|
|
1660
|
+
// Before admission opens, so every `running` record left is a previous instance's.
|
|
1664
1661
|
await this.analyzeLauncher.markInterrupted()
|
|
1665
|
-
|
|
1662
|
+
this.analyzeLauncher.open()
|
|
1666
1663
|
// Repair cards a previous instance left mid-flight (best-effort, background).
|
|
1667
1664
|
void this.repair.repairAll().catch((error: unknown) => {
|
|
1668
1665
|
this.ctx.logger?.warn?.('dsh-channels: inflight repair failed: %s', String(error))
|
|
@@ -1720,37 +1717,13 @@ export class FeishuService {
|
|
|
1720
1717
|
)
|
|
1721
1718
|
}
|
|
1722
1719
|
|
|
1723
|
-
/**
|
|
1724
|
-
* Bind the analyze listener when `analyzeListen` is set. A bind failure is
|
|
1725
|
-
* logged and folded into the health snapshot (panel reasons, `/health`); the Feishu connection stays up.
|
|
1726
|
-
*/
|
|
1727
|
-
private async openAnalyzeListener(): Promise<void> {
|
|
1728
|
-
this.analyzeListenError = undefined
|
|
1729
|
-
const listen = this.config.analyzeListen
|
|
1730
|
-
if (listen === undefined) return
|
|
1731
|
-
try {
|
|
1732
|
-
this.analyzeServer = await startAnalyzeServer(
|
|
1733
|
-
listen,
|
|
1734
|
-
this.analyzeLauncher,
|
|
1735
|
-
(message, ...args) => this.ctx.logger?.warn?.(message, ...args),
|
|
1736
|
-
)
|
|
1737
|
-
console.info('[dsh-channels] 日志分析接口已监听', listen)
|
|
1738
|
-
} catch (error) {
|
|
1739
|
-
this.analyzeListenError = messageOf(error)
|
|
1740
|
-
this.ctx.logger?.error?.('dsh-channels: analyze listener on %s failed: %s', listen, this.analyzeListenError)
|
|
1741
|
-
console.error('[dsh-channels] 日志分析接口监听失败:', listen, error)
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
1720
|
/** Tear down: close the connection and dispose all live agents. */
|
|
1746
1721
|
async shutdown(): Promise<void> {
|
|
1747
1722
|
// Close admission first. In-flight Host commands receive cancellation
|
|
1748
1723
|
// before their Agent/channel/storage dependencies are disposed, while
|
|
1749
1724
|
// already queued route operations observe `started === false` and retire.
|
|
1750
1725
|
this.started = false
|
|
1751
|
-
|
|
1752
|
-
this.analyzeServer = undefined
|
|
1753
|
-
await analyzeServer?.close()
|
|
1726
|
+
this.analyzeLauncher.close()
|
|
1754
1727
|
await this.analyzeLauncher.drain()
|
|
1755
1728
|
for (const controller of this.commandControllers) controller.abort()
|
|
1756
1729
|
await Promise.allSettled([...this.commandOperations])
|
|
@@ -2488,8 +2461,15 @@ export class FeishuService {
|
|
|
2488
2461
|
modelAdmission: ModelAdmission | undefined
|
|
2489
2462
|
}): Promise<void> {
|
|
2490
2463
|
const { spec, msg, current, live, result, compactBefore, renewAdmission, effortAdmission, modelAdmission } = input
|
|
2491
|
-
|
|
2492
|
-
|
|
2464
|
+
// A settled switch (not a bare view) reconciles the Agent's selection even
|
|
2465
|
+
// when the folded policy looks unchanged, e.g. a reset after restart.
|
|
2466
|
+
const reconcile = !input.bare && result.kind === 'success' ? spec.name : undefined
|
|
2467
|
+
if (spec.name === 'effort') {
|
|
2468
|
+
effortAdmission?.controller.sync(live.agent.session.snapshotEvents(), live.agent, reconcile === 'effort' ? 'effort' : undefined)
|
|
2469
|
+
}
|
|
2470
|
+
if (spec.name === 'model') {
|
|
2471
|
+
modelAdmission?.controller.sync(live.agent.session.snapshotEvents(), live.agent, reconcile === 'model' ? 'model' : undefined)
|
|
2472
|
+
}
|
|
2493
2473
|
if (input.bare && result.kind === 'success') {
|
|
2494
2474
|
const picker = spec.name === 'model' && modelAdmission !== undefined
|
|
2495
2475
|
? await this.modelPicker(current, live, modelAdmission)
|
|
@@ -2710,7 +2690,6 @@ export class FeishuService {
|
|
|
2710
2690
|
routeCount: this.routes.size(),
|
|
2711
2691
|
liveAgentCount: this.sessions.liveCount(),
|
|
2712
2692
|
inflightTurnCount,
|
|
2713
|
-
...(this.analyzeListenError === undefined ? {} : { analyzeListenError: this.analyzeListenError }),
|
|
2714
2693
|
})
|
|
2715
2694
|
}
|
|
2716
2695
|
|
|
@@ -3102,11 +3081,12 @@ function currentModelRoute(
|
|
|
3102
3081
|
controller: RequestPolicyController,
|
|
3103
3082
|
): { provider: string | undefined; model: string | undefined } {
|
|
3104
3083
|
const state = controller.state
|
|
3084
|
+
const selected = live.agent.model?.current()
|
|
3105
3085
|
const header = live.agent.session.requestHeader?.()?.config
|
|
3106
3086
|
return {
|
|
3107
|
-
provider: state.effectiveModel?.provider
|
|
3087
|
+
provider: state.effectiveModel?.provider ?? selected?.provider
|
|
3108
3088
|
?? (typeof header?.provider === 'string' && header.provider !== '' ? header.provider : undefined),
|
|
3109
|
-
model: state.effectiveModel?.model
|
|
3089
|
+
model: state.effectiveModel?.model ?? selected?.model
|
|
3110
3090
|
?? (typeof header?.model === 'string' && header.model !== '' ? header.model : undefined),
|
|
3111
3091
|
}
|
|
3112
3092
|
}
|
package/src/session/manager.ts
CHANGED
|
@@ -207,7 +207,7 @@ export class SessionManager {
|
|
|
207
207
|
|
|
208
208
|
/** Borrow one foreign live agent and mount this channel's scoped policy. */
|
|
209
209
|
private async borrow(sessionId: string, agent: Agent): Promise<LiveAgent> {
|
|
210
|
-
const borrowed = borrowLiveAgent(agent, this.mountRequestPolicy(agent.ctx, agent
|
|
210
|
+
const borrowed = borrowLiveAgent(agent, this.mountRequestPolicy(agent.ctx, agent))
|
|
211
211
|
this.live.set(sessionId, borrowed)
|
|
212
212
|
// Best-effort tool backfill (D7): the foreign owner may have composed the
|
|
213
213
|
// session without this plugin's setup (web UI resolver, previous plugin
|
|
@@ -253,13 +253,13 @@ export class SessionManager {
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
/** Mount one mutable Session-aware request policy outside every other wrapper. */
|
|
256
|
-
mountRequestPolicy(agentCtx: AgentContext,
|
|
256
|
+
mountRequestPolicy(agentCtx: AgentContext, agent?: Agent): () => void {
|
|
257
257
|
const controller = installRequestPolicy(agentCtx, {
|
|
258
258
|
effort: this.deps.config.reasoningEffort,
|
|
259
259
|
provider: this.deps.config.provider,
|
|
260
260
|
model: this.deps.config.model,
|
|
261
261
|
})
|
|
262
|
-
if (
|
|
262
|
+
if (agent !== undefined) controller.sync(agent.session.snapshotEvents(), agent)
|
|
263
263
|
this.policies.set(agentCtx, controller)
|
|
264
264
|
return () => {
|
|
265
265
|
controller.dispose()
|
|
@@ -273,7 +273,7 @@ export class SessionManager {
|
|
|
273
273
|
if (controller === undefined) {
|
|
274
274
|
throw new Error('dsh-channels: request policy is not mounted for this Agent')
|
|
275
275
|
}
|
|
276
|
-
controller.sync(agent.session.snapshotEvents())
|
|
276
|
+
controller.sync(agent.session.snapshotEvents(), agent)
|
|
277
277
|
return controller
|
|
278
278
|
}
|
|
279
279
|
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* rewrite the request after `next()` resolves. The outer channel wrapper
|
|
8
8
|
* therefore writes last.
|
|
9
9
|
*
|
|
10
|
+
* DSH 0.2.2 moved model choice onto the Agent (`agent.model`) and fails a
|
|
11
|
+
* step whose `agent/request` listener changes provider, model, or reasoning
|
|
12
|
+
* effort. On such hosts the waterfall passes the request through and `sync`
|
|
13
|
+
* records the effective policy with `agent.model.select()` instead; DSH 0.2.1
|
|
14
|
+
* hosts (no `agent.model`) keep the waterfall override.
|
|
15
|
+
*
|
|
10
16
|
* Session overrides are recovered from successful command lifecycles
|
|
11
17
|
* (`/effort`, `/model`). Reusing Harness' durable `command/run` /
|
|
12
18
|
* `command/done` pair avoids inventing an out-of-repo Session event that
|
|
@@ -15,7 +21,7 @@
|
|
|
15
21
|
* @module dsh-channels/session/request-policy
|
|
16
22
|
*/
|
|
17
23
|
|
|
18
|
-
import type { AgentContext, CommandId, SessionEvent } from '../dsh-types.ts'
|
|
24
|
+
import type { Agent, AgentContext, AgentModel, CommandId, SessionEvent } from '../dsh-types.ts'
|
|
19
25
|
|
|
20
26
|
/** A model selection override: a bare model keeps the composed provider. */
|
|
21
27
|
export interface ModelOverride {
|
|
@@ -51,8 +57,16 @@ export interface RequestPolicyState {
|
|
|
51
57
|
|
|
52
58
|
export interface RequestPolicyController {
|
|
53
59
|
readonly state: RequestPolicyState
|
|
54
|
-
/**
|
|
55
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Re-fold the durable log after resume or a settled command lifecycle.
|
|
62
|
+
* With an `agent` whose host owns model selection, a changed policy is
|
|
63
|
+
* recorded through `agent.model.select()` for the next step. `reconcile`
|
|
64
|
+
* names a just-settled `/effort` or `/model` switch: the policy is recorded
|
|
65
|
+
* even when this controller saw no change, so a reset also clears a
|
|
66
|
+
* selection persisted before the controller existed (earlier plugin
|
|
67
|
+
* instance or since-removed config default).
|
|
68
|
+
*/
|
|
69
|
+
sync(events: readonly SessionEvent[], agent?: Agent, reconcile?: 'effort' | 'model'): void
|
|
56
70
|
/** Remove only this controller's Agent-scoped request wrapper. */
|
|
57
71
|
dispose(): void
|
|
58
72
|
}
|
|
@@ -134,6 +148,39 @@ export function foldModelOverride(events: readonly SessionEvent[]): ModelOverrid
|
|
|
134
148
|
return foldRequestPolicy(events).model
|
|
135
149
|
}
|
|
136
150
|
|
|
151
|
+
/** Policy values last recorded through `agent.model`, compared to skip no-op selections. */
|
|
152
|
+
interface AppliedPolicy {
|
|
153
|
+
readonly effort: string | undefined
|
|
154
|
+
readonly model: ModelOverride | undefined
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function sameModel(left: ModelOverride | undefined, right: ModelOverride | undefined): boolean {
|
|
158
|
+
return left?.provider === right?.provider && left?.model === right?.model
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Record one effective policy as the Agent's next-step selection. A policy
|
|
163
|
+
* without a model keeps the current route, except right after a model
|
|
164
|
+
* override is cleared, when the Agent's live options (deployment default)
|
|
165
|
+
* supply it again. Without a resolvable provider and model nothing is recorded.
|
|
166
|
+
*/
|
|
167
|
+
function selectPolicy(
|
|
168
|
+
model: AgentModel,
|
|
169
|
+
agent: Agent,
|
|
170
|
+
effort: string | undefined,
|
|
171
|
+
target: ModelOverride | undefined,
|
|
172
|
+
modelCleared: boolean,
|
|
173
|
+
): void {
|
|
174
|
+
const current = model.current()
|
|
175
|
+
const base = modelCleared && agent.options.provider !== undefined && agent.options.model !== undefined
|
|
176
|
+
? { provider: agent.options.provider, model: agent.options.model }
|
|
177
|
+
: current
|
|
178
|
+
const provider = target?.provider ?? base?.provider
|
|
179
|
+
const modelId = target?.model ?? base?.model
|
|
180
|
+
if (provider === undefined || provider === '' || modelId === undefined || modelId === '') return
|
|
181
|
+
model.select({ provider, model: modelId, ...(effort === undefined ? {} : { reasoningEffort: effort }) })
|
|
182
|
+
}
|
|
183
|
+
|
|
137
184
|
/** Install the mutable request policy outside every other request wrapper. */
|
|
138
185
|
export function installRequestPolicy(
|
|
139
186
|
agentCtx: AgentContext,
|
|
@@ -142,12 +189,16 @@ export function installRequestPolicy(
|
|
|
142
189
|
let effortOverride: string | undefined
|
|
143
190
|
let modelOverride: ModelOverride | undefined
|
|
144
191
|
let disposed = false
|
|
192
|
+
/** Last policy recorded through `agent.model`; undefined until the first selecting sync. */
|
|
193
|
+
let applied: AppliedPolicy | undefined
|
|
145
194
|
const configuredModel: ModelOverride | undefined = config.provider !== undefined && config.model !== undefined
|
|
146
195
|
? { provider: config.provider, model: config.model }
|
|
147
196
|
: undefined
|
|
148
197
|
|
|
149
|
-
const disposeListener = agentCtx.on('agent/request', async (
|
|
198
|
+
const disposeListener = agentCtx.on('agent/request', async (payload, next) => {
|
|
150
199
|
const request = await next()
|
|
200
|
+
// Hosts that own model selection reject a changed route or effort here.
|
|
201
|
+
if (payload.agent?.model !== undefined) return request
|
|
151
202
|
const effort = effortOverride ?? config.effort
|
|
152
203
|
const model = modelOverride ?? configuredModel
|
|
153
204
|
if (effort === undefined && model === undefined) return request
|
|
@@ -178,10 +229,18 @@ export function installRequestPolicy(
|
|
|
178
229
|
: configuredModel !== undefined ? 'config' : 'deployment-default',
|
|
179
230
|
}
|
|
180
231
|
},
|
|
181
|
-
sync(events) {
|
|
232
|
+
sync(events, agent, reconcile) {
|
|
182
233
|
const folded = foldRequestPolicy(events)
|
|
183
234
|
effortOverride = folded.effort
|
|
184
235
|
modelOverride = folded.model
|
|
236
|
+
const model = agent?.model
|
|
237
|
+
if (agent === undefined || model === undefined || disposed) return
|
|
238
|
+
const next: AppliedPolicy = { effort: effortOverride ?? config.effort, model: modelOverride ?? configuredModel }
|
|
239
|
+
const previous = applied ?? { effort: undefined, model: undefined }
|
|
240
|
+
applied = next
|
|
241
|
+
if (reconcile === undefined && previous.effort === next.effort && sameModel(previous.model, next.model)) return
|
|
242
|
+
const modelCleared = next.model === undefined && (reconcile === 'model' || previous.model !== undefined)
|
|
243
|
+
selectPolicy(model, agent, next.effort, next.model, modelCleared)
|
|
185
244
|
},
|
|
186
245
|
dispose() {
|
|
187
246
|
if (disposed) return
|
package/src/settings/contract.ts
CHANGED
|
@@ -7,29 +7,45 @@
|
|
|
7
7
|
* @module dsh-channels/settings/contract
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Settings panel routes: exact DSH `/api` Fetch routes named
|
|
12
|
+
* `/api/dsh-channels.<method>`. Reads are GET; every write is POST, because
|
|
13
|
+
* DSH exact `/api` routes accept only GET, HEAD, and POST.
|
|
14
|
+
*/
|
|
15
|
+
export const STATUS_ROUTE = '/api/dsh-channels.status'
|
|
16
|
+
export const PANEL_ROUTE = '/api/dsh-channels.panel'
|
|
17
|
+
/** Persist a partial config edit (POST, body `{ values }`). */
|
|
18
|
+
export const SAVE_CONFIG_ROUTE = '/api/dsh-channels.saveConfig'
|
|
19
|
+
/** Drop every Web override (POST, no body). */
|
|
20
|
+
export const CLEAR_CONFIG_ROUTE = '/api/dsh-channels.clearConfig'
|
|
21
|
+
/** Store credential values (POST, body `SaveCredentialsRequest`). */
|
|
22
|
+
export const SAVE_CREDENTIALS_ROUTE = '/api/dsh-channels.saveCredentials'
|
|
23
|
+
/** Remove one stored credential (POST, body `{ field }`). */
|
|
24
|
+
export const CLEAR_CREDENTIAL_ROUTE = '/api/dsh-channels.clearCredential'
|
|
15
25
|
/**
|
|
16
26
|
* Connection lifecycle action (POST). While the channel runs it restarts the
|
|
17
27
|
* long connection; while it is stopped it opens one — so the panel has exactly
|
|
18
28
|
* one "connect / reconnect" button. Credentials are resolved again either way.
|
|
19
29
|
*/
|
|
20
|
-
export const RECONNECT_ROUTE = '/dsh-channels
|
|
30
|
+
export const RECONNECT_ROUTE = '/api/dsh-channels.reconnect'
|
|
21
31
|
/** 模型/推理档位/预设选择器的宿主目录(provider → model → effort 级联刷新)。 */
|
|
22
|
-
export const CATALOG_ROUTE = '/dsh-channels
|
|
23
|
-
/** 访问与授权只读快照(GET
|
|
24
|
-
export const ACCESS_ROUTE = '/dsh-channels
|
|
25
|
-
/**
|
|
26
|
-
export const
|
|
27
|
-
/**
|
|
28
|
-
export const
|
|
29
|
-
/**
|
|
30
|
-
export const
|
|
32
|
+
export const CATALOG_ROUTE = '/api/dsh-channels.catalog'
|
|
33
|
+
/** 访问与授权只读快照(GET):准入模式、管理员、群/用户/知识库白名单、待处理申请。 */
|
|
34
|
+
export const ACCESS_ROUTE = '/api/dsh-channels.access'
|
|
35
|
+
/** 授权一个群(POST,body `{ chatId, name? }`)。 */
|
|
36
|
+
export const ADD_ACCESS_GROUP_ROUTE = '/api/dsh-channels.addAccessGroup'
|
|
37
|
+
/** 撤销一个群(POST,body `{ chatId }`)。 */
|
|
38
|
+
export const REMOVE_ACCESS_GROUP_ROUTE = '/api/dsh-channels.removeAccessGroup'
|
|
39
|
+
/** 用户白名单添加(POST,body `{ userId, name? }`)。 */
|
|
40
|
+
export const ADD_ACCESS_USER_ROUTE = '/api/dsh-channels.addAccessUser'
|
|
41
|
+
/** 用户白名单移除(POST,body `{ userId }`)。 */
|
|
42
|
+
export const REMOVE_ACCESS_USER_ROUTE = '/api/dsh-channels.removeAccessUser'
|
|
43
|
+
/** 知识库白名单添加(POST,body `{ userId, name? }`)。 */
|
|
44
|
+
export const ADD_WIKI_USER_ROUTE = '/api/dsh-channels.addWikiUser'
|
|
45
|
+
/** 知识库白名单移除(POST,body `{ userId }`)。 */
|
|
46
|
+
export const REMOVE_WIKI_USER_ROUTE = '/api/dsh-channels.removeWikiUser'
|
|
31
47
|
/** 结算一条待处理授权申请(POST,body `{ token, decision }`)。 */
|
|
32
|
-
export const
|
|
48
|
+
export const RESOLVE_ACCESS_REQUEST_ROUTE = '/api/dsh-channels.resolveAccessRequest'
|
|
33
49
|
/**
|
|
34
50
|
* 打开宿主机的系统目录选择框(POST,无请求体),返回绝对路径。
|
|
35
51
|
*
|
|
@@ -37,9 +53,9 @@ export const ACCESS_REQUESTS_ROUTE = '/dsh-channels/access/requests'
|
|
|
37
53
|
* 会注册实际选择逻辑;远程/无头会话的 browse 后端不可用,面板不下发
|
|
38
54
|
* [`EditableFieldWire.browse`],保留手写输入。
|
|
39
55
|
*/
|
|
40
|
-
export const PICK_DIRECTORY_ROUTE = '/dsh-channels
|
|
41
|
-
/** 会话的飞书来源(GET
|
|
42
|
-
export const SESSION_ORIGIN_ROUTE = '/dsh-channels
|
|
56
|
+
export const PICK_DIRECTORY_ROUTE = '/api/dsh-channels.pickDirectory'
|
|
57
|
+
/** 会话的飞书来源(GET,`?sessionId=`):对话右侧栏「飞书来源」标签与会话头部入口使用。 */
|
|
58
|
+
export const SESSION_ORIGIN_ROUTE = '/api/dsh-channels.sessionOrigin'
|
|
43
59
|
|
|
44
60
|
/** Where one Feishu-routed Session came from; names are resolved live and may be absent. */
|
|
45
61
|
export interface SessionOriginWire {
|
|
@@ -76,7 +92,7 @@ export interface SessionOriginWire {
|
|
|
76
92
|
* names and avatars for the users and chats the panel shows. At most
|
|
77
93
|
* {@link PROFILES_MAX_IDS} ids per list.
|
|
78
94
|
*/
|
|
79
|
-
export const PROFILES_ROUTE = '/dsh-channels
|
|
95
|
+
export const PROFILES_ROUTE = '/api/dsh-channels.profiles'
|
|
80
96
|
|
|
81
97
|
/** Most `user_id`s, and separately most `chat_id`s, one profiles request may name. */
|
|
82
98
|
export const PROFILES_MAX_IDS = 200
|
|
@@ -111,7 +127,7 @@ export interface SessionOriginResponse {
|
|
|
111
127
|
* `cordis.patch.yml`(见 README「Web 设置面板」)。
|
|
112
128
|
*
|
|
113
129
|
* 刻意不包含 `appId` / `appSecret`:它们是**凭据引用名**,面板改为直接
|
|
114
|
-
* 编辑引用背后的值(`
|
|
130
|
+
* 编辑引用背后的值(`POST /api/dsh-channels.saveCredentials`,经 DSH 凭据服务写入),
|
|
115
131
|
* 因此不会落进覆盖层文件。
|
|
116
132
|
*
|
|
117
133
|
* `baseDir`(工作区根目录 = 模型/工具产生文件的落点)可覆盖,但面板只接受
|
|
@@ -172,7 +188,7 @@ export const EDITABLE_KEYS = [
|
|
|
172
188
|
'feishuTools.wikiWrite',
|
|
173
189
|
'wikiRoots',
|
|
174
190
|
// 日志平台分析接口(访问令牌是凭据引用 analyzeToken,值写在 DSH 凭据里)
|
|
175
|
-
'
|
|
191
|
+
'analyzeEnabled',
|
|
176
192
|
// 日志
|
|
177
193
|
'loggerLevel',
|
|
178
194
|
] as const
|
|
@@ -187,7 +203,6 @@ export const RESTART_KEYS = [
|
|
|
187
203
|
'dmMode',
|
|
188
204
|
'requireMention',
|
|
189
205
|
'slashCommandPanel',
|
|
190
|
-
'analyzeListen',
|
|
191
206
|
'refreshMs',
|
|
192
207
|
'statusRefreshMs',
|
|
193
208
|
'loggerLevel',
|
|
@@ -305,7 +320,7 @@ export interface SelectCatalogWire {
|
|
|
305
320
|
help?: string
|
|
306
321
|
}
|
|
307
322
|
|
|
308
|
-
/** `GET /dsh-channels
|
|
323
|
+
/** `GET /api/dsh-channels.catalog` payload: the model-facing selector catalog. */
|
|
309
324
|
export interface CatalogWire {
|
|
310
325
|
provider: SelectCatalogWire
|
|
311
326
|
model: SelectCatalogWire
|
|
@@ -499,7 +514,7 @@ export interface AccessRequestWire {
|
|
|
499
514
|
}
|
|
500
515
|
|
|
501
516
|
/**
|
|
502
|
-
* `GET /dsh-channels
|
|
517
|
+
* `GET /api/dsh-channels.access` payload: admission mode, the administrator
|
|
503
518
|
* reference, the group, user, and wiki whitelists, and the still-pending
|
|
504
519
|
* authorization requests.
|
|
505
520
|
*/
|
|
@@ -528,12 +543,12 @@ export interface AccessWire {
|
|
|
528
543
|
requests: AccessRequestWire[]
|
|
529
544
|
}
|
|
530
545
|
|
|
531
|
-
/** Write responses for every
|
|
546
|
+
/** Write responses for every access mutation route. */
|
|
532
547
|
export type AccessMutationResponse =
|
|
533
548
|
| { ok: true; access: AccessWire }
|
|
534
549
|
| { ok: false; error: string }
|
|
535
550
|
|
|
536
|
-
/** `GET /dsh-channels
|
|
551
|
+
/** `GET /api/dsh-channels.panel` payload. */
|
|
537
552
|
export interface PanelWire {
|
|
538
553
|
status: ChannelStatusWire
|
|
539
554
|
config: PanelConfigWire
|
|
@@ -542,7 +557,7 @@ export interface PanelWire {
|
|
|
542
557
|
}
|
|
543
558
|
|
|
544
559
|
/**
|
|
545
|
-
* `POST /dsh-channels
|
|
560
|
+
* `POST /api/dsh-channels.pickDirectory` response. `canceled` is a normal
|
|
546
561
|
* outcome (the operator dismissed the OS dialog), not an error; `path` is an
|
|
547
562
|
* absolute directory the Host's native chooser returned.
|
|
548
563
|
*/
|
|
@@ -551,7 +566,7 @@ export type PickDirectoryResponse =
|
|
|
551
566
|
| { ok: true; canceled: true }
|
|
552
567
|
| { ok: false; error: string }
|
|
553
568
|
|
|
554
|
-
/** `
|
|
569
|
+
/** `POST /api/dsh-channels.saveConfig` body. */
|
|
555
570
|
export interface SaveConfigRequest {
|
|
556
571
|
values: EditableValues
|
|
557
572
|
}
|
|
@@ -567,30 +582,30 @@ export interface FieldIssueWire {
|
|
|
567
582
|
index?: number
|
|
568
583
|
}
|
|
569
584
|
|
|
570
|
-
/** `
|
|
585
|
+
/** `POST /api/dsh-channels.saveConfig` response. */
|
|
571
586
|
export type SaveConfigResponse =
|
|
572
587
|
| { ok: true; panel: PanelWire; clearedKeys: EditableKey[] }
|
|
573
588
|
| { ok: false; error: string; issues?: FieldIssueWire[] }
|
|
574
589
|
|
|
575
|
-
/** `POST /dsh-channels
|
|
590
|
+
/** `POST /api/dsh-channels.reconnect` response. */
|
|
576
591
|
export type ReconnectResponse =
|
|
577
592
|
| { ok: true; panel: PanelWire }
|
|
578
593
|
/** `unconfigured` tells the panel to route the user to the credentials form. */
|
|
579
594
|
| { ok: false; error: string; unconfigured?: boolean }
|
|
580
595
|
|
|
581
|
-
/** `
|
|
596
|
+
/** `POST /api/dsh-channels.clearConfig` response. */
|
|
582
597
|
export type ClearConfigResponse =
|
|
583
598
|
| { ok: true; panel: PanelWire }
|
|
584
599
|
| { ok: false; error: string }
|
|
585
600
|
|
|
586
|
-
/** `
|
|
601
|
+
/** `POST /api/dsh-channels.saveCredentials` body: only provided, non-empty values are written. */
|
|
587
602
|
export interface SaveCredentialsRequest {
|
|
588
603
|
appId?: string
|
|
589
604
|
appSecret?: string
|
|
590
605
|
analyzeToken?: string
|
|
591
606
|
}
|
|
592
607
|
|
|
593
|
-
/** `
|
|
608
|
+
/** `POST /api/dsh-channels.clearCredential` body. */
|
|
594
609
|
export interface ClearCredentialRequest {
|
|
595
610
|
field: CredentialField
|
|
596
611
|
}
|