@ttctl/core 0.0.0 → 0.1.0-rc.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.
Files changed (195) hide show
  1. package/README.md +49 -9
  2. package/dist/__generated__/gateway.d.ts +4546 -0
  3. package/dist/__generated__/gateway.d.ts.map +1 -0
  4. package/dist/__generated__/gateway.js +9 -0
  5. package/dist/__generated__/gateway.js.map +1 -0
  6. package/dist/__generated__/talent-profile-zod-schemas.d.ts +1187 -0
  7. package/dist/__generated__/talent-profile-zod-schemas.d.ts.map +1 -0
  8. package/dist/__generated__/talent-profile-zod-schemas.js +1136 -0
  9. package/dist/__generated__/talent-profile-zod-schemas.js.map +1 -0
  10. package/dist/__generated__/talent-profile.d.ts +1397 -0
  11. package/dist/__generated__/talent-profile.d.ts.map +1 -0
  12. package/dist/__generated__/talent-profile.js +9 -0
  13. package/dist/__generated__/talent-profile.js.map +1 -0
  14. package/dist/__generated__/zod-schemas.d.ts +2895 -0
  15. package/dist/__generated__/zod-schemas.d.ts.map +1 -0
  16. package/dist/__generated__/zod-schemas.js +3121 -0
  17. package/dist/__generated__/zod-schemas.js.map +1 -0
  18. package/dist/__tests__/fixtures/profile/builders.d.ts +74 -0
  19. package/dist/__tests__/fixtures/profile/builders.d.ts.map +1 -0
  20. package/dist/__tests__/fixtures/profile/builders.js +196 -0
  21. package/dist/__tests__/fixtures/profile/builders.js.map +1 -0
  22. package/dist/__tests__/fixtures/profile/data.d.ts +39 -0
  23. package/dist/__tests__/fixtures/profile/data.d.ts.map +1 -0
  24. package/dist/__tests__/fixtures/profile/data.js +230 -0
  25. package/dist/__tests__/fixtures/profile/data.js.map +1 -0
  26. package/dist/__tests__/fixtures/profile/index.d.ts +9 -0
  27. package/dist/__tests__/fixtures/profile/index.d.ts.map +1 -0
  28. package/dist/__tests__/fixtures/profile/index.js +10 -0
  29. package/dist/__tests__/fixtures/profile/index.js.map +1 -0
  30. package/dist/__tests__/fixtures/profile/types.d.ts +53 -0
  31. package/dist/__tests__/fixtures/profile/types.d.ts.map +1 -0
  32. package/dist/__tests__/fixtures/profile/types.js +4 -0
  33. package/dist/__tests__/fixtures/profile/types.js.map +1 -0
  34. package/dist/auth/errors.d.ts +82 -0
  35. package/dist/auth/errors.d.ts.map +1 -0
  36. package/dist/auth/errors.js +68 -0
  37. package/dist/auth/errors.js.map +1 -0
  38. package/dist/auth.d.ts +192 -0
  39. package/dist/auth.d.ts.map +1 -0
  40. package/dist/auth.js +294 -0
  41. package/dist/auth.js.map +1 -0
  42. package/dist/config.d.ts +212 -0
  43. package/dist/config.d.ts.map +1 -0
  44. package/dist/config.js +349 -0
  45. package/dist/config.js.map +1 -0
  46. package/dist/configLock.d.ts +50 -0
  47. package/dist/configLock.d.ts.map +1 -0
  48. package/dist/configLock.js +88 -0
  49. package/dist/configLock.js.map +1 -0
  50. package/dist/configWriter.d.ts +97 -0
  51. package/dist/configWriter.d.ts.map +1 -0
  52. package/dist/configWriter.js +687 -0
  53. package/dist/configWriter.js.map +1 -0
  54. package/dist/index.d.ts +37 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +28 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/kill-switch.d.ts +161 -0
  59. package/dist/kill-switch.d.ts.map +1 -0
  60. package/dist/kill-switch.js +235 -0
  61. package/dist/kill-switch.js.map +1 -0
  62. package/dist/lib/date.d.ts +58 -0
  63. package/dist/lib/date.d.ts.map +1 -0
  64. package/dist/lib/date.js +104 -0
  65. package/dist/lib/date.js.map +1 -0
  66. package/dist/lib/diagnostic-log.d.ts +159 -0
  67. package/dist/lib/diagnostic-log.d.ts.map +1 -0
  68. package/dist/lib/diagnostic-log.js +186 -0
  69. package/dist/lib/diagnostic-log.js.map +1 -0
  70. package/dist/lib/package-version.d.ts +19 -0
  71. package/dist/lib/package-version.d.ts.map +1 -0
  72. package/dist/lib/package-version.js +38 -0
  73. package/dist/lib/package-version.js.map +1 -0
  74. package/dist/lib/redact.d.ts +153 -0
  75. package/dist/lib/redact.d.ts.map +1 -0
  76. package/dist/lib/redact.js +207 -0
  77. package/dist/lib/redact.js.map +1 -0
  78. package/dist/lib/text.d.ts +14 -0
  79. package/dist/lib/text.d.ts.map +1 -0
  80. package/dist/lib/text.js +21 -0
  81. package/dist/lib/text.js.map +1 -0
  82. package/dist/lib/wire-shape.d.ts +131 -0
  83. package/dist/lib/wire-shape.d.ts.map +1 -0
  84. package/dist/lib/wire-shape.js +376 -0
  85. package/dist/lib/wire-shape.js.map +1 -0
  86. package/dist/onepassword.d.ts +29 -0
  87. package/dist/onepassword.d.ts.map +1 -0
  88. package/dist/onepassword.js +112 -0
  89. package/dist/onepassword.js.map +1 -0
  90. package/dist/services/_shared/transport.d.ts +148 -0
  91. package/dist/services/_shared/transport.d.ts.map +1 -0
  92. package/dist/services/_shared/transport.js +102 -0
  93. package/dist/services/_shared/transport.js.map +1 -0
  94. package/dist/services/applications/index.d.ts +210 -0
  95. package/dist/services/applications/index.d.ts.map +1 -0
  96. package/dist/services/applications/index.js +240 -0
  97. package/dist/services/applications/index.js.map +1 -0
  98. package/dist/services/availability/index.d.ts +254 -0
  99. package/dist/services/availability/index.d.ts.map +1 -0
  100. package/dist/services/availability/index.js +310 -0
  101. package/dist/services/availability/index.js.map +1 -0
  102. package/dist/services/contracts/index.d.ts +132 -0
  103. package/dist/services/contracts/index.d.ts.map +1 -0
  104. package/dist/services/contracts/index.js +211 -0
  105. package/dist/services/contracts/index.js.map +1 -0
  106. package/dist/services/engagements/index.d.ts +504 -0
  107. package/dist/services/engagements/index.d.ts.map +1 -0
  108. package/dist/services/engagements/index.js +613 -0
  109. package/dist/services/engagements/index.js.map +1 -0
  110. package/dist/services/jobs/index.d.ts +490 -0
  111. package/dist/services/jobs/index.d.ts.map +1 -0
  112. package/dist/services/jobs/index.js +753 -0
  113. package/dist/services/jobs/index.js.map +1 -0
  114. package/dist/services/payments/index.d.ts +415 -0
  115. package/dist/services/payments/index.d.ts.map +1 -0
  116. package/dist/services/payments/index.js +636 -0
  117. package/dist/services/payments/index.js.map +1 -0
  118. package/dist/services/profile/__tests__/fixtures.d.ts +214 -0
  119. package/dist/services/profile/__tests__/fixtures.d.ts.map +1 -0
  120. package/dist/services/profile/__tests__/fixtures.js +176 -0
  121. package/dist/services/profile/__tests__/fixtures.js.map +1 -0
  122. package/dist/services/profile/basic/index.d.ts +390 -0
  123. package/dist/services/profile/basic/index.d.ts.map +1 -0
  124. package/dist/services/profile/basic/index.js +1007 -0
  125. package/dist/services/profile/basic/index.js.map +1 -0
  126. package/dist/services/profile/certifications/index.d.ts +74 -0
  127. package/dist/services/profile/certifications/index.d.ts.map +1 -0
  128. package/dist/services/profile/certifications/index.js +169 -0
  129. package/dist/services/profile/certifications/index.js.map +1 -0
  130. package/dist/services/profile/education/index.d.ts +73 -0
  131. package/dist/services/profile/education/index.d.ts.map +1 -0
  132. package/dist/services/profile/education/index.js +168 -0
  133. package/dist/services/profile/education/index.js.map +1 -0
  134. package/dist/services/profile/employment/index.d.ts +111 -0
  135. package/dist/services/profile/employment/index.d.ts.map +1 -0
  136. package/dist/services/profile/employment/index.js +202 -0
  137. package/dist/services/profile/employment/index.js.map +1 -0
  138. package/dist/services/profile/external/index.d.ts +219 -0
  139. package/dist/services/profile/external/index.d.ts.map +1 -0
  140. package/dist/services/profile/external/index.js +560 -0
  141. package/dist/services/profile/external/index.js.map +1 -0
  142. package/dist/services/profile/index.d.ts +24 -0
  143. package/dist/services/profile/index.d.ts.map +1 -0
  144. package/dist/services/profile/index.js +26 -0
  145. package/dist/services/profile/index.js.map +1 -0
  146. package/dist/services/profile/industries/index.d.ts +130 -0
  147. package/dist/services/profile/industries/index.d.ts.map +1 -0
  148. package/dist/services/profile/industries/index.js +292 -0
  149. package/dist/services/profile/industries/index.js.map +1 -0
  150. package/dist/services/profile/portfolio/index.d.ts +352 -0
  151. package/dist/services/profile/portfolio/index.d.ts.map +1 -0
  152. package/dist/services/profile/portfolio/index.js +833 -0
  153. package/dist/services/profile/portfolio/index.js.map +1 -0
  154. package/dist/services/profile/resume/index.d.ts +60 -0
  155. package/dist/services/profile/resume/index.d.ts.map +1 -0
  156. package/dist/services/profile/resume/index.js +212 -0
  157. package/dist/services/profile/resume/index.js.map +1 -0
  158. package/dist/services/profile/reviews/index.d.ts +137 -0
  159. package/dist/services/profile/reviews/index.d.ts.map +1 -0
  160. package/dist/services/profile/reviews/index.js +431 -0
  161. package/dist/services/profile/reviews/index.js.map +1 -0
  162. package/dist/services/profile/shared.d.ts +127 -0
  163. package/dist/services/profile/shared.d.ts.map +1 -0
  164. package/dist/services/profile/shared.js +155 -0
  165. package/dist/services/profile/shared.js.map +1 -0
  166. package/dist/services/profile/skills/index.d.ts +212 -0
  167. package/dist/services/profile/skills/index.d.ts.map +1 -0
  168. package/dist/services/profile/skills/index.js +461 -0
  169. package/dist/services/profile/skills/index.js.map +1 -0
  170. package/dist/services/profile/visas/index.d.ts +74 -0
  171. package/dist/services/profile/visas/index.d.ts.map +1 -0
  172. package/dist/services/profile/visas/index.js +306 -0
  173. package/dist/services/profile/visas/index.js.map +1 -0
  174. package/dist/services/timesheet/index.d.ts +326 -0
  175. package/dist/services/timesheet/index.d.ts.map +1 -0
  176. package/dist/services/timesheet/index.js +324 -0
  177. package/dist/services/timesheet/index.js.map +1 -0
  178. package/dist/services/translations.d.ts +79 -0
  179. package/dist/services/translations.d.ts.map +1 -0
  180. package/dist/services/translations.js +136 -0
  181. package/dist/services/translations.js.map +1 -0
  182. package/dist/transport-resilience.d.ts +136 -0
  183. package/dist/transport-resilience.d.ts.map +1 -0
  184. package/dist/transport-resilience.js +247 -0
  185. package/dist/transport-resilience.js.map +1 -0
  186. package/dist/transport.d.ts +408 -0
  187. package/dist/transport.d.ts.map +1 -0
  188. package/dist/transport.js +691 -0
  189. package/dist/transport.js.map +1 -0
  190. package/dist/types.d.ts +41 -0
  191. package/dist/types.d.ts.map +1 -0
  192. package/dist/types.js +18 -0
  193. package/dist/types.js.map +1 -0
  194. package/package.json +40 -12
  195. package/index.js +0 -7
