@docsxai/engine 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +130 -0
  3. package/dist/auth/api-login.d.ts +69 -0
  4. package/dist/auth/api-login.js +95 -0
  5. package/dist/auth/browser-session.d.ts +28 -0
  6. package/dist/auth/browser-session.js +43 -0
  7. package/dist/auth/cookie-jar.d.ts +58 -0
  8. package/dist/auth/cookie-jar.js +212 -0
  9. package/dist/auth/email-otp.d.ts +210 -0
  10. package/dist/auth/email-otp.js +166 -0
  11. package/dist/auth/http-basic.d.ts +5 -0
  12. package/dist/auth/http-basic.js +17 -0
  13. package/dist/auth/index.d.ts +47 -0
  14. package/dist/auth/index.js +137 -0
  15. package/dist/auth/jwt-injection.d.ts +153 -0
  16. package/dist/auth/jwt-injection.js +136 -0
  17. package/dist/auth/manual-capture.d.ts +35 -0
  18. package/dist/auth/manual-capture.js +30 -0
  19. package/dist/auth/mtls.d.ts +15 -0
  20. package/dist/auth/mtls.js +53 -0
  21. package/dist/auth/pat-header.d.ts +19 -0
  22. package/dist/auth/pat-header.js +34 -0
  23. package/dist/auth/storage-state-cache.d.ts +38 -0
  24. package/dist/auth/storage-state-cache.js +143 -0
  25. package/dist/auth/test-backdoor.d.ts +25 -0
  26. package/dist/auth/test-backdoor.js +51 -0
  27. package/dist/auth/totp.d.ts +39 -0
  28. package/dist/auth/totp.js +108 -0
  29. package/dist/auth/types.d.ts +86 -0
  30. package/dist/auth/types.js +57 -0
  31. package/dist/auth/ui-form.d.ts +204 -0
  32. package/dist/auth/ui-form.js +153 -0
  33. package/dist/auth/webauthn.d.ts +88 -0
  34. package/dist/auth/webauthn.js +67 -0
  35. package/dist/auth.d.ts +1 -0
  36. package/dist/auth.js +3 -0
  37. package/dist/backend-client-contracts.d.ts +88 -0
  38. package/dist/backend-client-contracts.js +19 -0
  39. package/dist/backend-client-oauth-login.d.ts +7 -0
  40. package/dist/backend-client-oauth-login.js +90 -0
  41. package/dist/backend-client-state-cache.d.ts +73 -0
  42. package/dist/backend-client-state-cache.js +185 -0
  43. package/dist/backend-client-token.d.ts +18 -0
  44. package/dist/backend-client-token.js +94 -0
  45. package/dist/backend-client-transport.d.ts +66 -0
  46. package/dist/backend-client-transport.js +181 -0
  47. package/dist/backend-client.d.ts +5 -0
  48. package/dist/backend-client.js +18 -0
  49. package/dist/calibrate.d.ts +31 -0
  50. package/dist/calibrate.js +68 -0
  51. package/dist/cli-commands-authoring.d.ts +5 -0
  52. package/dist/cli-commands-authoring.js +403 -0
  53. package/dist/cli-commands-backend.d.ts +5 -0
  54. package/dist/cli-commands-backend.js +211 -0
  55. package/dist/cli-commands-docpack.d.ts +5 -0
  56. package/dist/cli-commands-docpack.js +280 -0
  57. package/dist/cli-commands-session.d.ts +4 -0
  58. package/dist/cli-commands-session.js +398 -0
  59. package/dist/cli-shared.d.ts +5 -0
  60. package/dist/cli-shared.js +45 -0
  61. package/dist/cli-usage.d.ts +1 -0
  62. package/dist/cli-usage.js +137 -0
  63. package/dist/cli.d.ts +2 -0
  64. package/dist/cli.js +77 -0
  65. package/dist/diagnose.d.ts +50 -0
  66. package/dist/diagnose.js +168 -0
  67. package/dist/diff-compute.d.ts +13 -0
  68. package/dist/diff-compute.js +378 -0
  69. package/dist/diff-report.d.ts +7 -0
  70. package/dist/diff-report.js +125 -0
  71. package/dist/diff-types.d.ts +125 -0
  72. package/dist/diff-types.js +15 -0
  73. package/dist/diff.d.ts +3 -0
  74. package/dist/diff.js +16 -0
  75. package/dist/doc-pack-io.d.ts +30 -0
  76. package/dist/doc-pack-io.js +182 -0
  77. package/dist/doc-pack.d.ts +1814 -0
  78. package/dist/doc-pack.js +328 -0
  79. package/dist/doctor-checks-plugins.d.ts +2 -0
  80. package/dist/doctor-checks-plugins.js +136 -0
  81. package/dist/doctor-checks.d.ts +56 -0
  82. package/dist/doctor-checks.js +367 -0
  83. package/dist/doctor.d.ts +7 -0
  84. package/dist/doctor.js +62 -0
  85. package/dist/export/adf.d.ts +57 -0
  86. package/dist/export/adf.js +323 -0
  87. package/dist/export/playwright-test.d.ts +26 -0
  88. package/dist/export/playwright-test.js +221 -0
  89. package/dist/flow-file.d.ts +21 -0
  90. package/dist/flow-file.js +180 -0
  91. package/dist/flow-lint.d.ts +24 -0
  92. package/dist/flow-lint.js +203 -0
  93. package/dist/flow-runtime.d.ts +113 -0
  94. package/dist/flow-runtime.js +273 -0
  95. package/dist/flow-tree.d.ts +19 -0
  96. package/dist/flow-tree.js +104 -0
  97. package/dist/index.d.ts +27 -0
  98. package/dist/index.js +31 -0
  99. package/dist/playwright-driver.d.ts +105 -0
  100. package/dist/playwright-driver.js +363 -0
  101. package/dist/playwright-instrumented-browser.d.ts +51 -0
  102. package/dist/playwright-instrumented-browser.js +189 -0
  103. package/dist/plugins/load.d.ts +22 -0
  104. package/dist/plugins/load.js +99 -0
  105. package/dist/plugins/lock.d.ts +40 -0
  106. package/dist/plugins/lock.js +122 -0
  107. package/dist/plugins/manifest.d.ts +70 -0
  108. package/dist/plugins/manifest.js +115 -0
  109. package/dist/plugins/plan.d.ts +51 -0
  110. package/dist/plugins/plan.js +279 -0
  111. package/dist/plugins/registry.d.ts +59 -0
  112. package/dist/plugins/registry.js +71 -0
  113. package/dist/plugins/runtime.d.ts +7 -0
  114. package/dist/plugins/runtime.js +27 -0
  115. package/dist/plugins/types.d.ts +58 -0
  116. package/dist/plugins/types.js +4 -0
  117. package/dist/plugins-cli.d.ts +1 -0
  118. package/dist/plugins-cli.js +191 -0
  119. package/dist/redact.d.ts +16 -0
  120. package/dist/redact.js +72 -0
  121. package/dist/style.d.ts +46 -0
  122. package/dist/style.js +151 -0
  123. package/dist/viewer-bin.d.ts +20 -0
  124. package/dist/viewer-bin.js +97 -0
  125. package/dist/workspace.d.ts +60 -0
  126. package/dist/workspace.js +172 -0
  127. package/dist/zip.d.ts +17 -0
  128. package/dist/zip.js +113 -0
  129. package/package.json +64 -0
