@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.
Files changed (66) hide show
  1. package/dist/cli/commands/context/inject.js +1 -1
  2. package/dist/cli/commands/context/list.js +1 -1
  3. package/dist/cli/commands/context/publish.js +1 -1
  4. package/dist/cli/commands/context/read.js +1 -1
  5. package/dist/cli/commands/daemon.js +1 -1
  6. package/dist/cli/commands/history/index.d.ts +13 -2
  7. package/dist/cli/commands/history/index.d.ts.map +1 -1
  8. package/dist/cli/commands/history/index.js +2 -2
  9. package/dist/cli/commands/login.js +2 -2
  10. package/dist/cli/commands/logout.js +1 -1
  11. package/dist/cli/commands/preview/index.js +2 -2
  12. package/dist/cli/commands/preview/list.js +2 -2
  13. package/dist/cli/commands/preview/status.js +2 -2
  14. package/dist/cli/commands/preview/stop.js +2 -2
  15. package/dist/cli/commands/push.js +2 -2
  16. package/dist/cli/commands/session/list.js +1 -1
  17. package/dist/cli/commands/session/mark-done.js +1 -1
  18. package/dist/cli/commands/session/replay.js +1 -1
  19. package/dist/cli/commands/session/start.js +1 -1
  20. package/dist/cli/commands/session/stop.js +1 -1
  21. package/dist/cli/commands/start.d.ts +2 -1
  22. package/dist/cli/commands/start.d.ts.map +1 -1
  23. package/dist/cli/commands/start.js +17 -12
  24. package/dist/cli/commands/status.js +2 -2
  25. package/dist/cli/commands/sync.js +2 -2
  26. package/dist/cli/commands/windows/list.js +1 -1
  27. package/dist/lib/active-sessions.js +6 -6
  28. package/dist/lib/annotations-convex.js +1 -1
  29. package/dist/lib/events.js +1 -1
  30. package/dist/lib/provider-auth.d.ts +38 -0
  31. package/dist/lib/provider-auth.d.ts.map +1 -0
  32. package/dist/lib/provider-auth.js +193 -0
  33. package/dist/lib/report.js +1 -1
  34. package/dist/lib/runner.js +4 -4
  35. package/extensions/abbie/index.ts +63 -0
  36. package/oclif.manifest.json +1 -1
  37. package/package.json +3 -3
  38. package/src/cli/commands/__tests__/daemon.test.ts +1 -1
  39. package/src/cli/commands/context/inject.ts +1 -1
  40. package/src/cli/commands/context/list.ts +1 -1
  41. package/src/cli/commands/context/publish.ts +1 -1
  42. package/src/cli/commands/context/read.ts +1 -1
  43. package/src/cli/commands/daemon.ts +1 -1
  44. package/src/cli/commands/history/index.ts +15 -3
  45. package/src/cli/commands/login.ts +2 -2
  46. package/src/cli/commands/logout.ts +1 -1
  47. package/src/cli/commands/preview/index.ts +2 -2
  48. package/src/cli/commands/preview/list.ts +2 -2
  49. package/src/cli/commands/preview/status.ts +2 -2
  50. package/src/cli/commands/preview/stop.ts +2 -2
  51. package/src/cli/commands/push.ts +2 -2
  52. package/src/cli/commands/session/list.ts +1 -1
  53. package/src/cli/commands/session/mark-done.ts +1 -1
  54. package/src/cli/commands/session/replay.ts +1 -1
  55. package/src/cli/commands/session/start.ts +1 -1
  56. package/src/cli/commands/session/stop.ts +1 -1
  57. package/src/cli/commands/start.ts +19 -13
  58. package/src/cli/commands/status.ts +2 -2
  59. package/src/cli/commands/sync.ts +2 -2
  60. package/src/cli/commands/windows/list.ts +1 -1
  61. package/src/lib/active-sessions.ts +6 -6
  62. package/src/lib/annotations-convex.ts +1 -1
  63. package/src/lib/events.ts +1 -1
  64. package/src/lib/provider-auth.ts +258 -0
  65. package/src/lib/report.ts +1 -1
  66. package/src/lib/runner.ts +4 -4
@@ -13,8 +13,8 @@ import { existsSync, readFileSync, statSync, readdirSync } from "node:fs";
13
13
  import { join } from "node:path";
14
14
  import { homedir } from "node:os";
15
15
  import { Flags } from "@oclif/core";
