@kidsinai/kids-client 0.0.7 → 0.0.8

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@kidsinai/kids-client",
4
- "version": "0.0.7",
4
+ "version": "0.0.8",
5
5
  "type": "module",
6
6
  "description": "Own-client TUI for Kids OpenCode — talks to local `opencode serve` via @opencode-ai/sdk v2 with kid-warm rendering, mission progress, permission dialog, and stderr-tail audit pipeline.",
7
7
  "license": "MIT",
package/src/core/env.ts CHANGED
@@ -62,15 +62,18 @@ export function validateEnv(env: KidsClientEnv): { ok: true } | { ok: false; rea
62
62
  // setup wizard writes whatever the parent picked into ~/.config/kids-opencode/env
63
63
  // which the wrapper sources before exec.
64
64
  //
65
- // KIDS_OAUTH_PROVIDER was previously trusted as a credential signal —
66
- // that was wrong: upstream opencode dropped Anthropic Pro/Max OAuth in
67
- // 1.3.0, so the marker existed without a real credential and opencode
68
- // silently fell back to its own api-key picker. Users now get re-routed
69
- // through SetupScreen until they paste an actual API key.
65
+ // KIDS_OAUTH_PROVIDER signals that the wrapper finished
66
+ // `opencode auth login` and opencode now holds an OAuth token in its
67
+ // own auth.json store. Only trusted when paired with a provider opencode
68
+ // actually supports OAuth for Anthropic Pro/Max stale markers from
69
+ // 0.0.7/0.0.8 are filtered out in hasAnyProviderKey via the
70
+ // OAUTH_PROVIDERS check, but here at the env level we accept the bare
71
+ // marker since validity will be re-checked by opencode at serve time.
70
72
  const hasAnyKey =
71
73
  env.deeprouterApiKey
72
74
  || process.env.ANTHROPIC_API_KEY
73
75
  || process.env.OPENAI_API_KEY
76
+ || process.env.KIDS_OAUTH_PROVIDER
74
77
  if (!env.bypassGateway && !hasAnyKey) {
75
78
  return {
76
79
  ok: false,
package/src/core/setup.ts CHANGED
@@ -19,28 +19,40 @@ export type ProviderId = "anthropic" | "openai" | "deeprouter"
19
19
  /**
20
20
  * Wire protocol between SetupScreen and bin/kids-opencode wrapper:
21
21
  * kids-client exits with this code to ask the wrapper to run
22
- * `opencode auth login --provider <p>` (interactive OAuth that needs the
23
- * TTY), then re-exec kids-client. See bin/kids-opencode §11 loop.
22
+ * `opencode auth login --provider <p> --method <m>` (interactive OAuth
23
+ * that needs the TTY), then re-exec kids-client. See bin/kids-opencode loop.
24
24
  */
25
25
  export const OAUTH_HANDOFF_EXIT_CODE = 123
26
26
 
27
27
  /**
28
- * Providers for which upstream opencode currently ships an OAuth/subscription
29
- * login method. Anthropic Pro/Max was REMOVED in opencode 1.3.0 — Anthropic's
30
- * ToS prohibits using consumer Claude Pro/Max subscriptions through coding
31
- * agents like opencode. See:
32
- * ~/Documents/sites/kidsinai/opencode-kernel/packages/web/src/content/docs/providers.mdx
28
+ * Providers for which upstream opencode currently ships an OAuth /
29
+ * subscription login method.
33
30
  *
34
- * OpenAI ChatGPT Plus IS supported by upstream and is the obvious next
35
- * candidate to wire here — but until that's done, no provider triggers the
36
- * auth_choice step and every kid lands directly on the api-key screen.
31
+ * - openai: ChatGPT Pro/Plus via OAuth. Verified in
32
+ * ~/Documents/sites/kidsinai/opencode-kernel/packages/opencode/src/plugin/codex.ts
33
+ * (methods: "ChatGPT Pro/Plus (browser)" + "(headless)"). Allowed models
34
+ * include gpt-5.4-mini / gpt-5.3-codex / gpt-5.5.
37
35
  *
38
- * The OAuth handoff infrastructure below (OAUTH_HANDOFF_EXIT_CODE,
39
- * saveSetupOauth, the wrapper's exit-123 loop, the auth_choice screen) is
40
- * intentionally kept inert rather than deleted — flipping OPENAI back on
41
- * here when ChatGPT Plus is wired is a one-line revival.
36
+ * - anthropic: NOT supported. Removed in opencode 1.3.0 because Anthropic's
37
+ * ToS prohibits Pro/Max in coding agents. Do not re-add.
38
+ *
39
+ * - deeprouter: own gateway, uses api-key.
40
+ */
41
+ export const OAUTH_PROVIDERS: ReadonlyArray<ProviderId> = ["openai"] as const
42
+
43
+ /** Per-provider OAuth method label expected by `opencode auth login --method`. */
44
+ export const OAUTH_METHOD_LABELS: Partial<Record<ProviderId, string>> = {
45
+ openai: "ChatGPT Pro/Plus (browser)",
46
+ }
47
+
48
+ /**
49
+ * Default model id to write into opencode.json when the user picks OAuth
50
+ * (the api-key default is in PROVIDERS[i].defaultModel below — different
51
+ * because the codex plugin restricts the model list when OAuth is active).
42
52
  */
43
- export const OAUTH_PROVIDERS: ReadonlyArray<ProviderId> = [] as const
53
+ const OAUTH_DEFAULT_MODELS: Partial<Record<ProviderId, string>> = {
54
+ openai: "openai/gpt-5.4-mini",
55
+ }
44
56
 
45
57
  export interface ProviderChoice {
46
58
  id: ProviderId
@@ -68,8 +80,8 @@ export const PROVIDERS: ProviderChoice[] = [
68
80
  },
69
81
  {
70
82
  id: "openai",
71
- label: "OpenAI GPT-4",
72
- hint: "Also works. ~$5-10/month for typical kid use.",
83
+ label: "OpenAI GPT (ChatGPT Plus/Pro 可直接登录)",
84
+ hint: "Already pay for ChatGPT Plus/Pro? Sign in with that — no API key. Otherwise pay-as-you-go ~$5-10/month.",
73
85
  envVar: "OPENAI_API_KEY",
74
86
  apiKeyUrl: "https://platform.openai.com/api-keys",
75
87
  config: (env) => ({
@@ -136,38 +148,49 @@ export interface SaveOauthOptions {
136
148
  /**
137
149
  * Stage the OAuth handoff. We write opencode.json without an apiKey block
138
150
  * (opencode reads OAuth tokens from its own auth.json), drop any stale
139
- * provider API keys, and write a KIDS_OAUTH_PROVIDER marker so the
140
- * wrapper knows which provider to log into. The actual
141
- * `opencode auth login --provider <p>` invocation happens in
142
- * bin/kids-opencode after kids-client exits with OAUTH_HANDOFF_EXIT_CODE.
151
+ * provider API keys, and write KIDS_OAUTH_PROVIDER + KIDS_OAUTH_METHOD
152
+ * markers so the wrapper knows which provider+method to log into. The
153
+ * actual `opencode auth login --provider <p> --method <m>` invocation
154
+ * happens in bin/kids-opencode after kids-client exits with
155
+ * OAUTH_HANDOFF_EXIT_CODE.
143
156
  */
144
157
  export function saveSetupOauth(opts: SaveOauthOptions): void {
145
158
  const provider = findProvider(opts.provider)
159
+ const methodLabel = OAUTH_METHOD_LABELS[opts.provider]
160
+ if (!methodLabel) {
161
+ throw new Error(`No OAuth method label registered for provider '${opts.provider}'.`)
162
+ }
146
163
  ensureConfigDir(opts.configDir)
147
164
 
148
165
  // 1. Update env file: clear this provider's API key (avoid stale leakage),
149
- // set marker pointing at the OAuth provider.
166
+ // set markers pointing at the OAuth provider + method.
150
167
  const envPath = join(opts.configDir, "env")
151
168
  const existing = readEnvFile(envPath)
152
169
  delete existing[provider.envVar]
153
170
  existing.KIDS_OAUTH_PROVIDER = opts.provider
171
+ existing.KIDS_OAUTH_METHOD = methodLabel
154
172
  writeEnvFile(envPath, existing)
155
173
 
156
174
  // 2. opencode.json without apiKey — opencode uses its auth.json store.
157
- writeOpencodeConfig(opts.configDir, provider, { withApiKey: false })
175
+ // Use the OAuth-specific default model since the codex plugin restricts
176
+ // the model list when OAuth is the active credential.
177
+ writeOpencodeConfig(opts.configDir, provider, {
178
+ withApiKey: false,
179
+ modelOverride: OAUTH_DEFAULT_MODELS[opts.provider],
180
+ })
158
181
  }
159
182
 
160
183
  function writeOpencodeConfig(
161
184
  configDir: string,
162
185
  provider: ProviderChoice,
163
- flags: { withApiKey: boolean },
186
+ flags: { withApiKey: boolean; modelOverride?: string },
164
187
  ): void {
165
188
  const configPath = join(configDir, "opencode.json")
166
189
  const config = readJsonOrEmpty(configPath)
167
190
  config.provider = flags.withApiKey
168
191
  ? provider.config(provider.envVar)
169
192
  : { [provider.id]: {} }
170
- config.model = provider.defaultModel
193
+ config.model = flags.modelOverride ?? provider.defaultModel
171
194
  if (!config.permission) {
172
195
  config.permission = {
173
196
  default: "ask",
@@ -188,16 +211,18 @@ function writeOpencodeConfig(
188
211
  export function hasAnyProviderKey(configDir: string): boolean {
189
212
  const env = readEnvFile(join(configDir, "env"))
190
213
  if (env.ANTHROPIC_API_KEY || env.OPENAI_API_KEY || env.DEEPROUTER_API_KEY) return true
214
+ // KIDS_OAUTH_PROVIDER means setup wizard staged an OAuth handoff and the
215
+ // wrapper successfully completed `opencode auth login`. opencode keeps the
216
+ // real token in its own auth.json — we trust it to gate on validity at
217
+ // serve time. (Only meaningful for providers in OAUTH_PROVIDERS; stale
218
+ // markers from removed providers are cleaned up at next setup.)
219
+ if (env.KIDS_OAUTH_PROVIDER && OAUTH_PROVIDERS.includes(env.KIDS_OAUTH_PROVIDER as ProviderId)) return true
191
220
  // Also accept keys present in the parent shell env (advanced users).
192
221
  return !!(
193
222
  process.env.ANTHROPIC_API_KEY
194
223
  || process.env.OPENAI_API_KEY
195
224
  || process.env.DEEPROUTER_API_KEY
196
225
  )
197
- // Note: KIDS_OAUTH_PROVIDER marker is intentionally NOT accepted anymore.
198
- // Users who got stuck with the marker but no actual OAuth credential
199
- // (because opencode never had an Anthropic Pro/Max method to begin with)
200
- // will be routed back through SetupScreen to pick an API-key flow.
201
226
  }
202
227
 
203
228
  /** Crude check of API key shape — refuses obvious typos. */
@@ -427,8 +427,8 @@ const STRINGS = {
427
427
  keys: "[↑↓] 选 · [Enter] 确认 · [Esc] 返回",
428
428
  options: [
429
429
  {
430
- label: "用我的 Claude Pro/Max 订阅(推荐)",
431
- hint: "不用 API key、不用充值;用现有 claude.ai 账号一键登录",
430
+ label: "用我的 ChatGPT Plus / Pro 订阅(推荐)",
431
+ hint: "不用 API key、不用充值;用现有 chatgpt.com 账号一键登录",
432
432
  },
433
433
  {
434
434
  label: "用 API key(按量计费 ~$5/月)",
@@ -437,8 +437,8 @@ const STRINGS = {
437
437
  ],
438
438
  },
439
439
  oauthHandoff: {
440
- title: "正在让 Claude 登录接管屏幕…",
441
- line1: "马上会跳出浏览器让你登录 claude.ai 账号。",
440
+ title: "正在让 ChatGPT 登录接管屏幕…",
441
+ line1: "马上会跳出浏览器让你登录 chatgpt.com 账号。",
442
442
  line2: "登录完后我会自动接回来,给孩子继续。",
443
443
  },
444
444
  apiKeyTitle: (label: string) => `输入 ${label} 的 API key`,
@@ -496,8 +496,8 @@ const STRINGS = {
496
496
  keys: "[↑↓] choose · [Enter] confirm · [Esc] back",
497
497
  options: [
498
498
  {
499
- label: "Use my Claude Pro/Max subscription (recommended)",
500
- hint: "No API key, no top-up — sign in with your existing claude.ai account",
499
+ label: "Use my ChatGPT Plus / Pro subscription (recommended)",
500
+ hint: "No API key, no top-up — sign in with your existing chatgpt.com account",
501
501
  },
502
502
  {
503
503
  label: "Use an API key (pay-as-you-go ~$5/month)",
@@ -506,8 +506,8 @@ const STRINGS = {
506
506
  ],
507
507
  },
508
508
  oauthHandoff: {
509
- title: "Handing off to Claude login…",
510
- line1: "A browser window will open to sign in to your claude.ai account.",
509
+ title: "Handing off to ChatGPT login…",
510
+ line1: "A browser window will open to sign in to your chatgpt.com account.",
511
511
  line2: "I'll pick back up automatically once you're done.",
512
512
  },
513
513
  apiKeyTitle: (label: string) => `Enter your ${label} API key`,