@supanadit/morse-web 0.9.5 → 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 CHANGED
@@ -1706,11 +1706,11 @@ function promptDescription(raw) {
1706
1706
  }
1707
1707
  body = raw.slice(frontmatter[0].length);
1708
1708
  }
1709
- const firstLine = body.split("\n").find((line) => line.trim().length > 0);
1710
- if (firstLine === void 0) {
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 = firstLine.trim();
1713
+ const trimmed = firstLine2.trim();
1714
1714
  return trimmed.length > 60 ? `${trimmed.slice(0, 60)}...` : trimmed;
1715
1715
  }
1716
1716
  function unquote(value) {
@@ -3959,6 +3959,169 @@ async function readGitDiff(cwd, requested) {
3959
3959
  );
3960
3960
  return { path: rel, diff: diff ?? "" };
3961
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
+ }
3962
4125
  function toPosix2(path) {
3963
4126
  return path.split("\\").join("/");
3964
4127
  }
@@ -4006,6 +4169,37 @@ function gitText(args, cwd) {
4006
4169
  );
4007
4170
  });
4008
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
+ }
4009
4203
 
4010
4204
  // ../server/src/internal/workspace/workspace-index.ts
4011
4205
  import { execFile as execFile2 } from "node:child_process";
@@ -4265,6 +4459,54 @@ var MorseSessionFactory = class {
4265
4459
  }
4266
4460
  return readGitDiff(cwd, path);
4267
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
+ }
4268
4510
  default:
4269
4511
  throw new UnsupportedByHostError(`This host does not support "${command}".`);
4270
4512
  }
@@ -4299,6 +4541,13 @@ MorseSessionFactory = __decorateClass([
4299
4541
  __decorateParam(3, Inject3(MORSE_LOGGER)),
4300
4542
  __decorateParam(4, Inject3(MORSE_CONFIG))
4301
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
+ }
4302
4551
 
4303
4552
  // ../server/src/internal/ws/morse.gateway.ts
4304
4553
  var MorseGateway = class {