@xmoon76/dsh-pi-tui 0.2.2 → 0.3.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 +3 -1
- package/README.zh-CN.md +3 -1
- package/cordis.patch.yml +13 -1
- package/dist/builtins.mjs +4 -3
- package/dist/extensions.mjs +1 -1
- package/dist/index.d.mts +11 -1
- package/dist/index.mjs +2 -2
- package/dist/{src-BsSlpFFF.mjs → src-ClAKiW0z.mjs} +532 -73
- package/package.json +62 -60
package/README.md
CHANGED
|
@@ -30,7 +30,9 @@ output.
|
|
|
30
30
|
|
|
31
31
|
## Prerequisites
|
|
32
32
|
|
|
33
|
-
- A DeepSeek Harness installation with profiles support (`dsh` on your `PATH`)
|
|
33
|
+
- A DeepSeek Harness installation with profiles support (`dsh` on your `PATH`),
|
|
34
|
+
version `0.1.1-rc.1` or later on the same compat line (the TUI consumes the
|
|
35
|
+
split credential events and the `ctx.authorization` seam of that release).
|
|
34
36
|
- Node >= 22.19 (`^22.19.0 || >=24`, same range as dsh). Running from source
|
|
35
37
|
needs Node with native TypeScript support (>= 23.6) or the tsx ESM hook
|
|
36
38
|
(`node --import tsx/esm`, how dsh's own source launch works).
|
package/README.zh-CN.md
CHANGED
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
## 前置要求
|
|
30
30
|
|
|
31
|
-
- 一个支持 profile 的 DeepSeek Harness 安装(`dsh` 已在 `PATH` 中)
|
|
31
|
+
- 一个支持 profile 的 DeepSeek Harness 安装(`dsh` 已在 `PATH` 中),版本
|
|
32
|
+
`0.1.1-rc.1` 或同一兼容线上的更新版本——TUI 消费该版本的拆分凭据事件
|
|
33
|
+
与 `ctx.authorization` 服务。
|
|
32
34
|
- Node >= 22.19(`^22.19.0 || >=24`,与 dsh 相同范围)。从源码运行需要
|
|
33
35
|
原生支持 TypeScript 的 Node(>= 23.6)或 tsx ESM 钩子
|
|
34
36
|
(`node --import tsx/esm`,dsh 自身源码启动的方式)。
|
package/cordis.patch.yml
CHANGED
|
@@ -26,10 +26,22 @@
|
|
|
26
26
|
|
|
27
27
|
- id: tui-app
|
|
28
28
|
name: '@xmoon76/dsh-pi-tui'
|
|
29
|
-
inject: [tuiStartup, piTuiExtensions]
|
|
29
|
+
inject: [tuiStartup, piTuiExtensions, authorization]
|
|
30
30
|
config:
|
|
31
31
|
sessionId: ''
|
|
32
32
|
|
|
33
|
+
# The authorization seam (`ctx.authorization`): plugin-owned credential-
|
|
34
|
+
# obtaining flows, one attempt per key, transport-neutral notices and
|
|
35
|
+
# prompts. The dsh host does NOT mount it anywhere (the base layer ships
|
|
36
|
+
# only `credentials` + `llm-pi-ai`), so the TUI profile inserts the row
|
|
37
|
+
# itself — the TUI is the interactive surface that can answer its
|
|
38
|
+
# prompts. `AuthorizationService` injects `credentials`, so this row
|
|
39
|
+
# activates only after the base's credentials row; llm-pi-ai's
|
|
40
|
+
# provider login flows register once both services are up (it injects
|
|
41
|
+
# `authorization` optionally and degrades without it).
|
|
42
|
+
- id: authorization
|
|
43
|
+
name: '@deepseek-ai/dsh-authorization'
|
|
44
|
+
|
|
33
45
|
# The agent-preset roster. The SHIPPED root is an assembly fact of this
|
|
34
46
|
# bundle: the four presets (standard/code/minimal/cordis) travel with the
|
|
35
47
|
# package under config/agent-presets and carry `system` trust. The
|
package/dist/builtins.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TUI_STARTUP_SERVICE } from "./startup.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { A as dshVersion, F as PI_TUI_EXTENSIONS_SERVICE } from "./src-ClAKiW0z.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
//#region src/builtins.ts
|
|
@@ -27,12 +27,13 @@ function apply(ctx) {
|
|
|
27
27
|
if (ctx.get("tuiStartup") === void 0) return;
|
|
28
28
|
const service = ctx.get(PI_TUI_EXTENSIONS_SERVICE);
|
|
29
29
|
if (service === void 0) return;
|
|
30
|
+
const installedDsh = dshVersion();
|
|
30
31
|
service.register("chrome.header.badge", {
|
|
31
32
|
id: "builtin-version",
|
|
32
33
|
order: 1e3,
|
|
33
|
-
description: "The bundle
|
|
34
|
+
description: "The dsh and bundle versions (first-party builtin)."
|
|
34
35
|
}, {
|
|
35
|
-
text: `v${packageVersion()}`,
|
|
36
|
+
text: installedDsh === void 0 ? `tui-v${packageVersion()}` : `dsh-${installedDsh} · tui-v${packageVersion()}`,
|
|
36
37
|
tone: "info"
|
|
37
38
|
});
|
|
38
39
|
const counters = service.register("chrome.footer.status", {
|
package/dist/extensions.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import "./startup.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { F as PI_TUI_EXTENSIONS_SERVICE, I as API_VERSION, L as describeKey, M as apply, N as inject, P as name } from "./src-ClAKiW0z.mjs";
|
|
3
3
|
export { API_VERSION, PI_TUI_EXTENSIONS_SERVICE, apply, describeKey, inject, name };
|
package/dist/index.d.mts
CHANGED
|
@@ -125,6 +125,16 @@ interface Config {
|
|
|
125
125
|
sessionId?: string;
|
|
126
126
|
}
|
|
127
127
|
declare const Config: z<Config>;
|
|
128
|
+
/**
|
|
129
|
+
* Wire the credential surface refresh to the dsh 0.1.1-rc.1 credential
|
|
130
|
+
* events. The rc.1 release split the credential update event into the
|
|
131
|
+
* reference half and the durable-record half; both change what the footer
|
|
132
|
+
* model row and the welcome card show (a /login /logout, an external
|
|
133
|
+
* .credentials.yaml edit, or an authorization flow committing a record), so
|
|
134
|
+
* they share one best-effort callback. The callback must not read secrets
|
|
135
|
+
* or mutate providers — it only re-reads describe-level state.
|
|
136
|
+
*/
|
|
137
|
+
declare function registerCredentialSurfaceRefresh(ctx: Context, refresh: () => void): void;
|
|
128
138
|
/**
|
|
129
139
|
* Slash commands that need no session: before the first user message
|
|
130
140
|
* (deferred start) they run locally without creating one. Everything else
|
|
@@ -498,4 +508,4 @@ declare function recomposeBlank(ctx: Context, agent: {
|
|
|
498
508
|
*/
|
|
499
509
|
declare function apply(ctx: Context, config: Config): void;
|
|
500
510
|
//#endregion
|
|
501
|
-
export { AgentComposition, CompactionFold, Config, HOST_COMMAND_CATALOG, InitialCatalogResolution, LOCAL_COMMANDS, PendingSubagentCall, QueueFoldResult, QueueInboxMessage, QueueNoticeSource, RecomposableSession, RecomposeOutcome, ResolveInitialCatalogOptions, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, busyAfterTurnBoundary, compactingFromLog, composeAgent, dangerCommand, foldCompactionEvent, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, isUserQueueInput, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
|
|
511
|
+
export { AgentComposition, CompactionFold, Config, HOST_COMMAND_CATALOG, InitialCatalogResolution, LOCAL_COMMANDS, PendingSubagentCall, QueueFoldResult, QueueInboxMessage, QueueNoticeSource, RecomposableSession, RecomposeOutcome, ResolveInitialCatalogOptions, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, busyAfterTurnBoundary, compactingFromLog, composeAgent, dangerCommand, foldCompactionEvent, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, isUserQueueInput, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, registerCredentialSurfaceRefresh, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import "./startup.mjs";
|
|
2
|
-
import { C as
|
|
3
|
-
export { Config, HOST_COMMAND_CATALOG, LOCAL_COMMANDS, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, busyAfterTurnBoundary, compactingFromLog, composeAgent, dangerCommand, foldCompactionEvent, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, isUserQueueInput, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
|
|
2
|
+
import { C as resumeCommand, D as subagentJobTranscriptId, E as shouldSteerOnEnter, O as subagentJobViewHint, S as resolveInitialCatalog, T as shouldConsumeAdvertisedMiss, _ as matchPendingSubagentCall, a as SessionLockRefusedError, b as recordedPreset, c as compactingFromLog, d as foldCompactionEvent, f as foldQueueRows, g as isUserQueueInput, h as isSubagentSettlementNotice, i as SESSIONLESS_COMMANDS, j as forkSeed, k as subagentNoticeIsFailure, l as composeAgent, m as isPlainExitPrompt, n as HOST_COMMAND_CATALOG, o as apply, p as inject, r as LOCAL_COMMANDS, s as busyAfterTurnBoundary, t as Config, u as dangerCommand, v as name, w as runningProfile, x as registerCredentialSurfaceRefresh, y as recomposeBlank } from "./src-ClAKiW0z.mjs";
|
|
3
|
+
export { Config, HOST_COMMAND_CATALOG, LOCAL_COMMANDS, SESSIONLESS_COMMANDS, SessionLockRefusedError, apply, busyAfterTurnBoundary, compactingFromLog, composeAgent, dangerCommand, foldCompactionEvent, foldQueueRows, forkSeed, inject, isPlainExitPrompt, isSubagentSettlementNotice, isUserQueueInput, matchPendingSubagentCall, name, recomposeBlank, recordedPreset, registerCredentialSurfaceRefresh, resolveInitialCatalog, resumeCommand, runningProfile, shouldConsumeAdvertisedMiss, shouldSteerOnEnter, subagentJobTranscriptId, subagentJobViewHint, subagentNoticeIsFailure };
|
|
@@ -31,6 +31,8 @@ import { fileURLToPath } from "node:url";
|
|
|
31
31
|
import { EventEmitter } from "events";
|
|
32
32
|
import { Service } from "@deepseek-ai/cordis";
|
|
33
33
|
import { isUserInvocable, renderSkillContent } from "@deepseek-ai/dsh-skill";
|
|
34
|
+
import { AuthorizationDeclinedError } from "@deepseek-ai/dsh-authorization";
|
|
35
|
+
import { credentialKeyId, credentialKeyScope } from "@deepseek-ai/dsh-credentials";
|
|
34
36
|
//#region src/present.ts
|
|
35
37
|
/** Figma row titles per variant (design literals, not translatable copy). */
|
|
36
38
|
const VARIANT_TITLES = {
|
|
@@ -14014,6 +14016,11 @@ var QuestionFlow = class {
|
|
|
14014
14016
|
if (row.key === OTHER_ROW && this.editingOther) {
|
|
14015
14017
|
const inputLine = this.otherInput.render(Math.max(1, width - visibleWidth(prefix)))[0] ?? "";
|
|
14016
14018
|
const stripped = inputLine.startsWith("> ") ? inputLine.slice(2) : inputLine;
|
|
14019
|
+
if (question.masked === true && this.otherInput.getValue() !== "") {
|
|
14020
|
+
lines.push(prefix + color.textDim("•".repeat(this.otherInput.getValue().length)));
|
|
14021
|
+
hits.push(OTHER_ROW);
|
|
14022
|
+
continue;
|
|
14023
|
+
}
|
|
14017
14024
|
lines.push(prefix + (this.otherInput.getValue() === "" ? color.textDim(row.label) : stripped));
|
|
14018
14025
|
hits.push(OTHER_ROW);
|
|
14019
14026
|
continue;
|
|
@@ -14326,7 +14333,7 @@ var QuestionFlow = class {
|
|
|
14326
14333
|
const question = this.questions[qi];
|
|
14327
14334
|
const draft = this.drafts[qi];
|
|
14328
14335
|
if (question === void 0 || draft === void 0) continue;
|
|
14329
|
-
const value = draft.skipped ? "(skipped)" : draft.custom !== "" && question.multiSelect !== true ? draft.custom : [...draft.selected].join(", ") + (draft.custom !== "" ? ` + ${draft.custom}` : "");
|
|
14336
|
+
const value = draft.skipped ? "(skipped)" : draft.custom !== "" && question.multiSelect !== true ? question.masked === true ? "•".repeat(draft.custom.length) : draft.custom : [...draft.selected].join(", ") + (draft.custom !== "" ? ` + ${draft.custom}` : "");
|
|
14330
14337
|
reviewBudget = appendWrappedBudgeted(lines, `${color.textDim(`Q${qi + 1}`)} `, " ", question.question, safeWidth, reviewBudget);
|
|
14331
14338
|
if (reviewBudget > 0) reviewBudget = appendWrappedBudgeted(lines, " ", " ", value === "" ? color.textDim("(no answer)") : value, safeWidth, reviewBudget);
|
|
14332
14339
|
}
|
|
@@ -14387,7 +14394,8 @@ var QuestionFlow = class {
|
|
|
14387
14394
|
if (optionless) {
|
|
14388
14395
|
const inputLine = this.otherInput.render(Math.max(1, safeWidth - 2))[0] ?? "";
|
|
14389
14396
|
const stripped = inputLine.startsWith("> ") ? inputLine.slice(2) : inputLine;
|
|
14390
|
-
|
|
14397
|
+
if (question.masked === true && this.otherInput.getValue() !== "") lines.push(` ${color.textDim("•".repeat(this.otherInput.getValue().length))}`);
|
|
14398
|
+
else lines.push(this.otherInput.getValue() === "" ? color.textDim(" Type your answer…") : ` ${stripped}`);
|
|
14391
14399
|
}
|
|
14392
14400
|
if (draft.skipped) lines.push(color.textDim("(skipped)"));
|
|
14393
14401
|
lines.push("");
|
|
@@ -14513,8 +14521,15 @@ function parseShellWords(line) {
|
|
|
14513
14521
|
* the user's own draft.
|
|
14514
14522
|
* @module @xmoon76/dsh-pi-tui/shell-completion
|
|
14515
14523
|
*/
|
|
14516
|
-
/**
|
|
14517
|
-
|
|
14524
|
+
/**
|
|
14525
|
+
* Hard cap on one compgen spawn (ms). GitHub Actions runners are markedly
|
|
14526
|
+
* slower at a `bash -lc` cold start (measured 260-310ms in CI vs ~60ms
|
|
14527
|
+
* locally — a login shell sources /etc/profile, which is heavy on the
|
|
14528
|
+
* runner image), so a single tight cap made the suite flaky there (three
|
|
14529
|
+
* CI failures, every one at the 300ms boundary). The cap is therefore
|
|
14530
|
+
* raised under GitHub Actions only; local runs keep the tight cap.
|
|
14531
|
+
*/
|
|
14532
|
+
const COMPGEN_TIMEOUT_MS = process.env.GITHUB_ACTIONS === "true" ? 1500 : 300;
|
|
14518
14533
|
/** Command-name cache TTL (ms): command sets change rarely. */
|
|
14519
14534
|
const COMMAND_CACHE_TTL_MS = 3e4;
|
|
14520
14535
|
/** Suggestion cap per request (the fork's own lists are capped too). */
|
|
@@ -21575,7 +21590,8 @@ var TuiApp = class TuiApp {
|
|
|
21575
21590
|
...question.detail !== void 0 ? { detail: question.detail } : {},
|
|
21576
21591
|
...question.options !== void 0 ? { options: question.options } : {},
|
|
21577
21592
|
...question.multiSelect !== void 0 ? { multiSelect: question.multiSelect } : {},
|
|
21578
|
-
...question.intent !== void 0 ? { intent: question.intent } : {}
|
|
21593
|
+
...question.intent !== void 0 ? { intent: question.intent } : {},
|
|
21594
|
+
...question.masked !== void 0 ? { masked: question.masked } : {}
|
|
21579
21595
|
})), (answers) => this.settleQuestions(state, answers), () => this.settleQuestions(state, void 0)),
|
|
21580
21596
|
suspendedOverlays: /* @__PURE__ */ new Set(),
|
|
21581
21597
|
resolve,
|
|
@@ -25175,6 +25191,7 @@ function providerOptionsFor(entries, readSection) {
|
|
|
25175
25191
|
route: "deepseek-official",
|
|
25176
25192
|
configured: true,
|
|
25177
25193
|
declared: false,
|
|
25194
|
+
namesCredential: true,
|
|
25178
25195
|
group: "configured",
|
|
25179
25196
|
settingsNs: "",
|
|
25180
25197
|
settingsPath: []
|
|
@@ -25186,13 +25203,15 @@ function providerOptionsFor(entries, readSection) {
|
|
|
25186
25203
|
const profile = entry.settingsPath.length === 0 ? section : getPath(section, entry.settingsPath);
|
|
25187
25204
|
const configured = profile !== void 0;
|
|
25188
25205
|
const namedRef = configured && typeof profile === "object" && profile !== null && typeof profile.apiKeyEnv === "string" ? profile.apiKeyEnv : void 0;
|
|
25189
|
-
const
|
|
25206
|
+
const namesCredential = namedRef !== void 0 && namedRef !== "";
|
|
25207
|
+
const ref = namesCredential ? namedRef : deriveKeyRef(entry.provider);
|
|
25190
25208
|
push({
|
|
25191
25209
|
label: entry.displayName,
|
|
25192
25210
|
ref,
|
|
25193
25211
|
route: entry.provider,
|
|
25194
25212
|
configured,
|
|
25195
25213
|
declared: entry.declared === true,
|
|
25214
|
+
namesCredential,
|
|
25196
25215
|
group: configured ? "configured" : entry.declared === true ? "custom" : "available",
|
|
25197
25216
|
settingsNs: entry.settingsNs,
|
|
25198
25217
|
settingsPath: [...entry.settingsPath]
|
|
@@ -25237,6 +25256,184 @@ function resolveCredentialArg(arg, options) {
|
|
|
25237
25256
|
if (route !== void 0) return route.ref;
|
|
25238
25257
|
if (/^[a-zA-Z][a-zA-Z0-9_]*$/.test(trimmed)) return trimmed.toUpperCase();
|
|
25239
25258
|
}
|
|
25259
|
+
/** Map the seam's entries to /login targets, deriving the route from the
|
|
25260
|
+
* key's scope (llm-pi-ai flows address the provider route). */
|
|
25261
|
+
function authorizationTargets(entries) {
|
|
25262
|
+
return entries.map((entry) => ({
|
|
25263
|
+
kind: "authorization",
|
|
25264
|
+
route: credentialKeyScope(entry.key) === "llm-pi-ai" ? credentialKeyId(entry.key) : void 0,
|
|
25265
|
+
key: entry.key,
|
|
25266
|
+
label: entry.label,
|
|
25267
|
+
methods: entry.methods,
|
|
25268
|
+
inFlight: entry.inFlight
|
|
25269
|
+
}));
|
|
25270
|
+
}
|
|
25271
|
+
/** The flow target for a provider route, when one is registered. */
|
|
25272
|
+
function flowForRoute(targets, route) {
|
|
25273
|
+
return targets.find((target) => target.route === route);
|
|
25274
|
+
}
|
|
25275
|
+
/**
|
|
25276
|
+
* Merge the reference targets and the authorization flows into the /login
|
|
25277
|
+
* picker set, applying the rc.1 dedup rule per route:
|
|
25278
|
+
*
|
|
25279
|
+
* - explicit `apiKeyEnv` profile → the reference target wins (the flow is
|
|
25280
|
+
* NOT offered as a second default entry for the same route — requests
|
|
25281
|
+
* resolve the reference first and a "successful" flow login would leave
|
|
25282
|
+
* the reference unset, failing the next request);
|
|
25283
|
+
* - keyless route with a flow → the authorization target wins (no derived
|
|
25284
|
+
* `<ROUTE>_API_KEY` entry is offered as a default);
|
|
25285
|
+
* - keyless route without a flow → the derived reference fallback.
|
|
25286
|
+
*
|
|
25287
|
+
* Flows whose key maps no option route (foreign scopes, or catalog routes
|
|
25288
|
+
* the llm directory does not expose) are appended as standalone targets.
|
|
25289
|
+
*/
|
|
25290
|
+
function mergeLoginTargets(options, targets) {
|
|
25291
|
+
const flowByRoute = /* @__PURE__ */ new Map();
|
|
25292
|
+
const standalone = [];
|
|
25293
|
+
for (const target of targets) if (target.route !== void 0) flowByRoute.set(target.route, target);
|
|
25294
|
+
else standalone.push(target);
|
|
25295
|
+
const merged = [];
|
|
25296
|
+
const covered = /* @__PURE__ */ new Set();
|
|
25297
|
+
for (const option of options) {
|
|
25298
|
+
const flow = flowByRoute.get(option.route);
|
|
25299
|
+
merged.push(option.namesCredential || flow === void 0 ? {
|
|
25300
|
+
kind: "reference",
|
|
25301
|
+
route: option.route,
|
|
25302
|
+
label: option.label,
|
|
25303
|
+
ref: option.ref,
|
|
25304
|
+
configured: option.configured,
|
|
25305
|
+
declared: option.declared,
|
|
25306
|
+
namesCredential: option.namesCredential
|
|
25307
|
+
} : flow);
|
|
25308
|
+
covered.add(option.route);
|
|
25309
|
+
}
|
|
25310
|
+
for (const target of targets) if (target.route === void 0 || !covered.has(target.route)) merged.push(target);
|
|
25311
|
+
return merged;
|
|
25312
|
+
}
|
|
25313
|
+
/**
|
|
25314
|
+
* The one user-facing notice body: the message first, then the page to open
|
|
25315
|
+
* and the code to enter, each on its own line. Never truncated — the
|
|
25316
|
+
* output panel wraps, so the URL stays copyable. Notices never carry
|
|
25317
|
+
* secrets.
|
|
25318
|
+
*/
|
|
25319
|
+
function formatAuthorizationNotice(notice) {
|
|
25320
|
+
const lines = [notice.message];
|
|
25321
|
+
if (notice.url !== void 0 && notice.url !== "") lines.push("", "Open this page to continue:", notice.url);
|
|
25322
|
+
if (notice.code !== void 0 && notice.code !== "") lines.push("", `Code: ${notice.code}`);
|
|
25323
|
+
return lines.join("\n");
|
|
25324
|
+
}
|
|
25325
|
+
/**
|
|
25326
|
+
* Build the interaction half of one authorization attempt. One instance per
|
|
25327
|
+
* attempt: the notice panel is reused across progress notices, and
|
|
25328
|
+
* `close()` hides it when the attempt settles (the caller's finally).
|
|
25329
|
+
*
|
|
25330
|
+
* Prompt mapping:
|
|
25331
|
+
* - `text` and `secret` go through the question flow's free-text row;
|
|
25332
|
+
* `secret` is rendered masked (the real value never leaves the input's
|
|
25333
|
+
* memory and is never logged, put in history, or shown anywhere else);
|
|
25334
|
+
* - `select` goes through the picker and returns the chosen option's `id`,
|
|
25335
|
+
* never its label;
|
|
25336
|
+
* - the user closing the question is a decline → `AuthorizationDeclinedError`;
|
|
25337
|
+
* - the prompt's OWN signal withdrawing it is NOT a decline: the rejection
|
|
25338
|
+
* is passed through as-is so the flow can tell a refusal from a race.
|
|
25339
|
+
*/
|
|
25340
|
+
function createAuthorizationInteraction(app) {
|
|
25341
|
+
let noticeHandle;
|
|
25342
|
+
let noticeBody = "";
|
|
25343
|
+
const showNotice = (notice) => {
|
|
25344
|
+
noticeBody = formatAuthorizationNotice(notice);
|
|
25345
|
+
if (noticeHandle === void 0) noticeHandle = app.openOutputViewer({
|
|
25346
|
+
title: "Sign in",
|
|
25347
|
+
initial: noticeBody,
|
|
25348
|
+
refresh: () => noticeBody,
|
|
25349
|
+
onClose: () => {
|
|
25350
|
+
noticeHandle = void 0;
|
|
25351
|
+
}
|
|
25352
|
+
});
|
|
25353
|
+
};
|
|
25354
|
+
const close = () => {
|
|
25355
|
+
noticeHandle?.();
|
|
25356
|
+
noticeHandle = void 0;
|
|
25357
|
+
};
|
|
25358
|
+
return {
|
|
25359
|
+
interaction: {
|
|
25360
|
+
notify: (notice) => {
|
|
25361
|
+
showNotice(notice);
|
|
25362
|
+
},
|
|
25363
|
+
prompt: async (prompt) => {
|
|
25364
|
+
if (prompt.kind === "select") {
|
|
25365
|
+
const signal = prompt.signal;
|
|
25366
|
+
return await new Promise((resolve, reject) => {
|
|
25367
|
+
if (signal?.aborted === true) {
|
|
25368
|
+
reject(cancellationError("authorization prompt withdrawn"));
|
|
25369
|
+
return;
|
|
25370
|
+
}
|
|
25371
|
+
let settled = false;
|
|
25372
|
+
let handle;
|
|
25373
|
+
const cleanup = () => {
|
|
25374
|
+
if (signal !== void 0) signal.removeEventListener("abort", onAbort);
|
|
25375
|
+
};
|
|
25376
|
+
const onAbort = () => {
|
|
25377
|
+
if (settled) return;
|
|
25378
|
+
settled = true;
|
|
25379
|
+
cleanup();
|
|
25380
|
+
handle?.close?.();
|
|
25381
|
+
reject(cancellationError("authorization prompt withdrawn"));
|
|
25382
|
+
};
|
|
25383
|
+
if (signal !== void 0) signal.addEventListener("abort", onAbort, { once: true });
|
|
25384
|
+
handle = app.openPicker(prompt.options.map((option) => ({
|
|
25385
|
+
value: option.id,
|
|
25386
|
+
label: option.label,
|
|
25387
|
+
...option.description !== void 0 && option.description !== "" ? { description: option.description } : {}
|
|
25388
|
+
})), (value) => {
|
|
25389
|
+
if (settled) return;
|
|
25390
|
+
settled = true;
|
|
25391
|
+
cleanup();
|
|
25392
|
+
resolve(value);
|
|
25393
|
+
}, () => {
|
|
25394
|
+
if (settled) return;
|
|
25395
|
+
settled = true;
|
|
25396
|
+
cleanup();
|
|
25397
|
+
reject(new AuthorizationDeclinedError());
|
|
25398
|
+
}, {
|
|
25399
|
+
header: prompt.message,
|
|
25400
|
+
enableSearch: true
|
|
25401
|
+
});
|
|
25402
|
+
if (settled) handle?.close?.();
|
|
25403
|
+
});
|
|
25404
|
+
}
|
|
25405
|
+
try {
|
|
25406
|
+
const text = (await app.askQuestions([{
|
|
25407
|
+
id: "answer",
|
|
25408
|
+
question: prompt.message,
|
|
25409
|
+
...prompt.placeholder !== void 0 && prompt.placeholder !== "" ? { placeholder: prompt.placeholder } : {},
|
|
25410
|
+
...prompt.kind === "secret" ? { masked: true } : {}
|
|
25411
|
+
}], prompt.signal))[0]?.custom ?? "";
|
|
25412
|
+
if (text === "") throw new AuthorizationDeclinedError();
|
|
25413
|
+
return text;
|
|
25414
|
+
} catch (error) {
|
|
25415
|
+
if (prompt.signal?.aborted === true) throw error;
|
|
25416
|
+
throw new AuthorizationDeclinedError();
|
|
25417
|
+
}
|
|
25418
|
+
}
|
|
25419
|
+
},
|
|
25420
|
+
close
|
|
25421
|
+
};
|
|
25422
|
+
}
|
|
25423
|
+
/** The user-facing text for one authorization attempt failure, mapping the
|
|
25424
|
+
* seam's stable error taxonomy to copy (plan §15). `unknownErrorText` is
|
|
25425
|
+
* the safe fallback for provider/network errors (already passed through
|
|
25426
|
+
* safeErrorMessage by the caller). */
|
|
25427
|
+
function authorizationFailureText(error, unknownErrorText) {
|
|
25428
|
+
switch (error?.code) {
|
|
25429
|
+
case "NO_FLOW": return "login method is no longer available";
|
|
25430
|
+
case "UNKNOWN_METHOD": return "selected login method is no longer available";
|
|
25431
|
+
case "ALREADY_IN_FLIGHT": return "sign-in already in progress";
|
|
25432
|
+
case "NOT_COMMITTED": return "sign-in finished without storing a credential";
|
|
25433
|
+
case "DECLINED": return "login cancelled";
|
|
25434
|
+
default: return `sign-in failed: ${unknownErrorText}`;
|
|
25435
|
+
}
|
|
25436
|
+
}
|
|
25240
25437
|
//#endregion
|
|
25241
25438
|
//#region src/skill-catalog.ts
|
|
25242
25439
|
/**
|
|
@@ -25579,6 +25776,9 @@ function presetDisplayText(preset) {
|
|
|
25579
25776
|
}
|
|
25580
25777
|
/** The sentinel picker value for the "add a brand-new provider" action row. */
|
|
25581
25778
|
const ADD_PROVIDER_VALUE = "\0add-provider";
|
|
25779
|
+
/** The sentinel prefix for an authorization-target picker row (a credential
|
|
25780
|
+
* key can never start with NUL, so route values cannot collide). */
|
|
25781
|
+
const AUTH_VALUE_PREFIX = "\0auth:";
|
|
25582
25782
|
/**
|
|
25583
25783
|
* M11: the /status extension-health rows (plan §16 — /status extension
|
|
25584
25784
|
* health). Reads the extension registries' live snapshots: contribution
|
|
@@ -25670,6 +25870,7 @@ function readProviderOptions(ctx) {
|
|
|
25670
25870
|
route: "deepseek-official",
|
|
25671
25871
|
configured: true,
|
|
25672
25872
|
declared: false,
|
|
25873
|
+
namesCredential: true,
|
|
25673
25874
|
group: "configured",
|
|
25674
25875
|
settingsNs: "",
|
|
25675
25876
|
settingsPath: []
|
|
@@ -25678,26 +25879,36 @@ function readProviderOptions(ctx) {
|
|
|
25678
25879
|
route: option.label,
|
|
25679
25880
|
configured: true,
|
|
25680
25881
|
declared: false,
|
|
25882
|
+
namesCredential: true,
|
|
25681
25883
|
group: "configured",
|
|
25682
25884
|
settingsNs: "llm-pi-ai",
|
|
25683
25885
|
settingsPath: ["providers", option.label]
|
|
25684
25886
|
});
|
|
25685
25887
|
}
|
|
25686
|
-
/** Build the picker rows
|
|
25687
|
-
* available
|
|
25688
|
-
*
|
|
25689
|
-
* Platform action row pinned last.
|
|
25690
|
-
|
|
25888
|
+
/** Build the merged /login picker rows: reference targets keep their
|
|
25889
|
+
* configured/available/custom groups (the fork renders a non-interactive
|
|
25890
|
+
* header row per group), authorization targets get their own group, and the
|
|
25891
|
+
* Add New Platform action row is pinned last. Authorization row values
|
|
25892
|
+
* carry the AUTH_VALUE_PREFIX so they can never collide with a route. */
|
|
25893
|
+
function mergedPickerRows(merged) {
|
|
25691
25894
|
const rows = [];
|
|
25692
25895
|
const groupLabels = {
|
|
25693
25896
|
configured: "configured",
|
|
25694
25897
|
available: "available · catalog",
|
|
25695
|
-
custom: "custom"
|
|
25898
|
+
custom: "custom",
|
|
25899
|
+
authorization: "sign in with provider"
|
|
25696
25900
|
};
|
|
25697
|
-
for (const
|
|
25698
|
-
|
|
25699
|
-
|
|
25700
|
-
|
|
25901
|
+
for (const target of merged) if (target.kind === "reference") {
|
|
25902
|
+
const group = target.configured ? "configured" : target.declared ? "custom" : "available";
|
|
25903
|
+
rows.push({
|
|
25904
|
+
value: target.route,
|
|
25905
|
+
label: `${target.label} (${target.ref})`,
|
|
25906
|
+
group: groupLabels[group]
|
|
25907
|
+
});
|
|
25908
|
+
} else rows.push({
|
|
25909
|
+
value: AUTH_VALUE_PREFIX + target.key,
|
|
25910
|
+
label: target.inFlight ? `${target.label} — sign-in in progress` : `${target.label} — sign in`,
|
|
25911
|
+
group: groupLabels.authorization
|
|
25701
25912
|
});
|
|
25702
25913
|
rows.push({
|
|
25703
25914
|
value: ADD_PROVIDER_VALUE,
|
|
@@ -25705,6 +25916,161 @@ function providerPickerRows(options) {
|
|
|
25705
25916
|
});
|
|
25706
25917
|
return rows;
|
|
25707
25918
|
}
|
|
25919
|
+
/** Recover an authorization target from a picked row value (the marker
|
|
25920
|
+
* prefix guarantees no collision with reference route values). */
|
|
25921
|
+
function targetFromPickerValue(merged, value) {
|
|
25922
|
+
if (!value.startsWith(AUTH_VALUE_PREFIX)) return void 0;
|
|
25923
|
+
const key = value.slice(6);
|
|
25924
|
+
for (const target of merged) if (target.kind === "authorization" && target.key === key) return target;
|
|
25925
|
+
}
|
|
25926
|
+
/** One-line summary of every merged target, for the unknown-target error. */
|
|
25927
|
+
function mergedTargetsSummary(merged) {
|
|
25928
|
+
return merged.map((target) => target.kind === "reference" ? `${target.label} (${target.ref})` : `${target.label} (provider sign-in)`).join(", ");
|
|
25929
|
+
}
|
|
25930
|
+
/**
|
|
25931
|
+
* Run one authorization attempt on the seam and report it. Method picking
|
|
25932
|
+
* (single method → direct; multiple → a picker), notice rendering and
|
|
25933
|
+
* prompts all live behind the interaction built here; the seam's stable
|
|
25934
|
+
* error taxonomy maps to user-facing copy (§15). On success, a catalog
|
|
25935
|
+
* route that is not configured yet gets a minimal keyless profile so the
|
|
25936
|
+
* runtime keeps reading the credential record (§12.1 — never an apiKeyEnv,
|
|
25937
|
+
* which would switch the request path back to a reference that is not set).
|
|
25938
|
+
*/
|
|
25939
|
+
async function runAuthorizationLogin(ctx, app, runner, target, options) {
|
|
25940
|
+
const authorization = ctx.get("authorization");
|
|
25941
|
+
if (authorization === void 0) return {
|
|
25942
|
+
kind: "error",
|
|
25943
|
+
text: "authorization service unavailable"
|
|
25944
|
+
};
|
|
25945
|
+
if (target.inFlight) return {
|
|
25946
|
+
kind: "error",
|
|
25947
|
+
text: `sign-in already in progress for ${target.label}`
|
|
25948
|
+
};
|
|
25949
|
+
let method = target.methods[0]?.id;
|
|
25950
|
+
if (method === void 0) return {
|
|
25951
|
+
kind: "error",
|
|
25952
|
+
text: `no sign-in method available for ${target.label}`
|
|
25953
|
+
};
|
|
25954
|
+
if (target.methods.length > 1) {
|
|
25955
|
+
const picked = await new Promise((resolve) => {
|
|
25956
|
+
app.openPicker(target.methods.map((candidate) => ({
|
|
25957
|
+
value: candidate.id,
|
|
25958
|
+
label: candidate.label
|
|
25959
|
+
})), (value) => resolve(value), () => resolve(void 0), {
|
|
25960
|
+
header: `Sign in method · ${target.label}`,
|
|
25961
|
+
enableSearch: false
|
|
25962
|
+
});
|
|
25963
|
+
});
|
|
25964
|
+
if (picked === void 0) return {
|
|
25965
|
+
kind: "error",
|
|
25966
|
+
text: "login cancelled"
|
|
25967
|
+
};
|
|
25968
|
+
method = picked;
|
|
25969
|
+
}
|
|
25970
|
+
const { interaction, close } = createAuthorizationInteraction(app);
|
|
25971
|
+
let outcome;
|
|
25972
|
+
try {
|
|
25973
|
+
outcome = await authorization.begin({
|
|
25974
|
+
key: target.key,
|
|
25975
|
+
method,
|
|
25976
|
+
interaction,
|
|
25977
|
+
signal: runner.signal
|
|
25978
|
+
});
|
|
25979
|
+
} catch (error) {
|
|
25980
|
+
if (runner.signal.aborted) return {
|
|
25981
|
+
kind: "error",
|
|
25982
|
+
text: "login cancelled"
|
|
25983
|
+
};
|
|
25984
|
+
if (error?.code === "NOT_COMMITTED") runner.diag.error("authorization", {
|
|
25985
|
+
key: target.key,
|
|
25986
|
+
error: safeErrorMessage(error)
|
|
25987
|
+
});
|
|
25988
|
+
return {
|
|
25989
|
+
kind: "error",
|
|
25990
|
+
text: authorizationFailureText(error, safeErrorMessage(error))
|
|
25991
|
+
};
|
|
25992
|
+
} finally {
|
|
25993
|
+
close();
|
|
25994
|
+
}
|
|
25995
|
+
if (outcome.status === "cancelled") return {
|
|
25996
|
+
kind: "error",
|
|
25997
|
+
text: "login cancelled"
|
|
25998
|
+
};
|
|
25999
|
+
const profileNote = await provisionKeylessProfile(ctx, runner, target, options);
|
|
26000
|
+
return {
|
|
26001
|
+
kind: "success",
|
|
26002
|
+
text: `signed in to ${target.label}${profileNote}`
|
|
26003
|
+
};
|
|
26004
|
+
}
|
|
26005
|
+
/**
|
|
26006
|
+
* After a successful authorization, write a MINIMAL keyless profile for a
|
|
26007
|
+
* catalog route that is not configured yet (§12.1). The record alone does
|
|
26008
|
+
* not make the route selectable — llm-pi-ai registers a route only when
|
|
26009
|
+
* its settings section names it — and the profile must NOT carry
|
|
26010
|
+
* apiKeyEnv, or the request path would switch back to a reference that was
|
|
26011
|
+
* never set. Hand-declared/custom routes are left to the add wizard
|
|
26012
|
+
* (§12.2). Any failure degrades silently (the sign-in itself succeeded).
|
|
26013
|
+
* @returns a user-facing note, or '' when nothing was provisioned.
|
|
26014
|
+
*/
|
|
26015
|
+
async function provisionKeylessProfile(ctx, runner, target, options) {
|
|
26016
|
+
if (target.route === void 0) return "";
|
|
26017
|
+
const option = options.find((candidate) => candidate.route === target.route);
|
|
26018
|
+
if (option === void 0 || option.configured || option.declared || option.settingsNs === "") return "";
|
|
26019
|
+
const settings = ctx.get("settings");
|
|
26020
|
+
if (settings === void 0) return "";
|
|
26021
|
+
try {
|
|
26022
|
+
await settings.mutate(option.settingsNs, [{
|
|
26023
|
+
op: "set",
|
|
26024
|
+
path: [...option.settingsPath],
|
|
26025
|
+
value: {}
|
|
26026
|
+
}]);
|
|
26027
|
+
return " — provider profile recorded";
|
|
26028
|
+
} catch (error) {
|
|
26029
|
+
runner.diag.warn("authorization", {
|
|
26030
|
+
key: target.key,
|
|
26031
|
+
note: "profile write failed",
|
|
26032
|
+
error: safeErrorMessage(error)
|
|
26033
|
+
});
|
|
26034
|
+
return "";
|
|
26035
|
+
}
|
|
26036
|
+
}
|
|
26037
|
+
/** Value prefixes for the /logout picker rows (no collision with a ref). */
|
|
26038
|
+
const LOGOUT_REF_VALUE = "\0ref:";
|
|
26039
|
+
const LOGOUT_RECORD_VALUE = "\0record:";
|
|
26040
|
+
/** Build the /logout picker rows: every stored credential record plus every
|
|
26041
|
+
* configured reference (presence only — a secret's value never leaves the
|
|
26042
|
+
* credentials service). Records are deduplicated by key and labelled with
|
|
26043
|
+
* the authorization flow's user-facing name when one owns the key (a
|
|
26044
|
+
* record row must say what signing out actually clears). */
|
|
26045
|
+
async function logoutPickerRows(credentials, options, targets) {
|
|
26046
|
+
const rows = [];
|
|
26047
|
+
const seenRefs = /* @__PURE__ */ new Set();
|
|
26048
|
+
for (const option of options) {
|
|
26049
|
+
if (seenRefs.has(option.ref)) continue;
|
|
26050
|
+
seenRefs.add(option.ref);
|
|
26051
|
+
try {
|
|
26052
|
+
if ((await credentials.describe(option.ref)).configured) rows.push({
|
|
26053
|
+
value: LOGOUT_REF_VALUE + option.ref,
|
|
26054
|
+
label: `${option.label} (${option.ref})`,
|
|
26055
|
+
group: "API keys"
|
|
26056
|
+
});
|
|
26057
|
+
} catch {}
|
|
26058
|
+
}
|
|
26059
|
+
const records = await credentials.listRecords();
|
|
26060
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
26061
|
+
for (const record of records) {
|
|
26062
|
+
if (seenKeys.has(record.key)) continue;
|
|
26063
|
+
seenKeys.add(record.key);
|
|
26064
|
+
const owner = targets.find((target) => target.key === record.key);
|
|
26065
|
+
const label = owner !== void 0 ? `${owner.label} — stored credential${record.kind === void 0 ? "" : ` (${record.kind})`}` : `${record.key}${record.kind === void 0 ? "" : ` (${record.kind})`}`;
|
|
26066
|
+
rows.push({
|
|
26067
|
+
value: LOGOUT_RECORD_VALUE + record.key,
|
|
26068
|
+
label,
|
|
26069
|
+
group: "stored credentials"
|
|
26070
|
+
});
|
|
26071
|
+
}
|
|
26072
|
+
return rows;
|
|
26073
|
+
}
|
|
25708
26074
|
/** Run the add-provider wizard: collect route/api/baseURL/displayName/key
|
|
25709
26075
|
* through question flows, probe the endpoint for its models (falling back to
|
|
25710
26076
|
* hand entry), review, then persist the profile + credential.
|
|
@@ -27126,7 +27492,7 @@ function registerTuiCommands(runner, initial) {
|
|
|
27126
27492
|
});
|
|
27127
27493
|
commands.register({
|
|
27128
27494
|
name: "login",
|
|
27129
|
-
description: "
|
|
27495
|
+
description: "Sign in — deepseek official or an llm-pi-ai provider route (API key or provider-native flow)",
|
|
27130
27496
|
input: { hint: "[<route|env-var>]" },
|
|
27131
27497
|
handler: async (invocation) => {
|
|
27132
27498
|
const credentials = ctx.get("credentials");
|
|
@@ -27134,39 +27500,51 @@ function registerTuiCommands(runner, initial) {
|
|
|
27134
27500
|
kind: "error",
|
|
27135
27501
|
text: "credentials service unavailable"
|
|
27136
27502
|
};
|
|
27503
|
+
const authorization = ctx.get("authorization");
|
|
27137
27504
|
const options = readProviderOptions(ctx);
|
|
27505
|
+
const targets = authorizationTargets(authorization?.list() ?? []);
|
|
27506
|
+
const merged = mergeLoginTargets(options, targets);
|
|
27138
27507
|
const arg = invocation.rawInput.trim();
|
|
27139
27508
|
let route;
|
|
27140
27509
|
let ref;
|
|
27510
|
+
let target;
|
|
27141
27511
|
if (arg !== "") {
|
|
27142
27512
|
const known = resolveCredentialArg(arg, options);
|
|
27143
27513
|
if (known !== void 0) {
|
|
27144
27514
|
const option = options.find((candidate) => candidate.ref === known);
|
|
27145
27515
|
if (option !== void 0) {
|
|
27146
|
-
|
|
27147
|
-
|
|
27516
|
+
const flow = option.route !== "deepseek-official" ? flowForRoute(targets, option.route) : void 0;
|
|
27517
|
+
if (flow !== void 0 && option.namesCredential === false) target = flow;
|
|
27518
|
+
else {
|
|
27519
|
+
route = option.route;
|
|
27520
|
+
ref = option.ref;
|
|
27521
|
+
}
|
|
27148
27522
|
} else ref = known;
|
|
27149
27523
|
} else if (ROUTE_PATTERN.test(arg)) {
|
|
27150
|
-
const
|
|
27151
|
-
if (
|
|
27152
|
-
|
|
27153
|
-
|
|
27154
|
-
|
|
27155
|
-
|
|
27156
|
-
|
|
27157
|
-
|
|
27158
|
-
|
|
27159
|
-
|
|
27160
|
-
|
|
27161
|
-
|
|
27162
|
-
|
|
27524
|
+
const flow = flowForRoute(targets, arg);
|
|
27525
|
+
if (flow !== void 0) target = flow;
|
|
27526
|
+
else {
|
|
27527
|
+
const outcome = await askAddProvider(ctx, app, runner.signal, arg);
|
|
27528
|
+
if (outcome.kind === "cancelled") return {
|
|
27529
|
+
kind: "error",
|
|
27530
|
+
text: "add provider cancelled"
|
|
27531
|
+
};
|
|
27532
|
+
if (outcome.kind === "error") return {
|
|
27533
|
+
kind: "error",
|
|
27534
|
+
text: outcome.text
|
|
27535
|
+
};
|
|
27536
|
+
return {
|
|
27537
|
+
kind: "success",
|
|
27538
|
+
text: outcome.text
|
|
27539
|
+
};
|
|
27540
|
+
}
|
|
27163
27541
|
} else return {
|
|
27164
27542
|
kind: "error",
|
|
27165
|
-
text: `unknown credential target "${arg}" — ${
|
|
27543
|
+
text: `unknown credential target "${arg}" — ${mergedTargetsSummary(merged)}`
|
|
27166
27544
|
};
|
|
27167
|
-
} else if (
|
|
27545
|
+
} else if (merged.length > 1) {
|
|
27168
27546
|
const picked = await new Promise((resolve) => {
|
|
27169
|
-
app.openPicker(
|
|
27547
|
+
app.openPicker(mergedPickerRows(merged), (value) => resolve(value), () => resolve(void 0), {
|
|
27170
27548
|
enableSearch: true,
|
|
27171
27549
|
header: "login · providers",
|
|
27172
27550
|
noMatchText: " no matching providers",
|
|
@@ -27194,8 +27572,22 @@ function registerTuiCommands(runner, initial) {
|
|
|
27194
27572
|
text: outcome.text
|
|
27195
27573
|
};
|
|
27196
27574
|
}
|
|
27197
|
-
|
|
27198
|
-
|
|
27575
|
+
const authTarget = targetFromPickerValue(merged, picked);
|
|
27576
|
+
if (authTarget !== void 0) target = authTarget;
|
|
27577
|
+
else route = picked;
|
|
27578
|
+
} else {
|
|
27579
|
+
const only = merged[0];
|
|
27580
|
+
if (only === void 0) return {
|
|
27581
|
+
kind: "error",
|
|
27582
|
+
text: "no credential targets available"
|
|
27583
|
+
};
|
|
27584
|
+
if (only.kind === "authorization") target = only;
|
|
27585
|
+
else {
|
|
27586
|
+
route = only.route;
|
|
27587
|
+
ref = only.ref;
|
|
27588
|
+
}
|
|
27589
|
+
}
|
|
27590
|
+
if (target !== void 0) return runAuthorizationLogin(ctx, app, runner, target, options);
|
|
27199
27591
|
if (route === void 0 && ref === void 0) return {
|
|
27200
27592
|
kind: "error",
|
|
27201
27593
|
text: "no credential targets available"
|
|
@@ -27206,7 +27598,8 @@ function registerTuiCommands(runner, initial) {
|
|
|
27206
27598
|
try {
|
|
27207
27599
|
const key = (await app.askQuestions([{
|
|
27208
27600
|
id: "key",
|
|
27209
|
-
question: `Paste the API key for ${label}
|
|
27601
|
+
question: `Paste the API key for ${label}:`,
|
|
27602
|
+
masked: true
|
|
27210
27603
|
}]))[0]?.custom ?? "";
|
|
27211
27604
|
if (key === "") return {
|
|
27212
27605
|
kind: "error",
|
|
@@ -27227,7 +27620,7 @@ function registerTuiCommands(runner, initial) {
|
|
|
27227
27620
|
});
|
|
27228
27621
|
commands.register({
|
|
27229
27622
|
name: "logout",
|
|
27230
|
-
description: "Clear a stored
|
|
27623
|
+
description: "Clear a stored credential — deepseek official or an llm-pi-ai provider route (API key or stored record)",
|
|
27231
27624
|
input: { hint: "[<route|env-var>]" },
|
|
27232
27625
|
handler: async (invocation) => {
|
|
27233
27626
|
const credentials = ctx.get("credentials");
|
|
@@ -27235,24 +27628,72 @@ function registerTuiCommands(runner, initial) {
|
|
|
27235
27628
|
kind: "error",
|
|
27236
27629
|
text: "credentials service unavailable"
|
|
27237
27630
|
};
|
|
27631
|
+
const authorization = ctx.get("authorization");
|
|
27238
27632
|
const options = readProviderOptions(ctx);
|
|
27633
|
+
const targets = authorizationTargets(authorization?.list() ?? []);
|
|
27239
27634
|
const arg = invocation.rawInput.trim();
|
|
27240
27635
|
if (arg !== "") {
|
|
27241
27636
|
const resolved = resolveCredentialArg(arg, options);
|
|
27242
|
-
if (resolved
|
|
27637
|
+
if (resolved !== void 0) {
|
|
27638
|
+
const option = options.find((candidate) => candidate.ref === resolved);
|
|
27639
|
+
const flow = option !== void 0 && option.route !== "deepseek-official" && !option.namesCredential ? flowForRoute(targets, option.route) : void 0;
|
|
27640
|
+
if (flow !== void 0) {
|
|
27641
|
+
await credentials.deleteRecord(flow.key);
|
|
27642
|
+
return {
|
|
27643
|
+
kind: "success",
|
|
27644
|
+
text: `${flow.label} signed out locally — stored credential cleared`
|
|
27645
|
+
};
|
|
27646
|
+
}
|
|
27647
|
+
await credentials.unset(resolved);
|
|
27648
|
+
return {
|
|
27649
|
+
kind: "success",
|
|
27650
|
+
text: `${resolved} cleared`
|
|
27651
|
+
};
|
|
27652
|
+
}
|
|
27653
|
+
const flow = flowForRoute(targets, arg.toLowerCase());
|
|
27654
|
+
if (flow !== void 0) {
|
|
27655
|
+
await credentials.deleteRecord(flow.key);
|
|
27656
|
+
return {
|
|
27657
|
+
kind: "success",
|
|
27658
|
+
text: `${flow.label} signed out locally — stored credential cleared`
|
|
27659
|
+
};
|
|
27660
|
+
}
|
|
27661
|
+
return {
|
|
27243
27662
|
kind: "error",
|
|
27244
|
-
text: `unknown credential target "${arg}" — ${
|
|
27663
|
+
text: `unknown credential target "${arg}" — ${mergedTargetsSummary(mergeLoginTargets(options, targets))}`
|
|
27245
27664
|
};
|
|
27246
|
-
|
|
27665
|
+
}
|
|
27666
|
+
const rows = await logoutPickerRows(credentials, options, targets);
|
|
27667
|
+
if (rows.length === 0) return {
|
|
27668
|
+
kind: "error",
|
|
27669
|
+
text: "nothing to sign out"
|
|
27670
|
+
};
|
|
27671
|
+
const picked = await new Promise((resolve) => {
|
|
27672
|
+
app.openPicker(rows, (value) => resolve(value), () => resolve(void 0), {
|
|
27673
|
+
enableSearch: true,
|
|
27674
|
+
header: "logout · credentials",
|
|
27675
|
+
noMatchText: " nothing to sign out",
|
|
27676
|
+
width: 76,
|
|
27677
|
+
maxHeight: 26,
|
|
27678
|
+
showHint: true
|
|
27679
|
+
});
|
|
27680
|
+
});
|
|
27681
|
+
if (picked === void 0) return {
|
|
27682
|
+
kind: "error",
|
|
27683
|
+
text: "logout cancelled"
|
|
27684
|
+
};
|
|
27685
|
+
if (picked.startsWith(LOGOUT_RECORD_VALUE)) {
|
|
27686
|
+
await credentials.deleteRecord(picked.slice(8));
|
|
27247
27687
|
return {
|
|
27248
27688
|
kind: "success",
|
|
27249
|
-
text: `${
|
|
27689
|
+
text: `${picked.slice(8)} signed out locally — stored credential cleared`
|
|
27250
27690
|
};
|
|
27251
27691
|
}
|
|
27252
|
-
|
|
27692
|
+
const targetRef = picked.slice(5);
|
|
27693
|
+
await credentials.unset(targetRef);
|
|
27253
27694
|
return {
|
|
27254
27695
|
kind: "success",
|
|
27255
|
-
text: `${
|
|
27696
|
+
text: `${targetRef} cleared`
|
|
27256
27697
|
};
|
|
27257
27698
|
}
|
|
27258
27699
|
});
|
|
@@ -27483,6 +27924,33 @@ function appendHistoryLine(file, text, lastContent) {
|
|
|
27483
27924
|
return true;
|
|
27484
27925
|
}
|
|
27485
27926
|
//#endregion
|
|
27927
|
+
//#region src/dsh-version.ts
|
|
27928
|
+
/**
|
|
27929
|
+
* The installed dsh (DeepSeek Harness CLI) version, resolved from the
|
|
27930
|
+
* launcher's real path: `process.argv[1]` is the `dsh` bin, whose realpath
|
|
27931
|
+
* walks up to the `@deepseek-ai/dsh/package.json` that owns it. The header
|
|
27932
|
+
* and the welcome card show the harness the TUI runs on, not this bundle's
|
|
27933
|
+
* own patch level. Undefined when the launcher path is unreadable.
|
|
27934
|
+
* @module @xmoon76/dsh-pi-tui/dsh-version
|
|
27935
|
+
*/
|
|
27936
|
+
/** @returns the installed dsh version string, or undefined. */
|
|
27937
|
+
function dshVersion() {
|
|
27938
|
+
const bin = process.argv[1];
|
|
27939
|
+
if (bin === void 0) return void 0;
|
|
27940
|
+
try {
|
|
27941
|
+
let dir = dirname(realpathSync(bin));
|
|
27942
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
27943
|
+
try {
|
|
27944
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
27945
|
+
if (pkg.name === "@deepseek-ai/dsh" && typeof pkg.version === "string") return pkg.version;
|
|
27946
|
+
} catch {}
|
|
27947
|
+
const parent = dirname(dir);
|
|
27948
|
+
if (parent === dir) return void 0;
|
|
27949
|
+
dir = parent;
|
|
27950
|
+
}
|
|
27951
|
+
} catch {}
|
|
27952
|
+
}
|
|
27953
|
+
//#endregion
|
|
27486
27954
|
//#region src/exit.ts
|
|
27487
27955
|
/**
|
|
27488
27956
|
* The TUI exit contract: flush the live session with a HARD timeout, record
|
|
@@ -28741,6 +29209,19 @@ const inject = [
|
|
|
28741
29209
|
TUI_STARTUP_SERVICE
|
|
28742
29210
|
];
|
|
28743
29211
|
const Config = z.object({ sessionId: z.string() });
|
|
29212
|
+
/**
|
|
29213
|
+
* Wire the credential surface refresh to the dsh 0.1.1-rc.1 credential
|
|
29214
|
+
* events. The rc.1 release split the credential update event into the
|
|
29215
|
+
* reference half and the durable-record half; both change what the footer
|
|
29216
|
+
* model row and the welcome card show (a /login /logout, an external
|
|
29217
|
+
* .credentials.yaml edit, or an authorization flow committing a record), so
|
|
29218
|
+
* they share one best-effort callback. The callback must not read secrets
|
|
29219
|
+
* or mutate providers — it only re-reads describe-level state.
|
|
29220
|
+
*/
|
|
29221
|
+
function registerCredentialSurfaceRefresh(ctx, refresh) {
|
|
29222
|
+
ctx.on("credentials/reference-updated", refresh);
|
|
29223
|
+
ctx.on("credentials/record-updated", refresh);
|
|
29224
|
+
}
|
|
28744
29225
|
/** Display window in turns; older turns collapse into a summary entry. */
|
|
28745
29226
|
const WINDOW_TURNS = 15;
|
|
28746
29227
|
/** Coalesced repaint interval for streaming events, in ms. */
|
|
@@ -29034,33 +29515,10 @@ function foldQueueRows(messages, mode, notified) {
|
|
|
29034
29515
|
};
|
|
29035
29516
|
}
|
|
29036
29517
|
/**
|
|
29037
|
-
* The installed dsh version (e.g. `0.1.0-rc.6`), resolved from the launcher's
|
|
29038
|
-
* real path: `process.argv[1]` is the `dsh` bin, whose realpath walks up to
|
|
29039
|
-
* the `@deepseek-ai/dsh/package.json` that owns it. The version the welcome
|
|
29040
|
-
* card shows is the harness the TUI runs on, not this bundle's own patch
|
|
29041
|
-
* level. Undefined when the launcher path is unreadable.
|
|
29042
|
-
* @returns the installed dsh version string, or undefined.
|
|
29043
|
-
*/
|
|
29044
|
-
function dshVersion() {
|
|
29045
|
-
const bin = process.argv[1];
|
|
29046
|
-
if (bin === void 0) return void 0;
|
|
29047
|
-
try {
|
|
29048
|
-
let dir = dirname(realpathSync(bin));
|
|
29049
|
-
for (let depth = 0; depth < 8; depth += 1) {
|
|
29050
|
-
try {
|
|
29051
|
-
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
29052
|
-
if (pkg.name === "@deepseek-ai/dsh" && typeof pkg.version === "string") return pkg.version;
|
|
29053
|
-
} catch {}
|
|
29054
|
-
const parent = dirname(dir);
|
|
29055
|
-
if (parent === dir) return void 0;
|
|
29056
|
-
dir = parent;
|
|
29057
|
-
}
|
|
29058
|
-
} catch {}
|
|
29059
|
-
}
|
|
29060
|
-
/**
|
|
29061
29518
|
* The bundle's own version, read from package.json at runtime so the welcome
|
|
29062
29519
|
* card never drifts from the shipped version. The DISPLAYED version prefers
|
|
29063
|
-
* the installed dsh version (`dshVersion`
|
|
29520
|
+
* the installed dsh version (`dshVersion` — shared with the header badge
|
|
29521
|
+
* via src/dsh-version.ts), falling back to this one.
|
|
29064
29522
|
* @returns the version string, or a fallback when the file is unreadable.
|
|
29065
29523
|
*/
|
|
29066
29524
|
function packageVersion() {
|
|
@@ -31313,10 +31771,11 @@ function apply(ctx, config) {
|
|
|
31313
31771
|
updateWelcomeCard();
|
|
31314
31772
|
}
|
|
31315
31773
|
});
|
|
31316
|
-
|
|
31774
|
+
const refreshCredentialSurface = () => {
|
|
31317
31775
|
refreshStatus();
|
|
31318
31776
|
updateWelcomeCard();
|
|
31319
|
-
}
|
|
31777
|
+
};
|
|
31778
|
+
registerCredentialSurfaceRefresh(ctx, refreshCredentialSurface);
|
|
31320
31779
|
if (liveAgent !== void 0) {
|
|
31321
31780
|
app.setPlanMode(foldPlanMode(liveAgent.session.events));
|
|
31322
31781
|
app.setWorking(workingFromLog(liveAgent.session.events));
|
|
@@ -31384,4 +31843,4 @@ function apply(ctx, config) {
|
|
|
31384
31843
|
});
|
|
31385
31844
|
}
|
|
31386
31845
|
//#endregion
|
|
31387
|
-
export {
|
|
31846
|
+
export { dshVersion as A, resumeCommand as C, subagentJobTranscriptId as D, shouldSteerOnEnter as E, PI_TUI_EXTENSIONS_SERVICE as F, API_VERSION as I, describeKey as L, apply$1 as M, inject$1 as N, subagentJobViewHint as O, name$1 as P, resolveInitialCatalog as S, shouldConsumeAdvertisedMiss as T, matchPendingSubagentCall as _, SessionLockRefusedError as a, recordedPreset as b, compactingFromLog as c, foldCompactionEvent as d, foldQueueRows as f, isUserQueueInput as g, isSubagentSettlementNotice as h, SESSIONLESS_COMMANDS as i, forkSeed as j, subagentNoticeIsFailure as k, composeAgent as l, isPlainExitPrompt as m, HOST_COMMAND_CATALOG as n, apply as o, inject as p, LOCAL_COMMANDS as r, busyAfterTurnBoundary as s, Config as t, dangerCommand as u, name as v, runningProfile as w, registerCredentialSurfaceRefresh as x, recomposeBlank as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmoon76/dsh-pi-tui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "The dsh-pi-tui bundle: a TUI surface for DeepSeek Harness profiles (dsh --profile pi-tui)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "XMoon <xmoonox@gmail.com>",
|
|
@@ -77,72 +77,74 @@
|
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
79
79
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.2",
|
|
80
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
81
|
-
"@deepseek-ai/dsh-agent-default-model": "^0.1.
|
|
82
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.
|
|
83
|
-
"@deepseek-ai/dsh-
|
|
84
|
-
"@deepseek-ai/dsh-
|
|
85
|
-
"@deepseek-ai/dsh-
|
|
86
|
-
"@deepseek-ai/dsh-
|
|
87
|
-
"@deepseek-ai/dsh-
|
|
88
|
-
"@deepseek-ai/dsh-
|
|
89
|
-
"@deepseek-ai/dsh-llm
|
|
90
|
-
"@deepseek-ai/dsh-
|
|
91
|
-
"@deepseek-ai/dsh-
|
|
92
|
-
"@deepseek-ai/dsh-
|
|
93
|
-
"@deepseek-ai/dsh-
|
|
94
|
-
"@deepseek-ai/dsh-session
|
|
95
|
-
"@deepseek-ai/dsh-session-
|
|
96
|
-
"@deepseek-ai/dsh-
|
|
97
|
-
"@deepseek-ai/dsh-
|
|
98
|
-
"@deepseek-ai/dsh-
|
|
99
|
-
"@deepseek-ai/dsh-
|
|
100
|
-
"@deepseek-ai/dsh-
|
|
101
|
-
"@deepseek-ai/dsh-
|
|
102
|
-
"@deepseek-ai/dsh-
|
|
103
|
-
"@deepseek-ai/dsh-
|
|
104
|
-
"@deepseek-ai/dsh-user-
|
|
80
|
+
"@deepseek-ai/dsh-agent": "^0.1.1-rc.1",
|
|
81
|
+
"@deepseek-ai/dsh-agent-default-model": "^0.1.1-rc.1",
|
|
82
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.1-rc.1",
|
|
83
|
+
"@deepseek-ai/dsh-authorization": "^0.1.1-rc.1",
|
|
84
|
+
"@deepseek-ai/dsh-cmdline": "^0.1.1-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-commands": "^0.1.1-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-credentials": "^0.1.1-rc.1",
|
|
87
|
+
"@deepseek-ai/dsh-goal": "^0.1.1-rc.1",
|
|
88
|
+
"@deepseek-ai/dsh-jobs": "^0.1.1-rc.1",
|
|
89
|
+
"@deepseek-ai/dsh-llm": "^0.1.1-rc.1",
|
|
90
|
+
"@deepseek-ai/dsh-llm-retry": "^0.1.1-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-permission-presets": "^0.1.1-rc.1",
|
|
92
|
+
"@deepseek-ai/dsh-plan-mode": "^0.1.1-rc.1",
|
|
93
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.1-rc.1",
|
|
94
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.1",
|
|
95
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.1-rc.1",
|
|
96
|
+
"@deepseek-ai/dsh-session-title": "^0.1.1-rc.1",
|
|
97
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.1",
|
|
98
|
+
"@deepseek-ai/dsh-shell": "^0.1.1-rc.1",
|
|
99
|
+
"@deepseek-ai/dsh-skill": "^0.1.1-rc.1",
|
|
100
|
+
"@deepseek-ai/dsh-subagent": "^0.1.1-rc.1",
|
|
101
|
+
"@deepseek-ai/dsh-token-meter": "^0.1.1-rc.1",
|
|
102
|
+
"@deepseek-ai/dsh-tool-workflow": "^0.1.1-rc.1",
|
|
103
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.1",
|
|
104
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.1-rc.1",
|
|
105
|
+
"@deepseek-ai/dsh-user-questions": "^0.1.1-rc.1",
|
|
105
106
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
106
107
|
},
|
|
107
108
|
"devDependencies": {
|
|
108
|
-
"@xmoon76/pi-tui": "^0.84.3",
|
|
109
109
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.2",
|
|
110
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
111
|
-
"@deepseek-ai/dsh-agent-default-model": "^0.1.
|
|
112
|
-
"@deepseek-ai/dsh-
|
|
113
|
-
"@deepseek-ai/dsh-
|
|
114
|
-
"@deepseek-ai/dsh-
|
|
110
|
+
"@deepseek-ai/dsh-agent": "^0.1.1-rc.1",
|
|
111
|
+
"@deepseek-ai/dsh-agent-default-model": "^0.1.1-rc.1",
|
|
112
|
+
"@deepseek-ai/dsh-agent-loop": "^0.1.1-rc.1",
|
|
113
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.1-rc.1",
|
|
114
|
+
"@deepseek-ai/dsh-attachment": "^0.1.1-rc.1",
|
|
115
|
+
"@deepseek-ai/dsh-authorization": "^0.1.1-rc.1",
|
|
116
|
+
"@deepseek-ai/dsh-brand": "^0.1.1-rc.1",
|
|
117
|
+
"@deepseek-ai/dsh-commands": "^0.1.1-rc.1",
|
|
118
|
+
"@deepseek-ai/dsh-credentials": "^0.1.1-rc.1",
|
|
119
|
+
"@deepseek-ai/dsh-goal": "^0.1.1-rc.1",
|
|
120
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.1",
|
|
121
|
+
"@deepseek-ai/dsh-jobs": "^0.1.1-rc.1",
|
|
122
|
+
"@deepseek-ai/dsh-llm": "^0.1.1-rc.1",
|
|
123
|
+
"@deepseek-ai/dsh-llm-retry": "^0.1.1-rc.1",
|
|
124
|
+
"@deepseek-ai/dsh-permission-presets": "^0.1.1-rc.1",
|
|
125
|
+
"@deepseek-ai/dsh-plan-mode": "^0.1.1-rc.1",
|
|
126
|
+
"@deepseek-ai/dsh-scope": "^0.1.1-rc.1",
|
|
127
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.1",
|
|
128
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.1-rc.1",
|
|
129
|
+
"@deepseek-ai/dsh-session-title": "^0.1.1-rc.1",
|
|
130
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.1",
|
|
131
|
+
"@deepseek-ai/dsh-shell": "^0.1.1-rc.1",
|
|
132
|
+
"@deepseek-ai/dsh-skill": "^0.1.1-rc.1",
|
|
133
|
+
"@deepseek-ai/dsh-subagent": "^0.1.1-rc.1",
|
|
134
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.1-rc.1",
|
|
135
|
+
"@deepseek-ai/dsh-timeout": "^0.1.1-rc.1",
|
|
136
|
+
"@deepseek-ai/dsh-token-meter": "^0.1.1-rc.1",
|
|
137
|
+
"@deepseek-ai/dsh-tool-workflow": "^0.1.1-rc.1",
|
|
138
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.1",
|
|
139
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.1-rc.1",
|
|
140
|
+
"@deepseek-ai/dsh-user-questions": "^0.1.1-rc.1",
|
|
115
141
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
116
|
-
"@xterm/headless": "5.5.0",
|
|
117
142
|
"@types/node": "^22.0.0",
|
|
118
|
-
"
|
|
143
|
+
"@xmoon76/pi-tui": "^0.84.3",
|
|
144
|
+
"@xterm/headless": "5.5.0",
|
|
119
145
|
"tsdown": "0.22.0",
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"@deepseek-ai/dsh-skill": "^0.1.0-rc.8",
|
|
123
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
|
|
124
|
-
"@deepseek-ai/dsh-plan-mode": "^0.1.0-rc.8",
|
|
125
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.8",
|
|
126
|
-
"@deepseek-ai/dsh-user-questions": "^0.1.0-rc.8",
|
|
127
|
-
"@deepseek-ai/dsh-subagent": "^0.1.0-rc.8",
|
|
128
|
-
"@deepseek-ai/dsh-goal": "^0.1.0-rc.8",
|
|
129
|
-
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
|
|
130
|
-
"@deepseek-ai/dsh-tool-workflow": "^0.1.0-rc.8",
|
|
131
|
-
"@deepseek-ai/dsh-llm-retry": "^0.1.0-rc.8",
|
|
132
|
-
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.8",
|
|
133
|
-
"@deepseek-ai/dsh-session-title": "^0.1.0-rc.8",
|
|
134
|
-
"@deepseek-ai/dsh-jobs": "^0.1.0-rc.8",
|
|
135
|
-
"@deepseek-ai/dsh-shell": "^0.1.0-rc.8",
|
|
136
|
-
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
|
|
137
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.8",
|
|
138
|
-
"@deepseek-ai/dsh-agent-loop": "^0.1.0-rc.8",
|
|
139
|
-
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
|
|
140
|
-
"@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
|
|
141
|
-
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.8",
|
|
142
|
-
"@deepseek-ai/dsh-brand": "^0.1.0-rc.8",
|
|
143
|
-
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
|
|
144
|
-
"@deepseek-ai/dsh-timeout": "^0.1.0-rc.8",
|
|
145
|
-
"tsx": "^4.23.12"
|
|
146
|
+
"tsx": "^4.23.12",
|
|
147
|
+
"typescript": "^5.9.0"
|
|
146
148
|
},
|
|
147
149
|
"engines": {
|
|
148
150
|
"node": "^22.19.0 || >=24"
|