@make-u-free/migi 0.3.1 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@make-u-free/migi",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Your AI right-hand agent. Works anywhere, with any LLM API.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,10 +10,12 @@
10
10
  "start": "node bin/migi.js"
11
11
  },
12
12
  "dependencies": {
13
+ "adm-zip": "^0.5.16",
13
14
  "chalk": "^5.3.0",
14
15
  "dotenv": "^16.4.0",
15
16
  "glob": "^11.0.0",
16
17
  "openai": "^4.0.0",
18
+ "pdf-parse": "^2.4.5",
17
19
  "xlsx": "^0.18.5"
18
20
  },
19
21
  "engines": {
package/src/agent.js CHANGED
@@ -7,8 +7,9 @@ import { httpsAgent } from './tls.js'
7
7
 
8
8
  export class MigiAgent {
9
9
  constructor({ context = '', promptFn = null, apiKey = null, model = 'gpt-4.1-2025-04-14', name = 'Migi', userName = '', teamsWebhookUrl = '' } = {}) {
10
+ this.apiKey = apiKey || process.env.OPENAI_API_KEY
10
11
  this.client = new OpenAI({
11
- apiKey: apiKey || process.env.OPENAI_API_KEY,
12
+ apiKey: this.apiKey,
12
13
  ...(httpsAgent ? { httpAgent: httpsAgent } : {})
13
14
  })
14
15
  this.model = model
@@ -123,7 +124,11 @@ ${userNameLine}
123
124
 
124
125
  if (approved) {
125
126
  try {
126
- result = await executeTool(name, args, { teamsWebhookUrl: this.teamsWebhookUrl })
127
+ result = await executeTool(name, args, {
128
+ teamsWebhookUrl: this.teamsWebhookUrl,
129
+ apiKey: this.apiKey,
130
+ model: this.model
131
+ })
127
132
  } catch (err) {
128
133
  result = `エラー: ${err.message}`
129
134
  }
package/src/tools.js CHANGED
@@ -4,9 +4,16 @@ import { dirname, extname } from 'path'
4
4
  import { request } from 'https'
5
5
  import { glob } from 'glob'
6
6
  import xlsxPkg from 'xlsx'
7
+ import pdfParse from 'pdf-parse'
8
+ import AdmZip from 'adm-zip'
9
+ import OpenAI from 'openai'
7
10
  import { httpsAgent } from './tls.js'
8
11
  const { readFile: xlsxReadFile, utils: xlsxUtils } = xlsxPkg
9
12
 
13
+ const IMAGE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp'])
14
+ const OFFICE_EXTS = new Set(['.pdf', '.pptx', '.ppt', '.docx', '.doc', '.odp', '.odt'])
15
+ const MIME = { '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.webp': 'image/webp' }
16
+
10
17
  // ---- OpenAI ツールスキーマ定義 ----
11
18
 
12
19
  export const toolSchemas = [
@@ -14,7 +21,7 @@ export const toolSchemas = [
14
21
  type: 'function',
15
22
  function: {
16
23
  name: 'read_file',
17
- description: 'ファイルの内容を読み込む',
24
+ description: 'ファイルの内容を読み込む。テキスト・Excel・PDF・Word・PowerPoint・画像に対応',
18
25
  parameters: {
19
26
  type: 'object',
20
27
  properties: {
@@ -124,6 +131,8 @@ export async function executeTool(name, args, opts = {}) {
124
131
  case 'read_file': {
125
132
  if (!existsSync(args.path)) return `エラー: ファイルが見つかりません: ${args.path}`
126
133
  const ext = extname(args.path).toLowerCase()
134
+
135
+ // Excel
127
136
  if (ext === '.xlsx' || ext === '.xls') {
128
137
  const workbook = xlsxReadFile(args.path)
129
138
  const result = []
@@ -134,6 +143,64 @@ export async function executeTool(name, args, opts = {}) {
134
143
  }
135
144
  return result.join('\n\n')
136
145
  }
146
+
147
+ // PDF
148
+ if (ext === '.pdf') {
149
+ try {
150
+ const buf = readFileSync(args.path)
151
+ const data = await pdfParse(buf)
152
+ return data.text?.trim() || '(テキストが抽出できませんでした)'
153
+ } catch (err) {
154
+ return `エラー: PDFの解析に失敗しました: ${err.message}`
155
+ }
156
+ }
157
+
158
+ // PowerPoint(PPTX)/ Word(DOCX)→ ZIPを展開してXMLからテキスト抽出
159
+ if (['.pptx', '.ppt', '.docx', '.doc', '.odp', '.odt'].includes(ext)) {
160
+ try {
161
+ const zip = new AdmZip(args.path)
162
+ const entries = zip.getEntries()
163
+ const xmlTexts = []
164
+ for (const entry of entries) {
165
+ const name = entry.entryName
166
+ const isSlide = name.startsWith('ppt/slides/slide') && name.endsWith('.xml')
167
+ const isDoc = name === 'word/document.xml'
168
+ const isOdp = name === 'content.xml'
169
+ if (isSlide || isDoc || isOdp) {
170
+ const xml = entry.getData().toString('utf-8')
171
+ const text = xml.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim()
172
+ if (text) xmlTexts.push(text)
173
+ }
174
+ }
175
+ return xmlTexts.join('\n\n') || '(テキストが抽出できませんでした)'
176
+ } catch (err) {
177
+ return `エラー: ファイルの解析に失敗しました: ${err.message}`
178
+ }
179
+ }
180
+
181
+ // 画像 → Vision API で内容を説明させる
182
+ if (IMAGE_EXTS.has(ext)) {
183
+ if (!opts.apiKey) return 'エラー: 画像読み込みにはAPIキーが必要です'
184
+ const base64 = readFileSync(args.path).toString('base64')
185
+ const mimeType = MIME[ext] || 'image/jpeg'
186
+ const client = new OpenAI({
187
+ apiKey: opts.apiKey,
188
+ ...(httpsAgent ? { httpAgent: httpsAgent } : {})
189
+ })
190
+ const res = await client.chat.completions.create({
191
+ model: opts.model || 'gpt-4.1-2025-04-14',
192
+ messages: [{
193
+ role: 'user',
194
+ content: [
195
+ { type: 'image_url', image_url: { url: `data:${mimeType};base64,${base64}` } },
196
+ { type: 'text', text: 'この画像の内容を詳しく説明してください。テキストが含まれている場合はすべて書き起こしてください。' }
197
+ ]
198
+ }],
199
+ max_tokens: 2000
200
+ })
201
+ return res.choices[0].message.content
202
+ }
203
+
137
204
  return readFileSync(args.path, 'utf-8')
138
205
  }
139
206