@teamlearners/clawops 0.31.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agent/index.cjs +60 -9
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +15 -2
- package/dist/agent/index.d.ts +15 -2
- package/dist/agent/index.js +52 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/livekit/index.d.cts +1 -1
- package/dist/agent/livekit/index.d.ts +1 -1
- package/dist/{base-CINMfkOD.d.cts → base-B6S9WQRN.d.cts} +10 -0
- package/dist/{base-CINMfkOD.d.ts → base-B6S9WQRN.d.ts} +10 -0
- package/dist/{chunk-KHEU4DKD.cjs → chunk-6ABL54D2.cjs} +3 -3
- package/dist/{chunk-KHEU4DKD.cjs.map → chunk-6ABL54D2.cjs.map} +1 -1
- package/dist/{chunk-LBPPO62W.js → chunk-ESYMNP2G.js} +3 -3
- package/dist/{chunk-LBPPO62W.js.map → chunk-ESYMNP2G.js.map} +1 -1
- package/dist/index.cjs +35 -35
- package/dist/index.js +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -480,7 +480,7 @@ const client = new ClawOps({
|
|
|
480
480
|
|
|
481
481
|
| 언어 | 패키지 | 저장소 |
|
|
482
482
|
|------|--------|--------|
|
|
483
|
-
| Python | [`clawops`](https://pypi.org/project/clawops/) | [
|
|
483
|
+
| Python | [`clawops`](https://pypi.org/project/clawops/) | [sdk-python](https://github.com/clawopshq/sdk-python) |
|
|
484
484
|
|
|
485
485
|
## 요구사항
|
|
486
486
|
|
package/dist/agent/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk6ABL54D2_cjs = require('../chunk-6ABL54D2.cjs');
|
|
4
4
|
var chunkQD2ZY4UP_cjs = require('../chunk-QD2ZY4UP.cjs');
|
|
5
5
|
var fs = require('fs');
|
|
6
6
|
var path = require('path');
|
|
@@ -728,7 +728,7 @@ var MAX_ERROR_MESSAGE_LENGTH = 200;
|
|
|
728
728
|
function getSdkInfo() {
|
|
729
729
|
return {
|
|
730
730
|
name: "clawops-node",
|
|
731
|
-
version:
|
|
731
|
+
version: chunk6ABL54D2_cjs.VERSION,
|
|
732
732
|
runtime: `node/${process.versions.node}`,
|
|
733
733
|
os: `${process.platform}/${os__default.default.arch()}`
|
|
734
734
|
};
|
|
@@ -765,6 +765,7 @@ var CallSession = class {
|
|
|
765
765
|
metadata;
|
|
766
766
|
_status;
|
|
767
767
|
_endedStatus = null;
|
|
768
|
+
_endedDuration = null;
|
|
768
769
|
_sendAudioFn = null;
|
|
769
770
|
_clearAudioFn = null;
|
|
770
771
|
_hangupFn = null;
|
|
@@ -824,9 +825,22 @@ var CallSession = class {
|
|
|
824
825
|
get endedStatus() {
|
|
825
826
|
return this._endedStatus;
|
|
826
827
|
}
|
|
828
|
+
/**
|
|
829
|
+
* 서버가 확정한 통화 시간(초). 통화가 끝나기 전에는 `null`.
|
|
830
|
+
*
|
|
831
|
+
* ⚠️ `duration` 과 다르다 — 그쪽은 SDK 가 로컬 시계로 재는 **경과 시간**이라 통화 중에도
|
|
832
|
+
* 읽히고, 세션이 붙기 전후의 오차를 포함한다. 기록·정산에 쓸 값은 이쪽이다.
|
|
833
|
+
*/
|
|
834
|
+
get endedDuration() {
|
|
835
|
+
return this._endedDuration;
|
|
836
|
+
}
|
|
827
837
|
get duration() {
|
|
828
838
|
return (Date.now() - this.startTime.getTime()) / 1e3;
|
|
829
839
|
}
|
|
840
|
+
/** @internal 서버가 통보한 통화 시간을 확정한다. */
|
|
841
|
+
_setEndedDuration(seconds) {
|
|
842
|
+
this._endedDuration = seconds;
|
|
843
|
+
}
|
|
830
844
|
get metrics() {
|
|
831
845
|
return this._metrics;
|
|
832
846
|
}
|
|
@@ -1416,6 +1430,7 @@ var ATTR_CALL_DIRECTION = "clawops.call.direction";
|
|
|
1416
1430
|
var ATTR_AGENT_ID = "clawops.agent.id";
|
|
1417
1431
|
|
|
1418
1432
|
// src/agent/agent.ts
|
|
1433
|
+
var TERMINAL_FRAME_GRACE_MS = 2e3;
|
|
1419
1434
|
var ClawOpsAgent = class _ClawOpsAgent {
|
|
1420
1435
|
_apiKey;
|
|
1421
1436
|
_accountId;
|
|
@@ -1429,6 +1444,8 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1429
1444
|
_recording;
|
|
1430
1445
|
_recordingPath;
|
|
1431
1446
|
_activeSessions = /* @__PURE__ */ new Map();
|
|
1447
|
+
/** 미디어 정리를 마친 통화가 서버 종료 프레임을 기다리는 자리. callId → resolve. */
|
|
1448
|
+
_terminalWaiters = /* @__PURE__ */ new Map();
|
|
1432
1449
|
_builtinTools;
|
|
1433
1450
|
_passiveDtmfDebounceMs;
|
|
1434
1451
|
_passiveDtmfBuffer = [];
|
|
@@ -1451,7 +1468,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1451
1468
|
constructor(options) {
|
|
1452
1469
|
this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
|
|
1453
1470
|
this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
|
|
1454
|
-
this._baseUrl = options.baseUrl ??
|
|
1471
|
+
this._baseUrl = options.baseUrl ?? chunk6ABL54D2_cjs.DEFAULT_BASE_URL;
|
|
1455
1472
|
this._fromNumber = options.from;
|
|
1456
1473
|
this._session = options.session;
|
|
1457
1474
|
this._recording = options.recording ?? false;
|
|
@@ -1475,7 +1492,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1475
1492
|
}
|
|
1476
1493
|
static _validateGain(name, gain) {
|
|
1477
1494
|
if (typeof gain !== "number" || !Number.isFinite(gain) || gain < 0) {
|
|
1478
|
-
throw new
|
|
1495
|
+
throw new chunk6ABL54D2_cjs.AgentError(`${name}=${gain} must be a finite number >= 0`);
|
|
1479
1496
|
}
|
|
1480
1497
|
return gain;
|
|
1481
1498
|
}
|
|
@@ -1489,7 +1506,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1489
1506
|
tool(nameOrTool, description, parameters, handler) {
|
|
1490
1507
|
if (typeof nameOrTool === "string") {
|
|
1491
1508
|
if (!description || !parameters || !handler) {
|
|
1492
|
-
throw new
|
|
1509
|
+
throw new chunk6ABL54D2_cjs.AgentError(
|
|
1493
1510
|
"tool(name, description, parameters, handler) requires all arguments."
|
|
1494
1511
|
);
|
|
1495
1512
|
}
|
|
@@ -1525,10 +1542,10 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1525
1542
|
async connect() {
|
|
1526
1543
|
if (this._controlWs) return;
|
|
1527
1544
|
if (!this._apiKey) {
|
|
1528
|
-
throw new
|
|
1545
|
+
throw new chunk6ABL54D2_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
|
|
1529
1546
|
}
|
|
1530
1547
|
if (!this._accountId) {
|
|
1531
|
-
throw new
|
|
1548
|
+
throw new chunk6ABL54D2_cjs.AgentError(
|
|
1532
1549
|
"Account ID is required. Set CLAWOPS_ACCOUNT_ID or pass accountId option."
|
|
1533
1550
|
);
|
|
1534
1551
|
}
|
|
@@ -1552,7 +1569,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1552
1569
|
} catch {
|
|
1553
1570
|
}
|
|
1554
1571
|
} catch (err) {
|
|
1555
|
-
throw new
|
|
1572
|
+
throw new chunk6ABL54D2_cjs.AgentConnectionError(
|
|
1556
1573
|
`Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
|
|
1557
1574
|
);
|
|
1558
1575
|
}
|
|
@@ -1578,6 +1595,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1578
1595
|
this._controlWs.close();
|
|
1579
1596
|
this._controlWs = null;
|
|
1580
1597
|
}
|
|
1598
|
+
for (const wake of [...this._terminalWaiters.values()]) wake();
|
|
1581
1599
|
for (const session of this._activeSessions.values()) {
|
|
1582
1600
|
session._markEnded();
|
|
1583
1601
|
}
|
|
@@ -1616,7 +1634,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1616
1634
|
});
|
|
1617
1635
|
if (resp.status !== 201) {
|
|
1618
1636
|
const error = await resp.json();
|
|
1619
|
-
throw new
|
|
1637
|
+
throw new chunk6ABL54D2_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
|
|
1620
1638
|
}
|
|
1621
1639
|
const data = await resp.json();
|
|
1622
1640
|
const callSession = new CallSession({
|
|
@@ -1665,12 +1683,43 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1665
1683
|
this._safeStartCallSession(session, mediaUrl, callId);
|
|
1666
1684
|
}
|
|
1667
1685
|
}
|
|
1686
|
+
/**
|
|
1687
|
+
* 서버 종료 프레임을 짧게 기다린다. 미디어 정리 직후에만 부른다.
|
|
1688
|
+
*
|
|
1689
|
+
* 왜 기다리나: `call_end` 는 인바운드 사용자가 통화 결과를 받는 **유일한 통로**인데, 서버는
|
|
1690
|
+
* 미디어 WS 를 먼저 닫고 자원 정리를 마친 뒤에야 control 종료 프레임을 보낸다. 안 기다리면
|
|
1691
|
+
* 그 핸들러 안에서 `endedDuration` 은 영영 null 이다.
|
|
1692
|
+
*
|
|
1693
|
+
* 상한을 다 쓰는 경우는 제어 연결이 죽어 프레임이 아예 안 오는 때뿐이다 — 그 프레임은
|
|
1694
|
+
* 예전부터 늘 오던 것이고(이번에 바뀐 건 내용뿐), 정상 경로에서는 밀리초 안에 풀린다.
|
|
1695
|
+
*/
|
|
1696
|
+
async _awaitServerTerminal(session) {
|
|
1697
|
+
if (session.endedDuration !== null) return;
|
|
1698
|
+
await new Promise((resolve) => {
|
|
1699
|
+
const done = () => {
|
|
1700
|
+
clearTimeout(timer);
|
|
1701
|
+
this._terminalWaiters.delete(session.callId);
|
|
1702
|
+
resolve();
|
|
1703
|
+
};
|
|
1704
|
+
const timer = setTimeout(() => {
|
|
1705
|
+
this._log.debug(
|
|
1706
|
+
"\uC11C\uBC84 \uC885\uB8CC \uD504\uB808\uC784\uC774 %dms \uC548\uC5D0 \uC624\uC9C0 \uC54A\uC558\uB2E4 \u2014 endedDuration \uC5C6\uC774 \uC9C4\uD589: %s",
|
|
1707
|
+
TERMINAL_FRAME_GRACE_MS,
|
|
1708
|
+
session.callId
|
|
1709
|
+
);
|
|
1710
|
+
done();
|
|
1711
|
+
}, TERMINAL_FRAME_GRACE_MS);
|
|
1712
|
+
this._terminalWaiters.set(session.callId, done);
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1668
1715
|
_handleEnded(event) {
|
|
1669
1716
|
const callId = event["callId"];
|
|
1670
1717
|
const status = event["status"] || "completed";
|
|
1718
|
+
const endedDuration = event["duration"];
|
|
1671
1719
|
const session = this._activeSessions.get(callId);
|
|
1672
1720
|
if (session) {
|
|
1673
1721
|
this._log.info("Call ended (server): %s (status=%s)", callId, status);
|
|
1722
|
+
if (typeof endedDuration === "number") session._setEndedDuration(endedDuration);
|
|
1674
1723
|
if (status !== "completed") {
|
|
1675
1724
|
this._log.info("Outbound call not connected: %s (%s)", callId, status);
|
|
1676
1725
|
session._emit("call_failed", status);
|
|
@@ -1678,6 +1727,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1678
1727
|
session._markEnded(status);
|
|
1679
1728
|
this._activeSessions.delete(callId);
|
|
1680
1729
|
}
|
|
1730
|
+
this._terminalWaiters.get(callId)?.();
|
|
1681
1731
|
void this._cleanupPrewarm(callId);
|
|
1682
1732
|
}
|
|
1683
1733
|
/**
|
|
@@ -2032,6 +2082,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
2032
2082
|
this._controlWs?.send({ event: "call.metrics", callId: session.callId, metrics: session.metrics });
|
|
2033
2083
|
} catch {
|
|
2034
2084
|
}
|
|
2085
|
+
await this._awaitServerTerminal(session);
|
|
2035
2086
|
session._emit("call_end");
|
|
2036
2087
|
session._markEnded();
|
|
2037
2088
|
this._activeSessions.delete(session.callId);
|