@specific.dev/cli 0.1.74 → 0.1.75

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 (68) 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 +102 -36
  64. package/dist/postinstall.js +1 -1
  65. package/package.json +1 -1
  66. /package/dist/admin/_next/static/{rlz8iRhM93pN2MuVf1ScD → ntsk0waJiBFcaeQ8sbm0K}/_buildManifest.js +0 -0
  67. /package/dist/admin/_next/static/{rlz8iRhM93pN2MuVf1ScD → ntsk0waJiBFcaeQ8sbm0K}/_clientMiddlewareManifest.json +0 -0
  68. /package/dist/admin/_next/static/{rlz8iRhM93pN2MuVf1ScD → ntsk0waJiBFcaeQ8sbm0K}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -183485,7 +183485,11 @@ function caInstalledInTrustStore() {
183485
183485
  } else if (platform6 === "linux") {
183486
183486
  const trustPaths = [
183487
183487
  "/usr/local/share/ca-certificates/specific-local-ca.crt",
183488
- "/etc/pki/ca-trust/source/anchors/specific-local-ca.crt"
183488
+ // Debian/Ubuntu
183489
+ "/etc/pki/ca-trust/source/anchors/specific-local-ca.crt",
183490
+ // RHEL/Fedora
183491
+ "/etc/ca-certificates/trust-source/anchors/specific-local-ca.crt"
183492
+ // Arch
183489
183493
  ];
183490
183494
  for (const trustPath of trustPaths) {
183491
183495
  if (fs.existsSync(trustPath)) {
@@ -183609,6 +183613,11 @@ function getCAInstallCommands(certPath) {
183609
183613
  `cp "${certPath}" /etc/pki/ca-trust/source/anchors/specific-local-ca.crt`,
183610
183614
  "update-ca-trust extract"
183611
183615
  ];
183616
+ } else if (fs.existsSync("/etc/ca-certificates/trust-source/anchors")) {
183617
+ return [
183618
+ `cp "${certPath}" /etc/ca-certificates/trust-source/anchors/specific-local-ca.crt`,
183619
+ "trust extract-compat"
183620
+ ];
183612
183621
  }
183613
183622
  throw new Error("Could not detect Linux certificate trust mechanism");
183614
183623
  }
@@ -184362,9 +184371,12 @@ var ApiClient = class {
184362
184371
  }
184363
184372
  return response.json();
184364
184373
  }
