@tensor.chat/companion 0.4.3 → 0.4.6
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 +3 -1
- package/bin/tensor-companion.js +4 -4
- package/package.json +1 -1
- package/src/daemon.js +8 -86
- package/src/setup/i18n.js +10 -131
- package/src/setup/lang/de.js +68 -0
- package/src/setup/lang/en.js +68 -0
- package/src/setup/lang/es.js +68 -0
- package/src/setup/lang/fr.js +69 -0
- package/src/setup/lang/hi.js +68 -0
- package/src/setup/lang/pt.js +68 -0
- package/src/setup/lang/ru.js +69 -0
- package/src/setup/lang/zh.js +68 -0
- package/src/setup/layout.js +123 -5
- package/src/setup/term.js +4 -0
- package/src/setup/wizard.js +58 -33
- package/src/toolExecutor.js +85 -0
- package/src/toolRunner.js +85 -0
- package/src/toolWorker.js +47 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// The setup's words in French. is the no-break space French sets before
|
|
3
|
+
// ? : ! and between a number and its word.
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
setup: 'configuration',
|
|
7
|
+
steps: ['Quoi', 'Dossiers', 'Commandes', 'Enregistrer'],
|
|
8
|
+
'start.title': 'Que peut utiliser l’IA sur cet ordinateur ?',
|
|
9
|
+
'start.folders': 'Choisir des dossiers', 'start.foldersNote': 'recommandé',
|
|
10
|
+
'start.nothing': 'Rien pour l’instant', 'start.nothingNote': 'une discussion demande quand elle a besoin d’un dossier',
|
|
11
|
+
'start.everything': 'Tout', 'start.everythingNote': 'déconseillé',
|
|
12
|
+
'start.help': 'L’IA lit, modifie et supprime des fichiers uniquement là où vous l’autorisez ici.',
|
|
13
|
+
'start.now': 'Approuvé actuellement :',
|
|
14
|
+
'start.later': 'Modifiable à tout moment :',
|
|
15
|
+
'places.title': 'Quels dossiers l’IA peut-elle utiliser ?',
|
|
16
|
+
'places.loading': 'Recherche de dossiers…',
|
|
17
|
+
'places.none': 'Aucune suggestion sur cet ordinateur.',
|
|
18
|
+
'places.browse': 'Parcourir tous les dossiers…', 'places.type': 'Saisir un chemin…', 'places.continue': 'Continuer',
|
|
19
|
+
selected: 'Sélection : {n}',
|
|
20
|
+
'note.git': '{n} projets Git', 'note.git1': '1 projet Git', 'note.project': 'projet Git',
|
|
21
|
+
'note.web': 'sites web', 'note.srv': 'données du serveur', 'note.opt': 'programmes installés',
|
|
22
|
+
'note.etc': 'réglages du système', 'note.log': 'journaux', 'note.home': 'tout votre dossier personnel',
|
|
23
|
+
'note.readOnly': 'lecture seule pour vous', 'note.added': 'ajouté par vous', 'note.earlier': 'déjà approuvé',
|
|
24
|
+
'note.inside': 'dans {path}',
|
|
25
|
+
'help.home': 'Tout ce qui se trouve dans {home}, y compris les réglages cachés et les clés comme ~/.ssh.',
|
|
26
|
+
'help.etc': 'Réglages de tout le système. Une mauvaise modification peut arrêter le serveur.',
|
|
27
|
+
'help.log': 'Journaux du système et de ses services. Utiles pour trouver des erreurs.',
|
|
28
|
+
'help.web': 'Fichiers des sites web hébergés sur ce serveur.',
|
|
29
|
+
'help.srv': 'Données des services de ce serveur.',
|
|
30
|
+
'help.opt': 'Programmes installés en dehors du gestionnaire de paquets.',
|
|
31
|
+
'help.code': 'Vos projets de code.',
|
|
32
|
+
'help.readOnly': 'Vous pouvez lire ici, mais pas écrire. L’IA peut faire de même, pas plus.',
|
|
33
|
+
'help.browse': 'Choisissez n’importe quel dossier de cet ordinateur, comme dans un gestionnaire de fichiers.',
|
|
34
|
+
'help.type': 'Saisissez un chemin complet. Tab le complète.',
|
|
35
|
+
'help.continue': 'Ensuite : les commandes.',
|
|
36
|
+
'browse.title': 'Parcourir', 'browse.thisPc': 'Ce PC',
|
|
37
|
+
'browse.loading': 'chargement…', 'browse.empty': 'aucun dossier', 'browse.noAccess': 'pas d’accès', 'browse.link': 'lien',
|
|
38
|
+
'browse.blocked': '{path} se trouve dans {parent}, qui est déjà sélectionné.',
|
|
39
|
+
'browse.hidden': 'Les dossiers cachés sont affichés.', 'browse.hiddenOff': 'Les dossiers cachés sont masqués.',
|
|
40
|
+
'type.title': 'Saisir le chemin d’un dossier', 'type.label': 'Chemin',
|
|
41
|
+
'type.notFound': 'Le dossier {path} n’existe pas.',
|
|
42
|
+
'everything.title': 'Tout partager ?',
|
|
43
|
+
'everything.body': 'L’IA peut lire, modifier et supprimer tous les fichiers auxquels {user} a accès sur cet ordinateur.',
|
|
44
|
+
'everything.root': 'Vous êtes root : cela couvre tous les fichiers du système, y compris le système lui-même.',
|
|
45
|
+
'everything.free': 'L’IA peut ainsi travailler librement sur ce serveur. Déconseillé.',
|
|
46
|
+
'everything.no': 'Non, revenir', 'everything.yes': 'Oui, tout partager',
|
|
47
|
+
'everything.summary': 'Tout sur cet ordinateur',
|
|
48
|
+
'shell.title': 'L’IA peut-elle exécuter des commandes ici ?',
|
|
49
|
+
'shell.body': 'Les commandes s’exécutent en tant que {user}, avec un accès Internet complet. Elles ne se limitent pas aux dossiers partagés.',
|
|
50
|
+
'shell.keep': 'Laisser ainsi', 'shell.keepNote': 'jusqu’au {date}',
|
|
51
|
+
'shell.no': 'Pas maintenant', 'shell.noNote': 'une discussion demande d’abord quand elle en a besoin',
|
|
52
|
+
'shell.off': 'Désactiver', 'shell.offNote': 'une discussion demande d’abord quand elle en a besoin',
|
|
53
|
+
'shell.d7': 'Oui, pour 7 jours', 'shell.d30': 'Oui, pour 30 jours', 'shell.d365': 'Oui, pour un an',
|
|
54
|
+
'review.title': 'Vérifier et enregistrer',
|
|
55
|
+
'review.folders': 'Dossiers', 'review.commands': 'Commandes',
|
|
56
|
+
'review.noFolders': 'aucun — une discussion demande quand elle en a besoin',
|
|
57
|
+
'review.removed': 'approbation retirée',
|
|
58
|
+
'review.cmdNo': 'non approuvées — une discussion demande d’abord',
|
|
59
|
+
'review.cmdUntil': 'approuvées jusqu’au {date}',
|
|
60
|
+
'review.save': 'Enregistrer', 'review.back': 'Retour', 'review.quit': 'Quitter sans enregistrer',
|
|
61
|
+
'review.where': 'Enregistré uniquement sur cet ordinateur, dans {file}',
|
|
62
|
+
keyNames: { space: 'espace', enter: 'entrée', esc: 'échap', tab: 'tab', backspace: 'retour arrière' },
|
|
63
|
+
keys: { move: 'déplacer', select: 'valider', toggle: 'cocher', back: 'retour', quit: 'quitter', open: 'ouvrir', close: 'fermer', openClose: 'ouvrir/fermer', up: 'dossier parent', hidden: 'cachés', done: 'terminer', tab: 'compléter', add: 'ajouter' },
|
|
64
|
+
'done.saved': 'Enregistré sur cet ordinateur.',
|
|
65
|
+
'done.unchanged': 'Aucune modification.',
|
|
66
|
+
'done.web': 'Quand une discussion sur tensor.chat partage l’un de ces dossiers, cet ordinateur le confirme de lui-même.',
|
|
67
|
+
'done.run': 'Démarrez le Companion avec :',
|
|
68
|
+
'done.noTty': 'La configuration nécessite un terminal interactif. Sans terminal, utilisez approve-folder et approve-shell.',
|
|
69
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// The setup's words in Hindi.
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
setup: 'सेटअप',
|
|
6
|
+
steps: ['क्या', 'फ़ोल्डर', 'कमांड', 'सहेजें'],
|
|
7
|
+
'start.title': 'AI इस कंप्यूटर पर क्या इस्तेमाल कर सकता है?',
|
|
8
|
+
'start.folders': 'फ़ोल्डर चुनें', 'start.foldersNote': 'अनुशंसित',
|
|
9
|
+
'start.nothing': 'अभी कुछ नहीं', 'start.nothingNote': 'फ़ोल्डर की ज़रूरत होने पर चैट पूछती है',
|
|
10
|
+
'start.everything': 'सब कुछ', 'start.everythingNote': 'अनुशंसित नहीं',
|
|
11
|
+
'start.help': 'AI फ़ाइलें केवल वहीं पढ़ता, बदलता और हटाता है जहाँ आप यहाँ अनुमति देते हैं।',
|
|
12
|
+
'start.now': 'अभी स्वीकृत:',
|
|
13
|
+
'start.later': 'इसे कभी भी बदलें:',
|
|
14
|
+
'places.title': 'AI कौन-से फ़ोल्डर इस्तेमाल कर सकता है?',
|
|
15
|
+
'places.loading': 'फ़ोल्डर खोजे जा रहे हैं…',
|
|
16
|
+
'places.none': 'इस कंप्यूटर पर कोई सुझाव नहीं।',
|
|
17
|
+
'places.browse': 'सभी फ़ोल्डर ब्राउज़ करें…', 'places.type': 'पथ लिखें…', 'places.continue': 'जारी रखें',
|
|
18
|
+
selected: 'चयनित: {n}',
|
|
19
|
+
'note.git': '{n} Git प्रोजेक्ट', 'note.git1': '1 Git प्रोजेक्ट', 'note.project': 'Git प्रोजेक्ट',
|
|
20
|
+
'note.web': 'वेबसाइटें', 'note.srv': 'सर्वर डेटा', 'note.opt': 'इंस्टॉल किए गए प्रोग्राम',
|
|
21
|
+
'note.etc': 'सिस्टम सेटिंग्स', 'note.log': 'लॉग', 'note.home': 'आपका पूरा होम फ़ोल्डर',
|
|
22
|
+
'note.readOnly': 'आपके लिए केवल पढ़ने योग्य', 'note.added': 'आपने जोड़ा', 'note.earlier': 'पहले से स्वीकृत',
|
|
23
|
+
'note.inside': '{path} के अंदर',
|
|
24
|
+
'help.home': '{home} में सब कुछ, छिपी सेटिंग्स और ~/.ssh जैसी कुंजियाँ भी।',
|
|
25
|
+
'help.etc': 'पूरे सिस्टम की सेटिंग्स। वहाँ एक गलत बदलाव सर्वर को रोक सकता है।',
|
|
26
|
+
'help.log': 'सिस्टम और उसकी सेवाओं के लॉग। गड़बड़ी ढूँढने में काम आते हैं।',
|
|
27
|
+
'help.web': 'उन वेबसाइटों की फ़ाइलें जिन्हें यह सर्वर दिखाता है।',
|
|
28
|
+
'help.srv': 'इस सर्वर की सेवाओं का डेटा।',
|
|
29
|
+
'help.opt': 'पैकेज मैनेजर के बाहर इंस्टॉल किए गए प्रोग्राम।',
|
|
30
|
+
'help.code': 'आपके कोड प्रोजेक्ट।',
|
|
31
|
+
'help.readOnly': 'आप यहाँ पढ़ सकते हैं, लिख नहीं सकते। AI भी इतना ही कर सकता है, इससे ज़्यादा नहीं।',
|
|
32
|
+
'help.browse': 'इस कंप्यूटर पर कोई भी फ़ोल्डर चुनें, जैसे किसी फ़ाइल मैनेजर में।',
|
|
33
|
+
'help.type': 'पूरा पथ लिखें। Tab उसे पूरा करता है।',
|
|
34
|
+
'help.continue': 'आगे: कमांड।',
|
|
35
|
+
'browse.title': 'ब्राउज़ करें', 'browse.thisPc': 'यह पीसी',
|
|
36
|
+
'browse.loading': 'लोड हो रहा है…', 'browse.empty': 'कोई फ़ोल्डर नहीं', 'browse.noAccess': 'पहुँच नहीं', 'browse.link': 'लिंक',
|
|
37
|
+
'browse.blocked': '{path}, {parent} के अंदर है, और वह पहले से चुना हुआ है।',
|
|
38
|
+
'browse.hidden': 'छिपे फ़ोल्डर दिखाए जा रहे हैं।', 'browse.hiddenOff': 'छिपे फ़ोल्डर नहीं दिखाए जा रहे।',
|
|
39
|
+
'type.title': 'फ़ोल्डर का पथ लिखें', 'type.label': 'पथ',
|
|
40
|
+
'type.notFound': '{path} नाम का कोई फ़ोल्डर नहीं है।',
|
|
41
|
+
'everything.title': 'सब कुछ साझा करें?',
|
|
42
|
+
'everything.body': 'AI इस कंप्यूटर पर हर वह फ़ाइल पढ़, बदल और हटा सकता है जिस तक {user} पहुँच सकता है।',
|
|
43
|
+
'everything.root': 'आप root हैं: यानी सिस्टम की हर फ़ाइल, खुद सिस्टम भी।',
|
|
44
|
+
'everything.free': 'इससे AI इस सर्वर पर खुलकर काम कर सकता है। अनुशंसित नहीं।',
|
|
45
|
+
'everything.no': 'नहीं, वापस', 'everything.yes': 'हाँ, सब कुछ साझा करें',
|
|
46
|
+
'everything.summary': 'इस कंप्यूटर पर सब कुछ',
|
|
47
|
+
'shell.title': 'क्या AI यहाँ कमांड चला सकता है?',
|
|
48
|
+
'shell.body': 'कमांड {user} के रूप में, पूरी इंटरनेट पहुँच के साथ चलते हैं। वे साझा फ़ोल्डरों तक सीमित नहीं हैं।',
|
|
49
|
+
'shell.keep': 'जैसा है वैसा रखें', 'shell.keepNote': '{date} तक',
|
|
50
|
+
'shell.no': 'अभी नहीं', 'shell.noNote': 'ज़रूरत होने पर चैट पहले पूछती है',
|
|
51
|
+
'shell.off': 'बंद करें', 'shell.offNote': 'ज़रूरत होने पर चैट पहले पूछती है',
|
|
52
|
+
'shell.d7': 'हाँ, 7 दिनों के लिए', 'shell.d30': 'हाँ, 30 दिनों के लिए', 'shell.d365': 'हाँ, एक साल के लिए',
|
|
53
|
+
'review.title': 'जाँचें और सहेजें',
|
|
54
|
+
'review.folders': 'फ़ोल्डर', 'review.commands': 'कमांड',
|
|
55
|
+
'review.noFolders': 'कोई नहीं — ज़रूरत होने पर चैट पूछती है',
|
|
56
|
+
'review.removed': 'स्वीकृति हटाई गई',
|
|
57
|
+
'review.cmdNo': 'स्वीकृत नहीं — चैट पहले पूछती है',
|
|
58
|
+
'review.cmdUntil': '{date} तक स्वीकृत',
|
|
59
|
+
'review.save': 'सहेजें', 'review.back': 'वापस', 'review.quit': 'बिना सहेजे बाहर निकलें',
|
|
60
|
+
'review.where': 'केवल इस कंप्यूटर पर सहेजा जाता है, {file} में',
|
|
61
|
+
keyNames: { space: 'स्पेस', enter: 'Enter', esc: 'Esc', tab: 'Tab', backspace: 'Backspace' },
|
|
62
|
+
keys: { move: 'चलें', select: 'चुनें', toggle: 'टिक करें', back: 'वापस', quit: 'बाहर निकलें', open: 'खोलें', close: 'बंद करें', openClose: 'खोलें/बंद करें', up: 'ऊपर का फ़ोल्डर', hidden: 'छिपे', done: 'हो गया', tab: 'पूरा करें', add: 'जोड़ें' },
|
|
63
|
+
'done.saved': 'इस कंप्यूटर पर सहेजा गया।',
|
|
64
|
+
'done.unchanged': 'कुछ नहीं बदला।',
|
|
65
|
+
'done.web': 'जब tensor.chat पर कोई चैट इनमें से कोई फ़ोल्डर साझा करती है, तो यह कंप्यूटर खुद उसकी पुष्टि कर देता है।',
|
|
66
|
+
'done.run': 'Companion को इससे शुरू करें:',
|
|
67
|
+
'done.noTty': 'सेटअप के लिए इंटरैक्टिव टर्मिनल चाहिए। उसके बिना approve-folder और approve-shell इस्तेमाल करें।',
|
|
68
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// The setup's words in Portuguese (Brazil, as on the site).
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
setup: 'configuração',
|
|
6
|
+
steps: ['O quê', 'Pastas', 'Comandos', 'Salvar'],
|
|
7
|
+
'start.title': 'O que a IA pode usar neste computador?',
|
|
8
|
+
'start.folders': 'Escolher pastas', 'start.foldersNote': 'recomendado',
|
|
9
|
+
'start.nothing': 'Nada por enquanto', 'start.nothingNote': 'uma conversa pergunta quando precisar de uma pasta',
|
|
10
|
+
'start.everything': 'Tudo', 'start.everythingNote': 'não recomendado',
|
|
11
|
+
'start.help': 'A IA lê, altera e exclui arquivos somente onde você permitir aqui.',
|
|
12
|
+
'start.now': 'Aprovado agora:',
|
|
13
|
+
'start.later': 'Altere quando quiser:',
|
|
14
|
+
'places.title': 'Quais pastas a IA pode usar?',
|
|
15
|
+
'places.loading': 'Procurando pastas…',
|
|
16
|
+
'places.none': 'Nenhuma sugestão neste computador.',
|
|
17
|
+
'places.browse': 'Procurar em todas as pastas…', 'places.type': 'Digitar um caminho…', 'places.continue': 'Continuar',
|
|
18
|
+
selected: 'Seleção: {n}',
|
|
19
|
+
'note.git': '{n} projetos Git', 'note.git1': '1 projeto Git', 'note.project': 'projeto Git',
|
|
20
|
+
'note.web': 'sites', 'note.srv': 'dados do servidor', 'note.opt': 'programas instalados',
|
|
21
|
+
'note.etc': 'configurações do sistema', 'note.log': 'logs', 'note.home': 'toda a sua pasta pessoal',
|
|
22
|
+
'note.readOnly': 'somente leitura para você', 'note.added': 'adicionada por você', 'note.earlier': 'aprovada antes',
|
|
23
|
+
'note.inside': 'dentro de {path}',
|
|
24
|
+
'help.home': 'Tudo em {home}, inclusive configurações ocultas e chaves como ~/.ssh.',
|
|
25
|
+
'help.etc': 'Configurações de todo o sistema. Uma alteração errada ali pode derrubar o servidor.',
|
|
26
|
+
'help.log': 'Logs do sistema e de seus serviços. Úteis para encontrar erros.',
|
|
27
|
+
'help.web': 'Arquivos dos sites hospedados neste servidor.',
|
|
28
|
+
'help.srv': 'Dados dos serviços deste servidor.',
|
|
29
|
+
'help.opt': 'Programas instalados fora do gerenciador de pacotes.',
|
|
30
|
+
'help.code': 'Seus projetos de código.',
|
|
31
|
+
'help.readOnly': 'Aqui você pode ler, mas não escrever. A IA pode fazer o mesmo, nada mais.',
|
|
32
|
+
'help.browse': 'Escolha qualquer pasta deste computador, como em um gerenciador de arquivos.',
|
|
33
|
+
'help.type': 'Digite um caminho completo. Tab completa.',
|
|
34
|
+
'help.continue': 'Próximo: comandos.',
|
|
35
|
+
'browse.title': 'Procurar', 'browse.thisPc': 'Este Computador',
|
|
36
|
+
'browse.loading': 'carregando…', 'browse.empty': 'nenhuma pasta', 'browse.noAccess': 'sem acesso', 'browse.link': 'link',
|
|
37
|
+
'browse.blocked': '{path} está dentro de {parent}, que já está selecionada.',
|
|
38
|
+
'browse.hidden': 'As pastas ocultas estão visíveis.', 'browse.hiddenOff': 'As pastas ocultas não são mostradas.',
|
|
39
|
+
'type.title': 'Digite o caminho de uma pasta', 'type.label': 'Caminho',
|
|
40
|
+
'type.notFound': 'A pasta {path} não existe.',
|
|
41
|
+
'everything.title': 'Compartilhar tudo?',
|
|
42
|
+
'everything.body': 'A IA pode ler, alterar e excluir qualquer arquivo que {user} consiga acessar neste computador.',
|
|
43
|
+
'everything.root': 'Você é root: isso inclui cada arquivo do sistema, inclusive o próprio sistema.',
|
|
44
|
+
'everything.free': 'Assim a IA pode trabalhar livremente neste servidor. Não recomendado.',
|
|
45
|
+
'everything.no': 'Não, voltar', 'everything.yes': 'Sim, compartilhar tudo',
|
|
46
|
+
'everything.summary': 'Tudo neste computador',
|
|
47
|
+
'shell.title': 'A IA pode executar comandos aqui?',
|
|
48
|
+
'shell.body': 'Os comandos são executados como {user}, com acesso total à Internet. Eles não se limitam às pastas compartilhadas.',
|
|
49
|
+
'shell.keep': 'Manter como está', 'shell.keepNote': 'até {date}',
|
|
50
|
+
'shell.no': 'Agora não', 'shell.noNote': 'uma conversa pergunta antes quando precisar deles',
|
|
51
|
+
'shell.off': 'Desativar', 'shell.offNote': 'uma conversa pergunta antes quando precisar deles',
|
|
52
|
+
'shell.d7': 'Sim, por 7 dias', 'shell.d30': 'Sim, por 30 dias', 'shell.d365': 'Sim, por um ano',
|
|
53
|
+
'review.title': 'Revisar e salvar',
|
|
54
|
+
'review.folders': 'Pastas', 'review.commands': 'Comandos',
|
|
55
|
+
'review.noFolders': 'nenhuma — uma conversa pergunta quando precisar de uma',
|
|
56
|
+
'review.removed': 'aprovação removida',
|
|
57
|
+
'review.cmdNo': 'não aprovados — uma conversa pergunta antes',
|
|
58
|
+
'review.cmdUntil': 'aprovados até {date}',
|
|
59
|
+
'review.save': 'Salvar', 'review.back': 'Voltar', 'review.quit': 'Sair sem salvar',
|
|
60
|
+
'review.where': 'Salvo somente neste computador, em {file}',
|
|
61
|
+
keyNames: { space: 'espaço', enter: 'enter', esc: 'esc', tab: 'tab', backspace: 'backspace' },
|
|
62
|
+
keys: { move: 'mover', select: 'escolher', toggle: 'marcar', back: 'voltar', quit: 'sair', open: 'abrir', close: 'fechar', openClose: 'abrir/fechar', up: 'pasta acima', hidden: 'ocultas', done: 'concluir', tab: 'completar', add: 'adicionar' },
|
|
63
|
+
'done.saved': 'Salvo neste computador.',
|
|
64
|
+
'done.unchanged': 'Nada foi alterado.',
|
|
65
|
+
'done.web': 'Quando uma conversa no tensor.chat compartilhar uma dessas pastas, este computador confirma isso sozinho.',
|
|
66
|
+
'done.run': 'Inicie o Companion com:',
|
|
67
|
+
'done.noTty': 'A configuração precisa de um terminal interativo. Sem um, use approve-folder e approve-shell.',
|
|
68
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// The setup's words in Russian. A count comes after a colon ("Выбрано: 3"), so no
|
|
3
|
+
// word has to take one of the three plural forms.
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
setup: 'настройка',
|
|
7
|
+
steps: ['Что', 'Папки', 'Команды', 'Сохранение'],
|
|
8
|
+
'start.title': 'Что ИИ может использовать на этом компьютере?',
|
|
9
|
+
'start.folders': 'Выбрать папки', 'start.foldersNote': 'рекомендуется',
|
|
10
|
+
'start.nothing': 'Пока ничего', 'start.nothingNote': 'чат спросит, когда ему понадобится папка',
|
|
11
|
+
'start.everything': 'Всё', 'start.everythingNote': 'не рекомендуется',
|
|
12
|
+
'start.help': 'ИИ читает, изменяет и удаляет файлы только там, где вы это здесь разрешите.',
|
|
13
|
+
'start.now': 'Сейчас разрешено:',
|
|
14
|
+
'start.later': 'Изменить можно в любой момент:',
|
|
15
|
+
'places.title': 'Какие папки может использовать ИИ?',
|
|
16
|
+
'places.loading': 'Ищем папки…',
|
|
17
|
+
'places.none': 'На этом компьютере нет предложений.',
|
|
18
|
+
'places.browse': 'Обзор всех папок…', 'places.type': 'Ввести путь…', 'places.continue': 'Продолжить',
|
|
19
|
+
selected: 'Выбрано: {n}',
|
|
20
|
+
'note.git': 'Git-проектов: {n}', 'note.git1': '1 Git-проект', 'note.project': 'Git-проект',
|
|
21
|
+
'note.web': 'сайты', 'note.srv': 'данные сервера', 'note.opt': 'установленные программы',
|
|
22
|
+
'note.etc': 'настройки системы', 'note.log': 'журналы', 'note.home': 'вся ваша домашняя папка',
|
|
23
|
+
'note.readOnly': 'для вас только чтение', 'note.added': 'добавлена вами', 'note.earlier': 'разрешена ранее',
|
|
24
|
+
'note.inside': 'внутри {path}',
|
|
25
|
+
'help.home': 'Всё в {home}, включая скрытые настройки и ключи, например ~/.ssh.',
|
|
26
|
+
'help.etc': 'Настройки всей системы. Неверное изменение здесь может остановить сервер.',
|
|
27
|
+
'help.log': 'Журналы системы и её служб. Помогают находить ошибки.',
|
|
28
|
+
'help.web': 'Файлы сайтов, которые отдаёт этот сервер.',
|
|
29
|
+
'help.srv': 'Данные служб на этом сервере.',
|
|
30
|
+
'help.opt': 'Программы, установленные в обход менеджера пакетов.',
|
|
31
|
+
'help.code': 'Ваши проекты с кодом.',
|
|
32
|
+
'help.readOnly': 'Здесь вы можете читать, но не писать. ИИ может то же самое, не больше.',
|
|
33
|
+
'help.browse': 'Выберите любую папку на этом компьютере, как в файловом менеджере.',
|
|
34
|
+
'help.type': 'Введите полный путь. Tab дополняет его.',
|
|
35
|
+
'help.continue': 'Далее: команды.',
|
|
36
|
+
'browse.title': 'Обзор', 'browse.thisPc': 'Этот компьютер',
|
|
37
|
+
'browse.loading': 'загрузка…', 'browse.empty': 'нет папок', 'browse.noAccess': 'нет доступа', 'browse.link': 'ссылка',
|
|
38
|
+
'browse.blocked': '{path} находится внутри {parent}, а эта папка уже выбрана.',
|
|
39
|
+
'browse.hidden': 'Скрытые папки показаны.', 'browse.hiddenOff': 'Скрытые папки не показываются.',
|
|
40
|
+
'type.title': 'Введите путь к папке', 'type.label': 'Путь',
|
|
41
|
+
'type.notFound': 'Папки {path} не существует.',
|
|
42
|
+
'everything.title': 'Открыть доступ ко всему?',
|
|
43
|
+
'everything.body': 'ИИ сможет читать, изменять и удалять любой файл, доступный пользователю {user} на этом компьютере.',
|
|
44
|
+
'everything.root': 'Вы root: это все файлы системы, включая саму систему.',
|
|
45
|
+
'everything.free': 'Так ИИ сможет свободно работать на этом сервере. Не рекомендуется.',
|
|
46
|
+
'everything.no': 'Нет, назад', 'everything.yes': 'Да, открыть доступ ко всему',
|
|
47
|
+
'everything.summary': 'Всё на этом компьютере',
|
|
48
|
+
'shell.title': 'Может ли ИИ выполнять здесь команды?',
|
|
49
|
+
'shell.body': 'Команды выполняются от имени {user}, с полным доступом в Интернет. Они не ограничены общими папками.',
|
|
50
|
+
'shell.keep': 'Оставить как есть', 'shell.keepNote': 'до {date}',
|
|
51
|
+
'shell.no': 'Не сейчас', 'shell.noNote': 'чат сначала спросит, когда они понадобятся',
|
|
52
|
+
'shell.off': 'Отключить', 'shell.offNote': 'чат сначала спросит, когда они понадобятся',
|
|
53
|
+
'shell.d7': 'Да, на 7 дней', 'shell.d30': 'Да, на 30 дней', 'shell.d365': 'Да, на год',
|
|
54
|
+
'review.title': 'Проверка и сохранение',
|
|
55
|
+
'review.folders': 'Папки', 'review.commands': 'Команды',
|
|
56
|
+
'review.noFolders': 'нет — чат спросит, когда ему понадобится папка',
|
|
57
|
+
'review.removed': 'разрешение отозвано',
|
|
58
|
+
'review.cmdNo': 'не разрешены — чат сначала спросит',
|
|
59
|
+
'review.cmdUntil': 'разрешены до {date}',
|
|
60
|
+
'review.save': 'Сохранить', 'review.back': 'Назад', 'review.quit': 'Выйти без сохранения',
|
|
61
|
+
'review.where': 'Сохраняется только на этом компьютере, в {file}',
|
|
62
|
+
keyNames: { space: 'пробел', enter: 'Enter', esc: 'Esc', tab: 'Tab', backspace: 'Backspace' },
|
|
63
|
+
keys: { move: 'перемещение', select: 'выбор', toggle: 'отметить', back: 'назад', quit: 'выход', open: 'открыть', close: 'закрыть', openClose: 'открыть/закрыть', up: 'папка выше', hidden: 'скрытые', done: 'готово', tab: 'дополнить', add: 'добавить' },
|
|
64
|
+
'done.saved': 'Сохранено на этом компьютере.',
|
|
65
|
+
'done.unchanged': 'Ничего не изменилось.',
|
|
66
|
+
'done.web': 'Когда чат на tensor.chat откроет доступ к одной из этих папок, этот компьютер подтвердит это сам.',
|
|
67
|
+
'done.run': 'Запустите Companion командой:',
|
|
68
|
+
'done.noTty': 'Для настройки нужен интерактивный терминал. Без него используйте approve-folder и approve-shell.',
|
|
69
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// The setup's words in Chinese (Simplified).
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
setup: '设置',
|
|
6
|
+
steps: ['范围', '文件夹', '命令', '保存'],
|
|
7
|
+
'start.title': 'AI 可以使用这台电脑上的哪些内容?',
|
|
8
|
+
'start.folders': '选择文件夹', 'start.foldersNote': '推荐',
|
|
9
|
+
'start.nothing': '暂不共享', 'start.nothingNote': '聊天需要文件夹时会询问',
|
|
10
|
+
'start.everything': '全部', 'start.everythingNote': '不推荐',
|
|
11
|
+
'start.help': 'AI 只会在你在这里允许的位置读取、修改和删除文件。',
|
|
12
|
+
'start.now': '当前已批准:',
|
|
13
|
+
'start.later': '随时可以更改:',
|
|
14
|
+
'places.title': 'AI 可以使用哪些文件夹?',
|
|
15
|
+
'places.loading': '正在查找文件夹…',
|
|
16
|
+
'places.none': '这台电脑上没有建议。',
|
|
17
|
+
'places.browse': '浏览所有文件夹…', 'places.type': '输入路径…', 'places.continue': '继续',
|
|
18
|
+
selected: '已选 {n} 个',
|
|
19
|
+
'note.git': '{n} 个 Git 项目', 'note.git1': '1 个 Git 项目', 'note.project': 'Git 项目',
|
|
20
|
+
'note.web': '网站', 'note.srv': '服务器数据', 'note.opt': '已安装的程序',
|
|
21
|
+
'note.etc': '系统设置', 'note.log': '日志', 'note.home': '你的整个主目录',
|
|
22
|
+
'note.readOnly': '你只能读取', 'note.added': '由你添加', 'note.earlier': '之前已批准',
|
|
23
|
+
'note.inside': '位于 {path} 内',
|
|
24
|
+
'help.home': '{home} 中的所有内容,也包括隐藏的设置和密钥,例如 ~/.ssh。',
|
|
25
|
+
'help.etc': '整个系统的设置。改错一处就可能让服务器停止运行。',
|
|
26
|
+
'help.log': '系统及其服务的日志。便于排查错误。',
|
|
27
|
+
'help.web': '此服务器提供的网站文件。',
|
|
28
|
+
'help.srv': '此服务器上各项服务的数据。',
|
|
29
|
+
'help.opt': '在软件包管理器之外安装的程序。',
|
|
30
|
+
'help.code': '你的代码项目。',
|
|
31
|
+
'help.readOnly': '你在这里只能读取,不能写入。AI 也只有同样的权限。',
|
|
32
|
+
'help.browse': '像在文件管理器中一样,选择这台电脑上的任意文件夹。',
|
|
33
|
+
'help.type': '输入完整路径。按 Tab 自动补全。',
|
|
34
|
+
'help.continue': '下一步:命令。',
|
|
35
|
+
'browse.title': '浏览', 'browse.thisPc': '此电脑',
|
|
36
|
+
'browse.loading': '加载中…', 'browse.empty': '没有文件夹', 'browse.noAccess': '无权访问', 'browse.link': '链接',
|
|
37
|
+
'browse.blocked': '{path} 位于 {parent} 内,而后者已被选择。',
|
|
38
|
+
'browse.hidden': '隐藏文件夹已显示。', 'browse.hiddenOff': '隐藏文件夹未显示。',
|
|
39
|
+
'type.title': '输入文件夹路径', 'type.label': '路径',
|
|
40
|
+
'type.notFound': '文件夹 {path} 不存在。',
|
|
41
|
+
'everything.title': '共享全部内容?',
|
|
42
|
+
'everything.body': 'AI 可以读取、修改和删除 {user} 在这台电脑上能访问的所有文件。',
|
|
43
|
+
'everything.root': '你是 root:这意味着系统中的所有文件,包括系统本身。',
|
|
44
|
+
'everything.free': '这样 AI 可以在这台服务器上自由工作。不推荐。',
|
|
45
|
+
'everything.no': '不,返回', 'everything.yes': '是,共享全部',
|
|
46
|
+
'everything.summary': '这台电脑上的全部内容',
|
|
47
|
+
'shell.title': 'AI 可以在这里运行命令吗?',
|
|
48
|
+
'shell.body': '命令以 {user} 身份运行,并可完全访问互联网。命令不限于共享的文件夹。',
|
|
49
|
+
'shell.keep': '保持不变', 'shell.keepNote': '到 {date} 为止',
|
|
50
|
+
'shell.no': '暂不', 'shell.noNote': '聊天需要时会先询问',
|
|
51
|
+
'shell.off': '关闭', 'shell.offNote': '聊天需要时会先询问',
|
|
52
|
+
'shell.d7': '是,7 天', 'shell.d30': '是,30 天', 'shell.d365': '是,一年',
|
|
53
|
+
'review.title': '检查并保存',
|
|
54
|
+
'review.folders': '文件夹', 'review.commands': '命令',
|
|
55
|
+
'review.noFolders': '无 — 聊天需要时会询问',
|
|
56
|
+
'review.removed': '已撤销批准',
|
|
57
|
+
'review.cmdNo': '未批准 — 聊天会先询问',
|
|
58
|
+
'review.cmdUntil': '已批准,到 {date} 为止',
|
|
59
|
+
'review.save': '保存', 'review.back': '返回', 'review.quit': '退出且不保存',
|
|
60
|
+
'review.where': '仅保存在这台电脑上的 {file} 中',
|
|
61
|
+
keyNames: { space: '空格', enter: 'Enter', esc: 'Esc', tab: 'Tab', backspace: '退格' },
|
|
62
|
+
keys: { move: '移动', select: '选择', toggle: '勾选', back: '返回', quit: '退出', open: '展开', close: '收起', openClose: '展开/收起', up: '上一级', hidden: '隐藏项', done: '完成', tab: '补全', add: '添加' },
|
|
63
|
+
'done.saved': '已保存在这台电脑上。',
|
|
64
|
+
'done.unchanged': '没有更改。',
|
|
65
|
+
'done.web': '当 tensor.chat 上的聊天共享其中某个文件夹时,这台电脑会自动确认。',
|
|
66
|
+
'done.run': '使用以下命令启动 Companion:',
|
|
67
|
+
'done.noTty': '设置需要交互式终端。没有终端时,请使用 approve-folder 和 approve-shell。',
|
|
68
|
+
};
|
package/src/setup/layout.js
CHANGED
|
@@ -2,18 +2,100 @@
|
|
|
2
2
|
// How the setup's lines are laid out: wrapped text, columns, the key hints and framed
|
|
3
3
|
// blocks. Widths count what the terminal shows, not the escape codes.
|
|
4
4
|
|
|
5
|
-
const { width, pad } = require('./term');
|
|
5
|
+
const { width, pad, fit } = require('./term');
|
|
6
6
|
|
|
7
|
+
const ESC_SPLIT = /(\x1b\[[0-9;?]*[A-Za-z])/;
|
|
8
|
+
// Chinese and Japanese break between any two characters, but a line never starts
|
|
9
|
+
// with closing punctuation or ends with opening punctuation
|
|
10
|
+
const NO_START = new Set([...'、。,.:;?!)」』】〕》〉…・ー〜%’”),.:;?!%]}']);
|
|
11
|
+
const NO_END = new Set([...'(「『【〔《〈‘“([{']);
|
|
12
|
+
|
|
13
|
+
// The text's runs that stay together on a line, each knowing whether a space came
|
|
14
|
+
// before it. Only a real space separates words: French keeps its no-break space.
|
|
15
|
+
function runs(text) {
|
|
16
|
+
const out = [];
|
|
17
|
+
let run = null;
|
|
18
|
+
let spaced = false;
|
|
19
|
+
let pending = '';
|
|
20
|
+
let prev = '';
|
|
21
|
+
for (const part of String(text).split(ESC_SPLIT)) {
|
|
22
|
+
if (part.startsWith('\x1b[')) { pending += part; continue; }
|
|
23
|
+
for (const ch of part) {
|
|
24
|
+
// codes right before a space end the word before it
|
|
25
|
+
if (/[ \t\r\n]/.test(ch)) { if (run) { run.s += pending; pending = ''; out.push(run); } run = null; spaced = out.length > 0; prev = ''; continue; }
|
|
26
|
+
const w = width(ch);
|
|
27
|
+
if (run && (w === 2 || width(prev) === 2) && !NO_START.has(ch) && !NO_END.has(prev)) { out.push(run); run = null; }
|
|
28
|
+
if (!run) { run = { s: '', w: 0, spaced }; spaced = false; }
|
|
29
|
+
run.s += pending + ch;
|
|
30
|
+
run.w += w;
|
|
31
|
+
pending = '';
|
|
32
|
+
prev = ch;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (run) out.push(run);
|
|
36
|
+
if (pending && out.length) out[out.length - 1].s += pending;
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// The colours and weights still switched on after `s`, given those before it
|
|
41
|
+
function sgrAfter(active, s) {
|
|
42
|
+
let on = active;
|
|
43
|
+
for (const [, params] of s.matchAll(/\x1b\[([0-9;]*)m/g)) {
|
|
44
|
+
for (const p of params.split(';')) {
|
|
45
|
+
if (p === '' || p === '0') on = [];
|
|
46
|
+
else if (p === '22') on = on.filter((x) => x !== '1' && x !== '2');
|
|
47
|
+
else if (p === '24' || p === '27') on = on.filter((x) => x !== String(p - 20));
|
|
48
|
+
else if (p === '39') on = on.filter((x) => !/^(3[0-7]|9[0-7])$/.test(x));
|
|
49
|
+
else on = [...on, p];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return on;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Lines of at most `cols` cells. A styled text keeps its style on every line.
|
|
7
56
|
function wrap(text, cols) {
|
|
8
57
|
const lines = [];
|
|
9
58
|
let line = '';
|
|
10
|
-
|
|
11
|
-
|
|
59
|
+
let used = 0;
|
|
60
|
+
let on = [];
|
|
61
|
+
const add = (s, w) => { line += s; used += w; on = sgrAfter(on, s); };
|
|
62
|
+
const next = () => {
|
|
63
|
+
lines.push(line + (on.length ? '\x1b[0m' : ''));
|
|
64
|
+
line = on.map((p) => `\x1b[${p}m`).join('');
|
|
65
|
+
used = 0;
|
|
66
|
+
};
|
|
67
|
+
for (const run of runs(text)) {
|
|
68
|
+
const gap = used && run.spaced ? 1 : 0;
|
|
69
|
+
if (used && used + gap + run.w > cols) next();
|
|
70
|
+
else if (gap) add(' ', 1);
|
|
71
|
+
if (used + run.w <= cols) { add(run.s, run.w); continue; }
|
|
72
|
+
// a run longer than a whole line, like a long path, is cut where the line ends
|
|
73
|
+
for (const unit of run.s.match(/(?:\x1b\[[0-9;?]*[A-Za-z])*(?:[^\x1b]|$)/gu)) {
|
|
74
|
+
const w = width(unit);
|
|
75
|
+
if (used && used + w > cols) next();
|
|
76
|
+
add(unit, w);
|
|
77
|
+
}
|
|
12
78
|
}
|
|
13
|
-
if (
|
|
79
|
+
if (used) lines.push(line + (on.length ? '\x1b[0m' : ''));
|
|
14
80
|
return lines;
|
|
15
81
|
}
|
|
16
82
|
|
|
83
|
+
// A path too long for its place loses its middle: the start says where, the end which
|
|
84
|
+
function shorten(text, cols) {
|
|
85
|
+
if (width(text) <= cols) return text;
|
|
86
|
+
const chars = [...text];
|
|
87
|
+
let head = '';
|
|
88
|
+
while (width(head) < Math.floor((cols - 1) / 3)) head += chars.shift();
|
|
89
|
+
// both cuts fall on a separator where there is one
|
|
90
|
+
const sep = Math.max(head.lastIndexOf('/'), head.lastIndexOf('\\'));
|
|
91
|
+
if (sep > 0) { chars.unshift(...head.slice(sep + 1)); head = head.slice(0, sep + 1); }
|
|
92
|
+
let tail = '';
|
|
93
|
+
while (chars.length && width(tail) + width(chars[chars.length - 1]) <= cols - 1 - width(head)) tail = chars.pop() + tail;
|
|
94
|
+
const start = tail.search(/[\\/]/);
|
|
95
|
+
if (start > 0) tail = tail.slice(start);
|
|
96
|
+
return `${head}…${tail}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
17
99
|
// A label in its column, always followed by a gap
|
|
18
100
|
function cell(label, col) {
|
|
19
101
|
return pad(label, col - 2) + ' ';
|
|
@@ -46,4 +128,40 @@ function panel(S, lines, tone, w) {
|
|
|
46
128
|
];
|
|
47
129
|
}
|
|
48
130
|
|
|
49
|
-
|
|
131
|
+
// At most `n` of the lines; a cut note ends in "…"
|
|
132
|
+
function clip(lines, n, cols) {
|
|
133
|
+
if (lines.length <= n) return lines;
|
|
134
|
+
const last = lines[n - 1];
|
|
135
|
+
return [...lines.slice(0, n - 1), width(last) + 2 <= cols ? `${last} …` : fit(last, width(last) - 1)];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// The frame's top and bottom for a screen of `rows` lines. Before the list gets fewer
|
|
139
|
+
// than three lines, these give way in turn: a third line of the note, the blank lines
|
|
140
|
+
// on top, the gap above the keys, the rule, the steps, a second line of the note.
|
|
141
|
+
function squeeze({ rows, width: cols, brand, steps, heading, note, rule, keys }) {
|
|
142
|
+
const at = (l) => ' ' + l;
|
|
143
|
+
let noteMax = 3;
|
|
144
|
+
let blanks = true;
|
|
145
|
+
let gap = 1;
|
|
146
|
+
let stepped = true;
|
|
147
|
+
let ruled = true;
|
|
148
|
+
const build = () => {
|
|
149
|
+
const blank = blanks ? [''] : [];
|
|
150
|
+
const said = clip(note, noteMax, cols).map(at);
|
|
151
|
+
return {
|
|
152
|
+
top: [...blank, ...brand.map(at), ...(stepped ? [at(steps)] : []), ...blank, ...heading.map(at), ...blank],
|
|
153
|
+
bottom: [...(said.length ? said : ['']), ...(ruled ? [at(rule)] : []), ...keys],
|
|
154
|
+
gap,
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
const giveWay = [() => { noteMax = 2; }, () => { blanks = false; }, () => { gap = 0; }, () => { ruled = false; }, () => { stepped = false; }, () => { noteMax = 1; }];
|
|
158
|
+
let parts = build();
|
|
159
|
+
for (const next of giveWay) {
|
|
160
|
+
if (parts.top.length + parts.bottom.length + parts.gap + 3 <= rows) break;
|
|
161
|
+
next();
|
|
162
|
+
parts = build();
|
|
163
|
+
}
|
|
164
|
+
return parts;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = { wrap, shorten, cell, spread, keyLines, panel, squeeze };
|
package/src/setup/term.js
CHANGED
|
@@ -17,7 +17,11 @@ function styles(color) {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// Marks that sit on the letter before them, like most Devanagari vowel signs
|
|
21
|
+
const ZERO_WIDTH = /^[\p{Mn}\p{Me}\p{Cf}]$/u;
|
|
22
|
+
|
|
20
23
|
function charWidth(cp) {
|
|
24
|
+
if (ZERO_WIDTH.test(String.fromCodePoint(cp))) return 0;
|
|
21
25
|
// East Asian wide and fullwidth forms take two cells
|
|
22
26
|
if ((cp >= 0x1100 && cp <= 0x115f) || (cp >= 0x2e80 && cp <= 0xa4cf) || (cp >= 0xac00 && cp <= 0xd7a3)
|
|
23
27
|
|| (cp >= 0xf900 && cp <= 0xfaff) || (cp >= 0xfe30 && cp <= 0xfe4f) || (cp >= 0xff00 && cp <= 0xff60)
|