dsh-more 0.1.0 → 0.1.1
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/CONTRIBUTING.md +13 -0
- package/README.md +4 -2
- package/README.zh-CN.md +5 -2
- package/dist/client.js +687 -292
- package/dist/index.js +262 -8
- package/dist/types/generated/host-settings-schema.d.ts +2 -0
- package/dist/types/generated/patch-catalog.d.ts +8 -0
- package/dist/types/kernel/host/activation.d.ts +1 -0
- package/dist/types/kernel/host/patch.d.ts +2 -0
- package/dist/types/patches/fast-mode/client/index.d.ts +19 -0
- package/dist/types/patches/fast-mode/client/styles.d.ts +1 -0
- package/dist/types/patches/fast-mode/host/index.d.ts +26 -0
- package/dist/types/patches/fast-mode/shared.d.ts +17 -0
- package/dist/types/patches/projectless-tasks/client/adapter.d.ts +2 -3
- package/dist/types/patches/projectless-tasks/client/create-task.d.ts +2 -2
- package/dist/types/patches/projectless-tasks/client/groups.d.ts +1 -1
- package/dist/types/patches/projectless-tasks/client/new-session.d.ts +2 -1
- package/dist/types/platform/dsh/client/runtime.d.ts +17 -2
- package/dist/types/platform/dsh/client/session-menu.d.ts +10 -0
- package/dist/types/platform/dsh/identity.d.ts +1 -1
- package/docs/desktop-compatibility.md +76 -0
- package/dsh.plugin.json +1 -1
- package/package.json +29 -27
package/dist/index.js
CHANGED
|
@@ -409,7 +409,7 @@ var MESSAGE_VISIBILITY_PROJECTION_KEY = "dshMoreMessageDelete";
|
|
|
409
409
|
|
|
410
410
|
// src/platform/dsh/identity.ts
|
|
411
411
|
var PLUGIN_NAME = "dsh-more";
|
|
412
|
-
var PLUGIN_API_PREFIX =
|
|
412
|
+
var PLUGIN_API_PREFIX = `/api/${PLUGIN_NAME}`;
|
|
413
413
|
var PLUGIN_MUTATION_HEADER = "x-dsh-more";
|
|
414
414
|
var PATCH_SETTINGS_NAMESPACE = PLUGIN_NAME;
|
|
415
415
|
|
|
@@ -760,8 +760,12 @@ function installLiveSessionHandleTracker(ctx) {
|
|
|
760
760
|
tracker.originalResume = originalResume;
|
|
761
761
|
tracker.createDescriptor = Object.getOwnPropertyDescriptor(registry, "create");
|
|
762
762
|
tracker.resumeDescriptor = Object.getOwnPropertyDescriptor(registry, "resume");
|
|
763
|
-
tracker.wrappedCreate = async (options)
|
|
764
|
-
|
|
763
|
+
tracker.wrappedCreate = async function(options) {
|
|
764
|
+
return trackedHandle(tracker, await originalCreate.call(this, options));
|
|
765
|
+
};
|
|
766
|
+
tracker.wrappedResume = async function(options) {
|
|
767
|
+
return trackedHandle(tracker, await originalResume.call(this, options));
|
|
768
|
+
};
|
|
765
769
|
tracker.references = 1;
|
|
766
770
|
registry[TRACKER] = tracker;
|
|
767
771
|
registry.create = tracker.wrappedCreate;
|
|
@@ -882,7 +886,9 @@ async function deleteSessionPermanently(ctx, rawSessionId) {
|
|
|
882
886
|
// src/patches/session-delete/host/index.ts
|
|
883
887
|
var hostPatch3 = {
|
|
884
888
|
id: SESSION_DELETE_PATCH_ID,
|
|
885
|
-
|
|
889
|
+
// A disabled action still needs to remember handles created during that time.
|
|
890
|
+
// Track for the plugin lifetime; the API router remains gated by the switch.
|
|
891
|
+
observe: installLiveSessionHandleTracker,
|
|
886
892
|
routes: ({ ctx }) => ({
|
|
887
893
|
commit: async (payload) => {
|
|
888
894
|
const sessionId = requireString(payload, "sessionId");
|
|
@@ -1415,6 +1421,182 @@ var hostPatch5 = {
|
|
|
1415
1421
|
routes: ({ ctx }) => ({ read: () => read(ctx), save: (payload) => save(ctx, payload) })
|
|
1416
1422
|
};
|
|
1417
1423
|
|
|
1424
|
+
// src/patches/fast-mode/host/index.ts
|
|
1425
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1426
|
+
|
|
1427
|
+
// src/patches/fast-mode/shared.ts
|
|
1428
|
+
var FAST_MODE_PATCH_ID = "fast-mode";
|
|
1429
|
+
var FAST_MODE_SETTINGS_NS = "dsh-more/fast-mode";
|
|
1430
|
+
var DEFAULT_FAST_MODE_SETTINGS = {
|
|
1431
|
+
defaultEnabled: false,
|
|
1432
|
+
serviceTier: "priority",
|
|
1433
|
+
speed: 1.5,
|
|
1434
|
+
enableAnthropicFastMode: true,
|
|
1435
|
+
customBodyParams: {},
|
|
1436
|
+
customHeaders: {}
|
|
1437
|
+
};
|
|
1438
|
+
|
|
1439
|
+
// src/patches/fast-mode/host/index.ts
|
|
1440
|
+
var activeFastSessionStorage = new AsyncLocalStorage();
|
|
1441
|
+
var fastSessions = /* @__PURE__ */ new Set();
|
|
1442
|
+
function isFastSession(sessionId) {
|
|
1443
|
+
if (!sessionId) return false;
|
|
1444
|
+
return fastSessions.has(sessionId);
|
|
1445
|
+
}
|
|
1446
|
+
function setFastSession(sessionId, enabled) {
|
|
1447
|
+
if (enabled) fastSessions.add(sessionId);
|
|
1448
|
+
else fastSessions.delete(sessionId);
|
|
1449
|
+
}
|
|
1450
|
+
function toggleFastSession(sessionId) {
|
|
1451
|
+
const next = !fastSessions.has(sessionId);
|
|
1452
|
+
setFastSession(sessionId, next);
|
|
1453
|
+
return next;
|
|
1454
|
+
}
|
|
1455
|
+
var interceptorInstalled = false;
|
|
1456
|
+
var currentSettings = { ...DEFAULT_FAST_MODE_SETTINGS };
|
|
1457
|
+
function readSettings2(ctx) {
|
|
1458
|
+
const descriptor = ctx.settings?.describe?.().find((entry) => entry.ns === FAST_MODE_SETTINGS_NS);
|
|
1459
|
+
if (!descriptor || typeof descriptor.value !== "object" || descriptor.value === null) {
|
|
1460
|
+
return { ...DEFAULT_FAST_MODE_SETTINGS };
|
|
1461
|
+
}
|
|
1462
|
+
const val = descriptor.value;
|
|
1463
|
+
return {
|
|
1464
|
+
defaultEnabled: typeof val.defaultEnabled === "boolean" ? val.defaultEnabled : false,
|
|
1465
|
+
serviceTier: typeof val.serviceTier === "string" && val.serviceTier ? val.serviceTier : "priority",
|
|
1466
|
+
speed: typeof val.speed === "number" && val.speed > 0 ? val.speed : 1.5,
|
|
1467
|
+
enableAnthropicFastMode: typeof val.enableAnthropicFastMode === "boolean" ? val.enableAnthropicFastMode : true,
|
|
1468
|
+
customBodyParams: typeof val.customBodyParams === "object" && val.customBodyParams !== null ? val.customBodyParams : {},
|
|
1469
|
+
customHeaders: typeof val.customHeaders === "object" && val.customHeaders !== null ? val.customHeaders : {}
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
function bodyText(body) {
|
|
1473
|
+
if (typeof body === "string") return body;
|
|
1474
|
+
if (body instanceof Uint8Array) return new TextDecoder().decode(body);
|
|
1475
|
+
return void 0;
|
|
1476
|
+
}
|
|
1477
|
+
function codexFastTier(value) {
|
|
1478
|
+
const tier = (value ?? "").trim().toLowerCase();
|
|
1479
|
+
if (tier === "" || tier === "fast" || tier === "priority") return "priority";
|
|
1480
|
+
return tier;
|
|
1481
|
+
}
|
|
1482
|
+
function isModelPayload(value) {
|
|
1483
|
+
return typeof value.model === "string" && ("messages" in value || "input" in value || "contents" in value);
|
|
1484
|
+
}
|
|
1485
|
+
function requestUrl(input) {
|
|
1486
|
+
if (typeof input === "string") return input;
|
|
1487
|
+
if (input instanceof URL) return input.toString();
|
|
1488
|
+
return input.url;
|
|
1489
|
+
}
|
|
1490
|
+
function patchFastRequest(input, init) {
|
|
1491
|
+
const store = activeFastSessionStorage.getStore();
|
|
1492
|
+
if (!store?.isFast || init?.body == null) return { input, init };
|
|
1493
|
+
const text = bodyText(init.body);
|
|
1494
|
+
if (text === void 0) return { input, init };
|
|
1495
|
+
let parsed;
|
|
1496
|
+
try {
|
|
1497
|
+
parsed = JSON.parse(text);
|
|
1498
|
+
} catch {
|
|
1499
|
+
return { input, init };
|
|
1500
|
+
}
|
|
1501
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return { input, init };
|
|
1502
|
+
const body = parsed;
|
|
1503
|
+
if (!isModelPayload(body)) return { input, init };
|
|
1504
|
+
body.service_tier = codexFastTier(currentSettings.serviceTier);
|
|
1505
|
+
if (currentSettings.customBodyParams) Object.assign(body, currentSettings.customBodyParams);
|
|
1506
|
+
const nextBody = JSON.stringify(body);
|
|
1507
|
+
const headers = new Headers(init.headers);
|
|
1508
|
+
if (headers.has("content-length")) headers.set("content-length", String(Buffer.byteLength(nextBody)));
|
|
1509
|
+
const url = requestUrl(input);
|
|
1510
|
+
if (currentSettings.enableAnthropicFastMode && /anthropic\.com|\/v1\/messages\b/.test(url)) {
|
|
1511
|
+
const beta = headers.get("anthropic-beta");
|
|
1512
|
+
headers.set("anthropic-beta", beta && !beta.includes("fast-mode") ? `${beta},fast-mode-2026-02-01` : beta ?? "fast-mode-2026-02-01");
|
|
1513
|
+
}
|
|
1514
|
+
if (currentSettings.customHeaders) {
|
|
1515
|
+
for (const [name2, value] of Object.entries(currentSettings.customHeaders)) headers.set(name2, value);
|
|
1516
|
+
}
|
|
1517
|
+
return { input, init: { ...init, body: nextBody, headers } };
|
|
1518
|
+
}
|
|
1519
|
+
function bindFastStream(stream, store) {
|
|
1520
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
1521
|
+
const pull = (run) => activeFastSessionStorage.run(store, run);
|
|
1522
|
+
const bound = {
|
|
1523
|
+
[Symbol.asyncIterator]() {
|
|
1524
|
+
return bound;
|
|
1525
|
+
},
|
|
1526
|
+
next: () => pull(() => iterator.next()),
|
|
1527
|
+
return: (value) => pull(() => iterator.return?.(value) ?? Promise.resolve({ done: true, value: void 0 })),
|
|
1528
|
+
throw: (error) => pull(() => iterator.throw?.(error) ?? Promise.reject(error))
|
|
1529
|
+
};
|
|
1530
|
+
return bound;
|
|
1531
|
+
}
|
|
1532
|
+
function installFastModeInterceptor() {
|
|
1533
|
+
if (interceptorInstalled) return;
|
|
1534
|
+
interceptorInstalled = true;
|
|
1535
|
+
const originalFetch = globalThis.fetch;
|
|
1536
|
+
globalThis.fetch = async function(input, init) {
|
|
1537
|
+
try {
|
|
1538
|
+
const patched = patchFastRequest(input, init);
|
|
1539
|
+
return await originalFetch.call(this, patched.input, patched.init);
|
|
1540
|
+
} catch (error) {
|
|
1541
|
+
console.warn("fast-mode fetch interceptor error, sending the original request", error);
|
|
1542
|
+
return originalFetch.call(this, input, init);
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
var hostPatch6 = {
|
|
1547
|
+
id: FAST_MODE_PATCH_ID,
|
|
1548
|
+
setup: (ctx) => {
|
|
1549
|
+
currentSettings = readSettings2(ctx);
|
|
1550
|
+
installFastModeInterceptor();
|
|
1551
|
+
const dispose = ctx.on("llm/stream", (options, next) => {
|
|
1552
|
+
if (options?.purpose === "session-title" || options?.purpose === "compaction") return next();
|
|
1553
|
+
const sessionId = options?.sessionId === void 0 ? void 0 : String(options.sessionId);
|
|
1554
|
+
const enabled = sessionId !== void 0 && isFastSession(sessionId) || currentSettings.defaultEnabled;
|
|
1555
|
+
if (!enabled) return next();
|
|
1556
|
+
return bindFastStream(next(), { sessionId, isFast: true });
|
|
1557
|
+
}, { global: true, prepend: true });
|
|
1558
|
+
return () => {
|
|
1559
|
+
if (typeof dispose === "function") dispose();
|
|
1560
|
+
};
|
|
1561
|
+
},
|
|
1562
|
+
routes: ({ ctx }) => ({
|
|
1563
|
+
getState: (payload) => {
|
|
1564
|
+
const sessionId = typeof payload?.sessionId === "string" ? payload.sessionId : void 0;
|
|
1565
|
+
return { enabled: sessionId ? isFastSession(sessionId) : false, settings: readSettings2(ctx) };
|
|
1566
|
+
},
|
|
1567
|
+
toggle: (payload) => {
|
|
1568
|
+
const sessionId = typeof payload?.sessionId === "string" ? payload.sessionId : "";
|
|
1569
|
+
if (!sessionId) throw new Error("\u7F3A\u5C11 sessionId");
|
|
1570
|
+
return { enabled: toggleFastSession(sessionId) };
|
|
1571
|
+
},
|
|
1572
|
+
set: (payload) => {
|
|
1573
|
+
const sessionId = typeof payload?.sessionId === "string" ? payload.sessionId : "";
|
|
1574
|
+
if (!sessionId) throw new Error("\u7F3A\u5C11 sessionId");
|
|
1575
|
+
const enabled = Boolean(payload?.enabled);
|
|
1576
|
+
setFastSession(sessionId, enabled);
|
|
1577
|
+
return { enabled };
|
|
1578
|
+
},
|
|
1579
|
+
getSettings: () => readSettings2(ctx),
|
|
1580
|
+
saveSettings: async (payload) => {
|
|
1581
|
+
if (typeof payload !== "object" || payload === null) throw new Error("\u65E0\u6548\u7684\u914D\u7F6E\u5185\u5BB9");
|
|
1582
|
+
const descriptor = ctx.settings?.describe?.().find((entry) => entry.ns === FAST_MODE_SETTINGS_NS);
|
|
1583
|
+
const next = {
|
|
1584
|
+
defaultEnabled: Boolean(payload.defaultEnabled),
|
|
1585
|
+
serviceTier: typeof payload.serviceTier === "string" && payload.serviceTier ? payload.serviceTier : "priority",
|
|
1586
|
+
speed: typeof payload.speed === "number" && payload.speed > 0 ? payload.speed : 1.5,
|
|
1587
|
+
enableAnthropicFastMode: payload.enableAnthropicFastMode !== false,
|
|
1588
|
+
customBodyParams: typeof payload.customBodyParams === "object" && payload.customBodyParams !== null ? payload.customBodyParams : {},
|
|
1589
|
+
customHeaders: typeof payload.customHeaders === "object" && payload.customHeaders !== null ? payload.customHeaders : {}
|
|
1590
|
+
};
|
|
1591
|
+
currentSettings = next;
|
|
1592
|
+
if (typeof ctx.settings?.mutate === "function") {
|
|
1593
|
+
await ctx.settings.mutate(FAST_MODE_SETTINGS_NS, [{ op: "set", path: [], value: next }], descriptor?.revision ?? 0);
|
|
1594
|
+
}
|
|
1595
|
+
return next;
|
|
1596
|
+
}
|
|
1597
|
+
})
|
|
1598
|
+
};
|
|
1599
|
+
|
|
1418
1600
|
// src/patches/session-auto-title/host/index.ts
|
|
1419
1601
|
import { setTimeout as delay } from "node:timers/promises";
|
|
1420
1602
|
|
|
@@ -1477,7 +1659,7 @@ async function* validate(stream, signal, status) {
|
|
|
1477
1659
|
throw error;
|
|
1478
1660
|
}
|
|
1479
1661
|
}
|
|
1480
|
-
var
|
|
1662
|
+
var hostPatch7 = {
|
|
1481
1663
|
id: SESSION_AUTO_TITLE_PATCH_ID,
|
|
1482
1664
|
setup: (ctx) => {
|
|
1483
1665
|
const status = { requests: 0, successes: 0, failures: 0, retries: 0 };
|
|
@@ -1592,7 +1774,7 @@ function prepareTask(payload, homeDirectory = homedir()) {
|
|
|
1592
1774
|
}
|
|
1593
1775
|
return { sessionId: payload.sessionId, cwd: join(homeDirectory, "Documents", "DSH", "tasks", payload.sessionId) };
|
|
1594
1776
|
}
|
|
1595
|
-
var
|
|
1777
|
+
var hostPatch8 = {
|
|
1596
1778
|
id: PROJECTLESS_TASKS_PATCH_ID,
|
|
1597
1779
|
setup: (ctx) => {
|
|
1598
1780
|
const fiber = ctx.inject(["settings"], (scope) => {
|
|
@@ -1608,7 +1790,7 @@ var hostPatch7 = {
|
|
|
1608
1790
|
};
|
|
1609
1791
|
|
|
1610
1792
|
// src/generated/host-registry.ts
|
|
1611
|
-
var HOST_PATCHES = [hostPatch, hostPatch2, hostPatch3, hostPatch4, hostPatch5, hostPatch6, hostPatch7];
|
|
1793
|
+
var HOST_PATCHES = [hostPatch, hostPatch2, hostPatch3, hostPatch4, hostPatch5, hostPatch6, hostPatch7, hostPatch8];
|
|
1612
1794
|
|
|
1613
1795
|
// src/generated/patch-catalog.ts
|
|
1614
1796
|
var PATCH_CATALOG = [
|
|
@@ -1617,6 +1799,7 @@ var PATCH_CATALOG = [
|
|
|
1617
1799
|
{ "id": "session-delete", "name": "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD", "description": "\u505C\u6B62\u76EE\u6807\u4F1A\u8BDD\u5E76\u4ECE\u78C1\u76D8\u6C38\u4E45\u5220\u9664\uFF0C\u4E0D\u5F71\u54CD\u539F\u751F\u5F52\u6863", "defaultEnabled": true, "order": 300, "clientKind": "standalone" },
|
|
1618
1800
|
{ "id": "conversation-markdown-export", "name": "\u5BFC\u51FA\u5B8C\u6574\u804A\u5929\u8BB0\u5F55", "description": "\u5C06\u5F53\u524D\u4F1A\u8BDD\u7684\u5B8C\u6574\u804A\u5929\u8BB0\u5F55\u4E0B\u8F7D\u4E3A Markdown \u6587\u4EF6", "defaultEnabled": true, "order": 400, "clientKind": "standalone" },
|
|
1619
1801
|
{ "id": "model-capabilities", "name": "\u6A21\u578B\u80FD\u529B\u914D\u7F6E", "description": "\u5728\u6A21\u578B\u914D\u7F6E\u9875\u8BBE\u7F6E\u6BCF\u4E2A\u6A21\u578B\u7684\u8BC6\u56FE\u80FD\u529B\u3001\u53EF\u9009\u601D\u8003\u7C7B\u578B\u4E0E\u601D\u8003\u53C2\u6570\u683C\u5F0F", "defaultEnabled": true, "order": 500, "clientKind": "standalone" },
|
|
1802
|
+
{ "id": "fast-mode", "name": "\u52A0\u901F", "description": "\u5728\u8F93\u5165\u6846\u548C /fast \u547D\u4EE4\u4E2D\u5F00\u5173\u52A0\u901F\u3002\u54CD\u5E94\u66F4\u5FEB\uFF0C\u4F46\u4F1A\u66F4\u5FEB\u6D88\u8017\u989D\u5EA6", "defaultEnabled": true, "order": 550, "clientKind": "standalone" },
|
|
1620
1803
|
{ "id": "session-auto-title", "name": "\u4F1A\u8BDD\u81EA\u52A8\u547D\u540D", "description": "\u65B0\u4F1A\u8BDD\u9996\u6761\u9700\u6C42\u540E\uFF0C\u4F7F\u7528\u5F53\u524D\u6A21\u578B\u751F\u6210\u300C\u7C7B\u578B \xB7 \u5177\u4F53\u4EFB\u52A1\u300D\u6807\u9898\uFF1B\u4FDD\u7559\u624B\u52A8\u547D\u540D\uFF0C\u5931\u8D25\u65F6\u4FDD\u7559\u539F\u6709\u6807\u9898", "defaultEnabled": true, "order": 600, "clientKind": "standalone" },
|
|
1621
1804
|
{ "id": "projectless-tasks", "name": "\u72EC\u7ACB\u4EFB\u52A1", "description": "\u5728\u5DE5\u4F5C\u533A\u4E0B\u62C9\u6846\u521B\u5EFA\u72EC\u7ACB\u4EFB\u52A1\u5E76\u4FDD\u7559\u8349\u7A3F\uFF0C\u4E5F\u53EF\u5C06\u672A\u5206\u7EC4\u4F1A\u8BDD\u79FB\u5165\u5DE5\u4F5C\u533A\u5217\u8868", "defaultEnabled": true, "order": 700, "clientKind": "standalone" }
|
|
1622
1805
|
];
|
|
@@ -1626,6 +1809,7 @@ var DEFAULT_PATCH_SETTINGS = {
|
|
|
1626
1809
|
"session-delete": true,
|
|
1627
1810
|
"conversation-markdown-export": true,
|
|
1628
1811
|
"model-capabilities": true,
|
|
1812
|
+
"fast-mode": true,
|
|
1629
1813
|
"session-auto-title": true,
|
|
1630
1814
|
"projectless-tasks": true
|
|
1631
1815
|
};
|
|
@@ -1647,10 +1831,28 @@ var HostPatchActivation = class {
|
|
|
1647
1831
|
constructor(ctx, patches) {
|
|
1648
1832
|
this.ctx = ctx;
|
|
1649
1833
|
this.byId = new Map(patches.map((patch) => [patch.id, patch]));
|
|
1834
|
+
try {
|
|
1835
|
+
for (const patch of patches) {
|
|
1836
|
+
const dispose = patch.observe?.(ctx);
|
|
1837
|
+
if (dispose !== void 0) this.observers.push(dispose);
|
|
1838
|
+
}
|
|
1839
|
+
} catch (error) {
|
|
1840
|
+
const failures = [error];
|
|
1841
|
+
for (const dispose of this.observers.splice(0).reverse()) {
|
|
1842
|
+
try {
|
|
1843
|
+
dispose();
|
|
1844
|
+
} catch (failure) {
|
|
1845
|
+
failures.push(failure);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
if (failures.length > 1) throw new AggregateError(failures, "patch observation failed");
|
|
1849
|
+
throw error;
|
|
1850
|
+
}
|
|
1650
1851
|
}
|
|
1651
1852
|
ctx;
|
|
1652
1853
|
byId;
|
|
1653
1854
|
disposers = /* @__PURE__ */ new Map();
|
|
1855
|
+
observers = [];
|
|
1654
1856
|
settings = { ...DEFAULT_PATCH_SETTINGS };
|
|
1655
1857
|
apply(next) {
|
|
1656
1858
|
for (const [id, patch] of this.byId) {
|
|
@@ -1669,8 +1871,18 @@ var HostPatchActivation = class {
|
|
|
1669
1871
|
return this.settings[id] === true && this.byId.has(id);
|
|
1670
1872
|
}
|
|
1671
1873
|
dispose() {
|
|
1672
|
-
|
|
1874
|
+
const disposers = [...this.disposers.values()].reverse();
|
|
1673
1875
|
this.disposers.clear();
|
|
1876
|
+
disposers.push(...this.observers.splice(0).reverse());
|
|
1877
|
+
const failures = [];
|
|
1878
|
+
for (const dispose of disposers) {
|
|
1879
|
+
try {
|
|
1880
|
+
dispose();
|
|
1881
|
+
} catch (error) {
|
|
1882
|
+
failures.push(error);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
if (failures.length > 0) throw new AggregateError(failures, "patch cleanup failed");
|
|
1674
1886
|
}
|
|
1675
1887
|
};
|
|
1676
1888
|
|
|
@@ -1685,6 +1897,7 @@ var PatchSettingsSchema = z3.object({
|
|
|
1685
1897
|
"session-delete": z3.boolean().default(true),
|
|
1686
1898
|
"conversation-markdown-export": z3.boolean().default(true),
|
|
1687
1899
|
"model-capabilities": z3.boolean().default(true),
|
|
1900
|
+
"fast-mode": z3.boolean().default(true),
|
|
1688
1901
|
"session-auto-title": z3.boolean().default(true),
|
|
1689
1902
|
"projectless-tasks": z3.boolean().default(true)
|
|
1690
1903
|
});
|
|
@@ -1717,6 +1930,9 @@ function installPatchSettings(ctx, activation, config = DEFAULT_PATCH_SETTINGS)
|
|
|
1717
1930
|
});
|
|
1718
1931
|
}
|
|
1719
1932
|
|
|
1933
|
+
// src/platform/dsh/host/api-router.ts
|
|
1934
|
+
import { Readable } from "node:stream";
|
|
1935
|
+
|
|
1720
1936
|
// src/platform/dsh/host/trust.ts
|
|
1721
1937
|
function header(req, name2) {
|
|
1722
1938
|
const value = req.headers[name2];
|
|
@@ -1773,6 +1989,43 @@ function registerPatchApi(ctx, patches, confirmationSecret, isEnabled) {
|
|
|
1773
1989
|
const host = ctx;
|
|
1774
1990
|
const routes = collectRoutes(patches, { ctx, confirmationSecret });
|
|
1775
1991
|
const logger = ctx.logger(PLUGIN_NAME);
|
|
1992
|
+
const connection = typeof ctx.get === "function" ? ctx.get("connection") : void 0;
|
|
1993
|
+
if (connection?.fetch?.register !== void 0) {
|
|
1994
|
+
for (const [route, registered] of routes) {
|
|
1995
|
+
connection.fetch.register({
|
|
1996
|
+
path: `${PLUGIN_API_PREFIX}/${route}`,
|
|
1997
|
+
methods: ["POST"],
|
|
1998
|
+
requestBody: "buffered",
|
|
1999
|
+
fetch: async (request) => {
|
|
2000
|
+
let status = 200;
|
|
2001
|
+
let headers = {};
|
|
2002
|
+
let body = "";
|
|
2003
|
+
const res = {
|
|
2004
|
+
writeHead(code, values) {
|
|
2005
|
+
status = code;
|
|
2006
|
+
headers = values;
|
|
2007
|
+
},
|
|
2008
|
+
end(value) {
|
|
2009
|
+
body = value;
|
|
2010
|
+
}
|
|
2011
|
+
};
|
|
2012
|
+
try {
|
|
2013
|
+
if (request.headers.get(PLUGIN_MUTATION_HEADER) !== "1") throw new DshMoreError("forbidden", "\u8BF7\u6C42\u6765\u6E90\u4E0D\u53D7\u4FE1\u4EFB\u3002", 403);
|
|
2014
|
+
if (!isEnabled(registered.patchId)) throw new DshMoreError("patch-disabled", "\u8FD9\u4E2A\u8865\u4E01\u5F53\u524D\u5DF2\u5173\u95ED\u3002", 409);
|
|
2015
|
+
const req = request.body === null ? Readable.from([]) : Readable.fromWeb(request.body);
|
|
2016
|
+
req.headers = Object.fromEntries(request.headers);
|
|
2017
|
+
requireJsonContentType(req);
|
|
2018
|
+
writeOk(res, await registered.handler(await readJson(req)));
|
|
2019
|
+
} catch (error) {
|
|
2020
|
+
if (!(error instanceof DshMoreError) || error.status >= 500) logger.error("patch API %s failed: %s", route, String(error));
|
|
2021
|
+
writeError(res, error);
|
|
2022
|
+
}
|
|
2023
|
+
return new Response(body, { status, headers });
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
1776
2029
|
ctx.effect(() => ctx.webServer.register({
|
|
1777
2030
|
kind: "prefix",
|
|
1778
2031
|
path: PLUGIN_API_PREFIX,
|
|
@@ -1818,6 +2071,7 @@ var inject = [
|
|
|
1818
2071
|
"webRuntime",
|
|
1819
2072
|
"agents",
|
|
1820
2073
|
"sessions",
|
|
2074
|
+
"connection",
|
|
1821
2075
|
"sessionPersistence",
|
|
1822
2076
|
"sessionProjections",
|
|
1823
2077
|
"workspaceRegistry",
|
|
@@ -5,6 +5,7 @@ export declare const PatchSettingsSchema: z<Schemastery.ObjectS<{
|
|
|
5
5
|
"session-delete": z<boolean, boolean>;
|
|
6
6
|
"conversation-markdown-export": z<boolean, boolean>;
|
|
7
7
|
"model-capabilities": z<boolean, boolean>;
|
|
8
|
+
"fast-mode": z<boolean, boolean>;
|
|
8
9
|
"session-auto-title": z<boolean, boolean>;
|
|
9
10
|
"projectless-tasks": z<boolean, boolean>;
|
|
10
11
|
}>, Schemastery.ObjectT<{
|
|
@@ -13,6 +14,7 @@ export declare const PatchSettingsSchema: z<Schemastery.ObjectS<{
|
|
|
13
14
|
"session-delete": z<boolean, boolean>;
|
|
14
15
|
"conversation-markdown-export": z<boolean, boolean>;
|
|
15
16
|
"model-capabilities": z<boolean, boolean>;
|
|
17
|
+
"fast-mode": z<boolean, boolean>;
|
|
16
18
|
"session-auto-title": z<boolean, boolean>;
|
|
17
19
|
"projectless-tasks": z<boolean, boolean>;
|
|
18
20
|
}>>;
|
|
@@ -33,6 +33,13 @@ export declare const PATCH_CATALOG: readonly [{
|
|
|
33
33
|
readonly defaultEnabled: true;
|
|
34
34
|
readonly order: 500;
|
|
35
35
|
readonly clientKind: "standalone";
|
|
36
|
+
}, {
|
|
37
|
+
readonly id: "fast-mode";
|
|
38
|
+
readonly name: "加速";
|
|
39
|
+
readonly description: "在输入框和 /fast 命令中开关加速。响应更快,但会更快消耗额度";
|
|
40
|
+
readonly defaultEnabled: true;
|
|
41
|
+
readonly order: 550;
|
|
42
|
+
readonly clientKind: "standalone";
|
|
36
43
|
}, {
|
|
37
44
|
readonly id: "session-auto-title";
|
|
38
45
|
readonly name: "会话自动命名";
|
|
@@ -55,6 +62,7 @@ export interface PatchSettings {
|
|
|
55
62
|
"session-delete": boolean;
|
|
56
63
|
"conversation-markdown-export": boolean;
|
|
57
64
|
"model-capabilities": boolean;
|
|
65
|
+
"fast-mode": boolean;
|
|
58
66
|
"session-auto-title": boolean;
|
|
59
67
|
"projectless-tasks": boolean;
|
|
60
68
|
}
|
|
@@ -6,6 +6,7 @@ export declare class HostPatchActivation {
|
|
|
6
6
|
private readonly ctx;
|
|
7
7
|
private readonly byId;
|
|
8
8
|
private readonly disposers;
|
|
9
|
+
private readonly observers;
|
|
9
10
|
private settings;
|
|
10
11
|
constructor(ctx: Context, patches: readonly HostPatch[]);
|
|
11
12
|
apply(next: PatchSettings): void;
|
|
@@ -7,6 +7,8 @@ export interface HostPatchRuntime {
|
|
|
7
7
|
/** A self-contained host-side patch contributed to the umbrella plugin. */
|
|
8
8
|
export interface HostPatch {
|
|
9
9
|
readonly id: string;
|
|
10
|
+
/** Passive lifetime bookkeeping; must not perform the feature's user action. */
|
|
11
|
+
observe?(ctx: Context): () => void;
|
|
10
12
|
setup?(ctx: Context): () => void;
|
|
11
13
|
routes(runtime: HostPatchRuntime): Readonly<Record<string, PatchApiHandler>>;
|
|
12
14
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { ClientPatch } from '../../../kernel/client/patch.js';
|
|
3
|
+
interface SessionFastClientState {
|
|
4
|
+
active: boolean;
|
|
5
|
+
busy: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare class FastModeClientStore {
|
|
8
|
+
private states;
|
|
9
|
+
private listeners;
|
|
10
|
+
getState(sessionId?: string): SessionFastClientState;
|
|
11
|
+
setState(sessionId: string | undefined, next: Partial<SessionFastClientState>): void;
|
|
12
|
+
subscribe: (listener: () => void) => (() => void);
|
|
13
|
+
private notify;
|
|
14
|
+
}
|
|
15
|
+
export declare const fastModeClientStore: FastModeClientStore;
|
|
16
|
+
export declare function syncSessionFastState(sessionId: string): Promise<boolean>;
|
|
17
|
+
export declare function toggleFastMode(ctx: ClientContext, sessionId?: string): Promise<void>;
|
|
18
|
+
export declare const clientPatch: ClientPatch;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const fastModeStyles = "\n.dshmore-fast-btn {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n height: 28px;\n padding: 0 10px;\n font-family: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n border-radius: 14px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-interactive-bg-idle, rgba(128, 128, 128, 0.08));\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n transition: all 0.15s ease-in-out;\n user-select: none;\n white-space: nowrap;\n outline: none;\n margin-right: 6px;\n flex: none;\n}\n\n.dshmore-fast-btn:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.18));\n border-color: var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.5));\n}\n\n.dshmore-fast-btn--active {\n background: rgba(245, 158, 11, 0.16) !important;\n color: #f59e0b !important;\n border-color: #f59e0b !important;\n box-shadow: 0 0 8px rgba(245, 158, 11, 0.3) !important;\n font-weight: 600;\n}\n\n.dshmore-fast-btn:disabled,\n.dshmore-fast-btn--busy {\n opacity: 0.6;\n cursor: wait;\n}\n\n.dshmore-fast-btn-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n line-height: 1;\n}\n\n.dshmore-fast-btn-label {\n display: inline-block;\n line-height: 1;\n}\n";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { HostPatch } from '../../../kernel/host/patch.js';
|
|
4
|
+
import { type FastModeSettings } from '../shared.js';
|
|
5
|
+
export interface FastModeStore {
|
|
6
|
+
sessionId?: string | undefined;
|
|
7
|
+
isFast: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const activeFastSessionStorage: AsyncLocalStorage<FastModeStore>;
|
|
10
|
+
export declare function isFastSession(sessionId?: string): boolean;
|
|
11
|
+
export declare function setFastSession(sessionId: string, enabled: boolean): void;
|
|
12
|
+
export declare function toggleFastSession(sessionId: string): boolean;
|
|
13
|
+
export declare function readSettings(ctx: Context): FastModeSettings;
|
|
14
|
+
/** Apply Codex/OpenAI Fast to one outbound model call. Unknown fields such as speed are not invented. */
|
|
15
|
+
export declare function patchFastRequest(input: RequestInfo | URL, init?: RequestInit): {
|
|
16
|
+
input: RequestInfo | URL;
|
|
17
|
+
init: RequestInit | undefined;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The agent loop iterates the model stream after llm/stream listeners return.
|
|
21
|
+
* Creating the iterator inside AsyncLocalStorage is not enough: the fetch runs
|
|
22
|
+
* on a later next() call, so each pull re-enters the fast context.
|
|
23
|
+
*/
|
|
24
|
+
export declare function bindFastStream<T>(stream: AsyncIterable<T>, store: FastModeStore): AsyncIterable<T> & AsyncIterator<T>;
|
|
25
|
+
export declare function installFastModeInterceptor(): void;
|
|
26
|
+
export declare const hostPatch: HostPatch;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const FAST_MODE_PATCH_ID = "fast-mode";
|
|
2
|
+
export declare const FAST_MODE_SETTINGS_NS = "dsh-more/fast-mode";
|
|
3
|
+
export interface FastModeSettings {
|
|
4
|
+
/** Whether fast mode is enabled globally by default across all sessions */
|
|
5
|
+
defaultEnabled: boolean;
|
|
6
|
+
/** Wire service tier. Codex/CLIProxyAPI only keeps the exact value "priority" for Fast; "fast" is deleted. */
|
|
7
|
+
serviceTier: string;
|
|
8
|
+
/** Retained for older settings documents. Not sent on the wire; Fast is a service tier, not a speed field. */
|
|
9
|
+
speed: number;
|
|
10
|
+
/** Whether Anthropic hosts also receive the fast-mode beta header */
|
|
11
|
+
enableAnthropicFastMode: boolean;
|
|
12
|
+
/** Custom parameters merged into outgoing LLM request body */
|
|
13
|
+
customBodyParams: Record<string, unknown>;
|
|
14
|
+
/** Custom headers merged into outgoing LLM request */
|
|
15
|
+
customHeaders: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_FAST_MODE_SETTINGS: FastModeSettings;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import type { ComposerBarOwnerProps } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
3
|
-
import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
4
3
|
import type { PatchActivationSource } from '../../../kernel/client/activation.js';
|
|
5
|
-
|
|
6
|
-
export declare function clientSessionActions(ctx: Context): Pick<ISessions, 'create' | 'open'>;
|
|
4
|
+
export { clientSessionActions } from '../../../platform/dsh/client/runtime.js';
|
|
7
5
|
/** Only the native workspace gate is removed; loading and business blocks stay native. */
|
|
8
6
|
export declare function unlockComposer(owner: ComposerBarOwnerProps, independent: boolean, block: ComposerBarOwnerProps['blocked']): ComposerBarOwnerProps;
|
|
9
7
|
/**
|
|
@@ -15,3 +13,4 @@ export declare function unlockComposer(owner: ComposerBarOwnerProps, independent
|
|
|
15
13
|
export declare const CONVERSATION_SLOTS: readonly ["conversation", "main.conversation"];
|
|
16
14
|
export declare function conversationSlot(name: typeof CONVERSATION_SLOTS[number]): 'main.conversation';
|
|
17
15
|
export declare function installConversationAdapter(ctx: Context, activation: PatchActivationSource, name?: typeof CONVERSATION_SLOTS[number]): () => void;
|
|
16
|
+
export declare function installConversationFactoryAdapter(ctx: Context, activation: PatchActivationSource): () => void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
2
1
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
2
|
+
import type { TaskSessionActions } from '../../../platform/dsh/client/runtime.js';
|
|
3
3
|
/** One shared flight for selection/clear; retries adopt the same native Session. */
|
|
4
|
-
export declare function createTaskStarter(sessions:
|
|
4
|
+
export declare function createTaskStarter(sessions: TaskSessionActions): {
|
|
5
5
|
start(beforeOpen?: (id: SessionId) => boolean | void): Promise<void>;
|
|
6
6
|
dispose(): void;
|
|
7
7
|
};
|
|
@@ -16,6 +16,6 @@ interface Lifetime {
|
|
|
16
16
|
subscribe(listener: () => void): () => void;
|
|
17
17
|
getSnapshot(): boolean;
|
|
18
18
|
}
|
|
19
|
-
export declare function extendGroupedSidebar(original: ComponentType<SidebarProps>, activation: PatchActivationSource, source: SettingsScope<SessionGroups>, lifetime?: Lifetime): ComponentType<SidebarProps>;
|
|
19
|
+
export declare function extendGroupedSidebar(original: ComponentType<SidebarProps>, activation: PatchActivationSource, source: SettingsScope<SessionGroups>, lifetime?: Lifetime, ctx?: Context): ComponentType<SidebarProps>;
|
|
20
20
|
export declare function installSessionGrouping(ctx: Context, activation: PatchActivationSource): void;
|
|
21
21
|
export {};
|
|
@@ -3,10 +3,11 @@ import type { ISessions, SessionListState } from '@deepseek-ai/dsh-api-session-c
|
|
|
3
3
|
import type { IWorkspaces, WorkspaceId, WorkspaceSnapshot } from '@deepseek-ai/dsh-api-workspace-controller/client';
|
|
4
4
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
5
5
|
import type { PatchActivationSource } from '../../../kernel/client/activation.js';
|
|
6
|
+
import { type TaskSessionActions } from '../../../platform/dsh/client/runtime.js';
|
|
6
7
|
interface Navigation {
|
|
7
8
|
startSession(workspaceId?: WorkspaceId): void;
|
|
8
9
|
}
|
|
9
|
-
type Sessions = Pick<ISessions, 'list'
|
|
10
|
+
type Sessions = Pick<ISessions, 'list'> & TaskSessionActions;
|
|
10
11
|
type Activation = Pick<PatchActivationSource, 'getSnapshot' | 'subscribe'>;
|
|
11
12
|
type NewSessionTarget = {
|
|
12
13
|
kind: 'workspace';
|
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { ISessions, SessionListState } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
2
3
|
import type { IWorkspaces } from '@deepseek-ai/dsh-api-workspace-controller/client';
|
|
3
|
-
|
|
4
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
5
|
+
export interface RefreshableSessions extends Pick<ISessions, 'list' | 'open'> {
|
|
4
6
|
refresh(): Promise<void>;
|
|
5
7
|
}
|
|
6
8
|
export interface RefreshableWorkspaces extends IWorkspaces {
|
|
7
9
|
refresh(): Promise<void>;
|
|
8
10
|
}
|
|
11
|
+
export interface SessionReference {
|
|
12
|
+
ready: Promise<unknown>;
|
|
13
|
+
release(): void;
|
|
14
|
+
}
|
|
15
|
+
export interface TaskSessionActions extends Pick<ISessions, 'create' | 'open'> {
|
|
16
|
+
retain?(id: SessionId): SessionReference;
|
|
17
|
+
}
|
|
18
|
+
/** 0.1.7 moved view selection out of the catalog and exposes reference owners. */
|
|
19
|
+
export declare function currentSessionId(list: SessionListState): SessionId | undefined;
|
|
20
|
+
/** Keep navigation in its owning UI service; never add methods to native Sessions. */
|
|
21
|
+
export declare function clientSessions(ctx: Context): RefreshableSessions;
|
|
22
|
+
/** New desktop clients require a temporary reference before borrowing a created draft. */
|
|
23
|
+
export declare function clientSessionActions(ctx: Context): TaskSessionActions;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
3
|
+
/** DSH 0.1.7 gives each session menu row its own scope and exact Session id. */
|
|
4
|
+
export declare function installSessionMenuAction(ctx: Context, options: {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
order: number;
|
|
8
|
+
danger?: boolean;
|
|
9
|
+
select(sessionId: SessionId): void;
|
|
10
|
+
}): (() => void) | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const PLUGIN_NAME = "dsh-more";
|
|
2
|
-
export declare const PLUGIN_API_PREFIX = "/dsh-more
|
|
2
|
+
export declare const PLUGIN_API_PREFIX = "/api/dsh-more";
|
|
3
3
|
export declare const PLUGIN_MUTATION_HEADER = "x-dsh-more";
|
|
4
4
|
export declare const PATCH_SETTINGS_NAMESPACE = "dsh-more";
|