@@ -0,0 +1,204 @@
1
+ import { z } from "zod";
2
+ import { type AuthPage, type AuthPageLauncher } from "./browser-session.js";
3
+ import { type AuthContext, type AuthResult, type AuthStrategy } from "./types.js";
4
+ /** One pre-login step (cookie banner, locale picker) shared by the browser-driving strategies. */
5
+ export declare const PreStep: z.ZodObject<{
6
+ action: z.ZodEnum<["click", "fill"]>;
7
+ selector: z.ZodString;
8
+ /** Env var *name* holding the fill value (required for `fill`). */
9
+ value_env: z.ZodOptional<z.ZodString>;
10
+ }, "strict", z.ZodTypeAny, {
11
+ selector: string;
12
+ action: "fill" | "click";
13
+ value_env?: string | undefined;
14
+ }, {
15
+ selector: string;
16
+ action: "fill" | "click";
17
+ value_env?: string | undefined;
18
+ }>;
19
+ export type PreStep = z.infer<typeof PreStep>;
20
+ /** Read a required env var by *name*; missing/empty → config error that never echoes a value. */
21
+ export declare function requireEnvVar(strategy: string, what: string, varName: string, env: NodeJS.ProcessEnv): string;
22
+ /** Resolve every `fill` pre-step's `value_env` up front, so a missing var fails before any browser launches. */
23
+ export declare function resolvePreStepValues(strategy: string, steps: PreStep[], env: NodeJS.ProcessEnv): string[];
24
+ /** Execute pre-login steps in order (`values` from {@link resolvePreStepValues}, index-aligned). */
25
+ export declare function runPreSteps(page: AuthPage, steps: PreStep[], values: string[]): Promise<void>;
26
+ export declare const UiFormTotpOptions: z.ZodObject<{
27
+ /** Env var *name* holding the base32 TOTP secret. */
28
+ secret_env: z.ZodString;
29
+ otp_selector: z.ZodString;
30
+ submit_selector: z.ZodOptional<z.ZodString>;
31
+ digits: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<6>, z.ZodLiteral<8>]>>;
32
+ period: z.ZodDefault<z.ZodNumber>;
33
+ algorithm: z.ZodDefault<z.ZodEnum<["sha1", "sha256"]>>;
34
+ }, "strict", z.ZodTypeAny, {
35
+ digits: 8 | 6;
36
+ period: number;
37
+ algorithm: "sha1" | "sha256";
38
+ secret_env: string;
39
+ otp_selector: string;
40
+ submit_selector?: string | undefined;
41
+ }, {
42
+ secret_env: string;
43
+ otp_selector: string;
44
+ digits?: 8 | 6 | undefined;
45
+ period?: number | undefined;
46
+ algorithm?: "sha1" | "sha256" | undefined;
47
+ submit_selector?: string | undefined;
48
+ }>;
49
+ export type UiFormTotpOptions = z.infer<typeof UiFormTotpOptions>;
50
+ export declare const UiFormOptions: z.ZodEffects<z.ZodObject<{
51
+ /** Login page; resolved against the target's base URL when relative. */
52
+ login_url: z.ZodString;
53
+ username_selector: z.ZodString;
54
+ password_selector: z.ZodString;
55
+ submit_selector: z.ZodString;
56
+ /** Logged-in marker: a selector that appears… */
57
+ success_selector: z.ZodOptional<z.ZodString>;
58
+ /** …or a regex (source) the post-login URL matches. One of the two is required. */
59
+ url_matches: z.ZodOptional<z.ZodString>;
60
+ /** Per-wait timeout. Default 15000. */
61
+ timeout_ms: z.ZodDefault<z.ZodNumber>;
62
+ ignore_https_errors: z.ZodDefault<z.ZodBoolean>;
63
+ /** Pre-login chrome (cookie banners, locale pickers): clicked / filled before the form. */
64
+ pre_steps: z.ZodDefault<z.ZodArray<z.ZodObject<{
65
+ action: z.ZodEnum<["click", "fill"]>;
66
+ selector: z.ZodString;
67
+ /** Env var *name* holding the fill value (required for `fill`). */
68
+ value_env: z.ZodOptional<z.ZodString>;
69
+ }, "strict", z.ZodTypeAny, {
70
+ selector: string;
71
+ action: "fill" | "click";
72
+ value_env?: string | undefined;
73
+ }, {
74
+ selector: string;
75
+ action: "fill" | "click";
76
+ value_env?: string | undefined;
77
+ }>, "many">>;
78
+ totp: z.ZodOptional<z.ZodObject<{
79
+ /** Env var *name* holding the base32 TOTP secret. */
80
+ secret_env: z.ZodString;
81
+ otp_selector: z.ZodString;
82
+ submit_selector: z.ZodOptional<z.ZodString>;
83
+ digits: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<6>, z.ZodLiteral<8>]>>;
84
+ period: z.ZodDefault<z.ZodNumber>;
85
+ algorithm: z.ZodDefault<z.ZodEnum<["sha1", "sha256"]>>;
86
+ }, "strict", z.ZodTypeAny, {
87
+ digits: 8 | 6;
88
+ period: number;
89
+ algorithm: "sha1" | "sha256";
90
+ secret_env: string;
91
+ otp_selector: string;
92
+ submit_selector?: string | undefined;
93
+ }, {
94
+ secret_env: string;
95
+ otp_selector: string;
96
+ digits?: 8 | 6 | undefined;
97
+ period?: number | undefined;
98
+ algorithm?: "sha1" | "sha256" | undefined;
99
+ submit_selector?: string | undefined;
100
+ }>>;
101
+ }, "strict", z.ZodTypeAny, {
102
+ timeout_ms: number;
103
+ login_url: string;
104
+ ignore_https_errors: boolean;
105
+ submit_selector: string;
106
+ username_selector: string;
107
+ password_selector: string;
108
+ pre_steps: {
109
+ selector: string;
110
+ action: "fill" | "click";
111
+ value_env?: string | undefined;
112
+ }[];
113
+ url_matches?: string | undefined;
114
+ totp?: {
115
+ digits: 8 | 6;
116
+ period: number;
117
+ algorithm: "sha1" | "sha256";
118
+ secret_env: string;
119
+ otp_selector: string;
120
+ submit_selector?: string | undefined;
121
+ } | undefined;
122
+ success_selector?: string | undefined;
123
+ }, {
124
+ login_url: string;
125
+ submit_selector: string;
126
+ username_selector: string;
127
+ password_selector: string;
128
+ timeout_ms?: number | undefined;
129
+ url_matches?: string | undefined;
130
+ totp?: {
131
+ secret_env: string;
132
+ otp_selector: string;
133
+ digits?: 8 | 6 | undefined;
134
+ period?: number | undefined;
135
+ algorithm?: "sha1" | "sha256" | undefined;
136
+ submit_selector?: string | undefined;
137
+ } | undefined;
138
+ ignore_https_errors?: boolean | undefined;
139
+ success_selector?: string | undefined;
140
+ pre_steps?: {
141
+ selector: string;
142
+ action: "fill" | "click";
143
+ value_env?: string | undefined;
144
+ }[] | undefined;
145
+ }>, {
146
+ timeout_ms: number;
147
+ login_url: string;
148
+ ignore_https_errors: boolean;
149
+ submit_selector: string;
150
+ username_selector: string;
151
+ password_selector: string;
152
+ pre_steps: {
153
+ selector: string;
154
+ action: "fill" | "click";
155
+ value_env?: string | undefined;
156
+ }[];
157
+ url_matches?: string | undefined;
158
+ totp?: {
159
+ digits: 8 | 6;
160
+ period: number;
161
+ algorithm: "sha1" | "sha256";
162
+ secret_env: string;
163
+ otp_selector: string;
164
+ submit_selector?: string | undefined;
165
+ } | undefined;
166
+ success_selector?: string | undefined;
167
+ }, {
168
+ login_url: string;
169
+ submit_selector: string;
170
+ username_selector: string;
171
+ password_selector: string;
172
+ timeout_ms?: number | undefined;
173
+ url_matches?: string | undefined;
174
+ totp?: {
175
+ secret_env: string;
176
+ otp_selector: string;
177
+ digits?: 8 | 6 | undefined;
178
+ period?: number | undefined;
179
+ algorithm?: "sha1" | "sha256" | undefined;
180
+ submit_selector?: string | undefined;
181
+ } | undefined;
182
+ ignore_https_errors?: boolean | undefined;
183
+ success_selector?: string | undefined;
184
+ pre_steps?: {
185
+ selector: string;
186
+ action: "fill" | "click";
187
+ value_env?: string | undefined;
188
+ }[] | undefined;
189
+ }>;
190
+ export type UiFormOptions = z.infer<typeof UiFormOptions>;
191
+ export declare class UiFormStrategy implements AuthStrategy {
192
+ private readonly launcher;
193
+ private readonly env;
194
+ readonly name: "ui-form";
195
+ constructor(launcher?: AuthPageLauncher, env?: NodeJS.ProcessEnv);
196
+ authenticate(ctx: AuthContext): Promise<AuthResult>;
197
+ private waitStep;
198
+ }
199
+ /** Shared success wait: a selector that appears, or a URL regex match. Timeouts become config errors. */
200
+ export declare function waitForLoginSuccess(page: AuthPage, opts: {
201
+ success_selector?: string;
202
+ url_matches?: string;
203
+ timeout_ms: number;
204
+ }, strategy: string): Promise<void>;
@@ -0,0 +1,153 @@
1
+ // `ui-form` — drive the app's own login form in a headless Chromium: fill username/password,
2
+ // submit, wait for the logged-in marker, snapshot storageState. `pre_steps` dismiss cookie
3
+ // banners and similar pre-login chrome; `options.totp` hooks an RFC-6238 one-time code in after
4
+ // the password submit (the `totp` catalogue entry composes this strategy).
5
+ import { z } from "zod";
6
+ import { launchAuthPage } from "./browser-session.js";
7
+ import { jarAuthExpiry } from "./cookie-jar.js";
8
+ import { generateTotp } from "./totp.js";
9
+ import { AuthStrategyConfigError, maskSecret, parseStrategyOptions, } from "./types.js";
10
+ /** One pre-login step (cookie banner, locale picker) shared by the browser-driving strategies. */
11
+ export const PreStep = z
12
+ .object({
13
+ action: z.enum(["click", "fill"]),
14
+ selector: z.string().min(1),
15
+ /** Env var *name* holding the fill value (required for `fill`). */
16
+ value_env: z.string().min(1).optional(),
17
+ })
18
+ .strict();
19
+ /** Read a required env var by *name*; missing/empty → config error that never echoes a value. */
20
+ export function requireEnvVar(strategy, what, varName, env) {
21
+ const value = env[varName];
22
+ if (!value) {
23
+ throw new AuthStrategyConfigError(`${strategy}: ${what} $${varName} is ${maskSecret(value)}`);
24
+ }
25
+ return value;
26
+ }
27
+ /** Resolve every `fill` pre-step's `value_env` up front, so a missing var fails before any browser launches. */
28
+ export function resolvePreStepValues(strategy, steps, env) {
29
+ return steps.map((step) => {
30
+ if (step.action !== "fill")
31
+ return "";
32
+ if (!step.value_env) {
33
+ throw new AuthStrategyConfigError(`${strategy}: pre_steps fill on "${step.selector}" needs value_env (the env var name to fill from)`);
34
+ }
35
+ return requireEnvVar(strategy, "pre_steps value_env", step.value_env, env);
36
+ });
37
+ }
38
+ /** Execute pre-login steps in order (`values` from {@link resolvePreStepValues}, index-aligned). */
39
+ export async function runPreSteps(page, steps, values) {
40
+ for (const [i, step] of steps.entries()) {
41
+ if (step.action === "click")
42
+ await page.click(step.selector);
43
+ else
44
+ await page.fill(step.selector, values[i]);
45
+ }
46
+ }
47
+ export const UiFormTotpOptions = z
48
+ .object({
49
+ /** Env var *name* holding the base32 TOTP secret. */
50
+ secret_env: z.string().min(1),
51
+ otp_selector: z.string().min(1),
52
+ submit_selector: z.string().min(1).optional(),
53
+ digits: z.union([z.literal(6), z.literal(8)]).default(6),
54
+ period: z.number().int().positive().default(30),
55
+ algorithm: z.enum(["sha1", "sha256"]).default("sha1"),
56
+ })
57
+ .strict();
58
+ export const UiFormOptions = z
59
+ .object({
60
+ /** Login page; resolved against the target's base URL when relative. */
61
+ login_url: z.string().min(1),
62
+ username_selector: z.string().min(1),
63
+ password_selector: z.string().min(1),
64
+ submit_selector: z.string().min(1),
65
+ /** Logged-in marker: a selector that appears… */
66
+ success_selector: z.string().min(1).optional(),
67
+ /** …or a regex (source) the post-login URL matches. One of the two is required. */
68
+ url_matches: z.string().min(1).optional(),
69
+ /** Per-wait timeout. Default 15000. */
70
+ timeout_ms: z.number().int().positive().default(15_000),
71
+ ignore_https_errors: z.boolean().default(false),
72
+ /** Pre-login chrome (cookie banners, locale pickers): clicked / filled before the form. */
73
+ pre_steps: z.array(PreStep).default([]),
74
+ totp: UiFormTotpOptions.optional(),
75
+ })
76
+ .strict()
77
+ .refine((o) => o.success_selector !== undefined || o.url_matches !== undefined, {
78
+ message: "one of success_selector or url_matches is required",
79
+ });
80
+ export class UiFormStrategy {
81
+ launcher;
82
+ env;
83
+ name = "ui-form";
84
+ constructor(launcher = launchAuthPage, env = process.env) {
85
+ this.launcher = launcher;
86
+ this.env = env;
87
+ }
88
+ async authenticate(ctx) {
89
+ const opts = parseStrategyOptions(this.name, UiFormOptions, ctx.options);
90
+ const { username, password } = ctx.creds;
91
+ if (!username || !password) {
92
+ throw new AuthStrategyConfigError(`ui-form: creds_env must map "username" (${maskSecret(username)}) and "password" (${maskSecret(password)})`);
93
+ }
94
+ // Read env-var-name options up front so a missing var fails before a browser launches.
95
+ const totpSecret = opts.totp
96
+ ? requireEnvVar(this.name, "totp.secret_env", opts.totp.secret_env, this.env)
97
+ : "";
98
+ const preStepValues = resolvePreStepValues(this.name, opts.pre_steps, this.env);
99
+ const page = await this.launcher({
100
+ baseURL: ctx.baseURL,
101
+ ...(opts.ignore_https_errors ? { ignoreHTTPSErrors: true } : {}),
102
+ });
103
+ try {
104
+ await page.goto(new URL(opts.login_url, ctx.baseURL).href);
105
+ await runPreSteps(page, opts.pre_steps, preStepValues);
106
+ await page.fill(opts.username_selector, username);
107
+ await page.fill(opts.password_selector, password);
108
+ await page.click(opts.submit_selector);
109
+ if (opts.totp) {
110
+ await this.waitStep(page, opts.totp.otp_selector, opts.timeout_ms, "the TOTP prompt");
111
+ await page.fill(opts.totp.otp_selector, generateTotp(totpSecret, {
112
+ digits: opts.totp.digits,
113
+ period: opts.totp.period,
114
+ algorithm: opts.totp.algorithm,
115
+ }));
116
+ if (opts.totp.submit_selector)
117
+ await page.click(opts.totp.submit_selector);
118
+ }
119
+ await waitForLoginSuccess(page, opts, "ui-form");
120
+ const storageState = await page.storageState();
121
+ const expiresAt = jarAuthExpiry(storageState);
122
+ return { storageState, ...(expiresAt !== undefined ? { expiresAt } : {}) };
123
+ }
124
+ finally {
125
+ await page.close();
126
+ }
127
+ }
128
+ async waitStep(page, selector, timeoutMs, what) {
129
+ try {
130
+ await page.waitForSelector(selector, { timeoutMs });
131
+ }
132
+ catch {
133
+ throw new AuthStrategyConfigError(`ui-form: ${what} ("${selector}") did not appear within ${timeoutMs}ms`);
134
+ }
135
+ }
136
+ }
137
+ /** Shared success wait: a selector that appears, or a URL regex match. Timeouts become config errors. */
138
+ export async function waitForLoginSuccess(page, opts, strategy) {
139
+ try {
140
+ if (opts.success_selector !== undefined) {
141
+ await page.waitForSelector(opts.success_selector, { timeoutMs: opts.timeout_ms });
142
+ }
143
+ else {
144
+ await page.waitForUrl(new RegExp(opts.url_matches), { timeoutMs: opts.timeout_ms });
145
+ }
146
+ }
147
+ catch {
148
+ const expected = opts.success_selector !== undefined
149
+ ? `success_selector "${opts.success_selector}"`
150
+ : `url matching /${opts.url_matches}/`;
151
+ throw new AuthStrategyConfigError(`${strategy}: login did not reach ${expected} within ${opts.timeout_ms}ms — check the credential env vars (values not shown) and the selectors`);
152
+ }
153
+ }
@@ -0,0 +1,88 @@
1
+ import { z } from "zod";
2
+ import { type AuthPageLauncher } from "./browser-session.js";
3
+ import { type AuthContext, type AuthResult, type AuthStrategy } from "./types.js";
4
+ export declare const WebauthnOptions: z.ZodEffects<z.ZodObject<{
5
+ /** Login page; resolved against the target's base URL when relative. */
6
+ login_url: z.ZodString;
7
+ /** The "Sign in with a passkey" control that starts the WebAuthn ceremony. */
8
+ trigger_selector: z.ZodString;
9
+ /** Username-first flows: filled with the `username` credential before the trigger. */
10
+ username_selector: z.ZodOptional<z.ZodString>;
11
+ success_selector: z.ZodOptional<z.ZodString>;
12
+ url_matches: z.ZodOptional<z.ZodString>;
13
+ timeout_ms: z.ZodDefault<z.ZodNumber>;
14
+ ignore_https_errors: z.ZodDefault<z.ZodBoolean>;
15
+ pre_steps: z.ZodDefault<z.ZodArray<z.ZodObject<{
16
+ action: z.ZodEnum<["click", "fill"]>;
17
+ selector: z.ZodString;
18
+ value_env: z.ZodOptional<z.ZodString>;
19
+ }, "strict", z.ZodTypeAny, {
20
+ selector: string;
21
+ action: "fill" | "click";
22
+ value_env?: string | undefined;
23
+ }, {
24
+ selector: string;
25
+ action: "fill" | "click";
26
+ value_env?: string | undefined;
27
+ }>, "many">>;
28
+ }, "strict", z.ZodTypeAny, {
29
+ timeout_ms: number;
30
+ login_url: string;
31
+ ignore_https_errors: boolean;
32
+ pre_steps: {
33
+ selector: string;
34
+ action: "fill" | "click";
35
+ value_env?: string | undefined;
36
+ }[];
37
+ trigger_selector: string;
38
+ url_matches?: string | undefined;
39
+ username_selector?: string | undefined;
40
+ success_selector?: string | undefined;
41
+ }, {
42
+ login_url: string;
43
+ trigger_selector: string;
44
+ timeout_ms?: number | undefined;
45
+ url_matches?: string | undefined;
46
+ ignore_https_errors?: boolean | undefined;
47
+ username_selector?: string | undefined;
48
+ success_selector?: string | undefined;
49
+ pre_steps?: {
50
+ selector: string;
51
+ action: "fill" | "click";
52
+ value_env?: string | undefined;
53
+ }[] | undefined;
54
+ }>, {
55
+ timeout_ms: number;
56
+ login_url: string;
57
+ ignore_https_errors: boolean;
58
+ pre_steps: {
59
+ selector: string;
60
+ action: "fill" | "click";
61
+ value_env?: string | undefined;
62
+ }[];
63
+ trigger_selector: string;
64
+ url_matches?: string | undefined;
65
+ username_selector?: string | undefined;
66
+ success_selector?: string | undefined;
67
+ }, {
68
+ login_url: string;
69
+ trigger_selector: string;
70
+ timeout_ms?: number | undefined;
71
+ url_matches?: string | undefined;
72
+ ignore_https_errors?: boolean | undefined;
73
+ username_selector?: string | undefined;
74
+ success_selector?: string | undefined;
75
+ pre_steps?: {
76
+ selector: string;
77
+ action: "fill" | "click";
78
+ value_env?: string | undefined;
79
+ }[] | undefined;
80
+ }>;
81
+ export type WebauthnOptions = z.infer<typeof WebauthnOptions>;
82
+ export declare class WebauthnStrategy implements AuthStrategy {
83
+ private readonly launcher;
84
+ private readonly env;
85
+ readonly name: "webauthn";
86
+ constructor(launcher?: AuthPageLauncher, env?: NodeJS.ProcessEnv);
87
+ authenticate(ctx: AuthContext): Promise<AuthResult>;
88
+ }
@@ -0,0 +1,67 @@
1
+ // `webauthn` — passkey login through a CDP virtual authenticator. The authenticator is attached
2
+ // *before* navigation (so the login page's `navigator.credentials` sees a platform authenticator
3
+ // from its first feature probe), then the strategy walks the page's own passkey flow: optional
4
+ // username-first fill, click the trigger, wait for the logged-in marker, snapshot storageState.
5
+ //
6
+ // The virtual device is ctap2 / internal / user-verifying with automatic presence simulation —
7
+ // the standard headless-CI stand-in for Touch ID-style platform authenticators.
8
+ import { z } from "zod";
9
+ import { launchAuthPage } from "./browser-session.js";
10
+ import { jarAuthExpiry } from "./cookie-jar.js";
11
+ import { PreStep, resolvePreStepValues, runPreSteps, waitForLoginSuccess } from "./ui-form.js";
12
+ import { AuthStrategyConfigError, maskSecret, parseStrategyOptions, } from "./types.js";
13
+ export const WebauthnOptions = z
14
+ .object({
15
+ /** Login page; resolved against the target's base URL when relative. */
16
+ login_url: z.string().min(1),
17
+ /** The "Sign in with a passkey" control that starts the WebAuthn ceremony. */
18
+ trigger_selector: z.string().min(1),
19
+ /** Username-first flows: filled with the `username` credential before the trigger. */
20
+ username_selector: z.string().min(1).optional(),
21
+ success_selector: z.string().min(1).optional(),
22
+ url_matches: z.string().min(1).optional(),
23
+ timeout_ms: z.number().int().positive().default(15_000),
24
+ ignore_https_errors: z.boolean().default(false),
25
+ pre_steps: z.array(PreStep).default([]),
26
+ })
27
+ .strict()
28
+ .refine((o) => o.success_selector !== undefined || o.url_matches !== undefined, {
29
+ message: "one of success_selector or url_matches is required",
30
+ });
31
+ export class WebauthnStrategy {
32
+ launcher;
33
+ env;
34
+ name = "webauthn";
35
+ constructor(launcher = launchAuthPage, env = process.env) {
36
+ this.launcher = launcher;
37
+ this.env = env;
38
+ }
39
+ async authenticate(ctx) {
40
+ const opts = parseStrategyOptions(this.name, WebauthnOptions, ctx.options);
41
+ const { username } = ctx.creds;
42
+ if (opts.username_selector && !username) {
43
+ throw new AuthStrategyConfigError(`webauthn: username_selector is set, so creds_env must map "username" (${maskSecret(username)})`);
44
+ }
45
+ const preStepValues = resolvePreStepValues(this.name, opts.pre_steps, this.env);
46
+ const page = await this.launcher({
47
+ baseURL: ctx.baseURL,
48
+ ...(opts.ignore_https_errors ? { ignoreHTTPSErrors: true } : {}),
49
+ });
50
+ try {
51
+ // Must precede goto: the page feature-detects authenticators at load.
52
+ await page.enableVirtualAuthenticator();
53
+ await page.goto(new URL(opts.login_url, ctx.baseURL).href);
54
+ await runPreSteps(page, opts.pre_steps, preStepValues);
55
+ if (opts.username_selector)
56
+ await page.fill(opts.username_selector, username);
57
+ await page.click(opts.trigger_selector);
58
+ await waitForLoginSuccess(page, opts, this.name);
59
+ const storageState = await page.storageState();
60
+ const expiresAt = jarAuthExpiry(storageState);
61
+ return { storageState, ...(expiresAt !== undefined ? { expiresAt } : {}) };
62
+ }
63
+ finally {
64
+ await page.close();
65
+ }
66
+ }
67
+ }
package/dist/auth.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./auth/index.js";
package/dist/auth.js ADDED
@@ -0,0 +1,3 @@
1
+ // Target-site auth layer — re-export shim. The implementation lives under `auth/`
2
+ // (one module per strategy); existing imports of `./auth.js` keep working unchanged.
3
+ export * from "./auth/index.js";
@@ -0,0 +1,88 @@
1
+ import { type RevisionKind } from "./doc-pack.js";
2
+ export declare const API_VERSION: "1";
3
+ export declare const API_VERSION_HEADER = "docsxai-api-version";
4
+ export type RevisionArtifact = "flows" | "annotations" | "screenshots" | "style" | "locators";
5
+ export interface Workspace {
6
+ id: string;
7
+ name: string;
8
+ created_at: string;
9
+ }
10
+ export interface Project {
11
+ id: string;
12
+ workspace_id: string;
13
+ name: string;
14
+ created_at: string;
15
+ head_revision_id: string | null;
16
+ }
17
+ export interface Revision {
18
+ id: string;
19
+ project_id: string;
20
+ parent_revision_id: string | null;
21
+ kind: RevisionKind;
22
+ author: string;
23
+ created_at: string;
24
+ artifacts: RevisionArtifact[];
25
+ /** True once finalized — artifact PUTs are rejected with 409 from then on. */
26
+ finalized: boolean;
27
+ }
28
+ export interface RunRecord {
29
+ id: string;
30
+ project_id: string;
31
+ revision_id: string;
32
+ ok: boolean;
33
+ duration_ms: number;
34
+ summary: string;
35
+ created_at: string;
36
+ }
37
+ /** Reference to a content-addressed blob stored on the backend. */
38
+ export interface BlobRef {
39
+ sha256: string;
40
+ bytes: number;
41
+ }
42
+ export declare class BackendClientError extends Error {
43
+ readonly status?: number | undefined;
44
+ readonly body?: unknown | undefined;
45
+ constructor(message: string, status?: number | undefined, body?: unknown | undefined);
46
+ }
47
+ export interface BackendClientOptions {
48
+ baseUrl: string;
49
+ /** Bearer token. Reads from `DOCSX_TOKEN` env if omitted. */
50
+ token?: string;
51
+ /** Override the HTTP fetch (for tests). Defaults to `globalThis.fetch`. */
52
+ fetch?: typeof globalThis.fetch;
53
+ }
54
+ export interface FlowsPayload {
55
+ schema: "docsxai/flows@1";
56
+ files: Record<string, string>;
57
+ }
58
+ export interface AnnotationsPayload {
59
+ schema: "docsxai/annotations-bundle@1";
60
+ files: Record<string, unknown>;
61
+ }
62
+ export interface ScreenshotsPayload {
63
+ schema: "docsxai/screenshots@2";
64
+ files: Record<string, BlobRef>;
65
+ }
66
+ export interface StylePayload {
67
+ schema: "docsxai/style-bundle@1";
68
+ yaml: string | null;
69
+ json: unknown;
70
+ }
71
+ export interface LocatorsPayload {
72
+ schema: "docsxai/locators@1";
73
+ yaml: string | null;
74
+ }
75
+ export interface BackendTokenFile {
76
+ access_token: string;
77
+ refresh_token: string;
78
+ /** Epoch ms the access token expires. */
79
+ expires_at: number;
80
+ }
81
+ export interface OAuthLoginOptions {
82
+ backendUrl: string;
83
+ /** Receives the authorization URL the operator must open in a browser (the CLI prints it). */
84
+ onAuthorizeUrl: (url: string) => void;
85
+ fetch?: typeof globalThis.fetch;
86
+ /** How long to wait for the browser redirect before giving up. Default 5 minutes. */
87
+ timeoutMs?: number;
88
+ }
@@ -0,0 +1,19 @@
1
+ // Wire contracts for `@docsxai/backend` — the leaf shared by the transport, token, OAuth-login and
2
+ // state-cache siblings (`backend-client-*.ts`), re-exported in full from `./backend-client.js`.
3
+ //
4
+ // The contract types are *redeclared* here (not imported from the backend package) so the engine
5
+ // stays decoupled at the package level — there's no runtime nor build-time dep on the backend.
6
+ // Drift is caught by the round-trip integration test that spins up a real stub. The shapes mirror
7
+ // the backend's `api.ts` exactly; if you change one, update the other and the test will tell you.
8
+ export const API_VERSION = "1";
9
+ export const API_VERSION_HEADER = "docsxai-api-version";
10
+ export class BackendClientError extends Error {
11
+ status;
12
+ body;
13
+ constructor(message, status, body) {
14
+ super(message);
15
+ this.status = status;
16
+ this.body = body;
17
+ this.name = "BackendClientError";
18
+ }
19
+ }
@@ -0,0 +1,7 @@
1
+ import { type BackendTokenFile, type OAuthLoginOptions } from "./backend-client-contracts.js";
2
+ /**
3
+ * Drive the authorization-code + PKCE handshake against the backend's minimal authorization
4
+ * server: start a loopback listener for the redirect, hand the authorize URL to the caller,
5
+ * await the code, exchange it (S256 verifier) for tokens.
6
+ */
7
+ export declare function oauthLogin(opts: OAuthLoginOptions): Promise<BackendTokenFile>;