@link-assistant/agent 0.0.8 → 0.0.11

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 (104) hide show
  1. package/EXAMPLES.md +80 -1
  2. package/MODELS.md +72 -24
  3. package/README.md +95 -2
  4. package/TOOLS.md +20 -0
  5. package/package.json +36 -2
  6. package/src/agent/agent.ts +68 -54
  7. package/src/auth/claude-oauth.ts +426 -0
  8. package/src/auth/index.ts +28 -26
  9. package/src/auth/plugins.ts +876 -0
  10. package/src/bun/index.ts +53 -43
  11. package/src/bus/global.ts +5 -5
  12. package/src/bus/index.ts +59 -53
  13. package/src/cli/bootstrap.js +12 -12
  14. package/src/cli/bootstrap.ts +6 -6
  15. package/src/cli/cmd/agent.ts +97 -92
  16. package/src/cli/cmd/auth.ts +468 -0
  17. package/src/cli/cmd/cmd.ts +2 -2
  18. package/src/cli/cmd/export.ts +41 -41
  19. package/src/cli/cmd/mcp.ts +210 -53
  20. package/src/cli/cmd/models.ts +30 -29
  21. package/src/cli/cmd/run.ts +269 -213
  22. package/src/cli/cmd/stats.ts +185 -146
  23. package/src/cli/error.ts +17 -13
  24. package/src/cli/ui.ts +78 -0
  25. package/src/command/index.ts +26 -26
  26. package/src/config/config.ts +528 -288
  27. package/src/config/markdown.ts +15 -15
  28. package/src/file/ripgrep.ts +201 -169
  29. package/src/file/time.ts +21 -18
  30. package/src/file/watcher.ts +51 -42
  31. package/src/file.ts +1 -1
  32. package/src/flag/flag.ts +26 -11
  33. package/src/format/formatter.ts +206 -162
  34. package/src/format/index.ts +61 -61
  35. package/src/global/index.ts +21 -21
  36. package/src/id/id.ts +47 -33
  37. package/src/index.js +554 -332
  38. package/src/json-standard/index.ts +173 -0
  39. package/src/mcp/index.ts +135 -128
  40. package/src/patch/index.ts +336 -267
  41. package/src/project/bootstrap.ts +15 -15
  42. package/src/project/instance.ts +43 -36
  43. package/src/project/project.ts +47 -47
  44. package/src/project/state.ts +37 -33
  45. package/src/provider/models-macro.ts +5 -5
  46. package/src/provider/models.ts +32 -32
  47. package/src/provider/opencode.js +19 -19
  48. package/src/provider/provider.ts +518 -277
  49. package/src/provider/transform.ts +143 -102
  50. package/src/server/project.ts +21 -21
  51. package/src/server/server.ts +111 -105
  52. package/src/session/agent.js +66 -60
  53. package/src/session/compaction.ts +136 -111
  54. package/src/session/index.ts +189 -156
  55. package/src/session/message-v2.ts +312 -268
  56. package/src/session/message.ts +73 -57
  57. package/src/session/processor.ts +180 -166
  58. package/src/session/prompt.ts +678 -533
  59. package/src/session/retry.ts +26 -23
  60. package/src/session/revert.ts +76 -62
  61. package/src/session/status.ts +26 -26
  62. package/src/session/summary.ts +97 -76
  63. package/src/session/system.ts +77 -63
  64. package/src/session/todo.ts +22 -16
  65. package/src/snapshot/index.ts +92 -76
  66. package/src/storage/storage.ts +157 -120
  67. package/src/tool/bash.ts +116 -106
  68. package/src/tool/batch.ts +73 -59
  69. package/src/tool/codesearch.ts +60 -53
  70. package/src/tool/edit.ts +319 -263
  71. package/src/tool/glob.ts +32 -28
  72. package/src/tool/grep.ts +72 -53
  73. package/src/tool/invalid.ts +7 -7
  74. package/src/tool/ls.ts +77 -64
  75. package/src/tool/multiedit.ts +30 -21
  76. package/src/tool/patch.ts +121 -94
  77. package/src/tool/read.ts +140 -122
  78. package/src/tool/registry.ts +38 -38
  79. package/src/tool/task.ts +93 -60
  80. package/src/tool/todo.ts +16 -16
  81. package/src/tool/tool.ts +45 -36
  82. package/src/tool/webfetch.ts +97 -74
  83. package/src/tool/websearch.ts +78 -64
  84. package/src/tool/write.ts +21 -15
  85. package/src/util/binary.ts +27 -19
  86. package/src/util/context.ts +8 -8
  87. package/src/util/defer.ts +7 -5
  88. package/src/util/error.ts +24 -19
  89. package/src/util/eventloop.ts +16 -10
  90. package/src/util/filesystem.ts +37 -33
  91. package/src/util/fn.ts +11 -8
  92. package/src/util/iife.ts +1 -1
  93. package/src/util/keybind.ts +44 -44
  94. package/src/util/lazy.ts +7 -7
  95. package/src/util/locale.ts +20 -16
  96. package/src/util/lock.ts +43 -38
  97. package/src/util/log.ts +95 -85
  98. package/src/util/queue.ts +8 -8
  99. package/src/util/rpc.ts +35 -23
  100. package/src/util/scrap.ts +4 -4
  101. package/src/util/signal.ts +5 -5
  102. package/src/util/timeout.ts +6 -6
  103. package/src/util/token.ts +2 -2
  104. package/src/util/wildcard.ts +38 -27
