@prisma/cli 8.0.0-rc.2 → 8.0.0-rc.2-dev.49

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 (2) hide show
  1. package/dist/cli.js +334 -39
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -889,7 +889,7 @@ function findTokensForWorkspace(allCredentials, workspaceId) {
889
889
  function tokensEqual(a, b) {
890
890
  return a?.workspaceId === b?.workspaceId && a?.accessToken === b?.accessToken && a?.refreshToken === b?.refreshToken;
891
891
  }
892
- function sleep$1(ms, signal) {
892
+ function sleep$2(ms, signal) {
893
893
  signal?.throwIfAborted();
894
894
  return new Promise((resolve, reject) => {
895
895
  const onAbort = () => {
@@ -1106,7 +1106,7 @@ var FileTokenStorage = class {
1106
1106
  if (await this.tryCreateRefreshLock(lockId)) return lockId;
1107
1107
  if (await this.releaseStaleRefreshLock()) continue;
1108
1108
  this.throwIfRefreshLockWaitTimedOut(startedAt, waitTimeoutMs);
1109
- await sleep$1(retryMs, this.signal);
1109
+ await sleep$2(retryMs, this.signal);
1110
1110
  }
1111
1111
  }
1112
1112
  async tryCreateRefreshLock(lockId) {
@@ -4657,8 +4657,45 @@ const bucketListCommand = defineCommand({
4657
4657
  }
4658
4658
  });
4659
4659
  //#endregion
4660
+ //#region src/lib/ndjson.ts
4661
+ /**
4662
+ * Reads a newline-delimited JSON body line by line.
4663
+ *
4664
+ * Shared by every command that reads an NDJSON log page, so the stream
4665
+ * handling below is written and tested once. The two subtleties are the
4666
+ * reason: a chunk boundary can fall inside a line, and a body can end
4667
+ * without a trailing newline, so the last record arrives only if the
4668
+ * leftover buffer is flushed at `done`.
4669
+ */
4670
+ async function forEachNdjsonRecord(body, onRecord) {
4671
+ const reader = body.getReader();
4672
+ const decoder = new TextDecoder();
4673
+ let buffer = "";
4674
+ try {
4675
+ for (;;) {
4676
+ const { done, value } = await reader.read();
4677
+ if (value) buffer += decoder.decode(value, { stream: true });
4678
+ let newlineIndex = buffer.indexOf("\n");
4679
+ while (newlineIndex !== -1) {
4680
+ const line = buffer.slice(0, newlineIndex).trim();
4681
+ buffer = buffer.slice(newlineIndex + 1);
4682
+ if (line) onRecord(JSON.parse(line));
4683
+ newlineIndex = buffer.indexOf("\n");
4684
+ }
4685
+ if (done) {
4686
+ const tail = buffer.trim();
4687
+ if (tail) onRecord(JSON.parse(tail));
4688
+ return;
4689
+ }
4690
+ }
4691
+ } finally {
4692
+ await reader.cancel().catch(() => void 0);
4693
+ reader.releaseLock();
4694
+ }
4695
+ }
4696
+ //#endregion
4660
4697
  //#region src/commands/build/logs.ts
4661
- const TRAILING_NEWLINE = /\n$/;
4698
+ const TRAILING_NEWLINE$1 = /\n$/;
4662
4699
  function buildNotFoundError(buildId) {
4663
4700
  return new CliStructuredError("BUILD.NOT_FOUND", `Build ${buildId} was not found`, {
4664
4701
  why: "The build does not exist, or your workspace does not have access to it.",
@@ -4706,40 +4743,13 @@ function buildFailedError(buildId, record) {
4706
4743
  }] : []]
4707
4744
  });
4708
4745
  }
4709
- /** Reads a newline-delimited JSON body line by line. */
4710
- async function forEachNdjsonRecord(body, onRecord) {
4711
- const reader = body.getReader();
4712
- const decoder = new TextDecoder();
4713
- let buffer = "";
4714
- try {
4715
- for (;;) {
4716
- const { done, value } = await reader.read();
4717
- if (value) buffer += decoder.decode(value, { stream: true });
4718
- let newlineIndex = buffer.indexOf("\n");
4719
- while (newlineIndex !== -1) {
4720
- const line = buffer.slice(0, newlineIndex).trim();
4721
- buffer = buffer.slice(newlineIndex + 1);
4722
- if (line) onRecord(JSON.parse(line));
4723
- newlineIndex = buffer.indexOf("\n");
4724
- }
4725
- if (done) {
4726
- const tail = buffer.trim();
4727
- if (tail) onRecord(JSON.parse(tail));
4728
- return;
4729
- }
4730
- }
4731
- } finally {
4732
- await reader.cancel().catch(() => void 0);
4733
- reader.releaseLock();
4734
- }
4735
- }
4736
4746
  function reportRecord(ctx, record) {
4737
4747
  if (record.type === "log") {
4738
4748
  ctx.report({
4739
4749
  kind: "output",
4740
4750
  source: "build",
4741
4751
  channel: record.source === "stderr" || record.level === "error" ? "diagnostic" : "data",
4742
- line: record.text.replace(TRAILING_NEWLINE, ""),
4752
+ line: record.text.replace(TRAILING_NEWLINE$1, ""),
4743
4753
  data: {
4744
4754
  cursor: record.cursor,
4745
4755
  level: record.level,
@@ -11257,6 +11267,30 @@ function deploymentNotFoundError(deploymentId) {
11257
11267
  nextActions: [runCommandAction("Choose an available deployment id", "service deployment list")]
11258
11268
  });
11259
11269
  }
11270
+ /** `--tail` and `--from-start` ask for opposite ends of the log, so a
11271
+ * run naming both has no answer to give. Refused before any work. */
11272
+ function logsRangeConflictError() {
11273
+ return new CliStructuredError("SERVICE.LOGS_RANGE_CONFLICT", "Choose one end of the log to read from", {
11274
+ why: "--tail and --from-start are mutually exclusive: one reads the last lines, the other reads from the beginning.",
11275
+ nextActions: [adviceAction("Pass --tail <n> for the last n lines, or --from-start for the whole log.")]
11276
+ });
11277
+ }
11278
+ /** A deployment id resolves globally, so one that exists but belongs to
11279
+ * another project is its own failure — not "not found". */
11280
+ function deploymentOutsideProjectError(deploymentId) {
11281
+ return new CliStructuredError("SERVICE.DEPLOYMENT_OUTSIDE_PROJECT", `Deployment "${deploymentId}" belongs to another project`, {
11282
+ why: "The deployment exists, but the service that owns it is not in the resolved project.",
11283
+ nextActions: [adviceAction("Pass --project for the project that owns it."), runCommandAction("List services", "service list")]
11284
+ });
11285
+ }
11286
+ /** The deployment exists but names no owning service, so there is no
11287
+ * project to check it against and nothing to scope logs by. */
11288
+ function deploymentDetachedError(deploymentId) {
11289
+ return new CliStructuredError("SERVICE.DEPLOYMENT_DETACHED", `Deployment "${deploymentId}" has no owning service`, {
11290
+ why: "The Management API returned the deployment without a service, so it cannot be scoped to a project.",
11291
+ nextActions: [runCommandAction("Show the deployment", `service deployment show ${deploymentId}`)]
11292
+ });
11293
+ }
11260
11294
  function deploymentNotFoundForServiceError(deploymentId, serviceName) {
11261
11295
  return new CliStructuredError("SERVICE.DEPLOYMENT_NOT_FOUND", `Deployment "${deploymentId}" not found for service "${serviceName}"`, {
11262
11296
  why: "The requested deployment does not belong to the resolved service or is no longer available.",
@@ -12414,12 +12448,13 @@ async function resolveServiceReadState(ctx, options) {
12414
12448
  });
12415
12449
  const projectId = target.project.id;
12416
12450
  const stateStore = await openServiceStateStore(ctx);
12451
+ const services = await listServices(ctx, provider, projectId, target.branch.name);
12417
12452
  return {
12418
12453
  provider,
12419
12454
  stateStore,
12420
12455
  target,
12421
12456
  projectId,
12422
- selected: await resolveExistingServiceSelection(ctx, stateStore, projectId, await listServices(ctx, provider, projectId, target.branch.name), options.serviceName ?? compute.configServiceName)
12457
+ selected: options.skipSelection ? null : await resolveExistingServiceSelection(ctx, stateStore, projectId, services, options.serviceName ?? compute.configServiceName)
12423
12458
  };
12424
12459
  }
12425
12460
  async function resolveServiceDomainTarget(ctx, options) {
@@ -13325,7 +13360,7 @@ const serviceDomainShowCommand = defineCommand({
13325
13360
  //#endregion
13326
13361
  //#region src/commands/service/domain-wait.ts
13327
13362
  const DEFAULT_TIMEOUT_MS = 900 * 1e3;
13328
- const DEFAULT_POLL_INTERVAL_MS = 5e3;
13363
+ const DEFAULT_POLL_INTERVAL_MS$1 = 5e3;
13329
13364
  const UNIT_MULTIPLIER_MS = {
13330
13365
  ms: 1,
13331
13366
  s: 1e3,
@@ -13340,13 +13375,13 @@ function parseWaitTimeout(value) {
13340
13375
  if (!match) throw timeoutInvalidError(value);
13341
13376
  return Number.parseInt(match[1], 10) * (UNIT_MULTIPLIER_MS[match[2]] ?? 1);
13342
13377
  }
13343
- function pollIntervalMs(ctx) {
13378
+ function pollIntervalMs$1(ctx) {
13344
13379
  const raw = ctx.env.PRISMA_CLI_DOMAIN_WAIT_POLL_MS;
13345
- if (!raw) return DEFAULT_POLL_INTERVAL_MS;
13380
+ if (!raw) return DEFAULT_POLL_INTERVAL_MS$1;
13346
13381
  const parsed = Number.parseInt(raw, 10);
13347
- return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_POLL_INTERVAL_MS;
13382
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_POLL_INTERVAL_MS$1;
13348
13383
  }
13349
- async function sleep(milliseconds, signal) {
13384
+ async function sleep$1(milliseconds, signal) {
13350
13385
  if (milliseconds <= 0) return;
13351
13386
  signal.throwIfAborted();
13352
13387
  await new Promise((resolve, reject) => {
@@ -13392,7 +13427,7 @@ const serviceDomainWaitCommand = defineCommand({
13392
13427
  const domain = await resolveDomainByHostname(target.provider, target.service.id, hostname, "wait", ctx.signal);
13393
13428
  const start = Date.now();
13394
13429
  const deadline = start + timeoutMs;
13395
- const interval = pollIntervalMs(ctx);
13430
+ const interval = pollIntervalMs$1(ctx);
13396
13431
  let previousStatus = null;
13397
13432
  let current = domain;
13398
13433
  for (;;) {
@@ -13418,7 +13453,7 @@ const serviceDomainWaitCommand = defineCommand({
13418
13453
  }
13419
13454
  if (current.status === "failed") throw domainVerificationFailedError(hostname, current);
13420
13455
  if (timeoutMs === 0 || Date.now() >= deadline) throw domainVerificationTimeoutError(hostname, current.status);
13421
- await sleep(Math.min(interval, Math.max(deadline - Date.now(), 0)), ctx.signal);
13456
+ await sleep$1(Math.min(interval, Math.max(deadline - Date.now(), 0)), ctx.signal);
13422
13457
  current = await target.provider.showDomain(current.id, { signal: ctx.signal }).catch((error) => {
13423
13458
  throw domainCommandError("wait", error, hostname);
13424
13459
  });
@@ -13461,6 +13496,264 @@ const serviceListCommand = defineCommand({
13461
13496
  }
13462
13497
  });
13463
13498
  //#endregion
13499
+ //#region src/commands/service/logs.ts
13500
+ const TRAILING_NEWLINE = /\n$/;
13501
+ /** The endpoint's own default page size, restated so `--tail` and the
13502
+ * unflagged run send the same shape of request. */
13503
+ const DEFAULT_TAIL = 100;
13504
+ /** Contract: poll every 2s in --follow. Overridable so a test drives the
13505
+ * loop without waiting, the way `service domain wait` does. */
13506
+ const DEFAULT_POLL_INTERVAL_MS = 2e3;
13507
+ function pollIntervalMs(ctx) {
13508
+ const raw = ctx.env.PRISMA_CLI_SERVICE_LOGS_POLL_MS;
13509
+ if (!raw) return DEFAULT_POLL_INTERVAL_MS;
13510
+ const parsed = Number.parseInt(raw, 10);
13511
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : DEFAULT_POLL_INTERVAL_MS;
13512
+ }
13513
+ async function sleep(milliseconds, signal) {
13514
+ if (milliseconds <= 0) {
13515
+ signal.throwIfAborted();
13516
+ return;
13517
+ }
13518
+ signal.throwIfAborted();
13519
+ await new Promise((resolve, reject) => {
13520
+ const onAbort = () => {
13521
+ clearTimeout(timeout);
13522
+ reject(signal.reason);
13523
+ };
13524
+ const timeout = setTimeout(() => {
13525
+ signal.removeEventListener("abort", onAbort);
13526
+ resolve();
13527
+ }, milliseconds);
13528
+ signal.addEventListener("abort", onAbort, { once: true });
13529
+ });
13530
+ }
13531
+ function logsFailedError(deploymentId, status) {
13532
+ return new CliStructuredError("SERVICE.LOGS_FAILED", `Failed to read logs for deployment ${deploymentId}`, {
13533
+ why: `The Management API returned HTTP ${status}.`,
13534
+ meta: { status },
13535
+ nextActions: [adviceAction("Retry the command, or rerun with --log-level verbose for more detail."), runCommandAction("Show the deployment", `service deployment show ${deploymentId}`)]
13536
+ });
13537
+ }
13538
+ /**
13539
+ * The body ended mid-page, without the terminal record that closes one.
13540
+ * Distinct from SERVICE.LOGS_NO_CURSOR, which is a page that closed
13541
+ * properly and said there is nothing to resume from: this one is an
13542
+ * incomplete read, and the lines already printed are not the whole page.
13543
+ */
13544
+ function logsIncompleteError(deploymentId) {
13545
+ return new CliStructuredError("SERVICE.LOGS_INCOMPLETE", `Incomplete log page for deployment ${deploymentId}`, {
13546
+ why: "The response ended without the record that closes a page, so the lines shown may be only part of it.",
13547
+ nextActions: [adviceAction("Rerun the command to read the page again.")]
13548
+ });
13549
+ }
13550
+ /** An error terminal record is the platform reporting that the log read
13551
+ * itself failed, so it settles the run rather than printing. */
13552
+ function logStreamFailedError(deploymentId, record) {
13553
+ return new CliStructuredError("SERVICE.LOGS_FAILED", `Log stream failed for deployment ${deploymentId}`, {
13554
+ why: record.message,
13555
+ meta: {
13556
+ code: record.code,
13557
+ retryable: record.retryable,
13558
+ ...record.cursor === null ? {} : { cursor: record.cursor }
13559
+ },
13560
+ nextActions: [runCommandAction("Show the deployment", `service deployment show ${deploymentId}`)]
13561
+ });
13562
+ }
13563
+ function listDeployments(ctx, provider, serviceId) {
13564
+ return provider.listDeployments(serviceId, { signal: ctx.signal }).catch((error) => {
13565
+ throw deployFailedError("Failed to list service deployments", error, [runCommandAction("List deployments", "service deployment list")]);
13566
+ });
13567
+ }
13568
+ /** `--deployment <id>`: the id is global, so it is resolved directly and
13569
+ * then checked against the resolved project — a deployment that exists
13570
+ * but belongs elsewhere is reported as its own failure. */
13571
+ async function resolveExplicitDeployment(ctx, state, serviceName, deploymentId) {
13572
+ if (serviceName) {
13573
+ if (!state.selected) throw noDeploymentsError("No deployments available to read logs from", "The resolved project does not have any deployed service yet.");
13574
+ const deploymentsResult = await listDeployments(ctx, state.provider, state.selected.id);
13575
+ const deployment = requireDeploymentForService(deploymentsResult.deployments, deploymentId, state.selected.name);
13576
+ await rememberSelectedService(state.stateStore, state.projectId, deploymentsResult.app);
13577
+ return {
13578
+ service: deploymentsResult.app,
13579
+ deployment
13580
+ };
13581
+ }
13582
+ const shown = await state.provider.showDeployment(deploymentId, { signal: ctx.signal }).catch((error) => {
13583
+ throw deployFailedError("Failed to show deployment", error, [runCommandAction("List deployments", "service deployment list")]);
13584
+ });
13585
+ if (!shown) throw deploymentNotFoundError(deploymentId);
13586
+ if (!shown.app) throw deploymentDetachedError(deploymentId);
13587
+ const owning = (await listServices(ctx, state.provider, state.projectId, state.target.branch.name)).find((service) => service.id === shown.app?.id);
13588
+ if (!owning) throw deploymentOutsideProjectError(deploymentId);
13589
+ await rememberSelectedService(state.stateStore, state.projectId, owning);
13590
+ return {
13591
+ service: owning,
13592
+ deployment: shown.deployment
13593
+ };
13594
+ }
13595
+ /** No `--deployment`: read whatever is live for the selected service. */
13596
+ async function resolveLiveDeployment(ctx, state) {
13597
+ if (!state.selected) throw noDeploymentsError("No deployments available to read logs from", "The resolved project does not have any deployed service yet.");
13598
+ const deploymentsResult = await listDeployments(ctx, state.provider, state.selected.id);
13599
+ const currentLiveDeploymentId = resolveCurrentLiveDeploymentId(deploymentsResult.app, deploymentsResult.deployments);
13600
+ const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId);
13601
+ const deployment = currentLiveDeploymentId ? deployments.find((candidate) => candidate.id === currentLiveDeploymentId) ?? null : null;
13602
+ await rememberSelectedService(state.stateStore, state.projectId, deploymentsResult.app);
13603
+ if (!deployment) throw noDeploymentsError("No deployments available to read logs from", `The selected service "${deploymentsResult.app.name}" does not have a live deployment.`);
13604
+ return {
13605
+ service: deploymentsResult.app,
13606
+ deployment
13607
+ };
13608
+ }
13609
+ /**
13610
+ * Reads one page and reports its log records. Returns the terminal
13611
+ * record that closed it — the caller decides whether that ends the run
13612
+ * or starts the next page.
13613
+ *
13614
+ * Every page ends with a terminal record, so a body that stops without
13615
+ * one was truncated. The lines that did arrive have already been
13616
+ * reported, but the run must not settle as though it had read the whole
13617
+ * page: the user would have a partial log and no way to tell.
13618
+ */
13619
+ async function readPage(ctx, deploymentId, query) {
13620
+ const { data, response } = await ctx.api.GET("/v1/deployments/{deploymentId}/logs", {
13621
+ params: {
13622
+ path: { deploymentId },
13623
+ query
13624
+ },
13625
+ parseAs: "stream",
13626
+ signal: ctx.signal
13627
+ });
13628
+ const body = data;
13629
+ if (!response.ok || !body) {
13630
+ await body?.cancel().catch(() => void 0);
13631
+ throw response.status === 404 ? deploymentNotFoundError(deploymentId) : logsFailedError(deploymentId, response.status);
13632
+ }
13633
+ let terminal = null;
13634
+ await forEachNdjsonRecord(body, (record) => {
13635
+ if (record.type === "terminal") {
13636
+ terminal = record;
13637
+ return;
13638
+ }
13639
+ ctx.report({
13640
+ kind: "output",
13641
+ source: "logs",
13642
+ channel: "data",
13643
+ line: record.text.replace(TRAILING_NEWLINE, ""),
13644
+ data: {
13645
+ byteStart: record.byteStart,
13646
+ byteEnd: record.byteEnd
13647
+ }
13648
+ });
13649
+ });
13650
+ if (terminal === null) throw logsIncompleteError(deploymentId);
13651
+ return terminal;
13652
+ }
13653
+ /**
13654
+ * Following needs somewhere to resume from. Without a cursor the next
13655
+ * request would carry no range at all, the endpoint would apply its
13656
+ * default tail, and the same lines would print again every interval —
13657
+ * silent duplication the user cannot act on. So the run stops and says
13658
+ * why. It settles as an error rather than a clean end because `--follow`
13659
+ * has no successful ending: it runs until interrupted (130) or fails,
13660
+ * and an exit 0 here would be a novel outcome meaning "gave up".
13661
+ */
13662
+ function requireResumeCursor(deploymentId, cursor) {
13663
+ if (cursor === null) throw new CliStructuredError("SERVICE.LOGS_NO_CURSOR", `Cannot follow logs for deployment ${deploymentId}`, {
13664
+ why: "The log page ended without a resume cursor, so there is no point to continue reading from.",
13665
+ nextActions: [adviceAction("Rerun without --follow to read the page, or retry if the deployment is still starting.")]
13666
+ });
13667
+ return cursor;
13668
+ }
13669
+ /**
13670
+ * `--follow`: wait the poll interval, read the next page from the cursor
13671
+ * the last one ended on, repeat until the user interrupts. Never
13672
+ * returns — the run ends by abort (the engine settles 130) or by throw.
13673
+ */
13674
+ async function followPages(ctx, deploymentId, startCursor) {
13675
+ const interval = pollIntervalMs(ctx);
13676
+ let cursor = requireResumeCursor(deploymentId, startCursor);
13677
+ let retriedAfterError = false;
13678
+ for (;;) {
13679
+ await sleep(interval, ctx.signal);
13680
+ const next = await readPage(ctx, deploymentId, { cursor });
13681
+ if (next.kind === "error") {
13682
+ if (!next.retryable || retriedAfterError) throw logStreamFailedError(deploymentId, next);
13683
+ retriedAfterError = true;
13684
+ continue;
13685
+ }
13686
+ retriedAfterError = false;
13687
+ cursor = requireResumeCursor(deploymentId, next.cursor);
13688
+ }
13689
+ }
13690
+ const serviceLogsCommand = defineSessionCommand({
13691
+ help: {
13692
+ summary: "Read logs for a deployment of the service",
13693
+ examples: [
13694
+ "service logs",
13695
+ "service logs --tail 500",
13696
+ "service logs --follow",
13697
+ "service logs --deployment dep_123 --from-start"
13698
+ ]
13699
+ },
13700
+ args: {
13701
+ flags: {
13702
+ service: flag.string({
13703
+ brief: "Service name",
13704
+ placeholder: "name"
13705
+ }),
13706
+ project: flag.string({
13707
+ brief: "Project id or name",
13708
+ placeholder: "id-or-name"
13709
+ }),
13710
+ deployment: flag.string({
13711
+ brief: "Deployment id to read (default: the live deployment)",
13712
+ placeholder: "id"
13713
+ }),
13714
+ tail: flag.number({
13715
+ brief: `Read the last N lines (default ${DEFAULT_TAIL})`,
13716
+ placeholder: "n"
13717
+ }),
13718
+ fromStart: flag.boolean({ brief: "Read from the beginning instead of the last lines" }),
13719
+ follow: flag.boolean({ brief: "Keep polling for new lines until interrupted" })
13720
+ },
13721
+ positionals: { service: positional.optionalString({
13722
+ brief: "Service target from prisma.compute.ts when the config defines multiple services",
13723
+ placeholder: "service"
13724
+ }) }
13725
+ },
13726
+ needs: { credentials: true },
13727
+ handler: async (args, ctx) => {
13728
+ if (args.flags.fromStart && args.flags.tail !== void 0) throw logsRangeConflictError();
13729
+ const serviceNamed = args.flags.service ?? args.positionals.service;
13730
+ const resolveGlobally = Boolean(args.flags.deployment) && !serviceNamed;
13731
+ const state = await resolveServiceReadState(ctx, {
13732
+ ...args.flags.service !== void 0 ? { serviceName: args.flags.service } : {},
13733
+ ...args.flags.project !== void 0 ? { projectRef: args.flags.project } : {},
13734
+ ...args.positionals.service !== void 0 ? { configTarget: args.positionals.service } : {},
13735
+ commandName: "service logs",
13736
+ skipSelection: resolveGlobally
13737
+ });
13738
+ const target = args.flags.deployment ? await resolveExplicitDeployment(ctx, state, serviceNamed, args.flags.deployment) : await resolveLiveDeployment(ctx, state);
13739
+ const deploymentId = target.deployment.id;
13740
+ for (const line of [
13741
+ `project: ${state.projectId}`,
13742
+ `service: ${target.service.name}`,
13743
+ `deployment: ${deploymentId}`
13744
+ ]) ctx.report({
13745
+ kind: "output",
13746
+ source: "logs",
13747
+ channel: "diagnostic",
13748
+ line
13749
+ });
13750
+ const terminal = await readPage(ctx, deploymentId, args.flags.fromStart ? { from_start: "true" } : { tail: args.flags.tail ?? DEFAULT_TAIL });
13751
+ if (terminal.kind === "error") throw logStreamFailedError(deploymentId, terminal);
13752
+ if (!args.flags.follow) return ok(void 0);
13753
+ return followPages(ctx, deploymentId, terminal.cursor);
13754
+ }
13755
+ });
13756
+ //#endregion
13464
13757
  //#region src/commands/service/open.ts
13465
13758
  const serviceOpenCommand = defineCommand({
13466
13759
  help: {
@@ -13714,6 +14007,7 @@ const platformCommandFamily = defineCommandFamily({ commands: {
13714
14007
  gitConnect: gitConnectCommand,
13715
14008
  gitDisconnect: gitDisconnectCommand,
13716
14009
  serviceList: serviceListCommand,
14010
+ serviceLogs: serviceLogsCommand,
13717
14011
  serviceCreate: serviceCreateCommand,
13718
14012
  serviceShow: serviceShowCommand,
13719
14013
  serviceOpen: serviceOpenCommand,
@@ -13816,6 +14110,7 @@ const mountedCommands = {
13816
14110
  "git connect": gitConnectCommand,
13817
14111
  "git disconnect": gitDisconnectCommand,
13818
14112
  "service list": serviceListCommand,
14113
+ "service logs": serviceLogsCommand,
13819
14114
  "service create": serviceCreateCommand,
13820
14115
  "service show": serviceShowCommand,
13821
14116
  "service open": serviceOpenCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "8.0.0-rc.2",
3
+ "version": "8.0.0-rc.2-dev.49",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "license": "Apache-2.0",
40
40
  "dependencies": {
41
- "@prisma/cli-engine": "8.0.0-rc.2",
41
+ "@prisma/cli-engine": "0.1.1",
42
42
  "@prisma/composer": "0.6.0-dev.16",
43
43
  "@prisma/compute-sdk": "0.39.0",
44
44
  "@prisma/credentials-store": "^7.8.0",
@@ -51,9 +51,9 @@
51
51
  "open": "^11.0.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@repo/cli-conformance": "8.0.0-rc.2",
55
- "@repo/cli-telemetry": "8.0.0-rc.2",
56
- "@repo/tsconfig": "8.0.0-rc.2",
54
+ "@repo/cli-conformance": "8.0.0-rc.2-dev.49",
55
+ "@repo/cli-telemetry": "8.0.0-rc.2-dev.49",
56
+ "@repo/tsconfig": "8.0.0-rc.2-dev.49",
57
57
  "@types/node": "^22.19.19",
58
58
  "tsdown": "^0.21.10",
59
59
  "tsx": "^4.22.4",