@maxiaochao/pi-toolkit 0.9.4 → 0.10.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/CHANGELOG.md +18 -0
- package/README.md +1 -1
- package/package.json +5 -5
- package/packages/codex-edit/CHANGELOG.md +15 -0
- package/packages/codex-edit/README.md +10 -3
- package/packages/codex-edit/TESTING.md +40 -9
- package/packages/codex-edit/benchmark/explicit-edit-100.json +2824 -0
- package/packages/codex-edit/benchmark/export-evidence.mjs +77 -0
- package/packages/codex-edit/benchmark/incident-cases.mjs +37 -0
- package/packages/codex-edit/benchmark/prepare.mjs +181 -0
- package/packages/codex-edit/benchmark/results/explicit-edit-100-sol-low-v3.json +4562 -0
- package/packages/codex-edit/benchmark/run-with-budget.mjs +77 -0
- package/packages/codex-edit/benchmark/schedule.mjs +19 -0
- package/packages/codex-edit/benchmark/select-tasks.mjs +167 -0
- package/packages/codex-edit/codex-edit-explainer.html +4 -4
- package/packages/codex-edit/docs/test-report.html +181 -0
- package/packages/codex-edit/docs/test-report.md +242 -0
- package/packages/codex-edit/docs/test-summary.md +9 -0
- package/packages/codex-edit/extensions/codex-edit.ts +4 -2
- package/packages/codex-edit/package.json +8 -2
- package/packages/codex-edit/src/parser.ts +26 -3
- package/packages/codex-edit/tests/benchmark.test.mjs +81 -0
- package/packages/codex-edit/tests/comparison-cases.mjs +396 -0
- package/packages/codex-edit/tests/comparison-harness.mjs +44 -0
- package/packages/codex-edit/tests/generate-test-report.mjs +589 -0
- package/packages/codex-edit/tests/legacy-parser-v0.1.7.mjs +128 -0
- package/packages/codex-edit/tests/native-vs-codex.ts +109 -0
- package/packages/codex-edit/tests/package.test.mjs +8 -0
- package/packages/codex-edit/tests/parser.test.mjs +123 -1
- package/packages/codex-edit/tests/released-vs-local.test.mjs +31 -0
- package/packages/herdr-subagents/CHANGELOG.md +41 -0
- package/packages/herdr-subagents/README.md +155 -0
- package/packages/herdr-subagents/TESTING.md +24 -0
- package/packages/herdr-subagents/agents/explorer.md +8 -0
- package/packages/herdr-subagents/agents/reviewer.md +8 -0
- package/packages/{tiny-subagent → herdr-subagents}/agents/worker.md +1 -0
- package/packages/herdr-subagents/docs/demo.gif +0 -0
- package/packages/herdr-subagents/docs/screenshot.png +0 -0
- package/packages/herdr-subagents/extensions/herdr-subagents.ts +258 -0
- package/packages/herdr-subagents/extensions/subagent-report.ts +65 -0
- package/packages/{tiny-subagent → herdr-subagents}/package.json +7 -6
- package/packages/herdr-subagents/src/batch-runner.ts +219 -0
- package/packages/herdr-subagents/src/blocking.ts +175 -0
- package/packages/herdr-subagents/src/config.ts +125 -0
- package/packages/herdr-subagents/src/continue-run.ts +87 -0
- package/packages/herdr-subagents/src/dispatcher.ts +73 -0
- package/packages/herdr-subagents/src/errors.ts +3 -0
- package/packages/herdr-subagents/src/fleet-controller.ts +82 -0
- package/packages/herdr-subagents/src/fleet.ts +56 -0
- package/packages/herdr-subagents/src/herdr.ts +224 -0
- package/packages/herdr-subagents/src/history.ts +210 -0
- package/packages/herdr-subagents/src/live-agent.ts +87 -0
- package/packages/herdr-subagents/src/monitor.ts +18 -0
- package/packages/herdr-subagents/src/ownership.ts +17 -0
- package/packages/herdr-subagents/src/parameters.ts +21 -0
- package/packages/{tiny-subagent → herdr-subagents}/src/personas.ts +13 -2
- package/packages/herdr-subagents/src/protocol.ts +23 -0
- package/packages/herdr-subagents/src/queued-runs.ts +70 -0
- package/packages/herdr-subagents/src/reconcile.ts +167 -0
- package/packages/herdr-subagents/src/records.ts +213 -0
- package/packages/herdr-subagents/src/result-path.ts +7 -0
- package/packages/herdr-subagents/src/run-locator.ts +19 -0
- package/packages/herdr-subagents/src/run-pane-allocator.ts +78 -0
- package/packages/herdr-subagents/src/run-status.ts +23 -0
- package/packages/herdr-subagents/src/runner.ts +250 -0
- package/packages/herdr-subagents/src/state.ts +11 -0
- package/packages/herdr-subagents/src/tab-cleanup.ts +68 -0
- package/packages/herdr-subagents/src/tool.ts +217 -0
- package/packages/herdr-subagents/src/validation.ts +38 -0
- package/packages/herdr-subagents/tests/extension-contract.ts +225 -0
- package/packages/herdr-subagents/tests/run-tests.mjs +889 -0
- package/packages/todo/extensions/todo.ts +1 -1
- package/packages/tiny-subagent/CHANGELOG.md +0 -12
- package/packages/tiny-subagent/README.md +0 -128
- package/packages/tiny-subagent/TESTING.md +0 -20
- package/packages/tiny-subagent/docs/demo.gif +0 -0
- package/packages/tiny-subagent/docs/screenshot.png +0 -0
- package/packages/tiny-subagent/extensions/tiny-subagent.ts +0 -228
- package/packages/tiny-subagent/src/runner.ts +0 -319
- package/packages/tiny-subagent/tests/run-tests.mjs +0 -111
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { basename, resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
const [runDirectoryArg, outputArg] = process.argv.slice(2);
|
|
7
|
+
if (!runDirectoryArg || !outputArg) {
|
|
8
|
+
console.error("Usage: export-evidence.mjs <benchmark-run-directory> <output.json>");
|
|
9
|
+
process.exit(2);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const runDirectory = resolve(runDirectoryArg);
|
|
13
|
+
const output = resolve(outputArg);
|
|
14
|
+
const packageDirectory = resolve(new URL("..", import.meta.url).pathname);
|
|
15
|
+
const [progress, runManifest, taskManifestText] = await Promise.all([
|
|
16
|
+
readFile(resolve(runDirectory, "progress.json"), "utf8").then(JSON.parse),
|
|
17
|
+
readFile(resolve(runDirectory, "manifest.json"), "utf8").then(JSON.parse),
|
|
18
|
+
readFile(resolve(packageDirectory, "benchmark/explicit-edit-100.json"), "utf8"),
|
|
19
|
+
]);
|
|
20
|
+
const taskManifest = JSON.parse(taskManifestText);
|
|
21
|
+
const taskById = new Map(taskManifest.included.map((task) => [task.id, task]));
|
|
22
|
+
const sumAttempts = (result, field) => (result.recovery?.attempts ?? []).reduce(
|
|
23
|
+
(sum, attempt) => sum + Number(attempt.execution?.[field] ?? 0), 0,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const evidence = {
|
|
27
|
+
schemaVersion: 1,
|
|
28
|
+
runId: basename(runDirectory),
|
|
29
|
+
completed: progress.completed,
|
|
30
|
+
total: progress.total,
|
|
31
|
+
identities: {
|
|
32
|
+
taskManifestSha256: createHash("sha256").update(taskManifestText).digest("hex"),
|
|
33
|
+
focusedSelectionSha256: runManifest.focusedSelectionSha256,
|
|
34
|
+
configSha256: runManifest.configSha256,
|
|
35
|
+
verifierSha256: runManifest.verifierSha256,
|
|
36
|
+
},
|
|
37
|
+
source: taskManifest.source,
|
|
38
|
+
selection: {
|
|
39
|
+
seed: taskManifest.selection.seed,
|
|
40
|
+
scheduling: taskManifest.selection.scheduling,
|
|
41
|
+
neutral: taskManifest.included.filter((task) => task.cohort === "neutral").length,
|
|
42
|
+
challenge: taskManifest.included.filter((task) => task.cohort === "challenge").length,
|
|
43
|
+
},
|
|
44
|
+
run: {
|
|
45
|
+
contract: runManifest.contract,
|
|
46
|
+
attempts: runManifest.attempts,
|
|
47
|
+
oracleRecoveries: runManifest.oracleRecoveries,
|
|
48
|
+
concurrency: runManifest.concurrency,
|
|
49
|
+
timeoutMs: runManifest.timeoutMs,
|
|
50
|
+
},
|
|
51
|
+
harnesses: Object.fromEntries(Object.entries(runManifest.harnesses).map(([profile, harness]) => [profile, {
|
|
52
|
+
agentVersion: harness.agentVersion,
|
|
53
|
+
model: harness.model,
|
|
54
|
+
thinking: harness.thinking,
|
|
55
|
+
harnessVersion: harness.harnessVersion,
|
|
56
|
+
configurationId: harness.configurationId,
|
|
57
|
+
extensions: harness.configuration?.extensions ?? [],
|
|
58
|
+
}])),
|
|
59
|
+
results: progress.results.map((result) => ({
|
|
60
|
+
taskId: result.taskId,
|
|
61
|
+
profile: result.profile,
|
|
62
|
+
category: result.category,
|
|
63
|
+
cohort: taskById.get(result.taskId)?.cohort,
|
|
64
|
+
firstAttemptPassed: Boolean(result.recovery?.firstAttemptPassed),
|
|
65
|
+
eventuallyPassed: Boolean(result.recovery?.eventuallyPassed),
|
|
66
|
+
recoveriesUsed: Number(result.recovery?.recoveriesUsed ?? 0),
|
|
67
|
+
costUsd: sumAttempts(result, "costUsd"),
|
|
68
|
+
totalTokens: sumAttempts(result, "totalTokens"),
|
|
69
|
+
seconds: Number(result.seconds ?? 0),
|
|
70
|
+
toolCalls: sumAttempts(result, "toolCalls"),
|
|
71
|
+
failedToolCalls: sumAttempts(result, "failedToolCalls"),
|
|
72
|
+
providerFailures: (result.recovery?.attempts ?? []).filter((attempt) => attempt.execution?.providerFailure).length,
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
await writeFile(output, `${JSON.stringify(evidence, null, 2)}\n`, "utf8");
|
|
77
|
+
console.log(`Exported ${evidence.results.length} sanitized results to ${output}`);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const incidentCases = [
|
|
2
|
+
{
|
|
3
|
+
id: "incident-repeated-markdown-context",
|
|
4
|
+
protocolCaseId: "repeated-markdown-separator",
|
|
5
|
+
tier: "agent-and-protocol",
|
|
6
|
+
issue: "重复 Markdown 分隔符曾触发 ambiguous match",
|
|
7
|
+
prompt: "更新 SKILL.md 的 frontmatter 描述,并在 frontmatter 结束分隔符后插入指定说明;不要改动正文和页脚。",
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: "incident-repeated-update-same-path",
|
|
11
|
+
protocolCaseId: "repeated-update-same-path",
|
|
12
|
+
tier: "protocol-only",
|
|
13
|
+
issue: "同一个 raw path 的多个 Update File 块在发布版中冲突",
|
|
14
|
+
prompt: "验证同一次 apply_patch 调用中,同一路径的多个普通 Update File 块可以按顺序以上一块结果为输入。",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "incident-runner-type-rename",
|
|
18
|
+
protocolCaseId: "ordered-runner-type-renames",
|
|
19
|
+
tier: "agent-and-protocol",
|
|
20
|
+
issue: "runner.ts 中重复类型名的大范围重命名曾触发 ambiguous match",
|
|
21
|
+
prompt: "将 packages/herdr-subagents/src/runner.ts 中 TinySubagent 系列公开类型和引用完整重命名为 HerdrSubagents,保持其余代码逐字不变。",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "incident-multi-file-structural-edit",
|
|
25
|
+
protocolCaseId: "multi-file-add-delete-move",
|
|
26
|
+
tier: "agent-and-protocol",
|
|
27
|
+
issue: "一次 patch 中的 add/delete/move 需要原子预校验和执行",
|
|
28
|
+
prompt: "按要求移动并更新 old.ts,删除 remove.txt,再创建 added.txt;不要留下额外文件。",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "incident-bom-crlf",
|
|
32
|
+
protocolCaseId: "bom-and-crlf",
|
|
33
|
+
tier: "agent-and-protocol",
|
|
34
|
+
issue: "UTF-8 BOM 与 CRLF 曾导致原生精确文本匹配失败",
|
|
35
|
+
prompt: "只修改 app.ts 中的目标标识符,同时逐字保留 UTF-8 BOM、CRLF 和未指定行。",
|
|
36
|
+
},
|
|
37
|
+
];
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
|
|
9
|
+
const PI_VERSION = "0.85.1";
|
|
10
|
+
const MODEL = "openai-codex/gpt-5.6-sol";
|
|
11
|
+
const THINKING = "low";
|
|
12
|
+
const PACKAGE = "@maxiaochao/pi-codex-edit";
|
|
13
|
+
const nodeBinary = realpathSync(execFileSync("which", ["node"], { encoding: "utf8" }).trim());
|
|
14
|
+
const nodeRuntime = dirname(dirname(nodeBinary));
|
|
15
|
+
const root = resolve(".cache/codex-edit-benchmark");
|
|
16
|
+
const authFile = join(homedir(), ".pi/agent/auth.json");
|
|
17
|
+
const modelFile = join(homedir(), ".pi/agent/models.json");
|
|
18
|
+
for (const file of [authFile, modelFile]) {
|
|
19
|
+
if (!existsSync(file)) throw new Error(`Required Pi state is missing: ${file}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const npmJson = (...args) =>
|
|
23
|
+
JSON.parse(execFileSync("npm", args, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 }));
|
|
24
|
+
const latest = npmJson("view", `${PACKAGE}@latest`, "version", "dist.integrity", "--json");
|
|
25
|
+
latest.integrity ??= latest["dist.integrity"];
|
|
26
|
+
if (typeof latest.version !== "string" || typeof latest.integrity !== "string") {
|
|
27
|
+
throw new Error("npm latest did not resolve to an exact version and integrity");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
rmSync(root, { recursive: true, force: true });
|
|
31
|
+
mkdirSync(root, { recursive: true });
|
|
32
|
+
const tarballDirectory = join(root, "tarballs");
|
|
33
|
+
mkdirSync(tarballDirectory);
|
|
34
|
+
const [packed] = npmJson(
|
|
35
|
+
"pack",
|
|
36
|
+
resolve("packages/codex-edit"),
|
|
37
|
+
"--pack-destination",
|
|
38
|
+
tarballDirectory,
|
|
39
|
+
"--json",
|
|
40
|
+
);
|
|
41
|
+
if (!packed?.filename || !packed?.integrity) throw new Error("Local npm pack returned no integrity");
|
|
42
|
+
const localTarball = join(tarballDirectory, packed.filename);
|
|
43
|
+
|
|
44
|
+
function installRuntime(name, extensionSpec) {
|
|
45
|
+
const directory = join(root, "runtimes", name);
|
|
46
|
+
mkdirSync(directory, { recursive: true });
|
|
47
|
+
writeFileSync(join(directory, "package.json"), `${JSON.stringify({ private: true })}\n`);
|
|
48
|
+
const dependencies = [`@earendil-works/pi-coding-agent@${PI_VERSION}`];
|
|
49
|
+
if (extensionSpec) dependencies.push(extensionSpec);
|
|
50
|
+
execFileSync(
|
|
51
|
+
"npm",
|
|
52
|
+
["install", "--no-audit", "--no-fund", "--save-exact", ...dependencies],
|
|
53
|
+
{ cwd: directory, stdio: "inherit" },
|
|
54
|
+
);
|
|
55
|
+
const piManifest = JSON.parse(
|
|
56
|
+
readFileSync(join(directory, "node_modules/@earendil-works/pi-coding-agent/package.json")),
|
|
57
|
+
);
|
|
58
|
+
if (piManifest.version !== PI_VERSION) throw new Error(`${name}: Pi version drifted`);
|
|
59
|
+
if (extensionSpec) {
|
|
60
|
+
const extensionManifest = JSON.parse(
|
|
61
|
+
readFileSync(join(directory, "node_modules/@maxiaochao/pi-codex-edit/package.json")),
|
|
62
|
+
);
|
|
63
|
+
if (!extensionManifest.pi?.extensions?.includes("./extensions/codex-edit.ts")) {
|
|
64
|
+
throw new Error(`${name}: installed package does not declare Codex Edit`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return directory;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const nativeRuntime = installRuntime("native-edit");
|
|
71
|
+
const releasedRuntime = installRuntime("released-codex-edit", `${PACKAGE}@${latest.version}`);
|
|
72
|
+
const localRuntime = installRuntime("local-codex-edit", localTarball);
|
|
73
|
+
const lock = JSON.parse(readFileSync(join(releasedRuntime, "package-lock.json")));
|
|
74
|
+
const releasedLock = lock.packages?.["node_modules/@maxiaochao/pi-codex-edit"];
|
|
75
|
+
if (releasedLock?.version !== latest.version || releasedLock?.integrity !== latest.integrity) {
|
|
76
|
+
throw new Error("Released Codex Edit lockfile does not match the npm version and integrity pin");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const sourceDigest = createHash("sha256")
|
|
80
|
+
.update(readFileSync(resolve("packages/codex-edit/src/parser.ts")))
|
|
81
|
+
.update(readFileSync(resolve("packages/codex-edit/extensions/codex-edit.ts")))
|
|
82
|
+
.digest("hex");
|
|
83
|
+
const commonArgs = [
|
|
84
|
+
"--model",
|
|
85
|
+
MODEL,
|
|
86
|
+
"--thinking",
|
|
87
|
+
THINKING,
|
|
88
|
+
"--no-extensions",
|
|
89
|
+
"--no-skills",
|
|
90
|
+
"--no-prompt-templates",
|
|
91
|
+
"--no-themes",
|
|
92
|
+
"--no-context-files",
|
|
93
|
+
"--tools",
|
|
94
|
+
"read,bash,edit,write",
|
|
95
|
+
"--session-dir",
|
|
96
|
+
"/state/pi/sessions",
|
|
97
|
+
"--mode",
|
|
98
|
+
"json",
|
|
99
|
+
"-p",
|
|
100
|
+
];
|
|
101
|
+
function adapter(name, runtime, extension) {
|
|
102
|
+
const extensionEntry = extension
|
|
103
|
+
? join(runtime, "node_modules/@maxiaochao/pi-codex-edit/extensions/codex-edit.ts")
|
|
104
|
+
: null;
|
|
105
|
+
const extensionIdentity =
|
|
106
|
+
name === "released-codex-edit"
|
|
107
|
+
? `${PACKAGE}@${latest.version} (${latest.integrity})`
|
|
108
|
+
: name === "local-codex-edit"
|
|
109
|
+
? `${PACKAGE}@${packed.version}+local (${packed.integrity})`
|
|
110
|
+
: null;
|
|
111
|
+
return {
|
|
112
|
+
kind: "pi-default",
|
|
113
|
+
command: join(runtime, "node_modules/.bin/pi"),
|
|
114
|
+
args: [
|
|
115
|
+
...commonArgs,
|
|
116
|
+
...(extensionEntry ? ["--extension", extensionEntry] : []),
|
|
117
|
+
"--",
|
|
118
|
+
"{prompt}",
|
|
119
|
+
],
|
|
120
|
+
version: PI_VERSION,
|
|
121
|
+
model: MODEL,
|
|
122
|
+
thinking: THINKING,
|
|
123
|
+
transport: "harness-native",
|
|
124
|
+
ready: false,
|
|
125
|
+
readOnly: [nodeRuntime, join(runtime, "node_modules")],
|
|
126
|
+
seedFiles: { "pi/auth.json": authFile, "pi/models.json": modelFile },
|
|
127
|
+
env: {
|
|
128
|
+
PATH: `${dirname(nodeBinary)}:/usr/local/bin:/usr/bin:/bin`,
|
|
129
|
+
PI_CODING_AGENT_DIR: "/state/pi",
|
|
130
|
+
PI_TELEMETRY: "0",
|
|
131
|
+
},
|
|
132
|
+
agentFamily: "pi",
|
|
133
|
+
agentVersion: PI_VERSION,
|
|
134
|
+
modelFamily: "gpt-5.6-sol",
|
|
135
|
+
modelVersion: "gpt-5.6-sol",
|
|
136
|
+
provider: "openai-codex",
|
|
137
|
+
harnessFamily: name,
|
|
138
|
+
harnessVersion:
|
|
139
|
+
name === "native-edit"
|
|
140
|
+
? PI_VERSION
|
|
141
|
+
: name === "released-codex-edit"
|
|
142
|
+
? latest.version
|
|
143
|
+
: `${packed.version}+local.${sourceDigest.slice(0, 12)}`,
|
|
144
|
+
adapterVersion: "1",
|
|
145
|
+
configurationLabels: [`harness/${name}`, extension ? "editor/apply-patch" : "editor/edit"],
|
|
146
|
+
configurationId: `${name}/explicit-edit-v1`,
|
|
147
|
+
configuration: {
|
|
148
|
+
tools: extension ? ["read", "bash", "write", "apply_patch"] : ["read", "bash", "write", "edit"],
|
|
149
|
+
extensions: extensionIdentity ? [extensionIdentity] : [],
|
|
150
|
+
rules: extension ? ["Codex patch grammar", "native write retained"] : [],
|
|
151
|
+
runtimeFlags: [`thinking=${THINKING}`, "clean-pi-resources", "tool-parity-v1"],
|
|
152
|
+
environment: [],
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const config = {
|
|
158
|
+
harnesses: {
|
|
159
|
+
"native-edit": adapter("native-edit", nativeRuntime, false),
|
|
160
|
+
"released-codex-edit": adapter("released-codex-edit", releasedRuntime, true),
|
|
161
|
+
"local-codex-edit": adapter("local-codex-edit", localRuntime, true),
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
const configPath = join(root, "config.json");
|
|
165
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
166
|
+
const metadata = {
|
|
167
|
+
preparedAt: new Date().toISOString(),
|
|
168
|
+
piVersion: PI_VERSION,
|
|
169
|
+
model: MODEL,
|
|
170
|
+
thinking: THINKING,
|
|
171
|
+
released: { package: PACKAGE, version: latest.version, integrity: latest.integrity },
|
|
172
|
+
local: {
|
|
173
|
+
version: packed.version,
|
|
174
|
+
integrity: packed.integrity,
|
|
175
|
+
shasum: packed.shasum,
|
|
176
|
+
sourceDigest,
|
|
177
|
+
},
|
|
178
|
+
config: configPath,
|
|
179
|
+
};
|
|
180
|
+
writeFileSync(join(root, "metadata.json"), `${JSON.stringify(metadata, null, 2)}\n`);
|
|
181
|
+
console.log(JSON.stringify(metadata, null, 2));
|