@photon-ai/flux 0.3.7 → 0.3.9

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
@@ -1,80 +1,102 @@
1
1
  <div align="center">
2
2
 
3
- ![Banner](./assets/banner.png)
4
-
5
3
  # @photon-ai/flux
6
4
 
7
- > A new way to build and deploy your iMessage agents at the speed of light
5
+ > An open-source CLI for deploying LangChain agents to iMessage in seconds
8
6
 
9
7
  </div>
10
8
 
11
9
  [![npm version](https://img.shields.io/npm/v/@photon-ai/flux.svg)](https://www.npmjs.com/package/@photon-ai/flux)
12
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue.svg)](https://www.typescriptlang.org/)
13
- [![Python](https://img.shields.io/badge/Python-3.9+-blue.svg)](https://www.python.org/)
11
+ [![GitHub release](https://img.shields.io/github/v/release/photon-hq/flux?include_prereleases)](https://github.com/photon-hq/flux/releases)
14
12
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
15
13
  [![Discord](https://img.shields.io/badge/Discord-Join-5865F2.svg?logo=discord&logoColor=white)](https://discord.gg/bZd4CMd2H5)
16
14
 
17
15
  Flux is an open-sourced CLI tool that lets developers build and deploy LangChain agents that connect to iMessage at no cost and under 5 seconds.
18
16
 
19
- <!-- --- -->
17
+ ## Features
20
18
 
21
- ## Features
22
19
 
23
- - **Deploy with a single command**: Export a LangChain agent and deploy it to iMessage with a single command.
24
- - **Text your agent from your phone**: Send an iMessage to the Flux number and get responses from your running agent.
25
- - **Testing mode**: Test your agent through your terminal before connecting to the iMessage brigde.
26
- - **Phone Number Authentication**: Log in with just your phone number and iMessage.
27
- - **Agent Validation**: Automatically validate your LangChain agent in the CLI.
20
+ - **Deploy with a single command**: Export a LangChain agent and deploy it to iMessage with one command
21
+ - **Text your agent from your phone**: Send an iMessage to the Flux number and get responses from your running agent
22
+ - **Testing mode**: Test your agent through your terminal before connecting to the iMessage bridge
23
+ - **Phone Number Authentication**: Log in with just your phone number and iMessage
24
+ - **Agent Validation**: Automatically validate your LangChain agent in the CLI
28
25
 
29
- <!-- --- -->
26
+ ## Quick Start
30
27
 
31
- ## Installation
32
28
 
33
- ###
34
- ```
35
- npm install @photon-ai/flux
36
- #or
37
- bun add @photon-ai/flux
38
- ```
29
+ Get started with Flux in seconds:
39
30
 
40
- <!-- --- -->
31
+ ```bash
32
+ # No installation needed - use npx directly
33
+ npx @photon-ai/flux login
41
34
 
42
- ## CLI Commands
35
+ # Create your agent file
36
+ echo 'export default {
37
+ async invoke({ message }: { message: string }) {
38
+ return `You said: ${message}`;
39
+ }
40
+ };' > agent.ts
43
41
 
44
- | Command | Description |
45
- |---------|-------------|
46
- | `npx @photon-ai/flux` | Show help |
47
- | `npx @photon-ai/flux whoami` | Check account |
48
- | `npx @photon-ai/flux login` | Login and signup|
49
- | `npx @photon-ai/flux logout` | Logout |
50
- | `npx @photon-ai/flux run --local` | Start the development server (local mode) |
51
- | `npx @photon-ai/flux run --prod` | Start with live iMessage bridge |
52
- | `npx @photon-ai/flux validate` | Check your code for errors |
42
+ # Test locally
43
+ npx @photon-ai/flux run --local
53
44
 
54
- <!-- --- -->
45
+ # Deploy to production
46
+ npx @photon-ai/flux run --prod
47
+ ```
48
+
49
+ ## 📦 Installation
55
50
 
56
- ## Flux Number
51
+ ### Recommended: Global Installation
52
+
53
+ For the best experience, install Flux globally:
54
+
55
+ ```bash
56
+ npm install -g @photon-ai/flux
57
+ # or
58
+ bun add -g @photon-ai/flux
59
+ ```
57
60
 
58
- Message `+16286298650` with you phone number to text the LangChain agent that you built.
61
+ Then run commands directly:
59
62
 
60
- <!-- --- -->
63
+ ```bash
64
+ flux login
65
+ flux run --local
66
+ flux run --prod
67
+ ```
61
68
 
62
- ## Log in
69
+ ### Alternative: Use npx (No Installation Required)
63
70
 
64
- Authentication is based on iMessage:
65
- - The user (client) sends a code to the Flux number to prove phone ownership.
66
- - 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.
67
- - Credentials (token, phone, timestamp) are saved to credentials.json. This way, the user only has to log in once.
71
+ You can use Flux without installing it by using `npx` or `bunx`:
68
72
 
69
- <!-- --- -->
73
+ ```bash
74
+ npx @photon-ai/flux login
75
+ npx @photon-ai/flux run --local
76
+ // or
77
+ bunx @photon-ai/flux login
78
+ bunx @photon-ai/flux run --local
79
+ ```
70
80
 
71
- ## Usage
81
+ > **Note:** When using `npx` or `bunx`, there's no need to install the package first. `npx` or `bunx` will download and execute it automatically.
72
82
 
73
- ### Step 1: Create LangChain Agent
83
+ ### Local Installation (For Development)
74
84
 
75
- Create an `agent.ts` file with your LangChain agent. Make sure to have `export default agent`. Below is one simple example:
85
+ If you're integrating Flux into a project:
76
86
 
87
+ ```bash
88
+ npm install @photon-ai/flux
89
+ # or
90
+ bun add @photon-ai/flux
77
91
  ```
92
+
93
+ ## 📖 Usage Guide
94
+
95
+ ### Step 1: Create Your LangChain Agent
96
+
97
+ Create an `agent.ts` file with your LangChain agent. Make sure to have `export default agent`. Here's a simple example:
98
+
99
+ ```typescript
78
100
  // agent.ts
79
101
  export default {
80
102
  async invoke({ message }: { message: string }) {
@@ -83,13 +105,19 @@ export default {
83
105
  };
84
106
  ```
85
107
 
86
- ### Step 2: Login
108
+ ### Step 2: Authenticate with iMessage
87
109
 
88
- Authenticate with your phone number and iMessage:
110
+ Authenticate with your phone number and iMessage:
89
111
 
90
- ```
112
+ ```bash
113
+ flux login
114
+ # or
91
115
  npx @photon-ai/flux login
116
+ ```
92
117
 
118
+ **Example session:**
119
+
120
+ ```
93
121
  Enter your phone number (e.g. +15551234567): +1234567890
94
122
  [FLUX] Requesting verification code...
95
123
  [FLUX] Verification code: d33gwu
@@ -99,40 +127,52 @@ Enter your phone number (e.g. +15551234567): +1234567890
99
127
  [FLUX] Successfully logged in as +1234567890
100
128
  ```
101
129
 
102
- If already logged in:
130
+ If already logged in:
103
131
 
104
132
  ```
105
- npx @photon-ai/flux login
106
-
107
133
  [FLUX] Already logged in as +1234567890
108
134
  ```
109
135
 
110
- Log out:
136
+ To log out:
111
137
 
138
+ ```bash
139
+ flux logout
112
140
  ```
113
- npx @photon-ai/flux logout
114
141
 
142
+ ```
115
143
  [FLUX] Logged out.
116
144
  ```
117
145
 
118
- ### Step 3: Validate
146
+ ### Step 3: Validate Your Agent
119
147
 
120
- Validate that your agent works and exports correctly:
148
+ Validate that your agent works and exports correctly:
121
149
 
122
- ```
150
+ ```bash
151
+ flux validate
152
+ # or
123
153
  npx @photon-ai/flux validate
154
+ ```
155
+
156
+ **Output:**
124
157
 
158
+ ```
125
159
  [FLUX] Validating agent.ts...
126
160
  [FLUX] Agent is valid!
127
161
  ```
128
162
 
129
- ### Step 4: Testing Mode
163
+ ### Step 4: Test Locally (Development Mode)
130
164
 
131
- Test your agent through your terminal (no iMessage connection):
165
+ Test your agent through your terminal (no iMessage connection):
132
166
 
133
- ```
167
+ ```bash
168
+ flux run --local
169
+ # or
134
170
  npx @photon-ai/flux run --local
171
+ ```
172
+
173
+ **Interactive session:**
135
174
 
175
+ ```
136
176
  [FLUX] Welcome to Flux! Your agent is loaded.
137
177
  [FLUX] Type a message to test it. Press Ctrl+C to exit.
138
178
 
@@ -141,122 +181,191 @@ You: Hello!
141
181
  Agent: Hello! How can I assist you today?
142
182
  ```
143
183
 
144
- ### Step 5: Live Connection
184
+ ### Step 5: Deploy to Production (Live iMessage)
145
185
 
146
- 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:
186
+ Run your agent locally and connect it to the iMessage bridge. When you message the Flux number (`+16286298650`) from your registered phone, you'll receive responses from your LangChain agent:
147
187
 
148
- ```
188
+ ```bash
189
+ flux run --prod
190
+ # or
149
191
  npx @photon-ai/flux run --prod
192
+ ```
150
193
 
194
+ **Output:**
195
+
196
+ ```
151
197
  [FLUX] Loading agent from agent.ts...
152
198
  [FLUX] Agent loaded successfully!
153
199
  [FLUX] Connected to server at fluxy.photon.codes:443
154
200
  [FLUX] Registered agent for +1234567890
155
201
  [FLUX] Agent running in production mode. Press Ctrl+C to stop.
156
202
  [FLUX] Messages to +1234567890 will be processed by your agent.
157
-
158
203
  ```
159
204
 
160
- <!-- --- -->
205
+ Now text **`+16286298650`** from your phone to interact with your agent!
161
206
 
162
- ## Why Flux
207
+ ## 🛠️ CLI Commands
163
208
 
164
- Right now, connecting agents to messaging platforms involves complex processes such as setting up servers, configuring webhooks, and dealing with platform APIs. Furthermore, most current options use SMS or WhatsApp, which is unintuitive for many users.
209
+ | Command | Description |
210
+ |---------|-------------|
211
+ | `flux` or `npx @photon-ai/flux` | Show help |
212
+ | `flux whoami` | Check currently logged-in account |
213
+ | `flux login` | Login and signup with phone number |
214
+ | `flux logout` | Logout from current session |
215
+ | `flux validate` | Check your agent code for errors |
216
+ | `flux run --local` | Start development server (local testing mode) |
217
+ | `flux run --prod` | Start with live iMessage bridge |
165
218
 
166
- Flux solves these problems in the following ways:
219
+ ## 🔐 Authentication
167
220
 
168
- - **Deploy in < 5 seconds**: Link your LangChain agent to iMessage with a single command.
169
- - **Fully iMessage native**: Direct iMessage integration, not SMS or WhatsApp.
170
- - **Zero Infrastructure**: No servers to manage, webhooks to configure, or Apple Developer account needed.
171
- - **Open source**: Fully community driven.
172
- - **Free to use**: No subscription fees.
221
+ Authentication is based on iMessage to ensure secure and simple access:
173
222
 
174
- <!-- --- -->
223
+ 1. **Code Generation**: The server generates a unique UUID for each login attempt
224
+ 2. **Phone Verification**: You send the verification code to the Flux number (`+16286298650`) via iMessage to prove phone ownership
225
+ 3. **Token Issuance**: Once verified, the server issues an authentication token
226
+ 4. **Persistent Login**: Credentials (token, phone, timestamp) are saved to `credentials.json`, so you only need to log in once
175
227
 
176
- ## Examples
228
+ ## Proactive Messaging
177
229
 
178
- ### Echo Bot (No LLM)
230
+ Agents can initiate conversations without waiting for a user message. In production mode, `onInit` receives a sendMessage function that lets your agent send messages at any time.
179
231
 
180
232
  ```
181
- // agent.ts
233
+ let sendMessage: (to: string, text: string) => Promise<boolean>;
234
+
182
235
  export default {
183
- async invoke({ message }: { message: string }) {
184
- return `You said: ${message}`;
185
- }
236
+ async onInit(send) {
237
+ sendMessage = send;
238
+ // Now you can call sendMessage() anywhere in your agent
239
+ },
240
+ // ...
186
241
  };
187
242
  ```
188
243
 
189
- ### ChatGPT Bot
244
+ **Note**: Proactive messaging is only available in production mode `(flux run --prod`). In local mode, `onInit` is called without arguments.
245
+
246
+ ## 💡 Examples
247
+
248
+ ### Weather Agent
249
+
250
+ An agent with a weather tool (returns mock data).
251
+
252
+ ```typescript
253
+ import * as z from "zod";
254
+ import { createAgent, tool } from "langchain";
190
255
 
256
+ const getWeather = tool(
257
+ ({ city }) => `It's always sunny in ${city}!`,
258
+ {
259
+ name: "get_weather",
260
+ description: "Get the weather for a given city",
261
+ schema: z.object({
262
+ city: z.string(),
263
+ }),
264
+ },
265
+ );
266
+
267
+ const agent = createAgent({
268
+ model: "claude-sonnet-4-5-20250929",
269
+ tools: [getWeather],
270
+ });
271
+
272
+ export default agent
191
273
  ```
274
+
275
+ ### Chatbot with Memory
276
+
277
+ An advanced agent with memory:
278
+
279
+ ```typescript
192
280
  // agent.ts
281
+ import "dotenv/config";
193
282
  import { ChatOpenAI } from "@langchain/openai";
194
- import { SystemMessage, HumanMessage } from "@langchain/core/messages";
283
+ import { HumanMessage, AIMessage, SystemMessage } from "@langchain/core/messages";
195
284
 
196
285
  const llm = new ChatOpenAI({ modelName: "gpt-4o-mini" });
286
+ const history: Array<HumanMessage | AIMessage> = [];
197
287
 
198
288
  export default {
199
289
  async invoke({ message }: { message: string }) {
290
+ history.push(new HumanMessage(message));
291
+
200
292
  const response = await llm.invoke([
201
293
  new SystemMessage("You are a helpful assistant. Be concise."),
202
- new HumanMessage(message),
294
+ ...history,
203
295
  ]);
204
- return response.content as string;
296
+
297
+ const reply = response.content as string;
298
+ history.push(new AIMessage(reply));
299
+
300
+ // Keep last 20 messages to avoid token limits
301
+ if (history.length > 20) {
302
+ history.splice(0, 2);
303
+ }
304
+
305
+ return reply;
205
306
  }
206
307
  };
207
308
  ```
208
309
 
209
- ### Chatbot with tools
310
+ ## Why Flux?
210
311
 
211
- ```
212
- // agent.ts
213
- import { ChatOpenAI } from "@langchain/openai";
214
- import { tool } from "@langchain/core/tools";
215
- import { createReactAgent } from "@langchain/langgraph/prebuilt";
216
- import { z } from "zod";
312
+ Connecting agents to messaging platforms traditionally involves complex processes like setting up servers, configuring webhooks, and dealing with platform APIs. Most solutions rely on SMS or WhatsApp, which can be unintuitive for many users.
217
313
 
218
- const calculator = tool(
219
- async ({ expression }: { expression: string }) => {
220
- return String(eval(expression));
221
- },
222
- {
223
- name: "calculator",
224
- description: "Evaluate a math expression",
225
- schema: z.object({ expression: z.string() }),
226
- }
227
- );
314
+ **Flux solves these problems:**
228
315
 
229
- const getTime = tool(
230
- async () => new Date().toLocaleTimeString(),
231
- {
232
- name: "get_time",
233
- description: "Get the current time",
234
- schema: z.object({}),
235
- }
236
- );
316
+ - **Deploy in < 5 seconds** — Link your LangChain agent to iMessage with a single command
317
+ - **Fully iMessage native** — Direct iMessage integration, not SMS or WhatsApp
318
+ - **Zero Infrastructure** — No servers to manage, webhooks to configure, or Apple Developer account needed
319
+ - **Open source** — Fully community-driven and transparent
320
+ - **Free to use** — No subscription fees or hidden costs
237
321
 
238
- const llm = new ChatOpenAI({ modelName: "gpt-4o-mini" });
239
- const agent = createReactAgent({ llm, tools: [calculator, getTime] });
322
+ ## 👤 Single-User Design
240
323
 
241
- export default {
242
- async invoke({ message }: { message: string }) {
243
- const result = await agent.invoke({ messages: [{ role: "user", content: message }] });
244
- return result.messages[result.messages.length - 1].content as string;
245
- }
246
- };
247
- ```
324
+ **Important:** Flux is designed for personal use and development. When you deploy an agent with Flux, **only your registered phone number** can interact with it via iMessage. This means:
248
325
 
249
- <!-- --- -->
326
+ - Perfect for personal assistants and prototypes
327
+ - ✅ Great for testing and development
328
+ - ✅ Simple single-user experience
329
+ - ❌ Not designed for multi-user conversations
330
+ - ❌ No enterprise-level user management
250
331
 
251
- ## Requirements
332
+ ### Need Enterprise Support?
252
333
 
253
- - **Node.js** 18+ (for the CLI)
254
- - **Python** 3.9+ (for the agent)
255
- - **LLM Keys (e.g. OpenAI API key)**
334
+ For **enterprise-level iMessage agents** with advanced features, consider our [Advanced iMessage Kit](https://github.com/photon-hq/advanced-imessage-kit):
256
335
 
257
- <!-- --- -->
336
+ - **Multi-user support** — Handle thousands of users simultaneously
337
+ - **Dedicated phone line** — Get your own iMessage number
338
+ - **Enterprise features** — Advanced conversation management and analytics
339
+ - **Production-ready** — Enhanced stability and performance
340
+ - **More features** — Additional tools and integrations
258
341
 
342
+ [**Explore Advanced iMessage Kit →**](https://github.com/photon-hq/advanced-imessage-kit)
259
343
 
260
- <div align="center">
344
+ ## ⚙️ Requirements
345
+
346
+ - **Node.js** 18+ or **Bun** (for the CLI)
347
+ - **Python** 3.9+ (for the agent, if using Python-based LangChain)
348
+ - **LLM API Keys** (e.g., OpenAI API key for GPT-powered agents)
349
+
350
+ ## 🤝 Contributing
351
+
352
+ We welcome contributions! Flux is fully open source and community-driven. Feel free to:
353
+
354
+ - 🐛 [Report bugs](https://github.com/photon-hq/flux/issues)
355
+ - 💡 [Request features](https://github.com/photon-hq/flux/issues)
356
+ - 🔧 Submit pull requests
357
+ - ⭐ Star the repository
358
+
359
+ ## 💬 Support
360
+
361
+ - **Discord**: Join our community at [discord.gg/bZd4CMd2H5](https://discord.gg/bZd4CMd2H5)
362
+ - **Issues**: Report problems on [GitHub Issues](https://github.com/photon-hq/flux/issues)
363
+ - **Documentation**: Check out this README for comprehensive guides
364
+
365
+ ## 📄 License
366
+
367
+ MIT License - see the [LICENSE](./LICENSE) file for details.
368
+
369
+ <br /><div align="center">
261
370
  <sub>Built with ⚡ by <a href="https://photon.codes">Photon</a></sub>
262
- </div>
371
+ </div>
package/dist/index.js CHANGED
@@ -52115,6 +52115,13 @@ var FluxService = class extends Service("FluxService") {
52115
52115
 
52116
52116
  // src/flux-client.ts
52117
52117
  var GRPC_SERVER_ADDRESS = process.env.FLUX_SERVER_ADDRESS || "fluxy.photon.codes:443";
52118
+ function splitIntoMessages(response) {
52119
+ if (!response.includes("\n")) {
52120
+ return [response];
52121
+ }
52122
+ const parts = response.split("\n").map((p) => p.trim()).filter((p) => p);
52123
+ return parts.length > 0 ? parts : [response];
52124
+ }
52118
52125
  var FluxClient = class {
52119
52126
  client = null;
52120
52127
  phoneNumber;
@@ -52148,7 +52155,8 @@ var FluxClient = class {
52148
52155
  async startMessageStream() {
52149
52156
  if (!this.client) return;
52150
52157
  (async () => {
52151
- for await (const [message] of this.client.FluxService.messageStream) {
52158
+ const stream = this.client.FluxService.messageStream;
52159
+ for await (const [message] of stream) {
52152
52160
  if ("ack" in message) {
52153
52161
  console.log(`[FLUX] Received ack: ${message.ack}`);
52154
52162
  } else {
@@ -52156,7 +52164,10 @@ var FluxClient = class {
52156
52164
  await this.client.FluxService.messageStream({ ack: message.messageGuid });
52157
52165
  const response = await this.onMessage(message);
52158
52166
  if (response) {
52159
- await this.sendMessage(message.userPhoneNumber, response, message.chatGuid);
52167
+ const messages = splitIntoMessages(response);
52168
+ for (const msg of messages) {
52169
+ await this.sendMessage(message.userPhoneNumber, msg, message.chatGuid);
52170
+ }
52160
52171
  }
52161
52172
  }
52162
52173
  }
@@ -52384,10 +52395,18 @@ async function loadAgent(agentPath) {
52384
52395
  }
52385
52396
  const moduleUrl = url.pathToFileURL(agentPath).href;
52386
52397
  const agentModule = await import(moduleUrl);
52387
- return agentModule.default;
52398
+ const agent = agentModule.default;
52399
+ return agent;
52388
52400
  }
52389
52401
 
52390
52402
  // src/index.ts
52403
+ function splitIntoMessages2(response) {
52404
+ if (!response.includes("\n")) {
52405
+ return [response];
52406
+ }
52407
+ const parts = response.split("\n").map((p) => p.trim()).filter((p) => p);
52408
+ return parts.length > 0 ? parts : [response];
52409
+ }
52391
52410
  async function validateCommand() {
52392
52411
  const agentPath = findAgentFile();
52393
52412
  if (!agentPath) {
@@ -52418,6 +52437,9 @@ async function runLocal() {
52418
52437
  process.exit(1);
52419
52438
  }
52420
52439
  const agent = await loadAgent(agentPath);
52440
+ if (agent.onInit) {
52441
+ await agent.onInit();
52442
+ }
52421
52443
  console.log("\n[FLUX] Welcome to Flux! Your agent is loaded.");
52422
52444
  console.log("[FLUX] Type a message to test it. Press Ctrl+C to exit.\n");
52423
52445
  const rl = readline__namespace.createInterface({
@@ -52436,17 +52458,26 @@ async function runLocal() {
52436
52458
  message: input,
52437
52459
  userPhoneNumber: "+1234567890"
52438
52460
  });
52439
- console.log(`Agent: ${response}
52440
- `);
52461
+ const messages = splitIntoMessages2(response);
52462
+ for (const msg of messages) {
52463
+ console.log(`Agent: ${msg}`);
52464
+ }
52465
+ console.log();
52441
52466
  } catch (error) {
52442
52467
  console.log(`[FLUX] Error: ${error.message}
52443
52468
  `);
52469
+ if (agent.onError) {
52470
+ await agent.onError(error);
52471
+ }
52444
52472
  }
52445
52473
  askQuestion();
52446
52474
  });
52447
52475
  };
52448
- rl.on("close", () => {
52476
+ rl.on("close", async () => {
52449
52477
  console.log("\n[FLUX] Goodbye!");
52478
+ if (agent.onShutdown) {
52479
+ await agent.onShutdown();
52480
+ }
52450
52481
  process.exit(0);
52451
52482
  });
52452
52483
  askQuestion();
@@ -52484,11 +52515,25 @@ async function runProd() {
52484
52515
  });
52485
52516
  await flux.connect();
52486
52517
  await flux.register();
52518
+ if (agent.onInit) {
52519
+ console.log("[FLUX] Initializing agent with proactive messaging support...");
52520
+ await agent.onInit(async (to, text) => {
52521
+ const messages = splitIntoMessages2(text);
52522
+ for (const msg of messages) {
52523
+ const success = await flux.sendMessage(to, msg);
52524
+ if (!success) return false;
52525
+ }
52526
+ return true;
52527
+ });
52528
+ }
52487
52529
  console.log("[FLUX] Agent running in production mode. Press Ctrl+C to stop.");
52488
52530
  console.log(`[FLUX] Messages to ${phoneNumber} will be processed by your agent.
52489
52531
  `);
52490
52532
  process.on("SIGINT", async () => {
52491
52533
  console.log("\n[FLUX] Shutting down...");
52534
+ if (agent.onShutdown) {
52535
+ await agent.onShutdown();
52536
+ }
52492
52537
  await flux.disconnect();
52493
52538
  process.exit(0);
52494
52539
  });