@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,100 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { isDeepStrictEqual } from "node:util";
4
+ import { lock } from "proper-lockfile";
5
+ import { isKimchiProvider } from "./provider-ids.js";
6
+ /**
7
+ * Shared auth.json access — a port of the kimchi harness's `syncPiAuth`
8
+ * (kimchi-dev:src/pi-auth.ts). The file format is the harness's pi auth
9
+ * storage:
10
+ *
11
+ * { "<providerId>": { "type": "api_key", "key": "…" }, … }
12
+ *
13
+ * written pretty-printed (2-space indent, trailing newline), mode 0600, under
14
+ * a proper-lockfile lock. kimchictl must stay byte-compatible: the harness
15
+ * reads the entries this writes, and vice versa.
16
+ *
17
+ * Difference vs the harness port: the harness derives provider ids from
18
+ * models.json and throws when it is missing; kimchictl takes the ids as a
19
+ * parameter (the login command derives them from models.json when present and
20
+ * from the models metadata API otherwise) so it can run on machines that
21
+ * have never installed the harness.
22
+ */
23
+ /** Read the raw auth.json map. Returns {} when the file is absent or blank. */
24
+ export function readAuthJson(authPath) {
25
+ if (!existsSync(authPath))
26
+ return {};
27
+ return JSON.parse(readFileSync(authPath, "utf-8") || "{}");
28
+ }
29
+ /** Read the saved Kimchi API key, if the shared auth.json has one. */
30
+ export function readSavedApiKey(authPath) {
31
+ const entry = readAuthJson(authPath)["kimchi-dev"];
32
+ if (entry && typeof entry === "object" && "key" in entry && typeof entry.key === "string" && entry.key.length > 0) {
33
+ return entry.key;
34
+ }
35
+ return undefined;
36
+ }
37
+ /**
38
+ * Replace all Kimchi-managed entries in auth.json: delete every existing
39
+ * kimchi provider credential (`kimchi-dev`, `kimchi-dev/*`,
40
+ * `kimchi-experimental`), then, when apiKey is non-empty, write
41
+ * `{ type: "api_key", key }` for each id in providerIds (deduped,
42
+ * "kimchi-dev" always included). Empty apiKey = pure clear.
43
+ *
44
+ * Non-Kimchi providers are preserved untouched. Mirrors the harness: no-op
45
+ * content only re-chmods to 0600 instead of rewriting.
46
+ */
47
+ export async function syncSharedAuth(authPath, apiKey, providerIds) {
48
+ mkdirSync(dirname(authPath), { recursive: true, mode: 0o700 });
49
+ const release = await lock(authPath, { realpath: false, retries: 10 });
50
+ try {
51
+ const authExists = existsSync(authPath);
52
+ if (!apiKey && !authExists)
53
+ return;
54
+ const credentials = authExists ? readAuthJson(authPath) : {};
55
+ const nextCredentials = { ...credentials };
56
+ for (const providerId of Object.keys(nextCredentials)) {
57
+ if (isKimchiProvider(providerId)) {
58
+ delete nextCredentials[providerId];
59
+ }
60
+ }
61
+ if (apiKey) {
62
+ const ids = new Set([...providerIds.filter(isKimchiProvider), "kimchi-dev"]);
63
+ for (const providerId of ids) {
64
+ nextCredentials[providerId] = { type: "api_key", key: apiKey };
65
+ }
66
+ }
67
+ if (authExists && isDeepStrictEqual(credentials, nextCredentials)) {
68
+ chmodSync(authPath, 0o600);
69
+ return;
70
+ }
71
+ writeFileSync(authPath, `${JSON.stringify(nextCredentials, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
72
+ chmodSync(authPath, 0o600);
73
+ }
74
+ finally {
75
+ await release();
76
+ }
77
+ }
78
+ /** Remove all Kimchi credentials from auth.json (logout path). No-op when absent. */
79
+ export async function clearSharedAuth(authPath) {
80
+ await syncSharedAuth(authPath, "", []);
81
+ }
82
+ /** Kimchi provider ids listed in an existing harness models.json ({} when absent/unreadable). */
83
+ export function kimchiProviderIdsFromModelsJson(modelsPath) {
84
+ let parsed;
85
+ try {
86
+ parsed = JSON.parse(readFileSync(modelsPath, "utf-8"));
87
+ }
88
+ catch {
89
+ // models.json is the harness's file; an absent or corrupt one must not
90
+ // block kimchictl login — the provider-id list falls back to the metadata fetch.
91
+ return [];
92
+ }
93
+ if (!parsed || typeof parsed !== "object" || !("providers" in parsed))
94
+ return [];
95
+ const providers = parsed.providers;
96
+ if (!providers || typeof providers !== "object")
97
+ return [];
98
+ return Object.keys(providers).filter(isKimchiProvider);
99
+ }
100
+ //# sourceMappingURL=auth-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-file.js","sourceRoot":"../src/","sources":["auth/auth-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Browser login — ported from the kimchi harness (kimchi-dev:src/cli-auth/):
3
+ * transient 127.0.0.1 callback server → open `webApp/cli-auth?callback=…&state=…`
4
+ * → web app delivers the API key back to the callback. Same URL grammar and
5
+ * state-CSRF validation so both tools can drive the same web flow.
6
+ */
7
+ /** Web app base URL: KIMCHI_WEB_APP_URL override, default https://app.kimchi.dev. */
8
+ export declare function resolveWebAppUrl(options?: {
9
+ webAppUrl?: string;
10
+ }): string;
11
+ export interface CallbackResult {
12
+ token?: string;
13
+ error?: string;
14
+ }
15
+ export interface CallbackServer {
16
+ port: number;
17
+ url: string;
18
+ result: Promise<CallbackResult>;
19
+ close: () => void;
20
+ }
21
+ export declare function generateState(): string;
22
+ /**
23
+ * Start a temporary HTTP server on 127.0.0.1 to receive the token callback.
24
+ * Validates the state parameter against CSRF; times out after 5 minutes.
25
+ */
26
+ export declare function startCallbackServer(expectedState: string): Promise<CallbackServer>;
27
+ export interface BrowserAuthOptions {
28
+ /** Base URL of the Kimchi web app (default: https://app.kimchi.dev; env: KIMCHI_WEB_APP_URL) */
29
+ webAppUrl?: string;
30
+ /** Abort the flow (tears down the callback server early). */
31
+ signal?: AbortSignal;
32
+ /** Invoked with the browser URL before the browser opens. */
33
+ onBrowserUrl?: (url: string) => void;
34
+ /** Injected browser opener (tests). */
35
+ open?: (url: string) => Promise<unknown>;
36
+ }
37
+ export interface BrowserAuthResult {
38
+ token: string;
39
+ }
40
+ /**
41
+ * Authenticate via browser: redirect the user to the Kimchi web app; the
42
+ * resulting API key is sent back to the transient localhost callback server.
43
+ */
44
+ export declare function authenticateViaBrowser(options?: BrowserAuthOptions): Promise<BrowserAuthResult>;
45
+ //# sourceMappingURL=browser-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-login.d.ts","sourceRoot":"../src/","sources":["auth/browser-login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEzE;AAuBD,MAAM,WAAW,cAAc;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAA;CACjB;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAsHlF;AAED,MAAM,WAAW,kBAAkB;IAClC,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,uCAAuC;IACvC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,MAAM,CAAA;CACb;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4CzG"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Browser login — ported from the kimchi harness (kimchi-dev:src/cli-auth/):
3
+ * transient 127.0.0.1 callback server → open `webApp/cli-auth?callback=…&state=…`
4
+ * → web app delivers the API key back to the callback. Same URL grammar and
5
+ * state-CSRF validation so both tools can drive the same web flow.
6
+ */
7
+ import { randomBytes } from "node:crypto";
8
+ import { createServer } from "node:http";
9
+ import open from "open";
10
+ const CALLBACK_PATH = "/callback";
11
+ const CALLBACK_TIMEOUT_MS = 300_000; // 5 minutes, mirrors the harness
12
+ const SUCCESS_MESSAGE = "Your CLI is now connected. You can close this window and return to kimchictl.";
13
+ const DEFAULT_WEB_APP_URL = "https://app.kimchi.dev";
14
+ /** Web app base URL: KIMCHI_WEB_APP_URL override, default https://app.kimchi.dev. */
15
+ export function resolveWebAppUrl(options) {
16
+ return options?.webAppUrl ?? process.env.KIMCHI_WEB_APP_URL ?? DEFAULT_WEB_APP_URL;
17
+ }
18
+ function escapeHtml(text) {
19
+ return text
20
+ .replaceAll("&", "&amp;")
21
+ .replaceAll("<", "&lt;")
22
+ .replaceAll(">", "&gt;")
23
+ .replaceAll('"', "&quot;")
24
+ .replaceAll("'", "&#39;");
25
+ }
26
+ function pageHtml(title, heading, message, ok) {
27
+ const color = ok ? "#16a34a" : "#dc2626";
28
+ return `<!DOCTYPE html>
29
+ <html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title>
30
+ <style>body{font-family:system-ui,sans-serif;display:flex;min-height:100vh;margin:0;align-items:center;justify-content:center;background:#fafafa}
31
+ main{max-width:28rem;padding:2rem;text-align:center}h1{color:${color};font-size:1.25rem}p{color:#444}</style></head>
32
+ <body><main><h1>${escapeHtml(heading)}</h1><p>${escapeHtml(message)}</p></main></body></html>`;
33
+ }
34
+ const successHtml = (message) => pageHtml("Login successful", "Login successful", message, true);
35
+ const errorHtml = (title, message) => pageHtml(title, title, message, false);
36
+ export function generateState() {
37
+ return randomBytes(32).toString("hex");
38
+ }
39
+ /**
40
+ * Start a temporary HTTP server on 127.0.0.1 to receive the token callback.
41
+ * Validates the state parameter against CSRF; times out after 5 minutes.
42
+ */
43
+ export function startCallbackServer(expectedState) {
44
+ return new Promise((resolveStart, rejectStart) => {
45
+ let server;
46
+ let resolved = false;
47
+ let timeoutTimer;
48
+ let resolveResult;
49
+ function finish(result) {
50
+ if (resolved)
51
+ return;
52
+ resolved = true;
53
+ if (timeoutTimer)
54
+ clearTimeout(timeoutTimer);
55
+ resolveResult?.(result);
56
+ // Defer socket destruction so the HTTP response has time to flush
57
+ setTimeout(closeServer, 100);
58
+ }
59
+ function closeServer() {
60
+ if (!server)
61
+ return;
62
+ try {
63
+ server.closeAllConnections?.();
64
+ server.close();
65
+ }
66
+ catch {
67
+ // Already closing or closed — safe to ignore
68
+ }
69
+ server = undefined;
70
+ }
71
+ function onRequest(req, res) {
72
+ try {
73
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
74
+ const remote = req.socket.remoteAddress ?? "";
75
+ if (!(remote.startsWith("127.") || remote === "::1" || remote === "::ffff:127.0.0.1")) {
76
+ res.writeHead(403, { "Content-Type": "text/html", Connection: "close" });
77
+ res.end(errorHtml("Forbidden", "Only localhost connections are allowed."));
78
+ return;
79
+ }
80
+ if (url.pathname !== CALLBACK_PATH) {
81
+ res.writeHead(404, { "Content-Type": "text/plain", Connection: "close" });
82
+ res.end("Not found");
83
+ return;
84
+ }
85
+ const state = url.searchParams.get("state");
86
+ if (!state || state !== expectedState) {
87
+ const errorMsg = "This request isn't valid. Please try logging in again from your terminal.";
88
+ res.writeHead(400, { "Content-Type": "text/html", Connection: "close" });
89
+ res.end(errorHtml("Login error", errorMsg));
90
+ finish({ error: errorMsg });
91
+ return;
92
+ }
93
+ const error = url.searchParams.get("error");
94
+ if (error) {
95
+ const errorMsg = url.searchParams.get("error_description") || error;
96
+ res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
97
+ res.end(errorHtml("Authentication failed", errorMsg));
98
+ finish({ error: errorMsg });
99
+ return;
100
+ }
101
+ const token = url.searchParams.get("token");
102
+ if (!token) {
103
+ const errorMsg = "No token was returned by the authentication server";
104
+ res.writeHead(400, { "Content-Type": "text/html", Connection: "close" });
105
+ res.end(errorHtml("Missing token", errorMsg));
106
+ finish({ error: errorMsg });
107
+ return;
108
+ }
109
+ res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
110
+ res.end(successHtml(SUCCESS_MESSAGE));
111
+ finish({ token });
112
+ }
113
+ catch (err) {
114
+ const message = err instanceof Error ? err.message : String(err);
115
+ res.writeHead(500, { "Content-Type": "text/plain", Connection: "close" });
116
+ res.end("Internal server error");
117
+ finish({ error: `Unexpected server error: ${message}` });
118
+ req.socket.destroy();
119
+ }
120
+ }
121
+ server = createServer(onRequest);
122
+ server.listen(0, "127.0.0.1", () => {
123
+ const addr = server?.address();
124
+ if (!addr || typeof addr === "string") {
125
+ closeServer();
126
+ rejectStart(new Error("Could not determine callback server port"));
127
+ return;
128
+ }
129
+ const port = addr.port;
130
+ timeoutTimer = setTimeout(() => {
131
+ finish({ error: "Browser login timed out — please try again" });
132
+ }, CALLBACK_TIMEOUT_MS);
133
+ const resultPromise = new Promise((resolve) => {
134
+ resolveResult = resolve;
135
+ });
136
+ resolveStart({
137
+ port,
138
+ url: `http://127.0.0.1:${port}${CALLBACK_PATH}`,
139
+ result: resultPromise,
140
+ close: () => {
141
+ finish({ error: "Login cancelled" });
142
+ },
143
+ });
144
+ });
145
+ server.on("error", (err) => {
146
+ closeServer();
147
+ rejectStart(err);
148
+ });
149
+ });
150
+ }
151
+ /**
152
+ * Authenticate via browser: redirect the user to the Kimchi web app; the
153
+ * resulting API key is sent back to the transient localhost callback server.
154
+ */
155
+ export async function authenticateViaBrowser(options = {}) {
156
+ const webAppUrl = resolveWebAppUrl(options);
157
+ const state = generateState();
158
+ const callbackServer = await startCallbackServer(state);
159
+ const onAbort = () => callbackServer.close();
160
+ try {
161
+ if (options.signal?.aborted) {
162
+ callbackServer.close();
163
+ throw new Error("Login cancelled");
164
+ }
165
+ options.signal?.addEventListener("abort", onAbort, { once: true });
166
+ const callbackUrl = encodeURIComponent(callbackServer.url);
167
+ const browserUrl = `${webAppUrl}/cli-auth?callback=${callbackUrl}&state=${encodeURIComponent(state)}`;
168
+ options.onBrowserUrl?.(browserUrl);
169
+ console.log("Opening your browser to complete login…");
170
+ const opener = options.open ?? open;
171
+ try {
172
+ await opener(browserUrl);
173
+ }
174
+ catch {
175
+ console.log("Couldn't open your browser automatically. Please visit the URL above manually.");
176
+ }
177
+ const result = await callbackServer.result;
178
+ if (result.error) {
179
+ throw new Error(result.error);
180
+ }
181
+ if (!result.token) {
182
+ throw new Error("Browser login completed but no token was received");
183
+ }
184
+ callbackServer.close();
185
+ return { token: result.token };
186
+ }
187
+ catch (err) {
188
+ callbackServer.close();
189
+ throw err;
190
+ }
191
+ finally {
192
+ options.signal?.removeEventListener("abort", onAbort);
193
+ }
194
+ }
195
+ //# sourceMappingURL=browser-login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-login.js","sourceRoot":"../src/","sources":["auth/browser-login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function readApiKeyFromConfig(configPath: string): string | undefined;
2
+ /** Set the apiKey field (pass undefined to remove it). Preserves all other fields. */
3
+ export declare function writeApiKeyToConfig(configPath: string, apiKey: string | undefined): void;
4
+ //# sourceMappingURL=config-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.d.ts","sourceRoot":"../src/","sources":["auth/config-file.ts"],"names":[],"mappings":"AA0BA,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ3E;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAYxF"}
@@ -0,0 +1,52 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ /**
4
+ * Minimal config.json access — the harness's other credential slot.
5
+ *
6
+ * The harness browser login writes the API key to BOTH auth.json and
7
+ * config.json (`writeApiKey` in kimchi-dev:src/config.ts), and its startup
8
+ * sync repairs auth.json from config.json. kimchictl mirrors that so a
9
+ * kimchictl-first login activates the harness without a second login, and a
10
+ * `kimchictl logout` doesn't leave a live key behind in config.json.
11
+ *
12
+ * Only the `apiKey` field is ever touched; all other fields are preserved.
13
+ */
14
+ function readConfigRaw(configPath) {
15
+ if (!existsSync(configPath))
16
+ return {};
17
+ try {
18
+ const parsed = JSON.parse(readFileSync(configPath, "utf-8") || "{}");
19
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
20
+ }
21
+ catch {
22
+ // A corrupt config.json must not wedge kimchictl; leave the harness to re-write it.
23
+ return {};
24
+ }
25
+ }
26
+ export function readApiKeyFromConfig(configPath) {
27
+ const raw = readConfigRaw(configPath);
28
+ // The harness accepts camelCase and snake_case historical spellings.
29
+ for (const field of ["apiKey", "api_key"]) {
30
+ const value = raw[field];
31
+ if (typeof value === "string" && value.length > 0)
32
+ return value;
33
+ }
34
+ return undefined;
35
+ }
36
+ /** Set the apiKey field (pass undefined to remove it). Preserves all other fields. */
37
+ export function writeApiKeyToConfig(configPath, apiKey) {
38
+ const raw = readConfigRaw(configPath);
39
+ if (apiKey === undefined) {
40
+ if (raw.apiKey === undefined)
41
+ return;
42
+ // undefined-assignment instead of delete: the file is immediately
43
+ // JSON.stringify'd, which drops undefined properties the same way.
44
+ raw.apiKey = undefined;
45
+ }
46
+ else {
47
+ raw.apiKey = apiKey;
48
+ }
49
+ mkdirSync(dirname(configPath), { recursive: true, mode: 0o700 });
50
+ writeFileSync(configPath, `${JSON.stringify(raw, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
51
+ }
52
+ //# sourceMappingURL=config-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.js","sourceRoot":"../src/","sources":["auth/config-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC;;;;;;;;;;GAUG;AAEH,SAAS,aAAa,CAAC,UAAkB;IACxC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,CAAA;IACtC,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,CAAA;QACpE,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAkC,CAAC,CAAC,CAAC,EAAE,CAAA;IACjH,CAAC;IAAC,MAAM,CAAC;QACR,oFAAoF;QACpF,OAAO,EAAE,CAAA;IACV,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,CAAA;IACrC,qEAAqE;IACrE,KAAK,MAAM,KAAK,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;QACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAA;IAChE,CAAC;IACD,OAAO,SAAS,CAAA;AACjB,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,mBAAmB,CAAC,UAAkB,EAAE,MAA0B;IACjF,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,CAAA;IACrC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;YAAE,OAAM;QACpC,kEAAkE;QAClE,mEAAmE;QACnE,GAAG,CAAC,MAAM,GAAG,SAAS,CAAA;IACvB,CAAC;SAAM,CAAC;QACP,GAAG,CAAC,MAAM,GAAG,MAAM,CAAA;IACpB,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAChE,aAAa,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;AACnG,CAAC"}
@@ -0,0 +1,17 @@
1
+ export declare class ModelsFetchError extends Error {
2
+ readonly status?: number;
3
+ constructor(message: string, options?: {
4
+ status?: number;
5
+ });
6
+ }
7
+ export interface FetchProviderIdsOptions {
8
+ /** LLM service base URL; default https://llm.kimchi.dev. Env override: KIMCHI_LLM_ENDPOINT. */
9
+ endpoint?: string;
10
+ fetch?: typeof globalThis.fetch;
11
+ }
12
+ /**
13
+ * Fetch the active model list and derive Kimchi provider ids from it.
14
+ * Throws ModelsFetchError with .status on HTTP failure (401 = invalid key).
15
+ */
16
+ export declare function fetchKimchiProviderIds(apiKey: string, options?: FetchProviderIdsOptions): Promise<string[]>;
17
+ //# sourceMappingURL=models-providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models-providers.d.ts","sourceRoot":"../src/","sources":["auth/models-providers.ts"],"names":[],"mappings":"AAgBA,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;gBACZ,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;CAK9D;AAUD,MAAM,WAAW,uBAAuB;IACvC,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAC/B;AAMD;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BjH"}
@@ -0,0 +1,63 @@
1
+ import { KIMCHI_PROVIDER_ID } from "./provider-ids.js";
2
+ /**
3
+ * Minimal models-metadata fetch used to (a) validate an API key at login and
4
+ * (b) derive the full set of Kimchi provider ids for auth.json
5
+ * ("kimchi-dev" plus one "kimchi-dev/<upstream>" per upstream provider),
6
+ * matching what the harness writes from models.json.
7
+ *
8
+ * Endpoint shape and derivation rules mirror kimchi-dev:src/models.ts
9
+ * (buildModelsConfig). Only the id set matters here — models.json itself
10
+ * remains the harness's file to write and is never created by kimchictl.
11
+ */
12
+ const DEFAULT_LLM_ENDPOINT = "https://llm.kimchi.dev";
13
+ const FETCH_TIMEOUT_MS = 20_000;
14
+ export class ModelsFetchError extends Error {
15
+ status;
16
+ constructor(message, options = {}) {
17
+ super(message);
18
+ this.name = "ModelsFetchError";
19
+ this.status = options.status;
20
+ }
21
+ }
22
+ function normalizeLlmEndpoint(endpoint) {
23
+ const trimmed = endpoint?.trim();
24
+ if (!trimmed)
25
+ return DEFAULT_LLM_ENDPOINT;
26
+ // Mirror the harness: scheme-less values get https:// so `dev.example.com` works.
27
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
28
+ return withScheme.replace(/\/+$/, "");
29
+ }
30
+ function resolveLlmEndpoint(options) {
31
+ return options?.endpoint ?? process.env.KIMCHI_LLM_ENDPOINT ?? DEFAULT_LLM_ENDPOINT;
32
+ }
33
+ /**
34
+ * Fetch the active model list and derive Kimchi provider ids from it.
35
+ * Throws ModelsFetchError with .status on HTTP failure (401 = invalid key).
36
+ */
37
+ export async function fetchKimchiProviderIds(apiKey, options) {
38
+ const fetchImpl = options?.fetch ?? globalThis.fetch;
39
+ const url = `${normalizeLlmEndpoint(resolveLlmEndpoint(options))}/v1/models/metadata?include_in_cli=true`;
40
+ const resp = await fetchImpl(url, {
41
+ headers: { Authorization: `Bearer ${apiKey}` },
42
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
43
+ });
44
+ if (!resp.ok) {
45
+ throw new ModelsFetchError(`Failed to fetch models: ${resp.status} ${resp.statusText}`, { status: resp.status });
46
+ }
47
+ const body = (await resp.json().catch(() => undefined));
48
+ if (!body || !Array.isArray(body.models)) {
49
+ throw new ModelsFetchError("Unexpected response shape from models API");
50
+ }
51
+ const ids = new Set([KIMCHI_PROVIDER_ID]);
52
+ for (const model of body.models) {
53
+ if (model && typeof model === "object" && "provider" in model) {
54
+ const provider = model.provider;
55
+ // "ai-enabler" entries live under the base "kimchi-dev" provider (mirrors buildModelsConfig).
56
+ if (typeof provider === "string" && provider !== "ai-enabler") {
57
+ ids.add(`${KIMCHI_PROVIDER_ID}/${provider}`);
58
+ }
59
+ }
60
+ }
61
+ return [...ids];
62
+ }
63
+ //# sourceMappingURL=models-providers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models-providers.js","sourceRoot":"../src/","sources":["auth/models-providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEtD;;;;;;;;;GASG;AAEH,MAAM,oBAAoB,GAAG,wBAAwB,CAAA;AACrD,MAAM,gBAAgB,GAAG,MAAM,CAAA;AAE/B,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACjC,MAAM,CAAS;IACxB,YAAY,OAAe,EAAE,UAA+B,EAAE;QAC7D,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAA;QAC9B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAC7B,CAAC;CACD;AAED,SAAS,oBAAoB,CAAC,QAAiB;IAC9C,MAAM,OAAO,GAAG,QAAQ,EAAE,IAAI,EAAE,CAAA;IAChC,IAAI,CAAC,OAAO;QAAE,OAAO,oBAAoB,CAAA;IACzC,kFAAkF;IAClF,MAAM,UAAU,GAAG,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,OAAO,EAAE,CAAA;IAC5F,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AACtC,CAAC;AAQD,SAAS,kBAAkB,CAAC,OAAiC;IAC5D,OAAO,OAAO,EAAE,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,oBAAoB,CAAA;AACpF,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,MAAc,EAAE,OAAiC;IAC7F,MAAM,SAAS,GAAG,OAAO,EAAE,KAAK,IAAI,UAAU,CAAC,KAAK,CAAA;IACpD,MAAM,GAAG,GAAG,GAAG,oBAAoB,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,yCAAyC,CAAA;IAEzG,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;QACjC,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;QAC9C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC;KAC7C,CAAC,CAAA;IACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACd,MAAM,IAAI,gBAAgB,CAAC,2BAA2B,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IACjH,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAqC,CAAA;IAC3F,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,gBAAgB,CAAC,2CAA2C,CAAC,CAAA;IACxE,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAS,CAAC,kBAAkB,CAAC,CAAC,CAAA;IACjD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,UAAU,IAAI,KAAK,EAAE,CAAC;YAC/D,MAAM,QAAQ,GAAI,KAA+B,CAAC,QAAQ,CAAA;YAC1D,8FAA8F;YAC9F,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;gBAC/D,GAAG,CAAC,GAAG,CAAC,GAAG,kBAAkB,IAAI,QAAQ,EAAE,CAAC,CAAA;YAC7C,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAA;AAChB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared-credential paths, mirroring kimchi-dev exactly:
3
+ *
4
+ * ~/.config/kimchi/harness/ agent dir — auth.json, models.json
5
+ * (overridable via KIMCHI_CODING_AGENT_DIR,
6
+ * same fallback as kimchi-dev:src/cli.ts)
7
+ * ~/.config/kimchi/config.json shared wizard config — the `apiKey` slot
8
+ * (fixed path, no env override — same as
9
+ * kimchi-dev:src/config.ts KIMCHI_CONFIG_PATH)
10
+ */
11
+ export declare function resolveKimchiAgentDir(env?: NodeJS.ProcessEnv): string;
12
+ export declare function resolveAuthJsonPath(env?: NodeJS.ProcessEnv): string;
13
+ export declare function resolveModelsJsonPath(env?: NodeJS.ProcessEnv): string;
14
+ export declare function resolveConfigJsonPath(env?: NodeJS.ProcessEnv): string;
15
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"../src/","sources":["auth/paths.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAElF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAEhF;AAED,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAElF;AAED,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAElF"}
@@ -0,0 +1,25 @@
1
+ import { homedir } from "node:os";
2
+ import { resolve } from "node:path";
3
+ /**
4
+ * Shared-credential paths, mirroring kimchi-dev exactly:
5
+ *
6
+ * ~/.config/kimchi/harness/ agent dir — auth.json, models.json
7
+ * (overridable via KIMCHI_CODING_AGENT_DIR,
8
+ * same fallback as kimchi-dev:src/cli.ts)
9
+ * ~/.config/kimchi/config.json shared wizard config — the `apiKey` slot
10
+ * (fixed path, no env override — same as
11
+ * kimchi-dev:src/config.ts KIMCHI_CONFIG_PATH)
12
+ */
13
+ export function resolveKimchiAgentDir(env = process.env) {
14
+ return env.KIMCHI_CODING_AGENT_DIR ?? resolve(env.HOME ?? homedir(), ".config", "kimchi", "harness");
15
+ }
16
+ export function resolveAuthJsonPath(env = process.env) {
17
+ return resolve(resolveKimchiAgentDir(env), "auth.json");
18
+ }
19
+ export function resolveModelsJsonPath(env = process.env) {
20
+ return resolve(resolveKimchiAgentDir(env), "models.json");
21
+ }
22
+ export function resolveConfigJsonPath(env = process.env) {
23
+ return resolve(env.HOME ?? homedir(), ".config", "kimchi", "config.json");
24
+ }
25
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"../src/","sources":["auth/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACzE,OAAO,GAAG,CAAC,uBAAuB,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;AACrG,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACvE,OAAO,OAAO,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACzE,OAAO,OAAO,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,CAAA;AAC1D,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACzE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAA;AAC1E,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const KIMCHI_PROVIDER_ID = "kimchi-dev";
2
+ /** True for every Kimchi-managed provider, including experimental models. */
3
+ export declare function isKimchiProvider(provider: string): boolean;
4
+ //# sourceMappingURL=provider-ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-ids.d.ts","sourceRoot":"../src/","sources":["auth/provider-ids.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB,eAAe,CAAA;AAG9C,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAM1D"}
@@ -0,0 +1,12 @@
1
+ // Provider-id predicates. Dependency-free mirror of kimchi-dev's
2
+ // src/kimchi-provider.ts — the auth.json contract is shared with the harness,
3
+ // so the id grammar must stay in lockstep.
4
+ export const KIMCHI_PROVIDER_ID = "kimchi-dev";
5
+ const KIMCHI_EXPERIMENTAL_PROVIDER_ID = "kimchi-experimental";
6
+ /** True for every Kimchi-managed provider, including experimental models. */
7
+ export function isKimchiProvider(provider) {
8
+ return (provider === KIMCHI_PROVIDER_ID ||
9
+ provider.startsWith(`${KIMCHI_PROVIDER_ID}/`) ||
10
+ provider === KIMCHI_EXPERIMENTAL_PROVIDER_ID);
11
+ }
12
+ //# sourceMappingURL=provider-ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-ids.js","sourceRoot":"../src/","sources":["auth/provider-ids.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,8EAA8E;AAC9E,2CAA2C;AAE3C,MAAM,CAAC,MAAM,kBAAkB,GAAG,YAAY,CAAA;AAC9C,MAAM,+BAA+B,GAAG,qBAAqB,CAAA;AAE7D,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAChD,OAAO,CACN,QAAQ,KAAK,kBAAkB;QAC/B,QAAQ,CAAC,UAAU,CAAC,GAAG,kBAAkB,GAAG,CAAC;QAC7C,QAAQ,KAAK,+BAA+B,CAC5C,CAAA;AACF,CAAC"}
@@ -0,0 +1,19 @@
1
+ export type ApiKeySource = "environment" | "config" | "auth-json";
2
+ export interface ResolvedApiKey {
3
+ key: string;
4
+ source: ApiKeySource;
5
+ }
6
+ /**
7
+ * Credential resolution, mirroring the harness precedence
8
+ * (kimchi-dev:src/config.ts — env key beats the saved login):
9
+ * 1. KIMCHI_API_KEY environment variable
10
+ * 2. config.json `apiKey` (the harness's saved key)
11
+ * 3. auth.json `kimchi-dev` entry
12
+ */
13
+ export declare function resolveApiKey(env?: NodeJS.ProcessEnv): ResolvedApiKey | undefined;
14
+ /** Standard error for commands that need a credential that isn't there. */
15
+ export declare function requireApiKey(env?: NodeJS.ProcessEnv): ResolvedApiKey;
16
+ export declare class NotLoggedInError extends Error {
17
+ constructor();
18
+ }
19
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"../src/","sources":["auth/resolve.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEjE,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,YAAY,CAAA;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,cAAc,GAAG,SAAS,CAW9F;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,cAAc,CAMlF;AAED,qBAAa,gBAAiB,SAAQ,KAAK;;CAK1C"}