@@ -0,0 +1,155 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { AuthRevokedError, TtctlError } from "../../auth/errors.js";
4
+ import { buildWireShapeError } from "../../lib/wire-shape.js";
5
+ import { impersonatedTransport } from "../../transport.js";
6
+ import { show as basicShow, ProfileError } from "./basic/index.js";
7
+ /**
8
+ * Returns `true` when `extensions.code` on a GraphQL error indicates the
9
+ * session token has been revoked or expired and the user must re-run
10
+ * `ttctl auth signin`.
11
+ *
12
+ * Recognized stable codes (accumulated across surfaces and history):
13
+ *
14
+ * - `'UNAUTHENTICATED'` — talent-profile (Cloudflare-protected, web-portal API)
15
+ * - `'AUTHENTICATION_REQUIRED'` — defensive carryover from #77 (provenance unverified;
16
+ * see "Empirical history" below)
17
+ * - `'UNAUTHORIZED'` — mobile-gateway (federated `talent_schema` subgraph,
18
+ * empirically observed 2026-05-07 for invalid bearer
19
+ * tokens; see `research/notes/14-auth-error-extensions-code.md`)
20
+ *
21
+ * All three collapse to `AuthRevokedError` so the CLI / MCP surfaces apply a
22
+ * uniform "Run `ttctl auth signin`" recovery hint regardless of which surface
23
+ * raised the failure.
24
+ *
25
+ * **Empirical history** (issue #89): the original predicate (#77) recognized
26
+ * only the first two codes. Live mobile-gateway capture for an invalid bearer
27
+ * token returned HTTP 200 with `errors[0].extensions.code = 'UNAUTHORIZED'`
28
+ * (NOT `'AUTHENTICATION_REQUIRED'` as documentation suggested). All token-
29
+ * shape variants (corrupted, malformed, no-auth, plausible-but-invalid) flow
30
+ * through the same code path with identical responses, so this single code
31
+ * covers every "gateway can't resolve the token to an account" case.
32
+ *
33
+ * Future drift: a server-side rename adds another `||` clause here. New codes
34
+ * MUST be empirically captured before being added — see the research note for
35
+ * the capture procedure.
36
+ */
37
+ export function isAuthRevokedExtensionCode(code) {
38
+ return code === "UNAUTHENTICATED" || code === "AUTHENTICATION_REQUIRED" || code === "UNAUTHORIZED";
39
+ }
40
+ /**
41
+ * Resolve the signed-in user's `profileId` for use in mutation inputs that
42
+ * follow Pattern 2 (`{ profileId, <entity>: <Entity>Input }`) — see
43
+ * `research/notes/10-mutation-input-patterns.md`. Lazily fetches the
44
+ * profile via `profile.basic.show()` against the mobile-gateway surface,
45
+ * then extracts `viewerRole.profileId`.
46
+ *
47
+ * Errors propagate verbatim — a write attempt that can't read its own
48
+ * profile is unrecoverable, and surfacing the read-side error gives the
49
+ * user the same actionable message as `ttctl profile show`.
50
+ */
51
+ export async function extractProfileId(token) {
52
+ const profile = await basicShow(token);
53
+ const profileId = profile.viewer?.viewerRole.profileId;
54
+ if (profileId === undefined) {
55
+ throw new ProfileError("NO_VIEWER", "Cannot resolve profileId: viewer or profile id missing from the session response.");
56
+ }
57
+ return profileId;
58
+ }
59
+ /**
60
+ * Fire a GraphQL request against the talent-profile surface and turn
61
+ * transport-level outcomes into typed errors consistent across the four
62
+ * sub-domains landing in #74.
63
+ *
64
+ * Error mapping:
65
+ * - Underlying typed errors (`TtctlError` subclasses — `Cf403Error`,
66
+ * `AuthRevokedError`, …) propagate as-is so the CLI / MCP surfaces can
67
+ * render their `recovery` hints.
68
+ * - Other transport throws become `ProfileError("NETWORK_ERROR")` with
69
+ * the underlying message tagged by `verb` (e.g. "education list").
70
+ * - HTTP 401 becomes `AuthRevokedError`.
71
+ * - Non-2xx becomes `ProfileError("UNKNOWN")`.
72
+ *
73
+ * Top-level GraphQL `errors` are NOT inspected here — callers handle
74
+ * those via {@link ensureNoTopLevelErrors} once they've narrowed the body
75
+ * to their expected shape.
76
+ *
77
+ * Optional `schema` parameter (Z-3 / #286): when provided, the
78
+ * response `body.data` is validated against the schema as a SIDE
79
+ * EFFECT before the helper returns; on `ZodError` the call throws
80
+ * `ProfileError("WIRE_SHAPE_ERROR")` with the original ZodError
81
+ * chained via `cause` and a field-level diff in the message per
82
+ * `docs/wire-validation-error-format.md`. The return shape is
83
+ * unchanged (`TransportResponse`) — callers still narrow `res.body`
84
+ * to their per-operation type. Schema validation is a guard rail; it
85
+ * does NOT mutate the response. When omitted, the existing pass-
86
+ * through behavior is preserved. No production op wires `schema` in
87
+ * Wave 3; Z-4 (#288) ships the first beachhead.
88
+ */
89
+ export async function callTalentProfile(token, operationName, query, variables, verb, schema) {
90
+ let res;
91
+ try {
92
+ res = await impersonatedTransport({
93
+ surface: "talent-profile",
94
+ authToken: token,
95
+ body: { operationName, query, variables },
96
+ });
97
+ }
98
+ catch (err) {
99
+ if (err instanceof TtctlError)
100
+ throw err;
101
+ throw new ProfileError("NETWORK_ERROR", `${verb} request failed: ${err.message}`, {
102
+ cause: err,
103
+ });
104
+ }
105
+ if (res.status === 401)
106
+ throw new AuthRevokedError("Session is invalid or expired.");
107
+ if (res.status < 200 || res.status >= 300) {
108
+ throw new ProfileError("UNKNOWN", `${verb} returned HTTP ${res.status.toString()}`);
109
+ }
110
+ if (schema !== undefined) {
111
+ const body = res.body;
112
+ if (body?.data !== undefined && body.data !== null) {
113
+ const parsed = schema.safeParse(body.data);
114
+ if (!parsed.success) {
115
+ const payload = buildWireShapeError(operationName, parsed.error, body.data);
116
+ throw new ProfileError("WIRE_SHAPE_ERROR", payload.message, { cause: parsed.error });
117
+ }
118
+ }
119
+ }
120
+ return res;
121
+ }
122
+ /**
123
+ * Throw a typed error when the GraphQL response carries a non-empty
124
+ * top-level `errors` array. Auth-revoked codes (per
125
+ * {@link isAuthRevokedExtensionCode}) become `AuthRevokedError`; anything
126
+ * else becomes `ProfileError("GRAPHQL_ERROR")` tagged by `verb`.
127
+ */
128
+ export function ensureNoTopLevelErrors(body, verb) {
129
+ if (body && Array.isArray(body.errors) && body.errors.length > 0) {
130
+ const first = body.errors[0];
131
+ if (isAuthRevokedExtensionCode(first?.extensions?.code)) {
132
+ throw new AuthRevokedError("Session is invalid or expired.");
133
+ }
134
+ throw new ProfileError("GRAPHQL_ERROR", `${verb} failed: ${first?.message ?? "GraphQL error"}`);
135
+ }
136
+ }
137
+ /**
138
+ * Inspect a mutation payload for `success === false` or a non-empty
139
+ * `errors` array and convert either into `ProfileError("USER_ERROR")`. The
140
+ * common shape across the four sub-domains' mutation payloads is
141
+ * `{ success?, notice?, errors?: UserError[] }`; this helper handles
142
+ * exactly that subset and lets each sub-domain narrow the rest of the
143
+ * payload itself.
144
+ */
145
+ export function applyUserErrorsAndSuccess(payload, verb) {
146
+ if (Array.isArray(payload.errors) && payload.errors.length > 0) {
147
+ const first = payload.errors[0];
148
+ const fieldHint = first?.key ? ` (${first.key})` : "";
149
+ throw new ProfileError("USER_ERROR", `${verb} rejected${fieldHint}: ${first?.message ?? "unknown error"}`);
150
+ }
151
+ if (payload.success === false) {
152
+ throw new ProfileError("USER_ERROR", `${verb} reported success=false${payload.notice ? `: ${payload.notice}` : ""}`);
153
+ }
154
+ }
155
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/services/profile/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Skills-domain error codes. Mirrors `profile.basic.ProfileErrorCode`'s
3
+ * shape (NO_VIEWER, GRAPHQL_ERROR, NETWORK_ERROR, USER_ERROR, VALIDATION_ERROR,
4
+ * UNKNOWN) — the duplication is intentional so each sub-domain can carry
5
+ * its own typed error class without callers having to import a shared
6
+ * cross-domain enum. Auth-revoked failures throw `AuthRevokedError`
7
+ * (cross-cutting `TtctlError` subclass per issue #77), not a code on this
8
+ * enum.
9
+ */
10
+ export type SkillsErrorCode = "NO_VIEWER" | "GRAPHQL_ERROR" | "NETWORK_ERROR" | "USER_ERROR" | "VALIDATION_ERROR" | "PARTIAL_FAILURE" | "WIRE_SHAPE_ERROR" | "UNKNOWN";
11
+ export declare class SkillsError extends Error {
12
+ readonly code: SkillsErrorCode;
13
+ readonly name = "SkillsError";
14
+ constructor(code: SkillsErrorCode, message: string, options?: {
15
+ cause?: unknown;
16
+ });
17
+ }
18
+ /**
19
+ * Proficiency rating on a `ProfileSkillSet`. Confirmed enum values from
20
+ * `research/notes/10-mutation-input-patterns.md`. The wire-format `NOVICE`
21
+ * variant is documented as "possibly" present but not exercised by the
22
+ * write path — accept-on-read, validate-on-write.
23
+ */
24
+ export type ProficiencyRating = "COMPETENT" | "STRONG" | "EXPERT" | "NOVICE";
25
+ /**
26
+ * Visibility on a `ProfileSkillSet`. Boolean wire-format value drives the
27
+ * `--public` / `--private` CLI flag pair (CLI translates either flag to
28
+ * the same boolean before calling `set()`).
29
+ */
30
+ export type SkillVisibility = boolean;
31
+ /**
32
+ * Full skill set as exposed to consumers. Mirrors the GraphQL
33
+ * `ProfileSkillSet` selection set we ask for, normalised so optional
34
+ * fields surface as `null` rather than `undefined` (matches the
35
+ * `profile.basic` convention).
36
+ */
37
+ export interface ProfileSkillSet {
38
+ id: string;
39
+ experience: number | null;
40
+ rating: ProficiencyRating | null;
41
+ public: boolean;
42
+ position: number | null;
43
+ skill: {
44
+ id: string;
45
+ name: string;
46
+ };
47
+ connectionsCount: number;
48
+ }
49
+ /** Skill suggestion returned by `autocomplete`. */
50
+ export interface SkillSuggestion {
51
+ id: string;
52
+ name: string;
53
+ }
54
+ /** Skill-readiness flags. Each boolean reflects a sub-criterion. */
55
+ export interface SkillsReadiness {
56
+ isExpertProficiencyCountSatisfied: boolean;
57
+ isHighlightedItemsCountAndExperienceSatisfied: boolean;
58
+ isItemsCountSatisfied: boolean;
59
+ isProficiencyNotSetCountSatisfied: boolean;
60
+ isProgrammingLanguageSatisfied: boolean;
61
+ }
62
+ /**
63
+ * Multi-flag input for {@link set}. AT LEAST one of `rating` /
64
+ * `experience` / `public` must be supplied; an empty object is rejected
65
+ * with `VALIDATION_ERROR` (callers can detect the "user provided no
66
+ * flags" case at the CLI / MCP layer too — the duplicate gate keeps the
67
+ * service self-defending).
68
+ *
69
+ * `experience` is the talent's total years/months on this skill,
70
+ * expressed as an integer count of months on the wire; the CLI accepts a
71
+ * duration string (`"5y"`, `"60"`) and converts before calling.
72
+ */
73
+ export interface SkillUpdate {
74
+ rating?: ProficiencyRating;
75
+ experience?: number;
76
+ public?: boolean;
77
+ }
78
+ /**
79
+ * Result of {@link set}. Records the post-update server-confirmed state
80
+ * for each field that was touched, plus the collected `notice` strings
81
+ * (one per fired mutation). Callers display the notices verbatim — the
82
+ * server uses them for soft signals like "Profile review may be required".
83
+ */
84
+ export interface UpdateSkillResult {
85
+ id: string;
86
+ rating: ProficiencyRating | null;
87
+ experience: number | null;
88
+ public: boolean | null;
89
+ notices: string[];
90
+ }
91
+ /**
92
+ * Add a skill to the signed-in user's profile. Identifies the skill by
93
+ * its catalog name (e.g. `"TypeScript"`); the server resolves the name to
94
+ * a `Skill` id under the hood and returns the newly-attached
95
+ * `ProfileSkillSet` with default `rating`/`experience`/`public` values.
96
+ *
97
+ * Use {@link autocomplete} first if the caller needs to disambiguate
98
+ * between candidate skills (e.g., "Postgres" vs "PostgreSQL"). Once the
99
+ * skill is added, configure proficiency via {@link set}.
100
+ *
101
+ * Errors:
102
+ * - `SkillsError` `VALIDATION_ERROR` when `name` is empty or whitespace-only.
103
+ * - `SkillsError` `USER_ERROR` when the server reports a domain failure
104
+ * (e.g., skill already on profile, name not in catalog).
105
+ * - `AuthRevokedError`, `Cf403Error`, plus the standard
106
+ * `GRAPHQL_ERROR`/`NETWORK_ERROR`/`UNKNOWN` codes from the shared
107
+ * transport-error path.
108
+ */
109
+ export declare function add(token: string, name: string): Promise<ProfileSkillSet>;
110
+ /**
111
+ * Remove a skill from the signed-in user's profile by its ProfileSkillSet
112
+ * id (NOT the Skill catalog id). Removal cascades to any connections the
113
+ * skill set held to portfolio items, education, employment, or
114
+ * certifications — the server cleans those up server-side, no per-edge
115
+ * call required from the client.
116
+ *
117
+ * Errors:
118
+ * - `SkillsError` `VALIDATION_ERROR` when `id` is empty.
119
+ * - `SkillsError` `USER_ERROR` when the id doesn't match a skill set on
120
+ * the user's profile (caller used a stale id).
121
+ * - Standard transport-error path (auth-revoked / Cf403 / GraphQL /
122
+ * network / unknown).
123
+ */
124
+ export declare function rm(token: string, id: string): Promise<void>;
125
+ /**
126
+ * Update one or more fields on an existing skill set. Multi-flag atomic
127
+ * semantics: each provided field fires its own GraphQL mutation
128
+ * (`UPDATE_PROFILE_SKILL_SET_RATING` / `_EXPERIENCE` / `_PUBLICITY`)
129
+ * sequentially. **Partial-failure behavior**: if mutation N succeeds but
130
+ * mutation N+1 fails, the prior changes are NOT rolled back — the server
131
+ * lacks a multi-field atomic path, so the client serialises the updates
132
+ * and reports a `PARTIAL_FAILURE` error carrying which fields landed and
133
+ * which didn't. Callers re-issue only the missing fields after handling
134
+ * the failure.
135
+ *
136
+ * The mutation order is `rating → experience → public`, deterministic so
137
+ * tests can predict the call sequence.
138
+ *
139
+ * Errors:
140
+ * - `SkillsError` `VALIDATION_ERROR` when `id` is empty OR no fields
141
+ * are supplied.
142
+ * - `SkillsError` `PARTIAL_FAILURE` when the second or third mutation
143
+ * fails after at least one earlier mutation succeeded. The error
144
+ * message names the failing field and the underlying message; the
145
+ * error's `cause` carries the per-field failure for callers that want
146
+ * structured access. The successful fields are reflected in the
147
+ * `result` snapshot included on the error.
148
+ * - `SkillsError` `USER_ERROR` when the FIRST mutation fails — caller
149
+ * sees the same shape they'd see from a single-flag invocation.
150
+ * - Standard transport-error path.
151
+ */
152
+ export declare function set(token: string, id: string, fields: SkillUpdate): Promise<UpdateSkillResult>;
153
+ /**
154
+ * Fetch a single skill set by its id, including its connection edges to
155
+ * portfolio items / employment / education / certifications. Useful when
156
+ * the caller already has the id (from `list()` or a recent mutation) and
157
+ * needs to display the full detail without paging.
158
+ *
159
+ * Errors:
160
+ * - `SkillsError` `VALIDATION_ERROR` when `id` is empty.
161
+ * - `SkillsError` `USER_ERROR` when the id doesn't resolve to a
162
+ * `ProfileSkillSet` (server returns `node: null`).
163
+ * - Standard transport-error path.
164
+ */
165
+ export declare function show(token: string, id: string): Promise<ProfileSkillSet>;
166
+ /**
167
+ * List every skill set on the signed-in user's profile, with each entry
168
+ * carrying its connection-count summary (the digits the portal shows next
169
+ * to "linked to N items"). Order matches the server's `position` field;
170
+ * callers that need a specific sort apply it after fetching.
171
+ *
172
+ * The talent-profile surface keys this query on `profileId` rather than
173
+ * resolving it server-side from the auth token, so the caller passes the
174
+ * `profileId` it has on hand (typically obtained from `profile.basic.show`
175
+ * or cached for the session).
176
+ *
177
+ * Errors:
178
+ * - `SkillsError` `VALIDATION_ERROR` when `profileId` is empty.
179
+ * - `SkillsError` `USER_ERROR` when the profile id doesn't resolve.
180
+ * - Standard transport-error path.
181
+ */
182
+ export declare function list(token: string, profileId: string): Promise<ProfileSkillSet[]>;
183
+ /**
184
+ * Search the global skill catalog for entries matching `search`,
185
+ * suitable for populating an autocomplete dropdown. The server scopes
186
+ * results to skills the talent's vertical permits and excludes any ids
187
+ * passed in `withoutIds` (typically the talent's existing skill-set
188
+ * skill ids, so the dropdown doesn't suggest skills they already have).
189
+ *
190
+ * `limit` defaults to 10 (matches the portal's default page size).
191
+ *
192
+ * Errors:
193
+ * - `SkillsError` `VALIDATION_ERROR` when `profileId` or `search` is empty.
194
+ * - Standard transport-error path.
195
+ */
196
+ export declare function autocomplete(token: string, profileId: string, search: string, options?: {
197
+ limit?: number;
198
+ withoutIds?: string[];
199
+ }): Promise<SkillSuggestion[]>;
200
+ /**
201
+ * Fetch the skill-readiness snapshot for the user's profile — the same
202
+ * heuristics the portal uses to gate the "ready to apply" state on the
203
+ * skills section. Useful for surfacing a checklist of remaining tasks
204
+ * via CLI or MCP.
205
+ *
206
+ * Errors:
207
+ * - `SkillsError` `VALIDATION_ERROR` when `profileId` is empty.
208
+ * - `SkillsError` `USER_ERROR` when the profile id doesn't resolve.
209
+ * - Standard transport-error path.
210
+ */
211
+ export declare function readiness(token: string, profileId: string): Promise<SkillsReadiness>;
212
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/services/profile/skills/index.ts"],"names":[],"mappings":"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"}