@bbclaw/cli 0.1.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/auth/authStore.d.ts +17 -0
- package/dist/auth/authStore.d.ts.map +1 -0
- package/dist/auth/authStore.js +40 -0
- package/dist/auth/authStore.js.map +1 -0
- package/dist/auth/client.d.ts +23 -0
- package/dist/auth/client.d.ts.map +1 -0
- package/dist/auth/client.js +50 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +401 -0
- package/dist/index.js.map +1 -0
- package/dist/repl/App.d.ts +10 -0
- package/dist/repl/App.d.ts.map +1 -0
- package/dist/repl/App.js +86 -0
- package/dist/repl/App.js.map +1 -0
- package/dist/repl/Input.d.ts +25 -0
- package/dist/repl/Input.d.ts.map +1 -0
- package/dist/repl/Input.js +162 -0
- package/dist/repl/Input.js.map +1 -0
- package/dist/repl/MessageList.d.ts +8 -0
- package/dist/repl/MessageList.d.ts.map +1 -0
- package/dist/repl/MessageList.js +63 -0
- package/dist/repl/MessageList.js.map +1 -0
- package/dist/repl/ToolApprovalPrompt.d.ts +10 -0
- package/dist/repl/ToolApprovalPrompt.d.ts.map +1 -0
- package/dist/repl/ToolApprovalPrompt.js +34 -0
- package/dist/repl/ToolApprovalPrompt.js.map +1 -0
- package/dist/repl/animations.d.ts +24 -0
- package/dist/repl/animations.d.ts.map +1 -0
- package/dist/repl/animations.js +79 -0
- package/dist/repl/animations.js.map +1 -0
- package/dist/repl/runRepl.d.ts +4 -0
- package/dist/repl/runRepl.d.ts.map +1 -0
- package/dist/repl/runRepl.js +44 -0
- package/dist/repl/runRepl.js.map +1 -0
- package/dist/repl/sessionStore.d.ts +31 -0
- package/dist/repl/sessionStore.d.ts.map +1 -0
- package/dist/repl/sessionStore.js +79 -0
- package/dist/repl/sessionStore.js.map +1 -0
- package/dist/repl/slashCommands.d.ts +61 -0
- package/dist/repl/slashCommands.d.ts.map +1 -0
- package/dist/repl/slashCommands.js +328 -0
- package/dist/repl/slashCommands.js.map +1 -0
- package/dist/repl/useChat.d.ts +65 -0
- package/dist/repl/useChat.d.ts.map +1 -0
- package/dist/repl/useChat.js +517 -0
- package/dist/repl/useChat.js.map +1 -0
- package/dist/tools/Bash.d.ts +11 -0
- package/dist/tools/Bash.d.ts.map +1 -0
- package/dist/tools/Bash.js +61 -0
- package/dist/tools/Bash.js.map +1 -0
- package/dist/tools/Read.d.ts +11 -0
- package/dist/tools/Read.d.ts.map +1 -0
- package/dist/tools/Read.js +39 -0
- package/dist/tools/Read.js.map +1 -0
- package/dist/tools/Write.d.ts +12 -0
- package/dist/tools/Write.d.ts.map +1 -0
- package/dist/tools/Write.js +42 -0
- package/dist/tools/Write.js.map +1 -0
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +35 -0
- package/dist/tools/index.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
const MAX_BYTES = 10_000;
|
|
4
|
+
export const READ_TOOL = {
|
|
5
|
+
name: 'Read',
|
|
6
|
+
description: 'Read the contents of a UTF-8 file from disk. Returns the text content, truncated past 10,000 chars with a footer indicating the original size. Use for inspecting source files, config, logs, etc.',
|
|
7
|
+
input_schema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
path: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
description: 'Path to the file. Relative paths resolve against the user\'s current working directory.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
required: ['path'],
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
export async function execRead(input) {
|
|
19
|
+
if (typeof input.path !== 'string' || input.path.length === 0) {
|
|
20
|
+
return { output: 'Read requires { path: string }', isError: true };
|
|
21
|
+
}
|
|
22
|
+
const resolved = resolve(process.cwd(), input.path);
|
|
23
|
+
try {
|
|
24
|
+
const content = await readFile(resolved, 'utf8');
|
|
25
|
+
if (content.length <= MAX_BYTES)
|
|
26
|
+
return { output: content, isError: false };
|
|
27
|
+
return {
|
|
28
|
+
output: `${content.slice(0, MAX_BYTES)}\n[…truncated, full size ${content.length} chars]`,
|
|
29
|
+
isError: false,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
return {
|
|
34
|
+
output: `Failed to read ${resolved}: ${e instanceof Error ? e.message : String(e)}`,
|
|
35
|
+
isError: true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=Read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Read.js","sourceRoot":"","sources":["../../src/tools/Read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGnC,MAAM,SAAS,GAAG,MAAM,CAAA;AAExB,MAAM,CAAC,MAAM,SAAS,GAAS;IAC7B,IAAI,EAAE,MAAM;IACZ,WAAW,EACT,oMAAoM;IACtM,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,yFAAyF;aAC5F;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;CACF,CAAA;AAMD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAgB;IAC7C,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,MAAM,EAAE,gCAAgC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IACpE,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;IACnD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAChD,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;QAC3E,OAAO;YACL,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,4BAA4B,OAAO,CAAC,MAAM,SAAS;YACzF,OAAO,EAAE,KAAK;SACf,CAAA;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,MAAM,EAAE,kBAAkB,QAAQ,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YACnF,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Tool } from '@bbclaw/core';
|
|
2
|
+
export declare const WRITE_TOOL: Tool;
|
|
3
|
+
interface WriteInput {
|
|
4
|
+
path: string;
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function execWrite(input: WriteInput): Promise<{
|
|
8
|
+
output: string;
|
|
9
|
+
isError: boolean;
|
|
10
|
+
}>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=Write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Write.d.ts","sourceRoot":"","sources":["../../src/tools/Write.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAExC,eAAO,MAAM,UAAU,EAAE,IAkBxB,CAAA;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAmBhG"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
export const WRITE_TOOL = {
|
|
4
|
+
name: 'Write',
|
|
5
|
+
description: 'Write text content to a file on disk. Overwrites existing files; creates parent directories as needed. Use for creating new source files or updating config. Approval required.',
|
|
6
|
+
input_schema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
path: {
|
|
10
|
+
type: 'string',
|
|
11
|
+
description: 'Destination path. Relative paths resolve against the current working directory.',
|
|
12
|
+
},
|
|
13
|
+
content: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Full text content to write. This replaces any prior contents of the file.',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
required: ['path', 'content'],
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export async function execWrite(input) {
|
|
22
|
+
if (typeof input.path !== 'string' || input.path.length === 0) {
|
|
23
|
+
return { output: 'Write requires { path: string }', isError: true };
|
|
24
|
+
}
|
|
25
|
+
if (typeof input.content !== 'string') {
|
|
26
|
+
return { output: 'Write requires { content: string }', isError: true };
|
|
27
|
+
}
|
|
28
|
+
const resolved = resolve(process.cwd(), input.path);
|
|
29
|
+
try {
|
|
30
|
+
await mkdir(dirname(resolved), { recursive: true });
|
|
31
|
+
await writeFile(resolved, input.content, 'utf8');
|
|
32
|
+
const bytes = Buffer.byteLength(input.content, 'utf8');
|
|
33
|
+
return { output: `Wrote ${bytes} bytes to ${resolved}`, isError: false };
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
return {
|
|
37
|
+
output: `Failed to write ${resolved}: ${e instanceof Error ? e.message : String(e)}`,
|
|
38
|
+
isError: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=Write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Write.js","sourceRoot":"","sources":["../../src/tools/Write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAG5C,MAAM,CAAC,MAAM,UAAU,GAAS;IAC9B,IAAI,EAAE,OAAO;IACb,WAAW,EACT,iLAAiL;IACnL,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,iFAAiF;aAC/F;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,2EAA2E;aACzF;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;KAC9B;CACF,CAAA;AAOD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAiB;IAC/C,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,MAAM,EAAE,iCAAiC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IACrE,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,EAAE,MAAM,EAAE,oCAAoC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IACxE,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;IACnD,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACnD,MAAM,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAChD,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QACtD,OAAO,EAAE,MAAM,EAAE,SAAS,KAAK,aAAa,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IAC1E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,MAAM,EAAE,mBAAmB,QAAQ,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YACpF,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Tool } from '@bbclaw/core';
|
|
2
|
+
export interface BbclawTool {
|
|
3
|
+
def: Tool;
|
|
4
|
+
execute(input: Record<string, unknown>): Promise<{
|
|
5
|
+
output: string;
|
|
6
|
+
isError: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
export declare const BUILTIN_TOOLS: BbclawTool[];
|
|
10
|
+
export declare function getTool(name: string): BbclawTool | undefined;
|
|
11
|
+
export declare function runTool(name: string, input: Record<string, unknown>): Promise<{
|
|
12
|
+
output: string;
|
|
13
|
+
isError: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAKxC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,IAAI,CAAA;IACT,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CACvF;AAED,eAAO,MAAM,aAAa,EAAE,UAAU,EAarC,CAAA;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAE5D;AAED,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAW/C"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { BASH_TOOL, execBash } from './Bash.js';
|
|
2
|
+
import { READ_TOOL, execRead } from './Read.js';
|
|
3
|
+
import { WRITE_TOOL, execWrite } from './Write.js';
|
|
4
|
+
export const BUILTIN_TOOLS = [
|
|
5
|
+
{
|
|
6
|
+
def: BASH_TOOL,
|
|
7
|
+
execute: (input) => execBash(input),
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
def: READ_TOOL,
|
|
11
|
+
execute: (input) => execRead(input),
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
def: WRITE_TOOL,
|
|
15
|
+
execute: (input) => execWrite(input),
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
export function getTool(name) {
|
|
19
|
+
return BUILTIN_TOOLS.find((t) => t.def.name === name);
|
|
20
|
+
}
|
|
21
|
+
export async function runTool(name, input) {
|
|
22
|
+
const tool = getTool(name);
|
|
23
|
+
if (!tool)
|
|
24
|
+
return { output: `Unknown tool: ${name}`, isError: true };
|
|
25
|
+
try {
|
|
26
|
+
return await tool.execute(input);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
return {
|
|
30
|
+
output: `Tool ${name} threw: ${e instanceof Error ? e.message : String(e)}`,
|
|
31
|
+
isError: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAOlD,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC;QACE,GAAG,EAAE,SAAS;QACd,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAA4B,CAAC;KAC3D;IACD;QACE,GAAG,EAAE,SAAS;QACd,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAyB,CAAC;KACxD;IACD;QACE,GAAG,EAAE,UAAU;QACf,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAA0C,CAAC;KAC1E;CACF,CAAA;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAY,EACZ,KAA8B;IAE9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IACpE,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,MAAM,EAAE,QAAQ,IAAI,WAAW,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YAC3E,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bbclaw/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BBClaw — multi-provider AI agent CLI.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"bbclaw": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"commander": "^12.1.0",
|
|
19
|
+
"ink": "^5.0.1",
|
|
20
|
+
"react": "^18.3.1",
|
|
21
|
+
"@bbclaw/core": "0.1.0",
|
|
22
|
+
"@bbclaw/shared": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/react": "^18.3.12",
|
|
26
|
+
"ink-testing-library": "^4.0.0",
|
|
27
|
+
"tsx": "^4.19.0"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"start": "tsx src/index.ts",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"build": "tsc && chmod +x ./dist/index.js",
|
|
33
|
+
"test": "vitest run --passWithNoTests"
|
|
34
|
+
}
|
|
35
|
+
}
|