deepline 0.3.124 → 0.3.126

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.
Files changed (31) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +10 -0
  2. package/dist/bundling-sources/sdk/src/compat.ts +17 -2
  3. package/dist/bundling-sources/sdk/src/play.ts +3 -1
  4. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  5. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +61 -10
  6. package/dist/bundling-sources/shared_libs/observability/redaction.ts +3 -0
  7. package/dist/bundling-sources/shared_libs/observability/runtime-console.ts +174 -0
  8. package/dist/bundling-sources/shared_libs/observability/telemetry.ts +26 -2
  9. package/dist/bundling-sources/shared_libs/play-runtime/play-admission-outcome.ts +3 -1
  10. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/contracts.ts +20 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/index.ts +1 -0
  12. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/postgres-adapter.ts +28 -11
  13. package/dist/bundling-sources/shared_libs/play-runtime/runtime-telemetry.ts +121 -1
  14. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +91 -2
  15. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +87 -41
  16. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +4 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-capacity.ts +5 -0
  18. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +1 -0
  19. package/dist/bundling-sources/shared_libs/security/secret-redaction.ts +56 -1
  20. package/dist/cli/index.js +528 -466
  21. package/dist/cli/index.mjs +528 -466
  22. package/dist/index.d.mts +13 -1
  23. package/dist/index.d.ts +13 -1
  24. package/dist/index.js +1 -1
  25. package/dist/index.mjs +1 -1
  26. package/dist/install-integrity.json +1 -0
  27. package/dist/release.d.mts +1 -1
  28. package/dist/release.d.ts +1 -1
  29. package/dist/release.js +1 -1
  30. package/dist/release.mjs +1 -1
  31. package/package.json +1 -1