@@ -1,23 +1,23 @@
1
- import { Log } from "../util/log"
2
- import { describeRoute, validator, resolver } from "hono-openapi"
3
- import { Hono } from "hono"
4
- import { cors } from "hono/cors"
5
- import { stream } from "hono/streaming"
6
- import { Session } from "../session"
7
- import z from "zod"
8
- import { NamedError } from "../util/error"
9
- import { MessageV2 } from "../session/message-v2"
10
- import { Instance } from "../project/instance"
11
- import { SessionPrompt } from "../session/prompt"
12
- import { Storage } from "../storage/storage"
13
- import type { ContentfulStatusCode } from "hono/utils/http-status"
14
- import { lazy } from "../util/lazy"
1
+ import { Log } from '../util/log';
2
+ import { describeRoute, validator, resolver } from 'hono-openapi';
3
+ import { Hono } from 'hono';
4
+ import { cors } from 'hono/cors';
5
+ import { stream } from 'hono/streaming';
6
+ import { Session } from '../session';
7
+ import z from 'zod';
8
+ import { NamedError } from '../util/error';
9
+ import { MessageV2 } from '../session/message-v2';
10
+ import { Instance } from '../project/instance';
11
+ import { SessionPrompt } from '../session/prompt';
12
+ import { Storage } from '../storage/storage';
13
+ import type { ContentfulStatusCode } from 'hono/utils/http-status';
14
+ import { lazy } from '../util/lazy';
15
15
 
16
16
  const ERRORS = {
17
17
  400: {
18
- description: "Bad request",
18
+ description: 'Bad request',
19
19
  content: {
20
- "application/json": {
20
+ 'application/json': {
21
21
  schema: resolver(
22
22
  z
23
23
  .object({
@@ -26,98 +26,104 @@ const ERRORS = {
26
26
  success: z.literal(false),
27
27
  })
28
28
  .meta({
29
- ref: "BadRequestError",
30
- }),
29
+ ref: 'BadRequestError',
30
+ })
31
31
  ),
32
32
  },
33
33
  },
34
34
  },
35
35
  404: {
36
- description: "Not found",
36
+ description: 'Not found',
37
37
  content: {
38
- "application/json": {
38
+ 'application/json': {
39
39
  schema: resolver(Storage.NotFoundError.Schema),
40
40
  },
41
41
  },
42
42
  },
43
- } as const
43
+ } as const;
44
44
 
45
45
  function errors(...codes: number[]) {
46
- return Object.fromEntries(codes.map((code) => [code, ERRORS[code as keyof typeof ERRORS]]))
46
+ return Object.fromEntries(
47
+ codes.map((code) => [code, ERRORS[code as keyof typeof ERRORS]])
48
+ );
47
49
  }
48
50
 
