@viccydev/pi-fpa 0.9.3 → 0.9.4
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/README.md +2 -2
- package/bin/fpa-dashboard-worker.mjs +23 -1
- package/extensions/fpa-artifacts/store.ts +15 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,12 +159,12 @@ pi list
|
|
|
159
159
|
团队分发建议使用固定 Git tag:
|
|
160
160
|
|
|
161
161
|
```bash
|
|
162
|
-
pi install git:github.com/linyqh/pi-fpa@v0.9.
|
|
162
|
+
pi install git:github.com/linyqh/pi-fpa@v0.9.4
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
## 发布到 npm
|
|
166
166
|
|
|
167
|
-
发布动作由 GitHub Release 触发。Release 标签必须严格使用 `v<package.json version>`,例如版本 `0.9.
|
|
167
|
+
发布动作由 GitHub Release 触发。Release 标签必须严格使用 `v<package.json version>`,例如版本 `0.9.4` 对应 `v0.9.4`。工作流会检出该标签,执行 `npm ci`、`npm test` 和包内容预检,全部通过后发布公开包 `@viccydev/pi-fpa`。普通 Release 发布到 `latest`,Prerelease 发布到 `next`。
|
|
168
168
|
|
|
169
169
|
发布认证使用 npm Trusted Publishing / OIDC,不使用长期 npm Token。npm 包后台的 Trusted Publisher 配置为:
|
|
170
170
|
|
|
@@ -76,12 +76,34 @@ if (args.includes("--once")) {
|
|
|
76
76
|
if (!abortController.signal.aborted) throw error;
|
|
77
77
|
}
|
|
78
78
|
} else {
|
|
79
|
+
// A failure that persists — a missing ledger, an artifact a subscription
|
|
80
|
+
// still references — repeats on every tick. Reprinting its stack every
|
|
81
|
+
// interval buries everything else in the log without adding information, so
|
|
82
|
+
// an unchanged failure is reported once in full and then only counted.
|
|
83
|
+
let lastFailure = null;
|
|
84
|
+
let repeatCount = 0;
|
|
79
85
|
while (!stopping) {
|
|
80
86
|
try {
|
|
81
87
|
await runOnce();
|
|
88
|
+
if (lastFailure) {
|
|
89
|
+
process.stderr.write(`${new Date().toISOString()} recovered after ${repeatCount + 1} consecutive failure(s)\n`);
|
|
90
|
+
lastFailure = null;
|
|
91
|
+
repeatCount = 0;
|
|
92
|
+
}
|
|
82
93
|
} catch (error) {
|
|
83
94
|
if (abortController.signal.aborted) break;
|
|
84
|
-
|
|
95
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
96
|
+
if (message === lastFailure) {
|
|
97
|
+
repeatCount += 1;
|
|
98
|
+
// Back off the log, not the work: still retried every interval.
|
|
99
|
+
if (repeatCount === 1 || repeatCount % 20 === 0) {
|
|
100
|
+
process.stderr.write(`${new Date().toISOString()} still failing (${repeatCount + 1}x): ${message}\n`);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
lastFailure = message;
|
|
104
|
+
repeatCount = 0;
|
|
105
|
+
process.stderr.write(`${new Date().toISOString()} ${error instanceof Error ? error.stack ?? message : message}\n`);
|
|
106
|
+
}
|
|
85
107
|
}
|
|
86
108
|
if (!stopping) await new Promise((resolveWait) => {
|
|
87
109
|
const timer = setTimeout(() => {
|
|
@@ -144,7 +144,21 @@ async function existingLedgerDirectories(projectRoot: string): Promise<{ root: s
|
|
|
144
144
|
const objects = join(root, "objects");
|
|
145
145
|
const entries = join(root, "entries");
|
|
146
146
|
for (const [path, label] of [[root, ".ledger"], [objects, ".ledger/objects"], [entries, ".ledger/entries"]] as const) {
|
|
147
|
-
|
|
147
|
+
let stat;
|
|
148
|
+
try {
|
|
149
|
+
stat = await lstat(path);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
// A project with no ledger is a state a caller can act on — the
|
|
152
|
+
// artifacts were never committed through it, or the directory was
|
|
153
|
+
// removed out from under a subscription that still holds refs.
|
|
154
|
+
// Letting a raw ENOENT escape turns that into an unreadable stack
|
|
155
|
+
// trace in the coordinator's last_error, which the operator then
|
|
156
|
+
// has to reverse-engineer.
|
|
157
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
158
|
+
throw new Error(`This project has no artifact ledger (${label} is missing), so artifacts cannot be resolved by ref. Commit an artifact through the ledger before referencing one.`);
|
|
159
|
+
}
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
148
162
|
if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error(`${label} must be a regular directory, not a symlink.`);
|
|
149
163
|
}
|
|
150
164
|
return { root, objects, entries };
|