Remove ESBuild experiment. Add --install option for bash completions. Move shell scripts to separate files for auditability. Fix template inspect command autocomplete and output formatting
This commit is contained in:
@@ -8,12 +8,13 @@
|
||||
* Usage: xo-complete <context> [args...]
|
||||
*
|
||||
* Contexts:
|
||||
* mnemonics - List mnemonic file names
|
||||
* templates - List template names/IDs
|
||||
* actions <template> - List actions for a template
|
||||
* invitations - List invitation IDs
|
||||
* resources - List UTXO outpoints (txhash:vout)
|
||||
* subcommands <command> - List subcommands for a top-level command
|
||||
* mnemonics - List mnemonic file names
|
||||
* templates - List template names/IDs
|
||||
* actions <template> - List actions for a template
|
||||
* fields <category> <template> - List fields for a template category
|
||||
* invitations - List invitation IDs
|
||||
* resources - List UTXO outpoints (txhash:vout)
|
||||
* subcommands <command> - List subcommands for a top-level command
|
||||
*
|
||||
* Output: One completion suggestion per line, suitable for shell completion.
|
||||
*
|
||||
@@ -150,6 +151,33 @@ async function listTemplates(prefix?: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a template by name or ID.
|
||||
*/
|
||||
async function resolveTemplate(
|
||||
engine: Awaited<ReturnType<Awaited<ReturnType<typeof getOfflineEngineModule>>["tryCreateOfflineEngine"]>>,
|
||||
templateQuery: string,
|
||||
) {
|
||||
if (!engine) return null;
|
||||
|
||||
const { generateTemplateIdentifier } = await getEngineModule();
|
||||
const templates = await engine.listImportedTemplates();
|
||||
|
||||
// Try exact match on name or ID
|
||||
let template = templates.find(
|
||||
(t) => t.name === templateQuery || generateTemplateIdentifier(t) === templateQuery,
|
||||
);
|
||||
|
||||
// Try partial match on name
|
||||
if (!template) {
|
||||
template = templates.find((t) =>
|
||||
t.name?.toLowerCase().includes(templateQuery.toLowerCase()),
|
||||
);
|
||||
}
|
||||
|
||||
return template ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists actions for a specific template.
|
||||
*/
|
||||
@@ -158,7 +186,6 @@ async function listActions(templateQuery: string, prefix?: string): Promise<void
|
||||
if (!mnemonic) return;
|
||||
|
||||
const { tryCreateOfflineEngine } = await getOfflineEngineModule();
|
||||
const { generateTemplateIdentifier } = await getEngineModule();
|
||||
|
||||
const engine = await tryCreateOfflineEngine(mnemonic, {
|
||||
databasePath: getDataDir(),
|
||||
@@ -168,18 +195,7 @@ async function listActions(templateQuery: string, prefix?: string): Promise<void
|
||||
if (!engine) return;
|
||||
|
||||
try {
|
||||
// Try to resolve the template by name or ID
|
||||
const templates = await engine.listImportedTemplates();
|
||||
let template = templates.find(
|
||||
(t) => t.name === templateQuery || generateTemplateIdentifier(t) === templateQuery,
|
||||
);
|
||||
|
||||
if (!template) {
|
||||
// Try partial match on name
|
||||
template = templates.find((t) =>
|
||||
t.name?.toLowerCase().includes(templateQuery.toLowerCase()),
|
||||
);
|
||||
}
|
||||
const template = await resolveTemplate(engine, templateQuery);
|
||||
|
||||
if (template && template.actions) {
|
||||
const actions = Object.keys(template.actions);
|
||||
@@ -190,6 +206,53 @@ async function listActions(templateQuery: string, prefix?: string): Promise<void
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists fields (actions, transactions, outputs, etc.) for a specific template category.
|
||||
* Used for completing the 3rd argument of `template inspect <category> <template> <field>`.
|
||||
*/
|
||||
async function listFields(category: string, templateQuery: string, prefix?: string): Promise<void> {
|
||||
const mnemonic = getCurrentMnemonic();
|
||||
if (!mnemonic) return;
|
||||
|
||||
const { tryCreateOfflineEngine } = await getOfflineEngineModule();
|
||||
|
||||
const engine = await tryCreateOfflineEngine(mnemonic, {
|
||||
databasePath: getDataDir(),
|
||||
databaseFilename: "xo-wallet.db",
|
||||
});
|
||||
|
||||
if (!engine) return;
|
||||
|
||||
try {
|
||||
const template = await resolveTemplate(engine, templateQuery);
|
||||
if (!template) return;
|
||||
|
||||
let fields: string[] = [];
|
||||
|
||||
switch (category) {
|
||||
case "action":
|
||||
fields = Object.keys(template.actions || {});
|
||||
break;
|
||||
case "transaction":
|
||||
fields = Object.keys(template.transactions || {});
|
||||
break;
|
||||
case "output":
|
||||
fields = Object.keys(template.outputs || {});
|
||||
break;
|
||||
case "lockingscript":
|
||||
fields = Object.keys(template.lockingScripts || {});
|
||||
break;
|
||||
case "variable":
|
||||
fields = Object.keys(template.variables || {});
|
||||
break;
|
||||
}
|
||||
|
||||
outputCompletions(fields, prefix);
|
||||
} finally {
|
||||
await engine.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists invitation IDs from the invitation storage.
|
||||
*/
|
||||
@@ -278,6 +341,13 @@ async function main(): Promise<void> {
|
||||
}
|
||||
break;
|
||||
|
||||
case "fields":
|
||||
// fields <category> <template> [prefix]
|
||||
if (arg1 && arg2) {
|
||||
await listFields(arg1, arg2, process.argv[5]);
|
||||
}
|
||||
break;
|
||||
|
||||
case "invitations":
|
||||
await listInvitations(arg1);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user