@stemtrooper/learningcode 0.4.1 → 0.4.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.
- package/README.md +31 -1
- package/bin/learningcode.mjs +9 -5
- package/lib/token.mjs +19 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,6 +63,34 @@ npm install -g @stemtrooper/learningcode
|
|
|
63
63
|
learningcode --version
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
`node --version` must report **22.19.0 or newer**. If it is older — and
|
|
67
|
+
Ubuntu's own `apt` package is usually Node 18 — install a current one first:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
|
|
71
|
+
source ~/.bashrc
|
|
72
|
+
nvm install 24
|
|
73
|
+
nvm alias default 24
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**If the install fails with a permissions error**, npm's global directory belongs
|
|
77
|
+
to root. Move it somewhere you own, then install again:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
mkdir -p ~/.npm-global
|
|
81
|
+
npm config set prefix ~/.npm-global
|
|
82
|
+
export PATH="$HOME/.npm-global/bin:$PATH"
|
|
83
|
+
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
|
|
84
|
+
|
|
85
|
+
npm install -g @stemtrooper/learningcode
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The last line is what makes `learningcode` visible in future terminals; without
|
|
89
|
+
it you will think the install failed.
|
|
90
|
+
|
|
91
|
+
**Do not use `sudo npm install -g`.** It appears to work, then writes
|
|
92
|
+
root-owned files that break your next upgrade with a confusing error.
|
|
93
|
+
|
|
66
94
|
### Termux (Android)
|
|
67
95
|
|
|
68
96
|
No root and no `proot-distro` needed.
|
|
@@ -167,7 +195,9 @@ A footer shows your remaining quota whenever you are connected to Spark:
|
|
|
167
195
|
## Troubleshooting
|
|
168
196
|
|
|
169
197
|
**`Node 22.19.0 or newer is required`**
|
|
170
|
-
Upgrade Node, then reinstall: `npm i -g @stemtrooper/learningcode`.
|
|
198
|
+
Upgrade Node, then reinstall: `npm i -g @stemtrooper/learningcode`. On Linux this
|
|
199
|
+
is usually the first thing to check, because Ubuntu ships Node 18 — see
|
|
200
|
+
[macOS / Linux](#macos--linux).
|
|
171
201
|
|
|
172
202
|
**`No Spark API token configured`**
|
|
173
203
|
You do not have a token yet, or it is not cached. Run `learningcode --login` to
|
package/bin/learningcode.mjs
CHANGED
|
@@ -288,6 +288,9 @@ async function main() {
|
|
|
288
288
|
"it may not be a Spark token.\n",
|
|
289
289
|
);
|
|
290
290
|
}
|
|
291
|
+
// Keep Pi's stored credential and the environment in step. See the note
|
|
292
|
+
// beside saveProviderKey below.
|
|
293
|
+
await saveProviderKey(dir, PROVIDER_ID, token);
|
|
291
294
|
if (resolved.shouldCache) await writeCachedToken(dir, token);
|
|
292
295
|
} else if (explicitToken) {
|
|
293
296
|
process.stderr.write(
|
|
@@ -320,11 +323,12 @@ async function main() {
|
|
|
320
323
|
|
|
321
324
|
const childEnv = { ...process.env };
|
|
322
325
|
|
|
323
|
-
// Pi
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
326
|
+
// Pi prefers a stored credential in auth.json over the `$SPARK_API_KEY` the
|
|
327
|
+
// provider config names, so leaving the token only in the environment lets the
|
|
328
|
+
// two drift apart: Pi authenticates with whatever auth.json holds while the
|
|
329
|
+
// /quota and /seats commands read the environment, and a stale entry then shows
|
|
330
|
+
// up as a 401 on those two but not on completions. Writing the token to
|
|
331
|
+
// auth.json as well makes every path use the same value whichever Pi prefers.
|
|
328
332
|
const goKey = process.env.LEARNINGCODE_GO_KEY || process.env.OPENCODE_API_KEY;
|
|
329
333
|
if (goKey && process.env.LEARNINGCODE_ALLOW_ZEN !== "1") {
|
|
330
334
|
await saveProviderKey(dir, GO_PROVIDER, goKey);
|
package/lib/token.mjs
CHANGED
|
@@ -101,7 +101,10 @@ export async function resolveToken(agentDir, explicit) {
|
|
|
101
101
|
if (fromEnv) return { token: fromEnv.trim(), source: "environment" };
|
|
102
102
|
|
|
103
103
|
const cached = await readCachedToken(agentDir);
|
|
104
|
-
|
|
104
|
+
// A cached value that is not a Spark token is worse than no cache: it would be
|
|
105
|
+
// sent to Spark, rejected, and reported as a 401 that looks like a revoked
|
|
106
|
+
// token. Fall through and ask instead.
|
|
107
|
+
if (cached && isSparkToken(cached)) return { token: cached, source: "cached" };
|
|
105
108
|
|
|
106
109
|
const entered = await promptSecret(
|
|
107
110
|
"Paste your Spark API token (Bench > Issue / rotate token): ",
|
|
@@ -111,9 +114,23 @@ export async function resolveToken(agentDir, explicit) {
|
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
export function looksLikeToken(token) {
|
|
114
|
-
return token
|
|
117
|
+
return isSparkToken(token);
|
|
115
118
|
}
|
|
116
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Whether a value could be a Spark token at all. Spark mints them as
|
|
122
|
+
* `spark_live_<secret>`, so anything else in the cache slot came from somewhere
|
|
123
|
+
* else and must not be sent to Spark.
|
|
124
|
+
*
|
|
125
|
+
* The length of the secret is deliberately not checked: Spark chooses it, and
|
|
126
|
+
* tightening this to today's value would start rejecting valid tokens the day
|
|
127
|
+
* that changes. Requiring something after the prefix is the safe minimum.
|
|
128
|
+
*/
|
|
129
|
+
export const isSparkToken = (token) =>
|
|
130
|
+
typeof token === "string" &&
|
|
131
|
+
token.startsWith(TOKEN_PREFIX) &&
|
|
132
|
+
token.length > TOKEN_PREFIX.length;
|
|
133
|
+
|
|
117
134
|
/** True when a token can be found without asking the student anything. */
|
|
118
135
|
export async function hasToken(agentDir, explicit) {
|
|
119
136
|
if (explicit) return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stemtrooper/learningcode",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "TLC Spark coding agent for students: Pi wired to the Spark OpenAI-compatible endpoint with per-student tokens, quota and seat-queue awareness.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|