Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dist/bundle/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './tools/write_record.js';
export * from './tools/unlink_record.js';
export * from './tools/list_reports.js';
export * from './tools/download_report.js';
export * from './tools/download_file.js';
export * from './tools/get_info.js';
export * from './tools/get_environment.js';
export * from './tools/trace_ui_path.js';
Expand Down
81 changes: 75 additions & 6 deletions dist/bundle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8068,7 +8068,7 @@ function escapeJsonPtr(str) {

/***/ }),

/***/ 6906:
/***/ 4525:
/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) {

// Generated by CoffeeScript 1.10.0
Expand Down Expand Up @@ -8237,7 +8237,7 @@ function escapeJsonPtr(str) {

XMLDTDAttList = __nccwpck_require__(3742);

XMLDTDEntity = __nccwpck_require__(6906);
XMLDTDEntity = __nccwpck_require__(4525);

XMLDTDElement = __nccwpck_require__(6189);

Expand Down Expand Up @@ -8419,7 +8419,7 @@ function escapeJsonPtr(str) {

XMLDTDAttList = __nccwpck_require__(3742);

XMLDTDEntity = __nccwpck_require__(6906);
XMLDTDEntity = __nccwpck_require__(4525);

XMLDTDElement = __nccwpck_require__(6189);

Expand Down Expand Up @@ -9467,7 +9467,7 @@ function escapeJsonPtr(str) {

XMLDTDElement = __nccwpck_require__(6189);

XMLDTDEntity = __nccwpck_require__(6906);
XMLDTDEntity = __nccwpck_require__(4525);

XMLDTDNotation = __nccwpck_require__(7083);

Expand Down Expand Up @@ -9752,7 +9752,7 @@ function escapeJsonPtr(str) {

XMLDTDElement = __nccwpck_require__(6189);

XMLDTDEntity = __nccwpck_require__(6906);
XMLDTDEntity = __nccwpck_require__(4525);

XMLDTDNotation = __nccwpck_require__(7083);

Expand Down Expand Up @@ -38612,6 +38612,18 @@ const DOWNLOAD_REPORT_SCHEMA = {
},
required: ["model", "id", "report_name"],
};
const DOWNLOAD_FILE_SCHEMA = {
type: "object",
properties: {
model: { type: "string", description: "Technical name of the Odoo model containing the binary field (defaults to 'ir.attachment')." },
res_id: { type: "number", description: "Database ID of the record containing the file field." },
field: { type: "string", description: "The technical name of the binary field (e.g., 'datas' or 'raw', defaults to 'datas')." },
destination_path: { type: "string", description: "Absolute local file path where the file should be saved." },
justification: { type: "string", description: "Business justification for downloading this file." },
instance_alias: { type: "string", description: "Optional alias to use an instance other than the active one." },
},
required: ["res_id", "destination_path", "justification"],
};
const GET_INFO_SCHEMA = {
type: "object",
properties: {},
Expand Down Expand Up @@ -40354,6 +40366,56 @@ async function downloadReport(manager, input) {
return destination_path;
}

;// CONCATENATED MODULE: ./src/tools/download_file.ts



/**
* Zod schema for download_file tool input.
*/
const DownloadFileSchema = schemas_object({
model: classic_schemas_string().default('ir.attachment').describe('Technical name of the Odoo model containing the binary field.'),
res_id: classic_coerce_number().describe('Database ID of the record containing the file field.'),
field: classic_schemas_string().default('datas').describe('The technical name of the binary field (e.g., "datas" or "raw").'),
destination_path: classic_schemas_string().describe('Absolute local file path where the file should be saved.'),
justification: classic_schemas_string().min(1).describe('Business justification for downloading this file.'),
instance_alias: classic_schemas_string().optional().describe('Optional alias of the Odoo instance to use.'),
});
/**
* Tool to download any file or attachment from an Odoo database to the local workspace.
* @param manager The InstanceManager instance.
* @param input The DownloadFileInput parameters.
* @returns The absolute path to the saved file.
*/
async function downloadFile(manager, input) {
// Validate and parse the input using the schema to populate defaults
const parsedInput = DownloadFileSchema.parse(input);
const { model, res_id, field, destination_path, justification, instance_alias } = parsedInput;
const client = await manager.getClient(instance_alias);
const audit = await manager.getAudit(instance_alias);
if (!(0,external_path_.isAbsolute)(destination_path)) {
throw new Error(`The destination_path must be an absolute path: ${destination_path}`);
}
// Fetch only the requested binary field
const records = await client.executeKw(model, 'read', [[res_id], [field]]);
if (!records || !records[0]) {
throw new Error(`Record with ID ${res_id} not found in model ${model}`);
}
const record = records[0];
const base64Data = record[field];
if (base64Data === undefined || base64Data === null || base64Data === false || base64Data === '') {
throw new Error(`Field '${field}' is empty or not present on record ${res_id} in model ${model}`);
}
if (typeof base64Data !== 'string') {
throw new Error(`Field '${field}' on record ${res_id} in model ${model} did not return a valid base64-encoded string (got type ${typeof base64Data}).`);
}
const buffer = Buffer.from(base64Data, 'base64');
await (0,promises_.writeFile)(destination_path, buffer);
// Log the action for traceability
await audit.logSystemEvent(`Downloaded file from model '${model}' ID ${res_id} field '${field}' to '${destination_path}': ${justification}`);
return destination_path;
}

;// CONCATENATED MODULE: external "fs"
const external_fs_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs");
var external_fs_default = /*#__PURE__*/__nccwpck_require__.n(external_fs_namespaceObject);
Expand Down Expand Up @@ -40710,6 +40772,7 @@ async function getAuditLog(manager, input) {






// The extension manifest will typically be handled by the Gemini CLI
Expand All @@ -40730,7 +40793,7 @@ async function getAuditLog(manager, input) {

const mcp_server_dirname = external_path_default().dirname((0,external_url_.fileURLToPath)(import.meta.url));
// Read package.json for metadata
let mcp_server_version = "1.5.0";
let mcp_server_version = "1.5.1";
try {
// Try both possible locations (source vs bundled)
const pkgPaths = [
Expand Down Expand Up @@ -40863,6 +40926,12 @@ const toolRegistry = {
description: "Generate and retrieve report data (e.g., PDFs).",
deps: 'manager'
},
download_file: {
handler: downloadFile,
schema: DOWNLOAD_FILE_SCHEMA,
description: "Download any file or attachment from an Odoo database to the local workspace.",
deps: 'manager'
},
get_info: {
handler: getInfo,
schema: GET_INFO_SCHEMA,
Expand Down
2 changes: 1 addition & 1 deletion dist/bundle/package.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"name":"brass-monkey","version":"1.5.0","type":"module","main":"dist/index.js","scripts":{"build":"tsc && npm run bundle","bundle":"ncc build src/mcp-server.ts -o dist/bundle","test":"vitest run","test:watch":"vitest","lint":"eslint src/**/*.ts"},"keywords":["odoo","gemini","gemini-cli","cli","extension","mcp","ai-agent","xml-rpc","erp","crm"],"author":"Actinon","license":"MIT","description":"A high-fidelity Gemini CLI extension and MCP bridge for Odoo ERP/CRM.","dependencies":{"@modelcontextprotocol/sdk":"^1.29.0","keytar":"^7.9.0","xmlrpc":"^1.3.2","zod":"^4.3.6","zod-to-json-schema":"^3.25.2"},"devDependencies":{"@types/keytar":"^4.4.0","@types/node":"^25.6.0","@types/xmlrpc":"^1.3.10","@vercel/ncc":"^0.38.4","esbuild":"^0.28.0","typescript":"^6.0.3","vitest":"^4.1.4"}}
{"name":"brass-monkey","version":"1.5.1","type":"module","main":"dist/index.js","scripts":{"build":"tsc && npm run bundle","bundle":"ncc build src/mcp-server.ts -o dist/bundle","test":"vitest run","test:watch":"vitest","lint":"eslint src/**/*.ts"},"keywords":["odoo","gemini","gemini-cli","cli","extension","mcp","ai-agent","xml-rpc","erp","crm"],"author":"Actinon","license":"MIT","description":"A high-fidelity Gemini CLI extension and MCP bridge for Odoo ERP/CRM.","dependencies":{"@modelcontextprotocol/sdk":"^1.29.0","keytar":"^7.9.0","xmlrpc":"^1.3.2","zod":"^4.3.6","zod-to-json-schema":"^3.25.2"},"devDependencies":{"@types/keytar":"^4.4.0","@types/node":"^25.6.0","@types/xmlrpc":"^1.3.10","@vercel/ncc":"^0.38.4","esbuild":"^0.28.0","typescript":"^6.0.3","vitest":"^4.1.4"}}
21 changes: 21 additions & 0 deletions dist/bundle/tools/download_file.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { z } from 'zod';
import { InstanceManager } from '../services/instance-manager.js';
/**
* Zod schema for download_file tool input.
*/
export declare const DownloadFileSchema: z.ZodObject<{
model: z.ZodDefault<z.ZodString>;
res_id: z.ZodCoercedNumber<unknown>;
field: z.ZodDefault<z.ZodString>;
destination_path: z.ZodString;
justification: z.ZodString;
instance_alias: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type DownloadFileInput = z.infer<typeof DownloadFileSchema>;
/**
* Tool to download any file or attachment from an Odoo database to the local workspace.
* @param manager The InstanceManager instance.
* @param input The DownloadFileInput parameters.
* @returns The absolute path to the saved file.
*/
export declare function downloadFile(manager: InstanceManager, input: DownloadFileInput): Promise<string>;
30 changes: 30 additions & 0 deletions dist/bundle/tools/schemas.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,36 @@ export declare const DOWNLOAD_REPORT_SCHEMA: {
};
required: string[];
};
export declare const DOWNLOAD_FILE_SCHEMA: {
type: string;
properties: {
model: {
type: string;
description: string;
};
res_id: {
type: string;
description: string;
};
field: {
type: string;
description: string;
};
destination_path: {
type: string;
description: string;
};
justification: {
type: string;
description: string;
};
instance_alias: {
type: string;
description: string;
};
};
required: string[];
};
export declare const GET_INFO_SCHEMA: {
type: string;
properties: {};
Expand Down
1 change: 1 addition & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './tools/write_record.js';
export * from './tools/unlink_record.js';
export * from './tools/list_reports.js';
export * from './tools/download_report.js';
export * from './tools/download_file.js';
export * from './tools/get_info.js';
export * from './tools/get_environment.js';
export * from './tools/trace_ui_path.js';
Expand Down
1 change: 1 addition & 0 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion dist/mcp-server.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading