@ai.weget.jp/bot 0.1.9 → 0.1.10

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.
@@ -47,7 +47,10 @@
47
47
  <header class="app-header">
48
48
  <div class="brand-row compact">
49
49
  <img src="./logo.png" alt="weget logo" class="brand-logo" />
50
- <h1>Trade Console</h1>
50
+ <div class="brand-copy">
51
+ <h1>Bot Host Console</h1>
52
+ <span>codex host, skills, and local runtime</span>
53
+ </div>
51
54
  </div>
52
55
  <div class="status-wrap">
53
56
  <span class="label">Status</span>
@@ -55,15 +58,37 @@
55
58
  </div>
56
59
  </header>
57
60
 
58
- <nav class="tabs">
59
- <button type="button" class="tab-btn is-active" data-tab="coin">Coin</button>
60
- <button type="button" class="tab-btn" data-tab="fx">FX</button>
61
- <button type="button" class="tab-btn" data-tab="ai">AI</button>
62
- <button type="button" class="tab-btn" data-tab="config">Config</button>
61
+ <nav class="tabs-shell">
62
+ <div class="tab-group">
63
+ <span class="tab-group-label">Host</span>
64
+ <div class="tabs">
65
+ <button type="button" class="tab-btn" data-tab="config">Host Control</button>
66
+ <button type="button" class="tab-btn" data-tab="ai">Codex Console</button>
67
+ </div>
68
+ </div>
69
+ <div class="tab-group">
70
+ <span class="tab-group-label">Installed Skills</span>
71
+ <div class="tabs">
72
+ <button id="tab-btn-skill-gmo-coin" type="button" class="tab-btn is-active" data-tab="coin">skill-gmo-coin</button>
73
+ <button id="tab-btn-skill-gmo-fx" type="button" class="tab-btn" data-tab="fx">skill-gmo-fx</button>
74
+ <button id="tab-btn-skill-browser" type="button" class="tab-btn" data-tab="browser">skill-browser</button>
75
+ </div>
76
+ </div>
63
77
  </nav>
64
78
 
65
79
  <section id="tab-coin" class="tab-panel is-active">
66
80
  <section class="panel market-shell coin-theme">
81
+ <div class="skill-surface-head">
82
+ <div>
83
+ <div class="skill-surface-kicker">Installed Skill</div>
84
+ <h2 id="coin-skill-title">GMO Coin Skill</h2>
85
+ <div id="coin-skill-package" class="skill-surface-package">@ai.weget.jp/skill-gmo-coin</div>
86
+ </div>
87
+ <div class="skill-surface-statuses">
88
+ <span id="coin-skill-enabled" class="skill-surface-badge">enabled</span>
89
+ <span id="coin-skill-version" class="skill-surface-badge is-muted">builtin</span>
90
+ </div>
91
+ </div>
67
92
  <div class="market-layout">
68
93
  <div class="market-main">
69
94
  <section class="panel market-table-panel">
@@ -191,6 +216,17 @@
191
216
 
192
217
  <section id="tab-fx" class="tab-panel">
193
218
  <section class="panel market-shell fx-theme">
219
+ <div class="skill-surface-head">
220
+ <div>
221
+ <div class="skill-surface-kicker">Installed Skill</div>
222
+ <h2 id="fx-skill-title">GMO FX Skill</h2>
223
+ <div id="fx-skill-package" class="skill-surface-package">@ai.weget.jp/skill-gmo-fx</div>
224
+ </div>
225
+ <div class="skill-surface-statuses">
226
+ <span id="fx-skill-enabled" class="skill-surface-badge">enabled</span>
227
+ <span id="fx-skill-version" class="skill-surface-badge is-muted">builtin</span>
228
+ </div>
229
+ </div>
194
230
  <div class="market-layout">
195
231
  <div class="market-main">
196
232
  <section class="panel market-table-panel">
@@ -318,12 +354,41 @@
318
354
  </section>
319
355
  </section>
320
356
 
357
+ <section id="tab-browser" class="tab-panel">
358
+ <section class="panel">
359
+ <div class="skill-surface-head">
360
+ <div>
361
+ <div class="skill-surface-kicker">Installed Skill</div>
362
+ <h2 id="browser-skill-title">Browser Skill</h2>
363
+ <div id="browser-skill-package" class="skill-surface-package">@ai.weget.jp/skill-browser</div>
364
+ </div>
365
+ <div class="skill-surface-statuses">
366
+ <span id="browser-skill-enabled" class="skill-surface-badge">enabled</span>
367
+ <span id="browser-skill-version" class="skill-surface-badge is-muted">builtin</span>
368
+ </div>
369
+ </div>
370
+ <div class="browser-skill-shell">
371
+ <div class="browser-skill-card">
372
+ <h3>Local UI Status</h3>
373
+ <p class="muted">这个 skill 已经作为公开包安装进宿主,但当前本地页仍是 scaffold,占位用于后续接 browser runtime。</p>
374
+ </div>
375
+ <div class="browser-skill-card">
376
+ <h3>Declared Tools</h3>
377
+ <div id="browser-skill-tools" class="skill-tags"></div>
378
+ </div>
379
+ </div>
380
+ </section>
381
+ </section>
382
+
321
383
  <section id="tab-ai" class="tab-panel">
