@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 +1 -1
- package/src/core/env.ts +8 -5
- package/src/core/setup.ts +54 -29
- package/src/render/ink/screens/SetupScreen.tsx +8 -8
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.
|
|
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
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
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
|
|
23
|
-
* TTY), then re-exec kids-client. See bin/kids-opencode
|
|
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/
|
|
29
|
-
* login method.
|
|
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
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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
|
-
|
|
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
|
|
72
|
-
hint: "
|
|
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
|
|
140
|
-
* wrapper knows which provider to log into. The
|
|
141
|
-
* `opencode auth login --provider <p>` invocation
|
|
142
|
-
* bin/kids-opencode after kids-client exits with
|
|
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
|
|
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
|
-
|
|
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: "用我的
|
|
431
|
-
hint: "不用 API key、不用充值;用现有
|
|
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: "正在让
|
|
441
|
-
line1: "马上会跳出浏览器让你登录
|
|
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
|
|
500
|
-
hint: "No API key, no top-up — sign in with your existing
|
|
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
|
|
510
|
-
line1: "A browser window will open to sign in to your
|
|
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`,
|