@descix/cli 1.0.0 → 1.0.3
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/LICENSE.md +671 -0
- package/README.md +43 -4
- package/agent-assets/guides/README.md +2 -2
- package/agent-assets/guides/dynamic-site-pattern.md +29 -14
- package/agent-assets/guides/knowledge-only-pattern.md +5 -5
- package/agent-assets/guides/microservice-pattern.md +48 -4
- package/agent-assets/guides/static-site-pattern.md +8 -0
- package/agent-assets/instructions/app-asset-validation.md +5 -5
- package/agent-assets/instructions/onboarding-flow.md +4 -4
- package/agent-assets/instructions/standard-folder-structure.md +20 -11
- package/agent-assets/reference/app-creation.md +42 -20
- package/agent-assets/reference/architecture.md +24 -22
- package/agent-assets/reference/local-dev.md +230 -67
- package/agent-assets/reference/mcp-integration.md +9 -9
- package/agent-assets/reference/platform-api-consumer.md +244 -0
- package/agent-assets/reference/workspace-config.md +7 -7
- package/agent-assets/rules/descix_mcp.template.mdc +8 -8
- package/bin/descix.js +2559 -1435
- package/bin/kb-deny-lint.mjs +91 -0
- package/bin/mcp-server.js +175 -91
- package/lib/agent-files.js +37 -25
- package/lib/api-client.js +223 -56
- package/lib/auth-guard.js +13 -3
- package/lib/chat-session.js +187 -0
- package/lib/cli-version.js +59 -0
- package/lib/commands/airdrop.js +251 -0
- package/lib/commands/auth.js +254 -72
- package/lib/commands/briefer/errors.js +88 -0
- package/lib/commands/briefer/index.js +319 -0
- package/lib/commands/briefer/sources/canonical-sources.js +129 -0
- package/lib/commands/briefer/sources/entitlements.js +209 -0
- package/lib/commands/briefer/sources/environments.js +205 -0
- package/lib/commands/briefer/sources/identifiers.js +214 -0
- package/lib/commands/briefer/sources/microservice-deploy.js +267 -0
- package/lib/commands/briefer/sources/routing.js +275 -0
- package/lib/commands/briefer/sources/what-is-not.js +223 -0
- package/lib/commands/briefer/stitcher.js +134 -0
- package/lib/commands/briefer/util/source-reader.js +502 -0
- package/lib/commands/config.js +210 -90
- package/lib/commands/corpus.js +978 -0
- package/lib/commands/credits.js +122 -0
- package/lib/commands/health.js +627 -0
- package/lib/commands/kb-list-render.js +32 -0
- package/lib/commands/kb.js +190 -413
- package/lib/commands/mcp.js +5 -5
- package/lib/commands/microservice-restart.js +359 -0
- package/lib/commands/model-config.js +313 -0
- package/lib/commands/retired-kb-sync.js +134 -0
- package/lib/commands/serve.js +26 -1
- package/lib/commands/status.js +23 -51
- package/lib/commands/update.js +36 -107
- package/lib/core/Chunker.js +491 -17
- package/lib/core/CorpusDenyLint.js +326 -0
- package/lib/core/CorpusWalker.js +357 -0
- package/lib/core/Hydrator.js +75 -29
- package/lib/core/ManifestLoader.js +533 -0
- package/lib/core/SiteWalker.js +218 -0
- package/lib/core/Syncer.js +163 -11
- package/lib/diagnostic-log.js +41 -0
- package/lib/environment-report.js +100 -0
- package/lib/global-config.js +4 -1
- package/lib/oauth-client.js +164 -0
- package/lib/origin.js +143 -0
- package/lib/service-api-client.js +95 -0
- package/lib/wallet-environment.js +49 -0
- package/lib/wallet-file.js +16 -2
- package/lib/wizard/setup.js +10 -9
- package/lib/workspace-config.js +380 -235
- package/lib/workspace-identity.js +92 -0
- package/lib/workspace-utils.js +41 -61
- package/package.json +29 -9
- package/templates/agent-claude.md +157 -9
- package/templates/agent-cline.md +68 -8
- package/templates/agent-copilot.md +68 -8
- package/templates/agent-cursor.md +68 -8
- package/templates/alignment-schema.json +20 -0
- package/templates/cloud-brain-hook.sh +20 -0
- package/templates/drive/README.md +1 -1
- package/templates/drive_templates/README.md +2 -2
- package/templates/scaffolds/README.md +6 -10
- package/templates/scaffolds/microservice/SERVICE_README_sdk.md +30 -0
- package/templates/scaffolds/microservice/app.js +51 -10
- package/templates/scaffolds/microservice/app.yaml +2 -2
- package/templates/scaffolds/microservice/defaults-config-dev.json +4 -0
- package/templates/scaffolds/microservice/dev-overrides.example.json +5 -0
- package/templates/scaffolds/microservice/manifest.json +0 -1
- package/templates/scaffolds/microservice/package.json +3 -3
- package/templates/scaffolds/microservice/scripts/register.js +56 -41
- package/templates/scaffolds/microservice/services/mcpClient.js +55 -65
- package/templates/scaffolds/microservice/services/utils.js +20 -238
- package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +24 -0
- package/SERVICE_README_sdk.md +0 -399
- package/descix-cli-1.0.0.tgz +0 -0
- package/lib/commands/app-wizard.js +0 -627
- package/lib/commands/folder.js +0 -291
- package/lib/core/PathContext.js +0 -335
- package/templates/scaffolds/microservice/dev-overrides.json.example +0 -5
- package/templates/scaffolds/site/DeSciXAppSDK.js +0 -62
- package/templates/scaffolds/site/README.md +0 -51
- package/templates/scaffolds/site/app.js +0 -22
- package/templates/scaffolds/site/index.html +0 -42
- package/templates/scaffolds/site/styles.css +0 -123
- package/test-hydration.js +0 -150
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE ONE OWNER OF "what version of the CLI is this".
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS FILE EXISTS (measured, not assumed): PUBLISHED @descix/cli@1.0.1 declares version
|
|
5
|
+
* 1.0.1 in its package.json and prints `1.0.0` from `bin/descix.js:35`'s `.version('1.0.0')`.
|
|
6
|
+
* The same disagreement was live in the repo at 1.0.3. Two derivations of one fact is the
|
|
7
|
+
* general form of mirror drift, and this is its purest instance: the manifest is the version,
|
|
8
|
+
* and a literal beside it is a copy that can only go stale. There is now exactly one derivation,
|
|
9
|
+
* and every self-report consumes it.
|
|
10
|
+
*
|
|
11
|
+
* NO FALLBACK. A package root whose manifest cannot be read has no version, and inventing one
|
|
12
|
+
* is precisely the class of defect this contract exists to end — an artifact reporting something
|
|
13
|
+
* the developer did not choose. A miss THROWS, naming what it could not determine.
|
|
14
|
+
*/
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Read the version out of a package root's manifest, or throw naming the failure.
|
|
21
|
+
* @param {string} packageRoot - directory containing package.json
|
|
22
|
+
* @returns {string} the manifest's `version`
|
|
23
|
+
*/
|
|
24
|
+
export function readVersionFrom(packageRoot) {
|
|
25
|
+
const manifest = path.join(packageRoot, 'package.json');
|
|
26
|
+
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = fs.readFileSync(manifest, 'utf8');
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`descix: cannot determine its own version — ${manifest} is unreadable (${error.code || error.message}). ` +
|
|
33
|
+
`The version is read from the package manifest at runtime and has no default.`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let pkg;
|
|
38
|
+
try {
|
|
39
|
+
pkg = JSON.parse(raw);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`descix: cannot determine its own version — ${manifest} is not valid JSON (${error.message}).`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (typeof pkg.version !== 'string' || pkg.version.trim() === '') {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`descix: cannot determine its own version — ${manifest} declares no non-empty "version".`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return pkg.version;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** This package's root: lib/ sits one level under it, in the checkout and in the published tarball alike. */
|
|
56
|
+
export const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
57
|
+
|
|
58
|
+
/** The CLI's version. The ONLY place any surface should read it from. */
|
|
59
|
+
export const CLI_VERSION = readVersionFrom(PACKAGE_ROOT);
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Airdrop Commands
|
|
3
|
+
*
|
|
4
|
+
* Admin-facing CLI commands for WS-ADMIN-B1 airdrop migration operations.
|
|
5
|
+
* Per CEO-D-MANUAL-TRIGGER-NO-CRON (2026-04-20), Cloud Scheduler cron was dropped
|
|
6
|
+
* from Round B in favor of operator-invoked manual triggers via this CLI.
|
|
7
|
+
*
|
|
8
|
+
* Per CEO-D-B1-FIX-DESIGN-LOCK (2026-04-28), the CLI signature is:
|
|
9
|
+
* descix airdrop execute-queue --community <slug> [--dry-run | --signer-pk-file <path> --apply]
|
|
10
|
+
*
|
|
11
|
+
* --community <slug> REQUIRED for --apply mode (per-community batch scoping). Optional
|
|
12
|
+
* in --dry-run mode (returns aggregate-across-communities preview).
|
|
13
|
+
* --dry-run Read-only preview path. No PK prompt. No tx. No state mutation.
|
|
14
|
+
* --apply Live execution path. Requires --signer-pk-file or interactive
|
|
15
|
+
* prompt-password. Mutually exclusive with --dry-run.
|
|
16
|
+
* --signer-pk-file <path> File containing the admin's per-invocation signer PK (0x + 64 hex,
|
|
17
|
+
* one line). Inline --signer-pk and env vars REJECTED on security
|
|
18
|
+
* grounds (process listing leak).
|
|
19
|
+
*
|
|
20
|
+
* Access: requires admin session (platform-admin group membership enforced server-side
|
|
21
|
+
* in `airdrop_execute_queue` via `isPlatformAdmin(user)`). CLI-side, we require an
|
|
22
|
+
* authenticated session (`requireAuth`). The server rejects non-admin users.
|
|
23
|
+
*
|
|
24
|
+
* Safety: the `BATCH_UPDATE_BALANCES_BROADCAST_ENABLED` flag gates the actual on-chain
|
|
25
|
+
* broadcast in Powch. While that flag is false, `airdrop_execute_queue --apply` will assemble
|
|
26
|
+
* batches but Powch returns `stopped_at_broadcast_boundary` — useful for verifying queue
|
|
27
|
+
* assembly without spending MATIC.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import fs from 'fs';
|
|
31
|
+
import readline from 'readline';
|
|
32
|
+
import chalk from 'chalk';
|
|
33
|
+
import { DeSciXApiClient } from '../api-client.js';
|
|
34
|
+
import { requireAuth } from '../auth-guard.js';
|
|
35
|
+
|
|
36
|
+
const PK_REGEX = /^0x[a-fA-F0-9]{64}$/;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Read a signer PK from a file. Validates 0x + 64-hex format. Trims trailing whitespace
|
|
40
|
+
* and a trailing newline only — does NOT mutate any other formatting.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} path - Filesystem path to the PK file.
|
|
43
|
+
* @returns {string} Validated PK in `0x` + 64 hex form.
|
|
44
|
+
* @throws {Error} If the file is missing or contents are malformed.
|
|
45
|
+
*/
|
|
46
|
+
export function readSignerPkFromFile(path) {
|
|
47
|
+
let raw;
|
|
48
|
+
try {
|
|
49
|
+
raw = fs.readFileSync(path, 'utf8');
|
|
50
|
+
} catch (e) {
|
|
51
|
+
throw new Error(`--signer-pk-file: cannot read ${path}: ${e.message}`);
|
|
52
|
+
}
|
|
53
|
+
const trimmed = raw.trim();
|
|
54
|
+
if (!PK_REGEX.test(trimmed)) {
|
|
55
|
+
// CRITICAL: do not echo the contents (even partial) into the error message.
|
|
56
|
+
throw new Error(`--signer-pk-file: contents do not match expected 0x + 64 hex format`);
|
|
57
|
+
}
|
|
58
|
+
return trimmed;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Interactive password prompt (no echo). Reads a line of stdin with terminal echo disabled
|
|
63
|
+
* so the typed PK never appears on screen. Validates 0x + 64-hex format.
|
|
64
|
+
*
|
|
65
|
+
* @returns {Promise<string>} Validated PK in `0x` + 64 hex form.
|
|
66
|
+
*/
|
|
67
|
+
export async function promptSignerPkInteractive({ stdin = process.stdin, stdout = process.stdout } = {}) {
|
|
68
|
+
return await new Promise((resolve, reject) => {
|
|
69
|
+
const rl = readline.createInterface({ input: stdin, output: stdout, terminal: true });
|
|
70
|
+
// Disable echo so the PK isn't visible while typing.
|
|
71
|
+
const originalWrite = stdout.write.bind(stdout);
|
|
72
|
+
stdout.write = (str, ...rest) => {
|
|
73
|
+
// Suppress only the echoed characters during the prompt; allow our prompt to render.
|
|
74
|
+
if (typeof str === 'string' && str.length === 1) return true;
|
|
75
|
+
return originalWrite(str, ...rest);
|
|
76
|
+
};
|
|
77
|
+
stdout.write('Enter signer PK (0x + 64 hex, will not be echoed): ');
|
|
78
|
+
rl.question('', (answer) => {
|
|
79
|
+
stdout.write = originalWrite;
|
|
80
|
+
stdout.write('\n');
|
|
81
|
+
rl.close();
|
|
82
|
+
const trimmed = (answer || '').trim();
|
|
83
|
+
if (!PK_REGEX.test(trimmed)) {
|
|
84
|
+
reject(new Error('prompt-password: input does not match expected 0x + 64 hex format'));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
resolve(trimmed);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Execute the airdrop migration batch queue on the target environment.
|
|
94
|
+
*
|
|
95
|
+
* @param {Object} options
|
|
96
|
+
* @param {string} [options.community] - Community slug for per-community batch scoping.
|
|
97
|
+
* REQUIRED for --apply mode; optional for --dry-run mode (omitted = aggregate preview).
|
|
98
|
+
* @param {boolean} [options.dryRun] - If true, read-only preview path. Mutually exclusive with --apply.
|
|
99
|
+
* @param {boolean} [options.apply] - If true, live execution path. Requires --signer-pk-file or
|
|
100
|
+
* interactive prompt-password. Mutually exclusive with --dry-run.
|
|
101
|
+
* @param {string} [options.signerPkFile] - Path to file containing the admin's signer PK.
|
|
102
|
+
* @param {number} [options.batchSize] - Optional cap on number of users processed this run.
|
|
103
|
+
* @param {Object} [options.apiClient] - Optional injected API client (for tests).
|
|
104
|
+
* @param {Function} [options.promptFn] - Optional injected interactive PK prompt (for tests).
|
|
105
|
+
*/
|
|
106
|
+
export async function executeQueue(options) {
|
|
107
|
+
const apiClient = options.apiClient || new DeSciXApiClient();
|
|
108
|
+
await requireAuth(apiClient);
|
|
109
|
+
|
|
110
|
+
// Mode resolution: --dry-run and --apply are mutually exclusive. Default is dry-run when
|
|
111
|
+
// no --apply is provided (read-only is the safer default per CEO-D-B1-FIX-DESIGN-LOCK §4.A).
|
|
112
|
+
if (options.dryRun && options.apply) {
|
|
113
|
+
throw new Error('--dry-run and --apply are mutually exclusive (CEO-D-B1-FIX-DESIGN-LOCK Decision 1).');
|
|
114
|
+
}
|
|
115
|
+
const isDryRun = options.dryRun === true || (!options.apply && options.dryRun !== false);
|
|
116
|
+
const isApply = options.apply === true;
|
|
117
|
+
|
|
118
|
+
if (isApply && !options.community) {
|
|
119
|
+
throw new Error('--community <slug> is required for --apply mode (per-community batch scoping per CEO-D-B1-FIX-DESIGN-LOCK Decision 3).');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Resolve signer PK for --apply mode: --signer-pk-file (default) or interactive prompt fallback.
|
|
123
|
+
let signerPkHex = null;
|
|
124
|
+
if (isApply) {
|
|
125
|
+
if (options.signerPkFile) {
|
|
126
|
+
signerPkHex = readSignerPkFromFile(options.signerPkFile);
|
|
127
|
+
} else {
|
|
128
|
+
const promptFn = options.promptFn || promptSignerPkInteractive;
|
|
129
|
+
signerPkHex = await promptFn();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const params = {};
|
|
134
|
+
if (options.community) params.community = options.community;
|
|
135
|
+
params.mode = isApply ? 'apply' : 'dry-run';
|
|
136
|
+
if (signerPkHex) params.signer_pk_hex = signerPkHex;
|
|
137
|
+
if (options.batchSize !== undefined && options.batchSize !== null) {
|
|
138
|
+
const bs = Number(options.batchSize);
|
|
139
|
+
if (!Number.isFinite(bs) || bs <= 0) {
|
|
140
|
+
throw new Error(`--batch-size must be a positive integer, got: ${options.batchSize}`);
|
|
141
|
+
}
|
|
142
|
+
params.batch_size = bs;
|
|
143
|
+
}
|
|
144
|
+
// Legacy boolean dry_run parameter (preserved for backwards-compat with existing scripts).
|
|
145
|
+
if (isDryRun && !options.community) {
|
|
146
|
+
params.dry_run = true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const envLabel = process.env.DESCIX_API_URL || '(workspace default)';
|
|
150
|
+
const mode = isApply ? chalk.green('APPLY') : chalk.yellow('DRY-RUN');
|
|
151
|
+
console.log(chalk.cyan(`\nairdrop execute-queue [${mode}] target=${envLabel}`));
|
|
152
|
+
if (options.community) {
|
|
153
|
+
console.log(chalk.gray(` community: ${options.community}`));
|
|
154
|
+
}
|
|
155
|
+
if (params.batch_size) {
|
|
156
|
+
console.log(chalk.gray(` batch_size: ${params.batch_size}`));
|
|
157
|
+
}
|
|
158
|
+
if (signerPkHex) {
|
|
159
|
+
// Just confirm a PK is loaded; never echo any portion of it.
|
|
160
|
+
console.log(chalk.gray(` signer_pk: loaded (0x${signerPkHex.slice(2,6)}…${signerPkHex.slice(-4)})`));
|
|
161
|
+
}
|
|
162
|
+
console.log('');
|
|
163
|
+
|
|
164
|
+
let response;
|
|
165
|
+
try {
|
|
166
|
+
response = await apiClient.invoke('airdrop_execute_queue', params);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
// Auth/permission errors surface as HTTP-level failures with readable messages.
|
|
169
|
+
// CRITICAL: ensure no echo of params.signer_pk_hex into any error message we print.
|
|
170
|
+
const safeMsg = String(error.message || '').replace(/0x[a-fA-F0-9]{64}/g, '0x[REDACTED]');
|
|
171
|
+
console.error(chalk.red(`\nairdrop_execute_queue failed: ${safeMsg}\n`));
|
|
172
|
+
if (/platform-admin|service-account/i.test(error.message)) {
|
|
173
|
+
console.error(chalk.gray(' This command requires a platform-admin account.'));
|
|
174
|
+
console.error(chalk.gray(' Verify your membership in the DESCIX_ADMIN_GROUP Google Group.\n'));
|
|
175
|
+
}
|
|
176
|
+
throw error;
|
|
177
|
+
} finally {
|
|
178
|
+
// Belt-and-suspenders: clear local PK var after the single use. We deliberately
|
|
179
|
+
// do NOT mutate `params.signer_pk_hex` here because the api-client/axios layer may
|
|
180
|
+
// still be serializing the body, and modern axios keeps no reference after the
|
|
181
|
+
// request resolves anyway. The local var nulling is the meaningful action.
|
|
182
|
+
signerPkHex = null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const result = response?.message || response;
|
|
186
|
+
if (!result || typeof result !== 'object') {
|
|
187
|
+
throw new Error('airdrop_execute_queue returned no result');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (isDryRun && result.mode === 'dry-run') {
|
|
191
|
+
// Render the §4.F dry-run summary table.
|
|
192
|
+
console.log(chalk.bold('Dry-Run Summary:'));
|
|
193
|
+
console.log(` community: ${chalk.cyan(result.community || '(all)')}`);
|
|
194
|
+
if (result.token_symbol) console.log(` token_symbol: ${result.token_symbol}`);
|
|
195
|
+
if (result.token_contract_address) console.log(` token_contract: ${result.token_contract_address}`);
|
|
196
|
+
if (result.abi_source) console.log(` abi_source: ${result.abi_source.doc_id} (v${result.abi_source.version || '?'})`);
|
|
197
|
+
console.log(` transfers_total: ${chalk.yellow(result.transfers_total)}`);
|
|
198
|
+
console.log(` debit_total: ${result.debit_total}`);
|
|
199
|
+
console.log(` credit_total: ${result.credit_total}`);
|
|
200
|
+
console.log(` bonus_total: ${result.bonus_total}`);
|
|
201
|
+
const nz = result.net_zero_assertion || {};
|
|
202
|
+
const nzLabel = nz.passes ? chalk.green('PASS') : chalk.red('FAIL');
|
|
203
|
+
console.log(` net_zero_assertion: ${nzLabel} (Σ=${nz.sum})`);
|
|
204
|
+
console.log(` unique_source_wallets: ${result.unique_source_wallets}`);
|
|
205
|
+
console.log(` unique_master_wallets: ${result.unique_master_wallets}`);
|
|
206
|
+
console.log(` prospective_batch_id: ${result.prospective_batch_id}`);
|
|
207
|
+
if (result.gas_estimate) {
|
|
208
|
+
console.log(` gas_estimate: ${result.gas_estimate.units || '?'} units @ ${result.gas_estimate.gas_price_gwei || '?'} gwei`);
|
|
209
|
+
if (result.gas_estimate.matic_cost_estimate) {
|
|
210
|
+
console.log(` estimated_cost: ~${result.gas_estimate.matic_cost_estimate} MATIC`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
console.log('');
|
|
214
|
+
console.log(chalk.gray('No PK supplied. No tx submitted. No DB rows mutated.'));
|
|
215
|
+
console.log(chalk.gray('Re-run with --apply --signer-pk-file <path> to commit.'));
|
|
216
|
+
console.log('');
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
console.log(chalk.bold('Result:'));
|
|
221
|
+
console.log(` batches_executed: ${chalk.yellow(result.batches_executed ?? 0)}`);
|
|
222
|
+
console.log(` total_users: ${chalk.yellow(result.total_users ?? 0)}`);
|
|
223
|
+
console.log(` dry_run: ${result.dry_run ? chalk.yellow('true') : chalk.gray('false')}`);
|
|
224
|
+
|
|
225
|
+
const perCommunity = result.per_community || {};
|
|
226
|
+
const communityKeys = Object.keys(perCommunity);
|
|
227
|
+
if (communityKeys.length) {
|
|
228
|
+
console.log(` per_community:`);
|
|
229
|
+
for (const cid of communityKeys) {
|
|
230
|
+
console.log(` ${cid}: ${perCommunity[cid]}`);
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
console.log(` per_community: ${chalk.gray('{}')}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (result.caller) {
|
|
237
|
+
const who = result.caller.operator_email || result.caller.service_account || '(unknown)';
|
|
238
|
+
console.log(chalk.gray(` caller: ${who}`));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.log('');
|
|
242
|
+
if (isApply) {
|
|
243
|
+
console.log(chalk.gray('Apply complete. See on_chain_log for the trigger audit row.'));
|
|
244
|
+
} else {
|
|
245
|
+
console.log(chalk.gray('Dry-run complete. No broadcast attempted. No on_chain_log broadcast row written.'));
|
|
246
|
+
}
|
|
247
|
+
console.log('');
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export default { executeQueue, readSignerPkFromFile, promptSignerPkInteractive };
|