ticlawk 0.1.15-dev.6 → 0.1.16-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,8 @@
9
9
 
10
10
  import { existsSync } from 'node:fs';
11
11
  import { basename } from 'node:path';
12
+ import { buildAgentRuntimeEnv } from '../../core/runtime-env.mjs';
13
+ import { buildStandingPrompt } from '../_shared/standing-prompt.mjs';
12
14
  import {
13
15
  createOpenCodeSession,
14
16
  getOpenCodeRuntimeHealth,
@@ -27,7 +29,7 @@ import {
27
29
  shouldStreamRuntime,
28
30
  createDeltaAggregator,
29
31
  sendAdapterMessage,
30
- sendResult,
32
+ recordActivity,
31
33
  reportSubprocessFailure,
32
34
  terminalRuntimeFailure,
33
35
  updateBindingRuntimeMeta,
@@ -40,23 +42,27 @@ export const openCodeRuntime = {
40
42
  return createOpenCodeSession({ cwd, message: text, opencodePath });
41
43
  },
42
44
 
43
- runTurn({ sessionId, cwd, opencodePath }, text, opts = {}) {
45
+ runTurn({ sessionId, cwd, opencodePath, agentEnv }, text, opts = {}) {
44
46
  return runOpenCodePrompt({
45
47
  sessionId,
46
48
  cwd,
47
49
  message: text,
48
50
  opencodePath,
51
+ agentEnv,
52
+ standingPrompt: opts.standingPrompt || null,
49
53
  files: opts.files,
50
54
  timeoutMs: opts.timeoutMs,
51
55
  });
52
56
  },
53
57
 
54
- runTurnStream({ sessionId, cwd, opencodePath }, text, opts = {}) {
58
+ runTurnStream({ sessionId, cwd, opencodePath, agentEnv }, text, opts = {}) {
55
59
  return streamOpenCodePrompt({
56
60
  sessionId,
57
61
  cwd,
58
62
  message: text,
59
63
  opencodePath,
64
+ agentEnv,
65
+ standingPrompt: opts.standingPrompt || null,
60
66
  files: opts.files,
61
67
  timeoutMs: opts.timeoutMs,
62
68
  onEvent: opts.onEvent,
@@ -204,8 +210,15 @@ export const openCodeRuntime = {
204
210
  try {
205
211
  const opencodePath = requireOpenCodePath(runtimeOpenCodePath);
206
212
  const opencodeVersion = getOpenCodeRuntimeHealth(opencodePath).version || meta.opencodeVersion || null;
213
+ const agentEnv = buildAgentRuntimeEnv({
214
+ agentId: binding.id,
215
+ sessionId: meta.sessionId,
216
+ hostId: binding.runtime_host_id,
217
+ });
218
+ const standingPrompt = buildStandingPrompt({ agentId: binding.id });
207
219
  const result = shouldStreamRuntime(this.name, this)
208
- ? await this.runTurnStream({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, opencodePath }, message, {
220
+ ? await this.runTurnStream({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, opencodePath, agentEnv }, message, {
221
+ standingPrompt,
209
222
  files,
210
223
  onEvent: async (event) => {
211
224
  if (event?.type === 'turn.started') {
@@ -230,7 +243,7 @@ export const openCodeRuntime = {
230
243
  }
231
244
  },
232
245
  })
233
- : await this.runTurn({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, opencodePath }, message, { files });
246
+ : await this.runTurn({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, opencodePath, agentEnv }, message, { files, standingPrompt });
234
247
 
235
248
  await deltaAggregator.flush();
236
249
 
@@ -243,7 +256,7 @@ export const openCodeRuntime = {
243
256
  rotatePending: false,
244
257
  lastRotatedAt: shouldRotate ? new Date().toISOString() : meta.lastRotatedAt,
245
258
  }, { status: 'connected' });
246
- await sendResult(adapter, nextBinding, inbound, {
259
+ await recordActivity(adapter, nextBinding, inbound, {
247
260
  ...result,
248
261
  media: normalizeOutboundMedia(result),
249
262
  });
@@ -208,15 +208,24 @@ export function runOpenCodePrompt({
208
208
  message,
209
209
  files = [],
210
210
  opencodePath = null,
211
+ agentEnv = null,
212
+ standingPrompt = null,
211
213
  timeoutMs = Number(process.env.OPENCODE_RUN_TIMEOUT_MS || DEFAULT_OPENCODE_RUN_TIMEOUT_MS),
212
214
  onEvent,
213
215
  }) {
216
+ // opencode has no documented `--system` flag, so we prepend the
217
+ // standing prompt to the first-turn message body. Resumed sessions
218
+ // (sessionId set) skip injection because the model already saw it
219
+ // on the originating turn.
220
+ const finalMessage = standingPrompt && !sessionId
221
+ ? `${standingPrompt}\n\n---\n\n${message}`
222
+ : message;
214
223
  return new Promise((resolve, reject) => {
215
224
  const startedAt = Date.now();
216
225
  const opencodeCommand = requireOpenCodePath(opencodePath);
217
- const child = spawn(opencodeCommand, buildOpenCodeRunArgs({ sessionId, message, files }), {
226
+ const child = spawn(opencodeCommand, buildOpenCodeRunArgs({ sessionId, message: finalMessage, files }), {
218
227
  cwd,
219
- env: buildRuntimeEnv(),
228
+ env: buildRuntimeEnv(agentEnv || {}),
220
229
  stdio: ['ignore', 'pipe', 'ignore'],
221
230
  });
222
231
 
@@ -6,6 +6,8 @@
6
6
 
7
7
  import { existsSync } from 'node:fs';
8
8
  import { basename } from 'node:path';
9
+ import { buildAgentRuntimeEnv } from '../../core/runtime-env.mjs';
10
+ import { buildStandingPrompt } from '../_shared/standing-prompt.mjs';
9
11
  import {
10
12
  buildPiImagesFromInbound,
11
13
  discoverPiSessions,
@@ -21,7 +23,7 @@ import {
21
23
  shouldStreamRuntime,
22
24
  createDeltaAggregator,
23
25
  sendAdapterMessage,
24
- sendResult,
26
+ recordActivity,
25
27
  reportSubprocessFailure,
26
28
  terminalRuntimeFailure,
27
29
  updateBindingRuntimeMeta,
@@ -30,24 +32,28 @@ import {
30
32
  export const piRuntime = {
31
33
  name: 'pi',
32
34
 
33
- runTurn({ sessionId, cwd, piPath }, text, opts = {}) {
35
+ runTurn({ sessionId, cwd, piPath, agentEnv }, text, opts = {}) {
34
36
  return runPiPrompt({
35
37
  sessionId,
36
38
  cwd,
37
39
  message: text,
38
40
  images: opts.images,
39
41
  piPath,
42
+ agentEnv,
43
+ standingPrompt: opts.standingPrompt || null,
40
44
  timeoutMs: opts.timeoutMs,
41
45
  });
42
46
  },
43
47
 
44
- runTurnStream({ sessionId, cwd, piPath }, text, opts = {}) {
48
+ runTurnStream({ sessionId, cwd, piPath, agentEnv }, text, opts = {}) {
45
49
  return runPiPrompt({
46
50
  sessionId,
47
51
  cwd,
48
52
  message: text,
49
53
  images: opts.images,
50
54
  piPath,
55
+ agentEnv,
56
+ standingPrompt: opts.standingPrompt || null,
51
57
  timeoutMs: opts.timeoutMs,
52
58
  onEvent: opts.onEvent,
53
59
  });
@@ -176,8 +182,15 @@ export const piRuntime = {
176
182
  try {
177
183
  const runtimePiPath = requirePiPath(meta.piPath || meta.runtimePath);
178
184
  const runtimePiVersion = getPiRuntimeHealth(runtimePiPath).version || meta.piVersion || null;
185
+ const agentEnv = buildAgentRuntimeEnv({
186
+ agentId: binding.id,
187
+ sessionId: meta.sessionId,
188
+ hostId: binding.runtime_host_id,
189
+ });
190
+ const standingPrompt = buildStandingPrompt({ agentId: binding.id });
179
191
  const result = shouldStreamRuntime(this.name, this)
180
- ? await this.runTurnStream({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, piPath: runtimePiPath }, message, {
192
+ ? await this.runTurnStream({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, piPath: runtimePiPath, agentEnv }, message, {
193
+ standingPrompt,
181
194
  images,
182
195
  onEvent: async (event) => {
183
196
  if (event?.type === 'turn.started') {
@@ -202,7 +215,7 @@ export const piRuntime = {
202
215
  }
203
216
  },
204
217
  })
205
- : await this.runTurn({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, piPath: runtimePiPath }, message, { images });
218
+ : await this.runTurn({ sessionId: shouldRotate ? null : meta.sessionId, cwd: meta.cwd, piPath: runtimePiPath, agentEnv }, message, { images, standingPrompt });
206
219
 
207
220
  await deltaAggregator.flush();
208
221
  const nextBinding = await updateBindingRuntimeMeta(ctx, binding, {
@@ -215,7 +228,7 @@ export const piRuntime = {
215
228
  rotatePending: false,
216
229
  lastRotatedAt: shouldRotate ? new Date().toISOString() : meta.lastRotatedAt,
217
230
  }, { status: 'connected' });
218
- await sendResult(adapter, nextBinding, inbound, result);
231
+ await recordActivity(adapter, nextBinding, inbound, result);
219
232
  await emitWorkerEvent({
220
233
  adapter,
221
234
  binding: nextBinding,
@@ -195,15 +195,21 @@ export function runPiPrompt({
195
195
  message,
196
196
  images = [],
197
197
  piPath = null,
198
+ agentEnv = null,
199
+ standingPrompt = null,
198
200
  timeoutMs = Number(process.env.PI_RUN_TIMEOUT_MS || DEFAULT_PI_RUN_TIMEOUT_MS),
199
201
  onEvent,
200
202
  }) {
203
+ // pi has no documented system-prompt flag — prepend on first turn.
204
+ const finalMessage = standingPrompt && !sessionId
205
+ ? `${standingPrompt}\n\n---\n\n${message}`
206
+ : message;
201
207
  return new Promise((resolve, reject) => {
202
208
  const startedAt = Date.now();
203
209
  const piCommand = requirePiPath(piPath);
204
210
  const child = spawn(piCommand, buildPiRpcArgs({ sessionId }), {
205
211
  cwd,
206
- env: buildRuntimeEnv(),
212
+ env: buildRuntimeEnv(agentEnv || {}),
207
213
  stdio: ['pipe', 'pipe', 'pipe'],
208
214
  });
209
215
 
@@ -349,7 +355,7 @@ export function runPiPrompt({
349
355
 
350
356
  (async () => {
351
357
  try {
352
- await send({ type: 'prompt', message, images });
358
+ await send({ type: 'prompt', message: finalMessage, images });
353
359
  await completion;
354
360
  const state = await send({ type: 'get_state' }).catch(() => null);
355
361
  const lastAssistant = await send({ type: 'get_last_assistant_text' }).catch(() => null);
package/ticlawk.mjs CHANGED
@@ -17,7 +17,6 @@ import {
17
17
  AF_CONFIG_PATH,
18
18
  AF_LOG_PATH,
19
19
  AF_CRASH_LOG_PATH,
20
- getConfiguredAdapter,
21
20
  loadPersistentConfig,
22
21
  persistConfig,
23
22
  } from './src/core/config.mjs';
@@ -225,7 +224,6 @@ export async function startTiclawk() {
225
224
 
226
225
  const { runtimeList, runtimes } = await buildRuntimeContext();
227
226
  const resolveRuntimeBinding = createResolveRuntimeBinding(runtimes);
228
- const configuredAdapter = getConfiguredAdapter();
229
227
  let adapter;
230
228
  const cacheBinding = createCacheBinding(runtimes, () => adapter);
231
229
  let baseRuntimeCtx;
@@ -237,7 +235,7 @@ export async function startTiclawk() {
237
235
  };
238
236
  baseRuntimeCtx = createBaseRuntimeCtx(runtimes, cacheBinding, (binding) => syncBinding(binding));
239
237
  adapter = createAdapter(
240
- configuredAdapter,
238
+ 'ticlawk',
241
239
  createAdapterContext(baseRuntimeCtx, resolveRuntimeBinding)
242
240
  );
243
241
  syncBinding = createUpsertBindingWithSync(runtimes, adapter);
@@ -249,7 +247,11 @@ export async function startTiclawk() {
249
247
  }
250
248
  registerRuntimeHandlers(runtimeList, baseRuntimeCtx, adapter);
251
249
  await replayBindings(runtimes, adapter);
252
- startLocalHttpServer({ port: HTTP_PORT, adapter });
250
+ startLocalHttpServer({
251
+ port: HTTP_PORT,
252
+ adapter,
253
+ ctx: { listBindings, getBinding },
254
+ });
253
255
  await recoverAllRuntimes(runtimeList, adapter);
254
256
  await reconcileBindingsAfterRestart(runtimes, adapter);
255
257
  await adapter.start();
@@ -1,137 +0,0 @@
1
- <svg width="960" height="520" viewBox="0 0 960 520" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="title desc">
2
- <title id="title">ticlawk connects any client to any agent</title>
3
- <desc id="desc">A hub-and-spoke diagram with clients on the left, ticlawk in the center, and agent runtimes on the right.</desc>
4
-
5
- <defs>
6
- <linearGradient id="hub" x1="372" y1="162" x2="588" y2="378" gradientUnits="userSpaceOnUse">
7
- <stop stop-color="#1D4ED8"/>
8
- <stop offset="0.52" stop-color="#0F766E"/>
9
- <stop offset="1" stop-color="#7C2D12"/>
10
- </linearGradient>
11
- <linearGradient id="clientLine" x1="180" y1="72" x2="460" y2="260" gradientUnits="userSpaceOnUse">
12
- <stop stop-color="#2563EB"/>
13
- <stop offset="1" stop-color="#0F766E"/>
14
- </linearGradient>
15
- <linearGradient id="agentLine" x1="500" y1="260" x2="780" y2="448" gradientUnits="userSpaceOnUse">
16
- <stop stop-color="#0F766E"/>
17
- <stop offset="1" stop-color="#C2410C"/>
18
- </linearGradient>
19
- <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
20
- <feDropShadow dx="0" dy="10" stdDeviation="16" flood-color="#0F172A" flood-opacity="0.12"/>
21
- </filter>
22
- <style>
23
- .label { fill: #0f172a; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-weight: 700; font-size: 16px; }
24
- .small { fill: #475569; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-weight: 600; font-size: 13px; }
25
- .hubText { fill: white; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-weight: 800; font-size: 27px; }
26
- .hubSub { fill: #E0F2FE; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-weight: 650; font-size: 13px; letter-spacing: 0.08em; }
27
- .caption { fill: #334155; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-weight: 650; font-size: 14px; }
28
- .node { fill: #FFFFFF; stroke: #CBD5E1; stroke-width: 1.4; }
29
- .dot { fill: #F8FAFC; stroke: #CBD5E1; stroke-width: 1.4; }
30
- </style>
31
- </defs>
32
-
33
- <rect width="960" height="520" rx="22" fill="#F8FAFC"/>
34
- <rect x="24" y="24" width="912" height="472" rx="18" fill="#FFFFFF" stroke="#E2E8F0"/>
35
-
36
- <text x="180" y="64" text-anchor="middle" class="caption">Any client surface</text>
37
- <text x="780" y="64" text-anchor="middle" class="caption">Any agent runtime</text>
38
-
39
- <path d="M230 116 C310 126 350 166 405 215" stroke="url(#clientLine)" stroke-width="4" stroke-linecap="round"/>
40
- <path d="M230 188 C310 192 354 216 405 244" stroke="url(#clientLine)" stroke-width="4" stroke-linecap="round"/>
41
- <path d="M230 260 C306 260 348 260 405 260" stroke="url(#clientLine)" stroke-width="4" stroke-linecap="round"/>
42
- <path d="M230 332 C310 328 354 304 405 276" stroke="url(#clientLine)" stroke-width="4" stroke-linecap="round"/>
43
- <path d="M230 404 C310 394 350 354 405 305" stroke="url(#clientLine)" stroke-width="4" stroke-linecap="round"/>
44
-
45
- <path d="M555 215 C610 166 650 126 730 116" stroke="url(#agentLine)" stroke-width="4" stroke-linecap="round"/>
46
- <path d="M555 244 C606 216 650 192 730 188" stroke="url(#agentLine)" stroke-width="4" stroke-linecap="round"/>
47
- <path d="M555 260 C612 260 654 260 730 260" stroke="url(#agentLine)" stroke-width="4" stroke-linecap="round"/>
48
- <path d="M555 276 C606 304 650 328 730 332" stroke="url(#agentLine)" stroke-width="4" stroke-linecap="round"/>
49
- <path d="M555 305 C610 354 650 394 730 404" stroke="url(#agentLine)" stroke-width="4" stroke-linecap="round"/>
50
-
51
- <g filter="url(#shadow)">
52
- <rect x="70" y="88" width="160" height="56" rx="8" class="node"/>
53
- <circle cx="100" cy="116" r="14" fill="#DBEAFE"/>
54
- <path d="M94 116h12M100 110v12" stroke="#2563EB" stroke-width="2.4" stroke-linecap="round"/>
55
- <text x="124" y="112" class="label">Telegram</text>
56
- <text x="124" y="130" class="small">chat bot</text>
57
-
58
- <rect x="70" y="160" width="160" height="56" rx="8" class="node"/>
59
- <circle cx="100" cy="188" r="14" fill="#CCFBF1"/>
60
- <rect x="94" y="180" width="12" height="16" rx="3" stroke="#0F766E" stroke-width="2.2"/>
61
- <path d="M98 202h4" stroke="#0F766E" stroke-width="2.2" stroke-linecap="round"/>
62
- <text x="124" y="184" class="label">ticlawk</text>
63
- <text x="124" y="202" class="small">mobile app</text>
64
-
65
- <rect x="70" y="232" width="160" height="56" rx="8" class="node"/>
66
- <circle cx="100" cy="260" r="14" fill="#FDE68A"/>
67
- <path d="M92 256h16M92 262h16M100 252v16" stroke="#A16207" stroke-width="2" stroke-linecap="round"/>
68
- <text x="124" y="256" class="label">Web UI</text>
69
- <text x="124" y="274" class="small">browser</text>
70
-
71
- <rect x="70" y="304" width="160" height="56" rx="8" class="node"/>
72
- <circle cx="100" cy="332" r="14" fill="#EDE9FE"/>
73
- <path d="M94 326l6 6-6 6M102 338h6" stroke="#6D28D9" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
74
- <text x="124" y="328" class="label">CLI</text>
75
- <text x="124" y="346" class="small">terminal</text>
76
-
77
- <rect x="70" y="376" width="160" height="56" rx="8" class="node"/>
78
- <circle cx="100" cy="404" r="14" fill="#FFE4E6"/>
79
- <path d="M94 398h12v12H94z" stroke="#BE123C" stroke-width="2.2" stroke-linejoin="round"/>
80
- <path d="M97 395h6M97 413h6" stroke="#BE123C" stroke-width="2.2" stroke-linecap="round"/>
81
- <text x="124" y="400" class="label">Custom app</text>
82
- <text x="124" y="418" class="small">your surface</text>
83
- </g>
84
-
85
- <g filter="url(#shadow)">
86
- <rect x="730" y="88" width="160" height="56" rx="8" class="node"/>
87
- <circle cx="760" cy="116" r="14" fill="#FEF3C7"/>
88
- <path d="M753 121l7-12 7 12M756 117h8" stroke="#B45309" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
89
- <text x="784" y="112" class="label">Claude Code</text>
90
- <text x="784" y="130" class="small">project agent</text>
91
-
92
- <rect x="730" y="160" width="160" height="56" rx="8" class="node"/>
93
- <circle cx="760" cy="188" r="14" fill="#DBEAFE"/>
94
- <path d="M754 188a6 6 0 1 1 12 0a6 6 0 1 1-12 0" stroke="#1D4ED8" stroke-width="2.2"/>
95
- <path d="M760 176v4M760 196v4M748 188h4M768 188h4" stroke="#1D4ED8" stroke-width="2.2" stroke-linecap="round"/>
96
- <text x="784" y="184" class="label">Codex</text>
97
- <text x="784" y="202" class="small">coding agent</text>
98
-
99
- <rect x="730" y="232" width="160" height="56" rx="8" class="node"/>
100
- <circle cx="760" cy="260" r="14" fill="#CCFBF1"/>
101
- <path d="M752 260h16M760 252v16M755 255l10 10M765 255l-10 10" stroke="#0F766E" stroke-width="2" stroke-linecap="round"/>
102
- <text x="784" y="256" class="label">OpenClaw</text>
103
- <text x="784" y="274" class="small">local runtime</text>
104
-
105
- <rect x="730" y="304" width="160" height="56" rx="8" class="node"/>
106
- <circle cx="760" cy="332" r="14" fill="#FCE7F3"/>
107
- <path d="M752 332c3-8 13-8 16 0c-3 8-13 8-16 0Z" stroke="#BE185D" stroke-width="2.2"/>
108
- <circle cx="760" cy="332" r="3" fill="#BE185D"/>
109
- <text x="784" y="328" class="label">opencode</text>
110
- <text x="784" y="346" class="small">provider agnostic</text>
111
-
112
- <rect x="730" y="376" width="160" height="56" rx="8" class="node"/>
113
- <circle cx="760" cy="404" r="14" fill="#E0E7FF"/>
114
- <path d="M754 398h12v12h-12zM751 401h3M766 401h3M751 407h3M766 407h3" stroke="#4338CA" stroke-width="2" stroke-linejoin="round"/>
115
- <text x="784" y="400" class="label">Custom agent</text>
116
- <text x="784" y="418" class="small">your harness</text>
117
- </g>
118
-
119
- <g filter="url(#shadow)">
120
- <circle cx="480" cy="260" r="118" fill="url(#hub)"/>
121
- <circle cx="480" cy="260" r="86" fill="#FFFFFF" fill-opacity="0.12" stroke="#FFFFFF" stroke-opacity="0.32" stroke-width="1.5"/>
122
- <circle cx="480" cy="260" r="52" fill="#FFFFFF" fill-opacity="0.16" stroke="#FFFFFF" stroke-opacity="0.38" stroke-width="1.4"/>
123
-
124
- <circle cx="480" cy="176" r="8" class="dot"/>
125
- <circle cx="548" cy="212" r="8" class="dot"/>
126
- <circle cx="548" cy="308" r="8" class="dot"/>
127
- <circle cx="480" cy="344" r="8" class="dot"/>
128
- <circle cx="412" cy="308" r="8" class="dot"/>
129
- <circle cx="412" cy="212" r="8" class="dot"/>
130
- <path d="M480 184v152M420 216l120 88M540 216l-120 88" stroke="#FFFFFF" stroke-opacity="0.65" stroke-width="2.6" stroke-linecap="round"/>
131
-
132
- <text x="480" y="253" text-anchor="middle" class="hubText">ticlawk</text>
133
- <text x="480" y="281" text-anchor="middle" class="hubSub">UNIVERSAL ROUTER</text>
134
- </g>
135
-
136
- <text x="480" y="465" text-anchor="middle" class="small">Bind sessions, relay streams, keep every side replaceable.</text>
137
- </svg>