@kidsinai/kids-client 0.0.6 → 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 +9 -3
- package/src/core/setup.ts +58 -18
- 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
|
@@ -60,9 +60,15 @@ export function validateEnv(env: KidsClientEnv): { ok: true } | { ok: false; rea
|
|
|
60
60
|
}
|
|
61
61
|
// Accept any supported provider's API key, not just DeepRouter. The
|
|
62
62
|
// setup wizard writes whatever the parent picked into ~/.config/kids-opencode/env
|
|
63
|
-
// which the wrapper sources before exec.
|
|
64
|
-
//
|
|
65
|
-
//
|
|
63
|
+
// which the wrapper sources before exec.
|
|
64
|
+
//
|
|
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.
|
|
66
72
|
const hasAnyKey =
|
|
67
73
|
env.deeprouterApiKey
|
|
68
74
|
|| process.env.ANTHROPIC_API_KEY
|
package/src/core/setup.ts
CHANGED
|
@@ -19,13 +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
|
-
/**
|
|
28
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Providers for which upstream opencode currently ships an OAuth /
|
|
29
|
+
* subscription login method.
|
|
30
|
+
*
|
|
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.
|
|
35
|
+
*
|
|
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).
|
|
52
|
+
*/
|
|
53
|
+
const OAUTH_DEFAULT_MODELS: Partial<Record<ProviderId, string>> = {
|
|
54
|
+
openai: "openai/gpt-5.4-mini",
|
|
55
|
+
}
|
|
29
56
|
|
|
30
57
|
export interface ProviderChoice {
|
|
31
58
|
id: ProviderId
|
|
@@ -53,8 +80,8 @@ export const PROVIDERS: ProviderChoice[] = [
|
|
|
53
80
|
},
|
|
54
81
|
{
|
|
55
82
|
id: "openai",
|
|
56
|
-
label: "OpenAI GPT
|
|
57
|
-
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.",
|
|
58
85
|
envVar: "OPENAI_API_KEY",
|
|
59
86
|
apiKeyUrl: "https://platform.openai.com/api-keys",
|
|
60
87
|
config: (env) => ({
|
|
@@ -121,38 +148,49 @@ export interface SaveOauthOptions {
|
|
|
121
148
|
/**
|
|
122
149
|
* Stage the OAuth handoff. We write opencode.json without an apiKey block
|
|
123
150
|
* (opencode reads OAuth tokens from its own auth.json), drop any stale
|
|
124
|
-
* provider API keys, and write
|
|
125
|
-
* wrapper knows which provider to log into. The
|
|
126
|
-
* `opencode auth login --provider <p>` invocation
|
|
127
|
-
* 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.
|
|
128
156
|
*/
|
|
129
157
|
export function saveSetupOauth(opts: SaveOauthOptions): void {
|
|
130
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
|
+
}
|
|
131
163
|
ensureConfigDir(opts.configDir)
|
|
132
164
|
|
|
133
165
|
// 1. Update env file: clear this provider's API key (avoid stale leakage),
|
|
134
|
-
// set
|
|
166
|
+
// set markers pointing at the OAuth provider + method.
|
|
135
167
|
const envPath = join(opts.configDir, "env")
|
|
136
168
|
const existing = readEnvFile(envPath)
|
|
137
169
|
delete existing[provider.envVar]
|
|
138
170
|
existing.KIDS_OAUTH_PROVIDER = opts.provider
|
|
171
|
+
existing.KIDS_OAUTH_METHOD = methodLabel
|
|
139
172
|
writeEnvFile(envPath, existing)
|
|
140
173
|
|
|
141
174
|
// 2. opencode.json without apiKey — opencode uses its auth.json store.
|
|
142
|
-
|
|
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
|
+
})
|
|
143
181
|
}
|
|
144
182
|
|
|
145
183
|
function writeOpencodeConfig(
|
|
146
184
|
configDir: string,
|
|
147
185
|
provider: ProviderChoice,
|
|
148
|
-
flags: { withApiKey: boolean },
|
|
186
|
+
flags: { withApiKey: boolean; modelOverride?: string },
|
|
149
187
|
): void {
|
|
150
188
|
const configPath = join(configDir, "opencode.json")
|
|
151
189
|
const config = readJsonOrEmpty(configPath)
|
|
152
190
|
config.provider = flags.withApiKey
|
|
153
191
|
? provider.config(provider.envVar)
|
|
154
192
|
: { [provider.id]: {} }
|
|
155
|
-
config.model = provider.defaultModel
|
|
193
|
+
config.model = flags.modelOverride ?? provider.defaultModel
|
|
156
194
|
if (!config.permission) {
|
|
157
195
|
config.permission = {
|
|
158
196
|
default: "ask",
|
|
@@ -173,15 +211,17 @@ function writeOpencodeConfig(
|
|
|
173
211
|
export function hasAnyProviderKey(configDir: string): boolean {
|
|
174
212
|
const env = readEnvFile(join(configDir, "env"))
|
|
175
213
|
if (env.ANTHROPIC_API_KEY || env.OPENAI_API_KEY || env.DEEPROUTER_API_KEY) return true
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
|
|
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
|
|
179
220
|
// Also accept keys present in the parent shell env (advanced users).
|
|
180
221
|
return !!(
|
|
181
222
|
process.env.ANTHROPIC_API_KEY
|
|
182
223
|
|| process.env.OPENAI_API_KEY
|
|
183
224
|
|| process.env.DEEPROUTER_API_KEY
|
|
184
|
-
|| process.env.KIDS_OAUTH_PROVIDER
|
|
185
225
|
)
|
|
186
226
|
}
|
|
187
227
|
|
|
@@ -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`,
|