@nac3/forge-cli 1.23.3 → 1.23.4

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.
@@ -24,6 +24,32 @@
24
24
  * refresco a mano -- cae sobre un latido reciente y reconstruye que esta
25
25
  * corriendo, incluso si el telefono no estaba conectado cuando arranco.
26
26
  *
27
+ * POR QUE ES UN REGISTRO Y NO UNA VARIABLE (PND-237, 30-oct-2026).
28
+ *
29
+ * Esto era UN solo slot (`let current`), y un panel corre turnos SOLAPADOS: el
30
+ * dueno manda un pedido mientras el anterior sigue, o entra uno por el puente.
31
+ * Con un solo slot el segundo pisaba al primero, y `clearInFlightTurn(id)` --
32
+ * que NO limpia si el id no coincide, y eso esta bien, porque un cierre tardio
33
+ * no tiene que borrar el anuncio del que recien arranco -- dejaba al pisado
34
+ * SIN NINGUN camino de salida. Peor: `touchInFlightTurn()` refrescaba lo que
35
+ * hubiera en el slot sin mirar el id, asi que el latido de cualquier turno
36
+ * POSTERIOR mantenia vivo al huerfano y el tope de silencio no vencia nunca.
37
+ *
38
+ * Medido en produccion (D1, 30-oct-2026 13:25 UTC): el panel de adip-kikan
39
+ * venia anunciando `dt-mvuyvonj` -- arrancado 12:47:13 -- durante 152 latidos
40
+ * y 38 minutos, mientras el MISMO panel ya habia publicado los remote_turn
41
+ * completos de dos turnos que arrancaron a su lado (12:46:39 y 12:47:40: los
42
+ * tres se solapan en 61 segundos). El telefono mostraba la burbuja "trabajando"
43
+ * con el texto viejo, y como su guarda es por id, el turno REAL no recibia
44
+ * burbuja: el dueno veia una sesion trabajando sin su pedido a la vista.
45
+ *
46
+ * Las dos reglas que lo cierran, y que son el contrato de este modulo:
47
+ * - un turno es una ENTRADA propia, identificada por su id. Nadie pisa a
48
+ * nadie, y cada uno sale por su propio cierre o por su propio silencio.
49
+ * - la SENAL DE VIDA esta ligada al id que la produce (touchInFlightTurn(id)).
50
+ * Un latido solo puede hablar por SU turno. Eso es lo que le devuelve el
51
+ * poder al tope de silencio: un huerfano deja de ser tocado y vence.
52
+ *
27
53
  * ASCII puro.
28
54
  */
