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

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 +111 -40
  2. package/package.json +1 -1
package/dist/project.js CHANGED
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import kleur from "kleur";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { stdin as input, stdout as output } from "node:process";
6
- import { getInstafyProfileConfigPath, resolveControllerUrl, resolveUserAccessToken } from "./config.js";
6
+ import { getInstafyProfileConfigPath, resolveConfiguredStudioUrl, resolveControllerUrl, resolveUserAccessToken, } from "./config.js";
7
7
  import { formatAuthRequiredError } from "./errors.js";
8
8
  import { findProjectManifest } from "./project-manifest.js";
9
9
  async function fetchOrganizations(controllerUrl, token) {
@@ -32,12 +32,51 @@ async function fetchOrgProjects(controllerUrl, token, orgId) {
32
32
  const body = (await response.json());
33
33
  return Array.isArray(body.projects) ? body.projects : [];
34
34
  }
35
+ async function createOrganization(controllerUrl, token, payload) {
36
+ const response = await fetch(`${controllerUrl}/orgs`, {
37
+ method: "POST",
38
+ headers: {
39
+ authorization: `Bearer ${token}`,
40
+ "content-type": "application/json",
41
+ },
42
+ body: JSON.stringify(payload),
43
+ });
44
+ if (!response.ok) {
45
+ const text = await response.text().catch(() => "");
46
+ throw new Error(`Organization creation failed (${response.status} ${response.statusText}): ${text}`);
47
+ }
48
+ const json = (await response.json());
49
+ const orgId = json.org_id ?? json.orgId ?? json.id;
50
+ if (!orgId) {
51
+ throw new Error(`Organization creation response missing org id (expected org_id/orgId/id). Raw: ${JSON.stringify(json)}`);
52
+ }
53
+ return { orgId, orgName: json.org_name ?? json.orgName ?? null };
54
+ }
35
55
  async function resolveOrg(controllerUrl, token, options) {
36
56
  if (options.orgId) {
37
57
  return { orgId: options.orgId, orgName: options.orgName ?? null };
38
58
  }
39
59
  const orgSlug = options.orgSlug?.trim() || null;
40
60
  const orgName = options.orgName?.trim() || null;
61
+ const studioUrl = resolveConfiguredStudioUrl({ profile: options.profile ?? null }) ?? "https://staging.instafy.dev";
62
+ 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();
68
+ const payload = {
69
+ orgName: chosenName,
70
+ };
71
+ if (options.ownerUserId) {
72
+ payload.ownerUserId = options.ownerUserId;
73
+ }
74
+ if (enteredSlug) {
75
+ payload.orgSlug = enteredSlug;
76
+ }
77
+ const created = await createOrganization(controllerUrl, token, payload);
78
+ return { orgId: created.orgId, orgName: created.orgName ?? chosenName };
79
+ }
41
80
  if (orgSlug) {
42
81
  const orgs = await fetchOrganizations(controllerUrl, token);
43
82
  const matches = orgs.filter((org) => org.slug === orgSlug);
@@ -47,36 +86,84 @@ async function resolveOrg(controllerUrl, token, options) {
47
86
  }
48
87
  if (!orgSlug && !orgName) {
49
88
  const orgs = await fetchOrganizations(controllerUrl, token);
50
- if (orgs.length === 1) {
51
- return { orgId: orgs[0].id, orgName: orgs[0].name ?? null };
52
- }
53
- if (orgs.length > 1) {
54
- if (input.isTTY) {
55
- console.log(kleur.yellow("Multiple organizations found. Choose which one to use:"));
56
- orgs.forEach((org, index) => {
57
- const slug = org.slug ? ` · ${org.slug}` : "";
58
- console.log(` ${index + 1}) ${org.name}${slug} (${org.id})`);
59
- });
89
+ if (orgs.length === 0) {
90
+ if (allowInteractive) {
91
+ console.log(kleur.yellow("No organizations found for this account."));
92
+ console.log("");
93
+ console.log(`Create one in Studio: ${kleur.cyan(studioOrgUrl)}`);
60
94
  console.log("");
61
95
  const rl = createInterface({ input, output });
62
96
  try {
63
- while (true) {
64
- const answer = (await rl.question("Select org number: ")).trim();
65
- const selected = Number(answer);
66
- if (!Number.isFinite(selected) || selected < 1 || selected > orgs.length) {
67
- console.log(kleur.red("Invalid selection."));
68
- continue;
69
- }
70
- const picked = orgs[selected - 1];
71
- return { orgId: picked.id, orgName: picked.name ?? null };
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.");
72
100
  }
101
+ return await promptAndCreateOrg(rl);
73
102
  }
74
103
  finally {
75
104
  rl.close();
76
105
  }
77
106
  }
78
- throw new Error("Multiple organizations found.\n\nNext:\n- instafy org list\n- instafy project init --org-id <uuid>");
107
+ 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\"`);
79
108
  }
109
+ 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})`);
115
+ });
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
+ }
136
+ }
137
+ finally {
138
+ rl.close();
139
+ }
140
+ }
141
+ if (orgs.length === 1) {
142
+ return { orgId: orgs[0].id, orgName: orgs[0].name ?? null };
143
+ }
144
+ throw new Error("Multiple organizations found.\n\nNext:\n- instafy org list\n- instafy project init --org-id <uuid>");
145
+ }
146
+ if (orgSlug && !orgName) {
147
+ 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 };
161
+ }
162
+ finally {
163
+ rl.close();
164
+ }
165
+ }
166
+ 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}"`);
80
167
  }
81
168
  const payload = {};
82
169
  if (orgName) {
@@ -89,26 +176,10 @@ async function resolveOrg(controllerUrl, token, options) {
89
176
  payload.ownerUserId = options.ownerUserId;
90
177
  }
91
178
  if (!payload.orgName) {
92
- payload.orgName = "Personal";
93
- }
94
- const response = await fetch(`${controllerUrl}/orgs`, {
95
- method: "POST",
96
- headers: {
97
- authorization: `Bearer ${token}`,
98
- "content-type": "application/json",
99
- },
100
- body: JSON.stringify(payload),
101
- });
102
- if (!response.ok) {
103
- const text = await response.text().catch(() => "");
104
- throw new Error(`Organization creation failed (${response.status} ${response.statusText}): ${text}`);
179
+ throw new Error(`Organization name is required.\n\nNext:\n- Create an org in Studio: ${studioOrgUrl}\n- Or rerun: instafy project init --org-name \"My Org\"`);
105
180
  }
106
- const json = (await response.json());
107
- const orgId = json.org_id ?? json.orgId ?? json.id;
108
- if (!orgId) {
109
- throw new Error(`Organization creation response missing org id (expected org_id/orgId/id). Raw: ${JSON.stringify(json)}`);
110
- }
111
- return { orgId, orgName: json.org_name ?? json.orgName ?? null };
181
+ const created = await createOrganization(controllerUrl, token, payload);
182
+ return { orgId: created.orgId, orgName: created.orgName ?? orgName ?? null };
112
183
  }
113
184
  export async function listProjects(options) {
114
185
  const controllerUrl = resolveControllerUrl({ controllerUrl: options.controllerUrl ?? null });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instafy/cli",
3
- "version": "0.1.8-staging.370",
3
+ "version": "0.1.8-staging.372",
4
4
  "description": "Run Instafy projects locally, link folders to Studio, and share previews/webhooks via tunnels.",
5
5
  "private": false,
6
6
  "publishConfig": {