@bash0816/copilot-termux 1.0.68 → 1.0.70
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/bin/copilot +27 -2
- package/config/copilot-termux-release-manifest.json +5 -5
- package/config/manifest.json +2 -2
- package/lib/check-updates.js +15 -24
- package/lib/platform-patch.js +120 -6
- package/lib/setup.js +95 -29
- package/package.json +1 -1
package/bin/copilot
CHANGED
|
@@ -40,6 +40,7 @@ fi
|
|
|
40
40
|
_GLIBC_LD="${PREFIX}/glibc/lib/ld-linux-aarch64.so.1"
|
|
41
41
|
_GLIBC_LIBS="${PREFIX}/glibc/lib"
|
|
42
42
|
_GLIBC_RUNTIME="${CURRENT}/prebuilds/linux-arm64/runtime.node"
|
|
43
|
+
_GLIBC_CLI_NATIVE="${CURRENT}/prebuilds/linux-arm64/cli-native.node"
|
|
43
44
|
_GLIBC_NODE_AUTO="${CACHE_DIR}/glibc-node/node"
|
|
44
45
|
|
|
45
46
|
_USE_GLIBC_NODE=""
|
|
@@ -49,7 +50,19 @@ elif [ -x "${_GLIBC_NODE_AUTO}" ]; then
|
|
|
49
50
|
_USE_GLIBC_NODE="${_GLIBC_NODE_AUTO}"
|
|
50
51
|
fi
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
_GLIBC_READY=1
|
|
54
|
+
[ -n "${_USE_GLIBC_NODE}" ] || _GLIBC_READY=0
|
|
55
|
+
[ -f "${_GLIBC_RUNTIME}" ] || _GLIBC_READY=0
|
|
56
|
+
[ -f "${_GLIBC_CLI_NATIVE}" ] || _GLIBC_READY=0
|
|
57
|
+
[ -f "${_GLIBC_LD}" ] || _GLIBC_READY=0
|
|
58
|
+
|
|
59
|
+
_GLIBC_ANY=0
|
|
60
|
+
[ -n "${_USE_GLIBC_NODE}" ] && _GLIBC_ANY=1
|
|
61
|
+
[ -f "${_GLIBC_RUNTIME}" ] && _GLIBC_ANY=1
|
|
62
|
+
[ -f "${_GLIBC_CLI_NATIVE}" ] && _GLIBC_ANY=1
|
|
63
|
+
[ -f "${_GLIBC_LD}" ] && _GLIBC_ANY=1
|
|
64
|
+
|
|
65
|
+
if [ "${_GLIBC_READY}" = "1" ]; then
|
|
53
66
|
_SSL_CERT="${PREFIX}/etc/tls/cert.pem"
|
|
54
67
|
_SSL_CERT_DIR="${PREFIX}/etc/tls"
|
|
55
68
|
if [ ! -r "${_SSL_CERT}" ]; then
|
|
@@ -65,9 +78,21 @@ if [ -n "${_USE_GLIBC_NODE}" ] && [ -f "${_GLIBC_RUNTIME}" ] && [ -f "${_GLIBC_L
|
|
|
65
78
|
--require "${PACKAGE_DIR}/lib/platform-patch.js" \
|
|
66
79
|
"${COPILOT_INDEX}" \
|
|
67
80
|
"$@"
|
|
81
|
+
elif [ "${_GLIBC_ANY}" = "1" ]; then
|
|
82
|
+
# 注: システムの glibc-repo パッケージ(_GLIBC_LD)を別目的で入れているだけで
|
|
83
|
+
# copilot-termux 自身の glibc mode セットアップ(setup.js の setupGlibcRuntime/setupGlibcNode)が
|
|
84
|
+
# 未完の場合も、ここでは「glibc mode 設定が部分的」としてエラー扱いにする。
|
|
85
|
+
# これは意図的な判断(Free auto mode の既知の壊れた bionic 経路への黙示的フォールバックを防ぐため)。
|
|
86
|
+
echo "[copilot-termux] ERROR: glibc mode partially configured. Missing:" >&2
|
|
87
|
+
[ -n "${_USE_GLIBC_NODE}" ] || echo " - glibc node (${_GLIBC_NODE_AUTO})" >&2
|
|
88
|
+
[ -f "${_GLIBC_RUNTIME}" ] || echo " - glibc runtime.node (${_GLIBC_RUNTIME})" >&2
|
|
89
|
+
[ -f "${_GLIBC_CLI_NATIVE}" ] || echo " - glibc cli-native.node (${_GLIBC_CLI_NATIVE})" >&2
|
|
90
|
+
[ -f "${_GLIBC_LD}" ] || echo " - glibc loader (${_GLIBC_LD})" >&2
|
|
91
|
+
echo "[copilot-termux] Run: copilot-termux setup" >&2
|
|
92
|
+
exit 1
|
|
68
93
|
fi
|
|
69
94
|
|
|
70
|
-
# bionic fallback
|
|
95
|
+
# bionic fallback(glibc関連ファイルが一つも無い場合のみ。意図的なbionic-onlyの正当な状態)
|
|
71
96
|
LD_PRELOAD="${PRELOAD}" exec node \
|
|
72
97
|
--require "${PACKAGE_DIR}/lib/platform-patch.js" \
|
|
73
98
|
"${COPILOT_INDEX}" \
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 1,
|
|
3
3
|
"package_name": "@bash0816/copilot-termux",
|
|
4
|
-
"copilot_version": "1.0.
|
|
5
|
-
"latest_audited_version": "1.0.
|
|
6
|
-
"latest_candidate_version":
|
|
7
|
-
"previous_stable_version": "1.0.
|
|
4
|
+
"copilot_version": "1.0.70",
|
|
5
|
+
"latest_audited_version": "1.0.69",
|
|
6
|
+
"latest_candidate_version": null,
|
|
7
|
+
"previous_stable_version": "1.0.68-1",
|
|
8
8
|
"candidate_state": "none",
|
|
9
9
|
"canonical_package_status": "not_published",
|
|
10
10
|
"public_distribution_status": "staged",
|
|
11
11
|
"nodejs_glibc_version": "node-glibc-v24.15.0",
|
|
12
12
|
"build_run_id": null,
|
|
13
|
-
"last_updated": "2026-07-
|
|
13
|
+
"last_updated": "2026-07-10",
|
|
14
14
|
"manifest_url": "https://raw.githubusercontent.com/bash0816/Github-Copilot-Termux/main/packages/copilot-termux/config/copilot-termux-release-manifest.json"
|
|
15
15
|
}
|
package/config/manifest.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"wrapperVersion": "2.0.0",
|
|
3
3
|
"copilot": {
|
|
4
4
|
"package": "@github/copilot-linuxmusl-arm64",
|
|
5
|
-
"version": "1.0.
|
|
6
|
-
"integrity": "sha512-
|
|
5
|
+
"version": "1.0.70",
|
|
6
|
+
"integrity": "sha512-Xy3tDjIMmMkZZzJjCrK5aDCLLV5+pyOfIcT1lWLmqVWJG0erpHXL6KU82nGW+0nc0TPqGZFHvOyQeLuXl+s3ZA=="
|
|
7
7
|
},
|
|
8
8
|
"glibcNode": {
|
|
9
9
|
"version": "26.2.0",
|
package/lib/check-updates.js
CHANGED
|
@@ -31,8 +31,10 @@ function parseVer(v) {
|
|
|
31
31
|
return { nums, pre };
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// このプロジェクトの
|
|
35
|
-
//
|
|
34
|
+
// このプロジェクトの `-N` サフィックスは、汎用 semver のプレリリース識別子ではなく、
|
|
35
|
+
// 「正式版リリース後に追加したfork独自パッチのリビジョン番号」を意味する。
|
|
36
|
+
// suffixなし = revision 0、`-N` = revision N として扱い、Nが大きいほど新しい。
|
|
37
|
+
// (例: 1.0.68 < 1.0.68-1 < 1.0.68-2)
|
|
36
38
|
// returns negative if a < b, 0 if equal, positive if a > b
|
|
37
39
|
function compareVersions(a, b) {
|
|
38
40
|
const pa = parseVer(a);
|
|
@@ -42,28 +44,14 @@ function compareVersions(a, b) {
|
|
|
42
44
|
const nb = pb.nums[i] || 0;
|
|
43
45
|
if (na !== nb) return na - nb;
|
|
44
46
|
}
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const ap = aParts[j], bp = bParts[j];
|
|
54
|
-
if (ap === undefined) return -1;
|
|
55
|
-
if (bp === undefined) return 1;
|
|
56
|
-
const an = Number(ap), bn = Number(bp);
|
|
57
|
-
if (!isNaN(an) && !isNaN(bn)) {
|
|
58
|
-
if (an !== bn) return an - bn;
|
|
59
|
-
} else {
|
|
60
|
-
if (ap < bp) return -1;
|
|
61
|
-
if (ap > bp) return 1;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return 0;
|
|
65
|
-
}
|
|
66
|
-
return 0;
|
|
47
|
+
// このフォークの `-N` サフィックスは一般semverの「正式版より前のプレリリース」ではなく、
|
|
48
|
+
// 「正式版リリース後に追加したfork独自パッチのリビジョン番号」を意味する。
|
|
49
|
+
// suffixなし = revision 0、`-N` = revision N として扱い、Nが大きいほど新しいとみなす。
|
|
50
|
+
// (例: 1.0.68 < 1.0.68-1 < 1.0.68-2)
|
|
51
|
+
// parseVer前提: N は数字のみ(本ファイル冒頭コメントの前提を踏襲)。
|
|
52
|
+
const ra = pa.pre === null ? 0 : Number(pa.pre);
|
|
53
|
+
const rb = pb.pre === null ? 0 : Number(pb.pre);
|
|
54
|
+
return ra - rb;
|
|
67
55
|
}
|
|
68
56
|
|
|
69
57
|
function isPrerelease(v) {
|
|
@@ -172,6 +160,9 @@ async function runNotify() {
|
|
|
172
160
|
|
|
173
161
|
module.exports = { runUpdate, runNotify };
|
|
174
162
|
|
|
163
|
+
// テスト専用export・CLI実行経路には影響しない(node:test用)
|
|
164
|
+
module.exports.__test = { compareVersions };
|
|
165
|
+
|
|
175
166
|
// CLI entry
|
|
176
167
|
if (require.main === module) {
|
|
177
168
|
const mode = process.argv[2] || 'notify';
|
package/lib/platform-patch.js
CHANGED
|
@@ -18,6 +18,33 @@ if (process.report) {
|
|
|
18
18
|
const Module = require('module');
|
|
19
19
|
const fs = require('fs');
|
|
20
20
|
const path = require('path');
|
|
21
|
+
const { execFile } = require('child_process');
|
|
22
|
+
const { promisify } = require('util');
|
|
23
|
+
const execFileAsync = promisify(execFile);
|
|
24
|
+
|
|
25
|
+
// git CLIを配列引数で呼ぶ(shell経由なし、インジェクション対策)。失敗時は例外を投げず安全な既定値を返す。
|
|
26
|
+
async function runGit(cwd, args) {
|
|
27
|
+
try {
|
|
28
|
+
const { stdout } = await execFileAsync('git', args, { cwd, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
29
|
+
return stdout;
|
|
30
|
+
} catch (_) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// backupFile() は戻り値を `${r.substring(9,25)}-${Date.now()}` としてバックアップファイル名に使う。
|
|
36
|
+
// substring(9,25) は "git-sha1:" (9文字) プレフィックスを除いた先頭16文字を取る計算であり、
|
|
37
|
+
// ネイティブ実装の実際の戻り値フォーマットが "git-sha1:<40hex>" であることを示している。
|
|
38
|
+
// 互換性のため git hash-object 相当(blob SHA1)を "git-sha1:" プレフィックス付きで返す。
|
|
39
|
+
async function hashFileContent(gitRoot, filePath) {
|
|
40
|
+
try {
|
|
41
|
+
const out = await runGit(gitRoot, ['hash-object', '--', filePath]);
|
|
42
|
+
if (!out) return null;
|
|
43
|
+
return `git-sha1:${out.trim()}`;
|
|
44
|
+
} catch (_) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
21
48
|
|
|
22
49
|
const _pkgVersion = (() => {
|
|
23
50
|
try { return require(path.join(__dirname, '..', 'package.json')).version; } catch (_) { return '1.0.65'; }
|
|
@@ -56,6 +83,16 @@ Module._resolveFilename = function (request, parent, isMain, options) {
|
|
|
56
83
|
return origResolve(request, parent, isMain, options);
|
|
57
84
|
};
|
|
58
85
|
|
|
86
|
+
// [glibc mode の設計方針]
|
|
87
|
+
// glibc mode(COPILOT_TERMUX_GLIBC_MODE=1)は「native実装に全面的に任せるモード」ではない。
|
|
88
|
+
// 正しくは「glibc addon のロードを可能にしつつ、Copilot-Termux が検証済みの
|
|
89
|
+
// JS通信/認証/モデル解決の経路は両モード共通で維持するモード」である。
|
|
90
|
+
// 常時適用されるJSスタブ(isGlibcMode分岐がない箇所)は、bionic回避だけでなく
|
|
91
|
+
// Copilot token交換・Free/Enterprise endpoint補正・モデルstale対策・
|
|
92
|
+
// tools payload補正・stream互換維持を兼ねているため、glibc modeでも意図的に残す。
|
|
93
|
+
// native実装に戻す変更は、スタブ単位ではなくauth/network/model/streamの連鎖単位で
|
|
94
|
+
// 実機検証してから行う(2026-07-06 GPT-5.5レビュー結論)。
|
|
95
|
+
|
|
59
96
|
// [Android bionic 対応] linuxmusl-arm64/runtime.node は Rust tokio を使う。
|
|
60
97
|
// bionic 上で musl pthread ABI でスレッドを生成すると TUI 起動時に SIGSEGV。
|
|
61
98
|
// runtime.node ロード後に Rust async 初期化関数を no-op に差し替えて阻止する。
|
|
@@ -66,6 +103,7 @@ Module._load = function (request, parent, isMain) {
|
|
|
66
103
|
path.basename(request) === 'runtime.node') {
|
|
67
104
|
if (result.__copilotTermuxPatched) return result;
|
|
68
105
|
result.__copilotTermuxPatched = true;
|
|
106
|
+
let _nfIdSeq = 3e6;
|
|
69
107
|
const isGlibcMode = !!process.env.COPILOT_TERMUX_GLIBC_MODE;
|
|
70
108
|
if (!isGlibcMode) {
|
|
71
109
|
// Rust tokio を使う関数群を no-op に差し替え。
|
|
@@ -74,12 +112,29 @@ Module._load = function (request, parent, isMain) {
|
|
|
74
112
|
// jsonrpcServer* は拡張 JSON-RPC サーバー (ThreadsafeFunction)、
|
|
75
113
|
// lspClient* は LSP クライアント (ThreadsafeFunction)。
|
|
76
114
|
// featureFlagService* は同期 Rust のため除外(no-op にすると .handle クラッシュ)。
|
|
77
|
-
const TOKIO_PATTERN = /^(ahpRelay|ahpRelayAuthenticate|ahpRelayCancelTurn|ahpRelayCompleteInput|ahpRelayCompletions|ahpRelayConfirmToolCall|ahpRelayConnect|ahpRelayCreate|ahpRelayCreateSession|ahpRelayDispose|ahpRelayGetPlan|ahpRelayListCheckpoints|ahpRelayListWorkspaceFiles|ahpRelayPendingRequiredResourcesJson|ahpRelayReadCheckpoint|ahpRelayRefreshSessions|ahpRelayReleaseSession|ahpRelayRemovePendingMessage|ahpRelaySelectAgent|ahpRelaySessionDiff|ahpRelaySetMode|ahpRelaySetModel|ahpRelaySetPendingMessage|ahpRelaySetSessionApproveAll|ahpRelaySetTitle|ahpRelayStartTurn|ahpRelayStateJson|ahpRelaySubscribeSession|ahpRelayTerminalDispose|ahpRelayTerminalEnsure|ahpRelayTerminalWrite|jsonrpcServer|jsonrpcServerAddConnection|jsonrpcServerBeginShutdown|jsonrpcServerConnectionClose|jsonrpcServerConnectionNotify|jsonrpcServerConnectionNotifyAfterResponse|jsonrpcServerConnectionRequest|jsonrpcServerConnectionWrite|jsonrpcServerCreate|jsonrpcServerDispatchComplete|jsonrpcServerRegisterSession|jsonrpcServerRemove|jsonrpcServerRemoveSession|jsonrpcServerStartTcpListener|jsonrpcServerStopTcpListener|lspClient|lspClientCloseDocument|lspClientCreateOwned|lspClientCreateOwnedSandboxed|lspClientDispose|lspClientEnhanceStartupErrorMessage|lspClientFindSourceFile|lspClientInitialize|lspClientInitialized|lspClientOpenDocument|lspClientOwnedShutdown|lspClientRequest|lspClientTakeExitInfo|lspClientWaitForDiagnostics|lspClientWaitForProjectLoad|modelHttp|modelHttpCancelRequest|modelHttpRegisterCancellation|modelHttpResetNetworking|networkFetch|networkFetchResetClients|sessionSqlite|sessionSqliteClose|sessionSqliteExec|sessionStore|sessionStoreBeginForgeSkillProposalGeneration|sessionStoreClose|sessionStoreCompleteForgeSkillProposalGeneration|sessionStoreDefaultPath|sessionStoreDeleteDynamicContextItem|sessionStoreEnsureSession|sessionStoreExec|sessionStoreExecuteReadOnly|sessionStoreFailStaleGeneratingForgeSkillProposals|sessionStoreGetCheckpoints|sessionStoreGetDynamicContextBoard|sessionStoreGetDynamicContextItem|sessionStoreGetFiles|sessionStoreGetForgeSkillProposalByFingerprint|sessionStoreGetForgeSkillProposalById|sessionStoreGetForgeSkillProposalWorkspaceBefore|sessionStoreGetForgeTrajectoryEvents|sessionStoreGetForgeTrajectoryEventsForScope|sessionStoreGetMaxTurnIndex|sessionStoreGetRefs|sessionStoreGetSession|sessionStoreGetStats|sessionStoreGetTurns|sessionStoreIncrementDynamicContextCount|sessionStoreIncrementDynamicContextReadCount|sessionStoreIndexWorkspaceArtifact|sessionStoreInsertCheckpointWithRuntimeDefaults|sessionStoreInsertDynamicContextItem|sessionStoreInsertFileWithRuntimeDefaults|sessionStoreInsertForgeTrajectoryEventWithRuntimeDefaults|sessionStoreInsertRefWithRuntimeDefaults|sessionStoreInsertTurnWithRuntimeDefaults|sessionStoreListForgeSkillProposals|sessionStoreOpen|sessionStoreSearch|sessionStoreTrackingEventOperations|sessionStoreTrackingExtractFilePath|sessionStoreTrackingExtractForgeTrajectoryEvents|sessionStoreTrackingExtractRefsFromBash|sessionStoreTrackingExtractRefsFromMcpTool|sessionStoreTrackingExtractRepoFromMcpTool|sessionStoreTrackingFlushOperations|sessionStoreTrackingInitialState|sessionStoreTransitionForgeSkillProposalStatus|sessionStoreUpsertDynamicContextItem|sessionStoreUpsertSessionWithRuntimeDefaults|websocketResponses)/;
|
|
115
|
+
const TOKIO_PATTERN = /^(ahpRelay|ahpRelayAuthenticate|ahpRelayCancelTurn|ahpRelayCompleteInput|ahpRelayCompletions|ahpRelayConfirmToolCall|ahpRelayConnect|ahpRelayCreate|ahpRelayCreateSession|ahpRelayDispose|ahpRelayGetPlan|ahpRelayListCheckpoints|ahpRelayListWorkspaceFiles|ahpRelayPendingRequiredResourcesJson|ahpRelayReadCheckpoint|ahpRelayRefreshSessions|ahpRelayReleaseSession|ahpRelayRemovePendingMessage|ahpRelaySelectAgent|ahpRelaySessionDiff|ahpRelaySetMode|ahpRelaySetModel|ahpRelaySetPendingMessage|ahpRelaySetSessionApproveAll|ahpRelaySetTitle|ahpRelayStartTurn|ahpRelayStateJson|ahpRelaySubscribeSession|ahpRelayTerminalDispose|ahpRelayTerminalEnsure|ahpRelayTerminalWrite|jsonrpcServer|jsonrpcServerAddConnection|jsonrpcServerBeginShutdown|jsonrpcServerConnectionClose|jsonrpcServerConnectionNotify|jsonrpcServerConnectionNotifyAfterResponse|jsonrpcServerConnectionRequest|jsonrpcServerConnectionWrite|jsonrpcServerCreate|jsonrpcServerDispatchComplete|jsonrpcServerRegisterSession|jsonrpcServerRemove|jsonrpcServerRemoveSession|jsonrpcServerStartTcpListener|jsonrpcServerStopTcpListener|lspClient|lspClientCloseDocument|lspClientCreateOwned|lspClientCreateOwnedSandboxed|lspClientDispose|lspClientEnhanceStartupErrorMessage|lspClientFindSourceFile|lspClientInitialize|lspClientInitialized|lspClientOpenDocument|lspClientOwnedShutdown|lspClientRequest|lspClientTakeExitInfo|lspClientWaitForDiagnostics|lspClientWaitForProjectLoad|modelHttp|modelHttpCancelRequest|modelHttpRegisterCancellation|modelHttpResetNetworking|networkFetch|networkFetchNextRequestId|networkFetchResetClients|sessionSqlite|sessionSqliteClose|sessionSqliteExec|sessionStore|sessionStoreBeginForgeSkillProposalGeneration|sessionStoreClose|sessionStoreCompleteForgeSkillProposalGeneration|sessionStoreDefaultPath|sessionStoreDeleteDynamicContextItem|sessionStoreEnsureSession|sessionStoreExec|sessionStoreExecuteReadOnly|sessionStoreExecuteReadOnlyAsync|sessionStoreExecuteReadOnlyWithCap|sessionStoreFailStaleGeneratingForgeSkillProposals|sessionStoreGetCheckpoints|sessionStoreGetDynamicContextBoard|sessionStoreGetDynamicContextItem|sessionStoreGetFiles|sessionStoreGetForgeSkillProposalByFingerprint|sessionStoreGetForgeSkillProposalById|sessionStoreGetForgeSkillProposalWorkspaceBefore|sessionStoreGetForgeTrajectoryEvents|sessionStoreGetForgeTrajectoryEventsForScope|sessionStoreGetMaxTurnIndex|sessionStoreGetRefs|sessionStoreGetSession|sessionStoreGetStats|sessionStoreGetTurns|sessionStoreIncrementDynamicContextCount|sessionStoreIncrementDynamicContextReadCount|sessionStoreIndexWorkspaceArtifact|sessionStoreInsertAssistantUsageEventWithRuntimeDefaults|sessionStoreInsertCheckpointWithRuntimeDefaults|sessionStoreInsertDynamicContextItem|sessionStoreInsertFileWithRuntimeDefaults|sessionStoreInsertForgeTrajectoryEventWithRuntimeDefaults|sessionStoreInsertRefWithRuntimeDefaults|sessionStoreInsertTurnWithRuntimeDefaults|sessionStoreListForgeSkillProposals|sessionStoreOpen|sessionStoreSearch|sessionStoreTrackingEventOperations|sessionStoreTrackingExtractFilePath|sessionStoreTrackingExtractForgeTrajectoryEvents|sessionStoreTrackingExtractRefsFromBash|sessionStoreTrackingExtractRefsFromMcpTool|sessionStoreTrackingExtractRepoFromMcpTool|sessionStoreTrackingFlushOperations|sessionStoreTrackingInitialState|sessionStoreTransitionForgeSkillProposalStatus|sessionStoreUpsertDynamicContextItem|sessionStoreUpsertSessionWithRuntimeDefaults|websocketResponses)/;
|
|
78
116
|
for (const key of Object.keys(result)) {
|
|
79
117
|
if (TOKIO_PATTERN.test(key) && typeof result[key] === 'function') {
|
|
80
118
|
result[key] = () => undefined;
|
|
81
119
|
}
|
|
82
120
|
}
|
|
121
|
+
// networkFetchNextRequestId は networkFetchStreamStart/RequestCancel の相関キーとして
|
|
122
|
+
// 実際に使われるため、no-opのままにはできない。TOKIO_PATTERNの一括no-op化で
|
|
123
|
+
// 潰された直後にJSの単調増加ID生成で上書きする。
|
|
124
|
+
result.networkFetchNextRequestId = () => 'nf-' + (++_nfIdSeq);
|
|
125
|
+
// MCP-BIONIC-001: 実機でSIGSEGV確認済みの2関数を、bionicモード限定でクリーンエラー化する。
|
|
126
|
+
// 対象外の残り10関数はdocs/KNOWN-BUGS.mdのMCP-BIONIC-001セクションに未検証事項として個別記録済み。
|
|
127
|
+
const BIONIC_SIGSEGV_STUBS = [
|
|
128
|
+
'capiClientRetrieveAvailableModels',
|
|
129
|
+
'mcpClientConnectStreamableHttpWithHandlersAndOnclose',
|
|
130
|
+
];
|
|
131
|
+
for (const _key of BIONIC_SIGSEGV_STUBS) {
|
|
132
|
+
if (typeof result[_key] === 'function') {
|
|
133
|
+
result[_key] = () => {
|
|
134
|
+
throw new Error(`${_key} unsupported on Android bionic (native tokio disabled to avoid SIGSEGV)`);
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
83
138
|
// git*Async: Rust tokio async functions — type-safe stubs to prevent SIGSEGV.
|
|
84
139
|
// Returns empty/null values matching what app.js callers expect.
|
|
85
140
|
const GIT_ASYNC_STUBS = {
|
|
@@ -102,6 +157,58 @@ Module._load = function (request, parent, isMain) {
|
|
|
102
157
|
gitStatusPorcelainAllAsync: async () => null,
|
|
103
158
|
gitCurrentBranchRemoteAsync: async () => null,
|
|
104
159
|
gitWorkingTreeDiffStatsAsync: async () => ({ linesAdded: 0, linesRemoved: 0 }),
|
|
160
|
+
gitFindRootWithOptionalWorktreeResolutionAsync: async (cwd) => {
|
|
161
|
+
const out = await runGit(cwd || process.cwd(), ['rev-parse', '--show-toplevel']);
|
|
162
|
+
const root = out ? out.trim() : null;
|
|
163
|
+
return root ? { found: true, gitRoot: root } : { found: false };
|
|
164
|
+
},
|
|
165
|
+
gitCommitShaAsync: async (gitRoot) => {
|
|
166
|
+
const out = await runGit(gitRoot, ['rev-parse', 'HEAD']);
|
|
167
|
+
return out ? out.trim() : null;
|
|
168
|
+
},
|
|
169
|
+
gitStatusFilesAsync: async (gitRoot) => {
|
|
170
|
+
// --untracked-files=all: 未追跡ディレクトリを "?? dir/" に畳まず、配下ファイルを個別に列挙させる。
|
|
171
|
+
// SnapshotManager.backupFile はディレクトリを保存できないため、ディレクトリ単位の畳み込みだと
|
|
172
|
+
// 未追跡ディレクトリ配下のファイルが rollback 対象から漏れる。
|
|
173
|
+
const out = await runGit(gitRoot, ['status', '--porcelain=v1', '-z', '--untracked-files=all']);
|
|
174
|
+
if (!out) return [];
|
|
175
|
+
const entries = out.split('\0').filter(Boolean);
|
|
176
|
+
const results = [];
|
|
177
|
+
for (let i = 0; i < entries.length; i++) {
|
|
178
|
+
const entry = entries[i];
|
|
179
|
+
const status = entry.slice(0, 2);
|
|
180
|
+
const relPath = entry.slice(3);
|
|
181
|
+
// rename/copy: X or Y can be 'R'/'C' (index or worktree side); -z format is "to\0from\0"
|
|
182
|
+
const isRenameOrCopy = status[0] === 'R' || status[0] === 'C' || status[1] === 'R' || status[1] === 'C';
|
|
183
|
+
if (isRenameOrCopy && entries[i + 1] !== undefined) {
|
|
184
|
+
i++; // skip the old path field
|
|
185
|
+
}
|
|
186
|
+
// 削除(D)されたファイルは lstat/hash 対象が存在しないため除外する。
|
|
187
|
+
if (status[0] === 'D' || status[1] === 'D') continue;
|
|
188
|
+
results.push({ path: path.join(gitRoot, relPath), status });
|
|
189
|
+
}
|
|
190
|
+
return results;
|
|
191
|
+
},
|
|
192
|
+
gitHashFilesPrefixedAsync: async (gitRoot, files) => {
|
|
193
|
+
const list = Array.isArray(files) ? files : [];
|
|
194
|
+
const out = [];
|
|
195
|
+
for (const p of list) {
|
|
196
|
+
const hash = await hashFileContent(gitRoot, p);
|
|
197
|
+
if (hash) out.push({ path: p, hash });
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
200
|
+
},
|
|
201
|
+
gitHashSingleFileAsync: async (gitRoot, filePath) => {
|
|
202
|
+
const hash = await hashFileContent(gitRoot, filePath);
|
|
203
|
+
return hash || '';
|
|
204
|
+
},
|
|
205
|
+
gitUntrackedPathsWithOptionalDirectoryAsync: async (gitRoot, opts) => {
|
|
206
|
+
const args = ['ls-files', '--others', '--exclude-standard'];
|
|
207
|
+
if (opts && opts.directory) args.push('--directory');
|
|
208
|
+
const out = await runGit(gitRoot, args);
|
|
209
|
+
if (!out) return [];
|
|
210
|
+
return out.split('\n').filter(Boolean);
|
|
211
|
+
},
|
|
105
212
|
};
|
|
106
213
|
for (const [key, stub] of Object.entries(GIT_ASYNC_STUBS)) {
|
|
107
214
|
if (typeof result[key] === 'function') result[key] = stub;
|
|
@@ -273,15 +380,17 @@ Module._load = function (request, parent, isMain) {
|
|
|
273
380
|
}
|
|
274
381
|
// --- JS networkFetch* implementation (bionic: tokio networkFetch* are no-op'd) ---
|
|
275
382
|
// B7 が QXe() から直接呼ばれる MCP 専用パスでクラッシュするため JS で代替。
|
|
276
|
-
// networkFetchStreamStart
|
|
383
|
+
// networkFetchStreamStart(requestId, req) → Promise<{handle,url,status,statusText,headers}>
|
|
384
|
+
// (2026-07-12修正: 実際のCLI本体は requestId を第1引数、req を第2引数として渡し、
|
|
385
|
+
// 戻り値を直接thenableとして扱う。旧実装は1引数・{requestId,response}のラップ返却で
|
|
386
|
+
// この規約と不一致だったため s.then is not a function / Failed to parse URL from undefined
|
|
387
|
+
// エラーの原因になっていた。詳細は docs/KNOWN-BUGS.md AGENT-001参照)
|
|
277
388
|
// networkFetchStreamRead → Promise<{ done, body?: Uint8Array }>
|
|
278
389
|
// networkFetchStreamClose → void
|
|
279
390
|
// networkFetchRequestCancel → void
|
|
280
391
|
const _nfMap = new Map();
|
|
281
|
-
let _nfIdSeq = 3e6;
|
|
282
392
|
|
|
283
|
-
result.networkFetchStreamStart = function(req) {
|
|
284
|
-
const requestId = 'nf-' + (++_nfIdSeq);
|
|
393
|
+
result.networkFetchStreamStart = function(requestId, req) {
|
|
285
394
|
const abortCtrl = new AbortController();
|
|
286
395
|
const entry = { abort: () => abortCtrl.abort(), reader: null };
|
|
287
396
|
_nfMap.set(requestId, entry);
|
|
@@ -332,7 +441,7 @@ Module._load = function (request, parent, isMain) {
|
|
|
332
441
|
return { handle, url: res.url, status: res.status, statusText: res.statusText, headers: respHeaders };
|
|
333
442
|
})();
|
|
334
443
|
|
|
335
|
-
return
|
|
444
|
+
return response;
|
|
336
445
|
};
|
|
337
446
|
|
|
338
447
|
result.networkFetchStreamRead = function(handle) {
|
|
@@ -827,6 +936,7 @@ Module._load = function (request, parent, isMain) {
|
|
|
827
936
|
};
|
|
828
937
|
return { json: JSON.stringify({ kind: 'ok', completion, copilotUsage: null, ttftMs: null, interTokenLatencyMs: null }) };
|
|
829
938
|
};
|
|
939
|
+
// glibc modeでも維持(isGlibcMode分岐なし): bionic回避に加えCopilot token交換・アカウント切替キャッシュ管理を兼ねるため
|
|
830
940
|
// === authManager* JS stubs (1.0.64: tokio thread spawn → SIGSEGV on bionic) ===
|
|
831
941
|
const _authMgr = new Map(); // uuid → { cachedInfo, pendingInfo, cachedToken, cachedHost, gen }
|
|
832
942
|
let _modelListCache = null; // capiClientListModels が取得したモデルリストキャッシュ(modelResolver fallback 用)
|
|
@@ -1271,6 +1381,10 @@ Module._load = function (request, parent, isMain) {
|
|
|
1271
1381
|
// bionic 上の実 I/O で動作しないため、Node.js ビルトイン fetch(動作確認済み)に固定する。
|
|
1272
1382
|
// GitHub OAuth token via env var or gh CLI (keychain unavailable on bionic)
|
|
1273
1383
|
|
|
1384
|
+
// glibc modeでも維持(isGlibcMode分岐なし、かつファイルスコープのためそもそも分岐不可):
|
|
1385
|
+
// JS実装のnetworkFetch*/modelHttp*/capiClientListModelsがglobalThis.fetchに依存しているため、
|
|
1386
|
+
// ここだけglibc modeでnative fetchに戻すと、JSスタブがRust fetch経由になり前提が崩れる。
|
|
1387
|
+
|
|
1274
1388
|
const _COPILOT_TOKEN_DEFAULT_TTL_MS = 28 * 60 * 1000;
|
|
1275
1389
|
function _normalizeCopilotTokenExpiry(expiresAt, now = Date.now()) {
|
|
1276
1390
|
const parsed = expiresAt ? Date.parse(expiresAt) : NaN;
|
package/lib/setup.js
CHANGED
|
@@ -91,15 +91,16 @@ async function setup() {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
await setupGlibcWrap();
|
|
95
|
+
await setupGlibcRuntime(versionDir, version);
|
|
96
|
+
await setupGlibcNode();
|
|
97
|
+
|
|
94
98
|
const currentLink = path.join(CACHE_DIR, 'current');
|
|
95
99
|
const tmp = path.join(CACHE_DIR, `current.tmp.${process.pid}`);
|
|
96
100
|
try { fs.unlinkSync(tmp); } catch (_) {}
|
|
97
101
|
fs.symlinkSync(versionDir, tmp);
|
|
98
102
|
fs.renameSync(tmp, currentLink);
|
|
99
103
|
|
|
100
|
-
await setupGlibcWrap();
|
|
101
|
-
await setupGlibcRuntime(versionDir, version);
|
|
102
|
-
await setupGlibcNode();
|
|
103
104
|
console.log(`✓ copilot ${version} ready`);
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -144,7 +145,9 @@ async function setupGlibcWrap() {
|
|
|
144
145
|
|
|
145
146
|
async function setupGlibcRuntime(versionDir, version) {
|
|
146
147
|
const linux64Dir = path.join(versionDir, 'prebuilds', 'linux-arm64');
|
|
147
|
-
|
|
148
|
+
const runtimeNode = path.join(linux64Dir, 'runtime.node');
|
|
149
|
+
const cliNativeNode = path.join(linux64Dir, 'cli-native.node');
|
|
150
|
+
if (fs.existsSync(runtimeNode) && fs.existsSync(cliNativeNode)) {
|
|
148
151
|
return;
|
|
149
152
|
}
|
|
150
153
|
|
|
@@ -178,39 +181,102 @@ async function setupGlibcRuntime(versionDir, version) {
|
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
async function setupGlibcNode() {
|
|
181
|
-
const
|
|
182
|
-
const
|
|
184
|
+
const { version, sha256: expectedSha256 } = manifest.glibcNode;
|
|
185
|
+
const nodeVersionDir = path.join(CACHE_DIR, `glibc-node-${version}`);
|
|
186
|
+
const nodeBin = path.join(nodeVersionDir, 'node');
|
|
187
|
+
const currentLink = path.join(CACHE_DIR, 'glibc-node');
|
|
188
|
+
|
|
189
|
+
const PREFIX = process.env.PREFIX || '/data/data/com.termux/files/usr';
|
|
190
|
+
const glibcLd = path.join(PREFIX, 'glibc', 'lib', 'ld-linux-aarch64.so.1');
|
|
191
|
+
const glibcLibs = path.join(PREFIX, 'glibc', 'lib');
|
|
192
|
+
|
|
193
|
+
// glibc動的リンカ経由でないとTermux(bionic)上ではglibc Nodeバイナリを実行できないため、
|
|
194
|
+
// ld-linux 経由で `node -v` を実行しバージョン文字列を確認する。
|
|
195
|
+
function verifyNodeBinary(binPath) {
|
|
196
|
+
// bin/copilot と同様、Termux由来の LD_PRELOAD (bionic向けexecフック) を明示的にクリアしないと
|
|
197
|
+
// glibc loaderがロードするlibc.soがinvalid ELF headerエラーになる(2026-07-06実機確認)。
|
|
198
|
+
const out = execFileSync(glibcLd, ['--library-path', glibcLibs, binPath, '-v'], {
|
|
199
|
+
encoding: 'utf8',
|
|
200
|
+
env: { ...process.env, LD_PRELOAD: '' },
|
|
201
|
+
}).trim();
|
|
202
|
+
return out === `v${version}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let needsDownload = true;
|
|
183
206
|
if (fs.existsSync(nodeBin)) {
|
|
184
|
-
|
|
207
|
+
try {
|
|
208
|
+
if (verifyNodeBinary(nodeBin)) {
|
|
209
|
+
needsDownload = false;
|
|
210
|
+
} else {
|
|
211
|
+
console.log(`glibc node at ${nodeVersionDir} failed version check, re-downloading...`);
|
|
212
|
+
fs.rmSync(nodeVersionDir, { recursive: true, force: true });
|
|
213
|
+
}
|
|
214
|
+
} catch (e) {
|
|
215
|
+
// 破損バイナリ・実行不可などpartial stateからの自己修復
|
|
216
|
+
console.log(`glibc node at ${nodeVersionDir} failed to execute (${e.message}), re-downloading...`);
|
|
217
|
+
fs.rmSync(nodeVersionDir, { recursive: true, force: true });
|
|
218
|
+
}
|
|
185
219
|
}
|
|
186
220
|
|
|
187
|
-
|
|
188
|
-
|
|
221
|
+
if (needsDownload) {
|
|
222
|
+
const tarballUrl = `https://nodejs.org/dist/v${version}/node-v${version}-linux-arm64.tar.gz`;
|
|
223
|
+
console.log(`Downloading Node.js v${version} (glibc)...`);
|
|
224
|
+
const tarball = await httpsGet(tarballUrl);
|
|
189
225
|
|
|
190
|
-
|
|
191
|
-
|
|
226
|
+
const actualSha256 = crypto.createHash('sha256').update(tarball).digest('hex');
|
|
227
|
+
if (actualSha256 !== expectedSha256) {
|
|
228
|
+
throw new Error(`Node.js integrity check failed: expected ${expectedSha256}, got ${actualSha256}`);
|
|
229
|
+
}
|
|
192
230
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
231
|
+
const stagingDir = `${nodeVersionDir}.staging.${process.pid}`;
|
|
232
|
+
const tarballPath = path.join(CACHE_DIR, `node-glibc-${version}.tgz`);
|
|
233
|
+
try {
|
|
234
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
235
|
+
fs.writeFileSync(tarballPath, tarball);
|
|
236
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
197
237
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
238
|
+
console.log('Extracting node binary...');
|
|
239
|
+
execFileSync('tar', [
|
|
240
|
+
'-xzf', tarballPath,
|
|
241
|
+
'-C', stagingDir,
|
|
242
|
+
'--strip-components=2',
|
|
243
|
+
`node-v${version}-linux-arm64/bin/node`,
|
|
244
|
+
]);
|
|
245
|
+
const stagingBin = path.join(stagingDir, 'node');
|
|
246
|
+
fs.chmodSync(stagingBin, 0o755);
|
|
247
|
+
|
|
248
|
+
// staging側で実行検証(glibcローダー経由)。失敗すれば例外がそのまま
|
|
249
|
+
// 呼び出し元に伝播し、stagingはcatchで削除、nodeVersionDirには一切触れない。
|
|
250
|
+
if (!verifyNodeBinary(stagingBin)) {
|
|
251
|
+
throw new Error(`Downloaded glibc node binary failed version check (expected v${version})`);
|
|
252
|
+
}
|
|
202
253
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
254
|
+
// 既存の nodeVersionDir が壊れた状態で残っている場合に備え、rename前にクリア
|
|
255
|
+
// (ここまで到達した時点で staging 側の検証は完了済みなので安全)
|
|
256
|
+
fs.rmSync(nodeVersionDir, { recursive: true, force: true });
|
|
257
|
+
fs.renameSync(stagingDir, nodeVersionDir);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
260
|
+
throw err;
|
|
261
|
+
} finally {
|
|
262
|
+
fs.rmSync(tarballPath, { force: true });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Migration: 1.0.68以前の glibc-node は実ディレクトリだった。
|
|
267
|
+
// 新バージョンの用意(既存流用 or 新規ダウンロード、どちらもverifyNodeBinary通過済み)が
|
|
268
|
+
// 完全に成功した後(ここまで到達した時点)でのみ、旧実ディレクトリを削除する。
|
|
269
|
+
let currentLinkStat = null;
|
|
270
|
+
try { currentLinkStat = fs.lstatSync(currentLink); } catch (_) {}
|
|
271
|
+
if (currentLinkStat && currentLinkStat.isDirectory() && !currentLinkStat.isSymbolicLink()) {
|
|
272
|
+
fs.rmSync(currentLink, { recursive: true, force: true });
|
|
213
273
|
}
|
|
274
|
+
|
|
275
|
+
const tmpLink = path.join(CACHE_DIR, `glibc-node.tmp.${process.pid}`);
|
|
276
|
+
try { fs.unlinkSync(tmpLink); } catch (_) {}
|
|
277
|
+
fs.symlinkSync(nodeVersionDir, tmpLink);
|
|
278
|
+
fs.renameSync(tmpLink, currentLink);
|
|
279
|
+
|
|
214
280
|
console.log('✓ glibc node ready');
|
|
215
281
|
}
|
|
216
282
|
|