shepherd-onboard 0.1.17 → 0.1.19
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 +49 -5
- 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
|
}
|
|
@@ -1238,12 +1259,20 @@ async function selectChatsInBrowser(chats, opts = {}) {
|
|
|
1238
1259
|
const token = Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
|
|
1239
1260
|
let settled = false;
|
|
1240
1261
|
let server;
|
|
1262
|
+
const sockets = new Set();
|
|
1241
1263
|
|
|
1242
1264
|
return new Promise((resolve, reject) => {
|
|
1265
|
+
const closeSelectorServer = () => {
|
|
1266
|
+
server?.close();
|
|
1267
|
+
server?.closeIdleConnections?.();
|
|
1268
|
+
for (const socket of sockets) socket.destroy();
|
|
1269
|
+
sockets.clear();
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1243
1272
|
const timeout = setTimeout(() => {
|
|
1244
1273
|
if (settled) return;
|
|
1245
1274
|
settled = true;
|
|
1246
|
-
|
|
1275
|
+
closeSelectorServer();
|
|
1247
1276
|
reject(new Error("Messages chat selection timed out."));
|
|
1248
1277
|
}, 20 * 60 * 1000);
|
|
1249
1278
|
|
|
@@ -1271,11 +1300,11 @@ async function selectChatsInBrowser(chats, opts = {}) {
|
|
|
1271
1300
|
return;
|
|
1272
1301
|
}
|
|
1273
1302
|
|
|
1303
|
+
if (!settled) res.once("finish", closeSelectorServer);
|
|
1274
1304
|
sendHtml(res, renderMessagesDonePage(`${selected.length} chat${selected.length === 1 ? "" : "s"} selected.`));
|
|
1275
1305
|
if (!settled) {
|
|
1276
1306
|
settled = true;
|
|
1277
1307
|
clearTimeout(timeout);
|
|
1278
|
-
setTimeout(() => server.close(), 100);
|
|
1279
1308
|
resolve(selected);
|
|
1280
1309
|
}
|
|
1281
1310
|
return;
|
|
@@ -1286,16 +1315,22 @@ async function selectChatsInBrowser(chats, opts = {}) {
|
|
|
1286
1315
|
if (!settled) {
|
|
1287
1316
|
settled = true;
|
|
1288
1317
|
clearTimeout(timeout);
|
|
1289
|
-
|
|
1318
|
+
closeSelectorServer();
|
|
1290
1319
|
reject(err);
|
|
1291
1320
|
}
|
|
1292
1321
|
}
|
|
1293
1322
|
});
|
|
1294
1323
|
|
|
1324
|
+
server.on("connection", (socket) => {
|
|
1325
|
+
sockets.add(socket);
|
|
1326
|
+
socket.once("close", () => sockets.delete(socket));
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1295
1329
|
server.on("error", (err) => {
|
|
1296
1330
|
if (settled) return;
|
|
1297
1331
|
settled = true;
|
|
1298
1332
|
clearTimeout(timeout);
|
|
1333
|
+
closeSelectorServer();
|
|
1299
1334
|
reject(err);
|
|
1300
1335
|
});
|
|
1301
1336
|
|
|
@@ -1305,7 +1340,7 @@ async function selectChatsInBrowser(chats, opts = {}) {
|
|
|
1305
1340
|
if (!port) {
|
|
1306
1341
|
settled = true;
|
|
1307
1342
|
clearTimeout(timeout);
|
|
1308
|
-
|
|
1343
|
+
closeSelectorServer();
|
|
1309
1344
|
reject(new Error("Could not start local Messages selector."));
|
|
1310
1345
|
return;
|
|
1311
1346
|
}
|
|
@@ -1621,6 +1656,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1621
1656
|
const search = document.getElementById("search");
|
|
1622
1657
|
const empty = document.getElementById("empty");
|
|
1623
1658
|
const selected = document.getElementById("selection-count");
|
|
1659
|
+
const form = document.querySelector("form");
|
|
1624
1660
|
const checks = Array.from(document.querySelectorAll('input[name="chatId"]'));
|
|
1625
1661
|
|
|
1626
1662
|
function updateRows() {
|
|
@@ -1642,6 +1678,11 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1642
1678
|
}
|
|
1643
1679
|
|
|
1644
1680
|
search.addEventListener("input", updateRows);
|
|
1681
|
+
document.addEventListener("keydown", (event) => {
|
|
1682
|
+
if (event.key !== "Enter") return;
|
|
1683
|
+
event.preventDefault();
|
|
1684
|
+
form.requestSubmit();
|
|
1685
|
+
});
|
|
1645
1686
|
for (const check of checks) check.addEventListener("change", updateSelected);
|
|
1646
1687
|
updateRows();
|
|
1647
1688
|
updateSelected();
|
|
@@ -1651,6 +1692,7 @@ function renderMessagesSelectorPage(chats, token, error = "") {
|
|
|
1651
1692
|
}
|
|
1652
1693
|
|
|
1653
1694
|
function renderMessagesDonePage(message, isError = false) {
|
|
1695
|
+
const closeScript = isError ? "" : "<script>setTimeout(() => window.close(), 150);</script>";
|
|
1654
1696
|
return `<!doctype html>
|
|
1655
1697
|
<html lang="en">
|
|
1656
1698
|
<head>
|
|
@@ -1671,8 +1713,9 @@ function renderMessagesDonePage(message, isError = false) {
|
|
|
1671
1713
|
<main>
|
|
1672
1714
|
<div class="mark">${isError ? "!" : "OK"}</div>
|
|
1673
1715
|
<h1>${html(message)}</h1>
|
|
1674
|
-
<p>${isError ? "Return to the terminal and retry." : "
|
|
1716
|
+
<p>${isError ? "Return to the terminal and retry." : "Returning to the terminal."}</p>
|
|
1675
1717
|
</main>
|
|
1718
|
+
${closeScript}
|
|
1676
1719
|
</body>
|
|
1677
1720
|
</html>`;
|
|
1678
1721
|
}
|
|
@@ -1739,6 +1782,7 @@ function sendHtml(res, body, status = 200) {
|
|
|
1739
1782
|
res.writeHead(status, {
|
|
1740
1783
|
"Content-Type": "text/html; charset=utf-8",
|
|
1741
1784
|
"Cache-Control": "no-store",
|
|
1785
|
+
"Connection": "close",
|
|
1742
1786
|
});
|
|
1743
1787
|
res.end(body);
|
|
1744
1788
|
}
|