chatflux-gestao-agente 0.2.0 → 0.2.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.
@@ -31,7 +31,7 @@ export async function createComputerAdapters(deps = {}) {
31
31
  const unavailable = async () => { throw new Error('Receita Windows sem ensaio de campo.'); };
32
32
  return { editor: createEditorAdapter({ platform, spawnImpl }), reply: createReplyAdapter({ platform }),
33
33
  history: { capabilities: { lerHistoricoClaude: false, lerHistoricoCodex: false }, read: unavailable },
34
- folders: { ...folders, capabilities: { seletorPasta: false }, chooseFolder: unavailable },
34
+ folders: { ...folders, openLinkedFolder: options => folders.openLinkedFolder({ ...options, platform, fsImpl, execFileImpl }), capabilities: { seletorPasta: false }, chooseFolder: unavailable },
35
35
  git: {}, plugins: {}, capabilityDetails };
36
36
  }
37
37
  // launchd does not inherit the login-shell PATH. Keep personal credentials,
@@ -245,7 +245,8 @@ export async function createComputerAdapters(deps = {}) {
245
245
  if (!executable) throw Object.assign(new Error('CLI local ausente.'), { code: 'ENOENT' });
246
246
  return spawnImpl(executable, argv, { ...options, env: commandEnv });
247
247
  };
248
- return { editor, reply, history, folders: { ...folders, capabilities: { seletorPasta: true },
248
+ return { editor, reply, history, folders: { ...folders,
249
+ openLinkedFolder: options => folders.openLinkedFolder({ ...options, platform, fsImpl, execFileImpl }), capabilities: { seletorPasta: true },
249
250
  chooseFolder: options => folders.chooseFolder({ ...options, platform, fsImpl, spawnImpl }),
250
251
  adoptKnownFolder: options => folders.adoptKnownFolder({ ...options, platform, fsImpl, home, execFileImpl }) },
251
252
  git: { ...createGitAdapter({ platform, fsImpl, spawnImpl: (binary, argv, options) =>
@@ -44,6 +44,7 @@ const plugin = x => ({
44
44
  const opening = { git: optional(git), openingSent: optional(bool),
45
45
  sessaoNaPasta: optional(bool), atualizarPelaIaIndisponivel: optional(bool) };
46
46
  const privateResult = x => ({
47
+ abrir_pasta: shape({ kind: oneOf('abrir_pasta') }),
47
48
  abrir_sessao: shape({ kind: oneOf('abrir_sessao'), canonicalPath: optional(text), ...opening }),
48
49
  baixar_abrir: shape({ kind: oneOf('baixar_abrir'), canonicalPath: text, ...opening }),
49
50
  escolher_pasta_projeto: shape({ kind: oneOf('escolher_pasta_projeto'), canonicalPath: text, ...opening }),
@@ -12,7 +12,7 @@ import { readUserJson } from './computador-identidade.mjs';
12
12
  import { createReplyAdapter } from './computador-resposta.mjs';
13
13
 
14
14
  const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url))).version;
15
- const ACTIONS = ['abrir_sessao', 'baixar_abrir', 'escolher_pasta_projeto', 'escolher_pasta_padrao', 'atualizar_plugin', 'responder_sessao', 'ler_historico'];
15
+ const ACTIONS = ['abrir_pasta', 'abrir_sessao', 'baixar_abrir', 'escolher_pasta_projeto', 'escolher_pasta_padrao', 'atualizar_plugin', 'responder_sessao', 'ler_historico'];
16
16
  const OPENING = ['abrindo_pasta', 'aguardando_editor', 'abrindo_conversa'];
17
17
  const RETRIES = [2000, 5000, 15000, 30000, 60000, 120000];
18
18
  const CLAIM = 'ops.computerRequestActions.claim', REPORT = 'ops.computerRequestActions.report';
@@ -508,6 +508,18 @@ export async function runComputer({ identity, portal, journal, paths, folders, e
508
508
  check(r);
509
509
  deadlineTimer = later(() => controller.abort(), Math.max(0, r.expiresAt - now()));
510
510
  renew();
511
+ if (r.action === 'abrir_pasta') {
512
+ await phase('conferindo_pasta');
513
+ const link = await portal.call('ops.computers.projectPath', { projectId: r.projectId }, { deadlineAt: budget() }); check(r);
514
+ if (!link) throw fail('FOLDER_REQUIRED');
515
+ if (link.computerId !== identity.computerId || link.projectId !== r.projectId || link.repoFullName !== r.repoFullName) throw fail('PATH_NOT_AUTHORIZED');
516
+ await phase('abrindo_pasta');
517
+ await effect('abrindo_pasta', () => {
518
+ const local = authorized(link.canonicalPath);
519
+ return folders.openLinkedFolder({ path: local.path, registeredPath: link.canonicalPath, signal: operationSignal });
520
+ }, () => ({ kind: 'concluir', privateResult: { kind: 'abrir_pasta' } }));
521
+ return r;
522
+ }
511
523
  if (r.action === 'atualizar_plugin') {
512
524
  if (!capabilities.atualizarPlugin) throw fail('EDITOR_RECIPE_UNVERIFIED');
513
525
  await phase('atualizando_plugin');
@@ -155,3 +155,14 @@ export async function adoptKnownFolder(options) {
155
155
  }
156
156
  return null;
157
157
  }
158
+
159
+ /** Only the resident supplies this path after resolving the project binding. */
160
+ export async function openLinkedFolder({ path: candidate, registeredPath, platform = process.platform, fsImpl = fs, execFileImpl = execFile, signal }) {
161
+ const canonicalPath = validatePath({ candidate, registeredPath, platform, fsImpl });
162
+ if (canonicalPath !== registeredPath || !fsImpl.statSync(canonicalPath).isDirectory()) throw pathError();
163
+ const binary = platform === 'darwin' ? '/usr/bin/open' : platform === 'win32' ? 'explorer.exe' : null;
164
+ if (!binary) throw fail('FOLDER_PLATFORM_UNSUPPORTED');
165
+ await new Promise((resolve, reject) => execFileImpl(binary, [canonicalPath], {
166
+ shell: false, windowsHide: true, timeout: 10000, ...(signal ? { signal } : {}),
167
+ }, error => error ? reject(error) : resolve()));
168
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatflux-gestao-agente",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Opere o portal Gestão da ChatFlux pelo Claude Code, Claude Desktop ou qualquer harness MCP — com a sua identidade e o seu papel.",
6
6
  "keywords": [