brnch-cli 0.18.3 → 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 +726 -142
- package/package.json +1 -1
package/brnch.mjs
CHANGED
|
@@ -29789,8 +29789,8 @@ function convertBaseSchema(schema, ctx) {
|
|
|
29789
29789
|
});
|
|
29790
29790
|
}
|
|
29791
29791
|
ctx.processing.add(refPath);
|
|
29792
|
-
const
|
|
29793
|
-
const zodSchema2 = convertSchema(
|
|
29792
|
+
const resolved2 = resolveRef(refPath, ctx);
|
|
29793
|
+
const zodSchema2 = convertSchema(resolved2, ctx);
|
|
29794
29794
|
ctx.refs.set(refPath, zodSchema2);
|
|
29795
29795
|
ctx.processing.delete(refPath);
|
|
29796
29796
|
return zodSchema2;
|
|
@@ -30573,12 +30573,13 @@ 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
|
|
|
30580
30581
|
// packages/domain/src/capabilities.ts
|
|
30581
|
-
var VIEWER = ["org:read", "members:read", "repo:read", "task:read", "change:read", "session:read", "packages:read"];
|
|
30582
|
+
var VIEWER = ["org:read", "members:read", "repo:read", "task:read", "change:read", "session:read", "packages:read", "migrations:read"];
|
|
30582
30583
|
var REVIEWER = [...VIEWER, "review:comment", "review:resolve", "review:approve"];
|
|
30583
30584
|
var DEVELOPER = [
|
|
30584
30585
|
...VIEWER,
|
|
@@ -30591,9 +30592,11 @@ var DEVELOPER = [
|
|
|
30591
30592
|
"review:comment",
|
|
30592
30593
|
"evidence:report",
|
|
30593
30594
|
"promotion:request",
|
|
30594
|
-
"packages:write"
|
|
30595
|
+
"packages:write",
|
|
30596
|
+
"deploy:read"
|
|
30595
30597
|
];
|
|
30596
|
-
var
|
|
30598
|
+
var DEPLOY_AND_MIGRATIONS = ["deploy:write", "deploy:trigger", "deploy:manage", "migrations:manage", "migrations:approve"];
|
|
30599
|
+
var MAINTAINER = [...DEVELOPER, ...REVIEWER, "repo:admin", ...DEPLOY_AND_MIGRATIONS, "policy:write", "session:manage", "secret_scanning:read", "secret_scanning:resolve"];
|
|
30597
30600
|
var ADMIN = [
|
|
30598
30601
|
...MAINTAINER,
|
|
30599
30602
|
"org:admin",
|
|
@@ -31634,7 +31637,6 @@ var looksLikeHostedLabel = (label2) => /^brnch-hosted(?:-|$)/.test(label2);
|
|
|
31634
31637
|
var DEPLOY_LIMITS = {
|
|
31635
31638
|
maxStepsPerTarget: 12,
|
|
31636
31639
|
maxWatchPaths: 50,
|
|
31637
|
-
maxTargetsPerRepository: 10,
|
|
31638
31640
|
maxCommandLength: 2e3,
|
|
31639
31641
|
maxSummaryLength: 500,
|
|
31640
31642
|
maxDetailKeys: 20,
|
|
@@ -31744,7 +31746,8 @@ var PLANS = Object.freeze({
|
|
|
31744
31746
|
storage_bytes_per_seat: 0,
|
|
31745
31747
|
ai: { fast: 100, strong: 25, agent: 0, assistant: 0 },
|
|
31746
31748
|
private_repo_members: 5,
|
|
31747
|
-
overage: false
|
|
31749
|
+
overage: false,
|
|
31750
|
+
deploy_targets_per_repository: 1
|
|
31748
31751
|
},
|
|
31749
31752
|
pro: {
|
|
31750
31753
|
id: "pro",
|
|
@@ -31763,7 +31766,8 @@ var PLANS = Object.freeze({
|
|
|
31763
31766
|
storage_bytes_per_seat: 0,
|
|
31764
31767
|
ai: { fast: 300, strong: 100, agent: 10, assistant: 100 },
|
|
31765
31768
|
private_repo_members: 5,
|
|
31766
|
-
overage: true
|
|
31769
|
+
overage: true,
|
|
31770
|
+
deploy_targets_per_repository: 10
|
|
31767
31771
|
},
|
|
31768
31772
|
team: {
|
|
31769
31773
|
id: "team",
|
|
@@ -31782,7 +31786,8 @@ var PLANS = Object.freeze({
|
|
|
31782
31786
|
storage_bytes_per_seat: 2 * GB,
|
|
31783
31787
|
ai: { fast: 600, strong: 200, agent: 10, assistant: 100 },
|
|
31784
31788
|
private_repo_members: Number.POSITIVE_INFINITY,
|
|
31785
|
-
overage: true
|
|
31789
|
+
overage: true,
|
|
31790
|
+
deploy_targets_per_repository: 10
|
|
31786
31791
|
},
|
|
31787
31792
|
enterprise: {
|
|
31788
31793
|
id: "enterprise",
|
|
@@ -31801,7 +31806,8 @@ var PLANS = Object.freeze({
|
|
|
31801
31806
|
storage_bytes_per_seat: 5 * GB,
|
|
31802
31807
|
ai: { fast: 1500, strong: 500, agent: 40, assistant: 300 },
|
|
31803
31808
|
private_repo_members: Number.POSITIVE_INFINITY,
|
|
31804
|
-
overage: true
|
|
31809
|
+
overage: true,
|
|
31810
|
+
deploy_targets_per_repository: 1e3
|
|
31805
31811
|
}
|
|
31806
31812
|
});
|
|
31807
31813
|
|
|
@@ -32159,12 +32165,16 @@ var CreateInvitationRequest = external_exports.object({ email: external_exports.
|
|
|
32159
32165
|
var AcceptInvitationRequest = external_exports.object({ token: external_exports.string() });
|
|
32160
32166
|
var CreateTeamRequest = external_exports.object({ slug: Slug, name: displayLine().min(1).max(100) });
|
|
32161
32167
|
var AddTeamMemberRequest = external_exports.object({ user_id: external_exports.string() });
|
|
32168
|
+
var GrantExtraCapabilitySchema = external_exports.enum(["deploy:read", "deploy:trigger", "deploy:manage", "migrations:read", "migrations:manage", "migrations:approve"]);
|
|
32162
32169
|
var CreateGrantRequest = external_exports.object({
|
|
32163
32170
|
principal_type: external_exports.enum(["user", "team"]),
|
|
32164
32171
|
principal_id: external_exports.string().min(1).max(100),
|
|
32165
32172
|
role: RepoGrantRoleSchema,
|
|
32173
|
+
/** Beyond the role, e.g. a developers team that approves a database's migrations. */
|
|
32174
|
+
capabilities: external_exports.array(GrantExtraCapabilitySchema).max(6).optional(),
|
|
32166
32175
|
expires_at: external_exports.iso.datetime().nullable().optional()
|
|
32167
32176
|
});
|
|
32177
|
+
var UpdateGrantRequest = external_exports.object({ capabilities: external_exports.array(GrantExtraCapabilitySchema).max(6) });
|
|
32168
32178
|
var CreateRepoRequest = external_exports.object({
|
|
32169
32179
|
name: RepoName,
|
|
32170
32180
|
description: displayText().max(500).default(""),
|
|
@@ -32205,15 +32215,27 @@ var UpdateRepoRequest = external_exports.object({
|
|
|
32205
32215
|
default_branch: external_exports.string().min(1).max(100).optional(),
|
|
32206
32216
|
/** Replaces the topic list (≤20, lowercase). */
|
|
32207
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(),
|
|
32208
32220
|
version: external_exports.number().int()
|
|
32209
32221
|
});
|
|
32210
32222
|
var PATH_HISTORY_SCAN_MAX = 500;
|
|
32211
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");
|
|
32212
32226
|
var CommitListQuery = external_exports.object({
|
|
32213
32227
|
ref: external_exports.string().optional(),
|
|
32214
32228
|
path: RepoPathQuery.optional(),
|
|
32215
|
-
scan_limit: external_exports.coerce.number().int().min(1).max(PATH_HISTORY_SCAN_MAX).optional()
|
|
32216
|
-
|
|
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;
|
|
32217
32239
|
var RequiredCheckSchema = external_exports.object({
|
|
32218
32240
|
name: external_exports.string().min(1).max(200),
|
|
32219
32241
|
subject: external_exports.enum(["head", "candidate"]).default("head"),
|
|
@@ -32253,11 +32275,16 @@ var PolicyRulesSchema = external_exports.object({
|
|
|
32253
32275
|
});
|
|
32254
32276
|
var AdoptPolicyRequest = external_exports.object({ rules: PolicyRulesSchema, expected_version: external_exports.number().int().nonnegative(), note: external_exports.string().max(1e3).optional() });
|
|
32255
32277
|
var ApplySecureDefaultsRequest = external_exports.object({ expected_version: external_exports.number().int().nonnegative() });
|
|
32278
|
+
var MAX_TASK_BLOCKERS = 50;
|
|
32279
|
+
var TaskRef = external_exports.union([external_exports.number().int().positive(), external_exports.string().trim().regex(/^(task_[0-9a-z]{1,64}|#?\d{1,9})$/, "expected a task number (12 or #12) or a task_\u2026 id")]);
|
|
32280
|
+
var blockedBy = external_exports.array(TaskRef).max(MAX_TASK_BLOCKERS);
|
|
32256
32281
|
var CreateTaskRequest = external_exports.object({
|
|
32257
32282
|
title: displayLine().min(1).max(300),
|
|
32258
32283
|
description: external_exports.string().max(65536).default(""),
|
|
32259
32284
|
acceptance_criteria: external_exports.array(external_exports.string().max(1e3)).max(100).default([]),
|
|
32260
|
-
labels: external_exports.array(external_exports.string().max(50)).max(20).default([])
|
|
32285
|
+
labels: external_exports.array(external_exports.string().max(50)).max(20).default([]),
|
|
32286
|
+
/** Tasks in the same repository this one is blocked by. */
|
|
32287
|
+
blocked_by: blockedBy.default([])
|
|
32261
32288
|
});
|
|
32262
32289
|
var UpdateTaskRequest = external_exports.object({
|
|
32263
32290
|
title: displayLine().min(1).max(300).optional(),
|
|
@@ -32266,8 +32293,13 @@ var UpdateTaskRequest = external_exports.object({
|
|
|
32266
32293
|
labels: external_exports.array(external_exports.string().max(50)).max(20).optional(),
|
|
32267
32294
|
state: external_exports.enum(["open", "in_progress", "blocked", "done", "cancelled"]).optional(),
|
|
32268
32295
|
owner_id: external_exports.string().nullable().optional(),
|
|
32296
|
+
/** Replaces the whole set of blockers. Links are not task fields: changing them alone leaves `version` as it is. */
|
|
32297
|
+
blocked_by: blockedBy.optional(),
|
|
32269
32298
|
version: external_exports.number().int()
|
|
32270
32299
|
});
|
|
32300
|
+
var AddTaskDependencyRequest = external_exports.object({ blocker: TaskRef });
|
|
32301
|
+
var TaskListQuery = external_exports.object({ ready: external_exports.enum(["true", "false"]).optional().transform((v) => v === void 0 ? void 0 : v === "true") });
|
|
32302
|
+
var TaskNumberParam = external_exports.coerce.number().int().positive().max(999999999);
|
|
32271
32303
|
var ClaimTaskRequest = external_exports.object({ session_id: external_exports.string().optional() });
|
|
32272
32304
|
var AddLinkRequest = external_exports.object({ kind: external_exports.enum(["external", "change", "task"]), url: external_exports.url().optional(), target_id: external_exports.string().optional(), title: external_exports.string().max(300).optional() });
|
|
32273
32305
|
var CreateCommentRequest = external_exports.object({ body: external_exports.string().min(1).max(65536) });
|
|
@@ -32438,7 +32470,9 @@ var UpdateOrgRequest = external_exports.object({
|
|
|
32438
32470
|
/** Idle timeout of new agent sessions in minutes (ADR-0040). */
|
|
32439
32471
|
agent_session_idle_minutes: external_exports.number().int().min(15).max(1440).optional(),
|
|
32440
32472
|
/** Brnch Secure Defaults for new repositories (ADR-0081). Turning it on also requires a second factor for admins. */
|
|
32441
|
-
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()
|
|
32442
32476
|
});
|
|
32443
32477
|
var OAuthDecisionRequest = external_exports.object({
|
|
32444
32478
|
approve: external_exports.boolean(),
|
|
@@ -33700,6 +33734,16 @@ var CreateConnectionRequest = external_exports.discriminatedUnion("auth", [
|
|
|
33700
33734
|
name: Name3,
|
|
33701
33735
|
credentials: external_exports.object({ api_token: Secret2 })
|
|
33702
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
|
+
}),
|
|
33703
33747
|
external_exports.object({
|
|
33704
33748
|
auth: external_exports.literal("infisical_oidc"),
|
|
33705
33749
|
provider: external_exports.literal("infisical"),
|
|
@@ -33888,18 +33932,26 @@ var UpsertWorkerEnvRequest = external_exports.object({
|
|
|
33888
33932
|
});
|
|
33889
33933
|
|
|
33890
33934
|
// packages/protocol/src/db-migrations.ts
|
|
33891
|
-
var DB_MIGRATION_FORMATS = ["atlas", "plain", "golang_migrate", "drizzle", "prisma"];
|
|
33892
|
-
var DB_ENGINE_KINDS = ["postgres", "vitess"];
|
|
33893
33935
|
var DB_EXEC_ORDERS = ["linear", "linear_skip", "non_linear"];
|
|
33894
33936
|
var DB_CREDENTIAL_MODES = ["managed_role", "dsn", "branch_password"];
|
|
33937
|
+
var DB_APPLY_ON = ["merge", "deploy", "manual"];
|
|
33938
|
+
var DB_LINT_LEVELS = ["error", "warning", "off"];
|
|
33939
|
+
var DB_DRIFT_MODES = ["off", "on_run", "daily"];
|
|
33940
|
+
var DbLintPolicySchema = external_exports.object({
|
|
33941
|
+
levels: external_exports.record(external_exports.string().regex(/^[A-Za-z0-9_]{1,40}$/), external_exports.enum(DB_LINT_LEVELS)).refine((r) => Object.keys(r).length <= 50, "at most 50 entries").default({}),
|
|
33942
|
+
nolint: external_exports.enum(["honor", "ignore"]).default("honor"),
|
|
33943
|
+
/** Drift found by the last check fails the deploy's migrations step. */
|
|
33944
|
+
drift_blocks_deploy: external_exports.boolean().default(false)
|
|
33945
|
+
});
|
|
33895
33946
|
var Ident2 = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_$]{0,62}$/, "letters, digits and _ only (at most 63)");
|
|
33896
33947
|
var PsName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/, "a PlanetScale name: letters, digits, '.', '_' and '-'");
|
|
33897
33948
|
var SourceFields = {
|
|
33898
33949
|
name: external_exports.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9_-]*$/, "lowercase letters, digits, - and _"),
|
|
33899
33950
|
/** Directory relative to the repository root ("" = the root). */
|
|
33900
33951
|
path: external_exports.string().max(300),
|
|
33901
|
-
|
|
33902
|
-
|
|
33952
|
+
/** `wrangler` directories are D1 sources, which come from the Wrangler configuration. */
|
|
33953
|
+
format: external_exports.enum(["atlas", "plain", "golang_migrate", "drizzle", "prisma"]),
|
|
33954
|
+
engine: external_exports.enum(["postgres", "vitess"]),
|
|
33903
33955
|
/** Branch whose merges create runs; the repository's default branch when omitted. */
|
|
33904
33956
|
branch: external_exports.string().min(1).max(200).optional(),
|
|
33905
33957
|
/** PlanetScale connection (purpose `db_migrations`) of the organization or the repository. */
|
|
@@ -33921,12 +33973,19 @@ var SourceFields = {
|
|
|
33921
33973
|
credential_mode: external_exports.enum(DB_CREDENTIAL_MODES).optional(),
|
|
33922
33974
|
/** Postgres: the role new objects belong to (`SET ROLE`); `postgres` by default for managed roles, none for a DSN. */
|
|
33923
33975
|
run_as_role: external_exports.string().regex(/^[a-z_][a-z0-9_]{0,62}$/).nullable().optional(),
|
|
33924
|
-
/** `approval
|
|
33925
|
-
on_merge: external_exports.enum(["approval", "manual_only"]).
|
|
33976
|
+
/** Deprecated: `apply_on` (`approval` = `merge`, `manual_only` = `manual`). */
|
|
33977
|
+
on_merge: external_exports.enum(["approval", "manual_only"]).optional(),
|
|
33978
|
+
apply_on: external_exports.enum(DB_APPLY_ON).optional(),
|
|
33979
|
+
/** `deploy` and `manual` only: whether a person approves before anything is applied (`merge` always requires it). */
|
|
33980
|
+
require_approval: external_exports.boolean().optional(),
|
|
33926
33981
|
/** Validate the directory on changes (evidence `migrations/<name>`). */
|
|
33927
|
-
change_checks: external_exports.boolean().default(true)
|
|
33982
|
+
change_checks: external_exports.boolean().default(true),
|
|
33983
|
+
lint: DbLintPolicySchema.optional(),
|
|
33984
|
+
drift_mode: external_exports.enum(DB_DRIFT_MODES).optional()
|
|
33928
33985
|
};
|
|
33929
33986
|
var CreateDbMigrationSourceRequest = external_exports.object({
|
|
33987
|
+
/** Only PlanetScale sources are created here: D1 sources come from the Wrangler configuration. */
|
|
33988
|
+
provider: external_exports.literal("planetscale").default("planetscale"),
|
|
33930
33989
|
...SourceFields,
|
|
33931
33990
|
/** Postgres `credential_mode: dsn` only: `postgresql://user:password@host:5432/db?sslmode=verify-full`. Write-only. */
|
|
33932
33991
|
dsn: external_exports.string().min(10).max(2e3).optional()
|
|
@@ -33948,7 +34007,11 @@ var UpdateDbMigrationSourceRequest = external_exports.object({
|
|
|
33948
34007
|
credential_mode: external_exports.enum(DB_CREDENTIAL_MODES),
|
|
33949
34008
|
run_as_role: external_exports.string().regex(/^[a-z_][a-z0-9_]{0,62}$/).nullable(),
|
|
33950
34009
|
on_merge: external_exports.enum(["approval", "manual_only"]),
|
|
34010
|
+
apply_on: external_exports.enum(DB_APPLY_ON),
|
|
34011
|
+
require_approval: external_exports.boolean(),
|
|
33951
34012
|
change_checks: external_exports.boolean(),
|
|
34013
|
+
lint: DbLintPolicySchema,
|
|
34014
|
+
drift_mode: external_exports.enum(DB_DRIFT_MODES),
|
|
33952
34015
|
state: external_exports.enum(["active", "disabled"]),
|
|
33953
34016
|
dsn: external_exports.string().min(10).max(2e3).nullable()
|
|
33954
34017
|
}).partial().extend({ version: external_exports.number().int().min(1) });
|
|
@@ -33956,7 +34019,22 @@ var StartDbMigrationRunRequest = external_exports.object({
|
|
|
33956
34019
|
/** Branch, tag or commit to apply; the source's branch when omitted. */
|
|
33957
34020
|
ref: external_exports.string().min(1).max(200).optional()
|
|
33958
34021
|
});
|
|
33959
|
-
var DecideDbMigrationRunRequest = external_exports.object({
|
|
34022
|
+
var DecideDbMigrationRunRequest = external_exports.object({
|
|
34023
|
+
reason: external_exports.string().max(500).optional(),
|
|
34024
|
+
/** Approving a run a deploy waits on: redeploy that deploy at the same commit once the run succeeds. */
|
|
34025
|
+
redeploy: external_exports.boolean().optional()
|
|
34026
|
+
});
|
|
34027
|
+
|
|
34028
|
+
// packages/protocol/src/approvals.ts
|
|
34029
|
+
var PutApprovalRuleRequest = external_exports.object({
|
|
34030
|
+
required: external_exports.number().int().min(1).max(10),
|
|
34031
|
+
users: external_exports.array(external_exports.string().min(1).max(100)).max(20).default([]),
|
|
34032
|
+
teams: external_exports.array(external_exports.string().min(1).max(100)).max(10).default([]),
|
|
34033
|
+
separation_of_duties: external_exports.boolean().default(false),
|
|
34034
|
+
/** The rule's version when replacing one (optimistic concurrency); omit to create. */
|
|
34035
|
+
version: external_exports.number().int().positive().optional()
|
|
34036
|
+
});
|
|
34037
|
+
var DecideDeploymentRequest = external_exports.object({ reason: external_exports.string().max(500).optional() });
|
|
33960
34038
|
|
|
33961
34039
|
// packages/protocol/src/buildkite.ts
|
|
33962
34040
|
var BUILDKITE_TRUST_MODES = ["contributor_controlled", "pipeline_settings", "target_ref_upload"];
|
|
@@ -34445,6 +34523,48 @@ var MirrorStatus = external_exports.object({
|
|
|
34445
34523
|
timings: external_exports.record(external_exports.string(), external_exports.number())
|
|
34446
34524
|
});
|
|
34447
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
|
+
|
|
34448
34568
|
// packages/protocol/src/repo-mirrors.ts
|
|
34449
34569
|
var StopMirrorRequest = external_exports.object({ confirm: external_exports.string().min(3).max(300), expected_version: external_exports.number().int().positive().optional() }).strict();
|
|
34450
34570
|
var ResetMirrorRefsRequest = external_exports.object({ refs: external_exports.array(external_exports.string().min(1).max(1024)).min(1).max(100) }).strict();
|
|
@@ -35973,6 +36093,7 @@ function orgsGroup(t) {
|
|
|
35973
36093
|
create: (body, o) => t.json({ method: "POST", path: "/v1/orgs", json: validate2(CreateOrgRequest, body, "CreateOrgRequest"), options: o }),
|
|
35974
36094
|
list: (q = {}, o) => t.json({ method: "GET", path: "/v1/orgs", query: { ...q }, options: o }),
|
|
35975
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 }),
|
|
35976
36097
|
members: {
|
|
35977
36098
|
list: (org, q = {}, o) => t.json({ method: "GET", path: path2`/v1/orgs/${org}/members`, query: { ...q }, options: o }),
|
|
35978
36099
|
update: (org, userId, body, o) => t.json({
|
|
@@ -36264,13 +36385,25 @@ function reposGroup(t) {
|
|
|
36264
36385
|
/** Raw file bytes; never interpret them as HTML. */
|
|
36265
36386
|
raw: (repo, q, o) => t.bytes({ method: "GET", path: path2`/v1/repos/${repo}/raw`, query: { ...q }, options: o }),
|
|
36266
36387
|
/** First-parent history of `ref`; with `path`, only commits that changed it — each page examines at most
|
|
36267
|
-
* `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`. */
|
|
36268
36390
|
commits: (repo, q = {}, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/commits`, query: { ...q }, options: o }),
|
|
36269
36391
|
/** Write text files to a branch as one commit, through the same checks as a push (ADR-0078). Retry with the same
|
|
36270
36392
|
* `idempotencyKey`: a retry after an uncertain outcome never commits twice. */
|
|
36271
36393
|
commitFiles: (repo, body, o) => t.json({ method: "POST", path: path2`/v1/repos/${repo}/commits`, json: validate2(CommitFilesRequest, body, "CommitFilesRequest"), options: o }),
|
|
36272
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. */
|
|
36273
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 }),
|
|
36274
36407
|
diff: (repo, q, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/diff`, query: { ...q }, options: o }),
|
|
36275
36408
|
pushes: (repo, q = {}, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/pushes`, query: { ...q }, options: o }),
|
|
36276
36409
|
search: (repo, q, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/search`, query: { ...q }, options: o }),
|
|
@@ -36299,6 +36432,8 @@ function reposGroup(t) {
|
|
|
36299
36432
|
grants: {
|
|
36300
36433
|
list: (repo, q = {}, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/grants`, query: { ...q }, options: o }),
|
|
36301
36434
|
create: (repo, body, o) => t.json({ method: "POST", path: path2`/v1/repos/${repo}/grants`, json: validate2(CreateGrantRequest, body, "CreateGrantRequest"), options: o }),
|
|
36435
|
+
/** Replace the capabilities the grant carries beyond its role (ADR-0088), e.g. `["migrations:approve"]`. */
|
|
36436
|
+
update: (repo, grant, body, o) => t.json({ method: "PATCH", path: path2`/v1/repos/${repo}/grants/${grant}`, json: validate2(UpdateGrantRequest, body, "UpdateGrantRequest"), options: o }),
|
|
36302
36437
|
delete: (repo, grant, o) => t.void({ method: "DELETE", path: path2`/v1/repos/${repo}/grants/${grant}`, options: o })
|
|
36303
36438
|
}
|
|
36304
36439
|
};
|
|
@@ -36501,9 +36636,13 @@ function delegationsGroup(t) {
|
|
|
36501
36636
|
function tasksGroup(t) {
|
|
36502
36637
|
return {
|
|
36503
36638
|
create: (repo, body, o) => t.json({ method: "POST", path: path2`/v1/repos/${repo}/tasks`, json: validate2(CreateTaskRequest, body, "CreateTaskRequest"), options: o }),
|
|
36504
|
-
/**
|
|
36639
|
+
/**
|
|
36640
|
+
* `label` (name or id) and `assignee` (handle or user id) filter the list. `ready: true` keeps only the frontier:
|
|
36641
|
+
* open tasks whose blockers are all done or cancelled (`false`: every other task).
|
|
36642
|
+
*/
|
|
36505
36643
|
list: (repo, q = {}, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/tasks`, query: { ...q }, options: o }),
|
|
36506
36644
|
get: (task, o) => t.json({ method: "GET", path: path2`/v1/tasks/${task}`, options: o }),
|
|
36645
|
+
byNumber: (repo, number4, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/tasks/by-number/${number4}`, options: o }),
|
|
36507
36646
|
/** `version` is required: a stale version fails with VERSION_CONFLICT instead of overwriting someone else's edit. */
|
|
36508
36647
|
update: (task, body, o) => t.json({ method: "PATCH", path: path2`/v1/tasks/${task}`, json: validate2(UpdateTaskRequest, body, "UpdateTaskRequest"), options: o }),
|
|
36509
36648
|
/** Advisory claim; CONFLICT when someone else holds it. */
|
|
@@ -36513,28 +36652,50 @@ function tasksGroup(t) {
|
|
|
36513
36652
|
create: (task, body, o) => t.json({ method: "POST", path: path2`/v1/tasks/${task}/comments`, json: validate2(CreateCommentRequest, body, "CreateCommentRequest"), options: o }),
|
|
36514
36653
|
list: (task, q = {}, o) => t.json({ method: "GET", path: path2`/v1/tasks/${task}/comments`, query: { ...q }, options: o })
|
|
36515
36654
|
},
|
|
36655
|
+
/** "Blocked by" links within the task's repository (ADR-0089). A blocker is a task number or `task_…` id. */
|
|
36656
|
+
dependencies: {
|
|
36657
|
+
list: (task, o) => t.json({ method: "GET", path: path2`/v1/tasks/${task}/dependencies`, options: o }),
|
|
36658
|
+
/** Idempotent: adding an existing link changes nothing. CYCLE_DETECTED if the blocker already waits on this task. */
|
|
36659
|
+
add: (task, body, o) => t.json({ method: "POST", path: path2`/v1/tasks/${task}/dependencies`, json: validate2(AddTaskDependencyRequest, body, "AddTaskDependencyRequest"), options: o }),
|
|
36660
|
+
remove: (task, blocker, o) => t.void({ method: "DELETE", path: path2`/v1/tasks/${task}/dependencies/${String(blocker)}`, options: o })
|
|
36661
|
+
},
|
|
36516
36662
|
addLink: (task, body, o) => t.json({ method: "POST", path: path2`/v1/tasks/${task}/links`, json: validate2(AddLinkRequest, body, "AddLinkRequest"), options: o })
|
|
36517
36663
|
};
|
|
36518
36664
|
}
|
|
36519
36665
|
|
|
36520
36666
|
// packages/sdk/src/groups/db-migrations.ts
|
|
36521
36667
|
function dbMigrationsGroup(t) {
|
|
36522
|
-
const decide = (decision) => (id2, reason, o) =>
|
|
36668
|
+
const decide = (decision) => (id2, reason, o) => {
|
|
36669
|
+
const body = typeof reason === "string" ? { reason } : { ...reason?.reason ? { reason: reason.reason } : {}, ...reason?.redeploy ? { redeploy: true } : {} };
|
|
36670
|
+
return t.json({ method: "POST", path: path2`/v1/db-migration-runs/${id2}/${decision}`, json: validate2(DecideDbMigrationRunRequest, body, "DecideDbMigrationRunRequest"), options: o });
|
|
36671
|
+
};
|
|
36523
36672
|
return {
|
|
36524
36673
|
sources: {
|
|
36525
36674
|
list: (repo, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/db-migrations/sources`, options: o }),
|
|
36526
36675
|
create: (repo, body, o) => t.json({ method: "POST", path: path2`/v1/repos/${repo}/db-migrations/sources`, json: validate2(CreateDbMigrationSourceRequest, body, "CreateDbMigrationSourceRequest"), options: o }),
|
|
36527
36676
|
get: (id2, o) => t.json({ method: "GET", path: path2`/v1/db-migration-sources/${id2}`, options: o }),
|
|
36528
36677
|
update: (id2, body, o) => t.json({ method: "PATCH", path: path2`/v1/db-migration-sources/${id2}`, json: validate2(UpdateDbMigrationSourceRequest, body, "UpdateDbMigrationSourceRequest"), options: o }),
|
|
36529
|
-
delete: (id2, o) => t.void({ method: "DELETE", path: path2`/v1/db-migration-sources/${id2}`, options: o })
|
|
36678
|
+
delete: (id2, o) => t.void({ method: "DELETE", path: path2`/v1/db-migration-sources/${id2}`, options: o }),
|
|
36679
|
+
/** Who may approve the source's runs now (ADR-0088). */
|
|
36680
|
+
approvers: (id2, o) => t.json({ method: "GET", path: path2`/v1/db-migration-sources/${id2}/approvers`, options: o }),
|
|
36681
|
+
/** Protect the database: every run waits for these approvals, and it can no longer apply without approval. */
|
|
36682
|
+
protect: (id2, body, o) => t.json({ method: "PUT", path: path2`/v1/db-migration-sources/${id2}/approval-rule`, json: validate2(PutApprovalRuleRequest, body, "PutApprovalRuleRequest"), options: o }),
|
|
36683
|
+
unprotect: (id2, o) => t.json({ method: "DELETE", path: path2`/v1/db-migration-sources/${id2}/approval-rule`, options: o })
|
|
36530
36684
|
},
|
|
36531
36685
|
/** Migration directories found in the repository tree; `configured` marks those a source already covers. */
|
|
36532
36686
|
suggestions: (repo, ref, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/db-migrations/suggestions`, query: ref ? { ref } : {}, options: o }),
|
|
36533
36687
|
/** What applying the source at `ref` would do. `live` reads the database now instead of the last snapshot. */
|
|
36534
36688
|
plan: (sourceId2, q = {}, o) => t.json({ method: "GET", path: path2`/v1/db-migration-sources/${sourceId2}/plan`, query: { ref: q.ref, live: q.live ? "true" : void 0 }, options: o }),
|
|
36689
|
+
/** The schema the source's migrations produce at `ref` (the source's branch by default), from replaying them. */
|
|
36690
|
+
schema: (sourceId2, ref, o) => t.json({ method: "GET", path: path2`/v1/db-migration-sources/${sourceId2}/schema`, query: ref ? { ref } : {}, options: o }),
|
|
36691
|
+
/** Compares the database with what its applied migrations produce, now, and stores the result (admins). */
|
|
36692
|
+
checkDrift: (sourceId2, o) => t.json({ method: "POST", path: path2`/v1/db-migration-sources/${sourceId2}/drift`, options: o }),
|
|
36535
36693
|
/** Reads the applied versions from the database again and returns the plan at the source's branch. */
|
|
36536
36694
|
refresh: (sourceId2, o) => t.json({ method: "POST", path: path2`/v1/db-migration-sources/${sourceId2}/refresh`, options: o }),
|
|
36537
|
-
/**
|
|
36695
|
+
/**
|
|
36696
|
+
* A manual run (`migrations:manage`). The request is the approval when the caller also holds `migrations:approve` and
|
|
36697
|
+
* the source is not protected; otherwise the run waits for approval. `run` is null when nothing is pending.
|
|
36698
|
+
*/
|
|
36538
36699
|
startRun: (sourceId2, body = {}, o) => t.json({ method: "POST", path: path2`/v1/db-migration-sources/${sourceId2}/runs`, json: validate2(StartDbMigrationRunRequest, body, "StartDbMigrationRunRequest"), options: o }),
|
|
36539
36700
|
runs: {
|
|
36540
36701
|
list: (repo, q = {}, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/db-migrations/runs`, query: { ...q }, options: o }),
|
|
@@ -36544,12 +36705,13 @@ function dbMigrationsGroup(t) {
|
|
|
36544
36705
|
reject: decide("reject"),
|
|
36545
36706
|
retry: decide("retry"),
|
|
36546
36707
|
cancel: decide("cancel"),
|
|
36547
|
-
/** The plan of every source a change touches
|
|
36708
|
+
/** The plan of every source a change touches: the files the change adds (with statements), lint, schema effect. */
|
|
36548
36709
|
changePlans: (changeId, o) => t.json({ method: "GET", path: path2`/v1/changes/${changeId}/db-migrations`, options: o })
|
|
36549
36710
|
};
|
|
36550
36711
|
}
|
|
36551
36712
|
|
|
36552
36713
|
// packages/sdk/src/groups/deploy.ts
|
|
36714
|
+
var isCallOptions = (x) => "signal" in x || "idempotencyKey" in x;
|
|
36553
36715
|
function deployGroup(t) {
|
|
36554
36716
|
return {
|
|
36555
36717
|
stepKinds: (o) => t.json({ method: "GET", path: "/v1/deploy/step-kinds", options: o }),
|
|
@@ -36559,19 +36721,36 @@ function deployGroup(t) {
|
|
|
36559
36721
|
suggestions: (repo, ref, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/deploy-suggestions`, query: ref ? { ref } : {}, options: o }),
|
|
36560
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 }),
|
|
36561
36723
|
targets: {
|
|
36562
|
-
|
|
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
|
+
},
|
|
36563
36732
|
create: (repo, body, o) => t.json({ method: "POST", path: path2`/v1/repos/${repo}/deploy-targets`, json: validate2(CreateDeployTargetRequest, body, "CreateDeployTargetRequest"), options: o }),
|
|
36564
36733
|
get: (id2, o) => t.json({ method: "GET", path: path2`/v1/deploy-targets/${id2}`, options: o }),
|
|
36565
36734
|
update: (id2, body, o) => t.json({ method: "PATCH", path: path2`/v1/deploy-targets/${id2}`, json: validate2(UpdateDeployTargetRequest, body, "UpdateDeployTargetRequest"), options: o }),
|
|
36566
36735
|
remove: (id2, o) => t.json({ method: "DELETE", path: path2`/v1/deploy-targets/${id2}`, options: o }),
|
|
36567
36736
|
/** Deploy the branch head again (or a commit this target deployed before). */
|
|
36568
36737
|
deploy: (id2, body = {}, o) => t.json({ method: "POST", path: path2`/v1/deploy-targets/${id2}/deploy`, json: validate2(RedeployRequest, body, "RedeployRequest"), options: o }),
|
|
36569
|
-
rollback: (id2, body, o) => t.json({ method: "POST", path: path2`/v1/deploy-targets/${id2}/rollback`, json: validate2(RollbackRequest, body, "RollbackRequest"), options: o })
|
|
36738
|
+
rollback: (id2, body, o) => t.json({ method: "POST", path: path2`/v1/deploy-targets/${id2}/rollback`, json: validate2(RollbackRequest, body, "RollbackRequest"), options: o }),
|
|
36739
|
+
/** Who may approve the target's production deploys now (ADR-0088). */
|
|
36740
|
+
approvers: (id2, o) => t.json({ method: "GET", path: path2`/v1/deploy-targets/${id2}/approvers`, options: o }),
|
|
36741
|
+
/** Protect the target: production deploys and rollbacks wait for these approvals. Pass `version` to replace a rule. */
|
|
36742
|
+
protect: (id2, body, o) => t.json({ method: "PUT", path: path2`/v1/deploy-targets/${id2}/approval-rule`, json: validate2(PutApprovalRuleRequest, body, "PutApprovalRuleRequest"), options: o }),
|
|
36743
|
+
/** Remove the rule; deploys waiting for it go on. */
|
|
36744
|
+
unprotect: (id2, o) => t.json({ method: "DELETE", path: path2`/v1/deploy-targets/${id2}/approval-rule`, options: o })
|
|
36570
36745
|
},
|
|
36571
36746
|
deployments: {
|
|
36572
36747
|
list: (repo, q = {}, o) => t.json({ method: "GET", path: path2`/v1/repos/${repo}/deployments`, query: { ...q }, options: o }),
|
|
36573
36748
|
get: (id2, o) => t.json({ method: "GET", path: path2`/v1/deployments/${id2}`, options: o }),
|
|
36574
36749
|
cancel: (id2, o) => t.json({ method: "POST", path: path2`/v1/deployments/${id2}/cancel`, options: o }),
|
|
36750
|
+
/** Approve a deploy of a protected target (people only; the last approval needed starts it). */
|
|
36751
|
+
approve: (id2, o) => t.json({ method: "POST", path: path2`/v1/deployments/${id2}/approve`, json: {}, options: o }),
|
|
36752
|
+
/** Reject it: the deployment is cancelled. */
|
|
36753
|
+
reject: (id2, reason, o) => t.json({ method: "POST", path: path2`/v1/deployments/${id2}/reject`, json: validate2(DecideDeploymentRequest, reason ? { reason } : {}, "DecideDeploymentRequest"), options: o }),
|
|
36575
36754
|
ofChange: (change, o) => t.json({ method: "GET", path: path2`/v1/changes/${change}/deployments`, options: o })
|
|
36576
36755
|
}
|
|
36577
36756
|
};
|
|
@@ -37050,7 +37229,7 @@ function invalidAgentToolFlag(flag) {
|
|
|
37050
37229
|
}
|
|
37051
37230
|
|
|
37052
37231
|
// packages/cli/package.json
|
|
37053
|
-
var version2 = "0.
|
|
37232
|
+
var version2 = "0.19.1";
|
|
37054
37233
|
|
|
37055
37234
|
// packages/cli/src/brand.ts
|
|
37056
37235
|
var BRAND = {
|
|
@@ -38787,18 +38966,13 @@ async function gitConfigPath(deps) {
|
|
|
38787
38966
|
}
|
|
38788
38967
|
|
|
38789
38968
|
// packages/cli/src/work-ref.ts
|
|
38790
|
-
var MAX_PAGES = 20;
|
|
38791
|
-
var PAGE = 100;
|
|
38792
38969
|
async function issueByNumber(client, repo, n) {
|
|
38793
|
-
|
|
38794
|
-
|
|
38795
|
-
|
|
38796
|
-
|
|
38797
|
-
|
|
38798
|
-
if (!page.next_cursor) return null;
|
|
38799
|
-
cursor2 = page.next_cursor;
|
|
38970
|
+
try {
|
|
38971
|
+
return (await client.tasks.byNumber(repo, n)).id;
|
|
38972
|
+
} catch (e) {
|
|
38973
|
+
if (isBrnchApiError(e) && e.code === "NOT_FOUND") return null;
|
|
38974
|
+
throw e;
|
|
38800
38975
|
}
|
|
38801
|
-
return null;
|
|
38802
38976
|
}
|
|
38803
38977
|
async function changeByNumber(client, repo, n) {
|
|
38804
38978
|
try {
|
|
@@ -38824,6 +38998,21 @@ async function resolveChangeId(spec, client, repo) {
|
|
|
38824
38998
|
if (!id2) throw notFound(`change #${n}`, repo.spec());
|
|
38825
38999
|
return id2;
|
|
38826
39000
|
}
|
|
39001
|
+
var TASK_ARG = "task number (12 or #12) in --repo, or a task_\u2026 id";
|
|
39002
|
+
async function resolveTaskId(spec, client, repo) {
|
|
39003
|
+
const text2 = spec.trim();
|
|
39004
|
+
const rest = text2.startsWith("issue:") ? text2.slice("issue:".length) : text2;
|
|
39005
|
+
if (rest.startsWith("task_")) return rest;
|
|
39006
|
+
const digits = /^#?(\d{1,9})$/.exec(rest)?.[1];
|
|
39007
|
+
if (!digits) {
|
|
39008
|
+
if (rest.startsWith("chg_") || rest.startsWith("change:")) throw usage(`${spec} is a change, not a task`);
|
|
39009
|
+
throw usage(`cannot read task "${spec}": expected a number (12 or #12) or a task_\u2026 id`);
|
|
39010
|
+
}
|
|
39011
|
+
const n = Number(digits);
|
|
39012
|
+
const id2 = await issueByNumber(client, await repo.id(), n);
|
|
39013
|
+
if (!id2) throw notFound(`task #${n}`, repo.spec());
|
|
39014
|
+
return id2;
|
|
39015
|
+
}
|
|
38827
39016
|
async function resolveWorkRef(spec, client, repoId) {
|
|
38828
39017
|
const m = /^(issue|change):(.+)$/.exec(spec.trim());
|
|
38829
39018
|
const kind = m?.[1] ?? null;
|
|
@@ -39449,6 +39638,9 @@ async function createContext(deps, globals, state2) {
|
|
|
39449
39638
|
function changeId(spec, pref = "agent") {
|
|
39450
39639
|
return resolveChangeId(spec, client(pref), { id: () => repoId(void 0, pref), spec: () => repoSpec() });
|
|
39451
39640
|
}
|
|
39641
|
+
function taskId(spec, pref = "agent") {
|
|
39642
|
+
return resolveTaskId(spec, client(pref), { id: () => repoId(void 0, pref), spec: () => repoSpec() });
|
|
39643
|
+
}
|
|
39452
39644
|
function org() {
|
|
39453
39645
|
const o = targetOrg ?? storedCred?.account?.default_org;
|
|
39454
39646
|
if (!o) {
|
|
@@ -39519,6 +39711,7 @@ async function createContext(deps, globals, state2) {
|
|
|
39519
39711
|
repoSpec,
|
|
39520
39712
|
repoId,
|
|
39521
39713
|
changeId,
|
|
39714
|
+
taskId,
|
|
39522
39715
|
org,
|
|
39523
39716
|
sessionId,
|
|
39524
39717
|
info,
|
|
@@ -39638,6 +39831,15 @@ function cell(v) {
|
|
|
39638
39831
|
if (typeof v === "number" || typeof v === "boolean") return String(v);
|
|
39639
39832
|
return JSON.stringify(v);
|
|
39640
39833
|
}
|
|
39834
|
+
function actorLabel(a) {
|
|
39835
|
+
const person = a.handle ? `@${a.handle}` : a.user_name ?? null;
|
|
39836
|
+
if (a.type === "human") return person ?? a.display_name ?? `${a.type} ${a.id}`;
|
|
39837
|
+
if (a.type === "agent") {
|
|
39838
|
+
const name = a.display_name ?? `agent ${a.id}`;
|
|
39839
|
+
return person ? `${name} (agent for ${person})` : name;
|
|
39840
|
+
}
|
|
39841
|
+
return a.display_name ?? `${a.type} ${a.id}`;
|
|
39842
|
+
}
|
|
39641
39843
|
var tildePath = (path3, home2) => home2 && path3.startsWith(`${home2}/`) ? `~${path3.slice(home2.length)}` : path3;
|
|
39642
39844
|
function kv(obj3, keys) {
|
|
39643
39845
|
const o = obj3 ?? {};
|
|
@@ -39710,6 +39912,7 @@ function instructionsSnippet(tool) {
|
|
|
39710
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.`,
|
|
39711
39913
|
` - Commit authors and trailers (\`Agent-Session:\`, \`Co-Authored-By:\`) are claims and attribute nothing; you do not need them for attribution.`,
|
|
39712
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.`,
|
|
39713
39916
|
`- Record key decisions as you make them: \`${cli} decision "\u2026" --reason "\u2026" --file <path>\`. Blame shows them with the lines they explain.`,
|
|
39714
39917
|
`- Other agents may be working here at the same time. Run \`${cli} status\` before you start to see who is working on what.`,
|
|
39715
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.`,
|
|
@@ -39737,7 +39940,7 @@ ${snippet}
|
|
|
39737
39940
|
|
|
39738
39941
|
// packages/cli/src/commands/session.ts
|
|
39739
39942
|
var renderSession = (s) => kv(
|
|
39740
|
-
{ ...s, agent: `${s.agent.name} (${s.agent.id})`, sponsor:
|
|
39943
|
+
{ ...s, agent: `${s.agent.name} (${s.agent.id})`, sponsor: actorLabel(s.sponsor) },
|
|
39741
39944
|
["id", "title", "agent", "state", "presence", "attention", "repository_id", "task_id", "claimed_task_id", "sponsor", "scopes", "open_blockers", "last_seen_at", "expires_at", "max_expires_at", "end_reason"]
|
|
39742
39945
|
);
|
|
39743
39946
|
var renderStarted = (s) => `${renderSession(s.session)}
|
|
@@ -41242,7 +41445,7 @@ function sessionLines(s, now, you) {
|
|
|
41242
41445
|
...s.paths.length ? [` touching: ${pathList(s.paths)}`] : []
|
|
41243
41446
|
];
|
|
41244
41447
|
}
|
|
41245
|
-
var claimLine = (c8) => ` ${c8.key} ${c8.holder
|
|
41448
|
+
var claimLine = (c8) => ` ${c8.key} ${actorLabel(c8.holder)}${c8.session_id ? ` (${c8.session_id})` : ""}${c8.change ? ` \xB7 change #${c8.change.number}` : ""}${c8.note ? ` \xB7 ${c8.note}` : ""}`;
|
|
41246
41449
|
function renderStatus(st, now = Date.now()) {
|
|
41247
41450
|
const others = st.sessions.filter((s) => s.id !== st.you);
|
|
41248
41451
|
const out = [others.length ? `${others.length} other agent session${others.length === 1 ? "" : "s"} working in this repository.` : "No other agent sessions are working in this repository."];
|
|
@@ -41797,9 +42000,9 @@ async function runHook(deps, state2, tool, eventArg) {
|
|
|
41797
42000
|
let meta3 = await readMeta(deps.env, id2);
|
|
41798
42001
|
if (!meta3) {
|
|
41799
42002
|
const baseUrl = await baseUrlOf(deps);
|
|
41800
|
-
const
|
|
41801
|
-
if (!
|
|
41802
|
-
meta3 = { client_key: clientKey, tool: t, model: null, repo:
|
|
42003
|
+
const resolved2 = await resolveRepo(deps, info.cwd ?? deps.cwd, baseUrl);
|
|
42004
|
+
if (!resolved2) return;
|
|
42005
|
+
meta3 = { client_key: clientKey, tool: t, model: null, repo: resolved2.repo, repo_root: resolved2.root, base_url: baseUrl, started_at: new Date(now).toISOString(), last_event_at: new Date(now).toISOString(), ended: false };
|
|
41803
42006
|
}
|
|
41804
42007
|
if (info.model && !meta3.model) meta3.model = redactText(info.model).text.slice(0, 200);
|
|
41805
42008
|
if (!meta3.model && t === "claude-code" && typeof payload.transcript_path === "string" && /^(Stop|SessionEnd)$/.test(info.event)) meta3.model = await modelFromTranscript(deps, payload.transcript_path);
|
|
@@ -42003,22 +42206,22 @@ function registerCapture(program2, rt) {
|
|
|
42003
42206
|
act(rt, async (ctx, o, words) => {
|
|
42004
42207
|
const summary = words.join(" ").trim();
|
|
42005
42208
|
if (!summary) throw usage("decision text is required");
|
|
42006
|
-
const
|
|
42007
|
-
if (!
|
|
42209
|
+
const resolved2 = await resolveRepo(ctx.deps, ctx.deps.cwd, ctx.baseUrl);
|
|
42210
|
+
if (!resolved2) throw usage(`this directory is not a repository hosted on ${ctx.baseUrl}`);
|
|
42008
42211
|
const now = Date.now();
|
|
42009
|
-
const live = (await listSpool(ctx.deps.env)).filter((s) => s.meta.repo_root ===
|
|
42212
|
+
const live = (await listSpool(ctx.deps.env)).filter((s) => s.meta.repo_root === resolved2.root && !s.meta.ended && now - Date.parse(s.meta.last_event_at) < 12 * 36e5).sort((a, b) => b.meta.last_event_at.localeCompare(a.meta.last_event_at))[0];
|
|
42010
42213
|
let id2;
|
|
42011
42214
|
let meta3;
|
|
42012
42215
|
if (live) {
|
|
42013
42216
|
id2 = live.id;
|
|
42014
42217
|
meta3 = live.meta;
|
|
42015
42218
|
} else {
|
|
42016
|
-
const key = `manual:${spoolId(
|
|
42219
|
+
const key = `manual:${spoolId(resolved2.root)}:${new Date(now).toISOString().slice(0, 10)}`;
|
|
42017
42220
|
id2 = spoolId(key);
|
|
42018
|
-
meta3 = await readMeta(ctx.deps.env, id2) ?? { client_key: key, tool: "manual", model: null, repo:
|
|
42221
|
+
meta3 = await readMeta(ctx.deps.env, id2) ?? { client_key: key, tool: "manual", model: null, repo: resolved2.repo, repo_root: resolved2.root, base_url: ctx.baseUrl, started_at: new Date(now).toISOString(), last_event_at: new Date(now).toISOString(), ended: false };
|
|
42019
42222
|
}
|
|
42020
42223
|
const paths = (o.file ?? []).map((f) => {
|
|
42021
|
-
const rel = relative2(canonical(
|
|
42224
|
+
const rel = relative2(canonical(resolved2.root), canonical(isAbsolute6(f) ? f : resolve4(ctx.deps.cwd, f)));
|
|
42022
42225
|
if (!rel || rel.startsWith("..") || isAbsolute6(rel)) throw usage(`${f} is outside the repository`);
|
|
42023
42226
|
return rel.split(sep2).join("/");
|
|
42024
42227
|
});
|
|
@@ -42062,22 +42265,22 @@ ${report3.errors.join("\n")}` : ""}`);
|
|
|
42062
42265
|
rt.state.noUpdateHint = true;
|
|
42063
42266
|
const [cmd, ...args] = argv;
|
|
42064
42267
|
if (!cmd) throw usage(`usage: ${BRAND.cli} capture wrap -- <command> [args\u2026]`);
|
|
42065
|
-
const
|
|
42268
|
+
const resolved2 = await resolveRepo(ctx.deps, ctx.deps.cwd, ctx.baseUrl);
|
|
42066
42269
|
const cfg = await readCaptureConfig(ctx.deps.env);
|
|
42067
|
-
const before =
|
|
42068
|
-
if (!
|
|
42270
|
+
const before = resolved2 && cfg.enabled ? await snapshotWorktree(ctx.deps, resolved2.root) : null;
|
|
42271
|
+
if (!resolved2) ctx.info(`not a repository hosted on ${ctx.baseUrl}: running without capture`);
|
|
42069
42272
|
const code = await runInherit(cmd, args, ctx.deps.cwd, ctx.deps.env);
|
|
42070
42273
|
ctx.setExit(code);
|
|
42071
|
-
if (!
|
|
42072
|
-
const after = await snapshotWorktree(ctx.deps,
|
|
42274
|
+
if (!resolved2 || !before) return;
|
|
42275
|
+
const after = await snapshotWorktree(ctx.deps, resolved2.root);
|
|
42073
42276
|
if (!after) return;
|
|
42074
|
-
const diff = await ctx.deps.sys.run("git", ["-c", "core.hooksPath=/dev/null", "diff", "--no-color", "--no-renames", "--no-ext-diff", "-U0", before, after], { cwd:
|
|
42277
|
+
const diff = await ctx.deps.sys.run("git", ["-c", "core.hooksPath=/dev/null", "diff", "--no-color", "--no-renames", "--no-ext-diff", "-U0", before, after], { cwd: resolved2.root, timeoutMs: 12e4 });
|
|
42075
42278
|
const label2 = (o.tool ?? cmd.split(/[\\/]/).pop() ?? "command").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40);
|
|
42076
42279
|
const now = Date.now();
|
|
42077
42280
|
const key = `wrapped:${label2}:${randomBytes4(8).toString("hex")}`;
|
|
42078
42281
|
const id2 = spoolId(key);
|
|
42079
|
-
const meta3 = { client_key: key, tool: "wrapped", model: o.model ? redactText(o.model).text.slice(0, 200) : null, repo:
|
|
42080
|
-
const edits = await editsFromDiff(diff.stdout, { repoRoot:
|
|
42282
|
+
const meta3 = { client_key: key, tool: "wrapped", model: o.model ? redactText(o.model).text.slice(0, 200) : null, repo: resolved2.repo, repo_root: resolved2.root, base_url: ctx.baseUrl, started_at: new Date(now).toISOString(), last_event_at: new Date(now).toISOString(), ended: true };
|
|
42283
|
+
const edits = await editsFromDiff(diff.stdout, { repoRoot: resolved2.root, config: cfg, readFile: (p) => ctx.deps.readFile(p).catch(() => null) }, label2);
|
|
42081
42284
|
await writeMeta(ctx.deps.env, id2, meta3);
|
|
42082
42285
|
await appendEvents(ctx.deps.env, id2, [{ type: "capture.started", payload: { source: label2 } }, ...edits, { type: "capture.ended", payload: { reason: `exit ${code}` } }], now);
|
|
42083
42286
|
startFlush(ctx.deps);
|
|
@@ -45918,12 +46121,35 @@ function seedJson(p) {
|
|
|
45918
46121
|
}
|
|
45919
46122
|
|
|
45920
46123
|
// packages/cli/src/commands/task.ts
|
|
45921
|
-
var
|
|
46124
|
+
var BLOCKER_ARG = "the task it is blocked by: number (12 or #12) or task_\u2026 id";
|
|
46125
|
+
var resolved = (t) => t.state === "done" || t.state === "cancelled";
|
|
46126
|
+
var summaries = (list3) => list3.map((t) => `#${t.number} ${t.title} (${t.state})`).join("\n ") || "(none)";
|
|
46127
|
+
function renderDependencies(d) {
|
|
46128
|
+
const more = d.blocks_count > d.blocks.length ? `
|
|
46129
|
+
\u2026 and ${d.blocks_count - d.blocks.length} more` : "";
|
|
46130
|
+
return `Ready: ${d.is_ready ? "yes" : "no"}
|
|
46131
|
+
Blocked by:
|
|
46132
|
+
${summaries(d.blocked_by)}
|
|
46133
|
+
Blocks:
|
|
46134
|
+
${summaries(d.blocks)}${more}`;
|
|
46135
|
+
}
|
|
46136
|
+
var renderTask = (t) => kv({ ...t, claimed_by: t.claim ? actorLabel(t.claim.actor) : null }, ["id", "number", "title", "state", "owner_id", "claimed_by", "labels", "updated_at", "version"]) + (t.blocked_by?.length || t.blocks_count ? `
|
|
46137
|
+
|
|
46138
|
+
${renderDependencies(t)}` : "") + (t.description ? `
|
|
45922
46139
|
|
|
45923
46140
|
${t.description}` : "") + (t.acceptance_criteria.length ? `
|
|
45924
46141
|
|
|
45925
46142
|
Acceptance criteria:
|
|
45926
46143
|
${t.acceptance_criteria.map((c8) => ` - ${c8}`).join("\n")}` : "");
|
|
46144
|
+
var waitingOn = (t) => (t.blocked_by ?? []).filter((b) => !resolved(b)).map((b) => `#${b.number}`).join(" ");
|
|
46145
|
+
function blockersFrom(values, allowNone) {
|
|
46146
|
+
if (!values) return void 0;
|
|
46147
|
+
if (values.length === 1 && values[0] === "none") {
|
|
46148
|
+
if (!allowNone) throw usage("--blocked-by none only clears blockers on task update");
|
|
46149
|
+
return [];
|
|
46150
|
+
}
|
|
46151
|
+
return values.map((v) => v.trim().replace(/^issue:/, ""));
|
|
46152
|
+
}
|
|
45927
46153
|
async function bodyFrom(ctx, o, required2 = true) {
|
|
45928
46154
|
if (o.body !== void 0 && o.file !== void 0) throw usage("pass either --body or --file, not both");
|
|
45929
46155
|
if (o.file !== void 0) return readText(fileSpec(o.file), ctx.input);
|
|
@@ -45933,23 +46159,24 @@ async function bodyFrom(ctx, o, required2 = true) {
|
|
|
45933
46159
|
}
|
|
45934
46160
|
function registerTask(program2, rt) {
|
|
45935
46161
|
const task = program2.command("task").description("Tasks (issues) in --repo");
|
|
45936
|
-
task.command("create").description("Create a task").requiredOption("--title <title>", "title").option("--body <text>", "description (Markdown; @file or - for stdin)").option("--file <path>", "read the description from a file or - for stdin").option("--criterion <text>", "acceptance criterion (repeatable)", repeat).option("--label <label>", "label (repeatable)", repeat).action(
|
|
46162
|
+
task.command("create").description("Create a task").requiredOption("--title <title>", "title").option("--body <text>", "description (Markdown; @file or - for stdin)").option("--file <path>", "read the description from a file or - for stdin").option("--criterion <text>", "acceptance criterion (repeatable)", repeat).option("--label <label>", "label (repeatable)", repeat).option("--blocked-by <task>", `${BLOCKER_ARG} (repeatable)`, repeat).action(
|
|
45937
46163
|
act(rt, async (ctx, o) => {
|
|
45938
46164
|
const description = await bodyFrom(ctx, o, false);
|
|
45939
|
-
const t = await ctx.client().tasks.create(await ctx.repoId(), { title: o.title, description, acceptance_criteria: o.criterion, labels: o.label });
|
|
46165
|
+
const t = await ctx.client().tasks.create(await ctx.repoId(), { title: o.title, description, acceptance_criteria: o.criterion, labels: o.label, blocked_by: blockersFrom(o.blockedBy, false) });
|
|
45940
46166
|
ctx.result(t, renderTask);
|
|
45941
46167
|
})
|
|
45942
46168
|
);
|
|
45943
|
-
task.command("list").description("List tasks").option("--state <state>", "open | in_progress | blocked | done | cancelled").option("--limit <n>", "page size", (v) => parseIntOption(v, "--limit")).option("--cursor <cursor>", "page cursor").action(
|
|
46169
|
+
task.command("list").description("List tasks").option("--state <state>", "open | in_progress | blocked | done | cancelled").option("--ready", "only tasks ready to start: open, with every blocker done or cancelled").option("--limit <n>", "page size", (v) => parseIntOption(v, "--limit")).option("--cursor <cursor>", "page cursor").action(
|
|
45944
46170
|
act(rt, async (ctx, o) => {
|
|
45945
|
-
const page = await ctx.client().tasks.list(await ctx.repoId(), { state: o.state, limit: o.limit, cursor: o.cursor });
|
|
45946
|
-
ctx.result(page, (v) => listWithCursor(v, ["number", "state", "title", "id"]));
|
|
46171
|
+
const page = await ctx.client().tasks.list(await ctx.repoId(), { state: o.state, ...o.ready ? { ready: true } : {}, limit: o.limit, cursor: o.cursor });
|
|
46172
|
+
ctx.result(page, (v) => listWithCursor({ ...v, items: v.items.map((t) => ({ ...t, waiting_on: waitingOn(t) })) }, ["number", "state", "title", "waiting_on", "id"]));
|
|
45947
46173
|
})
|
|
45948
46174
|
);
|
|
45949
|
-
task.command("view
|
|
45950
|
-
task.command("update
|
|
45951
|
-
act(rt, async (ctx, o, [
|
|
46175
|
+
task.command("view").argument("<task>", TASK_ARG).description("Show a task, with what it is blocked by and what it blocks").action(act(rt, async (ctx, _o, [spec]) => ctx.result(await ctx.client().tasks.get(await ctx.taskId(spec)), renderTask)));
|
|
46176
|
+
task.command("update").argument("<task>", TASK_ARG).description("Update fields; uses the current version unless --expected-version pins it (then stale edits fail with exit 5)").option("--title <title>", "title").option("--body <text>", "description").option("--file <path>", "description from a file or -").option("--state <state>", "open | in_progress | blocked | done | cancelled").option("--owner <user-id>", "owner user id ('none' to clear)").option("--label <label>", "replace labels (repeatable)", repeat).option("--blocked-by <task>", `replace the blockers: ${BLOCKER_ARG} (repeatable; 'none' to clear)`, repeat).option("--expected-version <n>", "expected version (optimistic concurrency)", (v) => parseIntOption(v, "--expected-version")).action(
|
|
46177
|
+
act(rt, async (ctx, o, [spec]) => {
|
|
45952
46178
|
const c8 = ctx.client();
|
|
46179
|
+
const id2 = await ctx.taskId(spec);
|
|
45953
46180
|
const version4 = o.expectedVersion ?? (await c8.tasks.get(id2)).version;
|
|
45954
46181
|
const t = await c8.tasks.update(id2, {
|
|
45955
46182
|
title: o.title,
|
|
@@ -45957,17 +46184,35 @@ function registerTask(program2, rt) {
|
|
|
45957
46184
|
state: o.state,
|
|
45958
46185
|
owner_id: o.owner === void 0 ? void 0 : o.owner === "none" ? null : o.owner,
|
|
45959
46186
|
labels: o.label,
|
|
46187
|
+
blocked_by: blockersFrom(o.blockedBy, true),
|
|
45960
46188
|
version: version4
|
|
45961
46189
|
});
|
|
45962
46190
|
ctx.result(t, renderTask);
|
|
45963
46191
|
})
|
|
45964
46192
|
);
|
|
45965
|
-
task.command("
|
|
45966
|
-
|
|
45967
|
-
|
|
45968
|
-
|
|
46193
|
+
task.command("depend").argument("<task>", TASK_ARG).argument("[blocker]", BLOCKER_ARG).description("Mark <task> as blocked by [blocker] (same repository); without it, show what <task> is blocked by and blocks").action(
|
|
46194
|
+
act(rt, async (ctx, _o, [spec, blocker]) => {
|
|
46195
|
+
const c8 = ctx.client();
|
|
46196
|
+
const id2 = await ctx.taskId(spec);
|
|
46197
|
+
const deps = blocker ? await c8.tasks.dependencies.add(id2, { blocker: blockersFrom([blocker], false)[0] }) : await c8.tasks.dependencies.list(id2);
|
|
46198
|
+
ctx.result(deps, renderDependencies);
|
|
46199
|
+
})
|
|
46200
|
+
);
|
|
46201
|
+
task.command("undepend").argument("<task>", TASK_ARG).argument("<blocker>", BLOCKER_ARG).description("Remove <blocker> from what <task> is blocked by").action(
|
|
46202
|
+
act(rt, async (ctx, _o, [spec, blocker]) => {
|
|
46203
|
+
const id2 = await ctx.taskId(spec);
|
|
46204
|
+
const ref = blockersFrom([blocker], false)[0];
|
|
46205
|
+
await ctx.client().tasks.dependencies.remove(id2, ref);
|
|
46206
|
+
ctx.result({ task_id: id2, removed: ref }, `${spec} is no longer blocked by ${ref}`);
|
|
46207
|
+
})
|
|
46208
|
+
);
|
|
46209
|
+
task.command("claim").argument("<task>", TASK_ARG).description("Advisory claim (exit 5 if someone else holds it)").option("--session <id>", "claim on behalf of this agent session").action(act(rt, async (ctx, o, [spec]) => ctx.result(await ctx.client().tasks.claim(await ctx.taskId(spec), { session_id: o.session }), renderTask)));
|
|
46210
|
+
task.command("release").argument("<task>", TASK_ARG).description("Release your claim").option("--session <id>", "agent session that holds the claim").action(act(rt, async (ctx, o, [spec]) => ctx.result(await ctx.client().tasks.release(await ctx.taskId(spec), { session_id: o.session }), renderTask)));
|
|
46211
|
+
task.command("comment").argument("<task>", TASK_ARG).description("Add a comment").option("--body <text>", "comment (Markdown; @file or - for stdin)").option("--file <path>", "read the comment from a file or -").action(
|
|
46212
|
+
act(rt, async (ctx, o, [spec]) => {
|
|
46213
|
+
const id2 = await ctx.taskId(spec);
|
|
45969
46214
|
const comment = await ctx.client().tasks.comments.create(id2, { body: await bodyFrom(ctx, o) });
|
|
45970
|
-
ctx.result(comment, `Comment ${comment.id} added to ${
|
|
46215
|
+
ctx.result(comment, `Comment ${comment.id} added to ${spec}`);
|
|
45971
46216
|
})
|
|
45972
46217
|
);
|
|
45973
46218
|
}
|
|
@@ -46249,7 +46494,7 @@ var how = (strategy) => strategy ? `with ${strategy}` : "with the policy's defau
|
|
|
46249
46494
|
function renderAutoMerge(a, change, p, now) {
|
|
46250
46495
|
const ref = change ? String(change.number) : a.change_id;
|
|
46251
46496
|
const name = change ? `${p.bold(`#${change.number}`)} ${change.title}` : p.bold(a.change_id);
|
|
46252
|
-
const who3 = a.enabled_by ? a.enabled_by
|
|
46497
|
+
const who3 = a.enabled_by ? actorLabel(a.enabled_by) : null;
|
|
46253
46498
|
const out = [];
|
|
46254
46499
|
switch (a.state) {
|
|
46255
46500
|
case "enabled":
|
|
@@ -46269,7 +46514,7 @@ function renderAutoMerge(a, change, p, now) {
|
|
|
46269
46514
|
out.push(p.dim(` Turn it on: ${BRAND.cli} change auto-merge ${ref}`));
|
|
46270
46515
|
}
|
|
46271
46516
|
if (a.history.length) {
|
|
46272
|
-
const rows2 = a.history.map((h) => ({ when: ago2(h.at, now), action: h.action, actor: h.actor
|
|
46517
|
+
const rows2 = a.history.map((h) => ({ when: ago2(h.at, now), action: h.action, actor: actorLabel(h.actor), reason: h.reason }));
|
|
46273
46518
|
const w = { when: Math.max(...rows2.map((r) => r.when.length)), action: Math.max(...rows2.map((r) => r.action.length)) };
|
|
46274
46519
|
out.push("", p.bold("History"));
|
|
46275
46520
|
for (const r of rows2) out.push(` ${p.dim(r.when.padEnd(w.when))} ${r.action.padEnd(w.action)} ${r.actor}${r.reason ? p.dim(` \xB7 ${r.reason}`) : ""}`);
|
|
@@ -46335,11 +46580,11 @@ function stepOf(state2, previous) {
|
|
|
46335
46580
|
|
|
46336
46581
|
// packages/cli/src/commands/change.ts
|
|
46337
46582
|
var renderChange = (c8) => kv(
|
|
46338
|
-
{ ...c8, source: c8.source.workspace_id ?? c8.source.ref, author:
|
|
46583
|
+
{ ...c8, source: c8.source.workspace_id ?? c8.source.ref, author: actorLabel(c8.author), revision: `#${c8.current_revision.number} ${c8.current_revision.id}` },
|
|
46339
46584
|
["number", "id", "title", "state", "target_ref", "source", "task_id", "author", "revision", "current_revision.head_oid", "revision_count", "merged", "version"]
|
|
46340
46585
|
);
|
|
46341
46586
|
var renderThread = (t) => `${t.id} [${t.state}${t.blocking ? ", blocking" : ""}${t.anchor.outdated ? ", outdated" : ""}] ${t.anchor.path ?? "(change)"}${t.anchor.start_line ? `:${t.anchor.start_line}` : ""}
|
|
46342
|
-
` + t.comments.map((c8) => ` ${c8.author
|
|
46587
|
+
` + t.comments.map((c8) => ` ${actorLabel(c8.author)}: ${c8.body.split("\n").join("\n ")}`).join("\n");
|
|
46343
46588
|
function registerChange(program2, rt) {
|
|
46344
46589
|
const change = program2.command("change").description("Changes (proposed updates to a protected ref)");
|
|
46345
46590
|
change.command("create").description("Create a change from a workspace or a ref in --repo").requiredOption("--title <title>", "title").option("--body <text>", "description (Markdown; @file or - for stdin)").option("--file <path>", "read the description from a file or -").option("--workspace <id>", "source workspace").option("--ref <ref>", "source ref (e.g. refs/heads/feature)").option("--target <ref>", "target ref (default: the repository's default branch)").option("--task <id>", "linked task").option("--draft", "create as draft").action(
|
|
@@ -46359,7 +46604,7 @@ function registerChange(program2, rt) {
|
|
|
46359
46604
|
change.command("list").description("List changes in --repo").option("--state <state>", "draft | open | merged | closed").option("--limit <n>", "page size", (v) => parseIntOption(v, "--limit")).option("--cursor <cursor>", "page cursor").action(
|
|
46360
46605
|
act(rt, async (ctx, o) => {
|
|
46361
46606
|
const page = await ctx.client().changes.list(await ctx.repoId(), { state: o.state, limit: o.limit, cursor: o.cursor });
|
|
46362
|
-
ctx.result(page, (v) => listWithCursor(v, ["number", "state", "title", "target_ref", "id"]));
|
|
46607
|
+
ctx.result(page, (v) => listWithCursor({ ...v, items: v.items.map((c8) => ({ ...c8, author: actorLabel(c8.author) })) }, ["number", "state", "title", "author", "target_ref", "id"]));
|
|
46363
46608
|
})
|
|
46364
46609
|
);
|
|
46365
46610
|
change.command("view").argument("<change>", CHANGE_ARG).description("Show a change").action(act(rt, async (ctx, _o, [id2]) => ctx.result(await ctx.client().changes.get(await ctx.changeId(id2)), renderChange)));
|
|
@@ -46448,7 +46693,7 @@ function registerReview(program2, rt) {
|
|
|
46448
46693
|
const id2 = await ctx.changeId(spec);
|
|
46449
46694
|
const revision = o.revision ?? (await c8.changes.get(id2)).current_revision.id;
|
|
46450
46695
|
const r = await c8.reviews.create(id2, { revision_id: revision, decision, body: await bodyFrom(ctx, o, bodyRequired) });
|
|
46451
|
-
ctx.result(r, (v) => kv({ ...v, reviewer:
|
|
46696
|
+
ctx.result(r, (v) => kv({ ...v, reviewer: actorLabel(v.reviewer) }, ["id", "decision", "revision_id", "reviewer", "current"]));
|
|
46452
46697
|
})
|
|
46453
46698
|
);
|
|
46454
46699
|
decide("approve", "approved", "Approve a revision (approvals are bound to that revision)", false);
|
|
@@ -46569,7 +46814,7 @@ Wait for the findings: code-review wait ${spec}`);
|
|
|
46569
46814
|
|
|
46570
46815
|
// packages/cli/src/commands/evidence.ts
|
|
46571
46816
|
import { basename as basename4, extname, isAbsolute as isAbsolute8, resolve as resolve8 } from "node:path";
|
|
46572
|
-
var renderEvidence = (e) => kv({ ...e, issuer:
|
|
46817
|
+
var renderEvidence = (e) => kv({ ...e, issuer: actorLabel(e.issuer) }, ["id", "check_name", "result", "subject_kind", "subject_oid", "origin", "independence", "issuer", "summary", "attachment_ids", "revoked_at"]);
|
|
46573
46818
|
var CONTENT_TYPES = { ".txt": "text/plain", ".log": "text/plain", ".json": "application/json", ".xml": "application/xml", ".html": "text/plain" };
|
|
46574
46819
|
function registerEvidence(program2, rt) {
|
|
46575
46820
|
const ev = program2.command("evidence").description("Verification evidence");
|
|
@@ -46746,7 +46991,7 @@ var renderCoverage = (c8) => [
|
|
|
46746
46991
|
`code owner review ${c8.required ? "required" : "not required"} by policy v${c8.policy_version} (revision ${c8.revision_id})${c8.complete ? "" : " \u2014 path list truncated"}`,
|
|
46747
46992
|
table(c8.paths.map((p) => ({ ...p, owners: p.owners.join(" ") || "-", approved_by: p.approved_by.join(" ") || "-" })), ["path", "owners", "satisfied", "approved_by"])
|
|
46748
46993
|
].join("\n");
|
|
46749
|
-
var renderAlerts = (p) => listWithCursor({ ...p, items: p.items.map((a) => ({ ...a, where: `${a.path}:${a.line}`, pushed_by: a.pushed_by
|
|
46994
|
+
var renderAlerts = (p) => listWithCursor({ ...p, items: p.items.map((a) => ({ ...a, where: `${a.path}:${a.line}`, pushed_by: actorLabel(a.pushed_by) })) }, ["id", "state", "rule", "where", "blob_oid", "masked", "pushed_by", "created_at"]);
|
|
46750
46995
|
function registerGovernance(program2, rt) {
|
|
46751
46996
|
const change = program2.commands.find((c8) => c8.name() === "change") ?? program2.command("change");
|
|
46752
46997
|
change.command("draft").argument("<change>", CHANGE_ARG).description("Convert an open change back to a draft (drafts cannot merge and notify nobody)").action(act(rt, async (ctx, _o, [id2]) => ctx.result(await ctx.client().changes.draft(await ctx.changeId(id2)), (c8) => kv(c8, ["id", "number", "title", "state"]))));
|
|
@@ -46836,7 +47081,7 @@ new revision #${r.revision.number} ${r.revision.id}`);
|
|
|
46836
47081
|
}
|
|
46837
47082
|
const b = await ctx.client("user").pushProtection.bypasses.create(await ctx.repoId(), { rule: o.rule, path: o.path, blob_oid: o.blob, reason: o.reason, ...principal ? { principal } : {}, ...o.ref ? { ref: o.ref } : {} });
|
|
46838
47083
|
ctx.result(b, (v) => {
|
|
46839
|
-
const who3 = v.principal ? v.principal
|
|
47084
|
+
const who3 = v.principal ? actorLabel(v.principal) : "nobody";
|
|
46840
47085
|
return `${kv({ ...v, for: who3, ref: v.ref ?? "any" }, ["id", "rule", "path", "blob_oid", "for", "ref", "expires_at"])}
|
|
46841
47086
|
Only ${who3} can use it, for one push, before it expires.`;
|
|
46842
47087
|
});
|
|
@@ -46847,7 +47092,7 @@ Only ${who3} can use it, for one push, before it expires.`;
|
|
|
46847
47092
|
rt,
|
|
46848
47093
|
async (ctx, o) => ctx.result(
|
|
46849
47094
|
await ctx.client().pushProtection.bypasses.list(await ctx.repoId(), { limit: o.limit, cursor: o.cursor }),
|
|
46850
|
-
(p) => listWithCursor({ ...p, items: p.items.map((b) => ({ ...b, for: b.principal ? b.principal
|
|
47095
|
+
(p) => listWithCursor({ ...p, items: p.items.map((b) => ({ ...b, for: b.principal ? actorLabel(b.principal) : "-", created_by: actorLabel(b.created_by), used: b.used_at ? "yes" : "no" })) }, ["id", "rule", "path", "blob_oid", "for", "created_by", "used", "expires_at", "reason"])
|
|
46851
47096
|
)
|
|
46852
47097
|
)
|
|
46853
47098
|
);
|
|
@@ -46888,7 +47133,7 @@ var renderAlerts2 = (p) => listWithCursor(
|
|
|
46888
47133
|
["id", "state", "resolution", "confidence", "rule", "repo", "where", "masked", "source", "created_at"]
|
|
46889
47134
|
);
|
|
46890
47135
|
var renderAlert = (a) => [
|
|
46891
|
-
kv({ ...a, where: where(a), resolved_by: a.resolved_by ? a.resolved_by
|
|
47136
|
+
kv({ ...a, where: where(a), resolved_by: a.resolved_by ? actorLabel(a.resolved_by) : null, looks_fake: a.looks_fake?.label ?? null }, [
|
|
46892
47137
|
"id",
|
|
46893
47138
|
"state",
|
|
46894
47139
|
"resolution",
|
|
@@ -47229,8 +47474,8 @@ var renderDevices = (v) => v.items.length ? table(
|
|
|
47229
47474
|
v.items.map((d) => ({ id: d.id, name: d.name ?? "-", environment: d.environment, app_version: d.app_version ?? "-", last_seen: d.last_seen_at, current: d.current ? "yes" : "" })),
|
|
47230
47475
|
["id", "name", "environment", "app_version", "last_seen", "current"]
|
|
47231
47476
|
) : "No push devices. The iOS app registers itself when you allow notifications.";
|
|
47232
|
-
var
|
|
47233
|
-
var
|
|
47477
|
+
var PAGE = 100;
|
|
47478
|
+
var MAX_PAGES = 10;
|
|
47234
47479
|
async function findItems(client, specs) {
|
|
47235
47480
|
const wanted = specs.map((s) => s.trim().toLowerCase());
|
|
47236
47481
|
for (const s of wanted) if (s.length < 4) throw usage(`"${s}" is too short to name an inbox item: use the id \`${c4} inbox list\` shows`);
|
|
@@ -47238,8 +47483,8 @@ async function findItems(client, specs) {
|
|
|
47238
47483
|
const seen = /* @__PURE__ */ new Set();
|
|
47239
47484
|
for (const view of ["all", "snoozed", "done"]) {
|
|
47240
47485
|
let cursor2;
|
|
47241
|
-
for (let i = 0; i <
|
|
47242
|
-
const page = await client.inbox.list({ view, limit:
|
|
47486
|
+
for (let i = 0; i < MAX_PAGES; i++) {
|
|
47487
|
+
const page = await client.inbox.list({ view, limit: PAGE, ...cursor2 ? { cursor: cursor2 } : {} });
|
|
47243
47488
|
for (const item of page.items) {
|
|
47244
47489
|
if (seen.has(item.id)) continue;
|
|
47245
47490
|
seen.add(item.id);
|
|
@@ -47694,13 +47939,13 @@ function renderEntry(e, verb) {
|
|
|
47694
47939
|
if (e.flags.length) lines2.push(`flagged: ${e.flags.join(", ")}${e.flags.includes("instructions") ? "; never given to the assistant on its own" : ""}`);
|
|
47695
47940
|
return lines2.join("\n");
|
|
47696
47941
|
}
|
|
47697
|
-
var
|
|
47698
|
-
var
|
|
47942
|
+
var PAGE2 = 100;
|
|
47943
|
+
var MAX_PAGES2 = 10;
|
|
47699
47944
|
async function reviewable(ctx, org, q) {
|
|
47700
47945
|
const out = [];
|
|
47701
47946
|
let cursor2;
|
|
47702
|
-
for (let i = 0; i <
|
|
47703
|
-
const page = await ctx.client("user").assistant.memory.list(org, { ...q, status: "pending", limit:
|
|
47947
|
+
for (let i = 0; i < MAX_PAGES2; i++) {
|
|
47948
|
+
const page = await ctx.client("user").assistant.memory.list(org, { ...q, status: "pending", limit: PAGE2, ...cursor2 ? { cursor: cursor2 } : {} });
|
|
47704
47949
|
out.push(...page.items.filter((e) => e.can_review));
|
|
47705
47950
|
if (!page.next_cursor) break;
|
|
47706
47951
|
cursor2 = page.next_cursor;
|
|
@@ -48008,6 +48253,38 @@ function registerIntegrations(program2, rt) {
|
|
|
48008
48253
|
integrations.command("inherit <provider> <purpose>").description("Drop --repo's choice and inherit the nearest shared connection again").action(act(rt, async (ctx, _o, [provider, purpose]) => ctx.result(await ctx.client("user").grants.inherit(await ctx.repoId(void 0, "user"), provider, purpose), renderRepo)));
|
|
48009
48254
|
}
|
|
48010
48255
|
|
|
48256
|
+
// packages/cli/src/commands/approvals.ts
|
|
48257
|
+
function protectOptions(cmd) {
|
|
48258
|
+
return cmd.option("--user <handle>", "a person who may approve (@handle or id; repeatable)", repeat).option("--team <slug>", "a team whose members may approve (slug or id; repeatable)", repeat).option("--required <n>", "approvals needed from different people (default 1)", (v) => parseIntOption(v, "--required")).option("--separation-of-duties", "the change's author, its merger and whoever requested the deploy or run cannot approve it");
|
|
48259
|
+
}
|
|
48260
|
+
function protectBody(o, current) {
|
|
48261
|
+
const users = o.user ?? [];
|
|
48262
|
+
const teams = o.team ?? [];
|
|
48263
|
+
if (!users.length && !teams.length && o.required === void 0 && !o.separationOfDuties) throw usage("give --user, --team, --required or --separation-of-duties");
|
|
48264
|
+
return { required: o.required ?? 1, users, teams, separation_of_duties: !!o.separationOfDuties, ...current ? { version: current.version } : {} };
|
|
48265
|
+
}
|
|
48266
|
+
var who2 = (p) => p.handle ? `@${p.handle}` : p.name;
|
|
48267
|
+
var ruleSummary = (r) => r ? `${r.required} approval${r.required === 1 ? "" : "s"} from ${[...r.teams.map((t) => `@${t.slug} team`), ...r.users.map(who2)].join(", ") || "anyone who may approve"}${r.separation_of_duties ? "; not the author, merger or requester" : ""}` : "not protected";
|
|
48268
|
+
var renderApprovers = (v) => kv(
|
|
48269
|
+
{
|
|
48270
|
+
protected: ruleSummary(v.rule),
|
|
48271
|
+
capability: v.capability,
|
|
48272
|
+
eligible: v.eligible.map(who2).join(", ") || "nobody",
|
|
48273
|
+
lacking_capability: v.lacking_capability.length ? `${v.lacking_capability.map(who2).join(", ")} (named, but cannot approve without ${v.capability})` : null,
|
|
48274
|
+
unsatisfiable: v.unsatisfiable ? "yes: fewer people can approve than it needs" : null
|
|
48275
|
+
},
|
|
48276
|
+
["protected", "capability", "eligible", "lacking_capability", "unsatisfiable"]
|
|
48277
|
+
);
|
|
48278
|
+
var renderRule = (r) => `protected: ${ruleSummary(r)} (version ${r.version})`;
|
|
48279
|
+
function approvalLines(a) {
|
|
48280
|
+
if (!a) return "";
|
|
48281
|
+
const by2 = a.approvals.filter((x) => x.counted).map((x) => who2(x.user));
|
|
48282
|
+
const lines2 = [a.line ?? (a.satisfied ? `approved (${a.counted} of ${a.required})` : null), by2.length ? `approved by ${by2.join(", ")}` : null, a.you && !a.you.can_approve && a.line ? `you cannot approve: ${a.you.message}` : null].filter(Boolean);
|
|
48283
|
+
return lines2.length ? `
|
|
48284
|
+
|
|
48285
|
+
${lines2.join("\n")}` : "";
|
|
48286
|
+
}
|
|
48287
|
+
|
|
48011
48288
|
// packages/cli/src/commands/db.ts
|
|
48012
48289
|
async function sourceId(ctx, ref, repo) {
|
|
48013
48290
|
if (ref.startsWith("dms_")) return ref;
|
|
@@ -48018,18 +48295,58 @@ async function sourceId(ctx, ref, repo) {
|
|
|
48018
48295
|
}
|
|
48019
48296
|
var plural3 = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
|
|
48020
48297
|
var short = (oid) => oid.slice(0, 12);
|
|
48021
|
-
var database = (s) =>
|
|
48298
|
+
var database = (s) => {
|
|
48299
|
+
const t = s.target;
|
|
48300
|
+
return t.provider === "cloudflare_d1" ? `d1:${t.binding}\u2192${t.database_name ?? t.database_id}${t.environment ? ` (env ${t.environment})` : ""}` : `${t.organization}/${t.database}@${t.branch}`;
|
|
48301
|
+
};
|
|
48302
|
+
var PS_FORMATS = ["atlas", "plain", "golang_migrate", "drizzle", "prisma"];
|
|
48303
|
+
var PS_ENGINES = ["postgres", "vitess"];
|
|
48304
|
+
var LEVELS = ["error", "warning", "off"];
|
|
48305
|
+
function lintLines(findings2) {
|
|
48306
|
+
if (!findings2?.length) return [];
|
|
48307
|
+
return ["lint:", ...findings2.map((f) => ` ${f.level.padEnd(12)} ${(f.atlas_code ?? f.code).padEnd(6)} ${f.file}:${f.line} ${f.message}`)];
|
|
48308
|
+
}
|
|
48309
|
+
function diffLines(d) {
|
|
48310
|
+
if (!d) return [];
|
|
48311
|
+
const t = d.tables;
|
|
48312
|
+
const out = [];
|
|
48313
|
+
for (const x of t.added) out.push(` + table ${x.name} (${x.columns.map((c8) => c8.name).join(", ")})`);
|
|
48314
|
+
for (const x of t.renamed) out.push(` ~ table ${x.from} \u2192 ${x.to}`);
|
|
48315
|
+
for (const x of t.changed) {
|
|
48316
|
+
for (const c8 of x.columns.added) out.push(` + ${x.name}.${c8.name} ${c8.type}${c8.nullable ? "" : " NOT NULL"}`);
|
|
48317
|
+
for (const c8 of x.columns.renamed) out.push(` ~ ${x.name}.${c8.from} \u2192 ${c8.to}`);
|
|
48318
|
+
for (const c8 of x.columns.changed) out.push(` ~ ${x.name}.${c8.name} ${c8.before.type} \u2192 ${c8.after.type}`);
|
|
48319
|
+
for (const c8 of x.columns.dropped) out.push(` - ${x.name}.${c8}`);
|
|
48320
|
+
for (const i of x.indexes.added) out.push(` + index ${i} on ${x.name}`);
|
|
48321
|
+
for (const i of x.indexes.dropped) out.push(` - index ${i} on ${x.name}`);
|
|
48322
|
+
for (const k of x.foreign_keys.added) out.push(` + foreign key ${k} on ${x.name}`);
|
|
48323
|
+
for (const k of x.foreign_keys.dropped) out.push(` - foreign key ${k} on ${x.name}`);
|
|
48324
|
+
}
|
|
48325
|
+
for (const x of t.dropped) out.push(` - table ${x.name}`);
|
|
48326
|
+
if (!out.length && !d.unparsed.length) return ["schema effect: none"];
|
|
48327
|
+
return ["schema effect:", ...out, ...d.unparsed.length ? [` (${plural3(d.unparsed.length, "statement")} could not be interpreted, first at ${d.unparsed[0].file}:${d.unparsed[0].line})`] : []];
|
|
48328
|
+
}
|
|
48329
|
+
function renderDrift(d) {
|
|
48330
|
+
if (!d) return "drift: not checked yet";
|
|
48331
|
+
const lines2 = [`drift: ${d.status} (checked ${d.checked_at} against ${plural3(d.applied, "applied migration")})`];
|
|
48332
|
+
if (d.note) lines2.push(`note: ${d.note}`);
|
|
48333
|
+
for (const f of d.findings) lines2.push(` ${f.kind.padEnd(20)} ${f.object} ${f.detail}`);
|
|
48334
|
+
return lines2.join("\n");
|
|
48335
|
+
}
|
|
48022
48336
|
function issueLines(issues) {
|
|
48023
48337
|
if (!issues.length) return [];
|
|
48024
48338
|
return ["issues:", ...issues.map((i) => ` ${i.level.padEnd(7)} ${i.code} ${i.file ? `${i.file}: ` : ""}${i.message}`)];
|
|
48025
48339
|
}
|
|
48026
48340
|
var renderSources = (v) => table(
|
|
48027
|
-
v.items.map((s) => ({ ...s, database: database(s), applied: s.snapshot ? `${s.snapshot.applied.length} (read ${s.snapshot.read_at})` : "not read yet" })),
|
|
48028
|
-
["name", "id", "
|
|
48341
|
+
v.items.map((s) => ({ ...s, database: database(s), applied: s.snapshot ? `${s.snapshot.applied.length} (read ${s.snapshot.read_at})` : "not read yet", drift: s.drift?.status ?? "-" })),
|
|
48342
|
+
["name", "id", "provider", "path", "format", "database", "branch", "apply_on", "state", "applied", "drift"]
|
|
48029
48343
|
);
|
|
48030
|
-
var renderSource = (s) => `${kv(
|
|
48344
|
+
var renderSource = (s) => `${kv(
|
|
48345
|
+
{ ...s, approvers: ruleSummary(s.approval_rule), database: database(s), path: s.path || "(repository root)", destructive: s.lint.levels.destructive ?? "error", nolint: s.lint.nolint, ...s.target.provider === "cloudflare_d1" ? { from: `${s.target.config_path} on ${s.branch}` } : {} },
|
|
48346
|
+
s.provider === "cloudflare_d1" ? ["id", "name", "provider", "path", "format", "database", "from", "branch", "revisions_location", "apply_on", "require_approval", "approvers", "change_checks", "destructive", "nolint", "drift_mode", "state", "version"] : ["id", "name", "provider", "path", "format", "engine", "database", "branch", "revisions_location", "baseline_version", "exec_order", "credential_mode", "dsn_set", "run_as_role", "apply_on", "require_approval", "approvers", "change_checks", "destructive", "nolint", "drift_mode", "state", "version"]
|
|
48347
|
+
)}
|
|
48031
48348
|
|
|
48032
|
-
` + (s.
|
|
48349
|
+
` + (s.apply_on === "merge" ? `Merges to ${s.branch} that add migrations create a run waiting for approval: ${BRAND.cli} db runs` : s.apply_on === "deploy" ? `The production deploy's migrations step applies them${s.require_approval ? ", once the run is approved" : ""}` : `Runs start only by hand: ${BRAND.cli} db apply ${s.name}`);
|
|
48033
48350
|
var renderSuggestions = (v) => v.items.length ? table(
|
|
48034
48351
|
v.items.map((s) => ({ ...s, path: s.path || "(root)", configured: s.configured ? "yes" : "no" })),
|
|
48035
48352
|
["path", "format", "files", "revisions_schema", "baseline_version", "configured", "evidence"]
|
|
@@ -48048,16 +48365,17 @@ function renderPlan2(p) {
|
|
|
48048
48365
|
lines2.push(` ${f.name} ${plural3(f.statements, "statement")}${notes.length ? ` (${notes.join(", ")})` : ""}`);
|
|
48049
48366
|
}
|
|
48050
48367
|
}
|
|
48051
|
-
return [...lines2, ...issueLines(p.issues)].join("\n");
|
|
48368
|
+
return [...lines2, ...issueLines(p.issues), ...lintLines(p.lint), ...diffLines(p.schema_diff)].join("\n");
|
|
48052
48369
|
}
|
|
48053
48370
|
var renderPlans = (v) => v.items.length ? v.items.map(renderPlan2).join("\n\n") : "no migration sources";
|
|
48054
48371
|
var NEXT = {
|
|
48055
|
-
awaiting_approval: (id2) => `Waiting for
|
|
48372
|
+
awaiting_approval: (id2) => `Waiting for approval: ${BRAND.cli} db approve ${id2} (or reject)`,
|
|
48056
48373
|
failed: (id2) => `Fix the migration and merge it, then: ${BRAND.cli} db retry ${id2}`,
|
|
48057
|
-
needs_attention: (id2) => `Look at the
|
|
48374
|
+
needs_attention: (id2) => `Look at the database (PlanetScale, or the D1 bookmark's restore command), then: ${BRAND.cli} db retry ${id2} or cancel`
|
|
48058
48375
|
};
|
|
48059
48376
|
function renderRun(r) {
|
|
48060
|
-
const ps = r.planetscale;
|
|
48377
|
+
const ps = r.backend?.provider === "planetscale" ? r.backend : null;
|
|
48378
|
+
const d1 = r.backend?.provider === "cloudflare_d1" ? r.backend : null;
|
|
48061
48379
|
const head = kv(
|
|
48062
48380
|
{
|
|
48063
48381
|
...r,
|
|
@@ -48065,16 +48383,18 @@ function renderRun(r) {
|
|
|
48065
48383
|
change: r.change_id ?? "-",
|
|
48066
48384
|
requested_by: r.requested_by ? `${r.requested_by.type} ${r.requested_by.id}` : "-",
|
|
48067
48385
|
deploy_request: ps?.deploy_request_url ?? (ps?.deploy_request ? `#${ps.deploy_request}` : null),
|
|
48068
|
-
deployment_state: ps?.deployment_state ?? null
|
|
48386
|
+
deployment_state: ps?.deployment_state ?? null,
|
|
48387
|
+
bookmark: d1?.bookmark ?? null,
|
|
48388
|
+
restore: d1?.restore?.wrangler ?? null
|
|
48069
48389
|
},
|
|
48070
|
-
["id", "source_name", "status", "kind", "engine", "commit", "change", "baseline", "requested_by", "approved_by", "approved_at", "decision_reason", "deploy_request", "deployment_state", "error", "note", "created_at"]
|
|
48390
|
+
["id", "source_name", "status", "kind", "provider", "engine", "commit", "change", "deployment_id", "baseline", "requested_by", "approved_by", "approved_at", "decision_reason", "deploy_request", "deployment_state", "bookmark", "restore", "error", "note", "created_at"]
|
|
48071
48391
|
);
|
|
48072
48392
|
const files = r.files.length ? table(
|
|
48073
48393
|
r.files.map((f) => ({ ...f, statements: f.applied_statements ? `${f.applied_statements}/${f.statements}` : f.statements, error: f.error ? `${f.error}${f.error_statement ? ` (in: ${f.error_statement})` : ""}` : null })),
|
|
48074
48394
|
["name", "status", "statements", "execution_ms", "error"]
|
|
48075
48395
|
) : "no files";
|
|
48076
48396
|
const next = NEXT[r.status]?.(r.id);
|
|
48077
|
-
return [head, "", files, ...r.issues.length ? ["", ...issueLines(r.issues)] : [], ...next ? ["", next] : []].join("\n");
|
|
48397
|
+
return [head, "", files, ...r.issues.length ? ["", ...issueLines(r.issues)] : [], ...next ? ["", next] : []].join("\n") + approvalLines(r.approval);
|
|
48078
48398
|
}
|
|
48079
48399
|
var renderRuns = (v) => listWithCursor(
|
|
48080
48400
|
{ ...v, items: v.items.map((r) => ({ ...r, commit: short(r.commit_oid), files: `${r.files.filter((f) => f.status === "applied").length}/${r.files.length} applied` })) },
|
|
@@ -48095,9 +48415,9 @@ function sessionSettings(pairs) {
|
|
|
48095
48415
|
return out;
|
|
48096
48416
|
}
|
|
48097
48417
|
function registerDb(program2, rt) {
|
|
48098
|
-
const db = program2.command("db").description("Database migrations on PlanetScale for --repo: sources, plans,
|
|
48099
|
-
const sources = db.command("sources").description("Migration sources of --repo: a directory bound to a PlanetScale database branch").action(act(rt, async (ctx) => ctx.result(await ctx.client("user").dbMigrations.sources.list(await ctx.repoId(void 0, "user")), renderSources)));
|
|
48100
|
-
sources.command("add").description("Add a migration source (
|
|
48418
|
+
const db = program2.command("db").description("Database migrations on PlanetScale and Cloudflare D1 for --repo: sources, plans, lint, runs applied after approval, and drift");
|
|
48419
|
+
const sources = db.command("sources").description("Migration sources of --repo: a directory bound to a PlanetScale database branch or a D1 database (D1 sources come from the Wrangler configuration)").action(act(rt, async (ctx) => ctx.result(await ctx.client("user").dbMigrations.sources.list(await ctx.repoId(void 0, "user")), renderSources)));
|
|
48420
|
+
sources.command("add").description("Add a PlanetScale migration source (migrations:manage). The source is checked against PlanetScale; `db suggest` shows the directories found. D1 sources need no adding: they come from the Wrangler configuration").requiredOption("--name <name>", "source name (lowercase letters, digits, - and _)").requiredOption("--path <dir>", 'migration directory relative to the repository root ("" for the root)').requiredOption("--format <format>", PS_FORMATS.join(" | "), oneOf("--format", PS_FORMATS)).requiredOption("--engine <engine>", PS_ENGINES.join(" | "), oneOf("--engine", PS_ENGINES)).requiredOption("--connection <connection>", "PlanetScale connection for database migrations (icn_\u2026)").requiredOption("--organization <org>", "PlanetScale organization").requiredOption("--database <db>", "PlanetScale database").option("--ps-branch <branch>", "PlanetScale branch migrations are applied to", "main").option("--branch <branch>", "repository branch whose merges create runs (default: the default branch)").option("--revisions-schema <schema>", "where the revisions table lives (Postgres schema)").option("--revisions-table <table>", "revisions table name (default: the format's own)").option("--baseline <version>", "with no revision history yet, record this version as applied instead of applying files up to it").option("--exec-order <order>", DB_EXEC_ORDERS.join(" | "), oneOf("--exec-order", DB_EXEC_ORDERS)).option("--on-merge <mode>", "deprecated: --apply-on (approval = merge, manual_only = manual)", oneOf("--on-merge", ["approval", "manual_only"])).option("--apply-on <when>", `${DB_APPLY_ON.join(" | ")} (default merge: runs wait for approval after merge)`, oneOf("--apply-on", DB_APPLY_ON)).option("--no-approval", "--apply-on deploy or manual: apply without a person's approval").option("--destructive <level>", "lint level for destructive changes: error (default, fails the check) | warning | off", oneOf("--destructive", LEVELS)).option("--drift <mode>", DB_DRIFT_MODES.join(" | "), oneOf("--drift", DB_DRIFT_MODES)).option("--credential-mode <mode>", DB_CREDENTIAL_MODES.join(" | "), oneOf("--credential-mode", DB_CREDENTIAL_MODES)).option("--run-as-role <role>", "Postgres: SET ROLE for migration sessions").option("--session-setting <name=value>", "Postgres session setting for migration sessions (repeatable)", repeat).option("--statement-timeout <seconds>", "statement timeout (at most 600)", (v) => parseIntOption(v, "--statement-timeout")).option("--lock-timeout <seconds>", "lock timeout (at most 60)", (v) => parseIntOption(v, "--lock-timeout")).option("--no-change-checks", "do not validate the directory on changes").option("--dsn-file <path|->", "--credential-mode dsn: file holding the connection string, or - for stdin (never a flag value; never printed)").action(
|
|
48101
48421
|
act(rt, async (ctx, o) => {
|
|
48102
48422
|
let dsn;
|
|
48103
48423
|
if (o.dsnFile !== void 0) {
|
|
@@ -48124,6 +48444,10 @@ function registerDb(program2, rt) {
|
|
|
48124
48444
|
...o.baseline ? { baseline_version: o.baseline } : {},
|
|
48125
48445
|
...o.execOrder ? { exec_order: o.execOrder } : {},
|
|
48126
48446
|
...o.onMerge ? { on_merge: o.onMerge } : {},
|
|
48447
|
+
...o.applyOn ? { apply_on: o.applyOn } : {},
|
|
48448
|
+
...o.approval === false ? { require_approval: false } : {},
|
|
48449
|
+
...o.destructive ? { lint: { levels: { destructive: o.destructive }, nolint: "honor", drift_blocks_deploy: false } } : {},
|
|
48450
|
+
...o.drift ? { drift_mode: o.drift } : {},
|
|
48127
48451
|
...o.credentialMode ? { credential_mode: o.credentialMode } : {},
|
|
48128
48452
|
...o.runAsRole ? { run_as_role: o.runAsRole } : {},
|
|
48129
48453
|
...o.sessionSetting ? { session_settings: sessionSettings(o.sessionSetting) } : {},
|
|
@@ -48134,6 +48458,45 @@ function registerDb(program2, rt) {
|
|
|
48134
48458
|
ctx.result(await ctx.client("user").dbMigrations.sources.create(await ctx.repoId(void 0, "user"), body), renderSource);
|
|
48135
48459
|
})
|
|
48136
48460
|
);
|
|
48461
|
+
sources.command("set <source>").description("Change how a source applies, its lint and drift checks (migrations:manage; D1 sources: only these)").option("--apply-on <when>", DB_APPLY_ON.join(" | "), oneOf("--apply-on", DB_APPLY_ON)).option("--approval", "each run waits for approval (migrations:approve; a protected source's approvers)").option("--no-approval", "--apply-on deploy or manual: apply without approval").option("--destructive <level>", "lint level for destructive changes: error | warning | off", oneOf("--destructive", LEVELS)).option("--nolint <mode>", "honor | ignore: whether `-- atlas:nolint` in a file acknowledges a finding", oneOf("--nolint", ["honor", "ignore"])).option("--drift <mode>", DB_DRIFT_MODES.join(" | "), oneOf("--drift", DB_DRIFT_MODES)).option("--drift-blocks-deploy <yes|no>", "drift found by the last check fails the deploy's migrations step", oneOf("--drift-blocks-deploy", ["yes", "no"])).option("--enable", "activate the source").option("--disable", "disable the source (no runs, no checks)").action(
|
|
48462
|
+
act(rt, async (ctx, o, [ref]) => {
|
|
48463
|
+
if (o.enable && o.disable) throw usage("--enable and --disable exclude each other");
|
|
48464
|
+
const c8 = ctx.client("user").dbMigrations;
|
|
48465
|
+
const s = await c8.sources.get(await sourceId(ctx, ref));
|
|
48466
|
+
const levels = { ...s.lint.levels };
|
|
48467
|
+
if (o.destructive) {
|
|
48468
|
+
if (o.destructive === "error") delete levels.destructive;
|
|
48469
|
+
else levels.destructive = o.destructive;
|
|
48470
|
+
}
|
|
48471
|
+
const lintChanged = o.destructive || o.nolint || o.driftBlocksDeploy;
|
|
48472
|
+
const body = {
|
|
48473
|
+
version: s.version,
|
|
48474
|
+
...o.applyOn ? { apply_on: o.applyOn } : {},
|
|
48475
|
+
...o.approval !== void 0 ? { require_approval: o.approval } : {},
|
|
48476
|
+
...lintChanged ? { lint: { levels, nolint: o.nolint ?? s.lint.nolint, drift_blocks_deploy: o.driftBlocksDeploy ? o.driftBlocksDeploy === "yes" : s.lint.drift_blocks_deploy } } : {},
|
|
48477
|
+
...o.drift ? { drift_mode: o.drift } : {},
|
|
48478
|
+
...o.enable ? { state: "active" } : o.disable ? { state: "disabled" } : {}
|
|
48479
|
+
};
|
|
48480
|
+
if (Object.keys(body).length === 1) throw usage("nothing to change: give --apply-on, --approval, --destructive, --nolint, --drift, --drift-blocks-deploy, --enable or --disable");
|
|
48481
|
+
ctx.result(await c8.sources.update(s.id, body), renderSource);
|
|
48482
|
+
})
|
|
48483
|
+
);
|
|
48484
|
+
protectOptions(sources.command("protect <source>").description("Require approvals for every run of the source (people with migrations:manage). A protected database can no longer apply without approval. Replaces the current rule")).action(
|
|
48485
|
+
act(rt, async (ctx, o, [ref]) => {
|
|
48486
|
+
const c8 = ctx.client("user").dbMigrations.sources;
|
|
48487
|
+
const s = await c8.get(await sourceId(ctx, ref));
|
|
48488
|
+
ctx.result(await c8.protect(s.id, protectBody(o, s.approval_rule)), renderRule);
|
|
48489
|
+
})
|
|
48490
|
+
);
|
|
48491
|
+
sources.command("unprotect <source>").description("Remove the source's approval rule (runs still need approval as its apply mode says). Requires --yes when non-interactive").action(
|
|
48492
|
+
act(rt, async (ctx, _o, [ref]) => {
|
|
48493
|
+
const id2 = await sourceId(ctx, ref);
|
|
48494
|
+
await ctx.confirm(`remove the approval rule of ${ref}`);
|
|
48495
|
+
await ctx.client("user").dbMigrations.sources.unprotect(id2);
|
|
48496
|
+
ctx.result({ unprotected: id2 }, `${ref} is no longer protected`);
|
|
48497
|
+
})
|
|
48498
|
+
);
|
|
48499
|
+
sources.command("approvers <source>").description("Who may approve the source's runs now").action(act(rt, async (ctx, _o, [ref]) => ctx.result(await ctx.client("user").dbMigrations.sources.approvers(await sourceId(ctx, ref)), renderApprovers)));
|
|
48137
48500
|
sources.command("rm <source>").description("Delete a migration source (name or id; its runs stay listed). Requires --yes when non-interactive").action(
|
|
48138
48501
|
act(rt, async (ctx, _o, [ref]) => {
|
|
48139
48502
|
const id2 = await sourceId(ctx, ref);
|
|
@@ -48143,7 +48506,7 @@ function registerDb(program2, rt) {
|
|
|
48143
48506
|
})
|
|
48144
48507
|
);
|
|
48145
48508
|
db.command("suggest").description("Migration directories found in --repo (atlas.sum, drizzle journals, *.up.sql, numbered SQL files), and whether a source covers each").option("--ref <ref>", "branch, tag or commit (default: the default branch)").action(act(rt, async (ctx, o) => ctx.result(await ctx.client("user").dbMigrations.suggestions(await ctx.repoId(void 0, "user"), o.ref), renderSuggestions)));
|
|
48146
|
-
db.command("plan [source]").description("What applying a source (every source when omitted) would do: pending files and issues. Exits 7 when a plan has errors").option("--ref <ref>", "branch, tag or commit (default: the source's branch)").option("--live", "read the database now instead of its last snapshot (
|
|
48509
|
+
db.command("plan [source]").description("What applying a source (every source when omitted) would do: pending files and issues. Exits 7 when a plan has errors").option("--ref <ref>", "branch, tag or commit (default: the source's branch)").option("--live", "read the database now instead of its last snapshot (migrations:manage)").action(
|
|
48147
48510
|
act(rt, async (ctx, o, [ref]) => {
|
|
48148
48511
|
const c8 = ctx.client("user").dbMigrations;
|
|
48149
48512
|
const q = { ref: o.ref, live: o.live };
|
|
@@ -48159,7 +48522,29 @@ function registerDb(program2, rt) {
|
|
|
48159
48522
|
if (plans.items.some((p) => !p.ok)) ctx.setExit(EXIT.NOT_READY);
|
|
48160
48523
|
})
|
|
48161
48524
|
);
|
|
48162
|
-
db.command("
|
|
48525
|
+
db.command("diff <change>").description("What a change's migrations do: per source, the files it adds, lint findings and the schema effect. Exits 7 when a source would block the change").action(
|
|
48526
|
+
act(rt, async (ctx, _o, [change]) => {
|
|
48527
|
+
const plans = await ctx.client("user").dbMigrations.changePlans(await ctx.changeId(change, "user"));
|
|
48528
|
+
ctx.result(
|
|
48529
|
+
plans,
|
|
48530
|
+
(v) => v.items.length ? v.items.map((p) => {
|
|
48531
|
+
const mine = p.pending.filter((f) => f.introduced_by_change);
|
|
48532
|
+
return [`${p.source_name} (${p.provider === "cloudflare_d1" ? "Cloudflare D1" : `PlanetScale ${p.engine}`} ${p.target_label}): ${p.ok ? "ok" : "blocked"}`, mine.length ? `this change adds: ${mine.map((f) => f.name).join(", ")}` : "this change adds no migrations", ...issueLines(p.issues), ...lintLines(p.lint), ...diffLines(mine.length ? p.schema_diff : null)].join("\n");
|
|
48533
|
+
}).join("\n\n") : "no migration sources follow the change's target branch"
|
|
48534
|
+
);
|
|
48535
|
+
if (plans.items.some((p) => !p.ok)) ctx.setExit(EXIT.NOT_READY);
|
|
48536
|
+
})
|
|
48537
|
+
);
|
|
48538
|
+
db.command("drift <source>").description("The source's last drift check: its database against what the applied migrations produce. --check reads the database now (migrations:manage)").option("--check", "check now").action(
|
|
48539
|
+
act(rt, async (ctx, o, [ref]) => {
|
|
48540
|
+
const c8 = ctx.client("user").dbMigrations;
|
|
48541
|
+
const id2 = await sourceId(ctx, ref);
|
|
48542
|
+
const d = o.check ? await c8.checkDrift(id2) : (await c8.sources.get(id2)).drift;
|
|
48543
|
+
ctx.result(d, renderDrift);
|
|
48544
|
+
if (d?.status === "drift") ctx.setExit(EXIT.NOT_READY);
|
|
48545
|
+
})
|
|
48546
|
+
);
|
|
48547
|
+
db.command("refresh <source>").description("Read the applied versions from the database now and show the plan at the source's branch (migrations:manage)").action(act(rt, async (ctx, _o, [ref]) => ctx.result(await ctx.client("user").dbMigrations.refresh(await sourceId(ctx, ref)), renderPlan2)));
|
|
48163
48548
|
db.command("runs").description("Migration runs of --repo, newest first").option("--source <source>", "only this source (name or id)").option("--limit <n>", "page size", (v) => parseIntOption(v, "--limit")).option("--cursor <cursor>", "page cursor").action(
|
|
48164
48549
|
act(rt, async (ctx, o) => {
|
|
48165
48550
|
const repo = { id: await ctx.repoId(void 0, "user") };
|
|
@@ -48168,7 +48553,7 @@ function registerDb(program2, rt) {
|
|
|
48168
48553
|
})
|
|
48169
48554
|
);
|
|
48170
48555
|
db.command("run <run>").description("One migration run: status, files and issues").action(act(rt, async (ctx, _o, [id2]) => ctx.result(await ctx.client("user").dbMigrations.runs.get(id2), renderRun)));
|
|
48171
|
-
db.command("apply <source>").description("Apply a source's pending migrations now (
|
|
48556
|
+
db.command("apply <source>").description("Apply a source's pending migrations now (migrations:manage). Your request is the approval when you may approve and the source is not protected; otherwise the run waits for approval. Requires --yes when non-interactive").option("--ref <ref>", "branch, tag or commit to apply (default: the source's branch)").action(
|
|
48172
48557
|
act(rt, async (ctx, o, [ref]) => {
|
|
48173
48558
|
const id2 = await sourceId(ctx, ref);
|
|
48174
48559
|
await ctx.confirm(`apply the pending migrations of ${ref}${o.ref ? ` at ${o.ref}` : ""} to its database`);
|
|
@@ -48179,24 +48564,26 @@ ${renderPlan2(v.plan)}`);
|
|
|
48179
48564
|
})
|
|
48180
48565
|
);
|
|
48181
48566
|
const decisions = [
|
|
48182
|
-
["approve", "Approve a run waiting for approval: Brnch applies exactly the files it shows (
|
|
48183
|
-
["reject", "Reject a run waiting for approval; nothing is applied (
|
|
48184
|
-
["retry", "Retry a failed run from where it stopped (
|
|
48185
|
-
["cancel", "Cancel a run that has not finished (
|
|
48567
|
+
["approve", "Approve a run waiting for approval: Brnch applies exactly the files it shows once it has the approvals it needs (migrations:approve; a protected source's approvers)"],
|
|
48568
|
+
["reject", "Reject a run waiting for approval; nothing is applied (migrations:approve)"],
|
|
48569
|
+
["retry", "Retry a failed run from where it stopped (migrations:approve)"],
|
|
48570
|
+
["cancel", "Cancel a run that has not finished (migrations:approve)"]
|
|
48186
48571
|
];
|
|
48187
48572
|
for (const [decision, description] of decisions) {
|
|
48188
|
-
db.command(`${decision} <run>`).description(description).option("--reason <text>", "why (recorded with the decision)")
|
|
48573
|
+
const cmd = db.command(`${decision} <run>`).description(description).option("--reason <text>", "why (recorded with the decision)");
|
|
48574
|
+
if (decision === "approve") cmd.option("--redeploy", "a run a production deploy waits on: deploy the same commit again, which applies it");
|
|
48575
|
+
cmd.action(act(rt, async (ctx, o, [id2]) => ctx.result(await ctx.client("user").dbMigrations[decision](id2, o.redeploy ? { reason: o.reason, redeploy: true } : o.reason), renderRun)));
|
|
48189
48576
|
}
|
|
48190
48577
|
}
|
|
48191
48578
|
|
|
48192
48579
|
// packages/cli/src/commands/deploy.ts
|
|
48193
48580
|
var TERMINAL4 = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "superseded", "blocked"]);
|
|
48194
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)";
|
|
48195
|
-
var renderTargets = (v) =>
|
|
48196
|
-
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)}` : "-" })) },
|
|
48197
48584
|
["name", "id", "platform", "resource", "env", "branch", "runs_on", "previews", "state", "last_production"]
|
|
48198
48585
|
);
|
|
48199
|
-
var renderTarget = (t) => kv({ ...t, 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}` : "-" }, [
|
|
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}` : "-" }, [
|
|
48200
48587
|
"id",
|
|
48201
48588
|
"name",
|
|
48202
48589
|
"platform",
|
|
@@ -48214,12 +48601,13 @@ var renderTarget = (t) => kv({ ...t, watch_paths: t.watch_paths?.length ? t.watc
|
|
|
48214
48601
|
"watch_paths",
|
|
48215
48602
|
"previews",
|
|
48216
48603
|
"preview_check_name",
|
|
48604
|
+
"approvers",
|
|
48217
48605
|
"last_production",
|
|
48218
48606
|
"version"
|
|
48219
48607
|
]);
|
|
48220
48608
|
var renderDeployment = (d) => kv({ ...d, change: d.change ? `#${d.change.number}` : "-", logs: d.run?.logs_url ?? "-" }, ["id", "target_name", "kind", "environment", "status", "status_reason", "trigger", "commit_oid", "change", "version_id", "url", "alias_url", "verified", "duration_ms", "logs", "created_at"]) + (d.steps.length ? `
|
|
48221
48609
|
|
|
48222
|
-
${table(d.steps, ["name", "phase", "status", "summary"])}` : "");
|
|
48610
|
+
${table(d.steps, ["name", "phase", "status", "summary"])}` : "") + approvalLines(d.approval);
|
|
48223
48611
|
var renderDeployments = (v) => listWithCursor({ ...v, items: v.items.map((d) => ({ ...d, commit: d.commit_oid.slice(0, 12), change: d.change ? `#${d.change.number}` : "-" })) }, ["id", "target_name", "kind", "status", "commit", "change", "trigger", "created_at"]);
|
|
48224
48612
|
async function pick3(ctx, what, flag, options, label2) {
|
|
48225
48613
|
if (options.length === 1) return options[0];
|
|
@@ -48235,10 +48623,12 @@ ${lines2}
|
|
|
48235
48623
|
}
|
|
48236
48624
|
async function targetId(ctx, ref) {
|
|
48237
48625
|
if (ref.startsWith("dpt_")) return ref;
|
|
48238
|
-
const
|
|
48239
|
-
const
|
|
48240
|
-
|
|
48241
|
-
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");
|
|
48242
48632
|
}
|
|
48243
48633
|
var orNull = (v) => v === void 0 ? void 0 : v || null;
|
|
48244
48634
|
async function stepsOf(ctx, spec) {
|
|
@@ -48355,7 +48745,23 @@ function registerDeploy(program2, rt) {
|
|
|
48355
48745
|
Merges to ${v.branch} deploy automatically. Deploy now: brnch deploy run ${v.id}`);
|
|
48356
48746
|
})
|
|
48357
48747
|
);
|
|
48358
|
-
deploy.command("targets").description("Deploy targets of --repo").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)));
|
|
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(
|
|
48750
|
+
act(rt, async (ctx, o, [ref]) => {
|
|
48751
|
+
const c8 = ctx.client("user").deploy.targets;
|
|
48752
|
+
const t = await c8.get(await targetId(ctx, ref));
|
|
48753
|
+
ctx.result(await c8.protect(t.id, protectBody(o, t.approval_rule)), renderRule);
|
|
48754
|
+
})
|
|
48755
|
+
);
|
|
48756
|
+
targets2.command("unprotect <target>").description("Remove the target's approval rule; deploys waiting for it go on. Requires --yes when non-interactive").action(
|
|
48757
|
+
act(rt, async (ctx, _o, [ref]) => {
|
|
48758
|
+
const id2 = await targetId(ctx, ref);
|
|
48759
|
+
await ctx.confirm(`remove the approval rule of ${ref}: its production deploys will no longer wait for approval`);
|
|
48760
|
+
await ctx.client("user").deploy.targets.unprotect(id2);
|
|
48761
|
+
ctx.result({ unprotected: id2 }, `${ref} is no longer protected`);
|
|
48762
|
+
})
|
|
48763
|
+
);
|
|
48764
|
+
targets2.command("approvers <target>").description("Who may approve the target's production deploys now").action(act(rt, async (ctx, _o, [ref]) => ctx.result(await ctx.client("user").deploy.targets.approvers(await targetId(ctx, ref)), renderApprovers)));
|
|
48359
48765
|
deploy.command("target <target>").description("One deploy target (id, or name in --repo)").action(act(rt, async (ctx, _o, [ref]) => ctx.result(await ctx.client("user").deploy.targets.get(await targetId(ctx, ref)), renderTarget)));
|
|
48360
48766
|
targetOptions(deploy.command("update <target>").description("Change a deploy target (only the given flags; an empty value clears a command or the environment)")).option("--disable", "stop deploying (merges no longer deploy)").option("--enable", "deploy again").action(
|
|
48361
48767
|
act(rt, async (ctx, o, [ref]) => {
|
|
@@ -48414,6 +48820,8 @@ Merges to ${v.branch} deploy automatically. Deploy now: brnch deploy run ${v.id}
|
|
|
48414
48820
|
);
|
|
48415
48821
|
deploy.command("show <deployment>").description("One deployment with its steps").action(act(rt, async (ctx, _o, [id2]) => ctx.result(await ctx.client("user").deploy.deployments.get(id2), renderDeployment)));
|
|
48416
48822
|
deploy.command("cancel <deployment>").description("Cancel a queued or running deployment").action(act(rt, async (ctx, _o, [id2]) => ctx.result(await ctx.client("user").deploy.deployments.cancel(id2), renderDeployment)));
|
|
48823
|
+
deploy.command("approve <deployment>").description("Approve a deploy of a protected target (people who may approve; the last approval needed starts it)").action(act(rt, async (ctx, _o, [id2]) => ctx.result(await ctx.client("user").deploy.deployments.approve(id2), renderDeployment)));
|
|
48824
|
+
deploy.command("reject <deployment>").description("Reject a deploy waiting for approval; it is cancelled").option("--reason <text>", "why (recorded with the decision)").action(act(rt, async (ctx, o, [id2]) => ctx.result(await ctx.client("user").deploy.deployments.reject(id2, o.reason), renderDeployment)));
|
|
48417
48825
|
deploy.command("rollback <target>").description("Send all traffic back to the version of an earlier verified production deployment").requiredOption("--to <deployment>", "the deployment to roll back to (dpl_\u2026)").option("--message <text>", "deployment message").action(
|
|
48418
48826
|
act(rt, async (ctx, o, [ref]) => {
|
|
48419
48827
|
const id2 = await targetId(ctx, ref);
|
|
@@ -48426,7 +48834,6 @@ Merges to ${v.branch} deploy automatically. Deploy now: brnch deploy run ${v.id}
|
|
|
48426
48834
|
// packages/cli/src/commands/delegation.ts
|
|
48427
48835
|
var ITERATION_DONE = /* @__PURE__ */ new Set(["succeeded", "no_changes", "failed", "cancelled"]);
|
|
48428
48836
|
var STATES = ["working", "waiting", "stopped"];
|
|
48429
|
-
var who2 = (a) => a.display_name ?? `${a.type} ${a.id}`;
|
|
48430
48837
|
var short2 = (oid) => oid ? oid.slice(0, 12) : null;
|
|
48431
48838
|
function vendorArg(v) {
|
|
48432
48839
|
if (!CODING_AGENT_VENDORS.includes(v)) throw usage(`unknown agent "${v}"; one of ${CODING_AGENT_VENDORS.join(", ")}`);
|
|
@@ -48446,7 +48853,7 @@ function renderIterations(d) {
|
|
|
48446
48853
|
const rows2 = d.iterations.map((i) => ({
|
|
48447
48854
|
...i,
|
|
48448
48855
|
trigger: i.trigger_ref ? `${i.trigger} (${i.trigger_ref})` : i.trigger,
|
|
48449
|
-
by:
|
|
48856
|
+
by: actorLabel(i.trigger_actor),
|
|
48450
48857
|
head: short2(i.head_oid)
|
|
48451
48858
|
}));
|
|
48452
48859
|
const claims = d.iterations.map(reported).filter((x) => !!x);
|
|
@@ -48468,7 +48875,7 @@ var renderDelegation = (d) => kv(
|
|
|
48468
48875
|
...d,
|
|
48469
48876
|
agent: `${d.agent.name} (${d.agent.vendor})`,
|
|
48470
48877
|
status: delegationStatusLabel(d),
|
|
48471
|
-
sponsor:
|
|
48878
|
+
sponsor: actorLabel(d.sponsor),
|
|
48472
48879
|
branch: `${d.source_ref} \u2192 ${d.target_ref}`,
|
|
48473
48880
|
automatic: `on review ${d.auto_on_review ? "on" : "off"}, on check failure ${d.auto_on_check_failure ? "on" : "off"}`,
|
|
48474
48881
|
budget: `iterations ${d.usage.iterations_used}/${d.budget.max_iterations}, tokens ${d.usage.tokens_used}/${d.budget.max_tokens}, ${d.budget.iteration_minutes} min per iteration`
|
|
@@ -49029,15 +49436,78 @@ function authGuidance(cli, helperConfigured) {
|
|
|
49029
49436
|
}
|
|
49030
49437
|
|
|
49031
49438
|
// packages/cli/src/commands/repo.ts
|
|
49032
|
-
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"];
|
|
49033
49440
|
var renderRepo2 = (r) => kv(r, REPO_FIELDS) + (r.import ? `
|
|
49034
49441
|
import ${r.import.status}${r.import.shallow ? " (shallow)" : ""}` : "");
|
|
49035
49442
|
function renderLog(p) {
|
|
49036
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] }));
|
|
49037
49444
|
const lines2 = [table(rows2, ["oid", "date", "author", "subject"])];
|
|
49038
49445
|
const s = p.path_scan;
|
|
49446
|
+
const l = p.log_scan;
|
|
49039
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})`);
|
|
49040
|
-
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(", ")}`);
|
|
49041
49511
|
return lines2.join("\n");
|
|
49042
49512
|
}
|
|
49043
49513
|
function registerOrg(program2, rt) {
|
|
@@ -49048,6 +49518,14 @@ function registerOrg(program2, rt) {
|
|
|
49048
49518
|
ctx.result(created, (v) => kv(v, ["id", "slug", "name", "created_at"]));
|
|
49049
49519
|
})
|
|
49050
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
|
+
);
|
|
49051
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(
|
|
49052
49530
|
act(rt, async (ctx, o) => {
|
|
49053
49531
|
const page = await ctx.client("user").orgs.list({ limit: o.limit, cursor: o.cursor });
|
|
@@ -49150,7 +49628,18 @@ function registerRepo(program2, rt) {
|
|
|
49150
49628
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
49151
49629
|
job2 = await c8.migrations.get(job2.id);
|
|
49152
49630
|
}
|
|
49153
|
-
const
|
|
49631
|
+
const deps = job2.report.dependencies;
|
|
49632
|
+
const view = {
|
|
49633
|
+
id: job2.id,
|
|
49634
|
+
state: job2.state,
|
|
49635
|
+
issues: job2.report.counts.issues,
|
|
49636
|
+
pull_requests: job2.report.counts.pull_requests,
|
|
49637
|
+
comments: job2.report.counts.comments,
|
|
49638
|
+
releases: job2.report.counts.releases,
|
|
49639
|
+
...deps ? { blocked_by_links: `${deps.imported} imported${deps.skipped ? `, ${deps.skipped} skipped` : ""}` } : {},
|
|
49640
|
+
missing: job2.report.missing.join("; ") || "-",
|
|
49641
|
+
warnings: job2.report.warnings.join("; ") || "-"
|
|
49642
|
+
};
|
|
49154
49643
|
ctx.result(job2, () => kv(view, Object.keys(view)));
|
|
49155
49644
|
if (o.wait && job2.state !== "completed") throw new CliError(`migration ${job2.state}: ${job2.error ?? job2.report.missing.join("; ")}`, EXIT.NOT_READY);
|
|
49156
49645
|
})
|
|
@@ -49166,13 +49655,93 @@ function registerRepo(program2, rt) {
|
|
|
49166
49655
|
ctx.result(await ctx.client("agent").repos.get(await ctx.repoId(spec)), renderRepo2);
|
|
49167
49656
|
})
|
|
49168
49657
|
);
|
|
49169
|
-
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(
|
|
49170
49659
|
act(rt, async (ctx, o, [spec]) => {
|
|
49171
|
-
if (o.
|
|
49172
|
-
|
|
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(
|
|
49669
|
+
act(rt, async (ctx, o, [spec]) => {
|
|
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
|
+
});
|
|
49173
49684
|
ctx.result(page, renderLog);
|
|
49174
49685
|
})
|
|
49175
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
|
+
);
|
|
49176
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(
|
|
49177
49746
|
act(rt, async (ctx, o, [spec, dir]) => {
|
|
49178
49747
|
if (o.ssh && o.https) throw usage("--ssh and --https are mutually exclusive");
|
|
@@ -49183,24 +49752,39 @@ function registerRepo(program2, rt) {
|
|
|
49183
49752
|
if (t.transport === "ssh") {
|
|
49184
49753
|
if (!t.ssh.ready) throw usage(`this machine is not set up for SSH${t.ssh.alias ? ` to ${t.ssh.alias}` : ""} yet: run \`${BRAND.cli} ssh setup\`, or clone with --https`);
|
|
49185
49754
|
const url2 = sshCloneUrl(t.ssh.alias, r.org_slug, r.name);
|
|
49186
|
-
const code2 = await ctx.deps.spawnGit(["clone", "--", url2, directory], { env: { ...ctx.deps.env, GIT_TERMINAL_PROMPT: "0" }, cwd: ctx.deps.cwd, stdoutToStderr: true });
|
|
49755
|
+
const code2 = await ctx.deps.spawnGit([...SNAPSHOT_CLONE, "clone", "--", url2, directory], { env: { ...ctx.deps.env, GIT_TERMINAL_PROMPT: "0" }, cwd: ctx.deps.cwd, stdoutToStderr: true });
|
|
49187
49756
|
if (code2 !== 0) throw new CliError(`git clone over SSH exited with status ${code2}; \`${BRAND.cli} ssh test\` explains SSH problems, --https clones without SSH`, EXIT.UNEXPECTED, "GIT_FAILED");
|
|
49188
49757
|
return ctx.result({ repository: r, directory, transport: "ssh", transport_reason: t.reason, clone_url: url2 }, cloned(r, directory, `over SSH (${url2})`, paint(colorEnabled(ctx.deps, "stdout"))));
|
|
49189
49758
|
}
|
|
49190
49759
|
if (t.approval) ctx.info(`Cloning over HTTPS: ${t.reason}. Pass --ssh to use SSH anyway.`);
|
|
49191
49760
|
const token = await ctx.freshToken("agent");
|
|
49192
|
-
const
|
|
49761
|
+
const helper2 = await credentialHelperConfigured(ctx);
|
|
49762
|
+
const list3 = helper2 && await gitAtLeast(ctx, "2.40.0") && await bundlesOffered(ctx, r.clone_url, token) ? [`--bundle-uri=${r.clone_url}/bundles`] : [];
|
|
49763
|
+
const code = await ctx.deps.spawnGit([...SNAPSHOT_CLONE, "clone", ...list3, "--", r.clone_url, directory], {
|
|
49193
49764
|
env: gitAuthEnv(ctx.deps.env, token, r.clone_url),
|
|
49194
49765
|
cwd: ctx.deps.cwd,
|
|
49195
49766
|
stdoutToStderr: true
|
|
49196
49767
|
});
|
|
49197
49768
|
if (code !== 0) throw new CliError(`git clone exited with status ${code}`, EXIT.UNEXPECTED, "GIT_FAILED");
|
|
49198
|
-
const helper2 = await credentialHelperConfigured(ctx);
|
|
49199
49769
|
ctx.info(authGuidance(BRAND.cli, helper2));
|
|
49200
49770
|
ctx.result({ repository: r, directory, transport: "https", transport_reason: t.reason, clone_url: r.clone_url, credential_helper: helper2 }, cloned(r, directory, "over HTTPS", paint(colorEnabled(ctx.deps, "stdout"))));
|
|
49201
49771
|
})
|
|
49202
49772
|
);
|
|
49203
49773
|
}
|
|
49774
|
+
var SNAPSHOT_CLONE = ["-c", "transfer.bundleURI=true"];
|
|
49775
|
+
async function bundlesOffered(ctx, cloneUrl, token) {
|
|
49776
|
+
try {
|
|
49777
|
+
const res = await ctx.deps.sys.fetch(`${cloneUrl}/bundles`, { headers: { authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(5e3) });
|
|
49778
|
+
return res.ok && (await res.text()).includes('[bundle "');
|
|
49779
|
+
} catch {
|
|
49780
|
+
return false;
|
|
49781
|
+
}
|
|
49782
|
+
}
|
|
49783
|
+
async function gitAtLeast(ctx, min) {
|
|
49784
|
+
const r = await ctx.deps.sys.run("git", ["--version"], { timeoutMs: 1e4 }).catch(() => null);
|
|
49785
|
+
const version4 = r?.code === 0 ? /git version (\d+\.\d+\.\d+)/.exec(r.stdout)?.[1] : void 0;
|
|
49786
|
+
return !!version4 && compareVersions(version4, min) >= 0;
|
|
49787
|
+
}
|
|
49204
49788
|
var hasRepo = (ctx) => {
|
|
49205
49789
|
try {
|
|
49206
49790
|
ctx.repoSpec();
|
|
@@ -49268,7 +49852,7 @@ function registerGit(program2, rt) {
|
|
|
49268
49852
|
}
|
|
49269
49853
|
|
|
49270
49854
|
// packages/cli/src/commands/release.ts
|
|
49271
|
-
var renderRelease = (r) => kv({ ...r, author: r.author
|
|
49855
|
+
var renderRelease = (r) => kv({ ...r, author: actorLabel(r.author) }, ["id", "tag", "name", "commit_oid", "draft", "author", "created_at", "notes"]);
|
|
49272
49856
|
function registerRelease(program2, rt) {
|
|
49273
49857
|
const release = program2.command("release").description("Releases of --repo (a tag with notes)");
|
|
49274
49858
|
release.command("list").description("Releases, newest first").action(
|
|
@@ -49711,7 +50295,7 @@ var renderRangeDiff = (r) => {
|
|
|
49711
50295
|
if (r.truncated) lines2.push("(truncated)");
|
|
49712
50296
|
return lines2.join("\n");
|
|
49713
50297
|
};
|
|
49714
|
-
var renderOperation = (o) => `${o.id} ${o.created_at} ${o.operation}/${o.kind} ${o.ref} ${o.old_oid.slice(0, 12)} \u2192 ${o.new_oid.slice(0, 12)} by ${o.actor
|
|
50298
|
+
var renderOperation = (o) => `${o.id} ${o.created_at} ${o.operation}/${o.kind} ${o.ref} ${o.old_oid.slice(0, 12)} \u2192 ${o.new_oid.slice(0, 12)} by ${actorLabel(o.actor)}${o.protected ? " [protected]" : ""}${o.undone_by ? ` (undone by ${o.undone_by})` : ""}`;
|
|
49715
50299
|
async function waitForPromotion(ctx, p, timeout, interval) {
|
|
49716
50300
|
const deadline = Date.now() + timeout * 1e3;
|
|
49717
50301
|
let last = p.state;
|
|
@@ -49788,7 +50372,7 @@ next: --cursor ${v.next_cursor}` : "") : listWithCursor(v, ["id"]));
|
|
|
49788
50372
|
}
|
|
49789
50373
|
|
|
49790
50374
|
// packages/cli/src/commands/coordination.ts
|
|
49791
|
-
var holderOf = (c8) => `${c8.holder
|
|
50375
|
+
var holderOf = (c8) => `${actorLabel(c8.holder)}${c8.session_id ? ` (${c8.session_id})` : ""}`;
|
|
49792
50376
|
var renderClaim = (r) => [
|
|
49793
50377
|
`${r.already_held ? "Already yours" : "Claimed"}: ${r.claim.key}${r.claim.change ? ` (change #${r.claim.change.number})` : ""}`,
|
|
49794
50378
|
...r.skipped.length ? [`Taken by others: ${r.skipped.join(", ")}`] : [],
|
|
@@ -52091,7 +52675,7 @@ function safeLegs(m) {
|
|
|
52091
52675
|
}
|
|
52092
52676
|
|
|
52093
52677
|
// packages/cli/src/commands/actions.ts
|
|
52094
|
-
var renderRun2 = (r) => kv({ ...r, actor: r.actor
|
|
52678
|
+
var renderRun2 = (r) => kv({ ...r, actor: actorLabel(r.actor), trust: `${r.definition.source} ${r.definition.ref}${r.definition.trusted ? " (trusted)" : " (contributor-controlled)"}` }, [
|
|
52095
52679
|
"id",
|
|
52096
52680
|
"workflow_name",
|
|
52097
52681
|
"run_number",
|