@xiaoso/dsh-tool-plus 0.1.8-beta.2 → 0.1.8-beta.3
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/lib/client.js +193 -33
- package/lib/types/web/client/labels.d.ts +1 -1
- package/lib/types/web/client/rows/command-card-facts.d.ts +45 -0
- package/lib/types/web/client/rows/read-card-facts.d.ts +33 -0
- package/lib/types/web/client/rows/search-card-facts.d.ts +37 -0
- package/package.json +1 -1
- package/presets/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1720,13 +1720,13 @@ window.__ModuleLoader__.load({
|
|
|
1720
1720
|
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
1721
1721
|
});
|
|
1722
1722
|
}
|
|
1723
|
-
function isRecord$
|
|
1723
|
+
function isRecord$4(value) {
|
|
1724
1724
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1725
1725
|
}
|
|
1726
1726
|
function parseConnectionResponse(value) {
|
|
1727
|
-
if (!isRecord$
|
|
1727
|
+
if (!isRecord$4(value) || value.type !== "server-response" || typeof value.rpcId !== "string") throw new TypeError("connection: invalid server-response envelope");
|
|
1728
1728
|
const result = value.result;
|
|
1729
|
-
if (!isRecord$
|
|
1729
|
+
if (!isRecord$4(result)) throw new TypeError("connection: invalid server-response result");
|
|
1730
1730
|
if (result.ok === true) return {
|
|
1731
1731
|
rpcId: value.rpcId,
|
|
1732
1732
|
result: {
|
|
@@ -1734,9 +1734,9 @@ window.__ModuleLoader__.load({
|
|
|
1734
1734
|
value: result.value
|
|
1735
1735
|
}
|
|
1736
1736
|
};
|
|
1737
|
-
if (result.ok !== false || !isRecord$
|
|
1737
|
+
if (result.ok !== false || !isRecord$4(result.error)) throw new TypeError("connection: invalid server-response result");
|
|
1738
1738
|
const error = result.error;
|
|
1739
|
-
if (typeof error.code !== "string" || typeof error.message !== "string" || !isRecord$
|
|
1739
|
+
if (typeof error.code !== "string" || typeof error.message !== "string" || !isRecord$4(error.details)) throw new TypeError("connection: invalid server-response failure");
|
|
1740
1740
|
return {
|
|
1741
1741
|
rpcId: value.rpcId,
|
|
1742
1742
|
result: {
|
|
@@ -3085,7 +3085,7 @@ window.__ModuleLoader__.load({
|
|
|
3085
3085
|
return json === void 0 ? 0 : utf8ByteLength(json);
|
|
3086
3086
|
}
|
|
3087
3087
|
/** Whether `value` is a plain object (never an array or `null`). */
|
|
3088
|
-
function isRecord$
|
|
3088
|
+
function isRecord$3(value) {
|
|
3089
3089
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3090
3090
|
}
|
|
3091
3091
|
/** Non-blank string, or `null`. */
|
|
@@ -3110,7 +3110,7 @@ window.__ModuleLoader__.load({
|
|
|
3110
3110
|
* @returns the narrowed metadata, or `null` when it is not a usable write card.
|
|
3111
3111
|
*/
|
|
3112
3112
|
function narrowWriteCardMeta(meta) {
|
|
3113
|
-
if (!isRecord$
|
|
3113
|
+
if (!isRecord$3(meta) || meta.kind !== "write") return null;
|
|
3114
3114
|
const path = nonBlankString(meta.path);
|
|
3115
3115
|
if (path === null) return null;
|
|
3116
3116
|
return {
|
|
@@ -3130,13 +3130,13 @@ window.__ModuleLoader__.load({
|
|
|
3130
3130
|
* @returns the narrowed metadata, or `null` when it is not a usable diff card.
|
|
3131
3131
|
*/
|
|
3132
3132
|
function narrowEditCardMeta(meta) {
|
|
3133
|
-
if (!isRecord$
|
|
3133
|
+
if (!isRecord$3(meta) || meta.kind !== "edit") return null;
|
|
3134
3134
|
if (jsonByteLength(meta) > 131072) return null;
|
|
3135
3135
|
const raw = meta.diffs;
|
|
3136
3136
|
if (!Array.isArray(raw) || raw.length === 0) return null;
|
|
3137
3137
|
const diffs = [];
|
|
3138
3138
|
for (const hunk of raw) {
|
|
3139
|
-
if (!isRecord$
|
|
3139
|
+
if (!isRecord$3(hunk)) return null;
|
|
3140
3140
|
const path = nonBlankString(hunk.path);
|
|
3141
3141
|
if (path === null) return null;
|
|
3142
3142
|
const { oldText, newText } = hunk;
|
|
@@ -3159,7 +3159,7 @@ window.__ModuleLoader__.load({
|
|
|
3159
3159
|
* @returns the narrowed metadata, or `null` when it is not a usable preview.
|
|
3160
3160
|
*/
|
|
3161
3161
|
function narrowAstEditCardMeta(meta) {
|
|
3162
|
-
if (!isRecord$
|
|
3162
|
+
if (!isRecord$3(meta) || meta.kind !== "ast_edit") return null;
|
|
3163
3163
|
if (jsonByteLength(meta) > 65536) return null;
|
|
3164
3164
|
if (typeof meta.preview !== "string") return null;
|
|
3165
3165
|
const replacements = nonNegativeInteger(meta.replacements);
|
|
@@ -3167,7 +3167,7 @@ window.__ModuleLoader__.load({
|
|
|
3167
3167
|
if (!Array.isArray(meta.files)) return null;
|
|
3168
3168
|
const files = [];
|
|
3169
3169
|
for (const file of meta.files) {
|
|
3170
|
-
if (!isRecord$
|
|
3170
|
+
if (!isRecord$3(file)) return null;
|
|
3171
3171
|
const path = nonBlankString(file.path);
|
|
3172
3172
|
const count = nonNegativeInteger(file.count);
|
|
3173
3173
|
if (path === null || count === null) return null;
|
|
@@ -3195,7 +3195,7 @@ window.__ModuleLoader__.load({
|
|
|
3195
3195
|
* @returns the narrowed metadata, or `null` when it is not a usable read card.
|
|
3196
3196
|
*/
|
|
3197
3197
|
function narrowReadCardMeta(meta) {
|
|
3198
|
-
if (!isRecord$
|
|
3198
|
+
if (!isRecord$3(meta) || meta.kind !== "read") return null;
|
|
3199
3199
|
if (jsonByteLength(meta) > 262144) return null;
|
|
3200
3200
|
const path = nonBlankString(meta.path);
|
|
3201
3201
|
const offset = positiveInteger(meta.offset);
|
|
@@ -3205,7 +3205,7 @@ window.__ModuleLoader__.load({
|
|
|
3205
3205
|
const lines = [];
|
|
3206
3206
|
let previous = offset - 1;
|
|
3207
3207
|
for (const line of meta.lines) {
|
|
3208
|
-
if (!isRecord$
|
|
3208
|
+
if (!isRecord$3(line)) return null;
|
|
3209
3209
|
const number = positiveInteger(line.number);
|
|
3210
3210
|
if (number === null || number <= previous || number > totalLines) return null;
|
|
3211
3211
|
if (typeof line.text !== "string") return null;
|
|
@@ -3226,12 +3226,12 @@ window.__ModuleLoader__.load({
|
|
|
3226
3226
|
}
|
|
3227
3227
|
/** Narrow one grouped-matches file entry, or `null` when malformed. */
|
|
3228
3228
|
function narrowSearchFile(file) {
|
|
3229
|
-
if (!isRecord$
|
|
3229
|
+
if (!isRecord$3(file)) return null;
|
|
3230
3230
|
const path = nonBlankString(file.path);
|
|
3231
3231
|
if (path === null || !Array.isArray(file.matches)) return null;
|
|
3232
3232
|
const matches = [];
|
|
3233
3233
|
for (const match of file.matches) {
|
|
3234
|
-
if (!isRecord$
|
|
3234
|
+
if (!isRecord$3(match)) return null;
|
|
3235
3235
|
const lineNumber = positiveInteger(match.lineNumber);
|
|
3236
3236
|
if (lineNumber === null || typeof match.line !== "string") return null;
|
|
3237
3237
|
matches.push({
|
|
@@ -3250,7 +3250,7 @@ window.__ModuleLoader__.load({
|
|
|
3250
3250
|
* @returns the narrowed metadata, or `null` when it is not a usable search card.
|
|
3251
3251
|
*/
|
|
3252
3252
|
function narrowSearchCardMeta(meta) {
|
|
3253
|
-
if (!isRecord$
|
|
3253
|
+
if (!isRecord$3(meta) || meta.kind !== "search") return null;
|
|
3254
3254
|
if (jsonByteLength(meta) > 65536) return null;
|
|
3255
3255
|
if (typeof meta.truncated !== "boolean") return null;
|
|
3256
3256
|
const total = nonNegativeInteger(meta.total);
|
|
@@ -3291,7 +3291,7 @@ window.__ModuleLoader__.load({
|
|
|
3291
3291
|
* @returns the narrowed metadata, or `null` when it is not a usable terminal card.
|
|
3292
3292
|
*/
|
|
3293
3293
|
function narrowTerminalCardMeta(meta) {
|
|
3294
|
-
if (!isRecord$
|
|
3294
|
+
if (!isRecord$3(meta) || meta.kind !== "terminal") return null;
|
|
3295
3295
|
if (meta.mode === "background") {
|
|
3296
3296
|
const jobId = nonBlankString(meta.jobId);
|
|
3297
3297
|
return jobId === null ? null : {
|
|
@@ -3365,6 +3365,8 @@ window.__ModuleLoader__.load({
|
|
|
3365
3365
|
"astEdit.replacements": "{count} 处替换",
|
|
3366
3366
|
"astEdit.files": "{count} 个文件",
|
|
3367
3367
|
"astEdit.parseErrors": "解析错误",
|
|
3368
|
+
"astEdit.rules": "规则",
|
|
3369
|
+
"astEdit.rulesRest": "… 其余 {count} 条",
|
|
3368
3370
|
"read.window": "显示 {shown} / {total} 行",
|
|
3369
3371
|
"read.collapseAria": "收起内容",
|
|
3370
3372
|
"read.expandAria": "展开其余 {count} 行",
|
|
@@ -3374,6 +3376,7 @@ window.__ModuleLoader__.load({
|
|
|
3374
3376
|
"search.matches": "{shown} 处匹配 · {files} 个文件",
|
|
3375
3377
|
"search.matches.truncated": "显示 {shown} / 共 {total} 处匹配 · {files} 个文件",
|
|
3376
3378
|
"search.noResults": "无结果",
|
|
3379
|
+
"search.scope": "范围 {path}",
|
|
3377
3380
|
"search.collapseAria": "收起结果",
|
|
3378
3381
|
"search.expandAria": "展开其余 {count} 行结果",
|
|
3379
3382
|
"search.expandRest": "… 其余 {count} 行",
|
|
@@ -3425,6 +3428,8 @@ window.__ModuleLoader__.load({
|
|
|
3425
3428
|
"astEdit.replacements": "{count} replacements",
|
|
3426
3429
|
"astEdit.files": "{count} files",
|
|
3427
3430
|
"astEdit.parseErrors": "Parse errors",
|
|
3431
|
+
"astEdit.rules": "Rules",
|
|
3432
|
+
"astEdit.rulesRest": "… {count} more",
|
|
3428
3433
|
"read.window": "Showing {shown} of {total} lines",
|
|
3429
3434
|
"read.collapseAria": "Collapse content",
|
|
3430
3435
|
"read.expandAria": "Expand {count} more lines",
|
|
@@ -3434,6 +3439,7 @@ window.__ModuleLoader__.load({
|
|
|
3434
3439
|
"search.matches": "{shown} matches · {files} files",
|
|
3435
3440
|
"search.matches.truncated": "Showing {shown} of {total} matches · {files} files",
|
|
3436
3441
|
"search.noResults": "No results",
|
|
3442
|
+
"search.scope": "in {path}",
|
|
3437
3443
|
"search.collapseAria": "Collapse results",
|
|
3438
3444
|
"search.expandAria": "Expand {count} more result lines",
|
|
3439
3445
|
"search.expandRest": "… {count} more lines",
|
|
@@ -3483,7 +3489,7 @@ window.__ModuleLoader__.load({
|
|
|
3483
3489
|
return typeof call === "object" && call !== null ? call : null;
|
|
3484
3490
|
}
|
|
3485
3491
|
/** Whether `value` is a plain object, the only argument shape the cards accept. */
|
|
3486
|
-
function isRecord$
|
|
3492
|
+
function isRecord$2(value) {
|
|
3487
3493
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3488
3494
|
}
|
|
3489
3495
|
/**
|
|
@@ -3505,7 +3511,7 @@ window.__ModuleLoader__.load({
|
|
|
3505
3511
|
let parsed = null;
|
|
3506
3512
|
if (typeof raw === "string") try {
|
|
3507
3513
|
const value = JSON.parse(raw);
|
|
3508
|
-
if (isRecord$
|
|
3514
|
+
if (isRecord$2(value)) parsed = value;
|
|
3509
3515
|
} catch {
|
|
3510
3516
|
parsed = null;
|
|
3511
3517
|
}
|
|
@@ -3556,7 +3562,7 @@ window.__ModuleLoader__.load({
|
|
|
3556
3562
|
if (!isSettled(block) || !Array.isArray(block.content)) return "";
|
|
3557
3563
|
const parts = [];
|
|
3558
3564
|
for (const part of block.content) {
|
|
3559
|
-
if (!isRecord$
|
|
3565
|
+
if (!isRecord$2(part)) continue;
|
|
3560
3566
|
if (part.type === "text" && typeof part.text === "string") parts.push(part.text);
|
|
3561
3567
|
}
|
|
3562
3568
|
return parts.join("\n");
|
|
@@ -3810,9 +3816,8 @@ window.__ModuleLoader__.load({
|
|
|
3810
3816
|
.twc-chevron{color:var(--dsw-alias-label-secondary)}
|
|
3811
3817
|
.twc-title{font-weight:400}
|
|
3812
3818
|
.twc-sep{flex:none;width:2px;height:2px;border-radius:1px;margin:0 8px;background:var(--dsw-alias-label-caption)}
|
|
3813
|
-
.twc-summary{flex:
|
|
3819
|
+
.twc-summary{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary)}
|
|
3814
3820
|
.twc-summarySuffix{flex:none;margin-left:4px;white-space:nowrap;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary)}
|
|
3815
|
-
.twc-diffStat{margin-left:10px;font-family:var(--ds-font-family-code);font-size:calc(var(--dsh-content-font-size-secondary,13px) - 2px);color:var(--dsw-alias-label-caption)}
|
|
3816
3821
|
.twc-errorSummary{color:var(--dsw-alias-state-error-primary)}
|
|
3817
3822
|
.twc-fileLink{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0;padding:0;border:none;background:none;font:inherit;text-align:left;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-secondary);text-decoration:underline dotted;text-decoration-color:var(--dsw-alias-label-tertiary);text-decoration-thickness:1px;text-underline-offset:3px;cursor:pointer;border-radius:4px}
|
|
3818
3823
|
.twc-fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}
|
|
@@ -4049,6 +4054,76 @@ window.__ModuleLoader__.load({
|
|
|
4049
4054
|
}
|
|
4050
4055
|
};
|
|
4051
4056
|
//#endregion
|
|
4057
|
+
//#region src/web/client/rows/command-card-facts.ts
|
|
4058
|
+
/**
|
|
4059
|
+
* Collapsed-fact builders for the two command-shaped cards: the command a
|
|
4060
|
+
* `bash` description hides, and the rewrite rule an `ast_edit` preview only
|
|
4061
|
+
* shows once expanded.
|
|
4062
|
+
*
|
|
4063
|
+
* Both cards collapse to one head line. `bash`'s head used to be the model's
|
|
4064
|
+
* own `description` — a sentence about intent, never the command that ran — so
|
|
4065
|
+
* the only way to learn what executed was to expand the card. `ast_edit`'s head
|
|
4066
|
+
* names the file and the replacement count, and its body previews the *result*:
|
|
4067
|
+
* the rule that produced that result lives only in the call's `ops` argument,
|
|
4068
|
+
* which no card showed at all. Each builder recovers one fact from the call's
|
|
4069
|
+
* own arguments, so nothing is inferred from output, and a call head that fell
|
|
4070
|
+
* outside the session window degrades to the facts the card already holds.
|
|
4071
|
+
* @module @xiaoso/dsh-tool-plus/web/client/rows/command-card-facts
|
|
4072
|
+
*/
|
|
4073
|
+
/** Whether `value` is a plain object — the only op shape the engine sends. */
|
|
4074
|
+
function isRecord$1(value) {
|
|
4075
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4076
|
+
}
|
|
4077
|
+
/**
|
|
4078
|
+
* The `bash` card's collapsed summary: what ran, placed after the sentence
|
|
4079
|
+
* saying why.
|
|
4080
|
+
*
|
|
4081
|
+
* The command is the fact the head must carry, so the description can never be
|
|
4082
|
+
* the whole summary — it is a prefix at most. Only its first line is used: a
|
|
4083
|
+
* summary is one row high, and the rest of a multi-line command belongs to the
|
|
4084
|
+
* expanded body, which draws the whole thing.
|
|
4085
|
+
* @param description - the model's one-line intent, or `null` when absent.
|
|
4086
|
+
* @param command - the command actually executed (never empty).
|
|
4087
|
+
* @param outcome - the interruption word (`timedOut` / `cancelled`), or `null`.
|
|
4088
|
+
* @returns the outcome alone when the run was interrupted; otherwise the
|
|
4089
|
+
* description joined to the command's first line, or that line alone.
|
|
4090
|
+
*/
|
|
4091
|
+
function bashCardSummary(description, command, outcome) {
|
|
4092
|
+
if (outcome !== null && outcome !== "") return outcome;
|
|
4093
|
+
const intent = description === null ? "" : description.trim();
|
|
4094
|
+
const line = firstLine(command);
|
|
4095
|
+
return intent === "" ? line : `${intent} · ${line}`;
|
|
4096
|
+
}
|
|
4097
|
+
/**
|
|
4098
|
+
* The `ast_edit` card's rule line: the first usable `ops` entry, written the
|
|
4099
|
+
* way the tool's own `ops` argument writes it (`pat → out`).
|
|
4100
|
+
*
|
|
4101
|
+
* Only the first rule is stated, with the count of the entries behind it: the
|
|
4102
|
+
* card exists to preview one change, and a call may carry many rules whose
|
|
4103
|
+
* patterns are whole AST fragments — listing them would bury the preview. The
|
|
4104
|
+
* remaining count is the array tail, not the number of *usable* entries, so it
|
|
4105
|
+
* always describes the call as it was made.
|
|
4106
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
4107
|
+
* @param t - this plugin's card translate seat.
|
|
4108
|
+
* @returns the rule line, or `null` when the call carries no usable rule.
|
|
4109
|
+
*/
|
|
4110
|
+
function astEditRuleLine(args, t) {
|
|
4111
|
+
const ops = args?.ops;
|
|
4112
|
+
if (!Array.isArray(ops)) return null;
|
|
4113
|
+
for (let index = 0; index < ops.length; index += 1) {
|
|
4114
|
+
const op = ops[index];
|
|
4115
|
+
if (!isRecord$1(op)) continue;
|
|
4116
|
+
const pat = argText(op, "pat");
|
|
4117
|
+
if (pat === null) continue;
|
|
4118
|
+
const replacement = op.out;
|
|
4119
|
+
if (typeof replacement !== "string") continue;
|
|
4120
|
+
const rest = ops.length - 1;
|
|
4121
|
+
const rule = `${t("astEdit.rules")}: ${pat} →${replacement === "" ? "" : ` ${replacement}`}`;
|
|
4122
|
+
return rest === 0 ? rule : `${rule} · ${t("astEdit.rulesRest", { count: rest })}`;
|
|
4123
|
+
}
|
|
4124
|
+
return null;
|
|
4125
|
+
}
|
|
4126
|
+
//#endregion
|
|
4052
4127
|
//#region src/web/client/rows/AstEditRow.tsx
|
|
4053
4128
|
/**
|
|
4054
4129
|
* `ast_edit` row: the engine's own change preview.
|
|
@@ -4100,6 +4175,7 @@ window.__ModuleLoader__.load({
|
|
|
4100
4175
|
const single = files === 1 ? meta.files[0]?.path : void 0;
|
|
4101
4176
|
const output = resultText(block);
|
|
4102
4177
|
const parseErrors = PARSE_ERROR.test(output);
|
|
4178
|
+
const rule = astEditRuleLine(args, t);
|
|
4103
4179
|
const summary = single !== void 0 && single !== "" ? displayPath(single, cwd) : t("astEdit.files", { count: files });
|
|
4104
4180
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCardShell, {
|
|
4105
4181
|
t,
|
|
@@ -4112,14 +4188,21 @@ window.__ModuleLoader__.load({
|
|
|
4112
4188
|
inspect,
|
|
4113
4189
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4114
4190
|
className: "twc-body",
|
|
4115
|
-
children: [
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4191
|
+
children: [
|
|
4192
|
+
rule !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4193
|
+
className: "twc-note",
|
|
4194
|
+
children: rule
|
|
4195
|
+
}),
|
|
4196
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.CodeBlock, {
|
|
4197
|
+
code: meta.preview,
|
|
4198
|
+
copyLabel: t("copy"),
|
|
4199
|
+
copiedLabel: t("copied")
|
|
4200
|
+
}),
|
|
4201
|
+
output !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4202
|
+
className: "twc-note",
|
|
4203
|
+
children: parseErrors ? `${t("astEdit.parseErrors")}: ${output}` : output
|
|
4204
|
+
})
|
|
4205
|
+
]
|
|
4123
4206
|
})
|
|
4124
4207
|
});
|
|
4125
4208
|
}
|
|
@@ -4327,6 +4410,43 @@ window.__ModuleLoader__.load({
|
|
|
4327
4410
|
};
|
|
4328
4411
|
}
|
|
4329
4412
|
//#endregion
|
|
4413
|
+
//#region src/web/client/rows/read-card-facts.ts
|
|
4414
|
+
/**
|
|
4415
|
+
* Collapsed-summary facts for the `read` card.
|
|
4416
|
+
*
|
|
4417
|
+
* The Host projection strips the inline selector off the path
|
|
4418
|
+
* (`stripReadSelector` in `web/host/read.ts`) because the card's *link* must
|
|
4419
|
+
* point at a real file, and it keeps only the file's own window. The collapsed
|
|
4420
|
+
* head is then the one place where the question the model actually asked is
|
|
4421
|
+
* still readable — and the projected path cannot answer it: a windowed read of
|
|
4422
|
+
* `package.json:22-25,31-34` and a whole-file read of `package.json` project to
|
|
4423
|
+
* the very same string, so the card hides the difference.
|
|
4424
|
+
*
|
|
4425
|
+
* The summary is therefore built from the call's own argument, selector and
|
|
4426
|
+
* all, with the projected path kept as the fallback for a result whose call
|
|
4427
|
+
* head fell outside the session window (a truncated window carries no
|
|
4428
|
+
* `tool/call` event, so no argument survives). That fallback states a fact the
|
|
4429
|
+
* card already holds; nothing is invented.
|
|
4430
|
+
* @module @xiaoso/dsh-tool-plus/web/client/rows/read-card-facts
|
|
4431
|
+
*/
|
|
4432
|
+
/**
|
|
4433
|
+
* The `read` card's collapsed summary: the path the model asked for, keeping
|
|
4434
|
+
* whatever inline selector it carried (`:22-25`, `:raw`, `:conflicts`, `:28-`,
|
|
4435
|
+
* a sqlite row, an archive member) and shortened against the workspace root.
|
|
4436
|
+
*
|
|
4437
|
+
* The selector is deliberately not validated against the read grammar: the head
|
|
4438
|
+
* reports the call verbatim, and a selector this plugin does not recognize is
|
|
4439
|
+
* still what the model typed — substituting the projection's stripped path
|
|
4440
|
+
* would erase the evidence that two calls differ.
|
|
4441
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
4442
|
+
* @param metaPath - the Host-projected path (already selector-free).
|
|
4443
|
+
* @param cwd - the session workspace root, when known.
|
|
4444
|
+
* @returns the workspace-shortened argument path, or the projected path.
|
|
4445
|
+
*/
|
|
4446
|
+
function readCardSummary(args, metaPath, cwd) {
|
|
4447
|
+
return displayPath(argText(args, "path") ?? metaPath, cwd);
|
|
4448
|
+
}
|
|
4449
|
+
//#endregion
|
|
4330
4450
|
//#region src/web/client/rows/ReadRow.tsx
|
|
4331
4451
|
/**
|
|
4332
4452
|
* `read` row: a line-numbered window view, or the image the read returned.
|
|
@@ -4404,7 +4524,7 @@ window.__ModuleLoader__.load({
|
|
|
4404
4524
|
t,
|
|
4405
4525
|
state: cardState(block),
|
|
4406
4526
|
title,
|
|
4407
|
-
summary:
|
|
4527
|
+
summary: readCardSummary(args, meta.path, cwd),
|
|
4408
4528
|
filePath: meta.path,
|
|
4409
4529
|
filePathLine: meta.offset,
|
|
4410
4530
|
openFile,
|
|
@@ -4655,6 +4775,45 @@ window.__ModuleLoader__.load({
|
|
|
4655
4775
|
}), document.body);
|
|
4656
4776
|
}
|
|
4657
4777
|
//#endregion
|
|
4778
|
+
//#region src/web/client/rows/search-card-facts.ts
|
|
4779
|
+
/** Separator between the suffix's facts; the shell adds the summary's own. */
|
|
4780
|
+
const FACT_SEPARATOR = " · ";
|
|
4781
|
+
/**
|
|
4782
|
+
* A target may be a `;`-delimited list of paths. Shortening the whole list in
|
|
4783
|
+
* one pass would cut the workspace prefix off its first entry only — the rest
|
|
4784
|
+
* stay absolute, and the same scope then reads as two unrelated places — so
|
|
4785
|
+
* each entry is shortened on its own and the list is rejoined.
|
|
4786
|
+
* @param scope - the call's own target argument.
|
|
4787
|
+
* @param cwd - the session workspace root, when known.
|
|
4788
|
+
* @returns the workspace-shortened target.
|
|
4789
|
+
*/
|
|
4790
|
+
function displayScope(scope, cwd) {
|
|
4791
|
+
if (!scope.includes(";")) return displayPath(scope, cwd);
|
|
4792
|
+
return scope.split(";").map((entry) => displayPath(entry.trim(), cwd)).join("; ");
|
|
4793
|
+
}
|
|
4794
|
+
/**
|
|
4795
|
+
* The search card's collapsed trailing fragment: the scope the call searched,
|
|
4796
|
+
* then what the search found.
|
|
4797
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
4798
|
+
* @param meta - the Host-projected search metadata (already narrowed).
|
|
4799
|
+
* @param cwd - the session workspace root, for shortening an absolute scope.
|
|
4800
|
+
* @param t - the card translate seat.
|
|
4801
|
+
* @returns the joined fragment, or `null` when there is no fact to add.
|
|
4802
|
+
*/
|
|
4803
|
+
function searchCardSuffix(args, meta, cwd, t) {
|
|
4804
|
+
const facts = [];
|
|
4805
|
+
if (meta.shape === "matches") {
|
|
4806
|
+
const scope = argText(args, "path");
|
|
4807
|
+
if (scope !== null) facts.push(t("search.scope", { path: displayScope(scope, cwd) }));
|
|
4808
|
+
}
|
|
4809
|
+
if (meta.total === 0) facts.push(t("search.noResults"));
|
|
4810
|
+
else if (meta.shape === "matches") {
|
|
4811
|
+
const shown = meta.files.reduce((count, file) => count + file.matches.length, 0);
|
|
4812
|
+
facts.push(searchBlockLabels(t).matchesSummary(shown, meta.total, meta.files.length, meta.truncated));
|
|
4813
|
+
} else facts.push(searchBlockLabels(t).pathsSummary(meta.paths.length, meta.total, meta.truncated));
|
|
4814
|
+
return facts.length === 0 ? null : `· ${facts.join(FACT_SEPARATOR)}`;
|
|
4815
|
+
}
|
|
4816
|
+
//#endregion
|
|
4658
4817
|
//#region src/web/client/rows/SearchRow.tsx
|
|
4659
4818
|
/**
|
|
4660
4819
|
* `grep` / `glob` / `ast_grep` row: a search result card.
|
|
@@ -4689,7 +4848,7 @@ window.__ModuleLoader__.load({
|
|
|
4689
4848
|
* @returns the search card.
|
|
4690
4849
|
*/
|
|
4691
4850
|
function SearchCard(props) {
|
|
4692
|
-
const { t, block, inspect, title } = props;
|
|
4851
|
+
const { t, block, cwd, inspect, title } = props;
|
|
4693
4852
|
const args = parseCardArgs(block);
|
|
4694
4853
|
const meta = narrowSearchCardMeta(cardMeta(block));
|
|
4695
4854
|
if (isSubCall(block) || block.isError === true || meta === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GenericCard, {
|
|
@@ -4706,6 +4865,7 @@ window.__ModuleLoader__.load({
|
|
|
4706
4865
|
state: cardState(block),
|
|
4707
4866
|
title,
|
|
4708
4867
|
summary: subject ?? argsSummary(args, t),
|
|
4868
|
+
summarySuffix: searchCardSuffix(args, meta, cwd, t),
|
|
4709
4869
|
inspect,
|
|
4710
4870
|
children: meta.shape === "matches" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SearchBlock, {
|
|
4711
4871
|
kind: "matches",
|
|
@@ -4790,7 +4950,7 @@ window.__ModuleLoader__.load({
|
|
|
4790
4950
|
const interrupted = timedOut || aborted;
|
|
4791
4951
|
const exitCode = running || interrupted ? void 0 : typeof foreground?.exitCode === "number" ? foreground.exitCode : typeof status.exitCode === "number" ? status.exitCode : void 0;
|
|
4792
4952
|
const workdir = argText(args, "workdir") ?? foreground?.workingDir ?? sessionCwd;
|
|
4793
|
-
const summary = interrupted ? timedOut ? t("timedOut") : t("cancelled") :
|
|
4953
|
+
const summary = bashCardSummary(argText(args, "description"), command, interrupted ? timedOut ? t("timedOut") : t("cancelled") : null);
|
|
4794
4954
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCardShell, {
|
|
4795
4955
|
t,
|
|
4796
4956
|
state,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
/** Locale namespace of the tool-card bundle (registered by `registerToolCards`). */
|
|
18
18
|
export declare const CARD_LOCALE_NS = "tool-plus-cards";
|
|
19
19
|
/** Every copy key of the tool-card namespace. */
|
|
20
|
-
export type ToolCardLocaleKey = 'copy' | 'copied' | 'collapse' | 'expand' | 'collapseAria' | 'expandAria' | 'expandRest' | 'files.one' | 'files.other' | 'running' | 'done' | 'failed' | 'cancelled' | 'timedOut' | 'noOutput' | 'title.bash' | 'title.read' | 'title.write' | 'title.edit' | 'title.grep' | 'title.glob' | 'title.astGrep' | 'title.astEdit' | 'write.lines' | 'write.madeExecutable' | 'astEdit.replacements' | 'astEdit.files' | 'astEdit.parseErrors' | 'read.window' | 'read.collapseAria' | 'read.expandAria' | 'read.expandRest' | 'search.paths' | 'search.paths.truncated' | 'search.matches' | 'search.matches.truncated' | 'search.noResults' | 'search.collapseAria' | 'search.expandAria' | 'search.expandRest' | 'terminal.signal' | 'terminal.exitCode' | 'terminal.running' | 'terminal.failed' | 'terminal.done' | 'terminal.noOutput' | 'bash.background' | 'image.loading' | 'image.loadFailed' | 'image.open' | 'image.openNamed' | 'image.dialog' | 'image.close' | 'image.unnamed' | 'image.caption' | 'generic.params' | 'generic.noDetail' | 'inspect';
|
|
20
|
+
export type ToolCardLocaleKey = 'copy' | 'copied' | 'collapse' | 'expand' | 'collapseAria' | 'expandAria' | 'expandRest' | 'files.one' | 'files.other' | 'running' | 'done' | 'failed' | 'cancelled' | 'timedOut' | 'noOutput' | 'title.bash' | 'title.read' | 'title.write' | 'title.edit' | 'title.grep' | 'title.glob' | 'title.astGrep' | 'title.astEdit' | 'write.lines' | 'write.madeExecutable' | 'astEdit.replacements' | 'astEdit.files' | 'astEdit.parseErrors' | 'astEdit.rules' | 'astEdit.rulesRest' | 'read.window' | 'read.collapseAria' | 'read.expandAria' | 'read.expandRest' | 'search.paths' | 'search.paths.truncated' | 'search.matches' | 'search.matches.truncated' | 'search.noResults' | 'search.scope' | 'search.collapseAria' | 'search.expandAria' | 'search.expandRest' | 'terminal.signal' | 'terminal.exitCode' | 'terminal.running' | 'terminal.failed' | 'terminal.done' | 'terminal.noOutput' | 'bash.background' | 'image.loading' | 'image.loadFailed' | 'image.open' | 'image.openNamed' | 'image.dialog' | 'image.close' | 'image.unnamed' | 'image.caption' | 'generic.params' | 'generic.noDetail' | 'inspect';
|
|
21
21
|
/** Simplified Chinese copy (the source dictionary). */
|
|
22
22
|
export declare const zh: Record<ToolCardLocaleKey, string>;
|
|
23
23
|
/** English dictionary, checked complete against the zh key set. */
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapsed-fact builders for the two command-shaped cards: the command a
|
|
3
|
+
* `bash` description hides, and the rewrite rule an `ast_edit` preview only
|
|
4
|
+
* shows once expanded.
|
|
5
|
+
*
|
|
6
|
+
* Both cards collapse to one head line. `bash`'s head used to be the model's
|
|
7
|
+
* own `description` — a sentence about intent, never the command that ran — so
|
|
8
|
+
* the only way to learn what executed was to expand the card. `ast_edit`'s head
|
|
9
|
+
* names the file and the replacement count, and its body previews the *result*:
|
|
10
|
+
* the rule that produced that result lives only in the call's `ops` argument,
|
|
11
|
+
* which no card showed at all. Each builder recovers one fact from the call's
|
|
12
|
+
* own arguments, so nothing is inferred from output, and a call head that fell
|
|
13
|
+
* outside the session window degrades to the facts the card already holds.
|
|
14
|
+
* @module @xiaoso/dsh-tool-plus/web/client/rows/command-card-facts
|
|
15
|
+
*/
|
|
16
|
+
import { type CardTranslate } from '../row-utils.ts';
|
|
17
|
+
/**
|
|
18
|
+
* The `bash` card's collapsed summary: what ran, placed after the sentence
|
|
19
|
+
* saying why.
|
|
20
|
+
*
|
|
21
|
+
* The command is the fact the head must carry, so the description can never be
|
|
22
|
+
* the whole summary — it is a prefix at most. Only its first line is used: a
|
|
23
|
+
* summary is one row high, and the rest of a multi-line command belongs to the
|
|
24
|
+
* expanded body, which draws the whole thing.
|
|
25
|
+
* @param description - the model's one-line intent, or `null` when absent.
|
|
26
|
+
* @param command - the command actually executed (never empty).
|
|
27
|
+
* @param outcome - the interruption word (`timedOut` / `cancelled`), or `null`.
|
|
28
|
+
* @returns the outcome alone when the run was interrupted; otherwise the
|
|
29
|
+
* description joined to the command's first line, or that line alone.
|
|
30
|
+
*/
|
|
31
|
+
export declare function bashCardSummary(description: string | null, command: string, outcome: string | null): string;
|
|
32
|
+
/**
|
|
33
|
+
* The `ast_edit` card's rule line: the first usable `ops` entry, written the
|
|
34
|
+
* way the tool's own `ops` argument writes it (`pat → out`).
|
|
35
|
+
*
|
|
36
|
+
* Only the first rule is stated, with the count of the entries behind it: the
|
|
37
|
+
* card exists to preview one change, and a call may carry many rules whose
|
|
38
|
+
* patterns are whole AST fragments — listing them would bury the preview. The
|
|
39
|
+
* remaining count is the array tail, not the number of *usable* entries, so it
|
|
40
|
+
* always describes the call as it was made.
|
|
41
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
42
|
+
* @param t - this plugin's card translate seat.
|
|
43
|
+
* @returns the rule line, or `null` when the call carries no usable rule.
|
|
44
|
+
*/
|
|
45
|
+
export declare function astEditRuleLine(args: Record<string, unknown> | null, t: CardTranslate): string | null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapsed-summary facts for the `read` card.
|
|
3
|
+
*
|
|
4
|
+
* The Host projection strips the inline selector off the path
|
|
5
|
+
* (`stripReadSelector` in `web/host/read.ts`) because the card's *link* must
|
|
6
|
+
* point at a real file, and it keeps only the file's own window. The collapsed
|
|
7
|
+
* head is then the one place where the question the model actually asked is
|
|
8
|
+
* still readable — and the projected path cannot answer it: a windowed read of
|
|
9
|
+
* `package.json:22-25,31-34` and a whole-file read of `package.json` project to
|
|
10
|
+
* the very same string, so the card hides the difference.
|
|
11
|
+
*
|
|
12
|
+
* The summary is therefore built from the call's own argument, selector and
|
|
13
|
+
* all, with the projected path kept as the fallback for a result whose call
|
|
14
|
+
* head fell outside the session window (a truncated window carries no
|
|
15
|
+
* `tool/call` event, so no argument survives). That fallback states a fact the
|
|
16
|
+
* card already holds; nothing is invented.
|
|
17
|
+
* @module @xiaoso/dsh-tool-plus/web/client/rows/read-card-facts
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The `read` card's collapsed summary: the path the model asked for, keeping
|
|
21
|
+
* whatever inline selector it carried (`:22-25`, `:raw`, `:conflicts`, `:28-`,
|
|
22
|
+
* a sqlite row, an archive member) and shortened against the workspace root.
|
|
23
|
+
*
|
|
24
|
+
* The selector is deliberately not validated against the read grammar: the head
|
|
25
|
+
* reports the call verbatim, and a selector this plugin does not recognize is
|
|
26
|
+
* still what the model typed — substituting the projection's stripped path
|
|
27
|
+
* would erase the evidence that two calls differ.
|
|
28
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
29
|
+
* @param metaPath - the Host-projected path (already selector-free).
|
|
30
|
+
* @param cwd - the session workspace root, when known.
|
|
31
|
+
* @returns the workspace-shortened argument path, or the projected path.
|
|
32
|
+
*/
|
|
33
|
+
export declare function readCardSummary(args: Record<string, unknown> | null, metaPath: string, cwd: string | undefined): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapsed-summary facts for the `grep` / `ast_grep` / `glob` cards.
|
|
3
|
+
*
|
|
4
|
+
* The row's summary is the search's *subject* — `pattern` for grep, `pat` for
|
|
5
|
+
* ast_grep, the path pattern for glob — so the one question the collapsed head
|
|
6
|
+
* could not answer is "which tree was searched?", and the counts sat in the
|
|
7
|
+
* folded body. That reads worst on an empty result: a bare `Grep · zzz` looks
|
|
8
|
+
* like a bad pattern rather than like a search of the wrong directory.
|
|
9
|
+
*
|
|
10
|
+
* Two facts therefore ride the head as a trailing fragment, after the summary
|
|
11
|
+
* and outside its ellipsis, because they are the parts a narrow row must never
|
|
12
|
+
* clip:
|
|
13
|
+
* - the scope, taken from the call's own `path` argument. Only `matches`
|
|
14
|
+
* shapes take it: a glob's `path` argument *is* its pattern, which the
|
|
15
|
+
* summary already shows, so repeating it as a scope would claim a second
|
|
16
|
+
* fact out of one argument. A call without `path` searched the workspace
|
|
17
|
+
* root — naming no scope is honest, inventing one is not;
|
|
18
|
+
* - the count, or "no results" instead of a zero count (a zero count is what
|
|
19
|
+
* the empty-state failure reads as, not what it says).
|
|
20
|
+
*
|
|
21
|
+
* The counting labels are the {@link searchBlockLabels} adapter the block body
|
|
22
|
+
* already uses, so the head and the body say the same thing in the same words,
|
|
23
|
+
* including the "showing N of M" wording of a capped payload.
|
|
24
|
+
* @module @xiaoso/dsh-tool-plus/web/client/rows/search-card-facts
|
|
25
|
+
*/
|
|
26
|
+
import type { SearchCardMeta } from '../../contract.ts';
|
|
27
|
+
import type { CardTranslate } from '../row-utils.ts';
|
|
28
|
+
/**
|
|
29
|
+
* The search card's collapsed trailing fragment: the scope the call searched,
|
|
30
|
+
* then what the search found.
|
|
31
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
32
|
+
* @param meta - the Host-projected search metadata (already narrowed).
|
|
33
|
+
* @param cwd - the session workspace root, for shortening an absolute scope.
|
|
34
|
+
* @param t - the card translate seat.
|
|
35
|
+
* @returns the joined fragment, or `null` when there is no fact to add.
|
|
36
|
+
*/
|
|
37
|
+
export declare function searchCardSuffix(args: Record<string, unknown> | null, meta: SearchCardMeta, cwd: string | undefined, t: CardTranslate): string | null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiaoso/dsh-tool-plus",
|
|
3
3
|
"description": "Oh My Pi bash runtime ported as a deepseek-harness tool plugin: persistent shell session, output minimizer, managed background jobs, command interception",
|
|
4
|
-
"version": "0.1.8-beta.
|
|
4
|
+
"version": "0.1.8-beta.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"deepseek-harness",
|
package/presets/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiaoso/dsh-tool-plus-presets",
|
|
3
|
-
"version": "0.1.8-beta.
|
|
3
|
+
"version": "0.1.8-beta.3",
|
|
4
4
|
"description": "Agent-preset installer for dsh-tool-plus: installs the standard / PTC enhanced preset templates into a DeepSeek Harness home.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|