playkit-sdk 1.4.0 → 1.6.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * playkit-sdk v1.4.0
2
+ * playkit-sdk v1.6.0
3
3
  * PlayKit SDK for JavaScript
4
4
  * @license SEE LICENSE IN LICENSE
5
5
  */
@@ -394,6 +394,48 @@
394
394
  }
395
395
  return { role, content };
396
396
  }
397
+ /**
398
+ * Convert an OpenAI-compatible tool call returned by the API into the canonical
399
+ * PlayKit message content part used for future requests.
400
+ */
401
+ function createToolCallContentPart(toolCall) {
402
+ var _a;
403
+ let input = {};
404
+ const args = (_a = toolCall.function) === null || _a === void 0 ? void 0 : _a.arguments;
405
+ if (typeof args === 'string' && args.trim()) {
406
+ try {
407
+ input = JSON.parse(args);
408
+ }
409
+ catch (_b) {
410
+ input = args;
411
+ }
412
+ }
413
+ return {
414
+ type: 'tool-call',
415
+ toolCallId: toolCall.id,
416
+ toolName: toolCall.function.name,
417
+ input,
418
+ };
419
+ }
420
+ /**
421
+ * Create a canonical PlayKit tool-result content part.
422
+ */
423
+ function createToolResultContentPart(toolCallId, toolName, result) {
424
+ if (typeof result === 'string') {
425
+ return {
426
+ type: 'tool-result',
427
+ toolCallId,
428
+ toolName,
429
+ output: { type: 'text', value: result },
430
+ };
431
+ }
432
+ return {
433
+ type: 'tool-result',
434
+ toolCallId,
435
+ toolName,
436
+ output: { type: 'json', value: result },
437
+ };
438
+ }
397
439
  /**
398
440
  * Error types that can be thrown by the SDK
399
441
  */
@@ -1177,7 +1219,7 @@
1177
1219
  }
1178
1220
 
1179
1221
  const SDK_TYPE = 'Javascript';
1180
- const SDK_VERSION = '"1.4.0"';
1222
+ const SDK_VERSION = '"1.6.0"';
1181
1223
  function getSDKHeaders() {
1182
1224
  return {
1183
1225
  'X-SDK-Type': SDK_TYPE,
@@ -1185,867 +1227,6 @@
1185
1227
  };
1186
1228
  }
1187
1229
 
