@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.
- package/dist/project.js +111 -40
- 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 ===
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
107
|
-
|
|
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