@llblab/pi-actors 0.38.0 → 0.38.1

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 CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.38.1: Windows Recipe ACL Hotfix
6
+
7
+ - `[Registry]` Replaced POSIX mode-bit recipe-root writability checks on Windows with ACL-aware diagnostics, avoiding false `world-writable` and `group-writable` startup warnings from Node's NTFS mode emulation while still flagging broad Windows write grants.
8
+ - `[Tests]` Added portable coverage for broad Windows ACL parsing and skipped the POSIX chmod diagnostic regression on Windows.
9
+
5
10
  ## 0.38.0: Review Swarm Pipeline Hardening
6
11
 
7
12
  - `[Recipes]` Added current model/thinking inheritance for packaged review/lens-swarm recipes through `{current_model}` and `{current_thinking}`, so normal subagent review swarms use the selected Pi session policy by default while explicit args still override it.
@@ -45,6 +45,7 @@ export interface RecipesDiscoverySource {
45
45
  defaultTool?: boolean;
46
46
  mutableUsage?: boolean;
47
47
  }
48
+ export declare function hasBroadWindowsWriteAcl(icaclsOutput: string): boolean;
48
49
  export declare function discoverRecipeSources(sources: RecipesDiscoverySource[]): RecipesDiscoveryResult;
49
50
  export declare function discoverRecipes(roots: string[]): RecipesDiscoveryResult;
50
51
  export declare function createRecipeIntegrityManifest(result: RecipesDiscoveryResult): RecipeIntegrityManifestEntry[];
@@ -3,6 +3,7 @@
3
3
  * Zones: recipe discovery, tool exposure, registry diagnostics
4
4
  * Owns filename identity discovery across prioritized recipe roots
5
5
  */
6
+ import { execFileSync } from "node:child_process";
6
7
  import { createHash } from "node:crypto";
7
8
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
8
9
  import { join } from "node:path";
@@ -140,6 +141,50 @@ function filesForSource(source) {
140
141
  }))
141
142
  : [];
142
143
  }
