hissuno 0.2.2 → 0.2.3
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/README.md +39 -28
- package/dist/index.js +290 -207
- package/package.json +1 -1
- package/skills/SKILL.md +6 -6
- package/skills/references/CLI-REFERENCE.md +25 -12
- package/skills/references/INTEGRATIONS.md +33 -15
package/README.md
CHANGED
|
@@ -302,64 +302,73 @@ hissuno --json add issues
|
|
|
302
302
|
|
|
303
303
|
**feedback** - one or more conversation messages (role + content), name (optional), tags (optional)
|
|
304
304
|
|
|
305
|
-
### `hissuno
|
|
305
|
+
### `hissuno integrations`
|
|
306
306
|
|
|
307
|
-
Manage third-party integrations. Supported platforms: `intercom`, `gong`, `zendesk`, `slack`, `github`, `jira`, `linear`.
|
|
307
|
+
Manage third-party integrations. Supported platforms: `intercom`, `gong`, `zendesk`, `slack`, `github`, `jira`, `linear`, `fathom`, `hubspot`, `notion`.
|
|
308
308
|
|
|
309
309
|
```bash
|
|
310
310
|
# List all integrations with their connection status
|
|
311
|
-
hissuno
|
|
311
|
+
hissuno integrations list
|
|
312
312
|
|
|
313
313
|
# Interactive wizard for a specific platform
|
|
314
|
-
hissuno
|
|
314
|
+
hissuno integrations intercom
|
|
315
315
|
|
|
316
316
|
# Check detailed status
|
|
317
|
-
hissuno
|
|
317
|
+
hissuno integrations status gong
|
|
318
318
|
|
|
319
319
|
# Connect a platform
|
|
320
|
-
hissuno
|
|
320
|
+
hissuno integrations add slack
|
|
321
321
|
|
|
322
322
|
# Connect Gong with inline credentials (non-interactive)
|
|
323
|
-
hissuno
|
|
323
|
+
hissuno integrations add gong \
|
|
324
324
|
--access-key YOUR_KEY \
|
|
325
325
|
--access-key-secret YOUR_SECRET \
|
|
326
326
|
--sync-frequency 24h
|
|
327
327
|
|
|
328
328
|
# Connect Zendesk
|
|
329
|
-
hissuno
|
|
329
|
+
hissuno integrations add zendesk \
|
|
330
330
|
--subdomain mycompany \
|
|
331
331
|
--email admin@mycompany.com \
|
|
332
332
|
--api-token YOUR_TOKEN
|
|
333
333
|
|
|
334
334
|
# Connect Intercom with an API token
|
|
335
|
-
hissuno
|
|
335
|
+
hissuno integrations add intercom --access-token YOUR_TOKEN
|
|
336
|
+
|
|
337
|
+
# Connect Fathom with an API key
|
|
338
|
+
hissuno integrations add fathom --api-key YOUR_KEY --sync-frequency 24h
|
|
339
|
+
|
|
340
|
+
# Connect HubSpot with a private app token
|
|
341
|
+
hissuno integrations add hubspot --access-token YOUR_TOKEN --overwrite-policy fill_nulls
|
|
342
|
+
|
|
343
|
+
# Connect Notion with an internal integration token
|
|
344
|
+
hissuno integrations add notion --access-token YOUR_TOKEN
|
|
336
345
|
|
|
337
346
|
# Update sync settings
|
|
338
|
-
hissuno
|
|
347
|
+
hissuno integrations configure intercom
|
|
339
348
|
|
|
340
349
|
# Trigger a manual sync
|
|
341
|
-
hissuno
|
|
342
|
-
hissuno
|
|
350
|
+
hissuno integrations sync gong
|
|
351
|
+
hissuno integrations sync zendesk --mode full
|
|
343
352
|
|
|
344
353
|
# Disconnect an integration
|
|
345
|
-
hissuno
|
|
354
|
+
hissuno integrations disconnect github
|
|
346
355
|
|
|
347
356
|
# JSON output
|
|
348
|
-
hissuno --json
|
|
357
|
+
hissuno --json integrations list
|
|
349
358
|
```
|
|
350
359
|
|
|
351
|
-
####
|
|
360
|
+
#### Subcommands
|
|
352
361
|
|
|
353
|
-
|
|
|
354
|
-
|
|
355
|
-
|
|
|
356
|
-
| `
|
|
357
|
-
| `
|
|
358
|
-
| `configure` | Update settings (sync frequency, auto-sync toggle) |
|
|
359
|
-
| `sync` | Trigger a manual sync (Intercom, Gong, Zendesk
|
|
360
|
-
| `disconnect` | Disconnect the integration |
|
|
362
|
+
| Subcommand | Args | Description |
|
|
363
|
+
|------------|------|-------------|
|
|
364
|
+
| `list` | | List all integrations with connection status |
|
|
365
|
+
| `add` | `<platform>` | Connect a platform (OAuth or token-based) |
|
|
366
|
+
| `status` | `<platform>` | Show detailed connection status |
|
|
367
|
+
| `configure` | `<platform>` | Update settings (sync frequency, auto-sync toggle) |
|
|
368
|
+
| `sync` | `<platform>` | Trigger a manual sync (Intercom, Gong, Zendesk, Fathom, HubSpot) |
|
|
369
|
+
| `disconnect` | `<platform>` | Disconnect the integration |
|
|
361
370
|
|
|
362
|
-
#### Connection options
|
|
371
|
+
#### Connection options (for `add`)
|
|
363
372
|
|
|
364
373
|
| Option | Platform | Description |
|
|
365
374
|
|--------|----------|-------------|
|
|
@@ -369,11 +378,13 @@ hissuno --json integrate
|
|
|
369
378
|
| `--subdomain <subdomain>` | Zendesk | Zendesk subdomain |
|
|
370
379
|
| `--email <email>` | Zendesk | Admin email |
|
|
371
380
|
| `--api-token <token>` | Zendesk | API token |
|
|
372
|
-
| `--access-token <token>` | Intercom | Access token |
|
|
373
|
-
| `--
|
|
374
|
-
| `--
|
|
381
|
+
| `--access-token <token>` | Intercom, HubSpot, Notion | Access token |
|
|
382
|
+
| `--api-key <key>` | Fathom | API key |
|
|
383
|
+
| `--overwrite-policy <policy>` | HubSpot | `fill_nulls`, `hubspot_wins`, `never_overwrite` |
|
|
384
|
+
| `--sync-frequency <freq>` | Gong, Zendesk, Intercom, Fathom, HubSpot | Sync frequency: `manual`, `1h`, `6h`, `24h` |
|
|
385
|
+
| `--mode <mode>` | `sync` subcommand | Sync mode: `incremental` (default), `full` |
|
|
375
386
|
|
|
376
|
-
OAuth-based platforms (Slack, GitHub, Jira, Linear) open your browser to complete authorization.
|
|
387
|
+
OAuth-based platforms (Slack, GitHub, Jira, Linear) open your browser to complete authorization. HubSpot, Intercom, and Notion support both OAuth and token-based authentication.
|
|
377
388
|
|
|
378
389
|
### `hissuno types`
|
|
379
390
|
|
package/dist/index.js
CHANGED
|
@@ -95,9 +95,6 @@ function formatResourceList(type, items, total) {
|
|
|
95
95
|
case "knowledge":
|
|
96
96
|
lines.push(formatKnowledgeRow(item, name));
|
|
97
97
|
break;
|
|
98
|
-
case "sources":
|
|
99
|
-
lines.push(formatSourceRow(item, name));
|
|
100
|
-
break;
|
|
101
98
|
case "scopes":
|
|
102
99
|
lines.push(formatScopeRow(item, name));
|
|
103
100
|
break;
|
|
@@ -175,14 +172,6 @@ function formatScopeRow(item, name) {
|
|
|
175
172
|
${parts.join(" ")}`;
|
|
176
173
|
}
|
|
177
174
|
function formatKnowledgeRow(item, name) {
|
|
178
|
-
const desc = item.description ? dimText(truncate(String(item.description), 60)) : "";
|
|
179
|
-
const sources = item.sourceCount != null ? `${item.sourceCount} sources` : "";
|
|
180
|
-
const lastAnalyzed = item.lastAnalyzedAt ? `analyzed ${formatDate(item.lastAnalyzedAt)}` : dimText("not analyzed");
|
|
181
|
-
const parts = [desc, sources, lastAnalyzed].filter(Boolean);
|
|
182
|
-
return ` ${itemName(name)}
|
|
183
|
-
${parts.join(" ")}`;
|
|
184
|
-
}
|
|
185
|
-
function formatSourceRow(item, name) {
|
|
186
175
|
const id = dimText(truncate(String(item.id ?? ""), 12));
|
|
187
176
|
const type = item.type ? badge(String(item.type), MAGENTA) : "";
|
|
188
177
|
const status = item.status ? label(String(item.status)) : "";
|
|
@@ -213,9 +202,6 @@ function formatResourceDetail(type, item, extra) {
|
|
|
213
202
|
case "knowledge":
|
|
214
203
|
formatKnowledgeDetail(lines, item);
|
|
215
204
|
break;
|
|
216
|
-
case "sources":
|
|
217
|
-
formatSourceDetail(lines, item);
|
|
218
|
-
break;
|
|
219
205
|
case "scopes":
|
|
220
206
|
formatScopeDetail(lines, item);
|
|
221
207
|
break;
|
|
@@ -295,20 +281,6 @@ function formatScopeDetail(lines, item) {
|
|
|
295
281
|
}
|
|
296
282
|
}
|
|
297
283
|
function formatKnowledgeDetail(lines, item) {
|
|
298
|
-
if (item.description) lines.push(`${label("Description:")} ${item.description}`);
|
|
299
|
-
if (item.sourceCount != null) lines.push(`${label("Sources:")} ${item.sourceCount}`);
|
|
300
|
-
if (item.lastAnalyzedAt) lines.push(`${label("Last analyzed:")} ${formatDate(item.lastAnalyzedAt)}`);
|
|
301
|
-
if (item.compiled_at) lines.push(`${label("Compiled:")} ${formatDate(item.compiled_at)}`);
|
|
302
|
-
const sources = item.sources;
|
|
303
|
-
if (sources && sources.length > 0) {
|
|
304
|
-
lines.push("", `${BOLD}Sources:${RESET}`);
|
|
305
|
-
for (const src of sources) {
|
|
306
|
-
const parts = [src.type, src.status].filter(Boolean).join(" - ");
|
|
307
|
-
lines.push(` - ${src.name ?? "Unnamed"}${parts ? ` (${parts})` : ""}`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
function formatSourceDetail(lines, item) {
|
|
312
284
|
if (item.type) lines.push(`${label("Type:")} ${badge(String(item.type), MAGENTA)}`);
|
|
313
285
|
if (item.status) lines.push(`${label("Status:")} ${item.status}`);
|
|
314
286
|
if (item.enabled !== void 0) lines.push(`${label("Enabled:")} ${item.enabled ? badge("Yes", GREEN) : badge("No", RED)}`);
|
|
@@ -361,14 +333,10 @@ function formatResourceTypes() {
|
|
|
361
333
|
heading("Hissuno Resource Types"),
|
|
362
334
|
"",
|
|
363
335
|
`${BOLD}knowledge${RESET}`,
|
|
364
|
-
" Knowledge
|
|
336
|
+
" Knowledge sources (codebases, documents, URLs, Notion pages).",
|
|
365
337
|
` ${label("Filters:")} (none)`,
|
|
366
338
|
` ${label("Search:")} Semantic vector search across all knowledge chunks`,
|
|
367
339
|
"",
|
|
368
|
-
`${BOLD}sources${RESET}`,
|
|
369
|
-
" Individual knowledge sources (codebases, documents, URLs, Notion pages).",
|
|
370
|
-
` ${label("Filters:")} (none)`,
|
|
371
|
-
"",
|
|
372
340
|
`${BOLD}feedback${RESET}`,
|
|
373
341
|
" Customer feedback sessions (conversations from widget, Slack, Intercom, etc.).",
|
|
374
342
|
` ${label("Filters:")} --source, --status, --tags, --contact-id, --search`,
|
|
@@ -400,17 +368,11 @@ function formatResourceTypes() {
|
|
|
400
368
|
// src/commands/types.ts
|
|
401
369
|
var RESOURCE_TYPE_DEFINITIONS = {
|
|
402
370
|
knowledge: {
|
|
403
|
-
description: "Knowledge
|
|
371
|
+
description: "Knowledge sources (codebases, documents, URLs, Notion pages).",
|
|
404
372
|
filters: [],
|
|
405
373
|
search: "Semantic vector search across all knowledge chunks",
|
|
406
374
|
add: null
|
|
407
375
|
},
|
|
408
|
-
sources: {
|
|
409
|
-
description: "Individual knowledge sources (codebases, documents, URLs, Notion pages).",
|
|
410
|
-
filters: [],
|
|
411
|
-
search: null,
|
|
412
|
-
add: null
|
|
413
|
-
},
|
|
414
376
|
feedback: {
|
|
415
377
|
description: "Customer feedback sessions (conversations from widget, Slack, Intercom, etc.).",
|
|
416
378
|
filters: ["source", "status", "tags", "contact_id", "search"],
|
|
@@ -640,14 +602,13 @@ function resolveCustomerType(opt) {
|
|
|
640
602
|
|
|
641
603
|
// src/commands/list.ts
|
|
642
604
|
var TYPE_ENDPOINTS = {
|
|
643
|
-
knowledge: { path: "/api/knowledge/
|
|
644
|
-
sources: { path: "/api/knowledge/sources", key: "sources" },
|
|
605
|
+
knowledge: { path: "/api/knowledge/sources", key: "sources" },
|
|
645
606
|
feedback: { path: "/api/sessions", key: "sessions" },
|
|
646
607
|
issues: { path: "/api/issues", key: "issues" },
|
|
647
608
|
customers: { path: "/api/contacts", key: "contacts" },
|
|
648
609
|
scopes: { path: "/api/product-scopes", key: "scopes" }
|
|
649
610
|
};
|
|
650
|
-
var listCommand = new Command2("list").description("List resources of a given type").argument("<type>", "Resource type: knowledge,
|
|
611
|
+
var listCommand = new Command2("list").description("List resources of a given type").argument("<type>", "Resource type: knowledge, feedback, issues, customers, scopes").option("--source <source>", "Filter feedback by source (widget|slack|intercom|gong|api|manual)").option("--status <status>", "Filter by status").option("--tags <tags>", "Filter feedback by tags (comma-separated)").option("--contact-id <id>", "Filter feedback by contact ID").option("--search <query>", "Text search filter").option("--issue-type <type>", "Filter issues by type (bug|feature_request|change_request)").option("--priority <priority>", "Filter issues by priority (low|medium|high)").option("--company-id <id>", "Filter contacts by company ID").option("--role <role>", "Filter contacts by role").option("--customer-type <type>", "Customer sub-type: contacts (default) or companies").option("--stage <stage>", "Filter companies by stage (prospect|onboarding|active|churned|expansion)").option("--industry <industry>", "Filter companies by industry").option("--limit <n>", "Max results (default: 20)", "20").action(async (type, opts, cmd) => {
|
|
651
612
|
const config = requireConfig();
|
|
652
613
|
const jsonMode = cmd.parent?.opts().json;
|
|
653
614
|
const validTypes = Object.keys(TYPE_ENDPOINTS);
|
|
@@ -717,21 +678,19 @@ function cliTypeToEntityType(type, customerType) {
|
|
|
717
678
|
contact: "contact",
|
|
718
679
|
contacts: "contact",
|
|
719
680
|
knowledge: "knowledge_source",
|
|
720
|
-
sources: "knowledge_source",
|
|
721
681
|
scopes: "product_scope",
|
|
722
682
|
scope: "product_scope"
|
|
723
683
|
};
|
|
724
684
|
return map[type] ?? type;
|
|
725
685
|
}
|
|
726
686
|
var TYPE_ENDPOINTS2 = {
|
|
727
|
-
knowledge: { path: (id) => `/api/knowledge/
|
|
728
|
-
sources: { path: (id) => `/api/knowledge/sources/${id}`, key: "source" },
|
|
687
|
+
knowledge: { path: (id) => `/api/knowledge/sources/${id}`, key: "source" },
|
|
729
688
|
feedback: { path: (id) => `/api/sessions/${id}`, key: "session" },
|
|
730
689
|
issues: { path: (id) => `/api/issues/${id}`, key: "issue" },
|
|
731
690
|
customers: { path: (id) => `/api/contacts/${id}`, key: "contact" },
|
|
732
691
|
scopes: { path: (id) => `/api/product-scopes/${id}`, key: "scope" }
|
|
733
692
|
};
|
|
734
|
-
var getCommand = new Command3("get").description("Get full details of a specific resource").argument("<type>", "Resource type: knowledge,
|
|
693
|
+
var getCommand = new Command3("get").description("Get full details of a specific resource").argument("<type>", "Resource type: knowledge, feedback, issues, customers, scopes").argument("<id>", "Resource ID").option("--customer-type <type>", "Customer sub-type: contacts (default) or companies").action(async (type, id, opts, cmd) => {
|
|
735
694
|
const config = requireConfig();
|
|
736
695
|
const jsonMode = cmd.parent?.opts().json;
|
|
737
696
|
const validTypes = Object.keys(TYPE_ENDPOINTS2);
|
|
@@ -1054,7 +1013,7 @@ var addCommand = new Command5("add").description("Create a new resource interact
|
|
|
1054
1013
|
}
|
|
1055
1014
|
});
|
|
1056
1015
|
|
|
1057
|
-
// src/commands/
|
|
1016
|
+
// src/commands/integrations.ts
|
|
1058
1017
|
import { Command as Command6 } from "commander";
|
|
1059
1018
|
import { input as input2, select as select2, confirm as confirm2, password } from "@inquirer/prompts";
|
|
1060
1019
|
|
|
@@ -1066,10 +1025,10 @@ function openBrowser(url) {
|
|
|
1066
1025
|
exec(`${cmd} "${url}"`);
|
|
1067
1026
|
}
|
|
1068
1027
|
|
|
1069
|
-
// src/commands/
|
|
1070
|
-
var PLATFORMS = ["intercom", "gong", "zendesk", "slack", "github", "jira", "linear"];
|
|
1028
|
+
// src/commands/integrations.ts
|
|
1029
|
+
var PLATFORMS = ["intercom", "gong", "zendesk", "slack", "github", "jira", "linear", "fathom", "hubspot", "notion"];
|
|
1071
1030
|
var OAUTH_PLATFORMS = ["slack", "github", "jira", "linear"];
|
|
1072
|
-
var SYNCABLE_PLATFORMS = ["intercom", "gong", "zendesk"];
|
|
1031
|
+
var SYNCABLE_PLATFORMS = ["intercom", "gong", "zendesk", "fathom", "hubspot"];
|
|
1073
1032
|
var PLATFORM_LABELS = {
|
|
1074
1033
|
intercom: "Intercom",
|
|
1075
1034
|
gong: "Gong",
|
|
@@ -1077,10 +1036,19 @@ var PLATFORM_LABELS = {
|
|
|
1077
1036
|
slack: "Slack",
|
|
1078
1037
|
github: "GitHub",
|
|
1079
1038
|
jira: "Jira",
|
|
1080
|
-
linear: "Linear"
|
|
1039
|
+
linear: "Linear",
|
|
1040
|
+
fathom: "Fathom",
|
|
1041
|
+
hubspot: "HubSpot",
|
|
1042
|
+
notion: "Notion"
|
|
1081
1043
|
};
|
|
1082
|
-
|
|
1083
|
-
|
|
1044
|
+
var SYNC_FREQUENCY_CHOICES = [
|
|
1045
|
+
{ value: "manual", name: "Manual" },
|
|
1046
|
+
{ value: "1h", name: "Every hour" },
|
|
1047
|
+
{ value: "6h", name: "Every 6 hours" },
|
|
1048
|
+
{ value: "24h", name: "Every 24 hours" }
|
|
1049
|
+
];
|
|
1050
|
+
async function promptSyncFrequency(existing) {
|
|
1051
|
+
return existing || await select2({ message: "Sync frequency:", choices: [...SYNC_FREQUENCY_CHOICES], default: "24h" });
|
|
1084
1052
|
}
|
|
1085
1053
|
async function getStatus(config, platform2, projectId) {
|
|
1086
1054
|
return apiCall(config, "GET", `/api/integrations/${platform2}?projectId=${projectId}`);
|
|
@@ -1137,6 +1105,28 @@ function formatStatus(platform2, data) {
|
|
|
1137
1105
|
if (data.lastSyncStatus) lines.push(`**Last Sync Status:** ${data.lastSyncStatus}`);
|
|
1138
1106
|
lines.push(`**Tickets Synced:** ${data.lastSyncTicketsCount ?? 0}`);
|
|
1139
1107
|
}
|
|
1108
|
+
if (platform2 === "fathom") {
|
|
1109
|
+
if (data.syncFrequency) lines.push(`**Sync Frequency:** ${data.syncFrequency}`);
|
|
1110
|
+
lines.push(`**Sync Enabled:** ${data.syncEnabled ? "Yes" : "No"}`);
|
|
1111
|
+
if (data.lastSyncAt) lines.push(`**Last Sync:** ${data.lastSyncAt}`);
|
|
1112
|
+
if (data.lastSyncStatus) lines.push(`**Last Sync Status:** ${data.lastSyncStatus}`);
|
|
1113
|
+
lines.push(`**Meetings Synced:** ${data.lastSyncMeetingsCount ?? 0}`);
|
|
1114
|
+
}
|
|
1115
|
+
if (platform2 === "hubspot") {
|
|
1116
|
+
if (data.hubName) lines.push(`**Hub:** ${data.hubName}`);
|
|
1117
|
+
if (data.authMethod) lines.push(`**Auth Method:** ${data.authMethod}`);
|
|
1118
|
+
if (data.syncFrequency) lines.push(`**Sync Frequency:** ${data.syncFrequency}`);
|
|
1119
|
+
lines.push(`**Sync Enabled:** ${data.syncEnabled ? "Yes" : "No"}`);
|
|
1120
|
+
if (data.overwritePolicy) lines.push(`**Overwrite Policy:** ${data.overwritePolicy}`);
|
|
1121
|
+
if (data.lastSyncAt) lines.push(`**Last Sync:** ${data.lastSyncAt}`);
|
|
1122
|
+
if (data.lastSyncStatus) lines.push(`**Last Sync Status:** ${data.lastSyncStatus}`);
|
|
1123
|
+
lines.push(`**Companies Synced:** ${data.lastSyncCompaniesCount ?? 0}`);
|
|
1124
|
+
lines.push(`**Contacts Synced:** ${data.lastSyncContactsCount ?? 0}`);
|
|
1125
|
+
}
|
|
1126
|
+
if (platform2 === "notion") {
|
|
1127
|
+
if (data.workspaceName) lines.push(`**Workspace:** ${data.workspaceName}`);
|
|
1128
|
+
if (data.authMethod) lines.push(`**Auth Method:** ${data.authMethod}`);
|
|
1129
|
+
}
|
|
1140
1130
|
const stats = data.stats;
|
|
1141
1131
|
if (stats?.totalSynced != null) {
|
|
1142
1132
|
lines.push(`**Total Synced:** ${stats.totalSynced}`);
|
|
@@ -1165,16 +1155,7 @@ async function connectGong(config, projectId, opts) {
|
|
|
1165
1155
|
const accessKey = opts.accessKey || await input2({ message: "Access Key:", validate: (v) => v.length > 0 || "Required" });
|
|
1166
1156
|
const accessKeySecret = opts.accessKeySecret || await password({ message: "Access Key Secret:", mask: "*", validate: (v) => v.length > 0 || "Required" });
|
|
1167
1157
|
const baseUrl = opts.baseUrl || await input2({ message: "Base URL:", default: "https://api.gong.io" });
|
|
1168
|
-
const syncFrequency = opts.syncFrequency
|
|
1169
|
-
message: "Sync frequency:",
|
|
1170
|
-
choices: [
|
|
1171
|
-
{ value: "manual", name: "Manual" },
|
|
1172
|
-
{ value: "1h", name: "Every hour" },
|
|
1173
|
-
{ value: "6h", name: "Every 6 hours" },
|
|
1174
|
-
{ value: "24h", name: "Every 24 hours" }
|
|
1175
|
-
],
|
|
1176
|
-
default: "24h"
|
|
1177
|
-
});
|
|
1158
|
+
const syncFrequency = await promptSyncFrequency(opts.syncFrequency);
|
|
1178
1159
|
console.log("\nValidating credentials...");
|
|
1179
1160
|
const result = await apiCall(config, "POST", "/api/integrations/gong/connect", {
|
|
1180
1161
|
projectId,
|
|
@@ -1195,16 +1176,7 @@ async function connectZendesk(config, projectId, opts) {
|
|
|
1195
1176
|
const subdomain = opts.subdomain || await input2({ message: "Zendesk subdomain (e.g., mycompany):", validate: (v) => v.length > 0 || "Required" });
|
|
1196
1177
|
const email = opts.email || await input2({ message: "Admin email:", validate: (v) => v.includes("@") || "Must be a valid email" });
|
|
1197
1178
|
const apiToken = opts.apiToken || await password({ message: "API token:", mask: "*", validate: (v) => v.length > 0 || "Required" });
|
|
1198
|
-
const syncFrequency = opts.syncFrequency
|
|
1199
|
-
message: "Sync frequency:",
|
|
1200
|
-
choices: [
|
|
1201
|
-
{ value: "manual", name: "Manual" },
|
|
1202
|
-
{ value: "1h", name: "Every hour" },
|
|
1203
|
-
{ value: "6h", name: "Every 6 hours" },
|
|
1204
|
-
{ value: "24h", name: "Every 24 hours" }
|
|
1205
|
-
],
|
|
1206
|
-
default: "24h"
|
|
1207
|
-
});
|
|
1179
|
+
const syncFrequency = await promptSyncFrequency(opts.syncFrequency);
|
|
1208
1180
|
console.log("\nValidating credentials...");
|
|
1209
1181
|
const result = await apiCall(config, "POST", "/api/integrations/zendesk/connect", {
|
|
1210
1182
|
projectId,
|
|
@@ -1233,16 +1205,7 @@ async function connectIntercom(config, projectId, opts) {
|
|
|
1233
1205
|
return connectOAuth(config, "intercom", projectId);
|
|
1234
1206
|
}
|
|
1235
1207
|
const accessToken = opts.accessToken || await password({ message: "Access Token:", mask: "*", validate: (v) => v.length > 0 || "Required" });
|
|
1236
|
-
const syncFrequency = opts.syncFrequency
|
|
1237
|
-
message: "Sync frequency:",
|
|
1238
|
-
choices: [
|
|
1239
|
-
{ value: "manual", name: "Manual" },
|
|
1240
|
-
{ value: "1h", name: "Every hour" },
|
|
1241
|
-
{ value: "6h", name: "Every 6 hours" },
|
|
1242
|
-
{ value: "24h", name: "Every 24 hours" }
|
|
1243
|
-
],
|
|
1244
|
-
default: "24h"
|
|
1245
|
-
});
|
|
1208
|
+
const syncFrequency = await promptSyncFrequency(opts.syncFrequency);
|
|
1246
1209
|
console.log("\nValidating credentials...");
|
|
1247
1210
|
const result = await apiCall(config, "POST", "/api/integrations/intercom/connect", {
|
|
1248
1211
|
projectId,
|
|
@@ -1257,17 +1220,88 @@ async function connectIntercom(config, projectId, opts) {
|
|
|
1257
1220
|
error(`Connection failed: ${data.error || "Unknown error"}`);
|
|
1258
1221
|
return false;
|
|
1259
1222
|
}
|
|
1223
|
+
async function connectFathom(config, projectId, opts) {
|
|
1224
|
+
const apiKey = opts.apiKey || await password({ message: "Fathom API Key:", mask: "*", validate: (v) => v.length > 0 || "Required" });
|
|
1225
|
+
const syncFrequency = await promptSyncFrequency(opts.syncFrequency);
|
|
1226
|
+
console.log("\nValidating credentials...");
|
|
1227
|
+
const result = await apiCall(config, "POST", "/api/integrations/fathom/connect", {
|
|
1228
|
+
projectId,
|
|
1229
|
+
apiKey,
|
|
1230
|
+
syncFrequency
|
|
1231
|
+
});
|
|
1232
|
+
if (result.ok) {
|
|
1233
|
+
success("Fathom connected successfully!");
|
|
1234
|
+
return true;
|
|
1235
|
+
}
|
|
1236
|
+
const data = result.data;
|
|
1237
|
+
error(`Connection failed: ${data.error || "Unknown error"}`);
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
async function connectHubspot(config, projectId, opts) {
|
|
1241
|
+
const method = opts.accessToken ? "token" : await select2({
|
|
1242
|
+
message: "Connection method:",
|
|
1243
|
+
choices: [
|
|
1244
|
+
{ value: "token", name: "Private App Token" },
|
|
1245
|
+
{ value: "oauth", name: "OAuth (opens browser)" }
|
|
1246
|
+
]
|
|
1247
|
+
});
|
|
1248
|
+
if (method === "oauth") {
|
|
1249
|
+
return connectOAuth(config, "hubspot", projectId);
|
|
1250
|
+
}
|
|
1251
|
+
const accessToken = opts.accessToken || await password({ message: "Private App Token:", mask: "*", validate: (v) => v.length > 0 || "Required" });
|
|
1252
|
+
const syncFrequency = await promptSyncFrequency(opts.syncFrequency);
|
|
1253
|
+
const overwritePolicy = opts.overwritePolicy || await select2({
|
|
1254
|
+
message: "Overwrite policy for existing records:",
|
|
1255
|
+
choices: [
|
|
1256
|
+
{ value: "fill_nulls", name: "Fill nulls only (keep existing data, fill gaps)" },
|
|
1257
|
+
{ value: "hubspot_wins", name: "HubSpot wins (overwrite with HubSpot data)" },
|
|
1258
|
+
{ value: "never_overwrite", name: "Never overwrite (only create new records)" }
|
|
1259
|
+
],
|
|
1260
|
+
default: "fill_nulls"
|
|
1261
|
+
});
|
|
1262
|
+
console.log("\nValidating credentials...");
|
|
1263
|
+
const result = await apiCall(config, "POST", "/api/integrations/hubspot/connect", {
|
|
1264
|
+
projectId,
|
|
1265
|
+
accessToken,
|
|
1266
|
+
syncFrequency,
|
|
1267
|
+
overwritePolicy
|
|
1268
|
+
});
|
|
1269
|
+
if (result.ok) {
|
|
1270
|
+
success("HubSpot connected successfully!");
|
|
1271
|
+
return true;
|
|
1272
|
+
}
|
|
1273
|
+
const data = result.data;
|
|
1274
|
+
error(`Connection failed: ${data.error || "Unknown error"}`);
|
|
1275
|
+
return false;
|
|
1276
|
+
}
|
|
1277
|
+
async function connectNotion(config, projectId, opts) {
|
|
1278
|
+
const method = opts.accessToken ? "token" : await select2({
|
|
1279
|
+
message: "Connection method:",
|
|
1280
|
+
choices: [
|
|
1281
|
+
{ value: "token", name: "Internal Integration Token" },
|
|
1282
|
+
{ value: "oauth", name: "OAuth (opens browser)" }
|
|
1283
|
+
]
|
|
1284
|
+
});
|
|
1285
|
+
if (method === "oauth") {
|
|
1286
|
+
return connectOAuth(config, "notion", projectId);
|
|
1287
|
+
}
|
|
1288
|
+
const accessToken = opts.accessToken || await password({ message: "Integration Token:", mask: "*", validate: (v) => v.length > 0 || "Required" });
|
|
1289
|
+
console.log("\nValidating credentials...");
|
|
1290
|
+
const result = await apiCall(config, "POST", "/api/integrations/notion/connect", {
|
|
1291
|
+
projectId,
|
|
1292
|
+
accessToken
|
|
1293
|
+
});
|
|
1294
|
+
if (result.ok) {
|
|
1295
|
+
success("Notion connected successfully!");
|
|
1296
|
+
return true;
|
|
1297
|
+
}
|
|
1298
|
+
const data = result.data;
|
|
1299
|
+
error(`Connection failed: ${data.error || "Unknown error"}`);
|
|
1300
|
+
return false;
|
|
1301
|
+
}
|
|
1260
1302
|
async function configureIntegration(config, platform2, projectId) {
|
|
1261
1303
|
if (SYNCABLE_PLATFORMS.includes(platform2)) {
|
|
1262
|
-
const syncFrequency = await
|
|
1263
|
-
message: "Sync frequency:",
|
|
1264
|
-
choices: [
|
|
1265
|
-
{ value: "manual", name: "Manual" },
|
|
1266
|
-
{ value: "1h", name: "Every hour" },
|
|
1267
|
-
{ value: "6h", name: "Every 6 hours" },
|
|
1268
|
-
{ value: "24h", name: "Every 24 hours" }
|
|
1269
|
-
]
|
|
1270
|
-
});
|
|
1304
|
+
const syncFrequency = await promptSyncFrequency();
|
|
1271
1305
|
const result = await apiCall(config, "PATCH", `/api/integrations/${platform2}?projectId=${projectId}`, {
|
|
1272
1306
|
syncFrequency
|
|
1273
1307
|
});
|
|
@@ -1401,6 +1435,12 @@ async function interactiveWizard(config, platform2, projectId, jsonMode) {
|
|
|
1401
1435
|
await connectZendesk(config, projectId, {});
|
|
1402
1436
|
} else if (platform2 === "intercom") {
|
|
1403
1437
|
await connectIntercom(config, projectId, {});
|
|
1438
|
+
} else if (platform2 === "fathom") {
|
|
1439
|
+
await connectFathom(config, projectId, {});
|
|
1440
|
+
} else if (platform2 === "hubspot") {
|
|
1441
|
+
await connectHubspot(config, projectId, {});
|
|
1442
|
+
} else if (platform2 === "notion") {
|
|
1443
|
+
await connectNotion(config, projectId, {});
|
|
1404
1444
|
}
|
|
1405
1445
|
}
|
|
1406
1446
|
return;
|
|
@@ -1427,122 +1467,159 @@ async function interactiveWizard(config, platform2, projectId, jsonMode) {
|
|
|
1427
1467
|
break;
|
|
1428
1468
|
}
|
|
1429
1469
|
}
|
|
1430
|
-
|
|
1431
|
-
const config = requireConfig();
|
|
1432
|
-
const jsonMode = cmd.parent?.opts().json;
|
|
1433
|
-
const projectId = await getProjectId(config);
|
|
1434
|
-
if (!platformArg) {
|
|
1435
|
-
const results = await Promise.all(
|
|
1436
|
-
PLATFORMS.map(async (p) => {
|
|
1437
|
-
const result = await getStatus(config, p, projectId);
|
|
1438
|
-
return {
|
|
1439
|
-
platform: p,
|
|
1440
|
-
name: PLATFORM_LABELS[p],
|
|
1441
|
-
connected: result.ok ? result.data.connected === true : false,
|
|
1442
|
-
data: result.ok ? result.data : null
|
|
1443
|
-
};
|
|
1444
|
-
})
|
|
1445
|
-
);
|
|
1446
|
-
if (jsonMode) {
|
|
1447
|
-
console.log(renderJson(results));
|
|
1448
|
-
return;
|
|
1449
|
-
}
|
|
1450
|
-
console.log(renderMarkdown("# Integrations\n"));
|
|
1451
|
-
for (const r of results) {
|
|
1452
|
-
const status = r.connected ? "\x1B[32mConnected\x1B[0m" : "\x1B[2mNot connected\x1B[0m";
|
|
1453
|
-
console.log(` ${r.name.padEnd(12)} ${status}`);
|
|
1454
|
-
}
|
|
1455
|
-
console.log("\nRun `hissuno integrate <platform>` to manage a specific integration.");
|
|
1456
|
-
return;
|
|
1457
|
-
}
|
|
1470
|
+
function validatePlatform(platformArg) {
|
|
1458
1471
|
const platform2 = platformArg.toLowerCase();
|
|
1459
1472
|
if (!PLATFORMS.includes(platform2)) {
|
|
1460
1473
|
error(`Unknown platform "${platformArg}". Supported: ${PLATFORMS.join(", ")}`);
|
|
1461
1474
|
process.exit(1);
|
|
1462
1475
|
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1476
|
+
return platform2;
|
|
1477
|
+
}
|
|
1478
|
+
async function listIntegrations(config, projectId, jsonMode) {
|
|
1479
|
+
const results = await Promise.all(
|
|
1480
|
+
PLATFORMS.map(async (p) => {
|
|
1481
|
+
const result = await getStatus(config, p, projectId);
|
|
1482
|
+
return {
|
|
1483
|
+
platform: p,
|
|
1484
|
+
name: PLATFORM_LABELS[p],
|
|
1485
|
+
connected: result.ok ? result.data.connected === true : false,
|
|
1486
|
+
data: result.ok ? result.data : null
|
|
1487
|
+
};
|
|
1488
|
+
})
|
|
1489
|
+
);
|
|
1490
|
+
if (jsonMode) {
|
|
1491
|
+
console.log(renderJson(results));
|
|
1465
1492
|
return;
|
|
1466
1493
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
if (!result.ok) {
|
|
1472
|
-
const data2 = result.data;
|
|
1473
|
-
error(`Failed: ${data2.error || `HTTP ${result.status}`}`);
|
|
1474
|
-
process.exit(1);
|
|
1475
|
-
}
|
|
1476
|
-
const data = result.data;
|
|
1477
|
-
if (jsonMode) {
|
|
1478
|
-
console.log(renderJson(data));
|
|
1479
|
-
} else {
|
|
1480
|
-
console.log(renderMarkdown(formatStatus(platform2, data)));
|
|
1481
|
-
}
|
|
1482
|
-
break;
|
|
1483
|
-
}
|
|
1484
|
-
case "connect": {
|
|
1485
|
-
const statusResult = await getStatus(config, platform2, projectId);
|
|
1486
|
-
if (statusResult.ok && statusResult.data.connected) {
|
|
1487
|
-
warn(`${PLATFORM_LABELS[platform2]} is already connected.`);
|
|
1488
|
-
return;
|
|
1489
|
-
}
|
|
1490
|
-
if (OAUTH_PLATFORMS.includes(platform2)) {
|
|
1491
|
-
await connectOAuth(config, platform2, projectId);
|
|
1492
|
-
} else if (platform2 === "gong") {
|
|
1493
|
-
await connectGong(config, projectId, {
|
|
1494
|
-
accessKey: opts.accessKey || "",
|
|
1495
|
-
accessKeySecret: opts.accessKeySecret || "",
|
|
1496
|
-
baseUrl: opts.baseUrl || "",
|
|
1497
|
-
syncFrequency: opts.syncFrequency || ""
|
|
1498
|
-
});
|
|
1499
|
-
} else if (platform2 === "zendesk") {
|
|
1500
|
-
await connectZendesk(config, projectId, {
|
|
1501
|
-
subdomain: opts.subdomain || "",
|
|
1502
|
-
email: opts.email || "",
|
|
1503
|
-
apiToken: opts.apiToken || "",
|
|
1504
|
-
syncFrequency: opts.syncFrequency || ""
|
|
1505
|
-
});
|
|
1506
|
-
} else if (platform2 === "intercom") {
|
|
1507
|
-
await connectIntercom(config, projectId, {
|
|
1508
|
-
accessToken: opts.accessToken || "",
|
|
1509
|
-
syncFrequency: opts.syncFrequency || ""
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
|
-
break;
|
|
1513
|
-
}
|
|
1514
|
-
case "configure": {
|
|
1515
|
-
const statusResult = await getStatus(config, platform2, projectId);
|
|
1516
|
-
if (!statusResult.ok || !statusResult.data.connected) {
|
|
1517
|
-
error(`${PLATFORM_LABELS[platform2]} is not connected. Run 'hissuno integrate ${platform2} connect' first.`);
|
|
1518
|
-
process.exit(1);
|
|
1519
|
-
}
|
|
1520
|
-
await configureIntegration(config, platform2, projectId);
|
|
1521
|
-
break;
|
|
1522
|
-
}
|
|
1523
|
-
case "sync": {
|
|
1524
|
-
const statusResult = await getStatus(config, platform2, projectId);
|
|
1525
|
-
if (!statusResult.ok || !statusResult.data.connected) {
|
|
1526
|
-
error(`${PLATFORM_LABELS[platform2]} is not connected.`);
|
|
1527
|
-
process.exit(1);
|
|
1528
|
-
}
|
|
1529
|
-
await syncIntegration(config, platform2, projectId, opts.mode || "");
|
|
1530
|
-
break;
|
|
1531
|
-
}
|
|
1532
|
-
case "disconnect": {
|
|
1533
|
-
const statusResult = await getStatus(config, platform2, projectId);
|
|
1534
|
-
if (!statusResult.ok || !statusResult.data.connected) {
|
|
1535
|
-
error(`${PLATFORM_LABELS[platform2]} is not connected.`);
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
await disconnectIntegration(config, platform2, projectId);
|
|
1539
|
-
break;
|
|
1540
|
-
}
|
|
1541
|
-
default:
|
|
1542
|
-
error(`Unknown action "${action}". Valid actions: status, connect, configure, sync, disconnect`);
|
|
1543
|
-
process.exit(1);
|
|
1494
|
+
console.log(renderMarkdown("# Integrations\n"));
|
|
1495
|
+
for (const r of results) {
|
|
1496
|
+
const status = r.connected ? "\x1B[32mConnected\x1B[0m" : "\x1B[2mNot connected\x1B[0m";
|
|
1497
|
+
console.log(` ${r.name.padEnd(12)} ${status}`);
|
|
1544
1498
|
}
|
|
1499
|
+
console.log("\nRun `hissuno integrations <platform>` to manage a specific integration.");
|
|
1500
|
+
}
|
|
1501
|
+
var listSubcommand = new Command6("list").description("List all integrations with connection status").action(async (_opts, cmd) => {
|
|
1502
|
+
const config = requireConfig();
|
|
1503
|
+
const jsonMode = cmd.parent?.parent?.opts().json;
|
|
1504
|
+
const projectId = await resolveProjectId(config);
|
|
1505
|
+
await listIntegrations(config, projectId, jsonMode);
|
|
1506
|
+
});
|
|
1507
|
+
var addSubcommand = new Command6("add").description("Connect an integration platform").argument("<platform>", `Platform to connect (${PLATFORMS.join(", ")})`).option("--access-key <key>", "Gong access key").option("--access-key-secret <secret>", "Gong access key secret").option("--base-url <url>", "Gong base URL").option("--subdomain <subdomain>", "Zendesk subdomain").option("--email <email>", "Zendesk admin email").option("--api-token <token>", "Zendesk API token").option("--access-token <token>", "Intercom/HubSpot/Notion access token").option("--api-key <key>", "Fathom API key").option("--overwrite-policy <policy>", "HubSpot overwrite policy: fill_nulls, hubspot_wins, never_overwrite").option("--sync-frequency <freq>", "Sync frequency: manual, 1h, 6h, 24h").action(async (platformArg, opts, cmd) => {
|
|
1508
|
+
const config = requireConfig();
|
|
1509
|
+
const platform2 = validatePlatform(platformArg);
|
|
1510
|
+
const projectId = await resolveProjectId(config);
|
|
1511
|
+
const statusResult = await getStatus(config, platform2, projectId);
|
|
1512
|
+
if (statusResult.ok && statusResult.data.connected) {
|
|
1513
|
+
warn(`${PLATFORM_LABELS[platform2]} is already connected.`);
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
if (OAUTH_PLATFORMS.includes(platform2)) {
|
|
1517
|
+
await connectOAuth(config, platform2, projectId);
|
|
1518
|
+
} else if (platform2 === "gong") {
|
|
1519
|
+
await connectGong(config, projectId, {
|
|
1520
|
+
accessKey: opts.accessKey || "",
|
|
1521
|
+
accessKeySecret: opts.accessKeySecret || "",
|
|
1522
|
+
baseUrl: opts.baseUrl || "",
|
|
1523
|
+
syncFrequency: opts.syncFrequency || ""
|
|
1524
|
+
});
|
|
1525
|
+
} else if (platform2 === "zendesk") {
|
|
1526
|
+
await connectZendesk(config, projectId, {
|
|
1527
|
+
subdomain: opts.subdomain || "",
|
|
1528
|
+
email: opts.email || "",
|
|
1529
|
+
apiToken: opts.apiToken || "",
|
|
1530
|
+
syncFrequency: opts.syncFrequency || ""
|
|
1531
|
+
});
|
|
1532
|
+
} else if (platform2 === "intercom") {
|
|
1533
|
+
await connectIntercom(config, projectId, {
|
|
1534
|
+
accessToken: opts.accessToken || "",
|
|
1535
|
+
syncFrequency: opts.syncFrequency || ""
|
|
1536
|
+
});
|
|
1537
|
+
} else if (platform2 === "fathom") {
|
|
1538
|
+
await connectFathom(config, projectId, {
|
|
1539
|
+
apiKey: opts.apiKey || "",
|
|
1540
|
+
syncFrequency: opts.syncFrequency || ""
|
|
1541
|
+
});
|
|
1542
|
+
} else if (platform2 === "hubspot") {
|
|
1543
|
+
await connectHubspot(config, projectId, {
|
|
1544
|
+
accessToken: opts.accessToken || "",
|
|
1545
|
+
syncFrequency: opts.syncFrequency || "",
|
|
1546
|
+
overwritePolicy: opts.overwritePolicy || ""
|
|
1547
|
+
});
|
|
1548
|
+
} else if (platform2 === "notion") {
|
|
1549
|
+
await connectNotion(config, projectId, {
|
|
1550
|
+
accessToken: opts.accessToken || ""
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
});
|
|
1554
|
+
var statusSubcommand = new Command6("status").description("Show detailed status of an integration").argument("<platform>", `Platform to check (${PLATFORMS.join(", ")})`).action(async (platformArg, _opts, cmd) => {
|
|
1555
|
+
const config = requireConfig();
|
|
1556
|
+
const jsonMode = cmd.parent?.parent?.opts().json;
|
|
1557
|
+
const platform2 = validatePlatform(platformArg);
|
|
1558
|
+
const projectId = await resolveProjectId(config);
|
|
1559
|
+
const result = await getStatus(config, platform2, projectId);
|
|
1560
|
+
if (!result.ok) {
|
|
1561
|
+
const data2 = result.data;
|
|
1562
|
+
error(`Failed: ${data2.error || `HTTP ${result.status}`}`);
|
|
1563
|
+
process.exit(1);
|
|
1564
|
+
}
|
|
1565
|
+
const data = result.data;
|
|
1566
|
+
if (jsonMode) {
|
|
1567
|
+
console.log(renderJson(data));
|
|
1568
|
+
} else {
|
|
1569
|
+
console.log(renderMarkdown(formatStatus(platform2, data)));
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
var configureSubcommand = new Command6("configure").description("Update integration settings").argument("<platform>", `Platform to configure (${PLATFORMS.join(", ")})`).action(async (platformArg) => {
|
|
1573
|
+
const config = requireConfig();
|
|
1574
|
+
const platform2 = validatePlatform(platformArg);
|
|
1575
|
+
const projectId = await resolveProjectId(config);
|
|
1576
|
+
const statusResult = await getStatus(config, platform2, projectId);
|
|
1577
|
+
if (!statusResult.ok || !statusResult.data.connected) {
|
|
1578
|
+
error(`${PLATFORM_LABELS[platform2]} is not connected. Run 'hissuno integrations add ${platform2}' first.`);
|
|
1579
|
+
process.exit(1);
|
|
1580
|
+
}
|
|
1581
|
+
await configureIntegration(config, platform2, projectId);
|
|
1582
|
+
});
|
|
1583
|
+
var syncSubcommand = new Command6("sync").description("Trigger a manual sync").argument("<platform>", `Platform to sync (${PLATFORMS.join(", ")})`).option("--mode <mode>", "Sync mode: incremental, full").action(async (platformArg, opts) => {
|
|
1584
|
+
const config = requireConfig();
|
|
1585
|
+
const platform2 = validatePlatform(platformArg);
|
|
1586
|
+
const projectId = await resolveProjectId(config);
|
|
1587
|
+
const statusResult = await getStatus(config, platform2, projectId);
|
|
1588
|
+
if (!statusResult.ok || !statusResult.data.connected) {
|
|
1589
|
+
error(`${PLATFORM_LABELS[platform2]} is not connected.`);
|
|
1590
|
+
process.exit(1);
|
|
1591
|
+
}
|
|
1592
|
+
await syncIntegration(config, platform2, projectId, opts.mode || "");
|
|
1593
|
+
});
|
|
1594
|
+
var disconnectSubcommand = new Command6("disconnect").description("Disconnect an integration").argument("<platform>", `Platform to disconnect (${PLATFORMS.join(", ")})`).action(async (platformArg) => {
|
|
1595
|
+
const config = requireConfig();
|
|
1596
|
+
const platform2 = validatePlatform(platformArg);
|
|
1597
|
+
const projectId = await resolveProjectId(config);
|
|
1598
|
+
const statusResult = await getStatus(config, platform2, projectId);
|
|
1599
|
+
if (!statusResult.ok || !statusResult.data.connected) {
|
|
1600
|
+
error(`${PLATFORM_LABELS[platform2]} is not connected.`);
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
await disconnectIntegration(config, platform2, projectId);
|
|
1604
|
+
});
|
|
1605
|
+
var integrationsCommand = new Command6("integrations").description("Manage integrations (intercom, gong, zendesk, slack, github, jira, linear, fathom, hubspot, notion)").argument("[platform]", "Platform for interactive wizard").action(async (platformArg, _opts, cmd) => {
|
|
1606
|
+
const config = requireConfig();
|
|
1607
|
+
const jsonMode = cmd.parent?.opts().json;
|
|
1608
|
+
if (!platformArg) {
|
|
1609
|
+
const projectId2 = await resolveProjectId(config);
|
|
1610
|
+
await listIntegrations(config, projectId2, jsonMode);
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
const platform2 = validatePlatform(platformArg);
|
|
1614
|
+
const projectId = await resolveProjectId(config);
|
|
1615
|
+
await interactiveWizard(config, platform2, projectId, jsonMode);
|
|
1545
1616
|
});
|
|
1617
|
+
integrationsCommand.addCommand(listSubcommand);
|
|
1618
|
+
integrationsCommand.addCommand(addSubcommand);
|
|
1619
|
+
integrationsCommand.addCommand(statusSubcommand);
|
|
1620
|
+
integrationsCommand.addCommand(configureSubcommand);
|
|
1621
|
+
integrationsCommand.addCommand(syncSubcommand);
|
|
1622
|
+
integrationsCommand.addCommand(disconnectSubcommand);
|
|
1546
1623
|
|
|
1547
1624
|
// src/commands/update.ts
|
|
1548
1625
|
import { Command as Command7 } from "commander";
|
|
@@ -1751,9 +1828,9 @@ var log = {
|
|
|
1751
1828
|
},
|
|
1752
1829
|
_integrationHint() {
|
|
1753
1830
|
console.log(` ${BOLD2}Connect your data sources:${RESET2}`);
|
|
1754
|
-
console.log(` ${CYAN2}hissuno
|
|
1755
|
-
console.log(` ${CYAN2}hissuno
|
|
1756
|
-
console.log(` ${CYAN2}hissuno
|
|
1831
|
+
console.log(` ${CYAN2}hissuno integrations list${RESET2} ${DIM2}# see all integrations${RESET2}`);
|
|
1832
|
+
console.log(` ${CYAN2}hissuno integrations add slack${RESET2} ${DIM2}# connect Slack${RESET2}`);
|
|
1833
|
+
console.log(` ${CYAN2}hissuno integrations add intercom${RESET2} ${DIM2}# connect Intercom${RESET2}`);
|
|
1757
1834
|
console.log();
|
|
1758
1835
|
}
|
|
1759
1836
|
};
|
|
@@ -2239,7 +2316,7 @@ var OAUTH_PLATFORMS2 = {
|
|
|
2239
2316
|
};
|
|
2240
2317
|
var OAUTH_PLATFORM_NAMES = Object.keys(OAUTH_PLATFORMS2);
|
|
2241
2318
|
async function setupOAuth(appDir, platformArg, opts = {}) {
|
|
2242
|
-
const platform2 = platformArg ?
|
|
2319
|
+
const platform2 = platformArg ? validatePlatform2(platformArg) : await select5({
|
|
2243
2320
|
message: "Which integration?",
|
|
2244
2321
|
choices: OAUTH_PLATFORM_NAMES.map((p) => ({
|
|
2245
2322
|
value: p,
|
|
@@ -2289,7 +2366,7 @@ async function setupOAuth(appDir, platformArg, opts = {}) {
|
|
|
2289
2366
|
console.log(` npm run dev`);
|
|
2290
2367
|
console.log();
|
|
2291
2368
|
}
|
|
2292
|
-
function
|
|
2369
|
+
function validatePlatform2(name) {
|
|
2293
2370
|
const lower = name.toLowerCase();
|
|
2294
2371
|
if (!OAUTH_PLATFORM_NAMES.includes(lower)) {
|
|
2295
2372
|
throw new Error(
|
|
@@ -2553,6 +2630,12 @@ ${BOLD3}Step 3: Connect Data Sources (optional)${RESET3}`);
|
|
|
2553
2630
|
await connectZendesk(fullConfig, fullConfig.project_id, {});
|
|
2554
2631
|
} else if (platform2 === "intercom") {
|
|
2555
2632
|
await connectIntercom(fullConfig, fullConfig.project_id, {});
|
|
2633
|
+
} else if (platform2 === "fathom") {
|
|
2634
|
+
await connectFathom(fullConfig, fullConfig.project_id, {});
|
|
2635
|
+
} else if (platform2 === "hubspot") {
|
|
2636
|
+
await connectHubspot(fullConfig, fullConfig.project_id, {});
|
|
2637
|
+
} else if (platform2 === "notion") {
|
|
2638
|
+
await connectNotion(fullConfig, fullConfig.project_id, {});
|
|
2556
2639
|
}
|
|
2557
2640
|
connectMore = await confirm8({
|
|
2558
2641
|
message: "Connect another?",
|
|
@@ -2566,7 +2649,7 @@ ${DIM3}Next steps:${RESET3}
|
|
|
2566
2649
|
hissuno status Check connection health
|
|
2567
2650
|
hissuno search "checkout bugs" Search your intelligence
|
|
2568
2651
|
hissuno list feedback List feedback sessions
|
|
2569
|
-
hissuno
|
|
2652
|
+
hissuno integrations Manage integrations
|
|
2570
2653
|
`);
|
|
2571
2654
|
});
|
|
2572
2655
|
function maskApiKey(key) {
|
|
@@ -2980,6 +3063,6 @@ program.addCommand(getCommand);
|
|
|
2980
3063
|
program.addCommand(searchCommand);
|
|
2981
3064
|
program.addCommand(addCommand);
|
|
2982
3065
|
program.addCommand(updateCommand);
|
|
2983
|
-
program.addCommand(
|
|
3066
|
+
program.addCommand(integrationsCommand);
|
|
2984
3067
|
program.addCommand(membersCommand);
|
|
2985
3068
|
program.parseAsync(process.argv);
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -71,7 +71,7 @@ hissuno skills uninstall # Remove installed skills
|
|
|
71
71
|
| Find items by meaning | `hissuno search <query>` | "Find feedback about checkout flow" |
|
|
72
72
|
| Create new data | `hissuno add <type>` | "Log a bug about login failures" |
|
|
73
73
|
| Traverse relationships | `hissuno get <type> <id> --json` | "What companies are affected by this issue?" |
|
|
74
|
-
| Connect a data source | `hissuno
|
|
74
|
+
| Connect a data source | `hissuno integrations add <platform>` | "Connect Intercom" |
|
|
75
75
|
| View current connection | `hissuno config show` | "Which instance am I connected to?" |
|
|
76
76
|
| Switch environment | `hissuno profile use <name>` | "Switch to staging" |
|
|
77
77
|
|
|
@@ -111,9 +111,9 @@ Use `search` for semantic (meaning-based) matching. Use `list` when you want to
|
|
|
111
111
|
3. `hissuno config show` to verify the active connection
|
|
112
112
|
|
|
113
113
|
### Connect a Data Source
|
|
114
|
-
1. `hissuno
|
|
115
|
-
2. `hissuno
|
|
116
|
-
3. For syncable platforms: `hissuno
|
|
114
|
+
1. `hissuno integrations list` to see all integration statuses
|
|
115
|
+
2. `hissuno integrations <platform>` to start the interactive setup wizard
|
|
116
|
+
3. For syncable platforms: `hissuno integrations sync <platform>` to trigger initial data pull
|
|
117
117
|
|
|
118
118
|
## CLI Reference
|
|
119
119
|
|
|
@@ -132,8 +132,8 @@ hissuno get customers <id> # Contact details + company rel
|
|
|
132
132
|
hissuno add issues # Interactive issue creation
|
|
133
133
|
hissuno add scopes # Create a product scope with goals
|
|
134
134
|
hissuno update scopes <id> # Modify scope name, type, or goals
|
|
135
|
-
hissuno
|
|
136
|
-
hissuno
|
|
135
|
+
hissuno integrations list # List all integration statuses
|
|
136
|
+
hissuno integrations sync intercom # Sync Intercom conversations
|
|
137
137
|
hissuno config show # View active connection
|
|
138
138
|
hissuno profile list # List profiles
|
|
139
139
|
hissuno profile use staging # Switch to staging profile
|
|
@@ -320,23 +320,34 @@ hissuno update scopes <id>
|
|
|
320
320
|
|
|
321
321
|
---
|
|
322
322
|
|
|
323
|
-
### `hissuno
|
|
323
|
+
### `hissuno integrations`
|
|
324
324
|
|
|
325
325
|
Manage integrations with external platforms.
|
|
326
326
|
|
|
327
327
|
**Supported platforms:** `intercom`, `gong`, `zendesk`, `slack`, `github`, `jira`, `linear`
|
|
328
328
|
|
|
329
329
|
```bash
|
|
330
|
-
hissuno
|
|
331
|
-
hissuno
|
|
332
|
-
hissuno
|
|
333
|
-
hissuno
|
|
334
|
-
hissuno
|
|
335
|
-
hissuno
|
|
336
|
-
hissuno
|
|
330
|
+
hissuno integrations list # List all integrations with status
|
|
331
|
+
hissuno integrations <platform> # Interactive setup wizard
|
|
332
|
+
hissuno integrations status <platform> # Detailed status
|
|
333
|
+
hissuno integrations add <platform> # Connect (OAuth or token)
|
|
334
|
+
hissuno integrations configure <platform> # Update settings
|
|
335
|
+
hissuno integrations sync <platform> # Trigger manual sync
|
|
336
|
+
hissuno integrations disconnect <platform> # Disconnect
|
|
337
337
|
```
|
|
338
338
|
|
|
339
|
-
**
|
|
339
|
+
**Subcommands:**
|
|
340
|
+
|
|
341
|
+
| Subcommand | Args | Description |
|
|
342
|
+
|------------|------|-------------|
|
|
343
|
+
| `list` | | List all integrations with connection status |
|
|
344
|
+
| `add` | `<platform>` | Connect a platform (OAuth or token-based) |
|
|
345
|
+
| `status` | `<platform>` | Show detailed connection status |
|
|
346
|
+
| `configure` | `<platform>` | Update settings (sync frequency, auto-sync toggle) |
|
|
347
|
+
| `sync` | `<platform>` | Trigger a manual sync (Intercom, Gong, Zendesk only) |
|
|
348
|
+
| `disconnect` | `<platform>` | Disconnect the integration |
|
|
349
|
+
|
|
350
|
+
**Platform-specific add options:**
|
|
340
351
|
|
|
341
352
|
| Option | Platforms | Description |
|
|
342
353
|
|--------|-----------|-------------|
|
|
@@ -346,8 +357,10 @@ hissuno integrate <platform> disconnect # Disconnect
|
|
|
346
357
|
| `--subdomain <sub>` | zendesk | Zendesk subdomain |
|
|
347
358
|
| `--email <email>` | zendesk | Admin email |
|
|
348
359
|
| `--api-token <token>` | zendesk | API token |
|
|
349
|
-
| `--access-token <token>` | intercom | Access token |
|
|
350
|
-
| `--
|
|
351
|
-
| `--
|
|
360
|
+
| `--access-token <token>` | intercom, hubspot, notion | Access token |
|
|
361
|
+
| `--api-key <key>` | fathom | API key |
|
|
362
|
+
| `--overwrite-policy <policy>` | hubspot | `fill_nulls`, `hubspot_wins`, `never_overwrite` |
|
|
363
|
+
| `--sync-frequency <freq>` | gong, zendesk, intercom, fathom, hubspot | `manual`, `1h`, `6h`, `24h` |
|
|
364
|
+
| `--mode <mode>` | sync subcommand | `incremental` (default) or `full` |
|
|
352
365
|
|
|
353
366
|
See `references/INTEGRATIONS.md` for full platform details.
|
|
@@ -9,10 +9,13 @@ Connect external data sources to Hissuno via the CLI or dashboard.
|
|
|
9
9
|
| Intercom | Token or OAuth | Yes | Customer conversations |
|
|
10
10
|
| Gong | Token | Yes | Sales call recordings |
|
|
11
11
|
| Zendesk | Token | Yes | Support tickets |
|
|
12
|
+
| Fathom | Token | Yes | Meeting recordings |
|
|
13
|
+
| HubSpot | Token or OAuth | Yes | CRM (companies and contacts) |
|
|
12
14
|
| Slack | OAuth | No | Workspace messages |
|
|
13
15
|
| GitHub | OAuth | No | Issues and PRs |
|
|
14
16
|
| Jira | OAuth | No | Issue tracking (auto-sync available) |
|
|
15
17
|
| Linear | OAuth | No | Issue tracking (auto-sync available) |
|
|
18
|
+
| Notion | Token or OAuth | No | Documentation |
|
|
16
19
|
|
|
17
20
|
**Syncable** platforms pull data on a schedule (manual, 1h, 6h, 24h). Non-syncable platforms use real-time hooks or manual import.
|
|
18
21
|
|
|
@@ -21,34 +24,31 @@ Connect external data sources to Hissuno via the CLI or dashboard.
|
|
|
21
24
|
These integrations are configured through the Hissuno dashboard, not the CLI:
|
|
22
25
|
- Chat widget (embedded on your site)
|
|
23
26
|
- PostHog (product analytics)
|
|
24
|
-
- HubSpot (CRM)
|
|
25
|
-
- Fathom (meeting recordings)
|
|
26
|
-
- Notion (documentation)
|
|
27
27
|
|
|
28
28
|
## CLI Commands
|
|
29
29
|
|
|
30
30
|
### List all integrations
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
hissuno
|
|
34
|
-
hissuno
|
|
33
|
+
hissuno integrations list # Shows status of all 7 platforms
|
|
34
|
+
hissuno --json integrations list # JSON output
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
### Interactive setup
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
hissuno
|
|
40
|
+
hissuno integrations <platform> # Interactive wizard: shows status, offers add/configure/sync/disconnect
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
### Direct actions
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
hissuno
|
|
47
|
-
hissuno
|
|
48
|
-
hissuno
|
|
49
|
-
hissuno
|
|
50
|
-
hissuno
|
|
51
|
-
hissuno
|
|
46
|
+
hissuno integrations status <platform> # Detailed connection status
|
|
47
|
+
hissuno integrations add <platform> # Connect (OAuth opens browser, token prompts for credentials)
|
|
48
|
+
hissuno integrations configure <platform> # Update sync frequency or settings
|
|
49
|
+
hissuno integrations sync <platform> # Trigger manual sync (syncable platforms only)
|
|
50
|
+
hissuno integrations sync <platform> --mode full # Full re-sync (vs incremental)
|
|
51
|
+
hissuno integrations disconnect <platform> # Disconnect (with confirmation)
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
### Connection Options
|
|
@@ -57,17 +57,32 @@ Some platforms accept credentials directly (non-interactive):
|
|
|
57
57
|
|
|
58
58
|
**Gong:**
|
|
59
59
|
```bash
|
|
60
|
-
hissuno
|
|
60
|
+
hissuno integrations add gong --access-key <key> --access-key-secret <secret> --base-url <url> --sync-frequency 24h
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
**Zendesk:**
|
|
64
64
|
```bash
|
|
65
|
-
hissuno
|
|
65
|
+
hissuno integrations add zendesk --subdomain <sub> --email <email> --api-token <token> --sync-frequency 24h
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
**Intercom:**
|
|
69
69
|
```bash
|
|
70
|
-
hissuno
|
|
70
|
+
hissuno integrations add intercom --access-token <token> --sync-frequency 24h
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Fathom:**
|
|
74
|
+
```bash
|
|
75
|
+
hissuno integrations add fathom --api-key <key> --sync-frequency 24h
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**HubSpot:**
|
|
79
|
+
```bash
|
|
80
|
+
hissuno integrations add hubspot --access-token <token> --sync-frequency 24h --overwrite-policy fill_nulls
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Notion:**
|
|
84
|
+
```bash
|
|
85
|
+
hissuno integrations add notion --access-token <token>
|
|
71
86
|
```
|
|
72
87
|
|
|
73
88
|
## Status Details
|
|
@@ -77,7 +92,10 @@ Each platform returns different status fields when connected:
|
|
|
77
92
|
- **Intercom**: workspace name, auth method, sync frequency, last sync status, conversations synced
|
|
78
93
|
- **Gong**: sync frequency, last sync status, calls synced
|
|
79
94
|
- **Zendesk**: subdomain, account name, sync frequency, last sync status, tickets synced
|
|
95
|
+
- **Fathom**: sync frequency, last sync status, meetings synced
|
|
96
|
+
- **HubSpot**: hub name, auth method, sync frequency, overwrite policy, last sync status, companies/contacts synced
|
|
80
97
|
- **Slack**: workspace name, domain, installed by
|
|
81
98
|
- **GitHub**: account login, installed by
|
|
82
99
|
- **Jira**: site URL, project key, issue type, auto-sync status
|
|
83
100
|
- **Linear**: organization, team, auto-sync status
|
|
101
|
+
- **Notion**: workspace name, auth method
|