49
51
  export namespace Server {
50
- const log = Log.create({ service: "server" })
52
+ const log = Log.create({ service: 'server' });
51
53
 
52
- const app = new Hono()
54
+ const app = new Hono();
53
55
  export const App = lazy(() =>
54
56
  app
55
57
  .onError((err, c) => {
56
- log.error("failed", {
58
+ log.error('failed', {
57
59
  error: err,
58
- })
60
+ });
59
61
  if (err instanceof NamedError) {
60
- let status: ContentfulStatusCode
61
- if (err instanceof Storage.NotFoundError) status = 404
62
- else status = 500
63
- return c.json(err.toObject(), { status })
62
+ let status: ContentfulStatusCode;
63
+ if (err instanceof Storage.NotFoundError) status = 404;
64
+ else status = 500;
65
+ return c.json(err.toObject(), { status });
64
66
  }
65
- const message = err instanceof Error && err.stack ? err.stack : err.toString()
67
+ const message =
68
+ err instanceof Error && err.stack ? err.stack : err.toString();
66
69
  return c.json(new NamedError.Unknown({ message }).toObject(), {
67
70
  status: 500,
68
- })
71
+ });
69
72
  })
70
73
  .use(async (c, next) => {
71
- log.info("request", {
74
+ log.info('request', {
72
75
  method: c.req.method,
73
76
  path: c.req.path,
74
- })
75
- const timer = log.time("request", {
77
+ });
78
+ const timer = log.time('request', {
76
79
  method: c.req.method,
77
80
  path: c.req.path,
78
- })
79
- await next()
80
- timer.stop()
81
+ });
82
+ await next();
83
+ timer.stop();
81
84
  })
82
85
  .use(cors())
83
- .get("/health", (c) => {
84
- return c.json({ status: "ok" })
86
+ .get('/health', (c) => {
87
+ return c.json({ status: 'ok' });
85
88
  })
86
89
  .post(
87
- "/session",
90
+ '/session',
88
91
  describeRoute({
89
- description: "Create a new session",
90
- operationId: "session.create",
92
+ description: 'Create a new session',
93
+ operationId: 'session.create',
91
94
  responses: {
92
95
  200: {
93
- description: "Created session",
96
+ description: 'Created session',
94
97
  content: {
95
- "application/json": {
98
+ 'application/json': {
96
99
  schema: resolver(Session.Info),
97
100
  },
98
101
  },
99
102
  },
100
103
  },
101
104
  }),
102
- validator("json", z.object({ title: z.string().optional() }).optional()),
105
+ validator(
106
+ 'json',
107
+ z.object({ title: z.string().optional() }).optional()
108
+ ),
103
109
  async (c) => {
104
- const body = c.req.valid("json") || {}
110
+ const body = c.req.valid('json') || {};
105
111
  const session = await Session.create({
106
112
  title: body.title,
107
- })
108
- return c.json(session)
109
- },
113
+ });
114
+ return c.json(session);
115
+ }
110
116
  )
