@neverquits/nq 0.1.2 → 0.1.4
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/bin/nq.mjs +13 -13
- package/lib/commands/pull.mjs +46 -16
- package/package.json +1 -1
package/bin/nq.mjs
CHANGED
|
@@ -28,20 +28,20 @@ function printHelp() {
|
|
|
28
28
|
nq — NeverQuits CLI
|
|
29
29
|
|
|
30
30
|
Usage:
|
|
31
|
-
nq login
|
|
32
|
-
nq pull <
|
|
33
|
-
nq push [-m "message"]
|
|
34
|
-
nq logout
|
|
35
|
-
nq status
|
|
31
|
+
nq login Log in with your access token
|
|
32
|
+
nq pull <hiredAgentId> Clone the agent's workspace
|
|
33
|
+
nq push [-m "message"] Commit and push changes
|
|
34
|
+
nq logout Clear stored credentials
|
|
35
|
+
nq status Show current config
|
|
36
36
|
|
|
37
37
|
Options:
|
|
38
|
-
nq pull <
|
|
39
|
-
nq pull <
|
|
40
|
-
nq push --message "my changes"
|
|
38
|
+
nq pull <hiredAgentId> --session <id> Also write .mcp.json
|
|
39
|
+
nq pull <hiredAgentId> --dir <path> Custom clone directory
|
|
40
|
+
nq push --message "my changes" Custom commit message
|
|
41
41
|
|
|
42
42
|
Examples:
|
|
43
43
|
nq login
|
|
44
|
-
nq pull
|
|
44
|
+
nq pull cmpl6d28g0004fy3icu397lqk
|
|
45
45
|
nq push -m "updated research notes"
|
|
46
46
|
`)
|
|
47
47
|
}
|
|
@@ -81,13 +81,13 @@ async function main() {
|
|
|
81
81
|
|
|
82
82
|
if (command === 'pull') {
|
|
83
83
|
const { flags, positional } = parseFlags(args.slice(1))
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
86
|
-
console.error('Usage: nq pull <
|
|
84
|
+
const target = positional[0]
|
|
85
|
+
if (!target) {
|
|
86
|
+
console.error('Usage: nq pull <hiredAgentId>')
|
|
87
87
|
process.exit(1)
|
|
88
88
|
}
|
|
89
89
|
const { pull } = await import('../lib/commands/pull.mjs')
|
|
90
|
-
await pull(
|
|
90
|
+
await pull(target, {
|
|
91
91
|
org: flags.org,
|
|
92
92
|
session: flags.session,
|
|
93
93
|
dir: flags.dir,
|
package/lib/commands/pull.mjs
CHANGED
|
@@ -1,29 +1,40 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process'
|
|
2
2
|
import { existsSync, writeFileSync } from 'node:fs'
|
|
3
|
-
import { join, resolve } from 'node:path'
|
|
3
|
+
import { join, resolve, dirname } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
4
5
|
import { apiFetch, getToken } from '../api.mjs'
|
|
5
6
|
import { getApiUrl } from '../config.mjs'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const NQ_BIN = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', 'bin', 'nq.mjs')
|
|
9
|
+
const NODE_BIN = process.execPath
|
|
10
|
+
|
|
11
|
+
export async function pull(positional, options) {
|
|
12
|
+
// Two call shapes:
|
|
13
|
+
// nq pull <hiredAgentId> (preferred)
|
|
14
|
+
// nq pull <agentKey> --org <orgId> (legacy)
|
|
15
|
+
// Distinguish by whether --org was passed.
|
|
16
|
+
const useLegacy = Boolean(options.org)
|
|
17
|
+
const body = useLegacy
|
|
18
|
+
? { orgId: options.org, agentKey: positional }
|
|
19
|
+
: { hiredAgentId: positional }
|
|
20
|
+
|
|
21
|
+
if (!positional) {
|
|
22
|
+
console.error('Usage: nq pull <hiredAgentId>')
|
|
23
|
+
console.error(' or: nq pull <agentKey> --org <orgId> (legacy)')
|
|
11
24
|
process.exit(1)
|
|
12
25
|
}
|
|
13
26
|
|
|
14
|
-
const sessionId = options.session
|
|
15
|
-
const dir = options.dir ?? `${orgId}-${agentKey}`
|
|
16
|
-
const dest = resolve(dir)
|
|
17
|
-
|
|
18
27
|
console.log()
|
|
19
|
-
console.log(
|
|
28
|
+
console.log(useLegacy
|
|
29
|
+
? ` Pulling workspace: ${body.orgId}/${body.agentKey}`
|
|
30
|
+
: ` Pulling workspace: ${positional}`)
|
|
20
31
|
|
|
21
32
|
// Ask the backend for a fresh installation token + the repo identifier.
|
|
22
33
|
// The token is short-lived; we bake it into the clone URL once and then
|
|
23
34
|
// configure a credential helper so future pushes/pulls mint a new one.
|
|
24
35
|
const tokenRes = await apiFetch('/api/workspace/install-token', {
|
|
25
36
|
method: 'POST',
|
|
26
|
-
body: JSON.stringify(
|
|
37
|
+
body: JSON.stringify(body),
|
|
27
38
|
})
|
|
28
39
|
if (tokenRes.status === 404) {
|
|
29
40
|
console.error(' No workspace repo found. Has onboarding been completed for this org?')
|
|
@@ -33,7 +44,23 @@ export async function pull(agentKey, options) {
|
|
|
33
44
|
console.error(` Failed to mint install token: ${tokenRes.status}`)
|
|
34
45
|
process.exit(1)
|
|
35
46
|
}
|
|
36
|
-
const {
|
|
47
|
+
const {
|
|
48
|
+
token,
|
|
49
|
+
username,
|
|
50
|
+
githubOrg,
|
|
51
|
+
repoName,
|
|
52
|
+
orgId: resolvedOrgId,
|
|
53
|
+
agentKey: resolvedAgentKey,
|
|
54
|
+
} = await tokenRes.json()
|
|
55
|
+
|
|
56
|
+
// The server always echoes back resolvedOrgId + resolvedAgentKey so the
|
|
57
|
+
// CLI can stash them in .git/config for the credential helper.
|
|
58
|
+
const orgId = resolvedOrgId ?? options.org
|
|
59
|
+
const agentKey = resolvedAgentKey ?? positional
|
|
60
|
+
|
|
61
|
+
const sessionId = options.session
|
|
62
|
+
const dir = options.dir ?? `${orgId}-${agentKey}`
|
|
63
|
+
const dest = resolve(dir)
|
|
37
64
|
|
|
38
65
|
const gitDir = join(dest, '.git')
|
|
39
66
|
|
|
@@ -70,7 +97,7 @@ export async function pull(agentKey, options) {
|
|
|
70
97
|
console.log(' Wrote .mcp.json')
|
|
71
98
|
} else {
|
|
72
99
|
console.log(' Skipped .mcp.json (no --session). Create a session first, then run:')
|
|
73
|
-
console.log(` nq pull ${
|
|
100
|
+
console.log(` nq pull ${positional} --session <sessionId>`)
|
|
74
101
|
}
|
|
75
102
|
|
|
76
103
|
console.log()
|
|
@@ -100,14 +127,17 @@ function setRemote(dest, url) {
|
|
|
100
127
|
}
|
|
101
128
|
|
|
102
129
|
function configureCredentialHelper(dest, orgId, agentKey) {
|
|
103
|
-
// The helper is invoked by git
|
|
104
|
-
//
|
|
130
|
+
// The helper is invoked by git during a github.com auth challenge.
|
|
131
|
+
// We point at the *absolute* path of this nq install (node binary +
|
|
132
|
+
// bin/nq.mjs) so the helper works regardless of whether `nq` is on
|
|
133
|
+
// the user's PATH — e.g. when they invoked us via `npx`.
|
|
134
|
+
const helperCmd = `${shArg(NODE_BIN)} ${shArg(NQ_BIN)} credential-helper`
|
|
105
135
|
const cmds = [
|
|
106
136
|
['nq.orgId', orgId],
|
|
107
137
|
['nq.agentKey', agentKey],
|
|
108
138
|
// Per-host helper, applied only inside this repo via --local config.
|
|
109
139
|
// The '!' prefix tells git to run it as a shell command.
|
|
110
|
-
['credential.https://github.com.helper',
|
|
140
|
+
['credential.https://github.com.helper', `!${helperCmd}`],
|
|
111
141
|
]
|
|
112
142
|
for (const [k, v] of cmds) {
|
|
113
143
|
execSync(`git config --local ${shArg(k)} ${shArg(v)}`, {
|