@specific.dev/cli 0.1.113 → 0.1.115

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 +206 -106
  64. package/package.json +1 -1
  65. /package/dist/admin/_next/static/{zFZhmJWhp-S4pZTnCndRF → 3wOysvI433uU7Czi4vdl0}/_buildManifest.js +0 -0
  66. /package/dist/admin/_next/static/{zFZhmJWhp-S4pZTnCndRF → 3wOysvI433uU7Czi4vdl0}/_clientMiddlewareManifest.json +0 -0
  67. /package/dist/admin/_next/static/{zFZhmJWhp-S4pZTnCndRF → 3wOysvI433uU7Czi4vdl0}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -367514,8 +367514,14 @@ var ApiClient = class {
367514
367514
  Authorization: `Bearer ${token}`
367515
367515
  };
367516
367516
  }
367517
- async createDeployment(projectId, environment) {
367518
- const requestBody = { projectId, environment };
367517
+ async createDeployment(projectId, environment, options2) {
367518
+ const requestBody = {
367519
+ projectId,
367520
+ environment,
367521
+ ...options2?.triggeredBy && { triggeredBy: options2.triggeredBy },
367522
+ ...options2?.gitCommitSha && { gitCommitSha: options2.gitCommitSha },
367523
+ ...options2?.gitBranch && { gitBranch: options2.gitBranch }
367524
+ };
367519
367525
  writeLog("api", `POST ${this.baseUrl}/deployments`);
367520
367526
  writeLog("api", `Request body: ${JSON.stringify(requestBody)}`);
367521
367527
  const response = await fetch(`${this.baseUrl}/deployments`, {
@@ -367862,9 +367868,9 @@ var SpecificClient = class {
367862
367868
  return await this.client.getMe();
367863
367869
  }
367864
367870
  // --- Deployments ---
367865
- async createDeployment(projectId, environment) {
367871
+ async createDeployment(projectId, environment, options2) {
367866
367872
  return toDeployment(
367867
- await this.client.createDeployment(projectId, environment)
367873
+ await this.client.createDeployment(projectId, environment, options2)
367868
367874
  );
367869
367875
  }
367870
367876
  async uploadTarball(deploymentId, tarball, appPath) {
@@ -369458,7 +369464,7 @@ async function parseLocalFile(content) {
369458
369464
  if (block && typeof block === "object") {
369459
369465
  for (const [key, value] of Object.entries(block)) {
369460
369466
  if (typeof value === "string") {
369461
- secrets.set(key, value);
369467
+ secrets.set(key, value.trimEnd());
369462
369468
  }
369463
369469
  }
369464
369470
  }
@@ -369470,7 +369476,7 @@ async function parseLocalFile(content) {
369470
369476
  if (block && typeof block === "object") {
369471
369477
  for (const [key, value] of Object.entries(block)) {
369472
369478
  if (typeof value === "string") {
369473
- configs.set(key, value);
369479
+ configs.set(key, value.trimEnd());
369474
369480
  }
369475
369481
  }
369476
369482
  }
@@ -369485,57 +369491,42 @@ async function loadLocal() {
369485
369491
  const content = await readFile(LOCAL_FILE, "utf-8");
369486
369492
  return await parseLocalFile(content);
369487
369493
  }
369488
- function escapeHclValue(value) {
369489
- return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
369494
+ function formatHclValue(value) {
369495
+ if (value.includes("\n")) {
369496
+ return `<<-EOT
369497
+ ${value}
369498
+ EOT`;
369499
+ }
369500
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
369501
+ return `"${escaped}"`;
369490
369502
  }
369491
- function updateBlockValue(content, blockName, key, value) {
369492
- const escapedValue = escapeHclValue(value);
369493
- const newLine = ` ${key} = "${escapedValue}"`;
369494
- const blockRegex = new RegExp(`(${blockName}\\s*\\{)([^}]*)(\\})`, "s");
369495
- const blockMatch = content.match(blockRegex);
369496
- if (blockMatch) {
369497
- const blockContent = blockMatch[2];
369498
- const keyRegex = new RegExp(`^(\\s*)${key}\\s*=\\s*"[^"]*"\\s*$`, "m");
369499
- const keyMatch = blockContent.match(keyRegex);
369500
- if (keyMatch) {
369501
- const updatedBlockContent = blockContent.replace(keyRegex, newLine);
369502
- return content.replace(blockRegex, `$1${updatedBlockContent}$3`);
369503
- } else {
369504
- const trimmedContent = blockContent.trimEnd();
369505
- const newBlockContent = trimmedContent ? `${trimmedContent}
369506
- ${newLine}
369507
- ` : `
369508
- ${newLine}
369509
- `;
369510
- return content.replace(blockRegex, `$1${newBlockContent}$3`);
369511
- }
369512
- } else {
369513
- const newBlock = `${blockName} {
369514
- ${newLine}
369503
+ function serializeBlock(blockName, entries) {
369504
+ const lines = [`${blockName} {`];
369505
+ for (const [key, value] of entries) {
369506
+ lines.push(` ${key} = ${formatHclValue(value)}`);
369507
+ }
369508
+ lines.push("}");
369509
+ return lines.join("\n");
369515
369510
  }
369516
- `;
369517
- return content.trimEnd() + "\n\n" + newBlock;
369511
+ function serializeLocalFile(secrets, configs) {
369512
+ const blocks = [];
369513
+ if (secrets.size > 0) {
369514
+ blocks.push(serializeBlock("secrets", secrets));
369515
+ }
369516
+ if (configs.size > 0) {
369517
+ blocks.push(serializeBlock("config", configs));
369518
369518
  }
369519
+ return HEADER_COMMENT + blocks.join("\n\n") + "\n";
369519
369520
  }
369520
369521
  async function saveLocalSecret(name, value) {
369521
- let content = "";
369522
- if (existsSync5(LOCAL_FILE)) {
369523
- content = await readFile(LOCAL_FILE, "utf-8");
369524
- } else {
369525
- content = HEADER_COMMENT;
369526
- }
369527
- content = updateBlockValue(content, "secrets", name, value);
369528
- await writeFile(LOCAL_FILE, content);
369522
+ const { secrets, configs } = existsSync5(LOCAL_FILE) ? await parseLocalFile(await readFile(LOCAL_FILE, "utf-8")) : { secrets: /* @__PURE__ */ new Map(), configs: /* @__PURE__ */ new Map() };
369523
+ secrets.set(name, value);
369524
+ await writeFile(LOCAL_FILE, serializeLocalFile(secrets, configs));
369529
369525
  }
369530
369526
  async function saveLocalConfig(name, value) {
369531
- let content = "";
369532
- if (existsSync5(LOCAL_FILE)) {
369533
- content = await readFile(LOCAL_FILE, "utf-8");
369534
- } else {
369535
- content = HEADER_COMMENT;
369536
- }
369537
- content = updateBlockValue(content, "config", name, value);
369538
- await writeFile(LOCAL_FILE, content);
369527
+ const { secrets, configs } = existsSync5(LOCAL_FILE) ? await parseLocalFile(await readFile(LOCAL_FILE, "utf-8")) : { secrets: /* @__PURE__ */ new Map(), configs: /* @__PURE__ */ new Map() };
369528
+ configs.set(name, value);
369529
+ await writeFile(LOCAL_FILE, serializeLocalFile(secrets, configs));
369539
369530
  }
369540
369531
  function appendSearchPathToUrl(baseUrl, searchPath) {
369541
369532
  const url = new URL(baseUrl);
@@ -371394,6 +371385,16 @@ async function startResources(options2) {
371394
371385
  startedResources.push(instance);
371395
371386
  callbacks.onResourceReady?.(instance.name, instance);
371396
371387
  log(`Temporal namespace "${instance.name}" ready`);
371388
+ const dbState = {
371389
+ engine: "temporal",
371390
+ port: instance.port,
371391
+ host: instance.host,
371392
+ user: "",
371393
+ password: "",
371394
+ dbName: "",
371395
+ url: instance.url
371396
+ };
371397
+ await stateManager.registerDatabase(instance.name, dbState);
371397
371398
  }
371398
371399
  }
371399
371400
  for (const mail of mailConfigs) {
@@ -373277,7 +373278,7 @@ function trackEvent(event, properties) {
373277
373278
  event,
373278
373279
  properties: {
373279
373280
  ...properties,
373280
- cli_version: "0.1.113",
373281
+ cli_version: "0.1.115",
373281
373282
  platform: process.platform,
373282
373283
  node_version: process.version,
373283
373284
  project_id: getProjectId()
@@ -374510,53 +374511,113 @@ function checkCommand() {
374510
374511
  }
374511
374512
 
374512
374513
  // src/commands/dev.tsx
374513
- import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
374514
+ import React6, { useState as useState5, useEffect as useEffect5, useRef as useRef3 } from "react";
374514
374515
  import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static } from "ink";
374515
374516
  import Spinner3 from "ink-spinner";
374516
374517
  import { Readable as Readable2 } from "stream";
374517
374518
 
374518
374519
  // src/lib/ui/SecretInput.tsx
374519
- import React4, { useState as useState3 } from "react";
374520
+ import React4, { useState as useState3, useRef, useCallback, useEffect as useEffect3 } from "react";
374520
374521
  import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
374522
+ var PASTE_DETECT_DELAY_MS = 100;
374521
374523
  function SecretInput({ secretName, onSubmit, onCancel }) {
374522
374524
  const [value, setValue] = useState3("");
374525
+ const valueRef = useRef("");
374526
+ const submitTimer = useRef(null);
374527
+ useEffect3(() => {
374528
+ valueRef.current = value;
374529
+ }, [value]);
374530
+ const scheduleSubmit = useCallback(() => {
374531
+ if (submitTimer.current) {
374532
+ clearTimeout(submitTimer.current);
374533
+ }
374534
+ submitTimer.current = setTimeout(() => {
374535
+ submitTimer.current = null;
374536
+ if (valueRef.current.trim() !== "") {
374537
+ onSubmit(valueRef.current);
374538
+ }
374539
+ }, PASTE_DETECT_DELAY_MS);
374540
+ }, [onSubmit]);
374541
+ const cancelSubmit = useCallback(() => {
374542
+ if (submitTimer.current) {
374543
+ clearTimeout(submitTimer.current);
374544
+ submitTimer.current = null;
374545
+ setValue((prev) => prev + "\n");
374546
+ }
374547
+ }, []);
374548
+ useEffect3(() => {
374549
+ return () => {
374550
+ if (submitTimer.current) clearTimeout(submitTimer.current);
374551
+ };
374552
+ }, []);
374523
374553
  useInput2((input, key) => {
374524
374554
  if (key.return) {
374525
- if (value.trim() !== "") {
374526
- onSubmit(value);
374527
- }
374555
+ scheduleSubmit();
374528
374556
  } else if (key.escape) {
374529
374557
  onCancel();
374530
374558
  } else if (key.backspace || key.delete) {
374559
+ cancelSubmit();
374531
374560
  setValue((prev) => prev.slice(0, -1));
374532
374561
  } else if (!key.ctrl && !key.meta && input) {
374562
+ cancelSubmit();
374533
374563
  setValue((prev) => prev + input);
374534
374564
  }
374535
374565
  });
374536
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, "Enter value for secret ", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, secretName), ":"), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, "> "), /* @__PURE__ */ React4.createElement(Text4, null, value.length > 0 ? "*".repeat(value.length) : ""), /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "|")), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
374566
+ const lineCount = value.split("\n").length;
374567
+ return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, "Enter value for secret ", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, secretName), ":"), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, "> "), /* @__PURE__ */ React4.createElement(Text4, null, value.length > 0 ? lineCount > 1 ? `[${lineCount} lines, ${value.length} chars]` : "*".repeat(value.length) : ""), /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "|")), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
374537
374568
  }
374538
374569
 
374539
374570
  // src/lib/ui/ConfigInput.tsx
374540
- import React5, { useState as useState4 } from "react";
374571
+ import React5, { useState as useState4, useRef as useRef2, useCallback as useCallback2, useEffect as useEffect4 } from "react";
374541
374572
  import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
374573
+ var PASTE_DETECT_DELAY_MS2 = 100;
374542
374574
  function ConfigInput({ configName, defaultValue, onSubmit, onCancel }) {
374543
374575
  const [value, setValue] = useState4("");
374544
- useInput3((input, key) => {
374545
- if (key.return) {
374546
- if (value.trim() !== "") {
374547
- onSubmit(value);
374576
+ const valueRef = useRef2("");
374577
+ const submitTimer = useRef2(null);
374578
+ useEffect4(() => {
374579
+ valueRef.current = value;
374580
+ }, [value]);
374581
+ const scheduleSubmit = useCallback2(() => {
374582
+ if (submitTimer.current) {
374583
+ clearTimeout(submitTimer.current);
374584
+ }
374585
+ submitTimer.current = setTimeout(() => {
374586
+ submitTimer.current = null;
374587
+ if (valueRef.current.trim() !== "") {
374588
+ onSubmit(valueRef.current);
374548
374589
  } else if (defaultValue !== void 0) {
374549
374590
  onSubmit(defaultValue);
374550
374591
  }
374592
+ }, PASTE_DETECT_DELAY_MS2);
374593
+ }, [onSubmit, defaultValue]);
374594
+ const cancelSubmit = useCallback2(() => {
374595
+ if (submitTimer.current) {
374596
+ clearTimeout(submitTimer.current);
374597
+ submitTimer.current = null;
374598
+ setValue((prev) => prev + "\n");
374599
+ }
374600
+ }, []);
374601
+ useEffect4(() => {
374602
+ return () => {
374603
+ if (submitTimer.current) clearTimeout(submitTimer.current);
374604
+ };
374605
+ }, []);
374606
+ useInput3((input, key) => {
374607
+ if (key.return) {
374608
+ scheduleSubmit();
374551
374609
  } else if (key.escape) {
374552
374610
  onCancel();
374553
374611
  } else if (key.backspace || key.delete) {
374612
+ cancelSubmit();
374554
374613
  setValue((prev) => prev.slice(0, -1));
374555
374614
  } else if (!key.ctrl && !key.meta && input) {
374615
+ cancelSubmit();
374556
374616
  setValue((prev) => prev + input);
374557
374617
  }
374558
374618
  });
374559
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Enter value for config ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, configName), defaultValue !== void 0 && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " (default: ", defaultValue, ")"), ":"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, value), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, defaultValue !== void 0 ? "(Press Enter to accept default, type to override, Esc to cancel)" : "(Press Enter to save, Esc to cancel)"));
374619
+ const lineCount = value.split("\n").length;
374620
+ return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Enter value for config ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, configName), defaultValue !== void 0 && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " (default: ", defaultValue, ")"), ":"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, lineCount > 1 ? `[${lineCount} lines]` : value), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, defaultValue !== void 0 ? "(Press Enter to accept default, type to override, Esc to cancel)" : "(Press Enter to save, Esc to cancel)"));
374560
374621
  }
