@nac3/forge-cli 0.2.1-alpha.60 → 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/README.md +5 -1
- package/dist/agents/bridge.d.ts.map +1 -1
- package/dist/agents/bridge.js +27 -0
- package/dist/agents/bridge.js.map +1 -1
- package/dist/agents/config_resolver.d.ts.map +1 -1
- package/dist/agents/config_resolver.js +15 -15
- package/dist/agents/config_resolver.js.map +1 -1
- package/dist/agents/runner.d.ts +8 -0
- package/dist/agents/runner.d.ts.map +1 -1
- package/dist/agents/runner.js +58 -11
- package/dist/agents/runner.js.map +1 -1
- package/dist/agents/system_prompt.d.ts.map +1 -1
- package/dist/agents/system_prompt.js +2 -1
- package/dist/agents/system_prompt.js.map +1 -1
- package/dist/bin/yf.d.ts.map +1 -1
- package/dist/bin/yf.js +18 -0
- package/dist/bin/yf.js.map +1 -1
- package/dist/chat/claude.d.ts +13 -0
- package/dist/chat/claude.d.ts.map +1 -1
- package/dist/chat/claude.js +45 -10
- package/dist/chat/claude.js.map +1 -1
- package/dist/chat/claude_cli.d.ts +10 -0
- package/dist/chat/claude_cli.d.ts.map +1 -1
- package/dist/chat/claude_cli.js +97 -16
- package/dist/chat/claude_cli.js.map +1 -1
- package/dist/chat/completion_hallucination_detector.d.ts +11 -0
- package/dist/chat/completion_hallucination_detector.d.ts.map +1 -1
- package/dist/chat/completion_hallucination_detector.js +37 -2
- package/dist/chat/completion_hallucination_detector.js.map +1 -1
- package/dist/chat/doctrine_digest.d.ts.map +1 -1
- package/dist/chat/doctrine_digest.js +10 -6
- package/dist/chat/doctrine_digest.js.map +1 -1
- package/dist/chat/panel.d.ts.map +1 -1
- package/dist/chat/panel.js +885 -42
- package/dist/chat/panel.js.map +1 -1
- package/dist/chat/refusal_detector.d.ts.map +1 -1
- package/dist/chat/refusal_detector.js +2 -1
- package/dist/chat/refusal_detector.js.map +1 -1
- package/dist/chat/server.d.ts +1 -0
- package/dist/chat/server.d.ts.map +1 -1
- package/dist/chat/server.js +890 -62
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/session_projects.d.ts +31 -0
- package/dist/chat/session_projects.d.ts.map +1 -0
- package/dist/chat/session_projects.js +36 -0
- package/dist/chat/session_projects.js.map +1 -0
- package/dist/chat/session_store.d.ts +32 -0
- package/dist/chat/session_store.d.ts.map +1 -0
- package/dist/chat/session_store.js +58 -0
- package/dist/chat/session_store.js.map +1 -0
- package/dist/chat/tooling_section.d.ts +11 -0
- package/dist/chat/tooling_section.d.ts.map +1 -0
- package/dist/chat/tooling_section.js +70 -0
- package/dist/chat/tooling_section.js.map +1 -0
- package/dist/chat/tools/app_backend_dispatch.d.ts.map +1 -1
- package/dist/chat/tools/app_backend_dispatch.js +44 -1
- package/dist/chat/tools/app_backend_dispatch.js.map +1 -1
- package/dist/chat/tools/fs_editor.d.ts.map +1 -1
- package/dist/chat/tools/fs_editor.js +53 -3
- package/dist/chat/tools/fs_editor.js.map +1 -1
- package/dist/chat/tools/graph.js +2 -2
- package/dist/chat/tools/graph.js.map +1 -1
- package/dist/chat/tools/keys.d.ts.map +1 -1
- package/dist/chat/tools/keys.js +5 -18
- package/dist/chat/tools/keys.js.map +1 -1
- package/dist/chat/tools/nac3_invoke.d.ts.map +1 -1
- package/dist/chat/tools/nac3_invoke.js +16 -7
- package/dist/chat/tools/nac3_invoke.js.map +1 -1
- package/dist/chat/tools/shell.d.ts +6 -0
- package/dist/chat/tools/shell.d.ts.map +1 -1
- package/dist/chat/tools/shell.js +39 -0
- package/dist/chat/tools/shell.js.map +1 -1
- package/dist/chat/tools/workflow.d.ts +20 -0
- package/dist/chat/tools/workflow.d.ts.map +1 -1
- package/dist/chat/tools/workflow.js +98 -0
- package/dist/chat/tools/workflow.js.map +1 -1
- package/dist/chat/tools.d.ts.map +1 -1
- package/dist/chat/tools.js +79 -1
- package/dist/chat/tools.js.map +1 -1
- package/dist/chat/turn_control.d.ts +42 -0
- package/dist/chat/turn_control.d.ts.map +1 -0
- package/dist/chat/turn_control.js +53 -0
- package/dist/chat/turn_control.js.map +1 -0
- package/dist/commands/brain.js +1 -1
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +61 -10
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/graph.js +9 -9
- package/dist/commands/graph.js.map +1 -1
- package/dist/commands/keys_setup.d.ts +10 -28
- package/dist/commands/keys_setup.d.ts.map +1 -1
- package/dist/commands/keys_setup.js +54 -228
- package/dist/commands/keys_setup.js.map +1 -1
- package/dist/commands/license.d.ts.map +1 -1
- package/dist/commands/license.js +52 -28
- package/dist/commands/license.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +62 -6
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/mobile.d.ts +1 -1
- package/dist/commands/mobile.d.ts.map +1 -1
- package/dist/commands/mobile.js +32 -28
- package/dist/commands/mobile.js.map +1 -1
- package/dist/commands/onboard.d.ts +19 -0
- package/dist/commands/onboard.d.ts.map +1 -0
- package/dist/commands/onboard.js +126 -0
- package/dist/commands/onboard.js.map +1 -0
- package/dist/commands/pending.d.ts.map +1 -1
- package/dist/commands/pending.js +25 -1
- package/dist/commands/pending.js.map +1 -1
- package/dist/commands/projects.js +25 -1
- package/dist/commands/projects.js.map +1 -1
- package/dist/commands/review-screens.js +28 -1
- package/dist/commands/review-screens.js.map +1 -1
- package/dist/commands/workflow.d.ts +18 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +253 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/core/brain_config.d.ts +2 -0
- package/dist/core/brain_config.d.ts.map +1 -1
- package/dist/core/brain_config.js +18 -3
- package/dist/core/brain_config.js.map +1 -1
- package/dist/core/cf_tunnel.d.ts.map +1 -1
- package/dist/core/cf_tunnel.js +8 -2
- package/dist/core/cf_tunnel.js.map +1 -1
- package/dist/core/cost_router.d.ts.map +1 -1
- package/dist/core/cost_router.js +11 -2
- package/dist/core/cost_router.js.map +1 -1
- package/dist/core/desktop_open.d.ts +16 -0
- package/dist/core/desktop_open.d.ts.map +1 -0
- package/dist/core/desktop_open.js +114 -0
- package/dist/core/desktop_open.js.map +1 -0
- package/dist/core/keys_envelope.d.ts.map +1 -1
- package/dist/core/keys_envelope.js +5 -1
- package/dist/core/keys_envelope.js.map +1 -1
- package/dist/core/limits_config.d.ts +5 -0
- package/dist/core/limits_config.d.ts.map +1 -1
- package/dist/core/limits_config.js +5 -0
- package/dist/core/limits_config.js.map +1 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +3 -0
- package/dist/core/logger.js.map +1 -1
- package/dist/core/pairing.d.ts +6 -0
- package/dist/core/pairing.d.ts.map +1 -1
- package/dist/core/pairing.js +41 -2
- package/dist/core/pairing.js.map +1 -1
- package/dist/core/provider_keys.d.ts.map +1 -1
- package/dist/core/provider_keys.js +25 -12
- package/dist/core/provider_keys.js.map +1 -1
- package/dist/core/provider_models.js +1 -1
- package/dist/deploy/cloudflare.d.ts.map +1 -1
- package/dist/deploy/cloudflare.js +13 -2
- package/dist/deploy/cloudflare.js.map +1 -1
- package/dist/design/yujin_design_resolver.d.ts +16 -0
- package/dist/design/yujin_design_resolver.d.ts.map +1 -1
- package/dist/design/yujin_design_resolver.js +103 -20
- package/dist/design/yujin_design_resolver.js.map +1 -1
- package/dist/docs/doctrine/default-design-system.md +32 -0
- package/dist/docs/doctrine/office-tools.md +20 -0
- package/dist/docs/doctrine/verb-composition.md +16 -0
- package/dist/docs/doctrine/workflow-kinds.md +39 -22
- package/dist/docs/doctrine/workflow.md +12 -4
- package/dist/license/auto_sync.d.ts +22 -0
- package/dist/license/auto_sync.d.ts.map +1 -0
- package/dist/license/auto_sync.js +91 -0
- package/dist/license/auto_sync.js.map +1 -0
- package/dist/license/handle_key.d.ts +32 -0
- package/dist/license/handle_key.d.ts.map +1 -0
- package/dist/license/handle_key.js +56 -0
- package/dist/license/handle_key.js.map +1 -0
- package/dist/license/hito4_client.d.ts.map +1 -1
- package/dist/license/hito4_client.js +63 -25
- package/dist/license/hito4_client.js.map +1 -1
- package/dist/llm/gemini_client.d.ts.map +1 -1
- package/dist/llm/gemini_client.js +5 -2
- package/dist/llm/gemini_client.js.map +1 -1
- package/dist/llm/managed_client.d.ts.map +1 -1
- package/dist/llm/managed_client.js +14 -9
- package/dist/llm/managed_client.js.map +1 -1
- package/dist/mcp/plan_bridges.d.ts +17 -0
- package/dist/mcp/plan_bridges.d.ts.map +1 -0
- package/dist/mcp/plan_bridges.js +119 -0
- package/dist/mcp/plan_bridges.js.map +1 -0
- package/dist/migrate/ai-apply.d.ts.map +1 -1
- package/dist/migrate/ai-apply.js +25 -2
- package/dist/migrate/ai-apply.js.map +1 -1
- package/dist/nac3/internal_manifest.d.ts.map +1 -1
- package/dist/nac3/internal_manifest.js +200 -10
- package/dist/nac3/internal_manifest.js.map +1 -1
- package/dist/nac3/verb_tier_map.d.ts.map +1 -1
- package/dist/nac3/verb_tier_map.js +3 -0
- package/dist/nac3/verb_tier_map.js.map +1 -1
- package/dist/onboarding/concierge_client.d.ts +34 -0
- package/dist/onboarding/concierge_client.d.ts.map +1 -0
- package/dist/onboarding/concierge_client.js +80 -0
- package/dist/onboarding/concierge_client.js.map +1 -0
- package/dist/onboarding/next_step.d.ts +32 -0
- package/dist/onboarding/next_step.d.ts.map +1 -0
- package/dist/onboarding/next_step.js +60 -0
- package/dist/onboarding/next_step.js.map +1 -0
- package/dist/pending/store.d.ts +7 -0
- package/dist/pending/store.d.ts.map +1 -1
- package/dist/pending/store.js +19 -0
- package/dist/pending/store.js.map +1 -1
- package/dist/pending/types.d.ts +20 -0
- package/dist/pending/types.d.ts.map +1 -1
- package/dist/pending/types.js.map +1 -1
- package/dist/reader/parsers/epub.d.ts.map +1 -1
- package/dist/reader/parsers/epub.js +6 -1
- package/dist/reader/parsers/epub.js.map +1 -1
- package/dist/relay/board_snapshot.d.ts +69 -0
- package/dist/relay/board_snapshot.d.ts.map +1 -0
- package/dist/relay/board_snapshot.js +149 -0
- package/dist/relay/board_snapshot.js.map +1 -0
- package/dist/relay/command_consumer.d.ts +91 -0
- package/dist/relay/command_consumer.d.ts.map +1 -0
- package/dist/relay/command_consumer.js +129 -0
- package/dist/relay/command_consumer.js.map +1 -0
- package/dist/relay/mock_relay.d.ts +34 -0
- package/dist/relay/mock_relay.d.ts.map +1 -0
- package/dist/relay/mock_relay.js +93 -0
- package/dist/relay/mock_relay.js.map +1 -0
- package/dist/relay/relay_client.d.ts +28 -0
- package/dist/relay/relay_client.d.ts.map +1 -0
- package/dist/relay/relay_client.js +80 -0
- package/dist/relay/relay_client.js.map +1 -0
- package/dist/relay/relay_publisher.d.ts +59 -0
- package/dist/relay/relay_publisher.d.ts.map +1 -0
- package/dist/relay/relay_publisher.js +101 -0
- package/dist/relay/relay_publisher.js.map +1 -0
- package/dist/relay/types.d.ts +54 -0
- package/dist/relay/types.d.ts.map +1 -0
- package/dist/relay/types.js +8 -0
- package/dist/relay/types.js.map +1 -0
- package/dist/semantic_graph/ast_extract.d.ts +5 -0
- package/dist/semantic_graph/ast_extract.d.ts.map +1 -1
- package/dist/semantic_graph/ast_extract.js +46 -0
- package/dist/semantic_graph/ast_extract.js.map +1 -1
- package/dist/semantic_graph/derive.d.ts +12 -0
- package/dist/semantic_graph/derive.d.ts.map +1 -1
- package/dist/semantic_graph/derive.js +325 -6
- package/dist/semantic_graph/derive.js.map +1 -1
- package/dist/semantic_graph/store.d.ts +9 -0
- package/dist/semantic_graph/store.d.ts.map +1 -1
- package/dist/semantic_graph/store.js +23 -1
- package/dist/semantic_graph/store.js.map +1 -1
- package/dist/semantic_graph/types.d.ts +1 -1
- package/dist/semantic_graph/types.d.ts.map +1 -1
- package/dist/support/github_dispatcher.d.ts.map +1 -1
- package/dist/support/github_dispatcher.js +30 -2
- package/dist/support/github_dispatcher.js.map +1 -1
- package/dist/tasks/supervisor.d.ts +88 -0
- package/dist/tasks/supervisor.d.ts.map +1 -0
- package/dist/tasks/supervisor.js +308 -0
- package/dist/tasks/supervisor.js.map +1 -0
- package/dist/tasks/task_type.d.ts +11 -0
- package/dist/tasks/task_type.d.ts.map +1 -0
- package/dist/tasks/task_type.js +21 -0
- package/dist/tasks/task_type.js.map +1 -0
- package/dist/vault/store.d.ts.map +1 -1
- package/dist/vault/store.js +6 -1
- package/dist/vault/store.js.map +1 -1
- package/dist/version.d.ts +3 -5
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +3 -5
- package/dist/version.js.map +1 -1
- package/dist/voice/providers/google.d.ts.map +1 -1
- package/dist/voice/providers/google.js +6 -4
- package/dist/voice/providers/google.js.map +1 -1
- package/dist/workflow/config.d.ts +43 -0
- package/dist/workflow/config.d.ts.map +1 -0
- package/dist/workflow/config.js +199 -0
- package/dist/workflow/config.js.map +1 -0
- package/dist/workflow/instances.d.ts +40 -0
- package/dist/workflow/instances.d.ts.map +1 -0
- package/dist/workflow/instances.js +122 -0
- package/dist/workflow/instances.js.map +1 -0
- package/dist/workflow/multi_view.d.ts +27 -0
- package/dist/workflow/multi_view.d.ts.map +1 -0
- package/dist/workflow/multi_view.js +76 -0
- package/dist/workflow/multi_view.js.map +1 -0
- package/dist/workflow/orchestrator.d.ts +111 -0
- package/dist/workflow/orchestrator.d.ts.map +1 -0
- package/dist/workflow/orchestrator.js +241 -0
- package/dist/workflow/orchestrator.js.map +1 -0
- package/docs/doctrine/default-design-system.md +32 -0
- package/docs/doctrine/office-tools.md +20 -0
- package/docs/doctrine/verb-composition.md +16 -0
- package/docs/doctrine/workflow-kinds.md +39 -22
- package/docs/doctrine/workflow.md +12 -4
- package/package.json +2 -1
- package/src/i18n/catalogs/ar.json +223 -210
- package/src/i18n/catalogs/de.json +223 -210
- package/src/i18n/catalogs/en.json +223 -210
- package/src/i18n/catalogs/es.json +223 -210
- package/src/i18n/catalogs/fr.json +223 -210
- package/src/i18n/catalogs/hi.json +223 -210
- package/src/i18n/catalogs/it.json +223 -210
- package/src/i18n/catalogs/ja.json +223 -210
- package/src/i18n/catalogs/pt.json +223 -210
- package/src/i18n/catalogs/zh.json +223 -210
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact "board" snapshot for the P2 relay -- what the phone's Pizarron
|
|
3
|
+
* tab mirrors. The desktop's canonical boards (semantic graph, BPMN,
|
|
4
|
+
* etc.) are large; this is a small, high-signal digest that fits the
|
|
5
|
+
* relay payload budget (<32KB): the workflow phases/progress + the open
|
|
6
|
+
* pendings. Best-effort -- a missing source just drops that section.
|
|
7
|
+
*
|
|
8
|
+
* ASCII-only.
|
|
9
|
+
*/
|
|
10
|
+
import { readWorkflow } from '../workflow/state.js';
|
|
11
|
+
import { computeWorkflowView } from '../workflow/view.js';
|
|
12
|
+
import { readPending } from '../pending/store.js';
|
|
13
|
+
export const NODE_GROUPS = [
|
|
14
|
+
'backend', 'frontend', 'mobile', 'tests', 'tools', 'scripts', 'libs', 'protocol',
|
|
15
|
+
];
|
|
16
|
+
/** Deterministic high-level grouping of a graph node from its id + kind.
|
|
17
|
+
* Tool/script/lib are exact by id prefix; module/component fall back to
|
|
18
|
+
* path/name heuristics (best-effort -- coarse module nodes are fuzzy). */
|
|
19
|
+
export function nodeGroup(id, name, kind) {
|
|
20
|
+
if (id.startsWith('tool.ci.'))
|
|
21
|
+
return 'tools';
|
|
22
|
+
if (id.startsWith('tool.npm.') || id.startsWith('tool.script.'))
|
|
23
|
+
return 'scripts';
|
|
24
|
+
if (kind === 'library' || id.startsWith('lib.'))
|
|
25
|
+
return 'libs';
|
|
26
|
+
const s = (id + ' ' + name).toLowerCase();
|
|
27
|
+
if (/(^|[._-])(test|tests|spec|vitest|playwright|qa)([._-]|$)|test_agent|\.test\b/.test(s))
|
|
28
|
+
return 'tests';
|
|
29
|
+
if (/forge-mobile|nac3-rn|expo|metro|apps[._-]forge|(^|[._-])mobile([._-]|$)/.test(s))
|
|
30
|
+
return 'mobile';
|
|
31
|
+
if (/panel|pizarron|chat|reader|design|figma|(^|[._-])ui([._-]|$)|component|webview|board|view/.test(s))
|
|
32
|
+
return 'frontend';
|
|
33
|
+
if (kind === 'protocol' || id.startsWith('proto'))
|
|
34
|
+
return 'protocol';
|
|
35
|
+
return 'backend';
|
|
36
|
+
}
|
|
37
|
+
/** Build a compact board snapshot. Never throws. */
|
|
38
|
+
export async function buildBoardSnapshot(projectRoot, slug) {
|
|
39
|
+
const snap = { ts: new Date().toISOString() };
|
|
40
|
+
try {
|
|
41
|
+
const wf = await readWorkflow(projectRoot);
|
|
42
|
+
const v = computeWorkflowView(wf);
|
|
43
|
+
snap.workflow = {
|
|
44
|
+
title: v.title,
|
|
45
|
+
done: v.done_steps,
|
|
46
|
+
total: v.total_steps,
|
|
47
|
+
stages: v.stages.slice(0, 12).map((s) => ({
|
|
48
|
+
title: s.title, status: s.status, done: s.done, total: s.total,
|
|
49
|
+
})),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch { /* no workflow yet -> omit */ }
|
|
53
|
+
if (slug) {
|
|
54
|
+
try {
|
|
55
|
+
const pf = await readPending(slug);
|
|
56
|
+
const open = pf.items.filter((i) => !i.closed_at);
|
|
57
|
+
open.sort((a, b) => a.priority - b.priority);
|
|
58
|
+
snap.pendings = {
|
|
59
|
+
open: open.length,
|
|
60
|
+
/* Pablo 2026-06-15: 6 was too few -- the phone's list looked
|
|
61
|
+
* truncated. Send up to 40 (the mobile scrolls). */
|
|
62
|
+
top: open.slice(0, 40).map((i) => ({
|
|
63
|
+
id: i.id, title: i.title, priority: i.priority,
|
|
64
|
+
category: i.category,
|
|
65
|
+
/* functional detail for the phone's pending-detail view (#4),
|
|
66
|
+
* bounded so the snapshot stays compact. */
|
|
67
|
+
body: typeof i.body === 'string'
|
|
68
|
+
? i.body.slice(0, 500) : undefined,
|
|
69
|
+
})),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch { /* no pendings -> omit */ }
|
|
73
|
+
/* PND-094 ext -- the coexisting workflows + tasks-by-stage. Compact:
|
|
74
|
+
* task id/title/closed only (the phone cross-refs `pendings` for the
|
|
75
|
+
* full detail). Capped so the board stays small. */
|
|
76
|
+
try {
|
|
77
|
+
const { computeWorkflowsView } = await import('../workflow/multi_view.js');
|
|
78
|
+
const cards = await computeWorkflowsView(slug, 'open');
|
|
79
|
+
if (cards.length) {
|
|
80
|
+
snap.workflows = cards.slice(0, 8).map((c) => ({
|
|
81
|
+
code: c.code, name: c.name, kind: c.kind, status: c.status,
|
|
82
|
+
done: c.done, total: c.total,
|
|
83
|
+
stages: c.stages.map((s) => ({
|
|
84
|
+
id: s.id, title: s.title,
|
|
85
|
+
tasks: s.tasks.slice(0, 60).map((t) => ({
|
|
86
|
+
id: t.id, title: t.title.slice(0, 90), closed: t.closed,
|
|
87
|
+
...(t.ad_hoc ? { ad_hoc: true } : {}),
|
|
88
|
+
})),
|
|
89
|
+
})),
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch { /* no workflows -> omit */ }
|
|
94
|
+
}
|
|
95
|
+
return snap;
|
|
96
|
+
}
|
|
97
|
+
/* The semantic graph for the phone's "Grafo" tab travels in its OWN
|
|
98
|
+
* relay event (kind 'graph') so it does not share the board's 32KB
|
|
99
|
+
* budget -- that is what let the old code cap nodes at 60 and drop most
|
|
100
|
+
* edges. With its own budget we send the whole connected subgraph (all
|
|
101
|
+
* edges + their nodes) plus a generous fill of relevant isolated nodes.
|
|
102
|
+
*
|
|
103
|
+
* Same edge-fidelity rule: pick the EDGE-CONNECTED nodes first so no
|
|
104
|
+
* edge is orphaned, then fill with the most relevant nodes. */
|
|
105
|
+
export async function buildGraphSnapshot(slug) {
|
|
106
|
+
try {
|
|
107
|
+
const { listRelevant, listAllEdges } = await import('../semantic_graph/store.js');
|
|
108
|
+
const NODE_CAP = 300; /* own event budget -- far above the old 60 */
|
|
109
|
+
const EDGE_CAP = 600;
|
|
110
|
+
/* Exclude `comp.*` (function-level) nodes -- too granular + their long
|
|
111
|
+
* ids blow the payload past 32KB. Same level the desktop graph shows
|
|
112
|
+
* (modules / libs / tooling / protocol). */
|
|
113
|
+
const pool = (await listRelevant(slug, { limit: 2000, min_confidence: 'claimed' }))
|
|
114
|
+
.filter((n) => !n.id.startsWith('comp.'));
|
|
115
|
+
if (!pool.length)
|
|
116
|
+
return null;
|
|
117
|
+
const byId = new Map(pool.map((n) => [n.id, n]));
|
|
118
|
+
const allEdges = (await listAllEdges(slug)).slice(0, EDGE_CAP);
|
|
119
|
+
const chosen = new Map();
|
|
120
|
+
for (const e of allEdges) {
|
|
121
|
+
if (chosen.size >= NODE_CAP)
|
|
122
|
+
break;
|
|
123
|
+
const a = byId.get(e.from_id);
|
|
124
|
+
if (a)
|
|
125
|
+
chosen.set(a.id, a);
|
|
126
|
+
const b = byId.get(e.to_id);
|
|
127
|
+
if (b)
|
|
128
|
+
chosen.set(b.id, b);
|
|
129
|
+
}
|
|
130
|
+
for (const n of pool) {
|
|
131
|
+
if (chosen.size >= NODE_CAP)
|
|
132
|
+
break;
|
|
133
|
+
if (!chosen.has(n.id))
|
|
134
|
+
chosen.set(n.id, n);
|
|
135
|
+
}
|
|
136
|
+
const nodeIds = new Set(chosen.keys());
|
|
137
|
+
const edges = allEdges.filter((e) => nodeIds.has(e.from_id) && nodeIds.has(e.to_id));
|
|
138
|
+
return {
|
|
139
|
+
nodes: [...chosen.values()].map((n) => ({
|
|
140
|
+
id: n.id, label: n.name, kind: n.kind, group: nodeGroup(n.id, n.name, n.kind),
|
|
141
|
+
})),
|
|
142
|
+
edges: edges.map((e) => ({ from: e.from_id, to: e.to_id, kind: e.kind })),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=board_snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board_snapshot.js","sourceRoot":"","sources":["../../src/relay/board_snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Desktop relay command consumer (P2, Fase 2).
|
|
3
|
+
*
|
|
4
|
+
* The mirror of RelayPublisher: where the publisher pushes the desktop's
|
|
5
|
+
* activity OUT to the phone, this pulls the phone's chat commands IN.
|
|
6
|
+
* It polls the relay for `command` events, runs each through the chat
|
|
7
|
+
* turn pipeline, and publishes the assistant reply back as `chat_reply`
|
|
8
|
+
* (correlated by the command id the phone generated).
|
|
9
|
+
*
|
|
10
|
+
* This is what makes "command your laptop from your phone" real: the
|
|
11
|
+
* phone is a thin remote; the desktop session (with the user's BYOK
|
|
12
|
+
* brain + project context) does the actual work and answers.
|
|
13
|
+
*
|
|
14
|
+
* Best-effort + gated (YF_RELAY=1 + paired). A relay outage or a failed
|
|
15
|
+
* turn never crashes the desktop; the failure is reported back to the
|
|
16
|
+
* phone as a reply so the user is never left hanging.
|
|
17
|
+
*
|
|
18
|
+
* Deps are injected so the loop is unit-tested without a network or the
|
|
19
|
+
* full HTTP server (tests/relay-command-consumer.test.ts).
|
|
20
|
+
*
|
|
21
|
+
* ASCII-only.
|
|
22
|
+
*/
|
|
23
|
+
export interface ConsumerCommand {
|
|
24
|
+
/** command id generated by the phone (correlates the reply). */
|
|
25
|
+
id: string;
|
|
26
|
+
/** the user's message text (empty for control commands). */
|
|
27
|
+
text: string;
|
|
28
|
+
/** command type (default 'chat'). */
|
|
29
|
+
type?: string;
|
|
30
|
+
/** conversation thread the phone wants this to run in. Absent = the
|
|
31
|
+
* unified desktop thread ('Sesion de la notebook'). */
|
|
32
|
+
sessionId?: string;
|
|
33
|
+
/** Control commands (type='control') carry an action instead of text:
|
|
34
|
+
* e.g. switch_project (args.slug) or interrupt. They do NOT run a
|
|
35
|
+
* chat turn -- they go through deps.onControl. (tasks #33, #29) */
|
|
36
|
+
control?: {
|
|
37
|
+
action: string;
|
|
38
|
+
args: Record<string, unknown>;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export interface CommandConsumerDeps {
|
|
42
|
+
/** Poll the relay for command events with seq > since. `ts` is the
|
|
43
|
+
* event's ISO timestamp (used for the freshness window). */
|
|
44
|
+
pollCommands: (since: number) => Promise<{
|
|
45
|
+
ok: boolean;
|
|
46
|
+
events: Array<{
|
|
47
|
+
seq: number;
|
|
48
|
+
payload: Record<string, unknown>;
|
|
49
|
+
ts?: string;
|
|
50
|
+
}>;
|
|
51
|
+
cursor: number;
|
|
52
|
+
}>;
|
|
53
|
+
/** Run a chat turn (in the given thread) and resolve the reply text. */
|
|
54
|
+
runTurn: (text: string, sessionId?: string) => Promise<string>;
|
|
55
|
+
/** Handle a control command (switch_project, interrupt, ...) and
|
|
56
|
+
* resolve a short status string to send back. Optional: when absent,
|
|
57
|
+
* control commands are reported as unsupported. (tasks #33, #29) */
|
|
58
|
+
onControl?: (action: string, args: Record<string, unknown>) => Promise<string>;
|
|
59
|
+
/** Publish the reply for a command (chat_reply). */
|
|
60
|
+
reply: (commandId: string, text: string, done: boolean) => void;
|
|
61
|
+
/** Gate -- only runs when true (YF_RELAY flag + paired). */
|
|
62
|
+
enabled: () => boolean;
|
|
63
|
+
/** Optional filter -- process only commands this returns true for.
|
|
64
|
+
* Lets the desktop run TWO consumers over the same stream: a chat
|
|
65
|
+
* consumer (skips control) and a fast control consumer (skips chat),
|
|
66
|
+
* so an interrupt is handled mid-turn while the chat consumer is
|
|
67
|
+
* blocked running the turn. (task #29) */
|
|
68
|
+
accept?: (cmd: ConsumerCommand) => boolean;
|
|
69
|
+
/** Poll interval ms. Default 3000. */
|
|
70
|
+
intervalMs?: number;
|
|
71
|
+
/** Freshness window (Pablo 2026-06-15): a phone command is a REAL-TIME
|
|
72
|
+
* request. On (re)connect the consumer polls the relay from cursor 0
|
|
73
|
+
* and would otherwise replay the ENTIRE backlog -- executing every old
|
|
74
|
+
* pending request one by one. Commands whose event ts is older than
|
|
75
|
+
* this are skipped (cursor still advances). Default 120000 (2 min);
|
|
76
|
+
* 0/undefined disables the window. */
|
|
77
|
+
maxCommandAgeMs?: number;
|
|
78
|
+
/** Injectable clock for the freshness window (tests). Default Date.now. */
|
|
79
|
+
now?: () => number;
|
|
80
|
+
/** Optional error sink (diagnostics). */
|
|
81
|
+
onError?: (e: unknown) => void;
|
|
82
|
+
}
|
|
83
|
+
export interface CommandConsumerHandle {
|
|
84
|
+
stop(): void;
|
|
85
|
+
/** Run one poll+dispatch cycle now (used by tests). */
|
|
86
|
+
tickOnce(): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
/** Pull a usable command out of an event payload, or null to skip. */
|
|
89
|
+
export declare function parseCommand(payload: Record<string, unknown>): ConsumerCommand | null;
|
|
90
|
+
export declare function startCommandConsumer(deps: CommandConsumerDeps): CommandConsumerHandle;
|
|
91
|
+
//# sourceMappingURL=command_consumer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command_consumer.d.ts","sourceRoot":"","sources":["../../src/relay/command_consumer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;4DACwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;wEAEoE;IACpE,OAAO,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;CAC7D;AAED,MAAM,WAAW,mBAAmB;IAClC;iEAC6D;IAC7D,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QACvC,EAAE,EAAE,OAAO,CAAC;QACZ,MAAM,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC9E,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,wEAAwE;IACxE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D;;yEAEqE;IACrE,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/E,oDAAoD;IACpD,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAChE,4DAA4D;IAC5D,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB;;;;+CAI2C;IAC3C,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC;IAC3C,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;2CAKuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,yCAAyC;IACzC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,IAAI,IAAI,CAAC;IACb,uDAAuD;IACvD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAMD,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,GAAG,IAAI,CAoBrF;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,mBAAmB,GAAG,qBAAqB,CAmErF"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Desktop relay command consumer (P2, Fase 2).
|
|
3
|
+
*
|
|
4
|
+
* The mirror of RelayPublisher: where the publisher pushes the desktop's
|
|
5
|
+
* activity OUT to the phone, this pulls the phone's chat commands IN.
|
|
6
|
+
* It polls the relay for `command` events, runs each through the chat
|
|
7
|
+
* turn pipeline, and publishes the assistant reply back as `chat_reply`
|
|
8
|
+
* (correlated by the command id the phone generated).
|
|
9
|
+
*
|
|
10
|
+
* This is what makes "command your laptop from your phone" real: the
|
|
11
|
+
* phone is a thin remote; the desktop session (with the user's BYOK
|
|
12
|
+
* brain + project context) does the actual work and answers.
|
|
13
|
+
*
|
|
14
|
+
* Best-effort + gated (YF_RELAY=1 + paired). A relay outage or a failed
|
|
15
|
+
* turn never crashes the desktop; the failure is reported back to the
|
|
16
|
+
* phone as a reply so the user is never left hanging.
|
|
17
|
+
*
|
|
18
|
+
* Deps are injected so the loop is unit-tested without a network or the
|
|
19
|
+
* full HTTP server (tests/relay-command-consumer.test.ts).
|
|
20
|
+
*
|
|
21
|
+
* ASCII-only.
|
|
22
|
+
*/
|
|
23
|
+
function errMsg(e) {
|
|
24
|
+
return e instanceof Error ? e.message : String(e);
|
|
25
|
+
}
|
|
26
|
+
/** Pull a usable command out of an event payload, or null to skip. */
|
|
27
|
+
export function parseCommand(payload) {
|
|
28
|
+
const type = typeof payload.type === 'string' ? payload.type : 'chat';
|
|
29
|
+
const id = typeof payload.id === 'string' && payload.id ? payload.id : '';
|
|
30
|
+
if (!id)
|
|
31
|
+
return null;
|
|
32
|
+
/* Control commands (switch_project, interrupt) carry an action and
|
|
33
|
+
* never run a chat turn (tasks #33, #29). */
|
|
34
|
+
if (type === 'control') {
|
|
35
|
+
const action = typeof payload.action === 'string' && payload.action ? payload.action : '';
|
|
36
|
+
if (!action)
|
|
37
|
+
return null;
|
|
38
|
+
const args = (payload.args && typeof payload.args === 'object')
|
|
39
|
+
? payload.args : {};
|
|
40
|
+
return { id, text: '', type, control: { action, args } };
|
|
41
|
+
}
|
|
42
|
+
/* Only chat-style commands run a turn. */
|
|
43
|
+
if (type !== 'chat' && type !== 'command' && type !== '')
|
|
44
|
+
return null;
|
|
45
|
+
const text = typeof payload.text === 'string' ? payload.text.trim() : '';
|
|
46
|
+
if (!text)
|
|
47
|
+
return null;
|
|
48
|
+
const sessionId = typeof payload.session_id === 'string' && payload.session_id
|
|
49
|
+
? payload.session_id : undefined;
|
|
50
|
+
return { id, text, type, ...(sessionId ? { sessionId } : {}) };
|
|
51
|
+
}
|
|
52
|
+
export function startCommandConsumer(deps) {
|
|
53
|
+
let stopped = false;
|
|
54
|
+
let cursor = 0;
|
|
55
|
+
let inflight = false;
|
|
56
|
+
let timer = null;
|
|
57
|
+
const interval = deps.intervalMs ?? 3000;
|
|
58
|
+
const tickOnce = async () => {
|
|
59
|
+
if (inflight || !deps.enabled())
|
|
60
|
+
return;
|
|
61
|
+
inflight = true;
|
|
62
|
+
try {
|
|
63
|
+
const r = await deps.pollCommands(cursor);
|
|
64
|
+
if (r && r.ok) {
|
|
65
|
+
if (typeof r.cursor === 'number')
|
|
66
|
+
cursor = r.cursor;
|
|
67
|
+
const maxAge = deps.maxCommandAgeMs ?? 120_000;
|
|
68
|
+
const nowMs = (deps.now ?? Date.now)();
|
|
69
|
+
for (const ev of r.events) {
|
|
70
|
+
/* Freshness window -- never replay a stale backlog of phone
|
|
71
|
+
* commands on (re)connect. The cursor already advanced above,
|
|
72
|
+
* so a skipped command is dropped for good. */
|
|
73
|
+
if (maxAge > 0 && ev.ts) {
|
|
74
|
+
const age = nowMs - Date.parse(ev.ts);
|
|
75
|
+
if (Number.isFinite(age) && age > maxAge)
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const cmd = parseCommand(ev.payload || {});
|
|
79
|
+
if (!cmd)
|
|
80
|
+
continue;
|
|
81
|
+
/* When two consumers share the stream, each takes its subset
|
|
82
|
+
* (the other still advances its own cursor past skipped
|
|
83
|
+
* events). (task #29) */
|
|
84
|
+
if (deps.accept && !deps.accept(cmd))
|
|
85
|
+
continue;
|
|
86
|
+
try {
|
|
87
|
+
if (cmd.control) {
|
|
88
|
+
const status = deps.onControl
|
|
89
|
+
? await deps.onControl(cmd.control.action, cmd.control.args)
|
|
90
|
+
: 'Accion no soportada: ' + cmd.control.action;
|
|
91
|
+
deps.reply(cmd.id, status, true);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const replyText = await deps.runTurn(cmd.text, cmd.sessionId);
|
|
95
|
+
deps.reply(cmd.id, replyText, true);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
deps.onError?.(e);
|
|
100
|
+
deps.reply(cmd.id, 'No pude completar tu pedido: ' + errMsg(e), true);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
deps.onError?.(e);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
inflight = false;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const schedule = () => {
|
|
113
|
+
if (stopped)
|
|
114
|
+
return;
|
|
115
|
+
timer = setTimeout(() => { void tickOnce().finally(schedule); }, interval);
|
|
116
|
+
if (timer && typeof timer.unref === 'function') {
|
|
117
|
+
timer.unref();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
schedule();
|
|
121
|
+
return {
|
|
122
|
+
stop() { stopped = true; if (timer) {
|
|
123
|
+
clearTimeout(timer);
|
|
124
|
+
timer = null;
|
|
125
|
+
} },
|
|
126
|
+
tickOnce,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=command_consumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command_consumer.js","sourceRoot":"","sources":["../../src/relay/command_consumer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory mock of the remote-relay worker (P2). Implements the
|
|
3
|
+
* contract (docs/MOBILE_REMOTE_RELAY_CONTRACT.md) so the desktop +
|
|
4
|
+
* mobile relay clients can be built + tested WITHOUT the real
|
|
5
|
+
* Cloudflare worker. Also handy for `yf chat` local dev.
|
|
6
|
+
*
|
|
7
|
+
* It exposes a `fetch`-compatible method the clients use as their
|
|
8
|
+
* fetchImpl. No auth check (the real worker validates the bearer);
|
|
9
|
+
* the mock trusts the handle in the path.
|
|
10
|
+
*
|
|
11
|
+
* ASCII-only.
|
|
12
|
+
*/
|
|
13
|
+
import { type RelayAppendEvent, type RelayFrom, type RelayAppendResult, type RelayPollResult } from './types.js';
|
|
14
|
+
export declare class MockRelay {
|
|
15
|
+
private channels;
|
|
16
|
+
private now;
|
|
17
|
+
constructor(opts?: {
|
|
18
|
+
now?: () => string;
|
|
19
|
+
});
|
|
20
|
+
private chan;
|
|
21
|
+
append(handle: string, from: RelayFrom, deviceId: string, events: RelayAppendEvent[]): RelayAppendResult;
|
|
22
|
+
poll(handle: string, since: number, kinds?: string[]): RelayPollResult;
|
|
23
|
+
/** fetch-compatible impl. Routes POST/GET /v1/relay/<handle>. */
|
|
24
|
+
fetch: (input: string | URL | {
|
|
25
|
+
url: string;
|
|
26
|
+
}, init?: {
|
|
27
|
+
method?: string;
|
|
28
|
+
body?: string;
|
|
29
|
+
}) => Promise<Response>;
|
|
30
|
+
private json;
|
|
31
|
+
/** Test helper: total events stored for a handle. */
|
|
32
|
+
size(handle: string): number;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=mock_relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock_relay.d.ts","sourceRoot":"","sources":["../../src/relay/mock_relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACY,KAAK,gBAAgB,EAAE,KAAK,SAAS,EACtD,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAC7C,MAAM,YAAY,CAAC;AAIpB,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,GAAG,CAAe;gBAEd,IAAI,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO;IAI7C,OAAO,CAAC,IAAI;IAMZ,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,iBAAiB;IAuBxG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,eAAe;IAUtE,iEAAiE;IACjE,KAAK,UAAiB,MAAM,GAAG,GAAG,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,SAAS;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,QAAQ,CAAC,CAiBjH;IAEF,OAAO,CAAC,IAAI;IAIZ,qDAAqD;IACrD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAC7B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory mock of the remote-relay worker (P2). Implements the
|
|
3
|
+
* contract (docs/MOBILE_REMOTE_RELAY_CONTRACT.md) so the desktop +
|
|
4
|
+
* mobile relay clients can be built + tested WITHOUT the real
|
|
5
|
+
* Cloudflare worker. Also handy for `yf chat` local dev.
|
|
6
|
+
*
|
|
7
|
+
* It exposes a `fetch`-compatible method the clients use as their
|
|
8
|
+
* fetchImpl. No auth check (the real worker validates the bearer);
|
|
9
|
+
* the mock trusts the handle in the path.
|
|
10
|
+
*
|
|
11
|
+
* ASCII-only.
|
|
12
|
+
*/
|
|
13
|
+
import { RELAY_LIMITS, } from './types.js';
|
|
14
|
+
export class MockRelay {
|
|
15
|
+
channels = new Map();
|
|
16
|
+
now;
|
|
17
|
+
constructor(opts = {}) {
|
|
18
|
+
this.now = opts.now ?? (() => new Date().toISOString());
|
|
19
|
+
}
|
|
20
|
+
chan(handle) {
|
|
21
|
+
let c = this.channels.get(handle);
|
|
22
|
+
if (!c) {
|
|
23
|
+
c = { seq: 0, log: [], firstSeq: 1 };
|
|
24
|
+
this.channels.set(handle, c);
|
|
25
|
+
}
|
|
26
|
+
return c;
|
|
27
|
+
}
|
|
28
|
+
append(handle, from, deviceId, events) {
|
|
29
|
+
if (!Array.isArray(events) || events.length === 0)
|
|
30
|
+
return { ok: false, error: 'no_events' };
|
|
31
|
+
if (events.length > RELAY_LIMITS.maxEventsPerAppend)
|
|
32
|
+
return { ok: false, error: 'payload_too_large' };
|
|
33
|
+
for (const e of events) {
|
|
34
|
+
if (Buffer.byteLength(JSON.stringify(e.payload ?? {}), 'utf-8') > RELAY_LIMITS.maxPayloadBytes) {
|
|
35
|
+
return { ok: false, error: 'payload_too_large' };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const c = this.chan(handle);
|
|
39
|
+
const ts = this.now();
|
|
40
|
+
const seqFrom = c.seq + 1;
|
|
41
|
+
for (const e of events) {
|
|
42
|
+
c.seq += 1;
|
|
43
|
+
c.log.push({ seq: c.seq, ts, from, device_id: deviceId, kind: e.kind, payload: e.payload ?? {} });
|
|
44
|
+
}
|
|
45
|
+
/* retencion: ultimos N */
|
|
46
|
+
if (c.log.length > RELAY_LIMITS.retainEvents) {
|
|
47
|
+
c.log.splice(0, c.log.length - RELAY_LIMITS.retainEvents);
|
|
48
|
+
c.firstSeq = c.log[0].seq;
|
|
49
|
+
}
|
|
50
|
+
return { ok: true, seq_from: seqFrom, seq_to: c.seq, server_ts: ts };
|
|
51
|
+
}
|
|
52
|
+
poll(handle, since, kinds) {
|
|
53
|
+
const c = this.chan(handle);
|
|
54
|
+
const sinceN = Number.isFinite(since) ? since : 0;
|
|
55
|
+
const truncated = sinceN > 0 && sinceN < c.firstSeq - 1;
|
|
56
|
+
let events = c.log.filter((e) => e.seq > sinceN);
|
|
57
|
+
if (kinds && kinds.length)
|
|
58
|
+
events = events.filter((e) => kinds.indexOf(e.kind) >= 0);
|
|
59
|
+
const cursor = events.length ? events[events.length - 1].seq : sinceN;
|
|
60
|
+
return { ok: true, events, cursor, truncated };
|
|
61
|
+
}
|
|
62
|
+
/** fetch-compatible impl. Routes POST/GET /v1/relay/<handle>. */
|
|
63
|
+
fetch = async (input, init) => {
|
|
64
|
+
const urlStr = typeof input === 'string' ? input : ('url' in input ? input.url : String(input));
|
|
65
|
+
const url = new URL(urlStr, 'http://mock');
|
|
66
|
+
const m = url.pathname.match(/\/v1\/relay\/([^/]+)/);
|
|
67
|
+
if (!m)
|
|
68
|
+
return this.json(404, { ok: false, error: 'no_channel' });
|
|
69
|
+
const handle = decodeURIComponent(m[1]);
|
|
70
|
+
const method = (init?.method ?? 'GET').toUpperCase();
|
|
71
|
+
if (method === 'POST') {
|
|
72
|
+
let body = {};
|
|
73
|
+
try {
|
|
74
|
+
body = JSON.parse(init?.body ?? '{}');
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return this.json(400, { ok: false, error: 'bad_json' });
|
|
78
|
+
}
|
|
79
|
+
const r = this.append(handle, body.from === 'mobile' ? 'mobile' : 'desktop', String(body.device_id ?? ''), body.events ?? []);
|
|
80
|
+
return this.json(r.ok ? 200 : (r.error === 'payload_too_large' ? 413 : 400), r);
|
|
81
|
+
}
|
|
82
|
+
const since = Number(url.searchParams.get('since') ?? '0');
|
|
83
|
+
const kindsParam = url.searchParams.get('kinds');
|
|
84
|
+
const kinds = kindsParam ? kindsParam.split(',').map((s) => s.trim()).filter(Boolean) : undefined;
|
|
85
|
+
return this.json(200, this.poll(handle, since, kinds));
|
|
86
|
+
};
|
|
87
|
+
json(status, body) {
|
|
88
|
+
return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } });
|
|
89
|
+
}
|
|
90
|
+
/** Test helper: total events stored for a handle. */
|
|
91
|
+
size(handle) { return this.chan(handle).log.length; }
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=mock_relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock_relay.js","sourceRoot":"","sources":["../../src/relay/mock_relay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { RelayFrom, RelayAppendEvent, RelayAppendResult, RelayPollResult } from './types.js';
|
|
2
|
+
/** Minimal fetch shape we depend on (lets the MockRelay + RN fetch
|
|
3
|
+
* satisfy it without matching the full `typeof fetch` overloads). */
|
|
4
|
+
export type FetchLike = (url: string, init?: {
|
|
5
|
+
method?: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
body?: string;
|
|
8
|
+
}) => Promise<{
|
|
9
|
+
status: number;
|
|
10
|
+
json: () => Promise<unknown>;
|
|
11
|
+
}>;
|
|
12
|
+
export interface RelayDeps {
|
|
13
|
+
/** Stable device id (lets the other side ignore our echoes). */
|
|
14
|
+
deviceId: string;
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
fetchImpl?: FetchLike;
|
|
17
|
+
/** Test/override: returns the bearer + handle. Defaults to vault +
|
|
18
|
+
* license. */
|
|
19
|
+
getAuth?: () => Promise<{
|
|
20
|
+
bearer: string;
|
|
21
|
+
handle: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
/** Append events to this handle's relay log. */
|
|
25
|
+
export declare function relayAppend(deps: RelayDeps, from: RelayFrom, events: RelayAppendEvent[]): Promise<RelayAppendResult>;
|
|
26
|
+
/** Poll for events with seq > since (optionally filtered by kind). */
|
|
27
|
+
export declare function relayPoll(deps: RelayDeps, since: number, kinds?: string[]): Promise<RelayPollResult>;
|
|
28
|
+
//# sourceMappingURL=relay_client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay_client.d.ts","sourceRoot":"","sources":["../../src/relay/relay_client.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,SAAS,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAChE,MAAM,YAAY,CAAC;AAIpB;sEACsE;AACtE,MAAM,MAAM,SAAS,GAAG,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KACxE,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAE/D,MAAM,WAAW,SAAS;IACxB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;mBACe;IACf,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D;AAkCD,gDAAgD;AAChD,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAU1H;AAED,sEAAsE;AACtE,wBAAsB,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAa1G"}
|