Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,38 @@

All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- `GetClawSpace`: `client.spaces.get` and `arkclaw space get` for retrieving
ArkClaw space detail (endpoints, auth type, status, APM ID).
- `ListUsersModelConfig`: `client.spaces.list_users_model_config` and
`arkclaw space list-users-model-config` for paginated user model
configuration lookup with optional `UserIds` filter.
- `ListUsers`: `client.users.list` and `arkclaw user list` for listing users
in a space with department, group, email, phone, name, and user-ID filters.
- `CreateClawInstance`: new optional parameters `EnableHeadless`,
`ClientToken`, `DryRun` on `client.instances.create` and
`arkclaw instance create`.
- `UpdateClawInstance`: reassign or unbind the owning user via
`client.instances.update(user_id=...)` / `arkclaw instance update --user-id`,
which populates `Patch.UserId.Value` and `FieldMask.Paths=[Patch.UserId]`
under the hood. Omitting `user_id` leaves the binding untouched; passing an
empty string or `None` unbinds the current user.

### Changed

- `CreateClawInstance`: `UserId` is now optional (was required).
- `CreateUsers`: spec explicitly declares `Users` as a required `object[]`;
user element fields remain the same.

### Fixed

- Parameter spec `.N` list-marker stripping now uses a positional regex so
legitimate field names such as `Filter.Name` are no longer mangled into
`Filterame`.

## [0.1.0] - 2026-06-23

### Added
Expand Down
45 changes: 43 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ Spaces:
| OpenAPI action | SDK method | CLI command |
|---|---|---|
| `ListClawSpaces` | `client.spaces.list` | `arkclaw space list` |
| `GetClawSpace` | `client.spaces.get` | `arkclaw space get` |
| `UpdateUsersModelConfig` | `client.spaces.update_users_model_config` | `arkclaw space update-users-model-config` |
| `ListUsersModelConfig` | `client.spaces.list_users_model_config` | `arkclaw space list-users-model-config` |

Instances:

Expand All @@ -79,6 +81,7 @@ Users:
| `CreateUser` | `client.users.create` | `arkclaw user create` |
| `UpdateUser` | `client.users.update` | `arkclaw user update` |
| `DeleteUser` | `client.users.delete` | `arkclaw user delete` |
| `ListUsers` | `client.users.list` | `arkclaw user list` |

Additional SDK workflows include `wait_for_instance`, `provision_instance`, and
`prepare_chat_access` under `client.workflows`.
Expand All @@ -92,6 +95,20 @@ client = ArkClawClient.from_env()

spaces = client.spaces.list()

space = client.spaces.get(space_id="csi-xxx")

user_configs = client.spaces.list_users_model_config(
space_id="csi-xxx",
user_ids=["user-xxx"],
max_results=20,
)

users_page = client.users.list(
space_id="csi-xxx",
filter={"name": "zhang", "user_ids": ["user-xxx"]},
max_results=20,
)

