throughline 0.8.9 → 0.9.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 CHANGED
@@ -10,6 +10,16 @@ shipped to npm but were not individually tagged on GitHub.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.9.0] — 2026-08-04
14
+
15
+ ### Added
16
+
17
+ - `throughline handoff-context --session <id> --json` returns the exact
18
+ budgeted SessionStart inheritance context through a versioned local CLI
19
+ boundary. It opens only an existing database read-only and never creates or
20
+ migrates it, consumes a baton, merges sessions, changes `sessions.merged_into`,
21
+ or reassigns L1/L2/L3 memory rows.
22
+
13
23
  ## [0.8.9] — 2026-08-02
14
24
 
15
25
  ### Fixed
package/README.ja.md CHANGED
@@ -1,5 +1,7 @@
1
1
  <p align="center">
2
- <img src=".github/og.png" alt="Throughline — Claude Code のコンテキスト消費を約 90% 削減しつつ記憶はほぼ残す" width="100%">
2
+ <img src=".github/og.png" alt="Throughline — 環境や境界が変わっても方向と記憶を保って進むクジラの群れ" width="100%">
3
+ <br>
4
+ <sub><em>この画像は、環境や境界が変わっても、関係・方向・記憶を失わずに進み続ける連続性を表しています。</em></sub>
3
5
  </p>
4
6
 
5
7
  # Throughline
@@ -332,6 +334,7 @@ Throughline state をまだ書いていない現在セッションも表示で
332
334
  | `throughline doctor` | Node バージョン、hook 登録状況、DB、PATH をチェック |
333
335
  | `throughline doctor --trim --host claude` | trim boundary と手動手順を診断 |
334
336
  | `throughline handoff-preview --session <id>` | Codex 向け `throughline_handoff` JSON projection を表示 |
337
+ | `throughline handoff-context --session <id> --json` | SessionStart と同じ引き継ぎ文脈を versioned JSON で取得。記憶行の `session_id` と `sessions.merged_into` は変更せず、同一端末内の別ベンダーランチャーから使える |
335
338
  | `throughline codex-sidecar-diagnostics` | この project の `codex-sidecar` diagnostics status を確認 |
336
339
  | `throughline codex-sidecar-dry-run` | App Server を呼ばずに read-only sidecar request を正規化表示 |
337
340
  | `throughline trim --dry-run --host codex` | Codex same-thread trim の dry-run preview |
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  <p align="center">
2
- <img src=".github/og.png" alt="Throughline — cut ~90% of Claude Code's context usage while keeping nearly all the memory" width="100%">
2
+ <img src=".github/og.png" alt="Throughline — a whale family carrying direction and memory across changing boundaries" width="100%">
3
+ <br>
4
+ <sub><em>This image represents continuity that preserves relationships, direction, and memory even as environments and boundaries change.</em></sub>
3
5
  </p>
4
6
 
5
7
  # Throughline
@@ -797,6 +799,7 @@ aggregate は collection が既定OFFで、canonical dotagents config の
797
799
  | `throughline runtime-errors reopen <fingerprint> --json` | Explicitly reopen a resolved aggregate without fabricating a new occurrence |
798
800
  | `throughline runtime-errors compact --json` | Remove only acknowledged, resolved aggregates after retention; open or unacknowledged records remain |
799
801
  | `throughline handoff-preview --session <id>` | Print a Codex-facing `throughline_handoff` JSON projection |
802
+ | `throughline handoff-context --session <id> --json` | Print the exact SessionStart inheritance context as versioned JSON without moving memory rows or changing `sessions.merged_into`; intended for local launchers that need portable cross-vendor context |
800
803
  | `throughline codex-capture --codex-thread-id <id>` | Capture active Codex rollout turns into a `codex:<thread_id>` DB session |
801
804
  | `throughline codex-summarize --session codex:<id>` | Summarize captured Codex L2 into L1 with the Codex CLI backend |
802
805
  | `throughline codex-resume --session codex:<id>` | Render Codex active-work context from a captured Codex session |
@@ -11,6 +11,7 @@
11
11
  * throughline detail <時刻> # L2+L3 詳細取得 (Claude が Bash 経由で呼ぶ想定)
12
12
  * throughline recall --l2|--l1 # 注入案内から辿る pull 用 read-only 記憶取得
13
13
  * throughline handoff-preview # Codex-facing throughline_handoff JSON preview
