glimmervoid 0.32.1 → 0.33.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.
Files changed (26) hide show
  1. package/dist/bin/glimmervoid.js +14 -9
  2. package/dist/chunks/{adapters-Bn-dQ02i.js → adapters-C9gI2MHS.js} +1 -1
  3. package/dist/chunks/{adapters-BS_7o8fj.js → adapters-CUBEhjOR.js} +3 -3
  4. package/dist/chunks/agent-api-cli-CRJYxzVU.js +2 -0
  5. package/dist/chunks/{agent-api-cli-BR0GVDn6.js → agent-api-cli-DuT7437B.js} +3 -3
  6. package/dist/chunks/{agent-api-core-DUiWHSHg.js → agent-api-core-Ddg15j2j.js} +1 -1
  7. package/dist/chunks/{agent-setup-cli-BqYcwfOs.js → agent-setup-cli-CFielF2o.js} +1 -1
  8. package/dist/chunks/{config-B6BkTOcE.js → config-Cneb8Ujj.js} +1 -11
  9. package/dist/chunks/{config-store-aZpWeX92.js → config-store-DYsZJDux.js} +2 -4
  10. package/dist/chunks/config-store-DaRS2c2d.js +2 -0
  11. package/dist/chunks/{contracts-DzDg-Z9I.js → contracts-DWv6q8ir.js} +3 -41
  12. package/dist/chunks/{grok-7Z5KcgcY.js → grok-BYKnBgdr.js} +2 -1
  13. package/dist/chunks/{knowledge-graph-cli-D5Gf87Y7.js → knowledge-graph-cli-C7G-x5sY.js} +1 -1
  14. package/dist/chunks/{main-BG6iHwoY.js → main-J6y2RTri.js} +1034 -981
  15. package/dist/chunks/{pair-cli-DQkrt_-s.js → pair-cli-BuyzST-U.js} +2 -2
  16. package/dist/chunks/{remote-config-Da4C7tMG.js → remote-config-DJHVb-HU.js} +1 -1
  17. package/dist/chunks/{session-Dtkp9o3r.js → session-C7rstCmA.js} +15 -1
  18. package/dist/chunks/ssh-sign-cli-SWtTF00D.js +133 -0
  19. package/dist/chunks/ssh-signing-core-CRhVVWNW.js +49 -0
  20. package/dist/client/assets/index-CoFsm_PF.js +254 -0
  21. package/dist/client/index.html +1 -1
  22. package/dist/server/index.js +1 -1
  23. package/dist/session/sandbox-open-launcher.js +2 -1
  24. package/package.json +1 -1
  25. package/dist/chunks/config-store-C8Ry8ZFD.js +0 -2
  26. package/dist/client/assets/index-Co4S7tnh.js +0 -254
@@ -1,6 +1,6 @@
1
1
  import { r as errorMessage } from "./text-DoirglWV.js";
2
- import { l as resolveConfigPath } from "./config-store-aZpWeX92.js";
3
- import { c as defaultPairingsPath, i as REVOCATION_PROPAGATION_SECONDS, l as defaultSeenPath, n as normalizeRemoteConfig, o as createPairingsStore, s as createSeenStore } from "./remote-config-Da4C7tMG.js";
2
+ import { l as resolveConfigPath } from "./config-store-DYsZJDux.js";
3
+ import { c as defaultPairingsPath, i as REVOCATION_PROPAGATION_SECONDS, l as defaultSeenPath, n as normalizeRemoteConfig, o as createPairingsStore, s as createSeenStore } from "./remote-config-DJHVb-HU.js";
4
4
  import fs from "node:fs";
5
5
  //#region server/text-format.ts
