windows-development-sandbox 1.12.0 → 1.13.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 (160) hide show
  1. package/README.md +1 -0
  2. package/config.example.json +11 -1
  3. package/dist/app/features/access/access.def.d.ts +190 -0
  4. package/dist/app/features/access/access.def.js +273 -0
  5. package/dist/app/features/access/access.run.d.ts +79 -0
  6. package/dist/app/features/access/access.run.js +129 -0
  7. package/dist/app/features/access/index.d.ts +2 -0
  8. package/dist/app/features/access/index.js +3 -0
  9. package/dist/app/features/agents/agent-entry.js +5 -0
  10. package/dist/app/features/agents/agents.def.js +1 -0
  11. package/dist/app/features/apps/apps.def.js +1 -0
  12. package/dist/app/features/audit/audit.def.d.ts +11 -2
  13. package/dist/app/features/audit/audit.def.js +7 -4
  14. package/dist/app/features/audit/audit.run.d.ts +3 -0
  15. package/dist/app/features/defs.d.ts +155 -3
  16. package/dist/app/features/defs.js +12 -1
  17. package/dist/app/features/files/files.def.js +1 -0
  18. package/dist/app/features/health/health.def.js +1 -0
  19. package/dist/app/features/sandbox/sandbox.def.js +3 -0
  20. package/dist/app/features/sessions/sessions.def.d.ts +36 -0
  21. package/dist/app/features/sessions/sessions.def.js +77 -9
  22. package/dist/app/features/sessions/sessions.run.d.ts +25 -2
  23. package/dist/app/features/sessions/sessions.run.js +71 -3
  24. package/dist/app/features/settings/settings.def.d.ts +8 -0
  25. package/dist/app/features/settings/settings.def.js +17 -0
  26. package/dist/app/features/web/web.def.d.ts +9 -0
  27. package/dist/app/features/web/web.def.js +26 -1
  28. package/dist/app/features/web/web.run.d.ts +3 -0
  29. package/dist/app/features/web/web.run.js +11 -3
  30. package/dist/app/kernel/access.d.ts +33 -0
  31. package/dist/app/kernel/access.js +44 -0
  32. package/dist/app/kernel/define.d.ts +14 -1
  33. package/dist/app/kernel/index.d.ts +1 -0
  34. package/dist/app/kernel/index.js +1 -0
  35. package/dist/app/kernel/rpc.d.ts +5 -2
  36. package/dist/app/registry.d.ts +4 -1
  37. package/dist/app/registry.js +24 -4
  38. package/dist/cli/commands/registry.js +2 -0
  39. package/dist/cli/commands/remote.d.ts +2 -0
  40. package/dist/cli/commands/remote.js +49 -0
  41. package/dist/cli/commands/sessions/reopen.js +3 -0
  42. package/dist/cli/commands/web.js +66 -0
  43. package/dist/cli/manifest.d.ts +63 -4
  44. package/dist/cli/manifest.js +33 -4
  45. package/dist/cli/menu/io-menu.d.ts +5 -3
  46. package/dist/cli/menu/io-menu.js +12 -5
  47. package/dist/cli/menu/remote-menu.d.ts +19 -0
  48. package/dist/cli/menu/remote-menu.js +108 -0
  49. package/dist/cli/menu/views.js +2 -0
  50. package/dist/cli/operations/handler.js +15 -1
  51. package/dist/cli/services/remote-client.d.ts +16 -0
  52. package/dist/cli/services/remote-client.js +141 -0
  53. package/dist/cli/services/sessions-service.d.ts +5 -0
  54. package/dist/cli/services/sessions-service.js +17 -1
  55. package/dist/core/config/defaults.js +11 -1
  56. package/dist/core/local-config/file.js +16 -2
  57. package/dist/core/local-config/overrides.js +7 -0
  58. package/dist/core/run/versions.js +9 -8
  59. package/dist/core/schema/config.d.ts +22 -3
  60. package/dist/core/schema/config.js +39 -2
  61. package/dist/core/schema/web.d.ts +51 -0
  62. package/dist/core/schema/web.js +46 -1
  63. package/dist/core/util/fs.d.ts +5 -0
  64. package/dist/core/util/fs.js +23 -0
  65. package/dist/core/util/index.d.ts +1 -1
  66. package/dist/core/util/index.js +1 -1
  67. package/dist/guest/daemon/daemon.js +2 -0
  68. package/dist/guest/daemon/idle-shutdown.js +2 -1
  69. package/dist/guest/daemon/sessions.d.ts +16 -2
  70. package/dist/guest/daemon/sessions.js +126 -20
  71. package/dist/guest/git/decision.js +7 -0
  72. package/dist/guest/sessions/cmdline.d.ts +14 -0
  73. package/dist/guest/sessions/cmdline.js +10 -0
  74. package/dist/guest/toolchains/setup.js +3 -0
  75. package/dist/host/audit/log.d.ts +3 -2
  76. package/dist/host/audit/log.js +3 -3
  77. package/dist/host/channel/channel.d.ts +5 -1
  78. package/dist/host/channel/channel.js +12 -0
  79. package/dist/host/guard/agent-files.js +7 -1
  80. package/dist/host/guard/commands.js +13 -1
  81. package/dist/host/guard/git-config.js +27 -7
  82. package/dist/host/guard/git-dirs.js +78 -3
  83. package/dist/host/guard/mark-of-the-web.js +3 -1
  84. package/dist/host/guard/nested-repos.js +7 -2
  85. package/dist/host/guard/store.js +1 -0
  86. package/dist/host/guard/types.d.ts +2 -0
  87. package/dist/host/launch/mappings.js +11 -1
  88. package/dist/host/launch/project-scan.js +2 -21
  89. package/dist/host/launch/stage.js +4 -0
  90. package/dist/host/life/session-ledger.js +2 -1
  91. package/dist/host/remote/access-store.d.ts +156 -0
  92. package/dist/host/remote/access-store.js +308 -0
  93. package/dist/host/remote/cert.d.ts +20 -0
  94. package/dist/host/remote/cert.js +103 -0
  95. package/dist/host/remote/door-client.d.ts +13 -0
  96. package/dist/host/remote/door-client.js +54 -0
  97. package/dist/host/remote/firewall.d.ts +44 -0
  98. package/dist/host/remote/firewall.js +118 -0
  99. package/dist/host/remote/gate.d.ts +125 -0
  100. package/dist/host/remote/gate.js +391 -0
  101. package/dist/host/remote/index.d.ts +8 -0
  102. package/dist/host/remote/index.js +10 -0
  103. package/dist/host/remote/lan.d.ts +24 -0
  104. package/dist/host/remote/lan.js +88 -0
  105. package/dist/host/remote/tls-store.d.ts +22 -0
  106. package/dist/host/remote/tls-store.js +63 -0
  107. package/dist/host/remote/windows-helper.d.ts +67 -0
  108. package/dist/host/remote/windows-helper.js +129 -0
  109. package/dist/host/web/bridge.d.ts +5 -1
  110. package/dist/host/web/bridge.js +6 -2
  111. package/dist/host/web/browser.js +1 -1
  112. package/dist/host/web/control.d.ts +4 -1
  113. package/dist/host/web/control.js +28 -4
  114. package/dist/host/web/rpc.d.ts +16 -2
  115. package/dist/host/web/rpc.js +37 -4
  116. package/dist/host/web/runner.d.ts +4 -0
  117. package/dist/host/web/runner.js +12 -0
  118. package/dist/host/web/server.d.ts +18 -2
  119. package/dist/host/web/server.js +97 -24
  120. package/dist/host/web/state.d.ts +5 -0
  121. package/dist/host/web/state.js +8 -0
  122. package/dist/runtime/agent.js +74 -3
  123. package/dist/runtime/daemon.js +205 -24
  124. package/dist/runtime/db.js +74 -3
  125. package/dist/runtime/gitguard.js +6 -0
  126. package/dist/runtime/main.js +77 -3
  127. package/dist/runtime/netproxy.js +74 -3
  128. package/dist/runtime/view.js +76 -3
  129. package/dist/selftest/app.test.js +41 -6
  130. package/dist/selftest/channel.test.js +16 -0
  131. package/dist/selftest/cmdline.test.js +16 -0
  132. package/dist/selftest/git-access.test.js +8 -0
  133. package/dist/selftest/hostile-input.test.js +12 -1
  134. package/dist/selftest/index.js +2 -0
  135. package/dist/selftest/live-daemon.test.js +25 -0
  136. package/dist/selftest/local-config.test.js +30 -0
  137. package/dist/selftest/remote.test.d.ts +2 -0
  138. package/dist/selftest/remote.test.js +536 -0
  139. package/dist/selftest/repo-guard.test.js +56 -2
  140. package/dist/selftest/resume.test.js +2 -1
  141. package/dist/selftest/sharing.test.js +9 -2
  142. package/dist/selftest/support/fixtures.d.ts +13 -0
  143. package/dist/selftest/support/web-preview.js +63 -0
  144. package/dist/selftest/web.test.js +2 -0
  145. package/dist/shared/channel/schema.d.ts +23 -0
  146. package/dist/shared/channel/schema.js +8 -0
  147. package/dist/shared/pty/protocol.d.ts +2 -0
  148. package/dist/shared/pty/protocol.js +2 -0
  149. package/dist/web/index.html +1 -1
  150. package/dist/web/main-Z2WYAE2Z.js +221 -0
  151. package/native/csharp/HostTools/Program.cs +17 -1
  152. package/native/csharp/HostTools/WebHelper.cs +499 -0
  153. package/native/scripts/get-network-profile.ps1 +19 -0
  154. package/native/scripts/set-folder-access.ps1 +11 -0
  155. package/native/scripts/set-web-firewall.ps1 +103 -0
  156. package/package.json +1 -1
  157. package/sandbox/csharp/PtyHost.cs +20 -0
  158. package/schemas/config.schema.json +65 -0
  159. package/schemas/sandbox-config.schema.json +0 -18
  160. package/dist/web/main-UD4QCXZR.js +0 -219
