@kimchi-dev/kimchictl 0.0.2

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 (231) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +105 -0
  3. package/bin/kimchictl.mjs +17 -0
  4. package/dist/api/http.d.ts +7 -0
  5. package/dist/api/http.d.ts.map +1 -0
  6. package/dist/api/http.js +60 -0
  7. package/dist/api/http.js.map +1 -0
  8. package/dist/api/keys.d.ts +7 -0
  9. package/dist/api/keys.d.ts.map +1 -0
  10. package/dist/api/keys.js +28 -0
  11. package/dist/api/keys.js.map +1 -0
  12. package/dist/api/me.d.ts +12 -0
  13. package/dist/api/me.d.ts.map +1 -0
  14. package/dist/api/me.js +28 -0
  15. package/dist/api/me.js.map +1 -0
  16. package/dist/api/resolver.d.ts +26 -0
  17. package/dist/api/resolver.d.ts.map +1 -0
  18. package/dist/api/resolver.js +62 -0
  19. package/dist/api/resolver.js.map +1 -0
  20. package/dist/api/resources.d.ts +16 -0
  21. package/dist/api/resources.d.ts.map +1 -0
  22. package/dist/api/resources.js +96 -0
  23. package/dist/api/resources.js.map +1 -0
  24. package/dist/api/templates.d.ts +19 -0
  25. package/dist/api/templates.d.ts.map +1 -0
  26. package/dist/api/templates.js +25 -0
  27. package/dist/api/templates.js.map +1 -0
  28. package/dist/api/tokens.d.ts +23 -0
  29. package/dist/api/tokens.d.ts.map +1 -0
  30. package/dist/api/tokens.js +51 -0
  31. package/dist/api/tokens.js.map +1 -0
  32. package/dist/api/types.d.ts +34 -0
  33. package/dist/api/types.d.ts.map +1 -0
  34. package/dist/api/types.js +32 -0
  35. package/dist/api/types.js.map +1 -0
  36. package/dist/api/wait.d.ts +17 -0
  37. package/dist/api/wait.d.ts.map +1 -0
  38. package/dist/api/wait.js +37 -0
  39. package/dist/api/wait.js.map +1 -0
  40. package/dist/api/workspaces.d.ts +33 -0
  41. package/dist/api/workspaces.d.ts.map +1 -0
  42. package/dist/api/workspaces.js +159 -0
  43. package/dist/api/workspaces.js.map +1 -0
  44. package/dist/auth/auth-file.d.ts +37 -0
  45. package/dist/auth/auth-file.d.ts.map +1 -0
  46. package/dist/auth/auth-file.js +100 -0
  47. package/dist/auth/auth-file.js.map +1 -0
  48. package/dist/auth/browser-login.d.ts +45 -0
  49. package/dist/auth/browser-login.d.ts.map +1 -0
  50. package/dist/auth/browser-login.js +195 -0
  51. package/dist/auth/browser-login.js.map +1 -0
  52. package/dist/auth/config-file.d.ts +4 -0
  53. package/dist/auth/config-file.d.ts.map +1 -0
  54. package/dist/auth/config-file.js +52 -0
  55. package/dist/auth/config-file.js.map +1 -0
  56. package/dist/auth/models-providers.d.ts +17 -0
  57. package/dist/auth/models-providers.d.ts.map +1 -0
  58. package/dist/auth/models-providers.js +63 -0
  59. package/dist/auth/models-providers.js.map +1 -0
  60. package/dist/auth/paths.d.ts +15 -0
  61. package/dist/auth/paths.d.ts.map +1 -0
  62. package/dist/auth/paths.js +25 -0
  63. package/dist/auth/paths.js.map +1 -0
  64. package/dist/auth/provider-ids.d.ts +4 -0
  65. package/dist/auth/provider-ids.d.ts.map +1 -0
  66. package/dist/auth/provider-ids.js +12 -0
  67. package/dist/auth/provider-ids.js.map +1 -0
  68. package/dist/auth/resolve.d.ts +19 -0
  69. package/dist/auth/resolve.d.ts.map +1 -0
  70. package/dist/auth/resolve.js +37 -0
  71. package/dist/auth/resolve.js.map +1 -0
  72. package/dist/cli.d.ts +2 -0
  73. package/dist/cli.d.ts.map +1 -0
  74. package/dist/cli.js +15 -0
  75. package/dist/cli.js.map +1 -0
  76. package/dist/commands/completion.d.ts +2 -0
  77. package/dist/commands/completion.d.ts.map +1 -0
  78. package/dist/commands/completion.js +71 -0
  79. package/dist/commands/completion.js.map +1 -0
  80. package/dist/commands/flags.d.ts +16 -0
  81. package/dist/commands/flags.d.ts.map +1 -0
  82. package/dist/commands/flags.js +23 -0
  83. package/dist/commands/flags.js.map +1 -0
  84. package/dist/commands/guard.d.ts +10 -0
  85. package/dist/commands/guard.d.ts.map +1 -0
  86. package/dist/commands/guard.js +41 -0
  87. package/dist/commands/guard.js.map +1 -0
  88. package/dist/commands/help.d.ts +4 -0
  89. package/dist/commands/help.d.ts.map +1 -0
  90. package/dist/commands/help.js +19 -0
  91. package/dist/commands/help.js.map +1 -0
  92. package/dist/commands/login.d.ts +18 -0
  93. package/dist/commands/login.d.ts.map +1 -0
  94. package/dist/commands/login.js +80 -0
  95. package/dist/commands/login.js.map +1 -0
  96. package/dist/commands/logout.d.ts +14 -0
  97. package/dist/commands/logout.d.ts.map +1 -0
  98. package/dist/commands/logout.js +41 -0
  99. package/dist/commands/logout.js.map +1 -0
  100. package/dist/commands/prompt.d.ts +9 -0
  101. package/dist/commands/prompt.d.ts.map +1 -0
  102. package/dist/commands/prompt.js +21 -0
  103. package/dist/commands/prompt.js.map +1 -0
  104. package/dist/commands/registry.d.ts +16 -0
  105. package/dist/commands/registry.d.ts.map +1 -0
  106. package/dist/commands/registry.js +35 -0
  107. package/dist/commands/registry.js.map +1 -0
  108. package/dist/commands/ssh.d.ts +15 -0
  109. package/dist/commands/ssh.d.ts.map +1 -0
  110. package/dist/commands/ssh.js +90 -0
  111. package/dist/commands/ssh.js.map +1 -0
  112. package/dist/commands/table.d.ts +3 -0
  113. package/dist/commands/table.d.ts.map +1 -0
  114. package/dist/commands/table.js +10 -0
  115. package/dist/commands/table.js.map +1 -0
  116. package/dist/commands/update.d.ts +16 -0
  117. package/dist/commands/update.d.ts.map +1 -0
  118. package/dist/commands/update.js +57 -0
  119. package/dist/commands/update.js.map +1 -0
  120. package/dist/commands/version.d.ts +2 -0
  121. package/dist/commands/version.d.ts.map +1 -0
  122. package/dist/commands/version.js +6 -0
  123. package/dist/commands/version.js.map +1 -0
  124. package/dist/commands/whoami.d.ts +7 -0
  125. package/dist/commands/whoami.d.ts.map +1 -0
  126. package/dist/commands/whoami.js +30 -0
  127. package/dist/commands/whoami.js.map +1 -0
  128. package/dist/commands/workspace.d.ts +15 -0
  129. package/dist/commands/workspace.d.ts.map +1 -0
  130. package/dist/commands/workspace.js +206 -0
  131. package/dist/commands/workspace.js.map +1 -0
  132. package/dist/run.d.ts +10 -0
  133. package/dist/run.d.ts.map +1 -0
  134. package/dist/run.js +30 -0
  135. package/dist/run.js.map +1 -0
  136. package/dist/ssh/bridge.d.ts +25 -0
  137. package/dist/ssh/bridge.d.ts.map +1 -0
  138. package/dist/ssh/bridge.js +95 -0
  139. package/dist/ssh/bridge.js.map +1 -0
  140. package/dist/ssh/config.d.ts +65 -0
  141. package/dist/ssh/config.d.ts.map +1 -0
  142. package/dist/ssh/config.js +198 -0
  143. package/dist/ssh/config.js.map +1 -0
  144. package/dist/ssh/connect.d.ts +27 -0
  145. package/dist/ssh/connect.d.ts.map +1 -0
  146. package/dist/ssh/connect.js +67 -0
  147. package/dist/ssh/connect.js.map +1 -0
  148. package/dist/ssh/tunnel.d.ts +29 -0
  149. package/dist/ssh/tunnel.d.ts.map +1 -0
  150. package/dist/ssh/tunnel.js +36 -0
  151. package/dist/ssh/tunnel.js.map +1 -0
  152. package/dist/test-support.d.ts +30 -0
  153. package/dist/test-support.d.ts.map +1 -0
  154. package/dist/test-support.js +67 -0
  155. package/dist/test-support.js.map +1 -0
  156. package/dist/updates/check.d.ts +18 -0
  157. package/dist/updates/check.d.ts.map +1 -0
  158. package/dist/updates/check.js +90 -0
  159. package/dist/updates/check.js.map +1 -0
  160. package/dist/updates/self.d.ts +36 -0
  161. package/dist/updates/self.d.ts.map +1 -0
  162. package/dist/updates/self.js +103 -0
  163. package/dist/updates/self.js.map +1 -0
  164. package/dist/version.d.ts +7 -0
  165. package/dist/version.d.ts.map +1 -0
  166. package/dist/version.js +7 -0
  167. package/dist/version.js.map +1 -0
  168. package/package.json +77 -0
  169. package/src/api/http.ts +70 -0
  170. package/src/api/keys.ts +40 -0
  171. package/src/api/me.ts +48 -0
  172. package/src/api/resolver.test.ts +109 -0
  173. package/src/api/resolver.ts +72 -0
  174. package/src/api/resources.test.ts +56 -0
  175. package/src/api/resources.ts +92 -0
  176. package/src/api/templates.ts +28 -0
  177. package/src/api/tokens.ts +80 -0
  178. package/src/api/types.ts +50 -0
  179. package/src/api/wait.ts +45 -0
  180. package/src/api/workspaces.ts +234 -0
  181. package/src/auth/auth-file.test.ts +176 -0
  182. package/src/auth/auth-file.ts +103 -0
  183. package/src/auth/browser-login.test.ts +93 -0
  184. package/src/auth/browser-login.ts +247 -0
  185. package/src/auth/config-file.test.ts +68 -0
  186. package/src/auth/config-file.ts +50 -0
  187. package/src/auth/models-providers.test.ts +63 -0
  188. package/src/auth/models-providers.ts +76 -0
  189. package/src/auth/paths.ts +28 -0
  190. package/src/auth/provider-ids.ts +15 -0
  191. package/src/auth/resolve.test.ts +72 -0
  192. package/src/auth/resolve.ts +46 -0
  193. package/src/cli.ts +16 -0
  194. package/src/commands/completion.test.ts +31 -0
  195. package/src/commands/completion.ts +77 -0
  196. package/src/commands/flags.ts +29 -0
  197. package/src/commands/guard.ts +44 -0
  198. package/src/commands/help.ts +20 -0
  199. package/src/commands/login.test.ts +159 -0
  200. package/src/commands/login.ts +94 -0
  201. package/src/commands/logout.test.ts +89 -0
  202. package/src/commands/logout.ts +53 -0
  203. package/src/commands/prompt.ts +21 -0
  204. package/src/commands/registry.ts +50 -0
  205. package/src/commands/ssh.test.ts +101 -0
  206. package/src/commands/ssh.ts +112 -0
  207. package/src/commands/table.test.ts +24 -0
  208. package/src/commands/table.ts +10 -0
  209. package/src/commands/update.test.ts +91 -0
  210. package/src/commands/update.ts +78 -0
  211. package/src/commands/version.ts +6 -0
  212. package/src/commands/whoami.test.ts +72 -0
  213. package/src/commands/whoami.ts +36 -0
  214. package/src/commands/workspace.test.ts +360 -0
  215. package/src/commands/workspace.ts +250 -0
  216. package/src/run.test.ts +53 -0
  217. package/src/run.ts +34 -0
  218. package/src/ssh/bridge.test.ts +70 -0
  219. package/src/ssh/bridge.ts +110 -0
  220. package/src/ssh/config.test.ts +213 -0
  221. package/src/ssh/config.ts +233 -0
  222. package/src/ssh/connect.test.ts +123 -0
  223. package/src/ssh/connect.ts +98 -0
  224. package/src/ssh/tunnel.test.ts +119 -0
  225. package/src/ssh/tunnel.ts +64 -0
  226. package/src/test-support.ts +77 -0
  227. package/src/updates/check.test.ts +99 -0
  228. package/src/updates/check.ts +117 -0
  229. package/src/updates/self.test.ts +117 -0
  230. package/src/updates/self.ts +131 -0
  231. package/src/version.ts +6 -0
