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.
Files changed (129) hide show
  1. package/dist/api.d.ts +11 -2
  2. package/dist/api.js +13 -1
  3. package/dist/app/store/StoreView.svelte +36 -7
  4. package/dist/app/store/storeShard.svelte.js +9 -3
  5. package/dist/app/store/verbs.js +8 -2
  6. package/dist/apps/lifecycle.d.ts +11 -0
  7. package/dist/apps/lifecycle.js +21 -1
  8. package/dist/apps/lifecycle.test.js +50 -1
  9. package/dist/apps/types.d.ts +7 -2
  10. package/dist/createShell.d.ts +2 -0
  11. package/dist/createShell.js +9 -7
  12. package/dist/documents/handle.js +5 -0
  13. package/dist/documents/index.d.ts +1 -0
  14. package/dist/documents/index.js +1 -0
  15. package/dist/documents/journal-hook.d.ts +6 -0
  16. package/dist/documents/journal-hook.js +16 -0
  17. package/dist/documents/sync/activate-integration.test.d.ts +1 -0
  18. package/dist/documents/sync/activate-integration.test.js +37 -0
  19. package/dist/documents/sync/components/DocumentSyncExplorer.svelte +99 -0
  20. package/dist/documents/sync/components/DocumentSyncExplorer.svelte.d.ts +15 -0
  21. package/dist/documents/sync/components/SyncGrantPicker.svelte +70 -0
  22. package/dist/documents/sync/components/SyncGrantPicker.svelte.d.ts +12 -0
  23. package/dist/documents/sync/conflicts.d.ts +30 -0
  24. package/dist/documents/sync/conflicts.js +77 -0
  25. package/dist/documents/sync/conflicts.test.d.ts +1 -0
  26. package/dist/documents/sync/conflicts.test.js +71 -0
  27. package/dist/documents/sync/engine.d.ts +19 -0
  28. package/dist/documents/sync/engine.js +188 -0
  29. package/dist/documents/sync/engine.test.d.ts +1 -0
  30. package/dist/documents/sync/engine.test.js +169 -0
  31. package/dist/documents/sync/handle.d.ts +11 -0
  32. package/dist/documents/sync/handle.js +79 -0
  33. package/dist/documents/sync/handle.test.d.ts +1 -0
  34. package/dist/documents/sync/handle.test.js +56 -0
  35. package/dist/documents/sync/hash.d.ts +1 -0
  36. package/dist/documents/sync/hash.js +13 -0
  37. package/dist/documents/sync/hash.test.d.ts +1 -0
  38. package/dist/documents/sync/hash.test.js +20 -0
  39. package/dist/documents/sync/index.d.ts +6 -0
  40. package/dist/documents/sync/index.js +12 -0
  41. package/dist/documents/sync/journal.d.ts +30 -0
  42. package/dist/documents/sync/journal.js +179 -0
  43. package/dist/documents/sync/journal.test.d.ts +1 -0
  44. package/dist/documents/sync/journal.test.js +87 -0
  45. package/dist/documents/sync/registry.d.ts +10 -0
  46. package/dist/documents/sync/registry.js +66 -0
  47. package/dist/documents/sync/registry.test.d.ts +1 -0
  48. package/dist/documents/sync/registry.test.js +42 -0
  49. package/dist/documents/sync/serialization.d.ts +5 -0
  50. package/dist/documents/sync/serialization.js +24 -0
  51. package/dist/documents/sync/serialization.test.d.ts +1 -0
  52. package/dist/documents/sync/serialization.test.js +26 -0
  53. package/dist/documents/sync/singleton.d.ts +11 -0
  54. package/dist/documents/sync/singleton.js +26 -0
  55. package/dist/documents/sync/tombstones.d.ts +19 -0
  56. package/dist/documents/sync/tombstones.js +58 -0
  57. package/dist/documents/sync/tombstones.test.d.ts +1 -0
  58. package/dist/documents/sync/tombstones.test.js +37 -0
  59. package/dist/documents/sync/types.d.ts +116 -0
  60. package/dist/documents/sync/types.js +27 -0
  61. package/dist/documents/sync/write-hook.test.d.ts +1 -0
  62. package/dist/documents/sync/write-hook.test.js +36 -0
  63. package/dist/env/client.d.ts +10 -5
  64. package/dist/env/client.js +12 -4
  65. package/dist/registry/installer.d.ts +10 -7
  66. package/dist/registry/installer.js +39 -35
  67. package/dist/registry/register.d.ts +17 -0
  68. package/dist/registry/register.js +22 -0
  69. package/dist/registry/register.test.d.ts +1 -0
  70. package/dist/registry/register.test.js +28 -0
  71. package/dist/shards/activate.svelte.js +23 -2
  72. package/dist/shards/types.d.ts +10 -1
  73. package/dist/shell-shard/Terminal.svelte +140 -33
  74. package/dist/shell-shard/Terminal.svelte.d.ts +3 -0
  75. package/dist/shell-shard/auto-relocate.d.ts +12 -0
  76. package/dist/shell-shard/auto-relocate.js +20 -0
  77. package/dist/shell-shard/auto-relocate.test.d.ts +1 -0
  78. package/dist/shell-shard/auto-relocate.test.js +35 -0
  79. package/dist/shell-shard/dispatch.d.ts +15 -0
  80. package/dist/shell-shard/dispatch.js +56 -0
  81. package/dist/shell-shard/modes/builtin.d.ts +5 -0
  82. package/dist/shell-shard/modes/builtin.js +18 -0
  83. package/dist/shell-shard/modes/prefs.d.ts +5 -0
  84. package/dist/shell-shard/modes/prefs.js +31 -0
  85. package/dist/shell-shard/modes/prefs.test.d.ts +1 -0
  86. package/dist/shell-shard/modes/prefs.test.js +46 -0
  87. package/dist/shell-shard/modes/registry.d.ts +7 -0
  88. package/dist/shell-shard/modes/registry.js +27 -0
  89. package/dist/shell-shard/modes/registry.test.d.ts +1 -0
  90. package/dist/shell-shard/modes/registry.test.js +35 -0
  91. package/dist/shell-shard/modes/types.d.ts +8 -0
  92. package/dist/shell-shard/modes/types.js +1 -0
  93. package/dist/shell-shard/protocol.d.ts +6 -0
  94. package/dist/shell-shard/shellShard.svelte.js +5 -1
  95. package/dist/shell-shard/tenant-fs-client.d.ts +24 -0
  96. package/dist/shell-shard/tenant-fs-client.js +44 -0
  97. package/dist/shell-shard/tenant-fs-client.test.d.ts +1 -0
  98. package/dist/shell-shard/tenant-fs-client.test.js +49 -0
  99. package/dist/shell-shard/terminal-dispatch.test.d.ts +1 -0
  100. package/dist/shell-shard/terminal-dispatch.test.js +53 -0
  101. package/dist/shell-shard/toolbar/Toolbar.svelte +62 -0
  102. package/dist/shell-shard/toolbar/Toolbar.svelte.d.ts +11 -0
  103. package/dist/shell-shard/toolbar/slots/FocusLockSlot.svelte +28 -0
  104. package/dist/shell-shard/toolbar/slots/FocusLockSlot.svelte.d.ts +7 -0
  105. package/dist/shell-shard/toolbar/slots/ModeSlot.svelte +102 -0
  106. package/dist/shell-shard/toolbar/slots/ModeSlot.svelte.d.ts +11 -0
  107. package/dist/shell-shard/toolbar/slots/TargetShardSlot.svelte +17 -0
  108. package/dist/shell-shard/toolbar/slots/TargetShardSlot.svelte.d.ts +6 -0
  109. package/dist/shell-shard/toolbar/slots.d.ts +17 -0
  110. package/dist/shell-shard/toolbar/slots.js +26 -0
  111. package/dist/shell-shard/toolbar/slots.test.d.ts +1 -0
  112. package/dist/shell-shard/toolbar/slots.test.js +28 -0
  113. package/dist/shell-shard/verbs/cat.d.ts +2 -0
  114. package/dist/shell-shard/verbs/cat.js +34 -0
  115. package/dist/shell-shard/verbs/cd.test.d.ts +1 -0
  116. package/dist/shell-shard/verbs/cd.test.js +56 -0
  117. package/dist/shell-shard/verbs/env.d.ts +2 -0
  118. package/dist/shell-shard/verbs/env.js +14 -0
  119. package/dist/shell-shard/verbs/index.js +6 -1
  120. package/dist/shell-shard/verbs/ls.d.ts +2 -0
  121. package/dist/shell-shard/verbs/ls.js +29 -0
  122. package/dist/shell-shard/verbs/ls.test.d.ts +1 -0
  123. package/dist/shell-shard/verbs/ls.test.js +49 -0
  124. package/dist/shell-shard/verbs/session.d.ts +0 -1
  125. package/dist/shell-shard/verbs/session.js +58 -26
  126. package/dist/verbs/types.d.ts +2 -0
  127. package/dist/version.d.ts +1 -1
  128. package/dist/version.js +1 -1
  129. 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,2 @@
