@penpair/core 0.2.0 → 0.3.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/dist/adapters.js +58 -11
- package/dist/approvals.js +143 -29
- package/dist/atomic-output.js +9 -4
- package/dist/audit-rules.js +345 -79
- package/dist/backup-note.js +29 -8
- package/dist/baseline-coverage.js +169 -35
- package/dist/baselines-config.js +119 -29
- package/dist/baselines.js +280 -57
- package/dist/cli-arguments.js +18 -6
- package/dist/cli-main.js +1621 -392
- package/dist/client.d.ts +5 -2
- package/dist/client.js +127 -28
- package/dist/command-result.js +341 -52
- package/dist/coordination.js +91 -29
- package/dist/credentials.js +57 -13
- package/dist/deep-check.js +56 -12
- package/dist/design-spec.js +189 -49
- package/dist/endpoint.js +102 -34
- package/dist/export.js +86 -18
- package/dist/file-archive.js +33 -7
- package/dist/file-guard.js +29 -11
- package/dist/formats.js +215 -14
- package/dist/index.d.ts +8 -7
- package/dist/index.js +8 -7
- package/dist/inspection.d.ts +1 -1
- package/dist/inspection.js +378 -65
- package/dist/instance/config-schema.js +400 -41
- package/dist/instance/config.js +101 -30
- package/dist/instance/connect.js +151 -44
- package/dist/instance/connection.d.ts +26 -5
- package/dist/instance/connection.js +194 -21
- package/dist/instance/gitignore.js +5 -2
- package/dist/instance/init.js +32 -7
- package/dist/instance/runner.js +32 -10
- package/dist/instance/setup.js +46 -13
- package/dist/instance/tty.js +26 -8
- package/dist/journal.d.ts +10 -0
- package/dist/journal.js +212 -49
- package/dist/kit-smoke.js +82 -20
- package/dist/kit-source.js +10 -40
- package/dist/kit.js +102 -34
- package/dist/layout-policy.d.ts +2 -0
- package/dist/layout-policy.js +86 -0
- package/dist/layout.d.ts +7 -1
- package/dist/layout.js +27 -70
- package/dist/live-smoke.js +13 -4
- package/dist/local-profile.js +2 -1
- package/dist/mcp-server.js +360 -47
- package/dist/native-api.js +47 -11
- package/dist/onboard.js +33 -7
- package/dist/operation.d.ts +2 -2
- package/dist/operation.js +279 -69
- package/dist/page-session.d.ts +23 -0
- package/dist/page-session.js +142 -0
- package/dist/png.js +18 -3
- package/dist/portable-tokens.js +254 -57
- package/dist/probe.d.ts +1 -1
- package/dist/probe.js +48 -11
- package/dist/project-input.js +24 -10
- package/dist/project-snippets.js +18 -6
- package/dist/project-status.js +133 -24
- package/dist/registry.js +217 -60
- package/dist/run-file.js +129 -30
- package/dist/shots.js +108 -26
- package/dist/skill-instructions.js +91 -22
- package/dist/skills.js +129 -39
- package/dist/snippet.js +27 -7
- package/dist/snippets/read.js +8 -2
- package/dist/structure-snapshot.d.ts +12 -0
- package/dist/structure-snapshot.js +439 -85
- package/dist/token-edit.js +219 -64
- package/dist/token-projection.d.ts +56 -0
- package/dist/token-projection.js +286 -0
- package/dist/tokens-diff.js +125 -32
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/penpair-design/SKILL.md +3 -0
- package/skills/penpair-design/references/penpot-plugin-gotchas.md +2 -2
- package/skills/penpair-design/references/project-facts.md +2 -0
- package/skills/penpair-handoff/SKILL.md +2 -0
- package/skills/penpair-handoff/references/comparison-companion.md +13 -0
- package/skills/penpair-handoff/references/handoff-flow.md +2 -0
- package/skills/penpair-recipes/SKILL.md +5 -0
- package/skills/penpair-setup/SKILL.md +3 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -3
- package/dist/docker/lifecycle.d.ts +0 -96
- package/dist/docker/lifecycle.js +0 -273
- package/dist/docker/secrets.d.ts +0 -11
- package/dist/docker/secrets.js +0 -38
- package/dist/docker/template.d.ts +0 -8
- package/dist/docker/template.js +0 -158
- package/dist/docker-profile.d.ts +0 -114
- package/dist/docker-profile.js +0 -18
- package/dist/instance/lifecycle.d.ts +0 -129
- package/dist/instance/lifecycle.js +0 -336
- package/dist/instance/secrets.d.ts +0 -11
- package/dist/instance/secrets.js +0 -38
- package/dist/instance/template.d.ts +0 -8
- package/dist/instance/template.js +0 -158
package/dist/skills.js
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import { atomicWriteFile } from "./atomic-output.js";
|
|
2
2
|
import { resolveProjectOutput } from "./project-input.js";
|
|
3
|
-
import { planSkillInstructions, applySkillInstructions } from "./skill-instructions.js";
|
|
3
|
+
import { planSkillInstructions, applySkillInstructions, } from "./skill-instructions.js";
|
|
4
4
|
import { lstat, mkdir, readdir, readFile, symlink } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
/** The skills shipped inside the package (`skills/` next to `dist/`). */
|
|
8
8
|
export const bundledSkillsDirectory = () => path.join(path.dirname(fileURLToPath(import.meta.url)), "../skills");
|
|
9
9
|
/** Files a project fills in. They are written once and never replaced, not even with --force. */
|
|
10
|
-
const projectOwned = new Set([
|
|
10
|
+
const projectOwned = new Set([
|
|
11
|
+
"references/project-facts.md",
|
|
12
|
+
"PROJECT.md",
|
|
13
|
+
]);
|
|
11
14
|
export async function listBundledSkills(directory = bundledSkillsDirectory()) {
|
|
12
|
-
const entries = await readdir(directory, {
|
|
13
|
-
|
|
15
|
+
const entries = await readdir(directory, {
|
|
16
|
+
withFileTypes: true,
|
|
17
|
+
});
|
|
18
|
+
return entries
|
|
19
|
+
.filter((entry) => entry.isDirectory())
|
|
20
|
+
.map((entry) => entry.name)
|
|
21
|
+
.sort();
|
|
14
22
|
}
|
|
15
23
|
async function filesUnder(directory, prefix = "") {
|
|
16
24
|
const out = [];
|
|
17
|
-
for (const entry of await readdir(path.join(directory, prefix), {
|
|
25
|
+
for (const entry of await readdir(path.join(directory, prefix), {
|
|
26
|
+
withFileTypes: true,
|
|
27
|
+
})) {
|
|
18
28
|
const relative = path.posix.join(prefix, entry.name);
|
|
19
|
-
if (entry.isDirectory())
|
|
29
|
+
if (entry.isDirectory()) {
|
|
20
30
|
out.push(...(await filesUnder(directory, relative)));
|
|
21
|
-
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
22
33
|
out.push(relative);
|
|
34
|
+
}
|
|
23
35
|
}
|
|
24
36
|
return out.sort();
|
|
25
37
|
}
|
|
@@ -28,8 +40,9 @@ async function readIfExists(file) {
|
|
|
28
40
|
return await readFile(file, "utf8");
|
|
29
41
|
}
|
|
30
42
|
catch (error) {
|
|
31
|
-
if (error.code === "ENOENT")
|
|
43
|
+
if (error.code === "ENOENT") {
|
|
32
44
|
return undefined;
|
|
45
|
+
}
|
|
33
46
|
throw error;
|
|
34
47
|
}
|
|
35
48
|
}
|
|
@@ -39,8 +52,9 @@ async function exists(file) {
|
|
|
39
52
|
return true;
|
|
40
53
|
}
|
|
41
54
|
catch (error) {
|
|
42
|
-
if (error.code === "ENOENT")
|
|
55
|
+
if (error.code === "ENOENT") {
|
|
43
56
|
return false;
|
|
57
|
+
}
|
|
44
58
|
throw error;
|
|
45
59
|
}
|
|
46
60
|
}
|
|
@@ -49,17 +63,31 @@ export async function describeSkill(skillDirectory) {
|
|
|
49
63
|
const text = (await readIfExists(path.join(skillDirectory, "SKILL.md"))) ?? "";
|
|
50
64
|
const name = /^name:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? path.basename(skillDirectory);
|
|
51
65
|
const description = /^description:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? "";
|
|
52
|
-
return {
|
|
66
|
+
return {
|
|
67
|
+
name,
|
|
68
|
+
summary: description.split(/(?<=\.)\s/)[0] ?? description,
|
|
69
|
+
};
|
|
53
70
|
}
|
|
54
71
|
/** Skills that changed name. Installing the new one leaves the old folder behind, which `installSkills` then reports. */
|
|
55
|
-
export const renamedSkills = {
|
|
72
|
+
export const renamedSkills = {
|
|
73
|
+
"penpot-design": "penpair-design",
|
|
74
|
+
};
|
|
56
75
|
/** Where the penpair skills already live in a project as real folders (not links), so a later install goes to the same place. */
|
|
57
76
|
export async function existingSkillsFolder(root) {
|
|
58
|
-
for (const folder of [
|
|
59
|
-
|
|
77
|
+
for (const folder of [
|
|
78
|
+
".skills",
|
|
79
|
+
".claude/skills",
|
|
80
|
+
".agents/skills",
|
|
81
|
+
]) {
|
|
82
|
+
for (const name of [
|
|
83
|
+
"penpair-design",
|
|
84
|
+
"penpair-setup",
|
|
85
|
+
...Object.keys(renamedSkills),
|
|
86
|
+
]) {
|
|
60
87
|
const found = await lstat(path.join(root, folder, name)).catch(() => undefined);
|
|
61
|
-
if (found?.isDirectory())
|
|
88
|
+
if (found?.isDirectory()) {
|
|
62
89
|
return folder;
|
|
90
|
+
}
|
|
63
91
|
}
|
|
64
92
|
}
|
|
65
93
|
return undefined;
|
|
@@ -70,16 +98,38 @@ export async function installSkills(options) {
|
|
|
70
98
|
await resolveProjectOutput(options.root, to);
|
|
71
99
|
const names = await listBundledSkills(source);
|
|
72
100
|
const chosen = options.only ?? names;
|
|
73
|
-
for (const name of chosen)
|
|
74
|
-
if (!names.includes(name))
|
|
101
|
+
for (const name of chosen) {
|
|
102
|
+
if (!names.includes(name)) {
|
|
75
103
|
throw new Error(`no skill named ${name}; shipped: ${names.join(", ")}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
76
106
|
const rows = await Promise.all(chosen.map(async (name) => {
|
|
77
107
|
const { summary } = await describeSkill(path.join(source, name));
|
|
78
|
-
const link = path.posix
|
|
108
|
+
const link = path.posix
|
|
109
|
+
.join(to, name, "SKILL.md")
|
|
110
|
+
.split("/")
|
|
111
|
+
.map((part) => encodeURIComponent(part).replace(/[()]/g, (character) => `%${character.charCodeAt(0).toString(16)}`))
|
|
112
|
+
.join("/");
|
|
79
113
|
return `| [${name}](${link}) | ${summary.replace(/\|/g, "\\|")} |`;
|
|
80
114
|
}));
|
|
81
115
|
const instructionPlan = await planSkillInstructions(options.root, rows, options.agents);
|
|
82
|
-
const report = {
|
|
116
|
+
const report = {
|
|
117
|
+
to,
|
|
118
|
+
installed: [],
|
|
119
|
+
unchanged: [],
|
|
120
|
+
kept: [],
|
|
121
|
+
linked: [],
|
|
122
|
+
agentsRows: rows,
|
|
123
|
+
legacy: [],
|
|
124
|
+
instructions: instructionPlan.report,
|
|
125
|
+
...(options.dryRun
|
|
126
|
+
? {
|
|
127
|
+
dryRun: true,
|
|
128
|
+
wouldInstall: [],
|
|
129
|
+
wouldLink: [],
|
|
130
|
+
}
|
|
131
|
+
: {}),
|
|
132
|
+
};
|
|
83
133
|
const writes = new Map();
|
|
84
134
|
const links = [];
|
|
85
135
|
for (const name of chosen) {
|
|
@@ -89,56 +139,96 @@ export async function installSkills(options) {
|
|
|
89
139
|
const shipped = await readFile(path.join(from, file), "utf8");
|
|
90
140
|
const present = await readIfExists(target);
|
|
91
141
|
const label = path.posix.join(to, name, file);
|
|
92
|
-
if (present === shipped)
|
|
142
|
+
if (present === shipped) {
|
|
93
143
|
report.unchanged.push(label);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
144
|
+
}
|
|
145
|
+
else if (present === undefined || (options.force && !projectOwned.has(file))) {
|
|
146
|
+
writes.set(target, {
|
|
147
|
+
label,
|
|
148
|
+
before: present,
|
|
149
|
+
content: shipped,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
97
153
|
report.kept.push(label);
|
|
154
|
+
}
|
|
98
155
|
}
|
|
99
|
-
if (options.links !== false)
|
|
100
|
-
for (const hub of [
|
|
156
|
+
if (options.links !== false) {
|
|
157
|
+
for (const hub of [
|
|
158
|
+
".agents/skills",
|
|
159
|
+
".claude/skills",
|
|
160
|
+
]) {
|
|
101
161
|
const link = path.join(options.root, hub, name);
|
|
102
|
-
if (path.resolve(link) === path.resolve(options.root, to, name) || await exists(link))
|
|
162
|
+
if (path.resolve(link) === path.resolve(options.root, to, name) || (await exists(link))) {
|
|
103
163
|
continue;
|
|
164
|
+
}
|
|
104
165
|
const safe = await resolveProjectOutput(options.root, path.join(hub, name));
|
|
105
166
|
const target = await resolveProjectOutput(options.root, path.join(to, name));
|
|
106
|
-
if (safe === target)
|
|
167
|
+
if (safe === target) {
|
|
107
168
|
continue;
|
|
108
|
-
|
|
169
|
+
}
|
|
170
|
+
links.push({
|
|
171
|
+
file: safe,
|
|
172
|
+
target: path.relative(path.dirname(safe), target),
|
|
173
|
+
label: path.posix.join(hub, name),
|
|
174
|
+
});
|
|
109
175
|
}
|
|
176
|
+
}
|
|
110
177
|
}
|
|
111
178
|
for (const [oldName, newName] of Object.entries(renamedSkills)) {
|
|
112
|
-
if (!chosen.includes(newName) || !(await exists(path.join(options.root, to, oldName))))
|
|
179
|
+
if (!chosen.includes(newName) || !(await exists(path.join(options.root, to, oldName)))) {
|
|
113
180
|
continue;
|
|
181
|
+
}
|
|
114
182
|
const oldFacts = await readIfExists(await resolveProjectOutput(options.root, path.join(to, oldName, "references/project-facts.md")));
|
|
115
183
|
const newFactsFile = await resolveProjectOutput(options.root, path.join(to, newName, "references/project-facts.md"));
|
|
116
184
|
const template = await readIfExists(path.join(source, newName, "references/project-facts.md"));
|
|
117
|
-
const current = writes.get(newFactsFile)?.content ?? await readIfExists(newFactsFile);
|
|
185
|
+
const current = writes.get(newFactsFile)?.content ?? (await readIfExists(newFactsFile));
|
|
118
186
|
const carriedFacts = oldFacts !== undefined && oldFacts !== template && current === template;
|
|
119
|
-
if (carriedFacts)
|
|
120
|
-
writes.set(newFactsFile, {
|
|
121
|
-
|
|
187
|
+
if (carriedFacts) {
|
|
188
|
+
writes.set(newFactsFile, {
|
|
189
|
+
label: path.posix.join(to, newName, "references/project-facts.md"),
|
|
190
|
+
before: await readIfExists(newFactsFile),
|
|
191
|
+
content: oldFacts,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
report.legacy.push({
|
|
195
|
+
folder: path.posix.join(to, oldName),
|
|
196
|
+
renamedTo: newName,
|
|
197
|
+
carriedFacts: carriedFacts && !options.dryRun,
|
|
198
|
+
});
|
|
122
199
|
}
|
|
123
200
|
if (options.dryRun) {
|
|
124
|
-
report.wouldInstall = [
|
|
201
|
+
report.wouldInstall = [
|
|
202
|
+
...writes.values(),
|
|
203
|
+
].map((write) => write.label);
|
|
125
204
|
report.wouldLink = links.map((link) => link.label);
|
|
126
205
|
return report;
|
|
127
206
|
}
|
|
128
207
|
for (const [file, write] of writes) {
|
|
129
|
-
if (await readIfExists(file) !== write.before)
|
|
208
|
+
if ((await readIfExists(file)) !== write.before) {
|
|
130
209
|
throw new Error("A skill file changed after planning; nothing overwrote that file");
|
|
131
|
-
|
|
132
|
-
await
|
|
210
|
+
}
|
|
211
|
+
await mkdir(path.dirname(file), {
|
|
212
|
+
recursive: true,
|
|
213
|
+
});
|
|
214
|
+
await atomicWriteFile(file, write.content, {
|
|
215
|
+
replace: write.before !== undefined,
|
|
216
|
+
mode: write.before === undefined ? 0o644 : (await lstat(file)).mode & 0o777,
|
|
217
|
+
});
|
|
133
218
|
report.installed.push(write.label);
|
|
134
219
|
}
|
|
135
220
|
for (const link of links) {
|
|
136
|
-
await mkdir(path.dirname(link.file), {
|
|
221
|
+
await mkdir(path.dirname(link.file), {
|
|
222
|
+
recursive: true,
|
|
223
|
+
});
|
|
137
224
|
await symlink(link.target, link.file);
|
|
138
225
|
report.linked.push(link.label);
|
|
139
226
|
}
|
|
140
|
-
if (instructionPlan.report.path)
|
|
141
|
-
await mkdir(path.dirname(instructionPlan.report.path), {
|
|
227
|
+
if (instructionPlan.report.path) {
|
|
228
|
+
await mkdir(path.dirname(instructionPlan.report.path), {
|
|
229
|
+
recursive: true,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
142
232
|
report.instructions = await applySkillInstructions(instructionPlan);
|
|
143
233
|
return report;
|
|
144
234
|
}
|
package/dist/snippet.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
|
-
import { executeCode, maxSnippetBytes, UncertainExecutionError } from "./client.js";
|
|
2
|
+
import { executeCode, maxSnippetBytes, UncertainExecutionError, } from "./client.js";
|
|
3
3
|
import { PenpairError } from "./errors.js";
|
|
4
4
|
/**
|
|
5
5
|
* Wraps snippet body text for the Penpot editor: the parameters arrive as `CFG`, and any error comes back as
|
|
@@ -13,9 +13,14 @@ export const requirePage = `if (CFG.page && penpot.currentFile?.pages?.filter((p
|
|
|
13
13
|
if (CFG.page && penpot.currentPage.name !== CFG.page) return { ok: false, wrongPage: true, current: penpot.currentPage.name, message: "the editor shows " + penpot.currentPage.name + ", not " + CFG.page };`;
|
|
14
14
|
export function snippet(name, body, params = {}, effect = "unknown") {
|
|
15
15
|
const code = wrap(body, params);
|
|
16
|
-
if (Buffer.byteLength(code, "utf8") > maxSnippetBytes)
|
|
16
|
+
if (Buffer.byteLength(code, "utf8") > maxSnippetBytes) {
|
|
17
17
|
throw new PenpairError("snippet-too-large", `Snippet ${name} is ${Buffer.byteLength(code, "utf8")} bytes`);
|
|
18
|
-
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
name,
|
|
21
|
+
code,
|
|
22
|
+
effect,
|
|
23
|
+
};
|
|
19
24
|
}
|
|
20
25
|
/** Whether the plugin source is valid JavaScript for an async function. Catches typos without a Penpot connection. */
|
|
21
26
|
export function compiles(code) {
|
|
@@ -30,12 +35,27 @@ export function compiles(code) {
|
|
|
30
35
|
}
|
|
31
36
|
/** Runs a snippet and returns its result record. A snippet that reports `ok: false` throws, except a wrong page, which the caller handles. */
|
|
32
37
|
export async function runSnippet(client, s, options) {
|
|
33
|
-
const { result, runId, metrics } = await executeCode(client, s.code, {
|
|
34
|
-
|
|
38
|
+
const { result, runId, metrics } = await executeCode(client, s.code, {
|
|
39
|
+
effect: s.effect,
|
|
40
|
+
...options,
|
|
41
|
+
});
|
|
42
|
+
const record = typeof result === "object" && result !== null && !Array.isArray(result)
|
|
43
|
+
? result
|
|
44
|
+
: {
|
|
45
|
+
value: result,
|
|
46
|
+
};
|
|
35
47
|
if (record.ok === false && !record.wrongPage) {
|
|
36
|
-
if ((options?.effect ?? s.effect) !== "read")
|
|
48
|
+
if ((options?.effect ?? s.effect) !== "read") {
|
|
37
49
|
throw new UncertainExecutionError(runId, `${s.name}: ${record.message ?? "failed"}`);
|
|
50
|
+
}
|
|
38
51
|
throw new PenpairError("tool-failed", `${s.name}: ${record.message ?? "failed"}`);
|
|
39
52
|
}
|
|
40
|
-
return {
|
|
53
|
+
return {
|
|
54
|
+
...record,
|
|
55
|
+
execution: {
|
|
56
|
+
runId,
|
|
57
|
+
outcome: "ran",
|
|
58
|
+
metrics,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
41
61
|
}
|
package/dist/snippets/read.js
CHANGED
|
@@ -10,7 +10,9 @@ export function switchPage(name) {
|
|
|
10
10
|
if (!target) return { ok: false, message: "no page named " + CFG.name };
|
|
11
11
|
if (penpot.currentPage.id === target.id) return { ok: true, alreadyThere: true };
|
|
12
12
|
penpot.openPage(target);
|
|
13
|
-
return { ok: true, switched: true };`, {
|
|
13
|
+
return { ok: true, switched: true };`, {
|
|
14
|
+
name,
|
|
15
|
+
});
|
|
14
16
|
}
|
|
15
17
|
/** The top-level frames of a page: id, name and size, so they can be found by name and exported. */
|
|
16
18
|
export function frames(params) {
|
|
@@ -20,8 +22,12 @@ return { ok: true, readOnly: true, file: penpot.currentFile.name, fileId: penpot
|
|
|
20
22
|
}
|
|
21
23
|
/** One line per shape: type, name, size, position on the page, layout and sizing, the text it carries. */
|
|
22
24
|
export function tree(params) {
|
|
23
|
-
if (
|
|
25
|
+
if ((params.limit !== undefined &&
|
|
26
|
+
(!Number.isInteger(params.limit) || params.limit < 1 || params.limit > 1000)) ||
|
|
27
|
+
(params.depth !== undefined &&
|
|
28
|
+
(!Number.isInteger(params.depth) || params.depth < 0 || params.depth > 100))) {
|
|
24
29
|
throw new Error("Readable tree budgets require limit 1..1000 and depth 0..100; use inspect for larger structured scopes");
|
|
30
|
+
}
|
|
25
31
|
return readSnippet("tree", `${requirePage}
|
|
26
32
|
const depth=CFG.depth===undefined?3:CFG.depth,limit=CFG.limit||400,lines=[];
|
|
27
33
|
let depthLimited=false,textLimited=false,labelLimited=false;
|
|
@@ -48,8 +48,20 @@ export interface StructuralManifest {
|
|
|
48
48
|
export declare const structuralJson: (value: unknown) => string;
|
|
49
49
|
export declare function structuralPage(raw: InspectionDocument, name: string): StructuralPage;
|
|
50
50
|
export declare function assembleStructuralSnapshot(pages: StructuralPage[], rawTokens: InspectionDocument): StructuralSnapshot;
|
|
51
|
+
export interface SnapshotProgress {
|
|
52
|
+
pageId: string;
|
|
53
|
+
stage: "start" | "complete";
|
|
54
|
+
elapsedMs: number;
|
|
55
|
+
}
|
|
56
|
+
export declare class SnapshotCaptureError extends Error {
|
|
57
|
+
readonly progress: SnapshotProgress[];
|
|
58
|
+
readonly restorationError?: string | undefined;
|
|
59
|
+
constructor(message: string, progress: SnapshotProgress[], restorationError?: string | undefined);
|
|
60
|
+
}
|
|
51
61
|
export declare function captureStructuralSnapshot(client: PenpotToolClient, options?: ExecuteOptions & {
|
|
52
62
|
signal?: AbortSignal;
|
|
63
|
+
onStep?: (message: string) => void;
|
|
64
|
+
onProgress?: (event: SnapshotProgress) => void;
|
|
53
65
|
}): Promise<StructuralSnapshot>;
|
|
54
66
|
export declare function writeStructuralSnapshot(root: string, out: string, snapshot: StructuralSnapshot, redact?: (text: string) => string): Promise<StructuralManifest>;
|
|
55
67
|
export declare function readStructuralSnapshot(root: string, folder: string): Promise<StructuralSnapshot>;
|