@creativeintelligence/abbie 0.1.0 → 0.1.2
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/dist/cli/commands/context/inject.js +1 -1
- package/dist/cli/commands/context/list.js +1 -1
- package/dist/cli/commands/context/publish.js +1 -1
- package/dist/cli/commands/context/read.js +1 -1
- package/dist/cli/commands/daemon.js +1 -1
- package/dist/cli/commands/history/index.d.ts +13 -2
- package/dist/cli/commands/history/index.d.ts.map +1 -1
- package/dist/cli/commands/history/index.js +2 -2
- package/dist/cli/commands/login.js +2 -2
- package/dist/cli/commands/logout.js +1 -1
- package/dist/cli/commands/preview/index.js +2 -2
- package/dist/cli/commands/preview/list.js +2 -2
- package/dist/cli/commands/preview/status.js +2 -2
- package/dist/cli/commands/preview/stop.js +2 -2
- package/dist/cli/commands/push.js +2 -2
- package/dist/cli/commands/session/list.js +1 -1
- package/dist/cli/commands/session/mark-done.js +1 -1
- package/dist/cli/commands/session/replay.js +1 -1
- package/dist/cli/commands/session/start.js +1 -1
- package/dist/cli/commands/session/stop.js +1 -1
- package/dist/cli/commands/start.d.ts +2 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +17 -12
- package/dist/cli/commands/status.js +2 -2
- package/dist/cli/commands/sync.js +2 -2
- package/dist/cli/commands/windows/list.js +1 -1
- package/dist/lib/active-sessions.js +6 -6
- package/dist/lib/annotations-convex.js +1 -1
- package/dist/lib/events.js +1 -1
- package/dist/lib/provider-auth.d.ts +38 -0
- package/dist/lib/provider-auth.d.ts.map +1 -0
- package/dist/lib/provider-auth.js +193 -0
- package/dist/lib/report.js +1 -1
- package/dist/lib/runner.js +4 -4
- package/extensions/abbie/index.ts +63 -0
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
- package/src/cli/commands/__tests__/daemon.test.ts +1 -1
- package/src/cli/commands/context/inject.ts +1 -1
- package/src/cli/commands/context/list.ts +1 -1
- package/src/cli/commands/context/publish.ts +1 -1
- package/src/cli/commands/context/read.ts +1 -1
- package/src/cli/commands/daemon.ts +1 -1
- package/src/cli/commands/history/index.ts +15 -3
- package/src/cli/commands/login.ts +2 -2
- package/src/cli/commands/logout.ts +1 -1
- package/src/cli/commands/preview/index.ts +2 -2
- package/src/cli/commands/preview/list.ts +2 -2
- package/src/cli/commands/preview/status.ts +2 -2
- package/src/cli/commands/preview/stop.ts +2 -2
- package/src/cli/commands/push.ts +2 -2
- package/src/cli/commands/session/list.ts +1 -1
- package/src/cli/commands/session/mark-done.ts +1 -1
- package/src/cli/commands/session/replay.ts +1 -1
- package/src/cli/commands/session/start.ts +1 -1
- package/src/cli/commands/session/stop.ts +1 -1
- package/src/cli/commands/start.ts +19 -13
- package/src/cli/commands/status.ts +2 -2
- package/src/cli/commands/sync.ts +2 -2
- package/src/cli/commands/windows/list.ts +1 -1
- package/src/lib/active-sessions.ts +6 -6
- package/src/lib/annotations-convex.ts +1 -1
- package/src/lib/events.ts +1 -1
- package/src/lib/provider-auth.ts +258 -0
- package/src/lib/report.ts +1 -1
- package/src/lib/runner.ts +4 -4
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider OAuth Authentication
|
|
3
|
+
*
|
|
4
|
+
* Runs AI provider OAuth flows (Anthropic, OpenAI, Google, GitHub Copilot)
|
|
5
|
+
* using Pi's OAuth libraries, resolved dynamically from Pi's global install.
|
|
6
|
+
*
|
|
7
|
+
* Writes credentials to ~/.pi/agent/auth.json (Pi's auth storage format).
|
|
8
|
+
* This means Pi starts with providers already configured — no /login needed.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { execSync } from "node:child_process";
|
|
14
|
+
import { createInterface } from "node:readline";
|
|
15
|
+
import { exec } from "node:child_process";
|
|
16
|
+
const AUTH_JSON = join(homedir(), ".pi", "agent", "auth.json");
|
|
17
|
+
export function readAuthJson() {
|
|
18
|
+
try {
|
|
19
|
+
if (!existsSync(AUTH_JSON))
|
|
20
|
+
return {};
|
|
21
|
+
return JSON.parse(readFileSync(AUTH_JSON, "utf8"));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function writeAuthJson(data) {
|
|
28
|
+
const dir = join(homedir(), ".pi", "agent");
|
|
29
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
30
|
+
writeFileSync(AUTH_JSON, JSON.stringify(data, null, 2), "utf8");
|
|
31
|
+
chmodSync(AUTH_JSON, 0o600);
|
|
32
|
+
}
|
|
33
|
+
export function getConnectedProviders() {
|
|
34
|
+
const data = readAuthJson();
|
|
35
|
+
return Object.entries(data)
|
|
36
|
+
.filter(([, v]) => v?.type === "oauth" && v.access)
|
|
37
|
+
.map(([k]) => k);
|
|
38
|
+
}
|
|
39
|
+
export function hasAnyProvider() {
|
|
40
|
+
return getConnectedProviders().length > 0;
|
|
41
|
+
}
|
|
42
|
+
export const PROVIDERS = [
|
|
43
|
+
{ id: "anthropic", name: "Anthropic", description: "Claude Pro/Max subscription" },
|
|
44
|
+
{ id: "openai-codex", name: "OpenAI", description: "ChatGPT Plus/Pro subscription" },
|
|
45
|
+
{ id: "google-gemini-cli", name: "Google Gemini", description: "Google Cloud Code Assist" },
|
|
46
|
+
{ id: "github-copilot", name: "GitHub Copilot", description: "Copilot Individual/Business" },
|
|
47
|
+
{ id: "google-antigravity", name: "Google Antigravity", description: "Gemini 3, Claude, GPT via Google Cloud" },
|
|
48
|
+
];
|
|
49
|
+
async function importPiOAuth() {
|
|
50
|
+
// Strategy: find @mariozechner/pi-ai from Pi's global install
|
|
51
|
+
const candidates = [];
|
|
52
|
+
// 1. Try resolving from pi binary location
|
|
53
|
+
try {
|
|
54
|
+
const piPath = execSync("which pi", { encoding: "utf8", timeout: 3000 }).trim();
|
|
55
|
+
if (piPath) {
|
|
56
|
+
const { realpathSync } = await import("node:fs");
|
|
57
|
+
const realPath = realpathSync(piPath);
|
|
58
|
+
// pi binary -> bin/pi.mjs -> package root -> node_modules/@mariozechner/pi-ai
|
|
59
|
+
const pkgRoot = join(realPath, "..", "..");
|
|
60
|
+
candidates.push(join(pkgRoot, "node_modules", "@mariozechner", "pi-ai", "dist", "index.js"));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch { /* not found */ }
|
|
64
|
+
// 2. Try common global paths
|
|
65
|
+
const nodeVersion = process.version.replace("v", "");
|
|
66
|
+
candidates.push(join(homedir(), ".nvm", "versions", "node", `v${nodeVersion}`, "lib", "node_modules", "@mariozechner", "pi-coding-agent", "node_modules", "@mariozechner", "pi-ai", "dist", "index.js"), join("/usr", "local", "lib", "node_modules", "@mariozechner", "pi-coding-agent", "node_modules", "@mariozechner", "pi-ai", "dist", "index.js"));
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
if (existsSync(candidate)) {
|
|
69
|
+
try {
|
|
70
|
+
return await import(candidate);
|
|
71
|
+
}
|
|
72
|
+
catch { /* import failed */ }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// CLI OAuth flow helpers
|
|
79
|
+
// ============================================================================
|
|
80
|
+
function prompt(question) {
|
|
81
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
rl.question(question, (answer) => {
|
|
84
|
+
rl.close();
|
|
85
|
+
resolve(answer.trim());
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function openBrowser(url) {
|
|
90
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
91
|
+
exec(`${cmd} "${url}"`);
|
|
92
|
+
}
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Public API
|
|
95
|
+
// ============================================================================
|
|
96
|
+
/**
|
|
97
|
+
* Run the provider selection + OAuth flow.
|
|
98
|
+
* Returns the provider ID that was connected, or null if cancelled.
|
|
99
|
+
*/
|
|
100
|
+
export async function connectProvider(log) {
|
|
101
|
+
const piOAuth = await importPiOAuth();
|
|
102
|
+
if (!piOAuth) {
|
|
103
|
+
log(" ⚠ could not find Pi's OAuth module");
|
|
104
|
+
log(" connect providers in Pi instead: /login");
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const connected = getConnectedProviders();
|
|
108
|
+
// Show provider list
|
|
109
|
+
log("");
|
|
110
|
+
log(" connect an AI provider");
|
|
111
|
+
log(" ─────────────────────");
|
|
112
|
+
log("");
|
|
113
|
+
const providers = piOAuth.getOAuthProviders();
|
|
114
|
+
for (let i = 0; i < providers.length; i++) {
|
|
115
|
+
const p = providers[i];
|
|
116
|
+
const isConnected = connected.includes(p.id);
|
|
117
|
+
const status = isConnected ? " ✓ connected" : "";
|
|
118
|
+
log(` ${i + 1}. ${p.name}${status}`);
|
|
119
|
+
}
|
|
120
|
+
log(` ${providers.length + 1}. skip for now`);
|
|
121
|
+
log("");
|
|
122
|
+
const choice = await prompt(" select (1-" + (providers.length + 1) + "): ");
|
|
123
|
+
const idx = parseInt(choice, 10) - 1;
|
|
124
|
+
if (isNaN(idx) || idx < 0 || idx >= providers.length) {
|
|
125
|
+
return null; // skip
|
|
126
|
+
}
|
|
127
|
+
const provider = providers[idx];
|
|
128
|
+
log("");
|
|
129
|
+
log(` connecting ${provider.name}...`);
|
|
130
|
+
log("");
|
|
131
|
+
try {
|
|
132
|
+
const credentials = await provider.login({
|
|
133
|
+
onAuth: (info) => {
|
|
134
|
+
log(` open this URL to authenticate:`);
|
|
135
|
+
log(` ${info.url}`);
|
|
136
|
+
if (info.instructions) {
|
|
137
|
+
log(` ${info.instructions}`);
|
|
138
|
+
}
|
|
139
|
+
log("");
|
|
140
|
+
openBrowser(info.url);
|
|
141
|
+
},
|
|
142
|
+
onPrompt: async (promptInfo) => {
|
|
143
|
+
const answer = await prompt(` ${promptInfo.message} `);
|
|
144
|
+
if (!answer && !promptInfo.allowEmpty) {
|
|
145
|
+
throw new Error("Input required");
|
|
146
|
+
}
|
|
147
|
+
return answer;
|
|
148
|
+
},
|
|
149
|
+
onProgress: (message) => {
|
|
150
|
+
log(` ${message}`);
|
|
151
|
+
},
|
|
152
|
+
onManualCodeInput: async () => {
|
|
153
|
+
return await prompt(" paste the code or URL: ");
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
// Write to auth.json
|
|
157
|
+
const data = readAuthJson();
|
|
158
|
+
data[provider.id] = { type: "oauth", ...credentials };
|
|
159
|
+
writeAuthJson(data);
|
|
160
|
+
log(` ✓ ${provider.name} connected`);
|
|
161
|
+
return provider.id;
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
165
|
+
if (msg.includes("cancelled")) {
|
|
166
|
+
log(" cancelled");
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
log(` ✗ connection failed: ${msg}`);
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check providers and optionally prompt for connection.
|
|
176
|
+
* Used in the setup wizard.
|
|
177
|
+
*/
|
|
178
|
+
export async function ensureProviderConnected(log) {
|
|
179
|
+
const connected = getConnectedProviders();
|
|
180
|
+
if (connected.length > 0) {
|
|
181
|
+
const names = connected.map((id) => {
|
|
182
|
+
const info = PROVIDERS.find((p) => p.id === id);
|
|
183
|
+
return info?.name ?? id;
|
|
184
|
+
});
|
|
185
|
+
log(` [4/4] providers: ${names.join(", ")} ✓`);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
log(" [4/4] connect an AI provider");
|
|
189
|
+
log(" (uses your existing subscription — no API keys needed)");
|
|
190
|
+
log("");
|
|
191
|
+
const result = await connectProvider(log);
|
|
192
|
+
return result !== null;
|
|
193
|
+
}
|
package/dist/lib/report.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { spawnSync } from "node:child_process";
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
16
|
-
import { api, getHttpClient } from "@
|
|
16
|
+
import { api, getHttpClient } from "@creativeintelligence/sdk/convex";
|
|
17
17
|
import { getDeviceId } from "./device.js";
|
|
18
18
|
import { getDefaultSlackWorkspace } from "./slack-workspace.js";
|
|
19
19
|
// ============================================================================
|
package/dist/lib/runner.js
CHANGED
|
@@ -582,7 +582,7 @@ export async function runAgent(config) {
|
|
|
582
582
|
// Emit session:started progress event (best-effort, non-blocking)
|
|
583
583
|
const traceIdForLifecycle = process.env.AGENTS_TRACE_ID;
|
|
584
584
|
if (traceIdForLifecycle) {
|
|
585
|
-
import("@
|
|
585
|
+
import("@creativeintelligence/sdk/convex")
|
|
586
586
|
.then(({ api: cbApi, getHttpClient }) => getHttpClient().mutation(cbApi.contextBus.publish, {
|
|
587
587
|
traceId: traceIdForLifecycle,
|
|
588
588
|
sessionId: config.sessionId,
|
|
@@ -922,7 +922,7 @@ export async function runAgent(config) {
|
|
|
922
922
|
.map((m) => m.replace(/^.*?([^\s`'"]+\.\w{1,6}).*$/, "$1"))
|
|
923
923
|
.slice(0, 20);
|
|
924
924
|
const hasOutputContract = /\{[\s\S]*"status"[\s\S]*"confidence"[\s\S]*\}/.test(outputForContext.slice(-500));
|
|
925
|
-
const { api: cbApi, getHttpClient } = await import("@
|
|
925
|
+
const { api: cbApi, getHttpClient } = await import("@creativeintelligence/sdk/convex");
|
|
926
926
|
await getHttpClient().mutation(cbApi.contextBus.publish, {
|
|
927
927
|
traceId,
|
|
928
928
|
sessionId: config.sessionId,
|
|
@@ -954,7 +954,7 @@ export async function runAgent(config) {
|
|
|
954
954
|
// Update Convex session with terminal status + metadata (best-effort)
|
|
955
955
|
if (traceId) {
|
|
956
956
|
try {
|
|
957
|
-
const { getHttpClient, api } = await import("@
|
|
957
|
+
const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
|
|
958
958
|
const client = getHttpClient();
|
|
959
959
|
// Read output excerpt for metadata
|
|
960
960
|
let outputExcerpt = "";
|
|
@@ -994,7 +994,7 @@ export async function runAgent(config) {
|
|
|
994
994
|
// Emit session lifecycle event (completed/failed/stopped)
|
|
995
995
|
if (traceId) {
|
|
996
996
|
try {
|
|
997
|
-
const { api: cbApi, getHttpClient } = await import("@
|
|
997
|
+
const { api: cbApi, getHttpClient } = await import("@creativeintelligence/sdk/convex");
|
|
998
998
|
await getHttpClient().mutation(cbApi.contextBus.publish, {
|
|
999
999
|
traceId,
|
|
1000
1000
|
sessionId: config.sessionId,
|
|
@@ -535,6 +535,69 @@ export default function abbieExtension(pi: ExtensionAPI) {
|
|
|
535
535
|
},
|
|
536
536
|
});
|
|
537
537
|
|
|
538
|
+
// ===========================================================================
|
|
539
|
+
// Provider auth sync: sync auth.json → Convex via bridge on startup
|
|
540
|
+
// ===========================================================================
|
|
541
|
+
|
|
542
|
+
async function getConnectedProviders(): Promise<string[]> {
|
|
543
|
+
const { existsSync, readFileSync } = await import("node:fs");
|
|
544
|
+
const { join } = await import("node:path");
|
|
545
|
+
const { homedir } = await import("node:os");
|
|
546
|
+
const authPath = join(homedir(), ".pi", "agent", "auth.json");
|
|
547
|
+
if (!existsSync(authPath)) return [];
|
|
548
|
+
try {
|
|
549
|
+
const data = JSON.parse(readFileSync(authPath, "utf8"));
|
|
550
|
+
return Object.entries(data)
|
|
551
|
+
.filter(([, v]: [string, any]) => v?.type === "oauth" && v.access)
|
|
552
|
+
.map(([k]) => k);
|
|
553
|
+
} catch { return []; }
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// On session start: check if any providers are connected
|
|
557
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
558
|
+
const providers = await getConnectedProviders();
|
|
559
|
+
if (providers.length === 0 && ctx.hasUI) {
|
|
560
|
+
ctx.ui.notify(
|
|
561
|
+
"No AI provider connected. Use /login to connect Anthropic, OpenAI, Google, or GitHub Copilot.",
|
|
562
|
+
"warning",
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// Sync provider status to Convex after bridge is up
|
|
568
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
569
|
+
// After first agent interaction, sync provider info to Convex
|
|
570
|
+
// This runs once per session via a simple flag
|
|
571
|
+
if ((pi as any).__providersSynced) return;
|
|
572
|
+
(pi as any).__providersSynced = true;
|
|
573
|
+
|
|
574
|
+
const providers = await getConnectedProviders();
|
|
575
|
+
if (providers.length === 0) return;
|
|
576
|
+
|
|
577
|
+
// Sync via bridge if configured
|
|
578
|
+
try {
|
|
579
|
+
const { existsSync, readFileSync } = await import("node:fs");
|
|
580
|
+
const { join } = await import("node:path");
|
|
581
|
+
const { homedir } = await import("node:os");
|
|
582
|
+
const configPath = join(homedir(), ".abbie", "config.json");
|
|
583
|
+
if (!existsSync(configPath)) return;
|
|
584
|
+
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
585
|
+
const convexUrl = config.convexUrl || process.env.NEXT_PUBLIC_CONVEX_URL;
|
|
586
|
+
const bridgeSecret = config.bridgeSecret || process.env.ABBIE_BRIDGE_SECRET;
|
|
587
|
+
if (!convexUrl || !bridgeSecret) return;
|
|
588
|
+
|
|
589
|
+
// POST provider list to bridge (no credentials, just IDs)
|
|
590
|
+
await fetch(`${convexUrl.replace('.convex.cloud', '.convex.site')}/bridge/providers/sync`, {
|
|
591
|
+
method: "POST",
|
|
592
|
+
headers: {
|
|
593
|
+
"Content-Type": "application/json",
|
|
594
|
+
"Authorization": `Bearer ${bridgeSecret}`,
|
|
595
|
+
},
|
|
596
|
+
body: JSON.stringify({ providers }),
|
|
597
|
+
}).catch(() => {}); // best-effort
|
|
598
|
+
} catch { /* non-fatal */ }
|
|
599
|
+
});
|
|
600
|
+
|
|
538
601
|
// ===========================================================================
|
|
539
602
|
// Auto-bridge: start bridge daemon on Pi startup
|
|
540
603
|
// ===========================================================================
|
package/oclif.manifest.json
CHANGED
|
@@ -11672,5 +11672,5 @@
|
|
|
11672
11672
|
"summary": "Watch workspace windows (tmux panes) and stream updates"
|
|
11673
11673
|
}
|
|
11674
11674
|
},
|
|
11675
|
-
"version": "0.1.
|
|
11675
|
+
"version": "0.1.2"
|
|
11676
11676
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@creativeintelligence/abbie",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Abbie — agent orchestration CLI",
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"cli": "bun run ./bin/dev.js"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@
|
|
85
|
-
"@
|
|
84
|
+
"@creativeintelligence/backend": "^0.1.0",
|
|
85
|
+
"@creativeintelligence/sdk": "^0.1.1",
|
|
86
86
|
"@oclif/core": "^4",
|
|
87
87
|
"@oclif/plugin-help": "^6",
|
|
88
88
|
"@oclif/plugin-not-found": "^3",
|
|
@@ -5,7 +5,7 @@ type ExecuteQueuedCommand = (command: string, args: unknown) => Promise<ExecuteQ
|
|
|
5
5
|
|
|
6
6
|
const startSpy = vi.fn(async () => ({ session_id: "sess_test_123" }));
|
|
7
7
|
|
|
8
|
-
vi.mock("@
|
|
8
|
+
vi.mock("@creativeintelligence/sdk/convex", () => ({
|
|
9
9
|
api: {},
|
|
10
10
|
closeClients: vi.fn(),
|
|
11
11
|
getHttpClient: vi.fn(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getHttpClient, api, type ContextBusEntryType } from "@
|
|
1
|
+
import { getHttpClient, api, type ContextBusEntryType } from "@creativeintelligence/sdk/convex";
|
|
2
2
|
import { Flags } from "@oclif/core";
|
|
3
3
|
import { BaseCommand } from "../../base-command.js";
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getHttpClient, api, type ContextBusEntryType } from "@
|
|
1
|
+
import { getHttpClient, api, type ContextBusEntryType } from "@creativeintelligence/sdk/convex";
|
|
2
2
|
import { Flags } from "@oclif/core";
|
|
3
3
|
import { BaseCommand } from "../../base-command.js";
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { api, type ContextBusEntryType, getHttpClient } from "@
|
|
1
|
+
import { api, type ContextBusEntryType, getHttpClient } from "@creativeintelligence/sdk/convex";
|
|
2
2
|
import { Args, Flags } from "@oclif/core";
|
|
3
3
|
import { BaseCommand } from "../../base-command.js";
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getHttpClient, api, type ContextBusEntryType } from "@
|
|
1
|
+
import { getHttpClient, api, type ContextBusEntryType } from "@creativeintelligence/sdk/convex";
|
|
2
2
|
import { Flags } from "@oclif/core";
|
|
3
3
|
import { BaseCommand } from "../../base-command.js";
|
|
4
4
|
|
|
@@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { homedir, hostname } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
-
import { api, closeClients, getHttpClient, isConvexConfigured, subscribe } from "@
|
|
6
|
+
import { api, closeClients, getHttpClient, isConvexConfigured, subscribe } from "@creativeintelligence/sdk/convex";
|
|
7
7
|
import { Flags } from "@oclif/core";
|
|
8
8
|
import type { FunctionReturnType } from "convex/server";
|
|
9
9
|
import { type ActiveSession, getActiveSessionManager } from "../../lib/active-sessions.js";
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import { api,
|
|
1
|
+
import { api, getHttpClient } from "@creativeintelligence/sdk/convex";
|
|
2
2
|
import { Flags } from "@oclif/core";
|
|
3
3
|
import { BaseCommand } from "../../base-command.js";
|
|
4
4
|
|
|
5
|
-
type HistoryEvent =
|
|
5
|
+
type HistoryEvent = {
|
|
6
|
+
_id: string;
|
|
7
|
+
_creationTime: number;
|
|
8
|
+
timestamp?: number;
|
|
9
|
+
action?: string;
|
|
10
|
+
task?: string;
|
|
11
|
+
confidence?: number;
|
|
12
|
+
project?: string;
|
|
13
|
+
deviceId?: string;
|
|
14
|
+
agent?: string;
|
|
15
|
+
traceId?: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
};
|
|
6
18
|
|
|
7
19
|
interface HistoryResult {
|
|
8
20
|
success: boolean;
|
|
@@ -180,7 +192,7 @@ SYMMETRY: abbie report writes → abbie history reads`;
|
|
|
180
192
|
let filtered = events;
|
|
181
193
|
|
|
182
194
|
if (sinceMs !== undefined) {
|
|
183
|
-
filtered = filtered.filter((e) => e.timestamp >= sinceMs);
|
|
195
|
+
filtered = filtered.filter((e) => (e.timestamp ?? 0) >= sinceMs);
|
|
184
196
|
}
|
|
185
197
|
if (flags["trace-id"]) {
|
|
186
198
|
filtered = filtered.filter((e) => e.traceId === flags["trace-id"]);
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { Flags } from "@oclif/core";
|
|
14
|
-
import { getDeviceInfo, getStoredClerkId, saveClerkId, getWebUrl } from "@
|
|
15
|
-
import { api, getHttpClient, isConvexConfigured } from "@
|
|
14
|
+
import { getDeviceInfo, getStoredClerkId, saveClerkId, getWebUrl } from "@creativeintelligence/sdk/config";
|
|
15
|
+
import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
|
|
16
16
|
import { BaseCommand } from "../base-command.js";
|
|
17
17
|
|
|
18
18
|
const POLL_INTERVAL_MS = 1_500;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* abbie logout — clear stored credentials from this device.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { clearClerkId, getStoredClerkId, getDeviceInfo } from "@
|
|
5
|
+
import { clearClerkId, getStoredClerkId, getDeviceInfo } from "@creativeintelligence/sdk/config";
|
|
6
6
|
import { BaseCommand } from "../base-command.js";
|
|
7
7
|
|
|
8
8
|
type LogoutResult = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { basename, resolve } from "node:path";
|
|
3
|
-
import { getStoredClerkId } from "@
|
|
4
|
-
import { api, getHttpClient, isConvexConfigured } from "@
|
|
3
|
+
import { getStoredClerkId } from "@creativeintelligence/sdk/config";
|
|
4
|
+
import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
|
|
5
5
|
import { Args, Flags } from "@oclif/core";
|
|
6
6
|
import { detectProject } from "../../../lib/preview/detect.js";
|
|
7
7
|
import { BaseCommand } from "../../base-command.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getStoredClerkId } from "@
|
|
2
|
-
import { api, getHttpClient, isConvexConfigured } from "@
|
|
1
|
+
import { getStoredClerkId } from "@creativeintelligence/sdk/config";
|
|
2
|
+
import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
|
|
3
3
|
import { Flags } from "@oclif/core";
|
|
4
4
|
import { BaseCommand } from "../../base-command.js";
|
|
5
5
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { basename, resolve } from "node:path";
|
|
2
|
-
import { getStoredClerkId } from "@
|
|
3
|
-
import { api, getHttpClient, isConvexConfigured } from "@
|
|
2
|
+
import { getStoredClerkId } from "@creativeintelligence/sdk/config";
|
|
3
|
+
import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
|
|
4
4
|
import { Args, Flags } from "@oclif/core";
|
|
5
5
|
import { BaseCommand } from "../../base-command.js";
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { basename, resolve } from "node:path";
|
|
2
|
-
import { getStoredClerkId } from "@
|
|
3
|
-
import { api, getHttpClient, isConvexConfigured } from "@
|
|
2
|
+
import { getStoredClerkId } from "@creativeintelligence/sdk/config";
|
|
3
|
+
import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
|
|
4
4
|
import { Args, Flags } from "@oclif/core";
|
|
5
5
|
import { BaseCommand } from "../../base-command.js";
|
|
6
6
|
|
package/src/cli/commands/push.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { Flags } from "@oclif/core";
|
|
10
|
-
import { api, getHttpClient, isConvexConfigured } from "@
|
|
11
|
-
import { getStoredClerkId } from "@
|
|
10
|
+
import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
|
|
11
|
+
import { getStoredClerkId } from "@creativeintelligence/sdk/config";
|
|
12
12
|
import {
|
|
13
13
|
ContentSyncRunner,
|
|
14
14
|
type ContentPushResult,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { getHttpClient, api, type Session } from "@
|
|
2
|
+
import { getHttpClient, api, type Session } from "@creativeintelligence/sdk/convex";
|
|
3
3
|
import { Flags } from "@oclif/core";
|
|
4
4
|
import { type ActiveSession, getActiveSessionManager } from "../../../lib/active-sessions.js";
|
|
5
5
|
import { getDeviceId } from "../../../lib/device.js";
|
|
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { getHttpClient, api } from "@
|
|
5
|
+
import { getHttpClient, api } from "@creativeintelligence/sdk/convex";
|
|
6
6
|
import { Args } from "@oclif/core";
|
|
7
7
|
import { getActiveSessionManager } from "../../../lib/active-sessions.js";
|
|
8
8
|
import { createEvent, EventStore } from "../../../lib/events.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
-
import { api, getHttpClient } from "@
|
|
2
|
+
import { api, getHttpClient } from "@creativeintelligence/sdk/convex";
|
|
3
3
|
import { Args, Flags } from "@oclif/core";
|
|
4
4
|
import { type ActiveSession, getActiveSessionManager } from "../../../lib/active-sessions.js";
|
|
5
5
|
import { type AgentSession, AgentSessionMiner } from "../../../lib/agent-sessions.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
|
-
import { api, getHttpClient } from "@
|
|
3
|
+
import { api, getHttpClient } from "@creativeintelligence/sdk/convex";
|
|
4
4
|
import { Flags } from "@oclif/core";
|
|
5
5
|
import { type AgentType, getActiveSessionManager } from "../../../lib/active-sessions.js";
|
|
6
6
|
import { telemetry } from "../../../lib/analytics.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getHttpClient, api } from "@
|
|
1
|
+
import { getHttpClient, api } from "@creativeintelligence/sdk/convex";
|
|
2
2
|
import { Args, Flags } from "@oclif/core";
|
|
3
3
|
import { getActiveSessionManager } from "../../../lib/active-sessions.js";
|
|
4
4
|
import { BaseCommand } from "../../base-command.js";
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* 1. Check Pi → not found → auto-install
|
|
6
6
|
* 2. Install Abbie Pi extension
|
|
7
7
|
* 3. Open browser for Clerk auth
|
|
8
|
-
* 4.
|
|
8
|
+
* 4. Connect AI provider (OAuth — uses your subscription)
|
|
9
|
+
* 5. Launch Pi (extension auto-starts bridge)
|
|
9
10
|
*
|
|
10
11
|
* Subsequent runs:
|
|
11
12
|
* Launch Pi directly (extension handles bridge)
|
|
@@ -18,6 +19,7 @@ import { fileURLToPath } from "node:url";
|
|
|
18
19
|
import { spawnSync, spawn, execSync } from "node:child_process";
|
|
19
20
|
import { Flags } from "@oclif/core";
|
|
20
21
|
import { BaseCommand } from "../base-command.js";
|
|
22
|
+
import { hasAnyProvider, ensureProviderConnected, getConnectedProviders } from "../../lib/provider-auth.js";
|
|
21
23
|
|
|
22
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
25
|
const __dirname = dirname(__filename);
|
|
@@ -96,18 +98,21 @@ After setup, just run \`abbie\` to start working.`;
|
|
|
96
98
|
|
|
97
99
|
// --status: show state and exit
|
|
98
100
|
if (flags.status) {
|
|
101
|
+
const providers = getConnectedProviders();
|
|
99
102
|
this.log("");
|
|
100
103
|
this.log(" abbie status");
|
|
101
104
|
this.log("");
|
|
102
105
|
this.log(` pi: ${pi.installed ? `✓ ${pi.version}` : "✗ not installed"}`);
|
|
103
106
|
this.log(` extension: ${extOk ? "✓ installed" : "✗ not installed"}`);
|
|
104
107
|
this.log(` auth: ${configured ? "✓ configured" : "✗ not configured"}`);
|
|
108
|
+
this.log(` providers: ${providers.length > 0 ? `✓ ${providers.join(", ")}` : "✗ none connected"}`);
|
|
105
109
|
this.log("");
|
|
106
|
-
return { pi: pi.installed, extension: extOk, auth: configured };
|
|
110
|
+
return { pi: pi.installed, extension: extOk, auth: configured, providers };
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
// Setup needed?
|
|
110
|
-
const
|
|
114
|
+
const providerOk = hasAnyProvider();
|
|
115
|
+
const needsSetup = flags.setup || !pi.installed || !configured || !extOk || !providerOk;
|
|
111
116
|
|
|
112
117
|
if (needsSetup) {
|
|
113
118
|
await this.runSetup(pi, configured, extOk);
|
|
@@ -152,7 +157,7 @@ After setup, just run \`abbie\` to start working.`;
|
|
|
152
157
|
|
|
153
158
|
// Step 1: Install Pi
|
|
154
159
|
if (!pi.installed) {
|
|
155
|
-
this.log(" [1/
|
|
160
|
+
this.log(" [1/4] installing pi...");
|
|
156
161
|
this.log(` npm install -g ${PI_PACKAGE}`);
|
|
157
162
|
this.log("");
|
|
158
163
|
|
|
@@ -178,13 +183,13 @@ After setup, just run \`abbie\` to start working.`;
|
|
|
178
183
|
this.error("Pi is required to continue.");
|
|
179
184
|
}
|
|
180
185
|
} else {
|
|
181
|
-
this.log(` [1/
|
|
186
|
+
this.log(` [1/4] pi ${pi.version} ✓`);
|
|
182
187
|
}
|
|
183
188
|
this.log("");
|
|
184
189
|
|
|
185
190
|
// Step 2: Install Abbie extension
|
|
186
191
|
if (!extOk) {
|
|
187
|
-
this.log(" [2/
|
|
192
|
+
this.log(" [2/4] installing abbie extension...");
|
|
188
193
|
|
|
189
194
|
// Find the bundled extension
|
|
190
195
|
const extSource = this.findExtensionSource();
|
|
@@ -197,13 +202,13 @@ After setup, just run \`abbie\` to start working.`;
|
|
|
197
202
|
this.logWarn("this is non-fatal; you can install it manually later");
|
|
198
203
|
}
|
|
199
204
|
} else {
|
|
200
|
-
this.log(" [2/
|
|
205
|
+
this.log(" [2/4] extension ✓");
|
|
201
206
|
}
|
|
202
207
|
this.log("");
|
|
203
208
|
|
|
204
209
|
// Step 3: Authenticate
|
|
205
210
|
if (!configured) {
|
|
206
|
-
this.log(" [3/
|
|
211
|
+
this.log(" [3/4] authenticating...");
|
|
207
212
|
this.log(" opening browser for login...");
|
|
208
213
|
this.log("");
|
|
209
214
|
|
|
@@ -214,14 +219,15 @@ After setup, just run \`abbie\` to start working.`;
|
|
|
214
219
|
this.logWarn("login failed or was cancelled — you can run `abbie login` later");
|
|
215
220
|
}
|
|
216
221
|
} else {
|
|
217
|
-
this.log(" [3/
|
|
222
|
+
this.log(" [3/4] authenticated ✓");
|
|
218
223
|
}
|
|
219
|
-
|
|
220
224
|
this.log("");
|
|
221
|
-
|
|
225
|
+
|
|
226
|
+
// Step 4: Connect AI provider
|
|
227
|
+
await ensureProviderConnected((msg) => this.log(msg));
|
|
228
|
+
|
|
222
229
|
this.log("");
|
|
223
|
-
this.log("
|
|
224
|
-
this.log(" (Anthropic, OpenAI, Google, or GitHub Copilot)");
|
|
230
|
+
this.log(" setup complete ✓");
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
/**
|