322
384
  <section class="panel">
323
- <h2>AI Chat</h2>
385
+ <div class="panel-title-block">
386
+ <h2>Codex Console</h2>
387
+ <p class="muted">本地桌面聊天同样走 Codex 中枢,不单独保存任何模型 key。</p>
388
+ </div>
324
389
  <div id="messages" class="messages"></div>
325
390
  <form id="chat-form" class="composer">
326
- <textarea id="chat-input" rows="3" placeholder="Type your message..." required></textarea>
391
+ <textarea id="chat-input" rows="3" placeholder="Ask Codex to inspect code, reason about tasks, or operate through enabled skills..." required></textarea>
327
392
  <button id="send-btn" type="submit">Send</button>
328
393
  </form>
329
394
  </section>
@@ -331,63 +396,89 @@
331
396
 
332
397
  <section id="tab-config" class="tab-panel">
333
398
  <section class="panel">
334
- <h2>Bot Config</h2>
335
- <div id="session" class="muted">not logged in</div>
336
- <h3>GMO API Keys</h3>
337
- <label>
338
- <span>Error Log Directory</span>
339
- <input id="error-log-dir" type="text" placeholder="e.g. D:\\weget-bot\\logs" />
340
- </label>
341
- <label>
342
- <span>Risk Daily Loss Limit (JPY)</span>
343
- <input id="risk-daily-loss-limit-jpy" type="number" min="1" step="1" />
344
- </label>
345
- <label>
346
- <span>Crypto API Key</span>
347
- <input id="crypto-api-key" type="text" />
348
- </label>
349
- <label>
350
- <span>Crypto API Secret</span>
351
- <div class="inline-input-row">
352
- <input id="crypto-api-secret" type="password" />
353
- <button id="toggle-crypto-secret-btn" type="button" class="secondary">显示</button>
399
+ <div class="panel-title-block">
400
+ <h2>Host Control</h2>
401
+ <p class="muted">本地宿主状态、Codex 登录、默认模型、skill 状态和交易凭据都在这里管理。</p>
402
+ </div>
403
+ <section class="host-section">
404
+ <div class="host-section-head">
405
+ <h3>Session & Runtime</h3>
406
+ <span id="session" class="muted">local mode (not logged in)</span>
354
407
  </div>
355
- </label>
356
- <label>
357
- <span>FX API Key</span>
358
- <input id="fx-api-key" type="text" />
359
- </label>
360
- <label>
361
- <span>FX API Secret</span>
362
- <div class="inline-input-row">
363
- <input id="fx-api-secret" type="password" />
364
- <button id="toggle-fx-secret-btn" type="button" class="secondary">显示</button>
408
+ <div id="runtime-info" class="runtime-grid"></div>
409
+ </section>
410
+ <section class="host-section">
411
+ <h3>Codex Auth</h3>
412
+ <div class="codex-auth-box">
413
+ <div class="codex-auth-head">
414
+ <span class="muted">本地 OAuth 登录仅保存在当前机器,不通过 Platform 下发。</span>
415
+ </div>
416
+ <div class="codex-auth-actions">
417
+ <button id="codex-login-btn" type="button" class="secondary">Start Login</button>
418
+ <button id="codex-copy-login-btn" type="button" class="secondary">Copy Command</button>
419
+ <button id="codex-refresh-auth-btn" type="button" class="secondary">Refresh Status</button>
420
+ </div>
421
+ <code id="codex-login-command" class="codex-command">codex login --device-auth</code>
365
422
  </div>
366
- </label>
367
- <div class="row config-actions">
368
- <button id="save-api-config-btn" type="button">Save API Keys</button>
369
- </div>
370
- <h3>AI Settings</h3>
371
- <label>
372
- <span>模型</span>
373
- <select id="ai-model">
374
- <option value="gpt-4.1-mini">gpt-4.1-mini</option>
375
- <option value="gpt-4.1">gpt-4.1</option>
376
- <option value="gpt-4o-mini">gpt-4o-mini</option>
377
- </select>
378
- </label>
379
- <label>
380
- <span>OpenAI API Key</span>
381
- <div class="inline-input-row">
382
- <input id="openai-api-key" type="password" />
383
- <button id="toggle-openai-key-btn" type="button" class="secondary">显示</button>
423
+ </section>
424
+ <section class="host-section">
425
+ <h3>Default Model</h3>
426
+ <label>
427
+ <span>模型</span>
428
+ <select id="ai-model">
429
+ <option value="gpt-4.1-mini">gpt-4.1-mini</option>
430
+ <option value="gpt-4.1">gpt-4.1</option>
431
+ <option value="gpt-4o-mini">gpt-4o-mini</option>
432
+ </select>
433
+ </label>
434
+ <div class="row config-actions">
435
+ <button id="save-ai-config-btn" type="button">保存 AI 设置</button>
436
+ <button id="logout-btn" type="button" class="secondary">Logout</button>
384
437
  </div>
