@taskcast/cli 1.2.0 → 1.2.1
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/dist/client.d.ts +15 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +47 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/doctor.d.ts +23 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +95 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/logs.d.ts +11 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +171 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +84 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/node.d.ts +5 -0
- package/dist/commands/node.d.ts.map +1 -0
- package/dist/commands/node.js +57 -0
- package/dist/commands/node.js.map +1 -0
- package/dist/commands/ping.d.ts +9 -0
- package/dist/commands/ping.d.ts.map +1 -0
- package/dist/commands/ping.js +43 -0
- package/dist/commands/ping.js.map +1 -0
- package/dist/commands/playground.d.ts +3 -0
- package/dist/commands/playground.d.ts.map +1 -0
- package/dist/commands/playground.js +37 -0
- package/dist/commands/playground.js.map +1 -0
- package/dist/commands/start.d.ts +3 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +131 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/tasks.d.ts +11 -0
- package/dist/commands/tasks.d.ts.map +1 -0
- package/dist/commands/tasks.js +129 -0
- package/dist/commands/tasks.js.map +1 -0
- package/dist/commands/ui.d.ts +3 -0
- package/dist/commands/ui.d.ts.map +1 -0
- package/dist/commands/ui.js +103 -0
- package/dist/commands/ui.js.map +1 -0
- package/dist/index.js +27 -416
- package/dist/index.js.map +1 -1
- package/dist/node-config.d.ts +22 -0
- package/dist/node-config.d.ts.map +1 -0
- package/dist/node-config.js +71 -0
- package/dist/node-config.js.map +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +65 -0
- package/dist/utils.js.map +1 -0
- package/package.json +9 -8
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { TaskcastServerClient } from '@taskcast/server-sdk';
|
|
2
|
+
import type { NodeEntry } from './node-config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a TaskcastServerClient synchronously from a NodeEntry.
|
|
5
|
+
* Suitable for JWT or no-auth nodes. Admin token nodes should use
|
|
6
|
+
* createClientFromNodeAsync instead.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createClientFromNode(node: NodeEntry, fetchFn?: typeof fetch): TaskcastServerClient;
|
|
9
|
+
/**
|
|
10
|
+
* Creates a TaskcastServerClient asynchronously from a NodeEntry.
|
|
11
|
+
* Handles admin token exchange: POSTs to {url}/admin/token to get a JWT,
|
|
12
|
+
* then creates the client with the exchanged JWT.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createClientFromNodeAsync(node: NodeEntry, fetchFn?: typeof fetch): Promise<TaskcastServerClient>;
|
|
15
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,GAAG,oBAAoB,CAKlG;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA2BtH"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { TaskcastServerClient } from '@taskcast/server-sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a TaskcastServerClient synchronously from a NodeEntry.
|
|
4
|
+
* Suitable for JWT or no-auth nodes. Admin token nodes should use
|
|
5
|
+
* createClientFromNodeAsync instead.
|
|
6
|
+
*/
|
|
7
|
+
export function createClientFromNode(node, fetchFn) {
|
|
8
|
+
const opts = { baseUrl: node.url };
|
|
9
|
+
if (node.tokenType === 'jwt' && node.token)
|
|
10
|
+
opts.token = node.token;
|
|
11
|
+
if (fetchFn)
|
|
12
|
+
opts.fetch = fetchFn;
|
|
13
|
+
return new TaskcastServerClient(opts);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a TaskcastServerClient asynchronously from a NodeEntry.
|
|
17
|
+
* Handles admin token exchange: POSTs to {url}/admin/token to get a JWT,
|
|
18
|
+
* then creates the client with the exchanged JWT.
|
|
19
|
+
*/
|
|
20
|
+
export async function createClientFromNodeAsync(node, fetchFn) {
|
|
21
|
+
if (node.tokenType === 'admin' && node.token) {
|
|
22
|
+
const f = fetchFn ?? globalThis.fetch;
|
|
23
|
+
const res = await f(`${node.url}/admin/token`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/json' },
|
|
26
|
+
body: JSON.stringify({ adminToken: node.token }),
|
|
27
|
+
});
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
let message = `HTTP ${res.status}`;
|
|
30
|
+
try {
|
|
31
|
+
const err = (await res.json());
|
|
32
|
+
message = err.error ?? message;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// ignore parse errors
|
|
36
|
+
}
|
|
37
|
+
throw new Error(message);
|
|
38
|
+
}
|
|
39
|
+
const { token: jwt } = (await res.json());
|
|
40
|
+
const opts = { baseUrl: node.url, token: jwt };
|
|
41
|
+
if (fetchFn)
|
|
42
|
+
opts.fetch = fetchFn;
|
|
43
|
+
return new TaskcastServerClient(opts);
|
|
44
|
+
}
|
|
45
|
+
return createClientFromNode(node, fetchFn);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAG3D;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAe,EAAE,OAAsB;IAC1E,MAAM,IAAI,GAA8D,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAA;IAC7F,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IACnE,IAAI,OAAO;QAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAA;IACjC,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAA;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,IAAe,EAAE,OAAsB;IACrF,IAAI,IAAI,CAAC,SAAS,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC7C,MAAM,CAAC,GAAG,OAAO,IAAI,UAAU,CAAC,KAAK,CAAA;QACrC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,cAAc,EAAE;YAC7C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;SACjD,CAAC,CAAA;QAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,OAAO,GAAG,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAA;YAClC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAA;gBACpD,OAAO,GAAG,GAAG,CAAC,KAAK,IAAI,OAAO,CAAA;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,sBAAsB;YACxB,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAA;QAC9D,MAAM,IAAI,GAA8D,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;QACzG,IAAI,OAAO;YAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAA;QACjC,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAA;IACvC,CAAC;IAED,OAAO,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AAC5C,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { NodeEntry } from '../node-config.js';
|
|
3
|
+
export interface DoctorResult {
|
|
4
|
+
server: {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
url: string;
|
|
7
|
+
uptime?: number;
|
|
8
|
+
error?: string;
|
|
9
|
+
};
|
|
10
|
+
auth: {
|
|
11
|
+
status: 'ok' | 'warn';
|
|
12
|
+
mode?: string;
|
|
13
|
+
message?: string;
|
|
14
|
+
};
|
|
15
|
+
adapters: Record<string, {
|
|
16
|
+
provider: string;
|
|
17
|
+
status: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export declare function runDoctor(node: NodeEntry, fetchFn?: typeof fetch): Promise<DoctorResult>;
|
|
21
|
+
export declare function formatDoctorResult(result: DoctorResult): string;
|
|
22
|
+
export declare function registerDoctorCommand(program: Command): void;
|
|
23
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAElD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACrE,IAAI,EAAE;QAAE,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAChE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/D;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC,YAAY,CAAC,CA4BvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAuC/D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2B5D"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { NodeConfigManager } from '../node-config.js';
|
|
2
|
+
export async function runDoctor(node, fetchFn) {
|
|
3
|
+
try {
|
|
4
|
+
const res = await (fetchFn ?? fetch)(`${node.url}/health/detail`);
|
|
5
|
+
if (!res.ok) {
|
|
6
|
+
return {
|
|
7
|
+
server: { ok: false, url: node.url, error: `HTTP ${res.status}` },
|
|
8
|
+
auth: { status: 'warn' },
|
|
9
|
+
adapters: {},
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const body = await res.json();
|
|
13
|
+
const authStatus = (!node.token && body.auth.mode !== 'none') ? 'warn' : 'ok';
|
|
14
|
+
const auth = { status: authStatus, mode: body.auth.mode };
|
|
15
|
+
if (authStatus === 'warn')
|
|
16
|
+
auth.message = 'no token configured for this node';
|
|
17
|
+
return {
|
|
18
|
+
server: { ok: true, url: node.url, uptime: body.uptime },
|
|
19
|
+
auth,
|
|
20
|
+
adapters: body.adapters,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
return {
|
|
25
|
+
server: { ok: false, url: node.url, error: err.message },
|
|
26
|
+
auth: { status: 'warn' },
|
|
27
|
+
adapters: {},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function formatDoctorResult(result) {
|
|
32
|
+
const lines = [];
|
|
33
|
+
// Server line
|
|
34
|
+
if (result.server.ok) {
|
|
35
|
+
const uptimeStr = result.server.uptime != null ? ` (uptime: ${result.server.uptime}s)` : '';
|
|
36
|
+
lines.push(`Server: OK taskcast at ${result.server.url}${uptimeStr}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
lines.push(`Server: FAIL cannot reach ${result.server.url}: ${result.server.error}`);
|
|
40
|
+
}
|
|
41
|
+
// Auth line
|
|
42
|
+
if (result.auth.status === 'ok') {
|
|
43
|
+
lines.push(`Auth: OK ${result.auth.mode}`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const msg = result.auth.message ?? (result.auth.mode ?? 'unknown');
|
|
47
|
+
lines.push(`Auth: WARN ${msg}`);
|
|
48
|
+
}
|
|
49
|
+
// Adapter lines
|
|
50
|
+
const adapterNames = ['broadcast', 'shortTermStore', 'longTermStore'];
|
|
51
|
+
const labelMap = {
|
|
52
|
+
broadcast: 'Broadcast',
|
|
53
|
+
shortTermStore: 'ShortTerm',
|
|
54
|
+
longTermStore: 'LongTerm',
|
|
55
|
+
};
|
|
56
|
+
for (const name of adapterNames) {
|
|
57
|
+
const adapter = result.adapters[name];
|
|
58
|
+
const label = `${labelMap[name]}:`.padEnd(11);
|
|
59
|
+
if (adapter) {
|
|
60
|
+
const statusTag = adapter.status === 'ok' ? 'OK' : 'FAIL';
|
|
61
|
+
lines.push(`${label}${statusTag} ${adapter.provider}`);
|
|
62
|
+
}
|
|
63
|
+
else if (name === 'longTermStore') {
|
|
64
|
+
lines.push(`${label}SKIP not configured`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return lines.join('\n');
|
|
68
|
+
}
|
|
69
|
+
export function registerDoctorCommand(program) {
|
|
70
|
+
program
|
|
71
|
+
.command('doctor')
|
|
72
|
+
.description('Deep health check against a Taskcast server')
|
|
73
|
+
.option('--node <name>', 'Node name to check (default: current node)')
|
|
74
|
+
.action(async (opts) => {
|
|
75
|
+
const mgr = new NodeConfigManager();
|
|
76
|
+
let node;
|
|
77
|
+
if (opts.node) {
|
|
78
|
+
const found = mgr.get(opts.node);
|
|
79
|
+
if (!found) {
|
|
80
|
+
console.error(`Node "${opts.node}" not found`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
node = found;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
node = mgr.getCurrent();
|
|
87
|
+
}
|
|
88
|
+
const result = await runDoctor(node);
|
|
89
|
+
console.log(formatDoctorResult(result));
|
|
90
|
+
if (!result.server.ok) {
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AASrD,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAe,EACf,OAAsB;IAEtB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAA;QACjE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO;gBACL,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,EAAE;gBACjE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;gBACxB,QAAQ,EAAE,EAAE;aACb,CAAA;QACH,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAE7B,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;QAC7E,MAAM,IAAI,GAAyB,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAC/E,IAAI,UAAU,KAAK,MAAM;YAAE,IAAI,CAAC,OAAO,GAAG,mCAAmC,CAAA;QAE7E,OAAO;YACL,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;YACxD,IAAI;YACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAA;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE;YACnE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;YACxB,QAAQ,EAAE,EAAE;SACb,CAAA;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACrD,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,cAAc;IACd,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACrB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;QAC3F,KAAK,CAAC,IAAI,CAAC,8BAA8B,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,SAAS,EAAE,CAAC,CAAA;IAC3E,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,iCAAiC,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;IAC1F,CAAC;IAED,YAAY;IACZ,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAClD,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC,CAAA;QAClE,KAAK,CAAC,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAA;IACvC,CAAC;IAED,gBAAgB;IAChB,MAAM,YAAY,GAAG,CAAC,WAAW,EAAE,gBAAgB,EAAE,eAAe,CAAU,CAAA;IAC9E,MAAM,QAAQ,GAA2B;QACvC,SAAS,EAAE,WAAW;QACtB,cAAc,EAAE,WAAW;QAC3B,aAAa,EAAE,UAAU;KAC1B,CAAA;IAED,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACrC,MAAM,KAAK,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAC7C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAA;YACzD,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,GAAG,SAAS,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAA;QACzD,CAAC;aAAM,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,sBAAsB,CAAC,CAAA;QAC5C,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,6CAA6C,CAAC;SAC1D,MAAM,CAAC,eAAe,EAAE,4CAA4C,CAAC;SACrE,MAAM,CAAC,KAAK,EAAE,IAAuB,EAAE,EAAE;QACxC,MAAM,GAAG,GAAG,IAAI,iBAAiB,EAAE,CAAA;QACnC,IAAI,IAAe,CAAA;QAEnB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,IAAI,aAAa,CAAC,CAAA;gBAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,IAAI,GAAG,KAAK,CAAA;QACd,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,GAAG,CAAC,UAAU,EAAE,CAAA;QACzB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAA;QACpC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAA;QAEvC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;IACH,CAAC,CAAC,CAAA;AACN,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export declare function formatEvent(event: {
|
|
3
|
+
type: string;
|
|
4
|
+
level: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
data: unknown;
|
|
7
|
+
}, taskId?: string): string;
|
|
8
|
+
export declare function consumeSSE(url: string, token: string | undefined, onEvent: (event: Record<string, unknown>, sseEventName: string) => void, onDone?: () => void, fetchFn?: typeof fetch): Promise<void>;
|
|
9
|
+
export declare function registerLogsCommand(program: Command): void;
|
|
10
|
+
export declare function registerTailCommand(program: Command): void;
|
|
11
|
+
//# sourceMappingURL=logs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,wBAAgB,WAAW,CACzB,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EACxE,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAcR;AAID,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,EACvE,MAAM,CAAC,EAAE,MAAM,IAAI,EACnB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,IAAI,CAAC,CA4Cf;AAuBD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkD1D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2C1D"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { NodeConfigManager } from '../node-config.js';
|
|
2
|
+
// ─── Formatting ──────────────────────────────────────────────────────────────
|
|
3
|
+
export function formatEvent(event, taskId) {
|
|
4
|
+
const time = new Date(event.timestamp).toLocaleTimeString('en-US', { hour12: false });
|
|
5
|
+
const taskPrefix = taskId ? `${taskId.slice(0, 7)}.. ` : '';
|
|
6
|
+
if (event.type === 'taskcast:done' || event.type === 'taskcast.done') {
|
|
7
|
+
const reason = typeof event.data === 'object' && event.data !== null
|
|
8
|
+
? event.data.reason ?? 'unknown'
|
|
9
|
+
: 'unknown';
|
|
10
|
+
return `[${time}] ${taskPrefix}[DONE] ${reason}`;
|
|
11
|
+
}
|
|
12
|
+
const dataStr = JSON.stringify(event.data);
|
|
13
|
+
return `[${time}] ${taskPrefix}${event.type.padEnd(16)} ${event.level.padEnd(5)} ${dataStr}`;
|
|
14
|
+
}
|
|
15
|
+
// ─── SSE Consumer ────────────────────────────────────────────────────────────
|
|
16
|
+
export async function consumeSSE(url, token, onEvent, onDone, fetchFn = fetch) {
|
|
17
|
+
const headers = { Accept: 'text/event-stream' };
|
|
18
|
+
if (token)
|
|
19
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
20
|
+
const res = await fetchFn(url, { headers });
|
|
21
|
+
if (!res.ok)
|
|
22
|
+
throw new Error(`HTTP ${res.status}`);
|
|
23
|
+
if (!res.body)
|
|
24
|
+
throw new Error('No response body');
|
|
25
|
+
const reader = res.body.getReader();
|
|
26
|
+
const decoder = new TextDecoder();
|
|
27
|
+
let buffer = '';
|
|
28
|
+
let currentEvent = '';
|
|
29
|
+
let currentData = '';
|
|
30
|
+
while (true) {
|
|
31
|
+
const { done, value } = await reader.read();
|
|
32
|
+
if (done)
|
|
33
|
+
break;
|
|
34
|
+
buffer += decoder.decode(value, { stream: true });
|
|
35
|
+
// Parse SSE format: event: xxx\ndata: xxx\n\n
|
|
36
|
+
const lines = buffer.split('\n');
|
|
37
|
+
buffer = lines.pop(); // incomplete line stays in buffer
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
if (line.startsWith('event:')) {
|
|
40
|
+
currentEvent = line.slice(6).trim();
|
|
41
|
+
}
|
|
42
|
+
else if (line.startsWith('data:')) {
|
|
43
|
+
currentData = line.slice(5).trim();
|
|
44
|
+
}
|
|
45
|
+
else if (line === '') {
|
|
46
|
+
// Empty line = end of event
|
|
47
|
+
if (currentData) {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(currentData);
|
|
50
|
+
onEvent(parsed, currentEvent);
|
|
51
|
+
if (currentEvent === 'taskcast.done')
|
|
52
|
+
onDone?.();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// skip unparseable data
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
currentEvent = '';
|
|
59
|
+
currentData = '';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// ─── Token Resolution ────────────────────────────────────────────────────────
|
|
65
|
+
async function resolveToken(node, fetchFn = fetch) {
|
|
66
|
+
if (node.tokenType === 'admin' && node.token) {
|
|
67
|
+
const res = await fetchFn(`${node.url}/admin/token`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({ adminToken: node.token }),
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok)
|
|
73
|
+
throw new Error(`Admin token exchange failed: HTTP ${res.status}`);
|
|
74
|
+
const body = (await res.json());
|
|
75
|
+
return body.token;
|
|
76
|
+
}
|
|
77
|
+
return node.tokenType === 'jwt' ? node.token : node.token;
|
|
78
|
+
}
|
|
79
|
+
// ─── Commands ────────────────────────────────────────────────────────────────
|
|
80
|
+
export function registerLogsCommand(program) {
|
|
81
|
+
program
|
|
82
|
+
.command('logs <taskId>')
|
|
83
|
+
.description('Stream events from a task in real-time')
|
|
84
|
+
.option('--types <types>', 'Filter by event types (CSV, supports wildcards)')
|
|
85
|
+
.option('--levels <levels>', 'Filter by levels (CSV)')
|
|
86
|
+
.option('--node <name>', 'Target node')
|
|
87
|
+
.action(async (taskId, opts) => {
|
|
88
|
+
const mgr = new NodeConfigManager();
|
|
89
|
+
let node;
|
|
90
|
+
if (opts.node) {
|
|
91
|
+
node = mgr.get(opts.node);
|
|
92
|
+
if (!node) {
|
|
93
|
+
console.error(`Node "${opts.node}" not found`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
node = mgr.getCurrent();
|
|
99
|
+
}
|
|
100
|
+
const params = new URLSearchParams();
|
|
101
|
+
if (opts.types)
|
|
102
|
+
params.set('types', opts.types);
|
|
103
|
+
if (opts.levels)
|
|
104
|
+
params.set('levels', opts.levels);
|
|
105
|
+
const qs = params.toString();
|
|
106
|
+
const url = `${node.url}/tasks/${taskId}/events${qs ? `?${qs}` : ''}`;
|
|
107
|
+
try {
|
|
108
|
+
const token = await resolveToken(node);
|
|
109
|
+
await consumeSSE(url, token, (event, sseEventName) => {
|
|
110
|
+
if (sseEventName === 'taskcast.done') {
|
|
111
|
+
console.log(formatEvent({
|
|
112
|
+
type: 'taskcast.done',
|
|
113
|
+
level: 'info',
|
|
114
|
+
timestamp: Date.now(),
|
|
115
|
+
data: event,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
else if (sseEventName === 'taskcast.event') {
|
|
119
|
+
console.log(formatEvent(event));
|
|
120
|
+
}
|
|
121
|
+
}, () => process.exit(0));
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
console.error(`Error: ${err.message}`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
export function registerTailCommand(program) {
|
|
130
|
+
program
|
|
131
|
+
.command('tail')
|
|
132
|
+
.description('Stream events from all tasks in real-time')
|
|
133
|
+
.option('--types <types>', 'Filter by event types (CSV, supports wildcards)')
|
|
134
|
+
.option('--levels <levels>', 'Filter by levels (CSV)')
|
|
135
|
+
.option('--node <name>', 'Target node')
|
|
136
|
+
.action(async (opts) => {
|
|
137
|
+
const mgr = new NodeConfigManager();
|
|
138
|
+
let node;
|
|
139
|
+
if (opts.node) {
|
|
140
|
+
node = mgr.get(opts.node);
|
|
141
|
+
if (!node) {
|
|
142
|
+
console.error(`Node "${opts.node}" not found`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
node = mgr.getCurrent();
|
|
148
|
+
}
|
|
149
|
+
const params = new URLSearchParams();
|
|
150
|
+
if (opts.types)
|
|
151
|
+
params.set('types', opts.types);
|
|
152
|
+
if (opts.levels)
|
|
153
|
+
params.set('levels', opts.levels);
|
|
154
|
+
const qs = params.toString();
|
|
155
|
+
const url = `${node.url}/events${qs ? `?${qs}` : ''}`;
|
|
156
|
+
try {
|
|
157
|
+
const token = await resolveToken(node);
|
|
158
|
+
await consumeSSE(url, token, (event, sseEventName) => {
|
|
159
|
+
if (sseEventName === 'taskcast.event') {
|
|
160
|
+
const e = event;
|
|
161
|
+
console.log(formatEvent(e, e.taskId));
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
console.error(`Error: ${err.message}`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AASnC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmF7D"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import postgres from 'postgres';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { loadConfigFile } from '@taskcast/core';
|
|
5
|
+
import { loadMigrationFiles, runMigrations } from '@taskcast/postgres';
|
|
6
|
+
import { resolvePostgresUrl, formatDisplayUrl } from '../migrate-helpers.js';
|
|
7
|
+
import { promptConfirm } from '../utils.js';
|
|
8
|
+
export function registerMigrateCommand(program) {
|
|
9
|
+
program
|
|
10
|
+
.command('migrate')
|
|
11
|
+
.description('Run pending PostgreSQL migrations')
|
|
12
|
+
.option('--url <url>', 'Postgres connection URL (highest priority)')
|
|
13
|
+
.option('-c, --config <path>', 'config file path')
|
|
14
|
+
.option('-y, --yes', 'skip confirmation prompt')
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
// URL resolution priority: --url flag > env var > config file
|
|
17
|
+
const { config: fileConfig } = await loadConfigFile(options.config);
|
|
18
|
+
const pgUrl = resolvePostgresUrl({
|
|
19
|
+
url: options.url,
|
|
20
|
+
envUrl: process.env['TASKCAST_POSTGRES_URL'],
|
|
21
|
+
configUrl: fileConfig.adapters?.longTermStore?.url,
|
|
22
|
+
});
|
|
23
|
+
if (!pgUrl) {
|
|
24
|
+
console.error('[taskcast] No Postgres URL found. Provide --url, set TASKCAST_POSTGRES_URL, or configure adapters.longTermStore.url in config.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const target = formatDisplayUrl(pgUrl);
|
|
28
|
+
// TODO: This path works in the monorepo only. For npm publishing,
|
|
29
|
+
// migrations would need to be bundled with the package.
|
|
30
|
+
const migrationsDir = join(dirname(fileURLToPath(import.meta.url)), '../../../../migrations/postgres');
|
|
31
|
+
const sql = postgres(pgUrl);
|
|
32
|
+
try {
|
|
33
|
+
// Load migration files and check what's pending
|
|
34
|
+
const allFiles = loadMigrationFiles(migrationsDir);
|
|
35
|
+
// Ensure the migrations table exists so we can query applied versions
|
|
36
|
+
await sql.unsafe(`
|
|
37
|
+
CREATE TABLE IF NOT EXISTS _sqlx_migrations (
|
|
38
|
+
version BIGINT PRIMARY KEY,
|
|
39
|
+
description TEXT NOT NULL,
|
|
40
|
+
installed_on TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
41
|
+
success BOOLEAN NOT NULL,
|
|
42
|
+
checksum BYTEA NOT NULL,
|
|
43
|
+
execution_time BIGINT NOT NULL
|
|
44
|
+
)
|
|
45
|
+
`);
|
|
46
|
+
const appliedRows = await sql.unsafe('SELECT version FROM _sqlx_migrations WHERE success = true');
|
|
47
|
+
const appliedVersions = new Set(appliedRows.map((r) => Number(r['version'])));
|
|
48
|
+
const pending = allFiles.filter((f) => !appliedVersions.has(f.version));
|
|
49
|
+
if (pending.length === 0) {
|
|
50
|
+
console.log('[taskcast] Database is up to date.');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
console.log(`[taskcast] Target: ${target}`);
|
|
54
|
+
console.log(`[taskcast] Pending migrations:`);
|
|
55
|
+
for (const file of pending) {
|
|
56
|
+
console.log(` ${file.filename}`);
|
|
57
|
+
}
|
|
58
|
+
if (!options.yes) {
|
|
59
|
+
if (!process.stdin.isTTY) {
|
|
60
|
+
console.error('[taskcast] No TTY detected. Re-run with --yes (-y) to skip confirmation.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const confirmed = await promptConfirm(`Apply ${pending.length} migration(s) to ${target}? (Y/n) `);
|
|
64
|
+
if (!confirmed) {
|
|
65
|
+
console.log('[taskcast] Migration cancelled.');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const result = await runMigrations(sql, migrationsDir);
|
|
70
|
+
for (const filename of result.applied) {
|
|
71
|
+
console.log(` Applied ${filename}`);
|
|
72
|
+
}
|
|
73
|
+
console.log(`[taskcast] Applied ${result.applied.length} migration(s) successfully.`);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
console.error(`[taskcast] Migration failed: ${err.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await sql.end();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { NodeListEntry } from '../node-config.js';
|
|
3
|
+
export declare function formatNodeList(nodes: NodeListEntry[]): string;
|
|
4
|
+
export declare function registerNodeCommand(program: Command): void;
|
|
5
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/commands/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjE,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,MAAM,CAU7D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgD1D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { NodeConfigManager } from '../node-config.js';
|
|
2
|
+
export function formatNodeList(nodes) {
|
|
3
|
+
if (nodes.length === 0) {
|
|
4
|
+
return 'No nodes configured. Using default: http://localhost:3721';
|
|
5
|
+
}
|
|
6
|
+
return nodes.map(n => {
|
|
7
|
+
const marker = n.current ? '*' : ' ';
|
|
8
|
+
const tokenInfo = n.tokenType ? ` (${n.tokenType})` : '';
|
|
9
|
+
return `${marker} ${n.name} ${n.url}${tokenInfo}`;
|
|
10
|
+
}).join('\n');
|
|
11
|
+
}
|
|
12
|
+
export function registerNodeCommand(program) {
|
|
13
|
+
const node = program
|
|
14
|
+
.command('node')
|
|
15
|
+
.description('Manage Taskcast server connections');
|
|
16
|
+
node
|
|
17
|
+
.command('add <name>')
|
|
18
|
+
.description('Add a named node connection')
|
|
19
|
+
.requiredOption('--url <url>', 'Server URL')
|
|
20
|
+
.option('--token <token>', 'Authentication token')
|
|
21
|
+
.option('--token-type <type>', 'Token type: jwt or admin', 'jwt')
|
|
22
|
+
.action((name, opts) => {
|
|
23
|
+
const mgr = new NodeConfigManager();
|
|
24
|
+
const entry = { url: opts.url };
|
|
25
|
+
if (opts.token) {
|
|
26
|
+
entry.token = opts.token;
|
|
27
|
+
entry.tokenType = opts.tokenType ?? 'jwt';
|
|
28
|
+
}
|
|
29
|
+
mgr.add(name, entry);
|
|
30
|
+
console.log(`Added node "${name}" → ${opts.url}`);
|
|
31
|
+
});
|
|
32
|
+
node
|
|
33
|
+
.command('remove <name>')
|
|
34
|
+
.description('Remove a named node connection')
|
|
35
|
+
.action((name) => {
|
|
36
|
+
const mgr = new NodeConfigManager();
|
|
37
|
+
mgr.remove(name);
|
|
38
|
+
console.log(`Removed node "${name}"`);
|
|
39
|
+
});
|
|
40
|
+
node
|
|
41
|
+
.command('use <name>')
|
|
42
|
+
.description('Set the current active node')
|
|
43
|
+
.action((name) => {
|
|
44
|
+
const mgr = new NodeConfigManager();
|
|
45
|
+
mgr.use(name);
|
|
46
|
+
console.log(`Switched to node "${name}"`);
|
|
47
|
+
});
|
|
48
|
+
node
|
|
49
|
+
.command('list')
|
|
50
|
+
.description('List all configured nodes')
|
|
51
|
+
.action(() => {
|
|
52
|
+
const mgr = new NodeConfigManager();
|
|
53
|
+
const nodes = mgr.list();
|
|
54
|
+
console.log(formatNodeList(nodes));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/commands/node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export interface PingResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
latencyMs?: number;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function pingServer(url: string, fetchFn?: typeof fetch): Promise<PingResult>;
|
|
8
|
+
export declare function registerPingCommand(program: Command): void;
|
|
9
|
+
//# sourceMappingURL=ping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ping.d.ts","sourceRoot":"","sources":["../../src/commands/ping.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGnC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAUhG;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0B1D"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { NodeConfigManager } from '../node-config.js';
|
|
2
|
+
export async function pingServer(url, fetchFn = fetch) {
|
|
3
|
+
const start = Date.now();
|
|
4
|
+
try {
|
|
5
|
+
const res = await fetchFn(`${url}/health`);
|
|
6
|
+
const latencyMs = Date.now() - start;
|
|
7
|
+
if (!res.ok)
|
|
8
|
+
return { ok: false, error: `HTTP ${res.status}` };
|
|
9
|
+
return { ok: true, latencyMs };
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
return { ok: false, error: err.message };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function registerPingCommand(program) {
|
|
16
|
+
program
|
|
17
|
+
.command('ping')
|
|
18
|
+
.description('Check connectivity to a Taskcast server')
|
|
19
|
+
.option('--node <name>', 'Named node to ping')
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
const mgr = new NodeConfigManager();
|
|
22
|
+
let node;
|
|
23
|
+
if (opts.node) {
|
|
24
|
+
node = mgr.get(opts.node);
|
|
25
|
+
if (!node) {
|
|
26
|
+
console.error(`Node "${opts.node}" not found`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
node = mgr.getCurrent();
|
|
32
|
+
}
|
|
33
|
+
const result = await pingServer(node.url);
|
|
34
|
+
if (result.ok) {
|
|
35
|
+
console.log(`OK — taskcast at ${node.url} (${result.latencyMs}ms)`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.error(`FAIL — cannot reach ${node.url}: ${result.error}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ping.js.map
|