aitable-workflow-core 0.1.13-beta.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.
@@ -0,0 +1,71 @@
1
+ type RunRecordStatus = "todo" | "running" | "review" | "done" | "blocked";
2
+ type RunSnapshot = {
3
+ records: Array<{
4
+ id: string;
5
+ title: string;
6
+ state: string;
7
+ status: RunRecordStatus;
8
+ currentStepId: string | null;
9
+ doneStepIds: string[];
10
+ createdAt: number;
11
+ updatedAt: number;
12
+ }>;
13
+ logs: Array<{
14
+ id: string;
15
+ ts: number;
16
+ itemId: string;
17
+ stepId: string;
18
+ level: "info" | "ai" | "ok" | "warn" | "error";
19
+ text: string;
20
+ }>;
21
+ };
22
+ type RunExecutionEvent = {
23
+ type: "run.record.detected";
24
+ record: {
25
+ id: string;
26
+ title: string;
27
+ state: string;
28
+ currentStep: string | null;
29
+ };
30
+ } | {
31
+ type: "run.step.started";
32
+ itemId: string;
33
+ stepId: string;
34
+ stepName: string;
35
+ } | {
36
+ type: "run.step.succeeded";
37
+ itemId: string;
38
+ stepId: string;
39
+ stepName: string;
40
+ logs?: string[];
41
+ } | {
42
+ type: "run.step.failed";
43
+ itemId: string;
44
+ stepId: string;
45
+ stepName: string;
46
+ error: string;
47
+ logs?: string[];
48
+ } | {
49
+ type: "run.record.blocked";
50
+ itemId: string;
51
+ stepId: string;
52
+ stepName: string;
53
+ reason: string;
54
+ logs?: string[];
55
+ } | {
56
+ type: "run.record.completed";
57
+ itemId: string;
58
+ };
59
+ type RunSnapshotLogLevel = RunSnapshot["logs"][number]["level"];
60
+ type RunSnapshotStepLevelResolver = (stepId: string, event: Extract<RunExecutionEvent, {
61
+ type: "run.step.started";
62
+ }>) => RunSnapshotLogLevel;
63
+ type ApplyRunExecutionEventOptions = {
64
+ stepLevel?: RunSnapshotStepLevelResolver;
65
+ now?: () => number;
66
+ id?: () => string;
67
+ maxLogs?: number;
68
+ };
69
+ declare function applyRunExecutionEvent(snapshot: RunSnapshot, event: RunExecutionEvent, options?: ApplyRunExecutionEventOptions): RunSnapshot;
70
+
71
+ export { type ApplyRunExecutionEventOptions, type RunExecutionEvent, type RunRecordStatus, type RunSnapshot, type RunSnapshotLogLevel, type RunSnapshotStepLevelResolver, applyRunExecutionEvent };
@@ -0,0 +1 @@
1
+ import{b as a}from"../chunk-J5QGFP2Q.js";export{a as applyRunExecutionEvent};
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "aitable-workflow-core",
3
+ "version": "0.1.13-beta.1",
4
+ "description": "配置驱动的 AI 表格工作流自动化引擎(core):YAML 定义业务流程,AI 逐步执行并回写钉钉 AI 表格;含 CLI 与工作流引擎。",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./run-state": {
14
+ "types": "./dist/shared/run-state.d.ts",
15
+ "default": "./dist/shared/run-state.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "wolai-crawler"
21
+ ],
22
+ "engines": {
23
+ "node": ">=20.18.0"
24
+ },
25
+ "publishConfig": {
26
+ "registry": "https://registry.npmjs.org",
27
+ "access": "public"
28
+ },
29
+ "dependencies": {
30
+ "@inquirer/prompts": "^8.5.2",
31
+ "cron-parser": "^5.6.2",
32
+ "dingtalk-stream": "^2.1.5",
33
+ "dotenv": "^17.4.2",
34
+ "exceljs": "^4.4.0",
35
+ "mammoth": "^1.12.0",
36
+ "pdf-parse": "^2.4.5",
37
+ "playwright": "^1.61.1",
38
+ "tsx": "^4.20.6",
39
+ "typescript": "^5.9.3",
40
+ "yaml": "^2.8.1",
41
+ "zod": "^4.1.13"
42
+ }
43
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "_comment": "wolai 帮助中心爬虫默认配置。请复制为 config.json 后按需修改,尤其是 storageState 路径。",
3
+ "startUrl": "https://wolai.dingtalk.com/iAA41DDCVKitBP6gFaBRnE",
4
+ "loginUrl": "https://wolai.dingtalk.com/",
5
+ "outputDir": ".sandbox/kb-validate/aitable-helpcenter",
6
+ "rawDir": ".sandbox/kb-validate/.wolai-raw",
7
+ "storageState": ".sandbox/kb-validate/.wolai-raw/auth/wolai.storageState.json",
8
+ "headed": false,
9
+ "timeoutMs": 120000,
10
+ "pageWaitMs": 3000,
11
+ "downloadWaitMs": 10000,
12
+ "cooldownMs": 1500,
13
+ "concurrency": 1,
14
+ "urlMappingFile": ".sandbox/kb-validate/.wolai-raw/url-mapping.json",
15
+ "skipExisting": true
16
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { debugPage } = require('./lib/debug-page');
4
+
5
+ const url = process.argv[2] || 'https://wolai.dingtalk.com/iGFNvAYVF7bmkFcp6pqc9c';
6
+ const headed = process.argv.includes('--headed');
7
+
8
+ debugPage(url, { headed }).catch((err) => {
9
+ console.error('Debug error:', err);
10
+ process.exit(1);
11
+ });
@@ -0,0 +1,118 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+
4
+ const { ensureDir, requireDependency, resolveRepoPath } = require('./runtime');
5
+
6
+ function findLocalBrowserExecutable() {
7
+ const candidates = [
8
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
9
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
10
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
11
+ ];
12
+ return candidates.find((c) => fs.existsSync(c));
13
+ }
14
+
15
+ function resolveBrowserLaunchOptions({ headed = false } = {}) {
16
+ const options = { headless: !headed };
17
+ const executablePath = findLocalBrowserExecutable();
18
+ if (executablePath) {
19
+ options.executablePath = executablePath;
20
+ console.log(` [browser] 使用本地浏览器: ${executablePath}`);
21
+ } else {
22
+ console.log(' [browser] 未找到本地 Chrome/Edge,将使用 Playwright 内置 Chromium');
23
+ }
24
+ return options;
25
+ }
26
+
27
+ async function launchBrowserContext({ storageState, headed = false, viewport = { width: 1440, height: 960 }, downloadsPath = null } = {}) {
28
+ const { chromium } = requireDependency('playwright');
29
+ const launchOptions = resolveBrowserLaunchOptions({ headed });
30
+ console.log(` [browser] headless=${launchOptions.headless}`);
31
+ const browser = await chromium.launch(launchOptions);
32
+
33
+ const contextOptions = { viewport };
34
+ if (storageState && fs.existsSync(resolveRepoPath(storageState))) {
35
+ contextOptions.storageState = resolveRepoPath(storageState);
36
+ }
37
+ if (downloadsPath) {
38
+ contextOptions.downloadsPath = downloadsPath;
39
+ }
40
+
41
+ const context = await browser.newContext(contextOptions);
42
+ const page = await context.newPage();
43
+
44
+ return { browser, context, page };
45
+ }
46
+
47
+ async function saveStorageState(context, storageStatePath) {
48
+ const absolutePath = resolveRepoPath(storageStatePath);
49
+ ensureDir(path.dirname(absolutePath));
50
+ await context.storageState({ path: absolutePath });
51
+ return absolutePath;
52
+ }
53
+
54
+ function attachNetworkCapture(page, { rules = [], responseLimit = 200 } = {}) {
55
+ const bucket = { responses: [], failures: [], console: [] };
56
+
57
+ page.on('response', async (response) => {
58
+ const url = response.url();
59
+ const shouldCapture = rules.some((rule) => {
60
+ if (typeof rule === 'string') return url.includes(rule);
61
+ if (rule instanceof RegExp) return rule.test(url);
62
+ return false;
63
+ });
64
+ if (!shouldCapture) return;
65
+
66
+ const entry = {
67
+ at: new Date().toISOString(),
68
+ url,
69
+ method: response.request().method(),
70
+ status: response.status(),
71
+ };
72
+ try {
73
+ const text = await response.text();
74
+ entry.bodyJson = JSON.parse(text);
75
+ } catch { /* ignore */ }
76
+
77
+ bucket.responses.push(entry);
78
+ while (bucket.responses.length > responseLimit) {
79
+ bucket.responses.shift();
80
+ }
81
+ });
82
+
83
+ page.on('requestfailed', (request) => {
84
+ const url = request.url();
85
+ const shouldCapture = rules.some((rule) => {
86
+ if (typeof rule === 'string') return url.includes(rule);
87
+ if (rule instanceof RegExp) return rule.test(url);
88
+ return false;
89
+ });
90
+ if (!shouldCapture) return;
91
+ bucket.failures.push({
92
+ at: new Date().toISOString(),
93
+ url,
94
+ method: request.method(),
95
+ failure: request.failure(),
96
+ });
97
+ });
98
+
99
+ page.on('console', (message) => {
100
+ bucket.console.push({
101
+ at: new Date().toISOString(),
102
+ type: message.type(),
103
+ text: message.text().slice(0, 5000),
104
+ });
105
+ while (bucket.console.length > 200) {
106
+ bucket.console.shift();
107
+ }
108
+ });
109
+
110
+ return bucket;
111
+ }
112
+
113
+ module.exports = {
114
+ launchBrowserContext,
115
+ saveStorageState,
116
+ attachNetworkCapture,
117
+ resolveBrowserLaunchOptions,
118
+ };