brnch-cli 0.19.0 → 0.19.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 +1 -1
- package/brnch.mjs +269 -24
- package/package.json +1 -1
package/README.md
CHANGED
package/brnch.mjs
CHANGED
|
@@ -30573,7 +30573,8 @@ var PLATFORM_TOKEN_PREFIXES = {
|
|
|
30573
30573
|
fsfa: "pending second-factor sign-in",
|
|
30574
30574
|
fbkc: "Buildkite checkout token",
|
|
30575
30575
|
fsvw: "staff read-only view",
|
|
30576
|
-
fcrt: "container registry token"
|
|
30576
|
+
fcrt: "container registry token",
|
|
30577
|
+
fcpk: "clone-pack build token"
|
|
30577
30578
|
};
|
|
30578
30579
|
var PLATFORM_TOKEN_RE = new RegExp(`^(${Object.keys(PLATFORM_TOKEN_PREFIXES).join("|")})_[0-9a-f]{64}$`);
|
|
30579
30580
|
|
|
@@ -31636,7 +31637,6 @@ var looksLikeHostedLabel = (label2) => /^brnch-hosted(?:-|$)/.test(label2);
|
|
|
31636
31637
|
var DEPLOY_LIMITS = {
|
|
31637
31638
|
maxStepsPerTarget: 12,
|
|
31638
31639
|
maxWatchPaths: 50,
|
|
31639
|
-
maxTargetsPerRepository: 10,
|
|
31640
31640
|
maxCommandLength: 2e3,
|
|
31641
31641
|
maxSummaryLength: 500,
|
|
31642
31642
|
maxDetailKeys: 20,
|
|
@@ -31746,7 +31746,8 @@ var PLANS = Object.freeze({
|
|
|
31746
31746
|
storage_bytes_per_seat: 0,
|
|
31747
31747
|
ai: { fast: 100, strong: 25, agent: 0, assistant: 0 },
|
|
31748
31748
|
private_repo_members: 5,
|
|
31749
|
-
overage: false
|
|
31749
|
+
overage: false,
|
|
31750
|
+
deploy_targets_per_repository: 1
|
|
31750
31751
|
},
|
|
31751
31752
|
pro: {
|
|
31752
31753
|
id: "pro",
|
|
@@ -31765,7 +31766,8 @@ var PLANS = Object.freeze({
|
|
|
31765
31766
|
storage_bytes_per_seat: 0,
|
|
31766
31767
|
ai: { fast: 300, strong: 100, agent: 10, assistant: 100 },
|
|
31767
31768
|
private_repo_members: 5,
|
|
31768
|
-
overage: true
|
|
31769
|
+
overage: true,
|
|
31770
|
+
deploy_targets_per_repository: 10
|
|
31769
31771
|
},
|
|
31770
31772
|
team: {
|
|
31771
31773
|
id: "team",
|
|
@@ -31784,7 +31786,8 @@ var PLANS = Object.freeze({
|
|
|
31784
31786
|
storage_bytes_per_seat: 2 * GB,
|
|
31785
31787
|
ai: { fast: 600, strong: 200, agent: 10, assistant: 100 },
|
|
31786
31788
|
private_repo_members: Number.POSITIVE_INFINITY,
|
|
31787
|
-
overage: true
|
|
31789
|
+
overage: true,
|
|
31790
|
+
deploy_targets_per_repository: 10
|
|
31788
31791
|
},
|
|
31789
31792
|
enterprise: {
|
|
31790
31793
|
id: "enterprise",
|
|
@@ -31803,7 +31806,8 @@ var PLANS = Object.freeze({
|
|
|
31803
31806
|
storage_bytes_per_seat: 5 * GB,
|
|
31804
31807
|
ai: { fast: 1500, strong: 500, agent: 40, assistant: 300 },
|
|
31805
31808
|
private_repo_members: Number.POSITIVE_INFINITY,
|
|
31806
|
-
overage: true
|
|
31809
|
+
overage: true,
|
|
31810
|
+
deploy_targets_per_repository: 1e3
|
|
31807
31811
|
}
|
|
31808
31812
|
});
|
|
31809
31813
|
|
|
@@ -32211,15 +32215,27 @@ var UpdateRepoRequest = external_exports.object({
|
|
|
32211
32215
|
default_branch: external_exports.string().min(1).max(100).optional(),
|
|
32212
32216
|
/** Replaces the topic list (≤20, lowercase). */
|
|
32213
32217
|
topics: Topics.optional(),
|
|
32218
|
+
/** The repository Actions switch (ADR-0009 addendum 2026-10-18). Off: no automatic runs, workflow_dispatch refused. */
|
|
32219
|
+
actions_enabled: external_exports.boolean().optional(),
|
|
32214
32220
|
version: external_exports.number().int()
|
|
32215
32221
|
});
|
|
32216
32222
|
var PATH_HISTORY_SCAN_MAX = 500;
|
|
32217
32223
|
var RepoPathQuery = external_exports.string().min(1).max(4096).transform((p) => p.replace(/\/{2,}/g, "/").replace(/\/+$/, "")).refine((p) => p.length > 0 && !p.startsWith("/"), "path must be relative to the repository root").refine((p) => !/[\u0000-\u001f\u007f]/.test(p), "path must not contain control characters").refine((p) => p.split("/").every((s) => s !== "." && s !== ".."), "path must not contain . or .. segments");
|
|
32224
|
+
var isoDate = external_exports.string().max(64).refine((v) => !Number.isNaN(Date.parse(v)), { message: "must be an ISO-8601 date" });
|
|
32225
|
+
var queryFlag = external_exports.enum(["1", "true", "0", "false"]).transform((v) => v === "1" || v === "true");
|
|
32218
32226
|
var CommitListQuery = external_exports.object({
|
|
32219
32227
|
ref: external_exports.string().optional(),
|
|
32220
32228
|
path: RepoPathQuery.optional(),
|
|
32221
|
-
scan_limit: external_exports.coerce.number().int().min(1).max(PATH_HISTORY_SCAN_MAX).optional()
|
|
32222
|
-
|
|
32229
|
+
scan_limit: external_exports.coerce.number().int().min(1).max(PATH_HISTORY_SCAN_MAX).optional(),
|
|
32230
|
+
// Log filters (M6-SCL-02): substring matches, case-insensitive (no regular expressions); dates bound the committer
|
|
32231
|
+
// date, inclusive.
|
|
32232
|
+
author: external_exports.string().min(1).max(200).optional(),
|
|
32233
|
+
grep: external_exports.string().min(1).max(200).optional(),
|
|
32234
|
+
since: isoDate.optional(),
|
|
32235
|
+
until: isoDate.optional(),
|
|
32236
|
+
all_parents: queryFlag.optional()
|
|
32237
|
+
}).refine((q) => q.scan_limit === void 0 || q.path !== void 0 || isLogFiltered(q), { message: "scan_limit requires path or a filter", path: ["scan_limit"] });
|
|
32238
|
+
var isLogFiltered = (q) => q.author !== void 0 || q.grep !== void 0 || q.since !== void 0 || q.until !== void 0 || q.all_parents === true;
|
|
32223
32239
|
var RequiredCheckSchema = external_exports.object({
|
|
32224
32240
|
name: external_exports.string().min(1).max(200),
|
|
32225
32241
|
subject: external_exports.enum(["head", "candidate"]).default("head"),
|
|
@@ -32454,7 +32470,9 @@ var UpdateOrgRequest = external_exports.object({
|
|
|
32454
32470
|
/** Idle timeout of new agent sessions in minutes (ADR-0040). */
|
|
32455
32471
|
agent_session_idle_minutes: external_exports.number().int().min(15).max(1440).optional(),
|
|
32456
32472
|
/** Brnch Secure Defaults for new repositories (ADR-0081). Turning it on also requires a second factor for admins. */
|
|
32457
|
-
secure_defaults: external_exports.boolean().optional()
|
|
32473
|
+
secure_defaults: external_exports.boolean().optional(),
|
|
32474
|
+
/** The organization's Actions switch (ADR-0009 addendum 2026-10-19): off, no repository of it starts workflow runs. */
|
|
32475
|
+
actions_enabled: external_exports.boolean().optional()
|
|
32458
32476
|
});
|
|
32459
32477
|
var OAuthDecisionRequest = external_exports.object({
|
|
32460
32478
|
approve: external_exports.boolean(),
|
|
@@ -33716,6 +33734,16 @@ var CreateConnectionRequest = external_exports.discriminatedUnion("auth", [
|
|
|
33716
33734
|
name: Name3,
|
|
33717
33735
|
credentials: external_exports.object({ api_token: Secret2 })
|
|
33718
33736
|
}),
|
|
33737
|
+
external_exports.object({
|
|
33738
|
+
// Codex / Claude Code reviews with the key already saved in Settings → AI providers (an OpenAI or Anthropic
|
|
33739
|
+
// provider of this organization). Nothing is copied: the key is read from the provider at every review, so
|
|
33740
|
+
// rotating it there applies here, and deleting the provider stops the reviews.
|
|
33741
|
+
auth: external_exports.literal("ai_provider"),
|
|
33742
|
+
provider: external_exports.enum(["codex", "claude"]),
|
|
33743
|
+
purpose: external_exports.literal("code_review").default("code_review"),
|
|
33744
|
+
name: Name3,
|
|
33745
|
+
config: external_exports.object({ ai_provider_id: external_exports.string().min(1).max(100) })
|
|
33746
|
+
}),
|
|
33719
33747
|
external_exports.object({
|
|
33720
33748
|
auth: external_exports.literal("infisical_oidc"),
|
|
33721
33749
|
provider: external_exports.literal("infisical"),
|
|
@@ -34495,6 +34523,48 @@ var MirrorStatus = external_exports.object({
|
|
|
34495
34523
|
timings: external_exports.record(external_exports.string(), external_exports.number())
|
|
34496
34524
|
});
|
|
34497
34525
|
|
|
34526
|
+
// packages/protocol/src/clone-pack.ts
|
|
34527
|
+
var CLONE_PACK_ID = /^cpk_[0-9a-z]{10,40}$/;
|
|
34528
|
+
var Authorization2 = external_exports.string().regex(/^Bearer [A-Za-z0-9+/=._~-]{8,4096}$/);
|
|
34529
|
+
var HttpUrl2 = external_exports.string().max(2048).regex(/^https?:\/\/[^\s@]+$/);
|
|
34530
|
+
var Oid2 = external_exports.string().regex(/^[0-9a-f]{40}$/);
|
|
34531
|
+
var RefName3 = external_exports.string().max(1024).regex(/^refs\/[^\s\0]+$/);
|
|
34532
|
+
var PackRef = external_exports.object({ name: RefName3, oid: Oid2 });
|
|
34533
|
+
var PackJob = external_exports.object({
|
|
34534
|
+
/** One build; one container per id. */
|
|
34535
|
+
id: external_exports.string().regex(CLONE_PACK_ID),
|
|
34536
|
+
/**
|
|
34537
|
+
* The platform's endpoint for this build, `<base>/git` its git remote (storage behind the storage gate, private refs
|
|
34538
|
+
* hidden) and `<base>/pack/:n`, `<base>/idx`, `<base>/complete` its upload; `authorization` holds for all of them and
|
|
34539
|
+
* for nothing else.
|
|
34540
|
+
*/
|
|
34541
|
+
base_url: HttpUrl2,
|
|
34542
|
+
authorization: Authorization2,
|
|
34543
|
+
default_branch: external_exports.string().regex(/^[A-Za-z0-9._\/-]{1,200}$/).refine((b) => !b.includes(".."), "invalid branch"),
|
|
34544
|
+
/** The last ready pack, downloaded from `<base>/previous/pack` and `/previous/idx`, and the refs it holds. */
|
|
34545
|
+
previous: external_exports.object({ pack_sha1: Oid2, refs: external_exports.array(PackRef).max(2e4) }).nullable(),
|
|
34546
|
+
limits: external_exports.object({ max_pack_bytes: external_exports.number().int().positive(), part_bytes: external_exports.number().int().positive() }),
|
|
34547
|
+
expires_at: external_exports.iso.datetime()
|
|
34548
|
+
});
|
|
34549
|
+
var PackComplete = external_exports.object({
|
|
34550
|
+
pack_sha1: Oid2,
|
|
34551
|
+
pack_bytes: external_exports.number().int().positive(),
|
|
34552
|
+
parts: external_exports.number().int().positive().max(1e4),
|
|
34553
|
+
objects: external_exports.number().int().nonnegative(),
|
|
34554
|
+
refs: external_exports.array(PackRef).max(2e4)
|
|
34555
|
+
});
|
|
34556
|
+
var PackStatus = external_exports.object({
|
|
34557
|
+
id: external_exports.string(),
|
|
34558
|
+
state: external_exports.enum(["running", "completed", "failed"]),
|
|
34559
|
+
phase: external_exports.string().max(40),
|
|
34560
|
+
reason: external_exports.enum(["fetch_failed", "too_large", "upload_failed", "error"]).optional(),
|
|
34561
|
+
message: external_exports.string().max(4e3).nullable(),
|
|
34562
|
+
pack_bytes: external_exports.number().nullable(),
|
|
34563
|
+
objects: external_exports.number().nullable(),
|
|
34564
|
+
updated_at: external_exports.string(),
|
|
34565
|
+
timings: external_exports.record(external_exports.string(), external_exports.number())
|
|
34566
|
+
});
|
|
34567
|
+
|
|
34498
34568
|
// packages/protocol/src/repo-mirrors.ts
|
|
34499
34569
|
var StopMirrorRequest = external_exports.object({ confirm: external_exports.string().min(3).max(300), expected_version: external_exports.number().int().positive().optional() }).strict();
|
|
34500
34570
|
var ResetMirrorRefsRequest = external_exports.object({ refs: external_exports.array(external_exports.string().min(1).max(1024)).min(1).max(100) }).strict();
|
|
@@ -36023,6 +36093,7 @@ function orgsGroup(t) {
|
|
|
36023
36093
|
create: (body, o) => t.json({ method: "POST", path: "/v1/orgs", json: validate2(CreateOrgRequest, body, "CreateOrgRequest"), options: o }),
|
|
36024
36094
|
list: (q = {}, o) => t.json({ method: "GET", path: "/v1/orgs", query: { ...q }, options: o }),
|
|
36025
36095
|
get: (org, o) => t.json({ method: "GET", path: path2`/v1/orgs/${org}`, options: o }),
|
|
36096
|
+
update: (org, body, o) => t.json({ method: "PATCH", path: path2`/v1/orgs/${org}`, json: validate2(UpdateOrgRequest, body, "UpdateOrgRequest"), options: o }),
|
|
36026
36097
|
members: {
|
|
36027
36098
|
list: (org, q = {}, o) => t.json({ method: "GET", path: path2`/v1/orgs/${org}/members`, query: { ...q }, options: o }),
|
|
36028
36099
|
update: (org, userId, body, o) => t.json({
|
|
@@ -36314,13 +36385,25 @@ function reposGroup(t) {
|
|
|
36314
36385
|
/** Raw file bytes; never interpret them as HTML. */
|
|
36315
36386
|
raw: (repo, q, o) => t.bytes({ method: "GET", path: path2`/v1/repos/${repo}/raw`, query: { ...q }, options: o }),
|
|
36316
36387
|
/** First-parent history of `ref`; with `path`, only commits that changed it — each page examines at most
|
|
36317
|
-
* `scan_limit` commits and reports how many in `path_scan` (`complete` is false until the walk reaches the root).
|
|
36388
|
+
* `scan_limit` commits and reports how many in `path_scan` (`complete` is false until the walk reaches the root).
|
|
36389
|
+
* With log filters (`author`, `grep`, `since`, `until`, `all_parents`; substring matches), the same in `log_scan`. */
|
|
36318
36390
|
commits: (repo, q = {}, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/commits`, query: { ...q }, options: o }),
|
|
36319
36391
|
/** Write text files to a branch as one commit, through the same checks as a push (ADR-0078). Retry with the same
|
|
36320
36392
|
* `idempotencyKey`: a retry after an uncertain outcome never commits twice. */
|
|
36321
36393
|
commitFiles: (repo, body, o) => t.json({ method: "POST", path: path2`/v1/repos/${repo}/commits`, json: validate2(CommitFilesRequest, body, "CommitFilesRequest"), options: o }),
|
|
36322
36394
|
commit: (repo, oid, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/commits/${oid}`, options: o }),
|
|
36395
|
+
/** The changes that brought a commit onto each ref it landed on (M6-SCL-01). */
|
|
36396
|
+
commitChange: (repo, oid, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/commits/${oid}/change`, options: o }),
|
|
36397
|
+
/** `mode: "two-dot"` diffs the base tree against the head tree instead of against their merge base. */
|
|
36323
36398
|
compare: (repo, q, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/compare`, query: { ...q }, options: o }),
|
|
36399
|
+
/** The commits in `base..head` (newest first, at most `limit`, max 250) with ahead/behind counts (M6-SCL-02). */
|
|
36400
|
+
compareCommits: (repo, q, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/compare/commits`, query: { ...q }, options: o }),
|
|
36401
|
+
/** Every best common ancestor of two refs or commits (`git merge-base --all`). */
|
|
36402
|
+
mergeBase: (repo, q, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/merge-base`, query: { ...q }, options: o }),
|
|
36403
|
+
/** Whether `ancestor` is in `descendant`'s history; `is_ancestor` is null when the bounded walk cannot decide. */
|
|
36404
|
+
isAncestor: (repo, q, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/is-ancestor`, query: { ...q }, options: o }),
|
|
36405
|
+
/** Branches and tags whose history contains a commit (`git for-each-ref --contains`). */
|
|
36406
|
+
refsContaining: (repo, commit, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/commits/${commit}/refs`, options: o }),
|
|
36324
36407
|
diff: (repo, q, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/diff`, query: { ...q }, options: o }),
|
|
36325
36408
|
pushes: (repo, q = {}, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/pushes`, query: { ...q }, options: o }),
|
|
36326
36409
|
search: (repo, q, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/search`, query: { ...q }, options: o }),
|
|
@@ -36628,6 +36711,7 @@ function dbMigrationsGroup(t) {
|
|
|
36628
36711
|
}
|
|
36629
36712
|
|
|
36630
36713
|
// packages/sdk/src/groups/deploy.ts
|
|
36714
|
+
var isCallOptions = (x) => "signal" in x || "idempotencyKey" in x;
|
|
36631
36715
|
function deployGroup(t) {
|
|
36632
36716
|
return {
|
|
36633
36717
|
stepKinds: (o) => t.json({ method: "GET", path: "/v1/deploy/step-kinds", options: o }),
|
|
@@ -36637,7 +36721,14 @@ function deployGroup(t) {
|
|
|
36637
36721
|
suggestions: (repo, ref, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/deploy-suggestions`, query: ref ? { ref } : {}, options: o }),
|
|
36638
36722
|
resources: (repo, connection, type, parent, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/deploy-connections/${connection}/resources`, query: { type, ...parent ? { parent } : {} }, options: o }),
|
|
36639
36723
|
targets: {
|
|
36640
|
-
|
|
36724
|
+
/**
|
|
36725
|
+
* A page of the repository's targets (50 by default); `name` finds one target by name. Callers from before
|
|
36726
|
+
* pagination passed call options second: those are still taken as call options.
|
|
36727
|
+
*/
|
|
36728
|
+
list: (repo, q = {}, o) => {
|
|
36729
|
+
if (isCallOptions(q)) [q, o] = [{}, q];
|
|
36730
|
+
return t.json({ method: "GET", path: path2`/v1/repos/${repo}/deploy-targets`, query: { ...q }, options: o });
|
|
36731
|
+
},
|
|
36641
36732
|
create: (repo, body, o) => t.json({ method: "POST", path: path2`/v1/repos/${repo}/deploy-targets`, json: validate2(CreateDeployTargetRequest, body, "CreateDeployTargetRequest"), options: o }),
|
|
36642
36733
|
get: (id2, o) => t.json({ method: "GET", path: path2`/v1/deploy-targets/${id2}`, options: o }),
|
|
36643
36734
|
update: (id2, body, o) => t.json({ method: "PATCH", path: path2`/v1/deploy-targets/${id2}`, json: validate2(UpdateDeployTargetRequest, body, "UpdateDeployTargetRequest"), options: o }),
|
|
@@ -37138,7 +37229,7 @@ function invalidAgentToolFlag(flag) {
|
|
|
37138
37229
|
}
|
|
37139
37230
|
|
|
37140
37231
|
// packages/cli/package.json
|
|
37141
|
-
var version2 = "0.19.
|
|
37232
|
+
var version2 = "0.19.1";
|
|
37142
37233
|
|
|
37143
37234
|
// packages/cli/src/brand.ts
|
|
37144
37235
|
var BRAND = {
|
|
@@ -39821,6 +39912,7 @@ function instructionsSnippet(tool) {
|
|
|
39821
39912
|
` - Commit only what you wrote: stage your paths (\`git add <path>\u2026\`), not the person's uncommitted edits (\`git add -A\` takes them too). Do not squash other sessions' or the person's commits into yours.`,
|
|
39822
39913
|
` - Commit authors and trailers (\`Agent-Session:\`, \`Co-Authored-By:\`) are claims and attribute nothing; you do not need them for attribution.`,
|
|
39823
39914
|
` - Hooks wired: \`${cli} hooks status\`. Codex runs them only after the person trusts them once (\`${cli} hooks trust\` in their terminal).`,
|
|
39915
|
+
`- Ask Brnch which change brought a commit instead of searching \`git log\` or guessing from commit-message words: \`${cli} repo commit-change <commit>\` (MCP: \`commit_change_get\`), for every merge strategy. The change's description and review say why.`,
|
|
39824
39916
|
`- Record key decisions as you make them: \`${cli} decision "\u2026" --reason "\u2026" --file <path>\`. Blame shows them with the lines they explain.`,
|
|
39825
39917
|
`- Other agents may be working here at the same time. Run \`${cli} status\` before you start to see who is working on what.`,
|
|
39826
39918
|
`- Claim ADR numbers, migration numbers, release versions and exclusive operations before you use them, and use exactly the key you get: \`${cli} claim adr:<n> --next\` (also \`migration:<n>\`, \`version:<pkg>:<x.y.z>\`, \`lock:<operation>\`). If a key is taken, take the next; never reuse another session's key.`,
|
|
@@ -48487,8 +48579,8 @@ ${renderPlan2(v.plan)}`);
|
|
|
48487
48579
|
// packages/cli/src/commands/deploy.ts
|
|
48488
48580
|
var TERMINAL4 = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "superseded", "blocked"]);
|
|
48489
48581
|
var NO_DO_PREVIEWS = "previews off: Cloudflare makes no version URLs for Workers that implement a Durable Object (--previews --preview-mode worker_previews uses Worker Previews; it needs a `previews` block in the Wrangler configuration)";
|
|
48490
|
-
var renderTargets = (v) =>
|
|
48491
|
-
v.items.map((t) => ({ ...t, resource: t.resource_name, env: t.environment ?? "-", last_production: t.last_production ? `${t.last_production.status} ${t.last_production.commit_oid.slice(0, 12)}` : "-" })),
|
|
48582
|
+
var renderTargets = (v) => listWithCursor(
|
|
48583
|
+
{ ...v, items: v.items.map((t) => ({ ...t, resource: t.resource_name, env: t.environment ?? "-", last_production: t.last_production ? `${t.last_production.status} ${t.last_production.commit_oid.slice(0, 12)}` : "-" })) },
|
|
48492
48584
|
["name", "id", "platform", "resource", "env", "branch", "runs_on", "previews", "state", "last_production"]
|
|
48493
48585
|
);
|
|
48494
48586
|
var renderTarget = (t) => kv({ ...t, approvers: ruleSummary(t.approval_rule), watch_paths: t.watch_paths?.length ? t.watch_paths.join(" ") : "every change", connection: t.connection ? `${t.connection.name} (${t.connection.id}, ${t.connection.state})` : "-", last_production: t.last_production ? `${t.last_production.id} ${t.last_production.status}` : "-" }, [
|
|
@@ -48531,10 +48623,12 @@ ${lines2}
|
|
|
48531
48623
|
}
|
|
48532
48624
|
async function targetId(ctx, ref) {
|
|
48533
48625
|
if (ref.startsWith("dpt_")) return ref;
|
|
48534
|
-
const
|
|
48535
|
-
const
|
|
48536
|
-
|
|
48537
|
-
return t.id;
|
|
48626
|
+
const client = ctx.client("user");
|
|
48627
|
+
const repo = await ctx.repoId(void 0, "user");
|
|
48628
|
+
const t = (await client.deploy.targets.list(repo, { name: ref })).items.find((x) => x.name === ref);
|
|
48629
|
+
if (t) return t.id;
|
|
48630
|
+
const { items, next_cursor } = await client.deploy.targets.list(repo, { limit: 20 });
|
|
48631
|
+
throw new CliError(`no deploy target named "${ref}" (have: ${items.map((x) => x.name).join(", ") || "none"}${next_cursor ? ", \u2026" : ""})`, EXIT.NOT_FOUND, "NOT_FOUND");
|
|
48538
48632
|
}
|
|
48539
48633
|
var orNull = (v) => v === void 0 ? void 0 : v || null;
|
|
48540
48634
|
async function stepsOf(ctx, spec) {
|
|
@@ -48651,7 +48745,7 @@ function registerDeploy(program2, rt) {
|
|
|
48651
48745
|
Merges to ${v.branch} deploy automatically. Deploy now: brnch deploy run ${v.id}`);
|
|
48652
48746
|
})
|
|
48653
48747
|
);
|
|
48654
|
-
const targets2 = deploy.command("targets").description("Deploy targets of --repo; `protect` requires approvers for a target's production deploys").action(act(rt, async (ctx) => ctx.result(await ctx.client("user").deploy.targets.list(await ctx.repoId(void 0, "user")), renderTargets)));
|
|
48748
|
+
const targets2 = deploy.command("targets").description("Deploy targets of --repo; `protect` requires approvers for a target's production deploys").option("--limit <n>", "page size", (v) => parseIntOption(v, "--limit")).option("--cursor <cursor>", "page cursor").action(act(rt, async (ctx, o) => ctx.result(await ctx.client("user").deploy.targets.list(await ctx.repoId(void 0, "user"), { limit: o.limit, cursor: o.cursor }), renderTargets)));
|
|
48655
48749
|
protectOptions(targets2.command("protect <target>").description("Require approvals for the target's production deploys and rollbacks (people with deploy:manage). Replaces the current rule")).action(
|
|
48656
48750
|
act(rt, async (ctx, o, [ref]) => {
|
|
48657
48751
|
const c8 = ctx.client("user").deploy.targets;
|
|
@@ -49342,15 +49436,78 @@ function authGuidance(cli, helperConfigured) {
|
|
|
49342
49436
|
}
|
|
49343
49437
|
|
|
49344
49438
|
// packages/cli/src/commands/repo.ts
|
|
49345
|
-
var REPO_FIELDS = ["id", "org_slug", "name", "description", "visibility", "default_branch", "lifecycle", "clone_url", "updated_at"];
|
|
49439
|
+
var REPO_FIELDS = ["id", "org_slug", "name", "description", "visibility", "default_branch", "lifecycle", "clone_url", "actions_enabled", "updated_at"];
|
|
49346
49440
|
var renderRepo2 = (r) => kv(r, REPO_FIELDS) + (r.import ? `
|
|
49347
49441
|
import ${r.import.status}${r.import.shallow ? " (shallow)" : ""}` : "");
|
|
49348
49442
|
function renderLog(p) {
|
|
49349
49443
|
const rows2 = p.items.map((c8) => ({ oid: c8.oid.slice(0, 12), date: c8.committer.date.slice(0, 10), author: c8.author.name, subject: c8.message.split("\n", 1)[0] }));
|
|
49350
49444
|
const lines2 = [table(rows2, ["oid", "date", "author", "subject"])];
|
|
49351
49445
|
const s = p.path_scan;
|
|
49446
|
+
const l = p.log_scan;
|
|
49352
49447
|
if (s) lines2.push(s.complete ? `scanned ${s.scanned_total} commits: full first-parent history of ${s.path}` : `scanned ${s.scanned_total} commits so far; older history not searched yet (more: --cursor ${p.next_cursor})`);
|
|
49353
|
-
else if (
|
|
49448
|
+
else if (l) {
|
|
49449
|
+
if (l.complete) lines2.push(`scanned ${l.scanned_total} commits: all matching history searched`);
|
|
49450
|
+
else if (l.exhausted) lines2.push(`scanned ${l.scanned_total} commits, the most one search walks; older history not searched`);
|
|
49451
|
+
else lines2.push(`scanned ${l.scanned_total} commits so far; older history not searched yet (more: --cursor ${p.next_cursor})`);
|
|
49452
|
+
} else if (p.next_cursor) lines2.push(`(more: --cursor ${p.next_cursor})`);
|
|
49453
|
+
return lines2.join("\n");
|
|
49454
|
+
}
|
|
49455
|
+
function renderCommitChange(r) {
|
|
49456
|
+
const lines2 = r.landings.map((l) => {
|
|
49457
|
+
const how2 = l.role === "landing" ? `${l.strategy} commit of` : `brought in by ${l.strategy} of`;
|
|
49458
|
+
const receipt = l.receipt_id ? `, receipt ${l.receipt_id}` : "";
|
|
49459
|
+
return `${l.target_ref.replace(/^refs\/heads\//, "")}: ${how2} #${l.change.number} ${l.change.title} (${l.change.state}; landing ${l.landing_oid.slice(0, 12)}${receipt})`;
|
|
49460
|
+
});
|
|
49461
|
+
if (!lines2.length) lines2.push(r.indexing ? "no change on record yet: earlier landings are still being indexed, ask again shortly" : "no change brought this commit (pushed straight to a branch, or older than the repository on Brnch)");
|
|
49462
|
+
else if (r.indexing) lines2.push("(earlier landings are still being indexed)");
|
|
49463
|
+
return lines2.join("\n");
|
|
49464
|
+
}
|
|
49465
|
+
var renderTree = (t) => [`${t.path || "/"} at ${t.commit_oid.slice(0, 12)}`, table(t.entries.map((e) => ({ ...e, name: e.type === "tree" ? `${e.name}/` : e.name })), ["mode", "type", "name"])].join("\n");
|
|
49466
|
+
function renderBlob(b) {
|
|
49467
|
+
if (b.binary) return `(binary file, ${b.size} bytes; ${BRAND.cli} --json repo blob for its id)`;
|
|
49468
|
+
if (b.truncated || b.encoding !== "utf8") return `(${b.size} bytes, too large to show inline: clone, or download ${b.path} at ${b.commit_oid.slice(0, 12)})`;
|
|
49469
|
+
return b.content;
|
|
49470
|
+
}
|
|
49471
|
+
function renderBlame(b) {
|
|
49472
|
+
const rows2 = b.lines.map((l) => ({ line: l.line, oid: l.commit_oid.slice(0, 12), date: l.date.slice(0, 10), author: l.author, summary: l.summary }));
|
|
49473
|
+
const lines2 = [table(rows2, ["line", "oid", "date", "author", "summary"])];
|
|
49474
|
+
if (b.partial) lines2.push("partial: the walk ran out of time, older lines may belong to newer commits; the full blame is being computed, ask again shortly");
|
|
49475
|
+
else if (b.truncated) lines2.push("history not walked to the end: unresolved lines are attributed to the oldest commit examined");
|
|
49476
|
+
return lines2.join("\n");
|
|
49477
|
+
}
|
|
49478
|
+
function renderSearch(r) {
|
|
49479
|
+
const rows2 = r.items.map((i) => ({ kind: i.kind, where: i.path ?? i.title, snippet: i.snippet.replace(/\s+/g, " ").slice(0, 100) }));
|
|
49480
|
+
const lines2 = [table(rows2, ["kind", "where", "snippet"])];
|
|
49481
|
+
const ref = r.index.ref;
|
|
49482
|
+
if (ref) {
|
|
49483
|
+
lines2.push(
|
|
49484
|
+
ref.complete ? `code as of ${ref.name} (${ref.commit_oid.slice(0, 12)}): the index plus the ${ref.searched_files} files it changed` : `incomplete: ${ref.name} changed ${ref.changed_files}${ref.changed_files >= 500 ? "+" : ""} files and ${ref.searched_files} were searched; a missing hit is not proof of absence`
|
|
49485
|
+
);
|
|
49486
|
+
} else if (r.index.commit_oid && !r.index.fresh) lines2.push(`code index is behind the default branch (indexed ${r.index.commit_oid.slice(0, 12)}); catching up`);
|
|
49487
|
+
else if (!r.index.commit_oid && r.index.indexed_at === null) lines2.push("code index not built yet; catching up");
|
|
49488
|
+
if (r.index.truncated) lines2.push("the code index stopped at its size bounds: some files are not searchable");
|
|
49489
|
+
return lines2.join("\n");
|
|
49490
|
+
}
|
|
49491
|
+
function renderCompare(c8) {
|
|
49492
|
+
const rows2 = c8.files.map((f) => ({ status: f.status, path: f.status === "renamed" ? `${f.old_path} \u2192 ${f.new_path}` : f.new_path ?? f.old_path }));
|
|
49493
|
+
const head = c8.mode === "two-dot" ? `${c8.base_oid.slice(0, 12)}..${c8.head_oid.slice(0, 12)} (trees)` : `${c8.base_oid.slice(0, 12)}...${c8.head_oid.slice(0, 12)} (since merge base ${c8.merge_base_oid?.slice(0, 12) ?? "none: unrelated histories"})`;
|
|
49494
|
+
return [head, table(rows2, ["status", "path"]), ...c8.truncated ? ["(more files changed than listed)"] : []].join("\n");
|
|
49495
|
+
}
|
|
49496
|
+
function renderRange(r) {
|
|
49497
|
+
const rows2 = r.commits.map((c8) => ({ oid: c8.oid.slice(0, 12), date: c8.committer.date.slice(0, 10), author: c8.author.name, subject: c8.message.split("\n", 1)[0] }));
|
|
49498
|
+
const count = (n) => n === null ? "unknown" : String(n);
|
|
49499
|
+
return [
|
|
49500
|
+
`ahead ${count(r.ahead_by)}, behind ${count(r.behind_by)}`,
|
|
49501
|
+
table(rows2, ["oid", "date", "author", "subject"]),
|
|
49502
|
+
...r.truncated ? ["(more commits than listed: --limit up to 250)"] : [],
|
|
49503
|
+
...r.complete ? [] : ["incomplete: the bounded history walk did not finish; counts and the list may be short"]
|
|
49504
|
+
].join("\n");
|
|
49505
|
+
}
|
|
49506
|
+
var renderMergeBase = (m) => m.merge_bases.length ? m.merge_bases.join("\n") : m.complete ? "(none: unrelated histories)" : "unknown: the bounded history walk did not finish";
|
|
49507
|
+
var renderAncestry = (a) => a.is_ancestor === null ? "unknown: the bounded history walk could not decide (not a no)" : `${a.is_ancestor ? "yes" : "no"}: ${a.ancestor_oid.slice(0, 12)} is ${a.is_ancestor ? "" : "not "}in the history of ${a.descendant_oid.slice(0, 12)}`;
|
|
49508
|
+
function renderContaining(r) {
|
|
49509
|
+
const lines2 = r.refs.length ? [...r.refs] : [r.complete ? "(no branch or tag contains it)" : "(none found so far)"];
|
|
49510
|
+
if (r.unknown.length) lines2.push(`not settled (may contain it): ${r.unknown.join(", ")}`);
|
|
49354
49511
|
return lines2.join("\n");
|
|
49355
49512
|
}
|
|
49356
49513
|
function registerOrg(program2, rt) {
|
|
@@ -49361,6 +49518,14 @@ function registerOrg(program2, rt) {
|
|
|
49361
49518
|
ctx.result(created, (v) => kv(v, ["id", "slug", "name", "created_at"]));
|
|
49362
49519
|
})
|
|
49363
49520
|
);
|
|
49521
|
+
org.command("edit [org]").description("Change an organization's settings (organization admins)").option("--actions <on|off>", "turn Actions on or off for every repository of the organization: off starts no runs from pushes, changes or schedules and refuses workflow_dispatch; runs already started go on. A repository can also turn it off for itself (repo edit --actions)").action(
|
|
49522
|
+
act(rt, async (ctx, o, [spec]) => {
|
|
49523
|
+
if (o.actions === void 0) throw usage("nothing to change: pass --actions on|off");
|
|
49524
|
+
if (o.actions !== "on" && o.actions !== "off") throw usage("--actions must be on or off");
|
|
49525
|
+
const updated = await ctx.client("user").orgs.update(spec ?? ctx.org(), { actions_enabled: o.actions === "on" });
|
|
49526
|
+
ctx.result(updated, (v) => kv({ ...v, actions: v.settings.actions_enabled === false ? "off" : "on" }, ["id", "slug", "name", "actions"]));
|
|
49527
|
+
})
|
|
49528
|
+
);
|
|
49364
49529
|
org.command("list").description("Your personal account and the organizations you belong to").option("--limit <n>", "page size", (v) => parseIntOption(v, "--limit")).option("--cursor <cursor>", "page cursor").action(
|
|
49365
49530
|
act(rt, async (ctx, o) => {
|
|
49366
49531
|
const page = await ctx.client("user").orgs.list({ limit: o.limit, cursor: o.cursor });
|
|
@@ -49490,13 +49655,93 @@ function registerRepo(program2, rt) {
|
|
|
49490
49655
|
ctx.result(await ctx.client("agent").repos.get(await ctx.repoId(spec)), renderRepo2);
|
|
49491
49656
|
})
|
|
49492
49657
|
);
|
|
49493
|
-
repo.command("
|
|
49658
|
+
repo.command("edit [repo]").description("Change a repository's settings (repository admins)").option("--actions <on|off>", "turn Actions on or off: off starts no runs from pushes, changes or schedules and refuses workflow_dispatch; runs already started go on").action(
|
|
49659
|
+
act(rt, async (ctx, o, [spec]) => {
|
|
49660
|
+
if (o.actions === void 0) throw usage("nothing to change: pass --actions on|off");
|
|
49661
|
+
if (o.actions !== "on" && o.actions !== "off") throw usage("--actions must be on or off");
|
|
49662
|
+
const c8 = ctx.client("user");
|
|
49663
|
+
const id2 = await ctx.repoId(spec);
|
|
49664
|
+
const current = await c8.repos.get(id2);
|
|
49665
|
+
ctx.result(await c8.repos.update(id2, { actions_enabled: o.actions === "on", version: current.version }), renderRepo2);
|
|
49666
|
+
})
|
|
49667
|
+
);
|
|
49668
|
+
repo.command("log [repo]").description("First-parent commit history of a ref; with --path, only commits that changed that file or directory (no rename detection); filter like git log with --author, --grep, --since, --until (substring matches) and --all-parents (with --path: git log --full-history)").option("--ref <ref>", "branch, tag or commit (default branch when omitted)").option("--path <path>", "repository-relative file or directory").option("--limit <n>", "page size", (v) => parseIntOption(v, "--limit")).option("--scan-limit <n>", "with --path or a filter: commits examined per page (server default 200, 50 with --all-parents; max 500)", (v) => parseIntOption(v, "--scan-limit")).option("--author <text>", "author name or email contains this (case-insensitive)").option("--grep <text>", "commit message contains this (case-insensitive; not a regular expression)").option("--since <date>", "committed at or after this date (ISO-8601)").option("--until <date>", "committed at or before this date (ISO-8601)").option("--all-parents", "walk merged branches too, not just the first-parent line").option("--cursor <cursor>", "page cursor (resumes a --path walk where it stopped)").action(
|
|
49494
49669
|
act(rt, async (ctx, o, [spec]) => {
|
|
49495
|
-
|
|
49496
|
-
|
|
49670
|
+
const filtered = o.author !== void 0 || o.grep !== void 0 || o.since !== void 0 || o.until !== void 0 || !!o.allParents;
|
|
49671
|
+
if (o.scanLimit !== void 0 && o.path === void 0 && !filtered) throw usage("--scan-limit requires --path or a filter");
|
|
49672
|
+
const page = await ctx.client("agent").repos.commits(await ctx.repoId(spec), {
|
|
49673
|
+
ref: o.ref,
|
|
49674
|
+
path: o.path,
|
|
49675
|
+
limit: o.limit,
|
|
49676
|
+
cursor: o.cursor,
|
|
49677
|
+
scan_limit: o.scanLimit,
|
|
49678
|
+
author: o.author,
|
|
49679
|
+
grep: o.grep,
|
|
49680
|
+
since: o.since,
|
|
49681
|
+
until: o.until,
|
|
49682
|
+
all_parents: o.allParents || void 0
|
|
49683
|
+
});
|
|
49497
49684
|
ctx.result(page, renderLog);
|
|
49498
49685
|
})
|
|
49499
49686
|
);
|
|
49687
|
+
repo.command("tree [repo]").description("List a directory of a ref without a clone").option("--ref <ref>", "branch, tag or commit (default branch when omitted)").option("--path <path>", "repository-relative directory (root when omitted)").action(
|
|
49688
|
+
act(rt, async (ctx, o, [spec]) => {
|
|
49689
|
+
ctx.result(await ctx.client("agent").repos.tree(await ctx.repoId(spec), { ref: o.ref, path: o.path }), renderTree);
|
|
49690
|
+
})
|
|
49691
|
+
);
|
|
49692
|
+
repo.command("blob <path> [repo]").description("Print a file of a ref without a clone (binary and very large files are described, not printed)").option("--ref <ref>", "branch, tag or commit (default branch when omitted)").action(
|
|
49693
|
+
act(rt, async (ctx, o, [path3, spec]) => {
|
|
49694
|
+
ctx.result(await ctx.client("agent").repos.blob(await ctx.repoId(spec), { ref: o.ref, path: path3 }), renderBlob);
|
|
49695
|
+
})
|
|
49696
|
+
);
|
|
49697
|
+
repo.command("blame <path> [repo]").description("Who last changed each line of a file, without a clone").option("--ref <ref>", "branch, tag or commit (default branch when omitted)").action(
|
|
49698
|
+
act(rt, async (ctx, o, [path3, spec]) => {
|
|
49699
|
+
ctx.result(await ctx.client("agent").repos.blame(await ctx.repoId(spec), { ref: o.ref, path: path3 }), renderBlame);
|
|
49700
|
+
})
|
|
49701
|
+
);
|
|
49702
|
+
repo.command("search <query> [repo]").description("Search code, tasks, changes and comments; with --ref, code as of that branch, tag or commit (says when a ref was searched incompletely)").option("--kind <kind>", "code, task, change or comment").option("--ref <ref>", "search code as of this branch, tag or commit instead of the default branch").action(
|
|
49703
|
+
act(rt, async (ctx, o, [q, spec]) => {
|
|
49704
|
+
if (o.kind !== void 0 && !["code", "task", "change", "comment"].includes(o.kind)) throw usage("--kind must be code, task, change or comment");
|
|
49705
|
+
if (o.ref !== void 0 && o.kind !== void 0 && o.kind !== "code") throw usage("--ref searches code only");
|
|
49706
|
+
ctx.result(await ctx.client("agent").repos.search(await ctx.repoId(spec), { q, kind: o.kind, ref: o.ref }), renderSearch);
|
|
49707
|
+
})
|
|
49708
|
+
);
|
|
49709
|
+
repo.command("compare <base> <head> [repo]").description("Files head changed since its merge base with base (git diff base...head); --two-dot compares the trees; --commits lists the commits in base..head with ahead/behind counts").option("--two-dot", "diff base's tree against head's tree (git diff base head)").option("--commits", "list the commits in base..head instead of the files").option("--limit <n>", "with --commits: commits listed (default 100, max 250)", (v) => parseIntOption(v, "--limit")).action(
|
|
49710
|
+
act(rt, async (ctx, o, [base2, head, spec]) => {
|
|
49711
|
+
if (o.commits && o.twoDot) throw usage("--commits cannot be combined with --two-dot");
|
|
49712
|
+
if (o.limit !== void 0 && !o.commits) throw usage("--limit requires --commits");
|
|
49713
|
+
const c8 = ctx.client("agent");
|
|
49714
|
+
const id2 = await ctx.repoId(spec);
|
|
49715
|
+
if (o.commits) return ctx.result(await c8.repos.compareCommits(id2, { base: base2, head, limit: o.limit }), renderRange);
|
|
49716
|
+
ctx.result(await c8.repos.compare(id2, { base: base2, head, mode: o.twoDot ? "two-dot" : void 0 }), renderCompare);
|
|
49717
|
+
})
|
|
49718
|
+
);
|
|
49719
|
+
repo.command("merge-base <base> <head> [repo]").description("Every best common ancestor of two branches, tags or commits (git merge-base --all), without a clone").action(
|
|
49720
|
+
act(rt, async (ctx, _o, [base2, head, spec]) => {
|
|
49721
|
+
ctx.result(await ctx.client("agent").repos.mergeBase(await ctx.repoId(spec), { base: base2, head }), renderMergeBase);
|
|
49722
|
+
})
|
|
49723
|
+
);
|
|
49724
|
+
repo.command("is-ancestor <ancestor> <descendant> [repo]").description("Whether a commit is in another's history (git merge-base --is-ancestor): yes, no, or unknown when the bounded walk cannot decide").action(
|
|
49725
|
+
act(rt, async (ctx, _o, [ancestor, descendant, spec]) => {
|
|
49726
|
+
ctx.result(await ctx.client("agent").repos.isAncestor(await ctx.repoId(spec), { ancestor, descendant }), renderAncestry);
|
|
49727
|
+
})
|
|
49728
|
+
);
|
|
49729
|
+
repo.command("contains <commit> [repo]").description("Branches and tags whose history contains a commit (git for-each-ref --contains), without a clone").action(
|
|
49730
|
+
act(rt, async (ctx, _o, [commit, spec]) => {
|
|
49731
|
+
ctx.result(await ctx.client("agent").repos.refsContaining(await ctx.repoId(spec), commit), renderContaining);
|
|
49732
|
+
})
|
|
49733
|
+
);
|
|
49734
|
+
repo.command("commit-change <commit> [repo]").description("The change that brought a commit onto each branch it landed on, from Brnch's record of the landing (every merge strategy); ask this instead of searching git log").action(
|
|
49735
|
+
act(rt, async (ctx, _o, [commit, spec]) => {
|
|
49736
|
+
let oid = /^[0-9a-f]{40}$/.test(commit) ? commit : null;
|
|
49737
|
+
if (!oid) {
|
|
49738
|
+
const r = await ctx.deps.gitOutput(["rev-parse", "--verify", "--quiet", `${commit}^{commit}`], ctx.deps.cwd);
|
|
49739
|
+
if (r.code !== 0) throw usage("pass a full 40-character commit id; short ids and revisions like HEAD resolve only inside a clone");
|
|
49740
|
+
oid = r.stdout.trim();
|
|
49741
|
+
}
|
|
49742
|
+
ctx.result(await ctx.client("agent").repos.commitChange(await ctx.repoId(spec), oid), renderCommitChange);
|
|
49743
|
+
})
|
|
49744
|
+
);
|
|
49500
49745
|
repo.command("clone [repo] [directory]").description("git clone over SSH when this machine is set up for it (`ssh setup`) and the key can be used now, else HTTPS with your login (never written to .git/config); at a terminal without a repository, pick one of yours").option("--ssh", "clone over SSH").option("--https", "clone over HTTPS").action(
|
|
49501
49746
|
act(rt, async (ctx, o, [spec, dir]) => {
|
|
49502
49747
|
if (o.ssh && o.https) throw usage("--ssh and --https are mutually exclusive");
|