111
117
  .get(
112
- "/session",
118
+ '/session',
113
119
  describeRoute({
114
- description: "List all sessions",
115
- operationId: "session.list",
120
+ description: 'List all sessions',
121
+ operationId: 'session.list',
116
122
  responses: {
117
123
  200: {
118
- description: "List of sessions",
124
+ description: 'List of sessions',
119
125
  content: {
120
- "application/json": {
126
+ 'application/json': {
121
127
  schema: resolver(Session.Info.array()),
122
128
  },
123
129
  },
@@ -125,20 +131,20 @@ export namespace Server {
125
131
  },
126
132
  }),
127
133
  async (c) => {
128
- const sessions = await Session.list()
129
- return c.json(sessions)
130
- },
134
+ const sessions = await Session.list();
135
+ return c.json(sessions);
136
+ }
131
137
  )
132
138
  .get(
133
- "/session/:id",
139
+ '/session/:id',
134
140
  describeRoute({
135
- description: "Get a session",
136
- operationId: "session.get",
141
+ description: 'Get a session',
142
+ operationId: 'session.get',
137
143
  responses: {
138
144
  200: {
139
- description: "Session info",
145
+ description: 'Session info',
140
146
  content: {
141
- "application/json": {
147
+ 'application/json': {
142
148
  schema: resolver(Session.Info),
143
149
  },
144
150
  },
@@ -147,31 +153,31 @@ export namespace Server {
147
153
  },
148
154
  }),
149
155
  validator(
150
- "param",
156
+ 'param',
151
157
  z.object({
152
158
  id: z.string(),
153
- }),
159
+ })
154
160
  ),
155
161
  async (c) => {
156
- const session = await Session.get(c.req.valid("param").id)
157
- return c.json(session)
158
- },
162
+ const session = await Session.get(c.req.valid('param').id);
163
+ return c.json(session);
164
+ }
159
165
  )
160
166
  .post(
161
- "/session/:id/message",
167
+ '/session/:id/message',
162
168
  describeRoute({
163
- description: "Create and send a new message to a session",
164
- operationId: "session.prompt",
169
+ description: 'Create and send a new message to a session',
170
+ operationId: 'session.prompt',
165
171
  responses: {
166
172
  200: {
167
- description: "Created message",
173
+ description: 'Created message',
168
174
  content: {
169
- "application/json": {
175
+ 'application/json': {
170
176
  schema: resolver(
171
177
  z.object({
172
178
  info: MessageV2.Assistant,
173
179
  parts: MessageV2.Part.array(),
174
- }),
180
+ })
175
181
  ),
176
182
  },
177
183
  },
@@ -180,33 +186,33 @@ export namespace Server {
180
186
  },
181
187
  }),
182
188
  validator(
183
- "param",
189
+ 'param',
184
190
  z.object({
185
- id: z.string().meta({ description: "Session ID" }),
186
- }),
191
+ id: z.string().meta({ description: 'Session ID' }),
192
+ })
187
193
  ),
188
- validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
194
+ validator('json', SessionPrompt.PromptInput.omit({ sessionID: true })),
189
195
  async (c) => {
190
- c.status(200)
191
- c.header("Content-Type", "application/json")
196
+ c.status(200);
197
+ c.header('Content-Type', 'application/json');
192
198
  return stream(c, async (stream) => {
193
- const sessionID = c.req.valid("param").id
194
- const body = c.req.valid("json")
195
- const msg = await SessionPrompt.prompt({ ...body, sessionID })
196
- stream.write(JSON.stringify(msg))
197
- })
198
- },
199
+ const sessionID = c.req.valid('param').id;
200
+ const body = c.req.valid('json');
201
+ const msg = await SessionPrompt.prompt({ ...body, sessionID });
202
+ stream.write(JSON.stringify(msg));
203
+ });
204
+ }
199
205
  )
200
206
  .get(
201
- "/session/:id/message",
207
+ '/session/:id/message',
202
208
  describeRoute({
203
- description: "List messages for a session",
204
- operationId: "session.messages",
209
+ description: 'List messages for a session',
210
+ operationId: 'session.messages',
205
211
  responses: {
206
212
  200: {
207
- description: "List of messages",
213
+ description: 'List of messages',
208
214
  content: {
209
- "application/json": {
215
+ 'application/json': {
210
216
  schema: resolver(MessageV2.WithParts.array()),
211
217
  },
212
218
  },
@@ -215,27 +221,27 @@ export namespace Server {
215
221
  },
216
222
  }),
217
223
  validator(
218
- "param",
224
+ 'param',
219
225
  z.object({
220
- id: z.string().meta({ description: "Session ID" }),
221
- }),
226
+ id: z.string().meta({ description: 'Session ID' }),
227
+ })
222
228
  ),
223
229
  validator(
224
- "query",
230
+ 'query',
225
231
  z.object({
226
232
  limit: z.coerce.number().optional(),
227
- }),
233
+ })
228
234
  ),
229
235
  async (c) => {
230
- const query = c.req.valid("query")
236
+ const query = c.req.valid('query');
231
237
  const messages = await Session.messages({
232
- sessionID: c.req.valid("param").id,
238
+ sessionID: c.req.valid('param').id,
233
239
  limit: query.limit,
234
- })
235
- return c.json(messages)
236
- },
237
- ),
238
- )
240
+ });
241
+ return c.json(messages);
242
+ }
243
+ )
244
+ );
239
245
 
240
246
  export function listen(opts: { port: number; hostname: string }) {
241
247
  const server = Bun.serve({
@@ -243,7 +249,7 @@ export namespace Server {
243
249
  hostname: opts.hostname,
244
250
  idleTimeout: 0,
245
251
  fetch: App().fetch,
246
- })
247
- return server
252
+ });
253
+ return server;
248
254
  }
249
255
  }
