@chalksurf/cli 0.3.0 → 0.3.1

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.
@@ -132,6 +132,12 @@ var createApiClient = ({
132
132
  agentSetExerciseVisibility: async (input) => {
133
133
  return await mutation("agentSetExerciseVisibility", input);
134
134
  },
135
+ agentStartExerciseTranslationGeneration: async (input) => {
136
+ return await mutation(
137
+ "agentStartExerciseTranslationGeneration",
138
+ input
139
+ );
140
+ },
135
141
  agentGetSheet: async (id) => {
136
142
  return await query("agentGetSheet", { id });
137
143
  },
@@ -159,6 +165,12 @@ var createApiClient = ({
159
165
  agentSetSheetVisibility: async (input) => {
160
166
  return await mutation("agentSetSheetVisibility", input);
161
167
  },
168
+ agentStartSheetTranslationGeneration: async (input) => {
169
+ return await mutation(
170
+ "agentStartSheetTranslationGeneration",
171
+ input
172
+ );
173
+ },
162
174
  agentAppendExercisesToSheet: async (input) => {
163
175
  return await mutation("agentAppendExercisesToSheet", input);
164
176
  },
@@ -1448,7 +1460,11 @@ var Constants = {
1448
1460
  "import_sheet_started",
1449
1461
  "import_sheet_completed",
1450
1462
  "import_sheet_solutions_started",
1451
- "import_sheet_solutions_completed"
1463
+ "import_sheet_solutions_completed",
1464
+ "generate_exercise_translation_started",
1465
+ "generate_exercise_translation_completed",
1466
+ "generate_sheet_translation_started",
1467
+ "generate_sheet_translation_completed"
1452
1468
  ],
1453
1469
  agent_audit_outcome: ["started", "success", "failed", "denied"],
1454
1470
  agent_audit_resource_type: ["exercise", "sheet", "folder", "job", "event"],
@@ -1680,6 +1696,28 @@ var agentWriteOperationRegistry = {
1680
1696
  receiptOperation: null,
1681
1697
  resourceType: "sheet",
1682
1698
  sync: false
1699
+ },
1700
+ generate_exercise_translation_started: {
1701
+ agentToolName: "start_exercise_translation_generation",
1702
+ receiptOperation: null,
1703
+ resourceType: "job",
1704
+ sync: false
1705
+ },
1706
+ generate_exercise_translation_completed: {
1707
+ receiptOperation: null,
1708
+ resourceType: "exercise",
1709
+ sync: false
1710
+ },
1711
+ generate_sheet_translation_started: {
1712
+ agentToolName: "start_sheet_translation_generation",
1713
+ receiptOperation: null,
1714
+ resourceType: "job",
1715
+ sync: false
1716
+ },
1717
+ generate_sheet_translation_completed: {
1718
+ receiptOperation: null,
1719
+ resourceType: "sheet",
1720
+ sync: false
1683
1721
  }
1684
1722
  };
1685
1723
  var agentWriteReceiptOperations = Object.values(agentWriteOperationRegistry).map((operation) => operation.receiptOperation).filter((operation) => Boolean(operation));
@@ -1979,6 +2017,40 @@ var validateLatexSnippetsAgentInputSchema = strictObject({
1979
2017
  var validateLatexSnippetsAgentOutputSchema = strictObject({
1980
2018
  results: z.array(latexValidationResultSchema)
1981
2019
  });
2020
+ var queuedExerciseTranslationJobSchema = strictObject({
2021
+ exerciseId: uuidSchema,
2022
+ jobId: uuidSchema,
2023
+ languages: z.array(z.enum(translationLanguages)).min(1),
2024
+ type: z.literal("exercise_translation_generation")
2025
+ });
2026
+ var queuedSheetTranslationJobSchema = strictObject({
2027
+ exerciseSheetId: uuidSchema,
2028
+ jobId: uuidSchema,
2029
+ language: z.enum(translationLanguages),
2030
+ type: z.literal("exercise_sheet_translation_generation")
2031
+ });
2032
+ var skippedSheetTranslationJobSchema = strictObject({
2033
+ jobId: z.null(),
2034
+ language: z.enum(translationLanguages),
2035
+ reason: z.literal("already_complete"),
2036
+ type: z.literal("exercise_sheet_translation_generation")
2037
+ });
2038
+ var targetTranslationLanguagesSchema = z.array(z.enum(translationLanguages)).min(1).refine((languages) => new Set(languages).size === languages.length, "Target languages must be unique");
2039
+ var startExerciseTranslationGenerationAgentInputSchema = strictObject({
2040
+ id: uuidSchema,
2041
+ targetLanguages: targetTranslationLanguagesSchema
2042
+ });
2043
+ var startExerciseTranslationGenerationAgentOutputSchema = strictObject({
2044
+ jobs: z.array(queuedExerciseTranslationJobSchema)
2045
+ });
2046
+ var startSheetTranslationGenerationAgentInputSchema = strictObject({
2047
+ id: uuidSchema,
2048
+ sourceLanguage: z.enum(translationLanguages).optional(),
2049
+ targetLanguages: targetTranslationLanguagesSchema
2050
+ });
2051
+ var startSheetTranslationGenerationAgentOutputSchema = strictObject({
2052
+ jobs: z.array(z.union([queuedSheetTranslationJobSchema, skippedSheetTranslationJobSchema]))
2053
+ });
1982
2054
  var sendUserFeedbackAgentInputSchema = strictObject({
1983
2055
  message: feedbackMessageSchema,
1984
2056
  userConsent: z.literal(true),
@@ -2415,6 +2487,10 @@ var setExerciseVisibilityInputSchema = setExerciseVisibilityAgentInputSchema.ext
2415
2487
  organizationId: organizationIdSchema
2416
2488
  });
2417
2489
  var setExerciseVisibilityOutputSchema = setExerciseVisibilityAgentOutputSchema;
2490
+ var generateExerciseTranslationJobInputSchema = startExerciseTranslationGenerationAgentInputSchema.extend({
2491
+ organizationId: organizationIdSchema
2492
+ });
2493
+ var generateExerciseTranslationJobOutputSchema = startExerciseTranslationGenerationAgentOutputSchema;
2418
2494
  var importExerciseInputSchema = strictObject2({
2419
2495
  organizationId: organizationIdSchema,
2420
2496
  sources: importSourcesSchema,
@@ -2485,6 +2561,10 @@ var setSheetVisibilityInputSchema = setSheetVisibilityAgentInputSchema.extend({
2485
2561
  organizationId: organizationIdSchema
2486
2562
  });
2487
2563
  var setSheetVisibilityOutputSchema = setSheetVisibilityAgentOutputSchema;
2564
+ var generateSheetTranslationJobInputSchema = startSheetTranslationGenerationAgentInputSchema.extend({
2565
+ organizationId: organizationIdSchema
2566
+ });
2567
+ var generateSheetTranslationJobOutputSchema = startSheetTranslationGenerationAgentOutputSchema;
2488
2568
  var appendExercisesToSheetInputSchema = appendExercisesToSheetAgentInputSchema.extend({
2489
2569
  organizationId: organizationIdSchema
2490
2570
  });
@@ -2676,6 +2756,16 @@ var chalksurfMcpToolCapabilities = {
2676
2756
  requiredPermission: "write",
2677
2757
  security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
2678
2758
  },
2759
+ generate_exercise_translation_job: {
2760
+ name: "generate_exercise_translation_job",
2761
+ title: "Generate exercise translation job",
2762
+ description: "Use this to queue translation generation for one writable ChalkSurf exercise.",
2763
+ inputSchema: generateExerciseTranslationJobInputSchema,
2764
+ outputSchema: generateExerciseTranslationJobOutputSchema,
2765
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
2766
+ requiredPermission: "write",
2767
+ security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
2768
+ },
2679
2769
  import_exercise: {
2680
2770
  name: "import_exercise",
2681
2771
  title: "Import exercise",
@@ -2796,6 +2886,16 @@ var chalksurfMcpToolCapabilities = {
2796
2886
  requiredPermission: "write",
2797
2887
  security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
2798
2888
  },
2889
+ generate_sheet_translation_job: {
2890
+ name: "generate_sheet_translation_job",
2891
+ title: "Generate sheet translation job",
2892
+ description: "Use this to queue translation generation for one writable ChalkSurf exercise sheet.",
2893
+ inputSchema: generateSheetTranslationJobInputSchema,
2894
+ outputSchema: generateSheetTranslationJobOutputSchema,
2895
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
2896
+ requiredPermission: "write",
2897
+ security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
2898
+ },
2799
2899
  append_exercises_to_sheet: {
2800
2900
  name: "append_exercises_to_sheet",
2801
2901
  title: "Append exercises to sheet",
@@ -5053,6 +5153,13 @@ var formatUpdateExerciseOutput = ({ receipt }) => {
5053
5153
  var formatSetExerciseVisibilityOutput = ({ receipt }) => {
5054
5154
  return formatAgentWriteReceiptOutput({ receipt });
5055
5155
  };
5156
+ var getExerciseTranslationJobIds = ({ jobs }) => jobs.map((job) => job.jobId);
5157
+ var formatExerciseTranslationOutput = ({ jobs }) => {
5158
+ if (jobs.length === 0) {
5159
+ return "No exercise translation jobs queued.";
5160
+ }
5161
+ return jobs.map((job) => `${job.jobId} exercise ${job.exerciseId} ${job.languages.join(", ")}`).join("\n");
5162
+ };
5056
5163
  var resolveVisibilityFlag = ({ privateFlag, publicFlag }) => {
5057
5164
  if (publicFlag && privateFlag) {
5058
5165
  throw new CliCommandError("--public and --private cannot be used together.", 2);
@@ -5719,6 +5826,98 @@ var registerExerciseCommands = (exerciseYargs, context) => {
5719
5826
  }
5720
5827
  );
5721
5828
  }
5829
+ ).command(
5830
+ "translate [exerciseId]",
5831
+ "Queue translation generation for an existing exercise",
5832
+ (translateYargs) => translateYargs.positional("exerciseId", {
5833
+ type: "string",
5834
+ describe: "Exercise ID to translate"
5835
+ }).option("translate-to", {
5836
+ array: true,
5837
+ type: "string",
5838
+ describe: `Generate translated exercise content for these languages (${translationLanguages2.join(", ")})`
5839
+ }).option("timeout-ms", {
5840
+ type: "number",
5841
+ default: 3e5,
5842
+ describe: "Maximum time to wait before timing out"
5843
+ }).option("wait", {
5844
+ type: "boolean",
5845
+ default: false,
5846
+ describe: "Wait for the queued translation job to finish"
5847
+ }).example(
5848
+ "chalksurf exercise translate 00000000-0000-4000-8000-000000000001 --translate-to english --json",
5849
+ "Queue exercise translation generation"
5850
+ ),
5851
+ async (argv) => {
5852
+ const id = resolveRequestedUuid({
5853
+ fallbackValue: argv.exerciseId,
5854
+ label: "exerciseId",
5855
+ required: true
5856
+ });
5857
+ const targetLanguages = resolveRequestedTranslateToLanguages(argv.translateTo);
5858
+ if (!targetLanguages || targetLanguages.length === 0) {
5859
+ throw new CliCommandError("--translate-to is required at least once.", 2);
5860
+ }
5861
+ const input = { id, targetLanguages };
5862
+ const { apiClient, organizationId } = await createResolvedApiClient({
5863
+ context,
5864
+ baseUrlFlagValue: argv.baseUrl,
5865
+ organizationFlagValue: argv.organization,
5866
+ profileName: argv.profile
5867
+ });
5868
+ let result;
5869
+ try {
5870
+ result = await apiClient.agentStartExerciseTranslationGeneration(input);
5871
+ } catch (error) {
5872
+ throw mapApiErrorToCliError(error);
5873
+ }
5874
+ if (argv.wait !== true) {
5875
+ context.output.print(
5876
+ {
5877
+ request: {
5878
+ ...input,
5879
+ organizationId: organizationId ?? null,
5880
+ wait: false,
5881
+ timeoutMs: null
5882
+ },
5883
+ jobs: result.jobs
5884
+ },
5885
+ (output) => formatExerciseTranslationOutput({ jobs: output.jobs }),
5886
+ {
5887
+ command: "exercise translate"
5888
+ }
5889
+ );
5890
+ return;
5891
+ }
5892
+ const timeoutMs = normalizeWaitTimeoutMs(argv.timeoutMs);
5893
+ const waitResult = await waitForCliJobs({
5894
+ getUserJob: async (jobId) => await apiClient.getUserJob(jobId),
5895
+ jobIds: getExerciseTranslationJobIds(result),
5896
+ now: context.now,
5897
+ sleep: context.sleep,
5898
+ timeoutMs
5899
+ });
5900
+ const waitError = getWaitError(waitResult);
5901
+ context.output.print(
5902
+ {
5903
+ request: {
5904
+ ...input,
5905
+ organizationId: organizationId ?? null,
5906
+ wait: true,
5907
+ timeoutMs
5908
+ },
5909
+ jobs: result.jobs,
5910
+ wait: waitResult
5911
+ },
5912
+ (output) => output.wait.jobs.map((job) => formatCliJobSummary(job)).join("\n"),
5913
+ {
5914
+ command: "exercise translate",
5915
+ ok: waitError == null,
5916
+ error: waitError
5917
+ }
5918
+ );
5919
+ throwSilentExitCode(getWaitExitCode(waitResult));
5920
+ }
5722
5921
  ).command(
5723
5922
  "validate-latex",
5724
5923
  "Validate LaTeX snippets before updating exercise or sheet content",
@@ -7194,6 +7393,15 @@ var formatUpdateSheetOutput = ({ receipt }) => {
7194
7393
  var formatSetSheetVisibilityOutput = ({ receipt }) => {
7195
7394
  return formatAgentWriteReceiptOutput({ receipt });
7196
7395
  };
7396
+ var getSheetTranslationJobIds = ({ jobs }) => jobs.flatMap((job) => job.jobId ? [job.jobId] : []);
7397
+ var formatSheetTranslationOutput = ({ jobs }) => {
7398
+ if (jobs.length === 0 || jobs.every((job) => job.jobId === null)) {
7399
+ return "No sheet translation jobs queued.";
7400
+ }
7401
+ return jobs.map(
7402
+ (job) => job.jobId ? `${job.jobId} sheet ${job.exerciseSheetId} ${job.language}` : `already complete ${job.language}`
7403
+ ).join("\n");
7404
+ };
7197
7405
  var formatAppendExercisesToSheetOutput = ({ receipt }) => {
7198
7406
  return formatAgentWriteReceiptOutput({ receipt });
7199
7407
  };
@@ -7983,6 +8191,111 @@ var registerSheetCommands = (sheetYargs, context) => {
7983
8191
  }
7984
8192
  );
7985
8193
  }
8194
+ ).command(
8195
+ "translate [exerciseSheetId]",
8196
+ "Queue translation generation for an existing exercise sheet",
8197
+ (translateYargs) => translateYargs.positional("exerciseSheetId", {
8198
+ type: "string",
8199
+ describe: "Exercise sheet ID to translate"
8200
+ }).option("translate-to", {
8201
+ array: true,
8202
+ type: "string",
8203
+ describe: `Generate translated sheet content for these languages (${translationLanguages2.join(", ")})`
8204
+ }).option("source-language", {
8205
+ type: "string",
8206
+ describe: "Preferred source language for sheet metadata translation"
8207
+ }).option("timeout-ms", {
8208
+ type: "number",
8209
+ default: 3e5,
8210
+ describe: "Maximum time to wait before timing out"
8211
+ }).option("wait", {
8212
+ type: "boolean",
8213
+ default: false,
8214
+ describe: "Wait for queued translation jobs to finish"
8215
+ }).example(
8216
+ "chalksurf sheet translate 00000000-0000-4000-8000-000000000001 --translate-to english --json",
8217
+ "Queue sheet translation generation"
8218
+ ),
8219
+ async (argv) => {
8220
+ const id = resolveRequestedUuid2({
8221
+ fallbackValue: argv.exerciseSheetId,
8222
+ label: "exerciseSheetId",
8223
+ required: true
8224
+ });
8225
+ const targetLanguages = resolveRequestedTranslateToLanguages(argv.translateTo);
8226
+ if (!targetLanguages || targetLanguages.length === 0) {
8227
+ throw new CliCommandError("--translate-to is required at least once.", 2);
8228
+ }
8229
+ const sourceLanguage = normalizeChoiceValues({
8230
+ allowedValues: translationLanguages2,
8231
+ label: "--source-language",
8232
+ values: argv.sourceLanguage ? [argv.sourceLanguage] : void 0
8233
+ })?.[0];
8234
+ const input = {
8235
+ id,
8236
+ targetLanguages,
8237
+ ...sourceLanguage === void 0 ? {} : { sourceLanguage }
8238
+ };
8239
+ const { apiClient, organizationId } = await createResolvedApiClient({
8240
+ context,
8241
+ baseUrlFlagValue: argv.baseUrl,
8242
+ organizationFlagValue: argv.organization,
8243
+ profileName: argv.profile
8244
+ });
8245
+ let result;
8246
+ try {
8247
+ result = await apiClient.agentStartSheetTranslationGeneration(input);
8248
+ } catch (error) {
8249
+ throw mapApiErrorToCliError(error);
8250
+ }
8251
+ if (argv.wait !== true) {
8252
+ context.output.print(
8253
+ {
8254
+ request: {
8255
+ ...input,
8256
+ organizationId: organizationId ?? null,
8257
+ wait: false,
8258
+ timeoutMs: null
8259
+ },
8260
+ jobs: result.jobs
8261
+ },
8262
+ (output) => formatSheetTranslationOutput({ jobs: output.jobs }),
8263
+ {
8264
+ command: "sheet translate"
8265
+ }
8266
+ );
8267
+ return;
8268
+ }
8269
+ const timeoutMs = normalizeWaitTimeoutMs(argv.timeoutMs);
8270
+ const jobIds = getSheetTranslationJobIds(result);
8271
+ const waitResult = jobIds.length === 0 ? { jobs: [], timedOut: false } : await waitForCliJobs({
8272
+ getUserJob: async (jobId) => await apiClient.getUserJob(jobId),
8273
+ jobIds,
8274
+ now: context.now,
8275
+ sleep: context.sleep,
8276
+ timeoutMs
8277
+ });
8278
+ const waitError = getWaitError(waitResult);
8279
+ context.output.print(
8280
+ {
8281
+ request: {
8282
+ ...input,
8283
+ organizationId: organizationId ?? null,
8284
+ wait: true,
8285
+ timeoutMs
8286
+ },
8287
+ jobs: result.jobs,
8288
+ wait: waitResult
8289
+ },
8290
+ (output) => output.wait.jobs.length > 0 ? output.wait.jobs.map((job) => formatCliJobSummary(job)).join("\n") : formatSheetTranslationOutput({ jobs: output.jobs }),
8291
+ {
8292
+ command: "sheet translate",
8293
+ ok: waitError == null,
8294
+ error: waitError
8295
+ }
8296
+ );
8297
+ throwSilentExitCode(getWaitExitCode(waitResult));
8298
+ }
7986
8299
  ).command(
7987
8300
  "update [exerciseSheetId]",
7988
8301
  "Apply a field-level patch to an exercise sheet",
package/docs/agents.md CHANGED
@@ -172,6 +172,7 @@ MCP clients should use tool discovery from the client. The ChalkSurf MCP endpoin
172
172
  | `exercise copy` | `copy_exercise` |
173
173
  | `exercise update` | `update_exercise` |
174
174
  | `exercise set-visibility` | `set_exercise_visibility` |
175
+ | `exercise translate` | `generate_exercise_translation_job` |
175
176
  | `exercise delete` | `delete_exercise` |
176
177
  | `exercise validate-latex` | `validate_latex_snippets` |
177
178
  | `exercise import` | `import_exercise` |
@@ -186,6 +187,7 @@ MCP clients should use tool discovery from the client. The ChalkSurf MCP endpoin
186
187
  | `sheet append` | `append_exercises_to_sheet` |
187
188
  | `sheet update` | `update_sheet` |
188
189
  | `sheet set-visibility` | `set_sheet_visibility` |
190
+ | `sheet translate` | `generate_sheet_translation_job` |
189
191
  | `sheet delete` | `delete_sheet` |
190
192
  | `sheet import` | `import_sheet` |
191
193
  | `sheet import-solutions` | `import_sheet_solutions` |
package/docs/manual.md CHANGED
@@ -162,6 +162,15 @@ chalksurf job wait job_123 job_124
162
162
 
163
163
  `--wait` on an import command means the parsed content has been saved before the command exits. Requested translations run as separate jobs; use `--json` to capture their job IDs or `chalksurf job list` to inspect them later.
164
164
 
165
+ Queue translations for existing resources:
166
+
167
+ ```bash
168
+ chalksurf exercise translate 00000000-0000-4000-8000-000000000001 --translate-to english
169
+ chalksurf sheet translate 00000000-0000-4000-8000-000000000002 --translate-to english --source-language hungarian
170
+ ```
171
+
172
+ Add `--wait` to either translate command to wait for the queued translation job or jobs to finish.
173
+
165
174
  ## Searching From The CLI
166
175
 
167
176
  Search commands default to `--ownership own`, which means resources in the selected organization.
package/docs/mcp.md CHANGED
@@ -43,6 +43,7 @@ Treat write receipts as an agent audit log. They are suitable for user-visible s
43
43
  | `delete_exercise` | write | Soft-delete one unused writable exercise after `updated_at` and exact-ID confirmation checks. |
44
44
  | `update_exercise` | write | Patch exercise fields with an `updated_at` precondition and shared-usage acknowledgement. |
45
45
  | `set_exercise_visibility` | write | Make one writable exercise public or private when visibility controls are enabled. |
46
+ | `generate_exercise_translation_job` | write | Queue translation generation for one writable exercise. |
46
47
  | `import_exercise` | write | Queue an exercise import from MCP files or HTTPS URLs. |
47
48
  | `import_exercise_solution` | write | Queue a solution import for one exercise. |
48
49
  | `search_sheets` | read | Search visible sheets, optionally by readiness issues. |
@@ -56,6 +57,7 @@ Treat write receipts as an agent audit log. They are suitable for user-visible s
56
57
  | `update_sheet` | write | Patch sheet metadata with an `updated_at` precondition. |
57
58
  | `set_sheet_visibility` | write | Make one writable sheet public or private when visibility controls are enabled. |
58
59
  | `append_exercises_to_sheet` | write | Append existing visible exercises to the end of one writable sheet. |
60
+ | `generate_sheet_translation_job` | write | Queue translation generation for one writable exercise sheet. |
59
61
  | `list_folders` | read | List sheet folders in the selected organization. |
60
62
  | `create_folder` | write | Create a sheet folder. |
61
63
  | `rename_folder` | write | Rename a sheet folder. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chalksurf/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {