- Keep Kimi global installs as a Phase 1 no-op skeleton - Assert no unconverted Kimi skills, agents, hooks, or payload files are written
177 lines
6.3 KiB
JavaScript
177 lines
6.3 KiB
JavaScript
'use strict';
|
|
|
|
process.env.GSD_TEST_MODE = '1';
|
|
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { spawnSync } = require('node:child_process');
|
|
|
|
const { cleanup } = require('./helpers.cjs');
|
|
const { installerEnv } = require('./helpers/install-shared.cjs');
|
|
|
|
const ROOT = path.join(__dirname, '..');
|
|
const INSTALL_SCRIPT = path.join(ROOT, 'bin', 'install.js');
|
|
|
|
const {
|
|
getGlobalConfigDir,
|
|
getGlobalSkillsBase,
|
|
getGlobalSkillDir,
|
|
} = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'runtime-homes.cjs'));
|
|
const {
|
|
resolveRuntimeArtifactLayout,
|
|
} = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'runtime-artifact-layout.cjs'));
|
|
const {
|
|
getGlobalDir,
|
|
getConfigDirFromHome,
|
|
} = require('../bin/install.js');
|
|
|
|
function withEnv(updates, fn) {
|
|
const saved = {};
|
|
for (const key of Object.keys(updates)) {
|
|
saved[key] = process.env[key];
|
|
const value = updates[key];
|
|
if (value === undefined) delete process.env[key];
|
|
else process.env[key] = value;
|
|
}
|
|
try {
|
|
return fn();
|
|
} finally {
|
|
for (const key of Object.keys(updates)) {
|
|
if (saved[key] === undefined) delete process.env[key];
|
|
else process.env[key] = saved[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
describe('Kimi runtime homes', () => {
|
|
test('canonical global skills base is ~/.config/agents/skills, not ~/.kimi/skills', () => {
|
|
withEnv({ KIMI_CONFIG_DIR: undefined, XDG_CONFIG_HOME: undefined }, () => {
|
|
assert.strictEqual(
|
|
getGlobalConfigDir('kimi'),
|
|
path.join(os.homedir(), '.config', 'agents'),
|
|
);
|
|
assert.strictEqual(
|
|
getGlobalSkillsBase('kimi'),
|
|
path.join(os.homedir(), '.config', 'agents', 'skills'),
|
|
);
|
|
assert.strictEqual(
|
|
getGlobalSkillDir('kimi', 'gsd-help'),
|
|
path.join(os.homedir(), '.config', 'agents', 'skills', 'gsd-help'),
|
|
);
|
|
assert.notStrictEqual(
|
|
getGlobalSkillsBase('kimi'),
|
|
path.join(os.homedir(), '.kimi', 'skills'),
|
|
);
|
|
});
|
|
});
|
|
|
|
test('KIMI_CONFIG_DIR overrides the Kimi generic agents config root', () => {
|
|
withEnv({ KIMI_CONFIG_DIR: '/tmp/custom-kimi-agents', XDG_CONFIG_HOME: undefined }, () => {
|
|
assert.strictEqual(getGlobalConfigDir('kimi'), '/tmp/custom-kimi-agents');
|
|
assert.strictEqual(
|
|
getGlobalSkillsBase('kimi'),
|
|
path.join('/tmp/custom-kimi-agents', 'skills'),
|
|
);
|
|
assert.strictEqual(getGlobalDir('kimi'), '/tmp/custom-kimi-agents');
|
|
});
|
|
});
|
|
|
|
test('XDG_CONFIG_HOME participates in the default Kimi generic agents path', () => {
|
|
withEnv({ KIMI_CONFIG_DIR: undefined, XDG_CONFIG_HOME: '/tmp/xdg-home' }, () => {
|
|
assert.strictEqual(
|
|
getGlobalConfigDir('kimi'),
|
|
path.join('/tmp/xdg-home', 'agents'),
|
|
);
|
|
assert.strictEqual(
|
|
getConfigDirFromHome('kimi', true),
|
|
"'.config', 'agents'",
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Kimi runtime artifact layout', () => {
|
|
test('known runtime with empty Phase 1 layout placeholder', () => {
|
|
const globalLayout = resolveRuntimeArtifactLayout('kimi', '/tmp/kimi-config', 'global');
|
|
assert.strictEqual(globalLayout.runtime, 'kimi');
|
|
assert.strictEqual(globalLayout.configDir, '/tmp/kimi-config');
|
|
assert.deepStrictEqual(globalLayout.kinds, []);
|
|
|
|
const localLayout = resolveRuntimeArtifactLayout('kimi', '/tmp/kimi-config', 'local');
|
|
assert.strictEqual(localLayout.runtime, 'kimi');
|
|
assert.deepStrictEqual(localLayout.kinds, []);
|
|
});
|
|
});
|
|
|
|
describe('Kimi local install guard', () => {
|
|
test('--kimi --local exits successfully without writing local Kimi project artifacts', () => {
|
|
const tmpProject = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-local-project-'));
|
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-local-home-'));
|
|
try {
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
[INSTALL_SCRIPT, '--kimi', '--local', '--no-sdk'],
|
|
{
|
|
cwd: tmpProject,
|
|
encoding: 'utf8',
|
|
env: installerEnv({ HOME: tmpHome, USERPROFILE: tmpHome }),
|
|
},
|
|
);
|
|
|
|
assert.strictEqual(
|
|
result.status,
|
|
0,
|
|
`expected --kimi --local guard to no-op successfully\nstdout: ${result.stdout}\nstderr: ${result.stderr}`,
|
|
);
|
|
const combined = `${result.stdout}\n${result.stderr}`;
|
|
assert.match(combined, /Kimi local install/i);
|
|
assert.match(combined, /deferred/i);
|
|
|
|
assert.ok(!fs.existsSync(path.join(tmpProject, '.kimi')), 'must not create .kimi/');
|
|
assert.ok(!fs.existsSync(path.join(tmpProject, '.agents')), 'must not create .agents/');
|
|
assert.ok(!fs.existsSync(path.join(tmpProject, '.claude')), 'must not fall back to Claude local install');
|
|
} finally {
|
|
cleanup(tmpProject);
|
|
cleanup(tmpHome);
|
|
}
|
|
});
|
|
|
|
test('--kimi --global exits successfully without writing unconverted Kimi artifacts', () => {
|
|
const tmpProject = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-global-project-'));
|
|
const tmpConfig = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-global-config-'));
|
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-kimi-global-home-'));
|
|
try {
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
[INSTALL_SCRIPT, '--kimi', '--global', '--config-dir', tmpConfig, '--no-sdk'],
|
|
{
|
|
cwd: tmpProject,
|
|
encoding: 'utf8',
|
|
env: installerEnv({ HOME: tmpHome, USERPROFILE: tmpHome }),
|
|
},
|
|
);
|
|
|
|
assert.strictEqual(
|
|
result.status,
|
|
0,
|
|
`expected --kimi --global skeleton guard to no-op successfully\nstdout: ${result.stdout}\nstderr: ${result.stderr}`,
|
|
);
|
|
const combined = `${result.stdout}\n${result.stderr}`;
|
|
assert.match(combined, /Kimi global install/i);
|
|
assert.match(combined, /deferred/i);
|
|
|
|
assert.ok(!fs.existsSync(path.join(tmpConfig, 'skills')), 'must not write unconverted Kimi skills');
|
|
assert.ok(!fs.existsSync(path.join(tmpConfig, 'agents')), 'must not write unconverted Kimi agents');
|
|
assert.ok(!fs.existsSync(path.join(tmpConfig, 'gsd-core')), 'must not write workflow payloads as Kimi artifacts');
|
|
assert.ok(!fs.existsSync(path.join(tmpConfig, 'hooks')), 'must not write hooks under the Kimi root');
|
|
} finally {
|
|
cleanup(tmpProject);
|
|
cleanup(tmpConfig);
|
|
cleanup(tmpHome);
|
|
}
|
|
});
|
|
});
|