@specific.dev/cli 0.1.122 → 0.1.124

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 (70) 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 +156 -11
  64. package/dist/docs/index.md +1 -3
  65. package/dist/docs/integrations/temporal.md +1 -91
  66. package/dist/docs/temporal.md +0 -2
  67. package/package.json +1 -1
  68. /package/dist/admin/_next/static/{eboxHGqEe6h17B0pFV0xY → mW_zIz1G5hK3GXQcXI7Kb}/_buildManifest.js +0 -0
  69. /package/dist/admin/_next/static/{eboxHGqEe6h17B0pFV0xY → mW_zIz1G5hK3GXQcXI7Kb}/_clientMiddlewareManifest.json +0 -0
  70. /package/dist/admin/_next/static/{eboxHGqEe6h17B0pFV0xY → mW_zIz1G5hK3GXQcXI7Kb}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -367814,6 +367814,58 @@ var ApiClient = class {
367814
367814
  }
367815
367815
  return response.json();
367816
367816
  }
367817
+ async createPreviewEnvironment(projectId) {
367818
+ const url = `${this.baseUrl}/environments/preview`;
367819
+ writeLog("api", `POST ${url}`);
367820
+ const response = await fetch(url, {
367821
+ method: "POST",
367822
+ headers: {
367823
+ "Content-Type": "application/json",
367824
+ ...await this.authHeaders()
367825
+ },
367826
+ body: JSON.stringify({ projectId })
367827
+ });
367828
+ writeLog("api", `Response: ${response.status} ${response.statusText}`);
367829
+ if (!response.ok) {
367830
+ try {
367831
+ const error = await response.json();
367832
+ throw new Error(
367833
+ `Failed to create preview environment: ${error.error} (${error.code})`
367834
+ );
367835
+ } catch (e) {
367836
+ if (e instanceof Error && e.message.startsWith("Failed to create preview")) {
367837
+ throw e;
367838
+ }
367839
+ throw new Error(`Failed to create preview environment: ${response.statusText}`);
367840
+ }
367841
+ }
367842
+ return response.json();
367843
+ }
367844
+ async deletePreviewEnvironment(environmentId) {
367845
+ const url = `${this.baseUrl}/environments/${environmentId}`;
367846
+ writeLog("api", `DELETE ${url}`);
367847
+ const response = await fetch(url, {
367848
+ method: "DELETE",
367849
+ headers: await this.authHeaders()
367850
+ });
367851
+ writeLog("api", `Response: ${response.status} ${response.statusText}`);
367852
+ if (!response.ok) {
367853
+ if (response.status === 404) {
367854
+ return;
367855
+ }
367856
+ try {
367857
+ const error = await response.json();
367858
+ throw new Error(
367859
+ `Failed to delete preview environment: ${error.error} (${error.code})`
367860
+ );
367861
+ } catch (e) {
367862
+ if (e instanceof Error && e.message.startsWith("Failed to delete preview")) {
367863
+ throw e;
367864
+ }
367865
+ throw new Error(`Failed to delete preview environment: ${response.statusText}`);
367866
+ }
367867
+ }
367868
+ }
367817
367869
  async getMe(signal) {
367818
367870
  const url = `${this.baseUrl}/users/me`;
367819
367871
  writeLog("api", `GET ${url}`);
@@ -367891,6 +367943,13 @@ var SpecificClient = class {
367891
367943
  async submitConfigs(deploymentId, configs) {
367892
367944
  await this.client.submitConfigs(deploymentId, configs);
367893
367945
  }
367946
+ // --- Preview Environments ---
367947
+ async createPreviewEnvironment(projectId) {
367948
+ return this.client.createPreviewEnvironment(projectId);
367949
+ }
367950
+ async deletePreviewEnvironment(environmentId) {
367951
+ return this.client.deletePreviewEnvironment(environmentId);
367952
+ }
367894
367953
  };
367895
367954
  function toDeployment(response) {
367896
367955
  return {
@@ -372770,6 +372829,7 @@ import * as fs3 from "fs";
372770
372829
  import * as path3 from "path";
372771
372830
  var PROJECT_ID_FILE = ".specific/project_id";
372772
372831
  var ENVIRONMENT_ID_FILE = ".specific/environment_id";
372832
+ var PREVIEW_ENVIRONMENT_ID_FILE = ".specific/preview_environment_id";
372773
372833
  var ProjectNotLinkedError = class extends Error {
372774
372834
  constructor() {
372775
372835
  super(
@@ -372826,6 +372886,21 @@ function writeEnvironmentId(environmentId, projectDir = process.cwd()) {
372826
372886
  }
372827
372887
  fs3.writeFileSync(path3.join(specificDir, "environment_id"), environmentId + "\n");
372828
372888
  }
372889
+ function readPreviewEnvironmentId(projectDir = process.cwd()) {
372890
+ const filePath = path3.join(projectDir, PREVIEW_ENVIRONMENT_ID_FILE);
372891
+ if (!fs3.existsSync(filePath)) {
372892
+ return null;
372893
+ }
372894
+ const id = fs3.readFileSync(filePath, "utf-8").trim();
372895
+ return id || null;
372896
+ }
372897
+ function writePreviewEnvironmentId(environmentId, projectDir = process.cwd()) {
372898
+ const specificDir = path3.join(projectDir, ".specific");
372899
+ if (!fs3.existsSync(specificDir)) {
372900
+ fs3.mkdirSync(specificDir, { recursive: true });
372901
+ }
372902
+ fs3.writeFileSync(path3.join(specificDir, "preview_environment_id"), environmentId + "\n");
372903
+ }
372829
372904
 
372830
372905
  // src/lib/auth/credentials.ts
372831
372906
  import * as fs19 from "fs";
@@ -373266,7 +373341,7 @@ function trackEvent(event, properties) {
373266
373341
  event,
373267
373342
  properties: {
373268
373343
  ...properties,
373269
- cli_version: "0.1.122",
373344
+ cli_version: "0.1.124",
373270
373345
  platform: process.platform,
373271
373346
  node_version: process.version,
373272
373347
  project_id: getProjectId()
@@ -373591,10 +373666,6 @@ import { fileURLToPath as fileURLToPath3 } from "url";
373591
373666
 
373592
373667
  // src/lib/beta/registry.ts
373593
373668
  var BETA_REGISTRY = [
373594
- {
373595
- name: "temporal",
373596
- description: "Managed Temporal workflow engine for durable workflows and background tasks"
373597
- },
373598
373669
  {
373599
373670
  name: "mail",
373600
373671
  description: "Managed email sending via SMTP for transactional emails"
@@ -375148,6 +375219,7 @@ function PhaseIndicator({
375148
375219
  showSpinner = true
375149
375220
  }) {
375150
375221
  const phases = [
375222
+ "creating-preview",
375151
375223
  "creating-tarball",
375152
375224
  "creating-deployment",
375153
375225
  "uploading",
@@ -375299,7 +375371,7 @@ function formatErrorCode(code) {
375299
375371
  function StructuredError({ error }) {
375300
375372
  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)));
375301
375373
  }
375302
- function DeployUI({ envFlag, config }) {
375374
+ function DeployUI({ envFlag, preview, config }) {
375303
375375
  const { exit } = useApp3();
375304
375376
  const [state, setState] = useState6({ phase: "checking-auth" });
375305
375377
  const clientRef = React7.useRef(null);
@@ -375487,6 +375559,15 @@ function DeployUI({ envFlag, config }) {
375487
375559
  }));
375488
375560
  return;
375489
375561
  }
375562
+ if (preview) {
375563
+ setState((s) => ({
375564
+ ...s,
375565
+ phase: "creating-preview",
375566
+ environments,
375567
+ previewStatus: "Creating preview environment"
375568
+ }));
375569
+ return;
375570
+ }
375490
375571
  if (envFlag) {
375491
375572
  const match = environments.find((e) => e.name === envFlag);
375492
375573
  if (!match) {
@@ -375549,6 +375630,45 @@ function DeployUI({ envFlag, config }) {
375549
375630
  cancelled = true;
375550
375631
  };
375551
375632
  }, [state.phase, state.projectId]);
375633
+ useEffect6(() => {
375634
+ if (state.phase !== "creating-preview" || !state.projectId) return;
375635
+ let cancelled = false;
375636
+ async function createPreview() {
375637
+ try {
375638
+ const token = await getValidAccessToken();
375639
+ const client2 = new SpecificClient({ accessToken: token });
375640
+ const projectDir = process.cwd();
375641
+ const previousPreviewId = readPreviewEnvironmentId(projectDir);
375642
+ if (previousPreviewId) {
375643
+ setState((s) => ({ ...s, previewStatus: "Tearing down previous preview environment" }));
375644
+ try {
375645
+ await client2.deletePreviewEnvironment(previousPreviewId);
375646
+ } catch {
375647
+ }
375648
+ }
375649
+ if (cancelled) return;
375650
+ setState((s) => ({ ...s, previewStatus: "Creating preview environment" }));
375651
+ const previewEnv = await client2.createPreviewEnvironment(state.projectId);
375652
+ writePreviewEnvironmentId(previewEnv.id, projectDir);
375653
+ if (cancelled) return;
375654
+ setState((s) => ({
375655
+ ...s,
375656
+ phase: "creating-tarball",
375657
+ environmentName: previewEnv.name
375658
+ }));
375659
+ } catch (err) {
375660
+ if (cancelled) return;
375661
+ setState({
375662
+ phase: "error",
375663
+ error: err instanceof Error ? err.message : String(err)
375664
+ });
375665
+ }
375666
+ }
375667
+ createPreview();
375668
+ return () => {
375669
+ cancelled = true;
375670
+ };
375671
+ }, [state.phase, state.projectId]);
375552
375672
  const handleEnvironmentSelect = useCallback3(
375553
375673
  (env2) => {
375554
375674
  writeEnvironmentId(env2.id);
@@ -376100,7 +376220,14 @@ function DeployUI({ envFlag, config }) {
376100
376220
  }
376101
376221
  return "Preparing";
376102
376222
  };
376103
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, "Deploying to ", environment), deployment && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " (", deployment.id, ")")), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(
376223
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, preview ? `Deploying preview${environment !== "prod" ? ` (${environment})` : ""}` : `Deploying to ${environment}`), deployment && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " (", deployment.id, ")")), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, preview && /* @__PURE__ */ React7.createElement(
376224
+ PhaseIndicator,
376225
+ {
376226
+ phase: "creating-preview",
376227
+ currentPhase: displayPhase,
376228
+ label: state.previewStatus || "Creating preview environment"
376229
+ }
376230
+ ), /* @__PURE__ */ React7.createElement(
376104
376231
  PhaseIndicator,
376105
376232
  {
376106
376233
  phase: "creating-tarball",
@@ -376181,6 +376308,21 @@ async function runDeployPipeline(options2) {
376181
376308
  } else if (!hasProjectId(projectDir)) {
376182
376309
  writeProjectId(projectId);
376183
376310
  }
376311
+ if (options2.preview) {
376312
+ const previousPreviewId = readPreviewEnvironmentId(projectDir);
376313
+ if (previousPreviewId) {
376314
+ console.log("Cleaning up previous preview environment...");
376315
+ try {
376316
+ await client2.deletePreviewEnvironment(previousPreviewId);
376317
+ } catch {
376318
+ }
376319
+ }
376320
+ console.log("Creating preview environment...");
376321
+ const preview = await client2.createPreviewEnvironment(projectId);
376322
+ writePreviewEnvironmentId(preview.id, projectDir);
376323
+ console.log(`Preview environment "${preview.name}" created (expires: ${new Date(preview.expiresAt).toLocaleString()})`);
376324
+ options2.env = preview.name;
376325
+ }
376184
376326
  let environmentName;
376185
376327
  if (options2.env) {
376186
376328
  environmentName = options2.env;
@@ -376409,7 +376551,8 @@ async function deployCommand(options2) {
376409
376551
  projectId: options2.project,
376410
376552
  env: options2.env,
376411
376553
  secrets: options2.secret,
376412
- configs: options2.config
376554
+ configs: options2.config,
376555
+ preview: options2.preview
376413
376556
  });
376414
376557
  return;
376415
376558
  }
@@ -376418,6 +376561,7 @@ async function deployCommand(options2) {
376418
376561
  DeployUI,
376419
376562
  {
376420
376563
  envFlag: options2.env,
376564
+ preview: options2.preview,
376421
376565
  config
376422
376566
  }
376423
376567
  )
@@ -377278,7 +377422,7 @@ function compareVersions(a, b) {
377278
377422
  return 0;
377279
377423
  }
377280
377424
  async function checkForUpdate() {
377281
- const currentVersion = "0.1.122";
377425
+ const currentVersion = "0.1.124";
377282
377426
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
377283
377427
  if (!response.ok) {
377284
377428
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -377548,7 +377692,7 @@ async function projectListCommand() {
377548
377692
  var program = new Command();
377549
377693
  var env = "production";
377550
377694
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
377551
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.122").enablePositionalOptions();
377695
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.124").enablePositionalOptions();
377552
377696
  program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
377553
377697
  Examples:
377554
377698
  $ specific init
@@ -377569,11 +377713,12 @@ Examples:
377569
377713
  const key = options2.key ?? getDefaultKey();
377570
377714
  devCommand(key, options2.tunnel ?? false);
377571
377715
  });
377572
- 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", `
377716
+ 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)").option("--preview", "Deploy to an ephemeral preview environment").addHelpText("after", `
377573
377717
  Examples:
377574
377718
  $ specific deploy
377575
377719
  $ specific deploy --env staging
377576
377720
  $ specific deploy --project proj_123
377721
+ $ specific deploy --preview
377577
377722
  $ specific deploy --secret db_url=postgres://... --config domain=app.com`).action((options2) => {
377578
377723
  deployCommand(options2);
377579
377724
  });
@@ -22,9 +22,7 @@ A full development environment can be started with `specific dev`. To deploy any
22
22
  - [Storage](/storage): define S3-compatible object/blob storage for services to store files in.
23
23
  - [Redis](/redis): define non-durable Redis-compatible databases for caching and more.
24
24
  - [Volumes](/volumes): define persistent storage volumes for services to store files in.
25
- <!-- beta:temporal -->
26
25
  - [Temporal](/temporal): managed durable workflow engine for background tasks, AI agents, cron jobs and more.
27
- <!-- /beta:temporal -->
28
26
  <!-- beta:mail -->
29
27
  - [Mail](/mail): managed email sending via SMTP for transactional emails.
30
28
  <!-- /beta:mail -->
@@ -37,7 +35,7 @@ The following is a list of common frameworks and libraries with guidance on how
37
35
  - [Next.js](/integrations/nextjs): full-stack React framework
38
36
  - [Drizzle ORM](/integrations/drizzle): TypeScript ORM with type safety
39
37
  - [Prisma](/integrations/prisma): TypeScript ORM with auto-generated client
40
- - [Temporal](/integrations/temporal): durable workflow engine for background tasks, AI agents, cron jobs, batch jobs and more
38
+ - [Temporal](/integrations/temporal): using Temporal with Specific for durable workflows, background tasks, AI agents, cron jobs and more
41
39
 
42
40
  ## Adding Specific to existing projects / migrating to Specific
43
41
 
@@ -1,93 +1,3 @@
1
1
  # Temporal
2
2
 
3
- <!-- beta:temporal -->
4
- > **Beta**: Specific now has built-in Temporal support. See [Temporal](/temporal) for the recommended approach using the `temporal` block. The manual setup below still works but is no longer needed.
5
- <!-- /beta:temporal -->
6
-
7
- Temporal is a durable workflow engine. This guide covers running Temporal locally during development and connecting to Temporal Cloud in production.
8
-
9
- Temporal has extensive SDK support across many languages including TypeScript, Python, Go, Java, .NET, and PHP. See the [Temporal documentation](https://docs.temporal.io/) for language-specific guides on implementing workers, workflows, and activities. This document only covers integration with Specific.
10
-
11
- ## Configuration
12
-
13
- Define a dev-only Temporal service that runs locally, along with configs and secrets for production Temporal Cloud.
14
-
15
- ```hcl
16
- build "app" {
17
- base = "node"
18
- }
19
-
20
- # Dev-only service: runs local Temporal server during development
21
- # No top-level command means it's excluded from production deployment
22
- #
23
- service "temporal" {
24
- endpoint "grpc" {}
25
- endpoint "ui" { public = true }
26
-
27
- dev {
28
- command = "temporal server start-dev --port $GRPC_PORT --ui-port $UI_PORT --db-filename /tmp/temporal.db"
29
- env = {
30
- GRPC_PORT = endpoint.grpc.port
31
- UI_PORT = endpoint.ui.port
32
- }
33
- }
34
- }
35
-
36
- # Temporal Cloud address (e.g., your-namespace.tmprl.cloud:7233)
37
- config "temporal_address" {}
38
-
39
- # Namespace - uses "default" in dev, must be set for production
40
- config "temporal_namespace" {
41
- dev {
42
- default = "default"
43
- }
44
- }
45
-
46
- # API key for Temporal Cloud - not required in dev
47
- secret "temporal_api_key" {
48
- dev {
49
- required = false
50
- }
51
- }
52
-
53
- service "app" {
54
- build = build.app
55
- command = "node index.js"
56
- expose {}
57
-
58
- env = {
59
- PORT = port
60
- TEMPORAL_ADDRESS = config.temporal_address
61
- TEMPORAL_NAMESPACE = config.temporal_namespace
62
- TEMPORAL_API_KEY = secret.temporal_api_key
63
- }
64
-
65
- dev {
66
- command = "node --watch index.js"
67
- env = {
68
- # Override to use local Temporal service instead
69
- TEMPORAL_ADDRESS = ""
70
- TEMPORAL_HOST = service.temporal.endpoint.grpc.host
71
- TEMPORAL_PORT = service.temporal.endpoint.grpc.port
72
- }
73
- }
74
- }
75
- ```
76
-
77
- ## Development
78
-
79
- Running `specific dev` automatically starts a local Temporal server. The user must install the Temporal CLI for this: https://temporal.io/setup/install-temporal-cli. The Temporal Web UI is available at the public endpoint URL shown in the output.
80
-
81
- Your app connects to the local Temporal server using the service endpoint environment variables (`TEMPORAL_HOST` and `TEMPORAL_PORT`).
82
-
83
- ## Production
84
-
85
- In production, the dev-only `temporal` service is excluded from deployment. Your app connects to Temporal Cloud using:
86
-
87
- 1. `temporal_address` - Your Temporal Cloud address (e.g., `your-namespace.tmprl.cloud:7233`)
88
- 2. `temporal_namespace` - Your Temporal Cloud namespace
89
- 3. `temporal_api_key` - API key for authentication
90
-
91
- Set these with `specific secrets set` before deploying.
92
-
93
- ## SDK support
3
+ Specific has built-in Temporal support via the `temporal` block. See [Temporal](/temporal) for the full guide on configuration, development, and production deployment.
@@ -1,4 +1,3 @@
1
- <!-- beta:temporal -->
2
1
  # Temporal
3
2
 
4
3
  Define managed Temporal workflow engines for durable workflows, background tasks, AI agents, cron jobs, batch jobs and more.
@@ -66,4 +65,3 @@ Running `specific deploy` automatically provisions a managed [Temporal Cloud](ht
66
65
  4. Credentials are securely injected into your services
67
66
 
68
67
  The reference attributes (`temporal.<name>.url`, `.namespace`, `.api_key`) resolve to the Temporal Cloud values in production, so no environment-specific overrides are needed.
69
- <!-- /beta:temporal -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/cli",
3
- "version": "0.1.122",
3
+ "version": "0.1.124",
4
4
  "description": "CLI for Specific infrastructure-as-code",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",