@badgie/crm-cli 0.1.0
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/AGENTS.md +91 -0
- package/README.md +136 -0
- package/dist/bin.js +149 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/admin.js +90 -0
- package/dist/commands/admin.js.map +1 -0
- package/dist/commands/clients.js +135 -0
- package/dist/commands/clients.js.map +1 -0
- package/dist/commands/directory.js +175 -0
- package/dist/commands/directory.js.map +1 -0
- package/dist/commands/docs.js +98 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/finance.js +311 -0
- package/dist/commands/finance.js.map +1 -0
- package/dist/commands/intelligence.js +147 -0
- package/dist/commands/intelligence.js.map +1 -0
- package/dist/commands/leads-list.js +25 -0
- package/dist/commands/leads-list.js.map +1 -0
- package/dist/commands/leads.js +224 -0
- package/dist/commands/leads.js.map +1 -0
- package/dist/commands/login.js +11 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.js +6 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/marketing.js +135 -0
- package/dist/commands/marketing.js.map +1 -0
- package/dist/commands/operations.js +232 -0
- package/dist/commands/operations.js.map +1 -0
- package/dist/commands/outbound.js +172 -0
- package/dist/commands/outbound.js.map +1 -0
- package/dist/commands/query.js +189 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/search.js +71 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/tasks.js +239 -0
- package/dist/commands/tasks.js.map +1 -0
- package/dist/commands/top-clients.js +82 -0
- package/dist/commands/top-clients.js.map +1 -0
- package/dist/commands/webhooks.js +82 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/commands/welcome.js +81 -0
- package/dist/commands/welcome.js.map +1 -0
- package/dist/commands/whoami.js +16 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/core/auth.js +107 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/config.js +53 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/env.js +46 -0
- package/dist/core/env.js.map +1 -0
- package/dist/core/format.js +88 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/http.js +42 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/list-query.js +35 -0
- package/dist/core/list-query.js.map +1 -0
- package/dist/core/services/leads.js +41 -0
- package/dist/core/services/leads.js.map +1 -0
- package/dist/core/supabase.js +53 -0
- package/dist/core/supabase.js.map +1 -0
- package/dist/registry.js +2 -0
- package/dist/registry.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import readline from 'node:readline';
|
|
2
|
+
import { createClient } from '@supabase/supabase-js';
|
|
3
|
+
import { readConfig, writeConfig } from './config.js';
|
|
4
|
+
import { resolveEnv } from './env.js';
|
|
5
|
+
/**
|
|
6
|
+
* Interactive login via email + password (matches the apps auth method).
|
|
7
|
+
* Google OAuth is not enabled on the Supabase project, so we mirror the
|
|
8
|
+
* Next.js login at app/(auth)/login/login-button.tsx.
|
|
9
|
+
*/
|
|
10
|
+
export async function runLogin() {
|
|
11
|
+
const env = await resolveEnv();
|
|
12
|
+
const email = await prompt('Email: ');
|
|
13
|
+
const password = await promptHidden('Password: ');
|
|
14
|
+
const supabase = createClient(env.supabaseUrl, env.supabaseAnonKey, {
|
|
15
|
+
auth: { persistSession: false, autoRefreshToken: false },
|
|
16
|
+
});
|
|
17
|
+
const { data, error } = await supabase.auth.signInWithPassword({
|
|
18
|
+
email: email.trim(),
|
|
19
|
+
password,
|
|
20
|
+
});
|
|
21
|
+
if (error || !data.session || !data.user) {
|
|
22
|
+
throw new Error(`Sign-in failed: ${error?.message ?? 'no session returned'}`);
|
|
23
|
+
}
|
|
24
|
+
const userEmail = data.user.email ?? email.trim();
|
|
25
|
+
// Domain / team_members gate (same rules as the app)
|
|
26
|
+
if (!userEmail.endsWith('@badgie.com')) {
|
|
27
|
+
const { data: member } = await supabase
|
|
28
|
+
.from('team_members')
|
|
29
|
+
.select('email, is_active')
|
|
30
|
+
.eq('email', userEmail)
|
|
31
|
+
.maybeSingle();
|
|
32
|
+
if (!member) {
|
|
33
|
+
await supabase.auth.signOut();
|
|
34
|
+
throw new Error('No tienes acceso a esta aplicación.');
|
|
35
|
+
}
|
|
36
|
+
if (!member.is_active) {
|
|
37
|
+
await supabase.auth.signOut();
|
|
38
|
+
throw new Error('Tu cuenta está desactivada.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const existing = (await readConfig()) ?? {
|
|
42
|
+
supabaseUrl: env.supabaseUrl,
|
|
43
|
+
supabaseAnonKey: env.supabaseAnonKey,
|
|
44
|
+
};
|
|
45
|
+
existing.supabaseUrl = env.supabaseUrl;
|
|
46
|
+
existing.supabaseAnonKey = env.supabaseAnonKey;
|
|
47
|
+
existing.session = {
|
|
48
|
+
access_token: data.session.access_token,
|
|
49
|
+
refresh_token: data.session.refresh_token,
|
|
50
|
+
expires_at: data.session.expires_at ?? Math.floor(Date.now() / 1000) + 3600,
|
|
51
|
+
user_email: userEmail,
|
|
52
|
+
};
|
|
53
|
+
await writeConfig(existing);
|
|
54
|
+
return { email: userEmail };
|
|
55
|
+
}
|
|
56
|
+
function prompt(question) {
|
|
57
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
rl.question(question, (answer) => {
|
|
60
|
+
rl.close();
|
|
61
|
+
resolve(answer);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function promptHidden(question) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
process.stdout.write(question);
|
|
68
|
+
const stdin = process.stdin;
|
|
69
|
+
const wasRaw = stdin.isTTY ? stdin.isRaw : false;
|
|
70
|
+
if (stdin.isTTY)
|
|
71
|
+
stdin.setRawMode(true);
|
|
72
|
+
stdin.resume();
|
|
73
|
+
stdin.setEncoding('utf8');
|
|
74
|
+
let buf = '';
|
|
75
|
+
const onData = (chunk) => {
|
|
76
|
+
for (const ch of chunk) {
|
|
77
|
+
switch (ch) {
|
|
78
|
+
case '\n':
|
|
79
|
+
case '\r':
|
|
80
|
+
case '\u0004':
|
|
81
|
+
cleanup();
|
|
82
|
+
process.stdout.write('\n');
|
|
83
|
+
return resolve(buf);
|
|
84
|
+
case '\u0003':
|
|
85
|
+
cleanup();
|
|
86
|
+
process.stdout.write('\n');
|
|
87
|
+
return reject(new Error('Cancelled'));
|
|
88
|
+
case '\u007f':
|
|
89
|
+
case '\b':
|
|
90
|
+
if (buf.length > 0)
|
|
91
|
+
buf = buf.slice(0, -1);
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
buf += ch;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const cleanup = () => {
|
|
99
|
+
stdin.removeListener('data', onData);
|
|
100
|
+
if (stdin.isTTY)
|
|
101
|
+
stdin.setRawMode(wasRaw);
|
|
102
|
+
stdin.pause();
|
|
103
|
+
};
|
|
104
|
+
stdin.on('data', onData);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile, rm, stat } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
const CONFIG_DIR = join(homedir(), '.badgie-crm');
|
|
5
|
+
const CONFIG_PATH = join(CONFIG_DIR, 'config.json');
|
|
6
|
+
const LEGACY_CONFIG_PATH = join(homedir(), '.badgie', 'config.json');
|
|
7
|
+
async function migrateLegacyConfig() {
|
|
8
|
+
try {
|
|
9
|
+
await stat(CONFIG_PATH);
|
|
10
|
+
return; // new path already exists, nothing to do
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// continue
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const raw = await readFile(LEGACY_CONFIG_PATH, 'utf8');
|
|
17
|
+
await mkdir(dirname(CONFIG_PATH), { recursive: true, mode: 0o700 });
|
|
18
|
+
await writeFile(CONFIG_PATH, raw, { mode: 0o600 });
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// no legacy file, nothing to migrate
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function readConfig() {
|
|
25
|
+
await migrateLegacyConfig();
|
|
26
|
+
try {
|
|
27
|
+
const raw = await readFile(CONFIG_PATH, 'utf8');
|
|
28
|
+
return JSON.parse(raw);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
if (err.code === 'ENOENT')
|
|
32
|
+
return null;
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function writeConfig(cfg) {
|
|
37
|
+
await mkdir(dirname(CONFIG_PATH), { recursive: true, mode: 0o700 });
|
|
38
|
+
await writeFile(CONFIG_PATH, JSON.stringify(cfg, null, 2), { mode: 0o600 });
|
|
39
|
+
}
|
|
40
|
+
export async function clearSession() {
|
|
41
|
+
const cfg = await readConfig();
|
|
42
|
+
if (!cfg)
|
|
43
|
+
return;
|
|
44
|
+
delete cfg.session;
|
|
45
|
+
await writeConfig(cfg);
|
|
46
|
+
}
|
|
47
|
+
export async function deleteConfig() {
|
|
48
|
+
await rm(CONFIG_PATH, { force: true });
|
|
49
|
+
}
|
|
50
|
+
export function configPath() {
|
|
51
|
+
return CONFIG_PATH;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAazC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAA;AACjD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAA;AACnD,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAA;AAEpE,KAAK,UAAU,mBAAmB;IAChC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,WAAW,CAAC,CAAA;QACvB,OAAM,CAAC,yCAAyC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,WAAW;IACb,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAA;QACtD,MAAM,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACnE,MAAM,SAAS,CAAC,WAAW,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,qCAAqC;IACvC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,mBAAmB,EAAE,CAAA;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAA;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QACjE,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAc;IAC9C,MAAM,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACnE,MAAM,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;AAC7E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAA;IAC9B,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,OAAO,GAAG,CAAC,OAAO,CAAA;IAClB,MAAM,WAAW,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AACxC,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,WAAW,CAAA;AACpB,CAAC"}
|
package/dist/core/env.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { config as loadEnv } from 'dotenv';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { readConfig } from './config.js';
|
|
5
|
+
let loaded = false;
|
|
6
|
+
function loadRepoEnv() {
|
|
7
|
+
if (loaded)
|
|
8
|
+
return;
|
|
9
|
+
loaded = true;
|
|
10
|
+
// Load .env.local from the repo root (parent of cli/) so devs dont duplicate config
|
|
11
|
+
const candidates = [
|
|
12
|
+
join(process.cwd(), '.env.local'),
|
|
13
|
+
join(process.cwd(), '..', '.env.local'),
|
|
14
|
+
join(process.cwd(), '.env'),
|
|
15
|
+
];
|
|
16
|
+
for (const p of candidates) {
|
|
17
|
+
if (existsSync(p)) {
|
|
18
|
+
loadEnv({ path: p });
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function resolveEnv() {
|
|
24
|
+
loadRepoEnv();
|
|
25
|
+
const cfg = await readConfig();
|
|
26
|
+
const supabaseUrl = process.env.BADGIE_CRM_SUPABASE_URL ||
|
|
27
|
+
process.env.BADGIE_SUPABASE_URL ||
|
|
28
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL ||
|
|
29
|
+
cfg?.supabaseUrl;
|
|
30
|
+
const supabaseAnonKey = process.env.BADGIE_CRM_SUPABASE_ANON_KEY ||
|
|
31
|
+
process.env.BADGIE_SUPABASE_ANON_KEY ||
|
|
32
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ||
|
|
33
|
+
cfg?.supabaseAnonKey;
|
|
34
|
+
if (!supabaseUrl || !supabaseAnonKey) {
|
|
35
|
+
throw new Error('Supabase URL/anon key not found. Set NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY in .env.local or run `badgie-crm login` from the repo root.');
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
supabaseUrl,
|
|
39
|
+
supabaseAnonKey,
|
|
40
|
+
serviceKey: process.env.BADGIE_CRM_SERVICE_KEY ||
|
|
41
|
+
process.env.BADGIE_SERVICE_KEY ||
|
|
42
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY,
|
|
43
|
+
appUrl: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/core/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,QAAQ,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,IAAI,MAAM,GAAG,KAAK,CAAA;AAElB,SAAS,WAAW;IAClB,IAAI,MAAM;QAAE,OAAM;IAClB,MAAM,GAAG,IAAI,CAAA;IACb,oFAAoF;IACpF,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC;QACvC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC;KAC5B,CAAA;IACD,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YAClB,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;YACpB,MAAK;QACP,CAAC;IACH,CAAC;AACH,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,WAAW,EAAE,CAAA;IACb,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAA;IAC9B,MAAM,WAAW,GACf,OAAO,CAAC,GAAG,CAAC,uBAAuB;QACnC,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,OAAO,CAAC,GAAG,CAAC,wBAAwB;QACpC,GAAG,EAAE,WAAW,CAAA;IAClB,MAAM,eAAe,GACnB,OAAO,CAAC,GAAG,CAAC,4BAA4B;QACxC,OAAO,CAAC,GAAG,CAAC,wBAAwB;QACpC,OAAO,CAAC,GAAG,CAAC,6BAA6B;QACzC,GAAG,EAAE,eAAe,CAAA;IACtB,IAAI,CAAC,WAAW,IAAI,CAAC,eAAe,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,6JAA6J,CAC9J,CAAA;IACH,CAAC;IACD,OAAO;QACL,WAAW;QACX,eAAe;QACf,UAAU,EACR,OAAO,CAAC,GAAG,CAAC,sBAAsB;YAClC,OAAO,CAAC,GAAG,CAAC,kBAAkB;YAC9B,OAAO,CAAC,GAAG,CAAC,yBAAyB;QACvC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,uBAAuB;KACnE,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const C = {
|
|
2
|
+
reset: '\x1b[0m',
|
|
3
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
4
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
5
|
+
yellow: (s) => `\x1b[38;2;233;200;22m${s}\x1b[0m`,
|
|
6
|
+
blue: (s) => `\x1b[38;2;6;86;191m${s}\x1b[0m`,
|
|
7
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
8
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
9
|
+
grey: (s) => `\x1b[38;2;115;115;115m${s}\x1b[0m`,
|
|
10
|
+
};
|
|
11
|
+
export function output(data, opts = {}) {
|
|
12
|
+
if (!opts.pretty) {
|
|
13
|
+
console.log(JSON.stringify(data, null, 2));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(data)) {
|
|
17
|
+
if (data.length === 0) {
|
|
18
|
+
console.log(C.grey('(no rows)'));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
printTable(data, opts.columns);
|
|
22
|
+
console.log(C.grey(`\n${data.length} row${data.length === 1 ? '' : 's'}`));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (data && typeof data === 'object') {
|
|
26
|
+
for (const [k, v] of Object.entries(data)) {
|
|
27
|
+
console.log(`${C.bold(k.padEnd(22))} ${formatCell(v)}`);
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
console.log(String(data));
|
|
32
|
+
}
|
|
33
|
+
function formatCell(v, maxLen = 60) {
|
|
34
|
+
if (v === null || v === undefined)
|
|
35
|
+
return C.grey('—');
|
|
36
|
+
if (typeof v === 'object') {
|
|
37
|
+
const j = JSON.stringify(v);
|
|
38
|
+
return j.length > maxLen ? j.slice(0, maxLen - 1) + '…' : j;
|
|
39
|
+
}
|
|
40
|
+
const s = String(v);
|
|
41
|
+
return s.length > maxLen ? s.slice(0, maxLen - 1) + '…' : s;
|
|
42
|
+
}
|
|
43
|
+
function printTable(rows, columns) {
|
|
44
|
+
const cols = columns && columns.length > 0 ? columns : pickColumns(rows[0]);
|
|
45
|
+
const widths = cols.map((c) => Math.min(40, Math.max(c.length, ...rows.map((r) => formatCell(r[c], 40).replace(/\x1b\[[0-9;]*m/g, '').length))));
|
|
46
|
+
console.log(cols.map((c, i) => C.bold(c.padEnd(widths[i]))).join(' '));
|
|
47
|
+
console.log(widths.map((w) => C.grey('─'.repeat(w))).join(' '));
|
|
48
|
+
for (const r of rows) {
|
|
49
|
+
console.log(cols
|
|
50
|
+
.map((c, i) => padVisible(formatCell(r[c], 40), widths[i]))
|
|
51
|
+
.join(' '));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function padVisible(s, width) {
|
|
55
|
+
const visible = s.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
56
|
+
return s + ' '.repeat(Math.max(0, width - visible));
|
|
57
|
+
}
|
|
58
|
+
function pickColumns(row) {
|
|
59
|
+
if (!row)
|
|
60
|
+
return [];
|
|
61
|
+
const preferred = ['id', 'name', 'title', 'email', 'phone', 'status', 'amount', 'created_at'];
|
|
62
|
+
const present = preferred.filter((c) => c in row);
|
|
63
|
+
const rest = Object.keys(row).filter((k) => !present.includes(k));
|
|
64
|
+
return [...present, ...rest].slice(0, 6);
|
|
65
|
+
}
|
|
66
|
+
export function commonListOptions() {
|
|
67
|
+
return [
|
|
68
|
+
{ flag: '--limit <n>', description: 'max rows (default 50)' },
|
|
69
|
+
{ flag: '--pretty', description: 'human-readable table output' },
|
|
70
|
+
{ flag: '--json', description: 'force JSON output (default)' },
|
|
71
|
+
{ flag: '--columns <list>', description: 'comma-separated columns for --pretty' },
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
export function parseLimit(opts, def = 50) {
|
|
75
|
+
const l = opts.limit;
|
|
76
|
+
if (typeof l === 'string')
|
|
77
|
+
return parseInt(l, 10) || def;
|
|
78
|
+
if (typeof l === 'number')
|
|
79
|
+
return l;
|
|
80
|
+
return def;
|
|
81
|
+
}
|
|
82
|
+
export function parseColumns(opts) {
|
|
83
|
+
const c = opts.columns;
|
|
84
|
+
if (typeof c === 'string' && c.length > 0)
|
|
85
|
+
return c.split(',').map((s) => s.trim());
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/core/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { resolveEnv } from './env.js';
|
|
2
|
+
import { getAuthedClient } from './supabase.js';
|
|
3
|
+
/**
|
|
4
|
+
* Call an API route exposed by the Next app. Sends the Supabase access token as
|
|
5
|
+
* Authorization header so the route can (optionally) authenticate the caller.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: most Next routes today read the Supabase session from cookies; they do
|
|
8
|
+
* NOT check Authorization. Commands that wrap those endpoints will work only
|
|
9
|
+
* if the route is reachable (dev server up) AND either (a) doesnt require
|
|
10
|
+
* auth, or (b) has been extended to accept a bearer token. We still forward
|
|
11
|
+
* the token so it just works once the app is updated.
|
|
12
|
+
*/
|
|
13
|
+
export async function callAppApi(path, init = {}) {
|
|
14
|
+
const env = await resolveEnv();
|
|
15
|
+
const { client, mode } = await getAuthedClient();
|
|
16
|
+
const headers = new Headers(init.headers);
|
|
17
|
+
headers.set('content-type', headers.get('content-type') ?? 'application/json');
|
|
18
|
+
if (mode === 'user') {
|
|
19
|
+
const { data } = await client.auth.getSession();
|
|
20
|
+
const token = data.session?.access_token;
|
|
21
|
+
if (token)
|
|
22
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
23
|
+
}
|
|
24
|
+
else if (mode === 'service') {
|
|
25
|
+
const env2 = await resolveEnv();
|
|
26
|
+
if (env2.serviceKey)
|
|
27
|
+
headers.set('x-badgie-service-key', env2.serviceKey);
|
|
28
|
+
}
|
|
29
|
+
const url = new URL(path, env.appUrl).toString();
|
|
30
|
+
const res = await fetch(url, { ...init, headers });
|
|
31
|
+
const text = await res.text();
|
|
32
|
+
if (!res.ok) {
|
|
33
|
+
throw new Error(`${res.status} ${res.statusText} — ${text.slice(0, 300)}`);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(text);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return text;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAY,EACZ,OAAoB,EAAE;IAEtB,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAA;IAC9B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,eAAe,EAAE,CAAA;IAChD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACzC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,kBAAkB,CAAC,CAAA;IAC9E,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAA;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAA;QACxC,IAAI,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAA;IAC5D,CAAC;SAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,UAAU,EAAE,CAAA;QAC/B,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAC3E,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAA;IAChD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IAClD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;IAC5E,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAoB,CAAA;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getAuthedClient } from './supabase.js';
|
|
2
|
+
import { output, parseColumns, parseLimit } from './format.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shared executor for "list" commands — handles limit, columns, and pretty/json output.
|
|
5
|
+
*/
|
|
6
|
+
export async function runListCommand(cfg, opts) {
|
|
7
|
+
const { client } = await getAuthedClient();
|
|
8
|
+
const limit = parseLimit(opts, 50);
|
|
9
|
+
let query = client.from(cfg.table).select(cfg.select ?? '*').limit(limit);
|
|
10
|
+
if (cfg.defaultOrder) {
|
|
11
|
+
query = query.order(cfg.defaultOrder.column, {
|
|
12
|
+
ascending: cfg.defaultOrder.ascending ?? false,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
for (const f of cfg.filters ?? []) {
|
|
16
|
+
const v = opts[f.option];
|
|
17
|
+
if (typeof v === 'string' && v.length > 0) {
|
|
18
|
+
query = f.apply(query, v);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const { data, error } = await query;
|
|
22
|
+
if (error)
|
|
23
|
+
throw error;
|
|
24
|
+
output(data ?? [], { pretty: !!opts.pretty, columns: parseColumns(opts) });
|
|
25
|
+
}
|
|
26
|
+
export function eqFilter(column) {
|
|
27
|
+
return (q, v) => q.eq(column, v);
|
|
28
|
+
}
|
|
29
|
+
export function ilikeFilter(column) {
|
|
30
|
+
return (q, v) => q.ilike(column, `%${v}%`);
|
|
31
|
+
}
|
|
32
|
+
export function boolFilter(column) {
|
|
33
|
+
return (q, v) => q.eq(column, v === 'true' || v === '1');
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=list-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-query.js","sourceRoot":"","sources":["../../src/core/list-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAa9D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAoB,EACpB,IAA6B;IAE7B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,eAAe,EAAE,CAAA;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IAClC,IAAI,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACzE,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;QACrB,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE;YAC3C,SAAS,EAAE,GAAG,CAAC,YAAY,CAAC,SAAS,IAAI,KAAK;SAC/C,CAAC,CAAA;IACJ,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QACxB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,KAAK,CAAA;IACnC,IAAI,KAAK;QAAE,MAAM,KAAK,CAAA;IACtB,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC5E,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,OAAO,CAAC,CAAM,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,CAAC,CAAM,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;AACzD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,CAAC,CAAM,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;AACvE,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { getAuthedClient } from '../supabase.js';
|
|
2
|
+
export async function listLeads(opts = {}) {
|
|
3
|
+
const { client } = await getAuthedClient();
|
|
4
|
+
let query = client
|
|
5
|
+
.from('leads')
|
|
6
|
+
.select(`
|
|
7
|
+
id,
|
|
8
|
+
title,
|
|
9
|
+
assigned_to,
|
|
10
|
+
created_at,
|
|
11
|
+
status:lead_statuses(id, name),
|
|
12
|
+
assigned:team_members!leads_assigned_to_fkey(full_name, email),
|
|
13
|
+
client:clients!leads_client_id_fkey(id, name, sport)
|
|
14
|
+
`)
|
|
15
|
+
.order('created_at', { ascending: false })
|
|
16
|
+
.limit(opts.limit ?? 50);
|
|
17
|
+
if (opts.status) {
|
|
18
|
+
// Allow filtering by status name (case-insensitive) via an inner join is tricky;
|
|
19
|
+
// easier path: resolve status id first
|
|
20
|
+
const { data: statuses, error: statusErr } = await client
|
|
21
|
+
.from('lead_statuses')
|
|
22
|
+
.select('id, name')
|
|
23
|
+
.ilike('name', opts.status)
|
|
24
|
+
.limit(1);
|
|
25
|
+
if (statusErr)
|
|
26
|
+
throw statusErr;
|
|
27
|
+
const statusId = statuses?.[0]?.id;
|
|
28
|
+
if (!statusId) {
|
|
29
|
+
throw new Error(`No lead_status matches "${opts.status}"`);
|
|
30
|
+
}
|
|
31
|
+
query = query.eq('status_id', statusId);
|
|
32
|
+
}
|
|
33
|
+
if (opts.assignedTo) {
|
|
34
|
+
query = query.eq('assigned_to', opts.assignedTo);
|
|
35
|
+
}
|
|
36
|
+
const { data, error } = await query;
|
|
37
|
+
if (error)
|
|
38
|
+
throw error;
|
|
39
|
+
return (data ?? []);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=leads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leads.js","sourceRoot":"","sources":["../../../src/core/services/leads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAkBhD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAyB,EAAE;IACzD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,eAAe,EAAE,CAAA;IAE1C,IAAI,KAAK,GAAG,MAAM;SACf,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CACL;;;;;;;;KAQD,CACA;SACA,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;SACzC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;IAE1B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,iFAAiF;QACjF,uCAAuC;QACvC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM;aACtD,IAAI,CAAC,eAAe,CAAC;aACrB,MAAM,CAAC,UAAU,CAAC;aAClB,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;aAC1B,KAAK,CAAC,CAAC,CAAC,CAAA;QACX,IAAI,SAAS;YAAE,MAAM,SAAS,CAAA;QAC9B,MAAM,QAAQ,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;QAClC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;QAC5D,CAAC;QACD,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;IACzC,CAAC;IAED,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAClD,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,KAAK,CAAA;IACnC,IAAI,KAAK;QAAE,MAAM,KAAK,CAAA;IACtB,OAAO,CAAC,IAAI,IAAI,EAAE,CAAyB,CAAA;AAC7C,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createClient } from '@supabase/supabase-js';
|
|
2
|
+
import { readConfig, writeConfig } from './config.js';
|
|
3
|
+
import { resolveEnv } from './env.js';
|
|
4
|
+
/**
|
|
5
|
+
* Build a Supabase client using the best available credentials.
|
|
6
|
+
* Precedence:
|
|
7
|
+
* 1. BADGIE_SERVICE_KEY / SUPABASE_SERVICE_ROLE_KEY (bypasses RLS — for CI/automation)
|
|
8
|
+
* 2. Persisted user session from `badgie-crm login` (respects RLS)
|
|
9
|
+
* Throws if neither is available.
|
|
10
|
+
*/
|
|
11
|
+
export async function getAuthedClient() {
|
|
12
|
+
const env = await resolveEnv();
|
|
13
|
+
if (env.serviceKey) {
|
|
14
|
+
const client = createClient(env.supabaseUrl, env.serviceKey, {
|
|
15
|
+
auth: { autoRefreshToken: false, persistSession: false },
|
|
16
|
+
});
|
|
17
|
+
return { client, mode: 'service' };
|
|
18
|
+
}
|
|
19
|
+
const cfg = await readConfig();
|
|
20
|
+
if (!cfg?.session) {
|
|
21
|
+
throw new Error('Not authenticated. Run `badgie-crm login` or set BADGIE_SERVICE_KEY.');
|
|
22
|
+
}
|
|
23
|
+
const client = createClient(env.supabaseUrl, env.supabaseAnonKey, {
|
|
24
|
+
auth: { persistSession: false, autoRefreshToken: false },
|
|
25
|
+
});
|
|
26
|
+
// Refresh if expired (5min buffer)
|
|
27
|
+
const now = Math.floor(Date.now() / 1000);
|
|
28
|
+
if (cfg.session.expires_at - 300 <= now) {
|
|
29
|
+
const { data, error } = await client.auth.refreshSession({
|
|
30
|
+
refresh_token: cfg.session.refresh_token,
|
|
31
|
+
});
|
|
32
|
+
if (error || !data.session) {
|
|
33
|
+
throw new Error(`Session expired and refresh failed (${error?.message ?? 'unknown'}). Run \`badgie-crm login\` again.`);
|
|
34
|
+
}
|
|
35
|
+
cfg.session = {
|
|
36
|
+
access_token: data.session.access_token,
|
|
37
|
+
refresh_token: data.session.refresh_token,
|
|
38
|
+
expires_at: data.session.expires_at ?? now + 3600,
|
|
39
|
+
user_email: data.user?.email ?? cfg.session.user_email,
|
|
40
|
+
};
|
|
41
|
+
await writeConfig(cfg);
|
|
42
|
+
}
|
|
43
|
+
await client.auth.setSession({
|
|
44
|
+
access_token: cfg.session.access_token,
|
|
45
|
+
refresh_token: cfg.session.refresh_token,
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
client,
|
|
49
|
+
mode: 'user',
|
|
50
|
+
userEmail: cfg.session.user_email,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=supabase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../src/core/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,uBAAuB,CAAA;AACzE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAUrC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAA;IAE9B,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE;YAC3D,IAAI,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE;SACzD,CAAC,CAAA;QACF,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;IACpC,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAA;IAC9B,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,eAAe,EAAE;QAChE,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE;KACzD,CAAC,CAAA;IAEF,mCAAmC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IACzC,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QACxC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC;YACvD,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,aAAa;SACzC,CAAC,CAAA;QACF,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,uCAAuC,KAAK,EAAE,OAAO,IAAI,SAAS,oCAAoC,CACvG,CAAA;QACH,CAAC;QACD,GAAG,CAAC,OAAO,GAAG;YACZ,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;YACvC,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa;YACzC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,GAAG,GAAG,IAAI;YACjD,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU;SACvD,CAAA;QACD,MAAM,WAAW,CAAC,GAAG,CAAC,CAAA;IACxB,CAAC;IAED,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QAC3B,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY;QACtC,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,aAAa;KACzC,CAAC,CAAA;IAEF,OAAO;QACL,MAAM;QACN,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU;KAClC,CAAA;AACH,CAAC"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@badgie/crm-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Badgie CRM CLI — terminal operations for the Badgie CRM (leads, clients, invoices, webhooks) plus MCP server for AI agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"badgie-crm": "dist/bin.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"AGENTS.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.json && chmod +x dist/bin.js",
|
|
16
|
+
"dev": "tsx src/bin.ts",
|
|
17
|
+
"start": "node dist/bin.js",
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
20
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@supabase/supabase-js": "^2.81.1",
|
|
24
|
+
"commander": "^12.1.0",
|
|
25
|
+
"dotenv": "^17.3.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20",
|
|
29
|
+
"tsx": "^4.19.2",
|
|
30
|
+
"typescript": "^5"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/gregmontalvo/badgie_crm.git",
|
|
38
|
+
"directory": "cli"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/gregmontalvo/badgie_crm/tree/main/cli#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/gregmontalvo/badgie_crm/issues"
|
|
43
|
+
},
|
|
44
|
+
"license": "UNLICENSED",
|
|
45
|
+
"author": "Badgie <hello@badgie.com>",
|
|
46
|
+
"keywords": [
|
|
47
|
+
"badgie",
|
|
48
|
+
"crm",
|
|
49
|
+
"cli",
|
|
50
|
+
"supabase",
|
|
51
|
+
"mcp",
|
|
52
|
+
"openclaw"
|
|
53
|
+
],
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|