opc-agent 4.0.0 โ 4.0.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/README.md +404 -80
- package/README.zh-CN.md +82 -0
- package/dist/cli/chat.d.ts +2 -0
- package/dist/cli/chat.js +134 -0
- package/dist/cli/setup.d.ts +4 -0
- package/dist/cli/setup.js +303 -0
- package/dist/cli.js +106 -6
- package/dist/hub/brain-seed.d.ts +14 -0
- package/dist/hub/brain-seed.js +77 -0
- package/dist/hub/client.d.ts +25 -0
- package/dist/hub/client.js +44 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +12 -3
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +54 -1
- package/dist/scheduler/cron-engine.d.ts +41 -0
- package/dist/scheduler/cron-engine.js +200 -0
- package/dist/scheduler/index.d.ts +3 -0
- package/dist/scheduler/index.js +7 -0
- package/dist/skills/builtin/index.d.ts +6 -0
- package/dist/skills/builtin/index.js +402 -0
- package/dist/skills/marketplace.d.ts +30 -0
- package/dist/skills/marketplace.js +142 -0
- package/dist/skills/types.d.ts +34 -0
- package/dist/skills/types.js +16 -0
- package/dist/studio/server.d.ts +25 -0
- package/dist/studio/server.js +780 -0
- package/dist/studio/templates-data.d.ts +21 -0
- package/dist/studio/templates-data.js +148 -0
- package/dist/studio-ui/index.html +2502 -1073
- package/dist/tools/builtin/index.d.ts +1 -0
- package/dist/tools/builtin/index.js +7 -2
- package/dist/tools/builtin/web-search.d.ts +9 -0
- package/dist/tools/builtin/web-search.js +150 -0
- package/dist/tools/document-processor.d.ts +39 -0
- package/dist/tools/document-processor.js +188 -0
- package/dist/tools/image-generator.d.ts +42 -0
- package/dist/tools/image-generator.js +136 -0
- package/dist/tools/web-scraper.d.ts +20 -0
- package/dist/tools/web-scraper.js +148 -0
- package/dist/tools/web-search.d.ts +51 -0
- package/dist/tools/web-search.js +152 -0
- package/install.ps1 +154 -0
- package/install.sh +164 -0
- package/package.json +63 -52
- package/src/cli/chat.ts +99 -0
- package/src/cli/setup.ts +314 -0
- package/src/cli.ts +108 -6
- package/src/hub/brain-seed.ts +54 -0
- package/src/hub/client.ts +60 -0
- package/src/index.ts +4 -2
- package/src/providers/index.ts +64 -1
- package/src/scheduler/cron-engine.ts +191 -0
- package/src/scheduler/index.ts +2 -0
- package/src/skills/builtin/index.ts +408 -0
- package/src/skills/marketplace.ts +113 -0
- package/src/skills/types.ts +42 -0
- package/src/studio/server.ts +1591 -791
- package/src/studio/templates-data.ts +178 -0
- package/src/studio-ui/index.html +2502 -1073
- package/src/tools/builtin/index.ts +37 -35
- package/src/tools/builtin/web-search.ts +126 -0
- package/src/tools/document-processor.ts +213 -0
- package/src/tools/image-generator.ts +150 -0
- package/src/tools/web-scraper.ts +179 -0
- package/src/tools/web-search.ts +180 -0
- package/tests/cron-engine.test.ts +101 -0
- package/tests/document-processor.test.ts +69 -0
- package/tests/e2e-nocode.test.ts +442 -0
- package/tests/image-generator.test.ts +84 -0
- package/tests/settings-api.test.ts +148 -0
- package/tests/setup.test.ts +73 -0
- package/tests/studio.test.ts +402 -229
- package/tests/voice-interaction.test.ts +38 -0
- package/tests/web-search.test.ts +155 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Web Scraper - v0.10.0
|
|
4
|
+
* Fetch URL content and extract readable text in markdown format.
|
|
5
|
+
* Uses a simple readability-style extraction (no external dependencies).
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.scrapeUrl = scrapeUrl;
|
|
9
|
+
exports.extractReadableContent = extractReadableContent;
|
|
10
|
+
const MAX_CONTENT_LENGTH = 5000;
|
|
11
|
+
/**
|
|
12
|
+
* Fetch a URL and extract readable content as markdown.
|
|
13
|
+
*/
|
|
14
|
+
async function scrapeUrl(url, maxLength = MAX_CONTENT_LENGTH) {
|
|
15
|
+
const response = await fetch(url, {
|
|
16
|
+
headers: {
|
|
17
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
18
|
+
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
19
|
+
},
|
|
20
|
+
signal: AbortSignal.timeout(15000),
|
|
21
|
+
redirect: 'follow',
|
|
22
|
+
});
|
|
23
|
+
const contentType = response.headers.get('content-type') || '';
|
|
24
|
+
const text = await response.text();
|
|
25
|
+
// If not HTML, return raw text
|
|
26
|
+
if (!contentType.includes('html')) {
|
|
27
|
+
const truncated = text.slice(0, maxLength);
|
|
28
|
+
return {
|
|
29
|
+
title: url,
|
|
30
|
+
content: truncated,
|
|
31
|
+
url,
|
|
32
|
+
wordCount: truncated.split(/\s+/).length,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return extractReadableContent(text, url, maxLength);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extract readable content from HTML using simple heuristics.
|
|
39
|
+
*/
|
|
40
|
+
function extractReadableContent(html, url, maxLength = MAX_CONTENT_LENGTH) {
|
|
41
|
+
// Extract title
|
|
42
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
43
|
+
const title = titleMatch ? decodeEntities(titleMatch[1]).trim() : url;
|
|
44
|
+
// Remove non-content elements
|
|
45
|
+
let content = html;
|
|
46
|
+
// Remove script, style, nav, header, footer, aside, iframe
|
|
47
|
+
const removePatterns = [
|
|
48
|
+
/<script[\s\S]*?<\/script>/gi,
|
|
49
|
+
/<style[\s\S]*?<\/style>/gi,
|
|
50
|
+
/<nav[\s\S]*?<\/nav>/gi,
|
|
51
|
+
/<footer[\s\S]*?<\/footer>/gi,
|
|
52
|
+
/<aside[\s\S]*?<\/aside>/gi,
|
|
53
|
+
/<iframe[\s\S]*?<\/iframe>/gi,
|
|
54
|
+
/<noscript[\s\S]*?<\/noscript>/gi,
|
|
55
|
+
/<!--[\s\S]*?-->/g,
|
|
56
|
+
];
|
|
57
|
+
for (const pattern of removePatterns) {
|
|
58
|
+
content = content.replace(pattern, '');
|
|
59
|
+
}
|
|
60
|
+
// Try to find main content area
|
|
61
|
+
const mainContent = findMainContent(content);
|
|
62
|
+
content = mainContent || content;
|
|
63
|
+
// Convert to markdown-ish text
|
|
64
|
+
content = htmlToMarkdown(content);
|
|
65
|
+
// Clean up whitespace
|
|
66
|
+
content = content
|
|
67
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
68
|
+
.replace(/[ \t]+/g, ' ')
|
|
69
|
+
.trim();
|
|
70
|
+
// Truncate
|
|
71
|
+
if (content.length > maxLength) {
|
|
72
|
+
content = content.slice(0, maxLength) + '\n\n...[truncated]';
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
title,
|
|
76
|
+
content,
|
|
77
|
+
url,
|
|
78
|
+
wordCount: content.split(/\s+/).filter(Boolean).length,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Try to find the main content area of the page.
|
|
83
|
+
*/
|
|
84
|
+
function findMainContent(html) {
|
|
85
|
+
// Try common content selectors
|
|
86
|
+
const patterns = [
|
|
87
|
+
/<article[^>]*>([\s\S]*?)<\/article>/i,
|
|
88
|
+
/<main[^>]*>([\s\S]*?)<\/main>/i,
|
|
89
|
+
/<div[^>]*class="[^"]*(?:content|article|post|entry|main)[^"]*"[^>]*>([\s\S]*?)<\/div>/i,
|
|
90
|
+
/<div[^>]*id="[^"]*(?:content|article|post|entry|main)[^"]*"[^>]*>([\s\S]*?)<\/div>/i,
|
|
91
|
+
];
|
|
92
|
+
for (const pattern of patterns) {
|
|
93
|
+
const match = html.match(pattern);
|
|
94
|
+
if (match && match[1] && match[1].length > 200) {
|
|
95
|
+
return match[1];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Fallback: find body content
|
|
99
|
+
const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
|
100
|
+
return bodyMatch ? bodyMatch[1] : null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Simple HTML to Markdown conversion.
|
|
104
|
+
*/
|
|
105
|
+
function htmlToMarkdown(html) {
|
|
106
|
+
let md = html;
|
|
107
|
+
// Headers
|
|
108
|
+
md = md.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, '\n# $1\n');
|
|
109
|
+
md = md.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, '\n## $1\n');
|
|
110
|
+
md = md.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, '\n### $1\n');
|
|
111
|
+
md = md.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, '\n#### $1\n');
|
|
112
|
+
md = md.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, '\n##### $1\n');
|
|
113
|
+
md = md.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, '\n###### $1\n');
|
|
114
|
+
// Paragraphs and line breaks
|
|
115
|
+
md = md.replace(/<p[^>]*>/gi, '\n');
|
|
116
|
+
md = md.replace(/<\/p>/gi, '\n');
|
|
117
|
+
md = md.replace(/<br\s*\/?>/gi, '\n');
|
|
118
|
+
// Links
|
|
119
|
+
md = md.replace(/<a[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, '[$2]($1)');
|
|
120
|
+
// Bold and italic
|
|
121
|
+
md = md.replace(/<(?:strong|b)[^>]*>([\s\S]*?)<\/(?:strong|b)>/gi, '**$1**');
|
|
122
|
+
md = md.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, '*$1*');
|
|
123
|
+
// Code
|
|
124
|
+
md = md.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, '`$1`');
|
|
125
|
+
md = md.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, '\n```\n$1\n```\n');
|
|
126
|
+
// Lists
|
|
127
|
+
md = md.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '- $1\n');
|
|
128
|
+
// Blockquote
|
|
129
|
+
md = md.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, '\n> $1\n');
|
|
130
|
+
// Remove remaining HTML tags
|
|
131
|
+
md = md.replace(/<[^>]+>/g, '');
|
|
132
|
+
// Decode entities
|
|
133
|
+
md = decodeEntities(md);
|
|
134
|
+
return md;
|
|
135
|
+
}
|
|
136
|
+
function decodeEntities(text) {
|
|
137
|
+
return text
|
|
138
|
+
.replace(/&/g, '&')
|
|
139
|
+
.replace(/</g, '<')
|
|
140
|
+
.replace(/>/g, '>')
|
|
141
|
+
.replace(/"/g, '"')
|
|
142
|
+
.replace(/'/g, "'")
|
|
143
|
+
.replace(/'/g, "'")
|
|
144
|
+
.replace(/ /g, ' ')
|
|
145
|
+
.replace(/&#(\d+);/g, (_, n) => String.fromCharCode(parseInt(n)))
|
|
146
|
+
.replace(/&#x([0-9a-fA-F]+);/g, (_, n) => String.fromCharCode(parseInt(n, 16)));
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=web-scraper.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Search Engine Manager - v0.10.0
|
|
3
|
+
* Supports multiple search backends with automatic fallback.
|
|
4
|
+
* Default: DuckDuckGo (free, no API key required).
|
|
5
|
+
*/
|
|
6
|
+
export interface SearchResult {
|
|
7
|
+
title: string;
|
|
8
|
+
url: string;
|
|
9
|
+
snippet: string;
|
|
10
|
+
}
|
|
11
|
+
export interface SearchOptions {
|
|
12
|
+
maxResults?: number;
|
|
13
|
+
engine?: SearchEngine;
|
|
14
|
+
}
|
|
15
|
+
export type SearchEngine = 'duckduckgo' | 'brave' | 'searxng' | 'google';
|
|
16
|
+
export interface SearchEngineConfig {
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
apiKey?: string;
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface WebSearchConfig {
|
|
22
|
+
defaultEngine: SearchEngine;
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
engines: Partial<Record<SearchEngine, SearchEngineConfig>>;
|
|
25
|
+
}
|
|
26
|
+
export declare const DEFAULT_SEARCH_CONFIG: WebSearchConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Parse DuckDuckGo HTML search results.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseDuckDuckGoHTML(html: string): SearchResult[];
|
|
31
|
+
/**
|
|
32
|
+
* Search using DuckDuckGo HTML interface (no API key needed).
|
|
33
|
+
*/
|
|
34
|
+
export declare function searchDuckDuckGo(query: string, maxResults?: number): Promise<SearchResult[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Search using Brave Search API.
|
|
37
|
+
*/
|
|
38
|
+
export declare function searchBrave(query: string, apiKey: string, maxResults?: number): Promise<SearchResult[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Search using SearXNG instance.
|
|
41
|
+
*/
|
|
42
|
+
export declare function searchSearXNG(query: string, baseUrl: string, maxResults?: number): Promise<SearchResult[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Search using Google Custom Search API.
|
|
45
|
+
*/
|
|
46
|
+
export declare function searchGoogle(query: string, apiKey: string, maxResults?: number): Promise<SearchResult[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Unified search function with fallback.
|
|
49
|
+
*/
|
|
50
|
+
export declare function webSearch(query: string, config?: WebSearchConfig, options?: SearchOptions): Promise<SearchResult[]>;
|
|
51
|
+
//# sourceMappingURL=web-search.d.ts.map
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Web Search Engine Manager - v0.10.0
|
|
4
|
+
* Supports multiple search backends with automatic fallback.
|
|
5
|
+
* Default: DuckDuckGo (free, no API key required).
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.DEFAULT_SEARCH_CONFIG = void 0;
|
|
9
|
+
exports.parseDuckDuckGoHTML = parseDuckDuckGoHTML;
|
|
10
|
+
exports.searchDuckDuckGo = searchDuckDuckGo;
|
|
11
|
+
exports.searchBrave = searchBrave;
|
|
12
|
+
exports.searchSearXNG = searchSearXNG;
|
|
13
|
+
exports.searchGoogle = searchGoogle;
|
|
14
|
+
exports.webSearch = webSearch;
|
|
15
|
+
exports.DEFAULT_SEARCH_CONFIG = {
|
|
16
|
+
defaultEngine: 'duckduckgo',
|
|
17
|
+
enabled: true,
|
|
18
|
+
engines: {
|
|
19
|
+
duckduckgo: { enabled: true },
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Parse DuckDuckGo HTML search results.
|
|
24
|
+
*/
|
|
25
|
+
function parseDuckDuckGoHTML(html) {
|
|
26
|
+
const results = [];
|
|
27
|
+
// Match result blocks: <a class="result__a" href="...">title</a> ... <a class="result__snippet">snippet</a>
|
|
28
|
+
const resultBlocks = html.split(/class="result__body"/);
|
|
29
|
+
for (let i = 1; i < resultBlocks.length && results.length < 10; i++) {
|
|
30
|
+
const block = resultBlocks[i];
|
|
31
|
+
// Extract URL and title from result__a
|
|
32
|
+
const linkMatch = block.match(/class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/);
|
|
33
|
+
if (!linkMatch)
|
|
34
|
+
continue;
|
|
35
|
+
let url = linkMatch[1];
|
|
36
|
+
const title = stripHTML(linkMatch[2]).trim();
|
|
37
|
+
// DuckDuckGo wraps URLs in redirect, extract actual URL
|
|
38
|
+
const uddgMatch = url.match(/[?&]uddg=([^&]+)/);
|
|
39
|
+
if (uddgMatch) {
|
|
40
|
+
url = decodeURIComponent(uddgMatch[1]);
|
|
41
|
+
}
|
|
42
|
+
// Extract snippet
|
|
43
|
+
const snippetMatch = block.match(/class="result__snippet"[^>]*>([\s\S]*?)<\/a>/);
|
|
44
|
+
const snippet = snippetMatch ? stripHTML(snippetMatch[1]).trim() : '';
|
|
45
|
+
if (title && url) {
|
|
46
|
+
results.push({ title, url, snippet });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return results;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Search using DuckDuckGo HTML interface (no API key needed).
|
|
53
|
+
*/
|
|
54
|
+
async function searchDuckDuckGo(query, maxResults = 5) {
|
|
55
|
+
const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
|
|
56
|
+
const response = await fetch(url, {
|
|
57
|
+
headers: {
|
|
58
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
59
|
+
},
|
|
60
|
+
signal: AbortSignal.timeout(15000),
|
|
61
|
+
});
|
|
62
|
+
const html = await response.text();
|
|
63
|
+
return parseDuckDuckGoHTML(html).slice(0, maxResults);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Search using Brave Search API.
|
|
67
|
+
*/
|
|
68
|
+
async function searchBrave(query, apiKey, maxResults = 5) {
|
|
69
|
+
const url = `https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=${maxResults}`;
|
|
70
|
+
const response = await fetch(url, {
|
|
71
|
+
headers: { 'X-Subscription-Token': apiKey, Accept: 'application/json' },
|
|
72
|
+
signal: AbortSignal.timeout(15000),
|
|
73
|
+
});
|
|
74
|
+
const data = await response.json();
|
|
75
|
+
return (data.web?.results || []).slice(0, maxResults).map((r) => ({
|
|
76
|
+
title: r.title || '',
|
|
77
|
+
url: r.url || '',
|
|
78
|
+
snippet: r.description || '',
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Search using SearXNG instance.
|
|
83
|
+
*/
|
|
84
|
+
async function searchSearXNG(query, baseUrl, maxResults = 5) {
|
|
85
|
+
const url = `${baseUrl.replace(/\/$/, '')}/search?q=${encodeURIComponent(query)}&format=json`;
|
|
86
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(15000) });
|
|
87
|
+
const data = await response.json();
|
|
88
|
+
return (data.results || []).slice(0, maxResults).map((r) => ({
|
|
89
|
+
title: r.title || '',
|
|
90
|
+
url: r.url || '',
|
|
91
|
+
snippet: r.content || '',
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Search using Google Custom Search API.
|
|
96
|
+
*/
|
|
97
|
+
async function searchGoogle(query, apiKey, maxResults = 5) {
|
|
98
|
+
// apiKey format: "key:cx" (API key and Custom Search Engine ID)
|
|
99
|
+
const [key, cx] = apiKey.split(':');
|
|
100
|
+
const url = `https://www.googleapis.com/customsearch/v1?q=${encodeURIComponent(query)}&key=${key}&cx=${cx}&num=${maxResults}`;
|
|
101
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(15000) });
|
|
102
|
+
const data = await response.json();
|
|
103
|
+
return (data.items || []).slice(0, maxResults).map((r) => ({
|
|
104
|
+
title: r.title || '',
|
|
105
|
+
url: r.link || '',
|
|
106
|
+
snippet: r.snippet || '',
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Unified search function with fallback.
|
|
111
|
+
*/
|
|
112
|
+
async function webSearch(query, config, options) {
|
|
113
|
+
const cfg = config || exports.DEFAULT_SEARCH_CONFIG;
|
|
114
|
+
if (!cfg.enabled)
|
|
115
|
+
return [];
|
|
116
|
+
const maxResults = options?.maxResults || 5;
|
|
117
|
+
const engine = options?.engine || cfg.defaultEngine;
|
|
118
|
+
// Try requested engine first, then fallback chain
|
|
119
|
+
const fallbackOrder = [engine, 'duckduckgo', 'brave', 'searxng', 'google']
|
|
120
|
+
.filter((e, i, arr) => arr.indexOf(e) === i);
|
|
121
|
+
for (const eng of fallbackOrder) {
|
|
122
|
+
const engCfg = cfg.engines[eng];
|
|
123
|
+
if (engCfg && !engCfg.enabled)
|
|
124
|
+
continue;
|
|
125
|
+
try {
|
|
126
|
+
switch (eng) {
|
|
127
|
+
case 'duckduckgo':
|
|
128
|
+
return await searchDuckDuckGo(query, maxResults);
|
|
129
|
+
case 'brave':
|
|
130
|
+
if (engCfg?.apiKey)
|
|
131
|
+
return await searchBrave(query, engCfg.apiKey, maxResults);
|
|
132
|
+
continue;
|
|
133
|
+
case 'searxng':
|
|
134
|
+
if (engCfg?.baseUrl)
|
|
135
|
+
return await searchSearXNG(query, engCfg.baseUrl, maxResults);
|
|
136
|
+
continue;
|
|
137
|
+
case 'google':
|
|
138
|
+
if (engCfg?.apiKey)
|
|
139
|
+
return await searchGoogle(query, engCfg.apiKey, maxResults);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
continue; // Fallback to next engine
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
function stripHTML(html) {
|
|
150
|
+
return html.replace(/<[^>]+>/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, ' ');
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=web-search.js.map
|
package/install.ps1
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# OPC Agent ไธ้ฎๅฎ่ฃ
่ๆฌ for Windows
|
|
3
|
+
# ็จๆณ: irm https://raw.githubusercontent.com/Deepleaper/opc-agent/main/install.ps1 | iex
|
|
4
|
+
# ้ซ็บง: $env:OPC_YES='1'; $env:OPC_NO_OLLAMA='1'; irm ... | iex
|
|
5
|
+
# ============================================================================
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = "Stop"
|
|
8
|
+
|
|
9
|
+
# โโ ้ข่ฒ่พๅบ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
10
|
+
function Write-Step($msg) { Write-Host "`nโโโ $msg โโโ" -ForegroundColor Blue }
|
|
11
|
+
function Write-Ok($msg) { Write-Host "โ
$msg" -ForegroundColor Green }
|
|
12
|
+
function Write-Warn($msg) { Write-Host "โ ๏ธ $msg" -ForegroundColor Yellow }
|
|
13
|
+
function Write-Err($msg) { Write-Host "โ $msg" -ForegroundColor Red }
|
|
14
|
+
function Write-Info($msg) { Write-Host "โน๏ธ $msg" -ForegroundColor Cyan }
|
|
15
|
+
|
|
16
|
+
$AutoYes = $env:OPC_YES -eq '1'
|
|
17
|
+
$SkipOllama = $env:OPC_NO_OLLAMA -eq '1'
|
|
18
|
+
|
|
19
|
+
function Confirm-Action($prompt) {
|
|
20
|
+
if ($AutoYes) { return $true }
|
|
21
|
+
$choice = Read-Host "โ $prompt [Y/n]"
|
|
22
|
+
return ($choice -ne 'n' -and $choice -ne 'N' -and $choice -ne 'no')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# โโ ๆฃๆตๆไฝ็ณป็ป โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
26
|
+
Write-Step "๐ ๆฃๆตๆไฝ็ณป็ป / Detecting OS"
|
|
27
|
+
Write-Ok "Windows $([System.Environment]::OSVersion.Version) detected"
|
|
28
|
+
|
|
29
|
+
# โโ ๆฃๆต & ๅฎ่ฃ
Node.js โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
30
|
+
Write-Step "๐ฆ ๆฃๆต Node.js / Checking Node.js"
|
|
31
|
+
$NeedNode = $false
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
$nodeVer = (node -v) -replace 'v','' -split '\.' | Select-Object -First 1
|
|
35
|
+
if ([int]$nodeVer -ge 18) {
|
|
36
|
+
Write-Ok "Node.js $(node -v) ๅทฒๅฎ่ฃ
/ installed"
|
|
37
|
+
} else {
|
|
38
|
+
Write-Warn "Node.js $(node -v) ็ๆฌ่ฟไฝ๏ผ้่ฆ 18+ / version too old"
|
|
39
|
+
$NeedNode = $true
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
Write-Warn "ๆชๆฃๆตๅฐ Node.js / Node.js not found"
|
|
43
|
+
$NeedNode = $true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if ($NeedNode) {
|
|
47
|
+
Write-Info "ๅฐๅฎ่ฃ
Node.js 22 LTS / Installing Node.js 22 LTS"
|
|
48
|
+
if (Confirm-Action "ๅฎ่ฃ
Node.js 22? / Install Node.js 22?") {
|
|
49
|
+
$installed = $false
|
|
50
|
+
# ๅฐ่ฏ winget
|
|
51
|
+
try {
|
|
52
|
+
winget install OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements
|
|
53
|
+
$installed = $true
|
|
54
|
+
Write-Ok "Node.js ้่ฟ winget ๅฎ่ฃ
ๆๅ / installed via winget"
|
|
55
|
+
} catch {}
|
|
56
|
+
|
|
57
|
+
# ๅฐ่ฏ choco
|
|
58
|
+
if (-not $installed) {
|
|
59
|
+
try {
|
|
60
|
+
choco install nodejs-lts -y
|
|
61
|
+
$installed = $true
|
|
62
|
+
Write-Ok "Node.js ้่ฟ Chocolatey ๅฎ่ฃ
ๆๅ / installed via Chocolatey"
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (-not $installed) {
|
|
67
|
+
Write-Err "่ชๅจๅฎ่ฃ
ๅคฑ่ดฅ / Auto-install failed"
|
|
68
|
+
Write-Info "่ฏทๆๅจไธ่ฝฝๅฎ่ฃ
/ Please download manually: https://nodejs.org/"
|
|
69
|
+
Start-Process "https://nodejs.org/en/download/"
|
|
70
|
+
exit 1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# ๅทๆฐ PATH
|
|
74
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
75
|
+
} else {
|
|
76
|
+
Write-Err "Node.js ๆฏๅฟ
้็ / Node.js is required"
|
|
77
|
+
Write-Info "ไธ่ฝฝๅฐๅ / Download: https://nodejs.org/"
|
|
78
|
+
exit 1
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# โโ ๅฎ่ฃ
OPC Agent โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
83
|
+
Write-Step "๐ ๅฎ่ฃ
OPC Agent / Installing OPC Agent"
|
|
84
|
+
Write-Info "npm install -g opc-agent ..."
|
|
85
|
+
npm install -g opc-agent
|
|
86
|
+
Write-Ok "OPC Agent ๅฎ่ฃ
ๆๅ / installed"
|
|
87
|
+
|
|
88
|
+
# โโ Ollama๏ผๅฏ้๏ผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
89
|
+
if (-not $SkipOllama) {
|
|
90
|
+
Write-Step "๐ฆ ๆฃๆต Ollama / Checking Ollama"
|
|
91
|
+
|
|
92
|
+
$hasOllama = $false
|
|
93
|
+
try { ollama --version 2>$null; $hasOllama = $true } catch {}
|
|
94
|
+
|
|
95
|
+
if ($hasOllama) {
|
|
96
|
+
Write-Ok "Ollama ๅทฒๅฎ่ฃ
/ installed"
|
|
97
|
+
} else {
|
|
98
|
+
Write-Warn "ๆชๆฃๆตๅฐ Ollama / Ollama not found"
|
|
99
|
+
Write-Info "Ollama ๅฏ่ฎฉไฝ ๅจๆฌๅฐ่ฟ่ก AI ๆจกๅ๏ผๅ
่ดนใๆฐๆฎไธๅบ้จ๏ผ"
|
|
100
|
+
Write-Info "Ollama lets you run AI models locally (free, private)"
|
|
101
|
+
|
|
102
|
+
if (Confirm-Action "ๅฎ่ฃ
Ollama? / Install Ollama?") {
|
|
103
|
+
try {
|
|
104
|
+
winget install Ollama.Ollama --accept-package-agreements --accept-source-agreements
|
|
105
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
106
|
+
Write-Ok "Ollama ๅฎ่ฃ
ๆๅ / installed"
|
|
107
|
+
$hasOllama = $true
|
|
108
|
+
} catch {
|
|
109
|
+
Write-Warn "winget ๅฎ่ฃ
ๅคฑ่ดฅ๏ผ่ฏทๆๅจไธ่ฝฝ / Please download manually: https://ollama.com/download"
|
|
110
|
+
Start-Process "https://ollama.com/download"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# ๆๅๆจ่ๆจกๅ
|
|
116
|
+
if ($hasOllama) {
|
|
117
|
+
Write-Step "๐ง ๆจ่ๆจกๅ / Recommended Models"
|
|
118
|
+
Write-Info "ๆจ่ๆๅไปฅไธๆจกๅ็จไบๆฌๅฐ Agent๏ผ"
|
|
119
|
+
Write-Info " โข qwen2.5:7b โ ไธญ่ฑๆๅฏน่ฏ๏ผ4.7GB๏ผ"
|
|
120
|
+
Write-Info " โข nomic-embed-text โ ๆๆฌๅ้ๅ๏ผ274MB๏ผ"
|
|
121
|
+
|
|
122
|
+
if (Confirm-Action "ๆๅๆจ่ๆจกๅ? / Pull recommended models?") {
|
|
123
|
+
Write-Info "ๆๅ qwen2.5:7b ... (ๅฏ่ฝ้่ฆๅ ๅ้)"
|
|
124
|
+
try { ollama pull qwen2.5:7b; Write-Ok "qwen2.5:7b โ" } catch { Write-Warn "ๆๅๅคฑ่ดฅ๏ผๅฏ็จๅๆๅจ: ollama pull qwen2.5:7b" }
|
|
125
|
+
|
|
126
|
+
Write-Info "ๆๅ nomic-embed-text ..."
|
|
127
|
+
try { ollama pull nomic-embed-text; Write-Ok "nomic-embed-text โ" } catch { Write-Warn "ๆๅๅคฑ่ดฅ๏ผๅฏ็จๅๆๅจ: ollama pull nomic-embed-text" }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# โโ ๅฎๆ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
133
|
+
Write-Step "๐ ๅฎ่ฃ
ๅฎๆ๏ผ/ Installation Complete!"
|
|
134
|
+
Write-Host ""
|
|
135
|
+
Write-Host "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ" -ForegroundColor Green
|
|
136
|
+
Write-Host "โ OPC Agent ๅทฒๅฎ่ฃ
ๆๅ๏ผ โ" -ForegroundColor Green
|
|
137
|
+
Write-Host "โ OPC Agent installed successfully! โ" -ForegroundColor Green
|
|
138
|
+
Write-Host "โ โ" -ForegroundColor Green
|
|
139
|
+
Write-Host "โ ๅฟซ้ๅผๅง / Quick Start: โ" -ForegroundColor Green
|
|
140
|
+
Write-Host "โ opc init my-agent # ๅๅปบ Agent โ" -ForegroundColor Green
|
|
141
|
+
Write-Host "โ cd my-agent; npm i โ" -ForegroundColor Green
|
|
142
|
+
Write-Host "โ opc chat # ๅผๅงๅฏน่ฏ โ" -ForegroundColor Green
|
|
143
|
+
Write-Host "โ โ" -ForegroundColor Green
|
|
144
|
+
Write-Host "โ ๅฏ่งๅ้ขๆฟ / Dashboard: โ" -ForegroundColor Green
|
|
145
|
+
Write-Host "โ opc studio โ" -ForegroundColor Green
|
|
146
|
+
Write-Host "โ โ" -ForegroundColor Green
|
|
147
|
+
Write-Host "โ ไบคไบๅผ่ฎพ็ฝฎ / Setup wizard: โ" -ForegroundColor Green
|
|
148
|
+
Write-Host "โ opc setup โ" -ForegroundColor Green
|
|
149
|
+
Write-Host "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ" -ForegroundColor Green
|
|
150
|
+
Write-Host ""
|
|
151
|
+
|
|
152
|
+
if (Confirm-Action "็ฐๅจๆๅผ OPC Studio? / Open OPC Studio now?") {
|
|
153
|
+
opc studio
|
|
154
|
+
}
|
package/install.sh
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# OPC Agent ไธ้ฎๅฎ่ฃ
่ๆฌ (macOS / Linux / WSL)
|
|
4
|
+
# ็จๆณ: curl -fsSL https://raw.githubusercontent.com/Deepleaper/opc-agent/main/install.sh | bash
|
|
5
|
+
# ้ซ็บง: curl -fsSL ... | bash -s -- --yes --no-ollama
|
|
6
|
+
# ============================================================================
|
|
7
|
+
|
|
8
|
+
set -e
|
|
9
|
+
|
|
10
|
+
# โโ ้ข่ฒ & Emoji โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
11
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
12
|
+
info() { echo -e "${CYAN}โน๏ธ $1${NC}"; }
|
|
13
|
+
ok() { echo -e "${GREEN}โ
$1${NC}"; }
|
|
14
|
+
warn() { echo -e "${YELLOW}โ ๏ธ $1${NC}"; }
|
|
15
|
+
err() { echo -e "${RED}โ $1${NC}"; }
|
|
16
|
+
step() { echo -e "\n${BLUE}โโโ $1 โโโ${NC}"; }
|
|
17
|
+
|
|
18
|
+
# โโ ๅๆฐ่งฃๆ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
19
|
+
AUTO_YES=false
|
|
20
|
+
SKIP_OLLAMA=false
|
|
21
|
+
for arg in "$@"; do
|
|
22
|
+
case "$arg" in
|
|
23
|
+
--yes|-y) AUTO_YES=true ;;
|
|
24
|
+
--no-ollama) SKIP_OLLAMA=true ;;
|
|
25
|
+
esac
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
confirm() {
|
|
29
|
+
if $AUTO_YES; then return 0; fi
|
|
30
|
+
read -r -p "$(echo -e "${YELLOW}โ $1 [Y/n]: ${NC}")" choice
|
|
31
|
+
case "$choice" in n|N|no|NO) return 1 ;; *) return 0 ;; esac
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# โโ ๆฃๆตๆไฝ็ณป็ป โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
35
|
+
step "๐ ๆฃๆตๆไฝ็ณป็ป / Detecting OS"
|
|
36
|
+
OS="unknown"
|
|
37
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
38
|
+
OS="macos"
|
|
39
|
+
ok "macOS detected"
|
|
40
|
+
elif grep -qi microsoft /proc/version 2>/dev/null; then
|
|
41
|
+
OS="wsl"
|
|
42
|
+
ok "WSL (Windows Subsystem for Linux) detected"
|
|
43
|
+
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
44
|
+
OS="linux"
|
|
45
|
+
ok "Linux detected"
|
|
46
|
+
else
|
|
47
|
+
err "ไธๆฏๆ็ๆไฝ็ณป็ป / Unsupported OS: $OSTYPE"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# โโ ๆฃๆต & ๅฎ่ฃ
Node.js โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
52
|
+
step "๐ฆ ๆฃๆต Node.js / Checking Node.js"
|
|
53
|
+
NEED_NODE=false
|
|
54
|
+
|
|
55
|
+
if command -v node &>/dev/null; then
|
|
56
|
+
NODE_VER=$(node -v | sed 's/v//' | cut -d. -f1)
|
|
57
|
+
if [ "$NODE_VER" -ge 18 ]; then
|
|
58
|
+
ok "Node.js $(node -v) ๅทฒๅฎ่ฃ
/ installed"
|
|
59
|
+
else
|
|
60
|
+
warn "Node.js $(node -v) ็ๆฌ่ฟไฝ๏ผ้่ฆ 18+ / version too old, need 18+"
|
|
61
|
+
NEED_NODE=true
|
|
62
|
+
fi
|
|
63
|
+
else
|
|
64
|
+
warn "ๆชๆฃๆตๅฐ Node.js / Node.js not found"
|
|
65
|
+
NEED_NODE=true
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if $NEED_NODE; then
|
|
69
|
+
info "ๅฐ้่ฟ nvm ๅฎ่ฃ
Node.js 22 LTS / Installing Node.js 22 via nvm"
|
|
70
|
+
if confirm "ๅฎ่ฃ
Node.js 22? / Install Node.js 22?"; then
|
|
71
|
+
if command -v nvm &>/dev/null || [ -s "$HOME/.nvm/nvm.sh" ]; then
|
|
72
|
+
[ -s "$HOME/.nvm/nvm.sh" ] && . "$HOME/.nvm/nvm.sh"
|
|
73
|
+
info "nvm ๅทฒๅญๅจ๏ผๅฎ่ฃ
Node 22... / nvm found, installing Node 22..."
|
|
74
|
+
else
|
|
75
|
+
info "ๅฎ่ฃ
nvm... / Installing nvm..."
|
|
76
|
+
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
|
|
77
|
+
export NVM_DIR="$HOME/.nvm"
|
|
78
|
+
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
|
79
|
+
fi
|
|
80
|
+
nvm install 22
|
|
81
|
+
nvm use 22
|
|
82
|
+
ok "Node.js $(node -v) ๅฎ่ฃ
ๆๅ / installed successfully"
|
|
83
|
+
else
|
|
84
|
+
err "Node.js ๆฏๅฟ
้็ / Node.js is required"
|
|
85
|
+
echo "ๆๅจๅฎ่ฃ
/ Manual install: https://nodejs.org/"
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# โโ ๅฎ่ฃ
OPC Agent โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
91
|
+
step "๐ ๅฎ่ฃ
OPC Agent / Installing OPC Agent"
|
|
92
|
+
info "npm install -g opc-agent ..."
|
|
93
|
+
npm install -g opc-agent
|
|
94
|
+
ok "OPC Agent $(opc --version 2>/dev/null || echo '') ๅฎ่ฃ
ๆๅ / installed"
|
|
95
|
+
|
|
96
|
+
# โโ Ollama๏ผๅฏ้๏ผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
97
|
+
if ! $SKIP_OLLAMA; then
|
|
98
|
+
step "๐ฆ ๆฃๆต Ollama / Checking Ollama"
|
|
99
|
+
|
|
100
|
+
if command -v ollama &>/dev/null; then
|
|
101
|
+
ok "Ollama ๅทฒๅฎ่ฃ
/ installed ($(ollama --version 2>/dev/null || echo 'ok'))"
|
|
102
|
+
else
|
|
103
|
+
warn "ๆชๆฃๆตๅฐ Ollama / Ollama not found"
|
|
104
|
+
info "Ollama ๅฏ่ฎฉไฝ ๅจๆฌๅฐ่ฟ่ก AI ๆจกๅ๏ผๅ
่ดนใๆฐๆฎไธๅบ้จ๏ผ"
|
|
105
|
+
info "Ollama lets you run AI models locally (free, private)"
|
|
106
|
+
|
|
107
|
+
if confirm "ๅฎ่ฃ
Ollama? / Install Ollama?"; then
|
|
108
|
+
if [ "$OS" = "macos" ]; then
|
|
109
|
+
if command -v brew &>/dev/null; then
|
|
110
|
+
brew install ollama
|
|
111
|
+
else
|
|
112
|
+
info "่ฏทไป https://ollama.com/download ไธ่ฝฝๅฎ่ฃ
"
|
|
113
|
+
info "Download from https://ollama.com/download"
|
|
114
|
+
open "https://ollama.com/download" 2>/dev/null || true
|
|
115
|
+
fi
|
|
116
|
+
else
|
|
117
|
+
curl -fsSL https://ollama.com/install.sh | sh
|
|
118
|
+
fi
|
|
119
|
+
ok "Ollama ๅฎ่ฃ
ๆๅ / installed"
|
|
120
|
+
else
|
|
121
|
+
info "่ทณ่ฟ Ollama / Skipping Ollama"
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# ๆๅๆจ่ๆจกๅ
|
|
126
|
+
if command -v ollama &>/dev/null; then
|
|
127
|
+
step "๐ง ๆจ่ๆจกๅ / Recommended Models"
|
|
128
|
+
info "ๆจ่ๆๅไปฅไธๆจกๅ็จไบๆฌๅฐ Agent๏ผ"
|
|
129
|
+
info " โข qwen2.5:7b โ ไธญ่ฑๆๅฏน่ฏ๏ผ4.7GB๏ผ"
|
|
130
|
+
info " โข nomic-embed-text โ ๆๆฌๅ้ๅ๏ผ274MB๏ผ"
|
|
131
|
+
|
|
132
|
+
if confirm "ๆๅๆจ่ๆจกๅ? / Pull recommended models?"; then
|
|
133
|
+
info "ๆๅ qwen2.5:7b ... (ๅฏ่ฝ้่ฆๅ ๅ้)"
|
|
134
|
+
ollama pull qwen2.5:7b && ok "qwen2.5:7b โ" || warn "qwen2.5:7b ๆๅๅคฑ่ดฅ๏ผๅฏ็จๅๆๅจ: ollama pull qwen2.5:7b"
|
|
135
|
+
|
|
136
|
+
info "ๆๅ nomic-embed-text ..."
|
|
137
|
+
ollama pull nomic-embed-text && ok "nomic-embed-text โ" || warn "ๆๅๅคฑ่ดฅ๏ผๅฏ็จๅๆๅจ: ollama pull nomic-embed-text"
|
|
138
|
+
fi
|
|
139
|
+
fi
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# โโ ๅฏๅจ Studio โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
143
|
+
step "๐ ๅฎ่ฃ
ๅฎๆ๏ผ/ Installation Complete!"
|
|
144
|
+
echo ""
|
|
145
|
+
echo -e "${GREEN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
|
|
146
|
+
echo -e "${GREEN}โ OPC Agent ๅทฒๅฎ่ฃ
ๆๅ๏ผ โ${NC}"
|
|
147
|
+
echo -e "${GREEN}โ OPC Agent installed successfully! โ${NC}"
|
|
148
|
+
echo -e "${GREEN}โ โ${NC}"
|
|
149
|
+
echo -e "${GREEN}โ ๅฟซ้ๅผๅง / Quick Start: โ${NC}"
|
|
150
|
+
echo -e "${GREEN}โ opc init my-agent # ๅๅปบ Agent โ${NC}"
|
|
151
|
+
echo -e "${GREEN}โ cd my-agent && npm i โ${NC}"
|
|
152
|
+
echo -e "${GREEN}โ opc chat # ๅผๅงๅฏน่ฏ โ${NC}"
|
|
153
|
+
echo -e "${GREEN}โ โ${NC}"
|
|
154
|
+
echo -e "${GREEN}โ ๅฏ่งๅ้ขๆฟ / Dashboard: โ${NC}"
|
|
155
|
+
echo -e "${GREEN}โ opc studio โ${NC}"
|
|
156
|
+
echo -e "${GREEN}โ โ${NC}"
|
|
157
|
+
echo -e "${GREEN}โ ไบคไบๅผ่ฎพ็ฝฎ / Setup wizard: โ${NC}"
|
|
158
|
+
echo -e "${GREEN}โ opc setup โ${NC}"
|
|
159
|
+
echo -e "${GREEN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
|
|
160
|
+
echo ""
|
|
161
|
+
|
|
162
|
+
if confirm "็ฐๅจๆๅผ OPC Studio? / Open OPC Studio now?"; then
|
|
163
|
+
opc studio
|
|
164
|
+
fi
|