1
+ import type { Verb } from '../../verbs/types';
2
+ export declare const envVerb: Verb;
@@ -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, envVerb, whoamiVerb } from './session';
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,2 @@
1
+ import type { Verb } from '../../verbs/types';
2
+ export declare const lsVerb: Verb;
@@ -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,5 +1,4 @@
1
1
  import type { Verb } from '../../verbs/types';
2
2
  export declare const pwdVerb: Verb;
3
3
  export declare const cdVerb: Verb;
4
- export declare const envVerb: Verb;
5
4
  export declare const whoamiVerb: Verb;
@@ -1,18 +1,47 @@
1
1
  /*
2
- * Session verbs: pwd, cd, env, whoami.
2
+ * Session verbs: pwd, cd, whoami.
3
3
  *
4
- * cd is server-authoritative — the client forwards it as a 'submit' message
5
- * so ws.ts can intercept and update session.cwd without spawning a process.
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
- import EnvTable from '../rich/EnvTable.svelte';
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 server session cwd.',
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: [ctx.cwd + '\n'],
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 path = (_a = args[0]) !== null && _a !== void 0 ? _a : '';
26
- if (!path) {
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: 'usage: cd <path>',
30
- level: 'warn',
76
+ text: `cd: ${err.message}`,
77
+ level: 'error',
31
78
  ts: Date.now(),
32
79
  });
33
80
  return;
34
81
  }
35
- // cd is server-authoritative. Forward the submit so the server's
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 = {
@@ -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.7.5";
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.7.5';
2
+ export const VERSION = '0.8.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh3-core",
3
- "version": "0.7.5",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"