smithue-cli 0.8.0
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/README.md +146 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +190 -0
- package/dist/client.d.ts +37 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +184 -0
- package/dist/commands/batch.d.ts +13 -0
- package/dist/commands/batch.d.ts.map +1 -0
- package/dist/commands/batch.js +64 -0
- package/dist/commands/exec.d.ts +6 -0
- package/dist/commands/exec.d.ts.map +1 -0
- package/dist/commands/exec.js +18 -0
- package/dist/commands/list.d.ts +8 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +18 -0
- package/dist/commands/purge.d.ts +3 -0
- package/dist/commands/purge.d.ts.map +1 -0
- package/dist/commands/purge.js +154 -0
- package/dist/commands/search.d.ts +8 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +31 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +51 -0
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +20 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +139 -0
- package/dist/output.d.ts +8 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +46 -0
- package/dist/portfile.d.ts +31 -0
- package/dist/portfile.d.ts.map +1 -0
- package/dist/portfile.js +133 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/version-check.d.ts +2 -0
- package/dist/version-check.d.ts.map +1 -0
- package/dist/version-check.js +9 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAW3F"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { discoverPort } from '../portfile.js';
|
|
2
|
+
import { SmithUEClient } from '../client.js';
|
|
3
|
+
import { printResult, printError } from '../output.js';
|
|
4
|
+
import { checkVersionCompat } from '../version-check.js';
|
|
5
|
+
export async function listCommand(domain, opts) {
|
|
6
|
+
try {
|
|
7
|
+
const discovered = await discoverPort(opts);
|
|
8
|
+
if (opts.cliVersion)
|
|
9
|
+
checkVersionCompat(opts.cliVersion, discovered.plugin_version);
|
|
10
|
+
const { port } = discovered;
|
|
11
|
+
const client = new SmithUEClient({ host: '127.0.0.1', port });
|
|
12
|
+
const result = await client.listTools(domain);
|
|
13
|
+
printResult(result);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
printError(err);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purge.d.ts","sourceRoot":"","sources":["../../src/commands/purge.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAE7D,wBAAsB,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgK7D"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { readdir, readFile, lstat, unlink, rmdir } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { createInterface } from 'readline';
|
|
4
|
+
import { getPortfileDir, SmithUEError } from '../portfile.js';
|
|
5
|
+
import { printResult, printError } from '../output.js';
|
|
6
|
+
export async function purge(opts) {
|
|
7
|
+
if (!process.env['LOCALAPPDATA']) {
|
|
8
|
+
printError(new SmithUEError('LOCALAPPDATA env not set; smithue-cli purge is Windows-only', 2));
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const dir = getPortfileDir();
|
|
12
|
+
let dirStat;
|
|
13
|
+
try {
|
|
14
|
+
dirStat = await lstat(dir);
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
if (e.code === 'ENOENT') {
|
|
18
|
+
printResult({
|
|
19
|
+
status: 'nothing_to_purge',
|
|
20
|
+
path: dir,
|
|
21
|
+
scanned: 0,
|
|
22
|
+
deleted: 0,
|
|
23
|
+
skipped_live: 0,
|
|
24
|
+
failed: 0,
|
|
25
|
+
directory_removed: false,
|
|
26
|
+
errors: [],
|
|
27
|
+
warnings: [],
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
throw e;
|
|
32
|
+
}
|
|
33
|
+
if (dirStat.isSymbolicLink()) {
|
|
34
|
+
printError(new SmithUEError('Refusing to purge symlinked .smithue directory', 3));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const entries = await readdir(dir);
|
|
38
|
+
const portEntries = entries.filter((e) => e.endsWith('.port'));
|
|
39
|
+
const unknownEntries = entries.filter((e) => !e.endsWith('.port'));
|
|
40
|
+
const scanned = portEntries.length;
|
|
41
|
+
const portFiles = [];
|
|
42
|
+
let skipped_live = 0;
|
|
43
|
+
if (opts.force) {
|
|
44
|
+
for (const name of portEntries) {
|
|
45
|
+
portFiles.push({ path: join(dir, name), dead: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
for (const name of portEntries) {
|
|
50
|
+
const filePath = join(dir, name);
|
|
51
|
+
let port;
|
|
52
|
+
try {
|
|
53
|
+
const raw = await readFile(filePath, 'utf-8');
|
|
54
|
+
const data = JSON.parse(raw);
|
|
55
|
+
port = data.port;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
portFiles.push({ path: filePath, dead: true });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
let alive = false;
|
|
62
|
+
try {
|
|
63
|
+
await fetch(`http://127.0.0.1:${port}/ready`, { signal: AbortSignal.timeout(1000) });
|
|
64
|
+
alive = true;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
alive = false;
|
|
68
|
+
}
|
|
69
|
+
portFiles.push({ path: filePath, dead: !alive });
|
|
70
|
+
if (alive)
|
|
71
|
+
skipped_live++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const toDelete = opts.force
|
|
75
|
+
? [...portFiles.map((f) => f.path), ...unknownEntries.map((e) => join(dir, e))]
|
|
76
|
+
: portFiles.filter((f) => f.dead).map((f) => f.path);
|
|
77
|
+
const warnings = [];
|
|
78
|
+
if (!opts.force && unknownEntries.length > 0) {
|
|
79
|
+
warnings.push(`${unknownEntries.length} unknown file(s) skipped (use --force to remove): ${unknownEntries.join(', ')}`);
|
|
80
|
+
}
|
|
81
|
+
if (!opts.yes && !opts.dryRun) {
|
|
82
|
+
if (!process.stdin.isTTY) {
|
|
83
|
+
printError(new SmithUEError('Refusing to delete in non-interactive mode without -y/--yes', 1));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const promptMsg = [
|
|
87
|
+
`Path: ${dir}`,
|
|
88
|
+
` Files to delete: ${toDelete.length}`,
|
|
89
|
+
skipped_live > 0 ? ` Live portfiles skipped: ${skipped_live}` : null,
|
|
90
|
+
`Delete ${toDelete.length} file(s) from ${dir}? [y/N] `,
|
|
91
|
+
].filter(Boolean).join('\n');
|
|
92
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
93
|
+
const answer = await new Promise((resolve) => {
|
|
94
|
+
rl.question(promptMsg, (ans) => {
|
|
95
|
+
rl.close();
|
|
96
|
+
resolve(ans);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
if (!/^y(es)?$/i.test(answer)) {
|
|
100
|
+
printResult({
|
|
101
|
+
status: 'cancelled',
|
|
102
|
+
path: dir,
|
|
103
|
+
scanned,
|
|
104
|
+
deleted: 0,
|
|
105
|
+
skipped_live,
|
|
106
|
+
failed: 0,
|
|
107
|
+
directory_removed: false,
|
|
108
|
+
errors: [],
|
|
109
|
+
warnings,
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (opts.dryRun) {
|
|
115
|
+
printResult({
|
|
116
|
+
status: 'dry_run',
|
|
117
|
+
path: dir,
|
|
118
|
+
scanned,
|
|
119
|
+
deleted: toDelete.length,
|
|
120
|
+
skipped_live,
|
|
121
|
+
failed: 0,
|
|
122
|
+
directory_removed: false,
|
|
123
|
+
errors: [],
|
|
124
|
+
warnings,
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const errors = [];
|
|
129
|
+
let deleted = 0;
|
|
130
|
+
let failed = 0;
|
|
131
|
+
for (const filePath of toDelete) {
|
|
132
|
+
try {
|
|
133
|
+
await unlink(filePath);
|
|
134
|
+
deleted++;
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
failed++;
|
|
138
|
+
errors.push(`${filePath}: ${e.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
let directory_removed = false;
|
|
142
|
+
const remaining = await readdir(dir);
|
|
143
|
+
if (remaining.length === 0 && skipped_live === 0) {
|
|
144
|
+
try {
|
|
145
|
+
await rmdir(dir);
|
|
146
|
+
directory_removed = true;
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
warnings.push(`Failed to remove directory ${dir}: ${e.message}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const status = skipped_live > 0 || failed > 0 ? 'partial' : 'purged';
|
|
153
|
+
printResult({ status, path: dir, scanned, deleted, skipped_live, failed, directory_removed, errors, warnings });
|
|
154
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BpF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { discoverPort } from '../portfile.js';
|
|
2
|
+
import { SmithUEClient } from '../client.js';
|
|
3
|
+
import { printResult, printError } from '../output.js';
|
|
4
|
+
import { checkVersionCompat } from '../version-check.js';
|
|
5
|
+
export async function searchCommand(keyword, opts) {
|
|
6
|
+
try {
|
|
7
|
+
const discovered = await discoverPort(opts);
|
|
8
|
+
if (opts.cliVersion)
|
|
9
|
+
checkVersionCompat(opts.cliVersion, discovered.plugin_version);
|
|
10
|
+
const { port } = discovered;
|
|
11
|
+
const client = new SmithUEClient({ host: '127.0.0.1', port });
|
|
12
|
+
const domains = await client.listTools();
|
|
13
|
+
const kw = keyword.toLowerCase();
|
|
14
|
+
const matches = [];
|
|
15
|
+
for (const domainTool of domains) {
|
|
16
|
+
const domainName = domainTool.name;
|
|
17
|
+
const tools = await client.listTools(domainName);
|
|
18
|
+
for (const tool of tools) {
|
|
19
|
+
const name = tool.name ?? '';
|
|
20
|
+
const description = tool.description ?? '';
|
|
21
|
+
if (name.toLowerCase().includes(kw) || description.toLowerCase().includes(kw)) {
|
|
22
|
+
matches.push({ domain: domainName, name, description });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
printResult(matches);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
printError(err);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CnE"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SmithUEClient } from '../client.js';
|
|
2
|
+
import { discoverPort, SmithUEError } from '../portfile.js';
|
|
3
|
+
import { printResult, printError } from '../output.js';
|
|
4
|
+
import { checkVersionCompat } from '../version-check.js';
|
|
5
|
+
async function sleep(ms) {
|
|
6
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
|
+
}
|
|
8
|
+
export async function statusCommand(opts) {
|
|
9
|
+
try {
|
|
10
|
+
const discovered = await discoverPort(opts);
|
|
11
|
+
if (opts.cliVersion)
|
|
12
|
+
checkVersionCompat(opts.cliVersion, discovered.plugin_version);
|
|
13
|
+
const client = new SmithUEClient({ host: '127.0.0.1', port: discovered.port });
|
|
14
|
+
if (opts.wait !== undefined && opts.wait > 0) {
|
|
15
|
+
const timeoutMs = opts.wait * 1000;
|
|
16
|
+
const startedAt = Date.now();
|
|
17
|
+
while (true) {
|
|
18
|
+
const res = await client.getReady();
|
|
19
|
+
if (res.ready) {
|
|
20
|
+
printResult({
|
|
21
|
+
port: discovered.port,
|
|
22
|
+
pid: discovered.pid,
|
|
23
|
+
project_name: discovered.project_name,
|
|
24
|
+
ready: res.ready,
|
|
25
|
+
version: res.version,
|
|
26
|
+
pie_active: res.pie_active,
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (Date.now() - startedAt >= timeoutMs) {
|
|
31
|
+
throw new SmithUEError('Timed out waiting for editor to be ready', 6);
|
|
32
|
+
}
|
|
33
|
+
await sleep(1000);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const res = await client.getReady();
|
|
38
|
+
printResult({
|
|
39
|
+
port: discovered.port,
|
|
40
|
+
pid: discovered.pid,
|
|
41
|
+
project_name: discovered.project_name,
|
|
42
|
+
ready: res.ready,
|
|
43
|
+
version: res.version,
|
|
44
|
+
pie_active: res.pie_active,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
printError(err);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAIA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBpD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { SmithUEError } from '../portfile.js';
|
|
3
|
+
import { printResult, printError } from '../output.js';
|
|
4
|
+
export async function upgradeCommand() {
|
|
5
|
+
try {
|
|
6
|
+
const output = execSync('npm update -g smithue-cli', { stdio: 'pipe', encoding: 'utf-8' });
|
|
7
|
+
printResult({ status: 'updated', output: String(output).trim() });
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
if (err.code === 'ENOENT') {
|
|
11
|
+
printError(new SmithUEError('npm not found in PATH', 1));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (err instanceof Error) {
|
|
15
|
+
printError(new SmithUEError(err.message, 1));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
printError(new SmithUEError(String(err), 1));
|
|
19
|
+
}
|
|
20
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { readdir, unlink } from 'fs/promises';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { execCommand } from './commands/exec.js';
|
|
6
|
+
import { listCommand } from './commands/list.js';
|
|
7
|
+
import { searchCommand } from './commands/search.js';
|
|
8
|
+
import { statusCommand } from './commands/status.js';
|
|
9
|
+
import { printResult, printError } from './output.js';
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name('smithue')
|
|
13
|
+
.description('CLI for SmithUE Unreal Engine plugin')
|
|
14
|
+
.version('0.7.0')
|
|
15
|
+
.option('--pid <pid>', 'target SmithUE instance by PID', parseInt)
|
|
16
|
+
.option('--project <path>', 'target SmithUE instance by project path')
|
|
17
|
+
.option('--port <port>', 'connect directly to port (skip discovery)', parseInt);
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// exec
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
program
|
|
22
|
+
.command('exec <command> [params]')
|
|
23
|
+
.description('Execute a SmithUE command')
|
|
24
|
+
.action(async (command, params) => {
|
|
25
|
+
const globals = program.opts();
|
|
26
|
+
let parsedParams = {};
|
|
27
|
+
if (params) {
|
|
28
|
+
try {
|
|
29
|
+
parsedParams = JSON.parse(params);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
printError(new Error(`params must be valid JSON, got: ${params}`));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
await execCommand(command, parsedParams, globals);
|
|
37
|
+
});
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// list
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
program
|
|
42
|
+
.command('list [domain]')
|
|
43
|
+
.description('List available tools, optionally filtered by domain')
|
|
44
|
+
.action(async (domain) => {
|
|
45
|
+
const globals = program.opts();
|
|
46
|
+
await listCommand(domain, globals);
|
|
47
|
+
});
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// search
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
program
|
|
52
|
+
.command('search <keyword>')
|
|
53
|
+
.description('Search tools by keyword')
|
|
54
|
+
.action(async (keyword) => {
|
|
55
|
+
const globals = program.opts();
|
|
56
|
+
await searchCommand(keyword, globals);
|
|
57
|
+
});
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// status
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
program
|
|
62
|
+
.command('status')
|
|
63
|
+
.description('Get SmithUE editor status')
|
|
64
|
+
.option('--wait <seconds>', 'wait up to N seconds for editor to be ready', parseInt)
|
|
65
|
+
.action(async (cmdOpts) => {
|
|
66
|
+
const globals = program.opts();
|
|
67
|
+
await statusCommand({ ...globals, wait: cmdOpts.wait });
|
|
68
|
+
});
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// prune
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
program
|
|
73
|
+
.command('prune')
|
|
74
|
+
.description('Remove stale portfiles for SmithUE instances that are no longer running')
|
|
75
|
+
.action(async () => {
|
|
76
|
+
const localAppData = process.env['LOCALAPPDATA'];
|
|
77
|
+
if (!localAppData) {
|
|
78
|
+
printResult({ scanned: 0, pruned: 0, kept: 0 });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const dir = join(localAppData, '.smithue');
|
|
82
|
+
let entries;
|
|
83
|
+
try {
|
|
84
|
+
entries = await readdir(dir);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
printResult({ scanned: 0, pruned: 0, kept: 0 });
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const portFiles = entries.filter((e) => e.endsWith('.port'));
|
|
91
|
+
let scanned = 0;
|
|
92
|
+
let pruned = 0;
|
|
93
|
+
let kept = 0;
|
|
94
|
+
for (const entry of portFiles) {
|
|
95
|
+
const filePath = join(dir, entry);
|
|
96
|
+
// Extract port from filename: <port>.port
|
|
97
|
+
const portStr = entry.slice(0, -'.port'.length);
|
|
98
|
+
const port = parseInt(portStr, 10);
|
|
99
|
+
scanned++;
|
|
100
|
+
if (isNaN(port) || port <= 0) {
|
|
101
|
+
// Malformed filename — treat as stale
|
|
102
|
+
try {
|
|
103
|
+
await unlink(filePath);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// best effort
|
|
107
|
+
}
|
|
108
|
+
pruned++;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
let alive = false;
|
|
112
|
+
try {
|
|
113
|
+
const res = await fetch(`http://127.0.0.1:${port}/ready`, {
|
|
114
|
+
signal: AbortSignal.timeout(1000),
|
|
115
|
+
});
|
|
116
|
+
alive = res.status === 200;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
alive = false;
|
|
120
|
+
}
|
|
121
|
+
if (alive) {
|
|
122
|
+
kept++;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
try {
|
|
126
|
+
await unlink(filePath);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// best effort
|
|
130
|
+
}
|
|
131
|
+
pruned++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
printResult({ scanned, pruned, kept });
|
|
135
|
+
});
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Parse
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
await program.parseAsync(process.argv);
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface OutputOptions {
|
|
2
|
+
terse?: boolean;
|
|
3
|
+
outPath?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function setOutputOptions(opts: OutputOptions): void;
|
|
6
|
+
export declare function printResult(data: unknown): void;
|
|
7
|
+
export declare function printError(err: unknown): void;
|
|
8
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAE1D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CA0B/C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAiB7C"}
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { SmithUEError } from './portfile.js';
|
|
3
|
+
let _opts = {};
|
|
4
|
+
export function setOutputOptions(opts) {
|
|
5
|
+
_opts = { ..._opts, ...opts };
|
|
6
|
+
}
|
|
7
|
+
export function printResult(data) {
|
|
8
|
+
const json = _opts.terse
|
|
9
|
+
? JSON.stringify(data) + '\n'
|
|
10
|
+
: JSON.stringify(data, null, 2) + '\n';
|
|
11
|
+
if (_opts.outPath) {
|
|
12
|
+
let isDir = false;
|
|
13
|
+
try {
|
|
14
|
+
isDir = fs.statSync(_opts.outPath).isDirectory();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// path doesn't exist — fine, writeFileSync will create it
|
|
18
|
+
}
|
|
19
|
+
if (isDir) {
|
|
20
|
+
process.stderr.write(JSON.stringify({ error: `outPath is a directory: ${_opts.outPath}`, exit_code: 1 }) + '\n');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
fs.writeFileSync(_opts.outPath, json, 'utf8');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
process.stdout.write(json);
|
|
28
|
+
}
|
|
29
|
+
export function printError(err) {
|
|
30
|
+
let message;
|
|
31
|
+
let exitCode;
|
|
32
|
+
if (err instanceof SmithUEError) {
|
|
33
|
+
message = err.message;
|
|
34
|
+
exitCode = err.exitCode;
|
|
35
|
+
}
|
|
36
|
+
else if (err instanceof Error) {
|
|
37
|
+
message = err.message;
|
|
38
|
+
exitCode = 4;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
message = String(err);
|
|
42
|
+
exitCode = 4;
|
|
43
|
+
}
|
|
44
|
+
process.stderr.write(JSON.stringify({ error: message, exit_code: exitCode }) + '\n');
|
|
45
|
+
process.exit(exitCode);
|
|
46
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare class SmithUEError extends Error {
|
|
2
|
+
readonly exitCode: number;
|
|
3
|
+
constructor(message: string, exitCode: number);
|
|
4
|
+
}
|
|
5
|
+
export interface PortfileData {
|
|
6
|
+
port: number;
|
|
7
|
+
pid: number;
|
|
8
|
+
project: string;
|
|
9
|
+
project_name: string;
|
|
10
|
+
started_at: string;
|
|
11
|
+
plugin_version: string;
|
|
12
|
+
}
|
|
13
|
+
export interface DiscoverResult {
|
|
14
|
+
port: number;
|
|
15
|
+
pid: number;
|
|
16
|
+
project: string;
|
|
17
|
+
project_name: string;
|
|
18
|
+
plugin_version?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface DiscoverOpts {
|
|
21
|
+
pid?: number;
|
|
22
|
+
project?: string;
|
|
23
|
+
port?: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function getPortfileDir(): string;
|
|
26
|
+
export declare function readPortfiles(dir: string): Promise<Array<{
|
|
27
|
+
file: string;
|
|
28
|
+
data: PortfileData;
|
|
29
|
+
}>>;
|
|
30
|
+
export declare function discoverPort(opts?: DiscoverOpts): Promise<DiscoverResult>;
|
|
31
|
+
//# sourceMappingURL=portfile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portfile.d.ts","sourceRoot":"","sources":["../src/portfile.ts"],"names":[],"mappings":"AAQA,qBAAa,YAAa,SAAQ,KAAK;aAGnB,QAAQ,EAAE,MAAM;gBADhC,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM;CAKnC;AAMD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,wBAAgB,cAAc,IAAI,MAAM,CASvC;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC,CAuBrG;AA2BD,wBAAsB,YAAY,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAmFnF"}
|