184365
- async createProject(name) {
184374
+ async createProject(name, organizationId) {
184366
184375
  const url = `${this.baseUrl}/projects`;
184367
184376
  const requestBody = { name };
184377
+ if (organizationId) {
184378
+ requestBody.organizationId = organizationId;
184379
+ }
184368
184380
  writeLog("api", `POST ${url}`);
184369
184381
  writeLog("api", `Request body: ${JSON.stringify(requestBody)}`);
184370
184382
  const response = await fetch(url, {
@@ -184399,6 +184411,31 @@ var ApiClient = class {
184399
184411
  }
184400
184412
  return response.json();
184401
184413
  }
184414
+ async listOrganizations() {
184415
+ const url = `${this.baseUrl}/user/organizations`;
184416
+ writeLog("api", `GET ${url}`);
184417
+ const response = await fetch(url, {
184418
+ headers: await this.authHeaders()
184419
+ });
184420
+ writeLog("api", `Response: ${response.status} ${response.statusText}`);
184421
+ if (!response.ok) {
184422
+ let errorBody;
184423
+ try {
184424
+ const error = await response.json();
184425
+ errorBody = JSON.stringify(error);
184426
+ writeLog("api:error", `API error: ${error.error} (${error.code})`);
184427
+ throw new Error(`Failed to list organizations: ${error.error} (${error.code})`);
184428
+ } catch (e) {
184429
+ if (e instanceof Error && e.message.startsWith("Failed to list organizations")) {
184430
+ throw e;
184431
+ }
184432
+ errorBody = await response.text();
184433
+ writeLog("api:error", `Failed to parse error response: ${errorBody}`);
184434
+ throw new Error(`Failed to list organizations: ${response.statusText}`);
184435
+ }
184436
+ }
184437
+ return response.json();
184438
+ }
184402
184439
  async getMe(signal) {
184403
184440
  const url = `${this.baseUrl}/users/me`;
184404
184441
  writeLog("api", `GET ${url}`);
@@ -184587,7 +184624,7 @@ function trackEvent(event, properties) {
184587
184624
  event,
184588
184625
  properties: {
184589
184626
  ...properties,
184590
- cli_version: "0.1.74",
184627
+ cli_version: "0.1.75",
184591
184628
  platform: process.platform,
184592
184629
  node_version: process.version,
184593
184630
  project_id: getProjectId()
@@ -192866,22 +192903,35 @@ function PhaseIndicator({
192866
192903
  }
192867
192904
  return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " ", "\u25CB"), " ", /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, label));
192868
192905
  }
192906
+ function buildSelectorItems(projects, organizations) {
192907
+ const items = [];
192908
+ for (const org of organizations) {
192909
+ items.push({ type: "header", orgName: org.name });
192910
+ items.push({ type: "new", orgId: org.id });
192911
+ const orgProjects = projects.filter((p) => p.organizationId === org.id);
192912
+ for (const project of orgProjects) {
192913
+ items.push({ type: "project", project });
192914
+ }
192915
+ }
192916
+ return items;
192917
+ }
192869
192918
  function ProjectSelector({
192870
192919
  projects,
192920
+ organizations,
192871
192921
  selectedIndex,
192872
192922
  onSelect,
192873
192923
  onUp,
192874
192924
  onDown
192875
192925
  }) {
192926
+ const items = buildSelectorItems(projects, organizations);
192927
+ const selectableIndices = items.map((item, index) => item.type !== "header" ? index : -1).filter((i) => i >= 0);
192876
192928
  useInput5((input, key) => {
192877
192929
  if (key.return) {
192878
- if (selectedIndex === 0) {
192879
- onSelect("new");
192880
- } else {
192881
- const project = projects[selectedIndex - 1];
192882
- if (project) {
192883
- onSelect(project);
192884
- }
192930
+ const item = items[selectedIndex];
192931
+ if (item?.type === "new") {
192932
+ onSelect({ type: "new", orgId: item.orgId });
192933
+ } else if (item?.type === "project") {
192934
+ onSelect(item.project);
192885
192935
  }
192886
192936
  } else if (key.upArrow) {
192887
192937
  onUp();
@@ -192889,11 +192939,16 @@ function ProjectSelector({
192889
192939
  onDown();
192890
192940
  }
192891
192941
  });
192892
- const items = [
192893
- { id: "new", name: "Create new project", isNew: true },
192894
- ...projects.map((p) => ({ ...p, isNew: false }))
192895
- ];
192896
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Select a project to deploy:"), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, items.map((item, index) => /* @__PURE__ */ React7.createElement(Text7, { key: item.id }, index === selectedIndex ? /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, "> ") : /* @__PURE__ */ React7.createElement(Text7, null, " "), item.isNew ? /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, item.name) : /* @__PURE__ */ React7.createElement(Text7, null, item.name, " ", /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "(", item.id, ")"))))), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Use arrow keys to navigate, Enter to select"));
192942
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Select a project to deploy:"), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, items.map((item, index) => {
192943
+ if (item.type === "header") {
192944
+ return /* @__PURE__ */ React7.createElement(Text7, { key: `header-${item.orgName}` }, index > 0 ? "\n" : "", /* @__PURE__ */ React7.createElement(Text7, { bold: true }, item.orgName));
192945
+ }
192946
+ const isSelected = index === selectedIndex;
192947
+ if (item.type === "new") {
192948
+ return /* @__PURE__ */ React7.createElement(Text7, { key: `new-${item.orgId}` }, isSelected ? /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, " ", "> ") : /* @__PURE__ */ React7.createElement(Text7, null, " "), /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Create new project"));
192949
+ }
192950
+ return /* @__PURE__ */ React7.createElement(Text7, { key: item.project.id }, isSelected ? /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, " ", "> ") : /* @__PURE__ */ React7.createElement(Text7, null, " "), /* @__PURE__ */ React7.createElement(Text7, null, item.project.name, " ", /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "(", item.project.id, ")")));
192951
+ })), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Use arrow keys to navigate, Enter to select"));
192897
192952
  }