@@ -2,27 +2,27 @@
2
2
  // Permalink: https://github.com/sst/opencode/blob/main/packages/opencode/src/session/index.ts
3
3
  // Permalink: https://github.com/sst/opencode/blob/main/packages/opencode/src/provider/provider.ts
4
4
 
5
- import { ToolRegistry } from '../tool/registry.ts'
5
+ import { ToolRegistry } from '../tool/registry.ts';
6
6
 
7
7
  export class Agent {
8
8
  constructor() {
9
9
  // Generate IDs in the same format as opencode
10
- const randomId = Math.random().toString(36).substring(2, 15)
11
- this.sessionID = `ses_${Date.now().toString(36)}${randomId}`
12
- this.messageID = `msg_${Date.now().toString(36)}${randomId}`
13
- this.partCounter = 0
10
+ const randomId = Math.random().toString(36).substring(2, 15);
11
+ this.sessionID = `ses_${Date.now().toString(36)}${randomId}`;
12
+ this.messageID = `msg_${Date.now().toString(36)}${randomId}`;
13
+ this.partCounter = 0;
14
14
  }
15
15
 
16
16
  generatePartId() {
17
- return `prt_${Date.now().toString(36)}${Math.random().toString(36).substring(2, 15)}`
17
+ return `prt_${Date.now().toString(36)}${Math.random().toString(36).substring(2, 15)}`;
18
18
  }
19
19
 
20
20
  async process(request) {
21
- const message = request.message || "hi"
22
- const sessionID = this.sessionID
21
+ const message = request.message || 'hi';
22
+ const sessionID = this.sessionID;
23
23
 
24
24
  // Generate snapshot hash (mock)
25
- const snapshot = Math.random().toString(16).substring(2, 42)
25
+ const snapshot = Math.random().toString(16).substring(2, 42);
26
26
 
27
27
  // Emit step_start like opencode
28
28
  this.emitEvent('step_start', {
@@ -31,21 +31,21 @@ export class Agent {
31
31
  sessionID,
32
32
  messageID: this.messageID,
33
33
  type: 'step-start',
34
- snapshot
35
- }
36
- })
34
+ snapshot,
35
+ },
36
+ });
37
37
 
38
38
  // Check if this is a tool request
39
39
  if (request.tools && request.tools.length > 0) {
40
40
  // Handle tool execution
41
- const toolsList = await ToolRegistry.tools('', '')
42
- const toolsMap = Object.fromEntries(toolsList.map(t => [t.id, t]))
41
+ const toolsList = await ToolRegistry.tools('', '');
42
+ const toolsMap = Object.fromEntries(toolsList.map((t) => [t.id, t]));
43
43
  for (const tool of request.tools) {
44
- const toolFn = toolsMap[tool.name]
44
+ const toolFn = toolsMap[tool.name];
45
45
  if (toolFn) {
46
46
  try {
47
- const startTime = Date.now()
48
- const callID = `call_${Math.floor(Math.random() * 100000000)}`
47
+ const startTime = Date.now();
48
+ const callID = `call_${Math.floor(Math.random() * 100000000)}`;
49
49
 
50
50
  // Create OpenCode-compatible context
51
51
  const ctx = {
@@ -54,13 +54,13 @@ export class Agent {
54
54
  agent: 'default',
55
55
  callID,
56
56
  abort: new AbortController().signal,
57
- metadata: (data) => {
57
+ metadata: (_data) => {
58
58
  // Handle metadata updates during execution
59
- }
60
- }
59
+ },
60
+ };
61
61
 
62
- const result = await toolFn.execute(tool.params, ctx)
63
- const endTime = Date.now()
62
+ const result = await toolFn.execute(tool.params, ctx);
63
+ const endTime = Date.now();
64
64
 
65
65
  // Emit tool_use event
66
66
  this.emitEvent('tool_use', {
@@ -75,25 +75,29 @@ export class Agent {
75
75
  status: 'completed',
76
76
  input: tool.params,
77
77
  output: result.output,
78
- title: result.title || `${tool.name} ${JSON.stringify(tool.params)}`,
78
+ title:
79
+ result.title ||
80
+ `${tool.name} ${JSON.stringify(tool.params)}`,
79
81
  metadata: result.metadata || {
80
82
  output: result.output,
81
83
  exit: result.exitCode || 0,
82
- ...(tool.params.description && { description: tool.params.description })
84
+ ...(tool.params.description && {
85
+ description: tool.params.description,
86
+ }),
83
87
  },
84
88
  time: {
85
89
  start: startTime,
86
- end: endTime
87
- }
88
- }
89
- }
90
- })
90
+ end: endTime,
91
+ },
92
+ },
93
+ },
94
+ });
91
95
  } catch (error) {
92
- const errorTime = Date.now()
93
- const callID = `call_${Math.floor(Math.random() * 100000000)}`
96
+ const errorTime = Date.now();
97
+ const callID = `call_${Math.floor(Math.random() * 100000000)}`;
94
98
 
95
99
  // Log full error to stderr for debugging
96
- console.error('Tool execution error:', error)
100
+ console.error('Tool execution error:', error);
97
101
 
98
102
  // Emit tool_use event with error
99
103
  this.emitEvent('tool_use', {
@@ -110,11 +114,11 @@ export class Agent {
110
114
  error: error.message || String(error),
111
115
  time: {
112
116
  start: errorTime,
113
- end: errorTime
114
- }
115
- }
116
- }
117
- })
117
+ end: errorTime,
118
+ },
119
+ },
120
+ },
121
+ });
118
122
  }