@@ -3063,7 +3063,7 @@ var SDK_RELEASE = {
3063
3063
  // getters keep their established compatibility behavior.
3064
3064
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3065
3065
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3066
- version: "0.3.124",
3066
+ version: "0.3.126",
3067
3067
  updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
3068
3068
  packageCapabilities: {
3069
3069
  updatePreferences: 1
@@ -10298,6 +10298,12 @@ async function checkSdkCompatibility(baseUrl, options = {}) {
10298
10298
  clearTimeout(timeout);
10299
10299
  }
10300
10300
  }
10301
+ function isCiEnvironment() {
10302
+ return ["CI", "GITHUB_ACTIONS"].some((name) => {
10303
+ const value = process.env[name]?.trim().toLowerCase();
10304
+ return value === "1" || value === "true" || value === "yes";
10305
+ });
10306
+ }
10301
10307
  function enforceSdkCompatibilityResponse(response) {
10302
10308
  if (!response) {
10303
10309
  return;
@@ -10305,7 +10311,7 @@ function enforceSdkCompatibilityResponse(response) {
10305
10311
  if (response.update_required) {
10306
10312
  throw new Error(response.message);
10307
10313
  }
10308
- if (response.status === "deprecated" || response.status === "update_available") {
10314
+ if ((response.status === "deprecated" || response.status === "update_available") && !isCiEnvironment()) {
10309
10315
  process.stderr.write(`${response.message}
10310
10316
  `);
10311
10317
  }
@@ -21311,7 +21317,8 @@ function printResult(input2) {
21311
21317
  lines: [
21312
21318
  `${input2.directory}: ${input2.status}`,
21313
21319
  ...input2.outcomes.map(renderOutcome),
21314
- ...failed && input2.deployment.error ? [input2.deployment.error] : []
21320
+ ...failed && input2.deployment.error ? [input2.deployment.error] : [],
21321
+ `Details: deepline plays deployments ${input2.deployment.id}`
21315
21322
  ]
21316
21323
  }
21317
21324
  ]
@@ -21390,6 +21397,461 @@ async function handlePlaysDeploy(directory, options) {
21390
21397
  }
21391
21398
  }
21392
21399
 
21400
+ // src/cli/commands/git.ts
21401
+ var GIT_DEPLOY_FEATURE = "github-deploy";
21402
+ function shellQuote2(value) {
21403
+ return `'${value.replace(/'/g, `'\\''`)}'`;
21404
+ }
21405
+ function renderProject(project) {
21406
+ const root = project.rootDirectory ? ` (${project.rootDirectory})` : "";
21407
+ const flags = [
21408
+ project.disabled ? "unlinked" : "linked",
21409
+ `deploys ${project.productionBranch}`,
21410
+ project.checkOnPullRequest ? "PR checks on" : "PR checks off"
21411
+ ].join(", ");
21412
+ return `${project.id} ${project.repoFullName}${root} - ${flags}`;
21413
+ }
21414
+ function renderDeployment(deployment) {
21415
+ const fromDirectory = deployment.playProjectId === null;
21416
+ const kind = fromDirectory ? "directory deploy" : deployment.isProduction ? "production" : deployment.trigger === "pull_request" ? `pr #${deployment.pullRequestNumber ?? "?"}` : deployment.trigger;
21417
+ const suffix = deployment.error ? ` - ${deployment.error.slice(0, 80)}` : "";
21418
+ const source = [
21419
+ deployment.commitSha?.slice(0, 10),
21420
+ deployment.ref?.replace("refs/heads/", ""),
21421
+ fromDirectory ? deployment.repoFullName : null
21422
+ ].filter(Boolean).join(" ") || "no git metadata";
21423
+ return `${deployment.id} ${source} (${kind}) - ${deployment.status}${suffix}`;
21424
+ }
21425
+ async function handleList(options) {
21426
+ const { http } = getAuthedHttpClient();
21427
+ const payload = await http.get(
21428
+ `/api/v2/play-projects${options.all ? "?includeDisabled=1" : ""}`
21429
+ );
21430
+ printCommandEnvelope(
21431
+ {
21432
+ projects: payload.projects,
21433
+ count: payload.projects.length,
21434
+ render: {
21435
+ sections: [
21436
+ {
21437
+ title: "linked repositories",
21438
+ lines: payload.projects.length ? payload.projects.map(renderProject) : [
21439
+ "No repositories are linked. Run `deepline git repos` to see what the GitHub installation can reach, then `deepline git link`."
21440
+ ]
21441
+ }
21442
+ ]
21443
+ }
21444
+ },
21445
+ { json: options.json }
21446
+ );
21447
+ }
21448
+ function describeInstallationStatus(installation) {
21449
+ switch (installation.status) {
21450
+ case "active":
21451
+ return [
21452
+ `Connected to ${installation.accountLogin} (installation ${installation.installationId}).`
21453
+ ];
21454
+ case "uninstalled":
21455
+ return [
21456
+ `The Deepline GitHub App was uninstalled from ${installation.accountLogin}${installation.uninstalledAt ? ` at ${new Date(installation.uninstalledAt).toISOString()}` : ""}.`,
21457
+ "Deploys are paused for every linked repository. Run `deepline git connect`, then `deepline git link <owner/repo>` again for each repository; links from the old installation cannot be resumed."
21458
+ ];
21459
+ case "suspended":
21460
+ return [
21461
+ `GitHub suspended the Deepline App on ${installation.accountLogin}${installation.suspendedAt ? ` at ${new Date(installation.suspendedAt).toISOString()}` : ""}.`,
21462
+ "Pushes are not delivered while it is suspended; unsuspend it in GitHub settings."
21463
+ ];
21464
+ }
21465
+ }
21466
+ async function handleStatus2(options) {
21467
+ const { http } = getAuthedHttpClient();
21468
+ const payload = await http.get("/api/v2/github/installation");
21469
+ const installation = payload.installation;
21470
+ const others = (payload.installations ?? []).filter(
21471
+ (row) => row.installationId !== installation?.installationId
21472
+ );
21473
+ const lines = installation ? describeInstallationStatus(installation) : [
21474
+ "No GitHub installation is connected to this workspace. Run `deepline git connect`."
21475
+ ];
21476
+ for (const other of others) {
21477
+ lines.push(
21478
+ `Also: ${other.accountLogin} (installation ${other.installationId}) is ${other.status}.`
21479
+ );
21480
+ }
21481
+ const byId = new Map(
21482
+ (payload.installations ?? []).map((row) => [row.installationId, row])
21483
+ );
21484
+ const { projects } = await http.get(
21485
+ "/api/v2/play-projects?includeDisabled=1"
21486
+ );
21487
+ const affected_links = projects.map((project) => {
21488
+ const own = byId.get(project.installationId);
21489
+ const verdict = !own ? "unknown" : own.status === "active" ? null : own.status;
21490
+ return verdict ? {
21491
+ id: project.id,
21492
+ repoId: project.repoId,
21493
+ repoFullName: project.repoFullName,
21494
+ rootDirectory: project.rootDirectory,
21495
+ productionBranch: project.productionBranch,
21496
+ manifestPath: project.manifestPath,
21497
+ checkOnPullRequest: project.checkOnPullRequest,
21498
+ disabled: project.disabled,
21499
+ installationId: project.installationId,
21500
+ installationStatus: verdict
21501
+ } : null;
21502
+ }).filter((row) => row !== null);
21503
+ const activeInstallations = (payload.installations ?? []).filter(
21504
+ (row) => row.status === "active"
21505
+ );
21506
+ const relinkCommand = (link2) => [
21507
+ // The numeric repo id, not the name: a repository renamed while the
21508
+ // App was gone keeps a stale name here, and `git link` accepts the id.
21509
+ `deepline git link ${link2.repoId}`,
21510
+ // Stored values are admin-controlled text and this line is
21511
+ // copy-pasted into a shell by whoever reads it, so every
21512
+ // interpolated value below is quoted.
21513
+ `--branch ${shellQuote2(link2.productionBranch)}`,
21514
+ link2.rootDirectory ? `--root-dir ${shellQuote2(link2.rootDirectory)}` : "",
21515
+ link2.manifestPath && link2.manifestPath !== "deepline.json" ? `--manifest ${shellQuote2(link2.manifestPath)}` : "",
21516
+ link2.checkOnPullRequest ? "" : "--no-pr-checks",
21517
+ // With several active installations no id is guessed: the reader picks.
21518
+ activeInstallations.length > 1 ? "--installation <id>" : ""
21519
+ ].filter(Boolean).join(" ");
21520
+ for (const link2 of affected_links) {
21521
+ const label = `${link2.repoFullName}${link2.rootDirectory ? ` (${link2.rootDirectory})` : ""}`;
21522
+ lines.push(
21523
+ link2.installationStatus !== "suspended" ? `Link again: ${label} \u2014 its installation ${link2.installationId} is ${link2.installationStatus}; run \`${relinkCommand(link2)}\`${activeInstallations.length > 1 ? ` with <id> being the installation that reaches this repo: ${activeInstallations.map((row) => `${row.installationId} (${row.accountLogin})`).join(", ")}` : ""}.` : (
21524
+ // A link disabled during the suspension stays disabled; only an
21525
+ // enabled one picks up again on its own when GitHub unsuspends.
21526
+ link2.disabled ? `Paused and disabled: ${label} \u2014 its installation is suspended, and the link stays disabled after GitHub unsuspends it until you run \`deepline git set ${link2.id} --enabled\`.` : `Paused: ${label} \u2014 its installation is suspended; pushes resume when GitHub unsuspends it.`
21527
+ )
21528
+ );
21529
+ }
21530
+ printCommandEnvelope(
21531
+ {
21532
+ installation,
21533
+ installations: payload.installations ?? [],
21534
+ affected_links,
21535
+ render: { sections: [{ title: "github installation", lines }] }
21536
+ },
21537
+ { json: options.json }
21538
+ );
21539
+ }
21540
+ async function handleRepos(options) {
21541
+ const { http } = getAuthedHttpClient();
21542
+ const query = options.installation ? `?installationId=${encodeURIComponent(options.installation)}` : "";
21543
+ const payload = await http.get(`/api/v2/github/repos${query}`);
21544
+ printCommandEnvelope(
21545
+ {
21546
+ installation: payload.installation,
21547
+ repositories: payload.repositories,
21548
+ count: payload.repositories.length,
21549
+ render: {
21550
+ sections: [
21551
+ {
21552
+ title: `repos on installation @${payload.installation.accountLogin} (${payload.installation.repositorySelection})`,
21553
+ lines: payload.repositories.map(
21554
+ (repo) => `${repo.repoId} ${repo.fullName} (default: ${repo.defaultBranch}${repo.private ? ", private" : ""}${repo.fork ? ", fork" : ""})`
21555
+ )
21556
+ }
21557
+ ]
21558
+ }
21559
+ },
21560
+ { json: options.json }
21561
+ );
21562
+ }
21563
+ async function resolveRepoId(repo, installation) {
21564
+ if (/^\d+$/.test(repo)) return Number(repo);
21565
+ const { http } = getAuthedHttpClient();
21566
+ const query = installation ? `?installationId=${encodeURIComponent(installation)}` : "";
21567
+ const payload = await http.get(
21568
+ `/api/v2/github/repos${query}`
21569
+ );
21570
+ const match = payload.repositories.find(
21571
+ (entry) => entry.fullName.toLowerCase() === repo.toLowerCase()
21572
+ );
21573
+ if (!match) {
21574
+ throw new Error(
21575
+ `Repo "${repo}" is not accessible to the workspace's GitHub installation. Run \`deepline git repos\` to list what is.`
21576
+ );
21577
+ }
21578
+ return match.repoId;
21579
+ }
21580
+ async function handleLink(repo, options) {
21581
+ const repoId = await resolveRepoId(repo, options.installation);
21582
+ const { http } = getAuthedHttpClient();
21583
+ const payload = await http.post("/api/v2/play-projects", {
21584
+ repoId,
21585
+ ...options.installation !== void 0 ? { installationId: options.installation } : {},
21586
+ ...options.rootDir !== void 0 ? { rootDirectory: options.rootDir } : {},
21587
+ ...options.manifest !== void 0 ? { manifestPath: options.manifest } : {},
21588
+ ...options.branch !== void 0 ? { productionBranch: options.branch } : {},
21589
+ ...options.prChecks !== void 0 ? { checkOnPullRequest: options.prChecks } : {}
21590
+ });
21591
+ printCommandEnvelope(
21592
+ {
21593
+ project: payload.project,
21594
+ render: {
21595
+ sections: [
21596
+ {
21597
+ title: "linked",
21598
+ lines: [
21599
+ renderProject(payload.project),
21600
+ ...(payload.warnings ?? []).map(
21601
+ (warning) => `WARNING: ${warning}`
21602
+ ),
21603
+ "Pushes to the production branch now register and promote changed plays.",
21604
+ "Anyone who can push to this repo can change what runs in this workspace."
21605
+ ]
21606
+ }
21607
+ ]
21608
+ }
21609
+ },
21610
+ { json: options.json }
21611
+ );
21612
+ }
21613
+ async function handleGet(id, options) {
21614
+ const { http } = getAuthedHttpClient();
21615
+ const payload = await http.get(
21616
+ `/api/v2/play-projects/${encodeURIComponent(id)}`
21617
+ );
21618
+ printCommandEnvelope(
21619
+ {
21620
+ project: payload.project,
21621
+ render: {
21622
+ sections: [
21623
+ {
21624
+ title: "linked repository",
21625
+ lines: [renderProject(payload.project)]
21626
+ }
21627
+ ]
21628
+ }
21629
+ },
21630
+ { json: options.json }
21631
+ );
21632
+ }
21633
+ async function handleSet(id, options) {
21634
+ const { http } = getAuthedHttpClient();
21635
+ const payload = await http.patch(
21636
+ `/api/v2/play-projects/${encodeURIComponent(id)}`,
21637
+ {
21638
+ ...options.branch !== void 0 ? { productionBranch: options.branch } : {},
21639
+ ...options.rootDir !== void 0 ? { rootDirectory: options.rootDir } : {},
21640
+ ...options.manifest !== void 0 ? { manifestPath: options.manifest } : {},
21641
+ ...options.prChecks !== void 0 ? { checkOnPullRequest: options.prChecks } : {},
21642
+ ...options.disabled !== void 0 ? { disabled: options.disabled } : {}
21643
+ }
21644
+ );
21645
+ printCommandEnvelope(
21646
+ {
21647
+ project: payload.project,
21648
+ render: {
21649
+ sections: [
21650
+ { title: "updated", lines: [renderProject(payload.project)] }
21651
+ ]
21652
+ }
21653
+ },
21654
+ { json: options.json }
21655
+ );
21656
+ }
21657
+ async function handleUnlink(id, options) {
21658
+ const { http } = getAuthedHttpClient();
21659
+ await http.delete(`/api/v2/play-projects/${encodeURIComponent(id)}`);
21660
+ printCommandEnvelope(
21661
+ {
21662
+ ok: true,
21663
+ id,
21664
+ render: {
21665
+ sections: [
21666
+ {
21667
+ title: "unlinked",
21668
+ lines: [
21669
+ `Repository link ${id} removed. Deployments stop immediately and in-flight deploy credentials die; live plays keep running.`
21670
+ ]
21671
+ }
21672
+ ]
21673
+ }
21674
+ },
21675
+ { json: options.json }
21676
+ );
21677
+ }
21678
+ async function handleConnect(options) {
21679
+ const baseUrl = resolveBaseUrlOnly();
21680
+ const connectUrl = `${baseUrl.replace(/\/$/, "")}/api/v2/github/connect`;
21681
+ const opened = openInBrowser(connectUrl);
21682
+ printCommandEnvelope(
21683
+ {
21684
+ connectUrl,
21685
+ browser: opened,
21686
+ render: {
21687
+ sections: [
21688
+ {
21689
+ title: "connect GitHub",
21690
+ lines: [
21691
+ opened === "opened" ? `Opened ${connectUrl} in your browser.` : `Open ${connectUrl} in your browser.`,
21692
+ "Sign in as a workspace admin if prompted, approve the GitHub",
21693
+ "installation, then return here and run `deepline git repos`."
21694
+ ]
21695
+ }
21696
+ ]
21697
+ }
21698
+ },
21699
+ { json: options.json }
21700
+ );
21701
+ }
21702
+ async function handleDeploymentsList(options) {
21703
+ const { http } = getAuthedHttpClient();
21704
+ const query = new URLSearchParams();
21705
+ if (options.repo) query.set("projectId", options.repo);
21706
+ if (options.limit) query.set("limit", options.limit);
21707
+ const suffix = query.size > 0 ? `?${query.toString()}` : "";
21708
+ const payload = await http.get(
21709
+ `/api/v2/deployments${suffix}`
21710
+ );
21711
+ printCommandEnvelope(
21712
+ {
21713
+ deployments: payload.deployments,
21714
+ count: payload.deployments.length,
21715
+ render: {
21716
+ sections: [
21717
+ {
21718
+ title: "deployments",
21719
+ lines: payload.deployments.length ? payload.deployments.map(renderDeployment) : [
21720
+ "No deployments yet. Push to a linked repository, or run `deepline plays deploy`."
21721
+ ]
21722
+ }
21723
+ ]
21724
+ }
21725
+ },
21726
+ { json: options.json }
21727
+ );
21728
+ }
21729
+ function renderDeploymentPlayLines(results) {
21730
+ const plays = results?.plays;
21731
+ if (!Array.isArray(plays)) return [];
21732
+ return plays.flatMap((play) => {
21733
+ const version = play.version === void 0 ? "" : play.previousVersion === void 0 ? ` v${play.version}` : ` v${play.previousVersion} -> v${play.version}`;
21734
+ const file = play.file ? ` (${play.file})` : "";
21735
+ return [
21736
+ `${play.name ?? "?"}${file} - ${play.status ?? "?"}${version}`,
21737
+ ...play.error ? [` error: ${play.error}`] : [],
21738
+ ...play.warning ? [` warning: ${play.warning}`] : []
21739
+ ];
21740
+ });
21741
+ }
21742
+ async function handleDeploymentGet(id, options) {
21743
+ const { http } = getAuthedHttpClient();
21744
+ const payload = await http.get(`/api/v2/deployments/${encodeURIComponent(id)}`);
21745
+ const playLines = renderDeploymentPlayLines(payload.deployment.results);
21746
+ const repository = payload.project?.repoFullName ?? payload.deployment.repoFullName;
21747
+ printCommandEnvelope(
21748
+ {
21749
+ deployment: payload.deployment,
21750
+ project: payload.project,
21751
+ render: {
21752
+ sections: [
21753
+ {
21754
+ title: `deployment ${payload.deployment.id}${repository ? ` (${repository})` : ""}`,
21755
+ lines: [renderDeployment(payload.deployment)]
21756
+ },
21757
+ ...playLines.length ? [{ title: "plays", lines: playLines }] : []
21758
+ ]
21759
+ }
21760
+ },
21761
+ { json: options.json }
21762
+ );
21763
+ }
21764
+ async function handleDeploymentCancel(id, options) {
21765
+ const { http } = getAuthedHttpClient();
21766
+ const payload = await http.post(`/api/v2/deployments/${encodeURIComponent(id)}/cancel`, {});
21767
+ printCommandEnvelope(
21768
+ {
21769
+ cancelled: payload.cancelled,
21770
+ deployment: payload.deployment,
21771
+ render: {
21772
+ sections: [
21773
+ {
21774
+ title: payload.cancelled ? "cancelled" : "already cancelled",
21775
+ lines: [
21776
+ payload.deployment ? renderDeployment(payload.deployment) : `Deployment ${id} cancelled.`,
21777
+ "The build stops at its next step and nothing from it goes live."
21778
+ ]
21779
+ }
21780
+ ]
21781
+ }
21782
+ },
21783
+ { json: options.json }
21784
+ );
21785
+ }
21786
+ function registerGitCommands(program) {
21787
+ const git = program.command("git", { hidden: !cliFeatureEnabled(GIT_DEPLOY_FEATURE) }).alias("projects").description(
21788
+ "Git deploys: link repositories to this workspace and deploy plays on push."
21789
+ ).addHelpText(
21790
+ "after",
21791
+ `
21792
+ Examples:
21793
+ deepline git connect
21794
+ deepline git repos
21795
+ deepline git link acme/growth --branch main
21796
+ deepline git list
21797
+ deepline git set <id> --branch release
21798
+ deepline git unlink <id>
21799
+ deepline git deployments --repo <id>
21800
+ deepline git cancel <deployment-id>
21801
+ `
21802
+ );
21803
+ git.command("connect").description("Connect a GitHub installation (browser step).").option("--json", "Emit JSON output").action(async (options) => handleConnect(options));
21804
+ git.command("status").description(
21805
+ "Show the workspace's GitHub installation and whether it is still usable."
21806
+ ).option("--json", "Emit JSON output").action(async (options) => handleStatus2(options));
21807
+ git.command("repos").description("List repos the workspace's GitHub installation can reach.").option(
21808
+ "--installation <id>",
21809
+ "GitHub installation id (required when several are connected)"
21810
+ ).option("--json", "Emit JSON output").action(
21811
+ async (options) => handleRepos(options)
21812
+ );
21813
+ git.command("list").description("List linked repositories.").option("--all", "Include unlinked repositories").option("--json", "Emit JSON output").action(
21814
+ async (options) => handleList(options)
21815
+ );
21816
+ git.command("link").description("Link a repository to this workspace for deploy-on-push.").argument("<repo>", "Repo full name (owner/name) or numeric repo id").option("--root-dir <path>", "Base path inside the repo for this link").option("--branch <name>", "Production branch (default: repo default)").option(
21817
+ "--manifest <file>",
21818
+ "Manifest file name under the base path (default: deepline.json)"
21819
+ ).option(
21820
+ "--installation <id>",
21821
+ "GitHub installation id (required when several are connected)"
21822
+ ).option("--no-pr-checks", "Skip Check Runs on pull requests").option("--json", "Emit JSON output").action(
21823
+ async (repo, options) => handleLink(repo, options)
21824
+ );
21825
+ git.command("get").description("Show one linked repository.").argument("<id>", "Repository link id").option("--json", "Emit JSON output").action(async (id, options) => handleGet(id, options));
21826
+ git.command("set").description("Update a linked repository.").argument("<id>", "Repository link id").option("--branch <name>", "Production branch").option("--root-dir <path>", "Base path inside the repo for this link").option(
21827
+ "--manifest <file>",
21828
+ "Manifest file name under the base path (default: deepline.json)"
21829
+ ).option("--pr-checks", "Enable Check Runs on pull requests").option("--no-pr-checks", "Disable Check Runs on pull requests").option("--disabled", "Pause deployments for this link").option("--enabled", "Resume deployments for this link").option("--json", "Emit JSON output").action(
21830
+ async (id, options) => handleSet(id, {
21831
+ ...options,
21832
+ disabled: options.enabled === true ? false : options.disabled === true ? true : void 0
21833
+ })
21834
+ );
21835
+ git.command("unlink").description("Unlink a repository (live plays keep running).").argument("<id>", "Repository link id").option("--json", "Emit JSON output").action(
21836
+ async (id, options) => handleUnlink(id, options)
21837
+ );
21838
+ git.command("deployments").description("Deployment history; pass an id for one deployment.").argument("[id]", "Deployment id (omit to list)").option("--repo <id>", "Filter to one repository link").option("--limit <n>", "Max rows (default 50)").option("--json", "Emit JSON output").action(
21839
+ async (id, options) => id ? handleDeploymentGet(id, options) : handleDeploymentsList(options)
21840
+ );
21841
+ git.command("cancel").description(
21842
+ "Cancel a queued or running deployment before its plays go live (admin)."
21843
+ ).argument("<id>", "Deployment id").option("--json", "Emit JSON output").action(
21844
+ async (id, options) => handleDeploymentCancel(id, options)
21845
+ );
21846
+ const deployments = program.command("deployments", { hidden: true });
21847
+ deployments.command("list").option("--project <id>", "Filter to one repository link").option("--limit <n>", "Max rows (default 50)").option("--json", "Emit JSON output").action(
21848
+ async (options) => handleDeploymentsList({ ...options, repo: options.project })
21849
+ );
21850
+ deployments.command("get").argument("<id>", "Deployment id").option("--json", "Emit JSON output").action(
21851
+ async (id, options) => handleDeploymentGet(id, options)
21852
+ );
21853
+ }
21854
+
21393
21855
  // src/cli/commands/play.ts
21394
21856
  var PLAY_BUNDLER_MODULE_PATHS = [
21395
21857
  // Built CLI bundle: dist/cli/index.mjs -> dist/plays/bundle-play-file.mjs.
@@ -21736,7 +22198,7 @@ function rankPlayReferenceSuggestions(input2) {
21736
22198
  function searchQueryForMissingPlayReference(target) {
21737
22199
  return parseReferencedPlayTarget2(target).unqualifiedPlayName.replace(/[-_./]+/g, " ").replace(/\s+/g, " ").trim();
21738
22200
  }
21739
- function shellQuote2(value) {
22201
+ function shellQuote3(value) {
21740
22202
  return `'${value.replace(/'/g, `'\\''`)}'`;
21741
22203
  }
21742
22204
  function buildPlayReferenceNotFoundMessage(input2) {
@@ -21769,7 +22231,7 @@ function buildPlayReferenceNotFoundMessage(input2) {
21769
22231
  lines.push(
21770
22232
  "",
21771
22233
  "Search visible plays:",
21772
- ` deepline plays search ${shellQuote2(searchQuery)} --all --json`
22234
+ ` deepline plays search ${shellQuote3(searchQuery)} --all --json`
21773
22235
  );
21774
22236
  }
21775
22237
  return lines.join("\n");
@@ -26529,7 +26991,7 @@ function printPlayCheckOutcome(outcome, target, prefix) {
26529
26991
  printPlayCheckLimits(result.limits);
26530
26992
  if (result.artifactHash && outcome.exportName === PLAY_DEFAULT_EXPORT) {
26531
26993
  console.log(
26532
- ` publish: deepline plays publish ${shellQuote2(target)} --expected-artifact ${result.artifactHash}`
26994
+ ` publish: deepline plays publish ${shellQuote3(target)} --expected-artifact ${result.artifactHash}`
26533
26995
  );
26534
26996
  }
26535
26997
  printPlayTriggers(result.triggers);
@@ -27162,7 +27624,7 @@ async function handleRunsList(args) {
27162
27624
  return lines2.join("\n");
27163
27625
  });
27164
27626
  const nextOffset = (page.offset ?? offset ?? 0) + runs.length;
27165
- const nextPage = page.hasMore && playName ? `deepline runs list --play ${shellQuote2(playName)}${statusFilter ? ` --status ${shellQuote2(statusFilter)}` : ""} --limit ${page.limit ?? limit ?? 20} --offset ${nextOffset} --json` : null;
27627
+ const nextPage = page.hasMore && playName ? `deepline runs list --play ${shellQuote3(playName)}${statusFilter ? ` --status ${shellQuote3(statusFilter)}` : ""} --limit ${page.limit ?? limit ?? 20} --offset ${nextOffset} --json` : null;
27166
27628
  if (page.hasMore) {
27167
27629
  lines.push("List truncated \u2014 more runs match this filter.");
27168
27630
  lines.push(
@@ -27923,7 +28385,7 @@ async function handlePlayList(args) {
27923
28385
  cursor
27924
28386
  });
27925
28387
  const plays = page.plays;
27926
- const nextPage = page.hasMore && page.cursor ? `deepline plays list${categories ? ` --categories ${shellQuote2(categories)}` : ""}${limit ? ` --limit ${limit}` : ""}${showCost ? " --show-cost" : ""} --cursor ${shellQuote2(page.cursor)} --json` : null;
28388
+ const nextPage = page.hasMore && page.cursor ? `deepline plays list${categories ? ` --categories ${shellQuote3(categories)}` : ""}${limit ? ` --limit ${limit}` : ""}${showCost ? " --show-cost" : ""} --cursor ${shellQuote3(page.cursor)} --json` : null;
27927
28389
  if (page.hasMore && !nextPage) {
27928
28390
  throw new Error(
27929
28391
  "Play list reported more results without a continuation cursor"
@@ -28344,7 +28806,7 @@ async function handlePlaySave(args) {
28344
28806
  message: "Refusing to save: the local file no longer produces the artifact checked earlier.",
28345
28807
  expectedArtifactHash: options.expectedArtifactHash,
28346
28808
  currentArtifactHash: artifactHash,
28347
- next: `deepline plays check ${shellQuote2(options.target)}`
28809
+ next: `deepline plays check ${shellQuote3(options.target)}`
28348
28810
  };
28349
28811
  if (options.jsonOutput) {
28350
28812
  printCompactJson(result);
@@ -28409,7 +28871,7 @@ async function handlePlaySave(args) {
28409
28871
  graphHash: graph.root.artifact.graphHash,
28410
28872
  artifactStorageKey: saved.artifactStorageKey,
28411
28873
  next: {
28412
- publish: `deepline plays publish ${shellQuote2(options.target)} --expected-artifact ${artifactHash}`,
28874
+ publish: `deepline plays publish ${shellQuote3(options.target)} --expected-artifact ${artifactHash}`,
28413
28875
  get: `deepline plays get ${playName} --source --out ./${playName}/ --working`
28414
28876
  }
28415
28877
  };
@@ -28469,7 +28931,7 @@ async function handlePlayPublish(args) {
28469
28931
  message: "Refusing to publish: the local file no longer produces the artifact checked earlier.",
28470
28932
  expectedArtifactHash: options.expectedArtifactHash,
28471
28933
  currentArtifactHash: artifactHash,
28472
- next: `deepline plays check ${shellQuote2(playName)}`
28934
+ next: `deepline plays check ${shellQuote3(playName)}`
28473
28935
  };
28474
28936
  if (options.jsonOutput) {
28475
28937
  printCompactJson(result3);
@@ -29237,6 +29699,22 @@ Examples:
29237
29699
  ).option("--repo <owner/name>", "Repository to record on the deployment").option("--ref <ref>", "Branch or ref to record on the deployment").option("--commit <sha>", "Commit to record on the deployment").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(
29238
29700
  async (directory, options) => handlePlaysDeploy(directory, options)
29239
29701
  );
29702
+ play.command("deployments [id]").description(
29703
+ "Deployment history for this workspace; pass an id for one deployment and its plays."
29704
+ ).addHelpText(
29705
+ "after",
29706
+ `
29707
+ Notes:
29708
+ Lists every deployment, whether \`deepline plays deploy\` or a linked
29709
+ repository's push made it. The same views as \`deepline git deployments\`.
29710
+
29711
+ Examples:
29712
+ deepline plays deployments
29713
+ deepline plays deployments <deployment-id>
29714
+ `
29715
+ ).option("--limit <n>", "Max rows (default 50)").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(
29716
+ async (id, options) => id ? handleDeploymentGet(id, options) : handleDeploymentsList(options)
29717
+ );
29240
29718
  addPublishHelp(
29241
29719
  play.command("publish <target>").description(
29242
29720
  "Promote a saved play revision or publish a local play file."
@@ -37657,6 +38135,7 @@ function renderMonitorJob(payload) {
37657
38135
  if (!job) return void 0;
37658
38136
  const id = asString(job.id) ?? "<unknown>";
37659
38137
  const state = asString(job.state) ?? "unknown";
38138
+ const outcome = asString(job.outcome);
37660
38139
  const phase = asString(job.phase);
37661
38140
  const counts = asRecord2(job.counts);
37662
38141
  const reconciliation = asRecord2(job.reconciliation);
@@ -37664,6 +38143,9 @@ function renderMonitorJob(payload) {
37664
38143
  `Monitor job: ${id}`,
37665
38144
  `State: ${state}${phase ? ` (${phase})` : ""}`
37666
38145
  ];
38146
+ if (outcome) lines.push(`Outcome: ${outcome}`);
38147
+ const operationId = asString(job.operation_id);
38148
+ if (operationId) lines.push(`Operation: ${operationId}`);
37667
38149
  if (counts) {
37668
38150
  lines.push(
37669
38151
  `Counts: requested ${asFiniteNumber(counts.requested) ?? 0}, created ${asFiniteNumber(counts.created) ?? 0}, updated ${asFiniteNumber(counts.updated) ?? 0}, removed ${asFiniteNumber(counts.removed) ?? 0}, unchanged ${asFiniteNumber(counts.unchanged) ?? 0}, verified ${asFiniteNumber(counts.verified) ?? 0}, failed ${asFiniteNumber(counts.failed) ?? 0}`
@@ -40859,448 +41341,6 @@ Examples:
40859
41341
  );
40860
41342
  }
40861
41343
 
40862
- // src/cli/commands/git.ts
40863
- var GIT_DEPLOY_FEATURE = "github-deploy";
40864
- function shellQuote3(value) {
40865
- return `'${value.replace(/'/g, `'\\''`)}'`;
40866
- }
40867
- function renderProject(project) {
40868
- const root = project.rootDirectory ? ` (${project.rootDirectory})` : "";
40869
- const flags = [
40870
- project.disabled ? "unlinked" : "linked",
40871
- `deploys ${project.productionBranch}`,
40872
- project.checkOnPullRequest ? "PR checks on" : "PR checks off"
40873
- ].join(", ");
40874
- return `${project.id} ${project.repoFullName}${root} - ${flags}`;
40875
- }
40876
- function renderDeployment(deployment) {
40877
- const kind = deployment.isProduction ? "production" : deployment.trigger === "pull_request" ? `pr #${deployment.pullRequestNumber ?? "?"}` : deployment.trigger;
40878
- const suffix = deployment.error ? ` - ${deployment.error.slice(0, 80)}` : "";
40879
- const source = [
40880
- deployment.commitSha?.slice(0, 10),
40881
- deployment.ref?.replace("refs/heads/", "") ?? deployment.repoFullName
40882
- ].filter(Boolean).join(" ") || "directory";
40883
- return `${deployment.id} ${source} (${kind}) - ${deployment.status}${suffix}`;
40884
- }
40885
- async function handleList(options) {
40886
- const { http } = getAuthedHttpClient();
40887
- const payload = await http.get(
40888
- `/api/v2/play-projects${options.all ? "?includeDisabled=1" : ""}`
40889
- );
40890
- printCommandEnvelope(
40891
- {
40892
- projects: payload.projects,
40893
- count: payload.projects.length,
40894
- render: {
40895
- sections: [
40896
- {
40897
- title: "linked repositories",
40898
- lines: payload.projects.length ? payload.projects.map(renderProject) : [
40899
- "No repositories are linked. Run `deepline git repos` to see what the GitHub installation can reach, then `deepline git link`."
40900
- ]
40901
- }
40902
- ]
40903
- }
40904
- },
40905
- { json: options.json }
40906
- );
40907
- }
40908
- function describeInstallationStatus(installation) {
40909
- switch (installation.status) {
40910
- case "active":
40911
- return [
40912
- `Connected to ${installation.accountLogin} (installation ${installation.installationId}).`
40913
- ];
40914
- case "uninstalled":
40915
- return [
40916
- `The Deepline GitHub App was uninstalled from ${installation.accountLogin}${installation.uninstalledAt ? ` at ${new Date(installation.uninstalledAt).toISOString()}` : ""}.`,
40917
- "Deploys are paused for every linked repository. Run `deepline git connect`, then `deepline git link <owner/repo>` again for each repository; links from the old installation cannot be resumed."
40918
- ];
40919
- case "suspended":
40920
- return [
40921
- `GitHub suspended the Deepline App on ${installation.accountLogin}${installation.suspendedAt ? ` at ${new Date(installation.suspendedAt).toISOString()}` : ""}.`,
40922
- "Pushes are not delivered while it is suspended; unsuspend it in GitHub settings."
40923
- ];
40924
- }
40925
- }
40926
- async function handleStatus2(options) {
40927
- const { http } = getAuthedHttpClient();
40928
- const payload = await http.get("/api/v2/github/installation");
40929
- const installation = payload.installation;
40930
- const others = (payload.installations ?? []).filter(
40931
- (row) => row.installationId !== installation?.installationId
40932
- );
40933
- const lines = installation ? describeInstallationStatus(installation) : [
40934
- "No GitHub installation is connected to this workspace. Run `deepline git connect`."
40935
- ];
40936
- for (const other of others) {
40937
- lines.push(
40938
- `Also: ${other.accountLogin} (installation ${other.installationId}) is ${other.status}.`
40939
- );
40940
- }
40941
- const byId = new Map(
40942
- (payload.installations ?? []).map((row) => [row.installationId, row])
40943
- );
40944
- const { projects } = await http.get(
40945
- "/api/v2/play-projects?includeDisabled=1"
40946
- );
40947
- const affected_links = projects.map((project) => {
40948
- const own = byId.get(project.installationId);
40949
- const verdict = !own ? "unknown" : own.status === "active" ? null : own.status;
40950
- return verdict ? {
40951
- id: project.id,
40952
- repoId: project.repoId,
40953
- repoFullName: project.repoFullName,
40954
- rootDirectory: project.rootDirectory,
40955
- productionBranch: project.productionBranch,
40956
- manifestPath: project.manifestPath,
40957
- checkOnPullRequest: project.checkOnPullRequest,
40958
- disabled: project.disabled,
40959
- installationId: project.installationId,
40960
- installationStatus: verdict
40961
- } : null;
40962
- }).filter((row) => row !== null);
40963
- const activeInstallations = (payload.installations ?? []).filter(
40964
- (row) => row.status === "active"
40965
- );
40966
- const relinkCommand = (link2) => [
40967
- // The numeric repo id, not the name: a repository renamed while the
40968
- // App was gone keeps a stale name here, and `git link` accepts the id.
40969
- `deepline git link ${link2.repoId}`,
40970
- // Stored values are admin-controlled text and this line is
40971
- // copy-pasted into a shell by whoever reads it, so every
40972
- // interpolated value below is quoted.
40973
- `--branch ${shellQuote3(link2.productionBranch)}`,
40974
- link2.rootDirectory ? `--root-dir ${shellQuote3(link2.rootDirectory)}` : "",
40975
- link2.manifestPath && link2.manifestPath !== "deepline.json" ? `--manifest ${shellQuote3(link2.manifestPath)}` : "",
40976
- link2.checkOnPullRequest ? "" : "--no-pr-checks",
40977
- // With several active installations no id is guessed: the reader picks.
40978
- activeInstallations.length > 1 ? "--installation <id>" : ""
40979
- ].filter(Boolean).join(" ");
40980
- for (const link2 of affected_links) {
40981
- const label = `${link2.repoFullName}${link2.rootDirectory ? ` (${link2.rootDirectory})` : ""}`;
40982
- lines.push(
40983
- link2.installationStatus !== "suspended" ? `Link again: ${label} \u2014 its installation ${link2.installationId} is ${link2.installationStatus}; run \`${relinkCommand(link2)}\`${activeInstallations.length > 1 ? ` with <id> being the installation that reaches this repo: ${activeInstallations.map((row) => `${row.installationId} (${row.accountLogin})`).join(", ")}` : ""}.` : (
40984
- // A link disabled during the suspension stays disabled; only an
40985
- // enabled one picks up again on its own when GitHub unsuspends.
40986
- link2.disabled ? `Paused and disabled: ${label} \u2014 its installation is suspended, and the link stays disabled after GitHub unsuspends it until you run \`deepline git set ${link2.id} --enabled\`.` : `Paused: ${label} \u2014 its installation is suspended; pushes resume when GitHub unsuspends it.`
40987
- )
40988
- );
40989
- }
40990
- printCommandEnvelope(
40991
- {
40992
- installation,
40993
- installations: payload.installations ?? [],
40994
- affected_links,
40995
- render: { sections: [{ title: "github installation", lines }] }
40996
- },
40997
- { json: options.json }
40998
- );
40999
- }
41000
- async function handleRepos(options) {
41001
- const { http } = getAuthedHttpClient();
41002
- const query = options.installation ? `?installationId=${encodeURIComponent(options.installation)}` : "";
41003
- const payload = await http.get(`/api/v2/github/repos${query}`);
41004
- printCommandEnvelope(
41005
- {
41006
- installation: payload.installation,
41007
- repositories: payload.repositories,
41008
- count: payload.repositories.length,
41009
- render: {
41010
- sections: [
41011
- {
41012
- title: `repos on installation @${payload.installation.accountLogin} (${payload.installation.repositorySelection})`,
41013
- lines: payload.repositories.map(
41014
- (repo) => `${repo.repoId} ${repo.fullName} (default: ${repo.defaultBranch}${repo.private ? ", private" : ""}${repo.fork ? ", fork" : ""})`
41015
- )
41016
- }
41017
- ]
41018
- }
41019
- },
41020
- { json: options.json }
41021
- );
41022
- }
41023
- async function resolveRepoId(repo, installation) {
41024
- if (/^\d+$/.test(repo)) return Number(repo);
41025
- const { http } = getAuthedHttpClient();
41026
- const query = installation ? `?installationId=${encodeURIComponent(installation)}` : "";
41027
- const payload = await http.get(
41028
- `/api/v2/github/repos${query}`
41029
- );
41030
- const match = payload.repositories.find(
41031
- (entry) => entry.fullName.toLowerCase() === repo.toLowerCase()
41032
- );
41033
- if (!match) {
41034
- throw new Error(
41035
- `Repo "${repo}" is not accessible to the workspace's GitHub installation. Run \`deepline git repos\` to list what is.`
41036
- );
41037
- }
41038
- return match.repoId;
41039
- }
41040
- async function handleLink(repo, options) {
41041
- const repoId = await resolveRepoId(repo, options.installation);
41042
- const { http } = getAuthedHttpClient();
41043
- const payload = await http.post("/api/v2/play-projects", {
41044
- repoId,
41045
- ...options.installation !== void 0 ? { installationId: options.installation } : {},
41046
- ...options.rootDir !== void 0 ? { rootDirectory: options.rootDir } : {},
41047
- ...options.manifest !== void 0 ? { manifestPath: options.manifest } : {},
41048
- ...options.branch !== void 0 ? { productionBranch: options.branch } : {},
41049
- ...options.prChecks !== void 0 ? { checkOnPullRequest: options.prChecks } : {}
41050
- });
41051
- printCommandEnvelope(
41052
- {
41053
- project: payload.project,
41054
- render: {
41055
- sections: [
41056
- {
41057
- title: "linked",
41058
- lines: [
41059
- renderProject(payload.project),
41060
- ...(payload.warnings ?? []).map(
41061
- (warning) => `WARNING: ${warning}`
41062
- ),
41063
- "Pushes to the production branch now register and promote changed plays.",
41064
- "Anyone who can push to this repo can change what runs in this workspace."
41065
- ]
41066
- }
41067
- ]
41068
- }
41069
- },
41070
- { json: options.json }
41071
- );
41072
- }
41073
- async function handleGet(id, options) {
41074
- const { http } = getAuthedHttpClient();
41075
- const payload = await http.get(
41076
- `/api/v2/play-projects/${encodeURIComponent(id)}`
41077
- );
41078
- printCommandEnvelope(
41079
- {
41080
- project: payload.project,
41081
- render: {
41082
- sections: [
41083
- {
41084
- title: "linked repository",
41085
- lines: [renderProject(payload.project)]
41086
- }
41087
- ]
41088
- }
41089
- },
41090
- { json: options.json }
41091
- );
41092
- }
41093
- async function handleSet(id, options) {
41094
- const { http } = getAuthedHttpClient();
41095
- const payload = await http.patch(
41096
- `/api/v2/play-projects/${encodeURIComponent(id)}`,
41097
- {
41098
- ...options.branch !== void 0 ? { productionBranch: options.branch } : {},
41099
- ...options.rootDir !== void 0 ? { rootDirectory: options.rootDir } : {},
41100
- ...options.manifest !== void 0 ? { manifestPath: options.manifest } : {},
41101
- ...options.prChecks !== void 0 ? { checkOnPullRequest: options.prChecks } : {},
41102
- ...options.disabled !== void 0 ? { disabled: options.disabled } : {}
41103
- }
41104
- );
41105
- printCommandEnvelope(
41106
- {
41107
- project: payload.project,
41108
- render: {
41109
- sections: [
41110
- { title: "updated", lines: [renderProject(payload.project)] }
41111
- ]
41112
- }
41113
- },
41114
- { json: options.json }
41115
- );
41116
- }
41117
- async function handleUnlink(id, options) {
41118
- const { http } = getAuthedHttpClient();
41119
- await http.delete(`/api/v2/play-projects/${encodeURIComponent(id)}`);
41120
- printCommandEnvelope(
41121
- {
41122
- ok: true,
41123
- id,
41124
- render: {
41125
- sections: [
41126
- {
41127
- title: "unlinked",
41128
- lines: [
41129
- `Repository link ${id} removed. Deployments stop immediately and in-flight deploy credentials die; live plays keep running.`
41130
- ]
41131
- }
41132
- ]
41133
- }
41134
- },
41135
- { json: options.json }
41136
- );
41137
- }
41138
- async function handleConnect(options) {
41139
- const baseUrl = resolveBaseUrlOnly();
41140
- const connectUrl = `${baseUrl.replace(/\/$/, "")}/api/v2/github/connect`;
41141
- const opened = openInBrowser(connectUrl);
41142
- printCommandEnvelope(
41143
- {
41144
- connectUrl,
41145
- browser: opened,
41146
- render: {
41147
- sections: [
41148
- {
41149
- title: "connect GitHub",
41150
- lines: [
41151
- opened === "opened" ? `Opened ${connectUrl} in your browser.` : `Open ${connectUrl} in your browser.`,
41152
- "Sign in as a workspace admin if prompted, approve the GitHub",
41153
- "installation, then return here and run `deepline git repos`."
41154
- ]
41155
- }
41156
- ]
41157
- }
41158
- },
41159
- { json: options.json }
41160
- );
41161
- }
41162
- async function handleDeploymentsList(options) {
41163
- const { http } = getAuthedHttpClient();
41164
- const query = new URLSearchParams();
41165
- if (options.repo) query.set("projectId", options.repo);
41166
- if (options.limit) query.set("limit", options.limit);
41167
- const suffix = query.size > 0 ? `?${query.toString()}` : "";
41168
- const payload = await http.get(
41169
- `/api/v2/deployments${suffix}`
41170
- );
41171
- printCommandEnvelope(
41172
- {
41173
- deployments: payload.deployments,
41174
- count: payload.deployments.length,
41175
- render: {
41176
- sections: [
41177
- {
41178
- title: "deployments",
41179
- lines: payload.deployments.length ? payload.deployments.map(renderDeployment) : [
41180
- "No deployments yet. Push to a linked repository to create one."
41181
- ]
41182
- }
41183
- ]
41184
- }
41185
- },
41186
- { json: options.json }
41187
- );
41188
- }
41189
- async function handleDeploymentGet(id, options) {
41190
- const { http } = getAuthedHttpClient();
41191
- const payload = await http.get(`/api/v2/deployments/${encodeURIComponent(id)}`);
41192
- const results = payload.deployment.results;
41193
- const playLines = Array.isArray(results?.plays) ? results.plays.map(
41194
- (play) => `${play.name ?? "?"} - ${play.status ?? "?"}${play.error ? `: ${play.error}` : ""}`
41195
- ) : [];
41196
- printCommandEnvelope(
41197
- {
41198
- deployment: payload.deployment,
41199
- project: payload.project,
41200
- render: {
41201
- sections: [
41202
- {
41203
- title: `deployment ${payload.deployment.id}${payload.project ? ` (${payload.project.repoFullName})` : ""}`,
41204
- lines: [renderDeployment(payload.deployment)]
41205
- },
41206
- ...playLines.length ? [{ title: "plays", lines: playLines }] : []
41207
- ]
41208
- }
41209
- },
41210
- { json: options.json }
41211
- );
41212
- }
41213
- async function handleDeploymentCancel(id, options) {
41214
- const { http } = getAuthedHttpClient();
41215
- const payload = await http.post(`/api/v2/deployments/${encodeURIComponent(id)}/cancel`, {});
41216
- printCommandEnvelope(
41217
- {
41218
- cancelled: payload.cancelled,
41219
- deployment: payload.deployment,
41220
- render: {
41221
- sections: [
41222
- {
41223
- title: payload.cancelled ? "cancelled" : "already cancelled",
41224
- lines: [
41225
- payload.deployment ? renderDeployment(payload.deployment) : `Deployment ${id} cancelled.`,
41226
- "The build stops at its next step and nothing from it goes live."
41227
- ]
41228
- }
41229
- ]
41230
- }
41231
- },
41232
- { json: options.json }
41233
- );
41234
- }
41235
- function registerGitCommands(program) {
41236
- const git = program.command("git", { hidden: !cliFeatureEnabled(GIT_DEPLOY_FEATURE) }).alias("projects").description(
41237
- "Git deploys: link repositories to this workspace and deploy plays on push."
41238
- ).addHelpText(
41239
- "after",
41240
- `
41241
- Examples:
41242
- deepline git connect
41243
- deepline git repos
41244
- deepline git link acme/growth --branch main
41245
- deepline git list
41246
- deepline git set <id> --branch release
41247
- deepline git unlink <id>
41248
- deepline git deployments --repo <id>
41249
- deepline git cancel <deployment-id>
41250
- `
41251
- );
41252
- git.command("connect").description("Connect a GitHub installation (browser step).").option("--json", "Emit JSON output").action(async (options) => handleConnect(options));
41253
- git.command("status").description(
41254
- "Show the workspace's GitHub installation and whether it is still usable."
41255
- ).option("--json", "Emit JSON output").action(async (options) => handleStatus2(options));
41256
- git.command("repos").description("List repos the workspace's GitHub installation can reach.").option(
41257
- "--installation <id>",
41258
- "GitHub installation id (required when several are connected)"
41259
- ).option("--json", "Emit JSON output").action(
41260
- async (options) => handleRepos(options)
41261
- );
41262
- git.command("list").description("List linked repositories.").option("--all", "Include unlinked repositories").option("--json", "Emit JSON output").action(
41263
- async (options) => handleList(options)
41264
- );
41265
- git.command("link").description("Link a repository to this workspace for deploy-on-push.").argument("<repo>", "Repo full name (owner/name) or numeric repo id").option("--root-dir <path>", "Base path inside the repo for this link").option("--branch <name>", "Production branch (default: repo default)").option(
41266
- "--manifest <file>",
41267
- "Manifest file name under the base path (default: deepline.json)"
41268
- ).option(
41269
- "--installation <id>",
41270
- "GitHub installation id (required when several are connected)"
41271
- ).option("--no-pr-checks", "Skip Check Runs on pull requests").option("--json", "Emit JSON output").action(
41272
- async (repo, options) => handleLink(repo, options)
41273
- );
41274
- git.command("get").description("Show one linked repository.").argument("<id>", "Repository link id").option("--json", "Emit JSON output").action(async (id, options) => handleGet(id, options));
41275
- git.command("set").description("Update a linked repository.").argument("<id>", "Repository link id").option("--branch <name>", "Production branch").option("--root-dir <path>", "Base path inside the repo for this link").option(
41276
- "--manifest <file>",
41277
- "Manifest file name under the base path (default: deepline.json)"
41278
- ).option("--pr-checks", "Enable Check Runs on pull requests").option("--no-pr-checks", "Disable Check Runs on pull requests").option("--disabled", "Pause deployments for this link").option("--enabled", "Resume deployments for this link").option("--json", "Emit JSON output").action(
41279
- async (id, options) => handleSet(id, {
41280
- ...options,
41281
- disabled: options.enabled === true ? false : options.disabled === true ? true : void 0
41282
- })
41283
- );
41284
- git.command("unlink").description("Unlink a repository (live plays keep running).").argument("<id>", "Repository link id").option("--json", "Emit JSON output").action(
41285
- async (id, options) => handleUnlink(id, options)
41286
- );
41287
- git.command("deployments").description("Deployment history; pass an id for one deployment.").argument("[id]", "Deployment id (omit to list)").option("--repo <id>", "Filter to one repository link").option("--limit <n>", "Max rows (default 50)").option("--json", "Emit JSON output").action(
41288
- async (id, options) => id ? handleDeploymentGet(id, options) : handleDeploymentsList(options)
41289
- );
41290
- git.command("cancel").description(
41291
- "Cancel a queued or running deployment before its plays go live (admin)."
41292
- ).argument("<id>", "Deployment id").option("--json", "Emit JSON output").action(
41293
- async (id, options) => handleDeploymentCancel(id, options)
41294
- );
41295
- const deployments = program.command("deployments", { hidden: true });
41296
- deployments.command("list").option("--project <id>", "Filter to one repository link").option("--limit <n>", "Max rows (default 50)").option("--json", "Emit JSON output").action(
41297
- async (options) => handleDeploymentsList({ ...options, repo: options.project })
41298
- );
41299
- deployments.command("get").argument("<id>", "Deployment id").option("--json", "Emit JSON output").action(
41300
- async (id, options) => handleDeploymentGet(id, options)
41301
- );
41302
- }
41303
-
41304
41344
  // src/cli/getting-started.ts
41305
41345
  var DEEPLINE_GTM_SKILL = "/deepline-gtm";
41306
41346
  var DEEPLINE_GTM_STARTER_PROMPTS = [
@@ -41395,6 +41435,33 @@ function normalizeSecretName(value) {
41395
41435
  }
41396
41436
  return normalized;
41397
41437
  }
41438
+ function normalizePlaySelector(value) {
41439
+ const normalized = value?.trim();
41440
+ if (value !== void 0 && !normalized) {
41441
+ throw new Error("--play requires a bare Play name.");
41442
+ }
41443
+ if (!normalized) return void 0;
41444
+ if (normalized.includes("/")) {
41445
+ throw new Error(
41446
+ "--play expects the bare Play name, not an org-qualified name."
41447
+ );
41448
+ }
41449
+ return normalized;
41450
+ }
41451
+ function resolveSecretWriteScope(options) {
41452
+ const playName = normalizePlaySelector(options.play);
41453
+ if (options.scope && options.scope !== "org" && options.scope !== "play") {
41454
+ throw new Error("--scope must be either org or play.");
41455
+ }
41456
+ const scope = options.scope === void 0 ? playName ? "play" : "org" : options.scope;
41457
+ if (scope === "play" && !playName) {
41458
+ throw new Error("--play <name> is required when --scope play is used.");
41459
+ }
41460
+ if (scope === "org" && playName) {
41461
+ throw new Error("--play can only be used with --scope play.");
41462
+ }
41463
+ return scope === "play" ? { scope, playName } : { scope };
41464
+ }
41398
41465
  function renderSecret(secret) {
41399
41466
  const scope = secret.scope === "play" && secret.playName ? `play:${secret.playName}` : secret.scope;
41400
41467
  const added = new Date(secret.createdAt).toISOString();
@@ -41564,7 +41631,8 @@ async function handleList2(options) {
41564
41631
  async function handleCheck(nameInput, options) {
41565
41632
  const name = normalizeSecretName(nameInput);
41566
41633
  const client2 = new DeeplineClient();
41567
- const check = options.play !== void 0 ? await client2.inspectSecret(name, { playName: options.play }) : null;
41634
+ const playName = options.play === void 0 ? void 0 : normalizePlaySelector(options.play);
41635
+ const check = playName !== void 0 ? await client2.inspectSecret(name, { playName }) : null;
41568
41636
  const secret = check ? check.secret : await client2.checkSecret(name);
41569
41637
  const ok = check ? check.ok : Boolean(secret);
41570
41638
  const quotedPlay = `'${(options.play ?? "PLAY").replace(/'/g, `'"'"'`)}'`;
@@ -41595,11 +41663,7 @@ async function handleCheck(nameInput, options) {
41595
41663
  async function handleSet2(nameInput, forbidden, options) {
41596
41664
  preventShellHistoryLeak(forbidden);
41597
41665
  const name = normalizeSecretName(nameInput);
41598
- const scope = options.scope === "play" ? "play" : "org";
41599
- const playName = options.play?.trim();
41600
- if (scope === "play" && !playName) {
41601
- throw new Error("--play <name> is required when --scope play is used.");
41602
- }
41666
+ const { scope, playName } = resolveSecretWriteScope(options);
41603
41667
  const value = await readSecretValueFromOptions(options);
41604
41668
  const { http } = getAuthedHttpClient();
41605
41669
  const response = await http.post(
@@ -41691,7 +41755,8 @@ Notes:
41691
41755
  Secret values are never accepted as command arguments. Use the hidden prompt,
41692
41756
  or one explicit non-argv source: --value-stdin, --from-env NAME, or
41693
41757
  --from-file PATH. Values are never printed. secrets list shows only metadata,
41694
- including any note you intentionally added.
41758
+ including any note you intentionally added. Supplying --play implies
41759
+ --scope play; --play expects the bare Play name in the current workspace.
41695
41760
 
41696
41761
  Examples:
41697
41762
  deepline secrets list
@@ -41717,7 +41782,7 @@ Examples:
41717
41782
  ).option("--json", "Emit JSON output").action(async (name, options) => {
41718
41783
  await handleCheck(name, options);
41719
41784
  });
41720
- secrets.command("set").description("Set or rotate a secret without exposing its value in argv.").argument("<name>", "Secret name").argument("[forbidden...]", "Do not pass secret values here").option("--json", "Emit JSON output").option("--scope <scope>", "Secret scope: org or play", "org").option("--play <name>", "Play name for play-scoped secrets").option(
41785
+ secrets.command("set").description("Set or rotate a secret without exposing its value in argv.").argument("<name>", "Secret name").argument("[forbidden...]", "Do not pass secret values here").option("--json", "Emit JSON output").option("--scope <scope>", "Secret scope: org or play; --play implies play").option("--play <name>", "Play name for play-scoped secrets").option(
41721
41786
  "--note <note>",
41722
41787
  "Optional non-sensitive memo (maximum 500 characters)"
41723
41788
  ).option("--value-stdin", "Read the value from piped stdin").option("--from-env <name>", "Read the value from an environment variable").option("--from-file <path>", "Read the value from a file").action(
@@ -42354,7 +42419,7 @@ async function maybeReportSdkCliFailure(input2) {
42354
42419
  // src/cli/skills-sync.ts
42355
42420
  var CHECK_TIMEOUT_MS2 = 3e3;
42356
42421
  function shouldSkipSkillsSync() {
42357
- if (detectAgentRuntime() === "claude_cowork") {
42422
+ if (detectAgentRuntime() === "claude_cowork" || isCiEnvironment()) {
42358
42423
  return true;
42359
42424
  }
42360
42425
  const value = process.env.DEEPLINE_SKIP_SKILLS_SYNC?.trim().toLowerCase();
@@ -49816,11 +49881,8 @@ function envTruthy(name) {
49816
49881
  const value = process.env[name]?.trim().toLowerCase();
49817
49882
  return value === "1" || value === "true" || value === "yes";
49818
49883
  }
49819
- function isCi() {
49820
- return envTruthy("CI") || envTruthy("GITHUB_ACTIONS");
49821
- }
49822
49884
  function shouldSkipSelfUpdate() {
49823
- return envTruthy("DEEPLINE_SKIP_SELF_UPDATE") || envTruthy("DEEPLINE_NO_AUTO_UPDATE") || envTruthy("DEEPLINE_SKIP_SDK_AUTO_UPDATE") || envTruthy("DEEPLINE_DISABLE_AUTO_UPDATE") || !shouldRunCliAutoUpdate() || isCi();
49885
+ return envTruthy("DEEPLINE_SKIP_SELF_UPDATE") || envTruthy("DEEPLINE_NO_AUTO_UPDATE") || envTruthy("DEEPLINE_SKIP_SDK_AUTO_UPDATE") || envTruthy("DEEPLINE_DISABLE_AUTO_UPDATE") || !shouldRunCliAutoUpdate() || isCiEnvironment();
49824
49886
  }
49825
49887
  function parseSemver(version) {
49826
49888
  const trimmed = version?.trim();