chore: maintenance — dependency majors, rename step 4, member Claude config, OpenAPI coverage
Dependencies
- Security: body-parser and multer DoS advisories fixed (npm audit: 0).
- Server: Express 5 (query parser kept "extended", req.body defaults to {},
SPA catch-all /{*splat}, send-file helper restores Express 4's dot-folder
rule for sendFile/download, alerts ack route without inline regex),
better-sqlite3 13, js-yaml 5 (server/lib/yaml.js keeps js-yaml 4's default
schema), uuid dropped for crypto.randomUUID, node-pty beta.18, multer 2.5.
- Client: React 19, xterm 6 (WebGL renderer replaces the removed canvas one,
DOM fallback on context loss; Alt+arrow word moves re-mapped), lucide-react 1,
TypeScript 7, i18next/react-i18next/codemirror minors.
Workspace rename step 4
- `agenthub lanes` and `--lane` exit 1 with "renamed: use …"; client api.ts,
the CLI and the MCP server call /api/workspaces; /api/lanes stays mounted.
Server mode (S3): members manage their own Claude Code config
- server/lib/cc-target.js resolves whose config a /cc-config request touches
and gates the router by allowlist; /profiles* and /plugins/update-all stay
admin-only. Member writes go through write-as, CLI calls through sudo as the
member (never -y), reads through openSafe; cc_config_changed reaches only
its owner. Not verified live on an S3 install yet.
Other
- Chat: an HTML file is an artifact only when written whole and not a project
page (index.html, or under src/, public/, templates/, …).
- OpenAPI covers every /api route; openapi-coverage.test.js guards it.
- read-pool test keeps its event loop alive (its worker is unref'd).
- Docs: plan statuses, checklists, CLI.md, LANES.md, SERVER-MODE.md, API.md.
This commit is contained in:
+67
-69
@@ -1281,7 +1281,7 @@ async function cmdDoctor() {
|
||||
// Asked of the SERVER, not of this shell: a run is a tmux session the server
|
||||
// spawns, so the only PATH that decides whether Start Run works is its own —
|
||||
// a systemd --user service never reads your shell profile. Missing tmux is a
|
||||
// warning, not a failure: it costs the run console and `lanes shell`, nothing
|
||||
// warning, not a failure: it costs the run console and `workspace shell`, nothing
|
||||
// else.
|
||||
try {
|
||||
const { available, upgrade } = await get("/api/run/tmux");
|
||||
@@ -1693,7 +1693,7 @@ function cmdOpen() {
|
||||
}
|
||||
|
||||
/**
|
||||
* `agenthub lanes add` — adopt a working directory or provision a managed worktree.
|
||||
* `agenthub workspace add` — adopt a working directory or provision a managed worktree.
|
||||
*/
|
||||
async function cmdLanesAdd(args) {
|
||||
const flag = (name) => {
|
||||
@@ -1719,12 +1719,12 @@ async function cmdLanesAdd(args) {
|
||||
if (base) body.base = base;
|
||||
if (slug) body.slug = slug;
|
||||
if (tool) body.tool = tool;
|
||||
const { lane: provisioning } = await post("/api/lanes/worktree", body);
|
||||
const { lane: provisioning } = await post("/api/workspaces/worktree", body);
|
||||
const deadline = Date.now() + 30_000;
|
||||
let lane = provisioning;
|
||||
while (lane.status === "provisioning" && Date.now() < deadline) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||
({ lane } = await get(`/api/lanes/${provisioning.id}`));
|
||||
({ lane } = await get(`/api/workspaces/${provisioning.id}`));
|
||||
}
|
||||
if (lane.status === "provisioning") {
|
||||
console.error(
|
||||
@@ -1755,12 +1755,12 @@ async function cmdLanesAdd(args) {
|
||||
const body = { cwd, title };
|
||||
if (pipeline) body.pipeline = pipeline;
|
||||
if (tool) body.tool = tool;
|
||||
const { lane } = await post("/api/lanes", body);
|
||||
const { lane } = await post("/api/workspaces", body);
|
||||
console.log(`${c.green("✔")} Created workspace #${lane.id}: ${lane.title}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* `agenthub lanes gc [--dry-run]` — reap orphaned Playwright MCP processes
|
||||
* `agenthub workspace gc [--dry-run]` — reap orphaned Playwright MCP processes
|
||||
* (owning session died) and cap oversized hook logs, across every lane on
|
||||
* this machine. Machine-wide, not one lane's; pure local process/file
|
||||
* action, no HTTP round-trip.
|
||||
@@ -1790,7 +1790,7 @@ function cmdLanesGc(args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* `agenthub lanes profile init <repo>` — detect a Node.js project and scaffold
|
||||
* `agenthub workspace profile init <repo>` — detect a Node.js project and scaffold
|
||||
* `.agenthub/profile/`. Pure filesystem action against the SOURCE repo; does not
|
||||
* talk to the dashboard server at all.
|
||||
*/
|
||||
@@ -1844,9 +1844,9 @@ function cmdLanesProfileInit(args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* `agenthub lanes profile check [<path>]` — validate a profile without needing a
|
||||
* `agenthub workspace profile check [<path>]` — validate a profile without needing a
|
||||
* lane to exist for it yet. Defaults to the current directory, NOT lane-id
|
||||
* resolution (unlike every other `lanes` subcommand) — this is meant to run
|
||||
* resolution (unlike every other `workspace` subcommand) — this is meant to run
|
||||
* against a bare repo right after `profile init`.
|
||||
*/
|
||||
async function cmdLanesProfileCheck(args) {
|
||||
@@ -1883,7 +1883,7 @@ function printLaneFacts(title, facts, fields) {
|
||||
}
|
||||
}
|
||||
|
||||
/** `agenthub lanes reset|remove|purge` — destructive work only after a fact check. */
|
||||
/** `agenthub workspace reset|remove|purge` — destructive work only after a fact check. */
|
||||
async function cmdLanesLifecycle(action, args) {
|
||||
const id = args.find((arg) => !arg.startsWith("--"));
|
||||
const fields = LANE_PREFLIGHT_FIELDS[action];
|
||||
@@ -1894,7 +1894,7 @@ async function cmdLanesLifecycle(action, args) {
|
||||
return;
|
||||
}
|
||||
|
||||
const preflight = await get(`/api/lanes/${id}/preflight?action=${action}`);
|
||||
const preflight = await get(`/api/workspaces/${id}/preflight?action=${action}`);
|
||||
printLaneFacts(`Preflight for ${action} workspace #${id}:`, preflight, fields);
|
||||
const keepDb = action === "reset" && args.includes("--keep-db");
|
||||
if (preflight.database) {
|
||||
@@ -1938,7 +1938,7 @@ async function cmdLanesLifecycle(action, args) {
|
||||
const body = { confirm: true, expect };
|
||||
if (args.includes("--force")) body.force = true;
|
||||
if (keepDb) body.keepDb = true;
|
||||
const result = await post(`/api/lanes/${id}/${action}`, body, { allowError: true });
|
||||
const result = await post(`/api/workspaces/${id}/${action}`, body, { allowError: true });
|
||||
if (result.status) {
|
||||
const error = result.data?.error || {};
|
||||
console.error(`✖ ${action} workspace #${id} → ${error.message || `HTTP ${result.status}`}`);
|
||||
@@ -1961,7 +1961,7 @@ async function cmdLanesLifecycle(action, args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* `agenthub lanes` — one row per lane: what it is, where it is in its pipeline, and
|
||||
* `agenthub workspaces` — one row per lane: what it is, where it is in its pipeline, and
|
||||
* whether the driving session is still breathing.
|
||||
*/
|
||||
/**
|
||||
@@ -1993,7 +1993,7 @@ function detectionLeadsDeclaration(l) {
|
||||
}
|
||||
|
||||
async function cmdLanes() {
|
||||
const { lanes, counts } = await get("/api/lanes");
|
||||
const { lanes, counts } = await get("/api/workspaces");
|
||||
if (!lanes.length) {
|
||||
console.log(
|
||||
"no workspaces yet — create one with: agenthub workspace add --cwd <path> --title <text>"
|
||||
@@ -2040,7 +2040,7 @@ function toolFromEnv() {
|
||||
*
|
||||
* Only the FIRST positional counts as an id, and only when it is all digits.
|
||||
* Scanning the whole argv for a number would swallow flag values —
|
||||
* `agenthub lanes logs web --tail 4096` would have addressed lane 4096.
|
||||
* `agenthub workspace logs web --tail 4096` would have addressed lane 4096.
|
||||
*
|
||||
* @param {string[]} args - Arguments after the subcommand.
|
||||
* @returns {Promise<{laneId: string|number, rest: string[]}|null>} null once an error is printed.
|
||||
@@ -2051,13 +2051,13 @@ async function resolveLaneArg(args) {
|
||||
return i > -1 ? args[i + 1] : undefined;
|
||||
};
|
||||
|
||||
const explicit = flagValue("lane") || flagValue("workspace");
|
||||
const explicit = flagValue("workspace");
|
||||
if (explicit) return { laneId: explicit, rest: args };
|
||||
if (args.length && /^\d+$/.test(args[0])) return { laneId: args[0], rest: args.slice(1) };
|
||||
|
||||
const tool = flagValue("tool") || toolFromEnv();
|
||||
const cwd = require("path").resolve(flagValue("cwd") || process.cwd());
|
||||
const { lanes } = await get("/api/lanes");
|
||||
const { lanes } = await get("/api/workspaces");
|
||||
|
||||
// A pane's AGENTHUB_LANE_ID beats cwd matching, but only after it is checked
|
||||
// against the live lane list AND the directory: a pane outlives the lane it
|
||||
@@ -2146,7 +2146,7 @@ function printRuntime(runtime) {
|
||||
}
|
||||
|
||||
/**
|
||||
* `agenthub lanes up|down|runtime|logs|hook` — a lane's own application stack, as
|
||||
* `agenthub workspace up|down|runtime|logs|hook` — a lane's own application stack, as
|
||||
* opposed to `start`/`stop`, which drive its Claude run. Two lifecycles, one
|
||||
* lane id.
|
||||
*
|
||||
@@ -2160,7 +2160,7 @@ async function cmdLanesRuntime(sub, args) {
|
||||
const { laneId, rest: laneArgs } = resolved;
|
||||
|
||||
if (sub === "runtime") {
|
||||
printRuntime(await get(`/api/lanes/${laneId}/runtime`));
|
||||
printRuntime(await get(`/api/workspaces/${laneId}/runtime`));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2168,7 +2168,7 @@ async function cmdLanesRuntime(sub, args) {
|
||||
const body = {};
|
||||
if (laneArgs.includes("--no-build")) body.build = false;
|
||||
if (laneArgs.includes("--qc")) body.qc = true;
|
||||
const result = await post(`/api/lanes/${laneId}/up`, body, { allowError: true });
|
||||
const result = await post(`/api/workspaces/${laneId}/up`, body, { allowError: true });
|
||||
if (result.status) {
|
||||
console.error(`✖ up workspace #${laneId} → ${result.data?.error?.message || result.status}`);
|
||||
process.exitCode = 1;
|
||||
@@ -2181,7 +2181,7 @@ async function cmdLanesRuntime(sub, args) {
|
||||
const deadline = Date.now() + 15 * 60 * 1000;
|
||||
for (;;) {
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
const runtime = await get(`/api/lanes/${laneId}/runtime`);
|
||||
const runtime = await get(`/api/workspaces/${laneId}/runtime`);
|
||||
if (runtime.healthy) {
|
||||
printRuntime(runtime);
|
||||
return;
|
||||
@@ -2202,7 +2202,7 @@ async function cmdLanesRuntime(sub, args) {
|
||||
}
|
||||
|
||||
if (sub === "down") {
|
||||
const result = await post(`/api/lanes/${laneId}/down`, {}, { allowError: true });
|
||||
const result = await post(`/api/workspaces/${laneId}/down`, {}, { allowError: true });
|
||||
if (result.status) {
|
||||
console.error(
|
||||
`✖ down workspace #${laneId} → ${result.data?.error?.message || result.status}`
|
||||
@@ -2223,7 +2223,7 @@ async function cmdLanesRuntime(sub, args) {
|
||||
}
|
||||
const i = laneArgs.indexOf("--tail");
|
||||
const query = i > -1 && laneArgs[i + 1] ? `?tail=${encodeURIComponent(laneArgs[i + 1])}` : "";
|
||||
const log = await get(`/api/lanes/${laneId}/logs/${encodeURIComponent(svc)}${query}`);
|
||||
const log = await get(`/api/workspaces/${laneId}/logs/${encodeURIComponent(svc)}${query}`);
|
||||
if (!log.available) {
|
||||
console.log("no logs for this workspace yet.");
|
||||
return;
|
||||
@@ -2241,7 +2241,7 @@ async function cmdLanesRuntime(sub, args) {
|
||||
return;
|
||||
}
|
||||
const result = await post(
|
||||
`/api/lanes/${laneId}/hook/${encodeURIComponent(name)}`,
|
||||
`/api/workspaces/${laneId}/hook/${encodeURIComponent(name)}`,
|
||||
{ args: laneArgs.slice(1) },
|
||||
{ allowError: true }
|
||||
);
|
||||
@@ -2263,7 +2263,7 @@ async function cmdLanesRuntime(sub, args) {
|
||||
: "merge";
|
||||
const branch = laneArgs.find((arg) => !arg.startsWith("--"));
|
||||
const result = await post(
|
||||
`/api/lanes/${laneId}/sync-base`,
|
||||
`/api/workspaces/${laneId}/sync-base`,
|
||||
{ mode, branch },
|
||||
{ allowError: true }
|
||||
);
|
||||
@@ -2342,7 +2342,7 @@ async function defaultHolder(argsAfterName) {
|
||||
|
||||
const resolved = await resolveLaneArg(argsAfterName);
|
||||
if (!resolved) return null;
|
||||
const { lane } = await get(`/api/lanes/${resolved.laneId}`);
|
||||
const { lane } = await get(`/api/workspaces/${resolved.laneId}`);
|
||||
return lane.slot ? `lane${lane.slot}` : `lane${lane.id}`;
|
||||
}
|
||||
|
||||
@@ -2474,7 +2474,7 @@ function fmtFeatureRow(f) {
|
||||
async function cmdFeatureList(args) {
|
||||
const resolved = await resolveLaneArg(args);
|
||||
if (!resolved) return;
|
||||
const { features } = await get(`/api/lanes/${resolved.laneId}/features`);
|
||||
const { features } = await get(`/api/workspaces/${resolved.laneId}/features`);
|
||||
if (!features.length) {
|
||||
console.log("no features activated yet — start one with: agenthub feature activate <slug>");
|
||||
return;
|
||||
@@ -2496,7 +2496,7 @@ async function cmdFeatureActivate(args) {
|
||||
};
|
||||
const resolved = await resolveLaneArg(args.filter((a) => a !== slug));
|
||||
if (!resolved) return;
|
||||
const { lane, feature } = await post(`/api/lanes/${resolved.laneId}/features/activate`, {
|
||||
const { lane, feature } = await post(`/api/workspaces/${resolved.laneId}/features/activate`, {
|
||||
slug,
|
||||
title: flag("title"),
|
||||
});
|
||||
@@ -2506,13 +2506,13 @@ async function cmdFeatureActivate(args) {
|
||||
}
|
||||
|
||||
/** `agenthub feature show <slug> [<id>] [--cwd path]` — one feature's saved pipeline. */
|
||||
/** `agenthub lanes proof-link [<id>] [--cwd path]` — converge the clone-root
|
||||
/** `agenthub workspace proof-link [<id>] [--cwd path]` — converge the clone-root
|
||||
* `proof/` onto `.playwright-mcp/proof` (idempotent). Never run automatically
|
||||
* by anything else in this codebase; a session or hook calls it explicitly. */
|
||||
async function cmdLanesProofLink(args) {
|
||||
const resolved = await resolveLaneArg(args);
|
||||
if (!resolved) return;
|
||||
const { linked } = await post(`/api/lanes/${resolved.laneId}/proof-link`);
|
||||
const { linked } = await post(`/api/workspaces/${resolved.laneId}/proof-link`);
|
||||
console.log(
|
||||
linked
|
||||
? `${c.green("✔")} linked proof/ -> .playwright-mcp/proof`
|
||||
@@ -2520,13 +2520,13 @@ async function cmdLanesProofLink(args) {
|
||||
);
|
||||
}
|
||||
|
||||
/** `agenthub lanes mcp sync [<id>] [--cwd path]` — relocate the source repo's
|
||||
/** `agenthub workspace mcp sync [<id>] [--cwd path]` — relocate the source repo's
|
||||
* already-configured MCP servers into <lane>/.mcp.json and seed Chromium
|
||||
* profiles. Never run automatically; a session calls it explicitly. */
|
||||
async function cmdLanesMcpSync(args) {
|
||||
const resolved = await resolveLaneArg(args);
|
||||
if (!resolved) return;
|
||||
const result = await post(`/api/lanes/${resolved.laneId}/mcp/sync`, {}, { allowError: true });
|
||||
const result = await post(`/api/workspaces/${resolved.laneId}/mcp/sync`, {}, { allowError: true });
|
||||
if (result.status) {
|
||||
console.error(`✖ mcp sync → ${result.data?.error?.message || result.status}`);
|
||||
process.exitCode = 1;
|
||||
@@ -2538,7 +2538,7 @@ async function cmdLanesMcpSync(args) {
|
||||
}
|
||||
}
|
||||
|
||||
/** `agenthub lanes integration <name> [<id>]` — check whether a named
|
||||
/** `agenthub workspace integration <name> [<id>]` — check whether a named
|
||||
* integration (tracker, dev_qc, ci_wait) is turned on for this lane, per
|
||||
* its .agenthub/profile/integrations.env. Exit 0 = on, 1 = off. */
|
||||
async function cmdLanesIntegration(args) {
|
||||
@@ -2551,13 +2551,13 @@ async function cmdLanesIntegration(args) {
|
||||
const resolved = await resolveLaneArg(args.filter((a) => a !== name));
|
||||
if (!resolved) return;
|
||||
const { enabled } = await get(
|
||||
`/api/lanes/${resolved.laneId}/integrations/${encodeURIComponent(name)}`
|
||||
`/api/workspaces/${resolved.laneId}/integrations/${encodeURIComponent(name)}`
|
||||
);
|
||||
console.log(enabled ? "on" : "off");
|
||||
process.exitCode = enabled ? 0 : 1;
|
||||
}
|
||||
|
||||
/** `agenthub lanes ci status <branch-or-sha> [<lane-id>]` — one-shot GitHub
|
||||
/** `agenthub workspace ci status <branch-or-sha> [<lane-id>]` — one-shot GitHub
|
||||
* Actions status snapshot for a ref, via GET /:id/ci/status. Never polls;
|
||||
* the caller (ship-feature-lane Stage 13) owns the wait loop. Exit code
|
||||
* mirrors the derived state: 0 success, 1 failure or any error, 3 pending. */
|
||||
@@ -2576,7 +2576,7 @@ async function cmdLanesCi(args) {
|
||||
const resolved = await resolveLaneArg(args.slice(2));
|
||||
if (!resolved) return;
|
||||
const result = await get(
|
||||
`/api/lanes/${resolved.laneId}/ci/status?ref=${encodeURIComponent(ref)}`,
|
||||
`/api/workspaces/${resolved.laneId}/ci/status?ref=${encodeURIComponent(ref)}`,
|
||||
undefined,
|
||||
{ allowError: true }
|
||||
);
|
||||
@@ -2590,7 +2590,7 @@ async function cmdLanesCi(args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* `agenthub lanes node [<node-id>] [extra…] [<id>] [--print]` — run ONE node of the
|
||||
* `agenthub workspace node [<node-id>] [extra…] [<id>] [--print]` — run ONE node of the
|
||||
* lane's pipeline instead of the whole thing.
|
||||
*
|
||||
* Every node already carries the one command that drives it (`command`, added
|
||||
@@ -2599,7 +2599,7 @@ async function cmdLanesCi(args) {
|
||||
* pasted it into a session by hand. This runs it, in the lane's own cwd:
|
||||
*
|
||||
* - a slash command starts `claude` with it as the prompt;
|
||||
* - anything else (`npm test`, `agenthub lanes hook ci-gate`) is spawned directly.
|
||||
* - anything else (`npm test`, `agenthub workspace hook ci-gate`) is spawned directly.
|
||||
*
|
||||
* With no node id it lists the nodes and their commands, which is also the
|
||||
* error path's answer. `--print` shows the argv and runs nothing.
|
||||
@@ -2621,10 +2621,10 @@ async function cmdLanesNode(args) {
|
||||
const { flags, positional } = parseArgs(args);
|
||||
const resolved = await resolveLaneArg(args);
|
||||
if (!resolved) return;
|
||||
const { lane } = await get(`/api/lanes/${resolved.laneId}`);
|
||||
const { lane } = await get(`/api/workspaces/${resolved.laneId}`);
|
||||
// The lane payload's `pipeline_nodes` carries render state but not `aliases`;
|
||||
// the template does. Merge, so the listing can still mark the current node.
|
||||
const { pipelines } = await get("/api/lanes/pipelines");
|
||||
const { pipelines } = await get("/api/workspaces/pipelines");
|
||||
const stateOf = new Map((lane.pipeline_nodes || []).map((n) => [n.id, n.state]));
|
||||
const nodes = (pipelines.find((p) => p.id === lane.pipeline)?.nodes || []).map((n) => ({
|
||||
...n,
|
||||
@@ -2699,8 +2699,8 @@ async function cmdLanesNode(args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* `agenthub lanes pipeline [<template-id>] [<id>]` — show or switch which pipeline
|
||||
* template a lane renders against. `agenthub lanes add --pipeline` could only set
|
||||
* `agenthub workspace pipeline [<template-id>] [<id>]` — show or switch which pipeline
|
||||
* template a lane renders against. `agenthub workspace add --pipeline` could only set
|
||||
* this at creation time, so every lane added from the dashboard's "+ Add lane"
|
||||
* was stuck on `default` with no way back to a 16-node template.
|
||||
*/
|
||||
@@ -2710,15 +2710,15 @@ async function cmdLanesPipeline(args) {
|
||||
if (!resolved) return;
|
||||
|
||||
if (!target) {
|
||||
const { lane } = await get(`/api/lanes/${resolved.laneId}`);
|
||||
const { pipelines } = await get("/api/lanes/pipelines");
|
||||
const { lane } = await get(`/api/workspaces/${resolved.laneId}`);
|
||||
const { pipelines } = await get("/api/workspaces/pipelines");
|
||||
console.log(`workspace #${lane.id} → ${lane.pipeline} (${lane.pipeline_nodes.length} nodes)`);
|
||||
if (lane.pipeline_skill) console.log(`driver: ${lane.pipeline_skill} <requirement>`);
|
||||
console.log(`available: ${pipelines.map((p) => `${p.id} (${p.nodes.length})`).join(", ")}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const { lane } = await patch(`/api/lanes/${resolved.laneId}`, { pipeline: target });
|
||||
const { lane } = await patch(`/api/workspaces/${resolved.laneId}`, { pipeline: target });
|
||||
console.log(
|
||||
`${c.green("✔")} workspace #${lane.id} → pipeline ${lane.pipeline} ` +
|
||||
`(${lane.pipeline_nodes.length} nodes, stage ${lane.stage}, ${lane.progress}%)`
|
||||
@@ -2746,7 +2746,7 @@ async function cmdLanesPipeline(args) {
|
||||
async function cmdLanesShell(args) {
|
||||
const resolved = await resolveLaneArg(args);
|
||||
if (!resolved) return;
|
||||
const { lane } = await get(`/api/lanes/${resolved.laneId}`);
|
||||
const { lane } = await get(`/api/workspaces/${resolved.laneId}`);
|
||||
const sessionName = `agenthub-lane-${lane.id}`;
|
||||
const child = spawn("tmux", ["new-session", "-A", "-s", sessionName, "-c", lane.cwd], {
|
||||
stdio: "inherit",
|
||||
@@ -2771,7 +2771,7 @@ async function cmdFeatureShow(args) {
|
||||
const resolved = await resolveLaneArg(args.filter((a) => a !== slug));
|
||||
if (!resolved) return;
|
||||
const result = await get(
|
||||
`/api/lanes/${resolved.laneId}/features/${encodeURIComponent(slug)}`,
|
||||
`/api/workspaces/${resolved.laneId}/features/${encodeURIComponent(slug)}`,
|
||||
undefined,
|
||||
{ allowError: true }
|
||||
);
|
||||
@@ -2806,7 +2806,7 @@ async function cmdStage(args) {
|
||||
if (!resolved) return;
|
||||
const laneId = resolved.laneId;
|
||||
|
||||
const { lane } = await post(`/api/lanes/${laneId}/stage`, {
|
||||
const { lane } = await post(`/api/workspaces/${laneId}/stage`, {
|
||||
stage,
|
||||
status: flag("status"),
|
||||
evidence: flag("evidence"),
|
||||
@@ -3101,8 +3101,8 @@ falling back to http://127.0.0.1:4820.
|
||||
${c.bold("Output:")} colors auto-enable on a TTY and turn off when piped.
|
||||
Disable with --no-color or NO_COLOR=1; force with FORCE_COLOR=1 / AGENTHUB_COLOR=1.
|
||||
|
||||
${c.bold("Old name:")} ${c.bold("agenthub lanes …")} and ${c.bold("--lane")} still work — the same
|
||||
commands as ${c.bold("agenthub workspace …")} and ${c.bold("--workspace")}.
|
||||
${c.bold("Old name:")} ${c.bold("agenthub lanes …")} and ${c.bold("--lane")} were removed — use
|
||||
${c.bold("agenthub workspace …")} and ${c.bold("--workspace")}.
|
||||
|
||||
${c.bold("Note:")} agenthub talks to the local dashboard server — API-backed commands
|
||||
require it to be running (${c.bold("agenthub start")} brings one up in the background).
|
||||
@@ -3512,8 +3512,8 @@ function replHelp() {
|
||||
/** Detailed help for one command (or command prefix): every catalog entry
|
||||
* whose invocation starts with the token. Returns false if none matched. */
|
||||
function commandHelp(token) {
|
||||
// `lanes`, `workspace` and `workspaces` are one command (COMMAND_ALIASES),
|
||||
// so `help lanes` finds the `workspace …` rows and vice versa.
|
||||
// `workspace` and `workspaces` are one command (COMMAND_ALIASES), so
|
||||
// `help workspaces` finds the `workspace …` rows and vice versa.
|
||||
const canon = (words) => {
|
||||
const [head, ...tail] = words.split(" ");
|
||||
return [COMMAND_ALIASES[head] || head, ...tail].join(" ");
|
||||
@@ -3774,23 +3774,22 @@ async function cmdRepl() {
|
||||
// prompt. Handlers that need the server throw ServerDownError; the caller
|
||||
// decides whether to fall back to an offline reader or report the refusal.
|
||||
|
||||
/** Command names that are another command under the user-facing word.
|
||||
* `workspace` is the name to use; `lanes` (the handler's own name) and
|
||||
* `--lane` are the OLD names, still working but deprecated. Removing them is
|
||||
* step 4 of docs/plans/workspace-rename/README.md — not before the third
|
||||
* release after the first one that shipped these aliases. */
|
||||
const COMMAND_ALIASES = { workspace: "lanes", workspaces: "lanes" };
|
||||
/** Command names that are another command under the user-facing word. */
|
||||
const COMMAND_ALIASES = { workspaces: "workspace" };
|
||||
const canonicalArgv = (argv) =>
|
||||
COMMAND_ALIASES[argv[0]] ? [COMMAND_ALIASES[argv[0]], ...argv.slice(1)] : argv;
|
||||
|
||||
/** One stderr line when an old name is typed, so people and agents move over
|
||||
* before step 4 removes it. stderr only: scripts reading stdout see nothing. */
|
||||
function warnOldNames(argv) {
|
||||
const old = [];
|
||||
if (argv[0] === "lanes") old.push("`agenthub lanes` → `agenthub workspace`");
|
||||
if (argv.includes("--lane")) old.push("`--lane` → `--workspace`");
|
||||
if (old.length) {
|
||||
console.error(c.dim(`note: old name, still works for now — use ${old.join(", ")}`));
|
||||
/** The old names `agenthub workspace` and `--lane` were removed in step 4 of
|
||||
* docs/plans/workspace-rename/README.md. They exit 1 naming the new spelling
|
||||
* — never "Unknown command" or help, so a script written before says why. */
|
||||
function refuseOldNames(argv) {
|
||||
if (argv[0] === "lanes") {
|
||||
console.error(c.red("✖ renamed: use agenthub workspace …"));
|
||||
process.exit(1);
|
||||
}
|
||||
if (argv.includes("--lane")) {
|
||||
console.error(c.red("✖ renamed: use --workspace <id>"));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3803,7 +3802,6 @@ const COMMANDS = (() => {
|
||||
for (const [name] of items) set.add(name.split(" ")[0]);
|
||||
}
|
||||
set.add("help");
|
||||
// The old names stay typeable (and completable) for scripts written before.
|
||||
for (const [alias, canonical] of Object.entries(COMMAND_ALIASES)) set.add(alias).add(canonical);
|
||||
return [...set];
|
||||
})();
|
||||
@@ -4498,7 +4496,7 @@ async function cmdAccounts(sub, positional, flags = {}) {
|
||||
* for usage/validation failures. `cmd === "repl"` is handled by the entry
|
||||
* points, not here, to avoid recursive REPLs. */
|
||||
async function runCommand(argv) {
|
||||
warnOldNames(argv);
|
||||
refuseOldNames(argv);
|
||||
const [cmd, ...rest] = canonicalArgv(argv);
|
||||
const { flags, positional } = parseArgs(rest);
|
||||
switch (cmd) {
|
||||
@@ -4571,7 +4569,7 @@ async function runCommand(argv) {
|
||||
return cmdReinstallHooks();
|
||||
case "update-check":
|
||||
return cmdUpdateCheck();
|
||||
case "lanes":
|
||||
case "workspace":
|
||||
if (rest[0] === "add") {
|
||||
return cmdLanesAdd(rest.slice(1));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user