@@ -0,0 +1,129 @@
1
+ // Remote access's work (docs/design/remote-access.md): the access list in <storage>\web\access.json (shared with the
2
+ // running web server, which reads it again when it changes and closes the pages of whoever was signed out), the
3
+ // remote settings, the firewall rule and the network check of LAN mode. The one-time code is returned once and only
4
+ // its hash is kept.
5
+ import { requireOnPc } from '../../../cli/commands/context.js';
6
+ import { fail } from '../../../core/util/index.js';
7
+ import { AccessStore, addRuleRequest, applyFirewall, chooseLanAddress, lanUrl, networkProblem, networkProfile, removeRuleRequest, showCode, } from '../../../host/remote/index.js';
8
+ import { SecretStore } from '../../../host/secrets/index.js';
9
+ import { protectPrivateFolder } from '../../../host/sharing/index.js';
10
+ import { selfTestRunning } from '../../../host/test-guard.js';
11
+ import { WebStore } from '../../../host/web/index.js';
12
+ import { implement } from '../../context.js';
13
+ import { ConfigFile } from '../settings/index.js';
14
+ import { accessStatus, allowDevice, lanOff, lanOn, revokeAccess, revokeAllAccess } from './access.def.js';
15
+ /** The access list of this user (the folder is the web state's private one). */
16
+ export function accessStoreOf(ctx) {
17
+ return AccessStore.at(ctx.storageRoot, SecretStore.standard(), {
18
+ // The self-test never changes folder permissions; its storage is a temp folder.
19
+ protect: selfTestRunning() ? undefined : (dir) => protectPrivateFolder(dir),
20
+ });
21
+ }
22
+ function lanStateOf(ctx) {
23
+ const lan = new WebStore(ctx.storageRoot).read()?.lan;
24
+ return { url: lan?.url ?? null, fingerprint: lan?.fingerprint ?? null, problem: lan?.problem ?? null };
25
+ }
26
+ function setLan(ctx, enabled) {
27
+ const outcome = ConfigFile.user(ctx.loaded.configPath).set(['web', 'remote', 'lan', 'enabled'], enabled);
28
+ if (typeof outcome === 'object')
29
+ fail('Could not change web.remote.lan.enabled: ' + outcome.problems.join('; '));
30
+ }
31
+ export const accessStatusRun = implement(accessStatus, (ctx) => {
32
+ requireOnPc('web');
33
+ const remote = ctx.loaded.config.web.remote;
34
+ const data = accessStoreOf(ctx).read();
35
+ const now = Date.now();
36
+ const alive = (iso) => Date.parse(iso) > now;
37
+ const lan = lanStateOf(ctx);
38
+ return {
39
+ lan: {
40
+ enabled: remote.lan.enabled,
41
+ running: remote.lan.enabled && lan.url !== null,
42
+ address: remote.lan.enabled ? lan.url : null,
43
+ fingerprint: remote.lan.enabled ? lan.fingerprint : null,
44
+ problem: remote.lan.enabled ? lan.problem : null,
45
+ network: data.network,
46
+ },
47
+ windowsSignIn: remote.windowsSignIn,
48
+ users: remote.users.map((u) => ({ name: u.name, role: u.role })),
49
+ rememberDays: remote.rememberDays,
50
+ devices: data.devices
51
+ .filter((d) => alive(d.expiresAt))
52
+ .map((d) => ({
53
+ id: d.id,
54
+ name: d.name,
55
+ role: d.role,
56
+ address: d.address,
57
+ createdAt: d.createdAt,
58
+ expiresAt: d.expiresAt,
59
+ lastSeen: d.lastSeen ?? null,
60
+ })),
61
+ windows: data.windows
62
+ .filter((w) => alive(w.expiresAt))
63
+ .map((w) => ({ id: w.id, user: w.user, createdAt: w.createdAt, expiresAt: w.expiresAt })),
64
+ pending: data.code && alive(data.code.expiresAt) ? { role: data.code.role, expiresAt: data.code.expiresAt } : null,
65
+ codeVoided: data.codeVoided,
66
+ };
67
+ });
68
+ export const allowDeviceRun = implement(allowDevice, (ctx, input) => {
69
+ requireOnPc('web');
70
+ if (!ctx.loaded.config.web.remote.lan.enabled)
71
+ fail('LAN mode is off, so no device on your network can reach the web UI.', 'Turn it on first: sandbox web lan-on');
72
+ const { code, expiresAt } = accessStoreOf(ctx).newDeviceCode(input.role);
73
+ const lan = lanStateOf(ctx);
74
+ return { code: showCode(code), expiresAt, role: input.role, address: lan.url, fingerprint: lan.fingerprint };
75
+ });
76
+ export const revokeAccessRun = implement(revokeAccess, (ctx, input) => {
77
+ requireOnPc('web');
78
+ if (!accessStoreOf(ctx).revoke(input.id))
79
+ fail('There is no device or sign-in ' + input.id + '.', 'See sandbox web devices.');
80
+ return { message: 'Signed out. Its open pages close within half a minute.' };
81
+ });
82
+ export const revokeAllAccessRun = implement(revokeAllAccess, async (ctx, _input, io) => {
83
+ requireOnPc('web');
84
+ const ok = await io.confirm({
85
+ title: 'Sign out every device and Windows user now?',
86
+ danger: true,
87
+ yes: 'Sign out all',
88
+ });
89
+ if (!ok)
90
+ return { message: 'Nothing changed.' };
91
+ accessStoreOf(ctx).revokeAll('everything');
92
+ return { message: 'Everyone is signed out, and a code not used yet is void.' };
93
+ });
94
+ export const lanOnRun = implement(lanOn, async (ctx, _input, io) => {
95
+ requireOnPc('web');
96
+ const lan = ctx.loaded.config.web.remote.lan;
97
+ const bound = chooseLanAddress(lan.address);
98
+ io.progress('Checking the network');
99
+ const profile = await networkProfile(bound.address);
100
+ const problem = networkProblem(profile, null);
101
+ if (problem)
102
+ fail(problem, 'LAN mode only runs on a network you marked Private (Windows Settings > Network > your network).');
103
+ io.progress('Adding the firewall rule (Windows asks for permission)');
104
+ await applyFirewall(addRuleRequest(lan.port, bound.address));
105
+ accessStoreOf(ctx).setNetwork(profile.name);
106
+ setLan(ctx, true);
107
+ const address = lanUrl(bound.address, lan.port);
108
+ return {
109
+ address,
110
+ message: 'LAN mode is on for the network "' +
111
+ profile.name +
112
+ '": devices open ' +
113
+ address +
114
+ ' (the running web UI starts listening within half a minute). Next: Allow a new device.',
115
+ };
116
+ });
117
+ export const lanOffRun = implement(lanOff, async (ctx, _input, io) => {
118
+ requireOnPc('web');
119
+ const ok = await io.confirm({ title: 'Turn off LAN mode and sign out every device?', danger: true, yes: 'Turn off' });
120
+ if (!ok)
121
+ return { message: 'Nothing changed.' };
122
+ setLan(ctx, false);
123
+ const store = accessStoreOf(ctx);
124
+ store.revokeAll('devices');
125
+ store.setNetwork(null);
126
+ io.progress('Removing the firewall rule (Windows asks for permission)');
127
+ await applyFirewall(removeRuleRequest());
128
+ return { message: 'LAN mode is off: the firewall rule is gone and every device is signed out.' };
129
+ });
@@ -0,0 +1,2 @@
1
+ export * from './access.def.js';
2
+ export * from './access.run.js';
@@ -0,0 +1,3 @@
1
+ // Remote access: devices on your network and other Windows users of this PC (docs/design/remote-access.md).
2
+ export * from './access.def.js';
3
+ export * from './access.run.js';
@@ -2,6 +2,8 @@
2
2
  // adding and removing one change. Node side (it checks names with the config schema); the agents run writes them.
