@specific.dev/cli 0.1.119 → 0.1.120

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 (67) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +1 -1
  4. package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
  5. package/dist/admin/__next._full.txt +1 -1
  6. package/dist/admin/__next._head.txt +1 -1
  7. package/dist/admin/__next._index.txt +1 -1
  8. package/dist/admin/__next._tree.txt +1 -1
  9. package/dist/admin/_not-found/__next._full.txt +1 -1
  10. package/dist/admin/_not-found/__next._head.txt +1 -1
  11. package/dist/admin/_not-found/__next._index.txt +1 -1
  12. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  13. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  14. package/dist/admin/_not-found/__next._tree.txt +1 -1
  15. package/dist/admin/_not-found/index.html +1 -1
  16. package/dist/admin/_not-found/index.txt +1 -1
  17. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
  18. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
  19. package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
  20. package/dist/admin/databases/__next._full.txt +1 -1
  21. package/dist/admin/databases/__next._head.txt +1 -1
  22. package/dist/admin/databases/__next._index.txt +1 -1
  23. package/dist/admin/databases/__next._tree.txt +1 -1
  24. package/dist/admin/databases/index.html +1 -1
  25. package/dist/admin/databases/index.txt +1 -1
  26. package/dist/admin/fullscreen/__next._full.txt +1 -1
  27. package/dist/admin/fullscreen/__next._head.txt +1 -1
  28. package/dist/admin/fullscreen/__next._index.txt +1 -1
  29. package/dist/admin/fullscreen/__next._tree.txt +1 -1
  30. package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
  31. package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
  32. package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
  33. package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
  34. package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
  35. package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
  36. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
  37. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
  38. package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
  39. package/dist/admin/fullscreen/databases/index.html +1 -1
  40. package/dist/admin/fullscreen/databases/index.txt +1 -1
  41. package/dist/admin/fullscreen/index.html +1 -1
  42. package/dist/admin/fullscreen/index.txt +1 -1
  43. package/dist/admin/index.html +1 -1
  44. package/dist/admin/index.txt +1 -1
  45. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +1 -1
  46. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
  47. package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +1 -1
  48. package/dist/admin/mail/__next._full.txt +1 -1
  49. package/dist/admin/mail/__next._head.txt +1 -1
  50. package/dist/admin/mail/__next._index.txt +1 -1
  51. package/dist/admin/mail/__next._tree.txt +1 -1
  52. package/dist/admin/mail/index.html +1 -1
  53. package/dist/admin/mail/index.txt +1 -1
  54. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +1 -1
  55. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +1 -1
  56. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
  57. package/dist/admin/workflows/__next._full.txt +1 -1
  58. package/dist/admin/workflows/__next._head.txt +1 -1
  59. package/dist/admin/workflows/__next._index.txt +1 -1
  60. package/dist/admin/workflows/__next._tree.txt +1 -1
  61. package/dist/admin/workflows/index.html +1 -1
  62. package/dist/admin/workflows/index.txt +1 -1
  63. package/dist/cli.js +236 -22
  64. package/package.json +1 -1
  65. /package/dist/admin/_next/static/{Jq_9GUFfnF8eZkPZMF_EC → wSllkH7WRyJEpQx0UpLjo}/_buildManifest.js +0 -0
  66. /package/dist/admin/_next/static/{Jq_9GUFfnF8eZkPZMF_EC → wSllkH7WRyJEpQx0UpLjo}/_clientMiddlewareManifest.json +0 -0
  67. /package/dist/admin/_next/static/{Jq_9GUFfnF8eZkPZMF_EC → wSllkH7WRyJEpQx0UpLjo}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -372759,6 +372759,7 @@ import * as crypto3 from "crypto";
372759
372759
  import * as fs3 from "fs";
372760
372760
  import * as path3 from "path";
372761
372761
  var PROJECT_ID_FILE = ".specific/project_id";
