@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/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 (err2) {
476
- if (err2.code !== "ENOENT") throw err2;
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
- async navigate(url, timeout = 3e4) {
658
- if (this._humanizer) await this._humanizer.before("navigate");
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 (this._humanizer) await this._humanizer.after("navigate");
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
- if (this._humanizer) await this._humanizer.before("click");
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 (this._humanizer) await this._humanizer.after("click");
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
- if (this._humanizer) {
725
- await this._humanizer.before("type");
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 = this._humanizer ? ["natural", "careful"].includes(this._humanizer.profile.name) ? this._humanizer.profile.name : "natural" : null;
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 (this._humanizer) {
741
- await this._humanizer.after("type");
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
- if (this._humanizer) await this._humanizer.before("scroll");
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 (this._humanizer) await this._humanizer.after("scroll");
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(err2) {
1192
+ _rejectAllPending(err3) {
1179
1193
  for (const [id, pending] of this._pendingCdp) {
1180
1194
  clearTimeout(pending.timer);
1181
- pending.reject(err2);
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 err2 = new AuthError(reasonStr || `Auth failed (${code})`);
1607
+ const err3 = new AuthError(reasonStr || `Auth failed (${code})`);
1594
1608
  if (this._connectReject) {
1595
- this._connectReject(err2);
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", (err2) => {
1619
+ this._ws.on("error", (err3) => {
1606
1620
  if (this._connectReject) {
1607
- this._connectReject(new TransportError(err2.message));
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 err2 = new ConnectionLost("Max reconnection attempts exceeded");
1627
- this._rejectAllPending(err2);
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 err2;
1932
+ let err3;
1919
1933
  switch (code) {
1920
1934
  case -1012:
1921
- err2 = new InsufficientFunds(reason);
1922
- this._rejectAllPending(err2);
1935
+ err3 = new InsufficientFunds(reason);
1936
+ this._rejectAllPending(err3);
1923
1937
  break;
1924
1938
  case -1013:
1925
- err2 = new RateLimitExceeded(0, reason);
1926
- this._rejectAllPending(err2);
1939
+ err3 = new RateLimitExceeded(0, reason);
1940
+ this._rejectAllPending(err3);
1927
1941
  break;
1928
1942
  case -1015:
1929
- err2 = new ProviderOffline(reason);
1930
- this._rejectFirstPendingRent(err2);
1943
+ err3 = new ProviderOffline(reason);
1944
+ this._rejectFirstPendingRent(err3);
1931
1945
  break;
1932
1946
  default:
1933
- err2 = new CekiBrowserError(reason || `relay error ${code}`);
1934
- this._rejectFirstPendingRent(err2);
1947
+ err3 = new CekiBrowserError(reason || `relay error ${code}`);
1948
+ this._rejectFirstPendingRent(err3);
1935
1949
  break;
1936
1950
  }
1937
1951
  }
1938
- _rejectFirstPendingRent(err2) {
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(err2);
1958
+ pending.reject(err3);
1945
1959
  }
1946
1960
  }
1947
- _rejectAllPending(err2) {
1961
+ _rejectAllPending(err3) {
1948
1962
  for (const [key, pending] of this._pendingRents) {
1949
1963
  clearTimeout(pending.timer);
1950
- pending.reject(err2);
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(err2);
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/cli.ts
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 err(error, code = "error") {
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
- err("CEKI_API_KEY not set", "auth");
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
- err("invalid mode, must be incognito or main", "args");
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
- err("--schedule is required", "args");
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
- out({
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
- out(results);
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
- out(results);
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
- out(results);
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
- err("-o/--output is required", "args");
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
- out({ screenshot: outputPath, chat: chatList, ts: snap.ts.toISOString() });
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[0];
2949
+ const url = args.find((a) => !a.startsWith("--"));
2154
2950
  if (!url) {
2155
- err("URL is required", "args");
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
- out({ ok: true });
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 x = parseInt(args[0], 10);
2169
- const y = parseInt(args[1], 10);
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
- err("x and y coordinates are required", "args");
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
- out({ ok: true, pointer: [x, y] });
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 natural = false;
2984
+ let raw = false;
2186
2985
  for (let i = 0; i < args.length; i++) {
2187
- if (args[i] === "--natural") natural = true;
2188
- else if (!text) text = args[i];
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
- err("text is required", "args");
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, { human });
2199
- if (!natural) {
2200
- browser._humanizer = null;
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 x = parseInt(args[0], 10);
2210
- const y = parseInt(args[1], 10);
2211
- const dy = parseInt(args[2], 10);
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
- err("x, y, dy are required", "args");
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
- await browser.scroll({ x, y, deltaY: dy });
2220
- out({ ok: true });
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
- err("text is required", "args");
3033
+ err2("text is required", "args");
2234
3034
  process.exit(1);
2235
3035
  }
2236
3036
  const result = await browser.chat.send(text);
2237
- out({ ok: true, message_id: result.messageId });
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
- err("--image is required", "args");
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
- out({ ok: true, message_id: result.messageId });
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
- out({ from: m.sender_id, text: m.text, ts: m.created_at });
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
- out({ from: msg.sender_id, text: msg.text, ts: msg.created_at });
3089
+ out2({ from: msg.sender_id, text: msg.text, ts: msg.created_at });
2290
3090
  } else {
2291
- out(null);
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
- out(msgs.map((m) => ({ from: m.sender_id, text: m.text, ts: m.created_at })));
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
- err(`Unknown chat action: ${action}`, "args");
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
- out({ ok: true });
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
- err("-o/--output is required", "args");
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
- out({ ok: true, path: outputPath });
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
- err("-i/--input is required", "args");
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
- out({ ok: true });
3171
+ out2({ ok: true });
2372
3172
  break;
2373
3173
  }
2374
3174
  default:
2375
- err(`Unknown profile action: ${action}`, "args");
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
- out({ ended: true, reason });
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
- err("-o/--output is required", "args");
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
- out({ ok: true, path: outputPath });
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
- out({ ok: true });
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
- out({ ok: true });
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
- err("--method is required", "args");
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
- out(result);
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
- out({
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
- out({ solved: false, cancel_reason: `timeout:${e.phase}`, child_event_id: null, correction_id: null });
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
- err("--selector and --file are required", "args");
3305
+ err2("--selector and --file are required", "args");
2506
3306
  process.exit(1);
2507
3307
  }
2508
3308
  if (!fs4.existsSync(filePath)) {
2509
- err(`File not found: ${filePath}`, "args");
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
- out(result);
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>" [--natural]
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
- err("Usage: ceki chat <sid> <action> [args]", "args");
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
- err("Usage: ceki profile export|import <sid> [args]", "args");
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
- err(`Unknown command: ${command}`, "args");
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
- err(String(e), "session_not_found");
3490
+ err2(String(e), "session_not_found");
2651
3491
  process.exit(3);
2652
3492
  }
2653
3493
  if (e instanceof NotOwner) {
2654
- err(String(e), "not_owner");
3494
+ err2(String(e), "not_owner");
2655
3495
  process.exit(3);
2656
3496
  }
2657
3497
  if (e instanceof TimeoutError) {
2658
- err(String(e), "timeout");
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
- err(String(e), "network");
3502
+ err2(String(e), "network");
2663
3503
  process.exit(5);
2664
3504
  }
2665
3505
  if (e instanceof CekiBrowserError) {
2666
- err(String(e), "ceki_error");
3506
+ err2(String(e), "ceki_error");
2667
3507
  process.exit(1);
2668
3508
  }
2669
- err(e instanceof Error ? e.message : String(e), "error");
3509
+ err2(e instanceof Error ? e.message : String(e), "error");
2670
3510
  process.exit(1);
2671
3511
  });
2672
3512
  //# sourceMappingURL=cli.js.map