redmine-context 1.0.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/LICENSE +21 -0
- package/README.md +449 -0
- package/dist/bundle/index.d.ts +5 -0
- package/dist/bundle/index.js +5 -0
- package/dist/bundle/json.d.ts +90 -0
- package/dist/bundle/json.js +266 -0
- package/dist/bundle/markdown.d.ts +75 -0
- package/dist/bundle/markdown.js +294 -0
- package/dist/bundle/search-list.d.ts +43 -0
- package/dist/bundle/search-list.js +53 -0
- package/dist/bundle/stable-stringify.d.ts +26 -0
- package/dist/bundle/stable-stringify.js +50 -0
- package/dist/cache/contract.d.ts +157 -0
- package/dist/cache/contract.js +0 -0
- package/dist/cache/disk-index.d.ts +82 -0
- package/dist/cache/disk-index.js +220 -0
- package/dist/cache/disk.d.ts +133 -0
- package/dist/cache/disk.js +313 -0
- package/dist/cache/gc.d.ts +78 -0
- package/dist/cache/gc.js +123 -0
- package/dist/cache/get-or-compute.d.ts +36 -0
- package/dist/cache/get-or-compute.js +52 -0
- package/dist/cache/index.d.ts +9 -0
- package/dist/cache/index.js +8 -0
- package/dist/cache/keys.d.ts +76 -0
- package/dist/cache/keys.js +78 -0
- package/dist/cache/memory.d.ts +48 -0
- package/dist/cache/memory.js +110 -0
- package/dist/cache-first.d.ts +127 -0
- package/dist/cache-first.js +227 -0
- package/dist/client/errors.d.ts +33 -0
- package/dist/client/errors.js +49 -0
- package/dist/client/http.d.ts +110 -0
- package/dist/client/http.js +207 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +5 -0
- package/dist/client/issues.d.ts +71 -0
- package/dist/client/issues.js +100 -0
- package/dist/client/search.d.ts +58 -0
- package/dist/client/search.js +81 -0
- package/dist/config/credentials.d.ts +247 -0
- package/dist/config/credentials.js +427 -0
- package/dist/config/doctor.d.ts +123 -0
- package/dist/config/doctor.js +260 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.js +6 -0
- package/dist/config/keyring.d.ts +96 -0
- package/dist/config/keyring.js +158 -0
- package/dist/config/login.d.ts +97 -0
- package/dist/config/login.js +189 -0
- package/dist/config/settings.d.ts +94 -0
- package/dist/config/settings.js +140 -0
- package/dist/contract.d.ts +173 -0
- package/dist/contract.js +27 -0
- package/dist/core.d.ts +1 -0
- package/dist/core.js +8 -0
- package/dist/extract/audio-extractor.d.ts +105 -0
- package/dist/extract/audio-extractor.js +156 -0
- package/dist/extract/audio.d.ts +126 -0
- package/dist/extract/audio.js +184 -0
- package/dist/extract/dispatcher.d.ts +132 -0
- package/dist/extract/dispatcher.js +115 -0
- package/dist/extract/download.d.ts +111 -0
- package/dist/extract/download.js +261 -0
- package/dist/extract/duration.d.ts +106 -0
- package/dist/extract/duration.js +148 -0
- package/dist/extract/ffmpeg.d.ts +56 -0
- package/dist/extract/ffmpeg.js +95 -0
- package/dist/extract/gguf.d.ts +137 -0
- package/dist/extract/gguf.js +215 -0
- package/dist/extract/index.d.ts +19 -0
- package/dist/extract/index.js +19 -0
- package/dist/extract/magic.d.ts +80 -0
- package/dist/extract/magic.js +282 -0
- package/dist/extract/ooxml.d.ts +131 -0
- package/dist/extract/ooxml.js +336 -0
- package/dist/extract/pdf.d.ts +147 -0
- package/dist/extract/pdf.js +322 -0
- package/dist/extract/queue.d.ts +167 -0
- package/dist/extract/queue.js +217 -0
- package/dist/extract/subprocess.d.ts +145 -0
- package/dist/extract/subprocess.js +181 -0
- package/dist/extract/tesseract.d.ts +153 -0
- package/dist/extract/tesseract.js +321 -0
- package/dist/extract/video-extractor.d.ts +84 -0
- package/dist/extract/video-extractor.js +89 -0
- package/dist/extract/video.d.ts +198 -0
- package/dist/extract/video.js +418 -0
- package/dist/extract/which.d.ts +63 -0
- package/dist/extract/which.js +72 -0
- package/dist/extract/whisper-extract.d.ts +211 -0
- package/dist/extract/whisper-extract.js +323 -0
- package/dist/extract/whisper.d.ts +50 -0
- package/dist/extract/whisper.js +67 -0
- package/dist/extract/zip.d.ts +50 -0
- package/dist/extract/zip.js +165 -0
- package/dist/extract-issue-attachments.d.ts +82 -0
- package/dist/extract-issue-attachments.js +156 -0
- package/dist/fetch-attachment-text.d.ts +113 -0
- package/dist/fetch-attachment-text.js +155 -0
- package/dist/fetch-issue-bundle.d.ts +81 -0
- package/dist/fetch-issue-bundle.js +93 -0
- package/dist/fetch-issue-search.d.ts +74 -0
- package/dist/fetch-issue-search.js +120 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +67 -0
- package/dist/normalize/collections.d.ts +52 -0
- package/dist/normalize/collections.js +170 -0
- package/dist/normalize/helpers.d.ts +35 -0
- package/dist/normalize/helpers.js +59 -0
- package/dist/normalize/index.d.ts +2 -0
- package/dist/normalize/index.js +2 -0
- package/dist/normalize/issue.d.ts +34 -0
- package/dist/normalize/issue.js +154 -0
- package/dist/surfaces/cli/commands.d.ts +61 -0
- package/dist/surfaces/cli/commands.js +262 -0
- package/dist/surfaces/cli/main.d.ts +32 -0
- package/dist/surfaces/cli/main.js +210 -0
- package/dist/surfaces/cli/prompts.d.ts +66 -0
- package/dist/surfaces/cli/prompts.js +147 -0
- package/dist/surfaces/cli/tty.d.ts +27 -0
- package/dist/surfaces/cli/tty.js +35 -0
- package/dist/surfaces/cli/types.d.ts +39 -0
- package/dist/surfaces/cli/types.js +7 -0
- package/dist/surfaces/mcp/server.d.ts +171 -0
- package/dist/surfaces/mcp/server.js +427 -0
- package/dist/surfaces/tui/app.d.ts +55 -0
- package/dist/surfaces/tui/app.js +180 -0
- package/dist/surfaces/tui/attachment-status.d.ts +79 -0
- package/dist/surfaces/tui/attachment-status.js +113 -0
- package/dist/surfaces/tui/components/breadcrumb.d.ts +7 -0
- package/dist/surfaces/tui/components/breadcrumb.js +31 -0
- package/dist/surfaces/tui/components/gradient-text.d.ts +23 -0
- package/dist/surfaces/tui/components/gradient-text.js +75 -0
- package/dist/surfaces/tui/components/scroll-view.d.ts +25 -0
- package/dist/surfaces/tui/components/scroll-view.js +77 -0
- package/dist/surfaces/tui/components/spinner.d.ts +12 -0
- package/dist/surfaces/tui/components/spinner.js +39 -0
- package/dist/surfaces/tui/components/text-input.d.ts +45 -0
- package/dist/surfaces/tui/components/text-input.js +114 -0
- package/dist/surfaces/tui/format-file-size.d.ts +24 -0
- package/dist/surfaces/tui/format-file-size.js +43 -0
- package/dist/surfaces/tui/glyphs.d.ts +47 -0
- package/dist/surfaces/tui/glyphs.js +84 -0
- package/dist/surfaces/tui/hooks/use-auth-guard.d.ts +39 -0
- package/dist/surfaces/tui/hooks/use-auth-guard.js +135 -0
- package/dist/surfaces/tui/hooks/use-doctor-status.d.ts +64 -0
- package/dist/surfaces/tui/hooks/use-doctor-status.js +123 -0
- package/dist/surfaces/tui/hooks/use-escape-interceptor.d.ts +25 -0
- package/dist/surfaces/tui/hooks/use-escape-interceptor.js +65 -0
- package/dist/surfaces/tui/hooks/use-exit-guard.d.ts +18 -0
- package/dist/surfaces/tui/hooks/use-exit-guard.js +66 -0
- package/dist/surfaces/tui/hooks/use-export-bundle.d.ts +62 -0
- package/dist/surfaces/tui/hooks/use-export-bundle.js +100 -0
- package/dist/surfaces/tui/hooks/use-issue-detail.d.ts +61 -0
- package/dist/surfaces/tui/hooks/use-issue-detail.js +132 -0
- package/dist/surfaces/tui/hooks/use-issue-search.d.ts +71 -0
- package/dist/surfaces/tui/hooks/use-issue-search.js +168 -0
- package/dist/surfaces/tui/hooks/use-list-navigation.d.ts +44 -0
- package/dist/surfaces/tui/hooks/use-list-navigation.js +82 -0
- package/dist/surfaces/tui/hooks/use-media-binaries.d.ts +24 -0
- package/dist/surfaces/tui/hooks/use-media-binaries.js +44 -0
- package/dist/surfaces/tui/hooks/use-my-issues.d.ts +66 -0
- package/dist/surfaces/tui/hooks/use-my-issues.js +151 -0
- package/dist/surfaces/tui/hooks/use-onboarding-callbacks.d.ts +11 -0
- package/dist/surfaces/tui/hooks/use-onboarding-callbacks.js +104 -0
- package/dist/surfaces/tui/hooks/use-terminal-width.d.ts +52 -0
- package/dist/surfaces/tui/hooks/use-terminal-width.js +90 -0
- package/dist/surfaces/tui/index.d.ts +50 -0
- package/dist/surfaces/tui/index.js +158 -0
- package/dist/surfaces/tui/instance.d.ts +37 -0
- package/dist/surfaces/tui/instance.js +36 -0
- package/dist/surfaces/tui/job-registry.d.ts +113 -0
- package/dist/surfaces/tui/job-registry.js +123 -0
- package/dist/surfaces/tui/job-status.d.ts +45 -0
- package/dist/surfaces/tui/job-status.js +81 -0
- package/dist/surfaces/tui/navigation.d.ts +74 -0
- package/dist/surfaces/tui/navigation.js +87 -0
- package/dist/surfaces/tui/palettes.d.ts +38 -0
- package/dist/surfaces/tui/palettes.js +244 -0
- package/dist/surfaces/tui/screen.d.ts +30 -0
- package/dist/surfaces/tui/screen.js +51 -0
- package/dist/surfaces/tui/screens/about.d.ts +2 -0
- package/dist/surfaces/tui/screens/about.js +35 -0
- package/dist/surfaces/tui/screens/appearance.d.ts +2 -0
- package/dist/surfaces/tui/screens/appearance.js +74 -0
- package/dist/surfaces/tui/screens/config.d.ts +2 -0
- package/dist/surfaces/tui/screens/config.js +82 -0
- package/dist/surfaces/tui/screens/doctor.d.ts +2 -0
- package/dist/surfaces/tui/screens/doctor.js +109 -0
- package/dist/surfaces/tui/screens/export.d.ts +2 -0
- package/dist/surfaces/tui/screens/export.js +168 -0
- package/dist/surfaces/tui/screens/home-selection.d.ts +70 -0
- package/dist/surfaces/tui/screens/home-selection.js +80 -0
- package/dist/surfaces/tui/screens/home.d.ts +2 -0
- package/dist/surfaces/tui/screens/home.js +200 -0
- package/dist/surfaces/tui/screens/issue-detail.d.ts +6 -0
- package/dist/surfaces/tui/screens/issue-detail.js +182 -0
- package/dist/surfaces/tui/screens/jobs.d.ts +7 -0
- package/dist/surfaces/tui/screens/jobs.js +89 -0
- package/dist/surfaces/tui/screens/loaded-issue-context.d.ts +49 -0
- package/dist/surfaces/tui/screens/loaded-issue-context.js +57 -0
- package/dist/surfaces/tui/screens/onboarding/api-key.d.ts +2 -0
- package/dist/surfaces/tui/screens/onboarding/api-key.js +69 -0
- package/dist/surfaces/tui/screens/onboarding/login.d.ts +2 -0
- package/dist/surfaces/tui/screens/onboarding/login.js +50 -0
- package/dist/surfaces/tui/screens/onboarding/mode.d.ts +2 -0
- package/dist/surfaces/tui/screens/onboarding/mode.js +42 -0
- package/dist/surfaces/tui/screens/onboarding/onboarding-context.d.ts +221 -0
- package/dist/surfaces/tui/screens/onboarding/onboarding-context.js +131 -0
- package/dist/surfaces/tui/screens/onboarding/success.d.ts +2 -0
- package/dist/surfaces/tui/screens/onboarding/success.js +41 -0
- package/dist/surfaces/tui/screens/onboarding/url.d.ts +24 -0
- package/dist/surfaces/tui/screens/onboarding/url.js +84 -0
- package/dist/surfaces/tui/screens/onboarding/validating.d.ts +1 -0
- package/dist/surfaces/tui/screens/onboarding/validating.js +86 -0
- package/dist/surfaces/tui/screens/welcome.d.ts +6 -0
- package/dist/surfaces/tui/screens/welcome.js +95 -0
- package/dist/surfaces/tui/status-color.d.ts +21 -0
- package/dist/surfaces/tui/status-color.js +23 -0
- package/dist/surfaces/tui/symbols.d.ts +231 -0
- package/dist/surfaces/tui/symbols.js +14 -0
- package/dist/surfaces/tui/terminal-colors.d.ts +29 -0
- package/dist/surfaces/tui/terminal-colors.js +39 -0
- package/dist/surfaces/tui/theme.d.ts +156 -0
- package/dist/surfaces/tui/theme.js +86 -0
- package/dist/surfaces/tui/truncate.d.ts +31 -0
- package/dist/surfaces/tui/truncate.js +81 -0
- package/package.json +93 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts interativos do CLI (M1-11) — leitura de linha e senha mascarada.
|
|
3
|
+
*
|
|
4
|
+
* Implementação mínima sobre o `readline` nativo (sem dependência de terceiros):
|
|
5
|
+
* `prompt` lê uma linha com echo normal; `promptPassword` desliga o echo dos
|
|
6
|
+
* caracteres digitados (sem eco — a senha nunca aparece no terminal nem em logs).
|
|
7
|
+
* Os streams de entrada/saída são injetáveis para permitir testes determinísticos.
|
|
8
|
+
*/
|
|
9
|
+
import { createInterface } from 'node:readline';
|
|
10
|
+
/** I/O default apontando para o processo atual. */
|
|
11
|
+
function defaultIo() {
|
|
12
|
+
return { input: process.stdin, output: process.stdout };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Cria uma {@link PromptSession} sobre os streams informados.
|
|
16
|
+
*
|
|
17
|
+
* @param io - Streams de I/O (ver {@link PromptIo}). Default: stdin/stdout.
|
|
18
|
+
* @returns Sessão com `prompt`/`promptPassword`/`close`.
|
|
19
|
+
* @example
|
|
20
|
+
* const session = createPromptSession();
|
|
21
|
+
* const user = await session.prompt('Usuário: ');
|
|
22
|
+
* const pass = await session.promptPassword('Senha: ');
|
|
23
|
+
* session.close();
|
|
24
|
+
*/
|
|
25
|
+
export function createPromptSession(io = defaultIo()) {
|
|
26
|
+
let rl;
|
|
27
|
+
let muted = false;
|
|
28
|
+
// Fila própria: o readline EMITE 'line' assim que a entrada chega (pipes e
|
|
29
|
+
// colagens entregam várias linhas de uma vez) e eventos sem ouvinte com
|
|
30
|
+
// pergunta pendente se perderiam — segunda metade do bug #106.
|
|
31
|
+
const pending = [];
|
|
32
|
+
const waiters = [];
|
|
33
|
+
const ensure = () => {
|
|
34
|
+
if (rl === undefined) {
|
|
35
|
+
const created = createInterface({ input: io.input, output: io.output });
|
|
36
|
+
const writer = created;
|
|
37
|
+
writer._writeToOutput = (text) => {
|
|
38
|
+
if (!muted) {
|
|
39
|
+
io.output.write(text);
|
|
40
|
+
}
|
|
41
|
+
// muted: engole o echo dos caracteres sensíveis.
|
|
42
|
+
};
|
|
43
|
+
created.on('line', (line) => {
|
|
44
|
+
const waiter = waiters.shift();
|
|
45
|
+
if (waiter) {
|
|
46
|
+
waiter(line);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
pending.push(line);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// EOF (Ctrl+D / pipe encerrado) com prompt pendente: resolve vazio para o
|
|
53
|
+
// fluxo falhar com mensagem clara em vez de o processo morrer em silêncio.
|
|
54
|
+
created.on('close', () => {
|
|
55
|
+
while (waiters.length > 0) {
|
|
56
|
+
waiters.shift()?.('');
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
rl = created;
|
|
60
|
+
}
|
|
61
|
+
return rl;
|
|
62
|
+
};
|
|
63
|
+
const nextLine = () => {
|
|
64
|
+
const queued = pending.shift();
|
|
65
|
+
if (queued !== undefined) {
|
|
66
|
+
return Promise.resolve(queued);
|
|
67
|
+
}
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
waiters.push(resolve);
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
const ask = async (question, mute) => {
|
|
73
|
+
ensure();
|
|
74
|
+
io.output.write(question);
|
|
75
|
+
muted = mute;
|
|
76
|
+
try {
|
|
77
|
+
return await nextLine();
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
muted = false;
|
|
81
|
+
if (mute) {
|
|
82
|
+
io.output.write('\n');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
prompt: (question) => ask(question, false),
|
|
88
|
+
promptPassword: (question) => ask(question, true),
|
|
89
|
+
close: () => {
|
|
90
|
+
rl?.close();
|
|
91
|
+
rl = undefined;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Lê uma linha do usuário com echo normal.
|
|
97
|
+
*
|
|
98
|
+
* @param question - Texto exibido antes da entrada (ex.: `'Usuário: '`).
|
|
99
|
+
* @param io - Streams de I/O (ver {@link PromptIo}). Default: stdin/stdout.
|
|
100
|
+
* @returns A linha digitada (sem o `\n` final).
|
|
101
|
+
* @example
|
|
102
|
+
* const user = await prompt('Usuário: ');
|
|
103
|
+
*/
|
|
104
|
+
export function prompt(question, io = defaultIo()) {
|
|
105
|
+
const rl = createInterface({ input: io.input, output: io.output });
|
|
106
|
+
return new Promise((resolve) => {
|
|
107
|
+
rl.question(question, (answer) => {
|
|
108
|
+
rl.close();
|
|
109
|
+
resolve(answer);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Lê uma linha sensível (senha, api_key) SEM ecoar os caracteres digitados.
|
|
115
|
+
*
|
|
116
|
+
* A pergunta é escrita normalmente; a partir daí o writer interno do readline é
|
|
117
|
+
* silenciado, de modo que nenhum caractere da entrada aparece no terminal.
|
|
118
|
+
*
|
|
119
|
+
* @param question - Texto exibido antes da entrada (ex.: `'Senha: '`).
|
|
120
|
+
* @param io - Streams de I/O (ver {@link PromptIo}). Default: stdin/stdout.
|
|
121
|
+
* @returns O valor digitado (sem o `\n` final), nunca ecoado.
|
|
122
|
+
* @example
|
|
123
|
+
* const password = await promptPassword('Senha: ');
|
|
124
|
+
*/
|
|
125
|
+
export function promptPassword(question, io = defaultIo()) {
|
|
126
|
+
const { input, output } = io;
|
|
127
|
+
const rl = createInterface({ input, output, terminal: true });
|
|
128
|
+
// O readline expõe `_writeToOutput` como ponto de extensão do echo.
|
|
129
|
+
const writer = rl;
|
|
130
|
+
let muted = false;
|
|
131
|
+
writer._writeToOutput = (text) => {
|
|
132
|
+
if (!muted) {
|
|
133
|
+
output.write(text);
|
|
134
|
+
}
|
|
135
|
+
// muted: engole o echo dos caracteres da senha (sem eco).
|
|
136
|
+
};
|
|
137
|
+
output.write(question);
|
|
138
|
+
muted = true;
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
rl.question('', (answer) => {
|
|
141
|
+
muted = false;
|
|
142
|
+
rl.close();
|
|
143
|
+
output.write('\n');
|
|
144
|
+
resolve(answer);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decisão de degradação da TUI Ink (M2-03).
|
|
3
|
+
*
|
|
4
|
+
* Centraliza em uma função pura quando o `redmine-context` sem argumentos
|
|
5
|
+
* pode abrir a TUI Ink vs. quando deve cair para o caminho de texto puro da
|
|
6
|
+
* CLI (help). Sinais de degradação (qualquer um basta):
|
|
7
|
+
*
|
|
8
|
+
* - `NO_COLOR` definida (qualquer valor, inclusive string vazia — convenção
|
|
9
|
+
* de https://no-color.org: a mera presença da variável já sinaliza a
|
|
10
|
+
* preferência do usuário/ambiente por saída sem formatação);
|
|
11
|
+
* - `CI=true` ou `CI=1` (ambientes de integração contínua não têm um terminal real);
|
|
12
|
+
* - stdout não é um TTY (pipe, redirecionamento para arquivo, etc.).
|
|
13
|
+
*
|
|
14
|
+
* Pura e testável isoladamente: recebe `env` e `isTTY` em vez de ler
|
|
15
|
+
* `process.env`/`process.stdout` diretamente, seguindo o padrão de
|
|
16
|
+
* dependências injetáveis do CLI (`RunDeps`, ver `./types.ts`).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Decide se a TUI Ink pode ser aberta.
|
|
20
|
+
*
|
|
21
|
+
* @param env - Ambiente do processo (ou um subconjunto controlado em testes).
|
|
22
|
+
* @param isTTY - `true` quando o stdout real é um TTY.
|
|
23
|
+
* @returns `true` somente quando não há nenhum sinal de degradação.
|
|
24
|
+
* @example
|
|
25
|
+
* shouldRenderTui(process.env, process.stdout.isTTY === true);
|
|
26
|
+
*/
|
|
27
|
+
export declare function shouldRenderTui(env: NodeJS.ProcessEnv, isTTY: boolean): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decisão de degradação da TUI Ink (M2-03).
|
|
3
|
+
*
|
|
4
|
+
* Centraliza em uma função pura quando o `redmine-context` sem argumentos
|
|
5
|
+
* pode abrir a TUI Ink vs. quando deve cair para o caminho de texto puro da
|
|
6
|
+
* CLI (help). Sinais de degradação (qualquer um basta):
|
|
7
|
+
*
|
|
8
|
+
* - `NO_COLOR` definida (qualquer valor, inclusive string vazia — convenção
|
|
9
|
+
* de https://no-color.org: a mera presença da variável já sinaliza a
|
|
10
|
+
* preferência do usuário/ambiente por saída sem formatação);
|
|
11
|
+
* - `CI=true` ou `CI=1` (ambientes de integração contínua não têm um terminal real);
|
|
12
|
+
* - stdout não é um TTY (pipe, redirecionamento para arquivo, etc.).
|
|
13
|
+
*
|
|
14
|
+
* Pura e testável isoladamente: recebe `env` e `isTTY` em vez de ler
|
|
15
|
+
* `process.env`/`process.stdout` diretamente, seguindo o padrão de
|
|
16
|
+
* dependências injetáveis do CLI (`RunDeps`, ver `./types.ts`).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Decide se a TUI Ink pode ser aberta.
|
|
20
|
+
*
|
|
21
|
+
* @param env - Ambiente do processo (ou um subconjunto controlado em testes).
|
|
22
|
+
* @param isTTY - `true` quando o stdout real é um TTY.
|
|
23
|
+
* @returns `true` somente quando não há nenhum sinal de degradação.
|
|
24
|
+
* @example
|
|
25
|
+
* shouldRenderTui(process.env, process.stdout.isTTY === true);
|
|
26
|
+
*/
|
|
27
|
+
export function shouldRenderTui(env, isTTY) {
|
|
28
|
+
if (!isTTY)
|
|
29
|
+
return false;
|
|
30
|
+
if (env.NO_COLOR !== undefined)
|
|
31
|
+
return false;
|
|
32
|
+
if (env.CI === 'true' || env.CI === '1')
|
|
33
|
+
return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos compartilhados da superfície CLI (M1-11).
|
|
3
|
+
*
|
|
4
|
+
* Isolados neste módulo para evitar ciclos entre `main.ts` (parse/dispatch) e
|
|
5
|
+
* `commands.ts` (handlers) e manter cada arquivo com um propósito único.
|
|
6
|
+
*/
|
|
7
|
+
import type { SettingsStore } from '../../index.js';
|
|
8
|
+
/** Dependências injetáveis do CLI — permitem testar I/O e prompts sem tocar o processo real. */
|
|
9
|
+
export interface RunDeps {
|
|
10
|
+
/** Escreve no canal de saída (stdout) — recebe o bundle. */
|
|
11
|
+
stdout(text: string): void;
|
|
12
|
+
/** Escreve no canal de diagnóstico (stderr) — recebe progresso e mensagens. */
|
|
13
|
+
stderr(text: string): void;
|
|
14
|
+
/** Ambiente consultado para `REDMINE_URL` e pela cascata de credenciais. */
|
|
15
|
+
env: NodeJS.ProcessEnv;
|
|
16
|
+
/** Lê uma linha do usuário (ex.: URL, usuário). */
|
|
17
|
+
prompt(question: string): Promise<string>;
|
|
18
|
+
/** Lê um valor sensível sem echo (senha, api_key). */
|
|
19
|
+
promptPassword(question: string): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* `true` quando o stdout real é um TTY — decide se `redmine-context` sem
|
|
22
|
+
* argumentos abre a TUI (M2-01) ou degrada para o help (padrão atual).
|
|
23
|
+
* Opcional: nos testes, ausência equivale a `false` (não-TTY).
|
|
24
|
+
*/
|
|
25
|
+
isTTY?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Store da URL da instância persistida (#187): o `login` a grava e `issue`/`login`
|
|
28
|
+
* a usam como fallback além de `--url`/`REDMINE_URL`. Opcional: ausente nos testes
|
|
29
|
+
* (sem persistência), presente na execução real (`defaultSettingsStore`).
|
|
30
|
+
*/
|
|
31
|
+
settings?: SettingsStore;
|
|
32
|
+
}
|
|
33
|
+
/** Resultado do parse manual de `argv`: posicionais + flags. */
|
|
34
|
+
export interface ParsedArgs {
|
|
35
|
+
/** Argumentos posicionais na ordem (ex.: `['issue', '42']`). */
|
|
36
|
+
positionals: string[];
|
|
37
|
+
/** Flags parseadas: `true` para booleanas, string para as com valor. */
|
|
38
|
+
flags: Map<string, string | boolean>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Superfície MCP (stdio) do `redmine-context` (M1-12).
|
|
3
|
+
*
|
|
4
|
+
* Expõe a tool read-only `get_issue_context(issue_id, format?)`, que reutiliza a
|
|
5
|
+
* orquestração `fetchIssueBundle` do core. Segue a mesma fronteira da CLI
|
|
6
|
+
* (ADR-005): importa o core EXCLUSIVAMENTE por `../../index.js`; nenhum módulo
|
|
7
|
+
* interno é acessado diretamente (regra eslint `no-restricted-imports`).
|
|
8
|
+
*
|
|
9
|
+
* Segurança: NENHUMA tool aceita URL/host arbitrário — a instância vem sempre da
|
|
10
|
+
* config/env do processo (`REDMINE_URL`; sem ela, a URL persistida no `login`,
|
|
11
|
+
* #187) + cascata de credencial. Quando a URL vem da persistida (fonte mutável),
|
|
12
|
+
* a credencial de AMBIENTE (`REDMINE_API_KEY`, instance-agnóstica) é DESABILITADA
|
|
13
|
+
* — só credencial pinada à instância (keychain/arquivo) é aceita, impedindo que um
|
|
14
|
+
* `settings.json` adulterado exfiltre a chave (ver `resolveInstance`). No transporte
|
|
15
|
+
* stdio o protocolo ocupa o stdout, portanto todo diagnóstico vai para stderr.
|
|
16
|
+
*/
|
|
17
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
18
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
19
|
+
import * as core from '../../index.js';
|
|
20
|
+
import type { AttachmentTextResult, FetchAttachmentTextOptions, resolveApiKey } from '../../index.js';
|
|
21
|
+
/** Formato aceito pela tool MCP (nomes amigáveis expostos ao cliente). */
|
|
22
|
+
export type McpFormat = 'markdown' | 'json';
|
|
23
|
+
/** Argumentos da tool `get_issue_context` já validados pelo schema zod. */
|
|
24
|
+
export interface GetIssueContextArgs {
|
|
25
|
+
/** Identificador numérico da issue no Redmine. */
|
|
26
|
+
issue_id: number;
|
|
27
|
+
/** Formato de saída: `markdown` (padrão) ou `json`. */
|
|
28
|
+
format?: McpFormat | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Extrai o texto dos anexos de imagem (OCR) e o embute no bundle. Default:
|
|
31
|
+
* `false` — a extração adiciona latência (download + OCR por anexo). O M4 trará
|
|
32
|
+
* o modo cache-first/processing assíncrono; no M3 a extração é síncrona.
|
|
33
|
+
*/
|
|
34
|
+
extract_attachments?: boolean | undefined;
|
|
35
|
+
}
|
|
36
|
+
/** Argumentos da tool `get_attachment_text` já validados pelo schema zod. */
|
|
37
|
+
export interface GetAttachmentTextArgs {
|
|
38
|
+
/** Identificador numérico da issue que contém o anexo. */
|
|
39
|
+
issue_id: number;
|
|
40
|
+
/** Identificador numérico do anexo cujo texto será extraído. */
|
|
41
|
+
attachment_id: number;
|
|
42
|
+
}
|
|
43
|
+
/** Argumentos da tool `search_issues` já validados pelo schema zod. */
|
|
44
|
+
export interface SearchIssuesArgs {
|
|
45
|
+
/** Termo full-text opcional (`/search.json`, best-effort). */
|
|
46
|
+
query?: string | undefined;
|
|
47
|
+
/** Filtro `project_id`. */
|
|
48
|
+
project_id?: number | undefined;
|
|
49
|
+
/** Filtro `status_id` (`'open'`, `'closed'`, `'*'` ou um id). */
|
|
50
|
+
status_id?: number | string | undefined;
|
|
51
|
+
/** Filtro `assigned_to_id` (um id ou `'me'`). */
|
|
52
|
+
assigned_to_id?: number | string | undefined;
|
|
53
|
+
/** Filtro `updated_on` no formato do Redmine (ex.: `>=2026-01-01`). */
|
|
54
|
+
updated_on?: string | undefined;
|
|
55
|
+
/** Máximo de resultados. Default: {@link SEARCH_DEFAULT_LIMIT}. */
|
|
56
|
+
limit?: number | undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Dependências injetáveis do server MCP — permitem testar o handler sem tocar o
|
|
60
|
+
* processo real nem a rede. Os defaults (ver {@link defaultMcpDeps}) apontam para
|
|
61
|
+
* o core e o `process.env` reais.
|
|
62
|
+
*/
|
|
63
|
+
export interface McpServerDeps {
|
|
64
|
+
/** Orquestração get → normalize → bundle do core. */
|
|
65
|
+
fetchIssueBundle: typeof core.fetchIssueBundle;
|
|
66
|
+
/** Orquestração de busca (filtros + full-text best-effort) do core. */
|
|
67
|
+
searchIssues: typeof core.fetchIssueSearch;
|
|
68
|
+
/**
|
|
69
|
+
* Orquestração get → normalize → extração CACHE-FIRST de UM anexo (M4-11 #70):
|
|
70
|
+
* devolve o texto já cacheado na hora e `processing` (sem bloquear) para mídia
|
|
71
|
+
* ainda não processada. Default: {@link core.fetchAttachmentTextCacheFirst}.
|
|
72
|
+
* Tipada pela assinatura base para aceitar tanto a variante cache-first quanto
|
|
73
|
+
* a síncrona (M3) nos testes.
|
|
74
|
+
*/
|
|
75
|
+
fetchAttachmentText: (options: FetchAttachmentTextOptions) => Promise<AttachmentTextResult>;
|
|
76
|
+
/** Resolução de credencial pela cascata (arquivo → env). */
|
|
77
|
+
resolveApiKey: typeof resolveApiKey;
|
|
78
|
+
/** Ambiente consultado para `REDMINE_URL` e pela cascata de credencial. */
|
|
79
|
+
env: NodeJS.ProcessEnv;
|
|
80
|
+
/**
|
|
81
|
+
* Store da URL da instância persistida (#187): fallback quando `REDMINE_URL`
|
|
82
|
+
* não está no ambiente do processo (ex.: registro MCP sem a env, mas com
|
|
83
|
+
* `login` já feito). Continua uma ÚNICA instância configurada — nunca vem dos
|
|
84
|
+
* argumentos das tools. Opcional (ausente nos testes que fixam a env).
|
|
85
|
+
*/
|
|
86
|
+
settings?: core.SettingsStore;
|
|
87
|
+
/** Versão da ferramenta gravada no bundle. */
|
|
88
|
+
toolVersion: string;
|
|
89
|
+
/**
|
|
90
|
+
* Permite `http://` (sem TLS) com aviso ruidoso. Derivado de `REDMINE_INSECURE`
|
|
91
|
+
* no ambiente do processo. Default: `false` (só `https://`). Espelha a flag
|
|
92
|
+
* `--insecure` da CLI para os ambientes de teste/CI que usam http local.
|
|
93
|
+
*/
|
|
94
|
+
insecure?: boolean;
|
|
95
|
+
/** Sink de diagnóstico (progresso/erros) — SEMPRE stderr no stdio transport. */
|
|
96
|
+
log?: (message: string) => void;
|
|
97
|
+
}
|
|
98
|
+
/** Nome canônico da tool de contexto de issue. */
|
|
99
|
+
export declare const TOOL_NAME = "get_issue_context";
|
|
100
|
+
/** Nome canônico da tool de busca de issues. */
|
|
101
|
+
export declare const SEARCH_TOOL_NAME = "search_issues";
|
|
102
|
+
/** Nome canônico da tool de texto de anexo. */
|
|
103
|
+
export declare const ATTACHMENT_TOOL_NAME = "get_attachment_text";
|
|
104
|
+
/** Limite default de resultados da tool `search_issues` (documentado no schema). */
|
|
105
|
+
export declare const SEARCH_DEFAULT_LIMIT = 25;
|
|
106
|
+
/**
|
|
107
|
+
* Cria o handler da tool `get_issue_context`, testável isoladamente.
|
|
108
|
+
*
|
|
109
|
+
* Resolve a instância/credencial a partir da env (nunca de argumentos), invoca
|
|
110
|
+
* `fetchIssueBundle` e devolve o bundle. Erros viram `isError` com mensagem
|
|
111
|
+
* tipada; a ausência de `REDMINE_URL`/credencial orienta a configuração.
|
|
112
|
+
*
|
|
113
|
+
* @param deps - Ver {@link McpServerDeps}.
|
|
114
|
+
* @returns Função assíncrona que recebe os argumentos e devolve um CallToolResult.
|
|
115
|
+
* @example
|
|
116
|
+
* const handler = createGetIssueContextHandler(defaultMcpDeps());
|
|
117
|
+
* const result = await handler({ issue_id: 42, format: 'json' });
|
|
118
|
+
*/
|
|
119
|
+
export declare function createGetIssueContextHandler(deps: McpServerDeps): (args: GetIssueContextArgs) => Promise<CallToolResult>;
|
|
120
|
+
/**
|
|
121
|
+
* Cria o handler da tool `search_issues`, testável isoladamente.
|
|
122
|
+
*
|
|
123
|
+
* Resolve a instância/credencial da env (nunca de argumentos), delega à
|
|
124
|
+
* orquestração `fetchIssueSearch` (filtros + full-text best-effort) e devolve a
|
|
125
|
+
* lista compacta em Markdown. A degradação da busca full-text NÃO é erro: o
|
|
126
|
+
* aviso já vem embutido no payload retornado pelo core.
|
|
127
|
+
*
|
|
128
|
+
* @param deps - Ver {@link McpServerDeps}.
|
|
129
|
+
* @returns Função assíncrona que recebe os argumentos e devolve um CallToolResult.
|
|
130
|
+
* @example
|
|
131
|
+
* const handler = createSearchIssuesHandler(defaultMcpDeps());
|
|
132
|
+
* const result = await handler({ query: 'timeout', project_id: 5 });
|
|
133
|
+
*/
|
|
134
|
+
export declare function createSearchIssuesHandler(deps: McpServerDeps): (args: SearchIssuesArgs) => Promise<CallToolResult>;
|
|
135
|
+
/**
|
|
136
|
+
* Cria o handler da tool `get_attachment_text`, testável isoladamente.
|
|
137
|
+
*
|
|
138
|
+
* Resolve a instância/credencial da env (nunca de argumentos), delega à
|
|
139
|
+
* orquestração cache-first `fetchAttachmentText` (texto já cacheado volta na
|
|
140
|
+
* hora; mídia pesada ainda não processada volta como `processing` SEM bloquear —
|
|
141
|
+
* M4-11 #70) e devolve o texto extraído dentro da fence untrusted. Anexo não
|
|
142
|
+
* processável / `processing` vira status legível (não `isError`); 403/404 do
|
|
143
|
+
* Redmine e anexo inexistente viram `isError` tipado.
|
|
144
|
+
*
|
|
145
|
+
* @param deps - Ver {@link McpServerDeps}.
|
|
146
|
+
* @returns Função assíncrona que recebe os argumentos e devolve um CallToolResult.
|
|
147
|
+
* @example
|
|
148
|
+
* const handler = createGetAttachmentTextHandler(defaultMcpDeps());
|
|
149
|
+
* const result = await handler({ issue_id: 42, attachment_id: 77 });
|
|
150
|
+
*/
|
|
151
|
+
export declare function createGetAttachmentTextHandler(deps: McpServerDeps): (args: GetAttachmentTextArgs) => Promise<CallToolResult>;
|
|
152
|
+
/**
|
|
153
|
+
* Constrói um {@link McpServer} com a tool `get_issue_context` registrada.
|
|
154
|
+
*
|
|
155
|
+
* A tool é read-only e não expõe URL/host — a instância vem sempre da env.
|
|
156
|
+
*
|
|
157
|
+
* @param deps - Ver {@link McpServerDeps}.
|
|
158
|
+
* @returns O server pronto para `connect(transport)`.
|
|
159
|
+
*/
|
|
160
|
+
export declare function createMcpServer(deps: McpServerDeps): McpServer;
|
|
161
|
+
/** Constrói as dependências default apontando para o core e o processo reais. */
|
|
162
|
+
export declare function defaultMcpDeps(): McpServerDeps;
|
|
163
|
+
/**
|
|
164
|
+
* Sobe o server MCP no transporte stdio e resolve quando a conexão fecha.
|
|
165
|
+
*
|
|
166
|
+
* O stdout é reservado ao protocolo; qualquer log vai para stderr.
|
|
167
|
+
*
|
|
168
|
+
* @param overrides - Dependências injetáveis (defaults via {@link defaultMcpDeps}).
|
|
169
|
+
* @returns Promise que resolve quando o transporte encerra.
|
|
170
|
+
*/
|
|
171
|
+
export declare function runStdioServer(overrides?: Partial<McpServerDeps>): Promise<void>;
|