@dungsil/dsh-plugin-ui-vcs 0.3.0 → 0.4.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/dist/client.js +43 -43
- package/dist/index.js +182 -109
- package/package.json +2 -1
package/dist/client.js
CHANGED
|
@@ -13474,6 +13474,7 @@ function apply$2(ctx) {
|
|
|
13474
13474
|
function SessionRequest({ sessionId }) {
|
|
13475
13475
|
(0, react.useSyncExternalStore)(subscribe, () => ctx.locale.getSnapshot().active);
|
|
13476
13476
|
const copy = text();
|
|
13477
|
+
const current = (0, react.useSyncExternalStore)((listener) => ctx.sidebarRight.mounted.subscribe(listener), () => ctx.sidebarRight.mounted.getSnapshot() === sessionId);
|
|
13477
13478
|
const running = (0, react.useSyncExternalStore)((listener) => sessions.list.subscribe(listener), () => sessions.list.getSnapshot().byId[sessionId]?.running ?? false);
|
|
13478
13479
|
const cwd = (0, react.useSyncExternalStore)((listener) => sessions.list.subscribe(listener), () => sessions.list.getSnapshot().byId[sessionId]?.cwd);
|
|
13479
13480
|
const anchor = (0, react.useRef)(null);
|
|
@@ -13491,12 +13492,21 @@ function apply$2(ctx) {
|
|
|
13491
13492
|
return () => node.remove();
|
|
13492
13493
|
}, [sessionId]);
|
|
13493
13494
|
(0, react.useEffect)(() => {
|
|
13494
|
-
let active;
|
|
13495
13495
|
setView(void 0);
|
|
13496
|
+
}, [
|
|
13497
|
+
sessionId,
|
|
13498
|
+
current,
|
|
13499
|
+
cwd
|
|
13500
|
+
]);
|
|
13501
|
+
(0, react.useEffect)(() => {
|
|
13502
|
+
if (!current || running) return;
|
|
13503
|
+
let disposed = false;
|
|
13504
|
+
let active;
|
|
13496
13505
|
const refresh = async () => {
|
|
13497
|
-
active
|
|
13498
|
-
|
|
13499
|
-
|
|
13506
|
+
if (disposed || active || document.visibilityState === "hidden") return;
|
|
13507
|
+
const controller = new AbortController();
|
|
13508
|
+
active = controller;
|
|
13509
|
+
const { signal } = controller;
|
|
13500
13510
|
try {
|
|
13501
13511
|
const result = await connection.rpc.call(channel$1, method, {
|
|
13502
13512
|
sessionId,
|
|
@@ -13505,22 +13515,28 @@ function apply$2(ctx) {
|
|
|
13505
13515
|
if (!signal.aborted) setView(result.ok ? result.value : void 0);
|
|
13506
13516
|
} catch {
|
|
13507
13517
|
if (!signal.aborted) setView(void 0);
|
|
13518
|
+
} finally {
|
|
13519
|
+
if (active === controller) active = void 0;
|
|
13508
13520
|
}
|
|
13509
13521
|
};
|
|
13510
13522
|
refresh();
|
|
13511
13523
|
window.addEventListener("focus", refresh);
|
|
13524
|
+
document.addEventListener("visibilitychange", refresh);
|
|
13512
13525
|
const interval = window.setInterval(refresh, 6e4);
|
|
13513
13526
|
return () => {
|
|
13527
|
+
disposed = true;
|
|
13514
13528
|
active?.abort();
|
|
13515
13529
|
window.removeEventListener("focus", refresh);
|
|
13530
|
+
document.removeEventListener("visibilitychange", refresh);
|
|
13516
13531
|
window.clearInterval(interval);
|
|
13517
13532
|
};
|
|
13518
13533
|
}, [
|
|
13519
13534
|
sessionId,
|
|
13535
|
+
current,
|
|
13520
13536
|
running,
|
|
13521
13537
|
cwd
|
|
13522
13538
|
]);
|
|
13523
|
-
const item = view?.request;
|
|
13539
|
+
const item = current ? view?.request : void 0;
|
|
13524
13540
|
const state = item?.state === "open" && item.draft ? "draft" : item?.state;
|
|
13525
13541
|
const icon = state === "merged" ? git_merge_default : state === "closed" ? git_pull_request_closed_default : state === "draft" ? git_pull_request_draft_default : git_pull_request_default;
|
|
13526
13542
|
const label = item && state ? `${copy.title} ${view?.provider === "gitlab" ? "!" : "#"}${item.number}: ${copy[state]}` : "";
|
|
@@ -14279,7 +14295,6 @@ const copy$1 = {
|
|
|
14279
14295
|
invalid: "이름과 올바른 이메일을 함께 입력하거나 두 항목을 모두 비우세요.",
|
|
14280
14296
|
cli: "CLI 인증 상태",
|
|
14281
14297
|
refresh: "새로고침",
|
|
14282
|
-
selectSession: "CLI 상태를 확인하려면 세션을 먼저 여세요.",
|
|
14283
14298
|
authenticated: "인증됨",
|
|
14284
14299
|
unauthenticated: "인증되지 않음",
|
|
14285
14300
|
unavailable: "설치되지 않음",
|
|
@@ -14303,7 +14318,6 @@ const copy$1 = {
|
|
|
14303
14318
|
invalid: "Enter both a name and a valid email, or leave both fields empty.",
|
|
14304
14319
|
cli: "CLI authentication",
|
|
14305
14320
|
refresh: "Refresh",
|
|
14306
|
-
selectSession: "Open a session to check CLI status.",
|
|
14307
14321
|
authenticated: "Authenticated",
|
|
14308
14322
|
unauthenticated: "Not authenticated",
|
|
14309
14323
|
unavailable: "Not installed",
|
|
@@ -14327,7 +14341,6 @@ const copy$1 = {
|
|
|
14327
14341
|
invalid: "请输入姓名和有效邮箱,或将两个字段都留空。",
|
|
14328
14342
|
cli: "CLI 认证状态",
|
|
14329
14343
|
refresh: "刷新",
|
|
14330
|
-
selectSession: "请先打开会话以检查 CLI 状态。",
|
|
14331
14344
|
authenticated: "已认证",
|
|
14332
14345
|
unauthenticated: "未认证",
|
|
14333
14346
|
unavailable: "未安装",
|
|
@@ -14343,7 +14356,6 @@ function VcsSettings({ ctx }) {
|
|
|
14343
14356
|
const text = localized(active);
|
|
14344
14357
|
const id = (0, react.useId)();
|
|
14345
14358
|
const connection = ctx.get("connection");
|
|
14346
|
-
const sessionId = (0, react.useSyncExternalStore)((listener) => ctx.sidebarRight.mounted.subscribe(listener), () => ctx.sidebarRight.mounted.getSnapshot());
|
|
14347
14359
|
const [view, setView] = (0, react.useState)();
|
|
14348
14360
|
const [draft, setDraft] = (0, react.useState)();
|
|
14349
14361
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
@@ -14378,21 +14390,14 @@ function VcsSettings({ ctx }) {
|
|
|
14378
14390
|
const controller = new AbortController();
|
|
14379
14391
|
lifetime.current = controller;
|
|
14380
14392
|
load(controller.signal);
|
|
14393
|
+
check(controller.signal);
|
|
14381
14394
|
return () => controller.abort();
|
|
14382
14395
|
}, []);
|
|
14383
|
-
(0, react.useEffect)(() => {
|
|
14384
|
-
const controller = new AbortController();
|
|
14385
|
-
setCli(void 0);
|
|
14386
|
-
setCliError(false);
|
|
14387
|
-
setChecking(false);
|
|
14388
|
-
if (sessionId) check(controller.signal);
|
|
14389
|
-
return () => controller.abort();
|
|
14390
|
-
}, [sessionId]);
|
|
14391
14396
|
const check = async (signal) => {
|
|
14392
14397
|
setChecking(true);
|
|
14393
14398
|
setCliError(false);
|
|
14394
14399
|
try {
|
|
14395
|
-
const result = await connection.rpc.call(channel, "dungsil-vcs/cli-status", {
|
|
14400
|
+
const result = await connection.rpc.call(channel, "dungsil-vcs/cli-status", {}, signal);
|
|
14396
14401
|
if (signal.aborted) return;
|
|
14397
14402
|
if (!result.ok) throw new Error("unavailable");
|
|
14398
14403
|
setCli(result.value);
|
|
@@ -14452,7 +14457,6 @@ function VcsSettings({ ctx }) {
|
|
|
14452
14457
|
className: "dsh-vcs-settings",
|
|
14453
14458
|
"aria-label": text.nav,
|
|
14454
14459
|
children: [
|
|
14455
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: text.scope }),
|
|
14456
14460
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
14457
14461
|
onSubmit: (event) => {
|
|
14458
14462
|
event.preventDefault();
|
|
@@ -14463,6 +14467,7 @@ function VcsSettings({ ctx }) {
|
|
|
14463
14467
|
disabled: blocked,
|
|
14464
14468
|
children: [
|
|
14465
14469
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: text.author }),
|
|
14470
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: text.scope }),
|
|
14466
14471
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: text.authorHint }),
|
|
14467
14472
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsRow, {
|
|
14468
14473
|
label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
@@ -14545,31 +14550,26 @@ function VcsSettings({ ctx }) {
|
|
|
14545
14550
|
]
|
|
14546
14551
|
}),
|
|
14547
14552
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: text.cli }),
|
|
14548
|
-
|
|
14553
|
+
checking && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
14549
14554
|
role: "status",
|
|
14550
|
-
children: text.
|
|
14551
|
-
})
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
|
|
14563
|
-
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
account.login,
|
|
14569
|
-
account.method && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" · ", text[account.method]] })
|
|
14570
|
-
] }, index))]
|
|
14571
|
-
}, item.cli))
|
|
14572
|
-
] })
|
|
14555
|
+
children: text.loading
|
|
14556
|
+
}),
|
|
14557
|
+
cliError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
14558
|
+
role: "alert",
|
|
14559
|
+
children: text.error
|
|
14560
|
+
}),
|
|
14561
|
+
cli?.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14562
|
+
className: "dsh-vcs-settings-cli",
|
|
14563
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsRow, {
|
|
14564
|
+
label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: item.cli }),
|
|
14565
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text[item.status === "error" ? "errorStatus" : item.status] })
|
|
14566
|
+
}), item.accounts.map((account, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
14567
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: account.host }),
|
|
14568
|
+
" · ",
|
|
14569
|
+
account.login,
|
|
14570
|
+
account.method && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" · ", text[account.method]] })
|
|
14571
|
+
] }, index))]
|
|
14572
|
+
}, item.cli))
|
|
14573
14573
|
]
|
|
14574
14574
|
});
|
|
14575
14575
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { clientRequestSchema } from "@deepseek-ai/dsh-client-connection";
|
|
3
|
-
import { SessionId } from "@deepseek-ai/dsh-session";
|
|
3
|
+
import { Session, SessionId } from "@deepseek-ai/dsh-session";
|
|
4
4
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
5
5
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
|
+
import { homedir } from "node:os";
|
|
7
8
|
// \0rolldown/runtime.js
|
|
8
9
|
var __defProp = Object.defineProperty;
|
|
9
10
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -900,6 +901,69 @@ defineMethod("transform", [
|
|
|
900
901
|
"callback",
|
|
901
902
|
"preserve"
|
|
902
903
|
], ({ inner }, isInner) => inner.toString(isInner));
|
|
904
|
+
// src/inspect-session.ts
|
|
905
|
+
async function inspectSession(ctx, id, signal) {
|
|
906
|
+
signal.throwIfAborted();
|
|
907
|
+
const live = ctx.get("agents")?.get(id);
|
|
908
|
+
if (live) return live.session;
|
|
909
|
+
const inspection = await ctx.sessionController.inspect(id, signal);
|
|
910
|
+
signal.throwIfAborted();
|
|
911
|
+
return Session.create(id, inspection.events, inspection.meta, inspection.inheritedEventCount);
|
|
912
|
+
}
|
|
913
|
+
// ../../tools/async/src/index.ts
|
|
914
|
+
function createTaskQueue({ concurrency, maxPending }) {
|
|
915
|
+
if (!Number.isSafeInteger(concurrency) || concurrency < 1 || !Number.isSafeInteger(maxPending) || maxPending < 0) throw new RangeError("Invalid task queue limits");
|
|
916
|
+
let active = 0;
|
|
917
|
+
const pending = /* @__PURE__ */ new Set();
|
|
918
|
+
const drain = () => {
|
|
919
|
+
while (active < concurrency && pending.size) pending.values().next().value.start();
|
|
920
|
+
};
|
|
921
|
+
return (signal, task) => {
|
|
922
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
923
|
+
if (active >= concurrency && pending.size >= maxPending) return Promise.reject(/* @__PURE__ */ new Error("busy"));
|
|
924
|
+
return new Promise((resolve, reject) => {
|
|
925
|
+
const entry = {
|
|
926
|
+
start() {
|
|
927
|
+
pending.delete(entry);
|
|
928
|
+
signal.removeEventListener("abort", entry.cancel);
|
|
929
|
+
active++;
|
|
930
|
+
(async () => {
|
|
931
|
+
try {
|
|
932
|
+
signal.throwIfAborted();
|
|
933
|
+
resolve(await task());
|
|
934
|
+
} catch (error) {
|
|
935
|
+
reject(error);
|
|
936
|
+
} finally {
|
|
937
|
+
active--;
|
|
938
|
+
drain();
|
|
939
|
+
}
|
|
940
|
+
})();
|
|
941
|
+
},
|
|
942
|
+
cancel() {
|
|
943
|
+
pending.delete(entry);
|
|
944
|
+
signal.removeEventListener("abort", entry.cancel);
|
|
945
|
+
reject(signal.reason);
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
pending.add(entry);
|
|
949
|
+
signal.addEventListener("abort", entry.cancel, { once: true });
|
|
950
|
+
drain();
|
|
951
|
+
});
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
// src/read-queue.ts
|
|
955
|
+
const queues = /* @__PURE__ */ new WeakMap();
|
|
956
|
+
function readQueue(provider) {
|
|
957
|
+
let queue = queues.get(provider);
|
|
958
|
+
if (!queue) {
|
|
959
|
+
queue = createTaskQueue({
|
|
960
|
+
concurrency: 2,
|
|
961
|
+
maxPending: 128
|
|
962
|
+
});
|
|
963
|
+
queues.set(provider, queue);
|
|
964
|
+
}
|
|
965
|
+
return queue;
|
|
966
|
+
}
|
|
903
967
|
// src/pull-requests-protocol.ts
|
|
904
968
|
const channel$1 = "/api";
|
|
905
969
|
const method = "dungsil-vcs/pull-requests/list";
|
|
@@ -975,9 +1039,10 @@ function actionInput$1(value) {
|
|
|
975
1039
|
function apply$3(ctx, config) {
|
|
976
1040
|
const lifetime = new AbortController();
|
|
977
1041
|
const actionsInFlight = /* @__PURE__ */ new Set();
|
|
1042
|
+
const read = readQueue(ctx.subprocess);
|
|
978
1043
|
ctx.effect(() => () => lifetime.abort());
|
|
979
|
-
const list = async (
|
|
980
|
-
const cwd = ctx.workingDirectory.get(
|
|
1044
|
+
const list = async (session, signal, action, requested, summary = false) => {
|
|
1045
|
+
const cwd = ctx.workingDirectory.get(session);
|
|
981
1046
|
const run = async (executable, args, failure = "command_failed", allowed = [0], trim = true) => {
|
|
982
1047
|
let resolved;
|
|
983
1048
|
try {
|
|
@@ -986,7 +1051,7 @@ function apply$3(ctx, config) {
|
|
|
986
1051
|
throw new Error(executable === "git" ? "repository" : "cli_missing");
|
|
987
1052
|
}
|
|
988
1053
|
const argv = [resolved, ...args];
|
|
989
|
-
const policy = ctx.sandboxPolicy.resolve({ session
|
|
1054
|
+
const policy = ctx.sandboxPolicy.resolve({ session });
|
|
990
1055
|
const command = policy.mode === "danger-full-access" ? argv : (await ctx.sandbox.confine(argv, {
|
|
991
1056
|
...policy,
|
|
992
1057
|
mode: policy.mode
|
|
@@ -1119,7 +1184,7 @@ function apply$3(ctx, config) {
|
|
|
1119
1184
|
if (requested.origin !== new URL(origin).origin || provider === "github" && repository.split("/").length !== 2 || !match || !Number.isSafeInteger(Number(match[2]))) throw new Error("unsupported");
|
|
1120
1185
|
if (provider === "github" ? match[1].toLowerCase() !== prefix.toLowerCase() : match[1] !== prefix) {
|
|
1121
1186
|
if (provider === "gitlab") throw new Error("unsupported");
|
|
1122
|
-
const associated = await list(
|
|
1187
|
+
const associated = await list(session, signal, void 0, void 0, summary);
|
|
1123
1188
|
const linkedUrl = `${requested.origin}${match[1]}/${provider === "github" ? "pull" : forge ? "pulls" : "-/merge_requests"}/${match[2]}`;
|
|
1124
1189
|
if ("request" in associated && associated.request && (provider === "github" ? associated.request.url.toLowerCase() === linkedUrl.toLowerCase() : associated.request.url === linkedUrl)) return associated;
|
|
1125
1190
|
throw new Error("unsupported");
|
|
@@ -1714,14 +1779,24 @@ function apply$3(ctx, config) {
|
|
|
1714
1779
|
inFlight = action.sessionId;
|
|
1715
1780
|
}
|
|
1716
1781
|
const requested = route === "dungsil-vcs/pull-requests/list" && input.requestUrl !== void 0 ? lookupUrl(input.requestUrl) : void 0;
|
|
1717
|
-
const resolved = await ctx.sessionController.resolveAgent(SessionId(input.sessionId));
|
|
1718
|
-
if ("error" in resolved) throw new Error("unavailable");
|
|
1719
1782
|
const signal = AbortSignal.any([
|
|
1720
1783
|
request.signal,
|
|
1721
1784
|
lifetime.signal,
|
|
1722
1785
|
AbortSignal.timeout(2e4)
|
|
1723
1786
|
]);
|
|
1724
|
-
const
|
|
1787
|
+
const sessionId = SessionId(input.sessionId);
|
|
1788
|
+
const execute = async () => {
|
|
1789
|
+
signal.throwIfAborted();
|
|
1790
|
+
let session;
|
|
1791
|
+
if (action) {
|
|
1792
|
+
const resolved = await ctx.sessionController.resolveAgent(sessionId);
|
|
1793
|
+
if ("error" in resolved) throw new Error("unavailable");
|
|
1794
|
+
signal.throwIfAborted();
|
|
1795
|
+
session = resolved.agent.session;
|
|
1796
|
+
} else session = await inspectSession(ctx, sessionId, signal);
|
|
1797
|
+
return list(session, signal, action, requested, input.summary === true);
|
|
1798
|
+
};
|
|
1799
|
+
const value = action ? await execute() : await read(signal, execute);
|
|
1725
1800
|
if (action && !("completed" in value)) throw new Error("stale_request");
|
|
1726
1801
|
return reply({
|
|
1727
1802
|
ok: true,
|
|
@@ -5062,8 +5137,6 @@ const name$1 = "dungsil-vcs-settings";
|
|
|
5062
5137
|
const inject$1 = [
|
|
5063
5138
|
"connection",
|
|
5064
5139
|
"configEditor",
|
|
5065
|
-
"sessionController",
|
|
5066
|
-
"workingDirectory",
|
|
5067
5140
|
"subprocess",
|
|
5068
5141
|
"sandbox",
|
|
5069
5142
|
"sandboxPolicy"
|
|
@@ -5143,12 +5216,6 @@ function apply$1(ctx) {
|
|
|
5143
5216
|
value: snapshot()
|
|
5144
5217
|
};
|
|
5145
5218
|
} else {
|
|
5146
|
-
const sessionId = body.payload?.sessionId;
|
|
5147
|
-
if (typeof sessionId !== "string" || !sessionId || sessionId.length > 256) throw new Error("invalid");
|
|
5148
|
-
const resolved = await ctx.sessionController.resolveAgent(SessionId(sessionId));
|
|
5149
|
-
if ("error" in resolved) throw new Error("unavailable");
|
|
5150
|
-
const { agent } = resolved;
|
|
5151
|
-
const cwd = await ctx.workingDirectory.ensure(agent, signal);
|
|
5152
5219
|
const value = await inspectCliStatus(async (executable, args) => {
|
|
5153
5220
|
const bounded = AbortSignal.any([signal, AbortSignal.timeout(1e4)]);
|
|
5154
5221
|
let path;
|
|
@@ -5159,14 +5226,14 @@ function apply$1(ctx) {
|
|
|
5159
5226
|
throw error;
|
|
5160
5227
|
}
|
|
5161
5228
|
const argv = [path, ...args];
|
|
5162
|
-
const policy = ctx.sandboxPolicy.resolve(
|
|
5229
|
+
const policy = ctx.sandboxPolicy.resolve();
|
|
5163
5230
|
const command = policy.mode === "danger-full-access" ? argv : (await ctx.sandbox.confine(argv, {
|
|
5164
5231
|
...policy,
|
|
5165
5232
|
mode: policy.mode
|
|
5166
5233
|
}, bounded)).argv;
|
|
5167
5234
|
const child = ctx.subprocess.spawn({
|
|
5168
5235
|
argv: command,
|
|
5169
|
-
cwd,
|
|
5236
|
+
cwd: homedir(),
|
|
5170
5237
|
signal: bounded,
|
|
5171
5238
|
graceMs: 1e3,
|
|
5172
5239
|
env: {
|
|
@@ -5205,7 +5272,6 @@ function apply$1(ctx) {
|
|
|
5205
5272
|
}
|
|
5206
5273
|
});
|
|
5207
5274
|
signal.throwIfAborted();
|
|
5208
|
-
if (ctx.workingDirectory.get(agent.session) !== cwd) throw new Error("conflict");
|
|
5209
5275
|
result = {
|
|
5210
5276
|
ok: true,
|
|
5211
5277
|
value
|
|
@@ -5241,6 +5307,9 @@ const inject = [
|
|
|
5241
5307
|
"workingDirectory"
|
|
5242
5308
|
];
|
|
5243
5309
|
function apply(ctx, config) {
|
|
5310
|
+
const lifetime = new AbortController();
|
|
5311
|
+
const read = readQueue(ctx.subprocess);
|
|
5312
|
+
ctx.effect(() => () => lifetime.abort(), "ui-vcs: cancel context reads");
|
|
5244
5313
|
ctx.plugin(pull_requests_exports, config);
|
|
5245
5314
|
ctx.plugin(commands_exports, config);
|
|
5246
5315
|
ctx.plugin(settings_exports);
|
|
@@ -5265,100 +5334,104 @@ function apply(ctx, config) {
|
|
|
5265
5334
|
if (body.method !== "dungsil-vcs/describe") throw new Error("Invalid method");
|
|
5266
5335
|
const sessionId = body.payload?.sessionId;
|
|
5267
5336
|
if (typeof sessionId !== "string" || !sessionId) throw new Error("Invalid session");
|
|
5268
|
-
const
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
const cwd = await ctx.workingDirectory.ensure(agent, signal);
|
|
5273
|
-
const git = async (args) => {
|
|
5274
|
-
const argv = [
|
|
5275
|
-
await ctx.subprocess.resolveExecutable("git", void 0, signal),
|
|
5276
|
-
"-c",
|
|
5277
|
-
"core.fsmonitor=false",
|
|
5278
|
-
"-c",
|
|
5279
|
-
"core.hooksPath=/dev/null",
|
|
5280
|
-
...args
|
|
5281
|
-
];
|
|
5282
|
-
const policy = ctx.sandboxPolicy.resolve({ session: agent.session });
|
|
5283
|
-
const command = policy.mode === "danger-full-access" ? argv : (await ctx.sandbox.confine(argv, {
|
|
5284
|
-
...policy,
|
|
5285
|
-
mode: policy.mode
|
|
5286
|
-
}, signal)).argv;
|
|
5287
|
-
const child = ctx.subprocess.spawn({
|
|
5288
|
-
argv: command,
|
|
5289
|
-
cwd,
|
|
5290
|
-
signal,
|
|
5291
|
-
graceMs: 1e3,
|
|
5292
|
-
env: {
|
|
5293
|
-
...Object.fromEntries(Object.keys(process.env).filter((key) => key.startsWith("GIT_")).map((key) => [key, void 0])),
|
|
5294
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
5295
|
-
GIT_OPTIONAL_LOCKS: "0",
|
|
5296
|
-
GIT_NO_LAZY_FETCH: "1"
|
|
5297
|
-
},
|
|
5298
|
-
stdio: {
|
|
5299
|
-
stdin: "ignore",
|
|
5300
|
-
stdout: { maxBytes: 65536 },
|
|
5301
|
-
stderr: { maxBytes: 4096 }
|
|
5302
|
-
}
|
|
5303
|
-
});
|
|
5304
|
-
try {
|
|
5305
|
-
const outcome = await child.done;
|
|
5306
|
-
signal.throwIfAborted();
|
|
5307
|
-
const output = child.collected.stdout.readFrom(0);
|
|
5308
|
-
if (outcome.exitCode !== 0 || output.lossy) throw new Error("Git read failed");
|
|
5309
|
-
return output.text.replace(/\r?\n$/, "");
|
|
5310
|
-
} finally {
|
|
5311
|
-
child.terminate();
|
|
5312
|
-
await child.waitForExit();
|
|
5313
|
-
}
|
|
5314
|
-
};
|
|
5315
|
-
const gitDir = await git([
|
|
5316
|
-
"rev-parse",
|
|
5317
|
-
"--path-format=absolute",
|
|
5318
|
-
"--git-dir"
|
|
5319
|
-
]);
|
|
5320
|
-
const commonDir = await git([
|
|
5321
|
-
"rev-parse",
|
|
5322
|
-
"--path-format=absolute",
|
|
5323
|
-
"--git-common-dir"
|
|
5337
|
+
const signal = AbortSignal.any([
|
|
5338
|
+
request.signal,
|
|
5339
|
+
lifetime.signal,
|
|
5340
|
+
AbortSignal.timeout(1e4)
|
|
5324
5341
|
]);
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
const
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5342
|
+
result = await read(signal, async () => {
|
|
5343
|
+
const session = await inspectSession(ctx, SessionId(sessionId), signal);
|
|
5344
|
+
const cwd = ctx.workingDirectory.get(session);
|
|
5345
|
+
const git = async (args) => {
|
|
5346
|
+
const argv = [
|
|
5347
|
+
await ctx.subprocess.resolveExecutable("git", void 0, signal),
|
|
5348
|
+
"-c",
|
|
5349
|
+
"core.fsmonitor=false",
|
|
5350
|
+
"-c",
|
|
5351
|
+
"core.hooksPath=/dev/null",
|
|
5352
|
+
...args
|
|
5353
|
+
];
|
|
5354
|
+
const policy = ctx.sandboxPolicy.resolve({ session });
|
|
5355
|
+
const command = policy.mode === "danger-full-access" ? argv : (await ctx.sandbox.confine(argv, {
|
|
5356
|
+
...policy,
|
|
5357
|
+
mode: policy.mode
|
|
5358
|
+
}, signal)).argv;
|
|
5359
|
+
const child = ctx.subprocess.spawn({
|
|
5360
|
+
argv: command,
|
|
5361
|
+
cwd,
|
|
5362
|
+
signal,
|
|
5363
|
+
graceMs: 1e3,
|
|
5364
|
+
env: {
|
|
5365
|
+
...Object.fromEntries(Object.keys(process.env).filter((key) => key.startsWith("GIT_")).map((key) => [key, void 0])),
|
|
5366
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
5367
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
5368
|
+
GIT_NO_LAZY_FETCH: "1"
|
|
5369
|
+
},
|
|
5370
|
+
stdio: {
|
|
5371
|
+
stdin: "ignore",
|
|
5372
|
+
stdout: { maxBytes: 65536 },
|
|
5373
|
+
stderr: { maxBytes: 4096 }
|
|
5374
|
+
}
|
|
5375
|
+
});
|
|
5376
|
+
try {
|
|
5377
|
+
const outcome = await child.done;
|
|
5378
|
+
signal.throwIfAborted();
|
|
5379
|
+
const output = child.collected.stdout.readFrom(0);
|
|
5380
|
+
if (outcome.exitCode !== 0 || output.lossy) throw new Error("Git read failed");
|
|
5381
|
+
return output.text.replace(/\r?\n$/, "");
|
|
5382
|
+
} finally {
|
|
5383
|
+
child.terminate();
|
|
5384
|
+
await child.waitForExit();
|
|
5385
|
+
}
|
|
5386
|
+
};
|
|
5387
|
+
const gitDir = await git([
|
|
5388
|
+
"rev-parse",
|
|
5389
|
+
"--path-format=absolute",
|
|
5390
|
+
"--git-dir"
|
|
5332
5391
|
]);
|
|
5333
|
-
const
|
|
5334
|
-
"
|
|
5335
|
-
"--format
|
|
5336
|
-
|
|
5392
|
+
const commonDir = await git([
|
|
5393
|
+
"rev-parse",
|
|
5394
|
+
"--path-format=absolute",
|
|
5395
|
+
"--git-common-dir"
|
|
5337
5396
|
]);
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5397
|
+
let operation = "pr";
|
|
5398
|
+
if (gitDir === commonDir) {
|
|
5399
|
+
const branch = await git([
|
|
5400
|
+
"symbolic-ref",
|
|
5401
|
+
"--quiet",
|
|
5402
|
+
"--short",
|
|
5403
|
+
"HEAD"
|
|
5404
|
+
]);
|
|
5405
|
+
const upstream = await git([
|
|
5406
|
+
"for-each-ref",
|
|
5407
|
+
"--format=%(upstream:remotename)",
|
|
5408
|
+
`refs/heads/${branch}`
|
|
5409
|
+
]);
|
|
5410
|
+
const remotes = (await git(["remote"])).split("\n").filter(Boolean);
|
|
5411
|
+
const remote = upstream && upstream !== "." ? upstream : remotes.includes("origin") ? "origin" : remotes.length === 1 ? remotes[0] : void 0;
|
|
5412
|
+
if (!remote) throw new Error("Default remote unknown");
|
|
5413
|
+
operation = await git([
|
|
5414
|
+
"symbolic-ref",
|
|
5415
|
+
"--quiet",
|
|
5416
|
+
`refs/remotes/${remote}/HEAD`
|
|
5417
|
+
]) === `refs/remotes/${remote}/${branch}` ? "push" : "pr";
|
|
5359
5418
|
}
|
|
5360
|
-
|
|
5361
|
-
|
|
5419
|
+
const headSha = await git(["rev-parse", "HEAD"]);
|
|
5420
|
+
const dirty = !!await git([
|
|
5421
|
+
"status",
|
|
5422
|
+
"--porcelain",
|
|
5423
|
+
"--untracked-files=normal"
|
|
5424
|
+
]);
|
|
5425
|
+
if (ctx.workingDirectory.get(session) !== cwd) throw new Error("Working directory changed");
|
|
5426
|
+
return {
|
|
5427
|
+
ok: true,
|
|
5428
|
+
value: {
|
|
5429
|
+
operation,
|
|
5430
|
+
headSha,
|
|
5431
|
+
dirty
|
|
5432
|
+
}
|
|
5433
|
+
};
|
|
5434
|
+
});
|
|
5362
5435
|
} catch {
|
|
5363
5436
|
result = failure;
|
|
5364
5437
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dungsil/dsh-plugin-ui-vcs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Git actions and PR/MR review for GitHub, GitLab, Gitea and Forgejo.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"@deepseek-ai/dsh-subprocess-local": "0.2.1-rc.5",
|
|
53
53
|
"@deepseek-ai/dsh-working-directory": "0.2.1-rc.5",
|
|
54
54
|
"@deepseek-ai/schemastery": "~3.18.5",
|
|
55
|
+
"@dungsil/dsh-async": "0.0.0",
|
|
55
56
|
"@dungsil/dsh-tsconfig": "0.0.0",
|
|
56
57
|
"@dungsil/dsh-ui": "0.0.0",
|
|
57
58
|
"@types/react": "18.3.31",
|