@eventmodelers/cli 0.0.3 → 0.0.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/cli.js +87 -12
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -132,6 +132,67 @@ async function prompt(question) {
|
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
// Reads a pasted block of credentials, which may span one line (minified JSON,
|
|
136
|
+
// or comma-separated values) or several (pretty-printed JSON). Stops as soon as
|
|
137
|
+
// the accumulated text parses, or on a blank line, so a single-line paste + Enter
|
|
138
|
+
// doesn't require a second Enter to finish.
|
|
139
|
+
async function promptPasteBlock() {
|
|
140
|
+
const lines = [];
|
|
141
|
+
while (lines.length < 20) {
|
|
142
|
+
const line = await new Promise((resolve) => getSharedRl().question('', resolve));
|
|
143
|
+
if (line.trim() === '') {
|
|
144
|
+
if (lines.length > 0) break;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
lines.push(line);
|
|
148
|
+
try {
|
|
149
|
+
JSON.parse(lines.join('\n'));
|
|
150
|
+
break;
|
|
151
|
+
} catch {
|
|
152
|
+
// not yet valid JSON — if it's a single CSV-looking line, that's complete too
|
|
153
|
+
if (lines.length === 1 && line.includes(',')) break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return lines.join('\n').trim();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Accepts either a JSON object (as copied from the account page) or a comma-separated
|
|
160
|
+
// line of values, in the same order as requiredFields (organizationId[, boardId], token),
|
|
161
|
+
// with an optional base URL anywhere in the list.
|
|
162
|
+
function parseCredentialsPaste(text, requiredFields) {
|
|
163
|
+
const trimmed = text.trim();
|
|
164
|
+
if (!trimmed) return null;
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const obj = JSON.parse(trimmed);
|
|
168
|
+
if (obj && typeof obj === 'object') {
|
|
169
|
+
const result = {};
|
|
170
|
+
if (obj.organizationId || obj.orgId) result.organizationId = obj.organizationId || obj.orgId;
|
|
171
|
+
if (obj.boardId) result.boardId = obj.boardId;
|
|
172
|
+
if (obj.token) result.token = obj.token;
|
|
173
|
+
if (obj.baseUrl) result.baseUrl = obj.baseUrl;
|
|
174
|
+
if (requiredFields.every((f) => result[f])) return result;
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
// not JSON — fall through to comma-separated parsing
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const values = trimmed.split(/[,\n]/).map((v) => v.trim()).filter(Boolean);
|
|
182
|
+
const result = {};
|
|
183
|
+
const remaining = [];
|
|
184
|
+
for (const v of values) {
|
|
185
|
+
if (/^https?:\/\//i.test(v)) result.baseUrl = v;
|
|
186
|
+
else remaining.push(v);
|
|
187
|
+
}
|
|
188
|
+
if (remaining.length < requiredFields.length) return null;
|
|
189
|
+
requiredFields.forEach((field, i) => {
|
|
190
|
+
result[field] = remaining[i];
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return requiredFields.every((f) => result[f]) ? result : null;
|
|
194
|
+
}
|
|
195
|
+
|
|
135
196
|
// Arrow-key single-select menu. Falls back to a numbered prompt on non-TTY stdin (e.g. piped input, CI).
|
|
136
197
|
async function selectPrompt(question, choices, defaultIndex = 0) {
|
|
137
198
|
if (!process.stdin.isTTY) {
|
|
@@ -386,21 +447,25 @@ async function installStack(stackKey, stackCfg, options = {}) {
|
|
|
386
447
|
console.log('\n ℹ️ --print — skipping credential prompt, missing fields must be set via EVENTMODELERS_* env vars or config.json');
|
|
387
448
|
} else if (stillMissing) {
|
|
388
449
|
const choice = await selectPrompt('How do you want to configure credentials?', [
|
|
389
|
-
{ label: 'Paste
|
|
390
|
-
{ label: 'Enter values
|
|
391
|
-
{ label: '
|
|
450
|
+
{ label: 'Paste values copied from app.eventmodelers.ai/account', value: 'paste' },
|
|
451
|
+
{ label: 'Enter values one by one', value: 'manual' },
|
|
452
|
+
{ label: 'Get instructions for configuring later', value: 'instructions' },
|
|
453
|
+
{ label: 'Skip for now', value: 'skip' },
|
|
392
454
|
], 1);
|
|
393
455
|
|
|
394
456
|
if (choice === 'paste') {
|
|
395
|
-
console.log(
|
|
396
|
-
console.log(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
457
|
+
console.log('\n Copy your credentials from https://app.eventmodelers.ai/account,');
|
|
458
|
+
console.log(' then paste them below and press Enter:\n');
|
|
459
|
+
const pasted = await promptPasteBlock();
|
|
460
|
+
const parsed = parseCredentialsPaste(pasted, requiredFields);
|
|
461
|
+
if (parsed) {
|
|
462
|
+
config = { ...config, ...parsed };
|
|
463
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
464
|
+
console.log(`\n ✓ Saved to ${relative(targetDir, configPath)}`);
|
|
465
|
+
} else {
|
|
466
|
+
console.log(`\n ⚠️ Couldn't make sense of that paste — nothing was saved.`);
|
|
467
|
+
console.log(` Paste it into ${relative(targetDir, configPath)} yourself, or use /connect later.`);
|
|
468
|
+
}
|
|
404
469
|
} else if (choice === 'manual') {
|
|
405
470
|
console.log('\n🔑 Enter your Eventmodelers credentials:\n');
|
|
406
471
|
config.organizationId = await prompt(' Organization ID: ');
|
|
@@ -410,6 +475,16 @@ async function installStack(stackKey, stackCfg, options = {}) {
|
|
|
410
475
|
config.token = await prompt(' Token: ');
|
|
411
476
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
412
477
|
console.log(`\n ✓ Credentials saved to ${relative(targetDir, configPath)}`);
|
|
478
|
+
} else if (choice === 'instructions') {
|
|
479
|
+
console.log(`\n Paste your credentials into one of these locations:\n`);
|
|
480
|
+
console.log(` (a) ${configPath}`);
|
|
481
|
+
console.log(` (b) .eventmodelers/config.json in this directory or any parent directory\n`);
|
|
482
|
+
console.log(` The file should look like:`);
|
|
483
|
+
const sample = stackCfg.needsBoardId
|
|
484
|
+
? ` {\n "token": "...",\n "boardId": "...",\n "organizationId": "...",\n "baseUrl": "https://api.eventmodelers.ai"\n }\n`
|
|
485
|
+
: ` {\n "token": "...",\n "organizationId": "...",\n "baseUrl": "https://api.eventmodelers.ai"\n }\n`;
|
|
486
|
+
console.log(sample);
|
|
487
|
+
console.log(' Then re-run this installer, or just run the agent afterwards.\n');
|
|
413
488
|
} else {
|
|
414
489
|
console.log('\n ℹ️ Skipped — use /connect in Claude Code to add credentials later');
|
|
415
490
|
}
|
package/package.json
CHANGED