sh3-core 0.7.5 → 0.8.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/api.d.ts +11 -2
- package/dist/api.js +13 -1
- package/dist/app/store/StoreView.svelte +36 -7
- package/dist/app/store/storeShard.svelte.js +9 -3
- package/dist/app/store/verbs.js +8 -2
- package/dist/apps/lifecycle.d.ts +11 -0
- package/dist/apps/lifecycle.js +21 -1
- package/dist/apps/lifecycle.test.js +50 -1
- package/dist/apps/types.d.ts +7 -2
- package/dist/createShell.d.ts +2 -0
- package/dist/createShell.js +9 -7
- package/dist/documents/handle.js +5 -0
- package/dist/documents/index.d.ts +1 -0
- package/dist/documents/index.js +1 -0
- package/dist/documents/journal-hook.d.ts +6 -0
- package/dist/documents/journal-hook.js +16 -0
- package/dist/documents/sync/activate-integration.test.d.ts +1 -0
- package/dist/documents/sync/activate-integration.test.js +37 -0
- package/dist/documents/sync/components/DocumentSyncExplorer.svelte +99 -0
- package/dist/documents/sync/components/DocumentSyncExplorer.svelte.d.ts +15 -0
- package/dist/documents/sync/components/SyncGrantPicker.svelte +70 -0
- package/dist/documents/sync/components/SyncGrantPicker.svelte.d.ts +12 -0
- package/dist/documents/sync/conflicts.d.ts +30 -0
- package/dist/documents/sync/conflicts.js +77 -0
- package/dist/documents/sync/conflicts.test.d.ts +1 -0
- package/dist/documents/sync/conflicts.test.js +71 -0
- package/dist/documents/sync/engine.d.ts +19 -0
- package/dist/documents/sync/engine.js +188 -0
- package/dist/documents/sync/engine.test.d.ts +1 -0
- package/dist/documents/sync/engine.test.js +169 -0
- package/dist/documents/sync/handle.d.ts +11 -0
- package/dist/documents/sync/handle.js +79 -0
- package/dist/documents/sync/handle.test.d.ts +1 -0
- package/dist/documents/sync/handle.test.js +56 -0
- package/dist/documents/sync/hash.d.ts +1 -0
- package/dist/documents/sync/hash.js +13 -0
- package/dist/documents/sync/hash.test.d.ts +1 -0
- package/dist/documents/sync/hash.test.js +20 -0
- package/dist/documents/sync/index.d.ts +6 -0
- package/dist/documents/sync/index.js +12 -0
- package/dist/documents/sync/journal.d.ts +30 -0
- package/dist/documents/sync/journal.js +179 -0
- package/dist/documents/sync/journal.test.d.ts +1 -0
- package/dist/documents/sync/journal.test.js +87 -0
- package/dist/documents/sync/registry.d.ts +10 -0
- package/dist/documents/sync/registry.js +66 -0
- package/dist/documents/sync/registry.test.d.ts +1 -0
- package/dist/documents/sync/registry.test.js +42 -0
- package/dist/documents/sync/serialization.d.ts +5 -0
- package/dist/documents/sync/serialization.js +24 -0
- package/dist/documents/sync/serialization.test.d.ts +1 -0
- package/dist/documents/sync/serialization.test.js +26 -0
- package/dist/documents/sync/singleton.d.ts +11 -0
- package/dist/documents/sync/singleton.js +26 -0
- package/dist/documents/sync/tombstones.d.ts +19 -0
- package/dist/documents/sync/tombstones.js +58 -0
- package/dist/documents/sync/tombstones.test.d.ts +1 -0
- package/dist/documents/sync/tombstones.test.js +37 -0
- package/dist/documents/sync/types.d.ts +116 -0
- package/dist/documents/sync/types.js +27 -0
- package/dist/documents/sync/write-hook.test.d.ts +1 -0
- package/dist/documents/sync/write-hook.test.js +36 -0
- package/dist/env/client.d.ts +10 -5
- package/dist/env/client.js +12 -4
- package/dist/registry/installer.d.ts +10 -7
- package/dist/registry/installer.js +39 -35
- package/dist/registry/register.d.ts +17 -0
- package/dist/registry/register.js +22 -0
- package/dist/registry/register.test.d.ts +1 -0
- package/dist/registry/register.test.js +28 -0
- package/dist/shards/activate.svelte.js +23 -2
- package/dist/shards/types.d.ts +10 -1
- package/dist/shell-shard/Terminal.svelte +140 -33
- package/dist/shell-shard/Terminal.svelte.d.ts +3 -0
- package/dist/shell-shard/auto-relocate.d.ts +12 -0
- package/dist/shell-shard/auto-relocate.js +20 -0
- package/dist/shell-shard/auto-relocate.test.d.ts +1 -0
- package/dist/shell-shard/auto-relocate.test.js +35 -0
- package/dist/shell-shard/dispatch.d.ts +15 -0
- package/dist/shell-shard/dispatch.js +56 -0
- package/dist/shell-shard/modes/builtin.d.ts +5 -0
- package/dist/shell-shard/modes/builtin.js +18 -0
- package/dist/shell-shard/modes/prefs.d.ts +5 -0
- package/dist/shell-shard/modes/prefs.js +31 -0
- package/dist/shell-shard/modes/prefs.test.d.ts +1 -0
- package/dist/shell-shard/modes/prefs.test.js +46 -0
- package/dist/shell-shard/modes/registry.d.ts +7 -0
- package/dist/shell-shard/modes/registry.js +27 -0
- package/dist/shell-shard/modes/registry.test.d.ts +1 -0
- package/dist/shell-shard/modes/registry.test.js +35 -0
- package/dist/shell-shard/modes/types.d.ts +8 -0
- package/dist/shell-shard/modes/types.js +1 -0
- package/dist/shell-shard/protocol.d.ts +6 -0
- package/dist/shell-shard/shellShard.svelte.js +5 -1
- package/dist/shell-shard/tenant-fs-client.d.ts +24 -0
- package/dist/shell-shard/tenant-fs-client.js +44 -0
- package/dist/shell-shard/tenant-fs-client.test.d.ts +1 -0
- package/dist/shell-shard/tenant-fs-client.test.js +49 -0
- package/dist/shell-shard/terminal-dispatch.test.d.ts +1 -0
- package/dist/shell-shard/terminal-dispatch.test.js +53 -0
- package/dist/shell-shard/toolbar/Toolbar.svelte +62 -0
- package/dist/shell-shard/toolbar/Toolbar.svelte.d.ts +11 -0
- package/dist/shell-shard/toolbar/slots/FocusLockSlot.svelte +28 -0
- package/dist/shell-shard/toolbar/slots/FocusLockSlot.svelte.d.ts +7 -0
- package/dist/shell-shard/toolbar/slots/ModeSlot.svelte +102 -0
- package/dist/shell-shard/toolbar/slots/ModeSlot.svelte.d.ts +11 -0
- package/dist/shell-shard/toolbar/slots/TargetShardSlot.svelte +17 -0
- package/dist/shell-shard/toolbar/slots/TargetShardSlot.svelte.d.ts +6 -0
- package/dist/shell-shard/toolbar/slots.d.ts +17 -0
- package/dist/shell-shard/toolbar/slots.js +26 -0
- package/dist/shell-shard/toolbar/slots.test.d.ts +1 -0
- package/dist/shell-shard/toolbar/slots.test.js +28 -0
- package/dist/shell-shard/verbs/cat.d.ts +2 -0
- package/dist/shell-shard/verbs/cat.js +34 -0
- package/dist/shell-shard/verbs/cd.test.d.ts +1 -0
- package/dist/shell-shard/verbs/cd.test.js +56 -0
- package/dist/shell-shard/verbs/env.d.ts +2 -0
- package/dist/shell-shard/verbs/env.js +14 -0
- package/dist/shell-shard/verbs/index.js +6 -1
- package/dist/shell-shard/verbs/ls.d.ts +2 -0
- package/dist/shell-shard/verbs/ls.js +29 -0
- package/dist/shell-shard/verbs/ls.test.d.ts +1 -0
- package/dist/shell-shard/verbs/ls.test.js +49 -0
- package/dist/shell-shard/verbs/session.d.ts +0 -1
- package/dist/shell-shard/verbs/session.js +58 -26
- package/dist/verbs/types.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { cdVerb } from './session';
|
|
3
|
+
function makeCtx(initialCwd, fsStat) {
|
|
4
|
+
const pushed = [];
|
|
5
|
+
const session = { cwd: initialCwd };
|
|
6
|
+
const ctx = {
|
|
7
|
+
shell: {},
|
|
8
|
+
scrollback: { push: (e) => pushed.push(e) },
|
|
9
|
+
session,
|
|
10
|
+
cwd: initialCwd,
|
|
11
|
+
fs: {
|
|
12
|
+
list: async () => [],
|
|
13
|
+
stat: fsStat,
|
|
14
|
+
read: async () => '',
|
|
15
|
+
},
|
|
16
|
+
dispatch: async () => { },
|
|
17
|
+
};
|
|
18
|
+
return { ctx, session, pushed };
|
|
19
|
+
}
|
|
20
|
+
describe('cd verb', () => {
|
|
21
|
+
it('updates session.cwd on successful dir stat', async () => {
|
|
22
|
+
const { ctx, session } = makeCtx('', async (_path) => ({
|
|
23
|
+
name: 'projects',
|
|
24
|
+
kind: 'dir',
|
|
25
|
+
size: 0,
|
|
26
|
+
mtime: 0,
|
|
27
|
+
}));
|
|
28
|
+
await cdVerb.run(ctx, ['projects']);
|
|
29
|
+
expect(session.cwd).toBe('projects');
|
|
30
|
+
});
|
|
31
|
+
it('pushes error when target is a file, not a directory', async () => {
|
|
32
|
+
const { ctx, session, pushed } = makeCtx('', async (_path) => ({
|
|
33
|
+
name: 'readme.txt',
|
|
34
|
+
kind: 'file',
|
|
35
|
+
size: 100,
|
|
36
|
+
mtime: 0,
|
|
37
|
+
}));
|
|
38
|
+
await cdVerb.run(ctx, ['readme.txt']);
|
|
39
|
+
expect(session.cwd).toBe('');
|
|
40
|
+
const entry = pushed[0];
|
|
41
|
+
expect(entry.kind).toBe('status');
|
|
42
|
+
expect(entry.level).toBe('error');
|
|
43
|
+
expect(entry.text).toMatch(/not a directory/);
|
|
44
|
+
});
|
|
45
|
+
it('pushes error when fs.stat throws', async () => {
|
|
46
|
+
const { ctx, session, pushed } = makeCtx('', async (_path) => {
|
|
47
|
+
throw new Error('no such file');
|
|
48
|
+
});
|
|
49
|
+
await cdVerb.run(ctx, ['nonexistent']);
|
|
50
|
+
expect(session.cwd).toBe('');
|
|
51
|
+
const entry = pushed[0];
|
|
52
|
+
expect(entry.kind).toBe('status');
|
|
53
|
+
expect(entry.level).toBe('error');
|
|
54
|
+
expect(entry.text).toMatch(/no such file/);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import EnvTable from '../rich/EnvTable.svelte';
|
|
2
|
+
export const envVerb = {
|
|
3
|
+
name: 'env',
|
|
4
|
+
summary: 'Show the session environment.',
|
|
5
|
+
async run(ctx) {
|
|
6
|
+
const env = ctx.session.env;
|
|
7
|
+
ctx.scrollback.push({
|
|
8
|
+
kind: 'rich',
|
|
9
|
+
component: EnvTable,
|
|
10
|
+
props: { data: { env } },
|
|
11
|
+
ts: Date.now(),
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -9,7 +9,10 @@ import { appsVerb, appVerb } from './apps';
|
|
|
9
9
|
import { shardsVerb } from './shards';
|
|
10
10
|
import { viewsVerb, openVerb, closeVerb } from './views';
|
|
11
11
|
import { zonesVerb, zoneVerb } from './zones';
|
|
12
|
-
import { pwdVerb, cdVerb,
|
|
12
|
+
import { pwdVerb, cdVerb, whoamiVerb } from './session';
|
|
13
|
+
import { envVerb } from './env';
|
|
14
|
+
import { lsVerb } from './ls';
|
|
15
|
+
import { catVerb } from './cat';
|
|
13
16
|
export function registerV1Verbs(ctx) {
|
|
14
17
|
ctx.registerVerb(makeHelpVerb());
|
|
15
18
|
ctx.registerVerb(clearVerb);
|
|
@@ -26,4 +29,6 @@ export function registerV1Verbs(ctx) {
|
|
|
26
29
|
ctx.registerVerb(cdVerb);
|
|
27
30
|
ctx.registerVerb(envVerb);
|
|
28
31
|
ctx.registerVerb(whoamiVerb);
|
|
32
|
+
ctx.registerVerb(lsVerb);
|
|
33
|
+
ctx.registerVerb(catVerb);
|
|
29
34
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const lsVerb = {
|
|
2
|
+
name: 'ls',
|
|
3
|
+
summary: 'List the current directory.',
|
|
4
|
+
async run(ctx) {
|
|
5
|
+
let entries;
|
|
6
|
+
try {
|
|
7
|
+
entries = await ctx.fs.list(ctx.session.cwd);
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
ctx.scrollback.push({
|
|
11
|
+
kind: 'status',
|
|
12
|
+
text: `ls: ${err.message}`,
|
|
13
|
+
level: 'error',
|
|
14
|
+
ts: Date.now(),
|
|
15
|
+
});
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const lines = entries
|
|
19
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
20
|
+
.map((e) => (e.kind === 'dir' ? `${e.name}/` : e.name))
|
|
21
|
+
.join('\n');
|
|
22
|
+
ctx.scrollback.push({
|
|
23
|
+
kind: 'text',
|
|
24
|
+
stream: 'stdout',
|
|
25
|
+
chunks: [lines || '(empty)'],
|
|
26
|
+
ts: Date.now(),
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { lsVerb } from './ls';
|
|
3
|
+
function makeCtx(cwd, fsList) {
|
|
4
|
+
const pushed = [];
|
|
5
|
+
const ctx = {
|
|
6
|
+
shell: {},
|
|
7
|
+
scrollback: { push: (e) => pushed.push(e) },
|
|
8
|
+
session: { cwd },
|
|
9
|
+
cwd,
|
|
10
|
+
fs: {
|
|
11
|
+
list: fsList,
|
|
12
|
+
stat: async () => ({ kind: 'dir', name: '', size: 0, mtime: 0 }),
|
|
13
|
+
read: async () => '',
|
|
14
|
+
},
|
|
15
|
+
dispatch: async () => { },
|
|
16
|
+
};
|
|
17
|
+
return { ctx, pushed };
|
|
18
|
+
}
|
|
19
|
+
describe('ls verb', () => {
|
|
20
|
+
it('lists entries sorted, dirs with trailing slash', async () => {
|
|
21
|
+
const { ctx, pushed } = makeCtx('', async () => [
|
|
22
|
+
{ name: 'zebra.txt', kind: 'file', size: 1, mtime: 0 },
|
|
23
|
+
{ name: 'apple', kind: 'dir', size: 0, mtime: 0 },
|
|
24
|
+
{ name: 'mango.txt', kind: 'file', size: 2, mtime: 0 },
|
|
25
|
+
]);
|
|
26
|
+
await lsVerb.run(ctx, []);
|
|
27
|
+
expect(pushed).toHaveLength(1);
|
|
28
|
+
const entry = pushed[0];
|
|
29
|
+
expect(entry.kind).toBe('text');
|
|
30
|
+
expect(entry.chunks[0]).toBe('apple/\nmango.txt\nzebra.txt');
|
|
31
|
+
});
|
|
32
|
+
it('shows (empty) for empty directory', async () => {
|
|
33
|
+
const { ctx, pushed } = makeCtx('', async () => []);
|
|
34
|
+
await lsVerb.run(ctx, []);
|
|
35
|
+
const entry = pushed[0];
|
|
36
|
+
expect(entry.kind).toBe('text');
|
|
37
|
+
expect(entry.chunks[0]).toBe('(empty)');
|
|
38
|
+
});
|
|
39
|
+
it('pushes status error when fs.list throws', async () => {
|
|
40
|
+
const { ctx, pushed } = makeCtx('', async () => {
|
|
41
|
+
throw new Error('permission denied');
|
|
42
|
+
});
|
|
43
|
+
await lsVerb.run(ctx, []);
|
|
44
|
+
const entry = pushed[0];
|
|
45
|
+
expect(entry.kind).toBe('status');
|
|
46
|
+
expect(entry.level).toBe('error');
|
|
47
|
+
expect(entry.text).toMatch(/permission denied/);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -1,18 +1,47 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Session verbs: pwd, cd,
|
|
2
|
+
* Session verbs: pwd, cd, whoami.
|
|
3
3
|
*
|
|
4
|
-
* cd
|
|
5
|
-
*
|
|
4
|
+
* cd validates the target path via ctx.fs.stat() then updates session.cwd
|
|
5
|
+
* locally. The server still owns the authoritative cwd for forwarded
|
|
6
|
+
* commands, but local verbs track it via session.cwd.
|
|
7
|
+
*
|
|
8
|
+
* envVerb lives in env.ts (separate Svelte import).
|
|
6
9
|
*/
|
|
7
|
-
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Path helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
function joinRel(cwd, input) {
|
|
14
|
+
if (input.startsWith('/'))
|
|
15
|
+
return normalizeRel(input.slice(1));
|
|
16
|
+
const parts = [...cwd.split('/').filter(Boolean), ...input.split('/')];
|
|
17
|
+
const stack = [];
|
|
18
|
+
for (const p of parts) {
|
|
19
|
+
if (p === '' || p === '.')
|
|
20
|
+
continue;
|
|
21
|
+
if (p === '..') {
|
|
22
|
+
stack.pop();
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
stack.push(p);
|
|
26
|
+
}
|
|
27
|
+
return stack.join('/');
|
|
28
|
+
}
|
|
29
|
+
function normalizeRel(s) {
|
|
30
|
+
return s.split('/').filter((p) => p && p !== '.').join('/');
|
|
31
|
+
}
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Verbs
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
8
35
|
export const pwdVerb = {
|
|
9
36
|
name: 'pwd',
|
|
10
|
-
summary: 'Show the
|
|
37
|
+
summary: 'Show the current working directory.',
|
|
11
38
|
async run(ctx) {
|
|
39
|
+
const rel = ctx.session.cwd || '';
|
|
40
|
+
const display = rel.startsWith('/') ? rel : `/${rel}`;
|
|
12
41
|
ctx.scrollback.push({
|
|
13
42
|
kind: 'text',
|
|
14
43
|
stream: 'stdout',
|
|
15
|
-
chunks: [
|
|
44
|
+
chunks: [display + '\n'],
|
|
16
45
|
ts: Date.now(),
|
|
17
46
|
});
|
|
18
47
|
},
|
|
@@ -22,32 +51,35 @@ export const cdVerb = {
|
|
|
22
51
|
summary: 'Change the session working directory.',
|
|
23
52
|
async run(ctx, args) {
|
|
24
53
|
var _a;
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
54
|
+
const target = (_a = args[0]) !== null && _a !== void 0 ? _a : '';
|
|
55
|
+
if (target === '') {
|
|
56
|
+
ctx.session.cwd = '';
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const base = ctx.session.cwd;
|
|
60
|
+
const candidate = joinRel(base, target);
|
|
61
|
+
try {
|
|
62
|
+
const s = await ctx.fs.stat(candidate);
|
|
63
|
+
if (s.kind !== 'dir') {
|
|
64
|
+
ctx.scrollback.push({
|
|
65
|
+
kind: 'status',
|
|
66
|
+
text: `cd: not a directory: ${target}`,
|
|
67
|
+
level: 'error',
|
|
68
|
+
ts: Date.now(),
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
27
74
|
ctx.scrollback.push({
|
|
28
75
|
kind: 'status',
|
|
29
|
-
text:
|
|
30
|
-
level: '
|
|
76
|
+
text: `cd: ${err.message}`,
|
|
77
|
+
level: 'error',
|
|
31
78
|
ts: Date.now(),
|
|
32
79
|
});
|
|
33
80
|
return;
|
|
34
81
|
}
|
|
35
|
-
|
|
36
|
-
// dispatcher can intercept it and update session.cwd (see ws.ts).
|
|
37
|
-
ctx.session.send({ t: 'submit', line: `cd ${path}` });
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
export const envVerb = {
|
|
41
|
-
name: 'env',
|
|
42
|
-
summary: 'Show the session environment.',
|
|
43
|
-
async run(ctx) {
|
|
44
|
-
const env = ctx.session.env;
|
|
45
|
-
ctx.scrollback.push({
|
|
46
|
-
kind: 'rich',
|
|
47
|
-
component: EnvTable,
|
|
48
|
-
props: { data: { env } },
|
|
49
|
-
ts: Date.now(),
|
|
50
|
-
});
|
|
82
|
+
ctx.session.cwd = candidate;
|
|
51
83
|
},
|
|
52
84
|
};
|
|
53
85
|
export const whoamiVerb = {
|
package/dist/verbs/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Scrollback } from '../shell-shard/scrollback.svelte';
|
|
2
2
|
import type { SessionClient } from '../shell-shard/session-client.svelte';
|
|
3
|
+
import type { TenantFsClient } from '../shell-shard/tenant-fs-client';
|
|
3
4
|
export interface ShellApi {
|
|
4
5
|
listApps(): Array<{
|
|
5
6
|
id: string;
|
|
@@ -43,6 +44,7 @@ export interface VerbContext {
|
|
|
43
44
|
scrollback: Scrollback;
|
|
44
45
|
session: SessionClient;
|
|
45
46
|
cwd: string;
|
|
47
|
+
fs: TenantFsClient;
|
|
46
48
|
/** Invoke another registered verb programmatically (used by rich-entry clicks). */
|
|
47
49
|
dispatch(line: string): Promise<void>;
|
|
48
50
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export declare const VERSION = "0.
|
|
2
|
+
export declare const VERSION = "0.8.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export const VERSION = '0.
|
|
2
|
+
export const VERSION = '0.8.0';
|