119
123
  }
120
124
  }
@@ -133,23 +137,23 @@ export class Agent {
133
137
  input: 1273,
134
138
  output: 2,
135
139
  reasoning: 173,
136
- cache: { read: 9536, write: 0 }
137
- }
138
- }
139
- })
140
+ cache: { read: 9536, write: 0 },
141
+ },
142
+ },
143
+ });
140
144
 
141
145
  return {
142
146
  sessionID,
143
- timestamp: Date.now()
144
- }
147
+ timestamp: Date.now(),
148
+ };
145
149
  }
146
150
 
147
151
  // Regular message processing
148
152
  // Simulate processing delay
149
- await new Promise(resolve => setTimeout(resolve, 100))
153
+ await new Promise((resolve) => setTimeout(resolve, 100));
150
154
 
151
155
  // Emit text response like opencode
152
- const responseText = message === "hi" ? "Hi!" : `You said: "${message}"`
156
+ const responseText = message === 'hi' ? 'Hi!' : `You said: "${message}"`;
153
157
  this.emitEvent('text', {
154
158
  part: {
155
159
  id: this.generatePartId(),
@@ -159,10 +163,10 @@ export class Agent {
159
163
  text: responseText,
160
164
  time: {
161
165
  start: Date.now(),
162
- end: Date.now()
163
- }
164
- }
165
- })
166
+ end: Date.now(),
167
+ },
168
+ },
169
+ });
166
170
 
167
171
  // Emit step_finish with cost and tokens like opencode
168
172
  this.emitEvent('step_finish', {
@@ -178,15 +182,15 @@ export class Agent {
178
182
  input: 1273,
179
183
  output: 2,
180
184
  reasoning: 173,
181
- cache: { read: 9536, write: 0 }
182
- }
183
- }
184
- })
185
+ cache: { read: 9536, write: 0 },
186
+ },
187
+ },
188
+ });
185
189
 
186
190
  return {
187
191
  sessionID,
188
- timestamp: Date.now()
189
- }
192
+ timestamp: Date.now(),
193
+ };
190
194
  }
191
195
 
192
196
  emitEvent(type, data) {
@@ -194,11 +198,13 @@ export class Agent {
194
198
  type,
195
199
  timestamp: Date.now(),
196
200
  sessionID: this.sessionID,
197
- ...data
198
- }
201
+ ...data,
202
+ };
199
203
  // Pretty-print JSON for human readability, compact for programmatic use
200
204
  // Use AGENT_CLI_COMPACT=1 for compact output (tests, automation)
201
- const compact = process.env.AGENT_CLI_COMPACT === '1'
202
- console.log(compact ? JSON.stringify(event) : JSON.stringify(event, null, 2))
205
+ const compact = process.env.AGENT_CLI_COMPACT === '1';
206
+ console.log(
207
+ compact ? JSON.stringify(event) : JSON.stringify(event, null, 2)
208
+ );
203
209
  }
204
- }
210
+ }