docks-kit 0.16.8 → 0.16.10
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/AGENTS.md +1 -0
- package/cli/docs/plugins.md +11 -0
- package/cli/src/engine-native/claudePlugins.ts +73 -17
- package/cli/src/engine-native/codexSync.ts +7 -4
- package/cli/src/engine-native/failures.ts +16 -0
- package/cli/src/engine-native/index.ts +10 -1
- package/cli/src/engine-native/ompSync.ts +22 -9
- package/cli/src/generated/sotPayload.ts +1 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -78,6 +78,7 @@ For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`, `SoT/.omp/`), see the m
|
|
|
78
78
|
## Engineering rules
|
|
79
79
|
|
|
80
80
|
- **Idempotent operations.** Every EngineNative sync step must be safe to re-run. Settings merges, plugin installs, and marketplace adds are all idempotent — re-running with no SoT changes is a no-op.
|
|
81
|
+
- **Failed harness-CLI operations fail the sync.** A kit-managed marketplace or plugin command that exits non-zero is recorded through `failures.ts recordFailure` — it still warns in place, and `index.ts engineSync, failure ledger` lists every entry under `--- Failures ---` after the summary, then returns exit 1. A skip is not a failure: a missing harness CLI, missing git, a missing `SoT/toolchain.json` pin, and an unavailable plugin inventory stay warn-only with exit 0, and `ompSync.ts syncMarketplace, probe guard` returns before spawning so a host without omp never records one. Aggregate roll-up warnings (`N plugin operation(s) failed`) stay warns, because each counted site already records itself.
|
|
81
82
|
- **Removed bash engine.** The bash engine was removed after the `bash-engine-final` tag. `DOCKS_KIT_ENGINE=bash` must fail with the removed-engine message; engine bugs are fixed forward in EngineNative.
|
|
82
83
|
- **Effect 4 CLI stack.** The CLI pins `effect@4.0.0-rc.109` (including `effect/unstable/cli`), `@effect/platform-bun@4.0.0-rc.109` (`BunServices.layer`, `BunRuntime.runMain`), `@effect/vitest@4.0.0-rc.109`, and `vitest@4.1.11` (inside the `@effect/vitest` peer range `>=4.1.0 <5.0.0`; 4.1.11 fixes GHSA-82fw-gwwq-j7x9). `@effect/cli` and `@effect/platform` are removed and must not be reintroduced.
|
|
83
84
|
- **Effect skill routing.** Effect work in this checkout must verify migration and API call shapes against the installed declarations under `node_modules/effect/dist/unstable/cli/`, never from memory or a mutable dist-tag. The `effect-ts-setup`, `effect-ts-port`, and `effect-ts-specialist` skills target Effect 3.x and do not apply.
|
package/cli/docs/plugins.md
CHANGED
|
@@ -21,6 +21,17 @@ is absent from user settings.
|
|
|
21
21
|
5. Re-assert SoT enabled-state (undoes install's enable side effect on
|
|
22
22
|
false-keyed plugins)
|
|
23
23
|
|
|
24
|
+
## Failed operations fail the sync
|
|
25
|
+
|
|
26
|
+
A marketplace or plugin command that exits non-zero is warned in place AND
|
|
27
|
+
recorded, then listed under a `--- Failures ---` block after the summary; the
|
|
28
|
+
sync exits 1. A stale plugin therefore no longer looks like a clean sync.
|
|
29
|
+
|
|
30
|
+
Not a failure: a missing harness CLI, missing git, a missing
|
|
31
|
+
`SoT/toolchain.json` pin, and an unavailable plugin inventory. Those stay
|
|
32
|
+
warn-and-skip with exit 0, so a host without Claude, Codex, or omp installed
|
|
33
|
+
still syncs its deployed config cleanly.
|
|
34
|
+
|
|
24
35
|
## Optional opt-ins
|
|
25
36
|
|
|
26
37
|
Situational plugins are kept OUT of the SoT and opted in per machine:
|
|
@@ -5,14 +5,25 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync, renameSync, writeFileSync } from "node:fs"
|
|
7
7
|
import { p, spawnProcess } from "./exec"
|
|
8
|
+
import { recordFailure } from "./failures"
|
|
8
9
|
import type { Ctx } from "./index"
|
|
9
10
|
import { compareCodepoints, deepMerge, isObject, jqStringify, parseJson, readJsonFile, type Json } from "./jq"
|
|
10
11
|
import { field } from "./toolchain"
|
|
11
12
|
import { payloadText } from "../payload"
|
|
12
13
|
|
|
13
|
-
async function cli(args: Array<string>): Promise<{ ok: boolean; out: string }> {
|
|
14
|
+
async function cli(args: Array<string>): Promise<{ ok: boolean; out: string; detail: string }> {
|
|
14
15
|
const res = await spawnProcess("claude", args, { stdio: ["ignore", "pipe", "pipe"] })
|
|
15
|
-
|
|
16
|
+
// A spawn error carries its cause in `error`, not in either stream, and that
|
|
17
|
+
// is the case a failure message cannot afford to drop: it names an
|
|
18
|
+
// unresolvable launcher instead of a rejected command.
|
|
19
|
+
const out = res.error !== undefined ? res.error.message : `${res.stdout}${res.stderr}`
|
|
20
|
+
const detail = out.split("\n").map((line) => line.trim()).find((line) => line !== "") ?? "unknown error"
|
|
21
|
+
return { ok: res.error === undefined && res.exitCode === 0, out, detail }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Failure message tail: the cause, then the command to re-run by hand. */
|
|
25
|
+
function manually(detail: string, args: Array<string>): string {
|
|
26
|
+
return `${detail}; run manually: claude ${args.join(" ")}`
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
function sortedKeys(obj: Json | undefined): Array<string> {
|
|
@@ -95,7 +106,10 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
95
106
|
if (marketplaceResult.ok) {
|
|
96
107
|
addedMp++
|
|
97
108
|
} else {
|
|
98
|
-
|
|
109
|
+
recordFailure(
|
|
110
|
+
ctx,
|
|
111
|
+
`Failed to add marketplace: ${mpName} (${repo}): ${manually(marketplaceResult.detail, ["plugin", "marketplace", "add", repo])}`
|
|
112
|
+
)
|
|
99
113
|
f1++
|
|
100
114
|
}
|
|
101
115
|
}
|
|
@@ -104,6 +118,8 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
104
118
|
// refreshing each source marketplace once so the install resolves a current snapshot.
|
|
105
119
|
let addedPl = 0
|
|
106
120
|
let f2 = 0
|
|
121
|
+
let f3 = 0
|
|
122
|
+
let f4 = 0
|
|
107
123
|
const refreshedMarketplaces = new Set<string>()
|
|
108
124
|
for (const pluginId of sortedKeys(sotPlugins)) {
|
|
109
125
|
if (pluginUserScopeInstalled(installedPlugins, pluginId)) continue
|
|
@@ -111,8 +127,15 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
111
127
|
const mpName = separator > 0 ? pluginId.slice(separator + 1) : ""
|
|
112
128
|
if (mpName !== "" && !refreshedMarketplaces.has(mpName)) {
|
|
113
129
|
progress(`Refreshing marketplace ${mpName}...`)
|
|
114
|
-
await cli(["plugin", "marketplace", "update", mpName])
|
|
130
|
+
const refreshResult = await cli(["plugin", "marketplace", "update", mpName])
|
|
115
131
|
clearProgress()
|
|
132
|
+
if (!refreshResult.ok) {
|
|
133
|
+
recordFailure(
|
|
134
|
+
ctx,
|
|
135
|
+
`Failed to refresh marketplace: ${mpName}: ${manually(refreshResult.detail, ["plugin", "marketplace", "update", mpName])}`
|
|
136
|
+
)
|
|
137
|
+
f3++
|
|
138
|
+
}
|
|
116
139
|
refreshedMarketplaces.add(mpName)
|
|
117
140
|
}
|
|
118
141
|
progress(`Installing plugin ${pluginId}...`)
|
|
@@ -121,7 +144,10 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
121
144
|
if (installResult.ok) {
|
|
122
145
|
addedPl++
|
|
123
146
|
} else {
|
|
124
|
-
|
|
147
|
+
recordFailure(
|
|
148
|
+
ctx,
|
|
149
|
+
`Failed to install plugin: ${pluginId}: ${manually(installResult.detail, ["plugin", "install", pluginId])}`
|
|
150
|
+
)
|
|
125
151
|
f2++
|
|
126
152
|
}
|
|
127
153
|
}
|
|
@@ -143,12 +169,24 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
143
169
|
}
|
|
144
170
|
|
|
145
171
|
// Pass 3 — refresh the kit-owned marketplaces unless the update command
|
|
146
|
-
// selected its install-missing-only fast path.
|
|
172
|
+
// selected its install-missing-only fast path. Pass 2 already refreshed the
|
|
173
|
+
// source marketplace of every plugin it installed, so skip those: a second
|
|
174
|
+
// fetch seconds later cannot resolve a newer snapshot, and re-running it
|
|
175
|
+
// would duplicate one failure in the ledger and in the failed-operation count.
|
|
147
176
|
if (!ctx.skipPluginRefresh) {
|
|
148
177
|
for (const mpName of [...kitMarketplaces].sort(compareCodepoints)) {
|
|
178
|
+
if (refreshedMarketplaces.has(mpName)) continue
|
|
149
179
|
progress(`Refreshing marketplace ${mpName}...`)
|
|
150
|
-
await cli(["plugin", "marketplace", "update", mpName])
|
|
180
|
+
const refreshResult = await cli(["plugin", "marketplace", "update", mpName])
|
|
151
181
|
clearProgress()
|
|
182
|
+
refreshedMarketplaces.add(mpName)
|
|
183
|
+
if (!refreshResult.ok) {
|
|
184
|
+
recordFailure(
|
|
185
|
+
ctx,
|
|
186
|
+
`Failed to refresh marketplace: ${mpName}: ${manually(refreshResult.detail, ["plugin", "marketplace", "update", mpName])}`
|
|
187
|
+
)
|
|
188
|
+
f3++
|
|
189
|
+
}
|
|
152
190
|
}
|
|
153
191
|
// Pass 4 — update the kit-owned installed plugins.
|
|
154
192
|
for (const pluginId of [...kitPluginIds].sort(compareCodepoints)) {
|
|
@@ -156,7 +194,15 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
156
194
|
progress(`Updating plugin ${pluginId}...`)
|
|
157
195
|
const updateResult = await cli(["plugin", "update", pluginId, "--scope", "user"])
|
|
158
196
|
clearProgress()
|
|
159
|
-
if (updateResult.
|
|
197
|
+
if (!updateResult.ok) {
|
|
198
|
+
recordFailure(
|
|
199
|
+
ctx,
|
|
200
|
+
`Failed to update plugin: ${pluginId}: ${manually(updateResult.detail, ["plugin", "update", pluginId, "--scope", "user"])}`
|
|
201
|
+
)
|
|
202
|
+
f4++
|
|
203
|
+
} else if (updateResult.out.includes("Successfully updated")) {
|
|
204
|
+
updatedPl++
|
|
205
|
+
}
|
|
160
206
|
}
|
|
161
207
|
}
|
|
162
208
|
|
|
@@ -175,7 +221,10 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
175
221
|
if (uninstallResult.ok) {
|
|
176
222
|
removedPl++
|
|
177
223
|
} else {
|
|
178
|
-
|
|
224
|
+
recordFailure(
|
|
225
|
+
ctx,
|
|
226
|
+
`Failed to uninstall plugin: ${pluginId}: ${manually(uninstallResult.detail, ["plugin", "uninstall", "-y", "--scope", "user", pluginId])}`
|
|
227
|
+
)
|
|
179
228
|
f5++
|
|
180
229
|
}
|
|
181
230
|
}
|
|
@@ -191,7 +240,10 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
191
240
|
if (removeResult.ok) {
|
|
192
241
|
removedMp++
|
|
193
242
|
} else {
|
|
194
|
-
|
|
243
|
+
recordFailure(
|
|
244
|
+
ctx,
|
|
245
|
+
`Failed to remove marketplace: ${mpName}: ${manually(removeResult.detail, ["plugin", "marketplace", "remove", mpName])}`
|
|
246
|
+
)
|
|
195
247
|
f6++
|
|
196
248
|
}
|
|
197
249
|
}
|
|
@@ -203,7 +255,7 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
203
255
|
ctx.nextStepTriggers.claudePlugins = true
|
|
204
256
|
}
|
|
205
257
|
|
|
206
|
-
const failed = f1 + f2 + f5 + f6
|
|
258
|
+
const failed = f1 + f2 + f3 + f4 + f5 + f6
|
|
207
259
|
if (addedMp > 0 || addedPl > 0 || updatedPl > 0 || removedPl > 0 || removedMp > 0) {
|
|
208
260
|
change(`Plugins synced (marketplaces: +${addedMp} -${removedMp}, plugins: +${addedPl} ~${updatedPl} -${removedPl})`)
|
|
209
261
|
ctx.nextStepTriggers.claudePlugins = true
|
|
@@ -226,10 +278,14 @@ async function reassertEnabledState(ctx: Ctx, repoObj: { [k: string]: Json }, us
|
|
|
226
278
|
const user = readJsonFile(userSettingsFile)
|
|
227
279
|
const enabled = user !== undefined && isObject(user) && isObject(user["enabledPlugins"]) ? (user["enabledPlugins"] as { [k: string]: Json })[pluginId] : undefined
|
|
228
280
|
if (enabled !== true) continue
|
|
229
|
-
|
|
281
|
+
const disableResult = await cli(["plugin", "disable", pluginId])
|
|
282
|
+
if (disableResult.ok) {
|
|
230
283
|
cliDisabled = true
|
|
231
284
|
} else {
|
|
232
|
-
|
|
285
|
+
recordFailure(
|
|
286
|
+
ctx,
|
|
287
|
+
`Failed to disable SoT-false plugin: ${pluginId} (will retry next sync): ${manually(disableResult.detail, ["plugin", "disable", pluginId])}`
|
|
288
|
+
)
|
|
233
289
|
}
|
|
234
290
|
}
|
|
235
291
|
|
|
@@ -250,7 +306,7 @@ async function reassertEnabledState(ctx: Ctx, repoObj: { [k: string]: Json }, us
|
|
|
250
306
|
// ------------------------------------------------------ optional plugins ----
|
|
251
307
|
|
|
252
308
|
async function enableOptionalPlugin(ctx: Ctx, claudeDir: string, pluginId: string, marketplaceRepo: string): Promise<boolean> {
|
|
253
|
-
const { change, clearProgress, progress, verbose
|
|
309
|
+
const { change, clearProgress, progress, verbose } = ctx.services.logger
|
|
254
310
|
const installedPlugins = p(claudeDir, "plugins", "installed_plugins.json")
|
|
255
311
|
const knownMarketplaces = p(claudeDir, "plugins", "known_marketplaces.json")
|
|
256
312
|
const mpName = pluginId.slice(pluginId.lastIndexOf("@") + 1)
|
|
@@ -261,7 +317,7 @@ async function enableOptionalPlugin(ctx: Ctx, claudeDir: string, pluginId: strin
|
|
|
261
317
|
const has = known !== undefined && isObject(known) && known[mpName] !== undefined && known[mpName] !== null && known[mpName] !== false
|
|
262
318
|
if (!has) {
|
|
263
319
|
if (!(await cli(["plugin", "marketplace", "add", marketplaceRepo])).ok) {
|
|
264
|
-
|
|
320
|
+
recordFailure(ctx, `Failed to add marketplace ${marketplaceRepo} for ${pluginId}`)
|
|
265
321
|
return false
|
|
266
322
|
}
|
|
267
323
|
marketplaceAdded = true
|
|
@@ -275,7 +331,7 @@ async function enableOptionalPlugin(ctx: Ctx, claudeDir: string, pluginId: strin
|
|
|
275
331
|
clearProgress()
|
|
276
332
|
if (!installResult.ok) {
|
|
277
333
|
if (marketplaceAdded) change(`Optional plugin ${pluginId}: marketplace added (install failed — will retry next sync)`)
|
|
278
|
-
|
|
334
|
+
recordFailure(ctx, `Failed to install optional plugin ${pluginId}`)
|
|
279
335
|
return marketplaceAdded
|
|
280
336
|
}
|
|
281
337
|
}
|
|
@@ -288,7 +344,7 @@ async function enableOptionalPlugin(ctx: Ctx, claudeDir: string, pluginId: strin
|
|
|
288
344
|
|
|
289
345
|
if (!(await cli(["plugin", "enable", pluginId])).ok) {
|
|
290
346
|
if (marketplaceAdded || !wasInstalled) change(`Optional plugin ${pluginId}: installed (enable failed — will retry next sync)`)
|
|
291
|
-
|
|
347
|
+
recordFailure(ctx, `Failed to enable optional plugin ${pluginId}`)
|
|
292
348
|
return marketplaceAdded || !wasInstalled
|
|
293
349
|
}
|
|
294
350
|
const changed = marketplaceAdded || !wasInstalled || !wasEnabled
|
|
@@ -7,6 +7,7 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync,
|
|
|
7
7
|
|
|
8
8
|
import { mergeTableSettings, mergeTopLevelSettings, syncCodexEffort, syncCodexModel } from "./codexToml"
|
|
9
9
|
import { p, spawnProcess } from "./exec"
|
|
10
|
+
import { recordFailure } from "./failures"
|
|
10
11
|
import type { Ctx } from "./index"
|
|
11
12
|
import { compareCodepoints, isObject, jqStringify, parseJson, type Json } from "./jq"
|
|
12
13
|
import { hostOs } from "./os"
|
|
@@ -466,7 +467,7 @@ function marketplaceSource(marketplace: string, configFile: string): string {
|
|
|
466
467
|
}
|
|
467
468
|
|
|
468
469
|
async function removeLegacyDocksMarketplace(ctx: Ctx, userConfig: string): Promise<void> {
|
|
469
|
-
const { change, echo
|
|
470
|
+
const { change, echo } = ctx.services.logger
|
|
470
471
|
if (ctx.dryRun) {
|
|
471
472
|
echo("[dry-run] remove legacy configured Codex Docks marketplace when personal marketplace is deployed")
|
|
472
473
|
return
|
|
@@ -481,7 +482,7 @@ async function removeLegacyDocksMarketplace(ctx: Ctx, userConfig: string): Promi
|
|
|
481
482
|
change("Removed legacy configured Codex Docks marketplace; using personal marketplace file")
|
|
482
483
|
ctx.nextStepTriggers.codexRestart = true
|
|
483
484
|
} else {
|
|
484
|
-
|
|
485
|
+
recordFailure(ctx, "Failed to remove legacy configured Codex Docks marketplace")
|
|
485
486
|
}
|
|
486
487
|
}
|
|
487
488
|
|
|
@@ -592,13 +593,15 @@ async function syncPlugins(ctx: Ctx, sotConfigText: string): Promise<void> {
|
|
|
592
593
|
if (res.error === undefined && res.exitCode === 0) {
|
|
593
594
|
refreshed++
|
|
594
595
|
} else if (addOut.includes("could not find a Codex CLI binary")) {
|
|
595
|
-
|
|
596
|
+
recordFailure(
|
|
597
|
+
ctx,
|
|
596
598
|
`Codex plugin refresh hit a stale launcher/wrapper on PATH - install current standalone Codex with: ${standaloneInstallCommand(ctx)}`
|
|
597
599
|
)
|
|
598
600
|
failed++
|
|
599
601
|
} else {
|
|
600
602
|
const failureLine = addOut.split("\n")[0] ?? ""
|
|
601
|
-
|
|
603
|
+
recordFailure(
|
|
604
|
+
ctx,
|
|
602
605
|
`Codex plugin refresh failed for ${pluginId}: ${failureLine !== "" ? failureLine : "unknown error"}; run manually: codex plugin add ${pluginId}`
|
|
603
606
|
)
|
|
604
607
|
failed++
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Failure ledger for harness-CLI operations. A kit-managed marketplace or
|
|
3
|
+
* plugin command that exits non-zero is recorded here so engineSync can list
|
|
4
|
+
* it and exit non-zero instead of reporting a clean sync.
|
|
5
|
+
*/
|
|
6
|
+
import type { Ctx } from "./index"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Record a failed harness-CLI operation. The warning still prints immediately so
|
|
10
|
+
* position in the output is preserved; the recorded message also lets engineSync
|
|
11
|
+
* list the failure in the summary and exit non-zero.
|
|
12
|
+
*/
|
|
13
|
+
export function recordFailure(ctx: Ctx, message: string): void {
|
|
14
|
+
ctx.failures.push(message)
|
|
15
|
+
ctx.services.logger.warn(message)
|
|
16
|
+
}
|
|
@@ -117,6 +117,8 @@ export interface Ctx {
|
|
|
117
117
|
skillsRestart: boolean
|
|
118
118
|
ompRestart: boolean
|
|
119
119
|
}
|
|
120
|
+
/** Harness-CLI operations that failed this run; a non-empty list fails the sync. */
|
|
121
|
+
readonly failures: Array<string>
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
/** Globals default from env using the historical ${VAR:-default} contract. */
|
|
@@ -170,7 +172,8 @@ function makeCtx(services: EngineServices): Ctx {
|
|
|
170
172
|
codexRestart: false,
|
|
171
173
|
skillsRestart: false,
|
|
172
174
|
ompRestart: false
|
|
173
|
-
}
|
|
175
|
+
},
|
|
176
|
+
failures: []
|
|
174
177
|
}
|
|
175
178
|
}
|
|
176
179
|
|
|
@@ -291,6 +294,12 @@ async function engineSync(ctx: Ctx, args: ReadonlyArray<string>): Promise<number
|
|
|
291
294
|
echo("")
|
|
292
295
|
for (const line of advice) echo(line)
|
|
293
296
|
}
|
|
297
|
+
if (ctx.failures.length > 0) {
|
|
298
|
+
echo("")
|
|
299
|
+
echo("--- Failures ---")
|
|
300
|
+
for (const failure of ctx.failures) echo(`- ${failure}`)
|
|
301
|
+
return 1
|
|
302
|
+
}
|
|
294
303
|
return 0
|
|
295
304
|
}
|
|
296
305
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* EngineNative `sync omp` pipeline. config.yml
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* EngineNative `sync omp` pipeline. config.yml merges through mergeOmpConfig
|
|
3
|
+
* and models.yml through mergeOmpModels. Both preserve user-only keys.
|
|
4
|
+
* Paths come from ompPaths because profiles, PI_CONFIG_DIR,
|
|
5
|
+
* PI_CODING_AGENT_DIR, and XDG roots each move them.
|
|
5
6
|
* Resolution stays within the environment and filesystem probes so no omp
|
|
6
7
|
* subcommand runs under ctx.dryRun.
|
|
7
8
|
*/
|
|
@@ -11,6 +12,7 @@ import { basename, isAbsolute, resolve } from "node:path"
|
|
|
11
12
|
import { payloadDisplayPath, payloadText, type PayloadPath } from "../payload"
|
|
12
13
|
import { bunBootstrap } from "./bun"
|
|
13
14
|
import { p, spawnProcess, type AsyncProcessResult } from "./exec"
|
|
15
|
+
import { recordFailure } from "./failures"
|
|
14
16
|
import type { Ctx } from "./index"
|
|
15
17
|
import { isObject, parseJson } from "./jq"
|
|
16
18
|
import { ompPaths } from "./ompPaths"
|
|
@@ -190,7 +192,7 @@ function firstOutputLine(result: AsyncProcessResult): string {
|
|
|
190
192
|
* adoption and leaves the active registry present.
|
|
191
193
|
*/
|
|
192
194
|
async function syncMarketplace(ctx: Ctx, registryFile: string, legacyRegistryFile?: string): Promise<void> {
|
|
193
|
-
const { change, clearProgress, echo, progress, verbose
|
|
195
|
+
const { change, clearProgress, echo, progress, verbose } = ctx.services.logger
|
|
194
196
|
const registered = registryHasDocks(registryFile)
|
|
195
197
|
const adoptable = !registered && legacyRegistryFile !== undefined && registryHasDocks(legacyRegistryFile)
|
|
196
198
|
|
|
@@ -206,6 +208,13 @@ async function syncMarketplace(ctx: Ctx, registryFile: string, legacyRegistryFil
|
|
|
206
208
|
return
|
|
207
209
|
}
|
|
208
210
|
|
|
211
|
+
// syncPlugins owns the single skip message for both missing CLIs, and it runs
|
|
212
|
+
// right after this pass. Without omp no marketplace command can run at all,
|
|
213
|
+
// and without git a marketplace clone cannot resolve, so both are deliberate
|
|
214
|
+
// skips: return silently instead of spawning and recording a failure.
|
|
215
|
+
if (ctx.services.deps.probe("omp").state === "missing") return
|
|
216
|
+
if (ctx.services.deps.probe("git").state === "missing") return
|
|
217
|
+
|
|
209
218
|
if (!registered && !adoptable) {
|
|
210
219
|
progress("Registering omp docks marketplace...")
|
|
211
220
|
const result = await spawnProcess("omp", ["plugin", "marketplace", "add", MARKETPLACE_SOURCE], {
|
|
@@ -215,7 +224,8 @@ async function syncMarketplace(ctx: Ctx, registryFile: string, legacyRegistryFil
|
|
|
215
224
|
if (result.error === undefined && result.exitCode === 0) {
|
|
216
225
|
change("omp docks marketplace registered")
|
|
217
226
|
} else {
|
|
218
|
-
|
|
227
|
+
recordFailure(
|
|
228
|
+
ctx,
|
|
219
229
|
`omp docks marketplace registration failed: ${firstOutputLine(result)}; run manually: omp plugin marketplace add ${MARKETPLACE_SOURCE}`
|
|
220
230
|
)
|
|
221
231
|
}
|
|
@@ -239,7 +249,8 @@ async function syncMarketplace(ctx: Ctx, registryFile: string, legacyRegistryFil
|
|
|
239
249
|
if (listed.error === undefined && listed.exitCode === 0) {
|
|
240
250
|
change("omp docks marketplace registry adopted; refresh-only update skipped")
|
|
241
251
|
} else {
|
|
242
|
-
|
|
252
|
+
recordFailure(
|
|
253
|
+
ctx,
|
|
243
254
|
`omp docks marketplace adoption failed: ${firstOutputLine(listed)}; run manually: omp plugin marketplace list`
|
|
244
255
|
)
|
|
245
256
|
}
|
|
@@ -254,7 +265,8 @@ async function syncMarketplace(ctx: Ctx, registryFile: string, legacyRegistryFil
|
|
|
254
265
|
if (result.error === undefined && result.exitCode === 0) {
|
|
255
266
|
verbose("omp docks marketplace refreshed")
|
|
256
267
|
} else {
|
|
257
|
-
|
|
268
|
+
recordFailure(
|
|
269
|
+
ctx,
|
|
258
270
|
`omp docks marketplace update failed: ${firstOutputLine(result)}; run manually: omp plugin marketplace update ${MARKETPLACE_NAME}`
|
|
259
271
|
)
|
|
260
272
|
}
|
|
@@ -308,13 +320,14 @@ async function runPluginCommand(
|
|
|
308
320
|
plugin: string,
|
|
309
321
|
args: ReadonlyArray<string>
|
|
310
322
|
): Promise<boolean> {
|
|
311
|
-
const { clearProgress, progress
|
|
323
|
+
const { clearProgress, progress } = ctx.services.logger
|
|
312
324
|
progress(`Updating omp plugin ${plugin}...`)
|
|
313
325
|
const result = await spawnProcess("omp", args, { stdio: ["ignore", "pipe", "pipe"] })
|
|
314
326
|
clearProgress()
|
|
315
327
|
if (result.error === undefined && result.exitCode === 0) return true
|
|
316
328
|
|
|
317
|
-
|
|
329
|
+
recordFailure(
|
|
330
|
+
ctx,
|
|
318
331
|
`omp plugin operation failed for ${plugin}: ${firstOutputLine(result)}; run manually: omp ${args.join(" ")}`
|
|
319
332
|
)
|
|
320
333
|
return false
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated by cli/scripts/generate-sot-payload.ts. DO NOT EDIT.
|
|
2
2
|
// Edit SoT/, notification.mp3, or package.json, then run: bun cli/scripts/generate-sot-payload.ts
|
|
3
3
|
|
|
4
|
-
export const GENERATED_PACKAGE_VERSION = "0.16.
|
|
4
|
+
export const GENERATED_PACKAGE_VERSION = "0.16.10"
|
|
5
5
|
|
|
6
6
|
export const GENERATED_PAYLOAD_TEXT = {
|
|
7
7
|
"SoT/.agents/skills.txt": "# Universal AI-agent skill manifest intentionally empty.\n# Global skill discovery is opt-in: add one <owner>/<repo> slug per line.\n# EngineNative ignores comments and blank lines.\n",
|
package/package.json
CHANGED