@tita-agent/dsh-data-table 0.0.0-stage → 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 +21 -0
- package/README.md +172 -2
- package/cordis.patch.yml +7 -0
- package/lib/client.js +16766 -0
- package/lib/contract-envelope.d.ts +91 -0
- package/lib/contract-envelope.js +106 -0
- package/lib/contract-layout.d.ts +130 -0
- package/lib/contract-layout.js +138 -0
- package/lib/contract-refs.d.ts +39 -0
- package/lib/contract-refs.js +9 -0
- package/lib/contract-version.d.ts +311 -0
- package/lib/contract-version.js +270 -0
- package/lib/contract-wire.d.ts +147 -0
- package/lib/contract-wire.js +99 -0
- package/lib/contracts.d.ts +12 -0
- package/lib/contracts.js +12 -0
- package/lib/types/audit.d.ts +42 -0
- package/lib/types/audit.js +39 -0
- package/lib/types/client/ProjectImportMenu.d.ts +30 -0
- package/lib/types/client/ProjectImportMenu.js +70 -0
- package/lib/types/client/SheetPanel.d.ts +33 -0
- package/lib/types/client/SheetPanel.js +1766 -0
- package/lib/types/client/SheetToggle.d.ts +18 -0
- package/lib/types/client/SheetToggle.js +26 -0
- package/lib/types/client/index.d.ts +15 -0
- package/lib/types/client/index.js +110 -0
- package/lib/types/client/persistence/changes.d.ts +15 -0
- package/lib/types/client/persistence/changes.js +51 -0
- package/lib/types/client/persistence/ids.d.ts +34 -0
- package/lib/types/client/persistence/ids.js +48 -0
- package/lib/types/client/persistence/merge.d.ts +35 -0
- package/lib/types/client/persistence/merge.js +362 -0
- package/lib/types/client/store.d.ts +61 -0
- package/lib/types/client/store.js +152 -0
- package/lib/types/client/surface-context.d.ts +27 -0
- package/lib/types/client/surface-context.js +104 -0
- package/lib/types/client/surface-control.d.ts +2 -0
- package/lib/types/client/surface-control.js +12 -0
- package/lib/types/client/transport/workbookApi.d.ts +159 -0
- package/lib/types/client/transport/workbookApi.js +246 -0
- package/lib/types/client/univer/StandaloneWorkbookPage.d.ts +5 -0
- package/lib/types/client/univer/StandaloneWorkbookPage.js +7 -0
- package/lib/types/client/univer/WorkbookCanvas.d.ts +40 -0
- package/lib/types/client/univer/WorkbookCanvas.js +87 -0
- package/lib/types/client/univer/WorkbookView.d.ts +3 -0
- package/lib/types/client/univer/WorkbookView.js +2 -0
- package/lib/types/client/univer/loader.d.ts +18 -0
- package/lib/types/client/univer/loader.js +81 -0
- package/lib/types/client/univer/standalone-entry.d.ts +1 -0
- package/lib/types/client/univer/standalone-entry.js +19 -0
- package/lib/types/client/versions/VersionChrome.d.ts +33 -0
- package/lib/types/client/versions/VersionChrome.js +21 -0
- package/lib/types/client/versions/inspect.d.ts +70 -0
- package/lib/types/client/versions/inspect.js +88 -0
- package/lib/types/client/versions/preview-decoration.d.ts +11 -0
- package/lib/types/client/versions/preview-decoration.js +54 -0
- package/lib/types/client/versions/records.d.ts +19 -0
- package/lib/types/client/versions/records.js +70 -0
- package/lib/types/client/workspace-host.d.ts +61 -0
- package/lib/types/client/workspace-host.js +124 -0
- package/lib/types/collaboration-hub.d.ts +31 -0
- package/lib/types/collaboration-hub.js +69 -0
- package/lib/types/contract/ids.d.ts +24 -0
- package/lib/types/contract/ids.js +46 -0
- package/lib/types/contract/mutations.d.ts +25 -0
- package/lib/types/contract/mutations.js +782 -0
- package/lib/types/contract/operations.d.ts +54 -0
- package/lib/types/contract/operations.js +203 -0
- package/lib/types/contract/snapshots.d.ts +176 -0
- package/lib/types/contract/snapshots.js +351 -0
- package/lib/types/contract/univer-api-catalog.d.ts +29 -0
- package/lib/types/contract/univer-api-catalog.js +264 -0
- package/lib/types/errors.d.ts +8 -0
- package/lib/types/errors.js +12 -0
- package/lib/types/http/assets-routes.d.ts +3 -0
- package/lib/types/http/assets-routes.js +169 -0
- package/lib/types/http/collaboration.d.ts +10 -0
- package/lib/types/http/collaboration.js +154 -0
- package/lib/types/http/history.d.ts +15 -0
- package/lib/types/http/history.js +134 -0
- package/lib/types/http/project-files.d.ts +79 -0
- package/lib/types/http/project-files.js +206 -0
- package/lib/types/http/project-scope.d.ts +50 -0
- package/lib/types/http/project-scope.js +52 -0
- package/lib/types/http/routes.d.ts +98 -0
- package/lib/types/http/routes.js +323 -0
- package/lib/types/http/standalone.d.ts +9 -0
- package/lib/types/http/standalone.js +31 -0
- package/lib/types/http/trunk.d.ts +10 -0
- package/lib/types/http/trunk.js +322 -0
- package/lib/types/http/worktree.d.ts +9 -0
- package/lib/types/http/worktree.js +145 -0
- package/lib/types/index.d.ts +18 -0
- package/lib/types/index.js +156 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/invariant.js +15 -0
- package/lib/types/request-params.d.ts +21 -0
- package/lib/types/request-params.js +76 -0
- package/lib/types/store/paths.d.ts +68 -0
- package/lib/types/store/paths.js +148 -0
- package/lib/types/store/project.d.ts +88 -0
- package/lib/types/store/project.js +120 -0
- package/lib/types/store/read.d.ts +110 -0
- package/lib/types/store/read.js +404 -0
- package/lib/types/store/retention.d.ts +27 -0
- package/lib/types/store/retention.js +111 -0
- package/lib/types/store/write.d.ts +22 -0
- package/lib/types/store/write.js +210 -0
- package/lib/types/tools/arguments.d.ts +40 -0
- package/lib/types/tools/arguments.js +130 -0
- package/lib/types/tools/cells.d.ts +50 -0
- package/lib/types/tools/cells.js +298 -0
- package/lib/types/tools/index.d.ts +2 -0
- package/lib/types/tools/index.js +250 -0
- package/lib/types/tools/render.d.ts +47 -0
- package/lib/types/tools/render.js +290 -0
- package/lib/types/tools/univer-api.d.ts +18 -0
- package/lib/types/tools/univer-api.js +198 -0
- package/lib/types/tools/workbook-catalog.d.ts +3 -0
- package/lib/types/tools/workbook-catalog.js +83 -0
- package/lib/types/tools/workbook-edit.d.ts +192 -0
- package/lib/types/tools/workbook-edit.js +912 -0
- package/lib/types/tools/workbook-find.d.ts +11 -0
- package/lib/types/tools/workbook-find.js +89 -0
- package/lib/types/tools/workbook-history.d.ts +2 -0
- package/lib/types/tools/workbook-history.js +59 -0
- package/lib/types/tools/workbook-read.d.ts +21 -0
- package/lib/types/tools/workbook-read.js +204 -0
- package/lib/types/types.d.ts +377 -0
- package/lib/types/types.js +34 -0
- package/lib/types/univer/apply-operation.d.ts +14 -0
- package/lib/types/univer/apply-operation.js +398 -0
- package/lib/types/univer/chunk.d.ts +68 -0
- package/lib/types/univer/chunk.js +16 -0
- package/lib/types/univer/decoration.d.ts +34 -0
- package/lib/types/univer/decoration.js +88 -0
- package/lib/types/univer/diagnostics.d.ts +31 -0
- package/lib/types/univer/diagnostics.js +141 -0
- package/lib/types/univer/engine.d.ts +33 -0
- package/lib/types/univer/engine.js +338 -0
- package/lib/types/univer/entry.d.ts +8 -0
- package/lib/types/univer/entry.js +11 -0
- package/lib/types/univer/facade.d.ts +107 -0
- package/lib/types/univer/facade.js +1 -0
- package/lib/types/univer/fonts.d.ts +202 -0
- package/lib/types/univer/fonts.js +140 -0
- package/lib/types/univer/plugins/collaboration.d.ts +23 -0
- package/lib/types/univer/plugins/collaboration.js +47 -0
- package/lib/types/univer/plugins/export.d.ts +39 -0
- package/lib/types/univer/plugins/export.js +246 -0
- package/lib/types/univer/plugins/history.d.ts +24 -0
- package/lib/types/univer/plugins/history.js +50 -0
- package/lib/types/univer/plugins/version.d.ts +13 -0
- package/lib/types/univer/plugins/version.js +13 -0
- package/lib/types/version/diff.d.ts +36 -0
- package/lib/types/version/diff.js +156 -0
- package/lib/types/version/grouping.d.ts +7 -0
- package/lib/types/version/grouping.js +94 -0
- package/lib/types/version/records.d.ts +17 -0
- package/lib/types/version/records.js +102 -0
- package/lib/types/version/session.d.ts +36 -0
- package/lib/types/version/session.js +89 -0
- package/lib/types/version/summary.d.ts +24 -0
- package/lib/types/version/summary.js +111 -0
- package/lib/types/workbook-manager.d.ts +191 -0
- package/lib/types/workbook-manager.js +935 -0
- package/lib/types/xlsx-export-worker.d.ts +5 -0
- package/lib/types/xlsx-export-worker.js +24 -0
- package/lib/types/xlsx-export.d.ts +21 -0
- package/lib/types/xlsx-export.js +371 -0
- package/lib/types/xlsx-import-worker.d.ts +1 -0
- package/lib/types/xlsx-import-worker.js +19 -0
- package/lib/univer-client.js +690 -0
- package/lib/workbook-page.js +23826 -0
- package/lib/xlsx-export-worker.js +104 -0
- package/lib/xlsx-import-worker.js +46 -0
- package/package.json +106 -3
- package/src/audit.ts +58 -0
- package/src/client/ProjectImportMenu.tsx +100 -0
- package/src/client/SheetPanel.module.css +1578 -0
- package/src/client/SheetPanel.tsx +1934 -0
- package/src/client/SheetToggle.module.css +73 -0
- package/src/client/SheetToggle.tsx +65 -0
- package/src/client/contract.d.ts +19 -0
- package/src/client/index.ts +131 -0
- package/src/client/persistence/changes.ts +58 -0
- package/src/client/persistence/ids.ts +60 -0
- package/src/client/persistence/merge.ts +392 -0
- package/src/client/store.ts +188 -0
- package/src/client/surface-context.ts +123 -0
- package/src/client/surface-control.ts +13 -0
- package/src/client/transport/workbookApi.ts +355 -0
- package/src/client/univer/SheetView.module.css +110 -0
- package/src/client/univer/StandaloneWorkbookPage.module.css +8 -0
- package/src/client/univer/StandaloneWorkbookPage.tsx +15 -0
- package/src/client/univer/WorkbookCanvas.tsx +128 -0
- package/src/client/univer/WorkbookView.module.css +30 -0
- package/src/client/univer/WorkbookView.tsx +3 -0
- package/src/client/univer/loader.ts +90 -0
- package/src/client/univer/react-dom-client.d.ts +10 -0
- package/src/client/univer/standalone-entry.tsx +19 -0
- package/src/client/univer/standalone.css +12 -0
- package/src/client/univer/univer-locales.d.ts +89 -0
- package/src/client/univer/univer-modules.d.ts +26 -0
- package/src/client/versions/VersionChrome.tsx +114 -0
- package/src/client/versions/inspect.ts +138 -0
- package/src/client/versions/preview-decoration.ts +54 -0
- package/src/client/versions/records.ts +78 -0
- package/src/client/workspace-host.ts +147 -0
- package/src/collaboration-hub.ts +83 -0
- package/src/contract/ids.ts +50 -0
- package/src/contract/mutations.ts +791 -0
- package/src/contract/operations.ts +221 -0
- package/src/contract/snapshots.ts +383 -0
- package/src/contract/univer-api-catalog.ts +289 -0
- package/src/css-modules.d.ts +6 -0
- package/src/errors.ts +25 -0
- package/src/http/assets-routes.ts +177 -0
- package/src/http/collaboration.ts +144 -0
- package/src/http/history.ts +151 -0
- package/src/http/project-files.ts +227 -0
- package/src/http/project-scope.ts +77 -0
- package/src/http/routes.ts +364 -0
- package/src/http/standalone.ts +32 -0
- package/src/http/trunk.ts +322 -0
- package/src/http/worktree.ts +119 -0
- package/src/index.ts +165 -0
- package/src/invariant.ts +17 -0
- package/src/request-params.ts +92 -0
- package/src/store/paths.ts +165 -0
- package/src/store/project.ts +137 -0
- package/src/store/read.ts +454 -0
- package/src/store/retention.ts +106 -0
- package/src/store/write.ts +230 -0
- package/src/tools/arguments.ts +128 -0
- package/src/tools/cells.ts +298 -0
- package/src/tools/index.ts +274 -0
- package/src/tools/render.ts +301 -0
- package/src/tools/univer-api.ts +243 -0
- package/src/tools/workbook-catalog.ts +94 -0
- package/src/tools/workbook-edit.ts +987 -0
- package/src/tools/workbook-find.ts +80 -0
- package/src/tools/workbook-history.ts +63 -0
- package/src/tools/workbook-read.ts +210 -0
- package/src/types.ts +432 -0
- package/src/univer/apply-operation.ts +369 -0
- package/src/univer/chunk.ts +77 -0
- package/src/univer/decoration.ts +111 -0
- package/src/univer/diagnostics.ts +136 -0
- package/src/univer/engine.ts +370 -0
- package/src/univer/entry.ts +14 -0
- package/src/univer/facade.ts +91 -0
- package/src/univer/fonts.ts +148 -0
- package/src/univer/overrides.css +33 -0
- package/src/univer/plugins/collaboration.ts +47 -0
- package/src/univer/plugins/export.ts +236 -0
- package/src/univer/plugins/history.ts +49 -0
- package/src/univer/plugins/version.ts +13 -0
- package/src/version/diff.ts +169 -0
- package/src/version/grouping.ts +96 -0
- package/src/version/records.ts +113 -0
- package/src/version/session.ts +90 -0
- package/src/version/summary.ts +117 -0
- package/src/workbook-manager.ts +1111 -0
- package/src/xlsx-export-worker.ts +34 -0
- package/src/xlsx-export.ts +373 -0
- package/src/xlsx-import-worker.ts +30 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `workbook_find`: locate a value across sheets, cells or formulas
|
|
3
|
+
* (docs/issue/0022 P3b).
|
|
4
|
+
*/
|
|
5
|
+
import type { WorkbookRange } from '../types.ts'
|
|
6
|
+
import { readWorkbook } from '../workbook-manager.ts'
|
|
7
|
+
|
|
8
|
+
import { parseRange } from '../request-params.ts'
|
|
9
|
+
import { asToolArguments, parseCount, parseNonEmptyString, sheetRefFromArguments, type ToolArguments } from './arguments.ts'
|
|
10
|
+
|
|
11
|
+
export function parseFindScope(value: unknown): 'sheets' | 'cells' | 'formulas' {
|
|
12
|
+
if (value === undefined || value === null) return 'cells'
|
|
13
|
+
if (value === 'sheets' || value === 'cells' || value === 'formulas') return value
|
|
14
|
+
throw new Error('scope 必须是 sheets、cells 或 formulas')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function parseFindMatch(value: unknown): 'contains' | 'exact' | 'regex' {
|
|
18
|
+
if (value === undefined || value === null) return 'contains'
|
|
19
|
+
if (value === 'contains' || value === 'exact' || value === 'regex') return value
|
|
20
|
+
throw new Error('match 必须是 contains、exact 或 regex')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function textMatcher(query: string, match: 'contains' | 'exact' | 'regex'): (value: unknown) => boolean {
|
|
24
|
+
if (match === 'regex') {
|
|
25
|
+
let expression: RegExp
|
|
26
|
+
try { expression = new RegExp(query, 'i') } catch { throw new Error('query 不是有效的正则表达式') }
|
|
27
|
+
return value => expression.test(String(value ?? ''))
|
|
28
|
+
}
|
|
29
|
+
const needle = query.toLocaleLowerCase()
|
|
30
|
+
return value => {
|
|
31
|
+
const text = String(value ?? '').toLocaleLowerCase()
|
|
32
|
+
return match === 'exact' ? text === needle : text.includes(needle)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function inRange(row: number, column: number, range: WorkbookRange | undefined): boolean {
|
|
37
|
+
return range === undefined || row >= range.startRow && row <= range.endRow && column >= range.startColumn && column <= range.endColumn
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function findWorkbookContent(workbookId: string, args: ToolArguments): Promise<unknown> {
|
|
41
|
+
const meta = await readWorkbook(workbookId)
|
|
42
|
+
if (meta === null) throw new Error('工作簿不存在:' + workbookId)
|
|
43
|
+
const query = parseNonEmptyString(args.query, 'query')
|
|
44
|
+
const scope = parseFindScope(args.scope)
|
|
45
|
+
const match = parseFindMatch(args.match)
|
|
46
|
+
const matches = textMatcher(query, match)
|
|
47
|
+
const limit = args.limit === undefined ? 100 : Math.min(500, parseCount(args.limit))
|
|
48
|
+
const selectedSheet = sheetRefFromArguments(args, 'sheet')
|
|
49
|
+
const range = args.range === undefined ? undefined : parseRange(args.range)
|
|
50
|
+
const root = asToolArguments(meta.snapshot, 'workbook snapshot')
|
|
51
|
+
const sheets = asToolArguments(root.sheets, 'workbook snapshot.sheets') as Record<string, Record<string, unknown>>
|
|
52
|
+
const results: Array<Record<string, unknown>> = []
|
|
53
|
+
for (const [sheetId, sheet] of Object.entries(sheets)) {
|
|
54
|
+
const sheetName = typeof sheet.name === 'string' ? sheet.name : sheetId
|
|
55
|
+
if (selectedSheet !== undefined && selectedSheet !== sheetId && selectedSheet !== sheetName) continue
|
|
56
|
+
if (scope === 'sheets') {
|
|
57
|
+
if (matches(sheetName)) results.push({ sheetId, sheetName })
|
|
58
|
+
if (results.length >= limit) break
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
const cellData = sheet.cellData
|
|
62
|
+
if (typeof cellData !== 'object' || cellData === null || Array.isArray(cellData)) continue
|
|
63
|
+
for (const [rowKey, line] of Object.entries(cellData as Record<string, unknown>)) {
|
|
64
|
+
const row = Number(rowKey)
|
|
65
|
+
if (!Number.isInteger(row) || typeof line !== 'object' || line === null || Array.isArray(line)) continue
|
|
66
|
+
for (const [columnKey, rawCell] of Object.entries(line as Record<string, unknown>)) {
|
|
67
|
+
const column = Number(columnKey)
|
|
68
|
+
if (!Number.isInteger(column) || !inRange(row, column, range)) continue
|
|
69
|
+
const cell = typeof rawCell === 'object' && rawCell !== null && !Array.isArray(rawCell) ? rawCell as Record<string, unknown> : {}
|
|
70
|
+
const candidate = scope === 'formulas' ? cell.f : cell.v
|
|
71
|
+
if (!matches(candidate)) continue
|
|
72
|
+
results.push({ sheetId, sheetName, row, column, value: cell.v ?? null, formula: typeof cell.f === 'string' ? cell.f : null })
|
|
73
|
+
if (results.length >= limit) break
|
|
74
|
+
}
|
|
75
|
+
if (results.length >= limit) break
|
|
76
|
+
}
|
|
77
|
+
if (results.length >= limit) break
|
|
78
|
+
}
|
|
79
|
+
return { ok: true, workbookId, revision: meta.revision ?? 0, query, scope, match, results, truncated: results.length >= limit }
|
|
80
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `workbook_history`: the version list, one version, one Step, and a diff
|
|
3
|
+
* (docs/issue/0022 P3b). It answers with the same record shape the HTTP history
|
|
4
|
+
* endpoint returns, which is why `version/records.ts` owns that shape.
|
|
5
|
+
*/
|
|
6
|
+
import type { WorkbookOperation } from '../types.ts'
|
|
7
|
+
import { readWorkbook, readWorkbookAtRevision, readWorkbookAtStep } from '../workbook-manager.ts'
|
|
8
|
+
import { diffWorkbookSnapshots } from '../version/diff.ts'
|
|
9
|
+
import { historyVersionsFromOperations } from '../version/grouping.ts'
|
|
10
|
+
|
|
11
|
+
import { activeHistoryRevisions, canReplayHistoryRevision, checkpointHasher, toVersionRecords } from '../version/records.ts'
|
|
12
|
+
import { parseIndex } from '../request-params.ts'
|
|
13
|
+
import { parseCount, type ToolArguments } from './arguments.ts'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export async function executeHistoryQuery(workbookId: string, args: ToolArguments): Promise<unknown> {
|
|
17
|
+
const action = args.action === undefined || args.action === null ? 'list' : args.action
|
|
18
|
+
if (action !== 'list' && action !== 'diff' && action !== 'replay') throw new Error('action 必须是 list、diff 或 replay')
|
|
19
|
+
if (action === 'diff') {
|
|
20
|
+
const fromRevision = parseIndex(args.fromRevision, 'fromRevision')
|
|
21
|
+
const toRevision = parseIndex(args.toRevision, 'toRevision')
|
|
22
|
+
if (fromRevision > toRevision) throw new Error('fromRevision 不得大于 toRevision')
|
|
23
|
+
const left = await readWorkbookAtRevision(workbookId, fromRevision)
|
|
24
|
+
const right = await readWorkbookAtRevision(workbookId, toRevision)
|
|
25
|
+
if (left === null || right === null) throw new Error('指定 revision 已被压缩或无法重放')
|
|
26
|
+
return { ok: true, action, workbookId, fromRevision, toRevision, changes: diffWorkbookSnapshots(left.snapshot, right.snapshot) }
|
|
27
|
+
}
|
|
28
|
+
if (action === 'replay') {
|
|
29
|
+
const rawRevision = args.revision ?? args.toRevision
|
|
30
|
+
const revision = parseIndex(rawRevision, 'revision')
|
|
31
|
+
if (args.step !== undefined && args.step !== null) {
|
|
32
|
+
const step = parseIndex(args.step, 'step')
|
|
33
|
+
const replay = await readWorkbookAtStep(workbookId, revision, step)
|
|
34
|
+
if (replay === null) throw new Error('指定 revision/step 已被压缩或无法重放')
|
|
35
|
+
return { ok: true, action, workbookId, ...replay }
|
|
36
|
+
}
|
|
37
|
+
const replay = await readWorkbookAtRevision(workbookId, revision)
|
|
38
|
+
if (replay === null) throw new Error('指定 revision 已被压缩或无法重放')
|
|
39
|
+
return { ok: true, action, workbookId, ...replay }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const meta = await readWorkbook(workbookId, { includeCheckpoints: true })
|
|
43
|
+
if (meta === null) throw new Error('工作簿不存在:' + workbookId)
|
|
44
|
+
const limit = args.limit === undefined ? 20 : Math.min(100, Math.max(1, parseCount(args.limit)))
|
|
45
|
+
const allOperations = (meta.operations ?? []).slice().sort((left, right) => left.revision - right.revision)
|
|
46
|
+
const currentRevision = meta.revision ?? 0
|
|
47
|
+
const byRevision = new Map(allOperations.map(operation => [operation.revision, operation]))
|
|
48
|
+
const active = activeHistoryRevisions(currentRevision, byRevision)
|
|
49
|
+
const checkpoints = (meta.checkpoints ?? []).map(checkpoint => checkpoint.revision).sort((left, right) => left - right)
|
|
50
|
+
const canReplay = (revision: number): boolean => canReplayHistoryRevision(revision, currentRevision, checkpoints, byRevision)
|
|
51
|
+
const fullVersions = historyVersionsFromOperations(allOperations, canReplay, active).slice(-limit)
|
|
52
|
+
const versions = fullVersions.map(({ steps: _steps, ...version }) => version)
|
|
53
|
+
return {
|
|
54
|
+
ok: true,
|
|
55
|
+
action,
|
|
56
|
+
workbookId,
|
|
57
|
+
revision: currentRevision,
|
|
58
|
+
versions,
|
|
59
|
+
// Unified cross-plugin model (PRD §7.1). `versions` stays for compatibility.
|
|
60
|
+
versionRecords: toVersionRecords(fullVersions, workbookId, checkpointHasher(meta.checkpoints ?? [])),
|
|
61
|
+
checkpoints,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `workbook_read`: one bounded look at a sheet, a range or a single cell
|
|
3
|
+
* (docs/issue/0022 P3b).
|
|
4
|
+
*/
|
|
5
|
+
import type { WorkbookRange } from '../types.ts'
|
|
6
|
+
import type { SheetRef } from '#contracts'
|
|
7
|
+
|
|
8
|
+
import { readWorkbook, readWorkbookRange, summarize } from '../workbook-manager.ts'
|
|
9
|
+
import { parseIndex, parseRange } from '../request-params.ts'
|
|
10
|
+
import { asToolArguments, sheetRefForSnapshot } from './arguments.ts'
|
|
11
|
+
import { sheetOverviews } from './render.ts'
|
|
12
|
+
|
|
13
|
+
export function parseReadIncludes(value: unknown): Set<string> {
|
|
14
|
+
if (value === undefined || value === null) return new Set()
|
|
15
|
+
if (!Array.isArray(value)) throw new Error('include 必须是字符串数组')
|
|
16
|
+
const allowed = new Set(['values', 'formulas', 'cells', 'styles', 'snapshot', 'backfill'])
|
|
17
|
+
const result = new Set<string>()
|
|
18
|
+
for (const item of value) {
|
|
19
|
+
if (typeof item !== 'string' || !allowed.has(item)) throw new Error('include 仅支持 values、formulas、cells、styles、snapshot、backfill')
|
|
20
|
+
result.add(item)
|
|
21
|
+
}
|
|
22
|
+
return result
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function snapshotSheet(snapshot: unknown, sheetRef: string): Record<string, unknown> {
|
|
26
|
+
const root = asToolArguments(snapshot, 'workbook snapshot')
|
|
27
|
+
const sheets = asToolArguments(root.sheets, 'workbook snapshot.sheets') as Record<string, Record<string, unknown>>
|
|
28
|
+
const direct = sheets[sheetRef]
|
|
29
|
+
if (direct !== undefined) return direct
|
|
30
|
+
const byName = Object.values(sheets).find(sheet => sheet.name === sheetRef)
|
|
31
|
+
if (byName === undefined) throw new Error('sheet 不存在:' + sheetRef)
|
|
32
|
+
return byName
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function snapshotCell(snapshot: unknown, sheetRef: string, row: number, column: number): Record<string, unknown> | undefined {
|
|
36
|
+
const sheet = snapshotSheet(snapshot, sheetRef)
|
|
37
|
+
const cellData = sheet.cellData
|
|
38
|
+
if (typeof cellData !== 'object' || cellData === null || Array.isArray(cellData)) return undefined
|
|
39
|
+
const line = (cellData as Record<string, unknown>)[String(row)]
|
|
40
|
+
if (typeof line !== 'object' || line === null || Array.isArray(line)) return undefined
|
|
41
|
+
const cell = (line as Record<string, unknown>)[String(column)]
|
|
42
|
+
return typeof cell === 'object' && cell !== null && !Array.isArray(cell) ? cell as Record<string, unknown> : undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function styleMatrix(snapshot: unknown, sheetRef: string, range: WorkbookRange): unknown[][] {
|
|
46
|
+
const result: unknown[][] = []
|
|
47
|
+
for (let row = range.startRow; row <= range.endRow; row += 1) {
|
|
48
|
+
const line: unknown[] = []
|
|
49
|
+
for (let column = range.startColumn; column <= range.endColumn; column += 1) {
|
|
50
|
+
line.push(snapshotCell(snapshot, sheetRef, row, column)?.s ?? null)
|
|
51
|
+
}
|
|
52
|
+
result.push(line)
|
|
53
|
+
}
|
|
54
|
+
return result
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Per-sheet backfill status for `workbook_read(kind: 'workbook')`
|
|
59
|
+
* (docs/issue/0011 I5 / T7).
|
|
60
|
+
*
|
|
61
|
+
* The agent kept a prose table of "which sheet still needs what" because the
|
|
62
|
+
* read surface could only show scale. This reports the four things that table
|
|
63
|
+
* tracked — data area, run provenance, run time and a conclusion block — so a
|
|
64
|
+
* workbook's completeness is visible without opening every sheet. `complete` is
|
|
65
|
+
* deliberately conservative: it is a hint, not a gate.
|
|
66
|
+
*/
|
|
67
|
+
export function sheetBackfillStatus(sheet: Record<string, unknown>): Record<string, unknown> {
|
|
68
|
+
const asRecord = (value: unknown): Record<string, unknown> | undefined =>
|
|
69
|
+
typeof value === 'object' && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : undefined
|
|
70
|
+
const stats = asRecord(sheet.dataSheetStats)
|
|
71
|
+
const meta = asRecord(sheet.dataSheetMeta)
|
|
72
|
+
const schema = asRecord(sheet.dataSheetSchema)
|
|
73
|
+
const dataRows = typeof stats?.rowCount === 'number' && stats.rowCount >= 0 ? stats.rowCount : 0
|
|
74
|
+
const cellData = asRecord(sheet.cellData) ?? {}
|
|
75
|
+
// A conclusion block is not tracked anywhere in the snapshot: any write
|
|
76
|
+
// recomputes the row counts, so the counter cannot separate data from prose.
|
|
77
|
+
// What it can do is spot rows that do not span the data schema, which is the
|
|
78
|
+
// shape a conclusion row takes in these report sheets (row 0 is the header).
|
|
79
|
+
const schemaColumns = Array.isArray(schema?.columns) ? schema.columns.length : 0
|
|
80
|
+
let conclusionRows = 0
|
|
81
|
+
for (const [key, line] of Object.entries(cellData)) {
|
|
82
|
+
const row = Number(key)
|
|
83
|
+
if (!Number.isInteger(row) || row <= 0) continue
|
|
84
|
+
const cells = asRecord(line)
|
|
85
|
+
if (cells === undefined) continue
|
|
86
|
+
const filled = Object.values(cells).filter(cell => {
|
|
87
|
+
const value = asRecord(cell)?.v
|
|
88
|
+
return value !== undefined && value !== null && String(value).length > 0
|
|
89
|
+
}).length
|
|
90
|
+
if (filled === 0) continue
|
|
91
|
+
if (schemaColumns > 0 && filled >= schemaColumns) continue
|
|
92
|
+
conclusionRows += 1
|
|
93
|
+
}
|
|
94
|
+
const provenance = meta === undefined
|
|
95
|
+
? null
|
|
96
|
+
: { source: meta.source ?? null, query: meta.query ?? null, notes: meta.notes ?? null }
|
|
97
|
+
return {
|
|
98
|
+
/** Highest row index holding content. Every write recomputes it, so it
|
|
99
|
+
* covers a conclusion block as well as the data — it is a bound, not a
|
|
100
|
+
* count of data rows. */
|
|
101
|
+
lastOccupiedRow: typeof stats?.rowCount === 'number' && stats.rowCount >= 0 ? stats.rowCount : 0,
|
|
102
|
+
columnCount: typeof stats?.columnCount === 'number' ? stats.columnCount : 0,
|
|
103
|
+
conclusionRows,
|
|
104
|
+
runAt: typeof meta?.generatedAt === 'string' ? meta.generatedAt : null,
|
|
105
|
+
provenance,
|
|
106
|
+
schemaUpdatedAt: typeof schema?.updatedAt === 'string' ? schema.updatedAt : null,
|
|
107
|
+
complete: (typeof stats?.rowCount === 'number' ? stats.rowCount : 0) > 0 && provenance !== null,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function readWorkbookTarget(workbookId: string, rawTarget: unknown, rawInclude: unknown): Promise<unknown> {
|
|
112
|
+
const meta = await readWorkbook(workbookId)
|
|
113
|
+
if (meta === null) throw new Error('工作簿不存在:' + workbookId)
|
|
114
|
+
const target = asToolArguments(rawTarget, 'target')
|
|
115
|
+
const kind = target.kind
|
|
116
|
+
if (kind !== 'workbook' && kind !== 'sheet' && kind !== 'range' && kind !== 'cells') {
|
|
117
|
+
throw new Error('target.kind 必须是 workbook、sheet、range 或 cells')
|
|
118
|
+
}
|
|
119
|
+
const include = parseReadIncludes(rawInclude)
|
|
120
|
+
const summary = summarize(meta)
|
|
121
|
+
const base = { ok: true, workbookId, revision: meta.revision ?? 0 }
|
|
122
|
+
|
|
123
|
+
if (kind === 'workbook') {
|
|
124
|
+
const sheets = sheetOverviews(meta.snapshot)
|
|
125
|
+
return {
|
|
126
|
+
...base,
|
|
127
|
+
workbook: { id: summary.id, name: summary.name, sheets, sheetCount: sheets.length, updatedAt: summary.updatedAt },
|
|
128
|
+
...(include.has('snapshot') ? { snapshot: meta.snapshot } : {}),
|
|
129
|
+
...(include.has('backfill')
|
|
130
|
+
? {
|
|
131
|
+
backfill: sheets.map(overview => ({
|
|
132
|
+
sheetId: overview.id,
|
|
133
|
+
name: overview.name,
|
|
134
|
+
...sheetBackfillStatus(snapshotSheet(meta.snapshot, overview.id)),
|
|
135
|
+
})),
|
|
136
|
+
}
|
|
137
|
+
: {}),
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const sheetRef = sheetRefForSnapshot(meta.snapshot, target, 'target.sheet')
|
|
142
|
+
const sheet = snapshotSheet(meta.snapshot, sheetRef)
|
|
143
|
+
const sheetSummary = summary.sheets.find(candidate => candidate.id === sheetRef || candidate.name === sheetRef)
|
|
144
|
+
const resolvedSheetRef: SheetRef = {
|
|
145
|
+
workbookId,
|
|
146
|
+
sheetId: sheetSummary?.id ?? String(sheet.id ?? sheetRef),
|
|
147
|
+
sheetName: sheetSummary?.name ?? String(sheet.name ?? sheetRef),
|
|
148
|
+
}
|
|
149
|
+
if (kind === 'sheet') {
|
|
150
|
+
const overview = sheetOverviews(meta.snapshot).find(candidate => candidate.id === resolvedSheetRef.sheetId)
|
|
151
|
+
return {
|
|
152
|
+
...base,
|
|
153
|
+
sheetRef: resolvedSheetRef,
|
|
154
|
+
sheet: {
|
|
155
|
+
id: resolvedSheetRef.sheetId,
|
|
156
|
+
name: resolvedSheetRef.sheetName,
|
|
157
|
+
rowCount: overview?.rowCount ?? sheet.rowCount ?? 0,
|
|
158
|
+
columnCount: overview?.columnCount ?? sheet.columnCount ?? 0,
|
|
159
|
+
maxRow: overview?.maxRow ?? null,
|
|
160
|
+
maxColumn: overview?.maxColumn ?? null,
|
|
161
|
+
nonEmptyCells: overview?.nonEmptyCells ?? 0,
|
|
162
|
+
},
|
|
163
|
+
...(include.has('snapshot') ? { snapshot: sheet } : {}),
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (kind === 'range') {
|
|
168
|
+
const range = parseRange(target.range)
|
|
169
|
+
const result = readWorkbookRange(meta.snapshot, sheetRef, range)
|
|
170
|
+
// `readWorkbookRange` materializes values, formulas and cells together, but
|
|
171
|
+
// returning all three inline spent the result budget three times over
|
|
172
|
+
// (docs/issue/0011 I1 / T5). Only `values` is unconditional; the heavier views
|
|
173
|
+
// have to be asked for.
|
|
174
|
+
return {
|
|
175
|
+
...base,
|
|
176
|
+
sheetRef: { ...resolvedSheetRef, range: `${range.startRow}:${range.startColumn}-${range.endRow}:${range.endColumn}` },
|
|
177
|
+
sheetId: result.sheetId,
|
|
178
|
+
sheetName: result.sheetName,
|
|
179
|
+
range: result.range,
|
|
180
|
+
values: result.values,
|
|
181
|
+
...(include.has('formulas') ? { formulas: result.formulas } : {}),
|
|
182
|
+
...(include.has('cells') ? { cells: result.cells } : {}),
|
|
183
|
+
...(include.has('styles') ? { styles: styleMatrix(meta.snapshot, result.sheetId, range) } : {}),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!Array.isArray(target.cells) || target.cells.length === 0 || target.cells.length > 500) {
|
|
188
|
+
throw new Error('target.cells 必须是 1 到 500 项的单元格坐标数组')
|
|
189
|
+
}
|
|
190
|
+
const cells = target.cells.map((rawCell, index) => {
|
|
191
|
+
const coordinate = asToolArguments(rawCell, 'target.cells[' + index + ']')
|
|
192
|
+
const row = parseIndex(coordinate.row, 'target.cells[' + index + '].row')
|
|
193
|
+
const column = parseIndex(coordinate.column, 'target.cells[' + index + '].column')
|
|
194
|
+
const cell = snapshotCell(meta.snapshot, sheetRef, row, column)
|
|
195
|
+
const value = cell?.v
|
|
196
|
+
return {
|
|
197
|
+
row,
|
|
198
|
+
column,
|
|
199
|
+
value: value === undefined ? null : value,
|
|
200
|
+
formula: typeof cell?.f === 'string' ? cell.f : null,
|
|
201
|
+
...(include.has('styles') ? { style: cell?.s ?? null } : {}),
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
return {
|
|
205
|
+
...base,
|
|
206
|
+
sheetRef: resolvedSheetRef,
|
|
207
|
+
sheet: { id: resolvedSheetRef.sheetId, name: resolvedSheetRef.sheetName },
|
|
208
|
+
cells,
|
|
209
|
+
}
|
|
210
|
+
}
|