374561
374622
 
374562
374623
  // src/commands/dev.tsx
@@ -374573,8 +374634,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
374573
374634
  tunnelUrls: /* @__PURE__ */ new Map(),
374574
374635
  tunnelStatus: /* @__PURE__ */ new Map()
374575
374636
  });
374576
- const devEnvRef = useRef(null);
374577
- const startTimeRef = useRef(null);
374637
+ const devEnvRef = useRef3(null);
374638
+ const startTimeRef = useRef3(null);
374578
374639
  const buildColorMap = (config2) => {
374579
374640
  const colorMap = /* @__PURE__ */ new Map();
374580
374641
  let colorIndex = 0;
@@ -374600,7 +374661,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
374600
374661
  }
374601
374662
  return colorMap;
374602
374663
  };
374603
- useEffect3(() => {
374664
+ useEffect5(() => {
374604
374665
  const devEnv = new DevEnvironment({
374605
374666
  projectDir: process.cwd(),
374606
374667
  instanceKey,
@@ -374737,7 +374798,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
374737
374798
  devEnvRef.current = null;
374738
374799
  };
374739
374800
  }, [instanceKey, tunnelEnabled]);
374740
- useEffect3(() => {
374801
+ useEffect5(() => {
374741
374802
  const handleSignal = () => {
374742
374803
  const devEnv = devEnvRef.current;
374743
374804
  if (!devEnv) return;
@@ -374768,7 +374829,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
374768
374829
  process.off("unhandledRejection", handleCrash);
374769
374830
  };
374770
374831
  }, []);
374771
- useEffect3(() => {
374832
+ useEffect5(() => {
374772
374833
  if (state.status === "running" && !startTimeRef.current) {
374773
374834
  startTimeRef.current = Date.now();
374774
374835
  trackEvent("dev_started");
@@ -375064,11 +375125,29 @@ function devCommand(instanceKey, tunnelEnabled = false) {
375064
375125
 
375065
375126
  // src/commands/deploy.tsx
375066
375127
  init_open();
375067
- import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
375128
+ import React7, { useState as useState6, useEffect as useEffect6, useCallback as useCallback3 } from "react";
375068
375129
  import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput4 } from "ink";
375069
375130
  import Spinner4 from "ink-spinner";
375070
375131
  import * as fs22 from "fs";
375071
375132
  import * as path20 from "path";
375133
+ import { execFileSync as execFileSync2 } from "child_process";
375134
+ function getGitInfo(cwd) {
375135
+ try {
375136
+ const commitSha = execFileSync2("git", ["rev-parse", "HEAD"], {
375137
+ cwd,
375138
+ encoding: "utf-8",
375139
+ stdio: ["pipe", "pipe", "pipe"]
375140
+ }).trim();
375141
+ const branch = execFileSync2(
375142
+ "git",
375143
+ ["rev-parse", "--abbrev-ref", "HEAD"],
375144
+ { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
375145
+ ).trim();
375146
+ return { commitSha, branch };
375147
+ } catch {
375148
+ return null;
375149
+ }
375150
+ }
375072
375151
  function formatBytes(bytes) {
375073
375152
  if (bytes < 1024) return `${bytes} B`;
375074
375153
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -375214,7 +375293,7 @@ function DeployUI({ environment, config }) {
375214
375293
  const { exit } = useApp3();
375215
375294
  const [state, setState] = useState6({ phase: "checking-auth" });
375216
375295
  const clientRef = React7.useRef(null);
375217
- useEffect4(() => {
375296
+ useEffect6(() => {
375218
375297
  if (state.phase !== "checking-auth") return;
375219
375298
  const projectDir = process.cwd();
375220
375299
  if (hasProjectId(projectDir)) {
@@ -375235,7 +375314,7 @@ function DeployUI({ environment, config }) {
375235
375314
  }
375236
375315
  setState({ phase: "logging-in" });
375237
375316
  }, [state.phase]);
375238
- useEffect4(() => {
375317
+ useEffect6(() => {
375239
375318
  if (state.phase !== "logging-in") return;
375240
375319
  let cancelled = false;
375241
375320
  async function startLogin() {
@@ -375296,7 +375375,7 @@ function DeployUI({ environment, config }) {
375296
375375
  cancelled = true;
375297
375376
  };
375298
375377
  }, [state.phase]);
375299
- useEffect4(() => {
375378
+ useEffect6(() => {
375300
375379
  if (state.phase !== "loading-projects") return;
375301
375380
  let cancelled = false;
375302
375381
  async function loadProjects() {
@@ -375329,7 +375408,7 @@ function DeployUI({ environment, config }) {
375329
375408
  cancelled = true;
375330
375409
  };
375331
375410
  }, [state.phase]);
375332
- const handleProjectSelect = useCallback(
375411
+ const handleProjectSelect = useCallback3(
375333
375412
  (project) => {
375334
375413
  if ("type" in project && project.type === "new") {
375335
375414
  setState((s) => ({ ...s, phase: "entering-name", selectedOrganizationId: project.orgId }));
@@ -375344,13 +375423,13 @@ function DeployUI({ environment, config }) {
375344
375423
  },
375345
375424
  []
375346
375425
  );
375347
- const handleNameSubmit = useCallback((name) => {
375426
+ const handleNameSubmit = useCallback3((name) => {
375348
375427
  setState((s) => ({ ...s, phase: "creating-project", newProjectName: name }));
375349
375428
  }, []);
375350
- const handleNameCancel = useCallback(() => {
375429
+ const handleNameCancel = useCallback3(() => {
375351
375430
  setState((s) => ({ ...s, phase: "selecting-project" }));
375352
375431
  }, []);
375353
- useEffect4(() => {
375432
+ useEffect6(() => {
375354
375433
  if (state.phase !== "creating-project" || !state.newProjectName) return;
375355
375434
  let cancelled = false;
375356
375435
  async function createProject() {
@@ -375377,7 +375456,7 @@ function DeployUI({ environment, config }) {
375377
375456
  cancelled = true;
375378
375457
  };
375379
375458
  }, [state.phase, state.newProjectName]);
375380
- const handleSecretSubmit = useCallback((value) => {
375459
+ const handleSecretSubmit = useCallback3((value) => {
375381
375460
  setState((s) => {
375382
375461
  if (!s.missingSecrets || s.currentSecretIndex === void 0) return s;
375383
375462
  const currentSecret2 = s.missingSecrets[s.currentSecretIndex];
@@ -375400,14 +375479,14 @@ function DeployUI({ environment, config }) {
375400
375479
  };
375401
375480
  });
375402
375481
  }, []);
375403
- const handleSecretCancel = useCallback(() => {
375482
+ const handleSecretCancel = useCallback3(() => {
375404
375483
  setState((s) => ({
375405
375484
  ...s,
375406
375485
  phase: "error",
375407
375486
  error: "Deployment cancelled - secrets not provided"
375408
375487
  }));
375409
375488
  }, []);
375410
- const handleConfigSubmit = useCallback((value) => {
375489
+ const handleConfigSubmit = useCallback3((value) => {
375411
375490
  setState((s) => {
375412
375491
  if (!s.missingConfigs || s.currentConfigIndex === void 0) return s;
375413
375492
  const currentConfig2 = s.missingConfigs[s.currentConfigIndex];
@@ -375430,14 +375509,14 @@ function DeployUI({ environment, config }) {
375430
375509
  };
375431
375510
  });
375432
375511
  }, []);
375433
- const handleConfigCancel = useCallback(() => {
375512
+ const handleConfigCancel = useCallback3(() => {
375434
375513
  setState((s) => ({
375435
375514
  ...s,
375436
375515
  phase: "error",
375437
375516
  error: "Deployment cancelled - configs not provided"
375438
375517
  }));
375439
375518
  }, []);
375440
- useEffect4(() => {
375519
+ useEffect6(() => {
375441
375520
  const {
375442
375521
  phase: phase2,
375443
375522
  deployment: deployment2,
@@ -375479,7 +375558,7 @@ function DeployUI({ environment, config }) {
375479
375558
  }
375480
375559
  })();
375481
375560
  }, [state]);
375482
- useEffect4(() => {
375561
+ useEffect6(() => {
375483
375562
  const {
375484
375563
  phase: phase2,
375485
375564
  deployment: deployment2,
@@ -375521,7 +375600,7 @@ function DeployUI({ environment, config }) {
375521
375600
  }
375522
375601
  })();
375523
375602
  }, [state]);
375524
- useEffect4(() => {
375603
+ useEffect6(() => {
375525
375604
  if (state.phase !== "creating-tarball" || !state.projectId) return;
375526
375605
  let cancelled = false;
375527
375606
  async function runDeploy() {
@@ -375557,7 +375636,14 @@ function DeployUI({ environment, config }) {
375557
375636
  let deployment2;
375558
375637
  try {
375559
375638
  writeLog("deploy", `Creating deployment for project ${state.projectId}`);
375560
- deployment2 = await client2.createDeployment(state.projectId, environment);
375639
+ const gitInfo = getGitInfo(projectDir);
375640
+ deployment2 = await client2.createDeployment(state.projectId, environment, {
375641
+ triggeredBy: "cli",
375642
+ ...gitInfo && {
375643
+ gitCommitSha: gitInfo.commitSha,
375644
+ gitBranch: gitInfo.branch
375645
+ }
375646
+ });
375561
375647
  writeLog("deploy", `Deployment created: ${deployment2.id}`);
375562
375648
  } catch (err) {
375563
375649
  const errorMsg = `Failed to create deployment: ${err instanceof Error ? err.message : String(err)}`;
@@ -375594,7 +375680,7 @@ function DeployUI({ environment, config }) {
375594
375680
  cancelled = true;
375595
375681
  };
375596
375682
  }, [state.projectId, environment, config.builds]);
375597
- useEffect4(() => {
375683
+ useEffect6(() => {
375598
375684
  if (state.phase !== "pending" || !state.deployment) return;
375599
375685
  let pollInterval;
375600
375686
  let cancelled = false;
@@ -375703,7 +375789,7 @@ function DeployUI({ environment, config }) {
375703
375789
  if (pollInterval) clearInterval(pollInterval);
375704
375790
  };
375705
375791
  }, [state.phase, state.deployment?.id]);
375706
- useEffect4(() => {
375792
+ useEffect6(() => {
375707
375793
  if (state.phase !== "starting" || !state.deployment) return;
375708
375794
  let cancelled = false;
375709
375795
  const client2 = clientRef.current;
@@ -375733,7 +375819,7 @@ function DeployUI({ environment, config }) {
375733
375819
  cancelled = true;
375734
375820
  };
375735
375821
  }, [state.phase, state.deployment?.id]);
375736
- useEffect4(() => {
375822
+ useEffect6(() => {
375737
375823
  if (state.phase !== "queued" && state.phase !== "deploying" || !state.deployment) return;
375738
375824
  let pollInterval;
375739
375825
  let cancelled = false;
@@ -375786,12 +375872,12 @@ function DeployUI({ environment, config }) {
375786
375872
  if (pollInterval) clearInterval(pollInterval);
375787
375873
  };
375788
375874
  }, [state.phase, state.deployment?.id]);
375789
- useEffect4(() => {
375875
+ useEffect6(() => {
375790
375876
  if (state.phase === "creating-tarball") {
375791
375877
  trackEvent("deploy_started", { environment });
375792
375878
  }
375793
375879
  }, [state.phase, environment]);
375794
- useEffect4(() => {
375880
+ useEffect6(() => {
375795
375881
  if (state.phase === "success") {
375796
375882
  trackEvent("deploy_succeeded", { environment });
375797
375883
  closeDebugLog();
@@ -375998,7 +376084,14 @@ async function runDeployPipeline(options2) {
375998
376084
  console.log("Creating deployment...");
375999
376085
  let deployment;
376000
376086
  try {
376001
- deployment = await client2.createDeployment(projectId, "prod");
376087
+ const gitInfo = getGitInfo(projectDir);
376088
+ deployment = await client2.createDeployment(projectId, "prod", {
376089
+ triggeredBy: "cli",
376090
+ ...gitInfo && {
376091
+ gitCommitSha: gitInfo.commitSha,
376092
+ gitBranch: gitInfo.branch
376093
+ }
376094
+ });
376002
376095
  } catch (err) {
376003
376096
  console.error(`Error: Failed to create deployment: ${err instanceof Error ? err.message : String(err)}`);
376004
376097
  process.exit(1);
@@ -376222,7 +376315,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
376222
376315
  }
376223
376316
  const required = findRequiredResources(service);
376224
376317
  let resources = /* @__PURE__ */ new Map();
376225
- const hasRequiredResources = required.postgres.length > 0 || required.redis.length > 0 || required.storage.length > 0;
376318
+ const hasRequiredResources = required.postgres.length > 0 || required.redis.length > 0 || required.storage.length > 0 || required.temporal.length > 0 || required.mail.length > 0;
376226
376319
  stateManager = new InstanceStateManager(process.cwd(), instanceKey);
376227
376320
  await stateManager.cleanStaleState();
376228
376321
  const existingInstances = await stateManager.getExistingInstances();
@@ -376230,7 +376323,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
376230
376323
  if (hasRequiredResources) {
376231
376324
  if (existingInstances) {
376232
376325
  spinner = startSpinner("Starting resources...");
376233
- const allRequiredNames = [...required.postgres, ...required.redis, ...required.storage];
376326
+ const allRequiredNames = [...required.postgres, ...required.redis, ...required.storage, ...required.temporal, ...required.mail];
376234
376327
  for (const name of allRequiredNames) {
376235
376328
  const dbState = existingInstances.databases[name];
376236
376329
  if (!dbState) {
@@ -376240,9 +376333,16 @@ async function execCommand(serviceName, command, instanceKey = "default") {
376240
376333
  );
376241
376334
  process.exit(1);
376242
376335
  }
376336
+ const engineToType = {
376337
+ object_store: "storage",
376338
+ postgres: "postgres",
376339
+ redis: "redis",
376340
+ temporal: "temporal",
376341
+ mail: "mail"
376342
+ };
376243
376343
  resources.set(name, {
376244
376344
  name,
376245
- type: dbState.engine === "object_store" ? "storage" : dbState.engine,
376345
+ type: engineToType[dbState.engine] ?? dbState.engine,
376246
376346
  port: dbState.port,
376247
376347
  host: dbState.host,
376248
376348
  user: dbState.user,
@@ -376736,14 +376836,14 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
376736
376836
  }
376737
376837
 
376738
376838
  // src/commands/clean.tsx
376739
- import React8, { useState as useState7, useEffect as useEffect5 } from "react";
376839
+ import React8, { useState as useState7, useEffect as useEffect7 } from "react";
376740
376840
  import { render as render6, Text as Text8, Box as Box8 } from "ink";
376741
376841
  import Spinner5 from "ink-spinner";
376742
376842
  import * as fs26 from "fs";
376743
376843
  import * as path24 from "path";
376744
376844
  function CleanUI({ instanceKey }) {
376745
376845
  const [state, setState] = useState7({ status: "checking" });
376746
- useEffect5(() => {
376846
+ useEffect7(() => {
376747
376847
  async function clean() {
376748
376848
  const projectRoot = process.cwd();
376749
376849
  const specificDir = path24.join(projectRoot, ".specific");
@@ -376871,14 +376971,14 @@ async function loginCommand(options2 = {}) {
376871
376971
  }
376872
376972
 
376873
376973
  // src/commands/logout.tsx
376874
- import React9, { useState as useState8, useEffect as useEffect6 } from "react";
376974
+ import React9, { useState as useState8, useEffect as useEffect8 } from "react";
376875
376975
  import { render as render7, Text as Text9, useApp as useApp4 } from "ink";
376876
376976
  function LogoutUI() {
376877
376977
  const { exit } = useApp4();
376878
376978
  const [state, setState] = useState8({
376879
376979
  phase: "checking"
376880
376980
  });
376881
- useEffect6(() => {
376981
+ useEffect8(() => {
376882
376982
  if (state.phase !== "checking") return;
376883
376983
  if (!isLoggedIn()) {
376884
376984
  setState({ phase: "not-logged-in" });
@@ -376887,7 +376987,7 @@ function LogoutUI() {
376887
376987
  clearUserCredentials();
376888
376988
  setState({ phase: "done" });
376889
376989
  }, [state.phase]);
376890
- useEffect6(() => {
376990
+ useEffect8(() => {
376891
376991
  if (state.phase === "done" || state.phase === "not-logged-in") {
376892
376992
  const timer = setTimeout(() => exit(), 100);
376893
376993
  return () => clearTimeout(timer);
@@ -376906,7 +377006,7 @@ function logoutCommand() {
376906
377006
  }
376907
377007
 
376908
377008
  // src/commands/beta.tsx
376909
- import React10, { useState as useState9, useEffect as useEffect7 } from "react";
377009
+ import React10, { useState as useState9, useEffect as useEffect9 } from "react";
376910
377010
  import { render as render8, Text as Text10, Box as Box9, useInput as useInput5, useApp as useApp5 } from "ink";
376911
377011
  function BetaToggleUI() {
376912
377012
  const { exit } = useApp5();
@@ -376944,7 +377044,7 @@ function BetaToggleUI() {
376944
377044
  setSaved(true);
376945
377045
  }
376946
377046
  });
376947
- useEffect7(() => {
377047
+ useEffect9(() => {
376948
377048
  if (saved) {
376949
377049
  const timer = setTimeout(() => exit(), 100);
376950
377050
  return () => clearTimeout(timer);
@@ -376982,7 +377082,7 @@ function betaCommand() {
376982
377082
  }
376983
377083
 
376984
377084
  // src/commands/update.tsx
376985
- import React11, { useState as useState10, useEffect as useEffect8 } from "react";
377085
+ import React11, { useState as useState10, useEffect as useEffect10 } from "react";
376986
377086
  import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } from "ink";
376987
377087
  import Spinner6 from "ink-spinner";
376988
377088
 
@@ -377002,7 +377102,7 @@ function compareVersions(a, b) {
377002
377102
  return 0;
377003
377103
  }
377004
377104
  async function checkForUpdate() {
377005
- const currentVersion = "0.1.113";
377105
+ const currentVersion = "0.1.115";
377006
377106
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
377007
377107
  if (!response.ok) {
377008
377108
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -377098,7 +377198,7 @@ function maybeStartBackgroundUpdate() {
377098
377198
  function UpdateUI() {
377099
377199
  const { exit } = useApp6();
377100
377200
  const [state, setState] = useState10({ phase: "checking" });
377101
- useEffect8(() => {
377201
+ useEffect10(() => {
377102
377202
  if (state.phase !== "checking") return;
377103
377203
  let cancelled = false;
377104
377204
  async function check() {
@@ -377127,7 +377227,7 @@ function UpdateUI() {
377127
377227
  cancelled = true;
377128
377228
  };
377129
377229
  }, [state.phase]);
377130
- useEffect8(() => {
377230
+ useEffect10(() => {
377131
377231
  if (state.phase !== "downloading" || !state.checkResult) return;
377132
377232
  let cancelled = false;
377133
377233
  async function download() {
@@ -377159,7 +377259,7 @@ function UpdateUI() {
377159
377259
  cancelled = true;
377160
377260
  };
377161
377261
  }, [state.phase, state.checkResult]);
377162
- useEffect8(() => {
377262
+ useEffect10(() => {
377163
377263
  if (state.phase === "up-to-date" || state.phase === "success" || state.phase === "error" || state.phase === "permission-error") {
377164
377264
  const timer = setTimeout(() => exit(), 100);
377165
377265
  return () => clearTimeout(timer);
@@ -377270,7 +377370,7 @@ async function projectListCommand() {
377270
377370
  var program = new Command();
377271
377371
  var env = "production";
377272
377372
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
377273
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.113").enablePositionalOptions();
377373
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.115").enablePositionalOptions();
377274
377374
  program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
377275
377375
  Examples:
377276
377376
  $ specific init