omnikey-cli 1.0.27 → 1.0.29

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/dist/index.js CHANGED
@@ -10,6 +10,8 @@ const status_1 = require("./status");
10
10
  const showLogs_1 = require("./showLogs");
11
11
  const showConfig_1 = require("./showConfig");
12
12
  const setConfig_1 = require("./setConfig");
13
+ const grantBrowserAccess_1 = require("./grantBrowserAccess");
14
+ const scheduleJob_1 = require("./scheduleJob");
13
15
  const program = new commander_1.Command();
14
16
  program
15
17
  .name('omnikey')
@@ -79,4 +81,37 @@ program
79
81
  const port = Number(options.port) || 7071;
80
82
  await (0, daemon_1.startDaemon)(port);
81
83
  });
84
+ program
85
+ .command('grant-browser-access')
86
+ .description('Set up authenticated browser tab access for web fetch. ' +
87
+ 'Detects installed browsers, selects a profile, and configures a remote debugging port (CDP). ' +
88
+ 'On macOS you can also choose AppleScript mode instead.')
89
+ .action(async () => {
90
+ await (0, grantBrowserAccess_1.grantBrowserAccess)();
91
+ });
92
+ program
93
+ .command('browser open')
94
+ .description('Reopen the browser with the Omnikey debug profile')
95
+ .action(async () => {
96
+ await (0, grantBrowserAccess_1.reopenBrowserDebugProfile)();
97
+ });
98
+ const scheduleCmd = program
99
+ .command('schedule')
100
+ .description('Manage scheduled prompt jobs');
101
+ scheduleCmd
102
+ .command('add')
103
+ .description('Add a new scheduled job')
104
+ .action(async () => { await (0, scheduleJob_1.scheduleAdd)(); });
105
+ scheduleCmd
106
+ .command('list')
107
+ .description('List all scheduled jobs')
108
+ .action(async () => { await (0, scheduleJob_1.scheduleList)(); });
109
+ scheduleCmd
110
+ .command('remove')
111
+ .description('Remove a scheduled job')
112
+ .action(async () => { await (0, scheduleJob_1.scheduleRemove)(); });
113
+ scheduleCmd
114
+ .command('run-now <id>')
115
+ .description('Immediately run a scheduled job by ID')
116
+ .action(async (id) => { await (0, scheduleJob_1.scheduleRunNow)(id); });
82
117
  program.parseAsync(process.argv);
package/dist/onboard.js CHANGED
@@ -14,11 +14,12 @@ const AI_PROVIDERS = [
14
14
  { name: 'Google Gemini (gemini-2.5-flash / gemini-2.5-pro)', value: 'gemini' },
15
15
  ];
16
16
  const SEARCH_PROVIDERS = [
17
- { name: 'Skip (DuckDuckGo will be used by default — no key required)', value: 'skip' },
18
- { name: 'Serper (Google Search API — serper.dev, 2,500 free/mo)', value: 'serper' },
19
- { name: 'Brave Search (brave.com/search/api, 2,000 free/mo)', value: 'brave' },
20
- { name: 'Tavily (tavily.com, 1,000 free/mo, optimized for AI)', value: 'tavily' },
21
- { name: 'SearXNG (self-hosted, no key needed — provide your instance URL)', value: 'searxng' },
17
+ { name: 'Skip', value: 'skip' },
18
+ { name: 'DuckDuckGo', value: 'duckduckgo' },
19
+ { name: 'Serper', value: 'serper' },
20
+ { name: 'Brave Search', value: 'brave' },
21
+ { name: 'Tavily', value: 'tavily' },
22
+ { name: 'SearXNG', value: 'searxng' },
22
23
  ];
