Skip to content

Projects

Source code in src/cocalc_api/hub.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
class Projects:

    def __init__(self, parent: "Hub"):
        self._parent = parent

    def get(
        self,
        fields: Optional[list[str]] = None,
        all: Optional[bool] = False,
        project_id: Optional[str] = None,
        limit: Optional[int] = None,
        deleted: Optional[bool] = None,
        hidden: Optional[bool] = None,
        state: Optional[str] = None,
        account_id_for_hidden: Optional[str] = None,
    ) -> list[dict[str, Any]]:
        """
        Get data about projects that you are a collaborator on. Only gets
        recent projects by default; set all=True to get all projects.

        Args:
            fields (Optional[list[str]]): The fields about the project to get.
                Default: ['project_id', 'title', 'last_edited', 'created', 'state', 'deleted', 'users'], but see
                https://github.com/sagemathinc/cocalc/blob/master/src/packages/util/db-schema/projects.ts
            all (Optional[bool]): If True, return ALL your projects,
                not just the recent ones. False by default.
            project_id (Optional[str]): If given, gets just this
                one project (as a list of length 1).
            limit (Optional[int]): Maximum number of projects to return after filtering. None means no limit.
            deleted (Optional[bool]): If set, filter deleted status (True -> only deleted, False -> only not deleted).
            hidden (Optional[bool]): If set, filter by collaborator-specific hidden flag. Default None (no filter).
            state (Optional[str]): If set, only return projects whose state matches (e.g., 'opened', 'running').
            account_id_for_hidden (Optional[str]): Account ID used to evaluate the hidden flag in the users map.

        Returns:
            list[dict[str, Any]]: List of projects.
        """
        from datetime import datetime

        def _parse_ts(value: Any) -> float:
            if value is None:
                return 0.0
            if isinstance(value, (int, float)):
                return float(value)
            if isinstance(value, str):
                try:
                    return datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp()
                except ValueError:
                    try:
                        return float(value)
                    except Exception:
                        return 0.0
            return 0.0

        def _state_str(val: Any) -> str:
            if isinstance(val, dict):
                return str(val.get("state") or val.get("status") or "")
            if val is None:
                return ""
            return str(val)

        if fields is None:
            fields = ['project_id', 'title', 'last_edited', 'created', 'state', 'deleted', 'users']
        v: list[dict[str, Any]] = [{}]
        for field in fields:
            v[0][field] = None
        if project_id:
            v[0]['project_id'] = project_id
        query: dict[str, list[dict[str, Any]]] = {}
        table = 'projects_all' if all else 'projects'
        query[table] = v
        result = self._parent.db.query(query)
        projects: list[dict[str, Any]] = result[table]

        filtered: list[dict[str, Any]] = []
        for project in projects:
            if deleted is not None:
                if bool(project.get("deleted")) != deleted:
                    continue

            if state:
                project_state = _state_str(project.get("state")).lower()
                if project_state != state.lower():
                    continue

            if hidden is not None and account_id_for_hidden:
                users = project.get("users") or {}
                if isinstance(users, dict):
                    user_info = users.get(account_id_for_hidden, {})
                    is_hidden = False
                    if isinstance(user_info, dict):
                        is_hidden = bool(user_info.get("hide"))
                    if is_hidden != hidden:
                        continue

            filtered.append(project)

        filtered.sort(
            key=lambda p: (
                _parse_ts(p.get("last_edited")),
                _parse_ts(p.get("created")),
                (p.get("title") or "").lower(),
                p.get("project_id") or "",
            ),
            reverse=True,
        )

        if limit is not None and limit >= 0:
            filtered = filtered[:limit]

        return filtered

    @api_method("projects.copyPathBetweenProjects")
    def copy_path_between_projects(
        self,
        src_project_id: str,
        src_path: str,
        target_project_id: Optional[str] = None,
        target_path: Optional[str] = None,
    ) -> dict[str, Any]:  # type: ignore[empty-body]
        """
        Copy a path from one project to another (or within a project).

        Args:
            src_project_id (str): Source project ID.
            src_path (str): Path in the source project to copy.
            target_project_id (Optional[str]): Target project ID. Defaults to src_project_id.
            target_path (Optional[str]): Target path in the target project. Defaults to src_path.

        Returns:
            dict[str, Any]: JSON response indicating success or error.
        """
        ...

    @api_method("projects.createProject")
    def create_project(
        self,
        title: Optional[str] = None,
        description: Optional[str] = None,
        license: Optional[str] = None,
        public_path_id: Optional[str] = None,
    ) -> str:
        """
        Create a new project.

        Args:
            title (Optional[str]): Title of the project.
            description (Optional[str]): Description of the project.
            license (Optional[str]): License ID (or multiple IDs separated by commas).
            public_path_id (Optional[str]): If provided, project is populated with content from this shared path.

        Returns:
            str: The ID of the newly created project.
        """
        ...

    @api_method("projects.addCollaborator", opts=True)
    def add_collaborator(self, project_id: str | list[str], account_id: str | list[str]) -> dict[str, Any]:
        """
        Add a collaborator to a project.

        Args:
            project_id (str | list[str]): Project ID(s) to add a collaborator to.
            account_id (str | list[str]): Account ID(s) of the collaborator(s).

        Note:
            You can pass arrays of the same length for `project_id` and `account_id` to
            add several collaborators at once. In this case, `account_id[i]` is added to
            `project_id[i]`.

        Returns:
            dict[str, Any]: JSON response from the API.
        """
        ...  # pragma: no cover

    @api_method("projects.removeCollaborator", opts=True)
    def remove_collaborator(self, project_id: str, account_id: str) -> dict[str, Any]:
        """
        Remove a collaborator from a project.

        Args:
            project_id (str): The project to remove a user from.
            account_id (str): Account ID of the user to remove.

        Returns:
            dict[str, Any]: JSON response from the API.
        """
        ...  # pragma: no cover

    @api_method("projects.start")
    def start(self, project_id: str) -> dict[str, Any]:
        """
        Start a project.

        Args:
            project_id (str): Project ID of the project to start.
        """
        ...

    @api_method("projects.stop")
    def stop(self, project_id: str) -> dict[str, Any]:
        """
        Stop a project.

        Args:
            project_id (str): Project ID of the project to stop.
        """
        ...

    @api_method("projects.deleteProject")
    def delete(self, project_id: str) -> dict[str, Any]:
        """
        Delete a project by setting the deleted flag to true.

        Args:
            project_id (str): Project ID of the project to delete.

        Returns:
            dict[str, Any]: API response indicating success.
        """
        ...

    @api_method("projects.touch")
    def touch(self, project_id: str) -> dict[str, Any]:
        """
        Signal that the project is in use by updating its last_edited timestamp.
        This also ensures the project is started.

        Args:
            project_id (str): Project ID of the project to touch.

        Returns:
            dict[str, Any]: API response indicating success.
        """
        ...

    @api_method("projects.state")
    def state(self, project_id: str) -> dict[str, Any]:
        """
        Get the current state of a project (running, stopped, starting, etc.).

        Args:
            project_id (str): Project ID of the project.

        Returns:
            dict[str, Any]: Project state object containing:
                - state: "running" | "stopped" | "starting" | "restarting" | "error"
                - ip: IP address where project is running (if running)
                - error: Error message (if in error state)
                - time: Timestamp of last state change
        """
        ...

    @api_method("projects.status")
    def status(self, project_id: str) -> dict[str, Any]:
        """
        Get detailed status information about a project.

        Args:
            project_id (str): Project ID of the project.

        Returns:
            dict[str, Any]: Project status object containing:
                - project.pid: PID of project server process
                - start_ts: Timestamp when project started
                - version: Project code version
                - disk_MB: Disk usage in MB
                - memory: Memory usage information
        """
        ...

