feat(cli): cover OpenAPI with a remote npm CLI and internal panel tools (#3074)

* feat(cli): add unified Commander CLI for QingLong 2.x

* fix(cli): publish via npm and address security review feedback

* ci(cli): package npm artifacts and remove evaluation collateral

* test(cli): use a fixed shell fixture for log retention

* refactor(cli): separate remote npm client from panel tools

* feat(cli): cover active panel OpenAPI resources

* docs(cli): unify authentication and skill guidance

* refactor(cli): isolate internal commands and generate Commander help

* refactor(cli): organize remote and internal modules by responsibility

* ci(cli): publish verified npm archives from master

* fix(cli): publish under the whyour npm scope

* ci: use npm trusted publishing for both packages

* docs: introduce the published CLI on the project homepage

* fix(cli): preserve server log truncation and correct login hints

* fix(cli): accept dashboard record request bodies

* fix(cli): preserve stdin for local task execution

* fix(cli): resolve task executables after changing directory

* fix(cli): preserve shell function tasks and sanitize test failures

* fix(cli): preserve shell hook state and resolve workdir after hooks

* fix(cli): preserve cleanup across shared shell task timeouts

* fix(cli): isolate shell control descriptors and reap timed-out descendants
This commit is contained in:
whyour
2026-09-25 23:24:41 +08:00
committed by GitHub
parent f051135fc4
commit 801a71d740
185 changed files with 22412 additions and 6 deletions
+54
View File
@@ -0,0 +1,54 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');
const { spawn } = require('node:child_process');
// Separate acceptance gate until the reused language preloaders handle these
// paths. This runs the same real-language assertions as the normal preload suite.
test(
'language preloaders preserve hooks when the installation path contains spaces',
{
skip: process.env.QL_PRELOAD_PATH_INTEGRATION !== '1',
timeout: 120000,
},
async (t) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'ql preload paths '));
t.after(() => fs.rm(root, { recursive: true, force: true }));
const env = { ...process.env, TMPDIR: root, TMP: root, TEMP: root };
delete env.NODE_TEST_CONTEXT;
const child = spawn(
process.execPath,
[
'--test',
'--test-reporter=tap',
path.resolve(__dirname, '../preload.test.cjs'),
],
{
env,
stdio: ['ignore', 'pipe', 'pipe'],
},
);
t.after(() => {
if (child.exitCode === null) child.kill('SIGKILL');
});
let output = '';
child.stdout.on('data', (chunk) => {
output += chunk;
});
child.stderr.on('data', (chunk) => {
output += chunk;
});
const status = await new Promise((resolve, reject) => {
child.once('error', reject);
child.once('close', (code, signal) => resolve({ code, signal }));
});
assert.deepEqual(status, { code: 0, signal: null }, output);
assert.match(
output,
/# tests 6\b/,
'the child must actually run all five languages',
);
},
);