@automagik/omni 2.260430.2 → 2.260430.4
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/db/drizzle/0034_instances_require_genie_signature.sql +18 -0
- package/db/drizzle/meta/_journal.json +7 -0
- package/dist/commands/instances.d.ts.map +1 -1
- package/dist/commands/trust.d.ts +19 -0
- package/dist/commands/trust.d.ts.map +1 -0
- package/dist/index.js +102 -3
- package/dist/server/index.js +707 -591
- package/package.json +10 -10
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- Per-instance signature enforcement opt-in (omni-host-fingerprint-trust wish, Group 6).
|
|
2
|
+
--
|
|
3
|
+
-- Adds `instances.require_genie_signature` (default false). When true, any
|
|
4
|
+
-- request that targets the instance MUST carry a verified `X-Genie-Signature`
|
|
5
|
+
-- (see middleware/genie-signature.ts). Bearer-only requests get 401.
|
|
6
|
+
--
|
|
7
|
+
-- This flips the verification middleware from "verify when present" to
|
|
8
|
+
-- "require always" on a per-instance basis. Default is false so the rollout
|
|
9
|
+
-- stays additive — existing bearer flows keep working until an operator
|
|
10
|
+
-- explicitly opts the instance in via:
|
|
11
|
+
--
|
|
12
|
+
-- omni instances update <id> --require-genie-signature
|
|
13
|
+
--
|
|
14
|
+
-- Hand-written (not `drizzle-kit generate`) to avoid the gupshup column
|
|
15
|
+
-- rename prompts that block non-interactive runs in this repo.
|
|
16
|
+
|
|
17
|
+
ALTER TABLE "instances"
|
|
18
|
+
ADD COLUMN IF NOT EXISTS "require_genie_signature" boolean DEFAULT false NOT NULL;
|
|
@@ -239,6 +239,13 @@
|
|
|
239
239
|
"when": 1777590000000,
|
|
240
240
|
"tag": "0033_close_contact_logs",
|
|
241
241
|
"breakpoints": true
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"idx": 34,
|
|
245
|
+
"version": "7",
|
|
246
|
+
"when": 1777680000000,
|
|
247
|
+
"tag": "0034_instances_require_genie_signature",
|
|
248
|
+
"breakpoints": true
|
|
242
249
|
}
|
|
243
250
|
]
|
|
244
251
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../src/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../src/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgMpC,wBAAgB,sBAAsB,IAAI,OAAO,CAkuChD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust Commands — manage genie host fingerprint registrations.
|
|
3
|
+
*
|
|
4
|
+
* omni trust list List active genie hosts
|
|
5
|
+
* omni trust get <id> Show one host (active or revoked)
|
|
6
|
+
* omni trust update <id> --scope <a,b,c> Replace host scopes wholesale
|
|
7
|
+
* omni trust revoke <id> Soft-delete (stamps revoked_at)
|
|
8
|
+
*
|
|
9
|
+
* Wish: omni-host-fingerprint-trust, Group 1.2. Talks to the
|
|
10
|
+
* `/api/v2/trust/hosts` endpoints landed in Group 1.1 (#556).
|
|
11
|
+
*
|
|
12
|
+
* Why raw fetch instead of the OmniClient SDK: trust types aren't in the
|
|
13
|
+
* OpenAPI spec yet (the SDK is generated from there). Adding them requires
|
|
14
|
+
* a regen + version bump; out of scope for this PR. We use the same
|
|
15
|
+
* baseUrl / apiKey the SDK uses, so behavior is identical.
|
|
16
|
+
*/
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
export declare function createTrustCommand(): Command;
|
|
19
|
+
//# sourceMappingURL=trust.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust.d.ts","sourceRoot":"","sources":["../../src/commands/trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwHpC,wBAAgB,kBAAkB,IAAI,OAAO,CAmB5C"}
|
package/dist/index.js
CHANGED
|
@@ -58978,6 +58978,7 @@ var init_schema2 = __esm(() => {
|
|
|
58978
58978
|
chainMode: varchar("chain_mode", { length: 20 }).notNull().default("off"),
|
|
58979
58979
|
followUpConfig: jsonb("follow_up_config").$type(),
|
|
58980
58980
|
bridgeTmuxSession: text("bridge_tmux_session"),
|
|
58981
|
+
requireGenieSignature: boolean("require_genie_signature").notNull().default(false),
|
|
58981
58982
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
58982
58983
|
updatedAt: timestamp("updated_at").notNull().defaultNow()
|
|
58983
58984
|
}, (table3) => ({
|
|
@@ -113980,7 +113981,7 @@ import { fileURLToPath } from "url";
|
|
|
113980
113981
|
// package.json
|
|
113981
113982
|
var package_default = {
|
|
113982
113983
|
name: "@automagik/omni",
|
|
113983
|
-
version: "2.260430.
|
|
113984
|
+
version: "2.260430.4",
|
|
113984
113985
|
description: "LLM-optimized CLI for Omni",
|
|
113985
113986
|
type: "module",
|
|
113986
113987
|
bin: {
|
|
@@ -120451,6 +120452,7 @@ function applyMiscFields(body, opts) {
|
|
|
120451
120452
|
setVal(body, "twilioWebhookUrl", opts.twilioWebhookUrl);
|
|
120452
120453
|
setBool(body, "twilioValidateSignature", opts.twilioValidateSignature);
|
|
120453
120454
|
setVal(body, "bridgeTmuxSession", opts.bridgeTmuxSession);
|
|
120455
|
+
setBool(body, "requireGenieSignature", opts.requireGenieSignature);
|
|
120454
120456
|
if (opts.triggerEvents !== undefined) {
|
|
120455
120457
|
const raw2 = opts.triggerEvents;
|
|
120456
120458
|
body.triggerEvents = raw2 === "null" ? null : raw2.split(",").map((s) => s.trim());
|
|
@@ -120561,7 +120563,7 @@ function createInstancesCommand() {
|
|
|
120561
120563
|
error(`Failed to get instance: ${message}`, undefined, 3);
|
|
120562
120564
|
}
|
|
120563
120565
|
});
|
|
120564
|
-
instances.command("create").description("Create a new instance (supports all API fields)").requiredOption("--name <name>", "Instance name").requiredOption("--channel <type>", `Channel type (${VALID_CHANNELS2.join(", ")})`).option("--agent-fk-id <uuid>", 'Agent FK UUID (references agents table, use "null" to clear). When set without --reply-filter-mode, reply filter defaults to {mode:"all", onDm:true} so messages are dispatched instead of silently dropped (omni#443).').option("--agent-provider <id>", "Agent provider ID").option("--agent <id>", "Agent ID").option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v) => Number.parseInt(v, 10)).option("--agent-stream-mode", "Enable streaming responses").option("--agent-session-strategy <strategy>", "Session strategy: per_user, per_chat, per_user_per_chat").option("--agent-prefix-sender-name", "Prefix messages with sender name").option("--no-agent-prefix-sender-name", "Disable sender name prefix").option("--agent-wait-for-media", "Wait for media processing before dispatch").option("--no-agent-wait-for-media", "Dispatch immediately without waiting for media").option("--agent-send-media-path", "Include file path in formatted media text").option("--no-agent-send-media-path", "Exclude file path from formatted media text").option("--agent-send-media-path-types <types>", "Content types that receive file path (comma-separated: image,video,document)").option("--reply-filter-mode <mode>", "Reply filter: all or filtered").option("--reply-on-dm", "Reply to DMs").option("--no-reply-on-dm", "Ignore DMs").option("--reply-on-mention", "Reply when @mentioned").option("--no-reply-on-mention", "Ignore @mentions").option("--reply-on-reply", "Reply when message is reply to bot").option("--no-reply-on-reply", "Ignore replies").option("--reply-on-name", "Reply when bot name appears in text").option("--no-reply-on-name", "Ignore name matches").option("--reply-name-patterns <patterns>", "Custom name patterns (comma-separated)").option("--enable-auto-split", "Split responses on double newlines").option("--no-enable-auto-split", "Disable auto-split").option("--message-format-mode <mode>", "Format mode: convert or passthrough").option("--debounce-mode <mode>", "Debounce mode: disabled, fixed, or randomized").option("--debounce-min <ms>", "Minimum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--debounce-group <ms>", "Group chat debounce in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-mode <mode>", "Split delay mode: disabled, fixed, or randomized").option("--split-delay-fixed <ms>", "Fixed delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--agent-gate", "Enable LLM response gate").option("--agent-gate-model <model>", "Model for response gate").option("--agent-gate-prompt <prompt>", "Custom gate prompt").option("--tts-voice <id>", "ElevenLabs voice ID").option("--tts-model <id>", "ElevenLabs model ID").option("--read-receipts <mode>", "Read receipts mode: on, off, or exclude-self").option("--access-mode <mode>", "Access mode: disabled, blocklist, or allowlist").option("--reaction-ack <mode>", "Reaction ack mode (on|off)").option("--reaction-ack-emoji <json>", "Per-channel emoji map as JSON").option("--ack-timeout <ms>", "Ack timeout in milliseconds", (v) => Number.parseInt(v, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v) => Number.parseInt(v, 10)).option("--token <token>", "Generic bot token (auto-resolves to channel-specific field)").option("--telegram-token <token>", "Telegram bot token").option("--discord-token <token>", "Discord bot token").option("--slack-bot-token <token>", "Slack bot token").option("--slack-app-token <token>", "Slack app token").option("--gupshup-callback-url <url>", "Gupshup Custom Integration callback URL").option("--gupshup-auth-token <token>", "Gupshup Custom Integration auth token").option("--gupshup-event-id <id>", "Gupshup event ID (default: nx_omni_agent_reply)").option("--gupshup-webhook-verify-token <token>", "Gupshup webhook verify token").option("--twilio-account-sid <sid>", "Twilio Account SID").option("--twilio-auth-token <token>", "Twilio Auth Token").option("--twilio-from <address>", "Twilio WhatsApp sender address (whatsapp:+E164)").option("--twilio-messaging-service-sid <sid>", "Twilio Messaging Service SID").option("--twilio-status-callback-url <url>", "Twilio outbound status callback URL").option("--twilio-webhook-url <url>", "Public Twilio webhook URL for signature validation").option("--twilio-validate-signature", "Validate X-Twilio-Signature on webhooks").option("--no-twilio-validate-signature", "Disable X-Twilio-Signature validation").option("--bridge-tmux-session <name>", 'Tmux session name the genie bridge spawns into for this instance (propagated as GENIE_TMUX_SESSION via NATS). Use "null" to clear.').option("--is-default", "Set as default instance for channel").action(async (options) => {
|
|
120566
|
+
instances.command("create").description("Create a new instance (supports all API fields)").requiredOption("--name <name>", "Instance name").requiredOption("--channel <type>", `Channel type (${VALID_CHANNELS2.join(", ")})`).option("--agent-fk-id <uuid>", 'Agent FK UUID (references agents table, use "null" to clear). When set without --reply-filter-mode, reply filter defaults to {mode:"all", onDm:true} so messages are dispatched instead of silently dropped (omni#443).').option("--agent-provider <id>", "Agent provider ID").option("--agent <id>", "Agent ID").option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v) => Number.parseInt(v, 10)).option("--agent-stream-mode", "Enable streaming responses").option("--agent-session-strategy <strategy>", "Session strategy: per_user, per_chat, per_user_per_chat").option("--agent-prefix-sender-name", "Prefix messages with sender name").option("--no-agent-prefix-sender-name", "Disable sender name prefix").option("--agent-wait-for-media", "Wait for media processing before dispatch").option("--no-agent-wait-for-media", "Dispatch immediately without waiting for media").option("--agent-send-media-path", "Include file path in formatted media text").option("--no-agent-send-media-path", "Exclude file path from formatted media text").option("--agent-send-media-path-types <types>", "Content types that receive file path (comma-separated: image,video,document)").option("--reply-filter-mode <mode>", "Reply filter: all or filtered").option("--reply-on-dm", "Reply to DMs").option("--no-reply-on-dm", "Ignore DMs").option("--reply-on-mention", "Reply when @mentioned").option("--no-reply-on-mention", "Ignore @mentions").option("--reply-on-reply", "Reply when message is reply to bot").option("--no-reply-on-reply", "Ignore replies").option("--reply-on-name", "Reply when bot name appears in text").option("--no-reply-on-name", "Ignore name matches").option("--reply-name-patterns <patterns>", "Custom name patterns (comma-separated)").option("--enable-auto-split", "Split responses on double newlines").option("--no-enable-auto-split", "Disable auto-split").option("--message-format-mode <mode>", "Format mode: convert or passthrough").option("--debounce-mode <mode>", "Debounce mode: disabled, fixed, or randomized").option("--debounce-min <ms>", "Minimum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--debounce-group <ms>", "Group chat debounce in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-mode <mode>", "Split delay mode: disabled, fixed, or randomized").option("--split-delay-fixed <ms>", "Fixed delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--agent-gate", "Enable LLM response gate").option("--agent-gate-model <model>", "Model for response gate").option("--agent-gate-prompt <prompt>", "Custom gate prompt").option("--tts-voice <id>", "ElevenLabs voice ID").option("--tts-model <id>", "ElevenLabs model ID").option("--read-receipts <mode>", "Read receipts mode: on, off, or exclude-self").option("--access-mode <mode>", "Access mode: disabled, blocklist, or allowlist").option("--reaction-ack <mode>", "Reaction ack mode (on|off)").option("--reaction-ack-emoji <json>", "Per-channel emoji map as JSON").option("--ack-timeout <ms>", "Ack timeout in milliseconds", (v) => Number.parseInt(v, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v) => Number.parseInt(v, 10)).option("--token <token>", "Generic bot token (auto-resolves to channel-specific field)").option("--telegram-token <token>", "Telegram bot token").option("--discord-token <token>", "Discord bot token").option("--slack-bot-token <token>", "Slack bot token").option("--slack-app-token <token>", "Slack app token").option("--gupshup-callback-url <url>", "Gupshup Custom Integration callback URL").option("--gupshup-auth-token <token>", "Gupshup Custom Integration auth token").option("--gupshup-event-id <id>", "Gupshup event ID (default: nx_omni_agent_reply)").option("--gupshup-webhook-verify-token <token>", "Gupshup webhook verify token").option("--twilio-account-sid <sid>", "Twilio Account SID").option("--twilio-auth-token <token>", "Twilio Auth Token").option("--twilio-from <address>", "Twilio WhatsApp sender address (whatsapp:+E164)").option("--twilio-messaging-service-sid <sid>", "Twilio Messaging Service SID").option("--twilio-status-callback-url <url>", "Twilio outbound status callback URL").option("--twilio-webhook-url <url>", "Public Twilio webhook URL for signature validation").option("--twilio-validate-signature", "Validate X-Twilio-Signature on webhooks").option("--no-twilio-validate-signature", "Disable X-Twilio-Signature validation").option("--bridge-tmux-session <name>", 'Tmux session name the genie bridge spawns into for this instance (propagated as GENIE_TMUX_SESSION via NATS). Use "null" to clear.').option("--require-genie-signature", "Require a verified X-Genie-Signature on requests targeting this instance. Bearer-only requests will be rejected with 401.").option("--no-require-genie-signature", "Allow bearer-only requests targeting this instance (default).").option("--is-default", "Set as default instance for channel").action(async (options) => {
|
|
120565
120567
|
const channel = options.channel;
|
|
120566
120568
|
if (!VALID_CHANNELS2.includes(channel)) {
|
|
120567
120569
|
error(`Invalid channel: ${channel}`, { validChannels: VALID_CHANNELS2 });
|
|
@@ -120829,7 +120831,7 @@ function createInstancesCommand() {
|
|
|
120829
120831
|
throw new Error(err?.error?.message ?? `HTTP ${response.status}`);
|
|
120830
120832
|
}
|
|
120831
120833
|
}
|
|
120832
|
-
instances.command("update <id>").description("Update an instance (supports all API fields)").option("--name <name>", "Instance name").option("--is-default", "Set as default instance for channel").option("--no-is-default", "Unset as default instance for channel").option("--agent-fk-id <uuid>", 'Agent FK UUID (references agents table, use "null" to clear). When assigning an agent on an instance with no reply filter, the filter defaults to {mode:"all", onDm:true} so messages are dispatched instead of silently dropped (omni#443).').option("--agent-provider <id>", 'Agent provider ID (use "null" to clear)').option("--agent <id>", 'Agent ID (use "null" to clear)').option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v) => Number.parseInt(v, 10)).option("--agent-stream-mode", "Enable streaming responses").option("--no-agent-stream-mode", "Disable streaming responses").option("--agent-session-strategy <strategy>", "Session strategy: per_user, per_chat, per_user_per_chat").option("--agent-prefix-sender-name", "Prefix messages with sender name").option("--no-agent-prefix-sender-name", "Disable sender name prefix").option("--agent-wait-for-media", "Wait for media processing before dispatch").option("--no-agent-wait-for-media", "Dispatch immediately without waiting for media").option("--agent-send-media-path", "Include file path in formatted media text").option("--no-agent-send-media-path", "Exclude file path from formatted media text").option("--agent-send-media-path-types <types>", "Content types that receive file path (comma-separated: image,video,document)").option("--reply-filter-mode <mode>", "Reply filter: all or filtered").option("--reply-on-dm", "Reply to DMs (requires --reply-filter-mode filtered)").option("--no-reply-on-dm", "Ignore DMs").option("--reply-on-mention", "Reply when @mentioned").option("--no-reply-on-mention", "Ignore @mentions").option("--reply-on-reply", "Reply when message is reply to bot").option("--no-reply-on-reply", "Ignore replies").option("--reply-on-name", "Reply when bot name appears in text").option("--no-reply-on-name", "Ignore name matches").option("--reply-name-patterns <patterns>", "Custom name patterns (comma-separated)").option("--clear-reply-filter", "Remove reply filter (set to null)").option("--enable-auto-split", "Split responses on double newlines").option("--no-enable-auto-split", "Disable auto-split").option("--message-format-mode <mode>", "Format mode: convert or passthrough").option("--debounce-mode <mode>", "Debounce mode: disabled, fixed, or randomized").option("--debounce-min <ms>", "Minimum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--no-debounce-restart-on-typing", "Do not restart debounce on typing").option("--debounce-group <ms>", 'Group chat debounce in ms (use "null" to inherit)', (v) => v === "null" ? null : Number.parseInt(v, 10)).option("--split-delay-mode <mode>", "Split delay mode: disabled, fixed, or randomized").option("--split-delay-fixed <ms>", "Fixed delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--agent-gate", "Enable LLM response gate").option("--no-agent-gate", "Disable LLM response gate").option("--agent-gate-model <model>", 'Model for response gate (use "null" for default)').option("--agent-gate-prompt <prompt>", 'Custom gate prompt (use "null" for default)').option("--tts-voice <id>", 'ElevenLabs voice ID (use "null" to clear)').option("--tts-model <id>", 'ElevenLabs model ID (use "null" to clear)').option("--read-receipts <mode>", "Read receipts mode: on, off, or exclude-self").option("--access-mode <mode>", "Access mode: disabled, blocklist, or allowlist").option("--reaction-ack <mode>", "Reaction ack mode (on|off)").option("--reaction-ack-emoji <json>", "Per-channel emoji map as JSON").option("--ack-timeout <ms>", "Ack timeout in milliseconds", (v) => Number.parseInt(v, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v) => Number.parseInt(v, 10)).option("--token <token>", "Generic bot token (auto-resolves to channel-specific field)").option("--telegram-token <token>", 'Telegram bot token (use "null" to clear)').option("--discord-token <token>", 'Discord bot token (use "null" to clear)').option("--slack-bot-token <token>", 'Slack bot token (use "null" to clear)').option("--slack-app-token <token>", 'Slack app token (use "null" to clear)').option("--twilio-account-sid <sid>", 'Twilio Account SID (use "null" to clear)').option("--twilio-auth-token <token>", 'Twilio Auth Token (use "null" to clear)').option("--twilio-from <address>", 'Twilio WhatsApp sender address (use "null" to clear)').option("--twilio-messaging-service-sid <sid>", 'Twilio Messaging Service SID (use "null" to clear)').option("--twilio-status-callback-url <url>", 'Twilio outbound status callback URL (use "null" to clear)').option("--twilio-webhook-url <url>", 'Public Twilio webhook URL for signature validation (use "null" to clear)').option("--twilio-validate-signature", "Validate X-Twilio-Signature on webhooks").option("--no-twilio-validate-signature", "Disable X-Twilio-Signature validation").option("--trigger-events <events>", 'Trigger events (comma-separated, use "null" to clear)').option("--profile-name <name>", "Update WhatsApp display name (push name)").option("--bridge-tmux-session <name>", 'Tmux session name the genie bridge spawns into for this instance (propagated as GENIE_TMUX_SESSION via NATS). Use "null" to clear.').action(async (rawId, options) => {
|
|
120834
|
+
instances.command("update <id>").description("Update an instance (supports all API fields)").option("--name <name>", "Instance name").option("--is-default", "Set as default instance for channel").option("--no-is-default", "Unset as default instance for channel").option("--agent-fk-id <uuid>", 'Agent FK UUID (references agents table, use "null" to clear). When assigning an agent on an instance with no reply filter, the filter defaults to {mode:"all", onDm:true} so messages are dispatched instead of silently dropped (omni#443).').option("--agent-provider <id>", 'Agent provider ID (use "null" to clear)').option("--agent <id>", 'Agent ID (use "null" to clear)').option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v) => Number.parseInt(v, 10)).option("--agent-stream-mode", "Enable streaming responses").option("--no-agent-stream-mode", "Disable streaming responses").option("--agent-session-strategy <strategy>", "Session strategy: per_user, per_chat, per_user_per_chat").option("--agent-prefix-sender-name", "Prefix messages with sender name").option("--no-agent-prefix-sender-name", "Disable sender name prefix").option("--agent-wait-for-media", "Wait for media processing before dispatch").option("--no-agent-wait-for-media", "Dispatch immediately without waiting for media").option("--agent-send-media-path", "Include file path in formatted media text").option("--no-agent-send-media-path", "Exclude file path from formatted media text").option("--agent-send-media-path-types <types>", "Content types that receive file path (comma-separated: image,video,document)").option("--reply-filter-mode <mode>", "Reply filter: all or filtered").option("--reply-on-dm", "Reply to DMs (requires --reply-filter-mode filtered)").option("--no-reply-on-dm", "Ignore DMs").option("--reply-on-mention", "Reply when @mentioned").option("--no-reply-on-mention", "Ignore @mentions").option("--reply-on-reply", "Reply when message is reply to bot").option("--no-reply-on-reply", "Ignore replies").option("--reply-on-name", "Reply when bot name appears in text").option("--no-reply-on-name", "Ignore name matches").option("--reply-name-patterns <patterns>", "Custom name patterns (comma-separated)").option("--clear-reply-filter", "Remove reply filter (set to null)").option("--enable-auto-split", "Split responses on double newlines").option("--no-enable-auto-split", "Disable auto-split").option("--message-format-mode <mode>", "Format mode: convert or passthrough").option("--debounce-mode <mode>", "Debounce mode: disabled, fixed, or randomized").option("--debounce-min <ms>", "Minimum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--no-debounce-restart-on-typing", "Do not restart debounce on typing").option("--debounce-group <ms>", 'Group chat debounce in ms (use "null" to inherit)', (v) => v === "null" ? null : Number.parseInt(v, 10)).option("--split-delay-mode <mode>", "Split delay mode: disabled, fixed, or randomized").option("--split-delay-fixed <ms>", "Fixed delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--agent-gate", "Enable LLM response gate").option("--no-agent-gate", "Disable LLM response gate").option("--agent-gate-model <model>", 'Model for response gate (use "null" for default)').option("--agent-gate-prompt <prompt>", 'Custom gate prompt (use "null" for default)').option("--tts-voice <id>", 'ElevenLabs voice ID (use "null" to clear)').option("--tts-model <id>", 'ElevenLabs model ID (use "null" to clear)').option("--read-receipts <mode>", "Read receipts mode: on, off, or exclude-self").option("--access-mode <mode>", "Access mode: disabled, blocklist, or allowlist").option("--reaction-ack <mode>", "Reaction ack mode (on|off)").option("--reaction-ack-emoji <json>", "Per-channel emoji map as JSON").option("--ack-timeout <ms>", "Ack timeout in milliseconds", (v) => Number.parseInt(v, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v) => Number.parseInt(v, 10)).option("--token <token>", "Generic bot token (auto-resolves to channel-specific field)").option("--telegram-token <token>", 'Telegram bot token (use "null" to clear)').option("--discord-token <token>", 'Discord bot token (use "null" to clear)').option("--slack-bot-token <token>", 'Slack bot token (use "null" to clear)').option("--slack-app-token <token>", 'Slack app token (use "null" to clear)').option("--twilio-account-sid <sid>", 'Twilio Account SID (use "null" to clear)').option("--twilio-auth-token <token>", 'Twilio Auth Token (use "null" to clear)').option("--twilio-from <address>", 'Twilio WhatsApp sender address (use "null" to clear)').option("--twilio-messaging-service-sid <sid>", 'Twilio Messaging Service SID (use "null" to clear)').option("--twilio-status-callback-url <url>", 'Twilio outbound status callback URL (use "null" to clear)').option("--twilio-webhook-url <url>", 'Public Twilio webhook URL for signature validation (use "null" to clear)').option("--twilio-validate-signature", "Validate X-Twilio-Signature on webhooks").option("--no-twilio-validate-signature", "Disable X-Twilio-Signature validation").option("--trigger-events <events>", 'Trigger events (comma-separated, use "null" to clear)').option("--profile-name <name>", "Update WhatsApp display name (push name)").option("--bridge-tmux-session <name>", 'Tmux session name the genie bridge spawns into for this instance (propagated as GENIE_TMUX_SESSION via NATS). Use "null" to clear.').option("--require-genie-signature", "Require a verified X-Genie-Signature on requests targeting this instance. Bearer-only requests will be rejected with 401.").option("--no-require-genie-signature", "Allow bearer-only requests targeting this instance (default).").action(async (rawId, options) => {
|
|
120833
120835
|
const client = getClient();
|
|
120834
120836
|
try {
|
|
120835
120837
|
const id = await resolveInstanceId(rawId);
|
|
@@ -124978,6 +124980,97 @@ function createStopCommand() {
|
|
|
124978
124980
|
return new Command("stop").description("Stop all omni PM2 processes").action(runStop);
|
|
124979
124981
|
}
|
|
124980
124982
|
|
|
124983
|
+
// src/commands/trust.ts
|
|
124984
|
+
init_config();
|
|
124985
|
+
init_output();
|
|
124986
|
+
function trustEndpoint(path) {
|
|
124987
|
+
if (!hasAuth()) {
|
|
124988
|
+
error("Not authenticated. Run: omni auth login --api-key <key>", undefined, 2);
|
|
124989
|
+
}
|
|
124990
|
+
const config2 = loadConfig();
|
|
124991
|
+
const baseUrl = (config2.apiUrl ?? "http://localhost:8882").replace(/\/+$/, "");
|
|
124992
|
+
return `${baseUrl}/api/v2/trust${path}`;
|
|
124993
|
+
}
|
|
124994
|
+
function authHeaders() {
|
|
124995
|
+
const config2 = loadConfig();
|
|
124996
|
+
const headers = { "Content-Type": "application/json" };
|
|
124997
|
+
if (config2.apiKey) {
|
|
124998
|
+
headers.Authorization = `Bearer ${config2.apiKey}`;
|
|
124999
|
+
}
|
|
125000
|
+
return headers;
|
|
125001
|
+
}
|
|
125002
|
+
async function callApi(method, path, body) {
|
|
125003
|
+
const res = await fetch(trustEndpoint(path), {
|
|
125004
|
+
method,
|
|
125005
|
+
headers: authHeaders(),
|
|
125006
|
+
body: body === undefined ? undefined : JSON.stringify(body)
|
|
125007
|
+
});
|
|
125008
|
+
if (!res.ok) {
|
|
125009
|
+
const text3 = await res.text().catch(() => "");
|
|
125010
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}${text3 ? `: ${text3}` : ""}`);
|
|
125011
|
+
}
|
|
125012
|
+
return await res.json();
|
|
125013
|
+
}
|
|
125014
|
+
function formatHostRow(host) {
|
|
125015
|
+
return {
|
|
125016
|
+
id: host.id.slice(0, 8),
|
|
125017
|
+
hostname: host.hostname,
|
|
125018
|
+
scopes: host.scopes.join(", ") || "(none)",
|
|
125019
|
+
pubkeyPrefix: `${host.pubkey.slice(0, 12)}\u2026`,
|
|
125020
|
+
lastSeen: host.lastSeenAt ? new Date(host.lastSeenAt).toISOString().replace("T", " ").slice(0, 16) : "never",
|
|
125021
|
+
status: host.revokedAt ? "revoked" : "active"
|
|
125022
|
+
};
|
|
125023
|
+
}
|
|
125024
|
+
async function handleList4() {
|
|
125025
|
+
try {
|
|
125026
|
+
const { items } = await callApi("GET", "/hosts");
|
|
125027
|
+
list(items.map(formatHostRow), {
|
|
125028
|
+
emptyMessage: "No genie hosts registered. Run `genie omni handshake` from a genie installation to register one.",
|
|
125029
|
+
rawData: items
|
|
125030
|
+
});
|
|
125031
|
+
} catch (err2) {
|
|
125032
|
+
error(`Failed to list genie hosts: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
|
|
125033
|
+
}
|
|
125034
|
+
}
|
|
125035
|
+
async function handleGet3(id) {
|
|
125036
|
+
try {
|
|
125037
|
+
const { data: data2 } = await callApi("GET", `/hosts/${encodeURIComponent(id)}`);
|
|
125038
|
+
data(data2);
|
|
125039
|
+
} catch (err2) {
|
|
125040
|
+
error(`Failed to get genie host: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
|
|
125041
|
+
}
|
|
125042
|
+
}
|
|
125043
|
+
async function handleUpdate3(id, options3) {
|
|
125044
|
+
const scopes = options3.scope.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
125045
|
+
if (scopes.length === 0) {
|
|
125046
|
+
error("--scope must contain at least one scope (use `omni trust revoke <id>` to deny everything).");
|
|
125047
|
+
}
|
|
125048
|
+
try {
|
|
125049
|
+
const { data: data2 } = await callApi("PATCH", `/hosts/${encodeURIComponent(id)}`, { scopes });
|
|
125050
|
+
success(`Updated genie host ${data2.id} scopes: ${data2.scopes.join(", ")}`);
|
|
125051
|
+
data(data2);
|
|
125052
|
+
} catch (err2) {
|
|
125053
|
+
error(`Failed to update genie host: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
|
|
125054
|
+
}
|
|
125055
|
+
}
|
|
125056
|
+
async function handleRevoke2(id) {
|
|
125057
|
+
try {
|
|
125058
|
+
const { data: data2 } = await callApi("DELETE", `/hosts/${encodeURIComponent(id)}`);
|
|
125059
|
+
success(`Revoked genie host ${data2.id} (${data2.hostname}). Tombstone kept for audit.`);
|
|
125060
|
+
data(data2);
|
|
125061
|
+
} catch (err2) {
|
|
125062
|
+
error(`Failed to revoke genie host: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
|
|
125063
|
+
}
|
|
125064
|
+
}
|
|
125065
|
+
function createTrustCommand() {
|
|
125066
|
+
const trust = new Command("trust").description("Manage genie host fingerprint registrations");
|
|
125067
|
+
trust.command("list").description("List active (non-revoked) genie hosts").action(handleList4);
|
|
125068
|
+
trust.command("get <id>").description("Show details for one genie host (active or revoked)").action(handleGet3);
|
|
125069
|
+
trust.command("update <id>").description("Replace a genie host scopes (comma-separated)").requiredOption("--scope <list>", 'Comma-separated scope list, e.g. "agents:write,providers:write"').action(handleUpdate3);
|
|
125070
|
+
trust.command("revoke <id>").description("Revoke a genie host (irreversible \u2014 re-register with a fresh keypair to restore trust)").action(handleRevoke2);
|
|
125071
|
+
return trust;
|
|
125072
|
+
}
|
|
125073
|
+
|
|
124981
125074
|
// src/commands/tts.ts
|
|
124982
125075
|
init_output();
|
|
124983
125076
|
function createTtsCommand() {
|
|
@@ -126211,6 +126304,12 @@ var COMMANDS = [
|
|
|
126211
126304
|
helpGroup: "Management",
|
|
126212
126305
|
helpDescription: "API key management"
|
|
126213
126306
|
},
|
|
126307
|
+
{
|
|
126308
|
+
create: createTrustCommand,
|
|
126309
|
+
category: "advanced",
|
|
126310
|
+
helpGroup: "Management",
|
|
126311
|
+
helpDescription: "Genie host fingerprint trust (omni-host-fingerprint-trust wish)"
|
|
126312
|
+
},
|
|
126214
126313
|
{
|
|
126215
126314
|
create: createAccessCommand,
|
|
126216
126315
|
category: "advanced",
|