@ttctl/mcp 0.0.0 → 0.1.0-rc.2

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.
Files changed (195) hide show
  1. package/README.md +72 -9
  2. package/dist/auth.d.ts +40 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +69 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/data-handling.d.ts +91 -0
  7. package/dist/data-handling.d.ts.map +1 -0
  8. package/dist/data-handling.js +129 -0
  9. package/dist/data-handling.js.map +1 -0
  10. package/dist/diagnostic.d.ts +262 -0
  11. package/dist/diagnostic.d.ts.map +1 -0
  12. package/dist/diagnostic.js +362 -0
  13. package/dist/diagnostic.js.map +1 -0
  14. package/dist/errors.d.ts +54 -0
  15. package/dist/errors.d.ts.map +1 -0
  16. package/dist/errors.js +48 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +8 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +7 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/kill-switch-hook.d.ts +67 -0
  23. package/dist/kill-switch-hook.d.ts.map +1 -0
  24. package/dist/kill-switch-hook.js +61 -0
  25. package/dist/kill-switch-hook.js.map +1 -0
  26. package/dist/server.d.ts +100 -0
  27. package/dist/server.d.ts.map +1 -0
  28. package/dist/server.js +157 -0
  29. package/dist/server.js.map +1 -0
  30. package/dist/tools/_shared.d.ts +227 -0
  31. package/dist/tools/_shared.d.ts.map +1 -0
  32. package/dist/tools/_shared.js +238 -0
  33. package/dist/tools/_shared.js.map +1 -0
  34. package/dist/tools/applications.d.ts +27 -0
  35. package/dist/tools/applications.d.ts.map +1 -0
  36. package/dist/tools/applications.js +192 -0
  37. package/dist/tools/applications.js.map +1 -0
  38. package/dist/tools/availability.d.ts +33 -0
  39. package/dist/tools/availability.d.ts.map +1 -0
  40. package/dist/tools/availability.js +272 -0
  41. package/dist/tools/availability.js.map +1 -0
  42. package/dist/tools/contracts.d.ts +29 -0
  43. package/dist/tools/contracts.d.ts.map +1 -0
  44. package/dist/tools/contracts.js +157 -0
  45. package/dist/tools/contracts.js.map +1 -0
  46. package/dist/tools/engagements.d.ts +36 -0
  47. package/dist/tools/engagements.d.ts.map +1 -0
  48. package/dist/tools/engagements.js +408 -0
  49. package/dist/tools/engagements.js.map +1 -0
  50. package/dist/tools/file-upload.d.ts +133 -0
  51. package/dist/tools/file-upload.d.ts.map +1 -0
  52. package/dist/tools/file-upload.js +247 -0
  53. package/dist/tools/file-upload.js.map +1 -0
  54. package/dist/tools/index.d.ts +28 -0
  55. package/dist/tools/index.d.ts.map +1 -0
  56. package/dist/tools/index.js +133 -0
  57. package/dist/tools/index.js.map +1 -0
  58. package/dist/tools/jobs.d.ts +37 -0
  59. package/dist/tools/jobs.d.ts.map +1 -0
  60. package/dist/tools/jobs.js +505 -0
  61. package/dist/tools/jobs.js.map +1 -0
  62. package/dist/tools/output-schemas.d.ts +129 -0
  63. package/dist/tools/output-schemas.d.ts.map +1 -0
  64. package/dist/tools/output-schemas.js +138 -0
  65. package/dist/tools/output-schemas.js.map +1 -0
  66. package/dist/tools/payments.d.ts +36 -0
  67. package/dist/tools/payments.d.ts.map +1 -0
  68. package/dist/tools/payments.js +373 -0
  69. package/dist/tools/payments.js.map +1 -0
  70. package/dist/tools/profile/certifications.d.ts +18 -0
  71. package/dist/tools/profile/certifications.d.ts.map +1 -0
  72. package/dist/tools/profile/certifications.js +219 -0
  73. package/dist/tools/profile/certifications.js.map +1 -0
  74. package/dist/tools/profile/education.d.ts +23 -0
  75. package/dist/tools/profile/education.d.ts.map +1 -0
  76. package/dist/tools/profile/education.js +222 -0
  77. package/dist/tools/profile/education.js.map +1 -0
  78. package/dist/tools/profile/employment.d.ts +23 -0
  79. package/dist/tools/profile/employment.d.ts.map +1 -0
  80. package/dist/tools/profile/employment.js +254 -0
  81. package/dist/tools/profile/employment.js.map +1 -0
  82. package/dist/tools/profile/industries.d.ts +30 -0
  83. package/dist/tools/profile/industries.d.ts.map +1 -0
  84. package/dist/tools/profile/industries.js +196 -0
  85. package/dist/tools/profile/industries.js.map +1 -0
  86. package/dist/tools/profile/portfolio.d.ts +22 -0
  87. package/dist/tools/profile/portfolio.d.ts.map +1 -0
  88. package/dist/tools/profile/portfolio.js +341 -0
  89. package/dist/tools/profile/portfolio.js.map +1 -0
  90. package/dist/tools/profile/resume.d.ts +16 -0
  91. package/dist/tools/profile/resume.d.ts.map +1 -0
  92. package/dist/tools/profile/resume.js +107 -0
  93. package/dist/tools/profile/resume.js.map +1 -0
  94. package/dist/tools/profile/shared.d.ts +85 -0
  95. package/dist/tools/profile/shared.d.ts.map +1 -0
  96. package/dist/tools/profile/shared.js +128 -0
  97. package/dist/tools/profile/shared.js.map +1 -0
  98. package/dist/tools/profile/visas.d.ts +15 -0
  99. package/dist/tools/profile/visas.d.ts.map +1 -0
  100. package/dist/tools/profile/visas.js +170 -0
  101. package/dist/tools/profile/visas.js.map +1 -0
  102. package/dist/tools/profile_basic_photo_show.d.ts +14 -0
  103. package/dist/tools/profile_basic_photo_show.d.ts.map +1 -0
  104. package/dist/tools/profile_basic_photo_show.js +59 -0
  105. package/dist/tools/profile_basic_photo_show.js.map +1 -0
  106. package/dist/tools/profile_basic_photo_upload.d.ts +24 -0
  107. package/dist/tools/profile_basic_photo_upload.d.ts.map +1 -0
  108. package/dist/tools/profile_basic_photo_upload.js +90 -0
  109. package/dist/tools/profile_basic_photo_upload.js.map +1 -0
  110. package/dist/tools/profile_basic_show.d.ts +64 -0
  111. package/dist/tools/profile_basic_show.d.ts.map +1 -0
  112. package/dist/tools/profile_basic_show.js +108 -0
  113. package/dist/tools/profile_basic_show.js.map +1 -0
  114. package/dist/tools/profile_basic_update.d.ts +37 -0
  115. package/dist/tools/profile_basic_update.d.ts.map +1 -0
  116. package/dist/tools/profile_basic_update.js +97 -0
  117. package/dist/tools/profile_basic_update.js.map +1 -0
  118. package/dist/tools/profile_external_advanced_wizard_show.d.ts +14 -0
  119. package/dist/tools/profile_external_advanced_wizard_show.d.ts.map +1 -0
  120. package/dist/tools/profile_external_advanced_wizard_show.js +56 -0
  121. package/dist/tools/profile_external_advanced_wizard_show.js.map +1 -0
  122. package/dist/tools/profile_external_custom_requirements_set.d.ts +13 -0
  123. package/dist/tools/profile_external_custom_requirements_set.d.ts.map +1 -0
  124. package/dist/tools/profile_external_custom_requirements_set.js +75 -0
  125. package/dist/tools/profile_external_custom_requirements_set.js.map +1 -0
  126. package/dist/tools/profile_external_custom_requirements_show.d.ts +14 -0
  127. package/dist/tools/profile_external_custom_requirements_show.d.ts.map +1 -0
  128. package/dist/tools/profile_external_custom_requirements_show.js +56 -0
  129. package/dist/tools/profile_external_custom_requirements_show.js.map +1 -0
  130. package/dist/tools/profile_external_readiness.d.ts +12 -0
  131. package/dist/tools/profile_external_readiness.d.ts.map +1 -0
  132. package/dist/tools/profile_external_readiness.js +54 -0
  133. package/dist/tools/profile_external_readiness.js.map +1 -0
  134. package/dist/tools/profile_external_recommendations.d.ts +15 -0
  135. package/dist/tools/profile_external_recommendations.d.ts.map +1 -0
  136. package/dist/tools/profile_external_recommendations.js +57 -0
  137. package/dist/tools/profile_external_recommendations.js.map +1 -0
  138. package/dist/tools/profile_external_show.d.ts +15 -0
  139. package/dist/tools/profile_external_show.d.ts.map +1 -0
  140. package/dist/tools/profile_external_show.js +59 -0
  141. package/dist/tools/profile_external_show.js.map +1 -0
  142. package/dist/tools/profile_external_update.d.ts +14 -0
  143. package/dist/tools/profile_external_update.d.ts.map +1 -0
  144. package/dist/tools/profile_external_update.js +79 -0
  145. package/dist/tools/profile_external_update.js.map +1 -0
  146. package/dist/tools/profile_reviews_approve_item.d.ts +17 -0
  147. package/dist/tools/profile_reviews_approve_item.d.ts.map +1 -0
  148. package/dist/tools/profile_reviews_approve_item.js +77 -0
  149. package/dist/tools/profile_reviews_approve_item.js.map +1 -0
  150. package/dist/tools/profile_reviews_approve_section.d.ts +15 -0
  151. package/dist/tools/profile_reviews_approve_section.d.ts.map +1 -0
  152. package/dist/tools/profile_reviews_approve_section.js +70 -0
  153. package/dist/tools/profile_reviews_approve_section.js.map +1 -0
  154. package/dist/tools/profile_reviews_list.d.ts +16 -0
  155. package/dist/tools/profile_reviews_list.d.ts.map +1 -0
  156. package/dist/tools/profile_reviews_list.js +58 -0
  157. package/dist/tools/profile_reviews_list.js.map +1 -0
  158. package/dist/tools/profile_reviews_submit_for_review.d.ts +14 -0
  159. package/dist/tools/profile_reviews_submit_for_review.d.ts.map +1 -0
  160. package/dist/tools/profile_reviews_submit_for_review.js +56 -0
  161. package/dist/tools/profile_reviews_submit_for_review.js.map +1 -0
  162. package/dist/tools/profile_skills_add.d.ts +4 -0
  163. package/dist/tools/profile_skills_add.d.ts.map +1 -0
  164. package/dist/tools/profile_skills_add.js +52 -0
  165. package/dist/tools/profile_skills_add.js.map +1 -0
  166. package/dist/tools/profile_skills_autocomplete.d.ts +4 -0
  167. package/dist/tools/profile_skills_autocomplete.d.ts.map +1 -0
  168. package/dist/tools/profile_skills_autocomplete.js +78 -0
  169. package/dist/tools/profile_skills_autocomplete.js.map +1 -0
  170. package/dist/tools/profile_skills_list.d.ts +16 -0
  171. package/dist/tools/profile_skills_list.d.ts.map +1 -0
  172. package/dist/tools/profile_skills_list.js +65 -0
  173. package/dist/tools/profile_skills_list.js.map +1 -0
  174. package/dist/tools/profile_skills_readiness.d.ts +4 -0
  175. package/dist/tools/profile_skills_readiness.d.ts.map +1 -0
  176. package/dist/tools/profile_skills_readiness.js +53 -0
  177. package/dist/tools/profile_skills_readiness.js.map +1 -0
  178. package/dist/tools/profile_skills_remove.d.ts +4 -0
  179. package/dist/tools/profile_skills_remove.d.ts.map +1 -0
  180. package/dist/tools/profile_skills_remove.js +53 -0
  181. package/dist/tools/profile_skills_remove.js.map +1 -0
  182. package/dist/tools/profile_skills_show.d.ts +4 -0
  183. package/dist/tools/profile_skills_show.d.ts.map +1 -0
  184. package/dist/tools/profile_skills_show.js +51 -0
  185. package/dist/tools/profile_skills_show.js.map +1 -0
  186. package/dist/tools/profile_skills_update.d.ts +11 -0
  187. package/dist/tools/profile_skills_update.d.ts.map +1 -0
  188. package/dist/tools/profile_skills_update.js +97 -0
  189. package/dist/tools/profile_skills_update.js.map +1 -0
  190. package/dist/tools/timesheet.d.ts +29 -0
  191. package/dist/tools/timesheet.d.ts.map +1 -0
  192. package/dist/tools/timesheet.js +257 -0
  193. package/dist/tools/timesheet.js.map +1 -0
  194. package/package.json +33 -13
  195. package/index.js +0 -7
