@tolkbot/cli 0.2.0 → 0.3.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/dist/index.js +1713 -1931
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3650,8 +3650,8 @@ var require_utils3 = __commonJS({
|
|
|
3650
3650
|
exports.array = array2;
|
|
3651
3651
|
var errno = require_errno();
|
|
3652
3652
|
exports.errno = errno;
|
|
3653
|
-
var
|
|
3654
|
-
exports.fs =
|
|
3653
|
+
var fs10 = require_fs();
|
|
3654
|
+
exports.fs = fs10;
|
|
3655
3655
|
var path12 = require_path();
|
|
3656
3656
|
exports.path = path12;
|
|
3657
3657
|
var pattern = require_pattern();
|
|
@@ -3835,12 +3835,12 @@ var require_fs2 = __commonJS({
|
|
|
3835
3835
|
"use strict";
|
|
3836
3836
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3837
3837
|
exports.createFileSystemAdapter = exports.FILE_SYSTEM_ADAPTER = void 0;
|
|
3838
|
-
var
|
|
3838
|
+
var fs10 = __require("fs");
|
|
3839
3839
|
exports.FILE_SYSTEM_ADAPTER = {
|
|
3840
|
-
lstat:
|
|
3841
|
-
stat:
|
|
3842
|
-
lstatSync:
|
|
3843
|
-
statSync:
|
|
3840
|
+
lstat: fs10.lstat,
|
|
3841
|
+
stat: fs10.stat,
|
|
3842
|
+
lstatSync: fs10.lstatSync,
|
|
3843
|
+
statSync: fs10.statSync
|
|
3844
3844
|
};
|
|
3845
3845
|
function createFileSystemAdapter(fsMethods) {
|
|
3846
3846
|
if (fsMethods === void 0) {
|
|
@@ -3857,12 +3857,12 @@ var require_settings = __commonJS({
|
|
|
3857
3857
|
"../../node_modules/.pnpm/@nodelib+fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/settings.js"(exports) {
|
|
3858
3858
|
"use strict";
|
|
3859
3859
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3860
|
-
var
|
|
3860
|
+
var fs10 = require_fs2();
|
|
3861
3861
|
var Settings = class {
|
|
3862
3862
|
constructor(_options = {}) {
|
|
3863
3863
|
this._options = _options;
|
|
3864
3864
|
this.followSymbolicLink = this._getValue(this._options.followSymbolicLink, true);
|
|
3865
|
-
this.fs =
|
|
3865
|
+
this.fs = fs10.createFileSystemAdapter(this._options.fs);
|
|
3866
3866
|
this.markSymbolicLink = this._getValue(this._options.markSymbolicLink, false);
|
|
3867
3867
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
|
|
3868
3868
|
}
|
|
@@ -4019,8 +4019,8 @@ var require_utils4 = __commonJS({
|
|
|
4019
4019
|
"use strict";
|
|
4020
4020
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4021
4021
|
exports.fs = void 0;
|
|
4022
|
-
var
|
|
4023
|
-
exports.fs =
|
|
4022
|
+
var fs10 = require_fs3();
|
|
4023
|
+
exports.fs = fs10;
|
|
4024
4024
|
}
|
|
4025
4025
|
});
|
|
4026
4026
|
|
|
@@ -4215,14 +4215,14 @@ var require_fs4 = __commonJS({
|
|
|
4215
4215
|
"use strict";
|
|
4216
4216
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4217
4217
|
exports.createFileSystemAdapter = exports.FILE_SYSTEM_ADAPTER = void 0;
|
|
4218
|
-
var
|
|
4218
|
+
var fs10 = __require("fs");
|
|
4219
4219
|
exports.FILE_SYSTEM_ADAPTER = {
|
|
4220
|
-
lstat:
|
|
4221
|
-
stat:
|
|
4222
|
-
lstatSync:
|
|
4223
|
-
statSync:
|
|
4224
|
-
readdir:
|
|
4225
|
-
readdirSync:
|
|
4220
|
+
lstat: fs10.lstat,
|
|
4221
|
+
stat: fs10.stat,
|
|
4222
|
+
lstatSync: fs10.lstatSync,
|
|
4223
|
+
statSync: fs10.statSync,
|
|
4224
|
+
readdir: fs10.readdir,
|
|
4225
|
+
readdirSync: fs10.readdirSync
|
|
4226
4226
|
};
|
|
4227
4227
|
function createFileSystemAdapter(fsMethods) {
|
|
4228
4228
|
if (fsMethods === void 0) {
|
|
@@ -4241,12 +4241,12 @@ var require_settings2 = __commonJS({
|
|
|
4241
4241
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4242
4242
|
var path12 = __require("path");
|
|
4243
4243
|
var fsStat = require_out();
|
|
4244
|
-
var
|
|
4244
|
+
var fs10 = require_fs4();
|
|
4245
4245
|
var Settings = class {
|
|
4246
4246
|
constructor(_options = {}) {
|
|
4247
4247
|
this._options = _options;
|
|
4248
4248
|
this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false);
|
|
4249
|
-
this.fs =
|
|
4249
|
+
this.fs = fs10.createFileSystemAdapter(this._options.fs);
|
|
4250
4250
|
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path12.sep);
|
|
4251
4251
|
this.stats = this._getValue(this._options.stats, false);
|
|
4252
4252
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
|
|
@@ -5628,16 +5628,16 @@ var require_settings4 = __commonJS({
|
|
|
5628
5628
|
"use strict";
|
|
5629
5629
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5630
5630
|
exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0;
|
|
5631
|
-
var
|
|
5631
|
+
var fs10 = __require("fs");
|
|
5632
5632
|
var os3 = __require("os");
|
|
5633
5633
|
var CPU_COUNT = Math.max(os3.cpus().length, 1);
|
|
5634
5634
|
exports.DEFAULT_FILE_SYSTEM_ADAPTER = {
|
|
5635
|
-
lstat:
|
|
5636
|
-
lstatSync:
|
|
5637
|
-
stat:
|
|
5638
|
-
statSync:
|
|
5639
|
-
readdir:
|
|
5640
|
-
readdirSync:
|
|
5635
|
+
lstat: fs10.lstat,
|
|
5636
|
+
lstatSync: fs10.lstatSync,
|
|
5637
|
+
stat: fs10.stat,
|
|
5638
|
+
statSync: fs10.statSync,
|
|
5639
|
+
readdir: fs10.readdir,
|
|
5640
|
+
readdirSync: fs10.readdirSync
|
|
5641
5641
|
};
|
|
5642
5642
|
var Settings = class {
|
|
5643
5643
|
constructor(_options = {}) {
|
|
@@ -5784,114 +5784,41 @@ var require_out4 = __commonJS({
|
|
|
5784
5784
|
});
|
|
5785
5785
|
|
|
5786
5786
|
// src/main.ts
|
|
5787
|
-
import { Command as Command6 } from "commander";
|
|
5787
|
+
import { Command as Command6, CommanderError } from "commander";
|
|
5788
5788
|
|
|
5789
5789
|
// package.json
|
|
5790
|
-
var version = "0.
|
|
5790
|
+
var version = "0.3.0";
|
|
5791
5791
|
|
|
5792
5792
|
// src/commands/auth/index.ts
|
|
5793
5793
|
import { Command } from "commander";
|
|
5794
5794
|
import { z as z2 } from "zod/v4";
|
|
5795
5795
|
|
|
5796
|
-
// src/utils/validate-command.ts
|
|
5797
|
-
import { program } from "commander";
|
|
5798
|
-
function validateOrExit(schema, value) {
|
|
5799
|
-
const res = schema.safeParse(value);
|
|
5800
|
-
if (!res.success) {
|
|
5801
|
-
const msg = res.error.issues.map((e) => `${e.path.join(".") || "(root)"}: ${e.message}`).join("\n");
|
|
5802
|
-
program.error(`Invalid arguments:
|
|
5803
|
-
${msg}`);
|
|
5804
|
-
}
|
|
5805
|
-
return res.data;
|
|
5806
|
-
}
|
|
5807
|
-
|
|
5808
|
-
// src/commands/auth/login.tsx
|
|
5809
|
-
import http from "http";
|
|
5810
|
-
import crypto from "crypto";
|
|
5811
|
-
import { Spinner } from "@inkjs/ui";
|
|
5812
|
-
import { Box, render, Text } from "ink";
|
|
5813
|
-
import open from "open";
|
|
5814
|
-
|
|
5815
|
-
// src/lib/telemetry.ts
|
|
5816
|
-
import { randomUUID } from "crypto";
|
|
5817
|
-
import fs2 from "fs/promises";
|
|
5818
|
-
import os2 from "os";
|
|
5819
|
-
import path2 from "path";
|
|
5820
|
-
|
|
5821
|
-
// ../shared/src/analytics/index.ts
|
|
5822
|
-
var ORGANIZATION_GROUP = "organization";
|
|
5823
|
-
var analyticsEventNames = [
|
|
5824
|
-
"trial_started",
|
|
5825
|
-
"project_created",
|
|
5826
|
-
"project_page_viewed",
|
|
5827
|
-
"github_connect_started",
|
|
5828
|
-
"github_install_initiated",
|
|
5829
|
-
"plan_change_initiated",
|
|
5830
|
-
"credits_purchase_initiated",
|
|
5831
|
-
"glossary_created",
|
|
5832
|
-
"glossary_deleted",
|
|
5833
|
-
"glossary_imported",
|
|
5834
|
-
"translation_job_viewed",
|
|
5835
|
-
"translation_logs_viewed",
|
|
5836
|
-
"cli_login_completed",
|
|
5837
|
-
"cli_command_completed",
|
|
5838
|
-
"cli_init_completed",
|
|
5839
|
-
"cli_translate_completed",
|
|
5840
|
-
"cli_validate_completed",
|
|
5841
|
-
"translation_job_started",
|
|
5842
|
-
"translation_job_completed",
|
|
5843
|
-
"translation_job_failed",
|
|
5844
|
-
"pull_request_opened",
|
|
5845
|
-
"pull_request_merged",
|
|
5846
|
-
"pull_request_closed_unmerged",
|
|
5847
|
-
"github_app_installed",
|
|
5848
|
-
"github_app_uninstalled",
|
|
5849
|
-
"repository_connected",
|
|
5850
|
-
"subscription_started",
|
|
5851
|
-
"subscription_plan_changed",
|
|
5852
|
-
"subscription_canceled",
|
|
5853
|
-
"trial_ended",
|
|
5854
|
-
"credits_purchased",
|
|
5855
|
-
"credits_exhausted",
|
|
5856
|
-
"usage_limit_reached",
|
|
5857
|
-
"cta_clicked",
|
|
5858
|
-
"waitlist_joined",
|
|
5859
|
-
"signed_up",
|
|
5860
|
-
"signed_in",
|
|
5861
|
-
"onboarding_step_completed",
|
|
5862
|
-
"onboarding_completed",
|
|
5863
|
-
"language_added",
|
|
5864
|
-
"translation_edited",
|
|
5865
|
-
"translation_bulk_retranslated",
|
|
5866
|
-
"dashboard_translation_started",
|
|
5867
|
-
"project_settings_changed",
|
|
5868
|
-
"member_invited",
|
|
5869
|
-
"feedback_submitted",
|
|
5870
|
-
"analytics_smoke_tested"
|
|
5871
|
-
];
|
|
5872
|
-
var registeredNames = new Set(analyticsEventNames);
|
|
5873
|
-
|
|
5874
|
-
// src/lib/telemetry.ts
|
|
5875
|
-
import { PostHog } from "posthog-node";
|
|
5876
|
-
|
|
5877
5796
|
// src/utils/errors.ts
|
|
5878
5797
|
var CliError = class extends Error {
|
|
5879
5798
|
hint;
|
|
5880
5799
|
/** The message without the trailing hint. */
|
|
5881
5800
|
baseMessage;
|
|
5882
|
-
|
|
5801
|
+
code;
|
|
5802
|
+
constructor(message, hint, code = "failed") {
|
|
5883
5803
|
super(hint ? `${message} ${hint}` : message);
|
|
5884
5804
|
this.name = "CliError";
|
|
5885
5805
|
this.baseMessage = message;
|
|
5886
5806
|
this.hint = hint;
|
|
5807
|
+
this.code = code;
|
|
5887
5808
|
}
|
|
5888
5809
|
};
|
|
5889
5810
|
var AuthError = class extends CliError {
|
|
5890
|
-
constructor(message, hint) {
|
|
5891
|
-
super(message, hint);
|
|
5811
|
+
constructor(message, hint, code = "not_authenticated") {
|
|
5812
|
+
super(message, hint, code);
|
|
5892
5813
|
this.name = "AuthError";
|
|
5893
5814
|
}
|
|
5894
5815
|
};
|
|
5816
|
+
var UsageError = class extends CliError {
|
|
5817
|
+
constructor(message, hint) {
|
|
5818
|
+
super(message, hint, "usage");
|
|
5819
|
+
this.name = "UsageError";
|
|
5820
|
+
}
|
|
5821
|
+
};
|
|
5895
5822
|
function describeError(error62) {
|
|
5896
5823
|
if (error62 instanceof Error) return error62.message;
|
|
5897
5824
|
if (typeof error62 === "string") return error62;
|
|
@@ -5912,30 +5839,114 @@ function isUnauthenticatedError(error62) {
|
|
|
5912
5839
|
"Could not parse JWT"
|
|
5913
5840
|
].some((marker) => message.includes(marker));
|
|
5914
5841
|
}
|
|
5915
|
-
|
|
5916
|
-
|
|
5842
|
+
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
5843
|
+
"ECONNREFUSED",
|
|
5844
|
+
"ECONNRESET",
|
|
5845
|
+
"ENOTFOUND",
|
|
5846
|
+
"EAI_AGAIN",
|
|
5847
|
+
"ETIMEDOUT",
|
|
5848
|
+
"ENETUNREACH",
|
|
5849
|
+
"EHOSTUNREACH",
|
|
5850
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
5851
|
+
"UND_ERR_SOCKET"
|
|
5852
|
+
]);
|
|
5853
|
+
function isNetworkError(error62) {
|
|
5854
|
+
for (let current = error62, depth = 0; current instanceof Error && depth < 4; current = current.cause, depth++) {
|
|
5855
|
+
const code = current.code;
|
|
5856
|
+
if (typeof code === "string" && NETWORK_ERROR_CODES.has(code)) {
|
|
5857
|
+
return true;
|
|
5858
|
+
}
|
|
5859
|
+
if (current.name === "TypeError" && current.message === "fetch failed")
|
|
5860
|
+
return true;
|
|
5861
|
+
}
|
|
5862
|
+
return false;
|
|
5917
5863
|
}
|
|
5918
|
-
function
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5864
|
+
function noConfigError() {
|
|
5865
|
+
return new CliError(
|
|
5866
|
+
"No tolkbot.json found.",
|
|
5867
|
+
"Run 'tolkbot init' to create one.",
|
|
5868
|
+
"no_config"
|
|
5869
|
+
);
|
|
5870
|
+
}
|
|
5871
|
+
function projectNotFoundError() {
|
|
5872
|
+
return new CliError(
|
|
5873
|
+
"Project not found.",
|
|
5874
|
+
"Run 'tolkbot init' again to link this repository to a project.",
|
|
5875
|
+
"project_not_found"
|
|
5876
|
+
);
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
// src/utils/validate-command.ts
|
|
5880
|
+
function validateOrExit(schema, value) {
|
|
5881
|
+
const res = schema.safeParse(value);
|
|
5882
|
+
if (!res.success) {
|
|
5883
|
+
const msg = res.error.issues.map((e) => `${e.path.join(".") || "(root)"}: ${e.message}`).join("; ");
|
|
5884
|
+
throw new UsageError(`Invalid arguments: ${msg}`);
|
|
5885
|
+
}
|
|
5886
|
+
return res.data;
|
|
5887
|
+
}
|
|
5888
|
+
|
|
5889
|
+
// src/commands/auth/login.tsx
|
|
5890
|
+
import http from "http";
|
|
5891
|
+
import crypto from "crypto";
|
|
5892
|
+
import { Spinner } from "@inkjs/ui";
|
|
5893
|
+
import { Box, render, Text } from "ink";
|
|
5894
|
+
import open from "open";
|
|
5895
|
+
|
|
5896
|
+
// src/utils/env.ts
|
|
5897
|
+
import path from "path";
|
|
5898
|
+
import { fileURLToPath } from "url";
|
|
5899
|
+
import { createEnv } from "@t3-oss/env-core";
|
|
5900
|
+
import * as dotenv from "dotenv";
|
|
5901
|
+
import { z } from "zod/v4";
|
|
5902
|
+
var RELEASE_CONVEX_URL = "https://uncommon-monitor-462.convex.cloud"?.trim();
|
|
5903
|
+
var RELEASE_WEB_URL = "https://www.tolkbot.ai"?.trim();
|
|
5904
|
+
if (!RELEASE_CONVEX_URL) {
|
|
5905
|
+
try {
|
|
5906
|
+
const dir = path.dirname(fileURLToPath(import.meta.url));
|
|
5907
|
+
dotenv.config({
|
|
5908
|
+
path: path.resolve(dir, "../../../.env"),
|
|
5909
|
+
quiet: true
|
|
5910
|
+
});
|
|
5911
|
+
} catch {
|
|
5912
|
+
}
|
|
5913
|
+
}
|
|
5914
|
+
var env = createEnv({
|
|
5915
|
+
server: {
|
|
5916
|
+
// Optional here, required at the point of use: commands that never
|
|
5917
|
+
// reach the backend (help, logout) must work without deployment config.
|
|
5918
|
+
CONVEX_URL: z.url().optional(),
|
|
5919
|
+
TOLKBOT_WEB_URL: z.url().default("http://localhost:3000")
|
|
5920
|
+
},
|
|
5921
|
+
runtimeEnv: {
|
|
5922
|
+
CONVEX_URL: RELEASE_CONVEX_URL || process.env.CONVEX_URL,
|
|
5923
|
+
TOLKBOT_WEB_URL: RELEASE_WEB_URL || process.env.TOLKBOT_WEB_URL
|
|
5924
|
+
},
|
|
5925
|
+
skipValidation: !!process.env.CI || process.env.npm_lifecycle_event === "lint",
|
|
5926
|
+
onValidationError: (issues) => {
|
|
5927
|
+
const details = issues.map((issue2) => {
|
|
5928
|
+
const name = issue2.path?.join(".") ?? "(unknown)";
|
|
5929
|
+
return `${name}: ${issue2.message}`;
|
|
5930
|
+
}).join("\n ");
|
|
5931
|
+
throw new CliError(`Invalid configuration:
|
|
5932
|
+
${details}`);
|
|
5925
5933
|
}
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
if (!
|
|
5929
|
-
|
|
5930
|
-
|
|
5934
|
+
});
|
|
5935
|
+
function requireConvexUrl() {
|
|
5936
|
+
if (!env.CONVEX_URL) {
|
|
5937
|
+
throw new CliError(
|
|
5938
|
+
"CONVEX_URL is not set, so the CLI cannot reach the TolkBot backend.",
|
|
5939
|
+
"Set CONVEX_URL to your TolkBot deployment URL.",
|
|
5940
|
+
"network"
|
|
5941
|
+
);
|
|
5931
5942
|
}
|
|
5932
|
-
|
|
5943
|
+
return env.CONVEX_URL;
|
|
5933
5944
|
}
|
|
5934
5945
|
|
|
5935
5946
|
// src/utils/token.ts
|
|
5936
5947
|
import fs from "fs/promises";
|
|
5937
5948
|
import os from "os";
|
|
5938
|
-
import
|
|
5949
|
+
import path2 from "path";
|
|
5939
5950
|
var TOKEN_ENV_VAR = "TOLKBOT_TOKEN";
|
|
5940
5951
|
var EXPIRY_SKEW_SECONDS = 30;
|
|
5941
5952
|
var tokenOverride = null;
|
|
@@ -5947,11 +5958,11 @@ function setTokenOverride(token) {
|
|
|
5947
5958
|
tokenOverride = token?.trim() ? token.trim() : null;
|
|
5948
5959
|
}
|
|
5949
5960
|
function getTokenPath() {
|
|
5950
|
-
return
|
|
5961
|
+
return path2.join(os.homedir(), ".tolkbot", "token");
|
|
5951
5962
|
}
|
|
5952
5963
|
async function saveToken(token) {
|
|
5953
5964
|
const file2 = getTokenPath();
|
|
5954
|
-
await fs.mkdir(
|
|
5965
|
+
await fs.mkdir(path2.dirname(file2), { recursive: true, mode: 448 });
|
|
5955
5966
|
await fs.writeFile(file2, token, { mode: 384 });
|
|
5956
5967
|
}
|
|
5957
5968
|
async function loadToken() {
|
|
@@ -6043,274 +6054,48 @@ function reauthHint(source) {
|
|
|
6043
6054
|
}
|
|
6044
6055
|
}
|
|
6045
6056
|
|
|
6046
|
-
// src/
|
|
6047
|
-
|
|
6048
|
-
var
|
|
6049
|
-
var
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
}
|
|
6056
|
-
function projectKey() {
|
|
6057
|
-
const key = "phc_uSMLUC9D7bHeaHFAosnoDzBj6DY5uzLcCQkXZxzj0mQ"?.trim();
|
|
6058
|
-
if (!key) return void 0;
|
|
6059
|
-
return key;
|
|
6060
|
-
}
|
|
6061
|
-
function environment() {
|
|
6062
|
-
const nodeEnv = process.env.NODE_ENV;
|
|
6063
|
-
return nodeEnv === "development" || nodeEnv === "test" ? nodeEnv : "production";
|
|
6064
|
-
}
|
|
6065
|
-
function inCi() {
|
|
6066
|
-
return Boolean(process.env.CI);
|
|
6067
|
-
}
|
|
6068
|
-
function getTelemetryDir() {
|
|
6069
|
-
return path2.join(os2.homedir(), ".tolkbot");
|
|
6070
|
-
}
|
|
6071
|
-
function getPreferencePath() {
|
|
6072
|
-
return path2.join(getTelemetryDir(), "telemetry");
|
|
6073
|
-
}
|
|
6074
|
-
function getAnonymousIdPath() {
|
|
6075
|
-
return path2.join(getTelemetryDir(), "anonymous-id");
|
|
6076
|
-
}
|
|
6077
|
-
async function readFileOrNull(file2) {
|
|
6078
|
-
try {
|
|
6079
|
-
return (await fs2.readFile(file2, "utf-8")).trim() || null;
|
|
6080
|
-
} catch {
|
|
6081
|
-
return null;
|
|
6082
|
-
}
|
|
6083
|
-
}
|
|
6084
|
-
async function writePrivateFile(file2, content) {
|
|
6085
|
-
await fs2.mkdir(path2.dirname(file2), { recursive: true, mode: 448 });
|
|
6086
|
-
await fs2.writeFile(file2, content, { mode: 384 });
|
|
6087
|
-
}
|
|
6088
|
-
async function telemetryState() {
|
|
6089
|
-
const envValue = readEnv(TELEMETRY_ENV_VAR)?.toLowerCase();
|
|
6090
|
-
if (envValue !== void 0 && OFF_VALUES.has(envValue)) {
|
|
6091
|
-
return { enabled: false, reason: "env" };
|
|
6092
|
-
}
|
|
6093
|
-
const doNotTrack = readEnv(DO_NOT_TRACK_ENV_VAR)?.toLowerCase();
|
|
6094
|
-
if (doNotTrack !== void 0 && ON_VALUES.has(doNotTrack)) {
|
|
6095
|
-
return { enabled: false, reason: "do-not-track" };
|
|
6096
|
-
}
|
|
6097
|
-
const preference = (await readFileOrNull(getPreferencePath()))?.toLowerCase();
|
|
6098
|
-
if (preference !== void 0 && OFF_VALUES.has(preference)) {
|
|
6099
|
-
return { enabled: false, reason: "preference" };
|
|
6100
|
-
}
|
|
6101
|
-
if (!projectKey()) {
|
|
6102
|
-
return { enabled: false, reason: "no-key" };
|
|
6057
|
+
// src/commands/auth/login.tsx
|
|
6058
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6059
|
+
var CALLBACK_PORTS = [3003, 3013, 3023, 3033, 3043];
|
|
6060
|
+
var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
6061
|
+
function generateVerificationCode() {
|
|
6062
|
+
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
6063
|
+
let code = "";
|
|
6064
|
+
for (let i = 0; i < 8; i++) {
|
|
6065
|
+
code += chars.charAt(crypto.randomInt(chars.length));
|
|
6103
6066
|
}
|
|
6104
|
-
return
|
|
6067
|
+
return code;
|
|
6105
6068
|
}
|
|
6106
|
-
|
|
6107
|
-
|
|
6069
|
+
function respond(res, status, body) {
|
|
6070
|
+
res.writeHead(status, { "Content-Type": "text/html" });
|
|
6071
|
+
res.end(`
|
|
6072
|
+
<!DOCTYPE html>
|
|
6073
|
+
<html>
|
|
6074
|
+
<head><title>TolkBot CLI</title></head>
|
|
6075
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
6076
|
+
${body}
|
|
6077
|
+
</body>
|
|
6078
|
+
</html>
|
|
6079
|
+
`);
|
|
6108
6080
|
}
|
|
6109
|
-
function
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
if (
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
const existing = await readFileOrNull(file2);
|
|
6128
|
-
if (existing) return existing;
|
|
6129
|
-
const generated = randomUUID();
|
|
6130
|
-
try {
|
|
6131
|
-
await writePrivateFile(file2, `${generated}
|
|
6132
|
-
`);
|
|
6133
|
-
} catch {
|
|
6134
|
-
}
|
|
6135
|
-
return generated;
|
|
6136
|
-
}
|
|
6137
|
-
var organizationId = null;
|
|
6138
|
-
function setTelemetryOrganization(id) {
|
|
6139
|
-
organizationId = id;
|
|
6140
|
-
}
|
|
6141
|
-
var client = null;
|
|
6142
|
-
function getClient(key) {
|
|
6143
|
-
client ??= new PostHog(key, {
|
|
6144
|
-
host: POSTHOG_HOST,
|
|
6145
|
-
// A CLI process is short-lived: send each event as it happens
|
|
6146
|
-
// rather than batching for a flush that may never come.
|
|
6147
|
-
flushAt: 1,
|
|
6148
|
-
flushInterval: 0,
|
|
6149
|
-
requestTimeout: FLUSH_TIMEOUT_MS,
|
|
6150
|
-
// No IP-derived location on CLI events. The privacy policy lists
|
|
6151
|
-
// exactly what the CLI reports, and location is not on it.
|
|
6152
|
-
disableGeoip: true
|
|
6153
|
-
});
|
|
6154
|
-
return client;
|
|
6155
|
-
}
|
|
6156
|
-
var inFlight = /* @__PURE__ */ new Set();
|
|
6157
|
-
async function send(event, properties) {
|
|
6158
|
-
const state = await telemetryState();
|
|
6159
|
-
if (!state.enabled) return;
|
|
6160
|
-
const key = projectKey();
|
|
6161
|
-
if (!key) return;
|
|
6162
|
-
const distinctId = await resolveDistinctId();
|
|
6163
|
-
await getClient(key).captureImmediate({
|
|
6164
|
-
distinctId,
|
|
6165
|
-
event,
|
|
6166
|
-
properties: { ...properties, environment: environment() },
|
|
6167
|
-
groups: organizationId ? { [ORGANIZATION_GROUP]: organizationId } : void 0
|
|
6168
|
-
});
|
|
6169
|
-
}
|
|
6170
|
-
function track(event, properties) {
|
|
6171
|
-
const attempt = send(event, properties).catch(() => void 0);
|
|
6172
|
-
inFlight.add(attempt);
|
|
6173
|
-
void attempt.finally(() => inFlight.delete(attempt));
|
|
6174
|
-
}
|
|
6175
|
-
async function flushTelemetry(timeoutMs = FLUSH_TIMEOUT_MS) {
|
|
6176
|
-
if (inFlight.size === 0 && !client) return;
|
|
6177
|
-
const deadline = new Promise((resolve) => {
|
|
6178
|
-
const timer = setTimeout(resolve, timeoutMs);
|
|
6179
|
-
timer.unref();
|
|
6180
|
-
});
|
|
6181
|
-
await Promise.race([Promise.allSettled([...inFlight]), deadline]);
|
|
6182
|
-
if (client) {
|
|
6183
|
-
const closing = client.shutdown(timeoutMs).catch(() => void 0);
|
|
6184
|
-
client = null;
|
|
6185
|
-
await Promise.race([closing, deadline]);
|
|
6186
|
-
}
|
|
6187
|
-
}
|
|
6188
|
-
function runtimeProperties(cliVersion) {
|
|
6189
|
-
return {
|
|
6190
|
-
cli_version: cliVersion,
|
|
6191
|
-
node_version: process.version,
|
|
6192
|
-
platform: process.platform,
|
|
6193
|
-
ci: inCi()
|
|
6194
|
-
};
|
|
6195
|
-
}
|
|
6196
|
-
var activeCommand = null;
|
|
6197
|
-
function beginCommand(path12, cliVersion) {
|
|
6198
|
-
activeCommand = {
|
|
6199
|
-
path: path12,
|
|
6200
|
-
startedAt: Date.now(),
|
|
6201
|
-
cliVersion,
|
|
6202
|
-
reported: false
|
|
6203
|
-
};
|
|
6204
|
-
}
|
|
6205
|
-
function completeCommand(exitCode) {
|
|
6206
|
-
if (!activeCommand || activeCommand.reported) return;
|
|
6207
|
-
activeCommand.reported = true;
|
|
6208
|
-
track("cli_command_completed", {
|
|
6209
|
-
command: activeCommand.path,
|
|
6210
|
-
success: exitCode === 0,
|
|
6211
|
-
exit_code: exitCode,
|
|
6212
|
-
duration_ms: Date.now() - activeCommand.startedAt,
|
|
6213
|
-
json: wantsJson(),
|
|
6214
|
-
...runtimeProperties(activeCommand.cliVersion)
|
|
6215
|
-
});
|
|
6216
|
-
}
|
|
6217
|
-
async function exitAfterFlush(code) {
|
|
6218
|
-
completeCommand(code);
|
|
6219
|
-
await flushTelemetry();
|
|
6220
|
-
process.exit(code);
|
|
6221
|
-
}
|
|
6222
|
-
|
|
6223
|
-
// src/utils/env.ts
|
|
6224
|
-
import path3 from "path";
|
|
6225
|
-
import { fileURLToPath } from "url";
|
|
6226
|
-
import { createEnv } from "@t3-oss/env-core";
|
|
6227
|
-
import * as dotenv from "dotenv";
|
|
6228
|
-
import { z } from "zod/v4";
|
|
6229
|
-
var RELEASE_CONVEX_URL = "https://uncommon-monitor-462.convex.cloud"?.trim();
|
|
6230
|
-
var RELEASE_WEB_URL = "https://www.tolkbot.ai"?.trim();
|
|
6231
|
-
if (!RELEASE_CONVEX_URL) {
|
|
6232
|
-
try {
|
|
6233
|
-
const dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
6234
|
-
dotenv.config({
|
|
6235
|
-
path: path3.resolve(dir, "../../../.env"),
|
|
6236
|
-
quiet: true
|
|
6237
|
-
});
|
|
6238
|
-
} catch {
|
|
6239
|
-
}
|
|
6240
|
-
}
|
|
6241
|
-
var env = createEnv({
|
|
6242
|
-
server: {
|
|
6243
|
-
// Optional here, required at the point of use: commands that never
|
|
6244
|
-
// reach the backend (help, logout) must work without deployment config.
|
|
6245
|
-
CONVEX_URL: z.url().optional(),
|
|
6246
|
-
TOLKBOT_WEB_URL: z.url().default("http://localhost:3000")
|
|
6247
|
-
},
|
|
6248
|
-
runtimeEnv: {
|
|
6249
|
-
CONVEX_URL: RELEASE_CONVEX_URL || process.env.CONVEX_URL,
|
|
6250
|
-
TOLKBOT_WEB_URL: RELEASE_WEB_URL || process.env.TOLKBOT_WEB_URL
|
|
6251
|
-
},
|
|
6252
|
-
skipValidation: !!process.env.CI || process.env.npm_lifecycle_event === "lint",
|
|
6253
|
-
onValidationError: (issues) => {
|
|
6254
|
-
const details = issues.map((issue2) => {
|
|
6255
|
-
const name = issue2.path?.join(".") ?? "(unknown)";
|
|
6256
|
-
return `${name}: ${issue2.message}`;
|
|
6257
|
-
}).join("\n ");
|
|
6258
|
-
throw new CliError(`Invalid configuration:
|
|
6259
|
-
${details}`);
|
|
6260
|
-
}
|
|
6261
|
-
});
|
|
6262
|
-
function requireConvexUrl() {
|
|
6263
|
-
if (!env.CONVEX_URL) {
|
|
6264
|
-
throw new CliError(
|
|
6265
|
-
"CONVEX_URL is not set, so the CLI cannot reach the TolkBot backend.",
|
|
6266
|
-
"Set CONVEX_URL to your TolkBot deployment URL."
|
|
6267
|
-
);
|
|
6268
|
-
}
|
|
6269
|
-
return env.CONVEX_URL;
|
|
6270
|
-
}
|
|
6271
|
-
|
|
6272
|
-
// src/commands/auth/login.tsx
|
|
6273
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6274
|
-
var CALLBACK_PORTS = [3003, 3013, 3023, 3033, 3043];
|
|
6275
|
-
var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
6276
|
-
function generateVerificationCode() {
|
|
6277
|
-
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
6278
|
-
let code = "";
|
|
6279
|
-
for (let i = 0; i < 8; i++) {
|
|
6280
|
-
code += chars.charAt(crypto.randomInt(chars.length));
|
|
6281
|
-
}
|
|
6282
|
-
return code;
|
|
6283
|
-
}
|
|
6284
|
-
function respond(res, status, body) {
|
|
6285
|
-
res.writeHead(status, { "Content-Type": "text/html" });
|
|
6286
|
-
res.end(`
|
|
6287
|
-
<!DOCTYPE html>
|
|
6288
|
-
<html>
|
|
6289
|
-
<head><title>TolkBot CLI</title></head>
|
|
6290
|
-
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
6291
|
-
${body}
|
|
6292
|
-
</body>
|
|
6293
|
-
</html>
|
|
6294
|
-
`);
|
|
6295
|
-
}
|
|
6296
|
-
async function startCallbackServer(expectedCode) {
|
|
6297
|
-
let settle2;
|
|
6298
|
-
const settled = new Promise((resolve) => {
|
|
6299
|
-
settle2 = resolve;
|
|
6300
|
-
});
|
|
6301
|
-
const server = http.createServer((req, res) => {
|
|
6302
|
-
if (!req.url?.startsWith("/callback")) return;
|
|
6303
|
-
const url2 = new URL(req.url, "http://localhost");
|
|
6304
|
-
const token2 = url2.searchParams.get("token");
|
|
6305
|
-
const returnedCode = url2.searchParams.get("code");
|
|
6306
|
-
if (!token2) {
|
|
6307
|
-
respond(
|
|
6308
|
-
res,
|
|
6309
|
-
400,
|
|
6310
|
-
"<h1>Authentication failed</h1><p>No token received. Please try again.</p>"
|
|
6311
|
-
);
|
|
6312
|
-
settle2({ error: new Error("No token received from browser") });
|
|
6313
|
-
return;
|
|
6081
|
+
async function startCallbackServer(expectedCode) {
|
|
6082
|
+
let settle2;
|
|
6083
|
+
const settled = new Promise((resolve) => {
|
|
6084
|
+
settle2 = resolve;
|
|
6085
|
+
});
|
|
6086
|
+
const server = http.createServer((req, res) => {
|
|
6087
|
+
if (!req.url?.startsWith("/callback")) return;
|
|
6088
|
+
const url2 = new URL(req.url, "http://localhost");
|
|
6089
|
+
const token2 = url2.searchParams.get("token");
|
|
6090
|
+
const returnedCode = url2.searchParams.get("code");
|
|
6091
|
+
if (!token2) {
|
|
6092
|
+
respond(
|
|
6093
|
+
res,
|
|
6094
|
+
400,
|
|
6095
|
+
"<h1>Authentication failed</h1><p>No token received. Please try again.</p>"
|
|
6096
|
+
);
|
|
6097
|
+
settle2({ error: new Error("No token received from browser") });
|
|
6098
|
+
return;
|
|
6314
6099
|
}
|
|
6315
6100
|
if (returnedCode !== expectedCode) {
|
|
6316
6101
|
respond(
|
|
@@ -6404,22 +6189,16 @@ function LoginUI({ state }) {
|
|
|
6404
6189
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Confirm this code matches the one shown in your browser" })
|
|
6405
6190
|
] });
|
|
6406
6191
|
}
|
|
6407
|
-
if (state.type === "success") {
|
|
6408
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
6409
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
6410
|
-
/* @__PURE__ */ jsx(Text, { color: "green", children: "\u2713" }),
|
|
6411
|
-
/* @__PURE__ */ jsx(Text, { children: " Successfully authenticated!" })
|
|
6412
|
-
] }),
|
|
6413
|
-
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
6414
|
-
"Token saved to ",
|
|
6415
|
-
getTokenPath()
|
|
6416
|
-
] }) }),
|
|
6417
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: validityNote(state.expiresAt) })
|
|
6418
|
-
] });
|
|
6419
|
-
}
|
|
6420
6192
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
6421
|
-
/* @__PURE__ */
|
|
6422
|
-
|
|
6193
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
6194
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: "\u2713" }),
|
|
6195
|
+
/* @__PURE__ */ jsx(Text, { children: " Successfully authenticated!" })
|
|
6196
|
+
] }),
|
|
6197
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
6198
|
+
"Token saved to ",
|
|
6199
|
+
getTokenPath()
|
|
6200
|
+
] }) }),
|
|
6201
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: validityNote(state.expiresAt) })
|
|
6423
6202
|
] });
|
|
6424
6203
|
}
|
|
6425
6204
|
async function runPlainLogin(opts, verificationCode) {
|
|
@@ -6440,12 +6219,17 @@ async function runPlainLogin(opts, verificationCode) {
|
|
|
6440
6219
|
console.log(validityNote(decodeTokenExpiry(token)));
|
|
6441
6220
|
} catch (error62) {
|
|
6442
6221
|
server?.close();
|
|
6443
|
-
|
|
6444
|
-
`Authentication failed: ${error62 instanceof Error ? error62.message : String(error62)}`
|
|
6445
|
-
);
|
|
6446
|
-
return exitAfterFlush(1);
|
|
6222
|
+
throw loginFailed(error62);
|
|
6447
6223
|
}
|
|
6448
6224
|
}
|
|
6225
|
+
function loginFailed(error62) {
|
|
6226
|
+
if (error62 instanceof CliError) return error62;
|
|
6227
|
+
return new CliError(
|
|
6228
|
+
`Authentication failed: ${error62 instanceof Error ? error62.message : String(error62)}`,
|
|
6229
|
+
"Run 'tolkbot auth login' again.",
|
|
6230
|
+
"login_failed"
|
|
6231
|
+
);
|
|
6232
|
+
}
|
|
6449
6233
|
function loginUrl(port, verificationCode) {
|
|
6450
6234
|
return `${env.TOLKBOT_WEB_URL}/cli/login/confirm?port=${port}&code=${verificationCode}`;
|
|
6451
6235
|
}
|
|
@@ -6493,19 +6277,7 @@ async function runAuthLogin(opts = {}) {
|
|
|
6493
6277
|
} catch (error62) {
|
|
6494
6278
|
server?.close();
|
|
6495
6279
|
unmount();
|
|
6496
|
-
|
|
6497
|
-
/* @__PURE__ */ jsx(
|
|
6498
|
-
LoginUI,
|
|
6499
|
-
{
|
|
6500
|
-
state: {
|
|
6501
|
-
type: "error",
|
|
6502
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
6503
|
-
}
|
|
6504
|
-
}
|
|
6505
|
-
)
|
|
6506
|
-
);
|
|
6507
|
-
await errorUi.waitUntilExit();
|
|
6508
|
-
return exitAfterFlush(1);
|
|
6280
|
+
throw loginFailed(error62);
|
|
6509
6281
|
}
|
|
6510
6282
|
}
|
|
6511
6283
|
|
|
@@ -6541,10 +6313,10 @@ function readProperty(candidate, key) {
|
|
|
6541
6313
|
// src/lib/convex-client.ts
|
|
6542
6314
|
import { ConvexHttpClient } from "convex/browser";
|
|
6543
6315
|
var BACKEND_METHODS = /* @__PURE__ */ new Set(["query", "mutation", "action"]);
|
|
6544
|
-
var
|
|
6316
|
+
var client = null;
|
|
6545
6317
|
function getConvexClient() {
|
|
6546
|
-
|
|
6547
|
-
return
|
|
6318
|
+
client ??= new ConvexHttpClient(requireConvexUrl());
|
|
6319
|
+
return client;
|
|
6548
6320
|
}
|
|
6549
6321
|
function translateAuthError(error62, resolved) {
|
|
6550
6322
|
const signedOut = findCliSignedOut(error62);
|
|
@@ -6552,13 +6324,15 @@ function translateAuthError(error62, resolved) {
|
|
|
6552
6324
|
const at = new Date(signedOut.signedOutAt).toLocaleString();
|
|
6553
6325
|
return new AuthError(
|
|
6554
6326
|
`These credentials were signed out on ${at} (from ${describeTokenSource(resolved.source)}).`,
|
|
6555
|
-
reauthHint(resolved.source)
|
|
6327
|
+
reauthHint(resolved.source),
|
|
6328
|
+
"signed_out"
|
|
6556
6329
|
);
|
|
6557
6330
|
}
|
|
6558
6331
|
if (!isUnauthenticatedError(error62)) return error62;
|
|
6559
6332
|
return new AuthError(
|
|
6560
6333
|
`Your token was rejected (from ${describeTokenSource(resolved.source)}). It may be revoked, malformed, or issued for a different deployment.`,
|
|
6561
|
-
reauthHint(resolved.source)
|
|
6334
|
+
reauthHint(resolved.source),
|
|
6335
|
+
"token_rejected"
|
|
6562
6336
|
);
|
|
6563
6337
|
}
|
|
6564
6338
|
function withAuthErrors(target, resolved) {
|
|
@@ -6590,7 +6364,6 @@ function warnOnceIfExpiringSoon(resolved) {
|
|
|
6590
6364
|
console.error(warning);
|
|
6591
6365
|
}
|
|
6592
6366
|
async function getAuthenticatedConvexClient() {
|
|
6593
|
-
const convexClient = getConvexClient();
|
|
6594
6367
|
const resolved = await resolveToken();
|
|
6595
6368
|
if (!resolved) {
|
|
6596
6369
|
throw new AuthError(
|
|
@@ -6602,9 +6375,11 @@ async function getAuthenticatedConvexClient() {
|
|
|
6602
6375
|
const expiredAt = resolved.expiresAt ? new Date(resolved.expiresAt * 1e3).toLocaleString() : "an unknown time";
|
|
6603
6376
|
throw new AuthError(
|
|
6604
6377
|
`Your token expired at ${expiredAt} (from ${describeTokenSource(resolved.source)}).`,
|
|
6605
|
-
reauthHint(resolved.source)
|
|
6378
|
+
reauthHint(resolved.source),
|
|
6379
|
+
"token_expired"
|
|
6606
6380
|
);
|
|
6607
6381
|
}
|
|
6382
|
+
const convexClient = getConvexClient();
|
|
6608
6383
|
warnOnceIfExpiringSoon(resolved);
|
|
6609
6384
|
convexClient.setAuth(resolved.token);
|
|
6610
6385
|
return withAuthErrors(convexClient, resolved);
|
|
@@ -6637,11 +6412,6 @@ function jsonSuccess(data) {
|
|
|
6637
6412
|
console.log(JSON.stringify(data, null, 2));
|
|
6638
6413
|
});
|
|
6639
6414
|
}
|
|
6640
|
-
function jsonError(message) {
|
|
6641
|
-
throw requestExit(1, () => {
|
|
6642
|
-
console.error(JSON.stringify({ error: message }, null, 2));
|
|
6643
|
-
});
|
|
6644
|
-
}
|
|
6645
6415
|
function jsonResult(data, exitCode) {
|
|
6646
6416
|
console.log(JSON.stringify(data, null, 2));
|
|
6647
6417
|
process.exitCode = exitCode;
|
|
@@ -6657,12 +6427,6 @@ function stillValidNote(expiresAt) {
|
|
|
6657
6427
|
return `The token stays valid anywhere else it was copied ${until}. Run 'tolkbot auth logout --all' to sign out every CLI session.`;
|
|
6658
6428
|
}
|
|
6659
6429
|
function LogoutUI({ state }) {
|
|
6660
|
-
if (state.type === "error") {
|
|
6661
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
6662
|
-
/* @__PURE__ */ jsx2(Text2, { color: "red", children: "\u2717 Logout failed" }),
|
|
6663
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: state.message })
|
|
6664
|
-
] });
|
|
6665
|
-
}
|
|
6666
6430
|
const signedOutAll = state.signedOutAt !== null;
|
|
6667
6431
|
let headline = state.hadToken ? " Successfully logged out!" : " Already logged out.";
|
|
6668
6432
|
if (signedOutAll) headline = " Signed out every CLI session.";
|
|
@@ -6697,58 +6461,77 @@ async function signOutAllCliSessions() {
|
|
|
6697
6461
|
return signedOutAt;
|
|
6698
6462
|
}
|
|
6699
6463
|
async function runAuthLogout(opts = {}) {
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
});
|
|
6722
|
-
}
|
|
6723
|
-
const ui = render2(
|
|
6724
|
-
/* @__PURE__ */ jsx2(
|
|
6725
|
-
LogoutUI,
|
|
6726
|
-
{
|
|
6727
|
-
state: {
|
|
6728
|
-
type: "success",
|
|
6729
|
-
hadToken,
|
|
6730
|
-
envTokenStillSet,
|
|
6731
|
-
removedTokenExpiresAt,
|
|
6732
|
-
signedOutAt
|
|
6733
|
-
}
|
|
6734
|
-
}
|
|
6735
|
-
)
|
|
6736
|
-
);
|
|
6737
|
-
await ui.waitUntilExit();
|
|
6738
|
-
} catch (error62) {
|
|
6739
|
-
const message = error62 instanceof Error ? error62.message : String(error62);
|
|
6740
|
-
if (opts.json) {
|
|
6741
|
-
jsonError(message);
|
|
6742
|
-
}
|
|
6743
|
-
const ui = render2(/* @__PURE__ */ jsx2(LogoutUI, { state: { type: "error", message } }));
|
|
6744
|
-
await ui.waitUntilExit();
|
|
6745
|
-
return exitAfterFlush(1);
|
|
6464
|
+
const signedOutAt = opts.all ? await signOutAllCliSessions() : null;
|
|
6465
|
+
const stored = await loadToken();
|
|
6466
|
+
const hadToken = stored !== null;
|
|
6467
|
+
const removedTokenExpiresAt = stored ? decodeTokenExpiry(stored) : null;
|
|
6468
|
+
await deleteToken();
|
|
6469
|
+
const envTokenStillSet = hasEnvToken();
|
|
6470
|
+
if (opts.json) {
|
|
6471
|
+
const warnings = [
|
|
6472
|
+
...signedOutAt === null && hadToken ? [stillValidNote(removedTokenExpiresAt)] : [],
|
|
6473
|
+
...envTokenStillSet ? [
|
|
6474
|
+
signedOutAt === null ? `$${TOKEN_ENV_VAR} is still set and takes precedence over ${getTokenPath()}.` : `$${TOKEN_ENV_VAR} is still set; it was signed out too, so remove it.`
|
|
6475
|
+
] : []
|
|
6476
|
+
];
|
|
6477
|
+
jsonSuccess({
|
|
6478
|
+
loggedOut: true,
|
|
6479
|
+
hadStoredToken: hadToken,
|
|
6480
|
+
envTokenStillSet,
|
|
6481
|
+
signedOutAllCliSessions: signedOutAt !== null,
|
|
6482
|
+
...signedOutAt !== null ? { signedOutAt: new Date(signedOutAt).toISOString() } : {},
|
|
6483
|
+
...warnings.length > 0 ? { warning: warnings.join(" ") } : {}
|
|
6484
|
+
});
|
|
6746
6485
|
}
|
|
6486
|
+
render2(
|
|
6487
|
+
/* @__PURE__ */ jsx2(
|
|
6488
|
+
LogoutUI,
|
|
6489
|
+
{
|
|
6490
|
+
state: {
|
|
6491
|
+
type: "success",
|
|
6492
|
+
hadToken,
|
|
6493
|
+
envTokenStillSet,
|
|
6494
|
+
removedTokenExpiresAt,
|
|
6495
|
+
signedOutAt
|
|
6496
|
+
}
|
|
6497
|
+
}
|
|
6498
|
+
)
|
|
6499
|
+
).unmount();
|
|
6747
6500
|
}
|
|
6748
6501
|
|
|
6749
6502
|
// src/commands/auth/whoami.tsx
|
|
6750
6503
|
import { Spinner as Spinner2 } from "@inkjs/ui";
|
|
6751
|
-
import { Box as Box3, render as
|
|
6504
|
+
import { Box as Box3, render as render4, Text as Text3 } from "ink";
|
|
6505
|
+
|
|
6506
|
+
// src/utils/terminal.ts
|
|
6507
|
+
import { render as render3 } from "ink";
|
|
6508
|
+
function isInteractive() {
|
|
6509
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
6510
|
+
}
|
|
6511
|
+
var jsonOutput = null;
|
|
6512
|
+
function setJsonOutput(value) {
|
|
6513
|
+
jsonOutput = value;
|
|
6514
|
+
}
|
|
6515
|
+
function wantsJson(argv = process.argv) {
|
|
6516
|
+
if (jsonOutput !== null) return jsonOutput;
|
|
6517
|
+
if (argv.includes("--no-json")) return false;
|
|
6518
|
+
if (argv.includes("--json")) return true;
|
|
6519
|
+
return !isInteractive();
|
|
6520
|
+
}
|
|
6521
|
+
function resolveJson(flag) {
|
|
6522
|
+
return flag ?? !isInteractive();
|
|
6523
|
+
}
|
|
6524
|
+
var NOTHING = {
|
|
6525
|
+
unmount: () => void 0,
|
|
6526
|
+
rerender: (_node) => void 0,
|
|
6527
|
+
waitUntilExit: () => Promise.resolve()
|
|
6528
|
+
};
|
|
6529
|
+
function renderTransient(node2) {
|
|
6530
|
+
if (!isInteractive()) return NOTHING;
|
|
6531
|
+
return render3(node2);
|
|
6532
|
+
}
|
|
6533
|
+
|
|
6534
|
+
// src/commands/auth/whoami.tsx
|
|
6752
6535
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
6753
6536
|
function formatExpiry(expiresAt) {
|
|
6754
6537
|
if (expiresAt === null) return "unknown";
|
|
@@ -6766,118 +6549,73 @@ function WhoamiUI({ state }) {
|
|
|
6766
6549
|
/* @__PURE__ */ jsx3(Text3, { children: " Checking authentication\u2026" })
|
|
6767
6550
|
] });
|
|
6768
6551
|
}
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
/* @__PURE__ */
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
/* @__PURE__ */
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
/* @__PURE__ */
|
|
6783
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2502 " }),
|
|
6784
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
6785
|
-
" ",
|
|
6786
|
-
describeExpiry(state.resolved)
|
|
6787
|
-
] })
|
|
6788
|
-
] }),
|
|
6789
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
6790
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2514 " }),
|
|
6791
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2713" })
|
|
6792
|
-
] })
|
|
6793
|
-
] });
|
|
6794
|
-
}
|
|
6795
|
-
if (state.type === "not-logged-in") {
|
|
6796
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
6797
|
-
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u26A0 Not logged in" }),
|
|
6552
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
6553
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
6554
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u250C " }),
|
|
6555
|
+
/* @__PURE__ */ jsx3(Text3, { children: " Credentials " }),
|
|
6556
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: describeTokenSource(state.resolved.source) })
|
|
6557
|
+
] }),
|
|
6558
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2502" }),
|
|
6559
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
6560
|
+
/* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u25CF " }),
|
|
6561
|
+
/* @__PURE__ */ jsx3(Text3, { children: " Logged in as " }),
|
|
6562
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "yellow", children: state.email })
|
|
6563
|
+
] }),
|
|
6564
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
6565
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2502 " }),
|
|
6798
6566
|
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
6799
|
-
"
|
|
6800
|
-
|
|
6801
|
-
", or set $",
|
|
6802
|
-
TOKEN_ENV_VAR,
|
|
6803
|
-
" for CI."
|
|
6567
|
+
" ",
|
|
6568
|
+
describeExpiry(state.resolved)
|
|
6804
6569
|
] })
|
|
6805
|
-
] })
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
6811
|
-
"From ",
|
|
6812
|
-
describeTokenSource(state.resolved.source),
|
|
6813
|
-
", expired",
|
|
6814
|
-
" ",
|
|
6815
|
-
formatExpiry(state.resolved.expiresAt)
|
|
6816
|
-
] }),
|
|
6817
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: reauthHint(state.resolved.source) })
|
|
6818
|
-
] });
|
|
6819
|
-
}
|
|
6820
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
6821
|
-
/* @__PURE__ */ jsx3(Text3, { color: "red", children: "\u2717 Failed to read authentication" }),
|
|
6822
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: state.message })
|
|
6570
|
+
] }),
|
|
6571
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
6572
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2514 " }),
|
|
6573
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2713" })
|
|
6574
|
+
] })
|
|
6823
6575
|
] });
|
|
6824
6576
|
}
|
|
6825
|
-
async function renderAndExit(state, exitCode) {
|
|
6826
|
-
const ui = render3(/* @__PURE__ */ jsx3(WhoamiUI, { state }));
|
|
6827
|
-
await ui.waitUntilExit();
|
|
6828
|
-
return exitAfterFlush(exitCode);
|
|
6829
|
-
}
|
|
6830
6577
|
async function runAuthWhoami(opts = {}) {
|
|
6831
6578
|
const resolved = await resolveToken();
|
|
6832
6579
|
if (!resolved) {
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6580
|
+
throw new AuthError(
|
|
6581
|
+
"Not logged in.",
|
|
6582
|
+
`Run 'tolkbot auth login', or set $${TOKEN_ENV_VAR} for CI.`
|
|
6583
|
+
);
|
|
6837
6584
|
}
|
|
6838
6585
|
if (isTokenExpired(resolved)) {
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
return renderAndExit({ type: "expired", resolved }, 1);
|
|
6586
|
+
throw new AuthError(
|
|
6587
|
+
`Your token (from ${describeTokenSource(resolved.source)}) expired at ${formatExpiry(resolved.expiresAt)}.`,
|
|
6588
|
+
reauthHint(resolved.source),
|
|
6589
|
+
"token_expired"
|
|
6590
|
+
);
|
|
6845
6591
|
}
|
|
6846
|
-
const spinner = opts.json ? null :
|
|
6847
|
-
|
|
6592
|
+
const spinner = opts.json ? null : renderTransient(/* @__PURE__ */ jsx3(WhoamiUI, { state: { type: "loading" } }));
|
|
6593
|
+
let email3;
|
|
6848
6594
|
try {
|
|
6849
6595
|
const convexClient = await getAuthenticatedConvexClient();
|
|
6850
6596
|
const user = await convexClient.query(api.users.current, {});
|
|
6851
6597
|
if (!user) {
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
}
|
|
6858
|
-
unmount();
|
|
6859
|
-
if (opts.json) {
|
|
6860
|
-
jsonSuccess({
|
|
6861
|
-
authenticated: true,
|
|
6862
|
-
email: user.email,
|
|
6863
|
-
tokenSource: resolved.source,
|
|
6864
|
-
tokenPath: getTokenPath(),
|
|
6865
|
-
expiresAt: resolved.expiresAt ? new Date(resolved.expiresAt * 1e3).toISOString() : null,
|
|
6866
|
-
expiresInSeconds: expiresInSeconds(resolved)
|
|
6867
|
-
});
|
|
6868
|
-
}
|
|
6869
|
-
return renderAndExit(
|
|
6870
|
-
{ type: "success", email: user.email, resolved },
|
|
6871
|
-
0
|
|
6872
|
-
);
|
|
6873
|
-
} catch (error62) {
|
|
6874
|
-
unmount();
|
|
6875
|
-
const message = error62 instanceof Error ? error62.message : String(error62);
|
|
6876
|
-
if (opts.json) {
|
|
6877
|
-
jsonError(message);
|
|
6598
|
+
throw new AuthError(
|
|
6599
|
+
"Your token is valid, but TolkBot has no user for it.",
|
|
6600
|
+
reauthHint(resolved.source),
|
|
6601
|
+
"token_rejected"
|
|
6602
|
+
);
|
|
6878
6603
|
}
|
|
6879
|
-
|
|
6604
|
+
email3 = user.email;
|
|
6605
|
+
} finally {
|
|
6606
|
+
spinner?.unmount();
|
|
6607
|
+
}
|
|
6608
|
+
if (opts.json) {
|
|
6609
|
+
jsonSuccess({
|
|
6610
|
+
authenticated: true,
|
|
6611
|
+
email: email3,
|
|
6612
|
+
tokenSource: resolved.source,
|
|
6613
|
+
tokenPath: getTokenPath(),
|
|
6614
|
+
expiresAt: resolved.expiresAt ? new Date(resolved.expiresAt * 1e3).toISOString() : null,
|
|
6615
|
+
expiresInSeconds: expiresInSeconds(resolved)
|
|
6616
|
+
});
|
|
6880
6617
|
}
|
|
6618
|
+
render4(/* @__PURE__ */ jsx3(WhoamiUI, { state: { type: "success", email: email3, resolved } })).unmount();
|
|
6881
6619
|
}
|
|
6882
6620
|
|
|
6883
6621
|
// src/commands/auth/index.ts
|
|
@@ -6918,15 +6656,15 @@ import { z as z4 } from "zod/v4";
|
|
|
6918
6656
|
|
|
6919
6657
|
// src/commands/glossary/create.tsx
|
|
6920
6658
|
import { Spinner as Spinner3 } from "@inkjs/ui";
|
|
6921
|
-
import { Box as Box4, render as
|
|
6659
|
+
import { Box as Box4, render as render5, Text as Text4 } from "ink";
|
|
6922
6660
|
|
|
6923
6661
|
// ../config/dist/index.js
|
|
6924
|
-
import * as
|
|
6925
|
-
import * as
|
|
6662
|
+
import * as fs2 from "fs";
|
|
6663
|
+
import * as path3 from "path";
|
|
6926
6664
|
import * as fs22 from "fs";
|
|
6927
6665
|
import * as path22 from "path";
|
|
6928
6666
|
import { execSync } from "child_process";
|
|
6929
|
-
import * as
|
|
6667
|
+
import * as fs3 from "fs/promises";
|
|
6930
6668
|
import * as path32 from "path";
|
|
6931
6669
|
var __defProp2 = Object.defineProperty;
|
|
6932
6670
|
var __export = (target, all) => {
|
|
@@ -6934,12 +6672,12 @@ var __export = (target, all) => {
|
|
|
6934
6672
|
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
6935
6673
|
};
|
|
6936
6674
|
function readJSON(p) {
|
|
6937
|
-
return JSON.parse(
|
|
6675
|
+
return JSON.parse(fs2.readFileSync(p, "utf-8"));
|
|
6938
6676
|
}
|
|
6939
6677
|
function readPackageDeps(cwd) {
|
|
6940
6678
|
try {
|
|
6941
|
-
const pkgPath =
|
|
6942
|
-
if (!
|
|
6679
|
+
const pkgPath = path3.join(cwd, "package.json");
|
|
6680
|
+
if (!fs2.existsSync(pkgPath)) return {};
|
|
6943
6681
|
const pkg = readJSON(pkgPath);
|
|
6944
6682
|
return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
6945
6683
|
} catch {
|
|
@@ -6950,8 +6688,8 @@ function hasPackage(deps, name) {
|
|
|
6950
6688
|
return name in deps;
|
|
6951
6689
|
}
|
|
6952
6690
|
function detectPackageManager(cwd) {
|
|
6953
|
-
if (
|
|
6954
|
-
if (
|
|
6691
|
+
if (fs2.existsSync(path3.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
6692
|
+
if (fs2.existsSync(path3.join(cwd, "yarn.lock"))) return "yarn";
|
|
6955
6693
|
return "npm";
|
|
6956
6694
|
}
|
|
6957
6695
|
function tryReadNextI18n(cwd) {
|
|
@@ -6961,12 +6699,12 @@ function tryReadNextI18n(cwd) {
|
|
|
6961
6699
|
"next.config.cjs",
|
|
6962
6700
|
"next.config.ts",
|
|
6963
6701
|
"next.config.mts"
|
|
6964
|
-
].map((f) =>
|
|
6702
|
+
].map((f) => path3.join(cwd, f));
|
|
6965
6703
|
const res = {};
|
|
6966
6704
|
for (const f of files) {
|
|
6967
|
-
if (!
|
|
6705
|
+
if (!fs2.existsSync(f)) continue;
|
|
6968
6706
|
try {
|
|
6969
|
-
const txt =
|
|
6707
|
+
const txt = fs2.readFileSync(f, "utf-8");
|
|
6970
6708
|
const locMatch = txt.match(/locales\s*:\s*\[([\s\S]*?)\]/m);
|
|
6971
6709
|
if (locMatch) {
|
|
6972
6710
|
const arr = Array.from(
|
|
@@ -6986,14 +6724,14 @@ function tryReadNextI18n(cwd) {
|
|
|
6986
6724
|
}
|
|
6987
6725
|
function listSubdirs(p) {
|
|
6988
6726
|
try {
|
|
6989
|
-
return
|
|
6727
|
+
return fs2.readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6990
6728
|
} catch {
|
|
6991
6729
|
return [];
|
|
6992
6730
|
}
|
|
6993
6731
|
}
|
|
6994
6732
|
function listJSONFiles(p) {
|
|
6995
6733
|
try {
|
|
6996
|
-
return
|
|
6734
|
+
return fs2.readdirSync(p, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => e.name);
|
|
6997
6735
|
} catch {
|
|
6998
6736
|
return [];
|
|
6999
6737
|
}
|
|
@@ -7025,13 +6763,13 @@ function detectProject(cwd) {
|
|
|
7025
6763
|
const hasVue = hasPackage(deps, "vue");
|
|
7026
6764
|
const hasExpo = hasPackage(deps, "expo");
|
|
7027
6765
|
if (hasNext) {
|
|
7028
|
-
const hasAppDir =
|
|
7029
|
-
const hasPagesDir =
|
|
6766
|
+
const hasAppDir = fs2.existsSync(path3.join(cwd, "app"));
|
|
6767
|
+
const hasPagesDir = fs2.existsSync(path3.join(cwd, "pages"));
|
|
7030
6768
|
projectType = hasAppDir ? "next-app" : hasPagesDir ? "next-pages" : "next-app";
|
|
7031
6769
|
} else if (hasExpo) {
|
|
7032
6770
|
projectType = "expo";
|
|
7033
6771
|
} else if (hasReact) {
|
|
7034
|
-
const hasViteConfig =
|
|
6772
|
+
const hasViteConfig = fs2.existsSync(path3.join(cwd, "vite.config.js")) || fs2.existsSync(path3.join(cwd, "vite.config.ts"));
|
|
7035
6773
|
projectType = hasViteConfig ? "react-vite" : "react-cra";
|
|
7036
6774
|
} else if (hasVue) {
|
|
7037
6775
|
projectType = "vue";
|
|
@@ -7063,36 +6801,36 @@ function detectFromRepo(cwd) {
|
|
|
7063
6801
|
out.framework = "i18next";
|
|
7064
6802
|
}
|
|
7065
6803
|
const candidates = [
|
|
7066
|
-
|
|
6804
|
+
path3.join(cwd, "messages"),
|
|
7067
6805
|
// next-intl pattern
|
|
7068
|
-
|
|
7069
|
-
|
|
6806
|
+
path3.join(cwd, "locales"),
|
|
6807
|
+
path3.join(cwd, "public", "locales"),
|
|
7070
6808
|
// next-i18next / i18next pattern
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
].filter((p) =>
|
|
6809
|
+
path3.join(cwd, "app", "locales"),
|
|
6810
|
+
path3.join(cwd, "app", "messages"),
|
|
6811
|
+
path3.join(cwd, "src", "locales"),
|
|
6812
|
+
path3.join(cwd, "src", "messages")
|
|
6813
|
+
].filter((p) => fs2.existsSync(p) && fs2.statSync(p).isDirectory());
|
|
7076
6814
|
if (candidates.length) {
|
|
7077
6815
|
const dir = candidates[0];
|
|
7078
6816
|
out.outDir = dir;
|
|
7079
|
-
if (dir?.includes(`${
|
|
6817
|
+
if (dir?.includes(`${path3.sep}public${path3.sep}locales`)) {
|
|
7080
6818
|
out.include = ["public/locales/*/*.json"];
|
|
7081
|
-
} else if (dir?.endsWith(`${
|
|
6819
|
+
} else if (dir?.endsWith(`${path3.sep}messages`)) {
|
|
7082
6820
|
out.include = ["messages/*.json"];
|
|
7083
6821
|
} else {
|
|
7084
6822
|
out.include = ["**/messages/*.json", "**/locales/*.json"];
|
|
7085
6823
|
}
|
|
7086
6824
|
let locales;
|
|
7087
|
-
if (dir?.includes(`${
|
|
6825
|
+
if (dir?.includes(`${path3.sep}public${path3.sep}locales`)) {
|
|
7088
6826
|
const subdirs = listSubdirs(dir);
|
|
7089
6827
|
locales = subdirs.filter((name) => {
|
|
7090
|
-
const hasFiles = listJSONFiles(
|
|
6828
|
+
const hasFiles = listJSONFiles(path3.join(dir, name)).length > 0;
|
|
7091
6829
|
return hasFiles;
|
|
7092
6830
|
});
|
|
7093
6831
|
} else {
|
|
7094
6832
|
const files = listJSONFiles(dir ?? "");
|
|
7095
|
-
locales = files.map((f) =>
|
|
6833
|
+
locales = files.map((f) => path3.parse(f).name);
|
|
7096
6834
|
}
|
|
7097
6835
|
const nextI18n = tryReadNextI18n(cwd);
|
|
7098
6836
|
const preferred = nextI18n.defaultLocale;
|
|
@@ -26858,7 +26596,7 @@ function getFrameworkSetup(framework) {
|
|
|
26858
26596
|
function installDependencies(cwd, packageManager, deps) {
|
|
26859
26597
|
if (deps.length === 0) return;
|
|
26860
26598
|
const cmd = packageManager === "npm" ? `npm install ${deps.join(" ")}` : packageManager === "yarn" ? `yarn add ${deps.join(" ")}` : `pnpm add ${deps.join(" ")}`;
|
|
26861
|
-
execSync(cmd, { stdio: "
|
|
26599
|
+
execSync(cmd, { stdio: ["ignore", 2, 2], cwd });
|
|
26862
26600
|
}
|
|
26863
26601
|
async function createLocaleFiles(cwd, framework, baseLocale, targetLocales) {
|
|
26864
26602
|
const setup = FRAMEWORK_SETUPS[framework];
|
|
@@ -26866,22 +26604,22 @@ async function createLocaleFiles(cwd, framework, baseLocale, targetLocales) {
|
|
|
26866
26604
|
for (const file2 of setup.files) {
|
|
26867
26605
|
if (!file2.path.includes("{{lng}}")) {
|
|
26868
26606
|
const filePath = path32.join(cwd, file2.path);
|
|
26869
|
-
await
|
|
26870
|
-
await
|
|
26607
|
+
await fs3.mkdir(path32.dirname(filePath), { recursive: true });
|
|
26608
|
+
await fs3.writeFile(filePath, file2.content);
|
|
26871
26609
|
createdFiles.push(file2.path);
|
|
26872
26610
|
continue;
|
|
26873
26611
|
}
|
|
26874
26612
|
const basePath = file2.path.replace("{{lng}}", baseLocale);
|
|
26875
26613
|
const baseFilePath = path32.join(cwd, basePath);
|
|
26876
|
-
await
|
|
26877
|
-
await
|
|
26614
|
+
await fs3.mkdir(path32.dirname(baseFilePath), { recursive: true });
|
|
26615
|
+
await fs3.writeFile(baseFilePath, file2.content);
|
|
26878
26616
|
createdFiles.push(basePath);
|
|
26879
26617
|
for (const locale of targetLocales) {
|
|
26880
26618
|
const targetPath = file2.path.replace("{{lng}}", locale);
|
|
26881
26619
|
const targetFilePath = path32.join(cwd, targetPath);
|
|
26882
|
-
await
|
|
26620
|
+
await fs3.mkdir(path32.dirname(targetFilePath), { recursive: true });
|
|
26883
26621
|
const emptyContent = file2.path.endsWith(".json") ? "{}" : file2.content;
|
|
26884
|
-
await
|
|
26622
|
+
await fs3.writeFile(targetFilePath, emptyContent);
|
|
26885
26623
|
createdFiles.push(targetPath);
|
|
26886
26624
|
}
|
|
26887
26625
|
}
|
|
@@ -26906,18 +26644,12 @@ async function setupFramework(cwd, framework, baseLocale, targetLocales, package
|
|
|
26906
26644
|
async function getProjectContext(convexClient) {
|
|
26907
26645
|
const baseDir = process.cwd();
|
|
26908
26646
|
const config3 = readConfig(baseDir);
|
|
26909
|
-
if (!config3)
|
|
26910
|
-
throw new Error(
|
|
26911
|
-
"No tolkbot.json found. Run 'tolkbot init' to create one."
|
|
26912
|
-
);
|
|
26913
|
-
}
|
|
26647
|
+
if (!config3) throw noConfigError();
|
|
26914
26648
|
const projectId = config3.config.projectId;
|
|
26915
26649
|
const project = await convexClient.query(api.projects.getProjectForCli, {
|
|
26916
26650
|
projectId
|
|
26917
26651
|
});
|
|
26918
|
-
if (!project)
|
|
26919
|
-
throw new Error("Project not found. Please run 'tolkbot init' again.");
|
|
26920
|
-
}
|
|
26652
|
+
if (!project) throw projectNotFoundError();
|
|
26921
26653
|
return {
|
|
26922
26654
|
projectId,
|
|
26923
26655
|
organizationId: project.organizationId,
|
|
@@ -26928,15 +26660,17 @@ function parseTranslations(raw) {
|
|
|
26928
26660
|
return raw.map((entry) => {
|
|
26929
26661
|
const colonIdx = entry.indexOf(":");
|
|
26930
26662
|
if (colonIdx === -1) {
|
|
26931
|
-
throw new
|
|
26932
|
-
`Invalid translation format: "${entry}"
|
|
26663
|
+
throw new UsageError(
|
|
26664
|
+
`Invalid translation format: "${entry}".`,
|
|
26665
|
+
'Expected "locale:text", such as "no:Hei".'
|
|
26933
26666
|
);
|
|
26934
26667
|
}
|
|
26935
26668
|
const locale = entry.slice(0, colonIdx);
|
|
26936
26669
|
const text = entry.slice(colonIdx + 1);
|
|
26937
26670
|
if (!locale || !text) {
|
|
26938
|
-
throw new
|
|
26939
|
-
`Invalid translation format: "${entry}"
|
|
26671
|
+
throw new UsageError(
|
|
26672
|
+
`Invalid translation format: "${entry}".`,
|
|
26673
|
+
"Both locale and text are required."
|
|
26940
26674
|
);
|
|
26941
26675
|
}
|
|
26942
26676
|
return { locale, text };
|
|
@@ -26952,56 +26686,27 @@ function CreateUI({ state }) {
|
|
|
26952
26686
|
/* @__PURE__ */ jsx4(Text4, { children: " Creating glossary term\u2026" })
|
|
26953
26687
|
] });
|
|
26954
26688
|
}
|
|
26955
|
-
if (state.type === "success") {
|
|
26956
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
26957
|
-
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
26958
|
-
/* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u2713" }),
|
|
26959
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
26960
|
-
" ",
|
|
26961
|
-
"Created glossary term: ",
|
|
26962
|
-
/* @__PURE__ */ jsx4(Text4, { bold: true, children: state.term })
|
|
26963
|
-
] })
|
|
26964
|
-
] }),
|
|
26965
|
-
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
26966
|
-
"ID: ",
|
|
26967
|
-
state.id
|
|
26968
|
-
] }) })
|
|
26969
|
-
] });
|
|
26970
|
-
}
|
|
26971
26689
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
26972
|
-
/* @__PURE__ */
|
|
26973
|
-
|
|
26690
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
26691
|
+
/* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u2713" }),
|
|
26692
|
+
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
26693
|
+
" ",
|
|
26694
|
+
"Created glossary term: ",
|
|
26695
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, children: state.term })
|
|
26696
|
+
] })
|
|
26697
|
+
] }),
|
|
26698
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
26699
|
+
"ID: ",
|
|
26700
|
+
state.id
|
|
26701
|
+
] }) })
|
|
26974
26702
|
] });
|
|
26975
26703
|
}
|
|
26976
26704
|
async function runGlossaryCreate(opts) {
|
|
26977
|
-
|
|
26978
|
-
try {
|
|
26979
|
-
const translations = opts.translation ? parseTranslations(opts.translation) : void 0;
|
|
26980
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
26981
|
-
const { projectId, organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
26982
|
-
const glossaryId = await convexClient.mutation(
|
|
26983
|
-
api.glossaries.create,
|
|
26984
|
-
{
|
|
26985
|
-
organizationId: organizationId2,
|
|
26986
|
-
projectId,
|
|
26987
|
-
src: opts.term,
|
|
26988
|
-
srcLocale: opts.locale,
|
|
26989
|
-
aiContext: opts.context,
|
|
26990
|
-
doNotTranslate: opts.doNotTranslate,
|
|
26991
|
-
translations
|
|
26992
|
-
}
|
|
26993
|
-
);
|
|
26994
|
-
jsonSuccess({ id: glossaryId, term: opts.term });
|
|
26995
|
-
} catch (error62) {
|
|
26996
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
26997
|
-
}
|
|
26998
|
-
}
|
|
26999
|
-
let { unmount } = render4(/* @__PURE__ */ jsx4(CreateUI, { state: { type: "creating" } }));
|
|
27000
|
-
try {
|
|
26705
|
+
const create = async () => {
|
|
27001
26706
|
const translations = opts.translation ? parseTranslations(opts.translation) : void 0;
|
|
27002
26707
|
const convexClient = await getAuthenticatedConvexClient();
|
|
27003
26708
|
const { projectId, organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
27004
|
-
|
|
26709
|
+
return convexClient.mutation(api.glossaries.create, {
|
|
27005
26710
|
organizationId: organizationId2,
|
|
27006
26711
|
projectId,
|
|
27007
26712
|
src: opts.term,
|
|
@@ -27010,38 +26715,32 @@ async function runGlossaryCreate(opts) {
|
|
|
27010
26715
|
doNotTranslate: opts.doNotTranslate,
|
|
27011
26716
|
translations
|
|
27012
26717
|
});
|
|
26718
|
+
};
|
|
26719
|
+
if (opts.json) {
|
|
26720
|
+
jsonSuccess({ id: await create(), term: opts.term });
|
|
26721
|
+
}
|
|
26722
|
+
const { unmount } = renderTransient(
|
|
26723
|
+
/* @__PURE__ */ jsx4(CreateUI, { state: { type: "creating" } })
|
|
26724
|
+
);
|
|
26725
|
+
let glossaryId;
|
|
26726
|
+
try {
|
|
26727
|
+
glossaryId = await create();
|
|
26728
|
+
} finally {
|
|
27013
26729
|
unmount();
|
|
27014
|
-
const ui = render4(
|
|
27015
|
-
/* @__PURE__ */ jsx4(
|
|
27016
|
-
CreateUI,
|
|
27017
|
-
{
|
|
27018
|
-
state: { type: "success", id: glossaryId, term: opts.term }
|
|
27019
|
-
}
|
|
27020
|
-
)
|
|
27021
|
-
);
|
|
27022
|
-
unmount = ui.unmount;
|
|
27023
|
-
await ui.waitUntilExit();
|
|
27024
|
-
} catch (error62) {
|
|
27025
|
-
unmount();
|
|
27026
|
-
const ui = render4(
|
|
27027
|
-
/* @__PURE__ */ jsx4(
|
|
27028
|
-
CreateUI,
|
|
27029
|
-
{
|
|
27030
|
-
state: {
|
|
27031
|
-
type: "error",
|
|
27032
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
27033
|
-
}
|
|
27034
|
-
}
|
|
27035
|
-
)
|
|
27036
|
-
);
|
|
27037
|
-
await ui.waitUntilExit();
|
|
27038
|
-
return exitAfterFlush(1);
|
|
27039
26730
|
}
|
|
26731
|
+
render5(
|
|
26732
|
+
/* @__PURE__ */ jsx4(
|
|
26733
|
+
CreateUI,
|
|
26734
|
+
{
|
|
26735
|
+
state: { type: "success", id: glossaryId, term: opts.term }
|
|
26736
|
+
}
|
|
26737
|
+
)
|
|
26738
|
+
).unmount();
|
|
27040
26739
|
}
|
|
27041
26740
|
|
|
27042
26741
|
// src/commands/glossary/list.tsx
|
|
27043
26742
|
import { Spinner as Spinner4 } from "@inkjs/ui";
|
|
27044
|
-
import { Box as Box5, render as
|
|
26743
|
+
import { Box as Box5, render as render6, Text as Text5 } from "ink";
|
|
27045
26744
|
|
|
27046
26745
|
// ../shared/src/types/settings.ts
|
|
27047
26746
|
var SUPPORTED_LOCALES = [
|
|
@@ -27266,12 +26965,6 @@ function GlossaryListUI({ state }) {
|
|
|
27266
26965
|
/* @__PURE__ */ jsx5(Text5, { children: " Loading glossary terms\u2026" })
|
|
27267
26966
|
] });
|
|
27268
26967
|
}
|
|
27269
|
-
if (state.type === "error") {
|
|
27270
|
-
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
27271
|
-
/* @__PURE__ */ jsx5(Text5, { color: "red", children: "\u2717 Error" }),
|
|
27272
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: state.message })
|
|
27273
|
-
] });
|
|
27274
|
-
}
|
|
27275
26968
|
const { listing } = state;
|
|
27276
26969
|
const glossaries = listing.entries;
|
|
27277
26970
|
if (glossaries.length === 0) {
|
|
@@ -27372,43 +27065,23 @@ function jsonListing(listing) {
|
|
|
27372
27065
|
}
|
|
27373
27066
|
async function runGlossaryList(opts) {
|
|
27374
27067
|
if (opts.json) {
|
|
27375
|
-
|
|
27376
|
-
const listing = await loadListing(opts);
|
|
27377
|
-
jsonSuccess(jsonListing(listing));
|
|
27378
|
-
} catch (error62) {
|
|
27379
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
27380
|
-
}
|
|
27068
|
+
jsonSuccess(jsonListing(await loadListing(opts)));
|
|
27381
27069
|
}
|
|
27382
|
-
|
|
27070
|
+
const { unmount } = renderTransient(
|
|
27071
|
+
/* @__PURE__ */ jsx5(GlossaryListUI, { state: { type: "loading" } })
|
|
27072
|
+
);
|
|
27073
|
+
let listing;
|
|
27383
27074
|
try {
|
|
27384
|
-
|
|
27385
|
-
|
|
27386
|
-
const ui = render5(
|
|
27387
|
-
/* @__PURE__ */ jsx5(GlossaryListUI, { state: { type: "success", listing } })
|
|
27388
|
-
);
|
|
27389
|
-
unmount = ui.unmount;
|
|
27390
|
-
await ui.waitUntilExit();
|
|
27391
|
-
} catch (error62) {
|
|
27075
|
+
listing = await loadListing(opts);
|
|
27076
|
+
} finally {
|
|
27392
27077
|
unmount();
|
|
27393
|
-
const ui = render5(
|
|
27394
|
-
/* @__PURE__ */ jsx5(
|
|
27395
|
-
GlossaryListUI,
|
|
27396
|
-
{
|
|
27397
|
-
state: {
|
|
27398
|
-
type: "error",
|
|
27399
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
27400
|
-
}
|
|
27401
|
-
}
|
|
27402
|
-
)
|
|
27403
|
-
);
|
|
27404
|
-
await ui.waitUntilExit();
|
|
27405
|
-
return exitAfterFlush(1);
|
|
27406
27078
|
}
|
|
27079
|
+
render6(/* @__PURE__ */ jsx5(GlossaryListUI, { state: { type: "success", listing } })).unmount();
|
|
27407
27080
|
}
|
|
27408
27081
|
|
|
27409
27082
|
// src/commands/glossary/remove.tsx
|
|
27410
27083
|
import { Spinner as Spinner5 } from "@inkjs/ui";
|
|
27411
|
-
import { Box as Box6, render as
|
|
27084
|
+
import { Box as Box6, render as render7, Text as Text6 } from "ink";
|
|
27412
27085
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
27413
27086
|
function RemoveUI({ state }) {
|
|
27414
27087
|
if (state.type === "removing") {
|
|
@@ -27417,66 +27090,38 @@ function RemoveUI({ state }) {
|
|
|
27417
27090
|
/* @__PURE__ */ jsx6(Text6, { children: " Removing glossary term\u2026" })
|
|
27418
27091
|
] });
|
|
27419
27092
|
}
|
|
27420
|
-
|
|
27421
|
-
|
|
27422
|
-
|
|
27423
|
-
/* @__PURE__ */ jsx6(Text6, { children: " Glossary term removed" })
|
|
27424
|
-
] });
|
|
27425
|
-
}
|
|
27426
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
27427
|
-
/* @__PURE__ */ jsx6(Text6, { color: "red", children: "\u2717 Failed to remove glossary term" }),
|
|
27428
|
-
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: state.message })
|
|
27093
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
27094
|
+
/* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713" }),
|
|
27095
|
+
/* @__PURE__ */ jsx6(Text6, { children: " Glossary term removed" })
|
|
27429
27096
|
] });
|
|
27430
27097
|
}
|
|
27431
27098
|
async function runGlossaryRemove(opts) {
|
|
27432
|
-
|
|
27433
|
-
try {
|
|
27434
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
27435
|
-
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
27436
|
-
await convexClient.mutation(api.glossaries.remove, {
|
|
27437
|
-
organizationId: organizationId2,
|
|
27438
|
-
glossaryId: opts.id
|
|
27439
|
-
});
|
|
27440
|
-
jsonSuccess({ id: opts.id, ok: true });
|
|
27441
|
-
} catch (error62) {
|
|
27442
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
27443
|
-
}
|
|
27444
|
-
}
|
|
27445
|
-
let { unmount } = render6(/* @__PURE__ */ jsx6(RemoveUI, { state: { type: "removing" } }));
|
|
27446
|
-
try {
|
|
27099
|
+
const run = async () => {
|
|
27447
27100
|
const convexClient = await getAuthenticatedConvexClient();
|
|
27448
27101
|
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
27449
27102
|
await convexClient.mutation(api.glossaries.remove, {
|
|
27450
27103
|
organizationId: organizationId2,
|
|
27451
27104
|
glossaryId: opts.id
|
|
27452
27105
|
});
|
|
27106
|
+
};
|
|
27107
|
+
if (opts.json) {
|
|
27108
|
+
await run();
|
|
27109
|
+
jsonSuccess({ id: opts.id, ok: true });
|
|
27110
|
+
}
|
|
27111
|
+
const { unmount } = renderTransient(
|
|
27112
|
+
/* @__PURE__ */ jsx6(RemoveUI, { state: { type: "removing" } })
|
|
27113
|
+
);
|
|
27114
|
+
try {
|
|
27115
|
+
await run();
|
|
27116
|
+
} finally {
|
|
27453
27117
|
unmount();
|
|
27454
|
-
const ui = render6(
|
|
27455
|
-
/* @__PURE__ */ jsx6(RemoveUI, { state: { type: "success", id: opts.id } })
|
|
27456
|
-
);
|
|
27457
|
-
unmount = ui.unmount;
|
|
27458
|
-
await ui.waitUntilExit();
|
|
27459
|
-
} catch (error62) {
|
|
27460
|
-
unmount();
|
|
27461
|
-
const ui = render6(
|
|
27462
|
-
/* @__PURE__ */ jsx6(
|
|
27463
|
-
RemoveUI,
|
|
27464
|
-
{
|
|
27465
|
-
state: {
|
|
27466
|
-
type: "error",
|
|
27467
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
27468
|
-
}
|
|
27469
|
-
}
|
|
27470
|
-
)
|
|
27471
|
-
);
|
|
27472
|
-
await ui.waitUntilExit();
|
|
27473
|
-
return exitAfterFlush(1);
|
|
27474
27118
|
}
|
|
27119
|
+
render7(/* @__PURE__ */ jsx6(RemoveUI, { state: { type: "success", id: opts.id } })).unmount();
|
|
27475
27120
|
}
|
|
27476
27121
|
|
|
27477
27122
|
// src/commands/glossary/show.tsx
|
|
27478
27123
|
import { Spinner as Spinner6 } from "@inkjs/ui";
|
|
27479
|
-
import { Box as Box7, render as
|
|
27124
|
+
import { Box as Box7, render as render8, Text as Text7 } from "ink";
|
|
27480
27125
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
27481
27126
|
function GlossaryShowUI({ state }) {
|
|
27482
27127
|
if (state.type === "loading") {
|
|
@@ -27485,15 +27130,6 @@ function GlossaryShowUI({ state }) {
|
|
|
27485
27130
|
/* @__PURE__ */ jsx7(Text7, { children: " Loading glossary term\u2026" })
|
|
27486
27131
|
] });
|
|
27487
27132
|
}
|
|
27488
|
-
if (state.type === "not-found") {
|
|
27489
|
-
return /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text7, { color: "red", children: "\u2717 Glossary term not found" }) });
|
|
27490
|
-
}
|
|
27491
|
-
if (state.type === "error") {
|
|
27492
|
-
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
27493
|
-
/* @__PURE__ */ jsx7(Text7, { color: "red", children: "\u2717 Error" }),
|
|
27494
|
-
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: state.message })
|
|
27495
|
-
] });
|
|
27496
|
-
}
|
|
27497
27133
|
const { glossary } = state;
|
|
27498
27134
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
27499
27135
|
/* @__PURE__ */ jsxs7(Box7, { gap: 1, children: [
|
|
@@ -27528,73 +27164,49 @@ function GlossaryShowUI({ state }) {
|
|
|
27528
27164
|
] }) })
|
|
27529
27165
|
] });
|
|
27530
27166
|
}
|
|
27167
|
+
async function loadGlossary(id) {
|
|
27168
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
27169
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
27170
|
+
const glossary = await convexClient.query(api.glossaries.getById, {
|
|
27171
|
+
organizationId: organizationId2,
|
|
27172
|
+
glossaryId: id
|
|
27173
|
+
});
|
|
27174
|
+
if (!glossary) {
|
|
27175
|
+
throw new CliError(
|
|
27176
|
+
`Glossary term ${id} was not found.`,
|
|
27177
|
+
"List the terms with 'tolkbot glossary list'.",
|
|
27178
|
+
"not_found"
|
|
27179
|
+
);
|
|
27180
|
+
}
|
|
27181
|
+
return glossary;
|
|
27182
|
+
}
|
|
27531
27183
|
async function runGlossaryShow(opts) {
|
|
27532
27184
|
if (opts.json) {
|
|
27533
|
-
|
|
27534
|
-
|
|
27535
|
-
|
|
27536
|
-
|
|
27537
|
-
|
|
27538
|
-
|
|
27539
|
-
|
|
27540
|
-
|
|
27541
|
-
|
|
27542
|
-
}
|
|
27543
|
-
jsonSuccess({
|
|
27544
|
-
id: glossary._id,
|
|
27545
|
-
term: glossary.src,
|
|
27546
|
-
locale: glossary.srcLocale,
|
|
27547
|
-
doNotTranslate: glossary.doNotTranslate ?? false,
|
|
27548
|
-
translations: glossary.translations ?? [],
|
|
27549
|
-
context: glossary.aiContext ?? null
|
|
27550
|
-
});
|
|
27551
|
-
} catch (error62) {
|
|
27552
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
27553
|
-
}
|
|
27185
|
+
const glossary2 = await loadGlossary(opts.id);
|
|
27186
|
+
jsonSuccess({
|
|
27187
|
+
id: glossary2._id,
|
|
27188
|
+
term: glossary2.src,
|
|
27189
|
+
locale: glossary2.srcLocale,
|
|
27190
|
+
doNotTranslate: glossary2.doNotTranslate ?? false,
|
|
27191
|
+
translations: glossary2.translations ?? [],
|
|
27192
|
+
context: glossary2.aiContext ?? null
|
|
27193
|
+
});
|
|
27554
27194
|
}
|
|
27555
|
-
|
|
27195
|
+
const { unmount } = renderTransient(
|
|
27196
|
+
/* @__PURE__ */ jsx7(GlossaryShowUI, { state: { type: "loading" } })
|
|
27197
|
+
);
|
|
27198
|
+
let glossary;
|
|
27556
27199
|
try {
|
|
27557
|
-
|
|
27558
|
-
|
|
27559
|
-
const glossary = await convexClient.query(api.glossaries.getById, {
|
|
27560
|
-
organizationId: organizationId2,
|
|
27561
|
-
glossaryId: opts.id
|
|
27562
|
-
});
|
|
27563
|
-
if (!glossary) {
|
|
27564
|
-
unmount();
|
|
27565
|
-
const ui2 = render7(/* @__PURE__ */ jsx7(GlossaryShowUI, { state: { type: "not-found" } }));
|
|
27566
|
-
unmount = ui2.unmount;
|
|
27567
|
-
await ui2.waitUntilExit();
|
|
27568
|
-
return exitAfterFlush(1);
|
|
27569
|
-
return;
|
|
27570
|
-
}
|
|
27571
|
-
unmount();
|
|
27572
|
-
const ui = render7(
|
|
27573
|
-
/* @__PURE__ */ jsx7(GlossaryShowUI, { state: { type: "success", glossary } })
|
|
27574
|
-
);
|
|
27575
|
-
unmount = ui.unmount;
|
|
27576
|
-
await ui.waitUntilExit();
|
|
27577
|
-
} catch (error62) {
|
|
27200
|
+
glossary = await loadGlossary(opts.id);
|
|
27201
|
+
} finally {
|
|
27578
27202
|
unmount();
|
|
27579
|
-
const ui = render7(
|
|
27580
|
-
/* @__PURE__ */ jsx7(
|
|
27581
|
-
GlossaryShowUI,
|
|
27582
|
-
{
|
|
27583
|
-
state: {
|
|
27584
|
-
type: "error",
|
|
27585
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
27586
|
-
}
|
|
27587
|
-
}
|
|
27588
|
-
)
|
|
27589
|
-
);
|
|
27590
|
-
await ui.waitUntilExit();
|
|
27591
|
-
return exitAfterFlush(1);
|
|
27592
27203
|
}
|
|
27204
|
+
render8(/* @__PURE__ */ jsx7(GlossaryShowUI, { state: { type: "success", glossary } })).unmount();
|
|
27593
27205
|
}
|
|
27594
27206
|
|
|
27595
27207
|
// src/commands/glossary/update.tsx
|
|
27596
27208
|
import { Spinner as Spinner7 } from "@inkjs/ui";
|
|
27597
|
-
import { Box as Box8, render as
|
|
27209
|
+
import { Box as Box8, render as render9, Text as Text8 } from "ink";
|
|
27598
27210
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
27599
27211
|
function UpdateUI({ state }) {
|
|
27600
27212
|
if (state.type === "updating") {
|
|
@@ -27603,21 +27215,15 @@ function UpdateUI({ state }) {
|
|
|
27603
27215
|
/* @__PURE__ */ jsx8(Text8, { children: " Updating glossary term\u2026" })
|
|
27604
27216
|
] });
|
|
27605
27217
|
}
|
|
27606
|
-
if (state.type === "success") {
|
|
27607
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
27608
|
-
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
27609
|
-
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2713" }),
|
|
27610
|
-
/* @__PURE__ */ jsx8(Text8, { children: " Glossary term updated" })
|
|
27611
|
-
] }),
|
|
27612
|
-
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
27613
|
-
"ID: ",
|
|
27614
|
-
state.id
|
|
27615
|
-
] }) })
|
|
27616
|
-
] });
|
|
27617
|
-
}
|
|
27618
27218
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
27619
|
-
/* @__PURE__ */
|
|
27620
|
-
|
|
27219
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
27220
|
+
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2713" }),
|
|
27221
|
+
/* @__PURE__ */ jsx8(Text8, { children: " Glossary term updated" })
|
|
27222
|
+
] }),
|
|
27223
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
27224
|
+
"ID: ",
|
|
27225
|
+
state.id
|
|
27226
|
+
] }) })
|
|
27621
27227
|
] });
|
|
27622
27228
|
}
|
|
27623
27229
|
async function runGlossaryUpdate(opts) {
|
|
@@ -27631,124 +27237,333 @@ async function runGlossaryUpdate(opts) {
|
|
|
27631
27237
|
patch.doNotTranslate = opts.doNotTranslate;
|
|
27632
27238
|
if (translations !== void 0) patch.translations = translations;
|
|
27633
27239
|
if (Object.keys(patch).length === 0) {
|
|
27634
|
-
throw new
|
|
27635
|
-
"No fields to update.
|
|
27240
|
+
throw new UsageError(
|
|
27241
|
+
"No fields to update.",
|
|
27242
|
+
"Pass at least one of --term, --locale, --context, --do-not-translate, --translation."
|
|
27636
27243
|
);
|
|
27637
27244
|
}
|
|
27638
27245
|
return patch;
|
|
27639
27246
|
};
|
|
27247
|
+
const update = async () => {
|
|
27248
|
+
const patch = buildPatch();
|
|
27249
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
27250
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
27251
|
+
await convexClient.mutation(api.glossaries.update, {
|
|
27252
|
+
organizationId: organizationId2,
|
|
27253
|
+
glossaryId: opts.id,
|
|
27254
|
+
patch
|
|
27255
|
+
});
|
|
27256
|
+
};
|
|
27640
27257
|
if (opts.json) {
|
|
27641
|
-
|
|
27642
|
-
|
|
27643
|
-
|
|
27644
|
-
|
|
27645
|
-
|
|
27646
|
-
|
|
27647
|
-
|
|
27648
|
-
|
|
27649
|
-
|
|
27650
|
-
|
|
27651
|
-
|
|
27652
|
-
|
|
27258
|
+
await update();
|
|
27259
|
+
jsonSuccess({ id: opts.id, ok: true });
|
|
27260
|
+
}
|
|
27261
|
+
const { unmount } = renderTransient(
|
|
27262
|
+
/* @__PURE__ */ jsx8(UpdateUI, { state: { type: "updating" } })
|
|
27263
|
+
);
|
|
27264
|
+
try {
|
|
27265
|
+
await update();
|
|
27266
|
+
} finally {
|
|
27267
|
+
unmount();
|
|
27268
|
+
}
|
|
27269
|
+
render9(/* @__PURE__ */ jsx8(UpdateUI, { state: { type: "success", id: opts.id } })).unmount();
|
|
27270
|
+
}
|
|
27271
|
+
|
|
27272
|
+
// src/commands/glossary/index.ts
|
|
27273
|
+
var glossaryCommand = new Command2("glossary").description(
|
|
27274
|
+
"Manage glossary terms"
|
|
27275
|
+
);
|
|
27276
|
+
glossaryCommand.command("list").description("List glossary terms for the current project").option("--locale <locale>", "Filter by source locale").option("--limit <n>", "Max number of results", (v) => Number(v), 100).option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
27277
|
+
const schema = z4.object({
|
|
27278
|
+
locale: z4.string().optional(),
|
|
27279
|
+
limit: z4.coerce.number().int().positive().default(100),
|
|
27280
|
+
json: z4.boolean().optional()
|
|
27281
|
+
});
|
|
27282
|
+
const parsed = validateOrExit(schema, opts);
|
|
27283
|
+
await runGlossaryList(parsed);
|
|
27284
|
+
});
|
|
27285
|
+
glossaryCommand.command("show").description("Show details of a glossary term").requiredOption("--id <glossaryId>", "Glossary term ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
27286
|
+
const schema = z4.object({
|
|
27287
|
+
id: z4.string().min(1),
|
|
27288
|
+
json: z4.boolean().optional()
|
|
27289
|
+
});
|
|
27290
|
+
const parsed = validateOrExit(schema, opts);
|
|
27291
|
+
await runGlossaryShow(parsed);
|
|
27292
|
+
});
|
|
27293
|
+
glossaryCommand.command("create").description("Create a new glossary term").requiredOption("--term <text>", "Source term").requiredOption("--locale <locale>", "Source locale (e.g. en)").option("--context <text>", "AI context for translation guidance").option("--do-not-translate", "Mark term as do-not-translate").option(
|
|
27294
|
+
"--translation <locale:text...>",
|
|
27295
|
+
"Translations in locale:text format (e.g. --translation no:Hei de:Hallo)"
|
|
27296
|
+
).option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
27297
|
+
const schema = z4.object({
|
|
27298
|
+
term: z4.string().min(1),
|
|
27299
|
+
locale: z4.string().min(1),
|
|
27300
|
+
context: z4.string().optional(),
|
|
27301
|
+
doNotTranslate: z4.boolean().optional(),
|
|
27302
|
+
translation: z4.array(z4.string()).optional(),
|
|
27303
|
+
json: z4.boolean().optional()
|
|
27304
|
+
});
|
|
27305
|
+
const parsed = validateOrExit(schema, opts);
|
|
27306
|
+
await runGlossaryCreate(parsed);
|
|
27307
|
+
});
|
|
27308
|
+
glossaryCommand.command("update").description("Update an existing glossary term").requiredOption("--id <glossaryId>", "Glossary term ID").option("--term <text>", "New source term").option("--locale <locale>", "New source locale").option("--context <text>", "AI context for translation guidance").option("--do-not-translate", "Mark term as do-not-translate").option("--no-do-not-translate", "Unmark do-not-translate").option(
|
|
27309
|
+
"--translation <locale:text...>",
|
|
27310
|
+
"Translations in locale:text format (replaces all translations)"
|
|
27311
|
+
).option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
27312
|
+
const schema = z4.object({
|
|
27313
|
+
id: z4.string().min(1),
|
|
27314
|
+
term: z4.string().optional(),
|
|
27315
|
+
locale: z4.string().optional(),
|
|
27316
|
+
context: z4.string().optional(),
|
|
27317
|
+
doNotTranslate: z4.boolean().optional(),
|
|
27318
|
+
translation: z4.array(z4.string()).optional(),
|
|
27319
|
+
json: z4.boolean().optional()
|
|
27320
|
+
});
|
|
27321
|
+
const parsed = validateOrExit(schema, opts);
|
|
27322
|
+
await runGlossaryUpdate(parsed);
|
|
27323
|
+
});
|
|
27324
|
+
glossaryCommand.command("remove").description("Remove a glossary term").requiredOption("--id <glossaryId>", "Glossary term ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
27325
|
+
const schema = z4.object({
|
|
27326
|
+
id: z4.string().min(1),
|
|
27327
|
+
json: z4.boolean().optional()
|
|
27328
|
+
});
|
|
27329
|
+
const parsed = validateOrExit(schema, opts);
|
|
27330
|
+
await runGlossaryRemove(parsed);
|
|
27331
|
+
});
|
|
27332
|
+
|
|
27333
|
+
// src/commands/init/index.tsx
|
|
27334
|
+
import { Box as Box10, render as render10, Text as Text10 } from "ink";
|
|
27335
|
+
|
|
27336
|
+
// src/lib/telemetry.ts
|
|
27337
|
+
import { randomUUID } from "crypto";
|
|
27338
|
+
import fs4 from "fs/promises";
|
|
27339
|
+
import os2 from "os";
|
|
27340
|
+
import path4 from "path";
|
|
27341
|
+
|
|
27342
|
+
// ../shared/src/analytics/index.ts
|
|
27343
|
+
var ORGANIZATION_GROUP = "organization";
|
|
27344
|
+
var analyticsEventNames = [
|
|
27345
|
+
"trial_started",
|
|
27346
|
+
"project_created",
|
|
27347
|
+
"project_page_viewed",
|
|
27348
|
+
"github_connect_started",
|
|
27349
|
+
"github_install_initiated",
|
|
27350
|
+
"plan_change_initiated",
|
|
27351
|
+
"credits_purchase_initiated",
|
|
27352
|
+
"glossary_created",
|
|
27353
|
+
"glossary_deleted",
|
|
27354
|
+
"glossary_imported",
|
|
27355
|
+
"translation_job_viewed",
|
|
27356
|
+
"translation_logs_viewed",
|
|
27357
|
+
"cli_login_completed",
|
|
27358
|
+
"cli_command_completed",
|
|
27359
|
+
"cli_init_completed",
|
|
27360
|
+
"cli_translate_completed",
|
|
27361
|
+
"cli_validate_completed",
|
|
27362
|
+
"translation_job_started",
|
|
27363
|
+
"translation_job_completed",
|
|
27364
|
+
"translation_job_failed",
|
|
27365
|
+
"pull_request_opened",
|
|
27366
|
+
"pull_request_merged",
|
|
27367
|
+
"pull_request_closed_unmerged",
|
|
27368
|
+
"github_app_installed",
|
|
27369
|
+
"github_app_uninstalled",
|
|
27370
|
+
"repository_connected",
|
|
27371
|
+
"subscription_started",
|
|
27372
|
+
"subscription_plan_changed",
|
|
27373
|
+
"subscription_canceled",
|
|
27374
|
+
"trial_ended",
|
|
27375
|
+
"credits_purchased",
|
|
27376
|
+
"credits_exhausted",
|
|
27377
|
+
"usage_limit_reached",
|
|
27378
|
+
"cta_clicked",
|
|
27379
|
+
"waitlist_joined",
|
|
27380
|
+
"signed_up",
|
|
27381
|
+
"signed_in",
|
|
27382
|
+
"onboarding_step_completed",
|
|
27383
|
+
"onboarding_completed",
|
|
27384
|
+
"language_added",
|
|
27385
|
+
"translation_edited",
|
|
27386
|
+
"translation_bulk_retranslated",
|
|
27387
|
+
"dashboard_translation_started",
|
|
27388
|
+
"project_settings_changed",
|
|
27389
|
+
"member_invited",
|
|
27390
|
+
"feedback_submitted",
|
|
27391
|
+
"analytics_smoke_tested"
|
|
27392
|
+
];
|
|
27393
|
+
var registeredNames = new Set(analyticsEventNames);
|
|
27394
|
+
|
|
27395
|
+
// src/lib/telemetry.ts
|
|
27396
|
+
import { PostHog } from "posthog-node";
|
|
27397
|
+
var TELEMETRY_ENV_VAR = "TOLKBOT_TELEMETRY";
|
|
27398
|
+
var DO_NOT_TRACK_ENV_VAR = "DO_NOT_TRACK";
|
|
27399
|
+
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
27400
|
+
var FLUSH_TIMEOUT_MS = 1e3;
|
|
27401
|
+
var OFF_VALUES = /* @__PURE__ */ new Set(["0", "false", "off", "no"]);
|
|
27402
|
+
var ON_VALUES = /* @__PURE__ */ new Set(["1", "true", "on", "yes"]);
|
|
27403
|
+
function readEnv(name) {
|
|
27404
|
+
return process.env[name]?.trim();
|
|
27405
|
+
}
|
|
27406
|
+
function projectKey() {
|
|
27407
|
+
const key = "phc_uSMLUC9D7bHeaHFAosnoDzBj6DY5uzLcCQkXZxzj0mQ"?.trim();
|
|
27408
|
+
if (!key) return void 0;
|
|
27409
|
+
return key;
|
|
27410
|
+
}
|
|
27411
|
+
function environment() {
|
|
27412
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
27413
|
+
return nodeEnv === "development" || nodeEnv === "test" ? nodeEnv : "production";
|
|
27414
|
+
}
|
|
27415
|
+
function inCi() {
|
|
27416
|
+
return Boolean(process.env.CI);
|
|
27417
|
+
}
|
|
27418
|
+
function getTelemetryDir() {
|
|
27419
|
+
return path4.join(os2.homedir(), ".tolkbot");
|
|
27420
|
+
}
|
|
27421
|
+
function getPreferencePath() {
|
|
27422
|
+
return path4.join(getTelemetryDir(), "telemetry");
|
|
27423
|
+
}
|
|
27424
|
+
function getAnonymousIdPath() {
|
|
27425
|
+
return path4.join(getTelemetryDir(), "anonymous-id");
|
|
27426
|
+
}
|
|
27427
|
+
async function readFileOrNull(file2) {
|
|
27428
|
+
try {
|
|
27429
|
+
return (await fs4.readFile(file2, "utf-8")).trim() || null;
|
|
27430
|
+
} catch {
|
|
27431
|
+
return null;
|
|
27432
|
+
}
|
|
27433
|
+
}
|
|
27434
|
+
async function writePrivateFile(file2, content) {
|
|
27435
|
+
await fs4.mkdir(path4.dirname(file2), { recursive: true, mode: 448 });
|
|
27436
|
+
await fs4.writeFile(file2, content, { mode: 384 });
|
|
27437
|
+
}
|
|
27438
|
+
async function telemetryState() {
|
|
27439
|
+
const envValue = readEnv(TELEMETRY_ENV_VAR)?.toLowerCase();
|
|
27440
|
+
if (envValue !== void 0 && OFF_VALUES.has(envValue)) {
|
|
27441
|
+
return { enabled: false, reason: "env" };
|
|
27442
|
+
}
|
|
27443
|
+
const doNotTrack = readEnv(DO_NOT_TRACK_ENV_VAR)?.toLowerCase();
|
|
27444
|
+
if (doNotTrack !== void 0 && ON_VALUES.has(doNotTrack)) {
|
|
27445
|
+
return { enabled: false, reason: "do-not-track" };
|
|
27446
|
+
}
|
|
27447
|
+
const preference = (await readFileOrNull(getPreferencePath()))?.toLowerCase();
|
|
27448
|
+
if (preference !== void 0 && OFF_VALUES.has(preference)) {
|
|
27449
|
+
return { enabled: false, reason: "preference" };
|
|
27450
|
+
}
|
|
27451
|
+
if (!projectKey()) {
|
|
27452
|
+
return { enabled: false, reason: "no-key" };
|
|
27453
|
+
}
|
|
27454
|
+
return { enabled: true, reason: "default" };
|
|
27455
|
+
}
|
|
27456
|
+
async function setTelemetryPreference(enabled) {
|
|
27457
|
+
await writePrivateFile(getPreferencePath(), enabled ? "on\n" : "off\n");
|
|
27458
|
+
}
|
|
27459
|
+
function decodeTokenSubject(token) {
|
|
27460
|
+
const payload = token.split(".")[1];
|
|
27461
|
+
if (!payload) return null;
|
|
27462
|
+
try {
|
|
27463
|
+
const json2 = Buffer.from(payload, "base64url").toString("utf-8");
|
|
27464
|
+
const claims = JSON.parse(json2);
|
|
27465
|
+
if (typeof claims === "object" && claims !== null && "sub" in claims && typeof claims.sub === "string") {
|
|
27466
|
+
return claims.sub;
|
|
27653
27467
|
}
|
|
27468
|
+
} catch {
|
|
27654
27469
|
}
|
|
27655
|
-
|
|
27470
|
+
return null;
|
|
27471
|
+
}
|
|
27472
|
+
async function resolveDistinctId() {
|
|
27473
|
+
const resolved = await resolveToken();
|
|
27474
|
+
const subject = resolved ? decodeTokenSubject(resolved.token) : null;
|
|
27475
|
+
if (subject) return subject;
|
|
27476
|
+
const file2 = getAnonymousIdPath();
|
|
27477
|
+
const existing = await readFileOrNull(file2);
|
|
27478
|
+
if (existing) return existing;
|
|
27479
|
+
const generated = randomUUID();
|
|
27656
27480
|
try {
|
|
27657
|
-
|
|
27658
|
-
|
|
27659
|
-
|
|
27660
|
-
await convexClient.mutation(api.glossaries.update, {
|
|
27661
|
-
organizationId: organizationId2,
|
|
27662
|
-
glossaryId: opts.id,
|
|
27663
|
-
patch
|
|
27664
|
-
});
|
|
27665
|
-
unmount();
|
|
27666
|
-
const ui = render8(
|
|
27667
|
-
/* @__PURE__ */ jsx8(UpdateUI, { state: { type: "success", id: opts.id } })
|
|
27668
|
-
);
|
|
27669
|
-
unmount = ui.unmount;
|
|
27670
|
-
await ui.waitUntilExit();
|
|
27671
|
-
} catch (error62) {
|
|
27672
|
-
unmount();
|
|
27673
|
-
const ui = render8(
|
|
27674
|
-
/* @__PURE__ */ jsx8(
|
|
27675
|
-
UpdateUI,
|
|
27676
|
-
{
|
|
27677
|
-
state: {
|
|
27678
|
-
type: "error",
|
|
27679
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
27680
|
-
}
|
|
27681
|
-
}
|
|
27682
|
-
)
|
|
27683
|
-
);
|
|
27684
|
-
await ui.waitUntilExit();
|
|
27685
|
-
return exitAfterFlush(1);
|
|
27481
|
+
await writePrivateFile(file2, `${generated}
|
|
27482
|
+
`);
|
|
27483
|
+
} catch {
|
|
27686
27484
|
}
|
|
27485
|
+
return generated;
|
|
27687
27486
|
}
|
|
27688
|
-
|
|
27689
|
-
|
|
27690
|
-
|
|
27691
|
-
|
|
27692
|
-
|
|
27693
|
-
|
|
27694
|
-
|
|
27695
|
-
|
|
27696
|
-
|
|
27697
|
-
|
|
27698
|
-
|
|
27699
|
-
|
|
27700
|
-
|
|
27701
|
-
|
|
27702
|
-
|
|
27703
|
-
|
|
27704
|
-
id: z4.string().min(1),
|
|
27705
|
-
json: z4.boolean().optional()
|
|
27487
|
+
var organizationId = null;
|
|
27488
|
+
function setTelemetryOrganization(id) {
|
|
27489
|
+
organizationId = id;
|
|
27490
|
+
}
|
|
27491
|
+
var client2 = null;
|
|
27492
|
+
function getClient(key) {
|
|
27493
|
+
client2 ??= new PostHog(key, {
|
|
27494
|
+
host: POSTHOG_HOST,
|
|
27495
|
+
// A CLI process is short-lived: send each event as it happens
|
|
27496
|
+
// rather than batching for a flush that may never come.
|
|
27497
|
+
flushAt: 1,
|
|
27498
|
+
flushInterval: 0,
|
|
27499
|
+
requestTimeout: FLUSH_TIMEOUT_MS,
|
|
27500
|
+
// No IP-derived location on CLI events. The privacy policy lists
|
|
27501
|
+
// exactly what the CLI reports, and location is not on it.
|
|
27502
|
+
disableGeoip: true
|
|
27706
27503
|
});
|
|
27707
|
-
|
|
27708
|
-
|
|
27709
|
-
|
|
27710
|
-
|
|
27711
|
-
|
|
27712
|
-
|
|
27713
|
-
|
|
27714
|
-
|
|
27715
|
-
|
|
27716
|
-
|
|
27717
|
-
|
|
27718
|
-
|
|
27719
|
-
|
|
27720
|
-
|
|
27504
|
+
return client2;
|
|
27505
|
+
}
|
|
27506
|
+
var inFlight = /* @__PURE__ */ new Set();
|
|
27507
|
+
async function send(event, properties) {
|
|
27508
|
+
const state = await telemetryState();
|
|
27509
|
+
if (!state.enabled) return;
|
|
27510
|
+
const key = projectKey();
|
|
27511
|
+
if (!key) return;
|
|
27512
|
+
const distinctId = await resolveDistinctId();
|
|
27513
|
+
await getClient(key).captureImmediate({
|
|
27514
|
+
distinctId,
|
|
27515
|
+
event,
|
|
27516
|
+
properties: { ...properties, environment: environment() },
|
|
27517
|
+
groups: organizationId ? { [ORGANIZATION_GROUP]: organizationId } : void 0
|
|
27721
27518
|
});
|
|
27722
|
-
|
|
27723
|
-
|
|
27724
|
-
|
|
27725
|
-
|
|
27726
|
-
|
|
27727
|
-
|
|
27728
|
-
|
|
27729
|
-
|
|
27730
|
-
|
|
27731
|
-
|
|
27732
|
-
|
|
27733
|
-
context: z4.string().optional(),
|
|
27734
|
-
doNotTranslate: z4.boolean().optional(),
|
|
27735
|
-
translation: z4.array(z4.string()).optional(),
|
|
27736
|
-
json: z4.boolean().optional()
|
|
27519
|
+
}
|
|
27520
|
+
function track(event, properties) {
|
|
27521
|
+
const attempt = send(event, properties).catch(() => void 0);
|
|
27522
|
+
inFlight.add(attempt);
|
|
27523
|
+
void attempt.finally(() => inFlight.delete(attempt));
|
|
27524
|
+
}
|
|
27525
|
+
async function flushTelemetry(timeoutMs = FLUSH_TIMEOUT_MS) {
|
|
27526
|
+
if (inFlight.size === 0 && !client2) return;
|
|
27527
|
+
const deadline = new Promise((resolve) => {
|
|
27528
|
+
const timer = setTimeout(resolve, timeoutMs);
|
|
27529
|
+
timer.unref();
|
|
27737
27530
|
});
|
|
27738
|
-
|
|
27739
|
-
|
|
27740
|
-
|
|
27741
|
-
|
|
27742
|
-
|
|
27743
|
-
|
|
27744
|
-
|
|
27531
|
+
await Promise.race([Promise.allSettled([...inFlight]), deadline]);
|
|
27532
|
+
if (client2) {
|
|
27533
|
+
const closing = client2.shutdown(timeoutMs).catch(() => void 0);
|
|
27534
|
+
client2 = null;
|
|
27535
|
+
await Promise.race([closing, deadline]);
|
|
27536
|
+
}
|
|
27537
|
+
}
|
|
27538
|
+
function runtimeProperties(cliVersion) {
|
|
27539
|
+
return {
|
|
27540
|
+
cli_version: cliVersion,
|
|
27541
|
+
node_version: process.version,
|
|
27542
|
+
platform: process.platform,
|
|
27543
|
+
ci: inCi()
|
|
27544
|
+
};
|
|
27545
|
+
}
|
|
27546
|
+
var activeCommand = null;
|
|
27547
|
+
function beginCommand(path12, cliVersion) {
|
|
27548
|
+
activeCommand = {
|
|
27549
|
+
path: path12,
|
|
27550
|
+
startedAt: Date.now(),
|
|
27551
|
+
cliVersion,
|
|
27552
|
+
reported: false
|
|
27553
|
+
};
|
|
27554
|
+
}
|
|
27555
|
+
function completeCommand(exitCode) {
|
|
27556
|
+
if (!activeCommand || activeCommand.reported) return;
|
|
27557
|
+
activeCommand.reported = true;
|
|
27558
|
+
track("cli_command_completed", {
|
|
27559
|
+
command: activeCommand.path,
|
|
27560
|
+
success: exitCode === 0,
|
|
27561
|
+
exit_code: exitCode,
|
|
27562
|
+
duration_ms: Date.now() - activeCommand.startedAt,
|
|
27563
|
+
json: wantsJson(),
|
|
27564
|
+
...runtimeProperties(activeCommand.cliVersion)
|
|
27745
27565
|
});
|
|
27746
|
-
|
|
27747
|
-
await runGlossaryRemove(parsed);
|
|
27748
|
-
});
|
|
27749
|
-
|
|
27750
|
-
// src/commands/init/index.tsx
|
|
27751
|
-
import { Box as Box10, render as render9, Text as Text10 } from "ink";
|
|
27566
|
+
}
|
|
27752
27567
|
|
|
27753
27568
|
// src/commands/init/core.ts
|
|
27754
27569
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -27805,7 +27620,8 @@ function assertSupportedLocales(locales, label) {
|
|
|
27805
27620
|
if (unsupported.length > 0) {
|
|
27806
27621
|
throw new CliError(
|
|
27807
27622
|
`Unsupported ${label}: ${unsupported.join(", ")}.`,
|
|
27808
|
-
`TolkBot supports ${SUPPORTED.size} locales. Run 'tolkbot init' without --yes to pick from the list
|
|
27623
|
+
`TolkBot supports ${SUPPORTED.size} locales. Run 'tolkbot init' without --yes in a terminal to pick from the list.`,
|
|
27624
|
+
"usage"
|
|
27809
27625
|
);
|
|
27810
27626
|
}
|
|
27811
27627
|
return normalized.filter((l) => isSupportedLocale(l));
|
|
@@ -27831,7 +27647,8 @@ async function linkRepository(cwd, plan) {
|
|
|
27831
27647
|
if (!repository) {
|
|
27832
27648
|
throw new CliError(
|
|
27833
27649
|
`Repository ${owner}/${name} is not connected to TolkBot.`,
|
|
27834
|
-
"Install the GitHub App at https://github.com/apps/tolkbot, grant it access to this repository, then run 'tolkbot init' again."
|
|
27650
|
+
"Install the GitHub App at https://github.com/apps/tolkbot, grant it access to this repository, then run 'tolkbot init' again.",
|
|
27651
|
+
"repository_not_connected"
|
|
27835
27652
|
);
|
|
27836
27653
|
}
|
|
27837
27654
|
const existingProjects = await convexClient.query(
|
|
@@ -27927,7 +27744,8 @@ function requireSourcePattern(project, repository) {
|
|
|
27927
27744
|
const reason = project.initializationStatus === "failed" && project.initializationError ? ` Detection failed: ${project.initializationError}` : "";
|
|
27928
27745
|
throw new CliError(
|
|
27929
27746
|
`TolkBot found no translation files in ${repository}.${reason}`,
|
|
27930
|
-
`Set the file pattern in the project's settings at ${patternSettingsUrl(project.slug)}, then run 'tolkbot init' again
|
|
27747
|
+
`Set the file pattern in the project's settings at ${patternSettingsUrl(project.slug)}, then run 'tolkbot init' again.`,
|
|
27748
|
+
"no_translation_files"
|
|
27931
27749
|
);
|
|
27932
27750
|
}
|
|
27933
27751
|
|
|
@@ -28311,7 +28129,8 @@ function resolvePlan(cwd, opts) {
|
|
|
28311
28129
|
if (!FRAMEWORKS.includes(opts.framework)) {
|
|
28312
28130
|
throw new CliError(
|
|
28313
28131
|
`Unknown framework "${opts.framework}".`,
|
|
28314
|
-
`Expected one of: ${FRAMEWORKS.join(", ")}
|
|
28132
|
+
`Expected one of: ${FRAMEWORKS.join(", ")}.`,
|
|
28133
|
+
"usage"
|
|
28315
28134
|
);
|
|
28316
28135
|
}
|
|
28317
28136
|
framework = opts.framework;
|
|
@@ -28421,37 +28240,36 @@ async function runHeadless(cwd, opts) {
|
|
|
28421
28240
|
);
|
|
28422
28241
|
return;
|
|
28423
28242
|
}
|
|
28424
|
-
|
|
28243
|
+
render10(/* @__PURE__ */ jsx10(SummaryUI, { plan, result })).unmount();
|
|
28425
28244
|
}
|
|
28426
|
-
function
|
|
28427
|
-
|
|
28245
|
+
async function requireLogin() {
|
|
28246
|
+
const resolved = await resolveToken();
|
|
28247
|
+
if (!resolved) {
|
|
28248
|
+
throw new AuthError(
|
|
28249
|
+
"tolkbot init needs a TolkBot account, and this machine is not logged in.",
|
|
28250
|
+
`Run 'tolkbot auth login', which opens tolkbot.ai in a browser to sign in with GitHub, or set $${TOKEN_ENV_VAR} in CI. Init also needs the TolkBot GitHub App on this repository, and translating needs an organization with a subscription.`
|
|
28251
|
+
);
|
|
28252
|
+
}
|
|
28253
|
+
if (isTokenExpired(resolved)) {
|
|
28254
|
+
throw new AuthError(
|
|
28255
|
+
`Your token (from ${describeTokenSource(resolved.source)}) has expired.`,
|
|
28256
|
+
reauthHint(resolved.source),
|
|
28257
|
+
"token_expired"
|
|
28258
|
+
);
|
|
28259
|
+
}
|
|
28428
28260
|
}
|
|
28429
28261
|
async function runInit(opts = {}) {
|
|
28430
28262
|
const cwd = process.cwd();
|
|
28431
|
-
|
|
28432
|
-
|
|
28433
|
-
if (!headless) {
|
|
28263
|
+
await requireLogin();
|
|
28264
|
+
if (!opts.yes && !opts.json && isInteractive()) {
|
|
28434
28265
|
await runInteractive(opts);
|
|
28435
28266
|
return;
|
|
28436
28267
|
}
|
|
28437
|
-
|
|
28438
|
-
throw new CliError(
|
|
28439
|
-
"tolkbot init needs a terminal to prompt, and this session has none.",
|
|
28440
|
-
"Re-run with --yes to accept detected settings, overriding any of them with --base, --targets or --framework."
|
|
28441
|
-
);
|
|
28442
|
-
}
|
|
28443
|
-
try {
|
|
28444
|
-
await runHeadless(cwd, opts);
|
|
28445
|
-
} catch (error62) {
|
|
28446
|
-
if (opts.json && !(error62 instanceof CliError)) {
|
|
28447
|
-
throw new CliError(describeError(error62));
|
|
28448
|
-
}
|
|
28449
|
-
throw error62;
|
|
28450
|
-
}
|
|
28268
|
+
await runHeadless(cwd, opts);
|
|
28451
28269
|
}
|
|
28452
28270
|
async function runInteractive(opts) {
|
|
28453
28271
|
const outcome = { failed: false };
|
|
28454
|
-
const { unmount, waitUntilExit } =
|
|
28272
|
+
const { unmount, waitUntilExit } = render10(
|
|
28455
28273
|
/* @__PURE__ */ jsx10(
|
|
28456
28274
|
InitWizard,
|
|
28457
28275
|
{
|
|
@@ -28487,7 +28305,7 @@ import { z as z5 } from "zod/v4";
|
|
|
28487
28305
|
|
|
28488
28306
|
// src/commands/learnings/archive.tsx
|
|
28489
28307
|
import { Spinner as Spinner9 } from "@inkjs/ui";
|
|
28490
|
-
import { Box as Box11, render as
|
|
28308
|
+
import { Box as Box11, render as render11, Text as Text11 } from "ink";
|
|
28491
28309
|
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
28492
28310
|
function ArchiveUI({ state }) {
|
|
28493
28311
|
if (state.type === "archiving") {
|
|
@@ -28496,64 +28314,38 @@ function ArchiveUI({ state }) {
|
|
|
28496
28314
|
/* @__PURE__ */ jsx11(Text11, { children: " Archiving learning\u2026" })
|
|
28497
28315
|
] });
|
|
28498
28316
|
}
|
|
28499
|
-
|
|
28500
|
-
|
|
28501
|
-
|
|
28502
|
-
/* @__PURE__ */ jsx11(Text11, { children: " Learning archived" })
|
|
28503
|
-
] });
|
|
28504
|
-
}
|
|
28505
|
-
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
28506
|
-
/* @__PURE__ */ jsx11(Text11, { color: "red", children: "\u2717 Failed to archive learning" }),
|
|
28507
|
-
/* @__PURE__ */ jsx11(Text11, { dimColor: true, children: state.message })
|
|
28317
|
+
return /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
28318
|
+
/* @__PURE__ */ jsx11(Text11, { color: "green", children: "\u2713" }),
|
|
28319
|
+
/* @__PURE__ */ jsx11(Text11, { children: " Learning archived" })
|
|
28508
28320
|
] });
|
|
28509
28321
|
}
|
|
28510
28322
|
async function runLearningsArchive(opts) {
|
|
28511
|
-
|
|
28512
|
-
try {
|
|
28513
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
28514
|
-
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28515
|
-
await convexClient.mutation(api.learnings.archive, {
|
|
28516
|
-
organizationId: organizationId2,
|
|
28517
|
-
learningId: opts.id
|
|
28518
|
-
});
|
|
28519
|
-
jsonSuccess({ id: opts.id, ok: true });
|
|
28520
|
-
} catch (error62) {
|
|
28521
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
28522
|
-
}
|
|
28523
|
-
}
|
|
28524
|
-
let { unmount } = render10(/* @__PURE__ */ jsx11(ArchiveUI, { state: { type: "archiving" } }));
|
|
28525
|
-
try {
|
|
28323
|
+
const run = async () => {
|
|
28526
28324
|
const convexClient = await getAuthenticatedConvexClient();
|
|
28527
28325
|
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28528
28326
|
await convexClient.mutation(api.learnings.archive, {
|
|
28529
28327
|
organizationId: organizationId2,
|
|
28530
28328
|
learningId: opts.id
|
|
28531
28329
|
});
|
|
28330
|
+
};
|
|
28331
|
+
if (opts.json) {
|
|
28332
|
+
await run();
|
|
28333
|
+
jsonSuccess({ id: opts.id, ok: true });
|
|
28334
|
+
}
|
|
28335
|
+
const { unmount } = renderTransient(
|
|
28336
|
+
/* @__PURE__ */ jsx11(ArchiveUI, { state: { type: "archiving" } })
|
|
28337
|
+
);
|
|
28338
|
+
try {
|
|
28339
|
+
await run();
|
|
28340
|
+
} finally {
|
|
28532
28341
|
unmount();
|
|
28533
|
-
const ui = render10(/* @__PURE__ */ jsx11(ArchiveUI, { state: { type: "success" } }));
|
|
28534
|
-
unmount = ui.unmount;
|
|
28535
|
-
await ui.waitUntilExit();
|
|
28536
|
-
} catch (error62) {
|
|
28537
|
-
unmount();
|
|
28538
|
-
const ui = render10(
|
|
28539
|
-
/* @__PURE__ */ jsx11(
|
|
28540
|
-
ArchiveUI,
|
|
28541
|
-
{
|
|
28542
|
-
state: {
|
|
28543
|
-
type: "error",
|
|
28544
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
28545
|
-
}
|
|
28546
|
-
}
|
|
28547
|
-
)
|
|
28548
|
-
);
|
|
28549
|
-
await ui.waitUntilExit();
|
|
28550
|
-
return exitAfterFlush(1);
|
|
28551
28342
|
}
|
|
28343
|
+
render11(/* @__PURE__ */ jsx11(ArchiveUI, { state: { type: "success" } })).unmount();
|
|
28552
28344
|
}
|
|
28553
28345
|
|
|
28554
28346
|
// src/commands/learnings/list.tsx
|
|
28555
28347
|
import { Spinner as Spinner10 } from "@inkjs/ui";
|
|
28556
|
-
import { Box as Box12, render as
|
|
28348
|
+
import { Box as Box12, render as render12, Text as Text12 } from "ink";
|
|
28557
28349
|
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
28558
28350
|
var feedbackLabels = {
|
|
28559
28351
|
"incorrect-term": "Incorrect term",
|
|
@@ -28569,12 +28361,6 @@ function LearningsListUI({ state }) {
|
|
|
28569
28361
|
/* @__PURE__ */ jsx12(Text12, { children: " Loading learnings\u2026" })
|
|
28570
28362
|
] });
|
|
28571
28363
|
}
|
|
28572
|
-
if (state.type === "error") {
|
|
28573
|
-
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
28574
|
-
/* @__PURE__ */ jsx12(Text12, { color: "red", children: "\u2717 Error" }),
|
|
28575
|
-
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: state.message })
|
|
28576
|
-
] });
|
|
28577
|
-
}
|
|
28578
28364
|
const { learnings } = state;
|
|
28579
28365
|
if (learnings.length === 0) {
|
|
28580
28366
|
return /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "No learnings found." }) });
|
|
@@ -28617,143 +28403,90 @@ function LearningsListUI({ state }) {
|
|
|
28617
28403
|
] }, l._id))
|
|
28618
28404
|
] });
|
|
28619
28405
|
}
|
|
28406
|
+
async function loadLearnings(opts) {
|
|
28407
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
28408
|
+
const { projectId, organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28409
|
+
return convexClient.query(api.learnings.listByProject, {
|
|
28410
|
+
organizationId: organizationId2,
|
|
28411
|
+
projectId,
|
|
28412
|
+
status: opts.status,
|
|
28413
|
+
limit: opts.limit
|
|
28414
|
+
});
|
|
28415
|
+
}
|
|
28620
28416
|
async function runLearningsList(opts) {
|
|
28621
28417
|
if (opts.json) {
|
|
28622
|
-
|
|
28623
|
-
|
|
28624
|
-
|
|
28625
|
-
|
|
28626
|
-
|
|
28627
|
-
|
|
28628
|
-
|
|
28629
|
-
|
|
28630
|
-
|
|
28631
|
-
|
|
28632
|
-
|
|
28633
|
-
|
|
28634
|
-
|
|
28635
|
-
|
|
28636
|
-
id: l._id,
|
|
28637
|
-
sourceText: l.sourceText,
|
|
28638
|
-
suggestedTranslation: l.suggestedTranslation,
|
|
28639
|
-
srcLocale: l.srcLocale,
|
|
28640
|
-
dstLocale: l.dstLocale,
|
|
28641
|
-
feedbackType: l.feedbackType,
|
|
28642
|
-
reasoning: l.reasoning,
|
|
28643
|
-
status: l.status,
|
|
28644
|
-
source: l.source
|
|
28645
|
-
}))
|
|
28646
|
-
});
|
|
28647
|
-
} catch (error62) {
|
|
28648
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
28649
|
-
}
|
|
28418
|
+
const learnings2 = await loadLearnings(opts);
|
|
28419
|
+
jsonSuccess({
|
|
28420
|
+
learnings: learnings2.map((l) => ({
|
|
28421
|
+
id: l._id,
|
|
28422
|
+
sourceText: l.sourceText,
|
|
28423
|
+
suggestedTranslation: l.suggestedTranslation,
|
|
28424
|
+
srcLocale: l.srcLocale,
|
|
28425
|
+
dstLocale: l.dstLocale,
|
|
28426
|
+
feedbackType: l.feedbackType,
|
|
28427
|
+
reasoning: l.reasoning,
|
|
28428
|
+
status: l.status,
|
|
28429
|
+
source: l.source
|
|
28430
|
+
}))
|
|
28431
|
+
});
|
|
28650
28432
|
}
|
|
28651
|
-
|
|
28433
|
+
const { unmount } = renderTransient(
|
|
28434
|
+
/* @__PURE__ */ jsx12(LearningsListUI, { state: { type: "loading" } })
|
|
28435
|
+
);
|
|
28436
|
+
let learnings;
|
|
28652
28437
|
try {
|
|
28653
|
-
|
|
28654
|
-
|
|
28655
|
-
const learnings = await convexClient.query(
|
|
28656
|
-
api.learnings.listByProject,
|
|
28657
|
-
{
|
|
28658
|
-
organizationId: organizationId2,
|
|
28659
|
-
projectId,
|
|
28660
|
-
status: opts.status,
|
|
28661
|
-
limit: opts.limit
|
|
28662
|
-
}
|
|
28663
|
-
);
|
|
28664
|
-
unmount();
|
|
28665
|
-
const ui = render11(
|
|
28666
|
-
/* @__PURE__ */ jsx12(LearningsListUI, { state: { type: "success", learnings } })
|
|
28667
|
-
);
|
|
28668
|
-
unmount = ui.unmount;
|
|
28669
|
-
await ui.waitUntilExit();
|
|
28670
|
-
} catch (error62) {
|
|
28438
|
+
learnings = await loadLearnings(opts);
|
|
28439
|
+
} finally {
|
|
28671
28440
|
unmount();
|
|
28672
|
-
const ui = render11(
|
|
28673
|
-
/* @__PURE__ */ jsx12(
|
|
28674
|
-
LearningsListUI,
|
|
28675
|
-
{
|
|
28676
|
-
state: {
|
|
28677
|
-
type: "error",
|
|
28678
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
28679
|
-
}
|
|
28680
|
-
}
|
|
28681
|
-
)
|
|
28682
|
-
);
|
|
28683
|
-
await ui.waitUntilExit();
|
|
28684
|
-
return exitAfterFlush(1);
|
|
28685
28441
|
}
|
|
28442
|
+
render12(
|
|
28443
|
+
/* @__PURE__ */ jsx12(LearningsListUI, { state: { type: "success", learnings } })
|
|
28444
|
+
).unmount();
|
|
28686
28445
|
}
|
|
28687
28446
|
|
|
28688
28447
|
// src/commands/learnings/remove.tsx
|
|
28689
28448
|
import { Spinner as Spinner11 } from "@inkjs/ui";
|
|
28690
|
-
import { Box as Box13, render as
|
|
28449
|
+
import { Box as Box13, render as render13, Text as Text13 } from "ink";
|
|
28691
28450
|
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
28692
28451
|
function RemoveUI2({ state }) {
|
|
28693
28452
|
if (state.type === "removing") {
|
|
28694
28453
|
return /* @__PURE__ */ jsxs13(Box13, { children: [
|
|
28695
28454
|
/* @__PURE__ */ jsx13(Spinner11, {}),
|
|
28696
|
-
/* @__PURE__ */ jsx13(Text13, { children: " Removing learning\u2026" })
|
|
28697
|
-
] });
|
|
28698
|
-
}
|
|
28699
|
-
if (state.type === "success") {
|
|
28700
|
-
return /* @__PURE__ */ jsxs13(Box13, { children: [
|
|
28701
|
-
/* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713" }),
|
|
28702
|
-
/* @__PURE__ */ jsx13(Text13, { children: " Learning removed" })
|
|
28455
|
+
/* @__PURE__ */ jsx13(Text13, { children: " Removing learning\u2026" })
|
|
28703
28456
|
] });
|
|
28704
28457
|
}
|
|
28705
|
-
return /* @__PURE__ */ jsxs13(Box13, {
|
|
28706
|
-
/* @__PURE__ */ jsx13(Text13, { color: "
|
|
28707
|
-
/* @__PURE__ */ jsx13(Text13, {
|
|
28458
|
+
return /* @__PURE__ */ jsxs13(Box13, { children: [
|
|
28459
|
+
/* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713" }),
|
|
28460
|
+
/* @__PURE__ */ jsx13(Text13, { children: " Learning removed" })
|
|
28708
28461
|
] });
|
|
28709
28462
|
}
|
|
28710
28463
|
async function runLearningsRemove(opts) {
|
|
28711
|
-
|
|
28712
|
-
try {
|
|
28713
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
28714
|
-
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28715
|
-
await convexClient.mutation(api.learnings.remove, {
|
|
28716
|
-
organizationId: organizationId2,
|
|
28717
|
-
learningId: opts.id
|
|
28718
|
-
});
|
|
28719
|
-
jsonSuccess({ id: opts.id, ok: true });
|
|
28720
|
-
} catch (error62) {
|
|
28721
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
28722
|
-
}
|
|
28723
|
-
}
|
|
28724
|
-
let { unmount } = render12(/* @__PURE__ */ jsx13(RemoveUI2, { state: { type: "removing" } }));
|
|
28725
|
-
try {
|
|
28464
|
+
const run = async () => {
|
|
28726
28465
|
const convexClient = await getAuthenticatedConvexClient();
|
|
28727
28466
|
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28728
28467
|
await convexClient.mutation(api.learnings.remove, {
|
|
28729
28468
|
organizationId: organizationId2,
|
|
28730
28469
|
learningId: opts.id
|
|
28731
28470
|
});
|
|
28471
|
+
};
|
|
28472
|
+
if (opts.json) {
|
|
28473
|
+
await run();
|
|
28474
|
+
jsonSuccess({ id: opts.id, ok: true });
|
|
28475
|
+
}
|
|
28476
|
+
const { unmount } = renderTransient(
|
|
28477
|
+
/* @__PURE__ */ jsx13(RemoveUI2, { state: { type: "removing" } })
|
|
28478
|
+
);
|
|
28479
|
+
try {
|
|
28480
|
+
await run();
|
|
28481
|
+
} finally {
|
|
28732
28482
|
unmount();
|
|
28733
|
-
const ui = render12(/* @__PURE__ */ jsx13(RemoveUI2, { state: { type: "success" } }));
|
|
28734
|
-
unmount = ui.unmount;
|
|
28735
|
-
await ui.waitUntilExit();
|
|
28736
|
-
} catch (error62) {
|
|
28737
|
-
unmount();
|
|
28738
|
-
const ui = render12(
|
|
28739
|
-
/* @__PURE__ */ jsx13(
|
|
28740
|
-
RemoveUI2,
|
|
28741
|
-
{
|
|
28742
|
-
state: {
|
|
28743
|
-
type: "error",
|
|
28744
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
28745
|
-
}
|
|
28746
|
-
}
|
|
28747
|
-
)
|
|
28748
|
-
);
|
|
28749
|
-
await ui.waitUntilExit();
|
|
28750
|
-
return exitAfterFlush(1);
|
|
28751
28483
|
}
|
|
28484
|
+
render13(/* @__PURE__ */ jsx13(RemoveUI2, { state: { type: "success" } })).unmount();
|
|
28752
28485
|
}
|
|
28753
28486
|
|
|
28754
28487
|
// src/commands/learnings/show.tsx
|
|
28755
28488
|
import { Spinner as Spinner12 } from "@inkjs/ui";
|
|
28756
|
-
import { Box as Box14, render as
|
|
28489
|
+
import { Box as Box14, render as render14, Text as Text14 } from "ink";
|
|
28757
28490
|
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
28758
28491
|
function LearningShowUI({ state }) {
|
|
28759
28492
|
if (state.type === "loading") {
|
|
@@ -28762,15 +28495,6 @@ function LearningShowUI({ state }) {
|
|
|
28762
28495
|
/* @__PURE__ */ jsx14(Text14, { children: " Loading learning\u2026" })
|
|
28763
28496
|
] });
|
|
28764
28497
|
}
|
|
28765
|
-
if (state.type === "not-found") {
|
|
28766
|
-
return /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text14, { color: "red", children: "\u2717 Learning not found" }) });
|
|
28767
|
-
}
|
|
28768
|
-
if (state.type === "error") {
|
|
28769
|
-
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
28770
|
-
/* @__PURE__ */ jsx14(Text14, { color: "red", children: "\u2717 Error" }),
|
|
28771
|
-
/* @__PURE__ */ jsx14(Text14, { dimColor: true, children: state.message })
|
|
28772
|
-
] });
|
|
28773
|
-
}
|
|
28774
28498
|
const { learning } = state;
|
|
28775
28499
|
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
28776
28500
|
/* @__PURE__ */ jsxs14(Box14, { gap: 1, children: [
|
|
@@ -28811,70 +28535,47 @@ function LearningShowUI({ state }) {
|
|
|
28811
28535
|
] }) })
|
|
28812
28536
|
] });
|
|
28813
28537
|
}
|
|
28538
|
+
async function loadLearning(id) {
|
|
28539
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
28540
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28541
|
+
const learning = await convexClient.query(api.learnings.getById, {
|
|
28542
|
+
organizationId: organizationId2,
|
|
28543
|
+
learningId: id
|
|
28544
|
+
});
|
|
28545
|
+
if (!learning) {
|
|
28546
|
+
throw new CliError(
|
|
28547
|
+
`Learning ${id} was not found.`,
|
|
28548
|
+
"List them with 'tolkbot learnings list'.",
|
|
28549
|
+
"not_found"
|
|
28550
|
+
);
|
|
28551
|
+
}
|
|
28552
|
+
return learning;
|
|
28553
|
+
}
|
|
28814
28554
|
async function runLearningsShow(opts) {
|
|
28815
28555
|
if (opts.json) {
|
|
28816
|
-
|
|
28817
|
-
|
|
28818
|
-
|
|
28819
|
-
|
|
28820
|
-
|
|
28821
|
-
|
|
28822
|
-
|
|
28823
|
-
|
|
28824
|
-
|
|
28825
|
-
|
|
28826
|
-
|
|
28827
|
-
|
|
28828
|
-
sourceText: learning.sourceText,
|
|
28829
|
-
suggestedTranslation: learning.suggestedTranslation,
|
|
28830
|
-
srcLocale: learning.srcLocale,
|
|
28831
|
-
dstLocale: learning.dstLocale,
|
|
28832
|
-
feedbackType: learning.feedbackType,
|
|
28833
|
-
reasoning: learning.reasoning,
|
|
28834
|
-
status: learning.status,
|
|
28835
|
-
source: learning.source
|
|
28836
|
-
});
|
|
28837
|
-
} catch (error62) {
|
|
28838
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
28839
|
-
}
|
|
28556
|
+
const learning2 = await loadLearning(opts.id);
|
|
28557
|
+
jsonSuccess({
|
|
28558
|
+
id: learning2._id,
|
|
28559
|
+
sourceText: learning2.sourceText,
|
|
28560
|
+
suggestedTranslation: learning2.suggestedTranslation,
|
|
28561
|
+
srcLocale: learning2.srcLocale,
|
|
28562
|
+
dstLocale: learning2.dstLocale,
|
|
28563
|
+
feedbackType: learning2.feedbackType,
|
|
28564
|
+
reasoning: learning2.reasoning,
|
|
28565
|
+
status: learning2.status,
|
|
28566
|
+
source: learning2.source
|
|
28567
|
+
});
|
|
28840
28568
|
}
|
|
28841
|
-
|
|
28569
|
+
const { unmount } = renderTransient(
|
|
28570
|
+
/* @__PURE__ */ jsx14(LearningShowUI, { state: { type: "loading" } })
|
|
28571
|
+
);
|
|
28572
|
+
let learning;
|
|
28842
28573
|
try {
|
|
28843
|
-
|
|
28844
|
-
|
|
28845
|
-
const learning = await convexClient.query(api.learnings.getById, {
|
|
28846
|
-
organizationId: organizationId2,
|
|
28847
|
-
learningId: opts.id
|
|
28848
|
-
});
|
|
28849
|
-
if (!learning) {
|
|
28850
|
-
unmount();
|
|
28851
|
-
const ui2 = render13(/* @__PURE__ */ jsx14(LearningShowUI, { state: { type: "not-found" } }));
|
|
28852
|
-
unmount = ui2.unmount;
|
|
28853
|
-
await ui2.waitUntilExit();
|
|
28854
|
-
return exitAfterFlush(1);
|
|
28855
|
-
}
|
|
28856
|
-
unmount();
|
|
28857
|
-
const ui = render13(
|
|
28858
|
-
/* @__PURE__ */ jsx14(LearningShowUI, { state: { type: "success", learning } })
|
|
28859
|
-
);
|
|
28860
|
-
unmount = ui.unmount;
|
|
28861
|
-
await ui.waitUntilExit();
|
|
28862
|
-
} catch (error62) {
|
|
28574
|
+
learning = await loadLearning(opts.id);
|
|
28575
|
+
} finally {
|
|
28863
28576
|
unmount();
|
|
28864
|
-
const ui = render13(
|
|
28865
|
-
/* @__PURE__ */ jsx14(
|
|
28866
|
-
LearningShowUI,
|
|
28867
|
-
{
|
|
28868
|
-
state: {
|
|
28869
|
-
type: "error",
|
|
28870
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
28871
|
-
}
|
|
28872
|
-
}
|
|
28873
|
-
)
|
|
28874
|
-
);
|
|
28875
|
-
await ui.waitUntilExit();
|
|
28876
|
-
return exitAfterFlush(1);
|
|
28877
28577
|
}
|
|
28578
|
+
render14(/* @__PURE__ */ jsx14(LearningShowUI, { state: { type: "success", learning } })).unmount();
|
|
28878
28579
|
}
|
|
28879
28580
|
|
|
28880
28581
|
// src/commands/learnings/index.ts
|
|
@@ -28964,13 +28665,13 @@ import { Command as Command5 } from "commander";
|
|
|
28964
28665
|
import { z as z7 } from "zod/v4";
|
|
28965
28666
|
|
|
28966
28667
|
// src/commands/translate/plan.tsx
|
|
28967
|
-
import
|
|
28668
|
+
import fs8 from "fs/promises";
|
|
28968
28669
|
import path8 from "path";
|
|
28969
28670
|
import { Spinner as Spinner13 } from "@inkjs/ui";
|
|
28970
|
-
import { Box as Box15, render as
|
|
28671
|
+
import { Box as Box15, render as render15, Text as Text15 } from "ink";
|
|
28971
28672
|
|
|
28972
28673
|
// src/commands/translate/task-generator.ts
|
|
28973
|
-
import
|
|
28674
|
+
import fs7 from "fs/promises";
|
|
28974
28675
|
import path7 from "path";
|
|
28975
28676
|
|
|
28976
28677
|
// ../../node_modules/.pnpm/emoji-regex@10.6.0/node_modules/emoji-regex/index.mjs
|
|
@@ -29066,7 +28767,7 @@ function eastAsianWidth(codePoint, { ambiguousAsWide = false } = {}) {
|
|
|
29066
28767
|
var import_fast_glob = __toESM(require_out4(), 1);
|
|
29067
28768
|
var import_fast_glob2 = __toESM(require_out4(), 1);
|
|
29068
28769
|
import path5 from "path";
|
|
29069
|
-
import
|
|
28770
|
+
import fs5 from "fs/promises";
|
|
29070
28771
|
import path23 from "path";
|
|
29071
28772
|
import fs23 from "fs/promises";
|
|
29072
28773
|
import path33 from "path";
|
|
@@ -29174,11 +28875,11 @@ function flattenValues(data) {
|
|
|
29174
28875
|
const values = /* @__PURE__ */ new Map();
|
|
29175
28876
|
function walk(obj, prefix) {
|
|
29176
28877
|
for (const [key, value] of Object.entries(obj)) {
|
|
29177
|
-
const
|
|
28878
|
+
const fullKey4 = prefix ? `${prefix}.${key}` : key;
|
|
29178
28879
|
if (typeof value === "string") {
|
|
29179
|
-
values.set(
|
|
28880
|
+
values.set(fullKey4, value);
|
|
29180
28881
|
} else if (typeof value === "object" && value !== null) {
|
|
29181
|
-
walk(value,
|
|
28882
|
+
walk(value, fullKey4);
|
|
29182
28883
|
}
|
|
29183
28884
|
}
|
|
29184
28885
|
}
|
|
@@ -29280,23 +28981,23 @@ function parseTsContent(content) {
|
|
|
29280
28981
|
let parsed;
|
|
29281
28982
|
try {
|
|
29282
28983
|
parsed = JSON.parse(jsonString);
|
|
29283
|
-
} catch (
|
|
29284
|
-
if (
|
|
29285
|
-
const posMatch =
|
|
28984
|
+
} catch (jsonError) {
|
|
28985
|
+
if (jsonError instanceof SyntaxError) {
|
|
28986
|
+
const posMatch = jsonError.message.match(/position\s+(\d+)/);
|
|
29286
28987
|
const pos = posMatch ? Number(posMatch[1]) : -1;
|
|
29287
28988
|
const start = Math.max(0, pos - 40);
|
|
29288
28989
|
const end = Math.min(jsonString.length, pos + 40);
|
|
29289
28990
|
const snippet = jsonString.slice(start, end);
|
|
29290
28991
|
const pointer = pos >= 0 ? " ".repeat(Math.min(pos - start, 40)) + "^" : "";
|
|
29291
28992
|
throw new SyntaxError(
|
|
29292
|
-
`${
|
|
28993
|
+
`${jsonError.message}
|
|
29293
28994
|
--- context around position ${pos} ---
|
|
29294
28995
|
${snippet}
|
|
29295
28996
|
${pointer}`,
|
|
29296
|
-
{ cause:
|
|
28997
|
+
{ cause: jsonError }
|
|
29297
28998
|
);
|
|
29298
28999
|
}
|
|
29299
|
-
throw
|
|
29000
|
+
throw jsonError;
|
|
29300
29001
|
}
|
|
29301
29002
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
29302
29003
|
return {
|
|
@@ -30316,7 +30017,7 @@ var JsonAdapter = class extends BaseAdapter {
|
|
|
30316
30017
|
for (const dir of COMMON_LOCALE_DIRS) {
|
|
30317
30018
|
const dirPath = path23.join(baseDir, dir);
|
|
30318
30019
|
try {
|
|
30319
|
-
await
|
|
30020
|
+
await fs5.access(dirPath);
|
|
30320
30021
|
const files = await (0, import_fast_glob.default)("**/*.json", {
|
|
30321
30022
|
cwd: dirPath,
|
|
30322
30023
|
absolute: true,
|
|
@@ -30348,7 +30049,7 @@ var JsonAdapter = class extends BaseAdapter {
|
|
|
30348
30049
|
async parse(filePath) {
|
|
30349
30050
|
let content;
|
|
30350
30051
|
try {
|
|
30351
|
-
content = await
|
|
30052
|
+
content = await fs5.readFile(filePath, "utf-8");
|
|
30352
30053
|
} catch (error62) {
|
|
30353
30054
|
if (error62 instanceof Error && "code" in error62 && error62.code === "ENOENT") {
|
|
30354
30055
|
throw new FileNotFoundError(filePath, error62);
|
|
@@ -30389,16 +30090,16 @@ var JsonAdapter = class extends BaseAdapter {
|
|
|
30389
30090
|
let finalData = data;
|
|
30390
30091
|
if (shouldMerge) {
|
|
30391
30092
|
try {
|
|
30392
|
-
const existingContent = await
|
|
30093
|
+
const existingContent = await fs5.readFile(filePath, "utf-8");
|
|
30393
30094
|
const existing = parseJsonContent(existingContent);
|
|
30394
30095
|
finalData = merge2(existing.data, data);
|
|
30395
30096
|
} catch {
|
|
30396
30097
|
}
|
|
30397
30098
|
}
|
|
30398
30099
|
const dir = path23.dirname(filePath);
|
|
30399
|
-
await
|
|
30100
|
+
await fs5.mkdir(dir, { recursive: true });
|
|
30400
30101
|
const content = serializeJsonContent(finalData, { indent });
|
|
30401
|
-
await
|
|
30102
|
+
await fs5.writeFile(filePath, content, "utf-8");
|
|
30402
30103
|
}
|
|
30403
30104
|
};
|
|
30404
30105
|
var IGNORED = [
|
|
@@ -31298,7 +30999,7 @@ function planLocale(input2) {
|
|
|
31298
30999
|
}
|
|
31299
31000
|
|
|
31300
31001
|
// src/commands/translate/project-files.ts
|
|
31301
|
-
import
|
|
31002
|
+
import fs6 from "fs/promises";
|
|
31302
31003
|
import path6 from "path";
|
|
31303
31004
|
function fullPattern(scope) {
|
|
31304
31005
|
return scope.subpath ? path6.posix.join(scope.subpath, scope.sourcePattern) : scope.sourcePattern;
|
|
@@ -31346,7 +31047,7 @@ function targetFileFor(source, targetLocale, files, scope) {
|
|
|
31346
31047
|
}
|
|
31347
31048
|
async function readTranslationFile(absolutePath, format) {
|
|
31348
31049
|
try {
|
|
31349
|
-
const content = await
|
|
31050
|
+
const content = await fs6.readFile(absolutePath, "utf-8");
|
|
31350
31051
|
const file2 = await createAdapter(format).parse(absolutePath);
|
|
31351
31052
|
return { data: file2.data, content };
|
|
31352
31053
|
} catch (error62) {
|
|
@@ -31360,7 +31061,7 @@ async function readTranslationFile(absolutePath, format) {
|
|
|
31360
31061
|
var HOLD_CHUNK = 2e3;
|
|
31361
31062
|
async function loadTasksFromFile(tasksPath) {
|
|
31362
31063
|
const abs = path7.resolve(process.cwd(), tasksPath);
|
|
31363
|
-
const raw = await
|
|
31064
|
+
const raw = await fs7.readFile(abs, "utf8");
|
|
31364
31065
|
const parsed = JSON.parse(raw);
|
|
31365
31066
|
if (!Array.isArray(parsed)) throw new Error("tasks file must be an array");
|
|
31366
31067
|
return parsed;
|
|
@@ -31453,22 +31154,12 @@ async function generateTasks(ctx, srcLocale, dstLocale, opts = {}) {
|
|
|
31453
31154
|
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
31454
31155
|
async function loadPlanContext(cwd) {
|
|
31455
31156
|
const config3 = readConfig(cwd);
|
|
31456
|
-
if (!config3)
|
|
31457
|
-
throw new CliError(
|
|
31458
|
-
"No tolkbot.json found.",
|
|
31459
|
-
"Run 'tolkbot init' to create one."
|
|
31460
|
-
);
|
|
31461
|
-
}
|
|
31157
|
+
if (!config3) throw noConfigError();
|
|
31462
31158
|
const client3 = await getAuthenticatedConvexClient();
|
|
31463
31159
|
const project = await client3.query(api.projects.getProjectForCli, {
|
|
31464
31160
|
projectId: config3.config.projectId
|
|
31465
31161
|
});
|
|
31466
|
-
if (!project)
|
|
31467
|
-
throw new CliError(
|
|
31468
|
-
"Project not found.",
|
|
31469
|
-
"Run 'tolkbot init' again to link this repository to a project."
|
|
31470
|
-
);
|
|
31471
|
-
}
|
|
31162
|
+
if (!project) throw projectNotFoundError();
|
|
31472
31163
|
setTelemetryOrganization(project.organizationId);
|
|
31473
31164
|
return {
|
|
31474
31165
|
root: path8.dirname(config3.path),
|
|
@@ -31525,32 +31216,26 @@ function PlanUI({
|
|
|
31525
31216
|
] }) })
|
|
31526
31217
|
] });
|
|
31527
31218
|
}
|
|
31528
|
-
|
|
31529
|
-
const { plan } = state;
|
|
31530
|
-
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
31531
|
-
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
31532
|
-
/* @__PURE__ */ jsx15(Text15, { color: "green", children: "\u2713" }),
|
|
31533
|
-
/* @__PURE__ */ jsx15(Text15, { children: " Translation plan generated" })
|
|
31534
|
-
] }),
|
|
31535
|
-
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
31536
|
-
src,
|
|
31537
|
-
" \u2192 ",
|
|
31538
|
-
dst,
|
|
31539
|
-
": ",
|
|
31540
|
-
plan.tasks.length,
|
|
31541
|
-
" task(s)"
|
|
31542
|
-
] }) }),
|
|
31543
|
-
describePlan(plan).map((line) => /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: line }) }, line)),
|
|
31544
|
-
plan.tasks.length === 0 && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "yellow", children: [
|
|
31545
|
-
"No translations needed - ",
|
|
31546
|
-
dst,
|
|
31547
|
-
" is up to date."
|
|
31548
|
-
] }) })
|
|
31549
|
-
] });
|
|
31550
|
-
}
|
|
31219
|
+
const { plan } = state;
|
|
31551
31220
|
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
31552
|
-
/* @__PURE__ */
|
|
31553
|
-
|
|
31221
|
+
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
31222
|
+
/* @__PURE__ */ jsx15(Text15, { color: "green", children: "\u2713" }),
|
|
31223
|
+
/* @__PURE__ */ jsx15(Text15, { children: " Translation plan generated" })
|
|
31224
|
+
] }),
|
|
31225
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
31226
|
+
src,
|
|
31227
|
+
" \u2192 ",
|
|
31228
|
+
dst,
|
|
31229
|
+
": ",
|
|
31230
|
+
plan.tasks.length,
|
|
31231
|
+
" task(s)"
|
|
31232
|
+
] }) }),
|
|
31233
|
+
describePlan(plan).map((line) => /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: line }) }, line)),
|
|
31234
|
+
plan.tasks.length === 0 && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "yellow", children: [
|
|
31235
|
+
"No translations needed - ",
|
|
31236
|
+
dst,
|
|
31237
|
+
" is up to date."
|
|
31238
|
+
] }) })
|
|
31554
31239
|
] });
|
|
31555
31240
|
}
|
|
31556
31241
|
function planOutput(plan) {
|
|
@@ -31565,88 +31250,68 @@ function planOutput(plan) {
|
|
|
31565
31250
|
async function runPlan(opts) {
|
|
31566
31251
|
const cwd = process.cwd();
|
|
31567
31252
|
if (opts.json) {
|
|
31568
|
-
|
|
31569
|
-
|
|
31570
|
-
|
|
31571
|
-
|
|
31572
|
-
|
|
31573
|
-
|
|
31574
|
-
|
|
31575
|
-
|
|
31576
|
-
|
|
31577
|
-
|
|
31578
|
-
|
|
31579
|
-
|
|
31580
|
-
|
|
31581
|
-
|
|
31582
|
-
|
|
31583
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
31253
|
+
const ctx = await loadPlanContext(cwd);
|
|
31254
|
+
const src2 = opts.src ?? ctx.project.baseLanguage;
|
|
31255
|
+
const plan2 = await planForLocale(ctx, src2, opts.dst, {
|
|
31256
|
+
retranslateUntracked: opts.retranslateUntracked
|
|
31257
|
+
});
|
|
31258
|
+
track("cli_translate_completed", {
|
|
31259
|
+
mode: "plan",
|
|
31260
|
+
key_count: plan2.tasks.length,
|
|
31261
|
+
locale_count: 1,
|
|
31262
|
+
job_count: 0,
|
|
31263
|
+
json: true
|
|
31264
|
+
});
|
|
31265
|
+
if (opts.write) {
|
|
31266
|
+
const outputPath = await writeTasks(cwd, opts.write, plan2);
|
|
31267
|
+
jsonSuccess({ ...planOutput(plan2), writtenTo: outputPath });
|
|
31584
31268
|
}
|
|
31269
|
+
jsonSuccess(planOutput(plan2));
|
|
31585
31270
|
}
|
|
31586
31271
|
let src = opts.src ?? "";
|
|
31587
|
-
|
|
31272
|
+
const progress = renderTransient(
|
|
31588
31273
|
/* @__PURE__ */ jsx15(PlanUI, { state: { type: "loading" }, src, dst: opts.dst })
|
|
31589
31274
|
);
|
|
31275
|
+
let plan;
|
|
31590
31276
|
try {
|
|
31591
31277
|
const ctx = await loadPlanContext(cwd);
|
|
31592
31278
|
src = opts.src ?? ctx.project.baseLanguage;
|
|
31593
|
-
|
|
31594
|
-
const planningUi = render14(
|
|
31279
|
+
progress.rerender(
|
|
31595
31280
|
/* @__PURE__ */ jsx15(PlanUI, { state: { type: "planning" }, src, dst: opts.dst })
|
|
31596
31281
|
);
|
|
31597
|
-
|
|
31598
|
-
const plan = await planForLocale(ctx, src, opts.dst, {
|
|
31282
|
+
plan = await planForLocale(ctx, src, opts.dst, {
|
|
31599
31283
|
retranslateUntracked: opts.retranslateUntracked
|
|
31600
31284
|
});
|
|
31601
|
-
|
|
31602
|
-
|
|
31603
|
-
|
|
31604
|
-
|
|
31605
|
-
|
|
31606
|
-
|
|
31607
|
-
|
|
31608
|
-
|
|
31609
|
-
|
|
31610
|
-
|
|
31611
|
-
|
|
31612
|
-
|
|
31613
|
-
|
|
31614
|
-
|
|
31615
|
-
|
|
31616
|
-
|
|
31617
|
-
job_count: 0,
|
|
31618
|
-
json: false
|
|
31619
|
-
});
|
|
31620
|
-
const tasksJson = JSON.stringify(plan.tasks, null, 2);
|
|
31621
|
-
if (opts.write) {
|
|
31622
|
-
const outputPath = path8.resolve(cwd, opts.write);
|
|
31623
|
-
await fs7.writeFile(outputPath, tasksJson + "\n", "utf-8");
|
|
31624
|
-
await successUi.waitUntilExit();
|
|
31625
|
-
console.log(`
|
|
31285
|
+
} finally {
|
|
31286
|
+
progress.unmount();
|
|
31287
|
+
}
|
|
31288
|
+
render15(
|
|
31289
|
+
/* @__PURE__ */ jsx15(PlanUI, { state: { type: "success", plan }, src, dst: opts.dst })
|
|
31290
|
+
).unmount();
|
|
31291
|
+
track("cli_translate_completed", {
|
|
31292
|
+
mode: "plan",
|
|
31293
|
+
key_count: plan.tasks.length,
|
|
31294
|
+
locale_count: 1,
|
|
31295
|
+
job_count: 0,
|
|
31296
|
+
json: false
|
|
31297
|
+
});
|
|
31298
|
+
if (opts.write) {
|
|
31299
|
+
const outputPath = await writeTasks(cwd, opts.write, plan);
|
|
31300
|
+
console.log(`
|
|
31626
31301
|
Tasks written to: ${outputPath}`);
|
|
31627
|
-
|
|
31628
|
-
|
|
31629
|
-
console.log("\n" + tasksJson);
|
|
31630
|
-
}
|
|
31631
|
-
} catch (error62) {
|
|
31632
|
-
unmount();
|
|
31633
|
-
const errorUi = render14(
|
|
31634
|
-
/* @__PURE__ */ jsx15(
|
|
31635
|
-
PlanUI,
|
|
31636
|
-
{
|
|
31637
|
-
state: {
|
|
31638
|
-
type: "error",
|
|
31639
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
31640
|
-
},
|
|
31641
|
-
src,
|
|
31642
|
-
dst: opts.dst
|
|
31643
|
-
}
|
|
31644
|
-
)
|
|
31645
|
-
);
|
|
31646
|
-
await errorUi.waitUntilExit();
|
|
31647
|
-
return exitAfterFlush(1);
|
|
31302
|
+
} else {
|
|
31303
|
+
console.log("\n" + JSON.stringify(plan.tasks, null, 2));
|
|
31648
31304
|
}
|
|
31649
31305
|
}
|
|
31306
|
+
async function writeTasks(cwd, file2, plan) {
|
|
31307
|
+
const outputPath = path8.resolve(cwd, file2);
|
|
31308
|
+
await fs8.writeFile(
|
|
31309
|
+
outputPath,
|
|
31310
|
+
JSON.stringify(plan.tasks, null, 2) + "\n",
|
|
31311
|
+
"utf-8"
|
|
31312
|
+
);
|
|
31313
|
+
return outputPath;
|
|
31314
|
+
}
|
|
31650
31315
|
|
|
31651
31316
|
// src/commands/translate/adopt.tsx
|
|
31652
31317
|
var STAMP_CHUNK = 200;
|
|
@@ -31694,51 +31359,44 @@ async function runAdopt(opts) {
|
|
|
31694
31359
|
const src = opts.src ?? ctx.project.baseLanguage;
|
|
31695
31360
|
const locales = opts.all ? ctx.project.targetLanguages : opts.dst ? [opts.dst] : [];
|
|
31696
31361
|
if (locales.length === 0) {
|
|
31697
|
-
throw new CliError(
|
|
31698
|
-
|
|
31362
|
+
throw opts.all ? new CliError(
|
|
31363
|
+
"No target languages configured for this project.",
|
|
31364
|
+
void 0,
|
|
31365
|
+
"no_target_languages"
|
|
31366
|
+
) : new UsageError(
|
|
31367
|
+
"Either --dst <locale> or --all must be provided."
|
|
31699
31368
|
);
|
|
31700
31369
|
}
|
|
31701
|
-
const
|
|
31370
|
+
const outcomes2 = [];
|
|
31702
31371
|
for (const dst of locales)
|
|
31703
|
-
|
|
31372
|
+
outcomes2.push(await adoptLocale(ctx, src, dst));
|
|
31704
31373
|
track("cli_translate_completed", {
|
|
31705
31374
|
mode: "adopt",
|
|
31706
|
-
key_count:
|
|
31707
|
-
locale_count:
|
|
31375
|
+
key_count: outcomes2.reduce((n, o) => n + o.adopted, 0),
|
|
31376
|
+
locale_count: outcomes2.length,
|
|
31708
31377
|
job_count: 0,
|
|
31709
31378
|
json: opts.json === true
|
|
31710
31379
|
});
|
|
31711
|
-
return
|
|
31380
|
+
return outcomes2;
|
|
31712
31381
|
};
|
|
31713
31382
|
if (opts.json) {
|
|
31714
|
-
|
|
31715
|
-
jsonSuccess({ adopted: await run() });
|
|
31716
|
-
} catch (error62) {
|
|
31717
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
31718
|
-
}
|
|
31383
|
+
jsonSuccess({ adopted: await run() });
|
|
31719
31384
|
}
|
|
31720
|
-
|
|
31721
|
-
|
|
31722
|
-
|
|
31723
|
-
|
|
31724
|
-
`\u2713 ${o.dstLocale}: adopted ${o.adopted} untracked translation(s)` + (o.skipped > 0 ? `, ${o.skipped} already tracked` : "")
|
|
31725
|
-
);
|
|
31726
|
-
}
|
|
31727
|
-
} catch (error62) {
|
|
31728
|
-
console.error(
|
|
31729
|
-
`\u2717 ${error62 instanceof Error ? error62.message : String(error62)}`
|
|
31385
|
+
const outcomes = await run();
|
|
31386
|
+
for (const o of outcomes) {
|
|
31387
|
+
console.log(
|
|
31388
|
+
`\u2713 ${o.dstLocale}: adopted ${o.adopted} untracked translation(s)` + (o.skipped > 0 ? `, ${o.skipped} already tracked` : "")
|
|
31730
31389
|
);
|
|
31731
|
-
return exitAfterFlush(1);
|
|
31732
31390
|
}
|
|
31733
31391
|
}
|
|
31734
31392
|
|
|
31735
31393
|
// src/commands/translate/apply.tsx
|
|
31736
31394
|
import { useState as useState2 } from "react";
|
|
31737
|
-
import { MultiSelect
|
|
31738
|
-
import { Box as Box16, render as
|
|
31395
|
+
import { MultiSelect } from "@inkjs/ui";
|
|
31396
|
+
import { Box as Box16, render as render16, Text as Text16 } from "ink";
|
|
31739
31397
|
|
|
31740
31398
|
// src/commands/translate/apply-core.ts
|
|
31741
|
-
import
|
|
31399
|
+
import fs9 from "fs/promises";
|
|
31742
31400
|
import path9 from "path";
|
|
31743
31401
|
function fullKey2(namespace, key) {
|
|
31744
31402
|
return namespace ? `${namespace}.${key}` : key;
|
|
@@ -31785,26 +31443,32 @@ async function applyTasks(input2) {
|
|
|
31785
31443
|
for (const task of tasks) {
|
|
31786
31444
|
const sourceKey = sourceKeyFor(task.key, baseKeys);
|
|
31787
31445
|
const currentSource = sourceKey === null ? void 0 : getByPath(source.data, sourceKey);
|
|
31788
|
-
if (typeof currentSource !== "string" || computeValueHash(currentSource) !==
|
|
31446
|
+
if (typeof currentSource !== "string" || computeValueHash(currentSource) !== computeValueHash(task.src)) {
|
|
31789
31447
|
refuse(task, "source_changed");
|
|
31790
31448
|
continue;
|
|
31791
31449
|
}
|
|
31450
|
+
const translatedValue = task.dst ?? "";
|
|
31792
31451
|
const current = getByPath(data, task.key);
|
|
31793
31452
|
const currentHash = typeof current === "string" ? computeValueHash(current) : null;
|
|
31794
31453
|
if (task.expectedTargetHash !== void 0 && task.expectedTargetHash !== currentHash) {
|
|
31795
|
-
refuse(
|
|
31454
|
+
refuse(
|
|
31455
|
+
task,
|
|
31456
|
+
current === translatedValue ? "already_applied" : "target_changed"
|
|
31457
|
+
);
|
|
31796
31458
|
continue;
|
|
31797
31459
|
}
|
|
31798
|
-
const translatedValue = task.dst ?? "";
|
|
31799
31460
|
data = setByPath(data, task.key, translatedValue);
|
|
31800
31461
|
written.entries.push({
|
|
31801
31462
|
key: task.key,
|
|
31802
31463
|
namespace: task.namespace,
|
|
31803
31464
|
baseValue: task.src,
|
|
31804
|
-
translatedValue
|
|
31465
|
+
translatedValue,
|
|
31466
|
+
changesFile: current !== translatedValue
|
|
31805
31467
|
});
|
|
31806
31468
|
}
|
|
31807
31469
|
if (written.entries.length === 0) continue;
|
|
31470
|
+
result.written.push(written);
|
|
31471
|
+
if (input2.dryRun) continue;
|
|
31808
31472
|
await writeTranslationFile({
|
|
31809
31473
|
format,
|
|
31810
31474
|
targetPath,
|
|
@@ -31813,16 +31477,15 @@ async function applyTasks(input2) {
|
|
|
31813
31477
|
existingContent: existing?.content,
|
|
31814
31478
|
sourceContent: source.content
|
|
31815
31479
|
});
|
|
31816
|
-
result.written.push(written);
|
|
31817
31480
|
}
|
|
31818
31481
|
return result;
|
|
31819
31482
|
}
|
|
31820
31483
|
async function writeTranslationFile(input2) {
|
|
31821
31484
|
const { targetPath, data } = input2;
|
|
31822
31485
|
const model = input2.existingContent ?? input2.sourceContent;
|
|
31823
|
-
await
|
|
31486
|
+
await fs9.mkdir(path9.dirname(targetPath), { recursive: true });
|
|
31824
31487
|
if (input2.format === "json") {
|
|
31825
|
-
await
|
|
31488
|
+
await fs9.writeFile(
|
|
31826
31489
|
targetPath,
|
|
31827
31490
|
serializeJsonContent(data, detectJsonStyle(model)),
|
|
31828
31491
|
"utf-8"
|
|
@@ -31837,63 +31500,18 @@ async function writeTranslationFile(input2) {
|
|
|
31837
31500
|
exportName: input2.existingContent ? shape?.exportName ?? void 0 : localeExportIdentifier(input2.dstLocale),
|
|
31838
31501
|
useDefaultExport: shape?.useDefaultExport ?? false
|
|
31839
31502
|
});
|
|
31840
|
-
await
|
|
31503
|
+
await fs9.writeFile(targetPath, content, "utf-8");
|
|
31841
31504
|
}
|
|
31842
31505
|
|
|
31843
31506
|
// src/commands/translate/apply.tsx
|
|
31844
31507
|
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
31845
|
-
function ApplyUI({
|
|
31846
|
-
jobs,
|
|
31847
|
-
state,
|
|
31848
|
-
onSubmit
|
|
31849
|
-
}) {
|
|
31850
|
-
const [selectedValues, setSelectedValues] = useState2([]);
|
|
31851
|
-
if (state.status === "selecting") {
|
|
31852
|
-
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
|
|
31853
|
-
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "Select translation jobs to apply:" }),
|
|
31854
|
-
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Space to select, Enter to confirm, Ctrl+C to cancel" }),
|
|
31855
|
-
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(
|
|
31856
|
-
MultiSelect,
|
|
31857
|
-
{
|
|
31858
|
-
options: jobs,
|
|
31859
|
-
onChange: setSelectedValues,
|
|
31860
|
-
onSubmit: () => onSubmit(selectedValues)
|
|
31861
|
-
}
|
|
31862
|
-
) })
|
|
31863
|
-
] });
|
|
31864
|
-
}
|
|
31865
|
-
if (state.status === "applying") {
|
|
31866
|
-
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
|
|
31867
|
-
/* @__PURE__ */ jsx16(Spinner14, {}),
|
|
31868
|
-
/* @__PURE__ */ jsxs16(Text16, { children: [
|
|
31869
|
-
"Applying job ",
|
|
31870
|
-
state.currentJobIndex,
|
|
31871
|
-
"/",
|
|
31872
|
-
state.totalJobs
|
|
31873
|
-
] }),
|
|
31874
|
-
state.currentJob && /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: state.currentJob })
|
|
31875
|
-
] });
|
|
31876
|
-
}
|
|
31877
|
-
if (state.status === "success") {
|
|
31878
|
-
return /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsxs16(Text16, { color: "green", children: [
|
|
31879
|
-
"\u2713 Applied ",
|
|
31880
|
-
state.keys,
|
|
31881
|
-
" keys to ",
|
|
31882
|
-
state.files,
|
|
31883
|
-
" files from",
|
|
31884
|
-
" ",
|
|
31885
|
-
state.totalJobs,
|
|
31886
|
-
" job(s)",
|
|
31887
|
-
state.refused ? `, ${state.refused} not applied` : ""
|
|
31888
|
-
] }) });
|
|
31889
|
-
}
|
|
31890
|
-
return /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
|
|
31891
|
-
"\u2717 Error: ",
|
|
31892
|
-
state.message
|
|
31893
|
-
] }) });
|
|
31894
|
-
}
|
|
31895
31508
|
var STAMP_CHUNK2 = 200;
|
|
31896
|
-
|
|
31509
|
+
var RECENT_JOBS = 50;
|
|
31510
|
+
var APPLICABLE_STATUSES = /* @__PURE__ */ new Set(["succeeded", "partial", "failed"]);
|
|
31511
|
+
function fullKey3(namespace, key) {
|
|
31512
|
+
return namespace ? `${namespace}.${key}` : key;
|
|
31513
|
+
}
|
|
31514
|
+
async function readJob(ctx, jobId) {
|
|
31897
31515
|
const { client: client3, project } = ctx;
|
|
31898
31516
|
const organizationId2 = project.organizationId;
|
|
31899
31517
|
const job = await client3.query(api.translations.getJobById, {
|
|
@@ -31901,17 +31519,28 @@ async function applyJob(ctx, jobId) {
|
|
|
31901
31519
|
jobId
|
|
31902
31520
|
});
|
|
31903
31521
|
if (!job) {
|
|
31904
|
-
throw new
|
|
31522
|
+
throw new CliError(
|
|
31523
|
+
`Translation job ${jobId} was not found in this project.`,
|
|
31524
|
+
void 0,
|
|
31525
|
+
"job_not_found"
|
|
31526
|
+
);
|
|
31905
31527
|
}
|
|
31906
|
-
if (
|
|
31907
|
-
throw new
|
|
31908
|
-
`
|
|
31528
|
+
if (!APPLICABLE_STATUSES.has(job.status)) {
|
|
31529
|
+
throw new CliError(
|
|
31530
|
+
`Translation job ${jobId} is ${job.status}. Only a finished job can be applied.`,
|
|
31531
|
+
`Check it with 'tolkbot translate status --job-id ${jobId}'.`,
|
|
31532
|
+
"job_not_finished"
|
|
31909
31533
|
);
|
|
31910
31534
|
}
|
|
31911
31535
|
const tasks = await client3.query(api.translations.listTasksByJobId, {
|
|
31912
31536
|
organizationId: organizationId2,
|
|
31913
31537
|
jobId
|
|
31914
31538
|
});
|
|
31539
|
+
return { job, tasks };
|
|
31540
|
+
}
|
|
31541
|
+
async function applyJob(ctx, jobId) {
|
|
31542
|
+
const { client: client3, project } = ctx;
|
|
31543
|
+
const { job, tasks } = await readJob(ctx, jobId);
|
|
31915
31544
|
const result = await applyTasks({
|
|
31916
31545
|
root: ctx.root,
|
|
31917
31546
|
scope: scopeOf(project),
|
|
@@ -31921,24 +31550,36 @@ async function applyJob(ctx, jobId) {
|
|
|
31921
31550
|
});
|
|
31922
31551
|
const locale = governanceLocale(project, job.dstLocale);
|
|
31923
31552
|
for (const file2 of result.written) {
|
|
31924
|
-
|
|
31553
|
+
const entries = file2.entries.map(
|
|
31554
|
+
({ key, namespace, baseValue, translatedValue }) => ({
|
|
31555
|
+
key,
|
|
31556
|
+
namespace,
|
|
31557
|
+
baseValue,
|
|
31558
|
+
translatedValue
|
|
31559
|
+
})
|
|
31560
|
+
);
|
|
31561
|
+
for (let i = 0; i < entries.length; i += STAMP_CHUNK2) {
|
|
31925
31562
|
await client3.mutation(api.localeKeyStates.stampAppliedForCli, {
|
|
31926
|
-
organizationId:
|
|
31563
|
+
organizationId: project.organizationId,
|
|
31927
31564
|
projectId: project._id,
|
|
31928
31565
|
locale,
|
|
31929
31566
|
pathTemplate: file2.pathTemplate,
|
|
31930
|
-
entries:
|
|
31567
|
+
entries: entries.slice(i, i + STAMP_CHUNK2)
|
|
31931
31568
|
});
|
|
31932
31569
|
}
|
|
31933
31570
|
}
|
|
31934
31571
|
return {
|
|
31572
|
+
dstLocale: job.dstLocale,
|
|
31935
31573
|
files: result.written.length,
|
|
31936
31574
|
keys: result.written.reduce((n, f) => n + f.entries.length, 0),
|
|
31937
|
-
refused: result.refused.filter(
|
|
31575
|
+
refused: result.refused.filter(
|
|
31576
|
+
(r) => r.reason !== "not_translated" && r.reason !== "already_applied"
|
|
31577
|
+
)
|
|
31938
31578
|
};
|
|
31939
31579
|
}
|
|
31940
31580
|
var REFUSAL_TEXT = {
|
|
31941
31581
|
not_translated: "the task did not finish",
|
|
31582
|
+
already_applied: "the file already holds it",
|
|
31942
31583
|
no_source_file: "its source file is gone or unreadable",
|
|
31943
31584
|
source_changed: "the source text changed after it was planned",
|
|
31944
31585
|
target_unreadable: "the target file cannot be read",
|
|
@@ -31949,296 +31590,251 @@ function describeRefusals(refused) {
|
|
|
31949
31590
|
(r) => ` ${r.key}: not applied, ${REFUSAL_TEXT[r.reason]}`
|
|
31950
31591
|
);
|
|
31951
31592
|
}
|
|
31952
|
-
async function
|
|
31953
|
-
const
|
|
31954
|
-
const
|
|
31955
|
-
|
|
31956
|
-
|
|
31957
|
-
|
|
31958
|
-
|
|
31959
|
-
|
|
31960
|
-
|
|
31593
|
+
async function findUnappliedJobs(ctx) {
|
|
31594
|
+
const { client: client3, project } = ctx;
|
|
31595
|
+
const jobs = await client3.query(api.translations.listJobsByProject, {
|
|
31596
|
+
organizationId: project.organizationId,
|
|
31597
|
+
projectId: project._id,
|
|
31598
|
+
limit: RECENT_JOBS
|
|
31599
|
+
});
|
|
31600
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
31601
|
+
const plannedKeys = (srcLocale, dstLocale) => {
|
|
31602
|
+
const id = `${srcLocale}\0${dstLocale}`;
|
|
31603
|
+
let keys = wanted.get(id);
|
|
31604
|
+
if (!keys) {
|
|
31605
|
+
keys = planForLocale(ctx, srcLocale, dstLocale).then(
|
|
31606
|
+
(plan) => new Set(
|
|
31607
|
+
plan.tasks.map(
|
|
31608
|
+
(task) => fullKey3(task.namespace, task.key)
|
|
31609
|
+
)
|
|
31610
|
+
)
|
|
31961
31611
|
);
|
|
31612
|
+
wanted.set(id, keys);
|
|
31962
31613
|
}
|
|
31963
|
-
|
|
31964
|
-
|
|
31965
|
-
|
|
31966
|
-
|
|
31967
|
-
|
|
31968
|
-
|
|
31969
|
-
|
|
31970
|
-
|
|
31971
|
-
|
|
31972
|
-
|
|
31973
|
-
|
|
31974
|
-
|
|
31975
|
-
|
|
31976
|
-
|
|
31977
|
-
|
|
31978
|
-
|
|
31979
|
-
|
|
31980
|
-
|
|
31981
|
-
|
|
31982
|
-
|
|
31983
|
-
|
|
31984
|
-
|
|
31985
|
-
|
|
31986
|
-
|
|
31987
|
-
|
|
31988
|
-
|
|
31989
|
-
|
|
31990
|
-
|
|
31991
|
-
|
|
31992
|
-
|
|
31993
|
-
|
|
31994
|
-
|
|
31995
|
-
|
|
31996
|
-
|
|
31997
|
-
key_count: totalKeys,
|
|
31998
|
-
locale_count: opts.jobId.length,
|
|
31999
|
-
job_count: opts.jobId.length,
|
|
32000
|
-
json: false
|
|
31614
|
+
return keys;
|
|
31615
|
+
};
|
|
31616
|
+
const unapplied = [];
|
|
31617
|
+
for (const job of jobs) {
|
|
31618
|
+
if (!APPLICABLE_STATUSES.has(job.status)) continue;
|
|
31619
|
+
const tasks = await client3.query(api.translations.listTasksByJobId, {
|
|
31620
|
+
organizationId: project.organizationId,
|
|
31621
|
+
jobId: job._id
|
|
31622
|
+
});
|
|
31623
|
+
const preview2 = await applyTasks({
|
|
31624
|
+
root: ctx.root,
|
|
31625
|
+
scope: scopeOf(project),
|
|
31626
|
+
srcLocale: job.srcLocale,
|
|
31627
|
+
dstLocale: job.dstLocale,
|
|
31628
|
+
tasks,
|
|
31629
|
+
dryRun: true
|
|
31630
|
+
});
|
|
31631
|
+
let pending2 = 0;
|
|
31632
|
+
for (const file2 of preview2.written) {
|
|
31633
|
+
for (const entry of file2.entries) {
|
|
31634
|
+
if (entry.changesFile || (await plannedKeys(job.srcLocale, job.dstLocale)).has(
|
|
31635
|
+
fullKey3(entry.namespace, entry.key)
|
|
31636
|
+
)) {
|
|
31637
|
+
pending2++;
|
|
31638
|
+
}
|
|
31639
|
+
}
|
|
31640
|
+
}
|
|
31641
|
+
if (pending2 === 0) continue;
|
|
31642
|
+
unapplied.push({
|
|
31643
|
+
jobId: job._id,
|
|
31644
|
+
srcLocale: job.srcLocale,
|
|
31645
|
+
dstLocale: job.dstLocale,
|
|
31646
|
+
createdAt: job._creationTime,
|
|
31647
|
+
pending: pending2
|
|
32001
31648
|
});
|
|
32002
|
-
console.log(
|
|
32003
|
-
`\u2713 Applied ${totalKeys} keys to ${totalFiles} files from ${opts.jobId.length} job(s)` + (totalRefused > 0 ? `, ${totalRefused} not applied` : "")
|
|
32004
|
-
);
|
|
32005
|
-
return;
|
|
32006
31649
|
}
|
|
32007
|
-
|
|
32008
|
-
|
|
32009
|
-
|
|
32010
|
-
|
|
32011
|
-
|
|
32012
|
-
|
|
32013
|
-
|
|
32014
|
-
|
|
32015
|
-
|
|
31650
|
+
return unapplied;
|
|
31651
|
+
}
|
|
31652
|
+
function chooseJobs(unapplied) {
|
|
31653
|
+
if (unapplied.length === 0) return { kind: "none" };
|
|
31654
|
+
const byLocale = /* @__PURE__ */ new Map();
|
|
31655
|
+
for (const job of unapplied) {
|
|
31656
|
+
const group = byLocale.get(job.dstLocale) ?? [];
|
|
31657
|
+
group.push(job);
|
|
31658
|
+
byLocale.set(job.dstLocale, group);
|
|
32016
31659
|
}
|
|
32017
|
-
const
|
|
32018
|
-
|
|
31660
|
+
const locales = [...byLocale].filter(([, group]) => group.length > 1).map(([locale]) => locale);
|
|
31661
|
+
if (locales.length > 0) return { kind: "ambiguous", locales };
|
|
31662
|
+
return {
|
|
31663
|
+
kind: "apply",
|
|
31664
|
+
jobs: [...unapplied].sort((a, b) => a.createdAt - b.createdAt)
|
|
31665
|
+
};
|
|
31666
|
+
}
|
|
31667
|
+
function describeJob(job) {
|
|
31668
|
+
return `${job.jobId} (${job.srcLocale} \u2192 ${job.dstLocale}, ${job.pending} key(s), ${new Date(job.createdAt).toLocaleString()})`;
|
|
31669
|
+
}
|
|
31670
|
+
function nothingToApply() {
|
|
31671
|
+
return new CliError(
|
|
31672
|
+
"No finished translation job is waiting to be applied.",
|
|
31673
|
+
"Run 'tolkbot translate run --all' to translate what is missing, or pass --job-id to apply a job again.",
|
|
31674
|
+
"nothing_to_apply"
|
|
32019
31675
|
);
|
|
32020
|
-
|
|
32021
|
-
|
|
32022
|
-
|
|
32023
|
-
|
|
32024
|
-
|
|
32025
|
-
|
|
32026
|
-
|
|
32027
|
-
|
|
32028
|
-
|
|
32029
|
-
|
|
32030
|
-
|
|
32031
|
-
|
|
32032
|
-
|
|
32033
|
-
|
|
32034
|
-
|
|
32035
|
-
const
|
|
31676
|
+
}
|
|
31677
|
+
function ambiguousJobs(unapplied, locales) {
|
|
31678
|
+
const waiting = unapplied.filter((job) => locales.includes(job.dstLocale));
|
|
31679
|
+
const newest = locales.map(
|
|
31680
|
+
(locale) => waiting.filter((job) => job.dstLocale === locale).reduce((a, b) => b.createdAt > a.createdAt ? b : a)
|
|
31681
|
+
);
|
|
31682
|
+
return new UsageError(
|
|
31683
|
+
`More than one translation job is waiting to be applied for ${locales.join(", ")}: ${waiting.map(describeJob).join("; ")}.`,
|
|
31684
|
+
`Pass --job-id with the job(s) to apply. The newest is usually the one: --job-id ${newest.map((job) => job.jobId).join(" ")}.`
|
|
31685
|
+
);
|
|
31686
|
+
}
|
|
31687
|
+
function JobPicker({
|
|
31688
|
+
jobs,
|
|
31689
|
+
onSubmit
|
|
31690
|
+
}) {
|
|
31691
|
+
const [selected, setSelected] = useState2([]);
|
|
31692
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
|
|
31693
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "Select translation jobs to apply:" }),
|
|
31694
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Space to select, Enter to confirm, Ctrl+C to cancel" }),
|
|
31695
|
+
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(
|
|
31696
|
+
MultiSelect,
|
|
31697
|
+
{
|
|
31698
|
+
options: jobs.map((job) => ({
|
|
31699
|
+
label: describeJob(job),
|
|
31700
|
+
value: job.jobId
|
|
31701
|
+
})),
|
|
31702
|
+
onChange: setSelected,
|
|
31703
|
+
onSubmit: () => onSubmit(selected)
|
|
31704
|
+
}
|
|
31705
|
+
) })
|
|
31706
|
+
] });
|
|
31707
|
+
}
|
|
31708
|
+
async function pickJobs(jobs) {
|
|
31709
|
+
let chosen = [];
|
|
31710
|
+
const ui = render16(
|
|
32036
31711
|
/* @__PURE__ */ jsx16(
|
|
32037
|
-
|
|
31712
|
+
JobPicker,
|
|
32038
31713
|
{
|
|
32039
|
-
jobs
|
|
32040
|
-
state,
|
|
31714
|
+
jobs,
|
|
32041
31715
|
onSubmit: (selected) => {
|
|
32042
|
-
|
|
31716
|
+
chosen = selected;
|
|
31717
|
+
ui.unmount();
|
|
32043
31718
|
}
|
|
32044
31719
|
}
|
|
32045
31720
|
)
|
|
32046
31721
|
);
|
|
32047
|
-
|
|
32048
|
-
|
|
32049
|
-
|
|
32050
|
-
|
|
32051
|
-
|
|
32052
|
-
|
|
32053
|
-
|
|
32054
|
-
|
|
32055
|
-
|
|
32056
|
-
|
|
32057
|
-
|
|
32058
|
-
|
|
32059
|
-
|
|
31722
|
+
await ui.waitUntilExit();
|
|
31723
|
+
return chosen;
|
|
31724
|
+
}
|
|
31725
|
+
async function resolveJobIds(ctx, json2) {
|
|
31726
|
+
const unapplied = await findUnappliedJobs(ctx);
|
|
31727
|
+
const choice = chooseJobs(unapplied);
|
|
31728
|
+
if (choice.kind === "none") throw nothingToApply();
|
|
31729
|
+
if (choice.kind === "apply") return choice.jobs.map((job) => job.jobId);
|
|
31730
|
+
if (json2 || !isInteractive()) {
|
|
31731
|
+
throw ambiguousJobs(unapplied, choice.locales);
|
|
31732
|
+
}
|
|
31733
|
+
const picked = await pickJobs(unapplied);
|
|
31734
|
+
if (picked.length === 0) {
|
|
31735
|
+
throw new UsageError(
|
|
31736
|
+
"No job selected.",
|
|
31737
|
+
"Pass --job-id with the job(s) to apply."
|
|
32060
31738
|
);
|
|
32061
|
-
};
|
|
32062
|
-
await new Promise((resolve) => {
|
|
32063
|
-
const checkInterval = setInterval(() => {
|
|
32064
|
-
if (selectedJobIds.length > 0 && state.status === "selecting") {
|
|
32065
|
-
clearInterval(checkInterval);
|
|
32066
|
-
resolve();
|
|
32067
|
-
}
|
|
32068
|
-
}, 100);
|
|
32069
|
-
});
|
|
32070
|
-
if (selectedJobIds.length === 0) {
|
|
32071
|
-
unmount();
|
|
32072
|
-
console.log("No jobs selected.");
|
|
32073
|
-
return;
|
|
32074
31739
|
}
|
|
32075
|
-
|
|
32076
|
-
|
|
32077
|
-
|
|
32078
|
-
|
|
32079
|
-
|
|
32080
|
-
|
|
32081
|
-
|
|
32082
|
-
|
|
32083
|
-
const
|
|
32084
|
-
|
|
32085
|
-
|
|
32086
|
-
if (!jobId) continue;
|
|
32087
|
-
updateState({
|
|
32088
|
-
currentJob: jobId,
|
|
32089
|
-
currentJobIndex: i + 1
|
|
32090
|
-
});
|
|
32091
|
-
const { files, keys, refused } = await applyJob(ctx, jobId);
|
|
32092
|
-
totalFiles += files;
|
|
32093
|
-
totalKeys += keys;
|
|
32094
|
-
totalRefused += refused.length;
|
|
32095
|
-
refusals.push(...describeRefusals(refused));
|
|
32096
|
-
}
|
|
32097
|
-
updateState({
|
|
32098
|
-
status: "success",
|
|
32099
|
-
files: totalFiles,
|
|
32100
|
-
keys: totalKeys,
|
|
32101
|
-
refused: totalRefused
|
|
32102
|
-
});
|
|
31740
|
+
return picked;
|
|
31741
|
+
}
|
|
31742
|
+
async function runApply(opts) {
|
|
31743
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
31744
|
+
const json2 = opts.json ?? false;
|
|
31745
|
+
const jobIds = opts.jobId && opts.jobId.length > 0 ? opts.jobId : await resolveJobIds(ctx, json2);
|
|
31746
|
+
if (json2) {
|
|
31747
|
+
const applied = [];
|
|
31748
|
+
for (const jobId of jobIds) {
|
|
31749
|
+
applied.push({ jobId, ...await applyJob(ctx, jobId) });
|
|
31750
|
+
}
|
|
32103
31751
|
track("cli_translate_completed", {
|
|
32104
31752
|
mode: "apply",
|
|
32105
|
-
key_count:
|
|
32106
|
-
locale_count:
|
|
32107
|
-
job_count:
|
|
32108
|
-
json:
|
|
32109
|
-
});
|
|
32110
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
32111
|
-
unmount();
|
|
32112
|
-
for (const line of refusals) console.log(line);
|
|
32113
|
-
} catch (err) {
|
|
32114
|
-
updateState({
|
|
32115
|
-
status: "error",
|
|
32116
|
-
message: err instanceof Error ? err.message : String(err)
|
|
31753
|
+
key_count: applied.reduce((n, a) => n + a.keys, 0),
|
|
31754
|
+
locale_count: new Set(applied.map((a) => a.dstLocale)).size,
|
|
31755
|
+
job_count: applied.length,
|
|
31756
|
+
json: true
|
|
32117
31757
|
});
|
|
32118
|
-
|
|
32119
|
-
|
|
32120
|
-
|
|
32121
|
-
|
|
31758
|
+
jsonSuccess({ applied });
|
|
31759
|
+
}
|
|
31760
|
+
console.log(`Applying ${jobIds.length} job(s)...`);
|
|
31761
|
+
let totalFiles = 0;
|
|
31762
|
+
let totalKeys = 0;
|
|
31763
|
+
let totalRefused = 0;
|
|
31764
|
+
const locales = /* @__PURE__ */ new Set();
|
|
31765
|
+
for (const [i, jobId] of jobIds.entries()) {
|
|
31766
|
+
console.log(` [${i + 1}/${jobIds.length}] ${jobId}`);
|
|
31767
|
+
const { dstLocale, files, keys, refused } = await applyJob(ctx, jobId);
|
|
31768
|
+
locales.add(dstLocale);
|
|
31769
|
+
totalFiles += files;
|
|
31770
|
+
totalKeys += keys;
|
|
31771
|
+
totalRefused += refused.length;
|
|
31772
|
+
for (const line of describeRefusals(refused)) console.log(line);
|
|
31773
|
+
}
|
|
31774
|
+
track("cli_translate_completed", {
|
|
31775
|
+
mode: "apply",
|
|
31776
|
+
key_count: totalKeys,
|
|
31777
|
+
locale_count: locales.size,
|
|
31778
|
+
job_count: jobIds.length,
|
|
31779
|
+
json: false
|
|
31780
|
+
});
|
|
31781
|
+
console.log(
|
|
31782
|
+
`\u2713 Applied ${totalKeys} keys to ${totalFiles} files from ${jobIds.length} job(s)` + (totalRefused > 0 ? `, ${totalRefused} not applied` : "")
|
|
31783
|
+
);
|
|
32122
31784
|
}
|
|
32123
31785
|
|
|
32124
31786
|
// src/commands/translate/cancel.tsx
|
|
32125
|
-
import { Spinner as
|
|
32126
|
-
import { Box as Box17, render as
|
|
31787
|
+
import { Spinner as Spinner14 } from "@inkjs/ui";
|
|
31788
|
+
import { Box as Box17, render as render17, Text as Text17 } from "ink";
|
|
32127
31789
|
import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
32128
31790
|
function CancelUI({ state }) {
|
|
32129
31791
|
if (state.type === "cancelling") {
|
|
32130
31792
|
return /* @__PURE__ */ jsxs17(Box17, { children: [
|
|
32131
|
-
/* @__PURE__ */ jsx17(
|
|
31793
|
+
/* @__PURE__ */ jsx17(Spinner14, {}),
|
|
32132
31794
|
/* @__PURE__ */ jsx17(Text17, { children: " Cancelling job\u2026" })
|
|
32133
31795
|
] });
|
|
32134
31796
|
}
|
|
32135
|
-
if (state.type === "success") {
|
|
32136
|
-
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
32137
|
-
/* @__PURE__ */ jsxs17(Box17, { children: [
|
|
32138
|
-
/* @__PURE__ */ jsx17(Text17, { color: "green", children: "\u2713" }),
|
|
32139
|
-
/* @__PURE__ */ jsx17(Text17, { children: " Job cancelled successfully" })
|
|
32140
|
-
] }),
|
|
32141
|
-
/* @__PURE__ */ jsx17(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
32142
|
-
"Job ID: ",
|
|
32143
|
-
state.jobId
|
|
32144
|
-
] }) })
|
|
32145
|
-
] });
|
|
32146
|
-
}
|
|
32147
31797
|
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
32148
|
-
/* @__PURE__ */
|
|
32149
|
-
|
|
31798
|
+
/* @__PURE__ */ jsxs17(Box17, { children: [
|
|
31799
|
+
/* @__PURE__ */ jsx17(Text17, { color: "green", children: "\u2713" }),
|
|
31800
|
+
/* @__PURE__ */ jsx17(Text17, { children: " Job cancelled successfully" })
|
|
31801
|
+
] }),
|
|
31802
|
+
/* @__PURE__ */ jsx17(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
31803
|
+
"Job ID: ",
|
|
31804
|
+
state.jobId
|
|
31805
|
+
] }) })
|
|
32150
31806
|
] });
|
|
32151
31807
|
}
|
|
31808
|
+
async function cancelJob(jobId) {
|
|
31809
|
+
const { project } = await loadPlanContext(process.cwd());
|
|
31810
|
+
const client3 = await getAuthenticatedConvexClient();
|
|
31811
|
+
await client3.action(api.translations.cancelJob, {
|
|
31812
|
+
organizationId: project.organizationId,
|
|
31813
|
+
jobId
|
|
31814
|
+
});
|
|
31815
|
+
}
|
|
32152
31816
|
async function runCancel(opts) {
|
|
32153
31817
|
if (opts.json) {
|
|
32154
|
-
|
|
32155
|
-
|
|
32156
|
-
jsonError("--job-id is required");
|
|
32157
|
-
}
|
|
32158
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
32159
|
-
const baseDir = process.cwd();
|
|
32160
|
-
const config3 = readConfig(baseDir);
|
|
32161
|
-
if (!config3) {
|
|
32162
|
-
jsonError(
|
|
32163
|
-
"No tolkbot.json found. Run 'tolkbot init' to create one."
|
|
32164
|
-
);
|
|
32165
|
-
}
|
|
32166
|
-
const project = await convexClient.query(
|
|
32167
|
-
api.projects.getProjectForCli,
|
|
32168
|
-
{ projectId: config3.config.projectId }
|
|
32169
|
-
);
|
|
32170
|
-
if (!project) {
|
|
32171
|
-
jsonError(
|
|
32172
|
-
"Project not found. Please run 'tolkbot init' again."
|
|
32173
|
-
);
|
|
32174
|
-
}
|
|
32175
|
-
setTelemetryOrganization(project.organizationId);
|
|
32176
|
-
await convexClient.action(api.translations.cancelJob, {
|
|
32177
|
-
organizationId: project.organizationId,
|
|
32178
|
-
jobId: opts.jobId
|
|
32179
|
-
});
|
|
32180
|
-
jsonSuccess({ jobId: opts.jobId, ok: true });
|
|
32181
|
-
} catch (error62) {
|
|
32182
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
32183
|
-
}
|
|
31818
|
+
await cancelJob(opts.jobId);
|
|
31819
|
+
jsonSuccess({ jobId: opts.jobId, ok: true });
|
|
32184
31820
|
}
|
|
32185
|
-
|
|
31821
|
+
const { unmount } = renderTransient(
|
|
31822
|
+
/* @__PURE__ */ jsx17(CancelUI, { state: { type: "cancelling" } })
|
|
31823
|
+
);
|
|
32186
31824
|
try {
|
|
32187
|
-
|
|
32188
|
-
|
|
32189
|
-
}
|
|
32190
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
32191
|
-
const baseDir = process.cwd();
|
|
32192
|
-
const config3 = readConfig(baseDir);
|
|
32193
|
-
if (!config3) {
|
|
32194
|
-
throw new Error(
|
|
32195
|
-
"No tolkbot.json found. Run 'tolkbot init' to create one."
|
|
32196
|
-
);
|
|
32197
|
-
}
|
|
32198
|
-
const project = await convexClient.query(
|
|
32199
|
-
api.projects.getProjectForCli,
|
|
32200
|
-
{
|
|
32201
|
-
projectId: config3.config.projectId
|
|
32202
|
-
}
|
|
32203
|
-
);
|
|
32204
|
-
if (!project) {
|
|
32205
|
-
throw new Error(
|
|
32206
|
-
"Project not found. Please run 'tolkbot init' again."
|
|
32207
|
-
);
|
|
32208
|
-
}
|
|
32209
|
-
setTelemetryOrganization(project.organizationId);
|
|
32210
|
-
await convexClient.action(api.translations.cancelJob, {
|
|
32211
|
-
organizationId: project.organizationId,
|
|
32212
|
-
jobId: opts.jobId
|
|
32213
|
-
});
|
|
32214
|
-
unmount();
|
|
32215
|
-
const successUi = render16(
|
|
32216
|
-
/* @__PURE__ */ jsx17(CancelUI, { state: { type: "success", jobId: opts.jobId } })
|
|
32217
|
-
);
|
|
32218
|
-
unmount = successUi.unmount;
|
|
32219
|
-
await successUi.waitUntilExit();
|
|
32220
|
-
} catch (error62) {
|
|
31825
|
+
await cancelJob(opts.jobId);
|
|
31826
|
+
} finally {
|
|
32221
31827
|
unmount();
|
|
32222
|
-
const errorUi = render16(
|
|
32223
|
-
/* @__PURE__ */ jsx17(
|
|
32224
|
-
CancelUI,
|
|
32225
|
-
{
|
|
32226
|
-
state: {
|
|
32227
|
-
type: "error",
|
|
32228
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
32229
|
-
}
|
|
32230
|
-
}
|
|
32231
|
-
)
|
|
32232
|
-
);
|
|
32233
|
-
await errorUi.waitUntilExit();
|
|
32234
|
-
return exitAfterFlush(1);
|
|
32235
31828
|
}
|
|
31829
|
+
render17(
|
|
31830
|
+
/* @__PURE__ */ jsx17(CancelUI, { state: { type: "success", jobId: opts.jobId } })
|
|
31831
|
+
).unmount();
|
|
32236
31832
|
}
|
|
32237
31833
|
|
|
32238
31834
|
// src/commands/translate/edit.tsx
|
|
32239
31835
|
import path11 from "path";
|
|
32240
|
-
import { Spinner as
|
|
32241
|
-
import { Box as Box18, render as
|
|
31836
|
+
import { Spinner as Spinner15 } from "@inkjs/ui";
|
|
31837
|
+
import { Box as Box18, render as render18, Text as Text18 } from "ink";
|
|
32242
31838
|
|
|
32243
31839
|
// src/utils/locale-files.ts
|
|
32244
31840
|
import path10 from "path";
|
|
@@ -32280,51 +31876,44 @@ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
|
32280
31876
|
function EditUI({ state }) {
|
|
32281
31877
|
if (state.type === "editing") {
|
|
32282
31878
|
return /* @__PURE__ */ jsxs18(Box18, { children: [
|
|
32283
|
-
/* @__PURE__ */ jsx18(
|
|
31879
|
+
/* @__PURE__ */ jsx18(Spinner15, {}),
|
|
32284
31880
|
/* @__PURE__ */ jsx18(Text18, { children: " Editing translation\u2026" })
|
|
32285
31881
|
] });
|
|
32286
31882
|
}
|
|
32287
|
-
if (state.type === "success") {
|
|
32288
|
-
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
32289
|
-
/* @__PURE__ */ jsxs18(Box18, { children: [
|
|
32290
|
-
/* @__PURE__ */ jsx18(Text18, { color: "green", children: "\u2713" }),
|
|
32291
|
-
/* @__PURE__ */ jsxs18(Text18, { children: [
|
|
32292
|
-
" ",
|
|
32293
|
-
"Updated ",
|
|
32294
|
-
/* @__PURE__ */ jsx18(Text18, { bold: true, children: state.key })
|
|
32295
|
-
] })
|
|
32296
|
-
] }),
|
|
32297
|
-
/* @__PURE__ */ jsx18(Box18, { paddingLeft: 2, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: state.value }) }),
|
|
32298
|
-
/* @__PURE__ */ jsx18(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
32299
|
-
"File: ",
|
|
32300
|
-
state.filePath
|
|
32301
|
-
] }) })
|
|
32302
|
-
] });
|
|
32303
|
-
}
|
|
32304
31883
|
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
32305
|
-
/* @__PURE__ */
|
|
32306
|
-
|
|
31884
|
+
/* @__PURE__ */ jsxs18(Box18, { children: [
|
|
31885
|
+
/* @__PURE__ */ jsx18(Text18, { color: "green", children: "\u2713" }),
|
|
31886
|
+
/* @__PURE__ */ jsxs18(Text18, { children: [
|
|
31887
|
+
" ",
|
|
31888
|
+
"Updated ",
|
|
31889
|
+
/* @__PURE__ */ jsx18(Text18, { bold: true, children: state.key })
|
|
31890
|
+
] })
|
|
31891
|
+
] }),
|
|
31892
|
+
/* @__PURE__ */ jsx18(Box18, { paddingLeft: 2, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: state.value }) }),
|
|
31893
|
+
/* @__PURE__ */ jsx18(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
31894
|
+
"File: ",
|
|
31895
|
+
state.filePath
|
|
31896
|
+
] }) })
|
|
32307
31897
|
] });
|
|
32308
31898
|
}
|
|
32309
31899
|
async function performEdit(opts) {
|
|
32310
31900
|
const baseDir = process.cwd();
|
|
32311
31901
|
const config3 = readConfig(baseDir);
|
|
32312
|
-
if (!config3)
|
|
32313
|
-
throw new Error(
|
|
32314
|
-
"No tolkbot.json found. Run 'tolkbot init' to create one."
|
|
32315
|
-
);
|
|
32316
|
-
}
|
|
31902
|
+
if (!config3) throw noConfigError();
|
|
32317
31903
|
const localeFiles = await findLocaleFiles(baseDir);
|
|
32318
31904
|
const files = localeFiles.get(opts.locale);
|
|
32319
31905
|
if (!files || files.length === 0) {
|
|
32320
|
-
throw new
|
|
32321
|
-
`No translation files found for locale "${opts.locale}"
|
|
31906
|
+
throw new CliError(
|
|
31907
|
+
`No translation files found for locale "${opts.locale}".`,
|
|
31908
|
+
`Available locales: ${[...localeFiles.keys()].join(", ") || "none"}.`,
|
|
31909
|
+
"no_translation_files"
|
|
32322
31910
|
);
|
|
32323
31911
|
}
|
|
32324
31912
|
const targetFile = resolveFileForKey(files, opts.key, opts.namespace);
|
|
32325
31913
|
if (!targetFile) {
|
|
32326
|
-
throw new
|
|
32327
|
-
`Could not determine target file for key "${opts.key}" in locale "${opts.locale}"
|
|
31914
|
+
throw new CliError(
|
|
31915
|
+
`Could not determine target file for key "${opts.key}" in locale "${opts.locale}".`,
|
|
31916
|
+
"Pass --namespace to say which file the key belongs to."
|
|
32328
31917
|
);
|
|
32329
31918
|
}
|
|
32330
31919
|
const keyInFile = resolveKeyInFile(
|
|
@@ -32351,47 +31940,199 @@ async function performEdit(opts) {
|
|
|
32351
31940
|
}
|
|
32352
31941
|
async function runEdit(opts) {
|
|
32353
31942
|
if (opts.json) {
|
|
32354
|
-
|
|
32355
|
-
|
|
32356
|
-
|
|
32357
|
-
|
|
32358
|
-
|
|
32359
|
-
|
|
32360
|
-
});
|
|
32361
|
-
} catch (error62) {
|
|
32362
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
32363
|
-
}
|
|
31943
|
+
const result2 = await performEdit(opts);
|
|
31944
|
+
jsonSuccess({
|
|
31945
|
+
file: result2.filePath,
|
|
31946
|
+
key: result2.key,
|
|
31947
|
+
value: result2.value
|
|
31948
|
+
});
|
|
32364
31949
|
}
|
|
32365
|
-
|
|
31950
|
+
const { unmount } = renderTransient(/* @__PURE__ */ jsx18(EditUI, { state: { type: "editing" } }));
|
|
31951
|
+
let result;
|
|
32366
31952
|
try {
|
|
32367
|
-
|
|
32368
|
-
|
|
32369
|
-
const ui = render17(/* @__PURE__ */ jsx18(EditUI, { state: { type: "success", ...result } }));
|
|
32370
|
-
unmount = ui.unmount;
|
|
32371
|
-
await ui.waitUntilExit();
|
|
32372
|
-
} catch (error62) {
|
|
31953
|
+
result = await performEdit(opts);
|
|
31954
|
+
} finally {
|
|
32373
31955
|
unmount();
|
|
32374
|
-
const ui = render17(
|
|
32375
|
-
/* @__PURE__ */ jsx18(
|
|
32376
|
-
EditUI,
|
|
32377
|
-
{
|
|
32378
|
-
state: {
|
|
32379
|
-
type: "error",
|
|
32380
|
-
message: error62 instanceof Error ? error62.message : String(error62)
|
|
32381
|
-
}
|
|
32382
|
-
}
|
|
32383
|
-
)
|
|
32384
|
-
);
|
|
32385
|
-
await ui.waitUntilExit();
|
|
32386
|
-
return exitAfterFlush(1);
|
|
32387
31956
|
}
|
|
31957
|
+
render18(/* @__PURE__ */ jsx18(EditUI, { state: { type: "success", ...result } })).unmount();
|
|
32388
31958
|
}
|
|
32389
31959
|
|
|
32390
31960
|
// src/commands/translate/run.tsx
|
|
32391
|
-
import { render as
|
|
31961
|
+
import { render as render19 } from "ink";
|
|
31962
|
+
|
|
31963
|
+
// ../shared/src/conditions/index.ts
|
|
31964
|
+
var BILLING_BLOCKED = "BILLING_BLOCKED";
|
|
31965
|
+
|
|
31966
|
+
// ../shared/src/projects/localeSetup.ts
|
|
31967
|
+
var LOCALE_SETUP_STALE_MS = 15 * 60 * 1e3;
|
|
31968
|
+
|
|
31969
|
+
// ../shared/src/projects/index.ts
|
|
31970
|
+
var PROJECT_ARCHIVED = "PROJECT_ARCHIVED";
|
|
31971
|
+
|
|
31972
|
+
// src/utils/error-codes.ts
|
|
31973
|
+
var EXIT_CODES = {
|
|
31974
|
+
success: 0,
|
|
31975
|
+
failure: 1,
|
|
31976
|
+
usage: 2,
|
|
31977
|
+
auth: 3,
|
|
31978
|
+
billing: 4,
|
|
31979
|
+
network: 5
|
|
31980
|
+
};
|
|
31981
|
+
var ERROR_CODES = {
|
|
31982
|
+
// 1: the command ran and failed
|
|
31983
|
+
failed: EXIT_CODES.failure,
|
|
31984
|
+
internal: EXIT_CODES.failure,
|
|
31985
|
+
no_config: EXIT_CODES.failure,
|
|
31986
|
+
project_not_found: EXIT_CODES.failure,
|
|
31987
|
+
repository_not_connected: EXIT_CODES.failure,
|
|
31988
|
+
no_translation_files: EXIT_CODES.failure,
|
|
31989
|
+
no_target_languages: EXIT_CODES.failure,
|
|
31990
|
+
not_found: EXIT_CODES.failure,
|
|
31991
|
+
job_not_found: EXIT_CODES.failure,
|
|
31992
|
+
job_not_finished: EXIT_CODES.failure,
|
|
31993
|
+
job_failed: EXIT_CODES.failure,
|
|
31994
|
+
job_cancelled: EXIT_CODES.failure,
|
|
31995
|
+
job_timed_out: EXIT_CODES.failure,
|
|
31996
|
+
nothing_to_apply: EXIT_CODES.failure,
|
|
31997
|
+
validation_failed: EXIT_CODES.failure,
|
|
31998
|
+
login_failed: EXIT_CODES.failure,
|
|
31999
|
+
// 2: usage
|
|
32000
|
+
usage: EXIT_CODES.usage,
|
|
32001
|
+
// 3: not authenticated, or not allowed
|
|
32002
|
+
not_authenticated: EXIT_CODES.auth,
|
|
32003
|
+
token_expired: EXIT_CODES.auth,
|
|
32004
|
+
token_rejected: EXIT_CODES.auth,
|
|
32005
|
+
signed_out: EXIT_CODES.auth,
|
|
32006
|
+
project_archived: EXIT_CODES.auth,
|
|
32007
|
+
// 4: billing
|
|
32008
|
+
no_subscription: EXIT_CODES.billing,
|
|
32009
|
+
out_of_credits: EXIT_CODES.billing,
|
|
32010
|
+
payment_failed: EXIT_CODES.billing,
|
|
32011
|
+
// 5: could not reach TolkBot
|
|
32012
|
+
network: EXIT_CODES.network
|
|
32013
|
+
};
|
|
32014
|
+
function exitCodeFor(code) {
|
|
32015
|
+
return ERROR_CODES[code];
|
|
32016
|
+
}
|
|
32017
|
+
|
|
32018
|
+
// src/utils/report.ts
|
|
32019
|
+
function convexErrorData(error62) {
|
|
32020
|
+
if (error62 instanceof Error && error62.name === "ConvexError" && "data" in error62) {
|
|
32021
|
+
return { data: error62.data };
|
|
32022
|
+
}
|
|
32023
|
+
return null;
|
|
32024
|
+
}
|
|
32025
|
+
function billingHint() {
|
|
32026
|
+
return `Manage billing at ${env.TOLKBOT_WEB_URL}/subscription.`;
|
|
32027
|
+
}
|
|
32028
|
+
var BILLING_MESSAGES = [
|
|
32029
|
+
{ startsWith: "No billing account", code: "no_subscription" },
|
|
32030
|
+
{ startsWith: "Insufficient credits", code: "out_of_credits" },
|
|
32031
|
+
{ startsWith: "Overage limit", code: "out_of_credits" }
|
|
32032
|
+
];
|
|
32033
|
+
function fromConvexError(data) {
|
|
32034
|
+
if (typeof data === "string") {
|
|
32035
|
+
const billing = BILLING_MESSAGES.find(
|
|
32036
|
+
(m) => data.startsWith(m.startsWith)
|
|
32037
|
+
);
|
|
32038
|
+
if (billing) return new CliError(data, billingHint(), billing.code);
|
|
32039
|
+
return new CliError(data);
|
|
32040
|
+
}
|
|
32041
|
+
if (typeof data !== "object" || data === null) return null;
|
|
32042
|
+
const { code, message } = data;
|
|
32043
|
+
const text = typeof message === "string" ? message : describeError(data);
|
|
32044
|
+
if (code === PROJECT_ARCHIVED) {
|
|
32045
|
+
return new CliError(text, void 0, "project_archived");
|
|
32046
|
+
}
|
|
32047
|
+
if (code === BILLING_BLOCKED) {
|
|
32048
|
+
return new CliError(text, billingHint(), "payment_failed");
|
|
32049
|
+
}
|
|
32050
|
+
return typeof message === "string" ? new CliError(message) : null;
|
|
32051
|
+
}
|
|
32052
|
+
function serverMessage(message) {
|
|
32053
|
+
const unwrapped = message.replace(
|
|
32054
|
+
/^\[Request ID: [^\]]*\] Server Error\s*/,
|
|
32055
|
+
""
|
|
32056
|
+
);
|
|
32057
|
+
const uncaught = /Uncaught (?:\w*Error: )?([\s\S]*?)(?:\n\s+at |$)/.exec(
|
|
32058
|
+
unwrapped
|
|
32059
|
+
);
|
|
32060
|
+
return (uncaught?.[1] ?? unwrapped).trim();
|
|
32061
|
+
}
|
|
32062
|
+
function argumentError(message) {
|
|
32063
|
+
if (!message.includes("ArgumentValidationError")) return null;
|
|
32064
|
+
const path12 = /Path: \.?(\S+)/.exec(message)?.[1];
|
|
32065
|
+
const value = /Value: (.+)/.exec(message)?.[1];
|
|
32066
|
+
return new UsageError(
|
|
32067
|
+
path12 && value ? `TolkBot refused ${value} as ${path12}.` : "TolkBot refused the arguments.",
|
|
32068
|
+
"Check the id or value passed; list ids with the command's list subcommand."
|
|
32069
|
+
);
|
|
32070
|
+
}
|
|
32071
|
+
function toCliError(error62) {
|
|
32072
|
+
if (error62 instanceof CliError) return error62;
|
|
32073
|
+
const convex = convexErrorData(error62);
|
|
32074
|
+
if (convex) {
|
|
32075
|
+
const known = fromConvexError(convex.data);
|
|
32076
|
+
if (known) return known;
|
|
32077
|
+
}
|
|
32078
|
+
if (isUnauthenticatedError(error62)) {
|
|
32079
|
+
return new CliError(
|
|
32080
|
+
"Not authenticated.",
|
|
32081
|
+
"Run 'tolkbot auth login', or set $TOLKBOT_TOKEN for CI and headless environments.",
|
|
32082
|
+
"not_authenticated"
|
|
32083
|
+
);
|
|
32084
|
+
}
|
|
32085
|
+
if (isNetworkError(error62)) {
|
|
32086
|
+
return new CliError(
|
|
32087
|
+
"Could not reach TolkBot.",
|
|
32088
|
+
"Check the network connection and try again.",
|
|
32089
|
+
"network"
|
|
32090
|
+
);
|
|
32091
|
+
}
|
|
32092
|
+
const message = describeError(error62);
|
|
32093
|
+
const refused = argumentError(message);
|
|
32094
|
+
if (refused) return refused;
|
|
32095
|
+
if (message.includes("Server Error")) {
|
|
32096
|
+
return new CliError(serverMessage(message));
|
|
32097
|
+
}
|
|
32098
|
+
const internal = new CliError(message, void 0, "internal");
|
|
32099
|
+
if (error62 instanceof Error) internal.stack = error62.stack;
|
|
32100
|
+
return internal;
|
|
32101
|
+
}
|
|
32102
|
+
function errorPayload(error62) {
|
|
32103
|
+
return {
|
|
32104
|
+
error: {
|
|
32105
|
+
code: error62.code,
|
|
32106
|
+
message: error62.baseMessage,
|
|
32107
|
+
hint: error62.hint ?? null
|
|
32108
|
+
}
|
|
32109
|
+
};
|
|
32110
|
+
}
|
|
32111
|
+
function reportError(error62) {
|
|
32112
|
+
const cli = toCliError(error62);
|
|
32113
|
+
if (wantsJson()) {
|
|
32114
|
+
console.error(JSON.stringify(errorPayload(cli), null, 2));
|
|
32115
|
+
} else {
|
|
32116
|
+
console.error(`\u2717 ${cli.baseMessage}`);
|
|
32117
|
+
if (cli.hint) console.error(` ${cli.hint}`);
|
|
32118
|
+
if (cli.code === "internal" && cli.stack) {
|
|
32119
|
+
console.error("");
|
|
32120
|
+
console.error(cli.stack);
|
|
32121
|
+
}
|
|
32122
|
+
}
|
|
32123
|
+
const code = exitCodeFor(cli.code);
|
|
32124
|
+
process.exitCode = code;
|
|
32125
|
+
return code;
|
|
32126
|
+
}
|
|
32127
|
+
function fail(error62) {
|
|
32128
|
+
const cli = toCliError(error62);
|
|
32129
|
+
throw requestExit(exitCodeFor(cli.code), () => {
|
|
32130
|
+
reportError(cli);
|
|
32131
|
+
});
|
|
32132
|
+
}
|
|
32392
32133
|
|
|
32393
32134
|
// src/commands/translate/run-ui.tsx
|
|
32394
|
-
import { Spinner as
|
|
32135
|
+
import { Spinner as Spinner16 } from "@inkjs/ui";
|
|
32395
32136
|
import { Box as Box19, Text as Text19 } from "ink";
|
|
32396
32137
|
import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
32397
32138
|
function JobLink({ jobId }) {
|
|
@@ -32421,7 +32162,7 @@ function JobRow({ srcLocale, job }) {
|
|
|
32421
32162
|
}
|
|
32422
32163
|
if (status === "generating") {
|
|
32423
32164
|
return /* @__PURE__ */ jsxs19(Box19, { children: [
|
|
32424
|
-
/* @__PURE__ */ jsx19(
|
|
32165
|
+
/* @__PURE__ */ jsx19(Spinner16, {}),
|
|
32425
32166
|
/* @__PURE__ */ jsxs19(Text19, { children: [
|
|
32426
32167
|
" ",
|
|
32427
32168
|
srcLocale,
|
|
@@ -32436,7 +32177,7 @@ function JobRow({ srcLocale, job }) {
|
|
|
32436
32177
|
const percent = progress.total > 0 ? Math.round(processed / progress.total * 100) : 0;
|
|
32437
32178
|
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
32438
32179
|
/* @__PURE__ */ jsxs19(Box19, { children: [
|
|
32439
|
-
/* @__PURE__ */ jsx19(
|
|
32180
|
+
/* @__PURE__ */ jsx19(Spinner16, {}),
|
|
32440
32181
|
/* @__PURE__ */ jsxs19(Text19, { children: [
|
|
32441
32182
|
" ",
|
|
32442
32183
|
srcLocale,
|
|
@@ -32561,13 +32302,31 @@ function isJobFinished(status) {
|
|
|
32561
32302
|
function isJobUnsuccessful(status) {
|
|
32562
32303
|
return status === "failed" || status === "cancelled";
|
|
32563
32304
|
}
|
|
32305
|
+
function unsuccessfulJobError(jobId, status) {
|
|
32306
|
+
if (status === "cancelled") {
|
|
32307
|
+
return new CliError(
|
|
32308
|
+
`Translation job ${jobId} was cancelled.`,
|
|
32309
|
+
void 0,
|
|
32310
|
+
"job_cancelled"
|
|
32311
|
+
);
|
|
32312
|
+
}
|
|
32313
|
+
if (status === "failed") {
|
|
32314
|
+
return new CliError(
|
|
32315
|
+
`Translation job ${jobId} failed.`,
|
|
32316
|
+
`See it at ${env.TOLKBOT_WEB_URL}/translation-jobs/${jobId}.`,
|
|
32317
|
+
"job_failed"
|
|
32318
|
+
);
|
|
32319
|
+
}
|
|
32320
|
+
return null;
|
|
32321
|
+
}
|
|
32564
32322
|
var JOB_WAIT_TIMEOUT_MS = 60 * 6e4;
|
|
32565
32323
|
var JOB_POLL_MS = 500;
|
|
32566
32324
|
var JobWaitTimeoutError = class extends CliError {
|
|
32567
32325
|
constructor(jobId, timeoutMs) {
|
|
32568
32326
|
super(
|
|
32569
32327
|
`Translation job ${jobId} was still running after ${Math.round(timeoutMs / 6e4)} minutes, so the CLI stopped waiting.`,
|
|
32570
|
-
`The job keeps running. Check it with 'tolkbot translate status --job-id ${jobId}' and apply it with 'tolkbot translate apply --job-id ${jobId}' once it finishes
|
|
32328
|
+
`The job keeps running. Check it with 'tolkbot translate status --job-id ${jobId}' and apply it with 'tolkbot translate apply --job-id ${jobId}' once it finishes.`,
|
|
32329
|
+
"job_timed_out"
|
|
32571
32330
|
);
|
|
32572
32331
|
this.jobId = jobId;
|
|
32573
32332
|
this.name = "JobWaitTimeoutError";
|
|
@@ -32583,7 +32342,11 @@ async function waitForJob(jobId, read, opts = {}) {
|
|
|
32583
32342
|
for (; ; ) {
|
|
32584
32343
|
const summary = await read();
|
|
32585
32344
|
if (!summary) {
|
|
32586
|
-
throw new CliError(
|
|
32345
|
+
throw new CliError(
|
|
32346
|
+
`Translation job ${jobId} was not found.`,
|
|
32347
|
+
void 0,
|
|
32348
|
+
"job_not_found"
|
|
32349
|
+
);
|
|
32587
32350
|
}
|
|
32588
32351
|
opts.onProgress?.(summary);
|
|
32589
32352
|
if (isJobFinished(summary.status)) return summary;
|
|
@@ -32641,67 +32404,68 @@ async function runTranslate(opts) {
|
|
|
32641
32404
|
if (opts.all) {
|
|
32642
32405
|
dstLocales = project.targetLanguages;
|
|
32643
32406
|
if (dstLocales.length === 0) {
|
|
32644
|
-
throw new
|
|
32645
|
-
|
|
32646
|
-
|
|
32647
|
-
|
|
32407
|
+
throw new CliError(
|
|
32408
|
+
"No target languages configured.",
|
|
32409
|
+
`Configure them in the project settings at ${env.TOLKBOT_WEB_URL}/projects/${project.slug}/settings.`,
|
|
32410
|
+
"no_target_languages"
|
|
32648
32411
|
);
|
|
32649
32412
|
}
|
|
32650
32413
|
} else if (opts.dst) {
|
|
32651
32414
|
dstLocales = [opts.dst];
|
|
32652
32415
|
} else {
|
|
32653
|
-
throw new
|
|
32416
|
+
throw new UsageError(
|
|
32417
|
+
"Either --dst <locale> or --all must be provided."
|
|
32418
|
+
);
|
|
32654
32419
|
}
|
|
32655
32420
|
const planOpts = { retranslateUntracked: opts.retranslateUntracked };
|
|
32656
32421
|
const tasksFor = async (dstLocale) => opts.tasks ? { tasks: await loadTasksFromFile(opts.tasks), plan: void 0 } : await generateTasks(ctx, srcLocale, dstLocale, planOpts);
|
|
32657
32422
|
if (opts.json) {
|
|
32658
32423
|
const results = [];
|
|
32659
|
-
|
|
32660
|
-
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
|
|
32665
|
-
|
|
32666
|
-
|
|
32667
|
-
|
|
32668
|
-
|
|
32669
|
-
|
|
32670
|
-
}
|
|
32671
|
-
try {
|
|
32672
|
-
const { jobId, summary: job } = await runTranslationJob({
|
|
32673
|
-
projectId,
|
|
32674
|
-
organizationId: project.organizationId,
|
|
32675
|
-
framework,
|
|
32676
|
-
srcLocale,
|
|
32677
|
-
dstLocale,
|
|
32678
|
-
tasks
|
|
32679
|
-
});
|
|
32680
|
-
results.push({
|
|
32681
|
-
dstLocale,
|
|
32682
|
-
jobId,
|
|
32683
|
-
status: job.status,
|
|
32684
|
-
completed: job.completed,
|
|
32685
|
-
failed: job.failed,
|
|
32686
|
-
total: job.total,
|
|
32687
|
-
error: job.status === "cancelled" ? "The job was cancelled." : void 0,
|
|
32688
|
-
plan: summary
|
|
32689
|
-
});
|
|
32690
|
-
} catch (error62) {
|
|
32691
|
-
if (!(error62 instanceof JobWaitTimeoutError)) throw error62;
|
|
32692
|
-
results.push({
|
|
32693
|
-
dstLocale,
|
|
32694
|
-
jobId: error62.jobId,
|
|
32695
|
-
status: "timed-out",
|
|
32696
|
-
error: error62.message,
|
|
32697
|
-
plan: summary
|
|
32698
|
-
});
|
|
32699
|
-
break;
|
|
32700
|
-
}
|
|
32701
|
-
if (results.at(-1)?.status === "cancelled") break;
|
|
32424
|
+
let failure3 = null;
|
|
32425
|
+
for (const dstLocale of dstLocales) {
|
|
32426
|
+
const { tasks, plan } = await tasksFor(dstLocale);
|
|
32427
|
+
const summary = plan ? summarize(plan) : void 0;
|
|
32428
|
+
if (tasks.length === 0) {
|
|
32429
|
+
results.push({
|
|
32430
|
+
dstLocale,
|
|
32431
|
+
status: "up-to-date",
|
|
32432
|
+
plan: summary
|
|
32433
|
+
});
|
|
32434
|
+
continue;
|
|
32702
32435
|
}
|
|
32703
|
-
|
|
32704
|
-
|
|
32436
|
+
try {
|
|
32437
|
+
const { jobId, summary: job } = await runTranslationJob({
|
|
32438
|
+
projectId,
|
|
32439
|
+
organizationId: project.organizationId,
|
|
32440
|
+
framework,
|
|
32441
|
+
srcLocale,
|
|
32442
|
+
dstLocale,
|
|
32443
|
+
tasks
|
|
32444
|
+
});
|
|
32445
|
+
results.push({
|
|
32446
|
+
dstLocale,
|
|
32447
|
+
jobId,
|
|
32448
|
+
status: job.status,
|
|
32449
|
+
completed: job.completed,
|
|
32450
|
+
failed: job.failed,
|
|
32451
|
+
total: job.total,
|
|
32452
|
+
error: job.status === "cancelled" ? "The job was cancelled." : void 0,
|
|
32453
|
+
plan: summary
|
|
32454
|
+
});
|
|
32455
|
+
failure3 ??= unsuccessfulJobError(jobId, job.status);
|
|
32456
|
+
} catch (error62) {
|
|
32457
|
+
if (!(error62 instanceof JobWaitTimeoutError)) throw error62;
|
|
32458
|
+
results.push({
|
|
32459
|
+
dstLocale,
|
|
32460
|
+
jobId: error62.jobId,
|
|
32461
|
+
status: "timed-out",
|
|
32462
|
+
error: error62.message,
|
|
32463
|
+
plan: summary
|
|
32464
|
+
});
|
|
32465
|
+
failure3 ??= error62;
|
|
32466
|
+
break;
|
|
32467
|
+
}
|
|
32468
|
+
if (results.at(-1)?.status === "cancelled") break;
|
|
32705
32469
|
}
|
|
32706
32470
|
track("cli_translate_completed", {
|
|
32707
32471
|
mode: "run",
|
|
@@ -32710,18 +32474,15 @@ ${env.TOLKBOT_WEB_URL}/projects/${project.slug}/settings`
|
|
|
32710
32474
|
job_count: results.filter((r) => r.jobId).length,
|
|
32711
32475
|
json: true
|
|
32712
32476
|
});
|
|
32713
|
-
|
|
32714
|
-
(r) => r.status !== "timed-out" && !isJobUnsuccessful(r.status)
|
|
32715
|
-
);
|
|
32716
|
-
if (ok) jsonSuccess({ jobs: results });
|
|
32477
|
+
if (!failure3) jsonSuccess({ jobs: results });
|
|
32717
32478
|
jsonResult({ jobs: results }, 1);
|
|
32718
|
-
|
|
32479
|
+
fail(failure3);
|
|
32719
32480
|
}
|
|
32720
32481
|
const jobs = dstLocales.map((locale) => ({
|
|
32721
32482
|
dstLocale: locale,
|
|
32722
32483
|
status: "waiting"
|
|
32723
32484
|
}));
|
|
32724
|
-
const { rerender, unmount } =
|
|
32485
|
+
const { rerender, unmount } = render19(
|
|
32725
32486
|
/* @__PURE__ */ jsx20(RunUI, { srcLocale, jobs })
|
|
32726
32487
|
);
|
|
32727
32488
|
const updateJob = (locale, update) => {
|
|
@@ -32745,6 +32506,7 @@ ${env.TOLKBOT_WEB_URL}/projects/${project.slug}/settings`
|
|
|
32745
32506
|
});
|
|
32746
32507
|
}
|
|
32747
32508
|
};
|
|
32509
|
+
let failure2 = null;
|
|
32748
32510
|
try {
|
|
32749
32511
|
for (const dstLocale of dstLocales) {
|
|
32750
32512
|
updateJob(dstLocale, { status: "generating" });
|
|
@@ -32783,7 +32545,7 @@ ${env.TOLKBOT_WEB_URL}/projects/${project.slug}/settings`
|
|
|
32783
32545
|
jobId: error62.jobId,
|
|
32784
32546
|
error: error62.message
|
|
32785
32547
|
});
|
|
32786
|
-
|
|
32548
|
+
failure2 ??= error62;
|
|
32787
32549
|
skipRemaining(dstLocale, "the CLI stopped waiting for");
|
|
32788
32550
|
break;
|
|
32789
32551
|
}
|
|
@@ -32795,7 +32557,7 @@ ${env.TOLKBOT_WEB_URL}/projects/${project.slug}/settings`
|
|
|
32795
32557
|
jobId,
|
|
32796
32558
|
error: "Translation failed"
|
|
32797
32559
|
});
|
|
32798
|
-
|
|
32560
|
+
failure2 ??= unsuccessfulJobError(jobId, summary.status);
|
|
32799
32561
|
} else if (summary.status === "cancelled") {
|
|
32800
32562
|
updateJob(dstLocale, {
|
|
32801
32563
|
status: "error",
|
|
@@ -32803,7 +32565,7 @@ ${env.TOLKBOT_WEB_URL}/projects/${project.slug}/settings`
|
|
|
32803
32565
|
jobId,
|
|
32804
32566
|
error: "The job was cancelled."
|
|
32805
32567
|
});
|
|
32806
|
-
|
|
32568
|
+
failure2 ??= unsuccessfulJobError(jobId, summary.status);
|
|
32807
32569
|
skipRemaining(dstLocale, "someone cancelled");
|
|
32808
32570
|
break;
|
|
32809
32571
|
} else {
|
|
@@ -32821,10 +32583,10 @@ ${env.TOLKBOT_WEB_URL}/projects/${project.slug}/settings`
|
|
|
32821
32583
|
job_count: jobs.filter((j) => j.jobId).length,
|
|
32822
32584
|
json: false
|
|
32823
32585
|
});
|
|
32824
|
-
}
|
|
32586
|
+
} finally {
|
|
32825
32587
|
unmount();
|
|
32826
|
-
throw err;
|
|
32827
32588
|
}
|
|
32589
|
+
if (failure2) fail(failure2);
|
|
32828
32590
|
}
|
|
32829
32591
|
function summarize(plan) {
|
|
32830
32592
|
return {
|
|
@@ -32836,32 +32598,23 @@ function summarize(plan) {
|
|
|
32836
32598
|
}
|
|
32837
32599
|
|
|
32838
32600
|
// src/commands/translate/status.tsx
|
|
32839
|
-
import { Spinner as
|
|
32840
|
-
import { Box as Box20, render as
|
|
32601
|
+
import { Spinner as Spinner17 } from "@inkjs/ui";
|
|
32602
|
+
import { Box as Box20, render as render20, Text as Text20 } from "ink";
|
|
32841
32603
|
import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
32842
32604
|
function StatusUI({ state }) {
|
|
32843
32605
|
if (state.type === "loading") {
|
|
32844
32606
|
return /* @__PURE__ */ jsxs20(Box20, { children: [
|
|
32845
|
-
/* @__PURE__ */ jsx21(
|
|
32607
|
+
/* @__PURE__ */ jsx21(Spinner17, {}),
|
|
32846
32608
|
/* @__PURE__ */ jsx21(Text20, { children: " Loading job status\u2026" })
|
|
32847
32609
|
] });
|
|
32848
32610
|
}
|
|
32849
|
-
if (state.type === "not-found") {
|
|
32850
|
-
return /* @__PURE__ */ jsx21(Box20, { children: /* @__PURE__ */ jsx21(Text20, { color: "red", children: "\u2717 Job not found" }) });
|
|
32851
|
-
}
|
|
32852
|
-
if (state.type === "error") {
|
|
32853
|
-
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
32854
|
-
/* @__PURE__ */ jsx21(Text20, { color: "red", children: "\u2717 Error fetching job status" }),
|
|
32855
|
-
/* @__PURE__ */ jsx21(Text20, { dimColor: true, children: state.message })
|
|
32856
|
-
] });
|
|
32857
|
-
}
|
|
32858
32611
|
const { summary } = state;
|
|
32859
32612
|
const processed = summary.completed + summary.failed;
|
|
32860
32613
|
const progress = summary.total > 0 ? Math.round(processed / summary.total * 100) : 0;
|
|
32861
32614
|
if (state.type === "running") {
|
|
32862
32615
|
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
32863
32616
|
/* @__PURE__ */ jsxs20(Box20, { children: [
|
|
32864
|
-
/* @__PURE__ */ jsx21(
|
|
32617
|
+
/* @__PURE__ */ jsx21(Spinner17, {}),
|
|
32865
32618
|
/* @__PURE__ */ jsx21(Text20, { children: " Job is running\u2026" })
|
|
32866
32619
|
] }),
|
|
32867
32620
|
/* @__PURE__ */ jsx21(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { children: [
|
|
@@ -32912,96 +32665,59 @@ function StatusUI({ state }) {
|
|
|
32912
32665
|
] });
|
|
32913
32666
|
}
|
|
32914
32667
|
async function fetchJobSummary(jobId) {
|
|
32915
|
-
const
|
|
32916
|
-
const
|
|
32917
|
-
const config3 = readConfig(baseDir);
|
|
32918
|
-
if (!config3) {
|
|
32919
|
-
throw new Error(
|
|
32920
|
-
"No tolkbot.json found. Run 'tolkbot init' to create one."
|
|
32921
|
-
);
|
|
32922
|
-
}
|
|
32923
|
-
const project = await convexClient.query(api.projects.getProjectForCli, {
|
|
32924
|
-
projectId: config3.config.projectId
|
|
32925
|
-
});
|
|
32926
|
-
if (!project) {
|
|
32927
|
-
throw new Error("Project not found. Please run 'tolkbot init' again.");
|
|
32928
|
-
}
|
|
32929
|
-
setTelemetryOrganization(project.organizationId);
|
|
32930
|
-
const summary = await convexClient.query(api.translations.getJobSummary, {
|
|
32668
|
+
const { client: client3, project } = await loadPlanContext(process.cwd());
|
|
32669
|
+
const summary = await client3.query(api.translations.getJobSummary, {
|
|
32931
32670
|
organizationId: project.organizationId,
|
|
32932
32671
|
jobId
|
|
32933
32672
|
});
|
|
32673
|
+
if (!summary) {
|
|
32674
|
+
throw new CliError(
|
|
32675
|
+
`Translation job ${jobId} was not found in this project.`,
|
|
32676
|
+
void 0,
|
|
32677
|
+
"job_not_found"
|
|
32678
|
+
);
|
|
32679
|
+
}
|
|
32934
32680
|
return summary;
|
|
32935
32681
|
}
|
|
32936
32682
|
async function runStatus(opts) {
|
|
32937
32683
|
if (opts.json) {
|
|
32938
|
-
|
|
32939
|
-
|
|
32940
|
-
|
|
32941
|
-
|
|
32942
|
-
|
|
32943
|
-
|
|
32944
|
-
|
|
32945
|
-
|
|
32946
|
-
|
|
32947
|
-
|
|
32948
|
-
|
|
32949
|
-
|
|
32950
|
-
failed: summary.failed,
|
|
32951
|
-
total: summary.total
|
|
32952
|
-
});
|
|
32953
|
-
} catch (error62) {
|
|
32954
|
-
jsonError(error62 instanceof Error ? error62.message : String(error62));
|
|
32955
|
-
}
|
|
32684
|
+
const summary2 = await fetchJobSummary(opts.jobId);
|
|
32685
|
+
const output2 = {
|
|
32686
|
+
jobId: opts.jobId,
|
|
32687
|
+
status: summary2.status,
|
|
32688
|
+
completed: summary2.completed,
|
|
32689
|
+
failed: summary2.failed,
|
|
32690
|
+
total: summary2.total
|
|
32691
|
+
};
|
|
32692
|
+
const failure2 = unsuccessfulJobError(opts.jobId, summary2.status);
|
|
32693
|
+
if (!failure2) jsonSuccess(output2);
|
|
32694
|
+
jsonResult(output2, 1);
|
|
32695
|
+
fail(failure2);
|
|
32956
32696
|
}
|
|
32957
|
-
|
|
32697
|
+
const { unmount } = renderTransient(
|
|
32698
|
+
/* @__PURE__ */ jsx21(StatusUI, { state: { type: "loading" } })
|
|
32699
|
+
);
|
|
32700
|
+
let summary;
|
|
32958
32701
|
try {
|
|
32959
|
-
|
|
32960
|
-
|
|
32961
|
-
}
|
|
32962
|
-
const summary = await fetchJobSummary(opts.jobId);
|
|
32963
|
-
if (!summary) {
|
|
32964
|
-
unmount();
|
|
32965
|
-
const notFoundUi = render19(
|
|
32966
|
-
/* @__PURE__ */ jsx21(StatusUI, { state: { type: "not-found" } })
|
|
32967
|
-
);
|
|
32968
|
-
unmount = notFoundUi.unmount;
|
|
32969
|
-
await notFoundUi.waitUntilExit();
|
|
32970
|
-
return exitAfterFlush(1);
|
|
32971
|
-
}
|
|
32972
|
-
const isCompleted = isJobFinished(summary.status);
|
|
32973
|
-
unmount();
|
|
32974
|
-
const finalUi = render19(
|
|
32975
|
-
/* @__PURE__ */ jsx21(
|
|
32976
|
-
StatusUI,
|
|
32977
|
-
{
|
|
32978
|
-
state: {
|
|
32979
|
-
type: isCompleted ? "completed" : "running",
|
|
32980
|
-
summary
|
|
32981
|
-
}
|
|
32982
|
-
}
|
|
32983
|
-
)
|
|
32984
|
-
);
|
|
32985
|
-
unmount = finalUi.unmount;
|
|
32986
|
-
await finalUi.waitUntilExit();
|
|
32987
|
-
if (isJobUnsuccessful(summary.status)) {
|
|
32988
|
-
return exitAfterFlush(1);
|
|
32989
|
-
}
|
|
32990
|
-
} catch (error62) {
|
|
32702
|
+
summary = await fetchJobSummary(opts.jobId);
|
|
32703
|
+
} finally {
|
|
32991
32704
|
unmount();
|
|
32992
|
-
|
|
32993
|
-
|
|
32994
|
-
|
|
32995
|
-
|
|
32996
|
-
|
|
32997
|
-
|
|
32998
|
-
|
|
32999
|
-
|
|
32705
|
+
}
|
|
32706
|
+
const finalUi = render20(
|
|
32707
|
+
/* @__PURE__ */ jsx21(
|
|
32708
|
+
StatusUI,
|
|
32709
|
+
{
|
|
32710
|
+
state: {
|
|
32711
|
+
type: isJobFinished(summary.status) ? "completed" : "running",
|
|
32712
|
+
summary
|
|
33000
32713
|
}
|
|
33001
|
-
|
|
33002
|
-
)
|
|
33003
|
-
|
|
33004
|
-
|
|
32714
|
+
}
|
|
32715
|
+
)
|
|
32716
|
+
);
|
|
32717
|
+
finalUi.unmount();
|
|
32718
|
+
if (isJobUnsuccessful(summary.status)) {
|
|
32719
|
+
const failure2 = unsuccessfulJobError(opts.jobId, summary.status);
|
|
32720
|
+
if (failure2) fail(failure2);
|
|
33005
32721
|
}
|
|
33006
32722
|
}
|
|
33007
32723
|
|
|
@@ -33040,14 +32756,12 @@ translateCommand.command("run").description("Run translation job (auto-generates
|
|
|
33040
32756
|
});
|
|
33041
32757
|
const parsed = validateOrExit(schema, opts);
|
|
33042
32758
|
if (!parsed.dst && !parsed.all) {
|
|
33043
|
-
|
|
33044
|
-
"
|
|
32759
|
+
throw new UsageError(
|
|
32760
|
+
"Either --dst <locale> or --all must be provided."
|
|
33045
32761
|
);
|
|
33046
|
-
return exitAfterFlush(1);
|
|
33047
32762
|
}
|
|
33048
32763
|
if (parsed.dst && parsed.all) {
|
|
33049
|
-
|
|
33050
|
-
return exitAfterFlush(1);
|
|
32764
|
+
throw new UsageError("Cannot use both --dst and --all.");
|
|
33051
32765
|
}
|
|
33052
32766
|
await runTranslate(parsed);
|
|
33053
32767
|
});
|
|
@@ -33065,8 +32779,7 @@ translateCommand.command("adopt").description(
|
|
|
33065
32779
|
});
|
|
33066
32780
|
const parsed = validateOrExit(schema, opts);
|
|
33067
32781
|
if (parsed.dst && parsed.all) {
|
|
33068
|
-
|
|
33069
|
-
return exitAfterFlush(1);
|
|
32782
|
+
throw new UsageError("Cannot use both --dst and --all.");
|
|
33070
32783
|
}
|
|
33071
32784
|
await runAdopt(parsed);
|
|
33072
32785
|
});
|
|
@@ -33079,7 +32792,7 @@ translateCommand.command("cancel").description("Cancel translation job by job ID
|
|
|
33079
32792
|
await runCancel(parsed);
|
|
33080
32793
|
});
|
|
33081
32794
|
translateCommand.command("apply").description(
|
|
33082
|
-
"Apply translation
|
|
32795
|
+
"Apply finished translation jobs to the working tree. Without --job-id: the one unapplied job per language, or a picker in a terminal"
|
|
33083
32796
|
).option("--job-id <id...>", "Job ID(s) to apply").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
33084
32797
|
const schema = z7.object({
|
|
33085
32798
|
jobId: z7.array(z7.string()).optional(),
|
|
@@ -33109,7 +32822,7 @@ translateCommand.command("status").description("Check status of translation job
|
|
|
33109
32822
|
});
|
|
33110
32823
|
|
|
33111
32824
|
// src/commands/validate.tsx
|
|
33112
|
-
import { Box as Box21, render as
|
|
32825
|
+
import { Box as Box21, render as render21, Text as Text21 } from "ink";
|
|
33113
32826
|
|
|
33114
32827
|
// ../ai/src/validators/placeholders.ts
|
|
33115
32828
|
var IDENTIFIER = /^[A-Za-z0-9_$][A-Za-z0-9_$.[\]-]*$/;
|
|
@@ -34275,11 +33988,24 @@ function emit(report, json2) {
|
|
|
34275
33988
|
});
|
|
34276
33989
|
if (json2) {
|
|
34277
33990
|
jsonResult(report, exitCode);
|
|
33991
|
+
if (!report.valid) fail(validationFailure(report));
|
|
34278
33992
|
return;
|
|
34279
33993
|
}
|
|
34280
|
-
|
|
33994
|
+
render21(/* @__PURE__ */ jsx22(ReportUI, { report })).unmount();
|
|
34281
33995
|
process.exitCode = exitCode;
|
|
34282
33996
|
}
|
|
33997
|
+
function validationFailure(report) {
|
|
33998
|
+
if (report.issues.some((issue2) => issue2.rule === "no-config")) {
|
|
33999
|
+
return noConfigError();
|
|
34000
|
+
}
|
|
34001
|
+
const { error: error62, warning } = report.counts;
|
|
34002
|
+
const found = report.strict ? `${error62} error(s) and ${warning} warning(s)` : `${error62} error(s)`;
|
|
34003
|
+
return new CliError(
|
|
34004
|
+
`Validation found ${found}.`,
|
|
34005
|
+
"The report on stdout lists each one.",
|
|
34006
|
+
"validation_failed"
|
|
34007
|
+
);
|
|
34008
|
+
}
|
|
34283
34009
|
async function runValidate(opts = {}) {
|
|
34284
34010
|
const json2 = opts.json ?? false;
|
|
34285
34011
|
const strict = opts.strict ?? false;
|
|
@@ -34324,16 +34050,17 @@ function commandPath(command) {
|
|
|
34324
34050
|
return names.join(" ");
|
|
34325
34051
|
}
|
|
34326
34052
|
function buildProgram() {
|
|
34327
|
-
const
|
|
34053
|
+
const program = new Command6().name("tolkbot").description("Localization workflow for your repo").version(version, "-v, --version", "Output the version number").helpOption("-h, --help", "Show help").helpCommand("help [command]", "Show help for a command").option(
|
|
34328
34054
|
"--token <token>",
|
|
34329
34055
|
`Auth token to use for this invocation (overrides $${TOKEN_ENV_VAR} and the stored token)`
|
|
34330
34056
|
);
|
|
34331
|
-
|
|
34057
|
+
program.hook("preAction", (thisCommand, actionCommand) => {
|
|
34332
34058
|
const { token } = thisCommand.opts();
|
|
34333
34059
|
setTokenOverride(token);
|
|
34060
|
+
resolveJsonOption(actionCommand);
|
|
34334
34061
|
beginCommand(commandPath(actionCommand), version);
|
|
34335
34062
|
});
|
|
34336
|
-
|
|
34063
|
+
program.command("init").description("Initialize TolkBot in the current repo").option("--force", "Overwrite existing config").option(
|
|
34337
34064
|
"-y, --yes",
|
|
34338
34065
|
"Skip all prompts, accepting detected settings. Required outside a terminal."
|
|
34339
34066
|
).option("--json", "Output the result as JSON (implies --yes)").option("--base <locale>", "Base locale (default: detected, else en)").option(
|
|
@@ -34346,28 +34073,75 @@ function buildProgram() {
|
|
|
34346
34073
|
"--install",
|
|
34347
34074
|
"Also install i18n dependencies and scaffold files. Off by default when non-interactive."
|
|
34348
34075
|
).action((opts) => runInit(opts));
|
|
34349
|
-
|
|
34076
|
+
program.command("validate").description("Validate translation files and configuration").option("--json", "Output the report as JSON").option(
|
|
34350
34077
|
"--strict",
|
|
34351
34078
|
"Treat warnings (incomplete locales) as errors. Off by default, since a locale is normally behind the base while a translation job is in flight."
|
|
34352
34079
|
).action(
|
|
34353
34080
|
(opts) => runValidate(opts)
|
|
34354
34081
|
);
|
|
34355
|
-
|
|
34356
|
-
|
|
34357
|
-
|
|
34358
|
-
|
|
34359
|
-
|
|
34360
|
-
|
|
34361
|
-
|
|
34082
|
+
program.addCommand(authCommand);
|
|
34083
|
+
program.addCommand(glossaryCommand);
|
|
34084
|
+
program.addCommand(learningsCommand);
|
|
34085
|
+
program.addCommand(telemetryCommand);
|
|
34086
|
+
program.addCommand(translateCommand);
|
|
34087
|
+
program.addHelpText("after", PROGRAM_HELP_FOOTER);
|
|
34088
|
+
forEachCommand(program, (command) => {
|
|
34089
|
+
addNoJsonOption(command);
|
|
34090
|
+
reportUsageErrors(command);
|
|
34091
|
+
});
|
|
34092
|
+
return program;
|
|
34093
|
+
}
|
|
34094
|
+
function forEachCommand(command, visit2) {
|
|
34095
|
+
visit2(command);
|
|
34096
|
+
for (const sub of command.commands) forEachCommand(sub, visit2);
|
|
34097
|
+
}
|
|
34098
|
+
function jsonOptionOf(command) {
|
|
34099
|
+
return command.options.find((option) => option.long === "--json");
|
|
34100
|
+
}
|
|
34101
|
+
function addNoJsonOption(command) {
|
|
34102
|
+
const json2 = jsonOptionOf(command);
|
|
34103
|
+
if (!json2 || command.options.some((o) => o.long === "--no-json")) return;
|
|
34104
|
+
json2.description = `${json2.description.replace(" (machine-readable)", "")}, the default without a terminal`;
|
|
34105
|
+
command.option("--no-json", "Output text, even without a terminal");
|
|
34106
|
+
}
|
|
34107
|
+
function resolveJsonOption(command) {
|
|
34108
|
+
if (!jsonOptionOf(command)) {
|
|
34109
|
+
setJsonOutput(null);
|
|
34110
|
+
return;
|
|
34111
|
+
}
|
|
34112
|
+
const json2 = resolveJson(command.opts().json);
|
|
34113
|
+
command.setOptionValue("json", json2);
|
|
34114
|
+
setJsonOutput(json2);
|
|
34115
|
+
}
|
|
34116
|
+
function reportUsageErrors(command) {
|
|
34117
|
+
command.exitOverride().configureOutput({
|
|
34118
|
+
outputError: (message, write) => {
|
|
34119
|
+
if (wantsJson()) {
|
|
34120
|
+
const help = ["tolkbot", commandPath(command), "--help"].filter(Boolean).join(" ");
|
|
34121
|
+
pendingUsageError = new UsageError(
|
|
34122
|
+
message.replace(/^error:\s*/, "").trim(),
|
|
34123
|
+
`Run '${help}'.`
|
|
34124
|
+
);
|
|
34125
|
+
return;
|
|
34126
|
+
}
|
|
34127
|
+
write(message);
|
|
34128
|
+
}
|
|
34129
|
+
});
|
|
34362
34130
|
}
|
|
34131
|
+
var pendingUsageError = null;
|
|
34363
34132
|
var PROGRAM_HELP_FOOTER = `
|
|
34364
34133
|
Examples:
|
|
34365
34134
|
$ tolkbot auth login
|
|
34366
|
-
$ tolkbot init
|
|
34367
|
-
$ tolkbot translate plan --dst nb --json
|
|
34135
|
+
$ tolkbot init
|
|
34368
34136
|
$ tolkbot translate run --all
|
|
34369
|
-
$ tolkbot translate apply
|
|
34370
|
-
$ tolkbot validate
|
|
34137
|
+
$ tolkbot translate apply
|
|
34138
|
+
$ tolkbot validate
|
|
34139
|
+
|
|
34140
|
+
Without a terminal (an agent, CI), nothing prompts: commands take their
|
|
34141
|
+
defaults and print JSON (--no-json for text). A failure prints
|
|
34142
|
+
{"error": {"code", "message", "hint"}} on stderr and exits with
|
|
34143
|
+
1 failed 2 usage 3 not logged in or not allowed
|
|
34144
|
+
4 billing 5 could not reach TolkBot
|
|
34371
34145
|
|
|
34372
34146
|
Environment variables:
|
|
34373
34147
|
${TOKEN_ENV_VAR} Auth token for CI and headless environments (--token overrides it)
|
|
@@ -34388,8 +34162,14 @@ async function main() {
|
|
|
34388
34162
|
exitCode = error62.code;
|
|
34389
34163
|
return;
|
|
34390
34164
|
}
|
|
34391
|
-
|
|
34392
|
-
|
|
34165
|
+
if (error62 instanceof CommanderError) {
|
|
34166
|
+
if (error62.exitCode === 0) return;
|
|
34167
|
+
exitCode = EXIT_CODES.usage;
|
|
34168
|
+
process.exitCode = exitCode;
|
|
34169
|
+
if (pendingUsageError) reportError(pendingUsageError);
|
|
34170
|
+
return;
|
|
34171
|
+
}
|
|
34172
|
+
exitCode = reportError(error62);
|
|
34393
34173
|
} finally {
|
|
34394
34174
|
completeCommand(exitCode);
|
|
34395
34175
|
await flushTelemetry();
|
|
@@ -34397,7 +34177,9 @@ async function main() {
|
|
|
34397
34177
|
}
|
|
34398
34178
|
|
|
34399
34179
|
// src/index.ts
|
|
34400
|
-
main().catch(
|
|
34180
|
+
main().catch((error62) => {
|
|
34181
|
+
process.exitCode = reportError(error62);
|
|
34182
|
+
});
|
|
34401
34183
|
/*! Bundled license information:
|
|
34402
34184
|
|
|
34403
34185
|
is-extglob/index.js:
|