372762
+ var ENVIRONMENT_ID_FILE = ".specific/environment_id";
372762
372763
  var ProjectNotLinkedError = class extends Error {
372763
372764
  constructor() {
372764
372765
  super(
@@ -372793,6 +372794,28 @@ function writeProjectId(projectId, projectDir = process.cwd()) {
372793
372794
  }
372794
372795
  fs3.writeFileSync(path3.join(specificDir, "project_id"), projectId + "\n");
372795
372796
  }
372797
+ function readEnvironmentId(projectDir = process.cwd()) {
372798
+ const envIdPath = path3.join(projectDir, ENVIRONMENT_ID_FILE);
372799
+ if (!fs3.existsSync(envIdPath)) {
372800
+ throw new Error(`${ENVIRONMENT_ID_FILE} does not exist`);
372801
+ }
372802
+ const envId = fs3.readFileSync(envIdPath, "utf-8").trim();
372803
+ if (!envId) {
372804
+ throw new Error(`${ENVIRONMENT_ID_FILE} is empty`);
372805
+ }
372806
+ return envId;
372807
+ }
372808
+ function hasEnvironmentId(projectDir = process.cwd()) {
372809
+ const envIdPath = path3.join(projectDir, ENVIRONMENT_ID_FILE);
372810
+ return fs3.existsSync(envIdPath);
372811
+ }
372812
+ function writeEnvironmentId(environmentId, projectDir = process.cwd()) {
372813
+ const specificDir = path3.join(projectDir, ".specific");
372814
+ if (!fs3.existsSync(specificDir)) {
372815
+ fs3.mkdirSync(specificDir, { recursive: true });
372816
+ }
372817
+ fs3.writeFileSync(path3.join(specificDir, "environment_id"), environmentId + "\n");
372818
+ }
372796
372819
 
372797
372820
  // src/lib/auth/credentials.ts
372798
372821
  import * as fs19 from "fs";
@@ -373233,7 +373256,7 @@ function trackEvent(event, properties) {
373233
373256
  event,
373234
373257
  properties: {
373235
373258
  ...properties,
373236
- cli_version: "0.1.119",
373259
+ cli_version: "0.1.120",
373237
373260
  platform: process.platform,
373238
373261
  node_version: process.version,
373239
373262
  project_id: getProjectId()
@@ -375208,6 +375231,28 @@ function NameInput({ onSubmit, onCancel }) {
375208
375231
  });
375209
375232
  return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Create new project"), /* @__PURE__ */ React7.createElement(Text7, null, "Enter project name:"), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, "> "), /* @__PURE__ */ React7.createElement(Text7, null, value), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "|")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Press Enter to create, Esc to go back"));
375210
375233
  }
375234
+ function EnvironmentSelector({
375235
+ environments,
375236
+ selectedIndex,
375237
+ onSelect,
375238
+ onUp,
375239
+ onDown
375240
+ }) {
375241
+ useInput4((input, key) => {
375242
+ if (key.return) {
375243
+ const env2 = environments[selectedIndex];
375244
+ if (env2) onSelect(env2);
375245
+ } else if (key.upArrow) {
375246
+ onUp();
375247
+ } else if (key.downArrow) {
375248
+ onDown();
375249
+ }
375250
+ });
375251
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Select an environment to deploy to:"), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, environments.map((env2, index) => {
375252
+ const isSelected = index === selectedIndex;
375253
+ return /* @__PURE__ */ React7.createElement(Text7, { key: env2.id }, isSelected ? /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, " ", "> ") : /* @__PURE__ */ React7.createElement(Text7, null, " "), /* @__PURE__ */ React7.createElement(Text7, null, env2.name));
375254
+ })), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Use arrow keys to navigate, Enter to select"));
375255
+ }
375211
375256
  function getMissingSecrets(pendingActions) {
375212
375257
  if (!pendingActions) return [];
375213
375258
  const secretAction = pendingActions.find((a) => a.type === "missing_secrets");
@@ -375244,7 +375289,7 @@ function formatErrorCode(code) {
375244
375289
  function StructuredError({ error }) {
375245
375290
  return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "red", bold: true }, formatErrorCode(error.code), ": ", error.message), error.resource && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Resource: ", error.resource), error.output && /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Output:"), /* @__PURE__ */ React7.createElement(Text7, null, error.output)));
