@ceki/sdk 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -8
- package/dist/cli.js +956 -116
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +425 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +167 -5
- package/dist/index.d.ts +167 -5
- package/dist/index.js +414 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -472,8 +472,8 @@ function saveSession(sid, data) {
|
|
|
472
472
|
function deleteSession(sid) {
|
|
473
473
|
try {
|
|
474
474
|
fs2.unlinkSync(statePath(sid));
|
|
475
|
-
} catch (
|
|
476
|
-
if (
|
|
475
|
+
} catch (err3) {
|
|
476
|
+
if (err3.code !== "ENOENT") throw err3;
|
|
477
477
|
}
|
|
478
478
|
}
|
|
479
479
|
function getLastSeenTs(sid) {
|
|
@@ -654,27 +654,39 @@ var Browser = class _Browser {
|
|
|
654
654
|
this._sendRaw(msg);
|
|
655
655
|
});
|
|
656
656
|
}
|
|
657
|
-
|
|
658
|
-
|
|
657
|
+
// task 427 — per-call kill-switch. human=false bypasses humanizer timings
|
|
658
|
+
// AND tells the extension to skip mouse-jitter via the `_ceki_raw` marker
|
|
659
|
+
// (see cdp.ts). human=true forces humanizer; human undefined = session
|
|
660
|
+
// default. Global env CEKI_HUMAN_DISABLE=1 nulls this._humanizer in the
|
|
661
|
+
// constructor so all paths become raw.
|
|
662
|
+
_humanizeForCall(human) {
|
|
663
|
+
if (human === false) return null;
|
|
664
|
+
return this._humanizer;
|
|
665
|
+
}
|
|
666
|
+
async navigate(url, timeout = 3e4, opts) {
|
|
667
|
+
const h = this._humanizeForCall(opts?.human);
|
|
668
|
+
if (h) await h.before("navigate");
|
|
659
669
|
const result = await this.send({ method: "Page.navigate", params: { url } }, timeout);
|
|
660
|
-
if (
|
|
670
|
+
if (h) await h.after("navigate");
|
|
661
671
|
return {
|
|
662
672
|
url: String(result?.url ?? url),
|
|
663
673
|
frameId: result?.frameId ? String(result.frameId) : void 0
|
|
664
674
|
};
|
|
665
675
|
}
|
|
666
|
-
async click(x, y) {
|
|
667
|
-
|
|
676
|
+
async click(x, y, opts) {
|
|
677
|
+
const h = this._humanizeForCall(opts?.human);
|
|
678
|
+
if (h) await h.before("click");
|
|
679
|
+
const rawFlag = h === null ? { _ceki_raw: true } : {};
|
|
668
680
|
await this.send({
|
|
669
681
|
method: "Input.dispatchMouseEvent",
|
|
670
|
-
params: { type: "mousePressed", x, y, button: "left", clickCount: 1 }
|
|
682
|
+
params: { type: "mousePressed", x, y, button: "left", clickCount: 1, ...rawFlag }
|
|
671
683
|
});
|
|
672
684
|
await this.send({
|
|
673
685
|
method: "Input.dispatchMouseEvent",
|
|
674
686
|
params: { type: "mouseReleased", x, y, button: "left", clickCount: 1 }
|
|
675
687
|
});
|
|
676
688
|
this._lastPointer = [x, y];
|
|
677
|
-
if (
|
|
689
|
+
if (h) await h.after("click");
|
|
678
690
|
}
|
|
679
691
|
async _sendKeystroke(char) {
|
|
680
692
|
const mapping = keymapForChar(char);
|
|
@@ -720,9 +732,10 @@ var Browser = class _Browser {
|
|
|
720
732
|
});
|
|
721
733
|
}
|
|
722
734
|
}
|
|
723
|
-
async type(text) {
|
|
724
|
-
|
|
725
|
-
|
|
735
|
+
async type(text, opts) {
|
|
736
|
+
const h = this._humanizeForCall(opts?.human);
|
|
737
|
+
if (h) {
|
|
738
|
+
await h.before("type");
|
|
726
739
|
if (this._lastPointer) {
|
|
727
740
|
const [px, py] = this._lastPointer;
|
|
728
741
|
await this.send({
|
|
@@ -735,10 +748,10 @@ var Browser = class _Browser {
|
|
|
735
748
|
});
|
|
736
749
|
}
|
|
737
750
|
}
|
|
738
|
-
const human =
|
|
751
|
+
const human = h ? ["natural", "careful"].includes(h.profile.name) ? h.profile.name : "natural" : null;
|
|
739
752
|
await this.send({ method: "Ceki.typeText", params: { text, human } });
|
|
740
|
-
if (
|
|
741
|
-
await
|
|
753
|
+
if (h) {
|
|
754
|
+
await h.after("type");
|
|
742
755
|
}
|
|
743
756
|
}
|
|
744
757
|
async scroll(opts) {
|
|
@@ -746,13 +759,14 @@ var Browser = class _Browser {
|
|
|
746
759
|
const y = opts?.y ?? 0;
|
|
747
760
|
const deltaX = opts?.deltaX ?? 0;
|
|
748
761
|
const deltaY = opts?.deltaY ?? -300;
|
|
749
|
-
|
|
762
|
+
const h = this._humanizeForCall(opts?.human);
|
|
763
|
+
if (h) await h.before("scroll");
|
|
750
764
|
await this.send({
|
|
751
765
|
method: "Input.dispatchMouseEvent",
|
|
752
766
|
params: { type: "mouseWheel", x, y, deltaX, deltaY }
|
|
753
767
|
});
|
|
754
768
|
this._lastPointer = [x, y];
|
|
755
|
-
if (
|
|
769
|
+
if (h) await h.after("scroll");
|
|
756
770
|
}
|
|
757
771
|
async screenshot(opts) {
|
|
758
772
|
const format = opts?.format ?? "base64";
|
|
@@ -1175,10 +1189,10 @@ var Browser = class _Browser {
|
|
|
1175
1189
|
_onChatSendError(msg) {
|
|
1176
1190
|
this.chat._onSendError(msg);
|
|
1177
1191
|
}
|
|
1178
|
-
_rejectAllPending(
|
|
1192
|
+
_rejectAllPending(err3) {
|
|
1179
1193
|
for (const [id, pending] of this._pendingCdp) {
|
|
1180
1194
|
clearTimeout(pending.timer);
|
|
1181
|
-
pending.reject(
|
|
1195
|
+
pending.reject(err3);
|
|
1182
1196
|
}
|
|
1183
1197
|
this._pendingCdp.clear();
|
|
1184
1198
|
}
|
|
@@ -1590,9 +1604,9 @@ var Client = class _Client {
|
|
|
1590
1604
|
this._stopHeartbeat();
|
|
1591
1605
|
const reasonStr = reason.toString();
|
|
1592
1606
|
if (code === 4401 || code === 4403) {
|
|
1593
|
-
const
|
|
1607
|
+
const err3 = new AuthError(reasonStr || `Auth failed (${code})`);
|
|
1594
1608
|
if (this._connectReject) {
|
|
1595
|
-
this._connectReject(
|
|
1609
|
+
this._connectReject(err3);
|
|
1596
1610
|
this._connectResolve = null;
|
|
1597
1611
|
this._connectReject = null;
|
|
1598
1612
|
}
|
|
@@ -1602,9 +1616,9 @@ var Client = class _Client {
|
|
|
1602
1616
|
this._scheduleReconnect();
|
|
1603
1617
|
}
|
|
1604
1618
|
});
|
|
1605
|
-
this._ws.on("error", (
|
|
1619
|
+
this._ws.on("error", (err3) => {
|
|
1606
1620
|
if (this._connectReject) {
|
|
1607
|
-
this._connectReject(new TransportError(
|
|
1621
|
+
this._connectReject(new TransportError(err3.message));
|
|
1608
1622
|
this._connectResolve = null;
|
|
1609
1623
|
this._connectReject = null;
|
|
1610
1624
|
}
|
|
@@ -1623,8 +1637,8 @@ var Client = class _Client {
|
|
|
1623
1637
|
_scheduleReconnect() {
|
|
1624
1638
|
if (this._closed || this._reconnecting) return;
|
|
1625
1639
|
if (this._reconnectAttempt >= MAX_RECONNECT_ATTEMPTS) {
|
|
1626
|
-
const
|
|
1627
|
-
this._rejectAllPending(
|
|
1640
|
+
const err3 = new ConnectionLost("Max reconnection attempts exceeded");
|
|
1641
|
+
this._rejectAllPending(err3);
|
|
1628
1642
|
return;
|
|
1629
1643
|
}
|
|
1630
1644
|
this._reconnecting = true;
|
|
@@ -1915,44 +1929,44 @@ var Client = class _Client {
|
|
|
1915
1929
|
}
|
|
1916
1930
|
}
|
|
1917
1931
|
}
|
|
1918
|
-
let
|
|
1932
|
+
let err3;
|
|
1919
1933
|
switch (code) {
|
|
1920
1934
|
case -1012:
|
|
1921
|
-
|
|
1922
|
-
this._rejectAllPending(
|
|
1935
|
+
err3 = new InsufficientFunds(reason);
|
|
1936
|
+
this._rejectAllPending(err3);
|
|
1923
1937
|
break;
|
|
1924
1938
|
case -1013:
|
|
1925
|
-
|
|
1926
|
-
this._rejectAllPending(
|
|
1939
|
+
err3 = new RateLimitExceeded(0, reason);
|
|
1940
|
+
this._rejectAllPending(err3);
|
|
1927
1941
|
break;
|
|
1928
1942
|
case -1015:
|
|
1929
|
-
|
|
1930
|
-
this._rejectFirstPendingRent(
|
|
1943
|
+
err3 = new ProviderOffline(reason);
|
|
1944
|
+
this._rejectFirstPendingRent(err3);
|
|
1931
1945
|
break;
|
|
1932
1946
|
default:
|
|
1933
|
-
|
|
1934
|
-
this._rejectFirstPendingRent(
|
|
1947
|
+
err3 = new CekiBrowserError(reason || `relay error ${code}`);
|
|
1948
|
+
this._rejectFirstPendingRent(err3);
|
|
1935
1949
|
break;
|
|
1936
1950
|
}
|
|
1937
1951
|
}
|
|
1938
|
-
_rejectFirstPendingRent(
|
|
1952
|
+
_rejectFirstPendingRent(err3) {
|
|
1939
1953
|
const eventId = this._pendingRents.keys().next().value;
|
|
1940
1954
|
if (eventId != null) {
|
|
1941
1955
|
const pending = this._pendingRents.get(eventId);
|
|
1942
1956
|
clearTimeout(pending.timer);
|
|
1943
1957
|
this._pendingRents.delete(eventId);
|
|
1944
|
-
pending.reject(
|
|
1958
|
+
pending.reject(err3);
|
|
1945
1959
|
}
|
|
1946
1960
|
}
|
|
1947
|
-
_rejectAllPending(
|
|
1961
|
+
_rejectAllPending(err3) {
|
|
1948
1962
|
for (const [key, pending] of this._pendingRents) {
|
|
1949
1963
|
clearTimeout(pending.timer);
|
|
1950
|
-
pending.reject(
|
|
1964
|
+
pending.reject(err3);
|
|
1951
1965
|
}
|
|
1952
1966
|
this._pendingRents.clear();
|
|
1953
1967
|
for (const [key, pending] of this._pendingResumes) {
|
|
1954
1968
|
clearTimeout(pending.timer);
|
|
1955
|
-
pending.reject(
|
|
1969
|
+
pending.reject(err3);
|
|
1956
1970
|
}
|
|
1957
1971
|
this._pendingResumes.clear();
|
|
1958
1972
|
}
|
|
@@ -1976,17 +1990,796 @@ async function connect(apiKey, opts) {
|
|
|
1976
1990
|
return Client.create(apiKey, opts);
|
|
1977
1991
|
}
|
|
1978
1992
|
|
|
1979
|
-
// src/
|
|
1993
|
+
// src/contract.ts
|
|
1994
|
+
var ROLE_REVIEWER = 5;
|
|
1995
|
+
var ROLE_QA = 6;
|
|
1996
|
+
var ContractError = class extends Error {
|
|
1997
|
+
constructor(message) {
|
|
1998
|
+
super(message);
|
|
1999
|
+
this.name = "ContractError";
|
|
2000
|
+
}
|
|
2001
|
+
};
|
|
2002
|
+
function parseBenefitable(value) {
|
|
2003
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
2004
|
+
const parts = String(value).split(":");
|
|
2005
|
+
if (parts.length !== 2) {
|
|
2006
|
+
throw new Error(`benefitable must be 'type:id', got: ${JSON.stringify(value)}`);
|
|
2007
|
+
}
|
|
2008
|
+
const [btype, bid] = parts;
|
|
2009
|
+
const num = Number.parseInt(bid, 10);
|
|
2010
|
+
if (!Number.isFinite(num) || Number.isNaN(num)) {
|
|
2011
|
+
throw new Error(`benefitable id must be int, got: ${JSON.stringify(bid)}`);
|
|
2012
|
+
}
|
|
2013
|
+
return { type: btype, value: num };
|
|
2014
|
+
}
|
|
2015
|
+
function parseParticipant(value, roleId) {
|
|
2016
|
+
const base = parseBenefitable(value);
|
|
2017
|
+
if (base === null) return null;
|
|
2018
|
+
return {
|
|
2019
|
+
participable_id: base.value,
|
|
2020
|
+
type: base.type,
|
|
2021
|
+
role_id: roleId
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
function cleanArgs(o) {
|
|
2025
|
+
const out3 = {};
|
|
2026
|
+
for (const [k, v] of Object.entries(o)) {
|
|
2027
|
+
if (v !== void 0 && v !== null) {
|
|
2028
|
+
out3[k] = v;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
return out3;
|
|
2032
|
+
}
|
|
2033
|
+
function deriveLabel(desc) {
|
|
2034
|
+
if (!desc) return "progress";
|
|
2035
|
+
const lines = String(desc).split(/\r?\n/);
|
|
2036
|
+
for (const ln of lines) {
|
|
2037
|
+
const t = ln.trim();
|
|
2038
|
+
if (t) return t.slice(0, 60);
|
|
2039
|
+
}
|
|
2040
|
+
return "progress";
|
|
2041
|
+
}
|
|
2042
|
+
function contractIdsFromEnv() {
|
|
2043
|
+
const raw = (process.env.CEKI_CONTRACT_IDS ?? "").trim();
|
|
2044
|
+
if (!raw) return [];
|
|
2045
|
+
try {
|
|
2046
|
+
const parsed = JSON.parse(raw);
|
|
2047
|
+
if (Array.isArray(parsed)) return parsed.map((x) => String(x));
|
|
2048
|
+
} catch {
|
|
2049
|
+
}
|
|
2050
|
+
return raw.replace(/\[/g, "").replace(/\]/g, "").split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
2051
|
+
}
|
|
2052
|
+
function resolveEndpoint() {
|
|
2053
|
+
const override = process.env.CEKI_AGENT_MCP_ENDPOINT;
|
|
2054
|
+
if (override) return override.replace(/\/+$/, "");
|
|
2055
|
+
const base = (process.env.CEKI_API_URL ?? defaults.apiUrl).replace(/\/+$/, "");
|
|
2056
|
+
return `${base}/mcp/agent`;
|
|
2057
|
+
}
|
|
2058
|
+
function resolveApiBase() {
|
|
2059
|
+
const override = process.env.CEKI_API_BASE;
|
|
2060
|
+
if (override) return override.replace(/\/+$/, "");
|
|
2061
|
+
const base = (process.env.CEKI_API_URL ?? defaults.apiUrl).replace(/\/+$/, "");
|
|
2062
|
+
return `${base}/api`;
|
|
2063
|
+
}
|
|
2064
|
+
function resolveToken() {
|
|
2065
|
+
return process.env.CEKI_AGENT_TOKEN ?? process.env.CEKI_API_KEY ?? "";
|
|
2066
|
+
}
|
|
2067
|
+
var TOOL_MAP = {
|
|
2068
|
+
list: "get-my-contracts",
|
|
2069
|
+
members: "get-contract-members",
|
|
2070
|
+
tasks: "get-contract-events",
|
|
2071
|
+
"my-jobs": "get-my-jobs",
|
|
2072
|
+
task: "get-event",
|
|
2073
|
+
children: "get-event-children",
|
|
2074
|
+
history: "get-event-history",
|
|
2075
|
+
create: "create-contract-event",
|
|
2076
|
+
comment: "comment",
|
|
2077
|
+
propose: "propose-correction",
|
|
2078
|
+
vote: "vote-correction"
|
|
2079
|
+
};
|
|
2080
|
+
var FetchHttpClient = class {
|
|
2081
|
+
constructor(timeoutMs) {
|
|
2082
|
+
this.timeoutMs = timeoutMs;
|
|
2083
|
+
}
|
|
2084
|
+
timeoutMs;
|
|
2085
|
+
withTimeout() {
|
|
2086
|
+
const ctl = new AbortController();
|
|
2087
|
+
const t = setTimeout(() => ctl.abort(), this.timeoutMs);
|
|
2088
|
+
return { signal: ctl.signal, cancel: () => clearTimeout(t) };
|
|
2089
|
+
}
|
|
2090
|
+
async post(url, init) {
|
|
2091
|
+
const { signal, cancel } = this.withTimeout();
|
|
2092
|
+
try {
|
|
2093
|
+
const r = await fetch(url, { method: "POST", headers: init.headers, body: init.body, signal });
|
|
2094
|
+
const text = await r.text();
|
|
2095
|
+
return {
|
|
2096
|
+
status: r.status,
|
|
2097
|
+
text: async () => text,
|
|
2098
|
+
json: async () => {
|
|
2099
|
+
try {
|
|
2100
|
+
return JSON.parse(text);
|
|
2101
|
+
} catch {
|
|
2102
|
+
return { raw: text };
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
} finally {
|
|
2107
|
+
cancel();
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
async get(url, init) {
|
|
2111
|
+
const { signal, cancel } = this.withTimeout();
|
|
2112
|
+
try {
|
|
2113
|
+
const r = await fetch(url, { method: "GET", headers: init.headers, signal });
|
|
2114
|
+
const text = await r.text();
|
|
2115
|
+
return {
|
|
2116
|
+
status: r.status,
|
|
2117
|
+
text: async () => text,
|
|
2118
|
+
json: async () => {
|
|
2119
|
+
try {
|
|
2120
|
+
return JSON.parse(text);
|
|
2121
|
+
} catch {
|
|
2122
|
+
return { raw: text };
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
};
|
|
2126
|
+
} finally {
|
|
2127
|
+
cancel();
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
};
|
|
2131
|
+
var ContractClient = class {
|
|
2132
|
+
endpoint;
|
|
2133
|
+
apiBase;
|
|
2134
|
+
token;
|
|
2135
|
+
http;
|
|
2136
|
+
constructor(opts = {}) {
|
|
2137
|
+
this.endpoint = (opts.endpoint ?? resolveEndpoint()).replace(/\/+$/, "");
|
|
2138
|
+
this.apiBase = (opts.apiBase ?? resolveApiBase()).replace(/\/+$/, "");
|
|
2139
|
+
this.token = opts.token !== void 0 ? opts.token : resolveToken();
|
|
2140
|
+
this.http = opts.http ?? new FetchHttpClient(opts.timeoutMs ?? 3e4);
|
|
2141
|
+
}
|
|
2142
|
+
headers() {
|
|
2143
|
+
if (!this.token) {
|
|
2144
|
+
throw new ContractError("agent token not set (CEKI_AGENT_TOKEN or CEKI_API_KEY)");
|
|
2145
|
+
}
|
|
2146
|
+
return {
|
|
2147
|
+
"Content-Type": "application/json",
|
|
2148
|
+
Accept: "application/json",
|
|
2149
|
+
Authorization: `Bearer ${this.token}`
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
async rpc(method, params) {
|
|
2153
|
+
const body = JSON.stringify({
|
|
2154
|
+
jsonrpc: "2.0",
|
|
2155
|
+
id: Date.now(),
|
|
2156
|
+
method,
|
|
2157
|
+
params
|
|
2158
|
+
});
|
|
2159
|
+
const resp = await this.http.post(this.endpoint, { headers: this.headers(), body });
|
|
2160
|
+
let parsed;
|
|
2161
|
+
try {
|
|
2162
|
+
parsed = await resp.json();
|
|
2163
|
+
} catch {
|
|
2164
|
+
parsed = { raw: await resp.text() };
|
|
2165
|
+
}
|
|
2166
|
+
if (resp.status !== 200) {
|
|
2167
|
+
const snippet = JSON.stringify(parsed).slice(0, 400);
|
|
2168
|
+
throw new ContractError(`HTTP ${resp.status}: ${snippet}`);
|
|
2169
|
+
}
|
|
2170
|
+
return parsed ?? {};
|
|
2171
|
+
}
|
|
2172
|
+
/** Call MCP tool; unwrap content[].text (JSON-parsed) or structuredContent. */
|
|
2173
|
+
async call(tool, args = {}) {
|
|
2174
|
+
const body = await this.rpc("tools/call", { name: tool, arguments: args });
|
|
2175
|
+
if (body["error"]) {
|
|
2176
|
+
throw new ContractError(`${tool} \u2192 ${JSON.stringify(body["error"]).slice(0, 400)}`);
|
|
2177
|
+
}
|
|
2178
|
+
const result = body["result"] ?? {};
|
|
2179
|
+
const content = result["content"];
|
|
2180
|
+
if (Array.isArray(content)) {
|
|
2181
|
+
const texts = content.filter((c) => c["type"] === "text").map((c) => String(c["text"] ?? ""));
|
|
2182
|
+
const joined = texts.join("\n");
|
|
2183
|
+
try {
|
|
2184
|
+
return JSON.parse(joined);
|
|
2185
|
+
} catch {
|
|
2186
|
+
return joined;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
if (result["structuredContent"] !== void 0) return result["structuredContent"];
|
|
2190
|
+
return result;
|
|
2191
|
+
}
|
|
2192
|
+
async tools() {
|
|
2193
|
+
const body = await this.rpc("tools/list", {});
|
|
2194
|
+
const result = body["result"] ?? {};
|
|
2195
|
+
const tools = result["tools"];
|
|
2196
|
+
if (Array.isArray(tools)) return tools.map((t) => t["name"]);
|
|
2197
|
+
return body;
|
|
2198
|
+
}
|
|
2199
|
+
async raw(tool, args = {}) {
|
|
2200
|
+
return this.call(tool, args);
|
|
2201
|
+
}
|
|
2202
|
+
// ── domain helpers ──────────────────────────────────────────────
|
|
2203
|
+
async listContracts() {
|
|
2204
|
+
return this.call(TOOL_MAP.list, {});
|
|
2205
|
+
}
|
|
2206
|
+
async members(contractId) {
|
|
2207
|
+
return this.call(TOOL_MAP.members, { contract_id: Number(contractId) });
|
|
2208
|
+
}
|
|
2209
|
+
async tasks(contractId) {
|
|
2210
|
+
return this.call(TOOL_MAP.tasks, { contract_id: Number(contractId) });
|
|
2211
|
+
}
|
|
2212
|
+
async myJobs() {
|
|
2213
|
+
return this.call(TOOL_MAP["my-jobs"], {});
|
|
2214
|
+
}
|
|
2215
|
+
async task(eventId) {
|
|
2216
|
+
return this.call(TOOL_MAP.task, { event_id: Number(eventId) });
|
|
2217
|
+
}
|
|
2218
|
+
async children(eventId) {
|
|
2219
|
+
return this.call(TOOL_MAP.children, { event_id: Number(eventId) });
|
|
2220
|
+
}
|
|
2221
|
+
async history(eventId, opts = {}) {
|
|
2222
|
+
const args = cleanArgs({ event_id: Number(eventId), limit: opts.limit });
|
|
2223
|
+
return this.call(TOOL_MAP.history, args);
|
|
2224
|
+
}
|
|
2225
|
+
async create(contractId, opts) {
|
|
2226
|
+
const users = [];
|
|
2227
|
+
const rev = parseParticipant(opts.reviewer, ROLE_REVIEWER);
|
|
2228
|
+
if (rev !== null) users.push(rev);
|
|
2229
|
+
const qa = parseParticipant(opts.qa, ROLE_QA);
|
|
2230
|
+
if (qa !== null) users.push(qa);
|
|
2231
|
+
if (opts.participants && opts.participants.length) {
|
|
2232
|
+
users.push(...opts.participants);
|
|
2233
|
+
}
|
|
2234
|
+
const args = cleanArgs({
|
|
2235
|
+
contract_id: Number(contractId),
|
|
2236
|
+
label: opts.label,
|
|
2237
|
+
type_id: opts.type,
|
|
2238
|
+
status_id: opts.status,
|
|
2239
|
+
kal_schedule_id: opts.kalScheduleId,
|
|
2240
|
+
start: opts.start,
|
|
2241
|
+
end: opts.end,
|
|
2242
|
+
timezone: opts.timezone,
|
|
2243
|
+
date: opts.date,
|
|
2244
|
+
duration: opts.duration,
|
|
2245
|
+
amount: opts.amount,
|
|
2246
|
+
currency: opts.currency,
|
|
2247
|
+
description: opts.description,
|
|
2248
|
+
data: opts.data,
|
|
2249
|
+
benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0,
|
|
2250
|
+
users: users.length > 0 ? users : void 0
|
|
2251
|
+
});
|
|
2252
|
+
return this.call(TOOL_MAP.create, args);
|
|
2253
|
+
}
|
|
2254
|
+
async comment(eventId, opts = {}) {
|
|
2255
|
+
const args = cleanArgs({
|
|
2256
|
+
event_id: Number(eventId),
|
|
2257
|
+
label: opts.label,
|
|
2258
|
+
type_id: opts.type,
|
|
2259
|
+
status_id: opts.status,
|
|
2260
|
+
start: opts.start,
|
|
2261
|
+
end: opts.end,
|
|
2262
|
+
date: opts.date,
|
|
2263
|
+
duration: opts.duration,
|
|
2264
|
+
amount: opts.amount,
|
|
2265
|
+
currency: opts.currency,
|
|
2266
|
+
description: opts.description,
|
|
2267
|
+
benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0
|
|
2268
|
+
});
|
|
2269
|
+
return this.call(TOOL_MAP.comment, args);
|
|
2270
|
+
}
|
|
2271
|
+
async propose(eventId, opts = {}) {
|
|
2272
|
+
const args = cleanArgs({
|
|
2273
|
+
event_id: Number(eventId),
|
|
2274
|
+
status_id: opts.status,
|
|
2275
|
+
label: opts.label,
|
|
2276
|
+
description: opts.description,
|
|
2277
|
+
start: opts.start,
|
|
2278
|
+
end: opts.end,
|
|
2279
|
+
date: opts.date,
|
|
2280
|
+
duration: opts.duration,
|
|
2281
|
+
amount: opts.amount,
|
|
2282
|
+
currency: opts.currency,
|
|
2283
|
+
benefitable: opts.benefitable !== void 0 ? parseBenefitable(opts.benefitable) : void 0
|
|
2284
|
+
});
|
|
2285
|
+
return this.call(TOOL_MAP.propose, args);
|
|
2286
|
+
}
|
|
2287
|
+
/** Status correction (optional) + progress comment in one shot.
|
|
2288
|
+
*
|
|
2289
|
+
* The event's own description is NOT touched. `desc` becomes the
|
|
2290
|
+
* body of a child comment-event, not a label/description overwrite
|
|
2291
|
+
* on the parent event. Use this for Hand/QA/Reviewer progress
|
|
2292
|
+
* reports — `propose --desc` would clobber the parent spec.
|
|
2293
|
+
*/
|
|
2294
|
+
async progress(eventId, opts) {
|
|
2295
|
+
let statusResult = null;
|
|
2296
|
+
if (opts.status !== void 0 && opts.status !== null) {
|
|
2297
|
+
statusResult = await this.propose(eventId, { status: Number(opts.status) });
|
|
2298
|
+
}
|
|
2299
|
+
const label = deriveLabel(opts.desc);
|
|
2300
|
+
const commentResult = await this.comment(eventId, { label, description: opts.desc });
|
|
2301
|
+
return { status_correction: statusResult, comment: commentResult };
|
|
2302
|
+
}
|
|
2303
|
+
async vote(eventId, ids, vote) {
|
|
2304
|
+
return this.call(TOOL_MAP.vote, {
|
|
2305
|
+
event_id: Number(eventId),
|
|
2306
|
+
ids: ids.map((i) => Number(i)),
|
|
2307
|
+
vote: Boolean(vote)
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2310
|
+
// ── polling (REST, not MCP) ────────────────────────────────────
|
|
2311
|
+
/** GET /agent/polling. Returns [] on 429 (rate-limit, 10/min/token). */
|
|
2312
|
+
async poll() {
|
|
2313
|
+
const resp = await this.http.get(`${this.apiBase}/agent/polling`, {
|
|
2314
|
+
headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` }
|
|
2315
|
+
});
|
|
2316
|
+
if (resp.status === 429) return [];
|
|
2317
|
+
if (resp.status !== 200) {
|
|
2318
|
+
let body2;
|
|
2319
|
+
try {
|
|
2320
|
+
body2 = await resp.json();
|
|
2321
|
+
} catch {
|
|
2322
|
+
body2 = await resp.text();
|
|
2323
|
+
}
|
|
2324
|
+
throw new ContractError(`polling HTTP ${resp.status}: ${JSON.stringify(body2).slice(0, 300)}`);
|
|
2325
|
+
}
|
|
2326
|
+
const body = await resp.json();
|
|
2327
|
+
if (Array.isArray(body)) return body;
|
|
2328
|
+
if (body && typeof body === "object") {
|
|
2329
|
+
const obj = body;
|
|
2330
|
+
for (const k of ["notifications", "data", "items"]) {
|
|
2331
|
+
const v = obj[k];
|
|
2332
|
+
if (Array.isArray(v)) return v;
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
return [];
|
|
2336
|
+
}
|
|
2337
|
+
};
|
|
2338
|
+
|
|
2339
|
+
// src/timelog.ts
|
|
2340
|
+
var TOOL_MAP2 = {
|
|
2341
|
+
start: "timelog-start",
|
|
2342
|
+
stop: "timelog-stop",
|
|
2343
|
+
check: "timelog-check"
|
|
2344
|
+
};
|
|
2345
|
+
var TimelogClient = class {
|
|
2346
|
+
c;
|
|
2347
|
+
constructor(opts = {}) {
|
|
2348
|
+
if (opts.contract) {
|
|
2349
|
+
this.c = opts.contract;
|
|
2350
|
+
} else {
|
|
2351
|
+
this.c = new ContractClient(opts);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
async start(eventId) {
|
|
2355
|
+
return this.c.call(TOOL_MAP2.start, { event_id: Number(eventId) });
|
|
2356
|
+
}
|
|
2357
|
+
async stop(eventId, label) {
|
|
2358
|
+
const args = { event_id: Number(eventId) };
|
|
2359
|
+
if (label !== void 0 && label !== null) args["label"] = label;
|
|
2360
|
+
return this.c.call(TOOL_MAP2.stop, args);
|
|
2361
|
+
}
|
|
2362
|
+
async check(eventId) {
|
|
2363
|
+
return this.c.call(TOOL_MAP2.check, { event_id: Number(eventId) });
|
|
2364
|
+
}
|
|
2365
|
+
};
|
|
2366
|
+
|
|
2367
|
+
// src/contract-cli.ts
|
|
1980
2368
|
function out(data) {
|
|
2369
|
+
if (typeof data === "string") {
|
|
2370
|
+
process.stdout.write(data + "\n");
|
|
2371
|
+
} else {
|
|
2372
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
function err(message, code = "error") {
|
|
2376
|
+
process.stderr.write(JSON.stringify({ error: message, code }) + "\n");
|
|
2377
|
+
}
|
|
2378
|
+
function parseParticipantSpec(spec) {
|
|
2379
|
+
if (!spec || typeof spec !== "string") {
|
|
2380
|
+
throw new Error(`--participant must be a non-empty string, got: ${JSON.stringify(spec)}`);
|
|
2381
|
+
}
|
|
2382
|
+
const parts = spec.split(":");
|
|
2383
|
+
if (parts.length < 3) {
|
|
2384
|
+
throw new Error(
|
|
2385
|
+
`--participant must be 'type:id:role' (e.g. agent:5:reviewer), got: ${JSON.stringify(spec)}`
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
const [ptype, pid, role, ...rest] = parts;
|
|
2389
|
+
if (ptype !== "agent" && ptype !== "user") {
|
|
2390
|
+
throw new Error(`--participant type must be 'agent' or 'user', got: ${JSON.stringify(ptype)}`);
|
|
2391
|
+
}
|
|
2392
|
+
const value = Number.parseInt(pid, 10);
|
|
2393
|
+
if (!Number.isFinite(value) || Number.isNaN(value)) {
|
|
2394
|
+
throw new Error(`--participant id must be int, got: ${JSON.stringify(pid)}`);
|
|
2395
|
+
}
|
|
2396
|
+
const roleMap = { reviewer: ROLE_REVIEWER, qa: ROLE_QA };
|
|
2397
|
+
let roleId;
|
|
2398
|
+
if (role in roleMap) {
|
|
2399
|
+
roleId = roleMap[role];
|
|
2400
|
+
} else if (role === "role") {
|
|
2401
|
+
if (rest.length === 0) {
|
|
2402
|
+
throw new Error(`--participant 'role:NUMBER' needs a number, got: ${JSON.stringify(spec)}`);
|
|
2403
|
+
}
|
|
2404
|
+
const n = Number.parseInt(rest[0], 10);
|
|
2405
|
+
if (!Number.isFinite(n) || Number.isNaN(n)) {
|
|
2406
|
+
throw new Error(`--participant role id must be int, got: ${JSON.stringify(rest[0])}`);
|
|
2407
|
+
}
|
|
2408
|
+
roleId = n;
|
|
2409
|
+
} else {
|
|
2410
|
+
throw new Error(
|
|
2411
|
+
`--participant unknown role ${JSON.stringify(role)}; expected 'reviewer', 'qa', or 'role:NUMBER'`
|
|
2412
|
+
);
|
|
2413
|
+
}
|
|
2414
|
+
return { participable_id: value, type: ptype, role_id: roleId };
|
|
2415
|
+
}
|
|
2416
|
+
function parseArgs(argv) {
|
|
2417
|
+
const positional = [];
|
|
2418
|
+
const flags = {};
|
|
2419
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2420
|
+
const a = argv[i];
|
|
2421
|
+
if (a.startsWith("--")) {
|
|
2422
|
+
const name = a.slice(2);
|
|
2423
|
+
const next = argv[i + 1];
|
|
2424
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
2425
|
+
flags[name] = true;
|
|
2426
|
+
} else {
|
|
2427
|
+
if (flags[name] !== void 0) {
|
|
2428
|
+
const cur = flags[name];
|
|
2429
|
+
if (Array.isArray(cur)) cur.push(next);
|
|
2430
|
+
else flags[name] = [cur, next];
|
|
2431
|
+
} else {
|
|
2432
|
+
flags[name] = next;
|
|
2433
|
+
}
|
|
2434
|
+
i++;
|
|
2435
|
+
}
|
|
2436
|
+
} else {
|
|
2437
|
+
positional.push(a);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
return { positional, flags };
|
|
2441
|
+
}
|
|
2442
|
+
function flagStr(args, key) {
|
|
2443
|
+
const v = args.flags[key];
|
|
2444
|
+
if (v === void 0 || v === true) return void 0;
|
|
2445
|
+
if (Array.isArray(v)) return v[v.length - 1];
|
|
2446
|
+
return v;
|
|
2447
|
+
}
|
|
2448
|
+
function flagInt(args, key) {
|
|
2449
|
+
const v = flagStr(args, key);
|
|
2450
|
+
if (v === void 0) return void 0;
|
|
2451
|
+
const n = Number.parseInt(v, 10);
|
|
2452
|
+
if (Number.isNaN(n)) throw new Error(`--${key} must be int, got: ${JSON.stringify(v)}`);
|
|
2453
|
+
return n;
|
|
2454
|
+
}
|
|
2455
|
+
function flagList(args, key) {
|
|
2456
|
+
const v = args.flags[key];
|
|
2457
|
+
if (v === void 0) return [];
|
|
2458
|
+
if (v === true) return [];
|
|
2459
|
+
if (Array.isArray(v)) return v;
|
|
2460
|
+
return [v];
|
|
2461
|
+
}
|
|
2462
|
+
function requireFlag(args, key) {
|
|
2463
|
+
const v = flagStr(args, key);
|
|
2464
|
+
if (v === void 0) throw new Error(`--${key} is required`);
|
|
2465
|
+
return v;
|
|
2466
|
+
}
|
|
2467
|
+
function dump(value) {
|
|
2468
|
+
out(value);
|
|
2469
|
+
}
|
|
2470
|
+
var contractClientFactory = () => new ContractClient();
|
|
2471
|
+
var timelogClientFactory = () => new TimelogClient();
|
|
2472
|
+
async function cmdContract(argv) {
|
|
2473
|
+
const action = argv[0];
|
|
2474
|
+
if (!action) {
|
|
2475
|
+
err("contract: subcommand required", "args");
|
|
2476
|
+
return 1;
|
|
2477
|
+
}
|
|
2478
|
+
const rest = argv.slice(1);
|
|
2479
|
+
const args = parseArgs(rest);
|
|
2480
|
+
const client = contractClientFactory();
|
|
2481
|
+
try {
|
|
2482
|
+
switch (action) {
|
|
2483
|
+
case "list": {
|
|
2484
|
+
dump(await client.listContracts());
|
|
2485
|
+
return 0;
|
|
2486
|
+
}
|
|
2487
|
+
case "members": {
|
|
2488
|
+
const cid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2489
|
+
if (Number.isNaN(cid)) {
|
|
2490
|
+
err("contract members: cid required", "args");
|
|
2491
|
+
return 1;
|
|
2492
|
+
}
|
|
2493
|
+
dump(await client.members(cid));
|
|
2494
|
+
return 0;
|
|
2495
|
+
}
|
|
2496
|
+
case "tasks": {
|
|
2497
|
+
const explicit = args.positional[0];
|
|
2498
|
+
const ids = explicit !== void 0 ? [explicit] : contractIdsFromEnv();
|
|
2499
|
+
if (ids.length === 0) {
|
|
2500
|
+
err("no contract id (positional or CEKI_CONTRACT_IDS)", "args");
|
|
2501
|
+
return 1;
|
|
2502
|
+
}
|
|
2503
|
+
for (const cid of ids) {
|
|
2504
|
+
process.stdout.write(`--- contract ${cid} ---
|
|
2505
|
+
`);
|
|
2506
|
+
dump(await client.tasks(Number(cid)));
|
|
2507
|
+
}
|
|
2508
|
+
return 0;
|
|
2509
|
+
}
|
|
2510
|
+
case "my-jobs": {
|
|
2511
|
+
dump(await client.myJobs());
|
|
2512
|
+
return 0;
|
|
2513
|
+
}
|
|
2514
|
+
case "task": {
|
|
2515
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2516
|
+
if (Number.isNaN(eid)) {
|
|
2517
|
+
err("contract task: eid required", "args");
|
|
2518
|
+
return 1;
|
|
2519
|
+
}
|
|
2520
|
+
dump(await client.task(eid));
|
|
2521
|
+
return 0;
|
|
2522
|
+
}
|
|
2523
|
+
case "children": {
|
|
2524
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2525
|
+
if (Number.isNaN(eid)) {
|
|
2526
|
+
err("contract children: eid required", "args");
|
|
2527
|
+
return 1;
|
|
2528
|
+
}
|
|
2529
|
+
dump(await client.children(eid));
|
|
2530
|
+
return 0;
|
|
2531
|
+
}
|
|
2532
|
+
case "history": {
|
|
2533
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2534
|
+
if (Number.isNaN(eid)) {
|
|
2535
|
+
err("contract history: eid required", "args");
|
|
2536
|
+
return 1;
|
|
2537
|
+
}
|
|
2538
|
+
dump(await client.history(eid, { limit: flagInt(args, "limit") }));
|
|
2539
|
+
return 0;
|
|
2540
|
+
}
|
|
2541
|
+
case "create": {
|
|
2542
|
+
const explicit = args.positional[0];
|
|
2543
|
+
let cid;
|
|
2544
|
+
if (explicit !== void 0) {
|
|
2545
|
+
cid = Number.parseInt(explicit, 10);
|
|
2546
|
+
} else {
|
|
2547
|
+
const envIds = contractIdsFromEnv();
|
|
2548
|
+
if (envIds.length === 0) {
|
|
2549
|
+
err("contract id required (positional or CEKI_CONTRACT_IDS)", "args");
|
|
2550
|
+
return 1;
|
|
2551
|
+
}
|
|
2552
|
+
cid = Number.parseInt(envIds[0], 10);
|
|
2553
|
+
}
|
|
2554
|
+
if (Number.isNaN(cid)) {
|
|
2555
|
+
err("contract create: cid must be int", "args");
|
|
2556
|
+
return 1;
|
|
2557
|
+
}
|
|
2558
|
+
const label = requireFlag(args, "label");
|
|
2559
|
+
const dataRaw = flagStr(args, "data");
|
|
2560
|
+
const dataObj = dataRaw ? JSON.parse(dataRaw) : void 0;
|
|
2561
|
+
let extras = [];
|
|
2562
|
+
try {
|
|
2563
|
+
extras = flagList(args, "participant").map(parseParticipantSpec);
|
|
2564
|
+
} catch (e) {
|
|
2565
|
+
err(e.message, "args");
|
|
2566
|
+
return 1;
|
|
2567
|
+
}
|
|
2568
|
+
dump(
|
|
2569
|
+
await client.create(cid, {
|
|
2570
|
+
label,
|
|
2571
|
+
type: flagInt(args, "type"),
|
|
2572
|
+
status: flagInt(args, "status"),
|
|
2573
|
+
kalScheduleId: flagInt(args, "kal-schedule"),
|
|
2574
|
+
start: flagStr(args, "start"),
|
|
2575
|
+
end: flagStr(args, "end"),
|
|
2576
|
+
timezone: flagStr(args, "timezone"),
|
|
2577
|
+
date: flagStr(args, "date"),
|
|
2578
|
+
duration: flagInt(args, "duration"),
|
|
2579
|
+
amount: flagInt(args, "amount"),
|
|
2580
|
+
currency: flagStr(args, "currency"),
|
|
2581
|
+
description: flagStr(args, "desc"),
|
|
2582
|
+
data: dataObj,
|
|
2583
|
+
benefitable: flagStr(args, "benefitable"),
|
|
2584
|
+
reviewer: flagStr(args, "reviewer"),
|
|
2585
|
+
qa: flagStr(args, "qa"),
|
|
2586
|
+
participants: extras.length > 0 ? extras : void 0
|
|
2587
|
+
})
|
|
2588
|
+
);
|
|
2589
|
+
return 0;
|
|
2590
|
+
}
|
|
2591
|
+
case "comment": {
|
|
2592
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2593
|
+
if (Number.isNaN(eid)) {
|
|
2594
|
+
err("contract comment: eid required", "args");
|
|
2595
|
+
return 1;
|
|
2596
|
+
}
|
|
2597
|
+
dump(
|
|
2598
|
+
await client.comment(eid, {
|
|
2599
|
+
label: flagStr(args, "label"),
|
|
2600
|
+
type: flagInt(args, "type"),
|
|
2601
|
+
status: flagInt(args, "status"),
|
|
2602
|
+
start: flagStr(args, "start"),
|
|
2603
|
+
end: flagStr(args, "end"),
|
|
2604
|
+
date: flagStr(args, "date"),
|
|
2605
|
+
duration: flagInt(args, "duration"),
|
|
2606
|
+
amount: flagInt(args, "amount"),
|
|
2607
|
+
currency: flagStr(args, "currency"),
|
|
2608
|
+
description: flagStr(args, "desc"),
|
|
2609
|
+
benefitable: flagStr(args, "benefitable")
|
|
2610
|
+
})
|
|
2611
|
+
);
|
|
2612
|
+
return 0;
|
|
2613
|
+
}
|
|
2614
|
+
case "propose": {
|
|
2615
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2616
|
+
if (Number.isNaN(eid)) {
|
|
2617
|
+
err("contract propose: eid required", "args");
|
|
2618
|
+
return 1;
|
|
2619
|
+
}
|
|
2620
|
+
dump(
|
|
2621
|
+
await client.propose(eid, {
|
|
2622
|
+
status: flagInt(args, "status"),
|
|
2623
|
+
label: flagStr(args, "label"),
|
|
2624
|
+
description: flagStr(args, "desc"),
|
|
2625
|
+
start: flagStr(args, "start"),
|
|
2626
|
+
end: flagStr(args, "end"),
|
|
2627
|
+
date: flagStr(args, "date"),
|
|
2628
|
+
duration: flagInt(args, "duration"),
|
|
2629
|
+
amount: flagInt(args, "amount"),
|
|
2630
|
+
currency: flagStr(args, "currency"),
|
|
2631
|
+
benefitable: flagStr(args, "benefitable")
|
|
2632
|
+
})
|
|
2633
|
+
);
|
|
2634
|
+
return 0;
|
|
2635
|
+
}
|
|
2636
|
+
case "progress": {
|
|
2637
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2638
|
+
if (Number.isNaN(eid)) {
|
|
2639
|
+
err("contract progress: eid required", "args");
|
|
2640
|
+
return 1;
|
|
2641
|
+
}
|
|
2642
|
+
const desc = flagStr(args, "desc");
|
|
2643
|
+
if (desc === void 0) {
|
|
2644
|
+
err("contract progress: --desc is required", "args");
|
|
2645
|
+
return 1;
|
|
2646
|
+
}
|
|
2647
|
+
dump(
|
|
2648
|
+
await client.progress(eid, {
|
|
2649
|
+
status: flagInt(args, "status"),
|
|
2650
|
+
desc
|
|
2651
|
+
})
|
|
2652
|
+
);
|
|
2653
|
+
return 0;
|
|
2654
|
+
}
|
|
2655
|
+
case "vote": {
|
|
2656
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2657
|
+
if (Number.isNaN(eid)) {
|
|
2658
|
+
err("contract vote: eid required", "args");
|
|
2659
|
+
return 1;
|
|
2660
|
+
}
|
|
2661
|
+
const idsRaw = requireFlag(args, "ids");
|
|
2662
|
+
const voteRaw = requireFlag(args, "vote");
|
|
2663
|
+
const ids = idsRaw.split(",").map((s) => s.trim()).filter((s) => s.length > 0).map((s) => Number.parseInt(s, 10));
|
|
2664
|
+
const vote = ["true", "1", "yes"].includes(voteRaw.toLowerCase());
|
|
2665
|
+
dump(await client.vote(eid, ids, vote));
|
|
2666
|
+
return 0;
|
|
2667
|
+
}
|
|
2668
|
+
case "poll": {
|
|
2669
|
+
const items = await client.poll();
|
|
2670
|
+
dump({ count: items.length, notifications: items });
|
|
2671
|
+
return 0;
|
|
2672
|
+
}
|
|
2673
|
+
case "watch": {
|
|
2674
|
+
const interval = Math.max(6, Number.parseInt(args.positional[0] ?? "8", 10) || 8);
|
|
2675
|
+
process.stderr.write(
|
|
2676
|
+
`[watch] poll every ${interval}s (limit 10/min/token; do not go below 6s)
|
|
2677
|
+
`
|
|
2678
|
+
);
|
|
2679
|
+
while (true) {
|
|
2680
|
+
const items = await client.poll();
|
|
2681
|
+
if (items.length > 0) {
|
|
2682
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
2683
|
+
for (const n of items) {
|
|
2684
|
+
process.stdout.write(JSON.stringify({ ts, notification: n }) + "\n");
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
await new Promise((r) => setTimeout(r, interval * 1e3));
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
case "tools": {
|
|
2691
|
+
dump(await client.tools());
|
|
2692
|
+
return 0;
|
|
2693
|
+
}
|
|
2694
|
+
case "raw": {
|
|
2695
|
+
const tool = args.positional[0];
|
|
2696
|
+
if (!tool) {
|
|
2697
|
+
err("contract raw: tool name required", "args");
|
|
2698
|
+
return 1;
|
|
2699
|
+
}
|
|
2700
|
+
const payloadRaw = args.positional[1] ?? "{}";
|
|
2701
|
+
const payload = JSON.parse(payloadRaw);
|
|
2702
|
+
dump(await client.raw(tool, payload));
|
|
2703
|
+
return 0;
|
|
2704
|
+
}
|
|
2705
|
+
default: {
|
|
2706
|
+
err(`unknown contract action: ${action}`, "args");
|
|
2707
|
+
return 1;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
} catch (e) {
|
|
2711
|
+
if (e instanceof ContractError) {
|
|
2712
|
+
err(e.message, "contract");
|
|
2713
|
+
return 1;
|
|
2714
|
+
}
|
|
2715
|
+
err(e.message ?? String(e), "error");
|
|
2716
|
+
return 1;
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
async function cmdTimelog(argv) {
|
|
2720
|
+
const action = argv[0];
|
|
2721
|
+
if (!action) {
|
|
2722
|
+
err("timelog: subcommand required", "args");
|
|
2723
|
+
return 1;
|
|
2724
|
+
}
|
|
2725
|
+
const rest = argv.slice(1);
|
|
2726
|
+
const args = parseArgs(rest);
|
|
2727
|
+
const client = timelogClientFactory();
|
|
2728
|
+
try {
|
|
2729
|
+
switch (action) {
|
|
2730
|
+
case "start": {
|
|
2731
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2732
|
+
if (Number.isNaN(eid)) {
|
|
2733
|
+
err("timelog start: event_id required", "args");
|
|
2734
|
+
return 1;
|
|
2735
|
+
}
|
|
2736
|
+
dump(await client.start(eid));
|
|
2737
|
+
return 0;
|
|
2738
|
+
}
|
|
2739
|
+
case "stop": {
|
|
2740
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2741
|
+
if (Number.isNaN(eid)) {
|
|
2742
|
+
err("timelog stop: event_id required", "args");
|
|
2743
|
+
return 1;
|
|
2744
|
+
}
|
|
2745
|
+
dump(await client.stop(eid, flagStr(args, "label")));
|
|
2746
|
+
return 0;
|
|
2747
|
+
}
|
|
2748
|
+
case "check": {
|
|
2749
|
+
const eid = Number.parseInt(args.positional[0] ?? "", 10);
|
|
2750
|
+
if (Number.isNaN(eid)) {
|
|
2751
|
+
err("timelog check: event_id required", "args");
|
|
2752
|
+
return 1;
|
|
2753
|
+
}
|
|
2754
|
+
dump(await client.check(eid));
|
|
2755
|
+
return 0;
|
|
2756
|
+
}
|
|
2757
|
+
default: {
|
|
2758
|
+
err(`unknown timelog action: ${action}`, "args");
|
|
2759
|
+
return 1;
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
} catch (e) {
|
|
2763
|
+
if (e instanceof ContractError) {
|
|
2764
|
+
err(e.message, "timelog");
|
|
2765
|
+
return 1;
|
|
2766
|
+
}
|
|
2767
|
+
err(e.message ?? String(e), "error");
|
|
2768
|
+
return 1;
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
// src/cli.ts
|
|
2773
|
+
function out2(data) {
|
|
1981
2774
|
process.stdout.write(JSON.stringify(data) + "\n");
|
|
1982
2775
|
}
|
|
1983
|
-
function
|
|
2776
|
+
function err2(error, code = "error") {
|
|
1984
2777
|
process.stderr.write(JSON.stringify({ error, code }) + "\n");
|
|
1985
2778
|
}
|
|
1986
2779
|
function getApiKey() {
|
|
1987
2780
|
const key = process.env.CEKI_API_KEY;
|
|
1988
2781
|
if (!key) {
|
|
1989
|
-
|
|
2782
|
+
err2("CEKI_API_KEY not set", "auth");
|
|
1990
2783
|
process.exit(2);
|
|
1991
2784
|
}
|
|
1992
2785
|
return key;
|
|
@@ -2018,14 +2811,14 @@ async function cmdRent(args) {
|
|
|
2018
2811
|
if (args[i] === "--mode" && args[i + 1]) {
|
|
2019
2812
|
const v = args[++i];
|
|
2020
2813
|
if (v !== "incognito" && v !== "main") {
|
|
2021
|
-
|
|
2814
|
+
err2("invalid mode, must be incognito or main", "args");
|
|
2022
2815
|
process.exit(1);
|
|
2023
2816
|
}
|
|
2024
2817
|
mode = v;
|
|
2025
2818
|
}
|
|
2026
2819
|
}
|
|
2027
2820
|
if (scheduleId == null) {
|
|
2028
|
-
|
|
2821
|
+
err2("--schedule is required", "args");
|
|
2029
2822
|
process.exit(1);
|
|
2030
2823
|
}
|
|
2031
2824
|
const apiKey = getApiKey();
|
|
@@ -2043,7 +2836,7 @@ async function cmdRent(args) {
|
|
|
2043
2836
|
schedule_id: browser.scheduleId,
|
|
2044
2837
|
last_seen_ts: null
|
|
2045
2838
|
});
|
|
2046
|
-
|
|
2839
|
+
out2({
|
|
2047
2840
|
session_id: browser.sessionId,
|
|
2048
2841
|
chat_topic_id: browser.chatTopicId,
|
|
2049
2842
|
schedule_id: browser.scheduleId
|
|
@@ -2066,7 +2859,7 @@ async function cmdSearch(args) {
|
|
|
2066
2859
|
const client = await connect(apiKey, connectOptions());
|
|
2067
2860
|
try {
|
|
2068
2861
|
const results = await client.search(filters, limit);
|
|
2069
|
-
|
|
2862
|
+
out2(results);
|
|
2070
2863
|
} finally {
|
|
2071
2864
|
await closeClient(client);
|
|
2072
2865
|
}
|
|
@@ -2085,7 +2878,7 @@ async function cmdSessions(args) {
|
|
|
2085
2878
|
try {
|
|
2086
2879
|
const results = await client.listSessions({ active: !showAll, limit });
|
|
2087
2880
|
if (jsonOutput) {
|
|
2088
|
-
|
|
2881
|
+
out2(results);
|
|
2089
2882
|
} else {
|
|
2090
2883
|
if (!results.length) {
|
|
2091
2884
|
process.stdout.write("No sessions found.\n");
|
|
@@ -2114,7 +2907,7 @@ async function cmdMyBrowsers() {
|
|
|
2114
2907
|
const client = await connect(apiKey, connectOptions());
|
|
2115
2908
|
try {
|
|
2116
2909
|
const results = await client.myBrowsers();
|
|
2117
|
-
|
|
2910
|
+
out2(results);
|
|
2118
2911
|
} finally {
|
|
2119
2912
|
await closeClient(client);
|
|
2120
2913
|
}
|
|
@@ -2125,7 +2918,7 @@ async function cmdSnapshot(sid, args) {
|
|
|
2125
2918
|
if ((args[i] === "-o" || args[i] === "--output") && args[i + 1]) outputPath = args[++i];
|
|
2126
2919
|
}
|
|
2127
2920
|
if (!outputPath) {
|
|
2128
|
-
|
|
2921
|
+
err2("-o/--output is required", "args");
|
|
2129
2922
|
process.exit(1);
|
|
2130
2923
|
}
|
|
2131
2924
|
const apiKey = getApiKey();
|
|
@@ -2144,80 +2937,87 @@ async function cmdSnapshot(sid, args) {
|
|
|
2144
2937
|
text: m.text,
|
|
2145
2938
|
ts: m.created_at
|
|
2146
2939
|
}));
|
|
2147
|
-
|
|
2940
|
+
out2({ screenshot: outputPath, chat: chatList, ts: snap.ts.toISOString() });
|
|
2148
2941
|
} finally {
|
|
2149
2942
|
await closeClient(client);
|
|
2150
2943
|
}
|
|
2151
2944
|
}
|
|
2945
|
+
function parseNoHuman(args) {
|
|
2946
|
+
return args.includes("--no-human") || args.includes("--raw");
|
|
2947
|
+
}
|
|
2152
2948
|
async function cmdNavigate(sid, args) {
|
|
2153
|
-
const url = args
|
|
2949
|
+
const url = args.find((a) => !a.startsWith("--"));
|
|
2154
2950
|
if (!url) {
|
|
2155
|
-
|
|
2951
|
+
err2("URL is required", "args");
|
|
2156
2952
|
process.exit(1);
|
|
2157
2953
|
}
|
|
2954
|
+
const raw = parseNoHuman(args);
|
|
2158
2955
|
const apiKey = getApiKey();
|
|
2159
2956
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2160
2957
|
try {
|
|
2161
|
-
await browser.navigate(url);
|
|
2162
|
-
|
|
2958
|
+
await browser.navigate(url, 3e4, raw ? { human: false } : void 0);
|
|
2959
|
+
out2({ ok: true });
|
|
2163
2960
|
} finally {
|
|
2164
2961
|
await closeClient(client);
|
|
2165
2962
|
}
|
|
2166
2963
|
}
|
|
2167
2964
|
async function cmdClick(sid, args) {
|
|
2168
|
-
const
|
|
2169
|
-
const
|
|
2965
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
2966
|
+
const x = parseInt(positional[0], 10);
|
|
2967
|
+
const y = parseInt(positional[1], 10);
|
|
2170
2968
|
if (isNaN(x) || isNaN(y)) {
|
|
2171
|
-
|
|
2969
|
+
err2("x and y coordinates are required", "args");
|
|
2172
2970
|
process.exit(1);
|
|
2173
2971
|
}
|
|
2972
|
+
const raw = parseNoHuman(args);
|
|
2174
2973
|
const apiKey = getApiKey();
|
|
2175
2974
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2176
2975
|
try {
|
|
2177
|
-
await browser.click(x, y);
|
|
2178
|
-
|
|
2976
|
+
await browser.click(x, y, raw ? { human: false } : void 0);
|
|
2977
|
+
out2({ ok: true, pointer: [x, y] });
|
|
2179
2978
|
} finally {
|
|
2180
2979
|
await closeClient(client);
|
|
2181
2980
|
}
|
|
2182
2981
|
}
|
|
2183
2982
|
async function cmdType(sid, args) {
|
|
2184
2983
|
let text = "";
|
|
2185
|
-
let
|
|
2984
|
+
let raw = false;
|
|
2186
2985
|
for (let i = 0; i < args.length; i++) {
|
|
2187
|
-
if (args[i] === "--
|
|
2188
|
-
else if (
|
|
2986
|
+
if (args[i] === "--no-human" || args[i] === "--raw") raw = true;
|
|
2987
|
+
else if (args[i] === "--natural") {
|
|
2988
|
+
} else if (!text) text = args[i];
|
|
2189
2989
|
}
|
|
2190
2990
|
if (!text) {
|
|
2191
|
-
|
|
2991
|
+
err2("text is required", "args");
|
|
2192
2992
|
process.exit(1);
|
|
2193
2993
|
}
|
|
2194
2994
|
const apiKey = getApiKey();
|
|
2195
|
-
const human = natural ? "natural" : null;
|
|
2196
2995
|
const client = await connect(apiKey, connectOptions());
|
|
2197
2996
|
try {
|
|
2198
|
-
const browser = await client.resume(sid
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
}
|
|
2202
|
-
await browser.type(text);
|
|
2203
|
-
out({ ok: true });
|
|
2997
|
+
const browser = await client.resume(sid);
|
|
2998
|
+
await browser.type(text, raw ? { human: false } : void 0);
|
|
2999
|
+
out2({ ok: true });
|
|
2204
3000
|
} finally {
|
|
2205
3001
|
await closeClient(client);
|
|
2206
3002
|
}
|
|
2207
3003
|
}
|
|
2208
3004
|
async function cmdScroll(sid, args) {
|
|
2209
|
-
const
|
|
2210
|
-
const
|
|
2211
|
-
const
|
|
3005
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
3006
|
+
const x = parseInt(positional[0], 10);
|
|
3007
|
+
const y = parseInt(positional[1], 10);
|
|
3008
|
+
const dy = parseInt(positional[2], 10);
|
|
2212
3009
|
if (isNaN(x) || isNaN(y) || isNaN(dy)) {
|
|
2213
|
-
|
|
3010
|
+
err2("x, y, dy are required", "args");
|
|
2214
3011
|
process.exit(1);
|
|
2215
3012
|
}
|
|
3013
|
+
const raw = parseNoHuman(args);
|
|
2216
3014
|
const apiKey = getApiKey();
|
|
2217
3015
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2218
3016
|
try {
|
|
2219
|
-
|
|
2220
|
-
|
|
3017
|
+
const scrollOpts = { x, y, deltaY: dy };
|
|
3018
|
+
if (raw) scrollOpts.human = false;
|
|
3019
|
+
await browser.scroll(scrollOpts);
|
|
3020
|
+
out2({ ok: true });
|
|
2221
3021
|
} finally {
|
|
2222
3022
|
await closeClient(client);
|
|
2223
3023
|
}
|
|
@@ -2230,11 +3030,11 @@ async function cmdChat(sid, action, args) {
|
|
|
2230
3030
|
case "send": {
|
|
2231
3031
|
const text = args[0];
|
|
2232
3032
|
if (!text) {
|
|
2233
|
-
|
|
3033
|
+
err2("text is required", "args");
|
|
2234
3034
|
process.exit(1);
|
|
2235
3035
|
}
|
|
2236
3036
|
const result = await browser.chat.send(text);
|
|
2237
|
-
|
|
3037
|
+
out2({ ok: true, message_id: result.messageId });
|
|
2238
3038
|
break;
|
|
2239
3039
|
}
|
|
2240
3040
|
case "send-image": {
|
|
@@ -2245,14 +3045,14 @@ async function cmdChat(sid, action, args) {
|
|
|
2245
3045
|
if (args[i] === "--text" && args[i + 1]) text = args[++i];
|
|
2246
3046
|
}
|
|
2247
3047
|
if (!imagePath) {
|
|
2248
|
-
|
|
3048
|
+
err2("--image is required", "args");
|
|
2249
3049
|
process.exit(1);
|
|
2250
3050
|
}
|
|
2251
3051
|
if (text) {
|
|
2252
3052
|
await browser.chat.send(text);
|
|
2253
3053
|
}
|
|
2254
3054
|
const result = await browser.chat.sendImage(imagePath);
|
|
2255
|
-
|
|
3055
|
+
out2({ ok: true, message_id: result.messageId });
|
|
2256
3056
|
break;
|
|
2257
3057
|
}
|
|
2258
3058
|
case "next": {
|
|
@@ -2265,7 +3065,7 @@ async function cmdChat(sid, action, args) {
|
|
|
2265
3065
|
if (msgs.length > 0) {
|
|
2266
3066
|
const m = msgs[0];
|
|
2267
3067
|
updateLastSeenTs(sid, m.created_at);
|
|
2268
|
-
|
|
3068
|
+
out2({ from: m.sender_id, text: m.text, ts: m.created_at });
|
|
2269
3069
|
} else {
|
|
2270
3070
|
let resolved = false;
|
|
2271
3071
|
const waitPromise = new Promise((resolve2) => {
|
|
@@ -2286,9 +3086,9 @@ async function cmdChat(sid, action, args) {
|
|
|
2286
3086
|
const msg = await waitPromise;
|
|
2287
3087
|
if (msg) {
|
|
2288
3088
|
updateLastSeenTs(sid, msg.created_at);
|
|
2289
|
-
|
|
3089
|
+
out2({ from: msg.sender_id, text: msg.text, ts: msg.created_at });
|
|
2290
3090
|
} else {
|
|
2291
|
-
|
|
3091
|
+
out2(null);
|
|
2292
3092
|
}
|
|
2293
3093
|
}
|
|
2294
3094
|
break;
|
|
@@ -2309,11 +3109,11 @@ async function cmdChat(sid, action, args) {
|
|
|
2309
3109
|
if (args[i] === "--limit" && args[i + 1]) limit = parseInt(args[++i], 10);
|
|
2310
3110
|
}
|
|
2311
3111
|
const msgs = await browser.chat.history({ since, limit });
|
|
2312
|
-
|
|
3112
|
+
out2(msgs.map((m) => ({ from: m.sender_id, text: m.text, ts: m.created_at })));
|
|
2313
3113
|
break;
|
|
2314
3114
|
}
|
|
2315
3115
|
default:
|
|
2316
|
-
|
|
3116
|
+
err2(`Unknown chat action: ${action}`, "args");
|
|
2317
3117
|
process.exit(1);
|
|
2318
3118
|
}
|
|
2319
3119
|
} finally {
|
|
@@ -2326,7 +3126,7 @@ async function cmdStop(sid) {
|
|
|
2326
3126
|
try {
|
|
2327
3127
|
await browser.close();
|
|
2328
3128
|
deleteSession(sid);
|
|
2329
|
-
|
|
3129
|
+
out2({ ok: true });
|
|
2330
3130
|
} finally {
|
|
2331
3131
|
await closeClient(client);
|
|
2332
3132
|
}
|
|
@@ -2346,7 +3146,7 @@ async function cmdProfile(sid, action, args) {
|
|
|
2346
3146
|
if (args[i] === "--no-session-storage") noSessionStorage = true;
|
|
2347
3147
|
}
|
|
2348
3148
|
if (!outputPath) {
|
|
2349
|
-
|
|
3149
|
+
err2("-o/--output is required", "args");
|
|
2350
3150
|
process.exit(1);
|
|
2351
3151
|
}
|
|
2352
3152
|
const profile = await browser.profile.export({
|
|
@@ -2354,7 +3154,7 @@ async function cmdProfile(sid, action, args) {
|
|
|
2354
3154
|
includeSessionStorage: !noSessionStorage
|
|
2355
3155
|
});
|
|
2356
3156
|
fs4.writeFileSync(outputPath, JSON.stringify(profile, null, 2), "utf-8");
|
|
2357
|
-
|
|
3157
|
+
out2({ ok: true, path: outputPath });
|
|
2358
3158
|
break;
|
|
2359
3159
|
}
|
|
2360
3160
|
case "import": {
|
|
@@ -2363,16 +3163,16 @@ async function cmdProfile(sid, action, args) {
|
|
|
2363
3163
|
if ((args[i] === "-i" || args[i] === "--input") && args[i + 1]) inputPath = args[++i];
|
|
2364
3164
|
}
|
|
2365
3165
|
if (!inputPath) {
|
|
2366
|
-
|
|
3166
|
+
err2("-i/--input is required", "args");
|
|
2367
3167
|
process.exit(1);
|
|
2368
3168
|
}
|
|
2369
3169
|
const profileData = JSON.parse(fs4.readFileSync(inputPath, "utf-8"));
|
|
2370
3170
|
await browser.profile.import(profileData);
|
|
2371
|
-
|
|
3171
|
+
out2({ ok: true });
|
|
2372
3172
|
break;
|
|
2373
3173
|
}
|
|
2374
3174
|
default:
|
|
2375
|
-
|
|
3175
|
+
err2(`Unknown profile action: ${action}`, "args");
|
|
2376
3176
|
process.exit(1);
|
|
2377
3177
|
}
|
|
2378
3178
|
} finally {
|
|
@@ -2384,7 +3184,7 @@ async function cmdWait(sid) {
|
|
|
2384
3184
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2385
3185
|
try {
|
|
2386
3186
|
const reason = await browser.waitUntilEnded();
|
|
2387
|
-
|
|
3187
|
+
out2({ ended: true, reason });
|
|
2388
3188
|
} finally {
|
|
2389
3189
|
await closeClient(client);
|
|
2390
3190
|
}
|
|
@@ -2397,7 +3197,7 @@ async function cmdScreenshot(sid, args) {
|
|
|
2397
3197
|
if (args[i] === "--full") fullPage = true;
|
|
2398
3198
|
}
|
|
2399
3199
|
if (!outputPath) {
|
|
2400
|
-
|
|
3200
|
+
err2("-o/--output is required", "args");
|
|
2401
3201
|
process.exit(1);
|
|
2402
3202
|
}
|
|
2403
3203
|
const apiKey = getApiKey();
|
|
@@ -2405,7 +3205,7 @@ async function cmdScreenshot(sid, args) {
|
|
|
2405
3205
|
try {
|
|
2406
3206
|
const data = await browser.screenshot({ format: "png", fullPage });
|
|
2407
3207
|
fs4.writeFileSync(outputPath, data);
|
|
2408
|
-
|
|
3208
|
+
out2({ ok: true, path: outputPath });
|
|
2409
3209
|
} finally {
|
|
2410
3210
|
await closeClient(client);
|
|
2411
3211
|
}
|
|
@@ -2415,7 +3215,7 @@ async function cmdSwitchTab(sid) {
|
|
|
2415
3215
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2416
3216
|
try {
|
|
2417
3217
|
await browser.switchTab();
|
|
2418
|
-
|
|
3218
|
+
out2({ ok: true });
|
|
2419
3219
|
} finally {
|
|
2420
3220
|
await closeClient(client);
|
|
2421
3221
|
}
|
|
@@ -2430,7 +3230,7 @@ async function cmdConfigure(sid, args) {
|
|
|
2430
3230
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2431
3231
|
try {
|
|
2432
3232
|
await browser.configure(opts);
|
|
2433
|
-
|
|
3233
|
+
out2({ ok: true });
|
|
2434
3234
|
} finally {
|
|
2435
3235
|
await closeClient(client);
|
|
2436
3236
|
}
|
|
@@ -2443,14 +3243,14 @@ async function cmdCdp(sid, args) {
|
|
|
2443
3243
|
if (args[i] === "--params" && args[i + 1]) params = JSON.parse(args[++i]);
|
|
2444
3244
|
}
|
|
2445
3245
|
if (!method) {
|
|
2446
|
-
|
|
3246
|
+
err2("--method is required", "args");
|
|
2447
3247
|
process.exit(1);
|
|
2448
3248
|
}
|
|
2449
3249
|
const apiKey = getApiKey();
|
|
2450
3250
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2451
3251
|
try {
|
|
2452
3252
|
const result = await browser.send({ method, params });
|
|
2453
|
-
|
|
3253
|
+
out2(result);
|
|
2454
3254
|
} finally {
|
|
2455
3255
|
await closeClient(client);
|
|
2456
3256
|
}
|
|
@@ -2472,7 +3272,7 @@ async function cmdRequestCaptcha(sid, args) {
|
|
|
2472
3272
|
completionTimeout: completion,
|
|
2473
3273
|
autoAccept: !manual
|
|
2474
3274
|
});
|
|
2475
|
-
|
|
3275
|
+
out2({
|
|
2476
3276
|
solved: result.solved,
|
|
2477
3277
|
proof_message_id: result.proofMessageId,
|
|
2478
3278
|
cancel_reason: result.cancelReason,
|
|
@@ -2482,7 +3282,7 @@ async function cmdRequestCaptcha(sid, args) {
|
|
|
2482
3282
|
if (!result.solved) process.exit(1);
|
|
2483
3283
|
} catch (e) {
|
|
2484
3284
|
if (e instanceof CaptchaTimeoutError) {
|
|
2485
|
-
|
|
3285
|
+
out2({ solved: false, cancel_reason: `timeout:${e.phase}`, child_event_id: null, correction_id: null });
|
|
2486
3286
|
process.exit(1);
|
|
2487
3287
|
}
|
|
2488
3288
|
throw e;
|
|
@@ -2502,18 +3302,18 @@ async function cmdUpload(sid, args) {
|
|
|
2502
3302
|
if (args[i] === "--mime" && args[i + 1]) mime2 = args[++i];
|
|
2503
3303
|
}
|
|
2504
3304
|
if (!selector || !filePath) {
|
|
2505
|
-
|
|
3305
|
+
err2("--selector and --file are required", "args");
|
|
2506
3306
|
process.exit(1);
|
|
2507
3307
|
}
|
|
2508
3308
|
if (!fs4.existsSync(filePath)) {
|
|
2509
|
-
|
|
3309
|
+
err2(`File not found: ${filePath}`, "args");
|
|
2510
3310
|
process.exit(1);
|
|
2511
3311
|
}
|
|
2512
3312
|
const apiKey = getApiKey();
|
|
2513
3313
|
const [client, browser] = await resumeBrowser(apiKey, sid);
|
|
2514
3314
|
try {
|
|
2515
3315
|
const result = await browser.upload(selector, filePath, filename, mime2);
|
|
2516
|
-
|
|
3316
|
+
out2(result);
|
|
2517
3317
|
} finally {
|
|
2518
3318
|
await closeClient(client);
|
|
2519
3319
|
}
|
|
@@ -2529,10 +3329,10 @@ Commands:
|
|
|
2529
3329
|
search [--limit N] [--filter k=v]...
|
|
2530
3330
|
snapshot <sid> -o PATH
|
|
2531
3331
|
screenshot <sid> -o PATH [--full]
|
|
2532
|
-
navigate <sid> <url>
|
|
2533
|
-
click <sid> <x> <y>
|
|
2534
|
-
type <sid> "<text>" [--
|
|
2535
|
-
scroll <sid> <x> <y> <dy>
|
|
3332
|
+
navigate <sid> <url> [--no-human|--raw]
|
|
3333
|
+
click <sid> <x> <y> [--no-human|--raw]
|
|
3334
|
+
type <sid> "<text>" [--no-human|--raw] (humanized by default)
|
|
3335
|
+
scroll <sid> <x> <y> <dy> [--no-human|--raw]
|
|
2536
3336
|
switch-tab <sid>
|
|
2537
3337
|
configure <sid> [--masking-mode true|false] [--fingerprint true|false]
|
|
2538
3338
|
cdp <sid> --method <M> [--params JSON]
|
|
@@ -2547,12 +3347,46 @@ Commands:
|
|
|
2547
3347
|
profile import <sid> -i file
|
|
2548
3348
|
stop <sid>
|
|
2549
3349
|
|
|
3350
|
+
contract list
|
|
3351
|
+
contract members <cid>
|
|
3352
|
+
contract tasks [cid] (default: CEKI_CONTRACT_IDS)
|
|
3353
|
+
contract my-jobs
|
|
3354
|
+
contract task <eid>
|
|
3355
|
+
contract children <eid>
|
|
3356
|
+
contract history <eid> [--limit N]
|
|
3357
|
+
contract create [cid] --label X [--type N] [--status N] [--kal-schedule N]
|
|
3358
|
+
[--start S] [--end E] [--timezone TZ] [--date D]
|
|
3359
|
+
[--duration N] [--amount N] [--currency C]
|
|
3360
|
+
[--benefitable agent:N|user:N]
|
|
3361
|
+
[--reviewer agent:N|user:N] [--qa agent:N|user:N]
|
|
3362
|
+
[--participant agent:N:reviewer|user:N:qa|agent:N:role:NUM]...
|
|
3363
|
+
[--desc text] [--data JSON]
|
|
3364
|
+
contract comment <eid> [--label X] [--type N] [--status N] [--start S]
|
|
3365
|
+
[--end E] [--date D] [--duration N] [--amount N]
|
|
3366
|
+
[--currency C] [--benefitable agent:N] [--desc text]
|
|
3367
|
+
contract propose <eid> [--status N] [--label X] [--desc text] [--start S]
|
|
3368
|
+
[--end E] [--date D] [--duration N] [--amount N]
|
|
3369
|
+
[--currency C] [--benefitable agent:N]
|
|
3370
|
+
contract progress <eid> [--status N] --desc TEXT
|
|
3371
|
+
contract vote <eid> --ids 1,2,3 --vote true|false
|
|
3372
|
+
contract poll
|
|
3373
|
+
contract watch [interval]
|
|
3374
|
+
contract tools
|
|
3375
|
+
contract raw <tool> [JSON]
|
|
3376
|
+
|
|
3377
|
+
timelog start <event_id>
|
|
3378
|
+
timelog stop <event_id> [--label TEXT]
|
|
3379
|
+
timelog check <event_id>
|
|
3380
|
+
|
|
2550
3381
|
Environment:
|
|
2551
3382
|
CEKI_API_KEY (required)
|
|
2552
3383
|
CEKI_RELAY_URL (default: wss://browser.ceki.me/ws/agent)
|
|
2553
3384
|
CEKI_API_URL (default: https://api.ceki.me)
|
|
2554
3385
|
CEKI_CHAT_URL (default: https://chat.ceki.me/api/chat)
|
|
2555
3386
|
CEKI_BASIC_AUTH_USER / CEKI_BASIC_AUTH_PASS (optional)
|
|
3387
|
+
CEKI_AGENT_TOKEN (contract/timelog; falls back to CEKI_API_KEY)
|
|
3388
|
+
CEKI_AGENT_MCP_ENDPOINT (override /mcp/agent endpoint)
|
|
3389
|
+
CEKI_CONTRACT_IDS (default contract for 'contract tasks'/'create')
|
|
2556
3390
|
|
|
2557
3391
|
Exit codes: 0=success, 1=error, 2=auth, 3=session_not_found, 4=timeout, 5=network`);
|
|
2558
3392
|
}
|
|
@@ -2574,6 +3408,12 @@ async function main() {
|
|
|
2574
3408
|
}
|
|
2575
3409
|
const command = argv[0];
|
|
2576
3410
|
const rest = argv.slice(1);
|
|
3411
|
+
if (command === "contract") {
|
|
3412
|
+
process.exit(await cmdContract(rest));
|
|
3413
|
+
}
|
|
3414
|
+
if (command === "timelog") {
|
|
3415
|
+
process.exit(await cmdTimelog(rest));
|
|
3416
|
+
}
|
|
2577
3417
|
switch (command) {
|
|
2578
3418
|
case "rent":
|
|
2579
3419
|
await cmdRent(rest);
|
|
@@ -2628,45 +3468,45 @@ async function main() {
|
|
|
2628
3468
|
break;
|
|
2629
3469
|
case "chat":
|
|
2630
3470
|
if (rest.length < 2) {
|
|
2631
|
-
|
|
3471
|
+
err2("Usage: ceki chat <sid> <action> [args]", "args");
|
|
2632
3472
|
process.exit(1);
|
|
2633
3473
|
}
|
|
2634
3474
|
await cmdChat(rest[0], rest[1], rest.slice(2));
|
|
2635
3475
|
break;
|
|
2636
3476
|
case "profile":
|
|
2637
3477
|
if (rest.length < 2) {
|
|
2638
|
-
|
|
3478
|
+
err2("Usage: ceki profile export|import <sid> [args]", "args");
|
|
2639
3479
|
process.exit(1);
|
|
2640
3480
|
}
|
|
2641
3481
|
await cmdProfile(rest[1], rest[0], rest.slice(2));
|
|
2642
3482
|
break;
|
|
2643
3483
|
default:
|
|
2644
|
-
|
|
3484
|
+
err2(`Unknown command: ${command}`, "args");
|
|
2645
3485
|
process.exit(1);
|
|
2646
3486
|
}
|
|
2647
3487
|
}
|
|
2648
3488
|
main().catch((e) => {
|
|
2649
3489
|
if (e instanceof SessionExpired || e instanceof SessionNotFound) {
|
|
2650
|
-
|
|
3490
|
+
err2(String(e), "session_not_found");
|
|
2651
3491
|
process.exit(3);
|
|
2652
3492
|
}
|
|
2653
3493
|
if (e instanceof NotOwner) {
|
|
2654
|
-
|
|
3494
|
+
err2(String(e), "not_owner");
|
|
2655
3495
|
process.exit(3);
|
|
2656
3496
|
}
|
|
2657
3497
|
if (e instanceof TimeoutError) {
|
|
2658
|
-
|
|
3498
|
+
err2(String(e), "timeout");
|
|
2659
3499
|
process.exit(4);
|
|
2660
3500
|
}
|
|
2661
3501
|
if (e instanceof ConnectionLost || e instanceof AuthError || e instanceof TransportError) {
|
|
2662
|
-
|
|
3502
|
+
err2(String(e), "network");
|
|
2663
3503
|
process.exit(5);
|
|
2664
3504
|
}
|
|
2665
3505
|
if (e instanceof CekiBrowserError) {
|
|
2666
|
-
|
|
3506
|
+
err2(String(e), "ceki_error");
|
|
2667
3507
|
process.exit(1);
|
|
2668
3508
|
}
|
|
2669
|
-
|
|
3509
|
+
err2(e instanceof Error ? e.message : String(e), "error");
|
|
2670
3510
|
process.exit(1);
|
|
2671
3511
|
});
|
|
2672
3512
|
//# sourceMappingURL=cli.js.map
|