@vocoder/cli 0.10.0 → 0.12.0
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/README.md +137 -1
- package/dist/bin.mjs +669 -1049
- package/dist/bin.mjs.map +1 -1
- package/dist/{chunk-73U4VZYP.mjs → chunk-XUCVAFBG.mjs} +800 -37
- package/dist/chunk-XUCVAFBG.mjs.map +1 -0
- package/dist/lib.d.mts +358 -4
- package/dist/lib.mjs +15 -3
- package/dist/lib.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-73U4VZYP.mjs.map +0 -1
|
@@ -43568,6 +43568,730 @@ var require_brace_expansion = __commonJS({
|
|
|
43568
43568
|
}
|
|
43569
43569
|
});
|
|
43570
43570
|
|
|
43571
|
+
// src/utils/api.ts
|
|
43572
|
+
function isLimitErrorResponse(value) {
|
|
43573
|
+
if (!value || typeof value !== "object") {
|
|
43574
|
+
return false;
|
|
43575
|
+
}
|
|
43576
|
+
const candidate = value;
|
|
43577
|
+
return typeof candidate.errorCode === "string" && typeof candidate.limitType === "string" && typeof candidate.planId === "string" && typeof candidate.current === "number" && typeof candidate.required === "number" && typeof candidate.upgradeUrl === "string" && typeof candidate.message === "string";
|
|
43578
|
+
}
|
|
43579
|
+
function isSyncPolicyErrorResponse(value) {
|
|
43580
|
+
if (!value || typeof value !== "object") {
|
|
43581
|
+
return false;
|
|
43582
|
+
}
|
|
43583
|
+
const candidate = value;
|
|
43584
|
+
return (candidate.errorCode === "BRANCH_NOT_ALLOWED" || candidate.errorCode === "PROJECT_REPOSITORY_MISMATCH") && typeof candidate.message === "string";
|
|
43585
|
+
}
|
|
43586
|
+
function extractErrorMessage(payload, fallback) {
|
|
43587
|
+
if (!payload || typeof payload !== "object") {
|
|
43588
|
+
return fallback;
|
|
43589
|
+
}
|
|
43590
|
+
const candidate = payload;
|
|
43591
|
+
if (typeof candidate.message === "string") {
|
|
43592
|
+
return candidate.message;
|
|
43593
|
+
}
|
|
43594
|
+
if (typeof candidate.error === "string") {
|
|
43595
|
+
return candidate.error;
|
|
43596
|
+
}
|
|
43597
|
+
return fallback;
|
|
43598
|
+
}
|
|
43599
|
+
function parsePayload(raw) {
|
|
43600
|
+
if (raw.length === 0) {
|
|
43601
|
+
return null;
|
|
43602
|
+
}
|
|
43603
|
+
const trimmed = raw.trimStart();
|
|
43604
|
+
if (trimmed.startsWith("<!DOCTYPE") || trimmed.startsWith("<html")) {
|
|
43605
|
+
return {
|
|
43606
|
+
message: "Unexpected response from server (received HTML). Check your network connection or try again."
|
|
43607
|
+
};
|
|
43608
|
+
}
|
|
43609
|
+
try {
|
|
43610
|
+
return JSON.parse(raw);
|
|
43611
|
+
} catch {
|
|
43612
|
+
return { message: raw };
|
|
43613
|
+
}
|
|
43614
|
+
}
|
|
43615
|
+
async function readPayload(response) {
|
|
43616
|
+
if (typeof response.text === "function") {
|
|
43617
|
+
const raw = await response.text();
|
|
43618
|
+
return parsePayload(raw);
|
|
43619
|
+
}
|
|
43620
|
+
if (typeof response.json === "function") {
|
|
43621
|
+
return response.json();
|
|
43622
|
+
}
|
|
43623
|
+
return null;
|
|
43624
|
+
}
|
|
43625
|
+
var VocoderAPIError = class extends Error {
|
|
43626
|
+
constructor(params) {
|
|
43627
|
+
super(params.message);
|
|
43628
|
+
this.name = "VocoderAPIError";
|
|
43629
|
+
this.status = params.status;
|
|
43630
|
+
this.payload = params.payload;
|
|
43631
|
+
this.limitError = params.limitError ?? null;
|
|
43632
|
+
this.syncPolicyError = params.syncPolicyError ?? null;
|
|
43633
|
+
}
|
|
43634
|
+
};
|
|
43635
|
+
var VocoderAPI = class {
|
|
43636
|
+
constructor(config) {
|
|
43637
|
+
this.apiUrl = config.apiUrl;
|
|
43638
|
+
this.apiKey = config.apiKey;
|
|
43639
|
+
}
|
|
43640
|
+
async request(path2, init = {}, errorPrefix) {
|
|
43641
|
+
const response = await fetch(`${this.apiUrl}${path2}`, {
|
|
43642
|
+
...init,
|
|
43643
|
+
headers: {
|
|
43644
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
43645
|
+
...init.headers ?? {}
|
|
43646
|
+
}
|
|
43647
|
+
});
|
|
43648
|
+
const payload = await readPayload(response);
|
|
43649
|
+
if (!response.ok) {
|
|
43650
|
+
const limitError = isLimitErrorResponse(payload) ? payload : null;
|
|
43651
|
+
const syncPolicyError = isSyncPolicyErrorResponse(payload) ? payload : null;
|
|
43652
|
+
const baseMessage = extractErrorMessage(
|
|
43653
|
+
payload,
|
|
43654
|
+
`Request failed with status ${response.status}`
|
|
43655
|
+
);
|
|
43656
|
+
throw new VocoderAPIError({
|
|
43657
|
+
message: errorPrefix ? `${errorPrefix}: ${baseMessage}` : baseMessage,
|
|
43658
|
+
status: response.status,
|
|
43659
|
+
payload,
|
|
43660
|
+
limitError,
|
|
43661
|
+
syncPolicyError
|
|
43662
|
+
});
|
|
43663
|
+
}
|
|
43664
|
+
return payload;
|
|
43665
|
+
}
|
|
43666
|
+
/**
|
|
43667
|
+
* Fetch project configuration from API
|
|
43668
|
+
* Project is determined from the API key
|
|
43669
|
+
*/
|
|
43670
|
+
async getProjectConfig() {
|
|
43671
|
+
const data = await this.request("/api/cli/config", {}, "Failed to fetch project config");
|
|
43672
|
+
return {
|
|
43673
|
+
projectName: data.projectName,
|
|
43674
|
+
organizationName: data.organizationName,
|
|
43675
|
+
shortCode: data.shortCode,
|
|
43676
|
+
sourceLocale: data.sourceLocale,
|
|
43677
|
+
targetLocales: data.targetLocales,
|
|
43678
|
+
targetBranches: data.targetBranches ?? ["main"],
|
|
43679
|
+
primaryBranch: data.primaryBranch,
|
|
43680
|
+
syncPolicy: {
|
|
43681
|
+
blockingBranches: data.syncPolicy?.blockingBranches ?? [
|
|
43682
|
+
"main",
|
|
43683
|
+
"master"
|
|
43684
|
+
],
|
|
43685
|
+
blockingMode: data.syncPolicy?.blockingMode ?? "required",
|
|
43686
|
+
nonBlockingMode: data.syncPolicy?.nonBlockingMode ?? "best-effort",
|
|
43687
|
+
defaultMaxWaitMs: data.syncPolicy?.defaultMaxWaitMs ?? 6e4
|
|
43688
|
+
}
|
|
43689
|
+
};
|
|
43690
|
+
}
|
|
43691
|
+
/**
|
|
43692
|
+
* Submit strings for translation
|
|
43693
|
+
* Project is determined from the API key
|
|
43694
|
+
*/
|
|
43695
|
+
stableTextKey(text) {
|
|
43696
|
+
let hash = 2166136261;
|
|
43697
|
+
for (let i = 0; i < text.length; i++) {
|
|
43698
|
+
hash ^= text.charCodeAt(i);
|
|
43699
|
+
hash = Math.imul(hash, 16777619);
|
|
43700
|
+
}
|
|
43701
|
+
return `SK_TEXT_${(hash >>> 0).toString(16).toUpperCase().padStart(8, "0")}`;
|
|
43702
|
+
}
|
|
43703
|
+
normalizeStringEntries(entries) {
|
|
43704
|
+
if (entries.length === 0) {
|
|
43705
|
+
return [];
|
|
43706
|
+
}
|
|
43707
|
+
const first = entries[0];
|
|
43708
|
+
if (typeof first === "string") {
|
|
43709
|
+
return entries.map((text) => ({
|
|
43710
|
+
key: this.stableTextKey(text),
|
|
43711
|
+
text
|
|
43712
|
+
}));
|
|
43713
|
+
}
|
|
43714
|
+
return entries.map((entry, index) => ({
|
|
43715
|
+
key: entry.key || this.stableTextKey(`${entry.text}:${index}`),
|
|
43716
|
+
text: entry.text,
|
|
43717
|
+
...entry.context ? { context: entry.context } : {},
|
|
43718
|
+
...entry.formality ? { formality: entry.formality } : {},
|
|
43719
|
+
...entry.uiRole ? { uiRole: entry.uiRole } : {}
|
|
43720
|
+
}));
|
|
43721
|
+
}
|
|
43722
|
+
async submitTranslation(branch, entries, targetLocales, options, repoIdentity) {
|
|
43723
|
+
const stringEntries = this.normalizeStringEntries(entries);
|
|
43724
|
+
const strings = stringEntries.map((entry) => entry.text);
|
|
43725
|
+
const crypto = await import("crypto");
|
|
43726
|
+
const sortedStrings = [...strings].sort();
|
|
43727
|
+
const stringsHash = crypto.createHash("sha256").update(JSON.stringify(sortedStrings)).digest("hex");
|
|
43728
|
+
return this.request(
|
|
43729
|
+
"/api/cli/sync",
|
|
43730
|
+
{
|
|
43731
|
+
method: "POST",
|
|
43732
|
+
headers: {
|
|
43733
|
+
"Content-Type": "application/json"
|
|
43734
|
+
},
|
|
43735
|
+
body: JSON.stringify({
|
|
43736
|
+
branch,
|
|
43737
|
+
stringEntries,
|
|
43738
|
+
targetLocales,
|
|
43739
|
+
...options?.force ? {} : { stringsHash },
|
|
43740
|
+
...options?.requestedMode ? { requestedMode: options.requestedMode } : {},
|
|
43741
|
+
...typeof options?.requestedMaxWaitMs === "number" ? { requestedMaxWaitMs: options.requestedMaxWaitMs } : {},
|
|
43742
|
+
...options?.clientRunId ? { clientRunId: options.clientRunId } : {},
|
|
43743
|
+
...repoIdentity?.repoCanonical ? { repoCanonical: repoIdentity.repoCanonical } : {},
|
|
43744
|
+
...repoIdentity?.repoAppDir !== void 0 ? { repoAppDir: repoIdentity.repoAppDir } : {},
|
|
43745
|
+
...repoIdentity?.commitSha ? { commitSha: repoIdentity.commitSha } : {},
|
|
43746
|
+
...options?.appIndustry ? { appIndustry: options.appIndustry } : {}
|
|
43747
|
+
})
|
|
43748
|
+
},
|
|
43749
|
+
"Translation submission failed"
|
|
43750
|
+
);
|
|
43751
|
+
}
|
|
43752
|
+
/**
|
|
43753
|
+
* Check translation status
|
|
43754
|
+
*/
|
|
43755
|
+
async getTranslationStatus(batchId) {
|
|
43756
|
+
return this.request(
|
|
43757
|
+
`/api/cli/sync/status/${batchId}`,
|
|
43758
|
+
{},
|
|
43759
|
+
"Failed to check translation status"
|
|
43760
|
+
);
|
|
43761
|
+
}
|
|
43762
|
+
async getTranslationSnapshot(params) {
|
|
43763
|
+
const search = new URLSearchParams();
|
|
43764
|
+
search.set("branch", params.branch);
|
|
43765
|
+
for (const locale of params.targetLocales) {
|
|
43766
|
+
search.append("targetLocale", locale);
|
|
43767
|
+
}
|
|
43768
|
+
return this.request(
|
|
43769
|
+
`/api/cli/sync/snapshot?${search.toString()}`,
|
|
43770
|
+
{},
|
|
43771
|
+
"Failed to fetch translation snapshot"
|
|
43772
|
+
);
|
|
43773
|
+
}
|
|
43774
|
+
/**
|
|
43775
|
+
* Wait for translation to complete with polling
|
|
43776
|
+
*/
|
|
43777
|
+
async waitForCompletion(batchId, timeout = 6e4, onProgress) {
|
|
43778
|
+
const startTime = Date.now();
|
|
43779
|
+
const pollInterval = 1e3;
|
|
43780
|
+
while (Date.now() - startTime < timeout) {
|
|
43781
|
+
const status = await this.getTranslationStatus(batchId);
|
|
43782
|
+
if (onProgress) {
|
|
43783
|
+
onProgress(status.progress);
|
|
43784
|
+
}
|
|
43785
|
+
if (status.status === "COMPLETED") {
|
|
43786
|
+
if (!status.translations) {
|
|
43787
|
+
throw new Error("Translation completed but no translations returned");
|
|
43788
|
+
}
|
|
43789
|
+
return {
|
|
43790
|
+
translations: status.translations,
|
|
43791
|
+
localeMetadata: status.localeMetadata
|
|
43792
|
+
};
|
|
43793
|
+
}
|
|
43794
|
+
if (status.status === "FAILED") {
|
|
43795
|
+
throw new Error(
|
|
43796
|
+
`Translation failed: ${status.errorMessage || "Unknown error"}`
|
|
43797
|
+
);
|
|
43798
|
+
}
|
|
43799
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
43800
|
+
}
|
|
43801
|
+
throw new Error(`Translation timeout after ${timeout}ms`);
|
|
43802
|
+
}
|
|
43803
|
+
async startInitSession(input) {
|
|
43804
|
+
const response = await fetch(`${this.apiUrl}/api/cli/init/start`, {
|
|
43805
|
+
method: "POST",
|
|
43806
|
+
headers: {
|
|
43807
|
+
"Content-Type": "application/json"
|
|
43808
|
+
},
|
|
43809
|
+
body: JSON.stringify(input)
|
|
43810
|
+
});
|
|
43811
|
+
const payload = await readPayload(response);
|
|
43812
|
+
if (!response.ok) {
|
|
43813
|
+
throw new VocoderAPIError({
|
|
43814
|
+
message: extractErrorMessage(
|
|
43815
|
+
payload,
|
|
43816
|
+
`Failed to start init session (${response.status})`
|
|
43817
|
+
),
|
|
43818
|
+
status: response.status,
|
|
43819
|
+
payload
|
|
43820
|
+
});
|
|
43821
|
+
}
|
|
43822
|
+
return payload;
|
|
43823
|
+
}
|
|
43824
|
+
async getInitSessionStatus(params) {
|
|
43825
|
+
const response = await fetch(
|
|
43826
|
+
`${this.apiUrl}/api/cli/init/status/${params.sessionId}`,
|
|
43827
|
+
{
|
|
43828
|
+
headers: {
|
|
43829
|
+
Authorization: `Bearer ${params.pollToken}`
|
|
43830
|
+
}
|
|
43831
|
+
}
|
|
43832
|
+
);
|
|
43833
|
+
const payload = await readPayload(response);
|
|
43834
|
+
if (!response.ok) {
|
|
43835
|
+
throw new VocoderAPIError({
|
|
43836
|
+
message: extractErrorMessage(
|
|
43837
|
+
payload,
|
|
43838
|
+
`Failed to get init status (${response.status})`
|
|
43839
|
+
),
|
|
43840
|
+
status: response.status,
|
|
43841
|
+
payload
|
|
43842
|
+
});
|
|
43843
|
+
}
|
|
43844
|
+
return payload;
|
|
43845
|
+
}
|
|
43846
|
+
// ── CLI Auth endpoints (no project API key needed) ──────────────────────────
|
|
43847
|
+
/**
|
|
43848
|
+
* Start a CLI auth session. Returns `{ sessionId, verificationUrl, expiresAt }`.
|
|
43849
|
+
* `sessionId` is the raw poll token — keep it secret, used for polling.
|
|
43850
|
+
*/
|
|
43851
|
+
async startCliAuthSession(callbackPort, repoCanonical) {
|
|
43852
|
+
const response = await fetch(`${this.apiUrl}/api/cli/auth/start`, {
|
|
43853
|
+
method: "POST",
|
|
43854
|
+
headers: { "Content-Type": "application/json" },
|
|
43855
|
+
body: JSON.stringify({
|
|
43856
|
+
...callbackPort != null ? { callbackPort } : {},
|
|
43857
|
+
...repoCanonical ? { repoCanonical } : {}
|
|
43858
|
+
})
|
|
43859
|
+
});
|
|
43860
|
+
const payload = await readPayload(response);
|
|
43861
|
+
if (!response.ok) {
|
|
43862
|
+
throw new VocoderAPIError({
|
|
43863
|
+
message: extractErrorMessage(
|
|
43864
|
+
payload,
|
|
43865
|
+
`Failed to start auth session (${response.status})`
|
|
43866
|
+
),
|
|
43867
|
+
status: response.status,
|
|
43868
|
+
payload
|
|
43869
|
+
});
|
|
43870
|
+
}
|
|
43871
|
+
return payload;
|
|
43872
|
+
}
|
|
43873
|
+
/**
|
|
43874
|
+
* Poll for CLI auth session completion.
|
|
43875
|
+
* Returns `{ token }` on success, throws on failure/expiry.
|
|
43876
|
+
* The server returns HTTP 202 while still pending.
|
|
43877
|
+
*/
|
|
43878
|
+
async pollCliAuthSession(pollToken) {
|
|
43879
|
+
const response = await fetch(
|
|
43880
|
+
`${this.apiUrl}/api/cli/auth/session?session=${encodeURIComponent(pollToken)}`
|
|
43881
|
+
);
|
|
43882
|
+
const payload = await readPayload(response);
|
|
43883
|
+
if (response.status === 202) {
|
|
43884
|
+
return { status: "pending" };
|
|
43885
|
+
}
|
|
43886
|
+
if (response.status === 410) {
|
|
43887
|
+
return {
|
|
43888
|
+
status: "failed",
|
|
43889
|
+
reason: extractErrorMessage(payload, "Auth session expired or failed")
|
|
43890
|
+
};
|
|
43891
|
+
}
|
|
43892
|
+
if (!response.ok) {
|
|
43893
|
+
return {
|
|
43894
|
+
status: "failed",
|
|
43895
|
+
reason: extractErrorMessage(
|
|
43896
|
+
payload,
|
|
43897
|
+
`Auth session error (${response.status})`
|
|
43898
|
+
)
|
|
43899
|
+
};
|
|
43900
|
+
}
|
|
43901
|
+
const result = payload;
|
|
43902
|
+
if (!result.token) {
|
|
43903
|
+
return { status: "failed", reason: "No token in response" };
|
|
43904
|
+
}
|
|
43905
|
+
return {
|
|
43906
|
+
status: "complete",
|
|
43907
|
+
token: result.token,
|
|
43908
|
+
...result.organizationId ? { organizationId: result.organizationId } : {}
|
|
43909
|
+
};
|
|
43910
|
+
}
|
|
43911
|
+
/**
|
|
43912
|
+
* Validate a CLI user token and return the authenticated user's info.
|
|
43913
|
+
* Used by the CLI to verify stored credentials on startup.
|
|
43914
|
+
*/
|
|
43915
|
+
async getCliUserInfo(userToken) {
|
|
43916
|
+
const response = await fetch(`${this.apiUrl}/api/cli/auth/me`, {
|
|
43917
|
+
headers: { Authorization: `Bearer ${userToken}` }
|
|
43918
|
+
});
|
|
43919
|
+
const payload = await readPayload(response);
|
|
43920
|
+
if (!response.ok) {
|
|
43921
|
+
throw new VocoderAPIError({
|
|
43922
|
+
message: extractErrorMessage(
|
|
43923
|
+
payload,
|
|
43924
|
+
`Token validation failed (${response.status})`
|
|
43925
|
+
),
|
|
43926
|
+
status: response.status,
|
|
43927
|
+
payload
|
|
43928
|
+
});
|
|
43929
|
+
}
|
|
43930
|
+
return payload;
|
|
43931
|
+
}
|
|
43932
|
+
/**
|
|
43933
|
+
* Revoke the given CLI user token server-side.
|
|
43934
|
+
*/
|
|
43935
|
+
async revokeCliToken(userToken) {
|
|
43936
|
+
const response = await fetch(`${this.apiUrl}/api/cli/auth/token`, {
|
|
43937
|
+
method: "DELETE",
|
|
43938
|
+
headers: { Authorization: `Bearer ${userToken}` }
|
|
43939
|
+
});
|
|
43940
|
+
if (!response.ok) {
|
|
43941
|
+
const payload = await readPayload(response);
|
|
43942
|
+
throw new VocoderAPIError({
|
|
43943
|
+
message: extractErrorMessage(
|
|
43944
|
+
payload,
|
|
43945
|
+
`Token revocation failed (${response.status})`
|
|
43946
|
+
),
|
|
43947
|
+
status: response.status,
|
|
43948
|
+
payload
|
|
43949
|
+
});
|
|
43950
|
+
}
|
|
43951
|
+
}
|
|
43952
|
+
// ── Workspaces ────────────────────────────────────────────────────────────────
|
|
43953
|
+
async listWorkspaces(userToken, params) {
|
|
43954
|
+
const url = new URL(`${this.apiUrl}/api/cli/workspaces`);
|
|
43955
|
+
if (params?.repo) url.searchParams.set("repo", params.repo);
|
|
43956
|
+
const response = await fetch(url.toString(), {
|
|
43957
|
+
headers: { Authorization: `Bearer ${userToken}` }
|
|
43958
|
+
});
|
|
43959
|
+
const payload = await readPayload(response);
|
|
43960
|
+
if (!response.ok) {
|
|
43961
|
+
throw new VocoderAPIError({
|
|
43962
|
+
message: extractErrorMessage(
|
|
43963
|
+
payload,
|
|
43964
|
+
`Failed to list workspaces (${response.status})`
|
|
43965
|
+
),
|
|
43966
|
+
status: response.status,
|
|
43967
|
+
payload
|
|
43968
|
+
});
|
|
43969
|
+
}
|
|
43970
|
+
return payload;
|
|
43971
|
+
}
|
|
43972
|
+
async listProjects(userToken, organizationId) {
|
|
43973
|
+
const url = new URL(`${this.apiUrl}/api/cli/projects`);
|
|
43974
|
+
url.searchParams.set("organizationId", organizationId);
|
|
43975
|
+
const response = await fetch(url.toString(), {
|
|
43976
|
+
headers: { Authorization: `Bearer ${userToken}` }
|
|
43977
|
+
});
|
|
43978
|
+
const payload = await readPayload(response);
|
|
43979
|
+
if (!response.ok) {
|
|
43980
|
+
throw new VocoderAPIError({
|
|
43981
|
+
message: extractErrorMessage(
|
|
43982
|
+
payload,
|
|
43983
|
+
`Failed to list projects (${response.status})`
|
|
43984
|
+
),
|
|
43985
|
+
status: response.status,
|
|
43986
|
+
payload
|
|
43987
|
+
});
|
|
43988
|
+
}
|
|
43989
|
+
const result = payload;
|
|
43990
|
+
return result.projects;
|
|
43991
|
+
}
|
|
43992
|
+
async regenerateProjectApiKey(userToken, projectId) {
|
|
43993
|
+
const response = await fetch(
|
|
43994
|
+
`${this.apiUrl}/api/cli/project/regenerate-key`,
|
|
43995
|
+
{
|
|
43996
|
+
method: "POST",
|
|
43997
|
+
headers: {
|
|
43998
|
+
"Content-Type": "application/json",
|
|
43999
|
+
Authorization: `Bearer ${userToken}`
|
|
44000
|
+
},
|
|
44001
|
+
body: JSON.stringify({ projectId })
|
|
44002
|
+
}
|
|
44003
|
+
);
|
|
44004
|
+
const payload = await readPayload(response);
|
|
44005
|
+
if (!response.ok) {
|
|
44006
|
+
throw new VocoderAPIError({
|
|
44007
|
+
message: extractErrorMessage(
|
|
44008
|
+
payload,
|
|
44009
|
+
`Failed to regenerate API key (${response.status})`
|
|
44010
|
+
),
|
|
44011
|
+
status: response.status,
|
|
44012
|
+
payload
|
|
44013
|
+
});
|
|
44014
|
+
}
|
|
44015
|
+
return payload;
|
|
44016
|
+
}
|
|
44017
|
+
// ── CLI GitHub endpoints ──────────────────────────────────────────────────────
|
|
44018
|
+
async startCliGitHubInstall(userToken, params) {
|
|
44019
|
+
const response = await fetch(
|
|
44020
|
+
`${this.apiUrl}/api/cli/github/install/start`,
|
|
44021
|
+
{
|
|
44022
|
+
method: "POST",
|
|
44023
|
+
headers: {
|
|
44024
|
+
Authorization: `Bearer ${userToken}`,
|
|
44025
|
+
"Content-Type": "application/json"
|
|
44026
|
+
},
|
|
44027
|
+
body: JSON.stringify(params)
|
|
44028
|
+
}
|
|
44029
|
+
);
|
|
44030
|
+
const payload = await readPayload(response);
|
|
44031
|
+
if (!response.ok) {
|
|
44032
|
+
throw new VocoderAPIError({
|
|
44033
|
+
message: extractErrorMessage(
|
|
44034
|
+
payload,
|
|
44035
|
+
`Failed to start GitHub install (${response.status})`
|
|
44036
|
+
),
|
|
44037
|
+
status: response.status,
|
|
44038
|
+
payload
|
|
44039
|
+
});
|
|
44040
|
+
}
|
|
44041
|
+
return payload;
|
|
44042
|
+
}
|
|
44043
|
+
/**
|
|
44044
|
+
* Start the "link existing installation" discovery flow.
|
|
44045
|
+
* Unlike startCliGitHubOAuth, this requires no bearer token — the Vocoder
|
|
44046
|
+
* account is created from the OAuth code in the callback.
|
|
44047
|
+
*/
|
|
44048
|
+
async startCliGitHubLinkSession(sessionId, callbackPort) {
|
|
44049
|
+
const response = await fetch(
|
|
44050
|
+
`${this.apiUrl}/api/cli/github/oauth/link-start`,
|
|
44051
|
+
{
|
|
44052
|
+
method: "POST",
|
|
44053
|
+
headers: { "Content-Type": "application/json" },
|
|
44054
|
+
body: JSON.stringify({
|
|
44055
|
+
sessionId,
|
|
44056
|
+
...callbackPort != null ? { callbackPort } : {}
|
|
44057
|
+
})
|
|
44058
|
+
}
|
|
44059
|
+
);
|
|
44060
|
+
const payload = await readPayload(response);
|
|
44061
|
+
if (!response.ok) {
|
|
44062
|
+
throw new VocoderAPIError({
|
|
44063
|
+
message: extractErrorMessage(
|
|
44064
|
+
payload,
|
|
44065
|
+
`Failed to start GitHub link session (${response.status})`
|
|
44066
|
+
),
|
|
44067
|
+
status: response.status,
|
|
44068
|
+
payload
|
|
44069
|
+
});
|
|
44070
|
+
}
|
|
44071
|
+
return payload;
|
|
44072
|
+
}
|
|
44073
|
+
async startCliGitHubOAuth(userToken, params) {
|
|
44074
|
+
const response = await fetch(`${this.apiUrl}/api/cli/github/oauth/start`, {
|
|
44075
|
+
method: "POST",
|
|
44076
|
+
headers: {
|
|
44077
|
+
Authorization: `Bearer ${userToken}`,
|
|
44078
|
+
"Content-Type": "application/json"
|
|
44079
|
+
},
|
|
44080
|
+
body: JSON.stringify(params)
|
|
44081
|
+
});
|
|
44082
|
+
const payload = await readPayload(response);
|
|
44083
|
+
if (!response.ok) {
|
|
44084
|
+
throw new VocoderAPIError({
|
|
44085
|
+
message: extractErrorMessage(
|
|
44086
|
+
payload,
|
|
44087
|
+
`Failed to start GitHub OAuth (${response.status})`
|
|
44088
|
+
),
|
|
44089
|
+
status: response.status,
|
|
44090
|
+
payload
|
|
44091
|
+
});
|
|
44092
|
+
}
|
|
44093
|
+
return payload;
|
|
44094
|
+
}
|
|
44095
|
+
async getCliGitHubDiscovery(userToken) {
|
|
44096
|
+
const response = await fetch(`${this.apiUrl}/api/cli/github/discovery`, {
|
|
44097
|
+
headers: { Authorization: `Bearer ${userToken}` }
|
|
44098
|
+
});
|
|
44099
|
+
const payload = await readPayload(response);
|
|
44100
|
+
if (!response.ok) {
|
|
44101
|
+
throw new VocoderAPIError({
|
|
44102
|
+
message: extractErrorMessage(
|
|
44103
|
+
payload,
|
|
44104
|
+
`Failed to fetch GitHub discovery (${response.status})`
|
|
44105
|
+
),
|
|
44106
|
+
status: response.status,
|
|
44107
|
+
payload
|
|
44108
|
+
});
|
|
44109
|
+
}
|
|
44110
|
+
return payload;
|
|
44111
|
+
}
|
|
44112
|
+
async claimCliGitHubInstallation(userToken, params) {
|
|
44113
|
+
const response = await fetch(`${this.apiUrl}/api/cli/github/claim`, {
|
|
44114
|
+
method: "POST",
|
|
44115
|
+
headers: {
|
|
44116
|
+
Authorization: `Bearer ${userToken}`,
|
|
44117
|
+
"Content-Type": "application/json"
|
|
44118
|
+
},
|
|
44119
|
+
body: JSON.stringify(params)
|
|
44120
|
+
});
|
|
44121
|
+
const payload = await readPayload(response);
|
|
44122
|
+
if (!response.ok) {
|
|
44123
|
+
throw new VocoderAPIError({
|
|
44124
|
+
message: extractErrorMessage(
|
|
44125
|
+
payload,
|
|
44126
|
+
`Failed to claim GitHub installation (${response.status})`
|
|
44127
|
+
),
|
|
44128
|
+
status: response.status,
|
|
44129
|
+
payload
|
|
44130
|
+
});
|
|
44131
|
+
}
|
|
44132
|
+
return payload;
|
|
44133
|
+
}
|
|
44134
|
+
// ── Locales ───────────────────────────────────────────────────────────────────
|
|
44135
|
+
/**
|
|
44136
|
+
* Add a target locale to the project.
|
|
44137
|
+
* Idempotent: returns the current list unchanged if the locale is already configured.
|
|
44138
|
+
* Project determined from API key.
|
|
44139
|
+
*
|
|
44140
|
+
* @throws {VocoderAPIError} status 422 for invalid/unsupported locale code
|
|
44141
|
+
* @throws {VocoderAPIError} status 403 with limitError.limitType "target_locales" when plan limit reached
|
|
44142
|
+
*/
|
|
44143
|
+
async addLocale(locale, repoCanonical) {
|
|
44144
|
+
return this.request(
|
|
44145
|
+
"/api/cli/project/locales",
|
|
44146
|
+
{
|
|
44147
|
+
method: "POST",
|
|
44148
|
+
headers: { "Content-Type": "application/json" },
|
|
44149
|
+
body: JSON.stringify({
|
|
44150
|
+
locale,
|
|
44151
|
+
...repoCanonical ? { repoCanonical } : {}
|
|
44152
|
+
})
|
|
44153
|
+
},
|
|
44154
|
+
"Failed to add locale"
|
|
44155
|
+
);
|
|
44156
|
+
}
|
|
44157
|
+
/**
|
|
44158
|
+
* Remove a target locale from the project.
|
|
44159
|
+
* Idempotent: returns the current list unchanged if the locale is not configured.
|
|
44160
|
+
* Project determined from API key.
|
|
44161
|
+
*
|
|
44162
|
+
* @throws {VocoderAPIError} on auth or server errors
|
|
44163
|
+
*/
|
|
44164
|
+
async removeLocale(locale, repoCanonical) {
|
|
44165
|
+
return this.request(
|
|
44166
|
+
"/api/cli/project/locales",
|
|
44167
|
+
{
|
|
44168
|
+
method: "DELETE",
|
|
44169
|
+
headers: { "Content-Type": "application/json" },
|
|
44170
|
+
body: JSON.stringify({
|
|
44171
|
+
locale,
|
|
44172
|
+
...repoCanonical ? { repoCanonical } : {}
|
|
44173
|
+
})
|
|
44174
|
+
},
|
|
44175
|
+
"Failed to remove locale"
|
|
44176
|
+
);
|
|
44177
|
+
}
|
|
44178
|
+
async listLocales(userToken) {
|
|
44179
|
+
const response = await fetch(`${this.apiUrl}/api/cli/locales`, {
|
|
44180
|
+
headers: { Authorization: `Bearer ${userToken}` }
|
|
44181
|
+
});
|
|
44182
|
+
const payload = await readPayload(response);
|
|
44183
|
+
if (!response.ok) {
|
|
44184
|
+
throw new VocoderAPIError({
|
|
44185
|
+
message: extractErrorMessage(
|
|
44186
|
+
payload,
|
|
44187
|
+
`Failed to list locales (${response.status})`
|
|
44188
|
+
),
|
|
44189
|
+
status: response.status,
|
|
44190
|
+
payload
|
|
44191
|
+
});
|
|
44192
|
+
}
|
|
44193
|
+
const result = payload;
|
|
44194
|
+
return result;
|
|
44195
|
+
}
|
|
44196
|
+
async listCompatibleLocales(userToken, sourceLocale) {
|
|
44197
|
+
const url = `${this.apiUrl}/api/cli/locales/compatible?source=${encodeURIComponent(sourceLocale)}`;
|
|
44198
|
+
const response = await fetch(url, {
|
|
44199
|
+
headers: { Authorization: `Bearer ${userToken}` }
|
|
44200
|
+
});
|
|
44201
|
+
const payload = await readPayload(response);
|
|
44202
|
+
if (!response.ok) {
|
|
44203
|
+
throw new VocoderAPIError({
|
|
44204
|
+
message: extractErrorMessage(
|
|
44205
|
+
payload,
|
|
44206
|
+
`Failed to list compatible locales (${response.status})`
|
|
44207
|
+
),
|
|
44208
|
+
status: response.status,
|
|
44209
|
+
payload
|
|
44210
|
+
});
|
|
44211
|
+
}
|
|
44212
|
+
const result = payload;
|
|
44213
|
+
return result.locales;
|
|
44214
|
+
}
|
|
44215
|
+
// ── Project creation ──────────────────────────────────────────────────────────
|
|
44216
|
+
async createProject(userToken, params) {
|
|
44217
|
+
const response = await fetch(`${this.apiUrl}/api/cli/projects`, {
|
|
44218
|
+
method: "POST",
|
|
44219
|
+
headers: {
|
|
44220
|
+
"Content-Type": "application/json",
|
|
44221
|
+
Authorization: `Bearer ${userToken}`
|
|
44222
|
+
},
|
|
44223
|
+
body: JSON.stringify(params)
|
|
44224
|
+
});
|
|
44225
|
+
const payload = await readPayload(response);
|
|
44226
|
+
if (!response.ok) {
|
|
44227
|
+
throw new VocoderAPIError({
|
|
44228
|
+
message: extractErrorMessage(
|
|
44229
|
+
payload,
|
|
44230
|
+
`Failed to create project (${response.status})`
|
|
44231
|
+
),
|
|
44232
|
+
status: response.status,
|
|
44233
|
+
payload
|
|
44234
|
+
});
|
|
44235
|
+
}
|
|
44236
|
+
return payload;
|
|
44237
|
+
}
|
|
44238
|
+
// ── Project lookup ────────────────────────────────────────────────────────────
|
|
44239
|
+
/**
|
|
44240
|
+
* Look up all project apps for a given repo. Returns info about exact matches,
|
|
44241
|
+
* existing apps in other scopes, and whether a whole-repo app exists.
|
|
44242
|
+
* No auth required.
|
|
44243
|
+
*/
|
|
44244
|
+
async lookupProjectByRepo(params) {
|
|
44245
|
+
try {
|
|
44246
|
+
const response = await fetch(`${this.apiUrl}/api/cli/init/lookup`, {
|
|
44247
|
+
method: "POST",
|
|
44248
|
+
headers: { "Content-Type": "application/json" },
|
|
44249
|
+
body: JSON.stringify({
|
|
44250
|
+
repo: params.repoCanonical,
|
|
44251
|
+
appDir: params.appDir
|
|
44252
|
+
})
|
|
44253
|
+
});
|
|
44254
|
+
if (!response.ok) {
|
|
44255
|
+
return { exactMatch: null, existingApps: [], hasWholeRepoApp: false };
|
|
44256
|
+
}
|
|
44257
|
+
const data = await response.json();
|
|
44258
|
+
return {
|
|
44259
|
+
exactMatch: data.exactMatch ?? null,
|
|
44260
|
+
existingApps: data.existingApps ?? [],
|
|
44261
|
+
hasWholeRepoApp: data.hasWholeRepoApp ?? false
|
|
44262
|
+
};
|
|
44263
|
+
} catch {
|
|
44264
|
+
return { exactMatch: null, existingApps: [], hasWholeRepoApp: false };
|
|
44265
|
+
}
|
|
44266
|
+
}
|
|
44267
|
+
/**
|
|
44268
|
+
* Add a new ProjectApp to an existing project (monorepo: new app directory).
|
|
44269
|
+
* Does not check plan limits — no new project is created.
|
|
44270
|
+
*/
|
|
44271
|
+
async createProjectApp(userToken, params) {
|
|
44272
|
+
const response = await fetch(`${this.apiUrl}/api/cli/project/apps`, {
|
|
44273
|
+
method: "POST",
|
|
44274
|
+
headers: {
|
|
44275
|
+
"Content-Type": "application/json",
|
|
44276
|
+
Authorization: `Bearer ${userToken}`
|
|
44277
|
+
},
|
|
44278
|
+
body: JSON.stringify(params)
|
|
44279
|
+
});
|
|
44280
|
+
const payload = await readPayload(response);
|
|
44281
|
+
if (!response.ok) {
|
|
44282
|
+
throw new VocoderAPIError({
|
|
44283
|
+
message: extractErrorMessage(
|
|
44284
|
+
payload,
|
|
44285
|
+
`Failed to create project app (${response.status})`
|
|
44286
|
+
),
|
|
44287
|
+
status: response.status,
|
|
44288
|
+
payload
|
|
44289
|
+
});
|
|
44290
|
+
}
|
|
44291
|
+
return payload;
|
|
44292
|
+
}
|
|
44293
|
+
};
|
|
44294
|
+
|
|
43571
44295
|
// src/utils/detect-local.ts
|
|
43572
44296
|
import { existsSync, readFileSync } from "fs";
|
|
43573
44297
|
import { join } from "path";
|
|
@@ -43679,6 +44403,62 @@ function getPackagesToInstall(detection) {
|
|
|
43679
44403
|
return { devPackages, runtimePackages };
|
|
43680
44404
|
}
|
|
43681
44405
|
|
|
44406
|
+
// src/utils/auth-store.ts
|
|
44407
|
+
import { mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
|
|
44408
|
+
import { homedir } from "os";
|
|
44409
|
+
import { dirname, join as join2 } from "path";
|
|
44410
|
+
function getAuthFilePath() {
|
|
44411
|
+
return join2(homedir(), ".config", "vocoder", "auth.json");
|
|
44412
|
+
}
|
|
44413
|
+
function readAuthData() {
|
|
44414
|
+
const filePath = getAuthFilePath();
|
|
44415
|
+
try {
|
|
44416
|
+
const raw = readFileSync2(filePath, "utf8");
|
|
44417
|
+
const parsed = JSON.parse(raw);
|
|
44418
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
44419
|
+
const data = parsed;
|
|
44420
|
+
if (typeof data.token !== "string" || typeof data.userId !== "string" || typeof data.email !== "string" || typeof data.createdAt !== "string") {
|
|
44421
|
+
return null;
|
|
44422
|
+
}
|
|
44423
|
+
return {
|
|
44424
|
+
token: data.token,
|
|
44425
|
+
userId: data.userId,
|
|
44426
|
+
email: data.email,
|
|
44427
|
+
name: typeof data.name === "string" ? data.name : null,
|
|
44428
|
+
createdAt: data.createdAt
|
|
44429
|
+
};
|
|
44430
|
+
} catch {
|
|
44431
|
+
return null;
|
|
44432
|
+
}
|
|
44433
|
+
}
|
|
44434
|
+
function writeAuthData(data) {
|
|
44435
|
+
const filePath = getAuthFilePath();
|
|
44436
|
+
const dir = dirname(filePath);
|
|
44437
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
44438
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2), { mode: 384 });
|
|
44439
|
+
}
|
|
44440
|
+
async function verifyStoredAuth(api) {
|
|
44441
|
+
const stored = readAuthData();
|
|
44442
|
+
if (!stored) return { status: "none" };
|
|
44443
|
+
try {
|
|
44444
|
+
const userInfo = await api.getCliUserInfo(stored.token);
|
|
44445
|
+
return { status: "valid", token: stored.token, ...userInfo };
|
|
44446
|
+
} catch (err) {
|
|
44447
|
+
clearAuthData();
|
|
44448
|
+
if (err instanceof VocoderAPIError && err.status === 404) {
|
|
44449
|
+
return { status: "gone" };
|
|
44450
|
+
}
|
|
44451
|
+
return { status: "expired" };
|
|
44452
|
+
}
|
|
44453
|
+
}
|
|
44454
|
+
function clearAuthData() {
|
|
44455
|
+
const filePath = getAuthFilePath();
|
|
44456
|
+
try {
|
|
44457
|
+
unlinkSync(filePath);
|
|
44458
|
+
} catch {
|
|
44459
|
+
}
|
|
44460
|
+
}
|
|
44461
|
+
|
|
43682
44462
|
// src/utils/setup-snippets.ts
|
|
43683
44463
|
function getSetupSnippets(params) {
|
|
43684
44464
|
const { framework, ecosystem, sourceLocale } = params;
|
|
@@ -43840,28 +44620,28 @@ import { T } from '@vocoder/vue';
|
|
|
43840
44620
|
// ../extractor/src/config.ts
|
|
43841
44621
|
var import_parser = __toESM(require_lib());
|
|
43842
44622
|
var import_traverse = __toESM(require_lib8());
|
|
43843
|
-
import { existsSync as existsSync2, readFileSync as
|
|
43844
|
-
import { join as
|
|
44623
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
44624
|
+
import { join as join3 } from "path";
|
|
43845
44625
|
var traverse = import_traverse.default.default || import_traverse.default;
|
|
43846
44626
|
function loadVocoderConfig(cwd) {
|
|
43847
44627
|
const candidates = [
|
|
43848
|
-
|
|
43849
|
-
|
|
43850
|
-
|
|
43851
|
-
|
|
44628
|
+
join3(cwd, "vocoder.config.ts"),
|
|
44629
|
+
join3(cwd, "vocoder.config.js"),
|
|
44630
|
+
join3(cwd, "vocoder.config.mjs"),
|
|
44631
|
+
join3(cwd, "vocoder.config.cjs")
|
|
43852
44632
|
];
|
|
43853
44633
|
for (const configPath of candidates) {
|
|
43854
44634
|
if (!existsSync2(configPath)) continue;
|
|
43855
44635
|
try {
|
|
43856
|
-
const code =
|
|
44636
|
+
const code = readFileSync3(configPath, "utf-8");
|
|
43857
44637
|
return parseConfigFromSource(code);
|
|
43858
44638
|
} catch {
|
|
43859
44639
|
}
|
|
43860
44640
|
}
|
|
43861
|
-
const jsonPath =
|
|
44641
|
+
const jsonPath = join3(cwd, "vocoder.config.json");
|
|
43862
44642
|
if (existsSync2(jsonPath)) {
|
|
43863
44643
|
try {
|
|
43864
|
-
return JSON.parse(
|
|
44644
|
+
return JSON.parse(readFileSync3(jsonPath, "utf-8"));
|
|
43865
44645
|
} catch {
|
|
43866
44646
|
return null;
|
|
43867
44647
|
}
|
|
@@ -43922,7 +44702,7 @@ function extractFromObject(obj) {
|
|
|
43922
44702
|
// ../extractor/src/index.ts
|
|
43923
44703
|
var import_parser2 = __toESM(require_lib());
|
|
43924
44704
|
var import_traverse2 = __toESM(require_lib8());
|
|
43925
|
-
import { readFileSync as
|
|
44705
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
43926
44706
|
import { relative as pathRelative } from "path";
|
|
43927
44707
|
|
|
43928
44708
|
// ../../node_modules/.pnpm/minimatch@9.0.5/node_modules/minimatch/dist/esm/index.js
|
|
@@ -50399,7 +51179,7 @@ var StringExtractor = class {
|
|
|
50399
51179
|
const sortedFiles = Array.from(allFiles).sort();
|
|
50400
51180
|
for (const file of sortedFiles) {
|
|
50401
51181
|
try {
|
|
50402
|
-
const code =
|
|
51182
|
+
const code = readFileSync4(file, "utf-8");
|
|
50403
51183
|
const relPath = pathRelative(projectRoot, file).split("\\").join("/");
|
|
50404
51184
|
const strings = _extractFromContent(relPath, code);
|
|
50405
51185
|
allStrings.push(...strings);
|
|
@@ -50481,25 +51261,6 @@ function detectUiRole(path2) {
|
|
|
50481
51261
|
}
|
|
50482
51262
|
return "unknown";
|
|
50483
51263
|
}
|
|
50484
|
-
var FEATURE_PATTERNS = [
|
|
50485
|
-
[/\/(checkout|cart|basket)/, "checkout"],
|
|
50486
|
-
[/\/(auth|login|signup|sign-up|register|forgot|reset-password|verify)/, "auth"],
|
|
50487
|
-
[/\/(onboarding|welcome|setup)/, "onboarding"],
|
|
50488
|
-
[/\/(dashboard|overview|home)/, "dashboard"],
|
|
50489
|
-
[/\/(settings|preferences|profile|account)/, "settings"],
|
|
50490
|
-
[/\/(search|results|explore)/, "search"],
|
|
50491
|
-
[/\/(product|item|detail|pdp)/, "product"],
|
|
50492
|
-
[/\/(pricing|plans|billing|upgrade)/, "pricing"],
|
|
50493
|
-
[/\/(admin|manage)/, "admin"],
|
|
50494
|
-
[/\/(help|support|faq|docs)/, "support"]
|
|
50495
|
-
];
|
|
50496
|
-
function inferFeatureArea(filePath) {
|
|
50497
|
-
const normalized = filePath.replace(/\\/g, "/").toLowerCase();
|
|
50498
|
-
for (const [pattern, area] of FEATURE_PATTERNS) {
|
|
50499
|
-
if (pattern.test(normalized)) return area;
|
|
50500
|
-
}
|
|
50501
|
-
return void 0;
|
|
50502
|
-
}
|
|
50503
51264
|
function _extractFromContent(filePath, content) {
|
|
50504
51265
|
const strings = [];
|
|
50505
51266
|
try {
|
|
@@ -50573,7 +51334,6 @@ function _extractFromContent(filePath, content) {
|
|
|
50573
51334
|
const line = path2.node.loc?.start.line || 0;
|
|
50574
51335
|
const key = explicitKey && explicitKey.length > 0 ? explicitKey : generateMessageHash(text.trim(), context);
|
|
50575
51336
|
const uiRole = detectUiRole(path2);
|
|
50576
|
-
const featureArea = inferFeatureArea(filePath);
|
|
50577
51337
|
strings.push({
|
|
50578
51338
|
key,
|
|
50579
51339
|
text: text.trim(),
|
|
@@ -50581,8 +51341,7 @@ function _extractFromContent(filePath, content) {
|
|
|
50581
51341
|
line,
|
|
50582
51342
|
context,
|
|
50583
51343
|
formality,
|
|
50584
|
-
uiRole: uiRole !== "unknown" ? uiRole : void 0
|
|
50585
|
-
featureArea
|
|
51344
|
+
uiRole: uiRole !== "unknown" ? uiRole : void 0
|
|
50586
51345
|
});
|
|
50587
51346
|
},
|
|
50588
51347
|
JSXElement: (path2) => {
|
|
@@ -50613,7 +51372,6 @@ function _extractFromContent(filePath, content) {
|
|
|
50613
51372
|
const line = path2.node.loc?.start.line || 0;
|
|
50614
51373
|
const key = id && id.trim().length > 0 ? id.trim() : generateMessageHash(text.trim(), context);
|
|
50615
51374
|
const uiRole = detectUiRole(path2);
|
|
50616
|
-
const featureArea = inferFeatureArea(filePath);
|
|
50617
51375
|
strings.push({
|
|
50618
51376
|
key,
|
|
50619
51377
|
text: text.trim(),
|
|
@@ -50621,8 +51379,7 @@ function _extractFromContent(filePath, content) {
|
|
|
50621
51379
|
line,
|
|
50622
51380
|
context,
|
|
50623
51381
|
formality,
|
|
50624
|
-
uiRole: uiRole !== "unknown" ? uiRole : void 0
|
|
50625
|
-
featureArea
|
|
51382
|
+
uiRole: uiRole !== "unknown" ? uiRole : void 0
|
|
50626
51383
|
});
|
|
50627
51384
|
}
|
|
50628
51385
|
});
|
|
@@ -50730,11 +51487,17 @@ function deduplicateStrings(strings) {
|
|
|
50730
51487
|
}
|
|
50731
51488
|
|
|
50732
51489
|
export {
|
|
51490
|
+
VocoderAPIError,
|
|
51491
|
+
VocoderAPI,
|
|
50733
51492
|
detectLocalEcosystem,
|
|
50734
51493
|
buildInstallCommand,
|
|
50735
51494
|
getPackagesToInstall,
|
|
51495
|
+
readAuthData,
|
|
51496
|
+
writeAuthData,
|
|
51497
|
+
verifyStoredAuth,
|
|
51498
|
+
clearAuthData,
|
|
50736
51499
|
getSetupSnippets,
|
|
50737
51500
|
loadVocoderConfig,
|
|
50738
51501
|
StringExtractor
|
|
50739
51502
|
};
|
|
50740
|
-
//# sourceMappingURL=chunk-
|
|
51503
|
+
//# sourceMappingURL=chunk-XUCVAFBG.mjs.map
|