mergestorm 0.3.63 → 0.3.66
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 +30 -10
- package/dist/api.d.ts +68 -11
- package/dist/api.js +206 -8
- package/dist/automation-catalog.d.ts +17 -3
- package/dist/automation-catalog.js +4 -1
- package/dist/cli.js +21 -19
- package/dist/client/index.d.ts +6 -4
- package/dist/client/index.js +6 -4
- package/dist/commands/account.d.ts +3 -0
- package/dist/commands/account.js +75 -0
- package/dist/commands/browse.d.ts +1 -9
- package/dist/commands/browse.js +10 -8
- package/dist/commands/dismiss.d.ts +0 -2
- package/dist/commands/dismiss.js +2 -2
- package/dist/commands/help.js +3 -2
- package/dist/commands/issue.d.ts +24 -6
- package/dist/commands/issue.js +153 -31
- package/dist/commands/login.d.ts +2 -1
- package/dist/commands/login.js +50 -14
- package/dist/commands/logout.js +6 -7
- package/dist/commands/memory.d.ts +29 -0
- package/dist/commands/memory.js +154 -0
- package/dist/commands/registry.d.ts +1 -0
- package/dist/commands/registry.js +52 -11
- package/dist/commands/review-client.d.ts +0 -3
- package/dist/commands/review-client.js +3 -3
- package/dist/commands/review.d.ts +1 -1
- package/dist/commands/review.js +1 -1
- package/dist/commands/settings.d.ts +3 -4
- package/dist/commands/settings.js +51 -5
- package/dist/commands/stack.d.ts +20 -5
- package/dist/commands/stack.js +106 -7
- package/dist/commands/whoami.js +14 -10
- package/dist/config.d.ts +56 -2
- package/dist/config.js +271 -13
- package/dist/errors.d.ts +1 -1
- package/dist/gh.d.ts +0 -4
- package/dist/gh.js +4 -4
- package/dist/git.d.ts +1 -1
- package/dist/git.js +2 -2
- package/dist/memory-api.d.ts +74 -0
- package/dist/memory-api.js +69 -0
- package/dist/node-version.d.ts +0 -2
- package/dist/node-version.js +1 -1
- package/dist/pr-body.d.ts +4 -0
- package/dist/pr-body.js +15 -0
- package/dist/repo-identity.d.ts +2 -1
- package/dist/repo-identity.js +8 -4
- package/dist/server-publish.d.ts +5 -26
- package/dist/server-publish.js +47 -40
- package/dist/stack-blockers.d.ts +15 -36
- package/dist/stack-blockers.js +69 -33
- package/dist/stack-dto.d.ts +4 -0
- package/dist/stack-dto.js +15 -2
- package/dist/stack-meta.d.ts +2 -2
- package/dist/stack-meta.js +1 -1
- package/dist/stack-watch-loop.d.ts +2 -13
- package/dist/stack-watch-loop.js +35 -14
- package/dist/stack-watch.d.ts +2 -1
- package/dist/stack-watch.js +6 -2
- package/dist/submit-pr-base.d.ts +0 -1
- package/dist/submit-pr-base.js +1 -1
- package/dist/ui/ansi.d.ts +0 -10
- package/dist/ui/ansi.js +2 -2
- package/dist/ui/banner.d.ts +3 -1
- package/dist/ui/banner.js +13 -3
- package/dist/ui/box.d.ts +0 -2
- package/dist/ui/box.js +1 -1
- package/dist/ui/prompt.d.ts +0 -11
- package/dist/ui/prompt.js +7 -7
- package/dist/ui/tabs.d.ts +2 -2
- package/dist/ui/tabs.js +1 -1
- package/dist/ui/width.d.ts +0 -9
- package/dist/ui/width.js +3 -3
- package/dist/web-api.d.ts +17 -0
- package/dist/web-api.js +30 -0
- package/package.json +3 -1
- package/skill/SKILL.md +4 -0
- package/skill/mergestorm-bounce-watch/SKILL.md +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
+
import { splitAccountFlag, withAccount } from "./config.js";
|
|
3
4
|
import { CommandError } from "./errors.js";
|
|
4
5
|
import { dispatchCommand, formatUsageBody, } from "./commands/registry.js";
|
|
5
6
|
import { isSupportedNode, unsupportedNodeMessage, } from "./node-version.js";
|
|
@@ -17,37 +18,38 @@ function usage() {
|
|
|
17
18
|
${formatUsageBody()}
|
|
18
19
|
`);
|
|
19
20
|
}
|
|
20
|
-
async function runCommand(cmd, args) {
|
|
21
|
+
async function runCommand(cmd, args, account) {
|
|
21
22
|
if (cmd === "help" || cmd === "?") {
|
|
22
23
|
usage();
|
|
23
24
|
return;
|
|
24
25
|
}
|
|
25
|
-
const ok = await dispatchCommand(cmd, args, { mode: "oneshot" });
|
|
26
|
+
const ok = await dispatchCommand(cmd, args, { mode: "oneshot", account });
|
|
26
27
|
if (!ok) {
|
|
27
28
|
usage();
|
|
28
29
|
throw new CommandError(`unknown command: ${cmd}`);
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
async function main() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
try {
|
|
34
|
+
const { account, args: argv } = splitAccountFlag(process.argv.slice(2));
|
|
35
|
+
const [cmd, ...args] = argv;
|
|
36
|
+
if (!cmd) {
|
|
37
|
+
if (input.isTTY && output.isTTY) {
|
|
38
|
+
await withAccount(account, () => runShell());
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
usage();
|
|
36
42
|
return;
|
|
37
43
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
await runCommand(cmd, args);
|
|
44
|
+
if (cmd === "-h" || cmd === "--help" || cmd === "help" || cmd === "?") {
|
|
45
|
+
usage();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (cmd === "-v" || cmd === "--version" || cmd === "version") {
|
|
49
|
+
console.log(cliVersion());
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
await runCommand(cmd, args, account);
|
|
51
53
|
}
|
|
52
54
|
catch (err) {
|
|
53
55
|
if (err instanceof CommandError) {
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { DEFAULT_API,
|
|
1
|
+
export { ACCOUNT_LIMIT, DEFAULT_API, accountNameFromMe, apiBase, configPath, keyDisplay, listAccounts, loadConfig, mergestormHome, removeAccount, resolveApiKey, saveAccount, settleAccountName, splitAccountFlag, useAccount, withAccount, type AccountSummary, type Config, type StoredAccount, } from "../config.js";
|
|
2
2
|
export { BEARER_SETTINGS, BEARER_BOOLEAN_SETTINGS_KEYS, BEARER_SETTINGS_FLAGS, BEARER_SETTINGS_KEYS, BEARER_SETTINGS_LABELS, type BearerSettingsKey, } from "../automation-catalog.js";
|
|
3
|
-
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, apiFetch, cancelMergeQueueEntry, devicePost, dismissPrFindings, dismissThreadsLine, enqueueStack, ensureUpperPark, getEnrichedStack, getMe, getPrVortexReview, getReview, getSettings, listRepoSettings, patchRepoSettings, REPO_SETTINGS_KEYS, getStackPublication, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, openStackIssue, parseRetryAfterSeconds, patchSettings, restackStack, setStackPolicy, stackPolicyBody, submitStackPublication, type ApiFetchInit, type ApiFetchResult, type JobListItem, type MeResponse, type MergeQueueBounceDetail, type MergeQueueBounceKind, type MergeQueueEnqueuedBy, type MergeQueueEnqueuedVia, type MergeQueueEntryDto, type MergeQueueEntryState, type PrDismissedFinding, type PrFindingDismissInput, type PrFindingDismissOutcome, type PrFindingDismissResult, type PrReviewFinding, type PrReviewFindings, type PrVortexReview, type SettingsPatch, type RepoSettings, type RepoSettingsKey, type RepoSettingsPatch, type SettingsResponse, type StackDto, type StackPolicyPatch, type StackPublication, type StackPublicationRequest, type StackPublicationStatus, type ThreadDetail, type ThreadListItem, } from "../api.js";
|
|
3
|
+
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, accountLabel, apiFetch, cancelMergeQueueEntry, devicePost, dismissPrFindings, dismissThreadsLine, enqueueStack, ensureUpperPark, getEnrichedStack, getIssueReview, ISSUE_REVIEW_DEFAULT_TIMEOUT_MS, ISSUE_REVIEW_POLL_INTERVAL_MS, ISSUE_REVIEW_STATES, IssueReviewTimeoutError, issueNotApprovedError, issueNotApprovedRefusal, issueReviewLines, parseIssueReviewStatus, pollIssueReview, type IssueReviewPoint, type IssueReviewState, type IssueReviewStatus, type IssueReviewTarget, type PollIssueReviewOptions, getMe, getPrVortexReview, revokeOwnKey, getReview, getSettings, listRepoSettings, patchRepoSettings, REPO_SETTINGS_KEYS, getStackPublication, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, openStackIssue, parseRetryAfterSeconds, patchSettings, restackStack, setStackPolicy, stackPolicyBody, submitStackPublication, type ApiFetchInit, type ApiFetchResult, type JobListItem, type AccountIdentity, type MeResponse, type MergeQueueBounceDetail, type MergeQueueBounceKind, type MergeQueueEnqueuedBy, type MergeQueueEnqueuedVia, type MergeQueueEntryDto, type MergeQueueEntryState, type PrDismissedFinding, type PrFindingDismissInput, type PrFindingDismissOutcome, type PrFindingDismissResult, type PrReviewFinding, type PrReviewFindings, type PrVortexReview, type SettingsPatch, type RepoSettings, type RepoSettingsKey, type RepoSettingsPatch, type SettingsResponse, type StackDto, type StackPolicyPatch, type StackPublication, type StackPublicationRequest, type StackPublicationStatus, type ThreadDetail, type ThreadListItem, } from "../api.js";
|
|
4
4
|
export { CommandError, DetachedError, RATE_LIMITED_HINT, REVIEW_EXIT, isCommandErrorCode, rateLimitedMessage, type CommandErrorCode, type CommandErrorExtras, } from "../errors.js";
|
|
5
5
|
export { REVIEW_POLL_BACKOFF_MS, REVIEW_POLL_DEFAULT_TIMEOUT_MS, REVIEW_POLL_INTERVAL_MS, REVIEW_POLL_MAX_INTERVAL_MS, REVIEW_POLL_MAX_TRANSIENT_RETRIES, REVIEW_POLL_STRETCH_AFTER_MS, REVIEW_POLL_STRETCH_SPAN_MS, REVIEW_THREAD_SLUG_RE, PrReviewPollTimeoutError, ReviewPollTimeoutError, noPrReviewYetMessage, collectReviewInput, formatReviewSubmitError, isTransientReviewPollError, isTransientReviewPollStatus, loadReviewContext, pollPrVortexReview, pollReview, stretchedPollIntervalMs, submitReview, transientRetryWaitMs, type PollReviewOptions, type PollPrVortexReviewOptions, type ReviewInput, type SubmitReviewOptions, } from "../commands/review-client.js";
|
|
6
6
|
export { REVIEW_JOB_ENVELOPE_SCHEMA, toReviewJobEnvelope, type ReviewEnvelopeFallbacks, type ReviewJobEnvelope, type ReviewJobRow, } from "../ui/job-envelope.js";
|
|
@@ -8,6 +8,8 @@ export { discoverTrunk } from "../git-stack.js";
|
|
|
8
8
|
export { parseGithubOriginRepo } from "../git.js";
|
|
9
9
|
export { BOUNCE_KIND_LABELS, deriveStackAgentsBusy, mergeQueueBounceLabel, type StackAgentsBusy } from "../stack-dto.js";
|
|
10
10
|
export { STACK_WATCH_SCHEMA, STACK_WATCH_DEFAULT_TIMEOUT_MS, STACK_WATCH_BUSY_RECHECK_S, StackWatchError, StackWatchTimeoutError, pollStackWatch, type PollStackWatchOptions, type StackWatchCursor, type StackWatchEnvelope, } from "../stack-watch.js";
|
|
11
|
-
export { stackBlockers, stackBlockersSummary, sameHead, layerAgents, layerAgentsBusy, conflictLiveParent, stackRepair, agentsCannotClear, autoFixCiActive, autoResolveActive, type StackAgentBusy, type StackBlocker, type StackBusyAgent, type StackHeldBlocker, type StackLandGatePending, type StackLayerAgents, type StackRepairHint, } from "../stack-blockers.js";
|
|
11
|
+
export { BREEZE_BRANCH_PREFIX, stackBlockers, stackBlockersSummary, sameHead, layerAgents, layerAgentsBusy, conflictLiveParent, stackRepair, agentsCannotClear, autoFixCiActive, autoResolveActive, type StackAgentBusy, type StackBlocker, type StackBusyAgent, type StackHeldBlocker, type StackLandGatePending, type StackLayerAgents, type StackRepairHint, } from "../stack-blockers.js";
|
|
12
12
|
export { STACK_WATCH_NOT_DONE_SENTENCE, STACK_WATCH_NOTIFY_PATTERN, stackWatchBackgroundCommand, stackTerminalReason, stackWatchNextArgs, stackWatchObligation, type StackWatchDoneReason, type StackWatchNext, type StackWatchNextArgs, type StackWatchObligation, type StackWatchObligationInput, type StackWatchReason, } from "../stack-watch-obligation.js";
|
|
13
|
-
export { publishBranchServerSide, publishWorkingBranch, workingBranchPublication, type ServerPublishResult, type WorkingBranchInput, } from "../server-publish.js";
|
|
13
|
+
export { READ_ONLY_GIT, publishBranchServerSide, publishWorkingBranch, workingBranchPublication, type GitRead, type ServerPublishDeps, type ServerPublishInput, type ServerPublishResult, type WorkingBranchInput, } from "../server-publish.js";
|
|
14
|
+
export { MEMORY_KINDS, MEMORY_MODES, MEMORY_SCOPES, MEMORY_SEARCH_LIMIT_MAX, deleteMemory, getMemoryMode, listMemories, memoryChatId, saveMemory, searchMemories, setMemoryMode, updateMemory, type MemoryItem, type MemoryKind, type MemoryList, type MemoryMode, type MemoryScope, type MemorySearch, } from "../memory-api.js";
|
|
15
|
+
export { WEB_SEARCH_KINDS, WEB_SEARCH_PAGE_MAX, WEB_SEARCH_RECENCIES, readWebPage, searchWeb, type WebSearchInput, type WebText, } from "../web-api.js";
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { DEFAULT_API,
|
|
1
|
+
export { ACCOUNT_LIMIT, DEFAULT_API, accountNameFromMe, apiBase, configPath, keyDisplay, listAccounts, loadConfig, mergestormHome, removeAccount, resolveApiKey, saveAccount, settleAccountName, splitAccountFlag, useAccount, withAccount, } from "../config.js";
|
|
2
2
|
export { BEARER_SETTINGS, BEARER_BOOLEAN_SETTINGS_KEYS, BEARER_SETTINGS_FLAGS, BEARER_SETTINGS_KEYS, BEARER_SETTINGS_LABELS, } from "../automation-catalog.js";
|
|
3
|
-
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, apiFetch, cancelMergeQueueEntry, devicePost, dismissPrFindings, dismissThreadsLine, enqueueStack, ensureUpperPark, getEnrichedStack, getMe, getPrVortexReview, getReview, getSettings, listRepoSettings, patchRepoSettings, REPO_SETTINGS_KEYS, getStackPublication, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, openStackIssue, parseRetryAfterSeconds, patchSettings, restackStack, setStackPolicy, stackPolicyBody, submitStackPublication, } from "../api.js";
|
|
3
|
+
export { API_TIMEOUT_PREFIX, DEFAULT_API_TIMEOUT_MS, SETTINGS_WRITABLE_KEYS, adoptStack, accountLabel, apiFetch, cancelMergeQueueEntry, devicePost, dismissPrFindings, dismissThreadsLine, enqueueStack, ensureUpperPark, getEnrichedStack, getIssueReview, ISSUE_REVIEW_DEFAULT_TIMEOUT_MS, ISSUE_REVIEW_POLL_INTERVAL_MS, ISSUE_REVIEW_STATES, IssueReviewTimeoutError, issueNotApprovedError, issueNotApprovedRefusal, issueReviewLines, parseIssueReviewStatus, pollIssueReview, getMe, getPrVortexReview, revokeOwnKey, getReview, getSettings, listRepoSettings, patchRepoSettings, REPO_SETTINGS_KEYS, getStackPublication, landNextStack, listJobs, listMergeQueueEntries, listStacks, listThreads, openStackIssue, parseRetryAfterSeconds, patchSettings, restackStack, setStackPolicy, stackPolicyBody, submitStackPublication, } from "../api.js";
|
|
4
4
|
export { CommandError, DetachedError, RATE_LIMITED_HINT, REVIEW_EXIT, isCommandErrorCode, rateLimitedMessage, } from "../errors.js";
|
|
5
5
|
export { REVIEW_POLL_BACKOFF_MS, REVIEW_POLL_DEFAULT_TIMEOUT_MS, REVIEW_POLL_INTERVAL_MS, REVIEW_POLL_MAX_INTERVAL_MS, REVIEW_POLL_MAX_TRANSIENT_RETRIES, REVIEW_POLL_STRETCH_AFTER_MS, REVIEW_POLL_STRETCH_SPAN_MS, REVIEW_THREAD_SLUG_RE, PrReviewPollTimeoutError, ReviewPollTimeoutError, noPrReviewYetMessage, collectReviewInput, formatReviewSubmitError, isTransientReviewPollError, isTransientReviewPollStatus, loadReviewContext, pollPrVortexReview, pollReview, stretchedPollIntervalMs, submitReview, transientRetryWaitMs, } from "../commands/review-client.js";
|
|
6
6
|
export { REVIEW_JOB_ENVELOPE_SCHEMA, toReviewJobEnvelope, } from "../ui/job-envelope.js";
|
|
@@ -8,6 +8,8 @@ export { discoverTrunk } from "../git-stack.js";
|
|
|
8
8
|
export { parseGithubOriginRepo } from "../git.js";
|
|
9
9
|
export { BOUNCE_KIND_LABELS, deriveStackAgentsBusy, mergeQueueBounceLabel } from "../stack-dto.js";
|
|
10
10
|
export { STACK_WATCH_SCHEMA, STACK_WATCH_DEFAULT_TIMEOUT_MS, STACK_WATCH_BUSY_RECHECK_S, StackWatchError, StackWatchTimeoutError, pollStackWatch, } from "../stack-watch.js";
|
|
11
|
-
export { stackBlockers, stackBlockersSummary, sameHead, layerAgents, layerAgentsBusy, conflictLiveParent, stackRepair, agentsCannotClear, autoFixCiActive, autoResolveActive, } from "../stack-blockers.js";
|
|
11
|
+
export { BREEZE_BRANCH_PREFIX, stackBlockers, stackBlockersSummary, sameHead, layerAgents, layerAgentsBusy, conflictLiveParent, stackRepair, agentsCannotClear, autoFixCiActive, autoResolveActive, } from "../stack-blockers.js";
|
|
12
12
|
export { STACK_WATCH_NOT_DONE_SENTENCE, STACK_WATCH_NOTIFY_PATTERN, stackWatchBackgroundCommand, stackTerminalReason, stackWatchNextArgs, stackWatchObligation, } from "../stack-watch-obligation.js";
|
|
13
|
-
export { publishBranchServerSide, publishWorkingBranch, workingBranchPublication, } from "../server-publish.js";
|
|
13
|
+
export { READ_ONLY_GIT, publishBranchServerSide, publishWorkingBranch, workingBranchPublication, } from "../server-publish.js";
|
|
14
|
+
export { MEMORY_KINDS, MEMORY_MODES, MEMORY_SCOPES, MEMORY_SEARCH_LIMIT_MAX, deleteMemory, getMemoryMode, listMemories, memoryChatId, saveMemory, searchMemories, setMemoryMode, updateMemory, } from "../memory-api.js";
|
|
15
|
+
export { WEB_SEARCH_KINDS, WEB_SEARCH_PAGE_MAX, WEB_SEARCH_RECENCIES, readWebPage, searchWeb, } from "../web-api.js";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { revokeOwnKey } from "../api.js";
|
|
2
|
+
import { ACCOUNT_LIMIT, apiBase, configFor, listAccounts, readStore, removeAccount, useAccount, } from "../config.js";
|
|
3
|
+
import { CommandError } from "../errors.js";
|
|
4
|
+
import { ansi } from "../ui/ansi.js";
|
|
5
|
+
import { present } from "../ui/present.js";
|
|
6
|
+
export const ACCOUNT_USAGE = "usage: mergestorm account list [--json] | use <name> | remove <name>";
|
|
7
|
+
export async function logoutAccount(name) {
|
|
8
|
+
const store = await readStore();
|
|
9
|
+
const cfg = configFor(store, Object.hasOwn(store.accounts, name) ? name : undefined);
|
|
10
|
+
if (!cfg.account)
|
|
11
|
+
await removeAccount(name);
|
|
12
|
+
cfg.apiBase = apiBase(cfg);
|
|
13
|
+
cfg.ignoreEnv = true;
|
|
14
|
+
const revoked = await revokeOwnKey(cfg, { timeoutMs: 8_000 });
|
|
15
|
+
const after = await removeAccount(name);
|
|
16
|
+
const lines = [
|
|
17
|
+
revoked
|
|
18
|
+
? ` Logged out ${name}. Its API key is revoked.`
|
|
19
|
+
: ` Logged out ${name}. Its API key could not be revoked; revoke it at https://mergestorm.ai/settings#api if it should not stay valid.`,
|
|
20
|
+
];
|
|
21
|
+
if (after.activeAccount)
|
|
22
|
+
lines.push(` Active account: ${after.activeAccount}`);
|
|
23
|
+
else
|
|
24
|
+
lines.push(" No accounts left. Run `mergestorm login` to sign in.");
|
|
25
|
+
return lines;
|
|
26
|
+
}
|
|
27
|
+
async function accountList(asJson) {
|
|
28
|
+
const accounts = await listAccounts();
|
|
29
|
+
if (asJson) {
|
|
30
|
+
console.log(JSON.stringify({ active: accounts.find((a) => a.active)?.name ?? null, limit: ACCOUNT_LIMIT, accounts }, null, 2));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (accounts.length === 0) {
|
|
34
|
+
await present("Accounts", [" No accounts. Run `mergestorm login` to sign in."]);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const width = Math.max(...accounts.map((a) => a.name.length));
|
|
38
|
+
const lines = accounts.map((a) => {
|
|
39
|
+
const mark = a.active ? ansi.brightGreen("●") : ansi.dim("○");
|
|
40
|
+
const name = a.active ? ansi.bold(a.name.padEnd(width)) : a.name.padEnd(width);
|
|
41
|
+
return ` ${mark} ${name} ${ansi.dim(a.keyPrefix)} ${ansi.dim(a.apiBase)}`;
|
|
42
|
+
});
|
|
43
|
+
lines.push("", ` ${ansi.dim(`${accounts.length} of ${ACCOUNT_LIMIT} · account use <name> switches · --account <name> runs one command as another`)}`);
|
|
44
|
+
await present("Accounts", lines);
|
|
45
|
+
}
|
|
46
|
+
function nameArg(rest, verb) {
|
|
47
|
+
const name = rest.find((arg) => !arg.startsWith("-"));
|
|
48
|
+
if (!name) {
|
|
49
|
+
throw new CommandError(`mergestorm account ${verb} needs an account name.\n${ACCOUNT_USAGE}`, 1, "usage");
|
|
50
|
+
}
|
|
51
|
+
return name;
|
|
52
|
+
}
|
|
53
|
+
export async function cmdAccount(args) {
|
|
54
|
+
const asJson = args.includes("--json");
|
|
55
|
+
const [sub = "list", ...rest] = args.filter((arg) => arg !== "--json");
|
|
56
|
+
if (sub === "list") {
|
|
57
|
+
await accountList(asJson);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (sub === "use") {
|
|
61
|
+
const name = nameArg(rest, "use");
|
|
62
|
+
await useAccount(name);
|
|
63
|
+
await present("Account", [` Active account: ${name}`]);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (sub === "remove") {
|
|
67
|
+
await present("Account", await logoutAccount(nameArg(rest, "remove")));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (sub === "--help" || sub === "-h" || sub === "help") {
|
|
71
|
+
console.log(ACCOUNT_USAGE);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
throw new CommandError(`unknown account subcommand: ${sub}\n${ACCOUNT_USAGE}`, 1, "usage");
|
|
75
|
+
}
|
|
@@ -7,6 +7,7 @@ export declare function canBrowse(): boolean;
|
|
|
7
7
|
/** Shared job row mapper for the Usage tab and the static `credits` panel. */
|
|
8
8
|
export declare function toPanelJob(job: JobListItem): UsagePanelJob;
|
|
9
9
|
export type StatusTabInput = {
|
|
10
|
+
account?: string | null;
|
|
10
11
|
keyPrefix: string;
|
|
11
12
|
keyName: string | null;
|
|
12
13
|
plan: string | null;
|
|
@@ -20,19 +21,10 @@ export type StatusTabInput = {
|
|
|
20
21
|
resetsAt?: string | null;
|
|
21
22
|
now?: Date;
|
|
22
23
|
};
|
|
23
|
-
/** Short chrome note — lives on the hint row, not in the Status body. */
|
|
24
|
-
export declare function statusReachabilityNote(input: Pick<StatusTabInput, "reachable" | "keyInvalid">): string;
|
|
25
24
|
/** Status tab body: whoami + credits from `/me`. Reachability is the footer. */
|
|
26
25
|
export declare function buildStatusTabLines(input: StatusTabInput): string[];
|
|
27
26
|
/** In-place job detail for the Jobs tab (same envelope as `status <id>`). */
|
|
28
27
|
export declare function formatJobDetailLines(row: ReviewJobRow): string[];
|
|
29
|
-
/** Human labels for the Config tab and the static `settings` printout. */
|
|
30
|
-
export declare const SETTINGS_LABELS: Record<"auto_review_enabled" | "auto_patch_enabled" | "auto_resolve_conflicts_enabled" | "auto_fix_ci_enabled" | "vortex_show_thinking_traces" | "repo_overview_enabled" | "cyclone_skip_ci_enabled" | "cyclone_patch_unverified_languages" | "vortex_auto_overflow_enabled" | "vortex_skip_all_clear_comments" | "vortex_seam_specialist_enabled" | "auto_land_default" | "auto_land_settle_seconds" | "merge_queue_batch_enabled" | "merge_queue_batch_size" | "ignored_bot_logins" | "vortex_bot_skip_check" | "vortex_findings_check" | "cyclone_patch_failure_check", string>;
|
|
31
|
-
export declare const STACKS_READY_LABEL = "Stacks, merge queue, Auto land";
|
|
32
|
-
export declare const STACKS_READY_WORDS: {
|
|
33
|
-
readonly on: "ready";
|
|
34
|
-
readonly off: "not ready (install Mergestorm Surge)";
|
|
35
|
-
};
|
|
36
28
|
/** Config rows in a stable order: writable toggles, then connected flags. */
|
|
37
29
|
export declare function buildConfigRows(settings: SettingsResponse): TabsConfigRow[];
|
|
38
30
|
/**
|
package/dist/commands/browse.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getMe, getReview, getSettings, listJobs, patchSettings, } from "../api.js";
|
|
2
2
|
import { BEARER_BOOLEAN_SETTINGS_KEYS, BEARER_SETTINGS_LABELS } from "../automation-catalog.js";
|
|
3
|
-
import { apiBase, keyDisplay, loadConfig, resolveApiKey } from "../config.js";
|
|
3
|
+
import { apiBase, keyDisplay, loadConfig, resolveApiKey, settleAccountName } from "../config.js";
|
|
4
4
|
import { CommandError } from "../errors.js";
|
|
5
5
|
import { ansi } from "../ui/ansi.js";
|
|
6
6
|
import { humanSummary } from "../ui/human-summary.js";
|
|
@@ -23,7 +23,7 @@ export function toPanelJob(job) {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
/** Short chrome note — lives on the hint row, not in the Status body. */
|
|
26
|
-
|
|
26
|
+
function statusReachabilityNote(input) {
|
|
27
27
|
if (input.keyInvalid)
|
|
28
28
|
return "key revoked";
|
|
29
29
|
if (!input.reachable)
|
|
@@ -50,9 +50,10 @@ function creditsValue(input) {
|
|
|
50
50
|
}
|
|
51
51
|
/** Status tab body: whoami + credits from `/me`. Reachability is the footer. */
|
|
52
52
|
export function buildStatusTabLines(input) {
|
|
53
|
-
const lines = [
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const lines = [];
|
|
54
|
+
if (input.account)
|
|
55
|
+
lines.push(field("Account", ansi.bold(input.account)));
|
|
56
|
+
lines.push(field("Key", `${ansi.bold(input.keyPrefix)}${input.keyName ? ` (${input.keyName})` : ""}`));
|
|
56
57
|
if (input.plan)
|
|
57
58
|
lines.push(field("Plan", input.plan));
|
|
58
59
|
lines.push(field("API", input.apiBase));
|
|
@@ -120,9 +121,9 @@ export function formatJobDetailLines(row) {
|
|
|
120
121
|
return lines;
|
|
121
122
|
}
|
|
122
123
|
/** Human labels for the Config tab and the static `settings` printout. */
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
const SETTINGS_LABELS = BEARER_SETTINGS_LABELS;
|
|
125
|
+
const STACKS_READY_LABEL = "Stacks, merge queue, Auto land";
|
|
126
|
+
const STACKS_READY_WORDS = {
|
|
126
127
|
on: "ready",
|
|
127
128
|
off: "not ready (install Mergestorm Surge)",
|
|
128
129
|
};
|
|
@@ -219,6 +220,7 @@ export async function openTabsBrowser(initial) {
|
|
|
219
220
|
}
|
|
220
221
|
const data = {
|
|
221
222
|
statusLines: buildStatusTabLines({
|
|
223
|
+
account: me ? await settleAccountName(cfg, me) : cfg.account,
|
|
222
224
|
keyPrefix: me?.key.prefix ?? keyDisplay(key) ?? "msk_live_…",
|
|
223
225
|
keyName: me?.key.name ?? null,
|
|
224
226
|
plan: me ? (me.plan_label_key ?? me.plan_key) : null,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { type PrFindingDismissInput, type PrFindingDismissOutcome, type PrFindingDismissResult } from "../api.js";
|
|
2
2
|
import { type Config } from "../config.js";
|
|
3
3
|
import { type StackWatchObligation } from "../stack-watch-obligation.js";
|
|
4
|
-
export declare const DISMISS_USAGE: string;
|
|
5
4
|
export type ParsedDismissArgs = PrFindingDismissInput & {
|
|
6
5
|
json: boolean;
|
|
7
6
|
};
|
|
8
7
|
export declare function parseDismissArgs(args: string[]): ParsedDismissArgs;
|
|
9
|
-
export declare function dismissWatch(result: PrFindingDismissResult): StackWatchObligation | null;
|
|
10
8
|
export declare function formatDismissResult(result: PrFindingDismissResult, watch: StackWatchObligation | null): string;
|
|
11
9
|
export type DismissDeps = {
|
|
12
10
|
loadConfig?: () => Promise<Config>;
|
package/dist/commands/dismiss.js
CHANGED
|
@@ -3,7 +3,7 @@ import { loadConfig } from "../config.js";
|
|
|
3
3
|
import { CommandError, REVIEW_EXIT } from "../errors.js";
|
|
4
4
|
import { stackWatchObligation } from "../stack-watch-obligation.js";
|
|
5
5
|
import { parseAdoptTarget, withWatch } from "./stack.js";
|
|
6
|
-
|
|
6
|
+
const DISMISS_USAGE = [
|
|
7
7
|
"usage: mergestorm dismiss <owner/repo>#<n> --head <sha> --review <id>",
|
|
8
8
|
" (--finding <id> [--finding <id>…] | --all) --reason <text> [--evidence <url>] [--json]",
|
|
9
9
|
" mergestorm dismiss <owner/repo>#<n> --head <sha> --review <id> --preview [--json]",
|
|
@@ -84,7 +84,7 @@ export function parseDismissArgs(args) {
|
|
|
84
84
|
json,
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
function dismissWatch(result) {
|
|
88
88
|
if (!result.stack_id)
|
|
89
89
|
return null;
|
|
90
90
|
return stackWatchObligation({ stackId: result.stack_id, terminal: null, unread: true, freshCursor: true });
|
package/dist/commands/help.js
CHANGED
|
@@ -41,9 +41,10 @@ export function buildHelpTabs() {
|
|
|
41
41
|
"",
|
|
42
42
|
rule("Account"),
|
|
43
43
|
cmd("login", "sign in (--key; agents: --start)"),
|
|
44
|
-
cmd("whoami", "key · plan · API"),
|
|
44
|
+
cmd("whoami", "account · key · plan · API"),
|
|
45
|
+
cmd("account", "list · use <name> · remove <name>"),
|
|
45
46
|
cmd("settings", "automation toggles · Config tab"),
|
|
46
|
-
cmd("logout", "
|
|
47
|
+
cmd("logout", "log out of the active account"),
|
|
47
48
|
cmd("clear / exit", "reprint home · leave the shell"),
|
|
48
49
|
],
|
|
49
50
|
},
|
package/dist/commands/issue.d.ts
CHANGED
|
@@ -1,23 +1,41 @@
|
|
|
1
|
-
import { openStackIssue } from "../api.js";
|
|
1
|
+
import { openStackIssue, pollIssueReview, type IssueReviewStatus } from "../api.js";
|
|
2
2
|
import { loadConfig } from "../config.js";
|
|
3
3
|
import { parseGithubOriginRepo } from "../git.js";
|
|
4
|
-
|
|
4
|
+
type RepoTarget = {
|
|
5
|
+
owner: string;
|
|
6
|
+
repo: string;
|
|
7
|
+
};
|
|
5
8
|
export type ParsedIssueArgs = {
|
|
6
9
|
title: string;
|
|
7
10
|
body: string;
|
|
8
11
|
labels: string[];
|
|
9
|
-
repo?:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
repo?: RepoTarget;
|
|
13
|
+
json: boolean;
|
|
14
|
+
wait?: boolean;
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
};
|
|
17
|
+
export type ParsedIssueWaitArgs = {
|
|
18
|
+
number: number;
|
|
19
|
+
repo?: RepoTarget;
|
|
20
|
+
timeoutMs: number;
|
|
13
21
|
json: boolean;
|
|
14
22
|
};
|
|
15
23
|
export declare function parseIssueCreateArgs(args: string[], readBody?: (path: string) => string): ParsedIssueArgs;
|
|
24
|
+
export declare function parseIssueWaitArgs(args: string[]): ParsedIssueWaitArgs;
|
|
16
25
|
export type IssueDeps = {
|
|
17
26
|
cwd?: string;
|
|
18
27
|
loadConfig?: typeof loadConfig;
|
|
19
28
|
openStackIssue?: typeof openStackIssue;
|
|
29
|
+
pollIssueReview?: typeof pollIssueReview;
|
|
20
30
|
parseGithubOriginRepo?: typeof parseGithubOriginRepo;
|
|
21
31
|
readBody?: (path: string) => string;
|
|
22
32
|
};
|
|
33
|
+
export declare function waitForIssueReview(target: RepoTarget & {
|
|
34
|
+
number: number;
|
|
35
|
+
}, opts: {
|
|
36
|
+
timeoutMs: number;
|
|
37
|
+
json: boolean;
|
|
38
|
+
jsonShape?: (status: IssueReviewStatus) => unknown;
|
|
39
|
+
}, deps?: IssueDeps): Promise<void>;
|
|
23
40
|
export declare function cmdIssue(args: string[], deps?: IssueDeps): Promise<void>;
|
|
41
|
+
export {};
|
package/dist/commands/issue.js
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { openStackIssue } from "../api.js";
|
|
2
|
+
import { ISSUE_REVIEW_DEFAULT_TIMEOUT_MS, IssueReviewTimeoutError, issueNotApprovedError, issueReviewLines, openStackIssue, pollIssueReview, } from "../api.js";
|
|
3
3
|
import { loadConfig } from "../config.js";
|
|
4
4
|
import { CommandError, REVIEW_EXIT } from "../errors.js";
|
|
5
5
|
import { parseGithubOriginRepo } from "../git.js";
|
|
6
|
-
|
|
6
|
+
const ISSUE_USAGE = [
|
|
7
7
|
"usage: mergestorm issue create --title <title> [--body <text> | --body-file <path>]",
|
|
8
|
-
" [--label <name>]… [--repo <owner/repo>] [--json]",
|
|
8
|
+
" [--label <name>]… [--repo <owner/repo>] [--wait [--timeout <seconds>]] [--json]",
|
|
9
9
|
" Opens a GitHub issue through Mergestorm as the Surge App. Needs no gh and no GitHub login.",
|
|
10
10
|
" Uses existing labels only. Running it again with the same title returns the open issue it already made when that issue is among the newest 100 open issues; an older one, or two calls at once, can still create a duplicate.",
|
|
11
|
+
" --wait waits for the Vortex issue review afterwards, like issue wait.",
|
|
12
|
+
" mergestorm issue wait <number> [--repo <owner/repo>] [--timeout <seconds>] [--json]",
|
|
13
|
+
` Waits for the Vortex review of the issue (default ${ISSUE_REVIEW_DEFAULT_TIMEOUT_MS / 1000}s). Exits 0 once the issue is approved or issue reviews are off.`,
|
|
14
|
+
` Exits ${REVIEW_EXIT.failed} when the review has points to answer: reply in a comment on the issue, then wait again. Open the pull request only after approval.`,
|
|
11
15
|
].join("\n");
|
|
12
|
-
const VALUE_FLAGS = new Set(["--title", "--body", "--body-file", "--label", "--repo"]);
|
|
16
|
+
const VALUE_FLAGS = new Set(["--title", "--body", "--body-file", "--label", "--repo", "--timeout"]);
|
|
13
17
|
function usage(message) {
|
|
14
18
|
return new CommandError(message ? `${message}\n${ISSUE_USAGE}` : ISSUE_USAGE, REVIEW_EXIT.usage, "usage");
|
|
15
19
|
}
|
|
20
|
+
function flagValue(flag, raw) {
|
|
21
|
+
if (raw === undefined || raw.startsWith("--"))
|
|
22
|
+
throw usage(`${flag} needs a value`);
|
|
23
|
+
return raw;
|
|
24
|
+
}
|
|
25
|
+
function parseRepo(raw) {
|
|
26
|
+
const match = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(raw.trim());
|
|
27
|
+
if (!match)
|
|
28
|
+
throw usage("--repo needs <owner/repo>");
|
|
29
|
+
return { owner: match[1], repo: match[2] };
|
|
30
|
+
}
|
|
31
|
+
function parseTimeoutMs(raw) {
|
|
32
|
+
const seconds = Number(raw);
|
|
33
|
+
if (!Number.isInteger(seconds) || seconds < 1)
|
|
34
|
+
throw usage("--timeout needs a whole number of seconds, at least 1");
|
|
35
|
+
return seconds * 1000;
|
|
36
|
+
}
|
|
16
37
|
export function parseIssueCreateArgs(args, readBody = (path) => readFileSync(path, "utf8")) {
|
|
17
38
|
let title = "";
|
|
18
39
|
let body;
|
|
@@ -20,33 +41,30 @@ export function parseIssueCreateArgs(args, readBody = (path) => readFileSync(pat
|
|
|
20
41
|
let repo;
|
|
21
42
|
const labels = [];
|
|
22
43
|
let json = false;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
throw usage(`${flag} needs a value`);
|
|
26
|
-
return raw;
|
|
27
|
-
};
|
|
44
|
+
let wait = false;
|
|
45
|
+
let timeoutMs;
|
|
28
46
|
for (let i = 0; i < args.length; i++) {
|
|
29
47
|
const arg = args[i];
|
|
30
48
|
if (arg === "--title")
|
|
31
|
-
title =
|
|
49
|
+
title = flagValue(arg, args[++i]).trim();
|
|
32
50
|
else if (arg === "--body")
|
|
33
|
-
body =
|
|
51
|
+
body = flagValue(arg, args[++i]);
|
|
34
52
|
else if (arg === "--body-file")
|
|
35
|
-
bodyFile =
|
|
53
|
+
bodyFile = flagValue(arg, args[++i]);
|
|
36
54
|
else if (arg === "--label") {
|
|
37
|
-
const label =
|
|
55
|
+
const label = flagValue(arg, args[++i]).trim();
|
|
38
56
|
if (!label)
|
|
39
57
|
throw usage("--label needs a non-empty name");
|
|
40
58
|
labels.push(label);
|
|
41
59
|
}
|
|
42
|
-
else if (arg === "--repo")
|
|
43
|
-
|
|
44
|
-
if (!match)
|
|
45
|
-
throw usage("--repo needs <owner/repo>");
|
|
46
|
-
repo = { owner: match[1], repo: match[2] };
|
|
47
|
-
}
|
|
60
|
+
else if (arg === "--repo")
|
|
61
|
+
repo = parseRepo(flagValue(arg, args[++i]));
|
|
48
62
|
else if (arg === "--json")
|
|
49
63
|
json = true;
|
|
64
|
+
else if (arg === "--wait")
|
|
65
|
+
wait = true;
|
|
66
|
+
else if (arg === "--timeout")
|
|
67
|
+
timeoutMs = parseTimeoutMs(flagValue(arg, args[++i]));
|
|
50
68
|
else
|
|
51
69
|
throw usage(`unknown argument ${arg}`);
|
|
52
70
|
}
|
|
@@ -54,6 +72,8 @@ export function parseIssueCreateArgs(args, readBody = (path) => readFileSync(pat
|
|
|
54
72
|
throw usage("--title is required");
|
|
55
73
|
if (body !== undefined && bodyFile !== undefined)
|
|
56
74
|
throw usage("pass --body or --body-file, not both");
|
|
75
|
+
if (timeoutMs !== undefined && !wait)
|
|
76
|
+
throw usage("--timeout requires --wait");
|
|
57
77
|
if (bodyFile !== undefined) {
|
|
58
78
|
try {
|
|
59
79
|
body = readBody(bodyFile);
|
|
@@ -62,12 +82,102 @@ export function parseIssueCreateArgs(args, readBody = (path) => readFileSync(pat
|
|
|
62
82
|
throw new CommandError(`Could not read ${bodyFile}: ${err instanceof Error ? err.message : String(err)}`);
|
|
63
83
|
}
|
|
64
84
|
}
|
|
65
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
title,
|
|
87
|
+
body: body ?? "",
|
|
88
|
+
labels,
|
|
89
|
+
...(repo ? { repo } : {}),
|
|
90
|
+
json,
|
|
91
|
+
...(wait ? { wait } : {}),
|
|
92
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function parseIssueWaitArgs(args) {
|
|
96
|
+
let number;
|
|
97
|
+
let repo;
|
|
98
|
+
let timeoutMs = ISSUE_REVIEW_DEFAULT_TIMEOUT_MS;
|
|
99
|
+
let json = false;
|
|
100
|
+
for (let i = 0; i < args.length; i++) {
|
|
101
|
+
const arg = args[i];
|
|
102
|
+
if (arg === "--repo")
|
|
103
|
+
repo = parseRepo(flagValue(arg, args[++i]));
|
|
104
|
+
else if (arg === "--timeout")
|
|
105
|
+
timeoutMs = parseTimeoutMs(flagValue(arg, args[++i]));
|
|
106
|
+
else if (arg === "--json")
|
|
107
|
+
json = true;
|
|
108
|
+
else if (!arg.startsWith("--") && number === undefined) {
|
|
109
|
+
const match = /^#?(\d+)$/.exec(arg.trim());
|
|
110
|
+
const n = match ? Number(match[1]) : NaN;
|
|
111
|
+
if (!Number.isSafeInteger(n) || n < 1)
|
|
112
|
+
throw usage(`issue wait needs an issue number, got ${arg}`);
|
|
113
|
+
number = n;
|
|
114
|
+
}
|
|
115
|
+
else
|
|
116
|
+
throw usage(`unknown argument ${arg}`);
|
|
117
|
+
}
|
|
118
|
+
if (number === undefined)
|
|
119
|
+
throw usage("issue wait needs an issue number");
|
|
120
|
+
return { number, ...(repo ? { repo } : {}), timeoutMs, json };
|
|
121
|
+
}
|
|
122
|
+
function resolveTarget(repo, deps) {
|
|
123
|
+
const target = repo ?? (deps.parseGithubOriginRepo ?? parseGithubOriginRepo)(deps.cwd ?? process.cwd());
|
|
124
|
+
if (!target) {
|
|
125
|
+
throw new CommandError("Could not parse GitHub owner/repo from `origin`. Pass --repo <owner/repo>.");
|
|
126
|
+
}
|
|
127
|
+
return target;
|
|
128
|
+
}
|
|
129
|
+
function asAuthExit(err) {
|
|
130
|
+
if (err instanceof CommandError && (err.code === "auth_invalid" || err.code === "missing_api_key")) {
|
|
131
|
+
return new CommandError(err.message, REVIEW_EXIT.auth, err.code);
|
|
132
|
+
}
|
|
133
|
+
return err;
|
|
134
|
+
}
|
|
135
|
+
function approvedLine(status) {
|
|
136
|
+
if (status.state === "off")
|
|
137
|
+
return `Issue reviews are off for ${status.owner}/${status.repo}; #${status.number} needs no approval.`;
|
|
138
|
+
const by = status.approvedBy === "human" ? "a person" : "Vortex";
|
|
139
|
+
return `${status.owner}/${status.repo}#${status.number} is approved by ${by}.`;
|
|
140
|
+
}
|
|
141
|
+
export async function waitForIssueReview(target, opts, deps = {}) {
|
|
142
|
+
const printJson = (status) => console.log(JSON.stringify(opts.jsonShape ? opts.jsonShape(status) : status, null, 2));
|
|
143
|
+
const progress = (line) => (opts.json ? console.error(line) : console.log(line));
|
|
144
|
+
let status;
|
|
145
|
+
try {
|
|
146
|
+
const cfg = await (deps.loadConfig ?? loadConfig)();
|
|
147
|
+
progress(`Waiting for the Vortex review of ${target.owner}/${target.repo}#${target.number} …`);
|
|
148
|
+
status = await (deps.pollIssueReview ?? pollIssueReview)(cfg, target, { timeoutMs: opts.timeoutMs });
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (err instanceof IssueReviewTimeoutError) {
|
|
152
|
+
if (opts.json)
|
|
153
|
+
printJson(err.lastStatus);
|
|
154
|
+
else
|
|
155
|
+
console.log(issueReviewLines(err.lastStatus).join("\n"));
|
|
156
|
+
}
|
|
157
|
+
throw asAuthExit(err);
|
|
158
|
+
}
|
|
159
|
+
if (!status) {
|
|
160
|
+
throw new CommandError("This Mergestorm server does not review issues yet. Update the API, or open the pull request as before.", 1, "issue_review_unavailable");
|
|
161
|
+
}
|
|
162
|
+
if (opts.json)
|
|
163
|
+
printJson(status);
|
|
164
|
+
if (status.state === "approved" || status.state === "off") {
|
|
165
|
+
if (!opts.json) {
|
|
166
|
+
console.log(approvedLine(status));
|
|
167
|
+
if (status.next)
|
|
168
|
+
console.log(status.next);
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const refusal = issueNotApprovedError([status], `Vortex has not approved ${status.owner}/${status.repo}#${status.number} yet.`);
|
|
173
|
+
if (opts.json)
|
|
174
|
+
throw new CommandError(refusal.message.split("\n")[0], refusal.exitCode, refusal.code);
|
|
175
|
+
throw refusal;
|
|
66
176
|
}
|
|
67
177
|
export async function cmdIssue(args, deps = {}) {
|
|
68
178
|
const sub = args[0]?.toLowerCase();
|
|
69
179
|
const asksForHelp = (arg) => arg === "-h" || arg === "--help" || arg === "help";
|
|
70
|
-
const
|
|
180
|
+
const restAsksForHelp = (rest) => {
|
|
71
181
|
for (let i = 0; i < rest.length; i++) {
|
|
72
182
|
const arg = rest[i];
|
|
73
183
|
if (asksForHelp(arg))
|
|
@@ -77,22 +187,34 @@ export async function cmdIssue(args, deps = {}) {
|
|
|
77
187
|
}
|
|
78
188
|
return false;
|
|
79
189
|
};
|
|
80
|
-
if (!sub || asksForHelp(sub) || (sub === "create" &&
|
|
190
|
+
if (!sub || asksForHelp(sub) || ((sub === "create" || sub === "wait") && restAsksForHelp(args.slice(1)))) {
|
|
81
191
|
console.log(ISSUE_USAGE);
|
|
82
192
|
return;
|
|
83
193
|
}
|
|
194
|
+
if (sub === "wait") {
|
|
195
|
+
const parsed = parseIssueWaitArgs(args.slice(1));
|
|
196
|
+
const target = resolveTarget(parsed.repo, deps);
|
|
197
|
+
await waitForIssueReview({ ...target, number: parsed.number }, parsed, deps);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
84
200
|
if (sub !== "create")
|
|
85
201
|
throw usage(`unknown issue subcommand: ${sub}`);
|
|
86
202
|
const parsed = parseIssueCreateArgs(args.slice(1), deps.readBody);
|
|
87
|
-
const target = parsed.repo
|
|
88
|
-
if (!target) {
|
|
89
|
-
throw new CommandError("Could not parse GitHub owner/repo from `origin`. Pass --repo <owner/repo>.");
|
|
90
|
-
}
|
|
203
|
+
const target = resolveTarget(parsed.repo, deps);
|
|
91
204
|
const issue = await (deps.openStackIssue ?? openStackIssue)({ owner: target.owner, repo: target.repo, title: parsed.title, body: parsed.body, labels: parsed.labels }, await (deps.loadConfig ?? loadConfig)());
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
205
|
+
const created = { owner: target.owner, repo: target.repo, ...issue };
|
|
206
|
+
if (parsed.json && !parsed.wait) {
|
|
207
|
+
console.log(JSON.stringify(created, null, 2));
|
|
95
208
|
}
|
|
96
|
-
|
|
97
|
-
|
|
209
|
+
else if (!parsed.json) {
|
|
210
|
+
console.log(`${issue.created ? "Opened" : "Already open:"} ${target.owner}/${target.repo}#${issue.number} ${issue.url}`);
|
|
211
|
+
console.log(`Put \`Fixes #${issue.number}\` in the commit or pull request body.`);
|
|
212
|
+
}
|
|
213
|
+
if (!parsed.wait)
|
|
214
|
+
return;
|
|
215
|
+
await waitForIssueReview({ ...target, number: issue.number }, {
|
|
216
|
+
timeoutMs: parsed.timeoutMs ?? ISSUE_REVIEW_DEFAULT_TIMEOUT_MS,
|
|
217
|
+
json: parsed.json,
|
|
218
|
+
jsonShape: (review) => ({ ...created, review }),
|
|
219
|
+
}, deps);
|
|
98
220
|
}
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type Config } from "../config.js";
|
|
2
|
-
import { type MeResponse } from "../api.js";
|
|
2
|
+
import { type AccountIdentity, type MeResponse } from "../api.js";
|
|
3
3
|
/**
|
|
4
4
|
* Probe /api/v1/me with an in-memory config. Does not write disk.
|
|
5
5
|
* Throws CommandError when the key must not be saved.
|
|
6
6
|
*/
|
|
7
7
|
export declare function validateApiKey(cfg: Config): Promise<MeResponse>;
|
|
8
|
+
export declare function loggedInLine(account: AccountIdentity | null | undefined): string;
|
|
8
9
|
export declare function pendingLoginPath(): string;
|
|
9
10
|
export interface LoginDeps {
|
|
10
11
|
openBrowser: (url: string) => void;
|