@altronix/cli 0.7.13 → 0.7.15
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/about.d.ts +4 -0
- package/dist/about.js +46 -0
- package/dist/build.js +5 -7
- package/dist/build.ui.d.ts +2 -2
- package/dist/build.ui.js +8 -27
- package/dist/cloud.d.ts +3 -0
- package/dist/cloud.js +47 -0
- package/dist/exe.d.ts +5 -0
- package/dist/exe.js +52 -0
- package/dist/index.js +112 -42
- package/dist/listen.d.ts +2 -0
- package/dist/listen.js +23 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +8 -0
- package/dist/net.d.ts +4 -0
- package/dist/net.js +53 -0
- package/dist/progress.d.ts +2 -0
- package/dist/progress.js +13 -0
- package/dist/progress.ui.d.ts +12 -0
- package/dist/progress.ui.js +35 -0
- package/dist/quit.d.ts +2 -0
- package/dist/quit.js +30 -0
- package/dist/select.d.ts +4 -0
- package/dist/select.js +18 -0
- package/dist/select.ui.d.ts +11 -0
- package/dist/select.ui.js +72 -0
- package/dist/stress.d.ts +2 -0
- package/dist/stress.js +23 -0
- package/dist/stress.ui.d.ts +3 -0
- package/dist/stress.ui.js +31 -0
- package/dist/update.d.ts +2 -0
- package/dist/update.js +59 -0
- package/dist/useStdoutDimensions.d.ts +1 -0
- package/dist/useStdoutDimensions.js +17 -0
- package/package.json +6 -3
package/dist/about.d.ts
ADDED
package/dist/about.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { About, cborDecodeStr } from '@altronix/zdk';
|
|
2
|
+
import { Linq } from '@altronix/device';
|
|
3
|
+
import { EmptyError, finalize, firstValueFrom, map, switchMap } from 'rxjs';
|
|
4
|
+
import logger from './logger.js';
|
|
5
|
+
import { select, first } from './select.js';
|
|
6
|
+
export async function getAbout() {
|
|
7
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
8
|
+
const linq = new Linq();
|
|
9
|
+
const path = '/api/v1/about';
|
|
10
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => linq.get(sid, path)), map((resp) => About.fromCbor(resp.body)), finalize(() => linq.shutdown()));
|
|
11
|
+
return firstValueFrom(obs)
|
|
12
|
+
.then((about) => console.log(JSON.stringify(about.toJson(), null, 2)))
|
|
13
|
+
.catch((e) => {
|
|
14
|
+
if (!(e instanceof EmptyError))
|
|
15
|
+
throw e;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export async function getSite() {
|
|
19
|
+
const log = logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
20
|
+
const linq = new Linq();
|
|
21
|
+
const path = '/api/v1/about/site';
|
|
22
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => linq.get(sid, path)), map((resp) => ({ site: cborDecodeStr(resp.body) })), finalize(() => linq.shutdown()));
|
|
23
|
+
return firstValueFrom(obs)
|
|
24
|
+
.then((site) => log.info(`request complete`, { ...site }))
|
|
25
|
+
.then(() => void 0)
|
|
26
|
+
.catch((e) => {
|
|
27
|
+
if (!(e instanceof EmptyError)) {
|
|
28
|
+
throw e;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export async function setSite(site) {
|
|
33
|
+
const log = logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
34
|
+
const linq = new Linq();
|
|
35
|
+
const path = '/api/v1/about/site';
|
|
36
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => linq.put(sid, path, site)), finalize(() => linq.shutdown()));
|
|
37
|
+
return firstValueFrom(obs)
|
|
38
|
+
.then(({ meta }) => {
|
|
39
|
+
log.info(`request complete`, { code: meta.code, message: meta.mesg });
|
|
40
|
+
})
|
|
41
|
+
.catch((e) => {
|
|
42
|
+
if (!(e instanceof EmptyError)) {
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
package/dist/build.js
CHANGED
|
@@ -379,13 +379,11 @@ function extraBootConfs(board, extraConfs) {
|
|
|
379
379
|
function copy() {
|
|
380
380
|
return (obs$) => obs$.pipe(mergeMap((opts) => {
|
|
381
381
|
const { src, dst } = opts;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
.finally(() => subscriber.complete());
|
|
388
|
-
});
|
|
382
|
+
const promise = fs.promises
|
|
383
|
+
.copyFile(src, dst)
|
|
384
|
+
.then(() => opts)
|
|
385
|
+
.catch(() => ({ ...opts, err: dst }));
|
|
386
|
+
return from(promise);
|
|
389
387
|
}));
|
|
390
388
|
}
|
|
391
389
|
function concatFiles(src, dest) {
|
package/dist/build.ui.d.ts
CHANGED
|
@@ -18,10 +18,10 @@ export interface BuildProgress<K extends string = string> {
|
|
|
18
18
|
export interface Options {
|
|
19
19
|
items: BuildItem[];
|
|
20
20
|
progress$: Observable<BuildProgress>;
|
|
21
|
-
onComplete:
|
|
21
|
+
onComplete: BuildEffectCallback;
|
|
22
22
|
}
|
|
23
23
|
export default function ({ items, progress$, onComplete }: Options): React.JSX.Element;
|
|
24
|
-
interface
|
|
24
|
+
interface BuildEffectCallback<E extends Error = Error> {
|
|
25
25
|
(e?: E): void;
|
|
26
26
|
}
|
|
27
27
|
export {};
|
package/dist/build.ui.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, { useLayoutEffect,
|
|
2
|
-
import { Box, Text
|
|
1
|
+
import React, { useLayoutEffect, useState } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
3
|
import { scan } from 'rxjs';
|
|
4
|
+
import useStdoutDimensions from './useStdoutDimensions.js';
|
|
4
5
|
export class BuildError extends Error {
|
|
5
6
|
constructor(item, kind, message) {
|
|
6
7
|
super(message);
|
|
@@ -21,8 +22,7 @@ export class BuildError extends Error {
|
|
|
21
22
|
export default function ({ items, progress$, onComplete }) {
|
|
22
23
|
const [col, _rows] = useStdoutDimensions();
|
|
23
24
|
const [width, setWidth] = useState(0);
|
|
24
|
-
const progress = useBuildEffect(progress$, items.map(({ item: i }) => i));
|
|
25
|
-
useCompleteEffect(progress$, onComplete);
|
|
25
|
+
const progress = useBuildEffect(progress$, items.map(({ item: i }) => i), onComplete);
|
|
26
26
|
useLayoutEffect(() => {
|
|
27
27
|
const width = items.map(({ item }) => item).reduce(calculateItemWidth, 0);
|
|
28
28
|
setWidth(width);
|
|
@@ -81,34 +81,15 @@ function progressReducer(acc, next) {
|
|
|
81
81
|
: progressInc(acc[next.item]);
|
|
82
82
|
return acc;
|
|
83
83
|
}
|
|
84
|
-
function
|
|
85
|
-
useLayoutEffect(() => {
|
|
86
|
-
const s = obs$.subscribe({ complete: cb, error: cb });
|
|
87
|
-
return s.unsubscribe();
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
function useBuildEffect(obs$, items) {
|
|
84
|
+
function useBuildEffect(obs$, items, cb) {
|
|
91
85
|
const [progress, setProgress] = useState(initProgress(items));
|
|
92
86
|
useLayoutEffect(() => {
|
|
93
87
|
const s = obs$.pipe(scan(progressReducer, progress)).subscribe({
|
|
94
|
-
next: (progress) => setProgress({ ...progress })
|
|
88
|
+
next: (progress) => setProgress({ ...progress }),
|
|
89
|
+
complete: cb,
|
|
90
|
+
error: cb
|
|
95
91
|
});
|
|
96
92
|
return () => s.unsubscribe();
|
|
97
93
|
}, [obs$]);
|
|
98
94
|
return progress;
|
|
99
95
|
}
|
|
100
|
-
function useStdoutDimensions() {
|
|
101
|
-
const { stdout } = useStdout();
|
|
102
|
-
const [dimensions, setDimensions] = useState([
|
|
103
|
-
stdout.columns,
|
|
104
|
-
stdout.rows
|
|
105
|
-
]);
|
|
106
|
-
useEffect(() => {
|
|
107
|
-
const handler = () => setDimensions([stdout.columns, stdout.rows]);
|
|
108
|
-
stdout.on('resize', handler);
|
|
109
|
-
return () => {
|
|
110
|
-
stdout.off('resize', handler);
|
|
111
|
-
};
|
|
112
|
-
}, [stdout]);
|
|
113
|
-
return dimensions;
|
|
114
|
-
}
|
package/dist/cloud.d.ts
ADDED
package/dist/cloud.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cborDecodeNum, NetCloud } from '@altronix/zdk';
|
|
2
|
+
import { Linq } from '@altronix/device';
|
|
3
|
+
import { concat, EmptyError, finalize, lastValueFrom, map, reduce, switchMap } from 'rxjs';
|
|
4
|
+
import logger from './logger.js';
|
|
5
|
+
import { select, first } from './select.js';
|
|
6
|
+
export async function getCloud() {
|
|
7
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
8
|
+
const linq = new Linq();
|
|
9
|
+
const path = '/api/v1/net/cloud';
|
|
10
|
+
const status = '/api/v1/net/cloud/status';
|
|
11
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => {
|
|
12
|
+
const o0 = linq
|
|
13
|
+
.get(sid, path)
|
|
14
|
+
.pipe(map((resp) => NetCloud.fromCbor(resp.body).toJson()));
|
|
15
|
+
const o1 = linq
|
|
16
|
+
.get(sid, status)
|
|
17
|
+
.pipe(map((resp) => ({ status: Number(cborDecodeNum(resp.body)) })));
|
|
18
|
+
return concat(o0, o1);
|
|
19
|
+
}), reduce((acc, curr) => ({ ...acc, ...curr })), finalize(() => linq.shutdown()));
|
|
20
|
+
return lastValueFrom(obs)
|
|
21
|
+
.then((resp) => console.log(JSON.stringify(resp, null, 2)))
|
|
22
|
+
.catch((e) => {
|
|
23
|
+
if (!(e instanceof EmptyError))
|
|
24
|
+
throw e;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export async function setCloud(endpoint) {
|
|
28
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
29
|
+
const [ip, port] = endpoint.split(':');
|
|
30
|
+
if (!port)
|
|
31
|
+
throw new Error('invalid format! enter ${ip}:${port}');
|
|
32
|
+
const body = new NetCloud({ ip, port: parseInt(port), portEn: true });
|
|
33
|
+
const path = '/api/v1/net/cloud';
|
|
34
|
+
const save = '/api/v1/exe/saveAndReboot';
|
|
35
|
+
const linq = new Linq();
|
|
36
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => {
|
|
37
|
+
const o0 = linq.put(sid, path, body);
|
|
38
|
+
const o1 = linq.get(sid, save);
|
|
39
|
+
return concat(o0, o1);
|
|
40
|
+
}), finalize(() => linq.shutdown()));
|
|
41
|
+
return lastValueFrom(obs)
|
|
42
|
+
.then(({ meta }) => console.log(JSON.stringify(meta.toJson(), null, 2)))
|
|
43
|
+
.catch((e) => {
|
|
44
|
+
if (!(e instanceof EmptyError))
|
|
45
|
+
throw e;
|
|
46
|
+
});
|
|
47
|
+
}
|
package/dist/exe.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export declare function reboot(this: Command): Promise<void>;
|
|
3
|
+
export declare function save(this: Command): Promise<void>;
|
|
4
|
+
export declare function saveAndReboot(this: Command): Promise<void>;
|
|
5
|
+
export declare function erase(this: Command): Promise<void>;
|
package/dist/exe.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Linq } from '@altronix/device';
|
|
2
|
+
import { EmptyError, finalize, firstValueFrom, switchMap } from 'rxjs';
|
|
3
|
+
import logger from './logger.js';
|
|
4
|
+
import { select, first } from './select.js';
|
|
5
|
+
export async function reboot() {
|
|
6
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
7
|
+
const linq = new Linq();
|
|
8
|
+
const path = '/api/v1/exe/reboot';
|
|
9
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => linq.get(sid, path)), finalize(() => linq.shutdown()));
|
|
10
|
+
return firstValueFrom(obs)
|
|
11
|
+
.then(({ meta }) => console.log(JSON.stringify(meta.toJson(), null, 2)))
|
|
12
|
+
.catch((e) => {
|
|
13
|
+
if (!(e instanceof EmptyError))
|
|
14
|
+
throw e;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export async function save() {
|
|
18
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
19
|
+
const linq = new Linq();
|
|
20
|
+
const path = '/api/v1/exe/save';
|
|
21
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => linq.get(sid, path)), finalize(() => linq.shutdown()));
|
|
22
|
+
return firstValueFrom(obs)
|
|
23
|
+
.then(({ meta }) => console.log(JSON.stringify(meta.toJson(), null, 2)))
|
|
24
|
+
.catch((e) => {
|
|
25
|
+
if (!(e instanceof EmptyError))
|
|
26
|
+
throw e;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export async function saveAndReboot() {
|
|
30
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
31
|
+
const linq = new Linq();
|
|
32
|
+
const path = '/api/v1/exe/saveAndReboot';
|
|
33
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => linq.get(sid, path)), finalize(() => linq.shutdown()));
|
|
34
|
+
return firstValueFrom(obs)
|
|
35
|
+
.then(({ meta }) => console.log(JSON.stringify(meta.toJson(), null, 2)))
|
|
36
|
+
.catch((e) => {
|
|
37
|
+
if (!(e instanceof EmptyError))
|
|
38
|
+
throw e;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export async function erase() {
|
|
42
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
43
|
+
const linq = new Linq();
|
|
44
|
+
const path = '/api/v1/exe/erase';
|
|
45
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => linq.get(sid, path)), finalize(() => linq.shutdown()));
|
|
46
|
+
return firstValueFrom(obs)
|
|
47
|
+
.then(({ meta }) => console.log(JSON.stringify(meta.toJson(), null, 2)))
|
|
48
|
+
.catch((e) => {
|
|
49
|
+
if (!(e instanceof EmptyError))
|
|
50
|
+
throw e;
|
|
51
|
+
});
|
|
52
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -3,49 +3,119 @@ import { program } from 'commander';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import { build } from './build.js';
|
|
6
|
-
|
|
6
|
+
import { getAbout, getSite, setSite } from './about.js';
|
|
7
|
+
import { reboot, save, saveAndReboot } from './exe.js';
|
|
8
|
+
import { getNet, setNet, setDhcp } from './net.js';
|
|
7
9
|
import { fileURLToPath } from 'url';
|
|
10
|
+
import { getCloud, setCloud } from './cloud.js';
|
|
11
|
+
import { stress } from './stress.js';
|
|
12
|
+
import { update } from './update.js';
|
|
13
|
+
import { listen } from './listen.js';
|
|
8
14
|
// https://stackoverflow.com/questions/8817423/why-is-dirname-not-defined-in-node-repl
|
|
9
15
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
// Parse package.json to get version
|
|
17
|
+
const pkg = path.resolve(__dirname, '..', 'package.json');
|
|
18
|
+
const ver = JSON.parse(fs.readFileSync(pkg, 'ascii')).version;
|
|
19
|
+
// const cwd = path.resolve('./');
|
|
20
|
+
program
|
|
21
|
+
.name('atx')
|
|
22
|
+
.description('build atx zdk projects')
|
|
23
|
+
.version(ver)
|
|
24
|
+
.option('-Q, --quiet', 'no logs')
|
|
25
|
+
.option('-VV, --verbose', 'extra logging');
|
|
26
|
+
// Scan command
|
|
27
|
+
/*
|
|
28
|
+
const ignore = 'node_modules;target;build;.git';
|
|
15
29
|
program
|
|
16
|
-
.
|
|
17
|
-
.description('
|
|
18
|
-
.
|
|
19
|
-
.option('-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
30
|
+
.command('scan')
|
|
31
|
+
.description('scan for *.cddl files')
|
|
32
|
+
.option('-p, --path <PATH>', 'root directory to start scan', cwd)
|
|
33
|
+
.option('-m, --matches <REGEX>', 'match expression', '.*cddl$')
|
|
34
|
+
.option('-i, --ignores <REGEX>', 'ignore directories', ignore)
|
|
35
|
+
.action(seedle.scan);
|
|
36
|
+
*/
|
|
37
|
+
// Build command
|
|
38
|
+
// TODO - detect if west is available and fail early
|
|
39
|
+
program
|
|
40
|
+
.command('build')
|
|
41
|
+
.description('build atx zdk application')
|
|
42
|
+
.option('-c, --config <CONFIG>', 'workspace config file')
|
|
43
|
+
.option('-C, --concurrent <NUMBER>', 'how many builds to run concurrently')
|
|
44
|
+
.option('-A, --application', 'build applications')
|
|
45
|
+
.option('-B, --bootloader', 'build bootloaders')
|
|
46
|
+
.option('-W, --wasm', 'build wasm')
|
|
47
|
+
.option('-y, --yes', 'answer yes automatically')
|
|
48
|
+
.action(build);
|
|
49
|
+
const device = program
|
|
50
|
+
.command('device')
|
|
51
|
+
.description('manage zephyr device settings')
|
|
52
|
+
.option('-p, --port <PORT>', 'listen port for incoming device connections')
|
|
53
|
+
.option('-v, --verbose', 'print extra debug informating during request')
|
|
54
|
+
.option('-f, --first', 'perform request on first device we see (any device)');
|
|
55
|
+
device
|
|
56
|
+
.command('get-about')
|
|
57
|
+
.description('get about data on the device')
|
|
58
|
+
.action(getAbout);
|
|
59
|
+
device
|
|
60
|
+
.command('get-site')
|
|
61
|
+
.description('get the site ID of the device')
|
|
62
|
+
.action(getSite);
|
|
63
|
+
device
|
|
64
|
+
.command('set-site')
|
|
65
|
+
.description('set the site ID of the device')
|
|
66
|
+
.argument('<site>', 'new site id')
|
|
67
|
+
.action(setSite);
|
|
68
|
+
device
|
|
69
|
+
.command('save')
|
|
70
|
+
.description('save data to persistant storage. (does not reboot)')
|
|
71
|
+
.action(save);
|
|
72
|
+
device
|
|
73
|
+
.command('reboot')
|
|
74
|
+
.description('reboot the device. (does not save)')
|
|
75
|
+
.action(reboot);
|
|
76
|
+
device
|
|
77
|
+
.command('save-reboot')
|
|
78
|
+
.description('save data to persistant storage and reboot the device')
|
|
79
|
+
.action(saveAndReboot);
|
|
80
|
+
device
|
|
81
|
+
.command('get-net')
|
|
82
|
+
.description('get network configuration from the device')
|
|
83
|
+
.action(getNet);
|
|
84
|
+
device
|
|
85
|
+
.command('set-net')
|
|
86
|
+
.description('set network interface into static ip mode')
|
|
87
|
+
.argument('<ip>', 'the new IP address')
|
|
88
|
+
.argument('<sn>', 'the new SUBNET mask')
|
|
89
|
+
.argument('<gw>', 'the new GATEWAY address')
|
|
90
|
+
.action(setNet);
|
|
91
|
+
device
|
|
92
|
+
.command('set-dhcp')
|
|
93
|
+
.description('set network interface into DHCP mode')
|
|
94
|
+
.action(setDhcp);
|
|
95
|
+
device
|
|
96
|
+
.command('get-cloud')
|
|
97
|
+
.description('get cloud endpoint on the device')
|
|
98
|
+
.action(getCloud);
|
|
99
|
+
device
|
|
100
|
+
.command('set-cloud')
|
|
101
|
+
.description('set cloud endpoint on the device')
|
|
102
|
+
.argument('<endpoint>', 'cloud service location')
|
|
103
|
+
.action(setCloud);
|
|
104
|
+
device
|
|
105
|
+
.command('stress')
|
|
106
|
+
.description('run a stress test on a device')
|
|
107
|
+
.argument('<count>', 'how many requests to make')
|
|
108
|
+
.action(stress);
|
|
109
|
+
device
|
|
110
|
+
.command('update')
|
|
111
|
+
.description('run firmware update from file')
|
|
112
|
+
.argument('<file>', 'file to update device with')
|
|
113
|
+
.action(update);
|
|
114
|
+
device
|
|
115
|
+
.command('listen')
|
|
116
|
+
.description('listen for alerts and heartbeats')
|
|
117
|
+
.argument('<duration>', 'how long to listen')
|
|
118
|
+
.action(listen);
|
|
119
|
+
// Load plugins
|
|
120
|
+
// (await plugins()).forEach(({ plugin: _, path: __, description: ___ }) => {});
|
|
121
|
+
program.parseAsync().catch((e) => console.error(e));
|
package/dist/listen.d.ts
ADDED
package/dist/listen.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Linq } from '@altronix/device';
|
|
2
|
+
import { first, map, merge, timer } from 'rxjs';
|
|
3
|
+
import logger from './logger.js';
|
|
4
|
+
import { quit } from './quit.js';
|
|
5
|
+
export async function listen(ms) {
|
|
6
|
+
const log = logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
7
|
+
const linq = new Linq();
|
|
8
|
+
const abort = ms > 0
|
|
9
|
+
? merge(timer(ms), quit()).pipe(first(), map(() => void 0))
|
|
10
|
+
: quit();
|
|
11
|
+
abort.subscribe(() => linq.shutdown());
|
|
12
|
+
linq.connections().subscribe(({ about, transport }) => {
|
|
13
|
+
log.info('new connection', { ...about, transport });
|
|
14
|
+
});
|
|
15
|
+
linq.alerts().subscribe(({ about, transport, alert }) => {
|
|
16
|
+
const { sid: serial } = about;
|
|
17
|
+
log.info('new alert', { serial, transport, ...alert.toJson() });
|
|
18
|
+
});
|
|
19
|
+
linq.heartbeats().subscribe(({ about, transport, heartbeat }) => {
|
|
20
|
+
const { sid: serial } = about;
|
|
21
|
+
log.info('new heartbeat', { serial, transport, ...heartbeat.toJson() });
|
|
22
|
+
});
|
|
23
|
+
}
|
package/dist/logger.d.ts
ADDED
package/dist/logger.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { timestamp, inliner, installLogger } from '@altronix/device';
|
|
2
|
+
import { createLogger, format, transports } from 'winston';
|
|
3
|
+
export default (level) => installLogger(createLogger({
|
|
4
|
+
level,
|
|
5
|
+
silent: level ? false : true,
|
|
6
|
+
format: format.combine(inliner(), timestamp()),
|
|
7
|
+
transports: [new transports.Console()]
|
|
8
|
+
}));
|
package/dist/net.d.ts
ADDED
package/dist/net.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { concat, EmptyError, finalize, firstValueFrom, lastValueFrom, map, switchMap } from 'rxjs';
|
|
2
|
+
import { Linq } from '@altronix/device';
|
|
3
|
+
import { NetIp } from '@altronix/zdk';
|
|
4
|
+
import logger from './logger.js';
|
|
5
|
+
import { select, first } from './select.js';
|
|
6
|
+
export async function getNet() {
|
|
7
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
8
|
+
const linq = new Linq();
|
|
9
|
+
const path = '/api/v1/net/ip';
|
|
10
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => linq.get(sid, path)), map((resp) => NetIp.fromCbor(resp.body)), finalize(() => linq.shutdown()));
|
|
11
|
+
return firstValueFrom(obs)
|
|
12
|
+
.then((resp) => console.log(JSON.stringify(resp.toJson(), null, 2)))
|
|
13
|
+
.catch((e) => {
|
|
14
|
+
if (!(e instanceof EmptyError))
|
|
15
|
+
throw e;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export async function setNet(ip, sn, gw) {
|
|
19
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
20
|
+
const linq = new Linq();
|
|
21
|
+
const path = '/api/v1/net/ip';
|
|
22
|
+
const save = '/api/v1/exe/saveAndReboot';
|
|
23
|
+
const body = new NetIp({ ip, sn, gw, dhcp: false });
|
|
24
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => {
|
|
25
|
+
const o0 = linq.put(sid, path, body);
|
|
26
|
+
const o1 = linq.get(sid, save);
|
|
27
|
+
return concat(o0, o1);
|
|
28
|
+
}), finalize(() => linq.shutdown()));
|
|
29
|
+
return lastValueFrom(obs)
|
|
30
|
+
.then(({ meta }) => console.log(JSON.stringify(meta.toJson(), null, 2)))
|
|
31
|
+
.catch((e) => {
|
|
32
|
+
if (!(e instanceof EmptyError))
|
|
33
|
+
throw e;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export async function setDhcp() {
|
|
37
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
38
|
+
const linq = new Linq();
|
|
39
|
+
const path = '/api/v1/net/ip';
|
|
40
|
+
const save = '/api/v1/exe/saveAndReboot';
|
|
41
|
+
const body = new NetIp({ dhcp: true });
|
|
42
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((sid) => {
|
|
43
|
+
const o0 = linq.put(sid, path, body);
|
|
44
|
+
const o1 = linq.get(sid, save);
|
|
45
|
+
return concat(o0, o1);
|
|
46
|
+
}), finalize(() => linq.shutdown()));
|
|
47
|
+
return lastValueFrom(obs)
|
|
48
|
+
.then(({ meta }) => console.log(JSON.stringify(meta.toJson(), null, 2)))
|
|
49
|
+
.catch((e) => {
|
|
50
|
+
if (!(e instanceof EmptyError))
|
|
51
|
+
throw e;
|
|
52
|
+
});
|
|
53
|
+
}
|
package/dist/progress.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import Progress from './progress.ui.js';
|
|
5
|
+
export default function (total) {
|
|
6
|
+
return (obs$) => new Observable((subscriber) => {
|
|
7
|
+
const renderer = render(React.createElement(Progress, { total: total, "response$": obs$, onComplete: () => {
|
|
8
|
+
renderer.unmount();
|
|
9
|
+
subscriber.next();
|
|
10
|
+
subscriber.complete();
|
|
11
|
+
} }));
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
interface OnCompleteCallback {
|
|
4
|
+
(): void;
|
|
5
|
+
}
|
|
6
|
+
interface ProgressOptions<R> {
|
|
7
|
+
total: number;
|
|
8
|
+
response$: Observable<R>;
|
|
9
|
+
onComplete: OnCompleteCallback;
|
|
10
|
+
}
|
|
11
|
+
export default function Progress<R>({ total, response$, onComplete }: ProgressOptions<R>): React.JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { useLayoutEffect, useState } from 'react';
|
|
2
|
+
import { scan } from 'rxjs';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import useDimensions from './useStdoutDimensions.js';
|
|
5
|
+
export default function Progress({ total, response$, onComplete }) {
|
|
6
|
+
const [cols] = useDimensions();
|
|
7
|
+
const [width, setWidth] = useState(Math.min(cols - 4, 76));
|
|
8
|
+
const [progress, empty] = useProgress(response$, total, width, onComplete);
|
|
9
|
+
useLayoutEffect(() => setWidth(Math.min(cols - 4, 80)), [width]);
|
|
10
|
+
return (React.createElement(Box, null,
|
|
11
|
+
React.createElement(Box, null,
|
|
12
|
+
React.createElement(Text, null, "["),
|
|
13
|
+
new Array(progress).fill(0).map((_, idx) => (React.createElement(Text, { key: idx }, "#"))),
|
|
14
|
+
new Array(empty).fill(0).map((_, idx) => (React.createElement(Text, { key: idx }, "-"))),
|
|
15
|
+
React.createElement(Text, null, "]")),
|
|
16
|
+
React.createElement(Box, { marginLeft: 1 },
|
|
17
|
+
React.createElement(Text, null,
|
|
18
|
+
Math.ceil((100 * progress) / width),
|
|
19
|
+
"%"))));
|
|
20
|
+
}
|
|
21
|
+
function useProgress(obs$, total, width, onComplete) {
|
|
22
|
+
const [progress, setProgress] = useState([0, width]);
|
|
23
|
+
useLayoutEffect(() => {
|
|
24
|
+
const s = obs$.pipe(scan((acc) => acc + 1, 0)).subscribe({
|
|
25
|
+
complete: onComplete,
|
|
26
|
+
error: (error) => console.error(error),
|
|
27
|
+
next: (n) => {
|
|
28
|
+
const progress = Math.ceil((width * n) / total);
|
|
29
|
+
setProgress([progress, width - progress]);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return () => s.unsubscribe();
|
|
33
|
+
}, [obs$, width, total]);
|
|
34
|
+
return progress;
|
|
35
|
+
}
|
package/dist/quit.d.ts
ADDED
package/dist/quit.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { emitKeypressEvents } from 'node:readline';
|
|
3
|
+
export function quit() {
|
|
4
|
+
return new Observable((subscriber) => {
|
|
5
|
+
if (process.stdin.isTTY) {
|
|
6
|
+
process.stdin.setRawMode(true);
|
|
7
|
+
}
|
|
8
|
+
emitKeypressEvents(process.stdin);
|
|
9
|
+
function onQuit() {
|
|
10
|
+
subscriber.next();
|
|
11
|
+
subscriber.complete();
|
|
12
|
+
}
|
|
13
|
+
function onkeypress(data) {
|
|
14
|
+
if (data.indexOf('q') >= 0)
|
|
15
|
+
onQuit();
|
|
16
|
+
}
|
|
17
|
+
process.stdin.on('keypress', onkeypress);
|
|
18
|
+
process.stdin.on('error', onQuit);
|
|
19
|
+
process.stdin.on('end', onQuit);
|
|
20
|
+
process.stdin.on('close', onQuit);
|
|
21
|
+
return () => {
|
|
22
|
+
// https://stackoverflow.com/questions/59220095/node-doesnt-exit-automatically-once-a-listener-is-set-on-stdin
|
|
23
|
+
process.stdin.unref();
|
|
24
|
+
process.stdin.off('keypress', onkeypress);
|
|
25
|
+
process.stdin.off('error', onQuit);
|
|
26
|
+
process.stdin.off('end', onQuit);
|
|
27
|
+
process.stdin.off('close', onQuit);
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
package/dist/select.d.ts
ADDED
package/dist/select.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { first as rxFirst, map, Observable } from 'rxjs';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import Select from './select.ui.js';
|
|
5
|
+
export function select() {
|
|
6
|
+
return (obs$) => new Observable((subscriber) => {
|
|
7
|
+
const renderer = render(React.createElement(Select, { "connections$": obs$.pipe(map(({ about }) => about)), onSelect: (result) => {
|
|
8
|
+
renderer.unmount();
|
|
9
|
+
renderer.cleanup();
|
|
10
|
+
if (result)
|
|
11
|
+
subscriber.next(result);
|
|
12
|
+
subscriber.complete();
|
|
13
|
+
} }));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export function first() {
|
|
17
|
+
return (obs$) => obs$.pipe(rxFirst(), map(({ about }) => about.sid));
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AboutProps } from '@altronix/zdk';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
export interface OnSelectCallback {
|
|
5
|
+
(serial?: string): void;
|
|
6
|
+
}
|
|
7
|
+
export interface Options {
|
|
8
|
+
connections$: Observable<AboutProps>;
|
|
9
|
+
onSelect: OnSelectCallback;
|
|
10
|
+
}
|
|
11
|
+
export default function ({ connections$, onSelect }: Options): React.JSX.Element;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useLayoutEffect, useState } from 'react';
|
|
2
|
+
import { scan } from 'rxjs';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
// Main ui component
|
|
5
|
+
export default function ({ connections$, onSelect }) {
|
|
6
|
+
const connections = useConnections(connections$);
|
|
7
|
+
const dimensions = useTableDimensions(connections);
|
|
8
|
+
const [selected, setSelected] = useState(0);
|
|
9
|
+
useInput((input, key) => {
|
|
10
|
+
if (input === 'q')
|
|
11
|
+
onSelect();
|
|
12
|
+
if (input === 'k' || key.upArrow) {
|
|
13
|
+
setSelected(selected == 0 ? connections.length - 1 : selected - 1);
|
|
14
|
+
}
|
|
15
|
+
else if (input === 'j' || key.downArrow) {
|
|
16
|
+
setSelected(selected == connections.length - 1 ? 0 : selected + 1);
|
|
17
|
+
}
|
|
18
|
+
else if (key.return && connections[selected]) {
|
|
19
|
+
onSelect(connections[selected].sid);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return (React.createElement(Box, { flexDirection: "column", width: 80, marginBottom: 1 },
|
|
23
|
+
React.createElement(Box, { flexDirection: "column", width: 80, marginBottom: 1 },
|
|
24
|
+
React.createElement(Text, { underline: true }, "Please select device for request"),
|
|
25
|
+
React.createElement(Text, { dimColor: true },
|
|
26
|
+
'\u2191',
|
|
27
|
+
" move cursor up"),
|
|
28
|
+
React.createElement(Text, { dimColor: true },
|
|
29
|
+
'\u2193',
|
|
30
|
+
" move cursor down"),
|
|
31
|
+
React.createElement(Text, { dimColor: true },
|
|
32
|
+
'\u23ce',
|
|
33
|
+
" make request"),
|
|
34
|
+
React.createElement(Text, { dimColor: true }, "Q to quit")),
|
|
35
|
+
connections.map(({ sid, board, site }, idx) => (React.createElement(Box, { key: idx },
|
|
36
|
+
React.createElement(Box, { width: 2, marginRight: 1 },
|
|
37
|
+
React.createElement(Text, { bold: selected == idx, underline: selected == idx },
|
|
38
|
+
idx,
|
|
39
|
+
":")),
|
|
40
|
+
React.createElement(Box, { width: dimensions[0], marginRight: 1 },
|
|
41
|
+
React.createElement(Text, { wrap: "truncate", color: "yellow", bold: selected == idx, underline: selected == idx }, board)),
|
|
42
|
+
React.createElement(Box, { width: dimensions[1], marginRight: 1 },
|
|
43
|
+
React.createElement(Text, { wrap: "truncate", color: "blue", bold: selected == idx, underline: selected == idx }, site)),
|
|
44
|
+
React.createElement(Box, { width: dimensions[2], marginRight: 1 },
|
|
45
|
+
React.createElement(Text, { wrap: "truncate", color: "magenta", bold: selected == idx, underline: selected == idx }, sid)))))));
|
|
46
|
+
}
|
|
47
|
+
function useConnections(obs$) {
|
|
48
|
+
const [connections, setConnections] = useState([]);
|
|
49
|
+
useLayoutEffect(() => {
|
|
50
|
+
const s = obs$
|
|
51
|
+
.pipe(scan((acc, next) => [...acc, next], connections))
|
|
52
|
+
.subscribe({ next: (connections) => setConnections(connections) });
|
|
53
|
+
return () => s.unsubscribe();
|
|
54
|
+
}, [obs$]);
|
|
55
|
+
return connections;
|
|
56
|
+
}
|
|
57
|
+
function useTableDimensions(connections) {
|
|
58
|
+
const [dimensions, setDimensions] = useState([0, 0, 0]);
|
|
59
|
+
useLayoutEffect(() => {
|
|
60
|
+
const next = connections
|
|
61
|
+
.map((a) => [a.board.length, a.site.length, a.sid.length])
|
|
62
|
+
.reduce((acc, next) => {
|
|
63
|
+
return [
|
|
64
|
+
next[0] > acc[0] ? next[0] : acc[0],
|
|
65
|
+
next[1] > acc[1] ? next[1] : acc[1],
|
|
66
|
+
next[2] > acc[2] ? next[2] : acc[2]
|
|
67
|
+
];
|
|
68
|
+
}, dimensions);
|
|
69
|
+
setDimensions(next);
|
|
70
|
+
}, [connections]);
|
|
71
|
+
return dimensions;
|
|
72
|
+
}
|
package/dist/stress.d.ts
ADDED
package/dist/stress.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { METH_CONSTANTS } from '@altronix/zdk';
|
|
2
|
+
import { Linq } from '@altronix/device';
|
|
3
|
+
import { EmptyError, finalize, lastValueFrom, switchMap, pipe, repeat } from 'rxjs';
|
|
4
|
+
import progress from './progress.js';
|
|
5
|
+
import logger from './logger.js';
|
|
6
|
+
import { select, first } from './select.js';
|
|
7
|
+
export async function stress(n) {
|
|
8
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
9
|
+
const linq = new Linq();
|
|
10
|
+
const path = '/api/v1/about';
|
|
11
|
+
const request = {
|
|
12
|
+
path,
|
|
13
|
+
meth: METH_CONSTANTS.GET,
|
|
14
|
+
retry: 100,
|
|
15
|
+
timeout: 500
|
|
16
|
+
};
|
|
17
|
+
const progress$ = pipe(repeat(n), progress(n));
|
|
18
|
+
const obs = linq.connections().pipe(this.optsWithGlobals()['first'] ? first() : select(), switchMap((id) => linq.request(id, request).pipe(progress$)), finalize(() => linq.shutdown()));
|
|
19
|
+
return lastValueFrom(obs).catch((e) => {
|
|
20
|
+
if (!(e instanceof EmptyError))
|
|
21
|
+
throw e;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React, { useLayoutEffect, useState } from 'react';
|
|
2
|
+
import { Observable, scan } from 'rxjs';
|
|
3
|
+
import { Box, render, Text } from 'ink';
|
|
4
|
+
export default function () {
|
|
5
|
+
return (obs$) => new Observable((subscriber) => {
|
|
6
|
+
const renderer = render(React.createElement(View, { "response$": obs$, onComplete: () => {
|
|
7
|
+
renderer.unmount();
|
|
8
|
+
subscriber.next();
|
|
9
|
+
subscriber.complete();
|
|
10
|
+
} }));
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function View({ response$, onComplete }) {
|
|
14
|
+
const responses = useResponses(response$, onComplete);
|
|
15
|
+
return (React.createElement(Box, null,
|
|
16
|
+
React.createElement(Text, null, "Response count:"),
|
|
17
|
+
React.createElement(Text, null, responses.length)));
|
|
18
|
+
}
|
|
19
|
+
function useResponses(obs$, onComplete) {
|
|
20
|
+
const [responses, setResponses] = useState([]);
|
|
21
|
+
useLayoutEffect(() => {
|
|
22
|
+
const s = obs$
|
|
23
|
+
.pipe(scan((acc, next) => [...acc, next], responses))
|
|
24
|
+
.subscribe({
|
|
25
|
+
next: (responses) => setResponses(responses),
|
|
26
|
+
complete: onComplete
|
|
27
|
+
});
|
|
28
|
+
return () => s.unsubscribe();
|
|
29
|
+
}, [obs$]);
|
|
30
|
+
return responses;
|
|
31
|
+
}
|
package/dist/update.d.ts
ADDED
package/dist/update.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Linq } from '@altronix/device';
|
|
2
|
+
import { Update } from '@altronix/zdk';
|
|
3
|
+
import { concat, defer, EmptyError, finalize, from, lastValueFrom, map, mergeScan, switchMap } from 'rxjs';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import logger from './logger.js';
|
|
6
|
+
import { select, first } from './select.js';
|
|
7
|
+
import progress from './progress.js';
|
|
8
|
+
function chunks(data, size) {
|
|
9
|
+
let arr = [];
|
|
10
|
+
let remainder = data.length % size;
|
|
11
|
+
function slicer(n) {
|
|
12
|
+
for (let i = 0; i < n; i++) {
|
|
13
|
+
let idx = i * size;
|
|
14
|
+
arr.push(data.slice(idx, idx + size));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (remainder == 0) {
|
|
18
|
+
slicer(data.length / size);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
let n = Math.ceil(data.length / size);
|
|
22
|
+
let end = size * (n - 1);
|
|
23
|
+
slicer(n - 1);
|
|
24
|
+
let last = new Uint8Array(size);
|
|
25
|
+
last.set(data.slice(end));
|
|
26
|
+
arr.push(last);
|
|
27
|
+
}
|
|
28
|
+
return arr;
|
|
29
|
+
}
|
|
30
|
+
function updateMap(data, idx) {
|
|
31
|
+
let update = new Update({ data, offset: idx * 512 });
|
|
32
|
+
// NOTE: Have to initialize the Uint8Array across binding this way
|
|
33
|
+
// https://gitlab.altronix.com/software-engineering/sdk/atx-zdk/-/issues/1
|
|
34
|
+
update.data = data;
|
|
35
|
+
return update;
|
|
36
|
+
}
|
|
37
|
+
function updater(linq, serial) {
|
|
38
|
+
return (acc, update) => linq
|
|
39
|
+
.put(serial, '/api/v1/update/transfer', update.cbor())
|
|
40
|
+
.pipe(map(({ meta }) => ({ ...meta, remaining: acc.remaining - 1 })));
|
|
41
|
+
}
|
|
42
|
+
export async function update(file) {
|
|
43
|
+
logger(this.optsWithGlobals()['verbose'] ? 'silly' : 'info');
|
|
44
|
+
const linq = new Linq();
|
|
45
|
+
const dev$ = linq
|
|
46
|
+
.connections()
|
|
47
|
+
.pipe(this.optsWithGlobals()['first'] ? first() : select());
|
|
48
|
+
const obs = from(fs.promises.readFile(file)).pipe(switchMap((bin) => dev$.pipe(map((serial) => ({ serial, bin })))), switchMap(({ serial, bin }) => {
|
|
49
|
+
const update = chunks(bin, 512).map(updateMap);
|
|
50
|
+
const start$ = defer(() => linq.get(serial, '/api/v1/update/start'));
|
|
51
|
+
const finish$ = defer(() => linq.get(serial, '/api/v1/update/finish'));
|
|
52
|
+
const transfer$ = from(update).pipe(mergeScan(updater(linq, serial), { remaining: update.length }, 1));
|
|
53
|
+
return concat(start$, transfer$, finish$).pipe(progress(update.length));
|
|
54
|
+
}), finalize(() => linq.shutdown()));
|
|
55
|
+
return lastValueFrom(obs).catch((e) => {
|
|
56
|
+
if (!(e instanceof EmptyError))
|
|
57
|
+
throw e;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function useStdoutDimensions(): [number, number];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useStdout } from 'ink';
|
|
3
|
+
export default function useStdoutDimensions() {
|
|
4
|
+
const { stdout } = useStdout();
|
|
5
|
+
const [dimensions, setDimensions] = useState([
|
|
6
|
+
stdout.columns,
|
|
7
|
+
stdout.rows
|
|
8
|
+
]);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const handler = () => setDimensions([stdout.columns, stdout.rows]);
|
|
11
|
+
stdout.on('resize', handler);
|
|
12
|
+
return () => {
|
|
13
|
+
stdout.off('resize', handler);
|
|
14
|
+
};
|
|
15
|
+
}, [stdout]);
|
|
16
|
+
return dimensions;
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altronix/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.15",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"bin": {
|
|
6
7
|
"atx": "./dist/index.js"
|
|
7
8
|
},
|
|
8
|
-
"type": "module",
|
|
9
9
|
"engines": {
|
|
10
10
|
"node": ">=16"
|
|
11
11
|
},
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
"ink": "^4.1.0",
|
|
21
21
|
"jsonc-parser": "^3.3.1",
|
|
22
22
|
"react": "^18.2.0",
|
|
23
|
-
"rxjs": "^7.8.1"
|
|
23
|
+
"rxjs": "^7.8.1",
|
|
24
|
+
"winston": "^3.13.0",
|
|
25
|
+
"@altronix/device": "0.6.13",
|
|
26
|
+
"@altronix/zdk": "0.7.0"
|
|
24
27
|
},
|
|
25
28
|
"devDependencies": {
|
|
26
29
|
"@sindresorhus/tsconfig": "^3.0.1",
|