@dashai/cli 0.29.0 → 0.30.1
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 +24 -0
- package/dist/bin.js +305 -6
- package/dist/bin.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -837,6 +837,30 @@ The CLI persists state at `$XDG_CONFIG_HOME/dashwise/auth.json` (typically `~/.c
|
|
|
837
837
|
|
|
838
838
|
MIT
|
|
839
839
|
|
|
840
|
+
### `dashwise module ship [slug]`
|
|
841
|
+
|
|
842
|
+
Release and install in one command, for a person. It releases the latest push to the linked repository's **default branch** (or `--commit-sha <sha>`), waits for the build, then installs exactly that version the way `module install --version` does: for an update it first prints what the move does to the copy's data, asks once, and installs with the preview's `drift_hash`.
|
|
843
|
+
|
|
844
|
+
```bash
|
|
845
|
+
dashwise module ship # default-branch head of the module in module.json
|
|
846
|
+
dashwise module ship my-module --workspace acme --bump patch
|
|
847
|
+
dashwise module ship my-module --commit-sha <full-40-character-sha> --installation 156
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
Before anything is queued it prints the revision it will release (`Releasing <sha> from main`, from the server's release plan) and names newer work on another branch that is not included. If the release is still building at `--timeout` (exit 4), it prints `dashwise module ship <slug> --build <id>`, which waits for that build and then installs it, without queueing another release. `--yes` skips the install question when nothing is lost; it never accepts data loss. A move that loses data is accepted only by answering in a terminal; without one, it is refused and the refusal prints the `module install --accept-data-loss <drift_hash>` re-run line. **Refused inside an AI agent session**: an agent may release (`module release`), but taking a module live is a person's act. Exit codes are `module release`'s while building (4: still building) and `module install`'s after.
|
|
851
|
+
|
|
852
|
+
### `dashwise module build-log [slug] <buildId>`
|
|
853
|
+
|
|
854
|
+
Print a build's log as the server serves it: scrubbed (credentials, tokens, internal hosts and account ids are hidden; the count of hidden values is shown) and bounded to the newest part of the log. One positional is the build id, with the module from `module.json`.
|
|
855
|
+
|
|
856
|
+
```bash
|
|
857
|
+
dashwise module build-log 51
|
|
858
|
+
dashwise module build-log my-module 51 --follow # new lines until the build ends
|
|
859
|
+
dashwise module build-log my-module 51 --json
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
`--follow` re-reads the log while the build is queued or building and prints only what is new (every 5 s while it grows, backing off to 10 s while it does not); it exits 0 when the build succeeds, 1 when it fails, 4 at `--timeout` (default 900 s). Text written to the terminal is sanitized: escape sequences (cursor moves, window titles, OSC 8 links, OSC 52 clipboard writes) and control characters other than newline and tab are removed, keeping only colour on a terminal; `--json` prints the server's text unchanged. Needs the backend's author build logs switched on; until then it says so.
|
|
863
|
+
|
|
840
864
|
### `dashwise module release [slug]`
|
|
841
865
|
|
|
842
866
|
Release code already pushed to the module's linked GitHub repository. This uses
|
package/dist/bin.js
CHANGED
|
@@ -364,6 +364,14 @@ async function setOrigins(installationId, allowedOrigins, options = {}) {
|
|
|
364
364
|
options
|
|
365
365
|
);
|
|
366
366
|
}
|
|
367
|
+
async function getModuleBuildLog(moduleId, buildId, options = {}) {
|
|
368
|
+
return apiRequest(
|
|
369
|
+
"GET",
|
|
370
|
+
`/api/modules/${moduleId}/builds/${buildId}/log`,
|
|
371
|
+
void 0,
|
|
372
|
+
options
|
|
373
|
+
);
|
|
374
|
+
}
|
|
367
375
|
async function listInstallationSecrets(installationId, options = {}) {
|
|
368
376
|
return apiRequest(
|
|
369
377
|
"GET",
|
|
@@ -19217,8 +19225,16 @@ var MARKERS = [
|
|
|
19217
19225
|
["CODEX_SANDBOX_NETWORK_DISABLED", (v) => v.length > 0],
|
|
19218
19226
|
["CURSOR_AGENT", (v) => v.length > 0],
|
|
19219
19227
|
["DW_AGENT", (v) => v.length > 0],
|
|
19220
|
-
["DASHWISE_AGENT_SESSION", (v) => v === "1" || v === "true"]
|
|
19228
|
+
["DASHWISE_AGENT_SESSION", (v) => v === "1" || v === "true"],
|
|
19229
|
+
["CLAUDE_CODE_ENTRYPOINT", (v) => v.length > 0],
|
|
19230
|
+
["GEMINI_CLI", (v) => v === "1"],
|
|
19231
|
+
["COPILOT_AGENT_SESSION_ID", (v) => v.length > 0],
|
|
19232
|
+
["COPILOT_AGENT_JOB_ID", (v) => v.length > 0],
|
|
19233
|
+
["AI_AGENT", (v) => v.length > 0]
|
|
19221
19234
|
];
|
|
19235
|
+
MARKERS.map(
|
|
19236
|
+
([name]) => name
|
|
19237
|
+
);
|
|
19222
19238
|
function agentSessionMarker(env = process.env) {
|
|
19223
19239
|
for (const [name, test] of MARKERS) {
|
|
19224
19240
|
const value = env[name];
|
|
@@ -20787,7 +20803,10 @@ async function moduleReleaseCommand(slugArg, options) {
|
|
|
20787
20803
|
if (resuming && (options.commitSha !== void 0 || options.bump !== void 0 || options.noWait)) {
|
|
20788
20804
|
return usage2("--build only reads an existing release; do not combine it with --commit-sha, --bump or --no-wait.");
|
|
20789
20805
|
}
|
|
20790
|
-
if (!resuming &&
|
|
20806
|
+
if (!resuming && options.commitSha !== void 0 && !FULL_SHA.test(options.commitSha)) {
|
|
20807
|
+
return usage2("--commit-sha requires the full 40-character SHA already pushed to the linked repository.");
|
|
20808
|
+
}
|
|
20809
|
+
if (!resuming && options.commitSha === void 0 && !options.defaultBranchHead) {
|
|
20791
20810
|
return usage2("--commit-sha requires the full 40-character SHA already pushed to the linked repository.");
|
|
20792
20811
|
}
|
|
20793
20812
|
const bump = options.bump ?? "minor";
|
|
@@ -20810,7 +20829,7 @@ async function moduleReleaseCommand(slugArg, options) {
|
|
|
20810
20829
|
return usage2("Release creates a version. Pass --yes to confirm in non-interactive or --json mode.");
|
|
20811
20830
|
}
|
|
20812
20831
|
const answer = await select({
|
|
20813
|
-
message: `Release ${slug} at ${options.commitSha} with a ${bump} bump?`,
|
|
20832
|
+
message: `Release ${slug} at ${options.commitSha ?? "the head of its default branch"} with a ${bump} bump?`,
|
|
20814
20833
|
options: [{ value: "cancel", label: "Cancel" }, { value: "release", label: "Release this revision" }],
|
|
20815
20834
|
initialValue: "cancel"
|
|
20816
20835
|
});
|
|
@@ -20825,7 +20844,7 @@ async function moduleReleaseCommand(slugArg, options) {
|
|
|
20825
20844
|
if (!resuming) {
|
|
20826
20845
|
try {
|
|
20827
20846
|
initial = await apiRequest("POST", `/api/modules/${moduleId}/builds`, {
|
|
20828
|
-
commit_sha: pinnedSha,
|
|
20847
|
+
...pinnedSha !== void 0 ? { commit_sha: pinnedSha } : {},
|
|
20829
20848
|
version_bump: bump
|
|
20830
20849
|
}, auth);
|
|
20831
20850
|
} catch (err) {
|
|
@@ -20839,12 +20858,13 @@ async function moduleReleaseCommand(slugArg, options) {
|
|
|
20839
20858
|
if (!json && isTransientPollError(err)) info("Check the module build history in Dashwise before retrying; the release may already be queued.");
|
|
20840
20859
|
return reportRefusal(err, { module: slug }, { json, action: "Release" });
|
|
20841
20860
|
}
|
|
20842
|
-
if (!isRepositoryBuild(initial, moduleId) || initial.commit_sha.toLowerCase() !== pinnedSha) {
|
|
20861
|
+
if (!isRepositoryBuild(initial, moduleId) || pinnedSha !== void 0 && initial.commit_sha.toLowerCase() !== pinnedSha) {
|
|
20843
20862
|
return reportCliError("Release response did not confirm the requested repository revision. Check the module build history before retrying.", { json });
|
|
20844
20863
|
}
|
|
20845
20864
|
buildId = initial.id;
|
|
20846
20865
|
}
|
|
20847
20866
|
const id = buildId;
|
|
20867
|
+
options.onBuildId?.(id);
|
|
20848
20868
|
const resume = `dashwise module release ${slug} --build ${id}`;
|
|
20849
20869
|
if (!json) info(`Build #${id}. Resume checking: ${code(resume)}`);
|
|
20850
20870
|
if (options.noWait && isRepositoryBuild(initial, moduleId) && (initial.status === "queued" || initial.status === "building")) {
|
|
@@ -20883,6 +20903,13 @@ async function moduleReleaseCommand(slugArg, options) {
|
|
|
20883
20903
|
}
|
|
20884
20904
|
if (json) out(JSON.stringify(value));
|
|
20885
20905
|
else success(`Released ${slug} v${value.version} (build #${id}, revision ${pinnedSha}).`);
|
|
20906
|
+
options.onReleased?.({
|
|
20907
|
+
id,
|
|
20908
|
+
module_id: moduleId,
|
|
20909
|
+
commit_sha: pinnedSha,
|
|
20910
|
+
version: value.version,
|
|
20911
|
+
module_version_id: value.module_version_id
|
|
20912
|
+
});
|
|
20886
20913
|
return 0;
|
|
20887
20914
|
}
|
|
20888
20915
|
if (value.status === "failed") {
|
|
@@ -20924,6 +20951,248 @@ async function moduleReleaseCommand(slugArg, options) {
|
|
|
20924
20951
|
}
|
|
20925
20952
|
}
|
|
20926
20953
|
|
|
20954
|
+
// src/commands/module/build-log.ts
|
|
20955
|
+
init_api_client();
|
|
20956
|
+
init_config();
|
|
20957
|
+
init_manifest_io();
|
|
20958
|
+
init_output();
|
|
20959
|
+
init_permission_hint();
|
|
20960
|
+
var RUNNING = /* @__PURE__ */ new Set(["queued", "building"]);
|
|
20961
|
+
var FOLLOW_BASE_MS = 5e3;
|
|
20962
|
+
var FOLLOW_MAX_MS = 1e4;
|
|
20963
|
+
var MIN_OVERLAP = 64;
|
|
20964
|
+
function newLogText(printed, next) {
|
|
20965
|
+
if (printed === "") return { text: next, moved: false };
|
|
20966
|
+
if (next.startsWith(printed)) return { text: next.slice(printed.length), moved: false };
|
|
20967
|
+
const min = Math.min(MIN_OVERLAP, printed.length);
|
|
20968
|
+
const head = next.slice(0, min);
|
|
20969
|
+
for (let j = printed.indexOf(head); j !== -1; j = printed.indexOf(head, j + 1)) {
|
|
20970
|
+
const tail = printed.slice(j);
|
|
20971
|
+
if (tail.length >= min && next.startsWith(tail)) {
|
|
20972
|
+
return { text: next.slice(tail.length), moved: false };
|
|
20973
|
+
}
|
|
20974
|
+
}
|
|
20975
|
+
return { text: next, moved: true };
|
|
20976
|
+
}
|
|
20977
|
+
function sanitizeForTerminal(text4, keepColour = false) {
|
|
20978
|
+
const ESCAPE = /\u001b(?:\[[0-?]*[ -/]*[@-~]|\][\s\S]*?(?:\u0007|\u001b\\|$)|[PX^_][\s\S]*?(?:\u001b\\|$)|[ -/]*[0-~]|$)/g;
|
|
20979
|
+
const SGR = /^\u001b\[[0-9;]*m$/;
|
|
20980
|
+
return text4.replace(/\r\n/g, "\n").replace(ESCAPE, (seq) => keepColour && SGR.test(seq) ? seq : "").replace(
|
|
20981
|
+
/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g,
|
|
20982
|
+
(ch, at, all) => (
|
|
20983
|
+
// Keep the ESC that opens a kept SGR sequence; drop every other control.
|
|
20984
|
+
ch === "\x1B" && keepColour && SGR.test((/^\u001b\[[0-9;]*m/.exec(all.slice(at)) ?? [""])[0]) ? ch : ""
|
|
20985
|
+
)
|
|
20986
|
+
);
|
|
20987
|
+
}
|
|
20988
|
+
async function moduleBuildLogCommand(slugArg, buildIdArg, options) {
|
|
20989
|
+
const json = options.json;
|
|
20990
|
+
const usage2 = (message) => reportCliError(message, { json, code: "USAGE" });
|
|
20991
|
+
let slug = slugArg;
|
|
20992
|
+
let rawId = buildIdArg;
|
|
20993
|
+
if (rawId === void 0 && slug !== void 0 && /^\d+$/.test(slug)) {
|
|
20994
|
+
rawId = slug;
|
|
20995
|
+
slug = void 0;
|
|
20996
|
+
}
|
|
20997
|
+
const buildId = rawId === void 0 ? Number.NaN : Number(rawId);
|
|
20998
|
+
if (!Number.isSafeInteger(buildId) || buildId <= 0) {
|
|
20999
|
+
return usage2("Pass the build id: `dashwise module build-log [slug] <buildId>` (ids are in `dashwise module status`).");
|
|
21000
|
+
}
|
|
21001
|
+
if (json && options.follow) return usage2("--follow streams text; drop --json.");
|
|
21002
|
+
if (!slug) {
|
|
21003
|
+
try {
|
|
21004
|
+
const local = readManifest(options.dir)?.module?.slug;
|
|
21005
|
+
if (typeof local === "string" && local) slug = local;
|
|
21006
|
+
} catch {
|
|
21007
|
+
}
|
|
21008
|
+
}
|
|
21009
|
+
if (!slug) return usage2("Pass a module slug or run from a directory with a valid module.json.");
|
|
21010
|
+
const timeoutMs = (options.timeoutSec ?? 900) * 1e3;
|
|
21011
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return usage2("--timeout expects a positive number of seconds.");
|
|
21012
|
+
const cfg = readConfig();
|
|
21013
|
+
if (!isLoggedIn() || !cfg) return reportCliError("Not logged in. Run `dashwise login` first.", { json });
|
|
21014
|
+
const auth = { baseUrl: resolveApiUrl(), auth: { token: cfg.token } };
|
|
21015
|
+
let moduleId;
|
|
21016
|
+
try {
|
|
21017
|
+
const mod = await getModuleBySlug(slug, auth);
|
|
21018
|
+
moduleId = mod.id;
|
|
21019
|
+
slug = mod.slug;
|
|
21020
|
+
} catch (err) {
|
|
21021
|
+
return reportRefusal(err, { module: slug }, { json, action: "Resolving the module" });
|
|
21022
|
+
}
|
|
21023
|
+
const read = async () => {
|
|
21024
|
+
try {
|
|
21025
|
+
return await getModuleBuildLog(moduleId, buildId, auth);
|
|
21026
|
+
} catch (err) {
|
|
21027
|
+
if (err instanceof ApiError && err.code === "FEATURE_DISABLED") {
|
|
21028
|
+
return reportCliError(
|
|
21029
|
+
"Build logs are not available on this Dashwise yet (the backend serves them once its build-log scrubber is switched on).",
|
|
21030
|
+
{ json }
|
|
21031
|
+
);
|
|
21032
|
+
}
|
|
21033
|
+
return reportRefusal(err, { module: slug }, { json, action: `Reading the log of build #${buildId}` });
|
|
21034
|
+
}
|
|
21035
|
+
};
|
|
21036
|
+
const first = await read();
|
|
21037
|
+
if (typeof first === "number") return first;
|
|
21038
|
+
if (json) {
|
|
21039
|
+
out(JSON.stringify(first));
|
|
21040
|
+
return 0;
|
|
21041
|
+
}
|
|
21042
|
+
const printLog = (view, printed2) => {
|
|
21043
|
+
if (!view.available || view.text === null) return printed2;
|
|
21044
|
+
const delta = newLogText(printed2, view.text);
|
|
21045
|
+
if (delta.moved) info(dim("\u2026 (earlier lines scrolled out of the window the server keeps)"));
|
|
21046
|
+
if (delta.text) {
|
|
21047
|
+
const safe = sanitizeForTerminal(delta.text, Boolean(process.stdout.isTTY));
|
|
21048
|
+
process.stdout.write(safe.endsWith("\n") ? safe : `${safe}
|
|
21049
|
+
`);
|
|
21050
|
+
}
|
|
21051
|
+
return view.text;
|
|
21052
|
+
};
|
|
21053
|
+
if (!first.available && !(options.follow && RUNNING.has(first.status))) {
|
|
21054
|
+
info(`Build #${buildId} (${first.status}) has no log: it did not run on the build service, or the log expired.`);
|
|
21055
|
+
return 0;
|
|
21056
|
+
}
|
|
21057
|
+
if (first.truncated) info(dim("(showing the newest part of the log)"));
|
|
21058
|
+
let printed = printLog(first, "");
|
|
21059
|
+
const footer = (view) => {
|
|
21060
|
+
if (view.redactions > 0) info(dim(`${view.redactions} value(s) were hidden by the server's scrubber.`));
|
|
21061
|
+
};
|
|
21062
|
+
if (!options.follow || !RUNNING.has(first.status)) {
|
|
21063
|
+
footer(first);
|
|
21064
|
+
return 0;
|
|
21065
|
+
}
|
|
21066
|
+
const deadline = Date.now() + timeoutMs;
|
|
21067
|
+
let last = first;
|
|
21068
|
+
const base = options.pollIntervalMs ?? FOLLOW_BASE_MS;
|
|
21069
|
+
const cap = options.pollIntervalMs !== void 0 ? options.pollIntervalMs * 2 : FOLLOW_MAX_MS;
|
|
21070
|
+
let interval = base;
|
|
21071
|
+
while (Date.now() < deadline) {
|
|
21072
|
+
await new Promise(
|
|
21073
|
+
(resolve16) => setTimeout(resolve16, Math.min(interval, Math.max(0, deadline - Date.now())))
|
|
21074
|
+
);
|
|
21075
|
+
let view;
|
|
21076
|
+
try {
|
|
21077
|
+
view = await getModuleBuildLog(moduleId, buildId, auth);
|
|
21078
|
+
} catch (err) {
|
|
21079
|
+
if (isTransientPollError(err)) continue;
|
|
21080
|
+
return reportRefusal(err, { module: slug }, { json, action: `Reading the log of build #${buildId}` });
|
|
21081
|
+
}
|
|
21082
|
+
const grew = view.text !== null && view.text !== last.text;
|
|
21083
|
+
interval = grew ? base : Math.min(cap, Math.round(interval * 1.5));
|
|
21084
|
+
printed = printLog(view, printed);
|
|
21085
|
+
last = view;
|
|
21086
|
+
if (!RUNNING.has(view.status)) {
|
|
21087
|
+
footer(view);
|
|
21088
|
+
if (view.status === "failed") {
|
|
21089
|
+
fail(`Build #${buildId} failed.`);
|
|
21090
|
+
return 1;
|
|
21091
|
+
}
|
|
21092
|
+
info(`Build #${buildId}: ${view.status}.`);
|
|
21093
|
+
return 0;
|
|
21094
|
+
}
|
|
21095
|
+
}
|
|
21096
|
+
warn(`Stopped following build #${buildId} (still ${last.status}). Resume: ${code(`dashwise module build-log ${slug} ${buildId} --follow`)}`);
|
|
21097
|
+
return EXIT_TIMED_OUT;
|
|
21098
|
+
}
|
|
21099
|
+
|
|
21100
|
+
// src/commands/module/ship.ts
|
|
21101
|
+
init_api_client();
|
|
21102
|
+
init_config();
|
|
21103
|
+
init_manifest_io();
|
|
21104
|
+
init_output();
|
|
21105
|
+
init_permission_hint();
|
|
21106
|
+
async function moduleShipCommand(slugArg, options) {
|
|
21107
|
+
const marker = agentSessionMarker();
|
|
21108
|
+
if (marker !== null) {
|
|
21109
|
+
fail(
|
|
21110
|
+
`\`dashwise module ship\` takes a module live, which a person does (detected an AI agent session: ${marker}). An agent may release (${code("dashwise module release --commit-sha <sha> --yes")}); a person then installs it.`
|
|
21111
|
+
);
|
|
21112
|
+
return 2;
|
|
21113
|
+
}
|
|
21114
|
+
if (options.build !== void 0 && options.commitSha !== void 0) {
|
|
21115
|
+
fail("--build resumes an existing release; do not combine it with --commit-sha.");
|
|
21116
|
+
return 1;
|
|
21117
|
+
}
|
|
21118
|
+
const commitSha = options.build !== void 0 ? void 0 : options.commitSha ?? await defaultBranchHead(slugArg, options.dir);
|
|
21119
|
+
if (options.commitSha !== void 0) {
|
|
21120
|
+
info(`Releasing ${options.commitSha.slice(0, 12)} (--commit-sha).`);
|
|
21121
|
+
}
|
|
21122
|
+
let released = null;
|
|
21123
|
+
let buildId = options.build;
|
|
21124
|
+
const releaseExit = await moduleReleaseCommand(slugArg, {
|
|
21125
|
+
...options.dir !== void 0 ? { dir: options.dir } : {},
|
|
21126
|
+
...commitSha !== void 0 ? { commitSha } : {},
|
|
21127
|
+
...options.build !== void 0 ? { build: options.build } : {},
|
|
21128
|
+
...options.bump !== void 0 && options.build === void 0 ? { bump: options.bump } : {},
|
|
21129
|
+
...options.timeoutSec !== void 0 ? { timeoutSec: options.timeoutSec } : {},
|
|
21130
|
+
...options.pollIntervalMs !== void 0 ? { pollIntervalMs: options.pollIntervalMs } : {},
|
|
21131
|
+
// `ship` is the person's go for the release; the install below asks.
|
|
21132
|
+
yes: true,
|
|
21133
|
+
defaultBranchHead: commitSha === void 0 && options.build === void 0,
|
|
21134
|
+
onBuildId: (id) => {
|
|
21135
|
+
buildId = id;
|
|
21136
|
+
},
|
|
21137
|
+
onReleased: (build2) => {
|
|
21138
|
+
released = build2;
|
|
21139
|
+
}
|
|
21140
|
+
});
|
|
21141
|
+
if (releaseExit === EXIT_TIMED_OUT && buildId !== void 0) {
|
|
21142
|
+
info(
|
|
21143
|
+
`Resume the ship (wait for build #${buildId}, then install it): ${code(["dashwise module ship", slugArg, `--build ${buildId}`].filter(Boolean).join(" "))}`
|
|
21144
|
+
);
|
|
21145
|
+
}
|
|
21146
|
+
if (releaseExit !== 0) return releaseExit;
|
|
21147
|
+
const build = released;
|
|
21148
|
+
if (!build) {
|
|
21149
|
+
fail("The release finished without naming its version; nothing was installed.");
|
|
21150
|
+
return 1;
|
|
21151
|
+
}
|
|
21152
|
+
info(`Installing v${build.version} (build #${build.id}, revision ${build.commit_sha.slice(0, 12)}).`);
|
|
21153
|
+
return moduleInstallCommand(slugArg, {
|
|
21154
|
+
...options.dir !== void 0 ? { dir: options.dir } : {},
|
|
21155
|
+
...options.workspace !== void 0 ? { workspace: options.workspace } : {},
|
|
21156
|
+
...options.installation !== void 0 ? { installation: options.installation } : {},
|
|
21157
|
+
...options.keepDevInstall !== void 0 ? { keepDevInstall: options.keepDevInstall } : {},
|
|
21158
|
+
...options.replaceDevInstall !== void 0 ? { replaceDevInstall: options.replaceDevInstall } : {},
|
|
21159
|
+
...options.yes !== void 0 ? { yes: options.yes } : {},
|
|
21160
|
+
version: build.version
|
|
21161
|
+
});
|
|
21162
|
+
}
|
|
21163
|
+
async function defaultBranchHead(slugArg, dir) {
|
|
21164
|
+
const cfg = readConfig();
|
|
21165
|
+
let slug = slugArg;
|
|
21166
|
+
if (!slug) {
|
|
21167
|
+
try {
|
|
21168
|
+
slug = readManifest(dir)?.module?.slug;
|
|
21169
|
+
} catch {
|
|
21170
|
+
slug = void 0;
|
|
21171
|
+
}
|
|
21172
|
+
}
|
|
21173
|
+
if (!slug || !cfg || !isLoggedIn()) return void 0;
|
|
21174
|
+
const auth = { baseUrl: resolveApiUrl(), auth: { token: cfg.token } };
|
|
21175
|
+
try {
|
|
21176
|
+
const mod = await getModuleBySlug(slug, auth);
|
|
21177
|
+
const plan = await apiRequest("GET", `/api/modules/${mod.id}/builds/release-plan`, void 0, auth);
|
|
21178
|
+
const sha = plan.default_branch_sha ?? void 0;
|
|
21179
|
+
if (!sha || !/^[0-9a-f]{40}$/i.test(sha)) {
|
|
21180
|
+
info("Releasing the head of the default branch.");
|
|
21181
|
+
return void 0;
|
|
21182
|
+
}
|
|
21183
|
+
info(`Releasing ${sha.slice(0, 12)} from ${plan.default_branch ?? "the default branch"}.`);
|
|
21184
|
+
if (plan.pending_branch) {
|
|
21185
|
+
warn(
|
|
21186
|
+
`Newer work on ${plan.pending_branch.name}${plan.pending_branch.commits_ahead ? ` (${plan.pending_branch.commits_ahead} commit(s) ahead)` : ""} is not in this release: merge it to ${plan.default_branch ?? "the default branch"} first to ship it.`
|
|
21187
|
+
);
|
|
21188
|
+
}
|
|
21189
|
+
return sha.toLowerCase();
|
|
21190
|
+
} catch {
|
|
21191
|
+
info("Releasing the head of the default branch.");
|
|
21192
|
+
return void 0;
|
|
21193
|
+
}
|
|
21194
|
+
}
|
|
21195
|
+
|
|
20927
21196
|
// src/commands/module/status.ts
|
|
20928
21197
|
init_api_client();
|
|
20929
21198
|
init_config();
|
|
@@ -26874,6 +27143,36 @@ moduleCmd.command("release [slug]").description("Release an exact pushed reposit
|
|
|
26874
27143
|
timeoutSec: opts.timeout
|
|
26875
27144
|
});
|
|
26876
27145
|
});
|
|
27146
|
+
moduleCmd.command("build-log [slug] [buildId]").description(
|
|
27147
|
+
"Print a build's log, scrubbed by the server (credentials and internal hosts hidden) and bounded to its newest part. One positional is the build id (module from module.json). --follow prints new lines until the build ends."
|
|
27148
|
+
).option("--dir <path>", "Project root for module.json slug lookup (default: current directory).").option("--follow", "Keep printing new lines while the build is queued or building.").option("--timeout <seconds>", "Max seconds to follow (default 900); exit 4 means still building.", Number).option("--json", "Emit the log response as JSON (not with --follow).").action(
|
|
27149
|
+
async (slug, buildId, opts) => {
|
|
27150
|
+
process.exitCode = await moduleBuildLogCommand(slug, buildId, {
|
|
27151
|
+
...opts.dir !== void 0 ? { dir: opts.dir } : {},
|
|
27152
|
+
...opts.follow !== void 0 ? { follow: opts.follow } : {},
|
|
27153
|
+
...opts.timeout !== void 0 ? { timeoutSec: opts.timeout } : {},
|
|
27154
|
+
...opts.json !== void 0 ? { json: opts.json } : {}
|
|
27155
|
+
});
|
|
27156
|
+
}
|
|
27157
|
+
);
|
|
27158
|
+
moduleCmd.command("ship [slug]").description(
|
|
27159
|
+
"Release and install in one command, for a person: releases the latest push to the default branch (or --commit-sha), waits for the build, then installs that version, showing what the update does to the data and asking once. Refused inside an AI agent session (an agent releases; a person takes it live)."
|
|
27160
|
+
).option("--dir <path>", "Project root for module.json slug lookup (default: current directory).").option("--commit-sha <sha>", "Release this pushed revision (full 40-character SHA) instead of the default-branch head.").option("--build <id>", "Resume a ship whose release was still building: wait for that build, then install it. Never queues a release.", parsePositiveIdOption).option("--bump <direction>", "Semver bump: patch | minor | major (default minor).").option("--workspace <slug-or-id>", "Target workspace (as for module install).").option("--installation <id>", "Update exactly this production installation.", parsePositiveIdOption).option("--keep-dev-install", "Install alongside the workspace's dev install (as for module install).").option("--replace-dev-install", "Replace the workspace's dev install (as for module install).").option("-y, --yes", "Skip the install question when nothing is lost. Never accepts data loss.").option("--timeout <seconds>", "Max seconds to wait for the build (default 360); exit 4 means still building.", Number).action(
|
|
27161
|
+
async (slug, opts) => {
|
|
27162
|
+
process.exitCode = await moduleShipCommand(slug, {
|
|
27163
|
+
...opts.dir !== void 0 ? { dir: opts.dir } : {},
|
|
27164
|
+
...opts.commitSha !== void 0 ? { commitSha: opts.commitSha } : {},
|
|
27165
|
+
...opts.build !== void 0 ? { build: opts.build } : {},
|
|
27166
|
+
...opts.bump !== void 0 ? { bump: opts.bump } : {},
|
|
27167
|
+
...opts.workspace !== void 0 ? { workspace: opts.workspace } : {},
|
|
27168
|
+
...opts.installation !== void 0 ? { installation: opts.installation } : {},
|
|
27169
|
+
...opts.keepDevInstall !== void 0 ? { keepDevInstall: opts.keepDevInstall } : {},
|
|
27170
|
+
...opts.replaceDevInstall !== void 0 ? { replaceDevInstall: opts.replaceDevInstall } : {},
|
|
27171
|
+
...opts.yes !== void 0 ? { yes: opts.yes } : {},
|
|
27172
|
+
...opts.timeout !== void 0 ? { timeoutSec: opts.timeout } : {}
|
|
27173
|
+
});
|
|
27174
|
+
}
|
|
27175
|
+
);
|
|
26877
27176
|
moduleCmd.command("publish").description("Pack the current directory + upload it as a new module version.").option("--dir <path>", "Project root (default: current directory).").option("--bump <direction>", "Semver bump: patch | minor | major.").option("--dry-run", "Validate + upload but do not persist the version row.").option("-y, --yes", "Skip the confirmation prompt (for CI / scripted use).").option(
|
|
26878
27177
|
"--from-local",
|
|
26879
27178
|
"Publish local files to a module that has a linked repository (the next repository build replaces them)."
|
|
@@ -27297,7 +27596,7 @@ fieldCmd.command("set-type <table-slug> <field-slug> <new-type>").description(
|
|
|
27297
27596
|
}
|
|
27298
27597
|
);
|
|
27299
27598
|
function getVersion() {
|
|
27300
|
-
return "0.
|
|
27599
|
+
return "0.30.1";
|
|
27301
27600
|
}
|
|
27302
27601
|
function parsePositiveIdOption(value) {
|
|
27303
27602
|
if (!/^[1-9][0-9]*$/.test(value.trim())) {
|