@xfey/tutti 0.1.33 → 0.1.34

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 (34) hide show
  1. package/README.md +3 -3
  2. package/dist/server-shell/cli/args.d.ts +6 -0
  3. package/dist/server-shell/cli/args.js +30 -0
  4. package/dist/server-shell/cli/cli.js +118 -6
  5. package/dist/server-shell/cli/errors.d.ts +1 -1
  6. package/dist/server-shell/cli/host-runtime-endpoint.d.ts +1 -0
  7. package/dist/server-shell/cli/host-runtime-endpoint.js +10 -5
  8. package/dist/server-shell/cli/machine-project-inspector.d.ts +1 -0
  9. package/dist/server-shell/cli/machine-project-inspector.js +1 -0
  10. package/dist/server-shell/cli/managed-host.d.ts +1 -0
  11. package/dist/server-shell/cli/managed-host.js +6 -4
  12. package/dist/server-shell/cli/runtime-commands.d.ts +1 -0
  13. package/dist/server-shell/cli/runtime-commands.js +5 -1
  14. package/dist/server-shell/local-console/folder-picker.d.ts +0 -1
  15. package/dist/server-shell/local-console/folder-picker.js +2 -33
  16. package/dist/server-shell/local-console/invocation-context.d.ts +21 -0
  17. package/dist/server-shell/local-console/invocation-context.js +56 -0
  18. package/dist/server-shell/local-console/lifecycle-lock.d.ts +13 -0
  19. package/dist/server-shell/local-console/lifecycle-lock.js +135 -0
  20. package/dist/server-shell/local-console/managed-console.d.ts +38 -0
  21. package/dist/server-shell/local-console/managed-console.js +254 -36
  22. package/dist/server-shell/local-console/project-service.d.ts +11 -7
  23. package/dist/server-shell/local-console/project-service.js +102 -57
  24. package/dist/server-shell/local-console/runtime-endpoint.d.ts +15 -1
  25. package/dist/server-shell/local-console/runtime-endpoint.js +21 -5
  26. package/dist/server-shell/local-console/server.d.ts +12 -0
  27. package/dist/server-shell/local-console/server.js +152 -45
  28. package/dist/server-shell/local-console/session.d.ts +10 -15
  29. package/dist/server-shell/local-console/session.js +69 -17
  30. package/package.json +1 -1
  31. package/web/assets/index-B84rQ4YJ.js +29 -0
  32. package/web/assets/{index-BcZpeSaX.css → index-C7RDpM1L.css} +1 -1
  33. package/web/index.html +2 -2
  34. package/web/assets/index-DKf4jz_E.js +0 -29
@@ -1,33 +1,28 @@
1
+ import type { LocalConsoleInvocationContext } from "./invocation-context.js";
1
2
  export declare const LOCAL_CONSOLE_SESSION_COOKIE = "tutti_local_console_session";
2
- type ExpiringValue = {
3
- hash: string;
4
- expiresAt: number;
5
- };
6
- type LocalConsoleSession = ExpiringValue & {
7
- csrfToken: string;
8
- currentDirectory: string;
9
- };
10
3
  export type IssuedLocalConsoleAccessToken = {
11
4
  token: string;
12
5
  expires_at: string;
13
6
  };
14
7
  export type ExchangedLocalConsoleSession = {
15
- sessionToken: string;
8
+ sessionToken?: string;
9
+ contextToken: string;
16
10
  csrfToken: string;
17
11
  expires_at: string;
18
12
  };
13
+ export type ReadLocalConsoleSession = {
14
+ csrfToken: string;
15
+ context: LocalConsoleInvocationContext;
16
+ };
19
17
  export declare class LocalConsoleSessionRegistry {
20
18
  #private;
21
19
  constructor(options?: {
22
20
  now?: () => Date;
23
21
  });
24
- issueAccessToken(options: {
25
- currentDirectory: string;
26
- }): IssuedLocalConsoleAccessToken;
27
- exchangeAccessToken(token: string): ExchangedLocalConsoleSession | null;
28
- readSession(token: string | undefined): LocalConsoleSession | null;
22
+ issueAccessToken(context: LocalConsoleInvocationContext): IssuedLocalConsoleAccessToken;
23
+ exchangeAccessToken(token: string, existingSessionToken?: string): ExchangedLocalConsoleSession | null;
24
+ readSession(token: string | undefined, contextToken?: string): ReadLocalConsoleSession | null;
29
25
  }
30
26
  export declare function parseCookieHeader(header: string | undefined): Record<string, string>;
31
27
  export declare function localConsoleSessionCookie(token: string, expiresAt: string): string;
32
- export {};
33
28
  //# sourceMappingURL=session.d.ts.map