1188
- /**
1189
- * Authentication Flow Manager
1190
- * Manages the headless authentication flow with automatic UI
1191
- *
1192
- * @deprecated This class is deprecated. Use DeviceAuthFlowManager instead.
1193
- * Will be removed in v2.0
1194
- */
1195
- // i18n translations
1196
- const translations$2 = {
1197
- en: {
1198
- signIn: 'Sign In / Register',
1199
- signInSubtitle: 'This game uses PlayKit for cost management.\nIf you don\'t have an account, we\'ll automatically register you. Sign up and get free AI game credits!',
1200
- email: 'Email',
1201
- phone: 'Phone',
1202
- emailPlaceholder: 'Enter your email address',
1203
- phonePlaceholder: 'Enter your phone number (+86 Only)',
1204
- sendCode: 'Send Code',
1205
- enterCode: 'Enter Code',
1206
- enterCodeSubtitle: "We've sent a 6-digit code to your",
1207
- verify: 'Verify',
1208
- pleaseEnterEmail: 'Please enter your email address',
1209
- pleaseEnterPhone: 'Please enter your phone number',
1210
- enterAllDigits: 'Please enter all 6 digits',
1211
- verificationFailed: 'Verification failed',
1212
- failedToSendCode: 'Failed to send code',
1213
- invalidCode: 'Invalid verification code',
1214
- },
1215
- zh: {
1216
- signIn: '登录/注册',
1217
- signInSubtitle: '本游戏使用PlayKit进行成本管理。\n如果您没有帐户,我们会为您自动注册。注册即送AI游戏积分!',
1218
- email: '邮箱',
1219
- phone: '手机',
1220
- emailPlaceholder: '请输入邮箱地址',
1221
- phonePlaceholder: '请输入手机号(仅限 +86)',
1222
- sendCode: '发送验证码',
1223
- enterCode: '输入验证码',
1224
- enterCodeSubtitle: '我们已向您发送了 6 位验证码:',
1225
- verify: '验证',
1226
- pleaseEnterEmail: '请输入邮箱地址',
1227
- pleaseEnterPhone: '请输入手机号',
1228
- enterAllDigits: '请输入完整的 6 位验证码',
1229
- verificationFailed: '验证失败',
1230
- failedToSendCode: '发送验证码失败',
1231
- invalidCode: '验证码无效',
1232
- },
1233
- 'zh-TW': {
1234
- signIn: '登入/註冊',
1235
- signInSubtitle: '本遊戲使用PlayKit進行成本管理。\n如果您沒有帳戶,我們會為您自動註冊。註冊即送AI遊戲積分!',
1236
- email: '電子郵件',
1237
- phone: '手機',
1238
- emailPlaceholder: '請輸入電子郵件地址',
1239
- phonePlaceholder: '請輸入手機號碼(僅限 +86)',
1240
- sendCode: '發送驗證碼',
1241
- enterCode: '輸入驗證碼',
1242
- enterCodeSubtitle: '我們已向您發送了 6 位驗證碼:',
1243
- verify: '驗證',
1244
- pleaseEnterEmail: '請輸入電子郵件地址',
1245
- pleaseEnterPhone: '請輸入手機號碼',
1246
- enterAllDigits: '請輸入完整的 6 位驗證碼',
1247
- verificationFailed: '驗證失敗',
1248
- failedToSendCode: '發送驗證碼失敗',
1249
- invalidCode: '驗證碼無效',
1250
- },
1251
- ja: {
1252
- signIn: 'サインイン/登録',
1253
- signInSubtitle: 'このゲームはPlayKitでコスト管理を行っています。\nアカウントをお持ちでない場合は、自動的に登録します。登録するとAIゲームクレジットがもらえます!',
1254
- email: 'メール',
1255
- phone: '電話',
1256
- emailPlaceholder: 'メールアドレスを入力してください',
1257
- phonePlaceholder: '電話番号を入力してください(+86のみ)',
1258
- sendCode: '認証コードを送信',
1259
- enterCode: '認証コードを入力',
1260
- enterCodeSubtitle: '6桁の認証コードを送信しました:',
1261
- verify: '検証',
1262
- pleaseEnterEmail: 'メールアドレスを入力してください',
1263
- pleaseEnterPhone: '電話番号を入力してください',
1264
- enterAllDigits: '6桁すべて入力してください',
1265
- verificationFailed: '検証に失敗しました',
1266
- failedToSendCode: '認証コードの送信に失敗しました',
1267
- invalidCode: '認証コードが無効です',
1268
- },
1269
- ko: {
1270
- signIn: '로그인/가입',
1271
- signInSubtitle: '이 게임은 PlayKit으로 비용 관리를 합니다.\n계정이 없으시면 자동으로 등록해 드립니다. 가입하면 AI 게임 크레딧을 받으세요!',
1272
- email: '이메일',
1273
- phone: '전화',
1274
- emailPlaceholder: '이메일 주소를 입력하세요',
1275
- phonePlaceholder: '전화번호를 입력하세요(+86만 지원)',
1276
- sendCode: '인증 코드 전송',
1277
- enterCode: '인증 코드 입력',
1278
- enterCodeSubtitle: '6자리 인증 코드를 보냈습니다:',
1279
- verify: '확인',
1280
- pleaseEnterEmail: '이메일 주소를 입력하세요',
1281
- pleaseEnterPhone: '전화번호를 입력하세요',
1282
- enterAllDigits: '6자리를 모두 입력하세요',
1283
- verificationFailed: '인증 실패',
1284
- failedToSendCode: '인증 코드 전송 실패',
1285
- invalidCode: '인증 코드가 잘못되었습니다',
1286
- },
1287
- };
1288
- class AuthFlowManager extends EventEmitter {
1289
- constructor(baseURL) {
1290
- super();
1291
- this.currentSessionId = null;
1292
- this._uiContainer = null;
1293
- this._isSuccess = false;
1294
- this.currentLanguage = 'en';
1295
- // UI Elements
1296
- this.modal = null;
1297
- this.identifierPanel = null;
1298
- this.verificationPanel = null;
1299
- this.loadingOverlay = null;
1300
- this.otpInstance = null;
1301
- // @ts-ignore - replaced at build time
1302
- this.baseURL = baseURL || "https://api.playkit.ai";
1303
- this.currentLanguage = this.detectLanguage();
1304
- }
1305
- /**
1306
- * Detect browser language (safe for Node.js environment)
1307
- */
1308
- detectLanguage() {
1309
- // Check if running in browser environment
1310
- if (typeof window === 'undefined' || typeof navigator === 'undefined') {
1311
- return 'en'; // Default to English in Node.js environment
1312
- }
1313
- try {
1314
- const browserLang = navigator.language.toLowerCase();
1315
- // Match language codes
1316
- if (browserLang.startsWith('zh-tw') || browserLang.startsWith('zh-hk')) {
1317
- return 'zh-TW'; // Traditional Chinese
1318
- }
1319
- else if (browserLang.startsWith('zh')) {
1320
- return 'zh'; // Simplified Chinese
1321
- }
1322
- else if (browserLang.startsWith('ja')) {
1323
- return 'ja'; // Japanese
1324
- }
1325
- else if (browserLang.startsWith('ko')) {
1326
- return 'ko'; // Korean
1327
- }
1328
- else {
1329
- return 'en'; // Default to English
1330
- }
1331
- }
1332
- catch (error) {
1333
- // Fallback to English if detection fails
1334
- return 'en';
1335
- }
1336
- }
1337
- /**
1338
- * Get translated text
1339
- */
1340
- t(key) {
1341
- return translations$2[this.currentLanguage][key];
1342
- }
1343
- /**
1344
- * Start the authentication flow
1345
- * Returns a promise that resolves with the JWT token
1346
- */
1347
- async startFlow() {
1348
- return new Promise((resolve, reject) => {
1349
- // Create and show UI
1350
- this.createUI();
1351
- this.showModal();
1352
- // Listen for success/failure
1353
- this.once('success', (token) => {
1354
- this.hideModal();
1355
- resolve(token);
1356
- });
1357
- this.once('error', (error) => {
1358
- this.hideModal();
1359
- reject(error);
1360
- });
1361
- // Set default auth type based on region
1362
- this.setDefaultAuthTypeByRegion().catch((err) => {
1363
- console.error('[PlayKit Auth] Failed to detect region:', err);
1364
- });
1365
- });
1366
- }
1367
- /**
1368
- * Create the authentication UI
1369
- */
1370
- createUI() {
1371
- // Create modal container
1372
- this.modal = document.createElement('div');
1373
- this.modal.className = 'playkit-auth-modal';
1374
- this.modal.innerHTML = `
1375
- <div class="playkit-auth-overlay"></div>
1376
- <div class="playkit-auth-container">
1377
- <!-- Identifier Panel -->
1378
- <div class="playkit-auth-panel" id="playkit-identifier-panel">
1379
- <div class="playkit-auth-header">
1380
- <h2>${this.t('signIn')}</h2>
1381
- <p>${this.t('signInSubtitle')}</p>
1382
- </div>
1383
-
1384
- <div class="playkit-auth-toggle">
1385
- <label class="playkit-toggle-option">
1386
- <input type="radio" name="auth-type" value="email" checked>
1387
- <span>${this.t('email')}</span>
1388
- </label>
1389
- <label class="playkit-toggle-option">
1390
- <input type="radio" name="auth-type" value="phone">
1391
- <span>${this.t('phone')}</span>
1392
- </label>
1393
- </div>
1394
-
1395
- <div class="playkit-auth-input-group">
1396
- <div class="playkit-input-wrapper">
1397
- <svg class="playkit-input-icon" id="playkit-identifier-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1398
- <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
1399
- <polyline points="22,6 12,13 2,6"></polyline>
1400
- </svg>
1401
- <input
1402
- type="text"
1403
- id="playkit-identifier-input"
1404
- placeholder="${this.t('emailPlaceholder')}"
1405
- autocomplete="off"
1406
- >
1407
- </div>
1408
- </div>
1409
-
1410
- <button class="playkit-auth-button" id="playkit-send-code-btn">
1411
- ${this.t('sendCode')}
1412
- </button>
1413
-
1414
- <div class="playkit-auth-error" id="playkit-error-text"></div>
1415
- </div>
1416
-
1417
- <!-- Verification Panel -->
1418
- <div class="playkit-auth-panel" id="playkit-verification-panel" style="display: none;">
1419
- <div class="playkit-auth-header">
1420
- <button class="playkit-back-button" id="playkit-back-btn">
1421
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1422
- <path d="M19 12H5M12 19l-7-7 7-7"/>
1423
- </svg>
1424
- </button>
1425
- <h2>${this.t('enterCode')}</h2>
1426
- <p>${this.t('enterCodeSubtitle')} <span id="playkit-identifier-display"></span></p>
1427
- </div>
1428
-
1429
- <div class="playkit-auth-input-group">
1430
- <div class="playkit-code-inputs">
1431
- <input type="number" maxlength="1" class="playkit-code-input" data-index="0">
1432
- <input type="number" maxlength="1" class="playkit-code-input" data-index="1">
1433
- <input type="number" maxlength="1" class="playkit-code-input" data-index="2">
1434
- <input type="number" maxlength="1" class="playkit-code-input" data-index="3">
1435
- <input type="number" maxlength="1" class="playkit-code-input" data-index="4">
1436
- <input type="number" maxlength="1" class="playkit-code-input" data-index="5">
1437
- </div>
1438
- </div>
1439
-
1440
- <button class="playkit-auth-button" id="playkit-verify-btn">
1441
- ${this.t('verify')}
1442
- </button>
1443
-
1444
- <div class="playkit-auth-error" id="playkit-verify-error-text"></div>
1445
- </div>
1446
-
1447
- <!-- Loading Overlay -->
1448
- <div class="playkit-loading-overlay" id="playkit-loading-overlay" style="display: none;">
1449
- <div class="playkit-spinner"></div>
1450
- </div>
1451
- </div>
1452
- `;
1453
- // Add styles and load VanillaOTP
1454
- this.addStyles();
1455
- this.loadVanillaOTP();
1456
- // Append to body
1457
- document.body.appendChild(this.modal);
1458
- // Get references
1459
- this.identifierPanel = document.getElementById('playkit-identifier-panel');
1460
- this.verificationPanel = document.getElementById('playkit-verification-panel');
1461
- this.loadingOverlay = document.getElementById('playkit-loading-overlay');
1462
- // Setup event listeners
1463
- this.setupEventListeners();
1464
- }
1465
- /**
1466
- * Load VanillaOTP library
1467
- */
1468
- loadVanillaOTP() {
1469
- // Check if VanillaOTP is already loaded
1470
- if (window.VanillaOTP)
1471
- return;
1472
- // Inject VanillaOTP script
1473
- const script = document.createElement('script');
1474
- script.textContent = `"use strict";var VanillaOTP=function(t,e=null){if(this.emptyChar=" ","string"==typeof t)this.container=document.querySelector(t);else{if(!(t instanceof Element))return;this.container=t}e&&("string"==typeof e?this.updateTo=document.querySelector(e)||null:e instanceof Element?this.updateTo=e:this.updateTo=null),this.inputs=Array.from(this.container.querySelectorAll("input[type=text], input[type=number], input[type=password]"));let n=this,u=n.inputs.length;for(let i=0;i<u;i++){let l=n.inputs[i];l.addEventListener("input",function(){if(isNaN(l.value))return l.value=l.dataset.otpInputRestore||"",n._updateValue();if(0==l.value.length)return n._saveInputValue(i);if(1==l.value.length){n._saveInputValue(i),n._updateValue(),i+1<u&&n.inputs[i+1].focus();return}if(i==u-1)return n._setInputValue(i,l.value);let t=l.value.split("");for(let e=0;e<t.length&&!(e+i>=u);e++)n._setInputValue(e+i,t[e]);let a=Math.min(u-1,i+t.length);n.inputs[a].focus()}),l.addEventListener("keydown",function(t){if(8==t.keyCode&&""==l.value&&0!=i){n._setInputValue(i-1,""),n.inputs[i-1].focus();return}if(46==t.keyCode&&i!=u-1){let e=l.selectionStart||0;for(let a=i+e;a<u-1;a++)n._setInputValue(a,n.inputs[a+1].value);n._setInputValue(u-1,""),l.selectionStart&&(l.selectionStart=e),t.preventDefault();return}if(37==t.keyCode&&(null==l.selectionStart||0==l.selectionStart)){i>0&&(t.preventDefault(),n.inputs[i-1].focus(),n.inputs[i-1].select());return}if(39==t.keyCode&&(null==l.selectionStart||l.selectionEnd==l.value.length)){i+1<u&&(t.preventDefault(),n.inputs[i+1].focus(),n.inputs[i+1].select());return}})}};VanillaOTP.prototype.setEmptyChar=function(t){this.emptyChar=t},VanillaOTP.prototype.getValue=function(){let t="",e=this;return this.inputs.forEach(function(n){t+=""==n.value?e.emptyChar:n.value}),t},VanillaOTP.prototype.setValue=function(t){if(isNaN(t)){console.error("Please enter an integer value.");return}let e=(t=""+t).split("");for(let n=0;n<this.inputs.length;n++)this._setInputValue(n,e[n]||"")},VanillaOTP.prototype._setInputValue=function(t,e){return isNaN(e)?console.error("Please enter an integer value."):this.inputs[t]?void(this.inputs[t].value=String(e).substring(0,1),this._saveInputValue(t),this._updateValue()):console.error("Index not found.")},VanillaOTP.prototype._saveInputValue=function(t,e){if(!this.inputs[t])return console.error("Index not found.");this.inputs[t].dataset.otpInputRestore=e||this.inputs[t].value},VanillaOTP.prototype._updateValue=function(){this.updateTo&&(this.updateTo.value=this.getValue())};`;
1475
- document.head.appendChild(script);
1476
- }
1477
- /**
1478
- * Add CSS styles to the page
1479
- */
1480
- addStyles() {
1481
- const styleId = 'playkit-auth-styles';
1482
- if (document.getElementById(styleId))
1483
- return;
1484
- const style = document.createElement('style');
1485
- style.id = styleId;
1486
- style.textContent = `
1487
- .playkit-auth-modal {
1488
- position: fixed;
1489
- top: 0;
1490
- left: 0;
1491
- right: 0;
1492
- bottom: 0;
1493
- z-index: 999999;
1494
- display: flex;
1495
- justify-content: center;
1496
- align-items: center;
1497
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
1498
- }
1499
-
1500
- .playkit-auth-overlay {
1501
- position: absolute;
1502
- top: 0;
1503
- left: 0;
1504
- right: 0;
1505
- bottom: 0;
1506
- background: rgba(0, 0, 0, 0.8);
1507
- }
1508
-
1509
- .playkit-auth-container {
1510
- position: relative;
1511
- background: #fff;
1512
- border: 1px solid rgba(0, 0, 0, 0.1);
1513
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05);
1514
- width: 90%;
1515
- max-width: 320px;
1516
- overflow: hidden;
1517
- }
1518
-
1519
- .playkit-auth-panel {
1520
- padding: 24px;
1521
- }
1522
-
1523
- .playkit-auth-header {
1524
- text-align: center;
1525
- margin-bottom: 20px;
1526
- position: relative;
1527
- }
1528
-
1529
- .playkit-auth-header h2 {
1530
- margin: 0 0 8px 0;
1531
- font-size: 14px;
1532
- font-weight: 600;
1533
- color: #171717;
1534
- }
1535
-
1536
- .playkit-auth-header p {
1537
- margin: 0;
1538
- font-size: 14px;
1539
- color: #666;
1540
- line-height: 1.5;
1541
- }
1542
-
1543
- .playkit-back-button {
1544
- position: absolute;
1545
- left: 0;
1546
- top: 0;
1547
- background: transparent;
1548
- border: none;
1549
- cursor: pointer;
1550
- padding: 4px;
1551
- color: #666;
1552
- transition: background-color 0.2s ease, color 0.2s ease;
1553
- }
1554
-
1555
- .playkit-back-button:hover {
1556
- background: #f5f5f5;
1557
- color: #171717;
1558
- }
1559
-
1560
- .playkit-auth-toggle {
1561
- display: flex;
1562
- background: #f5f5f5;
1563
- padding: 2px;
1564
- margin-bottom: 20px;
1565
- gap: 2px;
1566
- }
1567
-
1568
- .playkit-toggle-option {
1569
- flex: 1;
1570
- display: flex;
1571
- justify-content: center;
1572
- align-items: center;
1573
- padding: 10px 16px;
1574
- cursor: pointer;
1575
- transition: background-color 0.2s ease;
1576
- }
1577
-
1578
- .playkit-toggle-option input {
1579
- display: none;
1580
- }
1581
-
1582
- .playkit-toggle-option span {
1583
- font-size: 14px;
1584
- font-weight: 500;
1585
- color: #666;
1586
- transition: color 0.2s ease;
1587
- }
1588
-
1589
- .playkit-toggle-option input:checked + span {
1590
- color: #fff;
1591
- }
1592
-
1593
- .playkit-toggle-option:has(input:checked) {
1594
- background: #171717;
1595
- }
1596
-
1597
- .playkit-auth-input-group {
1598
- margin-bottom: 20px;
1599
- }
1600
-
1601
- .playkit-input-wrapper {
1602
- position: relative;
1603
- display: flex;
1604
- align-items: center;
1605
- }
1606
-
1607
- .playkit-input-icon {
1608
- position: absolute;
1609
- left: 12px;
1610
- color: #999;
1611
- pointer-events: none;
1612
- }
1613
-
1614
- .playkit-input-wrapper input {
1615
- width: 100%;
1616
- padding: 10px 12px 10px 44px;
1617
- border: 1px solid #e5e7eb;
1618
- font-size: 14px;
1619
- transition: border-color 0.2s ease;
1620
- box-sizing: border-box;
1621
- background: #fff;
1622
- }
1623
-
1624
- .playkit-input-wrapper input:hover {
1625
- border-color: #d4d4d4;
1626
- }
1627
-
1628
- .playkit-input-wrapper input:focus {
1629
- outline: none;
1630
- border-color: #171717;
1631
- }
1632
-
1633
- .playkit-code-inputs {
1634
- display: flex;
1635
- gap: 8px;
1636
- justify-content: center;
1637
- }
1638
-
1639
- .playkit-code-input {
1640
- width: 40px !important;
1641
- height: 48px;
1642
- text-align: center;
1643
- font-size: 20px;
1644
- font-weight: 600;
1645
- border: 1px solid #e5e7eb !important;
1646
- padding: 0 !important;
1647
- transition: border-color 0.2s ease;
1648
- background: #fff;
1649
- -moz-appearance: textfield;
1650
- }
1651
-
1652
- .playkit-code-input::-webkit-outer-spin-button,
1653
- .playkit-code-input::-webkit-inner-spin-button {
1654
- -webkit-appearance: none;
1655
- margin: 0;
1656
- }
1657
-
1658
- .playkit-code-input:hover {
1659
- border-color: #d4d4d4 !important;
1660
- }
1661
-
1662
- .playkit-code-input:focus {
1663
- outline: none;
1664
- border-color: #171717 !important;
1665
- }
1666
-
1667
- .playkit-auth-button {
1668
- width: 100%;
1669
- padding: 10px 16px;
1670
- background: #171717;
1671
- color: white;
1672
- border: none;
1673
- font-size: 14px;
1674
- font-weight: 500;
1675
- cursor: pointer;
1676
- transition: background 0.2s ease;
1677
- }
1678
-
1679
- .playkit-auth-button:hover:not(:disabled) {
1680
- background: #404040;
1681
- }
1682
-
1683
- .playkit-auth-button:active:not(:disabled) {
1684
- background: #0a0a0a;
1685
- }
1686
-
1687
- .playkit-auth-button:disabled {
1688
- background: #e5e7eb;
1689
- color: #999;
1690
- cursor: not-allowed;
1691
- }
1692
-
1693
- .playkit-auth-error {
1694
- margin-top: 16px;
1695
- padding: 12px 16px;
1696
- background: #fef2f2;
1697
- border: 1px solid #fecaca;
1698
- color: #dc2626;
1699
- font-size: 13px;
1700
- text-align: left;
1701
- display: none;
1702
- }
1703
-
1704
- .playkit-auth-error.show {
1705
- display: block;
1706
- }
1707
-
1708
- .playkit-loading-overlay {
1709
- position: absolute;
1710
- top: 0;
1711
- left: 0;
1712
- right: 0;
1713
- bottom: 0;
1714
- background: rgba(255, 255, 255, 0.96);
1715
- display: flex;
1716
- justify-content: center;
1717
- align-items: center;
1718
- }
1719
-
1720
- .playkit-spinner {
1721
- width: 24px;
1722
- height: 24px;
1723
- border: 2px solid #e5e7eb;
1724
- border-top: 2px solid #171717;
1725
- border-radius: 50%;
1726
- animation: playkit-spin 1s linear infinite;
1727
- }
1728
-
1729
- @keyframes playkit-spin {
1730
- 0% { transform: rotate(0deg); }
1731
- 100% { transform: rotate(360deg); }
1732
- }
1733
-
1734
- @media (max-width: 480px) {
1735
- .playkit-auth-container {
1736
- width: 95%;
1737
- max-width: none;
1738
- }
1739
-
1740
- .playkit-auth-panel {
1741
- padding: 20px;
1742
- }
1743
-
1744
- .playkit-code-input {
1745
- width: 36px !important;
1746
- height: 44px;
1747
- font-size: 18px;
1748
- }
1749
-
1750
- .playkit-code-inputs {
1751
- gap: 6px;
1752
- }
1753
- }
1754
- `;
1755
- document.head.appendChild(style);
1756
- }
1757
- /**
1758
- * Setup event listeners
1759
- */
1760
- setupEventListeners() {
1761
- var _a, _b, _c, _d;
1762
- // Auth type toggle
1763
- const emailRadio = (_a = this.modal) === null || _a === void 0 ? void 0 : _a.querySelector('input[value="email"]');
1764
- const phoneRadio = (_b = this.modal) === null || _b === void 0 ? void 0 : _b.querySelector('input[value="phone"]');
1765
- const identifierInput = document.getElementById('playkit-identifier-input');
1766
- const identifierIcon = document.getElementById('playkit-identifier-icon');
1767
- const updateIcon = () => {
1768
- const isEmail = emailRadio === null || emailRadio === void 0 ? void 0 : emailRadio.checked;
1769
- identifierInput.placeholder = isEmail
1770
- ? this.t('emailPlaceholder')
1771
- : this.t('phonePlaceholder');
1772
- // Update icon
1773
- if (isEmail) {
1774
- identifierIcon.innerHTML = `
1775
- <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
1776
- <polyline points="22,6 12,13 2,6"></polyline>
1777
- `;
1778
- }
1779
- else {
1780
- identifierIcon.innerHTML = `
1781
- <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>
1782
- `;
1783
- }
1784
- };
1785
- emailRadio === null || emailRadio === void 0 ? void 0 : emailRadio.addEventListener('change', updateIcon);
1786
- phoneRadio === null || phoneRadio === void 0 ? void 0 : phoneRadio.addEventListener('change', updateIcon);
1787
- // Send code button
1788
- const sendCodeBtn = document.getElementById('playkit-send-code-btn');
1789
- sendCodeBtn === null || sendCodeBtn === void 0 ? void 0 : sendCodeBtn.addEventListener('click', () => this.onSendCodeClicked());
1790
- // Enter key in identifier input
1791
- identifierInput === null || identifierInput === void 0 ? void 0 : identifierInput.addEventListener('keypress', (e) => {
1792
- if (e.key === 'Enter') {
1793
- this.onSendCodeClicked();
1794
- }
1795
- });
1796
- // Initialize VanillaOTP for code inputs
1797
- const codeInputsContainer = (_c = this.modal) === null || _c === void 0 ? void 0 : _c.querySelector('.playkit-code-inputs');
1798
- if (codeInputsContainer && window.VanillaOTP) {
1799
- this.otpInstance = new window.VanillaOTP(codeInputsContainer);
1800
- // Auto-submit when all 6 digits entered
1801
- const codeInputs = (_d = this.modal) === null || _d === void 0 ? void 0 : _d.querySelectorAll('.playkit-code-input');
1802
- codeInputs === null || codeInputs === void 0 ? void 0 : codeInputs.forEach((input, _index) => {
1803
- input.addEventListener('input', () => {
1804
- // Check if all inputs are filled
1805
- const allFilled = Array.from(codeInputs).every(inp => inp.value.length === 1);
1806
- if (allFilled) {
1807
- // Small delay to ensure the last input is processed
1808
- setTimeout(() => this.onVerifyClicked(), 100);
1809
- }
1810
- });
1811
- });
1812
- }
1813
- // Verify button
1814
- const verifyBtn = document.getElementById('playkit-verify-btn');
1815
- verifyBtn === null || verifyBtn === void 0 ? void 0 : verifyBtn.addEventListener('click', () => this.onVerifyClicked());
1816
- // Back button
1817
- const backBtn = document.getElementById('playkit-back-btn');
1818
- backBtn === null || backBtn === void 0 ? void 0 : backBtn.addEventListener('click', () => {
1819
- this.showIdentifierPanel();
1820
- });
1821
- }
1822
- /**
1823
- * Handle send code button click
1824
- */
1825
- async onSendCodeClicked() {
1826
- var _a;
1827
- this.clearError();
1828
- const identifierInput = document.getElementById('playkit-identifier-input');
1829
- const identifier = identifierInput.value.trim();
1830
- const emailRadio = (_a = this.modal) === null || _a === void 0 ? void 0 : _a.querySelector('input[value="email"]');
1831
- const type = emailRadio.checked ? 'email' : 'phone';
1832
- if (!identifier) {
1833
- this.showError(type === 'email' ? this.t('pleaseEnterEmail') : this.t('pleaseEnterPhone'));
1834
- return;
1835
- }
1836
- const sendCodeBtn = document.getElementById('playkit-send-code-btn');
1837
- sendCodeBtn.disabled = true;
1838
- this.showLoading();
1839
- try {
1840
- const success = await this.sendVerificationCode(identifier, type);
1841
- if (success) {
1842
- // Store identifier for display
1843
- const displaySpan = document.getElementById('playkit-identifier-display');
1844
- if (displaySpan) {
1845
- displaySpan.textContent = type === 'email' ? identifier : identifier;
1846
- }
1847
- // Switch to verification panel
1848
- this.showVerificationPanel();
1849
- }
1850
- }
1851
- catch (error) {
1852
- this.showError(error instanceof Error ? error.message : this.t('failedToSendCode'));
1853
- }
1854
- finally {
1855
- this.hideLoading();
1856
- sendCodeBtn.disabled = false;
1857
- }
1858
- }
1859
- /**
1860
- * Handle verify button click
1861
- */
1862
- async onVerifyClicked() {
1863
- var _a;
1864
- this.clearError('verify');
1865
- // Get code from VanillaOTP instance or fallback to manual collection
1866
- let code = '';
1867
- if (this.otpInstance) {
1868
- code = this.otpInstance.getValue().replace(/\s/g, ''); // Remove spaces
1869
- }
1870
- else {
1871
- const codeInputs = (_a = this.modal) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.playkit-code-input');
1872
- code = Array.from(codeInputs).map((input) => input.value).join('');
1873
- }
1874
- if (code.length !== 6 || !/^\d{6}$/.test(code)) {
1875
- this.showError(this.t('enterAllDigits'), 'verify');
1876
- return;
1877
- }
1878
- this.showLoading();
1879
- try {
1880
- const globalToken = await this.verifyCode(code);
1881
- this.emit('success', globalToken);
1882
- }
1883
- catch (error) {
1884
- this.showError(error instanceof Error ? error.message : this.t('verificationFailed'), 'verify');
1885
- this.hideLoading();
1886
- }
1887
- }
1888
- /**
1889
- * Send verification code to backend
1890
- */
1891
- async sendVerificationCode(identifier, type) {
1892
- const response = await fetch(`${this.baseURL}/api/auth/send-code`, {
1893
- method: 'POST',
1894
- headers: Object.assign({ 'Content-Type': 'application/json' }, getSDKHeaders()),
1895
- body: JSON.stringify({ identifier, type }),
1896
- });
1897
- if (!response.ok) {
1898
- throw new PlayKitError(this.t('failedToSendCode'), 'SEND_CODE_ERROR', response.status);
1899
- }
1900
- const data = await response.json();
1901
- if (!data.success || !data.sessionId) {
1902
- throw new PlayKitError(this.t('failedToSendCode'), 'INVALID_RESPONSE');
1903
- }
1904
- this.currentSessionId = data.sessionId;
1905
- return true;
1906
- }
1907
- /**
1908
- * Verify the code and get global token
1909
- */
1910
- async verifyCode(code) {
1911
- if (!this.currentSessionId) {
1912
- throw new PlayKitError('No session ID available', 'NO_SESSION');
1913
- }
1914
- const response = await fetch(`${this.baseURL}/api/auth/verify-code`, {
1915
- method: 'POST',
1916
- headers: Object.assign({ 'Content-Type': 'application/json' }, getSDKHeaders()),
1917
- body: JSON.stringify({
1918
- sessionId: this.currentSessionId,
1919
- code,
1920
- }),
1921
- });
1922
- if (!response.ok) {
1923
- throw new PlayKitError(this.t('invalidCode'), 'INVALID_CODE', response.status);
1924
- }
1925
- const data = await response.json();
1926
- if (!data.success || !data.globalToken) {
1927
- throw new PlayKitError(this.t('verificationFailed'), 'VERIFICATION_FAILED');
1928
- }
1929
- return data.globalToken;
1930
- }
1931
- /**
1932
- * Set default auth type based on user region
1933
- */
1934
- async setDefaultAuthTypeByRegion() {
1935
- var _a;
1936
- try {
1937
- const response = await fetch(`${this.baseURL}/api/reachability`, {
1938
- headers: Object.assign({}, getSDKHeaders()),
1939
- });
1940
- if (response.ok) {
1941
- const data = await response.json();
1942
- if (data.region === 'CN') {
1943
- const phoneRadio = (_a = this.modal) === null || _a === void 0 ? void 0 : _a.querySelector('input[value="phone"]');
1944
- if (phoneRadio) {
1945
- phoneRadio.checked = true;
1946
- phoneRadio.dispatchEvent(new Event('change'));
1947
- }
1948
- }
1949
- }
1950
- }
1951
- catch (error) {
1952
- console.error('[PlayKit Auth] Failed to detect region:', error);
1953
- }
1954
- }
1955
- /**
1956
- * Show/hide panels
1957
- */
1958
- showIdentifierPanel() {
1959
- var _a;
1960
- if (this.identifierPanel)
1961
- this.identifierPanel.style.display = 'block';
1962
- if (this.verificationPanel)
1963
- this.verificationPanel.style.display = 'none';
1964
- // Clear code inputs
1965
- if (this.otpInstance) {
1966
- this.otpInstance.setValue(''); // Clear all inputs using VanillaOTP
1967
- }
1968
- else {
1969
- const codeInputs = (_a = this.modal) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.playkit-code-input');
1970
- codeInputs === null || codeInputs === void 0 ? void 0 : codeInputs.forEach((input) => (input.value = ''));
1971
- }
1972
- }
1973
- showVerificationPanel() {
1974
- var _a;
1975
- if (this.identifierPanel)
1976
- this.identifierPanel.style.display = 'none';
1977
- if (this.verificationPanel)
1978
- this.verificationPanel.style.display = 'block';
1979
- // Focus first code input
1980
- const firstInput = (_a = this.modal) === null || _a === void 0 ? void 0 : _a.querySelector('.playkit-code-input');
1981
- firstInput === null || firstInput === void 0 ? void 0 : firstInput.focus();
1982
- }
1983
- /**
1984
- * Show/hide loading
1985
- */
1986
- showLoading() {
1987
- if (this.loadingOverlay)
1988
- this.loadingOverlay.style.display = 'flex';
1989
- }
1990
- hideLoading() {
1991
- if (this.loadingOverlay)
1992
- this.loadingOverlay.style.display = 'none';
1993
- }
1994
- /**
1995
- * Show/hide error messages
1996
- */
1997
- showError(message, panel = 'identifier') {
1998
- const errorEl = panel === 'identifier'
1999
- ? document.getElementById('playkit-error-text')
2000
- : document.getElementById('playkit-verify-error-text');
2001
- if (errorEl) {
2002
- errorEl.textContent = message;
2003
- errorEl.classList.add('show');
2004
- }
2005
- }
2006
- clearError(panel = 'both') {
2007
- if (panel === 'identifier' || panel === 'both') {
2008
- const errorEl = document.getElementById('playkit-error-text');
2009
- if (errorEl) {
2010
- errorEl.textContent = '';
2011
- errorEl.classList.remove('show');
2012
- }
2013
- }
2014
- if (panel === 'verify' || panel === 'both') {
2015
- const errorEl = document.getElementById('playkit-verify-error-text');
2016
- if (errorEl) {
2017
- errorEl.textContent = '';
2018
- errorEl.classList.remove('show');
2019
- }
2020
- }
2021
- }
2022
- /**
2023
- * Show/hide modal
2024
- */
2025
- showModal() {
2026
- if (this.modal)
2027
- this.modal.style.display = 'flex';
2028
- }
2029
- hideModal() {
2030
- if (this.modal) {
2031
- this.modal.style.display = 'none';
2032
- // Remove from DOM after animation
2033
- setTimeout(() => {
2034
- var _a;
2035
- (_a = this.modal) === null || _a === void 0 ? void 0 : _a.remove();
2036
- }, 300);
2037
- }
2038
- }
2039
- /**
2040
- * Clean up
2041
- */
2042
- destroy() {
2043
- var _a;
2044
- (_a = this.modal) === null || _a === void 0 ? void 0 : _a.remove();
2045
- this.removeAllListeners();
2046
- }
2047
- }
2048
-
2049
1230
  /**
2050
1231
  * Device Authorization Flow Manager
2051
1232
  * Manages Device Auth polling flow for desktop/CLI/Unity applications
@@ -2419,14 +1600,12 @@
2419
1600
  // Store the flow promise so subsequent calls can await the same result
2420
1601
  const flowPromise = this.executeFlow(options);
2421
1602
  DeviceAuthFlowManager.currentFlowPromise = flowPromise;
2422
- DeviceAuthFlowManager.activeInstance = this;
2423
1603
  try {
2424
1604
  return await flowPromise;
2425
1605
  }
2426
1606
  finally {
2427
1607
  // Clean up static state when flow completes (success or failure)
2428
1608
  DeviceAuthFlowManager.currentFlowPromise = null;
2429
- DeviceAuthFlowManager.activeInstance = null;
2430
1609
  }
2431
1610
  }
2432
1611
  /**
@@ -2765,8 +1944,6 @@
2765
1944
  }
2766
1945
  /** Shared promise for the current flow - allows multiple callers to await the same result */
