@punica/editor 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.bundle.esm.js +2 -2
- package/dist/index.bundle.esm.js.map +1 -1
- package/dist/index.bundle.umd.js +2 -2
- package/dist/index.bundle.umd.js.map +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +1 -0
- package/types/punica.module.kernel.policy.d.ts +42 -0
- package/types/punica.module.runtime.api.d.ts +49 -0
- package/types/punica.module.runtime.fileOperations.d.ts +141 -0
- package/types/punica.module.shell.contentTabs.d.ts +57 -3
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
/// <reference path="punica.module.extensions.settings.d.ts" />
|
|
26
26
|
/// <reference path="punica.module.extensions.i18n.d.ts" />
|
|
27
27
|
/// <reference path="punica.module.runtime.fs.d.ts" />
|
|
28
|
+
/// <reference path="punica.module.runtime.fileOperations.d.ts" />
|
|
28
29
|
/// <reference path="punica.module.runtime.search.d.ts" />
|
|
29
30
|
/// <reference path="punica.module.runtime.vcs.d.ts" />
|
|
30
31
|
/// <reference path="punica.module.runtime.workspace.d.ts" />
|
|
@@ -266,6 +266,48 @@ declare module 'punica' {
|
|
|
266
266
|
approvals: ApprovalRecord[];
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
/**
|
|
270
|
+
* A signed approval grant. The gate matches a grant by (kind, id) plus
|
|
271
|
+
* scope and workspace; the token is what lets a later reader prove the
|
|
272
|
+
* grant was really issued and not edited afterwards.
|
|
273
|
+
*/
|
|
274
|
+
export interface ApprovalTokenPayload {
|
|
275
|
+
scope: ApprovalScope;
|
|
276
|
+
capabilityId: string;
|
|
277
|
+
expiry: number;
|
|
278
|
+
jti: string;
|
|
279
|
+
issuer: string;
|
|
280
|
+
workspaceId?: string;
|
|
281
|
+
delegatedFrom?: string;
|
|
282
|
+
delegationDepth?: number;
|
|
283
|
+
approval?: PolicyApproval;
|
|
284
|
+
risk?: PolicyRisk;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Signs approval grants. The substrate default is HMAC-SHA256 over an
|
|
289
|
+
* in-memory ephemeral key, which does not survive a restart; a
|
|
290
|
+
* production host injects its own through
|
|
291
|
+
* `punica.runtime.setApprovalTokenSigner`.
|
|
292
|
+
*/
|
|
293
|
+
export interface ApprovalTokenSigner {
|
|
294
|
+
/** Sign `payload`; returns the compact token string. */
|
|
295
|
+
sign(payload: ApprovalTokenPayload): Promise<string>;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** Verifies (and revokes) approval tokens. */
|
|
299
|
+
export interface ApprovalTokenVerifier {
|
|
300
|
+
/**
|
|
301
|
+
* Parse, check the header and signature, check expiry and jti
|
|
302
|
+
* revocation. Returns the payload, or throws with a stable code.
|
|
303
|
+
*/
|
|
304
|
+
verify(token: string): Promise<ApprovalTokenPayload>;
|
|
305
|
+
/** Mark a jti revoked; tokens carrying it then fail verification. */
|
|
306
|
+
revoke(jti: string): void;
|
|
307
|
+
/** Is a jti currently revoked? */
|
|
308
|
+
isRevoked(jti: string): boolean;
|
|
309
|
+
}
|
|
310
|
+
|
|
269
311
|
export namespace Policy {
|
|
270
312
|
const manager: PolicyApi;
|
|
271
313
|
}
|
|
@@ -70,6 +70,12 @@ declare module 'punica' {
|
|
|
70
70
|
lifecyclePhase?: LifecyclePhase;
|
|
71
71
|
fs: FileSystemProvider;
|
|
72
72
|
fileOpeners: FileOpenersApi;
|
|
73
|
+
/**
|
|
74
|
+
* Rename/move and delete, announced to the layers that key state by
|
|
75
|
+
* path (content tabs, editors). Prefer this over calling the
|
|
76
|
+
* equivalent `fs` methods directly.
|
|
77
|
+
*/
|
|
78
|
+
fileOperations: FileOperationsApi;
|
|
73
79
|
search: SearchApi;
|
|
74
80
|
vcs: VcsApi;
|
|
75
81
|
terminals: TerminalApi;
|
|
@@ -181,7 +187,50 @@ declare module 'punica' {
|
|
|
181
187
|
setResourcesProvider: (provider: ResourcesApi) => void;
|
|
182
188
|
setTasksProvider: (provider: TaskRunnerApi) => void;
|
|
183
189
|
setSecretsProvider: (provider: SecretsApi) => void;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Approval-token signing. The substrate default mints an in-memory
|
|
193
|
+
* ephemeral HMAC key on the first signature and loses it when the
|
|
194
|
+
* process exits, so a token signed before a restart no longer
|
|
195
|
+
* verifies after one — fine for dev and demo, useless as evidence.
|
|
196
|
+
* A production host installs a stable secret from the OS keychain
|
|
197
|
+
* during bootstrap, before `initialize()`:
|
|
198
|
+
*
|
|
199
|
+
* punica.runtime.installDefaultApprovalTokenPair(secret)
|
|
200
|
+
*
|
|
201
|
+
* The two setters are the same seam for a host that brings its own
|
|
202
|
+
* signer (KMS, HSM); passing `undefined` restores the substrate
|
|
203
|
+
* default. The key belongs to the host and never to an extension —
|
|
204
|
+
* an extension that can reach the signing key can also produce the
|
|
205
|
+
* thing it signs.
|
|
206
|
+
*/
|
|
207
|
+
installDefaultApprovalTokenPair: (secret: Uint8Array | string) => void;
|
|
208
|
+
setApprovalTokenSigner: (
|
|
209
|
+
signer: kernel.ApprovalTokenSigner | undefined
|
|
210
|
+
) => void;
|
|
211
|
+
setApprovalTokenVerifier: (
|
|
212
|
+
verifier: kernel.ApprovalTokenVerifier | undefined
|
|
213
|
+
) => void;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Who is at this workstation, so a user-initiated call's audit record
|
|
217
|
+
* names a subject instead of leaving `actor.id` empty. Absent is a
|
|
218
|
+
* normal state — a trial licence has no account — and the substrate
|
|
219
|
+
* invents nothing to fill it. Passing `undefined` clears it.
|
|
220
|
+
*/
|
|
221
|
+
setHostIdentity: (identity: HostIdentity | undefined) => void;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* The person a licence belongs to, as the host knows them. `email` is
|
|
226
|
+
* the identifier (a registry guarantees it unique and present);
|
|
227
|
+
* `displayName` is presentation and may be absent.
|
|
228
|
+
*/
|
|
229
|
+
export interface HostIdentity {
|
|
230
|
+
email: string;
|
|
231
|
+
displayName?: string | null;
|
|
184
232
|
}
|
|
233
|
+
|
|
185
234
|
}
|
|
186
235
|
|
|
187
236
|
export const runtime: runtime.RuntimeApi;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
declare module 'punica' {
|
|
2
|
+
export namespace runtime {
|
|
3
|
+
/**
|
|
4
|
+
* A rename (or move — the same thing at the filesystem level) that is
|
|
5
|
+
* about to happen, or has just happened. Both paths are
|
|
6
|
+
* workspace-relative, the same shape `FileSystemProvider` takes.
|
|
7
|
+
*/
|
|
8
|
+
export interface FileRenameEvent {
|
|
9
|
+
oldPath: string;
|
|
10
|
+
newPath: string;
|
|
11
|
+
isDirectory: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** A delete that is about to happen, or has just happened. */
|
|
15
|
+
export interface FileDeleteEvent {
|
|
16
|
+
path: string;
|
|
17
|
+
isDirectory: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A `will*` hook's refusal. Returning this aborts the operation before
|
|
22
|
+
* the filesystem is touched; `reason` becomes the thrown error's message,
|
|
23
|
+
* so it is read by a human and should say what the user can do about it.
|
|
24
|
+
*/
|
|
25
|
+
export interface ParticipantVeto {
|
|
26
|
+
veto: true;
|
|
27
|
+
reason?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A layer that needs to know about — or object to — file operations.
|
|
32
|
+
*
|
|
33
|
+
* This exists because a path is an identity, not just an argument. The
|
|
34
|
+
* shell keys open content tabs by path, the code editor keys its Monaco
|
|
35
|
+
* instances by path, notebook controllers key their models by path. When
|
|
36
|
+
* a file is renamed underneath them they are all holding a key that no
|
|
37
|
+
* longer names anything, and the failure is silent: a save writes to the
|
|
38
|
+
* old name, or finds no editor and writes nothing at all.
|
|
39
|
+
*
|
|
40
|
+
* So the operation cannot just be `fs.renameFile`. It has to be a
|
|
41
|
+
* transaction the interested layers take part in:
|
|
42
|
+
*
|
|
43
|
+
* - `will*` runs BEFORE the filesystem call and may veto (returning
|
|
44
|
+
* `{ veto: true }` or throwing). Use it for "this must not happen
|
|
45
|
+
* yet" — not for cleanup.
|
|
46
|
+
* - `did*` runs AFTER the filesystem call succeeded, awaited and in
|
|
47
|
+
* registration order. Use it to re-key state. A throwing `did*` hook
|
|
48
|
+
* is logged and skipped; it does not fail the operation, because the
|
|
49
|
+
* file has already moved and reporting failure would be a lie.
|
|
50
|
+
*
|
|
51
|
+
* Participants are host/shell territory. A headless boot registers none,
|
|
52
|
+
* and then an operation is exactly the filesystem call plus its event.
|
|
53
|
+
*/
|
|
54
|
+
export interface FileOperationParticipant {
|
|
55
|
+
/** Stable id, used in logs and to make double-registration visible. */
|
|
56
|
+
id: string;
|
|
57
|
+
willRename?(
|
|
58
|
+
event: FileRenameEvent
|
|
59
|
+
): Promise<void | ParticipantVeto> | void | ParticipantVeto;
|
|
60
|
+
didRename?(event: FileRenameEvent): Promise<void> | void;
|
|
61
|
+
willDelete?(
|
|
62
|
+
event: FileDeleteEvent
|
|
63
|
+
): Promise<void | ParticipantVeto> | void | ParticipantVeto;
|
|
64
|
+
didDelete?(event: FileDeleteEvent): Promise<void> | void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Options for `FileOperationsApi.rename`. */
|
|
68
|
+
export interface FileRenameOptions {
|
|
69
|
+
/**
|
|
70
|
+
* Whether the target is a directory. Callers that already know (the
|
|
71
|
+
* explorer reads it off its tree) should pass it; otherwise it is
|
|
72
|
+
* resolved via `fs.stat` and, on providers without `stat`, assumed to
|
|
73
|
+
* be a file.
|
|
74
|
+
*/
|
|
75
|
+
isDirectory?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Options for `FileOperationsApi.delete`. */
|
|
79
|
+
export interface FileDeleteOperationOptions {
|
|
80
|
+
isDirectory?: boolean;
|
|
81
|
+
/** Passed through to `FileSystemProvider.deleteFile` for idempotency. */
|
|
82
|
+
ifExists?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The single seam every file mutation that changes a path should go
|
|
87
|
+
* through: rename/move and delete.
|
|
88
|
+
*
|
|
89
|
+
* `runtime.fs` stays the raw filesystem surface and is still the right
|
|
90
|
+
* call for reads and for content writes, where the path is unchanged and
|
|
91
|
+
* nobody's key breaks. Creating and deleting a path, or moving one, is
|
|
92
|
+
* different: it invalidates state held elsewhere in the app, and the
|
|
93
|
+
* whole point of routing it here is that the invalidation is announced
|
|
94
|
+
* instead of discovered.
|
|
95
|
+
*
|
|
96
|
+
* Two consumers, one code path: the explorer's context menu and an agent
|
|
97
|
+
* invoking the `file-explorer.rename` capability both land here, so tab
|
|
98
|
+
* state stays correct whether a human or a model moved the file.
|
|
99
|
+
*
|
|
100
|
+
* ## Dirty (unsaved) tracking
|
|
101
|
+
*
|
|
102
|
+
* `setDirty` / `isDirty` / `getDirtyPaths` are a working-copy registry.
|
|
103
|
+
* The shell mirrors its content tabs into it, which lets a caller ask
|
|
104
|
+
* "would deleting this lose unsaved work?" without knowing that content
|
|
105
|
+
* tabs exist — the explorer's delete confirmation reads it to say so in
|
|
106
|
+
* the dialog, in one prompt rather than two.
|
|
107
|
+
*/
|
|
108
|
+
export interface FileOperationsApi {
|
|
109
|
+
/**
|
|
110
|
+
* Add a participant. Registering the same `id` twice replaces the
|
|
111
|
+
* first. Dispose to remove it — a shell that is torn down must not
|
|
112
|
+
* leave a hook pointing at dead state.
|
|
113
|
+
*/
|
|
114
|
+
registerParticipant(participant: FileOperationParticipant): {
|
|
115
|
+
dispose(): void;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/** Record (or clear) unsaved-changes state for a path. */
|
|
119
|
+
setDirty(path: string, dirty: boolean): void;
|
|
120
|
+
isDirty(path: string): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Every path with unsaved changes; with `prefix`, only that path and
|
|
123
|
+
* its descendants (so a folder delete can name what it would lose).
|
|
124
|
+
*/
|
|
125
|
+
getDirtyPaths(prefix?: string): string[];
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Move `oldPath` to `newPath`, announcing it. Rejects if a participant
|
|
129
|
+
* vetoes or the filesystem call fails; in both cases nothing has moved.
|
|
130
|
+
*/
|
|
131
|
+
rename(
|
|
132
|
+
oldPath: string,
|
|
133
|
+
newPath: string,
|
|
134
|
+
opts?: FileRenameOptions
|
|
135
|
+
): Promise<void>;
|
|
136
|
+
|
|
137
|
+
/** Delete `path`, announcing it. Same failure contract as `rename`. */
|
|
138
|
+
delete(path: string, opts?: FileDeleteOperationOptions): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
declare module 'punica' {
|
|
2
2
|
export namespace shell {
|
|
3
|
+
/** One open content tab, as reported by `ContentTabsApi.list`. */
|
|
4
|
+
export interface ContentTabInfo {
|
|
5
|
+
/** Workspace-relative file path, or a scheme URI for command-only views. */
|
|
6
|
+
path: string;
|
|
7
|
+
/** Label shown on the tab (without the unsaved-changes marker). */
|
|
8
|
+
title: string;
|
|
9
|
+
/** Set by whichever extension opened it (e.g. "code", "markdown"). */
|
|
10
|
+
viewType?: string;
|
|
11
|
+
/** Whether the tab holds unsaved edits. */
|
|
12
|
+
dirty: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
3
15
|
/**
|
|
4
16
|
* Content tab management capability for the main editor area.
|
|
17
|
+
*
|
|
18
|
+
* Tabs are identified by `path` — the same workspace-relative path the
|
|
19
|
+
* filesystem uses — which is what makes `rename` necessary rather than
|
|
20
|
+
* cosmetic: after a file moves, every tab keyed by the old path is
|
|
21
|
+
* holding an identity that no longer exists.
|
|
5
22
|
*/
|
|
6
23
|
export interface ContentTabsApi {
|
|
7
24
|
open(options: {
|
|
@@ -9,9 +26,18 @@ declare module 'punica' {
|
|
|
9
26
|
title?: string;
|
|
10
27
|
viewType?: string;
|
|
11
28
|
}): HTMLElement | null;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Close the tab at `path`, prompting first when it has unsaved changes
|
|
31
|
+
* (Save / Don't Save / Cancel). Resolves when the tab is gone or the
|
|
32
|
+
* user cancelled — check `list()` if you need to know which.
|
|
33
|
+
*/
|
|
34
|
+
close(path: string): Promise<void>;
|
|
35
|
+
/** As `close`, addressed by tab id rather than path. */
|
|
36
|
+
closeById(id: string): Promise<void>;
|
|
37
|
+
closeOthers(path: string): Promise<void>;
|
|
38
|
+
/** As `closeOthers`, addressed by tab id rather than path. */
|
|
39
|
+
closeOthersById(id: string): Promise<void>;
|
|
40
|
+
closeAll(): Promise<void>;
|
|
15
41
|
setDirty(path: string, dirty: boolean): void;
|
|
16
42
|
/**
|
|
17
43
|
* Returns the path of the currently active content tab, or null if none.
|
|
@@ -23,6 +49,34 @@ declare module 'punica' {
|
|
|
23
49
|
* (e.g. "code", "flow-book", "markdown").
|
|
24
50
|
*/
|
|
25
51
|
getActiveViewType(): string | null;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Re-key the tab at `oldPath` to `newPath`, keeping its content,
|
|
55
|
+
* dirty marker and selection. When `oldPath` names a directory, every
|
|
56
|
+
* tab beneath it moves too — renaming `src/` has to carry
|
|
57
|
+
* `src/app.ts` with it.
|
|
58
|
+
*
|
|
59
|
+
* This does not touch the filesystem; it is the tab half of a rename
|
|
60
|
+
* that already happened. Callers should not invoke it directly —
|
|
61
|
+
* `runtime.fileOperations.rename` drives it through the shell's
|
|
62
|
+
* participant, so an agent-initiated rename syncs the same way a
|
|
63
|
+
* context-menu one does.
|
|
64
|
+
*/
|
|
65
|
+
rename(oldPath: string, newPath: string): void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Close the tab at `path` (and, for a directory, everything beneath it)
|
|
69
|
+
* without prompting. For when the file is already gone: there is
|
|
70
|
+
* nothing left to save, so the unsaved-changes dialog would offer a
|
|
71
|
+
* choice that cannot be honored.
|
|
72
|
+
*/
|
|
73
|
+
forceClose(path: string): void;
|
|
74
|
+
|
|
75
|
+
/** Whether the tab at `path` holds unsaved edits. False when no such tab. */
|
|
76
|
+
isDirty(path: string): boolean;
|
|
77
|
+
|
|
78
|
+
/** Every open tab, in the order they appear in the tab strip. */
|
|
79
|
+
list(): readonly ContentTabInfo[];
|
|
26
80
|
}
|
|
27
81
|
|
|
28
82
|
/**
|