Files
qinglong/cli/src/internal/runtime/lifecycle.ts
T
whyour 801a71d740 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
2026-09-25 23:24:41 +08:00

52 lines
1.7 KiB
TypeScript

import fs from 'node:fs/promises';
import path from 'node:path';
import type { LocalContext } from './context';
// The released 2.20 API rejects unknown lifecycle fields and has no dashboard record API.
// Read the installed panel version, never the independently versioned CLI package.
export async function extendedLifecycle(
context: LocalContext,
): Promise<boolean> {
if (context.env.QL_CLI_LIFECYCLE === 'legacy') return false;
if (context.env.QL_CLI_LIFECYCLE === 'extended') return true;
let version: string | undefined;
try {
const manifest: unknown = JSON.parse(
await fs.readFile(path.join(context.root, 'package.json'), 'utf8'),
);
if (
manifest &&
typeof manifest === 'object' &&
'version' in manifest &&
typeof manifest.version === 'string'
)
version = manifest.version;
} catch {}
if (version === undefined) {
try {
// Official 2.x images may omit package.version. Read only the top-level
// scalar release field; no YAML dependency or expression evaluation.
const release = await fs.readFile(
path.join(context.root, 'version.yaml'),
'utf8',
);
const entries = release
.replace(/^\uFEFF/, '')
.split(/\r?\n/)
.filter((line) => /^version:/.test(line));
if (entries.length !== 1) return false;
const scalar =
/^version:[ \t]*(?:"([^"]+)"|'([^']+)'|([^\s#]+))[ \t]*(?:#.*)?$/.exec(
entries[0]!,
);
version = scalar?.[1] ?? scalar?.[2] ?? scalar?.[3];
} catch {
return false;
}
}
const match = /^2\.(\d+)\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.exec(
version ?? '',
);
return !!match && Number(match[1]) >= 21;
}