docks-kit 0.3.0 → 0.5.0
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 +7 -4
- package/README.md +9 -3
- package/cli/docs/flags.md +6 -1
- package/cli/docs/install.md +27 -1
- package/cli/docs/models.md +8 -8
- package/cli/docs/modifiers.md +20 -3
- package/cli/docs/overview.md +4 -1
- package/cli/docs/platforms.md +5 -2
- package/cli/docs/sync-layers.md +22 -11
- package/cli/docs/toolchain.md +8 -2
- package/cli/src/commands/sync.ts +34 -3
- package/cli/src/efforts.ts +91 -0
- package/cli/src/engine-native/DESIGN.md +21 -14
- package/cli/src/engine-native/bun.ts +87 -0
- package/cli/src/engine-native/claudeRuntime.ts +132 -0
- package/cli/src/engine-native/claudeSettingsModifiers.ts +101 -0
- package/cli/src/engine-native/claudeSync.ts +177 -104
- package/cli/src/engine-native/codexSync.ts +2 -1
- package/cli/src/engine-native/codexToml.ts +41 -8
- package/cli/src/engine-native/deps.ts +27 -10
- package/cli/src/engine-native/index.ts +22 -5
- package/cli/src/engine-native/modes.ts +6 -6
- package/cli/src/engine-native/parseArgs.ts +113 -22
- package/cli/src/engine-native/powershell.ts +11 -0
- package/cli/src/engine-native/skillsSync.ts +4 -36
- package/cli/src/generated/sotPayload.ts +14 -12
- package/cli/src/main.ts +19 -9
- package/package.json +1 -1
- package/cli/src/engine-native/claudeModel.ts +0 -54
|
@@ -10,11 +10,19 @@ import { homedir } from "node:os"
|
|
|
10
10
|
|
|
11
11
|
import { kitHome } from "../kitHome"
|
|
12
12
|
import { makeEngineServices, type EngineServices, type Logger } from "./services"
|
|
13
|
+
import type { BunRuntimeState } from "./bun"
|
|
13
14
|
import { claudeNextSteps, claudeSummary, claudeSync } from "./claudeSync"
|
|
14
15
|
import { codexNextSteps, codexSummary, codexSync } from "./codexSync"
|
|
15
16
|
import { skillsNextSteps, skillsSummary, skillsSync } from "./skillsSync"
|
|
16
17
|
import { modeModel, modeToolchain } from "./modes"
|
|
17
|
-
import { ExitError, parseArgs,
|
|
18
|
+
import { ExitError, parseArgs, validateModifierFlags } from "./parseArgs"
|
|
19
|
+
|
|
20
|
+
export type ModifierFlag =
|
|
21
|
+
| "--claude-model"
|
|
22
|
+
| "--claude-effort"
|
|
23
|
+
| "--claude-advisor"
|
|
24
|
+
| "--codex-model"
|
|
25
|
+
| "--codex-effort"
|
|
18
26
|
|
|
19
27
|
export interface Ctx {
|
|
20
28
|
readonly repoDir: string
|
|
@@ -30,9 +38,15 @@ export interface Ctx {
|
|
|
30
38
|
claudePermissive: boolean
|
|
31
39
|
claudePlugins: Array<string>
|
|
32
40
|
claudeModel: string
|
|
41
|
+
claudeEffort: string
|
|
42
|
+
claudeAdvisor: string
|
|
33
43
|
codexModel: string
|
|
44
|
+
codexEffort: string
|
|
45
|
+
/** Distinguishes an explicitly empty modifier from an option that was not supplied. */
|
|
46
|
+
modifierFlags?: Set<ModifierFlag>
|
|
34
47
|
/** Injected capability seam (logger/deps/platform) — see services.ts. */
|
|
35
48
|
readonly services: EngineServices
|
|
49
|
+
bunRuntime?: BunRuntimeState
|
|
36
50
|
targetFilterSet: boolean
|
|
37
51
|
syncClaude: boolean
|
|
38
52
|
syncCodex: boolean
|
|
@@ -64,7 +78,11 @@ function makeCtx(services: EngineServices): Ctx {
|
|
|
64
78
|
claudePermissive: env["CLAUDE_PERMISSIVE"] === "1",
|
|
65
79
|
claudePlugins: (env["CLAUDE_PLUGINS"] ?? "").split(" ").filter((s) => s !== ""),
|
|
66
80
|
claudeModel: env["CLAUDE_MODEL"] ?? "",
|
|
81
|
+
claudeEffort: "",
|
|
82
|
+
claudeAdvisor: "",
|
|
67
83
|
codexModel: env["CODEX_MODEL"] ?? "",
|
|
84
|
+
codexEffort: "",
|
|
85
|
+
modifierFlags: new Set(),
|
|
68
86
|
services,
|
|
69
87
|
targetFilterSet: false,
|
|
70
88
|
syncClaude: false,
|
|
@@ -77,11 +95,10 @@ function makeCtx(services: EngineServices): Ctx {
|
|
|
77
95
|
function engineSync(ctx: Ctx, args: ReadonlyArray<string>): number {
|
|
78
96
|
const { echo } = ctx.services.logger
|
|
79
97
|
parseArgs(ctx, args)
|
|
80
|
-
|
|
81
|
-
validateModelFlags(ctx)
|
|
98
|
+
validateModifierFlags(ctx)
|
|
82
99
|
|
|
83
100
|
const claudeRan = ctx.syncClaude
|
|
84
|
-
|
|
101
|
+
const claudeRuntime = claudeRan ? claudeSync(ctx) : undefined
|
|
85
102
|
|
|
86
103
|
const codexRan = ctx.syncCodex
|
|
87
104
|
if (codexRan) codexSync(ctx)
|
|
@@ -91,7 +108,7 @@ function engineSync(ctx: Ctx, args: ReadonlyArray<string>): number {
|
|
|
91
108
|
echo("")
|
|
92
109
|
echo("--- Sync complete ---")
|
|
93
110
|
echo(`Repo: ${ctx.repoDir}`)
|
|
94
|
-
if (
|
|
111
|
+
if (claudeRuntime !== undefined) claudeSummary(ctx, claudeRuntime)
|
|
95
112
|
if (codexRan) codexSummary(ctx)
|
|
96
113
|
if (skillsState !== undefined) skillsSummary(ctx, skillsState)
|
|
97
114
|
|
|
@@ -6,13 +6,14 @@ import { p } from "./exec"
|
|
|
6
6
|
import { readFileSync } from "node:fs"
|
|
7
7
|
import { payloadText } from "../payload"
|
|
8
8
|
|
|
9
|
-
import { syncClaudeModel } from "./
|
|
9
|
+
import { syncClaudeModel } from "./claudeSettingsModifiers"
|
|
10
10
|
import { syncCodexModel } from "./codexToml"
|
|
11
11
|
import type { Ctx } from "./index"
|
|
12
12
|
import { isObject, parseJson, type Json } from "./jq"
|
|
13
13
|
import { printModels, validateClaudeModel, validateCodexModel } from "./models"
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { ensureRtk } from "./claudeSync"
|
|
15
|
+
import { bunBootstrap } from "./bun"
|
|
16
|
+
import { agentBrowserInstall, effectSolutionsInstall } from "./skillsSync"
|
|
16
17
|
import { ensure, report } from "./toolchain"
|
|
17
18
|
|
|
18
19
|
export function modeModel(ctx: Ctx, args: ReadonlyArray<string>): number {
|
|
@@ -134,10 +135,9 @@ export function modeToolchain(ctx: Ctx, args: ReadonlyArray<string>): number {
|
|
|
134
135
|
}
|
|
135
136
|
switch (tool) {
|
|
136
137
|
case "rtk":
|
|
137
|
-
return
|
|
138
|
+
return ensureRtk(ctx, "cannot download RTK installer; toolchain ensure rtk aborted", 1)
|
|
138
139
|
case "bun":
|
|
139
|
-
|
|
140
|
-
return bunBootstrap(ctx, ctx.services) !== "" ? 0 : 1
|
|
140
|
+
return bunBootstrap(ctx, ctx.services).kind === "ready" ? 0 : 1
|
|
141
141
|
case "effect-solutions":
|
|
142
142
|
return ensure(ctx, "effect-solutions", effectSolutionsInstall(ctx))
|
|
143
143
|
case "agent-browser":
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* EngineNative flag layer: usage / target selection / compact-window parsing /
|
|
3
|
-
* optional-plugin parsing / model validation
|
|
3
|
+
* optional-plugin parsing / model validation. ExitError mirrors an
|
|
4
4
|
* early parser exit and is caught once in runEngineNative.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { Ctx } from "./index"
|
|
7
|
+
import type { Ctx, ModifierFlag } from "./index"
|
|
8
|
+
import {
|
|
9
|
+
CLAUDE_ADVISOR_STATES,
|
|
10
|
+
advisorCatalog,
|
|
11
|
+
advisorFlagGrammar,
|
|
12
|
+
advisorValueGrammar,
|
|
13
|
+
effortCatalog,
|
|
14
|
+
effortFlagGrammar,
|
|
15
|
+
effortValueGrammar,
|
|
16
|
+
isEffortModifierValue
|
|
17
|
+
} from "../efforts"
|
|
8
18
|
import { printModels, validateClaudeModel, validateCodexModel } from "./models"
|
|
9
19
|
|
|
10
20
|
export class ExitError extends Error {
|
|
@@ -14,6 +24,13 @@ export class ExitError extends Error {
|
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
const KNOWN_CLAUDE_OPTIN_PLUGINS = ["supabase", "n8n"]
|
|
27
|
+
const MODIFIER_FLAGS = new Set<ModifierFlag>([
|
|
28
|
+
"--claude-model",
|
|
29
|
+
"--claude-effort",
|
|
30
|
+
"--claude-advisor",
|
|
31
|
+
"--codex-model",
|
|
32
|
+
"--codex-effort"
|
|
33
|
+
])
|
|
17
34
|
|
|
18
35
|
function usage(ctx: Ctx): void {
|
|
19
36
|
const { echo } = ctx.services.logger
|
|
@@ -41,6 +58,8 @@ function usage(ctx: Ctx): void {
|
|
|
41
58
|
echo(
|
|
42
59
|
" --claude-model=<m> set deployed ~/.claude/settings.json model (aliases: best|opus|fable|sonnet|haiku, full claude-* IDs, or 'default' to unset)"
|
|
43
60
|
)
|
|
61
|
+
echo(` ${effortFlagGrammar("claude").padEnd(39)} set deployed effortLevel`)
|
|
62
|
+
echo(` ${advisorFlagGrammar().padEnd(39)} set deployed advisor state`)
|
|
44
63
|
echo(
|
|
45
64
|
" --claude-compact-window=<n> set deployed autocompact window in tokens (e.g. 680000 or 680k) for disposable sessions"
|
|
46
65
|
)
|
|
@@ -48,6 +67,7 @@ function usage(ctx: Ctx): void {
|
|
|
48
67
|
" --claude-permissive empty permissions.ask/deny in deployed settings (sandboxes/containers; unattended commits + pushes)"
|
|
49
68
|
)
|
|
50
69
|
echo(" --codex-model=<m> set deployed ~/.codex/config.toml model (e.g. gpt-5.5)")
|
|
70
|
+
echo(` ${effortFlagGrammar("codex").padEnd(39)} set deployed model_reasoning_effort`)
|
|
51
71
|
echo("")
|
|
52
72
|
echo("Sticky opt-ins (installed + enabled until --prune)")
|
|
53
73
|
echo(
|
|
@@ -81,9 +101,42 @@ function selectTarget(ctx: Ctx, target: string): void {
|
|
|
81
101
|
ctx.targetFilterSet = true
|
|
82
102
|
}
|
|
83
103
|
|
|
104
|
+
function setModifier(ctx: Ctx, flag: ModifierFlag, value: string): void {
|
|
105
|
+
switch (flag) {
|
|
106
|
+
case "--claude-model":
|
|
107
|
+
ctx.claudeModel = value
|
|
108
|
+
break
|
|
109
|
+
case "--claude-effort":
|
|
110
|
+
ctx.claudeEffort = value
|
|
111
|
+
break
|
|
112
|
+
case "--claude-advisor":
|
|
113
|
+
ctx.claudeAdvisor = value
|
|
114
|
+
break
|
|
115
|
+
case "--codex-model":
|
|
116
|
+
ctx.codexModel = value
|
|
117
|
+
break
|
|
118
|
+
case "--codex-effort":
|
|
119
|
+
ctx.codexEffort = value
|
|
120
|
+
break
|
|
121
|
+
}
|
|
122
|
+
const flags = ctx.modifierFlags ?? new Set<ModifierFlag>()
|
|
123
|
+
flags.add(flag)
|
|
124
|
+
ctx.modifierFlags = flags
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isModifierFlag(value: string): value is ModifierFlag {
|
|
128
|
+
return MODIFIER_FLAGS.has(value as ModifierFlag)
|
|
129
|
+
}
|
|
130
|
+
|
|
84
131
|
export function parseArgs(ctx: Ctx, args: ReadonlyArray<string>): void {
|
|
85
132
|
const { err } = ctx.services.logger
|
|
86
|
-
for (
|
|
133
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
134
|
+
const arg = args[index] ?? ""
|
|
135
|
+
if (isModifierFlag(arg) && args[index + 1] === "") {
|
|
136
|
+
setModifier(ctx, arg, "")
|
|
137
|
+
index += 1
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
87
140
|
switch (arg) {
|
|
88
141
|
case "claude":
|
|
89
142
|
case "codex":
|
|
@@ -116,6 +169,18 @@ export function parseArgs(ctx: Ctx, args: ReadonlyArray<string>): void {
|
|
|
116
169
|
printModels(ctx, "codex")
|
|
117
170
|
err("--codex-model requires a value: --codex-model=<model>")
|
|
118
171
|
throw new ExitError(2)
|
|
172
|
+
case "--claude-effort":
|
|
173
|
+
printCatalog(ctx, effortCatalog("claude"))
|
|
174
|
+
err(`--claude-effort requires a value: ${effortFlagGrammar("claude")}`)
|
|
175
|
+
throw new ExitError(2)
|
|
176
|
+
case "--codex-effort":
|
|
177
|
+
printCatalog(ctx, effortCatalog("codex"))
|
|
178
|
+
err(`--codex-effort requires a value: ${effortFlagGrammar("codex")}`)
|
|
179
|
+
throw new ExitError(2)
|
|
180
|
+
case "--claude-advisor":
|
|
181
|
+
printCatalog(ctx, advisorCatalog())
|
|
182
|
+
err(`--claude-advisor requires a value: ${advisorFlagGrammar()}`)
|
|
183
|
+
throw new ExitError(2)
|
|
119
184
|
case "--claude-compact-window":
|
|
120
185
|
err("--claude-compact-window requires a value: --claude-compact-window=<tokens> (e.g. 680k)")
|
|
121
186
|
throw new ExitError(2)
|
|
@@ -159,9 +224,15 @@ export function parseArgs(ctx: Ctx, args: ReadonlyArray<string>): void {
|
|
|
159
224
|
break
|
|
160
225
|
}
|
|
161
226
|
if (arg.startsWith("--claude-model=")) {
|
|
162
|
-
ctx
|
|
227
|
+
setModifier(ctx, "--claude-model", arg.slice("--claude-model=".length))
|
|
163
228
|
} else if (arg.startsWith("--codex-model=")) {
|
|
164
|
-
ctx
|
|
229
|
+
setModifier(ctx, "--codex-model", arg.slice("--codex-model=".length))
|
|
230
|
+
} else if (arg.startsWith("--claude-effort=")) {
|
|
231
|
+
setModifier(ctx, "--claude-effort", arg.slice("--claude-effort=".length))
|
|
232
|
+
} else if (arg.startsWith("--codex-effort=")) {
|
|
233
|
+
setModifier(ctx, "--codex-effort", arg.slice("--codex-effort=".length))
|
|
234
|
+
} else if (arg.startsWith("--claude-advisor=")) {
|
|
235
|
+
setModifier(ctx, "--claude-advisor", arg.slice("--claude-advisor=".length))
|
|
165
236
|
} else if (arg.startsWith("--claude-compact-window=")) {
|
|
166
237
|
const parsed = parseCompactWindow(arg.slice("--claude-compact-window=".length))
|
|
167
238
|
if (parsed === undefined) {
|
|
@@ -184,25 +255,15 @@ export function parseArgs(ctx: Ctx, args: ReadonlyArray<string>): void {
|
|
|
184
255
|
}
|
|
185
256
|
}
|
|
186
257
|
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
if (ctx.syncClaude || ctx.syncCodex) {
|
|
190
|
-
if (ctx.services.deps.probe("jq").state === "missing") {
|
|
191
|
-
err(`jq is required (deployed statusline/hooks call it). Install: ${ctx.services.deps.spec("jq").installHint()}`)
|
|
192
|
-
throw new ExitError(1)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (ctx.syncClaude) {
|
|
196
|
-
if (ctx.services.deps.probe("curl").state === "missing") {
|
|
197
|
-
err(`curl is required. Install: ${ctx.services.deps.spec("curl").installHint()}`)
|
|
198
|
-
throw new ExitError(1)
|
|
199
|
-
}
|
|
200
|
-
}
|
|
258
|
+
function printCatalog(ctx: Ctx, catalog: string): void {
|
|
259
|
+
for (const line of catalog.split("\n")) ctx.services.logger.echo(line)
|
|
201
260
|
}
|
|
202
261
|
|
|
203
|
-
export function
|
|
262
|
+
export function validateModifierFlags(ctx: Ctx): void {
|
|
204
263
|
const { err, warn } = ctx.services.logger
|
|
205
|
-
|
|
264
|
+
const supplied = (flag: ModifierFlag, value: string): boolean =>
|
|
265
|
+
value !== "" || ctx.modifierFlags?.has(flag) === true
|
|
266
|
+
if (supplied("--claude-model", ctx.claudeModel)) {
|
|
206
267
|
if (!ctx.syncClaude) {
|
|
207
268
|
warn("--claude-model ignored: claude target not selected")
|
|
208
269
|
ctx.claudeModel = ""
|
|
@@ -212,7 +273,27 @@ export function validateModelFlags(ctx: Ctx): void {
|
|
|
212
273
|
throw new ExitError(2)
|
|
213
274
|
}
|
|
214
275
|
}
|
|
215
|
-
if (ctx.
|
|
276
|
+
if (supplied("--claude-effort", ctx.claudeEffort)) {
|
|
277
|
+
if (!ctx.syncClaude) {
|
|
278
|
+
warn("--claude-effort ignored: claude target not selected")
|
|
279
|
+
ctx.claudeEffort = ""
|
|
280
|
+
} else if (!isEffortModifierValue("claude", ctx.claudeEffort)) {
|
|
281
|
+
printCatalog(ctx, effortCatalog("claude"))
|
|
282
|
+
err(`Invalid Claude effort '${ctx.claudeEffort}' — valid: ${effortValueGrammar("claude")}`)
|
|
283
|
+
throw new ExitError(2)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (supplied("--claude-advisor", ctx.claudeAdvisor)) {
|
|
287
|
+
if (!ctx.syncClaude) {
|
|
288
|
+
warn("--claude-advisor ignored: claude target not selected")
|
|
289
|
+
ctx.claudeAdvisor = ""
|
|
290
|
+
} else if (!CLAUDE_ADVISOR_STATES.some((state) => state === ctx.claudeAdvisor)) {
|
|
291
|
+
printCatalog(ctx, advisorCatalog())
|
|
292
|
+
err(`Invalid Claude advisor state '${ctx.claudeAdvisor}' — valid: ${advisorValueGrammar()}`)
|
|
293
|
+
throw new ExitError(2)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (supplied("--codex-model", ctx.codexModel)) {
|
|
216
297
|
if (!ctx.syncCodex) {
|
|
217
298
|
warn("--codex-model ignored: codex target not selected")
|
|
218
299
|
ctx.codexModel = ""
|
|
@@ -222,4 +303,14 @@ export function validateModelFlags(ctx: Ctx): void {
|
|
|
222
303
|
throw new ExitError(2)
|
|
223
304
|
}
|
|
224
305
|
}
|
|
306
|
+
if (supplied("--codex-effort", ctx.codexEffort)) {
|
|
307
|
+
if (!ctx.syncCodex) {
|
|
308
|
+
warn("--codex-effort ignored: codex target not selected")
|
|
309
|
+
ctx.codexEffort = ""
|
|
310
|
+
} else if (!isEffortModifierValue("codex", ctx.codexEffort)) {
|
|
311
|
+
printCatalog(ctx, effortCatalog("codex"))
|
|
312
|
+
err(`Invalid Codex effort '${ctx.codexEffort}' — valid: ${effortValueGrammar("codex")}`)
|
|
313
|
+
throw new ExitError(2)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
225
316
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function powerShellLiteral(value: string): string {
|
|
2
|
+
return `'${value.replaceAll("'", "''")}'`
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function encodePowerShellCommand(script: string): string {
|
|
6
|
+
return Buffer.from(script, "utf16le").toString("base64")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function decodePowerShellCommand(encoded: string): string {
|
|
10
|
+
return Buffer.from(encoded, "base64").toString("utf16le")
|
|
11
|
+
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { spawnSync } from "node:child_process"
|
|
8
8
|
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, realpathSync, rmdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs"
|
|
9
|
-
import { tmpdir } from "node:os"
|
|
10
9
|
import { p, writeFileIfChanged } from "./exec"
|
|
10
|
+
import { bunBootstrap } from "./bun"
|
|
11
11
|
import type { Ctx } from "./index"
|
|
12
12
|
import { compareCodepoints } from "./jq"
|
|
13
13
|
import type { EngineServices, Platform } from "./services"
|
|
@@ -277,39 +277,6 @@ function syncAgentBrowserCli(ctx: Ctx, manifest: string): void {
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
/** skills::_find_bun — resolved bun path or "". */
|
|
281
|
-
function findBun(ctx: Ctx): string {
|
|
282
|
-
return ctx.services.deps.path("bun")
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/** skills::_bun_bootstrap — bun path or "" after a failed bootstrap. */
|
|
286
|
-
export function bunBootstrap(ctx: Ctx, services: EngineServices): string {
|
|
287
|
-
const { change, warn } = services.logger
|
|
288
|
-
let bun = findBun(ctx)
|
|
289
|
-
if (bun !== "") return bun
|
|
290
|
-
|
|
291
|
-
if (services.deps.probe("curl").state === "missing") {
|
|
292
|
-
warn("Bun and curl both missing — cannot bootstrap Bun. Install Bun manually, then re-run sync.")
|
|
293
|
-
return ""
|
|
294
|
-
}
|
|
295
|
-
const pin = field(ctx, "bun", "verified")
|
|
296
|
-
warn(`Bun not found — installing Bun${pin !== "" ? ` ${pin} (kit-verified)` : ""}...`)
|
|
297
|
-
const installer = p(tmpdir(), `bun-install-${process.pid}.sh`)
|
|
298
|
-
const dl = spawnSync("curl", ["-fsSL", "https://bun.sh/install", "-o", installer], { stdio: "ignore" })
|
|
299
|
-
if (dl.error === undefined && dl.status === 0) {
|
|
300
|
-
spawnSync("bash", [installer, ...(pin !== "" ? [`bun-v${pin}`] : [])], { stdio: "ignore" })
|
|
301
|
-
}
|
|
302
|
-
rmSync(installer, { force: true })
|
|
303
|
-
bun = findBun(ctx)
|
|
304
|
-
if (bun === "") {
|
|
305
|
-
warn("Bun install failed. Install manually: curl -fsSL https://bun.sh/install -o /tmp/bun.sh && bash /tmp/bun.sh")
|
|
306
|
-
return ""
|
|
307
|
-
}
|
|
308
|
-
const version = services.deps.version("bun")
|
|
309
|
-
change(`Bun installed (${version !== "" ? version : "version unknown"})`)
|
|
310
|
-
return bun
|
|
311
|
-
}
|
|
312
|
-
|
|
313
280
|
/** skills::_effect_solutions_install. */
|
|
314
281
|
export function effectSolutionsInstall(
|
|
315
282
|
ctx: Ctx
|
|
@@ -319,8 +286,9 @@ export function effectSolutionsInstall(
|
|
|
319
286
|
const verb = mode === "upgrade" ? "Upgrading" : "Installing"
|
|
320
287
|
const pkg = `effect-solutions@${version !== "" ? version : "latest"}`
|
|
321
288
|
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
289
|
+
const bunState = bunBootstrap(ctx, services)
|
|
290
|
+
if (bunState.kind === "deferred") return 1
|
|
291
|
+
const bun = bunState.executable
|
|
324
292
|
|
|
325
293
|
verbose(`${verb} effect-solutions CLI via bun${version !== "" ? ` (pinned ${version})` : ""}...`)
|
|
326
294
|
if (spawnSync(bun, ["add", "-g", pkg], { stdio: "ignore" }).status !== 0) {
|