add_collaborator(project_id, account_id)

Add a collaborator to a project.

Parameters:

Name Type Description Default
project_id str | list[str]

Project ID(s) to add a collaborator to.

required
account_id str | list[str]

Account ID(s) of the collaborator(s).

required
Note

You can pass arrays of the same length for project_id and account_id to add several collaborators at once. In this case, account_id[i] is added to project_id[i].

Returns:

Type Description
dict[str, Any]

dict[str, Any]: JSON response from the API.

Source code in src/cocalc_api/hub.py
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
@api_method("projects.addCollaborator", opts=True)
def add_collaborator(self, project_id: str | list[str], account_id: str | list[str]) -> dict[str, Any]:
    """
    Add a collaborator to a project.

    Args:
        project_id (str | list[str]): Project ID(s) to add a collaborator to.
        account_id (str | list[str]): Account ID(s) of the collaborator(s).

    Note:
        You can pass arrays of the same length for `project_id` and `account_id` to
        add several collaborators at once. In this case, `account_id[i]` is added to
        `project_id[i]`.

    Returns:
        dict[str, Any]: JSON response from the API.
    """
    ...  # pragma: no cover

copy_path_between_projects(src_project_id, src_path, target_project_id=None, target_path=None)

Copy a path from one project to another (or within a project).