23
24
  const AI_PROVIDER_KEY_ENV = {
24
25
  openai: 'OPENAI_API_KEY',
@@ -59,7 +60,7 @@ async function onboard() {
59
60
  {
60
61
  type: 'list',
61
62
  name: 'provider',
62
- message: 'Select a web search provider for the AI agent (enhances research capabilities):',
63
+ message: 'Select a web search provider for the AI agent. Supported providers: DuckDuckGo, Serper, Brave Search, Tavily, SearXNG:',
63
64
  choices: SEARCH_PROVIDERS,
64
65
  default: 'skip',
65
66
  },
@@ -0,0 +1,268 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.scheduleAdd = scheduleAdd;
7
+ exports.scheduleList = scheduleList;
8
+ exports.scheduleRemove = scheduleRemove;
9
+ exports.scheduleRunNow = scheduleRunNow;
10
+ const axios_1 = __importDefault(require("axios"));
11
+ const inquirer_1 = __importDefault(require("inquirer"));
12
+ const node_readline_1 = __importDefault(require("node:readline"));
13
+ const utils_1 = require("./utils");
14
+ async function getJwt() {
15
+ const config = (0, utils_1.readConfig)();
16
+ const port = (0, utils_1.getPort)();
17
+ const licenseKey = config.OMNIKEY_LICENSE_KEY || '';
18
+ const baseUrl = `http://localhost:${port}`;
19
+ const res = await axios_1.default.post(`${baseUrl}/api/subscription/activate`, { licenseKey }, {
20
+ timeout: 10000,
21
+ });
22
+ return res.data.token;
23
+ }
24
+ function getBaseUrl() {
25
+ const port = (0, utils_1.getPort)();
26
+ return `http://localhost:${port}`;
27
+ }
28
+ async function authHeaders() {
29
+ try {
30
+ const token = await getJwt();
31
+ return { Authorization: `Bearer ${token}` };
32
+ }
33
+ catch {
34
+ // Self-hosted: try without auth
35
+ return {};
36
+ }
37
+ }
38
+ async function readMultilinePrompt() {
39
+ console.log('\nEnter prompt text below.');
40
+ console.log('Type END on a new line when finished.\n');
41
+ const rl = node_readline_1.default.createInterface({
42
+ input: process.stdin,
43
+ output: process.stdout,
44
+ });
45
+ const lines = [];
46
+ return new Promise((resolve) => {
47
+ const askLine = () => {
48
+ rl.question('', (line) => {
49
+ if (line.trim() === 'END') {
50
+ rl.close();
51
+ resolve(lines.join('\n').trim());
52
+ return;
53
+ }
54
+ lines.push(line);
55
+ askLine();
56
+ });
57
+ };
58
+ askLine();
59
+ });
60
+ }
61
+ async function scheduleAdd() {
62
+ const answers = await inquirer_1.default.prompt([
63
+ {
64
+ type: 'input',
65
+ name: 'label',
66
+ message: 'Job label (e.g. "Daily standup summary"):',
67
+ validate: (v) => v.trim().length > 0 || 'Label is required',
68
+ },
69
+ {
70
+ type: 'list',
71
+ name: 'scheduleType',
72
+ message: 'Schedule type:',
73
+ choices: [
74
+ { name: 'Recurring (cron expression)', value: 'cron' },
75
+ { name: 'One-time (specific date/time)', value: 'once' },
76
+ ],
77
+ },
78
+ ]);
79
+ const promptText = await readMultilinePrompt();
80
+ if (!promptText) {
81
+ console.error('Prompt is required.');
82
+ return;
83
+ }
84
+ let cronExpression;
85
+ let runAt;
86
+ if (answers.scheduleType === 'cron') {
87
+ const presets = [
88
+ { name: 'Every weekday at 9 AM (0 9 * * 1-5)', value: '0 9 * * 1-5' },
89
+ { name: 'Every day at midnight (0 0 * * *)', value: '0 0 * * *' },
90
+ { name: 'Every hour (0 * * * *)', value: '0 * * * *' },
91
+ { name: 'Every Monday at 8 AM (0 8 * * 1)', value: '0 8 * * 1' },
92
+ { name: 'Custom cron expression', value: '__custom__' },
93
+ ];
94
+ const { preset } = await inquirer_1.default.prompt([
95
+ {
96
+ type: 'list',
97
+ name: 'preset',
98
+ message: 'Choose a schedule preset or enter custom:',
99
+ choices: presets,
100
+ },
101
+ ]);
102
+ if (preset === '__custom__') {
103
+ const { customCron } = await inquirer_1.default.prompt([
104
+ {
105
+ type: 'input',
106
+ name: 'customCron',
107
+ message: 'Enter cron expression (5 fields, e.g. "0 9 * * 1-5"):',
108
+ validate: (v) => /^(\S+\s){4}\S+$/.test(v.trim()) || 'Invalid cron (must be 5 space-separated fields)',
109
+ },
110
+ ]);
111
+ cronExpression = customCron.trim();
112
+ }
113
+ else {
114
+ cronExpression = preset;
115
+ }
116
+ }
117
+ else {
118
+ const { dateStr, timeStr } = await inquirer_1.default.prompt([
119
+ {
120
+ type: 'input',
121
+ name: 'dateStr',
122
+ message: 'Date (YYYY-MM-DD):',
123
+ validate: (v) => /^\d{4}-\d{2}-\d{2}$/.test(v.trim()) || 'Use YYYY-MM-DD format',
124
+ },
125
+ {
126
+ type: 'input',
127
+ name: 'timeStr',
128
+ message: 'Time (HH:MM, 24-hour local time):',
129
+ validate: (v) => /^\d{2}:\d{2}$/.test(v.trim()) || 'Use HH:MM format',
130
+ },
131
+ ]);
132
+ const dt = new Date(`${dateStr.trim()}T${timeStr.trim()}:00`);
133
+ if (isNaN(dt.getTime())) {
134
+ console.error('Invalid date/time combination.');
135
+ return;
136
+ }
137
+ if (dt <= new Date()) {
138
+ console.error('Date/time must be in the future.');
139
+ return;
140
+ }
141
+ runAt = dt.toISOString();
142
+ }
143
+ try {
144
+ const headers = await authHeaders();
145
+ const res = await axios_1.default.post(`${getBaseUrl()}/api/scheduled-jobs`, {
146
+ label: answers.label.trim(),
147
+ prompt: promptText,
148
+ cronExpression,
149
+ runAt,
150
+ }, { headers, timeout: 15000 });
151
+ const job = res.data;
152
+ console.log('\nJob created successfully:');
153
+ printJobRow(job);
154
+ if (job.nextRunAt) {
155
+ console.log(` Next run: ${new Date(job.nextRunAt).toLocaleString()}`);
156
+ }
157
+ }
158
+ catch (err) {
159
+ const msg = err.response?.data?.error ?? err.message;
160
+ console.error(`Error creating job: ${msg}`);
161
+ }
162
+ }
163
+ async function scheduleList() {
164
+ try {
165
+ const headers = await authHeaders();
166
+ const res = await axios_1.default.get(`${getBaseUrl()}/api/scheduled-jobs`, {
167
+ headers,
168
+ timeout: 10000,
169
+ });
170
+ const { jobs } = res.data;
171
+ if (jobs.length === 0) {
172
+ console.log('No scheduled jobs found.');
173
+ return;
174
+ }
175
+ console.log('\nScheduled Jobs:');
176
+ console.log('─'.repeat(90));
177
+ console.log(padRight('ID', 28) +
178
+ padRight('Label', 24) +
179
+ padRight('Schedule', 18) +
180
+ padRight('Next Run', 22) +
181
+ 'Status');
182
+ console.log('─'.repeat(90));
183
+ for (const job of jobs) {
184
+ const schedule = job.cronExpression ?? (job.runAt ? `Once: ${new Date(job.runAt).toLocaleString()}` : '—');
185
+ const nextRun = job.nextRunAt ? new Date(job.nextRunAt).toLocaleString() : '—';
186
+ const status = job.isActive ? 'Active' : 'Inactive';
187
+ console.log(padRight(job.id.slice(0, 26), 28) +
188
+ padRight(job.label.slice(0, 22), 24) +
189
+ padRight(schedule.slice(0, 16), 18) +
190
+ padRight(nextRun.slice(0, 20), 22) +
191
+ status);
192
+ }
193
+ console.log('─'.repeat(90));
194
+ }
195
+ catch (err) {
196
+ const msg = err.response?.data?.error ?? err.message;
197
+ console.error(`Error fetching jobs: ${msg}`);
198
+ }
199
+ }
200
+ async function scheduleRemove() {
201
+ try {
202
+ const headers = await authHeaders();
203
+ const res = await axios_1.default.get(`${getBaseUrl()}/api/scheduled-jobs`, {
204
+ headers,
205
+ timeout: 10000,
206
+ });
207
+ const { jobs } = res.data;
208
+ if (jobs.length === 0) {
209
+ console.log('No scheduled jobs to remove.');
210
+ return;
211
+ }
212
+ const { jobId } = await inquirer_1.default.prompt([
213
+ {
214
+ type: 'list',
215
+ name: 'jobId',
216
+ message: 'Select a job to remove:',
217
+ choices: jobs.map((j) => ({
218
+ name: `${j.label} [${j.cronExpression ?? 'one-time'}] ${j.isActive ? '✓' : '✗'}`,
219
+ value: j.id,
220
+ })),
221
+ },
222
+ ]);
223
+ const { confirm } = await inquirer_1.default.prompt([
224
+ {
225
+ type: 'confirm',
226
+ name: 'confirm',
227
+ message: 'Are you sure you want to delete this job?',
228
+ default: false,
229
+ },
230
+ ]);
231
+ if (!confirm) {
232
+ console.log('Aborted.');
233
+ return;
234
+ }
235
+ await axios_1.default.delete(`${getBaseUrl()}/api/scheduled-jobs/${jobId}`, {
236
+ headers,
237
+ timeout: 10000,
238
+ });
239
+ console.log('Job deleted.');
240
+ }
241
+ catch (err) {
242
+ const msg = err.response?.data?.error ?? err.message;
243
+ console.error(`Error removing job: ${msg}`);
244
+ }
245
+ }
246
+ async function scheduleRunNow(id) {
247
+ try {
248
+ const headers = await authHeaders();
249
+ await axios_1.default.post(`${getBaseUrl()}/api/scheduled-jobs/${id}/run-now`, {}, {
250
+ headers,
251
+ timeout: 10000,
252
+ });
253
+ console.log(`Job ${id} triggered.`);
254
+ }
255
+ catch (err) {
256
+ const msg = err.response?.data?.error ?? err.message;
257
+ console.error(`Error triggering job: ${msg}`);
258
+ }
259
+ }
260
+ function printJobRow(job) {
261
+ console.log(` ID: ${job.id}`);
262
+ console.log(` Label: ${job.label}`);
263
+ console.log(` Schedule: ${job.cronExpression ?? (job.runAt ? `Once at ${new Date(job.runAt).toLocaleString()}` : '—')}`);
264
+ console.log(` Active: ${job.isActive}`);
265
+ }
266
+ function padRight(str, width) {
267
+ return str.length >= width ? str.slice(0, width) : str + ' '.repeat(width - str.length);
268
+ }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "registry": "https://registry.npmjs.org/"
6
6
  },
7
- "version": "1.0.27",
7
+ "version": "1.0.29",
8
8
  "description": "CLI for onboarding users to Omnikey AI and configuring OPENAI_API_KEY. Use Yarn for install/build.",
9
9
  "engines": {
10
10
  "node": ">=14.0.0",
@@ -27,25 +27,26 @@
27
27
  "author": "Gurinder Rawala",
28
28
  "license": "MIT",
29
29
  "dependencies": {
30
- "commander": "^11.0.0",
31
- "inquirer": "^9.0.0",
30
+ "@anthropic-ai/sdk": "^0.80.0",
31
+ "@google-cloud/storage": "^7.19.0",
32
+ "@google/genai": "^1.46.0",
32
33
  "axios": "^1.13.5",
34
+ "commander": "^11.0.0",
33
35
  "cors": "^2.8.5",
34
36
  "cuid": "^3.0.0",
35
37
  "dotenv": "^17.2.3",
36
38
  "express": "^4.21.2",
39
+ "inquirer": "^9.0.0",
37
40
  "jsonwebtoken": "^9.0.3",
38
41
  "openai": "^6.16.0",
39
- "@anthropic-ai/sdk": "^0.80.0",
40
- "@google/genai": "^1.46.0",
41
42
  "pg": "^8.18.0",
42
43
  "pg-hstore": "^2.3.4",
44
+ "playwright-core": "^1.50.0",
43
45
  "sequelize": "^6.37.7",
44
46
  "sqlite3": "^5.1.6",
45
47
  "winston": "^3.19.0",
46
48
  "ws": "^8.18.0",
47
- "zod": "^4.3.6",
48
- "playwright-core": "^1.50.0"
49
+ "zod": "^4.3.6"
49
50
  },
50
51
  "devDependencies": {
51
52
  "@types/inquirer": "^9.0.9",