385
- </label>
386
- <div class="row config-actions">
387
- <button id="save-ai-config-btn" type="button">保存 AI 设置</button>
388
- <button id="logout-btn" type="button" class="secondary">Logout</button>
389
- </div>
390
- <div id="runtime-info" class="runtime-grid"></div>
438
+ </section>
439
+ <section class="host-section">
440
+ <h3>GMO Credentials</h3>
441
+ <label>
442
+ <span>Error Log Directory</span>
443
+ <input id="error-log-dir" type="text" placeholder="e.g. D:\\weget-bot\\logs" />
444
+ </label>
445
+ <label>
446
+ <span>Risk Daily Loss Limit (JPY)</span>
447
+ <input id="risk-daily-loss-limit-jpy" type="number" min="1" step="1" />
448
+ </label>
449
+ <label>
450
+ <span>Crypto API Key</span>
451
+ <input id="crypto-api-key" type="text" />
452
+ </label>
453
+ <label>
454
+ <span>Crypto API Secret</span>
455
+ <div class="inline-input-row">
456
+ <input id="crypto-api-secret" type="password" />
457
+ <button id="toggle-crypto-secret-btn" type="button" class="secondary">显示</button>
458
+ </div>
459
+ </label>
460
+ <label>
461
+ <span>FX API Key</span>
462
+ <input id="fx-api-key" type="text" />
463
+ </label>
464
+ <label>
465
+ <span>FX API Secret</span>
466
+ <div class="inline-input-row">
467
+ <input id="fx-api-secret" type="password" />
468
+ <button id="toggle-fx-secret-btn" type="button" class="secondary">显示</button>
469
+ </div>
470
+ </label>
471
+ <div class="row config-actions">
472
+ <button id="save-api-config-btn" type="button">Save API Keys</button>
473
+ </div>
474
+ </section>
475
+ <section class="host-section skill-panel">
476
+ <div class="skill-panel-head">
477
+ <h3>Managed Skills</h3>
478
+ <span class="muted">本地宿主实际识别到的 skill 与启用状态</span>
479
+ </div>
480
+ <div id="skill-state-list" class="skill-state-list"></div>
481
+ </section>
391
482
  </section>
392
483
  </section>
393
484
  </main>
@@ -110,6 +110,44 @@ body {
110
110
  color: #5e6b81;
111
111
  }
112
112
 
