@getmonoceros/workbench 1.38.19 → 1.39.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/README.md +2 -2
- package/dist/bin.js +557 -198
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -479,6 +479,18 @@ function expandEnvRefs(env) {
|
|
|
479
479
|
}
|
|
480
480
|
return out;
|
|
481
481
|
}
|
|
482
|
+
function mergeEnvLayers(...layers) {
|
|
483
|
+
const out = {};
|
|
484
|
+
for (const layer of layers) {
|
|
485
|
+
for (const [key, value] of Object.entries(layer)) {
|
|
486
|
+
const incomingEmpty = value.trim() === "";
|
|
487
|
+
const haveNonEmpty = out[key] !== void 0 && out[key].trim() !== "";
|
|
488
|
+
if (incomingEmpty && haveNonEmpty) continue;
|
|
489
|
+
out[key] = value;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return out;
|
|
493
|
+
}
|
|
482
494
|
function interpolateServices(services, vars) {
|
|
483
495
|
const missing = [];
|
|
484
496
|
const resolved = services.map((svc) => {
|
|
@@ -1439,10 +1451,10 @@ function resolveRepoTokens(config, catalog, envVars) {
|
|
|
1439
1451
|
async function resolveContainerRepoTokens(name, home, catalog) {
|
|
1440
1452
|
const ymlPath = containerConfigPath(name, home);
|
|
1441
1453
|
const { config } = parseConfig(await fs4.readFile(ymlPath, "utf8"), ymlPath);
|
|
1442
|
-
const envVars =
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1454
|
+
const envVars = mergeEnvLayers(
|
|
1455
|
+
readEnvFile(globalEnvPath(home)),
|
|
1456
|
+
readEnvFile(containerEnvPath(name, home))
|
|
1457
|
+
);
|
|
1446
1458
|
return resolveRepoTokens(config, catalog, envVars);
|
|
1447
1459
|
}
|
|
1448
1460
|
function formatTokenUse(use) {
|
|
@@ -4452,6 +4464,9 @@ function generateAgentsMd(input) {
|
|
|
4452
4464
|
lines.push(sub ? ` ${sub}` : "");
|
|
4453
4465
|
}
|
|
4454
4466
|
}
|
|
4467
|
+
for (const mountLine of formatServiceMounts(svc)) {
|
|
4468
|
+
lines.push(mountLine);
|
|
4469
|
+
}
|
|
4455
4470
|
}
|
|
4456
4471
|
lines.push("");
|
|
4457
4472
|
const connEnv = serviceConnectionEnv(input.services);
|
|
@@ -4630,6 +4645,7 @@ function generateAgentsMd(input) {
|
|
|
4630
4645
|
);
|
|
4631
4646
|
lines.push("");
|
|
4632
4647
|
const examplePort = input.ports.length > 0 ? String(input.ports[0]) : "<port>";
|
|
4648
|
+
const secondPort = input.ports.length > 1 ? String(input.ports[1]) : "<port>";
|
|
4633
4649
|
lines.push("## Running a long-running server");
|
|
4634
4650
|
lines.push("");
|
|
4635
4651
|
lines.push(
|
|
@@ -4664,7 +4680,10 @@ function generateAgentsMd(input) {
|
|
|
4664
4680
|
lines.push("{");
|
|
4665
4681
|
lines.push(' "targets": [');
|
|
4666
4682
|
lines.push(
|
|
4667
|
-
` { "name": "
|
|
4683
|
+
` { "name": "api", "command": "<the API's start command>", "port": ${examplePort}, "default": true },`
|
|
4684
|
+
);
|
|
4685
|
+
lines.push(
|
|
4686
|
+
` { "name": "web", "command": "<the web start command>", "port": ${secondPort}, "default": true }`
|
|
4668
4687
|
);
|
|
4669
4688
|
lines.push(" ]");
|
|
4670
4689
|
lines.push("}");
|
|
@@ -4703,6 +4722,17 @@ function generateAgentsMd(input) {
|
|
|
4703
4722
|
"not started. Pass `--target <name>` to start or stop a single one."
|
|
4704
4723
|
);
|
|
4705
4724
|
lines.push("");
|
|
4725
|
+
lines.push(
|
|
4726
|
+
"When you add a server in a later session, revisit the existing",
|
|
4727
|
+
"`launch.json` instead of assuming its current `default` set is complete.",
|
|
4728
|
+
"If the new server belongs to the app that should come up together (a",
|
|
4729
|
+
"backend the frontend calls, a worker the app relies on), give it",
|
|
4730
|
+
'`"default": true` too and place its entry before whatever depends on it.',
|
|
4731
|
+
"A single pre-existing default entry does not mean later servers should",
|
|
4732
|
+
"stay non-default - most servers that make up the running app belong in",
|
|
4733
|
+
"the default set."
|
|
4734
|
+
);
|
|
4735
|
+
lines.push("");
|
|
4706
4736
|
lines.push(
|
|
4707
4737
|
"The server must listen on `0.0.0.0` (not `127.0.0.1`) on the exposed",
|
|
4708
4738
|
"port, or Traefik cannot reach it. You only have the ports already",
|
|
@@ -4740,6 +4770,42 @@ function formatServiceLine(svc) {
|
|
|
4740
4770
|
}
|
|
4741
4771
|
return `- **${svc.name}** (custom image \`${svc.image}\`) \u2014 reachable at \`${reach}\``;
|
|
4742
4772
|
}
|
|
4773
|
+
function formatServiceMounts(svc) {
|
|
4774
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
4775
|
+
for (const spec of svc.volumes) {
|
|
4776
|
+
const mount = parseBindMount(spec);
|
|
4777
|
+
if (!mount) continue;
|
|
4778
|
+
const project = projectOf(mount.source);
|
|
4779
|
+
if (!project) continue;
|
|
4780
|
+
const readOnly = mount.mode === "ro" ? " (read-only)" : "";
|
|
4781
|
+
const bucket = byProject.get(project) ?? [];
|
|
4782
|
+
bucket.push(` - \`${mount.source}\` \u2192 \`${mount.target}\`${readOnly}`);
|
|
4783
|
+
byProject.set(project, bucket);
|
|
4784
|
+
}
|
|
4785
|
+
if (byProject.size === 0) return [];
|
|
4786
|
+
const out = [" Workspace mounts (edit these on the host, then re-apply):"];
|
|
4787
|
+
for (const [project, entries] of byProject) {
|
|
4788
|
+
out.push(` - ${project}:`);
|
|
4789
|
+
out.push(...entries);
|
|
4790
|
+
}
|
|
4791
|
+
return out;
|
|
4792
|
+
}
|
|
4793
|
+
function parseBindMount(spec) {
|
|
4794
|
+
const parts = spec.split(":");
|
|
4795
|
+
if (parts.length < 2) return null;
|
|
4796
|
+
const source = parts[0];
|
|
4797
|
+
let mode;
|
|
4798
|
+
const last = parts[parts.length - 1];
|
|
4799
|
+
if (parts.length >= 3 && !last.includes("/")) {
|
|
4800
|
+
mode = parts.pop();
|
|
4801
|
+
}
|
|
4802
|
+
return { source, target: parts.slice(1).join(":"), mode };
|
|
4803
|
+
}
|
|
4804
|
+
function projectOf(source) {
|
|
4805
|
+
const segments = source.split("/");
|
|
4806
|
+
if (segments[0] !== "projects" || segments.length < 2) return null;
|
|
4807
|
+
return segments[1] ?? null;
|
|
4808
|
+
}
|
|
4743
4809
|
function agentsMdInputFromCreateOptions(opts, featureDisplayMap2, manifestLoader, hostPort = 80) {
|
|
4744
4810
|
const features = [];
|
|
4745
4811
|
for (const [ref, userOptions] of Object.entries(opts.features ?? {})) {
|
|
@@ -8355,11 +8421,13 @@ ${sectionLine(label)}
|
|
|
8355
8421
|
warnOnDeprecatedFeatureRefs(parsed.config.features, globalConfig, logger);
|
|
8356
8422
|
const envPath = containerEnvPath(opts.name, home);
|
|
8357
8423
|
await ensureEnvGitignored(containerConfigsDir(home));
|
|
8358
|
-
const envVars = expandEnvRefs(
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8424
|
+
const envVars = expandEnvRefs(
|
|
8425
|
+
mergeEnvLayers(
|
|
8426
|
+
readEnvFile(globalEnvPath(home)),
|
|
8427
|
+
readEnvFile(envPath),
|
|
8428
|
+
opts.env ?? {}
|
|
8429
|
+
)
|
|
8430
|
+
);
|
|
8363
8431
|
const catalog = await loadComponentCatalog();
|
|
8364
8432
|
const repoTokens = resolveRepoTokens(parsed.config, catalog, envVars);
|
|
8365
8433
|
const featureTokenPrompt = opts.featureTokenPrompt ?? defaultFeatureTokenPrompt;
|
|
@@ -9070,7 +9138,7 @@ var CLI_VERSION;
|
|
|
9070
9138
|
var init_version = __esm({
|
|
9071
9139
|
"src/version.ts"() {
|
|
9072
9140
|
"use strict";
|
|
9073
|
-
CLI_VERSION = true ? "1.
|
|
9141
|
+
CLI_VERSION = true ? "1.39.0" : "dev";
|
|
9074
9142
|
}
|
|
9075
9143
|
});
|
|
9076
9144
|
|
|
@@ -9145,164 +9213,6 @@ var init_apply2 = __esm({
|
|
|
9145
9213
|
}
|
|
9146
9214
|
});
|
|
9147
9215
|
|
|
9148
|
-
// src/commands/completion.ts
|
|
9149
|
-
import { defineCommand as defineCommand9 } from "citty";
|
|
9150
|
-
function renderCompletionScript(shell) {
|
|
9151
|
-
if (shell === "bash") {
|
|
9152
|
-
return [
|
|
9153
|
-
"# bash completion for monoceros",
|
|
9154
|
-
"# install: source this file from .bashrc, e.g.",
|
|
9155
|
-
"# monoceros completion bash > ~/.bash_completion.d/monoceros",
|
|
9156
|
-
'# echo "source ~/.bash_completion.d/monoceros" >> ~/.bashrc',
|
|
9157
|
-
"#",
|
|
9158
|
-
"# The work is done by `monoceros __complete --line --point`; this",
|
|
9159
|
-
"# shell wrapper only forwards the cursor view.",
|
|
9160
|
-
"",
|
|
9161
|
-
"_monoceros() {",
|
|
9162
|
-
" local IFS=$'\\n'",
|
|
9163
|
-
" local candidates",
|
|
9164
|
-
' candidates=$(monoceros __complete --line "$COMP_LINE" --point "$COMP_POINT" 2>/dev/null)',
|
|
9165
|
-
' local cur="${COMP_WORDS[COMP_CWORD]}"',
|
|
9166
|
-
' COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )',
|
|
9167
|
-
" # Suppress the trailing space when bash narrowed the candidate",
|
|
9168
|
-
" # set to a single token that ends with `=` \u2014 those are value-",
|
|
9169
|
-
" # flags (`--with-features=`, `--with-ports=`, \u2026) where the user types the",
|
|
9170
|
-
" # value immediately after.",
|
|
9171
|
-
' if [[ ${#COMPREPLY[@]} -eq 1 && "${COMPREPLY[0]}" == *= ]]; then',
|
|
9172
|
-
" compopt -o nospace",
|
|
9173
|
-
" fi",
|
|
9174
|
-
"}",
|
|
9175
|
-
"complete -F _monoceros monoceros",
|
|
9176
|
-
""
|
|
9177
|
-
].join("\n");
|
|
9178
|
-
}
|
|
9179
|
-
if (shell === "pwsh") {
|
|
9180
|
-
return [
|
|
9181
|
-
"# PowerShell completion for monoceros",
|
|
9182
|
-
"# install: dot-source this file from your $PROFILE, e.g.",
|
|
9183
|
-
"# monoceros completion pwsh > $HOME/.config/monoceros/completion.ps1",
|
|
9184
|
-
"# Add-Content $PROFILE '. $HOME/.config/monoceros/completion.ps1'",
|
|
9185
|
-
"#",
|
|
9186
|
-
"# The work is done by `monoceros __complete --line --point`; this",
|
|
9187
|
-
"# shell wrapper only forwards the cursor view.",
|
|
9188
|
-
"",
|
|
9189
|
-
"Register-ArgumentCompleter -Native -CommandName monoceros -ScriptBlock {",
|
|
9190
|
-
" param($wordToComplete, $commandAst, $cursorPosition)",
|
|
9191
|
-
" $line = $commandAst.Extent.Text",
|
|
9192
|
-
" $point = $cursorPosition - $commandAst.Extent.StartOffset",
|
|
9193
|
-
" if ($point -lt 0) { $point = 0 }",
|
|
9194
|
-
" $raw = & monoceros __complete --line $line --point $point 2>$null",
|
|
9195
|
-
" if (-not $raw) { return }",
|
|
9196
|
-
' $raw -split "`n" |',
|
|
9197
|
-
' Where-Object { $_.Length -gt 0 -and $_ -like "$wordToComplete*" } |',
|
|
9198
|
-
' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, "ParameterValue", $_) }',
|
|
9199
|
-
"}",
|
|
9200
|
-
""
|
|
9201
|
-
].join("\n");
|
|
9202
|
-
}
|
|
9203
|
-
return [
|
|
9204
|
-
"#compdef monoceros",
|
|
9205
|
-
"# zsh completion for monoceros",
|
|
9206
|
-
"# install: drop this file somewhere on your $fpath as `_monoceros`,",
|
|
9207
|
-
"# then start a new shell (or run `compinit`). Example:",
|
|
9208
|
-
'# monoceros completion zsh > "${fpath[1]}/_monoceros"',
|
|
9209
|
-
"#",
|
|
9210
|
-
"# The work is done by `monoceros __complete --line --point`; this",
|
|
9211
|
-
"# shell wrapper only forwards the cursor view.",
|
|
9212
|
-
"",
|
|
9213
|
-
"_monoceros() {",
|
|
9214
|
-
' local line="$BUFFER"',
|
|
9215
|
-
' local point="$CURSOR"',
|
|
9216
|
-
" local -a candidates with_eq without_eq",
|
|
9217
|
-
' candidates=("${(@f)$(monoceros __complete --line "$line" --point "$point" 2>/dev/null)}")',
|
|
9218
|
-
' candidates=("${(@)candidates:#}")',
|
|
9219
|
-
' # Split candidates into "ends with `=`" (value-flags \u2014 no suffix',
|
|
9220
|
-
" # space wanted because the user types the value immediately) and",
|
|
9221
|
-
' # "everything else" (positional values, boolean flags \u2014 default',
|
|
9222
|
-
" # space behaviour).",
|
|
9223
|
-
' for cand in "${candidates[@]}"; do',
|
|
9224
|
-
' if [[ "$cand" == *= ]]; then',
|
|
9225
|
-
' with_eq+=("$cand")',
|
|
9226
|
-
" else",
|
|
9227
|
-
' without_eq+=("$cand")',
|
|
9228
|
-
" fi",
|
|
9229
|
-
" done",
|
|
9230
|
-
" (( ${#with_eq[@]} )) && compadd -S '' -- \"${with_eq[@]}\"",
|
|
9231
|
-
' (( ${#without_eq[@]} )) && compadd -- "${without_eq[@]}"',
|
|
9232
|
-
"}",
|
|
9233
|
-
"",
|
|
9234
|
-
'_monoceros "$@"',
|
|
9235
|
-
""
|
|
9236
|
-
].join("\n");
|
|
9237
|
-
}
|
|
9238
|
-
var SHELLS, completionCommand;
|
|
9239
|
-
var init_completion = __esm({
|
|
9240
|
-
"src/commands/completion.ts"() {
|
|
9241
|
-
"use strict";
|
|
9242
|
-
SHELLS = ["bash", "zsh", "pwsh"];
|
|
9243
|
-
completionCommand = defineCommand9({
|
|
9244
|
-
meta: {
|
|
9245
|
-
name: "completion",
|
|
9246
|
-
group: "tooling",
|
|
9247
|
-
// Hidden from `monoceros --help`: the install scripts wire up
|
|
9248
|
-
// completion automatically; manual setup is documented at
|
|
9249
|
-
// getmonoceros.build/docs/reference/utilities/completion. Still runnable directly.
|
|
9250
|
-
hidden: true,
|
|
9251
|
-
description: "Print a shell completion script for bash, zsh or PowerShell to stdout. Pipe the output into a file your shell loads at startup. The install scripts (install.sh / install.ps1) call this automatically."
|
|
9252
|
-
},
|
|
9253
|
-
args: {
|
|
9254
|
-
shell: {
|
|
9255
|
-
type: "positional",
|
|
9256
|
-
description: "Target shell. One of: 'bash', 'zsh', 'pwsh'.",
|
|
9257
|
-
required: true
|
|
9258
|
-
}
|
|
9259
|
-
},
|
|
9260
|
-
run({ args }) {
|
|
9261
|
-
const shell = args.shell;
|
|
9262
|
-
if (shell !== "bash" && shell !== "zsh" && shell !== "pwsh") {
|
|
9263
|
-
process.stderr.write(
|
|
9264
|
-
`Unknown shell: ${JSON.stringify(shell)}. Supported: ${SHELLS.join(", ")}.
|
|
9265
|
-
`
|
|
9266
|
-
);
|
|
9267
|
-
process.exit(2);
|
|
9268
|
-
}
|
|
9269
|
-
process.stdout.write(renderCompletionScript(shell));
|
|
9270
|
-
}
|
|
9271
|
-
});
|
|
9272
|
-
}
|
|
9273
|
-
});
|
|
9274
|
-
|
|
9275
|
-
// src/commands/__bridge.ts
|
|
9276
|
-
import { defineCommand as defineCommand10 } from "citty";
|
|
9277
|
-
var __bridgeCommand;
|
|
9278
|
-
var init_bridge = __esm({
|
|
9279
|
-
"src/commands/__bridge.ts"() {
|
|
9280
|
-
"use strict";
|
|
9281
|
-
init_bridge_daemon();
|
|
9282
|
-
__bridgeCommand = defineCommand10({
|
|
9283
|
-
meta: {
|
|
9284
|
-
name: "__bridge",
|
|
9285
|
-
group: "internal",
|
|
9286
|
-
hidden: true,
|
|
9287
|
-
description: "Internal: host-side browser-bridge daemon (background)."
|
|
9288
|
-
},
|
|
9289
|
-
args: {
|
|
9290
|
-
root: {
|
|
9291
|
-
type: "positional",
|
|
9292
|
-
description: "Materialized container root ($MONOCEROS_HOME/container/<name>/).",
|
|
9293
|
-
required: true
|
|
9294
|
-
}
|
|
9295
|
-
},
|
|
9296
|
-
async run({ args }) {
|
|
9297
|
-
try {
|
|
9298
|
-
await runBridgeDaemon({ root: args.root });
|
|
9299
|
-
} catch {
|
|
9300
|
-
}
|
|
9301
|
-
}
|
|
9302
|
-
});
|
|
9303
|
-
}
|
|
9304
|
-
});
|
|
9305
|
-
|
|
9306
9216
|
// src/config/launch-config.ts
|
|
9307
9217
|
import { promises as fs16 } from "fs";
|
|
9308
9218
|
import path25 from "path";
|
|
@@ -9501,6 +9411,14 @@ function tokenize(text) {
|
|
|
9501
9411
|
function isShellWhitespace(ch) {
|
|
9502
9412
|
return ch === " " || ch === " ";
|
|
9503
9413
|
}
|
|
9414
|
+
function staticSource(fn) {
|
|
9415
|
+
pwshMeta.set(fn, { static: true });
|
|
9416
|
+
return fn;
|
|
9417
|
+
}
|
|
9418
|
+
function dynamicSource(kind, fn) {
|
|
9419
|
+
pwshMeta.set(fn, { static: false, kind });
|
|
9420
|
+
return fn;
|
|
9421
|
+
}
|
|
9504
9422
|
function dispatchCommand(spec, argTokens, ctx) {
|
|
9505
9423
|
const dashDashIdx = argTokens.indexOf("--");
|
|
9506
9424
|
const preDash = dashDashIdx < 0 ? argTokens : argTokens.slice(0, dashDashIdx);
|
|
@@ -9747,7 +9665,36 @@ async function resolveFeatureRefForCompletion(token) {
|
|
|
9747
9665
|
const f = c.file.contributes.features?.[0];
|
|
9748
9666
|
return f?.ref;
|
|
9749
9667
|
}
|
|
9750
|
-
|
|
9668
|
+
async function describeSource(src) {
|
|
9669
|
+
if (!src) return {};
|
|
9670
|
+
const meta = pwshMeta.get(src);
|
|
9671
|
+
if (!meta) return {};
|
|
9672
|
+
if (meta.static) return { values: await src(EMPTY_CTX) };
|
|
9673
|
+
return { kind: meta.kind };
|
|
9674
|
+
}
|
|
9675
|
+
async function buildPwshCompletionModel() {
|
|
9676
|
+
const specs = {};
|
|
9677
|
+
for (const [name, spec] of Object.entries(COMMAND_SPECS)) {
|
|
9678
|
+
const posSources = spec.positionals ?? [];
|
|
9679
|
+
const positionalCount = spec.positionalCount ?? posSources.length;
|
|
9680
|
+
const positionals = [];
|
|
9681
|
+
for (const src of posSources) positionals.push(await describeSource(src));
|
|
9682
|
+
const flags = {};
|
|
9683
|
+
for (const [flag, fspec] of Object.entries(spec.flags ?? {})) {
|
|
9684
|
+
const entry2 = {
|
|
9685
|
+
type: fspec.type,
|
|
9686
|
+
aliases: fspec.aliases ?? []
|
|
9687
|
+
};
|
|
9688
|
+
if (fspec.type === "value" && fspec.values) {
|
|
9689
|
+
entry2.value = await describeSource(fspec.values);
|
|
9690
|
+
}
|
|
9691
|
+
flags[flag] = entry2;
|
|
9692
|
+
}
|
|
9693
|
+
specs[name] = { positionals, positionalCount, flags };
|
|
9694
|
+
}
|
|
9695
|
+
return { commands: [...ALL_COMMANDS], specs };
|
|
9696
|
+
}
|
|
9697
|
+
var pwshMeta, WORKSPACE_DIR_SKIP, PROJECTS_DIR_MAX_DEPTH, ALL_COMMANDS, containerName, appCandidates, appOrServiceCandidates, targetCandidates, runInDirs, languageValues, serviceValues, featureValues, providerValues, shellValues, openToolValues, COMMAND_SPECS, COMPLETION_COMMAND_SPEC_KEYS, EMPTY_CTX;
|
|
9751
9698
|
var init_resolve = __esm({
|
|
9752
9699
|
"src/completion/resolve.ts"() {
|
|
9753
9700
|
"use strict";
|
|
@@ -9760,6 +9707,7 @@ var init_resolve = __esm({
|
|
|
9760
9707
|
init_catalog();
|
|
9761
9708
|
init_schema();
|
|
9762
9709
|
init_open();
|
|
9710
|
+
pwshMeta = /* @__PURE__ */ new WeakMap();
|
|
9763
9711
|
WORKSPACE_DIR_SKIP = /* @__PURE__ */ new Set(["node_modules"]);
|
|
9764
9712
|
PROJECTS_DIR_MAX_DEPTH = 4;
|
|
9765
9713
|
ALL_COMMANDS = [
|
|
@@ -9796,7 +9744,26 @@ var init_resolve = __esm({
|
|
|
9796
9744
|
"share",
|
|
9797
9745
|
"completion"
|
|
9798
9746
|
];
|
|
9799
|
-
containerName = (
|
|
9747
|
+
containerName = dynamicSource(
|
|
9748
|
+
"containerName",
|
|
9749
|
+
(ctx) => listContainerNames(ctx)
|
|
9750
|
+
);
|
|
9751
|
+
appCandidates = dynamicSource("app", (ctx) => listAppCandidates(ctx));
|
|
9752
|
+
appOrServiceCandidates = dynamicSource(
|
|
9753
|
+
"appOrService",
|
|
9754
|
+
(ctx) => listAppOrServiceCandidates(ctx)
|
|
9755
|
+
);
|
|
9756
|
+
targetCandidates = dynamicSource(
|
|
9757
|
+
"target",
|
|
9758
|
+
(ctx) => listTargetCandidates(ctx)
|
|
9759
|
+
);
|
|
9760
|
+
runInDirs = dynamicSource("runInDir", (ctx) => listRunInDirs(ctx));
|
|
9761
|
+
languageValues = staticSource(() => listLanguageNames());
|
|
9762
|
+
serviceValues = staticSource(() => listServiceNames());
|
|
9763
|
+
featureValues = staticSource(() => listFeatureComponents());
|
|
9764
|
+
providerValues = staticSource(() => listProviders());
|
|
9765
|
+
shellValues = staticSource(() => listShellNames());
|
|
9766
|
+
openToolValues = staticSource(() => [...OPEN_TOOLS]);
|
|
9800
9767
|
COMMAND_SPECS = {
|
|
9801
9768
|
init: {
|
|
9802
9769
|
// First positional is a FRESH name → no suggestion source, but
|
|
@@ -9805,12 +9772,9 @@ var init_resolve = __esm({
|
|
|
9805
9772
|
// flag suggestions.
|
|
9806
9773
|
positionalCount: 1,
|
|
9807
9774
|
flags: {
|
|
9808
|
-
"--with-languages": { type: "value", values:
|
|
9809
|
-
"--with-features": {
|
|
9810
|
-
|
|
9811
|
-
values: () => listFeatureComponents()
|
|
9812
|
-
},
|
|
9813
|
-
"--with-services": { type: "value", values: () => listServiceNames() },
|
|
9775
|
+
"--with-languages": { type: "value", values: languageValues },
|
|
9776
|
+
"--with-features": { type: "value", values: featureValues },
|
|
9777
|
+
"--with-services": { type: "value", values: serviceValues },
|
|
9814
9778
|
"--with-apt-packages": { type: "value" },
|
|
9815
9779
|
"--with-repos": { type: "value" },
|
|
9816
9780
|
"--with-ports": { type: "value" }
|
|
@@ -9820,7 +9784,7 @@ var init_resolve = __esm({
|
|
|
9820
9784
|
positionals: [containerName],
|
|
9821
9785
|
flags: {
|
|
9822
9786
|
"--yes": { type: "boolean", aliases: ["-y"] },
|
|
9823
|
-
"--open": { type: "value", values:
|
|
9787
|
+
"--open": { type: "value", values: openToolValues }
|
|
9824
9788
|
}
|
|
9825
9789
|
},
|
|
9826
9790
|
upgrade: {
|
|
@@ -9837,39 +9801,39 @@ var init_resolve = __esm({
|
|
|
9837
9801
|
}
|
|
9838
9802
|
},
|
|
9839
9803
|
shell: { positionals: [containerName] },
|
|
9840
|
-
open: { positionals: [containerName,
|
|
9804
|
+
open: { positionals: [containerName, openToolValues] },
|
|
9841
9805
|
run: {
|
|
9842
9806
|
positionals: [containerName],
|
|
9843
|
-
flags: { "--in": { type: "value", values:
|
|
9807
|
+
flags: { "--in": { type: "value", values: runInDirs } }
|
|
9844
9808
|
},
|
|
9845
9809
|
logs: {
|
|
9846
|
-
positionals: [containerName,
|
|
9810
|
+
positionals: [containerName, appCandidates],
|
|
9847
9811
|
flags: {
|
|
9848
|
-
"--target": { type: "value", values:
|
|
9812
|
+
"--target": { type: "value", values: targetCandidates }
|
|
9849
9813
|
}
|
|
9850
9814
|
},
|
|
9851
9815
|
start: {
|
|
9852
|
-
positionals: [containerName,
|
|
9816
|
+
positionals: [containerName, appCandidates],
|
|
9853
9817
|
flags: {
|
|
9854
|
-
"--target": { type: "value", values:
|
|
9855
|
-
"--open": { type: "value", values:
|
|
9818
|
+
"--target": { type: "value", values: targetCandidates },
|
|
9819
|
+
"--open": { type: "value", values: openToolValues }
|
|
9856
9820
|
}
|
|
9857
9821
|
},
|
|
9858
9822
|
stop: {
|
|
9859
|
-
positionals: [containerName,
|
|
9823
|
+
positionals: [containerName, appCandidates],
|
|
9860
9824
|
flags: {
|
|
9861
|
-
"--target": { type: "value", values:
|
|
9825
|
+
"--target": { type: "value", values: targetCandidates }
|
|
9862
9826
|
}
|
|
9863
9827
|
},
|
|
9864
9828
|
status: {
|
|
9865
|
-
positionals: [containerName,
|
|
9829
|
+
positionals: [containerName, appOrServiceCandidates]
|
|
9866
9830
|
},
|
|
9867
9831
|
"list-apps": { positionals: [containerName] },
|
|
9868
9832
|
"add-language": {
|
|
9869
|
-
positionals: [containerName,
|
|
9833
|
+
positionals: [containerName, languageValues]
|
|
9870
9834
|
},
|
|
9871
9835
|
"add-service": {
|
|
9872
|
-
positionals: [containerName,
|
|
9836
|
+
positionals: [containerName, serviceValues]
|
|
9873
9837
|
},
|
|
9874
9838
|
"add-apt-packages": {
|
|
9875
9839
|
positionals: [containerName],
|
|
@@ -9877,7 +9841,7 @@ var init_resolve = __esm({
|
|
|
9877
9841
|
// freeform package names — no useful suggestion list
|
|
9878
9842
|
},
|
|
9879
9843
|
"add-feature": {
|
|
9880
|
-
positionals: [containerName,
|
|
9844
|
+
positionals: [containerName, featureValues],
|
|
9881
9845
|
flags: { "--yes": { type: "boolean", aliases: ["-y"] } },
|
|
9882
9846
|
innerArgs: (ctx) => listFeatureOptionInnerArgs(ctx)
|
|
9883
9847
|
},
|
|
@@ -9888,7 +9852,7 @@ var init_resolve = __esm({
|
|
|
9888
9852
|
"--path": { type: "value" },
|
|
9889
9853
|
"--git-name": { type: "value" },
|
|
9890
9854
|
"--git-email": { type: "value" },
|
|
9891
|
-
"--provider": { type: "value", values:
|
|
9855
|
+
"--provider": { type: "value", values: providerValues },
|
|
9892
9856
|
"--yes": { type: "boolean", aliases: ["-y"] }
|
|
9893
9857
|
}
|
|
9894
9858
|
},
|
|
@@ -9901,17 +9865,17 @@ var init_resolve = __esm({
|
|
|
9901
9865
|
innerArgs: () => []
|
|
9902
9866
|
},
|
|
9903
9867
|
"remove-language": {
|
|
9904
|
-
positionals: [containerName,
|
|
9868
|
+
positionals: [containerName, languageValues]
|
|
9905
9869
|
},
|
|
9906
9870
|
"remove-service": {
|
|
9907
|
-
positionals: [containerName,
|
|
9871
|
+
positionals: [containerName, serviceValues]
|
|
9908
9872
|
},
|
|
9909
9873
|
"remove-apt-packages": {
|
|
9910
9874
|
positionals: [containerName],
|
|
9911
9875
|
innerArgs: () => []
|
|
9912
9876
|
},
|
|
9913
9877
|
"remove-feature": {
|
|
9914
|
-
positionals: [containerName,
|
|
9878
|
+
positionals: [containerName, featureValues],
|
|
9915
9879
|
flags: { "--yes": { type: "boolean", aliases: ["-y"] } }
|
|
9916
9880
|
},
|
|
9917
9881
|
"remove-from-url": { positionals: [containerName] },
|
|
@@ -9927,17 +9891,17 @@ var init_resolve = __esm({
|
|
|
9927
9891
|
innerArgs: () => []
|
|
9928
9892
|
},
|
|
9929
9893
|
tunnel: {
|
|
9930
|
-
positionals: [containerName,
|
|
9894
|
+
positionals: [containerName, serviceValues],
|
|
9931
9895
|
flags: {
|
|
9932
9896
|
"--local-port": { type: "value" },
|
|
9933
9897
|
"--local-address": { type: "value" }
|
|
9934
9898
|
}
|
|
9935
9899
|
},
|
|
9936
9900
|
share: {
|
|
9937
|
-
positionals: [containerName,
|
|
9901
|
+
positionals: [containerName, appCandidates]
|
|
9938
9902
|
},
|
|
9939
9903
|
completion: {
|
|
9940
|
-
positionals: [
|
|
9904
|
+
positionals: [shellValues]
|
|
9941
9905
|
},
|
|
9942
9906
|
"list-components": {},
|
|
9943
9907
|
restore: {
|
|
@@ -9948,6 +9912,401 @@ var init_resolve = __esm({
|
|
|
9948
9912
|
}
|
|
9949
9913
|
};
|
|
9950
9914
|
COMPLETION_COMMAND_SPEC_KEYS = Object.keys(COMMAND_SPECS);
|
|
9915
|
+
EMPTY_CTX = { prev: [], current: "", opts: {} };
|
|
9916
|
+
}
|
|
9917
|
+
});
|
|
9918
|
+
|
|
9919
|
+
// src/commands/completion.ts
|
|
9920
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
9921
|
+
function renderBashScript() {
|
|
9922
|
+
return [
|
|
9923
|
+
"# bash completion for monoceros",
|
|
9924
|
+
"# install: source this file from .bashrc, e.g.",
|
|
9925
|
+
"# monoceros completion bash > ~/.bash_completion.d/monoceros",
|
|
9926
|
+
'# echo "source ~/.bash_completion.d/monoceros" >> ~/.bashrc',
|
|
9927
|
+
"#",
|
|
9928
|
+
"# The work is done by `monoceros __complete --line --point`; this",
|
|
9929
|
+
"# shell wrapper only forwards the cursor view.",
|
|
9930
|
+
"",
|
|
9931
|
+
"_monoceros() {",
|
|
9932
|
+
" local IFS=$'\\n'",
|
|
9933
|
+
" local candidates",
|
|
9934
|
+
' candidates=$(monoceros __complete --line "$COMP_LINE" --point "$COMP_POINT" 2>/dev/null)',
|
|
9935
|
+
' local cur="${COMP_WORDS[COMP_CWORD]}"',
|
|
9936
|
+
' COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )',
|
|
9937
|
+
" # Suppress the trailing space when bash narrowed the candidate",
|
|
9938
|
+
" # set to a single token that ends with `=` \u2014 those are value-",
|
|
9939
|
+
" # flags (`--with-features=`, `--with-ports=`, \u2026) where the user types the",
|
|
9940
|
+
" # value immediately after.",
|
|
9941
|
+
' if [[ ${#COMPREPLY[@]} -eq 1 && "${COMPREPLY[0]}" == *= ]]; then',
|
|
9942
|
+
" compopt -o nospace",
|
|
9943
|
+
" fi",
|
|
9944
|
+
"}",
|
|
9945
|
+
"complete -F _monoceros monoceros",
|
|
9946
|
+
""
|
|
9947
|
+
].join("\n");
|
|
9948
|
+
}
|
|
9949
|
+
function renderZshScript() {
|
|
9950
|
+
return [
|
|
9951
|
+
"#compdef monoceros",
|
|
9952
|
+
"# zsh completion for monoceros",
|
|
9953
|
+
"# install: drop this file somewhere on your $fpath as `_monoceros`,",
|
|
9954
|
+
"# then start a new shell (or run `compinit`). Example:",
|
|
9955
|
+
'# monoceros completion zsh > "${fpath[1]}/_monoceros"',
|
|
9956
|
+
"#",
|
|
9957
|
+
"# The work is done by `monoceros __complete --line --point`; this",
|
|
9958
|
+
"# shell wrapper only forwards the cursor view.",
|
|
9959
|
+
"",
|
|
9960
|
+
"_monoceros() {",
|
|
9961
|
+
' local line="$BUFFER"',
|
|
9962
|
+
' local point="$CURSOR"',
|
|
9963
|
+
" local -a candidates with_eq without_eq",
|
|
9964
|
+
' candidates=("${(@f)$(monoceros __complete --line "$line" --point "$point" 2>/dev/null)}")',
|
|
9965
|
+
' candidates=("${(@)candidates:#}")',
|
|
9966
|
+
' # Split candidates into "ends with `=`" (value-flags \u2014 no suffix',
|
|
9967
|
+
" # space wanted because the user types the value immediately) and",
|
|
9968
|
+
' # "everything else" (positional values, boolean flags \u2014 default',
|
|
9969
|
+
" # space behaviour).",
|
|
9970
|
+
' for cand in "${candidates[@]}"; do',
|
|
9971
|
+
' if [[ "$cand" == *= ]]; then',
|
|
9972
|
+
' with_eq+=("$cand")',
|
|
9973
|
+
" else",
|
|
9974
|
+
' without_eq+=("$cand")',
|
|
9975
|
+
" fi",
|
|
9976
|
+
" done",
|
|
9977
|
+
" (( ${#with_eq[@]} )) && compadd -S '' -- \"${with_eq[@]}\"",
|
|
9978
|
+
' (( ${#without_eq[@]} )) && compadd -- "${without_eq[@]}"',
|
|
9979
|
+
"}",
|
|
9980
|
+
"",
|
|
9981
|
+
'_monoceros "$@"',
|
|
9982
|
+
""
|
|
9983
|
+
].join("\n");
|
|
9984
|
+
}
|
|
9985
|
+
async function renderPwshScript() {
|
|
9986
|
+
const model = await buildPwshCompletionModel();
|
|
9987
|
+
const json = JSON.stringify(model);
|
|
9988
|
+
return `# PowerShell completion for monoceros (self-contained).
|
|
9989
|
+
# install: dot-source this file from your $PROFILE, e.g.
|
|
9990
|
+
# monoceros completion pwsh > $HOME/.config/monoceros/completion.ps1
|
|
9991
|
+
# Add-Content $PROFILE '. $HOME/.config/monoceros/completion.ps1'
|
|
9992
|
+
#
|
|
9993
|
+
# Unlike the bash/zsh wrappers this never calls back into the CLI per
|
|
9994
|
+
# Tab (on Windows that would be a WSL round-trip each keystroke).
|
|
9995
|
+
# The static candidates are baked in below; the dynamic ones (container
|
|
9996
|
+
# names, apps, workspace dirs, launch targets) are read straight off the
|
|
9997
|
+
# host filesystem via the %USERPROFILE%\\.monoceros symlink.
|
|
9998
|
+
|
|
9999
|
+
$global:MonocerosModel = @'
|
|
10000
|
+
${json}
|
|
10001
|
+
'@ | ConvertFrom-Json
|
|
10002
|
+
|
|
10003
|
+
function __Monoceros_Home {
|
|
10004
|
+
if ($env:MONOCEROS_HOME) { return $env:MONOCEROS_HOME }
|
|
10005
|
+
return (Join-Path $env:USERPROFILE '.monoceros')
|
|
10006
|
+
}
|
|
10007
|
+
|
|
10008
|
+
function __Monoceros_ContainerNames {
|
|
10009
|
+
$dir = Join-Path (__Monoceros_Home) 'container-configs'
|
|
10010
|
+
if (-not (Test-Path -LiteralPath $dir)) { return @() }
|
|
10011
|
+
@(Get-ChildItem -LiteralPath $dir -Filter '*.yml' -File -ErrorAction SilentlyContinue |
|
|
10012
|
+
ForEach-Object { $_.BaseName } | Sort-Object)
|
|
10013
|
+
}
|
|
10014
|
+
|
|
10015
|
+
# Recursively collect directory paths under $at, relative to it, capped
|
|
10016
|
+
# at $maxDepth. Skips dot-dirs, node_modules, and symlinked dirs \u2014 the
|
|
10017
|
+
# host-side mirror of collectDirs() in completion/resolve.ts.
|
|
10018
|
+
function __Monoceros_CollectDirs($maxDepth, $acc, $at, $rel, $depth) {
|
|
10019
|
+
$entries = Get-ChildItem -LiteralPath $at -Directory -Force -ErrorAction SilentlyContinue
|
|
10020
|
+
foreach ($e in $entries) {
|
|
10021
|
+
if ($e.Name.StartsWith('.')) { continue }
|
|
10022
|
+
if ($e.Name -eq 'node_modules') { continue }
|
|
10023
|
+
if ($e.LinkType) { continue }
|
|
10024
|
+
$childRel = if ($rel) { "$rel/$($e.Name)" } else { $e.Name }
|
|
10025
|
+
[void]$acc.Add($childRel)
|
|
10026
|
+
if (($depth + 1) -lt $maxDepth) {
|
|
10027
|
+
__Monoceros_CollectDirs $maxDepth $acc $e.FullName $childRel ($depth + 1)
|
|
10028
|
+
}
|
|
10029
|
+
}
|
|
10030
|
+
}
|
|
10031
|
+
|
|
10032
|
+
function __Monoceros_WorkspaceDirs($name) {
|
|
10033
|
+
if (-not $name) { return @() }
|
|
10034
|
+
$root = Join-Path (Join-Path (__Monoceros_Home) 'container') $name
|
|
10035
|
+
if (-not (Test-Path -LiteralPath $root)) { return @() }
|
|
10036
|
+
$acc = New-Object System.Collections.Generic.List[string]
|
|
10037
|
+
__Monoceros_CollectDirs 1 $acc $root '' 0
|
|
10038
|
+
$projects = Join-Path $root 'projects'
|
|
10039
|
+
if (Test-Path -LiteralPath $projects) {
|
|
10040
|
+
$pacc = New-Object System.Collections.Generic.List[string]
|
|
10041
|
+
__Monoceros_CollectDirs 4 $pacc $projects '' 0
|
|
10042
|
+
foreach ($p in $pacc) { [void]$acc.Add("projects/$p") }
|
|
10043
|
+
}
|
|
10044
|
+
@($acc | Sort-Object -Unique)
|
|
10045
|
+
}
|
|
10046
|
+
|
|
10047
|
+
# App-relative paths under projects/ carrying .monoceros/launch.json.
|
|
10048
|
+
function __Monoceros_WalkApps($at, $rel, $depth, $acc) {
|
|
10049
|
+
if ($rel -and (Test-Path -LiteralPath (Join-Path (Join-Path $at '.monoceros') 'launch.json'))) {
|
|
10050
|
+
[void]$acc.Add($rel)
|
|
10051
|
+
}
|
|
10052
|
+
if ($depth -ge 4) { return }
|
|
10053
|
+
$entries = Get-ChildItem -LiteralPath $at -Directory -Force -ErrorAction SilentlyContinue
|
|
10054
|
+
foreach ($e in $entries) {
|
|
10055
|
+
if ($e.Name.StartsWith('.')) { continue }
|
|
10056
|
+
$childRel = if ($rel) { "$rel/$($e.Name)" } else { $e.Name }
|
|
10057
|
+
__Monoceros_WalkApps $e.FullName $childRel ($depth + 1) $acc
|
|
10058
|
+
}
|
|
10059
|
+
}
|
|
10060
|
+
|
|
10061
|
+
function __Monoceros_Apps($name) {
|
|
10062
|
+
if (-not $name) { return @() }
|
|
10063
|
+
$root = Join-Path (Join-Path (Join-Path (__Monoceros_Home) 'container') $name) 'projects'
|
|
10064
|
+
if (-not (Test-Path -LiteralPath $root)) { return @() }
|
|
10065
|
+
$acc = New-Object System.Collections.Generic.List[string]
|
|
10066
|
+
__Monoceros_WalkApps $root '' 0 $acc
|
|
10067
|
+
@($acc | Sort-Object)
|
|
10068
|
+
}
|
|
10069
|
+
|
|
10070
|
+
function __Monoceros_Targets($name, $app) {
|
|
10071
|
+
if (-not $name -or -not $app) { return @() }
|
|
10072
|
+
$appPath = $app -replace '/', '\\'
|
|
10073
|
+
$file = Join-Path (Join-Path (Join-Path (Join-Path (__Monoceros_Home) 'container') $name) 'projects') (Join-Path $appPath (Join-Path '.monoceros' 'launch.json'))
|
|
10074
|
+
if (-not (Test-Path -LiteralPath $file)) { return @() }
|
|
10075
|
+
try { $json = Get-Content -LiteralPath $file -Raw | ConvertFrom-Json } catch { return @() }
|
|
10076
|
+
$list = if ($json.targets) { $json.targets } elseif ($json.configurations) { $json.configurations } else { $null }
|
|
10077
|
+
if (-not $list) { return @() }
|
|
10078
|
+
@($list | ForEach-Object { $_.name } | Where-Object { $_ })
|
|
10079
|
+
}
|
|
10080
|
+
|
|
10081
|
+
# The positional tokens (skipping flags) after \`monoceros <cmd>\`.
|
|
10082
|
+
function __Monoceros_Positionals($argTokens) {
|
|
10083
|
+
$out = @()
|
|
10084
|
+
foreach ($t in $argTokens) {
|
|
10085
|
+
if ($t -eq '--') { break }
|
|
10086
|
+
if ($t.StartsWith('-')) { continue }
|
|
10087
|
+
$out += $t
|
|
10088
|
+
}
|
|
10089
|
+
,$out
|
|
10090
|
+
}
|
|
10091
|
+
|
|
10092
|
+
# Resolve a value descriptor ({values:[...]} static, or {kind:...}
|
|
10093
|
+
# dynamic) to its candidate list.
|
|
10094
|
+
function __Monoceros_Values($desc, $argTokens) {
|
|
10095
|
+
if (-not $desc) { return @() }
|
|
10096
|
+
if ($null -ne $desc.values) { return @($desc.values) }
|
|
10097
|
+
if ($desc.kind) {
|
|
10098
|
+
$pos = __Monoceros_Positionals $argTokens
|
|
10099
|
+
$name = if ($pos.Count -gt 0) { $pos[0] } else { $null }
|
|
10100
|
+
$app = if ($pos.Count -gt 1) { $pos[1] } else { $null }
|
|
10101
|
+
switch ($desc.kind) {
|
|
10102
|
+
'containerName' { return (__Monoceros_ContainerNames) }
|
|
10103
|
+
'app' { return (__Monoceros_Apps $name) }
|
|
10104
|
+
'appOrService' { return (__Monoceros_Apps $name) }
|
|
10105
|
+
'runInDir' { return (__Monoceros_WorkspaceDirs $name) }
|
|
10106
|
+
'target' { return (__Monoceros_Targets $name $app) }
|
|
10107
|
+
}
|
|
10108
|
+
}
|
|
10109
|
+
return @()
|
|
10110
|
+
}
|
|
10111
|
+
|
|
10112
|
+
# Comma-aware prefix filter \u2014 mirrors resolveValues() so
|
|
10113
|
+
# \`--with-features=a,b<TAB>\` completes after the last comma.
|
|
10114
|
+
function __Monoceros_Filter($values, $fragment) {
|
|
10115
|
+
if (-not $values) { return @() }
|
|
10116
|
+
$commaIdx = $fragment.LastIndexOf(',')
|
|
10117
|
+
if ($commaIdx -lt 0) {
|
|
10118
|
+
return @($values | Where-Object { $_.StartsWith($fragment) })
|
|
10119
|
+
}
|
|
10120
|
+
$prefix = $fragment.Substring(0, $commaIdx + 1)
|
|
10121
|
+
$tail = $fragment.Substring($commaIdx + 1)
|
|
10122
|
+
@($values | Where-Object { $_.StartsWith($tail) } | ForEach-Object { "$prefix$_" })
|
|
10123
|
+
}
|
|
10124
|
+
|
|
10125
|
+
function __Monoceros_LookupFlag($flags, $token) {
|
|
10126
|
+
if (-not $flags) { return $null }
|
|
10127
|
+
$direct = $flags.PSObject.Properties[$token]
|
|
10128
|
+
if ($direct) { return $direct.Value }
|
|
10129
|
+
foreach ($p in $flags.PSObject.Properties) {
|
|
10130
|
+
if ($p.Value.aliases -contains $token) { return $p.Value }
|
|
10131
|
+
}
|
|
10132
|
+
return $null
|
|
10133
|
+
}
|
|
10134
|
+
|
|
10135
|
+
# Value flags are offered with a trailing '=' (so no space is wanted
|
|
10136
|
+
# after Tab); boolean flags and aliases as bare names.
|
|
10137
|
+
function __Monoceros_FlagNames($flags, $fragment) {
|
|
10138
|
+
if (-not $flags) { return @() }
|
|
10139
|
+
$names = @()
|
|
10140
|
+
foreach ($p in $flags.PSObject.Properties) {
|
|
10141
|
+
if ($p.Value.type -eq 'value') { $names += "$($p.Name)=" } else { $names += $p.Name }
|
|
10142
|
+
foreach ($a in $p.Value.aliases) { $names += $a }
|
|
10143
|
+
}
|
|
10144
|
+
@($names | Where-Object { $_.StartsWith($fragment) })
|
|
10145
|
+
}
|
|
10146
|
+
|
|
10147
|
+
function __Monoceros_CountPositionals($argTokens, $flags) {
|
|
10148
|
+
$count = 0
|
|
10149
|
+
for ($i = 0; $i -lt $argTokens.Count; $i++) {
|
|
10150
|
+
$t = $argTokens[$i]
|
|
10151
|
+
if ($t -like '--*' -and $t.Contains('=')) { continue }
|
|
10152
|
+
if ($t.StartsWith('-')) {
|
|
10153
|
+
$flag = __Monoceros_LookupFlag $flags $t
|
|
10154
|
+
if ($flag -and $flag.type -eq 'value' -and ($i + 1) -lt $argTokens.Count) { $i++ }
|
|
10155
|
+
continue
|
|
10156
|
+
}
|
|
10157
|
+
$count++
|
|
10158
|
+
}
|
|
10159
|
+
$count
|
|
10160
|
+
}
|
|
10161
|
+
|
|
10162
|
+
function __Monoceros_Emit($candidates) {
|
|
10163
|
+
@($candidates | ForEach-Object {
|
|
10164
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
10165
|
+
})
|
|
10166
|
+
}
|
|
10167
|
+
|
|
10168
|
+
Register-ArgumentCompleter -Native -CommandName @('monoceros', 'monoceros.cmd', 'monoceros.exe') -ScriptBlock {
|
|
10169
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
10170
|
+
|
|
10171
|
+
$model = $global:MonocerosModel
|
|
10172
|
+
$current = if ($wordToComplete) { $wordToComplete } else { '' }
|
|
10173
|
+
|
|
10174
|
+
# Tokens already complete on the line. When the current word is
|
|
10175
|
+
# non-empty it is the last command element \u2014 drop it from "prev".
|
|
10176
|
+
$elements = @($commandAst.CommandElements | ForEach-Object { $_.Extent.Text })
|
|
10177
|
+
if ($current -ne '' -and $elements.Count -gt 0 -and $elements[-1] -eq $current) {
|
|
10178
|
+
$prev = if ($elements.Count -gt 1) { @($elements[0..($elements.Count - 2)]) } else { @() }
|
|
10179
|
+
} else {
|
|
10180
|
+
$prev = $elements
|
|
10181
|
+
}
|
|
10182
|
+
# Strip the program name (prev[0]).
|
|
10183
|
+
$tokens = if ($prev.Count -gt 1) { @($prev[1..($prev.Count - 1)]) } else { @() }
|
|
10184
|
+
|
|
10185
|
+
# No subcommand yet \u2192 complete the subcommand list.
|
|
10186
|
+
if ($tokens.Count -eq 0) {
|
|
10187
|
+
return (__Monoceros_Emit (@($model.commands | Where-Object { $_.StartsWith($current) })))
|
|
10188
|
+
}
|
|
10189
|
+
|
|
10190
|
+
$command = $tokens[0]
|
|
10191
|
+
$spec = $model.specs.PSObject.Properties[$command]
|
|
10192
|
+
if (-not $spec) { return }
|
|
10193
|
+
$spec = $spec.Value
|
|
10194
|
+
$argTokens = if ($tokens.Count -gt 1) { @($tokens[1..($tokens.Count - 1)]) } else { @() }
|
|
10195
|
+
|
|
10196
|
+
# Everything after \`--\` is the inner command \u2014 not ours to complete.
|
|
10197
|
+
if ($argTokens -contains '--') { return }
|
|
10198
|
+
|
|
10199
|
+
# Case A: current is \`--flag=fragment\` \u2192 that flag's values.
|
|
10200
|
+
if ($current -like '--*' -and $current.Contains('=')) {
|
|
10201
|
+
$eq = $current.IndexOf('=')
|
|
10202
|
+
$flagName = $current.Substring(0, $eq)
|
|
10203
|
+
$frag = $current.Substring($eq + 1)
|
|
10204
|
+
$flag = if ($spec.flags) { $spec.flags.PSObject.Properties[$flagName] } else { $null }
|
|
10205
|
+
if (-not $flag -or $flag.Value.type -ne 'value') { return }
|
|
10206
|
+
$vals = __Monoceros_Values $flag.Value.value $argTokens
|
|
10207
|
+
return (__Monoceros_Emit (@(__Monoceros_Filter $vals $frag | ForEach-Object { "$flagName=$_" })))
|
|
10208
|
+
}
|
|
10209
|
+
|
|
10210
|
+
# Case B: current is an incomplete flag name.
|
|
10211
|
+
if ($current.StartsWith('-')) {
|
|
10212
|
+
return (__Monoceros_Emit (__Monoceros_FlagNames $spec.flags $current))
|
|
10213
|
+
}
|
|
10214
|
+
|
|
10215
|
+
# Case C: previous token was a value flag expecting a value.
|
|
10216
|
+
$lastPrev = if ($argTokens.Count -gt 0) { $argTokens[-1] } else { $null }
|
|
10217
|
+
if ($lastPrev -and $lastPrev -like '--*' -and -not $lastPrev.Contains('=')) {
|
|
10218
|
+
$flag = if ($spec.flags) { $spec.flags.PSObject.Properties[$lastPrev] } else { $null }
|
|
10219
|
+
if ($flag -and $flag.Value.type -eq 'value' -and $flag.Value.value) {
|
|
10220
|
+
$vals = __Monoceros_Values $flag.Value.value $argTokens
|
|
10221
|
+
return (__Monoceros_Emit (__Monoceros_Filter $vals $current))
|
|
10222
|
+
}
|
|
10223
|
+
}
|
|
10224
|
+
|
|
10225
|
+
# Case D: a positional slot.
|
|
10226
|
+
$positionalIdx = __Monoceros_CountPositionals $argTokens $spec.flags
|
|
10227
|
+
$positionals = @($spec.positionals)
|
|
10228
|
+
$expected = $spec.positionalCount
|
|
10229
|
+
if ($positionalIdx -lt $positionals.Count) {
|
|
10230
|
+
$vals = __Monoceros_Values $positionals[$positionalIdx] $argTokens
|
|
10231
|
+
return (__Monoceros_Emit (__Monoceros_Filter $vals $current))
|
|
10232
|
+
}
|
|
10233
|
+
if ($positionalIdx -ge $expected) {
|
|
10234
|
+
return (__Monoceros_Emit (__Monoceros_FlagNames $spec.flags $current))
|
|
10235
|
+
}
|
|
10236
|
+
}
|
|
10237
|
+
`;
|
|
10238
|
+
}
|
|
10239
|
+
async function renderCompletionScript(shell) {
|
|
10240
|
+
if (shell === "bash") return renderBashScript();
|
|
10241
|
+
if (shell === "zsh") return renderZshScript();
|
|
10242
|
+
return renderPwshScript();
|
|
10243
|
+
}
|
|
10244
|
+
var SHELLS, completionCommand;
|
|
10245
|
+
var init_completion = __esm({
|
|
10246
|
+
"src/commands/completion.ts"() {
|
|
10247
|
+
"use strict";
|
|
10248
|
+
init_resolve();
|
|
10249
|
+
SHELLS = ["bash", "zsh", "pwsh"];
|
|
10250
|
+
completionCommand = defineCommand9({
|
|
10251
|
+
meta: {
|
|
10252
|
+
name: "completion",
|
|
10253
|
+
group: "tooling",
|
|
10254
|
+
// Hidden from `monoceros --help`: the install scripts wire up
|
|
10255
|
+
// completion automatically; manual setup is documented at
|
|
10256
|
+
// getmonoceros.build/docs/reference/utilities/completion. Still runnable directly.
|
|
10257
|
+
hidden: true,
|
|
10258
|
+
description: "Print a shell completion script for bash, zsh or PowerShell to stdout. Pipe the output into a file your shell loads at startup. The install scripts (install.sh / install.ps1) call this automatically."
|
|
10259
|
+
},
|
|
10260
|
+
args: {
|
|
10261
|
+
shell: {
|
|
10262
|
+
type: "positional",
|
|
10263
|
+
description: "Target shell. One of: 'bash', 'zsh', 'pwsh'.",
|
|
10264
|
+
required: true
|
|
10265
|
+
}
|
|
10266
|
+
},
|
|
10267
|
+
async run({ args }) {
|
|
10268
|
+
const shell = args.shell;
|
|
10269
|
+
if (shell !== "bash" && shell !== "zsh" && shell !== "pwsh") {
|
|
10270
|
+
process.stderr.write(
|
|
10271
|
+
`Unknown shell: ${JSON.stringify(shell)}. Supported: ${SHELLS.join(", ")}.
|
|
10272
|
+
`
|
|
10273
|
+
);
|
|
10274
|
+
process.exit(2);
|
|
10275
|
+
}
|
|
10276
|
+
process.stdout.write(await renderCompletionScript(shell));
|
|
10277
|
+
}
|
|
10278
|
+
});
|
|
10279
|
+
}
|
|
10280
|
+
});
|
|
10281
|
+
|
|
10282
|
+
// src/commands/__bridge.ts
|
|
10283
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
10284
|
+
var __bridgeCommand;
|
|
10285
|
+
var init_bridge = __esm({
|
|
10286
|
+
"src/commands/__bridge.ts"() {
|
|
10287
|
+
"use strict";
|
|
10288
|
+
init_bridge_daemon();
|
|
10289
|
+
__bridgeCommand = defineCommand10({
|
|
10290
|
+
meta: {
|
|
10291
|
+
name: "__bridge",
|
|
10292
|
+
group: "internal",
|
|
10293
|
+
hidden: true,
|
|
10294
|
+
description: "Internal: host-side browser-bridge daemon (background)."
|
|
10295
|
+
},
|
|
10296
|
+
args: {
|
|
10297
|
+
root: {
|
|
10298
|
+
type: "positional",
|
|
10299
|
+
description: "Materialized container root ($MONOCEROS_HOME/container/<name>/).",
|
|
10300
|
+
required: true
|
|
10301
|
+
}
|
|
10302
|
+
},
|
|
10303
|
+
async run({ args }) {
|
|
10304
|
+
try {
|
|
10305
|
+
await runBridgeDaemon({ root: args.root });
|
|
10306
|
+
} catch {
|
|
10307
|
+
}
|
|
10308
|
+
}
|
|
10309
|
+
});
|
|
9951
10310
|
}
|
|
9952
10311
|
});
|
|
9953
10312
|
|