@ozyman42/ozy-cli 0.4.8 → 0.4.9-darwin-arm64.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 (207) hide show
  1. package/multi-call-binary +0 -0
  2. package/package.json +11 -52
  3. package/bin/ozy +0 -191
  4. package/bin/ozy-signing-agent +0 -191
  5. package/bin/ozy-ssh-keygen +0 -191
  6. package/bin/ozy-virtual-security-key +0 -191
  7. package/dist/commands/git/hosts.js +0 -18
  8. package/dist/commands/git/hosts.js.map +0 -1
  9. package/dist/commands/git/index.js +0 -8
  10. package/dist/commands/git/index.js.map +0 -1
  11. package/dist/commands/git/setup.js +0 -145
  12. package/dist/commands/git/setup.js.map +0 -1
  13. package/dist/commands/index.js +0 -13
  14. package/dist/commands/index.js.map +0 -1
  15. package/dist/commands/npm/index.js +0 -7
  16. package/dist/commands/npm/index.js.map +0 -1
  17. package/dist/commands/npm/setup/git-remote.js +0 -66
  18. package/dist/commands/npm/setup/git-remote.js.map +0 -1
  19. package/dist/commands/npm/setup/index.js +0 -168
  20. package/dist/commands/npm/setup/index.js.map +0 -1
  21. package/dist/commands/npm/setup/npm-auth.js +0 -62
  22. package/dist/commands/npm/setup/npm-auth.js.map +0 -1
  23. package/dist/commands/npm/setup/package-json-validate.js +0 -47
  24. package/dist/commands/npm/setup/package-json-validate.js.map +0 -1
  25. package/dist/commands/npm/setup/package-registry.js +0 -20
  26. package/dist/commands/npm/setup/package-registry.js.map +0 -1
  27. package/dist/commands/npm/setup/setup.test.js +0 -95
  28. package/dist/commands/npm/setup/setup.test.js.map +0 -1
  29. package/dist/commands/npm/setup/trusted-publishing.js +0 -84
  30. package/dist/commands/npm/setup/trusted-publishing.js.map +0 -1
  31. package/dist/commands/npm/setup/workflow.js +0 -19
  32. package/dist/commands/npm/setup/workflow.js.map +0 -1
  33. package/dist/commands/ssh/get-sk-credential.js +0 -104
  34. package/dist/commands/ssh/get-sk-credential.js.map +0 -1
  35. package/dist/commands/ssh/index.js +0 -7
  36. package/dist/commands/ssh/index.js.map +0 -1
  37. package/dist/commands/upgrade.js +0 -199
  38. package/dist/commands/upgrade.js.map +0 -1
  39. package/dist/common/command.js +0 -15
  40. package/dist/common/command.js.map +0 -1
  41. package/dist/common/constants.js +0 -25
  42. package/dist/common/constants.js.map +0 -1
  43. package/dist/common/effective-modules-extensions.js +0 -13
  44. package/dist/common/effective-modules-extensions.js.map +0 -1
  45. package/dist/common/log.js +0 -4
  46. package/dist/common/log.js.map +0 -1
  47. package/dist/common/render-caller-tree.js +0 -20
  48. package/dist/common/render-caller-tree.js.map +0 -1
  49. package/dist/entrypoints/ozy-signing-agent.js +0 -48
  50. package/dist/entrypoints/ozy-signing-agent.js.map +0 -1
  51. package/dist/entrypoints/ozy-ssh-keygen.js +0 -9
  52. package/dist/entrypoints/ozy-ssh-keygen.js.map +0 -1
  53. package/dist/entrypoints/ozy-virtual-security-key.js +0 -18
  54. package/dist/entrypoints/ozy-virtual-security-key.js.map +0 -1
  55. package/dist/entrypoints/ozy.js +0 -3
  56. package/dist/entrypoints/ozy.js.map +0 -1
  57. package/dist/index.js +0 -2
  58. package/dist/index.js.map +0 -1
  59. package/dist/internal/thing.js +0 -4
  60. package/dist/internal/thing.js.map +0 -1
  61. package/dist/modules/cli/agent-client/impl.js +0 -109
  62. package/dist/modules/cli/agent-client/impl.js.map +0 -1
  63. package/dist/modules/cli/agent-client/interface.js +0 -11
  64. package/dist/modules/cli/agent-client/interface.js.map +0 -1
  65. package/dist/modules/cli/git/impl.js +0 -124
  66. package/dist/modules/cli/git/impl.js.map +0 -1
  67. package/dist/modules/cli/git/interface.js +0 -3
  68. package/dist/modules/cli/git/interface.js.map +0 -1
  69. package/dist/modules/cli/git/submodule/fix.js +0 -257
  70. package/dist/modules/cli/git/submodule/fix.js.map +0 -1
  71. package/dist/modules/cli/git/submodule/git-state.js +0 -154
  72. package/dist/modules/cli/git/submodule/git-state.js.map +0 -1
  73. package/dist/modules/cli/git/submodule/gitmodules.js +0 -113
  74. package/dist/modules/cli/git/submodule/gitmodules.js.map +0 -1
  75. package/dist/modules/cli/git/submodule/printer.js +0 -232
  76. package/dist/modules/cli/git/submodule/printer.js.map +0 -1
  77. package/dist/modules/cli/git/submodule/resolve-git-dir.js +0 -16
  78. package/dist/modules/cli/git/submodule/resolve-git-dir.js.map +0 -1
  79. package/dist/modules/cli/github/impl.js +0 -137
  80. package/dist/modules/cli/github/impl.js.map +0 -1
  81. package/dist/modules/cli/github/interface.js +0 -2
  82. package/dist/modules/cli/github/interface.js.map +0 -1
  83. package/dist/modules/cli/index.js +0 -9
  84. package/dist/modules/cli/index.js.map +0 -1
  85. package/dist/modules/common/crypto/impl.js +0 -133
  86. package/dist/modules/common/crypto/impl.js.map +0 -1
  87. package/dist/modules/common/crypto/interface.js +0 -2
  88. package/dist/modules/common/crypto/interface.js.map +0 -1
  89. package/dist/modules/common/index.js +0 -10
  90. package/dist/modules/common/index.js.map +0 -1
  91. package/dist/modules/common/kep-map-store/impl.js +0 -42
  92. package/dist/modules/common/kep-map-store/impl.js.map +0 -1
  93. package/dist/modules/common/kep-map-store/interface.js +0 -2
  94. package/dist/modules/common/kep-map-store/interface.js.map +0 -1
  95. package/dist/modules/common/os-platform/caller-info.js +0 -132
  96. package/dist/modules/common/os-platform/caller-info.js.map +0 -1
  97. package/dist/modules/common/os-platform/impl.js +0 -95
  98. package/dist/modules/common/os-platform/impl.js.map +0 -1
  99. package/dist/modules/common/os-platform/interface.js +0 -2
  100. package/dist/modules/common/os-platform/interface.js.map +0 -1
  101. package/dist/modules/common/os-platform/virtual-hid/index.js +0 -12
  102. package/dist/modules/common/os-platform/virtual-hid/index.js.map +0 -1
  103. package/dist/modules/common/os-platform/virtual-hid/linux.js +0 -5
  104. package/dist/modules/common/os-platform/virtual-hid/linux.js.map +0 -1
  105. package/dist/modules/common/os-platform/virtual-hid/mac.js +0 -119
  106. package/dist/modules/common/os-platform/virtual-hid/mac.js.map +0 -1
  107. package/dist/modules/common/os-platform/virtual-hid/windows.js +0 -5
  108. package/dist/modules/common/os-platform/virtual-hid/windows.js.map +0 -1
  109. package/dist/modules/common/ssh-config/impl.js +0 -116
  110. package/dist/modules/common/ssh-config/impl.js.map +0 -1
  111. package/dist/modules/common/ssh-config/interface.js +0 -10
  112. package/dist/modules/common/ssh-config/interface.js.map +0 -1
  113. package/dist/modules/ssh-agent/index.js +0 -8
  114. package/dist/modules/ssh-agent/index.js.map +0 -1
  115. package/dist/modules/ssh-agent/session/impl.js +0 -265
  116. package/dist/modules/ssh-agent/session/impl.js.map +0 -1
  117. package/dist/modules/ssh-agent/session/interface-rpc.js +0 -20
  118. package/dist/modules/ssh-agent/session/interface-rpc.js.map +0 -1
  119. package/dist/modules/ssh-agent/session/interface.js +0 -32
  120. package/dist/modules/ssh-agent/session/interface.js.map +0 -1
  121. package/dist/modules/ssh-agent/session/passkey-prf-page.js +0 -204
  122. package/dist/modules/ssh-agent/session/passkey-prf-page.js.map +0 -1
  123. package/dist/modules/ssh-agent/session/prf-flow.js +0 -103
  124. package/dist/modules/ssh-agent/session/prf-flow.js.map +0 -1
  125. package/dist/modules/ssh-agent/ssh-agent/impl.js +0 -117
  126. package/dist/modules/ssh-agent/ssh-agent/impl.js.map +0 -1
  127. package/dist/modules/ssh-agent/ssh-agent/interface.js +0 -2
  128. package/dist/modules/ssh-agent/ssh-agent/interface.js.map +0 -1
  129. package/dist/scripts/build.js +0 -410
  130. package/dist/scripts/build.js.map +0 -1
  131. package/dist/scripts/check-npm-version.js +0 -8
  132. package/dist/scripts/check-npm-version.js.map +0 -1
  133. package/dist/scripts/kill-existing-agent.js +0 -21
  134. package/dist/scripts/kill-existing-agent.js.map +0 -1
  135. package/dist/scripts/publish.js +0 -46
  136. package/dist/scripts/publish.js.map +0 -1
  137. package/src/commands/git/hosts.ts +0 -20
  138. package/src/commands/git/index.ts +0 -9
  139. package/src/commands/git/setup.ts +0 -182
  140. package/src/commands/index.ts +0 -15
  141. package/src/commands/npm/index.ts +0 -8
  142. package/src/commands/npm/setup/git-remote.ts +0 -78
  143. package/src/commands/npm/setup/index.ts +0 -189
  144. package/src/commands/npm/setup/npm-auth.ts +0 -58
  145. package/src/commands/npm/setup/package-json-validate.ts +0 -68
  146. package/src/commands/npm/setup/package-registry.ts +0 -24
  147. package/src/commands/npm/setup/publish-workflow.yml +0 -35
  148. package/src/commands/npm/setup/setup.test.ts +0 -117
  149. package/src/commands/npm/setup/trusted-publishing.ts +0 -106
  150. package/src/commands/npm/setup/workflow.ts +0 -20
  151. package/src/commands/ssh/get-sk-credential.ts +0 -110
  152. package/src/commands/ssh/index.ts +0 -8
  153. package/src/commands/upgrade.ts +0 -220
  154. package/src/common/command.ts +0 -16
  155. package/src/common/constants.ts +0 -26
  156. package/src/common/effective-modules-extensions.ts +0 -24
  157. package/src/common/log.ts +0 -3
  158. package/src/common/render-caller-tree.ts +0 -22
  159. package/src/entrypoints/ozy-signing-agent.ts +0 -48
  160. package/src/entrypoints/ozy-ssh-keygen.ts +0 -10
  161. package/src/entrypoints/ozy-virtual-security-key.ts +0 -21
  162. package/src/entrypoints/ozy.ts +0 -2
  163. package/src/index.ts +0 -1
  164. package/src/internal/thing.ts +0 -3
  165. package/src/modules/cli/agent-client/impl.ts +0 -152
  166. package/src/modules/cli/agent-client/interface.ts +0 -17
  167. package/src/modules/cli/git/impl.ts +0 -133
  168. package/src/modules/cli/git/interface.ts +0 -23
  169. package/src/modules/cli/git/submodule/fix.ts +0 -298
  170. package/src/modules/cli/git/submodule/git-state.ts +0 -217
  171. package/src/modules/cli/git/submodule/gitmodules.ts +0 -126
  172. package/src/modules/cli/git/submodule/printer.ts +0 -256
  173. package/src/modules/cli/git/submodule/resolve-git-dir.ts +0 -14
  174. package/src/modules/cli/github/impl.ts +0 -169
  175. package/src/modules/cli/github/interface.ts +0 -29
  176. package/src/modules/cli/index.ts +0 -16
  177. package/src/modules/common/crypto/impl.ts +0 -173
  178. package/src/modules/common/crypto/interface.ts +0 -20
  179. package/src/modules/common/index.ts +0 -19
  180. package/src/modules/common/kep-map-store/impl.ts +0 -47
  181. package/src/modules/common/kep-map-store/interface.ts +0 -9
  182. package/src/modules/common/os-platform/caller-info.ts +0 -152
  183. package/src/modules/common/os-platform/impl.ts +0 -91
  184. package/src/modules/common/os-platform/interface.ts +0 -16
  185. package/src/modules/common/os-platform/virtual-hid/index.ts +0 -12
  186. package/src/modules/common/os-platform/virtual-hid/linux.ts +0 -7
  187. package/src/modules/common/os-platform/virtual-hid/mac.ts +0 -150
  188. package/src/modules/common/os-platform/virtual-hid/windows.ts +0 -7
  189. package/src/modules/common/ssh-config/impl.ts +0 -130
  190. package/src/modules/common/ssh-config/interface.ts +0 -33
  191. package/src/modules/ssh-agent/index.ts +0 -13
  192. package/src/modules/ssh-agent/session/agent-notes.md +0 -210
  193. package/src/modules/ssh-agent/session/design.md +0 -2
  194. package/src/modules/ssh-agent/session/html-refactor.md +0 -23
  195. package/src/modules/ssh-agent/session/impl.ts +0 -328
  196. package/src/modules/ssh-agent/session/interface-rpc.ts +0 -24
  197. package/src/modules/ssh-agent/session/interface.ts +0 -62
  198. package/src/modules/ssh-agent/session/passkey-prf-page.ts +0 -224
  199. package/src/modules/ssh-agent/session/prf-flow.ts +0 -152
  200. package/src/modules/ssh-agent/ssh-agent/impl.ts +0 -136
  201. package/src/modules/ssh-agent/ssh-agent/interface.ts +0 -9
  202. package/src/scripts/build.ts +0 -504
  203. package/src/scripts/check-npm-version.ts +0 -7
  204. package/src/scripts/command-launcher.js.ejs +0 -168
  205. package/src/scripts/kill-existing-agent.ts +0 -22
  206. package/src/scripts/publish.ts +0 -47
  207. package/src/scripts/windows-bin-shims.md +0 -1104
