@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 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.3
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.3` 对应 `v0.9.3`。工作流会检出该标签,执行 `npm ci`、`npm test` 和包内容预检,全部通过后发布公开包 `@viccydev/pi-fpa`。普通 Release 发布到 `latest`,Prerelease 发布到 `next`。
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
- process.stderr.write(`${new Date().toISOString()} ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
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
- const stat = await lstat(path);
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viccydev/pi-fpa",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "type": "module",
5
5
  "description": "Full-cycle FP&A planning, strategy, forecast, and review prompts, skills, and data tools for Pi",
6
6
  "license": "UNLICENSED",