@optifye/dashboard-core 6.2.0 → 6.3.1
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/index.d.mts +3 -72
- package/dist/index.d.ts +3 -72
- package/dist/index.js +219 -388
- package/dist/index.mjs +220 -388
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -21,9 +21,9 @@ import jsPDF, { jsPDF as jsPDF$1 } from 'jspdf';
|
|
|
21
21
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
22
22
|
import videojs from 'video.js';
|
|
23
23
|
import 'video.js/dist/video-js.css';
|
|
24
|
-
import { Readable } from 'stream';
|
|
25
24
|
import { toast } from 'sonner';
|
|
26
25
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
26
|
+
import { Readable } from 'stream';
|
|
27
27
|
|
|
28
28
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
29
29
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
@@ -7263,7 +7263,7 @@ var S3ClipsService = class {
|
|
|
7263
7263
|
"best_cycle_time",
|
|
7264
7264
|
"worst_cycle_time",
|
|
7265
7265
|
"long_cycle_time",
|
|
7266
|
-
"
|
|
7266
|
+
"cycle_completion",
|
|
7267
7267
|
"bottleneck"
|
|
7268
7268
|
];
|
|
7269
7269
|
console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId}`);
|
|
@@ -29463,386 +29463,6 @@ var FactoryView = ({
|
|
|
29463
29463
|
};
|
|
29464
29464
|
var AuthenticatedFactoryView = withAuth(FactoryView);
|
|
29465
29465
|
var FactoryView_default = FactoryView;
|
|
29466
|
-
|
|
29467
|
-
// src/lib/api/optifye-agent.ts
|
|
29468
|
-
var OPTIFYE_API_URL = "https://optifye-agent-production.up.railway.app";
|
|
29469
|
-
var OptifyeAgentClient = class {
|
|
29470
|
-
constructor(apiUrl = OPTIFYE_API_URL) {
|
|
29471
|
-
this.apiUrl = apiUrl;
|
|
29472
|
-
}
|
|
29473
|
-
/**
|
|
29474
|
-
* Call Optifye Agent for manufacturing analysis
|
|
29475
|
-
*/
|
|
29476
|
-
async getManufacturingInsights(userQuestion, lineId, shiftId, companyId, context) {
|
|
29477
|
-
try {
|
|
29478
|
-
const requestData = {
|
|
29479
|
-
prompt: userQuestion,
|
|
29480
|
-
line_id: lineId,
|
|
29481
|
-
shift_id: shiftId,
|
|
29482
|
-
company_id: companyId
|
|
29483
|
-
};
|
|
29484
|
-
if (context) {
|
|
29485
|
-
requestData.context = context;
|
|
29486
|
-
}
|
|
29487
|
-
console.log("[OptifyeAgent] Sending request:", requestData);
|
|
29488
|
-
const response = await fetch(`${this.apiUrl}/api/custom-analysis`, {
|
|
29489
|
-
method: "POST",
|
|
29490
|
-
headers: {
|
|
29491
|
-
"Content-Type": "application/json"
|
|
29492
|
-
},
|
|
29493
|
-
body: JSON.stringify(requestData),
|
|
29494
|
-
signal: AbortSignal.timeout(6e4)
|
|
29495
|
-
// 60 second timeout
|
|
29496
|
-
});
|
|
29497
|
-
const responseText = await response.text();
|
|
29498
|
-
console.log("[OptifyeAgent] Response status:", response.status);
|
|
29499
|
-
console.log("[OptifyeAgent] Response text:", responseText);
|
|
29500
|
-
let result;
|
|
29501
|
-
try {
|
|
29502
|
-
result = JSON.parse(responseText);
|
|
29503
|
-
} catch (parseError) {
|
|
29504
|
-
console.error("[OptifyeAgent] Failed to parse response as JSON:", parseError);
|
|
29505
|
-
if (responseText.includes("'Agent' object has no attribute")) {
|
|
29506
|
-
return {
|
|
29507
|
-
success: false,
|
|
29508
|
-
error: "Server error: The AI agent service is experiencing issues. Please try again later or contact support."
|
|
29509
|
-
};
|
|
29510
|
-
}
|
|
29511
|
-
return {
|
|
29512
|
-
success: false,
|
|
29513
|
-
error: `Invalid response format from server: ${responseText.substring(0, 200)}`
|
|
29514
|
-
};
|
|
29515
|
-
}
|
|
29516
|
-
if (response.ok) {
|
|
29517
|
-
return {
|
|
29518
|
-
success: result.success ?? true,
|
|
29519
|
-
analysis: result.analysis || result.response || result.message || result.data,
|
|
29520
|
-
timestamp: result.timestamp,
|
|
29521
|
-
error: result.error
|
|
29522
|
-
};
|
|
29523
|
-
} else {
|
|
29524
|
-
const errorMsg = result.error || result.message || `API request failed: ${response.status}`;
|
|
29525
|
-
console.error("[OptifyeAgent] API error:", errorMsg);
|
|
29526
|
-
if (errorMsg.includes("'Agent' object has no attribute")) {
|
|
29527
|
-
return {
|
|
29528
|
-
success: false,
|
|
29529
|
-
error: "The AI service is currently being updated. Please try again in a few moments."
|
|
29530
|
-
};
|
|
29531
|
-
}
|
|
29532
|
-
return {
|
|
29533
|
-
success: false,
|
|
29534
|
-
error: errorMsg
|
|
29535
|
-
};
|
|
29536
|
-
}
|
|
29537
|
-
} catch (error) {
|
|
29538
|
-
const errorMsg = error instanceof Error ? `Failed to connect to Optifye Agent: ${error.message}` : "Failed to connect to Optifye Agent";
|
|
29539
|
-
console.error("[OptifyeAgent] Request failed:", error);
|
|
29540
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
29541
|
-
return {
|
|
29542
|
-
success: false,
|
|
29543
|
-
error: "Request timed out. The AI agent is taking too long to respond. Please try again."
|
|
29544
|
-
};
|
|
29545
|
-
}
|
|
29546
|
-
return {
|
|
29547
|
-
success: false,
|
|
29548
|
-
error: errorMsg
|
|
29549
|
-
};
|
|
29550
|
-
}
|
|
29551
|
-
}
|
|
29552
|
-
/**
|
|
29553
|
-
* Check if Optifye Agent API is healthy
|
|
29554
|
-
*/
|
|
29555
|
-
async checkHealth() {
|
|
29556
|
-
try {
|
|
29557
|
-
const response = await fetch(`${this.apiUrl}/health`, {
|
|
29558
|
-
signal: AbortSignal.timeout(1e4)
|
|
29559
|
-
// 10 second timeout
|
|
29560
|
-
});
|
|
29561
|
-
if (response.ok) {
|
|
29562
|
-
const healthData = await response.json();
|
|
29563
|
-
return healthData.status === "healthy";
|
|
29564
|
-
}
|
|
29565
|
-
return false;
|
|
29566
|
-
} catch (error) {
|
|
29567
|
-
console.error("[OptifyeAgent] Health check failed:", error);
|
|
29568
|
-
return false;
|
|
29569
|
-
}
|
|
29570
|
-
}
|
|
29571
|
-
};
|
|
29572
|
-
var optifyeAgentClient = new OptifyeAgentClient();
|
|
29573
|
-
async function getManufacturingInsights(userQuestion, lineId, shiftId, companyId, context) {
|
|
29574
|
-
if (!userQuestion || !userQuestion.trim()) {
|
|
29575
|
-
return {
|
|
29576
|
-
success: false,
|
|
29577
|
-
error: "Please provide a question"
|
|
29578
|
-
};
|
|
29579
|
-
}
|
|
29580
|
-
if (!lineId || !companyId) {
|
|
29581
|
-
console.warn("[OptifyeAgent] Missing required IDs:", { lineId, companyId });
|
|
29582
|
-
}
|
|
29583
|
-
return optifyeAgentClient.getManufacturingInsights(
|
|
29584
|
-
userQuestion,
|
|
29585
|
-
lineId,
|
|
29586
|
-
shiftId,
|
|
29587
|
-
companyId,
|
|
29588
|
-
context
|
|
29589
|
-
);
|
|
29590
|
-
}
|
|
29591
|
-
function createStreamProxyHandler(config) {
|
|
29592
|
-
const cloudFrontDomain = config?.cloudFrontDomain || "https://d1eiob0chi5jw.cloudfront.net";
|
|
29593
|
-
return async function handler(req, res) {
|
|
29594
|
-
if (req.method !== "GET") {
|
|
29595
|
-
return res.status(405).json({ error: "Method not allowed" });
|
|
29596
|
-
}
|
|
29597
|
-
const { path } = req.query;
|
|
29598
|
-
if (!path || !Array.isArray(path)) {
|
|
29599
|
-
return res.status(400).json({ error: "Missing path parameter" });
|
|
29600
|
-
}
|
|
29601
|
-
const fullPath = path.join("/");
|
|
29602
|
-
const targetUrl = `${cloudFrontDomain}/${fullPath}`;
|
|
29603
|
-
try {
|
|
29604
|
-
console.log(`[HLS Stream Proxy] Streaming: ${targetUrl}`);
|
|
29605
|
-
const response = await fetch(targetUrl, {
|
|
29606
|
-
method: "GET",
|
|
29607
|
-
headers: {
|
|
29608
|
-
"User-Agent": "NextJS-HLS-Stream-Proxy",
|
|
29609
|
-
// Pass through range headers for partial content requests
|
|
29610
|
-
...req.headers.range && { "Range": req.headers.range }
|
|
29611
|
-
}
|
|
29612
|
-
});
|
|
29613
|
-
if (!response.ok) {
|
|
29614
|
-
console.error(`[HLS Stream Proxy] CloudFront error: ${response.status} ${response.statusText}`);
|
|
29615
|
-
return res.status(response.status).json({
|
|
29616
|
-
error: `CloudFront request failed: ${response.statusText}`
|
|
29617
|
-
});
|
|
29618
|
-
}
|
|
29619
|
-
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
|
29620
|
-
const contentLength = response.headers.get("content-length");
|
|
29621
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
29622
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD");
|
|
29623
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Range");
|
|
29624
|
-
res.setHeader("Access-Control-Expose-Headers", "Content-Length, Content-Range");
|
|
29625
|
-
res.setHeader("Content-Type", contentType);
|
|
29626
|
-
if (contentLength) {
|
|
29627
|
-
res.setHeader("Content-Length", contentLength);
|
|
29628
|
-
}
|
|
29629
|
-
if (fullPath.endsWith(".m3u8")) {
|
|
29630
|
-
res.setHeader("Content-Type", "application/vnd.apple.mpegurl");
|
|
29631
|
-
} else if (fullPath.endsWith(".ts")) {
|
|
29632
|
-
res.setHeader("Content-Type", "video/mp2t");
|
|
29633
|
-
}
|
|
29634
|
-
if (response.status === 206) {
|
|
29635
|
-
res.status(206);
|
|
29636
|
-
const contentRange = response.headers.get("content-range");
|
|
29637
|
-
if (contentRange) {
|
|
29638
|
-
res.setHeader("Content-Range", contentRange);
|
|
29639
|
-
}
|
|
29640
|
-
res.setHeader("Accept-Ranges", "bytes");
|
|
29641
|
-
}
|
|
29642
|
-
if (response.body) {
|
|
29643
|
-
const reader = response.body.getReader();
|
|
29644
|
-
const nodeStream = new Readable({
|
|
29645
|
-
async read() {
|
|
29646
|
-
try {
|
|
29647
|
-
const { done, value } = await reader.read();
|
|
29648
|
-
if (done) {
|
|
29649
|
-
this.push(null);
|
|
29650
|
-
} else {
|
|
29651
|
-
this.push(Buffer.from(value));
|
|
29652
|
-
}
|
|
29653
|
-
} catch (error) {
|
|
29654
|
-
console.error("[HLS Stream Proxy] Stream read error:", error);
|
|
29655
|
-
this.destroy(error);
|
|
29656
|
-
}
|
|
29657
|
-
}
|
|
29658
|
-
});
|
|
29659
|
-
nodeStream.pipe(res);
|
|
29660
|
-
req.on("close", () => {
|
|
29661
|
-
reader.cancel();
|
|
29662
|
-
nodeStream.destroy();
|
|
29663
|
-
});
|
|
29664
|
-
} else {
|
|
29665
|
-
const buffer = await response.arrayBuffer();
|
|
29666
|
-
res.send(Buffer.from(buffer));
|
|
29667
|
-
}
|
|
29668
|
-
} catch (error) {
|
|
29669
|
-
console.error("[HLS Stream Proxy] Error:", error);
|
|
29670
|
-
res.status(500).json({
|
|
29671
|
-
error: "Failed to stream from CloudFront",
|
|
29672
|
-
details: error instanceof Error ? error.message : String(error)
|
|
29673
|
-
});
|
|
29674
|
-
}
|
|
29675
|
-
};
|
|
29676
|
-
}
|
|
29677
|
-
var streamProxyConfig = {
|
|
29678
|
-
api: {
|
|
29679
|
-
bodyParser: false,
|
|
29680
|
-
responseLimit: false
|
|
29681
|
-
}
|
|
29682
|
-
};
|
|
29683
|
-
|
|
29684
|
-
// src/lib/api/slackApi.ts
|
|
29685
|
-
var SlackAPI = class {
|
|
29686
|
-
/**
|
|
29687
|
-
* Sends a support ticket notification to Slack.
|
|
29688
|
-
* 1. If slackWebhookUrl is configured, send directly (preferred)
|
|
29689
|
-
* 2. Otherwise, if slackProxyEndpoint is configured, send to the proxy
|
|
29690
|
-
* If neither is configured, logs a warning.
|
|
29691
|
-
*/
|
|
29692
|
-
static async sendSupportTicketNotification(ticket) {
|
|
29693
|
-
try {
|
|
29694
|
-
const config = _getDashboardConfigInstance();
|
|
29695
|
-
const endpointsConfig = config.endpoints ?? DEFAULT_ENDPOINTS_CONFIG;
|
|
29696
|
-
const { slackWebhookUrl, slackProxyEndpoint } = endpointsConfig;
|
|
29697
|
-
console.log("SlackAPI Debug - Configuration check:", {
|
|
29698
|
-
hasConfig: !!config,
|
|
29699
|
-
hasEndpoints: !!config.endpoints,
|
|
29700
|
-
slackWebhookUrl: slackWebhookUrl ? "configured" : "not configured",
|
|
29701
|
-
slackProxyEndpoint: slackProxyEndpoint ? "configured" : "not configured",
|
|
29702
|
-
envVariable: process.env.SLACK_WEBHOOK_URL ? "set" : "not set",
|
|
29703
|
-
publicEnvVariable: process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL ? "set" : "not set"
|
|
29704
|
-
});
|
|
29705
|
-
if (slackProxyEndpoint) {
|
|
29706
|
-
const slackMessage = this.formatSlackMessage(ticket);
|
|
29707
|
-
const response = await fetch(slackProxyEndpoint, {
|
|
29708
|
-
method: "POST",
|
|
29709
|
-
headers: {
|
|
29710
|
-
"Content-Type": "application/json"
|
|
29711
|
-
},
|
|
29712
|
-
body: JSON.stringify(slackMessage)
|
|
29713
|
-
});
|
|
29714
|
-
if (!response.ok) {
|
|
29715
|
-
const errorData = await response.json().catch(() => ({ message: "Unknown error" }));
|
|
29716
|
-
throw new Error(errorData.message || `Proxy failed with status: ${response.status}`);
|
|
29717
|
-
}
|
|
29718
|
-
console.log("Support ticket notification sent to Slack successfully via proxy");
|
|
29719
|
-
return;
|
|
29720
|
-
}
|
|
29721
|
-
if (slackWebhookUrl) {
|
|
29722
|
-
const slackMessage = this.formatSlackMessage(ticket);
|
|
29723
|
-
const response = await fetch(slackWebhookUrl, {
|
|
29724
|
-
method: "POST",
|
|
29725
|
-
headers: {
|
|
29726
|
-
"Content-Type": "application/json"
|
|
29727
|
-
},
|
|
29728
|
-
body: JSON.stringify(slackMessage)
|
|
29729
|
-
});
|
|
29730
|
-
if (!response.ok) {
|
|
29731
|
-
const errorText = await response.text();
|
|
29732
|
-
console.error("Slack webhook error:", errorText);
|
|
29733
|
-
throw new Error(`Slack webhook failed with status: ${response.status}`);
|
|
29734
|
-
}
|
|
29735
|
-
console.log("Support ticket notification sent to Slack successfully via webhook");
|
|
29736
|
-
return;
|
|
29737
|
-
}
|
|
29738
|
-
console.warn("Slack notification skipped: No webhook or proxy endpoint configured");
|
|
29739
|
-
console.info("To fix this, either:");
|
|
29740
|
-
console.info("1. Set up a server-side proxy endpoint and configure slackProxyEndpoint in your dashboard config");
|
|
29741
|
-
console.info("2. For development only: use NEXT_PUBLIC_SLACK_WEBHOOK_URL (not recommended for production)");
|
|
29742
|
-
console.info("3. Configure the webhook URL directly in your dashboard config at runtime");
|
|
29743
|
-
} catch (error) {
|
|
29744
|
-
console.error("Failed to send Slack notification:", error);
|
|
29745
|
-
throw error;
|
|
29746
|
-
}
|
|
29747
|
-
}
|
|
29748
|
-
/**
|
|
29749
|
-
* Formats a support ticket into a Slack message format
|
|
29750
|
-
* This can be used by server-side implementations
|
|
29751
|
-
*/
|
|
29752
|
-
static formatSlackMessage(ticket) {
|
|
29753
|
-
const timestamp = new Date(ticket.timestamp).toLocaleString("en-US", {
|
|
29754
|
-
dateStyle: "medium",
|
|
29755
|
-
timeStyle: "short",
|
|
29756
|
-
timeZone: "UTC"
|
|
29757
|
-
});
|
|
29758
|
-
const maxDescriptionLength = 300;
|
|
29759
|
-
const description = ticket.description.length > maxDescriptionLength ? `${ticket.description.substring(0, maxDescriptionLength)}...` : ticket.description;
|
|
29760
|
-
const categoryConfig = {
|
|
29761
|
-
general: { emoji: "\u{1F4AC}", color: "#36a64f" },
|
|
29762
|
-
technical: { emoji: "\u{1F527}", color: "#ff6b6b" },
|
|
29763
|
-
feature: { emoji: "\u2728", color: "#4ecdc4" },
|
|
29764
|
-
billing: { emoji: "\u{1F4B3}", color: "#f7b731" }
|
|
29765
|
-
};
|
|
29766
|
-
const priorityConfig = {
|
|
29767
|
-
low: { emoji: "\u{1F7E2}", color: "#36a64f" },
|
|
29768
|
-
normal: { emoji: "\u{1F7E1}", color: "#ffc107" },
|
|
29769
|
-
high: { emoji: "\u{1F7E0}", color: "#ff8c00" },
|
|
29770
|
-
urgent: { emoji: "\u{1F534}", color: "#dc3545" }
|
|
29771
|
-
};
|
|
29772
|
-
const categoryInfo = categoryConfig[ticket.category] || categoryConfig.general;
|
|
29773
|
-
const priorityInfo = priorityConfig[ticket.priority] || priorityConfig.normal;
|
|
29774
|
-
const messageColor = ticket.priority === "high" || ticket.priority === "urgent" ? priorityInfo.color : categoryInfo.color;
|
|
29775
|
-
return {
|
|
29776
|
-
text: `New support ticket from ${ticket.email}`,
|
|
29777
|
-
blocks: [
|
|
29778
|
-
{
|
|
29779
|
-
type: "header",
|
|
29780
|
-
text: {
|
|
29781
|
-
type: "plain_text",
|
|
29782
|
-
text: "\u{1F3AB} New Support Ticket Submitted",
|
|
29783
|
-
emoji: true
|
|
29784
|
-
}
|
|
29785
|
-
},
|
|
29786
|
-
{
|
|
29787
|
-
type: "section",
|
|
29788
|
-
fields: [
|
|
29789
|
-
{
|
|
29790
|
-
type: "mrkdwn",
|
|
29791
|
-
text: `*From:*
|
|
29792
|
-
${ticket.email}`
|
|
29793
|
-
},
|
|
29794
|
-
{
|
|
29795
|
-
type: "mrkdwn",
|
|
29796
|
-
text: `*Category:*
|
|
29797
|
-
${categoryInfo.emoji} ${ticket.category.charAt(0).toUpperCase() + ticket.category.slice(1)}`
|
|
29798
|
-
},
|
|
29799
|
-
{
|
|
29800
|
-
type: "mrkdwn",
|
|
29801
|
-
text: `*Priority:*
|
|
29802
|
-
${priorityInfo.emoji} ${ticket.priority.charAt(0).toUpperCase() + ticket.priority.slice(1)}`
|
|
29803
|
-
},
|
|
29804
|
-
{
|
|
29805
|
-
type: "mrkdwn",
|
|
29806
|
-
text: `*Subject:*
|
|
29807
|
-
${ticket.subject}`
|
|
29808
|
-
},
|
|
29809
|
-
{
|
|
29810
|
-
type: "mrkdwn",
|
|
29811
|
-
text: `*Submitted:*
|
|
29812
|
-
${timestamp} UTC`
|
|
29813
|
-
}
|
|
29814
|
-
]
|
|
29815
|
-
},
|
|
29816
|
-
{
|
|
29817
|
-
type: "section",
|
|
29818
|
-
text: {
|
|
29819
|
-
type: "mrkdwn",
|
|
29820
|
-
text: `*Description:*
|
|
29821
|
-
${description}`
|
|
29822
|
-
}
|
|
29823
|
-
},
|
|
29824
|
-
{
|
|
29825
|
-
type: "divider"
|
|
29826
|
-
},
|
|
29827
|
-
{
|
|
29828
|
-
type: "context",
|
|
29829
|
-
elements: [
|
|
29830
|
-
{
|
|
29831
|
-
type: "mrkdwn",
|
|
29832
|
-
text: `Category: \`${ticket.category}\` | Priority: ${priorityInfo.emoji} \`${ticket.priority.toUpperCase()}\` | ${ticket.priority === "urgent" ? "\u26A0\uFE0F Requires immediate attention" : "Standard processing"}`
|
|
29833
|
-
}
|
|
29834
|
-
]
|
|
29835
|
-
}
|
|
29836
|
-
],
|
|
29837
|
-
attachments: [
|
|
29838
|
-
{
|
|
29839
|
-
color: messageColor,
|
|
29840
|
-
fields: []
|
|
29841
|
-
}
|
|
29842
|
-
]
|
|
29843
|
-
};
|
|
29844
|
-
}
|
|
29845
|
-
};
|
|
29846
29466
|
var HelpView = ({
|
|
29847
29467
|
onTicketSubmit,
|
|
29848
29468
|
supportEmail = "support@optifye.com"
|
|
@@ -29907,11 +29527,6 @@ var HelpView = ({
|
|
|
29907
29527
|
category: ticket.category,
|
|
29908
29528
|
priority: ticket.priority
|
|
29909
29529
|
});
|
|
29910
|
-
try {
|
|
29911
|
-
await SlackAPI.sendSupportTicketNotification(ticket);
|
|
29912
|
-
} catch (slackError) {
|
|
29913
|
-
console.error("Failed to send Slack notification:", slackError);
|
|
29914
|
-
}
|
|
29915
29530
|
if (onTicketSubmit) {
|
|
29916
29531
|
await onTicketSubmit(ticket);
|
|
29917
29532
|
} else {
|
|
@@ -35877,4 +35492,221 @@ var S3Service = class {
|
|
|
35877
35492
|
}
|
|
35878
35493
|
};
|
|
35879
35494
|
|
|
35880
|
-
|
|
35495
|
+
// src/lib/api/optifye-agent.ts
|
|
35496
|
+
var OPTIFYE_API_URL = "https://optifye-agent-production.up.railway.app";
|
|
35497
|
+
var OptifyeAgentClient = class {
|
|
35498
|
+
constructor(apiUrl = OPTIFYE_API_URL) {
|
|
35499
|
+
this.apiUrl = apiUrl;
|
|
35500
|
+
}
|
|
35501
|
+
/**
|
|
35502
|
+
* Call Optifye Agent for manufacturing analysis
|
|
35503
|
+
*/
|
|
35504
|
+
async getManufacturingInsights(userQuestion, lineId, shiftId, companyId, context) {
|
|
35505
|
+
try {
|
|
35506
|
+
const requestData = {
|
|
35507
|
+
prompt: userQuestion,
|
|
35508
|
+
line_id: lineId,
|
|
35509
|
+
shift_id: shiftId,
|
|
35510
|
+
company_id: companyId
|
|
35511
|
+
};
|
|
35512
|
+
if (context) {
|
|
35513
|
+
requestData.context = context;
|
|
35514
|
+
}
|
|
35515
|
+
console.log("[OptifyeAgent] Sending request:", requestData);
|
|
35516
|
+
const response = await fetch(`${this.apiUrl}/api/custom-analysis`, {
|
|
35517
|
+
method: "POST",
|
|
35518
|
+
headers: {
|
|
35519
|
+
"Content-Type": "application/json"
|
|
35520
|
+
},
|
|
35521
|
+
body: JSON.stringify(requestData),
|
|
35522
|
+
signal: AbortSignal.timeout(6e4)
|
|
35523
|
+
// 60 second timeout
|
|
35524
|
+
});
|
|
35525
|
+
const responseText = await response.text();
|
|
35526
|
+
console.log("[OptifyeAgent] Response status:", response.status);
|
|
35527
|
+
console.log("[OptifyeAgent] Response text:", responseText);
|
|
35528
|
+
let result;
|
|
35529
|
+
try {
|
|
35530
|
+
result = JSON.parse(responseText);
|
|
35531
|
+
} catch (parseError) {
|
|
35532
|
+
console.error("[OptifyeAgent] Failed to parse response as JSON:", parseError);
|
|
35533
|
+
if (responseText.includes("'Agent' object has no attribute")) {
|
|
35534
|
+
return {
|
|
35535
|
+
success: false,
|
|
35536
|
+
error: "Server error: The AI agent service is experiencing issues. Please try again later or contact support."
|
|
35537
|
+
};
|
|
35538
|
+
}
|
|
35539
|
+
return {
|
|
35540
|
+
success: false,
|
|
35541
|
+
error: `Invalid response format from server: ${responseText.substring(0, 200)}`
|
|
35542
|
+
};
|
|
35543
|
+
}
|
|
35544
|
+
if (response.ok) {
|
|
35545
|
+
return {
|
|
35546
|
+
success: result.success ?? true,
|
|
35547
|
+
analysis: result.analysis || result.response || result.message || result.data,
|
|
35548
|
+
timestamp: result.timestamp,
|
|
35549
|
+
error: result.error
|
|
35550
|
+
};
|
|
35551
|
+
} else {
|
|
35552
|
+
const errorMsg = result.error || result.message || `API request failed: ${response.status}`;
|
|
35553
|
+
console.error("[OptifyeAgent] API error:", errorMsg);
|
|
35554
|
+
if (errorMsg.includes("'Agent' object has no attribute")) {
|
|
35555
|
+
return {
|
|
35556
|
+
success: false,
|
|
35557
|
+
error: "The AI service is currently being updated. Please try again in a few moments."
|
|
35558
|
+
};
|
|
35559
|
+
}
|
|
35560
|
+
return {
|
|
35561
|
+
success: false,
|
|
35562
|
+
error: errorMsg
|
|
35563
|
+
};
|
|
35564
|
+
}
|
|
35565
|
+
} catch (error) {
|
|
35566
|
+
const errorMsg = error instanceof Error ? `Failed to connect to Optifye Agent: ${error.message}` : "Failed to connect to Optifye Agent";
|
|
35567
|
+
console.error("[OptifyeAgent] Request failed:", error);
|
|
35568
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
35569
|
+
return {
|
|
35570
|
+
success: false,
|
|
35571
|
+
error: "Request timed out. The AI agent is taking too long to respond. Please try again."
|
|
35572
|
+
};
|
|
35573
|
+
}
|
|
35574
|
+
return {
|
|
35575
|
+
success: false,
|
|
35576
|
+
error: errorMsg
|
|
35577
|
+
};
|
|
35578
|
+
}
|
|
35579
|
+
}
|
|
35580
|
+
/**
|
|
35581
|
+
* Check if Optifye Agent API is healthy
|
|
35582
|
+
*/
|
|
35583
|
+
async checkHealth() {
|
|
35584
|
+
try {
|
|
35585
|
+
const response = await fetch(`${this.apiUrl}/health`, {
|
|
35586
|
+
signal: AbortSignal.timeout(1e4)
|
|
35587
|
+
// 10 second timeout
|
|
35588
|
+
});
|
|
35589
|
+
if (response.ok) {
|
|
35590
|
+
const healthData = await response.json();
|
|
35591
|
+
return healthData.status === "healthy";
|
|
35592
|
+
}
|
|
35593
|
+
return false;
|
|
35594
|
+
} catch (error) {
|
|
35595
|
+
console.error("[OptifyeAgent] Health check failed:", error);
|
|
35596
|
+
return false;
|
|
35597
|
+
}
|
|
35598
|
+
}
|
|
35599
|
+
};
|
|
35600
|
+
var optifyeAgentClient = new OptifyeAgentClient();
|
|
35601
|
+
async function getManufacturingInsights(userQuestion, lineId, shiftId, companyId, context) {
|
|
35602
|
+
if (!userQuestion || !userQuestion.trim()) {
|
|
35603
|
+
return {
|
|
35604
|
+
success: false,
|
|
35605
|
+
error: "Please provide a question"
|
|
35606
|
+
};
|
|
35607
|
+
}
|
|
35608
|
+
if (!lineId || !companyId) {
|
|
35609
|
+
console.warn("[OptifyeAgent] Missing required IDs:", { lineId, companyId });
|
|
35610
|
+
}
|
|
35611
|
+
return optifyeAgentClient.getManufacturingInsights(
|
|
35612
|
+
userQuestion,
|
|
35613
|
+
lineId,
|
|
35614
|
+
shiftId,
|
|
35615
|
+
companyId,
|
|
35616
|
+
context
|
|
35617
|
+
);
|
|
35618
|
+
}
|
|
35619
|
+
function createStreamProxyHandler(config) {
|
|
35620
|
+
const cloudFrontDomain = config?.cloudFrontDomain || "https://d1eiob0chi5jw.cloudfront.net";
|
|
35621
|
+
return async function handler(req, res) {
|
|
35622
|
+
if (req.method !== "GET") {
|
|
35623
|
+
return res.status(405).json({ error: "Method not allowed" });
|
|
35624
|
+
}
|
|
35625
|
+
const { path } = req.query;
|
|
35626
|
+
if (!path || !Array.isArray(path)) {
|
|
35627
|
+
return res.status(400).json({ error: "Missing path parameter" });
|
|
35628
|
+
}
|
|
35629
|
+
const fullPath = path.join("/");
|
|
35630
|
+
const targetUrl = `${cloudFrontDomain}/${fullPath}`;
|
|
35631
|
+
try {
|
|
35632
|
+
console.log(`[HLS Stream Proxy] Streaming: ${targetUrl}`);
|
|
35633
|
+
const response = await fetch(targetUrl, {
|
|
35634
|
+
method: "GET",
|
|
35635
|
+
headers: {
|
|
35636
|
+
"User-Agent": "NextJS-HLS-Stream-Proxy",
|
|
35637
|
+
// Pass through range headers for partial content requests
|
|
35638
|
+
...req.headers.range && { "Range": req.headers.range }
|
|
35639
|
+
}
|
|
35640
|
+
});
|
|
35641
|
+
if (!response.ok) {
|
|
35642
|
+
console.error(`[HLS Stream Proxy] CloudFront error: ${response.status} ${response.statusText}`);
|
|
35643
|
+
return res.status(response.status).json({
|
|
35644
|
+
error: `CloudFront request failed: ${response.statusText}`
|
|
35645
|
+
});
|
|
35646
|
+
}
|
|
35647
|
+
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
|
35648
|
+
const contentLength = response.headers.get("content-length");
|
|
35649
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
35650
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD");
|
|
35651
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Range");
|
|
35652
|
+
res.setHeader("Access-Control-Expose-Headers", "Content-Length, Content-Range");
|
|
35653
|
+
res.setHeader("Content-Type", contentType);
|
|
35654
|
+
if (contentLength) {
|
|
35655
|
+
res.setHeader("Content-Length", contentLength);
|
|
35656
|
+
}
|
|
35657
|
+
if (fullPath.endsWith(".m3u8")) {
|
|
35658
|
+
res.setHeader("Content-Type", "application/vnd.apple.mpegurl");
|
|
35659
|
+
} else if (fullPath.endsWith(".ts")) {
|
|
35660
|
+
res.setHeader("Content-Type", "video/mp2t");
|
|
35661
|
+
}
|
|
35662
|
+
if (response.status === 206) {
|
|
35663
|
+
res.status(206);
|
|
35664
|
+
const contentRange = response.headers.get("content-range");
|
|
35665
|
+
if (contentRange) {
|
|
35666
|
+
res.setHeader("Content-Range", contentRange);
|
|
35667
|
+
}
|
|
35668
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
35669
|
+
}
|
|
35670
|
+
if (response.body) {
|
|
35671
|
+
const reader = response.body.getReader();
|
|
35672
|
+
const nodeStream = new Readable({
|
|
35673
|
+
async read() {
|
|
35674
|
+
try {
|
|
35675
|
+
const { done, value } = await reader.read();
|
|
35676
|
+
if (done) {
|
|
35677
|
+
this.push(null);
|
|
35678
|
+
} else {
|
|
35679
|
+
this.push(Buffer.from(value));
|
|
35680
|
+
}
|
|
35681
|
+
} catch (error) {
|
|
35682
|
+
console.error("[HLS Stream Proxy] Stream read error:", error);
|
|
35683
|
+
this.destroy(error);
|
|
35684
|
+
}
|
|
35685
|
+
}
|
|
35686
|
+
});
|
|
35687
|
+
nodeStream.pipe(res);
|
|
35688
|
+
req.on("close", () => {
|
|
35689
|
+
reader.cancel();
|
|
35690
|
+
nodeStream.destroy();
|
|
35691
|
+
});
|
|
35692
|
+
} else {
|
|
35693
|
+
const buffer = await response.arrayBuffer();
|
|
35694
|
+
res.send(Buffer.from(buffer));
|
|
35695
|
+
}
|
|
35696
|
+
} catch (error) {
|
|
35697
|
+
console.error("[HLS Stream Proxy] Error:", error);
|
|
35698
|
+
res.status(500).json({
|
|
35699
|
+
error: "Failed to stream from CloudFront",
|
|
35700
|
+
details: error instanceof Error ? error.message : String(error)
|
|
35701
|
+
});
|
|
35702
|
+
}
|
|
35703
|
+
};
|
|
35704
|
+
}
|
|
35705
|
+
var streamProxyConfig = {
|
|
35706
|
+
api: {
|
|
35707
|
+
bodyParser: false,
|
|
35708
|
+
responseLimit: false
|
|
35709
|
+
}
|
|
35710
|
+
};
|
|
35711
|
+
|
|
35712
|
+
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, ProfileView_default as ProfileView, RegistryProvider, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, cacheService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useTicketHistory, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optifye/dashboard-core",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "Reusable UI & logic for Optifye dashboard",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"@types/mixpanel-browser": "^2.51.0",
|
|
68
68
|
"@types/react": "^19.0.4",
|
|
69
69
|
"@types/react-dom": "^19.0.2",
|
|
70
|
+
"@types/video.js": "^7.3.58",
|
|
70
71
|
"autoprefixer": "^10.4.21",
|
|
71
72
|
"postcss": "^8.5.6",
|
|
72
73
|
"tsup": "^8.0.2",
|