#!/usr/bin/env nodejsscript /* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */ const css= echo.css` .error{ color: lightred; } .code, .method{ color: lightblue; } .code::before, .code::after{ content: "\`"; } .li{ padding-left: 2ch; } `; const default_wallet= "kdewallet"; const cmd= "qdbus"; if(!s.which(cmd)) $.exit(1, echo([ `%cError: ${cmd} not found.`, `%cInstall it with: %csudo apt install ${cmd}`, ].join("\n"), css.error, css.unset, css.code)); import { EventEmitter } from "node:events"; const events= new EventEmitter(); const exit_event= "exit"; if($.isMain(import.meta)){ events.on(exit_event, $.exit); $.api("", true) .version("2024-03-05") .describe([ `KWallet CLI using ${cmd}.`, "Call with no arguments to list all methods.", ]) .example("--wallet kdewallet folderList") .example("--wallet kdewallet readPassword folder entry") .option("--wallet, -w", "Wallet name", default_wallet) .action(function main({ _, wallet }){ const [ results, is_ls ]= kwallet(_, { wallet }); if(is_ls) echoLs(results); else echo(results); events.emit(exit_event); }) .parse(); } /** * @param {string[]} _ Query/arguments for qdbus * @param {object} [options] * @param {string} [options.wallet] Wallet name * @returns {[ string[], true ]|[ string, false ]} * */ export function kwallet(_, { wallet= default_wallet }= {}){ const qdbus= qdbusGenerator({ service: "org.kde.kwalletd6", methods: "/modules/kwalletd6", wallet, exit_event }); if(!_.length) return [ qdbus(), true ]; else{ //TODO: Map (`--literal` in qdbus and decode the result `echoMap`) const result= qdbus(_); return [ result, false ]; } } function qdbusGenerator({ service, methods, wallet, exit_event }){ const name= getScriptName(); const no_id= [ 'allWalletsClosed', 'applicationDisconnected', 'folderListUpdated', 'folderUpdated', 'walletClosed', 'walletCreated', 'walletDeleted', 'walletListDirty', 'walletOpened', 'changePassword', 'close', 'closeAllWallets', 'deleteWallet', 'disconnectApplication', 'folderDoesNotExist', 'isEnabled', 'isOpen', 'keyDoesNotExist', 'localWallet', 'networkWallet', 'open', 'openAsync', 'openPath', 'openPathAsync', 'pamOpen', 'reconfigure', 'users', 'wallets' ]; let id; const qdbus= (...args)=> s.$().run`${cmd} ${service} ${methods} ${args}`; events.on(exit_event, ()=>{ if(!id) return; qdbus("close", id, false, name); id= null; }); return function([ method, ...args ]= []){ if(!method) return qdbus(); if(no_id.includes(method)) return qdbus(method, ...args); if(!id) open(); return qdbus(method, id, ...args, name); }; function open(){ id= qdbus("open", wallet, 0, name).trim(); return id; } } function echoMap(map){ map= map.slice(1, -1).split(", ").map(n=> Number.parseInt(n)); const ab= new ArrayBuffer(map.length); const view= new Uint8Array(ab); for(let i= 0; i list.split("\n").filter(l=> l.startsWith("method") && -1!==l.indexOf(ns)), list=> list.map(l=> l.slice(l.indexOf(" ")+1)).map(l=> l.replace(ns, "")), list=> list.reduce((o, l)=> (o[Number(l.indexOf("int handle")!==-1)].push(l), o), [ [], [] ]), ([ no_wallet, wallet ])=> [ "Methods available without wallet:", ...no_wallet.map(formatMethod), "Methods available with wallet (" + echo.format("%c--wallet", css.code) + " option):", ...wallet.map(formatMethod) ].join("\n"), echo )(list); function formatMethod(line){ const ch_type= line.indexOf(" ")+1; const ch_name= line.indexOf("(", ch_type); return echo.format([ "", line.slice(0, ch_type), line.slice(ch_type, ch_name), line.slice(ch_name) ].join("%c"), css.li, css.method, css.unset); } } function getScriptName(){ const { url }= import.meta; return (new URL(url)).href.slice((new URL("./", url).href.length)); }