totopo 3.7.0-rc-1 → 3.7.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
CHANGED
|
@@ -127,27 +127,26 @@ profiles:
|
|
|
127
127
|
description: "Base image: Node.js, git, and AI CLIs"
|
|
128
128
|
dockerfile_hook: |
|
|
129
129
|
# No extras — uses the totopo base image as-is (Node.js + git + AI CLIs).
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
RUN curl -fsSL https://bun.sh/install | bash
|
|
130
|
+
|
|
131
|
+
# Uncomment to enable additional runtimes (Go, Java, Rust, Bun):
|
|
132
|
+
# extended:
|
|
133
|
+
# description: Base image + Go, Java, Rust, and Bun
|
|
134
|
+
# dockerfile_hook: |
|
|
135
|
+
# # Go
|
|
136
|
+
# RUN apt-get update && apt-get install -y --no-install-recommends golang-go && rm -rf /var/lib/apt/lists/*
|
|
137
|
+
# # Java (headless JDK)
|
|
138
|
+
# RUN apt-get update && apt-get install -y --no-install-recommends default-jdk-headless && rm -rf /var/lib/apt/lists/*
|
|
139
|
+
# # Rust (system-wide)
|
|
140
|
+
# ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo PATH=/usr/local/cargo/bin:$PATH
|
|
141
|
+
# RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path && chmod -R a+rx /usr/local/cargo /usr/local/rustup
|
|
142
|
+
# # Bun
|
|
143
|
+
# ENV BUN_INSTALL=/usr/local/bun PATH=/usr/local/bun/bin:$PATH
|
|
144
|
+
# RUN curl -fsSL https://bun.sh/install | bash
|
|
146
145
|
# Add more profiles here — or ask the agent inside the container to set one up for you.
|
|
147
146
|
|
|
148
147
|
```
|
|
149
148
|
|
|
150
|
-
|
|
149
|
+
New workspaces ship with the `default` profile active and an `extended` profile (Go, Java, Rust, Bun) included as a commented-out template — uncomment to enable. When multiple profiles are defined, totopo prompts you to pick one at session start (the choice is remembered). A profile change triggers a container rebuild on the next session.
|
|
151
150
|
|
|
152
151
|
The base image is defined in [`templates/Dockerfile`](templates/Dockerfile) — inspect it to see what's already included before adding your own layers. To force a fully fresh build (no Docker layer cache), use **Manage Workspace > Clean rebuild**.
|
|
153
152
|
|
package/dist/commands/dev.js
CHANGED
|
@@ -103,7 +103,10 @@ function stopAndRemoveContainer(containerName) {
|
|
|
103
103
|
}
|
|
104
104
|
// --- Run startup checks (AI CLI update + readiness validation) ---------------------------------------------------------------------------
|
|
105
105
|
function runStartup(containerName, quiet) {
|
|
106
|
-
|
|
106
|
+
// The SPACE-to-skip prompt in startup.mjs needs raw-mode stdin (-i) and a PTY (-t).
|
|
107
|
+
// Omitted when quiet so test output stays pipe-capturable.
|
|
108
|
+
const ttyFlags = quiet ? [] : ["-i", "-t"];
|
|
109
|
+
const result = spawnSync("docker", ["exec", "-u", "root", ...ttyFlags, containerName, "node", CONTAINER_STARTUP], {
|
|
107
110
|
stdio: quiet ? "pipe" : "inherit",
|
|
108
111
|
});
|
|
109
112
|
return result.status === 0;
|
package/dist/commands/onboard.js
CHANGED
|
@@ -121,7 +121,7 @@ export async function run(cwd) {
|
|
|
121
121
|
const workspaceId = deriveUniqueWorkspaceId(inputId, workspaceRoot);
|
|
122
122
|
// Build and write totopo.yaml
|
|
123
123
|
yaml = buildDefaultTotopoYaml(workspaceId);
|
|
124
|
-
writeTotopoYaml(workspaceRoot, yaml);
|
|
124
|
+
writeTotopoYaml(workspaceRoot, yaml, { includeExtendedTemplate: true });
|
|
125
125
|
log.success(`Created ${toTildePath(join(workspaceRoot, TOTOPO_YAML))}`);
|
|
126
126
|
}
|
|
127
127
|
// --- Non-git warning -----------------------------------------------------------------------------------------------------------------
|
|
@@ -197,7 +197,7 @@ async function resetTotopoYaml(ctx) {
|
|
|
197
197
|
if (isCancel(confirmed) || !confirmed)
|
|
198
198
|
return;
|
|
199
199
|
const freshYaml = buildDefaultTotopoYaml(yaml.workspace_id);
|
|
200
|
-
writeTotopoYaml(ctx.workspaceRoot, freshYaml);
|
|
200
|
+
writeTotopoYaml(ctx.workspaceRoot, freshYaml, { includeExtendedTemplate: true });
|
|
201
201
|
log.success("totopo.yaml reset to defaults.");
|
|
202
202
|
await promptStopContainer(ctx);
|
|
203
203
|
}
|
|
@@ -119,7 +119,7 @@ function migrateSingleV2Workspace(v2, existingIds) {
|
|
|
119
119
|
if (v2.shadowPaths.length > 0) {
|
|
120
120
|
yaml.shadow_paths = [...new Set([...(yaml.shadow_paths ?? []), ...v2.shadowPaths])];
|
|
121
121
|
}
|
|
122
|
-
writeTotopoYaml(v2.projectRoot, yaml);
|
|
122
|
+
writeTotopoYaml(v2.projectRoot, yaml, { includeExtendedTemplate: true });
|
|
123
123
|
log.info(`Created totopo.yaml for "${v2.displayName}" (workspace_id: ${workspaceId})`);
|
|
124
124
|
}
|
|
125
125
|
const newDir = join(getWorkspacesBaseDir(), workspaceId);
|
package/dist/lib/totopo-yaml.js
CHANGED
|
@@ -89,7 +89,7 @@ const YAML_COMMENTS = {
|
|
|
89
89
|
"# When multiple profiles exist, totopo prompts you to pick one on session start.",
|
|
90
90
|
};
|
|
91
91
|
/** Write totopo.yaml to a directory with schema header and inline comments. */
|
|
92
|
-
export function writeTotopoYaml(dir, config) {
|
|
92
|
+
export function writeTotopoYaml(dir, config, opts = {}) {
|
|
93
93
|
const filePath = join(dir, TOTOPO_YAML);
|
|
94
94
|
const yamlContent = dumpYaml(config, {
|
|
95
95
|
lineWidth: -1,
|
|
@@ -109,11 +109,14 @@ export function writeTotopoYaml(dir, config) {
|
|
|
109
109
|
output.push(line);
|
|
110
110
|
}
|
|
111
111
|
const body = output.join("\n").trimEnd();
|
|
112
|
-
|
|
112
|
+
const extendedBlock = opts.includeExtendedTemplate ? `\n\n${EXTENDED_PROFILE_TEMPLATE_PROMPT}\n${renderExtendedAsCommented()}` : "";
|
|
113
|
+
writeFileSync(filePath, `${body}${extendedBlock}\n${PROFILES_FOOTER_COMMENT}\n`);
|
|
113
114
|
}
|
|
114
115
|
// --- Defaults ----------------------------------------------------------------------------------------------------------------------------
|
|
116
|
+
const DEFAULT_PROFILE_DESCRIPTION = "Base image: Node.js, git, and AI CLIs";
|
|
115
117
|
const DEFAULT_PROFILE_HOOK = `# No extras — uses the totopo base image as-is (Node.js + git + AI CLIs).
|
|
116
118
|
`;
|
|
119
|
+
const EXTENDED_PROFILE_DESCRIPTION = "Base image + Go, Java, Rust, and Bun";
|
|
117
120
|
const EXTENDED_PROFILE_HOOK = `# Go
|
|
118
121
|
RUN apt-get update && apt-get install -y --no-install-recommends golang-go && rm -rf /var/lib/apt/lists/*
|
|
119
122
|
|
|
@@ -133,6 +136,20 @@ RUN curl -fsSL https://bun.sh/install | bash
|
|
|
133
136
|
`;
|
|
134
137
|
// Appended after the last profile to hint at adding more
|
|
135
138
|
const PROFILES_FOOTER_COMMENT = " # Add more profiles here — or ask the agent inside the container to set one up for you.";
|
|
139
|
+
const EXTENDED_PROFILE_TEMPLATE_PROMPT = " # Uncomment to enable additional runtimes (Go, Java, Rust, Bun):";
|
|
140
|
+
/**
|
|
141
|
+
* Render the `extended` profile as commented-out YAML, indented to live under `profiles:`.
|
|
142
|
+
* Generated via the same dumpYaml call as the live config so uncommenting (strip leading `# `
|
|
143
|
+
* from each line) yields YAML the parser/schema accepts without drift.
|
|
144
|
+
*/
|
|
145
|
+
function renderExtendedAsCommented() {
|
|
146
|
+
const dumped = dumpYaml({ extended: { description: EXTENDED_PROFILE_DESCRIPTION, dockerfile_hook: EXTENDED_PROFILE_HOOK } }, { lineWidth: -1, quotingType: '"', forceQuotes: false });
|
|
147
|
+
return dumped
|
|
148
|
+
.split("\n")
|
|
149
|
+
.map((line) => (line.length === 0 ? "" : ` # ${line}`))
|
|
150
|
+
.join("\n")
|
|
151
|
+
.trimEnd();
|
|
152
|
+
}
|
|
136
153
|
/** Create a default TotopoYamlConfig with sane defaults. */
|
|
137
154
|
export function buildDefaultTotopoYaml(workspaceId) {
|
|
138
155
|
return {
|
|
@@ -141,13 +158,9 @@ export function buildDefaultTotopoYaml(workspaceId) {
|
|
|
141
158
|
shadow_paths: [...DEFAULT_SHADOW_PATHS],
|
|
142
159
|
profiles: {
|
|
143
160
|
default: {
|
|
144
|
-
description:
|
|
161
|
+
description: DEFAULT_PROFILE_DESCRIPTION,
|
|
145
162
|
dockerfile_hook: DEFAULT_PROFILE_HOOK,
|
|
146
163
|
},
|
|
147
|
-
extended: {
|
|
148
|
-
description: "Base image + Go, Java, Rust, and Bun",
|
|
149
|
-
dockerfile_hook: EXTENDED_PROFILE_HOOK,
|
|
150
|
-
},
|
|
151
164
|
},
|
|
152
165
|
};
|
|
153
166
|
}
|
|
@@ -201,7 +214,7 @@ export function repairTotopoYaml(dir) {
|
|
|
201
214
|
return { repairedYaml: null, error: `${TOTOPO_YAML} could not be repaired: ${formatValidationErrors(validate.errors)}` };
|
|
202
215
|
}
|
|
203
216
|
const yaml = obj;
|
|
204
|
-
writeTotopoYaml(dir, yaml);
|
|
217
|
+
writeTotopoYaml(dir, yaml, { includeExtendedTemplate: fixes.includes("added default profiles") });
|
|
205
218
|
return { repairedYaml: yaml, message: `Repaired ${TOTOPO_YAML}: ${fixes.join(", ")}` };
|
|
206
219
|
}
|
|
207
220
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -45,8 +45,10 @@ function removeWrapperIfPresent() {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function applyAsRoot(gitMode, protocolValue, fail) {
|
|
48
|
+
// Use absolute path to bypass /usr/local/bin/git (which may already be the read-only
|
|
49
|
+
// wrapper from a prior strict-mode session and would block this legitimate setup write).
|
|
48
50
|
try {
|
|
49
|
-
execSync(
|
|
51
|
+
execSync(`/usr/bin/git config --system protocol.allow ${protocolValue}`, { stdio: "pipe" });
|
|
50
52
|
} catch {
|
|
51
53
|
fail("git mode", `failed to set protocol.allow=${protocolValue}`);
|
|
52
54
|
}
|