model_config_result = client.spaces.update_users_model_config(
space_id="csi-xxx",
user_ids=["user-xxx"],
Expand Down Expand Up @@ -128,6 +145,12 @@ client.instances.update(
instance_name="demo-claw-renamed",
)

client.instances.update(
space_id="csi-xxx",
instance_id=instance["InstanceId"],
user_id="user-yyy", # pass "" to unbind the current owner
)

terminal = client.instances.get_terminal_token(
space_id="csi-xxx",
instance_id=instance["InstanceId"],
Expand Down Expand Up @@ -221,8 +244,8 @@ Supported command groups and subcommands:

| Group | Subcommands |
|---|---|
| `space` | `list`, `update-users-model-config` |
| `user` | `create`, `create-many`, `update`, `delete` |
| `space` | `list`, `get`, `update-users-model-config`, `list-users-model-config` |
| `user` | `create`, `create-many`, `update`, `delete`, `list` |
| `instance` | `create`, `update-model`, `chat-token`, `get`, `update-channel`, `list`, `start`, `stop`, `reset`, `update`, `delete`, `terminal-token`, `wait` |
| `message` | `send`, `shell` |

Expand All @@ -231,11 +254,24 @@ Selected examples:
```bash
arkclaw space list

arkclaw space get --space-id csi-xxx

arkclaw space list-users-model-config \
--space-id csi-xxx \
--user-id user-xxx \
--max-results 20

arkclaw space update-users-model-config \
--space-id csi-xxx \
--user-id user-xxx \
--coding-plan-seat-type Lite

arkclaw user list \
--space-id csi-xxx \
--filter-name zhang \
--filter-user-id user-xxx \
--max-results 20

arkclaw user create \
--space-id csi-xxx \
--email alice@example.com \
Expand Down Expand Up @@ -266,6 +302,11 @@ arkclaw instance update \
--instance-id ci-xxx \
--instance-name demo-claw-renamed

arkclaw instance update \
--space-id csi-xxx \
--instance-id ci-xxx \
--user-id user-yyy # pass "" to unbind the current owner

arkclaw instance terminal-token \
--space-id csi-xxx \
--instance-id ci-xxx
Expand Down
35 changes: 26 additions & 9 deletions arkclaw/cli/instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,25 @@
from ._common import build_client, emit, info


_CLI_USER_ID_UNSET = object()


def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[type-arg]
group = subparsers.add_parser("instance", help="ClawInstance management")
sub = group.add_subparsers(dest="instance_action")
sub.required = True

p = sub.add_parser("create", help="Create a ClawInstance")
p.add_argument("--space-id", required=True)
p.add_argument("--user-id", required=True)
p.add_argument("--user-id", default=None)
p.add_argument("--instance-name", required=True)
p.add_argument("--seat-type", required=True, choices=["Starter", "Standard", "Premium", "Ultimate"])
p.add_argument("--description", default=None)
p.add_argument("--model-api-key", default=None)
p.add_argument("--template-id", default=None)
p.add_argument("--enable-headless", choices=["true", "false"], default=None)
p.add_argument("--client-token", default=None)
p.add_argument("--dry-run", choices=["true", "false"], default=None)
p.add_argument("--wait", action="store_true", default=False)
p.add_argument("--wait-timeout", type=float, default=600)
p.add_argument("--interval", type=float, default=5)
Expand Down Expand Up @@ -101,6 +107,11 @@ def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[ty
p.add_argument("--space-id", required=True)
p.add_argument("--instance-id", required=True)
p.add_argument("--instance-name", default=None)
p.add_argument(
"--user-id",
default=_CLI_USER_ID_UNSET,
help="Reassign the instance to this user ID; pass an empty string to unbind. Omit to leave the binding unchanged.",
)
p.add_argument("--client-token", default=None)
p.add_argument("--dry-run", choices=["true", "false"], default=None)
p.set_defaults(func=_update)
Expand Down Expand Up @@ -129,6 +140,8 @@ def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[ty

def _create(args: argparse.Namespace) -> None:
client = build_client(args)
enable_headless = None if args.enable_headless is None else args.enable_headless == "true"
dry_run = None if args.dry_run is None else args.dry_run == "true"
if args.wait:
info(f"Creating {args.instance_name} and waiting for Running...")
result = client.workflows.provision_instance(
Expand All @@ -151,6 +164,9 @@ def _create(args: argparse.Namespace) -> None:
description=args.description,
model_api_key=args.model_api_key,
template_id=args.template_id,
enable_headless=enable_headless,
client_token=args.client_token,
dry_run=dry_run,
)
emit(result)

Expand Down Expand Up @@ -258,15 +274,16 @@ def _reset(args: argparse.Namespace) -> None:
def _update(args: argparse.Namespace) -> None:
client = build_client(args)
dry_run = None if args.dry_run is None else args.dry_run == "true"
emit(
client.instances.update(
space_id=args.space_id,
instance_id=args.instance_id,
instance_name=None if args.instance_name in (None, "") else args.instance_name,
client_token=args.client_token,
dry_run=dry_run,
)
update_kwargs: dict[str, Any] = dict(
space_id=args.space_id,
instance_id=args.instance_id,
instance_name=None if args.instance_name in (None, "") else args.instance_name,
client_token=args.client_token,
dry_run=dry_run,
)
if args.user_id is not _CLI_USER_ID_UNSET:
update_kwargs["user_id"] = args.user_id
emit(client.instances.update(**update_kwargs))


def _delete(args: argparse.Namespace) -> None:
Expand Down
28 changes: 28 additions & 0 deletions arkclaw/cli/spaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[ty
p.add_argument("--space-name", default=None)
p.set_defaults(func=_list)

p = sub.add_parser("get", help="Get ClawSpace detail")
p.add_argument("--space-id", required=True)
p.set_defaults(func=_get)

p = sub.add_parser("update-users-model-config", help="Update codingPlan seat type and token limits for users")
p.add_argument("--space-id", required=True)
p.add_argument("--user-id", dest="user_ids", action="append", required=True)
Expand All @@ -38,6 +42,13 @@ def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[ty
p.add_argument("--token-rate-limit-per-day", type=int, default=None)
p.set_defaults(func=_update_users_model_config)

p = sub.add_parser("list-users-model-config", help="List user model configurations in a space")
p.add_argument("--space-id", required=True)
p.add_argument("--user-id", dest="user_ids", action="append", default=None)
p.add_argument("--max-results", type=int, default=None)
p.add_argument("--next-token", default=None)
p.set_defaults(func=_list_users_model_config)


def _model_config_kwargs(args: argparse.Namespace) -> dict[str, Any]:
return {
Expand All @@ -52,6 +63,11 @@ def _list(args: argparse.Namespace) -> None:
emit(client.spaces.list(project_name=args.project_name, space_name=args.space_name))


def _get(args: argparse.Namespace) -> None:
client = build_client(args)
emit(client.spaces.get(space_id=args.space_id))


def _update_users_model_config(args: argparse.Namespace) -> None:
client = build_client(args)
emit(
Expand All @@ -61,3 +77,15 @@ def _update_users_model_config(args: argparse.Namespace) -> None:
model_config=_model_config_kwargs(args),
)
)


def _list_users_model_config(args: argparse.Namespace) -> None:
client = build_client(args)
emit(
client.spaces.list_users_model_config(
space_id=args.space_id,
user_ids=args.user_ids,
max_results=args.max_results,
next_token=args.next_token,
)
)
67 changes: 67 additions & 0 deletions arkclaw/cli/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,48 @@ def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[ty
p.add_argument("--user-id", required=True)
p.set_defaults(func=_delete)

p = sub.add_parser("list", help="List users in a space with optional filters")
p.add_argument("--space-id", required=True)
p.add_argument("--max-results", type=int, default=None)
p.add_argument("--next-token", default=None)
p.add_argument("--filter-department-uid", default=None)
p.add_argument(
"--filter-department-uid-recursive",
dest="filter_department_uid_recursive",
action="store_true",
default=None,
)
p.add_argument("--filter-email", default=None)
p.add_argument(
"--filter-email-phone-name-is-null-or-empty",
dest="filter_email_phone_name_is_null_or_empty",
action="store_true",
default=None,
)
p.add_argument("--filter-group-uid", default=None)
p.add_argument("--filter-name", default=None)
p.add_argument(
"--filter-not-in-any-department",
dest="filter_not_in_any_department",
action="store_true",
default=None,
)
p.add_argument(
"--filter-not-in-any-group",
dest="filter_not_in_any_group",
action="store_true",
default=None,
)
p.add_argument("--filter-phone-number", default=None)
p.add_argument(
"--filter-user-id",
dest="filter_user_ids",
action="append",
default=None,
help="User ID filter; may be given multiple times",
)
p.set_defaults(func=_list)


def _add_user_fields(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--email", default=None)
Expand Down Expand Up @@ -90,3 +132,28 @@ def _delete(args: argparse.Namespace) -> None:
client = build_client(args)
emit(client.users.delete(space_id=args.space_id, user_id=args.user_id))


def _list(args: argparse.Namespace) -> None:
client = build_client(args)
filter_kwargs: dict[str, Any] = {
"department_uid": args.filter_department_uid,
"department_uid_recursive": args.filter_department_uid_recursive,
"email": args.filter_email,
"email_phone_name_is_null_or_empty": args.filter_email_phone_name_is_null_or_empty,
"group_uid": args.filter_group_uid,
"name": args.filter_name,
"not_in_any_department": args.filter_not_in_any_department,
"not_in_any_group": args.filter_not_in_any_group,
"phone_number": args.filter_phone_number,
"user_ids": args.filter_user_ids,
}
filter_payload = {k: v for k, v in filter_kwargs.items() if v is not None}
emit(
client.users.list(
space_id=args.space_id,
filter=filter_payload or None,
max_results=args.max_results,
next_token=args.next_token,
)
)

Loading
Loading