144
+ const WINDOWS_BROAD_WRITE_PRINCIPALS = [
145
+ "Everyone",
146
+ "BUILTIN\\Users",
147
+ "Authenticated Users",
148
+ "S-1-1-0",
149
+ "S-1-5-11",
150
+ "S-1-5-32-545",
151
+ ];
152
+ const WINDOWS_WRITE_ACL_PATTERN = /\((?:[^)]*,)?(?:F|M|W|WD|AD|DC|GA|GW)(?:,[^)]*)?\)/i;
153
+ export function hasBroadWindowsWriteAcl(icaclsOutput) {
154
+ return icaclsOutput
155
+ .split(/\r?\n/)
156
+ .some((line) => WINDOWS_BROAD_WRITE_PRINCIPALS.some((principal) => line.includes(principal)) && WINDOWS_WRITE_ACL_PATTERN.test(line));
157
+ }
158
+ function getWindowsRecipeRootDiagnostics(root) {
159
+ try {
160
+ const output = execFileSync("icacls", [root], {
161
+ encoding: "utf8",
162
+ windowsHide: true,
163
+ timeout: 5000,
164
+ });
165
+ if (!hasBroadWindowsWriteAcl(output))
166
+ return [];
167
+ return [
168
+ `Recipe root grants write access to broad Windows principals; review ACLs: ${root}`,
169
+ ];
170
+ }
171
+ catch (error) {
172
+ return [
173
+ `Failed to inspect Windows ACL for recipe root ${root}: ${error instanceof Error ? error.message : String(error)}`,
174
+ ];
175
+ }
176
+ }
177
+ function getPosixRecipeRootDiagnostics(root) {
178
+ const diagnostics = [];
179
+ const stat = statSync(root);
180
+ if ((stat.mode & 0o002) !== 0) {
181
+ diagnostics.push(`Recipe root is world-writable; review permissions: ${root}`);
182
+ }
183
+ if ((stat.mode & 0o020) !== 0) {
184
+ diagnostics.push(`Recipe root is group-writable; review ownership and permissions: ${root}`);
185
+ }
186
+ return diagnostics;
187
+ }
143
188
  function getRecipeRootDiagnostics(sources) {
144
189
  const diagnostics = [];
145
190
  const roots = new Set(sources
@@ -149,13 +194,9 @@ function getRecipeRootDiagnostics(sources) {
149
194
  try {
150
195
  if (!existsSync(root))
151
196
  continue;
152
- const stat = statSync(root);
153
- if ((stat.mode & 0o002) !== 0) {
154
- diagnostics.push(`Recipe root is world-writable; review permissions: ${root}`);
155
- }
156
- if ((stat.mode & 0o020) !== 0) {
157
- diagnostics.push(`Recipe root is group-writable; review ownership and permissions: ${root}`);
158
- }
197
+ diagnostics.push(...(process.platform === "win32"
198
+ ? getWindowsRecipeRootDiagnostics(root)
199
+ : getPosixRecipeRootDiagnostics(root)));
159
200
  }
160
201
  catch (error) {
161
202
  diagnostics.push(`Failed to inspect recipe root ${root}: ${error instanceof Error ? error.message : String(error)}`);
@@ -2,7 +2,7 @@
2
2
  name: actors
3
3
  description: Required practical guide for non-trivial pi-actors use. Read before using or changing spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
4
4
  metadata:
5
- version: 0.38.0
5
+ version: 0.38.1
6
6
  ---
7
7
 
8
8
  # Actors (pi-actors)
@@ -2,7 +2,7 @@
2
2
  name: swarm
3
3
  description: Subagent orchestration with scoped locks and quorum consensus. Use for multi-model review, parallel scoped work, delegated audit, and coordinated subagent execution.
4
4
  metadata:
5
- version: 0.38.0
5
+ version: 0.38.1
6
6
  ---
7
7
 
8
8
  # Swarm
@@ -4,6 +4,7 @@
4
4
  * Owns filename identity discovery across prioritized recipe roots
5
5
  */
6
6
 
7
+ import { execFileSync } from "node:child_process";
7
8
  import { createHash } from "node:crypto";
8
9
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
9
10
  import { join } from "node:path";
@@ -234,6 +235,61 @@ function filesForSource(source: RecipesDiscoverySource): Array<{
234
235
  : [];
235
236
  }
236
237
 
238
+ const WINDOWS_BROAD_WRITE_PRINCIPALS = [
239
+ "Everyone",
240
+ "BUILTIN\\Users",
241
+ "Authenticated Users",
242
+ "S-1-1-0",
243
+ "S-1-5-11",
244
+ "S-1-5-32-545",
245
+ ];
246
+
247
+ const WINDOWS_WRITE_ACL_PATTERN = /\((?:[^)]*,)?(?:F|M|W|WD|AD|DC|GA|GW)(?:,[^)]*)?\)/i;
248
+
249
+ export function hasBroadWindowsWriteAcl(icaclsOutput: string): boolean {
250
+ return icaclsOutput
251
+ .split(/\r?\n/)
252
+ .some((line) =>
253
+ WINDOWS_BROAD_WRITE_PRINCIPALS.some((principal) =>
254
+ line.includes(principal),
255
+ ) && WINDOWS_WRITE_ACL_PATTERN.test(line),
256
+ );
257
+ }
258
+
259
+ function getWindowsRecipeRootDiagnostics(root: string): string[] {
260
+ try {
261
+ const output = execFileSync("icacls", [root], {
262
+ encoding: "utf8",
263
+ windowsHide: true,
264
+ timeout: 5000,
265
+ });
266
+ if (!hasBroadWindowsWriteAcl(output)) return [];
267
+ return [
268
+ `Recipe root grants write access to broad Windows principals; review ACLs: ${root}`,
269
+ ];
270
+ } catch (error) {
271
+ return [
272
+ `Failed to inspect Windows ACL for recipe root ${root}: ${error instanceof Error ? error.message : String(error)}`,
273
+ ];
274
+ }
275
+ }
276
+
277
+ function getPosixRecipeRootDiagnostics(root: string): string[] {
278
+ const diagnostics: string[] = [];
279
+ const stat = statSync(root);
280
+ if ((stat.mode & 0o002) !== 0) {
281
+ diagnostics.push(
282
+ `Recipe root is world-writable; review permissions: ${root}`,
283
+ );
284
+ }
285
+ if ((stat.mode & 0o020) !== 0) {
286
+ diagnostics.push(
287
+ `Recipe root is group-writable; review ownership and permissions: ${root}`,
288
+ );
289
+ }
290
+ return diagnostics;
291
+ }
292
+
237
293
  function getRecipeRootDiagnostics(sources: RecipesDiscoverySource[]): string[] {
238
294
  const diagnostics: string[] = [];
239
295
  const roots = new Set(
@@ -244,17 +300,11 @@ function getRecipeRootDiagnostics(sources: RecipesDiscoverySource[]): string[] {
244
300
  for (const root of roots) {
245
301
  try {
246
302
  if (!existsSync(root)) continue;
247
- const stat = statSync(root);
248
- if ((stat.mode & 0o002) !== 0) {
249
- diagnostics.push(
250
- `Recipe root is world-writable; review permissions: ${root}`,
251
- );
252
- }
253
- if ((stat.mode & 0o020) !== 0) {
254
- diagnostics.push(
255
- `Recipe root is group-writable; review ownership and permissions: ${root}`,
256
- );
257
- }
303
+ diagnostics.push(
304
+ ...(process.platform === "win32"
305
+ ? getWindowsRecipeRootDiagnostics(root)
306
+ : getPosixRecipeRootDiagnostics(root)),
307
+ );
258
308
  } catch (error) {
259
309
  diagnostics.push(
260
310
  `Failed to inspect recipe root ${root}: ${error instanceof Error ? error.message : String(error)}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.38.0",
3
+ "version": "0.38.1",
4
4
  "private": false,
5
5
  "description": "Local Actor Kernel for Pi",
6
6
  "keywords": [
@@ -2,7 +2,7 @@
2
2
  name: actors
3
3
  description: Required practical guide for non-trivial pi-actors use. Read before using or changing spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
4
4
  metadata:
5
- version: 0.38.0
5
+ version: 0.38.1
6
6
  ---
7
7
 
8
8
  # Actors (pi-actors)
@@ -2,7 +2,7 @@
2
2
  name: swarm
3
3
  description: Subagent orchestration with scoped locks and quorum consensus. Use for multi-model review, parallel scoped work, delegated audit, and coordinated subagent execution.
4
4
  metadata:
5
- version: 0.38.0
5
+ version: 0.38.1
6
6
  ---
7
7
 
8
8
  # Swarm