Parameters:

Name Type Description Default
src_project_id str

Source project ID.

required
src_path str

Path in the source project to copy.

required
target_project_id Optional[str]

Target project ID. Defaults to src_project_id.

None
target_path Optional[str]

Target path in the target project. Defaults to src_path.

None

Returns:

Type Description
dict[str, Any]

dict[str, Any]: JSON response indicating success or error.

Source code in src/cocalc_api/hub.py
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
@api_method("projects.copyPathBetweenProjects")
def copy_path_between_projects(
    self,
    src_project_id: str,
    src_path: str,
    target_project_id: Optional[str] = None,
    target_path: Optional[str] = None,
) -> dict[str, Any]:  # type: ignore[empty-body]
    """
    Copy a path from one project to another (or within a project).

    Args:
        src_project_id (str): Source project ID.
        src_path (str): Path in the source project to copy.
        target_project_id (Optional[str]): Target project ID. Defaults to src_project_id.
        target_path (Optional[str]): Target path in the target project. Defaults to src_path.

    Returns:
        dict[str, Any]: JSON response indicating success or error.
    """
    ...

create_project(title=None, description=None, license=None, public_path_id=None)

Create a new project.

Parameters:

Name Type Description Default
title Optional[str]

Title of the project.

None
description Optional[str]

Description of the project.

None
license Optional[str]

License ID (or multiple IDs separated by commas).

None
public_path_id Optional[str]

If provided, project is populated with content from this shared path.

None

Returns:

Name Type Description
str str

The ID of the newly created project.

Source code in src/cocalc_api/hub.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
@api_method("projects.createProject")
def create_project(
    self,
    title: Optional[str] = None,
    description: Optional[str] = None,
    license: Optional[str] = None,
    public_path_id: Optional[str] = None,
) -> str:
    """
    Create a new project.

    Args:
        title (Optional[str]): Title of the project.
        description (Optional[str]): Description of the project.
        license (Optional[str]): License ID (or multiple IDs separated by commas).
        public_path_id (Optional[str]): If provided, project is populated with content from this shared path.

    Returns:
        str: The ID of the newly created project.
    """
    ...

delete(project_id)

Delete a project by setting the deleted flag to true.

Parameters:

Name Type Description Default
project_id str

Project ID of the project to delete.

required

Returns:

Type Description
dict[str, Any]

dict[str, Any]: API response indicating success.

Source code in src/cocalc_api/hub.py
362
363
364
365
366
367
368
369
370
371
372
373
@api_method("projects.deleteProject")
def delete(self, project_id: str) -> dict[str, Any]:
    """
    Delete a project by setting the deleted flag to true.

    Args:
        project_id (str): Project ID of the project to delete.

    Returns:
        dict[str, Any]: API response indicating success.
    """
    ...

get(fields=None, all=False, project_id=None, limit=None, deleted=None, hidden=None, state=None, account_id_for_hidden=None)

Get data about projects that you are a collaborator on. Only gets recent projects by default; set all=True to get all projects.

Parameters:

Name Type Description Default
fields Optional[list[str]]

The fields about the project to get. Default: ['project_id', 'title', 'last_edited', 'created', 'state', 'deleted', 'users'], but see https://github.com/sagemathinc/cocalc/blob/master/src/packages/util/db-schema/projects.ts

None
all Optional[bool]

