@orchestraworks/cli 0.5.8 → 0.6.0
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/README.md +1 -1
- package/dist/cli/api/approval.js +3 -3
- package/dist/cli/commands/outpost/local.js +3 -3
- package/dist/cli/commands/outpost/machine-names.d.ts +17 -0
- package/dist/cli/commands/outpost/machine-names.js +22 -0
- package/dist/cli/commands/outpost/up.js +7 -5
- package/dist/cli/commands/worker/enroll.d.ts +4 -3
- package/dist/cli/commands/worker/enroll.js +4 -3
- package/dist/cli/core/auth.d.ts +11 -1
- package/dist/cli/core/auth.js +21 -4
- package/dist/cli/core/session-body.d.ts +1 -1
- package/dist/cli/core/session-body.js +1 -1
- package/dist/cli/firstRun.js +2 -2
- package/dist/cli/main.js +4 -3
- package/dist/constructs/environment.d.ts +24 -11
- package/dist/constructs/environment.js +76 -12
- package/dist/constructs/secret.d.ts +17 -7
- package/dist/constructs/secret.js +24 -16
- package/dist/core/labels.d.ts +32 -6
- package/dist/core/labels.js +70 -8
- package/dist/engine/diff.js +38 -11
- package/dist/engine/gateway-key-reconcile.d.ts +1 -2
- package/dist/engine/gateway-key-reconcile.js +19 -15
- package/dist/engine/pipeline-reconcile.js +2 -2
- package/dist/engine/pipeline-workflow-reconcile.js +15 -9
- package/dist/engine/reconcile.js +48 -15
- package/dist/engine/refusals.d.ts +0 -5
- package/dist/engine/refusals.js +1 -5
- package/dist/engine/secrets-apply.d.ts +10 -14
- package/dist/engine/secrets-apply.js +12 -17
- package/dist/engine/workflow-reconcile.d.ts +1 -1
- package/dist/generated/cli-commands.json +170 -34
- package/dist/generated/providers.d.ts +40 -0
- package/dist/generated/providers.js +30 -0
- package/docs/REFERENCE.md +9 -7
- package/node_modules/@orchestraworks/client/dist/esm/models/ApiErrorDetail.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/esm/models/CreateEnvironmentRequest.d.ts +7 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/CreateEnvironmentRequest.js +3 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/CreateSecretRequest.d.ts +4 -3
- package/node_modules/@orchestraworks/client/dist/esm/models/Environment.d.ts +7 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/Environment.js +3 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentCredential.d.ts +38 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentCredential.js +45 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentProvisionSpec.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentProvisionSpecRequest.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentSnapshot.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/esm/models/InjectionRecipe.d.ts +6 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/InjectionRecipe.js +2 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/OAuthTokenRequest.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/esm/models/Secret.d.ts +1 -7
- package/node_modules/@orchestraworks/client/dist/esm/models/Secret.js +0 -4
- package/node_modules/@orchestraworks/client/dist/esm/models/UpdateEnvironmentRequest.d.ts +7 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/UpdateEnvironmentRequest.js +3 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/UpdateSecretRequest.d.ts +4 -3
- package/node_modules/@orchestraworks/client/dist/esm/models/index.d.ts +1 -0
- package/node_modules/@orchestraworks/client/dist/esm/models/index.js +1 -0
- package/node_modules/@orchestraworks/client/dist/models/ApiErrorDetail.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/models/CreateEnvironmentRequest.d.ts +7 -0
- package/node_modules/@orchestraworks/client/dist/models/CreateEnvironmentRequest.js +3 -0
- package/node_modules/@orchestraworks/client/dist/models/CreateSecretRequest.d.ts +4 -3
- package/node_modules/@orchestraworks/client/dist/models/Environment.d.ts +7 -0
- package/node_modules/@orchestraworks/client/dist/models/Environment.js +3 -0
- package/node_modules/@orchestraworks/client/dist/models/EnvironmentCredential.d.ts +38 -0
- package/node_modules/@orchestraworks/client/dist/models/EnvironmentCredential.js +52 -0
- package/node_modules/@orchestraworks/client/dist/models/EnvironmentProvisionSpec.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/models/EnvironmentProvisionSpecRequest.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/models/EnvironmentSnapshot.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/models/InjectionRecipe.d.ts +6 -0
- package/node_modules/@orchestraworks/client/dist/models/InjectionRecipe.js +2 -0
- package/node_modules/@orchestraworks/client/dist/models/OAuthTokenRequest.d.ts +1 -1
- package/node_modules/@orchestraworks/client/dist/models/Secret.d.ts +1 -7
- package/node_modules/@orchestraworks/client/dist/models/Secret.js +0 -4
- package/node_modules/@orchestraworks/client/dist/models/UpdateEnvironmentRequest.d.ts +7 -0
- package/node_modules/@orchestraworks/client/dist/models/UpdateEnvironmentRequest.js +3 -0
- package/node_modules/@orchestraworks/client/dist/models/UpdateSecretRequest.d.ts +4 -3
- package/node_modules/@orchestraworks/client/dist/models/index.d.ts +1 -0
- package/node_modules/@orchestraworks/client/dist/models/index.js +1 -0
- package/node_modules/@orchestraworks/client/package.json +1 -1
- package/package.json +4 -4
- package/skills/orchestra/SKILL.md +30 -14
- package/skills/orchestra-fdk/references/authoring.md +26 -20
- package/skills/orchestra-fdk/references/environment.md +49 -12
- package/skills/orchestra-fdk/references/integrations/code-repo.md +26 -5
- package/skills/orchestra-fdk/references/phases/1-discover.md +1 -1
- package/skills/orchestra-fdk/references/phases/2-plan.md +1 -1
- package/skills/orchestra-fdk/references/troubleshooting.md +3 -2
- package/templates/bug-fix/main.ts +7 -4
- package/templates/engineering-team/lib/environment.ts +6 -3
- package/templates/engineering-team/package.json +1 -1
- package/templates/todo-factory/main.ts +7 -4
- package/templates/todo-factory/package.json +1 -1
|
@@ -265,8 +265,8 @@ unless you set one, so it wins over the open default and over the usual allow ru
|
|
|
265
265
|
**Live effect.** The edge reads the environment's rules on each request. So an apply that changes the rules
|
|
266
266
|
reaches every machine at once, machines already running included, from their next request.
|
|
267
267
|
|
|
268
|
-
**How it relates.** On a request a rule allows, the edge adds
|
|
269
|
-
host,
|
|
268
|
+
**How it relates.** On a request a rule allows, the edge adds a secret or connection whose `hosts` cover the
|
|
269
|
+
host, only on `https`, and only when the request names it or an environment default covers it (section 6). Calls to the Orchestra platform itself skip these rules: the agent's
|
|
270
270
|
role decides them (section 1). So a machine blocked from the code host still downloads a file through
|
|
271
271
|
`orchestra files get-content`. The worker points each harness's tools at the edge through proxy variables, so
|
|
272
272
|
a service the agent runs in a container needs its name in `NO_PROXY` in the environment's settings.
|
|
@@ -291,10 +291,19 @@ resolved at the edge on an allow); AGENTS.md "Egress And Credentials". Kit: cli/
|
|
|
291
291
|
**What it's for.** It lets agents use a key without seeing it. No agent, no person and no apply output ever shows
|
|
292
292
|
the value.
|
|
293
293
|
|
|
294
|
-
**What it is.** A *secret* is a stored value that can be written and never read back. It declares
|
|
295
|
-
|
|
296
|
-
to one of those hosts. This is *credential injection at the network edge*.
|
|
297
|
-
|
|
294
|
+
**What it is.** A *secret* is a stored value that can be written and never read back. It declares an
|
|
295
|
+
`injection` recipe, the headers it sets and the `hosts` it sets them on, and the network edge adds it to an
|
|
296
|
+
allowed `https` request to one of those hosts. A secret with no recipe is stored only, and the edge never adds it. This is *credential injection at the network edge*. The edge adds it only in two cases:
|
|
297
|
+
|
|
298
|
+
- The request names it with a reference, `orchestra:secret/<id>` or `orchestra:connection/<id>`, in place
|
|
299
|
+
of its key. A `Secret` or `Connection` set as an environment `env` value is written as that reference:
|
|
300
|
+
`ANTHROPIC_API_KEY` for claude, `GH_TOKEN` for `gh`, `GITLAB_TOKEN` for `glab`, `ORCHESTRA_GIT_CREDENTIAL`
|
|
301
|
+
for git. `routeModelsViaGateway({ key })` names the gateway key this way for claude and codex. The
|
|
302
|
+
`linear`, `slack` and `teams` commands take `--connection-id <id>`.
|
|
303
|
+
- The environment's `credentials` list covers the host, for a request that names nothing. The first
|
|
304
|
+
covering entry serves; with `replace: false`, a request carrying its own value keeps it.
|
|
305
|
+
|
|
306
|
+
A request that names nothing and that no default covers leaves as it was sent.
|
|
298
307
|
|
|
299
308
|
**How it relates.** A secret is added only where a network rule lets the request leave (section 5). Never put
|
|
300
309
|
a key in an environment's settings or in instructions: the machine's processes read every setting.
|
|
@@ -325,8 +334,9 @@ network rules carry the option (sections 2 and 5).
|
|
|
325
334
|
**A Claude or Codex subscription.**
|
|
326
335
|
|
|
327
336
|
- *Claude.* `claude setup-token` prints a long-lived token. It is stored as a secret, and the network edge
|
|
328
|
-
adds it to each call to `api.anthropic.com
|
|
329
|
-
|
|
337
|
+
adds it to each call to `api.anthropic.com` that names it. The environment's `env` names the secret in
|
|
338
|
+
`CLAUDE_CODE_OAUTH_TOKEN` by reference, `orchestra:secret/<id>`, beside an empty API key, so no agent
|
|
339
|
+
and no person sees the real one. Never put the token in an environment's settings: they
|
|
330
340
|
are plain text.
|
|
331
341
|
- *Codex.* A personal access token, made by a ChatGPT Business or Enterprise member, or a separate ChatGPT
|
|
332
342
|
sign-in kept on a `/home` volume that every machine using it shares. A Codex sign-in stays off the model
|
|
@@ -337,7 +347,8 @@ network rules carry the option (sections 2 and 5).
|
|
|
337
347
|
|
|
338
348
|
**An Orchestra account, through the model gateway.** The model gateway is a hosted service at
|
|
339
349
|
`models.orchestraworks.ai`. One endpoint reaches OpenAI, Anthropic and open models. A *gateway key* is a key
|
|
340
|
-
to it, stored as a secret.
|
|
350
|
+
to it, stored as a secret. `routeModelsViaGateway({ key })` names it to each harness, and the network edge
|
|
351
|
+
adds it to each model call, so no agent and no person sees it.
|
|
341
352
|
Without it, an agent's first message fails with "Invalid API key".
|
|
342
353
|
|
|
343
354
|
- *Routing.* `orchestra model-configs` sets which providers serve a model, and in which order, so a call
|
|
@@ -348,10 +359,11 @@ Without it, an agent's first message fails with "Invalid API key".
|
|
|
348
359
|
- *A cap.* Each gateway key can carry a monthly cap, set in the console at **Settings › Gateway keys**.
|
|
349
360
|
|
|
350
361
|
**Their own provider API key.** The team's Anthropic or OpenAI API key is a secret that the network edge adds
|
|
351
|
-
to calls straight to the provider, with no gateway in between.
|
|
362
|
+
to calls straight to the provider, with no gateway in between. The environment names it in `ANTHROPIC_API_KEY`
|
|
363
|
+
or `ORCHESTRA_WORKER_OPENAI_API_KEY`, or lists it in `credentials`.
|
|
352
364
|
|
|
353
365
|
**How it relates.** Each option reaches the model through an ordinary allow rule on the model's host, and a
|
|
354
|
-
secret whose `hosts` name that host (sections 5 and 6). An agent on the model gateway turns it on in its model settings (section 2). An agent on a
|
|
366
|
+
secret whose recipe's `hosts` name that host (sections 5 and 6). An agent on the model gateway turns it on in its model settings (section 2). An agent on a
|
|
355
367
|
subscription or its own key turns it off. For plan questions, send the person to **Settings › Plan & usage**
|
|
356
368
|
in the console.
|
|
357
369
|
|
|
@@ -647,7 +659,7 @@ local/launch/modelkey.go (model key carriers per harness, the gateway address va
|
|
|
647
659
|
worker/facts/families.json (how each harness takes the gateway address); on develop at f62fad1481,
|
|
648
660
|
agents/runtime/assembly/common/placeholder-env (the images' placeholder ANTHROPIC_API_KEY),
|
|
649
661
|
LauncherProperties.kt (the gallery images at Versions/latest), launcher/config/config.go (Dsv5 default);
|
|
650
|
-
worker/facts/families.json (the ORCHESTRA_WORKER_ names), worker/service/
|
|
662
|
+
worker/facts/families.json (the ORCHESTRA_WORKER_ names), worker/service/service.go (OrchestraWorker, and the previous SfdkWorker),
|
|
651
663
|
infra/packer/azure-windows-runtime.pkr.hcl (the startup task), launcher/cloud/cloud.go (the VM tags);
|
|
652
664
|
the proven list is the test run of 2026-10-05. -->
|
|
653
665
|
|
|
@@ -677,8 +689,8 @@ the proven list is the test run of 2026-10-05. -->
|
|
|
677
689
|
1. The environment's `env` (`provisionSpec.env`). A declared value replaces our image's placeholder,
|
|
678
690
|
never a real value. The platform stores `env` values in plain text, an environment read returns
|
|
679
691
|
them, and the machine's startup data carries them.
|
|
680
|
-
2. Baked into the team's image at machine scope: on Windows the `
|
|
681
|
-
registry value, on Linux a drop-in on `
|
|
692
|
+
2. Baked into the team's image at machine scope: on Windows the `OrchestraWorker` service's
|
|
693
|
+
`Environment` registry value, on Linux a drop-in on `orchestra-worker.service`.
|
|
682
694
|
3. The `onStartAgent` hook writes the harness's own key file: `.claude/.credentials.json` for a Claude
|
|
683
695
|
sign-in, `.codex/auth.json` for `codex`, `.pi/agent/auth.json` for `pi`. A raw Claude API key has
|
|
684
696
|
no file.
|
|
@@ -686,6 +698,10 @@ the proven list is the test run of 2026-10-05. -->
|
|
|
686
698
|
`ORCHESTRA_WORKER_CLAUDE_OAUTH_TOKEN` for a Claude subscription token; `ORCHESTRA_WORKER_OPENAI_API_KEY`
|
|
687
699
|
or `ORCHESTRA_WORKER_CODEX_PERSONAL_ACCESS_TOKEN` for `codex`. With a gateway key, keep the agent's
|
|
688
700
|
model gateway on. The platform sets the gateway address.
|
|
701
|
+
- An install replaces the service an earlier release registered as `SfdkWorker` on Windows,
|
|
702
|
+
`sfdk-worker.service` on Linux or `ai.livingcomputers.sfdk.worker` on macOS. The new ones are
|
|
703
|
+
`OrchestraWorker`, `orchestra-worker.service` and `ai.livingcomputers.orchestra.worker`. Change your own
|
|
704
|
+
scripts that name the old ones.
|
|
689
705
|
- Never restart the worker service to load a key. A restart after the worker registered stops the
|
|
690
706
|
machine's work, and the platform parks the VM.
|
|
691
707
|
- The details: the orchestra-fdk skill's `references/environment.md`, "Tokens on a compute provider's
|
|
@@ -68,8 +68,10 @@ const factory = new Factory({ name: "acme-support" });
|
|
|
68
68
|
new Secret(factory, "openai-api-key", {
|
|
69
69
|
name: "OpenAI API key",
|
|
70
70
|
value: SecretValue.fromEnv("OPENAI_API_KEY_VALUE"), // read from env at apply — never in the plan
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
injection: {
|
|
72
|
+
headers: [{ name: "Authorization", value: "Bearer ${secret}" }], // how the edge presents it
|
|
73
|
+
hosts: ["api.openai.com"], // where
|
|
74
|
+
},
|
|
73
75
|
});
|
|
74
76
|
|
|
75
77
|
const runtime = new Environment(factory, "runtime", {
|
|
@@ -554,26 +556,29 @@ token the platform mints. The **Claude sign-in recipe** is the one to copy when
|
|
|
554
556
|
on a person's Claude subscription token rather than an API key:
|
|
555
557
|
|
|
556
558
|
```ts
|
|
557
|
-
new Secret(factory, "claude-sign-in", {
|
|
559
|
+
const claudeSignIn = new Secret(factory, "claude-sign-in", {
|
|
558
560
|
value: SecretValue.fromEnv("ORCHESTRA_FDK_CLAUDE_CODE_OAUTH_TOKEN"),
|
|
559
|
-
hosts: ["api.anthropic.com"],
|
|
560
561
|
injection: {
|
|
561
562
|
headers: [{ name: "authorization", value: "Bearer ${secret}" }],
|
|
562
563
|
removeHeaders: ["x-api-key"], // stripped before injection
|
|
564
|
+
hosts: ["api.anthropic.com"],
|
|
563
565
|
},
|
|
564
566
|
});
|
|
565
567
|
|
|
566
568
|
// …in the Environment's props:
|
|
567
|
-
env: { CLAUDE_CODE_OAUTH_TOKEN:
|
|
569
|
+
env: { CLAUDE_CODE_OAUTH_TOKEN: claudeSignIn, ANTHROPIC_API_KEY: "" },
|
|
568
570
|
egress: EgressPolicy.allowAll()
|
|
569
571
|
.allow("claude-sign-in", { hosts: ["api.anthropic.com"], priority: 300 }),
|
|
570
572
|
```
|
|
571
573
|
|
|
572
574
|
It injects `authorization` and nothing else. **Never inject an `anthropic-beta` header**: an injected
|
|
573
|
-
one replaces the harness's own list of beta flags, and every call then answers 400.
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
575
|
+
one replaces the harness's own list of beta flags, and every call then answers 400. A Secret as an `env`
|
|
576
|
+
value is written as its reference, `orchestra:secret/<id>`, with the id resolved at apply. In
|
|
577
|
+
`CLAUDE_CODE_OAUTH_TOKEN` it makes the harness sign in the subscription way and send its own list, and the
|
|
578
|
+
edge reads the reference where the token goes and swaps the real token in. The edge adds a credential only
|
|
579
|
+
to a request that names one, or that an environment default covers (`references/environment.md`, "Naming a
|
|
580
|
+
credential"). The empty `ANTHROPIC_API_KEY` covers the runtime image's placeholder key, which the harness
|
|
581
|
+
would otherwise send instead.
|
|
577
582
|
|
|
578
583
|
- Sources: `SecretValue.fromEnv(NAME)`, `SecretValue.fromCommandLine(label?)` and
|
|
579
584
|
`SecretValue.fromGatewayKey(key)`, plus the derived recipes `basicAuth`, `registryCredential` and
|
|
@@ -613,7 +618,7 @@ which the harness would otherwise send instead.
|
|
|
613
618
|
`GatewayKey` you declare in the same factory — `new GatewayKey(factory, "model-gateway-key", props)`,
|
|
614
619
|
whose only props are `slug`, `displayName`, `monthlyCreditCap` and `labels` — so apply mints the
|
|
615
620
|
key and writes the token it shows *exactly once* straight into this Secret, which the edge then
|
|
616
|
-
adds on its `hosts` like any other. It is legal only as the whole top-level value of exactly one Secret:
|
|
621
|
+
adds on its recipe's `hosts` like any other. It is legal only as the whole top-level value of exactly one Secret:
|
|
617
622
|
nesting it inside another recipe, or a key with zero or two Secrets, is a synth error, and there
|
|
618
623
|
is no importer because an existing key's token is unrecoverable. Rotation is new-slug-only.
|
|
619
624
|
- A **`ModelProviderKey`** is the other credential you can declare: your organization's own key for
|
|
@@ -648,11 +653,12 @@ which the harness would otherwise send instead.
|
|
|
648
653
|
suppresses it and none can supply it either, so a headless apply of a `fromCommandLine`
|
|
649
654
|
declaration refuses at *every* level and tells you to run from a terminal or change the source —
|
|
650
655
|
it never recommends a policy lever that could not clear it.
|
|
651
|
-
- Presenting a secret on the network: the `Secret` itself declares `
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
that passes `credential` or `credentials` is a synth error
|
|
655
|
-
nowhere.
|
|
656
|
+
- Presenting a secret on the network: the `Secret` itself declares an `injection` recipe — how: the
|
|
657
|
+
headers it sets, reading `${secret}`, or `${secret.<field>}` for a `SecretValue.fields` value; and
|
|
658
|
+
where: its `hosts`, each an exact host, `*.suffix`, or `*`. Egress rules carry no credential, so a rule
|
|
659
|
+
that passes `credential` or `credentials` is a synth error. A Secret with no `injection` is stored
|
|
660
|
+
only, and one whose recipe has no `hosts` is presented nowhere. The top-level `hosts` prop is
|
|
661
|
+
deprecated: it is moved into the recipe for one release.
|
|
656
662
|
|
|
657
663
|
## How the agents reach a model
|
|
658
664
|
|
|
@@ -668,9 +674,9 @@ on their own computer; run from your shell, its browser step hangs. Never read o
|
|
|
668
674
|
At apply, the shell exports it from that file into the variable the Secret reads. Declare the Claude
|
|
669
675
|
sign-in recipe (§ Secrets are recipes, never values): a `Secret`, an egress rule that allows
|
|
670
676
|
`api.anthropic.com` and sets `Authorization: Bearer ${secret}` with `x-api-key` removed, and, in the
|
|
671
|
-
environment's `env`, `CLAUDE_CODE_OAUTH_TOKEN
|
|
672
|
-
|
|
673
|
-
|
|
677
|
+
environment's `env`, `CLAUDE_CODE_OAUTH_TOKEN` set to that Secret and an empty `ANTHROPIC_API_KEY`.
|
|
678
|
+
Turn `enableModelGateway` off for the agent. The machine holds only the Secret's reference; the edge
|
|
679
|
+
adds the token. Copying the renewing credentials file instead is the fallback,
|
|
674
680
|
and follows the ChatGPT sign-in steps below.
|
|
675
681
|
- **Codex: a personal access token, where the workspace allows one.** A ChatGPT Business or Enterprise
|
|
676
682
|
member creates it at chatgpt.com/admin/access-tokens, once the workspace owner allows access tokens,
|
|
@@ -762,7 +768,7 @@ An environment's `egress` is a policy you build up from one of **two starting po
|
|
|
762
768
|
- **`EgressPolicy.allowAll()`** allows every host no rule refuses.
|
|
763
769
|
|
|
764
770
|
Rules fold onto either one the same way: a rule only opens or closes the network, a credential still goes
|
|
765
|
-
only to the hosts its own `hosts` cover, and a deny still refuses.
|
|
771
|
+
only to the hosts its own recipe's `hosts` cover, and a deny still refuses.
|
|
766
772
|
|
|
767
773
|
Most of what an agent needs to reach arrives ready-made as a **bundle** — a list of rules folded onto
|
|
768
774
|
the policy by one `applyPreset(...)` call — so **reach for the bundles first** and hand-write allow/deny
|
|
@@ -1079,7 +1085,7 @@ orchestra gateway-keys create --slug ci-smoke --output-file key.json
|
|
|
1079
1085
|
export, a CI secret) suppresses the only prompt you would have seen, the command says so in one
|
|
1080
1086
|
line on **stderr**, naming the lever — stdout stays the response document.
|
|
1081
1087
|
- **Writing to a factory-managed resource warns.** `apply` stamps what it manages with an
|
|
1082
|
-
`
|
|
1088
|
+
`orchestra.managedBy` labels key naming the owning factory, and a write to a stamped resource says so:
|
|
1083
1089
|
the next `apply` reverts it. It is a warning, not a refusal — the fallback has to stay usable in an
|
|
1084
1090
|
emergency — and it is the CLI telling you this belongs in `main.ts`.
|
|
1085
1091
|
|
|
@@ -219,7 +219,7 @@ the `agent` user. The worker writes a sign-in file only when none is there, so t
|
|
|
219
219
|
**Before any cloud apply,** search `<factory>` for that marker, and for any credential in the setup script,
|
|
220
220
|
in the environment's settings (`ENV`, which becomes `provisionSpec.env`), in the agents' instructions, and
|
|
221
221
|
in every work-item description you write. Remove each one. Use a Secret
|
|
222
|
-
instead: a gateway key, or a Secret whose `hosts` name where the edge adds it. Tell the developer why: the setup script and
|
|
222
|
+
instead: a gateway key, or a Secret whose recipe's `hosts` name where the edge adds it. Tell the developer why: the setup script and
|
|
223
223
|
the settings are stored as plain text and shown on every read.
|
|
224
224
|
|
|
225
225
|
### Start the local environment
|
|
@@ -318,7 +318,7 @@ The local key is never carried over. It stays on the developer's computer. In th
|
|
|
318
318
|
Orchestra Secret, and our network edge adds it to each model call, so no machine and no person holds it:
|
|
319
319
|
|
|
320
320
|
- For the gateway, `apply` creates the gateway key and its secret. The starter already declares them.
|
|
321
|
-
- For a subscription token or a provider key, declare a Secret whose `hosts` name the provider's host, and a
|
|
321
|
+
- For a subscription token or a provider key, declare a Secret whose recipe's `hosts` name the provider's host, and a
|
|
322
322
|
rule that opens that host, as phase 2 of the orchestra-fdk skill, "The model route", says (orchestra skill,
|
|
323
323
|
sections 6 and 7).
|
|
324
324
|
|
|
@@ -378,12 +378,15 @@ into `orchestra-factory-notes.md`.
|
|
|
378
378
|
captured Windows VM does not register. The worker's own command, `worker install-machine --cloud
|
|
379
379
|
azure`, adds the three parts the image must contain. The section "A team's own image" below gives the
|
|
380
380
|
steps:
|
|
381
|
-
- The worker's release package, at `C:\ProgramData\
|
|
382
|
-
- The worker's Windows service, `
|
|
381
|
+
- The worker's release package, at `C:\ProgramData\orchestra\worker\package`.
|
|
382
|
+
- The worker's Windows service, `OrchestraWorker`. It runs `worker.exe run --root C:\ProgramData\orchestra\worker
|
|
383
383
|
--service` as LocalSystem. It is demand-start (`Manual`), not automatic. On a failure it restarts
|
|
384
384
|
three times, 2 seconds apart, and a clean stop with a non-zero exit code counts as a failure.
|
|
385
385
|
- The startup task `orchestra-azure-userdata`. It runs `worker.exe boot-files --cloud azure` as SYSTEM
|
|
386
|
-
at each boot. It writes the files that the VM's startup data carries, then starts `
|
|
386
|
+
at each boot. It writes the files that the VM's startup data carries, then starts `OrchestraWorker`.
|
|
387
|
+
- An install replaces the service an earlier release registered as `SfdkWorker`. Change your own
|
|
388
|
+
scripts that name it. On an image an earlier release built, the startup task still starts
|
|
389
|
+
`SfdkWorker`.
|
|
387
390
|
|
|
388
391
|
Our image runs the same command, from `install-vm-runtime.ps1` and the image template
|
|
389
392
|
`infra/packer/azure-windows-runtime.pkr.hcl`. Under a Launcher, the image needs no WinDivert, no
|
|
@@ -463,7 +466,7 @@ agent can run the team's own `docker compose up` unchanged.
|
|
|
463
466
|
*machine endpoint*, a web address that only signed-in members of the organization can open.
|
|
464
467
|
4. Write the app's own settings files (`.env.local`) into the Set up item's description, because the code
|
|
465
468
|
is not on the machine when the setup script runs. Put only local or test values there. A real secret
|
|
466
|
-
becomes a Secret whose `hosts` name where the edge adds it.
|
|
469
|
+
becomes a Secret whose recipe's `hosts` name where the edge adds it.
|
|
467
470
|
5. Choose the size by what CI uses for the same work. The template's sizes are fixed per agent: lg is
|
|
468
471
|
32 CPU, md 16, sm 2.
|
|
469
472
|
6. When the project runs containers, keep the disk at `/var/lib/docker`. The starter declares it. It keeps
|
|
@@ -650,8 +653,8 @@ failure stops the run and names the step. The team can run it again.
|
|
|
650
653
|
- It refuses a VM that has already run as a machine, and a running worker service. Build on a new VM.
|
|
651
654
|
- On Windows it creates the worker's folders for SYSTEM and the administrators only, so other accounts on
|
|
652
655
|
the VM cannot add programs or read the boot files.
|
|
653
|
-
- It copies the worker's package to the machine-wide root, `C:\ProgramData\
|
|
654
|
-
`/var/lib/
|
|
656
|
+
- It copies the worker's package to the machine-wide root, `C:\ProgramData\orchestra\worker` or
|
|
657
|
+
`/var/lib/orchestra/worker`.
|
|
655
658
|
- On Linux it creates the `agent` account, uid 1000, home `/home`.
|
|
656
659
|
- It defines the worker's service, and the startup task that writes the boot files and starts the worker.
|
|
657
660
|
- `--harness claude` preinstalls a harness. Without it, the harness installs itself on its first turn.
|
|
@@ -691,7 +694,7 @@ a Launcher machine, and the team installs their own tools.
|
|
|
691
694
|
After each restart, run the check. Stop when it shows `"restartPending":false`:
|
|
692
695
|
|
|
693
696
|
```powershell
|
|
694
|
-
& "$env:ProgramData\
|
|
697
|
+
& "$env:ProgramData\orchestra\worker\package\worker.exe" check --profile launcher --output json
|
|
695
698
|
```
|
|
696
699
|
|
|
697
700
|
6. Generalize with Sysprep. It takes about 4 minutes, and the VM then stops:
|
|
@@ -815,8 +818,8 @@ Tokens on a compute provider's machines:
|
|
|
815
818
|
carries them. Tell the developer this. If it is not acceptable, use route 2 or 3, or a Secret added
|
|
816
819
|
at our network edge where one stands in front.
|
|
817
820
|
2. **Baked into the team's image**, at machine scope, before Sysprep or generalizing. On Windows, that
|
|
818
|
-
is the `
|
|
819
|
-
`
|
|
821
|
+
is the `OrchestraWorker` service's `Environment` registry value. On Linux, it is a drop-in on
|
|
822
|
+
`orchestra-worker.service`.
|
|
820
823
|
3. **The `onStartAgent` hook** writes the harness's own key file in the agent's home:
|
|
821
824
|
`.claude/.credentials.json` for a Claude sign-in, `.codex/auth.json` for `codex`, or
|
|
822
825
|
`.pi/agent/auth.json` for `pi`. The worker keeps a real value it finds there. The hook can read the
|
|
@@ -870,6 +873,40 @@ that need no private service.
|
|
|
870
873
|
**Check:** the typecheck and the synth pass, and the plan is as in item 3. Go back to the orchestra-fdk
|
|
871
874
|
skill's phase 4 for the apply.
|
|
872
875
|
|
|
876
|
+
### Naming a credential
|
|
877
|
+
|
|
878
|
+
The network edge adds a credential to a request in two cases only. A request that names none and that no
|
|
879
|
+
environment default covers leaves with the headers it was sent, a placeholder included.
|
|
880
|
+
|
|
881
|
+
- **The request names it.** Set the variable a tool reads its token from to a `Secret` or a `Connection`
|
|
882
|
+
in the environment's `env`. Apply writes `orchestra:secret/<id>` or `orchestra:connection/<id>`, and the
|
|
883
|
+
tool sends that reference where its token goes. The edge reads it and adds the credential, if the
|
|
884
|
+
agent may use it.
|
|
885
|
+
|
|
886
|
+
| Tool | Variable |
|
|
887
|
+
|---|---|
|
|
888
|
+
| claude, pi | `ANTHROPIC_API_KEY`, or `CLAUDE_CODE_OAUTH_TOKEN` for a subscription token |
|
|
889
|
+
| codex | `ORCHESTRA_WORKER_OPENAI_API_KEY`, which the worker writes into codex's own auth file |
|
|
890
|
+
| `gh` | `GH_TOKEN` |
|
|
891
|
+
| `glab` | `GITLAB_TOKEN`, which `glab` sends as `Private-Token` |
|
|
892
|
+
| git | `ORCHESTRA_GIT_CREDENTIAL`: the image's git credential helper answers it when the server asks |
|
|
893
|
+
| `linear`, `slack`, `teams` | `--connection-id <id>` on the command; it reads no connection |
|
|
894
|
+
|
|
895
|
+
`routeModelsViaGateway({ key })` sets `ANTHROPIC_API_KEY` and `ORCHESTRA_WORKER_OPENAI_API_KEY` to the
|
|
896
|
+
key Secret itself, so declare neither beside it.
|
|
897
|
+
|
|
898
|
+
- **An environment credential covers the host.** `credentials` lists Secrets and Connections. A
|
|
899
|
+
request from the environment's machines that names none gets the first whose hosts cover its host,
|
|
900
|
+
whatever the agent. `replace` is true unless set: with `replace: false`, a request that carries its own
|
|
901
|
+
value in the credential's header keeps it and gets nothing. Adding an entry needs your own use of that
|
|
902
|
+
credential.
|
|
903
|
+
|
|
904
|
+
```ts
|
|
905
|
+
credentials: [{ credential: githubConnection }, { credential: modelKey, replace: false }],
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
With no `credentials` declared, apply leaves the live list alone; `[]` clears it.
|
|
909
|
+
|
|
873
910
|
## 7. The cloud
|
|
874
911
|
|
|
875
912
|
The cloud is the fallback when no local mode can run. It is also the confirmation after the local proof:
|
|
@@ -914,7 +951,7 @@ agent's shell, its browser step hangs.
|
|
|
914
951
|
copy it, then `umask 077 && pbpaste | tr -d '\n' > <file> && pbcopy </dev/null`. At apply, the shell
|
|
915
952
|
exports it from that file into the variable a `Secret` reads. An egress rule allows `api.anthropic.com`
|
|
916
953
|
and adds `Authorization: Bearer ${secret}`, removing `x-api-key`. The environment's `env` holds only
|
|
917
|
-
`CLAUDE_CODE_OAUTH_TOKEN
|
|
954
|
+
`CLAUDE_CODE_OAUTH_TOKEN` set to the Secret, its reference, and an empty `ANTHROPIC_API_KEY`, and the
|
|
918
955
|
agent's gateway setting is off. Never put the token itself in `env`. The Claude sign-in recipe in
|
|
919
956
|
`references/authoring.md` shows the declaration. Copying the renewing credentials file is the fallback,
|
|
920
957
|
and follows the ChatGPT sign-in steps below.
|
|
@@ -4,7 +4,10 @@ Give access with a Secret the network edge adds, or with the provider's platform
|
|
|
4
4
|
so add only the rule that adds the credential. Git over HTTPS and the REST API often take the token in
|
|
5
5
|
different forms: give each its own rule, scoped by host or by path. A connection also brings events, which
|
|
6
6
|
a `Trigger` turns into work. The agent installs or uses whatever git or API client it prefers. The edge adds
|
|
7
|
-
the credential to
|
|
7
|
+
the credential only to a request that names it, or that an environment default covers
|
|
8
|
+
(`../environment.md`, "Naming a credential"). Name it in the environment's `env`: `GH_TOKEN` for `gh`,
|
|
9
|
+
`GITLAB_TOKEN` for `glab`, and `ORCHESTRA_GIT_CREDENTIAL` for git, each set to the Secret or the
|
|
10
|
+
Connection.
|
|
8
11
|
|
|
9
12
|
Phase 3, "From a recipe to code", turns a recipe into a Secret and a rule.
|
|
10
13
|
|
|
@@ -21,8 +24,25 @@ project or the profile; Azure DevOps **User settings › Personal access tokens*
|
|
|
21
24
|
|
|
22
25
|
## Git over HTTPS
|
|
23
26
|
|
|
24
|
-
Git sends Basic auth. The
|
|
25
|
-
|
|
27
|
+
Git sends Basic auth. The image's git credential helper answers the reference in `ORCHESTRA_GIT_CREDENTIAL`
|
|
28
|
+
as the password when the server asks, and the edge replaces it, so the agent clones
|
|
29
|
+
`https://<host>/<path>.git` with no user name and no password in the address. A default that covers the
|
|
30
|
+
host adds the credential with no reference.
|
|
31
|
+
|
|
32
|
+
A git token Secret stores the encoded value `base64("<user>:<token>")` and presents it as `Basic`. The
|
|
33
|
+
recipe has no base64 step, so encode the pair first. `SecretValue.basicAuth(user, value)` encodes it at
|
|
34
|
+
apply; to store it through the API or the console, encode it yourself:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
printf 'x-access-token:%s' "$TOKEN" | base64 | tr -d '\n'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
new Secret(factory, "github-git", {
|
|
42
|
+
value: SecretValue.basicAuth("x-access-token", SecretValue.fromEnv("ORCHESTRA_FDK_GITHUB_TOKEN")),
|
|
43
|
+
injection: { headers: [{ name: "Authorization", value: "Basic ${secret}" }], hosts: ["github.com"] },
|
|
44
|
+
});
|
|
45
|
+
```
|
|
26
46
|
|
|
27
47
|
- **GitHub:** the user name is `x-access-token`, and the password is the token:
|
|
28
48
|
`SecretValue.basicAuth("x-access-token", SecretValue.fromEnv("ORCHESTRA_FDK_GITHUB_TOKEN"))`. The GitHub
|
|
@@ -42,8 +62,9 @@ the first declared wins for every request. So scope the API rule to `paths: ["/a
|
|
|
42
62
|
priority, 310. The git rule at 300 takes the rest of the host.
|
|
43
63
|
|
|
44
64
|
The API rule replaces the `PRIVATE-TOKEN` header. A project access token must be sent this way: sent as
|
|
45
|
-
`Authorization: Bearer` beside
|
|
46
|
-
same way.
|
|
65
|
+
`Authorization: Bearer` beside another `PRIVATE-TOKEN`, GitLab answers 401. A personal token works the
|
|
66
|
+
same way. `glab` sends `GITLAB_TOKEN` as `Private-Token`, so set `GITLAB_TOKEN` to the Secret or the
|
|
67
|
+
Connection, and the edge reads the reference there.
|
|
47
68
|
|
|
48
69
|
With the `gitlab` connection and `Auth.oauth()`, use `.allow(gitlab, { paths: ["/api/*"], priority: 310 })`
|
|
49
70
|
for the API instead, and keep the git rule. GitLab does not let the platform register the webhook. The
|
|
@@ -19,7 +19,7 @@ npx --package @orchestraworks/cli orchestra workflows list --all-pages --output
|
|
|
19
19
|
npx --package @orchestraworks/cli orchestra connections list --output json
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
A row that an apply made carries the label `
|
|
22
|
+
A row that an apply made carries the label `orchestra.managedBy`, whose value is the factory's name.
|
|
23
23
|
|
|
24
24
|
1. Infer what the developer most likely wants:
|
|
25
25
|
- *No agents and no pipelines:* the account is empty. Infer **a new factory**.
|
|
@@ -90,7 +90,7 @@ your shell, its browser step hangs. You never read or copy the value.
|
|
|
90
90
|
|
|
91
91
|
- *Claude.* `claude setup-token` prints a long-lived token. The developer saves it in the private file as
|
|
92
92
|
`ORCHESTRA_FDK_CLAUDE_CODE_OAUTH_TOKEN`. The factory stores it as a Secret, and the network edge adds it to
|
|
93
|
-
calls to `api.anthropic.com
|
|
93
|
+
calls to `api.anthropic.com` that name it. The machine holds only the Secret's reference and an empty API key. Declare the
|
|
94
94
|
Claude sign-in recipe from `../authoring.md`, "Secrets are recipes, never values". Never put the token in
|
|
95
95
|
the environment's settings: they are stored and shown in plain text.
|
|
96
96
|
- *Codex.* A personal access token, which begins `at-`. A ChatGPT Business or Enterprise member makes it at
|
|
@@ -83,7 +83,8 @@ C, "Tokens on a compute provider's machines").
|
|
|
83
83
|
|
|
84
84
|
- A `401` from the model provider, or "Invalid API key": the agent has no model key. Check the route the
|
|
85
85
|
team chose: the environment's `env`, the image's worker service environment (on Windows, the
|
|
86
|
-
`
|
|
86
|
+
`OrchestraWorker` service's `Environment` registry value, or `SfdkWorker` on an image an earlier
|
|
87
|
+
release built), or the key file the `onStartAgent` hook writes.
|
|
87
88
|
- The image's value is a real key and `env` declares another: the image's value wins. A declared value
|
|
88
89
|
replaces only our image's placeholder.
|
|
89
90
|
- Never restart the worker service to load a key. A restart after the worker registered stops the
|
|
@@ -109,7 +110,7 @@ C, "Tokens on a compute provider's machines").
|
|
|
109
110
|
- A trigger that could not start a session shows the reason on the trigger's `lastDecision`. A work item
|
|
110
111
|
shows a feed note: `No session started for <agent>: ...`.
|
|
111
112
|
- The model gateway answers `403` with `credits_exhausted` or `budget_exceeded`, in the model provider's
|
|
112
|
-
own error shape, with
|
|
113
|
+
own error shape, with an `[orchestra]` prefix on the message.
|
|
113
114
|
- `403` with `PLAN_FEATURE_DISABLED`: the plan does not include the feature. This is not a credit problem.
|
|
114
115
|
|
|
115
116
|
`maxSessionsPerMachine` is not an error. A session past it starts another machine.
|
|
@@ -68,13 +68,16 @@ const github = new Connection(factory, "github", {
|
|
|
68
68
|
install: { permissions: ["contents:write"] },
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
// The edge adds the gateway key on the gateway's host.
|
|
72
|
-
//
|
|
71
|
+
// The edge adds the gateway key on the gateway's host. Claude sends the key's reference in x-api-key, and the
|
|
72
|
+
// gateway refuses two keys that disagree, so the recipe removes it.
|
|
73
73
|
const gatewayHost = required("ORCHESTRA_FDK_MODEL_GATEWAY_HOST");
|
|
74
74
|
const gatewayKey = new Secret(factory, "model-gateway-key", {
|
|
75
75
|
value: SecretValue.fromGatewayKey(new GatewayKey(factory, "model-gateway", { displayName: "__FACTORY_NAME__ bug fixing" })),
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
injection: {
|
|
77
|
+
headers: [{ name: "Authorization", value: "Bearer ${secret}" }],
|
|
78
|
+
removeHeaders: ["x-api-key"],
|
|
79
|
+
hosts: [gatewayHost],
|
|
80
|
+
},
|
|
78
81
|
});
|
|
79
82
|
// init-if: anthropic
|
|
80
83
|
|
|
@@ -59,9 +59,12 @@ export function declareKeys(factory: Factory): Keys {
|
|
|
59
59
|
name: "Model gateway key",
|
|
60
60
|
description: "Added to model calls at the network edge. The agents never see it.",
|
|
61
61
|
value: SecretValue.fromGatewayKey(gatewayKey),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
// Claude sends the key's reference in x-api-key, and the gateway refuses two keys that disagree.
|
|
63
|
+
injection: {
|
|
64
|
+
headers: [{ name: "Authorization", value: "Bearer ${secret}" }],
|
|
65
|
+
removeHeaders: ["x-api-key"],
|
|
66
|
+
hosts: [DEFAULT_MODEL_GATEWAY_HOST],
|
|
67
|
+
},
|
|
65
68
|
});
|
|
66
69
|
return { modelGateway };
|
|
67
70
|
}
|
|
@@ -66,13 +66,16 @@ const github = new Connection(factory, "github", {
|
|
|
66
66
|
install: { permissions: ["contents:write"] },
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
// The edge adds the gateway key on the gateway's host.
|
|
70
|
-
//
|
|
69
|
+
// The edge adds the gateway key on the gateway's host. Claude sends the key's reference in x-api-key, and the
|
|
70
|
+
// gateway refuses two keys that disagree, so the recipe removes it.
|
|
71
71
|
const gatewayHost = required("ORCHESTRA_FDK_MODEL_GATEWAY_HOST");
|
|
72
72
|
const gatewayKey = new Secret(factory, "model-gateway-key", {
|
|
73
73
|
value: SecretValue.fromGatewayKey(new GatewayKey(factory, "model-gateway", { displayName: "todo-app bug fixing" })),
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
injection: {
|
|
75
|
+
headers: [{ name: "Authorization", value: "Bearer ${secret}" }],
|
|
76
|
+
removeHeaders: ["x-api-key"],
|
|
77
|
+
hosts: [gatewayHost],
|
|
78
|
+
},
|
|
76
79
|
});
|
|
77
80
|
|
|
78
81
|
// The image tag hashes the runtime image and every file under image/: a rebuilt image under an unchanged tag
|