192898
192953
  function NameInput({ onSubmit, onCancel }) {
192899
192954
  const [value, setValue] = useState6("");
@@ -193040,12 +193095,18 @@ function DeployUI({ environment, config }) {
193040
193095
  async function loadProjects() {
193041
193096
  try {
193042
193097
  const client2 = new ApiClient();
193043
- const { projects: projects2 } = await client2.listProjects();
193098
+ const [{ projects: projects2 }, { organizations }] = await Promise.all([
193099
+ client2.listProjects(),
193100
+ client2.listOrganizations()
193101
+ ]);
193044
193102
  if (cancelled) return;
193103
+ const items = buildSelectorItems(projects2, organizations);
193104
+ const firstSelectable = items.findIndex((item) => item.type !== "header");
193045
193105
  setState({
193046
193106
  phase: "selecting-project",
193047
193107
  projects: projects2,
193048
- selectedIndex: 0
193108
+ organizations,
193109
+ selectedIndex: firstSelectable >= 0 ? firstSelectable : 0
193049
193110
  });
193050
193111
  } catch (err) {
193051
193112
  if (cancelled) return;
@@ -193062,13 +193123,14 @@ function DeployUI({ environment, config }) {
193062
193123
  }, [state.phase]);
193063
193124
  const handleProjectSelect = useCallback(
193064
193125
  (project) => {
193065
- if (project === "new") {
193066
- setState((s) => ({ ...s, phase: "entering-name" }));
193126
+ if ("type" in project && project.type === "new") {
193127
+ setState((s) => ({ ...s, phase: "entering-name", selectedOrganizationId: project.orgId }));
193067
193128
  } else {
193068
- writeProjectId(project.id);
193129
+ const proj = project;
193130
+ writeProjectId(proj.id);
193069
193131
  setState({
193070
193132
  phase: "creating-tarball",
193071
- projectId: project.id
193133
+ projectId: proj.id
193072
193134
  });
193073
193135
  }
193074
193136
  },
@@ -193086,7 +193148,7 @@ function DeployUI({ environment, config }) {
193086
193148
  async function createProject() {
193087
193149
  try {
193088
193150
  const client2 = new ApiClient();
193089
- const project = await client2.createProject(state.newProjectName);
193151
+ const project = await client2.createProject(state.newProjectName, state.selectedOrganizationId);
193090
193152
  if (cancelled) return;
193091
193153
  writeProjectId(project.id);
193092
193154
  setState({
@@ -193562,24 +193624,28 @@ function DeployUI({ environment, config }) {
193562
193624
  if (phase === "loading-projects") {
193563
193625
  return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading projects..."));
193564
193626
  }
193565
- if (phase === "selecting-project" && projects && selectedIndex !== void 0) {
193627
+ if (phase === "selecting-project" && projects && state.organizations && selectedIndex !== void 0) {
193628
+ const selectorItems = buildSelectorItems(projects, state.organizations);
193629
+ const selectableIndices = selectorItems.map((item, index) => item.type !== "header" ? index : -1).filter((i) => i >= 0);
193566
193630
  return /* @__PURE__ */ React7.createElement(
193567
193631
  ProjectSelector,
193568
193632
  {
193569
193633
  projects,
193634
+ organizations: state.organizations,
193570
193635
  selectedIndex,
193571
193636
  onSelect: handleProjectSelect,
193572
- onUp: () => setState((s) => ({
193573
- ...s,
193574
- selectedIndex: Math.max(0, (s.selectedIndex || 0) - 1)
193575
- })),
193576
- onDown: () => setState((s) => ({
193577
- ...s,
193578
- selectedIndex: Math.min(
193579
- s.projects?.length || 0,
193580
- (s.selectedIndex || 0) + 1
193581
- )
193582
- }))
193637
+ onUp: () => setState((s) => {
193638
+ const idx = s.selectedIndex ?? 0;
193639
+ const currentPos = selectableIndices.indexOf(idx);
193640
+ const prevPos = Math.max(0, currentPos - 1);
193641
+ return { ...s, selectedIndex: selectableIndices[prevPos] ?? idx };
193642
+ }),
193643
+ onDown: () => setState((s) => {
193644
+ const idx = s.selectedIndex ?? 0;
193645
+ const currentPos = selectableIndices.indexOf(idx);
193646
+ const nextPos = Math.min(selectableIndices.length - 1, currentPos + 1);
193647
+ return { ...s, selectedIndex: selectableIndices[nextPos] ?? idx };
193648
+ })
193583
193649
  }
193584
193650
  );
193585
193651
  }
@@ -194482,7 +194548,7 @@ function compareVersions(a, b) {
194482
194548
  return 0;
194483
194549
  }
194484
194550
  async function checkForUpdate() {
194485
- const currentVersion = "0.1.74";
194551
+ const currentVersion = "0.1.75";
194486
194552
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
194487
194553
  if (!response.ok) {
194488
194554
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -194681,7 +194747,7 @@ function updateCommand() {
194681
194747
  var program = new Command();
194682
194748
  var env = "production";
194683
194749
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
194684
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.74").enablePositionalOptions();
194750
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.75").enablePositionalOptions();
194685
194751
  program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").action((options2) => initCommand(options2));
194686
194752
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
194687
194753
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
@@ -126,7 +126,7 @@ function trackEvent(event, properties) {
126
126
  event,
127
127
  properties: {
128
128
  ...properties,
129
- cli_version: "0.1.74",
129
+ cli_version: "0.1.75",
130
130
  platform: process.platform,
131
131
  node_version: process.version,
132
132
  project_id: getProjectId()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/cli",
3
- "version": "0.1.74",
3
+ "version": "0.1.75",
4
4
  "description": "CLI for Specific infrastructure-as-code",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",