shepherd-onboard 0.1.16 → 0.1.18
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/bin/shepherd-onboard.js +106 -23
- package/package.json +1 -1
package/bin/shepherd-onboard.js
CHANGED
|
@@ -824,6 +824,8 @@ Domain-wide delegation OAuth Client ID: ${payload.googleWorkspaceDelegation.clie
|
|
|
824
824
|
Scopes:
|
|
825
825
|
${payload.googleWorkspaceDelegation.scopes.join("\n")}
|
|
826
826
|
|
|
827
|
+
The setup command copies those scopes to the clipboard as one comma-separated string on macOS. Tell the user they can paste directly into the OAuth scopes field. If clipboard copy is unavailable, use the scopes printed above.
|
|
828
|
+
|
|
827
829
|
The customer does not create a service account and does not upload service account JSON in the default Shepherd-managed flow. Their super admin only authorizes the Client ID and scopes in Google Admin Console. Shepherd's backend stores and uses its private service account JSON server-side.
|
|
828
830
|
Shepherd must still enforce selected users and groups internally before polling or impersonating any employee email.
|
|
829
831
|
|
|
@@ -918,15 +920,34 @@ function googleWorkspaceDelegationSetup(setup) {
|
|
|
918
920
|
|
|
919
921
|
function printGoogleWorkspaceDelegationSetup(setup) {
|
|
920
922
|
const resolved = googleWorkspaceDelegationSetup(setup);
|
|
923
|
+
const copiedScopes = copyTextToClipboard(resolved.scopes.join(","));
|
|
921
924
|
console.log(`App name: ${resolved.appName}`);
|
|
922
925
|
console.log(`Service account email: ${resolved.serviceAccountEmail}`);
|
|
923
926
|
console.log(`Domain-wide delegation OAuth Client ID: ${resolved.clientId}`);
|
|
924
927
|
console.log("Customer action: in Google Admin Console, add the Client ID above and paste these scopes.");
|
|
925
928
|
console.log("Customers do not create a service account or upload service account JSON; Shepherd stores its private service account JSON server-side.");
|
|
929
|
+
if (copiedScopes) {
|
|
930
|
+
console.log("Copied comma-separated scopes to the clipboard.");
|
|
931
|
+
} else if (platform() === "darwin") {
|
|
932
|
+
console.log("Could not copy scopes to the clipboard; copy the scopes below instead.");
|
|
933
|
+
}
|
|
926
934
|
console.log("\nScopes:");
|
|
927
935
|
for (const scope of resolved.scopes) console.log(scope);
|
|
928
936
|
}
|
|
929
937
|
|
|
938
|
+
function copyTextToClipboard(text) {
|
|
939
|
+
if (platform() !== "darwin") return false;
|
|
940
|
+
try {
|
|
941
|
+
execFileSync("pbcopy", [], {
|
|
942
|
+
input: text,
|
|
943
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
944
|
+
});
|
|
945
|
+
return true;
|
|
946
|
+
} catch {
|
|
947
|
+
return false;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
930
951
|
function authenticatedEmail(authenticated) {
|
|
931
952
|
return authenticated?.workosUser?.email ?? authenticated?.account?.email ?? null;
|
|
932
953
|
}
|
|
@@ -1356,15 +1377,15 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1356
1377
|
<title>Select Messages Chats</title>
|
|
1357
1378
|
<style>
|
|
1358
1379
|
:root {
|
|
1359
|
-
--bg: #
|
|
1380
|
+
--bg: #ffffff;
|
|
1360
1381
|
--fg: #141614;
|
|
1361
|
-
--muted: #
|
|
1362
|
-
--faint: #
|
|
1363
|
-
--line: #
|
|
1382
|
+
--muted: #615d59;
|
|
1383
|
+
--faint: #a39e98;
|
|
1384
|
+
--line: #e6e6e6;
|
|
1364
1385
|
--green: #1f5c2e;
|
|
1365
1386
|
--green-hover: #246836;
|
|
1366
1387
|
--radius: 8px;
|
|
1367
|
-
--grid: 20px minmax(0, 1fr)
|
|
1388
|
+
--grid: 20px minmax(0, 1fr) 76px 108px;
|
|
1368
1389
|
}
|
|
1369
1390
|
* { box-sizing: border-box; }
|
|
1370
1391
|
body {
|
|
@@ -1377,9 +1398,9 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1377
1398
|
-webkit-font-smoothing: antialiased;
|
|
1378
1399
|
}
|
|
1379
1400
|
main {
|
|
1380
|
-
width: min(
|
|
1401
|
+
width: min(640px, calc(100vw - 32px));
|
|
1381
1402
|
margin: 0 auto;
|
|
1382
|
-
padding:
|
|
1403
|
+
padding: 56px 0 28px;
|
|
1383
1404
|
}
|
|
1384
1405
|
.header {
|
|
1385
1406
|
display: grid;
|
|
@@ -1390,7 +1411,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1390
1411
|
.brand {
|
|
1391
1412
|
display: grid;
|
|
1392
1413
|
justify-items: center;
|
|
1393
|
-
gap:
|
|
1414
|
+
gap: 16px;
|
|
1394
1415
|
min-width: 0;
|
|
1395
1416
|
}
|
|
1396
1417
|
.logo {
|
|
@@ -1412,14 +1433,14 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1412
1433
|
}
|
|
1413
1434
|
h1 {
|
|
1414
1435
|
margin: 0;
|
|
1415
|
-
font-size:
|
|
1436
|
+
font-size: 30px;
|
|
1416
1437
|
line-height: 1.1;
|
|
1417
1438
|
font-weight: 700;
|
|
1418
1439
|
letter-spacing: 0;
|
|
1419
1440
|
}
|
|
1420
1441
|
.subtitle {
|
|
1421
1442
|
margin: 8px 0 0;
|
|
1422
|
-
font-size:
|
|
1443
|
+
font-size: 14px;
|
|
1423
1444
|
line-height: 1.4;
|
|
1424
1445
|
color: var(--muted);
|
|
1425
1446
|
}
|
|
@@ -1431,11 +1452,11 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1431
1452
|
}
|
|
1432
1453
|
.search {
|
|
1433
1454
|
width: 100%;
|
|
1434
|
-
border: 1px solid
|
|
1455
|
+
border: 1px solid var(--line);
|
|
1435
1456
|
border-radius: 8px;
|
|
1436
1457
|
background: #ffffff;
|
|
1437
1458
|
color: var(--fg);
|
|
1438
|
-
padding:
|
|
1459
|
+
padding: 12px 13px;
|
|
1439
1460
|
font: inherit;
|
|
1440
1461
|
font-size: 15px;
|
|
1441
1462
|
outline: none;
|
|
@@ -1463,6 +1484,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1463
1484
|
font-size: 11px;
|
|
1464
1485
|
font-weight: 600;
|
|
1465
1486
|
text-transform: uppercase;
|
|
1487
|
+
letter-spacing: 0.02em;
|
|
1466
1488
|
}
|
|
1467
1489
|
.list-head .right { text-align: left; }
|
|
1468
1490
|
.chat-list { display: block; }
|
|
@@ -1476,7 +1498,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1476
1498
|
cursor: pointer;
|
|
1477
1499
|
transition: background 120ms ease;
|
|
1478
1500
|
}
|
|
1479
|
-
.chat-row:hover { background: #
|
|
1501
|
+
.chat-row:hover { background: #f6f5f4; }
|
|
1480
1502
|
input[type="checkbox"] {
|
|
1481
1503
|
position: absolute;
|
|
1482
1504
|
opacity: 0;
|
|
@@ -1514,7 +1536,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1514
1536
|
text-overflow: ellipsis;
|
|
1515
1537
|
white-space: nowrap;
|
|
1516
1538
|
font-size: 15px;
|
|
1517
|
-
font-weight:
|
|
1539
|
+
font-weight: 500;
|
|
1518
1540
|
}
|
|
1519
1541
|
.chat-people {
|
|
1520
1542
|
color: var(--muted);
|
|
@@ -1528,9 +1550,9 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1528
1550
|
justify-self: start;
|
|
1529
1551
|
color: var(--muted);
|
|
1530
1552
|
font-size: 12.5px;
|
|
1531
|
-
font-weight:
|
|
1553
|
+
font-weight: 400;
|
|
1532
1554
|
}
|
|
1533
|
-
.chat-kind--group { color: var(--
|
|
1555
|
+
.chat-kind--group { color: var(--fg); }
|
|
1534
1556
|
.chat-meta {
|
|
1535
1557
|
color: var(--muted);
|
|
1536
1558
|
font-size: 12.5px;
|
|
@@ -1563,7 +1585,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1563
1585
|
button {
|
|
1564
1586
|
appearance: none;
|
|
1565
1587
|
border: 0;
|
|
1566
|
-
border-radius:
|
|
1588
|
+
border-radius: 8px;
|
|
1567
1589
|
background: var(--green);
|
|
1568
1590
|
color: #FFFFFF;
|
|
1569
1591
|
padding: 9px 13px;
|
|
@@ -1600,7 +1622,6 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1600
1622
|
</div>
|
|
1601
1623
|
${error ? `<p class="error">${html(error)}</p>` : ""}
|
|
1602
1624
|
<div class="list-head">
|
|
1603
|
-
<span></span>
|
|
1604
1625
|
<span></span>
|
|
1605
1626
|
<span>Name</span>
|
|
1606
1627
|
<span>Type</span>
|
|
@@ -1685,7 +1706,9 @@ function renderChatPeople(chat) {
|
|
|
1685
1706
|
|
|
1686
1707
|
function chatPeopleLine(chat) {
|
|
1687
1708
|
if (chat.kind !== "group") return "";
|
|
1688
|
-
const names = chat.participants
|
|
1709
|
+
const names = chat.participants
|
|
1710
|
+
?.map((participant) => participant.name)
|
|
1711
|
+
.filter((name) => name && !looksLikeHandleList(name)) ?? [];
|
|
1689
1712
|
const line = names.slice(0, 6).join(", ");
|
|
1690
1713
|
if (!line || normalizeDisplayText(line) === normalizeDisplayText(chat.label)) return "";
|
|
1691
1714
|
return line;
|
|
@@ -2022,8 +2045,8 @@ function createMessageSerializer(kit, contactLookup = emptyContactLookup()) {
|
|
|
2022
2045
|
};
|
|
2023
2046
|
}
|
|
2024
2047
|
|
|
2025
|
-
function buildContactLookup() {
|
|
2026
|
-
const contacts = loadContacts();
|
|
2048
|
+
function buildContactLookup(opts = {}) {
|
|
2049
|
+
const contacts = opts.loadAll === false ? [] : loadContacts();
|
|
2027
2050
|
const myCard = loadMyCard();
|
|
2028
2051
|
const handleToName = new Map();
|
|
2029
2052
|
const selfHandles = new Set();
|
|
@@ -2070,6 +2093,9 @@ function emptyContactLookup() {
|
|
|
2070
2093
|
|
|
2071
2094
|
function loadContacts() {
|
|
2072
2095
|
if (platform() !== "darwin") return [];
|
|
2096
|
+
const sqliteContacts = loadContactsFromAddressBookDb();
|
|
2097
|
+
if (sqliteContacts.length > 0) return sqliteContacts;
|
|
2098
|
+
|
|
2073
2099
|
const script = `
|
|
2074
2100
|
set output to ""
|
|
2075
2101
|
tell application "Contacts"
|
|
@@ -2093,14 +2119,71 @@ return output`;
|
|
|
2093
2119
|
try {
|
|
2094
2120
|
const raw = execFileSync("osascript", ["-e", script], {
|
|
2095
2121
|
encoding: "utf8",
|
|
2096
|
-
timeout:
|
|
2122
|
+
timeout: 120_000,
|
|
2097
2123
|
});
|
|
2098
2124
|
return parseContacts(raw);
|
|
2099
|
-
} catch {
|
|
2125
|
+
} catch (err) {
|
|
2126
|
+
if (args.debug === true) console.error("Could not load Contacts:", safeError(err));
|
|
2100
2127
|
return [];
|
|
2101
2128
|
}
|
|
2102
2129
|
}
|
|
2103
2130
|
|
|
2131
|
+
function loadContactsFromAddressBookDb() {
|
|
2132
|
+
const contacts = new Map();
|
|
2133
|
+
for (const dbPath of addressBookDatabasePaths()) {
|
|
2134
|
+
const query = `
|
|
2135
|
+
select r.Z_PK,
|
|
2136
|
+
coalesce(nullif(r.ZNAME, ''), nullif(trim(coalesce(r.ZFIRSTNAME, '') || ' ' || coalesce(r.ZLASTNAME, '')), ''), nullif(r.ZORGANIZATION, ''), '') as display_name,
|
|
2137
|
+
coalesce(p.ZFULLNUMBER, '') as phone,
|
|
2138
|
+
'' as email
|
|
2139
|
+
from ZABCDRECORD r
|
|
2140
|
+
join ZABCDPHONENUMBER p on p.ZOWNER = r.Z_PK
|
|
2141
|
+
where p.ZFULLNUMBER is not null and p.ZFULLNUMBER != ''
|
|
2142
|
+
union all
|
|
2143
|
+
select r.Z_PK,
|
|
2144
|
+
coalesce(nullif(r.ZNAME, ''), nullif(trim(coalesce(r.ZFIRSTNAME, '') || ' ' || coalesce(r.ZLASTNAME, '')), ''), nullif(r.ZORGANIZATION, ''), '') as display_name,
|
|
2145
|
+
'' as phone,
|
|
2146
|
+
coalesce(e.ZADDRESS, '') as email
|
|
2147
|
+
from ZABCDRECORD r
|
|
2148
|
+
join ZABCDEMAILADDRESS e on e.ZOWNER = r.Z_PK
|
|
2149
|
+
where e.ZADDRESS is not null and e.ZADDRESS != '';`;
|
|
2150
|
+
|
|
2151
|
+
try {
|
|
2152
|
+
const raw = execFileSync("sqlite3", ["-separator", "\t", dbPath, query], {
|
|
2153
|
+
encoding: "utf8",
|
|
2154
|
+
timeout: 10_000,
|
|
2155
|
+
});
|
|
2156
|
+
for (const line of raw.split("\n").filter(Boolean)) {
|
|
2157
|
+
const [id, rawName, phone, email] = line.split("\t");
|
|
2158
|
+
const name = rawName?.trim();
|
|
2159
|
+
if (!id || !name) continue;
|
|
2160
|
+
const key = `${dbPath}:${id}`;
|
|
2161
|
+
const current = contacts.get(key) ?? { name, phones: [], emails: [] };
|
|
2162
|
+
if (phone) current.phones.push(phone.trim());
|
|
2163
|
+
if (email) current.emails.push(email.trim());
|
|
2164
|
+
contacts.set(key, current);
|
|
2165
|
+
}
|
|
2166
|
+
} catch (err) {
|
|
2167
|
+
if (args.debug === true) console.error(`Could not read Contacts DB ${dbPath}:`, safeError(err));
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
return [...contacts.values()].filter((contact) => contact.name);
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
function addressBookDatabasePaths() {
|
|
2175
|
+
const addressBookDir = join(homedir(), "Library", "Application Support", "AddressBook");
|
|
2176
|
+
try {
|
|
2177
|
+
const raw = execFileSync("find", [addressBookDir, "-maxdepth", "4", "-name", "AddressBook-v22.abcddb"], {
|
|
2178
|
+
encoding: "utf8",
|
|
2179
|
+
timeout: 5_000,
|
|
2180
|
+
});
|
|
2181
|
+
return [...new Set(raw.split("\n").map((path) => path.trim()).filter(Boolean))];
|
|
2182
|
+
} catch {
|
|
2183
|
+
return [join(addressBookDir, "AddressBook-v22.abcddb")];
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2104
2187
|
function loadMyCard() {
|
|
2105
2188
|
if (platform() !== "darwin") return null;
|
|
2106
2189
|
const script = `
|