375246
375291
  }
375247
- function DeployUI({ environment, config }) {
375292
+ function DeployUI({ envFlag, config }) {
375248
375293
  const { exit } = useApp3();
375249
375294
  const [state, setState] = useState6({ phase: "checking-auth" });
375250
375295
  const clientRef = React7.useRef(null);
@@ -375254,10 +375299,7 @@ function DeployUI({ environment, config }) {
375254
375299
  if (hasProjectId(projectDir)) {
375255
375300
  const projectId = readProjectId(projectDir);
375256
375301
  if (isLoggedIn()) {
375257
- setState({
375258
- phase: "creating-tarball",
375259
- projectId
375260
- });
375302
+ setState({ phase: "loading-environments", projectId });
375261
375303
  } else {
375262
375304
  setState({ phase: "logging-in", projectId });
375263
375305
  }
@@ -375312,7 +375354,7 @@ function DeployUI({ environment, config }) {
375312
375354
  const successResponse = response;
375313
375355
  await saveCredentialsFromToken(successResponse);
375314
375356
  setState(
375315
- (s) => s.projectId ? { phase: "creating-tarball", projectId: s.projectId } : { phase: "loading-projects" }
375357
+ (s) => s.projectId ? { phase: "loading-environments", projectId: s.projectId } : { phase: "loading-projects" }
375316
375358
  );
375317
375359
  }
375318
375360
  };
@@ -375371,8 +375413,9 @@ function DeployUI({ environment, config }) {
375371
375413
  const proj = project;
375372
375414
  writeProjectId(proj.id);
375373
375415
  setState({
375374
- phase: "creating-tarball",
375375
- projectId: proj.id
375416
+ phase: "loading-environments",
375417
+ projectId: proj.id,
375418
+ environments: proj.environments
375376
375419
  });
375377
375420
  }
375378
375421
  },
@@ -375395,8 +375438,9 @@ function DeployUI({ environment, config }) {
375395
375438
  if (cancelled) return;
375396
375439
  writeProjectId(project.id);
375397
375440
  setState({
375398
- phase: "creating-tarball",
375399
- projectId: project.id
375441
+ phase: "loading-environments",
375442
+ projectId: project.id,
375443
+ environments: project.environments
375400
375444
  });
375401
375445
  } catch (err) {
375402
375446
  if (cancelled) return;
@@ -375411,6 +375455,101 @@ function DeployUI({ environment, config }) {
375411
375455
  cancelled = true;
375412
375456
  };
375413
375457
  }, [state.phase, state.newProjectName]);
375458
+ useEffect6(() => {
375459
+ if (state.phase !== "loading-environments" || !state.projectId) return;
375460
+ let cancelled = false;
375461
+ async function loadEnvironments() {
375462
+ try {
375463
+ let environments = state.environments;
375464
+ if (!environments) {
375465
+ const token = await getValidAccessToken();
375466
+ const client2 = new SpecificClient({ accessToken: token });
375467
+ const projects2 = await client2.listProjects();
375468
+ const project = projects2.find((p) => p.id === state.projectId);
375469
+ environments = project?.environments ?? [];
375470
+ }
375471
+ if (cancelled) return;
375472
+ if (environments.length === 0) {
375473
+ setState((s) => ({
375474
+ ...s,
375475
+ phase: "error",
375476
+ error: "No environments found for this project"
375477
+ }));
375478
+ return;
375479
+ }
375480
+ if (envFlag) {
375481
+ const match = environments.find((e) => e.name === envFlag);
375482
+ if (!match) {
375483
+ setState((s) => ({
375484
+ ...s,
375485
+ phase: "error",
375486
+ error: `Environment "${envFlag}" not found. Available: ${environments.map((e) => e.name).join(", ")}`
375487
+ }));
375488
+ return;
375489
+ }
375490
+ writeEnvironmentId(match.id);
375491
+ setState((s) => ({
375492
+ ...s,
375493
+ phase: "creating-tarball",
375494
+ environmentName: match.name,
375495
+ environments
375496
+ }));
375497
+ return;
375498
+ }
375499
+ const projectDir = process.cwd();
375500
+ if (hasEnvironmentId(projectDir)) {
375501
+ const savedEnvId = readEnvironmentId(projectDir);
375502
+ const match = environments.find((e) => e.id === savedEnvId);
375503
+ if (match) {
375504
+ setState((s) => ({
375505
+ ...s,
375506
+ phase: "creating-tarball",
375507
+ environmentName: match.name,
375508
+ environments
375509
+ }));
375510
+ return;
375511
+ }
375512
+ }
375513
+ if (environments.length === 1) {
375514
+ writeEnvironmentId(environments[0].id);
375515
+ setState((s) => ({
375516
+ ...s,
375517
+ phase: "creating-tarball",
375518
+ environmentName: environments[0].name,
375519
+ environments
375520
+ }));
375521
+ return;
375522
+ }
375523
+ setState((s) => ({
375524
+ ...s,
375525
+ phase: "selecting-environment",
375526
+ environments,
375527
+ environmentSelectedIndex: 0
375528
+ }));
375529
+ } catch (err) {
375530
+ if (cancelled) return;
375531
+ setState({
375532
+ phase: "error",
375533
+ error: err instanceof Error ? err.message : String(err)
375534
+ });
375535
+ }
375536
+ }
375537
+ loadEnvironments();
375538
+ return () => {
375539
+ cancelled = true;
375540
+ };
375541
+ }, [state.phase, state.projectId]);
375542
+ const handleEnvironmentSelect = useCallback3(
375543
+ (env2) => {
375544
+ writeEnvironmentId(env2.id);
375545
+ setState((s) => ({
375546
+ ...s,
375547
+ phase: "creating-tarball",
375548
+ environmentName: env2.name
375549
+ }));
375550
+ },
375551
+ []
375552
+ );
375414
375553
  const handleSecretSubmit = useCallback3((value) => {
375415
375554
  setState((s) => {
375416
375555
  if (!s.missingSecrets || s.currentSecretIndex === void 0) return s;
@@ -375555,12 +375694,13 @@ function DeployUI({ environment, config }) {
375555
375694
  }
375556
375695
  })();
375557
375696
  }, [state]);
375697
+ const environment = state.environmentName || "prod";
375558
375698
  useEffect6(() => {
375559
- if (state.phase !== "creating-tarball" || !state.projectId) return;
375699
+ if (state.phase !== "creating-tarball" || !state.projectId || !state.environmentName) return;
375560
375700
  let cancelled = false;
375561
375701
  async function runDeploy() {
375562
375702
  const projectDir = process.cwd();
375563
- writeLog("deploy", `Starting deployment to "${environment}"`);
375703
+ writeLog("deploy", `Starting deployment to "${state.environmentName}"`);
375564
375704
  writeLog("deploy", `Project directory: ${projectDir}`);
375565
375705
  const client2 = new SpecificClient({ tokenProvider: getValidAccessToken });
375566
375706
  clientRef.current = client2;
@@ -375592,7 +375732,7 @@ function DeployUI({ environment, config }) {
375592
375732
  try {
375593
375733
  writeLog("deploy", `Creating deployment for project ${state.projectId}`);
375594
375734
  const gitInfo = getGitInfo(projectDir);
375595
- deployment2 = await client2.createDeployment(state.projectId, environment, {
375735
+ deployment2 = await client2.createDeployment(state.projectId, state.environmentName, {
375596
375736
  triggeredBy: "cli",
375597
375737
  ...gitInfo && {
375598
375738
  gitCommitSha: gitInfo.commitSha,
@@ -375634,7 +375774,7 @@ function DeployUI({ environment, config }) {
375634
375774
  return () => {
375635
375775
  cancelled = true;
375636
375776
  };
375637
- }, [state.projectId, environment, config.builds]);
375777
+ }, [state.projectId, state.environmentName, config.builds]);
375638
375778
  useEffect6(() => {
375639
375779
  if (state.phase !== "pending" || !state.deployment) return;
375640
375780
  let pollInterval;
@@ -375902,6 +376042,30 @@ function DeployUI({ environment, config }) {
375902
376042
  if (phase === "creating-project") {
375903
376043
  return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Creating project..."));
375904
376044
  }
376045
+ if (phase === "loading-environments") {
376046
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading environments..."));
376047
+ }
376048
+ if (phase === "selecting-environment" && state.environments && state.environmentSelectedIndex !== void 0) {
376049
+ return /* @__PURE__ */ React7.createElement(
376050
+ EnvironmentSelector,
376051
+ {
376052
+ environments: state.environments,
376053
+ selectedIndex: state.environmentSelectedIndex,
376054
+ onSelect: handleEnvironmentSelect,
376055
+ onUp: () => setState((s) => ({
376056
+ ...s,
376057
+ environmentSelectedIndex: Math.max(0, (s.environmentSelectedIndex ?? 0) - 1)
376058
+ })),
376059
+ onDown: () => setState((s) => ({
376060
+ ...s,
376061
+ environmentSelectedIndex: Math.min(
376062
+ (s.environments?.length ?? 1) - 1,
376063
+ (s.environmentSelectedIndex ?? 0) + 1
376064
+ )
376065
+ }))
376066
+ }
376067
+ );
376068
+ }
375905
376069
  const currentSecret = missingSecrets && currentSecretIndex !== void 0 ? missingSecrets[currentSecretIndex] : void 0;
375906
376070
  const currentConfig = missingConfigs && currentConfigIndex !== void 0 ? missingConfigs[currentConfigIndex] : void 0;
375907
376071
  const getDisplayPhase = () => {
@@ -376007,6 +376171,52 @@ async function runDeployPipeline(options2) {
376007
376171
  } else if (!hasProjectId(projectDir)) {
376008
376172
  writeProjectId(projectId);
376009
376173
  }
376174
+ let environmentName;
376175
+ if (options2.env) {
376176
+ environmentName = options2.env;
376177
+ } else if (hasEnvironmentId(projectDir)) {
376178
+ const savedEnvId = readEnvironmentId(projectDir);
376179
+ const projects = await client2.listProjects();
376180
+ const project = projects.find((p) => p.id === projectId);
376181
+ const env2 = project?.environments.find((e) => e.id === savedEnvId);
376182
+ if (env2) {
376183
+ environmentName = env2.name;
376184
+ } else {
376185
+ const environments = project?.environments ?? [];
376186
+ if (environments.length === 1) {
376187
+ environmentName = environments[0].name;
376188
+ writeEnvironmentId(environments[0].id);
376189
+ } else if (environments.length === 0) {
376190
+ console.error("Error: No environments found for this project");
376191
+ process.exit(1);
376192
+ } else {
376193
+ console.error(
376194
+ `Error: Multiple environments available. Specify one with --env.
376195
+ Available: ${environments.map((e) => e.name).join(", ")}
376196
+ Example: specific deploy --env ${environments[0].name}`
376197
+ );
376198
+ process.exit(1);
376199
+ }
376200
+ }
376201
+ } else {
376202
+ const projects = await client2.listProjects();
376203
+ const project = projects.find((p) => p.id === projectId);
376204
+ const environments = project?.environments ?? [];
376205
+ if (environments.length === 1) {
376206
+ environmentName = environments[0].name;
376207
+ writeEnvironmentId(environments[0].id);
376208
+ } else if (environments.length === 0) {
376209
+ console.error("Error: No environments found for this project");
376210
+ process.exit(1);
376211
+ } else {
376212
+ console.error(
376213
+ `Error: Multiple environments available. Specify one with --env.
376214
+ Available: ${environments.map((e) => e.name).join(", ")}
376215
+ Example: specific deploy --env ${environments[0].name}`
376216
+ );
376217
+ process.exit(1);
376218
+ }
376219
+ }
376010
376220
  const parsedSecrets = {};
376011
376221
  if (options2.secrets) {
376012
376222
  for (const s of options2.secrets) {
@@ -376040,7 +376250,7 @@ async function runDeployPipeline(options2) {
376040
376250
  let deployment;
376041
376251
  try {
376042
376252
  const gitInfo = getGitInfo(projectDir);
376043
- deployment = await client2.createDeployment(projectId, "prod", {
376253
+ deployment = await client2.createDeployment(projectId, environmentName, {
376044
376254
  triggeredBy: "cli",
376045
376255
  ...gitInfo && {
376046
376256
  gitCommitSha: gitInfo.commitSha,
@@ -376187,6 +376397,7 @@ async function deployCommand(options2) {
376187
376397
  await runDeployPipeline({
376188
376398
  config,
376189
376399
  projectId: options2.project,
376400
+ env: options2.env,
376190
376401
  secrets: options2.secret,
376191
376402
  configs: options2.config
376192
376403
  });
@@ -376196,7 +376407,7 @@ async function deployCommand(options2) {
376196
376407
  /* @__PURE__ */ React7.createElement(
376197
376408
  DeployUI,
376198
376409
  {
376199
- environment: "prod",
376410
+ envFlag: options2.env,
376200
376411
  config
376201
376412
  }
376202
376413
  )
@@ -377057,7 +377268,7 @@ function compareVersions(a, b) {
377057
377268
  return 0;
377058
377269
  }
377059
377270
  async function checkForUpdate() {
377060
- const currentVersion = "0.1.119";
377271
+ const currentVersion = "0.1.120";
377061
377272
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
377062
377273
  if (!response.ok) {
377063
377274
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -377306,12 +377517,14 @@ async function projectListCommand() {
377306
377517
  if (!grouped.has(orgName)) {
377307
377518
  grouped.set(orgName, []);
377308
377519
  }
377309
- grouped.get(orgName).push({ id: project.id, name: project.name });
377520
+ grouped.get(orgName).push({ id: project.id, name: project.name, environments: project.environments });
377310
377521
  }
377311
377522
  for (const [orgName, orgProjects] of grouped) {
377312
377523
  console.log(`${orgName}:`);
377313
377524
  for (const project of orgProjects) {
377314
- console.log(` ${project.name} (${project.id})`);
377525
+ const envNames = project.environments.map((e) => e.name).join(", ");
377526
+ const envSuffix = envNames ? ` \u2014 ${envNames}` : "";
377527
+ console.log(` ${project.name} (${project.id})${envSuffix}`);
377315
377528
  }
377316
377529
  }
377317
377530
  } catch (error) {
@@ -377325,7 +377538,7 @@ async function projectListCommand() {
377325
377538
  var program = new Command();
377326
377539
  var env = "production";
377327
377540
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
377328
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.119").enablePositionalOptions();
377541
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.120").enablePositionalOptions();
377329
377542
  program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
377330
377543
  Examples:
377331
377544
  $ specific init
@@ -377346,9 +377559,10 @@ Examples:
377346
377559
  const key = options2.key ?? getDefaultKey();
377347
377560
  devCommand(key, options2.tunnel ?? false);
377348
377561
  });
377349
- program.command("deploy").description("Deploy to Specific infrastructure").option("--project <id>", "Project ID to deploy to (overrides .projectid file)").option("--secret <key=value...>", "Secret values (repeatable)").option("--config <key=value...>", "Config values (repeatable)").addHelpText("after", `
377562
+ program.command("deploy").description("Deploy to Specific infrastructure").option("--project <id>", "Project ID to deploy to (overrides .projectid file)").option("--env <name>", "Target environment (auto-selected if only one exists)").option("--secret <key=value...>", "Secret values (repeatable)").option("--config <key=value...>", "Config values (repeatable)").addHelpText("after", `
377350
377563
  Examples:
377351
377564
  $ specific deploy
377565
+ $ specific deploy --env staging
377352
377566
  $ specific deploy --project proj_123
377353
377567
  $ specific deploy --secret db_url=postgres://... --config domain=app.com`).action((options2) => {
377354
377568
  deployCommand(options2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/cli",
3
- "version": "0.1.119",
3
+ "version": "0.1.120",
4
4
  "description": "CLI for Specific infrastructure-as-code",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",