If True, return ALL your projects, not just the recent ones. False by default.

False
project_id Optional[str]

If given, gets just this one project (as a list of length 1).

None
limit Optional[int]

Maximum number of projects to return after filtering. None means no limit.

None
deleted Optional[bool]

If set, filter deleted status (True -> only deleted, False -> only not deleted).

None
hidden Optional[bool]

If set, filter by collaborator-specific hidden flag. Default None (no filter).

None
state Optional[str]

If set, only return projects whose state matches (e.g., 'opened', 'running').

None
account_id_for_hidden Optional[str]

Account ID used to evaluate the hidden flag in the users map.

None

Returns:

Type Description
list[dict[str, Any]]

list[dict[str, Any]]: List of projects.

Source code in src/cocalc_api/hub.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
def get(
    self,
    fields: Optional[list[str]] = None,
    all: Optional[bool] = False,
    project_id: Optional[str] = None,
    limit: Optional[int] = None,
    deleted: Optional[bool] = None,
    hidden: Optional[bool] = None,
    state: Optional[str] = None,
    account_id_for_hidden: Optional[str] = None,
) -> list[dict[str, Any]]:
    """
    Get data about projects that you are a collaborator on. Only gets
    recent projects by default; set all=True to get all projects.

    Args:
        fields (Optional[list[str]]): The fields about the project to get.
            Default: ['project_id', 'title', 'last_edited', 'created', 'state', 'deleted', 'users'], but see
            https://github.com/sagemathinc/cocalc/blob/master/src/packages/util/db-schema/projects.ts
        all (Optional[bool]): If True, return ALL your projects,
            not just the recent ones. False by default.
        project_id (Optional[str]): If given, gets just this
            one project (as a list of length 1).
        limit (Optional[int]): Maximum number of projects to return after filtering. None means no limit.
        deleted (Optional[bool]): If set, filter deleted status (True -> only deleted, False -> only not deleted).
        hidden (Optional[bool]): If set, filter by collaborator-specific hidden flag. Default None (no filter).
        state (Optional[str]): If set, only return projects whose state matches (e.g., 'opened', 'running').
        account_id_for_hidden (Optional[str]): Account ID used to evaluate the hidden flag in the users map.

    Returns:
        list[dict[str, Any]]: List of projects.
    """
    from datetime import datetime

    def _parse_ts(value: Any) -> float:
        if value is None:
            return 0.0
        if isinstance(value, (int, float)):
            return float(value)
        if isinstance(value, str):
            try:
                return datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp()
            except ValueError:
                try:
                    return float(value)
                except Exception:
                    return 0.0
        return 0.0

    def _state_str(val: Any) -> str:
        if isinstance(val, dict):
            return str(val.get("state") or val.get("status") or "")
        if val is None:
            return ""
        return str(val)

    if fields is None:
        fields = ['project_id', 'title', 'last_edited', 'created', 'state', 'deleted', 'users']
    v: list[dict[str, Any]] = [{}]
    for field in fields:
        v[0][field] = None
    if project_id:
        v[0]['project_id'] = project_id
    query: dict[str, list[dict[str, Any]]] = {}
    table = 'projects_all' if all else 'projects'
    query[table] = v
    result = self._parent.db.query(query)
    projects: list[dict[str, Any]] = result[table]

    filtered: list[dict[str, Any]] = []
    for project in projects:
        if deleted is not None:
            if bool(project.get("deleted")) != deleted:
                continue

        if state:
            project_state = _state_str(project.get("state")).lower()
            if project_state != state.lower():
                continue

        if hidden is not None and account_id_for_hidden:
            users = project.get("users") or {}
            if isinstance(users, dict):
                user_info = users.get(account_id_for_hidden, {})
                is_hidden = False
                if isinstance(user_info, dict):
                    is_hidden = bool(user_info.get("hide"))
                if is_hidden != hidden:
                    continue

        filtered.append(project)

    filtered.sort(
        key=lambda p: (
            _parse_ts(p.get("last_edited")),
            _parse_ts(p.get("created")),
            (p.get("title") or "").lower(),
            p.get("project_id") or "",
        ),
        reverse=True,
    )

    if limit is not None and limit >= 0:
        filtered = filtered[:limit]

    return filtered

