@timekast/factory 1.10.0 → 1.11.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/commands/bootstrap-version.js +43 -0
- package/dist/commands/launcher-path.js +31 -0
- package/dist/index.js +12 -0
- package/dist/launcher.applescript +239 -0
- package/dist/lib/preflight.js +2 -1
- package/package.json +2 -2
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `factory bootstrap-version` — report / compare the bootstrap version (BOOT-002,
|
|
3
|
+
* consumed by BOOT-003). The macOS launcher bootstrap is a tiny stable `.app`;
|
|
4
|
+
* when its logic changes we publish a new `launcher-v*` bootstrap. This command
|
|
5
|
+
* lets an installed bootstrap ask the CLI "am I outdated?" without baking the
|
|
6
|
+
* latest version number into the `.app`.
|
|
7
|
+
*
|
|
8
|
+
* Contract BOOT-003 consumes:
|
|
9
|
+
* factory bootstrap-version --have <version> → prints `outdated` (have < expected)
|
|
10
|
+
* or `ok` (have >= expected)
|
|
11
|
+
* factory bootstrap-version → prints EXPECTED_BOOTSTRAP_VERSION
|
|
12
|
+
* (debug aid)
|
|
13
|
+
*
|
|
14
|
+
* The comparison goes through `compare()` from `lib/semver.ts` — the existing
|
|
15
|
+
* total comparator — NOT a naive string compare (so `1.10.0` > `1.9.0`).
|
|
16
|
+
*/
|
|
17
|
+
import { compare } from '../lib/semver.js';
|
|
18
|
+
/**
|
|
19
|
+
* SSOT for the latest published bootstrap version. Bump this when a new
|
|
20
|
+
* `launcher-v*` bootstrap ships so installed launchers learn they are outdated.
|
|
21
|
+
*/
|
|
22
|
+
export const EXPECTED_BOOTSTRAP_VERSION = '1.0.0';
|
|
23
|
+
/** Read the value of a `--have <version>` flag from the arg list, if present. */
|
|
24
|
+
function parseHave(args) {
|
|
25
|
+
const idx = args.indexOf('--have');
|
|
26
|
+
if (idx === -1)
|
|
27
|
+
return undefined;
|
|
28
|
+
return args[idx + 1];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Run `factory bootstrap-version`. With `--have <version>`, compares it against
|
|
32
|
+
* EXPECTED_BOOTSTRAP_VERSION and prints `outdated` / `ok`; without it, prints
|
|
33
|
+
* the expected version.
|
|
34
|
+
*/
|
|
35
|
+
export function runBootstrapVersion(args) {
|
|
36
|
+
const have = parseHave(args);
|
|
37
|
+
if (have === undefined) {
|
|
38
|
+
console.log(EXPECTED_BOOTSTRAP_VERSION);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// have < expected → outdated; equal or newer → ok.
|
|
42
|
+
console.log(compare(have, EXPECTED_BOOTSTRAP_VERSION) < 0 ? 'outdated' : 'ok');
|
|
43
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `factory launcher-path` — print the absolute path to the packaged launcher
|
|
3
|
+
* AppleScript (BOOT-002). This is the CLI side of the launcher auto-update: the
|
|
4
|
+
* macOS bootstrap (BOOT-001) resolves `@timekast/factory@latest`, runs this
|
|
5
|
+
* command to discover where the GUI logic lives inside the installed package,
|
|
6
|
+
* and `run script`s it. So shipping a new `cli-v*` updates the launcher logic
|
|
7
|
+
* without recompiling or redistributing the `.app`.
|
|
8
|
+
*
|
|
9
|
+
* Path resolution is MODULE-RELATIVE, never `process.cwd()`: npx runs from a
|
|
10
|
+
* temporary cache directory, so `cwd` would point at the user's project, not at
|
|
11
|
+
* the installed package. At runtime this module lives at `dist/commands/`, so
|
|
12
|
+
* `..` lands on `dist/`, where the build copy-step places `launcher.applescript`
|
|
13
|
+
* (see the `build` script in `cli/package.json` + `files: ["dist"]`).
|
|
14
|
+
*
|
|
15
|
+
* We ship the SOURCE `.applescript` (not a compiled `.scpt`): it is legible and
|
|
16
|
+
* diffeable in git, and `run script` accepts a source script just fine. The few
|
|
17
|
+
* milliseconds a compiled script would save do not justify shipping an opaque
|
|
18
|
+
* binary blob in the package.
|
|
19
|
+
*/
|
|
20
|
+
import { dirname, join } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
/** Resolve the absolute path to the packaged `launcher.applescript`, relative to this module. */
|
|
23
|
+
export function launcherPath() {
|
|
24
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
// dist/commands/launcher-path.js → dist/launcher.applescript
|
|
26
|
+
return join(here, '..', 'launcher.applescript');
|
|
27
|
+
}
|
|
28
|
+
/** Print the absolute path of the packaged launcher script to stdout. */
|
|
29
|
+
export function runLauncherPath() {
|
|
30
|
+
console.log(launcherPath());
|
|
31
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,9 @@ import { dirname, join } from 'node:path';
|
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { CLIError } from './lib/cli-error.js';
|
|
13
13
|
import { parseAddFlags, runAdd } from './commands/add.js';
|
|
14
|
+
import { runBootstrapVersion } from './commands/bootstrap-version.js';
|
|
14
15
|
import { runDoctor } from './commands/doctor.js';
|
|
16
|
+
import { runLauncherPath } from './commands/launcher-path.js';
|
|
15
17
|
import { parseNewFlags, runNew } from './commands/new.js';
|
|
16
18
|
import { parsePublishArgs, runPublish } from './commands/publish.js';
|
|
17
19
|
import { secretsCommand } from './commands/secrets.js';
|
|
@@ -34,6 +36,8 @@ Comandos:
|
|
|
34
36
|
publish <qué> Publica el entregable (proposal|mockup|both) a proposals.timekast.mx
|
|
35
37
|
unpublish [qué] Baja del hub un entregable publicado (proposal|mockup|both; default both)
|
|
36
38
|
secrets <file> Mergea un archivo KEY=value de secretos de metodología a ~/.claude/.env (chmod 600)
|
|
39
|
+
launcher-path (interno) Imprime el path del launcher.applescript empacado (lo usa el bootstrap)
|
|
40
|
+
bootstrap-version (interno) Imprime/compara la versión del bootstrap (\`--have <ver>\` → ok|outdated)
|
|
37
41
|
|
|
38
42
|
Sobre \`new\`:
|
|
39
43
|
Pregunta el perfil de forma interactiva. Los flags \`--full\` / \`--core\` lo
|
|
@@ -147,6 +151,14 @@ async function main(argv) {
|
|
|
147
151
|
await secretsCommand(file);
|
|
148
152
|
return;
|
|
149
153
|
}
|
|
154
|
+
case 'launcher-path': {
|
|
155
|
+
runLauncherPath();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
case 'bootstrap-version': {
|
|
159
|
+
runBootstrapVersion(rest);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
150
162
|
default:
|
|
151
163
|
throw new CLIError(`Comando desconocido: \`${command}\`.\nEjecuta \`factory --help\` para ver los comandos disponibles.`);
|
|
152
164
|
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
-- TimeKast Factory — launcher (macOS)
|
|
2
|
+
-- Crea proyectos nuevos y actualiza proyectos existentes SIN abrir la terminal.
|
|
3
|
+
-- Envuelve `npx @timekast/factory`. Se ejecuta dentro de un login shell
|
|
4
|
+
-- (`${SHELL:-/bin/zsh} -lc`) para heredar el PATH real del usuario (node / npx /
|
|
5
|
+
-- gh por nvm o homebrew no están en el PATH mínimo de `do shell script`). Usa
|
|
6
|
+
-- `$SHELL` (que `do shell script` sí exporta) con fallback a /bin/zsh; soporte
|
|
7
|
+
-- solo para shells POSIX compatibles con `-lc` (zsh/bash).
|
|
8
|
+
--
|
|
9
|
+
-- Requisitos (una sola vez por máquina): Node 22+, `gh auth login` hecho,
|
|
10
|
+
-- miembro de la org TimeKast en GitHub.
|
|
11
|
+
|
|
12
|
+
property kCLIPkg : "@timekast/factory"
|
|
13
|
+
property kCLI : "@timekast/factory@latest" -- default (fallback); `on run` lo resuelve vía resolvedCLI (pin opcional)
|
|
14
|
+
property kTitle : "TimeKast Factory"
|
|
15
|
+
property kOrg : "TimeKast"
|
|
16
|
+
|
|
17
|
+
on run
|
|
18
|
+
-- Resuelve la versión del CLI a usar (pin opcional sin recompilar; ver `resolvedCLI`).
|
|
19
|
+
-- Se reasigna la property global kCLI para que los 3 call-sites la usen sin cambios.
|
|
20
|
+
set kCLI to resolvedCLI()
|
|
21
|
+
|
|
22
|
+
set homeRoot to (POSIX path of (path to home folder)) & "timekast"
|
|
23
|
+
do shell script "mkdir -p " & quoted form of homeRoot
|
|
24
|
+
|
|
25
|
+
if not checkPrereqs() then return
|
|
26
|
+
|
|
27
|
+
set theAction to choose from list {"Nuevo proyecto", "Actualizar proyecto", "Poner secrets"} with prompt "TimeKast Factory — ¿qué quieres hacer?" default items {"Nuevo proyecto"} OK button name "Continuar" cancel button name "Salir"
|
|
28
|
+
if theAction is false then return
|
|
29
|
+
set theAction to item 1 of theAction
|
|
30
|
+
|
|
31
|
+
if theAction is "Nuevo proyecto" then
|
|
32
|
+
newProject(homeRoot)
|
|
33
|
+
else if theAction is "Actualizar proyecto" then
|
|
34
|
+
updateProject(homeRoot)
|
|
35
|
+
else
|
|
36
|
+
installSecrets()
|
|
37
|
+
end if
|
|
38
|
+
end run
|
|
39
|
+
|
|
40
|
+
-- ───────────────────── Versión del CLI (pin opcional) ─────────────────────
|
|
41
|
+
-- Por defecto usa `@timekast/factory@latest`. Para fijar una versión SIN recompilar
|
|
42
|
+
-- la .app, crea `~/.timekast-launcher.conf` con una línea: CLI_VERSION=@1.9.0
|
|
43
|
+
-- (también acepta `CLI_VERSION=1.9.0`). Útil para validar una versión específica antes
|
|
44
|
+
-- de mover al equipo a @latest. Sin archivo o sin la línea → fallback a kCLI (@latest).
|
|
45
|
+
on resolvedCLI()
|
|
46
|
+
try
|
|
47
|
+
set confPath to (POSIX path of (path to home folder)) & ".timekast-launcher.conf"
|
|
48
|
+
set pinned to do shell script "grep -E '^CLI_VERSION=' " & quoted form of confPath & " 2>/dev/null | tail -1 | cut -d= -f2- | tr -d '[:space:]'"
|
|
49
|
+
if pinned is not "" then
|
|
50
|
+
if pinned starts with "@" then
|
|
51
|
+
return kCLIPkg & pinned
|
|
52
|
+
else
|
|
53
|
+
return kCLIPkg & "@" & pinned
|
|
54
|
+
end if
|
|
55
|
+
end if
|
|
56
|
+
end try
|
|
57
|
+
return kCLI
|
|
58
|
+
end resolvedCLI
|
|
59
|
+
|
|
60
|
+
-- ───────────────────── Preflight de requisitos ─────────────────────
|
|
61
|
+
-- true si node + gh + gh-auth están listos; si no, avisa cómo arreglarlo y
|
|
62
|
+
-- devuelve false. Corre en login shell (`${SHELL:-/bin/zsh} -lc`) para ver el PATH real.
|
|
63
|
+
on checkPrereqs()
|
|
64
|
+
try
|
|
65
|
+
do shell script "${SHELL:-/bin/zsh} -lc 'command -v node'"
|
|
66
|
+
on error
|
|
67
|
+
display dialog "Falta Node.js." & return & return & "Instálalo y vuelve a abrir la app:" & return & " https://nodejs.org (o: brew install node)" with title kTitle buttons {"Entendido"} default button "Entendido" with icon stop
|
|
68
|
+
return false
|
|
69
|
+
end try
|
|
70
|
+
-- Versión de Node: el CLI requiere Node 22+. El `22` va hardcodeado: boundary
|
|
71
|
+
-- AppleScript — no puede importar la constante de `engines.node` del CLI TypeScript.
|
|
72
|
+
-- Si sube el mínimo, actualizar aquí y en cli/launchers/README.md (Requisitos).
|
|
73
|
+
try
|
|
74
|
+
set nodeMajor to (do shell script "${SHELL:-/bin/zsh} -lc 'node -v' | sed 's/^v//' | cut -d. -f1") as integer
|
|
75
|
+
if nodeMajor < 22 then
|
|
76
|
+
display dialog "Necesitas Node 22 o superior." & return & return & "Tienes la versión " & nodeMajor & "." & return & return & "Instala una versión nueva desde:" & return & " https://nodejs.org" with title kTitle buttons {"Entendido"} default button "Entendido" with icon stop
|
|
77
|
+
return false
|
|
78
|
+
end if
|
|
79
|
+
on error
|
|
80
|
+
display dialog "No pude verificar la versión de Node.js." & return & return & "Asegúrate de tener Node 22 o superior:" & return & " https://nodejs.org" with title kTitle buttons {"Entendido"} default button "Entendido" with icon stop
|
|
81
|
+
return false
|
|
82
|
+
end try
|
|
83
|
+
try
|
|
84
|
+
do shell script "${SHELL:-/bin/zsh} -lc 'command -v gh'"
|
|
85
|
+
on error
|
|
86
|
+
display dialog "Falta GitHub CLI (gh)." & return & return & "Instálalo y vuelve a abrir la app:" & return & " brew install gh" with title kTitle buttons {"Entendido"} default button "Entendido" with icon stop
|
|
87
|
+
return false
|
|
88
|
+
end try
|
|
89
|
+
try
|
|
90
|
+
do shell script "${SHELL:-/bin/zsh} -lc 'gh auth status'"
|
|
91
|
+
on error
|
|
92
|
+
display dialog "GitHub CLI no está autenticado." & return & return & "Inicia sesión una sola vez en la terminal:" & return & " gh auth login" with title kTitle buttons {"Entendido"} default button "Entendido" with icon stop
|
|
93
|
+
return false
|
|
94
|
+
end try
|
|
95
|
+
-- Usuario de GitHub: gh api /user --jq .login — necesario para armar el check de membresía que sigue
|
|
96
|
+
try
|
|
97
|
+
set ghUser to do shell script "${SHELL:-/bin/zsh} -lc 'gh api /user --jq .login'"
|
|
98
|
+
on error
|
|
99
|
+
display dialog "No pude leer tu usuario de GitHub." & return & return & "Revisa tu sesión con:" & return & " gh auth status" with title kTitle buttons {"Entendido"} default button "Entendido" with icon stop
|
|
100
|
+
return false
|
|
101
|
+
end try
|
|
102
|
+
-- Membresía de la org: mismo check que el CLI (gh api /orgs/<org>/members/<login> → 204 si es miembro)
|
|
103
|
+
try
|
|
104
|
+
do shell script "${SHELL:-/bin/zsh} -lc " & quoted form of ("gh api /orgs/" & kOrg & "/members/" & ghUser)
|
|
105
|
+
on error
|
|
106
|
+
display dialog "Tu cuenta de GitHub (" & ghUser & ") no aparece como miembro de la org " & kOrg & "." & return & return & "Si YA eres miembro y aun así ves esto, a tu sesión le falta un permiso para ver la membresía. Ejecútalo una vez en la terminal:" & return & " gh auth refresh -s read:org" & return & return & "Si NO eres miembro, pide acceso a la org o entra con la cuenta correcta:" & return & " gh auth login" with title kTitle buttons {"Entendido"} default button "Entendido" with icon stop
|
|
107
|
+
return false
|
|
108
|
+
end try
|
|
109
|
+
return true
|
|
110
|
+
end checkPrereqs
|
|
111
|
+
|
|
112
|
+
-- ───────────────────────── Nuevo proyecto ─────────────────────────
|
|
113
|
+
on newProject(homeRoot)
|
|
114
|
+
-- Nombre
|
|
115
|
+
set nameAns to display dialog "¿Cómo se va a llamar el proyecto?" default answer "" with title kTitle buttons {"Cancelar", "Siguiente"} default button "Siguiente"
|
|
116
|
+
if button returned of nameAns is "Cancelar" then return
|
|
117
|
+
set projName to text returned of nameAns
|
|
118
|
+
if projName is "" then
|
|
119
|
+
display alert "Falta el nombre" message "Vuelve a abrir y escribe un nombre para el proyecto." as warning
|
|
120
|
+
return
|
|
121
|
+
end if
|
|
122
|
+
|
|
123
|
+
-- Perfil
|
|
124
|
+
set profChoice to choose from list {"App completa (starter kit + cerebro)", "Solo el cerebro"} with prompt "¿Qué quieres crear para «" & projName & "»?" default items {"App completa (starter kit + cerebro)"} OK button name "Siguiente" cancel button name "Cancelar"
|
|
125
|
+
if profChoice is false then return
|
|
126
|
+
if (item 1 of profChoice) starts with "App completa" then
|
|
127
|
+
set profileFlag to "--full"
|
|
128
|
+
else
|
|
129
|
+
set profileFlag to "--core"
|
|
130
|
+
end if
|
|
131
|
+
|
|
132
|
+
-- Ubicación (default ~/timekast, con opción de elegir otra)
|
|
133
|
+
set locAns to display dialog "¿Dónde guardar el proyecto?" with title kTitle buttons {"Cancelar", "Elegir otra…", "Usar ~/timekast"} default button "Usar ~/timekast"
|
|
134
|
+
set locBtn to button returned of locAns
|
|
135
|
+
if locBtn is "Cancelar" then return
|
|
136
|
+
if locBtn is "Usar ~/timekast" then
|
|
137
|
+
set destFolder to homeRoot
|
|
138
|
+
else
|
|
139
|
+
set destFolder to POSIX path of (choose folder with prompt "Elige dónde crear el proyecto" default location (POSIX file homeRoot))
|
|
140
|
+
-- choose folder devuelve con "/" final; lo normalizamos sin "/" para el join
|
|
141
|
+
if destFolder ends with "/" then set destFolder to text 1 thru -2 of destFolder
|
|
142
|
+
end if
|
|
143
|
+
|
|
144
|
+
-- Crear (oculto, con aviso no-bloqueante)
|
|
145
|
+
display notification "Creando «" & projName & "»… puede tardar ~30 s." with title kTitle
|
|
146
|
+
set innerCmd to "cd " & quoted form of destFolder & " && npx -y " & kCLI & " new " & quoted form of projName & " " & profileFlag
|
|
147
|
+
try
|
|
148
|
+
do shell script "${SHELL:-/bin/zsh} -lc " & quoted form of innerCmd
|
|
149
|
+
on error errMsg number errNum
|
|
150
|
+
display alert "No se pudo crear el proyecto" message errMsg as critical
|
|
151
|
+
return
|
|
152
|
+
end try
|
|
153
|
+
|
|
154
|
+
set projPath to destFolder & "/" & projName
|
|
155
|
+
set doneAns to display dialog "✔ Listo. Tu proyecto está en:" & return & projPath & return & return & "Ábrelo en Claude Code para empezar a trabajar." with title kTitle buttons {"OK", "Abrir carpeta"} default button "Abrir carpeta"
|
|
156
|
+
if button returned of doneAns is "Abrir carpeta" then
|
|
157
|
+
do shell script "open " & quoted form of projPath
|
|
158
|
+
end if
|
|
159
|
+
end newProject
|
|
160
|
+
|
|
161
|
+
-- ──────────────────────── Actualizar proyecto ────────────────────────
|
|
162
|
+
on updateProject(homeRoot)
|
|
163
|
+
set repoFolder to POSIX path of (choose folder with prompt "Elige la carpeta del proyecto a actualizar" default location (POSIX file homeRoot))
|
|
164
|
+
if not (repoFolder ends with "/") then set repoFolder to repoFolder & "/"
|
|
165
|
+
|
|
166
|
+
-- ¿Ya está registrado con el CLI? (tiene lockfile)
|
|
167
|
+
set lockPath to repoFolder & ".timekast/lockfile.json"
|
|
168
|
+
set hasLock to (do shell script "test -f " & quoted form of lockPath & " && echo yes || echo no")
|
|
169
|
+
-- ¿Tiene cerebro instalado a mano? (carpeta .claude sin lockfile = legacy hand-managed).
|
|
170
|
+
-- Solo importa cuando NO hay lockfile. Si no podemos leer el directorio (permisos),
|
|
171
|
+
-- el try cae al caso conservador (hasClaude = "yes" → advertencia + update --yes).
|
|
172
|
+
set hasClaude to "yes"
|
|
173
|
+
try
|
|
174
|
+
set hasClaude to (do shell script "test -d " & quoted form of (repoFolder & ".claude") & " && echo yes || echo no")
|
|
175
|
+
end try
|
|
176
|
+
|
|
177
|
+
-- Tres casos:
|
|
178
|
+
-- · con lockfile → `update` normal (la lógica de 4 cubetas del CLI resuelve conflictos)
|
|
179
|
+
-- · sin lockfile + con .claude → legacy hand-managed: advertencia + `update --yes` (auto-confirma el prompt)
|
|
180
|
+
-- · sin lockfile + sin .claude → repo fresco: `factory add` limpio, sin advertencia
|
|
181
|
+
set cliCmd to "update"
|
|
182
|
+
set updateFlag to ""
|
|
183
|
+
if hasLock is "no" then
|
|
184
|
+
if hasClaude is "yes" then
|
|
185
|
+
set warnAns to display dialog "Este proyecto tiene un cerebro (.claude) instalado a mano y todavía no está registrado con el CLI." & return & return & "Voy a ponerlo al día. Eso SOBRESCRIBE los archivos del cerebro que coincidan con la última versión; tu CLAUDE.md se conserva. Si editaste archivos del cerebro a mano, se reemplazarían." & return & return & "¿Continúo con el registro?" with title kTitle buttons {"Cancelar", "Registrar y actualizar"} default button "Registrar y actualizar" with icon caution
|
|
186
|
+
if button returned of warnAns is "Cancelar" then return
|
|
187
|
+
set updateFlag to " --yes"
|
|
188
|
+
else
|
|
189
|
+
-- Repo fresco: instalación limpia con `add`, sin advertencia de sobrescritura.
|
|
190
|
+
set cliCmd to "add"
|
|
191
|
+
end if
|
|
192
|
+
end if
|
|
193
|
+
|
|
194
|
+
-- Actualizar (oculto)
|
|
195
|
+
display notification "Actualizando el proyecto…" with title kTitle
|
|
196
|
+
set innerCmd to "cd " & quoted form of repoFolder & " && npx -y " & kCLI & " " & cliCmd & updateFlag
|
|
197
|
+
try
|
|
198
|
+
do shell script "${SHELL:-/bin/zsh} -lc " & quoted form of innerCmd
|
|
199
|
+
on error errMsg number errNum
|
|
200
|
+
if errNum is 130 then
|
|
201
|
+
display dialog "Este update tiene cambios que chocan con ediciones tuyas en el cerebro." & return & return & "No se modificó nada. Resuélvelo viendo los diffs en Claude Code, o en terminal con:" & return & " pnpm factory:update" with title kTitle buttons {"Entendido"} default button "Entendido" with icon caution
|
|
202
|
+
else
|
|
203
|
+
display alert "No se pudo actualizar" message errMsg as critical
|
|
204
|
+
end if
|
|
205
|
+
return
|
|
206
|
+
end try
|
|
207
|
+
|
|
208
|
+
display dialog "✔ Proyecto actualizado al día." with title kTitle buttons {"OK"} default button "OK"
|
|
209
|
+
end updateProject
|
|
210
|
+
|
|
211
|
+
-- ──────────────────────────── Poner secrets ────────────────────────────
|
|
212
|
+
-- Instala los secretos de metodología del equipo en ~/.claude/.env llamando
|
|
213
|
+
-- a `npx @timekast/factory secrets <archivo>`. El gate de org (checkPrereqs en
|
|
214
|
+
-- `on run`) ES la autorización: sin membresía no se llega aquí. El comando solo
|
|
215
|
+
-- imprime NOMBRES de clave (nunca valores) y eso es lo que mostramos tal cual.
|
|
216
|
+
on installSecrets()
|
|
217
|
+
-- Elige el archivo master de secrets. Sin filtro de tipo: los .env suelen no
|
|
218
|
+
-- tener extensión. Si el usuario cancela, volvemos al menú sin error.
|
|
219
|
+
try
|
|
220
|
+
set chosenFile to choose file with prompt "Elige tu archivo master de secrets"
|
|
221
|
+
on error number -128
|
|
222
|
+
-- El usuario presionó Cancelar — no es un fallo, simplemente regresamos.
|
|
223
|
+
return
|
|
224
|
+
end try
|
|
225
|
+
|
|
226
|
+
-- `quoted form of POSIX path` maneja rutas con espacios; obligatorio aquí.
|
|
227
|
+
display notification "Instalando secrets…" with title kTitle
|
|
228
|
+
set innerCmd to "npx -y " & kCLI & " secrets " & quoted form of (POSIX path of chosenFile)
|
|
229
|
+
try
|
|
230
|
+
set secretsResult to do shell script "${SHELL:-/bin/zsh} -lc " & quoted form of innerCmd
|
|
231
|
+
on error errMsg number errNum
|
|
232
|
+
display alert "No se pudieron poner los secrets" message errMsg as critical
|
|
233
|
+
return
|
|
234
|
+
end try
|
|
235
|
+
|
|
236
|
+
-- El stdout del CLI ya lista solo NOMBRES de clave (agregadas / actualizadas /
|
|
237
|
+
-- sin cambios), nunca valores — lo mostramos verbatim.
|
|
238
|
+
display dialog secretsResult with title kTitle buttons {"OK"} default button "OK"
|
|
239
|
+
end installSecrets
|
package/dist/lib/preflight.js
CHANGED
|
@@ -65,7 +65,8 @@ async function assertOrgMembership() {
|
|
|
65
65
|
}
|
|
66
66
|
catch {
|
|
67
67
|
throw new PreflightError(`No se pudo verificar que \`${login}\` sea miembro del org \`${FACTORY_ORG}\`.\n` +
|
|
68
|
-
`
|
|
68
|
+
`Si ya eres miembro, tu token puede carecer del scope \`read:org\` (la membresía privada queda invisible): ejecuta \`gh auth refresh -s read:org\` y vuelve a intentar.\n` +
|
|
69
|
+
`Si no eres miembro, solicita acceso al org \`${FACTORY_ORG}\` o verifica tu sesión con \`gh auth status\`.`);
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timekast/factory",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Public, thin CLI to bootstrap and maintain TimeKast Factory derived projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"node": ">=22"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"build": "tsc -p tsconfig.json",
|
|
33
|
+
"build": "tsc -p tsconfig.json && cp launchers/src/launcher.applescript dist/",
|
|
34
34
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
35
|
"prepublishOnly": "pnpm build",
|
|
36
36
|
"test": "vitest run",
|