opencode-harness-kit 0.3.6 → 0.3.7
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/CHANGELOG.md +18 -1
- package/package.json +1 -1
- package/src/trust.js +57 -22
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,22 @@ is not happening.
|
|
|
14
14
|
|
|
15
15
|
## [Unreleased]
|
|
16
16
|
|
|
17
|
+
## [0.3.7] — 2026-10-20
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`oc-harness trust` no longer reports a failed create as a configured
|
|
22
|
+
one.** It printed "the trusted publisher is configured" after npm refused,
|
|
23
|
+
because it checked the error TEXT rather than the exit code. The user walks
|
|
24
|
+
away believing the publish path is fixed and finds out at the next release.
|
|
25
|
+
The exit code is what decides now, and npm's own words are printed when it
|
|
26
|
+
is not zero.
|
|
27
|
+
- **A failed read is reported as a failed read**, with npm's answer attached,
|
|
28
|
+
rather than as "nothing is configured" - which sent the reader off to
|
|
29
|
+
create a configuration that already existed.
|
|
30
|
+
- **The one-time password prompt goes to stderr, on a line of its own.** A
|
|
31
|
+
question buried in npm's output is a question nobody answers.
|
|
32
|
+
|
|
17
33
|
## [0.3.6] — 2026-10-20
|
|
18
34
|
|
|
19
35
|
### Fixed
|
|
@@ -319,7 +335,8 @@ silent in the worst way:
|
|
|
319
335
|
|
|
320
336
|
---
|
|
321
337
|
|
|
322
|
-
[Unreleased]: https://github.com/pham-canh/space-harness/compare/v0.3.
|
|
338
|
+
[Unreleased]: https://github.com/pham-canh/space-harness/compare/v0.3.7...HEAD
|
|
339
|
+
[0.3.7]: https://github.com/pham-canh/space-harness/compare/v0.3.6...v0.3.7
|
|
323
340
|
[0.3.6]: https://github.com/pham-canh/space-harness/compare/v0.3.5...v0.3.6
|
|
324
341
|
[0.3.5]: https://github.com/pham-canh/space-harness/compare/v0.3.4...v0.3.5
|
|
325
342
|
[0.3.4]: https://github.com/pham-canh/space-harness/compare/v0.3.1...v0.3.4
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-harness-kit",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Harness kit for OpenCode: capability profiles, a fleet of job lanes with one free model each, role-based reasoning effort, context pruning, and measured baselines.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/pham-canh/space-harness#readme",
|
package/src/trust.js
CHANGED
|
@@ -96,7 +96,7 @@ function npm(args, opts = {}) {
|
|
|
96
96
|
*
|
|
97
97
|
* @param {string[]} args
|
|
98
98
|
* @param {{ otp?: string }} [state]
|
|
99
|
-
* @returns {Promise<string>}
|
|
99
|
+
* @returns {Promise<{ out: string, code: number }>}
|
|
100
100
|
*/
|
|
101
101
|
async function npmWithOtp(args, state = {}) {
|
|
102
102
|
/** @param {string[]} extra */
|
|
@@ -107,28 +107,28 @@ async function npmWithOtp(args, state = {}) {
|
|
|
107
107
|
encoding: "utf8",
|
|
108
108
|
stdio: ["ignore", "pipe", "pipe"],
|
|
109
109
|
}),
|
|
110
|
-
|
|
110
|
+
code: 0,
|
|
111
111
|
}
|
|
112
112
|
} catch (err) {
|
|
113
113
|
const e = /** @type {any} */ (err)
|
|
114
|
-
return { out: `${e.stdout ?? ""}${e.stderr ?? ""}`,
|
|
114
|
+
return { out: `${e.stdout ?? ""}${e.stderr ?? ""}`, code: e.status ?? 1 }
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
const withOtp = state.otp ? [`--otp=${state.otp}`] : []
|
|
119
119
|
let result = run(withOtp)
|
|
120
|
-
if (
|
|
120
|
+
if (result.code === 0 || !/one-time password|EOTP/i.test(result.out)) return result
|
|
121
121
|
|
|
122
122
|
// npm wants a code. Get one.
|
|
123
123
|
const code = state.otp ?? (await askForOtp())
|
|
124
124
|
if (!code) {
|
|
125
125
|
console.error(red("npm needs a one-time password and none was given."))
|
|
126
|
-
console.error(dim(
|
|
127
|
-
|
|
126
|
+
console.error(dim(" run it again with --otp=<the code from your authenticator app>"))
|
|
127
|
+
return { out: result.out, code: result.code }
|
|
128
128
|
}
|
|
129
129
|
state.otp = code
|
|
130
130
|
result = run([`--otp=${code}`])
|
|
131
|
-
return result
|
|
131
|
+
return result
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
@@ -145,9 +145,15 @@ async function askForOtp() {
|
|
|
145
145
|
return null
|
|
146
146
|
}
|
|
147
147
|
const { createInterface } = await import("node:readline/promises")
|
|
148
|
-
|
|
148
|
+
// The prompt goes to stderr, and it gets a line of its own: a question
|
|
149
|
+
// buried in the middle of npm's output is a question nobody answers.
|
|
150
|
+
process.stderr.write("\n")
|
|
151
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr })
|
|
149
152
|
try {
|
|
150
|
-
const answer = (
|
|
153
|
+
const answer = (
|
|
154
|
+
await rl.question(" one-time password from your authenticator app: ")
|
|
155
|
+
).trim()
|
|
156
|
+
process.stderr.write("\n")
|
|
151
157
|
return answer || null
|
|
152
158
|
} finally {
|
|
153
159
|
rl.close()
|
|
@@ -163,19 +169,22 @@ async function askForOtp() {
|
|
|
163
169
|
*
|
|
164
170
|
* @param {string} name
|
|
165
171
|
* @param {{ otp?: string }} [state]
|
|
166
|
-
* @returns {Promise<{ configs: any[], failed: boolean }>}
|
|
172
|
+
* @returns {Promise<{ configs: any[], failed: boolean, detail?: string }>}
|
|
167
173
|
*/
|
|
168
174
|
async function listConfigs(name, state = {}) {
|
|
169
|
-
const out = await npmWithOtp(["trust", "list", name, "--json"], state)
|
|
170
|
-
if (
|
|
171
|
-
|
|
175
|
+
const { out, code } = await npmWithOtp(["trust", "list", name, "--json"], state)
|
|
176
|
+
if (code !== 0) {
|
|
177
|
+
// A package with no configuration answers 404, and a failed call answers
|
|
178
|
+
// with something else. They are different facts.
|
|
179
|
+
if (/E404|not found/i.test(out)) return { configs: [], failed: false }
|
|
180
|
+
return { configs: [], failed: true, detail: out }
|
|
172
181
|
}
|
|
173
182
|
if (!out.trim()) return { configs: [], failed: false }
|
|
174
183
|
let parsed
|
|
175
184
|
try {
|
|
176
185
|
parsed = JSON.parse(out)
|
|
177
186
|
} catch {
|
|
178
|
-
return { configs: [], failed: true }
|
|
187
|
+
return { configs: [], failed: true, detail: out }
|
|
179
188
|
}
|
|
180
189
|
const items = Array.isArray(parsed) ? parsed : parsed.configs ?? [parsed]
|
|
181
190
|
return { configs: items.filter(Boolean), failed: false }
|
|
@@ -226,10 +235,13 @@ export async function main(argv = []) {
|
|
|
226
235
|
return 1
|
|
227
236
|
}
|
|
228
237
|
|
|
229
|
-
const { configs, failed } = await listConfigs(name, state)
|
|
238
|
+
const { configs, failed, detail } = await listConfigs(name, state)
|
|
230
239
|
if (failed && !configs.length) {
|
|
231
240
|
console.error(red("could not read the trusted publisher configuration."))
|
|
232
|
-
|
|
241
|
+
if (detail) {
|
|
242
|
+
console.error(dim(" npm said:"))
|
|
243
|
+
for (const line of detail.trim().split("\n").slice(-12)) console.error(dim(" " + line))
|
|
244
|
+
}
|
|
233
245
|
console.error(dim(` run \`npm trust list ${name}\` to see the raw answer.`))
|
|
234
246
|
return 1
|
|
235
247
|
}
|
|
@@ -244,7 +256,7 @@ export async function main(argv = []) {
|
|
|
244
256
|
return 1
|
|
245
257
|
}
|
|
246
258
|
console.log(` creating it: ${file}, no environment, publish and stage\n`)
|
|
247
|
-
const out = await npmWithOtp(
|
|
259
|
+
const { out, code } = await npmWithOtp(
|
|
248
260
|
[
|
|
249
261
|
"trust",
|
|
250
262
|
"github",
|
|
@@ -259,9 +271,19 @@ export async function main(argv = []) {
|
|
|
259
271
|
],
|
|
260
272
|
state,
|
|
261
273
|
)
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
274
|
+
if (code !== 0) {
|
|
275
|
+
// Printing "configured" after a failed create is the worst thing this
|
|
276
|
+
// command could do: the user walks away believing the publish path is
|
|
277
|
+
// fixed, and finds out at the next release.
|
|
278
|
+
console.error(red("npm did not create the trusted publisher."))
|
|
279
|
+
if (/one-time password|EOTP/i.test(out)) {
|
|
280
|
+
console.error(dim(" run it again with --otp=<the code from your authenticator app>"))
|
|
281
|
+
}
|
|
282
|
+
const detail = out.trim()
|
|
283
|
+
if (detail) {
|
|
284
|
+
console.error(dim(" npm said:"))
|
|
285
|
+
for (const line of detail.split("\n").slice(-12)) console.error(dim(" " + line))
|
|
286
|
+
}
|
|
265
287
|
return 1
|
|
266
288
|
}
|
|
267
289
|
console.log(`\n ${green("✓")} the trusted publisher is configured\n`)
|
|
@@ -314,8 +336,12 @@ export async function main(argv = []) {
|
|
|
314
336
|
console.error(" revoke it on npmjs.com and run this again.")
|
|
315
337
|
return 1
|
|
316
338
|
}
|
|
317
|
-
await npmWithOtp(["trust", "revoke", name, "--id", id, "-y"], state)
|
|
318
|
-
|
|
339
|
+
const revoked = await npmWithOtp(["trust", "revoke", name, "--id", id, "-y"], state)
|
|
340
|
+
if (revoked.code !== 0) {
|
|
341
|
+
console.error(red("npm did not revoke the old configuration, so nothing was replaced."))
|
|
342
|
+
return 1
|
|
343
|
+
}
|
|
344
|
+
const created = await npmWithOtp(
|
|
319
345
|
[
|
|
320
346
|
"trust",
|
|
321
347
|
"github",
|
|
@@ -330,6 +356,15 @@ export async function main(argv = []) {
|
|
|
330
356
|
],
|
|
331
357
|
state,
|
|
332
358
|
)
|
|
359
|
+
if (created.code !== 0) {
|
|
360
|
+
console.error(red("npm did not create the replacement configuration."))
|
|
361
|
+
const detail = created.out.trim()
|
|
362
|
+
if (detail) {
|
|
363
|
+
console.error(dim(" npm said:"))
|
|
364
|
+
for (const line of detail.split("\n").slice(-12)) console.error(dim(" " + line))
|
|
365
|
+
}
|
|
366
|
+
return 1
|
|
367
|
+
}
|
|
333
368
|
console.log(`\n ${green("✓")} the trusted publisher is configured\n`)
|
|
334
369
|
}
|
|
335
370
|
return 0
|