@@ -16,22 +16,24 @@ function secretEquals(value, expectedHash) {
16
16
  export class LocalConsoleSessionRegistry {
17
17
  #accessTokens = new Map();
18
18
  #sessions = new Map();
19
+ #contexts = new Map();
19
20
  #now;
21
+ #browserSessionToken;
20
22
  constructor(options = {}) {
21
23
  this.#now = options.now ?? (() => new Date());
22
24
  }
23
- issueAccessToken(options) {
25
+ issueAccessToken(context) {
24
26
  this.#purgeExpired();
25
27
  const token = createSecret();
26
28
  const expiresAt = this.#now().getTime() + ACCESS_TOKEN_TTL_MS;
27
29
  this.#accessTokens.set(hashSecret(token), {
28
30
  hash: hashSecret(token),
29
31
  expiresAt,
30
- currentDirectory: options.currentDirectory,
32
+ context,
31
33
  });
32
34
  return { token, expires_at: new Date(expiresAt).toISOString() };
33
35
  }
34
- exchangeAccessToken(token) {
36
+ exchangeAccessToken(token, existingSessionToken) {
35
37
  this.#purgeExpired();
36
38
  const accessTokenHash = hashSecret(token);
37
39
  const accessToken = this.#accessTokens.get(accessTokenHash);
@@ -41,25 +43,58 @@ export class LocalConsoleSessionRegistry {
41
43
  return null;
42
44
  }
43
45
  this.#accessTokens.delete(accessTokenHash);
44
- const sessionToken = createSecret();
45
- const expiresAt = this.#now().getTime() + SESSION_TTL_MS;
46
- this.#sessions.set(hashSecret(sessionToken), {
47
- hash: hashSecret(sessionToken),
48
- csrfToken: createSecret(),
49
- expiresAt,
50
- currentDirectory: accessToken.currentDirectory,
51
- });
52
- const session = this.#sessions.get(hashSecret(sessionToken));
46
+ let sessionToken;
47
+ let sessionHash = "";
48
+ let session;
49
+ if (existingSessionToken !== undefined) {
50
+ sessionHash = hashSecret(existingSessionToken);
51
+ const existingSession = this.#sessions.get(sessionHash);
52
+ if (existingSession !== undefined &&
53
+ existingSession.expiresAt > this.#now().getTime() &&
54
+ secretEquals(existingSessionToken, existingSession.hash)) {
55
+ session = existingSession;
56
+ }
57
+ }
58
+ if (session === undefined && this.#browserSessionToken !== undefined) {
59
+ const sharedSessionHash = hashSecret(this.#browserSessionToken);
60
+ const sharedSession = this.#sessions.get(sharedSessionHash);
61
+ if (sharedSession !== undefined &&
62
+ sharedSession.expiresAt > this.#now().getTime() &&
63
+ secretEquals(this.#browserSessionToken, sharedSession.hash)) {
64
+ sessionToken = this.#browserSessionToken;
65
+ sessionHash = sharedSessionHash;
66
+ session = sharedSession;
67
+ }
68
+ }
53
69
  if (session === undefined) {
54
- return null;
70
+ sessionToken = createSecret();
71
+ this.#browserSessionToken = sessionToken;
72
+ sessionHash = hashSecret(sessionToken);
73
+ session = {
74
+ hash: sessionHash,
75
+ csrfToken: createSecret(),
76
+ expiresAt: this.#now().getTime() + SESSION_TTL_MS,
77
+ defaultContextHash: "",
78
+ };
79
+ this.#sessions.set(sessionHash, session);
55
80
  }
81
+ const contextToken = createSecret();
82
+ const contextHash = hashSecret(contextToken);
83
+ session.defaultContextHash = contextHash;
84
+ this.#contexts.set(contextHash, {
85
+ hash: contextHash,
86
+ expiresAt: session.expiresAt,
87
+ sessionHash,
88
+ context: accessToken.context,
89
+ });
56
90
  return {
57
- sessionToken,
91
+ ...(sessionToken === undefined ? {} : { sessionToken }),
92
+ contextToken,
58
93
  csrfToken: session.csrfToken,
59
- expires_at: new Date(expiresAt).toISOString(),
94
+ expires_at: new Date(session.expiresAt).toISOString(),
60
95
  };
61
96
  }
62
- readSession(token) {
97
+ readSession(token, contextToken) {
63
98
  this.#purgeExpired();
64
99
  if (token === undefined || token.trim() === "") {
65
100
  return null;
@@ -70,7 +105,15 @@ export class LocalConsoleSessionRegistry {
70
105
  !secretEquals(token, session.hash)) {
71
106
  return null;
72
107
  }
73
- return session;
108
+ const contextHash = contextToken === undefined ? session.defaultContextHash : hashSecret(contextToken);
109
+ const context = this.#contexts.get(contextHash);
110
+ if (context === undefined ||
111
+ context.sessionHash !== hashSecret(token) ||
112
+ context.expiresAt <= this.#now().getTime() ||
113
+ (contextToken !== undefined && !secretEquals(contextToken, context.hash))) {
114
+ return null;
115
+ }
116
+ return { csrfToken: session.csrfToken, context: context.context };
74
117
  }
75
118
  #purgeExpired() {
76
119
  const now = this.#now().getTime();
@@ -84,6 +127,15 @@ export class LocalConsoleSessionRegistry {
84
127
  this.#sessions.delete(key);
85
128
  }
86
129
  }
130
+ if (this.#browserSessionToken !== undefined &&
131
+ !this.#sessions.has(hashSecret(this.#browserSessionToken))) {
132
+ this.#browserSessionToken = undefined;
133
+ }
134
+ for (const [key, value] of this.#contexts) {
135
+ if (value.expiresAt <= now || !this.#sessions.has(value.sessionHash)) {
136
+ this.#contexts.delete(key);
137
+ }
138
+ }
87
139
  }
88
140
  }
89
141
  export function parseCookieHeader(header) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfey/tutti",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",