@sente-labs/cli 0.5.0 → 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/dist/commands/auth.js +54 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/skill/SKILL.md +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAuthCommands = registerAuthCommands;
|
|
4
|
+
const client_1 = require("../client");
|
|
5
|
+
const output_1 = require("../output");
|
|
6
|
+
/**
|
|
7
|
+
* Authenticated-session seeding for SSO / 2FA flows. A human logs in once in a
|
|
8
|
+
* live browser bound to the project's persistent profile; the login persists,
|
|
9
|
+
* and later runs start already authenticated (so tests skip the login entirely).
|
|
10
|
+
*
|
|
11
|
+
* This is the answer for providers like "Log in with Google" that block
|
|
12
|
+
* automated per-run logins (bot detection + 2FA device prompts).
|
|
13
|
+
*/
|
|
14
|
+
function registerAuthCommands(program, globalOpts) {
|
|
15
|
+
const auth = program.command('auth').description('Authenticated-session seeding (SSO / 2FA)');
|
|
16
|
+
auth
|
|
17
|
+
.command('login')
|
|
18
|
+
.description('Open a one-time live browser to log in (incl. SSO/2FA); saves the session to the project profile')
|
|
19
|
+
.requiredOption('--project <id>', 'project id to seed')
|
|
20
|
+
.action(async (cmdOpts) => {
|
|
21
|
+
const opts = globalOpts();
|
|
22
|
+
try {
|
|
23
|
+
const client = new client_1.GatewayClient();
|
|
24
|
+
const res = await client.request(`/api/projects/${cmdOpts.project}/auth-session`, { method: 'POST', body: {} });
|
|
25
|
+
(0, output_1.printSuccess)(res, () => {
|
|
26
|
+
process.stdout.write('Open this URL and log in (complete any 2FA on your device):\n\n');
|
|
27
|
+
process.stdout.write(` ${res.liveUrl}\n\n`);
|
|
28
|
+
process.stdout.write('Once you have reached the logged-in app, run:\n');
|
|
29
|
+
process.stdout.write(` sente auth finish ${res.sessionId} --project ${cmdOpts.project}\n\n`);
|
|
30
|
+
process.stdout.write(`(The live session expires at ${res.expiresAt}.)\n`);
|
|
31
|
+
}, opts);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
process.exit((0, output_1.printError)(err, opts));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
auth
|
|
38
|
+
.command('finish')
|
|
39
|
+
.description('Close the live login session and persist the logged-in state to the project profile')
|
|
40
|
+
.argument('<sessionId>', 'the sessionId returned by `sente auth login`')
|
|
41
|
+
.requiredOption('--project <id>', 'project id')
|
|
42
|
+
.action(async (sessionId, cmdOpts) => {
|
|
43
|
+
const opts = globalOpts();
|
|
44
|
+
try {
|
|
45
|
+
const client = new client_1.GatewayClient();
|
|
46
|
+
const res = await client.request(`/api/projects/${cmdOpts.project}/auth-session/${sessionId}/finish`, { method: 'POST', body: {} });
|
|
47
|
+
(0, output_1.printSuccess)(res, 'Login saved to the project profile. Runs with the browser profile enabled will start authenticated.', opts);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
process.exit((0, output_1.printError)(err, opts));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":";;AAoBA,oDAqDC;AAxED,sCAA0C;AAC1C,sCAAiE;AAUjE;;;;;;;GAOG;AACH,SAAgB,oBAAoB,CAAC,OAAgB,EAAE,UAA4B;IACjF,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,2CAA2C,CAAC,CAAC;IAE9F,IAAI;SACD,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,kGAAkG,CAAC;SAC/G,cAAc,CAAC,gBAAgB,EAAE,oBAAoB,CAAC;SACtD,MAAM,CAAC,KAAK,EAAE,OAA4B,EAAE,EAAE;QAC7C,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,sBAAa,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAC9B,iBAAiB,OAAO,CAAC,OAAO,eAAe,EAC/C,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAC7B,CAAC;YACF,IAAA,qBAAY,EACV,GAAG,EACH,GAAG,EAAE;gBACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iEAAiE,CAAC,CAAC;gBACxF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC;gBAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;gBACxE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,GAAG,CAAC,SAAS,cAAc,OAAO,CAAC,OAAO,MAAM,CAAC,CAAC;gBAC9F,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,GAAG,CAAC,SAAS,MAAM,CAAC,CAAC;YAC5E,CAAC,EACD,IAAI,CACL,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,IAAA,mBAAU,EAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,IAAI;SACD,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,qFAAqF,CAAC;SAClG,QAAQ,CAAC,aAAa,EAAE,8CAA8C,CAAC;SACvE,cAAc,CAAC,gBAAgB,EAAE,YAAY,CAAC;SAC9C,MAAM,CAAC,KAAK,EAAE,SAAiB,EAAE,OAA4B,EAAE,EAAE;QAChE,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,sBAAa,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAC9B,iBAAiB,OAAO,CAAC,OAAO,iBAAiB,SAAS,SAAS,EACnE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAC7B,CAAC;YACF,IAAA,qBAAY,EACV,GAAG,EACH,qGAAqG,EACrG,IAAI,CACL,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,IAAA,mBAAU,EAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const task_1 = require("./commands/task");
|
|
|
9
9
|
const init_1 = require("./commands/init");
|
|
10
10
|
const secret_1 = require("./commands/secret");
|
|
11
11
|
const schedule_1 = require("./commands/schedule");
|
|
12
|
+
const auth_1 = require("./commands/auth");
|
|
12
13
|
const autoupdate_1 = require("./skill/autoupdate");
|
|
13
14
|
// Silently refresh the installed Claude Code skill to match the running CLI.
|
|
14
15
|
// Synchronous + cheap (a file stat + at most one small write). No-op if the
|
|
@@ -22,7 +23,7 @@ const program = new commander_1.Command();
|
|
|
22
23
|
program
|
|
23
24
|
.name('sente')
|
|
24
25
|
.description('Sente CLI -- manage QA tests from the command line and Claude Code')
|
|
25
|
-
.version('0.
|
|
26
|
+
.version('0.6.0')
|
|
26
27
|
.option('--json', 'Emit machine-readable JSON instead of human output');
|
|
27
28
|
/**
|
|
28
29
|
* Global options aren't auto-propagated to subcommands by Commander, so we
|
|
@@ -39,6 +40,7 @@ const globalOpts = () => program.opts();
|
|
|
39
40
|
(0, task_1.registerTaskCommands)(program, globalOpts);
|
|
40
41
|
(0, secret_1.registerSecretCommands)(program, globalOpts);
|
|
41
42
|
(0, schedule_1.registerScheduleCommands)(program, globalOpts);
|
|
43
|
+
(0, auth_1.registerAuthCommands)(program, globalOpts);
|
|
42
44
|
program.parseAsync(process.argv).catch((err) => {
|
|
43
45
|
// Defensive: command actions handle their own errors. This only catches
|
|
44
46
|
// setup-time errors (e.g. unknown subcommand).
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yCAAoC;AACpC,4CAAwD;AACxD,8CAA0D;AAC1D,gDAAkF;AAClF,0CAAuD;AACvD,0CAAmF;AACnF,8CAA2D;AAC3D,kDAA+D;AAC/D,mDAA+E;AAG/E,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,IAAA,mCAAsB,GAAE,CAAC;AAEzB,wEAAwE;AACxE,uEAAuE;AACvE,6DAA6D;AAC7D,KAAK,IAAA,8BAAiB,GAAE,CAAC;AAEzB,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,oEAAoE,CAAC;KACjF,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,QAAQ,EAAE,oDAAoD,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,GAAG,GAAe,EAAE,CAAC,OAAO,CAAC,IAAI,EAAc,CAAC;AAEhE,IAAA,4BAAoB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC1C,IAAA,8BAAqB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC3C,IAAA,0BAAmB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACzC,IAAA,kCAA2B,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACjD,IAAA,iCAAuB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC7C,IAAA,6BAAmB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACzC,IAAA,2BAAoB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC1C,IAAA,+BAAsB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC5C,IAAA,mCAAwB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yCAAoC;AACpC,4CAAwD;AACxD,8CAA0D;AAC1D,gDAAkF;AAClF,0CAAuD;AACvD,0CAAmF;AACnF,8CAA2D;AAC3D,kDAA+D;AAC/D,0CAAuD;AACvD,mDAA+E;AAG/E,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,IAAA,mCAAsB,GAAE,CAAC;AAEzB,wEAAwE;AACxE,uEAAuE;AACvE,6DAA6D;AAC7D,KAAK,IAAA,8BAAiB,GAAE,CAAC;AAEzB,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,oEAAoE,CAAC;KACjF,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,QAAQ,EAAE,oDAAoD,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,GAAG,GAAe,EAAE,CAAC,OAAO,CAAC,IAAI,EAAc,CAAC;AAEhE,IAAA,4BAAoB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC1C,IAAA,8BAAqB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC3C,IAAA,0BAAmB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACzC,IAAA,kCAA2B,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACjD,IAAA,iCAAuB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC7C,IAAA,6BAAmB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACzC,IAAA,2BAAoB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC1C,IAAA,+BAAsB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC5C,IAAA,mCAAwB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC9C,IAAA,2BAAoB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAE1C,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,wEAAwE;IACxE,+CAA+C;IAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,OAAO,IAAI,GAAG,IAAI,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -55,6 +55,20 @@ When the user pastes a failure or asks why a run failed:
|
|
|
55
55
|
- **`test_design`**: the test is wrong for the current app. Propose a fix to the steps, then update the task using recipe B.
|
|
56
56
|
3. If the failure is ambiguous, fetch full details with `sente task show <id> --json` and inspect step-by-step `actual` vs `assertion`.
|
|
57
57
|
|
|
58
|
+
### D. Authenticated flows (SSO / 2FA)
|
|
59
|
+
|
|
60
|
+
If a flow needs login, first determine the auth type (check the app or its repo):
|
|
61
|
+
|
|
62
|
+
- **Plain email/password form**: author login as normal steps with `{{secret.email}}` / `{{secret.password}}` placeholders (see Hard rules). The agent types them at runtime; you never see the values.
|
|
63
|
+
- **SSO or 2FA** (e.g. "Continue with Google", Okta, magic link, any device/2-step prompt): do NOT author provider-login steps — automated provider logins are blocked (bot detection + 2FA device prompts). Seed the session once instead:
|
|
64
|
+
1. `sente auth login --project $(jq -r .projectId .sente/config.json)` → returns a live browser URL.
|
|
65
|
+
2. Tell the user: "Open this URL and log in (complete any 2FA on your phone), then tell me when you're in." Give them the URL and wait. **Never ask for their password.**
|
|
66
|
+
3. `sente auth finish <sessionId> --project <id>` → saves the login into the project's persistent browser profile.
|
|
67
|
+
4. Author the test to **start already authenticated**: no login steps — navigate to an authenticated route and assert a logged-in-only element (top bar, account menu, etc.). The seeded profile makes runs start logged in.
|
|
68
|
+
5. If a later run fails at a login wall, the seeded session expired — re-run `sente auth login` / `finish` to re-seed.
|
|
69
|
+
|
|
70
|
+
**Never put a password in the chat.** Usernames/emails are fine; passwords go through `sente secret create` (hidden prompt) or the live `sente auth login` browser.
|
|
71
|
+
|
|
58
72
|
## Test Authoring Rubric
|
|
59
73
|
|
|
60
74
|
The agent that runs your tests enforces this style. Steps that follow it pass more reliably.
|
|
@@ -106,6 +120,9 @@ The top-level `--instruction` is consulted by the agent when step text is ambigu
|
|
|
106
120
|
| `sente task delete <id> --json` | Delete a task |
|
|
107
121
|
| `sente task results <id> --failed-step --json` | Focused failure payload for diagnosis |
|
|
108
122
|
| `sente task results <id> --json` | Full step-by-step results |
|
|
123
|
+
| `sente task push-recording <id> --from <file> --json` | Upload a hardened replay recording (per-step selectors + assertion predicates) for a task. Advanced — a deterministic-replay recipe, not action/assertion steps; produced by the recording pipeline, not hand-authored here. |
|
|
124
|
+
| `sente auth login --project <id>` | Open a one-time live browser to log in (SSO/2FA); seeds the project's browser profile so runs start authenticated |
|
|
125
|
+
| `sente auth finish <sessionId> --project <id>` | Close the seeding session; persist the login to the profile |
|
|
109
126
|
| `sente sync --json` | Re-pull tasks into `.sente/tasks.json` |
|
|
110
127
|
| `sente secret list --json` | List secrets (metadata only; values are never returned) |
|
|
111
128
|
| `sente secret create --name <key> --project <projectId> [--target-url <url>]` | Create a secret. Hidden prompt for value, or `--value-stdin` for piping |
|
package/package.json
CHANGED