kushi-agents 4.2.0 → 4.2.2
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/package.json +6 -1
- package/src/check-workiq.mjs +23 -3
- package/src/check-workiq.test.mjs +93 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kushi-agents",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "Install Kushi — multi-source project evidence agent with snapshot+stream capture across Email, Teams, OneNote, SharePoint, Meetings, CRM, ADO. WorkIQ-only for M365 sources (Graph / m365_* FORBIDDEN as fallbacks; user-paste is first-class). Host-agnostic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,6 +42,11 @@
|
|
|
42
42
|
"url": "https://github.com/gim-home/kushi/issues"
|
|
43
43
|
},
|
|
44
44
|
"license": "MIT",
|
|
45
|
+
"scripts": {
|
|
46
|
+
"test": "node --test src/check-workiq.test.mjs",
|
|
47
|
+
"smoke": "node scripts/smoke.mjs",
|
|
48
|
+
"prepublishOnly": "npm test && npm run smoke"
|
|
49
|
+
},
|
|
45
50
|
"publishConfig": {
|
|
46
51
|
"access": "public"
|
|
47
52
|
}
|
package/src/check-workiq.mjs
CHANGED
|
@@ -61,12 +61,32 @@ function findOnPath(name) {
|
|
|
61
61
|
const args = isWin ? [name] : ['-v', name];
|
|
62
62
|
const res = spawnSync(cmd, args, { encoding: 'utf-8', shell: !isWin });
|
|
63
63
|
if (res.status !== 0) return null;
|
|
64
|
-
const
|
|
65
|
-
|
|
64
|
+
const all = (res.stdout || '')
|
|
65
|
+
.split(/\r?\n/)
|
|
66
|
+
.map((s) => s.trim())
|
|
67
|
+
.filter(Boolean);
|
|
68
|
+
if (!isWin) return all[0] || null;
|
|
69
|
+
// Windows `where` may return multiple matches (e.g. npm shims produce both
|
|
70
|
+
// `workiq` (bash script, no extension) and `workiq.cmd`). Prefer
|
|
71
|
+
// executable extensions in priority order; fall back to the first match.
|
|
72
|
+
const priority = ['.cmd', '.exe', '.bat', '.ps1'];
|
|
73
|
+
for (const ext of priority) {
|
|
74
|
+
const hit = all.find((p) => p.toLowerCase().endsWith(ext));
|
|
75
|
+
if (hit) return hit;
|
|
76
|
+
}
|
|
77
|
+
return all[0] || null;
|
|
66
78
|
}
|
|
67
79
|
|
|
68
80
|
function runVersion(binPath) {
|
|
69
|
-
|
|
81
|
+
// Windows requires shell:true to spawn .cmd / .bat / shim files. The DEP0190
|
|
82
|
+
// warning is harmless here because we control both binPath (resolved via
|
|
83
|
+
// `where` / explicit --workiq-path) and args (literal ['--version']).
|
|
84
|
+
const isWin = process.platform === 'win32';
|
|
85
|
+
const res = spawnSync(binPath, ['--version'], {
|
|
86
|
+
encoding: 'utf-8',
|
|
87
|
+
timeout: 10_000,
|
|
88
|
+
shell: isWin,
|
|
89
|
+
});
|
|
70
90
|
if (res.status !== 0) {
|
|
71
91
|
return {
|
|
72
92
|
ok: false,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
|
|
9
|
+
import { checkWorkIQ } from './check-workiq.mjs';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const isWin = process.platform === 'win32';
|
|
13
|
+
|
|
14
|
+
function makeTempBin(name, body) {
|
|
15
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'kushi-test-'));
|
|
16
|
+
const full = path.join(dir, name);
|
|
17
|
+
fs.writeFileSync(full, body);
|
|
18
|
+
if (!isWin) fs.chmodSync(full, 0o755);
|
|
19
|
+
return { dir, full };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
test('checkWorkIQ: --workiq-path pointing at non-existent file returns path-invalid', () => {
|
|
23
|
+
const r = checkWorkIQ({ workiqPath: '/definitely/does/not/exist/workiq' });
|
|
24
|
+
assert.equal(r.ok, false);
|
|
25
|
+
assert.equal(r.reason, 'path-invalid');
|
|
26
|
+
assert.match(r.hint, /does not point at an existing file/i);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('checkWorkIQ: --workiq-path pointing at a runnable script returns ok with version', () => {
|
|
30
|
+
// Build a tiny fake that prints a version string and exits 0.
|
|
31
|
+
const name = isWin ? 'fake-workiq.cmd' : 'fake-workiq';
|
|
32
|
+
const body = isWin
|
|
33
|
+
? '@echo off\r\necho 9.9.9-fake\r\nexit /b 0\r\n'
|
|
34
|
+
: '#!/usr/bin/env bash\necho 9.9.9-fake\nexit 0\n';
|
|
35
|
+
const { full, dir } = makeTempBin(name, body);
|
|
36
|
+
try {
|
|
37
|
+
const r = checkWorkIQ({ workiqPath: full });
|
|
38
|
+
assert.equal(r.ok, true, `expected ok, got ${JSON.stringify(r)}`);
|
|
39
|
+
assert.equal(r.path, full);
|
|
40
|
+
assert.match(r.version, /9\.9\.9-fake/);
|
|
41
|
+
} finally {
|
|
42
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('checkWorkIQ: --workiq-path pointing at a failing binary returns not-executable', () => {
|
|
47
|
+
const name = isWin ? 'broken-workiq.cmd' : 'broken-workiq';
|
|
48
|
+
const body = isWin
|
|
49
|
+
? '@echo off\r\nexit /b 17\r\n'
|
|
50
|
+
: '#!/usr/bin/env bash\nexit 17\n';
|
|
51
|
+
const { full, dir } = makeTempBin(name, body);
|
|
52
|
+
try {
|
|
53
|
+
const r = checkWorkIQ({ workiqPath: full });
|
|
54
|
+
assert.equal(r.ok, false);
|
|
55
|
+
assert.equal(r.reason, 'not-executable');
|
|
56
|
+
assert.match(r.hint, /failed/i);
|
|
57
|
+
} finally {
|
|
58
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('checkWorkIQ: with no PATH match and no override returns not-found with platform hint', () => {
|
|
63
|
+
const savedPath = process.env.PATH;
|
|
64
|
+
const savedPathCap = process.env.Path;
|
|
65
|
+
const savedHome = process.env.HOME;
|
|
66
|
+
const savedProfile = process.env.USERPROFILE;
|
|
67
|
+
try {
|
|
68
|
+
const fakeRoot = path.join(os.tmpdir(), `kushi-empty-${Date.now()}`);
|
|
69
|
+
process.env.PATH = fakeRoot;
|
|
70
|
+
if (isWin) process.env.Path = fakeRoot;
|
|
71
|
+
process.env.HOME = fakeRoot;
|
|
72
|
+
if (isWin) process.env.USERPROFILE = fakeRoot;
|
|
73
|
+
|
|
74
|
+
const r = checkWorkIQ();
|
|
75
|
+
assert.equal(r.ok, false);
|
|
76
|
+
assert.equal(r.reason, 'not-found');
|
|
77
|
+
if (isWin) {
|
|
78
|
+
assert.match(r.hint, /winget install Microsoft\.WorkIQ/);
|
|
79
|
+
} else if (process.platform === 'darwin') {
|
|
80
|
+
assert.match(r.hint, /brew install --cask microsoft-workiq/);
|
|
81
|
+
} else {
|
|
82
|
+
assert.match(r.hint, /install-workiq/);
|
|
83
|
+
}
|
|
84
|
+
} finally {
|
|
85
|
+
process.env.PATH = savedPath;
|
|
86
|
+
if (savedPathCap !== undefined) process.env.Path = savedPathCap;
|
|
87
|
+
else delete process.env.Path;
|
|
88
|
+
if (savedHome !== undefined) process.env.HOME = savedHome;
|
|
89
|
+
else delete process.env.HOME;
|
|
90
|
+
if (savedProfile !== undefined) process.env.USERPROFILE = savedProfile;
|
|
91
|
+
else delete process.env.USERPROFILE;
|
|
92
|
+
}
|
|
93
|
+
});
|