@coeiro-operator/core 1.0.0
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 +11 -0
- package/dist/common/config-paths.d.ts +12 -0
- package/dist/common/config-paths.js +43 -0
- package/dist/dictionary/default-dictionaries.d.ts +20 -0
- package/dist/dictionary/default-dictionaries.js +48 -0
- package/dist/dictionary/dictionary-client.d.ts +73 -0
- package/dist/dictionary/dictionary-client.js +115 -0
- package/dist/dictionary/dictionary-persistence.d.ts +48 -0
- package/dist/dictionary/dictionary-persistence.js +82 -0
- package/dist/dictionary/dictionary-service.d.ts +32 -0
- package/dist/dictionary/dictionary-service.js +102 -0
- package/dist/environment/speaker-provider.d.ts +72 -0
- package/dist/environment/speaker-provider.js +174 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +27 -0
- package/dist/operator/character-defaults.d.ts +20 -0
- package/dist/operator/character-defaults.js +128 -0
- package/dist/operator/character-info-service.d.ts +73 -0
- package/dist/operator/character-info-service.js +152 -0
- package/dist/operator/config-manager.d.ts +145 -0
- package/dist/operator/config-manager.js +232 -0
- package/dist/operator/file-operation-manager.d.ts +82 -0
- package/dist/operator/file-operation-manager.js +230 -0
- package/dist/operator/index.d.ts +128 -0
- package/dist/operator/index.js +353 -0
- package/dist/terminal/terminal-background.d.ts +37 -0
- package/dist/terminal/terminal-background.js +207 -0
- package/dist/test-utils/test-env.d.ts +10 -0
- package/dist/test-utils/test-env.js +13 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.js +5 -0
- package/package.json +54 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { TerminalBackground as TermBg } from '@coeiro-operator/term-bg';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { writeFile } from 'fs/promises';
|
|
4
|
+
import { getSpeakerProvider } from '../environment/speaker-provider.js';
|
|
5
|
+
import { logger } from '@coeiro-operator/common';
|
|
6
|
+
export class TerminalBackground {
|
|
7
|
+
currentCharacterId = null;
|
|
8
|
+
configManager;
|
|
9
|
+
termBg;
|
|
10
|
+
sessionId;
|
|
11
|
+
constructor(configManager, sessionId) {
|
|
12
|
+
this.configManager = configManager;
|
|
13
|
+
this.termBg = new TermBg();
|
|
14
|
+
this.sessionId = sessionId || this.getSessionId();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* セッションIDを取得(OperatorManagerと同じロジック)
|
|
18
|
+
*/
|
|
19
|
+
getSessionId() {
|
|
20
|
+
// ITERM_SESSION_IDを優先、次にTERM_SESSION_IDを使用
|
|
21
|
+
let sessionId = process.env.ITERM_SESSION_ID || process.env.TERM_SESSION_ID;
|
|
22
|
+
if (sessionId && sessionId !== 'inherit') {
|
|
23
|
+
// プレフィックス(例: "w4t0p0:")を除去
|
|
24
|
+
// 環境変数のセッションIDは "w4t0p0:UUID" の形式だが、
|
|
25
|
+
// iTerm2 APIは "UUID" のみを期待する
|
|
26
|
+
const colonIndex = sessionId.indexOf(':');
|
|
27
|
+
if (colonIndex !== -1) {
|
|
28
|
+
sessionId = sessionId.substring(colonIndex + 1);
|
|
29
|
+
}
|
|
30
|
+
return sessionId;
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 背景画像が有効かチェック
|
|
36
|
+
*/
|
|
37
|
+
async isEnabled() {
|
|
38
|
+
const config = await this.configManager.getTerminalBackgroundConfig();
|
|
39
|
+
const isITerm = this.termBg.isITerm2();
|
|
40
|
+
logger.error('🔍 Terminal background check:', {
|
|
41
|
+
configEnabled: config.enabled,
|
|
42
|
+
isITerm2: isITerm,
|
|
43
|
+
TERM_PROGRAM: process.env.TERM_PROGRAM,
|
|
44
|
+
ITERM_SESSION_ID: process.env.ITERM_SESSION_ID,
|
|
45
|
+
TERM_SESSION_ID: process.env.TERM_SESSION_ID
|
|
46
|
+
});
|
|
47
|
+
return config.enabled && isITerm;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* キャラクター切り替え時の背景更新
|
|
51
|
+
*/
|
|
52
|
+
async switchCharacter(characterId) {
|
|
53
|
+
const config = await this.configManager.getTerminalBackgroundConfig();
|
|
54
|
+
logger.error('🎨 背景画像切り替え処理を開始:', {
|
|
55
|
+
characterId,
|
|
56
|
+
sessionId: this.sessionId,
|
|
57
|
+
configEnabled: config.enabled,
|
|
58
|
+
isITerm2: this.termBg.isITerm2()
|
|
59
|
+
});
|
|
60
|
+
if (!config.enabled || !this.termBg.isITerm2()) {
|
|
61
|
+
logger.error('⏭️ 背景画像設定をスキップ:', {
|
|
62
|
+
reason: !config.enabled ? '設定が無効' : 'iTerm2ではない',
|
|
63
|
+
configEnabled: config.enabled,
|
|
64
|
+
isITerm2: this.termBg.isITerm2()
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
this.currentCharacterId = characterId;
|
|
69
|
+
try {
|
|
70
|
+
// 背景画像を設定
|
|
71
|
+
if (config.backgroundImages?.[characterId]) {
|
|
72
|
+
logger.error(`📷 キャラクター専用背景画像を設定中: ${characterId}`);
|
|
73
|
+
await this.setBackgroundImage(config.backgroundImages[characterId]);
|
|
74
|
+
}
|
|
75
|
+
// オペレータ画像を表示(APIから取得して背景に設定)
|
|
76
|
+
if (config.operatorImage?.display === 'api') {
|
|
77
|
+
logger.error('🌐 APIからオペレータ画像を取得中...');
|
|
78
|
+
const imagePath = await this.fetchOperatorImageFromAPI(characterId);
|
|
79
|
+
if (imagePath) {
|
|
80
|
+
logger.error(`🖼️ APIから取得した画像を背景に設定中: ${imagePath}`);
|
|
81
|
+
await this.setBackgroundImage(imagePath, config.operatorImage.opacity);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
logger.error('⚠️ APIからオペレータ画像を取得できませんでした');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (config.operatorImage?.display === 'file' && config.operatorImage.filePath) {
|
|
88
|
+
const imagePath = config.operatorImage.filePath.startsWith('/')
|
|
89
|
+
? config.operatorImage.filePath
|
|
90
|
+
: join(process.cwd(), config.operatorImage.filePath);
|
|
91
|
+
logger.error(`📁 ファイルからオペレータ画像を設定中: ${imagePath}`);
|
|
92
|
+
await this.setBackgroundImage(imagePath, config.operatorImage.opacity);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
logger.error('ℹ️ オペレータ画像の設定なし:', {
|
|
96
|
+
display: config.operatorImage?.display,
|
|
97
|
+
hasFilePath: !!config.operatorImage?.filePath
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
logger.error(`背景画像の設定に失敗しました: ${error}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 背景画像を設定
|
|
107
|
+
*/
|
|
108
|
+
async setBackgroundImage(imagePath, opacity) {
|
|
109
|
+
try {
|
|
110
|
+
// 絶対パスに変換
|
|
111
|
+
const absolutePath = imagePath.startsWith('/') ? imagePath : join(process.cwd(), imagePath);
|
|
112
|
+
logger.error('🔄 背景画像設定を試行中:', {
|
|
113
|
+
imagePath: absolutePath,
|
|
114
|
+
sessionId: this.sessionId,
|
|
115
|
+
opacity: opacity ?? 0.3,
|
|
116
|
+
position: 'bottom-right',
|
|
117
|
+
scale: 0.15
|
|
118
|
+
});
|
|
119
|
+
await this.termBg.setBackground({
|
|
120
|
+
imagePath: absolutePath,
|
|
121
|
+
opacity: opacity ?? 0.3, // デフォルト30%の不透明度
|
|
122
|
+
position: 'bottom-right', // 右下に配置
|
|
123
|
+
scale: 0.15, // 15%のサイズに縮小
|
|
124
|
+
mode: 'fit', // アスペクト比を保持
|
|
125
|
+
sessionId: this.sessionId // SessionIDを指定
|
|
126
|
+
});
|
|
127
|
+
logger.error(`✅ 背景画像の設定に成功: ${absolutePath}${this.sessionId ? ` (Session: ${this.sessionId})` : ''}`);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
logger.error(`❌ 背景画像の設定に失敗: ${error}`);
|
|
131
|
+
logger.error('失敗の詳細:', {
|
|
132
|
+
errorMessage: error.message,
|
|
133
|
+
sessionId: this.sessionId,
|
|
134
|
+
imagePath
|
|
135
|
+
});
|
|
136
|
+
throw error; // エラーを再スロー
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* APIからオペレータ画像を取得
|
|
141
|
+
*/
|
|
142
|
+
async fetchOperatorImageFromAPI(characterId) {
|
|
143
|
+
try {
|
|
144
|
+
// ConfigManagerからキャラクター設定を取得
|
|
145
|
+
const characterConfig = await this.configManager.getCharacterConfig(characterId);
|
|
146
|
+
if (!characterConfig || !characterConfig.speakerId) {
|
|
147
|
+
logger.info(`キャラクター ${characterId} の設定が見つかりません`);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
// SpeakerProviderから立ち絵を取得
|
|
151
|
+
const connectionConfig = await this.configManager.getConnectionConfig();
|
|
152
|
+
const speakerProvider = getSpeakerProvider(connectionConfig);
|
|
153
|
+
const portrait = await speakerProvider.getSpeakerPortrait(characterConfig.speakerId);
|
|
154
|
+
if (!portrait) {
|
|
155
|
+
logger.info(`キャラクター ${characterId} の立ち絵が見つかりません`);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
// データURLプレフィックスを除去(必要な場合)
|
|
159
|
+
let base64Data = portrait;
|
|
160
|
+
if (base64Data.startsWith('data:image')) {
|
|
161
|
+
const commaIndex = base64Data.indexOf(',');
|
|
162
|
+
if (commaIndex !== -1) {
|
|
163
|
+
base64Data = base64Data.substring(commaIndex + 1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Base64画像をファイルとして保存
|
|
167
|
+
const tempPath = `/tmp/operator-${characterId}.png`;
|
|
168
|
+
const imageBuffer = Buffer.from(base64Data, 'base64');
|
|
169
|
+
await writeFile(tempPath, imageBuffer);
|
|
170
|
+
// PNGヘッダーの確認
|
|
171
|
+
const header = imageBuffer.slice(0, 4);
|
|
172
|
+
const isPNG = header[0] === 0x89 && header[1] === 0x50 && header[2] === 0x4E && header[3] === 0x47;
|
|
173
|
+
if (!isPNG) {
|
|
174
|
+
logger.error(`保存したファイルが有効なPNGファイルではありません`);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
return tempPath;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
logger.error(`API画像取得エラー: ${error}`);
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 背景をクリア
|
|
186
|
+
*/
|
|
187
|
+
async clearBackground() {
|
|
188
|
+
if (!this.termBg.isITerm2()) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
await this.termBg.clearBackground(this.sessionId);
|
|
193
|
+
this.currentCharacterId = null;
|
|
194
|
+
logger.info(`背景画像をクリアしました${this.sessionId ? ` (Session: ${this.sessionId})` : ''}`);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
logger.error(`背景のクリアに失敗しました: ${error}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 現在のキャラクターID取得
|
|
202
|
+
*/
|
|
203
|
+
getCurrentCharacter() {
|
|
204
|
+
return this.currentCharacterId;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=terminal-background.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* テスト環境用の共通環境変数設定
|
|
3
|
+
*/
|
|
4
|
+
export const getTestEnvironment = () => {
|
|
5
|
+
return {
|
|
6
|
+
...process.env,
|
|
7
|
+
NODE_ENV: 'test',
|
|
8
|
+
CI: 'true',
|
|
9
|
+
// デバッグログを有効化(必要に応じて)
|
|
10
|
+
COEIRO_LOG_LEVEL: process.env.COEIRO_LOG_LEVEL || 'info',
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=test-env.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共通の型定義
|
|
3
|
+
*/
|
|
4
|
+
export interface AudioConfig {
|
|
5
|
+
latencyMode?: 'ultra-low' | 'balanced' | 'quality';
|
|
6
|
+
splitMode?: 'none' | 'small' | 'medium' | 'large' | 'punctuation';
|
|
7
|
+
bufferSize?: number;
|
|
8
|
+
processing?: {
|
|
9
|
+
synthesisRate?: number;
|
|
10
|
+
playbackRate?: number;
|
|
11
|
+
noiseReduction?: boolean;
|
|
12
|
+
lowpassFilter?: boolean;
|
|
13
|
+
lowpassCutoff?: number;
|
|
14
|
+
};
|
|
15
|
+
splitSettings?: {
|
|
16
|
+
smallSize?: number;
|
|
17
|
+
mediumSize?: number;
|
|
18
|
+
largeSize?: number;
|
|
19
|
+
overlapRatio?: number;
|
|
20
|
+
};
|
|
21
|
+
bufferSettings?: {
|
|
22
|
+
highWaterMark?: number;
|
|
23
|
+
lowWaterMark?: number;
|
|
24
|
+
dynamicAdjustment?: boolean;
|
|
25
|
+
};
|
|
26
|
+
paddingSettings?: {
|
|
27
|
+
enabled?: boolean;
|
|
28
|
+
prePhonemeLength?: number;
|
|
29
|
+
postPhonemeLength?: number;
|
|
30
|
+
firstChunkOnly?: boolean;
|
|
31
|
+
};
|
|
32
|
+
crossfadeSettings?: {
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
skipFirstChunk?: boolean;
|
|
35
|
+
overlapSamples?: number;
|
|
36
|
+
};
|
|
37
|
+
parallelGeneration?: {
|
|
38
|
+
maxConcurrency?: number;
|
|
39
|
+
delayBetweenRequests?: number;
|
|
40
|
+
bufferAheadCount?: number;
|
|
41
|
+
pauseUntilFirstComplete?: boolean;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface ConnectionConfig {
|
|
45
|
+
host: string;
|
|
46
|
+
port: string;
|
|
47
|
+
}
|
|
48
|
+
export interface FullConfig {
|
|
49
|
+
connection: ConnectionConfig;
|
|
50
|
+
audio: AudioConfig;
|
|
51
|
+
operator: {
|
|
52
|
+
rate: number;
|
|
53
|
+
timeout: number;
|
|
54
|
+
assignmentStrategy: 'random';
|
|
55
|
+
};
|
|
56
|
+
characters: Record<string, any>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@coeiro-operator/core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Core utilities and shared functionality for COEIRO Operator",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"os": [
|
|
9
|
+
"darwin",
|
|
10
|
+
"linux",
|
|
11
|
+
"win32"
|
|
12
|
+
],
|
|
13
|
+
"cpu": [
|
|
14
|
+
"x64",
|
|
15
|
+
"arm64",
|
|
16
|
+
"ia32"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc --build",
|
|
20
|
+
"test": "vitest",
|
|
21
|
+
"test:watch": "vitest --watch",
|
|
22
|
+
"type-check": "tsc --noEmit",
|
|
23
|
+
"lint": "eslint src --ext .ts,.js",
|
|
24
|
+
"format": "prettier --write \"src/**/*.{ts,js,json,md}\""
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@coeiro-operator/common": "^1.0.0",
|
|
28
|
+
"@coeiro-operator/term-bg": "^1.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.18.3",
|
|
32
|
+
"typescript": "^5.7.3",
|
|
33
|
+
"vitest": "^3.2.4"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"core",
|
|
37
|
+
"utilities",
|
|
38
|
+
"config",
|
|
39
|
+
"logger"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/otolab/coeiro-operator.git",
|
|
45
|
+
"directory": "packages/core"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/otolab/coeiro-operator#readme",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/otolab/coeiro-operator/issues"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
}
|
|
54
|
+
}
|