@@ -0,0 +1,128 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { ConfigError, DateInputError, TtctlError, profile, resolveConfig } from "@ttctl/core";
4
+ import { z } from "zod";
5
+ import { emitMcpAuthResolve } from "../../diagnostic.js";
6
+ import { ttctlErrorToToolResponse } from "../../errors.js";
7
+ /**
8
+ * Zod schema for sub-domain `--from` / `--to` / `--issued` / `--expires` /
9
+ * other date-flavored flags. Accepts ISO-8601 (`YYYY-MM-DD`) or year-only
10
+ * (`YYYY`); semantic validation (real calendar dates, year range) happens
11
+ * later inside `parseDateInput` from `@ttctl/core`. Hoisted here so the
12
+ * four sub-domain tool files share a single source of truth.
13
+ */
14
+ export const dateInput = z
15
+ .string()
16
+ .regex(/^(\d{4}|\d{4}-\d{2}-\d{2})$/, "expected ISO-8601 date (YYYY-MM-DD) or year (YYYY)");
17
+ /**
18
+ * Render a JSON-serialized success response. Tools return structured data
19
+ * as JSON in the `text` content field — MCP-aware LLM clients can parse
20
+ * it; non-aware clients see human-readable JSON. The same payload is
21
+ * mirrored into `structuredContent` so tools declaring an `outputSchema`
22
+ * (#226) get SDK-validated structured output without per-tool plumbing.
23
+ *
24
+ * `structuredContent` is populated only when `payload` is an object —
25
+ * the MCP SDK's `structuredContent` slot expects an object shape, and
26
+ * array / primitive payloads stay in the `text` slot only.
27
+ */
28
+ export function jsonSuccess(payload) {
29
+ const response = {
30
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
31
+ };
32
+ if (payload !== null && typeof payload === "object" && !Array.isArray(payload)) {
33
+ response.structuredContent = payload;
34
+ }
35
+ return response;
36
+ }
37
+ /**
38
+ * Render a plain-text success response (useful for verbs like `remove` /
39
+ * `highlight` whose output is a confirmation, not structured data). Use
40
+ * {@link textWithStructuredSuccess} when the tool also declares an
41
+ * `outputSchema` requiring a typed acknowledgment.
42
+ */
43
+ export function textSuccess(text) {
44
+ return { content: [{ type: "text", text }] };
45
+ }
46
+ /**
47
+ * Render a confirmation line as the `text` content slot while also
48
+ * publishing a typed acknowledgment via `structuredContent`. Used by
49
+ * `*_remove` tools (#226) where the human-readable text stays for
50
+ * compatibility and the structured payload matches the tool's declared
51
+ * `outputSchema`.
52
+ */
53
+ export function textWithStructuredSuccess(text, structuredContent) {
54
+ return {
55
+ content: [{ type: "text", text }],
56
+ structuredContent,
57
+ };
58
+ }
59
+ /**
60
+ * Map a domain error from any sub-domain into an MCP tool-error response.
61
+ *
62
+ * Routes through three branches:
63
+ * - `TtctlError` subclasses → uniform Error/Recovery/Code blocks
64
+ * - `profile.basic.ProfileError` → `(<CODE>): <message>` line
65
+ * - anything else → generic `text: <message>`
66
+ *
67
+ * `commandLabel` (e.g. `"profile.education.add"`) prefixes the error
68
+ * line for sub-domain disambiguation.
69
+ */
70
+ export function presentToolError(commandLabel, err) {
71
+ if (err instanceof TtctlError)
72
+ return ttctlErrorToToolResponse(err);
73
+ if (err instanceof profile.basic.ProfileError) {
74
+ return {
75
+ isError: true,
76
+ content: [{ type: "text", text: `${commandLabel} failed (${err.code}): ${err.message}` }],
77
+ };
78
+ }
79
+ if (err instanceof DateInputError) {
80
+ return {
81
+ isError: true,
82
+ content: [{ type: "text", text: `${commandLabel} failed (${err.code}): ${err.message}` }],
83
+ };
84
+ }
85
+ const message = err instanceof Error ? err.message : String(err);
86
+ return {
87
+ isError: true,
88
+ content: [{ type: "text", text: `${commandLabel} failed: ${message}` }],
89
+ };
90
+ }
91
+ export function createTokenResolver(configPath) {
92
+ return async function resolveTokenForTool(commandLabel) {
93
+ let token;
94
+ try {
95
+ const { config } = resolveConfig({ path: configPath });
96
+ token = config.auth.token;
97
+ }
98
+ catch (err) {
99
+ if (err instanceof ConfigError) {
100
+ emitMcpAuthResolve(configPath, "config_error", false);
101
+ return {
102
+ error: {
103
+ isError: true,
104
+ content: [{ type: "text", text: `${commandLabel} failed (${err.code}): ${err.message}` }],
105
+ },
106
+ };
107
+ }
108
+ throw err;
109
+ }
110
+ if (token === undefined) {
111
+ emitMcpAuthResolve(configPath, "unauthenticated", false);
112
+ return {
113
+ error: {
114
+ isError: true,
115
+ content: [
116
+ {
117
+ type: "text",
118
+ text: `${commandLabel} failed (UNAUTHENTICATED): No auth token found. Run \`ttctl auth signin\` to sign in.`,
119
+ },
120
+ ],
121
+ },
122
+ };
123
+ }
124
+ emitMcpAuthResolve(configPath, "ok", true);
125
+ return Promise.resolve({ token });
126
+ };
127
+ }
128
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/tools/profile/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { type ToolRegistrationContext } from "../_shared.js";
3
+ /**
4
+ * Register the four `ttctl_profile_visas_*` MCP tools.
5
+ *
6
+ * Tool names use the canonical sub-domain `visas` (no CLI alias). Each
7
+ * tool maps 1:1 to a CLI leaf. Dry-run path (issue #165): every tool
8
+ * accepts `dryRun?: boolean` and emits the uniform `{ ok, dryRun, preview }`
9
+ * envelope via MCP-level preview building when `dryRun: true`.
10
+ * `variables.input.profileId` (list) carries
11
+ * `DRY_RUN_PROFILE_ID_PLACEHOLDER` because the apply path resolves it
12
+ * via a sibling profile read.
13
+ */
14
+ export declare function registerVisasTools(server: McpServer, ctx: ToolRegistrationContext): void;
15
+ //# sourceMappingURL=visas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visas.d.ts","sourceRoot":"","sources":["../../../src/tools/profile/visas.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,OAAO,EAAyC,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AASpG;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAyIxF"}
@@ -0,0 +1,170 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { profile } from "@ttctl/core";
4
+ import { z } from "zod";
5
+ import { ttctlErrorToToolResponseOrNull } from "../../errors.js";
6
+ import { buildMcpDryRunPreview, dryRunResponse } from "../_shared.js";
7
+ const DRY_RUN_FIELD = z
8
+ .boolean()
9
+ .optional()
10
+ .describe("Preview the request without executing. Returns `{ ok: true, dryRun: true, preview }` with operationName + variables + redacted bearer header. Default: false.");
11
+ /**
12
+ * Register the four `ttctl_profile_visas_*` MCP tools.
13
+ *
14
+ * Tool names use the canonical sub-domain `visas` (no CLI alias). Each
15
+ * tool maps 1:1 to a CLI leaf. Dry-run path (issue #165): every tool
16
+ * accepts `dryRun?: boolean` and emits the uniform `{ ok, dryRun, preview }`
17
+ * envelope via MCP-level preview building when `dryRun: true`.
18
+ * `variables.input.profileId` (list) carries
19
+ * `DRY_RUN_PROFILE_ID_PLACEHOLDER` because the apply path resolves it
20
+ * via a sibling profile read.
21
+ */
22
+ export function registerVisasTools(server, ctx) {
23
+ server.registerTool("ttctl_profile_visas_list", {
24
+ title: "List travel visas",
25
+ description: "List the signed-in user's travel-visa records (id, country, type, expiry). Pass `dryRun: true` to preview the request.",
26
+ inputSchema: { dryRun: DRY_RUN_FIELD },
27
+ }, async (args) => {
28
+ const auth = await ctx.resolveToolAuth();
29
+ if (!auth.ok)
30
+ return auth.response;
31
+ if (args.dryRun === true) {
32
+ return dryRunResponse(buildMcpDryRunPreview("getTravelVisas", "mobile-gateway", { profileId: profile.basic.DRY_RUN_PROFILE_ID_PLACEHOLDER }, auth.token));
33
+ }
34
+ try {
35
+ const visas = await profile.visas.list(auth.token);
36
+ return successResponse(visas);
37
+ }
38
+ catch (err) {
39
+ return mapVisasError(err);
40
+ }
41
+ });
42
+ server.registerTool("ttctl_profile_visas_add", {
43
+ title: "Create a travel-visa record",
44
+ description: "Create a new travel-visa record. Both `countryId` and `visaType` are required; `expiryDate` is optional. Pass `dryRun: true` to preview the request.",
45
+ inputSchema: {
46
+ countryId: z.string().describe("Country id (server-issued; query the Toptal travel-visa form for valid ids)"),
47
+ visaType: z.string().describe("Visa type (e.g., Schengen, B1/B2, Tier 5)"),
48
+ expiryDate: z.string().optional().describe("Expiry date in YYYY-MM-DD"),
49
+ dryRun: DRY_RUN_FIELD,
50
+ },
51
+ }, async (args) => {
52
+ const auth = await ctx.resolveToolAuth();
53
+ if (!auth.ok)
54
+ return auth.response;
55
+ const input = {
56
+ countryId: args.countryId,
57
+ visaType: args.visaType,
58
+ };
59
+ if (args.expiryDate !== undefined)
60
+ input.expiryDate = args.expiryDate;
61
+ if (args.dryRun === true) {
62
+ return dryRunResponse(buildMcpDryRunPreview("createTravelVisa", "talent-profile", { input: { profileId: profile.basic.DRY_RUN_PROFILE_ID_PLACEHOLDER, travelVisa: input } }, auth.token));
63
+ }
64
+ try {
65
+ const visas = await profile.visas.add(auth.token, input);
66
+ return successResponse(visas);
67
+ }
68
+ catch (err) {
69
+ return mapVisasError(err);
70
+ }
71
+ });
72
+ server.registerTool("ttctl_profile_visas_update", {
73
+ title: "Update a travel-visa record",
74
+ description: "Update fields on a travel-visa record by id. Only supplied fields are updated. Pass `dryRun: true` to preview the request.",
75
+ inputSchema: {
76
+ id: z.string().describe("Id of the travel-visa record"),
77
+ countryId: z.string().optional().describe("Country id"),
78
+ visaType: z.string().optional().describe("Visa type"),
79
+ expiryDate: z.string().optional().describe("Expiry date in YYYY-MM-DD"),
80
+ dryRun: DRY_RUN_FIELD,
81
+ },
82
+ }, async (args) => {
83
+ const auth = await ctx.resolveToolAuth();
84
+ if (!auth.ok)
85
+ return auth.response;
86
+ const changes = {};
87
+ if (args.countryId !== undefined)
88
+ changes.countryId = args.countryId;
89
+ if (args.visaType !== undefined)
90
+ changes.visaType = args.visaType;
91
+ if (args.expiryDate !== undefined)
92
+ changes.expiryDate = args.expiryDate;
93
+ if (args.dryRun === true) {
94
+ return dryRunResponse(buildMcpDryRunPreview("updateTravelVisa", "talent-profile", { input: { travelVisaId: args.id, travelVisa: changes } }, auth.token));
95
+ }
96
+ try {
97
+ const visas = await profile.visas.update(auth.token, args.id, changes);
98
+ return successResponse(visas);
99
+ }
100
+ catch (err) {
101
+ return mapVisasError(err);
102
+ }
103
+ });
104
+ server.registerTool("ttctl_profile_visas_remove", {
105
+ title: "Remove a travel-visa record",
106
+ description: "Remove a travel-visa record by id. Pass `dryRun: true` to preview the request.",
107
+ inputSchema: {
108
+ id: z.string().describe("Id of the travel-visa record to remove"),
109
+ dryRun: DRY_RUN_FIELD,
110
+ },
111
+ }, async (args) => {
112
+ const auth = await ctx.resolveToolAuth();
113
+ if (!auth.ok)
114
+ return auth.response;
115
+ if (args.dryRun === true) {
116
+ return dryRunResponse(buildMcpDryRunPreview("removeTravelVisa", "talent-profile", { input: { travelVisaId: args.id } }, auth.token));
117
+ }
118
+ try {
119
+ const visas = await profile.visas.remove(auth.token, args.id);
120
+ return successResponse(visas);
121
+ }
122
+ catch (err) {
123
+ return mapVisasError(err);
124
+ }
125
+ });
126
+ }
127
+ function successResponse(data) {
128
+ return {
129
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
130
+ };
131
+ }
132
+ function mapVisasError(err) {
133
+ const typed = ttctlErrorToToolResponseOrNull(err);
134
+ if (typed !== null)
135
+ return typed;
136
+ if (err instanceof profile.visas.VisasError) {
137
+ return {
138
+ isError: true,
139
+ content: [
140
+ {
141
+ type: "text",
142
+ text: [
143
+ `Error: ${err.message}`,
144
+ "",
145
+ "Recovery: Adjust the tool input or retry; see the code below.",
146
+ "",
147
+ `(Code: ${err.code})`,
148
+ ].join("\n"),
149
+ },
150
+ ],
151
+ };
152
+ }
153
+ const message = err instanceof Error ? err.message : String(err);
154
+ return {
155
+ isError: true,
156
+ content: [
157
+ {
158
+ type: "text",
159
+ text: [
160
+ `Error: travel-visa request failed: ${message}`,
161
+ "",
162
+ "Recovery: Retry; if the failure persists, file an issue.",
163
+ "",
164
+ "(Code: UNKNOWN)",
165
+ ].join("\n"),
166
+ },
167
+ ],
168
+ };
169
+ }
170
+ //# sourceMappingURL=visas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visas.js","sourceRoot":"","sources":["../../../src/tools/profile/visas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { type ToolRegistrationContext } from "./_shared.js";
3
+ /**
4
+ * Register the `ttctl_profile_basic_photo_show` MCP tool. Read-only.
5
+ *
6
+ * Dry-run path (issue #165): when `dryRun: true`, returns a preview of
7
+ * the `GET_PHOTO` query (talent-profile surface) that WOULD have been
8
+ * sent. The `profileId` variable carries `DRY_RUN_PROFILE_ID_PLACEHOLDER`
9
+ * because the apply path resolves it via a sibling `show()` call at
10
+ * execution time — the dry-run path skips that read entirely so no
11
+ * transport is invoked.
12
+ */
13
+ export declare function registerProfileBasicPhotoShowTool(server: McpServer, ctx: ToolRegistrationContext): void;
14
+ //# sourceMappingURL=profile_basic_photo_show.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile_basic_photo_show.d.ts","sourceRoot":"","sources":["../../src/tools/profile_basic_photo_show.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,OAAO,EAOL,KAAK,uBAAuB,EAC7B,MAAM,cAAc,CAAC;AAItB;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAoDvG"}
@@ -0,0 +1,59 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { profile } from "@ttctl/core";
4
+ import { z } from "zod";
5
+ import { ttctlErrorToToolResponseOrNull } from "../errors.js";
6
+ import { buildMcpDryRunPreview, domainErrorResponse, dryRunResponse, genericErrorResponse, isToolErrorResponse, jsonResponse, } from "./_shared.js";
7
+ const TOOL_NAME = "ttctl_profile_basic_photo_show";
8
+ /**
9
+ * Register the `ttctl_profile_basic_photo_show` MCP tool. Read-only.
10
+ *
11
+ * Dry-run path (issue #165): when `dryRun: true`, returns a preview of
12
+ * the `GET_PHOTO` query (talent-profile surface) that WOULD have been
13
+ * sent. The `profileId` variable carries `DRY_RUN_PROFILE_ID_PLACEHOLDER`
14
+ * because the apply path resolves it via a sibling `show()` call at
15
+ * execution time — the dry-run path skips that read entirely so no
16
+ * transport is invoked.
17
+ */
18
+ export function registerProfileBasicPhotoShowTool(server, ctx) {
19
+ server.registerTool(TOOL_NAME, {
20
+ title: "Show profile photo URLs",
21
+ description: [
22
+ "Fetch the URLs of the signed-in user's profile photo (default / original / small variants plus the recommended crop rectangle and a `isResolutionSatisfied` flag).",
23
+ "",
24
+ "Pass `dryRun: true` to preview the request without firing the query.",
25
+ "",
26
+ "Example user prompts that should map to this tool:",
27
+ ' - "Show me my Toptal profile photo."',
28
+ ' - "What are the URLs of my profile photo variants?"',
29
+ ' - "Is my profile photo resolution OK?"',
30
+ ].join("\n"),
31
+ inputSchema: {
32
+ dryRun: z
33
+ .boolean()
34
+ .optional()
35
+ .describe("Preview the request without executing. Returns `{ ok: true, dryRun: true, preview }` with the GET_PHOTO operation; `variables.profileId` is a placeholder that the apply path would have resolved via a sibling profile read. Default: false."),
36
+ },
37
+ }, async (input) => {
38
+ const auth = await ctx.loadTokenForTool(TOOL_NAME);
39
+ if (isToolErrorResponse(auth))
40
+ return auth;
41
+ if (input.dryRun === true) {
42
+ return dryRunResponse(buildMcpDryRunPreview("GET_PHOTO", "talent-profile", { profileId: profile.basic.DRY_RUN_PROFILE_ID_PLACEHOLDER }, auth.token));
43
+ }
44
+ try {
45
+ const payload = await profile.basic.photoShow(auth.token);
46
+ return jsonResponse(payload);
47
+ }
48
+ catch (err) {
49
+ const typed = ttctlErrorToToolResponseOrNull(err);
50
+ if (typed !== null)
51
+ return typed;
52
+ if (err instanceof profile.basic.ProfileError) {
53
+ return domainErrorResponse(TOOL_NAME, err);
54
+ }
55
+ return genericErrorResponse(TOOL_NAME, err);
56
+ }
57
+ });
58
+ }
59
+ //# sourceMappingURL=profile_basic_photo_show.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile_basic_photo_show.js","sourceRoot":"","sources":["../../src/tools/profile_basic_photo_show.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAGpC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,YAAY,GAEb,MAAM,cAAc,CAAC;AAEtB,MAAM,SAAS,GAAG,gCAAgC,CAAC;AAEnD;;;;;;;;;GASG;AACH,MAAM,UAAU,iCAAiC,CAAC,MAAiB,EAAE,GAA4B;IAC/F,MAAM,CAAC,YAAY,CACjB,SAAS,EACT;QACE,KAAK,EAAE,yBAAyB;QAChC,WAAW,EAAE;YACX,oKAAoK;YACpK,EAAE;YACF,sEAAsE;YACtE,EAAE;YACF,oDAAoD;YACpD,wCAAwC;YACxC,uDAAuD;YACvD,0CAA0C;SAC3C,CAAC,IAAI,CAAC,IAAI,CAAC;QACZ,WAAW,EAAE;YACX,MAAM,EAAE,CAAC;iBACN,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CACP,+OAA+O,CAChP;SACJ;KACF,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;QACd,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACnD,IAAI,mBAAmB,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAE3C,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YAC1B,OAAO,cAAc,CACnB,qBAAqB,CACnB,WAAW,EACX,gBAAgB,EAChB,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,EAC3D,IAAI,CAAC,KAAK,CACX,CACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1D,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,8BAA8B,CAAC,GAAG,CAAC,CAAC;YAClD,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YACjC,IAAI,GAAG,YAAY,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;gBAC9C,OAAO,mBAAmB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YAC7C,CAAC;YACD,OAAO,oBAAoB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { type ToolRegistrationContext } from "./_shared.js";
3
+ /**
4
+ * Register the `ttctl_profile_basic_photo_upload` MCP tool. Mirrors the
5
+ * `ttctl profile basic photo upload <file>` CLI leaf — uploads a new
6
+ * profile photo from a local file path.
7
+ *
8
+ * The tool accepts only a `file` path (string). Buffer input from the CLI
9
+ * surface is not exposed here because MCP transports are JSON-only —
10
+ * passing binary through MCP would require base64 wrapping that's brittle
11
+ * and confusing for LLM clients. Use the CLI leaf if you need to upload
12
+ * a buffer programmatically.
13
+ *
14
+ * Dry-run path (issue #165): when `dryRun: true`, returns a preview of
15
+ * the `UploadProfilePhoto` mutation's GraphQL operations envelope (the
16
+ * top-level fields of the multipart form's `operations` part). The
17
+ * multipart binary payload itself is NOT described — only the GraphQL
18
+ * request shape that would be sent in the `operations` part of the
19
+ * multipart envelope. `variables.input.profileId` is the placeholder
20
+ * `DRY_RUN_PROFILE_ID_PLACEHOLDER` (apply path resolves it via a
21
+ * sibling profile read).
22
+ */
23
+ export declare function registerProfileBasicPhotoUploadTool(server: McpServer, ctx: ToolRegistrationContext): void;
24
+ //# sourceMappingURL=profile_basic_photo_upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile_basic_photo_upload.d.ts","sourceRoot":"","sources":["../../src/tools/profile_basic_photo_upload.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,OAAO,EAOL,KAAK,uBAAuB,EAC7B,MAAM,cAAc,CAAC;AAMtB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAyEzG"}
@@ -0,0 +1,90 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { profile } from "@ttctl/core";
4
+ import { z } from "zod";
5
+ import { ttctlErrorToToolResponseOrNull } from "../errors.js";
6
+ import { buildMcpDryRunPreview, domainErrorResponse, dryRunResponse, genericErrorResponse, isToolErrorResponse, jsonResponse, } from "./_shared.js";
7
+ import { UPLOAD_CATEGORIES, validateUploadPath } from "./file-upload.js";
8
+ import { profileBasicPhotoUploadOutputSchema } from "./output-schemas.js";
9
+ const TOOL_NAME = "ttctl_profile_basic_photo_upload";
10
+ /**
11
+ * Register the `ttctl_profile_basic_photo_upload` MCP tool. Mirrors the
12
+ * `ttctl profile basic photo upload <file>` CLI leaf — uploads a new
13
+ * profile photo from a local file path.
14
+ *
15
+ * The tool accepts only a `file` path (string). Buffer input from the CLI
16
+ * surface is not exposed here because MCP transports are JSON-only —
17
+ * passing binary through MCP would require base64 wrapping that's brittle
18
+ * and confusing for LLM clients. Use the CLI leaf if you need to upload
19
+ * a buffer programmatically.
20
+ *
21
+ * Dry-run path (issue #165): when `dryRun: true`, returns a preview of
22
+ * the `UploadProfilePhoto` mutation's GraphQL operations envelope (the
23
+ * top-level fields of the multipart form's `operations` part). The
24
+ * multipart binary payload itself is NOT described — only the GraphQL
25
+ * request shape that would be sent in the `operations` part of the
26
+ * multipart envelope. `variables.input.profileId` is the placeholder
27
+ * `DRY_RUN_PROFILE_ID_PLACEHOLDER` (apply path resolves it via a
28
+ * sibling profile read).
29
+ */
30
+ export function registerProfileBasicPhotoUploadTool(server, ctx) {
31
+ server.registerTool(TOOL_NAME, {
32
+ title: "Upload profile photo",
33
+ description: [
34
+ "Upload a new profile photo from a local image file (jpg / png / gif / webp). Image content-type is inferred from the file extension.",
35
+ "",
36
+ "Pass `dryRun: true` to preview the request without firing the mutation. The preview describes the GraphQL `operations` envelope only (not the multipart binary body).",
37
+ "",
38
+ "Example user prompts that should map to this tool:",
39
+ ' - "Upload /Users/me/Pictures/headshot.jpg as my Toptal profile photo."',
40
+ ' - "Set my profile photo to ./avatar.png."',
41
+ ].join("\n"),
42
+ inputSchema: {
43
+ file: z
44
+ .string()
45
+ .min(1)
46
+ .describe("Absolute or relative path to the image file to upload. Must be readable by the MCP server process."),
47
+ dryRun: z
48
+ .boolean()
49
+ .optional()
50
+ .describe("Preview the request without executing. Returns `{ ok: true, dryRun: true, preview }` for the UploadProfilePhoto operation; the multipart binary body is NOT enumerated (only the GraphQL operations envelope). Default: false."),
51
+ },
52
+ outputSchema: profileBasicPhotoUploadOutputSchema.shape,
53
+ }, async (input) => {
54
+ const auth = await ctx.loadTokenForTool(TOOL_NAME);
55
+ if (isToolErrorResponse(auth))
56
+ return auth;
57
+ if (input.dryRun === true) {
58
+ // The multipart envelope's `operations` part carries:
59
+ // { operationName, query, variables: { input: <UpdatePhotoInput, file=null> } }
60
+ // — the actual file lives in a separate multipart part referenced
61
+ // via the `map` field. The dry-run preview describes the operations
62
+ // envelope only (mirroring the wire shape that `photoUpload` builds
63
+ // in core/services/profile/basic/index.ts).
64
+ return dryRunResponse(buildMcpDryRunPreview("UploadProfilePhoto", "talent-profile", {
65
+ input: {
66
+ profileId: profile.basic.DRY_RUN_PROFILE_ID_PLACEHOLDER,
67
+ photo: null,
68
+ transformation: { cropped: { x: 0, y: 0, width: 0, height: 0 } },
69
+ },
70
+ }, auth.token));
71
+ }
72
+ const pathError = validateUploadPath(input.file, UPLOAD_CATEGORIES.basicPhoto);
73
+ if (pathError !== null)
74
+ return pathError;
75
+ try {
76
+ const result = await profile.basic.photoUpload(auth.token, { file: input.file });
77
+ return jsonResponse(result);
78
+ }
79
+ catch (err) {
80
+ const typed = ttctlErrorToToolResponseOrNull(err);
81
+ if (typed !== null)
82
+ return typed;
83
+ if (err instanceof profile.basic.ProfileError) {
84
+ return domainErrorResponse(TOOL_NAME, err);
85
+ }
86
+ return genericErrorResponse(TOOL_NAME, err);
87
+ }
88
+ });
89
+ }
90
+ //# sourceMappingURL=profile_basic_photo_upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile_basic_photo_upload.js","sourceRoot":"","sources":["../../src/tools/profile_basic_photo_upload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { profile } from "@ttctl/core";
3
+ import type { ProfileShowQuery } from "@ttctl/core";
4
+ import { type ToolRegistrationContext } from "./_shared.js";
5
+ /**
6
+ * Merged payload returned by `ttctl_profile_basic_show`. Mirrors the
7
+ * CLI's `BasicShowPayload` shape (`packages/cli/src/commands/profile/
8
+ * basic/show.ts`) so the read surface speaks the same vocabulary across
9
+ * MCP and CLI clients.
10
+ *
11
+ * `profile` carries the mobile-gateway `ProfileShowQuery` (identity, role,
12
+ * vertical, skills, hours, rate); `basicInfo` carries the talent-profile
13
+ * `BasicInfo` projection (`bio`, `headline`, `languages`). The two come
14
+ * from different GraphQL surfaces — the mobile-gateway `Profile` type does
15
+ * NOT publish `about` / `quote`, hence the dedicated `getBasicInfo()`
16
+ * companion shipped in #127.
17
+ *
18
+ * `basicInfo` is `null` when the secondary `getBasicInfo()` call failed in
19
+ * a non-session-level way (network blip, talent-profile `GRAPHQL_ERROR`).
20
+ * Session-level failures (`AuthRevokedError`, `Cf403Error`) propagate as
21
+ * tool errors — they indicate the bearer is no longer accepted, and the
22
+ * primary `show()` call would have hit them too.
23
+ */
24
+ export interface BasicShowPayload {
25
+ profile: ProfileShowQuery;
26
+ basicInfo: profile.basic.BasicInfo | null;
27
+ }
28
+ /**
29
+ * Register the `ttctl_profile_basic_show` MCP tool. Mirrors the
30
+ * `ttctl profile basic show` CLI leaf — fetches the signed-in user's
31
+ * profile from both the mobile-gateway and the talent-profile surfaces
32
+ * and returns the merged payload.
33
+ *
34
+ * Two-call read surface (parity with CLI's post-#129 path, closed in
35
+ * this PR for MCP per #340):
36
+ *
37
+ * 1. `profile.basic.show()` → `mobile-gateway` for identity, role,
38
+ * vertical, skills, hours, rate.
39
+ * 2. `profile.basic.getBasicInfo()` → `talent_profile/graphql` for
40
+ * `bio` (`Profile.about`), `headline` (`Profile.quote`), and
41
+ * `languages`.
42
+ *
43
+ * Errors from the secondary `getBasicInfo()` call are non-fatal: the
44
+ * tool swallows non-session failures and renders `basicInfo: null`, so a
45
+ * glitch on the secondary surface doesn't blank the whole show. Session-
46
+ * level errors (`AuthRevokedError`, `Cf403Error`) still propagate — they
47
+ * indicate the bearer is no longer valid; the primary `show()` would
48
+ * have hit them too.
49
+ *
50
+ * Auth token identifies the user. MCP is the LLM-facing surface, so the
51
+ * description includes example user-intent phrases the model can match
52
+ * against, and reflects the bio/headline/languages coverage added in
53
+ * this PR.
54
+ *
55
+ * Dry-run path (issue #165): when `dryRun: true`, the tool returns a
56
+ * structured preview of the primary `ProfileShow` query — no transport
57
+ * call. The preview shows only the mobile-gateway operation; the
58
+ * secondary `GET_BASIC_INFO` call on the talent-profile surface is also
59
+ * dispatched on the apply path but is not part of the preview envelope
60
+ * (single-op envelope is the cross-cutting dry-run contract; the
61
+ * secondary read is an implementation detail of the merged response).
62
+ */
63
+ export declare function registerProfileBasicShowTool(server: McpServer, ctx: ToolRegistrationContext): void;
64
+ //# sourceMappingURL=profile_basic_show.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile_basic_show.d.ts","sourceRoot":"","sources":["../../src/tools/profile_basic_show.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,EAOL,KAAK,uBAAuB,EAC7B,MAAM,cAAc,CAAC;AAItB;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAqElG"}