@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
package/dist/auth.d.ts ADDED
@@ -0,0 +1,192 @@
1
+ import type { AuthCredentials } from "./config.js";
2
+ import type { Credentials } from "./types.js";
3
+ export type SignInErrorCode = "INVALID_CREDENTIALS" | "MFA_REQUIRED" | "NETWORK_ERROR" | "UNKNOWN";
4
+ export declare class SignInError extends Error {
5
+ readonly code: SignInErrorCode;
6
+ readonly name = "SignInError";
7
+ constructor(code: SignInErrorCode, message: string, options?: {
8
+ cause?: unknown;
9
+ });
10
+ }
11
+ /**
12
+ * Collapse the polymorphic credentials value into resolved credentials.
13
+ *
14
+ * Form A (string) → `op://[account/]vault/item` → resolved via `op` CLI
15
+ * Form B (object) → literal `{ username, password }` (username is an email
16
+ * per Toptal's `EmailPasswordSignIn` mutation contract;
17
+ * the YAML field is named `username` to match 1Password's
18
+ * USERNAME purpose semantics)
19
+ *
20
+ * Returns the internal `Credentials` shape `{ email, password }` — that's
21
+ * the GraphQL parameter name for the mutation. The username/email
22
+ * synonymy is documented and load-bearing.
23
+ */
24
+ export declare function resolveCredentials(auth: AuthCredentials): Credentials;
25
+ /**
26
+ * Sign in to Toptal Talent via `EmailPasswordSignIn` GraphQL mutation in
27
+ * persisted-query mode against the mobile gateway and capture the session
28
+ * token from the response.
29
+ *
30
+ * Returns the captured token. The CLI persists it back to the same YAML
31
+ * config file (under `auth.token`) via `persistAuthToken` so subsequent
32
+ * invocations can authenticate via `Authorization: Token token=<X>`
33
+ * without re-signing-in.
34
+ *
35
+ * The next authenticated call (e.g. `auth status`) is the de-facto session
36
+ * verifier — we do NOT issue a redundant Viewer probe inside `signIn` itself.
37
+ *
38
+ * See `hq/engineering/adr/ADR-005-auth-model.md` for the cross-surface
39
+ * auth model and `research/notes/02-auth-and-clients.md` for the empirical
40
+ * evidence behind it.
41
+ *
42
+ * Throws `SignInError("UNKNOWN", ...)` if the gateway reports `success: true`
43
+ * but does not return a token — that response shape is malformed and the
44
+ * caller has nothing useful to persist.
45
+ */
46
+ export declare function signIn(credentials: Credentials): Promise<{
47
+ token: string;
48
+ }>;
49
+ /**
50
+ * Result of a session-validity probe. Three terminal shapes:
51
+ *
52
+ * - `valid` — `Viewer` query returned 2xx with an email; the session is
53
+ * active and bound to that account.
54
+ * - `invalid` — no token persisted, OR the gateway responded with a
55
+ * non-2xx status, OR the response payload lacks `viewer.viewerRole.email`.
56
+ * `reason` carries a stable machine-readable code so the CLI can pick the
57
+ * right user-facing message without re-classifying the failure.
58
+ * - `unreachable` — the transport itself rejected (DNS failure, connect
59
+ * refused, TLS handshake timeout, etc.); the gateway was never reached.
60
+ * This is distinguished from `invalid` so the CLI can exit 2 (transient,
61
+ * retryable) rather than 1 (auth problem, action required).
62
+ */
63
+ export type AuthStatusResult = {
64
+ status: "valid";
65
+ email: string;
66
+ } | {
67
+ status: "invalid";
68
+ reason: AuthInvalidReason;
69
+ } | {
70
+ status: "unreachable";
71
+ reason: string;
72
+ };
73
+ export type AuthInvalidReason = "no-session" | "session-expired" | "no-email-in-response" | "unexpected-status";
74
+ /**
75
+ * Probe whether the persisted session token is currently valid.
76
+ *
77
+ * Issues a minimal full-doc `Viewer` query against the mobile gateway,
78
+ * authenticated with `Authorization: Token token=<X>`. The cataloged
79
+ * persisted `Viewer` query in `research/graphql/gateway/operations/mobile/Viewer.graphql`
80
+ * does not return `viewerRole.email`, so we can't use APQ here — see
81
+ * `VIEWER_VERIFY_QUERY` doc.
82
+ *
83
+ * Pass `null` (or an empty string) when no token has been persisted yet;
84
+ * the function short-circuits to `invalid/no-session` without touching the
85
+ * network. CLI consumers pass `config.auth.token ?? null` from the
86
+ * already-loaded `TtctlConfig` (no separate token file to read).
87
+ *
88
+ * Never throws on auth or network failure; classifies into the three
89
+ * `AuthStatusResult` shapes so CLI consumers can map cleanly to exit codes
90
+ * and output messages without re-parsing exceptions.
91
+ */
92
+ export declare function getAuthStatus(token: string | null): Promise<AuthStatusResult>;
93
+ /**
94
+ * Stable machine-readable reason codes returned alongside `SignOutResult`'s
95
+ * `invalid` and `unreachable` branches. The CLI / E2E teardown render
96
+ * user-facing messages from these — keep the code stable, fold UX shifts
97
+ * into the renderer.
98
+ */
99
+ export type SignOutInvalidReason = "no-session" | "session-expired" | "graphql-auth-error";
100
+ export type SignOutUnreachableReason = {
101
+ kind: "transport";
102
+ reason: string;
103
+ } | {
104
+ kind: "http-status";
105
+ status: number;
106
+ } | {
107
+ kind: "graphql-error";
108
+ message: string;
109
+ } | {
110
+ kind: "payload-missing";
111
+ } | {
112
+ kind: "success-false";
113
+ };
114
+ /**
115
+ * Result of a `signOut(token)` call. Three terminal shapes mirror
116
+ * `AuthStatusResult`'s discriminator so call-sites can pattern-match
117
+ * uniformly:
118
+ *
119
+ * - `logged-out` — talent-profile responded 2xx and
120
+ * `data.logOut.success === true`. The `LogOut` mutation flow on the
121
+ * `talent_profile/graphql` surface completed; the server acknowledged
122
+ * the signal. **This status name is deliberate** — see the
123
+ * "Bearer-invalidation scope" note below. The status does NOT claim
124
+ * the captured bearer was revoked for downstream mobile-gateway
125
+ * traffic; empirical evidence (issue #180 live discovery, 2026-05-12)
126
+ * shows it is NOT. The status only claims the LogOut mutation itself
127
+ * was processed.
128
+ * - `invalid` — the bearer was ALREADY invalid (401/403 or a GraphQL
129
+ * auth-revoke code). Functionally equivalent to "already revoked" from
130
+ * the security standpoint — the caller's intent (kill the bearer) is
131
+ * satisfied. CLI / teardown should still proceed to clear the local copy.
132
+ * - `unreachable` — could not reach the server or could not confirm
133
+ * mutation processing. The LogOut signal was not delivered. Caller
134
+ * MUST fall through to local clear (per CLI UX recommendation) and
135
+ * rely on the 24-72h aging-out as the load-bearing revocation defense.
136
+ *
137
+ * **Bearer-invalidation scope** (empirical, 2026-05-12; investigation
138
+ * captured in `.tmp/180-delayed-probe-report.json`): The `LogOut` mutation
139
+ * on `talent_profile/graphql` succeeds (`data.logOut.success === true`)
140
+ * but does NOT propagate to bearer invalidation for subsequent
141
+ * `mobile-gateway` calls. Probed across t=0/30/60/180/300s; the bearer
142
+ * remained `{ status: "valid" }` against `getAuthStatus` for the entire
143
+ * 5-minute window. The 24-72h natural aging-out (documented in CLAUDE.md
144
+ * § Auth Model) is the load-bearing revocation defense. `signOut()` is
145
+ * defense-in-depth: audit log + web-session/cookie cleanup on the
146
+ * talent_profile side + forward-compatible call site if Toptal ever wires
147
+ * up server-side bearer revocation.
148
+ *
149
+ * Never throws — every failure is classified into one of the three shapes.
150
+ * This matches `getAuthStatus`'s contract so the call-sites that branch on
151
+ * `result.status` look the same on both paths.
152
+ */
153
+ export type SignOutResult = {
154
+ status: "logged-out";
155
+ } | {
156
+ status: "invalid";
157
+ reason: SignOutInvalidReason;
158
+ } | {
159
+ status: "unreachable";
160
+ reason: SignOutUnreachableReason;
161
+ };
162
+ /**
163
+ * Issue the `LogOut` mutation server-side against the Cloudflare-protected
164
+ * `talent_profile/graphql` surface (defense-in-depth audit-log signal +
165
+ * web-session/cookie cleanup; does NOT revoke the captured bearer for
166
+ * downstream mobile-gateway calls per empirical scope — see CLAUDE.md §
167
+ * Auth Model and the `SignOutResult` type docblock above). Authenticates
168
+ * with `Authorization: Token token=<X>` (same bearer that `signIn`
169
+ * captured — see ADR-005).
170
+ *
171
+ * Pass `null` or an empty string when there's no token to log out; the
172
+ * function short-circuits to `invalid/no-session` without touching the
173
+ * network. CLI / teardown call sites that already check for token absence
174
+ * before invoking this are still safe — the defensive short-circuit just
175
+ * removes one decision from the call site.
176
+ *
177
+ * **Never throws.** Every transport, HTTP, GraphQL-level, or wire-shape
178
+ * failure is classified into one of the three `SignOutResult` shapes so
179
+ * call sites can branch on `result.status` without try/catch. The CLI
180
+ * `runAuthSignOut` and `runGlobalTeardown` rely on this contract to keep
181
+ * the local-clear path unconditional (the bearer aging out in 24-72h is
182
+ * the second-line defense if server-side revocation cannot be confirmed).
183
+ *
184
+ * Schema/contract status: the operation document and `LogOutPayload` field
185
+ * shapes are INFERRED — `Unknown` in the SDL. The live wire format is
186
+ * verified by `packages/e2e/src/97-auth-signout-server-side.e2e.test.ts`
187
+ * (TTCTL_E2E=1 gate). If the live response surfaces fields not yet
188
+ * declared here, expand the inline `LogOutPayload` interface — codegen
189
+ * wiring would produce `unknown`-typed fields and is not currently useful.
190
+ */
191
+ export declare function signOut(token: string | null): Promise<SignOutResult>;
192
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAsD9C,MAAM,MAAM,eAAe,GAAG,qBAAqB,GAAG,cAAc,GAAG,eAAe,GAAG,SAAS,CAAC;AAEnG,qBAAa,WAAY,SAAQ,KAAK;aAGlB,IAAI,EAAE,eAAe;IAFvC,SAAkB,IAAI,iBAAiB;gBAErB,IAAI,EAAE,eAAe,EACrC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAIhC;AA+BD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAKrE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAcjF;AA4CD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAChD;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,iBAAiB,GACjB,sBAAsB,GACtB,mBAAmB,CAAC;AAExB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgCnF;AAED;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAC5B,YAAY,GACZ,iBAAiB,GACjB,oBAAoB,CAAC;AAEzB,MAAM,MAAM,wBAAwB,GAChC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GACnD;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAA;CAAE,CAAC;AAwBhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CAiE1E"}
package/dist/auth.js ADDED
@@ -0,0 +1,294 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { resolveOnePasswordReference } from "./onepassword.js";
4
+ import { isAuthRevokedExtensionCode } from "./services/profile/shared.js";
5
+ import { impersonatedTransport, stockTransport } from "./transport.js";
6
+ /**
7
+ * Persisted-query SHA-256 hash for the `EmailPasswordSignIn` mutation. Sourced
8
+ * from `research/graphql/derived/operations/gateway-mobile.json` (operation extracted from the mobile
9
+ * APK at `jadx/sources/fn/t3.java`). Hardcoded because the synthesized SDL has
10
+ * no client-side queries to feed Apollo APQ codegen — operations live in the
11
+ * research workspace, not in this repo. Re-extract and bump if the gateway
12
+ * starts rejecting this hash; track via the operation extraction strategy
13
+ * (see `research/notes` ADR-004).
14
+ */
15
+ const EMAIL_PASSWORD_SIGNIN_HASH = "bd8e859a9f0a5c462ceb2ac736648068fa5bcdd874a8a49a460824dd0c5aef51";
16
+ /**
17
+ * Minimal full-doc Viewer query used to verify a session is live and bound to
18
+ * the expected user. The cataloged persisted `Viewer` query does not return
19
+ * `viewerRole.email`, so we cannot use APQ here. The mobile gateway accepts
20
+ * full-doc queries for non-mutation traffic, which is why this works without
21
+ * a published persisted hash.
22
+ */
23
+ const VIEWER_VERIFY_QUERY = "query ViewerVerify { viewer { id viewerRole { email } } }";
24
+ /**
25
+ * Full-document `LogOut` mutation string. Mirrors
26
+ * `research/graphql/talent_profile/operations/LogOut.graphql` exactly — the
27
+ * talent_profile surface does not publish a persisted-query catalog, so
28
+ * every operation is sent as a full document (same pattern as
29
+ * `UPDATE_BASIC_INFO_MUTATION` in `services/profile/basic/index.ts`).
30
+ *
31
+ * Wire format: `{ operationName: "LogOut", query, variables: { input: {} } }`
32
+ * — `LogOutInput` is Pattern 7 (trivial empty input, per
33
+ * `research/notes/10-mutation-input-patterns.md`).
34
+ *
35
+ * Schema/contract status: INFERRED. `LogOutPayload` fields are typed as
36
+ * `Unknown` in the synthesized SDL — the actual wire shape is observed only
37
+ * via the live integration test in `packages/e2e/src/97-auth-signout-
38
+ * server-side.e2e.test.ts`. The selection set here is what the bundle-
39
+ * extracted document selects; if a future capture reveals additional
40
+ * fields the live API returns, expand the inline `LogOutPayload` interface
41
+ * (no codegen wiring needed — `Unknown` would map to `unknown` and provide
42
+ * no narrowing).
43
+ */
44
+ const LOG_OUT_MUTATION = `mutation LogOut($input: LogOutInput!) {
45
+ logOut(input: $input) {
46
+ returnTo
47
+ errors {
48
+ key
49
+ message
50
+ }
51
+ notice
52
+ success
53
+ }
54
+ }`;
55
+ export class SignInError extends Error {
56
+ code;
57
+ name = "SignInError";
58
+ constructor(code, message, options) {
59
+ super(message, options);
60
+ this.code = code;
61
+ }
62
+ }
63
+ /**
64
+ * Collapse the polymorphic credentials value into resolved credentials.
65
+ *
66
+ * Form A (string) → `op://[account/]vault/item` → resolved via `op` CLI
67
+ * Form B (object) → literal `{ username, password }` (username is an email
68
+ * per Toptal's `EmailPasswordSignIn` mutation contract;
69
+ * the YAML field is named `username` to match 1Password's
70
+ * USERNAME purpose semantics)
71
+ *
72
+ * Returns the internal `Credentials` shape `{ email, password }` — that's
73
+ * the GraphQL parameter name for the mutation. The username/email
74
+ * synonymy is documented and load-bearing.
75
+ */
76
+ export function resolveCredentials(auth) {
77
+ if (typeof auth === "string") {
78
+ return resolveOnePasswordReference(auth);
79
+ }
80
+ return { email: auth.username, password: auth.password };
81
+ }
82
+ /**
83
+ * Sign in to Toptal Talent via `EmailPasswordSignIn` GraphQL mutation in
84
+ * persisted-query mode against the mobile gateway and capture the session
85
+ * token from the response.
86
+ *
87
+ * Returns the captured token. The CLI persists it back to the same YAML
88
+ * config file (under `auth.token`) via `persistAuthToken` so subsequent
89
+ * invocations can authenticate via `Authorization: Token token=<X>`
90
+ * without re-signing-in.
91
+ *
92
+ * The next authenticated call (e.g. `auth status`) is the de-facto session
93
+ * verifier — we do NOT issue a redundant Viewer probe inside `signIn` itself.
94
+ *
95
+ * See `hq/engineering/adr/ADR-005-auth-model.md` for the cross-surface
96
+ * auth model and `research/notes/02-auth-and-clients.md` for the empirical
97
+ * evidence behind it.
98
+ *
99
+ * Throws `SignInError("UNKNOWN", ...)` if the gateway reports `success: true`
100
+ * but does not return a token — that response shape is malformed and the
101
+ * caller has nothing useful to persist.
102
+ */
103
+ export async function signIn(credentials) {
104
+ const signInResponse = await postSignIn(credentials);
105
+ const payload = extractPayload(signInResponse.body);
106
+ if (!payload?.success) {
107
+ throw mapSignInError(payload);
108
+ }
109
+ const token = payload.token;
110
+ if (typeof token !== "string" || token === "") {
111
+ throw new SignInError("UNKNOWN", "Sign-in succeeded but no token was returned");
112
+ }
113
+ return { token };
114
+ }
115
+ async function postSignIn(credentials) {
116
+ try {
117
+ return await stockTransport({
118
+ surface: "mobile-gateway",
119
+ body: {
120
+ operationName: "EmailPasswordSignIn",
121
+ variables: { email: credentials.email, password: credentials.password },
122
+ extensions: {
123
+ persistedQuery: { version: 1, sha256Hash: EMAIL_PASSWORD_SIGNIN_HASH },
124
+ },
125
+ },
126
+ });
127
+ }
128
+ catch (err) {
129
+ throw new SignInError("NETWORK_ERROR", `Sign-in request failed: ${err.message}`, { cause: err });
130
+ }
131
+ }
132
+ function extractPayload(body) {
133
+ if (!body || typeof body !== "object")
134
+ return null;
135
+ const response = body;
136
+ return response.data?.auth?.signIn ?? null;
137
+ }
138
+ function mapSignInError(payload) {
139
+ if (!payload) {
140
+ return new SignInError("UNKNOWN", "Sign-in failed: empty or malformed response body");
141
+ }
142
+ const errors = payload.errors ?? [];
143
+ for (const err of errors) {
144
+ const code = (err.code ?? "").toUpperCase();
145
+ if (code === "INVALID_CREDENTIALS" || code === "INVALID_EMAIL_OR_PASSWORD") {
146
+ return new SignInError("INVALID_CREDENTIALS", err.message ?? "Invalid email or password");
147
+ }
148
+ if (code === "MFA_REQUIRED" || code === "OTP_REQUIRED" || code === "TWO_FACTOR_REQUIRED") {
149
+ return new SignInError("MFA_REQUIRED", err.message ?? "Multi-factor authentication required");
150
+ }
151
+ }
152
+ const first = errors[0];
153
+ const message = first?.message ?? "Sign-in failed for an unknown reason";
154
+ return new SignInError("UNKNOWN", message);
155
+ }
156
+ /**
157
+ * Probe whether the persisted session token is currently valid.
158
+ *
159
+ * Issues a minimal full-doc `Viewer` query against the mobile gateway,
160
+ * authenticated with `Authorization: Token token=<X>`. The cataloged
161
+ * persisted `Viewer` query in `research/graphql/gateway/operations/mobile/Viewer.graphql`
162
+ * does not return `viewerRole.email`, so we can't use APQ here — see
163
+ * `VIEWER_VERIFY_QUERY` doc.
164
+ *
165
+ * Pass `null` (or an empty string) when no token has been persisted yet;
166
+ * the function short-circuits to `invalid/no-session` without touching the
167
+ * network. CLI consumers pass `config.auth.token ?? null` from the
168
+ * already-loaded `TtctlConfig` (no separate token file to read).
169
+ *
170
+ * Never throws on auth or network failure; classifies into the three
171
+ * `AuthStatusResult` shapes so CLI consumers can map cleanly to exit codes
172
+ * and output messages without re-parsing exceptions.
173
+ */
174
+ export async function getAuthStatus(token) {
175
+ if (token === null || token === "") {
176
+ return { status: "invalid", reason: "no-session" };
177
+ }
178
+ let res;
179
+ try {
180
+ res = await stockTransport({
181
+ surface: "mobile-gateway",
182
+ authToken: token,
183
+ body: {
184
+ operationName: "ViewerVerify",
185
+ query: VIEWER_VERIFY_QUERY,
186
+ },
187
+ });
188
+ }
189
+ catch (err) {
190
+ return { status: "unreachable", reason: err.message };
191
+ }
192
+ if (res.status === 401 || res.status === 403) {
193
+ return { status: "invalid", reason: "session-expired" };
194
+ }
195
+ if (res.status < 200 || res.status >= 300) {
196
+ return { status: "invalid", reason: "unexpected-status" };
197
+ }
198
+ const body = res.body;
199
+ const email = body?.data?.viewer?.viewerRole?.email;
200
+ if (!email) {
201
+ return { status: "invalid", reason: "no-email-in-response" };
202
+ }
203
+ return { status: "valid", email };
204
+ }
205
+ /**
206
+ * Issue the `LogOut` mutation server-side against the Cloudflare-protected
207
+ * `talent_profile/graphql` surface (defense-in-depth audit-log signal +
208
+ * web-session/cookie cleanup; does NOT revoke the captured bearer for
209
+ * downstream mobile-gateway calls per empirical scope — see CLAUDE.md §
210
+ * Auth Model and the `SignOutResult` type docblock above). Authenticates
211
+ * with `Authorization: Token token=<X>` (same bearer that `signIn`
212
+ * captured — see ADR-005).
213
+ *
214
+ * Pass `null` or an empty string when there's no token to log out; the
215
+ * function short-circuits to `invalid/no-session` without touching the
216
+ * network. CLI / teardown call sites that already check for token absence
217
+ * before invoking this are still safe — the defensive short-circuit just
218
+ * removes one decision from the call site.
219
+ *
220
+ * **Never throws.** Every transport, HTTP, GraphQL-level, or wire-shape
221
+ * failure is classified into one of the three `SignOutResult` shapes so
222
+ * call sites can branch on `result.status` without try/catch. The CLI
223
+ * `runAuthSignOut` and `runGlobalTeardown` rely on this contract to keep
224
+ * the local-clear path unconditional (the bearer aging out in 24-72h is
225
+ * the second-line defense if server-side revocation cannot be confirmed).
226
+ *
227
+ * Schema/contract status: the operation document and `LogOutPayload` field
228
+ * shapes are INFERRED — `Unknown` in the SDL. The live wire format is
229
+ * verified by `packages/e2e/src/97-auth-signout-server-side.e2e.test.ts`
230
+ * (TTCTL_E2E=1 gate). If the live response surfaces fields not yet
231
+ * declared here, expand the inline `LogOutPayload` interface — codegen
232
+ * wiring would produce `unknown`-typed fields and is not currently useful.
233
+ */
234
+ export async function signOut(token) {
235
+ if (token === null || token === "") {
236
+ return { status: "invalid", reason: "no-session" };
237
+ }
238
+ let res;
239
+ try {
240
+ res = await impersonatedTransport({
241
+ surface: "talent-profile",
242
+ authToken: token,
243
+ body: {
244
+ operationName: "LogOut",
245
+ query: LOG_OUT_MUTATION,
246
+ variables: { input: {} },
247
+ },
248
+ });
249
+ }
250
+ catch (err) {
251
+ return {
252
+ status: "unreachable",
253
+ reason: { kind: "transport", reason: err.message },
254
+ };
255
+ }
256
+ if (res.status === 401 || res.status === 403) {
257
+ return { status: "invalid", reason: "session-expired" };
258
+ }
259
+ if (res.status < 200 || res.status >= 300) {
260
+ return {
261
+ status: "unreachable",
262
+ reason: { kind: "http-status", status: res.status },
263
+ };
264
+ }
265
+ const body = res.body;
266
+ // Top-level GraphQL errors[] — talent_profile surface conventionally
267
+ // surfaces auth-revoke states here (UNAUTHENTICATED / UNAUTHORIZED /
268
+ // AUTHENTICATION_REQUIRED). Treat those as "bearer already invalid"
269
+ // (the user's intent is satisfied); any other top-level error is
270
+ // unreachable (we cannot confirm revocation).
271
+ if (body && Array.isArray(body.errors) && body.errors.length > 0) {
272
+ const first = body.errors[0];
273
+ if (isAuthRevokedExtensionCode(first?.extensions?.code)) {
274
+ return { status: "invalid", reason: "graphql-auth-error" };
275
+ }
276
+ return {
277
+ status: "unreachable",
278
+ reason: { kind: "graphql-error", message: first?.message ?? "GraphQL error" },
279
+ };
280
+ }
281
+ const payload = body?.data?.logOut;
282
+ if (!payload) {
283
+ return { status: "unreachable", reason: { kind: "payload-missing" } };
284
+ }
285
+ if (payload.success === true) {
286
+ return { status: "logged-out" };
287
+ }
288
+ // success: false / null / undefined — server received the mutation but
289
+ // did not acknowledge the LogOut flow as processed. We don't know whether
290
+ // the LogOut signal was actually delivered to the audit log / session
291
+ // store; classify as unreachable so callers fall through to local clear.
292
+ return { status: "unreachable", reason: { kind: "success-false" } };
293
+ }
294
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}