@opennous/cli 0.13.0 → 0.14.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/package.json +1 -1
- package/src/backfill.js +3 -0
- package/src/index.js +37 -11
package/package.json
CHANGED
package/src/backfill.js
CHANGED
|
@@ -97,6 +97,9 @@ function runCommand({ cmd, args, input }, cwd) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/** Nothing to read in this window. Pure. */
|
|
101
|
+
export const emptyEstimate = (counts) => !Object.values(counts ?? {}).some((n) => n > 0);
|
|
102
|
+
|
|
100
103
|
/** One line on how far the job is, or null when there is nothing to say yet. Pure. */
|
|
101
104
|
export function progressLine(reading, answered) {
|
|
102
105
|
if (reading?.finished) return "Wrapping up: last summaries for your accounts.";
|
package/src/index.js
CHANGED
|
@@ -110,9 +110,9 @@ function writeConfig(cfg) {
|
|
|
110
110
|
/** A key is there to use: from the environment, or saved by a sign-in. */
|
|
111
111
|
const hasKey = () => !!(process.env.NOUS_API_KEY || readConfig().apiKey);
|
|
112
112
|
|
|
113
|
-
function apiClient() {
|
|
113
|
+
function apiClient(key) {
|
|
114
114
|
const cfg = readConfig();
|
|
115
|
-
const apiKey = process.env.NOUS_API_KEY || cfg.apiKey;
|
|
115
|
+
const apiKey = key || process.env.NOUS_API_KEY || cfg.apiKey;
|
|
116
116
|
const apiUrl = process.env.NOUS_API_URL || cfg.apiUrl || "https://api.opennous.cloud";
|
|
117
117
|
|
|
118
118
|
if (!apiKey) {
|
|
@@ -288,14 +288,18 @@ async function browserLogin(url, opts = {}) {
|
|
|
288
288
|
} catch { continue; }
|
|
289
289
|
|
|
290
290
|
if (j.status === "approved" && j.api_key) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
291
|
+
// `save: false` is one command's key: the sign-in on this machine stays what it was.
|
|
292
|
+
if (opts.save !== false) {
|
|
293
|
+
const cfg = readConfig();
|
|
294
|
+
cfg.apiKey = j.api_key;
|
|
295
|
+
cfg.apiUrl = apiUrl;
|
|
296
|
+
writeConfig(cfg);
|
|
297
|
+
}
|
|
295
298
|
const who = await fetchWhoami(apiUrl, j.api_key);
|
|
296
299
|
console.log(`\n\n ${green("✓")} ${bold("Signed in")}${who ? ` as ${identityLine(who)}` : ""}`);
|
|
297
|
-
console.log(` ${dim("Key saved to ~/.nous/config.json")}
|
|
298
|
-
|
|
300
|
+
if (opts.save !== false) console.log(` ${dim("Key saved to ~/.nous/config.json")}`);
|
|
301
|
+
console.log("");
|
|
302
|
+
return j.api_key;
|
|
299
303
|
}
|
|
300
304
|
if (j.status === "denied") { console.log("\nAuthorization was denied."); process.exit(1); }
|
|
301
305
|
if (j.status === "expired") { console.log("\nThis sign-in expired. Run it again."); process.exit(1); }
|
|
@@ -546,10 +550,11 @@ program
|
|
|
546
550
|
.description("Read your email and meeting history on your own Claude or ChatGPT plan")
|
|
547
551
|
.option("--agent <agent>", "claude or codex (default: whichever you're signed in to)")
|
|
548
552
|
.option("--days <days>", "how many days back to read, 1 to 730", "180")
|
|
553
|
+
.option("--workspace <id>", "the workspace to read; the app's command fills this in")
|
|
549
554
|
.option("--yes", "start without asking")
|
|
550
555
|
.option("--url <url>", "API base URL (default: https://api.opennous.cloud)")
|
|
551
556
|
.action(async (opts) => {
|
|
552
|
-
const { detectAgents, runBackfill, liveRun, RERUN, NO_AGENT } = await import("./backfill.js");
|
|
557
|
+
const { detectAgents, runBackfill, liveRun, emptyEstimate, RERUN, NO_AGENT } = await import("./backfill.js");
|
|
553
558
|
const days = Number(opts.days);
|
|
554
559
|
if (!Number.isInteger(days) || days < 1 || days > 730) {
|
|
555
560
|
console.error("--days must be a whole number from 1 to 730.");
|
|
@@ -563,9 +568,30 @@ program
|
|
|
563
568
|
}
|
|
564
569
|
// Asked after the check above, so nobody signs in to be told they can't run this.
|
|
565
570
|
if (!hasKey()) await browserLogin(opts.url);
|
|
566
|
-
|
|
571
|
+
// A key saved here may be for another workspace, so say which one this is, and never read the
|
|
572
|
+
// wrong one: the app's command names its workspace, and a key for any other is not used.
|
|
573
|
+
const cfg = readConfig();
|
|
574
|
+
const whoamiUrl = opts.url || process.env.NOUS_API_URL || cfg.apiUrl || "https://api.opennous.cloud";
|
|
575
|
+
let key = process.env.NOUS_API_KEY || cfg.apiKey;
|
|
576
|
+
let who = await fetchWhoami(whoamiUrl, key);
|
|
577
|
+
if (opts.workspace && who?.workspace?.id !== opts.workspace) {
|
|
578
|
+
console.log(`This machine is signed in to ${who?.workspace?.name ?? "another workspace"}. Sign in to the workspace this backfill is for.`);
|
|
579
|
+
key = await browserLogin(opts.url, { save: false });
|
|
580
|
+
who = await fetchWhoami(whoamiUrl, key);
|
|
581
|
+
if (who?.workspace?.id !== opts.workspace) {
|
|
582
|
+
console.error(`That sign-in is for ${who?.workspace?.name ?? "another workspace"}. Open the right workspace in the app, then run this again.`);
|
|
583
|
+
process.exit(1);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const workspace = who?.workspace?.name ?? "your workspace";
|
|
587
|
+
const api = apiClient(key);
|
|
567
588
|
const est = await api.post("/v2/backfill/own/estimate", { days });
|
|
568
|
-
|
|
589
|
+
if (emptyEstimate(est.counts)) {
|
|
590
|
+
console.log(`\nNothing to read in ${workspace} for the last ${days} days. If your sync is still running, try again when it's done.`);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
console.log(`\nWorkspace: ${workspace}`);
|
|
594
|
+
console.log(`Last ${days} days: about ${est.counts.email_thread} email threads, ${est.counts.meeting} meetings, ${est.counts.call} calls.`);
|
|
569
595
|
console.log(`They're filtered first, then read on your ${agent} plan. None of it uses your OpenNous credits.`);
|
|
570
596
|
console.log("You can stop at any time and run this again: it carries on where it stopped.\n");
|
|
571
597
|
if (!opts.yes) {
|