@gapi/gcli 1.8.243 → 1.8.244
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.
- package/.agents/skills/local-ai-runners/SKILL.md +9 -0
- package/AGENTS.md +3 -2
- package/README-CLI.md +55 -0
- package/README.md +50 -0
- package/docs/changelog.d/2026-10-23-local-workspaces-gcli-workspace-start-list-delete-the-chat-agents-work-in-a-l.md +13 -0
- package/docs/features/README.md +1 -0
- package/docs/features/local-workspaces.md +42 -0
- package/package.json +1 -1
- package/release/index.js +2754 -1098
|
@@ -44,3 +44,12 @@ node release/index.js runner:models --show qwen3.8:27b # keep the picker short
|
|
|
44
44
|
Lambforge socket (acks, operation replies, pushes requests), `fakeOllama`
|
|
45
45
|
plays Ollama (tags/show/streamed body, optional hold). Add a case there for
|
|
46
46
|
any protocol change.
|
|
47
|
+
|
|
48
|
+
## Shared with `workspace:*`
|
|
49
|
+
|
|
50
|
+
The WebSocket client is `TunnelConnection` (`src/commands/runner/tunnel.ts`);
|
|
51
|
+
`RunnerConnection` (Ollama) and `WorkspaceConnection` (local workspaces,
|
|
52
|
+
`src/commands/workspace/`, feature `local-workspaces`) are its two
|
|
53
|
+
subclasses. Protocol changes (frames, ack/ping, reconnect) go in the base
|
|
54
|
+
class and must keep both test files green (`test/runner.spec.ts`,
|
|
55
|
+
`test/workspace.spec.ts`).
|
package/AGENTS.md
CHANGED
|
@@ -34,7 +34,7 @@ fragment in every repo the change touches.
|
|
|
34
34
|
|---|---|
|
|
35
35
|
| `gcli-command` | Adding a command: registration, lazy actions, `GraphqlClienAPI`, tests, build/release, regenerating `README-CLI.md` |
|
|
36
36
|
| `cluster-targeting` | Commands touching resources that can live on a private cluster (`--clusterId/--clusterName`) |
|
|
37
|
-
| `local-ai-runners` | `runner:*` - the outbound WS client bridging a user's Ollama to the chat |
|
|
37
|
+
| `local-ai-runners` | `runner:*` - the outbound WS client bridging a user's Ollama to the chat (its `TunnelConnection` base also serves `workspace:*`) |
|
|
38
38
|
| `changelog` | Feature-keyed changelog fragments + feature docs |
|
|
39
39
|
|
|
40
40
|
## Documentation map
|
|
@@ -46,6 +46,7 @@ fragment in every repo the change touches.
|
|
|
46
46
|
| Features (current state) | [`docs/features/README.md`](docs/features/README.md) |
|
|
47
47
|
| Change history of a feature | `node scripts/changelog.mjs render --feature <slug>` |
|
|
48
48
|
| Local AI runner (slice; canonical in graphql-server) | [`docs/features/local-ai-runners.md`](docs/features/local-ai-runners.md) |
|
|
49
|
+
| Local workspaces - `workspace:*` (slice; canonical in graphql-server) | [`docs/features/local-workspaces.md`](docs/features/local-workspaces.md) |
|
|
49
50
|
| Claude Code config in this package | [`.claude/README.md`](.claude/README.md) |
|
|
50
51
|
|
|
51
52
|
## Key patterns
|
|
@@ -102,4 +103,4 @@ to debug" live.
|
|
|
102
103
|
gateway points every later command (and `runner:start`) at dev.
|
|
103
104
|
- Jest needs the `moduleNameMapper` in `jest.config.js` for the `~/` alias -
|
|
104
105
|
without it any spec importing command code fails to resolve.
|
|
105
|
-
- `runner:start`
|
|
106
|
+
- `runner:start` and `workspace:start` need Node >= 22 (global `WebSocket`).
|
package/README-CLI.md
CHANGED
|
@@ -63,6 +63,9 @@ Commands:
|
|
|
63
63
|
runner:list List your local AI runners and the models they serve
|
|
64
64
|
runner:models [options] Choose which of a runner's models appear in the chat model picker (no flags: list them)
|
|
65
65
|
runner:delete [options] Delete a local AI runner and revoke its token
|
|
66
|
+
workspace:start [options] Connect the current directory to the Lambforge chat as a local workspace: the AI agents can read/edit its files and run commands here (outbound connection only - works behind NAT)
|
|
67
|
+
workspace:list [options] List your local workspaces (online status, directory, processes)
|
|
68
|
+
workspace:delete [options] Delete a local workspace and revoke its token (default: the one registered for this directory)
|
|
66
69
|
help [command] display help for command
|
|
67
70
|
[36m
|
|
68
71
|
--- Subcommand: login ---[0m
|
|
@@ -132,6 +135,7 @@ Options:
|
|
|
132
135
|
--configs <configs...> List of kubernetes config names
|
|
133
136
|
--secrets <secrets...> List of kubernetes secret names
|
|
134
137
|
--envSecrets <envSecrets...> List of kubernetes secret names projected into process.env (graphql environment only)
|
|
138
|
+
--bodyLimit <bodyLimit> Largest request body the lambda accepts, e.g. 20mb (default 10mb, max 100mb)
|
|
135
139
|
--network <network> Can be private or public meaning exposed to the router or just for private available can be both also --network private --network public
|
|
136
140
|
--route <route> Lambda route in which will be accessible
|
|
137
141
|
--code <code> URL or local path for single file source code
|
|
@@ -174,6 +178,7 @@ Options:
|
|
|
174
178
|
--configs <configs...> List of kubernetes config names
|
|
175
179
|
--secrets <secrets...> List of kubernetes secret names
|
|
176
180
|
--envSecrets <envSecrets...> List of kubernetes secret names projected into process.env (graphql environment only)
|
|
181
|
+
--bodyLimit <bodyLimit> Largest request body the lambda accepts, e.g. 20mb (default 10mb, max 100mb)
|
|
177
182
|
--network <network> Can be private or public meaning exposed to the router or just for private available can be both also --network private --network public
|
|
178
183
|
--route <route> Lambda route in which will be accessible
|
|
179
184
|
--code <code> URL or local path for single file source code
|
|
@@ -289,6 +294,7 @@ Options:
|
|
|
289
294
|
--configs <configs...> List of kubernetes config names
|
|
290
295
|
--secrets <secrets...> List of kubernetes secret names
|
|
291
296
|
--envSecrets <envSecrets...> List of kubernetes secret names projected into process.env (graphql environment only)
|
|
297
|
+
--bodyLimit <bodyLimit> Largest request body the lambda accepts, e.g. 20mb (default 10mb, max 100mb)
|
|
292
298
|
--network <network> Can be private or public meaning exposed to the router or just for private available can be both also --network private --network public
|
|
293
299
|
--route <route> Lambda route in which will be accessible
|
|
294
300
|
--code <code> URL or local path for single file source code
|
|
@@ -924,3 +930,52 @@ Delete a local AI runner and revoke its token
|
|
|
924
930
|
Options:
|
|
925
931
|
--id <id> Runner id (default: the runner saved on this machine)
|
|
926
932
|
-h, --help display help for command
|
|
933
|
+
[36m
|
|
934
|
+
--- Subcommand: workspace:start ---[0m
|
|
935
|
+
Usage: gcli workspace:start [options]
|
|
936
|
+
|
|
937
|
+
Connect the current directory to the Lambforge chat as a local workspace: the AI agents can read/edit its files and run commands here (outbound connection only - works behind NAT)
|
|
938
|
+
|
|
939
|
+
Options:
|
|
940
|
+
-n, --name <name> Workspace name shown in the chat (default:
|
|
941
|
+
directory name)
|
|
942
|
+
-p, --project <projectId> Project the workspace belongs to (default: `gcli
|
|
943
|
+
project:use`)
|
|
944
|
+
-t, --token <token> Workspace token (instead of registering with `gcli
|
|
945
|
+
login`)
|
|
946
|
+
--allow <commands> Comma-separated commands that run without
|
|
947
|
+
approval; * matches anything (e.g. "npm test,npm
|
|
948
|
+
run *")
|
|
949
|
+
--no-default-allow Do not pre-allow read-only commands (git
|
|
950
|
+
status/diff/log, ls, ...)
|
|
951
|
+
--yolo Run EVERY command without asking (only in a
|
|
952
|
+
sandbox / a repo you can reset)
|
|
953
|
+
--no-terminal-approval Approve commands only in the chat, never prompt in
|
|
954
|
+
this terminal
|
|
955
|
+
--env <names> Comma-separated environment variables to pass to
|
|
956
|
+
commands (default: a minimal environment)
|
|
957
|
+
--inherit-env Pass your whole shell environment to commands
|
|
958
|
+
--allow-secrets Let the chat read/write .env files, keys and other
|
|
959
|
+
secrets files
|
|
960
|
+
--wsUrl <url> Lambforge subscriptions endpoint override
|
|
961
|
+
--reset Forget the workspace saved for this directory and
|
|
962
|
+
register a new one
|
|
963
|
+
-h, --help display help for command
|
|
964
|
+
[36m
|
|
965
|
+
--- Subcommand: workspace:list ---[0m
|
|
966
|
+
Usage: gcli workspace:list [options]
|
|
967
|
+
|
|
968
|
+
List your local workspaces (online status, directory, processes)
|
|
969
|
+
|
|
970
|
+
Options:
|
|
971
|
+
-p, --project <projectId> Only this project
|
|
972
|
+
-h, --help display help for command
|
|
973
|
+
[36m
|
|
974
|
+
--- Subcommand: workspace:delete ---[0m
|
|
975
|
+
Usage: gcli workspace:delete [options]
|
|
976
|
+
|
|
977
|
+
Delete a local workspace and revoke its token (default: the one registered for this directory)
|
|
978
|
+
|
|
979
|
+
Options:
|
|
980
|
+
--id <id> Workspace id (see `gcli workspace:list`)
|
|
981
|
+
-h, --help display help for command
|
package/README.md
CHANGED
|
@@ -920,6 +920,56 @@ Notes:
|
|
|
920
920
|
- Use Profile -> Local AI runners in the web app to see runners, choose
|
|
921
921
|
models with checkboxes, and create tokens.
|
|
922
922
|
|
|
923
|
+
## Local Workspaces (the chat works in your own project)
|
|
924
|
+
|
|
925
|
+
`gcli workspace:start` connects the **current directory** to the Lambforge
|
|
926
|
+
chat: the AI agents can read and edit its files, run commands (`npm install`,
|
|
927
|
+
`npm test`, `git diff`, `tsc`) and keep dev servers running (`npm start`) -
|
|
928
|
+
on your machine, in your real (git) project. Same **outbound** WebSocket as
|
|
929
|
+
the Ollama runner: works behind NAT, opens no port. Web workspaces keep
|
|
930
|
+
working as before; this is an additional, local kind.
|
|
931
|
+
|
|
932
|
+
```bash
|
|
933
|
+
gcli login
|
|
934
|
+
gcli project:use <projectId> # the workspace belongs to one project
|
|
935
|
+
cd ~/code/my-app
|
|
936
|
+
gcli workspace:start # --allow "npm test,npm run *" to skip approvals for those
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
Then, in a chat of that project (mode Auto): "run the tests in my local
|
|
940
|
+
project and fix what fails", "start the dev server and check it boots". The
|
|
941
|
+
agents only touch files under that directory.
|
|
942
|
+
|
|
943
|
+
**Commands need your approval** - in this terminal (`[y]es / [a]lways for
|
|
944
|
+
this session / [N]o`) or on the card in the chat; whichever you answer first
|
|
945
|
+
wins. Read-only commands (`git status/diff/log/show`, `ls`, `pwd`, ...) and
|
|
946
|
+
your `--allow` rules run unasked. Destructive ones (`git push`, `git reset
|
|
947
|
+
--hard`, `git clean`, `git commit`, `rm -r`, `sudo`, `curl ... | sh`, ...)
|
|
948
|
+
always ask. `--yolo` runs everything unasked - only for a sandbox or a repo
|
|
949
|
+
you can reset. Nobody answers in 5 minutes = denied.
|
|
950
|
+
|
|
951
|
+
| Command | Description |
|
|
952
|
+
|---|---|
|
|
953
|
+
| `gcli workspace:start` | Connect this directory. First run registers it (saved in `~/.graphql-server/workspaces.json`, per directory). Options: `--name`, `--project <id>` (default: `gcli project:use`), `--allow <list>` (comma-separated, `*` wildcard), `--no-default-allow`, `--yolo`, `--no-terminal-approval` (approve only in the chat), `--env A,B` (pass these variables to commands), `--inherit-env` (pass your whole environment), `--allow-secrets` (let the chat read `.env`/keys), `--token`, `--wsUrl`, `--reset` |
|
|
954
|
+
| `gcli workspace:list` | Your local workspaces: online, project, directory, running processes (`--project`) |
|
|
955
|
+
| `gcli workspace:delete` | Remove a workspace and revoke its token (`--id`, default: this directory's). Nothing on disk is deleted |
|
|
956
|
+
|
|
957
|
+
What the chat can and can't do:
|
|
958
|
+
|
|
959
|
+
- Files: only under the directory (symlinks resolved); `.git` is read-only;
|
|
960
|
+
`.env*`, private keys, `.npmrc`, ... are invisible unless `--allow-secrets`;
|
|
961
|
+
`node_modules` and `.gitignore`d files are not listed or searched.
|
|
962
|
+
- Commands run as you, in a minimal environment (PATH, HOME, nvm/pnpm
|
|
963
|
+
dirs - none of your shell's tokens) unless `--env` / `--inherit-env`.
|
|
964
|
+
- Background processes (`npm start`) live until the chat stops them or you
|
|
965
|
+
stop gcli - Ctrl+C stops every one of them.
|
|
966
|
+
- Every operation is printed here (`->` read, `✎` write, `$` command,
|
|
967
|
+
`▶` process), so you can watch what the agent does.
|
|
968
|
+
- Review with `git diff` and commit yourself - the agents are told not to
|
|
969
|
+
commit, push or reset.
|
|
970
|
+
|
|
971
|
+
Requires Node.js >= 22.
|
|
972
|
+
|
|
923
973
|
## Build System & Local Development
|
|
924
974
|
|
|
925
975
|
`gcli` includes a high-performance bundler powered by **esbuild**.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## 2026-10-23 - local-workspaces: gcli workspace:start/list/delete - the chat agents work in a local directory
|
|
2
|
+
|
|
3
|
+
**Feature:** local-workspaces · **Type:** feat
|
|
4
|
+
|
|
5
|
+
- New `workspace:start` (connect the current directory; registers it with the `gcli login` account in the `--project` / `project:use` project, token saved per directory in `~/.graphql-server/workspaces.json`), `workspace:list`, `workspace:delete`. Options: `--allow`, `--no-default-allow`, `--yolo`, `--no-terminal-approval`, `--env`, `--inherit-env`, `--allow-secrets`, `--token`, `--wsUrl`, `--reset`.
|
|
6
|
+
- The runner's WebSocket client is now a shared base class `TunnelConnection` (`src/commands/runner/tunnel.ts`); `RunnerConnection` keeps its behaviour (same tests) and `WorkspaceConnection` (`src/commands/workspace/connection.ts`) serves `POST /workspace/<op>`.
|
|
7
|
+
- `WorkspaceFiles`: everything confined to the root (symlinks resolved), `.git` read-only, secrets files hidden unless `--allow-secrets`, git-aware listing/search (`.gitignore` respected, `node_modules` skipped), edits by exact unique match with content-hash conflict checks.
|
|
8
|
+
- `CommandPolicy` + `ApprovalGate`: read-only defaults and `--allow` rules run unasked; everything else asks in the terminal AND on the chat card (first answer wins, 5-min expiry); destructive commands always ask unless `--yolo`; "always" remembers program + verb for the session.
|
|
9
|
+
- `runCommand` / `ProcessManager`: process groups (children die too), timeouts, output tails, background processes with a ring buffer and `waitFor`; all stopped when gcli exits. Commands get a minimal environment unless `--env` / `--inherit-env`.
|
|
10
|
+
- Backend half in graphql-server (registry, tunnel, agent tools, LOCAL worker), UI in gql-fission. Canonical doc: `graphql-server/docs/features/local-workspaces.md`.
|
|
11
|
+
- **Test:** `npx jest --coverage=false test/workspace test/runner` - files (escape, symlink, secrets, .gitignore, edit/hash), policy, approvals (web wins, early web answer, terminal deny, expiry), commands/processes (exit codes, timeout kill, waitFor/logs/stop), the connection end to end against a fake server.
|
|
12
|
+
- **Debug:** gcli's own log (`->`, `✎`, `$`, `▶`, `?`, `x`); "refused this workspace" = deleted -> `--reset`; wrong environment -> `~/.graphql-server/url`.
|
|
13
|
+
- **Verification:** full gcli suite 99/99 passing, `npx tsc --noEmit -p .` clean, `npx eslint src test` clean. Not yet run against a live backend.
|
package/docs/features/README.md
CHANGED
|
@@ -31,6 +31,7 @@ Outline (drop sections that don't apply):
|
|
|
31
31
|
| Feature | Doc | Notes |
|
|
32
32
|
|---|---|---|
|
|
33
33
|
| `local-ai-runners` | [local-ai-runners.md](local-ai-runners.md) | `runner:*` commands; canonical doc in graphql-server |
|
|
34
|
+
| `local-workspaces` | [local-workspaces.md](local-workspaces.md) | `workspace:*` commands - the chat agents edit files and run commands in a local directory; canonical doc in graphql-server |
|
|
34
35
|
| `lambforge-agent-skill` | [lambforge-agent-skill.md](lambforge-agent-skill.md) | The agent skill that teaches gcli + `mcp:start`; canonical doc in gql-fission |
|
|
35
36
|
| `dedicated-platform-cluster` | [dedicated-platform-cluster.md](dedicated-platform-cluster.md) | `cluster:status/health/scale/autoscale/...` commands; canonical doc in lambforge-agent |
|
|
36
37
|
| `ci-token-scopes` | [ci-token-scopes.md](ci-token-scopes.md) | Which permission each command needs; `mq-trigger:*` commands; canonical doc in graphql-server |
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Local workspaces (gcli slice)
|
|
2
|
+
|
|
3
|
+
**Status:** built 2026-10-23. **Feature slug:** `local-workspaces`.
|
|
4
|
+
Canonical doc (flow, backend, security model, debugging):
|
|
5
|
+
`graphql-server/docs/features/local-workspaces.md`.
|
|
6
|
+
|
|
7
|
+
`gcli workspace:start` serves the current directory to the chat agents over
|
|
8
|
+
the outbound WebSocket tunnel local AI runners use.
|
|
9
|
+
|
|
10
|
+
## Files
|
|
11
|
+
|
|
12
|
+
| Path | Responsibility |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `src/commands/runner/tunnel.ts` | `TunnelConnection` - the shared subscriptions-transport-ws client (connect/reconnect, heartbeat timer, request dispatch, ack/ping, `FrameSender`); `RunnerConnection` and `WorkspaceConnection` extend it |
|
|
15
|
+
| `src/commands/workspace/connection.ts` | `WorkspaceConnection`: `workspaceToken`, `localWorkspaceHeartbeat`, the ops allow-list (`WORKSPACE_OPS`, mirrors the backend), `dispatch(op)`, approval frames |
|
|
16
|
+
| `src/commands/workspace/files.ts` | `WorkspaceFiles`: root confinement (incl. symlinks), `.git` read-only, secrets files, git-aware list/search, read/write/edit with content hashes |
|
|
17
|
+
| `src/commands/workspace/policy.ts` | `CommandPolicy`: default allow rules, `--allow`, always-ask patterns, segment matching, "always" rules |
|
|
18
|
+
| `src/commands/workspace/approvals.ts` | `ApprovalGate` (policy -> web card + terminal prompt race, timeout, settle), `TerminalPrompt` |
|
|
19
|
+
| `src/commands/workspace/processes.ts` | `runCommand` (timeout, process-group kill, output tails), `ProcessManager` (background processes, ring buffer, waitFor), `buildEnv` |
|
|
20
|
+
| `src/commands/workspace/{start,list,delete,config,index}.ts` | the commands; `~/.graphql-server/workspaces.json` (0600, keyed by directory) |
|
|
21
|
+
|
|
22
|
+
## Protocol (what the backend sends)
|
|
23
|
+
|
|
24
|
+
`POST /workspace/<op>` with a JSON body, answered `head(status) + chunks + end`
|
|
25
|
+
with a JSON body; errors are `{ error }` with 400/403/404/409/500. Ops:
|
|
26
|
+
`info list read write edit delete search exec process/start process/list
|
|
27
|
+
process/logs process/stop`. Anything else is refused before the ack.
|
|
28
|
+
`type: 'approval'` frames carry `{ decision: allow|always|deny }` for an
|
|
29
|
+
approval id this client created with `requestLocalWorkspaceApproval`.
|
|
30
|
+
|
|
31
|
+
## How to test
|
|
32
|
+
|
|
33
|
+
`npx jest --coverage=false test/workspace` - real files and processes in a
|
|
34
|
+
temp dir; the connection runs against a fake subscriptions server
|
|
35
|
+
(edit over the tunnel, refused paths, web approval -> command runs, denial).
|
|
36
|
+
|
|
37
|
+
## How to debug
|
|
38
|
+
|
|
39
|
+
gcli prints every op: `->` read/list/search, `✎` write/edit/delete, `$`
|
|
40
|
+
command (+ exit code and duration), `▶` / `■` processes, `?` approval
|
|
41
|
+
needed, `x` failed/cancelled. "refused this workspace" = deleted on the web
|
|
42
|
+
(`--reset`). Pointing at the wrong environment: `~/.graphql-server/url`.
|