@vocoder/cli 0.1.9 → 0.1.10
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/bin.mjs +206 -73
- package/dist/bin.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -663,8 +663,10 @@ var VocoderAPI = class {
|
|
|
663
663
|
}
|
|
664
664
|
}
|
|
665
665
|
// ── Workspaces ────────────────────────────────────────────────────────────────
|
|
666
|
-
async listWorkspaces(userToken) {
|
|
667
|
-
const
|
|
666
|
+
async listWorkspaces(userToken, params) {
|
|
667
|
+
const url = new URL(`${this.apiUrl}/api/cli/workspaces`);
|
|
668
|
+
if (params?.repo) url.searchParams.set("repo", params.repo);
|
|
669
|
+
const response = await fetch(url.toString(), {
|
|
668
670
|
headers: { Authorization: `Bearer ${userToken}` }
|
|
669
671
|
});
|
|
670
672
|
const payload = await readPayload(response);
|
|
@@ -677,6 +679,23 @@ var VocoderAPI = class {
|
|
|
677
679
|
}
|
|
678
680
|
return payload;
|
|
679
681
|
}
|
|
682
|
+
async listProjects(userToken, organizationId) {
|
|
683
|
+
const url = new URL(`${this.apiUrl}/api/cli/projects`);
|
|
684
|
+
url.searchParams.set("organizationId", organizationId);
|
|
685
|
+
const response = await fetch(url.toString(), {
|
|
686
|
+
headers: { Authorization: `Bearer ${userToken}` }
|
|
687
|
+
});
|
|
688
|
+
const payload = await readPayload(response);
|
|
689
|
+
if (!response.ok) {
|
|
690
|
+
throw new VocoderAPIError({
|
|
691
|
+
message: extractErrorMessage(payload, `Failed to list projects (${response.status})`),
|
|
692
|
+
status: response.status,
|
|
693
|
+
payload
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
const result = payload;
|
|
697
|
+
return result.projects;
|
|
698
|
+
}
|
|
680
699
|
// ── CLI GitHub endpoints ──────────────────────────────────────────────────────
|
|
681
700
|
async startCliGitHubInstall(userToken, params) {
|
|
682
701
|
const response = await fetch(`${this.apiUrl}/api/cli/github/install/start`, {
|
|
@@ -1930,105 +1949,219 @@ async function init(options = {}) {
|
|
|
1930
1949
|
selectedWorkspaceName = claimResult.organizationName;
|
|
1931
1950
|
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
1932
1951
|
} else {
|
|
1933
|
-
const workspaceData = await api.listWorkspaces(userToken
|
|
1934
|
-
|
|
1935
|
-
|
|
1952
|
+
const workspaceData = await api.listWorkspaces(userToken, {
|
|
1953
|
+
repo: identity?.repoCanonical
|
|
1954
|
+
});
|
|
1955
|
+
const repoCanonical = identity?.repoCanonical ?? null;
|
|
1956
|
+
const covering = repoCanonical ? workspaceData.workspaces.filter((w) => w.coversRepo === true) : [];
|
|
1957
|
+
const connected = workspaceData.workspaces.filter((w) => w.hasGitHubConnection);
|
|
1958
|
+
if (repoCanonical && covering.length === 1) {
|
|
1959
|
+
const ws = covering[0];
|
|
1936
1960
|
selectedWorkspaceId = ws.id;
|
|
1937
1961
|
selectedWorkspaceName = ws.name;
|
|
1938
1962
|
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
1939
|
-
} else {
|
|
1940
|
-
const
|
|
1941
|
-
|
|
1963
|
+
} else if (repoCanonical && covering.length > 1) {
|
|
1964
|
+
const choice = await p5.select({
|
|
1965
|
+
message: "Select workspace for this repo",
|
|
1966
|
+
options: covering.map((w) => ({
|
|
1967
|
+
value: w.id,
|
|
1968
|
+
label: `${w.name} ${chalk6.dim(`(${w.projectCount} project${w.projectCount !== 1 ? "s" : ""})`)}`
|
|
1969
|
+
}))
|
|
1970
|
+
});
|
|
1971
|
+
if (p5.isCancel(choice)) {
|
|
1942
1972
|
p5.cancel("Setup cancelled.");
|
|
1943
1973
|
return 1;
|
|
1944
1974
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1975
|
+
const ws = covering.find((w) => w.id === choice);
|
|
1976
|
+
selectedWorkspaceId = ws.id;
|
|
1977
|
+
selectedWorkspaceName = ws.name;
|
|
1978
|
+
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
1979
|
+
} else if (repoCanonical && covering.length === 0 && connected.length > 0) {
|
|
1980
|
+
const shortRepo = repoCanonical.split(":")[1] ?? repoCanonical;
|
|
1981
|
+
p5.log.warn(
|
|
1982
|
+
`${chalk6.bold(shortRepo)} isn't accessible from your Vocoder installation.
|
|
1983
|
+
Grant access to this repository or install on the account that owns it.`
|
|
1984
|
+
);
|
|
1985
|
+
const fixOptions = [];
|
|
1986
|
+
for (const ws of connected) {
|
|
1987
|
+
if (ws.installationConfigureUrl) {
|
|
1988
|
+
fixOptions.push({
|
|
1989
|
+
value: `grant:${ws.id}`,
|
|
1990
|
+
label: `Configure ${chalk6.bold(ws.connectionLabel ?? ws.name)}'s GitHub App installation`
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
fixOptions.push({
|
|
1995
|
+
value: "install_new",
|
|
1996
|
+
label: "Install on a different GitHub account"
|
|
1997
|
+
});
|
|
1998
|
+
fixOptions.push({ value: "cancel", label: "Cancel" });
|
|
1999
|
+
const fix = await p5.select({
|
|
2000
|
+
message: "How would you like to fix this?",
|
|
2001
|
+
options: fixOptions
|
|
2002
|
+
});
|
|
2003
|
+
if (p5.isCancel(fix) || fix === "cancel") {
|
|
2004
|
+
p5.cancel("Setup cancelled.");
|
|
2005
|
+
return 1;
|
|
2006
|
+
}
|
|
2007
|
+
if (fix.startsWith("grant:")) {
|
|
2008
|
+
const ws = connected.find((w) => `grant:${w.id}` === fix);
|
|
2009
|
+
await tryOpenBrowser2(ws.installationConfigureUrl);
|
|
2010
|
+
p5.cancel(
|
|
2011
|
+
`Grant access to ${chalk6.bold(shortRepo)} in your browser,
|
|
2012
|
+
then re-run ${chalk6.bold("vocoder init")}.`
|
|
2013
|
+
);
|
|
2014
|
+
return 1;
|
|
2015
|
+
}
|
|
2016
|
+
const connectResult = await runGitHubInstallFlow({ api, userToken, yes: options.yes });
|
|
2017
|
+
if (!connectResult) {
|
|
2018
|
+
p5.log.error("GitHub App installation did not complete. Run `vocoder init` again.");
|
|
2019
|
+
return 1;
|
|
2020
|
+
}
|
|
2021
|
+
selectedWorkspaceId = connectResult.organizationId;
|
|
2022
|
+
selectedWorkspaceName = connectResult.organizationName;
|
|
2023
|
+
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
2024
|
+
} else {
|
|
2025
|
+
if (workspaceData.workspaces.length === 1 && !workspaceData.canCreateWorkspace) {
|
|
2026
|
+
const ws = workspaceData.workspaces[0];
|
|
2027
|
+
selectedWorkspaceId = ws.id;
|
|
2028
|
+
selectedWorkspaceName = ws.name;
|
|
1948
2029
|
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
1949
2030
|
} else {
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
options: [
|
|
1953
|
-
{ value: "install", label: "Install the Vocoder GitHub App" },
|
|
1954
|
-
{ value: "link", label: "Link an existing installation" }
|
|
1955
|
-
]
|
|
1956
|
-
});
|
|
1957
|
-
if (p5.isCancel(connectChoice)) {
|
|
2031
|
+
const workspaceResult = await selectWorkspace(workspaceData);
|
|
2032
|
+
if (workspaceResult.action === "cancelled") {
|
|
1958
2033
|
p5.cancel("Setup cancelled.");
|
|
1959
2034
|
return 1;
|
|
1960
2035
|
}
|
|
1961
|
-
if (
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
userToken,
|
|
1965
|
-
yes: options.yes
|
|
1966
|
-
});
|
|
1967
|
-
if (!connectResult) {
|
|
1968
|
-
p5.log.error("GitHub App installation did not complete. Run `vocoder init` again.");
|
|
1969
|
-
return 1;
|
|
1970
|
-
}
|
|
1971
|
-
selectedWorkspaceId = connectResult.organizationId;
|
|
1972
|
-
selectedWorkspaceName = connectResult.organizationName;
|
|
2036
|
+
if (workspaceResult.action === "use") {
|
|
2037
|
+
selectedWorkspaceId = workspaceResult.workspace.id;
|
|
2038
|
+
selectedWorkspaceName = workspaceResult.workspace.name;
|
|
1973
2039
|
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
1974
2040
|
} else {
|
|
1975
|
-
const
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
2041
|
+
const connectChoice = await p5.select({
|
|
2042
|
+
message: "Connect your new workspace to GitHub",
|
|
2043
|
+
options: [
|
|
2044
|
+
{ value: "install", label: "Install the Vocoder GitHub App" },
|
|
2045
|
+
{ value: "link", label: "Link an existing installation" }
|
|
2046
|
+
]
|
|
1979
2047
|
});
|
|
1980
|
-
if (
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
const connectResult = await runGitHubInstallFlow({
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
}
|
|
1990
|
-
if (!connectResult) return 1;
|
|
2048
|
+
if (p5.isCancel(connectChoice)) {
|
|
2049
|
+
p5.cancel("Setup cancelled.");
|
|
2050
|
+
return 1;
|
|
2051
|
+
}
|
|
2052
|
+
if (connectChoice === "install") {
|
|
2053
|
+
const connectResult = await runGitHubInstallFlow({ api, userToken, yes: options.yes });
|
|
2054
|
+
if (!connectResult) {
|
|
2055
|
+
p5.log.error("GitHub App installation did not complete. Run `vocoder init` again.");
|
|
2056
|
+
return 1;
|
|
2057
|
+
}
|
|
1991
2058
|
selectedWorkspaceId = connectResult.organizationId;
|
|
1992
2059
|
selectedWorkspaceName = connectResult.organizationName;
|
|
2060
|
+
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
1993
2061
|
} else {
|
|
1994
|
-
const
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
})),
|
|
2002
|
-
true
|
|
2003
|
-
);
|
|
2004
|
-
if (selectedInstallationId === null) {
|
|
2005
|
-
p5.cancel("Setup cancelled.");
|
|
2006
|
-
return 1;
|
|
2007
|
-
}
|
|
2008
|
-
if (selectedInstallationId === "install_new") {
|
|
2009
|
-
const connectResult = await runGitHubInstallFlow({
|
|
2010
|
-
api,
|
|
2011
|
-
userToken,
|
|
2012
|
-
yes: options.yes
|
|
2013
|
-
});
|
|
2062
|
+
const installations = await runGitHubDiscoveryFlow({ api, userToken, yes: options.yes });
|
|
2063
|
+
if (!installations) return 1;
|
|
2064
|
+
if (installations.length === 0) {
|
|
2065
|
+
p5.log.warn("No GitHub installations found. Install the Vocoder GitHub App first.");
|
|
2066
|
+
const installNow = await p5.confirm({ message: "Open GitHub to install the App?" });
|
|
2067
|
+
if (p5.isCancel(installNow) || !installNow) return 1;
|
|
2068
|
+
const connectResult = await runGitHubInstallFlow({ api, userToken, yes: options.yes });
|
|
2014
2069
|
if (!connectResult) return 1;
|
|
2015
2070
|
selectedWorkspaceId = connectResult.organizationId;
|
|
2016
2071
|
selectedWorkspaceName = connectResult.organizationName;
|
|
2017
2072
|
} else {
|
|
2018
|
-
const
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2073
|
+
const selectedInstallationId = await selectGitHubInstallation(
|
|
2074
|
+
installations.map((inst) => ({
|
|
2075
|
+
installationId: inst.installationId,
|
|
2076
|
+
accountLogin: inst.accountLogin,
|
|
2077
|
+
accountType: inst.accountType,
|
|
2078
|
+
isSuspended: inst.isSuspended,
|
|
2079
|
+
conflictLabel: inst.conflictLabel
|
|
2080
|
+
})),
|
|
2081
|
+
true
|
|
2082
|
+
);
|
|
2083
|
+
if (selectedInstallationId === null) {
|
|
2084
|
+
p5.cancel("Setup cancelled.");
|
|
2085
|
+
return 1;
|
|
2086
|
+
}
|
|
2087
|
+
if (selectedInstallationId === "install_new") {
|
|
2088
|
+
const connectResult = await runGitHubInstallFlow({ api, userToken, yes: options.yes });
|
|
2089
|
+
if (!connectResult) return 1;
|
|
2090
|
+
selectedWorkspaceId = connectResult.organizationId;
|
|
2091
|
+
selectedWorkspaceName = connectResult.organizationName;
|
|
2092
|
+
} else {
|
|
2093
|
+
const claimResult = await api.claimCliGitHubInstallation(userToken, {
|
|
2094
|
+
installationId: String(selectedInstallationId),
|
|
2095
|
+
organizationId: null
|
|
2096
|
+
});
|
|
2097
|
+
selectedWorkspaceId = claimResult.organizationId;
|
|
2098
|
+
selectedWorkspaceName = claimResult.organizationName;
|
|
2099
|
+
}
|
|
2024
2100
|
}
|
|
2101
|
+
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
2025
2102
|
}
|
|
2026
|
-
p5.log.success(`Workspace: ${chalk6.bold(selectedWorkspaceName)}`);
|
|
2027
2103
|
}
|
|
2028
2104
|
}
|
|
2029
2105
|
}
|
|
2030
2106
|
}
|
|
2031
2107
|
}
|
|
2108
|
+
try {
|
|
2109
|
+
const wsCheck = await api.listWorkspaces(userToken);
|
|
2110
|
+
const ws = wsCheck.workspaces.find((w) => w.id === selectedWorkspaceId);
|
|
2111
|
+
if (ws && ws.maxProjects !== -1 && ws.projectCount >= ws.maxProjects) {
|
|
2112
|
+
p5.log.warn(
|
|
2113
|
+
`Project limit reached \u2014 ${ws.projectCount}/${ws.maxProjects} on your ${chalk6.bold(ws.planId)} plan.`
|
|
2114
|
+
);
|
|
2115
|
+
const limitAction = await p5.select({
|
|
2116
|
+
message: "What would you like to do?",
|
|
2117
|
+
options: [
|
|
2118
|
+
{ value: "upgrade", label: "Upgrade plan" },
|
|
2119
|
+
{ value: "existing", label: "Use an existing project in this workspace" },
|
|
2120
|
+
{ value: "cancel", label: "Cancel" }
|
|
2121
|
+
]
|
|
2122
|
+
});
|
|
2123
|
+
if (p5.isCancel(limitAction) || limitAction === "cancel") {
|
|
2124
|
+
p5.cancel("Setup cancelled.");
|
|
2125
|
+
return 1;
|
|
2126
|
+
}
|
|
2127
|
+
if (limitAction === "upgrade") {
|
|
2128
|
+
await tryOpenBrowser2(`${apiUrl}/dashboard/billing`);
|
|
2129
|
+
p5.cancel("Upgrade your plan in the browser, then re-run `vocoder init`.");
|
|
2130
|
+
return 1;
|
|
2131
|
+
}
|
|
2132
|
+
const existingProjects = await api.listProjects(userToken, selectedWorkspaceId);
|
|
2133
|
+
if (existingProjects.length === 0) {
|
|
2134
|
+
p5.log.error("No projects found in this workspace.");
|
|
2135
|
+
return 1;
|
|
2136
|
+
}
|
|
2137
|
+
const chosenId = await p5.select({
|
|
2138
|
+
message: "Select a project",
|
|
2139
|
+
options: existingProjects.map((proj) => ({
|
|
2140
|
+
value: proj.id,
|
|
2141
|
+
label: proj.name,
|
|
2142
|
+
hint: `${proj.sourceLocale} \u2192 ${proj.targetLocales.join(", ")}`
|
|
2143
|
+
}))
|
|
2144
|
+
});
|
|
2145
|
+
if (p5.isCancel(chosenId)) {
|
|
2146
|
+
p5.cancel("Setup cancelled.");
|
|
2147
|
+
return 1;
|
|
2148
|
+
}
|
|
2149
|
+
const chosen = existingProjects.find((proj) => proj.id === chosenId);
|
|
2150
|
+
runScaffold({
|
|
2151
|
+
projectName: chosen.name,
|
|
2152
|
+
organizationName: selectedWorkspaceName,
|
|
2153
|
+
sourceLocale: chosen.sourceLocale,
|
|
2154
|
+
translationTriggers: chosen.translationTriggers
|
|
2155
|
+
});
|
|
2156
|
+
p5.log.info(
|
|
2157
|
+
`Get your project API key at:
|
|
2158
|
+
${apiUrl}/dashboard/projects/${chosen.id}/settings`
|
|
2159
|
+
);
|
|
2160
|
+
p5.outro("You're all set.");
|
|
2161
|
+
return 0;
|
|
2162
|
+
}
|
|
2163
|
+
} catch {
|
|
2164
|
+
}
|
|
2032
2165
|
const projectResult = await runProjectCreate({
|
|
2033
2166
|
api,
|
|
2034
2167
|
userToken,
|