assistme 0.1.2 → 0.1.3

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.
@@ -4,7 +4,11 @@ import { createClient } from "@supabase/supabase-js";
4
4
  // src/utils/config.ts
5
5
  import Conf from "conf";
6
6
  import { resolve } from "path";
7
+ var SUPABASE_URL_DEFAULT = "https://msgplwbgohpokajtibew.supabase.co";
8
+ var SUPABASE_ANON_KEY_DEFAULT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1zZ3Bsd2Jnb2hwb2thanRpYmV3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjE3MTMzNTEsImV4cCI6MjA3NzI4OTM1MX0.YqiluL_mIBWKv5dteIcPAPQ_jRp9rzZlvAXvkQttDjs";
7
9
  var CONFIG_DEFAULTS = {
10
+ supabaseUrl: SUPABASE_URL_DEFAULT,
11
+ supabaseAnonKey: SUPABASE_ANON_KEY_DEFAULT,
8
12
  sessionName: "Default",
9
13
  model: "claude-sonnet-4-20250514",
10
14
  maxTurns: 50
@@ -14,8 +18,8 @@ var config = new Conf({
14
18
  defaults: CONFIG_DEFAULTS
15
19
  });
16
20
  function getConfig() {
17
- const supabaseUrl = process.env.SUPABASE_URL || config.get("supabaseUrl") || "";
18
- const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || config.get("supabaseAnonKey") || "";
21
+ const supabaseUrl = process.env.SUPABASE_URL || config.get("supabaseUrl") || SUPABASE_URL_DEFAULT;
22
+ const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || config.get("supabaseAnonKey") || SUPABASE_ANON_KEY_DEFAULT;
19
23
  const anthropicApiKey = process.env.ANTHROPIC_API_KEY || config.get("anthropicApiKey") || "";
20
24
  const workspacePath = config.get("workspacePath") || process.cwd();
21
25
  return {
@@ -168,28 +172,63 @@ function getSupabase() {
168
172
  }
169
173
  return supabase;
170
174
  }
175
+ async function exchangeCliToken(cliToken) {
176
+ const config2 = getConfig();
177
+ const exchangeUrl = `${config2.supabaseUrl}/functions/v1/cli-token-exchange`;
178
+ const res = await fetch(exchangeUrl, {
179
+ method: "POST",
180
+ headers: {
181
+ "Content-Type": "application/json",
182
+ apikey: config2.supabaseAnonKey
183
+ },
184
+ body: JSON.stringify({ token: cliToken })
185
+ });
186
+ if (!res.ok) {
187
+ const body = await res.json().catch(() => ({ error: res.statusText }));
188
+ throw new Error(body.error || `Token exchange failed (${res.status})`);
189
+ }
190
+ const data = await res.json();
191
+ if (!data.access_token || !data.refresh_token) {
192
+ throw new Error("Token exchange returned incomplete session");
193
+ }
194
+ return data;
195
+ }
171
196
  async function loginWithToken(cliToken) {
172
- let parsed;
173
- try {
174
- const json = Buffer.from(cliToken, "base64").toString("utf-8");
175
- parsed = JSON.parse(json);
176
- if (!parsed.access_token || !parsed.refresh_token) {
177
- throw new Error("Missing token fields");
178
- }
179
- } catch {
197
+ if (!cliToken.startsWith("am_")) {
180
198
  throw new Error(
181
- "Invalid token format. Please copy the full token from the web page."
199
+ "Invalid token format. Use an am_ token from the web page."
182
200
  );
183
201
  }
202
+ const sessionTokens = await exchangeCliToken(cliToken);
203
+ const store = readAuthStore();
204
+ store["cli_token"] = cliToken;
205
+ writeAuthStore(store);
184
206
  const sb = getSupabase();
185
207
  const { data, error } = await sb.auth.setSession({
186
- access_token: parsed.access_token,
187
- refresh_token: parsed.refresh_token
208
+ access_token: sessionTokens.access_token,
209
+ refresh_token: sessionTokens.refresh_token
188
210
  });
189
211
  if (error) throw new Error(`Login failed: ${error.message}`);
190
212
  if (!data.user) throw new Error("Login failed: no user returned");
191
213
  return data.user.id;
192
214
  }
215
+ async function refreshWithCliToken() {
216
+ const store = readAuthStore();
217
+ const cliToken = store["cli_token"];
218
+ if (!cliToken || !cliToken.startsWith("am_")) return null;
219
+ try {
220
+ const sessionTokens = await exchangeCliToken(cliToken);
221
+ const sb = getSupabase();
222
+ const { data, error } = await sb.auth.setSession({
223
+ access_token: sessionTokens.access_token,
224
+ refresh_token: sessionTokens.refresh_token
225
+ });
226
+ if (error || !data.user) return null;
227
+ return data.user.id;
228
+ } catch {
229
+ return null;
230
+ }
231
+ }
193
232
  async function getSession() {
194
233
  const sb = getSupabase();
195
234
  const { data } = await sb.auth.getSession();
@@ -199,6 +238,8 @@ async function getCurrentUserId() {
199
238
  const sb = getSupabase();
200
239
  const { data, error } = await sb.auth.getUser();
201
240
  if (error || !data.user) {
241
+ const refreshed = await refreshWithCliToken();
242
+ if (refreshed) return refreshed;
202
243
  throw new Error("Not authenticated. Run `assistme login`.");
203
244
  }
204
245
  return data.user.id;
@@ -391,6 +432,7 @@ export {
391
432
  DAYBOX_AGENT_ID,
392
433
  getSupabase,
393
434
  loginWithToken,
435
+ refreshWithCliToken,
394
436
  getSession,
395
437
  getCurrentUserId,
396
438
  logout,
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  setLogLevel,
25
25
  setSessionBusy,
26
26
  updateHeartbeat
27
- } from "./chunk-3TP3YO56.js";
27
+ } from "./chunk-H5BZPIOY.js";
28
28
 
29
29
  // src/index.ts
30
30
  import { Command } from "commander";
@@ -2839,11 +2839,14 @@ function isThinkingContent(msg) {
2839
2839
 
2840
2840
  // src/index.ts
2841
2841
  import { createInterface } from "readline";
2842
+ import { createRequire } from "module";
2842
2843
  loadEnv();
2844
+ var require2 = createRequire(import.meta.url);
2845
+ var { version } = require2("../package.json");
2843
2846
  var program = new Command();
2844
2847
  program.name("assistme").description(
2845
2848
  "AssistMe CLI Agent - AI assistant that controls your real browser"
2846
- ).version("0.1.0");
2849
+ ).version(version);
2847
2850
  program.command("login").description("Authenticate with your AssistMe account via browser token").action(async () => {
2848
2851
  const config = getConfig();
2849
2852
  const webUrl = config.supabaseUrl ? config.supabaseUrl.replace(/\.supabase\.co.*/, ".supabase.co").replace("https://", "") : "";
@@ -2855,7 +2858,7 @@ program.command("login").description("Authenticate with your AssistMe account vi
2855
2858
  console.log();
2856
2859
  console.log(chalk.cyan(` ${tokenPageUrl}`));
2857
2860
  console.log();
2858
- console.log(" Step 2: Sign in and copy the CLI token shown on the page.");
2861
+ console.log(" Step 2: Sign in, create a token, and copy it.");
2859
2862
  console.log();
2860
2863
  try {
2861
2864
  const { exec: exec2 } = await import("child_process");
@@ -3005,19 +3008,6 @@ program.command("start", { isDefault: true }).description("Start the agent and l
3005
3008
  if (opts.name) {
3006
3009
  setConfig("sessionName", opts.name);
3007
3010
  }
3008
- const config = getConfig();
3009
- if (!config.supabaseUrl || !config.supabaseAnonKey) {
3010
- log.error("Supabase not configured.");
3011
- log.info("Run: assistme config set supabaseUrl <url>");
3012
- log.info("Run: assistme config set supabaseAnonKey <key>");
3013
- process.exit(1);
3014
- }
3015
- if (!config.anthropicApiKey) {
3016
- log.error("Anthropic API key not configured.");
3017
- log.info("Run: assistme config set anthropicApiKey <key>");
3018
- log.info("Or set ANTHROPIC_API_KEY environment variable.");
3019
- process.exit(1);
3020
- }
3021
3011
  console.log();
3022
3012
  console.log(
3023
3013
  chalk.bold.cyan(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")
@@ -3097,7 +3087,7 @@ program.command("start", { isDefault: true }).description("Start the agent and l
3097
3087
  }
3098
3088
  log.agent(`Processing: "${input}"`);
3099
3089
  try {
3100
- const { createTask: createTask2 } = await import("./supabase-BQRPXXNA.js");
3090
+ const { createTask: createTask2 } = await import("./supabase-DTKGPEER.js");
3101
3091
  const session = sessionManager.getSession();
3102
3092
  const conversationId = sessionManager.getConversationId();
3103
3093
  if (session && conversationId) {
@@ -3126,7 +3116,7 @@ program.command("start", { isDefault: true }).description("Start the agent and l
3126
3116
  program.command("status").description("Check the status of the current agent session").action(async () => {
3127
3117
  try {
3128
3118
  const userId = await getCurrentUserId();
3129
- const { getSupabase: getSupabase2 } = await import("./supabase-BQRPXXNA.js");
3119
+ const { getSupabase: getSupabase2 } = await import("./supabase-DTKGPEER.js");
3130
3120
  const sb = getSupabase2();
3131
3121
  const { data: sessions } = await sb.from("agent_sessions").select("*").eq("user_id", userId).in("status", ["online", "busy"]).order("started_at", { ascending: false }).limit(5);
3132
3122
  if (!sessions || sessions.length === 0) {
@@ -16,10 +16,11 @@ import {
16
16
  loginWithToken,
17
17
  logout,
18
18
  pollPendingTasks,
19
+ refreshWithCliToken,
19
20
  resetEventSequence,
20
21
  setSessionBusy,
21
22
  updateHeartbeat
22
- } from "./chunk-3TP3YO56.js";
23
+ } from "./chunk-H5BZPIOY.js";
23
24
  export {
24
25
  CLI_AGENT_ID,
25
26
  DAYBOX_AGENT_ID,
@@ -38,6 +39,7 @@ export {
38
39
  loginWithToken,
39
40
  logout,
40
41
  pollPendingTasks,
42
+ refreshWithCliToken,
41
43
  resetEventSequence,
42
44
  setSessionBusy,
43
45
  updateHeartbeat
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assistme",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "AssistMe CLI Agent - AI-powered assistant that controls your real browser",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -85,27 +85,56 @@ export function getSupabase(): SupabaseClient {
85
85
  // ── Auth: Token-Based Login ────────────────────────────────────────
86
86
 
87
87
  /**
88
- * Login using a CLI token (base64-encoded JSON with access_token + refresh_token).
89
- * The token is generated by the web UI at /agent/token.
88
+ * Exchange an am_ CLI token for a Supabase session via the Edge Function.
89
+ */
90
+ async function exchangeCliToken(
91
+ cliToken: string
92
+ ): Promise<{ access_token: string; refresh_token: string }> {
93
+ const config = getConfig();
94
+ const exchangeUrl = `${config.supabaseUrl}/functions/v1/cli-token-exchange`;
95
+
96
+ const res = await fetch(exchangeUrl, {
97
+ method: "POST",
98
+ headers: {
99
+ "Content-Type": "application/json",
100
+ apikey: config.supabaseAnonKey,
101
+ },
102
+ body: JSON.stringify({ token: cliToken }),
103
+ });
104
+
105
+ if (!res.ok) {
106
+ const body = await res.json().catch(() => ({ error: res.statusText }));
107
+ throw new Error(body.error || `Token exchange failed (${res.status})`);
108
+ }
109
+
110
+ const data = await res.json();
111
+ if (!data.access_token || !data.refresh_token) {
112
+ throw new Error("Token exchange returned incomplete session");
113
+ }
114
+ return data;
115
+ }
116
+
117
+ /**
118
+ * Login using an am_ CLI token (exchanged via Edge Function for a Supabase session).
90
119
  */
91
120
  export async function loginWithToken(cliToken: string): Promise<string> {
92
- let parsed: { access_token: string; refresh_token: string };
93
- try {
94
- const json = Buffer.from(cliToken, "base64").toString("utf-8");
95
- parsed = JSON.parse(json);
96
- if (!parsed.access_token || !parsed.refresh_token) {
97
- throw new Error("Missing token fields");
98
- }
99
- } catch {
121
+ if (!cliToken.startsWith("am_")) {
100
122
  throw new Error(
101
- "Invalid token format. Please copy the full token from the web page."
123
+ "Invalid token format. Use an am_ token from the web page."
102
124
  );
103
125
  }
104
126
 
127
+ const sessionTokens = await exchangeCliToken(cliToken);
128
+
129
+ // Persist the CLI token so we can re-authenticate later
130
+ const store = readAuthStore();
131
+ store["cli_token"] = cliToken;
132
+ writeAuthStore(store);
133
+
105
134
  const sb = getSupabase();
106
135
  const { data, error } = await sb.auth.setSession({
107
- access_token: parsed.access_token,
108
- refresh_token: parsed.refresh_token,
136
+ access_token: sessionTokens.access_token,
137
+ refresh_token: sessionTokens.refresh_token,
109
138
  });
110
139
 
111
140
  if (error) throw new Error(`Login failed: ${error.message}`);
@@ -114,6 +143,29 @@ export async function loginWithToken(cliToken: string): Promise<string> {
114
143
  return data.user.id;
115
144
  }
116
145
 
146
+ /**
147
+ * Re-authenticate using the stored CLI token (am_).
148
+ * Called automatically when the Supabase session has expired.
149
+ */
150
+ export async function refreshWithCliToken(): Promise<string | null> {
151
+ const store = readAuthStore();
152
+ const cliToken = store["cli_token"];
153
+ if (!cliToken || !cliToken.startsWith("am_")) return null;
154
+
155
+ try {
156
+ const sessionTokens = await exchangeCliToken(cliToken);
157
+ const sb = getSupabase();
158
+ const { data, error } = await sb.auth.setSession({
159
+ access_token: sessionTokens.access_token,
160
+ refresh_token: sessionTokens.refresh_token,
161
+ });
162
+ if (error || !data.user) return null;
163
+ return data.user.id;
164
+ } catch {
165
+ return null;
166
+ }
167
+ }
168
+
117
169
  export async function getSession() {
118
170
  const sb = getSupabase();
119
171
  const { data } = await sb.auth.getSession();
@@ -124,6 +176,9 @@ export async function getCurrentUserId(): Promise<string> {
124
176
  const sb = getSupabase();
125
177
  const { data, error } = await sb.auth.getUser();
126
178
  if (error || !data.user) {
179
+ // Try auto-refresh with stored CLI token
180
+ const refreshed = await refreshWithCliToken();
181
+ if (refreshed) return refreshed;
127
182
  throw new Error("Not authenticated. Run `assistme login`.");
128
183
  }
129
184
  return data.user.id;
package/src/index.ts CHANGED
@@ -19,9 +19,13 @@ import {
19
19
  } from "./agent/scheduler.js";
20
20
  import { MemoryManager } from "./agent/memory.js";
21
21
  import { SkillManager } from "./agent/skills.js";
22
+ import { createRequire } from "module";
22
23
 
23
24
  loadEnv();
24
25
 
26
+ const require = createRequire(import.meta.url);
27
+ const { version } = require("../package.json");
28
+
25
29
  const program = new Command();
26
30
 
27
31
  program
@@ -29,7 +33,7 @@ program
29
33
  .description(
30
34
  "AssistMe CLI Agent - AI assistant that controls your real browser"
31
35
  )
32
- .version("0.1.0");
36
+ .version(version);
33
37
 
34
38
  // ── assistme login ──────────────────────────────────────────────────
35
39
 
@@ -53,7 +57,7 @@ program
53
57
  console.log();
54
58
  console.log(chalk.cyan(` ${tokenPageUrl}`));
55
59
  console.log();
56
- console.log(" Step 2: Sign in and copy the CLI token shown on the page.");
60
+ console.log(" Step 2: Sign in, create a token, and copy it.");
57
61
  console.log();
58
62
 
59
63
  // Try to open the URL in the user's default browser
@@ -269,23 +273,6 @@ program
269
273
  setConfig("sessionName", opts.name);
270
274
  }
271
275
 
272
- const config = getConfig();
273
-
274
- // Validate configuration
275
- if (!config.supabaseUrl || !config.supabaseAnonKey) {
276
- log.error("Supabase not configured.");
277
- log.info("Run: assistme config set supabaseUrl <url>");
278
- log.info("Run: assistme config set supabaseAnonKey <key>");
279
- process.exit(1);
280
- }
281
-
282
- if (!config.anthropicApiKey) {
283
- log.error("Anthropic API key not configured.");
284
- log.info("Run: assistme config set anthropicApiKey <key>");
285
- log.info("Or set ANTHROPIC_API_KEY environment variable.");
286
- process.exit(1);
287
- }
288
-
289
276
  console.log();
290
277
  console.log(
291
278
  chalk.bold.cyan(" ╔══════════════════════════════════════════╗")
@@ -11,7 +11,14 @@ export interface AssistMeConfig {
11
11
  maxTurns: number;
12
12
  }
13
13
 
14
+ const SUPABASE_URL_DEFAULT =
15
+ "https://msgplwbgohpokajtibew.supabase.co";
16
+ const SUPABASE_ANON_KEY_DEFAULT =
17
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1zZ3Bsd2Jnb2hwb2thanRpYmV3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjE3MTMzNTEsImV4cCI6MjA3NzI4OTM1MX0.YqiluL_mIBWKv5dteIcPAPQ_jRp9rzZlvAXvkQttDjs";
18
+
14
19
  const CONFIG_DEFAULTS: Partial<AssistMeConfig> = {
20
+ supabaseUrl: SUPABASE_URL_DEFAULT,
21
+ supabaseAnonKey: SUPABASE_ANON_KEY_DEFAULT,
15
22
  sessionName: "Default",
16
23
  model: "claude-sonnet-4-20250514",
17
24
  maxTurns: 50,
@@ -24,9 +31,9 @@ const config = new Conf<Partial<AssistMeConfig>>({
24
31
 
25
32
  export function getConfig(): AssistMeConfig {
26
33
  const supabaseUrl =
27
- process.env.SUPABASE_URL || config.get("supabaseUrl") || "";
34
+ process.env.SUPABASE_URL || config.get("supabaseUrl") || SUPABASE_URL_DEFAULT;
28
35
  const supabaseAnonKey =
29
- process.env.SUPABASE_ANON_KEY || config.get("supabaseAnonKey") || "";
36
+ process.env.SUPABASE_ANON_KEY || config.get("supabaseAnonKey") || SUPABASE_ANON_KEY_DEFAULT;
30
37
  const anthropicApiKey =
31
38
  process.env.ANTHROPIC_API_KEY || config.get("anthropicApiKey") || "";
32
39
  const workspacePath =