@deepseek-harness-tui/dsh-tui 0.8.1 → 0.8.2
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 +22 -13
- package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
- package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
- package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
- package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
- package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
- package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
- package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
- package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
- package/dsh-ecosystem-spec/registry/README.md +14 -0
- package/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
- package/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
- package/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
- package/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
- package/{ecosystem-spec → dsh-ecosystem-spec}/registry/permissions-0.1.json +1 -1
- package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
- package/lib/types/command-trees.d.ts +2 -1
- package/lib/types/command-trees.d.ts.map +1 -1
- package/lib/types/command-trees.js +1 -1
- package/lib/types/components/LoadedContextPanel.d.ts +10 -5
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +18 -7
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +16 -7
- package/lib/types/components/Markdown.d.ts +13 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +15 -1
- package/lib/types/components/MessageList.d.ts +3 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +90 -9
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +26 -12
- package/lib/types/components/StreamingMarkdown.d.ts +2 -8
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +51 -9
- package/lib/types/components/activityFrames.d.ts +1 -1
- package/lib/types/components/activityFrames.d.ts.map +1 -1
- package/lib/types/components/activityFrames.js +4 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +5 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +26 -3
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +12 -1
- package/lib/types/dsh-adapter/channel.d.ts +31 -4
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +267 -102
- package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
- package/lib/types/dsh-adapter/childStderr.js +10 -0
- package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -1
- package/lib/types/dsh-adapter/command-attribution.js +2 -2
- package/lib/types/dsh-adapter/command-trees.d.ts +7 -1
- package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -1
- package/lib/types/dsh-adapter/command-trees.js +70 -20
- package/lib/types/dsh-adapter/component-identity.d.ts +5 -0
- package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -1
- package/lib/types/dsh-adapter/component-identity.js +14 -14
- package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -1
- package/lib/types/dsh-adapter/decision-guard.js +10 -16
- package/lib/types/dsh-adapter/dialogs.d.ts +2 -3
- package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -1
- package/lib/types/dsh-adapter/dialogs.js +114 -60
- package/lib/types/dsh-adapter/effect-ledger.d.ts +0 -8
- package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
- package/lib/types/dsh-adapter/effect-ledger.js +73 -53
- package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/extension-events.js +39 -1
- package/lib/types/dsh-adapter/grants.d.ts.map +1 -1
- package/lib/types/dsh-adapter/grants.js +43 -14
- package/lib/types/dsh-adapter/host-access.d.ts +37 -0
- package/lib/types/dsh-adapter/host-access.d.ts.map +1 -0
- package/lib/types/dsh-adapter/host-access.js +652 -0
- package/lib/types/dsh-adapter/host-descriptor.d.ts +2 -2
- package/lib/types/dsh-adapter/host-descriptor.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-descriptor.js +31 -8
- package/lib/types/dsh-adapter/index.d.ts +4 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +1 -0
- package/lib/types/dsh-adapter/message-observer.d.ts +10 -15
- package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -1
- package/lib/types/dsh-adapter/message-observer.js +114 -70
- package/lib/types/dsh-adapter/plugin-host.d.ts +39 -6
- package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-host.js +139 -44
- package/lib/types/dsh-adapter/plugin-storage.d.ts +0 -5
- package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-storage.js +48 -36
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +80 -16
- package/lib/types/dsh-adapter/promptDebug.d.ts +0 -6
- package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
- package/lib/types/dsh-adapter/promptDebug.js +13 -4
- package/lib/types/dsh-adapter/renderers.d.ts +6 -14
- package/lib/types/dsh-adapter/renderers.d.ts.map +1 -1
- package/lib/types/dsh-adapter/renderers.js +81 -46
- package/lib/types/dsh-adapter/scenes.d.ts +8 -4
- package/lib/types/dsh-adapter/scenes.d.ts.map +1 -1
- package/lib/types/dsh-adapter/scenes.js +136 -37
- package/lib/types/dsh-adapter/settings-sections.d.ts +7 -3
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.js +106 -26
- package/lib/types/dsh-adapter/shortcuts.d.ts +8 -10
- package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -1
- package/lib/types/dsh-adapter/shortcuts.js +108 -36
- package/lib/types/dsh-adapter/status.d.ts +6 -7
- package/lib/types/dsh-adapter/status.d.ts.map +1 -1
- package/lib/types/dsh-adapter/status.js +88 -22
- package/lib/types/dsh-adapter/workspaces.d.ts +16 -5
- package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
- package/lib/types/dsh-adapter/workspaces.js +287 -170
- package/lib/types/extensions.d.ts +10 -5
- package/lib/types/extensions.d.ts.map +1 -1
- package/lib/types/extensions.js +11 -5
- package/lib/types/force-production-react.d.ts +1 -0
- package/lib/types/force-production-react.d.ts.map +1 -0
- package/lib/types/force-production-react.js +12 -0
- package/lib/types/i18n.d.ts +14 -6
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +4 -2
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +1 -0
- package/lib/types/ink/components/App.d.ts +2 -0
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +4 -0
- package/lib/types/ink/ink.d.ts +20 -3
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +57 -5
- package/lib/types/ink/log-update.d.ts.map +1 -1
- package/lib/types/ink/log-update.js +128 -9
- package/lib/types/ink/output.d.ts +28 -2
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +158 -12
- package/lib/types/ink/screen.d.ts +39 -0
- package/lib/types/ink/screen.d.ts.map +1 -1
- package/lib/types/ink/screen.js +117 -0
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +10 -2
- package/lib/types/ink/wrapAnsi.d.ts +1 -1
- package/lib/types/ink/wrapAnsi.d.ts.map +1 -1
- package/lib/types/ink/wrapAnsi.js +29 -2
- package/lib/types/plugin-host.d.ts +13 -11
- package/lib/types/plugin-host.d.ts.map +1 -1
- package/lib/types/plugin-host.js +16 -16
- package/lib/types/plugin-spec/registry.d.ts +2 -2
- package/lib/types/plugin-spec/registry.d.ts.map +1 -1
- package/lib/types/plugin-spec/registry.js +113 -18
- package/lib/types/plugin-spec/tui-extension.d.ts.map +1 -1
- package/lib/types/plugin-spec/tui-extension.js +2 -0
- package/lib/types/plugin-spec/validate.d.ts.map +1 -1
- package/lib/types/plugin-spec/validate.js +20 -1
- package/lib/types/scenes.d.ts +2 -1
- package/lib/types/scenes.d.ts.map +1 -1
- package/lib/types/scenes.js +1 -1
- package/lib/types/screens/Chat.d.ts +3 -3
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +38 -17
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +10 -3
- package/lib/types/sessions/view.d.ts +3 -2
- package/lib/types/sessions/view.d.ts.map +1 -1
- package/lib/types/sessions/view.js +18 -0
- package/lib/types/settings-sections.d.ts +2 -1
- package/lib/types/settings-sections.d.ts.map +1 -1
- package/lib/types/settings-sections.js +1 -1
- package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
- package/lib/types/utils/execFileNoThrow.js +3 -0
- package/lib/types/workspaces.d.ts +2 -1
- package/lib/types/workspaces.d.ts.map +1 -1
- package/lib/types/workspaces.js +1 -1
- package/node_modules/@dsh-std/command/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/command/package.json +1 -1
- package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
- package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
- package/node_modules/@dsh-std/connection/lib/memory.js +1 -0
- package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/node_modules/@dsh-std/connection/package.json +1 -1
- package/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/core/package.json +1 -1
- package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
- package/node_modules/@dsh-std/manifest/package.json +1 -1
- package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/messages/package.json +1 -1
- package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
- package/node_modules/@dsh-std/presentation/README.md +2 -1
- package/node_modules/@dsh-std/presentation/README.zh.md +2 -1
- package/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
- package/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
- package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
- package/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
- package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
- package/node_modules/@dsh-std/presentation/lib/index.js +6 -3
- package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/presentation/package.json +7 -3
- package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/storage/package.json +1 -1
- package/package.json +10 -5
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +2 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +2 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +6 -3
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +7 -3
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +1 -1
- package/ecosystem-spec/registry/registry-0.15.json +0 -57
- /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/contracts/decision-events-v1alpha1.json +0 -0
- /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/host-descriptor.tui.example.json +0 -0
- /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/conformance-claim.schema.json +0 -0
- /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/effect-ledger-record.schema.json +0 -0
- /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/host-descriptor.schema.json +0 -0
|
@@ -41,6 +41,7 @@ import { DATA_DIR } from '../utils/paths.js';
|
|
|
41
41
|
import { loadSpecData } from '../plugin-spec/registry.js';
|
|
42
42
|
import { check } from '../plugin-spec/schema-check.js';
|
|
43
43
|
import { componentIdentityOf } from './component-identity.js';
|
|
44
|
+
import { compositionRoot, concreteService } from './host-access.js';
|
|
44
45
|
/** Default ledger file (JSONL, one record per line). */
|
|
45
46
|
export const EFFECT_LEDGER_FILE = join(DATA_DIR, 'effect-ledger.jsonl');
|
|
46
47
|
/** Resource kinds recorded by the host's wired emitters. */
|
|
@@ -65,26 +66,29 @@ function cleanField(value, max, fallback) {
|
|
|
65
66
|
}
|
|
66
67
|
/** `ctx.tuiEffectLedger` — append-only effect journal (C-060). */
|
|
67
68
|
export class TuiEffectLedgerRuntime extends Service {
|
|
68
|
-
file;
|
|
69
|
-
optionsGenerationId;
|
|
70
|
-
generationId;
|
|
71
|
-
ledgerSchema;
|
|
72
|
-
activations = new WeakMap();
|
|
73
|
-
nextActivation = 1;
|
|
74
|
-
sequence;
|
|
75
|
-
schemaWarned = false;
|
|
76
69
|
constructor(ctx, options = {}) {
|
|
77
70
|
super(ctx, 'tuiEffectLedger');
|
|
78
|
-
|
|
71
|
+
const file = options.file ?? EFFECT_LEDGER_FILE;
|
|
79
72
|
// The generation is resolved LAZILY (per first record): cordis does not
|
|
80
73
|
// make sibling services visible to constructors of plugins mounted later
|
|
81
74
|
// by the same apply(), so a constructor-time probe would always miss the
|
|
82
75
|
// plugin-host service and fall back to 'unknown-generation'.
|
|
83
|
-
|
|
76
|
+
const state = {
|
|
77
|
+
hostContext: compositionRoot(ctx),
|
|
78
|
+
file,
|
|
79
|
+
optionsGenerationId: options.generationId,
|
|
80
|
+
generationId: undefined,
|
|
81
|
+
activations: new WeakMap(),
|
|
82
|
+
nextActivation: 1,
|
|
83
|
+
sequence: 0,
|
|
84
|
+
schemaWarned: false,
|
|
85
|
+
ledgerSchema: undefined,
|
|
86
|
+
};
|
|
84
87
|
// `'ledgerSchema' in options` lets a caller force-undefined (fail-closed
|
|
85
88
|
// test seam), same contract as the message observer's envelopeSchema.
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
state.ledgerSchema = 'ledgerSchema' in options ? options.ledgerSchema : loadSpecData()?.schemas.ledger;
|
|
90
|
+
state.sequence = resumeSequence(file);
|
|
91
|
+
ledgerStates.set(this, state);
|
|
88
92
|
}
|
|
89
93
|
/**
|
|
90
94
|
* Append one record. `identity` is the caller's own context (plugins pass
|
|
@@ -92,11 +96,13 @@ export class TuiEffectLedgerRuntime extends Service {
|
|
|
92
96
|
* parameter); omitting it records `undeclared`, never a guess.
|
|
93
97
|
*/
|
|
94
98
|
record(entry, identity) {
|
|
99
|
+
let state;
|
|
95
100
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
state = ledgerStateFor(this);
|
|
102
|
+
if (state.ledgerSchema === undefined) {
|
|
103
|
+
if (!state.schemaWarned) {
|
|
104
|
+
state.schemaWarned = true;
|
|
105
|
+
state.hostContext.logger.warn('dsh-tui: effect ledger schema unavailable — suppressing all ledger writes (fail-closed)');
|
|
100
106
|
}
|
|
101
107
|
return;
|
|
102
108
|
}
|
|
@@ -105,7 +111,7 @@ export class TuiEffectLedgerRuntime extends Service {
|
|
|
105
111
|
const pluginId = this.pluginIdOf(identity, fiber, verified?.componentId);
|
|
106
112
|
const record = {
|
|
107
113
|
ledgerVersion: '0.15',
|
|
108
|
-
sequence:
|
|
114
|
+
sequence: state.sequence,
|
|
109
115
|
timestamp: new Date().toISOString(),
|
|
110
116
|
pluginId,
|
|
111
117
|
activationInstance: verified?.activationId ?? this.activationOf(fiber, pluginId),
|
|
@@ -135,54 +141,34 @@ export class TuiEffectLedgerRuntime extends Service {
|
|
|
135
141
|
// schema is DROPPED, not written (the schema's additionalProperties:
|
|
136
142
|
// false is the structural secret ban).
|
|
137
143
|
try {
|
|
138
|
-
check(record,
|
|
144
|
+
check(record, state.ledgerSchema, state.ledgerSchema);
|
|
139
145
|
}
|
|
140
146
|
catch (error) {
|
|
141
|
-
|
|
147
|
+
state.hostContext.logger.warn(`dsh-tui: effect ledger record dropped (schema: ${error instanceof Error ? error.message : String(error)})`);
|
|
142
148
|
return;
|
|
143
149
|
}
|
|
144
|
-
mkdirSync(dirname(
|
|
145
|
-
appendFileSync(
|
|
146
|
-
|
|
150
|
+
mkdirSync(dirname(state.file), { recursive: true, mode: 0o700 });
|
|
151
|
+
appendFileSync(state.file, `${JSON.stringify(record)}\n`, { mode: 0o600 });
|
|
152
|
+
state.sequence += 1;
|
|
147
153
|
}
|
|
148
154
|
catch (error) {
|
|
149
|
-
|
|
155
|
+
;
|
|
156
|
+
(state?.hostContext ?? this.ctx).logger.warn('dsh-tui: effect ledger write failed');
|
|
150
157
|
}
|
|
151
158
|
}
|
|
152
159
|
/** Runtime generation (C-050): options override, else the plugin-host
|
|
153
160
|
* service's id — resolved on first record and cached (the host row mounts
|
|
154
161
|
* before any caller can record). */
|
|
155
162
|
generation() {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
const state = ledgerStateFor(this);
|
|
164
|
+
if (state.optionsGenerationId !== undefined)
|
|
165
|
+
return state.optionsGenerationId;
|
|
166
|
+
state.generationId ??= state.hostContext.get('tuiPluginHost')?.generationId ?? 'unknown-generation';
|
|
167
|
+
return state.generationId;
|
|
160
168
|
}
|
|
161
169
|
/** Continue numbering after the existing file's max sequence (restart-safe). */
|
|
162
170
|
resumeSequence() {
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
text = readFileSync(this.file, 'utf8');
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
return 0; // missing file = fresh ledger, not corruption
|
|
169
|
-
}
|
|
170
|
-
let max = -1;
|
|
171
|
-
for (const line of text.split('\n')) {
|
|
172
|
-
if (line.trim() === '')
|
|
173
|
-
continue;
|
|
174
|
-
try {
|
|
175
|
-
const parsed = JSON.parse(line);
|
|
176
|
-
const sequence = parsed.sequence;
|
|
177
|
-
if (typeof sequence === 'number' && Number.isInteger(sequence) && sequence > max)
|
|
178
|
-
max = sequence;
|
|
179
|
-
}
|
|
180
|
-
catch {
|
|
181
|
-
// Corrupt line: skip it (never rewritten — the bytes stay for manual
|
|
182
|
-
// recovery, same posture as plugin-storage).
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return max + 1;
|
|
171
|
+
return resumeSequence(ledgerStateFor(this).file);
|
|
186
172
|
}
|
|
187
173
|
fiberOf(identity) {
|
|
188
174
|
if (identity === undefined)
|
|
@@ -220,12 +206,46 @@ export class TuiEffectLedgerRuntime extends Service {
|
|
|
220
206
|
// is exact; per-fiber ids only matter for plugin fibers.
|
|
221
207
|
if (fiber === undefined || pluginId === 'host' || pluginId === 'undeclared')
|
|
222
208
|
return pluginId;
|
|
223
|
-
|
|
209
|
+
const state = ledgerStateFor(this);
|
|
210
|
+
let activation = state.activations.get(fiber);
|
|
224
211
|
if (activation === undefined) {
|
|
225
|
-
activation = `activation-${
|
|
226
|
-
|
|
212
|
+
activation = `activation-${state.nextActivation++}`;
|
|
213
|
+
state.activations.set(fiber, activation);
|
|
227
214
|
}
|
|
228
215
|
return activation;
|
|
229
216
|
}
|
|
230
217
|
}
|
|
218
|
+
const ledgerStates = new WeakMap();
|
|
219
|
+
function ledgerStateFor(runtime) {
|
|
220
|
+
const state = ledgerStates.get(concreteService(runtime));
|
|
221
|
+
if (state === undefined)
|
|
222
|
+
throw new Error('tuiEffectLedger host state is unavailable');
|
|
223
|
+
return state;
|
|
224
|
+
}
|
|
225
|
+
/** Continue numbering after the existing file's max sequence (restart-safe). */
|
|
226
|
+
function resumeSequence(file) {
|
|
227
|
+
let text;
|
|
228
|
+
try {
|
|
229
|
+
text = readFileSync(file, 'utf8');
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return 0; // missing file = fresh ledger, not corruption
|
|
233
|
+
}
|
|
234
|
+
let max = -1;
|
|
235
|
+
for (const line of text.split('\n')) {
|
|
236
|
+
if (line.trim() === '')
|
|
237
|
+
continue;
|
|
238
|
+
try {
|
|
239
|
+
const parsed = JSON.parse(line);
|
|
240
|
+
const sequence = parsed.sequence;
|
|
241
|
+
if (typeof sequence === 'number' && Number.isInteger(sequence) && sequence > max)
|
|
242
|
+
max = sequence;
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// Corrupt line: skip it (never rewritten — the bytes stay for manual
|
|
246
|
+
// recovery, same posture as plugin-storage).
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return max + 1;
|
|
250
|
+
}
|
|
231
251
|
export default TuiEffectLedgerRuntime;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension-events.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/extension-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAYlD;;wEAEwE;AACxE,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAA;AA4C7C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,CAAC,GAAG,SAAS,GAC1E,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CA4DxB;AAED;uEACuE;AACvE,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"extension-events.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/extension-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAYlD;;wEAEwE;AACxE,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAA;AA4C7C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,CAAC,GAAG,SAAS,GAC1E,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CA4DxB;AAED;uEACuE;AACvE,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAwCf;AAED;;;kDAGkD;AAClD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC3B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAQ/C;AAED,mDAAmD;AACnD,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAA;IACjB,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAc,SAAQ,kBAAkB;IACvD,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,2EAA2E;IAC3E,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAA;CAC/B;AAED,MAAM,MAAM,gBAAgB;AAC1B,6DAA6D;AAC3D;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;AAClB;+EAC+E;GAC7E;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACpC,kEAAkE;GAChE;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACnC,gDAAgD;GAC9C,SAAS,CAAA;AAEb;;;;GAIG;AACH,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAA;IACV,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,MAAM,uBAAuB;AACjC,2EAA2E;AACzE;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACnC;0DAC0D;GACxD;IAAE,KAAK,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE;AACrC,oEAAoE;GAClE,SAAS,CAAA;AAEb;;;;GAIG;AACH,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAC5D,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAA;IACZ;oCACgC;IAChC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAA;IACnB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAA;IACvB,2DAA2D;IAC3D,cAAc,EAAE,MAAM,CAAA;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,MAAM,wBAAwB;AAClC;4BAC4B;AAC1B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,SAAS,CAAA;AAEb;;;;;GAKG;AACH,MAAM,WAAW,uBAAwB,SAAQ,kBAAkB;IACjE,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACjC,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,2CAA2C;AAC3C,MAAM,WAAW,eAAgB,SAAQ,kBAAkB;CAAG;AAE9D,MAAM,MAAM,kBAAkB;AAC5B,qEAAqE;AACnE;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,SAAS,CAAA;AAEb,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAC/E,mBAAmB,CAAC,KAAK,EAAE,oBAAoB,GAAG,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;QAC5G,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;QAC9F,oBAAoB,CAAC,KAAK,EAAE,qBAAqB,GAAG,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAA;QAChH,sBAAsB,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QAC5E,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACxF;CACF"}
|
|
@@ -151,7 +151,45 @@ export async function dispatchTuiDecision(ctx, name, payload, normalize) {
|
|
|
151
151
|
/** Fire a non-veto DecisionEvents point with the same bounded, isolated
|
|
152
152
|
* semantics. Notifications deliberately ignore every return value. */
|
|
153
153
|
export async function dispatchTuiNotification(ctx, name, payload) {
|
|
154
|
-
|
|
154
|
+
const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId : undefined;
|
|
155
|
+
const scope = sessionId === undefined ? undefined : `session:${sessionId}`;
|
|
156
|
+
const listeners = decisionHandlersOf(ctx, name, scope);
|
|
157
|
+
const log = (message, error) => {
|
|
158
|
+
try {
|
|
159
|
+
if (error === undefined)
|
|
160
|
+
ctx.logger.warn(message);
|
|
161
|
+
else
|
|
162
|
+
ctx.logger.warn(message, error);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Degraded ctx without a logger: warnings are best-effort.
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const started = Date.now();
|
|
169
|
+
// Notifications have no return-value or ordering semantics. Start every
|
|
170
|
+
// listener from the same snapshot so one slow plugin cannot delay another
|
|
171
|
+
// plugin's session rebind (or make a fire-and-forget dispatch look stuck).
|
|
172
|
+
await Promise.all(listeners.map(async (handler) => {
|
|
173
|
+
const permission = DECISION_EVENT_PERMISSIONS[name];
|
|
174
|
+
const principal = { componentId: handler.componentId, activationId: handler.activationId };
|
|
175
|
+
if (permission !== undefined
|
|
176
|
+
&& !decisionRegistryOf(ctx).grants.allows(principal, permission, scope ?? handler.scope)) {
|
|
177
|
+
log(`dsh-tui: ${name} handler from Component "${handler.componentId}" skipped after grant revocation`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const remaining = DECISION_TOTAL_TIMEOUT_MS - (Date.now() - started);
|
|
181
|
+
if (remaining <= 0) {
|
|
182
|
+
log(`dsh-tui: ${name} total decision budget exceeded; handler skipped`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const bounded = await runBounded(() => handler.listener(freezeClone(payload)), Math.min(DECISION_HANDLER_TIMEOUT_MS, remaining));
|
|
186
|
+
if (bounded.kind === 'timeout') {
|
|
187
|
+
log(`dsh-tui: ${name} handler from Component "${handler.componentId}" exceeded ${DECISION_HANDLER_TIMEOUT_MS}ms; continuing`);
|
|
188
|
+
}
|
|
189
|
+
else if (bounded.kind === 'error') {
|
|
190
|
+
log(`dsh-tui: ${name} listener failed; continuing with the other listeners: %o`, bounded.error);
|
|
191
|
+
}
|
|
192
|
+
}));
|
|
155
193
|
}
|
|
156
194
|
/** Shared normalizer for the veto-only decisions (session-switch, compact):
|
|
157
195
|
* `{ cancel: true, reason? }` or no opinion; everything else is ignored.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/grants.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAI5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAKjE,eAAO,MAAM,qBAAqB,0BAA0B,CAAA;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,MAAM,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IACtF,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;IAC/C,gBAAgB,IAAI,SAAS,MAAM,EAAE,CAAA;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;
|
|
1
|
+
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/grants.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAI5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAKjE,eAAO,MAAM,qBAAqB,0BAA0B,CAAA;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,MAAM,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IACtF,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;IAC/C,gBAAgB,IAAI,SAAS,MAAM,EAAE,CAAA;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAkJD,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,UAAU,CAKvF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAiB,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,UAAU,CA0DhG"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Live, scoped plugin grant evaluation. */
|
|
2
|
-
import { readFileSync
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { normalizePermissionScope, permissionScopeCovers } from '../plugin-spec/permission-scope.js';
|
|
5
5
|
import { loadSpecData } from '../plugin-spec/registry.js';
|
|
@@ -34,18 +34,32 @@ function parseTable(text) {
|
|
|
34
34
|
catch {
|
|
35
35
|
return { grants, denies, corrupt: true };
|
|
36
36
|
}
|
|
37
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
38
|
+
return { grants, denies, corrupt: true };
|
|
39
|
+
}
|
|
40
|
+
const root = parsed;
|
|
41
|
+
if (Object.keys(root).some(key => key !== 'grants' && key !== 'denies')) {
|
|
42
|
+
return { grants, denies, corrupt: true };
|
|
43
|
+
}
|
|
37
44
|
const readSection = (key, target) => {
|
|
38
|
-
const section =
|
|
45
|
+
const section = root[key];
|
|
46
|
+
if (section === undefined)
|
|
47
|
+
return true;
|
|
39
48
|
if (section === null || typeof section !== 'object' || Array.isArray(section))
|
|
40
|
-
return;
|
|
49
|
+
return false;
|
|
41
50
|
for (const [componentId, values] of Object.entries(section)) {
|
|
42
|
-
if (!Array.isArray(values))
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
if (componentId === '' || !Array.isArray(values))
|
|
52
|
+
return false;
|
|
53
|
+
const rules = values.map(parseRule);
|
|
54
|
+
if (rules.some(rule => rule === undefined))
|
|
55
|
+
return false;
|
|
56
|
+
target.set(componentId, rules);
|
|
45
57
|
}
|
|
58
|
+
return true;
|
|
46
59
|
};
|
|
47
|
-
readSection('grants', grants)
|
|
48
|
-
|
|
60
|
+
if (!readSection('grants', grants) || !readSection('denies', denies)) {
|
|
61
|
+
return { grants: new Map(), denies: new Map(), corrupt: true };
|
|
62
|
+
}
|
|
49
63
|
return { grants, denies, corrupt: false };
|
|
50
64
|
}
|
|
51
65
|
function resolveRegistry(registry) {
|
|
@@ -91,7 +105,11 @@ function hasUnknownActivationRule(rules, principal, permission, scope) {
|
|
|
91
105
|
}
|
|
92
106
|
function storeFrom(table, registry, onChange) {
|
|
93
107
|
const known = new Map((registry?.permissions ?? []).map(entry => [entry.name, entry.default]));
|
|
94
|
-
|
|
108
|
+
// The store is a capability, not a mutable configuration object. Keep the
|
|
109
|
+
// live table in the closure and freeze the facade so a traceable Cordis
|
|
110
|
+
// proxy (or an accidentally retained reference) cannot replace `allows`
|
|
111
|
+
// and turn an authorization check into an unconditional allow.
|
|
112
|
+
return Object.freeze({
|
|
95
113
|
get corrupt() {
|
|
96
114
|
return table().corrupt;
|
|
97
115
|
},
|
|
@@ -116,7 +134,7 @@ function storeFrom(table, registry, onChange) {
|
|
|
116
134
|
defaultOf: permission => known.get(permission) ?? 'deny',
|
|
117
135
|
knownPermissions: () => [...known.keys()],
|
|
118
136
|
onChange,
|
|
119
|
-
};
|
|
137
|
+
});
|
|
120
138
|
}
|
|
121
139
|
/** Parse a fixed snapshot, primarily for deterministic tests. */
|
|
122
140
|
export function parseGrantStore(text, registry) {
|
|
@@ -134,15 +152,18 @@ export function readGrantStore(dir = DATA_DIR, registry) {
|
|
|
134
152
|
const file = join(dir, EXTENSION_GRANTS_FILE);
|
|
135
153
|
const readCurrent = () => {
|
|
136
154
|
let text;
|
|
155
|
+
let missing = false;
|
|
137
156
|
try {
|
|
138
157
|
text = readFileSync(file, 'utf8');
|
|
139
158
|
}
|
|
140
159
|
catch (error) {
|
|
141
|
-
|
|
160
|
+
missing = error.code === 'ENOENT';
|
|
142
161
|
text = missing ? '' : '{unreadable';
|
|
143
162
|
}
|
|
144
163
|
return {
|
|
145
|
-
|
|
164
|
+
// An existing zero-byte file is corruption, not the same state as an
|
|
165
|
+
// absent grants file. Only ENOENT receives the registry defaults.
|
|
166
|
+
table: missing
|
|
146
167
|
? { grants: new Map(), denies: new Map(), corrupt: false }
|
|
147
168
|
: parseTable(text),
|
|
148
169
|
signature: text,
|
|
@@ -150,6 +171,7 @@ export function readGrantStore(dir = DATA_DIR, registry) {
|
|
|
150
171
|
};
|
|
151
172
|
const listeners = new Set();
|
|
152
173
|
let watching = false;
|
|
174
|
+
let watchTimer;
|
|
153
175
|
let signature = readCurrent().signature;
|
|
154
176
|
const changed = () => {
|
|
155
177
|
const next = readCurrent().signature;
|
|
@@ -170,13 +192,20 @@ export function readGrantStore(dir = DATA_DIR, registry) {
|
|
|
170
192
|
listeners.add(listener);
|
|
171
193
|
if (!watching) {
|
|
172
194
|
watching = true;
|
|
173
|
-
|
|
195
|
+
// A small unref'ed poll is deterministic across filesystems and
|
|
196
|
+
// timestamp granularities. The synchronous read on every operation
|
|
197
|
+
// remains the authorization source of truth; this loop only releases
|
|
198
|
+
// grant-owned subscriptions promptly after a revocation.
|
|
199
|
+
watchTimer = setInterval(changed, 50);
|
|
200
|
+
watchTimer.unref?.();
|
|
174
201
|
}
|
|
175
202
|
return () => {
|
|
176
203
|
listeners.delete(listener);
|
|
177
204
|
if (watching && listeners.size === 0) {
|
|
178
205
|
watching = false;
|
|
179
|
-
|
|
206
|
+
if (watchTimer !== undefined)
|
|
207
|
+
clearInterval(watchTimer);
|
|
208
|
+
watchTimer = undefined;
|
|
180
209
|
}
|
|
181
210
|
};
|
|
182
211
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
/** Host-only escape hatch for process teardown paths that intentionally act on
|
|
3
|
+
* the composition root while running from a host plugin callback. This module
|
|
4
|
+
* is internal and is not part of the public plugin package surface. */
|
|
5
|
+
export declare function withHostRootCapability<T>(callback: () => T): T;
|
|
6
|
+
export declare function concreteService<T extends object>(service: T): T;
|
|
7
|
+
/** Bind a host-side disposer to the caller's authenticated Cordis activation.
|
|
8
|
+
* A failed registration immediately rolls back the owned contribution. */
|
|
9
|
+
export declare function bindCallerEffect(ctx: Context, disposer: () => unknown, onRegistered?: (cleanup: () => unknown) => void): boolean;
|
|
10
|
+
/** Return the stable, authenticated Fiber identity for an activation. */
|
|
11
|
+
export declare function activationFiber(ctx: Context): object | undefined;
|
|
12
|
+
/** Return Cordis's canonical activation context, never a writable context
|
|
13
|
+
* shadow. Explicit owner arguments use this before resolving host services. */
|
|
14
|
+
export declare function activationContext(ctx: Context): Context | undefined;
|
|
15
|
+
/** Resolve a service's composition root once. Service methods are invoked
|
|
16
|
+
* through caller-bound Cordis proxies; keeping this root in host state avoids
|
|
17
|
+
* resolving sibling services through an attacker-provided caller shadow. */
|
|
18
|
+
export declare function compositionRoot(ctx: Context): Context;
|
|
19
|
+
/** Recover the composition root that owns a traceable service. Cordis stores
|
|
20
|
+
* the provider context on the concrete Service instance; callers may reach
|
|
21
|
+
* that instance through a proxy from another composition, so this must be
|
|
22
|
+
* resolved from the unwrapped service rather than from the call context. */
|
|
23
|
+
export declare function serviceCompositionRoot(service: object): Context | undefined;
|
|
24
|
+
/** Explicit context arguments on mediated APIs may be supplied by host code
|
|
25
|
+
* running from the composition root, or may be the activation making the
|
|
26
|
+
* service call. A non-root plugin cannot nominate a different activation's
|
|
27
|
+
* context and thereby borrow its verified identity/lifecycle. */
|
|
28
|
+
export declare function assertCallerContext(caller: Context, target: Context, capability: string, service?: object): void;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the caller for a plugin-owned effect. A plugin can read
|
|
31
|
+
* `ctx.root`, but using its root-bound service proxy would attach cleanup to
|
|
32
|
+
* the host fiber and leave the effect alive after the plugin unloads. Host
|
|
33
|
+
* code must use the module-local host accessor for controls it owns; the
|
|
34
|
+
* public service surface only accepts a live non-root activation.
|
|
35
|
+
*/
|
|
36
|
+
export declare function requirePluginCaller(caller: Context, capability: string, service?: object): Context;
|
|
37
|
+
//# sourceMappingURL=host-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-access.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/host-access.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,qBAAqB,CAAA;AAoSpD;;uEAEuE;AACvE,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAE9D;AA8FD,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAe/D;AAED;0EAC0E;AAC1E,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,OAAO,EACvB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,KAAK,IAAI,GAC9C,OAAO,CA2BT;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMhE;AAED;+EAC+E;AAC/E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAInE;AAED;;4EAE4E;AAC5E,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAwDrD;AAuDD;;;4EAG4E;AAC5E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAO3E;AAED;;;iEAGiE;AACjE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAehH;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAmBlG"}
|