Skip to content

Commit 087b7ad

Browse files
authored
Merge branch 'master' into news-expiration-date-8485
2 parents b9a1249 + 6f85977 commit 087b7ad

File tree

27 files changed

+485
-712
lines changed

27 files changed

+485
-712
lines changed

src/.claude/settings.json

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,39 @@
11
{
22
"permissions": {
33
"allow": [
4+
"Bash(../node_modules/.bin/tsc:*)",
5+
"Bash(NODE_OPTIONS=--max-old-space-size=8192 ../node_modules/.bin/tsc --noEmit)",
6+
"Bash(curl:*)",
7+
"Bash(docker run:*)",
48
"Bash(find:*)",
59
"Bash(gh pr view:*)",
610
"Bash(gh:*)",
711
"Bash(git add:*)",
12+
"Bash(git checkout:*)",
813
"Bash(git commit:*)",
14+
"Bash(git push:*)",
915
"Bash(grep:*)",
1016
"Bash(node:*)",
1117
"Bash(npm show:*)",
18+
"Bash(npm view:*)",
1219
"Bash(npx tsc:*)",
1320
"Bash(pnpm build:*)",
21+
"Bash(pnpm i18n:*)",
1422
"Bash(pnpm ts-build:*)",
1523
"Bash(pnpm tsc:*)",
1624
"Bash(prettier -w:*)",
25+
"Bash(psql:*)",
1726
"WebFetch(domain:cocalc.com)",
1827
"WebFetch(domain:doc.cocalc.com)",
1928
"WebFetch(domain:docs.anthropic.com)",
2029
"WebFetch(domain:github.com)",
21-
"Bash(git checkout:*)",
22-
"Bash(git push:*)",
23-
"Bash(NODE_OPTIONS=--max-old-space-size=8192 ../node_modules/.bin/tsc --noEmit)",
24-
"Bash(docker run:*)",
25-
"Bash(../node_modules/.bin/tsc:*)",
26-
"Bash(npm view:*)",
27-
"WebFetch(domain:www.anthropic.com)",
2830
"WebFetch(domain:mistral.ai)",
29-
"Bash(pnpm i18n:*)",
3031
"WebFetch(domain:simplelocalize.io)",
3132
"Bash(pnpm list:*)",
3233
"Bash(pnpm why:*)",
33-
"mcp__github__get_issue"
34+
"mcp__github__get_issue",
35+
"WebFetch(domain:www.anthropic.com)",
36+
"mcp__cclsp__find_definition"
3437
],
3538
"deny": []
3639
}

src/CLAUDE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ CoCalc is organized as a monorepo with key packages:
7373
- **TypeScript React Components**: All frontend code is TypeScript with proper typing
7474
- **Modular Store System**: Each feature has its own store/actions (AccountStore, BillingStore, etc.)
7575
- **WebSocket Communication**: Real-time communication with backend via WebSocket messages
76+
- **Authentication Waiting**: When frontend code needs to wait for user authentication, use `redux.getStore("account").async_wait({ until: () => store.get_account_id() != null, timeout: 0 })` to wait indefinitely until authentication completes
77+
- **Conat DKV Usage**: For key-value storage with real-time sync, use `webapp_client.conat_client.dkv({ account_id, name: "store-name" })` to get a distributed key-value store that syncs across sessions
7678

7779
#### Backend Architecture
7880

@@ -81,6 +83,8 @@ CoCalc is organized as a monorepo with key packages:
8183
- **Conat System**: Container orchestration for compute servers
8284
- **Event-Driven Architecture**: Extensive use of EventEmitter patterns
8385
- **Microservice-like Packages**: Each package handles specific functionality
86+
- **Database Access**: Use `getPool()` from `@cocalc/database/pool` for direct database queries in hub/backend code. Example: `const pool = getPool(); const { rows } = await pool.query('SELECT * FROM table WHERE id = $1', [id]);`
87+
- **Hub Migration Functions**: Migration functions in hub should be designed to run once at startup, use batch processing with delays between batches to avoid database saturation
8488

8589
#### Communication Patterns
8690

src/packages/conat/service/typed.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { Options, ServiceCall } from "./service";
99
export type { ConatService };
1010

1111
export interface Extra {
12-
ping: typeof pingConatService;
12+
ping: (opts?: { maxWait?: number }) => Promise<void>;
1313
waitFor: (opts?: { maxWait?: number }) => Promise<void>;
1414
}
1515

src/packages/frontend/cspell.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@
7070
"flyouts",
7171
"buttonbar",
7272
"noconf",
73-
"flyoutdragbar"
73+
"flyoutdragbar",
74+
"ptree",
75+
"pchildren",
76+
"nprocs",
77+
"pids"
7478
],
7579
"flagWords": [],
7680
"ignorePaths": ["node_modules/**", "dist/**", "dist-ts/**", "build/**"],

src/packages/frontend/frame-editors/terminal-editor/conat-terminal.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
} from "@cocalc/conat/service/terminal";
1313
import { until } from "@cocalc/util/async-utils";
1414

15+
const DEFAULT_HEARTBEAT_INTERVAL = 15_000;
16+
1517
type State = "disconnected" | "init" | "running" | "closed";
1618