16
- import { isConvexConfigured } from "@abbie/sdk/convex";
17
- import { getStoredClerkId } from "@abbie/sdk/config";
16
+ import { isConvexConfigured } from "@creativeintelligence/sdk/convex";
17
+ import { getStoredClerkId } from "@creativeintelligence/sdk/config";
18
18
  import { getAdapter, getSyncDir, type SidecarManifest, type ConfigSyncState } from "../../lib/config-sync/index.js";
19
19
  import { getLatestBackup } from "../../lib/config-sync/writer.js";
20
20
  import type { ContentSyncState, ContentManifest } from "../../lib/content-sync/types.js";
@@ -13,8 +13,8 @@
13
13
  */
14
14
 
15
15
  import { Flags } from "@oclif/core";
16
- import { api, getHttpClient, isConvexConfigured } from "@abbie/sdk/convex";
17
- import { getStoredClerkId } from "@abbie/sdk/config";
16
+ import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
17
+ import { getStoredClerkId } from "@creativeintelligence/sdk/config";
18
18
  import { ConfigSyncRunner, type ConfigSyncAgent, type SyncTickResult } from "../../lib/config-sync/index.js";
19
19
  import { ContentSyncRunner } from "../../lib/content-sync/index.js";
20
20
  import type { ContentSyncTickResult, ContentType } from "../../lib/content-sync/types.js";
@@ -1,4 +1,4 @@
1
- import { api, getHttpClient, isConvexConfigured } from "@abbie/sdk/convex";
1
+ import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
2
2
  import { Flags } from "@oclif/core";
3
3
  import { getDeviceId } from "../../../lib/device.js";
4
4
  import { isRunning } from "../../../lib/tmux/index.js";
@@ -1003,7 +1003,7 @@ Avoid editing the same files simultaneously. Check git status before committing.
1003
1003
  */