@@ -1,256 +0,0 @@
1
- import type { Gitmodules } from "./gitmodules";
2
- import type { GitState, ModuleDirInfo, IndexGitlink, WorkingTreeGitEntry } from "./git-state";
3
- import type { Fix } from "./fix";
4
-
5
- const RED = "\x1b[31m";
6
- const GREEN = "\x1b[32m";
7
- const CYAN = "\x1b[36m";
8
- const DIM = "\x1b[2m";
9
- const BOLD = "\x1b[1m";
10
- const RESET = "\x1b[0m";
11
-
12
- const ok = `${GREEN}✓${RESET}`;
13
- const fail = (msg: string) => `${RED}✗ ${msg}${RESET}`;
14
- const fsName = (s: string) => `${CYAN}${s}${RESET}`;
15
- const key = (s: string) => `${DIM}${s}${RESET}`;
16
-
17
- interface Line {
18
- label: string;
19
- isKey?: boolean; // dim key style instead of cyan fs style
20
- value?: string;
21
- status?: string;
22
- children?: Line[];
23
- }
24
-
25
- function render(lines: Line[], prefix = ""): void {
26
- for (let i = 0; i < lines.length; i++) {
27
- const last = i === lines.length - 1;
28
- const branch = last ? "└── " : "├── ";
29
- const child = last ? " " : "│ ";
30
- const l = lines[i];
31
- const label = l.isKey ? key(l.label) : fsName(l.label);
32
- let out = `${prefix}${branch}${label}`;
33
- if (l.value !== undefined) out += `: ${l.value}`;
34
- if (l.status !== undefined) out += ` ${l.status}`;
35
- console.log(out);
36
- if (l.children?.length) render(l.children, prefix + child);
37
- }
38
- }
39
-
40
- function buildPathTree(parts: string[], children: Line[]): Line {
41
- if (parts.length === 1) return { label: parts[0], children };
42
- return { label: parts[0], children: [buildPathTree(parts.slice(1), children)] };
43
- }
44
-
45
- function resultLine(label: string, result: string | { error: string } | undefined): Line {
46
- if (result === undefined) return { label, isKey: true, status: fail("unknown") };
47
- if (typeof result === "string") return { label, isKey: true, value: result };
48
- return { label, isKey: true, status: fail(result.error) };
49
- }
50
-
51
- function moduleHasIssues(
52
- m: { path: string; url: string },
53
- cfg: { path?: string; url?: string } | undefined,
54
- dir: ModuleDirInfo | undefined,
55
- idx: IndexGitlink | undefined,
56
- headSha: string | undefined,
57
- wt: WorkingTreeGitEntry | undefined,
58
- ): boolean {
59
- if (!cfg) return true;
60
- if (cfg.path !== m.path || cfg.url !== m.url) return true;
61
- if (!dir) return true;
62
- if (!idx) return true;
63
- if (headSha && idx.commit !== headSha) return true;
64
- if (!wt || wt.type === "missing" || wt.type === "dir") return true;
65
- if (typeof wt.headCommit !== "string") return true;
66
- if (typeof wt.remote !== "string" || wt.remote !== m.url) return true;
67
- return false;
68
- }
69
-
70
- export function printSyncState(modules: Gitmodules, state: GitState): void {
71
- const byName = new Map(state.config.map((c) => [c.name, c]));
72
- const byPath = new Map(state.moduleDirs.map((d) => [d.relativePath, d]));
73
- const byIndexPath = new Map(state.index.map((i) => [i.path, i]));
74
- const byTreePath = new Map(state.workingTree.map((w) => [w.path, w]));
75
- const modulePaths = new Set(modules.list.map((m) => m.path));
76
- const moduleNames = new Set(modules.list.map((m) => m.name));
77
-
78
- console.log("=== syncing submodules ===");
79
-
80
- for (const m of modules.list) {
81
- const cfg = byName.get(m.name);
82
- const dir = byPath.get(m.path);
83
- const idx = byIndexPath.get(m.path);
84
- const wt = byTreePath.get(m.path);
85
- const headSha = typeof wt?.headCommit === "string" ? wt.headCommit : undefined;
86
-
87
- if (!moduleHasIssues(m, cfg, dir, idx, headSha, wt)) {
88
- console.log(`${BOLD}[${m.name}]${RESET} ${ok}`);
89
- continue;
90
- }
91
-
92
- console.log(`${BOLD}[${m.name}]${RESET}`);
93
-
94
- // .gitmodules
95
- const gitmodulesNode: Line = {
96
- label: ".gitmodules",
97
- children: [
98
- { label: "path", isKey: true, value: m.path },
99
- { label: "url", isKey: true, value: m.url },
100
- ],
101
- };
102
-
103
- // .git/config
104
- let configNode: Line;
105
- if (!cfg) {
106
- configNode = { label: "config", status: fail("missing") };
107
- } else {
108
- configNode = {
109
- label: "config",
110
- children: [
111
- {
112
- label: "path",
113
- isKey: true,
114
- value: cfg.path ?? "(missing)",
115
- status: cfg.path === m.path ? ok : fail(`expected ${m.path}`),
116
- },
117
- {
118
- label: "url",
119
- isKey: true,
120
- value: cfg.url ?? "(missing)",
121
- status: cfg.url === m.url ? ok : fail(`expected ${m.url}`),
122
- },
123
- ],
124
- };
125
- }
126
-
127
- // .git/modules
128
- const modulesNode: Line = {
129
- label: "modules",
130
- children: [{ label: m.path, status: dir ? ok : fail("missing") }],
131
- };
132
-
133
- // .git/index
134
- let indexNode: Line;
135
- if (!idx) {
136
- indexNode = { label: "index", status: fail("missing") };
137
- } else {
138
- const idxSha8 = idx.commit.slice(0, 8);
139
- const matches = headSha && idx.commit === headSha;
140
- const mismatches = headSha && idx.commit !== headSha;
141
- indexNode = {
142
- label: "index",
143
- value: idxSha8,
144
- status: matches ? ok : mismatches ? fail(`head is ${headSha.slice(0, 8)}`) : undefined,
145
- };
146
- }
147
-
148
- const dotGitNode: Line = {
149
- label: ".git",
150
- children: [configNode, modulesNode, indexNode],
151
- };
152
-
153
- // working tree leaves
154
- const wtLeaves: Line[] = [];
155
-
156
- if (!wt || wt.type === "missing") {
157
- wtLeaves.push({ label: ".git", status: fail("missing") });
158
- } else if (wt.type === "dir") {
159
- wtLeaves.push({ label: ".git", status: fail("directory (standalone clone)") });
160
- } else {
161
- wtLeaves.push({ label: ".git", value: `gitdir → ${wt.gitdirTarget}` });
162
- }
163
-
164
- if (wt) {
165
- const head = headSha ?? wt.headCommit;
166
- wtLeaves.push(resultLine("head", typeof head === "string" ? head.slice(0, 8) : head));
167
-
168
- if (typeof wt.remote === "string") {
169
- wtLeaves.push({
170
- label: "remote",
171
- isKey: true,
172
- value: wt.remote,
173
- status: wt.remote === m.url ? ok : fail(`expected ${m.url}`),
174
- });
175
- } else {
176
- wtLeaves.push(resultLine("remote", wt.remote));
177
- }
178
- }
179
-
180
- const workingTreeNode = buildPathTree(m.path.split("/"), wtLeaves);
181
-
182
- render([gitmodulesNode, dotGitNode, workingTreeNode]);
183
- }
184
-
185
- // Stale entries
186
- const staleConfig = state.config.filter((c) => !moduleNames.has(c.name));
187
- const staleDirs = state.moduleDirs.filter((d) => !modulePaths.has(d.relativePath));
188
- const staleIndex = state.index.filter((i) => !modulePaths.has(i.path));
189
-
190
- if (staleConfig.length || staleDirs.length || state.orphanModuleDirs.length || staleIndex.length) {
191
- console.log(`\n${RED}${BOLD}=== stale (not in .gitmodules) ===${RESET}`);
192
- const staleNodes: Line[] = [];
193
-
194
- if (staleConfig.length) {
195
- staleNodes.push({
196
- label: ".git/config",
197
- children: staleConfig.map((c) => ({ label: c.name })),
198
- });
199
- }
200
- if (staleDirs.length || state.orphanModuleDirs.length) {
201
- staleNodes.push({
202
- label: ".git/modules",
203
- children: [
204
- ...staleDirs.map((d) => ({ label: d.relativePath })),
205
- ...state.orphanModuleDirs.map((d) => ({ label: d })),
206
- ],
207
- });
208
- }
209
- if (staleIndex.length) {
210
- staleNodes.push({
211
- label: ".git/index",
212
- children: staleIndex.map((i) => ({ label: i.path, isKey: true, value: i.commit.slice(0, 8) })),
213
- });
214
- }
215
-
216
- render(staleNodes);
217
- }
218
- }
219
-
220
- type FixTree = Map<string, { subtree: FixTree; fixes: Fix[] }>;
221
-
222
- function addToFixTree(tree: FixTree, fix: Fix, depth = 0): void {
223
- const seg = fix.location[depth];
224
- if (!tree.has(seg)) tree.set(seg, { subtree: new Map(), fixes: [] });
225
- const node = tree.get(seg)!;
226
- if (depth === fix.location.length - 1) {
227
- node.fixes.push(fix);
228
- } else {
229
- addToFixTree(node.subtree, fix, depth + 1);
230
- }
231
- }
232
-
233
- function fixTreeToLines(tree: FixTree): Line[] {
234
- const lines: Line[] = [];
235
- for (const [label, { subtree, fixes }] of tree) {
236
- const children = fixTreeToLines(subtree);
237
- if (fixes.length > 0) {
238
- for (const fix of fixes) {
239
- const actionStr = fix.action === "deleted" ? `${RED}deleted${RESET}` : `${GREEN}synced${RESET}`;
240
- const status = fix.detail ? `${actionStr} ${fix.detail}` : actionStr;
241
- lines.push({ label, status, children: children.length > 0 ? children : undefined });
242
- }
243
- } else {
244
- lines.push({ label, children });
245
- }
246
- }
247
- return lines;
248
- }
249
-
250
- export function printFixes(fixes: Fix[]): void {
251
- if (fixes.length === 0) return;
252
- const tree: FixTree = new Map();
253
- for (const fix of fixes) addToFixTree(tree, fix);
254
- console.log("\napplied fixes:");
255
- render(fixTreeToLines(tree));
256
- }
@@ -1,14 +0,0 @@
1
- import { statSync } from "node:fs";
2
- import { readFile } from "node:fs/promises";
3
- import { resolve } from "node:path";
4
-
5
- // A submodule's .git is a redirect file ("gitdir: <relative path>"), not a directory —
6
- // resolve it to the real git dir so callers don't try to treat the file as a directory.
7
- export async function resolveGitDir(repoRoot: string): Promise<string> {
8
- const dotGit = resolve(repoRoot, ".git");
9
- if (statSync(dotGit).isDirectory()) return dotGit;
10
- const content = await readFile(dotGit, "utf8");
11
- const match = content.match(/^gitdir:\s*(.+)$/m);
12
- if (!match) throw new Error(`Malformed .git file at ${dotGit}`);
13
- return resolve(repoRoot, match[1]!.trim());
14
- }
@@ -1,169 +0,0 @@
1
- import { Deferred, Effect, Schema } from "effect";
2
- import { implementing, type EffectGen } from "effective-modules";
3
- import { cliModules } from "@/modules/cli";
4
- import { commonModules } from "@/modules/common";
5
- import { log } from "@/common/log";
6
- import type { IGitHub, GitHubUser, GitHubSshSigningKey, GitHubAuthnKey } from "./interface";
7
-
8
- const API_BASE = 'https://api.github.com';
9
- const API_VERSION = '2022-11-28';
10
-
11
- const OAUTH_SUCCESS_HTML = `<!DOCTYPE html>
12
- <html><head><title>Authorization successful</title></head>
13
- <body><h1>Authorization successful</h1>
14
- <p>You can close this tab and return to the terminal.</p>
15
- </body></html>`;
16
-
17
- const GitHubUserSchema = Schema.Struct({
18
- login: Schema.String,
19
- name: Schema.String,
20
- id: Schema.Int
21
- });
22
-
23
- const GitHubEmailSchema = Schema.Struct({
24
- email: Schema.String,
25
- primary: Schema.Boolean,
26
- verified: Schema.Boolean,
27
- });
28
-
29
- const GitHubSshSigningKeySchema = Schema.Struct({
30
- id: Schema.Int,
31
- title: Schema.String,
32
- key: Schema.String,
33
- });
34
-
35
- const GitHubAuthnKeySchema = Schema.Struct({
36
- id: Schema.Int,
37
- title: Schema.String,
38
- key: Schema.String,
39
- });
40
-
41
- export class GitHubImpl extends implementing(cliModules.GitHub).uses(commonModules.OSPlatform) implements IGitHub {
42
- private api<A>(token: string, method: string, path: string, schema: Schema.Schema<A>, body?: unknown, debug?: boolean): Effect.Effect<{ status: number; data: A }, string> {
43
- return Effect.tryPromise({
44
- try: async () => {
45
- const res = await fetch(`${API_BASE}${path}`, {
46
- method,
47
- headers: {
48
- 'Authorization': `Bearer ${token}`,
49
- 'Accept': 'application/vnd.github+json',
50
- 'X-GitHub-Api-Version': API_VERSION,
51
- ...(body ? { 'Content-Type': 'application/json' } : {}),
52
- },
53
- ...(body ? { body: JSON.stringify(body) } : {}),
54
- });
55
- const rawData = await res.json();
56
- if (debug) log(`[debug] ${method} ${path} (${res.status}):\n${JSON.stringify(rawData, null, 2)}`);
57
- return { status: res.status, rawData };
58
- },
59
- catch: (e) => `GitHub API request failed: ${e instanceof Error ? e.message : String(e)}`,
60
- }).pipe(
61
- Effect.flatMap(({ status, rawData }) =>
62
- (Schema.decodeUnknownEffect(schema)(rawData) as Effect.Effect<A, unknown, never>).pipe(
63
- Effect.map(data => ({ status, data })),
64
- Effect.mapError(parseError =>
65
- status < 200 || status >= 300
66
- ? `${method} ${path} returned HTTP ${status}`
67
- : `Failed to parse ${method} ${path} response: ${String(parseError)}`
68
- )
69
- )
70
- )
71
- );
72
- }
73
-
74
- *authorize(clientId: string, clientSecret: string, scopes: string): EffectGen<string, string> {
75
- const osPlatform = this.dependencies.OSPlatform;
76
- return yield* Effect.scoped(Effect.gen(function* () {
77
- const state = crypto.randomUUID();
78
- const deferred = yield* Deferred.make<string, string>();
79
-
80
- const server = yield* Effect.acquireRelease(
81
- Effect.sync(() => Bun.serve({
82
- port: 0,
83
- async fetch(req) {
84
- const url = new URL(req.url);
85
- if (url.pathname === '/callback') {
86
- const code = url.searchParams.get('code');
87
- const returnedState = url.searchParams.get('state');
88
- if (returnedState !== state) {
89
- Effect.runFork(Deferred.fail(deferred, 'State mismatch — possible CSRF attack'));
90
- return new Response('State mismatch', { status: 400 });
91
- }
92
- if (!code) {
93
- Effect.runFork(Deferred.fail(deferred, 'Missing authorization code in callback'));
94
- return new Response('Missing code', { status: 400 });
95
- }
96
- Effect.runFork(Deferred.succeed(deferred, code));
97
- return new Response(OAUTH_SUCCESS_HTML, { headers: { 'Content-Type': 'text/html' } });
98
- }
99
- return new Response('Not found', { status: 404 });
100
- },
101
- })),
102
- (server) => Effect.sync(() => server.stop()),
103
- );
104
-
105
- const redirectUri = `http://localhost:${server.port}/callback`;
106
- const authUrl = new URL('https://github.com/login/oauth/authorize');
107
- authUrl.searchParams.set('client_id', clientId);
108
- authUrl.searchParams.set('scope', scopes);
109
- authUrl.searchParams.set('state', state);
110
- authUrl.searchParams.set('redirect_uri', redirectUri);
111
- authUrl.searchParams.set('prompt', 'select_account');
112
-
113
- log('Opening browser for GitHub authorization...');
114
- yield* osPlatform.openBrowserWindow(authUrl.toString());
115
-
116
- const code = yield* Deferred.await(deferred);
117
-
118
- const tokenRes = yield* Effect.tryPromise({
119
- try: () => fetch('https://github.com/login/oauth/access_token', {
120
- method: 'POST',
121
- headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
122
- body: JSON.stringify({ client_id: clientId, client_secret: clientSecret, code, redirect_uri: redirectUri }),
123
- }),
124
- catch: () => 'Token exchange request failed',
125
- });
126
-
127
- const tokenData = yield* Effect.tryPromise({
128
- try: () => tokenRes.json() as Promise<{ access_token?: string; error?: string; error_description?: string }>,
129
- catch: () => 'Failed to parse token exchange response',
130
- });
131
-
132
- if (!tokenData.access_token)
133
- return yield* Effect.fail(tokenData.error_description ?? tokenData.error ?? 'Token exchange failed');
134
-
135
- return tokenData.access_token;
136
- }));
137
- }
138
-
139
- *getUser(token: string): EffectGen<GitHubUser, string> {
140
- const { data: { login, name, id } } = yield* this.api(token, 'GET', '/user', GitHubUserSchema);
141
- const { data: emails } = yield* this.api(token, 'GET', '/user/emails', Schema.Array(GitHubEmailSchema));
142
- const primaryEmail = emails.find(e => e.primary);
143
- if (!primaryEmail)
144
- return yield* Effect.fail(`No primary email found on GitHub account — add one at github.com/settings/emails`);
145
- return { login, name, email: primaryEmail.email, id };
146
- }
147
-
148
- *getSigningKeys(token: string): EffectGen<GitHubSshSigningKey[], string> {
149
- const { data } = yield* this.api(token, 'GET', '/user/ssh_signing_keys', Schema.Array(GitHubSshSigningKeySchema));
150
- return data as GitHubSshSigningKey[];
151
- }
152
-
153
- *addSigningKey(token: string, title: string, key: string): EffectGen<void, string> {
154
- const { status } = yield* this.api(token, 'POST', '/user/ssh_signing_keys', Schema.Unknown, { title, key });
155
- if (status !== 201)
156
- yield* Effect.fail(`POST /user/ssh_signing_keys returned HTTP ${status}`);
157
- }
158
-
159
- *getAuthnKeys(token: string): EffectGen<readonly GitHubAuthnKey[], string> {
160
- const { data } = yield* this.api(token, 'GET', `/user/keys`, Schema.Array(GitHubAuthnKeySchema));
161
- return data;
162
- }
163
-
164
- *addAuthnKey(token: string, title: string, key: string): EffectGen<void, string> {
165
- const { status } = yield* this.api(token, 'POST', `/user/keys`, Schema.Unknown, { title, key });
166
- if (status !== 201)
167
- yield* Effect.fail(`POST /user/keys returned HTTP ${status}`);
168
- }
169
- }
@@ -1,29 +0,0 @@
1
- import type { EffectGen } from "effective-modules";
2
-
3
- export interface GitHubUser {
4
- login: string;
5
- name: string;
6
- email: string;
7
- id: number;
8
- }
9
-
10
- export interface GitHubSshSigningKey {
11
- id: number;
12
- title: string;
13
- key: string;
14
- }
15
-
16
- export interface GitHubAuthnKey {
17
- readonly id: number;
18
- readonly title: string;
19
- readonly key: string;
20
- }
21
-
22
- export interface IGitHub {
23
- authorize(clientId: string, clientSecret: string, scopes: string): EffectGen<string, string>;
24
- getUser(token: string): EffectGen<GitHubUser, string>;
25
- getSigningKeys(token: string): EffectGen<GitHubSshSigningKey[], string>;
26
- addSigningKey(token: string, title: string, key: string): EffectGen<void, string>;
27
- getAuthnKeys(token: string): EffectGen<readonly GitHubAuthnKey[], string>;
28
- addAuthnKey(token: string, title: string, key: string): EffectGen<void, string>;
29
- }
@@ -1,16 +0,0 @@
1
- import { interfaces } from "effective-modules";
2
- import type { IGit } from "./git/interface";
3
- import type { IGitHub } from "./github/interface";
4
- import type { IAgentClient } from "./agent-client/interface";
5
-
6
- export enum CLIModules {
7
- Git = "Git",
8
- GitHub = "GitHub",
9
- AgentClient = "AgentClient",
10
- }
11
-
12
- export const cliModules = interfaces<CLIModules, {
13
- Git: IGit;
14
- GitHub: IGitHub;
15
- AgentClient: IAgentClient;
16
- }>(CLIModules);
@@ -1,173 +0,0 @@
1
- import { Effect, pipe } from "effect";
2
- import { implementing, type EffectGen } from "effective-modules";
3
- import { commonModules } from "@/modules/common";
4
- import type { DecryptInput, ECDHKeyPair, ICrypto } from "./interface";
5
- import bs58 from "bs58";
6
- import friendlyWords from "friendly-words";
7
- import { sign as cryptoSign, createPrivateKey, createPublicKey } from "node:crypto";
8
- import { FUTURE_TOOL_NAME } from "@/common/constants";
9
-
10
- function toBase64url(bytes: Uint8Array): string {
11
- return Buffer.from(bytes).toString("base64url");
12
- }
13
-
14
- function fromBase64url(str: string): Uint8Array {
15
- return new Uint8Array(Buffer.from(str, "base64url"));
16
- }
17
-
18
- function u32(n: number): Buffer {
19
- const b = Buffer.allocUnsafe(4);
20
- b.writeUInt32BE(n, 0);
21
- return b;
22
- }
23
-
24
- function sshString(s: Buffer): Buffer {
25
- return Buffer.concat([u32(s.length), s]);
26
- }
27
-
28
- // PKCS#8 DER header for Ed25519
29
- const PKCS8_ED25519_HEADER = Buffer.from("302e020100300506032b657004220420", "hex");
30
-
31
- export class CredentialId {
32
- public readonly base64: string;
33
- public readonly raw: Uint8Array;
34
- public readonly humanReadableName: string;
35
- public static fromBase64(base64: string) {
36
- const base58 = bs58.encode(fromBase64url(base64));
37
- return new this(base58);
38
- }
39
- public constructor(public readonly base58: string) {
40
- this.raw = bs58.decode(base58);
41
- this.base64 = toBase64url(this.raw);
42
- // Extract three non-overlapping bit windows: 11 + 11 + 12 = 34 bits from bytes 0-4
43
- const b = this.raw;
44
- const adj1Idx = ((b[0]! << 3) | (b[1]! >> 5)) % friendlyWords.predicates.length;
45
- const adj2Idx = (((b[1]! & 0x1F) << 6) | (b[2]! >> 2)) % friendlyWords.predicates.length;
46
- const nounIdx = (((b[2]! & 0x3) << 10) | (b[3]! << 2) | (b[4]! >> 6)) % friendlyWords.objects.length;
47
- this.humanReadableName = [
48
- FUTURE_TOOL_NAME,
49
- friendlyWords.predicates[adj1Idx],
50
- friendlyWords.predicates[adj2Idx],
51
- friendlyWords.objects[nounIdx]
52
- ].join("-");
53
- }
54
- }
55
-
56
- export class SSHPubkey {
57
- readonly wire: Buffer;
58
- readonly keyType: string;
59
- readonly authorizedKey: string;
60
-
61
- private constructor(wire: Buffer) {
62
- this.wire = wire;
63
- const len = wire.readUInt32BE(0);
64
- this.keyType = wire.subarray(4, 4 + len).toString("utf8");
65
- this.authorizedKey = `${this.keyType} ${wire.toString("base64")}`;
66
- }
67
-
68
- static fromWire(wire: Buffer): SSHPubkey {
69
- return new SSHPubkey(wire);
70
- }
71
-
72
- static fromAuthorizedKey(s: string): SSHPubkey {
73
- return new SSHPubkey(Buffer.from(s.split(" ")[1]!, "base64"));
74
- }
75
- }
76
-
77
- export class SSHKeyPair {
78
- readonly pubkey: SSHPubkey;
79
- private readonly pemPrivateKey: string;
80
-
81
- private constructor(pubkey: SSHPubkey, pemPrivateKey: string) {
82
- this.pubkey = pubkey;
83
- this.pemPrivateKey = pemPrivateKey;
84
- }
85
-
86
- sign(data: Buffer): Buffer {
87
- return cryptoSign(null, data, createPrivateKey(this.pemPrivateKey));
88
- }
89
-
90
- static fromSeed(seed: Uint8Array, context: string): Effect.Effect<SSHKeyPair, string> {
91
- return Effect.tryPromise({
92
- try: async () => {
93
- const hkdfKey = await crypto.subtle.importKey("raw", seed, "HKDF", false, ["deriveBits"]);
94
- const bits = await crypto.subtle.deriveBits(
95
- { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(32), info: new TextEncoder().encode(context) } as any,
96
- hkdfKey,
97
- 256
98
- );
99
- return SSHKeyPair.build(new Uint8Array(bits));
100
- },
101
- catch: (e) => String(e),
102
- });
103
- }
104
-
105
- private static build(seed: Uint8Array): SSHKeyPair {
106
- const der = Buffer.concat([PKCS8_ED25519_HEADER, Buffer.from(seed)]);
107
- const priv = createPrivateKey({ key: der, format: "der", type: "pkcs8" });
108
- const spki = createPublicKey(priv).export({ type: "spki", format: "der" }) as Buffer;
109
- const pubBytes = Buffer.from(spki.subarray(-32));
110
-
111
- const keyTypeBuf = Buffer.from("ssh-ed25519");
112
- const wire = Buffer.concat([sshString(keyTypeBuf), sshString(pubBytes)]);
113
-
114
- const pemPrivateKey = priv.export({ type: "pkcs8", format: "pem" }) as string;
115
-
116
- return new SSHKeyPair(SSHPubkey.fromWire(wire), pemPrivateKey);
117
- }
118
- }
119
-
120
- const { Crypto } = commonModules;
121
-
122
- export class CryptoImpl extends implementing(Crypto) implements ICrypto {
123
-
124
- *getRandomChallenge(): EffectGen<string> {
125
- return toBase64url(crypto.getRandomValues(new Uint8Array(16)));
126
- }
127
-
128
- *createECDHKey(): EffectGen<ECDHKeyPair, string> {
129
- const keyPair = yield* Effect.tryPromise({
130
- try: () => crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits"]),
131
- catch: (e) => (e as Error).toString(),
132
- });
133
- const agentPubKeyRaw = yield* Effect.tryPromise({
134
- try: () => crypto.subtle.exportKey("raw", keyPair.publicKey),
135
- catch: (e) => (e as Error).toString(),
136
- });
137
- const pubkey = toBase64url(new Uint8Array(agentPubKeyRaw));
138
- return {
139
- base64Pubkey: pubkey,
140
- keyPair,
141
- }
142
- }
143
-
144
- *decrypt({cipherText, iv, hkdfInfo, receiverKey, senderPubkey}: DecryptInput): EffectGen<Uint8Array, string> {
145
- return yield* pipe(
146
- Effect.tryPromise(async () => {
147
- const browserPubKey = await crypto.subtle.importKey(
148
- "raw", fromBase64url(senderPubkey),
149
- { name: "ECDH", namedCurve: "P-256" }, false, []
150
- );
151
- const sharedBits = await crypto.subtle.deriveBits(
152
- { name: "ECDH", public: browserPubKey }, receiverKey.privateKey, 256
153
- );
154
- const hkdfKey = await crypto.subtle.importKey("raw", sharedBits, "HKDF", false, ["deriveKey"]);
155
- const aesKey = await crypto.subtle.deriveKey(
156
- {
157
- name: "HKDF", hash: "SHA-256",
158
- salt: new Uint8Array(32),
159
- info: new TextEncoder().encode(hkdfInfo),
160
- },
161
- hkdfKey, { name: "AES-GCM", length: 256 }, false, ["decrypt"]
162
- );
163
- const decrypted = await crypto.subtle.decrypt(
164
- { name: "AES-GCM", iv: fromBase64url(iv) },
165
- aesKey, fromBase64url(cipherText)
166
- );
167
- return new Uint8Array(decrypted);
168
- }),
169
- Effect.catchTag("UnknownError", err => Effect.fail(err.message))
170
- );
171
- }
172
-
173
- }
@@ -1,20 +0,0 @@
1
- import { type EffectGen } from "effective-modules";
2
-
3
- export interface ECDHKeyPair {
4
- base64Pubkey: string;
5
- keyPair: CryptoKeyPair;
6
- }
7
-
8
- export interface DecryptInput {
9
- cipherText: string;
10
- iv: string;
11
- senderPubkey: string;
12
- receiverKey: CryptoKeyPair;
13
- hkdfInfo: string;
14
- }
15
-
16
- export interface ICrypto {
17
- getRandomChallenge(): EffectGen<string>;
18
- createECDHKey(): EffectGen<ECDHKeyPair, string>;
19
- decrypt(input: DecryptInput): EffectGen<Uint8Array, string>;
20
- }