113
+ .codex-auth-box {
114
+ margin-top: 14px;
115
+ padding: 12px;
116
+ border: 1px solid #d9e2ef;
117
+ border-radius: 10px;
118
+ background: #f7faff;
119
+ }
120
+
121
+ .codex-auth-head {
122
+ display: flex;
123
+ justify-content: space-between;
124
+ gap: 12px;
125
+ align-items: center;
126
+ margin-bottom: 10px;
127
+ }
128
+
129
+ .codex-auth-head h3 {
130
+ margin: 0;
131
+ }
132
+
133
+ .codex-auth-actions {
134
+ display: flex;
135
+ gap: 8px;
136
+ flex-wrap: wrap;
137
+ margin-bottom: 10px;
138
+ }
139
+
140
+ .codex-command {
141
+ display: block;
142
+ padding: 10px 12px;
143
+ border-radius: 8px;
144
+ background: #0f172a;
145
+ color: #e2e8f0;
146
+ font-family: Consolas, "Courier New", monospace;
147
+ font-size: 13px;
148
+ overflow-x: auto;
149
+ }
150
+
113
151
  .status-pill {
114
152
  display: inline-block;
115
153
  border-radius: 999px;
@@ -132,10 +170,29 @@ body {
132
170
  color: #92400e;
133
171
  }
134
172
 
173
+ .tabs-shell {
174
+ display: grid;
175
+ gap: 10px;
176
+ margin-bottom: 12px;
177
+ }
178
+
179
+ .tab-group {
180
+ display: grid;
181
+ gap: 6px;
182
+ }
183
+
184
+ .tab-group-label {
185
+ color: #60708a;
186
+ font-size: 12px;
187
+ font-weight: 700;
188
+ letter-spacing: 0.04em;
189
+ text-transform: uppercase;
190
+ }
191
+
135
192
  .tabs {
136
193
  display: flex;
137
194
  gap: 8px;
138
- margin-bottom: 12px;
195
+ flex-wrap: wrap;
139
196
  }
140
197
 
141
198
  .tab-btn {
@@ -169,10 +226,98 @@ body {
169
226
  box-shadow: 0 8px 20px rgba(20, 25, 45, 0.08);
170
227
  }
171
228
 
229
+ .panel-title-block {
230
+ display: grid;
231
+ gap: 4px;
232
+ margin-bottom: 12px;
233
+ }
234
+
235
+ .panel-title-block h2,
236
+ .host-section h3 {
237
+ margin: 0;
238
+ }
239
+
240
+ .panel-title-block p {
241
+ margin: 0;
242
+ }
243
+
244
+ .host-section {
245
+ display: grid;
246
+ gap: 10px;
247
+ padding: 14px;
248
+ margin-top: 14px;
249
+ border: 1px solid #dbe4f1;
250
+ border-radius: 12px;
251
+ background: #fbfdff;
252
+ }
253
+
254
+ .host-section-head {
255
+ display: flex;
256
+ justify-content: space-between;
257
+ align-items: baseline;
258
+ gap: 12px;
259
+ flex-wrap: wrap;
260
+ }
261
+
172
262
  .market-shell {
173
263
  background: #eef2f6;
174
264
  }
175
265
 
266
+ .skill-surface-head {
267
+ display: flex;
268
+ justify-content: space-between;
269
+ align-items: flex-start;
270
+ gap: 16px;
271
+ margin-bottom: 12px;
272
+ }
273
+
274
+ .skill-surface-kicker {
275
+ color: #60708a;
276
+ font-size: 12px;
277
+ font-weight: 700;
278
+ letter-spacing: 0.04em;
279
+ text-transform: uppercase;
280
+ margin-bottom: 4px;
281
+ }
282
+
283
+ .skill-surface-package {
284
+ color: #49617f;
285
+ font-family: Consolas, "Courier New", monospace;
286
+ font-size: 13px;
287
+ }
288
+
289
+ .skill-surface-statuses {
290
+ display: flex;
291
+ gap: 8px;
292
+ flex-wrap: wrap;
293
+ justify-content: flex-end;
294
+ }
295
+
296
+ .skill-surface-badge {
297
+ border-radius: 999px;
298
+ padding: 5px 10px;
299
+ font-size: 12px;
300
+ font-weight: 700;
301
+ background: #dcfce7;
302
+ color: #166534;
303
+ }
304
+
305
+ .skill-surface-badge.is-disabled {
306
+ background: #fee2e2;
307
+ color: #991b1b;
308
+ }
309
+
310
+ .skill-surface-badge.is-muted {
311
+ background: #e8eef8;
312
+ color: #294267;
313
+ }
314
+
315
+ .tab-btn.is-disabled {
316
+ background: #f3f4f6;
317
+ color: #76859b;
318
+ border-color: #d8dee8;
319
+ }
320
+
176
321
  .market-layout {
177
322
  display: grid;
178
323
  grid-template-columns: 1fr 340px;
@@ -676,6 +821,176 @@ button.is-loading::before {
676
821
  word-break: break-all;
677
822
  }
678
823
 
824
+ .skill-panel {
825
+ display: grid;
826
+ gap: 10px;
827
+ }
828
+
829
+ .skill-panel-head {
830
+ display: flex;
831
+ justify-content: space-between;
832
+ align-items: baseline;
833
+ gap: 12px;
834
+ flex-wrap: wrap;
835
+ }
836
+
837
+ .skill-panel-head h3 {
838
+ margin: 0;
839
+ }
840
+
841
+ .skill-state-list {
842
+ display: grid;
843
+ gap: 10px;
844
+ }
845
+
846
+ .skill-card {
847
+ border: 1px solid #dbe4f1;
848
+ border-radius: 10px;
849
+ padding: 12px;
850
+ background: #f8fbff;
851
+ display: grid;
852
+ gap: 10px;
853
+ }
854
+
855
+ .skill-card.is-disabled {
856
+ background: #f6f7f9;
857
+ border-color: #d7dde7;
858
+ }
859
+
860
+ .skill-card-head {
861
+ display: flex;
862
+ justify-content: space-between;
863
+ align-items: flex-start;
864
+ gap: 10px;
865
+ }
866
+
867
+ .skill-card-title {
868
+ display: grid;
869
+ gap: 4px;
870
+ }
871
+
872
+ .skill-card-title strong {
873
+ font-size: 15px;
874
+ }
875
+
876
+ .skill-card-title code {
877
+ color: #5a6a84;
878
+ }
879
+
880
+ .skill-card-section {
881
+ display: grid;
882
+ gap: 6px;
883
+ }
884
+
885
+ .skill-section-label {
886
+ color: #62748f;
887
+ font-size: 12px;
888
+ font-weight: 700;
889
+ letter-spacing: 0.03em;
890
+ text-transform: uppercase;
891
+ }
892
+
893
+ .skill-package-code {
894
+ color: #334862;
895
+ word-break: break-all;
896
+ }
897
+
898
+ .skill-badges {
899
+ display: flex;
900
+ gap: 6px;
901
+ flex-wrap: wrap;
902
+ justify-content: flex-end;
903
+ }
904
+
905
+ .skill-badge {
906
+ border-radius: 999px;
907
+ padding: 3px 8px;
908
+ font-size: 12px;
909
+ font-weight: 600;
910
+ }
911
+
912
+ .skill-badge.enabled {
913
+ background: #dcfce7;
914
+ color: #166534;
915
+ }
916
+
917
+ .skill-badge.disabled {
918
+ background: #fee2e2;
919
+ color: #991b1b;
920
+ }
921
+
922
+ .skill-badge.status {
923
+ background: #e8eef8;
924
+ color: #294267;
925
+ }
926
+
927
+ .skill-meta {
928
+ display: grid;
929
+ grid-template-columns: repeat(2, minmax(0, 1fr));
930
+ gap: 8px 12px;
931
+ font-size: 13px;
932
+ }
933
+
934
+ .skill-meta-row {
935
+ display: grid;
936
+ gap: 2px;
937
+ }
938
+
939
+ .skill-meta-row span {
940
+ color: #62748f;
941
+ }
942
+
943
+ .skill-meta-row strong,
944
+ .skill-meta-row code {
945
+ word-break: break-word;
946
+ }
947
+
948
+ .skill-tags {
949
+ display: flex;
950
+ gap: 6px;
951
+ flex-wrap: wrap;
952
+ }
953
+
954
+ .skill-tag {
955
+ background: #e7f0ff;
956
+ color: #1f4d8f;
957
+ border-radius: 999px;
958
+ padding: 4px 8px;
959
+ font-size: 12px;
960
+ }
961
+
962
+ .skill-tag.permissions {
963
+ background: #fff1db;
964
+ color: #8a4b08;
965
+ }
966
+
967
+ .skill-empty {
968
+ border: 1px dashed #cbd6e5;
969
+ border-radius: 10px;
970
+ padding: 16px;
971
+ color: #617289;
972
+ background: #f8fbff;
973
+ }
974
+
975
+ .browser-skill-shell {
976
+ display: grid;
977
+ grid-template-columns: repeat(2, minmax(0, 1fr));
978
+ gap: 12px;
979
+ }
980
+
981
+ .browser-skill-card {
982
+ border: 1px solid #dbe4f1;
983
+ border-radius: 12px;
984
+ background: #f8fbff;
985
+ padding: 14px;
986
+ display: grid;
987
+ gap: 10px;
988
+ }
989
+
990
+ .browser-skill-card h3 {
991
+ margin: 0;
992
+ }
993
+
679
994
  .chat-tools {
680
995
  display: flex;
681
996
  gap: 8px;
@@ -825,4 +1140,12 @@ button.is-loading::before {
825
1140
  .positions-grid {
826
1141
  grid-template-columns: 1fr;
827
1142
  }
1143
+
1144
+ .skill-meta {
1145
+ grid-template-columns: 1fr;
1146
+ }
1147
+
1148
+ .browser-skill-shell {
1149
+ grid-template-columns: 1fr;
1150
+ }
828
1151
  }
@@ -1 +1 @@
1
- const o=o=>o&&"object"==typeof o?o:{},t=(o,r="")=>{if(null==o)return o;const e=String(r||"");if("string"==typeof o)return/(password|token|authorization|api_?key|secret|cookie|jwt)/i.test(e)?o.length<=8?"****":`${o.slice(0,3)}****${o.slice(-3)}`:o.length>2e3?`${o.slice(0,2e3)}...(truncated)`:o;if("number"==typeof o||"boolean"==typeof o)return o;if(Array.isArray(o)){const r=o.slice(0,50).map(o=>t(o,e));return o.length>50&&r.push(`...(${o.length-50} more items)`),r}if("object"==typeof o){const r={};for(const[e,i]of Object.entries(o))r[e]=t(i,e);return r}return String(o)};export const createAuthApiService=({getEnv:r,emitLog:e,getCurrentSession:i})=>{const n=async t=>{const r=await t.text();if(!r)return{};try{const t=JSON.parse(r);return o(t)}catch{return{raw:r}}};return{signInWithLoginApi:async(t,i)=>{const l=r();if(!l.loginApiUrl)throw new Error("Missing loginApiUrl in local config");const s=new AbortController,a=setTimeout(()=>s.abort(),15e3);let p;e("[auth] login api request",{url:l.loginApiUrl,email:t});try{p=await fetch(l.loginApiUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t,password:i,client:"bot"}),signal:s.signal})}catch(t){const r=o(o(t).cause),e=r?`${String(r.code||"")} ${String(r.syscall||"")} ${String(r.hostname||"")}`.trim():"";throw new Error("login fetch failed"+(e?`: ${e}`:""))}finally{clearTimeout(a)}e("[auth] login api response",{status:p.status});const c=await n(p);if(!p.ok)throw new Error(String(c.error||c.message||c.msg||"Login failed"));return((t,r)=>{const e=o(t),i=o(e.data),n=o(e.user),l=o(i.user),s=e.access_token||e.token||e.jwt||i.access_token||i.token;if(!s)throw new Error("Login API response missing token field (access_token/token/jwt)");const a=e.user_id||n.id||e.sub||i.user_id||l.id||"",p=e.email||n.email||i.email||l.email||r,c=e.chat_api_key||i.chat_api_key||"",u=e.chat_model||i.chat_model||"gpt-4.1-mini",g=e.chat_system_prompt||i.chat_system_prompt||"";return{accessToken:String(s),userId:String(a),email:String(p),chatApiKey:String(c),chatModel:String(u),chatSystemPrompt:String(g)}})(c,t)},callBotApi:async(o,r)=>{if(!o)throw new Error("memory api url is missing");const l=i();if(!l?.accessToken)throw new Error("bot session access token missing");e("[bot-api] request",{url:o,bot_id:l.botId||"",body:t(r)});const s=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l.accessToken}`},body:JSON.stringify(r||{})}),a=await n(s);if(e("[bot-api] response",{url:o,status:s.status,ok:s.ok,body:t(a)}),!s.ok)throw new Error(String(a.error||a.message||`api failed: ${s.status}`));return a},requestBotImageUploadUrl:async({filename:o,contentType:l})=>{const s=(()=>{const o=r();return o.botUploadApiUrl?o.botUploadApiUrl:o.loginApiUrl?o.loginApiUrl.replace(/\/login\/?$/i,"/bot/upload-url"):""})();if(!s)throw new Error("Missing upload/login api url in local config");const a=i();if(!a?.accessToken)throw new Error("bot session access token missing");e("[bot-api] request",{url:s,bot_id:a.botId||"",body:t({bot_id:a.botId||"",filename:o,content_type:l})});const p=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.accessToken}`},body:JSON.stringify({bot_id:a.botId||"",filename:o,content_type:l})}),c=await n(p);if(e("[bot-api] response",{url:s,status:p.status,ok:p.ok,body:t(c)}),!p.ok)throw new Error(String(c.error||c.message||"failed to request bot upload url"));if(!c.upload_url||!c.preview_url)throw new Error("upload url api response missing upload_url or preview_url");return{upload_url:String(c.upload_url),preview_url:String(c.preview_url),file_url:c.file_url?String(c.file_url):void 0}},uploadImageToSignedUrl:async({uploadUrl:o,contentType:t,bytes:r})=>{const e=await fetch(o,{method:"PUT",headers:{"Content-Type":t},body:r});if(!e.ok){const o=await e.text();throw new Error(`s3 upload failed: ${e.status} ${o}`)}},getBotMemoryUploadApiUrl:()=>{const o=r();return o.botMemoryUploadApiUrl?o.botMemoryUploadApiUrl:o.loginApiUrl?o.loginApiUrl.replace(/\/login\/?$/i,"/bot/memory/upload"):""},getBotMemorySyncApiUrl:()=>{const o=r();return o.botMemorySyncApiUrl?o.botMemorySyncApiUrl:o.loginApiUrl?o.loginApiUrl.replace(/\/login\/?$/i,"/bot/memory/sync"):""},getBotMemoryQueryApiUrl:()=>{const o=r();return o.botMemoryQueryApiUrl?o.botMemoryQueryApiUrl:o.loginApiUrl?o.loginApiUrl.replace(/\/login\/?$/i,"/bot/memory/query"):""},getBotWorkflowListApiUrl:()=>{const o=r();return o.botWorkflowListApiUrl?o.botWorkflowListApiUrl:o.loginApiUrl?o.loginApiUrl.replace(/\/login\/?$/i,"/bot/workflow/list"):""},getBotWorkflowQueryApiUrl:()=>{const o=r();return o.botWorkflowQueryApiUrl?o.botWorkflowQueryApiUrl:o.loginApiUrl?o.loginApiUrl.replace(/\/login\/?$/i,"/bot/workflow/query"):""},getBotWorkflowVerificationUpdateApiUrl:()=>{const o=r();return o.botWorkflowVerificationUpdateApiUrl?o.botWorkflowVerificationUpdateApiUrl:o.loginApiUrl?o.loginApiUrl.replace(/\/login\/?$/i,"/bot/workflow/verification/update"):""},getBotRuntimeConfigApiUrl:()=>{const o=r();return o.botRuntimeConfigApiUrl?o.botRuntimeConfigApiUrl:o.loginApiUrl?o.loginApiUrl.replace(/\/login\/?$/i,"/bot/runtime-config"):""}}};
1
+ const t=t=>t&&"object"==typeof t?t:{},e=(t,o="")=>{if(null==t)return t;const r=String(o||"");if("string"==typeof t)return/(password|token|authorization|api_?key|secret|cookie|jwt)/i.test(r)?t.length<=8?"****":`${t.slice(0,3)}****${t.slice(-3)}`:t.length>2e3?`${t.slice(0,2e3)}...(truncated)`:t;if("number"==typeof t||"boolean"==typeof t)return t;if(Array.isArray(t)){const o=t.slice(0,50).map(t=>e(t,r));return t.length>50&&o.push(`...(${t.length-50} more items)`),o}if("object"==typeof t){const o={};for(const[r,i]of Object.entries(t))o[r]=e(i,r);return o}return String(t)};export const createAuthApiService=({getEnv:o,emitLog:r,getCurrentSession:i})=>{const n=async e=>{const o=await e.text();if(!o)return{};try{const e=JSON.parse(o);return t(e)}catch{return{raw:o}}};return{signInWithLoginApi:async(e,i)=>{const s=o();if(!s.loginApiUrl)throw new Error("Missing loginApiUrl in local config");const l=new AbortController,a=setTimeout(()=>l.abort(),15e3);let c;r("[auth] login api request",{url:s.loginApiUrl,email:e});try{c=await fetch(s.loginApiUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:e,password:i,client:"bot"}),signal:l.signal})}catch(e){const o=t(t(e).cause),r=o?`${String(o.code||"")} ${String(o.syscall||"")} ${String(o.hostname||"")}`.trim():"";throw new Error("login fetch failed"+(r?`: ${r}`:""))}finally{clearTimeout(a)}r("[auth] login api response",{status:c.status});const p=await n(c);if(!c.ok)throw new Error(String(p.error||p.message||p.msg||"Login failed"));return((e,o)=>{const r=t(e),i=t(r.data),n=t(r.user),s=t(i.user),l=r.access_token||r.token||r.jwt||i.access_token||i.token;if(!l)throw new Error("Login API response missing token field (access_token/token/jwt)");const a=r.user_id||n.id||r.sub||i.user_id||s.id||"",c=r.email||n.email||i.email||s.email||o,p=r.chat_model||i.chat_model||"gpt-4.1-mini",u=r.chat_system_prompt||i.chat_system_prompt||"";return{accessToken:String(l),userId:String(a),email:String(c),chatModel:String(p),chatSystemPrompt:String(u)}})(p,e)},callBotApi:async(t,o)=>{if(!t)throw new Error("bot control api url is missing");const s=i();if(!s?.accessToken)throw new Error("bot session access token missing");r("[bot-api] request",{url:t,bot_id:s.botId||"",body:e(o)});const l=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s.accessToken}`},body:JSON.stringify(o||{})}),a=await n(l);if(r("[bot-api] response",{url:t,status:l.status,ok:l.ok,body:e(a)}),!l.ok)throw new Error(String(a.error||a.message||`api failed: ${l.status}`));return a},requestBotImageUploadUrl:async({filename:t,contentType:s})=>{const l=(()=>{const t=o();return t.botUploadApiUrl?t.botUploadApiUrl:t.loginApiUrl?t.loginApiUrl.replace(/\/login\/?$/i,"/bot/upload-url"):""})();if(!l)throw new Error("Missing upload/login api url in local config");const a=i();if(!a?.accessToken)throw new Error("bot session access token missing");r("[bot-api] request",{url:l,bot_id:a.botId||"",body:e({bot_id:a.botId||"",filename:t,content_type:s})});const c=await fetch(l,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.accessToken}`},body:JSON.stringify({bot_id:a.botId||"",filename:t,content_type:s})}),p=await n(c);if(r("[bot-api] response",{url:l,status:c.status,ok:c.ok,body:e(p)}),!c.ok)throw new Error(String(p.error||p.message||"failed to request bot upload url"));if(!p.upload_url||!p.preview_url)throw new Error("upload url api response missing upload_url or preview_url");return{upload_url:String(p.upload_url),preview_url:String(p.preview_url),file_url:p.file_url?String(p.file_url):void 0}},uploadImageToSignedUrl:async({uploadUrl:t,contentType:e,bytes:o})=>{const r=await fetch(t,{method:"PUT",headers:{"Content-Type":e},body:o});if(!r.ok){const t=await r.text();throw new Error(`s3 upload failed: ${r.status} ${t}`)}},getBotRuntimeConfigApiUrl:()=>{const t=o();return t.botRuntimeConfigApiUrl?t.botRuntimeConfigApiUrl:t.loginApiUrl?t.loginApiUrl.replace(/\/login\/?$/i,"/bot/runtime-config"):""},getBotSkillsListApiUrl:()=>{const t=o();return t.botSkillsListApiUrl?t.botSkillsListApiUrl:t.loginApiUrl?t.loginApiUrl.replace(/\/login\/?$/i,"/bot/skills/list"):""}}};
@@ -0,0 +1 @@
1
+ import t from"node:fs/promises";import e from"node:os";import o from"node:path";import{spawn as r}from"node:child_process";const n="gpt-4.1-mini",i=async t=>{if(!t)return"";const e=[];for await(const o of t)e.push(Buffer.isBuffer(o)?o:Buffer.from(String(o)));return Buffer.concat(e).toString("utf8")},d=async({args:t,stdinText:e,cwd:o})=>new Promise((n,d)=>{const s=r("codex",t,{cwd:o,stdio:["pipe","pipe","pipe"],windowsHide:!0});let a=!1;const c=t=>{a||(a=!0,t())},u=i(s.stdout),l=i(s.stderr);s.on("error",t=>{c(()=>d(t))}),s.on("close",async t=>{const[e,o]=await Promise.all([u,l]);c(()=>n({stdout:e,stderr:o,exitCode:Number.isFinite(t)?Number(t):1}))}),"string"==typeof e&&s.stdin.write(e),s.stdin.end()});export const createCodexService=({getModel:i,getWorkingDir:s,emitLog:a})=>({runPrompt:async({prompt:r,model:c,contextLabel:u})=>{const l=String(c||i()||n).trim()||n,g=s(),m=await t.mkdtemp(o.join(e.tmpdir(),"weget-codex-")),f=o.join(m,"last-message.txt"),w=`${String(r||"").trim()}\n\nRespond as plain text only.`;a("[codex] exec start",{model:l,cwd:g,context:String(u||"").trim()});try{const e=await d({cwd:g,args:["exec","-C",g,"--skip-git-repo-check","-m",l,"-o",f,"-"],stdinText:w}),o=await t.readFile(f,"utf8").catch(()=>"");if(0!==e.exitCode)throw new Error((e.stderr||e.stdout||`codex exec failed with code ${e.exitCode}`).trim());const r=String(o||e.stdout||"").trim();if(!r)throw new Error("codex returned empty output");return a("[codex] exec completed",{model:l,context:String(u||"").trim()}),r}finally{await t.rm(m,{recursive:!0,force:!0}).catch(()=>{})}},getAuthStatus:async()=>{try{const t=await d({cwd:s(),args:["login","status"]}),e=String(t.stdout||t.stderr||"").trim();return 0!==t.exitCode?{status:"unknown",detail:e||"codex login status failed"}:/logged in/i.test(e)?{status:"logged_in",detail:e}:/not logged in|logged out/i.test(e)?{status:"logged_out",detail:e}:{status:"unknown",detail:e||"unknown codex auth state"}}catch(t){return{status:"unknown",detail:t instanceof Error?t.message:String(t)}}},startLogin:async()=>{const t=s();try{if("win32"===process.platform)return r("cmd.exe",["/c","start",'""',"codex","login","--device-auth"],{cwd:t,detached:!0,stdio:"ignore",windowsHide:!1}).unref(),{ok:!0,detail:"Started `codex login --device-auth` in a new terminal window."};if("darwin"===process.platform){const e=`cd ${JSON.stringify(t)} && codex login --device-auth`;return r("open",["-a","Terminal",e],{cwd:t,detached:!0,stdio:"ignore"}).unref(),{ok:!0,detail:"Started `codex login --device-auth` in Terminal."}}const e=`cd ${JSON.stringify(t)} && codex login --device-auth; exec $SHELL`,o=[["x-terminal-emulator",["-e","bash","-lc",e]],["gnome-terminal",["--","bash","-lc",e]],["konsole",["-e","bash","-lc",e]]];for(const[e,n]of o)try{return r(e,n,{cwd:t,detached:!0,stdio:"ignore"}).unref(),{ok:!0,detail:`Started \`codex login --device-auth\` using ${e}.`}}catch{}return{ok:!1,detail:"No supported terminal launcher was found. Run `codex login --device-auth` manually."}}catch(t){const e=t instanceof Error?t.message:String(t);return a("[codex] login launch failed",{error:e}),{ok:!1,detail:e}}}});
@@ -1 +1 @@
1
- import e from"node:path";export const createWindowManager=({BrowserWindow:t,preloadPath:n,rendererDir:o,windowIconPath:i=""})=>{let r=null;return{getMainWindow:()=>r,createMainWindow:()=>(r=new t({width:1024,height:860,minWidth:1e3,minHeight:820,icon:i||void 0,autoHideMenuBar:!0,webPreferences:{preload:n,contextIsolation:!0,nodeIntegration:!1}}),r.loadFile(e.join(o,"index.html")),r.setMenuBarVisibility(!1),r),emitBotLog:e=>{r&&!r.isDestroyed()&&r.webContents.send("bot:log",e)},emitBotStatus:e=>{r&&!r.isDestroyed()&&r.webContents.send("bot:status",e)},emitChatEvent:e=>{r&&!r.isDestroyed()&&r.webContents.send("chat:event",e)}}};
1
+ import e from"node:path";export const createWindowManager=({BrowserWindow:t,preloadPath:n,rendererDir:o,windowIconPath:i=""})=>{let r=null;return{getMainWindow:()=>r,createMainWindow:()=>(r=new t({width:1024,height:860,minWidth:1e3,minHeight:820,icon:i||void 0,autoHideMenuBar:!0,webPreferences:{preload:n,contextIsolation:!0,nodeIntegration:!1}}),r.loadFile(e.join(o,"index.html")),r.setMenuBarVisibility(!1),r),emitBotLog:e=>{r&&!r.isDestroyed()&&r.webContents.send("bot:log",e)},emitBotStatus:e=>{r&&!r.isDestroyed()&&r.webContents.send("bot:status",e)},emitChatEvent:e=>{r&&!r.isDestroyed()&&r.webContents.send("chat:event",e)},emitTradeExecution:e=>{r&&!r.isDestroyed()&&r.webContents.send("trade:execution",e)}}};