dcp-client 5.1.11 → 5.3.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/index.js CHANGED
@@ -1305,16 +1305,15 @@ exports.createConfigFragments = async function dcpClient$$createConfigFragments(
1305
1305
  addConfigFile(localConfig, resourceDir, 'dcp-config');
1306
1306
  await addConfigRKey(localConfig, 'HKCU', 'dcp/dcp-config');
1307
1307
  addConfigFile(localConfig, resourceDir, `${programName}/dcp-config`);
1308
- await addConfigRKey(localConfig, 'HKCU', `dcp/${programName}/dcp-config`);
1308
+ await addConfigRKey(localConfig, 'HKCU', `${programName}/dcp-config`);
1309
1309
  addConfig (localConfig, initConfig);
1310
1310
  addConfigEnv (localConfig, 'DCP_CONFIG_');
1311
1311
  addConfig (localConfig, mkEnvConfig());
1312
1312
  addConfig (localConfig, mkCliConfig(options));
1313
1313
  addConfigFile(localConfig, etc, `dcp/${programName}/dcp-config`);
1314
- await addConfigRKey(localConfig, 'HKLM', `dcp/${programName}/dcp-config`);
1314
+ await addConfigRKey(localConfig, 'HKLM', `${programName}/dcp-config`);
1315
1315
  addConfigFile(localConfig, etc, 'dcp/override-dcp-config');
1316
1316
  await addConfigRKey(localConfig, 'HKLM', 'dcp/override-dcp-config');
1317
- await addConfigRKey(localConfig, 'HKLM', 'dcp-client/dcp-config'); /* legacy - used by screen saver, /wg sep'22 */
1318
1317
 
1319
1318
  /* 5. Use the aggregate of the default and local configs to figure out where the scheduler is. Use
1320
1319
  * this to figure where the web config is and where an auto-update bundle would be if auto-update
@@ -1351,8 +1350,10 @@ exports.createConfigFragments = async function dcpClient$$createConfigFragments(
1351
1350
  {
1352
1351
  if (reportErrors !== false)
1353
1352
  {
1354
- console.error('Error: dcp-client::init could not fetch scheduler configuration at ' + aggrConfig.scheduler.configLocation);
1355
- console.debug(require('dcp/utils').justFetchPrettyError(error));
1353
+ const code = error.code ? ` (${error.code})` : '';
1354
+ console.error('Error: dcp-client::init could not fetch scheduler configuration at ' + aggrConfig.scheduler.configLocation + code);
1355
+ if (error.code !== 'EHTTP_STATUS_ECONNREFUSED')
1356
+ console.debug(require('dcp/utils').justFetchPrettyError(error));
1356
1357
  process.exit(1);
1357
1358
  }
1358
1359
  throw error;
package/lib/getopt.js ADDED
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @file getopt.js - utility functions for parsing command-line options for DCP programs.
3
+ * Depends on posix-getopt npm library.
4
+ * @author Wes Garland, wes@distributive.network
5
+ * @date Nov 2025
6
+ */
7
+ 'use strict';
8
+
9
+ exports.parse = parse;
10
+ exports.uniq = uniq;
11
+
12
+ /**
13
+ * A posix getopt parser with a nice UX and API.
14
+ * - accepts optionsDefn string like posix-getopt module, but understands that the character * means
15
+ * long option only, and the -- option means to stop processing.
16
+ * - is a generator that yields for each option
17
+ * - option: character or long option when character is *.
18
+ * Magic option -- means that argvSegment is remainder of argv.
19
+ * - optarg: argument to option
20
+ * - optind: position of option character in argv
21
+ * - argvSegment: elements of argv used to make up option and optarg
22
+ *
23
+ * @note this generator intercepts process.stderr for the duration of iteration
24
+ * @note option characters above \u1000 are not supported
25
+ *
26
+ * @example parse(process.argv.slice(2), 'vh(help)*:(earnings-account)p:(port)')
27
+ * -v
28
+ * -h, --help
29
+ * --earnings-account=0xc0ffee
30
+ * -p123, --port=123
31
+ *
32
+ * @param {Array} argv array of options to parse
33
+ * @param {string} optionsDefn
34
+ * @throws {Error} when the options string is invalid
35
+ */
36
+ function *parse(argv, optionsDefn)
37
+ {
38
+ const { BasicParser } = require('posix-getopt');
39
+ var longOptCount = 0;
40
+
41
+ optionsDefn = optionsDefn /* patchup long opt * to > \u1000 chars */
42
+ .match(/[A-Za-z*]:?(\([^)]*\))?/g)
43
+ .map(s => s[0] === '*' ? String.fromCharCode(++longOptCount + 0x1000) + s.slice(1) : s).join('');
44
+ const optionChars = optionsDefn.match(/[A-Za-z\u1000-\u1100/](:?\([^)]*\))?/g).map(s=>s[0]);
45
+ const uniqueOptionChars = uniq(optionChars);
46
+ if (optionChars.length !== uniqueOptionChars.length)
47
+ {
48
+ uniqueOptionChars.forEach(ch => {
49
+ if (optionChars.lastIndexOf(ch) !== optionChars.indexOf(ch))
50
+ throw new Error(`getopt - option character '${ch}' used more than once`);
51
+ });
52
+ throw new Error('unreachable code');
53
+ }
54
+
55
+ argv = process.argv.slice(0,2).concat(argv);
56
+ const parser = new BasicParser(optionsDefn, argv);
57
+ const psw = process.stderr.write;
58
+ process.stderr.write = function patchupGetoptErrorOutput(s, ...args) {
59
+ /* posix-getopt library has useless chr for long opts when stderr.write('option requires an argument -- ' + chr + '\n'); */
60
+ if (s.startsWith('option requires an argument -- '))
61
+ s = `option ${argv[parser.optind() - 1]} requires an argument\n`;
62
+ psw.call(process.stderr, s, ...args);
63
+ };
64
+
65
+ for (let opthnd, lastOpt=2; (opthnd = parser.getopt()); lastOpt = parser.optind())
66
+ {
67
+ let option;
68
+
69
+ /* To fake long-only-opts, we use short opts >= \u1000), and present only the long opt below */
70
+ if (opthnd.option < '\u1000')
71
+ option = opthnd.option;
72
+ else
73
+ option = (Object.entries(parser.gop_aliases).filter(longShort => longShort[1] === opthnd.option)[0] || ['?'])[0];
74
+
75
+ yield { option, optarg: opthnd.optarg, optind: parser.optind() - 2, argvSegment: argv.slice(lastOpt, parser.optind()) };
76
+ if (opthnd.error)
77
+ break;
78
+ }
79
+
80
+ if (argv.length !== parser.optind())
81
+ yield { option: '--', optind: parser.optind(), argvSegment: argv.slice(parser.optind()) };
82
+
83
+ process.stderr.write = psw; // eslint-disable-line require-atomic-updates
84
+ }
85
+
86
+ /**
87
+ * Return a new array composed only of unique elements of arr
88
+ * @param {Array} arr
89
+ * @returns {Array}
90
+ */
91
+ function uniq(array)
92
+ {
93
+ return array.filter((val, idx, arr) => arr.indexOf(val) === idx);
94
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "dcp-client",
3
- "version": "5.1.11",
3
+ "version": "5.3.0",
4
4
  "dcp": {
5
- "version": "9f78dfbb5671e31a2a41670c694fd05a56962d65",
5
+ "version": "3eaced9015812e24ed08f2a0432c53b493483c37",
6
6
  "repository": "git@gitlab.com:Distributed-Compute-Protocol/dcp.git"
7
7
  },
8
8
  "description": "Core libraries for accessing DCP network",
@@ -21,7 +21,8 @@
21
21
  "author": "Distributive",
22
22
  "main": "index",
23
23
  "bin": {
24
- "dcp-config-value": "bin/dcp-config-value"
24
+ "dcp-config-value": "bin/dcp-config-value",
25
+ "dcp-bank": "bin/dcp-bank"
25
26
  },
26
27
  "directories": {
27
28
  "example": "examples"
@@ -45,11 +46,12 @@
45
46
  "http-proxy-agent": "^4.0.1",
46
47
  "https-agent": "^1.0.0",
47
48
  "https-proxy-agent": "^5.0.0",
48
- "kvin": "^1.2.18",
49
+ "kvin": "^9.0.0",
49
50
  "memfs": "4.17.2",
50
51
  "nanoid": "3.3.11",
51
52
  "physical-cpu-count": "^2.0.0",
52
53
  "polyfill-crypto.getrandomvalues": "^1.0.0",
54
+ "posix-getopt": "1.2.1",
53
55
  "regedit": "^3.0.3",
54
56
  "semver": "^7.3.5",
55
57
  "socket.io-client": "4.8.0",
@@ -1,156 +1,154 @@
1
- /**
2
- * @file windows-registry.js
3
- * Windows registry configuration/integration utility for DCP. All exported functions
4
- * return false on non-Windows platforms.
5
- *
6
- * Basic idea: summarize registry into an objects which can be used as the
7
- * initializer object for DCP Client and wallet cache. Multiple hives are read
8
- * to support higher-priviledged overrides for Administrators writing Group
9
- * Policy files in Enterprise installs.
10
- *
11
- * Note: In registry-speak, keys are like directories and values are like files -- i.e. a key can
12
- * contain keys and/or values, but values cannot contain keys; only strings and numbers and stuff.
13
- *
14
- *
15
- * future - we will need a better way to return registry values, current proposal is to use
16
- * REG_BINARY to encode richer types thus:
17
- * 0x00 - null
18
- * 0x20 - next 8 bytes are a little-endian float64
19
- * 0x40 - next byte is start of Uint8Array
20
- * 0x80 - false
21
- * 0x81 - true
22
- * 0x82 - undefined
23
- * future - we should probably expand our understanding of types as follows
24
- * REG_DWORD - integer
25
- * REG_QWORD - bigint
26
- * REG_NONE - treat as missing
27
- *
28
- * @author Wes Garland, wes@kingsds.network
29
- * @date Jul 2020
30
- */
31
- const machHive = 'HKLM';
32
- const userHive = 'HKCU';
33
- const regedit = require('regedit');
34
-
35
- exports.baseKey = process.env.DCP_REGISTRY_BASEKEY || 'Software\\Distributive\\DCP';
36
-
37
- /** Join multiple registry keys fragments together into a full path.
38
- * @param {string|Array} ...
39
- * @returns {string}
40
- */
41
- function keyJoin(/*...*/) {
42
- var args = Array.from(arguments);
43
- var flat = args.reduce((arr, x) => arr.concat(x), []);
44
-
45
- return flat.join('\\');
46
- }
47
-
48
- /** Get all of the entries for a given key. Keys are returned in a property
49
- * named keys. Values are returned in a property named values.
50
- */
51
- async function getEntries(key) {
52
- var entries = {};
53
-
54
- return new Promise(function (resolve, reject) {
55
- try {
56
- let stream = regedit.list([key]);
57
-
58
- stream.on('data', function(entry) {
59
- entries[entry.key] = entry.data;
60
- });
61
-
62
- stream.on('finish', function () {
63
- resolve(entries);
64
- })
65
- } catch(e) {
66
- reject(e);
67
- }
68
- })
69
- }
70
-
71
- /** Get all of the keys for a given key */
72
- async function getKeys(key) {
73
- let entries = await getEntries(key);
74
- return entries[key].keys || [];
75
- }
76
-
77
- /** Get all of the values for a given key */
78
- async function getValues(key) {
79
- let entries = await getEntries(key);
80
- return entries[key].values || {};
81
- }
82
-
83
- /** Returns true if a given key exists in the registry. Strange algorithm works
84
- * around bugs in the npm regedit package.
85
- *
86
- * @param {string} key The key to check, eg HKLM\\Software\\Kings Distributed Systems
87
- * @returns Promise which resolves to boolean
88
- */
89
- async function keyExists(key) {
90
- var lhs, rhs;
91
-
92
- for (rhs = key.split('\\'), lhs = rhs.splice(0,2).join('\\');
93
- rhs.length;
94
- lhs += '\\' + rhs.shift()) {
95
- let subKeys = await getKeys(lhs);
96
- if ((subKeys.map((a) => a.toUpperCase()).filter((a) => a === rhs[0].toUpperCase())).length === 0)
97
- return false;
98
- }
99
- return true;
100
- }
101
-
102
- /**
103
- * Return the registry tree, including both keys and values, from the rootPath on down.
104
- * keys are represented as JS objects, values are represented as JS values. For example,
105
- * a registry with \\HKCU\KDS\dcp-client\scheduler\location = 'https://scheduler.kds.net'
106
- * would become { 'dcp-client': scheduler: { location: 'https://scheduler.kds.net' } } if
107
- * the rootPath were \\HKCU\KDS.
108
- *
109
- * @param {string} rootPath The registry key to traverse
110
- * @param {object} tree [optional] the JS object to decorate with the registry tree
111
- * @returns tree or a newly-created object
112
- */
113
- async function regTree(rootPath, tree) {
114
- var values;
115
-
116
- if (arguments.length !== 2)
117
- tree = {};
118
-
119
- for (let path of await getKeys(rootPath)) {
120
- tree[path] = {};
121
- }
122
-
123
- for (let path in tree) {
124
- await regTree(rootPath + '\\' + path, tree[path]);
125
- }
126
-
127
- values = await getValues(rootPath);
128
- for (let prop in values) {
129
- tree[prop] = values[prop].value;
130
- }
131
-
132
- return tree;
133
- }
134
-
135
- exports.getObject = async function dcpClient$$windowsRegistry$getObject(hive, keyTail) {
136
- var tree = {};
137
-
138
- keyTail = keyTail.replace(/\//g, '\\');
139
- key = `${hive}\\${exports.baseKey}\\${keyTail}`;
140
-
141
- if (await keyExists(key))
142
- await regTree(key, tree);
143
- else
144
- return false;
145
-
146
- return tree;
147
- }
148
-
149
- /** Make all exported functions fast-path false return on non-windows */
150
- if (require('os').platform() !== 'win32') {
151
- for (let exp in exports) {
152
- if (typeof exports[exp] === 'function') {
153
- exports[exp] = function() { return false };
154
- }
155
- }
156
- }
1
+ /**
2
+ * @file windows-registry.js
3
+ * Windows registry configuration/integration utility for DCP. All exported functions
4
+ * return false on non-Windows platforms.
5
+ *
6
+ * Basic idea: summarize registry into an objects which can be used as the
7
+ * initializer object for DCP Client and wallet cache. Multiple hives are read
8
+ * to support higher-priviledged overrides for Administrators writing Group
9
+ * Policy files in Enterprise installs.
10
+ *
11
+ * Note: In registry-speak, keys are like directories and values are like files -- i.e. a key can
12
+ * contain keys and/or values, but values cannot contain keys; only strings and numbers and stuff.
13
+ *
14
+ *
15
+ * future - we will need a better way to return registry values, current proposal is to use
16
+ * REG_BINARY to encode richer types thus:
17
+ * 0x00 - null
18
+ * 0x20 - next 8 bytes are a little-endian float64
19
+ * 0x40 - next byte is start of Uint8Array
20
+ * 0x80 - false
21
+ * 0x81 - true
22
+ * 0x82 - undefined
23
+ * future - we should probably expand our understanding of types as follows
24
+ * REG_DWORD - integer
25
+ * REG_QWORD - bigint
26
+ * REG_NONE - treat as missing
27
+ *
28
+ * @author Wes Garland, wes@kingsds.network
29
+ * @date Jul 2020
30
+ */
31
+ 'use strict';
32
+
33
+ const regedit = require('regedit');
34
+ exports.baseKey = process.env.DCP_REGISTRY_BASEKEY || 'Software\\Distributive';
35
+
36
+ /** Get all of the entries for a given key. Keys are returned in a property
37
+ * named keys. Values are returned in a property named values.
38
+ */
39
+ async function getEntries(key) {
40
+ var entries = {};
41
+
42
+ return new Promise(function (resolve, reject) {
43
+ try {
44
+ let stream = regedit.list([key]);
45
+
46
+ stream.on('data', function(entry) {
47
+ entries[entry.key] = entry.data;
48
+ });
49
+
50
+ stream.on('finish', function () {
51
+ resolve(entries);
52
+ })
53
+ } catch(e) {
54
+ reject(e);
55
+ }
56
+ })
57
+ }
58
+
59
+ /** Get all of the keys for a given key */
60
+ async function getKeys(key) {
61
+ let entries = await getEntries(key);
62
+ return entries[key].keys || [];
63
+ }
64
+
65
+ /** Get all of the values for a given key */
66
+ async function getValues(key) {
67
+ let entries = await getEntries(key);
68
+ return entries[key].values || {};
69
+ }
70
+
71
+ /** Returns true if a given key exists in the registry. Strange algorithm works
72
+ * around bugs in the npm regedit package.
73
+ *
74
+ * @param {string} key The key to check, eg HKLM\\Software\\Kings Distributed Systems
75
+ * @returns Promise which resolves to boolean
76
+ */
77
+ async function keyExists(key) {
78
+ var lhs, rhs;
79
+
80
+ for (rhs = key.split('\\'), lhs = rhs.splice(0,2).join('\\');
81
+ rhs.length;
82
+ lhs += '\\' + rhs.shift()) {
83
+ let subKeys = await getKeys(lhs);
84
+ if ((subKeys.map((a) => a.toUpperCase()).filter((a) => a === rhs[0].toUpperCase())).length === 0)
85
+ return false;
86
+ }
87
+ return true;
88
+ }
89
+
90
+ /**
91
+ * Return the registry tree, including both keys and values, from the rootPath on down.
92
+ * keys are represented as JS objects, values are represented as JS values. For example,
93
+ * a registry with \\HKCU\KDS\dcp-client\scheduler\location = 'https://scheduler.kds.net'
94
+ * would become { 'dcp-client': scheduler: { location: 'https://scheduler.kds.net' } } if
95
+ * the rootPath were \\HKCU\KDS.
96
+ *
97
+ * @param {string} rootPath The registry key to traverse
98
+ * @param {object} tree [optional] the JS object to decorate with the registry tree
99
+ * @returns tree or a newly-created object
100
+ */
101
+ async function regTree(rootPath, tree) {
102
+ var values;
103
+
104
+ if (arguments.length !== 2)
105
+ tree = {};
106
+
107
+ for (let path of await getKeys(rootPath)) {
108
+ tree[path] = {};
109
+ }
110
+
111
+ for (let path in tree) {
112
+ await regTree(rootPath + '\\' + path, tree[path]);
113
+ }
114
+
115
+ values = await getValues(rootPath);
116
+ for (let prop in values) {
117
+ tree[prop] = values[prop].value;
118
+ }
119
+
120
+ return tree;
121
+ }
122
+
123
+ /**
124
+ * Get an object from the registry. This is can be a key or a tree. The object is identified
125
+ * by the hive, exports.baseKey, and the keyTail.
126
+ *
127
+ * @param {string} hive the hive to search (eg HKLM)
128
+ * @param {string} keyTail the end of the key
129
+ *
130
+ * @returns {Promise} that resolves to false (lookup miss), a registry tree, or a registry object from
131
+ * regTree().
132
+ */
133
+ exports.getObject = async function dcpClient$$windowsRegistry$getObject(hive, keyTail) {
134
+ var tree = {};
135
+
136
+ keyTail = keyTail.replace(/\//g, '\\');
137
+ const key = `${hive}\\${exports.baseKey}\\${keyTail}`;
138
+
139
+ if (await keyExists(key))
140
+ await regTree(key, tree);
141
+ else
142
+ return false;
143
+
144
+ return tree;
145
+ }
146
+
147
+ /** Make all exported functions fast-path false return on non-windows */
148
+ if (require('os').platform() !== 'win32') {
149
+ for (let exp in exports) {
150
+ if (typeof exports[exp] === 'function') {
151
+ exports[exp] = function() { return false };
152
+ }
153
+ }
154
+ }