@instafy/cli 0.1.8-staging.372 → 0.1.8-staging.374

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/project.js +74 -64
  2. package/package.json +2 -1
package/dist/project.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import kleur from "kleur";
4
- import { createInterface } from "node:readline/promises";
5
- import { stdin as input, stdout as output } from "node:process";
4
+ import { stdin as input } from "node:process";
6
5
  import { getInstafyProfileConfigPath, resolveConfiguredStudioUrl, resolveControllerUrl, resolveUserAccessToken, } from "./config.js";
7
6
  import { formatAuthRequiredError } from "./errors.js";
8
7
  import { findProjectManifest } from "./project-manifest.js";
8
+ let promptsModule = null;
9
+ async function loadPrompts() {
10
+ promptsModule ?? (promptsModule = import("@clack/prompts"));
11
+ return promptsModule;
12
+ }
9
13
  async function fetchOrganizations(controllerUrl, token) {
10
14
  const response = await fetch(`${controllerUrl}/orgs`, {
11
15
  headers: {
@@ -60,19 +64,32 @@ async function resolveOrg(controllerUrl, token, options) {
60
64
  const orgName = options.orgName?.trim() || null;
61
65
  const studioUrl = resolveConfiguredStudioUrl({ profile: options.profile ?? null }) ?? "https://staging.instafy.dev";
62
66
  const studioOrgUrl = `${studioUrl.replace(/\/$/, "")}/studio?panel=settings`;
63
- const allowInteractive = input.isTTY && options.json !== true;
64
- async function promptAndCreateOrg(rl) {
65
- const enteredName = (await rl.question("Organization name (default: Personal): ")).trim();
66
- const chosenName = enteredName || "Personal";
67
- const enteredSlug = (await rl.question("Organization slug (optional): ")).trim();
67
+ const allowInteractive = Boolean(input.isTTY && process.stdout.isTTY && options.json !== true && process.env.CI !== "true");
68
+ async function promptAndCreateOrg() {
69
+ const { isCancel, text } = await loadPrompts();
70
+ const enteredName = await text({
71
+ message: "Organization name",
72
+ defaultValue: "Personal",
73
+ });
74
+ if (isCancel(enteredName)) {
75
+ throw new Error("Cancelled.");
76
+ }
77
+ const chosenName = String(enteredName).trim() || "Personal";
78
+ const enteredSlug = await text({
79
+ message: "Organization slug (optional)",
80
+ });
81
+ if (isCancel(enteredSlug)) {
82
+ throw new Error("Cancelled.");
83
+ }
84
+ const chosenSlug = String(enteredSlug).trim();
68
85
  const payload = {
69
86
  orgName: chosenName,
70
87
  };
71
88
  if (options.ownerUserId) {
72
89
  payload.ownerUserId = options.ownerUserId;
73
90
  }
74
- if (enteredSlug) {
75
- payload.orgSlug = enteredSlug;
91
+ if (chosenSlug) {
92
+ payload.orgSlug = chosenSlug;
76
93
  }
77
94
  const created = await createOrganization(controllerUrl, token, payload);
78
95
  return { orgId: created.orgId, orgName: created.orgName ?? chosenName };
@@ -88,80 +105,73 @@ async function resolveOrg(controllerUrl, token, options) {
88
105
  const orgs = await fetchOrganizations(controllerUrl, token);
89
106
  if (orgs.length === 0) {
90
107
  if (allowInteractive) {
108
+ const { confirm, isCancel } = await loadPrompts();
91
109
  console.log(kleur.yellow("No organizations found for this account."));
92
110
  console.log("");
93
111
  console.log(`Create one in Studio: ${kleur.cyan(studioOrgUrl)}`);
94
112
  console.log("");
95
- const rl = createInterface({ input, output });
96
- try {
97
- const confirm = (await rl.question("Create a new organization now? (Y/n): ")).trim().toLowerCase();
98
- if (confirm && ["n", "no"].includes(confirm)) {
99
- throw new Error("No organization selected.");
100
- }
101
- return await promptAndCreateOrg(rl);
102
- }
103
- finally {
104
- rl.close();
113
+ const shouldCreate = await confirm({
114
+ message: "Create a new organization now?",
115
+ initialValue: true,
116
+ });
117
+ if (isCancel(shouldCreate) || !shouldCreate) {
118
+ throw new Error("No organization selected.");
105
119
  }
120
+ return await promptAndCreateOrg();
106
121
  }
107
122
  throw new Error(`No organizations found.\n\nNext:\n- Create an org in Studio: ${studioOrgUrl}\n- Or rerun: instafy project init --org-name \"My Org\"`);
108
123
  }
124
+ if (orgs.length === 1) {
125
+ return { orgId: orgs[0].id, orgName: orgs[0].name ?? null };
126
+ }
109
127
  if (allowInteractive) {
110
- console.log(kleur.yellow("Choose an organization for this project:"));
111
- console.log(` 0) ${kleur.cyan("+ Create a new organization")}`);
112
- orgs.forEach((org, index) => {
113
- const slug = org.slug ? ` · ${org.slug}` : "";
114
- console.log(` ${index + 1}) ${org.name}${slug} (${org.id})`);
128
+ const { isCancel, select } = await loadPrompts();
129
+ const selection = await select({
130
+ message: "Choose an organization for this project",
131
+ options: [
132
+ { value: "__create__", label: "+ Create a new organization" },
133
+ ...orgs.map((org) => ({
134
+ value: org.id,
135
+ label: org.name,
136
+ hint: `${org.slug ? `${org.slug} · ` : ""}${org.id}`,
137
+ })),
138
+ ],
139
+ initialValue: orgs[0].id,
115
140
  });
116
- console.log("");
117
- const rl = createInterface({ input, output });
118
- try {
119
- while (true) {
120
- const answer = (await rl.question("Select org number (default 1): ")).trim();
121
- if (!answer) {
122
- const picked = orgs[0];
123
- return { orgId: picked.id, orgName: picked.name ?? null };
124
- }
125
- const selected = Number(answer);
126
- if (selected === 0) {
127
- return await promptAndCreateOrg(rl);
128
- }
129
- if (!Number.isFinite(selected) || selected < 1 || selected > orgs.length) {
130
- console.log(kleur.red("Invalid selection."));
131
- continue;
132
- }
133
- const picked = orgs[selected - 1];
134
- return { orgId: picked.id, orgName: picked.name ?? null };
135
- }
141
+ if (isCancel(selection)) {
142
+ throw new Error("Cancelled.");
136
143
  }
137
- finally {
138
- rl.close();
144
+ if (selection === "__create__") {
145
+ return await promptAndCreateOrg();
139
146
  }
140
- }
141
- if (orgs.length === 1) {
142
- return { orgId: orgs[0].id, orgName: orgs[0].name ?? null };
147
+ const pickedOrg = orgs.find((org) => org.id === selection);
148
+ if (!pickedOrg) {
149
+ throw new Error("Selected organization not found.");
150
+ }
151
+ return { orgId: pickedOrg.id, orgName: pickedOrg.name ?? null };
143
152
  }
144
153
  throw new Error("Multiple organizations found.\n\nNext:\n- instafy org list\n- instafy project init --org-id <uuid>");
145
154
  }
146
155
  if (orgSlug && !orgName) {
147
156
  if (allowInteractive) {
148
- const rl = createInterface({ input, output });
149
- try {
150
- console.log(kleur.yellow("Organization slug did not match an existing org."));
151
- console.log(`Create one in Studio: ${kleur.cyan(studioOrgUrl)}`);
152
- console.log("");
153
- const enteredName = (await rl.question("Organization name (default: Personal): ")).trim();
154
- const chosenName = enteredName || "Personal";
155
- const payload = { orgName: chosenName, orgSlug };
156
- if (options.ownerUserId) {
157
- payload.ownerUserId = options.ownerUserId;
158
- }
159
- const created = await createOrganization(controllerUrl, token, payload);
160
- return { orgId: created.orgId, orgName: created.orgName ?? chosenName };
157
+ const { isCancel, text } = await loadPrompts();
158
+ console.log(kleur.yellow("Organization slug did not match an existing org."));
159
+ console.log(`Create one in Studio: ${kleur.cyan(studioOrgUrl)}`);
160
+ console.log("");
161
+ const enteredName = await text({
162
+ message: "Organization name",
163
+ defaultValue: "Personal",
164
+ });
165
+ if (isCancel(enteredName)) {
166
+ throw new Error("Cancelled.");
161
167
  }
162
- finally {
163
- rl.close();
168
+ const chosenName = String(enteredName).trim() || "Personal";
169
+ const payload = { orgName: chosenName, orgSlug };
170
+ if (options.ownerUserId) {
171
+ payload.ownerUserId = options.ownerUserId;
164
172
  }
173
+ const created = await createOrganization(controllerUrl, token, payload);
174
+ return { orgId: created.orgId, orgName: created.orgName ?? chosenName };
165
175
  }
166
176
  throw new Error(`Organization slug "${orgSlug}" did not match an existing org, and org name is required to create one.\n\nNext:\n- Create an org in Studio: ${studioOrgUrl}\n- Or rerun: instafy project init --org-name \"My Org\" --org-slug "${orgSlug}"`);
167
177
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instafy/cli",
3
- "version": "0.1.8-staging.372",
3
+ "version": "0.1.8-staging.374",
4
4
  "description": "Run Instafy projects locally, link folders to Studio, and share previews/webhooks via tunnels.",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -24,6 +24,7 @@
24
24
  "test:live": "pnpm build && TUNNEL_E2E_LIVE=1 vitest run test/tunnel-live.e2e.spec.ts"
25
25
  },
26
26
  "dependencies": {
27
+ "@clack/prompts": "^0.11.0",
27
28
  "commander": "^12.1.0",
28
29
  "kleur": "^4.1.5"
29
30
  },