2767
1946
  DeviceAuthFlowManager.currentFlowPromise = null;
2768
- /** Reference to the currently active instance */
2769
- DeviceAuthFlowManager.activeInstance = null;
2770
1947
 
2771
1948
  /**
2772
1949
  * Authentication manager
@@ -2774,12 +1951,10 @@
2774
1951
  */
2775
1952
  // @ts-ignore - replaced at build time
2776
1953
  const DEFAULT_BASE_URL$6 = "https://api.playkit.ai";
2777
- const JWT_EXCHANGE_ENDPOINT = '/api/external/exchange-jwt';
2778
1954
  const TOKEN_REFRESH_ENDPOINT = '/api/auth/refresh';
2779
1955
  class AuthManager extends EventEmitter {
2780
1956
  constructor(config) {
2781
1957
  super();
2782
- this.authFlowManager = null;
2783
1958
  this.deviceAuthFlowManager = null;
2784
1959
  this.logger = Logger.getLogger('AuthManager');
2785
1960
  /** Shared promise for current device auth flow - allows multiple callers to await the same result */
@@ -2847,11 +2022,6 @@
2847
2022
  }
2848
2023
  }
2849
2024
  }
2850
- // Check if player JWT was provided
2851
- if (this.config.playerJWT) {
2852
- await this.exchangeJWT(this.config.playerJWT);
2853
- return;
2854
- }
2855
2025
  // Check for platform-injected token (same-domain scenario)
