@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.
- package/LICENSE +202 -0
- package/README.md +130 -0
- package/dist/auth/api-login.d.ts +69 -0
- package/dist/auth/api-login.js +95 -0
- package/dist/auth/browser-session.d.ts +28 -0
- package/dist/auth/browser-session.js +43 -0
- package/dist/auth/cookie-jar.d.ts +58 -0
- package/dist/auth/cookie-jar.js +212 -0
- package/dist/auth/email-otp.d.ts +210 -0
- package/dist/auth/email-otp.js +166 -0
- package/dist/auth/http-basic.d.ts +5 -0
- package/dist/auth/http-basic.js +17 -0
- package/dist/auth/index.d.ts +47 -0
- package/dist/auth/index.js +137 -0
- package/dist/auth/jwt-injection.d.ts +153 -0
- package/dist/auth/jwt-injection.js +136 -0
- package/dist/auth/manual-capture.d.ts +35 -0
- package/dist/auth/manual-capture.js +30 -0
- package/dist/auth/mtls.d.ts +15 -0
- package/dist/auth/mtls.js +53 -0
- package/dist/auth/pat-header.d.ts +19 -0
- package/dist/auth/pat-header.js +34 -0
- package/dist/auth/storage-state-cache.d.ts +38 -0
- package/dist/auth/storage-state-cache.js +143 -0
- package/dist/auth/test-backdoor.d.ts +25 -0
- package/dist/auth/test-backdoor.js +51 -0
- package/dist/auth/totp.d.ts +39 -0
- package/dist/auth/totp.js +108 -0
- package/dist/auth/types.d.ts +86 -0
- package/dist/auth/types.js +57 -0
- package/dist/auth/ui-form.d.ts +204 -0
- package/dist/auth/ui-form.js +153 -0
- package/dist/auth/webauthn.d.ts +88 -0
- package/dist/auth/webauthn.js +67 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +3 -0
- package/dist/backend-client-contracts.d.ts +88 -0
- package/dist/backend-client-contracts.js +19 -0
- package/dist/backend-client-oauth-login.d.ts +7 -0
- package/dist/backend-client-oauth-login.js +90 -0
- package/dist/backend-client-state-cache.d.ts +73 -0
- package/dist/backend-client-state-cache.js +185 -0
- package/dist/backend-client-token.d.ts +18 -0
- package/dist/backend-client-token.js +94 -0
- package/dist/backend-client-transport.d.ts +66 -0
- package/dist/backend-client-transport.js +181 -0
- package/dist/backend-client.d.ts +5 -0
- package/dist/backend-client.js +18 -0
- package/dist/calibrate.d.ts +31 -0
- package/dist/calibrate.js +68 -0
- package/dist/cli-commands-authoring.d.ts +5 -0
- package/dist/cli-commands-authoring.js +403 -0
- package/dist/cli-commands-backend.d.ts +5 -0
- package/dist/cli-commands-backend.js +211 -0
- package/dist/cli-commands-docpack.d.ts +5 -0
- package/dist/cli-commands-docpack.js +280 -0
- package/dist/cli-commands-session.d.ts +4 -0
- package/dist/cli-commands-session.js +398 -0
- package/dist/cli-shared.d.ts +5 -0
- package/dist/cli-shared.js +45 -0
- package/dist/cli-usage.d.ts +1 -0
- package/dist/cli-usage.js +137 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +77 -0
- package/dist/diagnose.d.ts +50 -0
- package/dist/diagnose.js +168 -0
- package/dist/diff-compute.d.ts +13 -0
- package/dist/diff-compute.js +378 -0
- package/dist/diff-report.d.ts +7 -0
- package/dist/diff-report.js +125 -0
- package/dist/diff-types.d.ts +125 -0
- package/dist/diff-types.js +15 -0
- package/dist/diff.d.ts +3 -0
- package/dist/diff.js +16 -0
- package/dist/doc-pack-io.d.ts +30 -0
- package/dist/doc-pack-io.js +182 -0
- package/dist/doc-pack.d.ts +1814 -0
- package/dist/doc-pack.js +328 -0
- package/dist/doctor-checks-plugins.d.ts +2 -0
- package/dist/doctor-checks-plugins.js +136 -0
- package/dist/doctor-checks.d.ts +56 -0
- package/dist/doctor-checks.js +367 -0
- package/dist/doctor.d.ts +7 -0
- package/dist/doctor.js +62 -0
- package/dist/export/adf.d.ts +57 -0
- package/dist/export/adf.js +323 -0
- package/dist/export/playwright-test.d.ts +26 -0
- package/dist/export/playwright-test.js +221 -0
- package/dist/flow-file.d.ts +21 -0
- package/dist/flow-file.js +180 -0
- package/dist/flow-lint.d.ts +24 -0
- package/dist/flow-lint.js +203 -0
- package/dist/flow-runtime.d.ts +113 -0
- package/dist/flow-runtime.js +273 -0
- package/dist/flow-tree.d.ts +19 -0
- package/dist/flow-tree.js +104 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +31 -0
- package/dist/playwright-driver.d.ts +105 -0
- package/dist/playwright-driver.js +363 -0
- package/dist/playwright-instrumented-browser.d.ts +51 -0
- package/dist/playwright-instrumented-browser.js +189 -0
- package/dist/plugins/load.d.ts +22 -0
- package/dist/plugins/load.js +99 -0
- package/dist/plugins/lock.d.ts +40 -0
- package/dist/plugins/lock.js +122 -0
- package/dist/plugins/manifest.d.ts +70 -0
- package/dist/plugins/manifest.js +115 -0
- package/dist/plugins/plan.d.ts +51 -0
- package/dist/plugins/plan.js +279 -0
- package/dist/plugins/registry.d.ts +59 -0
- package/dist/plugins/registry.js +71 -0
- package/dist/plugins/runtime.d.ts +7 -0
- package/dist/plugins/runtime.js +27 -0
- package/dist/plugins/types.d.ts +58 -0
- package/dist/plugins/types.js +4 -0
- package/dist/plugins-cli.d.ts +1 -0
- package/dist/plugins-cli.js +191 -0
- package/dist/redact.d.ts +16 -0
- package/dist/redact.js +72 -0
- package/dist/style.d.ts +46 -0
- package/dist/style.js +151 -0
- package/dist/viewer-bin.d.ts +20 -0
- package/dist/viewer-bin.js +97 -0
- package/dist/workspace.d.ts +60 -0
- package/dist/workspace.js +172 -0
- package/dist/zip.d.ts +17 -0
- package/dist/zip.js +113 -0
- package/package.json +64 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Workspace scaffolding & config.
|
|
2
|
+
//
|
|
3
|
+
// A *workspace* is the directory docsxai reads/writes for a project: `flows/`, `docs/`, `auth/`, plus
|
|
4
|
+
// (gitignored) `.auth/` and `.viewer/`, plus a small `.docsxai.json` config so `run`/`render`/`capture-auth`
|
|
5
|
+
// don't need `--base-url` / `--ignore-https-errors` on every call. **It must live outside the app repo** — docsxai
|
|
6
|
+
// operates *on* a running app from outside; it never writes into the app's source tree.
|
|
7
|
+
//
|
|
8
|
+
// `docsxai init <dir> --app-url <url>` scaffolds one; `--persist tmp` puts it in a throwaway temp dir.
|
|
9
|
+
import { promises as fs } from "node:fs";
|
|
10
|
+
import * as os from "node:os";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
13
|
+
export const WORKSPACE_CONFIG_FILE = ".docsxai.json";
|
|
14
|
+
/** Thrown when a workspace-relative path resolves outside the workspace root. */
|
|
15
|
+
export class WorkspacePathEscapeError extends Error {
|
|
16
|
+
workspaceDir;
|
|
17
|
+
resolvedPath;
|
|
18
|
+
constructor(workspaceDir, resolvedPath) {
|
|
19
|
+
super(`path escapes workspace root ${workspaceDir}: ${resolvedPath}`);
|
|
20
|
+
this.workspaceDir = workspaceDir;
|
|
21
|
+
this.resolvedPath = resolvedPath;
|
|
22
|
+
this.name = "WorkspacePathEscapeError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function assertContained(root, candidate) {
|
|
26
|
+
// Prefix check on a path-separator boundary: `/ws/docs-evil` must not pass for root `/ws/docs`.
|
|
27
|
+
if (candidate !== root && !candidate.startsWith(root + path.sep)) {
|
|
28
|
+
throw new WorkspacePathEscapeError(root, candidate);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Resolve path segments against a workspace root, guaranteeing containment. */
|
|
32
|
+
export function resolveWorkspacePath(workspaceDir, ...segments) {
|
|
33
|
+
const root = path.resolve(workspaceDir);
|
|
34
|
+
const resolved = path.resolve(root, ...segments);
|
|
35
|
+
assertContained(root, resolved);
|
|
36
|
+
return resolved;
|
|
37
|
+
}
|
|
38
|
+
/** Realpath of the deepest existing ancestor of `p`, with the non-existing suffix re-appended. */
|
|
39
|
+
async function realpathDeepestExisting(p) {
|
|
40
|
+
let probe = p;
|
|
41
|
+
let suffix = "";
|
|
42
|
+
for (;;) {
|
|
43
|
+
try {
|
|
44
|
+
const real = await fs.realpath(probe);
|
|
45
|
+
return suffix ? path.join(real, suffix) : real;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
const parent = path.dirname(probe);
|
|
49
|
+
if (parent === probe)
|
|
50
|
+
return p; // not even the filesystem root resolves — give up lexically
|
|
51
|
+
suffix = suffix ? path.join(path.basename(probe), suffix) : path.basename(probe);
|
|
52
|
+
probe = parent;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Like {@link resolveWorkspacePath}, but additionally defends against symlink escape: the deepest
|
|
58
|
+
* existing ancestor of the resolved path is realpath'd and containment re-verified against the
|
|
59
|
+
* realpath'd workspace root. Use before writing to a path whose segments are user-influenced
|
|
60
|
+
* (flow names, step ids, role names) — a symlink inside the workspace pointing outside must throw.
|
|
61
|
+
*/
|
|
62
|
+
export async function resolveWorkspacePathReal(workspaceDir, ...segments) {
|
|
63
|
+
const resolved = resolveWorkspacePath(workspaceDir, ...segments);
|
|
64
|
+
const rootReal = await realpathDeepestExisting(path.resolve(workspaceDir));
|
|
65
|
+
const resolvedReal = await realpathDeepestExisting(resolved);
|
|
66
|
+
assertContained(rootReal, resolvedReal);
|
|
67
|
+
return resolved;
|
|
68
|
+
}
|
|
69
|
+
const README = (cfg) => `# docsxai workspace
|
|
70
|
+
|
|
71
|
+
This directory is a **docsxai workspace** — it holds the doc pack for one running app:
|
|
72
|
+
|
|
73
|
+
- \`flows/<flow>.flow.yaml\` — hand-editable flow-files (the source of truth for execution)
|
|
74
|
+
- \`docs/<flow>/{annotations.json, screenshots/, <step>.md}\`, \`docs/{style.yaml, locators.yaml}\` — the generated docs
|
|
75
|
+
- \`auth/strategy.yaml\` — how execution authenticates to the app
|
|
76
|
+
- \`.auth/<role>.json\` — captured session(s) (gitignored; never committed; never leaves this machine)
|
|
77
|
+
- \`.viewer/\` — the generated interactive viewer (gitignored)
|
|
78
|
+
- \`${WORKSPACE_CONFIG_FILE}\` — workspace config (\`app_url${cfg.app_url ? ` = ${cfg.app_url}` : ""}\`, \`ignore_https_errors = ${!!cfg.ignore_https_errors}\`)
|
|
79
|
+
|
|
80
|
+
**Do NOT place this directory inside the app's source repo.** docsxai documents the *running* app from outside;
|
|
81
|
+
keeping the workspace separate is what guarantees zero traces in the app repo.
|
|
82
|
+
|
|
83
|
+
Usage (from this directory):
|
|
84
|
+
|
|
85
|
+
\`\`\`
|
|
86
|
+
docsxai capture-auth . # if auth is manual-capture: opens an instrumented browser; log in, then run window.__docsxai.capture()
|
|
87
|
+
docsxai calibrate . --description path/to/flow.md # (when the calibration stages land) produce/refresh flow-files
|
|
88
|
+
docsxai run . # replay the flow-files headlessly; refresh annotations + screenshots
|
|
89
|
+
docsxai render . && open .viewer/index.html
|
|
90
|
+
\`\`\`
|
|
91
|
+
`;
|
|
92
|
+
async function isNonEmptyDir(p) {
|
|
93
|
+
try {
|
|
94
|
+
const entries = await fs.readdir(p);
|
|
95
|
+
return entries.length > 0;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Scaffold a workspace. Returns where it landed + what was created. */
|
|
102
|
+
export async function initWorkspace(opts) {
|
|
103
|
+
let dir;
|
|
104
|
+
let ephemeral = false;
|
|
105
|
+
if (opts.persistTmp) {
|
|
106
|
+
dir = await fs.mkdtemp(path.join(os.tmpdir(), "docsxai-workspace-"));
|
|
107
|
+
ephemeral = true;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
if (!opts.dir)
|
|
111
|
+
throw new Error("init: a target directory is required (or use --persist tmp)");
|
|
112
|
+
dir = path.resolve(opts.dir);
|
|
113
|
+
if (!opts.force && (await isNonEmptyDir(dir))) {
|
|
114
|
+
throw new Error(`init: ${dir} exists and is not empty — pick another dir or pass --force`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const role = opts.role ?? "editor";
|
|
118
|
+
const ttl = opts.ttl ?? "1h";
|
|
119
|
+
const trigger = opts.captureTrigger ?? "console";
|
|
120
|
+
const auth = opts.auth ?? "manual-capture";
|
|
121
|
+
const created = [];
|
|
122
|
+
for (const sub of ["flows", "docs", "auth", ".auth", ".viewer"]) {
|
|
123
|
+
await fs.mkdir(resolveWorkspacePath(dir, sub), { recursive: true });
|
|
124
|
+
}
|
|
125
|
+
created.push("flows/", "docs/", "auth/", ".auth/", ".viewer/");
|
|
126
|
+
await fs.writeFile(resolveWorkspacePath(dir, ".gitignore"), ".auth/\n.viewer/\n", "utf8");
|
|
127
|
+
created.push(".gitignore");
|
|
128
|
+
const cfg = {
|
|
129
|
+
schema: "docsxai/workspace@1",
|
|
130
|
+
...(opts.appUrl ? { app_url: opts.appUrl } : {}),
|
|
131
|
+
...(opts.ignoreHttpsErrors ? { ignore_https_errors: true } : {}),
|
|
132
|
+
created_at: new Date().toISOString(),
|
|
133
|
+
};
|
|
134
|
+
await fs.writeFile(resolveWorkspacePath(dir, WORKSPACE_CONFIG_FILE), JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
135
|
+
created.push(WORKSPACE_CONFIG_FILE);
|
|
136
|
+
if (auth === "manual-capture") {
|
|
137
|
+
const descriptor = {
|
|
138
|
+
schema: "docsxai/auth-strategy@1",
|
|
139
|
+
default_role: role,
|
|
140
|
+
roles: {
|
|
141
|
+
[role]: {
|
|
142
|
+
strategy: "manual-capture",
|
|
143
|
+
options: { capture_trigger: trigger },
|
|
144
|
+
cache: {
|
|
145
|
+
enabled: true,
|
|
146
|
+
store: "local",
|
|
147
|
+
ttl,
|
|
148
|
+
...(opts.authCookie ? { auth_cookie: opts.authCookie } : {}),
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
await fs.writeFile(resolveWorkspacePath(dir, "auth", "strategy.yaml"), stringifyYaml(descriptor, { lineWidth: 100 }), "utf8");
|
|
154
|
+
created.push("auth/strategy.yaml");
|
|
155
|
+
}
|
|
156
|
+
await fs.writeFile(resolveWorkspacePath(dir, "README.md"), README(cfg), "utf8");
|
|
157
|
+
created.push("README.md");
|
|
158
|
+
return { dir, created, ephemeral };
|
|
159
|
+
}
|
|
160
|
+
/** Read `<dir>/.docsxai.json` if present. Returns `null` if absent or unreadable. */
|
|
161
|
+
export async function loadWorkspaceConfig(dir) {
|
|
162
|
+
try {
|
|
163
|
+
const text = await fs.readFile(resolveWorkspacePath(dir, WORKSPACE_CONFIG_FILE), "utf8");
|
|
164
|
+
const parsed = JSON.parse(text);
|
|
165
|
+
if (parsed && parsed.schema === "docsxai/workspace@1")
|
|
166
|
+
return parsed;
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
package/dist/zip.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface ZipOptions {
|
|
2
|
+
workspace: string;
|
|
3
|
+
output: string;
|
|
4
|
+
/** Include the rendered viewer (`.viewer/`) in the archive. Default false — viewers are derived from the doc pack and re-rendering is cheap. */
|
|
5
|
+
includeViewer?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ZipResult {
|
|
8
|
+
output: string;
|
|
9
|
+
bytes: number;
|
|
10
|
+
/** Workspace-relative archive entries, in the (sorted) order they appear in the zip. */
|
|
11
|
+
entries: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare class ZipError extends Error {
|
|
14
|
+
readonly cause?: unknown | undefined;
|
|
15
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
16
|
+
}
|
|
17
|
+
export declare function zipDocPack(opts: ZipOptions): Promise<ZipResult>;
|
package/dist/zip.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Package a workspace's doc pack into a single archive for hand-off. Zips in-process (fflate) —
|
|
2
|
+
// no system `zip` binary required. Excludes operator-local state (`.auth/`, halt screenshots, the
|
|
3
|
+
// rendered `.viewer/` by default) so the archive is reviewer-ready without manual cleanup.
|
|
4
|
+
//
|
|
5
|
+
// Archives are deterministic: entries are sorted, every entry carries a fixed mtime (the zip
|
|
6
|
+
// format's 1980-01-01 epoch, built from local wall-clock fields so the stored bytes don't vary
|
|
7
|
+
// with the machine's timezone), and the compression level is pinned. Same tree → same bytes.
|
|
8
|
+
import { promises as fs } from "node:fs";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import { zipSync } from "fflate";
|
|
11
|
+
import { resolveWorkspacePath, resolveWorkspacePathReal, WorkspacePathEscapeError, } from "./workspace.js";
|
|
12
|
+
export class ZipError extends Error {
|
|
13
|
+
cause;
|
|
14
|
+
constructor(message, cause) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.cause = cause;
|
|
17
|
+
this.name = "ZipError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const DEFAULT_INCLUDES = ["flows/", "docs/", ".docsxai.json", "auth/strategy.yaml", "README.md"];
|
|
21
|
+
const FIXED_MTIME = new Date(1980, 0, 1);
|
|
22
|
+
const FIXED_LEVEL = 6;
|
|
23
|
+
function isExcluded(relPath, includeViewer) {
|
|
24
|
+
const segments = relPath.split("/");
|
|
25
|
+
if (segments.includes(".auth") || segments.includes("halts"))
|
|
26
|
+
return true;
|
|
27
|
+
if (!includeViewer && segments[0] === ".viewer")
|
|
28
|
+
return true;
|
|
29
|
+
const base = segments[segments.length - 1];
|
|
30
|
+
return base === ".DS_Store" || base.endsWith(".tmp");
|
|
31
|
+
}
|
|
32
|
+
async function collectFiles(workspace, relDir, includeViewer, out) {
|
|
33
|
+
const entries = await fs.readdir(resolveWorkspacePath(workspace, relDir), {
|
|
34
|
+
withFileTypes: true,
|
|
35
|
+
});
|
|
36
|
+
entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
39
|
+
if (isExcluded(rel, includeViewer))
|
|
40
|
+
continue;
|
|
41
|
+
if (entry.isDirectory()) {
|
|
42
|
+
await collectFiles(workspace, rel, includeViewer, out);
|
|
43
|
+
}
|
|
44
|
+
else if (entry.isFile()) {
|
|
45
|
+
out.push(rel);
|
|
46
|
+
}
|
|
47
|
+
else if (entry.isSymbolicLink()) {
|
|
48
|
+
// The archive is a hand-off artifact: follow a symlink only if its target stays inside the
|
|
49
|
+
// workspace — a link escaping the root must not exfiltrate whatever it points at.
|
|
50
|
+
let stat;
|
|
51
|
+
try {
|
|
52
|
+
await resolveWorkspacePathReal(workspace, rel);
|
|
53
|
+
stat = await fs.stat(resolveWorkspacePath(workspace, rel));
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
if (e instanceof WorkspacePathEscapeError)
|
|
57
|
+
continue;
|
|
58
|
+
continue; // broken symlink — nothing to archive
|
|
59
|
+
}
|
|
60
|
+
if (stat.isDirectory())
|
|
61
|
+
await collectFiles(workspace, rel, includeViewer, out);
|
|
62
|
+
else if (stat.isFile())
|
|
63
|
+
out.push(rel);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export async function zipDocPack(opts) {
|
|
68
|
+
const { workspace, output, includeViewer = false } = opts;
|
|
69
|
+
try {
|
|
70
|
+
await fs.access(workspace);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw new ZipError(`workspace doesn't exist: ${workspace}`);
|
|
74
|
+
}
|
|
75
|
+
const outAbs = path.resolve(output);
|
|
76
|
+
await fs.mkdir(path.dirname(outAbs), { recursive: true });
|
|
77
|
+
const includesAll = includeViewer ? [...DEFAULT_INCLUDES, ".viewer/"] : DEFAULT_INCLUDES;
|
|
78
|
+
const files = [];
|
|
79
|
+
let anyIncludePresent = false;
|
|
80
|
+
for (const inc of includesAll) {
|
|
81
|
+
const rel = inc.endsWith("/") ? inc.slice(0, -1) : inc;
|
|
82
|
+
let stat;
|
|
83
|
+
try {
|
|
84
|
+
stat = await fs.stat(resolveWorkspacePath(workspace, rel));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
continue; // skip absent
|
|
88
|
+
}
|
|
89
|
+
anyIncludePresent = true;
|
|
90
|
+
if (stat.isDirectory())
|
|
91
|
+
await collectFiles(workspace, rel, includeViewer, files);
|
|
92
|
+
else
|
|
93
|
+
files.push(rel);
|
|
94
|
+
}
|
|
95
|
+
if (!anyIncludePresent) {
|
|
96
|
+
throw new ZipError(`workspace ${workspace} has nothing to zip (no flows/ or docs/ found)`);
|
|
97
|
+
}
|
|
98
|
+
files.sort();
|
|
99
|
+
const zippable = {};
|
|
100
|
+
for (const rel of files) {
|
|
101
|
+
const data = await fs.readFile(resolveWorkspacePath(workspace, rel));
|
|
102
|
+
zippable[rel] = [new Uint8Array(data), { level: FIXED_LEVEL, mtime: FIXED_MTIME }];
|
|
103
|
+
}
|
|
104
|
+
let bytes;
|
|
105
|
+
try {
|
|
106
|
+
bytes = zipSync(zippable, { level: FIXED_LEVEL, mtime: FIXED_MTIME });
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
throw new ZipError(`failed to build archive: ${e.message}`, e);
|
|
110
|
+
}
|
|
111
|
+
await fs.writeFile(outAbs, bytes);
|
|
112
|
+
return { output: outAbs, bytes: bytes.length, entries: files };
|
|
113
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@docsxai/engine",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public",
|
|
7
|
+
"provenance": true
|
|
8
|
+
},
|
|
9
|
+
"description": "LLM-agnostic engine: flow-file parser + deterministic runtime + the docsxai CLI + the target-site auth-strategy layer.",
|
|
10
|
+
"license": "Apache-2.0",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/kalebteccom/docsxai.git",
|
|
14
|
+
"directory": "packages/engine"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/kalebteccom/docsxai#readme",
|
|
17
|
+
"bugs": "https://github.com/kalebteccom/docsxai/issues",
|
|
18
|
+
"keywords": [
|
|
19
|
+
"docsxai",
|
|
20
|
+
"documentation",
|
|
21
|
+
"playwright",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"agent"
|
|
24
|
+
],
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"types": "dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./cli": {
|
|
33
|
+
"default": "./dist/cli.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"bin": {
|
|
40
|
+
"docsxai": "./dist/cli.js"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"fflate": "^0.8.3",
|
|
44
|
+
"playwright-core": "^1.61.0",
|
|
45
|
+
"pngjs": "^7.0.0",
|
|
46
|
+
"yaml": "^2.5.0",
|
|
47
|
+
"zod": "^3.23.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^20.14.0",
|
|
51
|
+
"@types/pngjs": "^6.0.5",
|
|
52
|
+
"vitest": "^2.0.0",
|
|
53
|
+
"@docsxai/backend": "0.2.0"
|
|
54
|
+
},
|
|
55
|
+
"author": "Kalebtec <security@kalebtec.com>",
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=20"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "node ../../scripts/clean-dist.mjs && tsc -b tsconfig.build.json",
|
|
61
|
+
"typecheck": "tsc --noEmit",
|
|
62
|
+
"test": "vitest run --passWithNoTests"
|
|
63
|
+
}
|
|
64
|
+
}
|