@modelprofile.com/flexharness 10.17.1 → 10.19.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/errors.d.ts +4 -0
- package/dist_ts/errors.js +9 -1
- package/dist_ts/functions.toolpermissions.d.ts +24 -0
- package/dist_ts/functions.toolpermissions.js +38 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/plugins.d.ts +1 -1
- package/dist_ts/plugins.js +1 -1
- package/dist_ts_chat_harness/classes.chatclient.d.ts +95 -0
- package/dist_ts_chat_harness/classes.chatclient.js +263 -0
- package/dist_ts_chat_harness/classes.chatrequesterror.d.ts +4 -0
- package/dist_ts_chat_harness/classes.chatrequesterror.js +8 -0
- package/dist_ts_chat_harness/classes.transcript.d.ts +1 -1
- package/dist_ts_chat_harness/classes.transcript.js +1 -1
- package/dist_ts_chat_harness/functions.intents.d.ts +1 -2
- package/dist_ts_chat_harness/functions.intents.js +1 -1
- package/dist_ts_chat_harness/functions.mapping.d.ts +1 -1
- package/dist_ts_chat_harness/functions.mapping.js +1 -1
- package/dist_ts_chat_harness/index.d.ts +3 -0
- package/dist_ts_chat_harness/index.js +3 -1
- package/dist_ts_chat_harness/interfaces.chat.d.ts +136 -0
- package/dist_ts_chat_harness/interfaces.chat.js +2 -0
- package/dist_ts_chat_harness/plugins.js +1 -1
- package/dist_ts_chat_harness_server/classes.chathub.d.ts +78 -0
- package/dist_ts_chat_harness_server/classes.chathub.js +332 -0
- package/dist_ts_chat_harness_server/classes.chatsubscription.d.ts +55 -0
- package/dist_ts_chat_harness_server/classes.chatsubscription.js +236 -0
- package/dist_ts_chat_harness_server/classes.sessionlists.d.ts +21 -0
- package/dist_ts_chat_harness_server/classes.sessionlists.js +70 -0
- package/dist_ts_chat_harness_server/classes.turngate.d.ts +38 -0
- package/dist_ts_chat_harness_server/classes.turngate.js +207 -0
- package/dist_ts_chat_harness_server/errors.d.ts +5 -0
- package/dist_ts_chat_harness_server/errors.js +9 -0
- package/dist_ts_chat_harness_server/functions.errorprojector.d.ts +11 -0
- package/dist_ts_chat_harness_server/functions.errorprojector.js +32 -0
- package/dist_ts_chat_harness_server/functions.validation.d.ts +18 -0
- package/dist_ts_chat_harness_server/functions.validation.js +88 -0
- package/dist_ts_chat_harness_server/index.d.ts +4 -0
- package/dist_ts_chat_harness_server/index.js +4 -0
- package/dist_ts_chat_harness_server/interfaces.d.ts +67 -0
- package/dist_ts_chat_harness_server/interfaces.js +2 -0
- package/dist_ts_chat_harness_server/plugins.d.ts +7 -0
- package/dist_ts_chat_harness_server/plugins.js +6 -0
- package/dist_ts_chat_harness_typedsocket/classes.clienttransport.d.ts +22 -0
- package/dist_ts_chat_harness_typedsocket/classes.clienttransport.js +55 -0
- package/dist_ts_chat_harness_typedsocket/classes.serverbinding.d.ts +46 -0
- package/dist_ts_chat_harness_typedsocket/classes.serverbinding.js +110 -0
- package/dist_ts_chat_harness_typedsocket/index.d.ts +3 -0
- package/dist_ts_chat_harness_typedsocket/index.js +3 -0
- package/dist_ts_chat_harness_typedsocket/interfaces.requests.d.ts +38 -0
- package/dist_ts_chat_harness_typedsocket/interfaces.requests.js +2 -0
- package/dist_ts_chat_harness_typedsocket/plugins.d.ts +9 -0
- package/dist_ts_chat_harness_typedsocket/plugins.js +8 -0
- package/dist_ts_ocr/ocr.mistral.d.ts +20 -1
- package/dist_ts_ocr/ocr.mistral.js +66 -43
- package/dist_ts_tools/tool.filesystem.d.ts +2 -0
- package/dist_ts_tools/tool.filesystem.js +42 -1
- package/dist_ts_tools_node/classes.workspacereversion.d.ts +48 -0
- package/dist_ts_tools_node/classes.workspacereversion.js +202 -0
- package/dist_ts_tools_node/functions.fileimages.d.ts +22 -0
- package/dist_ts_tools_node/functions.fileimages.js +87 -0
- package/dist_ts_tools_node/index.d.ts +3 -1
- package/dist_ts_tools_node/index.js +2 -1
- package/dist_ts_tools_node/plugins.d.ts +1 -0
- package/dist_ts_tools_node/tool.context.d.ts +20 -0
- package/dist_ts_tools_node/tool.context.js +25 -4
- package/package.json +28 -4
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/errors.ts +10 -0
- package/ts/functions.toolpermissions.ts +60 -0
- package/ts/index.ts +6 -0
- package/ts/plugins.ts +1 -0
- package/ts_chat_harness/classes.chatclient.ts +343 -0
- package/ts_chat_harness/classes.chatrequesterror.ts +7 -0
- package/ts_chat_harness/classes.transcript.ts +1 -1
- package/ts_chat_harness/functions.intents.ts +2 -2
- package/ts_chat_harness/functions.mapping.ts +1 -1
- package/ts_chat_harness/index.ts +20 -0
- package/ts_chat_harness/interfaces.chat.ts +108 -0
- package/ts_chat_harness/readme.md +24 -0
- package/ts_chat_harness_server/classes.chathub.ts +400 -0
- package/ts_chat_harness_server/classes.chatsubscription.ts +257 -0
- package/ts_chat_harness_server/classes.sessionlists.ts +90 -0
- package/ts_chat_harness_server/classes.turngate.ts +247 -0
- package/ts_chat_harness_server/errors.ts +9 -0
- package/ts_chat_harness_server/functions.errorprojector.ts +40 -0
- package/ts_chat_harness_server/functions.validation.ts +104 -0
- package/ts_chat_harness_server/index.ts +19 -0
- package/ts_chat_harness_server/interfaces.ts +92 -0
- package/ts_chat_harness_server/plugins.ts +62 -0
- package/ts_chat_harness_server/readme.md +91 -0
- package/ts_chat_harness_typedsocket/classes.clienttransport.ts +87 -0
- package/ts_chat_harness_typedsocket/classes.serverbinding.ts +172 -0
- package/ts_chat_harness_typedsocket/index.ts +17 -0
- package/ts_chat_harness_typedsocket/interfaces.requests.ts +40 -0
- package/ts_chat_harness_typedsocket/plugins.ts +28 -0
- package/ts_chat_harness_typedsocket/readme.md +57 -0
- package/ts_ocr/ocr.mistral.ts +102 -49
- package/ts_ocr/readme.md +12 -1
- package/ts_tools/readme.md +10 -0
- package/ts_tools/tool.filesystem.ts +55 -0
- package/ts_tools_node/classes.workspacereversion.ts +255 -0
- package/ts_tools_node/functions.fileimages.ts +89 -0
- package/ts_tools_node/index.ts +7 -1
- package/ts_tools_node/plugins.ts +11 -0
- package/ts_tools_node/readme.md +27 -0
- package/ts_tools_node/tool.context.ts +49 -3
|
@@ -75,6 +75,47 @@ export function createFilesystemTools(context, options = {}) {
|
|
|
75
75
|
},
|
|
76
76
|
}),
|
|
77
77
|
};
|
|
78
|
+
if (options.includeEdit === true) {
|
|
79
|
+
tools.edit_file = plugins.tool({
|
|
80
|
+
description: 'Replace an exact snippet of a UTF-8 file in the active workspace. `find` must occur exactly once unless `replaceAll` is set; include enough surrounding text to make it unique. Requires host permission when configured.',
|
|
81
|
+
inputSchema: plugins.z.object({
|
|
82
|
+
path: plugins.z.string().describe('File path'),
|
|
83
|
+
find: plugins.z.string().min(1).describe('Exact text to replace, copied from the current file'),
|
|
84
|
+
replace: plugins.z.string().describe('The text to put in its place'),
|
|
85
|
+
replaceAll: plugins.z
|
|
86
|
+
.boolean()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe('Replace every occurrence instead of exactly one. Default: false'),
|
|
89
|
+
}),
|
|
90
|
+
execute: async ({ path: filePath, find, replace, replaceAll, }, invocationOptions) => {
|
|
91
|
+
if (!context.fs) {
|
|
92
|
+
throw new Error('Filesystem tools are not available in this execution context.');
|
|
93
|
+
}
|
|
94
|
+
const abortSignal = invocationOptions?.abortSignal ?? context.abortSignal;
|
|
95
|
+
const invocation = { toolCallId: invocationOptions?.toolCallId, abortSignal };
|
|
96
|
+
// asked before the file is read: an edit made while the request waits is kept, not overwritten
|
|
97
|
+
await context.requestPermission?.({
|
|
98
|
+
type: 'write',
|
|
99
|
+
title: 'Edit file',
|
|
100
|
+
metadata: { path: filePath },
|
|
101
|
+
toolCallId: invocationOptions?.toolCallId,
|
|
102
|
+
abortSignal,
|
|
103
|
+
});
|
|
104
|
+
const content = await context.fs.readFile(filePath, undefined, invocation);
|
|
105
|
+
const occurrences = content.split(find).length - 1;
|
|
106
|
+
if (occurrences === 0) {
|
|
107
|
+
throw new Error(`The text to replace does not occur in ${filePath}.`);
|
|
108
|
+
}
|
|
109
|
+
if (occurrences > 1 && replaceAll !== true) {
|
|
110
|
+
throw new Error(`The text to replace occurs ${occurrences} times in ${filePath}; include more surrounding text or set replaceAll.`);
|
|
111
|
+
}
|
|
112
|
+
const updated = replaceAll === true ? content.split(find).join(replace) : content.replace(find, () => replace);
|
|
113
|
+
await context.fs.writeFile(filePath, updated, invocation);
|
|
114
|
+
const replaced = replaceAll === true ? occurrences : 1;
|
|
115
|
+
return truncate(`Replaced ${replaced} occurrence${replaced === 1 ? '' : 's'} in ${filePath}`);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
78
119
|
if (options.includeDelete !== false) {
|
|
79
120
|
tools.delete_file = plugins.tool({
|
|
80
121
|
description: 'Delete a file or empty directory in the active workspace. Requires host permission when configured.',
|
|
@@ -103,4 +144,4 @@ export function createFilesystemTools(context, options = {}) {
|
|
|
103
144
|
}
|
|
104
145
|
return tools;
|
|
105
146
|
}
|
|
106
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
147
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbC5maWxlc3lzdGVtLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfdG9vbHMvdG9vbC5maWxlc3lzdGVtLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFDTCxnQkFBZ0IsR0FFakIsTUFBTSxtQkFBbUIsQ0FBQztBQWEzQixNQUFNLFVBQVUscUJBQXFCLENBQUMsT0FBOEIsRUFBRSxVQUF5QyxFQUFFO0lBQy9HLE1BQU0sUUFBUSxHQUFHLENBQUMsTUFBZSxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxFQUFFO1FBQ3JGLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUTtRQUMxQixRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVE7S0FDM0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztJQUNYLE1BQU0sS0FBSyxHQUFvQjtRQUM3QixTQUFTLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQztZQUN0QixXQUFXLEVBQ1QscUdBQXFHO1lBQ3ZHLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztnQkFDNUIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQztnQkFDOUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO3FCQUNqQixNQUFNLEVBQUU7cUJBQ1IsUUFBUSxFQUFFO3FCQUNWLFFBQVEsQ0FBQyxtQ0FBbUMsQ0FBQztnQkFDaEQsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO3FCQUNmLE1BQU0sRUFBRTtxQkFDUixRQUFRLEVBQUU7cUJBQ1YsUUFBUSxDQUFDLGtDQUFrQyxDQUFDO2FBQ2hELENBQUM7WUFDRixPQUFPLEVBQUUsS0FBSyxFQUNaLEVBQ0UsSUFBSSxFQUFFLFFBQVEsRUFDZCxTQUFTLEVBQ1QsT0FBTyxHQUtSLEVBQ0QsaUJBQXlELEVBQ3pELEVBQUU7Z0JBQ0YsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFDaEIsTUFBTSxJQUFJLEtBQUssQ0FBQywrREFBK0QsQ0FBQyxDQUFDO2dCQUNuRixDQUFDO2dCQUNELE9BQU8sUUFBUSxDQUFDLE1BQU0sT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxFQUFFO29CQUMxRSxVQUFVLEVBQUUsaUJBQWlCLEVBQUUsVUFBVTtvQkFDekMsV0FBVyxFQUFFLGlCQUFpQixFQUFFLFdBQVcsSUFBSSxPQUFPLENBQUMsV0FBVztpQkFDbkUsQ0FBQyxDQUFDLENBQUM7WUFDTixDQUFDO1NBQ0YsQ0FBQztRQUVGLFVBQVUsRUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDO1lBQ3ZCLFdBQVcsRUFDVCw4SkFBOEo7WUFDaEssV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO2dCQUM1QixJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDO2dCQUM5QyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsZ0NBQWdDLENBQUM7YUFDdkUsQ0FBQztZQUNGLE9BQU8sRUFBRSxLQUFLLEVBQ1osRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBcUMsRUFDOUQsaUJBQXlELEVBQ3pELEVBQUU7Z0JBQ0YsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFDaEIsTUFBTSxJQUFJLEtBQUssQ0FBQywrREFBK0QsQ0FBQyxDQUFDO2dCQUNuRixDQUFDO2dCQUNELE1BQU0sV0FBVyxHQUFHLGlCQUFpQixFQUFFLFdBQVcsSUFBSSxPQUFPLENBQUMsV0FBVyxDQUFDO2dCQUMxRSxNQUFNLE9BQU8sQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO29CQUNoQyxJQUFJLEVBQUUsT0FBTztvQkFDYixLQUFLLEVBQUUsWUFBWTtvQkFDbkIsUUFBUSxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLEVBQUU7b0JBQ3ZFLFVBQVUsRUFBRSxpQkFBaUIsRUFBRSxVQUFVO29CQUN6QyxXQUFXO2lCQUNaLENBQUMsQ0FBQztnQkFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLE9BQU8sQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxPQUFPLEVBQUU7b0JBQzNELFVBQVUsRUFBRSxpQkFBaUIsRUFBRSxVQUFVO29CQUN6QyxXQUFXO2lCQUNaLENBQUMsQ0FBQztnQkFDSCxPQUFPLFFBQVEsQ0FBQyxNQUFNLElBQUksV0FBVyxNQUFNLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsYUFBYSxRQUFRLEVBQUUsQ0FBQyxDQUFDO1lBQ2xHLENBQUM7U0FDRixDQUFDO1FBRUYsY0FBYyxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUM7WUFDM0IsV0FBVyxFQUFFLDhHQUE4RztZQUMzSCxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7Z0JBQzVCLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsd0JBQXdCLENBQUM7Z0JBQ3hFLFNBQVMsRUFBRSxPQUFPLENBQUMsQ0FBQztxQkFDakIsT0FBTyxFQUFFO3FCQUNULFFBQVEsRUFBRTtxQkFDVixRQUFRLENBQUMsa0NBQWtDLENBQUM7YUFDaEQsQ0FBQztZQUNGLE9BQU8sRUFBRSxLQUFLLEVBQ1osRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLFNBQVMsRUFBeUMsRUFDekUsaUJBQXlELEVBQ3pELEVBQUU7Z0JBQ0YsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFDaEIsTUFBTSxJQUFJLEtBQUssQ0FBQywrREFBK0QsQ0FBQyxDQUFDO2dCQUNuRixDQUFDO2dCQUNELE1BQU0sTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLEVBQUUsQ0FBQyxhQUFhLENBQUMsYUFBYSxFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUU7b0JBQzFFLFVBQVUsRUFBRSxpQkFBaUIsRUFBRSxVQUFVO29CQUN6QyxXQUFXLEVBQUUsaUJBQWlCLEVBQUUsV0FBVyxJQUFJLE9BQU8sQ0FBQyxXQUFXO2lCQUNuRSxDQUFDLENBQUM7Z0JBQ0gsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDdEUsQ0FBQztTQUNGLENBQUM7S0FDSCxDQUFDO0lBRUYsSUFBSSxPQUFPLENBQUMsV0FBVyxLQUFLLElBQUksRUFBRSxDQUFDO1FBQ2pDLEtBQUssQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQztZQUM3QixXQUFXLEVBQ1QsMk5BQTJOO1lBQzdOLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztnQkFDNUIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQztnQkFDOUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxxREFBcUQsQ0FBQztnQkFDL0YsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLDhCQUE4QixDQUFDO2dCQUNwRSxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUM7cUJBQ2xCLE9BQU8sRUFBRTtxQkFDVCxRQUFRLEVBQUU7cUJBQ1YsUUFBUSxDQUFDLGlFQUFpRSxDQUFDO2FBQy9FLENBQUM7WUFDRixPQUFPLEVBQUUsS0FBSyxFQUNaLEVBQ0UsSUFBSSxFQUFFLFFBQVEsRUFDZCxJQUFJLEVBQ0osT0FBTyxFQUNQLFVBQVUsR0FDNEQsRUFDeEUsaUJBQXlELEVBQ3pELEVBQUU7Z0JBQ0YsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFDaEIsTUFBTSxJQUFJLEtBQUssQ0FBQywrREFBK0QsQ0FBQyxDQUFDO2dCQUNuRixDQUFDO2dCQUNELE1BQU0sV0FBVyxHQUFHLGlCQUFpQixFQUFFLFdBQVcsSUFBSSxPQUFPLENBQUMsV0FBVyxDQUFDO2dCQUMxRSxNQUFNLFVBQVUsR0FBRyxFQUFFLFVBQVUsRUFBRSxpQkFBaUIsRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLENBQUM7Z0JBQzlFLCtGQUErRjtnQkFDL0YsTUFBTSxPQUFPLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztvQkFDaEMsSUFBSSxFQUFFLE9BQU87b0JBQ2IsS0FBSyxFQUFFLFdBQVc7b0JBQ2xCLFFBQVEsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUU7b0JBQzVCLFVBQVUsRUFBRSxpQkFBaUIsRUFBRSxVQUFVO29CQUN6QyxXQUFXO2lCQUNaLENBQUMsQ0FBQztnQkFDSCxNQUFNLE9BQU8sR0FBRyxNQUFNLE9BQU8sQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQzNFLE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztnQkFDbkQsSUFBSSxXQUFXLEtBQUssQ0FBQyxFQUFFLENBQUM7b0JBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLFFBQVEsR0FBRyxDQUFDLENBQUM7Z0JBQ3hFLENBQUM7Z0JBQ0QsSUFBSSxXQUFXLEdBQUcsQ0FBQyxJQUFJLFVBQVUsS0FBSyxJQUFJLEVBQUUsQ0FBQztvQkFDM0MsTUFBTSxJQUFJLEtBQUssQ0FDYiw4QkFBOEIsV0FBVyxhQUFhLFFBQVEsb0RBQW9ELENBQ25ILENBQUM7Z0JBQ0osQ0FBQztnQkFDRCxNQUFNLE9BQU8sR0FBRyxVQUFVLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLENBQUM7Z0JBQy9HLE1BQU0sT0FBTyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsUUFBUSxFQUFFLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDMUQsTUFBTSxRQUFRLEdBQUcsVUFBVSxLQUFLLElBQUksQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ3ZELE9BQU8sUUFBUSxDQUFDLFlBQVksUUFBUSxjQUFjLFFBQVEsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLFFBQVEsRUFBRSxDQUFDLENBQUM7WUFDaEcsQ0FBQztTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxJQUFJLE9BQU8sQ0FBQyxhQUFhLEtBQUssS0FBSyxFQUFFLENBQUM7UUFDcEMsS0FBSyxDQUFDLFdBQVcsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDO1lBQy9CLFdBQVcsRUFBRSxxR0FBcUc7WUFDbEgsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO2dCQUM1QixJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQUM7YUFDcEQsQ0FBQztZQUNGLE9BQU8sRUFBRSxLQUFLLEVBQ1osRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFvQixFQUN0QyxpQkFBeUQsRUFDekQsRUFBRTtnQkFDRixJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsRUFBRSxVQUFVLEVBQUUsQ0FBQztvQkFDNUIsTUFBTSxJQUFJLEtBQUssQ0FBQyw0REFBNEQsQ0FBQyxDQUFDO2dCQUNoRixDQUFDO2dCQUNELE1BQU0sV0FBVyxHQUFHLGlCQUFpQixFQUFFLFdBQVcsSUFBSSxPQUFPLENBQUMsV0FBVyxDQUFDO2dCQUMxRSxNQUFNLE9BQU8sQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO29CQUNoQyxJQUFJLEVBQUUsUUFBUTtvQkFDZCxLQUFLLEVBQUUsYUFBYTtvQkFDcEIsUUFBUSxFQUFFLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRTtvQkFDOUIsVUFBVSxFQUFFLGlCQUFpQixFQUFFLFVBQVU7b0JBQ3pDLFdBQVc7aUJBQ1osQ0FBQyxDQUFDO2dCQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsVUFBVSxFQUFFO29CQUNyRCxVQUFVLEVBQUUsaUJBQWlCLEVBQUUsVUFBVTtvQkFDekMsV0FBVztpQkFDWixDQUFDLENBQUM7Z0JBQ0gsT0FBTyxRQUFRLENBQUMsTUFBTSxJQUFJLFdBQVcsVUFBVSxFQUFFLENBQUMsQ0FBQztZQUNyRCxDQUFDO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQyJ9
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IToolFileChangeObserver } from './tool.context.js';
|
|
3
|
+
export interface IInMemoryWorkspaceReversionOptions {
|
|
4
|
+
/** The workspace a revertible turn names in the session's reversion info. */
|
|
5
|
+
workspace: plugins.IFlexAffectedWorkspace;
|
|
6
|
+
/**
|
|
7
|
+
* The bytes of file images one turn may hold. A turn that changes more is recorded as
|
|
8
|
+
* nonrevertible (`capture-too-large`) and its images are dropped. Default: 64 MiB.
|
|
9
|
+
*/
|
|
10
|
+
maxCaptureBytes?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Turn reversion for the local filesystem tools, held in memory: FlexHarness `/undo` and `/redo`
|
|
14
|
+
* move a turn's file changes together with its transcript.
|
|
15
|
+
*
|
|
16
|
+
* Give each run's `createLocalToolExecutionContext` the observer `observerFor(context.runId)`. The
|
|
17
|
+
* first change of a file in a turn records what the file held before; `finalize` records what it
|
|
18
|
+
* holds after. Undo puts the before images back and redo the after images, each only while every
|
|
19
|
+
* file still holds what the other direction left, so a later edit of the user is never overwritten.
|
|
20
|
+
* A turn that ran a shell command is nonrevertible, since the command may have changed files no
|
|
21
|
+
* observer saw.
|
|
22
|
+
*
|
|
23
|
+
* Captures live as long as the process. A capture FlexHarness asks about after a restart is
|
|
24
|
+
* `missing`, and undoing past it fails without changing anything. Only root-session runs are
|
|
25
|
+
* captured; a change of a run without a prepared capture is refused.
|
|
26
|
+
*/
|
|
27
|
+
export declare class InMemoryWorkspaceReversion<TScope = unknown> implements plugins.IFlexTurnReversionProviderV2<TScope> {
|
|
28
|
+
readonly protocolVersion = 2;
|
|
29
|
+
private readonly workspace;
|
|
30
|
+
private readonly maxCaptureBytes;
|
|
31
|
+
private readonly captures;
|
|
32
|
+
private readonly captureIdsByRun;
|
|
33
|
+
constructor(optionsArg: IInMemoryWorkspaceReversionOptions);
|
|
34
|
+
/** The observer of one run's file changes and shell commands. */
|
|
35
|
+
observerFor(runIdArg: string): IToolFileChangeObserver;
|
|
36
|
+
prepare(contextArg: plugins.IFlexTurnReversionBaseContext<TScope>): void;
|
|
37
|
+
inspectCapture(contextArg: plugins.IFlexTurnReversionBaseContext<TScope>): plugins.TFlexTurnReversionCaptureInspectionV2;
|
|
38
|
+
finalize(contextArg: plugins.IFlexTurnReversionBaseContext<TScope>): Promise<plugins.TFlexTurnReversionFinalizedOutcome>;
|
|
39
|
+
apply(contextArg: plugins.IFlexTurnReversionApplyContext<TScope>): Promise<void>;
|
|
40
|
+
inspectApply(contextArg: plugins.IFlexTurnReversionApplyContext<TScope>): plugins.IFlexTurnReversionApplyInspection;
|
|
41
|
+
release(contextArg: plugins.IFlexTurnReversionReleaseContext<TScope>): void;
|
|
42
|
+
private recordChange;
|
|
43
|
+
private markNonrevertible;
|
|
44
|
+
private rollBack;
|
|
45
|
+
private requirePreparedCapture;
|
|
46
|
+
private requireCapture;
|
|
47
|
+
private captureOf;
|
|
48
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { fileImageBytes, fileImagesEqual, missingAncestors, readFileImage, removeEmptyDirectories, writeFileImage, } from './functions.fileimages.js';
|
|
3
|
+
const DEFAULT_MAX_CAPTURE_BYTES = 64 * 1024 * 1024;
|
|
4
|
+
const isCaptureReference = (valueArg) => !!valueArg
|
|
5
|
+
&& typeof valueArg === 'object'
|
|
6
|
+
&& !Array.isArray(valueArg)
|
|
7
|
+
&& typeof valueArg.captureId === 'string';
|
|
8
|
+
/**
|
|
9
|
+
* Turn reversion for the local filesystem tools, held in memory: FlexHarness `/undo` and `/redo`
|
|
10
|
+
* move a turn's file changes together with its transcript.
|
|
11
|
+
*
|
|
12
|
+
* Give each run's `createLocalToolExecutionContext` the observer `observerFor(context.runId)`. The
|
|
13
|
+
* first change of a file in a turn records what the file held before; `finalize` records what it
|
|
14
|
+
* holds after. Undo puts the before images back and redo the after images, each only while every
|
|
15
|
+
* file still holds what the other direction left, so a later edit of the user is never overwritten.
|
|
16
|
+
* A turn that ran a shell command is nonrevertible, since the command may have changed files no
|
|
17
|
+
* observer saw.
|
|
18
|
+
*
|
|
19
|
+
* Captures live as long as the process. A capture FlexHarness asks about after a restart is
|
|
20
|
+
* `missing`, and undoing past it fails without changing anything. Only root-session runs are
|
|
21
|
+
* captured; a change of a run without a prepared capture is refused.
|
|
22
|
+
*/
|
|
23
|
+
export class InMemoryWorkspaceReversion {
|
|
24
|
+
protocolVersion = 2;
|
|
25
|
+
workspace;
|
|
26
|
+
maxCaptureBytes;
|
|
27
|
+
captures = new Map();
|
|
28
|
+
captureIdsByRun = new Map();
|
|
29
|
+
constructor(optionsArg) {
|
|
30
|
+
this.workspace = { id: optionsArg.workspace.id, label: optionsArg.workspace.label };
|
|
31
|
+
this.maxCaptureBytes = optionsArg.maxCaptureBytes ?? DEFAULT_MAX_CAPTURE_BYTES;
|
|
32
|
+
if (!Number.isSafeInteger(this.maxCaptureBytes) || this.maxCaptureBytes <= 0) {
|
|
33
|
+
throw new Error('maxCaptureBytes must be a positive integer.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** The observer of one run's file changes and shell commands. */
|
|
37
|
+
observerFor(runIdArg) {
|
|
38
|
+
return {
|
|
39
|
+
beforeFileChange: (changeArg) => this.recordChange(runIdArg, changeArg),
|
|
40
|
+
beforeShellCommand: () => {
|
|
41
|
+
this.markNonrevertible(this.requirePreparedCapture(runIdArg), 'shell-command');
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
prepare(contextArg) {
|
|
46
|
+
contextArg.signal.throwIfAborted();
|
|
47
|
+
if (this.captures.has(contextArg.captureId))
|
|
48
|
+
return;
|
|
49
|
+
this.captures.set(contextArg.captureId, {
|
|
50
|
+
captureId: contextArg.captureId,
|
|
51
|
+
runId: contextArg.runId,
|
|
52
|
+
state: 'prepared',
|
|
53
|
+
files: new Map(),
|
|
54
|
+
bytes: 0,
|
|
55
|
+
appliedOperations: new Set(),
|
|
56
|
+
uncertainOperations: new Set(),
|
|
57
|
+
});
|
|
58
|
+
this.captureIdsByRun.set(contextArg.runId, contextArg.captureId);
|
|
59
|
+
}
|
|
60
|
+
inspectCapture(contextArg) {
|
|
61
|
+
const capture = this.captures.get(contextArg.captureId);
|
|
62
|
+
if (!capture)
|
|
63
|
+
return { status: 'missing' };
|
|
64
|
+
return capture.outcome ? { status: 'finalized', outcome: capture.outcome } : { status: 'prepared' };
|
|
65
|
+
}
|
|
66
|
+
async finalize(contextArg) {
|
|
67
|
+
contextArg.signal.throwIfAborted();
|
|
68
|
+
const capture = this.requireCapture(contextArg.captureId);
|
|
69
|
+
if (capture.outcome)
|
|
70
|
+
return capture.outcome;
|
|
71
|
+
const reference = { captureId: capture.captureId };
|
|
72
|
+
if (!capture.nonrevertibleReason) {
|
|
73
|
+
for (const [filePath, record] of capture.files) {
|
|
74
|
+
record.after = await readFileImage(filePath);
|
|
75
|
+
if (fileImagesEqual(record.before, record.after))
|
|
76
|
+
capture.files.delete(filePath);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
capture.state = 'finalized';
|
|
80
|
+
if (capture.nonrevertibleReason) {
|
|
81
|
+
capture.outcome = {
|
|
82
|
+
disposition: 'nonrevertible',
|
|
83
|
+
reference: { ...reference },
|
|
84
|
+
reasonCode: capture.nonrevertibleReason,
|
|
85
|
+
affectedWorkspaces: [this.workspace],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
else if (capture.files.size === 0) {
|
|
89
|
+
capture.outcome = { disposition: 'no-change', reference: { ...reference } };
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
capture.outcome = { disposition: 'revertible', reference: { ...reference }, affectedWorkspaces: [this.workspace] };
|
|
93
|
+
}
|
|
94
|
+
this.captureIdsByRun.delete(capture.runId);
|
|
95
|
+
return capture.outcome;
|
|
96
|
+
}
|
|
97
|
+
async apply(contextArg) {
|
|
98
|
+
contextArg.signal.throwIfAborted();
|
|
99
|
+
const capture = this.captureOf(contextArg.reference);
|
|
100
|
+
if (capture.outcome?.disposition !== 'revertible') {
|
|
101
|
+
throw new Error('This turn has no revertible workspace changes.');
|
|
102
|
+
}
|
|
103
|
+
const records = [...capture.files.entries()];
|
|
104
|
+
const expected = (recordArg) => (contextArg.direction === 'undo' ? recordArg.after : recordArg.before);
|
|
105
|
+
const target = (recordArg) => (contextArg.direction === 'undo' ? recordArg.before : recordArg.after);
|
|
106
|
+
for (const [filePath, record] of records) {
|
|
107
|
+
if (!fileImagesEqual(await readFileImage(filePath), expected(record))) {
|
|
108
|
+
throw new Error(`"${filePath}" changed after the turn; its changes were not ${contextArg.direction === 'undo' ? 'undone' : 'redone'}.`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const written = [];
|
|
112
|
+
try {
|
|
113
|
+
for (const entry of records) {
|
|
114
|
+
await writeFileImage(entry[0], target(entry[1]));
|
|
115
|
+
written.push(entry);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
await this.rollBack(written, expected, contextArg.operationId, capture);
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
if (contextArg.direction === 'undo') {
|
|
123
|
+
for (const [, record] of records)
|
|
124
|
+
await removeEmptyDirectories(record.createdDirectories);
|
|
125
|
+
}
|
|
126
|
+
capture.appliedOperations.add(contextArg.operationId);
|
|
127
|
+
}
|
|
128
|
+
inspectApply(contextArg) {
|
|
129
|
+
const capture = this.captures.get(isCaptureReference(contextArg.reference) ? contextArg.reference.captureId : '');
|
|
130
|
+
if (!capture)
|
|
131
|
+
return { status: 'not-applied' };
|
|
132
|
+
if (capture.appliedOperations.has(contextArg.operationId))
|
|
133
|
+
return { status: 'applied' };
|
|
134
|
+
if (capture.uncertainOperations.has(contextArg.operationId))
|
|
135
|
+
return { status: 'unknown' };
|
|
136
|
+
return { status: 'not-applied' };
|
|
137
|
+
}
|
|
138
|
+
release(contextArg) {
|
|
139
|
+
const captureId = isCaptureReference(contextArg.reference) ? contextArg.reference.captureId : contextArg.captureId;
|
|
140
|
+
const capture = this.captures.get(captureId);
|
|
141
|
+
if (!capture)
|
|
142
|
+
return;
|
|
143
|
+
this.captures.delete(captureId);
|
|
144
|
+
if (this.captureIdsByRun.get(capture.runId) === captureId)
|
|
145
|
+
this.captureIdsByRun.delete(capture.runId);
|
|
146
|
+
}
|
|
147
|
+
async recordChange(runIdArg, changeArg) {
|
|
148
|
+
const capture = this.requirePreparedCapture(runIdArg);
|
|
149
|
+
if (capture.nonrevertibleReason || capture.files.has(changeArg.path))
|
|
150
|
+
return;
|
|
151
|
+
const before = await readFileImage(changeArg.path);
|
|
152
|
+
const createdDirectories = before.kind === 'absent' && changeArg.kind === 'write'
|
|
153
|
+
? await missingAncestors(changeArg.path)
|
|
154
|
+
: [];
|
|
155
|
+
if (capture.files.has(changeArg.path))
|
|
156
|
+
return;
|
|
157
|
+
capture.bytes += fileImageBytes(before);
|
|
158
|
+
if (capture.bytes > this.maxCaptureBytes) {
|
|
159
|
+
this.markNonrevertible(capture, 'capture-too-large');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
capture.files.set(changeArg.path, { before, createdDirectories });
|
|
163
|
+
}
|
|
164
|
+
markNonrevertible(captureArg, reasonArg) {
|
|
165
|
+
captureArg.nonrevertibleReason ??= reasonArg;
|
|
166
|
+
captureArg.files.clear();
|
|
167
|
+
captureArg.bytes = 0;
|
|
168
|
+
}
|
|
169
|
+
async rollBack(writtenArg, expectedArg, operationIdArg, captureArg) {
|
|
170
|
+
try {
|
|
171
|
+
for (const [filePath, record] of writtenArg.reverse())
|
|
172
|
+
await writeFileImage(filePath, expectedArg(record));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// the workspace holds part of the change; FlexHarness learns that the outcome is unknown
|
|
176
|
+
captureArg.uncertainOperations.add(operationIdArg);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
requirePreparedCapture(runIdArg) {
|
|
180
|
+
const captureId = this.captureIdsByRun.get(runIdArg);
|
|
181
|
+
const capture = captureId ? this.captures.get(captureId) : undefined;
|
|
182
|
+
if (!capture || capture.state !== 'prepared') {
|
|
183
|
+
throw new Error(`No workspace capture is open for run "${runIdArg}"; the change was refused.`);
|
|
184
|
+
}
|
|
185
|
+
return capture;
|
|
186
|
+
}
|
|
187
|
+
requireCapture(captureIdArg) {
|
|
188
|
+
const capture = this.captures.get(captureIdArg);
|
|
189
|
+
if (!capture)
|
|
190
|
+
throw new Error(`The workspace capture "${captureIdArg}" is not held.`);
|
|
191
|
+
return capture;
|
|
192
|
+
}
|
|
193
|
+
captureOf(referenceArg) {
|
|
194
|
+
if (!isCaptureReference(referenceArg))
|
|
195
|
+
throw new Error('The workspace capture reference is malformed.');
|
|
196
|
+
const capture = this.captures.get(referenceArg.captureId);
|
|
197
|
+
if (!capture)
|
|
198
|
+
throw new Error('The workspace changes of this turn are no longer held in memory.');
|
|
199
|
+
return capture;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy53b3Jrc3BhY2VyZXZlcnNpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c190b29sc19ub2RlL2NsYXNzZXMud29ya3NwYWNlcmV2ZXJzaW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBRXhDLE9BQU8sRUFDTCxjQUFjLEVBQ2QsZUFBZSxFQUNmLGdCQUFnQixFQUNoQixhQUFhLEVBQ2Isc0JBQXNCLEVBQ3RCLGNBQWMsR0FFZixNQUFNLDJCQUEyQixDQUFDO0FBcUNuQyxNQUFNLHlCQUF5QixHQUFHLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBRW5ELE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxRQUE0QixFQUFzRCxFQUFFLENBQzlHLENBQUMsQ0FBQyxRQUFRO09BQ1AsT0FBTyxRQUFRLEtBQUssUUFBUTtPQUM1QixDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDO09BQ3hCLE9BQU8sUUFBUSxDQUFDLFNBQVMsS0FBSyxRQUFRLENBQUM7QUFFNUM7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxNQUFNLE9BQU8sMEJBQTBCO0lBQ3JCLGVBQWUsR0FBRyxDQUFDLENBQUM7SUFFbkIsU0FBUyxDQUFpQztJQUMxQyxlQUFlLENBQVM7SUFDeEIsUUFBUSxHQUFHLElBQUksR0FBRyxFQUFvQixDQUFDO0lBQ3ZDLGVBQWUsR0FBRyxJQUFJLEdBQUcsRUFBa0IsQ0FBQztJQUU3RCxZQUFZLFVBQThDO1FBQ3hELElBQUksQ0FBQyxTQUFTLEdBQUcsRUFBRSxFQUFFLEVBQUUsVUFBVSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFFLFVBQVUsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDcEYsSUFBSSxDQUFDLGVBQWUsR0FBRyxVQUFVLENBQUMsZUFBZSxJQUFJLHlCQUF5QixDQUFDO1FBQy9FLElBQUksQ0FBQyxN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
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** What one path holds at a moment: nothing, a regular file, or a symbolic link. */
|
|
2
|
+
export type TFileImage = {
|
|
3
|
+
kind: 'absent';
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'file';
|
|
6
|
+
bytes: Buffer;
|
|
7
|
+
mode: number;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'symlink';
|
|
10
|
+
target: string;
|
|
11
|
+
};
|
|
12
|
+
/** Reads what the path holds, without following a final symbolic link. */
|
|
13
|
+
export declare const readFileImage: (pathArg: string) => Promise<TFileImage>;
|
|
14
|
+
/** The bytes an image keeps in memory. */
|
|
15
|
+
export declare const fileImageBytes: (imageArg: TFileImage) => number;
|
|
16
|
+
export declare const fileImagesEqual: (leftArg: TFileImage, rightArg: TFileImage) => boolean;
|
|
17
|
+
/** Puts the image at the path, creating parent directories as needed. */
|
|
18
|
+
export declare const writeFileImage: (pathArg: string, imageArg: TFileImage) => Promise<void>;
|
|
19
|
+
/** The ancestors of the path that do not exist yet, deepest first: a write would create them. */
|
|
20
|
+
export declare const missingAncestors: (pathArg: string) => Promise<string[]>;
|
|
21
|
+
/** Removes the directories, deepest first, as far as they are empty. */
|
|
22
|
+
export declare const removeEmptyDirectories: (directoriesArg: readonly string[]) => Promise<void>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
const isMissing = (error) => {
|
|
3
|
+
const code = error.code;
|
|
4
|
+
return code === 'ENOENT' || code === 'ENOTDIR';
|
|
5
|
+
};
|
|
6
|
+
/** Reads what the path holds, without following a final symbolic link. */
|
|
7
|
+
export const readFileImage = async (pathArg) => {
|
|
8
|
+
let stats;
|
|
9
|
+
try {
|
|
10
|
+
stats = await plugins.fs.promises.lstat(pathArg);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (isMissing(error))
|
|
14
|
+
return { kind: 'absent' };
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
if (stats.isSymbolicLink())
|
|
18
|
+
return { kind: 'symlink', target: await plugins.fs.promises.readlink(pathArg) };
|
|
19
|
+
if (stats.isFile())
|
|
20
|
+
return { kind: 'file', bytes: await plugins.fs.promises.readFile(pathArg), mode: stats.mode & 0o7777 };
|
|
21
|
+
throw new Error(`"${pathArg}" is neither a file nor a symbolic link.`);
|
|
22
|
+
};
|
|
23
|
+
/** The bytes an image keeps in memory. */
|
|
24
|
+
export const fileImageBytes = (imageArg) => imageArg.kind === 'file'
|
|
25
|
+
? imageArg.bytes.byteLength
|
|
26
|
+
: imageArg.kind === 'symlink'
|
|
27
|
+
? Buffer.byteLength(imageArg.target)
|
|
28
|
+
: 0;
|
|
29
|
+
export const fileImagesEqual = (leftArg, rightArg) => {
|
|
30
|
+
if (leftArg.kind === 'absent' || rightArg.kind === 'absent')
|
|
31
|
+
return leftArg.kind === rightArg.kind;
|
|
32
|
+
if (leftArg.kind === 'symlink' || rightArg.kind === 'symlink') {
|
|
33
|
+
return leftArg.kind === 'symlink' && rightArg.kind === 'symlink' && leftArg.target === rightArg.target;
|
|
34
|
+
}
|
|
35
|
+
return leftArg.bytes.equals(rightArg.bytes);
|
|
36
|
+
};
|
|
37
|
+
/** Puts the image at the path, creating parent directories as needed. */
|
|
38
|
+
export const writeFileImage = async (pathArg, imageArg) => {
|
|
39
|
+
if (imageArg.kind === 'absent') {
|
|
40
|
+
await plugins.fs.promises.rm(pathArg, { force: true });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
await plugins.fs.promises.mkdir(plugins.path.dirname(pathArg), { recursive: true });
|
|
44
|
+
if (imageArg.kind === 'symlink') {
|
|
45
|
+
await plugins.fs.promises.rm(pathArg, { force: true });
|
|
46
|
+
await plugins.fs.promises.symlink(imageArg.target, pathArg);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if ((await readFileImage(pathArg)).kind === 'symlink')
|
|
50
|
+
await plugins.fs.promises.rm(pathArg);
|
|
51
|
+
await plugins.fs.promises.writeFile(pathArg, imageArg.bytes);
|
|
52
|
+
await plugins.fs.promises.chmod(pathArg, imageArg.mode);
|
|
53
|
+
};
|
|
54
|
+
/** The ancestors of the path that do not exist yet, deepest first: a write would create them. */
|
|
55
|
+
export const missingAncestors = async (pathArg) => {
|
|
56
|
+
const missing = [];
|
|
57
|
+
let current = plugins.path.dirname(pathArg);
|
|
58
|
+
while (current !== plugins.path.dirname(current)) {
|
|
59
|
+
try {
|
|
60
|
+
await plugins.fs.promises.lstat(current);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (!isMissing(error))
|
|
65
|
+
throw error;
|
|
66
|
+
missing.push(current);
|
|
67
|
+
current = plugins.path.dirname(current);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return missing;
|
|
71
|
+
};
|
|
72
|
+
/** Removes the directories, deepest first, as far as they are empty. */
|
|
73
|
+
export const removeEmptyDirectories = async (directoriesArg) => {
|
|
74
|
+
for (const directory of directoriesArg) {
|
|
75
|
+
try {
|
|
76
|
+
await plugins.fs.promises.rmdir(directory);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const code = error.code;
|
|
80
|
+
if (code === 'ENOTEMPTY' || code === 'EEXIST')
|
|
81
|
+
return;
|
|
82
|
+
if (!isMissing(error))
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnVuY3Rpb25zLmZpbGVpbWFnZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c190b29sc19ub2RlL2Z1bmN0aW9ucy5maWxlaW1hZ2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBUXhDLE1BQU0sU0FBUyxHQUFHLENBQUMsS0FBYyxFQUFXLEVBQUU7SUFDNUMsTUFBTSxJQUFJLEdBQUksS0FBK0IsQ0FBQyxJQUFJLENBQUM7SUFDbkQsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLElBQUksS0FBSyxTQUFTLENBQUM7QUFDakQsQ0FBQyxDQUFDO0FBRUYsMEVBQTBFO0FBQzFFLE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxLQUFLLEVBQUUsT0FBZSxFQUF1QixFQUFFO0lBQzFFLElBQUksS0FBdUIsQ0FBQztJQUM1QixJQUFJLENBQUM7UUFDSCxLQUFLLEdBQUcsTUFBTSxPQUFPLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUM7WUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDO1FBQ2hELE1BQU0sS0FBSyxDQUFDO0lBQ2QsQ0FBQztJQUNELElBQUksS0FBSyxDQUFDLGNBQWMsRUFBRTtRQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLE9BQU8sQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO0lBQzVHLElBQUksS0FBSyxDQUFDLE1BQU0sRUFBRTtRQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxNQUFNLE9BQU8sQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEs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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { createLocalToolExecutionContext } from './tool.context.js';
|
|
2
|
-
export type { ILocalToolExecutionContextOptions } from './tool.context.js';
|
|
2
|
+
export type { ILocalToolExecutionContextOptions, IToolFileChange, IToolFileChangeObserver, } from './tool.context.js';
|
|
3
|
+
export { InMemoryWorkspaceReversion } from './classes.workspacereversion.js';
|
|
4
|
+
export type { IInMemoryWorkspaceReversionOptions } from './classes.workspacereversion.js';
|
|
3
5
|
export { FileToolJobStore } from './tool.persistence.js';
|
|
4
6
|
export type { IFileToolJobStoreOptions } from './tool.persistence.js';
|
|
5
7
|
export * from './tools.local.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createLocalToolExecutionContext } from './tool.context.js';
|
|
2
|
+
export { InMemoryWorkspaceReversion } from './classes.workspacereversion.js';
|
|
2
3
|
export { FileToolJobStore } from './tool.persistence.js';
|
|
3
4
|
export * from './tools.local.js';
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c190b29sc19ub2RlL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBTXBFLE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTdFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRXpELGNBQWMsa0JBQWtCLENBQUMifQ==
|
|
@@ -11,3 +11,4 @@ import { createFilesystemTools, createShellTools } from '@modelprofile.com/flexh
|
|
|
11
11
|
export { createFilesystemTools, createShellTools };
|
|
12
12
|
export type { ICreateFilesystemToolsOptions, ICreateShellToolsOptions, ToolSet } from '@modelprofile.com/flexharness/tools';
|
|
13
13
|
export type { IBrowserToolInput, IShellCommand, IShellFinishedResult, IShellStartedResult, IToolBrowserContext, IToolBrowserRunOptions, IToolExecutionContext, IToolFilesystemContext, IToolFilesystemListOptions, IToolFilesystemReadOptions, IToolInvocationOptions, IToolJobContext, IToolJobEvent, IToolJobHandle, IToolJobOutputEvent, IToolJobRequest, IToolJobSnapshot, IToolJobState, IToolJobStore, IToolPermissionRequest, IToolRunOptions, IToolShellContext, IToolShellResult, TBrowserToolAction, TToolJobEvent, TToolJobEventListener, } from '@modelprofile.com/flexharness-agent';
|
|
14
|
+
export type { IFlexAffectedWorkspace, IFlexTurnReversionApplyContext, IFlexTurnReversionApplyInspection, IFlexTurnReversionBaseContext, IFlexTurnReversionProviderV2, IFlexTurnReversionReleaseContext, TFlexTurnReversionCaptureInspectionV2, TFlexTurnReversionFinalizedOutcome, TJsonValue, } from '@modelprofile.com/flexharness';
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import type { IToolJobStore, IToolPermissionRequest, IToolJobState, IToolExecutionContext } from './plugins.js';
|
|
2
|
+
/** A file a tool is about to change, named by its canonical path. */
|
|
3
|
+
export interface IToolFileChange {
|
|
4
|
+
kind: 'write' | 'delete';
|
|
5
|
+
/**
|
|
6
|
+
* The canonical absolute path the operation changes. A write follows a symlink to its target; a
|
|
7
|
+
* delete removes the link itself, so its path is the link's.
|
|
8
|
+
*/
|
|
9
|
+
path: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Sees every change the local filesystem tools make, after the path checks and before the change,
|
|
13
|
+
* and every shell command before it starts. A rejection refuses the operation.
|
|
14
|
+
*/
|
|
15
|
+
export interface IToolFileChangeObserver {
|
|
16
|
+
beforeFileChange(change: IToolFileChange): Promise<void> | void;
|
|
17
|
+
/** A shell command or background job may change files the observer does not see. */
|
|
18
|
+
beforeShellCommand(): Promise<void> | void;
|
|
19
|
+
}
|
|
2
20
|
export interface ILocalToolExecutionContextOptions {
|
|
3
21
|
cwd?: string;
|
|
4
22
|
rootDir?: string;
|
|
@@ -7,6 +25,8 @@ export interface ILocalToolExecutionContextOptions {
|
|
|
7
25
|
abortSignal?: AbortSignal;
|
|
8
26
|
/** Guards each local filesystem operation or shell side effect. */
|
|
9
27
|
beforeOperation?: () => Promise<void> | void;
|
|
28
|
+
/** Observes every file change and shell command, for example to capture a turn for undo. */
|
|
29
|
+
fileChangeObserver?: IToolFileChangeObserver;
|
|
10
30
|
/** Hard combined stdout/stderr capture limit for each local shell command. Default: 1 MiB. */
|
|
11
31
|
maxShellOutputBytes?: number;
|
|
12
32
|
/** Number of terminal background job snapshots retained for get/list. Default: 100. */
|