@kittl/cli 0.0.1 → 0.0.3

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.
@@ -0,0 +1,196 @@
1
+ {
2
+ "commands": {
3
+ "whoami": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "Show authenticated Kittl account details",
7
+ "flags": {},
8
+ "hasDynamicHelp": false,
9
+ "hiddenAliases": [],
10
+ "id": "whoami",
11
+ "pluginAlias": "@kittl/cli",
12
+ "pluginName": "@kittl/cli",
13
+ "pluginType": "core",
14
+ "strict": true,
15
+ "isESM": true,
16
+ "relativePath": [
17
+ "dist",
18
+ "commands",
19
+ "auth",
20
+ "whoami.js"
21
+ ]
22
+ },
23
+ "app:init": {
24
+ "aliases": [],
25
+ "args": {
26
+ "path": {
27
+ "default": ".",
28
+ "description": "Directory for the app (created if it does not exist)",
29
+ "name": "path",
30
+ "required": false
31
+ }
32
+ },
33
+ "description": "Initialize a Kittl app in the current directory or a new folder",
34
+ "flags": {},
35
+ "hasDynamicHelp": false,
36
+ "hiddenAliases": [],
37
+ "id": "app:init",
38
+ "pluginAlias": "@kittl/cli",
39
+ "pluginName": "@kittl/cli",
40
+ "pluginType": "core",
41
+ "strict": true,
42
+ "isESM": true,
43
+ "relativePath": [
44
+ "dist",
45
+ "commands",
46
+ "app",
47
+ "init.js"
48
+ ]
49
+ },
50
+ "app:release": {
51
+ "aliases": [],
52
+ "args": {},
53
+ "description": "Mark the extension draft as ready for review. Kittl admin handles review from here.",
54
+ "flags": {
55
+ "yes": {
56
+ "char": "y",
57
+ "description": "Skip the confirmation screen",
58
+ "name": "yes",
59
+ "allowNo": false,
60
+ "type": "boolean"
61
+ }
62
+ },
63
+ "hasDynamicHelp": false,
64
+ "hiddenAliases": [],
65
+ "id": "app:release",
66
+ "pluginAlias": "@kittl/cli",
67
+ "pluginName": "@kittl/cli",
68
+ "pluginType": "core",
69
+ "strict": true,
70
+ "isESM": true,
71
+ "relativePath": [
72
+ "dist",
73
+ "commands",
74
+ "app",
75
+ "release.js"
76
+ ]
77
+ },
78
+ "app:update": {
79
+ "aliases": [],
80
+ "args": {},
81
+ "description": "Update the draft extension version with the current extension manifest file",
82
+ "flags": {},
83
+ "hasDynamicHelp": false,
84
+ "hiddenAliases": [],
85
+ "id": "app:update",
86
+ "pluginAlias": "@kittl/cli",
87
+ "pluginName": "@kittl/cli",
88
+ "pluginType": "core",
89
+ "strict": true,
90
+ "isESM": true,
91
+ "relativePath": [
92
+ "dist",
93
+ "commands",
94
+ "app",
95
+ "update.js"
96
+ ]
97
+ },
98
+ "app:upload": {
99
+ "aliases": [],
100
+ "args": {},
101
+ "description": "Upload build output to the extension draft.",
102
+ "flags": {
103
+ "dist": {
104
+ "description": "Path to the build output directory (relative to cwd)",
105
+ "name": "dist",
106
+ "default": "dist",
107
+ "hasDynamicHelp": false,
108
+ "multiple": false,
109
+ "type": "option"
110
+ },
111
+ "verbose": {
112
+ "char": "v",
113
+ "description": "List each dist file path on its own line as uploads complete (no S3 keys)",
114
+ "name": "verbose",
115
+ "allowNo": false,
116
+ "type": "boolean"
117
+ }
118
+ },
119
+ "hasDynamicHelp": false,
120
+ "hiddenAliases": [],
121
+ "id": "app:upload",
122
+ "pluginAlias": "@kittl/cli",
123
+ "pluginName": "@kittl/cli",
124
+ "pluginType": "core",
125
+ "strict": true,
126
+ "isESM": true,
127
+ "relativePath": [
128
+ "dist",
129
+ "commands",
130
+ "app",
131
+ "upload.js"
132
+ ]
133
+ },
134
+ "auth:login": {
135
+ "aliases": [],
136
+ "args": {},
137
+ "description": "Authenticate with your Kittl account",
138
+ "flags": {},
139
+ "hasDynamicHelp": false,
140
+ "hiddenAliases": [],
141
+ "id": "auth:login",
142
+ "pluginAlias": "@kittl/cli",
143
+ "pluginName": "@kittl/cli",
144
+ "pluginType": "core",
145
+ "strict": true,
146
+ "isESM": true,
147
+ "relativePath": [
148
+ "dist",
149
+ "commands",
150
+ "auth",
151
+ "login.js"
152
+ ]
153
+ },
154
+ "auth:logout": {
155
+ "aliases": [],
156
+ "args": {},
157
+ "description": "Clear local Kittl CLI session",
158
+ "flags": {},
159
+ "hasDynamicHelp": false,
160
+ "hiddenAliases": [],
161
+ "id": "auth:logout",
162
+ "pluginAlias": "@kittl/cli",
163
+ "pluginName": "@kittl/cli",
164
+ "pluginType": "core",
165
+ "strict": true,
166
+ "isESM": true,
167
+ "relativePath": [
168
+ "dist",
169
+ "commands",
170
+ "auth",
171
+ "logout.js"
172
+ ]
173
+ },
174
+ "auth:whoami": {
175
+ "aliases": [],
176
+ "args": {},
177
+ "description": "Show authenticated Kittl account details",
178
+ "flags": {},
179
+ "hasDynamicHelp": false,
180
+ "hiddenAliases": [],
181
+ "id": "auth:whoami",
182
+ "pluginAlias": "@kittl/cli",
183
+ "pluginName": "@kittl/cli",
184
+ "pluginType": "core",
185
+ "strict": true,
186
+ "isESM": true,
187
+ "relativePath": [
188
+ "dist",
189
+ "commands",
190
+ "auth",
191
+ "whoami.js"
192
+ ]
193
+ }
194
+ },
195
+ "version": "0.0.3"
196
+ }
package/package.json CHANGED
@@ -1,60 +1,51 @@
1
1
  {
2
2
  "name": "@kittl/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
+ "license": "Apache-2.0",
5
+ "private": false,
4
6
  "type": "module",
5
7
  "engines": {
6
8
  "node": ">=18"
7
9
  },
8
10
  "bin": {
9
- "kittl": "./bin/run.js"
11
+ "kittl": "./bin/run.js",
12
+ "kittl-dev": "./bin/dev.js"
10
13
  },
11
14
  "publishConfig": {
12
- "access": "public"
15
+ "access": "public",
16
+ "bin": {
17
+ "kittl": "./bin/run.js"
18
+ }
13
19
  },
14
20
  "files": [
21
+ "LICENSE",
22
+ "oclif.manifest.json",
15
23
  "bin/bootstrap.js",
16
24
  "bin/run.cmd",
17
25
  "bin/run.js",
18
26
  "dist"
19
27
  ],
28
+ "scripts": {
29
+ "postpack": "clean-package restore"
30
+ },
20
31
  "dependencies": {
21
32
  "@oclif/core": "^4.10.2",
22
33
  "axios": "^1.13.6",
23
34
  "cross-keychain": "^1.1.0",
24
- "tinyglobby": "^0.2.15",
25
35
  "ink": "^6.8.0",
26
36
  "ink-text-input": "^6.0.0",
27
37
  "jwt-decode": "^4.0.0",
28
38
  "mime-types": "^3.0.1",
29
39
  "open": "^11.0.0",
30
40
  "openid-client": "^6.8.2",
31
- "react": "19.0.0",
32
- "zod": "4.3.6"
33
- },
34
- "devDependencies": {
35
- "@types/mime-types": "^3.0.1",
36
- "@types/node": "^25.5.0",
37
- "@types/react": "19.0.0",
38
- "ink-testing-library": "^4.0.0",
39
- "oclif": "^4.22.96",
40
- "tsup": "8.0.2",
41
- "tsx": "4.21.0",
42
- "typescript": "5.9.3",
43
- "vitest": "^4.1.1"
41
+ "react": "catalog:",
42
+ "tinyglobby": "^0.2.15",
43
+ "zod": "catalog:"
44
44
  },
45
45
  "oclif": {
46
46
  "bin": "kittl",
47
47
  "commands": "./dist/commands",
48
48
  "dirname": "kittl",
49
49
  "topicSeparator": " "
50
- },
51
- "scripts": {
52
- "build": "tsup && oclif manifest",
53
- "build:watch": "tsup --watch --onSuccess \"oclif manifest\"",
54
- "dev": "node ./bin/dev.js",
55
- "dev:watch": "node --watch --watch-path=./src --watch-path=./bin ./bin/dev.js",
56
- "typecheck": "tsc --noEmit",
57
- "test": "vitest run",
58
- "test:watch": "vitest"
59
50
  }
60
- }
51
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/auth/whoami.ts"],"sourcesContent":["import { jwtDecode } from 'jwt-decode';\nimport type { IDToken } from 'openid-client';\nimport { BaseCommand } from '../../base-command';\n\nexport default class AuthWhoAmI extends BaseCommand {\n public static override description =\n 'Show authenticated Kittl account details';\n\n public async run(): Promise<void> {\n await this.parse(AuthWhoAmI);\n\n const session = await this.ensureAuthenticated();\n\n if (!session.idToken) {\n this.error(\n `No ID token found in session. Run \\`${this.config.bin} auth:login\\` again.`,\n { exit: 2 },\n );\n }\n\n const claims = jwtDecode<IDToken>(session.idToken);\n const email = claims.email ?? 'unknown';\n const name =\n claims.name ?? claims.preferred_username ?? claims.sub ?? 'unknown';\n\n this.log(`Name: ${name}`);\n this.log(`Email: ${email}`);\n }\n}\n"],"mappings":";;;;;AAAA,SAAS,iBAAiB;AAI1B,IAAqB,aAArB,MAAqB,oBAAmB,YAAY;AAAA,EAClD,OAAuB,cACrB;AAAA,EAEF,MAAa,MAAqB;AAChC,UAAM,KAAK,MAAM,WAAU;AAE3B,UAAM,UAAU,MAAM,KAAK,oBAAoB;AAE/C,QAAI,CAAC,QAAQ,SAAS;AACpB,WAAK;AAAA,QACH,uCAAuC,KAAK,OAAO,GAAG;AAAA,QACtD,EAAE,MAAM,EAAE;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,SAAS,UAAmB,QAAQ,OAAO;AACjD,UAAM,QAAQ,OAAO,SAAS;AAC9B,UAAM,OACJ,OAAO,QAAQ,OAAO,sBAAsB,OAAO,OAAO;AAE5D,SAAK,IAAI,SAAS,IAAI,EAAE;AACxB,SAAK,IAAI,UAAU,KAAK,EAAE;AAAA,EAC5B;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/services/auth.service.ts","../package.json","../src/utils.ts","../src/constants.ts","../src/services/session-vault.service.ts","../src/base-command.ts","../src/services/api.service.ts"],"sourcesContent":["import { createServer } from 'node:http';\nimport { URL } from 'node:url';\nimport open from 'open';\nimport * as oidc from 'openid-client';\nimport { z } from 'zod';\nimport { AUTH_CONFIG } from '../constants';\nimport type { Session } from '../types/session';\nimport { sessionVault } from './session-vault.service';\n\n// Seconds before access token expiry when we proactively refresh.\nconst ACCESS_TOKEN_REFRESH_BUFFER_SEC = 60;\n\nconst OAUTH2_TOKEN_ERROR_INVALID_GRANT = 'invalid_grant';\n\nexport type LoginOptions = {\n signal?: AbortSignal;\n};\n\nexport type CallbackResult = {\n callbackUrl: URL;\n};\n\nconst authConfigSchema = z.object({\n issuer: z.url(),\n clientId: z.string().min(1),\n redirectUri: z.url(),\n redirectPort: z.number().int().min(1).max(65_535),\n scope: z.string().min(1),\n oauthCallbackTimeoutMs: z.number().int().min(5_000).max(3_600_000),\n oauthSuccessRedirectUrl: z.url().optional(),\n});\n\nexport class LoginCancelledError extends Error {\n public constructor() {\n super('Login cancelled.');\n this.name = 'LoginCancelledError';\n }\n}\n\nexport class AuthService {\n private readonly config = authConfigSchema.parse(AUTH_CONFIG);\n\n // concurrent refresh attempts are merged into a single promise\n private refreshSessionPromise: Promise<Session | null> | null = null;\n\n /**\n * raw vault read.\n */\n public async getStoredSession(): Promise<Session | null> {\n return sessionVault.getSession();\n }\n\n /**\n * ensures token freshness (by silently refresh if needed)\n */\n public async getSession(): Promise<Session | null> {\n await this.getAccessToken();\n return this.getStoredSession();\n }\n\n /**\n * Returns a usable access token, silently refreshing when `expiresAt` is within {@link ACCESS_TOKEN_REFRESH_BUFFER_SEC}\n * seconds (or in the past). based on Session.expiresAt.\n */\n public async getAccessToken(): Promise<string | undefined> {\n const session = await this.getStoredSession();\n if (!session?.accessToken) return undefined;\n\n if (!this.shouldRefreshAccessToken(session)) {\n return session.accessToken;\n }\n\n if (!session.refreshToken) {\n await sessionVault.clear();\n return undefined;\n }\n\n const refreshed = await this.refreshSession();\n return refreshed?.accessToken;\n }\n\n /**\n * Refresh tokens via the OIDC token endpoint. On hard failure (e.g. `invalid_grant`), clears the vault.\n */\n public async refreshSession(): Promise<Session | null> {\n if (this.refreshSessionPromise) {\n return this.refreshSessionPromise;\n }\n this.refreshSessionPromise = this.performRefreshSession().finally(() => {\n this.refreshSessionPromise = null;\n });\n return this.refreshSessionPromise;\n }\n\n private shouldRefreshAccessToken(session: Session): boolean {\n if (session.expiresAt === undefined) return false;\n const now = Math.floor(Date.now() / 1000);\n return session.expiresAt <= now + ACCESS_TOKEN_REFRESH_BUFFER_SEC;\n }\n\n private async performRefreshSession(): Promise<Session | null> {\n const session = await this.getStoredSession();\n if (!session?.refreshToken) {\n await sessionVault.clear();\n return null;\n }\n\n try {\n const oidcConfig = await this.discoverOidcConfiguration();\n\n const tokenSet = await oidc.refreshTokenGrant(\n oidcConfig,\n session.refreshToken,\n { scope: this.config.scope },\n undefined,\n );\n\n const newSession = this.mapTokenSetToSession(tokenSet, session);\n\n await sessionVault.saveSession(newSession);\n return newSession;\n } catch (error) {\n if (\n error instanceof oidc.ResponseBodyError &&\n error.error === OAUTH2_TOKEN_ERROR_INVALID_GRANT\n ) {\n await sessionVault.clear();\n return null;\n }\n return null;\n }\n }\n\n private async discoverOidcConfiguration(): Promise<oidc.Configuration> {\n const issuerUrl = new URL(this.config.issuer);\n return oidc.discovery(\n issuerUrl,\n this.config.clientId,\n undefined,\n undefined,\n {\n ...(issuerUrl.protocol === 'http:'\n ? { execute: [oidc.allowInsecureRequests] as const }\n : {}),\n } as Parameters<typeof oidc.discovery>[4],\n );\n }\n\n public async login(options?: LoginOptions): Promise<Session> {\n const { signal } = options ?? {};\n\n const oidcConfig = await this.discoverOidcConfiguration();\n signal?.throwIfAborted();\n\n const codeVerifier = oidc.randomPKCECodeVerifier();\n const codeChallenge = await oidc.calculatePKCECodeChallenge(codeVerifier);\n const state = oidc.randomState();\n const nonce = oidc.randomNonce();\n\n const authorizationUrl = oidc.buildAuthorizationUrl(oidcConfig, {\n redirect_uri: this.config.redirectUri,\n response_type: 'code',\n scope: this.config.scope,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state,\n nonce,\n });\n\n signal?.throwIfAborted();\n await this.openBrowser(authorizationUrl.toString());\n\n const callback = await this.waitForCallback(signal);\n\n const tokenSet = await oidc.authorizationCodeGrant(\n oidcConfig,\n callback.callbackUrl,\n {\n pkceCodeVerifier: codeVerifier,\n expectedState: state,\n expectedNonce: nonce,\n },\n );\n\n const session = this.mapTokenSetToSession(tokenSet);\n\n await sessionVault.saveSession(session);\n return session;\n }\n\n public async logout(): Promise<void> {\n await sessionVault.clear();\n }\n\n /**\n * Starts a short-lived `http` server on the host/port from the configured `redirectUri` so the IdP can\n * redirect the browser to `…/callback?code=…&state=…` (Authorization Code + PKCE). The first matching request\n * stops the server and resolves with that URL for the token exchange (`authorizationCodeGrant`).\n *\n * - **Success response:** HTTP 302 to {@link AUTH_CONFIG.oauthSuccessRedirectUrl} when set; otherwise a minimal inline HTML page.\n */\n private async waitForCallback(signal?: AbortSignal): Promise<CallbackResult> {\n const redirectUrl = new URL(this.config.redirectUri);\n const hostname = redirectUrl.hostname;\n const port = Number(redirectUrl.port);\n const pathname = redirectUrl.pathname;\n\n const timeoutSignal = AbortSignal.timeout(\n this.config.oauthCallbackTimeoutMs,\n );\n const combinedSignal =\n signal !== undefined\n ? AbortSignal.any([signal, timeoutSignal])\n : timeoutSignal;\n\n return new Promise((resolve, reject) => {\n let closed = false;\n\n const server = createServer((req, res) => {\n try {\n const requestUrl = new URL(req.url ?? '', this.config.redirectUri);\n if (requestUrl.pathname !== pathname) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n\n const callbackUrl = new URL(this.config.redirectUri);\n callbackUrl.search = requestUrl.search;\n\n const brandUrl = this.config.oauthSuccessRedirectUrl;\n if (brandUrl) {\n res.writeHead(302, { Location: brandUrl });\n res.end();\n } else {\n res.statusCode = 200;\n res.setHeader('content-type', 'text/html; charset=utf-8');\n res.end(\n '<!doctype html><html><body><h2>Authentication complete.</h2><p>You can close this tab and return to the terminal.</p></body></html>',\n );\n }\n\n closeServer((closeErr?: Error) => {\n if (closeErr) {\n reject(closeErr);\n return;\n }\n resolve({ callbackUrl });\n });\n } catch (error) {\n closeServer(() => reject(error));\n }\n });\n\n const closeServer = (onClosed: (closeErr?: Error) => void): void => {\n if (closed) return;\n closed = true;\n combinedSignal.removeEventListener('abort', onCombinedAbort);\n if (!server.listening) {\n onClosed();\n return;\n }\n server.closeAllConnections();\n server.close((closeErr) => {\n onClosed(closeErr ?? undefined);\n });\n };\n\n function onCombinedAbort(): void {\n closeServer((closeErr?: Error) => {\n if (closeErr) {\n reject(closeErr);\n return;\n }\n if (signal?.aborted) {\n reject(signal.reason ?? new LoginCancelledError());\n return;\n }\n if (timeoutSignal.aborted) {\n reject(new Error('Login timed out. Please try again.'));\n return;\n }\n reject(new LoginCancelledError());\n });\n }\n\n server.on('error', (error: NodeJS.ErrnoException) => {\n closeServer(() => {\n if (error.code === 'EADDRINUSE') {\n const inUsePort =\n (error as NodeJS.ErrnoException & { port?: number }).port ?? port;\n reject(\n new Error(\n `Port ${String(inUsePort)} is already in use. Close the other app using it or set KITTL_REDIRECT_PORT to a free port, then try again.`,\n ),\n );\n return;\n }\n reject(error);\n });\n });\n\n // 1. Abort before any listen — avoids a half-started server; early exit skips `listen` entirely.\n combinedSignal.addEventListener('abort', onCombinedAbort, { once: true });\n if (combinedSignal.aborted) {\n onCombinedAbort();\n return;\n }\n\n server.listen(port, hostname);\n });\n }\n\n private async openBrowser(url: string): Promise<void> {\n await open(url);\n }\n\n private mapTokenSetToSession(\n tokenSet: oidc.TokenEndpointResponse,\n currentSession?: Session,\n ): Session {\n return {\n accessToken: tokenSet.access_token,\n refreshToken: tokenSet.refresh_token ?? currentSession?.refreshToken,\n idToken: tokenSet.id_token ?? currentSession?.idToken,\n tokenType: tokenSet.token_type ?? currentSession?.tokenType,\n expiresAt: tokenSet.expires_in\n ? Math.floor(Date.now() / 1000) + tokenSet.expires_in\n : undefined,\n };\n }\n}\n\nexport const authService = new AuthService();\n","{\n \"name\": \"@kittl/cli\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"type\": \"module\",\n \"bin\": {\n \"kittl\": \"./bin/run.js\",\n \"kittl-dev\": \"./bin/dev.js\"\n },\n \"publishConfig\": {\n \"bin\": {\n \"kittl\": \"./bin/run.js\"\n }\n },\n \"files\": [\n \"bin/bootstrap.js\",\n \"bin/run.cmd\",\n \"bin/run.js\",\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup && oclif manifest\",\n \"build:watch\": \"tsup --watch --onSuccess \\\"oclif manifest\\\"\",\n \"dev\": \"node ./bin/dev.js\",\n \"dev:watch\": \"node --watch --watch-path=./src --watch-path=./bin ./bin/dev.js\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@oclif/core\": \"^4.10.2\",\n \"axios\": \"^1.13.6\",\n \"cross-keychain\": \"^1.1.0\",\n \"ink\": \"^6.8.0\",\n \"jwt-decode\": \"^4.0.0\",\n \"open\": \"^11.0.0\",\n \"openid-client\": \"^6.8.2\",\n \"react\": \"catalog:\",\n \"zod\": \"catalog:\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.5.0\",\n \"@types/react\": \"catalog:\",\n \"ink-testing-library\": \"^4.0.0\",\n \"oclif\": \"^4.22.96\",\n \"tinyglobby\": \"^0.2.15\",\n \"tsup\": \"catalog:\",\n \"tsx\": \"catalog:\",\n \"typescript\": \"catalog:\",\n \"vitest\": \"^4.1.1\"\n },\n \"oclif\": {\n \"bin\": \"kittl\",\n \"commands\": \"./dist/commands\",\n \"dirname\": \"kittl\",\n \"topicSeparator\": \" \"\n }\n}\n","/** Silent UI teardown (e.g. Ink unmount) — not user-initiated cancel. */\nexport const INK_VIEW_UNMOUNT_REASON = Symbol('INK_VIEW_UNMOUNT_REASON');\n\n/**\n * Reads `KITTL_REDIRECT_PORT` (or {@link defaultPort}) and validates it is an integer in 1–65535.\n */\nexport function parseRedirectPortFromEnv(defaultPort: number): number {\n const raw = process.env.KITTL_REDIRECT_PORT ?? String(defaultPort);\n const n = Number(raw);\n if (!Number.isInteger(n) || n < 1 || n > 65_535) {\n throw new Error(\n `KITTL_REDIRECT_PORT must be an integer between 1 and 65535 (got ${JSON.stringify(raw)}).`,\n );\n }\n return n;\n}\n\n/** Fixed localhost OAuth callback path for Authorization Code + PKCE. */\nexport function localhostOAuthRedirectUri(port: number): string {\n return `http://localhost:${port}/callback`;\n}\n\nexport function getKittlEnvEntries(): string[] {\n return Object.entries(process.env)\n .filter(([key]) => key.startsWith('KITTL_'))\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => `${key}=${value ?? ''}`);\n}\n","import pkg from '../package.json' with { type: 'json' };\nimport { localhostOAuthRedirectUri, parseRedirectPortFromEnv } from './utils';\n\nconst { version } = pkg;\n\nexport const PRODUCTION = {\n issuer: 'https://auth.kittl.com/realms/kittl', //TODO: TBD\n apiBaseUrl: 'https://api.kittl.com',\n clientId: 'kittl-cli',\n redirectPort: 51771,\n} as const;\n\n// -----------------------------------------------------------------------------\n// OAuth / OIDC (PKCE login, keychain session)\n// -----------------------------------------------------------------------------\n\nexport const AUTH_CONFIG = {\n issuer: process.env.KITTL_ISSUER_URL ?? PRODUCTION.issuer,\n clientId: process.env.KITTL_CLIENT_ID ?? PRODUCTION.clientId,\n redirectPort: parseRedirectPortFromEnv(PRODUCTION.redirectPort),\n redirectUri: localhostOAuthRedirectUri(\n parseRedirectPortFromEnv(PRODUCTION.redirectPort),\n ),\n scope: process.env.KITTL_AUTH_SCOPE ?? 'openid profile email offline_access',\n oauthCallbackTimeoutMs: Number(\n process.env.KITTL_OAUTH_CALLBACK_TIMEOUT_MS ?? 120_000,\n ),\n /** Optional 302 after OAuth; if undefined it renders a fallback HTML. */\n oauthSuccessRedirectUrl:\n process.env.KITTL_OAUTH_SUCCESS_REDIRECT_URL || undefined,\n serviceName: 'kittl-cli',\n accountName: 'oauth-session',\n} as const;\n\n// -----------------------------------------------------------------------------\n// HTTP API (Axios base URL for Kittl APIs)\n// -----------------------------------------------------------------------------\n\nexport const API_CONFIG = {\n baseUrl: process.env.KITTL_API_BASE_URL ?? PRODUCTION.apiBaseUrl,\n timeoutMs: 30_000,\n userAgent: `kittl-cli/${version}`,\n} as const;\n","import { deletePassword, getPassword, setPassword } from 'cross-keychain';\nimport { AUTH_CONFIG } from '../constants';\nimport type { Session } from '../types/session';\n\n/**\n * Persists OIDC session (tokens) in the OS credential store (e.g. Keychain).\n */\nexport class SessionVault {\n private readonly keychainService = AUTH_CONFIG.serviceName;\n private readonly keychainAccount = AUTH_CONFIG.accountName;\n\n public async getSession(): Promise<Session | null> {\n const raw = await getPassword(this.keychainService, this.keychainAccount);\n if (!raw) return null;\n\n try {\n return JSON.parse(raw) as Session;\n } catch {\n return null;\n }\n }\n\n public async saveSession(session: Session): Promise<void> {\n await setPassword(\n this.keychainService,\n this.keychainAccount,\n JSON.stringify(session),\n );\n }\n\n public async clear(): Promise<void> {\n await deletePassword(this.keychainService, this.keychainAccount);\n }\n}\n\nexport const sessionVault = new SessionVault();\n","import { Command } from '@oclif/core';\nimport type { AxiosInstance } from 'axios';\nimport { render } from 'ink';\nimport type { FC } from 'react';\nimport React from 'react';\nimport { API_CONFIG } from './constants';\nimport { kittlApiService } from './services/api.service';\nimport { authService } from './services/auth.service';\nimport type { Session } from './types/session';\nimport { getKittlEnvEntries } from './utils';\n\nexport abstract class BaseCommand extends Command {\n protected session: Session | null = null;\n\n public override async init(): Promise<void> {\n await super.init();\n this.session = await authService.getSession();\n kittlApiService.setAccessTokenProvider(async () =>\n authService.getAccessToken(),\n );\n\n const envEntries = getKittlEnvEntries();\n this.debug(`API base URL: ${API_CONFIG.baseUrl}`);\n this.debug(`KITTL_* variables:\\n${envEntries.join('\\n') || '(none)'}`);\n }\n\n protected getKittlApiClient(): AxiosInstance {\n return kittlApiService.getClient();\n }\n\n /**\n * Ensures a valid access token (+ refreshes when the accessToken is near expiry).\n */\n protected async ensureAuthenticated(): Promise<Session> {\n const session = await authService.getSession();\n if (!session?.accessToken) {\n this.error(\n `Session expired. Run \\`${this.config.bin} auth:login\\` first.`,\n { exit: 2 },\n );\n }\n\n this.session = session;\n return session;\n }\n\n /**\n * Run an Ink view that reports a result via `onDone`, then unmount and return.\n * PRO TIP: use `this.log` / `this.error` after this resolves, not inside the view!! for final line stays on stdout.\n */\n protected async renderView<R>(\n View: FC<{ onDone: (result: R) => void }>,\n ): Promise<R> {\n return new Promise<R>((resolve, reject) => {\n const app = render(\n React.createElement(View, {\n onDone: (result: R) => {\n void (async () => {\n app.unmount();\n await app.waitUntilExit();\n resolve(result);\n })().catch(reject);\n },\n }),\n );\n });\n }\n}\n","import axios, {\n type AxiosError,\n AxiosHeaders,\n type AxiosInstance,\n type InternalAxiosRequestConfig,\n} from 'axios';\nimport { API_CONFIG } from '../constants';\nimport { authService } from './auth.service';\n\ntype AccessTokenProvider = () => Promise<string | undefined>;\ntype RetriableRequestConfig = InternalAxiosRequestConfig & {\n _retry?: boolean;\n};\n\nexport class KittlApiService {\n private readonly client: AxiosInstance;\n private accessTokenProvider?: AccessTokenProvider;\n\n public constructor() {\n this.client = axios.create({\n baseURL: API_CONFIG.baseUrl,\n timeout: API_CONFIG.timeoutMs,\n headers: {\n 'User-Agent': API_CONFIG.userAgent,\n },\n });\n\n this.client.interceptors.request.use(\n async (config: RetriableRequestConfig) => {\n if (config.skipAuth) {\n return config;\n }\n const token = await this.accessTokenProvider?.();\n if (token) {\n this.setAuthorizationHeader(config, token);\n }\n return config;\n },\n );\n\n this.client.interceptors.response.use(\n (response) => response,\n async (error: AxiosError) => {\n const originalRequest = error.config as\n | RetriableRequestConfig\n | undefined;\n // Retry only first-time 401 responses with a valid original request.\n if (\n error.response?.status !== 401 ||\n !originalRequest ||\n originalRequest.skipAuth ||\n originalRequest._retry\n ) {\n throw error;\n }\n\n // Mark as retried to avoid infinite retry loops.\n originalRequest._retry = true;\n // Force-refresh the token before replaying the request.\n const refreshedSession = await authService.refreshSession();\n const refreshedToken = refreshedSession?.accessToken;\n if (!refreshedToken) {\n throw error;\n }\n\n // Re-run the original request with updated Authorization header.\n this.setAuthorizationHeader(originalRequest, refreshedToken);\n return this.client.request(originalRequest);\n },\n );\n }\n\n public setAccessTokenProvider(provider: AccessTokenProvider): void {\n this.accessTokenProvider = provider;\n }\n\n public getClient(): AxiosInstance {\n return this.client;\n }\n\n private setAuthorizationHeader(\n config: InternalAxiosRequestConfig,\n token: string,\n ): void {\n const headers = AxiosHeaders.from(config.headers);\n headers.set('Authorization', `Bearer ${token}`);\n config.headers = headers;\n }\n}\n\nexport const kittlApiService = new KittlApiService();\n"],"mappings":"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","names":[]}
@@ -1,16 +0,0 @@
1
- import {
2
- BaseCommand
3
- } from "../../chunk-JGD3QFQS.js";
4
-
5
- // src/commands/app/create.ts
6
- var AppCreate = class _AppCreate extends BaseCommand {
7
- static description = "Create a Kittl app (coming soon)";
8
- async run() {
9
- await this.parse(_AppCreate);
10
- this.log("Not implemented yet.");
11
- }
12
- };
13
- export {
14
- AppCreate as default
15
- };
16
- //# sourceMappingURL=create.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/commands/app/create.ts"],"sourcesContent":["import { BaseCommand } from '../../base-command';\n\nexport default class AppCreate extends BaseCommand {\n public static override description = 'Create a Kittl app (coming soon)';\n\n public async run(): Promise<void> {\n await this.parse(AppCreate);\n\n this.log('Not implemented yet.');\n }\n}\n"],"mappings":";;;;;AAEA,IAAqB,YAArB,MAAqB,mBAAkB,YAAY;AAAA,EACjD,OAAuB,cAAc;AAAA,EAErC,MAAa,MAAqB;AAChC,UAAM,KAAK,MAAM,UAAS;AAE1B,SAAK,IAAI,sBAAsB;AAAA,EACjC;AACF;","names":[]}