1004
1004
  private async getFileProvenanceContext(project: string): Promise<string> {
1005
1005
  try {
1006
- const { getHttpClient, api } = await import("@abbie/sdk/convex");
1006
+ const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
1007
1007
  const client = getHttpClient();
1008
1008
  const entries = await client.query(api.contextBus.readByProject, {
1009
1009
  project,
@@ -1195,7 +1195,7 @@ Violation of this contract will cause session termination.
1195
1195
  */
1196
1196
  private async writeSessionToStore(session: ActiveSession): Promise<void> {
1197
1197
  try {
1198
- const { getHttpClient, api } = await import("@abbie/sdk/convex");
1198
+ const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
1199
1199
  const client = getHttpClient();
1200
1200
  await client.mutation(api.sessions.create, {
1201
1201
  sessionId: session.session_id,
@@ -1235,7 +1235,7 @@ Violation of this contract will cause session termination.
1235
1235
  metadata?: Record<string, unknown>,
1236
1236
  ): Promise<void> {
1237
1237
  try {
1238
- const { getHttpClient, api } = await import("@abbie/sdk/convex");
1238
+ const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
1239
1239
  const client = getHttpClient();
1240
1240
  await client.mutation(api.sessions.update, {
1241
1241
  sessionId,
@@ -1253,7 +1253,7 @@ Violation of this contract will cause session termination.
1253
1253
  */
1254
1254
  async getAsync(sessionId: string): Promise<ActiveSession | undefined> {
1255
1255
  try {
1256
- const { getHttpClient, api } = await import("@abbie/sdk/convex");
1256
+ const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
1257
1257
  const client = getHttpClient();
1258
1258
  const storeSession = await client.query(api.sessions.get, { sessionId });
1259
1259
  if (storeSession) {
@@ -1276,7 +1276,7 @@ Violation of this contract will cause session termination.
1276
1276
  limit?: number;
1277
1277
  }): Promise<ActiveSession[]> {
1278
1278
  try {
1279
- const { getHttpClient, api } = await import("@abbie/sdk/convex");
1279
+ const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
1280
1280
  const client = getHttpClient();
1281
1281
  const deviceId = getDeviceId();
1282
1282
  const storeSessions = await client.query(api.sessions.list, {
@@ -1315,7 +1315,7 @@ Violation of this contract will cause session termination.
1315
1315
  */
1316
1316
  async getActiveAsync(): Promise<ActiveSession[]> {
1317
1317
  try {
1318
- const { getHttpClient, api } = await import("@abbie/sdk/convex");
1318
+ const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
1319
1319
  const client = getHttpClient();
1320
1320
  const deviceId = getDeviceId();
1321
1321
  const storeSessions = await client.query(api.sessions.getActive, { deviceId });
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Falls back to local AnnotationStore when Convex is unavailable.
8
8
  */
9
- import { api, getHttpClient, isConvexConfigured } from "@abbie/sdk/convex";
9
+ import { api, getHttpClient, isConvexConfigured } from "@creativeintelligence/sdk/convex";
10
10
  import type {
11
11
  Annotation,
12
12
  AnnotationIntent,
package/src/lib/events.ts CHANGED
@@ -238,7 +238,7 @@ export class EventStore {
238
238
  */
239
239
  private async appendToStore(event: SessionEvent): Promise<void> {
240
240
  try {
241
- const { getHttpClient, api } = await import("@abbie/sdk/convex");
241
+ const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
242
242
  const client = getHttpClient();
243
243
  await client.mutation(api.events.append, {
244
244
  sessionId: event.session_id,
@@ -0,0 +1,258 @@
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
+
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { execSync } from "node:child_process";
15
+ import { createInterface } from "node:readline";
16
+ import { exec } from "node:child_process";
17
+
18
+ const AUTH_JSON = join(homedir(), ".pi", "agent", "auth.json");
19
+
20
+ // ============================================================================
21
+ // Auth.json helpers
22
+ // ============================================================================
23
+
24
+ export interface ProviderCredential {
25
+ type: "oauth";
26
+ refresh: string;
27
+ access: string;
28
+ expires: number;
29
+ [key: string]: unknown;
30
+ }
31
+
32
+ type AuthData = Record<string, ProviderCredential>;
33
+
34
+ export function readAuthJson(): AuthData {
35
+ try {
36
+ if (!existsSync(AUTH_JSON)) return {};
37
+ return JSON.parse(readFileSync(AUTH_JSON, "utf8")) as AuthData;
38
+ } catch {
39
+ return {};
40
+ }
41
+ }
42
+
43
+ function writeAuthJson(data: AuthData): void {
44
+ const dir = join(homedir(), ".pi", "agent");
45
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
46
+ writeFileSync(AUTH_JSON, JSON.stringify(data, null, 2), "utf8");
47
+ chmodSync(AUTH_JSON, 0o600);
48
+ }
49
+
50
+ export function getConnectedProviders(): string[] {
51
+ const data = readAuthJson();
52
+ return Object.entries(data)
53
+ .filter(([, v]) => v?.type === "oauth" && v.access)
54
+ .map(([k]) => k);
55
+ }
56
+
57
+ export function hasAnyProvider(): boolean {
58
+ return getConnectedProviders().length > 0;
59
+ }
60
+
61
+ // ============================================================================
62
+ // Provider info
63
+ // ============================================================================
64
+
65
+ export interface ProviderInfo {
66
+ id: string;
67
+ name: string;
68
+ description: string;
69
+ }
70
+
71
+ export const PROVIDERS: ProviderInfo[] = [
72
+ { id: "anthropic", name: "Anthropic", description: "Claude Pro/Max subscription" },
73
+ { id: "openai-codex", name: "OpenAI", description: "ChatGPT Plus/Pro subscription" },
74
+ { id: "google-gemini-cli", name: "Google Gemini", description: "Google Cloud Code Assist" },
75
+ { id: "github-copilot", name: "GitHub Copilot", description: "Copilot Individual/Business" },
76
+ { id: "google-antigravity", name: "Google Antigravity", description: "Gemini 3, Claude, GPT via Google Cloud" },
77
+ ];
78
+
79
+ // ============================================================================
80
+ // Dynamic import of Pi's OAuth module
81
+ // ============================================================================
82
+
83
+ interface OAuthModule {
84
+ getOAuthProvider: (id: string) => any;
85
+ getOAuthProviders: () => any[];
86
+ }
87
+
88
+ async function importPiOAuth(): Promise<OAuthModule | null> {
89
+ // Strategy: find @mariozechner/pi-ai from Pi's global install
90
+ const candidates: string[] = [];
91
+
92
+ // 1. Try resolving from pi binary location
93
+ try {
94
+ const piPath = execSync("which pi", { encoding: "utf8", timeout: 3000 }).trim();
95
+ if (piPath) {
96
+ const { realpathSync } = await import("node:fs");
97
+ const realPath = realpathSync(piPath);
98
+ // pi binary -> bin/pi.mjs -> package root -> node_modules/@mariozechner/pi-ai
99
+ const pkgRoot = join(realPath, "..", "..");
100
+ candidates.push(join(pkgRoot, "node_modules", "@mariozechner", "pi-ai", "dist", "index.js"));
101
+ }
102
+ } catch { /* not found */ }
103
+
104
+ // 2. Try common global paths
105
+ const nodeVersion = process.version.replace("v", "");
106
+ candidates.push(
107
+ join(homedir(), ".nvm", "versions", "node", `v${nodeVersion}`, "lib", "node_modules", "@mariozechner", "pi-coding-agent", "node_modules", "@mariozechner", "pi-ai", "dist", "index.js"),
108
+ join("/usr", "local", "lib", "node_modules", "@mariozechner", "pi-coding-agent", "node_modules", "@mariozechner", "pi-ai", "dist", "index.js"),
109
+ );
110
+
111
+ for (const candidate of candidates) {
112
+ if (existsSync(candidate)) {
113
+ try {
114
+ return await import(candidate) as OAuthModule;
115
+ } catch { /* import failed */ }
116
+ }
117
+ }
118
+
119
+ return null;
120
+ }
121
+
122
+ // ============================================================================
123
+ // CLI OAuth flow helpers
124
+ // ============================================================================
125
+
126
+ function prompt(question: string): Promise<string> {
127
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
128
+ return new Promise((resolve) => {
129
+ rl.question(question, (answer) => {
130
+ rl.close();
131
+ resolve(answer.trim());
132
+ });
133
+ });
134
+ }
135
+
136
+ function openBrowser(url: string): void {
137
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
138
+ exec(`${cmd} "${url}"`);
139
+ }
140
+
141
+ // ============================================================================
142
+ // Public API
143
+ // ============================================================================
144
+
145
+ /**
146
+ * Run the provider selection + OAuth flow.
147
+ * Returns the provider ID that was connected, or null if cancelled.
148
+ */
149
+ export async function connectProvider(
150
+ log: (msg: string) => void,
151
+ ): Promise<string | null> {
152
+ const piOAuth = await importPiOAuth();
153
+
154
+ if (!piOAuth) {
155
+ log(" ⚠ could not find Pi's OAuth module");
156
+ log(" connect providers in Pi instead: /login");
157
+ return null;
158
+ }
159
+
160
+ const connected = getConnectedProviders();
161
+
162
+ // Show provider list
163
+ log("");
164
+ log(" connect an AI provider");
165
+ log(" ─────────────────────");
166
+ log("");
167
+
168
+ const providers = piOAuth.getOAuthProviders();
169
+ for (let i = 0; i < providers.length; i++) {
170
+ const p = providers[i];
171
+ const isConnected = connected.includes(p.id);
172
+ const status = isConnected ? " ✓ connected" : "";
173
+ log(` ${i + 1}. ${p.name}${status}`);
174
+ }
175
+ log(` ${providers.length + 1}. skip for now`);
176
+ log("");
177
+
178
+ const choice = await prompt(" select (1-" + (providers.length + 1) + "): ");
179
+ const idx = parseInt(choice, 10) - 1;
180
+
181
+ if (isNaN(idx) || idx < 0 || idx >= providers.length) {
182
+ return null; // skip
183
+ }
184
+
185
+ const provider = providers[idx];
186
+ log("");
187
+ log(` connecting ${provider.name}...`);
188
+ log("");
189
+
190
+ try {
191
+ const credentials = await provider.login({
192
+ onAuth: (info: { url: string; instructions?: string }) => {
193
+ log(` open this URL to authenticate:`);
194
+ log(` ${info.url}`);
195
+ if (info.instructions) {
196
+ log(` ${info.instructions}`);
197
+ }
198
+ log("");
199
+ openBrowser(info.url);
200
+ },
201
+ onPrompt: async (promptInfo: { message: string; placeholder?: string; allowEmpty?: boolean }) => {
202
+ const answer = await prompt(` ${promptInfo.message} `);
203
+ if (!answer && !promptInfo.allowEmpty) {
204
+ throw new Error("Input required");
205
+ }
206
+ return answer;
207
+ },
208
+ onProgress: (message: string) => {
209
+ log(` ${message}`);
210
+ },
211
+ onManualCodeInput: async () => {
212
+ return await prompt(" paste the code or URL: ");
213
+ },
214
+ });
215
+
216
+ // Write to auth.json
217
+ const data = readAuthJson();
218
+ data[provider.id] = { type: "oauth", ...credentials };
219
+ writeAuthJson(data);
220
+
221
+ log(` ✓ ${provider.name} connected`);
222
+ return provider.id;
223
+ } catch (err) {
224
+ const msg = err instanceof Error ? err.message : String(err);
225
+ if (msg.includes("cancelled")) {
226
+ log(" cancelled");
227
+ } else {
228
+ log(` ✗ connection failed: ${msg}`);
229
+ }
230
+ return null;
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Check providers and optionally prompt for connection.
236
+ * Used in the setup wizard.
237
+ */
238
+ export async function ensureProviderConnected(
239
+ log: (msg: string) => void,
240
+ ): Promise<boolean> {
241
+ const connected = getConnectedProviders();
242
+
243
+ if (connected.length > 0) {
244
+ const names = connected.map((id) => {
245
+ const info = PROVIDERS.find((p) => p.id === id);
246
+ return info?.name ?? id;
247
+ });
248
+ log(` [4/4] providers: ${names.join(", ")} ✓`);
249
+ return true;
250
+ }
251
+
252
+ log(" [4/4] connect an AI provider");
253
+ log(" (uses your existing subscription — no API keys needed)");
254
+ log("");
255
+
256
+ const result = await connectProvider(log);
257
+ return result !== null;
258
+ }
package/src/lib/report.ts CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { spawnSync } from "node:child_process";
16
16
  import { randomUUID } from "node:crypto";
17
- import { api, getHttpClient } from "@abbie/sdk/convex";
17
+ import { api, getHttpClient } from "@creativeintelligence/sdk/convex";
18
18
  import { getDeviceId } from "./device.js";
19
19
  import { getDefaultSlackWorkspace } from "./slack-workspace.js";
20
20
 
package/src/lib/runner.ts CHANGED
@@ -734,7 +734,7 @@ export async function runAgent(config: RunnerConfig): Promise<number> {
734
734
  // Emit session:started progress event (best-effort, non-blocking)
735
735
  const traceIdForLifecycle = process.env.AGENTS_TRACE_ID;
736
736
  if (traceIdForLifecycle) {
737
- import("@abbie/sdk/convex")
737
+ import("@creativeintelligence/sdk/convex")
738
738
  .then(({ api: cbApi, getHttpClient }) =>
739
739
  getHttpClient().mutation(cbApi.contextBus.publish, {
740
740
  traceId: traceIdForLifecycle,
@@ -1107,7 +1107,7 @@ export async function runAgent(config: RunnerConfig): Promise<number> {
1107
1107
  outputForContext.slice(-500),
1108
1108
  );
1109
1109
 
1110
- const { api: cbApi, getHttpClient } = await import("@abbie/sdk/convex");
1110
+ const { api: cbApi, getHttpClient } = await import("@creativeintelligence/sdk/convex");
1111
1111
  await getHttpClient().mutation(cbApi.contextBus.publish, {
1112
1112
  traceId,
1113
1113
  sessionId: config.sessionId,
@@ -1139,7 +1139,7 @@ export async function runAgent(config: RunnerConfig): Promise<number> {
1139
1139
  // Update Convex session with terminal status + metadata (best-effort)
1140
1140
  if (traceId) {
1141
1141
  try {
1142
- const { getHttpClient, api } = await import("@abbie/sdk/convex");
1142
+ const { getHttpClient, api } = await import("@creativeintelligence/sdk/convex");
1143
1143
  const client = getHttpClient();
1144
1144
 
1145
1145
  // Read output excerpt for metadata
@@ -1182,7 +1182,7 @@ export async function runAgent(config: RunnerConfig): Promise<number> {
1182
1182
  // Emit session lifecycle event (completed/failed/stopped)
1183
1183
  if (traceId) {
1184
1184
  try {
1185
- const { api: cbApi, getHttpClient } = await import("@abbie/sdk/convex");
1185
+ const { api: cbApi, getHttpClient } = await import("@creativeintelligence/sdk/convex");
1186
1186
  await getHttpClient().mutation(cbApi.contextBus.publish, {
1187
1187
  traceId,
1188
1188
  sessionId: config.sessionId,