agent-takkub 1.0.84 → 1.0.86
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/dist/{agent_takkub-1.0.84-py3-none-any.whl → agent_takkub-1.0.86-py3-none-any.whl} +0 -0
- package/npm/bin/agent-takkub.js +14 -1
- package/npm/bin/takkub.js +17 -1
- package/npm/scripts/lib.js +75 -1
- package/npm/scripts/postinstall.js +7 -4
- package/npm/scripts/preflight-publish.js +62 -0
- package/package.json +5 -2
|
Binary file
|
package/npm/bin/agent-takkub.js
CHANGED
|
@@ -2,12 +2,25 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
// Launches the cockpit GUI from the provisioned (isolated) venv.
|
|
4
4
|
const { spawnSync } = require('child_process');
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
venvPythonIfExists,
|
|
7
|
+
pythonLooksExecutable,
|
|
8
|
+
brokenInterpreterMessage,
|
|
9
|
+
agentTakkubHome,
|
|
10
|
+
} = require('../scripts/lib');
|
|
6
11
|
|
|
7
12
|
const py = venvPythonIfExists();
|
|
8
13
|
if (!py) {
|
|
9
14
|
console.error('agent-takkub is not provisioned. Run: npm install -g agent-takkub');
|
|
10
15
|
process.exit(1);
|
|
11
16
|
}
|
|
17
|
+
if (!pythonLooksExecutable(py)) {
|
|
18
|
+
console.error(brokenInterpreterMessage(py, agentTakkubHome()));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
12
21
|
const r = spawnSync(py, ['-m', 'agent_takkub', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
22
|
+
if (r.error) {
|
|
23
|
+
console.error(`[agent-takkub] failed to launch the cockpit interpreter: ${r.error.message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
13
26
|
process.exit(r.status == null ? 1 : r.status);
|
package/npm/bin/takkub.js
CHANGED
|
@@ -2,12 +2,28 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
// The `takkub` CLI, run from the provisioned (isolated) venv.
|
|
4
4
|
const { spawnSync } = require('child_process');
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
venvPythonIfExists,
|
|
7
|
+
pythonLooksExecutable,
|
|
8
|
+
brokenInterpreterMessage,
|
|
9
|
+
agentTakkubHome,
|
|
10
|
+
} = require('../scripts/lib');
|
|
6
11
|
|
|
7
12
|
const py = venvPythonIfExists();
|
|
8
13
|
if (!py) {
|
|
9
14
|
console.error('takkub is not provisioned. Run: npm install -g agent-takkub');
|
|
10
15
|
process.exit(1);
|
|
11
16
|
}
|
|
17
|
+
if (!pythonLooksExecutable(py)) {
|
|
18
|
+
console.error(brokenInterpreterMessage(py, agentTakkubHome()));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
12
21
|
const r = spawnSync(py, ['-m', 'agent_takkub.cli', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
22
|
+
if (r.error) {
|
|
23
|
+
// spawnSync itself failed (e.g. the interpreter passed validation above
|
|
24
|
+
// but the OS still refused to launch it) — never let this read as a
|
|
25
|
+
// quiet success (#341).
|
|
26
|
+
console.error(`[takkub] failed to launch the cockpit interpreter: ${r.error.message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
13
29
|
process.exit(r.status == null ? 1 : r.status);
|
package/npm/scripts/lib.js
CHANGED
|
@@ -31,6 +31,71 @@ function venvPythonIfExists() {
|
|
|
31
31
|
return fs.existsSync(p) ? p : null;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Pick the wheel matching an exact version out of a dist/ dir — never the
|
|
35
|
+
// lexically-last filename. A string sort puts "1.0.9" after "1.0.10" (#340),
|
|
36
|
+
// and dist/ is gitignored so a stale wheel from a previous release can sit
|
|
37
|
+
// there unnoticed. `version` is the SemVer string as read from package.json;
|
|
38
|
+
// the filename convention (setuptools normalizes the project name's `-` to
|
|
39
|
+
// `_`) is `agent_takkub-{version}-py3-none-any.whl`.
|
|
40
|
+
function findWheelForVersion(distDir, version) {
|
|
41
|
+
if (!fs.existsSync(distDir)) return null;
|
|
42
|
+
const prefix = `agent_takkub-${version}-`;
|
|
43
|
+
const match = fs.readdirSync(distDir).find((f) => f.endsWith('.whl') && f.startsWith(prefix));
|
|
44
|
+
return match ? path.join(distDir, match) : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// A venv python that EXISTS but was overwritten/truncated by something
|
|
48
|
+
// outside agent-takkub (#341 — a stray tool once clobbered a live
|
|
49
|
+
// ~/.agent-takkub/venv/Scripts/python.exe with a 29-byte text file) must
|
|
50
|
+
// never be handed to spawnSync: every command built on top of it then fails
|
|
51
|
+
// to even start, with EMPTY stdout/stderr and no diagnostic — the cockpit
|
|
52
|
+
// "dies silently". This check is deliberately static (stat + a couple of
|
|
53
|
+
// header bytes, no attempt to execute the file) so a broken interpreter is
|
|
54
|
+
// never spawned at all — spawning it is exactly the risky step that can
|
|
55
|
+
// leave the file locked/hard to overwrite during recovery.
|
|
56
|
+
const MIN_PYTHON_EXE_BYTES = 40 * 1024; // a real venv python.exe/python is comfortably >90KB
|
|
57
|
+
|
|
58
|
+
function pythonLooksExecutable(py) {
|
|
59
|
+
let stat;
|
|
60
|
+
try {
|
|
61
|
+
stat = fs.statSync(py);
|
|
62
|
+
} catch (_e) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (!stat.isFile() || stat.size < MIN_PYTHON_EXE_BYTES) return false;
|
|
66
|
+
if (process.platform === 'win32') {
|
|
67
|
+
// Windows PE executables always start with the 'MZ' DOS-header magic.
|
|
68
|
+
let fd;
|
|
69
|
+
try {
|
|
70
|
+
fd = fs.openSync(py, 'r');
|
|
71
|
+
const buf = Buffer.alloc(2);
|
|
72
|
+
fs.readSync(fd, buf, 0, 2, 0);
|
|
73
|
+
return buf[0] === 0x4d && buf[1] === 0x5a; // 'M', 'Z'
|
|
74
|
+
} catch (_e) {
|
|
75
|
+
return false;
|
|
76
|
+
} finally {
|
|
77
|
+
if (fd !== undefined) fs.closeSync(fd);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function brokenInterpreterMessage(py, home) {
|
|
84
|
+
const venvDirPath = path.join(home, 'venv');
|
|
85
|
+
return (
|
|
86
|
+
`[agent-takkub] the cockpit interpreter looks broken (exists but is not a real executable):\n` +
|
|
87
|
+
` ${py}\n` +
|
|
88
|
+
' Something outside agent-takkub overwrote or truncated this file — the cockpit\n' +
|
|
89
|
+
' did not do this to itself. Fix:\n' +
|
|
90
|
+
' 1) close every running cockpit/takkub window and process for this install\n' +
|
|
91
|
+
' 2) run: npm install -g agent-takkub --force (reprovisions the venv in place)\n' +
|
|
92
|
+
` 3) if step 2 still can't overwrite it, delete this folder by hand:\n` +
|
|
93
|
+
` ${venvDirPath}\n` +
|
|
94
|
+
' then re-run npm install -g agent-takkub\n' +
|
|
95
|
+
' `takkub doctor` (once the interpreter itself works again) also checks this.'
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
34
99
|
// Windowless python for GUI launchers (Windows: pythonw.exe = no console
|
|
35
100
|
// window pops up behind the cockpit). macOS has no separate pythonw.
|
|
36
101
|
function venvPythonw() {
|
|
@@ -40,4 +105,13 @@ function venvPythonw() {
|
|
|
40
105
|
: path.join(dir, 'bin', 'python');
|
|
41
106
|
}
|
|
42
107
|
|
|
43
|
-
module.exports = {
|
|
108
|
+
module.exports = {
|
|
109
|
+
agentTakkubHome,
|
|
110
|
+
venvDir,
|
|
111
|
+
venvPython,
|
|
112
|
+
venvPythonIfExists,
|
|
113
|
+
venvPythonw,
|
|
114
|
+
findWheelForVersion,
|
|
115
|
+
pythonLooksExecutable,
|
|
116
|
+
brokenInterpreterMessage,
|
|
117
|
+
};
|
|
@@ -15,15 +15,18 @@
|
|
|
15
15
|
const { spawnSync } = require('child_process');
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
|
-
const { agentTakkubHome, venvDir, venvPython } = require('./lib');
|
|
18
|
+
const { agentTakkubHome, venvDir, venvPython, findWheelForVersion } = require('./lib');
|
|
19
19
|
const preflight = require('./preflight');
|
|
20
20
|
|
|
21
|
+
// The wheel matching THIS install's declared version (#340) — not the
|
|
22
|
+
// lexically-last filename in dist/. A string sort put "1.0.9" after
|
|
23
|
+
// "1.0.10", and a stale wheel from a previous release (dist/ is gitignored,
|
|
24
|
+
// so nothing flags it) got installed silently under a newer version number.
|
|
21
25
|
function findWheel() {
|
|
22
26
|
// Shipped alongside this package under dist/ (see package.json `files`).
|
|
23
27
|
const dist = path.join(__dirname, '..', '..', 'dist');
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return whl.length ? path.join(dist, whl[whl.length - 1]) : null;
|
|
28
|
+
const version = require('../../package.json').version;
|
|
29
|
+
return findWheelForVersion(dist, version);
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
function run(cmd, args) {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// `npm publish` preflight guard (#340) — wired as `prepublishOnly` so it runs
|
|
3
|
+
// automatically. dist/ is gitignored, so a wheel left over from a previous
|
|
4
|
+
// `takkub release` sits there unnoticed; without this check `npm publish`
|
|
5
|
+
// happily tars up that stale wheel under the NEW version number (v1.0.84
|
|
6
|
+
// shipped v1.0.82's code, see issue #340). Dies loudly instead of warning —
|
|
7
|
+
// a silent old-code publish is worse than a blocked one.
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { findWheelForVersion } = require('./lib');
|
|
12
|
+
|
|
13
|
+
function checkDistWheelMatchesVersion(distDir, version) {
|
|
14
|
+
if (!fs.existsSync(distDir)) {
|
|
15
|
+
return {
|
|
16
|
+
ok: false,
|
|
17
|
+
reason: `dist/ not found — build first: python -m build --wheel`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const whls = fs.readdirSync(distDir).filter((f) => f.endsWith('.whl'));
|
|
21
|
+
if (whls.length === 0) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
reason: `dist/ has no wheel — build first: python -m build --wheel`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (whls.length > 1) {
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
reason:
|
|
31
|
+
`dist/ has ${whls.length} wheels (expected exactly one): ${whls.join(', ')} — ` +
|
|
32
|
+
`rebuild clean: rm -f dist/*.whl && python -m build --wheel`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const wheel = findWheelForVersion(distDir, version);
|
|
36
|
+
if (!wheel) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
reason:
|
|
40
|
+
`dist/${whls[0]} does not match package.json version ${version} — ` +
|
|
41
|
+
`rebuild: rm -f dist/*.whl && python -m build --wheel`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { ok: true, wheel: path.basename(wheel) };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function main() {
|
|
48
|
+
const pkg = require('../../package.json');
|
|
49
|
+
const dist = path.join(__dirname, '..', '..', 'dist');
|
|
50
|
+
const result = checkDistWheelMatchesVersion(dist, pkg.version);
|
|
51
|
+
if (!result.ok) {
|
|
52
|
+
console.error(`[agent-takkub] publish preflight FAILED: ${result.reason}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
console.log(`[agent-takkub] publish preflight OK — dist/${result.wheel} matches package.json ${pkg.version}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { checkDistWheelMatchesVersion };
|
|
59
|
+
|
|
60
|
+
if (require.main === module) {
|
|
61
|
+
main();
|
|
62
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-takkub",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.86",
|
|
4
4
|
"description": "Desktop cockpit for orchestrating a team of Claude Code agents (Windows + macOS). Install globally: npm i -g agent-takkub",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -26,10 +26,13 @@
|
|
|
26
26
|
"takkub": "npm/bin/takkub.js"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
|
-
"postinstall": "node npm/scripts/postinstall.js"
|
|
29
|
+
"postinstall": "node npm/scripts/postinstall.js",
|
|
30
|
+
"prepublishOnly": "node npm/scripts/preflight-publish.js",
|
|
31
|
+
"test": "node --test npm/scripts/*.test.js"
|
|
30
32
|
},
|
|
31
33
|
"files": [
|
|
32
34
|
"npm/",
|
|
35
|
+
"!npm/scripts/*.test.js",
|
|
33
36
|
"dist/*.whl",
|
|
34
37
|
"assets/icon.ico",
|
|
35
38
|
"assets/icon.png",
|