@prettier-ai/dsh-workspace 0.1.2-alpha.1
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 +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +178 -0
- package/README.zh.md +178 -0
- package/lib/index.js +757 -0
- package/lib/invariant.js +114 -0
- package/lib/types/entity.d.ts +88 -0
- package/lib/types/entity.js +156 -0
- package/lib/types/index.d.ts +163 -0
- package/lib/types/index.js +600 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.js +43 -0
- package/lib/types/paths.d.ts +18 -0
- package/lib/types/paths.js +21 -0
- package/lib/types/spec.d.ts +85 -0
- package/lib/types/spec.js +63 -0
- package/lib/types/types.d.ts +92 -0
- package/lib/types/types.js +8 -0
- package/package.json +60 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path canonicalization for workspace identity.
|
|
3
|
+
* @module @prettier-ai/dsh-workspace/src/paths
|
|
4
|
+
*/
|
|
5
|
+
import { realpath } from 'node:fs/promises';
|
|
6
|
+
/**
|
|
7
|
+
* Canonicalize a directory path via `fs.realpath`: trailing slashes, `..`
|
|
8
|
+
* segments, and symlinks are all resolved. This is the ONE uniqueness canon of
|
|
9
|
+
* the package — workspace paths are stored canonicalized, uniqueness is
|
|
10
|
+
* string equality of canonicalized paths (a symlink to an existing
|
|
11
|
+
* workspace's directory collides), and attach-time session `cwd` checks go
|
|
12
|
+
* through the same canon. A path that does not exist rejects with the
|
|
13
|
+
* original `ENOENT` — this is `create`'s reject path (a workspace must point
|
|
14
|
+
* at an existing directory).
|
|
15
|
+
* @param path - The path to canonicalize.
|
|
16
|
+
* @returns the canonical absolute path.
|
|
17
|
+
*/
|
|
18
|
+
export async function realpathNormalize(path) {
|
|
19
|
+
return await realpath(path);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workspace domain declaration: record schema and the `defineDomain` spec
|
|
3
|
+
* the registry opens. The zod schema validates the shipped format at the
|
|
4
|
+
* durability boundary and is the direct source of a future RPC wire projection.
|
|
5
|
+
* @module @prettier-ai/dsh-workspace/src/spec
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { SessionId } from '@prettier-ai/dsh-session';
|
|
9
|
+
import type { WorkspaceId } from './types.ts';
|
|
10
|
+
/**
|
|
11
|
+
* Durable shape of one workspace record. `path` is the `fs.realpath` canon
|
|
12
|
+
* stamped at create; `sessionIds` is the ordered ownership account (array
|
|
13
|
+
* order is display order); timestamps are ISO-8601 strings.
|
|
14
|
+
*/
|
|
15
|
+
export declare const workspaceRecord: z.ZodObject<{
|
|
16
|
+
path: z.ZodString;
|
|
17
|
+
title: z.ZodString;
|
|
18
|
+
sessionIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>;
|
|
19
|
+
createdAt: z.ZodString;
|
|
20
|
+
updatedAt: z.ZodString;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
/** One stored workspace record, inferred from {@link workspaceRecord}. */
|
|
23
|
+
export type WorkspaceRecord = z.infer<typeof workspaceRecord>;
|
|
24
|
+
/**
|
|
25
|
+
* Durable registry state. `initialized` distinguishes a valid empty registry
|
|
26
|
+
* from one that still needs the header-only history bootstrap;
|
|
27
|
+
* `workspaceIds` is the authoritative display order. `archivedSessionIds` is
|
|
28
|
+
* the registry-global archive set layered over workspace accounting: an
|
|
29
|
+
* archived session keeps its `sessionIds` slot (unarchiving must restore the
|
|
30
|
+
* position), so the set never participates in the one-owner accounting
|
|
31
|
+
* invariant. Defaulted so records written before the field parse unchanged.
|
|
32
|
+
*/
|
|
33
|
+
export declare const workspaceDomainState: z.ZodObject<{
|
|
34
|
+
initialized: z.ZodBoolean;
|
|
35
|
+
workspaceIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>>;
|
|
36
|
+
archivedSessionIds: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
|
|
37
|
+
pendingMutation: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
38
|
+
operation: z.ZodLiteral<"create">;
|
|
39
|
+
workspaceId: z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>;
|
|
40
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
41
|
+
operation: z.ZodLiteral<"delete">;
|
|
42
|
+
workspaceId: z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>;
|
|
43
|
+
}, z.core.$strip>], "operation">>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
/** Durable registry state inferred from {@link workspaceDomainState}. */
|
|
46
|
+
export type WorkspaceDomainState = z.infer<typeof workspaceDomainState>;
|
|
47
|
+
/**
|
|
48
|
+
* The workspace domain spec: one `workspaces` table keyed by
|
|
49
|
+
* {@link WorkspaceId} plus the bootstrap/order singleton. The registry opens
|
|
50
|
+
* this through `ctx.storage.domain`; the spec object is the single source of
|
|
51
|
+
* the domain's identity, version, and schemas.
|
|
52
|
+
*/
|
|
53
|
+
export declare const workspaceDomainSpec: {
|
|
54
|
+
name: string;
|
|
55
|
+
version: number;
|
|
56
|
+
global: {
|
|
57
|
+
schema: z.ZodObject<{
|
|
58
|
+
initialized: z.ZodBoolean;
|
|
59
|
+
workspaceIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>>;
|
|
60
|
+
archivedSessionIds: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
|
|
61
|
+
pendingMutation: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
62
|
+
operation: z.ZodLiteral<"create">;
|
|
63
|
+
workspaceId: z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>;
|
|
64
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
65
|
+
operation: z.ZodLiteral<"delete">;
|
|
66
|
+
workspaceId: z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>;
|
|
67
|
+
}, z.core.$strip>], "operation">>;
|
|
68
|
+
}, z.core.$strip>;
|
|
69
|
+
initial: {
|
|
70
|
+
initialized: boolean;
|
|
71
|
+
workspaceIds: never[];
|
|
72
|
+
archivedSessionIds: never[];
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
tables: {
|
|
76
|
+
workspaces: import("@prettier-ai/dsh-storage-domain").DomainTableSpec<WorkspaceId, {
|
|
77
|
+
path: string;
|
|
78
|
+
title: string;
|
|
79
|
+
sessionIds: SessionId[];
|
|
80
|
+
createdAt: string;
|
|
81
|
+
updatedAt: string;
|
|
82
|
+
}>;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=spec.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workspace domain declaration: record schema and the `defineDomain` spec
|
|
3
|
+
* the registry opens. The zod schema validates the shipped format at the
|
|
4
|
+
* durability boundary and is the direct source of a future RPC wire projection.
|
|
5
|
+
* @module @prettier-ai/dsh-workspace/src/spec
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { SessionId } from '@prettier-ai/dsh-session';
|
|
9
|
+
import { defineDomain, domainTable } from '@prettier-ai/dsh-storage-domain';
|
|
10
|
+
/** Workspace id schema at the durable boundary; branding has no runtime representation. */
|
|
11
|
+
const workspaceId = z.string().transform(value => value);
|
|
12
|
+
/**
|
|
13
|
+
* Durable shape of one workspace record. `path` is the `fs.realpath` canon
|
|
14
|
+
* stamped at create; `sessionIds` is the ordered ownership account (array
|
|
15
|
+
* order is display order); timestamps are ISO-8601 strings.
|
|
16
|
+
*/
|
|
17
|
+
export const workspaceRecord = z.object({
|
|
18
|
+
path: z.string(),
|
|
19
|
+
title: z.string(),
|
|
20
|
+
sessionIds: z.array(z.string().transform(SessionId)),
|
|
21
|
+
createdAt: z.string(),
|
|
22
|
+
updatedAt: z.string(),
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Recoverable two-write mutation marker. The marker is persisted before the
|
|
26
|
+
* record/order pair can diverge, so startup can distinguish an interrupted
|
|
27
|
+
* registry operation from unexplained medium corruption.
|
|
28
|
+
*/
|
|
29
|
+
const workspacePendingMutation = z.discriminatedUnion('operation', [
|
|
30
|
+
z.object({ operation: z.literal('create'), workspaceId }),
|
|
31
|
+
z.object({ operation: z.literal('delete'), workspaceId }),
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Durable registry state. `initialized` distinguishes a valid empty registry
|
|
35
|
+
* from one that still needs the header-only history bootstrap;
|
|
36
|
+
* `workspaceIds` is the authoritative display order. `archivedSessionIds` is
|
|
37
|
+
* the registry-global archive set layered over workspace accounting: an
|
|
38
|
+
* archived session keeps its `sessionIds` slot (unarchiving must restore the
|
|
39
|
+
* position), so the set never participates in the one-owner accounting
|
|
40
|
+
* invariant. Defaulted so records written before the field parse unchanged.
|
|
41
|
+
*/
|
|
42
|
+
export const workspaceDomainState = z.object({
|
|
43
|
+
initialized: z.boolean(),
|
|
44
|
+
workspaceIds: z.array(workspaceId),
|
|
45
|
+
archivedSessionIds: z.array(z.string().transform(SessionId)).default([]),
|
|
46
|
+
pendingMutation: workspacePendingMutation.optional(),
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* The workspace domain spec: one `workspaces` table keyed by
|
|
50
|
+
* {@link WorkspaceId} plus the bootstrap/order singleton. The registry opens
|
|
51
|
+
* this through `ctx.storage.domain`; the spec object is the single source of
|
|
52
|
+
* the domain's identity, version, and schemas.
|
|
53
|
+
*/
|
|
54
|
+
export const workspaceDomainSpec = defineDomain({
|
|
55
|
+
name: 'workspace',
|
|
56
|
+
version: 2,
|
|
57
|
+
global: {
|
|
58
|
+
schema: workspaceDomainState,
|
|
59
|
+
initial: { initialized: false, workspaceIds: [], archivedSessionIds: [] },
|
|
60
|
+
},
|
|
61
|
+
tables: { workspaces: domainTable(workspaceRecord) },
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type vocabulary of the workspace entity: the `WorkspaceId` brand and
|
|
3
|
+
* the `Workspace` consumer interface. Types only — the `WorkspaceId` factory
|
|
4
|
+
* lives in `index.ts` (this file carries no runtime code).
|
|
5
|
+
* @module @prettier-ai/dsh-workspace/src/types
|
|
6
|
+
*/
|
|
7
|
+
import type { Branded } from '@prettier-ai/dsh-brand';
|
|
8
|
+
import type { SessionId } from '@prettier-ai/dsh-session/types';
|
|
9
|
+
/**
|
|
10
|
+
* Identifies one workspace record. A generated uuid, never the path: path
|
|
11
|
+
* normalization rewrites paths, and a reference anchor must stay stable.
|
|
12
|
+
*/
|
|
13
|
+
export type WorkspaceId = Branded<'WorkspaceId'>;
|
|
14
|
+
/**
|
|
15
|
+
* One workspace: a stable id over an existing directory, a display title, and
|
|
16
|
+
* an ordered candidate account of sessions. Membership requires both an id in
|
|
17
|
+
* that account and a session header whose canonical cwd equals the workspace
|
|
18
|
+
* path. Consumers only see this interface; the implementation stays private.
|
|
19
|
+
*/
|
|
20
|
+
export interface Workspace {
|
|
21
|
+
/** Stable record id (generated uuid). */
|
|
22
|
+
readonly id: WorkspaceId;
|
|
23
|
+
/**
|
|
24
|
+
* Canonical directory path: the `fs.realpath` of the path given at create
|
|
25
|
+
* time (trailing slashes, `..`, and symlinks all resolved). Never rewritten
|
|
26
|
+
* afterwards, even when the directory disappears (see {@link status}).
|
|
27
|
+
*/
|
|
28
|
+
readonly path: string;
|
|
29
|
+
/** Display title. Defaults to `basename(path)` at create; duplicates are allowed. */
|
|
30
|
+
readonly title: string;
|
|
31
|
+
/** ISO-8601 creation instant, stamped at create and never rewritten. */
|
|
32
|
+
readonly createdAt: string;
|
|
33
|
+
/** ISO-8601 instant of the last durable mutation (create counts as one). */
|
|
34
|
+
readonly updatedAt: string;
|
|
35
|
+
/**
|
|
36
|
+
* Header-validated sessions in manually owned order: a new session is
|
|
37
|
+
* prepended at attach, explicit reordering goes through
|
|
38
|
+
* `insertSessionBefore`, and activity never reorders. The durable candidate
|
|
39
|
+
* account is filtered synchronously: missing headers, invalid cwd values,
|
|
40
|
+
* and canonical cwd mismatches are never returned. A subsequent workspace
|
|
41
|
+
* mutation prunes those filtered candidates durably.
|
|
42
|
+
*/
|
|
43
|
+
readonly sessionIds: readonly SessionId[];
|
|
44
|
+
/**
|
|
45
|
+
* Replace the display title durably.
|
|
46
|
+
* @param title - New title; any string, duplicates across workspaces allowed.
|
|
47
|
+
* @returns resolution after durability.
|
|
48
|
+
*/
|
|
49
|
+
setTitle(title: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Prepend a session to this workspace's candidate account. An already
|
|
52
|
+
* accounted id resolves without writing, aside from the durable
|
|
53
|
+
* filtered-candidate prune every accepted mutation performs. A new id's
|
|
54
|
+
* live or persisted
|
|
55
|
+
* header cwd must resolve to an existing directory equal to {@link path};
|
|
56
|
+
* unknown ids, missing or invalid cwd values, and mismatches reject without
|
|
57
|
+
* writing.
|
|
58
|
+
* @param sessionId - The session to record.
|
|
59
|
+
* @returns resolution after durability.
|
|
60
|
+
*/
|
|
61
|
+
attachSession(sessionId: SessionId): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Move an accounted session within the manual order, DOM-insertBefore-like:
|
|
64
|
+
* with an anchor the session lands before it, without one it appends to the
|
|
65
|
+
* end. Only the moved id changes position. A session or anchor absent from
|
|
66
|
+
* the account rejects without writing; a move to the current position
|
|
67
|
+
* resolves without writing, aside from the durable filtered-candidate
|
|
68
|
+
* prune every accepted mutation performs; decided on the domain write
|
|
69
|
+
* chain.
|
|
70
|
+
* @param sessionId - The accounted session to move.
|
|
71
|
+
* @param beforeSessionId - Accounted anchor to insert before; omitted appends.
|
|
72
|
+
* @returns resolution after durability.
|
|
73
|
+
*/
|
|
74
|
+
insertSessionBefore(sessionId: SessionId, beforeSessionId?: SessionId): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Remove a session from this workspace's account. Idempotent: an id not on
|
|
77
|
+
* the account resolves without writing, aside from the durable
|
|
78
|
+
* filtered-candidate prune every accepted mutation performs; decided on
|
|
79
|
+
* the domain write chain like attach. Never touches the session's own stored log.
|
|
80
|
+
* @param sessionId - The session to remove.
|
|
81
|
+
* @returns resolution after durability.
|
|
82
|
+
*/
|
|
83
|
+
detachSession(sessionId: SessionId): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Live directory check, uncached: whether {@link path} currently exists and
|
|
86
|
+
* is a directory. A missing directory never mutates the record — the
|
|
87
|
+
* directory may only be temporarily moved.
|
|
88
|
+
* @returns `'ok'` when the directory exists, `'missing-dir'` otherwise.
|
|
89
|
+
*/
|
|
90
|
+
status(): Promise<'ok' | 'missing-dir'>;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type vocabulary of the workspace entity: the `WorkspaceId` brand and
|
|
3
|
+
* the `Workspace` consumer interface. Types only — the `WorkspaceId` factory
|
|
4
|
+
* lives in `index.ts` (this file carries no runtime code).
|
|
5
|
+
* @module @prettier-ai/dsh-workspace/src/types
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prettier-ai/dsh-workspace",
|
|
3
|
+
"description": "Workspace entity registry (ctx.workspaceRegistry): durable workspace records with validated session attachment over the domain data form for the DeepSeek Harness",
|
|
4
|
+
"version": "0.1.2-alpha.1",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/workspace/workspace"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./types": {
|
|
26
|
+
"types": "./lib/types/types.d.ts",
|
|
27
|
+
"default": "./lib/types/types.js"
|
|
28
|
+
},
|
|
29
|
+
"./src/*": "./src/*",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"lib/index.js",
|
|
34
|
+
"lib/invariant.js",
|
|
35
|
+
"lib/types/**/*.js",
|
|
36
|
+
"lib/types/**/*.d.ts"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@prettier-ai/dsh-brand": "^0.1.2-alpha.1",
|
|
41
|
+
"@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
|
|
42
|
+
"@prettier-ai/dsh-storage-domain": "^0.1.2-alpha.1",
|
|
43
|
+
"@prettier-ai/dsh-session": "^0.1.2-alpha.1",
|
|
44
|
+
"@prettier-ai/dsh-session-persistence": "^0.1.2-alpha.1",
|
|
45
|
+
"@prettier-ai/dsh-storage": "^0.1.2-alpha.1",
|
|
46
|
+
"@prettier-ai/cordis": "^4.0.1"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"zod": "^4.4.3"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@prettier-ai/dsh-storage-domain": "^0.1.2-alpha.1",
|
|
53
|
+
"@prettier-ai/dsh-brand": "^0.1.2-alpha.1",
|
|
54
|
+
"@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
|
|
55
|
+
"@prettier-ai/dsh-session": "^0.1.2-alpha.1",
|
|
56
|
+
"@prettier-ai/dsh-storage": "^0.1.2-alpha.1",
|
|
57
|
+
"@prettier-ai/dsh-session-persistence": "^0.1.2-alpha.1",
|
|
58
|
+
"@prettier-ai/cordis": "^4.0.1"
|
|
59
|
+
}
|
|
60
|
+
}
|