@autodevjapan/godd-mcp-alpha 2.9.4 → 2.10.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/README.md +49 -3
- package/assets/godd-mcp-alpha-icon.png +0 -0
- package/assets/native/godd-native-aarch64-apple-darwin +0 -0
- package/assets/native/godd-native-aarch64-apple-darwin.sha256 +1 -1
- package/assets/native/godd-native-x86_64-apple-darwin +0 -0
- package/assets/native/godd-native-x86_64-apple-darwin.sha256 +1 -1
- package/assets/native/godd-native-x86_64-pc-windows-msvc.exe +0 -0
- package/assets/native/godd-native-x86_64-pc-windows-msvc.exe.sha256 +1 -1
- package/assets/native/godd-native-x86_64-unknown-linux-musl +0 -0
- package/assets/native/godd-native-x86_64-unknown-linux-musl.sha256 +1 -1
- package/dist/godd.cjs +212 -206
- package/native-assets.json +12 -12
- package/package.json +3 -1
- package/scripts/atomic-binary-install.js +679 -0
- package/scripts/native-assets.js +3 -2
- package/scripts/postinstall-godd-tk.js +149 -82
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* レビューを通った manifest の digest である(Issue #1474)。
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { chmodSync, existsSync, mkdirSync,
|
|
16
|
-
import { join, resolve } from "node:path";
|
|
15
|
+
import { chmodSync, existsSync, mkdirSync, renameSync } from "node:fs";
|
|
16
|
+
import { basename, join, resolve } from "node:path";
|
|
17
17
|
import { homedir, platform, arch } from "node:os";
|
|
18
18
|
import { execFileSync } from "node:child_process";
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
@@ -23,6 +23,16 @@ import {
|
|
|
23
23
|
getGoddTkTarget,
|
|
24
24
|
verifyGoddTkArtifact,
|
|
25
25
|
} from "./godd-tk-assets.js";
|
|
26
|
+
import {
|
|
27
|
+
AtomicInstallCleanupError,
|
|
28
|
+
AtomicInstallIntegrityError,
|
|
29
|
+
AtomicInstallRollbackError,
|
|
30
|
+
beginAtomicBinaryInstall,
|
|
31
|
+
finishAtomicBinaryInstall,
|
|
32
|
+
publishAtomicBinary,
|
|
33
|
+
recoverAtomicBinaryInstall,
|
|
34
|
+
recoverInterruptedAtomicFinish,
|
|
35
|
+
} from "./atomic-binary-install.js";
|
|
26
36
|
|
|
27
37
|
const REPO = "AutoDevJapan/GoDD-MCP-Alpha";
|
|
28
38
|
const BINARY_NAME = "godd-tk";
|
|
@@ -73,8 +83,8 @@ function defaultDownload(tag, artifact, directory) {
|
|
|
73
83
|
* - "installed" / "already-installed" … 検証済みバイナリが所定の場所にある
|
|
74
84
|
* - "skipped" … そもそも取得しない(exit 0、install 継続)。
|
|
75
85
|
* 既存の未検証バイナリが居た場合は必ず隔離済み
|
|
76
|
-
* - "integrity-failure" …
|
|
77
|
-
*
|
|
86
|
+
* - "integrity-failure" … integrity / atomic recovery 境界の失敗。
|
|
87
|
+
* rollback成功時もexit 65でfail-closed
|
|
78
88
|
*/
|
|
79
89
|
export function installGoddTk({
|
|
80
90
|
currentPlatform = platform(),
|
|
@@ -85,13 +95,13 @@ export function installGoddTk({
|
|
|
85
95
|
verify = verifyGoddTkArtifact,
|
|
86
96
|
exists = existsSync,
|
|
87
97
|
makeDir = (path) => mkdirSync(path, { recursive: true }),
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
makeTempDir,
|
|
99
|
+
removeTree,
|
|
100
|
+
publishOperations,
|
|
101
|
+
isProcessAlive,
|
|
102
|
+
recoverInterruptedFinish =
|
|
103
|
+
process.env.GODD_RECOVER_GODD_TK_INSTALL === "1",
|
|
93
104
|
move = renameSync,
|
|
94
|
-
removeFile = unlinkSync,
|
|
95
105
|
chmod = chmodSync,
|
|
96
106
|
now = () => Date.now(),
|
|
97
107
|
log = console.log,
|
|
@@ -105,19 +115,86 @@ export function installGoddTk({
|
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
const binaryPath = getBinaryPath(installDir, currentPlatform);
|
|
118
|
+
let transaction;
|
|
119
|
+
try {
|
|
120
|
+
makeDir(installDir);
|
|
121
|
+
if (recoverInterruptedFinish) {
|
|
122
|
+
const recovery = recoverInterruptedAtomicFinish({
|
|
123
|
+
binaryPath,
|
|
124
|
+
...(isProcessAlive ? { isProcessAlive } : {}),
|
|
125
|
+
});
|
|
126
|
+
if (recovery.status === "blocked") {
|
|
127
|
+
logError(
|
|
128
|
+
`[godd-tk] SECURITY: explicit install recovery was rejected (${recovery.reason}).`,
|
|
129
|
+
);
|
|
130
|
+
return { status: "integrity-failure", reason: recovery.reason };
|
|
131
|
+
}
|
|
132
|
+
if (recovery.status === "recovered") {
|
|
133
|
+
log(`[godd-tk] Recovered an interrupted install owner release.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
transaction = beginAtomicBinaryInstall({
|
|
137
|
+
installDir,
|
|
138
|
+
binaryPath,
|
|
139
|
+
...(makeTempDir ? { makeTempDir: (prefix) => makeTempDir(prefix) } : {}),
|
|
140
|
+
...(isProcessAlive ? { isProcessAlive } : {}),
|
|
141
|
+
});
|
|
142
|
+
} catch {
|
|
143
|
+
logError(
|
|
144
|
+
`[godd-tk] SECURITY: could not create the atomic install transaction.`,
|
|
145
|
+
);
|
|
146
|
+
return { status: "integrity-failure", reason: "transaction-unavailable" };
|
|
147
|
+
}
|
|
148
|
+
if (transaction.status === "busy") {
|
|
149
|
+
log(`[godd-tk] Another verified install transaction is active; skipping this attempt.`);
|
|
150
|
+
return { status: "skipped", reason: "install-busy" };
|
|
151
|
+
}
|
|
152
|
+
if (transaction.status === "blocked") {
|
|
153
|
+
logError(`[godd-tk] SECURITY: install lock could not be validated (${transaction.reason}).`);
|
|
154
|
+
return { status: "integrity-failure", reason: transaction.reason };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let result;
|
|
158
|
+
let retainTransaction = false;
|
|
159
|
+
try {
|
|
160
|
+
recoverAtomicBinaryInstall(transaction, (path) => verify(path, target));
|
|
161
|
+
} catch {
|
|
162
|
+
logError(`[godd-tk] SECURITY: interrupted install recovery failed.`);
|
|
163
|
+
result = { status: "integrity-failure", reason: "recovery-failed" };
|
|
164
|
+
}
|
|
165
|
+
if (!result) {
|
|
166
|
+
try {
|
|
167
|
+
result = runLockedInstall();
|
|
168
|
+
} catch {
|
|
169
|
+
logError(`[godd-tk] Unexpected installer error.`);
|
|
170
|
+
result = { status: "integrity-failure", reason: "unexpected-error" };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (retainTransaction) return result;
|
|
175
|
+
const cleanup = finishAtomicBinaryInstall(
|
|
176
|
+
transaction,
|
|
177
|
+
removeTree ? { removeTree } : undefined,
|
|
178
|
+
);
|
|
179
|
+
if (!cleanup.ok) {
|
|
180
|
+
logError(`[godd-tk] SECURITY: install transaction cleanup failed.`);
|
|
181
|
+
return { status: "integrity-failure", reason: cleanup.reason };
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
|
|
185
|
+
function runLockedInstall() {
|
|
108
186
|
let unverifiedExisting = false;
|
|
109
187
|
if (exists(binaryPath)) {
|
|
110
188
|
try {
|
|
111
189
|
verify(binaryPath, target);
|
|
112
|
-
log(`[godd-tk] Already installed and verified
|
|
190
|
+
log(`[godd-tk] Already installed and verified (${GODD_TK_RELEASE_TAG}).`);
|
|
113
191
|
return { status: "already-installed", binaryPath };
|
|
114
|
-
} catch
|
|
192
|
+
} catch {
|
|
115
193
|
// 既存バイナリが pinned release と違う(自前ビルド・旧バージョン・改竄)。
|
|
116
194
|
// 「検証済み」と偽らず、pinned release での置き換えを試みる。
|
|
117
195
|
unverifiedExisting = true;
|
|
118
196
|
logError(
|
|
119
|
-
`[godd-tk] Existing binary does not match the pinned release ${GODD_TK_RELEASE_TAG}
|
|
120
|
-
`${error instanceof Error ? error.message : error}`,
|
|
197
|
+
`[godd-tk] Existing binary does not match the pinned release ${GODD_TK_RELEASE_TAG}.`,
|
|
121
198
|
);
|
|
122
199
|
log(`[godd-tk] Reinstalling the pinned, digest-verified release.`);
|
|
123
200
|
}
|
|
@@ -142,7 +219,7 @@ export function installGoddTk({
|
|
|
142
219
|
for (let suffix = 1; exists(quarantinePath); suffix += 1) {
|
|
143
220
|
if (suffix > 1000) {
|
|
144
221
|
logError(
|
|
145
|
-
`[godd-tk] SECURITY:
|
|
222
|
+
`[godd-tk] SECURITY: quarantine namespace is exhausted.`,
|
|
146
223
|
);
|
|
147
224
|
return { status: "integrity-failure", reason: "unverified-existing-binary" };
|
|
148
225
|
}
|
|
@@ -150,35 +227,26 @@ export function installGoddTk({
|
|
|
150
227
|
}
|
|
151
228
|
try {
|
|
152
229
|
move(binaryPath, quarantinePath);
|
|
153
|
-
} catch
|
|
154
|
-
logError(
|
|
155
|
-
`[godd-tk] SECURITY: could not quarantine the unverified binary at ${binaryPath} — ` +
|
|
156
|
-
`${error instanceof Error ? error.message : error}`,
|
|
157
|
-
);
|
|
230
|
+
} catch {
|
|
231
|
+
logError(`[godd-tk] SECURITY: could not quarantine the unverified binary.`);
|
|
158
232
|
return { status: "integrity-failure", reason: "unverified-existing-binary" };
|
|
159
233
|
}
|
|
160
234
|
logError(
|
|
161
|
-
`[godd-tk] Quarantined the unverified binary
|
|
235
|
+
`[godd-tk] Quarantined the unverified binary as ${basename(quarantinePath)}; ` +
|
|
162
236
|
`it is no longer on the install path. Delete it once you have confirmed its origin.`,
|
|
163
237
|
);
|
|
164
238
|
return { status: "skipped", reason };
|
|
165
239
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
);
|
|
177
|
-
return finishWithoutInstall("unexpected-error");
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function runDownloadAndInstall() {
|
|
181
|
-
if (!isGhAvailable()) {
|
|
240
|
+
let ghAvailable;
|
|
241
|
+
try {
|
|
242
|
+
ghAvailable = isGhAvailable();
|
|
243
|
+
} catch {
|
|
244
|
+
logError(
|
|
245
|
+
`[godd-tk] Unexpected installer error.`,
|
|
246
|
+
);
|
|
247
|
+
return finishWithoutInstall("unexpected-error");
|
|
248
|
+
}
|
|
249
|
+
if (!ghAvailable) {
|
|
182
250
|
log(`[godd-tk] GitHub CLI (gh) not found — required for private repo download.`);
|
|
183
251
|
log(`[godd-tk] Install gh: https://cli.github.com/ then run this script again.`);
|
|
184
252
|
log(`[godd-tk] Or build from source: cd godd-mcp-alpha/godd-tk && cargo build --release`);
|
|
@@ -186,30 +254,16 @@ export function installGoddTk({
|
|
|
186
254
|
}
|
|
187
255
|
|
|
188
256
|
log(`[godd-tk] Downloading ${target.artifact} (${GODD_TK_RELEASE_TAG}) via gh CLI...`);
|
|
189
|
-
|
|
190
|
-
let tmpDir;
|
|
191
|
-
try {
|
|
192
|
-
makeDir(installDir);
|
|
193
|
-
tmpDir = makeTempDir(installDir);
|
|
194
|
-
} catch (error) {
|
|
195
|
-
log(
|
|
196
|
-
`[godd-tk] Could not prepare a staging directory under ${installDir}: ` +
|
|
197
|
-
`${error instanceof Error ? error.message : error}`,
|
|
198
|
-
);
|
|
199
|
-
return finishWithoutInstall("staging-unavailable");
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const downloadedPath = join(tmpDir, target.artifact);
|
|
203
|
-
try {
|
|
257
|
+
const downloadedPath = join(transaction.tempDir, target.artifact);
|
|
204
258
|
try {
|
|
205
|
-
download(GODD_TK_RELEASE_TAG, target.artifact,
|
|
206
|
-
} catch
|
|
207
|
-
log(`[godd-tk] Download failed
|
|
259
|
+
download(GODD_TK_RELEASE_TAG, target.artifact, transaction.tempDir);
|
|
260
|
+
} catch {
|
|
261
|
+
log(`[godd-tk] Download failed.`);
|
|
208
262
|
log(`[godd-tk] Build from source: cd godd-mcp-alpha/godd-tk && cargo build --release`);
|
|
209
263
|
return finishWithoutInstall("download-failed");
|
|
210
264
|
}
|
|
211
265
|
if (!exists(downloadedPath)) {
|
|
212
|
-
log(`[godd-tk] Downloaded
|
|
266
|
+
log(`[godd-tk] Downloaded artifact is missing (${target.artifact}).`);
|
|
213
267
|
log(`[godd-tk] Build from source: cd godd-mcp-alpha/godd-tk && cargo build --release`);
|
|
214
268
|
return finishWithoutInstall("download-failed");
|
|
215
269
|
}
|
|
@@ -217,56 +271,69 @@ export function installGoddTk({
|
|
|
217
271
|
// ここから先は「取得できた」ケース。digest 不一致は skip ではなく失敗。
|
|
218
272
|
try {
|
|
219
273
|
verify(downloadedPath, target);
|
|
220
|
-
} catch
|
|
274
|
+
} catch {
|
|
221
275
|
logError(
|
|
222
|
-
`[godd-tk] SECURITY: downloaded artifact failed integrity verification
|
|
223
|
-
`${error instanceof Error ? error.message : error}`,
|
|
276
|
+
`[godd-tk] SECURITY: downloaded artifact failed integrity verification.`,
|
|
224
277
|
);
|
|
225
278
|
logError(
|
|
226
279
|
`[godd-tk] The artifact published at ${GODD_TK_RELEASE_TAG} does not match the digest ` +
|
|
227
280
|
`committed in godd-mcp-alpha/godd-tk-assets.json. Installation is aborted and nothing was installed.`,
|
|
228
281
|
);
|
|
282
|
+
if (unverifiedExisting && exists(binaryPath)) {
|
|
283
|
+
const quarantine = finishWithoutInstall("digest-mismatch");
|
|
284
|
+
if (quarantine.status === "integrity-failure") return quarantine;
|
|
285
|
+
}
|
|
229
286
|
return { status: "integrity-failure", reason: "digest-mismatch" };
|
|
230
287
|
}
|
|
231
288
|
|
|
232
289
|
try {
|
|
233
|
-
if (
|
|
234
|
-
|
|
290
|
+
if (currentPlatform !== "win32") chmod(downloadedPath, 0o755);
|
|
291
|
+
publishAtomicBinary(
|
|
292
|
+
transaction,
|
|
293
|
+
downloadedPath,
|
|
294
|
+
(path) => verify(path, target),
|
|
295
|
+
undefined,
|
|
296
|
+
publishOperations,
|
|
297
|
+
);
|
|
235
298
|
} catch (error) {
|
|
236
|
-
|
|
299
|
+
if (error instanceof AtomicInstallCleanupError) {
|
|
300
|
+
retainTransaction = true;
|
|
301
|
+
logError(
|
|
302
|
+
`[godd-tk] SECURITY: verified publish cleanup is pending; transaction retained.`,
|
|
303
|
+
);
|
|
304
|
+
return {
|
|
305
|
+
status: "integrity-failure",
|
|
306
|
+
reason: "publish-cleanup-pending",
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
if (error instanceof AtomicInstallIntegrityError) {
|
|
310
|
+
logError(`[godd-tk] SECURITY: published artifact failed integrity verification.`);
|
|
311
|
+
return {
|
|
312
|
+
status: "integrity-failure",
|
|
313
|
+
reason: "post-publish-integrity-failed",
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
if (error instanceof AtomicInstallRollbackError) {
|
|
317
|
+
retainTransaction = true;
|
|
318
|
+
logError(`[godd-tk] SECURITY: publish rollback failed; transaction retained.`);
|
|
319
|
+
return { status: "integrity-failure", reason: "rollback-failed" };
|
|
320
|
+
}
|
|
321
|
+
log(`[godd-tk] Installation failed.`);
|
|
237
322
|
log(`[godd-tk] Build from source: cd godd-mcp-alpha/godd-tk && cargo build --release`);
|
|
238
323
|
return finishWithoutInstall("install-failed");
|
|
239
324
|
}
|
|
240
325
|
// ここまで来た時点で、置かれているのは検証済み bytes だけ。
|
|
241
326
|
unverifiedExisting = false;
|
|
242
327
|
|
|
243
|
-
|
|
244
|
-
try {
|
|
245
|
-
chmod(binaryPath, 0o755);
|
|
246
|
-
} catch (error) {
|
|
247
|
-
logError(
|
|
248
|
-
`[godd-tk] Installed verified bytes but could not set the executable bit on ${binaryPath}: ` +
|
|
249
|
-
`${error instanceof Error ? error.message : error}`,
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
log(`[godd-tk] Installed and verified (sha256 ${target.sha256}): ${binaryPath}`);
|
|
328
|
+
log(`[godd-tk] Installed and verified (${GODD_TK_RELEASE_TAG}).`);
|
|
255
329
|
log(`[godd-tk] Run 'godd-tk init --agent cursor' to set up hooks`);
|
|
256
330
|
if (currentPlatform === "win32") {
|
|
257
331
|
const pathDirs = (process.env.PATH || "").toLowerCase().split(";");
|
|
258
332
|
if (!pathDirs.includes(installDir.toLowerCase())) {
|
|
259
|
-
log(`[godd-tk] Add
|
|
333
|
+
log(`[godd-tk] Add the godd-tk install directory to PATH for global access`);
|
|
260
334
|
}
|
|
261
335
|
}
|
|
262
336
|
return { status: "installed", binaryPath };
|
|
263
|
-
} finally {
|
|
264
|
-
try {
|
|
265
|
-
removeTree(tmpDir);
|
|
266
|
-
} catch {
|
|
267
|
-
// best effort
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
337
|
}
|
|
271
338
|
}
|
|
272
339
|
|