copilot-operator 0.1.12 → 0.1.15
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 +9 -14
- package/dist/src/api/operator.controller.js +31 -42
- package/dist/src/api/operator.service.js +692 -364
- package/dist/src/api/saveToDesktop.js +1 -1
- package/dist/src/api/security.js +40 -10
- package/dist/src/api/server.js +75 -12
- package/dist/src/api/settings.js +156 -15
- package/dist/src/cli.js +108 -97
- package/dist/src/config/handWritten.js +36 -0
- package/dist/src/config/layout.js +13 -4
- package/dist/src/config/lockedPolicy.js +56 -22
- package/dist/src/config/schema.js +13 -84
- package/dist/src/context/desktopDir.js +44 -0
- package/dist/src/exec/authorizer.js +9 -1
- package/dist/src/exec/checks.js +240 -23
- package/dist/src/exec/dangerous.js +29 -5
- package/dist/src/exec/network.js +105 -28
- package/dist/src/exec/redaction.js +52 -9
- package/dist/src/exec/reportFile.js +4 -5
- package/dist/src/exec/runner.js +146 -13
- package/dist/src/exec/shells.js +41 -6
- package/dist/src/exec/workDir.js +4 -4
- package/dist/src/orchestrator/derivedChecks.js +1 -0
- package/dist/src/orchestrator/review.js +25 -3
- package/dist/src/orchestrator/taskRunner.js +482 -150
- package/dist/src/plan/brief.js +56 -44
- package/dist/src/plan/importPlan.js +18 -7
- package/dist/src/plan/schema.js +90 -31
- package/dist/src/plan/script.js +79 -90
- package/dist/src/plan/systemGuide.js +39 -182
- package/dist/src/protocol/parser.js +5 -1
- package/dist/src/session/debugExport.js +1 -1
- package/dist/src/session/exportRecord.js +1 -1
- package/dist/src/session/exports.js +52 -9
- package/dist/src/session/store.js +215 -80
- package/dist/src/vcs/git.js +77 -2
- package/dist/src/vcs/scope.js +29 -6
- package/dist/src/vcs/taskVcs.js +149 -23
- package/dist/web/404.html +2 -2
- package/dist/web/_next/static/chunks/36-94660f1cde2da529.js +1 -0
- package/dist/web/_next/static/chunks/549-9548d9a132c8c7d4.js +1 -0
- package/dist/web/_next/static/chunks/867-8b95ae984befc0e2.js +1 -0
- package/dist/web/_next/static/chunks/app/defaults/page-71a43ed69134b48d.js +1 -0
- package/dist/web/_next/static/chunks/app/history/page-d7314ac22d47a3ab.js +1 -0
- package/dist/web/_next/static/chunks/app/sessions/view/page-886f5fc99e146408.js +1 -0
- package/dist/web/_next/static/css/{a0642fdb8adf9edb.css → e2a2c9eb3c226313.css} +1 -1
- package/dist/web/appearance.html +2 -2
- package/dist/web/appearance.txt +12 -12
- package/dist/web/defaults.html +2 -2
- package/dist/web/defaults.txt +12 -12
- package/dist/web/history.html +2 -2
- package/dist/web/history.txt +12 -12
- package/dist/web/import.html +2 -2
- package/dist/web/import.txt +12 -12
- package/dist/web/index.html +2 -2
- package/dist/web/index.txt +12 -12
- package/dist/web/level1.html +2 -2
- package/dist/web/level1.txt +12 -12
- package/dist/web/presets.html +2 -2
- package/dist/web/presets.txt +12 -12
- package/dist/web/sessions/view.html +2 -2
- package/dist/web/sessions/view.txt +12 -12
- package/dist/web/system.html +2 -2
- package/dist/web/system.txt +12 -12
- package/package.json +18 -10
- package/prompts/level1.md +2 -1
- package/prompts/organisation.bg.md +0 -1
- package/prompts/organisation.en.md +0 -1
- package/dist/src/context/contextFiles.js +0 -295
- package/dist/src/context/desktopMirror.js +0 -135
- package/dist/src/context/projectMirror.js +0 -432
- package/dist/web/_next/static/chunks/295-04151ded0741d18d.js +0 -1
- package/dist/web/_next/static/chunks/36-c3dd06c6a459c114.js +0 -1
- package/dist/web/_next/static/chunks/549-a5a59a94f92bb3eb.js +0 -1
- package/dist/web/_next/static/chunks/867-b64c436f5b25f39e.js +0 -1
- package/dist/web/_next/static/chunks/app/defaults/page-5f180a88398be4c9.js +0 -1
- package/dist/web/_next/static/chunks/app/history/page-854e3dde672e38a2.js +0 -1
- package/dist/web/_next/static/chunks/app/sessions/view/page-cc99401da5173b36.js +0 -1
- /package/dist/web/_next/static/{bcBE4tY_aNh6kzLzN1luP → IwNZiBrEMAcXEUFj2miiU}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{bcBE4tY_aNh6kzLzN1luP → IwNZiBrEMAcXEUFj2miiU}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -8,9 +8,8 @@ browser, so it needs no API licence and no admin consent.
|
|
|
8
8
|
|
|
9
9
|
> **Status: working, every part verified against a live Microsoft 365 Copilot tenant.**
|
|
10
10
|
> The loop, chat naming, reply parsing, command execution and the results file are confirmed.
|
|
11
|
-
> A step printing for 92 s survived while a silent one was stopped at its idle limit.
|
|
12
|
-
>
|
|
13
|
-
> one thing still untried, deliberately.
|
|
11
|
+
> A step printing for 92 s survived while a silent one was stopped at its idle limit. Unattended
|
|
12
|
+
> mode is the one thing still untried, deliberately.
|
|
14
13
|
|
|
15
14
|
## What it does
|
|
16
15
|
|
|
@@ -26,8 +25,8 @@ browser, so it needs no API licence and no admin consent.
|
|
|
26
25
|
6. Repeats until Copilot finishes with a **summary** of what it did and what the result is.
|
|
27
26
|
7. Runs the next queued task in the same conversation.
|
|
28
27
|
|
|
29
|
-
A **session** is one conversation with a queue of tasks.
|
|
30
|
-
|
|
28
|
+
A **session** is one conversation with a queue of tasks. No file of the project is attached to
|
|
29
|
+
the chat: it reads the project by running commands in the project folder, like any other step.
|
|
31
30
|
|
|
32
31
|
There is a web UI for all of this, and a terminal command for a single task.
|
|
33
32
|
|
|
@@ -42,7 +41,7 @@ are redacted from every report before it is uploaded, always, with `report.redac
|
|
|
42
41
|
applied on top. Running it in a dedicated Windows account or Windows Sandbox is recommended.
|
|
43
42
|
|
|
44
43
|
**Where the reports go.** Every report — the terminal output of every step, the check results,
|
|
45
|
-
the reviewer's transcripts —
|
|
44
|
+
the reviewer's transcripts — are uploaded into the
|
|
46
45
|
Copilot chat, which stores them in the signed-in account's OneDrive for Business and in the
|
|
47
46
|
Copilot conversation history. They stay inside the company's own Microsoft 365 tenant, under its
|
|
48
47
|
Purview DLP policies, sensitivity labels, retention and eDiscovery; nothing goes to any other
|
|
@@ -257,7 +256,7 @@ runs this on managed laptops, because no code change can decide it for them.
|
|
|
257
256
|
- *Data protection.* See "Where the reports go" above. Where the law requires it, record the
|
|
258
257
|
processing (a DPIA where automated tooling that stores every command's output and sends it to
|
|
259
258
|
an AI service triggers one; works-council consultation where tooling that records employees'
|
|
260
|
-
actions requires it).
|
|
259
|
+
actions requires it). Command output can carry personal data from fixtures and exports.
|
|
261
260
|
- *AI-use policy.* Commands written by a language model execute on the endpoint. Confirm mode
|
|
262
261
|
keeps a person on every line; unattended mode removes that person and is the decision most
|
|
263
262
|
AI-governance policies want recorded. `policy.lock.json` can forbid it machine-wide.
|
|
@@ -273,8 +272,6 @@ which the operator cannot edit (the same file beside the install is honoured too
|
|
|
273
272
|
{
|
|
274
273
|
"maxMode": "confirm",
|
|
275
274
|
"requireIsolation": true,
|
|
276
|
-
"allowDesktopMirror": false,
|
|
277
|
-
"allowEnvFiles": false,
|
|
278
275
|
"passEnv": [],
|
|
279
276
|
"allowedPrograms": ["pwsh", "powershell", "cmd", "node", "npm", "npx", "git", "dotnet"],
|
|
280
277
|
"update": { "requireSigned": true, "remote": "https://git.example.com/tools/copilot-operator.git" }
|
|
@@ -426,7 +423,8 @@ exist. What the software leaves, and where:
|
|
|
426
423
|
| backups taken by `npm run update` | `data-backups/` in the checkout | delete the folder |
|
|
427
424
|
| the bot's own Edge profile, with its Microsoft 365 session | `%LOCALAPPDATA%\copilot-operator\edge-profile` | sign out in that window (`cop login --fresh` deletes it) and delete the folder |
|
|
428
425
|
| the API key the browser was handed | the browser's storage for `localhost:3210` | clear site data for that origin |
|
|
429
|
-
| logs saved on request
|
|
426
|
+
| logs saved on request | `Desktop\copilot-operator-logs` | delete them; they may already be in OneDrive |
|
|
427
|
+
| the project copies of versions before 0.1.14 | `Desktop\copilot-operator-context` | delete the folder; nothing writes to it any more |
|
|
430
428
|
| the reports uploaded to Copilot | the account's OneDrive and Copilot chat history | delete the conversations, or let the tenant's retention run |
|
|
431
429
|
| a machine-wide policy lock, if an administrator placed one | `%ProgramData%\copilot-operator\policy.lock.json` | the administrator removes it |
|
|
432
430
|
|
|
@@ -480,8 +478,6 @@ npx tsx src/cli.ts run run.yaml
|
|
|
480
478
|
|---|---|
|
|
481
479
|
| `cop login` | opens Edge with the bot profile and waits for you to sign in |
|
|
482
480
|
| `cop doctor [run.yaml]` | checks Node, Edge, PowerShell, the Desktop, the config |
|
|
483
|
-
| `cop dirs <projectRoot>` | lists the directories you can select for the mirror |
|
|
484
|
-
| `cop mirror <run.yaml>` | refreshes the Desktop folder without touching the chat |
|
|
485
481
|
| `cop run <run.yaml>` | one task as a new session; `--unattended` skips the per-step prompt |
|
|
486
482
|
| `cop chat [run.yaml]` | prints the last run's conversation name and link |
|
|
487
483
|
|
|
@@ -499,8 +495,7 @@ ordered so the risk climbs slowly, starting with checks that need no account at
|
|
|
499
495
|
| Command runner, dual timeouts, process-tree kill | `src/exec/runner.ts` | implemented, checked |
|
|
500
496
|
| Chat naming and reattach after re-login | `src/transport/chatSession.ts` | implemented, checked |
|
|
501
497
|
| Covering message for the results file | `src/protocol/reporter.ts` | implemented, checked |
|
|
502
|
-
| Desktop folder resolution
|
|
503
|
-
| Project mirror, flattened names, incremental | `src/context/projectMirror.ts` | implemented, checked |
|
|
498
|
+
| Desktop folder resolution (for saved logs) | `src/context/desktopDir.ts` | implemented |
|
|
504
499
|
| Pacing, send cap, backoff | `src/util/pacing.ts` | implemented, checked |
|
|
505
500
|
| Playwright transport | `src/transport/copilotTransport.ts` | implemented; selectors verified live |
|
|
506
501
|
| Reply parser and the JSON contract | `src/protocol/parser.ts`, `replySchema.ts` | implemented, checked |
|
|
@@ -10,12 +10,36 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* The HTTP surface. Thin on purpose: every route hands straight to the service.
|
|
15
|
+
*
|
|
16
|
+
* Bound to localhost only (see main.ts). There is no authentication because there is no
|
|
17
|
+
* network: this API runs on the operator's own machine and drives that machine's browser.
|
|
18
|
+
*/
|
|
13
19
|
import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Query, Res, Sse, NotFoundException, BadRequestException } from '@nestjs/common';
|
|
14
20
|
import { Observable } from 'rxjs';
|
|
15
21
|
import { OperatorService } from './operator.service.js';
|
|
22
|
+
import { SettingsUnusableError } from './settings.js';
|
|
23
|
+
import { PolicyLockUnusableError } from '../config/lockedPolicy.js';
|
|
16
24
|
function fail(e) {
|
|
25
|
+
// A settings file or a policy lock that cannot be used is not the request's fault, and every route
|
|
26
|
+
// that reads the settings meets it, wrapped here or not: it is passed on as it is, for the server
|
|
27
|
+
// to answer the same way on all of them (see `server.ts`).
|
|
28
|
+
if (e instanceof SettingsUnusableError || e instanceof PolicyLockUnusableError)
|
|
29
|
+
throw e;
|
|
17
30
|
throw new BadRequestException(e.message);
|
|
18
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* `fail` for a route that writes its own response, as the downloads do: the request's own problem
|
|
34
|
+
* as plain text with a 400, and a settings file or a policy lock that cannot be used passed on to
|
|
35
|
+
* the server's answer for it. Each download answered both itself, so a broken file came back as a
|
|
36
|
+
* bad request there and as a 409 everywhere else.
|
|
37
|
+
*/
|
|
38
|
+
function failText(res, e) {
|
|
39
|
+
if (e instanceof SettingsUnusableError || e instanceof PolicyLockUnusableError)
|
|
40
|
+
throw e;
|
|
41
|
+
res.status(400).send(e.message);
|
|
42
|
+
}
|
|
19
43
|
/**
|
|
20
44
|
* A `content-disposition` header a session name cannot break.
|
|
21
45
|
*
|
|
@@ -84,7 +108,7 @@ let OperatorController = class OperatorController {
|
|
|
84
108
|
return this.ops.savePreset(name, body?.content ?? '').catch(fail);
|
|
85
109
|
}
|
|
86
110
|
async deletePreset(name) {
|
|
87
|
-
await this.ops.deletePreset(name);
|
|
111
|
+
await this.ops.deletePreset(name).catch(fail);
|
|
88
112
|
return { ok: true };
|
|
89
113
|
}
|
|
90
114
|
// --- named personas (the import page's second box, saved under a name) -----------------
|
|
@@ -103,7 +127,7 @@ let OperatorController = class OperatorController {
|
|
|
103
127
|
return this.ops.listSessions();
|
|
104
128
|
}
|
|
105
129
|
createSession(body) {
|
|
106
|
-
return this.ops.createSession(body?.name ?? '', body?.
|
|
130
|
+
return this.ops.createSession(body?.name ?? '', body?.projectDir).catch(fail);
|
|
107
131
|
}
|
|
108
132
|
async session(id) {
|
|
109
133
|
const s = await this.ops.getSession(id);
|
|
@@ -239,7 +263,7 @@ let OperatorController = class OperatorController {
|
|
|
239
263
|
res.send(content);
|
|
240
264
|
}
|
|
241
265
|
catch (e) {
|
|
242
|
-
res
|
|
266
|
+
failText(res, e);
|
|
243
267
|
}
|
|
244
268
|
}
|
|
245
269
|
/**
|
|
@@ -258,7 +282,7 @@ let OperatorController = class OperatorController {
|
|
|
258
282
|
res.send(content);
|
|
259
283
|
}
|
|
260
284
|
catch (e) {
|
|
261
|
-
res
|
|
285
|
+
failText(res, e);
|
|
262
286
|
}
|
|
263
287
|
}
|
|
264
288
|
/**
|
|
@@ -286,7 +310,7 @@ let OperatorController = class OperatorController {
|
|
|
286
310
|
res.send(content);
|
|
287
311
|
}
|
|
288
312
|
catch (e) {
|
|
289
|
-
res
|
|
313
|
+
failText(res, e);
|
|
290
314
|
}
|
|
291
315
|
}
|
|
292
316
|
/**
|
|
@@ -306,7 +330,7 @@ let OperatorController = class OperatorController {
|
|
|
306
330
|
res.send(content);
|
|
307
331
|
}
|
|
308
332
|
catch (e) {
|
|
309
|
-
res
|
|
333
|
+
failText(res, e);
|
|
310
334
|
}
|
|
311
335
|
}
|
|
312
336
|
// --- run control ------------------------------------------------------------------------
|
|
@@ -456,9 +480,7 @@ let OperatorController = class OperatorController {
|
|
|
456
480
|
}
|
|
457
481
|
/** Stores it. A field left out is kept; an empty `rootDir` clears the default. */
|
|
458
482
|
setProject(body) {
|
|
459
|
-
return this.ops
|
|
460
|
-
.setProject({ rootDir: body?.rootDir, name: body?.name, others: body?.others, mirrorToDesktop: body?.mirrorToDesktop, mirror: body?.mirror, desktop: body?.desktop })
|
|
461
|
-
.catch(fail);
|
|
483
|
+
return this.ops.setProject({ rootDir: body?.rootDir, name: body?.name, others: body?.others }).catch(fail);
|
|
462
484
|
}
|
|
463
485
|
/**
|
|
464
486
|
* Whether a folder can be used for version control at all, asked before the choice is made.
|
|
@@ -506,11 +528,6 @@ let OperatorController = class OperatorController {
|
|
|
506
528
|
return this.ops.metrics();
|
|
507
529
|
}
|
|
508
530
|
// --- helpers for the UI -----------------------------------------------------------------
|
|
509
|
-
dirs(root, gitignore) {
|
|
510
|
-
if (!root)
|
|
511
|
-
throw new BadRequestException('root is required');
|
|
512
|
-
return this.ops.dirs(root, gitignore !== '0' && gitignore !== 'false');
|
|
513
|
-
}
|
|
514
531
|
/**
|
|
515
532
|
* Opens the machine's own folder dialog. A POST because it has a visible effect: a window
|
|
516
533
|
* appears on the operator's desktop and waits for them.
|
|
@@ -518,19 +535,6 @@ let OperatorController = class OperatorController {
|
|
|
518
535
|
browseFolder(body) {
|
|
519
536
|
return this.ops.browseFolder(body?.start);
|
|
520
537
|
}
|
|
521
|
-
preview(body) {
|
|
522
|
-
if (!body?.rootDir?.trim())
|
|
523
|
-
throw new BadRequestException('rootDir is required');
|
|
524
|
-
return this.ops
|
|
525
|
-
.previewMirror({
|
|
526
|
-
rootDir: body.rootDir.trim(),
|
|
527
|
-
includeDirs: body.includeDirs ?? [],
|
|
528
|
-
excludeDirs: body.excludeDirs ?? [],
|
|
529
|
-
respectGitignore: body.respectGitignore ?? true,
|
|
530
|
-
includeEnvFiles: body.includeEnvFiles ?? false,
|
|
531
|
-
})
|
|
532
|
-
.catch(fail);
|
|
533
|
-
}
|
|
534
538
|
};
|
|
535
539
|
__decorate([
|
|
536
540
|
Get('health'),
|
|
@@ -1057,14 +1061,6 @@ __decorate([
|
|
|
1057
1061
|
__metadata("design:paramtypes", []),
|
|
1058
1062
|
__metadata("design:returntype", Promise)
|
|
1059
1063
|
], OperatorController.prototype, "metrics", null);
|
|
1060
|
-
__decorate([
|
|
1061
|
-
Get('dirs'),
|
|
1062
|
-
__param(0, Query('root')),
|
|
1063
|
-
__param(1, Query('gitignore')),
|
|
1064
|
-
__metadata("design:type", Function),
|
|
1065
|
-
__metadata("design:paramtypes", [String, String]),
|
|
1066
|
-
__metadata("design:returntype", Promise)
|
|
1067
|
-
], OperatorController.prototype, "dirs", null);
|
|
1068
1064
|
__decorate([
|
|
1069
1065
|
Post('browse-folder'),
|
|
1070
1066
|
__param(0, Body()),
|
|
@@ -1072,13 +1068,6 @@ __decorate([
|
|
|
1072
1068
|
__metadata("design:paramtypes", [Object]),
|
|
1073
1069
|
__metadata("design:returntype", Promise)
|
|
1074
1070
|
], OperatorController.prototype, "browseFolder", null);
|
|
1075
|
-
__decorate([
|
|
1076
|
-
Post('mirror/preview'),
|
|
1077
|
-
__param(0, Body()),
|
|
1078
|
-
__metadata("design:type", Function),
|
|
1079
|
-
__metadata("design:paramtypes", [Object]),
|
|
1080
|
-
__metadata("design:returntype", Promise)
|
|
1081
|
-
], OperatorController.prototype, "preview", null);
|
|
1082
1071
|
OperatorController = __decorate([
|
|
1083
1072
|
Controller(),
|
|
1084
1073
|
__param(0, Inject(OperatorService)),
|