2856
2026
  // This allows seamless auth when SDK runs in a context where the platform
2857
2027
  // (e.g., Agentland-Space) has already stored a token in localStorage
@@ -2872,35 +2042,30 @@
2872
2042
  this.emit('unauthenticated');
2873
2043
  // In server mode, don't try to show UI - just throw error
2874
2044
  if (this.config.mode === 'server') {
2875
- throw new PlayKitError('No authentication token provided. In server mode, please provide developerToken, playerToken, or playerJWT.', 'NOT_AUTHENTICATED');
2045
+ throw new PlayKitError('No authentication token provided. In server mode, please provide developerToken or playerToken.', 'NOT_AUTHENTICATED');
2876
2046
  }
2877
2047
  // Auto-start login flow in browser environment
2878
2048
  if (typeof window !== 'undefined') {
2879
- // Default to device auth if not specified
2880
- const authMethod = this.config.authMethod || 'device';
2881
- await this.startAuthFlow(authMethod);
2049
+ await this.startAuthFlow();
2882
2050
  // If we reach here, authentication was successful
2883
2051
  // If it failed, startAuthFlow() will have thrown an error
2884
2052
  }
2885
2053
  else {
2886
2054
  // Node.js environment - cannot show UI, must provide token manually
2887
- throw new PlayKitError('No authentication token provided. Please provide developerToken, playerToken, playerJWT, or call login() manually.', 'NOT_AUTHENTICATED');
2055
+ throw new PlayKitError('No authentication token provided. Please provide developerToken or playerToken.', 'NOT_AUTHENTICATED');
2888
2056
  }
