ticlawk 0.1.17-dev.2 → 0.1.17-dev.21

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.
Files changed (47) hide show
  1. package/README.md +26 -59
  2. package/bin/ticlawk.mjs +31 -301
  3. package/package.json +4 -2
  4. package/scripts/publish-dev.sh +77 -0
  5. package/src/adapters/ticlawk/api.mjs +50 -378
  6. package/src/adapters/ticlawk/credentials.mjs +1 -43
  7. package/src/adapters/ticlawk/index.mjs +61 -565
  8. package/src/adapters/ticlawk/wake-client.mjs +1 -1
  9. package/src/cli/agent-commands.mjs +18 -715
  10. package/src/core/adapter-registry.mjs +1 -19
  11. package/src/core/agent-cli-handlers.mjs +18 -556
  12. package/src/core/agent-home.mjs +1 -81
  13. package/src/core/daemon-install.mjs +5 -7
  14. package/src/core/events/worker-events.mjs +36 -32
  15. package/src/core/http.mjs +0 -152
  16. package/src/core/profiles.mjs +0 -1
  17. package/src/core/runtime-contract.mjs +1 -0
  18. package/src/core/runtime-env.mjs +0 -8
  19. package/src/core/runtime-support.mjs +78 -130
  20. package/src/runtimes/_shared/incoming-message-prompt.mjs +232 -0
  21. package/src/runtimes/_shared/runtime-base-instructions.mjs +34 -0
  22. package/src/runtimes/claude-code/index.mjs +48 -21
  23. package/src/runtimes/claude-code/session.mjs +7 -2
  24. package/src/runtimes/codex/index.mjs +64 -116
  25. package/src/runtimes/codex/session.mjs +12 -2
  26. package/src/runtimes/openclaw/index.mjs +30 -17
  27. package/src/runtimes/opencode/index.mjs +64 -42
  28. package/src/runtimes/opencode/session.mjs +14 -14
  29. package/src/runtimes/pi/index.mjs +64 -42
  30. package/src/runtimes/pi/session.mjs +8 -11
  31. package/ticlawk.mjs +32 -5
  32. package/src/runtimes/_shared/agent-handbook.mjs +0 -45
  33. package/src/runtimes/_shared/brand.mjs +0 -2
  34. package/src/runtimes/_shared/goal-step-prompt.mjs +0 -98
  35. package/src/runtimes/_shared/goal-task-protocol.mjs +0 -50
  36. package/src/runtimes/_shared/handbook/BASICS.md +0 -27
  37. package/src/runtimes/_shared/handbook/COLLABORATION.md +0 -37
  38. package/src/runtimes/_shared/handbook/COMMUNICATION.md +0 -55
  39. package/src/runtimes/_shared/handbook/DM_SCOPE.md +0 -13
  40. package/src/runtimes/_shared/handbook/GOAL_AUTHORITY.md +0 -47
  41. package/src/runtimes/_shared/handbook/GOAL_TASK_CORE.md +0 -43
  42. package/src/runtimes/_shared/handbook/GROUP_ADMIN_SCOPE.md +0 -21
  43. package/src/runtimes/_shared/handbook/GROUP_MEMBER_SCOPE.md +0 -15
  44. package/src/runtimes/_shared/handbook/SURFACES.md +0 -41
  45. package/src/runtimes/_shared/handbook/TASK_WORKER.md +0 -14
  46. package/src/runtimes/_shared/standing-prompt.mjs +0 -171
  47. package/src/runtimes/_shared/wake-prompt.mjs +0 -268
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "$ROOT_DIR"
6
+
7
+ EXPECTED_VERSION=""
8
+ DRY_RUN=false
9
+
10
+ while [[ $# -gt 0 ]]; do
11
+ case "$1" in
12
+ --expected-version)
13
+ EXPECTED_VERSION="${2:-}"
14
+ if [[ -z "$EXPECTED_VERSION" ]]; then
15
+ echo "--expected-version requires a value" >&2
16
+ exit 2
17
+ fi
18
+ shift 2
19
+ ;;
20
+ --dry-run)
21
+ DRY_RUN=true
22
+ shift
23
+ ;;
24
+ *)
25
+ echo "unknown argument: $1" >&2
26
+ echo "usage: npm run publish:dev -- [--expected-version X.Y.Z-dev.N] [--dry-run]" >&2
27
+ exit 2
28
+ ;;
29
+ esac
30
+ done
31
+
32
+ VERSION="$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version")"
33
+
34
+ if [[ -n "$EXPECTED_VERSION" && "$EXPECTED_VERSION" != "$VERSION" ]]; then
35
+ echo "expected version ($EXPECTED_VERSION) does not match cli/package.json ($VERSION)" >&2
36
+ exit 1
37
+ fi
38
+
39
+ if [[ "$VERSION" != *-* ]]; then
40
+ echo "refusing to publish stable version $VERSION with the dev script" >&2
41
+ exit 1
42
+ fi
43
+
44
+ NPM_ARGS=(--registry https://registry.npmjs.org/)
45
+ TMP_NPMRC=""
46
+ if [[ -n "${NPM_TOKEN:-}" ]]; then
47
+ TMP_NPMRC="$(mktemp)"
48
+ chmod 0600 "$TMP_NPMRC"
49
+ printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_TOKEN" > "$TMP_NPMRC"
50
+ NPM_ARGS+=(--userconfig "$TMP_NPMRC")
51
+ fi
52
+
53
+ cleanup() {
54
+ if [[ -n "$TMP_NPMRC" ]]; then
55
+ rm -f "$TMP_NPMRC"
56
+ fi
57
+ }
58
+ trap cleanup EXIT
59
+
60
+ if npm "${NPM_ARGS[@]}" view "ticlawk@$VERSION" version >/dev/null 2>&1; then
61
+ echo "ticlawk@$VERSION is already published" >&2
62
+ exit 1
63
+ fi
64
+
65
+ if ! npm "${NPM_ARGS[@]}" whoami >/dev/null; then
66
+ echo "npm auth failed; run npm login or set a valid NPM_TOKEN with publish access to ticlawk" >&2
67
+ exit 1
68
+ fi
69
+
70
+ npm run verify:public-package
71
+
72
+ PUBLISH_ARGS=(publish --access public --tag dev)
73
+ if [[ "$DRY_RUN" == "true" ]]; then
74
+ PUBLISH_ARGS+=(--dry-run)
75
+ fi
76
+
77
+ npm "${NPM_ARGS[@]}" "${PUBLISH_ARGS[@]}"
@@ -30,9 +30,8 @@ export function getConnectorWsUrl() {
30
30
  return DEFAULT_CONNECTOR_WS_URL;
31
31
  }
32
32
 
33
- // Non-terminal event writes are serialized per agent for stable logs.
34
- // Terminal turn events bypass this queue so telemetry cannot hold up the
35
- // delivery lifecycle.
33
+ // Agent event writes must preserve per-agent order while still allowing
34
+ // different agents to proceed concurrently.
36
35
  const agentEventQueues = new Map(); // agentId -> Promise
37
36
 
38
37
  export class TiclawkUpdateRequiredError extends Error {
@@ -152,13 +151,13 @@ export async function updateChannel(id, updates) {
152
151
 
153
152
  // ── Deliveries (replaces legacy message_jobs poll/ack) ──
154
153
 
155
- export async function claimPendingDeliveries(hostId, limit = 5, excludedChannels = []) {
154
+ export async function claimPendingDeliveries(hostId, limit = 5, excludedAgentIds = []) {
156
155
  const { data } = await apiFetch('/api/agent/deliveries/claim-pending', {
157
156
  method: 'POST',
158
157
  body: JSON.stringify(withTiclawkVersion({
159
158
  runtime_host_id: hostId,
160
159
  limit,
161
- excluded_channels: excludedChannels,
160
+ excluded_agent_ids: excludedAgentIds,
162
161
  })),
163
162
  });
164
163
  return data || [];
@@ -189,7 +188,6 @@ export async function sendAgentMessage({
189
188
  runtimeHostId,
190
189
  visibility,
191
190
  mediaAssetIds,
192
- metadata,
193
191
  }) {
194
192
  const { data } = await apiFetch('/api/agent/messages/send', {
195
193
  method: 'POST',
@@ -202,7 +200,6 @@ export async function sendAgentMessage({
202
200
  runtime_host_id: runtimeHostId ?? null,
203
201
  visibility: visibility || null,
204
202
  media_asset_ids: Array.isArray(mediaAssetIds) && mediaAssetIds.length > 0 ? mediaAssetIds : undefined,
205
- metadata: metadata ?? undefined,
206
203
  }),
207
204
  });
208
205
  return data || null;
@@ -225,7 +222,7 @@ export async function readAgentMessages({
225
222
  return data || [];
226
223
  }
227
224
 
228
- export async function createAgentTask({ actingAgentId, conversationId, text, title, assignAgentId }) {
225
+ export async function createAgentTask({ actingAgentId, conversationId, text, title }) {
229
226
  return apiFetch('/api/agent/tasks/create', {
230
227
  method: 'POST',
231
228
  body: JSON.stringify({
@@ -233,7 +230,6 @@ export async function createAgentTask({ actingAgentId, conversationId, text, tit
233
230
  conversation_id: conversationId,
234
231
  text,
235
232
  title: title ?? null,
236
- assign_agent_id: assignAgentId ?? null,
237
233
  }),
238
234
  });
239
235
  }
@@ -286,71 +282,6 @@ export async function updateAgentTask({ actingAgentId, taskId, status }) {
286
282
  });
287
283
  }
288
284
 
289
- export async function reportGoalTransition({
290
- actingAgentId, conversationId, transitionId, outcome, detail, currentTaskId,
291
- }) {
292
- return apiFetch('/api/agent/goal/report', {
293
- method: 'POST',
294
- body: JSON.stringify({
295
- acting_as_agent_id: actingAgentId,
296
- conversation_id: conversationId,
297
- transition_id: transitionId,
298
- outcome,
299
- detail: detail ?? null,
300
- current_task_id: currentTaskId ?? null,
301
- }),
302
- });
303
- }
304
-
305
- export async function noteGoalChanged({ actingAgentId, conversationId }) {
306
- return apiFetch('/api/agent/goal/changed', {
307
- method: 'POST',
308
- body: JSON.stringify({
309
- acting_as_agent_id: actingAgentId,
310
- conversation_id: conversationId,
311
- }),
312
- });
313
- }
314
-
315
- export async function requestGoalApproval({
316
- actingAgentId, conversationId, title, detail, ttlSeconds,
317
- }) {
318
- return apiFetch('/api/agent/approval/request', {
319
- method: 'POST',
320
- body: JSON.stringify({
321
- acting_as_agent_id: actingAgentId,
322
- conversation_id: conversationId,
323
- title,
324
- detail: detail ?? null,
325
- ttl_seconds: ttlSeconds ?? null,
326
- }),
327
- });
328
- }
329
-
330
- export async function listGoalApprovals({ actingAgentId, conversationId }) {
331
- const params = new URLSearchParams();
332
- params.set('acting_as_agent_id', actingAgentId);
333
- params.set('conversation_id', conversationId);
334
- const { data } = await apiFetch(`/api/agent/approval/list?${params}`);
335
- return data || [];
336
- }
337
-
338
- export async function resolveGoalApproval({
339
- actingAgentId, requestId, decision, originalText, confidence, sourceMessageId,
340
- }) {
341
- return apiFetch('/api/agent/approval/resolve', {
342
- method: 'POST',
343
- body: JSON.stringify({
344
- acting_as_agent_id: actingAgentId,
345
- request_id: requestId,
346
- decision,
347
- original_text: originalText ?? null,
348
- confidence: confidence ?? null,
349
- source_message_id: sourceMessageId ?? null,
350
- }),
351
- });
352
- }
353
-
354
285
  export async function listAgentTasks({ actingAgentId, conversationId }) {
355
286
  const params = new URLSearchParams();
356
287
  params.set('acting_as_agent_id', actingAgentId);
@@ -553,294 +484,63 @@ export async function removeAgentGroupMember({
553
484
  );
554
485
  }
555
486
 
556
- // ── Workstreams (managed groups) ──
557
-
558
- export async function createWorkstream({
559
- actingAgentId, name, description, charter, memberAgentIds,
560
- }) {
561
- return apiFetch('/api/agent/workstreams', {
562
- method: 'POST',
563
- body: JSON.stringify({
564
- acting_as_agent_id: actingAgentId,
565
- name,
566
- description: description ?? null,
567
- charter: charter ?? null,
568
- member_agent_ids: memberAgentIds || [],
569
- }),
570
- });
571
- }
572
-
573
- export async function deleteWorkstream({ actingAgentId, conversationId }) {
574
- return apiFetch(
575
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}`,
576
- {
577
- method: 'DELETE',
578
- body: JSON.stringify({ acting_as_agent_id: actingAgentId }),
579
- },
580
- );
581
- }
582
-
583
- export async function listWorkstreams({ actingAgentId }) {
584
- const params = new URLSearchParams();
585
- params.set('acting_as_agent_id', actingAgentId);
586
- const { data } = await apiFetch(`/api/agent/workstreams?${params}`);
587
- return data || [];
588
- }
589
-
590
- // ── Agents ──
591
-
592
- export async function listAgentSlots({ actingAgentId }) {
593
- const params = new URLSearchParams();
594
- params.set('acting_as_agent_id', actingAgentId);
595
- const { data } = await apiFetch(`/api/agent/agents?${params}`);
596
- return data || [];
597
- }
598
-
599
- export async function createAgentSlot({
600
- actingAgentId, name, runtime, description, displayName, model,
601
- }) {
602
- return apiFetch('/api/agent/agents', {
603
- method: 'POST',
604
- body: JSON.stringify({
605
- acting_as_agent_id: actingAgentId,
606
- name,
607
- runtime,
608
- description: description ?? null,
609
- display_name: displayName ?? null,
610
- model: model ?? null,
611
- }),
612
- });
613
- }
614
-
615
- export async function archiveAgentSlot({ actingAgentId, agentId }) {
616
- return apiFetch(
617
- `/api/agent/agents/${encodeURIComponent(agentId)}`,
618
- {
619
- method: 'DELETE',
620
- body: JSON.stringify({ acting_as_agent_id: actingAgentId }),
621
- },
622
- );
623
- }
624
-
625
- // ── Services ──
626
-
627
- export async function createService({
628
- actingAgentId, name, description, contractSchema, endpointConfig,
629
- }) {
630
- return apiFetch('/api/agent/services', {
631
- method: 'POST',
632
- body: JSON.stringify({
633
- acting_as_agent_id: actingAgentId,
634
- name,
635
- description: description ?? null,
636
- contract_schema: contractSchema ?? null,
637
- endpoint_config: endpointConfig,
638
- }),
639
- });
640
- }
641
-
642
- export async function updateService({ actingAgentId, serviceId, ...patch }) {
643
- return apiFetch(
644
- `/api/agent/services/${encodeURIComponent(serviceId)}`,
645
- {
646
- method: 'PATCH',
647
- body: JSON.stringify({ acting_as_agent_id: actingAgentId, ...patch }),
648
- },
649
- );
650
- }
651
-
652
- export async function deleteService({ actingAgentId, serviceId }) {
653
- return apiFetch(
654
- `/api/agent/services/${encodeURIComponent(serviceId)}`,
655
- {
656
- method: 'DELETE',
657
- body: JSON.stringify({ acting_as_agent_id: actingAgentId }),
658
- },
659
- );
660
- }
661
-
662
- export async function listServices({ actingAgentId }) {
663
- const params = new URLSearchParams();
664
- params.set('acting_as_agent_id', actingAgentId);
665
- const { data } = await apiFetch(`/api/agent/services?${params}`);
666
- return data || [];
667
- }
668
-
669
- export async function getServiceInfo({ actingAgentId, name }) {
670
- const params = new URLSearchParams();
671
- params.set('acting_as_agent_id', actingAgentId);
672
- return apiFetch(
673
- `/api/agent/services/${encodeURIComponent(name)}/info?${params}`,
674
- );
675
- }
676
-
677
- export async function callService({ actingAgentId, name, input }) {
678
- return apiFetch(
679
- `/api/agent/services/${encodeURIComponent(name)}/call`,
680
- {
681
- method: 'POST',
682
- body: JSON.stringify({ acting_as_agent_id: actingAgentId, input }),
683
- },
684
- );
685
- }
686
-
687
- // ── Briefings ──
688
-
689
- export async function getBriefing({actingAgentId, briefingId}) {
690
- const params = new URLSearchParams();
691
- params.set('acting_as_agent_id', actingAgentId);
692
- return apiFetch(`/api/agent/briefings/${encodeURIComponent(briefingId)}?${params}`);
693
- }
694
-
695
- export async function publishBriefing({actingAgentId, bodyText, attachmentAssetId, currentConversationId, responseMode}) {
696
- const body = { acting_as_agent_id: actingAgentId };
697
- if (bodyText != null) body.body_text = bodyText;
698
- if (attachmentAssetId != null) body.attachment_asset_id = attachmentAssetId;
699
- if (currentConversationId != null) body.current_conversation_id = currentConversationId;
700
- if (responseMode != null) body.response_mode = responseMode;
701
- return apiFetch('/api/agent/briefings', {
702
- method: 'POST',
703
- body: JSON.stringify(body),
704
- });
705
- }
706
-
707
- // ── Credentials (slot creation + daemon sync) ──
708
-
709
- export async function fetchCredentials() {
710
- return apiFetch('/api/agent/credentials', { method: 'GET' });
711
- }
712
-
713
- export async function requestCredential({
714
- actingAgentId, name, description, workstreamId,
715
- }) {
716
- return apiFetch('/api/agent/credentials', {
717
- method: 'POST',
718
- body: JSON.stringify({
719
- acting_as_agent_id: actingAgentId,
720
- name,
721
- description: description ?? null,
722
- workstream_id: workstreamId ?? null,
723
- }),
724
- });
725
- }
726
-
727
- // ── Workstream dashboard ──
728
-
729
- export async function setWorkstreamDashboard({
730
- actingAgentId, conversationId, dataJson, htmlTemplate,
731
- }) {
732
- const body = { acting_as_agent_id: actingAgentId };
733
- // Distinguish "omit" from "set to null" — only include keys the caller
734
- // explicitly passed (including null clears the field).
735
- if (dataJson !== undefined) body.data_json = dataJson;
736
- if (htmlTemplate !== undefined) body.html_template = htmlTemplate;
737
- return apiFetch(
738
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}/dashboard`,
739
- { method: 'POST', body: JSON.stringify(body) },
740
- );
741
- }
742
-
743
- export async function getWorkstreamDashboard({ actingAgentId, conversationId }) {
744
- const params = new URLSearchParams();
745
- params.set('acting_as_agent_id', actingAgentId);
746
- return apiFetch(
747
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}/dashboard?${params}`,
748
- );
749
- }
750
-
751
- // ── Workstream charter ──
752
-
753
- export async function getWorkstreamCharter({ actingAgentId, conversationId }) {
754
- const params = new URLSearchParams();
755
- params.set('acting_as_agent_id', actingAgentId);
756
- return apiFetch(
757
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}/charter?${params}`,
758
- );
759
- }
760
-
761
- export async function setWorkstreamCharter({ actingAgentId, conversationId, charter }) {
762
- return apiFetch(
763
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}/charter`,
764
- {
765
- method: 'POST',
766
- body: JSON.stringify({
767
- acting_as_agent_id: actingAgentId,
768
- charter: charter ?? null,
769
- }),
770
- },
771
- );
772
- }
773
-
774
487
  // ── Channel event pipe ──
775
488
 
776
489
  export async function postEvent({ agent, agent_id, runtime_host_id, session_id, cwd, runtime_version, event, required = false }) {
777
490
  const agentId = agent_id || null;
778
491
  const queueKey = agentId || `${agent}:${session_id || ''}`;
492
+ const previous = agentEventQueues.get(queueKey) || Promise.resolve(null);
779
493
  const eventName = event?.worker_event_name || event?.hook_event_name || event?.event_name || 'unknown';
780
494
  const turnId = event?.turn_id || event?.reply_to_message_id || null;
781
495
  const seq = event?.event_seq ?? null;
782
496
  const deltaChars = typeof event?.delta === 'string' ? event.delta.length : null;
783
497
 
784
- if (eventName === 'worker.message.delta') {
785
- debugLog('events', 'delta.drop', {
786
- agent,
787
- agentId,
788
- sessionId: shortId(session_id),
789
- turnId: shortId(turnId),
790
- deltaChars,
791
- });
792
- return null;
793
- }
794
-
795
- const postOnce = async () => {
796
- const startedAt = Date.now();
797
- try {
798
- const result = await apiFetch('/api/events', {
799
- method: 'POST',
800
- body: JSON.stringify({ agent, agent_id: agentId, runtime_host_id, session_id, cwd, runtime_version, event }),
801
- timeout: 5000,
802
- });
803
- if (required && result?.matched === false) {
804
- throw new Error(`event was not matched (${eventName})`);
805
- }
806
- debugLog('events', 'post.ok', {
807
- agent,
808
- agentId,
809
- sessionId: shortId(session_id),
810
- runtimeVersion: runtime_version ?? null,
811
- turnId: shortId(turnId),
812
- eventName,
813
- seq,
814
- durationMs: Date.now() - startedAt,
815
- });
816
- return result;
817
- } catch (err) {
818
- debugError('events', 'post.failed', {
819
- agent,
820
- agentId,
821
- sessionId: shortId(session_id),
822
- runtimeVersion: runtime_version ?? null,
823
- turnId: shortId(turnId),
824
- eventName,
825
- seq,
826
- durationMs: Date.now() - startedAt,
827
- error: err?.message || 'unknown error',
828
- });
829
- if (required) {
830
- throw err;
831
- }
832
- return null;
833
- }
834
- };
835
-
836
- if (eventName === 'worker.turn.complete' || eventName === 'worker.turn.error') {
837
- return postOnce();
838
- }
839
-
840
- const previous = agentEventQueues.get(queueKey) || Promise.resolve(null);
841
498
  const queued = previous
842
499
  .catch(() => null)
843
- .then(postOnce);
500
+ .then(async () => {
501
+ const startedAt = Date.now();
502
+ try {
503
+ const result = await apiFetch('/api/events', {
504
+ method: 'POST',
505
+ body: JSON.stringify({ agent, agent_id: agentId, runtime_host_id, session_id, cwd, runtime_version, event }),
506
+ timeout: 5000,
507
+ });
508
+ if (required && result?.matched === false) {
509
+ throw new Error(`event was not matched (${eventName})`);
510
+ }
511
+ debugLog('events', 'post.ok', {
512
+ agent,
513
+ agentId,
514
+ sessionId: shortId(session_id),
515
+ runtimeVersion: runtime_version ?? null,
516
+ turnId: shortId(turnId),
517
+ eventName,
518
+ seq,
519
+ deltaChars,
520
+ durationMs: Date.now() - startedAt,
521
+ });
522
+ return result;
523
+ } catch (err) {
524
+ debugError('events', 'post.failed', {
525
+ agent,
526
+ agentId,
527
+ sessionId: shortId(session_id),
528
+ runtimeVersion: runtime_version ?? null,
529
+ turnId: shortId(turnId),
530
+ eventName,
531
+ seq,
532
+ deltaChars,
533
+ durationMs: Date.now() - startedAt,
534
+ error: err?.message || 'unknown error',
535
+ });
536
+ if (required) {
537
+ throw err;
538
+ }
539
+ // Best-effort by default. Callers that need a hard failure set
540
+ // `required: true` (used for the final reply path).
541
+ return null;
542
+ }
543
+ });
844
544
 
845
545
  agentEventQueues.set(queueKey, queued);
846
546
  queued.finally(() => {
@@ -852,34 +552,6 @@ export async function postEvent({ agent, agent_id, runtime_host_id, session_id,
852
552
  return queued;
853
553
  }
854
554
 
855
- // ── Pair (no auth needed) ──
856
-
857
- export async function pair(payload) {
858
- const url = `${getApiUrl()}/dispatch`;
859
- const res = await fetch(url, {
860
- method: 'POST',
861
- headers: { 'Content-Type': 'application/json' },
862
- body: JSON.stringify({ action: 'pair', ...payload }),
863
- signal: AbortSignal.timeout(15000),
864
- });
865
- const body = await res.json().catch(() => ({}));
866
- return {
867
- statusCode: res.status,
868
- ...body,
869
- };
870
- }
871
-
872
- export async function pairPreview(payload) {
873
- const url = `${getApiUrl()}/dispatch`;
874
- const res = await fetch(url, {
875
- method: 'POST',
876
- headers: { 'Content-Type': 'application/json' },
877
- body: JSON.stringify({ action: 'pair-preview', ...payload }),
878
- signal: AbortSignal.timeout(15000),
879
- });
880
- return res.json();
881
- }
882
-
883
555
  async function pairingRequest(payload, timeout = 15000) {
884
556
  const url = `${getApiUrl()}/api/agent-pairings`;
885
557
  const res = await fetch(url, {
@@ -6,56 +6,14 @@
6
6
  * using the connector-specific env name.
7
7
  */
8
8
 
9
- import { AF_CONFIG_PATH, loadPersistentConfig, persistConfig, TICLAWK_CONNECTOR_API_KEY } from '../../core/config.mjs';
10
-
11
- export const TICLAWK_CREDENTIAL_NAMES = 'TICLAWK_CREDENTIAL_NAMES';
12
-
13
- function isRuntimeCredentialName(value) {
14
- return /^[A-Z][A-Z0-9_]*$/.test(String(value || '').trim());
15
- }
16
-
17
- function parseCredentialNames(value) {
18
- return String(value || '')
19
- .split(',')
20
- .map((name) => name.trim())
21
- .filter(isRuntimeCredentialName);
22
- }
9
+ import { AF_CONFIG_PATH, persistConfig, TICLAWK_CONNECTOR_API_KEY } from '../../core/config.mjs';
23
10
 
24
11
  export function persistApiCredential(apiKey) {
25
12
  if (!apiKey || !apiKey.startsWith('tk_')) return;
26
13
 
27
14
  persistConfig({
28
15
  [TICLAWK_CONNECTOR_API_KEY]: apiKey,
29
- TICLAWK_SETUP_CODE: '',
30
16
  });
31
17
  process.env[TICLAWK_CONNECTOR_API_KEY] = apiKey;
32
- delete process.env.TICLAWK_SETUP_CODE;
33
18
  console.log(`[connect] saved ${TICLAWK_CONNECTOR_API_KEY} to ${AF_CONFIG_PATH}`);
34
19
  }
35
-
36
- export function persistRuntimeCredentials(credentials = []) {
37
- const current = loadPersistentConfig();
38
- const previousNames = new Set(parseCredentialNames(current[TICLAWK_CREDENTIAL_NAMES]));
39
- const nextNames = [];
40
- const updates = {};
41
-
42
- for (const credential of Array.isArray(credentials) ? credentials : []) {
43
- const name = String(credential?.name || '').trim();
44
- const value = typeof credential?.value === 'string' ? credential.value : '';
45
- if (!isRuntimeCredentialName(name) || !value) continue;
46
- updates[name] = value;
47
- nextNames.push(name);
48
- }
49
-
50
- const nextNameSet = new Set(nextNames);
51
- for (const name of previousNames) {
52
- if (!nextNameSet.has(name)) updates[name] = '';
53
- }
54
- updates[TICLAWK_CREDENTIAL_NAMES] = nextNames.join(',');
55
-
56
- persistConfig(updates);
57
- return {
58
- saved: nextNames.length,
59
- removed: [...previousNames].filter((name) => !nextNameSet.has(name)).length,
60
- };
61
- }