@getmonoceros/workbench 1.29.1 → 1.30.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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
id: atlassian
|
|
2
2
|
category: feature
|
|
3
3
|
displayName: Atlassian
|
|
4
|
-
description: 'Rovo Dev (`acli rovodev`)
|
|
4
|
+
description: 'Rovo Dev (`acli rovodev`), Teamwork Graph (`twg`) and Forge (`forge`) CLIs sharing one Atlassian account. Auth persists across rebuilds.'
|
|
5
5
|
documentationURL: https://developer.atlassian.com/cloud/
|
|
6
6
|
options:
|
|
7
7
|
rovodev:
|
|
@@ -14,6 +14,11 @@ options:
|
|
|
14
14
|
default: true
|
|
15
15
|
description: 'Install twg (Teamwork Graph CLI).'
|
|
16
16
|
surface: yml
|
|
17
|
+
forge:
|
|
18
|
+
type: boolean
|
|
19
|
+
default: true
|
|
20
|
+
description: 'Install the Forge CLI (`forge`) for building Atlassian apps.'
|
|
21
|
+
surface: yml
|
|
17
22
|
instance:
|
|
18
23
|
type: string
|
|
19
24
|
default: ''
|
|
@@ -35,18 +40,36 @@ options:
|
|
|
35
40
|
description: "Optional Bitbucket app password; only needed for twg's Bitbucket commands."
|
|
36
41
|
surface: env
|
|
37
42
|
feature:
|
|
38
|
-
version: 1.
|
|
43
|
+
version: 1.2.0
|
|
39
44
|
persistentHomePaths: [.config/acli, .rovodev, .config/twg, .agents]
|
|
40
45
|
vscodeExtensions: [Atlassian.atlascode]
|
|
46
|
+
# Forge is keychain-free in a container: instead of `forge login` (which
|
|
47
|
+
# needs libsecret), Atlassian's documented path is the FORGE_EMAIL /
|
|
48
|
+
# FORGE_API_TOKEN env vars, read at command time. We hand them to the
|
|
49
|
+
# workspace runtime env from the shared account options, gated on the
|
|
50
|
+
# `forge` toggle. Sibling of a service's connectionEnv (ADR 0021).
|
|
51
|
+
workspaceEnv:
|
|
52
|
+
- whenOption: forge
|
|
53
|
+
vars:
|
|
54
|
+
FORGE_EMAIL: '${email}'
|
|
55
|
+
FORGE_API_TOKEN: '${apiToken}'
|
|
41
56
|
briefing:
|
|
42
57
|
- whenOption: rovodev
|
|
43
58
|
text: 'Atlassian Rovo Dev — invoke via `acli rovodev`. Pre-authenticated against the Atlassian account configured in the feature options (shared with twg).'
|
|
44
59
|
- whenOption: twg
|
|
45
60
|
text: 'Atlassian Teamwork Graph CLI (`twg`) — work-data access across Jira, Confluence, Bitbucket, JSM and Assets. Pre-authenticated. Sub-skills cover status rollups, engineering work, ops health and context discovery.'
|
|
61
|
+
- whenOption: forge
|
|
62
|
+
text: 'Atlassian Forge CLI (`forge`) for building and deploying Forge apps. Authenticated via FORGE_EMAIL / FORGE_API_TOKEN from the shared account (no `forge login` needed in the container).'
|
|
46
63
|
presets:
|
|
47
64
|
twg:
|
|
48
65
|
rovodev: false
|
|
49
66
|
twg: true
|
|
67
|
+
forge: false
|
|
50
68
|
rovodev:
|
|
51
69
|
rovodev: true
|
|
52
70
|
twg: false
|
|
71
|
+
forge: false
|
|
72
|
+
forge:
|
|
73
|
+
rovodev: false
|
|
74
|
+
twg: false
|
|
75
|
+
forge: true
|
|
@@ -3,25 +3,31 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Installs Atlassian CLIs that share a single Atlassian account:
|
|
5
5
|
# - Rovo Dev (acli, default on) — the `acli rovodev` AI agent
|
|
6
|
-
# - Teamwork Graph (twg, default
|
|
6
|
+
# - Teamwork Graph (twg, default on)
|
|
7
|
+
# - Forge (forge, default on) — the `@forge/cli` app-dev toolchain
|
|
7
8
|
#
|
|
8
|
-
# Each tool is toggled independently via the `rovodev` / `twg`
|
|
9
|
-
# options. Credentials (instance / email / apiToken) are shared
|
|
10
|
-
#
|
|
11
|
-
# /usr/local/share/monoceros/post-create.d/ perform the
|
|
12
|
-
# non-interactive login on first container start.
|
|
9
|
+
# Each tool is toggled independently via the `rovodev` / `twg` / `forge`
|
|
10
|
+
# options. Credentials (instance / email / apiToken) are shared across
|
|
11
|
+
# the tools when set; rovodev and twg get per-tool post-create hooks
|
|
12
|
+
# under /usr/local/share/monoceros/post-create.d/ that perform the
|
|
13
|
+
# non-interactive login on first container start. Forge has no login
|
|
14
|
+
# hook: it reads FORGE_EMAIL / FORGE_API_TOKEN from the workspace
|
|
15
|
+
# runtime env, which the workbench injects from the shared account
|
|
16
|
+
# options (feature.workspaceEnv) — the keychain-free path Atlassian
|
|
17
|
+
# recommends for containers.
|
|
13
18
|
|
|
14
19
|
set -euo pipefail
|
|
15
20
|
|
|
16
21
|
ROVODEV="${ROVODEV:-true}"
|
|
17
22
|
TWG="${TWG:-true}"
|
|
23
|
+
FORGE="${FORGE:-true}"
|
|
18
24
|
INSTANCE="${INSTANCE:-}"
|
|
19
25
|
EMAIL="${EMAIL:-}"
|
|
20
26
|
APITOKEN="${APITOKEN:-}"
|
|
21
27
|
BITBUCKETTOKEN="${BITBUCKETTOKEN:-}"
|
|
22
28
|
|
|
23
|
-
if [ "${ROVODEV}" != "true" ] && [ "${TWG}" != "true" ]; then
|
|
24
|
-
echo "[atlassian]
|
|
29
|
+
if [ "${ROVODEV}" != "true" ] && [ "${TWG}" != "true" ] && [ "${FORGE}" != "true" ]; then
|
|
30
|
+
echo "[atlassian] rovodev, twg and forge all disabled — nothing to install" >&2
|
|
25
31
|
exit 0
|
|
26
32
|
fi
|
|
27
33
|
|
|
@@ -185,4 +191,24 @@ EOF
|
|
|
185
191
|
fi
|
|
186
192
|
fi
|
|
187
193
|
|
|
194
|
+
# ─── Forge (@forge/cli) ───────────────────────────────────────────
|
|
195
|
+
if [ "${FORGE}" = "true" ]; then
|
|
196
|
+
# Forge is a Node CLI; the runtime ships Node, so a global npm install
|
|
197
|
+
# is enough. No post-create login hook: `forge login` stores creds in
|
|
198
|
+
# the OS keychain (libsecret on Linux), which we deliberately do not
|
|
199
|
+
# carry. Instead Forge reads FORGE_EMAIL / FORGE_API_TOKEN at command
|
|
200
|
+
# time; the workbench injects them into the workspace runtime env from
|
|
201
|
+
# this feature's `email` / `apiToken` options (feature.workspaceEnv),
|
|
202
|
+
# so the right account is in scope without a login step here.
|
|
203
|
+
echo "[atlassian/forge] installing @forge/cli globally via npm"
|
|
204
|
+
npm install -g @forge/cli
|
|
205
|
+
forge --version >/dev/null 2>&1 || {
|
|
206
|
+
echo "[atlassian/forge] ERROR: install completed but \`forge\` is not on PATH" >&2
|
|
207
|
+
exit 1
|
|
208
|
+
}
|
|
209
|
+
if [ -z "${EMAIL}" ] || [ -z "${APITOKEN}" ]; then
|
|
210
|
+
echo "[atlassian/forge] no email/apiToken set — set them in the container yml so FORGE_EMAIL/FORGE_API_TOKEN reach the shell"
|
|
211
|
+
fi
|
|
212
|
+
fi
|
|
213
|
+
|
|
188
214
|
echo "[atlassian] done"
|
package/dist/bin.js
CHANGED
|
@@ -665,7 +665,16 @@ var init_feature_doc = __esm({
|
|
|
665
665
|
|
|
666
666
|
// src/catalog/descriptor.ts
|
|
667
667
|
import { z as z2 } from "zod";
|
|
668
|
-
|
|
668
|
+
function workspaceEnvTokens(vars) {
|
|
669
|
+
const tokens = [];
|
|
670
|
+
for (const template of Object.values(vars)) {
|
|
671
|
+
for (const m of template.matchAll(/\$\{([A-Za-z0-9_]+)\}/g)) {
|
|
672
|
+
tokens.push(m[1]);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return tokens;
|
|
676
|
+
}
|
|
677
|
+
var DESCRIPTOR_ID_RE, CategorySchema, OptionTypeSchema, SurfaceSchema, OptionValueSchema, OptionSpecSchema, BriefingLineSchema, HealthcheckSchema, LanguageBlockSchema, ServiceBlockSchema, PersistentHomeFileSchema, WorkspaceEnvBlockSchema, FeatureBlockSchema, DescriptorSchema;
|
|
669
678
|
var init_descriptor = __esm({
|
|
670
679
|
"src/catalog/descriptor.ts"() {
|
|
671
680
|
"use strict";
|
|
@@ -766,12 +775,24 @@ var init_descriptor = __esm({
|
|
|
766
775
|
path: z2.string().min(1),
|
|
767
776
|
initialContent: z2.string().optional()
|
|
768
777
|
});
|
|
778
|
+
WorkspaceEnvBlockSchema = z2.object({
|
|
779
|
+
whenOption: z2.string().optional(),
|
|
780
|
+
vars: z2.record(z2.string(), z2.string())
|
|
781
|
+
});
|
|
769
782
|
FeatureBlockSchema = z2.object({
|
|
770
783
|
/** Publishable feature version (devcontainer-feature.json `version`). */
|
|
771
784
|
version: z2.string().min(1),
|
|
772
785
|
persistentHomePaths: z2.array(z2.string().min(1)).optional(),
|
|
773
786
|
persistentHomeFiles: z2.array(PersistentHomeFileSchema).optional(),
|
|
774
|
-
vscodeExtensions: z2.array(z2.string()).optional()
|
|
787
|
+
vscodeExtensions: z2.array(z2.string()).optional(),
|
|
788
|
+
/**
|
|
789
|
+
* Named runtime env injected into the workspace container (compose
|
|
790
|
+
* `environment:` / image-mode `containerEnv`). Catalog/CLI-side only — not
|
|
791
|
+
* emitted into the published devcontainer-feature.json (like `presets`),
|
|
792
|
+
* because it drives how the workbench wires the container, not the feature
|
|
793
|
+
* install. See `featureWorkspaceEnv` in create/scaffold.ts.
|
|
794
|
+
*/
|
|
795
|
+
workspaceEnv: z2.array(WorkspaceEnvBlockSchema).optional()
|
|
775
796
|
});
|
|
776
797
|
DescriptorSchema = z2.object({
|
|
777
798
|
id: z2.string().regex(DESCRIPTOR_ID_RE, "id must be lowercase letters/digits/hyphens"),
|
|
@@ -833,6 +854,24 @@ var init_descriptor = __esm({
|
|
|
833
854
|
});
|
|
834
855
|
}
|
|
835
856
|
});
|
|
857
|
+
data.feature?.workspaceEnv?.forEach((block, i) => {
|
|
858
|
+
if (block.whenOption !== void 0 && !optionKeys.has(block.whenOption)) {
|
|
859
|
+
ctx.addIssue({
|
|
860
|
+
code: z2.ZodIssueCode.custom,
|
|
861
|
+
path: ["feature", "workspaceEnv", i, "whenOption"],
|
|
862
|
+
message: `whenOption '${block.whenOption}' is not a declared option`
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
for (const token of workspaceEnvTokens(block.vars)) {
|
|
866
|
+
if (!optionKeys.has(token)) {
|
|
867
|
+
ctx.addIssue({
|
|
868
|
+
code: z2.ZodIssueCode.custom,
|
|
869
|
+
path: ["feature", "workspaceEnv", i, "vars"],
|
|
870
|
+
message: `workspaceEnv template references '\${${token}}', which is not a declared option`
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
});
|
|
836
875
|
if (data.presets) {
|
|
837
876
|
if (data.category !== "feature") {
|
|
838
877
|
ctx.addIssue({
|
|
@@ -2845,7 +2884,8 @@ function resolveFeatures(opts) {
|
|
|
2845
2884
|
devcontainerKey: entry2.feature,
|
|
2846
2885
|
options,
|
|
2847
2886
|
persistentHomePaths: [],
|
|
2848
|
-
persistentHomeFiles: []
|
|
2887
|
+
persistentHomeFiles: [],
|
|
2888
|
+
workspaceEnv: []
|
|
2849
2889
|
});
|
|
2850
2890
|
}
|
|
2851
2891
|
const aptPackages = [
|
|
@@ -2859,7 +2899,8 @@ function resolveFeatures(opts) {
|
|
|
2859
2899
|
devcontainerKey: "ghcr.io/devcontainers-contrib/features/apt-packages:1",
|
|
2860
2900
|
options: { packages: aptPackages.join(",") },
|
|
2861
2901
|
persistentHomePaths: [],
|
|
2862
|
-
persistentHomeFiles: []
|
|
2902
|
+
persistentHomeFiles: [],
|
|
2903
|
+
workspaceEnv: []
|
|
2863
2904
|
});
|
|
2864
2905
|
}
|
|
2865
2906
|
if (opts.features) {
|
|
@@ -2869,6 +2910,7 @@ function resolveFeatures(opts) {
|
|
|
2869
2910
|
const name = match.name;
|
|
2870
2911
|
const descriptor = featureDescriptor(name);
|
|
2871
2912
|
const { paths, files } = descriptorPersistentHome(descriptor);
|
|
2913
|
+
const workspaceEnv = descriptor?.feature?.workspaceEnv ?? [];
|
|
2872
2914
|
const sourceRoot = featuresSourceRoot();
|
|
2873
2915
|
const localSourceDir = sourceRoot ? path10.join(sourceRoot, name) : null;
|
|
2874
2916
|
if (descriptor && localSourceDir && existsSync7(localSourceDir)) {
|
|
@@ -2879,7 +2921,8 @@ function resolveFeatures(opts) {
|
|
|
2879
2921
|
localName: name,
|
|
2880
2922
|
generatedManifest: descriptorToFeatureManifest(descriptor),
|
|
2881
2923
|
persistentHomePaths: paths,
|
|
2882
|
-
persistentHomeFiles: files
|
|
2924
|
+
persistentHomeFiles: files,
|
|
2925
|
+
workspaceEnv
|
|
2883
2926
|
});
|
|
2884
2927
|
continue;
|
|
2885
2928
|
}
|
|
@@ -2887,7 +2930,8 @@ function resolveFeatures(opts) {
|
|
|
2887
2930
|
devcontainerKey: rawRef,
|
|
2888
2931
|
options,
|
|
2889
2932
|
persistentHomePaths: paths,
|
|
2890
|
-
persistentHomeFiles: files
|
|
2933
|
+
persistentHomeFiles: files,
|
|
2934
|
+
workspaceEnv
|
|
2891
2935
|
});
|
|
2892
2936
|
continue;
|
|
2893
2937
|
}
|
|
@@ -2895,7 +2939,8 @@ function resolveFeatures(opts) {
|
|
|
2895
2939
|
devcontainerKey: rawRef,
|
|
2896
2940
|
options,
|
|
2897
2941
|
persistentHomePaths: [],
|
|
2898
|
-
persistentHomeFiles: []
|
|
2942
|
+
persistentHomeFiles: [],
|
|
2943
|
+
workspaceEnv: []
|
|
2899
2944
|
});
|
|
2900
2945
|
}
|
|
2901
2946
|
}
|
|
@@ -2915,6 +2960,33 @@ function descriptorPersistentHome(descriptor) {
|
|
|
2915
2960
|
files: filterFileEntries(descriptor?.feature?.persistentHomeFiles)
|
|
2916
2961
|
};
|
|
2917
2962
|
}
|
|
2963
|
+
function isOptionEnabled(value) {
|
|
2964
|
+
if (typeof value === "boolean") return value;
|
|
2965
|
+
if (typeof value === "number") return value !== 0;
|
|
2966
|
+
if (typeof value === "string") return value !== "" && value !== "false";
|
|
2967
|
+
return false;
|
|
2968
|
+
}
|
|
2969
|
+
function featureWorkspaceEnv(features) {
|
|
2970
|
+
const env = {};
|
|
2971
|
+
for (const f of features) {
|
|
2972
|
+
for (const block of f.workspaceEnv) {
|
|
2973
|
+
if (block.whenOption !== void 0 && !isOptionEnabled(f.options[block.whenOption])) {
|
|
2974
|
+
continue;
|
|
2975
|
+
}
|
|
2976
|
+
for (const [name, template] of Object.entries(block.vars)) {
|
|
2977
|
+
const value = template.replace(
|
|
2978
|
+
/\$\{([A-Za-z0-9_]+)\}/g,
|
|
2979
|
+
(_, token) => {
|
|
2980
|
+
const v = f.options[token];
|
|
2981
|
+
return v === void 0 || v === null ? "" : String(v);
|
|
2982
|
+
}
|
|
2983
|
+
);
|
|
2984
|
+
if (value !== "") env[name] = value;
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
return env;
|
|
2989
|
+
}
|
|
2918
2990
|
function filterSubpaths(raw) {
|
|
2919
2991
|
if (!Array.isArray(raw)) return [];
|
|
2920
2992
|
return raw.filter(
|
|
@@ -3034,6 +3106,8 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
3034
3106
|
}
|
|
3035
3107
|
} : void 0;
|
|
3036
3108
|
const sshPostStart = runtimeSupportsSshAttach(opts.runtimeVersion) ? { postStartCommand: "sudo /usr/local/bin/monoceros-sshd-up.sh" } : {};
|
|
3109
|
+
const workspaceEnv = featureWorkspaceEnv(resolvedFeatures);
|
|
3110
|
+
const containerEnvField = Object.keys(workspaceEnv).length > 0 ? { containerEnv: workspaceEnv } : void 0;
|
|
3037
3111
|
if (needsCompose(opts)) {
|
|
3038
3112
|
return {
|
|
3039
3113
|
name: opts.name,
|
|
@@ -3074,6 +3148,7 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
3074
3148
|
forwardPorts: ports,
|
|
3075
3149
|
postCreateCommand: ".devcontainer/post-create.sh",
|
|
3076
3150
|
...sshPostStart,
|
|
3151
|
+
...containerEnvField ?? {},
|
|
3077
3152
|
...featuresField ?? {},
|
|
3078
3153
|
...customizationsField ?? {}
|
|
3079
3154
|
};
|
|
@@ -3123,7 +3198,10 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
|
|
|
3123
3198
|
for (const v of ideVolumes) {
|
|
3124
3199
|
lines.push(` - ${v.volume}:${v.target}`);
|
|
3125
3200
|
}
|
|
3126
|
-
const wsEnv =
|
|
3201
|
+
const wsEnv = {
|
|
3202
|
+
...serviceConnectionEnv(opts.services),
|
|
3203
|
+
...featureWorkspaceEnv(resolvedFeatures)
|
|
3204
|
+
};
|
|
3127
3205
|
const wsEnvKeys = Object.keys(wsEnv);
|
|
3128
3206
|
if (wsEnvKeys.length > 0) {
|
|
3129
3207
|
lines.push(" environment:");
|
|
@@ -3812,26 +3890,39 @@ function addInstallUrlToDoc(doc, url) {
|
|
|
3812
3890
|
seq.add(url);
|
|
3813
3891
|
return true;
|
|
3814
3892
|
}
|
|
3815
|
-
function addFeatureToDoc(doc, ref, options = {},
|
|
3893
|
+
function addFeatureToDoc(doc, ref, options = {}, opts = {}) {
|
|
3816
3894
|
const seq = ensureSeq(doc, "features");
|
|
3817
|
-
const label = displayName ?? ref;
|
|
3895
|
+
const label = opts.displayName ?? ref;
|
|
3818
3896
|
const summary = loadFeatureManifestSummary(ref);
|
|
3819
|
-
const
|
|
3820
|
-
|
|
3821
|
-
|
|
3897
|
+
const withHints = (o) => {
|
|
3898
|
+
const out = { ...o };
|
|
3899
|
+
for (const h of featureOptionHints(summary, ref, Object.keys(out))) {
|
|
3900
|
+
out[h.key] = h.placeholder;
|
|
3901
|
+
}
|
|
3902
|
+
return out;
|
|
3903
|
+
};
|
|
3822
3904
|
for (const item of seq.items) {
|
|
3823
3905
|
if (!isMap2(item)) continue;
|
|
3824
3906
|
const itemRef = item.get("ref");
|
|
3825
3907
|
if (itemRef !== ref) continue;
|
|
3826
3908
|
const itemJs = item.toJS(doc);
|
|
3827
3909
|
const existingJs = itemJs.options ?? {};
|
|
3828
|
-
if (
|
|
3910
|
+
if (opts.isPreset) {
|
|
3911
|
+
const merged = withHints(mergeFeatureOptions(existingJs, options));
|
|
3912
|
+
if (JSON.stringify(existingJs) === JSON.stringify(merged)) {
|
|
3913
|
+
return false;
|
|
3914
|
+
}
|
|
3915
|
+
item.set("options", merged);
|
|
3916
|
+
return true;
|
|
3917
|
+
}
|
|
3918
|
+
if (JSON.stringify(existingJs) === JSON.stringify(withHints(options))) {
|
|
3829
3919
|
return false;
|
|
3830
3920
|
}
|
|
3831
3921
|
throw new Error(
|
|
3832
3922
|
`Feature ${label} is already configured with different options. Remove it first (\`monoceros remove-feature ${label}\`) before re-adding.`
|
|
3833
3923
|
);
|
|
3834
3924
|
}
|
|
3925
|
+
const mergedOptions = withHints(options);
|
|
3835
3926
|
const entry2 = new YAMLMap2();
|
|
3836
3927
|
entry2.set("ref", ref);
|
|
3837
3928
|
if (Object.keys(mergedOptions).length > 0) {
|
|
@@ -3993,6 +4084,7 @@ var init_yml = __esm({
|
|
|
3993
4084
|
"use strict";
|
|
3994
4085
|
init_scaffold();
|
|
3995
4086
|
init_manifest();
|
|
4087
|
+
init_components();
|
|
3996
4088
|
init_feature_doc();
|
|
3997
4089
|
init_env_file();
|
|
3998
4090
|
GIT_USER_HEADER_COMMENT = [
|
|
@@ -4340,7 +4432,10 @@ async function runAddFeature(input) {
|
|
|
4340
4432
|
};
|
|
4341
4433
|
const result = await mutate(
|
|
4342
4434
|
input,
|
|
4343
|
-
(doc) => addFeatureToDoc(doc, resolved.ref, merged,
|
|
4435
|
+
(doc) => addFeatureToDoc(doc, resolved.ref, merged, {
|
|
4436
|
+
isPreset: resolved.isPreset,
|
|
4437
|
+
displayName: raw
|
|
4438
|
+
})
|
|
4344
4439
|
);
|
|
4345
4440
|
if (result.status === "updated") {
|
|
4346
4441
|
const summary = loadFeatureManifestSummary(resolved.ref);
|
|
@@ -4368,7 +4463,7 @@ async function runAddFeature(input) {
|
|
|
4368
4463
|
}
|
|
4369
4464
|
async function resolveFeatureRefOrShortname(input) {
|
|
4370
4465
|
if (REGEX.featureRef.test(input)) {
|
|
4371
|
-
return { ref: input, defaultOptions: {} };
|
|
4466
|
+
return { ref: input, defaultOptions: {}, isPreset: false };
|
|
4372
4467
|
}
|
|
4373
4468
|
const catalog = await loadComponentCatalog();
|
|
4374
4469
|
const component = catalog.get(input);
|
|
@@ -4400,7 +4495,8 @@ async function resolveFeatureRefOrShortname(input) {
|
|
|
4400
4495
|
const [first] = features;
|
|
4401
4496
|
return {
|
|
4402
4497
|
ref: first.ref,
|
|
4403
|
-
defaultOptions: { ...first.options ?? {} }
|
|
4498
|
+
defaultOptions: { ...first.options ?? {} },
|
|
4499
|
+
isPreset: input.includes("/")
|
|
4404
4500
|
};
|
|
4405
4501
|
}
|
|
4406
4502
|
function runRemoveLanguage(input) {
|
|
@@ -4683,7 +4779,7 @@ var init_add_feature = __esm({
|
|
|
4683
4779
|
meta: {
|
|
4684
4780
|
name: "add-feature",
|
|
4685
4781
|
group: "edit",
|
|
4686
|
-
description: "Add a devcontainer feature by ref to the container config. Options follow `--` as `key=value` pairs.
|
|
4782
|
+
description: "Add a devcontainer feature by ref to the container config. Options follow `--` as `key=value` pairs. Adding a sub-tool selector (e.g. `atlassian/forge`) to an already-present feature merges it in additively, keeping the other sub-tools on. A plain feature already present with different options is an error (remove + re-add to change it)."
|
|
4687
4783
|
},
|
|
4688
4784
|
args: {
|
|
4689
4785
|
name: {
|
|
@@ -4846,7 +4942,7 @@ var init_add_repo = __esm({
|
|
|
4846
4942
|
},
|
|
4847
4943
|
provider: {
|
|
4848
4944
|
type: "string",
|
|
4849
|
-
description: "Git provider for credential-helper guidance: github | gitlab | bitbucket. Required when the URL host is not github.com, gitlab.com, or bitbucket.org \u2014 Monoceros uses this to suggest the right
|
|
4945
|
+
description: "Git provider for credential-helper guidance: github | gitlab | bitbucket | gitea. Required when the URL host is not github.com, gitlab.com, or bitbucket.org \u2014 Monoceros uses this to suggest the right auth (gh / glab / a provider token) on missing credentials."
|
|
4850
4946
|
},
|
|
4851
4947
|
yes: {
|
|
4852
4948
|
type: "boolean",
|
|
@@ -5400,7 +5496,9 @@ async function setupWindowsBridge(name, hostAlias, privateKey, deps, logger) {
|
|
|
5400
5496
|
}
|
|
5401
5497
|
const monoDir = path13.join(profile.homeWsl, ".ssh", "monoceros");
|
|
5402
5498
|
await fs10.mkdir(monoDir, { recursive: true });
|
|
5403
|
-
|
|
5499
|
+
const keyDst = path13.join(monoDir, name);
|
|
5500
|
+
await fs10.rm(keyDst, { force: true });
|
|
5501
|
+
await fs10.copyFile(privateKey, keyDst);
|
|
5404
5502
|
const keyWin = `${profile.homeWin}\\.ssh\\monoceros\\${name}`;
|
|
5405
5503
|
await upsertMarkedBlock(
|
|
5406
5504
|
path13.join(profile.homeWsl, ".ssh", "config"),
|
|
@@ -8082,7 +8180,7 @@ var CLI_VERSION;
|
|
|
8082
8180
|
var init_version = __esm({
|
|
8083
8181
|
"src/version.ts"() {
|
|
8084
8182
|
"use strict";
|
|
8085
|
-
CLI_VERSION = true ? "1.
|
|
8183
|
+
CLI_VERSION = true ? "1.30.0" : "dev";
|
|
8086
8184
|
}
|
|
8087
8185
|
});
|
|
8088
8186
|
|