@photon-ai/flux 0.3.2 → 0.3.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.
package/README.md CHANGED
@@ -31,8 +31,8 @@ Flux is an open-sourced CLI tool that lets developers build and deploy LangChain
31
31
  ## Installation
32
32
 
33
33
  ```
34
- npm install @photon-cli/flux
35
- bun add @photon-cli/flux
34
+ npm install @photon-ai/flux
35
+ bun add @photon-ai/flux
36
36
  ```
37
37
 
38
38
  ---
@@ -41,13 +41,13 @@ bun add @photon-cli/flux
41
41
 
42
42
  | Command | Description |
43
43
  |---------|-------------|
44
- | `npx @photon-cli/flux` | Show help |
45
- | `npx @photon-cli/flux whoami` | Check account |
46
- | `npx @photon-cli/flux login` | Login and signup|
47
- | `npx @photon-cli/flux logout` | Logout |
48
- | `npx @photon-cli/flux run --local` | Start the development server (local mode) |
49
- | `npx @photon-cli/flux run --prod` | Start with live iMessage bridge |
50
- | `npx @photon-cli/flux validate` | Check your code for errors |
44
+ | `npx @photon-ai/flux` | Show help |
45
+ | `npx @photon-ai/flux whoami` | Check account |
46
+ | `npx @photon-ai/flux login` | Login and signup|
47
+ | `npx @photon-ai/flux logout` | Logout |
48
+ | `npx @photon-ai/flux run --local` | Start the development server (local mode) |
49
+ | `npx @photon-ai/flux run --prod` | Start with live iMessage bridge |
50
+ | `npx @photon-ai/flux validate` | Check your code for errors |
51
51
 
52
52
  ---
53
53
 
@@ -57,6 +57,15 @@ Message +16286298650 with you phone number to text the LangChain agent that you
57
57
 
58
58
  ---
59
59
 
60
+ ## Log in
61
+
62
+ Authentication is based on iMessage:
63
+ - The user (client) sends a code to the Flux number to prove phone ownership.
64
+ - The server generates a UUID per login attempt. It then waits for the iMessage text from the client with the UUID. Once verified, it will issue a token.
65
+ - Credentials (token, phone, timestamp) are saved to credentials.json. This way, the user only has to log in once.
66
+
67
+ ---
68
+
60
69
  ## Usage
61
70
 
62
71
  #### Step 1: Create LangChain Agent