3
3
  import { DEFAULTS } from '../../../core/config/index.js';
4
4
  import { HarnessSchema, NameSchema, describeIssues } from '../../../core/schema/index.js';
5
+ /** Names an agent cannot have. */
6
+ const RESERVED = ['shell', 'pwsh', 'powershell', 'cmd', 'close'];
5
7
  /** Nothing wrong with the name (true), or what is wrong. Pure. */
6
8
  export function agentNameProblem(name, taken) {
7
9
  const typed = name.trim();
@@ -12,6 +14,9 @@ export function agentNameProblem(name, taken) {
12
14
  return 'Use letters, numbers, - and _ only.';
13
15
  if (taken.some((t) => t.toLowerCase() === typed.toLowerCase()))
14
16
  return typed + ' is an agent already.';
17
+ // Shell sessions are listed by these names, and sandbox shell / close are commands.
18
+ if (RESERVED.includes(typed.toLowerCase()))
19
+ return typed + ' is taken by the sandbox; choose another name.';
15
20
  return true;
16
21
  }
17
22
  /** The command a package usually provides: "@scope/my-agent@latest" gives "my-agent". Pure. */
@@ -25,6 +25,7 @@ export const listAgents = defineQuery({
25
25
  });
26
26
  export const startAgent = defineOperation({
27
27
  id: 'agents.start',
28
+ access: 'control',
28
29
  title: 'Start an agent',
29
30
  summary: 'start an agent in a project of the running sandbox',
30
31
  input: z.object({
@@ -145,6 +145,7 @@ export const setCustomApps = defineOperation({
145
145
  });
146
146
  export const requestApp = defineOperation({
147
147
  id: 'apps.request',
148
+ access: 'control',
148
149
  title: 'Request an app…',
149
150
  summary: 'open a prefilled GitHub request for an app that is not in the list',
150
151
  input: z.object({}),
@@ -1,13 +1,14 @@
1
1
  import { z } from 'zod';
2
2
  import { type Page } from '../../kernel/index.js';
3
- export declare const AUDIT_EVENT_KINDS: readonly ["proc", "file", "commit", "session", "guard", "note"];
4
- export declare const AUDIT_KIND_FILTERS: readonly ["all", "proc", "file", "commit", "session", "guard"];
3
+ export declare const AUDIT_EVENT_KINDS: readonly ["proc", "file", "commit", "session", "guard", "note", "remote"];
4
+ export declare const AUDIT_KIND_FILTERS: readonly ["all", "proc", "file", "commit", "session", "guard", "remote"];
5
5
  /** The session filter that means "not linked to any session". */
6
6
  export declare const NO_SESSION = "none";
7
7
  export declare const AuditRowSchema: z.ZodObject<{
8
8
  time: z.ZodString;
9
9
  kind: z.ZodEnum<{
10
10
  file: "file";
11
+ remote: "remote";
11
12
  session: "session";
12
13
  note: "note";
13
14
  commit: "commit";
@@ -60,6 +61,7 @@ export declare const AuditListSchema: z.ZodObject<{
60
61
  days: z.ZodNumber;
61
62
  kind: z.ZodEnum<{
62
63
  file: "file";
64
+ remote: "remote";
63
65
  all: "all";
64
66
  session: "session";
65
67
  commit: "commit";
@@ -76,6 +78,7 @@ export declare const AuditListSchema: z.ZodObject<{
76
78
  time: z.ZodString;
77
79
  kind: z.ZodEnum<{
78
80
  file: "file";
81
+ remote: "remote";
79
82
  session: "session";
80
83
  note: "note";
81
84
  commit: "commit";
@@ -134,6 +137,7 @@ export declare const listAudit: import("../../kernel/define.js").QueryDef<z.ZodO
134
137
  days: z.ZodDefault<z.ZodNumber>;
135
138
  kind: z.ZodDefault<z.ZodEnum<{
136
139
  file: "file";
140
+ remote: "remote";
137
141
  all: "all";
138
142
  session: "session";
139
143
  commit: "commit";
@@ -155,6 +159,7 @@ export declare const listAudit: import("../../kernel/define.js").QueryDef<z.ZodO
155
159
  days: z.ZodNumber;
156
160
  kind: z.ZodEnum<{
157
161
  file: "file";
162
+ remote: "remote";
158
163
  all: "all";
159
164
  session: "session";
160
165
  commit: "commit";
@@ -171,6 +176,7 @@ export declare const listAudit: import("../../kernel/define.js").QueryDef<z.ZodO
171
176
  time: z.ZodString;
172
177
  kind: z.ZodEnum<{
173
178
  file: "file";
179
+ remote: "remote";
174
180
  session: "session";
175
181
  note: "note";
176
182
  commit: "commit";
@@ -248,6 +254,7 @@ export declare const auditView: import("../../kernel/define.js").ViewDef<{
248
254
  days: z.ZodDefault<z.ZodNumber>;
249
255
  kind: z.ZodDefault<z.ZodEnum<{
250
256
  file: "file";
257
+ remote: "remote";
251
258
  all: "all";
252
259
  session: "session";
253
260
  commit: "commit";
@@ -269,6 +276,7 @@ export declare const auditView: import("../../kernel/define.js").ViewDef<{
269
276
  days: z.ZodNumber;
270
277
  kind: z.ZodEnum<{
271
278
  file: "file";
279
+ remote: "remote";
272
280
  all: "all";
273
281
  session: "session";
274
282
  commit: "commit";
@@ -285,6 +293,7 @@ export declare const auditView: import("../../kernel/define.js").ViewDef<{
285
293
  time: z.ZodString;
286
294
  kind: z.ZodEnum<{
287
295
  file: "file";
296
+ remote: "remote";
288
297
  session: "session";
289
298
  note: "note";
290
299
  commit: "commit";
@@ -5,8 +5,8 @@
5
5
  import { z } from 'zod';
6
6
  import { action, ago, bytesText, defineQuery, defineView, link, plural, } from '../../kernel/index.js';
7
7
  import { setSetting } from '../settings/settings.def.js';
8
- export const AUDIT_EVENT_KINDS = ['proc', 'file', 'commit', 'session', 'guard', 'note'];
9
- export const AUDIT_KIND_FILTERS = ['all', 'proc', 'file', 'commit', 'session', 'guard'];
8
+ export const AUDIT_EVENT_KINDS = ['proc', 'file', 'commit', 'session', 'guard', 'note', 'remote'];
9
+ export const AUDIT_KIND_FILTERS = ['all', 'proc', 'file', 'commit', 'session', 'guard', 'remote'];
10
10
  /** The days the page offers (the log keeps at most 30). */
11
11
  const PERIODS = [1, 3, 7, 14, 30];
12
12
  /** The session filter that means "not linked to any session". */
@@ -20,12 +20,13 @@ const KIND_LABEL = {
20
20
  session: 'session',
21
21
  guard: 'GUARD',
22
22
  note: 'note',
23
+ remote: 'remote',
23
24
  };
24
25
  export const AuditRowSchema = z.object({
25
26
  time: z.string().describe('When it happened (ISO time)'),
26
27
  kind: z
27
28
  .enum(AUDIT_EVENT_KINDS)
28
- .describe('proc: a program started; file: changed in a project; commit; session; guard: the repo guard; note'),
29
+ .describe('proc: a program started; file: changed in a project; commit; session; guard: the repo guard; note; remote: what another Windows user or a paired device did in the web UI'),
29
30
  what: z.string().describe('The program, the file, the commit message, the agent, or what happened'),
30
31
  command: z.string().optional().describe("A program's command line (secrets hidden)"),
31
32
  parent: z.string().optional().describe('The program that started it'),
@@ -43,7 +44,7 @@ export const AuditRowSchema = z.object({
43
44
  .describe('How it was linked: process = by the programs the session started; only-session = the only session running in that project then (likely, not proven)'),
44
45
  count: z.number().int().describe('How many times it happened in that minute'),
45
46
  sha: z.string().optional().describe("A commit's id"),
46
- author: z.string().optional().describe("A commit's author"),
47
+ author: z.string().optional().describe("A commit's author; for remote: who did it"),
47
48
  });
48
49
  const CountsSchema = z.object({
49
50
  commands: z.number().int().describe('Programs started'),
@@ -152,6 +153,8 @@ export function eventWhat(e) {
152
153
  (e.action && e.action !== 'commit' ? ' (' + e.action + ')' : ''));
153
154
  case 'session':
154
155
  return e.what + (e.action === 'end' ? ' ended' : ' started') + (e.project ? ' in ' + e.project : '');
156
+ case 'remote':
157
+ return (e.author ?? 'someone') + ': ' + e.what + (e.action && e.action !== 'ok' ? ' (' + e.action + ')' : '');
155
158
  default:
156
159
  return e.what;
157
160
  }
@@ -14,6 +14,7 @@ export declare const listAuditRun: import("../../kernel/define.js").Implementati
14
14
  days: import("zod").ZodDefault<import("zod").ZodNumber>;
15
15
  kind: import("zod").ZodDefault<import("zod").ZodEnum<{
16
16
  file: "file";
17
+ remote: "remote";
17
18
  all: "all";
18
19
  session: "session";
19
20
  commit: "commit";
@@ -35,6 +36,7 @@ export declare const listAuditRun: import("../../kernel/define.js").Implementati
35
36
  days: import("zod").ZodNumber;
36
37
  kind: import("zod").ZodEnum<{
37
38
  file: "file";
39
+ remote: "remote";
38
40
  all: "all";
39
41
  session: "session";
40
42
  commit: "commit";
@@ -51,6 +53,7 @@ export declare const listAuditRun: import("../../kernel/define.js").Implementati
51
53
  time: import("zod").ZodString;
52
54
  kind: import("zod").ZodEnum<{
53
55
  file: "file";
56
+ remote: "remote";
54
57
  session: "session";
55
58
  note: "note";
56
59
  commit: "commit";
@@ -11,6 +11,7 @@ export type { SessionRow, SessionsList } from './sessions/sessions.def.js';
11
11
  export type { SettingRow, SettingsList } from './settings/settings.def.js';
12
12
  export type { VpnStatus } from './vpn/vpn.def.js';
13
13
  export type { GithubStatus } from './github/github.def.js';
14
+ export type { AccessStatus } from './access/access.def.js';
14
15
  export declare const OPERATIONS: readonly [OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
15
16
  available: import("zod").ZodBoolean;
16
17
  stopped: import("zod").ZodNumber;
@@ -29,6 +30,22 @@ export declare const OPERATIONS: readonly [OperationDef<import("zod").ZodObject<
29
30
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
30
31
  message: import("zod").ZodString;
31
32
  }, import("zod/v4/core").$strip>, "sessions.restart">, OperationDef<import("zod").ZodObject<{
33
+ project: import("zod").ZodOptional<import("zod").ZodString>;
34
+ shell: import("zod").ZodDefault<import("zod").ZodEnum<{
35
+ pwsh: "pwsh";
36
+ auto: "auto";
37
+ powershell: "powershell";
38
+ cmd: "cmd";
39
+ }>>;
40
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
41
+ session: import("zod").ZodString;
42
+ shell: import("zod").ZodString;
43
+ message: import("zod").ZodString;
44
+ }, import("zod/v4/core").$strip>, "sessions.shell">, OperationDef<import("zod").ZodObject<{
45
+ session: import("zod").ZodString;
46
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
47
+ message: import("zod").ZodString;
48
+ }, import("zod/v4/core").$strip>, "sessions.close">, OperationDef<import("zod").ZodObject<{
32
49
  agent: import("zod").ZodString;
33
50
  project: import("zod").ZodOptional<import("zod").ZodString>;
34
51
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
@@ -257,7 +274,32 @@ export declare const OPERATIONS: readonly [OperationDef<import("zod").ZodObject<
257
274
  }, import("zod/v4/core").$strip>, "github.login">, OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strict>, import("zod").ZodObject<{
258
275
  deleted: import("zod").ZodBoolean;
259
276
  message: import("zod").ZodString;
260
- }, import("zod/v4/core").$strip>, "github.logout">];
277
+ }, import("zod/v4/core").$strip>, "github.logout">, OperationDef<import("zod").ZodObject<{
278
+ role: import("zod").ZodDefault<import("zod").ZodEnum<{
279
+ view: "view";
280
+ control: "control";
281
+ }>>;
282
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
283
+ code: import("zod").ZodString;
284
+ expiresAt: import("zod").ZodString;
285
+ role: import("zod").ZodEnum<{
286
+ view: "view";
287
+ control: "control";
288
+ }>;
289
+ address: import("zod").ZodNullable<import("zod").ZodString>;
290
+ fingerprint: import("zod").ZodNullable<import("zod").ZodString>;
291
+ }, import("zod/v4/core").$strip>, "access.allowDevice">, OperationDef<import("zod").ZodObject<{
292
+ id: import("zod").ZodString;
293
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
294
+ message: import("zod").ZodString;
295
+ }, import("zod/v4/core").$strip>, "access.revoke">, OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
296
+ message: import("zod").ZodString;
297
+ }, import("zod/v4/core").$strip>, "access.revokeAll">, OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
298
+ message: import("zod").ZodString;
299
+ address: import("zod").ZodString;
300
+ }, import("zod/v4/core").$strip>, "access.lanOn">, OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
301
+ message: import("zod").ZodString;
302
+ }, import("zod/v4/core").$strip>, "access.lanOff">];
261
303
  export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
262
304
  shared: import("zod").ZodOptional<import("zod").ZodObject<{
263
305
  line: import("zod").ZodString;
@@ -326,6 +368,10 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
326
368
  id: import("zod").ZodString;
327
369
  launchId: import("zod").ZodString;
328
370
  harness: import("zod").ZodString;
371
+ kind: import("zod").ZodOptional<import("zod").ZodEnum<{
372
+ shell: "shell";
373
+ agent: "agent";
374
+ }>>;
329
375
  cwd: import("zod").ZodString;
330
376
  project: import("zod").ZodOptional<import("zod").ZodString>;
331
377
  phase: import("zod").ZodEnum<{
@@ -623,6 +669,7 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
623
669
  days: import("zod").ZodDefault<import("zod").ZodNumber>;
624
670
  kind: import("zod").ZodDefault<import("zod").ZodEnum<{
625
671
  file: "file";
672
+ remote: "remote";
626
673
  all: "all";
627
674
  session: "session";
628
675
  commit: "commit";
@@ -644,6 +691,7 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
644
691
  days: import("zod").ZodNumber;
645
692
  kind: import("zod").ZodEnum<{
646
693
  file: "file";
694
+ remote: "remote";
647
695
  all: "all";
648
696
  session: "session";
649
697
  commit: "commit";
@@ -660,6 +708,7 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
660
708
  time: import("zod").ZodString;
661
709
  kind: import("zod").ZodEnum<{
662
710
  file: "file";
711
+ remote: "remote";
663
712
  session: "session";
664
713
  note: "note";
665
714
  commit: "commit";
@@ -799,6 +848,9 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
799
848
  running: import("zod").ZodBoolean;
800
849
  port: import("zod").ZodNullable<import("zod").ZodNumber>;
801
850
  address: import("zod").ZodNullable<import("zod").ZodString>;
851
+ lanAddress: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
852
+ lanFingerprint: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
853
+ lanProblem: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
802
854
  }, import("zod/v4/core").$strip>, "web.status">, QueryDef<import("zod").ZodObject<{
803
855
  tunnel: import("zod").ZodDefault<import("zod").ZodBoolean>;
804
856
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
@@ -845,7 +897,51 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
845
897
  classic: "classic";
846
898
  }>>;
847
899
  savedAt: import("zod").ZodNullable<import("zod").ZodString>;
848
- }, import("zod/v4/core").$strip>, "github.status">];
900
+ }, import("zod/v4/core").$strip>, "github.status">, QueryDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
901
+ lan: import("zod").ZodObject<{
902
+ enabled: import("zod").ZodBoolean;
903
+ running: import("zod").ZodBoolean;
904
+ address: import("zod").ZodNullable<import("zod").ZodString>;
905
+ fingerprint: import("zod").ZodNullable<import("zod").ZodString>;
906
+ problem: import("zod").ZodNullable<import("zod").ZodString>;
907
+ network: import("zod").ZodNullable<import("zod").ZodString>;
908
+ }, import("zod/v4/core").$strip>;
909
+ windowsSignIn: import("zod").ZodBoolean;
910
+ users: import("zod").ZodArray<import("zod").ZodObject<{
911
+ name: import("zod").ZodString;
912
+ role: import("zod").ZodEnum<{
913
+ view: "view";
914
+ control: "control";
915
+ }>;
916
+ }, import("zod/v4/core").$strip>>;
917
+ rememberDays: import("zod").ZodNumber;
918
+ devices: import("zod").ZodArray<import("zod").ZodObject<{
919
+ id: import("zod").ZodString;
920
+ name: import("zod").ZodString;
921
+ role: import("zod").ZodEnum<{
922
+ view: "view";
923
+ control: "control";
924
+ }>;
925
+ address: import("zod").ZodString;
926
+ createdAt: import("zod").ZodString;
927
+ expiresAt: import("zod").ZodString;
928
+ lastSeen: import("zod").ZodNullable<import("zod").ZodString>;
929
+ }, import("zod/v4/core").$strip>>;
930
+ windows: import("zod").ZodArray<import("zod").ZodObject<{
931
+ id: import("zod").ZodString;
932
+ user: import("zod").ZodString;
933
+ createdAt: import("zod").ZodString;
934
+ expiresAt: import("zod").ZodString;
935
+ }, import("zod/v4/core").$strip>>;
936
+ pending: import("zod").ZodNullable<import("zod").ZodObject<{
937
+ role: import("zod").ZodEnum<{
938
+ view: "view";
939
+ control: "control";
940
+ }>;
941
+ expiresAt: import("zod").ZodString;
942
+ }, import("zod/v4/core").$strip>>;
943
+ codeVoided: import("zod").ZodBoolean;
944
+ }, import("zod/v4/core").$strip>, "access.status">];
849
945
  export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
850
946
  status: QueryDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
851
947
  shared: import("zod").ZodOptional<import("zod").ZodObject<{
@@ -917,6 +1013,10 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
917
1013
  id: import("zod").ZodString;
918
1014
  launchId: import("zod").ZodString;
919
1015
  harness: import("zod").ZodString;
1016
+ kind: import("zod").ZodOptional<import("zod").ZodEnum<{
1017
+ shell: "shell";
1018
+ agent: "agent";
1019
+ }>>;
920
1020
  cwd: import("zod").ZodString;
921
1021
  project: import("zod").ZodOptional<import("zod").ZodString>;
922
1022
  phase: import("zod").ZodEnum<{
@@ -1216,6 +1316,7 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
1216
1316
  days: import("zod").ZodDefault<import("zod").ZodNumber>;
1217
1317
  kind: import("zod").ZodDefault<import("zod").ZodEnum<{
1218
1318
  file: "file";
1319
+ remote: "remote";
1219
1320
  all: "all";
1220
1321
  session: "session";
1221
1322
  commit: "commit";
@@ -1237,6 +1338,7 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
1237
1338
  days: import("zod").ZodNumber;
1238
1339
  kind: import("zod").ZodEnum<{
1239
1340
  file: "file";
1341
+ remote: "remote";
1240
1342
  all: "all";
1241
1343
  session: "session";
1242
1344
  commit: "commit";
@@ -1253,6 +1355,7 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
1253
1355
  time: import("zod").ZodString;
1254
1356
  kind: import("zod").ZodEnum<{
1255
1357
  file: "file";
1358
+ remote: "remote";
1256
1359
  session: "session";
1257
1360
  note: "note";
1258
1361
  commit: "commit";
@@ -1374,6 +1477,9 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
1374
1477
  running: import("zod").ZodBoolean;
1375
1478
  port: import("zod").ZodNullable<import("zod").ZodNumber>;
1376
1479
  address: import("zod").ZodNullable<import("zod").ZodString>;
1480
+ lanAddress: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
1481
+ lanFingerprint: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
1482
+ lanProblem: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
1377
1483
  }, import("zod/v4/core").$strip>, "web.status">;
1378
1484
  }, Record<string, never>, "web">, import("../kernel/define.js").ViewDef<{
1379
1485
  status: QueryDef<import("zod").ZodObject<{
@@ -1425,7 +1531,53 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
1425
1531
  }>>;
1426
1532
  savedAt: import("zod").ZodNullable<import("zod").ZodString>;
1427
1533
  }, import("zod/v4/core").$strip>, "github.status">;
1428
- }, Record<string, never>, "github">];
1534
+ }, Record<string, never>, "github">, import("../kernel/define.js").ViewDef<{
1535
+ status: QueryDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
1536
+ lan: import("zod").ZodObject<{
1537
+ enabled: import("zod").ZodBoolean;
1538
+ running: import("zod").ZodBoolean;
1539
+ address: import("zod").ZodNullable<import("zod").ZodString>;
1540
+ fingerprint: import("zod").ZodNullable<import("zod").ZodString>;
1541
+ problem: import("zod").ZodNullable<import("zod").ZodString>;
1542
+ network: import("zod").ZodNullable<import("zod").ZodString>;
1543
+ }, import("zod/v4/core").$strip>;
1544
+ windowsSignIn: import("zod").ZodBoolean;
1545
+ users: import("zod").ZodArray<import("zod").ZodObject<{
1546
+ name: import("zod").ZodString;
1547
+ role: import("zod").ZodEnum<{
1548
+ view: "view";
1549
+ control: "control";
1550
+ }>;
1551
+ }, import("zod/v4/core").$strip>>;
1552
+ rememberDays: import("zod").ZodNumber;
1553
+ devices: import("zod").ZodArray<import("zod").ZodObject<{
1554
+ id: import("zod").ZodString;
1555
+ name: import("zod").ZodString;
1556
+ role: import("zod").ZodEnum<{
1557
+ view: "view";
1558
+ control: "control";
1559
+ }>;
1560
+ address: import("zod").ZodString;
1561
+ createdAt: import("zod").ZodString;
1562
+ expiresAt: import("zod").ZodString;
1563
+ lastSeen: import("zod").ZodNullable<import("zod").ZodString>;
1564
+ }, import("zod/v4/core").$strip>>;
1565
+ windows: import("zod").ZodArray<import("zod").ZodObject<{
1566
+ id: import("zod").ZodString;
1567
+ user: import("zod").ZodString;
1568
+ createdAt: import("zod").ZodString;
1569
+ expiresAt: import("zod").ZodString;
1570
+ }, import("zod/v4/core").$strip>>;
1571
+ pending: import("zod").ZodNullable<import("zod").ZodObject<{
1572
+ role: import("zod").ZodEnum<{
1573
+ view: "view";
1574
+ control: "control";
1575
+ }>;
1576
+ expiresAt: import("zod").ZodString;
1577
+ }, import("zod/v4/core").$strip>>;
1578
+ codeVoided: import("zod").ZodBoolean;
1579
+ }, import("zod/v4/core").$strip>, "access.status">;
1580
+ }, Record<string, never>, "access">];
1429
1581
  export type Operation = (typeof OPERATIONS)[number];
1430
1582
  export type Query = (typeof QUERIES)[number];
1431
1583
  export type View = (typeof VIEWS)[number];
@@ -10,18 +10,21 @@ import { hideWindow, sandboxStatus, sandboxView, showWindow, stopSandbox } from
10
10
  import { addProject, allProjects, chooseProjects, excludeProject, includeProject, listProjects, projectRoots, projectsView, removeProject, } from './projects/projects.def.js';
11
11
  import { listLogs, logsView, tailLog } from './logs/logs.def.js';
12
12
  import { auditView, listAudit } from './audit/audit.def.js';
13
- import { listSessions, restartSessions, sessionsView } from './sessions/sessions.def.js';
13
+ import { closeSession, listSessions, restartSessions, sessionsView, startShell } from './sessions/sessions.def.js';
14
14
  import { vpnLogin, vpnLogout, vpnStatus, vpnView } from './vpn/vpn.def.js';
15
15
  import { githubLogin, githubLogout, githubStatus, githubView } from './github/github.def.js';
16
16
  import { exportConfig, getSetting, importConfig, listSettings, resetSetting, setSetting, settingsView, } from './settings/settings.def.js';
17
17
  import { migrateSettings, runUpdate, updatesStatus, updatesView } from './updates/updates.def.js';
18
18
  import { openWeb, startWeb, stopWeb, webStatus, webView } from './web/web.def.js';
19
+ import { accessStatus, accessView, allowDevice, lanOff, lanOn, revokeAccess, revokeAllAccess, } from './access/access.def.js';
19
20
  export const OPERATIONS = [
20
21
  cleanEngine,
21
22
  showWindow,
22
23
  hideWindow,
23
24
  stopSandbox,
24
25
  restartSessions,
26
+ startShell,
27
+ closeSession,
25
28
  startAgent,
26
29
  addAgent,
27
30
  makeDefaultAgent,
@@ -64,6 +67,11 @@ export const OPERATIONS = [
64
67
  vpnLogout,
65
68
  githubLogin,
66
69
  githubLogout,
70
+ allowDevice,
71
+ revokeAccess,
72
+ revokeAllAccess,
73
+ lanOn,
74
+ lanOff,
67
75
  ];
68
76
  export const QUERIES = [
69
77
  sandboxStatus,
@@ -89,6 +97,7 @@ export const QUERIES = [
89
97
  webStatus,
90
98
  vpnStatus,
91
99
  githubStatus,
100
+ accessStatus,
92
101
  ];
93
102
  export const VIEWS = [
94
103
  sandboxView,
@@ -108,6 +117,7 @@ export const VIEWS = [
108
117
  webView,
109
118
  vpnView,
110
119
  githubView,
120
+ accessView,
111
121
  ];
112
122
  export const DEFINITIONS = [...OPERATIONS, ...QUERIES];
113
123
  export function definitionById(id) {
@@ -155,6 +165,7 @@ export const NAV = [
155
165
  ],
156
166
  },
157
167
  { label: 'Accounts', view: 'accounts' },
168
+ { label: 'Remote access', view: 'access' },
158
169
  {
159
170
  label: 'Maintenance',
160
171
  children: [
@@ -22,6 +22,7 @@ export const FolderListingSchema = z.object({
22
22
  });
23
23
  export const browseFolder = defineQuery({
24
24
  id: 'files.browse',
25
+ access: 'owner',
25
26
  title: 'Browse folders',
26
27
  summary: 'list what is in a folder of this PC, to pick a folder or a file',
27
28
  input: z.object({