omnispace 0.11.0 → 0.12.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/README.md +7 -0
- package/dist/env.js +6 -6
- package/dist/include.js +12 -2
- package/dist/index.js +42 -38
- package/dist/permissoes.js +92 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,6 +84,13 @@ Tirar esta máquina da conta e o `.omni/` das pastas dela (pede duas confirmaç
|
|
|
84
84
|
npx -y omnispace@latest desvincular
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
Ver e mudar o que o agente pode fazer sozinho com cada ação dos conectores do espaço. Por padrão, leitura é automática, escrita pede confirmação e ação destrutiva fica bloqueada. Restringir vale na hora; liberar pede confirmação no navegador:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
npx -y omnispace@latest permissoes
|
|
91
|
+
npx -y omnispace@latest permissoes set slack send_message bloqueado
|
|
92
|
+
```
|
|
93
|
+
|
|
87
94
|
No Windows, se o caminho tiver espaço ou apóstrofo, entre na pasta com aspas duplas: `cd "C:\caminho\da\pasta"`.
|
|
88
95
|
|
|
89
96
|
## O que o Omni lê e envia
|
package/dist/env.js
CHANGED
|
@@ -11,9 +11,9 @@ const STATUS_TEXT = {
|
|
|
11
11
|
error: "com erro",
|
|
12
12
|
disabled: "desligado",
|
|
13
13
|
};
|
|
14
|
-
const USAGE = `Uso:
|
|
15
|
-
omnispace env push [pasta] pergunta chave por chave o que vai para o cofre do espaço
|
|
16
|
-
omnispace env list [pasta] mostra as chaves guardadas (só nomes)
|
|
14
|
+
const USAGE = `Uso:
|
|
15
|
+
omnispace env push [pasta] pergunta chave por chave o que vai para o cofre do espaço
|
|
16
|
+
omnispace env list [pasta] mostra as chaves guardadas (só nomes)
|
|
17
17
|
omnispace env rm <CHAVE>... [--arquivo .env] [--pasta <pasta>] apaga do cofre, de vez`;
|
|
18
18
|
async function askTerminal(question) {
|
|
19
19
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -25,7 +25,7 @@ async function askTerminal(question) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
/** The space of this folder and this machine's session, or a printed reason why not. */
|
|
28
|
-
async function context(root) {
|
|
28
|
+
export async function context(root) {
|
|
29
29
|
const config = await readSpaceConfig(root);
|
|
30
30
|
if (!config?.spaceId) {
|
|
31
31
|
console.error(config ? "O espaço desta pasta ainda não foi criado. Termine a conta no navegador." : "Esta pasta não é um espaço Omni. Rode omnispace init primeiro.");
|
|
@@ -44,7 +44,7 @@ async function context(root) {
|
|
|
44
44
|
}
|
|
45
45
|
return { apiUrl: config.apiUrl, token, spaceId: config.spaceId };
|
|
46
46
|
}
|
|
47
|
-
function refused(res, what) {
|
|
47
|
+
export function refused(res, what) {
|
|
48
48
|
console.error(`${what}: ${res.error}`);
|
|
49
49
|
if (res.expired || res.status === 401)
|
|
50
50
|
console.error("Para entrar de novo: omnispace login");
|
|
@@ -53,7 +53,7 @@ function refused(res, what) {
|
|
|
53
53
|
* Asks for the on-the-spot verification and waits for the browser. Returns the device code to send
|
|
54
54
|
* with the action, or null (refused, expired, failed).
|
|
55
55
|
*/
|
|
56
|
-
async function verify(ctx, action, keys, opts) {
|
|
56
|
+
export async function verify(ctx, action, keys, opts) {
|
|
57
57
|
const log = opts.log ?? ((line) => console.log(line));
|
|
58
58
|
const start = await callApi(ctx.apiUrl, ctx.token, "/api/cli/verify/start", { spaceId: ctx.spaceId, action, keys }, { retryDelaysMs: RETRY_DELAYS_MS });
|
|
59
59
|
if (!start.ok) {
|
package/dist/include.js
CHANGED
|
@@ -9,6 +9,15 @@ export const SKIP_DIRS = new Set([
|
|
|
9
9
|
"node_modules", ".git", ".next", ".vercel", "dist", "build", "out", "coverage",
|
|
10
10
|
".turbo", ".cache", "venv", ".venv", "__pycache__", ".idea", ".vscode", "target",
|
|
11
11
|
]);
|
|
12
|
+
/**
|
|
13
|
+
* Folders skipped by where they are, not by their name alone. `.claude/worktrees/` holds full copies
|
|
14
|
+
* of a repo that Claude Code sessions work on in parallel (Lucas, 2026-09-28): synced, every copy
|
|
15
|
+
* would go up as text. Matches at any depth (a repo inside the space can have its own).
|
|
16
|
+
*/
|
|
17
|
+
const SKIP_DIR_PATHS_RE = /(^|\/)\.claude\/worktrees$/i;
|
|
18
|
+
export function isSkippedDirPath(path) {
|
|
19
|
+
return SKIP_DIR_PATHS_RE.test(path);
|
|
20
|
+
}
|
|
12
21
|
// Local state of the Omni link itself: machine specific, never part of the space. `.omni/tmp/`
|
|
13
22
|
// holds downloads of an unfinished pull (fatia 3); `.omni/conflicts.json` and `.omni/backups/`
|
|
14
23
|
// are the conflict resolution of this machine (fatia 4); `.omni/pull-pending.json` and
|
|
@@ -74,7 +83,8 @@ export function isMachineConfig(path) {
|
|
|
74
83
|
*/
|
|
75
84
|
export function neverSyncs(path) {
|
|
76
85
|
const name = path.slice(path.lastIndexOf("/") + 1);
|
|
77
|
-
|
|
86
|
+
const inSkippedDir = /(^|\/)\.claude\/worktrees\//i.test(path);
|
|
87
|
+
return isSecret(name) || isMachineConfig(path) || isLocalOnly(path) || inSkippedDir;
|
|
78
88
|
}
|
|
79
89
|
export function isText(name) {
|
|
80
90
|
const lower = name.toLowerCase();
|
|
@@ -143,7 +153,7 @@ export async function buildManifest(root) {
|
|
|
143
153
|
const abs = join(dir, entry.name);
|
|
144
154
|
const relParts = [...parts, entry.name];
|
|
145
155
|
if (entry.isDirectory()) {
|
|
146
|
-
if (SKIP_DIRS.has(entry.name))
|
|
156
|
+
if (SKIP_DIRS.has(entry.name) || isSkippedDirPath(normalizePath(relParts)))
|
|
147
157
|
continue;
|
|
148
158
|
// A subfolder with its own .git (repo, submodule or worktree) DOES sync, text only: the
|
|
149
159
|
// agent needs the code to act on it (find a bug, fix it, report back). Lucas, 2026-09-23,
|
package/dist/index.js
CHANGED
|
@@ -18,30 +18,32 @@ import { terminalAsk, unlinkMachine } from "./desvincular.js";
|
|
|
18
18
|
import { linkExistingSpace } from "./vincular.js";
|
|
19
19
|
import { askSpace, parseAskArgs } from "./ask.js";
|
|
20
20
|
import { runEnv } from "./env.js";
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
omni
|
|
27
|
-
omni
|
|
28
|
-
omni
|
|
29
|
-
omni init [pasta] --
|
|
30
|
-
omni init [pasta] --
|
|
31
|
-
omni
|
|
32
|
-
omni
|
|
33
|
-
|
|
34
|
-
[--
|
|
35
|
-
|
|
36
|
-
omni sync [pasta] --
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
omni
|
|
40
|
-
omni
|
|
41
|
-
omni
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
21
|
+
import { runPermissoes } from "./permissoes.js";
|
|
22
|
+
const USAGE = `
|
|
23
|
+
Omni CLI
|
|
24
|
+
|
|
25
|
+
Uso:
|
|
26
|
+
omni login entra na sua conta pelo navegador (a sessão vale 14 dias sem uso)
|
|
27
|
+
omni logout encerra a sessão da CLI nesta máquina
|
|
28
|
+
omni whoami mostra a conta, a máquina e até quando a sessão vale
|
|
29
|
+
omni init [pasta] [--nome <nome>] com a CLI logada, cria o espaço na sua conta e liga a pasta
|
|
30
|
+
omni init [pasta] --scan-only varre a pasta e mostra o que encontrou
|
|
31
|
+
omni init [pasta] --link <código> varre a pasta e envia pro código mostrado no navegador
|
|
32
|
+
omni status [pasta] [--resumo] o que mudou aqui e no servidor, e o próximo comando (só lê)
|
|
33
|
+
omni ask "<pergunta>" [pasta] responde com base na versão publicada do espaço, com as fontes
|
|
34
|
+
[--fontes] só mostra quais arquivos seriam lidos, sem chamar o modelo
|
|
35
|
+
[--detalhes] mostra também custo, contexto e arquivos consultados
|
|
36
|
+
omni sync [pasta] --push [--yes] publica uma nova versão do espaço no servidor
|
|
37
|
+
omni sync [pasta] --pull traz para a pasta a versão mais nova do servidor
|
|
38
|
+
[--conflito=local|mesclar|backup] mesma resposta para todos os conflitos, sem perguntar
|
|
39
|
+
omni clone <código> <pasta> baixa um espaço da sua conta numa pasta nova desta máquina
|
|
40
|
+
omni vincular [pasta] [--espaco <nome>] liga uma pasta que já tem os arquivos a um espaço da conta
|
|
41
|
+
omni desvincular [pasta] tira esta máquina da conta e o .omni/ das pastas (pede 2 confirmações)
|
|
42
|
+
omni env push|list|rm [pasta] guarda chaves do .env no cofre do espaço, uma a uma (confirma no navegador)
|
|
43
|
+
omni permissoes [set <conector> <ação> <estado>] [pasta] o que o agente pode fazer sozinho em cada ação
|
|
44
|
+
|
|
45
|
+
A CLI não reorganiza a sua pasta. Com --link, o que fazer com o que foi
|
|
46
|
+
varrido é decidido na tela de revisão, no navegador.
|
|
45
47
|
`;
|
|
46
48
|
async function main(argv) {
|
|
47
49
|
const args = argv.slice(2);
|
|
@@ -67,6 +69,8 @@ async function main(argv) {
|
|
|
67
69
|
return runClone(args.slice(1));
|
|
68
70
|
if (command === "env")
|
|
69
71
|
return runEnv(args.slice(1), { openBrowser: process.stdout.isTTY });
|
|
72
|
+
if (command === "permissoes")
|
|
73
|
+
return runPermissoes(args.slice(1), { openBrowser: process.stdout.isTTY });
|
|
70
74
|
if (command === "vincular") {
|
|
71
75
|
const rest = args.slice(1);
|
|
72
76
|
const espacoIndex = rest.indexOf("--espaco");
|
|
@@ -114,20 +118,20 @@ async function main(argv) {
|
|
|
114
118
|
if (!who.ok && !who.noToken)
|
|
115
119
|
console.error(`\n${who.error}`);
|
|
116
120
|
const site = defaultApiUrl();
|
|
117
|
-
console.log(`
|
|
118
|
-
Omni CLI
|
|
119
|
-
|
|
120
|
-
Já tem conta no Omni? Entre por aqui e rode o init de novo:
|
|
121
|
-
npx -y omnispace@latest login
|
|
122
|
-
|
|
123
|
-
Primeira vez? Comece pelo navegador:
|
|
124
|
-
${site}/omnispace/escolha
|
|
125
|
-
|
|
126
|
-
Lá aparece um código (tipo ABCD-2345). Com ele, dentro da pasta do espaço, rode:
|
|
127
|
-
npx -y omnispace@latest init . --link <código>
|
|
128
|
-
|
|
129
|
-
Só quer ver o que o Omni leria nesta pasta, sem enviar nada?
|
|
130
|
-
npx -y omnispace@latest init . --scan-only
|
|
121
|
+
console.log(`
|
|
122
|
+
Omni CLI
|
|
123
|
+
|
|
124
|
+
Já tem conta no Omni? Entre por aqui e rode o init de novo:
|
|
125
|
+
npx -y omnispace@latest login
|
|
126
|
+
|
|
127
|
+
Primeira vez? Comece pelo navegador:
|
|
128
|
+
${site}/omnispace/escolha
|
|
129
|
+
|
|
130
|
+
Lá aparece um código (tipo ABCD-2345). Com ele, dentro da pasta do espaço, rode:
|
|
131
|
+
npx -y omnispace@latest init . --link <código>
|
|
132
|
+
|
|
133
|
+
Só quer ver o que o Omni leria nesta pasta, sem enviar nada?
|
|
134
|
+
npx -y omnispace@latest init . --scan-only
|
|
131
135
|
`);
|
|
132
136
|
return who.ok || who.noToken ? 0 : 1;
|
|
133
137
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { callApi } from "./api.js";
|
|
3
|
+
import { RETRY_DELAYS_MS } from "./retry.js";
|
|
4
|
+
import { context, refused, verify } from "./env.js";
|
|
5
|
+
// `omnispace permissoes` (docs/plan/permissoes-por-acao.md; Lucas, 2026-09-28): what the agent may
|
|
6
|
+
// do on its own with each connector action of the space. Tightening applies right away; loosening
|
|
7
|
+
// asks for the confirmation in the browser, like a credential.
|
|
8
|
+
const STATES = ["automatico", "confirmar", "bloqueado"];
|
|
9
|
+
const STATE_LABEL = { automatico: "automático", confirmar: "pedir confirmação", bloqueado: "bloqueado" };
|
|
10
|
+
const ORIGIN_LABEL = { padrao: "padrão do Omni", usuario: "definida por você", importacao: "importada" };
|
|
11
|
+
const USAGE = `Uso:
|
|
12
|
+
omnispace permissoes [pasta] lista as ações dos conectores do espaço e o que o agente pode fazer
|
|
13
|
+
omnispace permissoes set <conector> <ação> <estado> [pasta] muda uma ação: automatico, confirmar ou bloqueado
|
|
14
|
+
Liberar (bloqueado para confirmar, ou para automático) pede confirmação no navegador; restringir vale na hora.`;
|
|
15
|
+
export function parseStateArg(value) {
|
|
16
|
+
const plain = (value ?? "").toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "");
|
|
17
|
+
return STATES.find((s) => s === plain) ?? null;
|
|
18
|
+
}
|
|
19
|
+
async function list(root, opts) {
|
|
20
|
+
const log = opts.log ?? ((line) => console.log(line));
|
|
21
|
+
const ctx = await context(root);
|
|
22
|
+
if (!ctx)
|
|
23
|
+
return 1;
|
|
24
|
+
const res = await callApi(ctx.apiUrl, ctx.token, "/api/cli/permissions/list", { spaceId: ctx.spaceId }, { retryDelaysMs: RETRY_DELAYS_MS });
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
refused(res, "Falha ao ler as permissões");
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
const rows = res.permissions;
|
|
30
|
+
if (rows.length === 0) {
|
|
31
|
+
log("Este espaço ainda não tem conector. As permissões aparecem aqui quando houver um (omnispace env push).");
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
let current = "";
|
|
35
|
+
for (const r of rows) {
|
|
36
|
+
if (r.connectorId !== current) {
|
|
37
|
+
current = r.connectorId;
|
|
38
|
+
log(`\n${r.connectorName} (${r.connectorId})`);
|
|
39
|
+
}
|
|
40
|
+
log(` ${r.actionKey.padEnd(22)} ${r.actionName.padEnd(26)} ${r.kind.padEnd(10)} ${STATE_LABEL[r.state].padEnd(18)} ${ORIGIN_LABEL[r.origin] ?? r.origin}`);
|
|
41
|
+
}
|
|
42
|
+
log("\nPara mudar: omnispace permissoes set <conector> <ação> <automatico|confirmar|bloqueado>");
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
async function set(root, connectorId, actionKey, state, opts) {
|
|
46
|
+
const log = opts.log ?? ((line) => console.log(line));
|
|
47
|
+
const ctx = await context(root);
|
|
48
|
+
if (!ctx)
|
|
49
|
+
return 1;
|
|
50
|
+
const changes = [{ connectorId, actionKey, state }];
|
|
51
|
+
// Not retried: a repeat after a cut connection is harmless (same state), but it is reported.
|
|
52
|
+
let res = await callApi(ctx.apiUrl, ctx.token, "/api/cli/permissions/set", { spaceId: ctx.spaceId, changes });
|
|
53
|
+
if (!res.ok && res.needsVerification) {
|
|
54
|
+
log(`Liberar ${connectorId}:${actionKey} (${STATE_LABEL[state]}) deixa o agente fazer mais sozinho.`);
|
|
55
|
+
const interactive = opts.interactive ?? Boolean(process.stdin.isTTY);
|
|
56
|
+
if (!interactive) {
|
|
57
|
+
console.error("Sem terminal para acompanhar a confirmação no navegador. Nada mudou.");
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
const code = await verify(ctx, "perm_set", [{ file: `${connectorId}:${actionKey}`, key: state }], opts);
|
|
61
|
+
if (!code)
|
|
62
|
+
return 1;
|
|
63
|
+
res = await callApi(ctx.apiUrl, ctx.token, "/api/cli/permissions/set", { spaceId: ctx.spaceId, changes, verification: code });
|
|
64
|
+
}
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
refused(res, "A permissão não mudou");
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
const applied = res.applied;
|
|
70
|
+
if (applied.length === 0)
|
|
71
|
+
log(`${connectorId}:${actionKey} já estava como ${STATE_LABEL[state]}. Nada mudou.`);
|
|
72
|
+
else
|
|
73
|
+
log(`✓ ${connectorId}:${actionKey}: ${STATE_LABEL[applied[0].from]} → ${STATE_LABEL[applied[0].to]}.`);
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
export async function runPermissoes(args, opts = {}) {
|
|
77
|
+
const [first, ...rest] = args;
|
|
78
|
+
if (first === "set") {
|
|
79
|
+
const [connectorId, actionKey, stateArg, folder] = rest;
|
|
80
|
+
const state = parseStateArg(stateArg);
|
|
81
|
+
if (!connectorId || !actionKey || !state) {
|
|
82
|
+
console.error(USAGE);
|
|
83
|
+
return 1;
|
|
84
|
+
}
|
|
85
|
+
return set(resolve(folder ?? process.cwd()), connectorId, actionKey, state, opts);
|
|
86
|
+
}
|
|
87
|
+
if (first === "--help" || first === "-h" || first === "help") {
|
|
88
|
+
console.log(USAGE);
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
return list(resolve(first && first !== "list" ? first : (rest[0] ?? process.cwd())), opts);
|
|
92
|
+
}
|