29
55
  export interface InFlightTurn {
@@ -60,30 +86,45 @@ export declare const PULSO_DE_PROGRESO_MS: number;
60
86
  *
61
87
  * Diez pulsos de margen: tolera que se pierdan nueve latidos seguidos antes de
62
88
  * dar por muerto un turno que esta vivo.
89
+ *
90
+ * PND-237: esta garantia solo existe de verdad desde que la senal de vida va
91
+ * por id. Mientras `touch()` refrescaba el slot entero, el latido de un turno
92
+ * nuevo reseteaba el reloj del huerfano y este tope era letra muerta.
63
93
  */
64
94
  export declare const SILENCIO_MAX_MS: number;
65
95
  export declare function setInFlightTurn(t: InFlightTurn, now?: number): void;
66
96
  /**
67
- * Senal de vida del turno en curso. La llama el latido de actividad del
68
- * servidor, que corre solo mientras hay turno.
97
+ * Senal de vida de UN turno. La llama el latido de actividad de ese turno,
98
+ * que corre solo mientras el turno vive.
69
99
  *
70
- * No crea nada: si no hay turno registrado, no hace nada. Asi un latido
71
- * rezagado no puede resucitar un turno ya cerrado.
100
+ * No crea nada: si ese id no esta registrado, no hace nada. Asi un latido
101
+ * rezagado no puede resucitar un turno ya cerrado -- y, desde PND-237, no
102
+ * puede tampoco mantener vivo el anuncio de OTRO turno, que era el agujero
103
+ * por el que un huerfano se anunciaba para siempre.
72
104
  */
73
- export declare function touchInFlightTurn(now?: number): void;
105
+ export declare function touchInFlightTurn(id: string, now?: number): void;
74
106
  /**
75
- * Limpia el turno en vuelo. Con `id`, SOLO si coincide: dos turnos encadenados
76
- * pueden cerrar fuera de orden, y un cierre tardio del viejo no tiene que
77
- * borrar el anuncio del que recien arranco.
107
+ * Limpia el turno en vuelo. Con `id`, SOLO ese: dos turnos encadenados pueden
108
+ * cerrar fuera de orden, y un cierre tardio del viejo no tiene que borrar el
109
+ * anuncio del que recien arranco. Sin `id`, TODOS (apagado del servidor).
78
110
  */
79
111
  export declare function clearInFlightTurn(id?: string): void;
80
112
  /**
81
- * El turno en vuelo, o null si no hay o si se quedo CALLADO.
113
+ * El turno que se ANUNCIA, o null si no hay ninguno vivo.
114
+ *
115
+ * El latido de presencia lleva UN turno (contrato del relay), asi que entre
116
+ * varios solapados hay que elegir, y se elige el que arranco ULTIMO: es el
117
+ * pedido que el dueno acaba de hacer y que esta esperando ver. Era justo el
118
+ * que se perdia -- con el slot unico, el que quedaba anunciado podia ser
119
+ * cualquiera, y si era un huerfano la guarda por id del telefono dejaba al
120
+ * pedido nuevo sin burbuja (PND-237, sintoma 6).
82
121
  *
83
- * La edad del turno no entra en la decision: un turno de tres horas que sigue
84
- * latiendo es un turno vivo y se anuncia. Lo que lo saca es el silencio.
122
+ * La edad del turno no entra en la decision de VIDA: un turno de tres horas
123
+ * que sigue latiendo es un turno vivo. Lo que lo saca es el silencio.
85
124
  */
86
125
  export declare function inFlightTurn(now?: number): InFlightTurn | null;
126
+ /** Cuantos turnos vivos declara este panel (diagnostico / tests). */
127
+ export declare function inFlightCount(now?: number): number;
87
128
  /** Solo para tests. */
88
129
  export declare function _resetInFlightTurn(): void;
89
130
  //# sourceMappingURL=in_flight_turn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"in_flight_turn.d.ts","sourceRoot":"","sources":["../../src/relay/in_flight_turn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,QAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe,QAA4B,CAAC;AASzD,wBAAgB,eAAe,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI,CAQ/E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAmB,GAAG,IAAI,CAGhE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAInD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAmB,GAAG,YAAY,GAAG,IAAI,CAU1E;AAED,uBAAuB;AACvB,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
1
+ {"version":3,"file":"in_flight_turn.d.ts","sourceRoot":"","sources":["../../src/relay/in_flight_turn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,QAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,eAAe,QAA4B,CAAC;AAUzD,wBAAgB,eAAe,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI,CAS/E;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI,CAK5E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGnD;AAWD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAmB,GAAG,YAAY,GAAG,IAAI,CAiB1E;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAmB,GAAG,MAAM,CAG9D;AAED,uBAAuB;AACvB,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -24,6 +24,32 @@
24
24
  * refresco a mano -- cae sobre un latido reciente y reconstruye que esta
25
25
  * corriendo, incluso si el telefono no estaba conectado cuando arranco.
26
26
  *
27
+ * POR QUE ES UN REGISTRO Y NO UNA VARIABLE (PND-237, 30-oct-2026).
28
+ *
29
+ * Esto era UN solo slot (`let current`), y un panel corre turnos SOLAPADOS: el
30
+ * dueno manda un pedido mientras el anterior sigue, o entra uno por el puente.
31
+ * Con un solo slot el segundo pisaba al primero, y `clearInFlightTurn(id)` --
32
+ * que NO limpia si el id no coincide, y eso esta bien, porque un cierre tardio
33
+ * no tiene que borrar el anuncio del que recien arranco -- dejaba al pisado
34
+ * SIN NINGUN camino de salida. Peor: `touchInFlightTurn()` refrescaba lo que
35
+ * hubiera en el slot sin mirar el id, asi que el latido de cualquier turno
36
+ * POSTERIOR mantenia vivo al huerfano y el tope de silencio no vencia nunca.
37
+ *
38
+ * Medido en produccion (D1, 30-oct-2026 13:25 UTC): el panel de adip-kikan
39
+ * venia anunciando `dt-mvuyvonj` -- arrancado 12:47:13 -- durante 152 latidos
40
+ * y 38 minutos, mientras el MISMO panel ya habia publicado los remote_turn
41
+ * completos de dos turnos que arrancaron a su lado (12:46:39 y 12:47:40: los
42
+ * tres se solapan en 61 segundos). El telefono mostraba la burbuja "trabajando"
43
+ * con el texto viejo, y como su guarda es por id, el turno REAL no recibia
44
+ * burbuja: el dueno veia una sesion trabajando sin su pedido a la vista.
45
+ *
46
+ * Las dos reglas que lo cierran, y que son el contrato de este modulo:
47
+ * - un turno es una ENTRADA propia, identificada por su id. Nadie pisa a
48
+ * nadie, y cada uno sale por su propio cierre o por su propio silencio.
49
+ * - la SENAL DE VIDA esta ligada al id que la produce (touchInFlightTurn(id)).
50
+ * Un latido solo puede hablar por SU turno. Eso es lo que le devuelve el
51
+ * poder al tope de silencio: un huerfano deja de ser tocado y vence.
52
+ *
27
53
  * ASCII puro.
28
54
  */
29
55
  /**
@@ -54,63 +80,105 @@ export const PULSO_DE_PROGRESO_MS = 60 * 1000;
54
80
  *
55
81
  * Diez pulsos de margen: tolera que se pierdan nueve latidos seguidos antes de
56
82
  * dar por muerto un turno que esta vivo.
83
+ *
84
+ * PND-237: esta garantia solo existe de verdad desde que la senal de vida va
85
+ * por id. Mientras `touch()` refrescaba el slot entero, el latido de un turno
86
+ * nuevo reseteaba el reloj del huerfano y este tope era letra muerta.
57
87
  */
58
88
  export const SILENCIO_MAX_MS = PULSO_DE_PROGRESO_MS * 10;
59
- let current = null;
89
+ /** Los turnos vivos de ESTE panel, por id. Varios a la vez es lo normal. */
90
+ const vivos = new Map();
60
91
  export function setInFlightTurn(t, now = Date.now()) {
61
92
  if (!t || !t.id)
62
93
  return;
63
- current = {
64
- id: String(t.id),
94
+ const id = String(t.id);
95
+ vivos.set(id, {
96
+ id,
65
97
  user: String(t.user || ''),
66
98
  started_at: String(t.started_at || new Date(now).toISOString()),
67
99
  visto: now,
68
- };
100
+ });
69
101
  }
70
102
  /**
71
- * Senal de vida del turno en curso. La llama el latido de actividad del
72
- * servidor, que corre solo mientras hay turno.
103
+ * Senal de vida de UN turno. La llama el latido de actividad de ese turno,
104
+ * que corre solo mientras el turno vive.
73
105
  *
74
- * No crea nada: si no hay turno registrado, no hace nada. Asi un latido
75
- * rezagado no puede resucitar un turno ya cerrado.
106
+ * No crea nada: si ese id no esta registrado, no hace nada. Asi un latido
107
+ * rezagado no puede resucitar un turno ya cerrado -- y, desde PND-237, no
108
+ * puede tampoco mantener vivo el anuncio de OTRO turno, que era el agujero
109
+ * por el que un huerfano se anunciaba para siempre.
76
110
  */
77
- export function touchInFlightTurn(now = Date.now()) {
78
- if (!current)
111
+ export function touchInFlightTurn(id, now = Date.now()) {
112
+ if (!id)
113
+ return;
114
+ const r = vivos.get(String(id));
115
+ if (!r)
79
116
  return;
80
- current.visto = now;
117
+ r.visto = now;
81
118
  }
82
119
  /**
83
- * Limpia el turno en vuelo. Con `id`, SOLO si coincide: dos turnos encadenados
84
- * pueden cerrar fuera de orden, y un cierre tardio del viejo no tiene que
85
- * borrar el anuncio del que recien arranco.
120
+ * Limpia el turno en vuelo. Con `id`, SOLO ese: dos turnos encadenados pueden
121
+ * cerrar fuera de orden, y un cierre tardio del viejo no tiene que borrar el
122
+ * anuncio del que recien arranco. Sin `id`, TODOS (apagado del servidor).
86
123
  */
87
124
  export function clearInFlightTurn(id) {
88
- if (!current)
125
+ if (!id) {
126
+ vivos.clear();
89
127
  return;
90
- if (id && current.id !== id)
91
- return;
92
- current = null;
128
+ }
129
+ vivos.delete(String(id));
130
+ }
131
+ /** Poda las entradas que se quedaron CALLADAS. */
132
+ function podar(now) {
133
+ for (const [id, r] of vivos) {
134
+ /* Un reloj que salta hacia atras da silencio negativo: no vence. Preferible
135
+ * anunciar un turno de mas que descartar uno vivo por un salto de NTP. */
136
+ if (now - r.visto > SILENCIO_MAX_MS)
137
+ vivos.delete(id);
138
+ }
93
139
  }
94
140
  /**
95
- * El turno en vuelo, o null si no hay o si se quedo CALLADO.
141
+ * El turno que se ANUNCIA, o null si no hay ninguno vivo.
96
142
  *
97
- * La edad del turno no entra en la decision: un turno de tres horas que sigue
98
- * latiendo es un turno vivo y se anuncia. Lo que lo saca es el silencio.
143
+ * El latido de presencia lleva UN turno (contrato del relay), asi que entre
144
+ * varios solapados hay que elegir, y se elige el que arranco ULTIMO: es el
145
+ * pedido que el dueno acaba de hacer y que esta esperando ver. Era justo el
146
+ * que se perdia -- con el slot unico, el que quedaba anunciado podia ser
147
+ * cualquiera, y si era un huerfano la guarda por id del telefono dejaba al
148
+ * pedido nuevo sin burbuja (PND-237, sintoma 6).
149
+ *
150
+ * La edad del turno no entra en la decision de VIDA: un turno de tres horas
151
+ * que sigue latiendo es un turno vivo. Lo que lo saca es el silencio.
99
152
  */
100
153
  export function inFlightTurn(now = Date.now()) {
101
- if (!current)
102
- return null;
103
- /* Un reloj que salta hacia atras da silencio negativo: no vence. Preferible
104
- * anunciar un turno de mas que descartar uno vivo por un salto de NTP. */
105
- if (now - current.visto > SILENCIO_MAX_MS) {
106
- current = null;
107
- return null;
154
+ podar(now);
155
+ let elegido = null;
156
+ let mejor = -Infinity;
157
+ for (const r of vivos.values()) {
158
+ /* Un started_at ilegible NO puede ganar la eleccion. Date.parse devuelve
159
+ * NaN y toda comparacion con NaN es false, asi que comparando en crudo el
160
+ * primero de la iteracion se quedaba con el anuncio para siempre -- el
161
+ * mismo sintoma que estamos arreglando, por otra puerta. Vale -Infinity:
162
+ * se anuncia solo si es el unico que hay. */
163
+ const t = Date.parse(r.started_at);
164
+ const orden = Number.isFinite(t) ? t : -Infinity;
165
+ if (!elegido || orden > mejor) {
166
+ elegido = r;
167
+ mejor = orden;
168
+ }
108
169
  }
109
- const { id, user, started_at } = current;
170
+ if (!elegido)
171
+ return null;
172
+ const { id, user, started_at } = elegido;
110
173
  return { id, user, started_at };
111
174
  }
175
+ /** Cuantos turnos vivos declara este panel (diagnostico / tests). */
176
+ export function inFlightCount(now = Date.now()) {
177
+ podar(now);
178
+ return vivos.size;
179
+ }
112
180
  /** Solo para tests. */
113
181
  export function _resetInFlightTurn() {
114
- current = null;
182
+ vivos.clear();
115
183
  }
116
184
  //# sourceMappingURL=in_flight_turn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"in_flight_turn.js","sourceRoot":"","sources":["../../src/relay/in_flight_turn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,oBAAoB,GAAG,EAAE,CAAC;AAOzD,IAAI,OAAO,GAAoB,IAAI,CAAC;AAEpC,MAAM,UAAU,eAAe,CAAC,CAAe,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IACvE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;QAAE,OAAO;IACxB,OAAO,GAAG;QACR,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;QAC1B,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC/D,KAAK,EAAE,GAAG;KACX,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;IACxD,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,OAAO,CAAC,KAAK,GAAG,GAAG,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAW;IAC3C,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,IAAI,EAAE,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE;QAAE,OAAO;IACpC,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;IACnD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B;8EAC0E;IAC1E,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,GAAG,eAAe,EAAE,CAAC;QAC1C,OAAO,GAAG,IAAI,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IACzC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAClC,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,kBAAkB;IAChC,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"in_flight_turn.js","sourceRoot":"","sources":["../../src/relay/in_flight_turn.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -2,5 +2,5 @@
2
2
  * Yujin Forge CLI version. Kept in sync with package.json by the npm
3
3
  * "version" lifecycle (scripts/sync_version.mjs) so the two never drift.
4
4
  */
5
- export declare const VERSION = "1.23.3";
5
+ export declare const VERSION = "1.23.4";
6
6
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  * Yujin Forge CLI version. Kept in sync with package.json by the npm
3
3
  * "version" lifecycle (scripts/sync_version.mjs) so the two never drift.
4
4
  */
5
- export const VERSION = '1.23.3';
5
+ export const VERSION = '1.23.4';
6
6
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nac3/forge-cli",
3
- "version": "1.23.3",
3
+ "version": "1.23.4",
4
4
  "description": "Yujin Forge -- voice-first NAC-3 React development framework. CLI + chat panel + spec ingest + 10-format document reader + voice loop.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Pablo Kuschnirof <pablo@rpaforce.com>",
@@ -15,6 +15,23 @@
15
15
  "exports": {
16
16
  ".": "./dist/index.js"
17
17
  },
18
+ "scripts": {
19
+ "version": "node scripts/sync_version.mjs && git add src/version.ts",
20
+ "build": "node scripts/clean_dist.mjs && tsc && node scripts/copy_qa_assets.mjs",
21
+ "dev": "tsx watch src/bin/yf.ts",
22
+ "typecheck": "tsc --noEmit",
23
+ "typecheck:all": "pnpm typecheck && pnpm --filter @yujin/hito4-server typecheck && pnpm --filter @yujin/forge-mobile typecheck",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "lint": "tsc --noEmit",
27
+ "yf": "tsx src/bin/yf.ts",
28
+ "catalog:lint": "node scripts/catalog_lint.mjs",
29
+ "pairing:check": "node scripts/check_pairing_sync.mjs",
30
+ "brain-matrix:gate": "node scripts/brain_matrix_gate.mjs",
31
+ "hito4:lint": "node scripts/hito4_lint_bundle.mjs",
32
+ "publish-alpha-emergency": "node scripts/publish_alpha_emergency.mjs",
33
+ "prepublishOnly": "pnpm run build && pnpm run typecheck && pnpm run catalog:lint && pnpm run test && pnpm run brain-matrix:gate && pnpm run hito4:lint"
34
+ },
18
35
  "dependencies": {
19
36
  "@anthropic-ai/sdk": "^0.32.1",
20
37
  "@aws-sdk/client-ecs": "^3.1053.0",
@@ -51,6 +68,7 @@
51
68
  "node": ">=18.17",
52
69
  "pnpm": ">=9"
53
70
  },
71
+ "packageManager": "pnpm@11.3.0",
54
72
  "files": [
55
73
  "dist",
56
74
  "templates",
@@ -95,21 +113,5 @@
95
113
  "publishConfig": {
96
114
  "access": "public",
97
115
  "tag": "latest"
98
- },
99
- "scripts": {
100
- "version": "node scripts/sync_version.mjs && git add src/version.ts",
101
- "build": "node scripts/clean_dist.mjs && tsc && node scripts/copy_qa_assets.mjs",
102
- "dev": "tsx watch src/bin/yf.ts",
103
- "typecheck": "tsc --noEmit",
104
- "typecheck:all": "pnpm typecheck && pnpm --filter @yujin/hito4-server typecheck && pnpm --filter @yujin/forge-mobile typecheck",
105
- "test": "vitest run",
106
- "test:watch": "vitest",
107
- "lint": "tsc --noEmit",
108
- "yf": "tsx src/bin/yf.ts",
109
- "catalog:lint": "node scripts/catalog_lint.mjs",
110
- "pairing:check": "node scripts/check_pairing_sync.mjs",
111
- "brain-matrix:gate": "node scripts/brain_matrix_gate.mjs",
112
- "hito4:lint": "node scripts/hito4_lint_bundle.mjs",
113
- "publish-alpha-emergency": "node scripts/publish_alpha_emergency.mjs"
114
116
  }
115
- }
117
+ }