@@ -77,11 +86,31 @@ export default {
77
86
  Authenticate with your phone number and iMessage:
78
87
 
79
88
  ```
80
- npx @photon-cli/flux login
89
+ npx @photon-ai/flux login
81
90
 
82
91
  Enter your phone number (e.g. +15551234567): +1234567890
83
- [FLUX] Validating with server...
84
- [FLUX] Logged in as +1234567890
92
+ [FLUX] Requesting verification code...
93
+ [FLUX] Verification code: d33gwu
94
+ [FLUX] Opening iMessage to send verification code...
95
+ [FLUX] Please send the code "d33gwu" to +16286298650 via iMessage.
96
+ [FLUX] Waiting for verification...
97
+ [FLUX] Successfully logged in as +1234567890
98
+ ```
99
+
100
+ If already logged in:
101
+
102
+ ```
103
+ npx @photon-ai/flux login
104
+
105
+ [FLUX] Already logged in as +1234567890
106
+ ```
107
+
108
+ Log out:
109
+
110
+ ```
111
+ npx @photon-ai/flux logout
112
+
113
+ [FLUX] Logged out.
85
114
  ```
86
115
 
87
116
  ### Step 3: Validate
@@ -89,7 +118,7 @@ Enter your phone number (e.g. +15551234567): +1234567890
89
118
  Validate that your agent works and exports correctly:
90
119
 
91
120
  ```
92
- npx @photon-cli/flux validate
121
+ npx @photon-ai/flux validate
93
122
 
94
123
  [FLUX] Validating agent.ts...
95
124
  [FLUX] Agent is valid!
@@ -100,7 +129,7 @@ npx @photon-cli/flux validate
100
129
  Test your agent through your terminal (no iMessage connection):
101
130
 
102
131
  ```
103
- npx @photon-cli/flux run --local
132
+ npx @photon-ai/flux run --local
104
133
 
105
134
  [FLUX] Welcome to Flux! Your agent is loaded.
106
135
  [FLUX] Type a message to test it. Press Ctrl+C to exit.
@@ -115,7 +144,7 @@ Agent: Hello! How can I assist you today?
115
144
  Run your agent locally and connect it to the iMessage bridge. When you message the FLUX number with your phone number, you will receive the output of your LangChain agent:
116
145
 
117
146
  ```
118
- npx @photon-cli/flux run --prod
147
+ npx @photon-ai/flux run --prod
119
148
 
120
149
  [FLUX] Loading agent from agent.ts...
121
150
  [FLUX] Agent loaded successfully!
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ var p3 = require('net');
11
11
  var util = require('util');
12
12
  var worker_threads = require('worker_threads');
13
13
  var readline = require('readline');
14
+ var child_process = require('child_process');
14
15
 
15
16
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
16
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -58169,12 +58170,17 @@ async function createGrpcClient(address, grpcOptionsOrServiceImpls, ...serviceIm
58169
58170
 
58170
58171
  // src/service.ts
58171
58172
  var FluxService = class extends Service("FluxService") {
58173
+ // Message handling
58172
58174
  sendMessage = server();
58173
58175
  messageStream = bidi();
58174
58176
  registerAgent = server();
58175
58177
  unregisterAgent = server();
58176
58178
  onIncomingMessage = client();
58177
- validateUser = server();
58179
+ // Authentication - iMessage verification flow
58180
+ getDynamicCode = server();
58181
+ waitingVerified = server();
58182
+ validateToken = server();
58183
+ revokeToken = server();
58178
58184
  };
58179
58185
 
58180
58186
  // src/flux-client.ts
@@ -58182,9 +58188,11 @@ var GRPC_SERVER_ADDRESS = process.env.FLUX_SERVER_ADDRESS || "fluxy.photon.codes
58182
58188
  var FluxClient = class {
58183
58189
  client = null;
58184
58190
  phoneNumber;
58191
+ token;
58185
58192
  onMessage;
58186
- constructor(phoneNumber, onMessage) {
58193
+ constructor(phoneNumber, token, onMessage) {
58187
58194
  this.phoneNumber = phoneNumber.replace(/[\s\-\(\)]/g, "");
58195
+ this.token = token;
58188
58196
  this.onMessage = onMessage;
58189
58197
  }
58190
58198
  async connect() {
@@ -58198,7 +58206,7 @@ var FluxClient = class {
58198
58206
  }
58199
58207
  async register() {
58200
58208
  if (!this.client) throw new Error("Not connected. Call connect() first.");
58201
- const result = await this.client.FluxService.registerAgent(this.phoneNumber);
58209
+ const result = await this.client.FluxService.registerAgent(this.phoneNumber, this.token);
58202
58210
  if (result.success) {
58203
58211
  console.log(`[FLUX] Registered agent for ${this.phoneNumber}`);
58204
58212
  this.startMessageStream();
@@ -58245,8 +58253,9 @@ var FluxClient = class {
58245
58253
  };
58246
58254
  var GRPC_SERVER_ADDRESS2 = process.env.FLUX_SERVER_ADDRESS || "fluxy.photon.codes:443";
58247
58255
  var CONFIG_DIR = m3__namespace.join(process.env.HOME || "~", ".flux");
58248
- var CONFIG_FILE = m3__namespace.join(CONFIG_DIR, "config.json");
58249
- function loadConfig() {
58256
+ var CONFIG_FILE = m3__namespace.join(CONFIG_DIR, "credentials.json");
58257
+ var VERIFICATION_NUMBER = "+16286298650";
58258
+ function loadCredentials() {
58250
58259
  try {
58251
58260
  if (U__namespace.existsSync(CONFIG_FILE)) {
58252
58261
  return JSON.parse(U__namespace.readFileSync(CONFIG_FILE, "utf-8"));
@@ -58255,13 +58264,13 @@ function loadConfig() {
58255
58264
  }
58256
58265
  return {};
58257
58266
  }
58258
- function saveConfig(config) {
58267
+ function saveCredentials(credentials) {
58259
58268
  if (!U__namespace.existsSync(CONFIG_DIR)) {
58260
58269
  U__namespace.mkdirSync(CONFIG_DIR, { recursive: true });
58261
58270
  }
58262
- U__namespace.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
58271
+ U__namespace.writeFileSync(CONFIG_FILE, JSON.stringify(credentials, null, 2));
58263
58272
  }
58264
- function clearConfig() {
58273
+ function clearCredentials() {
58265
58274
  if (U__namespace.existsSync(CONFIG_FILE)) {
58266
58275
  U__namespace.unlinkSync(CONFIG_FILE);
58267
58276
  }
@@ -58278,50 +58287,124 @@ async function prompt(question) {
58278
58287
  });
58279
58288
  });
58280
58289
  }
58290
+ function openIMessage(to, body) {
58291
+ return new Promise((resolve, reject) => {
58292
+ const url = `sms:${to}&body=${encodeURIComponent(body)}`;
58293
+ const command = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
58294
+ child_process.exec(command, (error) => {
58295
+ if (error) {
58296
+ reject(error);
58297
+ } else {
58298
+ resolve();
58299
+ }
58300
+ });
58301
+ });
58302
+ }
58303
+ async function createGrpcClientWithRetry() {
58304
+ const clientImpl = FluxService.Client({
58305
+ async onIncomingMessage() {
58306
+ return { received: true };
58307
+ }
58308
+ });
58309
+ return await createGrpcClient(GRPC_SERVER_ADDRESS2, clientImpl);
58310
+ }
58281
58311
  async function login() {
58312
+ const existing = loadCredentials();
58313
+ if (existing.token) {
58314
+ try {
58315
+ const client2 = await createGrpcClientWithRetry();
58316
+ const result = await client2.FluxService.validateToken(existing.token);
58317
+ if (result.valid) {
58318
+ console.log(`[FLUX] Already logged in as ${result.phone}`);
58319
+ return result.phone;
58320
+ }
58321
+ } catch {
58322
+ }
58323
+ clearCredentials();
58324
+ }
58282
58325
  const phoneNumber = await prompt("Enter your phone number (e.g. +15551234567): ");
58283
58326
  if (!phoneNumber.match(/^\+?[0-9]{10,15}$/)) {
58284
- console.error("Invalid phone number format.");
58327
+ console.error("[FLUX] Invalid phone number format.");
58285
58328
  process.exit(1);
58286
58329
  }
58287
- console.log("[FLUX] Validating with server...");
58330
+ const normalizedPhone = phoneNumber.startsWith("+") ? phoneNumber : `+${phoneNumber}`;
58331
+ console.log("[FLUX] Requesting verification code...");
58288
58332
  try {
58289
- const clientImpl = FluxService.Client({
58290
- async onIncomingMessage() {
58291
- return { received: true };
58292
- }
58293
- });
58294
- const client2 = await createGrpcClient(GRPC_SERVER_ADDRESS2, clientImpl);
58295
- const result = await client2.FluxService.validateUser(phoneNumber);
58296
- if (result.error) {
58297
- console.error(`[FLUX] Login failed: ${result.error}`);
58333
+ const client2 = await createGrpcClientWithRetry();
58334
+ const clientId = crypto.randomUUID();
58335
+ const codeResult = await client2.FluxService.getDynamicCode(clientId, normalizedPhone);
58336
+ if (codeResult.error) {
58337
+ console.error(`[FLUX] Failed to get verification code: ${codeResult.error}`);
58298
58338
  process.exit(1);
58299
58339
  }
58300
- if (result.created) {
58301
- console.log(`[FLUX] New account created for ${phoneNumber}`);
58302
- } else if (result.exists) {
58303
- console.log(`[FLUX] Welcome back, ${phoneNumber}`);
58340
+ const code = codeResult.code;
58341
+ console.log(`[FLUX] Verification code: ${code}`);
58342
+ console.log(`[FLUX] Opening iMessage to send verification code...`);
58343
+ try {
58344
+ await openIMessage(VERIFICATION_NUMBER, code);
58345
+ console.log(`[FLUX] Please send the code "${code}" to ${VERIFICATION_NUMBER} via iMessage.`);
58346
+ } catch {
58347
+ console.log(`[FLUX] Could not open iMessage automatically.`);
58348
+ console.log(`[FLUX] Please manually send "${code}" to ${VERIFICATION_NUMBER} via iMessage.`);
58304
58349
  }
58305
- saveConfig({ phoneNumber });
58306
- console.log(`[FLUX] Logged in as ${phoneNumber}`);
58307
- return phoneNumber;
58350
+ console.log("[FLUX] Waiting for verification...");
58351
+ const verifyResult = await client2.FluxService.waitingVerified(clientId);
58352
+ if (verifyResult.error || !verifyResult.token) {
58353
+ console.error(`[FLUX] Verification failed: ${verifyResult.error || "No token received"}`);
58354
+ process.exit(1);
58355
+ }
58356
+ const credentials = {
58357
+ token: verifyResult.token,
58358
+ phone: normalizedPhone,
58359
+ authenticatedAt: (/* @__PURE__ */ new Date()).toISOString()
58360
+ };
58361
+ saveCredentials(credentials);
58362
+ console.log(`[FLUX] Successfully logged in as ${normalizedPhone}`);
58363
+ return normalizedPhone;
58308
58364
  } catch (error) {
58309
58365
  console.error(`[FLUX] Failed to connect to server: ${error.message}`);
58310
58366
  console.error(`[FLUX] Make sure the Flux server is running at ${GRPC_SERVER_ADDRESS2}`);
58311
58367
  process.exit(1);
58312
58368
  }
58313
58369
  }
58314
- function logout() {
58315
- clearConfig();
58370
+ async function logout() {
58371
+ const credentials = loadCredentials();
58372
+ if (credentials.token) {
58373
+ try {
58374
+ const client2 = await createGrpcClientWithRetry();
58375
+ await client2.FluxService.revokeToken(credentials.token);
58376
+ } catch {
58377
+ }
58378
+ }
58379
+ clearCredentials();
58316
58380
  console.log("[FLUX] Logged out.");
58317
58381
  }
58318
- async function getPhoneNumber() {
58319
- const config = loadConfig();
58320
- if (config.phoneNumber) {
58321
- return config.phoneNumber;
58382
+ async function getAuthToken() {
58383
+ const credentials = loadCredentials();
58384
+ if (credentials.token && credentials.phone) {
58385
+ try {
58386
+ const client2 = await createGrpcClientWithRetry();
58387
+ const result = await client2.FluxService.validateToken(credentials.token);
58388
+ if (result.valid) {
58389
+ return { token: credentials.token, phone: result.phone };
58390
+ }
58391
+ } catch {
58392
+ }
58393
+ console.log("[FLUX] Session expired. Please log in again.");
58394
+ clearCredentials();
58322
58395
  }
58323
58396
  console.log("[FLUX] Not logged in.");
58324
- return await login();
58397
+ const phone = await login();
58398
+ const newCredentials = loadCredentials();
58399
+ if (!newCredentials.token) {
58400
+ console.error("[FLUX] Login failed.");
58401
+ process.exit(1);
58402
+ }
58403
+ return { token: newCredentials.token, phone };
58404
+ }
58405
+ function loadConfig() {
58406
+ const credentials = loadCredentials();
58407
+ return { phoneNumber: credentials.phone };
58325
58408
  }
58326
58409
  var AGENT_FILE_NAME = "agent.ts";
58327
58410
  var tsxRegistered = false;
@@ -58439,7 +58522,7 @@ async function runLocal() {
58439
58522
  askQuestion();
58440
58523
  }
58441
58524
  async function runProd() {
58442
- const phoneNumber = await getPhoneNumber();
58525
+ const { token, phone: phoneNumber } = await getAuthToken();
58443
58526
  const agentPath = findAgentFile();
58444
58527
  if (!agentPath) {
58445
58528
  console.error("[FLUX] No agent.ts or agent.js found in current directory.");
@@ -58454,7 +58537,7 @@ async function runProd() {
58454
58537
  console.log(`[FLUX] Loading agent from ${m3__namespace.basename(agentPath)}...`);
58455
58538
  const agent = await loadAgent(agentPath);
58456
58539
  console.log("[FLUX] Agent loaded successfully!");
58457
- const flux = new FluxClient(phoneNumber, async (message) => {
58540
+ const flux = new FluxClient(phoneNumber, token, async (message) => {
58458
58541
  console.log(`[FLUX] Processing message from ${message.userPhoneNumber}: ${message.text}`);
58459
58542
  try {
58460
58543
  const response = await agent.invoke({
@@ -58488,10 +58571,10 @@ async function main() {
58488
58571
  switch (command) {
58489
58572
  case "login":
58490
58573
  await login();
58491
- break;
58574
+ process.exit(0);
58492
58575
  case "logout":
58493
- logout();
58494
- break;
58576
+ await logout();
58577
+ process.exit(0);
58495
58578
  case "run":
58496
58579
  if (flag === "--local") {
58497
58580
  await runLocal();