@sente-labs/cli 0.5.0 → 0.7.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/commands/project.js +20 -0
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/workflow.js +69 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/skill/SKILL.md +32 -1
- 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/commands/project.js
CHANGED
|
@@ -39,6 +39,7 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const client_1 = require("../client");
|
|
41
41
|
const output_1 = require("../output");
|
|
42
|
+
const workflow_1 = require("./workflow");
|
|
42
43
|
/**
|
|
43
44
|
* Read the local .sente/config.json (written by `sente init` in M4).
|
|
44
45
|
* Walks up from the cwd looking for it -- same pattern as git/node_modules.
|
|
@@ -93,6 +94,25 @@ function registerProjectCommands(program, globalOpts) {
|
|
|
93
94
|
process.exit((0, output_1.printError)(err, opts));
|
|
94
95
|
}
|
|
95
96
|
});
|
|
97
|
+
project
|
|
98
|
+
.command('run')
|
|
99
|
+
.description('Queue a run for every task across all workflows in a project (sequential, against staging)')
|
|
100
|
+
.option('--id <projectId>', 'Project id (overrides .sente/config.json)')
|
|
101
|
+
.action(async (cmdOpts) => {
|
|
102
|
+
const opts = globalOpts();
|
|
103
|
+
try {
|
|
104
|
+
const projectId = cmdOpts.id ?? readLocalProjectId();
|
|
105
|
+
if (!projectId) {
|
|
106
|
+
throw new Error('No project id found. Pass --id <projectId> or run `sente init` to create a .sente/config.json.');
|
|
107
|
+
}
|
|
108
|
+
const client = new client_1.GatewayClient();
|
|
109
|
+
const res = await client.request(`/api/projects/${projectId}/run`, { method: 'POST', body: {} });
|
|
110
|
+
(0, output_1.printSuccess)(res, () => (0, workflow_1.printBulkRun)(res), opts);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
process.exit((0, output_1.printError)(err, opts));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
96
116
|
}
|
|
97
117
|
function registerSyncCommand(program, globalOpts) {
|
|
98
118
|
program
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/commands/project.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/commands/project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printBulkRun = printBulkRun;
|
|
4
|
+
exports.registerWorkflowCommands = registerWorkflowCommands;
|
|
5
|
+
const client_1 = require("../client");
|
|
6
|
+
const config_1 = require("../config");
|
|
7
|
+
const output_1 = require("../output");
|
|
8
|
+
/** Print the per-task outcome of a bulk run (workflow or project). */
|
|
9
|
+
function printBulkRun(res) {
|
|
10
|
+
process.stdout.write(`${res.message}\n\n`);
|
|
11
|
+
for (const r of res.results) {
|
|
12
|
+
const tag = r.status.toUpperCase().padEnd(7);
|
|
13
|
+
const wf = r.workflowName ? `[${r.workflowName}] ` : '';
|
|
14
|
+
const detail = r.error ? ` -- ${r.error}` : r.sessionId ? ` (session ${r.sessionId})` : '';
|
|
15
|
+
process.stdout.write(` ${tag} ${wf}${r.taskName} (${r.taskId})${detail}\n`);
|
|
16
|
+
}
|
|
17
|
+
process.stdout.write('\nRuns are asynchronous. Track progress with `sente task results <taskId> --json` or the report URL in the dashboard.\n');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Workflow commands: list the workflows in a project, and run every task in a
|
|
21
|
+
* specific workflow with one call (`sente workflow run <id>`).
|
|
22
|
+
*/
|
|
23
|
+
function registerWorkflowCommands(program, globalOpts) {
|
|
24
|
+
const workflow = program.command('workflow').description('Workflow commands');
|
|
25
|
+
workflow
|
|
26
|
+
.command('list')
|
|
27
|
+
.description('List the workflows in a project (from .sente/config.json or --project)')
|
|
28
|
+
.option('--project <id>', 'project id (overrides .sente/config.json)')
|
|
29
|
+
.action(async (cmdOpts) => {
|
|
30
|
+
const opts = globalOpts();
|
|
31
|
+
try {
|
|
32
|
+
const projectId = cmdOpts.project ?? (0, config_1.readLocalProjectId)();
|
|
33
|
+
if (!projectId) {
|
|
34
|
+
throw new Error('No project id found. Pass --project <id> or run `sente init` to create a .sente/config.json.');
|
|
35
|
+
}
|
|
36
|
+
const client = new client_1.GatewayClient();
|
|
37
|
+
const res = await client.request(`/api/projects/${projectId}/workflows`, { query: { includeTasks: 'true' } });
|
|
38
|
+
(0, output_1.printSuccess)(res, () => {
|
|
39
|
+
if (res.workflows.length === 0) {
|
|
40
|
+
process.stdout.write('No workflows in this project.\n');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
for (const w of res.workflows) {
|
|
44
|
+
const count = typeof w.taskCount === 'number' ? ` (${w.taskCount} task${w.taskCount === 1 ? '' : 's'})` : '';
|
|
45
|
+
process.stdout.write(`${w.id} ${w.name}${count}\n`);
|
|
46
|
+
}
|
|
47
|
+
}, opts);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
process.exit((0, output_1.printError)(err, opts));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
workflow
|
|
54
|
+
.command('run')
|
|
55
|
+
.description('Queue a run for every task in a workflow (sequential, against staging)')
|
|
56
|
+
.argument('<workflowId>', 'workflow id')
|
|
57
|
+
.action(async (workflowId) => {
|
|
58
|
+
const opts = globalOpts();
|
|
59
|
+
try {
|
|
60
|
+
const client = new client_1.GatewayClient();
|
|
61
|
+
const res = await client.request(`/api/workflows/${workflowId}/run`, { method: 'POST', body: {} });
|
|
62
|
+
(0, output_1.printSuccess)(res, () => printBulkRun(res), opts);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
process.exit((0, output_1.printError)(err, opts));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
CHANGED
|
@@ -36,12 +36,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.CREDENTIALS_PATH = exports.CREDENTIALS_DIR = exports.DEFAULT_GATEWAY_URL = void 0;
|
|
37
37
|
exports.gatewayUrl = gatewayUrl;
|
|
38
38
|
exports.envToken = envToken;
|
|
39
|
+
exports.readLocalProjectId = readLocalProjectId;
|
|
39
40
|
/**
|
|
40
41
|
* CLI runtime configuration. Env vars take precedence over defaults.
|
|
41
42
|
*
|
|
42
43
|
* SENTE_GATEWAY_URL -- override the gateway base URL
|
|
43
44
|
* SENTE_API_TOKEN -- override the stored PAT (CI use)
|
|
44
45
|
*/
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
45
47
|
const os = __importStar(require("os"));
|
|
46
48
|
const path = __importStar(require("path"));
|
|
47
49
|
exports.DEFAULT_GATEWAY_URL = 'https://api.sente.run';
|
|
@@ -53,4 +55,29 @@ function gatewayUrl() {
|
|
|
53
55
|
function envToken() {
|
|
54
56
|
return process.env.SENTE_API_TOKEN || undefined;
|
|
55
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Read the project id from the local .sente/config.json, walking up from the
|
|
60
|
+
* cwd (same discovery as git/node_modules). Returns null if none is found.
|
|
61
|
+
* Canonical home for this lookup; commands that need a project default to it.
|
|
62
|
+
*/
|
|
63
|
+
function readLocalProjectId() {
|
|
64
|
+
let dir = process.cwd();
|
|
65
|
+
while (true) {
|
|
66
|
+
const candidate = path.join(dir, '.sente', 'config.json');
|
|
67
|
+
if (fs.existsSync(candidate)) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(fs.readFileSync(candidate, 'utf8'));
|
|
70
|
+
if (typeof parsed.projectId === 'string')
|
|
71
|
+
return parsed.projectId;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const parent = path.dirname(dir);
|
|
78
|
+
if (parent === dir)
|
|
79
|
+
return null;
|
|
80
|
+
dir = parent;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
56
83
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA,gCAEC;AAED,4BAEC;AAOD,gDAgBC;AA3CD;;;;;GAKG;AACH,uCAAyB;AACzB,uCAAyB;AACzB,2CAA6B;AAEhB,QAAA,mBAAmB,GAAG,uBAAuB,CAAC;AAC9C,QAAA,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AACpD,QAAA,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,uBAAe,EAAE,aAAa,CAAC,CAAC;AAE1E,SAAgB,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,2BAAmB,CAAC;AAC9D,CAAC;AAED,SAAgB,QAAQ;IACtB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB;IAChC,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACxB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC1D,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC9D,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;oBAAE,OAAO,MAAM,CAAC,SAAS,CAAC;YACpE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,10 +5,12 @@ const commander_1 = require("commander");
|
|
|
5
5
|
const login_1 = require("./commands/login");
|
|
6
6
|
const whoami_1 = require("./commands/whoami");
|
|
7
7
|
const project_1 = require("./commands/project");
|
|
8
|
+
const workflow_1 = require("./commands/workflow");
|
|
8
9
|
const task_1 = require("./commands/task");
|
|
9
10
|
const init_1 = require("./commands/init");
|
|
10
11
|
const secret_1 = require("./commands/secret");
|
|
11
12
|
const schedule_1 = require("./commands/schedule");
|
|
13
|
+
const auth_1 = require("./commands/auth");
|
|
12
14
|
const autoupdate_1 = require("./skill/autoupdate");
|
|
13
15
|
// Silently refresh the installed Claude Code skill to match the running CLI.
|
|
14
16
|
// Synchronous + cheap (a file stat + at most one small write). No-op if the
|
|
@@ -22,7 +24,7 @@ const program = new commander_1.Command();
|
|
|
22
24
|
program
|
|
23
25
|
.name('sente')
|
|
24
26
|
.description('Sente CLI -- manage QA tests from the command line and Claude Code')
|
|
25
|
-
.version('0.
|
|
27
|
+
.version('0.7.0')
|
|
26
28
|
.option('--json', 'Emit machine-readable JSON instead of human output');
|
|
27
29
|
/**
|
|
28
30
|
* Global options aren't auto-propagated to subcommands by Commander, so we
|
|
@@ -36,9 +38,11 @@ const globalOpts = () => program.opts();
|
|
|
36
38
|
(0, init_1.registerInstallSkillCommand)(program, globalOpts);
|
|
37
39
|
(0, project_1.registerProjectCommands)(program, globalOpts);
|
|
38
40
|
(0, project_1.registerSyncCommand)(program, globalOpts);
|
|
41
|
+
(0, workflow_1.registerWorkflowCommands)(program, globalOpts);
|
|
39
42
|
(0, task_1.registerTaskCommands)(program, globalOpts);
|
|
40
43
|
(0, secret_1.registerSecretCommands)(program, globalOpts);
|
|
41
44
|
(0, schedule_1.registerScheduleCommands)(program, globalOpts);
|
|
45
|
+
(0, auth_1.registerAuthCommands)(program, globalOpts);
|
|
42
46
|
program.parseAsync(process.argv).catch((err) => {
|
|
43
47
|
// Defensive: command actions handle their own errors. This only catches
|
|
44
48
|
// 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,kDAA+D;AAC/D,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,mCAAwB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC9C,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
|
@@ -28,7 +28,7 @@ Sente runs QA tests against the user's deployed staging environment via a cloud-
|
|
|
28
28
|
### A. Creating a new test
|
|
29
29
|
|
|
30
30
|
1. Read `.sente/config.json` and `.sente/tasks.json` to confirm the project exists and to learn the staging baseUrl.
|
|
31
|
-
2. Identify the workflow id to attach the task to — list with `sente workflow list --json` (
|
|
31
|
+
2. Identify the workflow id to attach the task to — list with `sente workflow list --json` (defaults to the repo's project; pass `--project <id>` to target another).
|
|
32
32
|
3. Draft steps following the **Test Authoring Rubric** below.
|
|
33
33
|
4. Write the steps to a temp file (e.g., `/tmp/sente-steps-<timestamp>.json`).
|
|
34
34
|
5. Call `sente task create --workflow <id> --name "..." --instruction "..." --steps-from <file> --json`.
|
|
@@ -55,6 +55,30 @@ 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
|
+
|
|
72
|
+
### E. Running tests (one task, a workflow, or a whole project)
|
|
73
|
+
|
|
74
|
+
Runs execute against the **current staging URL**, so only run after confirming the deploy you want to test has landed (see Critical context).
|
|
75
|
+
|
|
76
|
+
- **One task**: `sente task run <taskId>`.
|
|
77
|
+
- **A whole workflow** (all its tasks, in order): `sente workflow run <workflowId>`. Get ids with `sente workflow list --json`.
|
|
78
|
+
- **A whole project** (every task across every workflow): `sente project run` (defaults to the repo's project; `--id <projectId>` to target another).
|
|
79
|
+
|
|
80
|
+
All runs are **asynchronous and fire-and-forget** — the command queues the run(s) and returns immediately with a per-task status (`running` / `queued` / `failed`) plus a session id. **Do not sit in a foreground polling loop.** To report outcomes, poll lightly with `sente task results <taskId> --json` (status flips to `passed`/`failed` only when a run finishes), or point the user at the report URL in the dashboard. Bulk runs are sequential and respect the org's concurrent-session limit (overflow shows as `queued` and starts as capacity frees). Authenticated tests work in bulk runs too — the seeded browser profile (Recipe D) is reused for every task.
|
|
81
|
+
|
|
58
82
|
## Test Authoring Rubric
|
|
59
83
|
|
|
60
84
|
The agent that runs your tests enforces this style. Steps that follow it pass more reliably.
|
|
@@ -104,8 +128,15 @@ The top-level `--instruction` is consulted by the agent when step text is ambigu
|
|
|
104
128
|
| `sente task create --workflow <id> --name "..." --instruction "..." --steps-from <file> --json` | Create a new QA task |
|
|
105
129
|
| `sente task update <id> --steps-from <file> --json` | Replace steps; also accepts `--name`, `--instruction`, `--description` |
|
|
106
130
|
| `sente task delete <id> --json` | Delete a task |
|
|
131
|
+
| `sente task run <id> --json` | Queue a run for one task (async; against staging) |
|
|
132
|
+
| `sente workflow list [--project <id>] --json` | List the workflows in a project (id + task count) |
|
|
133
|
+
| `sente workflow run <workflowId> --json` | Queue a run for every task in a workflow (sequential) |
|
|
134
|
+
| `sente project run [--id <projectId>] --json` | Queue a run for every task across all workflows in a project |
|
|
107
135
|
| `sente task results <id> --failed-step --json` | Focused failure payload for diagnosis |
|
|
108
136
|
| `sente task results <id> --json` | Full step-by-step results |
|
|
137
|
+
| `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. |
|
|
138
|
+
| `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 |
|
|
139
|
+
| `sente auth finish <sessionId> --project <id>` | Close the seeding session; persist the login to the profile |
|
|
109
140
|
| `sente sync --json` | Re-pull tasks into `.sente/tasks.json` |
|
|
110
141
|
| `sente secret list --json` | List secrets (metadata only; values are never returned) |
|
|
111
142
|
| `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