throughline 0.10.15 → 0.10.17
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 +20 -1
- package/README.md +4 -0
- package/bin/throughline.mjs +0 -0
- package/docs/04_public_release_plan.md +3 -0
- package/package.json +1 -1
- package/src/cli/codex-handoff-start.mjs +9 -2
- package/src/cli/codex-handoff-start.test.mjs +3 -0
- package/src/cli/self-update.mjs +2 -1
- package/src/cli/self-update.test.mjs +46 -0
- package/src/docs-contract.test.mjs +11 -0
- package/src/os/codex-runtime.mjs +36 -0
- package/src/os/codex-runtime.test.mjs +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,23 @@ shipped to npm but were not individually tagged on GitHub.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.10.17] — 2026-09-12
|
|
14
|
+
|
|
15
|
+
### 修正
|
|
16
|
+
|
|
17
|
+
- npm 12が単一要素配列で返す公開版情報を自己更新で受け入れるようにした。
|
|
18
|
+
正常にpackageを更新しても`version_verification_failed`で停止する問題を修正した。
|
|
19
|
+
旧文字列形式を維持し、複数版・空配列・不正な値は後続の設定・移行処理の前に拒否する。
|
|
20
|
+
- 公開前の梱包文書検査も、npm 12がpackage名をキーにして返す梱包情報に対応した。
|
|
21
|
+
|
|
22
|
+
## [0.10.16] — 2026-09-07
|
|
23
|
+
|
|
24
|
+
### 修正
|
|
25
|
+
|
|
26
|
+
- WindowsのDesktop引き継ぎで、稼働中のアプリが使うCodex実行ファイルを選ぶようにした。
|
|
27
|
+
PATH上の古いCodexがデスクトップ版の設定を読めず、新規タスクの作成に失敗する問題を修正した。
|
|
28
|
+
- 引き継ぎ結果へCodex実行元を表示する。アプリの実体を特定できない場合は理由を出して停止する。
|
|
29
|
+
|
|
13
30
|
## [0.10.15] — 2026-09-06
|
|
14
31
|
|
|
15
32
|
### Fixed
|
|
@@ -1449,7 +1466,9 @@ two attempts, instrument first instead of patching again.
|
|
|
1449
1466
|
|
|
1450
1467
|
---
|
|
1451
1468
|
|
|
1452
|
-
[Unreleased]: https://github.com/kitepon/Throughline/compare/v0.10.
|
|
1469
|
+
[Unreleased]: https://github.com/kitepon/Throughline/compare/v0.10.17...HEAD
|
|
1470
|
+
[0.10.17]: https://github.com/kitepon/Throughline/compare/v0.10.16...v0.10.17
|
|
1471
|
+
[0.10.16]: https://github.com/kitepon/Throughline/compare/v0.10.15...v0.10.16
|
|
1453
1472
|
[0.10.15]: https://github.com/kitepon/Throughline/compare/v0.10.14...v0.10.15
|
|
1454
1473
|
[0.10.14]: https://github.com/kitepon/Throughline/compare/v0.10.13...v0.10.14
|
|
1455
1474
|
[0.10.13]: https://github.com/kitepon/Throughline/compare/v0.10.12...v0.10.13
|
package/README.md
CHANGED
|
@@ -638,6 +638,10 @@ opens the new task in Codex Desktop when invoked there, while preserving the
|
|
|
638
638
|
existing VS Code and CLI routes. The result reports both the requested and
|
|
639
639
|
resolved host. The installed `$throughline` skill passes the current Codex
|
|
640
640
|
surface explicitly so a persistent shell or PTY cannot redirect the handoff.
|
|
641
|
+
WindowsのDesktop引き継ぎは、稼働中のCodex Desktopが使う実行ファイルを選びます。
|
|
642
|
+
PATH上の別バージョンによる設定読取りエラーを避け、選んだ実行元を結果へ表示します。
|
|
643
|
+
Desktopを起動してから実行してください。実行ファイルが見つからない場合や、異なる実行ファイルの
|
|
644
|
+
Desktopが複数稼働している場合は理由を出して停止します。明示した`--codex-app-server-bin`は優先します。
|
|
641
645
|
The
|
|
642
646
|
individual commands remain available: validate the fresh-thread handoff with
|
|
643
647
|
`throughline codex-handoff-smoke --session codex:<thread-id>`, optionally audit
|
package/bin/throughline.mjs
CHANGED
|
File without changes
|
|
@@ -44,6 +44,9 @@ Windows PowerShell 5.1へ切り替えない。更新前CLIを再利用せず、
|
|
|
44
44
|
新CLI、公開PATHから起動したCLI、そのversion、`throughline.self_update.v1`成功結果がすべて
|
|
45
45
|
一致した場合だけ完了とする。複数npm prefixが混在して公開PATHが旧実体を指す場合は失敗する。
|
|
46
46
|
|
|
47
|
+
公開版の照合は、npmの旧文字列形式と[npm 12の単一結果配列](https://docs.npmjs.com/cli/v12/commands/npm-view/#output)を受け入れる。
|
|
48
|
+
複数版・空配列・不正な版情報から一つを推測して選ばず、後続の設定・移行処理の前に失敗を返す。
|
|
49
|
+
|
|
47
50
|
## 明示的失敗の契約
|
|
48
51
|
|
|
49
52
|
想定外の状態、外部入力違反、I/O失敗、依存不足を成功扱いにしない。
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import { sameProjectPath } from '../project-path.mjs';
|
|
|
9
9
|
import { shQuote } from '../os/shell.mjs';
|
|
10
10
|
import { openUrlWithOsHandler } from '../os/open-url.mjs';
|
|
11
11
|
import { runTerminalDoScript } from '../os/macos-terminal.mjs';
|
|
12
|
+
import { resolveCodexRuntime } from '../os/codex-runtime.mjs';
|
|
12
13
|
|
|
13
14
|
async function readStdin() {
|
|
14
15
|
let raw = '';
|
|
@@ -242,6 +243,7 @@ function renderTextResult(result) {
|
|
|
242
243
|
lines.push(` execute: ${result.execute ? 'yes' : 'no'}`);
|
|
243
244
|
lines.push(` open requested: ${result.requestedOpenHost ?? result.openHost}`);
|
|
244
245
|
lines.push(` open resolved: ${result.resolvedOpenHost ?? result.open?.host ?? result.openHost}`);
|
|
246
|
+
if (result.runtime) lines.push(` Codex実行元: ${result.runtime.command} (${result.runtime.source})`);
|
|
245
247
|
lines.push(` handoff smoke: ${result.handoffSmoke.status}`);
|
|
246
248
|
lines.push(` prompt chars: ${result.handoffSmoke.promptChars}/${result.handoffSmoke.maxPromptChars}`);
|
|
247
249
|
lines.push(` model prompt: ${result.modelPromptChars}`);
|
|
@@ -452,11 +454,16 @@ export async function run(args) {
|
|
|
452
454
|
}
|
|
453
455
|
|
|
454
456
|
if (parsed.execute && result.status === 'ready') {
|
|
457
|
+
const runtime = resolveCodexRuntime({
|
|
458
|
+
host: result.resolvedOpenHost,
|
|
459
|
+
override: parsed.codexAppServerBin,
|
|
460
|
+
});
|
|
461
|
+
result.runtime = runtime;
|
|
455
462
|
const startResult = await runCodexNewThreadHandoff({
|
|
456
463
|
cwd: process.cwd(),
|
|
457
464
|
prompt: handoffPrompt,
|
|
458
|
-
command:
|
|
459
|
-
commandArgs:
|
|
465
|
+
command: runtime.command,
|
|
466
|
+
commandArgs: runtime.commandArgs,
|
|
460
467
|
timeoutMs: parsed.timeoutMs,
|
|
461
468
|
requestTimeoutMs: parsed.requestTimeoutMs,
|
|
462
469
|
waitForTurn: false,
|
|
@@ -310,6 +310,9 @@ test('codex-handoff-start execute creates a new app-server thread and can skip o
|
|
|
310
310
|
assert.equal(payload.startThreadManually, false);
|
|
311
311
|
assert.equal(payload.newThread.threadId, '019e2000-0000-7000-8000-000000000001');
|
|
312
312
|
assert.equal(payload.newThread.delivery, 'developer-item');
|
|
313
|
+
assert.equal(payload.runtime.command, script);
|
|
314
|
+
assert.equal(payload.runtime.source, 'explicit');
|
|
315
|
+
assert.deepEqual(payload.runtime.commandArgs, []);
|
|
313
316
|
assert.equal(payload.newThread.injectSent, true);
|
|
314
317
|
assert.equal(payload.newThread.turnStatus, 'not-started');
|
|
315
318
|
assert.equal(payload.open.status, 'skipped');
|
package/src/cli/self-update.mjs
CHANGED
|
@@ -77,7 +77,8 @@ function succeeded(result) {
|
|
|
77
77
|
function parseRegistryVersion(result) {
|
|
78
78
|
if (!succeeded(result)) return null;
|
|
79
79
|
try {
|
|
80
|
-
|
|
80
|
+
let value = JSON.parse(result.stdout);
|
|
81
|
+
if (Array.isArray(value) && value.length === 1) [value] = value;
|
|
81
82
|
return typeof value === 'string' && VERSION_PATTERN.test(value) ? value : null;
|
|
82
83
|
} catch {
|
|
83
84
|
return null;
|
|
@@ -181,6 +181,52 @@ test('new CLI completes install, migration, and diagnostics in order', () => {
|
|
|
181
181
|
});
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
test('自己更新はnpmの単一版を文字列と単一要素配列から読み取る', () => {
|
|
185
|
+
for (const platform of ['darwin', 'linux', 'win32']) {
|
|
186
|
+
for (const registryVersion of ['0.10.16', ['0.10.16']]) {
|
|
187
|
+
const stdout = sink();
|
|
188
|
+
const calls = [];
|
|
189
|
+
const runner = (command, args) => {
|
|
190
|
+
calls.push([command, ...args]);
|
|
191
|
+
if (command === 'npm' || command === 'pwsh.exe') return ok(JSON.stringify(registryVersion));
|
|
192
|
+
if (args.at(-1) === 'install') return ok();
|
|
193
|
+
if (args.at(-2) === 'migrate') return ok(JSON.stringify(migration()));
|
|
194
|
+
if (args.at(-2) === 'factory-diagnostics') return ok(JSON.stringify(readyDiagnostics('0.10.16')));
|
|
195
|
+
throw new Error('想定外の更新工程');
|
|
196
|
+
};
|
|
197
|
+
assert.equal(run(['--json'], {
|
|
198
|
+
stdout, stderr: sink(), runner, platform, packageVersion: '0.10.16',
|
|
199
|
+
nodePath: '/node', cliPath: '/throughline.mjs',
|
|
200
|
+
env: {
|
|
201
|
+
THROUGHLINE_SELF_UPDATE_PHASE: 'post-install',
|
|
202
|
+
THROUGHLINE_SELF_UPDATE_BEFORE_VERSION: '0.10.15',
|
|
203
|
+
THROUGHLINE_SELF_UPDATE_EXPECTED_VERSION: '0.10.16',
|
|
204
|
+
},
|
|
205
|
+
}), 0, `${platform}: ${JSON.stringify(registryVersion)}`);
|
|
206
|
+
assert.equal(calls.length, 4);
|
|
207
|
+
assert.deepEqual(JSON.parse(stdout.values.join('')), updateSuccess('0.10.15', '0.10.16'));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test('自己更新は複数版・空配列・不正な版情報で後続工程を始めない', () => {
|
|
213
|
+
for (const value of [[], ['0.10.16', '0.10.17'], [['0.10.16']], [null], [{ version: '0.10.16' }], ['invalid']]) {
|
|
214
|
+
const stdout = sink();
|
|
215
|
+
let calls = 0;
|
|
216
|
+
assert.equal(run(['--json'], {
|
|
217
|
+
stdout, stderr: sink(), packageVersion: '0.10.16', platform: 'darwin',
|
|
218
|
+
runner: () => { calls += 1; return ok(JSON.stringify(value)); },
|
|
219
|
+
env: {
|
|
220
|
+
THROUGHLINE_SELF_UPDATE_PHASE: 'post-install',
|
|
221
|
+
THROUGHLINE_SELF_UPDATE_BEFORE_VERSION: '0.10.15',
|
|
222
|
+
THROUGHLINE_SELF_UPDATE_EXPECTED_VERSION: '0.10.16',
|
|
223
|
+
},
|
|
224
|
+
}), 1);
|
|
225
|
+
assert.equal(calls, 1);
|
|
226
|
+
assert.equal(JSON.parse(stdout.values.join('')).stage, 'version_verification_failed');
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
184
230
|
test('self-update fails closed without reflecting command output', () => {
|
|
185
231
|
const stdout = sink();
|
|
186
232
|
const stderr = sink();
|
|
@@ -6,9 +6,20 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import {
|
|
7
7
|
extractMarkdownTargets,
|
|
8
8
|
findMissingPackedTargets,
|
|
9
|
+
packedFileSet,
|
|
9
10
|
} from '../scripts/verify-pack-markdown-links.mjs';
|
|
10
11
|
import { spawnPortableSync } from './os/portable-spawn-sync.mjs';
|
|
11
12
|
|
|
13
|
+
test('梱包文書検査はnpmの旧配列形式とpackage名をキーにする形式を読む', () => {
|
|
14
|
+
const packed = { name: 'throughline', files: [{ path: 'README.md' }, { path: 'docs/00_overview.md' }] };
|
|
15
|
+
for (const report of [[packed], { throughline: packed }]) {
|
|
16
|
+
assert.deepEqual([...packedFileSet(report)], ['README.md', 'docs/00_overview.md']);
|
|
17
|
+
}
|
|
18
|
+
for (const report of [null, {}, [], [packed, packed], { throughline: { files: null } }]) {
|
|
19
|
+
assert.throws(() => packedFileSet(report), /npm pack dry-run JSON shape invalid/);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
12
23
|
test('Markdown-only CI runs the product-owned documentation contract', () => {
|
|
13
24
|
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
14
25
|
assert.equal(
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { spawnPortableSync } from './portable-spawn-sync.mjs';
|
|
2
|
+
|
|
3
|
+
function discoverWindowsDesktopCodex() {
|
|
4
|
+
const result = spawnPortableSync('pwsh.exe', ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', `
|
|
5
|
+
$ErrorActionPreference = 'Stop'
|
|
6
|
+
$processes = @(Get-CimInstance Win32_Process)
|
|
7
|
+
$paths = @($processes | Where-Object { $_.Name -eq 'codex.exe' } | ForEach-Object {
|
|
8
|
+
$child = $_
|
|
9
|
+
$parent = $processes | Where-Object { $_.ProcessId -eq $child.ParentProcessId }
|
|
10
|
+
if ($parent.ExecutablePath -match '[\\\\/]OpenAI\\.Codex_[^\\\\/]+[\\\\/]app[\\\\/]ChatGPT\\.exe$' -and $child.ExecutablePath) {
|
|
11
|
+
$child.ExecutablePath
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
ConvertTo-Json -InputObject $paths -Compress
|
|
15
|
+
`], { encoding: 'utf8', timeout: 15000 });
|
|
16
|
+
if (result.error || result.status !== 0) {
|
|
17
|
+
throw new Error(`Codex Desktopの実行ファイルを取得できません: ${result.error?.message ?? result.stderr.trim()}`);
|
|
18
|
+
}
|
|
19
|
+
return JSON.parse(result.stdout.trim());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function resolveCodexRuntime({ host, override = null, platform = process.platform,
|
|
23
|
+
discover = discoverWindowsDesktopCodex } = {}) {
|
|
24
|
+
if (override) return { command: override, commandArgs: [], source: 'explicit' };
|
|
25
|
+
const commandArgs = ['app-server', '--listen', 'stdio://'];
|
|
26
|
+
if (platform !== 'win32' || host !== 'desktop') {
|
|
27
|
+
return { command: 'codex', commandArgs, source: 'path' };
|
|
28
|
+
}
|
|
29
|
+
const candidates = [...new Set(discover())];
|
|
30
|
+
if (candidates.length !== 1) {
|
|
31
|
+
throw new Error(candidates.length === 0
|
|
32
|
+
? '稼働中のCodex Desktopの実行ファイルが見つかりません。アプリを起動してから再実行してください。'
|
|
33
|
+
: '複数のCodex Desktop実行ファイルが稼働しています。使用するアプリだけを残して再実行してください。');
|
|
34
|
+
}
|
|
35
|
+
return { command: candidates[0], commandArgs, source: 'desktop-process' };
|
|
36
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { resolveCodexRuntime } from './codex-runtime.mjs';
|
|
4
|
+
|
|
5
|
+
test('Windows Desktopは稼働中アプリのCodexを使う', () => {
|
|
6
|
+
const runtime = resolveCodexRuntime({ platform: 'win32', host: 'desktop',
|
|
7
|
+
discover: () => ['C:\\Codex\\codex.exe', 'C:\\Codex\\codex.exe'] });
|
|
8
|
+
assert.equal(runtime.command, 'C:\\Codex\\codex.exe');
|
|
9
|
+
assert.deepEqual(runtime.commandArgs, ['app-server', '--listen', 'stdio://']);
|
|
10
|
+
assert.equal(runtime.source, 'desktop-process');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('明示したapp-serverはDesktopの探索より優先する', () => {
|
|
14
|
+
const runtime = resolveCodexRuntime({ platform: 'win32', host: 'desktop', override: 'fake.mjs',
|
|
15
|
+
discover: () => { throw new Error('探索してはいけない'); } });
|
|
16
|
+
assert.equal(runtime.command, 'fake.mjs');
|
|
17
|
+
assert.deepEqual(runtime.commandArgs, []);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('対象外のOSとhostは従来のCodexを使う', () => {
|
|
21
|
+
for (const [platform, host] of [['darwin', 'desktop'], ['win32', 'cli'], ['win32', 'vscode']]) {
|
|
22
|
+
assert.equal(resolveCodexRuntime({ platform, host }).command, 'codex');
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('Desktopの実体がない場合や複数ある場合は古いCLIへ切り替えない', () => {
|
|
27
|
+
for (const candidates of [[], ['C:\\one\\codex.exe', 'C:\\two\\codex.exe']]) {
|
|
28
|
+
assert.throws(() => resolveCodexRuntime({ platform: 'win32', host: 'desktop',
|
|
29
|
+
discover: () => candidates }), /Codex Desktop/);
|
|
30
|
+
}
|
|
31
|
+
});
|