6
6
  function trimIso(iso) {
@@ -3,7 +3,7 @@ import { i as equalsIgnoringCaseOnWindows, t as canonicalizePath } from "./paths
3
3
  import { n as isRecord } from "./coerce-NSst8oRP.js";
4
4
  import { n as unrefTimer, t as DEFAULT_TIMER_FNS } from "./timer-deps-u2DAQLok.js";
5
5
  import { a as jsonStateLoadError, d as writeJsonAtomicSync, l as sleepSync, s as loadJsonStateFileSync, u as writeJsonAtomic } from "./json-file-oEH9vt-0.js";
6
- import { s as glimmervoidHomeDir } from "./config-store-aZpWeX92.js";
6
+ import { s as glimmervoidHomeDir } from "./config-store-DYsZJDux.js";
7
7
  import fs from "node:fs";
8
8
  import path from "node:path";
9
9
  import crypto from "node:crypto";
@@ -136,6 +136,20 @@ function isSamePromptQuestion(left, right) {
136
136
  return left.text === right.text && left.multiSelect === right.multiSelect && left.options.length === right.options.length && left.options.every((option, index) => option === right.options[index]);
137
137
  }
138
138
  var AGENT_URL_ENV = "GLIMMERVOID_AGENT_URL";
139
+ var SIGN_URL_ENV = "GLIMMERVOID_SIGN_URL";
140
+ var SSH_SIGN_BODY_CAP_BYTES = 1048576;
141
+ var SshSignRequest = z.object({
142
+ namespace: z.literal("git", { error: "namespace must be git" }),
143
+ publicKey: z.string({ error: "publicKey must be a string" }).min(1, { error: "publicKey must not be empty" }).max(16384, { error: "publicKey must be at most 16384 characters" }),
144
+ payload: z.base64({ error: "payload must be base64" })
145
+ }).strict();
146
+ var SshSignReply = z.union([z.object({
147
+ ok: z.literal(true),
148
+ signature: z.string()
149
+ }).strict(), z.object({
150
+ ok: z.literal(false),
151
+ error: z.string()
152
+ }).strict()]);
139
153
  var AGENT_API_VERBS = [
140
154
  "spawn",
141
155
  "attention",
@@ -193,4 +207,4 @@ z.discriminatedUnion("ok", [z.object({
193
207
  reason: z.string()
194
208
  })]);
195
209
  //#endregion
196
- export { firstDetailLine as C, MERGEABLE_LIVE_STATES as D, KILLABLE_STATES as E, RESTARTABLE_STATES as O, isSamePromptQuestion as S, toolDetailLine as T, SessionDoc as _, AgentDispatchRequest as a, TASK_TITLE_CONTROL_CHARACTERS as b, AgentTicketRequest as c, PendingPromptDetail as d, PendingPromptQuestion as f, SESSION_DOC_PATH_MAX_CHARS as g, SESSION_DOC_BODY_CAP_BYTES as h, AgentAttentionRequest as i, STATES as k, ORCHESTRATOR_AGENT_VERBS as l, RefinedTaskTitle as m, AGENT_URL_ENV as n, AgentEditRequest as o, PendingWakeup as p, ASK_USER_QUESTION_TOOL_NAME as r, AgentSpawnRequest as s, AGENT_API_VERBS as t, PROMPT_DETAIL_HIDDEN_CHARACTERS as u, SessionSnapshot as v, toolDetailFieldValue as w, TaskTitle as x, SessionState as y };
210
+ export { KILLABLE_STATES as A, SshSignRequest as C, firstDetailLine as D, isSamePromptQuestion as E, RESTARTABLE_STATES as M, STATES as N, toolDetailFieldValue as O, SshSignReply as S, TaskTitle as T, SIGN_URL_ENV as _, AgentDispatchRequest as a, SessionSnapshot as b, AgentTicketRequest as c, PendingPromptDetail as d, PendingPromptQuestion as f, SESSION_DOC_PATH_MAX_CHARS as g, SESSION_DOC_BODY_CAP_BYTES as h, AgentAttentionRequest as i, MERGEABLE_LIVE_STATES as j, toolDetailLine as k, ORCHESTRATOR_AGENT_VERBS as l, RefinedTaskTitle as m, AGENT_URL_ENV as n, AgentEditRequest as o, PendingWakeup as p, ASK_USER_QUESTION_TOOL_NAME as r, AgentSpawnRequest as s, AGENT_API_VERBS as t, PROMPT_DETAIL_HIDDEN_CHARACTERS as u, SSH_SIGN_BODY_CAP_BYTES as v, TASK_TITLE_CONTROL_CHARACTERS as w, SessionState as x, SessionDoc as y };
@@ -0,0 +1,133 @@
1
+ import { r as errorMessage } from "./text-DoirglWV.js";
2
+ import { S as SshSignReply, _ as SIGN_URL_ENV } from "./session-C7rstCmA.js";
3
+ import { n as execFileAsync, o as spawn } from "./child-process-safe-DDELk9XC.js";
4
+ import { a as isLoopbackHostname } from "./hook-relay-core-DA_bz2KA.js";
5
+ import { t as postToAgentIngress } from "./agent-api-cli-DuT7437B.js";
6
+ import { l as parseSshPublicKey } from "./ssh-signing-core-CRhVVWNW.js";
7
+ import path from "node:path";
8
+ import fsp from "node:fs/promises";
9
+ //#region server/ssh-sign-cli.ts
10
+ var SIGN_PATH_PREFIX = "/sign/";
11
+ var DEFAULT_SSH_SIGNING_PROGRAM = "ssh-keygen";
12
+ var SIGNING_PROGRAM_LOOKUP_TIMEOUT_MS = 5e3;
13
+ var OPTIONS_TAKING_A_VALUE = /* @__PURE__ */ new Set([
14
+ "-Y",
15
+ "-n",
16
+ "-f",
17
+ "-I",
18
+ "-s",
19
+ "-O"
20
+ ]);
21
+ function readSignInvocation(args) {
22
+ const optionValues = /* @__PURE__ */ new Map();
23
+ const operands = [];
24
+ for (let index = 0; index < args.length; index += 1) {
25
+ const argument = args[index] ?? "";
26
+ if (OPTIONS_TAKING_A_VALUE.has(argument)) {
27
+ optionValues.set(argument, args[index + 1] ?? "");
28
+ index += 1;
29
+ continue;
30
+ }
31
+ if (!argument.startsWith("-")) operands.push(argument);
32
+ }
33
+ const namespace = optionValues.get("-n");
34
+ const keyFile = optionValues.get("-f");
35
+ const bufferFile = operands.at(-1);
36
+ if (optionValues.get("-Y") !== "sign" || !namespace || !keyFile || operands.length !== 1 || !bufferFile) return null;
37
+ return {
38
+ namespace,
39
+ keyFile,
40
+ bufferFile
41
+ };
42
+ }
43
+ function signEndpoint(rawUrl) {
44
+ if (!rawUrl) return null;
45
+ try {
46
+ const endpoint = new URL(rawUrl);
47
+ if (endpoint.protocol !== "http:" || !isLoopbackHostname(endpoint.hostname) || !endpoint.pathname.startsWith(SIGN_PATH_PREFIX)) return null;
48
+ const token = endpoint.searchParams.get("t") || "";
49
+ endpoint.search = "";
50
+ return {
51
+ endpoint,
52
+ token
53
+ };
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+ function parseSignReply(text) {
59
+ const unanswered = {
60
+ ok: false,
61
+ error: `Glimmervoid did not answer the signing request: ${text.slice(0, 200)}`
62
+ };
63
+ try {
64
+ const parsed = SshSignReply.safeParse(JSON.parse(text));
65
+ return parsed.success ? parsed.data : unanswered;
66
+ } catch {
67
+ return unanswered;
68
+ }
69
+ }
70
+ async function operatorSigningProgram(env) {
71
+ const envWithoutInjectedConfig = Object.fromEntries(Object.entries(env).filter(([name]) => name !== "GIT_CONFIG_PARAMETERS"));
72
+ const configuredProgram = await execFileAsync("git", [
73
+ "config",
74
+ "--type=path",
75
+ "--get",
76
+ "gpg.ssh.program"
77
+ ], {
78
+ env: envWithoutInjectedConfig,
79
+ timeout: SIGNING_PROGRAM_LOOKUP_TIMEOUT_MS,
80
+ encoding: "utf8"
81
+ }).then(({ stdout }) => stdout.trim(), () => "");
82
+ if (!configuredProgram || path.basename(configuredProgram) === "glimmervoid-ssh-sign") return DEFAULT_SSH_SIGNING_PROGRAM;
83
+ return configuredProgram;
84
+ }
85
+ function runSigningProgram(program, args, { captureStderr }) {
86
+ return new Promise((resolve) => {
87
+ const child = spawn(program, [...args], { stdio: [
88
+ "inherit",
89
+ "inherit",
90
+ captureStderr ? "pipe" : "inherit"
91
+ ] });
92
+ const stderrChunks = [];
93
+ child.stderr?.on("data", (chunk) => {
94
+ stderrChunks.push(chunk);
95
+ });
96
+ child.on("error", (error) => resolve({
97
+ exitCode: 127,
98
+ stderr: errorMessage(error)
99
+ }));
100
+ child.on("close", (exitCode) => resolve({
101
+ exitCode: exitCode ?? 1,
102
+ stderr: Buffer.concat(stderrChunks).toString("utf8").trim()
103
+ }));
104
+ });
105
+ }
106
+ async function runSshSignCli(args, env = process.env) {
107
+ const invocation = readSignInvocation(args);
108
+ const target = signEndpoint(env[SIGN_URL_ENV]);
109
+ const publicKeyText = invocation && target ? await fsp.readFile(invocation.keyFile, "utf8").catch(() => "") : "";
110
+ const publicKey = parseSshPublicKey(publicKeyText);
111
+ const signingProgram = await operatorSigningProgram(env);
112
+ if (!invocation || !target || !publicKey) return (await runSigningProgram(signingProgram, args, { captureStderr: false })).exitCode;
113
+ const localAttempt = env.SSH_AUTH_SOCK ? await runSigningProgram(signingProgram, args, { captureStderr: true }) : null;
114
+ if (localAttempt?.exitCode === 0) return 0;
115
+ const payload = await fsp.readFile(invocation.bufferFile);
116
+ const reply = await postToAgentIngress(target.endpoint, target.token, {
117
+ namespace: invocation.namespace,
118
+ publicKey: publicKeyText,
119
+ payload: payload.toString("base64")
120
+ }).then((answer) => parseSignReply(answer.text), (error) => ({
121
+ ok: false,
122
+ error: `Glimmervoid did not answer the signing request: ${errorMessage(error)}`
123
+ }));
124
+ if (reply.ok) {
125
+ await fsp.writeFile(`${invocation.bufferFile}.sig`, reply.signature);
126
+ return 0;
127
+ }
128
+ if (localAttempt?.stderr) console.error(localAttempt.stderr);
129
+ console.error(`glimmervoid: ${reply.error}`);
130
+ return 1;
131
+ }
132
+ //#endregion
133
+ export { runSshSignCli };
@@ -0,0 +1,49 @@
1
+ //#region server/core/ssh-signing-core.ts
2
+ var LITERAL_KEY_PREFIX = "key::";
3
+ var PUBLIC_KEY_LINE = /^(\S+) ([A-Za-z0-9+/]+={0,3})(?:\s.*)?$/;
4
+ var SIGNABLE_GIT_OBJECT_HEADER = /^(?:tree|object) [0-9a-f]{40}(?:[0-9a-f]{24})?\n/;
5
+ var FORWARDED_AGENT_DIR_NAME = /^(?:auth-agent|ssh-)[A-Za-z0-9_-]*$/;
6
+ function parseSshPublicKey(text) {
7
+ if (text.includes("PRIVATE KEY")) return null;
8
+ const firstLine = text.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? "";
9
+ const keyLine = firstLine.startsWith(LITERAL_KEY_PREFIX) ? firstLine.slice(5) : firstLine;
10
+ const match = PUBLIC_KEY_LINE.exec(keyLine);
11
+ if (!match?.[1] || !match[2]) return null;
12
+ return {
13
+ keyType: match[1],
14
+ keyBlob: match[2]
15
+ };
16
+ }
17
+ function formatSshPublicKey(publicKey) {
18
+ return `${publicKey.keyType} ${publicKey.keyBlob}`;
19
+ }
20
+ function isSameSshKey(left, right) {
21
+ return left.keyType === right.keyType && left.keyBlob === right.keyBlob;
22
+ }
23
+ function agentListsKey(agentListing, wantedKey) {
24
+ return agentListing.split("\n").some((line) => {
25
+ const listedKey = parseSshPublicKey(line);
26
+ return listedKey !== null && isSameSshKey(listedKey, wantedKey);
27
+ });
28
+ }
29
+ function isSignableGitObject(payload) {
30
+ return SIGNABLE_GIT_OBJECT_HEADER.test(payload.subarray(0, 128).toString("latin1"));
31
+ }
32
+ function isForwardedAgentDirName(entryName) {
33
+ return FORWARDED_AGENT_DIR_NAME.test(entryName);
34
+ }
35
+ function orderAgentSockets({ inheritedSocket, discovered }) {
36
+ const newestFirst = [...discovered].sort((left, right) => right.modifiedAtMs - left.modifiedAtMs).map((candidate) => candidate.socketPath);
37
+ const ordered = inheritedSocket ? [inheritedSocket, ...newestFirst] : newestFirst;
38
+ return ordered.filter((socketPath, index) => ordered.indexOf(socketPath) === index);
39
+ }
40
+ var COMMIT_SIGNING_SHIM_NAME = "glimmervoid-ssh-sign";
41
+ function posixSingleQuote(text) {
42
+ return `'${text.replace(/'/g, `'\\''`)}'`;
43
+ }
44
+ function commitSigningShimText({ nodePath, cliPath }) {
45
+ return `#!/bin/sh\nexec ${posixSingleQuote(nodePath)} ${posixSingleQuote(cliPath)} ssh-sign "$@"\n`;
46
+ }
47
+ var NO_AGENT_HOLDS_KEY = "no reachable ssh agent holds the signing key; connect to this machine with agent forwarding (ssh -A, or ForwardAgent yes from coder config-ssh) and commit again";
48
+ //#endregion
49
+ export { formatSshPublicKey as a, orderAgentSockets as c, commitSigningShimText as i, parseSshPublicKey as l, NO_AGENT_HOLDS_KEY as n, isForwardedAgentDirName as o, agentListsKey as r, isSignableGitObject as s, COMMIT_SIGNING_SHIM_NAME as t };