@supanadit/morse-web 0.9.4 → 0.10.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/server.mjs +276 -4
- package/dist/server.mjs.map +3 -3
- package/dist/ui/index.html +1 -1
- package/dist/ui/main-DHSGAX7S.js +98 -0
- package/dist/ui/webview.manifest.json +2 -2
- package/package.json +1 -1
- package/dist/ui/main-OAXMIIXE.js +0 -98
package/dist/server.mjs
CHANGED
|
@@ -1706,11 +1706,11 @@ function promptDescription(raw) {
|
|
|
1706
1706
|
}
|
|
1707
1707
|
body = raw.slice(frontmatter[0].length);
|
|
1708
1708
|
}
|
|
1709
|
-
const
|
|
1710
|
-
if (
|
|
1709
|
+
const firstLine2 = body.split("\n").find((line) => line.trim().length > 0);
|
|
1710
|
+
if (firstLine2 === void 0) {
|
|
1711
1711
|
return void 0;
|
|
1712
1712
|
}
|
|
1713
|
-
const trimmed =
|
|
1713
|
+
const trimmed = firstLine2.trim();
|
|
1714
1714
|
return trimmed.length > 60 ? `${trimmed.slice(0, 60)}...` : trimmed;
|
|
1715
1715
|
}
|
|
1716
1716
|
function unquote(value) {
|
|
@@ -2383,6 +2383,11 @@ var SessionTranscriptStore = class {
|
|
|
2383
2383
|
projectors = /* @__PURE__ */ new Map();
|
|
2384
2384
|
cursors = /* @__PURE__ */ new Map();
|
|
2385
2385
|
listeners = /* @__PURE__ */ new Set();
|
|
2386
|
+
/**
|
|
2387
|
+
* Agent events already projected, by instance (see `apply`). A weak set, so
|
|
2388
|
+
* a projected record never keeps itself alive after it has been fanned out.
|
|
2389
|
+
*/
|
|
2390
|
+
applied = /* @__PURE__ */ new WeakSet();
|
|
2386
2391
|
subscribe(listener) {
|
|
2387
2392
|
this.listeners.add(listener);
|
|
2388
2393
|
return () => {
|
|
@@ -2402,7 +2407,25 @@ var SessionTranscriptStore = class {
|
|
|
2402
2407
|
setHistoryCursor(sessionKey, cursor) {
|
|
2403
2408
|
this.cursors.set(sessionKey, cursor);
|
|
2404
2409
|
}
|
|
2410
|
+
/**
|
|
2411
|
+
* Feeds one agent event in. Idempotent per event *instance*: the registry
|
|
2412
|
+
* fans a single tagged event out to every subscribed controller, and each of
|
|
2413
|
+
* them writes it here, so a host with more than one connected client would
|
|
2414
|
+
* otherwise project the same stream once per connection. The symptoms are
|
|
2415
|
+
* unmistakable — a delta doubled into "therethere", a second `message_end`
|
|
2416
|
+
* creating a duplicate prose row, and a second `tool-start` re-binding the
|
|
2417
|
+
* call id so the first tool item never receives its `tool-end` and stays
|
|
2418
|
+
* `running` forever ("Working for …" never settling).
|
|
2419
|
+
*
|
|
2420
|
+
* Identity is the right key because `SessionRegistry.emit` passes the same
|
|
2421
|
+
* event object to every listener: the first writer projects it, the rest are
|
|
2422
|
+
* no-ops, whatever the connection count.
|
|
2423
|
+
*/
|
|
2405
2424
|
apply(sessionKey, event) {
|
|
2425
|
+
if (this.applied.has(event)) {
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
this.applied.add(event);
|
|
2406
2429
|
this.projectorFor(sessionKey).apply(event);
|
|
2407
2430
|
}
|
|
2408
2431
|
/** Replays a resumed session's history into an empty transcript. */
|
|
@@ -3253,7 +3276,7 @@ var HostSessionController = class {
|
|
|
3253
3276
|
this.agentReady = false;
|
|
3254
3277
|
this.agentError = event.message;
|
|
3255
3278
|
this.agentFailure = hintOnly(this.options.agentHint);
|
|
3256
|
-
this.options.transcripts.
|
|
3279
|
+
this.options.transcripts.apply(sessionKey, event);
|
|
3257
3280
|
this.emitState();
|
|
3258
3281
|
return;
|
|
3259
3282
|
default:
|
|
@@ -3936,6 +3959,169 @@ async function readGitDiff(cwd, requested) {
|
|
|
3936
3959
|
);
|
|
3937
3960
|
return { path: rel, diff: diff ?? "" };
|
|
3938
3961
|
}
|
|
3962
|
+
async function readCommitFiles(cwd, requested) {
|
|
3963
|
+
const hash = requested.trim();
|
|
3964
|
+
if (!isCommitHash(hash)) {
|
|
3965
|
+
return { isRepo: false, hash, files: [] };
|
|
3966
|
+
}
|
|
3967
|
+
const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
3968
|
+
if (inside?.trim() !== "true") {
|
|
3969
|
+
return { isRepo: false, hash, files: [] };
|
|
3970
|
+
}
|
|
3971
|
+
const raw = await gitText(
|
|
3972
|
+
["show", "--name-status", "--format=", "-z", "--find-renames", "--relative", hash],
|
|
3973
|
+
cwd
|
|
3974
|
+
);
|
|
3975
|
+
return { isRepo: true, hash, files: parseNameStatus(raw ?? "") };
|
|
3976
|
+
}
|
|
3977
|
+
async function readCommitDiff(cwd, hash, requested) {
|
|
3978
|
+
const rel = toPosix2(relative3(cwd, resolveWithin(cwd, requested)));
|
|
3979
|
+
if (!isCommitHash(hash.trim())) {
|
|
3980
|
+
return { path: rel, diff: "" };
|
|
3981
|
+
}
|
|
3982
|
+
const diff = await gitText(
|
|
3983
|
+
["show", "--format=", "--no-color", "--no-ext-diff", "-U3", "--relative", hash.trim(), "--", rel],
|
|
3984
|
+
cwd
|
|
3985
|
+
);
|
|
3986
|
+
return { path: rel, diff: diff ?? "" };
|
|
3987
|
+
}
|
|
3988
|
+
function isCommitHash(hash) {
|
|
3989
|
+
return /^[0-9a-fA-F]{4,40}$/.test(hash);
|
|
3990
|
+
}
|
|
3991
|
+
function parseNameStatus(raw) {
|
|
3992
|
+
const tokens = raw.split("\0");
|
|
3993
|
+
const files = [];
|
|
3994
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
3995
|
+
const status = tokens[index];
|
|
3996
|
+
if (!status) {
|
|
3997
|
+
continue;
|
|
3998
|
+
}
|
|
3999
|
+
const letter = status[0] ?? "";
|
|
4000
|
+
const rename = letter === "R" || letter === "C";
|
|
4001
|
+
const path = tokens[index + (rename ? 2 : 1)];
|
|
4002
|
+
index += rename ? 2 : 1;
|
|
4003
|
+
if (path) {
|
|
4004
|
+
files.push({ path: toPosix2(path), status: `${letter} ` });
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
return files;
|
|
4008
|
+
}
|
|
4009
|
+
async function stageGitPaths(cwd, requested) {
|
|
4010
|
+
const paths = resolvePaths(cwd, requested);
|
|
4011
|
+
if (paths.length > 0) {
|
|
4012
|
+
await gitRun(["add", "--", ...paths], cwd);
|
|
4013
|
+
}
|
|
4014
|
+
return readGitStatus(cwd);
|
|
4015
|
+
}
|
|
4016
|
+
async function unstageGitPaths(cwd, requested) {
|
|
4017
|
+
const paths = resolvePaths(cwd, requested);
|
|
4018
|
+
if (paths.length > 0) {
|
|
4019
|
+
const reset = await gitRun(["reset", "--quiet", "HEAD", "--", ...paths], cwd);
|
|
4020
|
+
if (!reset) {
|
|
4021
|
+
await gitRun(["rm", "--cached", "--quiet", "--", ...paths], cwd);
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
return readGitStatus(cwd);
|
|
4025
|
+
}
|
|
4026
|
+
async function readGitSync(cwd) {
|
|
4027
|
+
const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
4028
|
+
if (inside?.trim() !== "true") {
|
|
4029
|
+
return { isRepo: false, ahead: 0, behind: 0 };
|
|
4030
|
+
}
|
|
4031
|
+
const [branch, upstream] = await Promise.all([
|
|
4032
|
+
gitText(["rev-parse", "--abbrev-ref", "HEAD"], cwd),
|
|
4033
|
+
gitText(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"], cwd)
|
|
4034
|
+
]);
|
|
4035
|
+
const name = branch?.trim() || void 0;
|
|
4036
|
+
const tracked = upstream?.trim() || void 0;
|
|
4037
|
+
if (tracked === void 0) {
|
|
4038
|
+
return { isRepo: true, branch: name, ahead: 0, behind: 0 };
|
|
4039
|
+
}
|
|
4040
|
+
const counts = await gitText(
|
|
4041
|
+
["rev-list", "--left-right", "--count", `${tracked}...HEAD`],
|
|
4042
|
+
cwd
|
|
4043
|
+
);
|
|
4044
|
+
const [behind, ahead] = parseCounts(counts ?? "");
|
|
4045
|
+
return { isRepo: true, branch: name, upstream: tracked, ahead, behind };
|
|
4046
|
+
}
|
|
4047
|
+
async function pullGit(cwd) {
|
|
4048
|
+
const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
4049
|
+
if (inside?.trim() === "true") {
|
|
4050
|
+
await gitRun(["pull", "--no-edit"], cwd, 18e4);
|
|
4051
|
+
}
|
|
4052
|
+
return readGitSync(cwd);
|
|
4053
|
+
}
|
|
4054
|
+
async function pushGit(cwd) {
|
|
4055
|
+
const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
4056
|
+
if (inside?.trim() === "true") {
|
|
4057
|
+
await gitRun(["push", "--follow-tags"], cwd, 18e4);
|
|
4058
|
+
}
|
|
4059
|
+
return readGitSync(cwd);
|
|
4060
|
+
}
|
|
4061
|
+
async function readGitBranches(cwd) {
|
|
4062
|
+
const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
4063
|
+
if (inside?.trim() !== "true") {
|
|
4064
|
+
return { isRepo: false, local: [], remote: [], tags: [] };
|
|
4065
|
+
}
|
|
4066
|
+
const [current, local, remote, tags] = await Promise.all([
|
|
4067
|
+
gitText(["rev-parse", "--abbrev-ref", "HEAD"], cwd),
|
|
4068
|
+
gitText(["for-each-ref", "--format=%(refname:short)", "refs/heads"], cwd),
|
|
4069
|
+
gitText(["for-each-ref", "--format=%(refname:short)", "refs/remotes"], cwd),
|
|
4070
|
+
gitText(["for-each-ref", "--format=%(refname:short)", "refs/tags"], cwd)
|
|
4071
|
+
]);
|
|
4072
|
+
return {
|
|
4073
|
+
isRepo: true,
|
|
4074
|
+
current: current?.trim() || void 0,
|
|
4075
|
+
local: nonEmptyLines(local),
|
|
4076
|
+
remote: nonEmptyLines(remote).filter((name) => !name.endsWith("/HEAD")),
|
|
4077
|
+
tags: nonEmptyLines(tags)
|
|
4078
|
+
};
|
|
4079
|
+
}
|
|
4080
|
+
async function commitGit(cwd, message) {
|
|
4081
|
+
const text = message.trim();
|
|
4082
|
+
if (text.length === 0) {
|
|
4083
|
+
return { ok: false, message: "A commit needs a message." };
|
|
4084
|
+
}
|
|
4085
|
+
const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
4086
|
+
if (inside?.trim() !== "true") {
|
|
4087
|
+
return { ok: false, message: "This project is not a git repository." };
|
|
4088
|
+
}
|
|
4089
|
+
const result = await gitExec(["commit", "-m", text], cwd, 6e4);
|
|
4090
|
+
return { ok: result.ok, message: result.ok ? void 0 : firstLine(result.output) };
|
|
4091
|
+
}
|
|
4092
|
+
async function checkoutGit(cwd, branch, create) {
|
|
4093
|
+
const name = branch.trim();
|
|
4094
|
+
if (!isBranchName(name)) {
|
|
4095
|
+
return { ok: false, message: "That is not a usable branch name." };
|
|
4096
|
+
}
|
|
4097
|
+
const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
4098
|
+
if (inside?.trim() !== "true") {
|
|
4099
|
+
return { ok: false, message: "This project is not a git repository." };
|
|
4100
|
+
}
|
|
4101
|
+
const result = await gitExec(
|
|
4102
|
+
create ? ["checkout", "-b", name] : ["checkout", name],
|
|
4103
|
+
cwd,
|
|
4104
|
+
6e4
|
|
4105
|
+
);
|
|
4106
|
+
return { ok: result.ok, message: result.ok ? void 0 : firstLine(result.output) };
|
|
4107
|
+
}
|
|
4108
|
+
function isBranchName(name) {
|
|
4109
|
+
return name.length > 0 && !name.startsWith("-") && !name.includes("..") && !name.includes("@{") && !name.endsWith("/") && !/\s/.test(name) && !/[~^:?*\[\\]/.test(name);
|
|
4110
|
+
}
|
|
4111
|
+
function firstLine(output) {
|
|
4112
|
+
const line = output.split("\n").find((candidate) => candidate.trim().length > 0)?.trim();
|
|
4113
|
+
if (line === void 0) {
|
|
4114
|
+
return void 0;
|
|
4115
|
+
}
|
|
4116
|
+
return line.length > 200 ? `${line.slice(0, 197)}\u2026` : line;
|
|
4117
|
+
}
|
|
4118
|
+
function nonEmptyLines(output) {
|
|
4119
|
+
return (output ?? "").split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
4120
|
+
}
|
|
4121
|
+
function parseCounts(raw) {
|
|
4122
|
+
const [behind, ahead] = raw.trim().split(/\s+/).map((value) => Number.parseInt(value, 10));
|
|
4123
|
+
return [Number.isFinite(behind) ? behind : 0, Number.isFinite(ahead) ? ahead : 0];
|
|
4124
|
+
}
|
|
3939
4125
|
function toPosix2(path) {
|
|
3940
4126
|
return path.split("\\").join("/");
|
|
3941
4127
|
}
|
|
@@ -3983,6 +4169,37 @@ function gitText(args, cwd) {
|
|
|
3983
4169
|
);
|
|
3984
4170
|
});
|
|
3985
4171
|
}
|
|
4172
|
+
function resolvePaths(cwd, requested) {
|
|
4173
|
+
return requested.filter((path) => typeof path === "string" && path.length > 0).map((path) => toPosix2(relative3(cwd, resolveWithin(cwd, path))));
|
|
4174
|
+
}
|
|
4175
|
+
async function gitRun(args, cwd, timeout = 5e3) {
|
|
4176
|
+
return (await gitExec(args, cwd, timeout)).ok;
|
|
4177
|
+
}
|
|
4178
|
+
function gitExec(args, cwd, timeout = 5e3) {
|
|
4179
|
+
return new Promise((resolve6) => {
|
|
4180
|
+
execFile(
|
|
4181
|
+
"git",
|
|
4182
|
+
args,
|
|
4183
|
+
{
|
|
4184
|
+
cwd,
|
|
4185
|
+
timeout,
|
|
4186
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
4187
|
+
windowsHide: true,
|
|
4188
|
+
env: {
|
|
4189
|
+
...process.env,
|
|
4190
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
4191
|
+
GIT_EDITOR: "true",
|
|
4192
|
+
GIT_MERGE_AUTOEDIT: "no"
|
|
4193
|
+
}
|
|
4194
|
+
},
|
|
4195
|
+
(error, stdout, stderr) => {
|
|
4196
|
+
resolve6(
|
|
4197
|
+
error === null ? { ok: true, output: stdout.trim() } : { ok: false, output: (stderr || error.message || "").trim() }
|
|
4198
|
+
);
|
|
4199
|
+
}
|
|
4200
|
+
);
|
|
4201
|
+
});
|
|
4202
|
+
}
|
|
3986
4203
|
|
|
3987
4204
|
// ../server/src/internal/workspace/workspace-index.ts
|
|
3988
4205
|
import { execFile as execFile2 } from "node:child_process";
|
|
@@ -4242,6 +4459,54 @@ var MorseSessionFactory = class {
|
|
|
4242
4459
|
}
|
|
4243
4460
|
return readGitDiff(cwd, path);
|
|
4244
4461
|
}
|
|
4462
|
+
case "gitStage": {
|
|
4463
|
+
const cwd = this.requireWritableCwd(context);
|
|
4464
|
+
return stageGitPaths(cwd, gitPaths(args));
|
|
4465
|
+
}
|
|
4466
|
+
case "gitUnstage": {
|
|
4467
|
+
const cwd = this.requireWritableCwd(context);
|
|
4468
|
+
return unstageGitPaths(cwd, gitPaths(args));
|
|
4469
|
+
}
|
|
4470
|
+
case "gitCommitFiles": {
|
|
4471
|
+
const cwd = this.requireWritableCwd(context);
|
|
4472
|
+
const hash = typeof args?.hash === "string" ? args.hash : "";
|
|
4473
|
+
return readCommitFiles(cwd, hash);
|
|
4474
|
+
}
|
|
4475
|
+
case "gitCommitDiff": {
|
|
4476
|
+
const cwd = this.requireWritableCwd(context);
|
|
4477
|
+
const hash = typeof args?.hash === "string" ? args.hash : "";
|
|
4478
|
+
const path = typeof args?.path === "string" ? args.path : "";
|
|
4479
|
+
if (path.length === 0) {
|
|
4480
|
+
throw new UnsupportedByHostError('gitCommitDiff needs a "path" argument.');
|
|
4481
|
+
}
|
|
4482
|
+
return readCommitDiff(cwd, hash, path);
|
|
4483
|
+
}
|
|
4484
|
+
case "gitSync": {
|
|
4485
|
+
const cwd = this.requireWritableCwd(context);
|
|
4486
|
+
return readGitSync(cwd);
|
|
4487
|
+
}
|
|
4488
|
+
case "gitPull": {
|
|
4489
|
+
const cwd = this.requireWritableCwd(context);
|
|
4490
|
+
return pullGit(cwd);
|
|
4491
|
+
}
|
|
4492
|
+
case "gitPush": {
|
|
4493
|
+
const cwd = this.requireWritableCwd(context);
|
|
4494
|
+
return pushGit(cwd);
|
|
4495
|
+
}
|
|
4496
|
+
case "gitCommit": {
|
|
4497
|
+
const cwd = this.requireWritableCwd(context);
|
|
4498
|
+
const message = typeof args?.message === "string" ? args.message : "";
|
|
4499
|
+
return commitGit(cwd, message);
|
|
4500
|
+
}
|
|
4501
|
+
case "gitBranches": {
|
|
4502
|
+
const cwd = this.requireWritableCwd(context);
|
|
4503
|
+
return readGitBranches(cwd);
|
|
4504
|
+
}
|
|
4505
|
+
case "gitCheckout": {
|
|
4506
|
+
const cwd = this.requireWritableCwd(context);
|
|
4507
|
+
const branch = typeof args?.branch === "string" ? args.branch : "";
|
|
4508
|
+
return checkoutGit(cwd, branch, args?.create === true);
|
|
4509
|
+
}
|
|
4245
4510
|
default:
|
|
4246
4511
|
throw new UnsupportedByHostError(`This host does not support "${command}".`);
|
|
4247
4512
|
}
|
|
@@ -4276,6 +4541,13 @@ MorseSessionFactory = __decorateClass([
|
|
|
4276
4541
|
__decorateParam(3, Inject3(MORSE_LOGGER)),
|
|
4277
4542
|
__decorateParam(4, Inject3(MORSE_CONFIG))
|
|
4278
4543
|
], MorseSessionFactory);
|
|
4544
|
+
function gitPaths(args) {
|
|
4545
|
+
const paths = args?.paths;
|
|
4546
|
+
if (!Array.isArray(paths)) {
|
|
4547
|
+
return [];
|
|
4548
|
+
}
|
|
4549
|
+
return paths.filter((path) => typeof path === "string" && path.length > 0);
|
|
4550
|
+
}
|
|
4279
4551
|
|
|
4280
4552
|
// ../server/src/internal/ws/morse.gateway.ts
|
|
4281
4553
|
var MorseGateway = class {
|