urur 0.1.0 → 0.1.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.
- package/README.md +4 -2
- package/dist/index.js +18 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,13 +18,15 @@ npx urur
|
|
|
18
18
|
|
|
19
19
|
### `urur login`
|
|
20
20
|
|
|
21
|
-
GitHub
|
|
21
|
+
GitHub または メールアドレスでログインします。
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
urur login
|
|
25
|
-
urur login --port 3000 # カスタムポート指定
|
|
26
25
|
```
|
|
27
26
|
|
|
27
|
+
- **GitHub**: Device Flow でブラウザ認証(認証コードを入力)
|
|
28
|
+
- **メールアドレス**: Magic Link(OTP コード)で認証
|
|
29
|
+
|
|
28
30
|
### `urur submit`
|
|
29
31
|
|
|
30
32
|
プロダクトを投稿します。
|
package/dist/index.js
CHANGED
|
@@ -220,6 +220,7 @@ async function loginWithGitHub() {
|
|
|
220
220
|
expires_at: session.expiresAt
|
|
221
221
|
});
|
|
222
222
|
console.log(pc.green(`\u30ED\u30B0\u30A4\u30F3\u6210\u529F: ${session.userName}`));
|
|
223
|
+
printPostLoginGuide();
|
|
223
224
|
} catch (err) {
|
|
224
225
|
spinner.fail("\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
225
226
|
throw err;
|
|
@@ -252,6 +253,7 @@ async function loginWithEmail() {
|
|
|
252
253
|
expires_at: session.expiresAt
|
|
253
254
|
});
|
|
254
255
|
console.log(pc.green(`\u30ED\u30B0\u30A4\u30F3\u6210\u529F: ${session.email}`));
|
|
256
|
+
printPostLoginGuide();
|
|
255
257
|
}
|
|
256
258
|
} catch (err) {
|
|
257
259
|
console.log(
|
|
@@ -290,6 +292,12 @@ async function attemptOtpVerification(email) {
|
|
|
290
292
|
}
|
|
291
293
|
return null;
|
|
292
294
|
}
|
|
295
|
+
function printPostLoginGuide() {
|
|
296
|
+
console.log();
|
|
297
|
+
console.log(
|
|
298
|
+
`\u6B21\u306E\u30B9\u30C6\u30C3\u30D7: ${pc.cyan("urur submit")} \u3067\u30D7\u30ED\u30C0\u30AF\u30C8\u3092\u6295\u7A3F\u3067\u304D\u307E\u3059`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
293
301
|
|
|
294
302
|
// src/commands/logout.ts
|
|
295
303
|
import pc2 from "picocolors";
|
|
@@ -515,13 +523,15 @@ async function submit(options) {
|
|
|
515
523
|
}
|
|
516
524
|
}
|
|
517
525
|
displaySummary(formData);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
526
|
+
if (!options.yes) {
|
|
527
|
+
const confirmed = await confirm2({
|
|
528
|
+
message: "\u6295\u7A3F\u3057\u307E\u3059\u304B\uFF1F",
|
|
529
|
+
default: true
|
|
530
|
+
});
|
|
531
|
+
if (!confirmed) {
|
|
532
|
+
console.log(pc3.yellow("\u6295\u7A3F\u3092\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F\u3002"));
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
525
535
|
}
|
|
526
536
|
const spinner = ora2("\u6295\u7A3F\u4E2D...").start();
|
|
527
537
|
const { error: insertError } = await supabase.from("services").insert({
|
|
@@ -568,7 +578,7 @@ GitHub Device Flow \u307E\u305F\u306F\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\
|
|
|
568
578
|
`
|
|
569
579
|
~/.urur/credentials.json \u3092\u524A\u9664\u3057\u307E\u3059\u3002`
|
|
570
580
|
).action(logout);
|
|
571
|
-
program2.command("submit").description("\u30D7\u30ED\u30C0\u30AF\u30C8\u3092\u6295\u7A3F").option("--name <name>", "\u30D7\u30ED\u30C0\u30AF\u30C8\u540D\uFF08\u5FC5\u9808\uFF09").option("--url <url>", "\u30D7\u30ED\u30C0\u30AF\u30C8URL\uFF08\u5FC5\u9808\uFF09").option("--tagline <text>", "\u30BF\u30B0\u30E9\u30A4\u30F3\uFF08200\u6587\u5B57\u4EE5\u5185\uFF09").option("--description <text>", "\u8AAC\u660E\uFF082000\u6587\u5B57\u4EE5\u5185\uFF09").option("--logo-url <url>", "\u30ED\u30B4URL").option("-i, --interactive", "\u5BFE\u8A71\u30E2\u30FC\u30C9\u3067\u5165\u529B").addHelpText(
|
|
581
|
+
program2.command("submit").description("\u30D7\u30ED\u30C0\u30AF\u30C8\u3092\u6295\u7A3F").option("--name <name>", "\u30D7\u30ED\u30C0\u30AF\u30C8\u540D\uFF08\u5FC5\u9808\uFF09").option("--url <url>", "\u30D7\u30ED\u30C0\u30AF\u30C8URL\uFF08\u5FC5\u9808\uFF09").option("--tagline <text>", "\u30BF\u30B0\u30E9\u30A4\u30F3\uFF08200\u6587\u5B57\u4EE5\u5185\uFF09").option("--description <text>", "\u8AAC\u660E\uFF082000\u6587\u5B57\u4EE5\u5185\uFF09").option("--logo-url <url>", "\u30ED\u30B4URL").option("-i, --interactive", "\u5BFE\u8A71\u30E2\u30FC\u30C9\u3067\u5165\u529B").option("-y, --yes", "\u78BA\u8A8D\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u3066\u6295\u7A3F").addHelpText(
|
|
572
582
|
"after",
|
|
573
583
|
`
|
|
574
584
|
\u4F7F\u7528\u4F8B:
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/program.ts","../src/commands/login.ts","../src/lib/auth.ts","../src/lib/config.ts","../src/lib/edgeFunctionApi.ts","../src/lib/deviceFlow.ts","../src/lib/supabase.ts","../src/lib/magicLink.ts","../src/commands/logout.ts","../src/commands/submit.ts","../src/lib/validation.ts","../src/commands/whoami.ts","../src/index.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { login } from './commands/login.js'\nimport { logout } from './commands/logout.js'\nimport { submit } from './commands/submit.js'\nimport { whoami } from './commands/whoami.js'\n\nexport function createProgram(): Command {\n const program = new Command()\n\n program\n .name('urur')\n .description('CLI for urur.dev - 個人開発プロダクトディレクトリ')\n .version('0.1.0')\n\n program\n .command('login')\n .description('GitHubまたはメールアドレスでログイン')\n .addHelpText(\n 'after',\n `\n使用例:\n $ urur login\n\nGitHub Device Flow またはメールアドレス(Magic Link)で認証します。`,\n )\n .action(login)\n\n program\n .command('logout')\n .description('ログアウト(認証情報を削除)')\n .addHelpText(\n 'after',\n `\n~/.urur/credentials.json を削除します。`,\n )\n .action(logout)\n\n program\n .command('submit')\n .description('プロダクトを投稿')\n .option('--name <name>', 'プロダクト名(必須)')\n .option('--url <url>', 'プロダクトURL(必須)')\n .option('--tagline <text>', 'タグライン(200文字以内)')\n .option('--description <text>', '説明(2000文字以内)')\n .option('--logo-url <url>', 'ロゴURL')\n .option('-i, --interactive', '対話モードで入力')\n .addHelpText(\n 'after',\n `\n使用例:\n $ urur submit -i\n $ urur submit --name \"My App\" --url \"https://example.com\"\n $ urur submit --name \"My App\" --url \"https://example.com\" --tagline \"便利なツール\"\n\nオプション未指定の場合は対話モードで入力します。`,\n )\n .action(submit)\n\n program\n .command('whoami')\n .description('ログイン中のユーザー情報を表示')\n .addHelpText(\n 'after',\n `\nGitHubユーザー名とメールアドレスを表示します。\n未ログインの場合は \"urur login\" を実行してください。`,\n )\n .action(whoami)\n\n return program\n}\n","import { confirm, input, select } from '@inquirer/prompts'\nimport open from 'open'\nimport ora from 'ora'\nimport pc from 'picocolors'\nimport {\n clearCredentials,\n loadCredentials,\n saveCredentials,\n} from '../lib/auth.js'\nimport { pollForSession, requestCode } from '../lib/deviceFlow.js'\nimport { sendOtp, verifyOtp } from '../lib/magicLink.js'\nimport { getSupabaseClient } from '../lib/supabase.js'\n\nconst MAX_OTP_ATTEMPTS = 3\n\nexport async function login(): Promise<void> {\n const supabase = getSupabaseClient()\n\n // Check existing credentials\n const existing = await loadCredentials()\n if (existing) {\n await supabase.auth.setSession({\n access_token: existing.access_token,\n refresh_token: existing.refresh_token,\n })\n const { data } = await supabase.auth.getUser()\n const username = data?.user?.user_metadata?.user_name ?? '不明'\n\n console.log(pc.green(`既にログイン済みです: ${username}`))\n\n const shouldRelogin = await confirm({\n message: '再ログインしますか?',\n default: false,\n })\n\n if (!shouldRelogin) {\n return\n }\n\n await clearCredentials()\n }\n\n // Select login method\n const method = await select({\n message: 'ログイン方法を選択してください',\n choices: [\n { name: 'GitHub', value: 'github' },\n { name: 'メールアドレス', value: 'email' },\n ],\n })\n\n if (method === 'github') {\n await loginWithGitHub()\n } else {\n await loginWithEmail()\n }\n}\n\nasync function loginWithGitHub(): Promise<void> {\n try {\n const deviceCode = await requestCode()\n\n console.log()\n console.log(pc.bold(`認証コード: ${pc.cyan(deviceCode.userCode)}`))\n console.log(\n `以下の URL でコードを入力してください: ${deviceCode.verificationUri}`,\n )\n console.log()\n\n await open(deviceCode.verificationUri)\n\n const spinner = ora('GitHub で認証を待っています...').start()\n\n try {\n const session = await pollForSession(\n deviceCode.deviceCode,\n deviceCode.interval,\n deviceCode.expiresIn,\n )\n\n spinner.succeed('認証完了')\n\n await saveCredentials({\n access_token: session.accessToken,\n refresh_token: session.refreshToken,\n expires_at: session.expiresAt,\n })\n\n console.log(pc.green(`ログイン成功: ${session.userName}`))\n } catch (err) {\n spinner.fail('認証に失敗しました')\n throw err\n }\n } catch (err) {\n console.log(\n pc.red(`エラー: ${err instanceof Error ? err.message : String(err)}`),\n )\n process.exitCode = 1\n }\n}\n\nasync function loginWithEmail(): Promise<void> {\n try {\n const email = await input({\n message: 'メールアドレスを入力してください',\n validate: (value) => {\n if (!value.includes('@')) {\n return '有効なメールアドレスを入力してください'\n }\n return true\n },\n })\n\n await sendOtp(email)\n console.log(pc.green(`${email} に認証コードを送信しました`))\n\n const session = await attemptOtpVerification(email)\n\n if (session) {\n await saveCredentials({\n access_token: session.accessToken,\n refresh_token: session.refreshToken,\n expires_at: session.expiresAt,\n })\n\n console.log(pc.green(`ログイン成功: ${session.email}`))\n }\n } catch (err) {\n console.log(\n pc.red(`エラー: ${err instanceof Error ? err.message : String(err)}`),\n )\n process.exitCode = 1\n }\n}\n\nasync function attemptOtpVerification(email: string): Promise<{\n accessToken: string\n refreshToken: string\n expiresAt: number\n email: string\n} | null> {\n for (let attempt = 0; attempt < MAX_OTP_ATTEMPTS; attempt++) {\n const token = await input({\n message: '認証コードを入力してください',\n })\n\n try {\n return await verifyOtp(email, token)\n } catch {\n const remaining = MAX_OTP_ATTEMPTS - attempt - 1\n if (remaining > 0) {\n console.log(\n pc.yellow(`認証コードが無効です。残り ${remaining} 回試行できます。`),\n )\n }\n }\n }\n\n // All attempts failed, offer resend\n const shouldResend = await confirm({\n message: '認証コードを再送信しますか?',\n default: true,\n })\n\n if (shouldResend) {\n await sendOtp(email)\n console.log(pc.green(`${email} に認証コードを再送信しました`))\n\n const token = await input({\n message: '認証コードを入力してください',\n })\n\n return await verifyOtp(email, token)\n }\n\n return null\n}\n","import fs from 'node:fs/promises'\nimport { CONFIG_DIR, CREDENTIALS_PATH } from './config.js'\n\nexport interface Credentials {\n access_token: string\n refresh_token: string\n expires_at: number\n}\n\nexport async function loadCredentials(): Promise<Credentials | null> {\n try {\n const data = await fs.readFile(CREDENTIALS_PATH, 'utf-8')\n return JSON.parse(data) as Credentials\n } catch {\n return null\n }\n}\n\nexport async function saveCredentials(credentials: Credentials): Promise<void> {\n await fs.mkdir(CONFIG_DIR, { recursive: true })\n await fs.writeFile(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2))\n}\n\nexport async function clearCredentials(): Promise<void> {\n try {\n await fs.unlink(CREDENTIALS_PATH)\n } catch {\n // Ignore if file doesn't exist\n }\n}\n","import os from 'node:os'\nimport path from 'node:path'\n\nexport const CONFIG_DIR = path.join(os.homedir(), '.urur')\nexport const CREDENTIALS_PATH = path.join(CONFIG_DIR, 'credentials.json')\n\n// tsup の define でビルド時に埋め込まれる\ndeclare const __SUPABASE_URL__: string\ndeclare const __SUPABASE_ANON_KEY__: string\ndeclare const __WEB_URL__: string\n\nexport const SUPABASE_URL: string = __SUPABASE_URL__\nexport const SUPABASE_ANON_KEY: string = __SUPABASE_ANON_KEY__\nexport const WEB_URL: string = __WEB_URL__\n","import { SUPABASE_ANON_KEY, SUPABASE_URL } from './config.js'\n\nexport interface DeviceCodeResponse {\n userCode: string\n verificationUri: string\n expiresIn: number\n interval: number\n deviceCode: string\n}\n\nexport interface DeviceFlowSession {\n accessToken: string\n refreshToken: string\n expiresAt: number\n userName: string\n}\n\nexport type PollTokenResponse =\n | { status: 'pending' }\n | { status: 'slow_down' }\n | { status: 'expired' }\n | { status: 'success'; session: DeviceFlowSession }\n | { status: 'error'; message: string }\n\nconst EDGE_FUNCTION_URL = `${SUPABASE_URL}/functions/v1/cli-auth`\n\nfunction buildHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${SUPABASE_ANON_KEY}`,\n }\n}\n\nexport async function requestDeviceCode(): Promise<DeviceCodeResponse> {\n const res = await fetch(EDGE_FUNCTION_URL, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({ action: 'device-code' }),\n })\n\n if (!res.ok) {\n throw new Error(`Edge Function エラー: ${res.status}`)\n }\n\n return res.json()\n}\n\nexport async function pollToken(\n deviceCode: string,\n): Promise<PollTokenResponse> {\n const res = await fetch(EDGE_FUNCTION_URL, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({ action: 'poll-token', deviceCode }),\n })\n\n if (!res.ok) {\n throw new Error(`Edge Function エラー: ${res.status}`)\n }\n\n return res.json()\n}\n","import {\n type DeviceCodeResponse,\n type DeviceFlowSession,\n pollToken,\n requestDeviceCode,\n} from './edgeFunctionApi.js'\n\nexport type { DeviceCodeResponse, DeviceFlowSession }\n\nexport async function requestCode(): Promise<DeviceCodeResponse> {\n return requestDeviceCode()\n}\n\nexport async function pollForSession(\n deviceCode: string,\n interval: number,\n expiresIn: number,\n): Promise<DeviceFlowSession> {\n let currentInterval = interval\n let elapsed = 0\n\n while (elapsed < expiresIn) {\n await new Promise((resolve) => setTimeout(resolve, currentInterval * 1000))\n elapsed += currentInterval\n\n if (elapsed >= expiresIn) {\n break\n }\n\n const response = await pollToken(deviceCode)\n\n switch (response.status) {\n case 'success':\n return response.session\n case 'pending':\n continue\n case 'slow_down':\n currentInterval += 5\n continue\n case 'expired':\n throw new Error(\n '認証の有効期限が切れました。再度 urur login を実行してください。',\n )\n case 'error':\n throw new Error(response.message)\n }\n }\n\n throw new Error(\n '認証がタイムアウトしました。再度 urur login を実行してください。',\n )\n}\n","import { createClient } from '@supabase/supabase-js'\nimport { SUPABASE_ANON_KEY, SUPABASE_URL } from './config.js'\n\nfunction createMemoryStorage() {\n const store = new Map<string, string>()\n return {\n getItem(key: string): string | null {\n return store.get(key) ?? null\n },\n setItem(key: string, value: string): void {\n store.set(key, value)\n },\n removeItem(key: string): void {\n store.delete(key)\n },\n }\n}\n\nexport function getSupabaseClient() {\n return createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {\n auth: {\n storage: createMemoryStorage(),\n autoRefreshToken: false,\n detectSessionInUrl: false,\n },\n })\n}\n","import { getSupabaseClient } from './supabase.js'\n\nexport interface MagicLinkSession {\n accessToken: string\n refreshToken: string\n expiresAt: number\n email: string\n}\n\nexport async function sendOtp(email: string): Promise<void> {\n const supabase = getSupabaseClient()\n const { error } = await supabase.auth.signInWithOtp({ email })\n\n if (error) {\n throw new Error(error.message)\n }\n}\n\nexport async function verifyOtp(\n email: string,\n token: string,\n): Promise<MagicLinkSession> {\n const supabase = getSupabaseClient()\n const { data, error } = await supabase.auth.verifyOtp({\n email,\n token,\n type: 'email',\n })\n\n if (error) {\n throw new Error(error.message)\n }\n\n if (!data.session) {\n throw new Error('セッションが取得できませんでした')\n }\n\n return {\n accessToken: data.session.access_token,\n refreshToken: data.session.refresh_token,\n expiresAt: data.session.expires_at ?? 0,\n email: data.session.user?.email ?? email,\n }\n}\n","import pc from 'picocolors'\nimport { clearCredentials, loadCredentials } from '../lib/auth.js'\n\nexport async function logout(): Promise<void> {\n const credentials = await loadCredentials()\n if (!credentials) {\n console.log(pc.yellow('ログインしていません。'))\n return\n }\n\n await clearCredentials()\n console.log(pc.green('ログアウトしました。'))\n}\n","import { confirm, input } from '@inquirer/prompts'\nimport ora from 'ora'\nimport pc from 'picocolors'\nimport { loadCredentials } from '../lib/auth.js'\nimport { getSupabaseClient } from '../lib/supabase.js'\nimport type { ServiceFormData } from '../lib/validation.js'\nimport {\n validateAll,\n validateDescription,\n validateLogoUrl,\n validateName,\n validateTagline,\n validateUrl,\n} from '../lib/validation.js'\n\ninterface SubmitOptions {\n name?: string\n url?: string\n tagline?: string\n description?: string\n logoUrl?: string\n interactive?: boolean\n}\n\nfunction shouldUseInteractive(options: SubmitOptions): boolean {\n return options.interactive === true || !options.name || !options.url\n}\n\nasync function promptServiceData(): Promise<ServiceFormData> {\n const name = await input({\n message: 'プロダクト名(必須):',\n validate: (value) => validateName(value) ?? true,\n })\n\n const url = await input({\n message: 'プロダクトURL(必須):',\n validate: (value) => validateUrl(value) ?? true,\n })\n\n const tagline = await input({\n message: 'タグライン(省略可):',\n validate: (value) => {\n if (!value) return true\n return validateTagline(value) ?? true\n },\n })\n\n const description = await input({\n message: '説明(省略可):',\n validate: (value) => {\n if (!value) return true\n return validateDescription(value) ?? true\n },\n })\n\n const logo_url = await input({\n message: 'ロゴURL(省略可):',\n validate: (value) => {\n if (!value) return true\n return validateLogoUrl(value) ?? true\n },\n })\n\n return {\n name,\n url,\n tagline: tagline || undefined,\n description: description || undefined,\n logo_url: logo_url || undefined,\n }\n}\n\nfunction displaySummary(data: ServiceFormData): void {\n console.log(pc.bold('\\n投稿内容:'))\n console.log(` プロダクト名: ${data.name}`)\n console.log(` URL: ${data.url}`)\n if (data.tagline) console.log(` タグライン: ${data.tagline}`)\n if (data.description) console.log(` 説明: ${data.description}`)\n if (data.logo_url) console.log(` ロゴURL: ${data.logo_url}`)\n console.log('')\n}\n\nfunction displayErrors(errors: Record<string, string>): void {\n console.log(pc.red('\\nバリデーションエラー:'))\n for (const [, message] of Object.entries(errors)) {\n console.log(pc.red(` - ${message}`))\n }\n}\n\nexport async function submit(options: SubmitOptions): Promise<void> {\n // 1. Authentication check\n const credentials = await loadCredentials()\n if (!credentials) {\n console.log(pc.red('ログインが必要です。`urur login` を実行してください。'))\n process.exitCode = 1\n return\n }\n\n const supabase = getSupabaseClient()\n await supabase.auth.setSession({\n access_token: credentials.access_token,\n refresh_token: credentials.refresh_token,\n })\n\n const { data: userData, error: userError } = await supabase.auth.getUser()\n if (userError || !userData.user) {\n console.log(\n pc.red(\n `認証エラー: ${userError?.message ?? 'ユーザー情報を取得できませんでした'}。\\`urur login\\` で再ログインしてください。`,\n ),\n )\n process.exitCode = 1\n return\n }\n\n // 2. Data collection\n let formData: ServiceFormData\n\n if (shouldUseInteractive(options)) {\n formData = await promptServiceData()\n } else {\n // Build form data from CLI options\n formData = {\n name: options.name as string,\n url: options.url as string,\n tagline: options.tagline,\n description: options.description,\n logo_url: options.logoUrl,\n }\n\n // 3. Validation (CLI mode only)\n const result = validateAll(formData)\n if (!result.valid) {\n displayErrors(result.errors)\n process.exitCode = 1\n return\n }\n }\n\n // 4. Summary and confirmation\n displaySummary(formData)\n\n const confirmed = await confirm({\n message: '投稿しますか?',\n default: true,\n })\n\n if (!confirmed) {\n console.log(pc.yellow('投稿をキャンセルしました。'))\n return\n }\n\n // 5. Submit to Supabase\n const spinner = ora('投稿中...').start()\n\n const { error: insertError } = await supabase\n .from('services')\n .insert({\n user_id: userData.user.id,\n name: formData.name,\n url: formData.url,\n tagline: formData.tagline ?? null,\n description: formData.description ?? null,\n logo_url: formData.logo_url ?? null,\n source: 'cli' as const,\n })\n .select()\n .single()\n\n if (insertError) {\n spinner.fail()\n console.log(pc.red(`投稿エラー: ${insertError.message}`))\n process.exitCode = 1\n return\n }\n\n spinner.succeed()\n console.log(pc.green(`「${formData.name}」を投稿しました!`))\n}\n","/**\n * Service submission form validation\n * Shared between client and server\n */\n\nexport interface ServiceFormData {\n name: string\n url: string\n category_id?: string\n tagline?: string\n description?: string\n logo_url?: string\n}\n\nexport interface ValidationResult {\n valid: boolean\n errors: Record<string, string>\n}\n\n// Error messages in Japanese\nconst ERROR_MESSAGES = {\n name: {\n required: 'プロダクト名は必須です',\n maxLength: 'プロダクト名は100文字以内で入力してください',\n },\n url: {\n required: 'URLは必須です',\n invalid: '有効なURLを入力してください',\n },\n tagline: {\n maxLength: 'タグラインは200文字以内で入力してください',\n },\n description: {\n maxLength: '説明は2000文字以内で入力してください',\n },\n logo_url: {\n invalid: '有効なURLを入力してください',\n },\n} as const\n\n// Validation rules\nconst VALIDATION_RULES = {\n name: { required: true, maxLength: 100 },\n url: { required: true },\n tagline: { maxLength: 200 },\n description: { maxLength: 2000 },\n} as const\n\nfunction isValidUrl(value: string): boolean {\n try {\n new URL(value)\n return true\n } catch {\n return false\n }\n}\n\nexport function validateName(value: string): string | null {\n const trimmed = value.trim()\n if (!trimmed) {\n return ERROR_MESSAGES.name.required\n }\n if (trimmed.length > VALIDATION_RULES.name.maxLength) {\n return ERROR_MESSAGES.name.maxLength\n }\n return null\n}\n\nexport function validateUrl(value: string): string | null {\n const trimmed = value.trim()\n if (!trimmed) {\n return ERROR_MESSAGES.url.required\n }\n if (!isValidUrl(trimmed)) {\n return ERROR_MESSAGES.url.invalid\n }\n return null\n}\n\nexport function validateTagline(value: string | undefined): string | null {\n if (!value) {\n return null\n }\n if (value.length > VALIDATION_RULES.tagline.maxLength) {\n return ERROR_MESSAGES.tagline.maxLength\n }\n return null\n}\n\nexport function validateDescription(value: string | undefined): string | null {\n if (!value) {\n return null\n }\n if (value.length > VALIDATION_RULES.description.maxLength) {\n return ERROR_MESSAGES.description.maxLength\n }\n return null\n}\n\nexport function validateLogoUrl(value: string | undefined): string | null {\n if (!value) {\n return null\n }\n if (!isValidUrl(value)) {\n return ERROR_MESSAGES.logo_url.invalid\n }\n return null\n}\n\nexport function validateAll(data: Partial<ServiceFormData>): ValidationResult {\n const errors: Record<string, string> = {}\n\n const nameError = validateName(data.name ?? '')\n if (nameError) {\n errors.name = nameError\n }\n\n const urlError = validateUrl(data.url ?? '')\n if (urlError) {\n errors.url = urlError\n }\n\n const taglineError = validateTagline(data.tagline)\n if (taglineError) {\n errors.tagline = taglineError\n }\n\n const descriptionError = validateDescription(data.description)\n if (descriptionError) {\n errors.description = descriptionError\n }\n\n const logoUrlError = validateLogoUrl(data.logo_url)\n if (logoUrlError) {\n errors.logo_url = logoUrlError\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors,\n }\n}\n","import pc from 'picocolors'\n\nexport async function whoami(): Promise<void> {\n console.log(\n pc.yellow('⚠ urur whoami は未実装です。Kiro specで実装を進めてください。'),\n )\n}\n","import { createProgram } from './program.js'\n\nconst program = createProgram()\nprogram.parse()\n"],"mappings":"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","names":["pc","pc","confirm","input","ora","pc","input","pc","confirm","ora","pc","program"]}
|
|
1
|
+
{"version":3,"sources":["../src/program.ts","../src/commands/login.ts","../src/lib/auth.ts","../src/lib/config.ts","../src/lib/edgeFunctionApi.ts","../src/lib/deviceFlow.ts","../src/lib/supabase.ts","../src/lib/magicLink.ts","../src/commands/logout.ts","../src/commands/submit.ts","../src/lib/validation.ts","../src/commands/whoami.ts","../src/index.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { login } from './commands/login.js'\nimport { logout } from './commands/logout.js'\nimport { submit } from './commands/submit.js'\nimport { whoami } from './commands/whoami.js'\n\nexport function createProgram(): Command {\n const program = new Command()\n\n program\n .name('urur')\n .description('CLI for urur.dev - 個人開発プロダクトディレクトリ')\n .version('0.1.0')\n\n program\n .command('login')\n .description('GitHubまたはメールアドレスでログイン')\n .addHelpText(\n 'after',\n `\n使用例:\n $ urur login\n\nGitHub Device Flow またはメールアドレス(Magic Link)で認証します。`,\n )\n .action(login)\n\n program\n .command('logout')\n .description('ログアウト(認証情報を削除)')\n .addHelpText(\n 'after',\n `\n~/.urur/credentials.json を削除します。`,\n )\n .action(logout)\n\n program\n .command('submit')\n .description('プロダクトを投稿')\n .option('--name <name>', 'プロダクト名(必須)')\n .option('--url <url>', 'プロダクトURL(必須)')\n .option('--tagline <text>', 'タグライン(200文字以内)')\n .option('--description <text>', '説明(2000文字以内)')\n .option('--logo-url <url>', 'ロゴURL')\n .option('-i, --interactive', '対話モードで入力')\n .option('-y, --yes', '確認をスキップして投稿')\n .addHelpText(\n 'after',\n `\n使用例:\n $ urur submit -i\n $ urur submit --name \"My App\" --url \"https://example.com\"\n $ urur submit --name \"My App\" --url \"https://example.com\" --tagline \"便利なツール\"\n\nオプション未指定の場合は対話モードで入力します。`,\n )\n .action(submit)\n\n program\n .command('whoami')\n .description('ログイン中のユーザー情報を表示')\n .addHelpText(\n 'after',\n `\nGitHubユーザー名とメールアドレスを表示します。\n未ログインの場合は \"urur login\" を実行してください。`,\n )\n .action(whoami)\n\n return program\n}\n","import { confirm, input, select } from '@inquirer/prompts'\nimport open from 'open'\nimport ora from 'ora'\nimport pc from 'picocolors'\nimport {\n clearCredentials,\n loadCredentials,\n saveCredentials,\n} from '../lib/auth.js'\nimport { pollForSession, requestCode } from '../lib/deviceFlow.js'\nimport { sendOtp, verifyOtp } from '../lib/magicLink.js'\nimport { getSupabaseClient } from '../lib/supabase.js'\n\nconst MAX_OTP_ATTEMPTS = 3\n\nexport async function login(): Promise<void> {\n const supabase = getSupabaseClient()\n\n // Check existing credentials\n const existing = await loadCredentials()\n if (existing) {\n await supabase.auth.setSession({\n access_token: existing.access_token,\n refresh_token: existing.refresh_token,\n })\n const { data } = await supabase.auth.getUser()\n const username = data?.user?.user_metadata?.user_name ?? '不明'\n\n console.log(pc.green(`既にログイン済みです: ${username}`))\n\n const shouldRelogin = await confirm({\n message: '再ログインしますか?',\n default: false,\n })\n\n if (!shouldRelogin) {\n return\n }\n\n await clearCredentials()\n }\n\n // Select login method\n const method = await select({\n message: 'ログイン方法を選択してください',\n choices: [\n { name: 'GitHub', value: 'github' },\n { name: 'メールアドレス', value: 'email' },\n ],\n })\n\n if (method === 'github') {\n await loginWithGitHub()\n } else {\n await loginWithEmail()\n }\n}\n\nasync function loginWithGitHub(): Promise<void> {\n try {\n const deviceCode = await requestCode()\n\n console.log()\n console.log(pc.bold(`認証コード: ${pc.cyan(deviceCode.userCode)}`))\n console.log(\n `以下の URL でコードを入力してください: ${deviceCode.verificationUri}`,\n )\n console.log()\n\n await open(deviceCode.verificationUri)\n\n const spinner = ora('GitHub で認証を待っています...').start()\n\n try {\n const session = await pollForSession(\n deviceCode.deviceCode,\n deviceCode.interval,\n deviceCode.expiresIn,\n )\n\n spinner.succeed('認証完了')\n\n await saveCredentials({\n access_token: session.accessToken,\n refresh_token: session.refreshToken,\n expires_at: session.expiresAt,\n })\n\n console.log(pc.green(`ログイン成功: ${session.userName}`))\n printPostLoginGuide()\n } catch (err) {\n spinner.fail('認証に失敗しました')\n throw err\n }\n } catch (err) {\n console.log(\n pc.red(`エラー: ${err instanceof Error ? err.message : String(err)}`),\n )\n process.exitCode = 1\n }\n}\n\nasync function loginWithEmail(): Promise<void> {\n try {\n const email = await input({\n message: 'メールアドレスを入力してください',\n validate: (value) => {\n if (!value.includes('@')) {\n return '有効なメールアドレスを入力してください'\n }\n return true\n },\n })\n\n await sendOtp(email)\n console.log(pc.green(`${email} に認証コードを送信しました`))\n\n const session = await attemptOtpVerification(email)\n\n if (session) {\n await saveCredentials({\n access_token: session.accessToken,\n refresh_token: session.refreshToken,\n expires_at: session.expiresAt,\n })\n\n console.log(pc.green(`ログイン成功: ${session.email}`))\n printPostLoginGuide()\n }\n } catch (err) {\n console.log(\n pc.red(`エラー: ${err instanceof Error ? err.message : String(err)}`),\n )\n process.exitCode = 1\n }\n}\n\nasync function attemptOtpVerification(email: string): Promise<{\n accessToken: string\n refreshToken: string\n expiresAt: number\n email: string\n} | null> {\n for (let attempt = 0; attempt < MAX_OTP_ATTEMPTS; attempt++) {\n const token = await input({\n message: '認証コードを入力してください',\n })\n\n try {\n return await verifyOtp(email, token)\n } catch {\n const remaining = MAX_OTP_ATTEMPTS - attempt - 1\n if (remaining > 0) {\n console.log(\n pc.yellow(`認証コードが無効です。残り ${remaining} 回試行できます。`),\n )\n }\n }\n }\n\n // All attempts failed, offer resend\n const shouldResend = await confirm({\n message: '認証コードを再送信しますか?',\n default: true,\n })\n\n if (shouldResend) {\n await sendOtp(email)\n console.log(pc.green(`${email} に認証コードを再送信しました`))\n\n const token = await input({\n message: '認証コードを入力してください',\n })\n\n return await verifyOtp(email, token)\n }\n\n return null\n}\n\nfunction printPostLoginGuide(): void {\n console.log()\n console.log(\n `次のステップ: ${pc.cyan('urur submit')} でプロダクトを投稿できます`,\n )\n}\n","import fs from 'node:fs/promises'\nimport { CONFIG_DIR, CREDENTIALS_PATH } from './config.js'\n\nexport interface Credentials {\n access_token: string\n refresh_token: string\n expires_at: number\n}\n\nexport async function loadCredentials(): Promise<Credentials | null> {\n try {\n const data = await fs.readFile(CREDENTIALS_PATH, 'utf-8')\n return JSON.parse(data) as Credentials\n } catch {\n return null\n }\n}\n\nexport async function saveCredentials(credentials: Credentials): Promise<void> {\n await fs.mkdir(CONFIG_DIR, { recursive: true })\n await fs.writeFile(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2))\n}\n\nexport async function clearCredentials(): Promise<void> {\n try {\n await fs.unlink(CREDENTIALS_PATH)\n } catch {\n // Ignore if file doesn't exist\n }\n}\n","import os from 'node:os'\nimport path from 'node:path'\n\nexport const CONFIG_DIR = path.join(os.homedir(), '.urur')\nexport const CREDENTIALS_PATH = path.join(CONFIG_DIR, 'credentials.json')\n\n// tsup の define でビルド時に埋め込まれる\ndeclare const __SUPABASE_URL__: string\ndeclare const __SUPABASE_ANON_KEY__: string\ndeclare const __WEB_URL__: string\n\nexport const SUPABASE_URL: string = __SUPABASE_URL__\nexport const SUPABASE_ANON_KEY: string = __SUPABASE_ANON_KEY__\nexport const WEB_URL: string = __WEB_URL__\n","import { SUPABASE_ANON_KEY, SUPABASE_URL } from './config.js'\n\nexport interface DeviceCodeResponse {\n userCode: string\n verificationUri: string\n expiresIn: number\n interval: number\n deviceCode: string\n}\n\nexport interface DeviceFlowSession {\n accessToken: string\n refreshToken: string\n expiresAt: number\n userName: string\n}\n\nexport type PollTokenResponse =\n | { status: 'pending' }\n | { status: 'slow_down' }\n | { status: 'expired' }\n | { status: 'success'; session: DeviceFlowSession }\n | { status: 'error'; message: string }\n\nconst EDGE_FUNCTION_URL = `${SUPABASE_URL}/functions/v1/cli-auth`\n\nfunction buildHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${SUPABASE_ANON_KEY}`,\n }\n}\n\nexport async function requestDeviceCode(): Promise<DeviceCodeResponse> {\n const res = await fetch(EDGE_FUNCTION_URL, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({ action: 'device-code' }),\n })\n\n if (!res.ok) {\n throw new Error(`Edge Function エラー: ${res.status}`)\n }\n\n return res.json()\n}\n\nexport async function pollToken(\n deviceCode: string,\n): Promise<PollTokenResponse> {\n const res = await fetch(EDGE_FUNCTION_URL, {\n method: 'POST',\n headers: buildHeaders(),\n body: JSON.stringify({ action: 'poll-token', deviceCode }),\n })\n\n if (!res.ok) {\n throw new Error(`Edge Function エラー: ${res.status}`)\n }\n\n return res.json()\n}\n","import {\n type DeviceCodeResponse,\n type DeviceFlowSession,\n pollToken,\n requestDeviceCode,\n} from './edgeFunctionApi.js'\n\nexport type { DeviceCodeResponse, DeviceFlowSession }\n\nexport async function requestCode(): Promise<DeviceCodeResponse> {\n return requestDeviceCode()\n}\n\nexport async function pollForSession(\n deviceCode: string,\n interval: number,\n expiresIn: number,\n): Promise<DeviceFlowSession> {\n let currentInterval = interval\n let elapsed = 0\n\n while (elapsed < expiresIn) {\n await new Promise((resolve) => setTimeout(resolve, currentInterval * 1000))\n elapsed += currentInterval\n\n if (elapsed >= expiresIn) {\n break\n }\n\n const response = await pollToken(deviceCode)\n\n switch (response.status) {\n case 'success':\n return response.session\n case 'pending':\n continue\n case 'slow_down':\n currentInterval += 5\n continue\n case 'expired':\n throw new Error(\n '認証の有効期限が切れました。再度 urur login を実行してください。',\n )\n case 'error':\n throw new Error(response.message)\n }\n }\n\n throw new Error(\n '認証がタイムアウトしました。再度 urur login を実行してください。',\n )\n}\n","import { createClient } from '@supabase/supabase-js'\nimport { SUPABASE_ANON_KEY, SUPABASE_URL } from './config.js'\n\nfunction createMemoryStorage() {\n const store = new Map<string, string>()\n return {\n getItem(key: string): string | null {\n return store.get(key) ?? null\n },\n setItem(key: string, value: string): void {\n store.set(key, value)\n },\n removeItem(key: string): void {\n store.delete(key)\n },\n }\n}\n\nexport function getSupabaseClient() {\n return createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {\n auth: {\n storage: createMemoryStorage(),\n autoRefreshToken: false,\n detectSessionInUrl: false,\n },\n })\n}\n","import { getSupabaseClient } from './supabase.js'\n\nexport interface MagicLinkSession {\n accessToken: string\n refreshToken: string\n expiresAt: number\n email: string\n}\n\nexport async function sendOtp(email: string): Promise<void> {\n const supabase = getSupabaseClient()\n const { error } = await supabase.auth.signInWithOtp({ email })\n\n if (error) {\n throw new Error(error.message)\n }\n}\n\nexport async function verifyOtp(\n email: string,\n token: string,\n): Promise<MagicLinkSession> {\n const supabase = getSupabaseClient()\n const { data, error } = await supabase.auth.verifyOtp({\n email,\n token,\n type: 'email',\n })\n\n if (error) {\n throw new Error(error.message)\n }\n\n if (!data.session) {\n throw new Error('セッションが取得できませんでした')\n }\n\n return {\n accessToken: data.session.access_token,\n refreshToken: data.session.refresh_token,\n expiresAt: data.session.expires_at ?? 0,\n email: data.session.user?.email ?? email,\n }\n}\n","import pc from 'picocolors'\nimport { clearCredentials, loadCredentials } from '../lib/auth.js'\n\nexport async function logout(): Promise<void> {\n const credentials = await loadCredentials()\n if (!credentials) {\n console.log(pc.yellow('ログインしていません。'))\n return\n }\n\n await clearCredentials()\n console.log(pc.green('ログアウトしました。'))\n}\n","import { confirm, input } from '@inquirer/prompts'\nimport ora from 'ora'\nimport pc from 'picocolors'\nimport { loadCredentials } from '../lib/auth.js'\nimport { getSupabaseClient } from '../lib/supabase.js'\nimport type { ServiceFormData } from '../lib/validation.js'\nimport {\n validateAll,\n validateDescription,\n validateLogoUrl,\n validateName,\n validateTagline,\n validateUrl,\n} from '../lib/validation.js'\n\ninterface SubmitOptions {\n name?: string\n url?: string\n tagline?: string\n description?: string\n logoUrl?: string\n interactive?: boolean\n yes?: boolean\n}\n\nfunction shouldUseInteractive(options: SubmitOptions): boolean {\n return options.interactive === true || !options.name || !options.url\n}\n\nasync function promptServiceData(): Promise<ServiceFormData> {\n const name = await input({\n message: 'プロダクト名(必須):',\n validate: (value) => validateName(value) ?? true,\n })\n\n const url = await input({\n message: 'プロダクトURL(必須):',\n validate: (value) => validateUrl(value) ?? true,\n })\n\n const tagline = await input({\n message: 'タグライン(省略可):',\n validate: (value) => {\n if (!value) return true\n return validateTagline(value) ?? true\n },\n })\n\n const description = await input({\n message: '説明(省略可):',\n validate: (value) => {\n if (!value) return true\n return validateDescription(value) ?? true\n },\n })\n\n const logo_url = await input({\n message: 'ロゴURL(省略可):',\n validate: (value) => {\n if (!value) return true\n return validateLogoUrl(value) ?? true\n },\n })\n\n return {\n name,\n url,\n tagline: tagline || undefined,\n description: description || undefined,\n logo_url: logo_url || undefined,\n }\n}\n\nfunction displaySummary(data: ServiceFormData): void {\n console.log(pc.bold('\\n投稿内容:'))\n console.log(` プロダクト名: ${data.name}`)\n console.log(` URL: ${data.url}`)\n if (data.tagline) console.log(` タグライン: ${data.tagline}`)\n if (data.description) console.log(` 説明: ${data.description}`)\n if (data.logo_url) console.log(` ロゴURL: ${data.logo_url}`)\n console.log('')\n}\n\nfunction displayErrors(errors: Record<string, string>): void {\n console.log(pc.red('\\nバリデーションエラー:'))\n for (const [, message] of Object.entries(errors)) {\n console.log(pc.red(` - ${message}`))\n }\n}\n\nexport async function submit(options: SubmitOptions): Promise<void> {\n // 1. Authentication check\n const credentials = await loadCredentials()\n if (!credentials) {\n console.log(pc.red('ログインが必要です。`urur login` を実行してください。'))\n process.exitCode = 1\n return\n }\n\n const supabase = getSupabaseClient()\n await supabase.auth.setSession({\n access_token: credentials.access_token,\n refresh_token: credentials.refresh_token,\n })\n\n const { data: userData, error: userError } = await supabase.auth.getUser()\n if (userError || !userData.user) {\n console.log(\n pc.red(\n `認証エラー: ${userError?.message ?? 'ユーザー情報を取得できませんでした'}。\\`urur login\\` で再ログインしてください。`,\n ),\n )\n process.exitCode = 1\n return\n }\n\n // 2. Data collection\n let formData: ServiceFormData\n\n if (shouldUseInteractive(options)) {\n formData = await promptServiceData()\n } else {\n // Build form data from CLI options\n formData = {\n name: options.name as string,\n url: options.url as string,\n tagline: options.tagline,\n description: options.description,\n logo_url: options.logoUrl,\n }\n\n // 3. Validation (CLI mode only)\n const result = validateAll(formData)\n if (!result.valid) {\n displayErrors(result.errors)\n process.exitCode = 1\n return\n }\n }\n\n // 4. Summary and confirmation\n displaySummary(formData)\n\n if (!options.yes) {\n const confirmed = await confirm({\n message: '投稿しますか?',\n default: true,\n })\n\n if (!confirmed) {\n console.log(pc.yellow('投稿をキャンセルしました。'))\n return\n }\n }\n\n // 5. Submit to Supabase\n const spinner = ora('投稿中...').start()\n\n const { error: insertError } = await supabase\n .from('services')\n .insert({\n user_id: userData.user.id,\n name: formData.name,\n url: formData.url,\n tagline: formData.tagline ?? null,\n description: formData.description ?? null,\n logo_url: formData.logo_url ?? null,\n source: 'cli' as const,\n })\n .select()\n .single()\n\n if (insertError) {\n spinner.fail()\n console.log(pc.red(`投稿エラー: ${insertError.message}`))\n process.exitCode = 1\n return\n }\n\n spinner.succeed()\n console.log(pc.green(`「${formData.name}」を投稿しました!`))\n}\n","/**\n * Service submission form validation\n * Shared between client and server\n */\n\nexport interface ServiceFormData {\n name: string\n url: string\n category_id?: string\n tagline?: string\n description?: string\n logo_url?: string\n}\n\nexport interface ValidationResult {\n valid: boolean\n errors: Record<string, string>\n}\n\n// Error messages in Japanese\nconst ERROR_MESSAGES = {\n name: {\n required: 'プロダクト名は必須です',\n maxLength: 'プロダクト名は100文字以内で入力してください',\n },\n url: {\n required: 'URLは必須です',\n invalid: '有効なURLを入力してください',\n },\n tagline: {\n maxLength: 'タグラインは200文字以内で入力してください',\n },\n description: {\n maxLength: '説明は2000文字以内で入力してください',\n },\n logo_url: {\n invalid: '有効なURLを入力してください',\n },\n} as const\n\n// Validation rules\nconst VALIDATION_RULES = {\n name: { required: true, maxLength: 100 },\n url: { required: true },\n tagline: { maxLength: 200 },\n description: { maxLength: 2000 },\n} as const\n\nfunction isValidUrl(value: string): boolean {\n try {\n new URL(value)\n return true\n } catch {\n return false\n }\n}\n\nexport function validateName(value: string): string | null {\n const trimmed = value.trim()\n if (!trimmed) {\n return ERROR_MESSAGES.name.required\n }\n if (trimmed.length > VALIDATION_RULES.name.maxLength) {\n return ERROR_MESSAGES.name.maxLength\n }\n return null\n}\n\nexport function validateUrl(value: string): string | null {\n const trimmed = value.trim()\n if (!trimmed) {\n return ERROR_MESSAGES.url.required\n }\n if (!isValidUrl(trimmed)) {\n return ERROR_MESSAGES.url.invalid\n }\n return null\n}\n\nexport function validateTagline(value: string | undefined): string | null {\n if (!value) {\n return null\n }\n if (value.length > VALIDATION_RULES.tagline.maxLength) {\n return ERROR_MESSAGES.tagline.maxLength\n }\n return null\n}\n\nexport function validateDescription(value: string | undefined): string | null {\n if (!value) {\n return null\n }\n if (value.length > VALIDATION_RULES.description.maxLength) {\n return ERROR_MESSAGES.description.maxLength\n }\n return null\n}\n\nexport function validateLogoUrl(value: string | undefined): string | null {\n if (!value) {\n return null\n }\n if (!isValidUrl(value)) {\n return ERROR_MESSAGES.logo_url.invalid\n }\n return null\n}\n\nexport function validateAll(data: Partial<ServiceFormData>): ValidationResult {\n const errors: Record<string, string> = {}\n\n const nameError = validateName(data.name ?? '')\n if (nameError) {\n errors.name = nameError\n }\n\n const urlError = validateUrl(data.url ?? '')\n if (urlError) {\n errors.url = urlError\n }\n\n const taglineError = validateTagline(data.tagline)\n if (taglineError) {\n errors.tagline = taglineError\n }\n\n const descriptionError = validateDescription(data.description)\n if (descriptionError) {\n errors.description = descriptionError\n }\n\n const logoUrlError = validateLogoUrl(data.logo_url)\n if (logoUrlError) {\n errors.logo_url = logoUrlError\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors,\n }\n}\n","import pc from 'picocolors'\n\nexport async function whoami(): Promise<void> {\n console.log(\n pc.yellow('⚠ urur whoami は未実装です。Kiro specで実装を進めてください。'),\n )\n}\n","import { createProgram } from './program.js'\n\nconst program = createProgram()\nprogram.parse()\n"],"mappings":"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","names":["pc","pc","confirm","input","ora","pc","input","pc","confirm","ora","pc","program"]}
|