2889
2057
  }
2890
2058
  /**
2891
- * Start the authentication flow UI
2892
- *
2893
- * @param authMethod - Authentication method to use ('device' or 'headless')
2894
- * @deprecated 'headless' authentication is deprecated and will be removed in v2.0. Use 'device' instead.
2059
+ * Start the authentication flow (Device Authorization + PKCE).
2895
2060
  */
2896
- async startAuthFlow(authMethod = 'device') {
2061
+ async startAuthFlow() {
2897
2062
  // If a flow is already in progress, return the shared promise so all callers await the same result
2898
2063
  if (this.currentAuthFlowPromise) {
2899
2064
  this.logger.debug('Auth flow already in progress, waiting for existing flow');
2900
2065
  return this.currentAuthFlowPromise;
2901
2066
  }
2902
2067
  // Store the flow promise so subsequent calls can await the same result
2903
- const flowPromise = this.executeAuthFlow(authMethod);
2068
+ const flowPromise = this.executeAuthFlow();
2904
2069
  this.currentAuthFlowPromise = flowPromise;
2905
2070
  try {
2906
2071
  return await flowPromise;
@@ -2910,96 +2075,38 @@
2910
2075
  }
2911
2076
  }
2912
2077
  /**
2913
- * Internal method that executes the actual auth flow
2078
+ * Internal method that executes the Device Authorization flow.
2914
2079
  * @private
2915
2080
  */
2916
- async executeAuthFlow(authMethod = 'device') {
2917
- var _a, _b;
2918
- // Deprecation warning for headless auth
2919
- if (authMethod === 'headless') {
2920
- this.logger.warn('"headless" authentication is deprecated and will be removed in v2.0. ' +
2921
- 'Please migrate to "device" authentication.');
2922
- }
2923
- try {
2924
- if (authMethod === 'device') {
2925
- // Use Device Authorization flow (recommended)
2926
- this.deviceAuthFlowManager = new DeviceAuthFlowManager(this.baseURL, this.config.gameId);
2927
- const result = await this.deviceAuthFlowManager.startFlow({
2928
- scope: 'player:play',
2929
- });
2930
- // Update auth state with the player token and refresh token
2931
- this.authState = {
2932
- isAuthenticated: true,
2933
- token: result.access_token,
2934
- tokenType: 'player',
2935
- expiresAt: Date.now() + result.expires_in * 1000,
2936
- refreshToken: result.refresh_token,
2937
- refreshExpiresAt: Date.now() + result.refresh_expires_in * 1000,
2938
- };
2939
- // Save to storage
2940
- await this.storage.saveAuthState(this.config.gameId, this.authState);
2941
- this.emit('authenticated', this.authState);
2942
- // Clean up
2943
- this.deviceAuthFlowManager.destroy();
2944
- this.deviceAuthFlowManager = null;
2945
- }
2946
- else {
2947
- // Use headless verification code flow
2948
- this.authFlowManager = new AuthFlowManager(this.baseURL);
2949
- // Get global token from auth flow
2950
- const globalToken = await this.authFlowManager.startFlow();
2951
- // Exchange for player token
2952
- await this.exchangeJWT(globalToken);
2953
- // Clean up
2954
- this.authFlowManager.destroy();
2955
- this.authFlowManager = null;
2956
- }
2957
- }
2958
- catch (error) {
2959
- // User canceled or error occurred
2960
- (_a = this.authFlowManager) === null || _a === void 0 ? void 0 : _a.destroy();
2961
- this.authFlowManager = null;
2962
- (_b = this.deviceAuthFlowManager) === null || _b === void 0 ? void 0 : _b.destroy();
2963
- this.deviceAuthFlowManager = null;
2964
- // Re-emit error
2965
- this.emit('error', error);
2966
- throw error;
2967
- }
2968
- }
2969
- /**
2970
- * Exchange JWT for player token
2971
- */
2972
- async exchangeJWT(jwt) {
2081
+ async executeAuthFlow() {
2082
+ var _a;
2973
2083
  try {
2974
- const response = await fetch(`${this.baseURL}${JWT_EXCHANGE_ENDPOINT}`, {
2975
- method: 'POST',
2976
- headers: Object.assign({ Authorization: `Bearer ${jwt}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
2977
- body: JSON.stringify({ gameId: this.config.gameId }),
2084
+ // Device Authorization flow with PKCE — the only login flow.
2085
+ this.deviceAuthFlowManager = new DeviceAuthFlowManager(this.baseURL, this.config.gameId);
2086
+ const result = await this.deviceAuthFlowManager.startFlow({
2087
+ scope: 'player:play',
2978
2088
  });
2979
- if (!response.ok) {
2980
- const error = await response.json().catch(() => ({ message: 'JWT exchange failed' }));
2981
- throw new PlayKitError(error.message || 'JWT exchange failed', error.code, response.status);
2982
- }
2983
- const data = await response.json();
2984
- const playerToken = data.playerToken || data.token;
2985
- if (!playerToken) {
2986
- throw new PlayKitError('No player token received from server');
2987
- }
2988
- // Calculate expiration (assume 24 hours if not provided)
2989
- const expiresIn = data.expiresIn || 86400;
2990
- const expiresAt = Date.now() + expiresIn * 1000;
2089
+ // Update auth state with the player token and refresh token
2991
2090
  this.authState = {
2992
2091
  isAuthenticated: true,
2993
- token: playerToken,
2092
+ token: result.access_token,
2994
2093
  tokenType: 'player',
2995
- expiresAt,
2094
+ expiresAt: Date.now() + result.expires_in * 1000,
2095
+ refreshToken: result.refresh_token,
2096
+ refreshExpiresAt: Date.now() + result.refresh_expires_in * 1000,
2996
2097
  };
2997
2098
  // Save to storage
2998
2099
  await this.storage.saveAuthState(this.config.gameId, this.authState);
2999
2100
  this.emit('authenticated', this.authState);
3000
- return playerToken;
2101
+ // Clean up
2102
+ this.deviceAuthFlowManager.destroy();
2103
+ this.deviceAuthFlowManager = null;
3001
2104
  }
3002
2105
  catch (error) {
2106
+ // User canceled or error occurred
2107
+ (_a = this.deviceAuthFlowManager) === null || _a === void 0 ? void 0 : _a.destroy();
2108
+ this.deviceAuthFlowManager = null;
2109
+ // Re-emit error
3003
2110
  this.emit('error', error);
3004
2111
  throw error;
3005
2112
  }
@@ -3249,7 +2356,7 @@
3249
2356
  * Check if an authentication flow is currently in progress
3250
2357
  */
3251
2358
  isAuthFlowInProgress() {
3252
- return !!(this.authFlowManager || this.deviceAuthFlowManager);
2359
+ return !!this.deviceAuthFlowManager;
3253
2360
  }
3254
2361
  /**
3255
2362
  * Poll for authorization token after initiateDeviceAuth().
@@ -4170,6 +3277,11 @@
4170
3277
  'file',
4171
3278
  'audio',
4172
3279
  'input_audio',
3280
+ 'reasoning',
3281
+ 'tool-call',
3282
+ 'tool-result',
3283
+ 'tool-approval-request',
3284
+ 'tool-approval-response',
4173
3285
  ]);
4174
3286
  function describePart(part) {
4175
3287
  if (part === null)
@@ -4212,13 +3324,13 @@
4212
3324
  if (!hasType) {
4213
3325
  if ('role' in part && 'content' in part) {
4214
3326
  throw new PlayKitError(`messages[${i}].content[${j}] is shaped like a Message (has role/content) ` +
4215
- `but content parts must be {type:'text'|'image'|'image_url'|'file'|'audio'|'input_audio',...}. ` +
3327
+ `but content parts must include a recognized 'type' field. ` +
4216
3328
  `Did you mean to pass that array as messages directly? ` +
4217
3329
  `e.g. \`messages: theArray\` instead of \`messages: [{role:'user', content: theArray}]\`. ` +
4218
3330
  `Got part ${describePart(part)}`, 'INVALID_MESSAGES');
4219
3331
  }
4220
3332
  throw new PlayKitError(`messages[${i}].content[${j}] is missing a recognized 'type' field ` +
4221
- `(expected one of text|image|image_url|file|audio|input_audio). Got part ${describePart(part)}`, 'INVALID_MESSAGES');
3333
+ `(expected one of ${Array.from(VALID_PART_TYPES).join('|')}). Got part ${describePart(part)}`, 'INVALID_MESSAGES');
4222
3334
  }
4223
3335
  }
4224
3336
  }
@@ -4253,6 +3365,38 @@
4253
3365
  setPlayerClient(playerClient) {
4254
3366
  this.playerClient = playerClient;
4255
3367
  }
3368
+ /**
3369
+ * Resolve the `thinking` payload to send on the wire.
3370
+ *
3371
+ * Resolution order for effort: per-request `thinking.effort` > SDK-level
3372
+ * `defaultThinkingEffort` > omit (the server then defaults to off). This
3373
+ * mirrors how the chat model resolves (`chatConfig.model || defaultChatModel`).
3374
+ *
3375
+ * The deprecated `enabled` flag is preserved as an alias: when no effort can be
3376
+ * resolved, `enabled: false` maps to `{ effort: 'off' }` and `enabled: true`
3377
+ * maps to `{ effort: 'minimal' }`.
3378
+ *
3379
+ * @returns The `{ effort }` (and/or `enabled`) object to assign to
3380
+ * `requestBody.thinking`, or `undefined` to send nothing.
3381
+ */
3382
+ resolveThinking(chatConfig) {
3383
+ const thinking = chatConfig.thinking;
3384
+ // Precedence: per-request `effort` > per-request `enabled` alias > SDK-level
3385
+ // `defaultThinkingEffort` > omit (server then defaults to off). The `enabled`
3386
+ // alias MUST be checked BEFORE the SDK default, so an explicit per-request
3387
+ // `enabled: false` is never overridden by a configured default effort.
3388
+ if (thinking === null || thinking === void 0 ? void 0 : thinking.effort) {
3389
+ return { effort: thinking.effort };
3390
+ }
3391
+ if ((thinking === null || thinking === void 0 ? void 0 : thinking.enabled) !== undefined) {
3392
+ return { effort: thinking.enabled ? 'minimal' : 'off' };
3393
+ }
3394
+ if (this.config.defaultThinkingEffort) {
3395
+ return { effort: this.config.defaultThinkingEffort };
3396
+ }
3397
+ // Nothing to send — server defaults to off.
3398
+ return undefined;
3399
+ }
4256
3400
  /**
4257
3401
  * Make a chat completion request (non-streaming)
4258
3402
  */
@@ -4277,8 +3421,9 @@
4277
3421
  stop: chatConfig.stop || null,
4278
3422
  top_p: chatConfig.topP || null,
4279
3423
  };
4280
- if (chatConfig.thinking) {
4281
- requestBody.thinking = chatConfig.thinking;
3424
+ const thinking = this.resolveThinking(chatConfig);
3425
+ if (thinking) {
3426
+ requestBody.thinking = thinking;
4282
3427
  }
4283
3428
  try {
4284
3429
  const response = await fetch(`${this.baseURL}${endpoint}`, {
@@ -4337,8 +3482,9 @@
4337
3482
  stop: chatConfig.stop || null,
4338
3483
  top_p: chatConfig.topP || null,
4339
3484
  };
4340
- if (chatConfig.thinking) {
4341
- requestBody.thinking = chatConfig.thinking;
3485
+ const thinking = this.resolveThinking(chatConfig);
3486
+ if (thinking) {
3487
+ requestBody.thinking = thinking;
4342
3488
  }
4343
3489
  try {
4344
3490
  const response = await fetch(`${this.baseURL}${endpoint}`, {
@@ -4404,8 +3550,9 @@
4404
3550
  if (chatConfig.tool_choice) {
4405
3551
  requestBody.tool_choice = chatConfig.tool_choice;
4406
3552
  }
4407
- if (chatConfig.thinking) {
4408
- requestBody.thinking = chatConfig.thinking;
3553
+ const thinking = this.resolveThinking(chatConfig);
3554
+ if (thinking) {
3555
+ requestBody.thinking = thinking;
4409
3556
  }
4410
3557
  try {
4411
3558
  const response = await fetch(`${this.baseURL}${endpoint}`, {
@@ -4465,8 +3612,9 @@
4465
3612
  if (chatConfig.tool_choice) {
4466
3613
  requestBody.tool_choice = chatConfig.tool_choice;
4467
3614
  }
4468
- if (chatConfig.thinking) {
4469
- requestBody.thinking = chatConfig.thinking;
3615
+ const thinking = this.resolveThinking(chatConfig);
3616
+ if (thinking) {
3617
+ requestBody.thinking = thinking;
4470
3618
  }
4471
3619
  try {
4472
3620
  const response = await fetch(`${this.baseURL}${endpoint}`, {
@@ -4851,6 +3999,25 @@
4851
3999
  }
4852
4000
  return response;
4853
4001
  }
4002
+ /** GET a TTS endpoint; throws a PlayKitError on a non-ok response. */
4003
+ async get(endpoint) {
4004
+ await this.authManager.ensureValidToken();
4005
+ const token = this.authManager.getToken();
4006
+ if (!token) {
4007
+ throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
4008
+ }
4009
+ const response = await fetch(`${this.baseURL}${endpoint}`, {
4010
+ method: 'GET',
4011
+ headers: Object.assign({ Authorization: `Bearer ${token}` }, getSDKHeaders()),
4012
+ });
4013
+ if (!response.ok) {
4014
+ const error = await response
4015
+ .json()
4016
+ .catch(() => ({ message: 'Request failed' }));
4017
+ throw new PlayKitError(error.message || 'Request failed', error.code, response.status);
4018
+ }
4019
+ return response;
4020
+ }
4854
4021
  checkBalanceAfter() {
4855
4022
  if (this.playerClient) {
4856
4023
  this.playerClient.checkBalanceAfterApiCall().catch(() => {
@@ -4935,6 +4102,39 @@
4935
4102
  throw new PlayKitError(error instanceof Error ? error.message : 'Unknown error', 'TTS_ERROR');
4936
4103
  }
4937
4104
  }
4105
+ /**
4106
+ * List the voices available for speech synthesis.
4107
+ */
4108
+ async listVoices() {
4109
+ var _a;
4110
+ const endpoint = `/ai/${this.config.gameId}/v2/audio/voices`;
4111
+ try {
4112
+ const response = await this.get(endpoint);
4113
+ const json = (await response.json());
4114
+ const voices = ((_a = json.voices) !== null && _a !== void 0 ? _a : []).map((v) => {
4115
+ const voice = {
4116
+ voiceId: v.voice_id,
4117
+ kind: v.kind === 'custom' ? 'custom' : 'system',
4118
+ };
4119
+ if (v.name !== undefined)
4120
+ voice.name = v.name;
4121
+ if (v.description !== undefined)
4122
+ voice.description = v.description;
4123
+ if (v.language !== undefined)
4124
+ voice.language = v.language;
4125
+ return voice;
4126
+ });
4127
+ return {
4128
+ voices,
4129
+ total: Number(json.total) || voices.length,
4130
+ };
4131
+ }
4132
+ catch (error) {
4133
+ if (error instanceof PlayKitError)
4134
+ throw error;
4135
+ throw new PlayKitError(error instanceof Error ? error.message : 'Unknown error', 'TTS_ERROR');
4136
+ }
4137
+ }
4938
4138
  }
4939
4139
 
4940
4140
  /******************************************************************************
@@ -5492,14 +4692,14 @@
5492
4692
  * Generate a single image
5493
4693
  */
5494
4694
  async generateImage(config) {
5495
- var _a;
4695
+ var _a, _b;
5496
4696
  const imageConfig = Object.assign(Object.assign({}, config), { model: config.model || this.model, n: 1 });
5497
4697
  const response = await this.provider.generateImages(imageConfig);
5498
4698
  const imageData = response.data[0];
5499
4699
  if (!imageData || !imageData.b64_json) {
5500
4700
  throw new Error('No image data in response');
5501
4701
  }
5502
- return new GeneratedImageImpl(imageData.b64_json, config.prompt, (_a = imageData.revised_prompt) !== null && _a !== void 0 ? _a : config.prompt, config.size, imageData.b64_json_original, imageData.transparent_success);
4702
+ return new GeneratedImageImpl(imageData.b64_json, (_a = config.prompt) !== null && _a !== void 0 ? _a : '', (_b = imageData.revised_prompt) !== null && _b !== void 0 ? _b : config.prompt, config.size, imageData.b64_json_original, imageData.transparent_success);
5503
4703
  }
5504
4704
  /**
5505
4705
  * Generate multiple images
@@ -5508,11 +4708,11 @@
5508
4708
  const imageConfig = Object.assign(Object.assign({}, config), { model: config.model || this.model, n: config.n || 1 });
5509
4709
  const response = await this.provider.generateImages(imageConfig);
5510
4710
  return response.data.map((imageData) => {
5511
- var _a;
4711
+ var _a, _b;
5512
4712
  if (!imageData.b64_json) {
5513
4713
  throw new Error('No image data in response');
5514
4714
  }
5515
- return new GeneratedImageImpl(imageData.b64_json, config.prompt, (_a = imageData.revised_prompt) !== null && _a !== void 0 ? _a : config.prompt, config.size, imageData.b64_json_original, imageData.transparent_success);
4715
+ return new GeneratedImageImpl(imageData.b64_json, (_a = config.prompt) !== null && _a !== void 0 ? _a : '', (_b = imageData.revised_prompt) !== null && _b !== void 0 ? _b : config.prompt, config.size, imageData.b64_json_original, imageData.transparent_success);
5516
4716
  });
5517
4717
  }
5518
4718
  /**
@@ -5687,6 +4887,13 @@
5687
4887
  async synthesizeWithTimestamps(config) {
5688
4888
  return this.provider.synthesizeWithTimestamps(Object.assign(Object.assign({}, config), { model: config.model || this.model }));
5689
4889
  }
4890
+ /**
4891
+ * List the voices available for speech synthesis
4892
+ * @returns The available voices and a total count
4893
+ */
4894
+ async listVoices() {
4895
+ return this.provider.listVoices();
4896
+ }
5690
4897
  /**
5691
4898
  * Synthesize text into speech and return it as a Blob (browser-friendly)
5692
4899
  * @param config - Full TTS configuration
@@ -6560,12 +5767,8 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
6560
5767
  }));
6561
5768
  response.hasActions = response.actionCalls.length > 0;
6562
5769
  }
6563
- // Add assistant response to history
6564
- const assistantMessage = {
6565
- role: 'assistant',
6566
- content: response.text,
6567
- tool_calls: result.tool_calls,
6568
- };
5770
+ // Add assistant response to history in canonical PlayKit format.
5771
+ const assistantMessage = this.createAssistantHistoryMessage(response.text, result.tool_calls);
6569
5772
  this.history.push(assistantMessage);
6570
5773
  this.trimHistory();
6571
5774
  this.emit('response', response.text);
@@ -6623,12 +5826,8 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
6623
5826
  }));
6624
5827
  response.hasActions = response.actionCalls.length > 0;
6625
5828
  }
6626
- // Add assistant response to history
6627
- const assistantMessage = {
6628
- role: 'assistant',
6629
- content: response.text,
6630
- tool_calls: result.tool_calls,
6631
- };
5829
+ // Add assistant response to history in canonical PlayKit format.
5830
+ const assistantMessage = this.createAssistantHistoryMessage(response.text, result.tool_calls);
6632
5831
  this.history.push(assistantMessage);
6633
5832
  this.trimHistory();
6634
5833
  this.emit('response', response.text);
@@ -6655,22 +5854,64 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
6655
5854
  */
6656
5855
  reportActionResults(results) {
6657
5856
  for (const [callId, result] of Object.entries(results)) {
6658
- this.history.push({
6659
- role: 'tool',
6660
- tool_call_id: callId,
6661
- content: result,
6662
- });
5857
+ this.history.push(this.createToolResultHistoryMessage(callId, result));
6663
5858
  }
6664
5859
  }
6665
5860
  /**
6666
5861
  * Report a single action result
6667
5862
  */
6668
5863
  reportActionResult(callId, result) {
6669
- this.history.push({
5864
+ this.history.push(this.createToolResultHistoryMessage(callId, result));
5865
+ }
5866
+ createAssistantHistoryMessage(text, toolCalls) {
5867
+ if (!toolCalls || toolCalls.length === 0) {
5868
+ return { role: 'assistant', content: text };
5869
+ }
5870
+ const content = [];
5871
+ if (text) {
5872
+ content.push({ type: 'text', text });
5873
+ }
5874
+ content.push(...toolCalls.map(createToolCallContentPart));
5875
+ return {
5876
+ role: 'assistant',
5877
+ content,
5878
+ };
5879
+ }
5880
+ createToolResultHistoryMessage(callId, result) {
5881
+ const toolName = this.findToolNameForCall(callId);
5882
+ if (!toolName) {
5883
+ // Saved histories from older SDKs may not include enough context to infer
5884
+ // the tool name. Keep the legacy shape; the server normalizer can still
5885
+ // infer it when the matching assistant call is present.
5886
+ return {
5887
+ role: 'tool',
5888
+ tool_call_id: callId,
5889
+ content: result,
5890
+ };
5891
+ }
5892
+ return {
6670
5893
  role: 'tool',
6671
5894
  tool_call_id: callId,
6672
- content: result,
6673
- });
5895
+ content: [createToolResultContentPart(callId, toolName, result)],
5896
+ };
5897
+ }
5898
+ findToolNameForCall(callId) {
5899
+ var _a;
5900
+ for (let i = this.history.length - 1; i >= 0; i--) {
5901
+ const message = this.history[i];
5902
+ if (Array.isArray(message.content)) {
5903
+ for (const part of message.content) {
5904
+ if (part.type === 'tool-call' && part.toolCallId === callId) {
5905
+ return part.toolName;
5906
+ }
5907
+ }
5908
+ }
5909
+ const legacyCall = (_a = message.tool_calls) === null || _a === void 0 ? void 0 : _a.find(tc => tc.id === callId);
5910
+ if (legacyCall) {
5911
+ return legacyCall.function.name;
5912
+ }
5913
+ }
5914
+ return undefined;
6674
5915
  }
6675
5916
  /**
6676
5917
  * Parse tool arguments from JSON string
@@ -7110,8 +6351,7 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
7110
6351
  // Auto-restart login flow in browser environment
7111
6352
  if (typeof window !== 'undefined') {
7112
6353
  this.logger.debug('Restarting authentication flow...');
7113
- const authMethod = this.config.authMethod || 'device';
7114
- await this.authManager.startAuthFlow(authMethod);
6354
+ await this.authManager.startAuthFlow();
7115
6355
  // Retry getting player info after re-authentication
7116
6356
  await this.playerClient.getPlayerInfo();
7117
6357
  this.logger.debug('Re-authentication successful, token validated');
@@ -7154,12 +6394,11 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
7154
6394
  throw new PlayKitError('DeveloperToken validation failed: ' + (error instanceof Error ? error.message : String(error)), 'DEVELOPER_TOKEN_INVALID');
7155
6395
  }
7156
6396
  // Emit fallback started event
7157
- const fallbackMethod = this.config.authMethod || 'device';
7158
- this.emit('developer_token_fallback_started', { fallbackMethod });
7159
- this.logger.debug('Starting fallback to player login', { fallbackMethod });
6397
+ this.emit('developer_token_fallback_started', { fallbackMethod: 'device' });
6398
+ this.logger.debug('Starting fallback to player login');
7160
6399
  try {
7161
6400
  // Start player login flow
7162
- await this.authManager.startAuthFlow(fallbackMethod);
6401
+ await this.authManager.startAuthFlow();
7163
6402
  // Verify the new token
7164
6403
  await this.playerClient.getPlayerInfo();
7165
6404
  // Emit fallback completed event
@@ -7222,24 +6461,6 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
7222
6461
  isAuthenticated() {
7223
6462
  return this.authManager.isAuthenticated();
7224
6463
  }
7225
- /**
7226
- * Exchange JWT for player token
7227
- */
7228
- async login(jwt) {
7229
- const token = await this.authManager.exchangeJWT(jwt);
7230
- // Verify token validity and fetch user info
7231
- try {
7232
- await this.playerClient.getPlayerInfo();
7233
- this.logger.debug('Login successful, token validated and user info fetched');
7234
- }
7235
- catch (error) {
7236
- // If token is invalid, logout and re-throw error
7237
- this.logger.error('Token validation failed after login:', error);
7238
- await this.authManager.logout();
7239
- throw new Error('Token validation failed: ' + (error instanceof Error ? error.message : String(error)));
7240
- }
7241
- return token;
7242
- }
7243
6464
  /**
7244
6465
  * Logout
7245
6466
  */
@@ -7696,7 +6917,6 @@ Output ONLY a JSON array of ${predictionNum} strings, nothing else:
7696
6917
  var namespace = /*#__PURE__*/Object.freeze({
7697
6918
  __proto__: null,
7698
6919
  AIContextManager: AIContextManager,
7699
- AuthFlowManager: AuthFlowManager,
7700
6920
  AuthManager: AuthManager,
7701
6921
  BrowserStorage: BrowserStorage,
7702
6922
  BufferLogHandler: BufferLogHandler,