oft-agt 0.5.9 → 0.5.11
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/cli.mjs +8 -4
- package/compile-plan.mjs +3 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/runtime/api-config.mjs +11 -0
- package/runtime/api-request.mjs +20 -0
- package/runtime/api-secrets.mjs +7 -0
- package/runtime/execute.mjs +27 -6
- package/runtime/functions.mjs +3 -1
- package/version.mjs +1 -1
package/cli.mjs
CHANGED
|
@@ -10,6 +10,8 @@ import { createRequire } from 'node:module';
|
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
11
|
import { spawn } from 'node:child_process';
|
|
12
12
|
import WebSocket from 'ws';
|
|
13
|
+
import {secretOperation,readApiSecret} from './runtime/api-secrets.mjs';
|
|
14
|
+
import {requestApi} from './runtime/api-request.mjs';
|
|
13
15
|
import { writeBot, compile } from './compiler.mjs';
|
|
14
16
|
import { acceptSecret } from './secret.mjs';
|
|
15
17
|
import { createUsageCollector } from './usage.mjs';
|
|
@@ -56,7 +58,7 @@ async function restartBot(config) {
|
|
|
56
58
|
if (!/^[a-f0-9-]{36}$/.test(release.id)) throw new Error('INVALID_RELEASE');
|
|
57
59
|
const secret = await credentials(config);
|
|
58
60
|
await stopBot();
|
|
59
|
-
child = watchUsage(spawn(process.execPath, ['bot.mjs'], { cwd: path.join(home, 'releases', release.id), env: { ...Object.fromEntries(['PATH', 'HOME', 'USERPROFILE', 'SystemRoot', 'WINDIR', 'TEMP', 'TMP'].filter(key => process.env[key]).map(key => [key, process.env[key]])), DISCORD_TOKEN: secret.token, DISCORD_CLIENT_ID: secret.applicationId, OFTONE_VARIABLES_PATH: path.join(home, 'data', 'variables.sqlite') }, stdio: ['ignore', 'pipe', 'pipe', 'ipc'], windowsHide: true, shell: false }));
|
|
61
|
+
child = watchUsage(spawn(process.execPath, ['bot.mjs'], { cwd: path.join(home, 'releases', release.id), env: { ...Object.fromEntries(['PATH', 'HOME', 'USERPROFILE', 'SystemRoot', 'WINDIR', 'TEMP', 'TMP'].filter(key => process.env[key]).map(key => [key, process.env[key]])), DISCORD_TOKEN: secret.token, DISCORD_CLIENT_ID: secret.applicationId, OFTONE_API_HOME: home, OFTONE_VARIABLES_PATH: path.join(home, 'data', 'variables.sqlite') }, stdio: ['ignore', 'pipe', 'pipe', 'ipc'], windowsHide: true, shell: false }));
|
|
60
62
|
secret.token = '';
|
|
61
63
|
child.stderr.on('data', () => {});
|
|
62
64
|
await new Promise((resolve, reject) => { const timer = setTimeout(() => { child.kill(); reject(new Error('BOT_START_TIMEOUT')); }, 45000); const fail = () => { clearTimeout(timer); reject(new Error('BOT_START_FAILED')); }; child.once('error', fail); child.once('exit', fail); child.stdout.on('data', bytes => { if (bytes.toString().includes('Bot connecté :')) { clearTimeout(timer); child.off('error', fail); child.off('exit', fail); resolve(); } }); });
|
|
@@ -80,6 +82,7 @@ async function deploy(config, message, notify) {
|
|
|
80
82
|
const env = { ...Object.fromEntries(['PATH', 'HOME', 'USERPROFILE', 'SystemRoot', 'WINDIR', 'TEMP', 'TMP', 'LANG', 'TZ'].filter(key => process.env[key]).map(key => [key, process.env[key]])), DISCORD_TOKEN: secret.token, DISCORD_CLIENT_ID: secret.applicationId };
|
|
81
83
|
// Remove credentials unrelated to running the Discord bot.
|
|
82
84
|
for (const key of Object.keys(env)) if (/OFTONE|AGENT_RELAY|CLOUDFLARE|SESSION_SECRET/.test(key)) delete env[key];
|
|
85
|
+
env.OFTONE_API_HOME = home;
|
|
83
86
|
env.OFTONE_VARIABLES_PATH = path.join(home, 'data', 'variables.sqlite');
|
|
84
87
|
await run(process.execPath, ['bot.mjs', '--deploy'], { cwd: target, env, stdio: 'ignore' });
|
|
85
88
|
const previous = child;
|
|
@@ -137,7 +140,7 @@ async function start(config) {
|
|
|
137
140
|
if (!/^[a-f0-9-]{36}$/.test(previous.id)) throw new Error('État local invalide.');
|
|
138
141
|
const secret = await credentials(config);
|
|
139
142
|
const target = path.join(home, 'releases', previous.id);
|
|
140
|
-
child = watchUsage(spawn(process.execPath, ['bot.mjs'], { cwd: target, env: { PATH: process.env.PATH, DISCORD_TOKEN: secret.token, DISCORD_CLIENT_ID: secret.applicationId, OFTONE_VARIABLES_PATH: path.join(home, 'data', 'variables.sqlite') }, stdio: ['ignore', 'ignore', 'ignore', 'ipc'], windowsHide: true, shell: false }));
|
|
143
|
+
child = watchUsage(spawn(process.execPath, ['bot.mjs'], { cwd: target, env: { PATH: process.env.PATH, DISCORD_TOKEN: secret.token, DISCORD_CLIENT_ID: secret.applicationId, OFTONE_API_HOME: home, OFTONE_VARIABLES_PATH: path.join(home, 'data', 'variables.sqlite') }, stdio: ['ignore', 'ignore', 'ignore', 'ipc'], windowsHide: true, shell: false }));
|
|
141
144
|
child.on('error', () => {});
|
|
142
145
|
active = previous.id;
|
|
143
146
|
}
|
|
@@ -157,7 +160,7 @@ async function start(config) {
|
|
|
157
160
|
try { const response = await fetch('https://discord.com/api/v10/users/@me', { headers: { Authorization: 'Bot ' + secret.token }, signal: AbortSignal.timeout(15000) }); if (response.ok) { const user = await response.json(); profile = { id: user.id, username: user.username, avatar: user.avatar }; } } finally { secret.token = ''; }
|
|
158
161
|
} catch {}
|
|
159
162
|
}
|
|
160
|
-
send({ type: 'hello', agentVersion: AGENT_VERSION, updatesVersion: 1, officialUpdatesVersion: 1, protocol: 2, errorBranch: true, diagnosticsVersion: 2, optionsVersion: 1, conditionsVersion: 1, eventsVersion: 1, complementaryBlocksVersion: 1, discordActionsVersion: 1, messageButtonsVersion: 2, variablesVersion: Number(process.versions.node.split('.')[0]) > 22 || (Number(process.versions.node.split('.')[0]) === 22 && Number(process.versions.node.split('.')[1]) >= 5) ? 1 : 0, metricsVersion: usageReady ? 1 : 0, profile, running: Boolean(child && child.exitCode === null && !child.signalCode), hasToken: await exists(secretPath), deploymentId: active });
|
|
163
|
+
send({ type: 'hello', agentVersion: AGENT_VERSION, updatesVersion: 1, officialUpdatesVersion: 1, protocol: 2, errorBranch: true, diagnosticsVersion: 2, optionsVersion: 1, conditionsVersion: 1, eventsVersion: 1, apiVersion: 1, complementaryBlocksVersion: 1, discordActionsVersion: 1, messageButtonsVersion: 2, variablesVersion: Number(process.versions.node.split('.')[0]) > 22 || (Number(process.versions.node.split('.')[0]) === 22 && Number(process.versions.node.split('.')[1]) >= 5) ? 1 : 0, metricsVersion: usageReady ? 1 : 0, profile, running: Boolean(child && child.exitCode === null && !child.signalCode), hasToken: await exists(secretPath), deploymentId: active });
|
|
161
164
|
if (process.connected) process.send?.({ type: 'agent-ready' }, () => {});
|
|
162
165
|
await usage.flush(send).catch(() => console.error('Statistiques locales indisponibles.'));
|
|
163
166
|
usageTimer = setInterval(() => usage.flush(send).catch(() => console.error('Statistiques locales indisponibles.')), 60000);
|
|
@@ -192,7 +195,8 @@ async function start(config) {
|
|
|
192
195
|
if (message.command === 'stop') { await save(path.join(home, 'paused.json'), {}); await stopBot(); await usage.flush(send).catch(() => {}); send({ type: 'runtime-status', state: 'stopped' }); }
|
|
193
196
|
else if (message.command === 'restart') { await restartBot(config); if (await exists(path.join(home, 'paused.json'))) await unlink(path.join(home, 'paused.json')); send({ type: 'runtime-status', state: 'running' }); }
|
|
194
197
|
} catch { send({ type: 'runtime-status', state: 'failed' }); }
|
|
195
|
-
} else if
|
|
198
|
+
} else if(message.type==='api-tool'){try{const result=message.payload.op==='test'?await requestApi(message.payload.config,{resolveSecret:name=>readApiSecret(name,home)}):await secretOperation(home,config.agentId,message.payload);send({type:'api-result',id:message.id,result});}catch{send({type:'api-result',id:message.id,error:'Opération API impossible. Vérifiez la requête et les secrets.'});}return;}
|
|
199
|
+
if (message.type === 'deploy') {
|
|
196
200
|
console.log(`[${new Date().toISOString()}] Nouveau déploiement reçu.`);
|
|
197
201
|
if (await exists(path.join(home, 'paused.json'))) await unlink(path.join(home, 'paused.json'));
|
|
198
202
|
try { await deploy(config, message, state => { send({ type: 'deployment-status', id: message.id, state }); console.log(({ received: 'Déploiement validé.', installing: 'Préparation du bot.', running: 'Déploiement terminé. Bot démarré.', failed: 'Échec du déploiement.' })[state] || 'État du déploiement modifié.'); }); }
|
package/compile-plan.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {validateApiRequest} from './runtime/api-config.mjs';
|
|
1
2
|
import { validateEventGraph } from './runtime/events.mjs';
|
|
2
3
|
import { isVariableBlock, validateVariableBlock, validateVariableDefinitions } from './runtime/custom-variables.mjs';
|
|
3
4
|
import { validateButton } from './runtime/message-buttons.mjs';
|
|
@@ -63,7 +64,7 @@ export function compile(input) {
|
|
|
63
64
|
|
|
64
65
|
if (!plain(block) || typeof block.id !== 'string' || !block.id || block.id.length > 128 || blocks.has(block.id)) fail(`/${name} : identifiant de bloc absent, invalide ou dupliqué.`);
|
|
65
66
|
|
|
66
|
-
if (!isVariableBlock(block.type) && !isDiscordBlock(block.type) && !isOption(block.type) && !isCondition(block.type) && !['root', 'errorRoot', 'conditionBranch', 'messageButton', 'sendMessage', 'wait', 'deleteMessage'].includes(block.type)) fail(`/${name}/${block.id} : type de bloc inconnu : ${block.type}.`);
|
|
67
|
+
if (!isVariableBlock(block.type) && !isDiscordBlock(block.type) && !isOption(block.type) && !isCondition(block.type) && !['root', 'errorRoot', 'conditionBranch', 'apiRequest', 'messageButton', 'sendMessage', 'wait', 'deleteMessage'].includes(block.type)) fail(`/${name}/${block.id} : type de bloc inconnu : ${block.type}.`);
|
|
67
68
|
|
|
68
69
|
if (!plain(block.config || {})) fail(`/${name}/${block.id} : paramètres invalides.`);
|
|
69
70
|
|
|
@@ -249,6 +250,7 @@ export function compile(input) {
|
|
|
249
250
|
buttonBranches.push({id:child.id,parentId:current,config,calls:childCalls});
|
|
250
251
|
}
|
|
251
252
|
|
|
253
|
+
} else if (block.type === 'apiRequest') { fn='apiRequest'; args=validateApiRequest(config); if([...blocks.values()].some(b=>b.id!==block.id&&b.type==='apiRequest'&&b.config?.name===args.name))fail('Chaque requête API doit avoir un nom unique.');
|
|
252
254
|
} else if (block.type === 'wait') {
|
|
253
255
|
|
|
254
256
|
const seconds = Number(config.seconds);
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oft-agt",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.11",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "oft-agt",
|
|
9
|
-
"version": "0.5.
|
|
9
|
+
"version": "0.5.11",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"discord.js": "^14.14.0",
|
|
12
12
|
"ws": "^8.18.0"
|
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function validateApiRequest(c={}) {
|
|
2
|
+
if(!/^[A-Za-z][A-Za-z0-9_]{0,39}$/.test(c.name||''))throw Error('Nom de requête invalide.');
|
|
3
|
+
if(!['GET','POST','PUT','PATCH','DELETE','HEAD','OPTIONS'].includes(c.method))throw Error('Méthode invalide.');
|
|
4
|
+
if(typeof c.url!=='string'||c.url.length>4000||!/^https?:\/\//.test(c.url))throw Error('URL HTTP ou HTTPS requise.');
|
|
5
|
+
const pairs=value=>{if(!Array.isArray(value)||value.length>40)throw Error('Trop de paramètres.');return value.map(p=>{if(typeof p.key!=='string'||typeof p.value!=='string'||p.key.length>200||p.value.length>8000)throw Error('Paramètre invalide.');return {key:p.key,value:p.value};});};
|
|
6
|
+
const outputs=(c.outputs||[]).map(o=>{if(!/^[A-Za-z][A-Za-z0-9_]{0,39}$/.test(o.name)||!Array.isArray(o.path)||o.path.length>16||o.path.some(k=>typeof k!=='string'||['__proto__','prototype','constructor'].includes(k)))throw Error('Champ JSON invalide.');return {name:o.name,path:o.path};});
|
|
7
|
+
if(outputs.some(o=>o.name==='response')||outputs.length>50||new Set(outputs.map(o=>o.name)).size!==outputs.length)throw Error('Variables de résultat invalides.');
|
|
8
|
+
if(typeof(c.body||'')!=='string'||(c.body||'').length>64000)throw Error('Corps trop volumineux.');
|
|
9
|
+
return {name:c.name,method:c.method,url:c.url,params:pairs(c.params||[]),headers:pairs(c.headers||[]),body:c.body||'',outputs,timeout:Math.min(30000,Math.max(1000,Number(c.timeout)||15000))};
|
|
10
|
+
}
|
|
11
|
+
export const apiVariables=c=>[{name:c.name+'_status-code',label:'Code HTTP'},{name:c.name+'_status-text',label:'Statut HTTP'},{name:c.name+'_response',label:'Réponse complète'},...(c.outputs||[]).map(o=>({name:c.name+'_'+o.name,label:o.path.join('.')}))];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import https from 'node:https';
|
|
3
|
+
import {lookup} from 'node:dns';
|
|
4
|
+
import {isIP} from 'node:net';
|
|
5
|
+
import {validateApiRequest} from './api-config.mjs';
|
|
6
|
+
export function publicAddress(ip){if(ip.includes(':'))return /^2[0-9a-f]{3}:/i.test(ip)&&!/^2001:(db8|0):/i.test(ip);const a=ip.split('.').map(Number);return a.length===4&&a[0]>0&&a[0]<224&&![10,127].includes(a[0])&&!(a[0]===169&&a[1]===254)&&!(a[0]===172&&a[1]>=16&&a[1]<=31)&&!(a[0]===192&&a[1]===168)&&!(a[0]===100&&a[1]>=64&&a[1]<=127);}
|
|
7
|
+
export async function requestApi(raw,{resolveSecret=async()=>{throw Error('Secret indisponible.');},signal}={}){
|
|
8
|
+
const resolvedSecrets=[];const c=validateApiRequest(raw),replace=async s=>{let out=s;for(const m of s.matchAll(/\{secret\.([A-Za-z][A-Za-z0-9_]*)\}/g)){const value=await resolveSecret(m[1]);resolvedSecrets.push(value);out=out.replaceAll(m[0],value);}return out;};
|
|
9
|
+
const url=new URL(await replace(c.url));if(url.username||url.password||!['http:','https:'].includes(url.protocol))throw Error('URL refusée.');
|
|
10
|
+
for(const p of c.params)if(p.key)url.searchParams.append(p.key,await replace(p.value));
|
|
11
|
+
const headers={'User-Agent':'Oftone-Agent/1'};for(const h of c.headers)if(h.key){if(/^(host|content-length|connection|transfer-encoding|upgrade)$/i.test(h.key)||/[\r\n]/.test(h.key))throw Error('En-tête refusé.');headers[h.key]=await replace(h.value);}
|
|
12
|
+
const body=await replace(c.body);
|
|
13
|
+
const result=await new Promise((resolve,reject)=>{
|
|
14
|
+
if(isIP(url.hostname.replaceAll(/[\[\]]/g,''))&&!publicAddress(url.hostname.replaceAll(/[\[\]]/g,'')))return reject(Error('Adresse locale refusée.'));
|
|
15
|
+
const req=(url.protocol==='https:'?https:http).request(url,{method:c.method,headers,signal:signal?AbortSignal.any([signal,AbortSignal.timeout(c.timeout)]):AbortSignal.timeout(c.timeout),lookup:(host,options,cb)=>lookup(host,{all:true},(err,addresses)=>{if(err||!addresses.length||addresses.some(a=>!publicAddress(a.address)))return cb(Error('Adresse réseau refusée.'));const a=addresses[0];cb(null,options.all?[a]:a.address,a.family);})},res=>{let size=0;const chunks=[];res.on('data',chunk=>{size+=chunk.length;if(size>262144){req.destroy(Error('Réponse trop volumineuse.'));return;}chunks.push(chunk);});res.on('end',()=>{const text=Buffer.concat(chunks).toString('utf8');let response;try{response=JSON.parse(text);}catch{response=text;}resolve({status:res.statusCode,statusText:res.statusMessage||'',response});});res.on('error',reject);});
|
|
16
|
+
req.setTimeout(c.timeout,()=>req.destroy(Error('Délai de requête dépassé.')));req.on('error',()=>reject(Error('Requête API échouée.')));if(!['GET','HEAD'].includes(c.method))req.write(body);req.end();
|
|
17
|
+
});
|
|
18
|
+
let safe=JSON.stringify(result);for(const secret of resolvedSecrets)if(secret)safe=safe.split(JSON.stringify(secret).slice(1,-1)).join('••••');return JSON.parse(safe);
|
|
19
|
+
}
|
|
20
|
+
export function responseVariables(c,result){const values={[c.name+'_status-code']:String(result.status),[c.name+'_status-text']:result.statusText,[c.name+'_response']:typeof result.response==='string'?result.response:JSON.stringify(result.response)};for(const o of c.outputs){let v=result.response;for(const key of o.path)v=v&&typeof v==='object'&&Object.hasOwn(v,key)?v[key]:undefined;values[c.name+'_'+o.name]=v===undefined?'':typeof v==='object'?JSON.stringify(v):String(v);}return values;}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import {readFile,writeFile,rename,mkdir,unlink,readdir} from 'node:fs/promises';
|
|
2
|
+
import {privateDecrypt,constants,createDecipheriv} from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export function openEnvelope(e,key){const aes=privateDecrypt({key,oaepHash:'sha256',padding:constants.RSA_PKCS1_OAEP_PADDING},Buffer.from(e.key,'base64'));const b=Buffer.from(e.ciphertext,'base64'),d=createDecipheriv('aes-256-gcm',aes,Buffer.from(e.iv,'base64'));d.setAuthTag(b.subarray(-16));return JSON.parse(Buffer.concat([d.update(b.subarray(0,-16)),d.final()]).toString('utf8'));}
|
|
5
|
+
const valid=name=>/^[A-Za-z][A-Za-z0-9_]{0,39}$/.test(name||'');
|
|
6
|
+
export async function secretOperation(home,agentId,p){const directory=path.join(home,'api-secrets');await mkdir(directory,{recursive:true,mode:0o700});if(p.op==='list')return {secrets:(await readdir(directory)).filter(n=>n.endsWith('.json')&&valid(n.slice(0,-5))).map(n=>n.slice(0,-5))};if(!valid(p.name))throw Error('Nom de secret invalide.');const file=path.join(directory,p.name+'.json');if(p.op==='delete'){await unlink(file).catch(e=>{if(e.code!=='ENOENT')throw e;});return {ok:true};}if(p.op!=='set')throw Error('Opération refusée.');const secret=openEnvelope(p.envelope,await readFile(path.join(home,'private.pem'),'utf8'));try{if(secret.agentId!==agentId||secret.name!==p.name||typeof secret.value!=='string'||!secret.value||secret.value.length>8000)throw Error('Secret invalide.');await writeFile(file+'.tmp',JSON.stringify(p.envelope),{mode:0o600});await rename(file+'.tmp',file);return {ok:true};}finally{secret.value='';}}
|
|
7
|
+
export async function readApiSecret(name,home=process.env.OFTONE_API_HOME){if(!valid(name)||!home)throw Error('Secret indisponible.');const e=JSON.parse(await readFile(path.join(home,'api-secrets',name+'.json'),'utf8'));const secret=openEnvelope(e,await readFile(path.join(home,'private.pem'),'utf8'));if(secret.name!==name)throw Error('Secret invalide.');return secret.value;}
|
package/runtime/execute.mjs
CHANGED
|
@@ -84,12 +84,33 @@ async function executeAdmitted(command, interaction, client, options) {
|
|
|
84
84
|
const optionValues = { ...(options.buttonSnapshot?.variables || resolveOptions(command.options, interaction)), ...contextVariables(interaction, client), ...(options.contextVariables || {}) };
|
|
85
85
|
|
|
86
86
|
optionValues.command_name=command.name;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
// Visibility belongs to the configured reply, not to its use of variables.
|
|
88
|
+
// Inspect possible first replies without reading variables, running actions
|
|
89
|
+
// or drawing random branches before execution. A private first reply on any
|
|
90
|
+
// path keeps mixed-visibility commands private until their branch is known.
|
|
91
|
+
function firstReplies(calls) {
|
|
92
|
+
const visibility = new Set();
|
|
93
|
+
for (const call of calls) {
|
|
94
|
+
if (call.fn === 'sendMessage' && (call.args.sendMode || 'reply') === 'reply' && (call.args.replyMode || 'command') === 'command') {
|
|
95
|
+
visibility.add(Boolean(call.args.ephemeral));
|
|
96
|
+
return { visibility, fallsThrough: false };
|
|
97
|
+
}
|
|
98
|
+
if (call.fn === 'condition') {
|
|
99
|
+
let branches = [...call.branches, call.otherwise];
|
|
100
|
+
if (call.args.kind !== 'randomCondition' && !referencedVariables(call.args).length) {
|
|
101
|
+
const args = interpolateOptions(call.args, optionValues);
|
|
102
|
+
const index = selectConditionBranch(args.kind, args.rules, interaction);
|
|
103
|
+
branches = [index < 0 ? call.otherwise : call.branches[index]];
|
|
104
|
+
}
|
|
105
|
+
const paths = branches.map(branch => firstReplies(branch.calls));
|
|
106
|
+
for (const path of paths) for (const value of path.visibility) visibility.add(value);
|
|
107
|
+
if (!paths.some(path => path.fallsThrough)) return { visibility, fallsThrough: false };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { visibility, fallsThrough: true };
|
|
111
|
+
}
|
|
112
|
+
const { visibility } = firstReplies(command.calls);
|
|
113
|
+
const privateAck = !visibility.size || visibility.has(true);
|
|
93
114
|
if (!interaction.eventTrigger) await interaction.deferReply(privateAck ? { flags: 64 } : {});
|
|
94
115
|
if (options.signal.aborted) throw options.signal.reason;
|
|
95
116
|
const context = { interaction, client, messages: new Map(options.buttonSnapshot?.messages), channels: new Map(options.buttonSnapshot?.channels), hasCommandReply: false, signal: options.signal || AbortSignal.timeout(870000), variableStore: options.variableStore, ackEphemeral: privateAck };
|
package/runtime/functions.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {requestApi,responseVariables} from './api-request.mjs';
|
|
2
|
+
import {readApiSecret} from './api-secrets.mjs';
|
|
1
3
|
import { VARIABLE_ACTIONS } from './variable-actions.mjs';
|
|
2
4
|
import { DISCORD_ACTIONS } from './discord-actions.mjs';
|
|
3
5
|
|
|
@@ -153,5 +155,5 @@ async function deleteMessage(context, parameters) {
|
|
|
153
155
|
|
|
154
156
|
// Extend this registry together with compiler validation when adding a new block type.
|
|
155
157
|
|
|
156
|
-
export const FUNCTIONS = Object.freeze(Object.assign(Object.create(null), Object.fromEntries(Object.entries({ sendMessage, wait, deleteMessage, ...DISCORD_ACTIONS, ...VARIABLE_ACTIONS }).map(([name, fn]) => [name, async (context, args) => { try { return await fn(context, args); } catch (error) { throw runtimeError(undefined, name, error); } }]))));
|
|
158
|
+
export const FUNCTIONS = Object.freeze(Object.assign(Object.create(null), Object.fromEntries(Object.entries({ apiRequest: async(context,args)=>{const result=await requestApi(args,{signal:context.signal,resolveSecret:readApiSecret});Object.assign(context.variableOptions.base,responseVariables(args,result));return result;}, sendMessage, wait, deleteMessage, ...DISCORD_ACTIONS, ...VARIABLE_ACTIONS }).map(([name, fn]) => [name, async (context, args) => { try { return await fn(context, args); } catch (error) { throw runtimeError(undefined, name, error); } }]))));
|
|
157
159
|
|
package/version.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_VERSION = '0.5.
|
|
1
|
+
export const AGENT_VERSION = '0.5.11';
|