remove_collaborator(project_id, account_id)

Remove a collaborator from a project.

Parameters:

Name Type Description Default
project_id str

The project to remove a user from.

required
account_id str

Account ID of the user to remove.

required

Returns:

Type Description
dict[str, Any]

dict[str, Any]: JSON response from the API.

Source code in src/cocalc_api/hub.py
328
329
330
331
332
333
334
335
336
337
338
339
340
@api_method("projects.removeCollaborator", opts=True)
def remove_collaborator(self, project_id: str, account_id: str) -> dict[str, Any]:
    """
    Remove a collaborator from a project.

    Args:
        project_id (str): The project to remove a user from.
        account_id (str): Account ID of the user to remove.

    Returns:
        dict[str, Any]: JSON response from the API.
    """
    ...  # pragma: no cover

start(project_id)

Start a project.

Parameters:

Name Type Description Default
project_id str

Project ID of the project to start.

required
Source code in src/cocalc_api/hub.py
342
343
344
345
346
347
348
349
350
@api_method("projects.start")
def start(self, project_id: str) -> dict[str, Any]:
    """
    Start a project.

    Args:
        project_id (str): Project ID of the project to start.
    """
    ...

state(project_id)

Get the current state of a project (running, stopped, starting, etc.).

Parameters:

Name Type Description Default
project_id str

Project ID of the project.

required

Returns:

Type Description
dict[str, Any]

dict[str, Any]: Project state object containing: - state: "running" | "stopped" | "starting" | "restarting" | "error" - ip: IP address where project is running (if running) - error: Error message (if in error state) - time: Timestamp of last state change

Source code in src/cocalc_api/hub.py
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
@api_method("projects.state")
def state(self, project_id: str) -> dict[str, Any]:
    """
    Get the current state of a project (running, stopped, starting, etc.).

    Args:
        project_id (str): Project ID of the project.

    Returns:
        dict[str, Any]: Project state object containing:
            - state: "running" | "stopped" | "starting" | "restarting" | "error"
            - ip: IP address where project is running (if running)
            - error: Error message (if in error state)
            - time: Timestamp of last state change
    """
    ...

status(project_id)

Get detailed status information about a project.

Parameters:

Name Type Description Default
project_id str

Project ID of the project.

required

Returns:

Type Description
dict[str, Any]

dict[str, Any]: Project status object containing: - project.pid: PID of project server process - start_ts: Timestamp when project started - version: Project code version - disk_MB: Disk usage in MB - memory: Memory usage information

Source code in src/cocalc_api/hub.py
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
@api_method("projects.status")
def status(self, project_id: str) -> dict[str, Any]:
    """
    Get detailed status information about a project.

    Args:
        project_id (str): Project ID of the project.

    Returns:
        dict[str, Any]: Project status object containing:
            - project.pid: PID of project server process
            - start_ts: Timestamp when project started
            - version: Project code version
            - disk_MB: Disk usage in MB
            - memory: Memory usage information
    """
    ...

stop(project_id)

Stop a project.

Parameters:

Name Type Description Default
project_id str

Project ID of the project to stop.

required
Source code in src/cocalc_api/hub.py
352
353
354
355
356
357
358
359
360
@api_method("projects.stop")
def stop(self, project_id: str) -> dict[str, Any]:
    """
    Stop a project.

    Args:
        project_id (str): Project ID of the project to stop.
    """
    ...

touch(project_id)

Signal that the project is in use by updating its last_edited timestamp. This also ensures the project is started.

Parameters:

Name Type Description Default
project_id str

Project ID of the project to touch.

required

Returns:

Type Description
dict[str, Any]

dict[str, Any]: API response indicating success.

Source code in src/cocalc_api/hub.py
375
376
377
378
379
380
381
382
383
384
385
386
387
@api_method("projects.touch")
def touch(self, project_id: str) -> dict[str, Any]:
    """
    Signal that the project is in use by updating its last_edited timestamp.
    This also ensures the project is started.

    Args:
        project_id (str): Project ID of the project to touch.

    Returns:
        dict[str, Any]: API response indicating success.
    """
    ...