1719
export class ConatTerminal extends EventEmitter {
@@ -29,6 +31,7 @@ export class ConatTerminal extends EventEmitter {
2931
private writeQueue: string = "";
3032
private ephemeral?: boolean;
3133
private computeServers?;
34+
private heartbeatInterval: number;
3235

3336
constructor({
3437
project_id,
@@ -40,6 +43,7 @@ export class ConatTerminal extends EventEmitter {
4043
options,
4144
measureSize,
4245
ephemeral,
46+
heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL,
4347
}: {
4448
project_id: string;
4549
path: string;
@@ -50,6 +54,7 @@ export class ConatTerminal extends EventEmitter {
5054
options?;
5155
measureSize?;
5256
ephemeral?: boolean;
57+
heartbeatInterval?: number;
5358
}) {
5459
super();
5560
this.ephemeral = ephemeral;
@@ -63,13 +68,35 @@ export class ConatTerminal extends EventEmitter {
6368
this.terminalResize = terminalResize;
6469
this.openPaths = openPaths;
6570
this.closePaths = closePaths;
71+
this.heartbeatInterval = heartbeatInterval;
6672
webapp_client.conat_client.on("connected", this.clearWriteQueue);
6773
this.computeServers = webapp_client.project_client.computeServers(
6874
this.project_id,
6975
);
7076
this.computeServers?.on("change", this.handleComputeServersChange);
7177
}
7278

79+
// ping server periodically -- if failure, closes conenction immediately
80+
// so it can be fixed when user comes back, instead of having to react to
81+
// a write failing (which also handles the same issue)
82+
private heartbeat = reuseInFlight(async () => {
83+
await until(
84+
async () => {
85+
if (this.isClosed()) {
86+
return true;
87+
}
88+
try {
89+
await this.api.conat.ping({ maxWait: 5000 });
90+
return false;
91+
} catch {
92+
this.close();
93+
return true;
94+
}
95+
},
96+
{ min: this.heartbeatInterval, max: this.heartbeatInterval },
97+
);
98+
});
99+
73100
clearWriteQueue = () => {
74101
if (this.writeQueue) {
75102
this.write("");
@@ -147,6 +174,8 @@ export class ConatTerminal extends EventEmitter {
147174
}
148175
};
149176

177+
isClosed = () => this.state == "closed";
178+
150179
close = () => {
151180
webapp_client.conat_client.removeListener(
152181
"connected",
@@ -239,6 +268,7 @@ export class ConatTerminal extends EventEmitter {
239268
init = reuseInFlight(async () => {
240269
await Promise.all([this.start(), this.getStream()]);
241270
await this.setReady();
271+
this.heartbeat();
242272
});
243273

244274
private handleStreamData = (data) => {

src/packages/frontend/project/context.tsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface ProjectContextState {
3232
actions?: ProjectActions;
3333
active_project_tab?: string;
3434
compute_image: string | undefined;
35+
contentSize: { width: number; height: number };
3536
enabledLLMs: LLMServicesAvailable;
3637
flipTabs: [number, React.Dispatch<React.SetStateAction<number>>];
3738
group?: UserGroup;
@@ -47,22 +48,15 @@ export interface ProjectContextState {
4748
onCoCalcDocker: boolean;
4849
project_id: string;
4950
project?: Project;
51+
setContentSize: (size: { width: number; height: number }) => void;
5052
status: ProjectStatus;
5153
}
5254

5355
export const emptyProjectContext = {
5456
actions: undefined,
5557
active_project_tab: undefined,
56-
group: undefined,
57-
project: undefined,
58-
is_active: false,
59-
project_id: "",
60-
isRunning: undefined,
61-
status: INIT_PROJECT_STATE,
62-
hasInternet: undefined,
63-
flipTabs: [0, () => {}],
64-
onCoCalcCom: true,
65-
onCoCalcDocker: false,
58+
compute_image: undefined,
59+
contentSize: { width: 0, height: 0 },
6660
enabledLLMs: {
6761
openai: false,
6862
google: false,
@@ -72,12 +66,22 @@ export const emptyProjectContext = {
7266
custom_openai: false,
7367
user: false,
7468
},
69+
flipTabs: [0, () => {}],
70+
group: undefined,
71+
hasInternet: undefined,
72+
is_active: false,
73+
isRunning: undefined,
7574
mainWidthPx: 0,
7675
manageStarredFiles: {
7776
starred: [],
7877
setStarredPath: () => {},
7978
},
80-
compute_image: undefined,
79+
onCoCalcCom: true,
80+
onCoCalcDocker: false,
81+
project: undefined,
82+
project_id: "",
83+
setContentSize: () => {},
84+
status: INIT_PROJECT_STATE,
8185
} as ProjectContextState;
8286

8387
export const ProjectContext: Context<ProjectContextState> =
@@ -141,10 +145,13 @@ export function useProjectContextProvider({
141145
userDefinedLLM,
142146
]);
143147

148+
const [contentSize, setContentSize] = useState({ width: 0, height: 0 });
149+
144150
return {
145151
actions,
146152
active_project_tab,
147153
compute_image,
154+
contentSize,
148155
enabledLLMs,
149156
flipTabs,
150157
group,
@@ -157,6 +164,7 @@ export function useProjectContextProvider({
157164
onCoCalcDocker,
158165
project_id,
159166
project,
167+
setContentSize,
160168
status,
161169
};
162170
}

0 commit comments

Comments
 (0)