14
+ * throughline handoff-context --session <id> --json # Read-only inheritance context JSON
14
15
  * throughline auditor-context --json # Read-only bounded auditor context JSON
15
16
  * throughline factory-diagnostics --json # Native factory read-only readiness JSON
16
17
  * throughline migrate --json # Migrate the existing Throughline database only
@@ -76,6 +77,11 @@ switch (cmd) {
76
77
  case 'handoff-preview':
77
78
  await (await import('../src/cli/handoff-preview.mjs')).run(rest);
78
79
  break;
80
+ case 'handoff-context': {
81
+ const exitCode = (await import('../src/cli/handoff-context.mjs')).run(rest);
82
+ if (exitCode !== 0) process.exitCode = exitCode;
83
+ break;
84
+ }
79
85
  case 'auditor-context': {
80
86
  const exitCode = (await import('../src/cli/auditor-context.mjs')).run(rest);
81
87
  if (exitCode !== 0) process.exitCode = exitCode;
@@ -202,6 +208,9 @@ Usage:
202
208
  throughline monitor Multi-session token monitor (use --all, --session <id>)
203
209
  throughline detail <time> Retrieve L2+L3 detail for a turn (e.g. 14:23:05 or 14:23-14:30)
204
210
  throughline handoff-preview Print Codex-facing throughline_handoff JSON
211
+ throughline handoff-context --session <id> --json
212
+ Print the exact inheritance context without
213
+ changing database ownership
205
214
  throughline auditor-context --session <id> --project <root>
206
215
  Read only bounded completed user/assistant context
207
216
  for an auditor; requires either --host plus --transcript,
@@ -20,6 +20,7 @@
20
20
  | [13_native_factory_diagnostics_plan.md](13_native_factory_diagnostics_plan.md) | native factory read-only readiness 診断の実装記録 |
21
21
  | [14_observer_completed_turn_feed_plan.md](14_observer_completed_turn_feed_plan.md) | Observer向けcompleted-only read / wait CLIのactive計画。CLI・opaque cursor・pagination・最大3600秒waitは実装済み、公開/full regression gateは継続中 |
22
22
  | [15_windows_ci_release_latency_plan.md](15_windows_ci_release_latency_plan.md) | Windows CI 18分の原因、ACL安全網を維持した短縮、release gateの受入条件 |
23
+ | [16_readonly_handoff_context_plan.md](16_readonly_handoff_context_plan.md) | DB所有権を変更せずSessionStartと同じ記憶を返す、ローカルランチャー向けread-only I/F |
23
24
  | [BUGHUB_RUNTIME_ERROR_STORE_PLAN.md](BUGHUB_RUNTIME_ERROR_STORE_PLAN.md) | local runtime error aggregate store の契約と実装 TODO |
24
25
 
25
26
  ## Supporting Records
@@ -144,7 +144,7 @@ schema v4 で PostToolUse (`capture-tool`) は廃止、L2/L3 は Stop 内で一
144
144
  | **npm公開 (v0.8.6): 製品所有DB migration入口** | `throughline migrate --json` は既存DBだけをproduction migrationで現行schemaへ移行し、versioned bounded JSONを返す。DB不在は作らず`not_applicable`、現行は`already_current`、future schemaと失敗は非0。`factory-diagnostics`のread-only契約は維持する。0.8.5はdry-run後に別作業の未コミット文書が混入したためdeprecateし、同一runtimeをclean worktreeから0.8.6として再公開した。設計判断は [ADR 0018](adr/0018-product-owned-database-migration.md) |
145
145
  | **公開済み (v0.8.7): Windows CI release latency** | completed-turn receiptの256件境界fixtureと実ACL検証を分離し、production mutationのPowerShellはdistinct state transitionだけに限定する。temporary ACL失敗時の旧store保持とdirectory/lock/final storeのnative ACL testを維持する。CIは9 matrixのままevent/ref単位concurrency、unit test 5分SLO/8分timeoutとする。設計判断は [ADR 0020](adr/0020-windows-ci-release-latency.md) |
146
146
  | **npm公開 (v0.8.8): Codex hook timeout key修理** | Codex `hooks.json` の3 managed hookは秒単位の正規key `timeout`を30/30/300で書く。無視される旧`timeoutSec` entryは次回global installでcommand identityにより置換し、doctorは旧形式を要再install、factory diagnosticsはnot readyとする。Claude hooksとObserver wait deadline APIは変更しない。npm `latest` として2026-08-02T00:43Zに公開済み。tag / GitHub Releaseは未作成(要作成) |
147
- | **release準備 (v0.8.9): Codex hook診断のPATH非依存化** | 期待hook commandのnode表記は呼び出し元の`PATH`で変わるため、登録済みcommandとの文字列比較では最小PATH(launchd factory reporter等)から正規登録が「legacy command」と誤判定され、`codex_hooks`が恒常的に`not_ready`になっていた。比較を解析済みidentity(node実体realpath+CLI script realpath+event)へ変更する。別install/別event/旧PATH解決型/realpath未解決は従来どおり要再install扱い。hooks.json の書式と Claude-facing hook は変更しない |
147
+ | **npm公開 (v0.8.9): Codex hook診断のPATH非依存化** | 期待hook commandのnode表記は呼び出し元の`PATH`で変わるため、登録済みcommandとの文字列比較では最小PATH(launchd factory reporter等)から正規登録が「legacy command」と誤判定され、`codex_hooks`が恒常的に`not_ready`になっていた。比較を解析済みidentity(node実体realpath+CLI script realpath+event)へ変更した。別install/別event/旧PATH解決型/realpath未解決は従来どおり要再install扱い。hooks.jsonの書式とClaude-facing hookは変更しない。公開commit `5dbd3af`、CI run `30731697079` 9/9 green、npm `latest` 0.8.9、shasum `780f8e72d9bd4b0c4a331db48116a415a4bde34b`、tag / GitHub Release(v0.8.8分も遡って作成)、registry由来global installを2026-08-02に確認した |
148
148
  | **未リリース: Codex current-session 75% trigger** | Codex 自動発火を token-monitor に依存させず、global install が Codex `UserPromptSubmit` / `PostToolUse` hooks も登録する。hook は当該 Codex session の rollout `token_count` を直接読み、verified 75% 以上なら同じ user turn または tool loop 継続前に `$throughline` workflow 実行指示を `additionalContext` で注入する。Stop hook の guarded auto-refresh は残す。`~/.codex/config.toml` は旧 `codex_hooks = true` に加えて現行 `hooks = true` も有効化する |
149
149
  | **グローバル E2E 検証** | 2026-04-17 別ディレクトリから `throughline doctor` 全緑を確認 |
150
150
 
@@ -0,0 +1,31 @@
1
+ # Read-only handoff context
2
+
3
+ ## 目的
4
+
5
+ 通常 handoff が使う `buildBudgetedResumeContext` の出力を、DB 所有権を変えずにローカルプログラムが
6
+ 取得できる read-only CLI として公開する。最初の consumer は AIterm の portable fork だが、I/F は
7
+ 特定 consumer や vendor に依存させない。
8
+
9
+ 実行 ToDo、依存、状態、完了証拠の正本は Lattice plan `readonly-handoff-context` とする。
10
+
11
+ ## 契約
12
+
13
+ - `throughline handoff-context --session <id> --json` は session を明示必須とする。
14
+ - 既存 DB を `DatabaseSync(..., { readOnly: true })` で開き、作成・migration・書込みをしない。
15
+ - `buildBudgetedResumeContext(db, { sessionId, isInheritance: true })` をそのまま使う。
16
+ - 成功 JSON は schema、status、sessionId、context だけを返す。renderer 統計は公開しない。
17
+ - 引数不正、DB open 失敗、context 不在は非 0 終了とする。
18
+
19
+ ## 非目標
20
+
21
+ - baton、pending handoff、merge、backfill、通常 SessionStart / UserPromptSubmit の変更。
22
+ - Observer projection、Codex 専用 renderer、latest session 推測、project/cwd 照合。
23
+ - DB schema 全面診断、hash、cursor、暗号化、daemon、network I/O、retry、cache。
24
+ - budget や `excludeOriginId` を CLI option として公開すること。
25
+
26
+ ## 受入条件
27
+
28
+ - CLI の context が同じ DB に対する既存 budgeted renderer の出力と完全一致する。
29
+ - 実行前後で `sessions.merged_into` と L1/L2/L3 の `session_id` が変わらない。
30
+ - DB 不在時に DB や親ディレクトリを作らない。
31
+ - focused test と全回帰が成功し、公開 package と global install から新 command を実行できる。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "throughline",
3
- "version": "0.8.9",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "description": "Claude Code hooks plugin for structured context compression (/clear-safe persistent memory)",
6
6
  "keywords": [
@@ -38,7 +38,9 @@
38
38
  "LICENSE"
39
39
  ],
40
40
  "scripts": {
41
- "test": "node --import ./src/test-env.mjs --test src/*.test.mjs src/cli/*.test.mjs"
41
+ "test": "node --import ./src/test-env.mjs --test scripts/verify-release-commit.test.mjs src/*.test.mjs src/cli/*.test.mjs",
42
+ "verify:release-commit": "node scripts/verify-release-commit.mjs",
43
+ "prepublishOnly": "npm run verify:release-commit && npm test"
42
44
  },
43
45
  "engines": {
44
46
  "node": ">=22.13"
@@ -0,0 +1,71 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { existsSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+
6
+ import { buildBudgetedResumeContext } from '../resume-context.mjs';
7
+
8
+ export const HANDOFF_CONTEXT_SCHEMA = 'throughline.handoff_context.v1';
9
+
10
+ export function parseArgs(argv = []) {
11
+ if (
12
+ argv.length !== 3 ||
13
+ argv[0] !== '--session' ||
14
+ typeof argv[1] !== 'string' ||
15
+ argv[1].length === 0 ||
16
+ argv[2] !== '--json'
17
+ ) {
18
+ throw new TypeError('usage error');
19
+ }
20
+ return { sessionId: argv[1] };
21
+ }
22
+
23
+ export function readHandoffContext(sessionId, {
24
+ dbPath = join(homedir(), '.throughline', 'throughline.db'),
25
+ } = {}) {
26
+ if (!existsSync(dbPath)) return null;
27
+
28
+ const db = new DatabaseSync(dbPath, { readOnly: true });
29
+ try {
30
+ return buildBudgetedResumeContext(db, {
31
+ sessionId,
32
+ isInheritance: true,
33
+ })?.text ?? null;
34
+ } finally {
35
+ db.close();
36
+ }
37
+ }
38
+
39
+ export function run(argv = [], {
40
+ stdout = process.stdout,
41
+ stderr = process.stderr,
42
+ readContext = readHandoffContext,
43
+ } = {}) {
44
+ let sessionId;
45
+ try {
46
+ ({ sessionId } = parseArgs(argv));
47
+ } catch {
48
+ stderr.write('Usage: throughline handoff-context --session <id> --json\n');
49
+ return 2;
50
+ }
51
+
52
+ let context;
53
+ try {
54
+ context = readContext(sessionId);
55
+ } catch {
56
+ stderr.write('Throughline handoff context could not be read.\n');
57
+ return 1;
58
+ }
59
+ if (!context) {
60
+ stderr.write('Throughline handoff context is not available for that session.\n');
61
+ return 1;
62
+ }
63
+
64
+ stdout.write(`${JSON.stringify({
65
+ schema: HANDOFF_CONTEXT_SCHEMA,
66
+ status: 'ready',
67
+ sessionId,
68
+ context,
69
+ })}\n`);
70
+ return 0;
71
+ }
@@ -0,0 +1,143 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { spawnSync } from 'node:child_process';
4
+ import { existsSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { DatabaseSync } from 'node:sqlite';
9
+
10
+ import { buildBudgetedResumeContext } from '../resume-context.mjs';
11
+
12
+ const REPO_ROOT = fileURLToPath(new URL('../..', import.meta.url));
13
+ const BIN_PATH = join(REPO_ROOT, 'bin/throughline.mjs');
14
+ const SESSION_ID = 'claude-source-session';
15
+
16
+ function runCli(home, args = ['handoff-context', '--session', SESSION_ID, '--json']) {
17
+ return spawnSync(process.execPath, [BIN_PATH, ...args], {
18
+ cwd: REPO_ROOT,
19
+ env: { ...process.env, HOME: home, USERPROFILE: home },
20
+ encoding: 'utf8',
21
+ });
22
+ }
23
+
24
+ function createFixture(home) {
25
+ const dir = join(home, '.throughline');
26
+ mkdirSync(dir, { recursive: true });
27
+ const dbPath = join(dir, 'throughline.db');
28
+ const db = new DatabaseSync(dbPath);
29
+ db.exec(`
30
+ PRAGMA user_version = 9;
31
+ CREATE TABLE sessions (
32
+ session_id TEXT PRIMARY KEY,
33
+ project_path TEXT NOT NULL,
34
+ status TEXT NOT NULL DEFAULT 'active',
35
+ created_at INTEGER NOT NULL,
36
+ updated_at INTEGER NOT NULL,
37
+ merged_into TEXT
38
+ );
39
+ CREATE TABLE skeletons (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ session_id TEXT NOT NULL,
42
+ turn_number INTEGER NOT NULL,
43
+ role TEXT NOT NULL,
44
+ summary TEXT NOT NULL,
45
+ created_at INTEGER NOT NULL,
46
+ origin_session_id TEXT
47
+ );
48
+ CREATE TABLE bodies (
49
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
50
+ session_id TEXT NOT NULL,
51
+ origin_session_id TEXT NOT NULL,
52
+ turn_number INTEGER NOT NULL,
53
+ role TEXT NOT NULL,
54
+ text TEXT NOT NULL,
55
+ token_count INTEGER,
56
+ created_at INTEGER NOT NULL
57
+ );
58
+ CREATE TABLE details (
59
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
60
+ session_id TEXT NOT NULL,
61
+ turn_number INTEGER,
62
+ tool_name TEXT NOT NULL,
63
+ input_text TEXT,
64
+ output_text TEXT,
65
+ token_count INTEGER NOT NULL DEFAULT 0,
66
+ created_at INTEGER NOT NULL,
67
+ origin_session_id TEXT,
68
+ kind TEXT NOT NULL DEFAULT 'tool_input',
69
+ source_id TEXT
70
+ );
71
+ `);
72
+ db.prepare(
73
+ `INSERT INTO sessions
74
+ (session_id, project_path, status, created_at, updated_at, merged_into)
75
+ VALUES (?, ?, 'active', ?, ?, NULL)`,
76
+ ).run(SESSION_ID, '/work/project', 1_700_000_000_000, 1_700_000_004_000);
77
+ db.prepare(
78
+ `INSERT INTO skeletons
79
+ (session_id, origin_session_id, turn_number, role, summary, created_at)
80
+ VALUES (?, ?, 1, 'assistant', ?, ?)`,
81
+ ).run(SESSION_ID, 'older-origin', '以前に portable fork の方針を決めた', 1_700_000_001_000);
82
+ const insertBody = db.prepare(
83
+ `INSERT INTO bodies
84
+ (session_id, origin_session_id, turn_number, role, text, token_count, created_at)
85
+ VALUES (?, ?, 2, ?, ?, 8, ?)`,
86
+ );
87
+ insertBody.run(SESSION_ID, SESSION_ID, 'user', '所有権を変えずに記憶を渡して', 1_700_000_002_000);
88
+ insertBody.run(SESSION_ID, SESSION_ID, 'assistant', 'read-only I/F を実装する', 1_700_000_003_000);
89
+ db.prepare(
90
+ `INSERT INTO details
91
+ (session_id, origin_session_id, turn_number, tool_name, output_text, created_at, kind, source_id)
92
+ VALUES (?, ?, 2, 'Read', 'schema inspected', ?, 'tool_output', 'detail-1')`,
93
+ ).run(SESSION_ID, SESSION_ID, 1_700_000_003_500);
94
+ return { db, dbPath };
95
+ }
96
+
97
+ function ownershipSnapshot(db) {
98
+ return {
99
+ sessions: db.prepare('SELECT session_id, merged_into FROM sessions ORDER BY session_id').all(),
100
+ skeletons: db.prepare('SELECT id, session_id FROM skeletons ORDER BY id').all(),
101
+ bodies: db.prepare('SELECT id, session_id FROM bodies ORDER BY id').all(),
102
+ details: db.prepare('SELECT id, session_id FROM details ORDER BY id').all(),
103
+ };
104
+ }
105
+
106
+ test('handoff-context emits the exact inheritance context without changing DB ownership', () => {
107
+ const home = mkdtempSync(join(tmpdir(), 'tl-handoff-context-'));
108
+ try {
109
+ const { db, dbPath } = createFixture(home);
110
+ const before = ownershipSnapshot(db);
111
+ const expected = buildBudgetedResumeContext(db, {
112
+ sessionId: SESSION_ID,
113
+ isInheritance: true,
114
+ })?.text;
115
+ db.close();
116
+
117
+ const result = runCli(home);
118
+ assert.equal(result.status, 0, result.stderr);
119
+ assert.deepEqual(JSON.parse(result.stdout), {
120
+ schema: 'throughline.handoff_context.v1',
121
+ status: 'ready',
122
+ sessionId: SESSION_ID,
123
+ context: expected,
124
+ });
125
+
126
+ const verify = new DatabaseSync(dbPath, { readOnly: true });
127
+ assert.deepEqual(ownershipSnapshot(verify), before);
128
+ verify.close();
129
+ } finally {
130
+ rmSync(home, { recursive: true, force: true });
131
+ }
132
+ });
133
+
134
+ test('handoff-context fails without creating a missing database', () => {
135
+ const home = mkdtempSync(join(tmpdir(), 'tl-handoff-context-missing-'));
136
+ try {
137
+ const result = runCli(home);
138
+ assert.notEqual(result.status, 0);
139
+ assert.equal(existsSync(join(home, '.throughline')), false);
140
+ } finally {
141
+ rmSync(home, { recursive: true, force: true });
142
+ }
143
+ });
@@ -51,6 +51,8 @@ test('CLI help exposes guided Codex handoff and guarded execute semantics', () =
51
51
  assert.match(result.stdout, /Use --execute to/);
52
52
  assert.match(result.stdout, /--open-host auto\|desktop\|vscode\|cli\|none/);
53
53
  assert.match(result.stdout, /throughline trim --execute/);
54
+ assert.match(result.stdout, /throughline handoff-context --session <id> --json/);
55
+ assert.match(result.stdout, /without\s+changing database ownership/);
54
56
  assert.match(result.stdout, /injectable DB memory/);
55
57
  assert.match(result.stdout, /matching/);
56
58
  assert.match(result.stdout, /rollout\/app-server turns/);
@@ -66,3 +68,8 @@ test('CLI help Codex commands are dispatchable', () => {
66
68
  assert.match(bin, new RegExp(`case '${subcommand}':`), `${command} is missing dispatch`);
67
69
  }
68
70
  });
71
+
72
+ test('CLI help exposes the dispatchable read-only handoff context boundary', () => {
73
+ const bin = readFileSync(BIN_PATH, 'utf8');
74
+ assert.match(bin, /case 'handoff-context':/);
75
+ });
@@ -1,5 +1,10 @@
1
1
  import { spawnSync } from 'node:child_process';
2
2
 
3
+ // GitHub hosted Windows runners can spend more than 15 seconds starting the
4
+ // inbox Windows PowerShell process while the Node matrix is contended. This is
5
+ // a hang guard for a fixture boundary, not a latency assertion.
6
+ const WINDOWS_ACL_FIXTURE_TIMEOUT_MS = 30_000;
7
+
3
8
  export function applyWindowsPrivateAcl(path, directory = false) {
4
9
  if (process.platform !== 'win32') return;
5
10
  const script = String.raw`
@@ -14,10 +19,10 @@ $acl.AddAccessRule($rule)
14
19
  if($isDir){[System.IO.Directory]::SetAccessControl($target,$acl)}else{[System.IO.File]::SetAccessControl($target,$acl)}
15
20
  `;
16
21
  const result = spawnSync('powershell.exe', [
17
- '-NoProfile', '-NonInteractive', '-Command', script,
22
+ '-NoLogo', '-NoProfile', '-NonInteractive', '-Command', script,
18
23
  ], {
19
24
  encoding: 'utf8',
20
- timeout: 15_000,
25
+ timeout: WINDOWS_ACL_FIXTURE_TIMEOUT_MS,
21
26
  windowsHide: true,
22
27
  env: {
23
28
  ...process.env,
@@ -43,10 +48,10 @@ $rule=$rules[0]
43
48
  if($rule.IdentityReference.Value -ne $sid -or $rule.AccessControlType -ne 'Allow' -or $rule.IsInherited -or ($rule.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -ne [System.Security.AccessControl.FileSystemRights]::FullControl){exit 43}
44
49
  `;
45
50
  const result = spawnSync('powershell.exe', [
46
- '-NoProfile', '-NonInteractive', '-Command', script,
51
+ '-NoLogo', '-NoProfile', '-NonInteractive', '-Command', script,
47
52
  ], {
48
53
  encoding: 'utf8',
49
- timeout: 15_000,
54
+ timeout: WINDOWS_ACL_FIXTURE_TIMEOUT_MS,
50
55
  windowsHide: true,
51
56
  env: {
52
57
  ...process.env,