@poping/yome 0.0.2 → 0.0.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.
- package/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +306 -27
- package/README.zh-CN.md +333 -0
- package/bin/yome-calwatch +0 -0
- package/dist/agent.d.ts +24 -2
- package/dist/agent.js +34 -2
- package/dist/agent.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.js +121 -13
- package/dist/context.js.map +1 -1
- package/dist/daemon/calendarPermission.d.ts +10 -0
- package/dist/daemon/calendarPermission.js +68 -0
- package/dist/daemon/calendarPermission.js.map +1 -0
- package/dist/daemon/cronCli.d.ts +19 -0
- package/dist/daemon/cronCli.js +403 -0
- package/dist/daemon/cronCli.js.map +1 -0
- package/dist/daemon/envHint.d.ts +5 -0
- package/dist/daemon/envHint.js +139 -0
- package/dist/daemon/envHint.js.map +1 -0
- package/dist/daemon/humanCron.d.ts +1 -0
- package/dist/daemon/humanCron.js +72 -0
- package/dist/daemon/humanCron.js.map +1 -0
- package/dist/daemon/humanOnce.d.ts +1 -0
- package/dist/daemon/humanOnce.js +54 -0
- package/dist/daemon/humanOnce.js.map +1 -0
- package/dist/daemon/index.d.ts +5 -0
- package/dist/daemon/index.js +168 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/launchd.d.ts +7 -0
- package/dist/daemon/launchd.js +93 -0
- package/dist/daemon/launchd.js.map +1 -0
- package/dist/daemon/log.d.ts +17 -0
- package/dist/daemon/log.js +57 -0
- package/dist/daemon/log.js.map +1 -0
- package/dist/daemon/paths.d.ts +11 -0
- package/dist/daemon/paths.js +27 -0
- package/dist/daemon/paths.js.map +1 -0
- package/dist/daemon/runTaskEntry.d.ts +1 -0
- package/dist/daemon/runTaskEntry.js +67 -0
- package/dist/daemon/runTaskEntry.js.map +1 -0
- package/dist/daemon/runner.d.ts +21 -0
- package/dist/daemon/runner.js +175 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/scheduler.d.ts +5 -0
- package/dist/daemon/scheduler.js +162 -0
- package/dist/daemon/scheduler.js.map +1 -0
- package/dist/daemon/taskStore.d.ts +62 -0
- package/dist/daemon/taskStore.js +88 -0
- package/dist/daemon/taskStore.js.map +1 -0
- package/dist/daemon/triggers/calendar.d.ts +8 -0
- package/dist/daemon/triggers/calendar.js +248 -0
- package/dist/daemon/triggers/calendar.js.map +1 -0
- package/dist/daemon/triggers/childRunner.d.ts +14 -0
- package/dist/daemon/triggers/childRunner.js +111 -0
- package/dist/daemon/triggers/childRunner.js.map +1 -0
- package/dist/daemon/triggers/cron.d.ts +14 -0
- package/dist/daemon/triggers/cron.js +91 -0
- package/dist/daemon/triggers/cron.js.map +1 -0
- package/dist/daemon/triggers/file.d.ts +7 -0
- package/dist/daemon/triggers/file.js +123 -0
- package/dist/daemon/triggers/file.js.map +1 -0
- package/dist/daemon/triggers/once.d.ts +10 -0
- package/dist/daemon/triggers/once.js +80 -0
- package/dist/daemon/triggers/once.js.map +1 -0
- package/dist/index.js +240 -15
- package/dist/index.js.map +1 -1
- package/dist/llm.js +45 -2
- package/dist/llm.js.map +1 -1
- package/dist/loops/chain.js +8 -0
- package/dist/loops/chain.js.map +1 -1
- package/dist/loops/evaluator.js +8 -0
- package/dist/loops/evaluator.js.map +1 -1
- package/dist/loops/orchestrator.js +8 -0
- package/dist/loops/orchestrator.js.map +1 -1
- package/dist/loops/parallel.js.map +1 -1
- package/dist/loops/route.js +8 -0
- package/dist/loops/route.js.map +1 -1
- package/dist/loops/simple.js +15 -0
- package/dist/loops/simple.js.map +1 -1
- package/dist/permissions/index.d.ts +1 -1
- package/dist/permissions/index.js +1 -1
- package/dist/permissions/index.js.map +1 -1
- package/dist/permissions/loader.d.ts +20 -1
- package/dist/permissions/loader.js +51 -0
- package/dist/permissions/loader.js.map +1 -1
- package/dist/redact.d.ts +56 -0
- package/dist/redact.js +191 -0
- package/dist/redact.js.map +1 -0
- package/dist/skills/runner/applescript.d.ts +49 -0
- package/dist/skills/runner/applescript.js +100 -0
- package/dist/skills/runner/applescript.js.map +1 -0
- package/dist/skills/runner/dispatcher.d.ts +128 -0
- package/dist/skills/runner/dispatcher.js +617 -0
- package/dist/skills/runner/dispatcher.js.map +1 -0
- package/dist/skills/runner/dispatcher.test.d.ts +1 -0
- package/dist/skills/runner/dispatcher.test.js +141 -0
- package/dist/skills/runner/dispatcher.test.js.map +1 -0
- package/dist/skills/runner/kernel.d.ts +8 -0
- package/dist/skills/runner/kernel.js +731 -0
- package/dist/skills/runner/kernel.js.map +1 -0
- package/dist/skills/runner/nodeBackend.d.ts +32 -0
- package/dist/skills/runner/nodeBackend.js +147 -0
- package/dist/skills/runner/nodeBackend.js.map +1 -0
- package/dist/skills/runner/tokenizer.d.ts +36 -0
- package/dist/skills/runner/tokenizer.js +177 -0
- package/dist/skills/runner/tokenizer.js.map +1 -0
- package/dist/state/todos.d.ts +12 -0
- package/dist/state/todos.js +32 -0
- package/dist/state/todos.js.map +1 -0
- package/dist/threadCli.d.ts +11 -0
- package/dist/threadCli.js +177 -0
- package/dist/threadCli.js.map +1 -0
- package/dist/threadShare.d.ts +21 -0
- package/dist/threadShare.js +121 -0
- package/dist/threadShare.js.map +1 -0
- package/dist/threadSubmit.d.ts +32 -0
- package/dist/threadSubmit.js +199 -0
- package/dist/threadSubmit.js.map +1 -0
- package/dist/tools/askUser.d.ts +20 -0
- package/dist/tools/askUser.js +126 -0
- package/dist/tools/askUser.js.map +1 -0
- package/dist/tools/bash.js +63 -14
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/index.d.ts +24 -2
- package/dist/tools/index.js +54 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/skillCall.d.ts +2 -0
- package/dist/tools/skillCall.js +77 -0
- package/dist/tools/skillCall.js.map +1 -0
- package/dist/tools/todoWrite.d.ts +2 -0
- package/dist/tools/todoWrite.js +141 -0
- package/dist/tools/todoWrite.js.map +1 -0
- package/dist/tools/yome.d.ts +2 -0
- package/dist/tools/yome.js +87 -0
- package/dist/tools/yome.js.map +1 -0
- package/dist/ui/AgentPicker.js +3 -3
- package/dist/ui/AgentPicker.js.map +1 -1
- package/dist/ui/App.js +263 -61
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/AskUserPrompt.d.ts +7 -0
- package/dist/ui/AskUserPrompt.js +78 -0
- package/dist/ui/AskUserPrompt.js.map +1 -0
- package/dist/ui/Banner.d.ts +2 -1
- package/dist/ui/Banner.js +23 -4
- package/dist/ui/Banner.js.map +1 -1
- package/dist/ui/InputBar.js +25 -36
- package/dist/ui/InputBar.js.map +1 -1
- package/dist/ui/Markdown.d.ts +2 -2
- package/dist/ui/Markdown.js +22 -7
- package/dist/ui/Markdown.js.map +1 -1
- package/dist/ui/MarketplacePicker.d.ts +7 -0
- package/dist/ui/MarketplacePicker.js +122 -0
- package/dist/ui/MarketplacePicker.js.map +1 -0
- package/dist/ui/MessageList.d.ts +12 -1
- package/dist/ui/MessageList.js +72 -7
- package/dist/ui/MessageList.js.map +1 -1
- package/dist/ui/ModelPicker.js +4 -4
- package/dist/ui/ModelPicker.js.map +1 -1
- package/dist/ui/MultilineTextInput.d.ts +31 -0
- package/dist/ui/MultilineTextInput.js +393 -0
- package/dist/ui/MultilineTextInput.js.map +1 -0
- package/dist/ui/MultilineTextInput.test.d.ts +1 -0
- package/dist/ui/MultilineTextInput.test.js +30 -0
- package/dist/ui/MultilineTextInput.test.js.map +1 -0
- package/dist/ui/PermissionPrompt.d.ts +16 -4
- package/dist/ui/PermissionPrompt.js +60 -15
- package/dist/ui/PermissionPrompt.js.map +1 -1
- package/dist/ui/SessionPicker.js +2 -2
- package/dist/ui/SessionPicker.js.map +1 -1
- package/dist/ui/ShimmerText.d.ts +8 -0
- package/dist/ui/ShimmerText.js +40 -0
- package/dist/ui/ShimmerText.js.map +1 -0
- package/dist/ui/Spinner.js +3 -9
- package/dist/ui/Spinner.js.map +1 -1
- package/dist/ui/TodoPanel.d.ts +7 -0
- package/dist/ui/TodoPanel.js +36 -0
- package/dist/ui/TodoPanel.js.map +1 -0
- package/dist/ui/TogglePicker.js +4 -4
- package/dist/ui/TogglePicker.js.map +1 -1
- package/dist/ui/ToolResult.js +6 -0
- package/dist/ui/ToolResult.js.map +1 -1
- package/dist/ui/UnifiedSkillsPicker.d.ts +10 -0
- package/dist/ui/UnifiedSkillsPicker.js +63 -0
- package/dist/ui/UnifiedSkillsPicker.js.map +1 -0
- package/dist/ui/animation.d.ts +3 -0
- package/dist/ui/animation.js +48 -0
- package/dist/ui/animation.js.map +1 -0
- package/dist/ui/useThrottledStream.d.ts +7 -0
- package/dist/ui/useThrottledStream.js +63 -0
- package/dist/ui/useThrottledStream.js.map +1 -0
- package/dist/yomeSkills/auth.d.ts +20 -0
- package/dist/yomeSkills/auth.js +70 -0
- package/dist/yomeSkills/auth.js.map +1 -0
- package/dist/yomeSkills/blacklist.d.ts +33 -0
- package/dist/yomeSkills/blacklist.js +101 -0
- package/dist/yomeSkills/blacklist.js.map +1 -0
- package/dist/yomeSkills/capabilities.d.ts +54 -0
- package/dist/yomeSkills/capabilities.js +175 -0
- package/dist/yomeSkills/capabilities.js.map +1 -0
- package/dist/yomeSkills/capabilityGuard.d.ts +31 -0
- package/dist/yomeSkills/capabilityGuard.js +113 -0
- package/dist/yomeSkills/capabilityGuard.js.map +1 -0
- package/dist/yomeSkills/cli.d.ts +25 -0
- package/dist/yomeSkills/cli.js +624 -0
- package/dist/yomeSkills/cli.js.map +1 -0
- package/dist/yomeSkills/deprecate.d.ts +29 -0
- package/dist/yomeSkills/deprecate.js +99 -0
- package/dist/yomeSkills/deprecate.js.map +1 -0
- package/dist/yomeSkills/devLink.d.ts +17 -0
- package/dist/yomeSkills/devLink.js +91 -0
- package/dist/yomeSkills/devLink.js.map +1 -0
- package/dist/yomeSkills/doctor.d.ts +13 -0
- package/dist/yomeSkills/doctor.js +152 -0
- package/dist/yomeSkills/doctor.js.map +1 -0
- package/dist/yomeSkills/enable.d.ts +8 -0
- package/dist/yomeSkills/enable.js +67 -0
- package/dist/yomeSkills/enable.js.map +1 -0
- package/dist/yomeSkills/hubPing.d.ts +1 -0
- package/dist/yomeSkills/hubPing.js +41 -0
- package/dist/yomeSkills/hubPing.js.map +1 -0
- package/dist/yomeSkills/install.d.ts +18 -0
- package/dist/yomeSkills/install.js +143 -0
- package/dist/yomeSkills/install.js.map +1 -0
- package/dist/yomeSkills/installFromHubTarball.d.ts +26 -0
- package/dist/yomeSkills/installFromHubTarball.js +161 -0
- package/dist/yomeSkills/installFromHubTarball.js.map +1 -0
- package/dist/yomeSkills/installGithub.d.ts +33 -0
- package/dist/yomeSkills/installGithub.js +213 -0
- package/dist/yomeSkills/installGithub.js.map +1 -0
- package/dist/yomeSkills/installMeta.d.ts +8 -0
- package/dist/yomeSkills/installMeta.js +76 -0
- package/dist/yomeSkills/installMeta.js.map +1 -0
- package/dist/yomeSkills/integrity.d.ts +26 -0
- package/dist/yomeSkills/integrity.js +107 -0
- package/dist/yomeSkills/integrity.js.map +1 -0
- package/dist/yomeSkills/invoke.d.ts +29 -0
- package/dist/yomeSkills/invoke.js +135 -0
- package/dist/yomeSkills/invoke.js.map +1 -0
- package/dist/yomeSkills/list.d.ts +11 -0
- package/dist/yomeSkills/list.js +55 -0
- package/dist/yomeSkills/list.js.map +1 -0
- package/dist/yomeSkills/login.d.ts +41 -0
- package/dist/yomeSkills/login.js +221 -0
- package/dist/yomeSkills/login.js.map +1 -0
- package/dist/yomeSkills/manifest.d.ts +60 -0
- package/dist/yomeSkills/manifest.js +47 -0
- package/dist/yomeSkills/manifest.js.map +1 -0
- package/dist/yomeSkills/paths.d.ts +13 -0
- package/dist/yomeSkills/paths.js +33 -0
- package/dist/yomeSkills/paths.js.map +1 -0
- package/dist/yomeSkills/publish.d.ts +18 -0
- package/dist/yomeSkills/publish.js +114 -0
- package/dist/yomeSkills/publish.js.map +1 -0
- package/dist/yomeSkills/rollback.d.ts +10 -0
- package/dist/yomeSkills/rollback.js +83 -0
- package/dist/yomeSkills/rollback.js.map +1 -0
- package/dist/yomeSkills/search.d.ts +21 -0
- package/dist/yomeSkills/search.js +31 -0
- package/dist/yomeSkills/search.js.map +1 -0
- package/dist/yomeSkills/skillsIndex.d.ts +36 -0
- package/dist/yomeSkills/skillsIndex.js +111 -0
- package/dist/yomeSkills/skillsIndex.js.map +1 -0
- package/dist/yomeSkills/unified.d.ts +53 -0
- package/dist/yomeSkills/unified.js +187 -0
- package/dist/yomeSkills/unified.js.map +1 -0
- package/dist/yomeSkills/uninstall.d.ts +7 -0
- package/dist/yomeSkills/uninstall.js +22 -0
- package/dist/yomeSkills/uninstall.js.map +1 -0
- package/dist/yomeSkills/update.d.ts +18 -0
- package/dist/yomeSkills/update.js +75 -0
- package/dist/yomeSkills/update.js.map +1 -0
- package/dist/yomeSkills/validate.d.ts +11 -0
- package/dist/yomeSkills/validate.js +99 -0
- package/dist/yomeSkills/validate.js.map +1 -0
- package/package.json +23 -6
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// useThrottledStream — coalesce high-frequency text deltas into ~30fps UI updates.
|
|
2
|
+
//
|
|
3
|
+
// LLM streams emit dozens of token deltas per second. Calling React setState
|
|
4
|
+
// on every delta forces Ink to reconcile + paint the entire tree per token —
|
|
5
|
+
// which on long sessions snowballs into terminal IO storms (the root cause of
|
|
6
|
+
// the "long session crashes my terminal" bug). Claude Code / Gemini CLI both
|
|
7
|
+
// solve this by debouncing the live region: tokens are appended to a ref, and
|
|
8
|
+
// React state is updated on a timer.
|
|
9
|
+
//
|
|
10
|
+
// This hook returns:
|
|
11
|
+
// - displayText: the throttled state suitable for rendering
|
|
12
|
+
// - append: O(1) ref-mutation, safe to call from inside an LLM stream
|
|
13
|
+
// - reset: clear buffer + state (call on /new, on done, before next run)
|
|
14
|
+
// - flush: force-publish ref buffer to state immediately
|
|
15
|
+
//
|
|
16
|
+
// Frame rate is capped at ~30 fps (33ms) by default — Ink's own renderer caps
|
|
17
|
+
// at 30 fps anyway, so anything faster is wasted work.
|
|
18
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
19
|
+
const DEFAULT_INTERVAL_MS = 33;
|
|
20
|
+
export function useThrottledStream(intervalMs = DEFAULT_INTERVAL_MS) {
|
|
21
|
+
const [displayText, setDisplayText] = useState('');
|
|
22
|
+
const bufferRef = useRef('');
|
|
23
|
+
const lastPublishedRef = useRef('');
|
|
24
|
+
const timerRef = useRef(null);
|
|
25
|
+
const ensureTimer = useCallback(() => {
|
|
26
|
+
if (timerRef.current)
|
|
27
|
+
return;
|
|
28
|
+
timerRef.current = setInterval(() => {
|
|
29
|
+
if (bufferRef.current !== lastPublishedRef.current) {
|
|
30
|
+
lastPublishedRef.current = bufferRef.current;
|
|
31
|
+
setDisplayText(bufferRef.current);
|
|
32
|
+
}
|
|
33
|
+
}, intervalMs);
|
|
34
|
+
}, [intervalMs]);
|
|
35
|
+
const stopTimer = useCallback(() => {
|
|
36
|
+
if (timerRef.current) {
|
|
37
|
+
clearInterval(timerRef.current);
|
|
38
|
+
timerRef.current = null;
|
|
39
|
+
}
|
|
40
|
+
}, []);
|
|
41
|
+
const append = useCallback((delta) => {
|
|
42
|
+
bufferRef.current += delta;
|
|
43
|
+
ensureTimer();
|
|
44
|
+
}, [ensureTimer]);
|
|
45
|
+
const flush = useCallback(() => {
|
|
46
|
+
if (bufferRef.current !== lastPublishedRef.current) {
|
|
47
|
+
lastPublishedRef.current = bufferRef.current;
|
|
48
|
+
setDisplayText(bufferRef.current);
|
|
49
|
+
}
|
|
50
|
+
}, []);
|
|
51
|
+
const reset = useCallback(() => {
|
|
52
|
+
stopTimer();
|
|
53
|
+
bufferRef.current = '';
|
|
54
|
+
lastPublishedRef.current = '';
|
|
55
|
+
setDisplayText('');
|
|
56
|
+
}, [stopTimer]);
|
|
57
|
+
// Cleanup on unmount — critical for long sessions to avoid orphaned timers.
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
return () => stopTimer();
|
|
60
|
+
}, [stopTimer]);
|
|
61
|
+
return { displayText, append, reset, flush };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=useThrottledStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useThrottledStream.js","sourceRoot":"","sources":["../../src/ui/useThrottledStream.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,qCAAqC;AACrC,EAAE;AACF,qBAAqB;AACrB,8DAA8D;AAC9D,6EAA6E;AAC7E,iFAAiF;AACjF,iEAAiE;AACjE,EAAE;AACF,8EAA8E;AAC9E,uDAAuD;AAEvD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjE,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAS/B,MAAM,UAAU,kBAAkB,CAAC,aAAqB,mBAAmB;IACzE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,CAAwC,IAAI,CAAC,CAAC;IAErE,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,IAAI,QAAQ,CAAC,OAAO;YAAE,OAAO;QAC7B,QAAQ,CAAC,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;YAClC,IAAI,SAAS,CAAC,OAAO,KAAK,gBAAgB,CAAC,OAAO,EAAE,CAAC;gBACnD,gBAAgB,CAAC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;gBAC7C,cAAc,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC;QACH,CAAC,EAAE,UAAU,CAAC,CAAC;IACjB,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAChC,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,KAAa,EAAE,EAAE;QAC3C,SAAS,CAAC,OAAO,IAAI,KAAK,CAAC;QAC3B,WAAW,EAAE,CAAC;IAChB,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,IAAI,SAAS,CAAC,OAAO,KAAK,gBAAgB,CAAC,OAAO,EAAE,CAAC;YACnD,gBAAgB,CAAC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;YAC7C,cAAc,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,SAAS,EAAE,CAAC;QACZ,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC;QACvB,gBAAgB,CAAC,OAAO,GAAG,EAAE,CAAC;QAC9B,cAAc,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,4EAA4E;IAC5E,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;IAC3B,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface YomeAuthState {
|
|
2
|
+
/** Opaque token issued by /api/cli/auth/exchange. 64 hex chars. */
|
|
3
|
+
yome_token: string;
|
|
4
|
+
/** Supabase auth.users.id — the Yome principal. */
|
|
5
|
+
yome_user_id: string;
|
|
6
|
+
/** Provider used at login time ("github" today; future: apple/wechat/email). */
|
|
7
|
+
provider: 'github' | 'apple' | 'email' | 'wechat';
|
|
8
|
+
/** Provider-side display name at login time, for whoami output. */
|
|
9
|
+
provider_login?: string;
|
|
10
|
+
/** ISO-8601 timestamp the token expires. */
|
|
11
|
+
expires_at: string;
|
|
12
|
+
/** ISO-8601 timestamp the token was first written. */
|
|
13
|
+
obtained_at: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function getAuthFilePath(): string;
|
|
16
|
+
export declare function readAuthState(): YomeAuthState | null;
|
|
17
|
+
export declare function writeAuthState(state: YomeAuthState): void;
|
|
18
|
+
export declare function clearAuthState(): boolean;
|
|
19
|
+
/** Bearer header object suitable for fetch({headers}). null if logged out. */
|
|
20
|
+
export declare function bearerHeader(): Record<string, string> | null;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// CLI-side auth state for `yome login` / `yome whoami` / hub-authenticated
|
|
2
|
+
// commands like `yome skill publish`.
|
|
3
|
+
//
|
|
4
|
+
// Yome's identity model:
|
|
5
|
+
// - Yome user (Supabase auth.users) is the principal.
|
|
6
|
+
// - GitHub / Apple / WeChat / email are *providers* attached to that user.
|
|
7
|
+
// - The CLI cannot hold a Supabase session, so after the user authenticates
|
|
8
|
+
// against any provider, the hub mints us an opaque "yome_token" tied to
|
|
9
|
+
// the Yome user_id. That token is what we persist here.
|
|
10
|
+
//
|
|
11
|
+
// We store a single JSON file at ~/.yome/auth.json with mode 0600. Token is
|
|
12
|
+
// plaintext — same trust model as gh CLI's keyring fallback / npm's
|
|
13
|
+
// authToken — but with strict perms so other local users cannot read it.
|
|
14
|
+
//
|
|
15
|
+
// Why no encryption: a passphrase prompt every command would defeat the
|
|
16
|
+
// point of CLI auth, and any keychain integration is platform-specific
|
|
17
|
+
// (macOS Security framework, Linux libsecret). Plain mode 0600 is the
|
|
18
|
+
// pragmatic baseline.
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, chmodSync, } from 'fs';
|
|
20
|
+
import { homedir } from 'os';
|
|
21
|
+
import { join, dirname } from 'path';
|
|
22
|
+
export function getAuthFilePath() {
|
|
23
|
+
return join(homedir(), '.yome', 'auth.json');
|
|
24
|
+
}
|
|
25
|
+
export function readAuthState() {
|
|
26
|
+
const f = getAuthFilePath();
|
|
27
|
+
if (!existsSync(f))
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
const text = readFileSync(f, 'utf-8');
|
|
31
|
+
const obj = JSON.parse(text);
|
|
32
|
+
if (typeof obj !== 'object' || obj == null)
|
|
33
|
+
return null;
|
|
34
|
+
if (typeof obj.yome_token !== 'string' || typeof obj.yome_user_id !== 'string')
|
|
35
|
+
return null;
|
|
36
|
+
return obj;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function writeAuthState(state) {
|
|
43
|
+
const f = getAuthFilePath();
|
|
44
|
+
mkdirSync(dirname(f), { recursive: true });
|
|
45
|
+
writeFileSync(f, JSON.stringify(state, null, 2) + '\n', { mode: 0o600 });
|
|
46
|
+
// mode in writeFileSync only applies on file CREATION; chmod again to
|
|
47
|
+
// guarantee the perms even when the file existed already.
|
|
48
|
+
try {
|
|
49
|
+
chmodSync(f, 0o600);
|
|
50
|
+
}
|
|
51
|
+
catch { /* best effort on Windows */ }
|
|
52
|
+
}
|
|
53
|
+
export function clearAuthState() {
|
|
54
|
+
const f = getAuthFilePath();
|
|
55
|
+
if (!existsSync(f))
|
|
56
|
+
return false;
|
|
57
|
+
try {
|
|
58
|
+
unlinkSync(f);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Bearer header object suitable for fetch({headers}). null if logged out. */
|
|
66
|
+
export function bearerHeader() {
|
|
67
|
+
const s = readAuthState();
|
|
68
|
+
return s ? { Authorization: `Bearer ${s.yome_token}` } : null;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/yomeSkills/auth.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sCAAsC;AACtC,EAAE;AACF,yBAAyB;AACzB,wDAAwD;AACxD,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,4DAA4D;AAC5D,EAAE;AACF,4EAA4E;AAC5E,oEAAoE;AACpE,yEAAyE;AACzE,EAAE;AACF,wEAAwE;AACxE,uEAAuE;AACvE,sEAAsE;AACtE,sBAAsB;AAEtB,OAAO,EACL,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,GAC1E,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAiBrC,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,CAAC,GAAG,eAAe,EAAE,CAAC;IAC5B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACxD,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC5F,OAAO,GAAoB,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAoB;IACjD,MAAM,CAAC,GAAG,eAAe,EAAE,CAAC;IAC5B,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,sEAAsE;IACtE,0DAA0D;IAC1D,IAAI,CAAC;QAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,4BAA4B,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,CAAC,GAAG,eAAe,EAAE,CAAC;IAC5B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,CAAC;QAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AAC7D,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY;IAC1B,MAAM,CAAC,GAAG,aAAa,EAAE,CAAC;IAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface BlacklistEntry {
|
|
2
|
+
slug: string;
|
|
3
|
+
reason: string;
|
|
4
|
+
detail?: string | null;
|
|
5
|
+
added_at: string;
|
|
6
|
+
}
|
|
7
|
+
export interface BlacklistCache {
|
|
8
|
+
fetched_at: string;
|
|
9
|
+
entries: BlacklistEntry[];
|
|
10
|
+
}
|
|
11
|
+
export declare const DEFAULT_HUB_BASE = "https://yome.work";
|
|
12
|
+
export declare function getCachePath(): string;
|
|
13
|
+
export declare function readBlacklistCache(): BlacklistCache | null;
|
|
14
|
+
export declare function writeBlacklistCache(c: BlacklistCache): void;
|
|
15
|
+
export interface RefreshOptions {
|
|
16
|
+
/** Hub base URL override; default = env YOME_HUB_BASE or https://yome.work. */
|
|
17
|
+
hubBase?: string;
|
|
18
|
+
/** Test seam — replaces global fetch. */
|
|
19
|
+
fetcher?: typeof fetch;
|
|
20
|
+
/** Force refresh even if the cache is still fresh. */
|
|
21
|
+
force?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Pull the latest blacklist from the hub if needed, write it to the
|
|
25
|
+
* cache, return whatever we ended up with. Network errors are swallowed
|
|
26
|
+
* — the previous cache is returned instead so install never breaks
|
|
27
|
+
* because of a transient hub outage.
|
|
28
|
+
*/
|
|
29
|
+
export declare function refreshBlacklist(opts?: RefreshOptions): Promise<BlacklistCache>;
|
|
30
|
+
/** Sync lookup against the cache only. Returns the entry or null. */
|
|
31
|
+
export declare function isBlacklistedSync(slug: string): BlacklistEntry | null;
|
|
32
|
+
/** Fetch + check. Use during install. */
|
|
33
|
+
export declare function pingBlacklist(slug: string, opts?: RefreshOptions): Promise<BlacklistEntry | null>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// blacklist.ts — local cache + lookups for the hub's skill blacklist.
|
|
2
|
+
//
|
|
3
|
+
// Lifecycle:
|
|
4
|
+
// - `yome skill install` calls `pingBlacklist(slug)` which (a) refreshes
|
|
5
|
+
// the cache if older than 5 min, (b) returns the matching entry if
|
|
6
|
+
// the slug is denied.
|
|
7
|
+
// - `yome skill list` consults the cache (no network) so it's instant.
|
|
8
|
+
// - The CLI agent runtime, on every skill load, checks
|
|
9
|
+
// `isBlacklistedSync(slug)` against the cache.
|
|
10
|
+
//
|
|
11
|
+
// The cache lives at ~/.yome/blacklist-cache.json. Format:
|
|
12
|
+
//
|
|
13
|
+
// { fetched_at: ISO,
|
|
14
|
+
// entries: [{ slug, reason, detail?, added_at }] }
|
|
15
|
+
//
|
|
16
|
+
// We intentionally cache even when `entries` is empty so the next `list`
|
|
17
|
+
// doesn't need a network call.
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
import { dirname, join } from 'node:path';
|
|
21
|
+
export const DEFAULT_HUB_BASE = 'https://yome.work';
|
|
22
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
23
|
+
export function getCachePath() {
|
|
24
|
+
return join(homedir(), '.yome', 'blacklist-cache.json');
|
|
25
|
+
}
|
|
26
|
+
export function readBlacklistCache() {
|
|
27
|
+
const f = getCachePath();
|
|
28
|
+
if (!existsSync(f))
|
|
29
|
+
return null;
|
|
30
|
+
try {
|
|
31
|
+
const obj = JSON.parse(readFileSync(f, 'utf-8'));
|
|
32
|
+
if (!obj || typeof obj !== 'object')
|
|
33
|
+
return null;
|
|
34
|
+
if (!Array.isArray(obj.entries))
|
|
35
|
+
return null;
|
|
36
|
+
return obj;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function writeBlacklistCache(c) {
|
|
43
|
+
const f = getCachePath();
|
|
44
|
+
mkdirSync(dirname(f), { recursive: true });
|
|
45
|
+
writeFileSync(f, JSON.stringify(c, null, 2) + '\n');
|
|
46
|
+
}
|
|
47
|
+
function isFresh(c) {
|
|
48
|
+
const t = Date.parse(c.fetched_at);
|
|
49
|
+
if (!Number.isFinite(t))
|
|
50
|
+
return false;
|
|
51
|
+
return (Date.now() - t) < CACHE_TTL_MS;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pull the latest blacklist from the hub if needed, write it to the
|
|
55
|
+
* cache, return whatever we ended up with. Network errors are swallowed
|
|
56
|
+
* — the previous cache is returned instead so install never breaks
|
|
57
|
+
* because of a transient hub outage.
|
|
58
|
+
*/
|
|
59
|
+
export async function refreshBlacklist(opts = {}) {
|
|
60
|
+
const cached = readBlacklistCache();
|
|
61
|
+
if (cached && isFresh(cached) && !opts.force)
|
|
62
|
+
return cached;
|
|
63
|
+
const hubBase = (opts.hubBase ?? process.env.YOME_HUB_BASE ?? DEFAULT_HUB_BASE).replace(/\/+$/, '');
|
|
64
|
+
const fetcher = opts.fetcher ?? fetch;
|
|
65
|
+
try {
|
|
66
|
+
const resp = await fetcher(`${hubBase}/api/hub/blacklist`, { headers: { Accept: 'application/json' } });
|
|
67
|
+
if (!resp.ok)
|
|
68
|
+
return cached ?? { fetched_at: new Date().toISOString(), entries: [] };
|
|
69
|
+
const j = (await resp.json());
|
|
70
|
+
if (!j || j.ok !== true || !Array.isArray(j.entries)) {
|
|
71
|
+
return cached ?? { fetched_at: new Date().toISOString(), entries: [] };
|
|
72
|
+
}
|
|
73
|
+
const fresh = {
|
|
74
|
+
fetched_at: new Date().toISOString(),
|
|
75
|
+
entries: j.entries.map((e) => ({
|
|
76
|
+
slug: e.slug,
|
|
77
|
+
reason: e.reason,
|
|
78
|
+
detail: e.detail ?? null,
|
|
79
|
+
added_at: e.added_at,
|
|
80
|
+
})),
|
|
81
|
+
};
|
|
82
|
+
writeBlacklistCache(fresh);
|
|
83
|
+
return fresh;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return cached ?? { fetched_at: new Date().toISOString(), entries: [] };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Sync lookup against the cache only. Returns the entry or null. */
|
|
90
|
+
export function isBlacklistedSync(slug) {
|
|
91
|
+
const c = readBlacklistCache();
|
|
92
|
+
if (!c)
|
|
93
|
+
return null;
|
|
94
|
+
return c.entries.find((e) => e.slug === slug) ?? null;
|
|
95
|
+
}
|
|
96
|
+
/** Fetch + check. Use during install. */
|
|
97
|
+
export async function pingBlacklist(slug, opts = {}) {
|
|
98
|
+
const c = await refreshBlacklist(opts);
|
|
99
|
+
return c.entries.find((e) => e.slug === slug) ?? null;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=blacklist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blacklist.js","sourceRoot":"","sources":["../../src/yomeSkills/blacklist.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export declare const KNOWN_CAPABILITIES: readonly ["fs:read", "fs:write", "fs:delete", "applescript", "network", "shell"];
|
|
2
|
+
export type Capability = (typeof KNOWN_CAPABILITIES)[number];
|
|
3
|
+
export interface AllowedCapabilities {
|
|
4
|
+
/** Schema version for forward compatibility. */
|
|
5
|
+
version: 1;
|
|
6
|
+
/** ISO-8601 timestamp when the user (or auto-grant) decided. */
|
|
7
|
+
granted_at: string;
|
|
8
|
+
/** Subset of the manifest's declared capabilities the user said yes to. */
|
|
9
|
+
allowed: Capability[];
|
|
10
|
+
/** "user" / "auto-official" / "yes-flag". */
|
|
11
|
+
granted_by: 'user' | 'auto-official' | 'yes-flag';
|
|
12
|
+
}
|
|
13
|
+
export declare function describeCapability(c: Capability): string;
|
|
14
|
+
/** Strip duplicates and unknown entries from a manifest's `capabilities`. */
|
|
15
|
+
export declare function normaliseDeclared(declared: readonly string[] | undefined): Capability[];
|
|
16
|
+
export declare function isOfficialSlug(slug: string): boolean;
|
|
17
|
+
export declare function readAllowedCapabilities(slug: string): AllowedCapabilities | null;
|
|
18
|
+
export declare function writeAllowedCapabilities(slug: string, ac: AllowedCapabilities): void;
|
|
19
|
+
/**
|
|
20
|
+
* Decide which capabilities to grant during a fresh install.
|
|
21
|
+
*
|
|
22
|
+
* Rules:
|
|
23
|
+
* - Empty declared → grant nothing (no prompt).
|
|
24
|
+
* - Official slug → auto-grant everything declared.
|
|
25
|
+
* - `assumeYes` (--yes) → grant everything declared, mark "yes-flag".
|
|
26
|
+
* - Otherwise → ask the user (TTY).
|
|
27
|
+
* - In non-TTY, default to **deny** (return ok=false, reason).
|
|
28
|
+
*/
|
|
29
|
+
export interface GrantOptions {
|
|
30
|
+
/** Bypass the prompt and grant everything (CI / -y). */
|
|
31
|
+
assumeYes?: boolean;
|
|
32
|
+
/** Test seam: replace the prompt with a deterministic answer. */
|
|
33
|
+
prompt?: (declared: readonly Capability[], slug: string) => Promise<boolean>;
|
|
34
|
+
/** Test seam: control TTY detection. */
|
|
35
|
+
isTty?: boolean;
|
|
36
|
+
/** Override clock for tests. */
|
|
37
|
+
now?: () => Date;
|
|
38
|
+
}
|
|
39
|
+
export interface GrantResult {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
granted?: AllowedCapabilities;
|
|
42
|
+
/** Set when ok=false (user declined / non-TTY). */
|
|
43
|
+
reason?: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function decideCapabilities(slug: string, declared: readonly string[] | undefined, opts?: GrantOptions): Promise<GrantResult>;
|
|
46
|
+
/** True iff the skill is currently allowed to use `cap`. */
|
|
47
|
+
export declare function isCapabilityAllowed(slug: string, cap: Capability): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Revoke a single capability from an installed skill.
|
|
50
|
+
* Returns true if the file was modified.
|
|
51
|
+
*/
|
|
52
|
+
export declare function revokeCapability(slug: string, cap: Capability, now?: () => Date): boolean;
|
|
53
|
+
/** Re-grant a previously revoked capability (must be in the manifest's declared list to take effect). */
|
|
54
|
+
export declare function grantCapability(slug: string, cap: Capability, now?: () => Date): boolean;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// capabilities.ts — declared / granted *system* capabilities for installed
|
|
2
|
+
// skills.
|
|
3
|
+
//
|
|
4
|
+
// Note on naming: yome-skill.json has had a `capabilities` field since
|
|
5
|
+
// schema v1, but it's used for *semantic* labels ("calendar:read",
|
|
6
|
+
// "ppt:write") that drive the marketplace UI. M9 introduces a separate
|
|
7
|
+
// `system_capabilities` field for the security model — the OS-level
|
|
8
|
+
// resources a skill needs (fs:read, network, applescript, …). At install
|
|
9
|
+
// time we show the user the system_capabilities list and ask "Allow?".
|
|
10
|
+
// The decision is persisted to <skillDir>/allowed-capabilities.json. The
|
|
11
|
+
// agent runtime later refuses any command that exercises a system
|
|
12
|
+
// capability that isn't allowed.
|
|
13
|
+
//
|
|
14
|
+
// "Official" first-party skills (slug starts with "@yome/") get an
|
|
15
|
+
// auto-allow grant — we trust ourselves and don't want to badger the
|
|
16
|
+
// user during the bundled install path. Third-party skills always
|
|
17
|
+
// prompt unless the user passes `--yes`.
|
|
18
|
+
//
|
|
19
|
+
// The system-capability vocabulary is deliberately small + frozen for
|
|
20
|
+
// v0.1. Anything not in KNOWN_CAPABILITIES is silently dropped from a
|
|
21
|
+
// manifest (so the prompt only ever shows things we actually enforce).
|
|
22
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
import { createInterface } from 'node:readline';
|
|
25
|
+
import { installPathForSlug } from './paths.js';
|
|
26
|
+
export const KNOWN_CAPABILITIES = [
|
|
27
|
+
'fs:read',
|
|
28
|
+
'fs:write',
|
|
29
|
+
'fs:delete',
|
|
30
|
+
'applescript',
|
|
31
|
+
'network',
|
|
32
|
+
'shell',
|
|
33
|
+
];
|
|
34
|
+
const HUMAN_DESCRIPTIONS = {
|
|
35
|
+
'fs:read': 'read files in your home / working directory',
|
|
36
|
+
'fs:write': 'create or modify files in your home / working directory',
|
|
37
|
+
'fs:delete': 'delete files in your home / working directory',
|
|
38
|
+
applescript: 'drive macOS applications via Apple Events (PowerPoint, Calendar, Reminders, …)',
|
|
39
|
+
network: 'make outbound HTTP/HTTPS requests',
|
|
40
|
+
shell: 'run arbitrary shell commands',
|
|
41
|
+
};
|
|
42
|
+
export function describeCapability(c) {
|
|
43
|
+
return HUMAN_DESCRIPTIONS[c] ?? c;
|
|
44
|
+
}
|
|
45
|
+
/** Strip duplicates and unknown entries from a manifest's `capabilities`. */
|
|
46
|
+
export function normaliseDeclared(declared) {
|
|
47
|
+
if (!declared)
|
|
48
|
+
return [];
|
|
49
|
+
const seen = new Set();
|
|
50
|
+
for (const c of declared) {
|
|
51
|
+
if (KNOWN_CAPABILITIES.includes(c)) {
|
|
52
|
+
seen.add(c);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return [...seen].sort();
|
|
56
|
+
}
|
|
57
|
+
export function isOfficialSlug(slug) {
|
|
58
|
+
return slug.startsWith('@yome/');
|
|
59
|
+
}
|
|
60
|
+
function allowedFilePath(slug) {
|
|
61
|
+
const dir = installPathForSlug(slug);
|
|
62
|
+
return dir ? join(dir, 'allowed-capabilities.json') : null;
|
|
63
|
+
}
|
|
64
|
+
export function readAllowedCapabilities(slug) {
|
|
65
|
+
const f = allowedFilePath(slug);
|
|
66
|
+
if (!f || !existsSync(f))
|
|
67
|
+
return null;
|
|
68
|
+
try {
|
|
69
|
+
const obj = JSON.parse(readFileSync(f, 'utf-8'));
|
|
70
|
+
if (obj && typeof obj === 'object' && Array.isArray(obj.allowed)) {
|
|
71
|
+
return obj;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch { /* ignored */ }
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
export function writeAllowedCapabilities(slug, ac) {
|
|
78
|
+
const f = allowedFilePath(slug);
|
|
79
|
+
if (!f)
|
|
80
|
+
throw new Error(`cannot derive install dir for ${slug}`);
|
|
81
|
+
writeFileSync(f, JSON.stringify(ac, null, 2) + '\n');
|
|
82
|
+
}
|
|
83
|
+
export async function decideCapabilities(slug, declared, opts = {}) {
|
|
84
|
+
const norm = normaliseDeclared(declared);
|
|
85
|
+
const now = (opts.now ?? (() => new Date()))().toISOString();
|
|
86
|
+
if (norm.length === 0) {
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
89
|
+
granted: { version: 1, allowed: [], granted_at: now, granted_by: 'auto-official' },
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (isOfficialSlug(slug)) {
|
|
93
|
+
return {
|
|
94
|
+
ok: true,
|
|
95
|
+
granted: { version: 1, allowed: norm, granted_at: now, granted_by: 'auto-official' },
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (opts.assumeYes) {
|
|
99
|
+
return {
|
|
100
|
+
ok: true,
|
|
101
|
+
granted: { version: 1, allowed: norm, granted_at: now, granted_by: 'yes-flag' },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Need a yes/no decision from the human.
|
|
105
|
+
const answer = opts.prompt
|
|
106
|
+
? await opts.prompt(norm, slug)
|
|
107
|
+
: await defaultPrompt(norm, slug, opts.isTty);
|
|
108
|
+
if (!answer) {
|
|
109
|
+
return { ok: false, reason: 'user declined the capability grant' };
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
ok: true,
|
|
113
|
+
granted: { version: 1, allowed: norm, granted_at: now, granted_by: 'user' },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async function defaultPrompt(declared, slug, isTtyOverride) {
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
118
|
+
const stdinTty = (typeof process.stdin === 'object' && process.stdin.isTTY) === true;
|
|
119
|
+
const isTty = isTtyOverride ?? stdinTty;
|
|
120
|
+
if (!isTty) {
|
|
121
|
+
process.stderr.write(`! ${slug} requires capabilities (${declared.join(', ')}). ` +
|
|
122
|
+
`Re-run with --yes to allow non-interactively, or in a TTY.\n`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
process.stdout.write(`\n${slug} requests the following capabilities:\n`);
|
|
126
|
+
for (const c of declared) {
|
|
127
|
+
process.stdout.write(` - ${c}: ${describeCapability(c)}\n`);
|
|
128
|
+
}
|
|
129
|
+
process.stdout.write('Allow? [y/N]: ');
|
|
130
|
+
return await new Promise((resolve) => {
|
|
131
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: false });
|
|
132
|
+
rl.once('line', (line) => {
|
|
133
|
+
rl.close();
|
|
134
|
+
resolve(/^y(es)?$/i.test(line.trim()));
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/** True iff the skill is currently allowed to use `cap`. */
|
|
139
|
+
export function isCapabilityAllowed(slug, cap) {
|
|
140
|
+
const ac = readAllowedCapabilities(slug);
|
|
141
|
+
if (!ac)
|
|
142
|
+
return false;
|
|
143
|
+
return ac.allowed.includes(cap);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Revoke a single capability from an installed skill.
|
|
147
|
+
* Returns true if the file was modified.
|
|
148
|
+
*/
|
|
149
|
+
export function revokeCapability(slug, cap, now = () => new Date()) {
|
|
150
|
+
const ac = readAllowedCapabilities(slug);
|
|
151
|
+
if (!ac)
|
|
152
|
+
return false;
|
|
153
|
+
const before = ac.allowed.length;
|
|
154
|
+
ac.allowed = ac.allowed.filter((x) => x !== cap);
|
|
155
|
+
if (ac.allowed.length === before)
|
|
156
|
+
return false;
|
|
157
|
+
ac.granted_at = now().toISOString();
|
|
158
|
+
ac.granted_by = 'user';
|
|
159
|
+
writeAllowedCapabilities(slug, ac);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
/** Re-grant a previously revoked capability (must be in the manifest's declared list to take effect). */
|
|
163
|
+
export function grantCapability(slug, cap, now = () => new Date()) {
|
|
164
|
+
const ac = readAllowedCapabilities(slug) ?? {
|
|
165
|
+
version: 1, allowed: [], granted_at: now().toISOString(), granted_by: 'user',
|
|
166
|
+
};
|
|
167
|
+
if (ac.allowed.includes(cap))
|
|
168
|
+
return false;
|
|
169
|
+
ac.allowed = [...ac.allowed, cap].sort();
|
|
170
|
+
ac.granted_at = now().toISOString();
|
|
171
|
+
ac.granted_by = 'user';
|
|
172
|
+
writeAllowedCapabilities(slug, ac);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/yomeSkills/capabilities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Capability } from './capabilities.js';
|
|
2
|
+
export interface CapDecision {
|
|
3
|
+
allowed: boolean;
|
|
4
|
+
/** Human-readable reason on denial (suitable for printing to the user). */
|
|
5
|
+
reason?: string;
|
|
6
|
+
/** Where the decision came from — useful in logs. */
|
|
7
|
+
source: 'sidecar' | 'official-no-sidecar' | 'unknown-skill' | 'unknown-cap';
|
|
8
|
+
}
|
|
9
|
+
/** Snapshot of denials since process start. Used by `yome skill doctor` v2. */
|
|
10
|
+
export declare function getDenialCounts(): Array<{
|
|
11
|
+
slug: string;
|
|
12
|
+
capability: string;
|
|
13
|
+
count: number;
|
|
14
|
+
}>;
|
|
15
|
+
/** Reset counters (test seam). */
|
|
16
|
+
export declare function resetDenialCounts(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Synchronous capability check — safe to call from inside a tool dispatch
|
|
19
|
+
* hot path. Reads two small files (manifest + allowed-cap sidecar) but
|
|
20
|
+
* does not block on network or DB.
|
|
21
|
+
*
|
|
22
|
+
* @returns Decision with `allowed: true` ⇒ proceed; otherwise deny and
|
|
23
|
+
* surface `reason` to the LLM so it can pick a different action.
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkCapability(slug: string, cap: Capability | string): CapDecision;
|
|
26
|
+
/**
|
|
27
|
+
* Throwing variant — convenience for dispatchers that prefer exceptions
|
|
28
|
+
* over decision objects. Errors are tagged so callers can render them
|
|
29
|
+
* cleanly to the LLM.
|
|
30
|
+
*/
|
|
31
|
+
export declare function assertCapability(slug: string, cap: Capability | string): void;
|