@@ -0,0 +1,37 @@
1
+ import { readSavedApiKey } from "./auth-file.js";
2
+ import { readApiKeyFromConfig } from "./config-file.js";
3
+ import { resolveAuthJsonPath, resolveConfigJsonPath } from "./paths.js";
4
+ /**
5
+ * Credential resolution, mirroring the harness precedence
6
+ * (kimchi-dev:src/config.ts — env key beats the saved login):
7
+ * 1. KIMCHI_API_KEY environment variable
8
+ * 2. config.json `apiKey` (the harness's saved key)
9
+ * 3. auth.json `kimchi-dev` entry
10
+ */
11
+ export function resolveApiKey(env = process.env) {
12
+ const envKey = env.KIMCHI_API_KEY?.trim();
13
+ if (envKey)
14
+ return { key: envKey, source: "environment" };
15
+ const configKey = readApiKeyFromConfig(resolveConfigJsonPath(env));
16
+ if (configKey)
17
+ return { key: configKey, source: "config" };
18
+ const authKey = readSavedApiKey(resolveAuthJsonPath(env));
19
+ if (authKey)
20
+ return { key: authKey, source: "auth-json" };
21
+ return undefined;
22
+ }
23
+ /** Standard error for commands that need a credential that isn't there. */
24
+ export function requireApiKey(env = process.env) {
25
+ const resolved = resolveApiKey(env);
26
+ if (!resolved) {
27
+ throw new NotLoggedInError();
28
+ }
29
+ return resolved;
30
+ }
31
+ export class NotLoggedInError extends Error {
32
+ constructor() {
33
+ super("Not logged in. Run `kimchictl login` first (or set KIMCHI_API_KEY).");
34
+ this.name = "NotLoggedInError";
35
+ }
36
+ }
37
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"../src/","sources":["auth/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AASvE;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,MAAM,MAAM,GAAG,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAA;IACzC,IAAI,MAAM;QAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAA;IAEzD,MAAM,SAAS,GAAG,oBAAoB,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAA;IAClE,IAAI,SAAS;QAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAA;IAE1D,MAAM,OAAO,GAAG,eAAe,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAA;IACzD,IAAI,OAAO;QAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAA;IAEzD,OAAO,SAAS,CAAA;AACjB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,aAAa,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;IACnC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,MAAM,IAAI,gBAAgB,EAAE,CAAA;IAC7B,CAAC;IACD,OAAO,QAAQ,CAAA;AAChB,CAAC;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAC1C;QACC,KAAK,CAAC,qEAAqE,CAAC,CAAA;QAC5E,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAA;IAC/B,CAAC;CACD"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"../src/","sources":["cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,15 @@
1
+ import { run } from "./run.js";
2
+ import { maybeNotifyUpdate } from "./updates/check.js";
3
+ // Executable CLI target: `bun build --compile src/cli.ts`, `bun run dev`,
4
+ // and the published dist/cli.js behind bin/kimchictl.mjs. Self-executes on
5
+ // import; never imported as a library (run.ts is the library surface).
6
+ const argv = process.argv.slice(2);
7
+ const code = await run(argv);
8
+ // Daily background update notice: prints to stderr, never fails the command.
9
+ // Skipped for `ssh` — its stdio belongs to the SSH session / proxy bridge,
10
+ // where any extra bytes would corrupt the stream.
11
+ if (argv[0] !== "ssh") {
12
+ await maybeNotifyUpdate();
13
+ }
14
+ process.exitCode = code;
15
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"../src/","sources":["cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,0EAA0E;AAC1E,2EAA2E;AAC3E,uEAAuE;AACvE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAClC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,CAAA;AAE5B,6EAA6E;AAC7E,2EAA2E;AAC3E,kDAAkD;AAClD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;IACvB,MAAM,iBAAiB,EAAE,CAAA;AAC1B,CAAC;AACD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare function runCompletion(args: string[]): Promise<number>;
2
+ //# sourceMappingURL=completion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion.d.ts","sourceRoot":"../src/","sources":["commands/completion.ts"],"names":[],"mappings":"AAyDA,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBnE"}
@@ -0,0 +1,71 @@
1
+ import { parseFlags, UsageError } from "./flags.js";
2
+ /**
3
+ * Shell completions — deliberately minimal: complete top-level command names
4
+ * plus workspace subverbs; expand on demand rather than building a full spec.
5
+ */
6
+ const TOP_LEVEL = ["login", "logout", "whoami", "workspace", "ws", "ssh", "update", "completion", "version", "help"];
7
+ const WORKSPACE_VERBS = ["create", "list", "get", "delete", "--output", "--desc", "--force", "--no-wait", "--help"];
8
+ const BASH = `# kimchictl bash completion — source this file or place it in /etc/bash_completion.d/
9
+ _kimchictl() {
10
+ local cur prev
11
+ cur="\${COMP_WORDS[COMP_CWORD]}"
12
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
13
+ if [ "$COMP_CWORD" -eq 1 ]; then
14
+ COMPREPLY=( $(compgen -W "${TOP_LEVEL.join(" ")}" -- "$cur") )
15
+ return 0
16
+ fi
17
+ case "\${COMP_WORDS[1]}" in
18
+ workspace|ws)
19
+ [ "$COMP_CWORD" -eq 2 ] && COMPREPLY=( $(compgen -W "${WORKSPACE_VERBS.join(" ")}" -- "$cur") ) ;;
20
+ completion)
21
+ [ "$COMP_CWORD" -eq 2 ] && COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur") ) ;;
22
+ esac
23
+ return 0
24
+ }
25
+ complete -F _kimchictl kimchictl
26
+ `;
27
+ const ZSH_COMMANDS = TOP_LEVEL.map((c) => `"${c}"`).join(" ");
28
+ const ZSH = `#compdef kimchictl
29
+ # kimchictl zsh completion — place in a directory on $fpath as _kimchictl
30
+ _kimchictl() {
31
+ local -a commands=${ZSH_COMMANDS}
32
+ if (( CURRENT == 2 )); then
33
+ _arguments "1:command:(\${commands})"
34
+ return
35
+ fi
36
+ case "$words[2]" in
37
+ workspace|ws)
38
+ (( CURRENT == 3 )) && _arguments "2:verb:(${WORKSPACE_VERBS.join(" ")})" ;;
39
+ completion)
40
+ (( CURRENT == 3 )) && _arguments "2:shell:(bash zsh fish)" ;;
41
+ esac
42
+ }
43
+ _kimchictl "$@"
44
+ `;
45
+ const FISH = `# kimchictl fish completion — place in ~/.config/fish/completions/kimchictl.fish
46
+ complete -c kimchictl -f
47
+ complete -c kimchictl -n '__fish_use_subcommand' -a '${TOP_LEVEL.join(" ")}'
48
+ complete -c kimchictl -n '__fish_seen_subcommand_from workspace ws' -a '${WORKSPACE_VERBS.join(" ")}'
49
+ complete -c kimchictl -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish'
50
+ `;
51
+ export async function runCompletion(args) {
52
+ const { positionals } = parseFlags("completion", args, {});
53
+ const shell = positionals[0];
54
+ if (positionals.length > 1) {
55
+ throw new UsageError("kimchictl completion: expected one shell (bash|zsh|fish)");
56
+ }
57
+ switch (shell) {
58
+ case "bash":
59
+ console.log(BASH);
60
+ return 0;
61
+ case "zsh":
62
+ console.log(ZSH);
63
+ return 0;
64
+ case "fish":
65
+ console.log(FISH);
66
+ return 0;
67
+ default:
68
+ throw new UsageError(`kimchictl completion: unsupported shell '${shell ?? ""}' — one of: bash, zsh, fish`);
69
+ }
70
+ }
71
+ //# sourceMappingURL=completion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion.js","sourceRoot":"../src/","sources":["commands/completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEnD;;;GAGG;AAEH,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;AACpH,MAAM,eAAe,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAA;AAEnH,MAAM,IAAI,GAAG;;;;;;gCAMmB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;;;;;6DAKU,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;;;;;;;CAOrF,CAAA;AAED,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE7D,MAAM,GAAG,GAAG;;;sBAGU,YAAY;;;;;;;kDAOgB,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;;;;;;CAM1E,CAAA;AAED,MAAM,IAAI,GAAG;;uDAE0C,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;0EACA,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;;CAElG,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAc;IACjD,MAAM,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC,YAAY,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC1D,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAC5B,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,UAAU,CAAC,0DAA0D,CAAC,CAAA;IACjF,CAAC;IACD,QAAQ,KAAK,EAAE,CAAC;QACf,KAAK,MAAM;YACV,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACjB,OAAO,CAAC,CAAA;QACT,KAAK,KAAK;YACT,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAChB,OAAO,CAAC,CAAA;QACT,KAAK,MAAM;YACV,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACjB,OAAO,CAAC,CAAA;QACT;YACC,MAAM,IAAI,UAAU,CAAC,4CAA4C,KAAK,IAAI,EAAE,6BAA6B,CAAC,CAAA;IAC5G,CAAC;AACF,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type ParseArgsOptionsConfig } from "node:util";
2
+ /** Bad CLI input — surfaced with exit code 2, distinct from runtime failures (1). */
3
+ export declare class UsageError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ export type FlagValues = Record<string, string | boolean | undefined>;
7
+ /**
8
+ * Strict flag parsing with a uniform usage-error shape across commands.
9
+ * Values keep their raw type (declared options are TypeScript-blind here —
10
+ * narrow at the call site, relying on the declared option `type`).
11
+ */
12
+ export declare function parseFlags(command: string, args: string[], options: ParseArgsOptionsConfig): {
13
+ values: FlagValues;
14
+ positionals: string[];
15
+ };
16
+ //# sourceMappingURL=flags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flags.d.ts","sourceRoot":"../src/","sources":["commands/flags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,sBAAsB,EAAa,MAAM,WAAW,CAAA;AAElE,qFAAqF;AACrF,qBAAa,UAAW,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI3B;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAA;AAErE;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,sBAAsB,GAC7B;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CAO/C"}
@@ -0,0 +1,23 @@
1
+ import { parseArgs } from "node:util";
2
+ /** Bad CLI input — surfaced with exit code 2, distinct from runtime failures (1). */
3
+ export class UsageError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "UsageError";
7
+ }
8
+ }
9
+ /**
10
+ * Strict flag parsing with a uniform usage-error shape across commands.
11
+ * Values keep their raw type (declared options are TypeScript-blind here —
12
+ * narrow at the call site, relying on the declared option `type`).
13
+ */
14
+ export function parseFlags(command, args, options) {
15
+ try {
16
+ const parsed = parseArgs({ args, options, strict: true, allowPositionals: true });
17
+ return { values: parsed.values, positionals: parsed.positionals };
18
+ }
19
+ catch (err) {
20
+ throw new UsageError(`kimchictl ${command}: ${err instanceof Error ? err.message : String(err)}`);
21
+ }
22
+ }
23
+ //# sourceMappingURL=flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flags.js","sourceRoot":"../src/","sources":["commands/flags.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,SAAS,EAAE,MAAM,WAAW,CAAA;AAElE,qFAAqF;AACrF,MAAM,OAAO,UAAW,SAAQ,KAAK;IACpC,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,YAAY,CAAA;IACzB,CAAC;CACD;AAID;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACzB,OAAe,EACf,IAAc,EACd,OAA+B;IAE/B,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAA;QACjF,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAoB,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAA;IAChF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,IAAI,UAAU,CAAC,aAAa,OAAO,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAClG,CAAC;AACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Convert known domain errors into a clean message + exit code instead of a
3
+ * stack trace. Unknown errors rethrow so real bugs stay loud.
4
+ * A wrapped handler still fits CommandDefinition["run"] (deps optional).
5
+ *
6
+ * ✗ <message> → 1 (auth/network/usage-state errors)
7
+ * kimchictl cmd: <usage> → 2 (flag parsing)
8
+ */
9
+ export declare function guardCommand<TDeps>(handler: (args: string[], deps?: TDeps) => Promise<number>): (args: string[], deps?: TDeps) => Promise<number>;
10
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"../src/","sources":["commands/guard.ts"],"names":[],"mappings":"AAQA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EACjC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,GACxD,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAyBnD"}
@@ -0,0 +1,41 @@
1
+ import { AmbiguousWorkspaceError, WorkspaceNotFoundError } from "../api/resolver.js";
2
+ import { TemplatesUnavailableError } from "../api/templates.js";
3
+ import { RemoteAuthError, RemoteNetworkError } from "../api/types.js";
4
+ import { ModelsFetchError } from "../auth/models-providers.js";
5
+ import { NotLoggedInError } from "../auth/resolve.js";
6
+ import { BridgeError } from "../ssh/bridge.js";
7
+ import { UsageError } from "./flags.js";
8
+ /**
9
+ * Convert known domain errors into a clean message + exit code instead of a
10
+ * stack trace. Unknown errors rethrow so real bugs stay loud.
11
+ * A wrapped handler still fits CommandDefinition["run"] (deps optional).
12
+ *
13
+ * ✗ <message> → 1 (auth/network/usage-state errors)
14
+ * kimchictl cmd: <usage> → 2 (flag parsing)
15
+ */
16
+ export function guardCommand(handler) {
17
+ return async (args, deps) => {
18
+ try {
19
+ return await handler(args, deps);
20
+ }
21
+ catch (err) {
22
+ if (err instanceof UsageError) {
23
+ console.error(err.message);
24
+ return 2;
25
+ }
26
+ if (err instanceof NotLoggedInError ||
27
+ err instanceof ModelsFetchError ||
28
+ err instanceof TemplatesUnavailableError ||
29
+ err instanceof WorkspaceNotFoundError ||
30
+ err instanceof AmbiguousWorkspaceError ||
31
+ err instanceof BridgeError ||
32
+ err instanceof RemoteAuthError ||
33
+ err instanceof RemoteNetworkError) {
34
+ console.error(`✗ ${err.message}`);
35
+ return 1;
36
+ }
37
+ throw err;
38
+ }
39
+ };
40
+ }
41
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"../src/","sources":["commands/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AACpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC3B,OAA0D;IAE1D,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QAC3B,IAAI,CAAC;YACJ,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;gBAC/B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;gBAC1B,OAAO,CAAC,CAAA;YACT,CAAC;YACD,IACC,GAAG,YAAY,gBAAgB;gBAC/B,GAAG,YAAY,gBAAgB;gBAC/B,GAAG,YAAY,yBAAyB;gBACxC,GAAG,YAAY,sBAAsB;gBACrC,GAAG,YAAY,uBAAuB;gBACtC,GAAG,YAAY,WAAW;gBAC1B,GAAG,YAAY,eAAe;gBAC9B,GAAG,YAAY,kBAAkB,EAChC,CAAC;gBACF,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;gBACjC,OAAO,CAAC,CAAA;YACT,CAAC;YACD,MAAM,GAAG,CAAA;QACV,CAAC;IACF,CAAC,CAAA;AACF,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { CommandDefinition } from "./registry.js";
2
+ /** Print top-level usage. Hidden commands are deliberately omitted. */
3
+ export declare function printHelp(commands: readonly CommandDefinition[], log?: (line: string) => void): void;
4
+ //# sourceMappingURL=help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"../src/","sources":["commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAEtD,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,EAAE,GAAG,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAkB,GAAG,IAAI,CAgBjH"}
@@ -0,0 +1,19 @@
1
+ /** Print top-level usage. Hidden commands are deliberately omitted. */
2
+ export function printHelp(commands, log = console.log) {
3
+ log(`kimchictl — manage kimchi remote workspaces
4
+ `);
5
+ log(`Usage:
6
+ kimchictl <command> [options]
7
+ `);
8
+ log(`Commands:`);
9
+ const visible = commands.filter((c) => !c.hidden);
10
+ const width = Math.max(...visible.map((c) => c.name.length));
11
+ for (const command of visible) {
12
+ log(` ${command.name.padEnd(width)} ${command.summary}`);
13
+ }
14
+ log(`
15
+ Global flags:
16
+ -h, --help Show this help
17
+ -v, --version Print the version`);
18
+ }
19
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"../src/","sources":["commands/help.ts"],"names":[],"mappings":"AAEA,uEAAuE;AACvE,MAAM,UAAU,SAAS,CAAC,QAAsC,EAAE,MAA8B,OAAO,CAAC,GAAG;IAC1G,GAAG,CAAC;CACJ,CAAC,CAAA;IACD,GAAG,CAAC;;CAEJ,CAAC,CAAA;IACD,GAAG,CAAC,WAAW,CAAC,CAAA;IAChB,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAC5D,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;QAC/B,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IAC3D,CAAC;IACD,GAAG,CAAC;;;mCAG8B,CAAC,CAAA;AACpC,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface LoginDeps {
2
+ /** Browser opener override (tests). */
3
+ open?: (url: string) => Promise<unknown>;
4
+ fetch?: typeof globalThis.fetch;
5
+ env?: NodeJS.ProcessEnv;
6
+ /** Override TTY interactivity detection (tests). */
7
+ interactive?: boolean;
8
+ }
9
+ /**
10
+ * `kimchictl login` — obtain an API key (browser OAuth or --api-key) and
11
+ * store it in the harness's shared credential files:
12
+ * - auth.json: one `{ type: "api_key", key }` per Kimchi provider id
13
+ * (ids from an existing models.json plus a fresh metadata fetch)
14
+ * - config.json: the `apiKey` field
15
+ * …so the harness picks the login up without its own `kimchi login`.
16
+ */
17
+ export declare function runLogin(args: string[], deps?: LoginDeps): Promise<number>;
18
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"../src/","sources":["commands/login.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,SAAS;IACzB,uCAAuC;IACvC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmEpF"}
@@ -0,0 +1,80 @@
1
+ import { kimchiProviderIdsFromModelsJson, syncSharedAuth } from "../auth/auth-file.js";
2
+ import { authenticateViaBrowser } from "../auth/browser-login.js";
3
+ import { writeApiKeyToConfig } from "../auth/config-file.js";
4
+ import { fetchKimchiProviderIds, ModelsFetchError } from "../auth/models-providers.js";
5
+ import { resolveAuthJsonPath, resolveConfigJsonPath, resolveModelsJsonPath } from "../auth/paths.js";
6
+ import { ensureSshIntegration } from "../ssh/connect.js";
7
+ import { parseFlags } from "./flags.js";
8
+ import { isInteractive } from "./prompt.js";
9
+ /**
10
+ * `kimchictl login` — obtain an API key (browser OAuth or --api-key) and
11
+ * store it in the harness's shared credential files:
12
+ * - auth.json: one `{ type: "api_key", key }` per Kimchi provider id
13
+ * (ids from an existing models.json plus a fresh metadata fetch)
14
+ * - config.json: the `apiKey` field
15
+ * …so the harness picks the login up without its own `kimchi login`.
16
+ */
17
+ export async function runLogin(args, deps = {}) {
18
+ const { values } = parseFlags("login", args, {
19
+ "api-key": { type: "string" },
20
+ });
21
+ const env = deps.env ?? process.env;
22
+ const authPath = resolveAuthJsonPath(env);
23
+ const configPath = resolveConfigJsonPath(env);
24
+ const modelsPath = resolveModelsJsonPath(env);
25
+ let apiKey;
26
+ const apiKeyFlag = values["api-key"];
27
+ const apiKeyProvided = apiKeyFlag !== undefined;
28
+ if (apiKeyProvided) {
29
+ // parseArgs guarantees a string here (--api-key is declared type: "string").
30
+ apiKey = typeof apiKeyFlag === "string" ? apiKeyFlag.trim() : "";
31
+ if (!apiKey) {
32
+ console.error("kimchictl login: --api-key must not be empty");
33
+ return 2;
34
+ }
35
+ }
36
+ else {
37
+ const interactive = deps.interactive ?? isInteractive();
38
+ if (!interactive) {
39
+ console.error("kimchictl login: interactive login requires a TTY; use --api-key for non-interactive mode");
40
+ return 1;
41
+ }
42
+ const { token } = await authenticateViaBrowser({
43
+ open: deps.open,
44
+ onBrowserUrl: (url) => console.log(`If the browser does not open, visit:\n${url}`),
45
+ });
46
+ apiKey = token;
47
+ }
48
+ // Validate the key against the models metadata API and derive the full
49
+ // Kimchi provider-id set (kimchi-dev + kimchi-dev/<upstream>) in one call.
50
+ let fetchedIds = [];
51
+ try {
52
+ fetchedIds = await fetchKimchiProviderIds(apiKey, { fetch: deps.fetch });
53
+ }
54
+ catch (err) {
55
+ if (err instanceof ModelsFetchError && err.status === 401) {
56
+ console.error("✗ Invalid API key. Please check the key and try again.");
57
+ return 1;
58
+ }
59
+ const detail = err instanceof Error ? err.message : String(err);
60
+ if (apiKeyProvided) {
61
+ // Mirror the harness's strict api-key login: an unverifiable key is not saved.
62
+ console.error(`✗ Could not verify the API key (${detail}). Try again later.`);
63
+ return 1;
64
+ }
65
+ // Browser login: the token came from the web app seconds ago, so keep it and
66
+ // fall back to the ids we know. (Harness: non-strict refresh with cache fallback.)
67
+ console.error(`⚠ Could not refresh the Kimchi model list (${detail}); saving the base credential. ` +
68
+ `Run \`kimchictl login\` again when the endpoint is reachable to pick up sub-provider credentials.`);
69
+ }
70
+ const providerIds = [...new Set([...kimchiProviderIdsFromModelsJson(modelsPath), ...fetchedIds])];
71
+ await syncSharedAuth(authPath, apiKey, providerIds);
72
+ writeApiKeyToConfig(configPath, apiKey);
73
+ console.log(`✓ Logged in to Kimchi — credentials saved to ${authPath}`);
74
+ console.log(" Shared with the kimchi coding harness; no second login needed there.");
75
+ // First login is the natural moment to finish the native SSH integration
76
+ // (automatic-with-notice; KIMCHICTL_NO_SSH_SETUP opts out).
77
+ await ensureSshIntegration({ env });
78
+ return 0;
79
+ }
80
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"../src/","sources":["commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface LogoutDeps {
2
+ /** Confirmation override (tests). */
3
+ confirm?: () => Promise<boolean>;
4
+ env?: NodeJS.ProcessEnv;
5
+ /** Override TTY interactivity detection (tests). */
6
+ interactive?: boolean;
7
+ }
8
+ /**
9
+ * `kimchictl logout` — remove the shared Kimchi credential from BOTH
10
+ * auth.json and config.json. This signs out the kimchi coding harness too
11
+ * (the credential is shared by design), which the user must acknowledge.
12
+ */
13
+ export declare function runLogout(args: string[], deps?: LogoutDeps): Promise<number>;
14
+ //# sourceMappingURL=logout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"../src/","sources":["commands/logout.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IAC1B,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAiCtF"}
@@ -0,0 +1,41 @@
1
+ import { clearSharedAuth, readSavedApiKey } from "../auth/auth-file.js";
2
+ import { readApiKeyFromConfig, writeApiKeyToConfig } from "../auth/config-file.js";
3
+ import { resolveAuthJsonPath, resolveConfigJsonPath } from "../auth/paths.js";
4
+ import { parseFlags } from "./flags.js";
5
+ import { confirm, isInteractive } from "./prompt.js";
6
+ /**
7
+ * `kimchictl logout` — remove the shared Kimchi credential from BOTH
8
+ * auth.json and config.json. This signs out the kimchi coding harness too
9
+ * (the credential is shared by design), which the user must acknowledge.
10
+ */
11
+ export async function runLogout(args, deps = {}) {
12
+ const { values } = parseFlags("logout", args, {
13
+ force: { type: "boolean", short: "f" },
14
+ });
15
+ const env = deps.env ?? process.env;
16
+ const authPath = resolveAuthJsonPath(env);
17
+ const configPath = resolveConfigJsonPath(env);
18
+ const hasCredential = readSavedApiKey(authPath) !== undefined || readApiKeyFromConfig(configPath) !== undefined;
19
+ if (!hasCredential) {
20
+ console.log("Not logged in.");
21
+ return 0;
22
+ }
23
+ if (!values.force) {
24
+ console.error("This also signs out the kimchi coding harness (the login is shared).");
25
+ const interactive = deps.interactive ?? isInteractive();
26
+ const ok = await (deps.confirm ? deps.confirm() : interactive ? confirm("Log out everywhere? [y/N] ") : false);
27
+ if (!ok) {
28
+ if (!deps.confirm && !interactive) {
29
+ console.error("kimchictl logout: not interactive — re-run with --force to confirm");
30
+ return 1;
31
+ }
32
+ console.log("Aborted.");
33
+ return 0;
34
+ }
35
+ }
36
+ await clearSharedAuth(authPath);
37
+ writeApiKeyToConfig(configPath, undefined);
38
+ console.log("✓ Logged out of Kimchi (kimchictl and the kimchi harness).");
39
+ return 0;
40
+ }
41
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"../src/","sources":["commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACvE,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAClF,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAUpD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc,EAAE,OAAmB,EAAE;IACpE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE;QAC7C,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;KACtC,CAAC,CAAA;IACF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAA;IAEnC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAA;IACzC,MAAM,UAAU,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAA;IAE7C,MAAM,aAAa,GAAG,eAAe,CAAC,QAAQ,CAAC,KAAK,SAAS,IAAI,oBAAoB,CAAC,UAAU,CAAC,KAAK,SAAS,CAAA;IAC/G,IAAI,CAAC,aAAa,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;QAC7B,OAAO,CAAC,CAAA;IACT,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,sEAAsE,CAAC,CAAA;QACrF,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,aAAa,EAAE,CAAA;QACvD,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QAC9G,IAAI,CAAC,EAAE,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;gBACnC,OAAO,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAA;gBACnF,OAAO,CAAC,CAAA;YACT,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;YACvB,OAAO,CAAC,CAAA;QACT,CAAC;IACF,CAAC;IAED,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC/B,mBAAmB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;IAC1C,OAAO,CAAC,GAAG,CAAC,4DAA4D,CAAC,CAAA;IACzE,OAAO,CAAC,CAAA;AACT,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** True when an interactive prompt can be shown (both streams are terminals). */
2
+ export declare function isInteractive(): boolean;
3
+ /**
4
+ * Ask a y/N question on the TTY. Anything but exactly "y" or "yes"
5
+ * (case-insensitive) counts as no. Only call when isInteractive() — callers
6
+ * must supply the non-interactive fallback (`--force` hint or a default).
7
+ */
8
+ export declare function confirm(question: string): Promise<boolean>;
9
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"../src/","sources":["commands/prompt.ts"],"names":[],"mappings":"AAEA,iFAAiF;AACjF,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQhE"}
@@ -0,0 +1,21 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ /** True when an interactive prompt can be shown (both streams are terminals). */
3
+ export function isInteractive() {
4
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
5
+ }
6
+ /**
7
+ * Ask a y/N question on the TTY. Anything but exactly "y" or "yes"
8
+ * (case-insensitive) counts as no. Only call when isInteractive() — callers
9
+ * must supply the non-interactive fallback (`--force` hint or a default).
10
+ */
11
+ export async function confirm(question) {
12
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
13
+ try {
14
+ const answer = await rl.question(question);
15
+ return /^(y|yes)$/i.test(answer.trim());
16
+ }
17
+ finally {
18
+ rl.close();
19
+ }
20
+ }
21
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"../src/","sources":["commands/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,iFAAiF;AACjF,MAAM,UAAU,aAAa;IAC5B,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,QAAgB;IAC7C,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC5E,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAC1C,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACxC,CAAC;YAAS,CAAC;QACV,EAAE,CAAC,KAAK,EAAE,CAAA;IACX,CAAC;AACF,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shape mirrors kimchi-dev's `src/commands/registry.ts` CommandDefinition so
3
+ * the harness bridge can mount kimchictl commands without adaptation.
4
+ */
5
+ export interface CommandDefinition {
6
+ name: string;
7
+ summary: string;
8
+ /** Hidden commands stay functional but are omitted from help (internal bridges like `ssh proxy`). */
9
+ hidden?: boolean;
10
+ /** Run this command. Receives args after the command name. Return the exit code; undefined means 0. */
11
+ run: (args: string[]) => Promise<number | undefined> | number | undefined;
12
+ }
13
+ export declare const COMMANDS: CommandDefinition[];
14
+ export declare function findCommand(name: string): CommandDefinition | undefined;
15
+ export declare function isKnownCommand(name: string | undefined): boolean;
16
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"../src/","sources":["commands/registry.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,qGAAqG;IACrG,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,uGAAuG;IACvG,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAAA;CACzE;AAED,eAAO,MAAM,QAAQ,EAAE,iBAAiB,EAkBvC,CAAA;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAEvE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEhE"}
@@ -0,0 +1,35 @@
1
+ import { runCompletion } from "./completion.js";
2
+ import { guardCommand } from "./guard.js";
3
+ import { runLogin } from "./login.js";
4
+ import { runLogout } from "./logout.js";
5
+ import { runSsh } from "./ssh.js";
6
+ import { runUpdate } from "./update.js";
7
+ import { runVersion } from "./version.js";
8
+ import { runWhoami } from "./whoami.js";
9
+ import { runWorkspace } from "./workspace.js";
10
+ export const COMMANDS = [
11
+ { name: "login", summary: "Authenticate via browser (shared with the kimchi harness)", run: guardCommand(runLogin) },
12
+ { name: "logout", summary: "Sign out everywhere (also signs out the harness)", run: guardCommand(runLogout) },
13
+ { name: "whoami", summary: "Show the authenticated user", run: guardCommand(runWhoami) },
14
+ {
15
+ name: "workspace",
16
+ summary: "Manage remote workspaces (create/list/get/delete)",
17
+ run: guardCommand(runWorkspace),
18
+ },
19
+ { name: "ws", summary: "Alias for workspace", hidden: true, run: guardCommand(runWorkspace) },
20
+ { name: "ssh", summary: "Connect to a workspace over SSH", run: guardCommand(runSsh) },
21
+ { name: "update", summary: "Check for and install a newer kimchictl release", run: guardCommand(runUpdate) },
22
+ {
23
+ name: "completion",
24
+ summary: "Print shell completions (kimchictl completion bash|zsh|fish)",
25
+ run: guardCommand(runCompletion),
26
+ },
27
+ { name: "version", summary: "Print the kimchictl version", run: runVersion },
28
+ ];
29
+ export function findCommand(name) {
30
+ return COMMANDS.find((c) => c.name === name);
31
+ }
32
+ export function isKnownCommand(name) {
33
+ return name !== undefined && COMMANDS.some((c) => c.name === name);
34
+ }
35
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"../src/","sources":["commands/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAe7C,MAAM,CAAC,MAAM,QAAQ,GAAwB;IAC5C,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,2DAA2D,EAAE,GAAG,EAAE,YAAY,CAAC,QAAQ,CAAC,EAAE;IACpH,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,kDAAkD,EAAE,GAAG,EAAE,YAAY,CAAC,SAAS,CAAC,EAAE;IAC7G,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,6BAA6B,EAAE,GAAG,EAAE,YAAY,CAAC,SAAS,CAAC,EAAE;IACxF;QACC,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,mDAAmD;QAC5D,GAAG,EAAE,YAAY,CAAC,YAAY,CAAC;KAC/B;IACD,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,YAAY,CAAC,YAAY,CAAC,EAAE;IAC7F,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,iCAAiC,EAAE,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE;IACtF,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iDAAiD,EAAE,GAAG,EAAE,YAAY,CAAC,SAAS,CAAC,EAAE;IAC5G;QACC,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,8DAA8D;QACvE,GAAG,EAAE,YAAY,CAAC,aAAa,CAAC;KAChC;IACD,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,6BAA6B,EAAE,GAAG,EAAE,UAAU,EAAE;CAC5E,CAAA;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACvC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAwB;IACtD,OAAO,IAAI,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AACnE,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface SshDeps {
2
+ fetch?: typeof globalThis.fetch;
3
+ env?: NodeJS.ProcessEnv;
4
+ interactive?: boolean;
5
+ /** Exec seam (tests). */
6
+ exec?: (args: string[]) => number;
7
+ sleep?: (ms: number) => Promise<void>;
8
+ }
9
+ /**
10
+ * `kimchictl ssh …` — connect, or manage the SSH integration. The hidden
11
+ * `proxy` subcommand is the ProxyCommand target in the generated ssh_config
12
+ * and is deliberately absent from help (internal plumbing, not user-facing).
13
+ */
14
+ export declare function runSsh(args: string[], deps?: SshDeps): Promise<number>;
15
+ //# sourceMappingURL=ssh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.d.ts","sourceRoot":"../src/","sources":["commands/ssh.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,OAAO;IACvB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,yBAAyB;IACzB,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAA;IACjC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC;AAMD;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,OAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBhF"}